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

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

Powered by Google App Engine
This is Rietveld 408576698