| 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.h" | 20 #include "base/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 345 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 573 // Update the visit_details for this visit. | 523 // Update the visit_details for this visit. |
| 574 UpdateVisitDuration(from_visit_id, request.time); | 524 UpdateVisitDuration(from_visit_id, request.time); |
| 575 } | 525 } |
| 576 | 526 |
| 577 // Subsequent transitions in the redirect list must all be server | 527 // Subsequent transitions in the redirect list must all be server |
| 578 // redirects. | 528 // redirects. |
| 579 redirect_info = content::PAGE_TRANSITION_SERVER_REDIRECT; | 529 redirect_info = content::PAGE_TRANSITION_SERVER_REDIRECT; |
| 580 } | 530 } |
| 581 | 531 |
| 582 // Last, save this redirect chain for later so we can set titles & favicons | 532 // Last, save this redirect chain for later so we can set titles & favicons |
| 583 // on the redirected pages properly. It is indexed by the destination page. | 533 // on the redirected pages properly. |
| 584 recent_redirects_.Put(request.url, redirects); | 534 recent_redirects_.Put(request.url, redirects); |
| 585 } | 535 } |
| 586 | 536 |
| 587 // TODO(brettw) bug 1140015: Add an "add page" notification so the history | 537 // TODO(brettw) bug 1140015: Add an "add page" notification so the history |
| 588 // views can keep in sync. | 538 // views can keep in sync. |
| 589 | 539 |
| 590 // Add the last visit to the tracker so we can get outgoing transitions. | 540 // Add the last visit to the tracker so we can get outgoing transitions. |
| 591 // TODO(evanm): Due to http://b/1194536 we lose the referrers of a subframe | 541 // TODO(evanm): Due to http://b/1194536 we lose the referrers of a subframe |
| 592 // navigation anyway, so last_visit_id is always zero for them. But adding | 542 // navigation anyway, so last_visit_id is always zero for them. But adding |
| 593 // them here confuses main frame history, so we skip them for now. | 543 // them here confuses main frame history, so we skip them for now. |
| 594 if (stripped_transition != content::PAGE_TRANSITION_AUTO_SUBFRAME && | 544 if (stripped_transition != content::PAGE_TRANSITION_AUTO_SUBFRAME && |
| 595 stripped_transition != content::PAGE_TRANSITION_MANUAL_SUBFRAME && | 545 stripped_transition != content::PAGE_TRANSITION_MANUAL_SUBFRAME && |
| 596 !is_keyword_generated) { | 546 !is_keyword_generated) { |
| 597 tracker_.AddVisit(request.id_scope, request.page_id, request.url, | 547 tracker_.AddVisit(request.id_scope, request.page_id, request.url, |
| 598 last_ids.second); | 548 last_ids.second); |
| 599 } | 549 } |
| 600 | 550 |
| 601 if (text_database_) { | |
| 602 text_database_->AddPageURL(request.url, last_ids.first, last_ids.second, | |
| 603 request.time); | |
| 604 } | |
| 605 | |
| 606 ScheduleCommit(); | 551 ScheduleCommit(); |
| 607 } | 552 } |
| 608 | 553 |
| 609 void HistoryBackend::InitImpl(const std::string& languages) { | 554 void HistoryBackend::InitImpl(const std::string& languages) { |
| 610 DCHECK(!db_) << "Initializing HistoryBackend twice"; | 555 DCHECK(!db_) << "Initializing HistoryBackend twice"; |
| 611 // In the rare case where the db fails to initialize a dialog may get shown | 556 // In the rare case where the db fails to initialize a dialog may get shown |
| 612 // the blocks the caller, yet allows other messages through. For this reason | 557 // the blocks the caller, yet allows other messages through. For this reason |
| 613 // we only set db_ to the created database if creation is successful. That | 558 // we only set db_ to the created database if creation is successful. That |
| 614 // way other methods won't do anything as db_ is still NULL. | 559 // way other methods won't do anything as db_ is still NULL. |
| 615 | 560 |
| 616 TimeTicks beginning_time = TimeTicks::Now(); | 561 TimeTicks beginning_time = TimeTicks::Now(); |
| 617 | 562 |
| 618 // Compute the file names. Note that the index file can be removed when the | 563 // Compute the file names. |
| 619 // text db manager is finished being hooked up. | |
| 620 base::FilePath history_name = history_dir_.Append(chrome::kHistoryFilename); | 564 base::FilePath history_name = history_dir_.Append(chrome::kHistoryFilename); |
| 621 base::FilePath thumbnail_name = GetThumbnailFileName(); | 565 base::FilePath thumbnail_name = GetThumbnailFileName(); |
| 622 base::FilePath archived_name = GetArchivedFileName(); | 566 base::FilePath archived_name = GetArchivedFileName(); |
| 623 | 567 |
| 568 // Delete the old index database files which are no longer used. |
| 569 DeleteFTSIndexDatabases(); |
| 570 |
| 624 // History database. | 571 // History database. |
| 625 db_.reset(new HistoryDatabase()); | 572 db_.reset(new HistoryDatabase()); |
| 626 | 573 |
| 627 // Unretained to avoid a ref loop with db_. | 574 // Unretained to avoid a ref loop with db_. |
| 628 db_->set_error_callback( | 575 db_->set_error_callback( |
| 629 base::Bind(&HistoryBackend::DatabaseErrorCallback, | 576 base::Bind(&HistoryBackend::DatabaseErrorCallback, |
| 630 base::Unretained(this))); | 577 base::Unretained(this))); |
| 631 | 578 |
| 632 sql::InitStatus status = db_->Init(history_name); | 579 sql::InitStatus status = db_->Init(history_name); |
| 633 switch (status) { | 580 switch (status) { |
| (...skipping 19 matching lines...) Expand all Loading... |
| 653 // Fill the in-memory database and send it back to the history service on the | 600 // Fill the in-memory database and send it back to the history service on the |
| 654 // main thread. | 601 // main thread. |
| 655 InMemoryHistoryBackend* mem_backend = new InMemoryHistoryBackend; | 602 InMemoryHistoryBackend* mem_backend = new InMemoryHistoryBackend; |
| 656 if (mem_backend->Init(history_name, db_.get())) | 603 if (mem_backend->Init(history_name, db_.get())) |
| 657 delegate_->SetInMemoryBackend(id_, mem_backend); // Takes ownership of | 604 delegate_->SetInMemoryBackend(id_, mem_backend); // Takes ownership of |
| 658 // pointer. | 605 // pointer. |
| 659 else | 606 else |
| 660 delete mem_backend; // Error case, run without the in-memory DB. | 607 delete mem_backend; // Error case, run without the in-memory DB. |
| 661 db_->BeginExclusiveMode(); // Must be after the mem backend read the data. | 608 db_->BeginExclusiveMode(); // Must be after the mem backend read the data. |
| 662 | 609 |
| 663 // Create the history publisher which needs to be passed on to the text and | 610 // Create the history publisher which needs to be passed on to the thumbnail |
| 664 // thumbnail databases for publishing history. | 611 // database for publishing history. |
| 665 history_publisher_.reset(new HistoryPublisher()); | 612 history_publisher_.reset(new HistoryPublisher()); |
| 666 if (!history_publisher_->Init()) { | 613 if (!history_publisher_->Init()) { |
| 667 // The init may fail when there are no indexers wanting our history. | 614 // The init may fail when there are no indexers wanting our history. |
| 668 // Hence no need to log the failure. | 615 // Hence no need to log the failure. |
| 669 history_publisher_.reset(); | 616 history_publisher_.reset(); |
| 670 } | 617 } |
| 671 | 618 |
| 672 // Full-text database. This has to be first so we can pass it to the | |
| 673 // HistoryDatabase for migration. | |
| 674 text_database_.reset(new TextDatabaseManager(history_dir_, | |
| 675 db_.get(), db_.get())); | |
| 676 if (!text_database_->Init(history_publisher_.get())) { | |
| 677 LOG(WARNING) << "Text database initialization failed, running without it."; | |
| 678 text_database_.reset(); | |
| 679 } | |
| 680 if (db_->needs_version_17_migration()) { | |
| 681 // See needs_version_17_migration() decl for more. In this case, we want | |
| 682 // to erase all the text database files. This must be done after the text | |
| 683 // database manager has been initialized, since it knows about all the | |
| 684 // files it manages. | |
| 685 text_database_->DeleteAll(); | |
| 686 } | |
| 687 | |
| 688 // Thumbnail database. | 619 // Thumbnail database. |
| 689 thumbnail_db_.reset(new ThumbnailDatabase()); | 620 thumbnail_db_.reset(new ThumbnailDatabase()); |
| 690 if (!db_->GetNeedsThumbnailMigration()) { | 621 if (!db_->GetNeedsThumbnailMigration()) { |
| 691 // No convertion needed - use new filename right away. | 622 // No convertion needed - use new filename right away. |
| 692 thumbnail_name = GetFaviconsFileName(); | 623 thumbnail_name = GetFaviconsFileName(); |
| 693 } | 624 } |
| 694 if (thumbnail_db_->Init(thumbnail_name, | 625 if (thumbnail_db_->Init(thumbnail_name, |
| 695 history_publisher_.get(), | 626 history_publisher_.get(), |
| 696 db_.get()) != sql::INIT_OK) { | 627 db_.get()) != sql::INIT_OK) { |
| 697 // Unlike the main database, we don't error out when the database is too | 628 // 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... |
| 730 } | 661 } |
| 731 | 662 |
| 732 // Tell the expiration module about all the nice databases we made. This must | 663 // Tell the expiration module about all the nice databases we made. This must |
| 733 // happen before db_->Init() is called since the callback ForceArchiveHistory | 664 // happen before db_->Init() is called since the callback ForceArchiveHistory |
| 734 // may need to expire stuff. | 665 // may need to expire stuff. |
| 735 // | 666 // |
| 736 // *sigh*, this can all be cleaned up when that migration code is removed. | 667 // *sigh*, this can all be cleaned up when that migration code is removed. |
| 737 // The main DB initialization should intuitively be first (not that it | 668 // The main DB initialization should intuitively be first (not that it |
| 738 // actually matters) and the expirer should be set last. | 669 // actually matters) and the expirer should be set last. |
| 739 expirer_.SetDatabases(db_.get(), archived_db_.get(), | 670 expirer_.SetDatabases(db_.get(), archived_db_.get(), |
| 740 thumbnail_db_.get(), text_database_.get()); | 671 thumbnail_db_.get()); |
| 741 | 672 |
| 742 // Open the long-running transaction. | 673 // Open the long-running transaction. |
| 743 db_->BeginTransaction(); | 674 db_->BeginTransaction(); |
| 744 if (thumbnail_db_) | 675 if (thumbnail_db_) |
| 745 thumbnail_db_->BeginTransaction(); | 676 thumbnail_db_->BeginTransaction(); |
| 746 if (archived_db_) | 677 if (archived_db_) |
| 747 archived_db_->BeginTransaction(); | 678 archived_db_->BeginTransaction(); |
| 748 if (text_database_) | |
| 749 text_database_->BeginTransaction(); | |
| 750 | 679 |
| 751 // Get the first item in our database. | 680 // Get the first item in our database. |
| 752 db_->GetStartDate(&first_recorded_time_); | 681 db_->GetStartDate(&first_recorded_time_); |
| 753 | 682 |
| 754 // Start expiring old stuff. | 683 // Start expiring old stuff. |
| 755 expirer_.StartArchivingOldStuff(TimeDelta::FromDays(kArchiveDaysThreshold)); | 684 expirer_.StartArchivingOldStuff(TimeDelta::FromDays(kArchiveDaysThreshold)); |
| 756 | 685 |
| 757 #if defined(OS_ANDROID) | 686 #if defined(OS_ANDROID) |
| 758 if (thumbnail_db_) { | 687 if (thumbnail_db_) { |
| 759 android_provider_backend_.reset(new AndroidProviderBackend( | 688 android_provider_backend_.reset(new AndroidProviderBackend( |
| (...skipping 13 matching lines...) Expand all Loading... |
| 773 db_.reset(); | 702 db_.reset(); |
| 774 } | 703 } |
| 775 if (thumbnail_db_) { | 704 if (thumbnail_db_) { |
| 776 thumbnail_db_->CommitTransaction(); | 705 thumbnail_db_->CommitTransaction(); |
| 777 thumbnail_db_.reset(); | 706 thumbnail_db_.reset(); |
| 778 } | 707 } |
| 779 if (archived_db_) { | 708 if (archived_db_) { |
| 780 archived_db_->CommitTransaction(); | 709 archived_db_->CommitTransaction(); |
| 781 archived_db_.reset(); | 710 archived_db_.reset(); |
| 782 } | 711 } |
| 783 if (text_database_) { | |
| 784 text_database_->CommitTransaction(); | |
| 785 text_database_.reset(); | |
| 786 } | |
| 787 } | 712 } |
| 788 | 713 |
| 789 std::pair<URLID, VisitID> HistoryBackend::AddPageVisit( | 714 std::pair<URLID, VisitID> HistoryBackend::AddPageVisit( |
| 790 const GURL& url, | 715 const GURL& url, |
| 791 Time time, | 716 Time time, |
| 792 VisitID referring_visit, | 717 VisitID referring_visit, |
| 793 content::PageTransition transition, | 718 content::PageTransition transition, |
| 794 VisitSource visit_source) { | 719 VisitSource visit_source) { |
| 795 // Top-level frame navigations are visible, everything else is hidden | 720 // Top-level frame navigations are visible, everything else is hidden |
| 796 bool new_hidden = !content::PageTransitionIsMainFrame(transition); | 721 bool new_hidden = !content::PageTransitionIsMainFrame(transition); |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 840 url_info.set_typed_count(typed_increment); | 765 url_info.set_typed_count(typed_increment); |
| 841 url_info.set_last_visit(time); | 766 url_info.set_last_visit(time); |
| 842 url_info.set_hidden(new_hidden); | 767 url_info.set_hidden(new_hidden); |
| 843 | 768 |
| 844 url_id = db_->AddURL(url_info); | 769 url_id = db_->AddURL(url_info); |
| 845 if (!url_id) { | 770 if (!url_id) { |
| 846 NOTREACHED() << "Adding URL failed."; | 771 NOTREACHED() << "Adding URL failed."; |
| 847 return std::make_pair(0, 0); | 772 return std::make_pair(0, 0); |
| 848 } | 773 } |
| 849 url_info.id_ = url_id; | 774 url_info.id_ = url_id; |
| 850 | |
| 851 // We don't actually add the URL to the full text index at this point. It | |
| 852 // might be nice to do this so that even if we get no title or body, the | |
| 853 // user can search for URL components and get the page. | |
| 854 // | |
| 855 // However, in most cases, we'll get at least a title and usually contents, | |
| 856 // and this add will be redundant, slowing everything down. As a result, | |
| 857 // we ignore this edge case. | |
| 858 } | 775 } |
| 859 | 776 |
| 860 // Add the visit with the time to the database. | 777 // Add the visit with the time to the database. |
| 861 VisitRow visit_info(url_id, time, referring_visit, transition, 0); | 778 VisitRow visit_info(url_id, time, referring_visit, transition, 0); |
| 862 VisitID visit_id = db_->AddVisit(&visit_info, visit_source); | 779 VisitID visit_id = db_->AddVisit(&visit_info, visit_source); |
| 863 NotifyVisitObservers(visit_info); | 780 NotifyVisitObservers(visit_info); |
| 864 | 781 |
| 865 if (visit_info.visit_time < first_recorded_time_) | 782 if (visit_info.visit_time < first_recorded_time_) |
| 866 first_recorded_time_ = visit_info.visit_time; | 783 first_recorded_time_ = visit_info.visit_time; |
| 867 | 784 |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 917 NOTREACHED() << "Could not add row to DB"; | 834 NOTREACHED() << "Could not add row to DB"; |
| 918 return; | 835 return; |
| 919 } | 836 } |
| 920 | 837 |
| 921 if (i->typed_count() > 0) { | 838 if (i->typed_count() > 0) { |
| 922 modified->changed_urls.push_back(*i); | 839 modified->changed_urls.push_back(*i); |
| 923 modified->changed_urls.back().set_id(url_id); // *i likely has |id_| 0. | 840 modified->changed_urls.back().set_id(url_id); // *i likely has |id_| 0. |
| 924 } | 841 } |
| 925 } | 842 } |
| 926 | 843 |
| 927 // Add the page to the full text index. This function is also used for | |
| 928 // importing. Even though we don't have page contents, we can at least | |
| 929 // add the title and URL to the index so they can be searched. We don't | |
| 930 // bother to delete any already-existing FTS entries for the URL, since | |
| 931 // this is normally called on import. | |
| 932 // | |
| 933 // If you ever import *after* first run (selecting import from the menu), | |
| 934 // then these additional entries will "shadow" the originals when querying | |
| 935 // for the most recent match only, and the user won't get snippets. This is | |
| 936 // a very minor issue, and fixing it will make import slower, so we don't | |
| 937 // bother. | |
| 938 bool has_indexed = false; | |
| 939 if (text_database_) { | |
| 940 // We do not have to make it update the visit database, below, we will | |
| 941 // create the visit entry with the indexed flag set. | |
| 942 has_indexed = text_database_->AddPageData(i->url(), url_id, 0, | |
| 943 i->last_visit(), | |
| 944 i->title(), string16()); | |
| 945 } | |
| 946 | |
| 947 // Sync code manages the visits itself. | 844 // Sync code manages the visits itself. |
| 948 if (visit_source != SOURCE_SYNCED) { | 845 if (visit_source != SOURCE_SYNCED) { |
| 949 // Make up a visit to correspond to the last visit to the page. | 846 // Make up a visit to correspond to the last visit to the page. |
| 950 VisitRow visit_info(url_id, i->last_visit(), 0, | 847 VisitRow visit_info(url_id, i->last_visit(), 0, |
| 951 content::PageTransitionFromInt( | 848 content::PageTransitionFromInt( |
| 952 content::PAGE_TRANSITION_LINK | | 849 content::PAGE_TRANSITION_LINK | |
| 953 content::PAGE_TRANSITION_CHAIN_START | | 850 content::PAGE_TRANSITION_CHAIN_START | |
| 954 content::PAGE_TRANSITION_CHAIN_END), 0); | 851 content::PAGE_TRANSITION_CHAIN_END), 0); |
| 955 visit_info.is_indexed = has_indexed; | |
| 956 if (!visit_database->AddVisit(&visit_info, visit_source)) { | 852 if (!visit_database->AddVisit(&visit_info, visit_source)) { |
| 957 NOTREACHED() << "Adding visit failed."; | 853 NOTREACHED() << "Adding visit failed."; |
| 958 return; | 854 return; |
| 959 } | 855 } |
| 960 NotifyVisitObservers(visit_info); | 856 NotifyVisitObservers(visit_info); |
| 961 | 857 |
| 962 if (visit_info.visit_time < first_recorded_time_) | 858 if (visit_info.visit_time < first_recorded_time_) |
| 963 first_recorded_time_ = visit_info.visit_time; | 859 first_recorded_time_ = visit_info.visit_time; |
| 964 } | 860 } |
| 965 } | 861 } |
| (...skipping 14 matching lines...) Expand all Loading... |
| 980 | 876 |
| 981 bool HistoryBackend::IsExpiredVisitTime(const base::Time& time) { | 877 bool HistoryBackend::IsExpiredVisitTime(const base::Time& time) { |
| 982 return time < expirer_.GetCurrentArchiveTime(); | 878 return time < expirer_.GetCurrentArchiveTime(); |
| 983 } | 879 } |
| 984 | 880 |
| 985 void HistoryBackend::SetPageTitle(const GURL& url, | 881 void HistoryBackend::SetPageTitle(const GURL& url, |
| 986 const string16& title) { | 882 const string16& title) { |
| 987 if (!db_) | 883 if (!db_) |
| 988 return; | 884 return; |
| 989 | 885 |
| 990 // Update the full text index. | |
| 991 if (text_database_) | |
| 992 text_database_->AddPageTitle(url, title); | |
| 993 | |
| 994 // Search for recent redirects which should get the same title. We make a | 886 // Search for recent redirects which should get the same title. We make a |
| 995 // dummy list containing the exact URL visited if there are no redirects so | 887 // dummy list containing the exact URL visited if there are no redirects so |
| 996 // the processing below can be the same. | 888 // the processing below can be the same. |
| 997 history::RedirectList dummy_list; | 889 history::RedirectList dummy_list; |
| 998 history::RedirectList* redirects; | 890 history::RedirectList* redirects; |
| 999 RedirectCache::iterator iter = recent_redirects_.Get(url); | 891 RedirectCache::iterator iter = recent_redirects_.Get(url); |
| 1000 if (iter != recent_redirects_.end()) { | 892 if (iter != recent_redirects_.end()) { |
| 1001 redirects = &iter->second; | 893 redirects = &iter->second; |
| 1002 | 894 |
| 1003 // This redirect chain should have the destination URL as the last item. | 895 // 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... |
| 1478 for (std::vector<URLResult>::iterator it = matching_visits.begin(); | 1370 for (std::vector<URLResult>::iterator it = matching_visits.begin(); |
| 1479 it != matching_visits.end() && result->size() < max_results; ++it) { | 1371 it != matching_visits.end() && result->size() < max_results; ++it) { |
| 1480 result->AppendURLBySwapping(&(*it)); | 1372 result->AppendURLBySwapping(&(*it)); |
| 1481 } | 1373 } |
| 1482 | 1374 |
| 1483 if (matching_visits.size() == result->size() && | 1375 if (matching_visits.size() == result->size() && |
| 1484 options.begin_time <= first_recorded_time_) | 1376 options.begin_time <= first_recorded_time_) |
| 1485 result->set_reached_beginning(true); | 1377 result->set_reached_beginning(true); |
| 1486 } | 1378 } |
| 1487 | 1379 |
| 1488 void HistoryBackend::QueryHistoryFTS(const string16& text_query, | |
| 1489 const QueryOptions& options, | |
| 1490 QueryResults* result) { | |
| 1491 if (!text_database_) | |
| 1492 return; | |
| 1493 | |
| 1494 // Full text query, first get all the FTS results in the time range. | |
| 1495 std::vector<TextDatabase::Match> fts_matches; | |
| 1496 Time first_time_searched; | |
| 1497 text_database_->GetTextMatches(text_query, options, | |
| 1498 &fts_matches, &first_time_searched); | |
| 1499 | |
| 1500 URLQuerier querier(db_.get(), archived_db_.get(), true); | |
| 1501 | |
| 1502 // Now get the row and visit information for each one. | |
| 1503 URLResult url_result; // Declare outside loop to prevent re-construction. | |
| 1504 for (size_t i = 0; i < fts_matches.size(); i++) { | |
| 1505 if (options.max_count != 0 && | |
| 1506 static_cast<int>(result->size()) >= options.max_count) | |
| 1507 break; // Got too many items. | |
| 1508 | |
| 1509 // Get the URL, querying the main and archived databases as necessary. If | |
| 1510 // this is not found, the history and full text search databases are out | |
| 1511 // of sync and we give up with this result. | |
| 1512 if (!querier.GetRowForURL(fts_matches[i].url, &url_result)) | |
| 1513 continue; | |
| 1514 | |
| 1515 if (!url_result.url().is_valid()) | |
| 1516 continue; // Don't report invalid URLs in case of corruption. | |
| 1517 | |
| 1518 // Copy over the FTS stuff that the URLDatabase doesn't know about. | |
| 1519 // We do this with swap() to avoid copying, since we know we don't | |
| 1520 // need the original any more. Note that we override the title with the | |
| 1521 // one from FTS, since that will match the title_match_positions (the | |
| 1522 // FTS title and the history DB title may differ). | |
| 1523 url_result.set_title(fts_matches[i].title); | |
| 1524 url_result.title_match_positions_.swap( | |
| 1525 fts_matches[i].title_match_positions); | |
| 1526 url_result.snippet_.Swap(&fts_matches[i].snippet); | |
| 1527 | |
| 1528 // The visit time also comes from the full text search database. Since it | |
| 1529 // has the time, we can avoid an extra query of the visits table. | |
| 1530 url_result.set_visit_time(fts_matches[i].time); | |
| 1531 | |
| 1532 // Add it to the vector, this will clear our |url_row| object as a | |
| 1533 // result of the swap. | |
| 1534 result->AppendURLBySwapping(&url_result); | |
| 1535 } | |
| 1536 | |
| 1537 if (first_time_searched <= first_recorded_time_) | |
| 1538 result->set_reached_beginning(true); | |
| 1539 } | |
| 1540 | |
| 1541 // Frontend to GetMostRecentRedirectsFrom from the history thread. | 1380 // Frontend to GetMostRecentRedirectsFrom from the history thread. |
| 1542 void HistoryBackend::QueryRedirectsFrom( | 1381 void HistoryBackend::QueryRedirectsFrom( |
| 1543 scoped_refptr<QueryRedirectsRequest> request, | 1382 scoped_refptr<QueryRedirectsRequest> request, |
| 1544 const GURL& url) { | 1383 const GURL& url) { |
| 1545 if (request->canceled()) | 1384 if (request->canceled()) |
| 1546 return; | 1385 return; |
| 1547 bool success = GetMostRecentRedirectsFrom(url, &request->value); | 1386 bool success = GetMostRecentRedirectsFrom(url, &request->value); |
| 1548 request->ForwardResult(request->handle(), url, success, &request->value); | 1387 request->ForwardResult(request->handle(), url, success, &request->value); |
| 1549 } | 1388 } |
| 1550 | 1389 |
| (...skipping 239 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1790 GetRedirectsToSpecificVisit(cur_visit, redirects); | 1629 GetRedirectsToSpecificVisit(cur_visit, redirects); |
| 1791 return true; | 1630 return true; |
| 1792 } | 1631 } |
| 1793 | 1632 |
| 1794 void HistoryBackend::ScheduleAutocomplete(HistoryURLProvider* provider, | 1633 void HistoryBackend::ScheduleAutocomplete(HistoryURLProvider* provider, |
| 1795 HistoryURLProviderParams* params) { | 1634 HistoryURLProviderParams* params) { |
| 1796 // ExecuteWithDB should handle the NULL database case. | 1635 // ExecuteWithDB should handle the NULL database case. |
| 1797 provider->ExecuteWithDB(this, db_.get(), params); | 1636 provider->ExecuteWithDB(this, db_.get(), params); |
| 1798 } | 1637 } |
| 1799 | 1638 |
| 1800 void HistoryBackend::SetPageContents(const GURL& url, | |
| 1801 const string16& contents) { | |
| 1802 // This is histogrammed in the text database manager. | |
| 1803 if (!text_database_) | |
| 1804 return; | |
| 1805 text_database_->AddPageContents(url, contents); | |
| 1806 } | |
| 1807 | |
| 1808 void HistoryBackend::SetPageThumbnail( | 1639 void HistoryBackend::SetPageThumbnail( |
| 1809 const GURL& url, | 1640 const GURL& url, |
| 1810 const gfx::Image* thumbnail, | 1641 const gfx::Image* thumbnail, |
| 1811 const ThumbnailScore& score) { | 1642 const ThumbnailScore& score) { |
| 1812 if (!db_ || !thumbnail_db_) | 1643 if (!db_ || !thumbnail_db_) |
| 1813 return; | 1644 return; |
| 1814 | 1645 |
| 1815 URLRow url_row; | 1646 URLRow url_row; |
| 1816 URLID url_id = db_->GetRowForURL(url, &url_row); | 1647 URLID url_id = db_->GetRowForURL(url, &url_row); |
| 1817 if (url_id) { | 1648 if (url_id) { |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1881 if (db_) { | 1712 if (db_) { |
| 1882 // If there is no thumbnail DB, we can still record a successful migration. | 1713 // If there is no thumbnail DB, we can still record a successful migration. |
| 1883 if (thumbnail_db_) { | 1714 if (thumbnail_db_) { |
| 1884 thumbnail_db_->RenameAndDropThumbnails(GetThumbnailFileName(), | 1715 thumbnail_db_->RenameAndDropThumbnails(GetThumbnailFileName(), |
| 1885 GetFaviconsFileName()); | 1716 GetFaviconsFileName()); |
| 1886 } | 1717 } |
| 1887 db_->ThumbnailMigrationDone(); | 1718 db_->ThumbnailMigrationDone(); |
| 1888 } | 1719 } |
| 1889 } | 1720 } |
| 1890 | 1721 |
| 1722 void HistoryBackend::DeleteFTSIndexDatabases() { |
| 1723 // Find files on disk matching the text databases file pattern so we can |
| 1724 // quickly test for and delete them. |
| 1725 base::FilePath::StringType filepattern = |
| 1726 FILE_PATH_LITERAL("History Index *"); |
| 1727 base::FileEnumerator enumerator( |
| 1728 history_dir_, false, base::FileEnumerator::FILES, filepattern); |
| 1729 int num_databases_deleted = 0; |
| 1730 base::FilePath current_file; |
| 1731 while (!(current_file = enumerator.Next()).empty()) { |
| 1732 if (sql::Connection::Delete(current_file)) |
| 1733 num_databases_deleted++; |
| 1734 } |
| 1735 UMA_HISTOGRAM_COUNTS("History.DeleteFTSIndexDatabases", |
| 1736 num_databases_deleted); |
| 1737 } |
| 1738 |
| 1891 bool HistoryBackend::GetThumbnailFromOlderRedirect( | 1739 bool HistoryBackend::GetThumbnailFromOlderRedirect( |
| 1892 const GURL& page_url, | 1740 const GURL& page_url, |
| 1893 std::vector<unsigned char>* data) { | 1741 std::vector<unsigned char>* data) { |
| 1894 // Look at a few previous visit sessions. | 1742 // Look at a few previous visit sessions. |
| 1895 VisitVector older_sessions; | 1743 VisitVector older_sessions; |
| 1896 URLID page_url_id = db_->GetRowForURL(page_url, NULL); | 1744 URLID page_url_id = db_->GetRowForURL(page_url, NULL); |
| 1897 static const int kVisitsToSearchForThumbnail = 4; | 1745 static const int kVisitsToSearchForThumbnail = 4; |
| 1898 db_->GetMostRecentVisitsForURL( | 1746 db_->GetMostRecentVisitsForURL( |
| 1899 page_url_id, kVisitsToSearchForThumbnail, &older_sessions); | 1747 page_url_id, kVisitsToSearchForThumbnail, &older_sessions); |
| 1900 | 1748 |
| (...skipping 746 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2647 thumbnail_db_->CommitTransaction(); | 2495 thumbnail_db_->CommitTransaction(); |
| 2648 DCHECK(thumbnail_db_->transaction_nesting() == 0) << | 2496 DCHECK(thumbnail_db_->transaction_nesting() == 0) << |
| 2649 "Somebody left a transaction open"; | 2497 "Somebody left a transaction open"; |
| 2650 thumbnail_db_->BeginTransaction(); | 2498 thumbnail_db_->BeginTransaction(); |
| 2651 } | 2499 } |
| 2652 | 2500 |
| 2653 if (archived_db_) { | 2501 if (archived_db_) { |
| 2654 archived_db_->CommitTransaction(); | 2502 archived_db_->CommitTransaction(); |
| 2655 archived_db_->BeginTransaction(); | 2503 archived_db_->BeginTransaction(); |
| 2656 } | 2504 } |
| 2657 | |
| 2658 if (text_database_) { | |
| 2659 text_database_->CommitTransaction(); | |
| 2660 text_database_->BeginTransaction(); | |
| 2661 } | |
| 2662 } | 2505 } |
| 2663 | 2506 |
| 2664 void HistoryBackend::ScheduleCommit() { | 2507 void HistoryBackend::ScheduleCommit() { |
| 2665 if (scheduled_commit_.get()) | 2508 if (scheduled_commit_.get()) |
| 2666 return; | 2509 return; |
| 2667 scheduled_commit_ = new CommitLaterTask(this); | 2510 scheduled_commit_ = new CommitLaterTask(this); |
| 2668 base::MessageLoop::current()->PostDelayedTask( | 2511 base::MessageLoop::current()->PostDelayedTask( |
| 2669 FROM_HERE, | 2512 FROM_HERE, |
| 2670 base::Bind(&CommitLaterTask::RunCommit, scheduled_commit_.get()), | 2513 base::Bind(&CommitLaterTask::RunCommit, scheduled_commit_.get()), |
| 2671 base::TimeDelta::FromSeconds(kCommitIntervalSeconds)); | 2514 base::TimeDelta::FromSeconds(kCommitIntervalSeconds)); |
| (...skipping 210 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2882 bool success = db_->Raze(); | 2725 bool success = db_->Raze(); |
| 2883 UMA_HISTOGRAM_BOOLEAN("History.KillHistoryDatabaseResult", success); | 2726 UMA_HISTOGRAM_BOOLEAN("History.KillHistoryDatabaseResult", success); |
| 2884 | 2727 |
| 2885 #if defined(OS_ANDROID) | 2728 #if defined(OS_ANDROID) |
| 2886 // Release AndroidProviderBackend before other objects. | 2729 // Release AndroidProviderBackend before other objects. |
| 2887 android_provider_backend_.reset(); | 2730 android_provider_backend_.reset(); |
| 2888 #endif | 2731 #endif |
| 2889 | 2732 |
| 2890 // The expirer keeps tabs on the active databases. Tell it about the | 2733 // The expirer keeps tabs on the active databases. Tell it about the |
| 2891 // databases which will be closed. | 2734 // databases which will be closed. |
| 2892 expirer_.SetDatabases(NULL, NULL, NULL, NULL); | 2735 expirer_.SetDatabases(NULL, NULL, NULL); |
| 2893 | 2736 |
| 2894 // Reopen a new transaction for |db_| for the sake of CloseAllDatabases(). | 2737 // Reopen a new transaction for |db_| for the sake of CloseAllDatabases(). |
| 2895 db_->BeginTransaction(); | 2738 db_->BeginTransaction(); |
| 2896 CloseAllDatabases(); | 2739 CloseAllDatabases(); |
| 2897 } | 2740 } |
| 2898 | 2741 |
| 2899 void HistoryBackend::ProcessDBTask( | 2742 void HistoryBackend::ProcessDBTask( |
| 2900 scoped_refptr<HistoryDBTaskRequest> request) { | 2743 scoped_refptr<HistoryDBTaskRequest> request) { |
| 2901 DCHECK(request.get()); | 2744 DCHECK(request.get()); |
| 2902 if (request->canceled()) | 2745 if (request->canceled()) |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2972 // We continue in this error case. If the user wants to delete their | 2815 // We continue in this error case. If the user wants to delete their |
| 2973 // history, we should delete as much as we can. | 2816 // history, we should delete as much as we can. |
| 2974 } | 2817 } |
| 2975 | 2818 |
| 2976 // ClearAllMainHistory will change the IDs of the URLs in kept_urls. Therfore, | 2819 // ClearAllMainHistory will change the IDs of the URLs in kept_urls. Therfore, |
| 2977 // we clear the list afterwards to make sure nobody uses this invalid data. | 2820 // we clear the list afterwards to make sure nobody uses this invalid data. |
| 2978 if (!ClearAllMainHistory(kept_urls)) | 2821 if (!ClearAllMainHistory(kept_urls)) |
| 2979 LOG(ERROR) << "Main history could not be cleared"; | 2822 LOG(ERROR) << "Main history could not be cleared"; |
| 2980 kept_urls.clear(); | 2823 kept_urls.clear(); |
| 2981 | 2824 |
| 2982 // Delete FTS files & archived history. | 2825 // Delete archived history. |
| 2983 if (text_database_) { | |
| 2984 // We assume that the text database has one transaction on them that we need | |
| 2985 // to close & restart (the long-running history transaction). | |
| 2986 text_database_->CommitTransaction(); | |
| 2987 text_database_->DeleteAll(); | |
| 2988 text_database_->BeginTransaction(); | |
| 2989 } | |
| 2990 | |
| 2991 if (archived_db_) { | 2826 if (archived_db_) { |
| 2992 // Close the database and delete the file. | 2827 // Close the database and delete the file. |
| 2993 archived_db_.reset(); | 2828 archived_db_.reset(); |
| 2994 base::FilePath archived_file_name = GetArchivedFileName(); | 2829 base::FilePath archived_file_name = GetArchivedFileName(); |
| 2995 sql::Connection::Delete(archived_file_name); | 2830 sql::Connection::Delete(archived_file_name); |
| 2996 | 2831 |
| 2997 // Now re-initialize the database (which may fail). | 2832 // Now re-initialize the database (which may fail). |
| 2998 archived_db_.reset(new ArchivedDatabase()); | 2833 archived_db_.reset(new ArchivedDatabase()); |
| 2999 if (!archived_db_->Init(archived_file_name)) { | 2834 if (!archived_db_->Init(archived_file_name)) { |
| 3000 LOG(WARNING) << "Could not initialize the archived database."; | 2835 LOG(WARNING) << "Could not initialize the archived database."; |
| (...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3150 int rank = kPageVisitStatsMaxTopSites; | 2985 int rank = kPageVisitStatsMaxTopSites; |
| 3151 std::map<GURL, int>::const_iterator it = most_visited_urls_map_.find(url); | 2986 std::map<GURL, int>::const_iterator it = most_visited_urls_map_.find(url); |
| 3152 if (it != most_visited_urls_map_.end()) | 2987 if (it != most_visited_urls_map_.end()) |
| 3153 rank = (*it).second; | 2988 rank = (*it).second; |
| 3154 UMA_HISTOGRAM_ENUMERATION("History.TopSitesVisitsByRank", | 2989 UMA_HISTOGRAM_ENUMERATION("History.TopSitesVisitsByRank", |
| 3155 rank, kPageVisitStatsMaxTopSites + 1); | 2990 rank, kPageVisitStatsMaxTopSites + 1); |
| 3156 } | 2991 } |
| 3157 #endif | 2992 #endif |
| 3158 | 2993 |
| 3159 } // namespace history | 2994 } // namespace history |
| OLD | NEW |