1 // Copyright 2014 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 "components/search/search.h" 6 7 #include "base/command_line.h" 8 #include "base/metrics/field_trial.h" 9 #include "base/strings/string_number_conversions.h" 10 #include "base/strings/string_split.h" 11 #include "base/strings/string_util.h" 12 #include "base/strings/utf_string_conversions.h" 13 #include "components/search/search_switches.h" 14 15 namespace chrome { 16 17 namespace { 18 19 // Configuration options for Embedded Search. 20 // EmbeddedSearch field trials are named in such a way that we can parse out 21 // the experiment configuration from the trial's group name in order to give 22 // us maximum flexability in running experiments. 23 // Field trial groups should be named things like "Group7 espv:2 instant:1". 24 // The first token is always GroupN for some integer N, followed by a 25 // space-delimited list of key:value pairs which correspond to these flags: 26 const char kEmbeddedPageVersionFlagName[] = "espv"; 27 28 #if defined(OS_IOS) 29 const uint64 kEmbeddedPageVersionDefault = 1; 30 #elif defined(OS_ANDROID) 31 const uint64 kEmbeddedPageVersionDefault = 1; 32 // Use this variant to enable EmbeddedSearch SearchBox API in the results page. 33 const uint64 kEmbeddedSearchEnabledVersion = 2; 34 #else 35 const uint64 kEmbeddedPageVersionDefault = 2; 36 #endif 37 38 const char kHideVerbatimFlagName[] = "hide_verbatim"; 39 40 // Constants for the field trial name and group prefix. 41 // Note in M30 and below this field trial was named "InstantExtended" and in 42 // M31 was renamed to EmbeddedSearch for clarity and cleanliness. Since we 43 // can't easilly sync up Finch configs with the pushing of this change to 44 // Dev & Canary, for now the code accepts both names. 45 // TODO(dcblack): Remove the InstantExtended name once M31 hits the Beta 46 // channel. 47 const char kInstantExtendedFieldTrialName[] = "InstantExtended"; 48 const char kEmbeddedSearchFieldTrialName[] = "EmbeddedSearch"; 49 50 // If the field trial's group name ends with this string its configuration will 51 // be ignored and Instant Extended will not be enabled by default. 52 const char kDisablingSuffix[] = "DISABLED"; 53 54 } // namespace 55 56 bool IsInstantExtendedAPIEnabled() { 57 #if defined(OS_IOS) 58 return false; 59 #elif defined(OS_ANDROID) 60 return EmbeddedSearchPageVersion() == kEmbeddedSearchEnabledVersion; 61 #else 62 return true; 63 #endif // defined(OS_IOS) 64 } 65 66 // Determine what embedded search page version to request from the user's 67 // default search provider. If 0, the embedded search UI should not be enabled. 68 uint64 EmbeddedSearchPageVersion() { 69 #if defined(OS_ANDROID) 70 if (CommandLine::ForCurrentProcess()->HasSwitch( 71 switches::kEnableEmbeddedSearchAPI)) { 72 return kEmbeddedSearchEnabledVersion; 73 } 74 #endif 75 76 FieldTrialFlags flags; 77 if (GetFieldTrialInfo(&flags)) { 78 return GetUInt64ValueForFlagWithDefault(kEmbeddedPageVersionFlagName, 79 kEmbeddedPageVersionDefault, 80 flags); 81 } 82 return kEmbeddedPageVersionDefault; 83 } 84 85 bool GetFieldTrialInfo(FieldTrialFlags* flags) { 86 // Get the group name. If the EmbeddedSearch trial doesn't exist, look for 87 // the older InstantExtended name. 88 std::string group_name = base::FieldTrialList::FindFullName( 89 kEmbeddedSearchFieldTrialName); 90 if (group_name.empty()) { 91 group_name = base::FieldTrialList::FindFullName( 92 kInstantExtendedFieldTrialName); 93 } 94 95 if (EndsWith(group_name, kDisablingSuffix, true)) 96 return false; 97 98 // We have a valid trial that isn't disabled. Extract the flags. 99 std::string group_prefix(group_name); 100 size_t first_space = group_name.find(" "); 101 if (first_space != std::string::npos) { 102 // There is a flags section of the group name. Split that out and parse it. 103 group_prefix = group_name.substr(0, first_space); 104 if (!base::SplitStringIntoKeyValuePairs(group_name.substr(first_space), 105 ':', ' ', flags)) { 106 // Failed to parse the flags section. Assume the whole group name is 107 // invalid. 108 return false; 109 } 110 } 111 return true; 112 } 113 114 // Given a FieldTrialFlags object, returns the string value of the provided 115 // flag. 116 std::string GetStringValueForFlagWithDefault(const std::string& flag, 117 const std::string& default_value, 118 const FieldTrialFlags& flags) { 119 FieldTrialFlags::const_iterator i; 120 for (i = flags.begin(); i != flags.end(); i++) { 121 if (i->first == flag) 122 return i->second; 123 } 124 return default_value; 125 } 126 127 // Given a FieldTrialFlags object, returns the uint64 value of the provided 128 // flag. 129 uint64 GetUInt64ValueForFlagWithDefault(const std::string& flag, 130 uint64 default_value, 131 const FieldTrialFlags& flags) { 132 uint64 value; 133 std::string str_value = 134 GetStringValueForFlagWithDefault(flag, std::string(), flags); 135 if (base::StringToUint64(str_value, &value)) 136 return value; 137 return default_value; 138 } 139 140 // Given a FieldTrialFlags object, returns the boolean value of the provided 141 // flag. 142 bool GetBoolValueForFlagWithDefault(const std::string& flag, 143 bool default_value, 144 const FieldTrialFlags& flags) { 145 return !!GetUInt64ValueForFlagWithDefault(flag, default_value ? 1 : 0, flags); 146 } 147 148 bool ShouldHideTopVerbatimMatch() { 149 FieldTrialFlags flags; 150 return GetFieldTrialInfo(&flags) && GetBoolValueForFlagWithDefault( 151 kHideVerbatimFlagName, false, flags); 152 } 153 154 } // namespace chrome 155