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

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

Issue 12386019: Instant: Use only one hidden WebContents per profile. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: . Created 7 years, 9 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
(Empty)
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/instant/instant_controller_impl.h"
6
7 #include "base/string_util.h"
8 #include "base/stringprintf.h"
9 #include "base/utf_string_conversions.h"
10 #include "chrome/browser/autocomplete/autocomplete_match.h"
11 #include "chrome/browser/history/history_service.h"
12 #include "chrome/browser/history/history_service_factory.h"
13 #include "chrome/browser/instant/instant_controller_utils.h"
14 #include "chrome/browser/instant/instant_overlay.h"
15 #include "chrome/browser/instant/instant_overlay_model.h"
16 #include "chrome/browser/instant/instant_preloader.h"
17 #include "chrome/browser/instant/instant_service.h"
18 #include "chrome/browser/instant/instant_service_factory.h"
19 #include "chrome/browser/ui/browser_instant_controller.h"
20 #include "chrome/common/chrome_notification_types.h"
21 #include "content/public/browser/navigation_entry.h"
22 #include "content/public/browser/notification_service.h"
23 #include "content/public/browser/web_contents.h"
24
25 #if defined(OS_MACOSX)
26 #include "content/public/browser/render_widget_host_view.h"
27 #endif
28
29 namespace {
30
31 // An artificial delay (in milliseconds) we introduce before telling the Instant
32 // page about the new omnibox bounds, in cases where the bounds shrink. This is
33 // to avoid the page jumping up/down very fast in response to bounds changes.
34 const int kUpdateBoundsDelayMS = 1000;
35
36 } // namespace
37
38 InstantControllerImpl::InstantControllerImpl(
39 chrome::BrowserInstantController* browser,
40 Profile* profile)
41 : browser_(browser),
42 service_(InstantServiceFactory::GetForProfile(profile)),
43 last_verbatim_(false),
44 last_transition_type_(content::PAGE_TRANSITION_LINK) {
45 service_->AddObserver(this);
46 model_.reset(new InstantOverlayModel(service_));
47 }
48
49 InstantControllerImpl::~InstantControllerImpl() {
50 service_->RemoveObserver(this);
51 }
52
53 bool InstantControllerImpl::Update(const AutocompleteMatch& match,
54 const string16& user_text,
55 const string16& full_text,
56 size_t selection_start,
57 size_t selection_end,
58 bool verbatim,
59 bool /* user_input_in_progress */,
60 bool omnibox_popup_is_open,
61 bool /* escape_pressed */,
62 bool is_keyword_search) {
63 // If the overlay is being committed, don't change anything.
64 // TODO(sreeram): Add a browser test for this.
65 if (IsOverlayBeingCommitted())
66 return false;
67
68 // No overlay for URLs and keyword searches or if the user isn't typing.
69 if (!AutocompleteMatch::IsSearchType(match.type) || is_keyword_search ||
70 !omnibox_popup_is_open || user_text.empty() || full_text.empty()) {
71 HideOverlay();
72 return false;
73 }
74
75 if (!overlay_) {
76 // If we don't have a valid (Instant-supporting) web contents ready, bail.
77 if (!service_->preloader()->supports_instant())
78 return false;
79
80 overlay_.reset(new InstantOverlay(
81 this, service_, service_->preloader()->ReleaseContents()));
82 }
83
84 // Query is verbatim not only when |verbatim| is true (which indicates that
85 // the user pressed Delete/Backspace), but also when there's any selection
86 // (including inline autocompletion) or if the cursor is not at the end.
87 verbatim = verbatim || selection_start != selection_end ||
88 selection_start != full_text.size();
89
90 last_omnibox_text_ = full_text;
91 last_verbatim_ = verbatim;
92 last_transition_type_ = match.transition;
93 url_for_history_ = match.destination_url;
94
95 service_->LogDebugEvent(base::StringPrintf("%p Update '%s' %s [%d,%d]",
96 this,
97 UTF16ToUTF8(last_omnibox_text_).c_str(),
98 verbatim ? "verbatim" : "psychic",
99 static_cast<int>(selection_start),
100 static_cast<int>(selection_end)));
101
102 overlay_->page()->Change(full_text, verbatim, selection_start, selection_end);
103
104 content::NotificationService::current()->Notify(
105 chrome::NOTIFICATION_INSTANT_UPDATED,
106 content::NotificationService::AllSources(),
107 content::NotificationService::NoDetails());
108
109 return true;
110 }
111
112 void InstantControllerImpl::HandleAutocompleteResults(
113 const std::vector<AutocompleteProvider*>& /* providers */) {
114 }
115
116 bool InstantControllerImpl::OnUpOrDownKeyPressed(int /* count */) {
117 return false;
118 }
119
120 void InstantControllerImpl::OnCancel(const AutocompleteMatch& /* match */,
121 const string16& /* full_text */) {
122 }
123
124 bool InstantControllerImpl::IsOverlayingSearchResults() const {
125 return model_->contents() != NULL;
126 }
127
128 content::WebContents* InstantControllerImpl::GetOverlayContents() const {
129 return overlay_ ? overlay_->contents() : NULL;
130 }
131
132 const InstantOverlayModel* InstantControllerImpl::model() const {
133 return model_.get();
134 }
135
136 void InstantControllerImpl::AddOverlayModelObserver(
137 InstantOverlayModelObserver* observer) {
138 model_->AddObserver(observer);
139 }
140
141 void InstantControllerImpl::RemoveOverlayModelObserver(
142 InstantOverlayModelObserver* observer) {
143 model_->RemoveObserver(observer);
144 }
145
146 bool InstantControllerImpl::CommitIfPossible(InstantCommitType type) {
147 if (!IsOverlayingSearchResults())
148 return false;
149
150 service_->LogDebugEvent(base::StringPrintf("%p CommitIfPossible '%s' %s",
151 this,
152 UTF16ToUTF8(last_omnibox_text_).c_str(),
153 InstantControllerUtils::CommitTypeToString(type).c_str()));
154
155 if (type == INSTANT_COMMIT_FOCUS_LOST)
156 overlay_->page()->Blur(last_omnibox_text_);
157 else if (type != INSTANT_COMMIT_NAVIGATED)
158 overlay_->page()->Submit(last_omnibox_text_);
159
160 HistoryService* history = HistoryServiceFactory::GetForProfile(
161 service_->profile(), Profile::EXPLICIT_ACCESS);
162 if (history) {
163 history->AddPage(url_for_history_, base::Time::Now(), NULL, 0, GURL(),
164 history::RedirectList(), last_transition_type_,
165 history::SOURCE_BROWSED, false);
166
167 if (overlay_->page()->navigated_after_change() ||
168 type == INSTANT_COMMIT_NAVIGATED) {
169 content::NavigationEntry* entry =
170 GetOverlayContents()->GetController().GetLastCommittedEntry();
171 history->AddPage(entry->GetVirtualURL(), base::Time::Now(),
172 history::SOURCE_BROWSED);
173 history->SetPageTitle(entry->GetVirtualURL(),
174 entry->GetTitleForDisplay(""));
175 }
176 }
177
178 scoped_ptr<content::WebContents> contents = overlay_->ReleaseContents();
179
180 if (type == INSTANT_COMMIT_PRESSED_ALT_ENTER) {
181 contents->GetController().PruneAllButActive();
182 } else {
183 content::WebContents* active_tab = browser_->GetActiveWebContents();
184 contents->GetController().CopyStateFromAndPrune(
185 &active_tab->GetController());
186 }
187
188 browser_->CommitInstant(contents.Pass(), last_transition_type_,
189 type == INSTANT_COMMIT_PRESSED_ALT_ENTER);
190
191 service_->LogDebugEvent(base::StringPrintf("%p Committed", this));
192
193 HideOverlay();
194
195 return true;
196 }
197
198 scoped_ptr<content::WebContents> InstantControllerImpl::ReleaseNTPContents() {
199 return scoped_ptr<content::WebContents>();
200 }
201
202 // TODO(tonyg): This method only fires when the popup bounds change. It also
203 // needs to fire when the overlay bounds change (e.g.: open/close info bar).
204 void InstantControllerImpl::SetPopupBounds(const gfx::Rect& bounds) {
205 if (popup_bounds_ == bounds)
206 return;
207
208 popup_bounds_ = bounds;
209
210 if (bounds.height() > last_popup_bounds_.height()) {
211 update_bounds_timer_.Stop();
212 SendPopupBoundsToPage();
213 } else if (!update_bounds_timer_.IsRunning()) {
214 update_bounds_timer_.Start(FROM_HERE,
215 base::TimeDelta::FromMilliseconds(kUpdateBoundsDelayMS),
216 this, &InstantControllerImpl::SendPopupBoundsToPage);
217 }
218 }
219
220 void InstantControllerImpl::SetOmniboxBounds(const gfx::Rect& /* bounds */) {
221 }
222
223 void InstantControllerImpl::OmniboxFocusChanged(
224 OmniboxFocusState state,
225 OmniboxFocusChangeReason reason,
226 gfx::NativeView view_gaining_focus) {
227
228 service_->LogDebugEvent(base::StringPrintf("%p OmniboxFocus %s due to %s",
229 this,
230 InstantControllerUtils::FocusStateToString(state).c_str(),
231 InstantControllerUtils::FocusChangeReasonToString(reason).c_str()));
232
233 if (state == OMNIBOX_FOCUS_NONE) {
234 // Don't do anything if we don't have an overlay or if the overlay isn't
235 // showing or if we are in the midst of committing the overlay.
236 if (!overlay_ || !IsOverlayingSearchResults() || IsOverlayBeingCommitted())
237 return;
238
239 view_gaining_focus =
240 InstantControllerUtils::GetViewGainingFocus(view_gaining_focus);
241 if (InstantControllerUtils::IsViewInContents(view_gaining_focus,
242 GetOverlayContents()))
243 CommitIfPossible(INSTANT_COMMIT_FOCUS_LOST);
244 else
245 HideOverlay();
246 } else {
247 service_->preloader()->InitContents();
248 }
249 }
250
251 void InstantControllerImpl::TabDeactivated(
252 content::WebContents* /* contents */) {
253 if (GetOverlayContents())
254 HideOverlay();
255 }
256
257 void InstantControllerImpl::SearchModeChanged(
258 const chrome::search::Mode& /* old_mode */,
259 const chrome::search::Mode& /* new_mode */) {
260 }
261
262 void InstantControllerImpl::ActiveTabChanged() {
263 }
264
265 void InstantControllerImpl::SwappedOverlayContents() {
266 if (IsOverlayingSearchResults())
267 ShowOverlay();
268 }
269
270 void InstantControllerImpl::FocusedOverlayContents() {
271 if (IsOverlayingSearchResults())
272 browser_->InstantOverlayFocused();
273 }
274
275 void InstantControllerImpl::RenderViewGone(
276 const content::WebContents* /* contents */) {
277 HideOverlay();
278 }
279
280 void InstantControllerImpl::InitSearchBox(
281 const content::WebContents* /* contents */) {
282 }
283
284 void InstantControllerImpl::InstantSupportDetermined(
285 const content::WebContents* /* contents */) {
286 if (!overlay_->page()->supports_instant())
287 HideOverlay();
288 service_->InstantSupportDetermined();
289 }
290
291 void InstantControllerImpl::SetSuggestion(
292 const content::WebContents* /* contents */,
293 const InstantSuggestion& suggestion) {
294 string16 text = suggestion.text;
295
296 if (StartsWith(text, last_omnibox_text_, true)) {
297 text.erase(0, last_omnibox_text_.size());
298 } else if (!InstantControllerUtils::NormalizeAndStripPrefix(
299 &text, last_omnibox_text_)) {
300 text.clear();
301 }
302
303 if (last_verbatim_)
304 text.clear();
305
306 if (!text.empty()) {
307 service_->LogDebugEvent(base::StringPrintf("%p SetSuggestion '%s'",
308 this,
309 UTF16ToUTF8(text).c_str()));
310 browser_->SetInstantSuggestion(InstantSuggestion(
311 text, INSTANT_COMPLETE_NOW, INSTANT_SUGGESTION_SEARCH));
312 }
313
314 ShowOverlay();
315 }
316
317 void InstantControllerImpl::NavigateToURL(
318 const content::WebContents* /* contents */,
319 const GURL& /* url */,
320 content::PageTransition /* transition */,
321 WindowOpenDisposition /* disposition */) {
322 }
323
324 void InstantControllerImpl::ShowOverlay(
325 const content::WebContents* /* contents */,
326 int /* height */,
327 InstantSizeUnits /* height_units */) {
328 }
329
330 void InstantControllerImpl::SetFocusState(
331 const content::WebContents* /* contents */,
332 OmniboxFocusState /* focus_state */) {
333 }
334
335 void InstantControllerImpl::DeleteMostVisitedItem(
336 const content::WebContents* /* contents */,
337 const GURL& /* url */) {
338 }
339
340 void InstantControllerImpl::UndoMostVisitedItemDeletion(
341 const content::WebContents* /* contents */,
342 const GURL& /* url */) {
343 }
344
345 void InstantControllerImpl::UndoAllMostVisitedItemDeletions(
346 const content::WebContents* /* contents */) {
347 }
348
349 void InstantControllerImpl::InstantStatusChanged() {
350 HideOverlay();
351 }
352
353 void InstantControllerImpl::ThemeInfoChanged() {
354 }
355
356 void InstantControllerImpl::MostVisitedItemsChanged() {
357 }
358
359 bool InstantControllerImpl::IsOverlayBeingCommitted() const {
360 return (overlay_ && overlay_->is_pointer_down_from_activate()) ||
361 (IsOverlayingSearchResults() && !GetOverlayContents());
362 }
363
364 void InstantControllerImpl::ShowOverlay() {
365 #if defined(OS_MACOSX)
366 content::RenderWidgetHostView* rwhv =
367 GetOverlayContents()->GetRenderWidgetHostView();
368 if (rwhv)
369 rwhv->SetTakesFocusOnlyOnMouseDown(true);
370 #endif
371 model_->SetOverlayState(GetOverlayContents(), 100, INSTANT_SIZE_PERCENT);
372 }
373
374 void InstantControllerImpl::HideOverlay() {
375 model_->SetOverlayState(NULL, 0, INSTANT_SIZE_PIXELS);
376 last_popup_bounds_ = gfx::Rect();
377 if (overlay_) {
378 overlay_->ReleaseContents();
379 MessageLoop::current()->DeleteSoon(FROM_HERE, overlay_.release());
380 }
381 }
382
383 void InstantControllerImpl::SendPopupBoundsToPage() {
384 if (!overlay_ || last_popup_bounds_ == popup_bounds_ ||
385 overlay_->is_pointer_down_from_activate())
386 return;
387
388 last_popup_bounds_ = popup_bounds_;
389
390 gfx::Rect overlay_bounds = browser_->GetInstantBounds();
391 gfx::Rect intersection = gfx::IntersectRects(popup_bounds_, overlay_bounds);
392
393 // Translate into window coordinates.
394 if (!intersection.IsEmpty()) {
395 intersection.Offset(-overlay_bounds.origin().x(),
396 -overlay_bounds.origin().y());
397 }
398
399 // In the current Chrome UI, these must always be true so they sanity check
400 // the above operations. In a future UI, these may be removed or adjusted.
401 // There is no point in sanity-checking intersection.y() because the omnibox
402 // can be placed anywhere vertically relative to the overlay (for example, in
403 // Mac fullscreen mode, the omnibox is fully enclosed by the overlay bounds).
404 DCHECK_LE(0, intersection.x());
405 DCHECK_LE(0, intersection.width());
406 DCHECK_LE(0, intersection.height());
407
408 overlay_->page()->PopupBounds(intersection);
409 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698