Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(4502)

Unified Diff: chrome/browser/instant/instant_controller_impl.cc

Issue 12386019: Instant: Use only one hidden WebContents per profile. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/instant/instant_controller_impl.cc
diff --git a/chrome/browser/instant/instant_controller_impl.cc b/chrome/browser/instant/instant_controller_impl.cc
new file mode 100644
index 0000000000000000000000000000000000000000..36232b42873d59cbb4db0a8c9323073dcdf8dd89
--- /dev/null
+++ b/chrome/browser/instant/instant_controller_impl.cc
@@ -0,0 +1,399 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/instant/instant_controller_impl.h"
+
+#include "base/string_util.h"
+#include "base/stringprintf.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/history/history_service.h"
+#include "chrome/browser/history/history_service_factory.h"
+#include "chrome/browser/instant/instant_controller_utils.h"
+#include "chrome/browser/instant/instant_loader.h"
+#include "chrome/browser/instant/instant_model.h"
+#include "chrome/browser/instant/instant_overlay.h"
+#include "chrome/browser/instant/instant_service.h"
+#include "chrome/browser/instant/instant_service_factory.h"
+#include "chrome/browser/ui/browser_instant_controller.h"
+#include "chrome/common/chrome_notification_types.h"
+#include "content/public/browser/navigation_entry.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/web_contents.h"
+
+namespace {
+
+// An artificial delay (in milliseconds) we introduce before telling the Instant
+// page about the new omnibox bounds, in cases where the bounds shrink. This is
+// to avoid the page jumping up/down very fast in response to bounds changes.
+const int kUpdateBoundsDelayMS = 1000;
+
+} // namespace
+
+InstantControllerImpl::InstantControllerImpl(
+ chrome::BrowserInstantController* browser,
+ Profile* profile)
+ : browser_(browser),
+ service_(InstantServiceFactory::GetForProfile(profile)),
+ last_verbatim_(false),
+ last_transition_type_(content::PAGE_TRANSITION_LINK) {
+ service_->AddObserver(this);
+ model_.reset(new InstantModel(service_));
+}
+
+InstantControllerImpl::~InstantControllerImpl() {
+ service_->RemoveObserver(this);
+}
+
+bool InstantControllerImpl::Update(const AutocompleteMatch& match,
+ const string16& user_text,
+ const string16& full_text,
+ size_t selection_start,
+ size_t selection_end,
+ bool verbatim,
+ bool /* user_input_in_progress */,
+ bool omnibox_popup_is_open,
+ bool /* escape_pressed */) {
+ // The overlay is being clicked and will commit soon. Don't change anything.
+ // TODO(sreeram): Add a browser test for this.
+ if (overlay_ && overlay_->is_pointer_down_from_activate())
+ return false;
+
+ // No overlay for URLs and keyword searches or if the user isn't typing.
+ if (!AutocompleteMatch::IsSearchType(match.type) ||
+ match.type == AutocompleteMatch::SEARCH_OTHER_ENGINE ||
+ !omnibox_popup_is_open || user_text.empty() || full_text.empty()) {
+ HideOverlay();
+ return false;
+ }
+
+ if (!overlay_) {
+ // If we don't have a valid (Instant-supporting) web contents ready, bail.
+ if (!service_->loader()->supports_instant())
+ return false;
+
+ overlay_.reset(new InstantOverlay(this, service_,
+ service_->loader()->ReleaseContents()));
+ }
+
+ // Query is verbatim not only when |verbatim| is true (which indicates that
+ // the user pressed Delete/Backspace), but also when there's any selection
+ // (including inline autocompletion) or if the cursor is not at the end.
+ verbatim = verbatim || selection_start != selection_end ||
+ selection_start != full_text.size();
+
+ last_omnibox_text_ = full_text;
+ last_verbatim_ = verbatim;
+ last_transition_type_ = match.transition;
+ url_for_history_ = match.destination_url;
+
+ service_->LogDebugEvent(base::StringPrintf("%p Update '%s' %s [%d,%d]",
+ this,
+ UTF16ToUTF8(last_omnibox_text_).c_str(),
+ verbatim ? "verbatim" : "psychic",
+ static_cast<int>(selection_start),
+ static_cast<int>(selection_end)));
+
+ overlay_->page()->Change(full_text, verbatim, selection_start, selection_end);
+
+ content::NotificationService::current()->Notify(
+ chrome::NOTIFICATION_INSTANT_UPDATED,
+ content::NotificationService::AllSources(),
+ content::NotificationService::NoDetails());
+
+ return true;
+}
+
+void InstantControllerImpl::HandleAutocompleteResults(
+ const std::vector<AutocompleteProvider*>& /* providers */) {
+}
+
+bool InstantControllerImpl::OnUpOrDownKeyPressed(int /* count */) {
+ return false;
+}
+
+void InstantControllerImpl::OnCancel(const AutocompleteMatch& /* match */,
+ const string16& /* full_text */) {
+}
+
+const InstantModel* InstantControllerImpl::model() const {
+ return model_.get();
+}
+
+bool InstantControllerImpl::IsOverlayingSearchResults() const {
+ return model_->overlay() != NULL;
+}
+
+content::WebContents* InstantControllerImpl::GetOverlayContents() const {
+ return overlay_ ? overlay_->contents() : NULL;
+}
+
+bool InstantControllerImpl::CommitIfPossible(InstantCommitType type) {
+ if (!IsOverlayingSearchResults())
+ return false;
+
+ service_->LogDebugEvent(base::StringPrintf("%p CommitIfPossible '%s' %s",
+ this,
+ UTF16ToUTF8(last_omnibox_text_).c_str(),
+ InstantControllerUtils::CommitTypeToString(type).c_str()));
+
+ if (type == INSTANT_COMMIT_FOCUS_LOST)
+ overlay_->page()->Blur(last_omnibox_text_);
+ else if (type != INSTANT_COMMIT_NAVIGATED)
+ overlay_->page()->Submit(last_omnibox_text_);
+
+ HistoryService* history = HistoryServiceFactory::GetForProfile(
+ service_->profile(), Profile::EXPLICIT_ACCESS);
+ if (history) {
+ history->AddPage(url_for_history_, base::Time::Now(), NULL, 0, GURL(),
+ history::RedirectList(), last_transition_type_,
+ history::SOURCE_BROWSED, false);
+
+ if (overlay_->page()->navigated_after_change() ||
+ type == INSTANT_COMMIT_NAVIGATED) {
+ content::NavigationEntry* entry =
+ overlay_->contents()->GetController().GetLastCommittedEntry();
+ history->AddPage(entry->GetVirtualURL(), base::Time::Now(),
+ history::SOURCE_BROWSED);
+ history->SetPageTitle(entry->GetVirtualURL(),
+ entry->GetTitleForDisplay(""));
+ }
+ }
+
+ scoped_ptr<content::WebContents> contents = overlay_->ReleaseContents();
+
+ if (type == INSTANT_COMMIT_PRESSED_ALT_ENTER) {
+ contents->GetController().PruneAllButActive();
+ } else {
+ content::WebContents* active_tab = browser_->GetActiveWebContents();
+ contents->GetController().CopyStateFromAndPrune(
+ &active_tab->GetController());
+ }
+
+ browser_->CommitInstant(contents.Pass(), last_transition_type_,
+ type == INSTANT_COMMIT_PRESSED_ALT_ENTER);
+
+ HideOverlay();
+
+ service_->LogDebugEvent(base::StringPrintf("%p Committed", this));
+
+ return true;
+}
+
+scoped_ptr<content::WebContents> InstantControllerImpl::ReleaseNTPContents() {
+ return scoped_ptr<content::WebContents>();
+}
+
+void InstantControllerImpl::AddModelObserver(InstantModelObserver* observer) {
+ model_->AddObserver(observer);
+}
+
+void InstantControllerImpl::RemoveModelObserver(
+ InstantModelObserver* observer) {
+ model_->RemoveObserver(observer);
+}
+
+// TODO(tonyg): This method only fires when the popup bounds change. It also
+// needs to fire when the overlay bounds change (e.g.: open/close info bar).
+void InstantControllerImpl::SetPopupBounds(const gfx::Rect& bounds) {
+ if (popup_bounds_ == bounds)
+ return;
+
+ popup_bounds_ = bounds;
+
+ if (bounds.height() > last_popup_bounds_.height()) {
+ update_bounds_timer_.Stop();
+ SendPopupBoundsToPage();
+ } else if (!update_bounds_timer_.IsRunning()) {
+ update_bounds_timer_.Start(FROM_HERE,
+ base::TimeDelta::FromMilliseconds(kUpdateBoundsDelayMS),
+ this, &InstantControllerImpl::SendPopupBoundsToPage);
+ }
+}
+
+void InstantControllerImpl::SetOmniboxBounds(const gfx::Rect& /* bounds */) {
+}
+
+void InstantControllerImpl::BookmarkBarStateChanged(bool /* is_detached */) {
+}
+
+void InstantControllerImpl::OmniboxFocusChanged(
+ OmniboxFocusState state,
+ OmniboxFocusChangeReason reason,
+ gfx::NativeView view_gaining_focus) {
+
+ service_->LogDebugEvent(base::StringPrintf("%p OmniboxFocus %s due to %s",
+ this,
+ InstantControllerUtils::FocusStateToString(state).c_str(),
+ InstantControllerUtils::FocusChangeReasonToString(reason).c_str()));
+
+ if (state == OMNIBOX_FOCUS_NONE) {
+ // Don't do anything if we don't have an overlay or if the overlay isn't
+ // showing or if we are in the midst of committing the overlay.
+ if (!overlay_ || !IsOverlayingSearchResults() || !overlay_->contents())
+ return;
+
+ view_gaining_focus =
+ InstantControllerUtils::GetViewGainingFocus(view_gaining_focus);
+ if (InstantControllerUtils::IsViewInContents(view_gaining_focus,
+ overlay_->contents()))
+ CommitIfPossible(INSTANT_COMMIT_FOCUS_LOST);
+ else
+ HideOverlay();
+ } else {
+ service_->loader()->InitContents();
+ }
+}
+
+void InstantControllerImpl::TabDeactivated(
+ content::WebContents* /* contents */) {
+ if (overlay_ && overlay_->contents())
+ HideOverlay();
+}
+
+void InstantControllerImpl::SearchModeChanged(
+ const chrome::search::Mode& /* old_mode */,
+ const chrome::search::Mode& /* new_mode */) {
+}
+
+void InstantControllerImpl::ActiveTabChanged() {
+}
+
+void InstantControllerImpl::SwappedOverlayContents() {
+ if (IsOverlayingSearchResults()) {
+ model_->SetOverlayState(overlay_->contents(),
+ model_->height(), model_->is_height_in_pixels());
+ }
+}
+
+void InstantControllerImpl::FocusedOverlayContents() {
+ if (IsOverlayingSearchResults())
+ browser_->InstantOverlayFocused();
+}
+
+void InstantControllerImpl::RenderViewGone(
+ const content::WebContents* /* contents */) {
+ HideOverlay();
+}
+
+void InstantControllerImpl::InitSearchBox(
+ const content::WebContents* /* contents */) {
+}
+
+void InstantControllerImpl::InstantSupportDetermined(
+ const content::WebContents* /* contents */) {
+ if (!overlay_->page()->supports_instant())
+ HideOverlay();
+ service_->InstantSupportDecided();
+}
+
+void InstantControllerImpl::SetSuggestion(
+ const content::WebContents* /* contents */,
+ const InstantSuggestion& suggestion) {
+ string16 text = suggestion.text;
+
+ if (StartsWith(text, last_omnibox_text_, true)) {
+ text.erase(0, last_omnibox_text_.size());
+ } else if (!InstantControllerUtils::NormalizeAndStripPrefix(
+ &text, last_omnibox_text_)) {
+ text.clear();
+ }
+
+ if (last_verbatim_)
+ text.clear();
+
+ if (!text.empty()) {
+ service_->LogDebugEvent(base::StringPrintf("%p SetSuggestion '%s'",
+ this,
+ UTF16ToUTF8(text).c_str()));
+ browser_->SetInstantSuggestion(
+ InstantSuggestion(text, INSTANT_COMPLETE_NOW, false));
+ }
+
+ model_->SetOverlayState(overlay_->contents(), 100, false);
+}
+
+void InstantControllerImpl::NavigateToURL(
+ const content::WebContents* /* contents */,
+ const GURL& /* url */,
+ content::PageTransition /* transition */,
+ WindowOpenDisposition /* disposition */) {
+}
+
+void InstantControllerImpl::ShowOverlay(
+ const content::WebContents* /* contents */,
+ int /* height */,
+ bool /* is_height_in_pixels */) {
+}
+
+void InstantControllerImpl::StartKeyCapture(
+ const content::WebContents* /* contents */) {
+}
+
+void InstantControllerImpl::StopKeyCapture(
+ const content::WebContents* /* contents */) {
+}
+
+void InstantControllerImpl::DeleteMostVisitedItem(
+ const content::WebContents* /* contents */,
+ const GURL& /* url */) {
+}
+
+void InstantControllerImpl::UndoMostVisitedItemDeletion(
+ const content::WebContents* /* contents */,
+ const GURL& /* url */) {
+}
+
+void InstantControllerImpl::UndoAllMostVisitedItemDeletions(
+ const content::WebContents* /* contents */) {
+}
+
+void InstantControllerImpl::InstantStatusChanged() {
+ HideOverlay();
+}
+
+void InstantControllerImpl::ThemeInfoChanged() {
+}
+
+void InstantControllerImpl::MostVisitedItemsChanged() {
+}
+
+void InstantControllerImpl::InstantSupportDecided() {
+}
+
+void InstantControllerImpl::HideOverlay() {
+ model_->SetOverlayState(NULL, 0, false);
+ last_popup_bounds_ = gfx::Rect();
+ if (overlay_) {
+ overlay_->ReleaseContents();
+ MessageLoop::current()->DeleteSoon(FROM_HERE, overlay_.release());
+ }
+}
+
+void InstantControllerImpl::SendPopupBoundsToPage() {
+ if (!overlay_ || last_popup_bounds_ == popup_bounds_ ||
+ overlay_->is_pointer_down_from_activate())
+ return;
+
+ last_popup_bounds_ = popup_bounds_;
+
+ gfx::Rect overlay_bounds = browser_->GetInstantBounds();
+ gfx::Rect intersection = gfx::IntersectRects(popup_bounds_, overlay_bounds);
+
+ // Translate into window coordinates.
+ if (!intersection.IsEmpty()) {
+ intersection.Offset(-overlay_bounds.origin().x(),
+ -overlay_bounds.origin().y());
+ }
+
+ // In the current Chrome UI, these must always be true so they sanity check
+ // the above operations. In a future UI, these may be removed or adjusted.
+ // There is no point in sanity-checking intersection.y() because the omnibox
+ // can be placed anywhere vertically relative to the overlay (for example, in
+ // Mac fullscreen mode, the omnibox is fully enclosed by the overlay bounds).
+ DCHECK_LE(0, intersection.x());
+ DCHECK_LE(0, intersection.width());
+ DCHECK_LE(0, intersection.height());
+
+ overlay_->page()->PopupBounds(intersection);
+}

Powered by Google App Engine
This is Rietveld 408576698