OLD | NEW |
---|---|
1 // Copyright 2012 The Chromium Authors. All rights reserved. | 1 // Copyright 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/instant/instant_controller.h" | 5 #include "chrome/browser/instant/instant_controller.h" |
6 | 6 |
7 #include "base/command_line.h" | 7 #include "base/command_line.h" |
8 #include "base/metrics/histogram.h" | 8 #include "base/metrics/histogram.h" |
9 #include "base/string_util.h" | 9 #include "base/string_util.h" |
10 #include "base/utf_string_conversions.h" | 10 #include "base/utf_string_conversions.h" |
11 #include "chrome/browser/autocomplete/autocomplete_provider.h" | 11 #include "chrome/browser/autocomplete/autocomplete_provider.h" |
12 #include "chrome/browser/google/google_util.h" | 12 #include "chrome/browser/google/google_util.h" |
13 #include "chrome/browser/history/history.h" | 13 #include "chrome/browser/history/history.h" |
14 #include "chrome/browser/history/history_service_factory.h" | 14 #include "chrome/browser/history/history_service_factory.h" |
15 #include "chrome/browser/history/history_tab_helper.h" | 15 #include "chrome/browser/history/history_tab_helper.h" |
16 #include "chrome/browser/instant/instant_loader.h" | 16 #include "chrome/browser/instant/instant_loader.h" |
17 #include "chrome/browser/instant/instant_tab.h" | |
17 #include "chrome/browser/platform_util.h" | 18 #include "chrome/browser/platform_util.h" |
18 #include "chrome/browser/search_engines/template_url_service.h" | 19 #include "chrome/browser/search_engines/template_url_service.h" |
19 #include "chrome/browser/search_engines/template_url_service_factory.h" | 20 #include "chrome/browser/search_engines/template_url_service_factory.h" |
20 #include "chrome/browser/ui/browser_instant_controller.h" | 21 #include "chrome/browser/ui/browser_instant_controller.h" |
21 #include "chrome/browser/ui/search/search_tab_helper.h" | 22 #include "chrome/browser/ui/search/search_tab_helper.h" |
22 #include "chrome/browser/ui/tab_contents/tab_contents.h" | |
23 #include "chrome/common/chrome_notification_types.h" | 23 #include "chrome/common/chrome_notification_types.h" |
24 #include "chrome/common/chrome_switches.h" | 24 #include "chrome/common/chrome_switches.h" |
25 #include "content/public/browser/navigation_entry.h" | 25 #include "content/public/browser/navigation_entry.h" |
26 #include "content/public/browser/notification_service.h" | 26 #include "content/public/browser/notification_service.h" |
27 #include "content/public/browser/render_widget_host_view.h" | 27 #include "content/public/browser/render_widget_host_view.h" |
28 #include "net/base/escape.h" | 28 #include "net/base/escape.h" |
29 #include "unicode/normalizer2.h" | 29 #include "unicode/normalizer2.h" |
30 | 30 |
31 #if defined(TOOLKIT_VIEWS) | 31 #if defined(TOOLKIT_VIEWS) |
32 #include "ui/views/widget/widget.h" | 32 #include "ui/views/widget/widget.h" |
33 #endif | 33 #endif |
34 | 34 |
35 namespace { | 35 namespace { |
36 | 36 |
37 // An artificial delay (in milliseconds) we introduce before telling the Instant | 37 // An artificial delay (in milliseconds) we introduce before telling the Instant |
38 // page about the new omnibox bounds, in cases where the bounds shrink. This is | 38 // page about the new omnibox bounds, in cases where the bounds shrink. This is |
39 // to avoid the page jumping up/down very fast in response to bounds changes. | 39 // to avoid the page jumping up/down very fast in response to bounds changes. |
40 const int kUpdateBoundsDelayMS = 1000; | 40 const int kUpdateBoundsDelayMS = 1000; |
41 | 41 |
42 // The maximum number of times we'll load a non-Instant-supporting search engine | 42 // The maximum number of times we'll load a non-Instant-supporting search engine |
43 // before we give up and blacklist it for the rest of the browsing session. | 43 // before we give up and blacklist it for the rest of the browsing session. |
44 const int kMaxInstantSupportFailures = 10; | 44 const int kMaxInstantSupportFailures = 10; |
45 | 45 |
46 // If an Instant page has not been used in these many milliseconds, it is | 46 // If an Instant page has not been used in these many milliseconds, it is |
47 // reloaded so that the page does not become stale. | 47 // reloaded so that the page does not become stale. |
48 const int kStaleLoaderTimeoutMS = 3 * 3600 * 1000; | 48 const int kStaleLoaderTimeoutMS = 3 * 3600 * 1000; |
49 | 49 |
50 void AddSessionStorageHistogram(bool extended_enabled, | 50 void AddSessionStorageHistogram(bool extended_enabled, |
51 const TabContents* tab1, | 51 const content::WebContents* tab1, |
52 const TabContents* tab2) { | 52 const content::WebContents* tab2) { |
53 base::Histogram* histogram = base::BooleanHistogram::FactoryGet( | 53 base::Histogram* histogram = base::BooleanHistogram::FactoryGet( |
54 std::string("Instant.SessionStorageNamespace") + | 54 std::string("Instant.SessionStorageNamespace") + |
55 (extended_enabled ? "_Extended" : "_Instant"), | 55 (extended_enabled ? "_Extended" : "_Instant"), |
56 base::Histogram::kUmaTargetedHistogramFlag); | 56 base::Histogram::kUmaTargetedHistogramFlag); |
57 const content::SessionStorageNamespaceMap& session_storage_map1 = | 57 const content::SessionStorageNamespaceMap& session_storage_map1 = |
58 tab1->web_contents()->GetController().GetSessionStorageNamespaceMap(); | 58 tab1->GetController().GetSessionStorageNamespaceMap(); |
59 const content::SessionStorageNamespaceMap& session_storage_map2 = | 59 const content::SessionStorageNamespaceMap& session_storage_map2 = |
60 tab2->web_contents()->GetController().GetSessionStorageNamespaceMap(); | 60 tab2->GetController().GetSessionStorageNamespaceMap(); |
61 bool is_session_storage_the_same = | 61 bool is_session_storage_the_same = |
62 session_storage_map1.size() == session_storage_map2.size(); | 62 session_storage_map1.size() == session_storage_map2.size(); |
63 if (is_session_storage_the_same) { | 63 if (is_session_storage_the_same) { |
64 // The size is the same, so let's check that all entries match. | 64 // The size is the same, so let's check that all entries match. |
65 for (content::SessionStorageNamespaceMap::const_iterator | 65 for (content::SessionStorageNamespaceMap::const_iterator |
66 it1 = session_storage_map1.begin(), | 66 it1 = session_storage_map1.begin(), |
67 it2 = session_storage_map2.begin(); | 67 it2 = session_storage_map2.begin(); |
68 it1 != session_storage_map1.end() && it2 != session_storage_map2.end(); | 68 it1 != session_storage_map1.end() && it2 != session_storage_map2.end(); |
69 ++it1, ++it2) { | 69 ++it1, ++it2) { |
70 if (it1->first != it2->first || it1->second != it2->second) { | 70 if (it1->first != it2->first || it1->second != it2->second) { |
(...skipping 12 matching lines...) Expand all Loading... | |
83 if (normalizer == NULL || U_FAILURE(status)) | 83 if (normalizer == NULL || U_FAILURE(status)) |
84 return str; | 84 return str; |
85 icu::UnicodeString norm_str(normalizer->normalize( | 85 icu::UnicodeString norm_str(normalizer->normalize( |
86 icu::UnicodeString(FALSE, str.c_str(), str.size()), status)); | 86 icu::UnicodeString(FALSE, str.c_str(), str.size()), status)); |
87 if (U_FAILURE(status)) | 87 if (U_FAILURE(status)) |
88 return str; | 88 return str; |
89 return string16(norm_str.getBuffer(), norm_str.length()); | 89 return string16(norm_str.getBuffer(), norm_str.length()); |
90 } | 90 } |
91 | 91 |
92 bool NormalizeAndStripPrefix(string16* text, const string16& prefix) { | 92 bool NormalizeAndStripPrefix(string16* text, const string16& prefix) { |
93 const string16 norm_prefix = Normalize(prefix); | 93 string16 norm_prefix = Normalize(prefix); |
94 string16 norm_text = Normalize(*text); | 94 string16 norm_text = Normalize(*text); |
95 if (norm_prefix.size() <= norm_text.size() && | 95 if (norm_prefix.size() <= norm_text.size() && |
96 norm_text.compare(0, norm_prefix.size(), norm_prefix) == 0) { | 96 norm_text.compare(0, norm_prefix.size(), norm_prefix) == 0) { |
97 *text = norm_text.erase(0, norm_prefix.size()); | 97 *text = norm_text.erase(0, norm_prefix.size()); |
98 return true; | 98 return true; |
99 } | 99 } |
100 return false; | 100 return false; |
101 } | 101 } |
102 | 102 |
103 // For TOOLKIT_VIEWS, the top level widget is always focused. If the focus | 103 // For TOOLKIT_VIEWS, the top level widget is always focused. If the focus |
104 // change originated in views determine the child Widget from the view that is | 104 // change originated in views determine the child Widget from the view that is |
105 // being focused. | 105 // being focused. |
106 gfx::NativeView GetViewGainingFocus(gfx::NativeView view_gaining_focus) { | 106 gfx::NativeView GetViewGainingFocus(gfx::NativeView view_gaining_focus) { |
107 #if defined(TOOLKIT_VIEWS) | 107 #if defined(TOOLKIT_VIEWS) |
108 views::Widget* widget = view_gaining_focus ? | 108 views::Widget* widget = view_gaining_focus ? |
109 views::Widget::GetWidgetForNativeView(view_gaining_focus) : NULL; | 109 views::Widget::GetWidgetForNativeView(view_gaining_focus) : NULL; |
110 if (widget) { | 110 if (widget) { |
111 views::FocusManager* focus_manager = widget->GetFocusManager(); | 111 views::FocusManager* focus_manager = widget->GetFocusManager(); |
112 if (focus_manager && focus_manager->is_changing_focus() && | 112 if (focus_manager && focus_manager->is_changing_focus() && |
113 focus_manager->GetFocusedView() && | 113 focus_manager->GetFocusedView() && |
114 focus_manager->GetFocusedView()->GetWidget()) { | 114 focus_manager->GetFocusedView()->GetWidget()) |
115 return focus_manager->GetFocusedView()->GetWidget()->GetNativeView(); | 115 return focus_manager->GetFocusedView()->GetWidget()->GetNativeView(); |
116 } | |
117 } | 116 } |
118 #endif | 117 #endif |
119 return view_gaining_focus; | 118 return view_gaining_focus; |
120 } | 119 } |
121 | 120 |
122 // Returns true if |view| is the top-level contents view or a child view in the | 121 // Returns true if |view| is the top-level contents view or a child view in the |
123 // view hierarchy of |contents|. | 122 // view hierarchy of |contents|. |
124 bool IsViewInContents(gfx::NativeView view, content::WebContents* contents) { | 123 bool IsViewInContents(gfx::NativeView view, content::WebContents* contents) { |
125 content::RenderWidgetHostView* rwhv = contents->GetRenderWidgetHostView(); | 124 content::RenderWidgetHostView* rwhv = contents->GetRenderWidgetHostView(); |
126 if (!view || !rwhv) | 125 if (!view || !rwhv) |
(...skipping 15 matching lines...) Expand all Loading... | |
142 } | 141 } |
143 | 142 |
144 } // namespace | 143 } // namespace |
145 | 144 |
146 InstantController::InstantController(chrome::BrowserInstantController* browser, | 145 InstantController::InstantController(chrome::BrowserInstantController* browser, |
147 bool extended_enabled) | 146 bool extended_enabled) |
148 : browser_(browser), | 147 : browser_(browser), |
149 extended_enabled_(extended_enabled), | 148 extended_enabled_(extended_enabled), |
150 instant_enabled_(false), | 149 instant_enabled_(false), |
151 model_(ALLOW_THIS_IN_INITIALIZER_LIST(this)), | 150 model_(ALLOW_THIS_IN_INITIALIZER_LIST(this)), |
151 last_omnibox_text_has_inline_autocompletion_(false), | |
152 last_verbatim_(false), | 152 last_verbatim_(false), |
153 last_transition_type_(content::PAGE_TRANSITION_LINK), | 153 last_transition_type_(content::PAGE_TRANSITION_LINK), |
154 last_match_was_search_(false), | 154 last_match_was_search_(false), |
155 is_omnibox_focused_(false) { | 155 is_omnibox_focused_(false), |
156 allow_preview_to_show_search_suggestions_(false) { | |
156 } | 157 } |
157 | 158 |
158 InstantController::~InstantController() { | 159 InstantController::~InstantController() { |
159 } | 160 } |
160 | 161 |
161 bool InstantController::Update(const AutocompleteMatch& match, | 162 bool InstantController::Update(const AutocompleteMatch& match, |
162 const string16& user_text, | 163 const string16& user_text, |
163 const string16& full_text, | 164 const string16& full_text, |
164 const bool verbatim, | 165 size_t selection_start, |
165 const bool user_input_in_progress, | 166 size_t selection_end, |
166 const bool omnibox_popup_is_open) { | 167 bool verbatim, |
168 bool user_input_in_progress, | |
169 bool omnibox_popup_is_open) { | |
167 if (!extended_enabled_ && !instant_enabled_) | 170 if (!extended_enabled_ && !instant_enabled_) |
168 return false; | 171 return false; |
169 | 172 |
170 DVLOG(1) << "Update: " << AutocompleteMatch::TypeToString(match.type) | 173 DVLOG(1) << "Update: " << AutocompleteMatch::TypeToString(match.type) |
171 << " user_text='" << user_text << "' full_text='" << full_text << "'" | 174 << " user_text='" << user_text << "' full_text='" << full_text << "'" |
172 << " verbatim=" << verbatim << " typing=" << user_input_in_progress | 175 << " selection_start=" << selection_start << " selection_end=" |
173 << " popup=" << omnibox_popup_is_open; | 176 << selection_end << " verbatim=" << verbatim << " typing=" |
177 << user_input_in_progress << " popup=" << omnibox_popup_is_open; | |
174 | 178 |
175 // If the popup is open, the user has to be typing. | 179 // If the popup is open, the user has to be typing. |
176 DCHECK(!omnibox_popup_is_open || user_input_in_progress); | 180 DCHECK(!omnibox_popup_is_open || user_input_in_progress); |
177 | 181 |
178 // If the popup is closed, there should be no inline autocompletion. | 182 // If the popup is closed, there should be no inline autocompletion. |
179 DCHECK(omnibox_popup_is_open || user_text == full_text) << user_text << "|" | 183 DCHECK(omnibox_popup_is_open || user_text.empty() || user_text == full_text) |
180 << full_text; | 184 << user_text << "|" << full_text; |
181 | |
182 // If there's inline autocompletion, the query has to be verbatim. | |
183 DCHECK(user_text == full_text || verbatim) << user_text << "|" << full_text; | |
184 | 185 |
185 // If there's no text in the omnibox, the user can't have typed any. | 186 // If there's no text in the omnibox, the user can't have typed any. |
186 DCHECK(!full_text.empty() || user_text.empty()) << user_text; | 187 DCHECK(!full_text.empty() || user_text.empty()) << user_text; |
187 | 188 |
188 // If the user isn't typing, and the popup is closed, there can't be any | 189 // If the user isn't typing, and the popup is closed, there can't be any |
189 // user-typed text. | 190 // user-typed text. |
190 DCHECK(user_input_in_progress || omnibox_popup_is_open || user_text.empty()) | 191 DCHECK(user_input_in_progress || omnibox_popup_is_open || user_text.empty()) |
191 << user_text; | 192 << user_text; |
192 | 193 |
193 // In non-extended mode, SearchModeChanged() is never called, so fake it. The | 194 // In non-extended mode, SearchModeChanged() is never called, so fake it. The |
194 // mode is set to "disallow suggestions" here, so that if one of the early | 195 // mode is set to "disallow suggestions" here, so that if one of the early |
195 // "return false" conditions is hit, suggestions will be disallowed. If the | 196 // "return false" conditions is hit, suggestions will be disallowed. If the |
196 // query is sent to the loader, the mode is set to "allow" further below. | 197 // query is sent to the loader, the mode is set to "allow" further below. |
197 if (!extended_enabled_) | 198 if (!extended_enabled_) |
198 search_mode_.mode = chrome::search::Mode::MODE_DEFAULT; | 199 search_mode_.mode = chrome::search::Mode::MODE_DEFAULT; |
199 | 200 |
200 // If there's no active tab, the browser is closing. | 201 last_match_was_search_ = AutocompleteMatch::IsSearchType(match.type) && |
201 const TabContents* const active_tab = browser_->GetActiveTabContents(); | 202 !user_text.empty(); |
202 if (!active_tab) { | 203 |
203 Hide(true); | 204 if (!ResetLoaderForMatch(match)) { |
205 HideLoader(); | |
204 return false; | 206 return false; |
205 } | 207 } |
206 | 208 |
207 // Legend: OPIO == |omnibox_popup_is_open|, UIIP = |user_input_in_progress|. | |
208 // | |
209 // # OPIO UIIP full_text Notes | |
210 // - ---- ---- --------- ----- | |
211 // 1 no no blank } Navigation, or user hit Escape. |full_text| is | |
212 // 2 no no non-blank } blank if the page is NTP, non-blank otherwise. | |
213 // | |
214 // 3 no yes blank User backspaced away all omnibox text. | |
215 // | |
216 // 4 no yes non-blank User switched to a tab with a partial query. | |
217 // | |
218 // 5 yes no blank } Impossible. DCHECK()ed above. | |
219 // 6 yes no non-blank } | |
220 // | |
221 // 7 yes yes blank User typed a "?" into the omnibox. | |
222 // | |
223 // 8 yes yes non-blank User typed text into the omnibox. | |
224 // | |
225 // In non-extended mode, #1 to #7 call Hide(). #8 calls loader_->Update(). | |
226 // | |
227 // In extended mode, #2 and #4 call Hide(). #1 doesn't Hide() as the preview | |
228 // may be showing custom NTP content, but doesn't Update() either. #3 calls | |
229 // Hide() unless on the NTP _and_ sends a blank query; otherwise #3 and #7 | |
230 // don't Hide(), but send a blank query to Update(). #8 calls Update(). | |
231 | |
232 if (extended_enabled_) { | 209 if (extended_enabled_) { |
233 if (!omnibox_popup_is_open) { | 210 if (!omnibox_popup_is_open) { |
234 if (!full_text.empty() || | 211 if (!user_input_in_progress) { |
235 (user_input_in_progress && !search_mode_.is_origin_ntp())) { | 212 // If the user isn't typing and the omnibox popup is closed, it means a |
236 Hide(true); | 213 // regular navigation, tab-switch or the user hitting Escape. |
237 return false; | 214 if (instant_tab_) { |
215 // The user is on a search results page. It may be showing results for | |
216 // a partial query the user typed before they hit Escape. Send the | |
217 // omnibox text to the page to restore the original results. | |
218 // | |
219 // In a tab switch, |instant_tab_| won't have updated yet, so it may | |
220 // be pointing to the previous tab (which was a search results page). | |
221 // Ensure we don't send the omnibox text to a random webpage (the new | |
222 // tab), by comparing the old and new WebContents. | |
223 if (instant_tab_->contents() == browser_->GetActiveWebContents()) | |
224 instant_tab_->Submit(full_text); | |
225 } else if (!full_text.empty()) { | |
226 // If |full_text| is empty, the user is on the NTP. The preview may | |
227 // be showing custom NTP content; hide only if that's not the case. | |
228 HideLoader(); | |
229 } | |
230 } else if (full_text.empty()) { | |
231 // The user is typing, and backspaced away all omnibox text. Clear | |
232 // |last_omnibox_text_| so that we don't attempt to set suggestions. | |
233 last_omnibox_text_.clear(); | |
234 last_suggestion_ = InstantSuggestion(); | |
235 if (instant_tab_) { | |
236 // On a search results page, tell it to clear old results. | |
237 instant_tab_->Update(string16(), 0, 0, true); | |
238 } else if (search_mode_.is_origin_ntp()) { | |
239 // On the NTP, tell the preview to clear old results. Don't hide the | |
240 // preview so it can show a blank page or logo if it wants. | |
241 loader_->Update(string16(), 0, 0, true); | |
242 } else { | |
243 HideLoader(); | |
244 } | |
245 } else { | |
246 // The user switched to a tab with partial text already in the omnibox. | |
247 HideLoader(); | |
248 | |
249 // The new tab may or may not be a search results page; we don't know | |
250 // since SearchModeChanged() hasn't been called yet. If it later turns | |
251 // out to be, we should store |full_text| now, so that if the user hits | |
252 // Enter, we'll send the correct query to instant_tab_->Submit(). If the | |
253 // partial text is not a query (|last_match_was_search_| is false), we | |
254 // won't Submit(), so no need to worry about that. | |
255 last_omnibox_text_ = full_text; | |
256 last_suggestion_ = InstantSuggestion(); | |
238 } | 257 } |
239 if (!user_input_in_progress && full_text.empty()) | 258 return false; |
240 return false; | 259 } else if (full_text.empty()) { |
260 // The user typed a solitary "?". Same as the backspace case above. | |
261 last_omnibox_text_.clear(); | |
262 last_suggestion_ = InstantSuggestion(); | |
263 if (instant_tab_) | |
264 instant_tab_->Update(string16(), 0, 0, true); | |
265 else if (search_mode_.is_origin_ntp()) | |
266 loader_->Update(string16(), 0, 0, true); | |
267 else | |
268 HideLoader(); | |
269 return false; | |
241 } | 270 } |
242 } else if (!omnibox_popup_is_open || full_text.empty()) { | 271 } else if (!omnibox_popup_is_open || full_text.empty()) { |
243 // Update() can be called if the user clicks the preview while composing | 272 // In the non-extended case, hide the preview as long as the user isn't |
244 // text with an IME. If so, we should commit on mouse up, so don't Hide(). | 273 // actively typing a non-empty query. However, Update() can be called if the |
245 if (!GetPreviewContents() || !loader_->IsPointerDownFromActivate()) | 274 // user clicks the preview while composing text with an IME, so don't hide |
246 Hide(true); | 275 // if the mouse is down, since we'll commit on mouse up later. |
276 if (!loader_ || !loader_->is_pointer_down_from_activate()) | |
277 HideLoader(); | |
247 return false; | 278 return false; |
248 } | 279 } |
249 | 280 |
250 // Ensure we have a loader that can process this match. First, try to use the | 281 last_omnibox_text_has_inline_autocompletion_ = user_text != full_text; |
251 // TemplateURL of the |match|. If that's invalid, in non-extended mode, stop. | |
252 // In extended mode, try using the default search engine, but only when the | |
253 // match is for a URL (i.e., not some other kind of non-Instant search). | |
254 // A completely blank query shows up as a search, and we do want to allow | |
255 // that, hence the "!full_text.empty()" clause. | |
256 Profile* const profile = active_tab->profile(); | |
257 const bool match_is_search = AutocompleteMatch::IsSearchType(match.type); | |
258 if (!ResetLoader(match.GetTemplateURL(profile, false), active_tab) && | |
259 (!extended_enabled_ || (match_is_search && !full_text.empty()) || | |
260 !CreateDefaultLoader())) { | |
261 Hide(true); | |
262 return false; | |
263 } | |
264 | 282 |
265 // If the user continues typing the same query as the suggested text is | 283 // If the user continues typing the same query as the suggested text is |
266 // showing, reuse the suggestion (but only for INSTANT_COMPLETE_NEVER). | 284 // showing, reuse the suggestion (but only for INSTANT_COMPLETE_NEVER). |
267 bool reused_suggestion = false; | 285 bool reused_suggestion = false; |
268 if (last_suggestion_.behavior == INSTANT_COMPLETE_NEVER) { | 286 if (last_suggestion_.behavior == INSTANT_COMPLETE_NEVER && |
269 if (StartsWith(last_user_text_, user_text, false) && !user_text.empty()) { | 287 !last_omnibox_text_has_inline_autocompletion_) { |
288 if (StartsWith(last_omnibox_text_, full_text, false)) { | |
270 // The user is backspacing away characters. | 289 // The user is backspacing away characters. |
271 last_suggestion_.text.insert(0, last_user_text_, user_text.size(), | 290 last_suggestion_.text.insert(0, last_omnibox_text_, full_text.size(), |
272 last_user_text_.size() - user_text.size()); | 291 last_omnibox_text_.size() - full_text.size()); |
273 reused_suggestion = true; | 292 reused_suggestion = true; |
274 } else if (StartsWith(user_text, last_user_text_, false)) { | 293 } else if (StartsWith(full_text, last_omnibox_text_, false)) { |
275 // The user is typing forward. Normalize any added characters. | 294 // The user is typing forward. Normalize any added characters. |
276 reused_suggestion = NormalizeAndStripPrefix(&last_suggestion_.text, | 295 reused_suggestion = NormalizeAndStripPrefix(&last_suggestion_.text, |
277 string16(user_text, last_user_text_.size())); | 296 string16(full_text, last_omnibox_text_.size())); |
278 } | 297 } |
279 } | 298 } |
280 | |
281 last_user_text_ = user_text; | |
282 last_full_text_ = full_text; | |
283 last_verbatim_ = verbatim; | |
284 | |
285 if (!reused_suggestion) | 299 if (!reused_suggestion) |
286 last_suggestion_ = InstantSuggestion(); | 300 last_suggestion_ = InstantSuggestion(); |
287 | 301 |
302 last_omnibox_text_ = full_text; | |
303 | |
304 if (!extended_enabled_) { | |
305 // In non-extended mode, the query is verbatim if there's any selection | |
306 // (including inline autocompletion) or if the cursor is not at the end. | |
307 verbatim = verbatim || selection_start != selection_end || | |
308 selection_start != full_text.size(); | |
309 } | |
310 last_verbatim_ = verbatim; | |
311 | |
288 last_transition_type_ = match.transition; | 312 last_transition_type_ = match.transition; |
289 last_match_was_search_ = match_is_search; | |
290 url_for_history_ = match.destination_url; | 313 url_for_history_ = match.destination_url; |
291 | 314 |
292 // Store the first interaction time for use with latency histograms. | |
293 if (first_interaction_time_.is_null()) | |
294 first_interaction_time_ = base::Time::Now(); | |
295 | |
296 // Allow search suggestions. In extended mode, SearchModeChanged() will set | 315 // Allow search suggestions. In extended mode, SearchModeChanged() will set |
297 // this, but it's not called in non-extended mode, so fake it. | 316 // this, but it's not called in non-extended mode, so fake it. |
298 if (!extended_enabled_) | 317 if (!extended_enabled_) |
299 search_mode_.mode = chrome::search::Mode::MODE_SEARCH_SUGGESTIONS; | 318 search_mode_.mode = chrome::search::Mode::MODE_SEARCH_SUGGESTIONS; |
300 | 319 |
301 loader_->Update(extended_enabled_ ? user_text : full_text, verbatim); | 320 if (instant_tab_) { |
321 instant_tab_->Update(user_text, selection_start, selection_end, verbatim); | |
322 } else { | |
323 if (first_interaction_time_.is_null()) | |
324 first_interaction_time_ = base::Time::Now(); | |
325 allow_preview_to_show_search_suggestions_ = true; | |
326 loader_->Update(extended_enabled_ ? user_text : full_text, | |
327 selection_start, selection_end, verbatim); | |
328 } | |
302 | 329 |
303 content::NotificationService::current()->Notify( | 330 content::NotificationService::current()->Notify( |
304 chrome::NOTIFICATION_INSTANT_CONTROLLER_UPDATED, | 331 chrome::NOTIFICATION_INSTANT_CONTROLLER_UPDATED, |
305 content::Source<InstantController>(this), | 332 content::Source<InstantController>(this), |
306 content::NotificationService::NoDetails()); | 333 content::NotificationService::NoDetails()); |
307 | 334 |
308 // We don't have new suggestions yet, but we can either reuse the existing | 335 // We don't have new suggestions yet, but we can either reuse the existing |
309 // suggestion or reset the existing "gray text". | 336 // suggestion or reset the existing "gray text". |
310 browser_->SetInstantSuggestion(last_suggestion_); | 337 browser_->SetInstantSuggestion(last_suggestion_); |
311 | 338 |
312 // Though we may have handled a URL match above, we return false here, so that | 339 // Though we may have handled a URL match above, we return false here, so that |
313 // omnibox prerendering can kick in. TODO(sreeram): Remove this (and always | 340 // omnibox prerendering can kick in. TODO(sreeram): Remove this (and always |
314 // return true) once we are able to commit URLs as well. | 341 // return true) once we are able to commit URLs as well. |
315 return match_is_search; | 342 return last_match_was_search_; |
316 } | 343 } |
317 | 344 |
318 // TODO(tonyg): This method only fires when the omnibox bounds change. It also | 345 // TODO(tonyg): This method only fires when the omnibox bounds change. It also |
319 // needs to fire when the preview bounds change (e.g.: open/close info bar). | 346 // needs to fire when the preview bounds change (e.g.: open/close info bar). |
320 void InstantController::SetOmniboxBounds(const gfx::Rect& bounds) { | 347 void InstantController::SetOmniboxBounds(const gfx::Rect& bounds) { |
321 if (!extended_enabled_ && !instant_enabled_) | 348 if (!extended_enabled_ && !instant_enabled_) |
322 return; | 349 return; |
323 | 350 |
324 if (omnibox_bounds_ == bounds) | 351 if (omnibox_bounds_ == bounds) |
325 return; | 352 return; |
326 | 353 |
327 omnibox_bounds_ = bounds; | 354 omnibox_bounds_ = bounds; |
328 if (omnibox_bounds_.height() > last_omnibox_bounds_.height()) { | 355 if (omnibox_bounds_.height() > last_omnibox_bounds_.height()) { |
329 update_bounds_timer_.Stop(); | 356 update_bounds_timer_.Stop(); |
330 SendBoundsToPage(); | 357 SendBoundsToPage(); |
331 } else if (!update_bounds_timer_.IsRunning()) { | 358 } else if (!update_bounds_timer_.IsRunning()) { |
332 update_bounds_timer_.Start(FROM_HERE, | 359 update_bounds_timer_.Start(FROM_HERE, |
333 base::TimeDelta::FromMilliseconds(kUpdateBoundsDelayMS), this, | 360 base::TimeDelta::FromMilliseconds(kUpdateBoundsDelayMS), this, |
334 &InstantController::SendBoundsToPage); | 361 &InstantController::SendBoundsToPage); |
335 } | 362 } |
336 } | 363 } |
337 | 364 |
338 void InstantController::HandleAutocompleteResults( | 365 void InstantController::HandleAutocompleteResults( |
339 const std::vector<AutocompleteProvider*>& providers) { | 366 const std::vector<AutocompleteProvider*>& providers) { |
340 if (!extended_enabled_) | 367 if (!extended_enabled_) |
341 return; | 368 return; |
342 | 369 |
343 if (!GetPreviewContents()) | 370 if (!instant_tab_ && !loader_) |
344 return; | 371 return; |
345 | 372 |
346 DVLOG(1) << "AutocompleteResults:"; | 373 DVLOG(1) << "AutocompleteResults:"; |
347 std::vector<InstantAutocompleteResult> results; | 374 std::vector<InstantAutocompleteResult> results; |
348 for (ACProviders::const_iterator provider = providers.begin(); | 375 for (ACProviders::const_iterator provider = providers.begin(); |
349 provider != providers.end(); ++provider) { | 376 provider != providers.end(); ++provider) { |
350 for (ACMatches::const_iterator match = (*provider)->matches().begin(); | 377 for (ACMatches::const_iterator match = (*provider)->matches().begin(); |
351 match != (*provider)->matches().end(); ++match) { | 378 match != (*provider)->matches().end(); ++match) { |
352 InstantAutocompleteResult result; | 379 InstantAutocompleteResult result; |
353 result.provider = UTF8ToUTF16((*provider)->GetName()); | 380 result.provider = UTF8ToUTF16((*provider)->GetName()); |
354 result.type = UTF8ToUTF16(AutocompleteMatch::TypeToString(match->type)); | 381 result.type = UTF8ToUTF16(AutocompleteMatch::TypeToString(match->type)); |
355 result.description = match->description; | 382 result.description = match->description; |
356 result.destination_url = UTF8ToUTF16(match->destination_url.spec()); | 383 result.destination_url = UTF8ToUTF16(match->destination_url.spec()); |
357 result.relevance = match->relevance; | 384 result.relevance = match->relevance; |
358 DVLOG(1) << " " << result.relevance << " " << result.type << " " | 385 DVLOG(1) << " " << result.relevance << " " << result.type << " " |
359 << result.provider << " " << result.destination_url << " '" | 386 << result.provider << " " << result.destination_url << " '" |
360 << result.description << "'"; | 387 << result.description << "'"; |
361 results.push_back(result); | 388 results.push_back(result); |
362 } | 389 } |
363 } | 390 } |
364 | 391 |
365 loader_->SendAutocompleteResults(results); | 392 if (instant_tab_) |
393 instant_tab_->SendAutocompleteResults(results); | |
394 else | |
395 loader_->SendAutocompleteResults(results); | |
366 } | 396 } |
367 | 397 |
368 bool InstantController::OnUpOrDownKeyPressed(int count) { | 398 bool InstantController::OnUpOrDownKeyPressed(int count) { |
369 if (!extended_enabled_) | 399 if (!extended_enabled_) |
370 return false; | 400 return false; |
371 | 401 |
372 if (!GetPreviewContents()) | 402 if (!instant_tab_ && !loader_) |
373 return false; | 403 return false; |
374 | 404 |
375 loader_->OnUpOrDownKeyPressed(count); | 405 if (instant_tab_) |
406 instant_tab_->UpOrDownKeyPressed(count); | |
407 else | |
408 loader_->UpOrDownKeyPressed(count); | |
409 | |
376 return true; | 410 return true; |
377 } | 411 } |
378 | 412 |
379 TabContents* InstantController::GetPreviewContents() const { | 413 content::WebContents* InstantController::GetPreviewContents() const { |
380 return loader_ ? loader_->preview_contents() : NULL; | 414 return loader_ ? loader_->contents() : NULL; |
381 } | 415 } |
382 | 416 |
383 bool InstantController::IsCurrent() const { | 417 bool InstantController::IsCurrent() const { |
384 return model_.mode().is_search_suggestions() && last_match_was_search_; | 418 return model_.mode().is_search_suggestions() && last_match_was_search_; |
385 } | 419 } |
386 | 420 |
387 bool InstantController::CommitIfCurrent(InstantCommitType type) { | 421 bool InstantController::CommitIfCurrent(InstantCommitType type) { |
388 if (!extended_enabled_ && !instant_enabled_) | 422 if (!extended_enabled_ && !instant_enabled_) |
389 return false; | 423 return false; |
390 | 424 |
425 DVLOG(1) << "CommitIfCurrent: type=" << type << " last_omnibox_text_='" | |
426 << last_omnibox_text_ << "' last_match_was_search_=" | |
427 << last_match_was_search_ << " instant_tab_=" << instant_tab_; | |
428 | |
429 // If we are on an already committed search results page, send a submit event | |
430 // to the page, but otherwise, nothing else to do. | |
431 if (instant_tab_) { | |
432 if (last_match_was_search_ && type == INSTANT_COMMIT_PRESSED_ENTER) { | |
433 instant_tab_->Submit(last_omnibox_text_); | |
434 return true; | |
435 } | |
436 return false; | |
437 } | |
438 | |
391 if (!IsCurrent()) | 439 if (!IsCurrent()) |
392 return false; | 440 return false; |
393 | 441 |
394 DVLOG(1) << "CommitIfCurrent"; | 442 if (type == INSTANT_COMMIT_FOCUS_LOST) |
395 TabContents* preview = loader_->ReleasePreviewContents(type, last_full_text_); | 443 loader_->Cancel(last_omnibox_text_); |
444 else | |
445 loader_->Submit(last_omnibox_text_); | |
446 | |
447 content::WebContents* preview = loader_->ReleaseContents(); | |
396 | 448 |
397 if (extended_enabled_) { | 449 if (extended_enabled_) { |
398 // Consider what's happening: | 450 // Consider what's happening: |
399 // 1. The user has typed a query in the omnibox and committed it (either | 451 // 1. The user has typed a query in the omnibox and committed it (either |
400 // by pressing Enter or clicking on the preview). | 452 // by pressing Enter or clicking on the preview). |
401 // 2. We commit the preview to the tab strip, and tell the page. | 453 // 2. We commit the preview to the tab strip, and tell the page. |
402 // 3. The page will update the URL hash fragment with the query terms. | 454 // 3. The page will update the URL hash fragment with the query terms. |
403 // After steps 1 and 3, the omnibox will show the query terms. However, if | 455 // After steps 1 and 3, the omnibox will show the query terms. However, if |
404 // the URL we are committing at step 2 doesn't already have query terms, it | 456 // the URL we are committing at step 2 doesn't already have query terms, it |
405 // will flash for a brief moment as a plain URL. So, avoid that flicker by | 457 // will flash for a brief moment as a plain URL. So, avoid that flicker by |
406 // pretending that the plain URL is actually the typed query terms. | 458 // pretending that the plain URL is actually the typed query terms. |
407 // TODO(samarth,beaudoin): Instead of this hack, we should add a new field | 459 // TODO(samarth,beaudoin): Instead of this hack, we should add a new field |
408 // to NavigationEntry to keep track of what the correct query, if any, is. | 460 // to NavigationEntry to keep track of what the correct query, if any, is. |
409 content::NavigationEntry* entry = | 461 content::NavigationEntry* entry = |
410 preview->web_contents()->GetController().GetVisibleEntry(); | 462 preview->GetController().GetVisibleEntry(); |
411 std::string url = entry->GetVirtualURL().spec(); | 463 std::string url = entry->GetVirtualURL().spec(); |
412 if (!google_util::IsInstantExtendedAPIGoogleSearchUrl(url) && | 464 if (!google_util::IsInstantExtendedAPIGoogleSearchUrl(url) && |
413 google_util::IsGoogleDomainUrl(url, google_util::ALLOW_SUBDOMAIN, | 465 google_util::IsGoogleDomainUrl(url, google_util::ALLOW_SUBDOMAIN, |
414 google_util::ALLOW_NON_STANDARD_PORTS)) { | 466 google_util::ALLOW_NON_STANDARD_PORTS)) { |
415 entry->SetVirtualURL(GURL( | 467 entry->SetVirtualURL(GURL( |
416 url + "#q=" + | 468 url + "#q=" + |
417 net::EscapeQueryParamValue(UTF16ToUTF8(last_full_text_), true))); | 469 net::EscapeQueryParamValue(UTF16ToUTF8(last_omnibox_text_), true))); |
418 chrome::search::SearchTabHelper::FromWebContents( | 470 chrome::search::SearchTabHelper::FromWebContents(preview)-> |
419 preview->web_contents())->NavigationEntryUpdated(); | 471 NavigationEntryUpdated(); |
420 } | 472 } |
421 } | 473 } |
422 | 474 |
423 // If the preview page has navigated since the last Update(), we need to add | 475 // If the preview page has navigated since the last Update(), we need to add |
424 // the navigation to history ourselves. Else, the page will navigate after | 476 // the navigation to history ourselves. Else, the page will navigate after |
425 // commit, and it will be added to history in the usual manner. | 477 // commit, and it will be added to history in the usual manner. |
426 const history::HistoryAddPageArgs& last_navigation = | 478 const history::HistoryAddPageArgs& last_navigation = |
427 loader_->last_navigation(); | 479 loader_->last_navigation(); |
428 if (!last_navigation.url.is_empty()) { | 480 if (!last_navigation.url.is_empty()) { |
429 content::NavigationEntry* entry = | 481 content::NavigationEntry* entry = preview->GetController().GetActiveEntry(); |
430 preview->web_contents()->GetController().GetActiveEntry(); | |
431 DCHECK_EQ(last_navigation.url, entry->GetURL()); | 482 DCHECK_EQ(last_navigation.url, entry->GetURL()); |
432 | 483 |
433 // Add the page to history. | 484 // Add the page to history. |
434 HistoryTabHelper* history_tab_helper = | 485 HistoryTabHelper* history_tab_helper = |
435 HistoryTabHelper::FromWebContents(preview->web_contents()); | 486 HistoryTabHelper::FromWebContents(preview); |
436 history_tab_helper->UpdateHistoryForNavigation(last_navigation); | 487 history_tab_helper->UpdateHistoryForNavigation(last_navigation); |
437 | 488 |
438 // Update the page title. | 489 // Update the page title. |
439 history_tab_helper->UpdateHistoryPageTitle(*entry); | 490 history_tab_helper->UpdateHistoryPageTitle(*entry); |
440 } | 491 } |
441 | 492 |
442 // Add a fake history entry with a non-Instant search URL, so that search | 493 // Add a fake history entry with a non-Instant search URL, so that search |
443 // terms extraction (for autocomplete history matches) works. | 494 // terms extraction (for autocomplete history matches) works. |
444 if (HistoryService* history = HistoryServiceFactory::GetForProfile( | 495 HistoryService* history = HistoryServiceFactory::GetForProfile( |
445 preview->profile(), Profile::EXPLICIT_ACCESS)) { | 496 Profile::FromBrowserContext(preview->GetBrowserContext()), |
497 Profile::EXPLICIT_ACCESS); | |
498 if (history) { | |
446 history->AddPage(url_for_history_, base::Time::Now(), NULL, 0, GURL(), | 499 history->AddPage(url_for_history_, base::Time::Now(), NULL, 0, GURL(), |
447 history::RedirectList(), last_transition_type_, | 500 history::RedirectList(), last_transition_type_, |
448 history::SOURCE_BROWSED, false); | 501 history::SOURCE_BROWSED, false); |
449 } | 502 } |
450 | 503 |
451 preview->web_contents()->GetController().PruneAllButActive(); | 504 preview->GetController().PruneAllButActive(); |
452 | 505 |
453 if (type != INSTANT_COMMIT_PRESSED_ALT_ENTER) { | 506 if (type != INSTANT_COMMIT_PRESSED_ALT_ENTER) { |
454 const TabContents* active_tab = browser_->GetActiveTabContents(); | 507 content::WebContents* active_tab = browser_->GetActiveWebContents(); |
455 AddSessionStorageHistogram(extended_enabled_, active_tab, preview); | 508 AddSessionStorageHistogram(extended_enabled_, active_tab, preview); |
456 preview->web_contents()->GetController().CopyStateFromAndPrune( | 509 preview->GetController().CopyStateFromAndPrune( |
457 &active_tab->web_contents()->GetController()); | 510 &active_tab->GetController()); |
458 } | 511 } |
459 | 512 |
460 DeleteLoader(); | |
461 | |
462 // Browser takes ownership of the preview. | 513 // Browser takes ownership of the preview. |
463 browser_->CommitInstant(preview, type == INSTANT_COMMIT_PRESSED_ALT_ENTER); | 514 browser_->CommitInstant(preview, type == INSTANT_COMMIT_PRESSED_ALT_ENTER); |
464 | 515 |
465 content::NotificationService::current()->Notify( | 516 content::NotificationService::current()->Notify( |
466 chrome::NOTIFICATION_INSTANT_COMMITTED, | 517 chrome::NOTIFICATION_INSTANT_COMMITTED, |
467 content::Source<content::WebContents>(preview->web_contents()), | 518 content::Source<content::WebContents>(preview), |
468 content::NotificationService::NoDetails()); | 519 content::NotificationService::NoDetails()); |
469 | 520 |
521 // Hide explicitly. See comments in HideLoader() for why. | |
470 model_.SetPreviewState(chrome::search::Mode(), 0, INSTANT_SIZE_PERCENT); | 522 model_.SetPreviewState(chrome::search::Mode(), 0, INSTANT_SIZE_PERCENT); |
471 | 523 |
524 // Delay deletion as we could've gotten here from an InstantLoader method. | |
525 MessageLoop::current()->DeleteSoon(FROM_HERE, loader_.release()); | |
526 | |
472 // Try to create another loader immediately so that it is ready for the next | 527 // Try to create another loader immediately so that it is ready for the next |
473 // user interaction. | 528 // user interaction. |
474 CreateDefaultLoader(); | 529 CreateDefaultLoader(); |
475 | 530 |
476 return true; | 531 return true; |
477 } | 532 } |
478 | 533 |
479 void InstantController::OmniboxLostFocus(gfx::NativeView view_gaining_focus) { | 534 void InstantController::OmniboxLostFocus(gfx::NativeView view_gaining_focus) { |
480 DVLOG(1) << "OmniboxLostFocus"; | 535 DVLOG(1) << "OmniboxLostFocus"; |
481 is_omnibox_focused_ = false; | 536 is_omnibox_focused_ = false; |
482 | 537 |
483 if (!extended_enabled_ && !instant_enabled_) | 538 if (!extended_enabled_ && !instant_enabled_) |
484 return; | 539 return; |
485 | 540 |
486 // If the preview isn't showing search suggestions, nothing to do. The check | 541 // If the preview isn't showing search suggestions, nothing to do. The check |
487 // for GetPreviewContents() (which normally is redundant, given IsCurrent()) | 542 // for GetPreviewContents() (which normally is redundant, given IsCurrent()) |
488 // is to handle the case when we get here during a commit. | 543 // is to handle the case when we get here during a commit. |
489 if (!IsCurrent() || !GetPreviewContents()) { | 544 if (!IsCurrent() || !GetPreviewContents()) { |
490 OnStaleLoader(); | 545 OnStaleLoader(); |
491 return; | 546 return; |
492 } | 547 } |
493 | 548 |
494 #if defined(OS_MACOSX) | 549 #if defined(OS_MACOSX) |
495 if (!loader_->IsPointerDownFromActivate()) | 550 if (!loader_->is_pointer_down_from_activate()) |
496 Hide(true); | 551 HideLoader(); |
497 #else | 552 #else |
498 if (IsViewInContents(GetViewGainingFocus(view_gaining_focus), | 553 if (IsViewInContents(GetViewGainingFocus(view_gaining_focus), |
499 GetPreviewContents()->web_contents())) | 554 loader_->contents())) |
500 CommitIfCurrent(INSTANT_COMMIT_FOCUS_LOST); | 555 CommitIfCurrent(INSTANT_COMMIT_FOCUS_LOST); |
501 else | 556 else |
502 Hide(true); | 557 HideLoader(); |
503 #endif | 558 #endif |
504 } | 559 } |
505 | 560 |
506 void InstantController::OmniboxGotFocus() { | 561 void InstantController::OmniboxGotFocus() { |
507 DVLOG(1) << "OmniboxGotFocus"; | 562 DVLOG(1) << "OmniboxGotFocus"; |
508 is_omnibox_focused_ = true; | 563 is_omnibox_focused_ = true; |
509 | 564 |
510 if (!extended_enabled_ && !instant_enabled_) | 565 if (!extended_enabled_ && !instant_enabled_) |
511 return; | 566 return; |
512 | 567 |
513 if (!GetPreviewContents()) | 568 CreateDefaultLoader(); |
514 CreateDefaultLoader(); | |
515 } | 569 } |
516 | 570 |
517 void InstantController::SearchModeChanged( | 571 void InstantController::SearchModeChanged( |
518 const chrome::search::Mode& old_mode, | 572 const chrome::search::Mode& old_mode, |
519 const chrome::search::Mode& new_mode) { | 573 const chrome::search::Mode& new_mode) { |
520 if (!extended_enabled_) | 574 if (!extended_enabled_) |
521 return; | 575 return; |
522 | 576 |
523 DVLOG(1) << "SearchModeChanged: [origin:mode] " << old_mode.origin << ":" | 577 DVLOG(1) << "SearchModeChanged: [origin:mode] " << old_mode.origin << ":" |
524 << old_mode.mode << " to " << new_mode.origin << ":" | 578 << old_mode.mode << " to " << new_mode.origin << ":" |
525 << new_mode.mode; | 579 << new_mode.mode; |
580 | |
526 search_mode_ = new_mode; | 581 search_mode_ = new_mode; |
582 if (!new_mode.is_search_suggestions()) | |
583 HideLoader(); | |
527 | 584 |
528 if (new_mode.is_search_suggestions()) { | 585 if (loader_) |
529 // The preview is showing NTP content, but it's not appropriate anymore. | 586 loader_->SearchModeChanged(new_mode); |
530 if (model_.mode().is_ntp() && !new_mode.is_origin_ntp()) | |
531 Hide(false); | |
532 } else { | |
533 Hide(true); | |
534 } | |
535 | 587 |
536 if (GetPreviewContents()) | 588 ResetInstantTab(); |
537 loader_->SearchModeChanged(new_mode); | |
538 } | 589 } |
539 | 590 |
540 void InstantController::ActiveTabChanged() { | 591 void InstantController::ActiveTabChanged() { |
541 if (!extended_enabled_ && !instant_enabled_) | 592 if (!extended_enabled_ && !instant_enabled_) |
542 return; | 593 return; |
543 | 594 |
544 DVLOG(1) << "ActiveTabChanged"; | 595 DVLOG(1) << "ActiveTabChanged"; |
545 | 596 |
546 // By this time, SearchModeChanged() should've been called, so we only need to | 597 // When switching tabs, always hide the preview, except if it's showing NTP |
547 // handle the case when the search mode does NOT change, as in the case of | 598 // content, and the new tab is also an NTP. |
548 // going from search_suggestions to search_suggestions (i.e., partial queries | 599 if (!search_mode_.is_ntp() || !model_.mode().is_ntp()) |
549 // on both old and new tabs). | 600 HideLoader(); |
550 if (search_mode_.is_search_suggestions() && | 601 |
551 model_.mode().is_search_suggestions()) | 602 if (extended_enabled_) |
552 Hide(false); | 603 ResetInstantTab(); |
553 } | 604 } |
554 | 605 |
555 void InstantController::SetInstantEnabled(bool instant_enabled) { | 606 void InstantController::SetInstantEnabled(bool instant_enabled) { |
556 DVLOG(1) << "SetInstantEnabled: " << instant_enabled; | 607 DVLOG(1) << "SetInstantEnabled: " << instant_enabled; |
557 instant_enabled_ = instant_enabled; | 608 instant_enabled_ = instant_enabled; |
558 if (extended_enabled_) { | 609 HideInternal(); |
559 // Reset the loader whenever the Instant pref changes. | 610 loader_.reset(); |
560 DeleteLoader(); | 611 if (extended_enabled_ || instant_enabled_) |
561 CreateDefaultLoader(); | 612 CreateDefaultLoader(); |
562 } else if (!instant_enabled_) { | |
563 DeleteLoader(); | |
564 } | |
565 } | 613 } |
566 | 614 |
567 void InstantController::ThemeChanged(const ThemeBackgroundInfo& theme_info) { | 615 void InstantController::ThemeChanged(const ThemeBackgroundInfo& theme_info) { |
568 if (!extended_enabled_) | 616 if (!extended_enabled_) |
569 return; | 617 return; |
570 | 618 |
571 if (GetPreviewContents()) | 619 if (loader_) |
572 loader_->SendThemeBackgroundInfo(theme_info); | 620 loader_->SendThemeBackgroundInfo(theme_info); |
573 } | 621 } |
574 | 622 |
575 void InstantController::ThemeAreaHeightChanged(int height) { | 623 void InstantController::ThemeAreaHeightChanged(int height) { |
576 if (!extended_enabled_) | 624 if (!extended_enabled_) |
577 return; | 625 return; |
578 | 626 |
579 if (GetPreviewContents()) | 627 if (loader_) |
580 loader_->SendThemeAreaHeight(height); | 628 loader_->SendThemeAreaHeight(height); |
581 } | 629 } |
582 | 630 |
583 void InstantController::SetSuggestions( | 631 void InstantController::SetSuggestions( |
584 InstantLoader* loader, | 632 const content::WebContents* contents, |
585 const std::vector<InstantSuggestion>& suggestions) { | 633 const std::vector<InstantSuggestion>& suggestions) { |
586 DVLOG(1) << "SetSuggestions"; | 634 DVLOG(1) << "SetSuggestions"; |
587 if (loader_ != loader || !search_mode_.is_search_suggestions()) | 635 |
636 // Ignore if we are not currently accepting search suggestions. | |
637 if (!search_mode_.is_search_suggestions() || last_omnibox_text_.empty()) | |
588 return; | 638 return; |
589 | 639 |
640 // Ignore if the message is from an unexpected source. | |
641 if (instant_tab_) { | |
642 if (instant_tab_->contents() != contents) | |
643 return; | |
644 } else if (!loader_ || loader_->contents() != contents || | |
645 !allow_preview_to_show_search_suggestions_) { | |
646 return; | |
647 } | |
648 | |
590 InstantSuggestion suggestion; | 649 InstantSuggestion suggestion; |
591 if (!suggestions.empty()) | 650 if (!suggestions.empty()) |
592 suggestion = suggestions[0]; | 651 suggestion = suggestions[0]; |
593 | 652 |
594 if (suggestion.behavior == INSTANT_COMPLETE_REPLACE) { | 653 if (suggestion.behavior == INSTANT_COMPLETE_REPLACE) { |
595 // We don't get an Update() when changing the omnibox due to a REPLACE | 654 // We don't get an Update() when changing the omnibox due to a REPLACE |
596 // suggestion (so that we don't inadvertently cause the preview to change | 655 // suggestion (so that we don't inadvertently cause the preview to change |
597 // what it's showing, as the user arrows up/down through the page-provided | 656 // what it's showing, as the user arrows up/down through the page-provided |
598 // suggestions). So, update these state variables here. | 657 // suggestions). So, update these state variables here. |
599 last_full_text_ = suggestion.text; | 658 last_omnibox_text_ = suggestion.text; |
600 last_user_text_.clear(); | |
601 last_verbatim_ = true; | |
602 last_suggestion_ = InstantSuggestion(); | 659 last_suggestion_ = InstantSuggestion(); |
603 last_match_was_search_ = suggestion.type == INSTANT_SUGGESTION_SEARCH; | 660 last_match_was_search_ = suggestion.type == INSTANT_SUGGESTION_SEARCH; |
604 DVLOG(1) << "SetReplaceSuggestion: text='" << suggestion.text << "'" | 661 DVLOG(1) << "SetReplaceSuggestion: text='" << suggestion.text << "'" |
605 << " type=" << suggestion.type; | 662 << " type=" << suggestion.type; |
606 browser_->SetInstantSuggestion(suggestion); | 663 browser_->SetInstantSuggestion(suggestion); |
607 } else { | 664 } else { |
608 // Suggestion text should be a full URL for URL suggestions, or the | 665 // Suggestion text should be a full URL for URL suggestions, or the |
609 // completion of a query for query suggestions. | 666 // completion of a query for query suggestions. |
610 if (suggestion.type == INSTANT_SUGGESTION_URL) { | 667 if (suggestion.type == INSTANT_SUGGESTION_URL) { |
611 // If the suggestion is not a valid URL, perhaps it's something like | 668 // If the suggestion is not a valid URL, perhaps it's something like |
612 // "foo.com". Try prefixing "http://". If it still isn't valid, drop it. | 669 // "foo.com". Try prefixing "http://". If it still isn't valid, drop it. |
613 if (!GURL(suggestion.text).is_valid()) { | 670 if (!GURL(suggestion.text).is_valid()) { |
614 suggestion.text.insert(0, ASCIIToUTF16("http://")); | 671 suggestion.text.insert(0, ASCIIToUTF16("http://")); |
615 if (!GURL(suggestion.text).is_valid()) | 672 if (!GURL(suggestion.text).is_valid()) |
616 suggestion = InstantSuggestion(); | 673 suggestion = InstantSuggestion(); |
617 } | 674 } |
618 } else if (StartsWith(suggestion.text, last_user_text_, true)) { | 675 } else if (StartsWith(suggestion.text, last_omnibox_text_, true)) { |
619 // The user typed an exact prefix of the suggestion. | 676 // The user typed an exact prefix of the suggestion. |
620 suggestion.text.erase(0, last_user_text_.size()); | 677 suggestion.text.erase(0, last_omnibox_text_.size()); |
621 } else if (!NormalizeAndStripPrefix(&suggestion.text, last_user_text_)) { | 678 } else if (!NormalizeAndStripPrefix(&suggestion.text, last_omnibox_text_)) { |
622 // Unicode normalize and case-fold the user text and suggestion. If the | 679 // Unicode normalize and case-fold the user text and suggestion. If the |
623 // user text is a prefix, suggest the normalized, case-folded completion; | 680 // user text is a prefix, suggest the normalized, case-folded completion; |
624 // for instance, if the user types 'i' and the suggestion is 'INSTANT', | 681 // for instance, if the user types 'i' and the suggestion is 'INSTANT', |
625 // suggest 'nstant'. Otherwise, the user text really isn't a prefix, so | 682 // suggest 'nstant'. Otherwise, the user text really isn't a prefix, so |
626 // suggest nothing. | 683 // suggest nothing. |
627 suggestion = InstantSuggestion(); | 684 suggestion = InstantSuggestion(); |
628 } | 685 } |
629 | 686 |
630 // If the omnibox is blank, this suggestion is for an older query. Ignore. | |
631 if (last_user_text_.empty()) | |
632 suggestion = InstantSuggestion(); | |
633 | |
634 // Don't suggest gray text if there already was inline autocompletion. | 687 // Don't suggest gray text if there already was inline autocompletion. |
635 // http://crbug.com/162303 | 688 // http://crbug.com/162303 |
636 if (suggestion.behavior == INSTANT_COMPLETE_NEVER && | 689 if (suggestion.behavior == INSTANT_COMPLETE_NEVER && |
637 last_user_text_ != last_full_text_) | 690 last_omnibox_text_has_inline_autocompletion_) |
638 suggestion = InstantSuggestion(); | 691 suggestion = InstantSuggestion(); |
639 | 692 |
640 // Don't allow inline autocompletion if the query was verbatim. | 693 // Don't allow inline autocompletion if the query was verbatim. |
641 if (suggestion.behavior == INSTANT_COMPLETE_NOW && last_verbatim_) | 694 if (suggestion.behavior == INSTANT_COMPLETE_NOW && last_verbatim_) |
642 suggestion = InstantSuggestion(); | 695 suggestion = InstantSuggestion(); |
643 | 696 |
644 last_suggestion_ = suggestion; | 697 last_suggestion_ = suggestion; |
645 | 698 |
646 if (!suggestion.text.empty()) { | 699 if (!suggestion.text.empty()) { |
647 DVLOG(1) << "SetInstantSuggestion: text='" << suggestion.text << "'" | 700 DVLOG(1) << "SetInstantSuggestion: text='" << suggestion.text << "'" |
648 << " behavior=" << suggestion.behavior << " type=" | 701 << " behavior=" << suggestion.behavior << " type=" |
649 << suggestion.type; | 702 << suggestion.type; |
650 browser_->SetInstantSuggestion(suggestion); | 703 browser_->SetInstantSuggestion(suggestion); |
651 } | 704 } |
652 } | 705 } |
653 | 706 |
654 // Extended mode pages will show() when ready. | 707 // Extended mode pages will call ShowLoader() when they are ready. |
655 if (!extended_enabled_) | 708 if (!extended_enabled_) |
656 Show(INSTANT_SHOWN_QUERY_SUGGESTIONS, 100, INSTANT_SIZE_PERCENT); | 709 ShowLoader(INSTANT_SHOWN_QUERY_SUGGESTIONS, 100, INSTANT_SIZE_PERCENT); |
657 } | 710 } |
658 | 711 |
659 void InstantController::CommitInstantLoader(InstantLoader* loader) { | 712 void InstantController::InstantSupportDetermined( |
660 if (loader_ == loader) | 713 const content::WebContents* contents, |
661 CommitIfCurrent(INSTANT_COMMIT_FOCUS_LOST); | 714 bool supports_instant) { |
715 if (instant_tab_ && instant_tab_->contents() == contents) { | |
716 if (!supports_instant) | |
717 MessageLoop::current()->DeleteSoon(FROM_HERE, instant_tab_.release()); | |
718 return; | |
719 } | |
720 | |
721 if (loader_ && loader_->contents() == contents) { | |
722 if (supports_instant) { | |
723 blacklisted_urls_.erase(loader_->instant_url()); | |
724 } else { | |
725 ++blacklisted_urls_[loader_->instant_url()]; | |
726 HideInternal(); | |
727 delete loader_->ReleaseContents(); | |
728 MessageLoop::current()->DeleteSoon(FROM_HERE, loader_.release()); | |
729 CreateDefaultLoader(); | |
730 } | |
731 content::NotificationService::current()->Notify( | |
732 chrome::NOTIFICATION_INSTANT_SUPPORT_DETERMINED, | |
733 content::Source<InstantController>(this), | |
734 content::NotificationService::NoDetails()); | |
735 } | |
662 } | 736 } |
663 | 737 |
664 void InstantController::ShowInstantPreview(InstantLoader* loader, | 738 void InstantController::ShowInstantPreview(InstantShownReason reason, |
665 InstantShownReason reason, | |
666 int height, | 739 int height, |
667 InstantSizeUnits units) { | 740 InstantSizeUnits units) { |
668 if (loader_ == loader && extended_enabled_) | 741 if (extended_enabled_) |
669 Show(reason, height, units); | 742 ShowLoader(reason, height, units); |
670 } | 743 } |
671 | 744 |
672 void InstantController::InstantSupportDetermined(InstantLoader* loader, | 745 void InstantController::SwappedWebContents() { |
673 bool supports_instant) { | 746 model_.SetPreviewContents(GetPreviewContents()); |
674 if (supports_instant) { | |
675 blacklisted_urls_.erase(loader->instant_url()); | |
676 } else { | |
677 ++blacklisted_urls_[loader->instant_url()]; | |
678 if (loader_ == loader) | |
679 DeleteLoader(); | |
680 } | |
681 | |
682 content::NotificationService::current()->Notify( | |
683 chrome::NOTIFICATION_INSTANT_SUPPORT_DETERMINED, | |
684 content::Source<InstantController>(this), | |
685 content::NotificationService::NoDetails()); | |
686 } | 747 } |
687 | 748 |
688 void InstantController::SwappedTabContents(InstantLoader* loader) { | 749 void InstantController::InstantLoaderContentsFocused() { |
689 if (loader_ == loader) | |
690 model_.SetPreviewContents(GetPreviewContents()); | |
691 } | |
692 | |
693 void InstantController::InstantLoaderContentsFocused(InstantLoader* loader) { | |
694 #if defined(USE_AURA) | 750 #if defined(USE_AURA) |
695 // On aura the omnibox only receives a focus lost if we initiate the focus | 751 // On aura the omnibox only receives a focus lost if we initiate the focus |
696 // change. This does that. | 752 // change. This does that. |
697 if (loader_ == loader && !model_.mode().is_default()) | 753 if (!model_.mode().is_default()) |
698 browser_->InstantPreviewFocused(); | 754 browser_->InstantPreviewFocused(); |
699 #endif | 755 #endif |
700 } | 756 } |
701 | 757 |
702 bool InstantController::ResetLoader(const TemplateURL* template_url, | 758 bool InstantController::ResetLoader(const TemplateURL* template_url, |
703 const TabContents* active_tab) { | 759 const content::WebContents* active_tab) { |
704 std::string instant_url; | 760 std::string instant_url; |
705 if (!GetInstantURL(template_url, &instant_url)) | 761 if (!GetInstantURL(template_url, &instant_url)) |
706 return false; | 762 return false; |
707 | 763 |
708 if (GetPreviewContents() && loader_->instant_url() != instant_url) | 764 if (loader_ && loader_->instant_url() == instant_url) |
709 DeleteLoader(); | 765 return true; |
710 | 766 |
711 if (!GetPreviewContents()) { | 767 HideInternal(); |
712 loader_.reset(new InstantLoader(this, instant_url, active_tab)); | 768 loader_.reset(new InstantLoader(this, instant_url)); |
713 loader_->Init(); | 769 loader_->InitContents(active_tab); |
714 | 770 |
715 // Ensure the searchbox API has the correct initial state. | 771 // Ensure the searchbox API has the correct initial state. |
716 if (extended_enabled_) { | 772 if (extended_enabled_) { |
717 browser_->UpdateThemeInfoForPreview(); | 773 browser_->UpdateThemeInfoForPreview(); |
718 loader_->SetDisplayInstantResults(instant_enabled_); | 774 loader_->SetDisplayInstantResults(instant_enabled_); |
719 loader_->SearchModeChanged(search_mode_); | 775 loader_->SearchModeChanged(search_mode_); |
720 } | 776 } |
721 | 777 |
722 // Reset the loader timer. | 778 // Restart the stale loader timer. |
723 stale_loader_timer_.Start( | 779 stale_loader_timer_.Start(FROM_HERE, |
724 FROM_HERE, | 780 base::TimeDelta::FromMilliseconds(kStaleLoaderTimeoutMS), this, |
725 base::TimeDelta::FromMilliseconds(kStaleLoaderTimeoutMS), this, | 781 &InstantController::OnStaleLoader); |
726 &InstantController::OnStaleLoader); | |
727 } | |
728 | 782 |
729 return true; | 783 return true; |
730 } | 784 } |
731 | 785 |
732 bool InstantController::CreateDefaultLoader() { | 786 bool InstantController::CreateDefaultLoader() { |
733 // If there's no active tab, the browser is closing. | 787 // If there's no active tab, the browser is closing. |
734 const TabContents* active_tab = browser_->GetActiveTabContents(); | 788 const content::WebContents* active_tab = browser_->GetActiveWebContents(); |
735 if (!active_tab) | 789 if (!active_tab) |
736 return false; | 790 return false; |
737 | 791 |
738 const TemplateURL* template_url = | 792 const TemplateURL* template_url = TemplateURLServiceFactory::GetForProfile( |
739 TemplateURLServiceFactory::GetForProfile(active_tab->profile())-> | 793 Profile::FromBrowserContext(active_tab->GetBrowserContext()))-> |
740 GetDefaultSearchProvider(); | 794 GetDefaultSearchProvider(); |
741 | 795 |
742 return ResetLoader(template_url, active_tab); | 796 return ResetLoader(template_url, active_tab); |
743 } | 797 } |
744 | 798 |
745 void InstantController::OnStaleLoader() { | 799 void InstantController::OnStaleLoader() { |
746 // If the preview is showing or the omnibox has focus, don't delete the | 800 // If the preview is showing or the omnibox has focus, don't delete the |
747 // loader. It will get refreshed the next time the preview is hidden or the | 801 // loader. It will get refreshed the next time the preview is hidden or the |
748 // omnibox loses focus. | 802 // omnibox loses focus. |
749 if (!stale_loader_timer_.IsRunning() && !is_omnibox_focused_ && | 803 if (!stale_loader_timer_.IsRunning() && !is_omnibox_focused_ && |
750 model_.mode().is_default()) { | 804 model_.mode().is_default()) { |
751 DeleteLoader(); | 805 loader_.reset(); |
752 CreateDefaultLoader(); | 806 CreateDefaultLoader(); |
753 } | 807 } |
754 } | 808 } |
755 | 809 |
756 void InstantController::DeleteLoader() { | 810 void InstantController::ResetInstantTab() { |
757 // Clear all state, except |last_transition_type_| as it's used during commit. | 811 if (search_mode_.is_origin_search()) { |
758 last_user_text_.clear(); | 812 content::WebContents* active_tab = browser_->GetActiveWebContents(); |
759 last_full_text_.clear(); | 813 if (!instant_tab_ || active_tab != instant_tab_->contents()) { |
760 last_verbatim_ = false; | 814 instant_tab_.reset(new InstantTab(this, active_tab)); |
761 last_suggestion_ = InstantSuggestion(); | 815 instant_tab_->Init(); |
762 last_match_was_search_ = false; | 816 } |
817 | |
818 // Hide the |loader_| since we are now using |instant_tab_| instead. | |
819 HideLoader(); | |
820 } else { | |
821 instant_tab_.reset(); | |
822 } | |
823 } | |
824 | |
825 bool InstantController::ResetLoaderForMatch(const AutocompleteMatch& match) { | |
826 // If we are on a search results page, we'll use that instead of a loader. | |
827 // TODO(sreeram): If |instant_tab_|'s URL is not the same as the instant_url | |
828 // of |match|, we shouldn't use the committed tab. | |
829 if (instant_tab_) | |
830 return true; | |
831 | |
832 // If there's no active tab, the browser is closing. | |
833 const content::WebContents* active_tab = browser_->GetActiveWebContents(); | |
834 if (!active_tab) | |
835 return false; | |
836 | |
837 // Try to create a loader for the instant_url in the TemplateURL of |match|. | |
838 const TemplateURL* template_url = match.GetTemplateURL( | |
839 Profile::FromBrowserContext(active_tab->GetBrowserContext()), false); | |
840 if (ResetLoader(template_url, active_tab)) | |
841 return true; | |
842 | |
843 // In non-extended mode, stop if we couldn't get a loader for the |match|. | |
763 if (!extended_enabled_) | 844 if (!extended_enabled_) |
764 search_mode_.mode = chrome::search::Mode::MODE_DEFAULT; | 845 return false; |
765 omnibox_bounds_ = gfx::Rect(); | 846 |
766 last_omnibox_bounds_ = gfx::Rect(); | 847 // If the match is a query, it is for a non-Instant search engine; stop. |
767 update_bounds_timer_.Stop(); | 848 if (last_match_was_search_) |
768 stale_loader_timer_.Stop(); | 849 return false; |
769 url_for_history_ = GURL(); | 850 |
770 first_interaction_time_ = base::Time(); | 851 // The match is a URL, or a blank query. Try the default search engine. |
852 return CreateDefaultLoader(); | |
853 } | |
854 | |
855 void InstantController::HideLoader() { | |
856 HideInternal(); | |
857 OnStaleLoader(); | |
858 } | |
859 | |
860 void InstantController::HideInternal() { | |
861 DVLOG(1) << "Hide"; | |
862 | |
863 // If GetPreviewContents() returns NULL, either we're already in the desired | |
864 // MODE_DEFAULT state, or we're in the commit path. For the latter, don't | |
865 // change the state just yet; else we may hide the preview unnecessarily. | |
866 // Instead, the state will be set correctly after the commit is done. | |
771 if (GetPreviewContents()) { | 867 if (GetPreviewContents()) { |
772 model_.SetPreviewState(chrome::search::Mode(), 0, INSTANT_SIZE_PERCENT); | 868 model_.SetPreviewState(chrome::search::Mode(), 0, INSTANT_SIZE_PERCENT); |
773 loader_->CleanupPreviewContents(); | 869 allow_preview_to_show_search_suggestions_ = false; |
870 | |
871 // Send a message asking the preview to clear out old results. | |
872 loader_->Update(string16(), 0, 0, true); | |
774 } | 873 } |
775 | 874 |
776 // Schedule the deletion for later, since we may have gotten here from a call | |
777 // within a |loader_| method (i.e., it's still on the stack). If we deleted | |
778 // the loader immediately, things would still be fine so long as the caller | |
779 // doesn't access any instance members after we return, but why rely on that? | |
780 MessageLoop::current()->DeleteSoon(FROM_HERE, loader_.release()); | |
781 } | |
782 | |
783 void InstantController::Hide(bool clear_query) { | |
784 DVLOG(1) << "Hide: clear_query=" << clear_query; | |
785 | |
786 // The only time when the preview is not already in the desired MODE_DEFAULT | |
787 // state and GetPreviewContents() returns NULL is when we are in the commit | |
788 // path. In that case, don't change the state just yet; otherwise we may | |
789 // cause the preview to hide unnecessarily. Instead, the state will be set | |
790 // correctly after the commit is done. | |
791 if (GetPreviewContents()) | |
792 model_.SetPreviewState(chrome::search::Mode(), 0, INSTANT_SIZE_PERCENT); | |
793 | |
794 // Clear the first interaction timestamp for later use. | 875 // Clear the first interaction timestamp for later use. |
795 first_interaction_time_ = base::Time(); | 876 first_interaction_time_ = base::Time(); |
796 | |
797 if (clear_query) { | |
798 if (GetPreviewContents() && !last_full_text_.empty()) | |
799 loader_->Update(string16(), true); | |
800 last_user_text_.clear(); | |
801 last_full_text_.clear(); | |
802 } | |
803 | |
804 OnStaleLoader(); | |
805 } | 877 } |
806 | 878 |
807 void InstantController::Show(InstantShownReason reason, | 879 void InstantController::ShowLoader(InstantShownReason reason, |
808 int height, | 880 int height, |
809 InstantSizeUnits units) { | 881 InstantSizeUnits units) { |
882 // If we are on a committed search results page, the |loader_| is not in use. | |
883 if (instant_tab_) | |
884 return; | |
885 | |
810 DVLOG(1) << "Show: reason=" << reason << " height=" << height << " units=" | 886 DVLOG(1) << "Show: reason=" << reason << " height=" << height << " units=" |
811 << units; | 887 << units; |
812 | 888 |
813 // Must be on NTP to show NTP content. | 889 // Must be on NTP to show NTP content. |
814 if (reason == INSTANT_SHOWN_CUSTOM_NTP_CONTENT && !search_mode_.is_ntp()) | 890 if (reason == INSTANT_SHOWN_CUSTOM_NTP_CONTENT && !search_mode_.is_ntp()) |
815 return; | 891 return; |
816 | 892 |
817 // Must have updated omnibox after most recent Hide() to show suggestions. | 893 // Must have updated omnibox after the last HideLoader() to show suggestions. |
818 if (reason == INSTANT_SHOWN_QUERY_SUGGESTIONS && | 894 if (reason == INSTANT_SHOWN_QUERY_SUGGESTIONS && |
819 !search_mode_.is_search_suggestions()) | 895 !allow_preview_to_show_search_suggestions_) |
Jered
2012/12/04 19:05:03
When is allow_preview_to_show_search_suggestions !
sreeram
2012/12/04 19:12:37
Whenever we call HideLoader() when the search_mode
| |
820 return; | 896 return; |
821 | 897 |
822 // If the preview is being shown because of the first set of suggestions to | 898 // The page is trying to hide itself. Hide explicitly (i.e., don't use |
823 // arrive for this query editing session, record a histogram value. | 899 // HideLoader()) so that it can change its mind. |
900 if (height == 0) { | |
901 model_.SetPreviewState(chrome::search::Mode(), 0, INSTANT_SIZE_PERCENT); | |
902 return; | |
903 } | |
904 | |
905 // If the preview is being shown for the first time since the user started | |
906 // typing, record a histogram value. | |
824 if (!first_interaction_time_.is_null() && model_.mode().is_default()) { | 907 if (!first_interaction_time_.is_null() && model_.mode().is_default()) { |
825 base::TimeDelta delta = base::Time::Now() - first_interaction_time_; | 908 base::TimeDelta delta = base::Time::Now() - first_interaction_time_; |
826 UMA_HISTOGRAM_TIMES("Instant.TimeToFirstShow", delta); | 909 UMA_HISTOGRAM_TIMES("Instant.TimeToFirstShow", delta); |
827 } | 910 } |
828 | 911 |
829 // Show at 100% height except in the following cases: | 912 // Show at 100% height except in the following cases: |
830 // - Instant is disabled. In this case the page should only ever show | 913 // - Instant is disabled. The page needs to be able to show only a dropdown. |
831 // a dropdown and we should always accept its height. | |
832 // - The page wants to hide (height=0). | |
833 // - The page wants to show custom NTP content. | 914 // - The page wants to show custom NTP content. |
834 // - The page is over a website other than search or an NTP, and is not | 915 // - The page is over a website other than search or an NTP, and is not |
835 // already showing at 100% height. | 916 // already showing at 100% height. |
836 const bool is_full_height = | 917 bool is_full_height = model_.height() == 100 && |
837 model_.height() == 100 && model_.height_units() == INSTANT_SIZE_PERCENT; | 918 model_.height_units() == INSTANT_SIZE_PERCENT; |
838 if (height == 0 || !instant_enabled_ || | 919 if (!instant_enabled_ || reason == INSTANT_SHOWN_CUSTOM_NTP_CONTENT || |
839 reason == INSTANT_SHOWN_CUSTOM_NTP_CONTENT || | |
840 (search_mode_.is_origin_default() && !is_full_height)) | 920 (search_mode_.is_origin_default() && !is_full_height)) |
841 model_.SetPreviewState(search_mode_, height, units); | 921 model_.SetPreviewState(search_mode_, height, units); |
842 else | 922 else |
843 model_.SetPreviewState(search_mode_, 100, INSTANT_SIZE_PERCENT); | 923 model_.SetPreviewState(search_mode_, 100, INSTANT_SIZE_PERCENT); |
844 } | 924 } |
845 | 925 |
846 void InstantController::SendBoundsToPage() { | 926 void InstantController::SendBoundsToPage() { |
847 if (last_omnibox_bounds_ == omnibox_bounds_ || | 927 if (last_omnibox_bounds_ == omnibox_bounds_ || !loader_ || |
848 !GetPreviewContents() || loader_->IsPointerDownFromActivate()) | 928 loader_->is_pointer_down_from_activate()) |
849 return; | 929 return; |
850 | 930 |
851 last_omnibox_bounds_ = omnibox_bounds_; | 931 last_omnibox_bounds_ = omnibox_bounds_; |
852 gfx::Rect preview_bounds = browser_->GetInstantBounds(); | 932 gfx::Rect preview_bounds = browser_->GetInstantBounds(); |
853 gfx::Rect intersection = gfx::IntersectRects(omnibox_bounds_, preview_bounds); | 933 gfx::Rect intersection = gfx::IntersectRects(omnibox_bounds_, preview_bounds); |
854 | 934 |
855 // Translate into window coordinates. | 935 // Translate into window coordinates. |
856 if (!intersection.IsEmpty()) { | 936 if (!intersection.IsEmpty()) { |
857 intersection.Offset(-preview_bounds.origin().x(), | 937 intersection.Offset(-preview_bounds.origin().x(), |
858 -preview_bounds.origin().y()); | 938 -preview_bounds.origin().y()); |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
892 | 972 |
893 // Extended mode should always use HTTPS. TODO(sreeram): This section can be | 973 // Extended mode should always use HTTPS. TODO(sreeram): This section can be |
894 // removed if TemplateURLs supported "https://{google:host}/..." instead of | 974 // removed if TemplateURLs supported "https://{google:host}/..." instead of |
895 // only supporting "{google:baseURL}...". | 975 // only supporting "{google:baseURL}...". |
896 if (extended_enabled_) { | 976 if (extended_enabled_) { |
897 GURL url_obj(*instant_url); | 977 GURL url_obj(*instant_url); |
898 if (!url_obj.is_valid()) | 978 if (!url_obj.is_valid()) |
899 return false; | 979 return false; |
900 | 980 |
901 if (!url_obj.SchemeIsSecure()) { | 981 if (!url_obj.SchemeIsSecure()) { |
902 const std::string new_scheme = "https"; | 982 std::string new_scheme = "https"; |
903 const std::string new_port = "443"; | 983 std::string new_port = "443"; |
904 GURL::Replacements secure; | 984 GURL::Replacements secure; |
905 secure.SetSchemeStr(new_scheme); | 985 secure.SetSchemeStr(new_scheme); |
906 secure.SetPortStr(new_port); | 986 secure.SetPortStr(new_port); |
907 url_obj = url_obj.ReplaceComponents(secure); | 987 url_obj = url_obj.ReplaceComponents(secure); |
908 | 988 |
909 if (!url_obj.is_valid()) | 989 if (!url_obj.is_valid()) |
910 return false; | 990 return false; |
911 | 991 |
912 *instant_url = url_obj.spec(); | 992 *instant_url = url_obj.spec(); |
913 } | 993 } |
914 } | 994 } |
915 | 995 |
916 std::map<std::string, int>::const_iterator iter = | 996 std::map<std::string, int>::const_iterator iter = |
917 blacklisted_urls_.find(*instant_url); | 997 blacklisted_urls_.find(*instant_url); |
918 if (iter != blacklisted_urls_.end() && | 998 if (iter != blacklisted_urls_.end() && |
919 iter->second > kMaxInstantSupportFailures) | 999 iter->second > kMaxInstantSupportFailures) |
920 return false; | 1000 return false; |
921 | 1001 |
922 return true; | 1002 return true; |
923 } | 1003 } |
OLD | NEW |