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

Side by Side Diff: chrome/browser/instant/instant_controller.cc

Issue 10732002: Upstream rewrite of Instant. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: First round of comments Created 8 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/instant/instant_controller.h" 5 #include "chrome/browser/instant/instant_controller.h"
6 6
7 #include <string>
8
7 #include "base/bind.h" 9 #include "base/bind.h"
8 #include "base/command_line.h" 10 #include "base/command_line.h"
11 #include "base/i18n/case_conversion.h"
9 #include "base/message_loop.h" 12 #include "base/message_loop.h"
10 #include "base/metrics/histogram.h" 13 #include "base/metrics/histogram.h"
14 #include "base/utf_string_conversions.h"
11 #include "build/build_config.h" 15 #include "build/build_config.h"
12 #include "chrome/browser/autocomplete/autocomplete_match.h" 16 #include "chrome/browser/autocomplete/autocomplete_match.h"
13 #include "chrome/browser/instant/instant_controller_delegate.h" 17 #include "chrome/browser/instant/instant_controller_delegate.h"
14 #include "chrome/browser/instant/instant_loader.h" 18 #include "chrome/browser/instant/instant_loader.h"
15 #include "chrome/browser/platform_util.h" 19 #include "chrome/browser/platform_util.h"
16 #include "chrome/browser/prefs/pref_service.h" 20 #include "chrome/browser/prefs/pref_service.h"
17 #include "chrome/browser/profiles/profile.h" 21 #include "chrome/browser/profiles/profile.h"
18 #include "chrome/browser/search_engines/template_url.h" 22 #include "chrome/browser/search_engines/template_url.h"
19 #include "chrome/browser/search_engines/template_url_service.h" 23 #include "chrome/browser/search_engines/template_url_service.h"
20 #include "chrome/browser/search_engines/template_url_service_factory.h" 24 #include "chrome/browser/search_engines/template_url_service_factory.h"
21 #include "chrome/browser/ui/blocked_content/blocked_content_tab_helper.h" 25 #include "chrome/browser/ui/search/search_model.h"
26 #include "chrome/browser/ui/search/search_tab_helper.h"
27 #include "chrome/browser/ui/search/search_types.h"
22 #include "chrome/browser/ui/tab_contents/tab_contents.h" 28 #include "chrome/browser/ui/tab_contents/tab_contents.h"
23 #include "chrome/common/chrome_notification_types.h" 29 #include "chrome/common/chrome_notification_types.h"
24 #include "chrome/common/chrome_switches.h" 30 #include "chrome/common/chrome_switches.h"
25 #include "chrome/common/pref_names.h" 31 #include "chrome/common/pref_names.h"
26 #include "content/public/browser/notification_service.h" 32 #include "content/public/browser/notification_service.h"
27 #include "content/public/browser/render_widget_host_view.h" 33 #include "content/public/browser/render_widget_host_view.h"
28 #include "content/public/browser/web_contents.h" 34 #include "content/public/browser/web_contents.h"
29 35
30 #if defined(TOOLKIT_VIEWS) 36 #if defined(TOOLKIT_VIEWS)
31 #include "ui/views/focus/focus_manager.h" 37 #include "ui/views/focus/focus_manager.h"
32 #include "ui/views/view.h" 38 #include "ui/views/view.h"
33 #include "ui/views/widget/widget.h" 39 #include "ui/views/widget/widget.h"
34 #endif 40 #endif
35 41
42 namespace {
43
44 enum PreviewUsageType {
45 PREVIEW_CREATED,
46 PREVIEW_DELETED,
47 PREVIEW_LOADED,
48 PREVIEW_SHOWED,
49 PREVIEW_COMMITTED,
50 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.
51 };
52
53 // An artificial delay (in milliseconds) we introduce before telling the Instant
54 // page about the new omnibox bounds, in cases where the bounds shrink. This is
55 // to avoid the page jumping up/down very fast in response to bounds changes.
56 const int kUpdateBoundsDelayMS = 1000;
57
58 // The maximum number of times we'll load a non-Instant-supporting search engine
59 // before we give up and blacklist it for the rest of the browsing session.
60 const int kMaxInstantSupportFailures = 10;
61
62 std::string ModeToString(InstantController::Mode mode) {
63 switch (mode) {
64 case InstantController::INSTANT: return "Instant";
65 case InstantController::SUGGEST: return "Suggest";
66 case InstantController::HIDDEN: return "Hidden";
67 case InstantController::SILENT: return "Silent";
68 }
69
70 NOTREACHED();
71 return std::string();
72 }
73
74 void AddPreviewUsageForHistogram(InstantController::Mode mode,
75 PreviewUsageType usage) {
76 DCHECK(0 <= usage && usage < PREVIEW_NUM_TYPES) << usage;
77 base::Histogram* histogram = base::LinearHistogram::FactoryGet(
78 "Instant.Previews." + ModeToString(mode), 1, PREVIEW_NUM_TYPES,
79 PREVIEW_NUM_TYPES + 1, base::Histogram::kUmaTargetedHistogramFlag);
80 histogram->Add(usage);
81 }
82
83 } // namespace
84
36 InstantController::InstantController(InstantControllerDelegate* delegate, 85 InstantController::InstantController(InstantControllerDelegate* delegate,
37 Mode mode) 86 Mode mode)
38 : delegate_(delegate), 87 : delegate_(delegate),
39 is_displayable_(false), 88 mode_(mode),
40 is_out_of_date_(true), 89 last_tab_contents_(NULL),
41 commit_on_mouse_up_(false), 90 last_verbatim_(false),
42 last_transition_type_(content::PAGE_TRANSITION_LINK), 91 last_complete_behavior_(INSTANT_COMPLETE_NOW),
43 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)), 92 is_showing_(false),
44 mode_(mode) { 93 loader_ready_(false) {
45 DCHECK(mode_ == INSTANT || mode_ == SUGGEST || mode_ == HIDDEN ||
46 mode_ == SILENT);
47 } 94 }
48 95
49 InstantController::~InstantController() { 96 InstantController::~InstantController() {
97 // We do this explicitly for two reasons: To |Hide| if we are showing the
98 // preview, and for proper accounting of PREVIEW_DELETED in histograms.
99 DeleteLoader();
50 } 100 }
51 101
52 // static 102 // static
53 void InstantController::RegisterUserPrefs(PrefService* prefs) { 103 void InstantController::RegisterUserPrefs(PrefService* prefs) {
54 prefs->RegisterBooleanPref(prefs::kInstantConfirmDialogShown, 104 prefs->RegisterBooleanPref(prefs::kInstantConfirmDialogShown, false,
55 false,
56 PrefService::SYNCABLE_PREF); 105 PrefService::SYNCABLE_PREF);
57 prefs->RegisterBooleanPref(prefs::kInstantEnabled, 106 prefs->RegisterBooleanPref(prefs::kInstantEnabled, false,
58 false,
59 PrefService::SYNCABLE_PREF); 107 PrefService::SYNCABLE_PREF);
60 108
61 // TODO(jamescook): Move this to search controller. 109 // TODO(jamescook): Move this to search controller.
62 prefs->RegisterDoublePref(prefs::kInstantAnimationScaleFactor, 110 prefs->RegisterDoublePref(prefs::kInstantAnimationScaleFactor,
63 1.0, 111 1.0,
64 PrefService::UNSYNCABLE_PREF); 112 PrefService::UNSYNCABLE_PREF);
65 } 113 }
66 114
67 // static 115 // static
68 void InstantController::RecordMetrics(Profile* profile) { 116 void InstantController::RecordMetrics(Profile* profile) {
69 UMA_HISTOGRAM_ENUMERATION("Instant.Status", IsEnabled(profile), 2); 117 UMA_HISTOGRAM_BOOLEAN("Instant.Status", IsEnabled(profile));
70 } 118 }
71 119
72 // static 120 // static
73 bool InstantController::IsEnabled(Profile* profile) { 121 bool InstantController::IsEnabled(Profile* profile) {
74 const PrefService* prefs = profile->GetPrefs(); 122 const PrefService* prefs = profile->GetPrefs();
75 return prefs && prefs->GetBoolean(prefs::kInstantEnabled); 123 return prefs && prefs->GetBoolean(prefs::kInstantEnabled);
76 } 124 }
77 125
78 // static 126 // static
79 void InstantController::Enable(Profile* profile) { 127 void InstantController::Enable(Profile* profile) {
80 PrefService* prefs = profile->GetPrefs(); 128 PrefService* prefs = profile->GetPrefs();
81 if (!prefs) 129 if (prefs) {
82 return; 130 prefs->SetBoolean(prefs::kInstantEnabled, true);
83 131 prefs->SetBoolean(prefs::kInstantConfirmDialogShown, true);
84 prefs->SetBoolean(prefs::kInstantEnabled, true); 132 UMA_HISTOGRAM_BOOLEAN("Instant.Preference", true);
85 prefs->SetBoolean(prefs::kInstantConfirmDialogShown, true); 133 }
86 UMA_HISTOGRAM_ENUMERATION("Instant.Preference", 1, 2);
87 } 134 }
88 135
89 // static 136 // static
90 void InstantController::Disable(Profile* profile) { 137 void InstantController::Disable(Profile* profile) {
91 PrefService* prefs = profile->GetPrefs(); 138 PrefService* prefs = profile->GetPrefs();
92 if (!prefs) 139 if (prefs) {
93 return; 140 prefs->SetBoolean(prefs::kInstantEnabled, false);
94 141 UMA_HISTOGRAM_BOOLEAN("Instant.Preference", false);
95 prefs->SetBoolean(prefs::kInstantEnabled, false); 142 }
96 UMA_HISTOGRAM_ENUMERATION("Instant.Preference", 0, 2);
97 } 143 }
98 144
99 bool InstantController::Update(const AutocompleteMatch& match, 145 bool InstantController::Update(const AutocompleteMatch& match,
100 const string16& user_text, 146 const string16& user_text,
101 bool verbatim, 147 bool verbatim,
102 string16* suggested_text) { 148 string16* suggested_text,
103 suggested_text->clear(); 149 InstantCompleteBehavior* complete_behavior) {
104 150 const TabContents* tab_contents = delegate_->GetInstantHostTabContents();
105 is_out_of_date_ = false; 151 DCHECK(tab_contents);
106 commit_on_mouse_up_ = false;
107 last_transition_type_ = match.transition;
108 last_url_ = match.destination_url;
109 last_user_text_ = user_text;
110
111 TabContents* tab_contents = delegate_->GetInstantHostTabContents();
112 if (!tab_contents) { 152 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
113 Hide(); 153 Hide();
114 return false; 154 return false;
115 } 155 }
116 156
157 string16 instant_id;
158 GURL instant_url;
117 Profile* profile = tab_contents->profile(); 159 Profile* profile = tab_contents->profile();
118 const TemplateURL* template_url = match.GetTemplateURL(profile); 160
119 const TemplateURL* default_t_url = 161 // If the match's TemplateURL isn't valid, it likely not a query.
120 TemplateURLServiceFactory::GetForProfile(profile) 162 if (!GetInstantURL(match.GetTemplateURL(profile), &instant_id,
121 ->GetDefaultSearchProvider(); 163 &instant_url)) {
122 if (!IsValidInstantTemplateURL(template_url) || !default_t_url ||
123 template_url->id() != default_t_url->id()) {
124 Hide(); 164 Hide();
125 return false; 165 return false;
126 } 166 }
127 167
128 if (!loader_.get() || loader_->template_url_id() != template_url->id()) 168 // The presence of any suggested_text implies verbatim.
129 loader_.reset(new InstantLoader(this, template_url->id(), std::string())); 169 DCHECK(suggested_text->empty() || verbatim)
130 170 << user_text << "|" << *suggested_text;
131 if (mode_ == SILENT) { 171
132 // For the SILENT mode, we process |user_text| at commit time. 172 ResetLoader(instant_id, instant_url, tab_contents);
133 loader_->MaybeLoadInstantURL(tab_contents, template_url); 173
174 // Track the full omnibox text, so we can send it on commit.
175 last_full_text_ = user_text + *suggested_text;
176
177 // Don't send an update to the loader if the |user_text| hasn't changed.
178 if (user_text == last_user_text_ && verbatim == last_verbatim_) {
179 // Since we are updating |suggested_text|, shouldn't we also update
180 // |last_full_text_|? No. There's no guarantee that our suggestion will
181 // actually be inline autocompleted. For example, it may get trumped by
182 // a history suggestion. If our suggestion does make it, the omnibox will
183 // 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.
184 *suggested_text = last_suggestion_;
185 *complete_behavior = last_complete_behavior_;
186
187 // We need to call |Show| here because of this:
188 // 1. User has typed a query (say Q). Instant overlay is showing results.
189 // 2. User arrows-down to a URL entry or erases all omnibox text. Both of
190 // these cause the overlay to |Hide|.
191 // 3. User arrows-up to Q or types Q again. |last_user_text_| is still Q, so
192 // we don't need to |Update| the loader, but we do need to |Show|.
193 if (loader_ready_ && mode_ == INSTANT)
194 Show();
134 return true; 195 return true;
135 } 196 }
136 197
137 UpdateLoader(tab_contents, template_url, match.destination_url, 198 last_user_text_ = user_text;
138 match.transition, user_text, verbatim, suggested_text); 199 last_verbatim_ = verbatim;
139 200 loader_ready_ = false;
140 content::NotificationService::current()->Notify( 201
141 chrome::NOTIFICATION_INSTANT_CONTROLLER_UPDATED, 202 // Reset the last suggestion, as it's no longer valid.
142 content::Source<InstantController>(this), 203 suggested_text->clear();
143 content::NotificationService::NoDetails()); 204 last_suggestion_.clear();
205 *complete_behavior = last_complete_behavior_ = INSTANT_COMPLETE_NOW;
206
207 if (mode_ != SILENT) {
208 loader_->Update(UTF16ToUTF8(last_user_text_), last_verbatim_);
209
210 content::NotificationService::current()->Notify(
211 chrome::NOTIFICATION_INSTANT_CONTROLLER_UPDATED,
212 content::Source<InstantController>(this),
213 content::NotificationService::NoDetails());
214 }
215
144 return true; 216 return true;
145 } 217 }
146 218
219 // TODO(tonyg): This method only fires when the omnibox bounds change. It also
220 // needs to fire when the preview bounds change (e.g.: open/close info bar).
147 void InstantController::SetOmniboxBounds(const gfx::Rect& bounds) { 221 void InstantController::SetOmniboxBounds(const gfx::Rect& bounds) {
148 if (omnibox_bounds_ == bounds) 222 if (omnibox_bounds_ == bounds || mode_ != INSTANT)
149 return; 223 return;
150 224
151 // Always track the omnibox bounds. That way if Update is later invoked the
152 // bounds are in sync.
153 omnibox_bounds_ = bounds; 225 omnibox_bounds_ = bounds;
154 226 if (omnibox_bounds_.height() > last_omnibox_bounds_.height()) {
155 if (loader_.get() && !is_out_of_date_ && mode_ == INSTANT) 227 update_bounds_timer_.Stop();
156 loader_->SetOmniboxBounds(bounds); 228 SendBoundsToPage();
157 } 229 } else if (!update_bounds_timer_.IsRunning()) {
158 230 update_bounds_timer_.Start(FROM_HERE,
159 void InstantController::DestroyPreviewContents() { 231 base::TimeDelta::FromMilliseconds(kUpdateBoundsDelayMS), this,
160 if (!loader_.get()) { 232 &InstantController::SendBoundsToPage);
161 // We're not showing anything, nothing to do. 233 }
162 return; 234 }
163 } 235
164 236 TabContents* InstantController::GetPreviewContents() const {
165 if (is_displayable_) { 237 return loader_.get() ? loader_->preview_contents() : NULL;
166 is_displayable_ = false; 238 }
239
240 void InstantController::Hide() {
241 last_tab_contents_ = NULL;
242 if (is_showing_) {
243 is_showing_ = false;
167 delegate_->HideInstant(); 244 delegate_->HideInstant();
168 } 245 }
169 delete ReleasePreviewContents(INSTANT_COMMIT_DESTROY, NULL);
170 }
171
172 void InstantController::Hide() {
173 is_out_of_date_ = true;
174 commit_on_mouse_up_ = false;
175 if (is_displayable_) {
176 is_displayable_ = false;
177 delegate_->HideInstant();
178 }
179 } 246 }
180 247
181 bool InstantController::IsCurrent() const { 248 bool InstantController::IsCurrent() const {
182 // TODO(mmenke): See if we can do something more intelligent in the 249 DCHECK(IsOutOfDate() || loader_.get());
183 // navigation pending case. 250 return !IsOutOfDate() && loader_.get() && loader_->supports_instant() &&
184 return is_displayable_ && !loader_->IsNavigationPending() && 251 !last_full_text_.empty();
185 !loader_->needs_reload();
186 }
187
188 bool InstantController::PrepareForCommit() {
189 if (is_out_of_date_ || !loader_.get())
190 return false;
191
192 // If we are in the visible (INSTANT) mode, return the status of the preview.
193 if (mode_ == INSTANT)
194 return IsCurrent();
195
196 TabContents* tab_contents = delegate_->GetInstantHostTabContents();
197 if (!tab_contents)
198 return false;
199
200 const TemplateURL* template_url =
201 TemplateURLServiceFactory::GetForProfile(tab_contents->profile())
202 ->GetDefaultSearchProvider();
203 if (!IsValidInstantTemplateURL(template_url) ||
204 loader_->template_url_id() != template_url->id() ||
205 loader_->IsNavigationPending() ||
206 loader_->is_determining_if_page_supports_instant()) {
207 return false;
208 }
209
210 // In the SUGGEST and HIDDEN modes, we must have sent an Update() by now, so
211 // check if the loader failed to process it.
212 if ((mode_ == SUGGEST || mode_ == HIDDEN)
213 && (!loader_->ready() || !loader_->http_status_ok())) {
214 return false;
215 }
216
217 // Ignore the suggested text, as we are about to commit the verbatim query.
218 string16 suggested_text;
219 UpdateLoader(tab_contents, template_url, last_url_, last_transition_type_,
220 last_user_text_, true, &suggested_text);
221 return true;
222 } 252 }
223 253
224 TabContents* InstantController::CommitCurrentPreview(InstantCommitType type) { 254 TabContents* InstantController::CommitCurrentPreview(InstantCommitType type) {
225 DCHECK(loader_.get()); 255 const TabContents* tab_contents = delegate_->GetInstantHostTabContents();
226 TabContents* tab_contents = delegate_->GetInstantHostTabContents(); 256 TabContents* preview = ReleasePreviewContents(type);
227 DCHECK(tab_contents);
228 TabContents* preview = ReleasePreviewContents(type, tab_contents);
229 preview->web_contents()->GetController().CopyStateFromAndPrune( 257 preview->web_contents()->GetController().CopyStateFromAndPrune(
230 &tab_contents->web_contents()->GetController()); 258 &tab_contents->web_contents()->GetController());
231 delegate_->CommitInstant(preview); 259 delegate_->CommitInstant(preview);
232 CompleteRelease(preview);
233 return preview; 260 return preview;
234 } 261 }
235 262
236 bool InstantController::CommitIfCurrent() { 263 TabContents* InstantController::ReleasePreviewContents(InstantCommitType type) {
237 if (IsCurrent()) { 264 std::string full_text = UTF16ToUTF8(last_full_text_);
238 CommitCurrentPreview(INSTANT_COMMIT_PRESSED_ENTER); 265 TabContents* tab_contents = loader_->ReleasePreviewContents(type, full_text);
239 return true; 266 AddPreviewUsageForHistogram(mode_, PREVIEW_COMMITTED);
240 } 267 // We may have gotten here from |CommitInstant|, which means the loader may
241 return false; 268 // still be on the stack. So, schedule a destruction for later.
242 } 269 MessageLoop::current()->DeleteSoon(FROM_HERE, loader_.release());
243 270 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.
244 void InstantController::SetCommitOnMouseUp() { 271 return tab_contents;
245 commit_on_mouse_up_ = true; 272 }
246 } 273
247 274 void InstantController::OnAutocompleteLostFocus() {
248 bool InstantController::IsMouseDownFromActivate() { 275 DCHECK(!is_showing_ || loader_.get());
249 DCHECK(loader_.get()); 276
250 return loader_->IsMouseDownFromActivate(); 277 // TODO(sreeram): Since we never delete the loader except when committing
251 } 278 // Instant, the loader may have a very stale page. Reload it when stale.
252 279 if (is_showing_ && loader_.get() && !loader_->IsMouseDownFromActivate())
253 #if defined(OS_MACOSX) 280 Hide();
254 void InstantController::OnAutocompleteLostFocus( 281 }
255 gfx::NativeView view_gaining_focus) { 282
256 // If |IsMouseDownFromActivate()| returns false, the RenderWidgetHostView did 283 void InstantController::OnAutocompleteGotFocus() {
257 // not receive a mouseDown event. Therefore, we should destroy the preview. 284 const TabContents* tab_contents = delegate_->GetInstantHostTabContents();
258 // Otherwise, the RWHV was clicked, so we commit the preview. 285
259 if (!IsCurrent() || !IsMouseDownFromActivate()) 286 // We could get here with no underlying tab if the Browser is in the process
260 DestroyPreviewContents(); 287 // of closing.
261 else 288 if (!tab_contents)
262 SetCommitOnMouseUp(); 289 return;
263 } 290
264 #else 291 // Since we don't have any autocomplete match to work with, we'll just use
265 void InstantController::OnAutocompleteLostFocus( 292 // the default search provider's Instant URL.
266 gfx::NativeView view_gaining_focus) { 293 const TemplateURL* template_url =
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
267 if (!IsCurrent()) { 294 TemplateURLServiceFactory::GetForProfile(tab_contents->profile())->
268 DestroyPreviewContents(); 295 GetDefaultSearchProvider();
269 return; 296
270 } 297 string16 instant_id;
271 298 GURL instant_url;
272 content::RenderWidgetHostView* rwhv = 299 if (!GetInstantURL(template_url, &instant_id, &instant_url))
273 GetPreviewContents()->web_contents()->GetRenderWidgetHostView(); 300 return;
274 if (!view_gaining_focus || !rwhv) { 301
275 DestroyPreviewContents(); 302 ResetLoader(instant_id, instant_url, tab_contents);
276 return; 303 }
277 } 304
278 305 bool InstantController::commit_on_mouse_up() const {
279 #if defined(TOOLKIT_VIEWS) 306 return loader_.get() && loader_->IsMouseDownFromActivate();
280 // For views the top level widget is always focused. If the focus change 307 }
281 // originated in views determine the child Widget from the view that is being 308
282 // focused. 309 void InstantController::SetSuggestions(
283 views::Widget* widget = 310 InstantLoader* loader,
284 views::Widget::GetWidgetForNativeView(view_gaining_focus); 311 const std::vector<std::string>& suggestions,
285 if (widget) { 312 InstantCompleteBehavior behavior) {
286 views::FocusManager* focus_manager = widget->GetFocusManager(); 313 DCHECK_EQ(loader_.get(), loader);
287 if (focus_manager && focus_manager->is_changing_focus() && 314 if (loader_ != loader || IsOutOfDate() || mode_ == SILENT || mode_ == HIDDEN)
288 focus_manager->GetFocusedView() && 315 return;
289 focus_manager->GetFocusedView()->GetWidget()) { 316
290 view_gaining_focus = 317 string16 suggestion;
291 focus_manager->GetFocusedView()->GetWidget()->GetNativeView(); 318 if (!suggestions.empty()) {
319 suggestion = UTF8ToUTF16(suggestions[0]);
320 }
321
322 string16 suggestion_lower = base::i18n::ToLower(suggestion);
323 string16 user_text_lower = base::i18n::ToLower(last_user_text_);
324 if (user_text_lower.size() >= suggestion_lower.size() ||
325 suggestion_lower.compare(0, user_text_lower.size(), user_text_lower)) {
326 suggestion.clear();
327 } else {
328 suggestion.erase(0, last_user_text_.size());
329 }
330 last_suggestion_ = suggestion;
331 last_complete_behavior_ = behavior;
332 if (!last_verbatim_)
333 delegate_->SetSuggestedText(suggestion, behavior);
334
335 TabContents* tab_contents = delegate_->GetInstantHostTabContents();
336 DCHECK(tab_contents);
337 if (mode_ != SUGGEST && tab_contents &&
338 !tab_contents->search_tab_helper()->model()->mode().is_ntp()) {
339 Show();
340 }
341
342 loader_ready_ = true;
343 }
344
345 void InstantController::CommitInstantLoader(InstantLoader* loader) {
346 DCHECK_EQ(loader_.get(), loader);
347 DCHECK(is_showing_ && !IsOutOfDate()) << is_showing_;
348 if (loader_ != loader || !is_showing_ || IsOutOfDate())
349 return;
350
351 CommitCurrentPreview(INSTANT_COMMIT_FOCUS_LOST);
352 }
353
354 void InstantController::InstantLoaderPreviewLoaded(InstantLoader* loader) {
355 DCHECK_EQ(loader_.get(), loader);
356 AddPreviewUsageForHistogram(mode_, PREVIEW_LOADED);
357 }
358
359 void InstantController::InstantSupportDetermined(InstantLoader* loader,
360 bool supports_instant) {
361 DCHECK_EQ(loader_.get(), loader);
362 if (supports_instant) {
363 blacklisted_ids_.erase(loader->loader_id());
364 } else {
365 ++blacklisted_ids_[loader->loader_id()];
366 if (loader_ == loader) {
367 // Because of the state of the stack, we can't destroy the loader now.
368 if (GetPreviewContents())
369 AddPreviewUsageForHistogram(mode_, PREVIEW_DELETED);
370 MessageLoop::current()->DeleteSoon(FROM_HERE, loader_.release());
371 DeleteLoader();
292 } 372 }
293 } 373 }
294 #endif
295
296 gfx::NativeView tab_view =
297 GetPreviewContents()->web_contents()->GetNativeView();
298 // Focus is going to the renderer.
299 if (rwhv->GetNativeView() == view_gaining_focus ||
300 tab_view == view_gaining_focus) {
301 if (!IsMouseDownFromActivate()) {
302 // If the mouse is not down, focus is not going to the renderer. Someone
303 // else moved focus and we shouldn't commit.
304 DestroyPreviewContents();
305 return;
306 }
307
308 // We're showing instant results. As instant results may shift when
309 // committing we commit on the mouse up. This way a slow click still works
310 // fine.
311 SetCommitOnMouseUp();
312 return;
313 }
314
315 // Walk up the view hierarchy. If the view gaining focus is a subview of the
316 // WebContents view (such as a windowed plugin or http auth dialog), we want
317 // to keep the preview contents. Otherwise, focus has gone somewhere else,
318 // such as the JS inspector, and we want to cancel the preview.
319 gfx::NativeView view_gaining_focus_ancestor = view_gaining_focus;
320 while (view_gaining_focus_ancestor &&
321 view_gaining_focus_ancestor != tab_view) {
322 view_gaining_focus_ancestor =
323 platform_util::GetParent(view_gaining_focus_ancestor);
324 }
325
326 if (view_gaining_focus_ancestor) {
327 CommitCurrentPreview(INSTANT_COMMIT_FOCUS_LOST);
328 return;
329 }
330
331 DestroyPreviewContents();
332 }
333 #endif
334
335 void InstantController::OnAutocompleteGotFocus() {
336 TabContents* tab_contents = delegate_->GetInstantHostTabContents();
337 if (!tab_contents)
338 return;
339
340 const TemplateURL* template_url =
341 TemplateURLServiceFactory::GetForProfile(tab_contents->profile())
342 ->GetDefaultSearchProvider();
343 if (!IsValidInstantTemplateURL(template_url))
344 return;
345
346 if (!loader_.get() || loader_->template_url_id() != template_url->id())
347 loader_.reset(new InstantLoader(this, template_url->id(), std::string()));
348 loader_->MaybeLoadInstantURL(tab_contents, template_url);
349 }
350
351 TabContents* InstantController::ReleasePreviewContents(
352 InstantCommitType type,
353 TabContents* current_tab) {
354 if (!loader_.get())
355 return NULL;
356
357 TabContents* tab = loader_->ReleasePreviewContents(type, current_tab);
358 ClearBlacklist();
359 is_out_of_date_ = true;
360 is_displayable_ = false;
361 commit_on_mouse_up_ = false;
362 omnibox_bounds_ = gfx::Rect();
363 loader_.reset();
364 return tab;
365 }
366
367 void InstantController::CompleteRelease(TabContents* tab) {
368 tab->blocked_content_tab_helper()->SetAllContentsBlocked(false);
369 }
370
371 TabContents* InstantController::GetPreviewContents() const {
372 return loader_.get() ? loader_->preview_contents() : NULL;
373 }
374
375 void InstantController::InstantStatusChanged(InstantLoader* loader) {
376 DCHECK(loader_.get());
377 UpdateIsDisplayable();
378 }
379
380 void InstantController::SetSuggestedTextFor(
381 InstantLoader* loader,
382 const string16& text,
383 InstantCompleteBehavior behavior) {
384 if (is_out_of_date_)
385 return;
386
387 if (mode_ == INSTANT || mode_ == SUGGEST)
388 delegate_->SetSuggestedText(text, behavior);
389 }
390
391 gfx::Rect InstantController::GetInstantBounds() {
392 return delegate_->GetInstantBounds();
393 }
394
395 bool InstantController::ShouldCommitInstantOnMouseUp() {
396 return commit_on_mouse_up_;
397 }
398
399 void InstantController::CommitInstantLoader(InstantLoader* loader) {
400 if (loader_.get() && loader_.get() == loader) {
401 CommitCurrentPreview(INSTANT_COMMIT_FOCUS_LOST);
402 } else {
403 // This can happen if the mouse was down, we swapped out the preview and
404 // the mouse was released. Generally this shouldn't happen, but if it does
405 // revert.
406 DestroyPreviewContents();
407 }
408 }
409
410 void InstantController::InstantLoaderDoesntSupportInstant(
411 InstantLoader* loader) {
412 VLOG(1) << "provider does not support instant";
413
414 // Don't attempt to use instant for this search engine again.
415 BlacklistFromInstant();
416 }
417
418 void InstantController::AddToBlacklist(InstantLoader* loader, const GURL& url) {
419 // Don't attempt to use instant for this search engine again.
420 BlacklistFromInstant();
421 } 374 }
422 375
423 void InstantController::SwappedTabContents(InstantLoader* loader) { 376 void InstantController::SwappedTabContents(InstantLoader* loader) {
424 if (is_displayable_) 377 DCHECK_EQ(loader_.get(), loader);
425 delegate_->ShowInstant(loader->preview_contents()); 378 if (loader_ == loader && is_showing_)
426 } 379 delegate_->ShowInstant();
427 380 }
428 void InstantController::InstantLoaderContentsFocused() { 381
382 void InstantController::InstantLoaderContentsFocused(InstantLoader* loader) {
383 DCHECK_EQ(loader_.get(), loader);
384 DCHECK(is_showing_ && !IsOutOfDate()) << is_showing_;
429 #if defined(USE_AURA) 385 #if defined(USE_AURA)
430 // On aura the omnibox only receives a focus lost if we initiate the focus 386 // On aura the omnibox only receives a focus lost if we initiate the focus
431 // change. This does that. 387 // change. This does that.
432 if (mode_ == INSTANT) 388 if (is_showing_ && !IsOutOfDate())
433 delegate_->InstantPreviewFocused(); 389 delegate_->InstantPreviewFocused();
434 #endif 390 #endif
435 } 391 }
436 392
437 void InstantController::UpdateIsDisplayable() { 393 void InstantController::ResetLoader(const string16& instant_id,
438 bool displayable = !is_out_of_date_ && loader_.get() && loader_->ready() && 394 const GURL& instant_url,
439 loader_->http_status_ok(); 395 const TabContents* tab_contents) {
440 if (displayable == is_displayable_ || mode_ != INSTANT) 396 if (loader_.get() && loader_->loader_id() != instant_id)
441 return; 397 DeleteLoader();
442 398
443 is_displayable_ = displayable; 399 if (!loader_.get()) {
444 if (!is_displayable_) { 400 loader_.reset(
445 delegate_->HideInstant(); 401 new InstantLoader(this, instant_id, instant_url, tab_contents));
446 } else { 402 AddPreviewUsageForHistogram(mode_, PREVIEW_CREATED);
447 delegate_->ShowInstant(loader_->preview_contents()); 403 }
448 content::NotificationService::current()->Notify( 404
449 chrome::NOTIFICATION_INSTANT_CONTROLLER_SHOWN, 405 last_tab_contents_ = tab_contents;
450 content::Source<InstantController>(this), 406 }
451 content::NotificationService::NoDetails()); 407
452 } 408 void InstantController::DeleteLoader() {
453 } 409 Hide();
454 410 last_full_text_.clear();
455 void InstantController::UpdateLoader(TabContents* tab_contents, 411 last_user_text_.clear();
456 const TemplateURL* template_url, 412 last_verbatim_ = false;
457 const GURL& url, 413 last_suggestion_.clear();
458 content::PageTransition transition_type, 414 last_complete_behavior_ = INSTANT_COMPLETE_NOW;
459 const string16& user_text, 415 last_omnibox_bounds_ = gfx::Rect();
460 bool verbatim, 416 if (GetPreviewContents())
461 string16* suggested_text) { 417 AddPreviewUsageForHistogram(mode_, PREVIEW_DELETED);
462 if (mode_ == INSTANT) 418 loader_.reset();
463 loader_->SetOmniboxBounds(omnibox_bounds_); 419 }
464 loader_->Update(tab_contents, template_url, url, transition_type, user_text, 420
465 verbatim, suggested_text); 421 void InstantController::Show() {
466 UpdateIsDisplayable(); 422 if (!is_showing_) {
467 // For the HIDDEN and SILENT modes, don't send back suggestions. 423 is_showing_ = true;
468 if (mode_ == HIDDEN || mode_ == SILENT) 424 delegate_->ShowInstant();
469 suggested_text->clear(); 425 AddPreviewUsageForHistogram(mode_, PREVIEW_SHOWED);
470 } 426 }
471 427 }
472 // Returns true if |template_url| is a valid TemplateURL for use by instant. 428
473 bool InstantController::IsValidInstantTemplateURL( 429 void InstantController::SendBoundsToPage() {
474 const TemplateURL* template_url) { 430 if (last_omnibox_bounds_ == omnibox_bounds_ || IsOutOfDate() ||
475 return template_url && template_url->id() && 431 !GetPreviewContents() || loader_->IsMouseDownFromActivate()) {
476 template_url->instant_url_ref().SupportsReplacement() && 432 return;
477 !IsBlacklistedFromInstant(template_url->id()); 433 }
478 } 434
479 435 last_omnibox_bounds_ = omnibox_bounds_;
480 void InstantController::BlacklistFromInstant() { 436 gfx::Rect preview_bounds = delegate_->GetInstantBounds();
481 if (!loader_.get()) 437 gfx::Rect intersection = omnibox_bounds_.Intersect(preview_bounds);
482 return; 438
483 439 // Translate into window coordinates.
484 DCHECK(loader_->template_url_id()); 440 if (!intersection.IsEmpty()) {
485 blacklisted_ids_.insert(loader_->template_url_id()); 441 intersection.Offset(-preview_bounds.origin().x(),
486 442 -preview_bounds.origin().y());
487 // Because of the state of the stack we can't destroy the loader now. 443 }
488 ScheduleDestroy(loader_.release()); 444
489 UpdateIsDisplayable(); 445 // In the current Chrome UI, these must always be true so they sanity check
490 } 446 // the above operations. In a future UI, these may be removed or adjusted.
491 447 // There is no point in sanity-checking |intersection.y()| because the omnibox
492 bool InstantController::IsBlacklistedFromInstant(TemplateURLID id) { 448 // can be placed anywhere vertically relative to the preview (for example, in
493 return blacklisted_ids_.count(id) > 0; 449 // Mac fullscreen mode, the omnibox is fully enclosed by the preview bounds).
494 } 450 DCHECK_LE(0, intersection.x());
495 451 DCHECK_LE(0, intersection.width());
496 void InstantController::ClearBlacklist() { 452 DCHECK_LE(0, intersection.height());
497 blacklisted_ids_.clear(); 453
498 } 454 loader_->SetOmniboxBounds(intersection);
499 455 }
500 void InstantController::ScheduleDestroy(InstantLoader* loader) { 456
501 loaders_to_destroy_.push_back(loader); 457 bool InstantController::GetInstantURL(const TemplateURL* template_url,
502 if (!weak_factory_.HasWeakPtrs()) { 458 string16* instant_id,
503 MessageLoop::current()->PostTask( 459 GURL* instant_url) const {
504 FROM_HERE, base::Bind(&InstantController::DestroyLoaders, 460 CommandLine* command_line = CommandLine::ForCurrentProcess();
505 weak_factory_.GetWeakPtr())); 461 if (command_line->HasSwitch(switches::kInstantURL)) {
506 } 462 *instant_url =
507 } 463 GURL(command_line->GetSwitchValueASCII(switches::kInstantURL));
508 464 instant_id->clear();
509 void InstantController::DestroyLoaders() { 465 return true;
510 loaders_to_destroy_.reset(); 466 }
511 } 467
468 if (!template_url)
469 return false;
470
471 std::map<string16, int>::const_iterator iter =
472 blacklisted_ids_.find(template_url->keyword());
473 if (iter != blacklisted_ids_.end() &&
474 iter->second > kMaxInstantSupportFailures) {
475 return false;
476 }
477
478 const TemplateURLRef& instant_url_ref = template_url->instant_url_ref();
479 if (!instant_url_ref.IsValid())
480 return false;
481
482 *instant_id = template_url->keyword();
483 *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.
484 GURL(instant_url_ref.ReplaceSearchTerms(
485 TemplateURLRef::SearchTermsArgs(string16()))) :
486 GURL(instant_url_ref.GetURL());
487
488 return true;
489 }
490
491 bool InstantController::IsOutOfDate() const {
492 return !last_tab_contents_ ||
493 last_tab_contents_ != delegate_->GetInstantHostTabContents();
494 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698