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); |
samarth
2012/12/03 19:46:03
FWIW, I find this kind of const usage useful (as o
sreeram
2012/12/04 08:10:52
Ack. I do vacillate between too much and too littl
| |
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 27 matching lines...) Expand all Loading... | |
154 last_match_was_search_(false), | 153 last_match_was_search_(false), |
155 is_omnibox_focused_(false) { | 154 is_omnibox_focused_(false) { |
156 } | 155 } |
157 | 156 |
158 InstantController::~InstantController() { | 157 InstantController::~InstantController() { |
159 } | 158 } |
160 | 159 |
161 bool InstantController::Update(const AutocompleteMatch& match, | 160 bool InstantController::Update(const AutocompleteMatch& match, |
162 const string16& user_text, | 161 const string16& user_text, |
163 const string16& full_text, | 162 const string16& full_text, |
164 const bool verbatim, | 163 bool verbatim, |
165 const bool user_input_in_progress, | 164 bool user_input_in_progress, |
166 const bool omnibox_popup_is_open) { | 165 bool omnibox_popup_is_open) { |
167 if (!extended_enabled_ && !instant_enabled_) | 166 if (!extended_enabled_ && !instant_enabled_) |
168 return false; | 167 return false; |
169 | 168 |
170 DVLOG(1) << "Update: " << AutocompleteMatch::TypeToString(match.type) | 169 DVLOG(1) << "Update: " << AutocompleteMatch::TypeToString(match.type) |
171 << " user_text='" << user_text << "' full_text='" << full_text << "'" | 170 << " user_text='" << user_text << "' full_text='" << full_text << "'" |
172 << " verbatim=" << verbatim << " typing=" << user_input_in_progress | 171 << " verbatim=" << verbatim << " typing=" << user_input_in_progress |
173 << " popup=" << omnibox_popup_is_open; | 172 << " popup=" << omnibox_popup_is_open; |
174 | 173 |
175 // If the popup is open, the user has to be typing. | 174 // If the popup is open, the user has to be typing. |
176 DCHECK(!omnibox_popup_is_open || user_input_in_progress); | 175 DCHECK(!omnibox_popup_is_open || user_input_in_progress); |
177 | 176 |
178 // If the popup is closed, there should be no inline autocompletion. | 177 // If the popup is closed, there should be no inline autocompletion. |
179 DCHECK(omnibox_popup_is_open || user_text == full_text) << user_text << "|" | 178 DCHECK(omnibox_popup_is_open || user_text.empty() || user_text == full_text) |
180 << full_text; | 179 << user_text << "|" << full_text; |
181 | 180 |
182 // If there's inline autocompletion, the query has to be verbatim. | 181 // If there's inline autocompletion, the query has to be verbatim. |
183 DCHECK(user_text == full_text || verbatim) << user_text << "|" << full_text; | 182 DCHECK(user_text.empty() || user_text == full_text || verbatim) |
183 << user_text << "|" << full_text; | |
184 | 184 |
185 // If there's no text in the omnibox, the user can't have typed any. | 185 // 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; | 186 DCHECK(!full_text.empty() || user_text.empty()) << user_text; |
187 | 187 |
188 // If the user isn't typing, and the popup is closed, there can't be any | 188 // If the user isn't typing, and the popup is closed, there can't be any |
189 // user-typed text. | 189 // user-typed text. |
190 DCHECK(user_input_in_progress || omnibox_popup_is_open || user_text.empty()) | 190 DCHECK(user_input_in_progress || omnibox_popup_is_open || user_text.empty()) |
191 << user_text; | 191 << user_text; |
192 | 192 |
193 // In non-extended mode, SearchModeChanged() is never called, so fake it. The | 193 // 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 | 194 // 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 | 195 // "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. | 196 // query is sent to the loader, the mode is set to "allow" further below. |
197 if (!extended_enabled_) | 197 if (!extended_enabled_) |
198 search_mode_.mode = chrome::search::Mode::MODE_DEFAULT; | 198 search_mode_.mode = chrome::search::Mode::MODE_DEFAULT; |
199 | 199 |
200 // If there's no active tab, the browser is closing. | 200 // If there's no active tab, the browser is closing. |
201 const TabContents* const active_tab = browser_->GetActiveTabContents(); | 201 const content::WebContents* active_tab = browser_->GetActiveWebContents(); |
202 if (!active_tab) { | 202 if (!active_tab) { |
203 Hide(true); | 203 Hide(); |
204 return false; | 204 return false; |
205 } | 205 } |
206 | 206 |
207 const TemplateURL* template_url = match.GetTemplateURL( | |
208 Profile::FromBrowserContext(active_tab->GetBrowserContext()), false); | |
209 bool match_is_search = AutocompleteMatch::IsSearchType(match.type); | |
210 | |
211 // Ensure we have a loader or tab that can process this |match|. | |
212 if (extended_enabled_) { | |
213 // In extended mode, use the first among the following that succeeds: | |
214 // 1. A committed search results page. | |
215 // 2. An Instant loader for |template_url|. | |
216 // 3. If the |match| is a URL or a blank search query, the Instant loader | |
217 // for the default search engine. | |
218 // TODO(sreeram): If |template_url| doesn't match the |instant_tab_| URL, | |
219 // we shouldn't blindly use the committed tab. | |
220 if (!instant_tab_ && !ResetLoader(template_url, active_tab) && | |
221 ((match_is_search && !user_text.empty()) || !CreateDefaultLoader())) { | |
Jered
2012/11/29 19:57:05
Should this check full_text.empty() as it used to?
Jered
2012/11/29 19:57:05
It took me a second to unwind this if. Maybe
bool
sreeram
2012/12/04 08:10:52
No, user_text is the right thing here. If we are i
sreeram
2012/12/04 08:10:52
Done.
| |
222 Hide(); | |
223 return false; | |
224 } | |
225 } else if (!ResetLoader(template_url, active_tab)) { | |
226 // In non-extended mode, if the TemplateURL of the |match| doesn't have a | |
227 // valid Instant URL, do not proceed. | |
228 Hide(); | |
229 return false; | |
230 } | |
231 | |
207 // Legend: OPIO == |omnibox_popup_is_open|, UIIP = |user_input_in_progress|. | 232 // Legend: OPIO == |omnibox_popup_is_open|, UIIP = |user_input_in_progress|. |
Jered
2012/11/29 19:57:05
I'd love to extract everything from this table dow
sreeram
2012/12/04 08:10:52
I've removed the table and made the section much b
| |
208 // | 233 // |
209 // # OPIO UIIP full_text Notes | 234 // # OPIO UIIP full_text Notes |
210 // - ---- ---- --------- ----- | 235 // - ---- ---- --------- ----- |
211 // 1 no no blank } Navigation, or user hit Escape. |full_text| is | 236 // 1 no no blank } Navigation, tab-switch or Escape. |full_text| is |
212 // 2 no no non-blank } blank if the page is NTP, non-blank otherwise. | 237 // 2 no no non-blank } blank if the page is NTP, non-blank otherwise. |
213 // | 238 // |
214 // 3 no yes blank User backspaced away all omnibox text. | 239 // 3 no yes blank User backspaced away all omnibox text. |
215 // | 240 // |
216 // 4 no yes non-blank User switched to a tab with a partial query. | 241 // 4 no yes non-blank User switched to a tab with a partial query. |
217 // | 242 // |
218 // 5 yes no blank } Impossible. DCHECK()ed above. | 243 // 5 yes no blank } Impossible. DCHECK()ed above. |
219 // 6 yes no non-blank } | 244 // 6 yes no non-blank } |
220 // | 245 // |
221 // 7 yes yes blank User typed a "?" into the omnibox. | 246 // 7 yes yes blank User typed a "?" into the omnibox. |
222 // | 247 // |
223 // 8 yes yes non-blank User typed text into the omnibox. | 248 // 8 yes yes non-blank User typed text into the omnibox. |
224 // | 249 // |
225 // In non-extended mode, #1 to #7 call Hide(). #8 calls loader_->Update(). | 250 // In non-extended mode, #1 to #7 call Hide(). #8 calls loader_->Update(). |
226 // | 251 // |
227 // In extended mode, #2 and #4 call Hide(). #1 doesn't Hide() as the preview | 252 // In extended mode, #2 and #4 call Hide(). #1 doesn't Hide() as the preview |
228 // may be showing custom NTP content, but doesn't Update() either. #3 and #7 | 253 // may be showing custom NTP content, but doesn't Update() either. #3 and #7 |
229 // don't Hide(), but send a blank query to Update(). #8 calls Update(). | 254 // don't Hide(), and send a blank query to Update(). #8 calls Update(). |
230 | 255 |
231 if (extended_enabled_) { | 256 if (extended_enabled_) { |
232 if (!omnibox_popup_is_open) { | 257 if (!omnibox_popup_is_open) { |
233 if (!full_text.empty()) { | 258 if (!user_input_in_progress) { |
234 Hide(true); | 259 if (!full_text.empty()) { |
235 return false; | 260 Hide(); // #2 |
261 // If the user hit Escape when on a search results page, restore the | |
262 // original results by resending the query. If the user was not on a | |
Jered
2012/11/29 19:57:05
What "original" results? Do you mean the committed
sreeram
2012/12/04 08:10:52
I've tried to explain this better in the new patch
| |
263 // search results page, SearchModeChanged() would've caught it, and | |
264 // |instant_tab_| will be NULL, so we won't accidentally send a URL | |
265 // to |instant_tab_|. Except if the user just switched tabs. Hence the | |
266 // comparison of WebContents, to catch that case as well. | |
267 if (instant_tab_ && instant_tab_->contents() == active_tab) | |
268 instant_tab_->Update(full_text, true); | |
269 } | |
270 return false; // #1 | |
Jered
2012/11/29 19:57:05
Note this is both #1 and #2.
sreeram
2012/12/04 08:10:52
Ack. See new patch.
| |
271 } else { | |
272 if (!full_text.empty()) { | |
273 Hide(); // #4 | |
274 if (match_is_search) { | |
275 // The user just switched to a tab with a partial query already in | |
276 // the omnibox. This tab may or may not be a search results page | |
277 // (SearchModeChanged() hasn't been called yet). So, assume that it | |
278 // could be, and store |full_text| so that if the user then hits | |
279 // Enter, we'll send the correct query in instant_tab_->Submit(). | |
280 last_full_text_ = full_text; | |
281 last_match_was_search_ = true; | |
282 } | |
283 return false; | |
284 } | |
236 } | 285 } |
237 if (!user_input_in_progress && full_text.empty()) | |
238 return false; | |
239 } | 286 } |
240 } else if (!omnibox_popup_is_open || full_text.empty()) { | 287 } else if (!omnibox_popup_is_open || full_text.empty()) { |
241 // Update() can be called if the user clicks the preview while composing | 288 // Update() can be called if the user clicks the preview while composing |
242 // text with an IME. If so, we should commit on mouse up, so don't Hide(). | 289 // text with an IME. If so, we should commit on mouse up, so don't Hide(). |
243 if (!GetPreviewContents() || !loader_->IsPointerDownFromActivate()) | 290 if (!loader_ || !loader_->is_pointer_down_from_activate()) |
244 Hide(true); | 291 Hide(); |
245 return false; | 292 return false; |
246 } | 293 } |
247 | 294 |
248 // Ensure we have a loader that can process this match. First, try to use the | |
249 // TemplateURL of the |match|. If that's invalid, in non-extended mode, stop. | |
250 // In extended mode, try using the default search engine, but only when the | |
251 // match is for a URL (i.e., not some other kind of non-Instant search). | |
252 // A completely blank query shows up as a search, and we do want to allow | |
253 // that, hence the "!full_text.empty()" clause. | |
254 Profile* const profile = active_tab->profile(); | |
255 const bool match_is_search = AutocompleteMatch::IsSearchType(match.type); | |
256 if (!ResetLoader(match.GetTemplateURL(profile, false), active_tab) && | |
257 (!extended_enabled_ || (match_is_search && !full_text.empty()) || | |
258 !CreateDefaultLoader())) { | |
259 Hide(true); | |
260 return false; | |
261 } | |
262 | |
263 // If the user continues typing the same query as the suggested text is | 295 // If the user continues typing the same query as the suggested text is |
264 // showing, reuse the suggestion (but only for INSTANT_COMPLETE_NEVER). | 296 // showing, reuse the suggestion (but only for INSTANT_COMPLETE_NEVER). |
265 bool reused_suggestion = false; | 297 bool reused_suggestion = false; |
266 if (last_suggestion_.behavior == INSTANT_COMPLETE_NEVER) { | 298 if (last_suggestion_.behavior == INSTANT_COMPLETE_NEVER) { |
267 if (StartsWith(last_user_text_, user_text, false) && !user_text.empty()) { | 299 if (StartsWith(last_user_text_, user_text, false) && !user_text.empty()) { |
268 // The user is backspacing away characters. | 300 // The user is backspacing away characters. |
269 last_suggestion_.text.insert(0, last_user_text_, user_text.size(), | 301 last_suggestion_.text.insert(0, last_user_text_, user_text.size(), |
270 last_user_text_.size() - user_text.size()); | 302 last_user_text_.size() - user_text.size()); |
271 reused_suggestion = true; | 303 reused_suggestion = true; |
272 } else if (StartsWith(user_text, last_user_text_, false)) { | 304 } else if (StartsWith(user_text, last_user_text_, false)) { |
273 // The user is typing forward. Normalize any added characters. | 305 // The user is typing forward. Normalize any added characters. |
274 reused_suggestion = NormalizeAndStripPrefix(&last_suggestion_.text, | 306 reused_suggestion = NormalizeAndStripPrefix(&last_suggestion_.text, |
275 string16(user_text, last_user_text_.size())); | 307 string16(user_text, last_user_text_.size())); |
276 } | 308 } |
277 } | 309 } |
278 | 310 |
279 last_user_text_ = user_text; | 311 last_user_text_ = user_text; |
280 last_full_text_ = full_text; | 312 last_full_text_ = full_text; |
281 last_verbatim_ = verbatim; | 313 last_verbatim_ = verbatim; |
282 | 314 |
283 if (!reused_suggestion) | 315 if (!reused_suggestion) |
284 last_suggestion_ = InstantSuggestion(); | 316 last_suggestion_ = InstantSuggestion(); |
285 | 317 |
286 last_transition_type_ = match.transition; | 318 last_transition_type_ = match.transition; |
287 last_match_was_search_ = match_is_search; | 319 last_match_was_search_ = match_is_search; |
288 url_for_history_ = match.destination_url; | 320 url_for_history_ = match.destination_url; |
289 | 321 |
290 // Store the first interaction time for use with latency histograms. | 322 // Store the first interaction time for use with latency histograms (but only |
291 if (first_interaction_time_.is_null()) | 323 // if we are talking to the |loader_| and not a committed tab). |
324 if (!instant_tab_ && first_interaction_time_.is_null()) | |
292 first_interaction_time_ = base::Time::Now(); | 325 first_interaction_time_ = base::Time::Now(); |
293 | 326 |
294 // Allow search suggestions. In extended mode, SearchModeChanged() will set | 327 // Allow search suggestions. In extended mode, SearchModeChanged() will set |
295 // this, but it's not called in non-extended mode, so fake it. | 328 // this, but it's not called in non-extended mode, so fake it. |
296 if (!extended_enabled_) | 329 if (!extended_enabled_) |
297 search_mode_.mode = chrome::search::Mode::MODE_SEARCH_SUGGESTIONS; | 330 search_mode_.mode = chrome::search::Mode::MODE_SEARCH_SUGGESTIONS; |
298 | 331 |
299 loader_->Update(extended_enabled_ ? user_text : full_text, verbatim); | 332 if (instant_tab_) |
333 instant_tab_->Update(user_text, verbatim); | |
334 else | |
335 loader_->Update(extended_enabled_ ? user_text : full_text, verbatim); | |
300 | 336 |
301 content::NotificationService::current()->Notify( | 337 content::NotificationService::current()->Notify( |
302 chrome::NOTIFICATION_INSTANT_CONTROLLER_UPDATED, | 338 chrome::NOTIFICATION_INSTANT_CONTROLLER_UPDATED, |
303 content::Source<InstantController>(this), | 339 content::Source<InstantController>(this), |
304 content::NotificationService::NoDetails()); | 340 content::NotificationService::NoDetails()); |
305 | 341 |
306 // We don't have new suggestions yet, but we can either reuse the existing | 342 // We don't have new suggestions yet, but we can either reuse the existing |
307 // suggestion or reset the existing "gray text". | 343 // suggestion or reset the existing "gray text". |
308 browser_->SetInstantSuggestion(last_suggestion_); | 344 browser_->SetInstantSuggestion(last_suggestion_); |
309 | 345 |
(...skipping 21 matching lines...) Expand all Loading... | |
331 base::TimeDelta::FromMilliseconds(kUpdateBoundsDelayMS), this, | 367 base::TimeDelta::FromMilliseconds(kUpdateBoundsDelayMS), this, |
332 &InstantController::SendBoundsToPage); | 368 &InstantController::SendBoundsToPage); |
333 } | 369 } |
334 } | 370 } |
335 | 371 |
336 void InstantController::HandleAutocompleteResults( | 372 void InstantController::HandleAutocompleteResults( |
337 const std::vector<AutocompleteProvider*>& providers) { | 373 const std::vector<AutocompleteProvider*>& providers) { |
338 if (!extended_enabled_) | 374 if (!extended_enabled_) |
339 return; | 375 return; |
340 | 376 |
341 if (!GetPreviewContents()) | 377 if (!instant_tab_ && !loader_) |
342 return; | 378 return; |
343 | 379 |
344 DVLOG(1) << "AutocompleteResults:"; | 380 DVLOG(1) << "AutocompleteResults:"; |
345 std::vector<InstantAutocompleteResult> results; | 381 std::vector<InstantAutocompleteResult> results; |
346 for (ACProviders::const_iterator provider = providers.begin(); | 382 for (ACProviders::const_iterator provider = providers.begin(); |
347 provider != providers.end(); ++provider) { | 383 provider != providers.end(); ++provider) { |
348 for (ACMatches::const_iterator match = (*provider)->matches().begin(); | 384 for (ACMatches::const_iterator match = (*provider)->matches().begin(); |
349 match != (*provider)->matches().end(); ++match) { | 385 match != (*provider)->matches().end(); ++match) { |
350 InstantAutocompleteResult result; | 386 InstantAutocompleteResult result; |
351 result.provider = UTF8ToUTF16((*provider)->GetName()); | 387 result.provider = UTF8ToUTF16((*provider)->GetName()); |
352 result.type = UTF8ToUTF16(AutocompleteMatch::TypeToString(match->type)); | 388 result.type = UTF8ToUTF16(AutocompleteMatch::TypeToString(match->type)); |
353 result.description = match->description; | 389 result.description = match->description; |
354 result.destination_url = UTF8ToUTF16(match->destination_url.spec()); | 390 result.destination_url = UTF8ToUTF16(match->destination_url.spec()); |
355 result.relevance = match->relevance; | 391 result.relevance = match->relevance; |
356 DVLOG(1) << " " << result.relevance << " " << result.type << " " | 392 DVLOG(1) << " " << result.relevance << " " << result.type << " " |
357 << result.provider << " " << result.destination_url << " '" | 393 << result.provider << " " << result.destination_url << " '" |
358 << result.description << "'"; | 394 << result.description << "'"; |
359 results.push_back(result); | 395 results.push_back(result); |
360 } | 396 } |
361 } | 397 } |
362 | 398 |
363 loader_->SendAutocompleteResults(results); | 399 if (instant_tab_) |
400 instant_tab_->SendAutocompleteResults(results); | |
401 else | |
402 loader_->SendAutocompleteResults(results); | |
364 } | 403 } |
365 | 404 |
366 bool InstantController::OnUpOrDownKeyPressed(int count) { | 405 bool InstantController::OnUpOrDownKeyPressed(int count) { |
367 if (!extended_enabled_) | 406 if (!extended_enabled_) |
368 return false; | 407 return false; |
369 | 408 |
370 if (!GetPreviewContents()) | 409 if (!instant_tab_ && !loader_) |
371 return false; | 410 return false; |
372 | 411 |
373 loader_->OnUpOrDownKeyPressed(count); | 412 if (instant_tab_) |
374 return true; | 413 instant_tab_->UpOrDownKeyPressed(count); |
414 else | |
415 loader_->UpOrDownKeyPressed(count); | |
416 | |
417 return false; | |
Jered
2012/11/29 19:57:05
Should this be return true;?
sreeram
2012/12/04 08:10:52
Indeed. Done. ('Twas a typo.)
| |
375 } | 418 } |
376 | 419 |
377 TabContents* InstantController::GetPreviewContents() const { | 420 content::WebContents* InstantController::GetPreviewContents() const { |
378 return loader_ ? loader_->preview_contents() : NULL; | 421 return loader_ ? loader_->contents() : NULL; |
379 } | 422 } |
380 | 423 |
381 bool InstantController::IsCurrent() const { | 424 bool InstantController::IsCurrent() const { |
382 return model_.mode().is_search_suggestions() && last_match_was_search_; | 425 return model_.mode().is_search_suggestions() && last_match_was_search_; |
383 } | 426 } |
384 | 427 |
385 bool InstantController::CommitIfCurrent(InstantCommitType type) { | 428 bool InstantController::CommitIfCurrent(InstantCommitType type) { |
386 if (!extended_enabled_ && !instant_enabled_) | 429 if (!extended_enabled_ && !instant_enabled_) |
387 return false; | 430 return false; |
388 | 431 |
432 // If we are on a committed search results page, send a submit event to the | |
433 // page, but otherwise, nothing else to do. | |
Jered
2012/11/29 19:57:05
From the "nothing else to do" part, I would expect
sreeram
2012/12/04 08:10:52
Done. It worked the way it was before, since IsCur
| |
434 if (instant_tab_ && last_match_was_search_ && | |
435 type == INSTANT_COMMIT_PRESSED_ENTER) { | |
436 instant_tab_->Submit(last_full_text_); | |
437 return true; | |
438 } | |
439 | |
389 if (!IsCurrent()) | 440 if (!IsCurrent()) |
390 return false; | 441 return false; |
391 | 442 |
392 DVLOG(1) << "CommitIfCurrent"; | 443 DVLOG(1) << "CommitIfCurrent"; |
393 TabContents* preview = loader_->ReleasePreviewContents(type, last_full_text_); | 444 |
445 if (type == INSTANT_COMMIT_FOCUS_LOST) | |
446 loader_->Cancel(last_full_text_); | |
447 else | |
448 loader_->Submit(last_full_text_); | |
449 | |
450 content::WebContents* preview = loader_->ReleaseContents(); | |
394 | 451 |
395 if (extended_enabled_) { | 452 if (extended_enabled_) { |
396 // Consider what's happening: | 453 // Consider what's happening: |
397 // 1. The user has typed a query in the omnibox and committed it (either | 454 // 1. The user has typed a query in the omnibox and committed it (either |
398 // by pressing Enter or clicking on the preview). | 455 // by pressing Enter or clicking on the preview). |
399 // 2. We commit the preview to the tab strip, and tell the page. | 456 // 2. We commit the preview to the tab strip, and tell the page. |
400 // 3. The page will update the URL hash fragment with the query terms. | 457 // 3. The page will update the URL hash fragment with the query terms. |
401 // After steps 1 and 3, the omnibox will show the query terms. However, if | 458 // After steps 1 and 3, the omnibox will show the query terms. However, if |
402 // the URL we are committing at step 2 doesn't already have query terms, it | 459 // the URL we are committing at step 2 doesn't already have query terms, it |
403 // will flash for a brief moment as a plain URL. So, avoid that flicker by | 460 // will flash for a brief moment as a plain URL. So, avoid that flicker by |
404 // pretending that the plain URL is actually the typed query terms. | 461 // pretending that the plain URL is actually the typed query terms. |
405 // TODO(samarth,beaudoin): Instead of this hack, we should add a new field | 462 // TODO(samarth,beaudoin): Instead of this hack, we should add a new field |
406 // to NavigationEntry to keep track of what the correct query, if any, is. | 463 // to NavigationEntry to keep track of what the correct query, if any, is. |
407 content::NavigationEntry* entry = | 464 content::NavigationEntry* entry = |
408 preview->web_contents()->GetController().GetVisibleEntry(); | 465 preview->GetController().GetVisibleEntry(); |
409 std::string url = entry->GetVirtualURL().spec(); | 466 std::string url = entry->GetVirtualURL().spec(); |
410 if (!google_util::IsInstantExtendedAPIGoogleSearchUrl(url) && | 467 if (!google_util::IsInstantExtendedAPIGoogleSearchUrl(url) && |
411 google_util::IsGoogleDomainUrl(url, google_util::ALLOW_SUBDOMAIN, | 468 google_util::IsGoogleDomainUrl(url, google_util::ALLOW_SUBDOMAIN, |
412 google_util::ALLOW_NON_STANDARD_PORTS)) { | 469 google_util::ALLOW_NON_STANDARD_PORTS)) { |
413 entry->SetVirtualURL(GURL( | 470 entry->SetVirtualURL(GURL( |
414 url + "#q=" + | 471 url + "#q=" + |
415 net::EscapeQueryParamValue(UTF16ToUTF8(last_full_text_), true))); | 472 net::EscapeQueryParamValue(UTF16ToUTF8(last_full_text_), true))); |
416 chrome::search::SearchTabHelper::FromWebContents( | 473 chrome::search::SearchTabHelper::FromWebContents(preview)-> |
417 preview->web_contents())->NavigationEntryUpdated(); | 474 NavigationEntryUpdated(); |
418 } | 475 } |
419 } | 476 } |
420 | 477 |
421 // If the preview page has navigated since the last Update(), we need to add | 478 // If the preview page has navigated since the last Update(), we need to add |
422 // the navigation to history ourselves. Else, the page will navigate after | 479 // the navigation to history ourselves. Else, the page will navigate after |
423 // commit, and it will be added to history in the usual manner. | 480 // commit, and it will be added to history in the usual manner. |
424 const history::HistoryAddPageArgs& last_navigation = | 481 const history::HistoryAddPageArgs& last_navigation = |
425 loader_->last_navigation(); | 482 loader_->last_navigation(); |
426 if (!last_navigation.url.is_empty()) { | 483 if (!last_navigation.url.is_empty()) { |
427 content::NavigationEntry* entry = | 484 content::NavigationEntry* entry = preview->GetController().GetActiveEntry(); |
428 preview->web_contents()->GetController().GetActiveEntry(); | |
429 DCHECK_EQ(last_navigation.url, entry->GetURL()); | 485 DCHECK_EQ(last_navigation.url, entry->GetURL()); |
430 | 486 |
431 // Add the page to history. | 487 // Add the page to history. |
432 HistoryTabHelper* history_tab_helper = | 488 HistoryTabHelper* history_tab_helper = |
433 HistoryTabHelper::FromWebContents(preview->web_contents()); | 489 HistoryTabHelper::FromWebContents(preview); |
434 history_tab_helper->UpdateHistoryForNavigation(last_navigation); | 490 history_tab_helper->UpdateHistoryForNavigation(last_navigation); |
435 | 491 |
436 // Update the page title. | 492 // Update the page title. |
437 history_tab_helper->UpdateHistoryPageTitle(*entry); | 493 history_tab_helper->UpdateHistoryPageTitle(*entry); |
438 } | 494 } |
439 | 495 |
440 // Add a fake history entry with a non-Instant search URL, so that search | 496 // Add a fake history entry with a non-Instant search URL, so that search |
441 // terms extraction (for autocomplete history matches) works. | 497 // terms extraction (for autocomplete history matches) works. |
442 if (HistoryService* history = HistoryServiceFactory::GetForProfile( | 498 HistoryService* history = HistoryServiceFactory::GetForProfile( |
443 preview->profile(), Profile::EXPLICIT_ACCESS)) { | 499 Profile::FromBrowserContext(preview->GetBrowserContext()), |
500 Profile::EXPLICIT_ACCESS); | |
501 if (history) { | |
444 history->AddPage(url_for_history_, base::Time::Now(), NULL, 0, GURL(), | 502 history->AddPage(url_for_history_, base::Time::Now(), NULL, 0, GURL(), |
445 history::RedirectList(), last_transition_type_, | 503 history::RedirectList(), last_transition_type_, |
446 history::SOURCE_BROWSED, false); | 504 history::SOURCE_BROWSED, false); |
447 } | 505 } |
448 | 506 |
449 preview->web_contents()->GetController().PruneAllButActive(); | 507 preview->GetController().PruneAllButActive(); |
450 | 508 |
451 if (type != INSTANT_COMMIT_PRESSED_ALT_ENTER) { | 509 if (type != INSTANT_COMMIT_PRESSED_ALT_ENTER) { |
452 const TabContents* active_tab = browser_->GetActiveTabContents(); | 510 content::WebContents* active_tab = browser_->GetActiveWebContents(); |
453 AddSessionStorageHistogram(extended_enabled_, active_tab, preview); | 511 AddSessionStorageHistogram(extended_enabled_, active_tab, preview); |
454 preview->web_contents()->GetController().CopyStateFromAndPrune( | 512 preview->GetController().CopyStateFromAndPrune( |
455 &active_tab->web_contents()->GetController()); | 513 &active_tab->GetController()); |
456 } | 514 } |
457 | 515 |
458 DeleteLoader(); | |
459 | |
460 // Browser takes ownership of the preview. | 516 // Browser takes ownership of the preview. |
461 browser_->CommitInstant(preview, type == INSTANT_COMMIT_PRESSED_ALT_ENTER); | 517 browser_->CommitInstant(preview, type == INSTANT_COMMIT_PRESSED_ALT_ENTER); |
462 | 518 |
463 content::NotificationService::current()->Notify( | 519 content::NotificationService::current()->Notify( |
464 chrome::NOTIFICATION_INSTANT_COMMITTED, | 520 chrome::NOTIFICATION_INSTANT_COMMITTED, |
465 content::Source<content::WebContents>(preview->web_contents()), | 521 content::Source<content::WebContents>(preview), |
466 content::NotificationService::NoDetails()); | 522 content::NotificationService::NoDetails()); |
467 | 523 |
524 // Hide explicitly. See comments in Hide() for why calling it won't work. | |
468 model_.SetPreviewState(chrome::search::Mode(), 0, INSTANT_SIZE_PERCENT); | 525 model_.SetPreviewState(chrome::search::Mode(), 0, INSTANT_SIZE_PERCENT); |
469 | 526 |
527 // Delay deletion as we could've gotten here from an InstantLoader method. | |
528 MessageLoop::current()->DeleteSoon(FROM_HERE, loader_.release()); | |
529 | |
470 // Try to create another loader immediately so that it is ready for the next | 530 // Try to create another loader immediately so that it is ready for the next |
471 // user interaction. | 531 // user interaction. |
472 CreateDefaultLoader(); | 532 CreateDefaultLoader(); |
473 | 533 |
474 return true; | 534 return true; |
475 } | 535 } |
476 | 536 |
477 void InstantController::OmniboxLostFocus(gfx::NativeView view_gaining_focus) { | 537 void InstantController::OmniboxLostFocus(gfx::NativeView view_gaining_focus) { |
478 DVLOG(1) << "OmniboxLostFocus"; | 538 DVLOG(1) << "OmniboxLostFocus"; |
479 is_omnibox_focused_ = false; | 539 is_omnibox_focused_ = false; |
480 | 540 |
481 if (!extended_enabled_ && !instant_enabled_) | 541 if (!extended_enabled_ && !instant_enabled_) |
482 return; | 542 return; |
483 | 543 |
484 // If the preview isn't showing search suggestions, nothing to do. The check | 544 // If the preview isn't showing search suggestions, nothing to do. The check |
485 // for GetPreviewContents() (which normally is redundant, given IsCurrent()) | 545 // for GetPreviewContents() (which normally is redundant, given IsCurrent()) |
486 // is to handle the case when we get here during a commit. | 546 // is to handle the case when we get here during a commit. |
487 if (!IsCurrent() || !GetPreviewContents()) { | 547 if (!IsCurrent() || !GetPreviewContents()) { |
488 OnStaleLoader(); | 548 OnStaleLoader(); |
489 return; | 549 return; |
490 } | 550 } |
491 | 551 |
492 #if defined(OS_MACOSX) | 552 #if defined(OS_MACOSX) |
493 if (!loader_->IsPointerDownFromActivate()) | 553 if (!loader_->is_pointer_down_from_activate()) |
494 Hide(true); | 554 Hide(); |
495 #else | 555 #else |
496 if (IsViewInContents(GetViewGainingFocus(view_gaining_focus), | 556 if (IsViewInContents(GetViewGainingFocus(view_gaining_focus), |
497 GetPreviewContents()->web_contents())) | 557 loader_->contents())) |
498 CommitIfCurrent(INSTANT_COMMIT_FOCUS_LOST); | 558 CommitIfCurrent(INSTANT_COMMIT_FOCUS_LOST); |
499 else | 559 else |
500 Hide(true); | 560 Hide(); |
501 #endif | 561 #endif |
502 } | 562 } |
503 | 563 |
504 void InstantController::OmniboxGotFocus() { | 564 void InstantController::OmniboxGotFocus() { |
505 DVLOG(1) << "OmniboxGotFocus"; | 565 DVLOG(1) << "OmniboxGotFocus"; |
506 is_omnibox_focused_ = true; | 566 is_omnibox_focused_ = true; |
507 | 567 |
508 if (!extended_enabled_ && !instant_enabled_) | 568 if (!extended_enabled_ && !instant_enabled_) |
509 return; | 569 return; |
510 | 570 |
511 if (!GetPreviewContents()) | 571 CreateDefaultLoader(); |
512 CreateDefaultLoader(); | |
513 } | 572 } |
514 | 573 |
515 void InstantController::SearchModeChanged( | 574 void InstantController::SearchModeChanged( |
516 const chrome::search::Mode& old_mode, | 575 const chrome::search::Mode& old_mode, |
517 const chrome::search::Mode& new_mode) { | 576 const chrome::search::Mode& new_mode) { |
518 if (!extended_enabled_) | 577 if (!extended_enabled_) |
519 return; | 578 return; |
520 | 579 |
521 DVLOG(1) << "SearchModeChanged: [origin:mode] " << old_mode.origin << ":" | 580 DVLOG(1) << "SearchModeChanged: [origin:mode] " << old_mode.origin << ":" |
522 << old_mode.mode << " to " << new_mode.origin << ":" | 581 << old_mode.mode << " to " << new_mode.origin << ":" |
523 << new_mode.mode; | 582 << new_mode.mode; |
524 search_mode_ = new_mode; | 583 search_mode_ = new_mode; |
525 | 584 |
526 if (new_mode.is_search_suggestions()) { | 585 if (new_mode.is_search_suggestions()) { |
527 // The preview is showing NTP content, but it's not appropriate anymore. | 586 // The preview is showing NTP content, but it's not appropriate anymore. |
528 if (model_.mode().is_ntp() && !new_mode.is_origin_ntp()) | 587 if (model_.mode().is_ntp() && !new_mode.is_origin_ntp()) { |
529 Hide(false); | 588 // Hide() clears |last_full_text_|. Restore it so that if the user just |
589 // switched to a tab with a partial query, we don't lose it. | |
590 string16 text = last_full_text_; | |
591 Hide(); | |
592 last_full_text_ = text; | |
593 } | |
530 } else { | 594 } else { |
531 Hide(true); | 595 Hide(); |
532 } | 596 } |
533 | 597 |
534 if (GetPreviewContents()) | 598 if (loader_) |
535 loader_->SearchModeChanged(new_mode); | 599 loader_->SearchModeChanged(new_mode); |
600 | |
601 ResetInstantTab(); | |
536 } | 602 } |
537 | 603 |
538 void InstantController::ActiveTabChanged() { | 604 void InstantController::ActiveTabChanged() { |
539 if (!extended_enabled_ && !instant_enabled_) | 605 if (!extended_enabled_ && !instant_enabled_) |
540 return; | 606 return; |
541 | 607 |
542 DVLOG(1) << "ActiveTabChanged"; | 608 DVLOG(1) << "ActiveTabChanged"; |
543 | 609 |
544 // By this time, SearchModeChanged() should've been called, so we only need to | 610 // By this time, SearchModeChanged() should've been called, so we only need to |
545 // handle the case when the search mode does NOT change, as in the case of | 611 // handle the case when the search mode does NOT change, as in the case of |
546 // going from search_suggestions to search_suggestions (i.e., partial queries | 612 // going from search_suggestions to search_suggestions (i.e., partial queries |
547 // on both old and new tabs). | 613 // on both old and new tabs). |
548 if (search_mode_.is_search_suggestions() && | 614 if (search_mode_.is_search_suggestions() && |
549 model_.mode().is_search_suggestions()) | 615 model_.mode().is_search_suggestions()) { |
550 Hide(false); | 616 // Hide() clears |last_full_text_|. Restore it so that if the user just |
617 // switched to a tab with a partial query, we don't lose it. | |
618 string16 text = last_full_text_; | |
619 Hide(); | |
620 last_full_text_ = text; | |
621 } | |
622 | |
623 if (extended_enabled_) | |
624 ResetInstantTab(); | |
551 } | 625 } |
552 | 626 |
553 void InstantController::SetInstantEnabled(bool instant_enabled) { | 627 void InstantController::SetInstantEnabled(bool instant_enabled) { |
554 instant_enabled_ = instant_enabled; | 628 instant_enabled_ = instant_enabled; |
555 if (!extended_enabled_ && !instant_enabled_) | 629 HideInternal(); |
556 DeleteLoader(); | 630 loader_.reset(); |
631 if (extended_enabled_ || instant_enabled_) | |
632 CreateDefaultLoader(); | |
557 } | 633 } |
558 | 634 |
559 void InstantController::ThemeChanged(const ThemeBackgroundInfo& theme_info) { | 635 void InstantController::ThemeChanged(const ThemeBackgroundInfo& theme_info) { |
560 if (!extended_enabled_) | 636 if (!extended_enabled_) |
561 return; | 637 return; |
562 | 638 |
563 if (GetPreviewContents()) | 639 if (loader_) |
564 loader_->SendThemeBackgroundInfo(theme_info); | 640 loader_->SendThemeBackgroundInfo(theme_info); |
565 } | 641 } |
566 | 642 |
567 void InstantController::ThemeAreaHeightChanged(int height) { | 643 void InstantController::ThemeAreaHeightChanged(int height) { |
568 if (!extended_enabled_) | 644 if (!extended_enabled_) |
569 return; | 645 return; |
570 | 646 |
571 if (GetPreviewContents()) | 647 if (loader_) |
572 loader_->SendThemeAreaHeight(height); | 648 loader_->SendThemeAreaHeight(height); |
573 } | 649 } |
574 | 650 |
575 void InstantController::SetSuggestions( | 651 void InstantController::SetSuggestions( |
576 InstantLoader* loader, | 652 const content::WebContents* contents, |
577 const std::vector<InstantSuggestion>& suggestions) { | 653 const std::vector<InstantSuggestion>& suggestions) { |
578 DVLOG(1) << "SetSuggestions"; | 654 DVLOG(1) << "SetSuggestions"; |
579 if (loader_ != loader || !search_mode_.is_search_suggestions()) | 655 |
656 // Ignore if we are not currently accepting search suggestions, or if they are | |
657 // coming from the wrong loader. | |
658 if (!search_mode_.is_search_suggestions() || | |
659 (instant_tab_ && instant_tab_->contents() != contents) || | |
660 (!instant_tab_ && loader_ && loader_->contents() != contents)) | |
samarth
2012/12/03 19:46:03
Yikes! The last two clauses appear all over the pl
sreeram
2012/12/04 08:10:52
Actually, it's just in two places - the only two c
| |
580 return; | 661 return; |
581 | 662 |
582 InstantSuggestion suggestion; | 663 InstantSuggestion suggestion; |
583 if (!suggestions.empty()) | 664 if (!suggestions.empty()) |
584 suggestion = suggestions[0]; | 665 suggestion = suggestions[0]; |
585 | 666 |
586 if (suggestion.behavior == INSTANT_COMPLETE_REPLACE) { | 667 if (suggestion.behavior == INSTANT_COMPLETE_REPLACE) { |
587 // We don't get an Update() when changing the omnibox due to a REPLACE | 668 // We don't get an Update() when changing the omnibox due to a REPLACE |
588 // suggestion (so that we don't inadvertently cause the preview to change | 669 // suggestion (so that we don't inadvertently cause the preview to change |
589 // what it's showing, as the user arrows up/down through the page-provided | 670 // what it's showing, as the user arrows up/down through the page-provided |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
639 DVLOG(1) << "SetInstantSuggestion: text='" << suggestion.text << "'" | 720 DVLOG(1) << "SetInstantSuggestion: text='" << suggestion.text << "'" |
640 << " behavior=" << suggestion.behavior << " type=" | 721 << " behavior=" << suggestion.behavior << " type=" |
641 << suggestion.type; | 722 << suggestion.type; |
642 browser_->SetInstantSuggestion(suggestion); | 723 browser_->SetInstantSuggestion(suggestion); |
643 } | 724 } |
644 } | 725 } |
645 | 726 |
646 Show(INSTANT_SHOWN_QUERY_SUGGESTIONS, 100, INSTANT_SIZE_PERCENT); | 727 Show(INSTANT_SHOWN_QUERY_SUGGESTIONS, 100, INSTANT_SIZE_PERCENT); |
647 } | 728 } |
648 | 729 |
649 void InstantController::CommitInstantLoader(InstantLoader* loader) { | 730 void InstantController::InstantSupportDetermined( |
650 if (loader_ == loader) | 731 const content::WebContents* contents, |
651 CommitIfCurrent(INSTANT_COMMIT_FOCUS_LOST); | 732 bool supports_instant) { |
733 if (instant_tab_ && instant_tab_->contents() == contents) { | |
734 if (!supports_instant) | |
Jered
2012/11/29 19:57:05
Wow, I hope not. Do we not want to update the blac
sreeram
2012/12/04 08:10:52
No. The blacklist is to make sure that we are not
| |
735 MessageLoop::current()->DeleteSoon(FROM_HERE, instant_tab_.release()); | |
736 return; | |
737 } | |
738 | |
739 if (loader_ && loader_->contents() == contents) { | |
740 if (supports_instant) { | |
741 blacklisted_urls_.erase(loader_->instant_url()); | |
742 } else { | |
743 ++blacklisted_urls_[loader_->instant_url()]; | |
744 HideInternal(); | |
745 delete loader_->ReleaseContents(); | |
746 MessageLoop::current()->DeleteSoon(FROM_HERE, loader_.release()); | |
747 CreateDefaultLoader(); | |
Jered
2012/11/29 19:57:05
Is this CreateDefaultLoader() call new? I think th
sreeram
2012/12/04 08:10:52
Yeah, the call is new. The infinite loop can't hap
| |
748 } | |
749 content::NotificationService::current()->Notify( | |
750 chrome::NOTIFICATION_INSTANT_SUPPORT_DETERMINED, | |
751 content::Source<InstantController>(this), | |
752 content::NotificationService::NoDetails()); | |
753 } | |
652 } | 754 } |
653 | 755 |
654 void InstantController::ShowInstantPreview(InstantLoader* loader, | 756 void InstantController::ShowInstantPreview(InstantShownReason reason, |
655 InstantShownReason reason, | |
656 int height, | 757 int height, |
657 InstantSizeUnits units) { | 758 InstantSizeUnits units) { |
658 if (loader_ == loader && extended_enabled_) | 759 if (extended_enabled_) |
659 Show(reason, height, units); | 760 Show(reason, height, units); |
660 } | 761 } |
661 | 762 |
662 void InstantController::InstantSupportDetermined(InstantLoader* loader, | 763 void InstantController::SwappedWebContents() { |
663 bool supports_instant) { | 764 model_.SetPreviewContents(GetPreviewContents()); |
664 if (supports_instant) { | |
665 blacklisted_urls_.erase(loader->instant_url()); | |
666 } else { | |
667 ++blacklisted_urls_[loader->instant_url()]; | |
668 if (loader_ == loader) | |
669 DeleteLoader(); | |
670 } | |
671 | |
672 content::NotificationService::current()->Notify( | |
673 chrome::NOTIFICATION_INSTANT_SUPPORT_DETERMINED, | |
674 content::Source<InstantController>(this), | |
675 content::NotificationService::NoDetails()); | |
676 } | 765 } |
677 | 766 |
678 void InstantController::SwappedTabContents(InstantLoader* loader) { | 767 void InstantController::InstantLoaderContentsFocused() { |
679 if (loader_ == loader) | |
680 model_.SetPreviewContents(GetPreviewContents()); | |
681 } | |
682 | |
683 void InstantController::InstantLoaderContentsFocused(InstantLoader* loader) { | |
684 #if defined(USE_AURA) | 768 #if defined(USE_AURA) |
685 // On aura the omnibox only receives a focus lost if we initiate the focus | 769 // On aura the omnibox only receives a focus lost if we initiate the focus |
686 // change. This does that. | 770 // change. This does that. |
687 if (loader_ == loader && !model_.mode().is_default()) | 771 if (!model_.mode().is_default()) |
688 browser_->InstantPreviewFocused(); | 772 browser_->InstantPreviewFocused(); |
689 #endif | 773 #endif |
690 } | 774 } |
691 | 775 |
692 bool InstantController::ResetLoader(const TemplateURL* template_url, | 776 bool InstantController::ResetLoader(const TemplateURL* template_url, |
693 const TabContents* active_tab) { | 777 const content::WebContents* active_tab) { |
694 std::string instant_url; | 778 std::string instant_url; |
695 if (!GetInstantURL(template_url, &instant_url)) | 779 if (!GetInstantURL(template_url, &instant_url)) |
696 return false; | 780 return false; |
697 | 781 |
698 if (GetPreviewContents() && loader_->instant_url() != instant_url) | 782 if (loader_ && loader_->instant_url() == instant_url) |
699 DeleteLoader(); | 783 return true; |
700 | 784 |
701 if (!GetPreviewContents()) { | 785 HideInternal(); |
702 loader_.reset(new InstantLoader(this, instant_url, active_tab)); | 786 loader_.reset(new InstantLoader(this, instant_url)); |
703 loader_->Init(); | 787 loader_->InitContents(active_tab); |
704 | 788 |
705 // Ensure the searchbox API has the correct theme-related info and context. | 789 // Ensure the searchbox API has the correct theme-related info and context. |
706 if (extended_enabled_) { | 790 if (extended_enabled_) { |
707 browser_->UpdateThemeInfoForPreview(); | 791 browser_->UpdateThemeInfoForPreview(); |
708 loader_->SearchModeChanged(search_mode_); | 792 loader_->SearchModeChanged(search_mode_); |
709 } | 793 } |
710 | 794 |
711 // Reset the loader timer. | 795 // Restart the stale loader timer. |
712 stale_loader_timer_.Start( | 796 stale_loader_timer_.Start(FROM_HERE, |
713 FROM_HERE, | 797 base::TimeDelta::FromMilliseconds(kStaleLoaderTimeoutMS), this, |
714 base::TimeDelta::FromMilliseconds(kStaleLoaderTimeoutMS), this, | 798 &InstantController::OnStaleLoader); |
715 &InstantController::OnStaleLoader); | |
716 } | |
717 | 799 |
718 return true; | 800 return true; |
719 } | 801 } |
720 | 802 |
721 bool InstantController::CreateDefaultLoader() { | 803 bool InstantController::CreateDefaultLoader() { |
722 // If there's no active tab, the browser is closing. | 804 // If there's no active tab, the browser is closing. |
723 const TabContents* active_tab = browser_->GetActiveTabContents(); | 805 const content::WebContents* active_tab = browser_->GetActiveWebContents(); |
724 if (!active_tab) | 806 if (!active_tab) |
725 return false; | 807 return false; |
726 | 808 |
727 const TemplateURL* template_url = | 809 const TemplateURL* template_url = TemplateURLServiceFactory::GetForProfile( |
728 TemplateURLServiceFactory::GetForProfile(active_tab->profile())-> | 810 Profile::FromBrowserContext(active_tab->GetBrowserContext()))-> |
729 GetDefaultSearchProvider(); | 811 GetDefaultSearchProvider(); |
730 | 812 |
731 return ResetLoader(template_url, active_tab); | 813 return ResetLoader(template_url, active_tab); |
732 } | 814 } |
733 | 815 |
734 void InstantController::OnStaleLoader() { | 816 void InstantController::OnStaleLoader() { |
735 // If the preview is showing or the omnibox has focus, don't delete the | 817 // If the preview is showing or the omnibox has focus, don't delete the |
736 // loader. It will get refreshed the next time the preview is hidden or the | 818 // loader. It will get refreshed the next time the preview is hidden or the |
737 // omnibox loses focus. | 819 // omnibox loses focus. |
738 if (!stale_loader_timer_.IsRunning() && !is_omnibox_focused_ && | 820 if (!stale_loader_timer_.IsRunning() && !is_omnibox_focused_ && |
739 model_.mode().is_default()) { | 821 model_.mode().is_default()) { |
740 DeleteLoader(); | 822 loader_.reset(); |
741 CreateDefaultLoader(); | 823 CreateDefaultLoader(); |
742 } | 824 } |
743 } | 825 } |
744 | 826 |
745 void InstantController::DeleteLoader() { | 827 void InstantController::ResetInstantTab() { |
746 // Clear all state, except |last_transition_type_| as it's used during commit. | 828 if (search_mode_.is_origin_search()) { |
747 last_user_text_.clear(); | 829 content::WebContents* active_tab = browser_->GetActiveWebContents(); |
748 last_full_text_.clear(); | 830 if (!instant_tab_ || active_tab != instant_tab_->contents()) |
749 last_verbatim_ = false; | 831 instant_tab_.reset(new InstantTab(this, active_tab)); |
750 last_suggestion_ = InstantSuggestion(); | 832 |
751 last_match_was_search_ = false; | 833 // We are now using |instant_tab_| instead of |loader_|, so Hide() the |
752 if (!extended_enabled_) | 834 // latter, but preserve |last_full_text_|. |
Jered
2012/11/29 19:57:05
This happens in 3 places. Perhaps extract a method
sreeram
2012/12/04 08:10:52
Ack. No longer repeated in the new patch.
| |
753 search_mode_.mode = chrome::search::Mode::MODE_DEFAULT; | 835 string16 text = last_full_text_; |
754 omnibox_bounds_ = gfx::Rect(); | 836 Hide(); |
755 last_omnibox_bounds_ = gfx::Rect(); | 837 last_full_text_ = text; |
756 update_bounds_timer_.Stop(); | 838 } else { |
757 stale_loader_timer_.Stop(); | 839 instant_tab_.reset(); |
758 url_for_history_ = GURL(); | 840 } |
759 first_interaction_time_ = base::Time(); | 841 } |
842 | |
843 void InstantController::Hide() { | |
844 HideInternal(); | |
845 OnStaleLoader(); | |
846 } | |
847 | |
848 void InstantController::HideInternal() { | |
849 DVLOG(1) << "Hide"; | |
850 | |
851 // If GetPreviewContents() returns NULL, either we're already in the desired | |
852 // MODE_DEFAULT state, or we're in the commit path. For the latter, don't | |
853 // change the state just yet; else we may hide the preview unnecessarily. | |
854 // Instead, the state will be set correctly after the commit is done. | |
760 if (GetPreviewContents()) { | 855 if (GetPreviewContents()) { |
761 model_.SetPreviewState(chrome::search::Mode(), 0, INSTANT_SIZE_PERCENT); | 856 model_.SetPreviewState(chrome::search::Mode(), 0, INSTANT_SIZE_PERCENT); |
762 loader_->CleanupPreviewContents(); | 857 |
858 // Send a message asking the preview to clear out old results. | |
859 if (!last_full_text_.empty()) { | |
860 last_full_text_.clear(); | |
861 loader_->Update(last_full_text_, true); | |
862 } | |
763 } | 863 } |
764 | 864 |
765 // Schedule the deletion for later, since we may have gotten here from a call | |
766 // within a |loader_| method (i.e., it's still on the stack). If we deleted | |
767 // the loader immediately, things would still be fine so long as the caller | |
768 // doesn't access any instance members after we return, but why rely on that? | |
769 MessageLoop::current()->DeleteSoon(FROM_HERE, loader_.release()); | |
770 } | |
771 | |
772 void InstantController::Hide(bool clear_query) { | |
773 DVLOG(1) << "Hide: clear_query=" << clear_query; | |
774 | |
775 // The only time when the preview is not already in the desired MODE_DEFAULT | |
776 // state and GetPreviewContents() returns NULL is when we are in the commit | |
777 // path. In that case, don't change the state just yet; otherwise we may | |
778 // cause the preview to hide unnecessarily. Instead, the state will be set | |
779 // correctly after the commit is done. | |
780 if (GetPreviewContents()) | |
781 model_.SetPreviewState(chrome::search::Mode(), 0, INSTANT_SIZE_PERCENT); | |
782 | |
783 // Clear the first interaction timestamp for later use. | 865 // Clear the first interaction timestamp for later use. |
784 first_interaction_time_ = base::Time(); | 866 first_interaction_time_ = base::Time(); |
785 | |
786 if (clear_query) { | |
787 if (GetPreviewContents() && !last_full_text_.empty()) | |
788 loader_->Update(string16(), true); | |
789 last_user_text_.clear(); | |
790 last_full_text_.clear(); | |
791 } | |
792 | |
793 OnStaleLoader(); | |
794 } | 867 } |
795 | 868 |
796 void InstantController::Show(InstantShownReason reason, | 869 void InstantController::Show(InstantShownReason reason, |
797 int height, | 870 int height, |
798 InstantSizeUnits units) { | 871 InstantSizeUnits units) { |
872 if (instant_tab_) | |
873 return; | |
874 | |
799 DVLOG(1) << "Show: reason=" << reason << " height=" << height << " units=" | 875 DVLOG(1) << "Show: reason=" << reason << " height=" << height << " units=" |
800 << units; | 876 << units; |
801 | 877 |
802 // Must be on NTP to show NTP content. | 878 // Must be on NTP to show NTP content. |
803 if (reason == INSTANT_SHOWN_CUSTOM_NTP_CONTENT && !search_mode_.is_ntp()) | 879 if (reason == INSTANT_SHOWN_CUSTOM_NTP_CONTENT && !search_mode_.is_ntp()) |
804 return; | 880 return; |
805 | 881 |
806 // Must have updated omnibox after most recent Hide() to show suggestions. | 882 // Must have updated omnibox after most recent Hide() to show suggestions. |
807 if (reason == INSTANT_SHOWN_QUERY_SUGGESTIONS && | 883 if (reason == INSTANT_SHOWN_QUERY_SUGGESTIONS && |
808 !search_mode_.is_search_suggestions()) | 884 !search_mode_.is_search_suggestions()) |
809 return; | 885 return; |
810 | 886 |
811 // If the preview is being shown because of the first set of suggestions to | 887 // If the preview is being shown for the first time since the user started |
812 // arrive for this query editing session, record a histogram value. | 888 // typing, record a histogram value. |
813 if (!first_interaction_time_.is_null() && model_.mode().is_default()) { | 889 if (!first_interaction_time_.is_null() && model_.mode().is_default()) { |
814 base::TimeDelta delta = base::Time::Now() - first_interaction_time_; | 890 base::TimeDelta delta = base::Time::Now() - first_interaction_time_; |
815 UMA_HISTOGRAM_TIMES("Instant.TimeToFirstShow", delta); | 891 UMA_HISTOGRAM_TIMES("Instant.TimeToFirstShow", delta); |
816 } | 892 } |
817 | 893 |
818 // Show at 100% height except in the following cases: | 894 // Show at 100% height except in the following cases: |
819 // - The page wants to hide (height=0). | 895 // - The page wants to hide (height=0). |
820 // - The page wants to show custom NTP content. | 896 // - The page wants to show custom NTP content. |
821 // - The page is over a website other than search or an NTP, and is not | 897 // - The page is over a website other than search or an NTP, and is not |
822 // already showing at 100% height. | 898 // already showing at 100% height. |
823 const bool is_full_height = | 899 bool is_full_height = model_.height() == 100 && |
824 model_.height() == 100 && model_.height_units() == INSTANT_SIZE_PERCENT; | 900 model_.height_units() == INSTANT_SIZE_PERCENT; |
825 if (height == 0 || | 901 if (height == 0 || |
826 reason == INSTANT_SHOWN_CUSTOM_NTP_CONTENT || | 902 reason == INSTANT_SHOWN_CUSTOM_NTP_CONTENT || |
827 (search_mode_.is_origin_default() && !is_full_height)) | 903 (search_mode_.is_origin_default() && !is_full_height)) |
828 model_.SetPreviewState(search_mode_, height, units); | 904 model_.SetPreviewState(search_mode_, height, units); |
829 else | 905 else |
830 model_.SetPreviewState(search_mode_, 100, INSTANT_SIZE_PERCENT); | 906 model_.SetPreviewState(search_mode_, 100, INSTANT_SIZE_PERCENT); |
831 } | 907 } |
832 | 908 |
833 void InstantController::SendBoundsToPage() { | 909 void InstantController::SendBoundsToPage() { |
834 if (last_omnibox_bounds_ == omnibox_bounds_ || | 910 if (last_omnibox_bounds_ == omnibox_bounds_ || !loader_ || |
835 !GetPreviewContents() || loader_->IsPointerDownFromActivate()) | 911 loader_->is_pointer_down_from_activate()) |
836 return; | 912 return; |
837 | 913 |
838 last_omnibox_bounds_ = omnibox_bounds_; | 914 last_omnibox_bounds_ = omnibox_bounds_; |
839 gfx::Rect preview_bounds = browser_->GetInstantBounds(); | 915 gfx::Rect preview_bounds = browser_->GetInstantBounds(); |
840 gfx::Rect intersection = gfx::IntersectRects(omnibox_bounds_, preview_bounds); | 916 gfx::Rect intersection = gfx::IntersectRects(omnibox_bounds_, preview_bounds); |
841 | 917 |
842 // Translate into window coordinates. | 918 // Translate into window coordinates. |
843 if (!intersection.IsEmpty()) { | 919 if (!intersection.IsEmpty()) { |
844 intersection.Offset(-preview_bounds.origin().x(), | 920 intersection.Offset(-preview_bounds.origin().x(), |
845 -preview_bounds.origin().y()); | 921 -preview_bounds.origin().y()); |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
879 | 955 |
880 // Extended mode should always use HTTPS. TODO(sreeram): This section can be | 956 // Extended mode should always use HTTPS. TODO(sreeram): This section can be |
881 // removed if TemplateURLs supported "https://{google:host}/..." instead of | 957 // removed if TemplateURLs supported "https://{google:host}/..." instead of |
882 // only supporting "{google:baseURL}...". | 958 // only supporting "{google:baseURL}...". |
883 if (extended_enabled_) { | 959 if (extended_enabled_) { |
884 GURL url_obj(*instant_url); | 960 GURL url_obj(*instant_url); |
885 if (!url_obj.is_valid()) | 961 if (!url_obj.is_valid()) |
886 return false; | 962 return false; |
887 | 963 |
888 if (!url_obj.SchemeIsSecure()) { | 964 if (!url_obj.SchemeIsSecure()) { |
889 const std::string new_scheme = "https"; | 965 std::string new_scheme = "https"; |
Jered
2012/11/29 19:57:05
There is probably a constant for this somewhere.
sreeram
2012/12/04 08:10:52
I'd think so too, but I couldn't find one!
| |
890 const std::string new_port = "443"; | 966 std::string new_port = "443"; |
Jered
2012/11/29 19:57:05
This too I'd expect.
sreeram
2012/12/04 08:10:52
Likewise. (Note that this is worse, since it needs
| |
891 GURL::Replacements secure; | 967 GURL::Replacements secure; |
892 secure.SetSchemeStr(new_scheme); | 968 secure.SetSchemeStr(new_scheme); |
893 secure.SetPortStr(new_port); | 969 secure.SetPortStr(new_port); |
894 url_obj = url_obj.ReplaceComponents(secure); | 970 url_obj = url_obj.ReplaceComponents(secure); |
895 | 971 |
896 if (!url_obj.is_valid()) | 972 if (!url_obj.is_valid()) |
897 return false; | 973 return false; |
898 | 974 |
899 *instant_url = url_obj.spec(); | 975 *instant_url = url_obj.spec(); |
900 } | 976 } |
901 } | 977 } |
902 | 978 |
903 std::map<std::string, int>::const_iterator iter = | 979 std::map<std::string, int>::const_iterator iter = |
904 blacklisted_urls_.find(*instant_url); | 980 blacklisted_urls_.find(*instant_url); |
905 if (iter != blacklisted_urls_.end() && | 981 if (iter != blacklisted_urls_.end() && |
906 iter->second > kMaxInstantSupportFailures) | 982 iter->second > kMaxInstantSupportFailures) |
907 return false; | 983 return false; |
908 | 984 |
909 return true; | 985 return true; |
910 } | 986 } |
OLD | NEW |