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

Side by Side Diff: chrome/browser/instant/instant_controller.cc

Issue 11421079: Persist the Instant API to committed search result pages. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Kittens live! Created 8 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright 2012 The Chromium Authors. All rights reserved. 1 // Copyright 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/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
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
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
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698