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

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

Issue 10836031: Remove Instant v1 API. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Update history, title, favicon Created 8 years, 4 months 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 (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 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/bind.h"
8 #include "base/command_line.h" 7 #include "base/command_line.h"
9 #include "base/message_loop.h" 8 #include "base/i18n/case_conversion.h"
10 #include "base/metrics/histogram.h" 9 #include "base/metrics/histogram.h"
11 #include "build/build_config.h"
12 #include "chrome/browser/autocomplete/autocomplete_match.h" 10 #include "chrome/browser/autocomplete/autocomplete_match.h"
11 #include "chrome/browser/history/history.h"
12 #include "chrome/browser/history/history_service_factory.h"
13 #include "chrome/browser/history/history_tab_helper.h"
13 #include "chrome/browser/instant/instant_controller_delegate.h" 14 #include "chrome/browser/instant/instant_controller_delegate.h"
14 #include "chrome/browser/instant/instant_loader.h" 15 #include "chrome/browser/instant/instant_loader.h"
15 #include "chrome/browser/platform_util.h" 16 #include "chrome/browser/platform_util.h"
16 #include "chrome/browser/prefs/pref_service.h" 17 #include "chrome/browser/prefs/pref_service.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "chrome/browser/search_engines/template_url.h"
19 #include "chrome/browser/search_engines/template_url_service.h" 18 #include "chrome/browser/search_engines/template_url_service.h"
20 #include "chrome/browser/search_engines/template_url_service_factory.h" 19 #include "chrome/browser/search_engines/template_url_service_factory.h"
21 #include "chrome/browser/ui/blocked_content/blocked_content_tab_helper.h"
22 #include "chrome/browser/ui/tab_contents/tab_contents.h" 20 #include "chrome/browser/ui/tab_contents/tab_contents.h"
23 #include "chrome/common/chrome_notification_types.h" 21 #include "chrome/common/chrome_notification_types.h"
24 #include "chrome/common/chrome_switches.h" 22 #include "chrome/common/chrome_switches.h"
25 #include "chrome/common/pref_names.h" 23 #include "chrome/common/pref_names.h"
24 #include "content/public/browser/favicon_status.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 "content/public/browser/web_contents.h" 28 #include "content/public/browser/web_contents.h"
29 #include "ui/gfx/codec/png_codec.h"
29 30
30 #if defined(TOOLKIT_VIEWS) 31 #if defined(TOOLKIT_VIEWS)
31 #include "ui/views/focus/focus_manager.h"
32 #include "ui/views/view.h"
33 #include "ui/views/widget/widget.h" 32 #include "ui/views/widget/widget.h"
34 #endif 33 #endif
35 34
35 namespace {
36
37 enum PreviewUsageType {
38 PREVIEW_CREATED = 0,
39 PREVIEW_DELETED,
40 PREVIEW_LOADED,
41 PREVIEW_SHOWED,
42 PREVIEW_COMMITTED,
43 PREVIEW_NUM_TYPES,
44 };
45
46 // An artificial delay (in milliseconds) we introduce before telling the Instant
47 // page about the new omnibox bounds, in cases where the bounds shrink. This is
48 // to avoid the page jumping up/down very fast in response to bounds changes.
49 const int kUpdateBoundsDelayMS = 1000;
50
51 // The maximum number of times we'll load a non-Instant-supporting search engine
52 // before we give up and blacklist it for the rest of the browsing session.
53 const int kMaxInstantSupportFailures = 10;
54
55 std::string ModeToString(InstantController::Mode mode) {
56 switch (mode) {
57 case InstantController::INSTANT: return "_Instant";
58 case InstantController::SUGGEST: return "_Suggest";
59 case InstantController::HIDDEN: return "_Hidden";
60 case InstantController::SILENT: return "_Silent";
61 }
62
63 NOTREACHED();
64 return std::string();
65 }
66
67 void AddPreviewUsageForHistogram(InstantController::Mode mode,
68 PreviewUsageType usage) {
69 DCHECK(0 <= usage && usage < PREVIEW_NUM_TYPES) << usage;
70 base::Histogram* histogram = base::LinearHistogram::FactoryGet(
71 "Instant.Previews" + ModeToString(mode), 1, PREVIEW_NUM_TYPES,
72 PREVIEW_NUM_TYPES + 1, base::Histogram::kUmaTargetedHistogramFlag);
73 histogram->Add(usage);
74 }
75
76 void AddSessionStorageHistogram(InstantController::Mode mode,
77 const TabContents* tab1,
78 const TabContents* tab2) {
79 base::Histogram* histogram = base::BooleanHistogram::FactoryGet(
80 "Instant.SessionStorageNamespace" + ModeToString(mode),
81 base::Histogram::kUmaTargetedHistogramFlag);
82 histogram->AddBoolean(
83 tab1->web_contents()->GetController().GetSessionStorageNamespace() ==
84 tab2->web_contents()->GetController().GetSessionStorageNamespace());
85 }
86
87 } // namespace
88
36 InstantController::InstantController(InstantControllerDelegate* delegate, 89 InstantController::InstantController(InstantControllerDelegate* delegate,
37 Mode mode) 90 Mode mode)
38 : delegate_(delegate), 91 : delegate_(delegate),
39 is_displayable_(false), 92 mode_(mode),
40 is_out_of_date_(true), 93 last_active_tab_(NULL),
41 commit_on_pointer_release_(false), 94 last_verbatim_(false),
95 last_complete_behavior_(INSTANT_COMPLETE_NOW),
42 last_transition_type_(content::PAGE_TRANSITION_LINK), 96 last_transition_type_(content::PAGE_TRANSITION_LINK),
43 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)), 97 is_showing_(false),
44 mode_(mode) { 98 loader_processed_last_update_(false) {
45 DCHECK(mode_ == INSTANT || mode_ == SUGGEST || mode_ == HIDDEN ||
46 mode_ == SILENT);
47 } 99 }
48 100
49 InstantController::~InstantController() { 101 InstantController::~InstantController() {
102 if (GetPreviewContents())
103 AddPreviewUsageForHistogram(mode_, PREVIEW_DELETED);
50 } 104 }
51 105
52 // static 106 // static
53 void InstantController::RegisterUserPrefs(PrefService* prefs) { 107 void InstantController::RegisterUserPrefs(PrefService* prefs) {
54 prefs->RegisterBooleanPref(prefs::kInstantConfirmDialogShown, 108 prefs->RegisterBooleanPref(prefs::kInstantConfirmDialogShown, false,
55 false,
56 PrefService::SYNCABLE_PREF); 109 PrefService::SYNCABLE_PREF);
57 prefs->RegisterBooleanPref(prefs::kInstantEnabled, 110 prefs->RegisterBooleanPref(prefs::kInstantEnabled, false,
58 false,
59 PrefService::SYNCABLE_PREF); 111 PrefService::SYNCABLE_PREF);
60 112
61 // TODO(jamescook): Move this to search controller. 113 // TODO(jamescook): Move this to search controller.
62 prefs->RegisterDoublePref(prefs::kInstantAnimationScaleFactor, 114 prefs->RegisterDoublePref(prefs::kInstantAnimationScaleFactor,
63 1.0, 115 1.0,
64 PrefService::UNSYNCABLE_PREF); 116 PrefService::UNSYNCABLE_PREF);
65 } 117 }
66 118
67 // static 119 // static
68 void InstantController::RecordMetrics(Profile* profile) {
69 UMA_HISTOGRAM_ENUMERATION("Instant.Status", IsEnabled(profile), 2);
70 }
71
72 // static
73 bool InstantController::IsEnabled(Profile* profile) { 120 bool InstantController::IsEnabled(Profile* profile) {
74 const PrefService* prefs = profile->GetPrefs(); 121 const PrefService* prefs = profile ? profile->GetPrefs() : NULL;
75 return prefs && prefs->GetBoolean(prefs::kInstantEnabled); 122 return prefs && prefs->GetBoolean(prefs::kInstantEnabled);
76 } 123 }
77 124
78 // static
79 void InstantController::Enable(Profile* profile) {
80 PrefService* prefs = profile->GetPrefs();
81 if (!prefs)
82 return;
83
84 prefs->SetBoolean(prefs::kInstantEnabled, true);
85 prefs->SetBoolean(prefs::kInstantConfirmDialogShown, true);
86 UMA_HISTOGRAM_ENUMERATION("Instant.Preference", 1, 2);
87 }
88
89 // static
90 void InstantController::Disable(Profile* profile) {
91 PrefService* prefs = profile->GetPrefs();
92 if (!prefs)
93 return;
94
95 prefs->SetBoolean(prefs::kInstantEnabled, false);
96 UMA_HISTOGRAM_ENUMERATION("Instant.Preference", 0, 2);
97 }
98
99 bool InstantController::Update(const AutocompleteMatch& match, 125 bool InstantController::Update(const AutocompleteMatch& match,
100 const string16& user_text, 126 const string16& user_text,
101 bool verbatim, 127 bool verbatim,
102 string16* suggested_text) { 128 string16* suggested_text,
129 InstantCompleteBehavior* complete_behavior) {
130 const TabContents* active_tab = delegate_->GetActiveTabContents();
131
132 // We could get here with no active tab if the Browser is closing.
133 if (!active_tab) {
134 Hide();
135 return false;
136 }
137
138 std::string instant_url;
139 Profile* profile = active_tab->profile();
140
141 // If the match's TemplateURL isn't valid, it is likely not a query.
142 if (!GetInstantURL(match.GetTemplateURL(profile), &instant_url)) {
143 Hide();
144 return false;
145 }
146
147 string16 full_text = user_text + *suggested_text;
148
149 if (full_text.empty()) {
150 Hide();
151 return false;
152 }
153
154 // The presence of any suggested_text implies verbatim.
155 DCHECK(suggested_text->empty() || verbatim)
156 << user_text << "|" << *suggested_text;
157
158 ResetLoader(instant_url, active_tab);
159 last_active_tab_ = active_tab;
160
161 // Track the non-Instant search URL for this query.
162 url_for_history_ = match.destination_url;
163 last_transition_type_ = match.transition;
164
165 last_user_text_ = user_text;
166
167 // Don't send an update to the loader if the query text hasn't changed.
168 if (full_text == last_full_text_ && verbatim == last_verbatim_) {
169 // Since we are updating |suggested_text|, shouldn't we also update
170 // |last_full_text_|? No. There's no guarantee that our suggestion will
171 // actually be inline autocompleted. For example, it may get trumped by
172 // a history suggestion. If our suggestion does make it, the omnibox will
173 // call Update() again, at which time we'll update |last_full_text_|.
174 *suggested_text = last_suggestion_;
175 *complete_behavior = last_complete_behavior_;
176
177 // We need to call Show() here because of this:
178 // 1. User has typed a query (say Q). Instant overlay is showing results.
179 // 2. User arrows-down to a URL entry or erases all omnibox text. Both of
180 // these cause the overlay to Hide().
181 // 3. User arrows-up to Q or types Q again. The last text we processed is
182 // still Q, so we don't Update() the loader, but we do need to Show().
183 if (loader_processed_last_update_ && mode_ == INSTANT)
184 Show();
185 return true;
186 }
187
188 last_full_text_ = full_text;
189 last_verbatim_ = verbatim;
190 loader_processed_last_update_ = false;
191
192 // Reset the last suggestion, as it's no longer valid.
103 suggested_text->clear(); 193 suggested_text->clear();
104 194 last_suggestion_.clear();
105 is_out_of_date_ = false; 195 *complete_behavior = last_complete_behavior_ = INSTANT_COMPLETE_NOW;
106 commit_on_pointer_release_ = false; 196
107 last_transition_type_ = match.transition; 197 if (mode_ != SILENT) {
108 last_url_ = match.destination_url; 198 loader_->Update(last_full_text_, last_verbatim_);
109 last_user_text_ = user_text; 199
110 200 content::NotificationService::current()->Notify(
111 TabContents* tab_contents = delegate_->GetInstantHostTabContents(); 201 chrome::NOTIFICATION_INSTANT_CONTROLLER_UPDATED,
112 if (!tab_contents) { 202 content::Source<InstantController>(this),
113 Hide(); 203 content::NotificationService::NoDetails());
114 return false; 204 }
115 } 205
116
117 Profile* profile = tab_contents->profile();
118 const TemplateURL* template_url = match.GetTemplateURL(profile);
119 const TemplateURL* default_t_url =
120 TemplateURLServiceFactory::GetForProfile(profile)
121 ->GetDefaultSearchProvider();
122 if (!IsValidInstantTemplateURL(template_url) || !default_t_url ||
123 template_url->id() != default_t_url->id()) {
124 Hide();
125 return false;
126 }
127
128 if (!loader_.get() || loader_->template_url_id() != template_url->id())
129 loader_.reset(new InstantLoader(this, template_url->id(), std::string()));
130
131 if (mode_ == SILENT) {
132 // For the SILENT mode, we process |user_text| at commit time.
133 loader_->MaybeLoadInstantURL(tab_contents, template_url);
134 return true;
135 }
136
137 UpdateLoader(tab_contents, template_url, match.destination_url,
138 match.transition, user_text, verbatim, suggested_text);
139
140 content::NotificationService::current()->Notify(
141 chrome::NOTIFICATION_INSTANT_CONTROLLER_UPDATED,
142 content::Source<InstantController>(this),
143 content::NotificationService::NoDetails());
144 return true; 206 return true;
145 } 207 }
146 208
209 // TODO(tonyg): This method only fires when the omnibox bounds change. It also
210 // needs to fire when the preview bounds change (e.g.: open/close info bar).
147 void InstantController::SetOmniboxBounds(const gfx::Rect& bounds) { 211 void InstantController::SetOmniboxBounds(const gfx::Rect& bounds) {
148 if (omnibox_bounds_ == bounds) 212 if (omnibox_bounds_ == bounds || mode_ != INSTANT)
149 return; 213 return;
150 214
151 // Always track the omnibox bounds. That way if Update is later invoked the
152 // bounds are in sync.
153 omnibox_bounds_ = bounds; 215 omnibox_bounds_ = bounds;
154 216 if (omnibox_bounds_.height() > last_omnibox_bounds_.height()) {
155 if (loader_.get() && !is_out_of_date_ && mode_ == INSTANT) 217 update_bounds_timer_.Stop();
156 loader_->SetOmniboxBounds(bounds); 218 SendBoundsToPage();
157 } 219 } else if (!update_bounds_timer_.IsRunning()) {
158 220 update_bounds_timer_.Start(FROM_HERE,
159 void InstantController::DestroyPreviewContents() { 221 base::TimeDelta::FromMilliseconds(kUpdateBoundsDelayMS), this,
160 if (!loader_.get()) { 222 &InstantController::SendBoundsToPage);
161 // We're not showing anything, nothing to do. 223 }
162 return; 224 }
163 } 225
164 226 TabContents* InstantController::GetPreviewContents() const {
165 if (is_displayable_) { 227 return loader_.get() ? loader_->preview_contents() : NULL;
166 is_displayable_ = false; 228 }
229
230 void InstantController::Hide() {
231 last_active_tab_ = NULL;
232 if (is_showing_) {
233 is_showing_ = false;
167 delegate_->HideInstant(); 234 delegate_->HideInstant();
168 } 235 }
169 delete ReleasePreviewContents(INSTANT_COMMIT_DESTROY, NULL);
170 }
171
172 void InstantController::Hide() {
173 is_out_of_date_ = true;
174 commit_on_pointer_release_ = false;
175 if (is_displayable_) {
176 is_displayable_ = false;
177 delegate_->HideInstant();
178 }
179 } 236 }
180 237
181 bool InstantController::IsCurrent() const { 238 bool InstantController::IsCurrent() const {
182 // TODO(mmenke): See if we can do something more intelligent in the 239 DCHECK(IsOutOfDate() || GetPreviewContents());
183 // navigation pending case. 240 return !IsOutOfDate() && GetPreviewContents() && loader_->supports_instant();
184 return is_displayable_ && !loader_->IsNavigationPending() &&
185 !loader_->needs_reload();
186 }
187
188 bool InstantController::PrepareForCommit() {
189 if (is_out_of_date_ || !loader_.get())
190 return false;
191
192 // If we are in the visible (INSTANT) mode, return the status of the preview.
193 if (mode_ == INSTANT)
194 return IsCurrent();
195
196 TabContents* tab_contents = delegate_->GetInstantHostTabContents();
197 if (!tab_contents)
198 return false;
199
200 const TemplateURL* template_url =
201 TemplateURLServiceFactory::GetForProfile(tab_contents->profile())
202 ->GetDefaultSearchProvider();
203 if (!IsValidInstantTemplateURL(template_url) ||
204 loader_->template_url_id() != template_url->id() ||
205 loader_->IsNavigationPending() ||
206 loader_->is_determining_if_page_supports_instant()) {
207 return false;
208 }
209
210 // In the SUGGEST and HIDDEN modes, we must have sent an Update() by now, so
211 // check if the loader failed to process it.
212 if ((mode_ == SUGGEST || mode_ == HIDDEN)
213 && (!loader_->ready() || !loader_->http_status_ok())) {
214 return false;
215 }
216
217 // Ignore the suggested text, as we are about to commit the verbatim query.
218 string16 suggested_text;
219 UpdateLoader(tab_contents, template_url, last_url_, last_transition_type_,
220 last_user_text_, true, &suggested_text);
221 return true;
222 } 241 }
223 242
224 TabContents* InstantController::CommitCurrentPreview(InstantCommitType type) { 243 TabContents* InstantController::CommitCurrentPreview(InstantCommitType type) {
225 DCHECK(loader_.get()); 244 const TabContents* active_tab = delegate_->GetActiveTabContents();
226 TabContents* tab_contents = delegate_->GetInstantHostTabContents(); 245 TabContents* preview = ReleasePreviewContents(type);
227 DCHECK(tab_contents); 246 AddSessionStorageHistogram(mode_, active_tab, preview);
228 TabContents* preview = ReleasePreviewContents(type, tab_contents);
229 preview->web_contents()->GetController().CopyStateFromAndPrune( 247 preview->web_contents()->GetController().CopyStateFromAndPrune(
230 &tab_contents->web_contents()->GetController()); 248 &active_tab->web_contents()->GetController());
231 delegate_->CommitInstant(preview); 249 delegate_->CommitInstant(preview);
232 CompleteRelease(preview);
233 return preview; 250 return preview;
234 } 251 }
235 252
236 bool InstantController::CommitIfCurrent() { 253 TabContents* InstantController::ReleasePreviewContents(InstantCommitType type) {
237 if (IsCurrent()) { 254 TabContents* preview = loader_->ReleasePreviewContents(type, last_full_text_);
238 CommitCurrentPreview(INSTANT_COMMIT_PRESSED_ENTER); 255
239 return true; 256 // If the preview page has navigated since the last Update(), we need to add
240 } 257 // the navigation to history ourselves. Else, the page will navigate after
241 return false; 258 // commit, and it will be added to history in the usual manner.
242 } 259 scoped_refptr<history::HistoryAddPageArgs> last_navigation =
243 260 loader_->last_navigation();
244 void InstantController::SetCommitOnPointerRelease() { 261 if (last_navigation != NULL) {
245 commit_on_pointer_release_ = true; 262 content::NavigationEntry* entry =
246 } 263 preview->web_contents()->GetController().GetActiveEntry();
247 264 DCHECK_EQ(last_navigation->url, entry->GetURL());
248 bool InstantController::IsPointerDownFromActivate() { 265
249 DCHECK(loader_.get()); 266 // Add the page to history.
250 return loader_->IsPointerDownFromActivate(); 267 preview->history_tab_helper()->UpdateHistoryForNavigation(last_navigation);
251 } 268
252 269 // Update the page title.
253 #if defined(OS_MACOSX) 270 preview->history_tab_helper()->UpdateHistoryPageTitle(*entry);
271
272 // Update the favicon.
273 FaviconService* favicon_service =
274 preview->profile()->GetFaviconService(Profile::EXPLICIT_ACCESS);
275 if (favicon_service && entry->GetFavicon().valid &&
276 entry->GetFavicon().image.IsEmpty()) {
277 std::vector<unsigned char> image_data;
278 // TODO: Add all variants once the history service supports it.
279 gfx::PNGCodec::EncodeBGRASkBitmap(
280 entry->GetFavicon().image.AsBitmap(), false, &image_data);
281 favicon_service->SetFavicon(entry->GetURL(),
282 entry->GetFavicon().url,
283 image_data,
284 history::FAVICON);
285 }
286 }
287
288 // Add a fake history entry with a non-Instant search URL, so that search
289 // terms extraction (for autocomplete history matches) works.
290 HistoryService* history = HistoryServiceFactory::GetForProfile(
291 preview->profile(), Profile::EXPLICIT_ACCESS);
292 if (history) {
293 history->AddPage(url_for_history_, NULL, 0, GURL(), last_transition_type_,
294 history::RedirectList(), history::SOURCE_BROWSED, false);
295 }
296
297 AddPreviewUsageForHistogram(mode_, PREVIEW_COMMITTED);
298
299 // We may have gotten here from CommitInstant(), which means the loader may
300 // still be on the stack. So, schedule a destruction for later.
301 MessageLoop::current()->DeleteSoon(FROM_HERE, loader_.release());
302
303 // This call is here to hide the preview and reset view state. It won't
304 // actually delete |loader_| because it was just released to DeleteSoon().
305 DeleteLoader();
306
307 return preview;
308 }
309
310 // TODO(sreeram): Since we never delete the loader except when committing
311 // Instant, the loader may have a very stale page. Reload it when stale.
254 void InstantController::OnAutocompleteLostFocus( 312 void InstantController::OnAutocompleteLostFocus(
255 gfx::NativeView view_gaining_focus) { 313 gfx::NativeView view_gaining_focus) {
256 // If |IsPointerDownFromActivate()| returns false, the RenderWidgetHostView 314 DCHECK(!is_showing_ || GetPreviewContents());
257 // did not receive a mouseDown event. Therefore, we should destroy the 315
258 // preview. Otherwise, the RWHV was clicked, so we commit the preview. 316 // If the preview is not showing, nothing to do.
259 if (!IsCurrent() || !IsPointerDownFromActivate()) 317 if (!is_showing_ || !GetPreviewContents())
260 DestroyPreviewContents(); 318 return;
261 else 319
262 SetCommitOnPointerRelease(); 320 #if defined(OS_MACOSX)
263 } 321 if (!loader_->IsPointerDownFromActivate())
322 Hide();
264 #else 323 #else
265 void InstantController::OnAutocompleteLostFocus(
266 gfx::NativeView view_gaining_focus) {
267 if (!IsCurrent()) {
268 DestroyPreviewContents();
269 return;
270 }
271
272 content::RenderWidgetHostView* rwhv = 324 content::RenderWidgetHostView* rwhv =
273 GetPreviewContents()->web_contents()->GetRenderWidgetHostView(); 325 GetPreviewContents()->web_contents()->GetRenderWidgetHostView();
274 if (!view_gaining_focus || !rwhv) { 326 if (!view_gaining_focus || !rwhv) {
275 DestroyPreviewContents(); 327 Hide();
276 return; 328 return;
277 } 329 }
278 330
279 #if defined(TOOLKIT_VIEWS) 331 #if defined(TOOLKIT_VIEWS)
280 // For views the top level widget is always focused. If the focus change 332 // For views the top level widget is always focused. If the focus change
281 // originated in views determine the child Widget from the view that is being 333 // originated in views determine the child Widget from the view that is being
282 // focused. 334 // focused.
283 views::Widget* widget = 335 views::Widget* widget =
284 views::Widget::GetWidgetForNativeView(view_gaining_focus); 336 views::Widget::GetWidgetForNativeView(view_gaining_focus);
285 if (widget) { 337 if (widget) {
286 views::FocusManager* focus_manager = widget->GetFocusManager(); 338 views::FocusManager* focus_manager = widget->GetFocusManager();
287 if (focus_manager && focus_manager->is_changing_focus() && 339 if (focus_manager && focus_manager->is_changing_focus() &&
288 focus_manager->GetFocusedView() && 340 focus_manager->GetFocusedView() &&
289 focus_manager->GetFocusedView()->GetWidget()) { 341 focus_manager->GetFocusedView()->GetWidget()) {
290 view_gaining_focus = 342 view_gaining_focus =
291 focus_manager->GetFocusedView()->GetWidget()->GetNativeView(); 343 focus_manager->GetFocusedView()->GetWidget()->GetNativeView();
292 } 344 }
293 } 345 }
294 #endif 346 #endif
295 347
296 gfx::NativeView tab_view = 348 gfx::NativeView tab_view =
297 GetPreviewContents()->web_contents()->GetNativeView(); 349 GetPreviewContents()->web_contents()->GetNativeView();
350
298 // Focus is going to the renderer. 351 // Focus is going to the renderer.
299 if (rwhv->GetNativeView() == view_gaining_focus || 352 if (rwhv->GetNativeView() == view_gaining_focus ||
300 tab_view == view_gaining_focus) { 353 tab_view == view_gaining_focus) {
301 if (!IsPointerDownFromActivate()) {
302 // If the mouse is not down, focus is not going to the renderer. Someone
303 // else moved focus and we shouldn't commit.
304 DestroyPreviewContents();
305 return;
306 }
307 354
308 // We're showing instant results. As instant results may shift when 355 // If the mouse is not down, focus is not going to the renderer. Someone
309 // committing we commit on the mouse up. This way a slow click still works 356 // else moved focus and we shouldn't commit.
310 // fine. 357 if (!loader_->IsPointerDownFromActivate())
311 SetCommitOnPointerRelease(); 358 Hide();
359
312 return; 360 return;
313 } 361 }
314 362
315 // Walk up the view hierarchy. If the view gaining focus is a subview of the 363 // Walk up the view hierarchy. If the view gaining focus is a subview of the
316 // WebContents view (such as a windowed plugin or http auth dialog), we want 364 // WebContents view (such as a windowed plugin or http auth dialog), we want
317 // to keep the preview contents. Otherwise, focus has gone somewhere else, 365 // to keep the preview contents. Otherwise, focus has gone somewhere else,
318 // such as the JS inspector, and we want to cancel the preview. 366 // such as the JS inspector, and we want to cancel the preview.
319 gfx::NativeView view_gaining_focus_ancestor = view_gaining_focus; 367 gfx::NativeView view_gaining_focus_ancestor = view_gaining_focus;
320 while (view_gaining_focus_ancestor && 368 while (view_gaining_focus_ancestor &&
321 view_gaining_focus_ancestor != tab_view) { 369 view_gaining_focus_ancestor != tab_view) {
322 view_gaining_focus_ancestor = 370 view_gaining_focus_ancestor =
323 platform_util::GetParent(view_gaining_focus_ancestor); 371 platform_util::GetParent(view_gaining_focus_ancestor);
324 } 372 }
325 373
326 if (view_gaining_focus_ancestor) { 374 if (view_gaining_focus_ancestor) {
327 CommitCurrentPreview(INSTANT_COMMIT_FOCUS_LOST); 375 CommitCurrentPreview(INSTANT_COMMIT_FOCUS_LOST);
328 return; 376 return;
329 } 377 }
330 378
331 DestroyPreviewContents(); 379 Hide();
332 }
333 #endif 380 #endif
381 }
334 382
335 void InstantController::OnAutocompleteGotFocus() { 383 void InstantController::OnAutocompleteGotFocus() {
336 TabContents* tab_contents = delegate_->GetInstantHostTabContents(); 384 const TabContents* active_tab = delegate_->GetActiveTabContents();
337 if (!tab_contents) 385
338 return; 386 // We could get here with no active tab if the Browser is closing.
339 387 if (!active_tab)
388 return;
389
390 // Since we don't have any autocomplete match to work with, we'll just use
391 // the default search provider's Instant URL.
340 const TemplateURL* template_url = 392 const TemplateURL* template_url =
341 TemplateURLServiceFactory::GetForProfile(tab_contents->profile()) 393 TemplateURLServiceFactory::GetForProfile(active_tab->profile())->
342 ->GetDefaultSearchProvider(); 394 GetDefaultSearchProvider();
343 if (!IsValidInstantTemplateURL(template_url)) 395
344 return; 396 std::string instant_url;
345 397 if (!GetInstantURL(template_url, &instant_url))
346 if (!loader_.get() || loader_->template_url_id() != template_url->id()) 398 return;
347 loader_.reset(new InstantLoader(this, template_url->id(), std::string())); 399
348 loader_->MaybeLoadInstantURL(tab_contents, template_url); 400 ResetLoader(instant_url, active_tab);
349 } 401 }
350 402
351 TabContents* InstantController::ReleasePreviewContents( 403 bool InstantController::commit_on_pointer_release() const {
352 InstantCommitType type, 404 return GetPreviewContents() && loader_->IsPointerDownFromActivate();
353 TabContents* current_tab) { 405 }
354 if (!loader_.get()) 406
355 return NULL; 407 void InstantController::SetSuggestions(
356
357 TabContents* tab = loader_->ReleasePreviewContents(type, current_tab);
358 ClearBlacklist();
359 is_out_of_date_ = true;
360 is_displayable_ = false;
361 commit_on_pointer_release_ = false;
362 omnibox_bounds_ = gfx::Rect();
363 loader_.reset();
364 return tab;
365 }
366
367 void InstantController::CompleteRelease(TabContents* tab) {
368 tab->blocked_content_tab_helper()->SetAllContentsBlocked(false);
369 }
370
371 TabContents* InstantController::GetPreviewContents() const {
372 return loader_.get() ? loader_->preview_contents() : NULL;
373 }
374
375 void InstantController::InstantStatusChanged(InstantLoader* loader) {
376 DCHECK(loader_.get());
377 UpdateIsDisplayable();
378 }
379
380 void InstantController::SetSuggestedTextFor(
381 InstantLoader* loader, 408 InstantLoader* loader,
382 const string16& text, 409 const std::vector<string16>& suggestions,
383 InstantCompleteBehavior behavior) { 410 InstantCompleteBehavior behavior) {
384 if (is_out_of_date_) 411 DCHECK_EQ(loader_.get(), loader);
385 return; 412 if (loader_ != loader || IsOutOfDate() || mode_ == SILENT || mode_ == HIDDEN)
386 413 return;
387 if (mode_ == INSTANT || mode_ == SUGGEST) 414
388 delegate_->SetSuggestedText(text, behavior); 415 loader_processed_last_update_ = true;
389 } 416
390 417 string16 suggestion;
391 gfx::Rect InstantController::GetInstantBounds() { 418 if (!suggestions.empty())
392 return delegate_->GetInstantBounds(); 419 suggestion = suggestions[0];
393 } 420
394 421 string16 suggestion_lower = base::i18n::ToLower(suggestion);
395 bool InstantController::ShouldCommitInstantOnPointerRelease() { 422 string16 user_text_lower = base::i18n::ToLower(last_user_text_);
396 return commit_on_pointer_release_; 423 if (user_text_lower.size() >= suggestion_lower.size() ||
424 suggestion_lower.compare(0, user_text_lower.size(), user_text_lower)) {
425 suggestion.clear();
426 } else {
427 suggestion.erase(0, last_user_text_.size());
428 }
429
430 last_suggestion_ = suggestion;
431 last_complete_behavior_ = behavior;
432 if (!last_verbatim_)
433 delegate_->SetSuggestedText(suggestion, behavior);
434
435 if (mode_ != SUGGEST)
436 Show();
397 } 437 }
398 438
399 void InstantController::CommitInstantLoader(InstantLoader* loader) { 439 void InstantController::CommitInstantLoader(InstantLoader* loader) {
400 if (loader_.get() && loader_.get() == loader) { 440 DCHECK_EQ(loader_.get(), loader);
401 CommitCurrentPreview(INSTANT_COMMIT_FOCUS_LOST); 441 DCHECK(is_showing_ && !IsOutOfDate()) << is_showing_;
442 if (loader_ != loader || !is_showing_ || IsOutOfDate())
443 return;
444
445 CommitCurrentPreview(INSTANT_COMMIT_FOCUS_LOST);
446 }
447
448 void InstantController::InstantLoaderPreviewLoaded(InstantLoader* loader) {
449 DCHECK_EQ(loader_.get(), loader);
450 AddPreviewUsageForHistogram(mode_, PREVIEW_LOADED);
451 }
452
453 void InstantController::InstantSupportDetermined(InstantLoader* loader,
454 bool supports_instant) {
455 DCHECK_EQ(loader_.get(), loader);
456 if (supports_instant) {
457 blacklisted_urls_.erase(loader->instant_url());
402 } else { 458 } else {
403 // This can happen if the mouse was down, we swapped out the preview and 459 ++blacklisted_urls_[loader->instant_url()];
404 // the mouse was released. Generally this shouldn't happen, but if it does 460 if (loader_ == loader) {
405 // revert. 461 if (GetPreviewContents())
406 DestroyPreviewContents(); 462 AddPreviewUsageForHistogram(mode_, PREVIEW_DELETED);
407 } 463
408 } 464 // Because of the state of the stack, we can't destroy the loader now.
409 465 MessageLoop::current()->DeleteSoon(FROM_HERE, loader_.release());
410 void InstantController::InstantLoaderDoesntSupportInstant( 466 DeleteLoader();
411 InstantLoader* loader) { 467 }
412 VLOG(1) << "provider does not support instant"; 468 }
413 469
414 // Don't attempt to use instant for this search engine again. 470 content::Details<const bool> details(&supports_instant);
415 BlacklistFromInstant(); 471 content::NotificationService::current()->Notify(
416 } 472 chrome::NOTIFICATION_INSTANT_SUPPORT_DETERMINED,
417 473 content::NotificationService::AllSources(),
418 void InstantController::AddToBlacklist(InstantLoader* loader, const GURL& url) { 474 details);
419 // Don't attempt to use instant for this search engine again.
420 BlacklistFromInstant();
421 } 475 }
422 476
423 void InstantController::SwappedTabContents(InstantLoader* loader) { 477 void InstantController::SwappedTabContents(InstantLoader* loader) {
424 if (is_displayable_) 478 DCHECK_EQ(loader_.get(), loader);
425 delegate_->ShowInstant(loader->preview_contents()); 479 if (loader_ == loader && is_showing_)
426 } 480 delegate_->ShowInstant();
427 481 }
428 void InstantController::InstantLoaderContentsFocused() { 482
483 void InstantController::InstantLoaderContentsFocused(InstantLoader* loader) {
484 DCHECK_EQ(loader_.get(), loader);
485 DCHECK(is_showing_ && !IsOutOfDate()) << is_showing_;
429 #if defined(USE_AURA) 486 #if defined(USE_AURA)
430 // On aura the omnibox only receives a focus lost if we initiate the focus 487 // On aura the omnibox only receives a focus lost if we initiate the focus
431 // change. This does that. 488 // change. This does that.
432 if (mode_ == INSTANT) 489 if (is_showing_ && !IsOutOfDate())
433 delegate_->InstantPreviewFocused(); 490 delegate_->InstantPreviewFocused();
434 #endif 491 #endif
435 } 492 }
436 493
437 void InstantController::UpdateIsDisplayable() { 494 void InstantController::ResetLoader(const std::string& instant_url,
438 bool displayable = !is_out_of_date_ && loader_.get() && loader_->ready() && 495 const TabContents* active_tab) {
439 loader_->http_status_ok(); 496 if (GetPreviewContents() && loader_->instant_url() != instant_url)
440 if (displayable == is_displayable_ || mode_ != INSTANT) 497 DeleteLoader();
441 return; 498
442 499 if (!GetPreviewContents()) {
443 is_displayable_ = displayable; 500 loader_.reset(new InstantLoader(this, instant_url, active_tab));
444 if (!is_displayable_) { 501 loader_->Init();
445 delegate_->HideInstant(); 502 AddPreviewUsageForHistogram(mode_, PREVIEW_CREATED);
446 } else { 503 }
447 delegate_->ShowInstant(loader_->preview_contents()); 504 }
448 content::NotificationService::current()->Notify( 505
449 chrome::NOTIFICATION_INSTANT_CONTROLLER_SHOWN, 506 void InstantController::DeleteLoader() {
450 content::Source<InstantController>(this), 507 Hide();
451 content::NotificationService::NoDetails()); 508 last_full_text_.clear();
452 } 509 last_user_text_.clear();
453 } 510 last_verbatim_ = false;
454 511 last_suggestion_.clear();
455 void InstantController::UpdateLoader(TabContents* tab_contents, 512 last_complete_behavior_ = INSTANT_COMPLETE_NOW;
456 const TemplateURL* template_url, 513 last_transition_type_ = content::PAGE_TRANSITION_LINK;
457 const GURL& url, 514 last_omnibox_bounds_ = gfx::Rect();
458 content::PageTransition transition_type, 515 url_for_history_ = GURL();
459 const string16& user_text, 516 if (GetPreviewContents())
460 bool verbatim, 517 AddPreviewUsageForHistogram(mode_, PREVIEW_DELETED);
461 string16* suggested_text) { 518 loader_.reset();
462 if (mode_ == INSTANT) 519 }
463 loader_->SetOmniboxBounds(omnibox_bounds_); 520
464 loader_->Update(tab_contents, template_url, url, transition_type, user_text, 521 void InstantController::Show() {
465 verbatim, suggested_text); 522 if (!is_showing_) {
466 UpdateIsDisplayable(); 523 is_showing_ = true;
467 // For the HIDDEN and SILENT modes, don't send back suggestions. 524 delegate_->ShowInstant();
468 if (mode_ == HIDDEN || mode_ == SILENT) 525 AddPreviewUsageForHistogram(mode_, PREVIEW_SHOWED);
469 suggested_text->clear(); 526 }
470 } 527 }
471 528
472 // Returns true if |template_url| is a valid TemplateURL for use by instant. 529 void InstantController::SendBoundsToPage() {
473 bool InstantController::IsValidInstantTemplateURL( 530 if (last_omnibox_bounds_ == omnibox_bounds_ || IsOutOfDate() ||
474 const TemplateURL* template_url) { 531 !GetPreviewContents() || loader_->IsPointerDownFromActivate()) {
475 return template_url && template_url->id() && 532 return;
476 template_url->instant_url_ref().SupportsReplacement() && 533 }
477 !IsBlacklistedFromInstant(template_url->id()); 534
478 } 535 last_omnibox_bounds_ = omnibox_bounds_;
479 536 gfx::Rect preview_bounds = delegate_->GetInstantBounds();
480 void InstantController::BlacklistFromInstant() { 537 gfx::Rect intersection = omnibox_bounds_.Intersect(preview_bounds);
481 if (!loader_.get()) 538
482 return; 539 // Translate into window coordinates.
483 540 if (!intersection.IsEmpty()) {
484 DCHECK(loader_->template_url_id()); 541 intersection.Offset(-preview_bounds.origin().x(),
485 blacklisted_ids_.insert(loader_->template_url_id()); 542 -preview_bounds.origin().y());
486 543 }
487 // Because of the state of the stack we can't destroy the loader now. 544
488 ScheduleDestroy(loader_.release()); 545 // In the current Chrome UI, these must always be true so they sanity check
489 UpdateIsDisplayable(); 546 // the above operations. In a future UI, these may be removed or adjusted.
490 } 547 // There is no point in sanity-checking |intersection.y()| because the omnibox
491 548 // can be placed anywhere vertically relative to the preview (for example, in
492 bool InstantController::IsBlacklistedFromInstant(TemplateURLID id) { 549 // Mac fullscreen mode, the omnibox is fully enclosed by the preview bounds).
493 return blacklisted_ids_.count(id) > 0; 550 DCHECK_LE(0, intersection.x());
494 } 551 DCHECK_LE(0, intersection.width());
495 552 DCHECK_LE(0, intersection.height());
496 void InstantController::ClearBlacklist() { 553
497 blacklisted_ids_.clear(); 554 loader_->SetOmniboxBounds(intersection);
498 } 555 }
499 556
500 void InstantController::ScheduleDestroy(InstantLoader* loader) { 557 bool InstantController::GetInstantURL(const TemplateURL* template_url,
501 loaders_to_destroy_.push_back(loader); 558 std::string* instant_url) const {
502 if (!weak_factory_.HasWeakPtrs()) { 559 CommandLine* command_line = CommandLine::ForCurrentProcess();
503 MessageLoop::current()->PostTask( 560 if (command_line->HasSwitch(switches::kInstantURL)) {
504 FROM_HERE, base::Bind(&InstantController::DestroyLoaders, 561 *instant_url = command_line->GetSwitchValueASCII(switches::kInstantURL);
505 weak_factory_.GetWeakPtr())); 562 return true;
506 } 563 }
507 } 564
508 565 if (!template_url)
509 void InstantController::DestroyLoaders() { 566 return false;
510 loaders_to_destroy_.clear(); 567
511 } 568 const TemplateURLRef& instant_url_ref = template_url->instant_url_ref();
569 if (!instant_url_ref.IsValid() || !instant_url_ref.SupportsReplacement())
570 return false;
571
572 *instant_url = instant_url_ref.ReplaceSearchTerms(
573 TemplateURLRef::SearchTermsArgs(string16()));
574
575 std::map<std::string, int>::const_iterator iter =
576 blacklisted_urls_.find(*instant_url);
577 if (iter != blacklisted_urls_.end() &&
578 iter->second > kMaxInstantSupportFailures) {
579 instant_url->clear();
580 return false;
581 }
582
583 return true;
584 }
585
586 bool InstantController::IsOutOfDate() const {
587 return !last_active_tab_ ||
588 last_active_tab_ != delegate_->GetActiveTabContents();
589 }
OLDNEW
« no previous file with comments | « chrome/browser/instant/instant_controller.h ('k') | chrome/browser/instant/instant_controller_delegate.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698