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

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

Issue 10732002: Upstream rewrite of Instant. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Merge test Created 8 years, 5 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.cc
diff --git a/chrome/browser/instant/instant_controller.cc b/chrome/browser/instant/instant_controller.cc
index 629bdc64128516c6ac4796142dfdb7cfbd9b2fca..f94ac3d3ffb0ab111188a2be1ef0c81435760de5 100644
--- a/chrome/browser/instant/instant_controller.cc
+++ b/chrome/browser/instant/instant_controller.cc
@@ -4,10 +4,14 @@
#include "chrome/browser/instant/instant_controller.h"
+#include <string>
+
#include "base/bind.h"
#include "base/command_line.h"
+#include "base/i18n/case_conversion.h"
#include "base/message_loop.h"
#include "base/metrics/histogram.h"
+#include "base/utf_string_conversions.h"
#include "build/build_config.h"
#include "chrome/browser/autocomplete/autocomplete_match.h"
#include "chrome/browser/instant/instant_controller_delegate.h"
@@ -18,7 +22,9 @@
#include "chrome/browser/search_engines/template_url.h"
#include "chrome/browser/search_engines/template_url_service.h"
#include "chrome/browser/search_engines/template_url_service_factory.h"
-#include "chrome/browser/ui/blocked_content/blocked_content_tab_helper.h"
+#include "chrome/browser/ui/search/search_model.h"
+#include "chrome/browser/ui/search/search_tab_helper.h"
+#include "chrome/browser/ui/search/search_types.h"
#include "chrome/browser/ui/tab_contents/tab_contents.h"
#include "chrome/common/chrome_notification_types.h"
#include "chrome/common/chrome_switches.h"
@@ -33,29 +39,71 @@
#include "ui/views/widget/widget.h"
#endif
+namespace {
+
+enum PreviewUsageType {
+ PREVIEW_CREATED,
+ PREVIEW_DELETED,
+ PREVIEW_LOADED,
+ PREVIEW_SHOWED,
+ PREVIEW_COMMITTED,
+ PREVIEW_NUM_TYPES,
+};
+
+// 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;
+
+// The maximum number of times we'll load a non-Instant-supporting search engine
+// before we give up and blacklist it for the rest of the browsing session.
+const int kMaxInstantSupportFailures = 10;
+
+std::string ModeToString(InstantController::Mode mode) {
+ switch (mode) {
+ case InstantController::INSTANT: return "Instant";
+ case InstantController::SUGGEST: return "Suggest";
+ case InstantController::HIDDEN: return "Hidden";
+ case InstantController::SILENT: return "Silent";
+ }
+
+ NOTREACHED();
+ return std::string();
+}
+
+void AddPreviewUsageForHistogram(InstantController::Mode mode,
+ PreviewUsageType usage) {
+ DCHECK(0 <= usage && usage < PREVIEW_NUM_TYPES) << usage;
+ base::Histogram* histogram = base::LinearHistogram::FactoryGet(
+ "Instant.Previews." + ModeToString(mode), 1, PREVIEW_NUM_TYPES,
+ PREVIEW_NUM_TYPES + 1, base::Histogram::kUmaTargetedHistogramFlag);
+ histogram->Add(usage);
+}
+
+} // namespace
+
InstantController::InstantController(InstantControllerDelegate* delegate,
Mode mode)
: delegate_(delegate),
- is_displayable_(false),
- is_out_of_date_(true),
- commit_on_mouse_up_(false),
- last_transition_type_(content::PAGE_TRANSITION_LINK),
- ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)),
- mode_(mode) {
- DCHECK(mode_ == INSTANT || mode_ == SUGGEST || mode_ == HIDDEN ||
- mode_ == SILENT);
+ mode_(mode),
+ last_tab_contents_(NULL),
+ last_verbatim_(false),
+ last_complete_behavior_(INSTANT_COMPLETE_NOW),
+ is_showing_(false),
+ loader_ready_(false) {
}
InstantController::~InstantController() {
+ // We do this explicitly for two reasons: To |Hide| if we are showing the
+ // preview, and for proper accounting of PREVIEW_DELETED in histograms.
+ DeleteLoader();
}
// static
void InstantController::RegisterUserPrefs(PrefService* prefs) {
- prefs->RegisterBooleanPref(prefs::kInstantConfirmDialogShown,
- false,
+ prefs->RegisterBooleanPref(prefs::kInstantConfirmDialogShown, false,
PrefService::SYNCABLE_PREF);
- prefs->RegisterBooleanPref(prefs::kInstantEnabled,
- false,
+ prefs->RegisterBooleanPref(prefs::kInstantEnabled, false,
PrefService::SYNCABLE_PREF);
// TODO(jamescook): Move this to search controller.
@@ -66,7 +114,7 @@ void InstantController::RegisterUserPrefs(PrefService* prefs) {
// static
void InstantController::RecordMetrics(Profile* profile) {
- UMA_HISTOGRAM_ENUMERATION("Instant.Status", IsEnabled(profile), 2);
+ UMA_HISTOGRAM_BOOLEAN("Instant.Status", IsEnabled(profile));
}
// static
@@ -78,434 +126,369 @@ bool InstantController::IsEnabled(Profile* profile) {
// static
void InstantController::Enable(Profile* profile) {
PrefService* prefs = profile->GetPrefs();
- if (!prefs)
- return;
-
- prefs->SetBoolean(prefs::kInstantEnabled, true);
- prefs->SetBoolean(prefs::kInstantConfirmDialogShown, true);
- UMA_HISTOGRAM_ENUMERATION("Instant.Preference", 1, 2);
+ if (prefs) {
+ prefs->SetBoolean(prefs::kInstantEnabled, true);
+ prefs->SetBoolean(prefs::kInstantConfirmDialogShown, true);
+ UMA_HISTOGRAM_BOOLEAN("Instant.Preference", true);
+ }
}
// static
void InstantController::Disable(Profile* profile) {
PrefService* prefs = profile->GetPrefs();
- if (!prefs)
- return;
-
- prefs->SetBoolean(prefs::kInstantEnabled, false);
- UMA_HISTOGRAM_ENUMERATION("Instant.Preference", 0, 2);
+ if (prefs) {
+ prefs->SetBoolean(prefs::kInstantEnabled, false);
+ UMA_HISTOGRAM_BOOLEAN("Instant.Preference", false);
+ }
}
bool InstantController::Update(const AutocompleteMatch& match,
const string16& user_text,
bool verbatim,
- string16* suggested_text) {
- suggested_text->clear();
-
- is_out_of_date_ = false;
- commit_on_mouse_up_ = false;
- last_transition_type_ = match.transition;
- last_url_ = match.destination_url;
- last_user_text_ = user_text;
-
- TabContents* tab_contents = delegate_->GetInstantHostTabContents();
+ string16* suggested_text,
+ InstantCompleteBehavior* complete_behavior) {
+ const TabContents* tab_contents = delegate_->GetInstantHostTabContents();
+ DCHECK(tab_contents);
if (!tab_contents) {
Hide();
return false;
}
+ string16 instant_id;
+ GURL instant_url;
Profile* profile = tab_contents->profile();
- const TemplateURL* template_url = match.GetTemplateURL(profile);
- const TemplateURL* default_t_url =
- TemplateURLServiceFactory::GetForProfile(profile)
- ->GetDefaultSearchProvider();
- if (!IsValidInstantTemplateURL(template_url) || !default_t_url ||
- template_url->id() != default_t_url->id()) {
+
+ // If the match's TemplateURL isn't valid, it likely not a query.
+ if (!GetInstantURL(match.GetTemplateURL(profile), &instant_id,
+ &instant_url)) {
Hide();
return false;
}
- if (!loader_.get() || loader_->template_url_id() != template_url->id())
- loader_.reset(new InstantLoader(this, template_url->id(), std::string()));
-
- if (mode_ == SILENT) {
- // For the SILENT mode, we process |user_text| at commit time.
- loader_->MaybeLoadInstantURL(tab_contents, template_url);
+ // The presence of any suggested_text implies verbatim.
+ DCHECK(suggested_text->empty() || verbatim)
+ << user_text << "|" << *suggested_text;
+
+ ResetLoader(instant_id, instant_url, tab_contents);
+
+ // Track the full omnibox text, so we can send it on commit.
+ last_full_text_ = user_text + *suggested_text;
+
+ // Don't send an update to the loader if the |user_text| hasn't changed.
+ if (user_text == last_user_text_ && verbatim == last_verbatim_) {
+ // Since we are updating |suggested_text|, shouldn't we also update
+ // |last_full_text_|? No. There's no guarantee that our suggestion will
+ // actually be inline autocompleted. For example, it may get trumped by
+ // a history suggestion. If our suggestion does make it, the omnibox will
+ // call |Update| again, at which time we'll update last_full_text_.
+ *suggested_text = last_suggestion_;
+ *complete_behavior = last_complete_behavior_;
+
+ // We need to call |Show| here because of this:
+ // 1. User has typed a query (say Q). Instant overlay is showing results.
+ // 2. User arrows-down to a URL entry or erases all omnibox text. Both of
+ // these cause the overlay to |Hide|.
+ // 3. User arrows-up to Q or types Q again. |last_user_text_| is still Q, so
+ // we don't need to |Update| the loader, but we do need to |Show|.
+ if (loader_ready_ && mode_ == INSTANT)
+ Show();
return true;
}
- UpdateLoader(tab_contents, template_url, match.destination_url,
- match.transition, user_text, verbatim, suggested_text);
+ last_user_text_ = user_text;
+ last_verbatim_ = verbatim;
+ loader_ready_ = false;
+
+ // Reset the last suggestion, as it's no longer valid.
+ suggested_text->clear();
+ last_suggestion_.clear();
+ *complete_behavior = last_complete_behavior_ = INSTANT_COMPLETE_NOW;
+
+ if (mode_ != SILENT) {
+ loader_->Update(UTF16ToUTF8(last_user_text_), last_verbatim_);
+
+ content::NotificationService::current()->Notify(
+ chrome::NOTIFICATION_INSTANT_CONTROLLER_UPDATED,
+ content::Source<InstantController>(this),
+ content::NotificationService::NoDetails());
+ }
- content::NotificationService::current()->Notify(
- chrome::NOTIFICATION_INSTANT_CONTROLLER_UPDATED,
- content::Source<InstantController>(this),
- content::NotificationService::NoDetails());
return true;
}
+// TODO(tonyg): This method only fires when the omnibox bounds change. It also
+// needs to fire when the preview bounds change (e.g.: open/close info bar).
void InstantController::SetOmniboxBounds(const gfx::Rect& bounds) {
- if (omnibox_bounds_ == bounds)
+ if (omnibox_bounds_ == bounds || mode_ != INSTANT)
return;
- // Always track the omnibox bounds. That way if Update is later invoked the
- // bounds are in sync.
omnibox_bounds_ = bounds;
-
- if (loader_.get() && !is_out_of_date_ && mode_ == INSTANT)
- loader_->SetOmniboxBounds(bounds);
-}
-
-void InstantController::DestroyPreviewContents() {
- if (!loader_.get()) {
- // We're not showing anything, nothing to do.
- return;
+ if (omnibox_bounds_.height() > last_omnibox_bounds_.height()) {
+ update_bounds_timer_.Stop();
+ SendBoundsToPage();
+ } else if (!update_bounds_timer_.IsRunning()) {
+ update_bounds_timer_.Start(FROM_HERE,
+ base::TimeDelta::FromMilliseconds(kUpdateBoundsDelayMS), this,
+ &InstantController::SendBoundsToPage);
}
+}
- if (is_displayable_) {
- is_displayable_ = false;
- delegate_->HideInstant();
- }
- delete ReleasePreviewContents(INSTANT_COMMIT_DESTROY, NULL);
+TabContents* InstantController::GetPreviewContents() const {
+ return loader_.get() ? loader_->preview_contents() : NULL;
}
void InstantController::Hide() {
- is_out_of_date_ = true;
- commit_on_mouse_up_ = false;
- if (is_displayable_) {
- is_displayable_ = false;
+ last_tab_contents_ = NULL;
+ if (is_showing_) {
+ is_showing_ = false;
delegate_->HideInstant();
}
}
bool InstantController::IsCurrent() const {
- // TODO(mmenke): See if we can do something more intelligent in the
- // navigation pending case.
- return is_displayable_ && !loader_->IsNavigationPending() &&
- !loader_->needs_reload();
-}
-
-bool InstantController::PrepareForCommit() {
- if (is_out_of_date_ || !loader_.get())
- return false;
-
- // If we are in the visible (INSTANT) mode, return the status of the preview.
- if (mode_ == INSTANT)
- return IsCurrent();
-
- TabContents* tab_contents = delegate_->GetInstantHostTabContents();
- if (!tab_contents)
- return false;
-
- const TemplateURL* template_url =
- TemplateURLServiceFactory::GetForProfile(tab_contents->profile())
- ->GetDefaultSearchProvider();
- if (!IsValidInstantTemplateURL(template_url) ||
- loader_->template_url_id() != template_url->id() ||
- loader_->IsNavigationPending() ||
- loader_->is_determining_if_page_supports_instant()) {
- return false;
- }
-
- // In the SUGGEST and HIDDEN modes, we must have sent an Update() by now, so
- // check if the loader failed to process it.
- if ((mode_ == SUGGEST || mode_ == HIDDEN)
- && (!loader_->ready() || !loader_->http_status_ok())) {
- return false;
- }
-
- // Ignore the suggested text, as we are about to commit the verbatim query.
- string16 suggested_text;
- UpdateLoader(tab_contents, template_url, last_url_, last_transition_type_,
- last_user_text_, true, &suggested_text);
- return true;
+ DCHECK(IsOutOfDate() || loader_.get());
+ return !IsOutOfDate() && loader_.get() && loader_->supports_instant() &&
+ !last_full_text_.empty();
}
TabContents* InstantController::CommitCurrentPreview(InstantCommitType type) {
- DCHECK(loader_.get());
- TabContents* tab_contents = delegate_->GetInstantHostTabContents();
- DCHECK(tab_contents);
- TabContents* preview = ReleasePreviewContents(type, tab_contents);
+ const TabContents* tab_contents = delegate_->GetInstantHostTabContents();
+ TabContents* preview = ReleasePreviewContents(type);
preview->web_contents()->GetController().CopyStateFromAndPrune(
&tab_contents->web_contents()->GetController());
delegate_->CommitInstant(preview);
- CompleteRelease(preview);
return preview;
}
-bool InstantController::CommitIfCurrent() {
- if (IsCurrent()) {
- CommitCurrentPreview(INSTANT_COMMIT_PRESSED_ENTER);
- return true;
- }
- return false;
+TabContents* InstantController::ReleasePreviewContents(InstantCommitType type) {
+ std::string full_text = UTF16ToUTF8(last_full_text_);
+ TabContents* tab_contents = loader_->ReleasePreviewContents(type, full_text);
+ AddPreviewUsageForHistogram(mode_, PREVIEW_COMMITTED);
+ // We may have gotten here from |CommitInstant|, which means the loader may
+ // still be on the stack. So, schedule a destruction for later.
+ MessageLoop::current()->DeleteSoon(FROM_HERE, loader_.release());
+ DeleteLoader();
+ return tab_contents;
}
-void InstantController::SetCommitOnMouseUp() {
- commit_on_mouse_up_ = true;
-}
-
-bool InstantController::IsMouseDownFromActivate() {
- DCHECK(loader_.get());
- return loader_->IsMouseDownFromActivate();
-}
-
-#if defined(OS_MACOSX)
-void InstantController::OnAutocompleteLostFocus(
- gfx::NativeView view_gaining_focus) {
- // If |IsMouseDownFromActivate()| returns false, the RenderWidgetHostView did
- // not receive a mouseDown event. Therefore, we should destroy the preview.
- // Otherwise, the RWHV was clicked, so we commit the preview.
- if (!IsCurrent() || !IsMouseDownFromActivate())
- DestroyPreviewContents();
- else
- SetCommitOnMouseUp();
-}
-#else
-void InstantController::OnAutocompleteLostFocus(
- gfx::NativeView view_gaining_focus) {
- if (!IsCurrent()) {
- DestroyPreviewContents();
- return;
- }
-
- content::RenderWidgetHostView* rwhv =
- GetPreviewContents()->web_contents()->GetRenderWidgetHostView();
- if (!view_gaining_focus || !rwhv) {
- DestroyPreviewContents();
- return;
- }
-
-#if defined(TOOLKIT_VIEWS)
- // For views the top level widget is always focused. If the focus change
- // originated in views determine the child Widget from the view that is being
- // focused.
- views::Widget* widget =
- views::Widget::GetWidgetForNativeView(view_gaining_focus);
- if (widget) {
- views::FocusManager* focus_manager = widget->GetFocusManager();
- if (focus_manager && focus_manager->is_changing_focus() &&
- focus_manager->GetFocusedView() &&
- focus_manager->GetFocusedView()->GetWidget()) {
- view_gaining_focus =
- focus_manager->GetFocusedView()->GetWidget()->GetNativeView();
- }
- }
-#endif
-
- gfx::NativeView tab_view =
- GetPreviewContents()->web_contents()->GetNativeView();
- // Focus is going to the renderer.
- if (rwhv->GetNativeView() == view_gaining_focus ||
- tab_view == view_gaining_focus) {
- if (!IsMouseDownFromActivate()) {
- // If the mouse is not down, focus is not going to the renderer. Someone
- // else moved focus and we shouldn't commit.
- DestroyPreviewContents();
- return;
- }
-
- // We're showing instant results. As instant results may shift when
- // committing we commit on the mouse up. This way a slow click still works
- // fine.
- SetCommitOnMouseUp();
- return;
- }
+void InstantController::OnAutocompleteLostFocus() {
+ DCHECK(!is_showing_ || loader_.get());
- // Walk up the view hierarchy. If the view gaining focus is a subview of the
- // WebContents view (such as a windowed plugin or http auth dialog), we want
- // to keep the preview contents. Otherwise, focus has gone somewhere else,
- // such as the JS inspector, and we want to cancel the preview.
- gfx::NativeView view_gaining_focus_ancestor = view_gaining_focus;
- while (view_gaining_focus_ancestor &&
- view_gaining_focus_ancestor != tab_view) {
- view_gaining_focus_ancestor =
- platform_util::GetParent(view_gaining_focus_ancestor);
- }
-
- if (view_gaining_focus_ancestor) {
- CommitCurrentPreview(INSTANT_COMMIT_FOCUS_LOST);
- return;
- }
-
- DestroyPreviewContents();
+ // TODO(sreeram): Since we never delete the loader except when committing
+ // Instant, the loader may have a very stale page. Reload it when stale.
+ if (is_showing_ && loader_.get() && !loader_->IsMouseDownFromActivate())
+ Hide();
}
-#endif
void InstantController::OnAutocompleteGotFocus() {
- TabContents* tab_contents = delegate_->GetInstantHostTabContents();
+ const TabContents* tab_contents = delegate_->GetInstantHostTabContents();
+
+ // We could get here with no underlying tab if the Browser is in the process
+ // of closing.
if (!tab_contents)
return;
+ // Since we don't have any autocomplete match to work with, we'll just use
+ // the default search provider's Instant URL.
const TemplateURL* template_url =
- TemplateURLServiceFactory::GetForProfile(tab_contents->profile())
- ->GetDefaultSearchProvider();
- if (!IsValidInstantTemplateURL(template_url))
- return;
-
- if (!loader_.get() || loader_->template_url_id() != template_url->id())
- loader_.reset(new InstantLoader(this, template_url->id(), std::string()));
- loader_->MaybeLoadInstantURL(tab_contents, template_url);
-}
+ TemplateURLServiceFactory::GetForProfile(tab_contents->profile())->
+ GetDefaultSearchProvider();
-TabContents* InstantController::ReleasePreviewContents(
- InstantCommitType type,
- TabContents* current_tab) {
- if (!loader_.get())
- return NULL;
-
- TabContents* tab = loader_->ReleasePreviewContents(type, current_tab);
- ClearBlacklist();
- is_out_of_date_ = true;
- is_displayable_ = false;
- commit_on_mouse_up_ = false;
- omnibox_bounds_ = gfx::Rect();
- loader_.reset();
- return tab;
-}
-
-void InstantController::CompleteRelease(TabContents* tab) {
- tab->blocked_content_tab_helper()->SetAllContentsBlocked(false);
-}
+ string16 instant_id;
+ GURL instant_url;
+ if (!GetInstantURL(template_url, &instant_id, &instant_url))
+ return;
-TabContents* InstantController::GetPreviewContents() const {
- return loader_.get() ? loader_->preview_contents() : NULL;
+ ResetLoader(instant_id, instant_url, tab_contents);
}
-void InstantController::InstantStatusChanged(InstantLoader* loader) {
- DCHECK(loader_.get());
- UpdateIsDisplayable();
+bool InstantController::commit_on_mouse_up() const {
+ return loader_.get() && loader_->IsMouseDownFromActivate();
}
-void InstantController::SetSuggestedTextFor(
+void InstantController::SetSuggestions(
InstantLoader* loader,
- const string16& text,
+ const std::vector<std::string>& suggestions,
InstantCompleteBehavior behavior) {
- if (is_out_of_date_)
+ DCHECK_EQ(loader_.get(), loader);
+ if (loader_ != loader || IsOutOfDate() || mode_ == SILENT || mode_ == HIDDEN)
return;
- if (mode_ == INSTANT || mode_ == SUGGEST)
- delegate_->SetSuggestedText(text, behavior);
-}
+ string16 suggestion;
+ if (!suggestions.empty()) {
+ suggestion = UTF8ToUTF16(suggestions[0]);
+ }
-gfx::Rect InstantController::GetInstantBounds() {
- return delegate_->GetInstantBounds();
-}
+ string16 suggestion_lower = base::i18n::ToLower(suggestion);
+ string16 user_text_lower = base::i18n::ToLower(last_user_text_);
+ if (user_text_lower.size() >= suggestion_lower.size() ||
+ suggestion_lower.compare(0, user_text_lower.size(), user_text_lower)) {
+ suggestion.clear();
+ } else {
+ suggestion.erase(0, last_user_text_.size());
+ }
+ last_suggestion_ = suggestion;
+ last_complete_behavior_ = behavior;
+ if (!last_verbatim_)
+ delegate_->SetSuggestedText(suggestion, behavior);
-bool InstantController::ShouldCommitInstantOnMouseUp() {
- return commit_on_mouse_up_;
+ TabContents* tab_contents = delegate_->GetInstantHostTabContents();
+ DCHECK(tab_contents);
+ if (mode_ != SUGGEST && tab_contents &&
+ !tab_contents->search_tab_helper()->model()->mode().is_ntp()) {
+ Show();
+ }
+
+ loader_ready_ = true;
}
void InstantController::CommitInstantLoader(InstantLoader* loader) {
- if (loader_.get() && loader_.get() == loader) {
- CommitCurrentPreview(INSTANT_COMMIT_FOCUS_LOST);
- } else {
- // This can happen if the mouse was down, we swapped out the preview and
- // the mouse was released. Generally this shouldn't happen, but if it does
- // revert.
- DestroyPreviewContents();
- }
-}
+ DCHECK_EQ(loader_.get(), loader);
+ DCHECK(is_showing_ && !IsOutOfDate()) << is_showing_;
+ if (loader_ != loader || !is_showing_ || IsOutOfDate())
+ return;
-void InstantController::InstantLoaderDoesntSupportInstant(
- InstantLoader* loader) {
- VLOG(1) << "provider does not support instant";
+ CommitCurrentPreview(INSTANT_COMMIT_FOCUS_LOST);
+}
- // Don't attempt to use instant for this search engine again.
- BlacklistFromInstant();
+void InstantController::InstantLoaderPreviewLoaded(InstantLoader* loader) {
+ DCHECK_EQ(loader_.get(), loader);
+ AddPreviewUsageForHistogram(mode_, PREVIEW_LOADED);
}
-void InstantController::AddToBlacklist(InstantLoader* loader, const GURL& url) {
- // Don't attempt to use instant for this search engine again.
- BlacklistFromInstant();
+void InstantController::InstantSupportDetermined(InstantLoader* loader,
+ bool supports_instant) {
+ DCHECK_EQ(loader_.get(), loader);
+ if (supports_instant) {
+ blacklisted_ids_.erase(loader->loader_id());
+ } else {
+ ++blacklisted_ids_[loader->loader_id()];
+ if (loader_ == loader) {
+ // Because of the state of the stack, we can't destroy the loader now.
+ if (GetPreviewContents())
+ AddPreviewUsageForHistogram(mode_, PREVIEW_DELETED);
+ MessageLoop::current()->DeleteSoon(FROM_HERE, loader_.release());
+ DeleteLoader();
+ }
+ }
}
void InstantController::SwappedTabContents(InstantLoader* loader) {
- if (is_displayable_)
- delegate_->ShowInstant(loader->preview_contents());
+ DCHECK_EQ(loader_.get(), loader);
+ if (loader_ == loader && is_showing_)
+ delegate_->ShowInstant();
}
-void InstantController::InstantLoaderContentsFocused() {
+void InstantController::InstantLoaderContentsFocused(InstantLoader* loader) {
+ DCHECK_EQ(loader_.get(), loader);
+ DCHECK(is_showing_ && !IsOutOfDate()) << is_showing_;
#if defined(USE_AURA)
// On aura the omnibox only receives a focus lost if we initiate the focus
// change. This does that.
- if (mode_ == INSTANT)
+ if (is_showing_ && !IsOutOfDate())
delegate_->InstantPreviewFocused();
#endif
}
-void InstantController::UpdateIsDisplayable() {
- bool displayable = !is_out_of_date_ && loader_.get() && loader_->ready() &&
- loader_->http_status_ok();
- if (displayable == is_displayable_ || mode_ != INSTANT)
- return;
+void InstantController::ResetLoader(const string16& instant_id,
+ const GURL& instant_url,
+ const TabContents* tab_contents) {
+ if (loader_.get() && loader_->loader_id() != instant_id)
+ DeleteLoader();
- is_displayable_ = displayable;
- if (!is_displayable_) {
- delegate_->HideInstant();
- } else {
- delegate_->ShowInstant(loader_->preview_contents());
- content::NotificationService::current()->Notify(
- chrome::NOTIFICATION_INSTANT_CONTROLLER_SHOWN,
- content::Source<InstantController>(this),
- content::NotificationService::NoDetails());
+ if (!loader_.get()) {
+ loader_.reset(
+ new InstantLoader(this, instant_id, instant_url, tab_contents));
+ AddPreviewUsageForHistogram(mode_, PREVIEW_CREATED);
}
+
+ last_tab_contents_ = tab_contents;
}
-void InstantController::UpdateLoader(TabContents* tab_contents,
- const TemplateURL* template_url,
- const GURL& url,
- content::PageTransition transition_type,
- const string16& user_text,
- bool verbatim,
- string16* suggested_text) {
- if (mode_ == INSTANT)
- loader_->SetOmniboxBounds(omnibox_bounds_);
- loader_->Update(tab_contents, template_url, url, transition_type, user_text,
- verbatim, suggested_text);
- UpdateIsDisplayable();
- // For the HIDDEN and SILENT modes, don't send back suggestions.
- if (mode_ == HIDDEN || mode_ == SILENT)
- suggested_text->clear();
+void InstantController::DeleteLoader() {
+ Hide();
+ last_full_text_.clear();
+ last_user_text_.clear();
+ last_verbatim_ = false;
+ last_suggestion_.clear();
+ last_complete_behavior_ = INSTANT_COMPLETE_NOW;
+ last_omnibox_bounds_ = gfx::Rect();
+ if (GetPreviewContents())
+ AddPreviewUsageForHistogram(mode_, PREVIEW_DELETED);
+ loader_.reset();
}
-// Returns true if |template_url| is a valid TemplateURL for use by instant.
-bool InstantController::IsValidInstantTemplateURL(
- const TemplateURL* template_url) {
- return template_url && template_url->id() &&
- template_url->instant_url_ref().SupportsReplacement() &&
- !IsBlacklistedFromInstant(template_url->id());
+void InstantController::Show() {
+ if (!is_showing_) {
+ is_showing_ = true;
+ delegate_->ShowInstant();
+ AddPreviewUsageForHistogram(mode_, PREVIEW_SHOWED);
+ }
}
-void InstantController::BlacklistFromInstant() {
- if (!loader_.get())
+void InstantController::SendBoundsToPage() {
+ if (last_omnibox_bounds_ == omnibox_bounds_ || IsOutOfDate() ||
+ !GetPreviewContents() || loader_->IsMouseDownFromActivate()) {
return;
+ }
- DCHECK(loader_->template_url_id());
- blacklisted_ids_.insert(loader_->template_url_id());
+ last_omnibox_bounds_ = omnibox_bounds_;
+ gfx::Rect preview_bounds = delegate_->GetInstantBounds();
+ gfx::Rect intersection = omnibox_bounds_.Intersect(preview_bounds);
- // Because of the state of the stack we can't destroy the loader now.
- ScheduleDestroy(loader_.release());
- UpdateIsDisplayable();
-}
+ // Translate into window coordinates.
+ if (!intersection.IsEmpty()) {
+ intersection.Offset(-preview_bounds.origin().x(),
+ -preview_bounds.origin().y());
+ }
-bool InstantController::IsBlacklistedFromInstant(TemplateURLID id) {
- return blacklisted_ids_.count(id) > 0;
-}
+ // 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 preview (for example, in
+ // Mac fullscreen mode, the omnibox is fully enclosed by the preview bounds).
+ DCHECK_LE(0, intersection.x());
+ DCHECK_LE(0, intersection.width());
+ DCHECK_LE(0, intersection.height());
+
+ loader_->SetOmniboxBounds(intersection);
+}
+
+bool InstantController::GetInstantURL(const TemplateURL* template_url,
+ string16* instant_id,
+ GURL* instant_url) const {
+ CommandLine* command_line = CommandLine::ForCurrentProcess();
+ if (command_line->HasSwitch(switches::kInstantURL)) {
+ *instant_url =
+ GURL(command_line->GetSwitchValueASCII(switches::kInstantURL));
+ instant_id->clear();
+ return true;
+ }
-void InstantController::ClearBlacklist() {
- blacklisted_ids_.clear();
-}
+ if (!template_url)
+ return false;
-void InstantController::ScheduleDestroy(InstantLoader* loader) {
- loaders_to_destroy_.push_back(loader);
- if (!weak_factory_.HasWeakPtrs()) {
- MessageLoop::current()->PostTask(
- FROM_HERE, base::Bind(&InstantController::DestroyLoaders,
- weak_factory_.GetWeakPtr()));
+ std::map<string16, int>::const_iterator iter =
+ blacklisted_ids_.find(template_url->keyword());
+ if (iter != blacklisted_ids_.end() &&
+ iter->second > kMaxInstantSupportFailures) {
+ return false;
}
+
+ const TemplateURLRef& instant_url_ref = template_url->instant_url_ref();
+ if (!instant_url_ref.IsValid())
+ return false;
+
+ *instant_id = template_url->keyword();
+ *instant_url = instant_url_ref.SupportsReplacement() ?
+ GURL(instant_url_ref.ReplaceSearchTerms(
+ TemplateURLRef::SearchTermsArgs(string16()))) :
+ GURL(instant_url_ref.GetURL());
+
+ return true;
}
-void InstantController::DestroyLoaders() {
- loaders_to_destroy_.reset();
+bool InstantController::IsOutOfDate() const {
+ return !last_tab_contents_ ||
+ last_tab_contents_ != delegate_->GetInstantHostTabContents();
}

Powered by Google App Engine
This is Rietveld 408576698