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

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

Issue 11421079: Persist the Instant API to committed search result pages. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years 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 2012 The Chromium Authors. All rights reserved. 1 // Copyright 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 "base/command_line.h" 7 #include "base/command_line.h"
8 #include "base/metrics/histogram.h" 8 #include "base/metrics/histogram.h"
9 #include "base/string_util.h" 9 #include "base/string_util.h"
10 #include "base/utf_string_conversions.h" 10 #include "base/utf_string_conversions.h"
(...skipping 158 matching lines...) Expand 10 before | Expand all | Expand 10 after
169 169
170 DVLOG(1) << "Update: " << AutocompleteMatch::TypeToString(match.type) 170 DVLOG(1) << "Update: " << AutocompleteMatch::TypeToString(match.type)
171 << " user_text='" << user_text << "' full_text='" << full_text << "'" 171 << " user_text='" << user_text << "' full_text='" << full_text << "'"
172 << " verbatim=" << verbatim << " typing=" << user_input_in_progress 172 << " verbatim=" << verbatim << " typing=" << user_input_in_progress
173 << " popup=" << omnibox_popup_is_open; 173 << " popup=" << omnibox_popup_is_open;
174 174
175 // If the popup is open, the user has to be typing. 175 // If the popup is open, the user has to be typing.
176 DCHECK(!omnibox_popup_is_open || user_input_in_progress); 176 DCHECK(!omnibox_popup_is_open || user_input_in_progress);
177 177
178 // If the popup is closed, there should be no inline autocompletion. 178 // If the popup is closed, there should be no inline autocompletion.
179 DCHECK(omnibox_popup_is_open || user_text == full_text) << user_text << "|" 179 DCHECK(omnibox_popup_is_open || user_text.empty() || user_text == full_text)
180 << full_text; 180 << user_text << "|" << full_text;
181 181
182 // If there's inline autocompletion, the query has to be verbatim. 182 // If there's inline autocompletion, the query has to be verbatim.
183 DCHECK(user_text == full_text || verbatim) << user_text << "|" << full_text; 183 DCHECK(user_text.empty() || user_text == full_text || verbatim)
184 << user_text << "|" << full_text;
184 185
185 // If there's no text in the omnibox, the user can't have typed any. 186 // If there's no text in the omnibox, the user can't have typed any.
186 DCHECK(!full_text.empty() || user_text.empty()) << user_text; 187 DCHECK(!full_text.empty() || user_text.empty()) << user_text;
187 188
188 // If the user isn't typing, and the popup is closed, there can't be any 189 // If the user isn't typing, and the popup is closed, there can't be any
189 // user-typed text. 190 // user-typed text.
190 DCHECK(user_input_in_progress || omnibox_popup_is_open || user_text.empty()) 191 DCHECK(user_input_in_progress || omnibox_popup_is_open || user_text.empty())
191 << user_text; 192 << user_text;
192 193
193 // In non-extended mode, SearchModeChanged() is never called, so fake it. The 194 // In non-extended mode, SearchModeChanged() is never called, so fake it. The
194 // mode is set to "disallow suggestions" here, so that if one of the early 195 // mode is set to "disallow suggestions" here, so that if one of the early
195 // "return false" conditions is hit, suggestions will be disallowed. If the 196 // "return false" conditions is hit, suggestions will be disallowed. If the
196 // query is sent to the loader, the mode is set to "allow" further below. 197 // query is sent to the loader, the mode is set to "allow" further below.
197 if (!extended_enabled_) 198 if (!extended_enabled_)
198 search_mode_.mode = chrome::search::Mode::MODE_DEFAULT; 199 search_mode_.mode = chrome::search::Mode::MODE_DEFAULT;
199 200
200 // If there's no active tab, the browser is closing. 201 // If there's no active tab, the browser is closing.
201 const TabContents* const active_tab = browser_->GetActiveTabContents(); 202 const TabContents* const active_tab = browser_->GetActiveTabContents();
202 if (!active_tab) { 203 if (!active_tab) {
203 Hide(true); 204 Hide(true);
204 return false; 205 return false;
205 } 206 }
206 207
208 // Ensure we have a loader that can process this match. First, try to use the
209 // TemplateURL of the |match|. If that's invalid, in non-extended mode, stop.
210 // In extended mode, try using the default search engine, but only when the
211 // match is for a URL (i.e., not some other kind of non-Instant search).
212 // A completely blank query shows up as a search, and we do want to allow
213 // that, hence the "!user_text.empty()" clause.
214 Profile* const profile = active_tab->profile();
215 const bool match_is_search = AutocompleteMatch::IsSearchType(match.type);
216 if (!ResetLoader(match.GetTemplateURL(profile, false), active_tab) &&
Jered 2012/11/27 19:21:57 Why reset the loader here if client_ is set?
sreeram 2012/11/29 07:33:19 Done.
217 (!extended_enabled_ || (match_is_search && !user_text.empty()) ||
218 (!client_ && !CreateDefaultLoader()))) {
219 Hide(true);
220 return false;
221 }
222
207 // Legend: OPIO == |omnibox_popup_is_open|, UIIP = |user_input_in_progress|. 223 // Legend: OPIO == |omnibox_popup_is_open|, UIIP = |user_input_in_progress|.
208 // 224 //
209 // # OPIO UIIP full_text Notes 225 // # OPIO UIIP full_text Notes
210 // - ---- ---- --------- ----- 226 // - ---- ---- --------- -----
211 // 1 no no blank } Navigation, or user hit Escape. |full_text| is 227 // 1 no no blank } Navigation, or user hit Escape. |full_text| is
212 // 2 no no non-blank } blank if the page is NTP, non-blank otherwise. 228 // 2 no no non-blank } blank if the page is NTP, non-blank otherwise.
213 // 229 //
214 // 3 no yes blank User backspaced away all omnibox text. 230 // 3 no yes blank User backspaced away all omnibox text.
215 // 231 //
216 // 4 no yes non-blank User switched to a tab with a partial query. 232 // 4 no yes non-blank User switched to a tab with a partial query.
217 // 233 //
218 // 5 yes no blank } Impossible. DCHECK()ed above. 234 // 5 yes no blank } Impossible. DCHECK()ed above.
219 // 6 yes no non-blank } 235 // 6 yes no non-blank }
220 // 236 //
221 // 7 yes yes blank User typed a "?" into the omnibox. 237 // 7 yes yes blank User typed a "?" into the omnibox.
222 // 238 //
223 // 8 yes yes non-blank User typed text into the omnibox. 239 // 8 yes yes non-blank User typed text into the omnibox.
224 // 240 //
225 // In non-extended mode, #1 to #7 call Hide(). #8 calls loader_->Update(). 241 // In non-extended mode, #1 to #7 call Hide(). #8 calls loader_->Update().
226 // 242 //
227 // In extended mode, #2 and #4 call Hide(). #1 doesn't Hide() as the preview 243 // In extended mode, #2 and #4 call Hide(). #1 doesn't Hide() as the preview
228 // may be showing custom NTP content, but doesn't Update() either. #3 and #7 244 // may be showing custom NTP content, but doesn't Update() either. #3 and #7
229 // don't Hide(), but send a blank query to Update(). #8 calls Update(). 245 // don't Hide(), and send a blank query to Update(). #8 calls Update().
230 246
231 if (extended_enabled_) { 247 if (extended_enabled_) {
232 if (!omnibox_popup_is_open) { 248 if (!omnibox_popup_is_open) {
233 if (!full_text.empty()) { 249 if (!user_input_in_progress) {
234 Hide(true); 250 if (!full_text.empty()) {
235 return false; 251 Hide(true); // #2
samarth 2012/11/27 18:09:43 Mostly for my understanding: Hide() will be a no-o
sreeram 2012/11/29 07:33:19 No, Hide() won't be a no-op. For one, it will actu
252 // If the user hit Escape when on a search results page, restore the
253 // original results by resending the query. If the user was not on a
254 // search results page, SearchModeChanged() would've caught it, and
255 // |client_| will be NULL, so we won't accidentally send non-query
256 // |full_text| to |client_|. Except if the user just switched tabs.
257 // Hence the comparison of WebContents, to catch that case as well.
258 if (client_ && client_->contents() == active_tab->web_contents())
259 client_->Update(full_text, true);
260 }
261 return false; // #1
262 } else {
263 if (!full_text.empty()) {
264 Hide(true); // #4
265 if (match_is_search) {
266 // The user just switched to a tab with a partial query already in
267 // the omnibox. This tab may or may not be a search results page
268 // (SearchModeChanged() hasn't been called yet). So, assume that it
269 // could be, and store |full_text| so that if the user then hits
270 // Enter, we'll send the correct query in client_->Submit().
271 last_full_text_ = full_text;
272 last_match_was_search_ = true;
273 }
274 return false;
275 }
236 } 276 }
237 if (!user_input_in_progress && full_text.empty())
238 return false;
239 } 277 }
240 } else if (!omnibox_popup_is_open || full_text.empty()) { 278 } else if (!omnibox_popup_is_open || full_text.empty()) {
241 // Update() can be called if the user clicks the preview while composing 279 // Update() can be called if the user clicks the preview while composing
242 // text with an IME. If so, we should commit on mouse up, so don't Hide(). 280 // text with an IME. If so, we should commit on mouse up, so don't Hide().
243 if (!GetPreviewContents() || !loader_->IsPointerDownFromActivate()) 281 if (!loader_ || !loader_->IsPointerDownFromActivate())
244 Hide(true); 282 Hide(true);
245 return false; 283 return false;
246 } 284 }
247 285
248 // Ensure we have a loader that can process this match. First, try to use the
249 // TemplateURL of the |match|. If that's invalid, in non-extended mode, stop.
250 // In extended mode, try using the default search engine, but only when the
251 // match is for a URL (i.e., not some other kind of non-Instant search).
252 // A completely blank query shows up as a search, and we do want to allow
253 // that, hence the "!full_text.empty()" clause.
254 Profile* const profile = active_tab->profile();
255 const bool match_is_search = AutocompleteMatch::IsSearchType(match.type);
256 if (!ResetLoader(match.GetTemplateURL(profile, false), active_tab) &&
257 (!extended_enabled_ || (match_is_search && !full_text.empty()) ||
258 !CreateDefaultLoader())) {
259 Hide(true);
260 return false;
261 }
262
263 // If the user continues typing the same query as the suggested text is 286 // If the user continues typing the same query as the suggested text is
264 // showing, reuse the suggestion (but only for INSTANT_COMPLETE_NEVER). 287 // showing, reuse the suggestion (but only for INSTANT_COMPLETE_NEVER).
265 bool reused_suggestion = false; 288 bool reused_suggestion = false;
266 if (last_suggestion_.behavior == INSTANT_COMPLETE_NEVER) { 289 if (last_suggestion_.behavior == INSTANT_COMPLETE_NEVER) {
267 if (StartsWith(last_user_text_, user_text, false) && !user_text.empty()) { 290 if (StartsWith(last_user_text_, user_text, false) && !user_text.empty()) {
268 // The user is backspacing away characters. 291 // The user is backspacing away characters.
269 last_suggestion_.text.insert(0, last_user_text_, user_text.size(), 292 last_suggestion_.text.insert(0, last_user_text_, user_text.size(),
270 last_user_text_.size() - user_text.size()); 293 last_user_text_.size() - user_text.size());
271 reused_suggestion = true; 294 reused_suggestion = true;
272 } else if (StartsWith(user_text, last_user_text_, false)) { 295 } else if (StartsWith(user_text, last_user_text_, false)) {
273 // The user is typing forward. Normalize any added characters. 296 // The user is typing forward. Normalize any added characters.
274 reused_suggestion = NormalizeAndStripPrefix(&last_suggestion_.text, 297 reused_suggestion = NormalizeAndStripPrefix(&last_suggestion_.text,
275 string16(user_text, last_user_text_.size())); 298 string16(user_text, last_user_text_.size()));
276 } 299 }
277 } 300 }
278 301
279 last_user_text_ = user_text; 302 last_user_text_ = user_text;
280 last_full_text_ = full_text; 303 last_full_text_ = full_text;
281 last_verbatim_ = verbatim; 304 last_verbatim_ = verbatim;
282 305
283 if (!reused_suggestion) 306 if (!reused_suggestion)
284 last_suggestion_ = InstantSuggestion(); 307 last_suggestion_ = InstantSuggestion();
285 308
286 last_transition_type_ = match.transition; 309 last_transition_type_ = match.transition;
287 last_match_was_search_ = match_is_search; 310 last_match_was_search_ = match_is_search;
288 url_for_history_ = match.destination_url; 311 url_for_history_ = match.destination_url;
289 312
290 // Store the first interaction time for use with latency histograms. 313 // Store the first interaction time for use with latency histograms.
291 if (first_interaction_time_.is_null()) 314 if (loader_ == CurrentClient() && first_interaction_time_.is_null())
Jered 2012/11/27 19:21:57 Please extract a method like IsCommitted() for loa
sreeram 2012/11/29 07:33:19 I've gone with the simpler "if (instant_tab_)" idi
292 first_interaction_time_ = base::Time::Now(); 315 first_interaction_time_ = base::Time::Now();
293 316
294 // Allow search suggestions. In extended mode, SearchModeChanged() will set 317 // Allow search suggestions. In extended mode, SearchModeChanged() will set
295 // this, but it's not called in non-extended mode, so fake it. 318 // this, but it's not called in non-extended mode, so fake it.
296 if (!extended_enabled_) 319 if (!extended_enabled_)
297 search_mode_.mode = chrome::search::Mode::MODE_SEARCH_SUGGESTIONS; 320 search_mode_.mode = chrome::search::Mode::MODE_SEARCH_SUGGESTIONS;
298 321
299 loader_->Update(extended_enabled_ ? user_text : full_text, verbatim); 322 CurrentClient()->Update(extended_enabled_ ? user_text : full_text, verbatim);
323
324 if (loader_ == CurrentClient())
325 loader_->DidNavigate(history::HistoryAddPageArgs());
300 326
301 content::NotificationService::current()->Notify( 327 content::NotificationService::current()->Notify(
302 chrome::NOTIFICATION_INSTANT_CONTROLLER_UPDATED, 328 chrome::NOTIFICATION_INSTANT_CONTROLLER_UPDATED,
303 content::Source<InstantController>(this), 329 content::Source<InstantController>(this),
304 content::NotificationService::NoDetails()); 330 content::NotificationService::NoDetails());
305 331
306 // We don't have new suggestions yet, but we can either reuse the existing 332 // We don't have new suggestions yet, but we can either reuse the existing
307 // suggestion or reset the existing "gray text". 333 // suggestion or reset the existing "gray text".
308 browser_->SetInstantSuggestion(last_suggestion_); 334 browser_->SetInstantSuggestion(last_suggestion_);
309 335
310 // Though we may have handled a URL match above, we return false here, so that 336 // Though we may have handled a URL match above, we return false here, so that
311 // omnibox prerendering can kick in. TODO(sreeram): Remove this (and always 337 // omnibox prerendering can kick in. TODO(sreeram): Remove this (and always
312 // return true) once we are able to commit URLs as well. 338 // return true) once we are able to commit URLs as well.
313 return match_is_search; 339 return match_is_search;
314 } 340 }
315 341
316 // TODO(tonyg): This method only fires when the omnibox bounds change. It also 342 // TODO(tonyg): This method only fires when the omnibox bounds change. It also
317 // needs to fire when the preview bounds change (e.g.: open/close info bar). 343 // needs to fire when the preview bounds change (e.g.: open/close info bar).
318 void InstantController::SetOmniboxBounds(const gfx::Rect& bounds) { 344 void InstantController::SetOmniboxBounds(const gfx::Rect& bounds) {
319 if (!extended_enabled_ && !instant_enabled_) 345 if (!extended_enabled_ && !instant_enabled_)
320 return; 346 return;
321 347
322 if (omnibox_bounds_ == bounds) 348 if (omnibox_bounds_ == bounds)
samarth 2012/11/28 06:31:15 This no longer works correctly. Well, we don't ca
sreeram 2012/11/29 07:33:19 As discussed offline, this does work correctly (be
323 return; 349 return;
324 350
325 omnibox_bounds_ = bounds; 351 omnibox_bounds_ = bounds;
326 if (omnibox_bounds_.height() > last_omnibox_bounds_.height()) { 352 if (omnibox_bounds_.height() > last_omnibox_bounds_.height()) {
327 update_bounds_timer_.Stop(); 353 update_bounds_timer_.Stop();
328 SendBoundsToPage(); 354 SendBoundsToPage();
329 } else if (!update_bounds_timer_.IsRunning()) { 355 } else if (!update_bounds_timer_.IsRunning()) {
330 update_bounds_timer_.Start(FROM_HERE, 356 update_bounds_timer_.Start(FROM_HERE,
331 base::TimeDelta::FromMilliseconds(kUpdateBoundsDelayMS), this, 357 base::TimeDelta::FromMilliseconds(kUpdateBoundsDelayMS), this,
332 &InstantController::SendBoundsToPage); 358 &InstantController::SendBoundsToPage);
333 } 359 }
334 } 360 }
335 361
336 void InstantController::HandleAutocompleteResults( 362 void InstantController::HandleAutocompleteResults(
337 const std::vector<AutocompleteProvider*>& providers) { 363 const std::vector<AutocompleteProvider*>& providers) {
338 if (!extended_enabled_) 364 if (!extended_enabled_)
339 return; 365 return;
340 366
sky 2012/11/27 01:07:51 Shouldn't we early out if no client?
sreeram 2012/11/29 07:33:19 Done.
341 if (!GetPreviewContents())
342 return;
343
344 DVLOG(1) << "AutocompleteResults:"; 367 DVLOG(1) << "AutocompleteResults:";
345 std::vector<InstantAutocompleteResult> results; 368 std::vector<InstantAutocompleteResult> results;
346 for (ACProviders::const_iterator provider = providers.begin(); 369 for (ACProviders::const_iterator provider = providers.begin();
347 provider != providers.end(); ++provider) { 370 provider != providers.end(); ++provider) {
348 for (ACMatches::const_iterator match = (*provider)->matches().begin(); 371 for (ACMatches::const_iterator match = (*provider)->matches().begin();
349 match != (*provider)->matches().end(); ++match) { 372 match != (*provider)->matches().end(); ++match) {
350 InstantAutocompleteResult result; 373 InstantAutocompleteResult result;
351 result.provider = UTF8ToUTF16((*provider)->GetName()); 374 result.provider = UTF8ToUTF16((*provider)->GetName());
352 result.type = UTF8ToUTF16(AutocompleteMatch::TypeToString(match->type)); 375 result.type = UTF8ToUTF16(AutocompleteMatch::TypeToString(match->type));
353 result.description = match->description; 376 result.description = match->description;
354 result.destination_url = UTF8ToUTF16(match->destination_url.spec()); 377 result.destination_url = UTF8ToUTF16(match->destination_url.spec());
355 result.relevance = match->relevance; 378 result.relevance = match->relevance;
356 DVLOG(1) << " " << result.relevance << " " << result.type << " " 379 DVLOG(1) << " " << result.relevance << " " << result.type << " "
357 << result.provider << " " << result.destination_url << " '" 380 << result.provider << " " << result.destination_url << " '"
358 << result.description << "'"; 381 << result.description << "'";
359 results.push_back(result); 382 results.push_back(result);
360 } 383 }
361 } 384 }
362 385
363 loader_->SendAutocompleteResults(results); 386 if (InstantClient* client = CurrentClient())
samarth 2012/11/27 18:09:43 nit: does the Chrome style guide allow this? I hav
sreeram 2012/11/29 07:33:19 The style guide doesn't mention it. @sky hates it.
387 client->SendAutocompleteResults(results);
364 } 388 }
365 389
366 bool InstantController::OnUpOrDownKeyPressed(int count) { 390 bool InstantController::OnUpOrDownKeyPressed(int count) {
367 if (!extended_enabled_) 391 if (!extended_enabled_)
368 return false; 392 return false;
369 393
370 if (!GetPreviewContents()) 394 if (InstantClient* client = CurrentClient()) {
371 return false; 395 client->UpOrDownKeyPressed(count);
396 return true;
397 }
372 398
373 loader_->OnUpOrDownKeyPressed(count); 399 return false;
374 return true;
375 } 400 }
376 401
377 TabContents* InstantController::GetPreviewContents() const { 402 TabContents* InstantController::GetPreviewContents() const {
378 return loader_ ? loader_->preview_contents() : NULL; 403 return loader_ ? loader_->preview_contents() : NULL;
379 } 404 }
380 405
381 bool InstantController::IsCurrent() const { 406 bool InstantController::IsCurrent() const {
samarth 2012/11/27 18:09:43 What should IsCurrent return if client_ is set? Fo
sreeram 2012/11/29 07:33:19 If |instant_tab_| is non-NULL, IsCurrent() should
382 return model_.mode().is_search_suggestions() && last_match_was_search_; 407 return model_.mode().is_search_suggestions() && last_match_was_search_;
383 } 408 }
384 409
385 bool InstantController::CommitIfCurrent(InstantCommitType type) { 410 bool InstantController::CommitIfCurrent(InstantCommitType type) {
386 if (!extended_enabled_ && !instant_enabled_) 411 if (!extended_enabled_ && !instant_enabled_)
387 return false; 412 return false;
388 413
414 if (client_ == CurrentClient() && last_match_was_search_ &&
Jered 2012/11/27 19:21:57 I am very confused. Why doesn't this cancel someti
sreeram 2012/11/29 07:33:19 Cancel() isn't applicable in the case of a committ
415 type == INSTANT_COMMIT_PRESSED_ENTER) {
416 client_->Submit(last_full_text_);
417 return true;
418 }
419
389 if (!IsCurrent()) 420 if (!IsCurrent())
390 return false; 421 return false;
391 422
392 DVLOG(1) << "CommitIfCurrent"; 423 DVLOG(1) << "CommitIfCurrent";
393 TabContents* preview = loader_->ReleasePreviewContents(type, last_full_text_); 424
425 if (type == INSTANT_COMMIT_FOCUS_LOST)
426 loader_->Cancel(last_full_text_);
427 else
428 loader_->Submit(last_full_text_);
429 loader_->CleanupPreviewContents();
430
431 TabContents* preview = loader_->release_preview_contents();
394 432
395 if (extended_enabled_) { 433 if (extended_enabled_) {
396 // Consider what's happening: 434 // Consider what's happening:
397 // 1. The user has typed a query in the omnibox and committed it (either 435 // 1. The user has typed a query in the omnibox and committed it (either
398 // by pressing Enter or clicking on the preview). 436 // by pressing Enter or clicking on the preview).
399 // 2. We commit the preview to the tab strip, and tell the page. 437 // 2. We commit the preview to the tab strip, and tell the page.
400 // 3. The page will update the URL hash fragment with the query terms. 438 // 3. The page will update the URL hash fragment with the query terms.
401 // After steps 1 and 3, the omnibox will show the query terms. However, if 439 // After steps 1 and 3, the omnibox will show the query terms. However, if
402 // the URL we are committing at step 2 doesn't already have query terms, it 440 // the URL we are committing at step 2 doesn't already have query terms, it
403 // will flash for a brief moment as a plain URL. So, avoid that flicker by 441 // will flash for a brief moment as a plain URL. So, avoid that flicker by
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after
487 if (!IsCurrent() || !GetPreviewContents()) { 525 if (!IsCurrent() || !GetPreviewContents()) {
488 OnStaleLoader(); 526 OnStaleLoader();
489 return; 527 return;
490 } 528 }
491 529
492 #if defined(OS_MACOSX) 530 #if defined(OS_MACOSX)
493 if (!loader_->IsPointerDownFromActivate()) 531 if (!loader_->IsPointerDownFromActivate())
494 Hide(true); 532 Hide(true);
495 #else 533 #else
496 if (IsViewInContents(GetViewGainingFocus(view_gaining_focus), 534 if (IsViewInContents(GetViewGainingFocus(view_gaining_focus),
497 GetPreviewContents()->web_contents())) 535 loader_->contents()))
498 CommitIfCurrent(INSTANT_COMMIT_FOCUS_LOST); 536 CommitIfCurrent(INSTANT_COMMIT_FOCUS_LOST);
499 else 537 else
500 Hide(true); 538 Hide(true);
501 #endif 539 #endif
502 } 540 }
503 541
504 void InstantController::OmniboxGotFocus() { 542 void InstantController::OmniboxGotFocus() {
505 DVLOG(1) << "OmniboxGotFocus"; 543 DVLOG(1) << "OmniboxGotFocus";
506 is_omnibox_focused_ = true; 544 is_omnibox_focused_ = true;
507 545
508 if (!extended_enabled_ && !instant_enabled_) 546 if (!extended_enabled_ && !instant_enabled_)
509 return; 547 return;
510 548
511 if (!GetPreviewContents()) 549 if (!client_ && !loader_)
512 CreateDefaultLoader(); 550 CreateDefaultLoader();
513 } 551 }
514 552
515 void InstantController::SearchModeChanged( 553 void InstantController::SearchModeChanged(
516 const chrome::search::Mode& old_mode, 554 const chrome::search::Mode& old_mode,
517 const chrome::search::Mode& new_mode) { 555 const chrome::search::Mode& new_mode) {
518 if (!extended_enabled_) 556 if (!extended_enabled_)
519 return; 557 return;
520 558
521 DVLOG(1) << "SearchModeChanged: [origin:mode] " << old_mode.origin << ":" 559 DVLOG(1) << "SearchModeChanged: [origin:mode] " << old_mode.origin << ":"
522 << old_mode.mode << " to " << new_mode.origin << ":" 560 << old_mode.mode << " to " << new_mode.origin << ":"
523 << new_mode.mode; 561 << new_mode.mode;
524 search_mode_ = new_mode; 562 search_mode_ = new_mode;
525 563
526 if (new_mode.is_search_suggestions()) { 564 if (new_mode.is_search_suggestions()) {
samarth 2012/11/27 18:09:43 Is this code path relevant when client_ is set? In
sreeram 2012/11/29 07:33:19 I think there's a balance to be struck here. I don
527 // The preview is showing NTP content, but it's not appropriate anymore. 565 // The preview is showing NTP content, but it's not appropriate anymore.
528 if (model_.mode().is_ntp() && !new_mode.is_origin_ntp()) 566 if (model_.mode().is_ntp() && !new_mode.is_origin_ntp())
529 Hide(false); 567 Hide(false);
530 } else { 568 } else {
531 Hide(true); 569 Hide(true);
532 } 570 }
533 571
534 if (GetPreviewContents()) 572 if (loader_)
535 loader_->SearchModeChanged(new_mode); 573 loader_->SearchModeChanged(new_mode);
574
575 ResetClient();
536 } 576 }
537 577
538 void InstantController::ActiveTabChanged() { 578 void InstantController::ActiveTabChanged() {
539 if (!extended_enabled_ && !instant_enabled_) 579 if (!extended_enabled_ && !instant_enabled_)
540 return; 580 return;
541 581
542 DVLOG(1) << "ActiveTabChanged"; 582 DVLOG(1) << "ActiveTabChanged";
543 583
544 // By this time, SearchModeChanged() should've been called, so we only need to 584 // By this time, SearchModeChanged() should've been called, so we only need to
545 // handle the case when the search mode does NOT change, as in the case of 585 // handle the case when the search mode does NOT change, as in the case of
546 // going from search_suggestions to search_suggestions (i.e., partial queries 586 // going from search_suggestions to search_suggestions (i.e., partial queries
547 // on both old and new tabs). 587 // on both old and new tabs).
548 if (search_mode_.is_search_suggestions() && 588 if (search_mode_.is_search_suggestions() &&
549 model_.mode().is_search_suggestions()) 589 model_.mode().is_search_suggestions())
550 Hide(false); 590 Hide(false);
591
592 if (extended_enabled_)
593 ResetClient();
551 } 594 }
552 595
553 void InstantController::SetInstantEnabled(bool instant_enabled) { 596 void InstantController::SetInstantEnabled(bool instant_enabled) {
554 instant_enabled_ = instant_enabled; 597 instant_enabled_ = instant_enabled;
555 if (!extended_enabled_ && !instant_enabled_) 598 if (!extended_enabled_ && !instant_enabled_)
556 DeleteLoader(); 599 DeleteLoader();
557 } 600 }
558 601
559 void InstantController::ThemeChanged(const ThemeBackgroundInfo& theme_info) { 602 void InstantController::ThemeChanged(const ThemeBackgroundInfo& theme_info) {
560 if (!extended_enabled_) 603 if (!extended_enabled_)
561 return; 604 return;
562 605
563 if (GetPreviewContents()) 606 if (loader_)
564 loader_->SendThemeBackgroundInfo(theme_info); 607 loader_->SendThemeBackgroundInfo(theme_info);
565 } 608 }
566 609
567 void InstantController::ThemeAreaHeightChanged(int height) { 610 void InstantController::ThemeAreaHeightChanged(int height) {
568 if (!extended_enabled_) 611 if (!extended_enabled_)
569 return; 612 return;
570 613
571 if (GetPreviewContents()) 614 if (loader_)
572 loader_->SendThemeAreaHeight(height); 615 loader_->SendThemeAreaHeight(height);
573 } 616 }
574 617
575 void InstantController::SetSuggestions( 618 void InstantController::SetSuggestions(
576 InstantLoader* loader, 619 InstantClient* client,
577 const std::vector<InstantSuggestion>& suggestions) { 620 const std::vector<InstantSuggestion>& suggestions) {
578 DVLOG(1) << "SetSuggestions"; 621 DVLOG(1) << "SetSuggestions";
579 if (loader_ != loader || !search_mode_.is_search_suggestions()) 622 if (client != CurrentClient() || !search_mode_.is_search_suggestions())
580 return; 623 return;
581 624
582 InstantSuggestion suggestion; 625 InstantSuggestion suggestion;
583 if (!suggestions.empty()) 626 if (!suggestions.empty())
584 suggestion = suggestions[0]; 627 suggestion = suggestions[0];
585 628
586 if (suggestion.behavior == INSTANT_COMPLETE_REPLACE) { 629 if (suggestion.behavior == INSTANT_COMPLETE_REPLACE) {
587 // We don't get an Update() when changing the omnibox due to a REPLACE 630 // We don't get an Update() when changing the omnibox due to a REPLACE
588 // suggestion (so that we don't inadvertently cause the preview to change 631 // suggestion (so that we don't inadvertently cause the preview to change
589 // what it's showing, as the user arrows up/down through the page-provided 632 // what it's showing, as the user arrows up/down through the page-provided
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after
652 } 695 }
653 696
654 void InstantController::ShowInstantPreview(InstantLoader* loader, 697 void InstantController::ShowInstantPreview(InstantLoader* loader,
655 InstantShownReason reason, 698 InstantShownReason reason,
656 int height, 699 int height,
657 InstantSizeUnits units) { 700 InstantSizeUnits units) {
658 if (loader_ == loader && extended_enabled_) 701 if (loader_ == loader && extended_enabled_)
659 Show(reason, height, units); 702 Show(reason, height, units);
660 } 703 }
661 704
662 void InstantController::InstantSupportDetermined(InstantLoader* loader, 705 void InstantController::InstantSupportDetermined(InstantClient* client,
663 bool supports_instant) { 706 bool supports_instant) {
707 if (client_ == client) {
708 if (!supports_instant) {
709 // TODO(sreeram): This should be scheduled for later deletion, but we need
710 // to disconnect the observer before doing so.
711 client_.reset();
712 }
713 return;
714 }
715
716 InstantLoader* loader = static_cast<InstantLoader*>(client);
664 if (supports_instant) { 717 if (supports_instant) {
665 blacklisted_urls_.erase(loader->instant_url()); 718 blacklisted_urls_.erase(loader->instant_url());
666 } else { 719 } else {
667 ++blacklisted_urls_[loader->instant_url()]; 720 ++blacklisted_urls_[loader->instant_url()];
668 if (loader_ == loader) 721 if (loader_ == loader)
669 DeleteLoader(); 722 DeleteLoader();
670 } 723 }
671 724
672 content::NotificationService::current()->Notify( 725 content::NotificationService::current()->Notify(
673 chrome::NOTIFICATION_INSTANT_SUPPORT_DETERMINED, 726 chrome::NOTIFICATION_INSTANT_SUPPORT_DETERMINED,
674 content::Source<InstantController>(this), 727 content::Source<InstantController>(this),
675 content::NotificationService::NoDetails()); 728 content::NotificationService::NoDetails());
676 } 729 }
677 730
678 void InstantController::SwappedTabContents(InstantLoader* loader) { 731 void InstantController::SwappedTabContents(InstantLoader* loader) {
679 if (loader_ == loader) 732 if (loader_ == loader)
680 model_.SetPreviewContents(GetPreviewContents()); 733 model_.SetPreviewContents(GetPreviewContents());
681 } 734 }
682 735
683 void InstantController::InstantLoaderContentsFocused(InstantLoader* loader) { 736 void InstantController::InstantLoaderContentsFocused(InstantLoader* loader) {
684 #if defined(USE_AURA) 737 #if defined(USE_AURA)
685 // On aura the omnibox only receives a focus lost if we initiate the focus 738 // On aura the omnibox only receives a focus lost if we initiate the focus
686 // change. This does that. 739 // change. This does that.
687 if (loader_ == loader && !model_.mode().is_default()) 740 if (loader_ == loader && !model_.mode().is_default())
688 browser_->InstantPreviewFocused(); 741 browser_->InstantPreviewFocused();
689 #endif 742 #endif
690 } 743 }
691 744
745 void InstantController::ResetClient() {
746 if (search_mode_.is_origin_search()) {
747 content::WebContents* contents =
748 browser_->GetActiveTabContents()->web_contents();
749 if (!client_ || contents != client_->contents()) {
750 client_.reset(new InstantClient(this, contents));
751 client_->DetermineIfPageSupportsInstant();
752 }
753 // We are now using |client_| instead of |loader_|, so Hide() the latter. We
754 // want to call Hide(true) to clear old query results on the |loader_|, but
755 // that would also clear |last_full_text_|, which is bad if the user then
756 // immediately tries to commit the query on |client_|.
757 Hide(!search_mode_.is_search_suggestions());
758 } else {
759 client_.reset();
760 }
761 }
762
692 bool InstantController::ResetLoader(const TemplateURL* template_url, 763 bool InstantController::ResetLoader(const TemplateURL* template_url,
693 const TabContents* active_tab) { 764 const TabContents* active_tab) {
694 std::string instant_url; 765 std::string instant_url;
695 if (!GetInstantURL(template_url, &instant_url)) 766 if (!GetInstantURL(template_url, &instant_url))
696 return false; 767 return false;
697 768
698 if (GetPreviewContents() && loader_->instant_url() != instant_url) 769 if (loader_ && loader_->instant_url() != instant_url)
699 DeleteLoader(); 770 DeleteLoader();
700 771
701 if (!GetPreviewContents()) { 772 if (!loader_) {
702 loader_.reset(new InstantLoader(this, instant_url, active_tab)); 773 loader_.reset(new InstantLoader(this, instant_url, active_tab));
703 loader_->Init(); 774 loader_->Init();
704 775
705 // Ensure the searchbox API has the correct theme-related info and context. 776 // Ensure the searchbox API has the correct theme-related info and context.
706 if (extended_enabled_) { 777 if (extended_enabled_) {
707 browser_->UpdateThemeInfoForPreview(); 778 browser_->UpdateThemeInfoForPreview();
708 loader_->SearchModeChanged(search_mode_); 779 loader_->SearchModeChanged(search_mode_);
709 } 780 }
710 781
711 // Reset the loader timer. 782 // Reset the loader timer.
(...skipping 16 matching lines...) Expand all
728 TemplateURLServiceFactory::GetForProfile(active_tab->profile())-> 799 TemplateURLServiceFactory::GetForProfile(active_tab->profile())->
729 GetDefaultSearchProvider(); 800 GetDefaultSearchProvider();
730 801
731 return ResetLoader(template_url, active_tab); 802 return ResetLoader(template_url, active_tab);
732 } 803 }
733 804
734 void InstantController::OnStaleLoader() { 805 void InstantController::OnStaleLoader() {
735 // If the preview is showing or the omnibox has focus, don't delete the 806 // If the preview is showing or the omnibox has focus, don't delete the
736 // loader. It will get refreshed the next time the preview is hidden or the 807 // loader. It will get refreshed the next time the preview is hidden or the
737 // omnibox loses focus. 808 // omnibox loses focus.
738 if (!stale_loader_timer_.IsRunning() && !is_omnibox_focused_ && 809 if (!stale_loader_timer_.IsRunning() && !is_omnibox_focused_ && !client_ &&
739 model_.mode().is_default()) { 810 model_.mode().is_default()) {
740 DeleteLoader(); 811 DeleteLoader();
741 CreateDefaultLoader(); 812 CreateDefaultLoader();
742 } 813 }
743 } 814 }
744 815
745 void InstantController::DeleteLoader() { 816 void InstantController::DeleteLoader() {
746 // Clear all state, except |last_transition_type_| as it's used during commit. 817 // Clear all state, except |last_transition_type_| as it's used during commit.
747 last_user_text_.clear(); 818 last_user_text_.clear();
748 last_full_text_.clear(); 819 last_full_text_.clear();
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
789 last_user_text_.clear(); 860 last_user_text_.clear();
790 last_full_text_.clear(); 861 last_full_text_.clear();
791 } 862 }
792 863
793 OnStaleLoader(); 864 OnStaleLoader();
794 } 865 }
795 866
796 void InstantController::Show(InstantShownReason reason, 867 void InstantController::Show(InstantShownReason reason,
797 int height, 868 int height,
798 InstantSizeUnits units) { 869 InstantSizeUnits units) {
870 if (client_ == CurrentClient())
871 return;
872
799 DVLOG(1) << "Show: reason=" << reason << " height=" << height << " units=" 873 DVLOG(1) << "Show: reason=" << reason << " height=" << height << " units="
800 << units; 874 << units;
801 875
802 // Must be on NTP to show NTP content. 876 // Must be on NTP to show NTP content.
803 if (reason == INSTANT_SHOWN_CUSTOM_NTP_CONTENT && !search_mode_.is_ntp()) 877 if (reason == INSTANT_SHOWN_CUSTOM_NTP_CONTENT && !search_mode_.is_ntp())
804 return; 878 return;
805 879
806 // Must have updated omnibox after most recent Hide() to show suggestions. 880 // Must have updated omnibox after most recent Hide() to show suggestions.
807 if (reason == INSTANT_SHOWN_QUERY_SUGGESTIONS && 881 if (reason == INSTANT_SHOWN_QUERY_SUGGESTIONS &&
808 !search_mode_.is_search_suggestions()) 882 !search_mode_.is_search_suggestions())
809 return; 883 return;
810 884
811 // If the preview is being shown because of the first set of suggestions to 885 // If the preview is being shown because of the first set of suggestions to
812 // arrive for this query editing session, record a histogram value. 886 // arrive for this query editing session, record a histogram value.
813 if (!first_interaction_time_.is_null() && model_.mode().is_default()) { 887 if (!first_interaction_time_.is_null() && model_.mode().is_default()) {
814 base::TimeDelta delta = base::Time::Now() - first_interaction_time_; 888 base::TimeDelta delta = base::Time::Now() - first_interaction_time_;
815 UMA_HISTOGRAM_TIMES("Instant.TimeToFirstShow", delta); 889 UMA_HISTOGRAM_TIMES("Instant.TimeToFirstShow", delta);
816 } 890 }
817 891
818 model_.SetPreviewState(search_mode_, height, units); 892 model_.SetPreviewState(search_mode_, height, units);
819 } 893 }
820 894
821 void InstantController::SendBoundsToPage() { 895 void InstantController::SendBoundsToPage() {
822 if (last_omnibox_bounds_ == omnibox_bounds_ || 896 if (last_omnibox_bounds_ == omnibox_bounds_ || !loader_ ||
823 !GetPreviewContents() || loader_->IsPointerDownFromActivate()) 897 loader_->IsPointerDownFromActivate())
824 return; 898 return;
825 899
826 last_omnibox_bounds_ = omnibox_bounds_; 900 last_omnibox_bounds_ = omnibox_bounds_;
827 gfx::Rect preview_bounds = browser_->GetInstantBounds(); 901 gfx::Rect preview_bounds = browser_->GetInstantBounds();
828 gfx::Rect intersection = gfx::IntersectRects(omnibox_bounds_, preview_bounds); 902 gfx::Rect intersection = gfx::IntersectRects(omnibox_bounds_, preview_bounds);
829 903
830 // Translate into window coordinates. 904 // Translate into window coordinates.
831 if (!intersection.IsEmpty()) { 905 if (!intersection.IsEmpty()) {
832 intersection.Offset(-preview_bounds.origin().x(), 906 intersection.Offset(-preview_bounds.origin().x(),
833 -preview_bounds.origin().y()); 907 -preview_bounds.origin().y());
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
889 } 963 }
890 964
891 std::map<std::string, int>::const_iterator iter = 965 std::map<std::string, int>::const_iterator iter =
892 blacklisted_urls_.find(*instant_url); 966 blacklisted_urls_.find(*instant_url);
893 if (iter != blacklisted_urls_.end() && 967 if (iter != blacklisted_urls_.end() &&
894 iter->second > kMaxInstantSupportFailures) 968 iter->second > kMaxInstantSupportFailures)
895 return false; 969 return false;
896 970
897 return true; 971 return true;
898 } 972 }
973
974 InstantClient* InstantController::CurrentClient() const {
975 return client_ ? client_.get() : loader_.get();
976 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698