OLD | NEW |
| (Empty) |
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 | |
3 // found in the LICENSE file. | |
4 | |
5 #ifndef CHROME_BROWSER_INSTANT_INSTANT_CONTROLLER_H_ | |
6 #define CHROME_BROWSER_INSTANT_INSTANT_CONTROLLER_H_ | |
7 | |
8 #include <list> | |
9 #include <map> | |
10 #include <string> | |
11 #include <utility> | |
12 #include <vector> | |
13 | |
14 #include "base/basictypes.h" | |
15 #include "base/gtest_prod_util.h" | |
16 #include "base/memory/scoped_ptr.h" | |
17 #include "base/string16.h" | |
18 #include "base/time.h" | |
19 #include "base/timer.h" | |
20 #include "chrome/browser/history/history_types.h" | |
21 #include "chrome/browser/instant/instant_commit_type.h" | |
22 #include "chrome/browser/instant/instant_overlay_model.h" | |
23 #include "chrome/browser/instant/instant_page.h" | |
24 #include "chrome/browser/ui/omnibox/omnibox_edit_model.h" | |
25 #include "chrome/common/instant_types.h" | |
26 #include "chrome/common/search_types.h" | |
27 #include "content/public/browser/notification_observer.h" | |
28 #include "content/public/browser/notification_registrar.h" | |
29 #include "content/public/common/page_transition_types.h" | |
30 #include "googleurl/src/gurl.h" | |
31 #include "ui/base/window_open_disposition.h" | |
32 #include "ui/gfx/native_widget_types.h" | |
33 #include "ui/gfx/rect.h" | |
34 | |
35 struct AutocompleteMatch; | |
36 class AutocompleteProvider; | |
37 class InstantNTP; | |
38 class InstantOverlay; | |
39 class InstantTab; | |
40 class TemplateURL; | |
41 | |
42 namespace chrome { | |
43 class BrowserInstantController; | |
44 } | |
45 | |
46 namespace content { | |
47 class WebContents; | |
48 } | |
49 | |
50 // Macro used for logging debug events. |message| should be a std::string. | |
51 #define LOG_INSTANT_DEBUG_EVENT(controller, message) \ | |
52 controller->LogDebugEvent(message) | |
53 | |
54 // InstantController drives Chrome Instant, i.e., the browser implementation of | |
55 // the Embedded Search API (see http://dev.chromium.org/embeddedsearch). | |
56 // | |
57 // In extended mode, InstantController maintains and coordinates three | |
58 // instances of InstantPage: | |
59 // (1) An InstantOverlay instance that is used to show search suggestions and | |
60 // results in an overlay over a non-search page. | |
61 // (2) An InstantNTP instance which is a preloaded search page that will be | |
62 // swapped-in the next time the user navigates to the New Tab Page. It is | |
63 // never shown to the user in an uncommitted state. | |
64 // (3) An InstantTab instance which points to the currently active tab, if it | |
65 // supports the Embedded Search API. | |
66 // | |
67 // All three are backed by a WebContents. InstantOverlay and InstantNTP own | |
68 // their corresponding WebContents; InstantTab does not. In non-extended mode, | |
69 // only an InstantOverlay instance is kept. | |
70 // | |
71 // InstantController is owned by Browser via BrowserInstantController. | |
72 class InstantController : public InstantPage::Delegate, | |
73 public content::NotificationObserver { | |
74 public: | |
75 InstantController(chrome::BrowserInstantController* browser, | |
76 bool extended_enabled); | |
77 virtual ~InstantController(); | |
78 | |
79 // Invoked as the user types into the omnibox. |user_text| is what the user | |
80 // has typed. |full_text| is what the omnibox is showing. These may differ if | |
81 // the user typed only some text, and the rest was inline autocompleted. If | |
82 // |verbatim| is true, search results are shown for the exact omnibox text, | |
83 // rather than the best guess as to what the user means. Returns true if the | |
84 // update is accepted (i.e., if |match| is a search rather than a URL). | |
85 // |is_keyword_search| is true if keyword searching is in effect. | |
86 bool Update(const AutocompleteMatch& match, | |
87 const string16& user_text, | |
88 const string16& full_text, | |
89 size_t selection_start, | |
90 size_t selection_end, | |
91 bool verbatim, | |
92 bool user_input_in_progress, | |
93 bool omnibox_popup_is_open, | |
94 bool escape_pressed, | |
95 bool is_keyword_search); | |
96 | |
97 // Releases and returns the NTP WebContents. May be NULL. Loads a new | |
98 // WebContents for the NTP. | |
99 scoped_ptr<content::WebContents> ReleaseNTPContents() WARN_UNUSED_RESULT; | |
100 | |
101 // Sets the bounds of the omnibox popup, in screen coordinates. | |
102 void SetPopupBounds(const gfx::Rect& bounds); | |
103 | |
104 // Sets the stored start-edge margin and width of the omnibox. | |
105 void SetOmniboxBounds(const gfx::Rect& bounds); | |
106 | |
107 // Send autocomplete results from |providers| to the overlay page. | |
108 void HandleAutocompleteResults( | |
109 const std::vector<AutocompleteProvider*>& providers); | |
110 | |
111 // Called when the user presses up or down. |count| is a repeat count, | |
112 // negative for moving up, positive for moving down. Returns true if Instant | |
113 // handled the key press. | |
114 bool OnUpOrDownKeyPressed(int count); | |
115 | |
116 // Called when the user has arrowed into the suggestions but wants to cancel, | |
117 // typically by pressing ESC. The omnibox text is expected to have been | |
118 // reverted to |full_text| by the OmniboxEditModel prior to calling this. | |
119 // |match| is the match reverted to. | |
120 void OnCancel(const AutocompleteMatch& match, | |
121 const string16& user_text, | |
122 const string16& full_text); | |
123 | |
124 // The overlay WebContents. May be NULL. InstantController retains ownership. | |
125 content::WebContents* GetOverlayContents() const; | |
126 | |
127 // Returns true if Instant is showing a search results overlay. Returns false | |
128 // if the overlay is not showing, or if it's showing only suggestions. | |
129 bool IsOverlayingSearchResults() const; | |
130 | |
131 // If the overlay is showing search results, commits the overlay, calling | |
132 // CommitInstant() on the browser, and returns true. Else, returns false. | |
133 bool CommitIfPossible(InstantCommitType type); | |
134 | |
135 // Called to indicate that the omnibox focus state changed with the given | |
136 // |reason|. If |focus_state| is FOCUS_NONE, |view_gaining_focus| is set to | |
137 // the view gaining focus. | |
138 void OmniboxFocusChanged(OmniboxFocusState focus_state, | |
139 OmniboxFocusChangeReason reason, | |
140 gfx::NativeView view_gaining_focus); | |
141 | |
142 // The search mode in the active tab has changed. Pass the message down to | |
143 // the overlay which will notify the renderer. Create |instant_tab_| if the | |
144 // |new_mode| reflects an Instant search results page. | |
145 void SearchModeChanged(const chrome::search::Mode& old_mode, | |
146 const chrome::search::Mode& new_mode); | |
147 | |
148 // The user switched tabs. Hide the overlay. Create |instant_tab_| if the | |
149 // newly active tab is an Instant search results page. | |
150 void ActiveTabChanged(); | |
151 | |
152 // The user is about to switch tabs. Commit the overlay if needed. | |
153 void TabDeactivated(content::WebContents* contents); | |
154 | |
155 // Sets whether Instant should show result overlays. |use_local_overlay_only| | |
156 // will force the use of kLocalOmniboxPopupURL as the Instant URL and is only | |
157 // applicable if |extended_enabled_| is true. | |
158 void SetInstantEnabled(bool instant_enabled, bool use_local_overlay_only); | |
159 | |
160 // The theme has changed. Pass the message to the overlay page. | |
161 void ThemeChanged(const ThemeBackgroundInfo& theme_info); | |
162 | |
163 // Called when someone else swapped in a different contents in the |overlay_|. | |
164 void SwappedOverlayContents(); | |
165 | |
166 // Called when contents for |overlay_| received focus. | |
167 void FocusedOverlayContents(); | |
168 | |
169 // Called when the |overlay_| might be stale. If it's actually stale, and the | |
170 // omnibox doesn't have focus, and the overlay isn't showing, the |overlay_| | |
171 // is deleted and recreated. Else the refresh is skipped. | |
172 void ReloadOverlayIfStale(); | |
173 | |
174 // Called when the |overlay_|'s main frame has finished loading. | |
175 void OverlayLoadCompletedMainFrame(); | |
176 | |
177 // Adds a new event to |debug_events_| and also DVLOG's it. Ensures that | |
178 // |debug_events_| doesn't get too large. | |
179 void LogDebugEvent(const std::string& info) const; | |
180 | |
181 // Resets list of debug events. | |
182 void ClearDebugEvents(); | |
183 | |
184 // See comments for |debug_events_| below. | |
185 const std::list<std::pair<int64, std::string> >& debug_events() { | |
186 return debug_events_; | |
187 } | |
188 | |
189 // Returns the transition type of the last AutocompleteMatch passed to Update. | |
190 content::PageTransition last_transition_type() const { | |
191 return last_transition_type_; | |
192 } | |
193 | |
194 // Non-const for Add/RemoveObserver only. Other model changes should only | |
195 // happen through the InstantController interface. | |
196 InstantOverlayModel* model() { return &model_; } | |
197 | |
198 private: | |
199 FRIEND_TEST_ALL_PREFIXES(InstantTest, OmniboxFocusLoadsInstant); | |
200 FRIEND_TEST_ALL_PREFIXES(InstantTest, SetWithTemplateURL); | |
201 FRIEND_TEST_ALL_PREFIXES(InstantTest, NonInstantSearchProvider); | |
202 FRIEND_TEST_ALL_PREFIXES(InstantTest, InstantOverlayRefresh); | |
203 FRIEND_TEST_ALL_PREFIXES(InstantExtendedTest, ExtendedModeIsOn); | |
204 FRIEND_TEST_ALL_PREFIXES(InstantExtendedTest, MostVisited); | |
205 FRIEND_TEST_ALL_PREFIXES(InstantExtendedTest, RestrictedItemReadback); | |
206 FRIEND_TEST_ALL_PREFIXES(InstantExtendedTest, OmniboxFocusLoadsInstant); | |
207 FRIEND_TEST_ALL_PREFIXES(InstantExtendedTest, | |
208 OmniboxTextUponFocusedCommittedSERP); | |
209 FRIEND_TEST_ALL_PREFIXES(InstantExtendedTest, | |
210 MiddleClickOnSuggestionOpensInNewTab); | |
211 FRIEND_TEST_ALL_PREFIXES(InstantExtendedTest, NTPIsPreloaded); | |
212 FRIEND_TEST_ALL_PREFIXES(InstantExtendedTest, PreloadedNTPIsUsedInNewTab); | |
213 FRIEND_TEST_ALL_PREFIXES(InstantExtendedTest, PreloadedNTPIsUsedInSameTab); | |
214 FRIEND_TEST_ALL_PREFIXES(InstantExtendedTest, ProcessIsolation); | |
215 FRIEND_TEST_ALL_PREFIXES(InstantExtendedTest, UnrelatedSiteInstance); | |
216 FRIEND_TEST_ALL_PREFIXES(InstantExtendedTest, ValidatesSuggestions); | |
217 FRIEND_TEST_ALL_PREFIXES(InstantExtendedTest, | |
218 OmniboxCommitsWhenShownFullHeight); | |
219 FRIEND_TEST_ALL_PREFIXES(InstantExtendedManualTest, | |
220 MANUAL_OmniboxFocusLoadsInstant); | |
221 | |
222 // Overridden from content::NotificationObserver: | |
223 virtual void Observe(int type, | |
224 const content::NotificationSource& source, | |
225 const content::NotificationDetails& details) OVERRIDE; | |
226 | |
227 // Overridden from InstantPage::Delegate: | |
228 // TODO(shishir): We assume that the WebContent's current RenderViewHost is | |
229 // the RenderViewHost being created which is not always true. Fix this. | |
230 virtual void InstantPageRenderViewCreated( | |
231 const content::WebContents* contents) OVERRIDE; | |
232 virtual void InstantSupportDetermined( | |
233 const content::WebContents* contents, | |
234 bool supports_instant) OVERRIDE; | |
235 virtual void InstantPageRenderViewGone( | |
236 const content::WebContents* contents) OVERRIDE; | |
237 virtual void InstantPageAboutToNavigateMainFrame( | |
238 const content::WebContents* contents, | |
239 const GURL& url) OVERRIDE; | |
240 virtual void SetSuggestions( | |
241 const content::WebContents* contents, | |
242 const std::vector<InstantSuggestion>& suggestions) OVERRIDE; | |
243 virtual void ShowInstantOverlay( | |
244 const content::WebContents* contents, | |
245 int height, | |
246 InstantSizeUnits units) OVERRIDE; | |
247 virtual void FocusOmnibox(const content::WebContents* contents) OVERRIDE; | |
248 virtual void StartCapturingKeyStrokes( | |
249 const content::WebContents* contents) OVERRIDE; | |
250 virtual void StopCapturingKeyStrokes(content::WebContents* contents) OVERRIDE; | |
251 virtual void NavigateToURL( | |
252 const content::WebContents* contents, | |
253 const GURL& url, | |
254 content::PageTransition transition, | |
255 WindowOpenDisposition disposition) OVERRIDE; | |
256 | |
257 // Invoked by the InstantLoader when the Instant page wants to delete a | |
258 // Most Visited item. | |
259 virtual void DeleteMostVisitedItem(uint64 most_visited_item_id) OVERRIDE; | |
260 | |
261 // Invoked by the InstantLoader when the Instant page wants to undo a | |
262 // Most Visited deletion. | |
263 virtual void UndoMostVisitedDeletion(uint64 most_visited_item_id) OVERRIDE; | |
264 | |
265 // Invoked by the InstantLoader when the Instant page wants to undo all | |
266 // Most Visited deletions. | |
267 virtual void UndoAllMostVisitedDeletions() OVERRIDE; | |
268 | |
269 // Helper for OmniboxFocusChanged. Commit or discard the overlay. | |
270 void OmniboxLostFocus(gfx::NativeView view_gaining_focus); | |
271 | |
272 // Creates a new NTP, using the instant_url property of the default | |
273 // TemplateURL. | |
274 void ResetNTP(); | |
275 | |
276 // Ensures that |overlay_| uses the Instant URL returned by GetInstantURL(), | |
277 // creating a new overlay if necessary. In extended mode, will fallback to | |
278 // using the kLocalOmniboxPopupURL as the Instant URL in case GetInstantURL() | |
279 // returns false. Returns true if an Instant URL could be determined. | |
280 // For |ignore_blacklist| look at comments in |GetInstantURL|. | |
281 bool EnsureOverlayIsCurrent(bool ignore_blacklist); | |
282 | |
283 // Recreates the |overlay_| with |instant_url|. Note that |overlay_| is | |
284 // deleted in this call. | |
285 void CreateOverlay(const std::string& instant_url, | |
286 const content::WebContents* active_tab); | |
287 | |
288 // If the |overlay_| being used is in fallback mode, it will be switched back | |
289 // to the remote overlay if the overlay is not showing and the omnibox does | |
290 // not have focus. | |
291 void MaybeSwitchToRemoteOverlay(); | |
292 | |
293 // If the active tab is an Instant search results page, sets |instant_tab_| to | |
294 // point to it. Else, deletes any existing |instant_tab_|. | |
295 void ResetInstantTab(); | |
296 | |
297 // Hide the overlay. Also sends an onchange event (with blank query) to the | |
298 // overlay, telling it to clear out results for any old queries. | |
299 void HideOverlay(); | |
300 | |
301 // Like HideOverlay(), but doesn't call OnStaleOverlay(). Use HideOverlay() | |
302 // unless you are going to call overlay_.reset() yourself subsequently. | |
303 void HideInternal(); | |
304 | |
305 // Counterpart to HideOverlay(). Asks the |browser_| to display the overlay | |
306 // with the given |height| in |units|. | |
307 void ShowOverlay(int height, InstantSizeUnits units); | |
308 | |
309 // Send the omnibox popup bounds to the page. | |
310 void SendPopupBoundsToPage(); | |
311 | |
312 // Determines the Instant URL based on a number of factors: | |
313 // If |extended_enabled_|: | |
314 // - If |use_local_overlay_only_| is true return kLocalOmniboxPopupURL, else | |
315 // - If the Instant URL is specified by command line, returns it, else | |
316 // - If the default Instant URL is present returns it. | |
317 // If !|extended_enabled_|: | |
318 // - If the Instant URL is specified by command line, returns it, else | |
319 // - If the default Instant URL is present returns it. | |
320 // | |
321 // If |ignore_blacklist| is set to true, Instant URLs are not filtered through | |
322 // the blacklist. | |
323 // | |
324 // Returns true if a valid Instant URL could be found that is not blacklisted. | |
325 bool GetInstantURL(Profile* profile, | |
326 bool ignore_blacklist, | |
327 std::string* instant_url) const; | |
328 | |
329 // Adds the URL for the page to the blacklist. Deletes the contents held and | |
330 // recreates a new page. | |
331 void BlacklistAndResetOverlay(); | |
332 void BlacklistAndResetNTP(); | |
333 | |
334 // Removes |url| from the blacklist. | |
335 void RemoveFromBlacklist(const std::string& url); | |
336 | |
337 InstantOverlay* overlay() const { return overlay_.get(); } | |
338 InstantTab* instant_tab() const { return instant_tab_.get(); } | |
339 InstantNTP* ntp() const { return ntp_.get(); } | |
340 | |
341 // Begin listening to change notifications from TopSites and fire off an | |
342 // initial request for most visited items. | |
343 void StartListeningToMostVisitedChanges(); | |
344 | |
345 // Fire off an async request for most visited items to the TopNav code. | |
346 void RequestMostVisitedItems(); | |
347 | |
348 // Called when we get new most visited items from the TopNav code, | |
349 // registered as an async callback. Parses them and sends them to the | |
350 // renderer via SendMostVisitedItems. | |
351 void OnMostVisitedItemsReceived(const history::MostVisitedURLList& data); | |
352 | |
353 // Sends a collection of MostVisitedItems to the renderer process via | |
354 // the appropriate InstantPage subclass. | |
355 void SendMostVisitedItems(const std::vector<InstantMostVisitedItem>& items); | |
356 | |
357 // If possible, tries to mutate |suggestion| to a valid suggestion. Returns | |
358 // true if successful. (Note that |suggestion| may be modified even if this | |
359 // returns false.) | |
360 bool FixSuggestion(InstantSuggestion* suggestion) const; | |
361 | |
362 chrome::BrowserInstantController* const browser_; | |
363 | |
364 // Whether the extended API and regular API are enabled. If both are false, | |
365 // Instant is effectively disabled. | |
366 const bool extended_enabled_; | |
367 bool instant_enabled_; | |
368 | |
369 // If true, the Instant URL is set to kLocalOmniboxPopupURL. | |
370 bool use_local_overlay_only_; | |
371 | |
372 // The state of the overlay page, i.e., the page owned by |overlay_|. Ignored | |
373 // if |instant_tab_| is in use. | |
374 InstantOverlayModel model_; | |
375 | |
376 // The three instances of InstantPage maintained by InstantController as | |
377 // described above. All three may be non-NULL in extended mode. If | |
378 // |instant_tab_| is not NULL, then |overlay_| is guaranteed to be hidden and | |
379 // messages will be sent to |instant_tab_| instead. | |
380 // | |
381 // In non-extended mode, only |overlay_| is ever non-NULL. | |
382 scoped_ptr<InstantOverlay> overlay_; | |
383 scoped_ptr<InstantNTP> ntp_; | |
384 scoped_ptr<InstantTab> instant_tab_; | |
385 | |
386 // The most recent full_text passed to Update(). If empty, we'll not accept | |
387 // search suggestions from |overlay_| or |instant_tab_|. | |
388 string16 last_omnibox_text_; | |
389 | |
390 // The most recent user_text passed to Update(). Used to filter out-of-date | |
391 // URL suggestions from the Instant page. | |
392 string16 last_user_text_; | |
393 | |
394 // True if the last Update() had an inline autocompletion. Used only to make | |
395 // sure that we don't accidentally suggest gray text suggestion in that case. | |
396 bool last_omnibox_text_has_inline_autocompletion_; | |
397 | |
398 // The most recent verbatim passed to Update(). Used only to ensure that we | |
399 // don't accidentally suggest an inline autocompletion. | |
400 bool last_verbatim_; | |
401 | |
402 // The most recent suggestion received from the page, minus any prefix that | |
403 // the user has typed. | |
404 InstantSuggestion last_suggestion_; | |
405 | |
406 // See comments on the getter above. | |
407 content::PageTransition last_transition_type_; | |
408 | |
409 // True if the last match passed to Update() was a search (versus a URL). | |
410 // Used to ensure that the overlay page is committable. | |
411 bool last_match_was_search_; | |
412 | |
413 // Omnibox focus state. | |
414 OmniboxFocusState omnibox_focus_state_; | |
415 | |
416 // The search model mode for the active tab. | |
417 chrome::search::Mode search_mode_; | |
418 | |
419 // Current omnibox popup bounds. | |
420 gfx::Rect popup_bounds_; | |
421 | |
422 // Last popup bounds passed to the page. | |
423 gfx::Rect last_popup_bounds_; | |
424 | |
425 // The start-edge margin and width of the omnibox, used by the page to align | |
426 // its suggestions with the omnibox. | |
427 gfx::Rect omnibox_bounds_; | |
428 | |
429 // Timer used to update the bounds of the omnibox popup. | |
430 base::OneShotTimer<InstantController> update_bounds_timer_; | |
431 | |
432 // For each key K => value N, the map says that we found that the search | |
433 // engine identified by Instant URL K didn't support the Instant API, or | |
434 // caused RenderView crashes in each of the last N times that we loaded it. | |
435 // If an Instant URL isn't present in the map at all or has a value 0, | |
436 // it means that search engine supports the Instant API (or we assume it does, | |
437 // since we haven't determined it doesn't) and it did not cause a crash. | |
438 std::map<std::string, int> blacklisted_urls_; | |
439 | |
440 // Search terms extraction (for autocomplete history matches) doesn't work | |
441 // on Instant URLs. So, whenever the user commits an Instant search, we add | |
442 // an equivalent non-Instant search URL to history, so that the search shows | |
443 // up in autocomplete history matches. | |
444 // TODO(sreeram): Remove when http://crbug.com/155373 is fixed. | |
445 GURL url_for_history_; | |
446 | |
447 // The timestamp at which query editing began. This value is used when the | |
448 // overlay is showed and cleared when the overlay is hidden. | |
449 base::Time first_interaction_time_; | |
450 | |
451 // Whether to allow the overlay to show search suggestions. In general, the | |
452 // overlay is allowed to show search suggestions whenever |search_mode_| is | |
453 // MODE_SEARCH_SUGGESTIONS, except in those cases where this is false. | |
454 bool allow_overlay_to_show_search_suggestions_; | |
455 | |
456 // List of events and their timestamps, useful in debugging Instant behaviour. | |
457 mutable std::list<std::pair<int64, std::string> > debug_events_; | |
458 | |
459 // Used for Top Sites async retrieval. | |
460 base::WeakPtrFactory<InstantController> weak_ptr_factory_; | |
461 | |
462 // Used to get notifications about Most Visted changes. | |
463 content::NotificationRegistrar registrar_; | |
464 | |
465 DISALLOW_COPY_AND_ASSIGN(InstantController); | |
466 }; | |
467 | |
468 #endif // CHROME_BROWSER_INSTANT_INSTANT_CONTROLLER_H_ | |
OLD | NEW |