| // Copyright (c) 2011 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.h" |
| |
| #include "base/command_line.h" |
| #include "base/message_loop.h" |
| #include "base/metrics/histogram.h" |
| #include "build/build_config.h" |
| #include "chrome/browser/autocomplete/autocomplete_match.h" |
| #include "chrome/browser/instant/instant_delegate.h" |
| #include "chrome/browser/instant/instant_loader.h" |
| #include "chrome/browser/instant/instant_loader_manager.h" |
| #include "chrome/browser/instant/promo_counter.h" |
| #include "chrome/browser/platform_util.h" |
| #include "chrome/browser/prefs/pref_service.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/search_engines/template_url.h" |
| #include "chrome/browser/search_engines/template_url_model.h" |
| #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" |
| #include "chrome/common/chrome_switches.h" |
| #include "chrome/common/pref_names.h" |
| #include "chrome/common/url_constants.h" |
| #include "content/browser/renderer_host/render_widget_host_view.h" |
| #include "content/browser/tab_contents/tab_contents.h" |
| #include "content/common/notification_service.h" |
| |
| // Number of ms to delay between loading urls. |
| static const int kUpdateDelayMS = 200; |
| |
| // Amount of time we delay before showing pages that have a non-200 status. |
| static const int kShowDelayMS = 800; |
| |
| // static |
| InstantController::HostBlacklist* InstantController::host_blacklist_ = NULL; |
| |
| InstantController::InstantController(Profile* profile, |
| InstantDelegate* delegate) |
| : delegate_(delegate), |
| tab_contents_(NULL), |
| is_active_(false), |
| displayable_loader_(NULL), |
| commit_on_mouse_up_(false), |
| last_transition_type_(PageTransition::LINK), |
| ALLOW_THIS_IN_INITIALIZER_LIST(destroy_factory_(this)) { |
| PrefService* service = profile->GetPrefs(); |
| if (service) { |
| // kInstantWasEnabledOnce was added after instant, set it now to make sure |
| // it is correctly set. |
| service->SetBoolean(prefs::kInstantEnabledOnce, true); |
| } |
| } |
| |
| InstantController::~InstantController() { |
| } |
| |
| // static |
| void InstantController::RegisterUserPrefs(PrefService* prefs) { |
| prefs->RegisterBooleanPref(prefs::kInstantConfirmDialogShown, false); |
| prefs->RegisterBooleanPref(prefs::kInstantEnabled, false); |
| prefs->RegisterBooleanPref(prefs::kInstantEnabledOnce, false); |
| prefs->RegisterInt64Pref(prefs::kInstantEnabledTime, false); |
| PromoCounter::RegisterUserPrefs(prefs, prefs::kInstantPromo); |
| } |
| |
| // static |
| void InstantController::RecordMetrics(Profile* profile) { |
| if (!IsEnabled(profile)) |
| return; |
| |
| PrefService* service = profile->GetPrefs(); |
| if (service) { |
| int64 enable_time = service->GetInt64(prefs::kInstantEnabledTime); |
| if (!enable_time) { |
| service->SetInt64(prefs::kInstantEnabledTime, |
| base::Time::Now().ToInternalValue()); |
| } else { |
| base::TimeDelta delta = |
| base::Time::Now() - base::Time::FromInternalValue(enable_time); |
| // Histogram from 1 hour to 30 days. |
| UMA_HISTOGRAM_CUSTOM_COUNTS("Instant.EnabledTime.Predictive", |
| delta.InHours(), 1, 30 * 24, 50); |
| } |
| } |
| } |
| |
| // static |
| bool InstantController::IsEnabled(Profile* profile) { |
| PrefService* prefs = profile->GetPrefs(); |
| return prefs->GetBoolean(prefs::kInstantEnabled); |
| } |
| |
| // static |
| void InstantController::Enable(Profile* profile) { |
| PromoCounter* promo_counter = profile->GetInstantPromoCounter(); |
| if (promo_counter) |
| promo_counter->Hide(); |
| |
| PrefService* service = profile->GetPrefs(); |
| if (!service) |
| return; |
| |
| service->SetBoolean(prefs::kInstantEnabled, true); |
| service->SetBoolean(prefs::kInstantConfirmDialogShown, true); |
| service->SetInt64(prefs::kInstantEnabledTime, |
| base::Time::Now().ToInternalValue()); |
| service->SetBoolean(prefs::kInstantEnabledOnce, true); |
| } |
| |
| // static |
| void InstantController::Disable(Profile* profile) { |
| PrefService* service = profile->GetPrefs(); |
| if (!service || !IsEnabled(profile)) |
| return; |
| |
| int64 enable_time = service->GetInt64(prefs::kInstantEnabledTime); |
| if (enable_time) { |
| base::TimeDelta delta = |
| base::Time::Now() - base::Time::FromInternalValue(enable_time); |
| // Histogram from 1 minute to 10 days. |
| UMA_HISTOGRAM_CUSTOM_COUNTS("Instant.TimeToDisable.Predictive", |
| delta.InMinutes(), 1, 60 * 24 * 10, 50); |
| } |
| |
| service->SetBoolean(prefs::kInstantEnabled, false); |
| } |
| |
| // static |
| bool InstantController::CommitIfCurrent(InstantController* controller) { |
| if (controller && controller->IsCurrent()) { |
| controller->CommitCurrentPreview(INSTANT_COMMIT_PRESSED_ENTER); |
| return true; |
| } |
| return false; |
| } |
| |
| void InstantController::Update(TabContentsWrapper* tab_contents, |
| const AutocompleteMatch& match, |
| const string16& user_text, |
| bool verbatim, |
| string16* suggested_text) { |
| suggested_text->clear(); |
| |
| if (tab_contents != tab_contents_) |
| DestroyPreviewContents(); |
| |
| const GURL& url = match.destination_url; |
| tab_contents_ = tab_contents; |
| commit_on_mouse_up_ = false; |
| last_transition_type_ = match.transition; |
| const TemplateURL* template_url = NULL; |
| |
| if (url.is_empty() || !url.is_valid()) { |
| // Assume we were invoked with GURL() and should destroy all. |
| DestroyPreviewContents(); |
| return; |
| } |
| |
| if (!ShouldShowPreviewFor(match, &template_url)) { |
| DestroyPreviewContentsAndLeaveActive(); |
| return; |
| } |
| |
| if (!loader_manager_.get()) |
| loader_manager_.reset(new InstantLoaderManager(this)); |
| |
| if (!is_active_) { |
| is_active_ = true; |
| delegate_->PrepareForInstant(); |
| } |
| |
| TemplateURLID template_url_id = template_url ? template_url->id() : 0; |
| // Verbatim only makes sense if the search engines supports instant. |
| bool real_verbatim = template_url_id ? verbatim : false; |
| |
| if (ShouldUpdateNow(template_url_id, match.destination_url)) { |
| UpdateLoader(template_url, match.destination_url, match.transition, |
| user_text, real_verbatim, suggested_text); |
| } else { |
| ScheduleUpdate(match.destination_url); |
| } |
| |
| NotificationService::current()->Notify( |
| NotificationType::INSTANT_CONTROLLER_UPDATED, |
| Source<InstantController>(this), |
| NotificationService::NoDetails()); |
| } |
| |
| void InstantController::SetOmniboxBounds(const gfx::Rect& bounds) { |
| if (omnibox_bounds_ == bounds) |
| return; |
| |
| // Always track the omnibox bounds. That way if Update is later invoked the |
| // bounds are in sync. |
| omnibox_bounds_ = bounds; |
| if (loader_manager_.get()) { |
| if (loader_manager_->current_loader()) |
| loader_manager_->current_loader()->SetOmniboxBounds(bounds); |
| if (loader_manager_->pending_loader()) |
| loader_manager_->pending_loader()->SetOmniboxBounds(bounds); |
| } |
| } |
| |
| void InstantController::DestroyPreviewContents() { |
| if (!loader_manager_.get()) { |
| // We're not showing anything, nothing to do. |
| return; |
| } |
| |
| // ReleasePreviewContents sets is_active_ to false, but we need to set it |
| // before notifying the delegate, otherwise if the delegate asks for the state |
| // we'll still be active. |
| is_active_ = false; |
| delegate_->HideInstant(); |
| delete ReleasePreviewContents(INSTANT_COMMIT_DESTROY); |
| } |
| |
| void InstantController::DestroyPreviewContentsAndLeaveActive() { |
| commit_on_mouse_up_ = false; |
| if (displayable_loader_) { |
| displayable_loader_ = NULL; |
| delegate_->HideInstant(); |
| } |
| |
| // TODO(sky): this shouldn't nuke the loader. It should just nuke non-instant |
| // loaders and hide instant loaders. |
| loader_manager_.reset(new InstantLoaderManager(this)); |
| show_timer_.Stop(); |
| update_timer_.Stop(); |
| } |
| |
| bool InstantController::IsCurrent() { |
| return loader_manager_.get() && loader_manager_->active_loader() && |
| loader_manager_->active_loader()->ready() && |
| !loader_manager_->active_loader()->needs_reload() && |
| !update_timer_.IsRunning(); |
| } |
| |
| void InstantController::CommitCurrentPreview(InstantCommitType type) { |
| if (type == INSTANT_COMMIT_PRESSED_ENTER && show_timer_.IsRunning()) { |
| // The user pressed enter and the show timer is running. This means the |
| // pending_loader returned an error code and we're not showing it. Force it |
| // to be shown. |
| show_timer_.Stop(); |
| ShowTimerFired(); |
| } |
| DCHECK(loader_manager_.get()); |
| DCHECK(loader_manager_->current_loader()); |
| bool showing_instant = |
| loader_manager_->current_loader()->is_showing_instant(); |
| TabContentsWrapper* tab = ReleasePreviewContents(type); |
| // If the loader was showing an instant page then it's navigation stack is |
| // something like: search-engine-home-page (eg google.com) search-term1 |
| // search-term2 .... Each search-term navigation corresponds to the page |
| // deciding enough time has passed to commit a navigation. We don't want the |
| // searche-engine-home-page navigation in this case so we pass true to |
| // CopyStateFromAndPrune to have the search-engine-home-page navigation |
| // removed. |
| tab->controller().CopyStateFromAndPrune( |
| &tab_contents_->controller(), showing_instant); |
| delegate_->CommitInstant(tab); |
| CompleteRelease(tab->tab_contents()); |
| } |
| |
| void InstantController::SetCommitOnMouseUp() { |
| commit_on_mouse_up_ = true; |
| } |
| |
| bool InstantController::IsMouseDownFromActivate() { |
| DCHECK(loader_manager_.get()); |
| DCHECK(loader_manager_->current_loader()); |
| return loader_manager_->current_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 (!is_displayable() || !GetPreviewContents() || |
| !IsMouseDownFromActivate()) { |
| DestroyPreviewContents(); |
| } else if (IsShowingInstant()) { |
| SetCommitOnMouseUp(); |
| } else { |
| CommitCurrentPreview(INSTANT_COMMIT_FOCUS_LOST); |
| } |
| } |
| #else |
| void InstantController::OnAutocompleteLostFocus( |
| gfx::NativeView view_gaining_focus) { |
| if (!is_active() || !GetPreviewContents()) { |
| DestroyPreviewContents(); |
| return; |
| } |
| |
| RenderWidgetHostView* rwhv = |
| GetPreviewContents()->tab_contents()->GetRenderWidgetHostView(); |
| if (!view_gaining_focus || !rwhv) { |
| DestroyPreviewContents(); |
| return; |
| } |
| |
| gfx::NativeView tab_view = |
| GetPreviewContents()->tab_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; |
| } |
| |
| if (IsShowingInstant()) { |
| // 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; |
| } |
| |
| CommitCurrentPreview(INSTANT_COMMIT_FOCUS_LOST); |
| return; |
| } |
| |
| // Walk up the view hierarchy. If the view gaining focus is a subview of the |
| // TabContents 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(); |
| } |
| #endif |
| |
| TabContentsWrapper* InstantController::ReleasePreviewContents( |
| InstantCommitType type) { |
| if (!loader_manager_.get()) |
| return NULL; |
| |
| // Make sure the pending loader is active. Ideally we would call |
| // ShowTimerFired, but if Release is invoked from the browser we don't want to |
| // attempt to show the tab contents (since its being added to a new tab). |
| if (type == INSTANT_COMMIT_PRESSED_ENTER && show_timer_.IsRunning()) { |
| InstantLoader* loader = loader_manager_->active_loader(); |
| if (loader && loader->ready() && |
| loader == loader_manager_->pending_loader()) { |
| scoped_ptr<InstantLoader> old_loader; |
| loader_manager_->MakePendingCurrent(&old_loader); |
| } |
| } |
| |
| // Loader may be null if the url blacklisted instant. |
| scoped_ptr<InstantLoader> loader; |
| if (loader_manager_->current_loader()) |
| loader.reset(loader_manager_->ReleaseCurrentLoader()); |
| TabContentsWrapper* tab = loader.get() ? |
| loader->ReleasePreviewContents(type) : NULL; |
| |
| ClearBlacklist(); |
| is_active_ = false; |
| displayable_loader_ = NULL; |
| commit_on_mouse_up_ = false; |
| omnibox_bounds_ = gfx::Rect(); |
| loader_manager_.reset(); |
| update_timer_.Stop(); |
| show_timer_.Stop(); |
| return tab; |
| } |
| |
| void InstantController::CompleteRelease(TabContents* tab) { |
| tab->SetAllContentsBlocked(false); |
| } |
| |
| TabContentsWrapper* InstantController::GetPreviewContents() { |
| return loader_manager_.get() && loader_manager_->current_loader() ? |
| loader_manager_->current_loader()->preview_contents() : NULL; |
| } |
| |
| bool InstantController::IsShowingInstant() { |
| return loader_manager_.get() && loader_manager_->current_loader() && |
| loader_manager_->current_loader()->is_showing_instant(); |
| } |
| |
| bool InstantController::MightSupportInstant() { |
| return loader_manager_.get() && loader_manager_->active_loader() && |
| loader_manager_->active_loader()->is_showing_instant(); |
| } |
| |
| GURL InstantController::GetCurrentURL() { |
| return loader_manager_.get() && loader_manager_->active_loader() ? |
| loader_manager_->active_loader()->url() : GURL(); |
| } |
| |
| void InstantController::InstantStatusChanged(InstantLoader* loader) { |
| if (!loader->http_status_ok()) { |
| // Status isn't ok, start a timer that when fires shows the result. This |
| // delays showing 403 pages and the like. |
| show_timer_.Stop(); |
| show_timer_.Start( |
| base::TimeDelta::FromMilliseconds(kShowDelayMS), |
| this, &InstantController::ShowTimerFired); |
| UpdateDisplayableLoader(); |
| return; |
| } |
| |
| ProcessInstantStatusChanged(loader); |
| } |
| |
| void InstantController::SetSuggestedTextFor( |
| InstantLoader* loader, |
| const string16& text, |
| InstantCompleteBehavior behavior) { |
| if (loader_manager_->current_loader() == loader) |
| delegate_->SetSuggestedText(text, behavior); |
| } |
| |
| gfx::Rect InstantController::GetInstantBounds() { |
| return delegate_->GetInstantBounds(); |
| } |
| |
| bool InstantController::ShouldCommitInstantOnMouseUp() { |
| return commit_on_mouse_up_; |
| } |
| |
| void InstantController::CommitInstantLoader(InstantLoader* loader) { |
| if (loader_manager_.get() && loader_manager_->current_loader() == 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(); |
| } |
| } |
| |
| void InstantController::InstantLoaderDoesntSupportInstant( |
| InstantLoader* loader) { |
| DCHECK(!loader->ready()); // We better not be showing this loader. |
| DCHECK(loader->template_url_id()); |
| |
| VLOG(1) << "provider does not support instant"; |
| |
| // Don't attempt to use instant for this search engine again. |
| BlacklistFromInstant(loader->template_url_id()); |
| |
| // Because of the state of the stack we can't destroy the loader now. |
| bool was_pending = loader_manager_->pending_loader() == loader; |
| ScheduleDestroy(loader_manager_->ReleaseLoader(loader)); |
| if (was_pending) { |
| // |loader| was the pending loader. We may be showing another TabContents to |
| // the user (what was current). Destroy it. |
| DestroyPreviewContentsAndLeaveActive(); |
| } else { |
| // |loader| wasn't pending, yet it may still be the displayed loader. |
| UpdateDisplayableLoader(); |
| } |
| } |
| |
| void InstantController::AddToBlacklist(InstantLoader* loader, const GURL& url) { |
| std::string host = url.host(); |
| if (host.empty()) |
| return; |
| |
| if (!host_blacklist_) |
| host_blacklist_ = new HostBlacklist; |
| host_blacklist_->insert(host); |
| |
| if (!loader_manager_.get()) |
| return; |
| |
| // Because of the state of the stack we can't destroy the loader now. |
| ScheduleDestroy(loader); |
| |
| loader_manager_->ReleaseLoader(loader); |
| |
| UpdateDisplayableLoader(); |
| } |
| |
| void InstantController::UpdateDisplayableLoader() { |
| InstantLoader* loader = NULL; |
| // As soon as the pending loader is displayable it becomes the current loader, |
| // so we need only concern ourselves with the current loader here. |
| if (loader_manager_.get() && loader_manager_->current_loader() && |
| loader_manager_->current_loader()->ready() && |
| (!show_timer_.IsRunning() || |
| loader_manager_->current_loader()->http_status_ok())) { |
| loader = loader_manager_->current_loader(); |
| } |
| if (loader == displayable_loader_) |
| return; |
| |
| displayable_loader_ = loader; |
| |
| if (!displayable_loader_) { |
| delegate_->HideInstant(); |
| } else { |
| delegate_->ShowInstant(displayable_loader_->preview_contents()); |
| NotificationService::current()->Notify( |
| NotificationType::INSTANT_CONTROLLER_SHOWN, |
| Source<InstantController>(this), |
| NotificationService::NoDetails()); |
| } |
| } |
| |
| TabContentsWrapper* InstantController::GetPendingPreviewContents() { |
| return loader_manager_.get() && loader_manager_->pending_loader() ? |
| loader_manager_->pending_loader()->preview_contents() : NULL; |
| } |
| |
| bool InstantController::ShouldUpdateNow(TemplateURLID instant_id, |
| const GURL& url) { |
| DCHECK(loader_manager_.get()); |
| |
| if (instant_id) { |
| // Update sites that support instant immediately, they can do their own |
| // throttling. |
| return true; |
| } |
| |
| if (url.SchemeIsFile()) |
| return true; // File urls should load quickly, so don't delay loading them. |
| |
| if (loader_manager_->WillUpateChangeActiveLoader(instant_id)) { |
| // If Update would change loaders, update now. This indicates transitioning |
| // from an instant to non-instant loader. |
| return true; |
| } |
| |
| InstantLoader* active_loader = loader_manager_->active_loader(); |
| // WillUpateChangeActiveLoader should return true if no active loader, so |
| // we know there will be an active loader if we get here. |
| DCHECK(active_loader); |
| // Immediately update if the url is the same (which should result in nothing |
| // happening) or the hosts differ, otherwise we'll delay the update. |
| return (active_loader->url() == url) || |
| (active_loader->url().host() != url.host()); |
| } |
| |
| void InstantController::ScheduleUpdate(const GURL& url) { |
| scheduled_url_ = url; |
| |
| update_timer_.Stop(); |
| update_timer_.Start(base::TimeDelta::FromMilliseconds(kUpdateDelayMS), |
| this, &InstantController::ProcessScheduledUpdate); |
| } |
| |
| void InstantController::ProcessScheduledUpdate() { |
| DCHECK(loader_manager_.get()); |
| |
| // We only delay loading of sites that don't support instant, so we can ignore |
| // suggested_text here. |
| string16 suggested_text; |
| UpdateLoader(NULL, scheduled_url_, last_transition_type_, string16(), false, |
| &suggested_text); |
| } |
| |
| void InstantController::ProcessInstantStatusChanged(InstantLoader* loader) { |
| DCHECK(loader_manager_.get()); |
| scoped_ptr<InstantLoader> old_loader; |
| if (loader == loader_manager_->pending_loader()) { |
| loader_manager_->MakePendingCurrent(&old_loader); |
| } else if (loader != loader_manager_->current_loader()) { |
| // Notification from a loader that is no longer the current (either we have |
| // a pending, or its an instant loader). Ignore it. |
| return; |
| } |
| |
| UpdateDisplayableLoader(); |
| } |
| |
| void InstantController::ShowTimerFired() { |
| if (!loader_manager_.get()) |
| return; |
| |
| InstantLoader* loader = loader_manager_->active_loader(); |
| if (loader && loader->ready()) |
| ProcessInstantStatusChanged(loader); |
| } |
| |
| void InstantController::UpdateLoader(const TemplateURL* template_url, |
| const GURL& url, |
| PageTransition::Type transition_type, |
| const string16& user_text, |
| bool verbatim, |
| string16* suggested_text) { |
| update_timer_.Stop(); |
| |
| scoped_ptr<InstantLoader> owned_loader; |
| TemplateURLID template_url_id = template_url ? template_url->id() : 0; |
| InstantLoader* new_loader = |
| loader_manager_->UpdateLoader(template_url_id, &owned_loader); |
| |
| new_loader->SetOmniboxBounds(omnibox_bounds_); |
| if (new_loader->Update(tab_contents_, template_url, url, transition_type, |
| user_text, verbatim, suggested_text)) { |
| show_timer_.Stop(); |
| if (!new_loader->http_status_ok()) { |
| show_timer_.Start( |
| base::TimeDelta::FromMilliseconds(kShowDelayMS), |
| this, &InstantController::ShowTimerFired); |
| } |
| } |
| UpdateDisplayableLoader(); |
| } |
| |
| bool InstantController::ShouldShowPreviewFor(const AutocompleteMatch& match, |
| const TemplateURL** template_url) { |
| const TemplateURL* t_url = GetTemplateURL(match); |
| if (t_url) { |
| if (!t_url->id() || |
| !t_url->instant_url() || |
| IsBlacklistedFromInstant(t_url->id()) || |
| !t_url->instant_url()->SupportsReplacement()) { |
| // To avoid extra load on other search engines we only enable previews if |
| // they support the instant API. |
| return false; |
| } |
| } |
| *template_url = t_url; |
| |
| if (match.destination_url.SchemeIs(chrome::kJavaScriptScheme)) |
| return false; |
| |
| // Extension keywords don't have a real destionation URL. |
| if (match.template_url && match.template_url->IsExtensionKeyword()) |
| return false; |
| |
| // Was the host blacklisted? |
| if (host_blacklist_ && host_blacklist_->count(match.destination_url.host())) |
| return false; |
| |
| return true; |
| } |
| |
| void InstantController::BlacklistFromInstant(TemplateURLID id) { |
| blacklisted_ids_.insert(id); |
| } |
| |
| bool InstantController::IsBlacklistedFromInstant(TemplateURLID id) { |
| return blacklisted_ids_.count(id) > 0; |
| } |
| |
| void InstantController::ClearBlacklist() { |
| blacklisted_ids_.clear(); |
| } |
| |
| void InstantController::ScheduleDestroy(InstantLoader* loader) { |
| loaders_to_destroy_.push_back(loader); |
| if (destroy_factory_.empty()) { |
| MessageLoop::current()->PostTask( |
| FROM_HERE, destroy_factory_.NewRunnableMethod( |
| &InstantController::DestroyLoaders)); |
| } |
| } |
| |
| void InstantController::DestroyLoaders() { |
| loaders_to_destroy_.reset(); |
| } |
| |
| const TemplateURL* InstantController::GetTemplateURL( |
| const AutocompleteMatch& match) { |
| const TemplateURL* template_url = match.template_url; |
| if (match.type == AutocompleteMatch::SEARCH_WHAT_YOU_TYPED || |
| match.type == AutocompleteMatch::SEARCH_HISTORY || |
| match.type == AutocompleteMatch::SEARCH_SUGGEST) { |
| TemplateURLModel* model = tab_contents_->profile()->GetTemplateURLModel(); |
| template_url = model ? model->GetDefaultSearchProvider() : NULL; |
| } |
| return template_url; |
| } |