ozone: evdev: Sync caps lock LED state to evdev
[chromium-blink-merge.git] / chrome / browser / ui / search / search_tab_helper.cc
blob3c4af9745d0404d84336acba969a3fbac9c248e9
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.
5 #include "chrome/browser/ui/search/search_tab_helper.h"
7 #include <set>
9 #include "base/memory/scoped_ptr.h"
10 #include "base/metrics/histogram.h"
11 #include "base/strings/string16.h"
12 #include "base/strings/string_util.h"
13 #include "chrome/browser/chrome_notification_types.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/search/instant_service.h"
16 #include "chrome/browser/search/instant_service_factory.h"
17 #include "chrome/browser/search/search.h"
18 #include "chrome/browser/signin/signin_manager_factory.h"
19 #include "chrome/browser/sync/profile_sync_service.h"
20 #include "chrome/browser/sync/profile_sync_service_factory.h"
21 #include "chrome/browser/ui/app_list/app_list_util.h"
22 #include "chrome/browser/ui/browser_navigator.h"
23 #include "chrome/browser/ui/browser_window.h"
24 #include "chrome/browser/ui/location_bar/location_bar.h"
25 #include "chrome/browser/ui/omnibox/omnibox_edit_model.h"
26 #include "chrome/browser/ui/omnibox/omnibox_popup_model.h"
27 #include "chrome/browser/ui/omnibox/omnibox_view.h"
28 #include "chrome/browser/ui/search/instant_search_prerenderer.h"
29 #include "chrome/browser/ui/search/search_ipc_router_policy_impl.h"
30 #include "chrome/browser/ui/search/search_tab_helper_delegate.h"
31 #include "chrome/browser/ui/tab_contents/core_tab_helper.h"
32 #include "chrome/browser/ui/webui/ntp/ntp_user_data_logger.h"
33 #include "chrome/common/url_constants.h"
34 #include "chrome/grit/generated_resources.h"
35 #include "components/google/core/browser/google_util.h"
36 #include "components/search/search.h"
37 #include "components/signin/core/browser/signin_manager.h"
38 #include "content/public/browser/navigation_controller.h"
39 #include "content/public/browser/navigation_details.h"
40 #include "content/public/browser/navigation_entry.h"
41 #include "content/public/browser/navigation_type.h"
42 #include "content/public/browser/notification_service.h"
43 #include "content/public/browser/notification_source.h"
44 #include "content/public/browser/render_frame_host.h"
45 #include "content/public/browser/render_process_host.h"
46 #include "content/public/browser/user_metrics.h"
47 #include "content/public/browser/web_contents.h"
48 #include "content/public/common/referrer.h"
49 #include "google_apis/gaia/gaia_auth_util.h"
50 #include "net/base/net_errors.h"
51 #include "ui/base/l10n/l10n_util.h"
52 #include "ui/base/page_transition_types.h"
53 #include "url/gurl.h"
55 DEFINE_WEB_CONTENTS_USER_DATA_KEY(SearchTabHelper);
57 namespace {
59 bool IsCacheableNTP(const content::WebContents* contents) {
60 const content::NavigationEntry* entry =
61 contents->GetController().GetLastCommittedEntry();
62 return chrome::NavEntryIsInstantNTP(contents, entry) &&
63 entry->GetURL() != GURL(chrome::kChromeSearchLocalNtpUrl);
66 bool IsNTP(const content::WebContents* contents) {
67 // We can't use WebContents::GetURL() because that uses the active entry,
68 // whereas we want the visible entry.
69 const content::NavigationEntry* entry =
70 contents->GetController().GetVisibleEntry();
71 if (entry && entry->GetVirtualURL() == GURL(chrome::kChromeUINewTabURL))
72 return true;
74 return chrome::IsInstantNTP(contents);
77 bool IsSearchResults(const content::WebContents* contents) {
78 return !chrome::GetSearchTerms(contents).empty();
81 bool IsLocal(const content::WebContents* contents) {
82 if (!contents)
83 return false;
84 const content::NavigationEntry* entry =
85 contents->GetController().GetVisibleEntry();
86 return entry && entry->GetURL() == GURL(chrome::kChromeSearchLocalNtpUrl);
89 // Returns true if |contents| are rendered inside an Instant process.
90 bool InInstantProcess(Profile* profile,
91 const content::WebContents* contents) {
92 if (!profile || !contents)
93 return false;
95 InstantService* instant_service =
96 InstantServiceFactory::GetForProfile(profile);
97 return instant_service &&
98 instant_service->IsInstantProcess(
99 contents->GetRenderProcessHost()->GetID());
102 // Called when an NTP finishes loading. If the load start time was noted,
103 // calculates and logs the total load time.
104 void RecordNewTabLoadTime(content::WebContents* contents) {
105 CoreTabHelper* core_tab_helper = CoreTabHelper::FromWebContents(contents);
106 if (core_tab_helper->new_tab_start_time().is_null())
107 return;
109 base::TimeDelta duration =
110 base::TimeTicks::Now() - core_tab_helper->new_tab_start_time();
111 if (IsCacheableNTP(contents)) {
112 if (google_util::IsGoogleDomainUrl(
113 contents->GetController().GetLastCommittedEntry()->GetURL(),
114 google_util::ALLOW_SUBDOMAIN,
115 google_util::DISALLOW_NON_STANDARD_PORTS)) {
116 UMA_HISTOGRAM_TIMES("Tab.NewTabOnload.Google", duration);
117 } else {
118 UMA_HISTOGRAM_TIMES("Tab.NewTabOnload.Other", duration);
120 } else {
121 UMA_HISTOGRAM_TIMES("Tab.NewTabOnload.Local", duration);
123 core_tab_helper->set_new_tab_start_time(base::TimeTicks());
126 // Returns true if the user wants to sync history. This function returning true
127 // is not a guarantee that history is being synced, but it can be used to
128 // disable a feature that should not be shown to users who prefer not to sync
129 // their history.
130 bool IsHistorySyncEnabled(Profile* profile) {
131 ProfileSyncService* sync =
132 ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile);
133 return sync &&
134 sync->GetPreferredDataTypes().Has(syncer::HISTORY_DELETE_DIRECTIVES);
137 bool OmniboxHasFocus(OmniboxView* omnibox) {
138 return omnibox && omnibox->model()->has_focus();
141 } // namespace
143 SearchTabHelper::SearchTabHelper(content::WebContents* web_contents)
144 : WebContentsObserver(web_contents),
145 is_search_enabled_(chrome::IsInstantExtendedAPIEnabled()),
146 web_contents_(web_contents),
147 ipc_router_(web_contents,
148 this,
149 make_scoped_ptr(new SearchIPCRouterPolicyImpl(web_contents))),
150 instant_service_(NULL),
151 delegate_(NULL),
152 omnibox_has_focus_fn_(&OmniboxHasFocus) {
153 if (!is_search_enabled_)
154 return;
156 instant_service_ =
157 InstantServiceFactory::GetForProfile(
158 Profile::FromBrowserContext(web_contents_->GetBrowserContext()));
159 if (instant_service_)
160 instant_service_->AddObserver(this);
163 SearchTabHelper::~SearchTabHelper() {
164 if (instant_service_)
165 instant_service_->RemoveObserver(this);
168 void SearchTabHelper::InitForPreloadedNTP() {
169 UpdateMode(true, true);
172 void SearchTabHelper::OmniboxInputStateChanged() {
173 if (!is_search_enabled_)
174 return;
176 UpdateMode(false, false);
179 void SearchTabHelper::OmniboxFocusChanged(OmniboxFocusState state,
180 OmniboxFocusChangeReason reason) {
181 content::NotificationService::current()->Notify(
182 chrome::NOTIFICATION_OMNIBOX_FOCUS_CHANGED,
183 content::Source<SearchTabHelper>(this),
184 content::NotificationService::NoDetails());
186 ipc_router_.OmniboxFocusChanged(state, reason);
188 // Don't send oninputstart/oninputend updates in response to focus changes
189 // if there's a navigation in progress. This prevents Chrome from sending
190 // a spurious oninputend when the user accepts a match in the omnibox.
191 if (web_contents_->GetController().GetPendingEntry() == NULL) {
192 ipc_router_.SetInputInProgress(IsInputInProgress());
194 InstantSearchPrerenderer* prerenderer =
195 InstantSearchPrerenderer::GetForProfile(profile());
196 if (!prerenderer || !chrome::ShouldPrerenderInstantUrlOnOmniboxFocus())
197 return;
199 if (state == OMNIBOX_FOCUS_NONE) {
200 prerenderer->Cancel();
201 return;
204 if (!IsSearchResultsPage()) {
205 prerenderer->Init(
206 web_contents_->GetController().GetDefaultSessionStorageNamespace(),
207 web_contents_->GetContainerBounds().size());
212 void SearchTabHelper::NavigationEntryUpdated() {
213 if (!is_search_enabled_)
214 return;
216 UpdateMode(false, false);
219 void SearchTabHelper::InstantSupportChanged(bool instant_support) {
220 if (!is_search_enabled_)
221 return;
223 InstantSupportState new_state = instant_support ? INSTANT_SUPPORT_YES :
224 INSTANT_SUPPORT_NO;
226 model_.SetInstantSupportState(new_state);
228 content::NavigationEntry* entry =
229 web_contents_->GetController().GetLastCommittedEntry();
230 if (entry) {
231 chrome::SetInstantSupportStateInNavigationEntry(new_state, entry);
232 if (delegate_ && !instant_support)
233 delegate_->OnWebContentsInstantSupportDisabled(web_contents_);
237 bool SearchTabHelper::SupportsInstant() const {
238 return model_.instant_support() == INSTANT_SUPPORT_YES;
241 void SearchTabHelper::SetSuggestionToPrefetch(
242 const InstantSuggestion& suggestion) {
243 ipc_router_.SetSuggestionToPrefetch(suggestion);
246 void SearchTabHelper::Submit(const base::string16& text,
247 const EmbeddedSearchRequestParams& params) {
248 ipc_router_.Submit(text, params);
251 void SearchTabHelper::OnTabActivated() {
252 ipc_router_.OnTabActivated();
254 OmniboxView* omnibox_view = GetOmniboxView();
255 if (chrome::ShouldPrerenderInstantUrlOnOmniboxFocus() &&
256 omnibox_has_focus_fn_(omnibox_view)) {
257 InstantSearchPrerenderer* prerenderer =
258 InstantSearchPrerenderer::GetForProfile(profile());
259 if (prerenderer && !IsSearchResultsPage()) {
260 prerenderer->Init(
261 web_contents_->GetController().GetDefaultSessionStorageNamespace(),
262 web_contents_->GetContainerBounds().size());
267 void SearchTabHelper::OnTabDeactivated() {
268 ipc_router_.OnTabDeactivated();
271 void SearchTabHelper::ToggleVoiceSearch() {
272 ipc_router_.ToggleVoiceSearch();
275 bool SearchTabHelper::IsSearchResultsPage() {
276 return model_.mode().is_origin_search();
279 void SearchTabHelper::RenderViewCreated(
280 content::RenderViewHost* render_view_host) {
281 ipc_router_.SetPromoInformation(IsAppLauncherEnabled());
284 void SearchTabHelper::DidStartNavigationToPendingEntry(
285 const GURL& url,
286 content::NavigationController::ReloadType /* reload_type */) {
287 if (chrome::IsNTPURL(url, profile())) {
288 // Set the title on any pending entry corresponding to the NTP. This
289 // prevents any flickering of the tab title.
290 content::NavigationEntry* entry =
291 web_contents_->GetController().GetPendingEntry();
292 if (entry)
293 entry->SetTitle(l10n_util::GetStringUTF16(IDS_NEW_TAB_TITLE));
297 void SearchTabHelper::DidNavigateMainFrame(
298 const content::LoadCommittedDetails& details,
299 const content::FrameNavigateParams& params) {
300 if (IsCacheableNTP(web_contents_)) {
301 UMA_HISTOGRAM_ENUMERATION("InstantExtended.CacheableNTPLoad",
302 chrome::CACHEABLE_NTP_LOAD_SUCCEEDED,
303 chrome::CACHEABLE_NTP_LOAD_MAX);
306 // Always set the title on the new tab page to be the one from our UI
307 // resources. Normally, we set the title when we begin a NTP load, but it can
308 // get reset in several places (like when you press Reload). This check
309 // ensures that the title is properly set to the string defined by the Chrome
310 // UI language (rather than the server language) in all cases.
312 // We only override the title when it's nonempty to allow the page to set the
313 // title if it really wants. An empty title means to use the default. There's
314 // also a race condition between this code and the page's SetTitle call which
315 // this rule avoids.
316 content::NavigationEntry* entry =
317 web_contents_->GetController().GetLastCommittedEntry();
318 if (entry && entry->GetTitle().empty() &&
319 (entry->GetVirtualURL() == GURL(chrome::kChromeUINewTabURL) ||
320 chrome::NavEntryIsInstantNTP(web_contents_, entry))) {
321 entry->SetTitle(l10n_util::GetStringUTF16(IDS_NEW_TAB_TITLE));
325 void SearchTabHelper::DidFinishLoad(content::RenderFrameHost* render_frame_host,
326 const GURL& /* validated_url */) {
327 if (!render_frame_host->GetParent()) {
328 if (chrome::IsInstantNTP(web_contents_))
329 RecordNewTabLoadTime(web_contents_);
331 DetermineIfPageSupportsInstant();
335 void SearchTabHelper::NavigationEntryCommitted(
336 const content::LoadCommittedDetails& load_details) {
337 if (!is_search_enabled_)
338 return;
340 if (!load_details.is_main_frame)
341 return;
343 if (chrome::ShouldAssignURLToInstantRenderer(web_contents_->GetURL(),
344 profile())) {
345 InstantService* instant_service =
346 InstantServiceFactory::GetForProfile(profile());
347 ipc_router_.SetOmniboxStartMargin(instant_service->omnibox_start_margin());
348 ipc_router_.SetDisplayInstantResults();
351 UpdateMode(true, false);
353 content::NavigationEntry* entry =
354 web_contents_->GetController().GetVisibleEntry();
355 DCHECK(entry);
357 // Already determined the instant support state for this page, do not reset
358 // the instant support state.
360 // When we get a navigation entry committed event, there seem to be two ways
361 // to tell whether the navigation was "in-page". Ideally, when
362 // LoadCommittedDetails::is_in_page is true, we should have
363 // LoadCommittedDetails::type to be NAVIGATION_TYPE_IN_PAGE. Unfortunately,
364 // they are different in some cases. To workaround this bug, we are checking
365 // (is_in_page || type == NAVIGATION_TYPE_IN_PAGE). Please refer to
366 // crbug.com/251330 for more details.
367 if (load_details.is_in_page ||
368 load_details.type == content::NAVIGATION_TYPE_IN_PAGE) {
369 // When an "in-page" navigation happens, we will not receive a
370 // DidFinishLoad() event. Therefore, we will not determine the Instant
371 // support for the navigated page. So, copy over the Instant support from
372 // the previous entry. If the page does not support Instant, update the
373 // location bar from here to turn off search terms replacement.
374 chrome::SetInstantSupportStateInNavigationEntry(model_.instant_support(),
375 entry);
376 if (delegate_ && model_.instant_support() == INSTANT_SUPPORT_NO)
377 delegate_->OnWebContentsInstantSupportDisabled(web_contents_);
378 return;
381 model_.SetInstantSupportState(INSTANT_SUPPORT_UNKNOWN);
382 model_.SetVoiceSearchSupported(false);
383 chrome::SetInstantSupportStateInNavigationEntry(model_.instant_support(),
384 entry);
386 if (InInstantProcess(profile(), web_contents_))
387 ipc_router_.OnNavigationEntryCommitted();
390 void SearchTabHelper::OnInstantSupportDetermined(bool supports_instant) {
391 InstantSupportChanged(supports_instant);
394 void SearchTabHelper::OnSetVoiceSearchSupport(bool supports_voice_search) {
395 model_.SetVoiceSearchSupported(supports_voice_search);
398 void SearchTabHelper::ThemeInfoChanged(const ThemeBackgroundInfo& theme_info) {
399 ipc_router_.SendThemeBackgroundInfo(theme_info);
402 void SearchTabHelper::MostVisitedItemsChanged(
403 const std::vector<InstantMostVisitedItem>& items) {
404 ipc_router_.SendMostVisitedItems(items);
407 void SearchTabHelper::OmniboxStartMarginChanged(int omnibox_start_margin) {
408 ipc_router_.SetOmniboxStartMargin(omnibox_start_margin);
411 void SearchTabHelper::FocusOmnibox(OmniboxFocusState state) {
412 // TODO(kmadhusu): Move platform specific code from here and get rid of #ifdef.
413 #if !defined(OS_ANDROID)
414 OmniboxView* omnibox = GetOmniboxView();
415 if (!omnibox)
416 return;
418 // Do not add a default case in the switch block for the following reasons:
419 // (1) Explicitly handle the new states. If new states are added in the
420 // OmniboxFocusState, the compiler will warn the developer to handle the new
421 // states.
422 // (2) An attacker may control the renderer and sends the browser process a
423 // malformed IPC. This function responds to the invalid |state| values by
424 // doing nothing instead of crashing the browser process (intentional no-op).
425 switch (state) {
426 case OMNIBOX_FOCUS_VISIBLE:
427 omnibox->SetFocus();
428 omnibox->model()->SetCaretVisibility(true);
429 break;
430 case OMNIBOX_FOCUS_INVISIBLE:
431 omnibox->SetFocus();
432 omnibox->model()->SetCaretVisibility(false);
433 // If the user clicked on the fakebox, any text already in the omnibox
434 // should get cleared when they start typing. Selecting all the existing
435 // text is a convenient way to accomplish this. It also gives a slight
436 // visual cue to users who really understand selection state about what
437 // will happen if they start typing.
438 omnibox->SelectAll(false);
439 omnibox->ShowImeIfNeeded();
440 break;
441 case OMNIBOX_FOCUS_NONE:
442 // Remove focus only if the popup is closed. This will prevent someone
443 // from changing the omnibox value and closing the popup without user
444 // interaction.
445 if (!omnibox->model()->popup_model()->IsOpen())
446 web_contents()->Focus();
447 break;
449 #endif
452 void SearchTabHelper::NavigateToURL(const GURL& url,
453 WindowOpenDisposition disposition,
454 bool is_most_visited_item_url) {
455 if (is_most_visited_item_url) {
456 content::RecordAction(
457 base::UserMetricsAction("InstantExtended.MostVisitedClicked"));
460 if (delegate_)
461 delegate_->NavigateOnThumbnailClick(url, disposition, web_contents_);
464 void SearchTabHelper::OnDeleteMostVisitedItem(const GURL& url) {
465 DCHECK(!url.is_empty());
466 if (instant_service_)
467 instant_service_->DeleteMostVisitedItem(url);
470 void SearchTabHelper::OnUndoMostVisitedDeletion(const GURL& url) {
471 DCHECK(!url.is_empty());
472 if (instant_service_)
473 instant_service_->UndoMostVisitedDeletion(url);
476 void SearchTabHelper::OnUndoAllMostVisitedDeletions() {
477 if (instant_service_)
478 instant_service_->UndoAllMostVisitedDeletions();
481 void SearchTabHelper::OnLogEvent(NTPLoggingEventType event) {
482 // TODO(kmadhusu): Move platform specific code from here and get rid of #ifdef.
483 #if !defined(OS_ANDROID)
484 NTPUserDataLogger::GetOrCreateFromWebContents(
485 web_contents())->LogEvent(event);
486 #endif
489 void SearchTabHelper::OnLogMostVisitedImpression(
490 int position, const base::string16& provider) {
491 // TODO(kmadhusu): Move platform specific code from here and get rid of #ifdef.
492 #if !defined(OS_ANDROID)
493 NTPUserDataLogger::GetOrCreateFromWebContents(
494 web_contents())->LogMostVisitedImpression(position, provider);
495 #endif
498 void SearchTabHelper::OnLogMostVisitedNavigation(
499 int position, const base::string16& provider) {
500 // TODO(kmadhusu): Move platform specific code from here and get rid of #ifdef.
501 #if !defined(OS_ANDROID)
502 NTPUserDataLogger::GetOrCreateFromWebContents(
503 web_contents())->LogMostVisitedNavigation(position, provider);
504 #endif
507 void SearchTabHelper::PasteIntoOmnibox(const base::string16& text) {
508 // TODO(kmadhusu): Move platform specific code from here and get rid of #ifdef.
509 #if !defined(OS_ANDROID)
510 OmniboxView* omnibox = GetOmniboxView();
511 if (!omnibox)
512 return;
513 // The first case is for right click to paste, where the text is retrieved
514 // from the clipboard already sanitized. The second case is needed to handle
515 // drag-and-drop value and it has to be sanitazed before setting it into the
516 // omnibox.
517 base::string16 text_to_paste = text.empty() ? omnibox->GetClipboardText() :
518 omnibox->SanitizeTextForPaste(text);
520 if (text_to_paste.empty())
521 return;
523 if (!omnibox->model()->has_focus())
524 omnibox->SetFocus();
526 omnibox->OnBeforePossibleChange();
527 omnibox->model()->OnPaste();
528 omnibox->SetUserText(text_to_paste);
529 omnibox->OnAfterPossibleChange();
530 #endif
533 void SearchTabHelper::OnChromeIdentityCheck(const base::string16& identity) {
534 SigninManagerBase* manager = SigninManagerFactory::GetForProfile(profile());
535 if (manager) {
536 ipc_router_.SendChromeIdentityCheckResult(
537 identity, gaia::AreEmailsSame(base::UTF16ToUTF8(identity),
538 manager->GetAuthenticatedUsername()));
539 } else {
540 ipc_router_.SendChromeIdentityCheckResult(identity, false);
544 void SearchTabHelper::OnHistorySyncCheck() {
545 ipc_router_.SendHistorySyncCheckResult(IsHistorySyncEnabled(profile()));
548 void SearchTabHelper::UpdateMode(bool update_origin, bool is_preloaded_ntp) {
549 SearchMode::Type type = SearchMode::MODE_DEFAULT;
550 SearchMode::Origin origin = SearchMode::ORIGIN_DEFAULT;
551 if (IsNTP(web_contents_) || is_preloaded_ntp) {
552 type = SearchMode::MODE_NTP;
553 origin = SearchMode::ORIGIN_NTP;
554 } else if (IsSearchResults(web_contents_)) {
555 type = SearchMode::MODE_SEARCH_RESULTS;
556 origin = SearchMode::ORIGIN_SEARCH;
558 if (!update_origin)
559 origin = model_.mode().origin;
561 OmniboxView* omnibox = GetOmniboxView();
562 if (omnibox && omnibox->model()->user_input_in_progress())
563 type = SearchMode::MODE_SEARCH_SUGGESTIONS;
565 SearchMode old_mode(model_.mode());
566 model_.SetMode(SearchMode(type, origin));
567 if (old_mode.is_ntp() != model_.mode().is_ntp()) {
568 ipc_router_.SetInputInProgress(IsInputInProgress());
572 void SearchTabHelper::DetermineIfPageSupportsInstant() {
573 if (!InInstantProcess(profile(), web_contents_)) {
574 // The page is not in the Instant process. This page does not support
575 // instant. If we send an IPC message to a page that is not in the Instant
576 // process, it will never receive it and will never respond. Therefore,
577 // return immediately.
578 InstantSupportChanged(false);
579 } else if (IsLocal(web_contents_)) {
580 // Local pages always support Instant.
581 InstantSupportChanged(true);
582 } else {
583 ipc_router_.DetermineIfPageSupportsInstant();
587 Profile* SearchTabHelper::profile() const {
588 return Profile::FromBrowserContext(web_contents_->GetBrowserContext());
591 bool SearchTabHelper::IsInputInProgress() const {
592 OmniboxView* omnibox = GetOmniboxView();
593 return !model_.mode().is_ntp() && omnibox &&
594 omnibox->model()->focus_state() == OMNIBOX_FOCUS_VISIBLE;
597 OmniboxView* SearchTabHelper::GetOmniboxView() const {
598 return delegate_ ? delegate_->GetOmniboxView() : NULL;