blob: 4e02f28ce2d3ffa013b987078e4d858274733e82 [file] [log] [blame]
[email protected]fd2b9ce2010-08-11 04:03:571// Copyright (c) 2010 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]6b723f82010-10-05 20:14:275#include "chrome/browser/instant/instant_controller.h"
[email protected]fd2b9ce2010-08-11 04:03:576
[email protected]bfc031f2010-11-29 21:39:577#include "build/build_config.h"
[email protected]fd2b9ce2010-08-11 04:03:578#include "base/command_line.h"
[email protected]f38adeeb2010-12-08 01:08:119#include "base/message_loop.h"
[email protected]7e03e812010-11-15 23:01:0110#include "base/metrics/histogram.h"
11#include "base/rand_util.h"
[email protected]9ac40092010-10-27 23:05:2612#include "chrome/browser/autocomplete/autocomplete_match.h"
[email protected]6b723f82010-10-05 20:14:2713#include "chrome/browser/instant/instant_delegate.h"
14#include "chrome/browser/instant/instant_loader.h"
15#include "chrome/browser/instant/instant_loader_manager.h"
[email protected]7e03e812010-11-15 23:01:0116#include "chrome/browser/instant/promo_counter.h"
[email protected]ba6680f2010-11-01 20:35:0817#include "chrome/browser/platform_util.h"
[email protected]018cbb22010-10-11 22:32:0918#include "chrome/browser/prefs/pref_service.h"
[email protected]8ecad5e2010-12-02 21:18:3319#include "chrome/browser/profiles/profile.h"
[email protected]ba6680f2010-11-01 20:35:0820#include "chrome/browser/renderer_host/render_widget_host_view.h"
[email protected]03bb953d2010-09-14 21:38:3021#include "chrome/browser/search_engines/template_url.h"
22#include "chrome/browser/search_engines/template_url_model.h"
[email protected]fd2b9ce2010-08-11 04:03:5723#include "chrome/browser/tab_contents/tab_contents.h"
[email protected]6a3ec2312010-12-02 19:30:1924#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
[email protected]fd2b9ce2010-08-11 04:03:5725#include "chrome/common/chrome_switches.h"
[email protected]eac60f72010-10-21 19:11:3926#include "chrome/common/notification_service.h"
[email protected]018cbb22010-10-11 22:32:0927#include "chrome/common/pref_names.h"
[email protected]a0b84662010-10-04 23:22:0428#include "chrome/common/url_constants.h"
[email protected]fd2b9ce2010-08-11 04:03:5729
[email protected]804d6c8f2010-10-08 02:49:5030// Number of ms to delay between loading urls.
31static const int kUpdateDelayMS = 200;
32
[email protected]f38adeeb2010-12-08 01:08:1133// static
34InstantController::HostBlacklist* InstantController::host_blacklist_ = NULL;
35
[email protected]fdf773c52010-11-01 20:58:1936InstantController::InstantController(Profile* profile,
37 InstantDelegate* delegate)
[email protected]03bb953d2010-09-14 21:38:3038 : delegate_(delegate),
39 tab_contents_(NULL),
40 is_active_(false),
[email protected]1946c932010-12-15 00:07:3841 is_displayable_(false),
[email protected]484ae5912010-09-29 19:16:1442 commit_on_mouse_up_(false),
[email protected]fdf773c52010-11-01 20:58:1943 last_transition_type_(PageTransition::LINK),
[email protected]f38adeeb2010-12-08 01:08:1144 ALLOW_THIS_IN_INITIALIZER_LIST(destroy_factory_(this)),
[email protected]bfc031f2010-11-29 21:39:5745 type_(FIRST_TYPE) {
46 bool enabled = GetType(profile, &type_);
47 DCHECK(enabled);
[email protected]7e03e812010-11-15 23:01:0148 PrefService* service = profile->GetPrefs();
49 if (service) {
50 // kInstantWasEnabledOnce was added after instant, set it now to make sure
51 // it is correctly set.
52 service->SetBoolean(prefs::kInstantEnabledOnce, true);
53 }
[email protected]03bb953d2010-09-14 21:38:3054}
55
[email protected]6b723f82010-10-05 20:14:2756InstantController::~InstantController() {
[email protected]03bb953d2010-09-14 21:38:3057}
58
[email protected]7e03e812010-11-15 23:01:0159// static
60void InstantController::RegisterUserPrefs(PrefService* prefs) {
61 prefs->RegisterBooleanPref(prefs::kInstantConfirmDialogShown, false);
62 prefs->RegisterBooleanPref(prefs::kInstantEnabled, false);
63 prefs->RegisterBooleanPref(prefs::kInstantEnabledOnce, false);
64 prefs->RegisterInt64Pref(prefs::kInstantEnabledTime, false);
[email protected]7e03e812010-11-15 23:01:0165 PromoCounter::RegisterUserPrefs(prefs, prefs::kInstantPromo);
66}
67
68// static
69void InstantController::RecordMetrics(Profile* profile) {
70 if (!IsEnabled(profile))
71 return;
72
73 PrefService* service = profile->GetPrefs();
74 if (service) {
75 int64 enable_time = service->GetInt64(prefs::kInstantEnabledTime);
76 if (!enable_time) {
77 service->SetInt64(prefs::kInstantEnabledTime,
78 base::Time::Now().ToInternalValue());
79 } else {
80 base::TimeDelta delta =
81 base::Time::Now() - base::Time::FromInternalValue(enable_time);
[email protected]bfc031f2010-11-29 21:39:5782 std::string name = "Instant.EnabledTime. " + GetTypeString(profile);
83 // Can't use histogram macros as name isn't constant.
[email protected]7e03e812010-11-15 23:01:0184 // Histogram from 1 hour to 30 days.
[email protected]bfc031f2010-11-29 21:39:5785 scoped_refptr<base::Histogram> counter =
86 base::Histogram::FactoryGet(name, 1, 30 * 24, 50,
87 base::Histogram::kUmaTargetedHistogramFlag);
88 counter->Add(delta.InHours());
[email protected]7e03e812010-11-15 23:01:0189 }
90 }
91}
92
93// static
94bool InstantController::IsEnabled(Profile* profile) {
[email protected]bfc031f2010-11-29 21:39:5795 Type type;
96 return GetType(profile, &type);
[email protected]7e03e812010-11-15 23:01:0197}
98
99// static
100bool InstantController::IsEnabled(Profile* profile, Type type) {
[email protected]bfc031f2010-11-29 21:39:57101 Type enabled_type;
102 return GetType(profile, &enabled_type) && type == enabled_type;
[email protected]7e03e812010-11-15 23:01:01103}
104
105// static
106void InstantController::Enable(Profile* profile) {
107 PromoCounter* promo_counter = profile->GetInstantPromoCounter();
108 if (promo_counter)
109 promo_counter->Hide();
110
111 PrefService* service = profile->GetPrefs();
112 if (!service)
113 return;
114
115 service->SetBoolean(prefs::kInstantEnabled, true);
116 service->SetBoolean(prefs::kInstantConfirmDialogShown, true);
117 service->SetInt64(prefs::kInstantEnabledTime,
118 base::Time::Now().ToInternalValue());
119 service->SetBoolean(prefs::kInstantEnabledOnce, true);
[email protected]7e03e812010-11-15 23:01:01120}
121
122// static
123void InstantController::Disable(Profile* profile) {
124 PrefService* service = profile->GetPrefs();
[email protected]bfc031f2010-11-29 21:39:57125 if (!service || !IsEnabled(profile))
[email protected]7e03e812010-11-15 23:01:01126 return;
127
[email protected]7e03e812010-11-15 23:01:01128 int64 enable_time = service->GetInt64(prefs::kInstantEnabledTime);
[email protected]bfc031f2010-11-29 21:39:57129 if (enable_time) {
130 base::TimeDelta delta =
131 base::Time::Now() - base::Time::FromInternalValue(enable_time);
132 std::string name = "Instant.TimeToDisable." + GetTypeString(profile);
133 // Can't use histogram macros as name isn't constant.
134 // Histogram from 1 minute to 10 days.
135 scoped_refptr<base::Histogram> counter =
136 base::Histogram::FactoryGet(name, 1, 60 * 24 * 10, 50,
137 base::Histogram::kUmaTargetedHistogramFlag);
138 counter->Add(delta.InMinutes());
139 }
[email protected]7e03e812010-11-15 23:01:01140
[email protected]bfc031f2010-11-29 21:39:57141 service->SetBoolean(prefs::kInstantEnabled, false);
[email protected]7e03e812010-11-15 23:01:01142}
143
[email protected]3c9e1872010-11-18 16:17:49144void InstantController::Update(TabContentsWrapper* tab_contents,
[email protected]6b723f82010-10-05 20:14:27145 const AutocompleteMatch& match,
146 const string16& user_text,
[email protected]57ad7b8c2010-11-18 19:13:49147 bool verbatim,
[email protected]6b723f82010-10-05 20:14:27148 string16* suggested_text) {
[email protected]bdf1d862010-11-24 02:46:11149 suggested_text->clear();
150
[email protected]03bb953d2010-09-14 21:38:30151 if (tab_contents != tab_contents_)
152 DestroyPreviewContents();
153
[email protected]a0b84662010-10-04 23:22:04154 const GURL& url = match.destination_url;
[email protected]03bb953d2010-09-14 21:38:30155 tab_contents_ = tab_contents;
[email protected]484ae5912010-09-29 19:16:14156 commit_on_mouse_up_ = false;
[email protected]97b6c4f2010-09-27 19:31:26157 last_transition_type_ = match.transition;
[email protected]0cc0a2462010-12-07 20:56:20158 const TemplateURL* template_url = NULL;
[email protected]03bb953d2010-09-14 21:38:30159
[email protected]1946c932010-12-15 00:07:38160 if (url.is_empty() || !url.is_valid()) {
161 // Assume we were invoked with GURL() and should destroy all.
[email protected]fd2b9ce2010-08-11 04:03:57162 DestroyPreviewContents();
163 return;
164 }
165
[email protected]1946c932010-12-15 00:07:38166 if (!ShouldShowPreviewFor(match, &template_url)) {
167 DestroyAndLeaveActive();
168 return;
169 }
170
[email protected]a0b84662010-10-04 23:22:04171 if (!loader_manager_.get())
[email protected]6b723f82010-10-05 20:14:27172 loader_manager_.reset(new InstantLoaderManager(this));
[email protected]03bb953d2010-09-14 21:38:30173
[email protected]1946c932010-12-15 00:07:38174 if (!is_active_) {
175 is_active_ = true;
[email protected]36d5e5592010-11-15 20:45:59176 delegate_->PrepareForInstant();
[email protected]1946c932010-12-15 00:07:38177 }
[email protected]36d5e5592010-11-15 20:45:59178
[email protected]bdf1d862010-11-24 02:46:11179 TemplateURLID template_url_id = template_url ? template_url->id() : 0;
180 // Verbatim only makes sense if the search engines supports instant.
181 bool real_verbatim = template_url_id ? verbatim : false;
182
[email protected]804d6c8f2010-10-08 02:49:50183 if (ShouldUpdateNow(template_url_id, match.destination_url)) {
[email protected]7c71e942010-10-14 18:18:59184 UpdateLoader(template_url, match.destination_url, match.transition,
[email protected]57ad7b8c2010-11-18 19:13:49185 user_text, real_verbatim, suggested_text);
[email protected]804d6c8f2010-10-08 02:49:50186 } else {
187 ScheduleUpdate(match.destination_url);
[email protected]03bb953d2010-09-14 21:38:30188 }
[email protected]eac60f72010-10-21 19:11:39189
190 NotificationService::current()->Notify(
191 NotificationType::INSTANT_CONTROLLER_UPDATED,
192 Source<InstantController>(this),
193 NotificationService::NoDetails());
[email protected]fd2b9ce2010-08-11 04:03:57194}
195
[email protected]6b723f82010-10-05 20:14:27196void InstantController::SetOmniboxBounds(const gfx::Rect& bounds) {
[email protected]46fe8e92010-09-22 03:32:47197 if (omnibox_bounds_ == bounds)
198 return;
199
[email protected]6e6b59f42010-12-13 20:20:23200 // Always track the omnibox bounds. That way if Update is later invoked the
201 // bounds are in sync.
202 omnibox_bounds_ = bounds;
[email protected]a0b84662010-10-04 23:22:04203 if (loader_manager_.get()) {
[email protected]a0b84662010-10-04 23:22:04204 if (loader_manager_->current_loader())
205 loader_manager_->current_loader()->SetOmniboxBounds(bounds);
206 if (loader_manager_->pending_loader())
207 loader_manager_->pending_loader()->SetOmniboxBounds(bounds);
[email protected]46fe8e92010-09-22 03:32:47208 }
209}
210
[email protected]6b723f82010-10-05 20:14:27211void InstantController::DestroyPreviewContents() {
[email protected]a0b84662010-10-04 23:22:04212 if (!loader_manager_.get()) {
[email protected]17336512010-09-22 17:28:27213 // We're not showing anything, nothing to do.
214 return;
215 }
216
[email protected]1946c932010-12-15 00:07:38217 // ReleasePreviewContents sets is_active_ to false, but we need to set it
218 // beore notifying the delegate so.
219 is_active_ = false;
[email protected]6b723f82010-10-05 20:14:27220 delegate_->HideInstant();
221 delete ReleasePreviewContents(INSTANT_COMMIT_DESTROY);
[email protected]fd2b9ce2010-08-11 04:03:57222}
223
[email protected]0a387472010-10-07 00:18:20224bool InstantController::IsCurrent() {
[email protected]1946c932010-12-15 00:07:38225 return loader_manager_.get() && loader_manager_->active_loader() &&
226 loader_manager_->active_loader()->ready() && !update_timer_.IsRunning();
[email protected]0a387472010-10-07 00:18:20227}
228
[email protected]6b723f82010-10-05 20:14:27229void InstantController::CommitCurrentPreview(InstantCommitType type) {
[email protected]a0b84662010-10-04 23:22:04230 DCHECK(loader_manager_.get());
231 DCHECK(loader_manager_->current_loader());
[email protected]3c9e1872010-11-18 16:17:49232 TabContentsWrapper* tab = ReleasePreviewContents(type);
[email protected]c65e2f152010-10-14 15:30:40233 delegate_->CommitInstant(tab);
[email protected]3c9e1872010-11-18 16:17:49234 CompleteRelease(tab->tab_contents());
[email protected]fd2b9ce2010-08-11 04:03:57235}
236
[email protected]6b723f82010-10-05 20:14:27237void InstantController::SetCommitOnMouseUp() {
[email protected]484ae5912010-09-29 19:16:14238 commit_on_mouse_up_ = true;
239}
240
[email protected]6b723f82010-10-05 20:14:27241bool InstantController::IsMouseDownFromActivate() {
[email protected]a0b84662010-10-04 23:22:04242 DCHECK(loader_manager_.get());
243 DCHECK(loader_manager_->current_loader());
244 return loader_manager_->current_loader()->IsMouseDownFromActivate();
[email protected]484ae5912010-09-29 19:16:14245}
246
[email protected]ba6680f2010-11-01 20:35:08247void InstantController::OnAutocompleteLostFocus(
248 gfx::NativeView view_gaining_focus) {
[email protected]1946c932010-12-15 00:07:38249 if (!is_active() || !GetPreviewContents()) {
250 DestroyPreviewContents();
[email protected]ba6680f2010-11-01 20:35:08251 return;
[email protected]1946c932010-12-15 00:07:38252 }
[email protected]ba6680f2010-11-01 20:35:08253
254 RenderWidgetHostView* rwhv =
[email protected]3c9e1872010-11-18 16:17:49255 GetPreviewContents()->tab_contents()->GetRenderWidgetHostView();
[email protected]5a85751b2010-11-17 01:33:27256 if (!view_gaining_focus || !rwhv) {
257 DestroyPreviewContents();
258 return;
259 }
[email protected]ba6680f2010-11-01 20:35:08260
[email protected]3c9e1872010-11-18 16:17:49261 gfx::NativeView tab_view =
262 GetPreviewContents()->tab_contents()->GetNativeView();
[email protected]ba6680f2010-11-01 20:35:08263 // Focus is going to the renderer.
264 if (rwhv->GetNativeView() == view_gaining_focus ||
265 tab_view == view_gaining_focus) {
266 if (!IsMouseDownFromActivate()) {
267 // If the mouse is not down, focus is not going to the renderer. Someone
268 // else moved focus and we shouldn't commit.
[email protected]5a85751b2010-11-17 01:33:27269 DestroyPreviewContents();
270 return;
[email protected]ba6680f2010-11-01 20:35:08271 }
272
273 if (IsShowingInstant()) {
274 // We're showing instant results. As instant results may shift when
275 // committing we commit on the mouse up. This way a slow click still
276 // works fine.
[email protected]5a85751b2010-11-17 01:33:27277 SetCommitOnMouseUp();
278 return;
[email protected]ba6680f2010-11-01 20:35:08279 }
280
[email protected]5a85751b2010-11-17 01:33:27281 CommitCurrentPreview(INSTANT_COMMIT_FOCUS_LOST);
282 return;
[email protected]ba6680f2010-11-01 20:35:08283 }
284
285 // Walk up the view hierarchy. If the view gaining focus is a subview of the
286 // TabContents view (such as a windowed plugin or http auth dialog), we want
287 // to keep the preview contents. Otherwise, focus has gone somewhere else,
288 // such as the JS inspector, and we want to cancel the preview.
289 gfx::NativeView view_gaining_focus_ancestor = view_gaining_focus;
290 while (view_gaining_focus_ancestor &&
291 view_gaining_focus_ancestor != tab_view) {
292 view_gaining_focus_ancestor =
293 platform_util::GetParent(view_gaining_focus_ancestor);
294 }
295
[email protected]5a85751b2010-11-17 01:33:27296 if (view_gaining_focus_ancestor) {
297 CommitCurrentPreview(INSTANT_COMMIT_FOCUS_LOST);
298 return;
299 }
[email protected]ba6680f2010-11-01 20:35:08300
[email protected]5a85751b2010-11-17 01:33:27301 DestroyPreviewContents();
[email protected]ba6680f2010-11-01 20:35:08302}
303
[email protected]3c9e1872010-11-18 16:17:49304TabContentsWrapper* InstantController::ReleasePreviewContents(
305 InstantCommitType type) {
[email protected]a0b84662010-10-04 23:22:04306 if (!loader_manager_.get())
[email protected]3aac77a2010-09-24 17:00:13307 return NULL;
308
[email protected]f38adeeb2010-12-08 01:08:11309 // Loader may be null if the url blacklisted instant.
310 scoped_ptr<InstantLoader> loader;
311 if (loader_manager_->current_loader())
312 loader.reset(loader_manager_->ReleaseCurrentLoader());
313 TabContentsWrapper* tab = loader.get() ?
314 loader->ReleasePreviewContents(type) : NULL;
[email protected]a0b84662010-10-04 23:22:04315
[email protected]0a387472010-10-07 00:18:20316 ClearBlacklist();
[email protected]a0b84662010-10-04 23:22:04317 is_active_ = false;
[email protected]1946c932010-12-15 00:07:38318 is_displayable_ = false;
[email protected]a0b84662010-10-04 23:22:04319 commit_on_mouse_up_ = false;
[email protected]1946c932010-12-15 00:07:38320 omnibox_bounds_ = gfx::Rect();
321 loader_manager_.reset();
[email protected]804d6c8f2010-10-08 02:49:50322 update_timer_.Stop();
[email protected]a0b84662010-10-04 23:22:04323 return tab;
324}
325
[email protected]c65e2f152010-10-14 15:30:40326void InstantController::CompleteRelease(TabContents* tab) {
327 tab->SetAllContentsBlocked(false);
328}
329
[email protected]3c9e1872010-11-18 16:17:49330TabContentsWrapper* InstantController::GetPreviewContents() {
[email protected]1946c932010-12-15 00:07:38331 return loader_manager_.get() && loader_manager_->current_loader() ?
[email protected]a0b84662010-10-04 23:22:04332 loader_manager_->current_loader()->preview_contents() : NULL;
333}
334
[email protected]6b723f82010-10-05 20:14:27335bool InstantController::IsShowingInstant() {
[email protected]1946c932010-12-15 00:07:38336 return loader_manager_.get() && loader_manager_->current_loader() &&
[email protected]a0b84662010-10-04 23:22:04337 loader_manager_->current_loader()->is_showing_instant();
338}
339
[email protected]058baa2f2010-12-08 21:33:20340bool InstantController::MightSupportInstant() {
[email protected]1946c932010-12-15 00:07:38341 return loader_manager_.get() && loader_manager_->active_loader() &&
[email protected]058baa2f2010-12-08 21:33:20342 loader_manager_->active_loader()->is_showing_instant();
343}
344
[email protected]6b723f82010-10-05 20:14:27345void InstantController::ShowInstantLoader(InstantLoader* loader) {
[email protected]a0b84662010-10-04 23:22:04346 DCHECK(loader_manager_.get());
347 if (loader_manager_->current_loader() == loader) {
[email protected]1946c932010-12-15 00:07:38348 is_displayable_ = true;
[email protected]6b723f82010-10-05 20:14:27349 delegate_->ShowInstant(loader->preview_contents());
[email protected]a0b84662010-10-04 23:22:04350 } else if (loader_manager_->pending_loader() == loader) {
[email protected]6b723f82010-10-05 20:14:27351 scoped_ptr<InstantLoader> old_loader;
[email protected]a0b84662010-10-04 23:22:04352 loader_manager_->MakePendingCurrent(&old_loader);
[email protected]6b723f82010-10-05 20:14:27353 delegate_->ShowInstant(loader->preview_contents());
[email protected]a0b84662010-10-04 23:22:04354 } else {
[email protected]0a387472010-10-07 00:18:20355 // The loader supports instant but isn't active yet. Nothing to do.
[email protected]03bb953d2010-09-14 21:38:30356 }
[email protected]ce833282010-11-04 15:48:39357
358 NotificationService::current()->Notify(
359 NotificationType::INSTANT_CONTROLLER_SHOWN,
360 Source<InstantController>(this),
361 NotificationService::NoDetails());
[email protected]fd2b9ce2010-08-11 04:03:57362}
[email protected]03bb953d2010-09-14 21:38:30363
[email protected]6b723f82010-10-05 20:14:27364void InstantController::SetSuggestedTextFor(InstantLoader* loader,
365 const string16& text) {
[email protected]a0b84662010-10-04 23:22:04366 if (loader_manager_->current_loader() == loader)
367 delegate_->SetSuggestedText(text);
368}
[email protected]03bb953d2010-09-14 21:38:30369
[email protected]6b723f82010-10-05 20:14:27370gfx::Rect InstantController::GetInstantBounds() {
371 return delegate_->GetInstantBounds();
[email protected]a0b84662010-10-04 23:22:04372}
373
[email protected]6b723f82010-10-05 20:14:27374bool InstantController::ShouldCommitInstantOnMouseUp() {
[email protected]a0b84662010-10-04 23:22:04375 return commit_on_mouse_up_;
376}
377
[email protected]6b723f82010-10-05 20:14:27378void InstantController::CommitInstantLoader(InstantLoader* loader) {
[email protected]a0b84662010-10-04 23:22:04379 if (loader_manager_.get() && loader_manager_->current_loader() == loader) {
[email protected]6b723f82010-10-05 20:14:27380 CommitCurrentPreview(INSTANT_COMMIT_FOCUS_LOST);
[email protected]a0b84662010-10-04 23:22:04381 } else {
382 // This can happen if the mouse was down, we swapped out the preview and
383 // the mouse was released. Generally this shouldn't happen, but if it does
384 // revert.
385 DestroyPreviewContents();
[email protected]03bb953d2010-09-14 21:38:30386 }
[email protected]03bb953d2010-09-14 21:38:30387}
388
[email protected]0a387472010-10-07 00:18:20389void InstantController::InstantLoaderDoesntSupportInstant(
[email protected]4c1b035d2010-12-10 02:32:23390 InstantLoader* loader) {
[email protected]0a387472010-10-07 00:18:20391 DCHECK(!loader->ready()); // We better not be showing this loader.
392 DCHECK(loader->template_url_id());
393
[email protected]1946c932010-12-15 00:07:38394 VLOG(1) << "provider does not support instant";
[email protected]4c1b035d2010-12-10 02:32:23395
396 // Don't attempt to use instant for this search engine again.
[email protected]0a387472010-10-07 00:18:20397 BlacklistFromInstant(loader->template_url_id());
398
399 if (loader_manager_->active_loader() == loader) {
[email protected]1946c932010-12-15 00:07:38400 // The loader is active, hide all.
401 DestroyAndLeaveActive();
[email protected]0a387472010-10-07 00:18:20402 } else {
[email protected]1946c932010-12-15 00:07:38403 if (loader_manager_->current_loader() == loader && is_displayable_) {
[email protected]4c1b035d2010-12-10 02:32:23404 // There is a pending loader and we're active. Hide the preview. When then
405 // pending loader finishes loading we'll notify the delegate to show.
406 DCHECK(loader_manager_->pending_loader());
[email protected]1946c932010-12-15 00:07:38407 is_displayable_ = false;
[email protected]4c1b035d2010-12-10 02:32:23408 delegate_->HideInstant();
409 }
[email protected]0a387472010-10-07 00:18:20410 loader_manager_->DestroyLoader(loader);
[email protected]0a387472010-10-07 00:18:20411 }
412}
413
[email protected]f38adeeb2010-12-08 01:08:11414void InstantController::AddToBlacklist(InstantLoader* loader, const GURL& url) {
415 std::string host = url.host();
416 if (host.empty())
417 return;
418
419 if (!host_blacklist_)
420 host_blacklist_ = new HostBlacklist;
421 host_blacklist_->insert(host);
422
423 if (!loader_manager_.get())
424 return;
425
426 // Because of the state of the stack we can't destroy the loader now.
427 ScheduleDestroy(loader);
428
429 loader_manager_->ReleaseLoader(loader);
[email protected]1946c932010-12-15 00:07:38430 if (is_displayable_ &&
[email protected]f38adeeb2010-12-08 01:08:11431 (!loader_manager_->active_loader() ||
432 !loader_manager_->current_loader()->ready())) {
433 // Hide instant. When the pending loader finishes loading we'll go active
434 // again.
[email protected]1946c932010-12-15 00:07:38435 is_displayable_ = false;
[email protected]f38adeeb2010-12-08 01:08:11436 delegate_->HideInstant();
437 }
438}
439
[email protected]1946c932010-12-15 00:07:38440void InstantController::DestroyAndLeaveActive() {
441 is_displayable_ = false;
442 commit_on_mouse_up_ = false;
443 delegate_->HideInstant();
444
445 loader_manager_.reset(new InstantLoaderManager(this));
446 update_timer_.Stop();
447}
448
449TabContentsWrapper* InstantController::GetPendingPreviewContents() {
450 return loader_manager_.get() && loader_manager_->pending_loader() ?
451 loader_manager_->pending_loader()->preview_contents() : NULL;
452}
453
[email protected]804d6c8f2010-10-08 02:49:50454bool InstantController::ShouldUpdateNow(TemplateURLID instant_id,
455 const GURL& url) {
456 DCHECK(loader_manager_.get());
457
458 if (instant_id) {
459 // Update sites that support instant immediately, they can do their own
460 // throttling.
461 return true;
462 }
463
464 if (url.SchemeIsFile())
465 return true; // File urls should load quickly, so don't delay loading them.
466
467 if (loader_manager_->WillUpateChangeActiveLoader(instant_id)) {
468 // If Update would change loaders, update now. This indicates transitioning
469 // from an instant to non-instant loader.
470 return true;
471 }
472
473 InstantLoader* active_loader = loader_manager_->active_loader();
474 // WillUpateChangeActiveLoader should return true if no active loader, so
475 // we know there will be an active loader if we get here.
476 DCHECK(active_loader);
[email protected]1946c932010-12-15 00:07:38477 // Immediately update if the url is the same (which should result in nothing
478 // happening) or the hosts differ, otherwise we'll delay the update.
479 return (active_loader->url() == url) ||
480 (active_loader->url().host() != url.host());
[email protected]804d6c8f2010-10-08 02:49:50481}
482
483void InstantController::ScheduleUpdate(const GURL& url) {
484 scheduled_url_ = url;
485
486 if (update_timer_.IsRunning())
487 update_timer_.Stop();
488 update_timer_.Start(base::TimeDelta::FromMilliseconds(kUpdateDelayMS),
489 this, &InstantController::ProcessScheduledUpdate);
490}
491
492void InstantController::ProcessScheduledUpdate() {
493 DCHECK(loader_manager_.get());
494
495 // We only delay loading of sites that don't support instant, so we can ignore
496 // suggested_text here.
497 string16 suggested_text;
[email protected]57ad7b8c2010-11-18 19:13:49498 UpdateLoader(NULL, scheduled_url_, last_transition_type_, string16(), false,
[email protected]804d6c8f2010-10-08 02:49:50499 &suggested_text);
500}
501
[email protected]7c71e942010-10-14 18:18:59502void InstantController::UpdateLoader(const TemplateURL* template_url,
[email protected]0a387472010-10-07 00:18:20503 const GURL& url,
504 PageTransition::Type transition_type,
505 const string16& user_text,
[email protected]57ad7b8c2010-11-18 19:13:49506 bool verbatim,
[email protected]0a387472010-10-07 00:18:20507 string16* suggested_text) {
[email protected]804d6c8f2010-10-08 02:49:50508 update_timer_.Stop();
509
[email protected]0a387472010-10-07 00:18:20510 InstantLoader* old_loader = loader_manager_->current_loader();
511 scoped_ptr<InstantLoader> owned_loader;
[email protected]7c71e942010-10-14 18:18:59512 TemplateURLID template_url_id = template_url ? template_url->id() : 0;
[email protected]0a387472010-10-07 00:18:20513 InstantLoader* new_loader =
514 loader_manager_->UpdateLoader(template_url_id, &owned_loader);
515
516 new_loader->SetOmniboxBounds(omnibox_bounds_);
[email protected]7c71e942010-10-14 18:18:59517 new_loader->Update(tab_contents_, template_url, url, transition_type,
[email protected]57ad7b8c2010-11-18 19:13:49518 user_text, verbatim, suggested_text);
[email protected]0a387472010-10-07 00:18:20519 if (old_loader != new_loader && new_loader->ready())
520 delegate_->ShowInstant(new_loader->preview_contents());
521}
522
[email protected]0cc0a2462010-12-07 20:56:20523bool InstantController::ShouldShowPreviewFor(const AutocompleteMatch& match,
524 const TemplateURL** template_url) {
525 const TemplateURL* t_url = GetTemplateURL(match);
526 if (t_url) {
527 if (!t_url->id() ||
528 !t_url->instant_url() ||
529 IsBlacklistedFromInstant(t_url->id()) ||
530 !t_url->instant_url()->SupportsReplacement()) {
531 // To avoid extra load on other search engines we only enable previews if
532 // they support the instant API.
533 return false;
534 }
535 }
536 *template_url = t_url;
537
[email protected]773d92a2010-11-11 01:19:08538 if (match.destination_url.SchemeIs(chrome::kJavaScriptScheme))
539 return false;
540
541 // Extension keywords don't have a real destionation URL.
542 if (match.template_url && match.template_url->IsExtensionKeyword())
543 return false;
544
[email protected]f38adeeb2010-12-08 01:08:11545 // Was the host blacklisted?
546 if (host_blacklist_ && host_blacklist_->count(match.destination_url.host()))
547 return false;
548
[email protected]773d92a2010-11-11 01:19:08549 return true;
[email protected]46fe8e92010-09-22 03:32:47550}
[email protected]0a387472010-10-07 00:18:20551
552void InstantController::BlacklistFromInstant(TemplateURLID id) {
553 blacklisted_ids_.insert(id);
554}
555
556bool InstantController::IsBlacklistedFromInstant(TemplateURLID id) {
557 return blacklisted_ids_.count(id) > 0;
558}
559
560void InstantController::ClearBlacklist() {
561 blacklisted_ids_.clear();
562}
[email protected]804d6c8f2010-10-08 02:49:50563
[email protected]f38adeeb2010-12-08 01:08:11564void InstantController::ScheduleDestroy(InstantLoader* loader) {
565 loaders_to_destroy_.push_back(loader);
566 if (destroy_factory_.empty()) {
567 MessageLoop::current()->PostTask(
568 FROM_HERE, destroy_factory_.NewRunnableMethod(
569 &InstantController::DestroyLoaders));
570 }
571}
572
573void InstantController::DestroyLoaders() {
574 loaders_to_destroy_.reset();
575}
576
[email protected]7c71e942010-10-14 18:18:59577const TemplateURL* InstantController::GetTemplateURL(
[email protected]804d6c8f2010-10-08 02:49:50578 const AutocompleteMatch& match) {
[email protected]fdf773c52010-11-01 20:58:19579 if (type_ == VERBATIM_TYPE) {
580 // When using VERBATIM_TYPE we don't want to attempt to use the instant
581 // JavaScript API, otherwise the page would show predictive results. By
582 // returning NULL here we ensure we don't attempt to use the instant API.
583 //
584 // TODO: when the full search box API is in place we can lift this
585 // restriction and force the page to show verbatim results always.
586 return NULL;
587 }
588
[email protected]804d6c8f2010-10-08 02:49:50589 const TemplateURL* template_url = match.template_url;
590 if (match.type == AutocompleteMatch::SEARCH_WHAT_YOU_TYPED ||
591 match.type == AutocompleteMatch::SEARCH_HISTORY ||
592 match.type == AutocompleteMatch::SEARCH_SUGGEST) {
593 TemplateURLModel* model = tab_contents_->profile()->GetTemplateURLModel();
594 template_url = model ? model->GetDefaultSearchProvider() : NULL;
595 }
[email protected]0cc0a2462010-12-07 20:56:20596 return template_url;
[email protected]804d6c8f2010-10-08 02:49:50597}
[email protected]bfc031f2010-11-29 21:39:57598
599// static
600bool InstantController::GetType(Profile* profile, Type* type) {
601 *type = FIRST_TYPE;
602 // CommandLine takes precedence.
603 CommandLine* cl = CommandLine::ForCurrentProcess();
604 if (cl->HasSwitch(switches::kEnablePredictiveInstant)) {
605 *type = PREDICTIVE_TYPE;
606 return true;
607 }
608 if (cl->HasSwitch(switches::kEnableVerbatimInstant)) {
609 *type = VERBATIM_TYPE;
610 return true;
611 }
[email protected]8a6ff28d2010-12-02 16:35:19612 if (cl->HasSwitch(switches::kEnablePredictiveNoAutoCompleteInstant)) {
613 *type = PREDICTIVE_NO_AUTO_COMPLETE_TYPE;
614 return true;
615 }
[email protected]bfc031f2010-11-29 21:39:57616
617 // Then prefs.
618 PrefService* prefs = profile->GetPrefs();
619 if (!prefs->GetBoolean(prefs::kInstantEnabled))
620 return false;
621
[email protected]98dc0102010-11-30 02:59:36622 // PREDICTIVE_TYPE is the default if enabled via preferences.
623 *type = PREDICTIVE_TYPE;
[email protected]bfc031f2010-11-29 21:39:57624 return true;
625}
626
627// static
628std::string InstantController::GetTypeString(Profile* profile) {
629 Type type;
630 if (!GetType(profile, &type)) {
631 NOTREACHED();
632 return std::string();
633 }
634 switch (type) {
635 case PREDICTIVE_TYPE:
636 return "Predictive";
637 case VERBATIM_TYPE:
638 return "Verbatim";
639 case PREDICTIVE_NO_AUTO_COMPLETE_TYPE:
640 return "PredictiveNoAutoComplete";
641 default:
642 NOTREACHED();
643 return std::string();
644 }
645}