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

Side by Side Diff: chrome/browser/ui/search/instant_controller.cc

Issue 18223002: InstantExtended: Remove overlay control code. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Call renamed method. Created 7 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 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/ui/search/instant_controller.h" 5 #include "chrome/browser/ui/search/instant_controller.h"
6 6
7 #include <iterator>
8
9 #include "base/metrics/histogram.h" 7 #include "base/metrics/histogram.h"
10 #include "base/prefs/pref_service.h" 8 #include "base/prefs/pref_service.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/stringprintf.h" 9 #include "base/strings/stringprintf.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "chrome/browser/autocomplete/autocomplete_provider.h"
15 #include "chrome/browser/autocomplete/autocomplete_result.h"
16 #include "chrome/browser/autocomplete/search_provider.h"
17 #include "chrome/browser/content_settings/content_settings_provider.h" 10 #include "chrome/browser/content_settings/content_settings_provider.h"
18 #include "chrome/browser/content_settings/host_content_settings_map.h" 11 #include "chrome/browser/content_settings/host_content_settings_map.h"
19 #include "chrome/browser/history/history_service.h"
20 #include "chrome/browser/history/history_service_factory.h"
21 #include "chrome/browser/history/history_tab_helper.h"
22 #include "chrome/browser/platform_util.h" 12 #include "chrome/browser/platform_util.h"
13 #include "chrome/browser/profiles/profile.h"
23 #include "chrome/browser/search/instant_service.h" 14 #include "chrome/browser/search/instant_service.h"
24 #include "chrome/browser/search/instant_service_factory.h" 15 #include "chrome/browser/search/instant_service_factory.h"
25 #include "chrome/browser/search/search.h" 16 #include "chrome/browser/search/search.h"
26 #include "chrome/browser/search_engines/search_terms_data.h" 17 #include "chrome/browser/search_engines/search_terms_data.h"
27 #include "chrome/browser/search_engines/template_url_service.h" 18 #include "chrome/browser/search_engines/template_url_service.h"
28 #include "chrome/browser/search_engines/template_url_service_factory.h" 19 #include "chrome/browser/search_engines/template_url_service_factory.h"
29 #include "chrome/browser/ui/browser_instant_controller.h" 20 #include "chrome/browser/ui/browser_instant_controller.h"
30 #include "chrome/browser/ui/search/instant_ntp.h" 21 #include "chrome/browser/ui/search/instant_ntp.h"
31 #include "chrome/browser/ui/search/instant_overlay.h"
32 #include "chrome/browser/ui/search/instant_tab.h" 22 #include "chrome/browser/ui/search/instant_tab.h"
33 #include "chrome/browser/ui/search/search_tab_helper.h" 23 #include "chrome/browser/ui/search/search_tab_helper.h"
34 #include "chrome/common/chrome_notification_types.h" 24 #include "chrome/common/chrome_notification_types.h"
35 #include "chrome/common/chrome_switches.h" 25 #include "chrome/common/chrome_switches.h"
36 #include "chrome/common/content_settings_types.h" 26 #include "chrome/common/content_settings_types.h"
37 #include "chrome/common/pref_names.h" 27 #include "chrome/common/pref_names.h"
38 #include "chrome/common/url_constants.h" 28 #include "chrome/common/url_constants.h"
39 #include "components/sessions/serialized_navigation_entry.h" 29 #include "components/sessions/serialized_navigation_entry.h"
40 #include "content/public/browser/navigation_entry.h" 30 #include "content/public/browser/navigation_entry.h"
41 #include "content/public/browser/notification_service.h" 31 #include "content/public/browser/notification_service.h"
42 #include "content/public/browser/render_process_host.h" 32 #include "content/public/browser/render_process_host.h"
43 #include "content/public/browser/render_widget_host_view.h" 33 #include "content/public/browser/render_widget_host_view.h"
44 #include "content/public/browser/user_metrics.h" 34 #include "content/public/browser/user_metrics.h"
45 #include "content/public/browser/web_contents.h" 35 #include "content/public/browser/web_contents.h"
46 #include "content/public/browser/web_contents_view.h" 36 #include "content/public/browser/web_contents_view.h"
47 #include "net/base/escape.h" 37 #include "net/base/escape.h"
48 #include "net/base/network_change_notifier.h" 38 #include "net/base/network_change_notifier.h"
49 #include "third_party/icu/public/common/unicode/normalizer2.h"
50 39
51 #if defined(TOOLKIT_VIEWS) 40 #if defined(TOOLKIT_VIEWS)
52 #include "ui/views/widget/widget.h" 41 #include "ui/views/widget/widget.h"
53 #endif 42 #endif
54 43
55 namespace { 44 namespace {
56 45
57 // An artificial delay (in milliseconds) we introduce before telling the Instant
58 // page about the new omnibox bounds, in cases where the bounds shrink. This is
59 // to avoid the page jumping up/down very fast in response to bounds changes.
60 const int kUpdateBoundsDelayMS = 1000;
61
62 // For reporting Instant navigations. 46 // For reporting Instant navigations.
63 enum InstantNavigation { 47 enum InstantNavigation {
64 INSTANT_NAVIGATION_LOCAL_CLICK = 0, 48 INSTANT_NAVIGATION_LOCAL_CLICK = 0,
65 INSTANT_NAVIGATION_LOCAL_SUBMIT = 1, 49 INSTANT_NAVIGATION_LOCAL_SUBMIT = 1,
66 INSTANT_NAVIGATION_ONLINE_CLICK = 2, 50 INSTANT_NAVIGATION_ONLINE_CLICK = 2,
67 INSTANT_NAVIGATION_ONLINE_SUBMIT = 3, 51 INSTANT_NAVIGATION_ONLINE_SUBMIT = 3,
68 INSTANT_NAVIGATION_NONEXTENDED = 4, 52 INSTANT_NAVIGATION_NONEXTENDED = 4,
69 INSTANT_NAVIGATION_MAX = 5 53 INSTANT_NAVIGATION_MAX = 5
70 }; 54 };
71 55
72 void RecordNavigationHistogram(bool is_local, bool is_click, bool is_extended) { 56 void RecordNavigationHistogram(bool is_local, bool is_click, bool is_extended) {
73 InstantNavigation navigation; 57 InstantNavigation navigation;
74 if (!is_extended) { 58 if (!is_extended) {
75 navigation = INSTANT_NAVIGATION_NONEXTENDED; 59 navigation = INSTANT_NAVIGATION_NONEXTENDED;
76 } else if (is_local) { 60 } else if (is_local) {
77 navigation = is_click ? INSTANT_NAVIGATION_LOCAL_CLICK : 61 navigation = is_click ? INSTANT_NAVIGATION_LOCAL_CLICK :
78 INSTANT_NAVIGATION_LOCAL_SUBMIT; 62 INSTANT_NAVIGATION_LOCAL_SUBMIT;
79 } else { 63 } else {
80 navigation = is_click ? INSTANT_NAVIGATION_ONLINE_CLICK : 64 navigation = is_click ? INSTANT_NAVIGATION_ONLINE_CLICK :
81 INSTANT_NAVIGATION_ONLINE_SUBMIT; 65 INSTANT_NAVIGATION_ONLINE_SUBMIT;
82 } 66 }
83 UMA_HISTOGRAM_ENUMERATION("InstantExtended.InstantNavigation", 67 UMA_HISTOGRAM_ENUMERATION("InstantExtended.InstantNavigation",
84 navigation, 68 navigation,
85 INSTANT_NAVIGATION_MAX); 69 INSTANT_NAVIGATION_MAX);
86 } 70 }
87 71
88 void RecordFallbackReasonHistogram(
89 const InstantController::InstantFallbackReason fallback_reason) {
90 UMA_HISTOGRAM_ENUMERATION("InstantExtended.FallbackToLocalOverlay",
91 fallback_reason,
92 InstantController::INSTANT_FALLBACK_MAX);
93 }
94
95 InstantController::InstantFallbackReason DetermineFallbackReason(
96 const InstantPage* page, std::string instant_url) {
97 InstantController::InstantFallbackReason fallback_reason;
98 if (!page) {
99 fallback_reason = InstantController::INSTANT_FALLBACK_NO_OVERLAY;
100 } else if (instant_url.empty()) {
101 fallback_reason = InstantController::INSTANT_FALLBACK_INSTANT_URL_EMPTY;
102 } else if (!chrome::MatchesOriginAndPath(GURL(page->instant_url()),
103 GURL(instant_url))) {
104 fallback_reason = InstantController::INSTANT_FALLBACK_ORIGIN_PATH_MISMATCH;
105 } else if (!page->supports_instant()) {
106 fallback_reason = InstantController::INSTANT_FALLBACK_INSTANT_NOT_SUPPORTED;
107 } else {
108 fallback_reason = InstantController::INSTANT_FALLBACK_UNKNOWN;
109 }
110 return fallback_reason;
111 }
112
113 void AddSessionStorageHistogram(bool extended_enabled,
114 const content::WebContents* tab1,
115 const content::WebContents* tab2) {
116 base::HistogramBase* histogram = base::BooleanHistogram::FactoryGet(
117 std::string("Instant.SessionStorageNamespace") +
118 (extended_enabled ? "_Extended" : "_Instant"),
119 base::HistogramBase::kUmaTargetedHistogramFlag);
120 const content::SessionStorageNamespaceMap& session_storage_map1 =
121 tab1->GetController().GetSessionStorageNamespaceMap();
122 const content::SessionStorageNamespaceMap& session_storage_map2 =
123 tab2->GetController().GetSessionStorageNamespaceMap();
124 bool is_session_storage_the_same =
125 session_storage_map1.size() == session_storage_map2.size();
126 if (is_session_storage_the_same) {
127 // The size is the same, so let's check that all entries match.
128 for (content::SessionStorageNamespaceMap::const_iterator
129 it1 = session_storage_map1.begin(),
130 it2 = session_storage_map2.begin();
131 it1 != session_storage_map1.end() && it2 != session_storage_map2.end();
132 ++it1, ++it2) {
133 if (it1->first != it2->first || it1->second.get() != it2->second.get()) {
134 is_session_storage_the_same = false;
135 break;
136 }
137 }
138 }
139 histogram->AddBoolean(is_session_storage_the_same);
140 }
141
142 string16 Normalize(const string16& str) {
143 UErrorCode status = U_ZERO_ERROR;
144 const icu::Normalizer2* normalizer =
145 icu::Normalizer2::getInstance(NULL, "nfkc_cf", UNORM2_COMPOSE, status);
146 if (normalizer == NULL || U_FAILURE(status))
147 return str;
148 icu::UnicodeString norm_str(normalizer->normalize(
149 icu::UnicodeString(FALSE, str.c_str(), str.size()), status));
150 if (U_FAILURE(status))
151 return str;
152 return string16(norm_str.getBuffer(), norm_str.length());
153 }
154
155 bool NormalizeAndStripPrefix(string16* text, const string16& prefix) {
156 string16 norm_prefix = Normalize(prefix);
157 string16 norm_text = Normalize(*text);
158 if (norm_prefix.size() <= norm_text.size() &&
159 norm_text.compare(0, norm_prefix.size(), norm_prefix) == 0) {
160 *text = norm_text.erase(0, norm_prefix.size());
161 return true;
162 }
163 return false;
164 }
165
166 // For TOOLKIT_VIEWS, the top level widget is always focused. If the focus
167 // change originated in views determine the child Widget from the view that is
168 // being focused.
169 gfx::NativeView GetViewGainingFocus(gfx::NativeView view_gaining_focus) {
170 #if defined(TOOLKIT_VIEWS)
171 views::Widget* widget = view_gaining_focus ?
172 views::Widget::GetWidgetForNativeView(view_gaining_focus) : NULL;
173 if (widget) {
174 views::FocusManager* focus_manager = widget->GetFocusManager();
175 if (focus_manager && focus_manager->is_changing_focus() &&
176 focus_manager->GetFocusedView() &&
177 focus_manager->GetFocusedView()->GetWidget())
178 return focus_manager->GetFocusedView()->GetWidget()->GetNativeView();
179 }
180 #endif
181 return view_gaining_focus;
182 }
183
184 // Returns true if |view| is the top-level contents view or a child view in the
185 // view hierarchy of |contents|.
186 bool IsViewInContents(gfx::NativeView view, content::WebContents* contents) {
187 content::RenderWidgetHostView* rwhv = contents->GetRenderWidgetHostView();
188 if (!view || !rwhv)
189 return false;
190
191 gfx::NativeView tab_view = contents->GetView()->GetNativeView();
192 if (view == rwhv->GetNativeView() || view == tab_view)
193 return true;
194
195 // Walk up the view hierarchy to determine if the view is a subview of the
196 // WebContents view (such as a windowed plugin or http auth dialog).
197 while (view) {
198 view = platform_util::GetParent(view);
199 if (view == tab_view)
200 return true;
201 }
202
203 return false;
204 }
205
206 bool IsFullHeight(const InstantOverlayModel& model) {
207 return model.height() == 100 && model.height_units() == INSTANT_SIZE_PERCENT;
208 }
209
210 bool IsContentsFrom(const InstantPage* page, 72 bool IsContentsFrom(const InstantPage* page,
211 const content::WebContents* contents) { 73 const content::WebContents* contents) {
212 return page && (page->contents() == contents); 74 return page && (page->contents() == contents);
213 } 75 }
214 76
215 // Adds a transient NavigationEntry to the supplied |contents|'s 77 // Adds a transient NavigationEntry to the supplied |contents|'s
216 // NavigationController if the page's URL has not already been updated with the 78 // NavigationController if the page's URL has not already been updated with the
217 // supplied |search_terms|. Sets the |search_terms| on the transient entry for 79 // supplied |search_terms|. Sets the |search_terms| on the transient entry for
218 // search terms extraction to work correctly. 80 // search terms extraction to work correctly.
219 void EnsureSearchTermsAreSet(content::WebContents* contents, 81 void EnsureSearchTermsAreSet(content::WebContents* contents,
(...skipping 29 matching lines...) Expand all
249 111
250 base::MessageLoop::current()->DeleteSoon(FROM_HERE, page.release()); 112 base::MessageLoop::current()->DeleteSoon(FROM_HERE, page.release());
251 } 113 }
252 114
253 } // namespace 115 } // namespace
254 116
255 InstantController::InstantController(BrowserInstantController* browser, 117 InstantController::InstantController(BrowserInstantController* browser,
256 bool extended_enabled) 118 bool extended_enabled)
257 : browser_(browser), 119 : browser_(browser),
258 extended_enabled_(extended_enabled), 120 extended_enabled_(extended_enabled),
259 instant_enabled_(false),
260 use_local_page_only_(true),
261 preload_ntp_(true),
262 model_(this),
263 use_tab_for_suggestions_(false),
264 last_omnibox_text_has_inline_autocompletion_(false),
265 last_verbatim_(false),
266 last_transition_type_(content::PAGE_TRANSITION_LINK),
267 last_match_was_search_(false),
268 omnibox_focus_state_(OMNIBOX_FOCUS_NONE), 121 omnibox_focus_state_(OMNIBOX_FOCUS_NONE),
269 omnibox_focus_change_reason_(OMNIBOX_FOCUS_CHANGE_EXPLICIT), 122 omnibox_focus_change_reason_(OMNIBOX_FOCUS_CHANGE_EXPLICIT),
270 omnibox_bounds_(-1, -1, 0, 0), 123 omnibox_bounds_(-1, -1, 0, 0) {
271 allow_overlay_to_show_search_suggestions_(false) {
272 124
273 // When the InstantController lives, the InstantService should live. 125 // When the InstantController lives, the InstantService should live.
274 // InstantService sets up profile-level facilities such as the ThemeSource for 126 // InstantService sets up profile-level facilities such as the ThemeSource for
275 // the NTP. 127 // the NTP.
276 // However, in some tests, browser_ may be null. 128 // However, in some tests, browser_ may be null.
277 if (browser_) { 129 if (browser_) {
278 InstantService* instant_service = GetInstantService(); 130 InstantService* instant_service = GetInstantService();
279 instant_service->AddObserver(this); 131 instant_service->AddObserver(this);
280 } 132 }
281 } 133 }
282 134
283 InstantController::~InstantController() { 135 InstantController::~InstantController() {
284 if (browser_) { 136 if (browser_) {
285 InstantService* instant_service = GetInstantService(); 137 InstantService* instant_service = GetInstantService();
286 instant_service->RemoveObserver(this); 138 instant_service->RemoveObserver(this);
287 } 139 }
288 } 140 }
289 141
290 void InstantController::OnAutocompleteStart() {
291 if (UseTabForSuggestions() && instant_tab_->supports_instant()) {
292 LOG_INSTANT_DEBUG_EVENT(
293 this, "OnAutocompleteStart: using InstantTab");
294 return;
295 }
296
297 // Not using |instant_tab_|. Check if overlay is OK to use.
298 InstantFallbackReason fallback_reason = ShouldSwitchToLocalOverlay();
299 if (fallback_reason != INSTANT_FALLBACK_NONE) {
300 ResetOverlay(GetLocalInstantURL());
301 RecordFallbackReasonHistogram(fallback_reason);
302 LOG_INSTANT_DEBUG_EVENT(
303 this, "OnAutocompleteStart: switching to local overlay");
304 } else {
305 LOG_INSTANT_DEBUG_EVENT(
306 this, "OnAutocompleteStart: using existing overlay");
307 }
308 use_tab_for_suggestions_ = false;
309 }
310
311 bool InstantController::Update(const AutocompleteMatch& match,
312 const string16& user_text,
313 const string16& full_text,
314 size_t selection_start,
315 size_t selection_end,
316 bool verbatim,
317 bool user_input_in_progress,
318 bool omnibox_popup_is_open,
319 bool escape_pressed,
320 bool is_keyword_search) {
321 if (!extended_enabled() && !instant_enabled_)
322 return false;
323
324 LOG_INSTANT_DEBUG_EVENT(this, base::StringPrintf(
325 "Update: %s user_text='%s' full_text='%s' selection_start=%d "
326 "selection_end=%d verbatim=%d typing=%d popup=%d escape_pressed=%d "
327 "is_keyword_search=%d",
328 AutocompleteMatchType::ToString(match.type).c_str(),
329 UTF16ToUTF8(user_text).c_str(), UTF16ToUTF8(full_text).c_str(),
330 static_cast<int>(selection_start), static_cast<int>(selection_end),
331 verbatim, user_input_in_progress, omnibox_popup_is_open, escape_pressed,
332 is_keyword_search));
333
334 // Store the current |last_omnibox_text_| and update |last_omnibox_text_|
335 // upfront with the contents of |full_text|. Even if we do an early return,
336 // |last_omnibox_text_| will be updated.
337 string16 previous_omnibox_text = last_omnibox_text_;
338 last_omnibox_text_ = full_text;
339 last_match_was_search_ = AutocompleteMatch::IsSearchType(match.type) &&
340 !user_text.empty();
341
342 // TODO(dhollowa): Complete keyword match UI. For now just hide suggestions.
343 // http://crbug.com/153932. Note, this early escape is happens prior to the
344 // DCHECKs below because |user_text| and |full_text| have different semantics
345 // when keyword search is in effect.
346 if (is_keyword_search) {
347 if (UseTabForSuggestions())
348 instant_tab_->sender()->Update(string16(), 0, 0, true);
349 else
350 HideOverlay();
351 last_match_was_search_ = false;
352 last_suggestion_ = InstantSuggestion();
353 return false;
354 }
355
356 // Ignore spurious updates when the omnibox is blurred; otherwise click
357 // targets on the page may vanish before a click event arrives.
358 if (omnibox_focus_state_ == OMNIBOX_FOCUS_NONE)
359 return false;
360
361 // If the popup is open, the user has to be typing.
362 DCHECK(!omnibox_popup_is_open || user_input_in_progress);
363
364 // If the popup is closed, there should be no inline autocompletion.
365 DCHECK(omnibox_popup_is_open || user_text.empty() || user_text == full_text)
366 << user_text << "|" << full_text;
367
368 // If there's no text in the omnibox, the user can't have typed any.
369 DCHECK(!full_text.empty() || user_text.empty()) << user_text;
370
371 // If the user isn't typing, and the popup is closed, there can't be any
372 // user-typed text.
373 DCHECK(user_input_in_progress || omnibox_popup_is_open || user_text.empty())
374 << user_text;
375
376 // The overlay is being clicked and will commit soon. Don't change anything.
377 // TODO(sreeram): Add a browser test for this.
378 if (overlay_ && overlay_->is_pointer_down_from_activate())
379 return false;
380
381 // In non-extended mode, SearchModeChanged() is never called, so fake it. The
382 // mode is set to "disallow suggestions" here, so that if one of the early
383 // "return false" conditions is hit, suggestions will be disallowed. If the
384 // query is sent to the overlay, the mode is set to "allow" further below.
385 if (!extended_enabled())
386 search_mode_.mode = SearchMode::MODE_DEFAULT;
387
388 // In non extended mode, Instant is disabled for URLs and keyword mode.
389 if (!extended_enabled() &&
390 (!last_match_was_search_ ||
391 match.type == AutocompleteMatchType::SEARCH_OTHER_ENGINE)) {
392 HideOverlay();
393 return false;
394 }
395
396 if (!UseTabForSuggestions() && !overlay_) {
397 HideOverlay();
398 return false;
399 }
400
401 if (extended_enabled()) {
402 if (!omnibox_popup_is_open) {
403 if (!user_input_in_progress) {
404 // If the user isn't typing and the omnibox popup is closed, it means a
405 // regular navigation, tab-switch or the user hitting Escape.
406 if (UseTabForSuggestions()) {
407 // The user is on a search results page. It may be showing results for
408 // a partial query the user typed before they hit Escape. Send the
409 // omnibox text to the page to restore the original results.
410 //
411 // In a tab switch, |instant_tab_| won't have updated yet, so it may
412 // be pointing to the previous tab (which was a search results page).
413 // Ensure we don't send the omnibox text to a random webpage (the new
414 // tab), by comparing the old and new WebContents.
415 if (escape_pressed &&
416 instant_tab_->contents() == browser_->GetActiveWebContents()) {
417 // TODO(kmadhusu): If the |full_text| is not empty, send an
418 // onkeypress(esc) to the Instant page. Do not call
419 // onsubmit(full_text). Fix.
420 if (full_text.empty()) {
421 // Call onchange("") to clear the query for the page.
422 instant_tab_->sender()->Update(string16(), 0, 0, true);
423 instant_tab_->sender()->EscKeyPressed();
424 } else {
425 instant_tab_->sender()->Submit(full_text);
426 }
427 }
428 } else if (!full_text.empty()) {
429 // If |full_text| is empty, the user is on the NTP. The overlay may
430 // be showing custom NTP content; hide only if that's not the case.
431 HideOverlay();
432 }
433 } else if (full_text.empty()) {
434 // The user is typing, and backspaced away all omnibox text. Clear
435 // |last_omnibox_text_| so that we don't attempt to set suggestions.
436 last_omnibox_text_.clear();
437 last_user_text_.clear();
438 last_suggestion_ = InstantSuggestion();
439 if (UseTabForSuggestions()) {
440 // On a search results page, tell it to clear old results.
441 instant_tab_->sender()->Update(string16(), 0, 0, true);
442 } else if (overlay_ && search_mode_.is_origin_ntp()) {
443 // On the NTP, tell the overlay to clear old results. Don't hide the
444 // overlay so it can show a blank page or logo if it wants.
445 overlay_->Update(string16(), 0, 0, true);
446 } else {
447 HideOverlay();
448 }
449 } else {
450 // The user switched to a tab with partial text already in the omnibox.
451 HideOverlay();
452
453 // The new tab may or may not be a search results page; we don't know
454 // since SearchModeChanged() hasn't been called yet. If it later turns
455 // out to be, we should store |full_text| now, so that if the user hits
456 // Enter, we'll send the correct query to
457 // instant_tab_->sender()->Submit(). If the partial text is not a query
458 // (|last_match_was_search_| is false), we won't Submit(), so no need to
459 // worry about that.
460 last_user_text_ = user_text;
461 last_suggestion_ = InstantSuggestion();
462 }
463 return false;
464 } else if (full_text.empty()) {
465 // The user typed a solitary "?". Same as the backspace case above.
466 last_omnibox_text_.clear();
467 last_user_text_.clear();
468 last_suggestion_ = InstantSuggestion();
469 if (UseTabForSuggestions())
470 instant_tab_->sender()->Update(string16(), 0, 0, true);
471 else if (overlay_ && search_mode_.is_origin_ntp())
472 overlay_->Update(string16(), 0, 0, true);
473 else
474 HideOverlay();
475 return false;
476 }
477 } else if (!omnibox_popup_is_open || full_text.empty()) {
478 // In the non-extended case, hide the overlay as long as the user isn't
479 // actively typing a non-empty query.
480 HideOverlay();
481 return false;
482 }
483
484 last_omnibox_text_has_inline_autocompletion_ = user_text != full_text;
485
486 // If the user continues typing the same query as the suggested text is
487 // showing, reuse the suggestion (but only for INSTANT_COMPLETE_NEVER).
488 bool reused_suggestion = false;
489 if (last_suggestion_.behavior == INSTANT_COMPLETE_NEVER &&
490 !last_omnibox_text_has_inline_autocompletion_) {
491 if (StartsWith(previous_omnibox_text, full_text, false)) {
492 // The user is backspacing away characters.
493 last_suggestion_.text.insert(0, previous_omnibox_text, full_text.size(),
494 previous_omnibox_text.size() - full_text.size());
495 reused_suggestion = true;
496 } else if (StartsWith(full_text, previous_omnibox_text, false)) {
497 // The user is typing forward. Normalize any added characters.
498 reused_suggestion = NormalizeAndStripPrefix(&last_suggestion_.text,
499 string16(full_text, previous_omnibox_text.size()));
500 }
501 }
502 if (!reused_suggestion)
503 last_suggestion_ = InstantSuggestion();
504
505 // TODO(kmadhusu): Investigate whether it's possible to update
506 // |last_user_text_| at the beginning of this function.
507 last_user_text_ = user_text;
508
509 if (!extended_enabled()) {
510 // In non-extended mode, the query is verbatim if there's any selection
511 // (including inline autocompletion) or if the cursor is not at the end.
512 verbatim = verbatim || selection_start != selection_end ||
513 selection_start != full_text.size();
514 }
515 last_verbatim_ = verbatim;
516
517 last_transition_type_ = match.transition;
518 url_for_history_ = match.destination_url;
519
520 // Allow search suggestions. In extended mode, SearchModeChanged() will set
521 // this, but it's not called in non-extended mode, so fake it.
522 if (!extended_enabled())
523 search_mode_.mode = SearchMode::MODE_SEARCH_SUGGESTIONS;
524
525 if (UseTabForSuggestions()) {
526 instant_tab_->sender()->Update(user_text, selection_start,
527 selection_end, verbatim);
528 } else if (overlay_) {
529 allow_overlay_to_show_search_suggestions_ = true;
530
531 overlay_->Update(extended_enabled() ? user_text : full_text,
532 selection_start, selection_end, verbatim);
533 }
534
535 content::NotificationService::current()->Notify(
536 chrome::NOTIFICATION_INSTANT_CONTROLLER_UPDATED,
537 content::Source<InstantController>(this),
538 content::NotificationService::NoDetails());
539
540 // We don't have new suggestions yet, but we can either reuse the existing
541 // suggestion or reset the existing "gray text".
542 browser_->SetInstantSuggestion(last_suggestion_);
543
544 // Record the time of the first keypress for logging histograms.
545 if (!first_interaction_time_recorded_ && first_interaction_time_.is_null())
546 first_interaction_time_ = base::Time::Now();
547
548 return true;
549 }
550
551 scoped_ptr<content::WebContents> InstantController::ReleaseNTPContents() { 142 scoped_ptr<content::WebContents> InstantController::ReleaseNTPContents() {
552 if (!extended_enabled() || !browser_->profile() || 143 if (!extended_enabled() || !browser_->profile() ||
553 browser_->profile()->IsOffTheRecord() || 144 browser_->profile()->IsOffTheRecord() ||
554 !chrome::ShouldShowInstantNTP()) 145 !chrome::ShouldShowInstantNTP())
555 return scoped_ptr<content::WebContents>(); 146 return scoped_ptr<content::WebContents>();
556 147
557 LOG_INSTANT_DEBUG_EVENT(this, "ReleaseNTPContents"); 148 LOG_INSTANT_DEBUG_EVENT(this, "ReleaseNTPContents");
558 149
559 if (ShouldSwitchToLocalNTP()) 150 if (ShouldSwitchToLocalNTP())
560 ResetNTP(GetLocalInstantURL()); 151 ResetNTP(GetLocalInstantURL());
561 152
562 scoped_ptr<content::WebContents> ntp_contents = ntp_->ReleaseContents(); 153 scoped_ptr<content::WebContents> ntp_contents = ntp_->ReleaseContents();
563 154
564 // Preload a new Instant NTP. 155 // Preload a new Instant NTP.
565 if (preload_ntp_) 156 ResetNTP(GetInstantURL());
566 ResetNTP(GetInstantURL());
567 else
568 ntp_.reset();
569 157
570 return ntp_contents.Pass(); 158 return ntp_contents.Pass();
571 } 159 }
572 160
573 // TODO(tonyg): This method only fires when the omnibox bounds change. It also
574 // needs to fire when the overlay bounds change (e.g.: open/close info bar).
575 void InstantController::SetPopupBounds(const gfx::Rect& bounds) {
576 if (!extended_enabled() && !instant_enabled_)
577 return;
578
579 if (popup_bounds_ == bounds)
580 return;
581
582 popup_bounds_ = bounds;
583 if (popup_bounds_.height() > last_popup_bounds_.height()) {
584 update_bounds_timer_.Stop();
585 SendPopupBoundsToPage();
586 } else if (!update_bounds_timer_.IsRunning()) {
587 update_bounds_timer_.Start(FROM_HERE,
588 base::TimeDelta::FromMilliseconds(kUpdateBoundsDelayMS), this,
589 &InstantController::SendPopupBoundsToPage);
590 }
591 }
592
593 void InstantController::SetOmniboxBounds(const gfx::Rect& bounds) { 161 void InstantController::SetOmniboxBounds(const gfx::Rect& bounds) {
594 if (!extended_enabled() || omnibox_bounds_ == bounds) 162 if (!extended_enabled() || omnibox_bounds_ == bounds)
595 return; 163 return;
596 164
597 omnibox_bounds_ = bounds; 165 omnibox_bounds_ = bounds;
598 if (overlay_)
599 overlay_->sender()->SetOmniboxBounds(omnibox_bounds_);
600 if (ntp_) 166 if (ntp_)
601 ntp_->sender()->SetOmniboxBounds(omnibox_bounds_); 167 ntp_->sender()->SetOmniboxBounds(omnibox_bounds_);
602 if (instant_tab_) 168 if (instant_tab_)
603 instant_tab_->sender()->SetOmniboxBounds(omnibox_bounds_); 169 instant_tab_->sender()->SetOmniboxBounds(omnibox_bounds_);
604 } 170 }
605 171
606 void InstantController::HandleAutocompleteResults(
607 const std::vector<AutocompleteProvider*>& providers,
608 const AutocompleteResult& autocomplete_result) {
609 if (!extended_enabled())
610 return;
611
612 if (!UseTabForSuggestions() && !overlay_)
613 return;
614
615 // The omnibox sends suggestions when its possibly imaginary popup closes
616 // as it stops autocomplete. Ignore these.
617 if (omnibox_focus_state_ == OMNIBOX_FOCUS_NONE)
618 return;
619
620 DVLOG(1) << "AutocompleteResults:";
621 std::vector<InstantAutocompleteResult> results;
622 if (UsingLocalPage()) {
623 for (AutocompleteResult::const_iterator match(autocomplete_result.begin());
624 match != autocomplete_result.end(); ++match) {
625 InstantAutocompleteResult result;
626 PopulateInstantAutocompleteResultFromMatch(
627 *match, std::distance(autocomplete_result.begin(), match), &result);
628 results.push_back(result);
629 }
630 } else {
631 for (ACProviders::const_iterator provider = providers.begin();
632 provider != providers.end(); ++provider) {
633 for (ACMatches::const_iterator match = (*provider)->matches().begin();
634 match != (*provider)->matches().end(); ++match) {
635 // When the top match is an inline history URL, the page calls
636 // SetSuggestions(url) which calls FinalizeInstantQuery() in
637 // SearchProvider creating a NAVSUGGEST match for the URL. If we sent
638 // this NAVSUGGEST match back to the page, it would be deduped against
639 // the original history match and replace it. But since the page ignores
640 // SearchProvider suggestions, the match would then disappear. Yuck.
641 // TODO(jered): Remove this when FinalizeInstantQuery() is ripped out.
642 if ((*provider)->type() == AutocompleteProvider::TYPE_SEARCH &&
643 match->type == AutocompleteMatchType::NAVSUGGEST) {
644 continue;
645 }
646 InstantAutocompleteResult result;
647 PopulateInstantAutocompleteResultFromMatch(*match, kNoMatchIndex,
648 &result);
649 results.push_back(result);
650 }
651 }
652 }
653 LOG_INSTANT_DEBUG_EVENT(this, base::StringPrintf(
654 "HandleAutocompleteResults: total_results=%d",
655 static_cast<int>(results.size())));
656
657 if (UseTabForSuggestions())
658 instant_tab_->sender()->SendAutocompleteResults(results);
659 else if (overlay_)
660 overlay_->sender()->SendAutocompleteResults(results);
661
662 content::NotificationService::current()->Notify(
663 chrome::NOTIFICATION_INSTANT_SENT_AUTOCOMPLETE_RESULTS,
664 content::Source<InstantController>(this),
665 content::NotificationService::NoDetails());
666 }
667
668 void InstantController::OnDefaultSearchProviderChanged() { 172 void InstantController::OnDefaultSearchProviderChanged() {
669 if (ntp_ && extended_enabled()) { 173 if (ntp_ && extended_enabled()) {
670 ntp_.reset(); 174 ntp_.reset();
671 if (preload_ntp_) 175 ResetNTP(GetInstantURL());
672 ResetNTP(GetInstantURL());
673 } 176 }
674
675 // Do not reload the overlay if it's actually the local overlay.
676 if (overlay_ && !overlay_->IsLocal()) {
677 overlay_.reset();
678 if (extended_enabled() || instant_enabled_) {
679 // Try to create another overlay immediately so that it is ready for the
680 // next user interaction.
681 ResetOverlay(GetInstantURL());
682 }
683 }
684 }
685
686 bool InstantController::OnUpOrDownKeyPressed(int count) {
687 if (!extended_enabled())
688 return false;
689
690 if (!UseTabForSuggestions() && !overlay_)
691 return false;
692
693 if (UseTabForSuggestions())
694 instant_tab_->sender()->UpOrDownKeyPressed(count);
695 else if (overlay_)
696 overlay_->sender()->UpOrDownKeyPressed(count);
697
698 return true;
699 }
700
701 void InstantController::OnCancel(const AutocompleteMatch& match,
702 const string16& user_text,
703 const string16& full_text) {
704 if (!extended_enabled())
705 return;
706
707 if (!UseTabForSuggestions() && !overlay_)
708 return;
709
710 // We manually reset the state here since the JS is not expected to do it.
711 // TODO(sreeram): Handle the case where user_text is now a URL
712 last_match_was_search_ = AutocompleteMatch::IsSearchType(match.type) &&
713 !full_text.empty();
714 last_omnibox_text_ = full_text;
715 last_user_text_ = user_text;
716 last_suggestion_ = InstantSuggestion();
717
718 // Say |full_text| is "amazon.com" and |user_text| is "ama". This means the
719 // inline autocompletion is "zon.com"; so the selection should span from
720 // user_text.size() to full_text.size(). The selection bounds are inverted
721 // because the caret is at the end of |user_text|, not |full_text|.
722 if (UseTabForSuggestions()) {
723 instant_tab_->sender()->CancelSelection(user_text, full_text.size(),
724 user_text.size(), last_verbatim_);
725 } else if (overlay_) {
726 overlay_->sender()->CancelSelection(user_text, full_text.size(),
727 user_text.size(), last_verbatim_);
728 }
729 }
730
731 void InstantController::OmniboxNavigateToURL() {
732 RecordNavigationHistogram(UsingLocalPage(), false, extended_enabled());
733 if (!extended_enabled())
734 return;
735 if (UseTabForSuggestions())
736 instant_tab_->sender()->Submit(string16());
737 } 177 }
738 178
739 void InstantController::ToggleVoiceSearch() { 179 void InstantController::ToggleVoiceSearch() {
740 if (instant_tab_) 180 if (instant_tab_)
741 instant_tab_->sender()->ToggleVoiceSearch(); 181 instant_tab_->sender()->ToggleVoiceSearch();
742 } 182 }
743 183
744 void InstantController::InstantPageLoadFailed(content::WebContents* contents) { 184 void InstantController::InstantPageLoadFailed(content::WebContents* contents) {
745 if (!chrome::ShouldPreferRemoteNTPOnStartup() || !extended_enabled()) { 185 if (!chrome::ShouldPreferRemoteNTPOnStartup() || !extended_enabled()) {
746 // We only need to fall back on errors if we're showing the online page 186 // We only need to fall back on errors if we're showing the online page
(...skipping 19 matching lines...) Expand all
766 contents->GetController().CanGoForward()) 206 contents->GetController().CanGoForward())
767 return; 207 return;
768 LOG_INSTANT_DEBUG_EVENT(this, "InstantPageLoadFailed: instant_tab"); 208 LOG_INSTANT_DEBUG_EVENT(this, "InstantPageLoadFailed: instant_tab");
769 RedirectToLocalNTP(contents); 209 RedirectToLocalNTP(contents);
770 } else if (IsContentsFrom(ntp(), contents)) { 210 } else if (IsContentsFrom(ntp(), contents)) {
771 LOG_INSTANT_DEBUG_EVENT(this, "InstantPageLoadFailed: ntp"); 211 LOG_INSTANT_DEBUG_EVENT(this, "InstantPageLoadFailed: ntp");
772 bool is_local = ntp_->IsLocal(); 212 bool is_local = ntp_->IsLocal();
773 DeletePageSoon(ntp_.Pass()); 213 DeletePageSoon(ntp_.Pass());
774 if (!is_local) 214 if (!is_local)
775 ResetNTP(GetLocalInstantURL()); 215 ResetNTP(GetLocalInstantURL());
776 } else if (IsContentsFrom(overlay(), contents)) { 216 } else {
777 LOG_INSTANT_DEBUG_EVENT(this, "InstantPageLoadFailed: overlay"); 217 NOTREACHED();
778 bool is_local = overlay_->IsLocal();
779 DeletePageSoon(overlay_.Pass());
780 if (!is_local)
781 ResetOverlay(GetLocalInstantURL());
782 } 218 }
783 } 219 }
784 220
785 content::WebContents* InstantController::GetOverlayContents() const {
786 return overlay_ ? overlay_->contents() : NULL;
787 }
788
789 content::WebContents* InstantController::GetNTPContents() const { 221 content::WebContents* InstantController::GetNTPContents() const {
790 return ntp_ ? ntp_->contents() : NULL; 222 return ntp_ ? ntp_->contents() : NULL;
791 } 223 }
792 224
793 bool InstantController::IsOverlayingSearchResults() const {
794 return model_.mode().is_search_suggestions() && IsFullHeight(model_) &&
795 (last_match_was_search_ ||
796 last_suggestion_.behavior == INSTANT_COMPLETE_NEVER);
797 }
798
799 bool InstantController::SubmitQuery(const string16& search_terms) { 225 bool InstantController::SubmitQuery(const string16& search_terms) {
800 if (extended_enabled() && instant_tab_ && instant_tab_->supports_instant() && 226 if (extended_enabled() && instant_tab_ && instant_tab_->supports_instant() &&
801 search_mode_.is_origin_search()) { 227 search_mode_.is_origin_search()) {
802 // Use |instant_tab_| to run the query if we're already on a search results 228 // Use |instant_tab_| to run the query if we're already on a search results
803 // page. (NOTE: in particular, we do not send the query to NTPs.) 229 // page. (NOTE: in particular, we do not send the query to NTPs.)
804 instant_tab_->sender()->Submit(search_terms); 230 instant_tab_->sender()->Submit(search_terms);
805 instant_tab_->contents()->GetView()->Focus(); 231 instant_tab_->contents()->GetView()->Focus();
806 EnsureSearchTermsAreSet(instant_tab_->contents(), search_terms); 232 EnsureSearchTermsAreSet(instant_tab_->contents(), search_terms);
807 return true; 233 return true;
808 } 234 }
809 return false; 235 return false;
810 } 236 }
811 237
812 bool InstantController::CommitIfPossible(InstantCommitType type) {
813 if (!extended_enabled() && !instant_enabled_)
814 return false;
815
816 LOG_INSTANT_DEBUG_EVENT(this, base::StringPrintf(
817 "CommitIfPossible: type=%d last_omnibox_text_='%s' "
818 "last_match_was_search_=%d use_tab_for_suggestions=%d", type,
819 UTF16ToUTF8(last_omnibox_text_).c_str(), last_match_was_search_,
820 UseTabForSuggestions()));
821
822 // If we are on an already committed search results page, send a submit event
823 // to the page, but otherwise, nothing else to do.
824 if (UseTabForSuggestions()) {
825 if (type == INSTANT_COMMIT_PRESSED_ENTER &&
826 !instant_tab_->IsLocal() &&
827 (last_match_was_search_ ||
828 last_suggestion_.behavior == INSTANT_COMPLETE_NEVER)) {
829 last_suggestion_.text.clear();
830 instant_tab_->sender()->Submit(last_omnibox_text_);
831 instant_tab_->contents()->GetView()->Focus();
832 EnsureSearchTermsAreSet(instant_tab_->contents(), last_omnibox_text_);
833 return true;
834 }
835 return false;
836 }
837
838 if (!overlay_)
839 return false;
840
841 // If the overlay is not showing at all, don't commit it.
842 if (!model_.mode().is_search_suggestions())
843 return false;
844
845 // If the overlay is showing at full height (with results), commit it.
846 // If it's showing at parial height, commit if it's navigating.
847 if (!IsOverlayingSearchResults() && type != INSTANT_COMMIT_NAVIGATED)
848 return false;
849
850 // There may re-entrance here, from the call to browser_->CommitInstant below,
851 // which can cause a TabDeactivated notification which gets back here.
852 // In this case, overlay_->ReleaseContents() was called already.
853 if (!GetOverlayContents())
854 return false;
855
856 // Never commit the local overlay.
857 if (overlay_->IsLocal())
858 return false;
859
860 if (type == INSTANT_COMMIT_FOCUS_LOST) {
861 // Extended mode doesn't need or use the Cancel message.
862 if (!extended_enabled())
863 overlay_->sender()->Cancel(last_omnibox_text_);
864 } else if (type != INSTANT_COMMIT_NAVIGATED) {
865 overlay_->sender()->Submit(last_omnibox_text_);
866 }
867
868 // We expect the WebContents to be in a valid state (i.e., has a last
869 // committed entry, no transient entry, and no existing pending entry).
870 scoped_ptr<content::WebContents> overlay = overlay_->ReleaseContents();
871 CHECK(overlay->GetController().CanPruneAllButVisible());
872
873 // If the overlay page has navigated since the last Update(), we need to add
874 // the navigation to history ourselves. Else, the page will navigate after
875 // commit, and it will be added to history in the usual manner.
876 const history::HistoryAddPageArgs& last_navigation =
877 overlay_->last_navigation();
878 if (!last_navigation.url.is_empty()) {
879 content::NavigationEntry* entry = overlay->GetController().GetActiveEntry();
880
881 // The last navigation should be the same as the active entry if the overlay
882 // is in search mode. During navigation, the active entry could have
883 // changed since DidCommitProvisionalLoadForFrame is called after the entry
884 // is changed.
885 // TODO(shishir): Should we commit the last navigation for
886 // INSTANT_COMMIT_NAVIGATED.
887 DCHECK(type == INSTANT_COMMIT_NAVIGATED ||
888 last_navigation.url == entry->GetURL());
889
890 // Add the page to history.
891 HistoryTabHelper* history_tab_helper =
892 HistoryTabHelper::FromWebContents(overlay.get());
893 history_tab_helper->UpdateHistoryForNavigation(last_navigation);
894
895 // Update the page title.
896 history_tab_helper->UpdateHistoryPageTitle(*entry);
897 }
898
899 // Add a fake history entry with a non-Instant search URL, so that search
900 // terms extraction (for autocomplete history matches) works.
901 HistoryService* history = HistoryServiceFactory::GetForProfile(
902 Profile::FromBrowserContext(overlay->GetBrowserContext()),
903 Profile::EXPLICIT_ACCESS);
904 if (history) {
905 history->AddPage(url_for_history_, base::Time::Now(), NULL, 0, GURL(),
906 history::RedirectList(), last_transition_type_,
907 history::SOURCE_BROWSED, false);
908 }
909
910 if (type == INSTANT_COMMIT_PRESSED_ALT_ENTER) {
911 overlay->GetController().PruneAllButVisible();
912 } else {
913 content::WebContents* active_tab = browser_->GetActiveWebContents();
914 AddSessionStorageHistogram(extended_enabled(), active_tab, overlay.get());
915 overlay->GetController().CopyStateFromAndPrune(
916 &active_tab->GetController());
917 }
918
919 if (extended_enabled()) {
920 // Adjust the search terms shown in the omnibox for this query. Hitting
921 // ENTER searches for what the user typed, so use last_omnibox_text_.
922 // Clicking on the overlay commits what is currently showing, so add in the
923 // gray text in that case.
924 if (type == INSTANT_COMMIT_FOCUS_LOST &&
925 last_suggestion_.behavior == INSTANT_COMPLETE_NEVER) {
926 // Update |last_omnibox_text_| so that the controller commits the proper
927 // query if the user focuses the omnibox and presses Enter.
928 last_omnibox_text_ += last_suggestion_.text;
929 }
930
931 EnsureSearchTermsAreSet(overlay.get(), last_omnibox_text_);
932 }
933
934 // Save notification source before we release the overlay.
935 content::Source<content::WebContents> notification_source(overlay.get());
936
937 browser_->CommitInstant(overlay.Pass(),
938 type == INSTANT_COMMIT_PRESSED_ALT_ENTER);
939
940 content::NotificationService::current()->Notify(
941 chrome::NOTIFICATION_INSTANT_COMMITTED,
942 notification_source,
943 content::NotificationService::NoDetails());
944
945 // Hide explicitly. See comments in HideOverlay() for why.
946 model_.SetOverlayState(SearchMode(), 0, INSTANT_SIZE_PERCENT);
947
948 // Delay deletion as we could've gotten here from an InstantOverlay method.
949 DeletePageSoon(overlay_.Pass());
950
951 // Try to create another overlay immediately so that it is ready for the next
952 // user interaction.
953 ResetOverlay(GetInstantURL());
954
955 if (instant_tab_)
956 use_tab_for_suggestions_ = true;
957
958 LOG_INSTANT_DEBUG_EVENT(this, "Committed");
959 return true;
960 }
961
962 void InstantController::OmniboxFocusChanged( 238 void InstantController::OmniboxFocusChanged(
963 OmniboxFocusState state, 239 OmniboxFocusState state,
964 OmniboxFocusChangeReason reason, 240 OmniboxFocusChangeReason reason,
965 gfx::NativeView view_gaining_focus) { 241 gfx::NativeView view_gaining_focus) {
966 LOG_INSTANT_DEBUG_EVENT(this, base::StringPrintf( 242 LOG_INSTANT_DEBUG_EVENT(this, base::StringPrintf(
967 "OmniboxFocusChanged: %d to %d for reason %d", omnibox_focus_state_, 243 "OmniboxFocusChanged: %d to %d for reason %d", omnibox_focus_state_,
968 state, reason)); 244 state, reason));
969 245
970 OmniboxFocusState old_focus_state = omnibox_focus_state_;
971 omnibox_focus_state_ = state; 246 omnibox_focus_state_ = state;
972 if (!extended_enabled() && !instant_enabled_) 247 if (!extended_enabled() || !instant_tab_)
973 return; 248 return;
974 249
975 content::NotificationService::current()->Notify( 250 content::NotificationService::current()->Notify(
976 chrome::NOTIFICATION_OMNIBOX_FOCUS_CHANGED, 251 chrome::NOTIFICATION_OMNIBOX_FOCUS_CHANGED,
977 content::Source<InstantController>(this), 252 content::Source<InstantController>(this),
978 content::NotificationService::NoDetails()); 253 content::NotificationService::NoDetails());
979 254
980 if (extended_enabled()) { 255 instant_tab_->sender()->FocusChanged(omnibox_focus_state_, reason);
981 if (overlay_) 256 // Don't send oninputstart/oninputend updates in response to focus changes
982 overlay_->sender()->FocusChanged(omnibox_focus_state_, reason); 257 // if there's a navigation in progress. This prevents Chrome from sending
983 258 // a spurious oninputend when the user accepts a match in the omnibox.
984 if (instant_tab_) { 259 if (instant_tab_->contents()->GetController().GetPendingEntry() == NULL)
985 instant_tab_->sender()->FocusChanged(omnibox_focus_state_, reason); 260 instant_tab_->sender()->SetInputInProgress(IsInputInProgress());
986 // Don't send oninputstart/oninputend updates in response to focus changes
987 // if there's a navigation in progress. This prevents Chrome from sending
988 // a spurious oninputend when the user accepts a match in the omnibox.
989 if (instant_tab_->contents()->GetController().GetPendingEntry() == NULL)
990 instant_tab_->sender()->SetInputInProgress(IsInputInProgress());
991 }
992 }
993
994 if (state == OMNIBOX_FOCUS_VISIBLE && old_focus_state == OMNIBOX_FOCUS_NONE) {
995 // If the user explicitly focused the omnibox, then create the overlay if
996 // it doesn't exist. If we're using a fallback overlay, try loading the
997 // remote overlay again.
998 if (!overlay_ || (overlay_->IsLocal() && !use_local_page_only_))
999 ResetOverlay(GetInstantURL());
1000 } else if (state == OMNIBOX_FOCUS_NONE &&
1001 old_focus_state != OMNIBOX_FOCUS_NONE) {
1002 // If the focus went from the omnibox to outside the omnibox, commit or
1003 // discard the overlay.
1004 OmniboxLostFocus(view_gaining_focus);
1005 }
1006 } 261 }
1007 262
1008 void InstantController::SearchModeChanged(const SearchMode& old_mode, 263 void InstantController::SearchModeChanged(const SearchMode& old_mode,
1009 const SearchMode& new_mode) { 264 const SearchMode& new_mode) {
1010 if (!extended_enabled()) 265 if (!extended_enabled())
1011 return; 266 return;
1012 267
1013 LOG_INSTANT_DEBUG_EVENT(this, base::StringPrintf( 268 LOG_INSTANT_DEBUG_EVENT(this, base::StringPrintf(
1014 "SearchModeChanged: [origin:mode] %d:%d to %d:%d", old_mode.origin, 269 "SearchModeChanged: [origin:mode] %d:%d to %d:%d", old_mode.origin,
1015 old_mode.mode, new_mode.origin, new_mode.mode)); 270 old_mode.mode, new_mode.origin, new_mode.mode));
1016 271
1017 search_mode_ = new_mode; 272 search_mode_ = new_mode;
1018 if (!new_mode.is_search_suggestions())
1019 HideOverlay();
1020
1021 ResetInstantTab(); 273 ResetInstantTab();
1022 274
1023 if (instant_tab_ && old_mode.is_ntp() != new_mode.is_ntp()) 275 if (instant_tab_ && old_mode.is_ntp() != new_mode.is_ntp())
1024 instant_tab_->sender()->SetInputInProgress(IsInputInProgress()); 276 instant_tab_->sender()->SetInputInProgress(IsInputInProgress());
1025 } 277 }
1026 278
1027 void InstantController::ActiveTabChanged() { 279 void InstantController::ActiveTabChanged() {
1028 if (!extended_enabled() && !instant_enabled_) 280 if (!extended_enabled())
1029 return; 281 return;
1030 282
1031 LOG_INSTANT_DEBUG_EVENT(this, "ActiveTabChanged"); 283 LOG_INSTANT_DEBUG_EVENT(this, "ActiveTabChanged");
1032 284 ResetInstantTab();
1033 // When switching tabs, always hide the overlay.
1034 HideOverlay();
1035
1036 if (extended_enabled())
1037 ResetInstantTab();
1038 } 285 }
1039 286
1040 void InstantController::TabDeactivated(content::WebContents* contents) { 287 void InstantController::TabDeactivated(content::WebContents* contents) {
1041 LOG_INSTANT_DEBUG_EVENT(this, "TabDeactivated");
1042 if (extended_enabled() && !contents->IsBeingDestroyed())
1043 CommitIfPossible(INSTANT_COMMIT_FOCUS_LOST);
1044
1045 if (GetOverlayContents())
1046 HideOverlay();
1047 }
1048
1049 void InstantController::SetInstantEnabled(bool instant_enabled,
1050 bool use_local_page_only) {
1051 LOG_INSTANT_DEBUG_EVENT(this, base::StringPrintf(
1052 "SetInstantEnabled: instant_enabled=%d, use_local_page_only=%d",
1053 instant_enabled, use_local_page_only));
1054
1055 // Non extended mode does not care about |use_local_page_only|.
1056 if (instant_enabled == instant_enabled_ &&
1057 (!extended_enabled() ||
1058 use_local_page_only == use_local_page_only_)) {
1059 return;
1060 }
1061
1062 instant_enabled_ = instant_enabled;
1063 use_local_page_only_ = use_local_page_only;
1064 preload_ntp_ = !use_local_page_only_;
1065
1066 // Preload the overlay.
1067 HideInternal();
1068 overlay_.reset();
1069 if (extended_enabled() || instant_enabled_)
1070 ResetOverlay(GetInstantURL());
1071
1072 // Preload the Instant NTP.
1073 ntp_.reset();
1074 if (extended_enabled() && preload_ntp_)
1075 ResetNTP(GetInstantURL());
1076
1077 if (instant_tab_)
1078 instant_tab_->sender()->SetDisplayInstantResults(instant_enabled_);
1079 } 288 }
1080 289
1081 void InstantController::ThemeInfoChanged( 290 void InstantController::ThemeInfoChanged(
1082 const ThemeBackgroundInfo& theme_info) { 291 const ThemeBackgroundInfo& theme_info) {
1083 if (!extended_enabled()) 292 if (!extended_enabled())
1084 return; 293 return;
1085 294
1086 if (overlay_)
1087 overlay_->sender()->SendThemeBackgroundInfo(theme_info);
1088 if (ntp_) 295 if (ntp_)
1089 ntp_->sender()->SendThemeBackgroundInfo(theme_info); 296 ntp_->sender()->SendThemeBackgroundInfo(theme_info);
1090 if (instant_tab_) 297 if (instant_tab_)
1091 instant_tab_->sender()->SendThemeBackgroundInfo(theme_info); 298 instant_tab_->sender()->SendThemeBackgroundInfo(theme_info);
1092 } 299 }
1093 300
1094 void InstantController::SwappedOverlayContents() {
1095 model_.SetOverlayContents(GetOverlayContents());
1096 }
1097
1098 void InstantController::FocusedOverlayContents() {
1099 #if defined(USE_AURA)
1100 // On aura the omnibox only receives a focus lost if we initiate the focus
1101 // change. This does that.
1102 if (!model_.mode().is_default())
1103 browser_->InstantOverlayFocused();
1104 #endif
1105 }
1106
1107 void InstantController::ReloadOverlayIfStale() {
1108 // The local overlay is never stale.
1109 if (overlay_ && (overlay_->IsLocal() || !overlay_->is_stale()))
1110 return;
1111
1112 // If the overlay is showing or the omnibox has focus, don't refresh the
1113 // overlay. It will get refreshed the next time the overlay is hidden or the
1114 // omnibox loses focus.
1115 if (omnibox_focus_state_ == OMNIBOX_FOCUS_NONE && model_.mode().is_default())
1116 ResetOverlay(GetInstantURL());
1117 }
1118
1119 void InstantController::OverlayLoadCompletedMainFrame() {
1120 if (!overlay_ || overlay_->supports_instant())
1121 return;
1122 InstantService* instant_service = GetInstantService();
1123 content::WebContents* contents = overlay_->contents();
1124 DCHECK(contents);
1125 if (instant_service->IsInstantProcess(
1126 contents->GetRenderProcessHost()->GetID())) {
1127 return;
1128 }
1129 InstantSupportDetermined(contents, false);
1130 }
1131
1132 void InstantController::LogDebugEvent(const std::string& info) const { 301 void InstantController::LogDebugEvent(const std::string& info) const {
1133 DVLOG(1) << info; 302 DVLOG(1) << info;
1134 303
1135 debug_events_.push_front(std::make_pair( 304 debug_events_.push_front(std::make_pair(
1136 base::Time::Now().ToInternalValue(), info)); 305 base::Time::Now().ToInternalValue(), info));
1137 static const size_t kMaxDebugEventSize = 2000; 306 static const size_t kMaxDebugEventSize = 2000;
1138 if (debug_events_.size() > kMaxDebugEventSize) 307 if (debug_events_.size() > kMaxDebugEventSize)
1139 debug_events_.pop_back(); 308 debug_events_.pop_back();
1140 } 309 }
1141 310
1142 void InstantController::ClearDebugEvents() { 311 void InstantController::ClearDebugEvents() {
1143 debug_events_.clear(); 312 debug_events_.clear();
1144 } 313 }
1145 314
1146 void InstantController::MostVisitedItemsChanged( 315 void InstantController::MostVisitedItemsChanged(
1147 const std::vector<InstantMostVisitedItem>& items) { 316 const std::vector<InstantMostVisitedItem>& items) {
1148 if (overlay_)
1149 overlay_->sender()->SendMostVisitedItems(items);
1150
1151 if (ntp_) 317 if (ntp_)
1152 ntp_->sender()->SendMostVisitedItems(items); 318 ntp_->sender()->SendMostVisitedItems(items);
1153
1154 if (instant_tab_) 319 if (instant_tab_)
1155 instant_tab_->sender()->SendMostVisitedItems(items); 320 instant_tab_->sender()->SendMostVisitedItems(items);
1156 321
1157 content::NotificationService::current()->Notify( 322 content::NotificationService::current()->Notify(
1158 chrome::NOTIFICATION_INSTANT_SENT_MOST_VISITED_ITEMS, 323 chrome::NOTIFICATION_INSTANT_SENT_MOST_VISITED_ITEMS,
1159 content::Source<InstantController>(this), 324 content::Source<InstantController>(this),
1160 content::NotificationService::NoDetails()); 325 content::NotificationService::NoDetails());
1161 } 326 }
1162 327
1163 void InstantController::DeleteMostVisitedItem(const GURL& url) { 328 void InstantController::DeleteMostVisitedItem(const GURL& url) {
(...skipping 19 matching lines...) Expand all
1183 if (!instant_service) 348 if (!instant_service)
1184 return; 349 return;
1185 350
1186 instant_service->UndoAllMostVisitedDeletions(); 351 instant_service->UndoAllMostVisitedDeletions();
1187 } 352 }
1188 353
1189 Profile* InstantController::profile() const { 354 Profile* InstantController::profile() const {
1190 return browser_->profile(); 355 return browser_->profile();
1191 } 356 }
1192 357
1193 InstantOverlay* InstantController::overlay() const {
1194 return overlay_.get();
1195 }
1196
1197 InstantTab* InstantController::instant_tab() const { 358 InstantTab* InstantController::instant_tab() const {
1198 return instant_tab_.get(); 359 return instant_tab_.get();
1199 } 360 }
1200 361
1201 InstantNTP* InstantController::ntp() const { 362 InstantNTP* InstantController::ntp() const {
1202 return ntp_.get(); 363 return ntp_.get();
1203 } 364 }
1204 365
1205 void InstantController::OnNetworkChanged( 366 void InstantController::OnNetworkChanged(
1206 net::NetworkChangeNotifier::ConnectionType type) { 367 net::NetworkChangeNotifier::ConnectionType type) {
1207 // Not interested in events conveying change to offline 368 // Not interested in events conveying change to offline
1208 if (type == net::NetworkChangeNotifier::CONNECTION_NONE) 369 if (type == net::NetworkChangeNotifier::CONNECTION_NONE)
1209 return; 370 return;
1210 if (!extended_enabled_ || use_local_page_only_) 371 if (!extended_enabled_)
1211 return; 372 return;
1212 if (!ntp_ || ntp_->IsLocal()) 373 if (!ntp_ || ntp_->IsLocal())
1213 ResetNTP(GetInstantURL()); 374 ResetNTP(GetInstantURL());
1214 } 375 }
1215 376
1216 // TODO(shishir): We assume that the WebContent's current RenderViewHost is the 377 // TODO(shishir): We assume that the WebContent's current RenderViewHost is the
1217 // RenderViewHost being created which is not always true. Fix this. 378 // RenderViewHost being created which is not always true. Fix this.
1218 void InstantController::InstantPageRenderViewCreated( 379 void InstantController::InstantPageRenderViewCreated(
1219 const content::WebContents* contents) { 380 const content::WebContents* contents) {
1220 if (!extended_enabled()) 381 if (!extended_enabled())
1221 return; 382 return;
1222 383
1223 // Update theme info so that the page picks it up. 384 // Update theme info so that the page picks it up.
1224 InstantService* instant_service = GetInstantService(); 385 InstantService* instant_service = GetInstantService();
1225 if (instant_service) { 386 if (instant_service) {
1226 instant_service->UpdateThemeInfo(); 387 instant_service->UpdateThemeInfo();
1227 instant_service->UpdateMostVisitedItemsInfo(); 388 instant_service->UpdateMostVisitedItemsInfo();
1228 } 389 }
1229 390
1230 // Ensure the searchbox API has the correct initial state. 391 // Ensure the searchbox API has the correct initial state.
1231 if (IsContentsFrom(overlay(), contents)) { 392 if (IsContentsFrom(ntp(), contents)) {
1232 overlay_->sender()->SetDisplayInstantResults(instant_enabled_);
1233 overlay_->sender()->FocusChanged(omnibox_focus_state_,
1234 omnibox_focus_change_reason_);
1235 overlay_->sender()->SetOmniboxBounds(omnibox_bounds_);
1236 overlay_->InitializeFonts();
1237 } else if (IsContentsFrom(ntp(), contents)) {
1238 ntp_->sender()->SetDisplayInstantResults(instant_enabled_);
1239 ntp_->sender()->SetOmniboxBounds(omnibox_bounds_); 393 ntp_->sender()->SetOmniboxBounds(omnibox_bounds_);
1240 ntp_->InitializeFonts(); 394 ntp_->InitializeFonts();
1241 ntp_->InitializePromos(); 395 ntp_->InitializePromos();
1242 } else { 396 } else {
1243 NOTREACHED(); 397 NOTREACHED();
1244 } 398 }
1245 } 399 }
1246 400
1247 void InstantController::InstantSupportChanged( 401 void InstantController::InstantSupportChanged(
1248 InstantSupportState instant_support) { 402 InstantSupportState instant_support) {
(...skipping 30 matching lines...) Expand all
1279 // Preload a local NTP in place of the broken online one. 433 // Preload a local NTP in place of the broken online one.
1280 if (!is_local) 434 if (!is_local)
1281 ResetNTP(GetLocalInstantURL()); 435 ResetNTP(GetLocalInstantURL());
1282 } 436 }
1283 437
1284 content::NotificationService::current()->Notify( 438 content::NotificationService::current()->Notify(
1285 chrome::NOTIFICATION_INSTANT_NTP_SUPPORT_DETERMINED, 439 chrome::NOTIFICATION_INSTANT_NTP_SUPPORT_DETERMINED,
1286 content::Source<InstantController>(this), 440 content::Source<InstantController>(this),
1287 content::NotificationService::NoDetails()); 441 content::NotificationService::NoDetails());
1288 442
1289 } else if (IsContentsFrom(overlay(), contents)) { 443 } else {
1290 if (!supports_instant) { 444 NOTREACHED();
1291 HideInternal();
1292 bool is_local = overlay_->IsLocal();
1293 DeletePageSoon(overlay_.Pass());
1294 // Preload a local overlay in place of the broken online one.
1295 if (!is_local && extended_enabled())
1296 ResetOverlay(GetLocalInstantURL());
1297 }
1298
1299 content::NotificationService::current()->Notify(
1300 chrome::NOTIFICATION_INSTANT_OVERLAY_SUPPORT_DETERMINED,
1301 content::Source<InstantController>(this),
1302 content::NotificationService::NoDetails());
1303 } 445 }
1304 } 446 }
1305 447
1306 void InstantController::InstantPageRenderViewGone( 448 void InstantController::InstantPageRenderViewGone(
1307 const content::WebContents* contents) { 449 const content::WebContents* contents) {
1308 if (IsContentsFrom(overlay(), contents)) { 450 if (IsContentsFrom(ntp(), contents)) {
1309 HideInternal();
1310 DeletePageSoon(overlay_.Pass());
1311 } else if (IsContentsFrom(ntp(), contents)) {
1312 DeletePageSoon(ntp_.Pass()); 451 DeletePageSoon(ntp_.Pass());
1313 } else { 452 } else {
1314 NOTREACHED(); 453 NOTREACHED();
1315 } 454 }
1316 } 455 }
1317 456
1318 void InstantController::InstantPageAboutToNavigateMainFrame( 457 void InstantController::InstantPageAboutToNavigateMainFrame(
1319 const content::WebContents* contents, 458 const content::WebContents* contents,
1320 const GURL& url) { 459 const GURL& url) {
1321 if (IsContentsFrom(overlay(), contents)) { 460 if (IsContentsFrom(instant_tab(), contents)) {
1322 // If the page does not yet support Instant, we allow redirects and other
1323 // navigations to go through since the Instant URL can redirect - e.g. to
1324 // country specific pages.
1325 if (!overlay_->supports_instant())
1326 return;
1327
1328 GURL instant_url(overlay_->instant_url());
1329
1330 // If we are navigating to the Instant URL, do nothing.
1331 if (url == instant_url)
1332 return;
1333
1334 // Commit the navigation if either:
1335 // - The page is in NTP mode (so it could only navigate on a user click) or
1336 // - The page is not in NTP mode and we are navigating to a URL with a
1337 // different host or path than the Instant URL. This enables the instant
1338 // page when it is showing search results to change the query parameters
1339 // and fragments of the URL without it navigating.
1340 if (model_.mode().is_ntp() ||
1341 (url.host() != instant_url.host() ||
1342 url.path() != instant_url.path())) {
1343 CommitIfPossible(INSTANT_COMMIT_NAVIGATED);
1344 }
1345 } else if (IsContentsFrom(instant_tab(), contents)) {
1346 // The Instant tab navigated. Send it the data it needs to display 461 // The Instant tab navigated. Send it the data it needs to display
1347 // properly. 462 // properly.
1348 UpdateInfoForInstantTab(); 463 UpdateInfoForInstantTab();
1349 } else { 464 } else {
1350 NOTREACHED(); 465 NOTREACHED();
1351 } 466 }
1352 } 467 }
1353 468
1354 void InstantController::SetSuggestions(
1355 const content::WebContents* contents,
1356 const std::vector<InstantSuggestion>& suggestions) {
1357 LOG_INSTANT_DEBUG_EVENT(this, "SetSuggestions");
1358
1359 // Ignore if the message is from an unexpected source.
1360 if (IsContentsFrom(ntp(), contents))
1361 return;
1362 if (UseTabForSuggestions() && !IsContentsFrom(instant_tab(), contents))
1363 return;
1364 if (IsContentsFrom(overlay(), contents) &&
1365 !allow_overlay_to_show_search_suggestions_)
1366 return;
1367
1368 InstantSuggestion suggestion;
1369 if (!suggestions.empty())
1370 suggestion = suggestions[0];
1371
1372 // TODO(samarth): allow InstantTabs to call SetSuggestions() from the NTP once
1373 // that is better supported.
1374 bool can_use_instant_tab = UseTabForSuggestions() &&
1375 search_mode_.is_search();
1376 bool can_use_overlay = search_mode_.is_search_suggestions() &&
1377 !last_omnibox_text_.empty();
1378 if (!can_use_instant_tab && !can_use_overlay)
1379 return;
1380
1381 if (suggestion.behavior == INSTANT_COMPLETE_REPLACE) {
1382 if (omnibox_focus_state_ == OMNIBOX_FOCUS_NONE) {
1383 // TODO(samarth,skanuj): setValue() needs to be handled differently when
1384 // the omnibox doesn't have focus. Instead of setting temporary text, we
1385 // should be setting search terms on the appropriate NavigationEntry.
1386 // (Among other things, this ensures that URL-shaped values will get the
1387 // additional security token.)
1388 //
1389 // Note that this also breaks clicking on a suggestion corresponding to
1390 // gray-text completion: we can't distinguish between the user
1391 // clicking on white space (where we don't accept the gray text) and the
1392 // user clicking on the suggestion (when we do accept the gray text).
1393 // This needs to be fixed before we can turn on Instant again.
1394 return;
1395 }
1396
1397 // We don't get an Update() when changing the omnibox due to a REPLACE
1398 // suggestion (so that we don't inadvertently cause the overlay to change
1399 // what it's showing, as the user arrows up/down through the page-provided
1400 // suggestions). So, update these state variables here.
1401 last_omnibox_text_ = suggestion.text;
1402 last_user_text_.clear();
1403 last_suggestion_ = InstantSuggestion();
1404 last_match_was_search_ = suggestion.type == INSTANT_SUGGESTION_SEARCH;
1405 LOG_INSTANT_DEBUG_EVENT(this, base::StringPrintf(
1406 "ReplaceSuggestion text='%s' type=%d",
1407 UTF16ToUTF8(suggestion.text).c_str(), suggestion.type));
1408 browser_->SetInstantSuggestion(suggestion);
1409 } else {
1410 if (FixSuggestion(&suggestion)) {
1411 last_suggestion_ = suggestion;
1412 if (suggestion.type == INSTANT_SUGGESTION_SEARCH &&
1413 suggestion.behavior == INSTANT_COMPLETE_NEVER)
1414 last_omnibox_text_ = last_user_text_;
1415 LOG_INSTANT_DEBUG_EVENT(this, base::StringPrintf(
1416 "SetInstantSuggestion: text='%s' behavior=%d",
1417 UTF16ToUTF8(suggestion.text).c_str(),
1418 suggestion.behavior));
1419 browser_->SetInstantSuggestion(suggestion);
1420 content::NotificationService::current()->Notify(
1421 chrome::NOTIFICATION_INSTANT_SET_SUGGESTION,
1422 content::Source<InstantController>(this),
1423 content::NotificationService::NoDetails());
1424 } else {
1425 last_suggestion_ = InstantSuggestion();
1426 }
1427 }
1428
1429 // Extended mode pages will call ShowOverlay() when they are ready.
1430 if (!extended_enabled())
1431 ShowOverlay(100, INSTANT_SIZE_PERCENT);
1432 }
1433
1434 void InstantController::ShowInstantOverlay(const content::WebContents* contents,
1435 int height,
1436 InstantSizeUnits units) {
1437 if (extended_enabled() && IsContentsFrom(overlay(), contents))
1438 ShowOverlay(height, units);
1439 }
1440
1441 void InstantController::LogDropdownShown() {
1442 // If suggestions are being shown for the first time since the user started
1443 // typing, record a histogram value.
1444 if (!first_interaction_time_.is_null() && !first_interaction_time_recorded_) {
1445 base::TimeDelta delta = base::Time::Now() - first_interaction_time_;
1446 first_interaction_time_recorded_ = true;
1447 if (search_mode_.is_origin_ntp()) {
1448 UMA_HISTOGRAM_TIMES("Instant.TimeToFirstShowFromNTP", delta);
1449 LOG_INSTANT_DEBUG_EVENT(this, base::StringPrintf(
1450 "LogShowInstantOverlay: TimeToFirstShowFromNTP=%d",
1451 static_cast<int>(delta.InMilliseconds())));
1452 } else if (search_mode_.is_origin_search()) {
1453 UMA_HISTOGRAM_TIMES("Instant.TimeToFirstShowFromSERP", delta);
1454 LOG_INSTANT_DEBUG_EVENT(this, base::StringPrintf(
1455 "LogShowInstantOverlay: TimeToFirstShowFromSERP=%d",
1456 static_cast<int>(delta.InMilliseconds())));
1457 } else {
1458 UMA_HISTOGRAM_TIMES("Instant.TimeToFirstShowFromWeb", delta);
1459 LOG_INSTANT_DEBUG_EVENT(this, base::StringPrintf(
1460 "LogShowInstantOverlay: TimeToFirstShowFromWeb=%d",
1461 static_cast<int>(delta.InMilliseconds())));
1462 }
1463 }
1464 }
1465
1466 void InstantController::FocusOmnibox(const content::WebContents* contents, 469 void InstantController::FocusOmnibox(const content::WebContents* contents,
1467 OmniboxFocusState state) { 470 OmniboxFocusState state) {
1468 if (!extended_enabled()) 471 if (!extended_enabled())
1469 return; 472 return;
1470 473
1471 DCHECK(IsContentsFrom(instant_tab(), contents)); 474 DCHECK(IsContentsFrom(instant_tab(), contents));
1472 475
1473 // Do not add a default case in the switch block for the following reasons: 476 // Do not add a default case in the switch block for the following reasons:
1474 // (1) Explicitly handle the new states. If new states are added in the 477 // (1) Explicitly handle the new states. If new states are added in the
1475 // OmniboxFocusState, the compiler will warn the developer to handle the new 478 // OmniboxFocusState, the compiler will warn the developer to handle the new
(...skipping 22 matching lines...) Expand all
1498 content::PageTransition transition, 501 content::PageTransition transition,
1499 WindowOpenDisposition disposition, 502 WindowOpenDisposition disposition,
1500 bool is_search_type) { 503 bool is_search_type) {
1501 LOG_INSTANT_DEBUG_EVENT(this, base::StringPrintf( 504 LOG_INSTANT_DEBUG_EVENT(this, base::StringPrintf(
1502 "NavigateToURL: url='%s'", url.spec().c_str())); 505 "NavigateToURL: url='%s'", url.spec().c_str()));
1503 506
1504 // TODO(samarth): handle case where contents are no longer "active" (e.g. user 507 // TODO(samarth): handle case where contents are no longer "active" (e.g. user
1505 // has switched tabs). 508 // has switched tabs).
1506 if (!extended_enabled()) 509 if (!extended_enabled())
1507 return; 510 return;
1508 if (overlay_) {
1509 HideOverlay();
1510 }
1511 511
1512 if (transition == content::PAGE_TRANSITION_AUTO_BOOKMARK) { 512 if (transition == content::PAGE_TRANSITION_AUTO_BOOKMARK) {
1513 content::RecordAction( 513 content::RecordAction(
1514 content::UserMetricsAction("InstantExtended.MostVisitedClicked")); 514 content::UserMetricsAction("InstantExtended.MostVisitedClicked"));
1515 } else { 515 } else {
1516 // Exclude navigation by Most Visited click and searches. 516 // Exclude navigation by Most Visited click and searches.
1517 if (!is_search_type) 517 if (!is_search_type)
1518 RecordNavigationHistogram(UsingLocalPage(), true, true); 518 RecordNavigationHistogram(UsingLocalPage(), true, true);
1519 } 519 }
1520 browser_->OpenURL(url, transition, disposition); 520 browser_->OpenURL(url, transition, disposition);
1521 } 521 }
1522 522
1523 void InstantController::OmniboxLostFocus(gfx::NativeView view_gaining_focus) {
1524 // If the overlay is showing custom NTP content, don't hide it, commit it
1525 // (no matter where the user clicked) or try to recreate it.
1526 if (model_.mode().is_ntp())
1527 return;
1528
1529 if (model_.mode().is_default()) {
1530 // If the overlay is not showing at all, recreate it if it's stale.
1531 ReloadOverlayIfStale();
1532 return;
1533 }
1534
1535 // The overlay is showing search suggestions. If GetOverlayContents() is NULL,
1536 // we are in the commit path. Don't do anything.
1537 if (!GetOverlayContents())
1538 return;
1539
1540 #if defined(OS_MACOSX)
1541 // TODO(sreeram): See if Mac really needs this special treatment.
1542 if (!overlay_->is_pointer_down_from_activate())
1543 HideOverlay();
1544 #else
1545 if (IsFullHeight(model_))
1546 CommitIfPossible(INSTANT_COMMIT_FOCUS_LOST);
1547 else if (!IsViewInContents(GetViewGainingFocus(view_gaining_focus),
1548 overlay_->contents()))
1549 HideOverlay();
1550 #endif
1551 }
1552
1553 std::string InstantController::GetLocalInstantURL() const { 523 std::string InstantController::GetLocalInstantURL() const {
1554 return chrome::GetLocalInstantURL(profile()).spec(); 524 return chrome::GetLocalInstantURL(profile()).spec();
1555 } 525 }
1556 526
1557 std::string InstantController::GetInstantURL() const { 527 std::string InstantController::GetInstantURL() const {
1558 if (extended_enabled() && 528 if (extended_enabled() && net::NetworkChangeNotifier::IsOffline())
1559 (use_local_page_only_ || net::NetworkChangeNotifier::IsOffline()))
1560 return GetLocalInstantURL(); 529 return GetLocalInstantURL();
1561 530
1562 const GURL instant_url = chrome::GetInstantURL(profile(), 531 const GURL instant_url = chrome::GetInstantURL(profile(),
1563 omnibox_bounds_.x()); 532 omnibox_bounds_.x());
1564 if (instant_url.is_valid()) 533 if (instant_url.is_valid())
1565 return instant_url.spec(); 534 return instant_url.spec();
1566 535
1567 // Only extended mode has a local fallback. 536 // Only extended mode has a local fallback.
1568 return extended_enabled() ? GetLocalInstantURL() : std::string(); 537 return extended_enabled() ? GetLocalInstantURL() : std::string();
1569 } 538 }
(...skipping 24 matching lines...) Expand all
1594 ntp_.reset(new InstantNTP(this, instant_url, 563 ntp_.reset(new InstantNTP(this, instant_url,
1595 browser_->profile()->IsOffTheRecord())); 564 browser_->profile()->IsOffTheRecord()));
1596 ntp_->InitContents(profile(), browser_->GetActiveWebContents(), 565 ntp_->InitContents(profile(), browser_->GetActiveWebContents(),
1597 base::Bind(&InstantController::ReloadStaleNTP, 566 base::Bind(&InstantController::ReloadStaleNTP,
1598 base::Unretained(this))); 567 base::Unretained(this)));
1599 LOG_INSTANT_DEBUG_EVENT(this, base::StringPrintf( 568 LOG_INSTANT_DEBUG_EVENT(this, base::StringPrintf(
1600 "ResetNTP: instant_url='%s'", instant_url.c_str())); 569 "ResetNTP: instant_url='%s'", instant_url.c_str()));
1601 } 570 }
1602 571
1603 void InstantController::ReloadStaleNTP() { 572 void InstantController::ReloadStaleNTP() {
1604 ResetNTP(GetInstantURL()); 573 if (extended_enabled())
574 ResetNTP(GetInstantURL());
1605 } 575 }
1606 576
1607 bool InstantController::ShouldSwitchToLocalNTP() const { 577 bool InstantController::ShouldSwitchToLocalNTP() const {
1608 if (!ntp()) 578 if (!ntp())
1609 return true; 579 return true;
1610 580
1611 // Assume users with Javascript disabled do not want the online experience. 581 // Assume users with Javascript disabled do not want the online experience.
1612 if (!IsJavascriptEnabled()) 582 if (!IsJavascriptEnabled())
1613 return true; 583 return true;
1614 584
1615 // Already a local page. Not calling IsLocal() because we want to distinguish 585 // Already a local page. Not calling IsLocal() because we want to distinguish
1616 // between the Google-specific and generic local NTP. 586 // between the Google-specific and generic local NTP.
1617 if (extended_enabled() && ntp()->instant_url() == GetLocalInstantURL()) 587 if (extended_enabled() && ntp()->instant_url() == GetLocalInstantURL())
1618 return false; 588 return false;
1619 589
1620 if (PageIsCurrent(ntp())) 590 if (PageIsCurrent(ntp()))
1621 return false; 591 return false;
1622 592
1623 // The preloaded NTP does not support instant yet. If we're not in startup, 593 // The preloaded NTP does not support instant yet. If we're not in startup,
1624 // always fall back to the local NTP. If we are in startup, use the local NTP 594 // always fall back to the local NTP. If we are in startup, use the local NTP
1625 // (unless the finch flag to use the remote NTP is set). 595 // (unless the finch flag to use the remote NTP is set).
1626 return !(InStartup() && chrome::ShouldPreferRemoteNTPOnStartup()); 596 return !(InStartup() && chrome::ShouldPreferRemoteNTPOnStartup());
1627 } 597 }
1628 598
1629 void InstantController::ResetOverlay(const std::string& instant_url) {
1630 HideInternal();
1631 overlay_.reset();
1632 }
1633
1634 InstantController::InstantFallbackReason
1635 InstantController::ShouldSwitchToLocalOverlay() const {
1636 if (!extended_enabled())
1637 return INSTANT_FALLBACK_NONE;
1638
1639 if (!overlay())
1640 return DetermineFallbackReason(NULL, std::string());
1641
1642 // Assume users with Javascript disabled do not want the online experience.
1643 if (!IsJavascriptEnabled())
1644 return INSTANT_FALLBACK_JAVASCRIPT_DISABLED;
1645
1646 if (overlay()->IsLocal())
1647 return INSTANT_FALLBACK_NONE;
1648
1649 bool page_is_current = PageIsCurrent(overlay());
1650 if (!page_is_current)
1651 return DetermineFallbackReason(overlay(), GetInstantURL());
1652
1653 return INSTANT_FALLBACK_NONE;
1654 }
1655
1656 void InstantController::ResetInstantTab() { 599 void InstantController::ResetInstantTab() {
1657 if (!search_mode_.is_origin_default()) { 600 if (!search_mode_.is_origin_default()) {
1658 content::WebContents* active_tab = browser_->GetActiveWebContents(); 601 content::WebContents* active_tab = browser_->GetActiveWebContents();
1659 if (!instant_tab_ || active_tab != instant_tab_->contents()) { 602 if (!instant_tab_ || active_tab != instant_tab_->contents()) {
1660 instant_tab_.reset( 603 instant_tab_.reset(
1661 new InstantTab(this, browser_->profile()->IsOffTheRecord())); 604 new InstantTab(this, browser_->profile()->IsOffTheRecord()));
1662 instant_tab_->Init(active_tab); 605 instant_tab_->Init(active_tab);
1663 UpdateInfoForInstantTab(); 606 UpdateInfoForInstantTab();
1664 use_tab_for_suggestions_ = true;
1665 } 607 }
1666
1667 // Hide the |overlay_| since we are now using |instant_tab_| instead.
1668 HideOverlay();
1669 } else { 608 } else {
1670 instant_tab_.reset(); 609 instant_tab_.reset();
1671 } 610 }
1672 } 611 }
1673 612
1674 void InstantController::UpdateInfoForInstantTab() { 613 void InstantController::UpdateInfoForInstantTab() {
1675 if (instant_tab_) { 614 if (instant_tab_) {
1676 instant_tab_->sender()->SetDisplayInstantResults(instant_enabled_);
1677 instant_tab_->sender()->SetOmniboxBounds(omnibox_bounds_); 615 instant_tab_->sender()->SetOmniboxBounds(omnibox_bounds_);
1678 616
1679 // Update theme details. 617 // Update theme details.
1680 InstantService* instant_service = GetInstantService(); 618 InstantService* instant_service = GetInstantService();
1681 if (instant_service) { 619 if (instant_service) {
1682 instant_service->UpdateThemeInfo(); 620 instant_service->UpdateThemeInfo();
1683 instant_service->UpdateMostVisitedItemsInfo(); 621 instant_service->UpdateMostVisitedItemsInfo();
1684 } 622 }
1685 623
1686 instant_tab_->InitializeFonts(); 624 instant_tab_->InitializeFonts();
1687 instant_tab_->InitializePromos(); 625 instant_tab_->InitializePromos();
1688 instant_tab_->sender()->FocusChanged(omnibox_focus_state_, 626 instant_tab_->sender()->FocusChanged(omnibox_focus_state_,
1689 omnibox_focus_change_reason_); 627 omnibox_focus_change_reason_);
1690 instant_tab_->sender()->SetInputInProgress(IsInputInProgress()); 628 instant_tab_->sender()->SetInputInProgress(IsInputInProgress());
1691 } 629 }
1692 } 630 }
1693 631
1694 bool InstantController::IsInputInProgress() const { 632 bool InstantController::IsInputInProgress() const {
1695 return !search_mode_.is_ntp() && 633 return !search_mode_.is_ntp() &&
1696 omnibox_focus_state_ == OMNIBOX_FOCUS_VISIBLE; 634 omnibox_focus_state_ == OMNIBOX_FOCUS_VISIBLE;
1697 } 635 }
1698 636
1699 void InstantController::HideOverlay() {
1700 HideInternal();
1701 ReloadOverlayIfStale();
1702 }
1703
1704 void InstantController::HideInternal() {
1705 LOG_INSTANT_DEBUG_EVENT(this, "Hide");
1706
1707 // If GetOverlayContents() returns NULL, either we're already in the desired
1708 // MODE_DEFAULT state, or we're in the commit path. For the latter, don't
1709 // change the state just yet; else we may hide the overlay unnecessarily.
1710 // Instead, the state will be set correctly after the commit is done.
1711 if (GetOverlayContents()) {
1712 model_.SetOverlayState(SearchMode(), 0, INSTANT_SIZE_PERCENT);
1713 allow_overlay_to_show_search_suggestions_ = false;
1714
1715 // Send a message asking the overlay to clear out old results.
1716 overlay_->Update(string16(), 0, 0, true);
1717 }
1718
1719 // Clear the first interaction timestamp for later use.
1720 first_interaction_time_ = base::Time();
1721 first_interaction_time_recorded_ = false;
1722
1723 if (instant_tab_)
1724 use_tab_for_suggestions_ = true;
1725 }
1726
1727 void InstantController::ShowOverlay(int height, InstantSizeUnits units) {
1728 // Nothing to see here.
1729 if (!overlay_)
1730 return;
1731
1732 // If we are on a committed search results page, the |overlay_| is not in use.
1733 if (UseTabForSuggestions())
1734 return;
1735
1736 LOG_INSTANT_DEBUG_EVENT(this, base::StringPrintf(
1737 "Show: height=%d units=%d", height, units));
1738
1739 // Must have updated omnibox after the last HideOverlay() to show suggestions.
1740 if (!allow_overlay_to_show_search_suggestions_)
1741 return;
1742
1743 // The page is trying to hide itself. Hide explicitly (i.e., don't use
1744 // HideOverlay()) so that it can change its mind.
1745 if (height == 0) {
1746 model_.SetOverlayState(SearchMode(), 0, INSTANT_SIZE_PERCENT);
1747 if (instant_tab_)
1748 use_tab_for_suggestions_ = true;
1749 return;
1750 }
1751
1752 // Show at 100% height except in the following cases:
1753 // - The local overlay (omnibox popup) is being loaded.
1754 // - Instant is disabled. The page needs to be able to show only a dropdown.
1755 // - The page is over a website other than search or an NTP, and is not
1756 // already showing at 100% height.
1757 if (overlay_->IsLocal() || !instant_enabled_ ||
1758 (search_mode_.is_origin_default() && !IsFullHeight(model_)))
1759 model_.SetOverlayState(search_mode_, height, units);
1760 else
1761 model_.SetOverlayState(search_mode_, 100, INSTANT_SIZE_PERCENT);
1762
1763 // If the overlay is being shown at full height and the omnibox is not
1764 // focused, commit right away.
1765 if (IsFullHeight(model_) && omnibox_focus_state_ == OMNIBOX_FOCUS_NONE)
1766 CommitIfPossible(INSTANT_COMMIT_FOCUS_LOST);
1767 }
1768
1769 void InstantController::SendPopupBoundsToPage() {
1770 if (last_popup_bounds_ == popup_bounds_ || !overlay_ ||
1771 overlay_->is_pointer_down_from_activate())
1772 return;
1773
1774 last_popup_bounds_ = popup_bounds_;
1775 gfx::Rect overlay_bounds = browser_->GetInstantBounds();
1776 gfx::Rect intersection = gfx::IntersectRects(popup_bounds_, overlay_bounds);
1777
1778 // Translate into window coordinates.
1779 if (!intersection.IsEmpty()) {
1780 intersection.Offset(-overlay_bounds.origin().x(),
1781 -overlay_bounds.origin().y());
1782 }
1783
1784 // In the current Chrome UI, these must always be true so they sanity check
1785 // the above operations. In a future UI, these may be removed or adjusted.
1786 // There is no point in sanity-checking |intersection.y()| because the omnibox
1787 // can be placed anywhere vertically relative to the overlay (for example, in
1788 // Mac fullscreen mode, the omnibox is fully enclosed by the overlay bounds).
1789 DCHECK_LE(0, intersection.x());
1790 DCHECK_LE(0, intersection.width());
1791 DCHECK_LE(0, intersection.height());
1792
1793 overlay_->sender()->SetPopupBounds(intersection);
1794 }
1795
1796
1797
1798 bool InstantController::FixSuggestion(InstantSuggestion* suggestion) const {
1799 // We only accept suggestions if the user has typed text. If the user is
1800 // arrowing up/down (|last_user_text_| is empty), we reject suggestions.
1801 if (last_user_text_.empty())
1802 return false;
1803
1804 // If the page is trying to set inline autocompletion in verbatim mode,
1805 // instead try suggesting the exact omnibox text. This makes the omnibox
1806 // interpret user text as an URL if possible while preventing unwanted
1807 // autocompletion during backspacing.
1808 if (suggestion->behavior == INSTANT_COMPLETE_NOW && last_verbatim_)
1809 suggestion->text = last_omnibox_text_;
1810
1811 // Suggestion text should be a full URL for URL suggestions, or the
1812 // completion of a query for query suggestions.
1813 if (suggestion->type == INSTANT_SUGGESTION_URL) {
1814 // If the suggestion is not a valid URL, perhaps it's something like
1815 // "foo.com". Try prefixing "http://". If it still isn't valid, drop it.
1816 if (!GURL(suggestion->text).is_valid()) {
1817 suggestion->text.insert(0, ASCIIToUTF16("http://"));
1818 if (!GURL(suggestion->text).is_valid())
1819 return false;
1820 }
1821
1822 // URL suggestions are only accepted if the query for which the suggestion
1823 // was generated is the same as |last_user_text_|.
1824 //
1825 // Any other URL suggestions--in particular suggestions for old user_text
1826 // lagging behind a slow IPC--are ignored. See crbug.com/181589.
1827 //
1828 // TODO(samarth): Accept stale suggestions if they would be accepted by
1829 // SearchProvider as an inlinable suggestion. http://crbug.com/191656.
1830 return suggestion->query == last_user_text_;
1831 }
1832
1833 // We use |last_user_text_| because |last_omnibox_text| may contain text from
1834 // a previous URL suggestion at this point.
1835 if (suggestion->type == INSTANT_SUGGESTION_SEARCH) {
1836 if (StartsWith(suggestion->text, last_user_text_, true)) {
1837 // The user typed an exact prefix of the suggestion.
1838 suggestion->text.erase(0, last_user_text_.size());
1839 return true;
1840 } else if (NormalizeAndStripPrefix(&suggestion->text, last_user_text_)) {
1841 // Unicode normalize and case-fold the user text and suggestion. If the
1842 // user text is a prefix, suggest the normalized, case-folded completion
1843 // for instance, if the user types 'i' and the suggestion is 'INSTANT',
1844 // suggest 'nstant'. Otherwise, the user text really isn't a prefix, so
1845 // suggest nothing.
1846 // TODO(samarth|jered): revisit this logic. http://crbug.com/196572.
1847 return true;
1848 }
1849 }
1850
1851 return false;
1852 }
1853
1854 bool InstantController::UsingLocalPage() const { 637 bool InstantController::UsingLocalPage() const {
1855 return (UseTabForSuggestions() && instant_tab_->IsLocal()) || 638 return instant_tab_ && instant_tab_->IsLocal();
1856 (!UseTabForSuggestions() && overlay_ && overlay_->IsLocal());
1857 }
1858
1859 bool InstantController::UseTabForSuggestions() const {
1860 return instant_tab_ && use_tab_for_suggestions_;
1861 } 639 }
1862 640
1863 void InstantController::RedirectToLocalNTP(content::WebContents* contents) { 641 void InstantController::RedirectToLocalNTP(content::WebContents* contents) {
1864 contents->GetController().LoadURL( 642 contents->GetController().LoadURL(
1865 chrome::GetLocalInstantURL(browser_->profile()), 643 chrome::GetLocalInstantURL(browser_->profile()),
1866 content::Referrer(), 644 content::Referrer(),
1867 content::PAGE_TRANSITION_SERVER_REDIRECT, 645 content::PAGE_TRANSITION_SERVER_REDIRECT,
1868 std::string()); // No extra headers. 646 std::string()); // No extra headers.
1869 // TODO(dcblack): Remove extraneous history entry caused by 404s. 647 // TODO(dcblack): Remove extraneous history entry caused by 404s.
1870 // Note that the base case of a 204 being returned doesn't push a history 648 // Note that the base case of a 204 being returned doesn't push a history
1871 // entry. 649 // entry.
1872 } 650 }
1873 651
1874 void InstantController::PopulateInstantAutocompleteResultFromMatch(
1875 const AutocompleteMatch& match, size_t autocomplete_match_index,
1876 InstantAutocompleteResult* result) {
1877 DCHECK(result);
1878 result->provider = UTF8ToUTF16(match.provider->GetName());
1879 result->type = match.type;
1880 result->description = match.description;
1881 result->destination_url = UTF8ToUTF16(match.destination_url.spec());
1882
1883 // Setting the search_query field tells the Instant page to treat the
1884 // suggestion as a query.
1885 if (AutocompleteMatch::IsSearchType(match.type))
1886 result->search_query = match.contents;
1887
1888 result->transition = match.transition;
1889 result->relevance = match.relevance;
1890 result->autocomplete_match_index = autocomplete_match_index;
1891
1892 DVLOG(1) << " " << result->relevance << " "
1893 << UTF8ToUTF16(AutocompleteMatchType::ToString(result->type)) << " "
1894 << result->provider << " " << result->destination_url << " '"
1895 << result->description << "' '" << result->search_query << "' "
1896 << result->transition << " " << result->autocomplete_match_index;
1897 }
1898
1899 bool InstantController::IsJavascriptEnabled() const { 652 bool InstantController::IsJavascriptEnabled() const {
1900 GURL instant_url(GetInstantURL()); 653 GURL instant_url(GetInstantURL());
1901 GURL origin(instant_url.GetOrigin()); 654 GURL origin(instant_url.GetOrigin());
1902 ContentSetting js_setting = profile()->GetHostContentSettingsMap()-> 655 ContentSetting js_setting = profile()->GetHostContentSettingsMap()->
1903 GetContentSetting(origin, origin, CONTENT_SETTINGS_TYPE_JAVASCRIPT, 656 GetContentSetting(origin, origin, CONTENT_SETTINGS_TYPE_JAVASCRIPT,
1904 NO_RESOURCE_IDENTIFIER); 657 NO_RESOURCE_IDENTIFIER);
1905 // Javascript can be disabled either in content settings or via a WebKit 658 // Javascript can be disabled either in content settings or via a WebKit
1906 // preference, so check both. Disabling it through the Settings page affects 659 // preference, so check both. Disabling it through the Settings page affects
1907 // content settings. I'm not sure how to disable the WebKit preference, but 660 // content settings. I'm not sure how to disable the WebKit preference, but
1908 // it's theoretically possible some users have it off. 661 // it's theoretically possible some users have it off.
1909 bool js_content_enabled = 662 bool js_content_enabled =
1910 js_setting == CONTENT_SETTING_DEFAULT || 663 js_setting == CONTENT_SETTING_DEFAULT ||
1911 js_setting == CONTENT_SETTING_ALLOW; 664 js_setting == CONTENT_SETTING_ALLOW;
1912 bool js_webkit_enabled = profile()->GetPrefs()->GetBoolean( 665 bool js_webkit_enabled = profile()->GetPrefs()->GetBoolean(
1913 prefs::kWebKitJavascriptEnabled); 666 prefs::kWebKitJavascriptEnabled);
1914 return js_content_enabled && js_webkit_enabled; 667 return js_content_enabled && js_webkit_enabled;
1915 } 668 }
1916 669
1917 bool InstantController::InStartup() const { 670 bool InstantController::InStartup() const {
1918 // TODO(shishir): This is not completely reliable. Find a better way to detect 671 // TODO(shishir): This is not completely reliable. Find a better way to detect
1919 // startup time. 672 // startup time.
1920 return !browser_->GetActiveWebContents(); 673 return !browser_->GetActiveWebContents();
1921 } 674 }
1922 675
1923 InstantService* InstantController::GetInstantService() const { 676 InstantService* InstantController::GetInstantService() const {
1924 return InstantServiceFactory::GetForProfile(profile()); 677 return InstantServiceFactory::GetForProfile(profile());
1925 } 678 }
OLDNEW
« no previous file with comments | « chrome/browser/ui/search/instant_controller.h ('k') | chrome/browser/ui/search/instant_controller_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698