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

Side by Side Diff: chrome/browser/ui/search/search.cc

Issue 12335141: Move c/b/ui/search/search.* to c/b/instant/ (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebase. 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
« no previous file with comments | « chrome/browser/ui/search/search.h ('k') | chrome/browser/ui/search/search_delegate_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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 #include "chrome/browser/ui/search/search.h"
6
7 #include "base/command_line.h"
8 #include "base/metrics/field_trial.h"
9 #include "base/metrics/histogram.h"
10 #include "base/prefs/pref_service.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/string_split.h"
13 #include "chrome/browser/instant/instant_service.h"
14 #include "chrome/browser/instant/instant_service_factory.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "chrome/browser/search_engines/template_url_service.h"
17 #include "chrome/browser/search_engines/template_url_service_factory.h"
18 #include "chrome/common/chrome_switches.h"
19 #include "chrome/common/pref_names.h"
20 #include "chrome/common/url_constants.h"
21 #include "components/user_prefs/pref_registry_syncable.h"
22 #include "content/public/browser/navigation_entry.h"
23 #include "content/public/browser/render_process_host.h"
24 #include "content/public/browser/web_contents.h"
25
26 namespace chrome {
27 namespace search {
28
29 namespace {
30
31 // The default value we should assign to the instant_extended.enabled pref. As
32 // with other prefs, the default is used only when the user hasn't toggled the
33 // pref explicitly.
34 enum InstantExtendedDefault {
35 INSTANT_DEFAULT_ON, // Default the pref to be enabled.
36 INSTANT_USE_EXISTING, // Use the current value of the instant.enabled pref.
37 INSTANT_DEFAULT_OFF, // Default the pref to be disabled.
38 };
39
40 // Configuration options for Embedded Search.
41 // InstantExtended field trials are named in such a way that we can parse out
42 // the experiment configuration from the trial's group name in order to give
43 // us maximum flexability in running experiments.
44 // Field trial groups should be named things like "Group7 espv:2 instant:1".
45 // The first token is always GroupN for some integer N, followed by a
46 // space-delimited list of key:value pairs which correspond to these flags:
47 const char kEmbeddedPageVersionFlagName[] = "espv";
48 const uint64 kEmbeddedPageVersionDisabled = 0;
49 const uint64 kEmbeddedPageVersionDefault = 2;
50
51 const char kInstantExtendedActivationName[] = "instant";
52 const InstantExtendedDefault kInstantExtendedActivationDefault =
53 INSTANT_DEFAULT_ON;
54
55 // Constants for the field trial name and group prefix.
56 const char kInstantExtendedFieldTrialName[] = "InstantExtended";
57 const char kGroupNumberPrefix[] = "Group";
58
59 // If the field trial's group name ends with this string its configuration will
60 // be ignored and Instant Extended will not be enabled by default.
61 const char kDisablingSuffix[] = "DISABLED";
62
63 TemplateURL* GetDefaultSearchProviderTemplateURL(Profile* profile) {
64 TemplateURLService* template_url_service =
65 TemplateURLServiceFactory::GetForProfile(profile);
66 if (template_url_service)
67 return template_url_service->GetDefaultSearchProvider();
68 return NULL;
69 }
70
71 GURL TemplateURLRefToGURL(const TemplateURLRef& ref) {
72 return GURL(
73 ref.ReplaceSearchTerms(TemplateURLRef::SearchTermsArgs(string16())));
74 }
75
76 bool MatchesOriginAndPath(const GURL& my_url, const GURL& other_url) {
77 return my_url.host() == other_url.host() &&
78 my_url.port() == other_url.port() &&
79 my_url.path() == other_url.path() &&
80 (my_url.scheme() == other_url.scheme() ||
81 (my_url.SchemeIs(chrome::kHttpsScheme) &&
82 other_url.SchemeIs(chrome::kHttpScheme)));
83 }
84
85 bool IsCommandLineInstantURL(const GURL& url) {
86 const CommandLine* cl = CommandLine::ForCurrentProcess();
87 const GURL instant_url(cl->GetSwitchValueASCII(switches::kInstantURL));
88 return instant_url.is_valid() && MatchesOriginAndPath(url, instant_url);
89 }
90
91 bool MatchesAnySearchURL(const GURL& url, TemplateURL* template_url) {
92 GURL search_url = TemplateURLRefToGURL(template_url->url_ref());
93 if (search_url.is_valid() && MatchesOriginAndPath(url, search_url))
94 return true;
95
96 // "URLCount() - 1" because we already tested url_ref above.
97 for (size_t i = 0; i < template_url->URLCount() - 1; ++i) {
98 TemplateURLRef ref(template_url, i);
99 search_url = TemplateURLRefToGURL(ref);
100 if (search_url.is_valid() && MatchesOriginAndPath(url, search_url))
101 return true;
102 }
103
104 return false;
105 }
106
107 enum OptInState {
108 NOT_SET, // The user has not manually opted into or out of InstantExtended.
109 OPT_IN, // The user has opted-in to InstantExtended.
110 OPT_OUT, // The user has opted-out of InstantExtended.
111 OPT_IN_STATE_ENUM_COUNT,
112 };
113
114 void RecordInstantExtendedOptInState(OptInState state) {
115 static bool recorded = false;
116 if (!recorded) {
117 recorded = true;
118 UMA_HISTOGRAM_ENUMERATION("InstantExtended.OptInState", state,
119 OPT_IN_STATE_ENUM_COUNT);
120 }
121 }
122
123 // Returns true if |contents| is rendered inside the Instant process for
124 // |profile|.
125 bool IsRenderedInInstantProcess(const content::WebContents* contents,
126 Profile* profile) {
127 const content::RenderProcessHost* process_host =
128 contents->GetRenderProcessHost();
129 if (!process_host)
130 return false;
131
132 const InstantService* instant_service =
133 InstantServiceFactory::GetForProfile(profile);
134 if (!instant_service)
135 return false;
136
137 return instant_service->IsInstantProcess(process_host->GetID());
138 }
139
140 // Returns true if |url| can be used as an Instant URL for |profile|.
141 bool IsInstantURL(const GURL& url, Profile* profile) {
142 TemplateURL* template_url = GetDefaultSearchProviderTemplateURL(profile);
143 if (!template_url)
144 return false;
145
146 const TemplateURLRef& instant_url_ref = template_url->instant_url_ref();
147 const bool extended_api_enabled = IsInstantExtendedAPIEnabled();
148 GURL effective_url = url;
149
150 if (IsCommandLineInstantURL(url))
151 effective_url = CoerceCommandLineURLToTemplateURL(url, instant_url_ref);
152
153 if (!effective_url.is_valid())
154 return false;
155
156 if (extended_api_enabled && !effective_url.SchemeIsSecure())
157 return false;
158
159 if (extended_api_enabled &&
160 !template_url->HasSearchTermsReplacementKey(effective_url))
161 return false;
162
163 const GURL instant_url = TemplateURLRefToGURL(instant_url_ref);
164 if (!instant_url.is_valid())
165 return false;
166
167 if (MatchesOriginAndPath(effective_url, instant_url))
168 return true;
169
170 if (extended_api_enabled && MatchesAnySearchURL(effective_url, template_url))
171 return true;
172
173 return false;
174 }
175
176 string16 GetSearchTermsImpl(const content::WebContents* contents,
177 const content::NavigationEntry* entry) {
178 if (!IsQueryExtractionEnabled())
179 return string16();
180
181 // For security reasons, don't extract search terms if the page is not being
182 // rendered in the privileged Instant renderer process. This is to protect
183 // against a malicious page somehow scripting the search results page and
184 // faking search terms in the URL. Random pages can't get into the Instant
185 // renderer and scripting doesn't work cross-process, so if the page is in
186 // the Instant process, we know it isn't being exploited.
187 Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext());
188 if (!IsRenderedInInstantProcess(contents, profile))
189 return string16();
190
191 // Check to see if search terms have already been extracted.
192 string16 search_terms = GetSearchTermsFromNavigationEntry(entry);
193 if (!search_terms.empty())
194 return search_terms;
195
196 // Otherwise, extract from the URL.
197 TemplateURL* template_url = GetDefaultSearchProviderTemplateURL(profile);
198 if (!template_url)
199 return string16();
200
201 GURL url = entry->GetVirtualURL();
202
203 if (IsCommandLineInstantURL(url))
204 url = CoerceCommandLineURLToTemplateURL(url, template_url->url_ref());
205
206 if (url.SchemeIsSecure() && template_url->HasSearchTermsReplacementKey(url))
207 template_url->ExtractSearchTermsFromURL(url, &search_terms);
208
209 return search_terms;
210 }
211
212 } // namespace
213
214 const char kInstantExtendedSearchTermsKey[] = "search_terms";
215
216 const char kLocalOmniboxPopupURL[] =
217 "chrome://local-omnibox-popup/local-omnibox-popup.html";
218
219 bool IsInstantExtendedAPIEnabled() {
220 return EmbeddedSearchPageVersion() != kEmbeddedPageVersionDisabled;
221 }
222
223 // Determine what embedded search page version to request from the user's
224 // default search provider. If 0, the embedded search UI should not be enabled.
225 uint64 EmbeddedSearchPageVersion() {
226 // Check the command-line/about:flags setting first, which should have
227 // precedence and allows the trial to not be reported (if it's never queried).
228 const CommandLine* command_line = CommandLine::ForCurrentProcess();
229 if (command_line->HasSwitch(switches::kDisableInstantExtendedAPI)) {
230 RecordInstantExtendedOptInState(OPT_OUT);
231 return kEmbeddedPageVersionDisabled;
232 }
233 if (command_line->HasSwitch(switches::kEnableInstantExtendedAPI)) {
234 // The user has set the about:flags switch to Enabled - give the default
235 // UI version.
236 RecordInstantExtendedOptInState(OPT_IN);
237 return kEmbeddedPageVersionDefault;
238 }
239
240 RecordInstantExtendedOptInState(NOT_SET);
241 FieldTrialFlags flags;
242 if (GetFieldTrialInfo(
243 base::FieldTrialList::FindFullName(kInstantExtendedFieldTrialName),
244 &flags, NULL)) {
245 return GetUInt64ValueForFlagWithDefault(kEmbeddedPageVersionFlagName,
246 kEmbeddedPageVersionDefault,
247 flags);
248 }
249
250 return kEmbeddedPageVersionDisabled;
251 }
252
253 bool IsQueryExtractionEnabled() {
254 #if defined(OS_IOS)
255 const CommandLine* cl = CommandLine::ForCurrentProcess();
256 return cl->HasSwitch(switches::kEnableQueryExtraction);
257 #else
258 // On desktop, query extraction is controlled by the instant-extended-api
259 // flag.
260 return IsInstantExtendedAPIEnabled();
261 #endif
262 }
263
264 string16 GetSearchTermsFromNavigationEntry(
265 const content::NavigationEntry* entry) {
266 string16 search_terms;
267 if (entry)
268 entry->GetExtraData(kInstantExtendedSearchTermsKey, &search_terms);
269 return search_terms;
270 }
271
272 string16 GetSearchTerms(const content::WebContents* contents) {
273 if (!contents)
274 return string16();
275
276 const content::NavigationEntry* entry =
277 contents->GetController().GetVisibleEntry();
278 if (!entry)
279 return string16();
280
281 return GetSearchTermsImpl(contents, entry);
282 }
283
284 bool IsInstantNTP(const content::WebContents* contents) {
285 if (!contents)
286 return false;
287
288 return NavEntryIsInstantNTP(contents,
289 contents->GetController().GetVisibleEntry());
290 }
291
292 bool NavEntryIsInstantNTP(const content::WebContents* contents,
293 const content::NavigationEntry* entry) {
294 if (!contents || !entry)
295 return false;
296
297 Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext());
298 return IsInstantExtendedAPIEnabled() &&
299 IsRenderedInInstantProcess(contents, profile) &&
300 IsInstantURL(entry->GetVirtualURL(), profile) &&
301 GetSearchTermsImpl(contents, entry).empty();
302 }
303
304 bool ShouldAssignURLToInstantRenderer(const GURL& url, Profile* profile) {
305 return url.is_valid() &&
306 profile &&
307 (url.SchemeIs(chrome::kChromeSearchScheme) ||
308 IsInstantURL(url, profile) ||
309 (IsInstantExtendedAPIEnabled() &&
310 url == GURL(kLocalOmniboxPopupURL)));
311 }
312
313 void RegisterUserPrefs(PrefRegistrySyncable* registry) {
314 registry->RegisterBooleanPref(prefs::kInstantConfirmDialogShown, false,
315 PrefRegistrySyncable::SYNCABLE_PREF);
316 registry->RegisterBooleanPref(prefs::kInstantEnabled, false,
317 PrefRegistrySyncable::SYNCABLE_PREF);
318 // This default is overridden by SetInstantExtendedPrefDefault().
319 registry->RegisterBooleanPref(prefs::kInstantExtendedEnabled, false,
320 PrefRegistrySyncable::SYNCABLE_PREF);
321 }
322
323 const char* GetInstantPrefName() {
324 return IsInstantExtendedAPIEnabled() ? prefs::kInstantExtendedEnabled :
325 prefs::kInstantEnabled;
326 }
327
328 bool IsInstantPrefEnabled(Profile* profile) {
329 if (!profile || profile->IsOffTheRecord())
330 return false;
331
332 const PrefService* prefs = profile->GetPrefs();
333 if (!prefs)
334 return false;
335
336 return prefs->GetBoolean(GetInstantPrefName());
337 }
338
339 void SetInstantExtendedPrefDefault(Profile* profile) {
340 PrefService* prefs = profile ? profile->GetPrefs() : NULL;
341 if (!prefs)
342 return;
343
344 bool pref_default = false;
345
346 // Check the command-line/about:flags setting first, which should have
347 // precedence and allows the trial to not be reported (if it's never queried).
348 const CommandLine* command_line = CommandLine::ForCurrentProcess();
349 if (command_line->HasSwitch(switches::kEnableInstantExtendedAPI)) {
350 pref_default = true;
351 } else if (!command_line->HasSwitch(switches::kDisableInstantExtendedAPI)) {
352 uint64 trial_default = kInstantExtendedActivationDefault;
353
354 FieldTrialFlags flags;
355 if (GetFieldTrialInfo(
356 base::FieldTrialList::FindFullName(kInstantExtendedFieldTrialName),
357 &flags, NULL)) {
358 trial_default = GetUInt64ValueForFlagWithDefault(
359 kInstantExtendedActivationName,
360 kInstantExtendedActivationDefault,
361 flags);
362 }
363
364 if (trial_default == INSTANT_DEFAULT_ON) {
365 pref_default = true;
366 } else if (trial_default != INSTANT_DEFAULT_OFF) {
367 pref_default = prefs->GetBoolean(prefs::kInstantEnabled);
368 }
369 }
370
371 prefs->SetDefaultPrefValue(prefs::kInstantExtendedEnabled,
372 Value::CreateBooleanValue(pref_default));
373 }
374
375 GURL GetInstantURL(Profile* profile) {
376 const bool extended_api_enabled = IsInstantExtendedAPIEnabled();
377
378 const PrefService* prefs = profile && !profile->IsOffTheRecord() ?
379 profile->GetPrefs() : NULL;
380 if (!IsInstantPrefEnabled(profile) &&
381 !(extended_api_enabled && prefs &&
382 prefs->GetBoolean(prefs::kSearchSuggestEnabled)))
383 return GURL();
384
385 TemplateURL* template_url = GetDefaultSearchProviderTemplateURL(profile);
386 if (!template_url)
387 return GURL();
388
389 CommandLine* cl = CommandLine::ForCurrentProcess();
390 if (cl->HasSwitch(switches::kInstantURL)) {
391 GURL instant_url(cl->GetSwitchValueASCII(switches::kInstantURL));
392 if (extended_api_enabled) {
393 // Extended mode won't work if the search terms replacement key is absent.
394 GURL coerced_url = CoerceCommandLineURLToTemplateURL(
395 instant_url, template_url->instant_url_ref());
396 if (!template_url->HasSearchTermsReplacementKey(coerced_url))
397 return GURL();
398 }
399 return instant_url;
400 }
401
402 GURL instant_url = TemplateURLRefToGURL(template_url->instant_url_ref());
403
404 if (extended_api_enabled) {
405 // Extended mode won't work if the search terms replacement key is absent.
406 if (!template_url->HasSearchTermsReplacementKey(instant_url))
407 return GURL();
408
409 // Extended mode requires HTTPS. Force it if necessary.
410 if (!instant_url.SchemeIsSecure()) {
411 const std::string secure_scheme = chrome::kHttpsScheme;
412 GURL::Replacements replacements;
413 replacements.SetSchemeStr(secure_scheme);
414 instant_url = instant_url.ReplaceComponents(replacements);
415 }
416 }
417
418 return instant_url;
419 }
420
421 bool IsInstantEnabled(Profile* profile) {
422 return GetInstantURL(profile).is_valid();
423 }
424
425 void EnableInstantExtendedAPIForTesting() {
426 CommandLine* cl = CommandLine::ForCurrentProcess();
427 cl->AppendSwitch(switches::kEnableInstantExtendedAPI);
428 }
429
430 void EnableQueryExtractionForTesting() {
431 #if defined(OS_IOS)
432 CommandLine* cl = CommandLine::ForCurrentProcess();
433 cl->AppendSwitch(switches::kEnableQueryExtraction);
434 #else
435 EnableInstantExtendedAPIForTesting();
436 #endif
437 }
438
439 bool GetFieldTrialInfo(const std::string& group_name,
440 FieldTrialFlags* flags,
441 uint64* group_number) {
442 if (EndsWith(group_name, kDisablingSuffix, true) ||
443 !StartsWithASCII(group_name, kGroupNumberPrefix, true))
444 return false;
445
446 // We have a valid trial that starts with "Group" and isn't disabled.
447 // First extract the flags.
448 std::string group_prefix(group_name);
449
450 size_t first_space = group_name.find(" ");
451 if (first_space != std::string::npos) {
452 // There is a flags section of the group name. Split that out and parse it.
453 group_prefix = group_name.substr(0, first_space);
454 if (!base::SplitStringIntoKeyValuePairs(group_name.substr(first_space),
455 ':', ' ', flags)) {
456 // Failed to parse the flags section. Assume the whole group name is
457 // invalid.
458 return false;
459 }
460 }
461
462 // Now extract the group number, making sure we get a non-zero value.
463 uint64 temp_group_number = 0;
464 std::string group_suffix = group_prefix.substr(strlen(kGroupNumberPrefix));
465 if (!base::StringToUint64(group_suffix, &temp_group_number) ||
466 temp_group_number == 0)
467 return false;
468
469 if (group_number)
470 *group_number = temp_group_number;
471
472 return true;
473 }
474
475 // Given a FieldTrialFlags object, returns the string value of the provided
476 // flag.
477 std::string GetStringValueForFlagWithDefault(const std::string& flag,
478 const std::string& default_value,
479 const FieldTrialFlags& flags) {
480 FieldTrialFlags::const_iterator i;
481 for (i = flags.begin(); i != flags.end(); i++) {
482 if (i->first == flag)
483 return i->second;
484 }
485 return default_value;
486 }
487
488 // Given a FieldTrialFlags object, returns the uint64 value of the provided
489 // flag.
490 uint64 GetUInt64ValueForFlagWithDefault(const std::string& flag,
491 uint64 default_value,
492 const FieldTrialFlags& flags) {
493 uint64 value;
494 std::string str_value = GetStringValueForFlagWithDefault(flag, "", flags);
495 if (base::StringToUint64(str_value, &value))
496 return value;
497 return default_value;
498 }
499
500 // Given a FieldTrialFlags object, returns the boolean value of the provided
501 // flag.
502 bool GetBoolValueForFlagWithDefault(const std::string& flag,
503 bool default_value,
504 const FieldTrialFlags& flags) {
505 return !!GetUInt64ValueForFlagWithDefault(flag, default_value ? 1 : 0, flags);
506 }
507
508 // Coerces the commandline Instant URL to look like a template URL, so that we
509 // can extract search terms from it.
510 GURL CoerceCommandLineURLToTemplateURL(const GURL& instant_url,
511 const TemplateURLRef& ref) {
512 GURL search_url = TemplateURLRefToGURL(ref);
513 // NOTE(samarth): GURL returns temporaries which we must save because
514 // GURL::Replacements expects the replacements to live until
515 // ReplaceComponents is called.
516 const std::string search_scheme = chrome::kHttpsScheme;
517 const std::string search_host = search_url.host();
518 const std::string search_port = search_url.port();
519 const std::string search_path = search_url.path();
520
521 GURL::Replacements replacements;
522 replacements.SetSchemeStr(search_scheme);
523 replacements.SetHostStr(search_host);
524 replacements.SetPortStr(search_port);
525 replacements.SetPathStr(search_path);
526 return instant_url.ReplaceComponents(replacements);
527 }
528
529 } // namespace search
530 } // namespace chrome
OLDNEW
« no previous file with comments | « chrome/browser/ui/search/search.h ('k') | chrome/browser/ui/search/search_delegate_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698