blob: e793ba920c7c290dc675038b38fe4482f7cb0ca8 [file] [log] [blame]
[email protected]f0dc75232012-01-05 01:07:001// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]fd2b9ce2010-08-11 04:03:572// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]6b723f82010-10-05 20:14:275#include "chrome/browser/instant/instant_controller.h"
[email protected]fd2b9ce2010-08-11 04:03:576
[email protected]fd2b9ce2010-08-11 04:03:577#include "base/command_line.h"
[email protected]c55e3b82012-08-09 15:27:058#include "base/i18n/case_conversion.h"
[email protected]7e03e812010-11-15 23:01:019#include "base/metrics/histogram.h"
[email protected]853e01b2012-09-21 20:14:1110#include "base/time.h"
[email protected]941b260c2012-08-23 23:00:4711#include "base/utf_string_conversions.h"
12#include "chrome/browser/autocomplete/autocomplete_provider.h"
[email protected]f3d2b312012-08-23 22:27:5913#include "chrome/browser/favicon/favicon_service_factory.h"
[email protected]c55e3b82012-08-09 15:27:0514#include "chrome/browser/history/history.h"
15#include "chrome/browser/history/history_service_factory.h"
16#include "chrome/browser/history/history_tab_helper.h"
[email protected]37c917b2012-06-28 15:53:5917#include "chrome/browser/instant/instant_controller_delegate.h"
[email protected]6b723f82010-10-05 20:14:2718#include "chrome/browser/instant/instant_loader.h"
[email protected]ba6680f2010-11-01 20:35:0819#include "chrome/browser/platform_util.h"
[email protected]018cbb22010-10-11 22:32:0920#include "chrome/browser/prefs/pref_service.h"
[email protected]8e5c89a2011-06-07 18:13:3321#include "chrome/browser/search_engines/template_url_service.h"
22#include "chrome/browser/search_engines/template_url_service_factory.h"
[email protected]b67d0a42012-09-04 20:57:3523#include "chrome/browser/ui/search/search.h"
[email protected]a7eef8f2012-06-08 22:19:3924#include "chrome/browser/ui/tab_contents/tab_contents.h"
[email protected]432115822011-07-10 15:52:2725#include "chrome/common/chrome_notification_types.h"
[email protected]fd2b9ce2010-08-11 04:03:5726#include "chrome/common/chrome_switches.h"
[email protected]018cbb22010-10-11 22:32:0927#include "chrome/common/pref_names.h"
[email protected]c55e3b82012-08-09 15:27:0528#include "content/public/browser/favicon_status.h"
29#include "content/public/browser/navigation_entry.h"
[email protected]ad50def52011-10-19 23:17:0730#include "content/public/browser/notification_service.h"
[email protected]5626b0892012-02-20 14:46:5831#include "content/public/browser/render_widget_host_view.h"
[email protected]ef9572e2012-01-04 22:14:1232#include "content/public/browser/web_contents.h"
[email protected]fd2b9ce2010-08-11 04:03:5733
[email protected]6eb8ea92011-08-22 21:01:4134#if defined(TOOLKIT_VIEWS)
[email protected]c13be0d2011-11-22 02:09:5835#include "ui/views/widget/widget.h"
[email protected]6eb8ea92011-08-22 21:01:4136#endif
37
[email protected]c55e3b82012-08-09 15:27:0538namespace {
39
40enum PreviewUsageType {
41 PREVIEW_CREATED = 0,
42 PREVIEW_DELETED,
43 PREVIEW_LOADED,
44 PREVIEW_SHOWED,
45 PREVIEW_COMMITTED,
46 PREVIEW_NUM_TYPES,
47};
48
49// An artificial delay (in milliseconds) we introduce before telling the Instant
50// page about the new omnibox bounds, in cases where the bounds shrink. This is
51// to avoid the page jumping up/down very fast in response to bounds changes.
52const int kUpdateBoundsDelayMS = 1000;
53
54// The maximum number of times we'll load a non-Instant-supporting search engine
55// before we give up and blacklist it for the rest of the browsing session.
56const int kMaxInstantSupportFailures = 10;
57
[email protected]08a9f972012-08-27 23:23:1558// If an Instant page has not been used in these many milliseconds, it is
59// reloaded so that the page does not become stale.
60const int kStaleLoaderTimeoutMS = 3 * 3600 * 1000;
61
[email protected]c55e3b82012-08-09 15:27:0562std::string ModeToString(InstantController::Mode mode) {
63 switch (mode) {
[email protected]b67d0a42012-09-04 20:57:3564 case InstantController::EXTENDED: return "_Extended";
[email protected]c55e3b82012-08-09 15:27:0565 case InstantController::INSTANT: return "_Instant";
66 case InstantController::SUGGEST: return "_Suggest";
67 case InstantController::HIDDEN: return "_Hidden";
68 case InstantController::SILENT: return "_Silent";
[email protected]b67d0a42012-09-04 20:57:3569 case InstantController::DISABLED: return "_Disabled";
[email protected]c55e3b82012-08-09 15:27:0570 }
71
72 NOTREACHED();
73 return std::string();
74}
75
76void AddPreviewUsageForHistogram(InstantController::Mode mode,
77 PreviewUsageType usage) {
78 DCHECK(0 <= usage && usage < PREVIEW_NUM_TYPES) << usage;
79 base::Histogram* histogram = base::LinearHistogram::FactoryGet(
80 "Instant.Previews" + ModeToString(mode), 1, PREVIEW_NUM_TYPES,
81 PREVIEW_NUM_TYPES + 1, base::Histogram::kUmaTargetedHistogramFlag);
82 histogram->Add(usage);
83}
84
85void AddSessionStorageHistogram(InstantController::Mode mode,
86 const TabContents* tab1,
87 const TabContents* tab2) {
88 base::Histogram* histogram = base::BooleanHistogram::FactoryGet(
89 "Instant.SessionStorageNamespace" + ModeToString(mode),
90 base::Histogram::kUmaTargetedHistogramFlag);
[email protected]d1198fd2012-08-13 22:50:1991 const content::SessionStorageNamespaceMap& session_storage_map1 =
92 tab1->web_contents()->GetController().GetSessionStorageNamespaceMap();
93 const content::SessionStorageNamespaceMap& session_storage_map2 =
94 tab2->web_contents()->GetController().GetSessionStorageNamespaceMap();
95 bool is_session_storage_the_same =
96 session_storage_map1.size() == session_storage_map2.size();
97 if (is_session_storage_the_same) {
98 // The size is the same, so let's check that all entries match.
99 for (content::SessionStorageNamespaceMap::const_iterator
100 it1 = session_storage_map1.begin(),
101 it2 = session_storage_map2.begin();
102 it1 != session_storage_map1.end() &&
103 it2 != session_storage_map2.end();
104 ++it1, ++it2) {
105 if (it1->first != it2->first || it1->second != it2->second) {
106 is_session_storage_the_same = false;
107 break;
108 }
109 }
110 }
111 histogram->AddBoolean(is_session_storage_the_same);
[email protected]c55e3b82012-08-09 15:27:05112}
113
[email protected]b67d0a42012-09-04 20:57:35114InstantController::Mode GetModeForProfile(Profile* profile) {
115 if (!profile || profile->IsOffTheRecord() || !profile->GetPrefs() ||
116 !profile->GetPrefs()->GetBoolean(prefs::kInstantEnabled))
117 return InstantController::DISABLED;
[email protected]c55e3b82012-08-09 15:27:05118
[email protected]b67d0a42012-09-04 20:57:35119 return chrome::search::IsInstantExtendedAPIEnabled(profile) ?
120 InstantController::EXTENDED : InstantController::INSTANT;
[email protected]03bb953d2010-09-14 21:38:30121}
122
[email protected]b67d0a42012-09-04 20:57:35123} // namespace
124
[email protected]6b723f82010-10-05 20:14:27125InstantController::~InstantController() {
[email protected]c55e3b82012-08-09 15:27:05126 if (GetPreviewContents())
127 AddPreviewUsageForHistogram(mode_, PREVIEW_DELETED);
[email protected]03bb953d2010-09-14 21:38:30128}
129
[email protected]7e03e812010-11-15 23:01:01130// static
[email protected]b67d0a42012-09-04 20:57:35131InstantController* InstantController::CreateInstant(
132 Profile* profile,
133 InstantControllerDelegate* delegate) {
134 const Mode mode = GetModeForProfile(profile);
135 return mode == DISABLED ? NULL : new InstantController(delegate, mode);
136}
137
138// static
139bool InstantController::IsExtendedAPIEnabled(Profile* profile) {
140 return GetModeForProfile(profile) == EXTENDED;
141}
142
143// static
144bool InstantController::IsInstantEnabled(Profile* profile) {
145 const Mode mode = GetModeForProfile(profile);
146 return mode == EXTENDED || mode == INSTANT;
147}
148
149// static
150bool InstantController::IsSuggestEnabled(Profile* profile) {
151 const Mode mode = GetModeForProfile(profile);
152 return mode == EXTENDED || mode == INSTANT || mode == SUGGEST;
153}
154
155// static
[email protected]7e03e812010-11-15 23:01:01156void InstantController::RegisterUserPrefs(PrefService* prefs) {
[email protected]c55e3b82012-08-09 15:27:05157 prefs->RegisterBooleanPref(prefs::kInstantConfirmDialogShown, false,
[email protected]18c2ffac2011-09-16 21:07:29158 PrefService::SYNCABLE_PREF);
[email protected]c55e3b82012-08-09 15:27:05159 prefs->RegisterBooleanPref(prefs::kInstantEnabled, false,
[email protected]18c2ffac2011-09-16 21:07:29160 PrefService::SYNCABLE_PREF);
[email protected]7ab402e62012-06-20 19:49:18161
162 // TODO(jamescook): Move this to search controller.
[email protected]b67d0a42012-09-04 20:57:35163 prefs->RegisterDoublePref(prefs::kInstantAnimationScaleFactor, 1.0,
[email protected]7ab402e62012-06-20 19:49:18164 PrefService::UNSYNCABLE_PREF);
[email protected]7e03e812010-11-15 23:01:01165}
166
[email protected]05cf2fa2012-05-29 20:36:06167bool InstantController::Update(const AutocompleteMatch& match,
[email protected]6b723f82010-10-05 20:14:27168 const string16& user_text,
[email protected]cd223322012-08-26 19:54:42169 const string16& full_text,
170 bool verbatim) {
[email protected]c55e3b82012-08-09 15:27:05171 const TabContents* active_tab = delegate_->GetActiveTabContents();
[email protected]bdf1d862010-11-24 02:46:11172
[email protected]c55e3b82012-08-09 15:27:05173 // We could get here with no active tab if the Browser is closing.
174 if (!active_tab) {
175 Hide();
176 return false;
177 }
178
179 std::string instant_url;
180 Profile* profile = active_tab->profile();
181
[email protected]de595562012-09-07 18:31:04182 // If the match's TemplateURL is valid, it's a search query; use it. If it's
183 // not valid, it's likely a URL; in EXTENDED mode, try using the default
184 // search engine's TemplateURL instead.
[email protected]2e4164d2012-09-18 18:22:15185 const GURL& tab_url = active_tab->web_contents()->GetURL();
186 if (GetInstantURL(match.GetTemplateURL(profile), tab_url, &instant_url)) {
[email protected]de595562012-09-07 18:31:04187 ResetLoader(instant_url, active_tab);
188 } else if (mode_ != EXTENDED || !CreateDefaultLoader()) {
[email protected]c55e3b82012-08-09 15:27:05189 Hide();
190 return false;
191 }
192
[email protected]c55e3b82012-08-09 15:27:05193 if (full_text.empty()) {
194 Hide();
195 return false;
196 }
197
[email protected]c55e3b82012-08-09 15:27:05198 // Track the non-Instant search URL for this query.
199 url_for_history_ = match.destination_url;
[email protected]97b6c4f2010-09-27 19:31:26200 last_transition_type_ = match.transition;
[email protected]de595562012-09-07 18:31:04201 last_active_tab_ = active_tab;
202 last_match_was_search_ = AutocompleteMatch::IsSearchType(match.type);
[email protected]c55e3b82012-08-09 15:27:05203
[email protected]cd223322012-08-26 19:54:42204 // In EXTENDED mode, we send only |user_text| as the query text. In all other
205 // modes, we use the entire |full_text|.
206 const string16& query_text = mode_ == EXTENDED ? user_text : full_text;
[email protected]b67d0a42012-09-04 20:57:35207 string16 last_query_text = mode_ == EXTENDED ?
208 last_user_text_ : last_full_text_;
[email protected]64f09b82011-10-13 16:17:20209 last_user_text_ = user_text;
[email protected]cd223322012-08-26 19:54:42210 last_full_text_ = full_text;
[email protected]64f09b82011-10-13 16:17:20211
[email protected]c55e3b82012-08-09 15:27:05212 // Don't send an update to the loader if the query text hasn't changed.
[email protected]cd223322012-08-26 19:54:42213 if (query_text == last_query_text && verbatim == last_verbatim_) {
214 // Reuse the last suggestion, as it's still valid.
215 delegate_->SetSuggestedText(last_suggestion_.text,
216 last_suggestion_.behavior);
[email protected]fd2b9ce2010-08-11 04:03:57217
[email protected]c55e3b82012-08-09 15:27:05218 // We need to call Show() here because of this:
219 // 1. User has typed a query (say Q). Instant overlay is showing results.
220 // 2. User arrows-down to a URL entry or erases all omnibox text. Both of
221 // these cause the overlay to Hide().
222 // 3. User arrows-up to Q or types Q again. The last text we processed is
223 // still Q, so we don't Update() the loader, but we do need to Show().
[email protected]941b260c2012-08-23 23:00:47224 if (loader_processed_last_update_ &&
[email protected]b67d0a42012-09-04 20:57:35225 (mode_ == INSTANT || mode_ == EXTENDED))
[email protected]b0628ad2012-09-20 00:48:47226 Show(100, INSTANT_SIZE_PERCENT);
[email protected]64f09b82011-10-13 16:17:20227 return true;
228 }
[email protected]36d5e5592010-11-15 20:45:59229
[email protected]c55e3b82012-08-09 15:27:05230 last_verbatim_ = verbatim;
231 loader_processed_last_update_ = false;
[email protected]0e7cb682012-08-15 04:04:38