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