summaryrefslogtreecommitdiffstats
path: root/chrome/browser/history_model.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/history_model.cc')
-rw-r--r--chrome/browser/history_model.cc298
1 files changed, 298 insertions, 0 deletions
diff --git a/chrome/browser/history_model.cc b/chrome/browser/history_model.cc
new file mode 100644
index 0000000..80088d6
--- /dev/null
+++ b/chrome/browser/history_model.cc
@@ -0,0 +1,298 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "chrome/browser/history_model.h"
+
+#include "chrome/browser/bookmark_bar_model.h"
+#include "chrome/browser/profile.h"
+
+// The max number of results to retrieve when browsing user's history.
+static const int kMaxBrowseResults = 800;
+
+// The max number of search results to retrieve.
+static const int kMaxSearchResults = 100;
+
+HistoryModel::HistoryModel(Profile* profile, const std::wstring& search_text)
+ : BaseHistoryModel(profile),
+ search_text_(search_text),
+ search_depth_(0) {
+ // Register for notifications about URL starredness changing on this profile.
+ NotificationService::current()->
+ AddObserver(this, NOTIFY_URLS_STARRED,
+ Source<Profile>(profile->GetOriginalProfile()));
+ NotificationService::current()->
+ AddObserver(this, NOTIFY_HISTORY_URLS_DELETED,
+ Source<Profile>(profile->GetOriginalProfile()));
+}
+
+HistoryModel::~HistoryModel() {
+ // Unregister for notifications about URL starredness.
+ NotificationService::current()->
+ RemoveObserver(this, NOTIFY_URLS_STARRED,
+ Source<Profile>(profile_->GetOriginalProfile()));
+ NotificationService::current()->
+ RemoveObserver(this, NOTIFY_HISTORY_URLS_DELETED,
+ Source<Profile>(profile_->GetOriginalProfile()));
+}
+
+int HistoryModel::GetItemCount() {
+ return static_cast<int>(results_.size());
+}
+
+Time HistoryModel::GetVisitTime(int index) {
+#ifndef NDEBUG
+ DCHECK(IsValidIndex(index));
+#endif
+ return results_[index].visit_time();
+}
+
+const std::wstring& HistoryModel::GetTitle(int index) {
+ return results_[index].title();
+}
+
+const GURL& HistoryModel::GetURL(int index) {
+ return results_[index].url();
+}
+
+history::URLID HistoryModel::GetURLID(int index) {
+ return results_[index].id();
+}
+
+bool HistoryModel::IsStarred(int index) {
+ return results_[index].starred();
+}
+
+const Snippet& HistoryModel::GetSnippet(int index) {
+ return results_[index].snippet();
+}
+
+void HistoryModel::RemoveFromModel(int start, int length) {
+ DCHECK(start >= 0 && start + length <= GetItemCount());
+ results_.DeleteRange(start, start + length);
+ if (observer_)
+ observer_->ModelChanged(true);
+}
+
+void HistoryModel::SetSearchText(const std::wstring& search_text) {
+ if (search_text == search_text_)
+ return;
+
+ search_text_ = search_text;
+ search_depth_ = 0;
+ Refresh();
+}
+
+void HistoryModel::InitVisitRequest(int depth) {
+ HistoryService* history_service =
+ profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
+ if (!history_service)
+ return;
+
+ AboutToScheduleRequest();
+
+ history::QueryOptions options;
+
+ // Limit our search so that it doesn't return more than the maximum required
+ // number of results.
+ int max_total_results = search_text_.empty() ?
+ kMaxBrowseResults : kMaxSearchResults;
+
+ if (depth == 0) {
+ // Set the end time of this first search to null (which will
+ // show results from the future, should the user's clock have
+ // been set incorrectly).
+ options.end_time = Time();
+
+ search_start_ = Time::Now();
+
+ // Configure the begin point of the search to the start of the
+ // current month.
+ Time::Exploded start_exploded;
+ search_start_.LocalMidnight().LocalExplode(&start_exploded);
+ start_exploded.day_of_month = 1;
+ options.begin_time = Time::FromLocalExploded(start_exploded);
+
+ options.only_starred = false;
+ options.max_count = max_total_results;
+ } else {
+ Time::Exploded exploded;
+ search_start_.LocalMidnight().LocalExplode(&exploded);
+ exploded.day_of_month = 1;
+
+ // Set the end-time of this search to the end of the month that is
+ // |depth| months before the search end point. The end time is not
+ // inclusive, so we should feel free to set it to midnight on the
+ // first day of the following month.
+ exploded.month -= depth - 1;
+ while (exploded.month < 1) {
+ exploded.month += 12;
+ exploded.year--;
+ }
+ options.end_time = Time::FromLocalExploded(exploded);
+
+ // Set the begin-time of the search to the start of the month
+ // that is |depth| months prior to search_start_.
+ if (exploded.month > 1) {
+ exploded.month--;
+ } else {
+ exploded.month = 12;
+ exploded.year--;
+ }
+ options.begin_time = Time::FromLocalExploded(exploded);
+
+ options.only_starred = false;
+
+ // Subtract off the number of pages we already got.
+ options.max_count = max_total_results - static_cast<int>(results_.size());
+ }
+
+ // This will make us get only one entry for each page. This is definitely
+ // correct for "starred only" queries, but more debatable for regular
+ // history queries. We might want to get all of them but then remove adjacent
+ // duplicates like Mozilla.
+ //
+ // We'll still get duplicates across month boundaries, which is probably fine.
+ options.most_recent_visit_only = true;
+
+ HistoryService::QueryHistoryCallback* callback =
+ NewCallback(this, &HistoryModel::VisitedPagesQueryComplete);
+ history_service->QueryHistory(search_text_, options,
+ &cancelable_consumer_, callback);
+}
+
+void HistoryModel::SetPageStarred(int index, bool state) {
+ const history::URLResult& result = results_[index];
+ if (!UpdateStarredStateOfURL(result.url(), state))
+ return; // Nothing was changed.
+
+ if (observer_)
+ observer_->ModelChanged(false);
+
+ BookmarkBarModel* bb_model = profile_->GetBookmarkBarModel();
+ if (bb_model)
+ bb_model->SetURLStarred(result.url(), result.title(), state);
+}
+
+void HistoryModel::Refresh() {
+ cancelable_consumer_.CancelAllRequests();
+ if (observer_)
+ observer_->ModelEndWork();
+ search_depth_ = 0;
+ InitVisitRequest(search_depth_);
+}
+
+void HistoryModel::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ switch (type) {
+ case NOTIFY_URLS_STARRED: { // Somewhere, a URL has been starred.
+ Details<history::URLsStarredDetails> starred_state(details);
+
+ // In the degenerate case when there are a lot of pages starred, this may
+ // be unacceptably slow.
+ std::set<GURL>::const_iterator i;
+ bool changed = false;
+ for (i = starred_state->changed_urls.begin();
+ i != starred_state->changed_urls.end(); ++i) {
+ changed |= UpdateStarredStateOfURL(*i, starred_state->starred);
+ }
+ if (changed && observer_)
+ observer_->ModelChanged(false);
+ break;
+ }
+
+ case NOTIFY_HISTORY_URLS_DELETED:
+ // TODO(brettw) bug 1140015: This should actually update the current query
+ // rather than re-querying. This should be much more efficient and
+ // user-friendly.
+ //
+ // Note that we can special case when the "all_history" flag is set to just
+ // clear the view.
+ Refresh();
+ break;
+
+ // TODO(brettw) bug 1140015, 1140017, 1140020: Add a more observers to catch
+ // title changes, new additions, etc.. Also, URLS_ADDED when that
+ // notification exists.
+
+ default:
+ NOTREACHED();
+ break;
+ }
+}
+
+void HistoryModel::VisitedPagesQueryComplete(
+ HistoryService::Handle request_handle,
+ history::QueryResults* results) {
+ bool changed = (results->size() > 0);
+ if (search_depth_ == 0) {
+ if (results_.size() > 0)
+ changed = true;
+ results_.Swap(results);
+ } else {
+ results_.AppendResultsBySwapping(results, true);
+ }
+
+ is_search_results_ = !search_text_.empty();
+
+ if (changed && observer_)
+ observer_->ModelChanged(true);
+
+ search_depth_++;
+
+ int max_results = search_text_.empty() ?
+ kMaxBrowseResults : kMaxSearchResults;
+
+ // TODO(glen/brettw): bug 1203052 - Need to detect if we've reached the
+ // end of the user's history.
+ if (search_depth_ < kHistoryScopeMonths &&
+ static_cast<int>(results_.size()) < max_results) {
+ InitVisitRequest(search_depth_);
+ } else {
+ RequestCompleted();
+ }
+}
+
+bool HistoryModel::UpdateStarredStateOfURL(const GURL& url, bool is_starred) {
+ bool changed = false;
+
+ // See if we've got any of the changed URLs in our results. There may be
+ // more than once instance of the URL, and we have to update them all.
+ size_t num_matches;
+ const size_t* match_indices = results_.MatchesForURL(url, &num_matches);
+ for (size_t i = 0; i < num_matches; i++) {
+ // Update the view. (We don't care about the ID, just 0 or nonzero.)
+ history::URLResult& result = results_[match_indices[i]];
+ if (result.starred() != is_starred) {
+ result.set_star_id(is_starred ? 1 : 0);
+ changed = true;
+ }
+ }
+ return changed;
+} \ No newline at end of file