blob: f0dbe539edde2a3b9f70088707754f603c51b1aa [file] [log] [blame]
[email protected]6eff9392011-01-26 21:46:501// Copyright (c) 2011 The Chromium Authors. All rights reserved.
[email protected]fd2b9ce2010-08-11 04:03:572// 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"
[email protected]9ac40092010-10-27 23:05:2611#include "chrome/browser/autocomplete/autocomplete_match.h"
[email protected]6b723f82010-10-05 20:14:2712#include "chrome/browser/instant/instant_delegate.h"
13#include "chrome/browser/instant/instant_loader.h"
14#include "chrome/browser/instant/instant_loader_manager.h"
[email protected]7e03e812010-11-15 23:01:0115#include "chrome/browser/instant/promo_counter.h"
[email protected]ba6680f2010-11-01 20:35:0816#include "chrome/browser/platform_util.h"
[email protected]018cbb22010-10-11 22:32:0917#include "chrome/browser/prefs/pref_service.h"
[email protected]8ecad5e2010-12-02 21:18:3318#include "chrome/browser/profiles/profile.h"
[email protected]03bb953d2010-09-14 21:38:3019#include "chrome/browser/search_engines/template_url.h"
20#include "chrome/browser/search_engines/template_url_model.h"
[email protected]6a3ec2312010-12-02 19:30:1921#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
[email protected]fd2b9ce2010-08-11 04:03:5722#include "chrome/common/chrome_switches.h"
[email protected]eac60f72010-10-21 19:11:3923#include "chrome/common/notification_service.h"
[email protected]018cbb22010-10-11 22:32:0924#include "chrome/common/pref_names.h"
[email protected]a0b84662010-10-04 23:22:0425#include "chrome/common/url_constants.h"
[email protected]5de634712011-03-02 00:20:1926#include "content/browser/renderer_host/render_widget_host_view.h"
27#include "content/browser/tab_contents/tab_contents.h"
[email protected]fd2b9ce2010-08-11 04:03:5728
[email protected]804d6c8f2010-10-08 02:49:5029// Number of ms to delay between loading urls.
30static const int kUpdateDelayMS = 200;
31
[email protected]f38adeeb2010-12-08 01:08:1132// static
33InstantController::HostBlacklist* InstantController::host_blacklist_ = NULL;
34
[email protected]fdf773c52010-11-01 20:58:1935InstantController::InstantController(Profile* profile,
36 InstantDelegate* delegate)
[email protected]03bb953d2010-09-14 21:38:3037 : delegate_(delegate),
38 tab_contents_(NULL),
39 is_active_(false),
[email protected]2573b8d2011-03-01 16:20:3640 displayable_loader_(NULL),
[email protected]484ae5912010-09-29 19:16:1441 commit_on_mouse_up_(false),
[email protected]fdf773c52010-11-01 20:58:1942 last_transition_type_(PageTransition::LINK),
[email protected]6eff9392011-01-26 21:46:5043 ALLOW_THIS_IN_INITIALIZER_LIST(destroy_factory_(this)) {
[email protected]7e03e812010-11-15 23:01:0144 PrefService* service = profile->GetPrefs();
45 if (service) {
46 // kInstantWasEnabledOnce was added after instant, set it now to make sure
47 // it is correctly set.
48 service->SetBoolean(prefs::kInstantEnabledOnce, true);
49 }
[email protected]03bb953d2010-09-14 21:38:3050}
51
[email protected]6b723f82010-10-05 20:14:2752InstantController::~InstantController() {
[email protected]03bb953d2010-09-14 21:38:3053}
54
[email protected]7e03e812010-11-15 23:01:0155// static
56void InstantController::RegisterUserPrefs(PrefService* prefs) {
57 prefs->RegisterBooleanPref(prefs::kInstantConfirmDialogShown, false);
58 prefs->RegisterBooleanPref(prefs::kInstantEnabled, false);
59 prefs->RegisterBooleanPref(prefs::kInstantEnabledOnce, false);
60 prefs->RegisterInt64Pref(prefs::kInstantEnabledTime, false);
[email protected]7e03e812010-11-15 23:01:0161 PromoCounter::RegisterUserPrefs(prefs, prefs::kInstantPromo);
62}
63
64// static
65void InstantController::RecordMetrics(Profile* profile) {
66 if (!IsEnabled(profile))
67 return;
68
69 PrefService* service = profile->GetPrefs();
70 if (service) {
71 int64 enable_time = service->GetInt64(prefs::kInstantEnabledTime);
72 if (!enable_time) {
73 service->SetInt64(prefs::kInstantEnabledTime,
74 base::Time::Now().ToInternalValue());
75 } else {
76 base::TimeDelta delta =
77 base::Time::Now() - base::Time::FromInternalValue(enable_time);
[email protected]7e03e812010-11-15 23:01:0178 // Histogram from 1 hour to 30 days.
[email protected]6eff9392011-01-26 21:46:5079 UMA_HISTOGRAM_CUSTOM_COUNTS("Instant.EnabledTime.Predictive",
80 delta.InHours(), 1, 30 * 24, 50);
[email protected]7e03e812010-11-15 23:01:0181 }
82 }
83}
84
85// static
86bool InstantController::IsEnabled(Profile* profile) {
[email protected]6eff9392011-01-26 21:46:5087 PrefService* prefs = profile->GetPrefs();
88 return prefs->GetBoolean(prefs::kInstantEnabled);
[email protected]7e03e812010-11-15 23:01:0189}
90
91// static
92void InstantController::Enable(Profile* profile) {
93 PromoCounter* promo_counter = profile->GetInstantPromoCounter();
94 if (promo_counter)
95 promo_counter->Hide();
96
97 PrefService* service = profile->GetPrefs();
98 if (!service)
99 return;
100
101 service->SetBoolean(prefs::kInstantEnabled, true);
102 service->SetBoolean(prefs::kInstantConfirmDialogShown, true);
103 service->SetInt64(prefs::kInstantEnabledTime,
104 base::Time::Now().ToInternalValue());
105 service->SetBoolean(prefs::kInstantEnabledOnce, true);
[email protected]7e03e812010-11-15 23:01:01106}
107
108// static
109void InstantController::Disable(Profile* profile) {
110 PrefService* service = profile->GetPrefs();
[email protected]bfc031f2010-11-29 21:39:57111 if (!service || !IsEnabled(profile))
[email protected]7e03e812010-11-15 23:01:01112 return;
113
[email protected]7e03e812010-11-15 23:01:01114 int64 enable_time = service->GetInt64(prefs::kInstantEnabledTime);
[email protected]bfc031f2010-11-29 21:39:57115 if (enable_time) {
116 base::TimeDelta delta =
117 base::Time::Now() - base::Time::FromInternalValue(enable_time);
[email protected]bfc031f2010-11-29 21:39:57118 // Histogram from 1 minute to 10 days.
[email protected]6eff9392011-01-26 21:46:50119 UMA_HISTOGRAM_CUSTOM_COUNTS("Instant.TimeToDisable.Predictive",
120 delta.InMinutes(), 1, 60 * 24 * 10, 50);
[email protected]bfc031f2010-11-29 21:39:57121 }
[email protected]7e03e812010-11-15 23:01:01122
[email protected]bfc031f2010-11-29 21:39:57123 service->SetBoolean(prefs::kInstantEnabled, false);
[email protected]7e03e812010-11-15 23:01:01124}
125
[email protected]939e54a2010-12-18 02:42:07126// static
127bool InstantController::CommitIfCurrent(InstantController* controller) {
128 if (controller && controller->IsCurrent()) {
129 controller->CommitCurrentPreview(INSTANT_COMMIT_PRESSED_ENTER);
130 return true;
131 }
132 return false;
133}
134
[email protected]3c9e1872010-11-18 16:17:49135void InstantController::Update(TabContentsWrapper* tab_contents,
[email protected]6b723f82010-10-05 20:14:27136 const AutocompleteMatch& match,
137 const string16& user_text,
[email protected]57ad7b8c2010-11-18 19:13:49138 bool verbatim,
[email protected]6b723f82010-10-05 20:14:27139 string16* suggested_text) {
[email protected]bdf1d862010-11-24 02:46:11140 suggested_text->clear();
141
[email protected]03bb953d2010-09-14 21:38:30142 if (tab_contents != tab_contents_)
143 DestroyPreviewContents();
144
[email protected]a0b84662010-10-04 23:22:04145 const GURL& url = match.destination_url;
[email protected]03bb953d2010-09-14 21:38:30146 tab_contents_ = tab_contents;
[email protected]484ae5912010-09-29 19:16:14147 commit_on_mouse_up_ = false;
[email protected]97b6c4f2010-09-27 19:31:26148 last_transition_type_ = match.transition;
[email protected]0cc0a2462010-12-07 20:56:20149 const TemplateURL* template_url = NULL;
[email protected]03bb953d2010-09-14 21:38:30150
[email protected]1946c932010-12-15 00:07:38151 if (url.is_empty() || !url.is_valid()) {
152 // Assume we were invoked with GURL() and should destroy all.
[email protected]fd2b9ce2010-08-11 04:03:57153 DestroyPreviewContents();
154 return;
155 }
156
[email protected]1946c932010-12-15 00:07:38157 if (!ShouldShowPreviewFor(match, &template_url)) {
[email protected]7cce9f22011-02-28 22:02:47158 DestroyPreviewContentsAndLeaveActive();
[email protected]1946c932010-12-15 00:07:38159 return;
160 }
161
[email protected]a0b84662010-10-04 23:22:04162 if (!loader_manager_.get())
[email protected]6b723f82010-10-05 20:14:27163 loader_manager_.reset(new InstantLoaderManager(this));
[email protected]03bb953d2010-09-14 21:38:30164
[email protected]1946c932010-12-15 00:07:38165 if (!is_active_) {
166 is_active_ = true;
[email protected]36d5e5592010-11-15 20:45:59167 delegate_->PrepareForInstant();
[email protected]1946c932010-12-15 00:07:38168 }
[email protected]36d5e5592010-11-15 20:45:59169
[email protected]bdf1d862010-11-24 02:46:11170 TemplateURLID template_url_id = template_url ? template_url->id() : 0;
171 // Verbatim only makes sense if the search engines supports instant.
172 bool real_verbatim = template_url_id ? verbatim : false;
173
[email protected]804d6c8f2010-10-08 02:49:50174 if (ShouldUpdateNow(template_url_id, match.destination_url)) {
[email protected]7c71e942010-10-14 18:18:59175 UpdateLoader(template_url, match.destination_url, match.transition,
[email protected]57ad7b8c2010-11-18 19:13:49176 user_text, real_verbatim, suggested_text);
[email protected]804d6c8f2010-10-08 02:49:50177 } else {
178 ScheduleUpdate(match.destination_url);
[email protected]03bb953d2010-09-14 21:38:30179 }
[email protected]eac60f72010-10-21 19:11:39180
181 NotificationService::current()->Notify(
182 NotificationType::INSTANT_CONTROLLER_UPDATED,
183 Source<InstantController>(this),
184 NotificationService::NoDetails());
[email protected]fd2b9ce2010-08-11 04:03:57185}
186
[email protected]6b723f82010-10-05 20:14:27187void InstantController::SetOmniboxBounds(const gfx::Rect& bounds) {
[email protected]46fe8e92010-09-22 03:32:47188 if (omnibox_bounds_ == bounds)
189 return;
190
[email protected]6e6b59f42010-12-13 20:20:23191 // Always track the omnibox bounds. That way if Update is later invoked the
192 // bounds are in sync.
193 omnibox_bounds_ = bounds;
[email protected]a0b84662010-10-04 23:22:04194 if (loader_manager_.get()) {
195 if (loader_manager_->current_loader())
196 loader_manager_->current_loader()->SetOmniboxBounds(bounds);
197 if (loader_manager_->pending_loader())
198 loader_manager_->pending_loader()->SetOmniboxBounds(bounds);
[email protected]46fe8e92010-09-22 03:32:47199 }
200}
201
[email protected]6b723f82010-10-05 20:14:27202void InstantController::DestroyPreviewContents() {
[email protected]a0b84662010-10-04 23:22:04203 if (!loader_manager_.get()) {
[email protected]17336512010-09-22 17:28:27204 // We're not showing anything, nothing to do.
205 return;
206 }
207
[email protected]1946c932010-12-15 00:07:38208 // ReleasePreviewContents sets is_active_ to false, but we need to set it
[email protected]a0df03c122011-01-05 00:22:55209 // before notifying the delegate, otherwise if the delegate asks for the state
210 // we'll still be active.
[email protected]1946c932010-12-15 00:07:38211 is_active_ = false;
[email protected]6b723f82010-10-05 20:14:27212 delegate_->HideInstant();
213 delete ReleasePreviewContents(INSTANT_COMMIT_DESTROY);
[email protected]fd2b9ce2010-08-11 04:03:57214}
215
[email protected]7cce9f22011-02-28 22:02:47216void InstantController::DestroyPreviewContentsAndLeaveActive() {
[email protected]7cce9f22011-02-28 22:02:47217 commit_on_mouse_up_ = false;
[email protected]2573b8d2011-03-01 16:20:36218 if (displayable_loader_) {
219 displayable_loader_ = NULL;
220 delegate_->HideInstant();
221 }
[email protected]7cce9f22011-02-28 22:02:47222
223 // TODO(sky): this shouldn't nuke the loader. It should just nuke non-instant
224 // loaders and hide instant loaders.
225 loader_manager_.reset(new InstantLoaderManager(this));
226 update_timer_.Stop();
227}
228
[email protected]0a387472010-10-07 00:18:20229bool InstantController::IsCurrent() {
[email protected]1946c932010-12-15 00:07:38230 return loader_manager_.get() && loader_manager_->active_loader() &&
231 loader_manager_->active_loader()->ready() && !update_timer_.IsRunning();
[email protected]0a387472010-10-07 00:18:20232}
233
[email protected]6b723f82010-10-05 20:14:27234void InstantController::CommitCurrentPreview(InstantCommitType type) {
[email protected]a0b84662010-10-04 23:22:04235 DCHECK(loader_manager_.get());
236 DCHECK(loader_manager_->current_loader());
[email protected]3c9e1872010-11-18 16:17:49237 TabContentsWrapper* tab = ReleasePreviewContents(type);
[email protected]c65e2f152010-10-14 15:30:40238 delegate_->CommitInstant(tab);
[email protected]3c9e1872010-11-18 16:17:49239 CompleteRelease(tab->tab_contents());
[email protected]fd2b9ce2010-08-11 04:03:57240}
241
[email protected]6b723f82010-10-05 20:14:27242void InstantController::SetCommitOnMouseUp() {
[email protected]484ae5912010-09-29 19:16:14243 commit_on_mouse_up_ = true;
244}
245
[email protected]6b723f82010-10-05 20:14:27246bool InstantController::IsMouseDownFromActivate() {
[email protected]a0b84662010-10-04 23:22:04247 DCHECK(loader_manager_.get());
248 DCHECK(loader_manager_->current_loader());
249 return loader_manager_->current_loader()->IsMouseDownFromActivate();
[email protected]484ae5912010-09-29 19:16:14250}
251
[email protected]20ac3c32011-03-06 17:59:19252#if defined(OS_MACOSX)
253void InstantController::OnAutocompleteLostFocus(
254 gfx::NativeView view_gaining_focus) {
255 // If |IsMouseDownFromActivate()| returns false, the RenderWidgetHostView did
256 // not receive a mouseDown event. Therefore, we should destroy the preview.
257 // Otherwise, the RWHV was clicked, so we commit the preview.
258 if (!is_displayable() || !GetPreviewContents() ||
259 !IsMouseDownFromActivate()) {
260 DestroyPreviewContents();
261 } else if (IsShowingInstant()) {
262 SetCommitOnMouseUp();
263 } else {
264 CommitCurrentPreview(INSTANT_COMMIT_FOCUS_LOST);
265 }
266}
267#else
[email protected]ba6680f2010-11-01 20:35:08268void InstantController::OnAutocompleteLostFocus(
269 gfx::NativeView view_gaining_focus) {
[email protected]1946c932010-12-15 00:07:38270 if (!is_active() || !GetPreviewContents()) {
271 DestroyPreviewContents();
[email protected]ba6680f2010-11-01 20:35:08272 return;
[email protected]1946c932010-12-15 00:07:38273 }
[email protected]ba6680f2010-11-01 20:35:08274
275 RenderWidgetHostView* rwhv =
[email protected]3c9e1872010-11-18 16:17:49276 GetPreviewContents()->tab_contents()->GetRenderWidgetHostView();
[email protected]5a85751b2010-11-17 01:33:27277 if (!view_gaining_focus || !rwhv) {
278 DestroyPreviewContents();
279 return;
280 }
[email protected]ba6680f2010-11-01 20:35:08281
[email protected]3c9e1872010-11-18 16:17:49282 gfx::NativeView tab_view =
283 GetPreviewContents()->tab_contents()->GetNativeView();
[email protected]ba6680f2010-11-01 20:35:08284 // Focus is going to the renderer.
285 if (rwhv->GetNativeView() == view_gaining_focus ||
286 tab_view == view_gaining_focus) {
287 if (!IsMouseDownFromActivate()) {
288 // If the mouse is not down, focus is not going to the renderer. Someone
289 // else moved focus and we shouldn't commit.
[email protected]5a85751b2010-11-17 01:33:27290 DestroyPreviewContents();
291 return;
[email protected]ba6680f2010-11-01 20:35:08292 }
293
294 if (IsShowingInstant()) {
295 // We're showing instant results. As instant results may shift when
296 // committing we commit on the mouse up. This way a slow click still
297 // works fine.
[email protected]5a85751b2010-11-17 01:33:27298 SetCommitOnMouseUp();
299 return;
[email protected]ba6680f2010-11-01 20:35:08300 }
301
[email protected]5a85751b2010-11-17 01:33:27302 CommitCurrentPreview(INSTANT_COMMIT_FOCUS_LOST);
303 return;
[email protected]ba6680f2010-11-01 20:35:08304 }
305
306 // Walk up the view hierarchy. If the view gaining focus is a subview of the
307 // TabContents view (such as a windowed plugin or http auth dialog), we want
308 // to keep the preview contents. Otherwise, focus has gone somewhere else,
309 // such as the JS inspector, and we want to cancel the preview.
310 gfx::NativeView view_gaining_focus_ancestor = view_gaining_focus;
311 while (view_gaining_focus_ancestor &&
312 view_gaining_focus_ancestor != tab_view) {
313 view_gaining_focus_ancestor =
314 platform_util::GetParent(view_gaining_focus_ancestor);
315 }
316
[email protected]5a85751b2010-11-17 01:33:27317 if (view_gaining_focus_ancestor) {
318 CommitCurrentPreview(INSTANT_COMMIT_FOCUS_LOST);
319 return;
320 }
[email protected]ba6680f2010-11-01 20:35:08321
[email protected]5a85751b2010-11-17 01:33:27322 DestroyPreviewContents();
[email protected]ba6680f2010-11-01 20:35:08323}
[email protected]20ac3c32011-03-06 17:59:19324#endif
[email protected]ba6680f2010-11-01 20:35:08325
[email protected]3c9e1872010-11-18 16:17:49326TabContentsWrapper* InstantController::ReleasePreviewContents(
327 InstantCommitType type) {
[email protected]a0b84662010-10-04 23:22:04328 if (!loader_manager_.get())
[email protected]3aac77a2010-09-24 17:00:13329 return NULL;
330
[email protected]f38adeeb2010-12-08 01:08:11331 // Loader may be null if the url blacklisted instant.
332 scoped_ptr<InstantLoader> loader;
333 if (loader_manager_->current_loader())
334 loader.reset(loader_manager_->ReleaseCurrentLoader());
335 TabContentsWrapper* tab = loader.get() ?
336 loader->ReleasePreviewContents(type) : NULL;
[email protected]a0b84662010-10-04 23:22:04337
[email protected]0a387472010-10-07 00:18:20338 ClearBlacklist();
[email protected]a0b84662010-10-04 23:22:04339 is_active_ = false;
[email protected]2573b8d2011-03-01 16:20:36340 displayable_loader_ = NULL;
[email protected]a0b84662010-10-04 23:22:04341 commit_on_mouse_up_ = false;
[email protected]1946c932010-12-15 00:07:38342 omnibox_bounds_ = gfx::Rect();
343 loader_manager_.reset();
[email protected]804d6c8f2010-10-08 02:49:50344 update_timer_.Stop();
[email protected]a0b84662010-10-04 23:22:04345 return tab;
346}
347
[email protected]c65e2f152010-10-14 15:30:40348void InstantController::CompleteRelease(TabContents* tab) {
349 tab->SetAllContentsBlocked(false);
350}
351
[email protected]3c9e1872010-11-18 16:17:49352TabContentsWrapper* InstantController::GetPreviewContents() {
[email protected]1946c932010-12-15 00:07:38353 return loader_manager_.get() && loader_manager_->current_loader() ?
[email protected]a0b84662010-10-04 23:22:04354 loader_manager_->current_loader()->preview_contents() : NULL;
355}
356
[email protected]6b723f82010-10-05 20:14:27357bool InstantController::IsShowingInstant() {
[email protected]1946c932010-12-15 00:07:38358 return loader_manager_.get() && loader_manager_->current_loader() &&
[email protected]a0b84662010-10-04 23:22:04359 loader_manager_->current_loader()->is_showing_instant();
360}
361
[email protected]058baa2f2010-12-08 21:33:20362bool InstantController::MightSupportInstant() {
[email protected]1946c932010-12-15 00:07:38363 return loader_manager_.get() && loader_manager_->active_loader() &&
[email protected]058baa2f2010-12-08 21:33:20364 loader_manager_->active_loader()->is_showing_instant();
365}
366
[email protected]a5ec3ea2011-02-09 17:01:28367GURL InstantController::GetCurrentURL() {
368 return loader_manager_.get() && loader_manager_->active_loader() ?
369 loader_manager_->active_loader()->url() : GURL();
370}
371
[email protected]6b723f82010-10-05 20:14:27372void InstantController::ShowInstantLoader(InstantLoader* loader) {
[email protected]a0b84662010-10-04 23:22:04373 DCHECK(loader_manager_.get());
[email protected]2573b8d2011-03-01 16:20:36374 scoped_ptr<InstantLoader> old_loader;
375 if (loader == loader_manager_->pending_loader()) {
[email protected]a0b84662010-10-04 23:22:04376 loader_manager_->MakePendingCurrent(&old_loader);
[email protected]2573b8d2011-03-01 16:20:36377 } else if (loader != loader_manager_->current_loader()) {
378 // Notification from a loader that is no longer the current (either we have
379 // a pending, or its an instant loader). Ignore it.
380 return;
[email protected]03bb953d2010-09-14 21:38:30381 }
[email protected]ce833282010-11-04 15:48:39382
[email protected]2573b8d2011-03-01 16:20:36383 UpdateDisplayableLoader();
384
[email protected]ce833282010-11-04 15:48:39385 NotificationService::current()->Notify(
386 NotificationType::INSTANT_CONTROLLER_SHOWN,
387 Source<InstantController>(this),
388 NotificationService::NoDetails());
[email protected]fd2b9ce2010-08-11 04:03:57389}
[email protected]03bb953d2010-09-14 21:38:30390
[email protected]6b723f82010-10-05 20:14:27391void InstantController::SetSuggestedTextFor(InstantLoader* loader,
392 const string16& text) {
[email protected]a0b84662010-10-04 23:22:04393 if (loader_manager_->current_loader() == loader)
394 delegate_->SetSuggestedText(text);
395}
[email protected]03bb953d2010-09-14 21:38:30396
[email protected]6b723f82010-10-05 20:14:27397gfx::Rect InstantController::GetInstantBounds() {
398 return delegate_->GetInstantBounds();
[email protected]a0b84662010-10-04 23:22:04399}
400
[email protected]6b723f82010-10-05 20:14:27401bool InstantController::ShouldCommitInstantOnMouseUp() {
[email protected]a0b84662010-10-04 23:22:04402 return commit_on_mouse_up_;
403}
404
[email protected]6b723f82010-10-05 20:14:27405void InstantController::CommitInstantLoader(InstantLoader* loader) {
[email protected]a0b84662010-10-04 23:22:04406 if (loader_manager_.get() && loader_manager_->current_loader() == loader) {
[email protected]6b723f82010-10-05 20:14:27407 CommitCurrentPreview(INSTANT_COMMIT_FOCUS_LOST);
[email protected]a0b84662010-10-04 23:22:04408 } else {
409 // This can happen if the mouse was down, we swapped out the preview and
410 // the mouse was released. Generally this shouldn't happen, but if it does
411 // revert.
412 DestroyPreviewContents();
[email protected]03bb953d2010-09-14 21:38:30413 }
[email protected]03bb953d2010-09-14 21:38:30414}
415
[email protected]0a387472010-10-07 00:18:20416void InstantController::InstantLoaderDoesntSupportInstant(
[email protected]4c1b035d2010-12-10 02:32:23417 InstantLoader* loader) {
[email protected]0a387472010-10-07 00:18:20418 DCHECK(!loader->ready()); // We better not be showing this loader.
419 DCHECK(loader->template_url_id());
420
[email protected]1946c932010-12-15 00:07:38421 VLOG(1) << "provider does not support instant";
[email protected]4c1b035d2010-12-10 02:32:23422
423 // Don't attempt to use instant for this search engine again.
[email protected]0a387472010-10-07 00:18:20424 BlacklistFromInstant(loader->template_url_id());
425
426 if (loader_manager_->active_loader() == loader) {
[email protected]1946c932010-12-15 00:07:38427 // The loader is active, hide all.
[email protected]7cce9f22011-02-28 22:02:47428 DestroyPreviewContentsAndLeaveActive();
[email protected]0a387472010-10-07 00:18:20429 } else {
[email protected]2573b8d2011-03-01 16:20:36430 scoped_ptr<InstantLoader> owned_loader(
431 loader_manager_->ReleaseLoader(loader));
432 UpdateDisplayableLoader();
[email protected]0a387472010-10-07 00:18:20433 }
434}
435
[email protected]f38adeeb2010-12-08 01:08:11436void InstantController::AddToBlacklist(InstantLoader* loader, const GURL& url) {
437 std::string host = url.host();
438 if (host.empty())
439 return;
440
441 if (!host_blacklist_)
442 host_blacklist_ = new HostBlacklist;
443 host_blacklist_->insert(host);
444
445 if (!loader_manager_.get())
446 return;
447
448 // Because of the state of the stack we can't destroy the loader now.
449 ScheduleDestroy(loader);
450
451 loader_manager_->ReleaseLoader(loader);
[email protected]2573b8d2011-03-01 16:20:36452
453 UpdateDisplayableLoader();
454}
455
456void InstantController::UpdateDisplayableLoader() {
457 InstantLoader* loader = NULL;
458 // As soon as the pending loader is displayable it becomes the current loader,
459 // so we need only concern ourselves with the current loader here.
460 if (loader_manager_.get() && loader_manager_->current_loader() &&
461 loader_manager_->current_loader()->ready()) {
462 loader = loader_manager_->current_loader();
463 }
464 if (loader == displayable_loader_)
465 return;
466
467 displayable_loader_ = loader;
468
469 if (!displayable_loader_) {
[email protected]f38adeeb2010-12-08 01:08:11470 delegate_->HideInstant();
[email protected]2573b8d2011-03-01 16:20:36471 } else {
472 delegate_->ShowInstant(displayable_loader_->preview_contents());
473 NotificationService::current()->Notify(
474 NotificationType::INSTANT_CONTROLLER_SHOWN,
475 Source<InstantController>(this),
476 NotificationService::NoDetails());
[email protected]f38adeeb2010-12-08 01:08:11477 }
478}
479
[email protected]1946c932010-12-15 00:07:38480TabContentsWrapper* InstantController::GetPendingPreviewContents() {
481 return loader_manager_.get() && loader_manager_->pending_loader() ?
482 loader_manager_->pending_loader()->preview_contents() : NULL;
483}
484
[email protected]804d6c8f2010-10-08 02:49:50485bool InstantController::ShouldUpdateNow(TemplateURLID instant_id,
486 const GURL& url) {
487 DCHECK(loader_manager_.get());
488
489 if (instant_id) {
490 // Update sites that support instant immediately, they can do their own
491 // throttling.
492 return true;
493 }
494
495 if (url.SchemeIsFile())
496 return true; // File urls should load quickly, so don't delay loading them.
497
498 if (loader_manager_->WillUpateChangeActiveLoader(instant_id)) {
499 // If Update would change loaders, update now. This indicates transitioning
500 // from an instant to non-instant loader.
501 return true;
502 }
503
504 InstantLoader* active_loader = loader_manager_->active_loader();
505 // WillUpateChangeActiveLoader should return true if no active loader, so
506 // we know there will be an active loader if we get here.
507 DCHECK(active_loader);
[email protected]1946c932010-12-15 00:07:38508 // Immediately update if the url is the same (which should result in nothing
509 // happening) or the hosts differ, otherwise we'll delay the update.
510 return (active_loader->url() == url) ||
511 (active_loader->url().host() != url.host());
[email protected]804d6c8f2010-10-08 02:49:50512}
513
514void InstantController::ScheduleUpdate(const GURL& url) {
515 scheduled_url_ = url;
516
[email protected]2573b8d2011-03-01 16:20:36517 update_timer_.Stop();
[email protected]804d6c8f2010-10-08 02:49:50518 update_timer_.Start(base::TimeDelta::FromMilliseconds(kUpdateDelayMS),
519 this, &InstantController::ProcessScheduledUpdate);
520}
521
522void InstantController::ProcessScheduledUpdate() {
523 DCHECK(loader_manager_.get());
524
525 // We only delay loading of sites that don't support instant, so we can ignore
526 // suggested_text here.
527 string16 suggested_text;
[email protected]57ad7b8c2010-11-18 19:13:49528 UpdateLoader(NULL, scheduled_url_, last_transition_type_, string16(), false,
[email protected]804d6c8f2010-10-08 02:49:50529 &suggested_text);
530}
531
[email protected]7c71e942010-10-14 18:18:59532void InstantController::UpdateLoader(const TemplateURL* template_url,
[email protected]0a387472010-10-07 00:18:20533 const GURL& url,
534 PageTransition::Type transition_type,
535 const string16& user_text,
[email protected]57ad7b8c2010-11-18 19:13:49536 bool verbatim,
[email protected]0a387472010-10-07 00:18:20537 string16* suggested_text) {
[email protected]804d6c8f2010-10-08 02:49:50538 update_timer_.Stop();
539
[email protected]0a387472010-10-07 00:18:20540 scoped_ptr<InstantLoader> owned_loader;
[email protected]7c71e942010-10-14 18:18:59541 TemplateURLID template_url_id = template_url ? template_url->id() : 0;
[email protected]0a387472010-10-07 00:18:20542 InstantLoader* new_loader =
543 loader_manager_->UpdateLoader(template_url_id, &owned_loader);
544
545 new_loader->SetOmniboxBounds(omnibox_bounds_);
[email protected]7c71e942010-10-14 18:18:59546 new_loader->Update(tab_contents_, template_url, url, transition_type,
[email protected]57ad7b8c2010-11-18 19:13:49547 user_text, verbatim, suggested_text);
[email protected]2573b8d2011-03-01 16:20:36548 UpdateDisplayableLoader();
[email protected]0a387472010-10-07 00:18:20549}
550
[email protected]0cc0a2462010-12-07 20:56:20551bool InstantController::ShouldShowPreviewFor(const AutocompleteMatch& match,
552 const TemplateURL** template_url) {
553 const TemplateURL* t_url = GetTemplateURL(match);
554 if (t_url) {
555 if (!t_url->id() ||
556 !t_url->instant_url() ||
557 IsBlacklistedFromInstant(t_url->id()) ||
558 !t_url->instant_url()->SupportsReplacement()) {
559 // To avoid extra load on other search engines we only enable previews if
560 // they support the instant API.
561 return false;
562 }
563 }
564 *template_url = t_url;
565
[email protected]773d92a2010-11-11 01:19:08566 if (match.destination_url.SchemeIs(chrome::kJavaScriptScheme))
567 return false;
568
569 // Extension keywords don't have a real destionation URL.
570 if (match.template_url && match.template_url->IsExtensionKeyword())
571 return false;
572
[email protected]f38adeeb2010-12-08 01:08:11573 // Was the host blacklisted?
574 if (host_blacklist_ && host_blacklist_->count(match.destination_url.host()))
575 return false;
576
[email protected]773d92a2010-11-11 01:19:08577 return true;
[email protected]46fe8e92010-09-22 03:32:47578}
[email protected]0a387472010-10-07 00:18:20579
580void InstantController::BlacklistFromInstant(TemplateURLID id) {
581 blacklisted_ids_.insert(id);
582}
583
584bool InstantController::IsBlacklistedFromInstant(TemplateURLID id) {
585 return blacklisted_ids_.count(id) > 0;
586}
587
588void InstantController::ClearBlacklist() {
589 blacklisted_ids_.clear();
590}
[email protected]804d6c8f2010-10-08 02:49:50591
[email protected]f38adeeb2010-12-08 01:08:11592void InstantController::ScheduleDestroy(InstantLoader* loader) {
593 loaders_to_destroy_.push_back(loader);
594 if (destroy_factory_.empty()) {
595 MessageLoop::current()->PostTask(
596 FROM_HERE, destroy_factory_.NewRunnableMethod(
597 &InstantController::DestroyLoaders));
598 }
599}
600
601void InstantController::DestroyLoaders() {
602 loaders_to_destroy_.reset();
603}
604
[email protected]7c71e942010-10-14 18:18:59605const TemplateURL* InstantController::GetTemplateURL(
[email protected]804d6c8f2010-10-08 02:49:50606 const AutocompleteMatch& match) {
607 const TemplateURL* template_url = match.template_url;
608 if (match.type == AutocompleteMatch::SEARCH_WHAT_YOU_TYPED ||
609 match.type == AutocompleteMatch::SEARCH_HISTORY ||
610 match.type == AutocompleteMatch::SEARCH_SUGGEST) {
611 TemplateURLModel* model = tab_contents_->profile()->GetTemplateURLModel();
612 template_url = model ? model->GetDefaultSearchProvider() : NULL;
613 }
[email protected]0cc0a2462010-12-07 20:56:20614 return template_url;
[email protected]804d6c8f2010-10-08 02:49:50615}