OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 } | |
OLD | NEW |