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, |
sky
2012/07/19 17:55:19
If you're going to have NUM_TYPES, explicitly set
Jered
2012/07/19 22:01:33
Done.
|
+}; |
+ |
+// 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) { |
sky
2012/07/19 17:55:19
It doesn't make sense to have a DCHECK then a cond
Jered
2012/07/19 22:01:33
Done. This ultimately calls chrome::GetActiveTabCo
|
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_. |
sky
2012/07/19 17:55:19
|last_full_text_|
Jered
2012/07/19 22:01:33
Done.
|
+ *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(); |
sky
2012/07/19 17:55:19
Add a comment as to why DeleteLoader is invoked af
Jered
2012/07/19 22:01:33
Done.
|
+ 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) { |
sky
2012/07/19 17:55:19
Why is it safe to nuke all this code?
Jered
2012/07/19 22:01:33
I don't know. I think we now hide instead of destr
sreeram
2012/07/23 16:55:40
Most of the previous code was to destroy the previ
|
- 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() ? |
sky
2012/07/19 17:55:19
If the instant_url_ref doesn't support replacement
Jered
2012/07/19 22:01:33
Done.
|
+ 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(); |
} |