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

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

Powered by Google App Engine
This is Rietveld 408576698