1 // Copyright 2013 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/bookmarks/enhanced_bookmarks_features.h" 6 7 #include "base/command_line.h" 8 #include "base/metrics/histogram.h" 9 #include "base/prefs/pref_service.h" 10 #include "base/prefs/scoped_user_pref_update.h" 11 #include "base/sha1.h" 12 #include "base/strings/string_number_conversions.h" 13 #include "chrome/common/chrome_switches.h" 14 #include "chrome/common/pref_names.h" 15 #include "components/sync_driver/pref_names.h" 16 #include "components/variations/variations_associated_data.h" 17 #include "extensions/common/features/feature.h" 18 #include "extensions/common/features/feature_provider.h" 19 20 namespace { 21 22 const char kFieldTrialName[] = "EnhancedBookmarks"; 23 24 // Get extension id from Finch EnhancedBookmarks group parameters. 25 std::string GetEnhancedBookmarksExtensionIdFromFinch() { 26 return chrome_variations::GetVariationParamValue(kFieldTrialName, "id"); 27 } 28 29 // Returns true if enhanced bookmarks experiment is enabled from Finch. 30 bool IsEnhancedBookmarksExperimentEnabledFromFinch() { 31 std::string ext_id = GetEnhancedBookmarksExtensionIdFromFinch(); 32 const extensions::FeatureProvider* feature_provider = 33 extensions::FeatureProvider::GetPermissionFeatures(); 34 extensions::Feature* feature = feature_provider->GetFeature("metricsPrivate"); 35 return feature && feature->IsIdInWhitelist(ext_id); 36 } 37 38 }; // namespace 39 40 bool GetBookmarksExperimentExtensionID(const PrefService* user_prefs, 41 std::string* extension_id) { 42 BookmarksExperimentState bookmarks_experiment_state = 43 static_cast<BookmarksExperimentState>(user_prefs->GetInteger( 44 sync_driver::prefs::kEnhancedBookmarksExperimentEnabled)); 45 if (bookmarks_experiment_state == BOOKMARKS_EXPERIMENT_ENABLED_FROM_FINCH) { 46 *extension_id = GetEnhancedBookmarksExtensionIdFromFinch(); 47 return !extension_id->empty(); 48 } 49 if (bookmarks_experiment_state == BOOKMARKS_EXPERIMENT_ENABLED) { 50 *extension_id = user_prefs->GetString( 51 sync_driver::prefs::kEnhancedBookmarksExtensionId); 52 return !extension_id->empty(); 53 } 54 55 return false; 56 } 57 58 void UpdateBookmarksExperimentState( 59 PrefService* user_prefs, 60 PrefService* local_state, 61 bool user_signed_in, 62 BookmarksExperimentState experiment_enabled_from_sync) { 63 PrefService* flags_storage = local_state; 64 #if defined(OS_CHROMEOS) 65 // Chrome OS is using user prefs for flags storage. 66 flags_storage = user_prefs; 67 #endif 68 69 BookmarksExperimentState bookmarks_experiment_state_before = 70 static_cast<BookmarksExperimentState>(user_prefs->GetInteger( 71 sync_driver::prefs::kEnhancedBookmarksExperimentEnabled)); 72 // If user signed out, clear possible previous state. 73 if (!user_signed_in) { 74 bookmarks_experiment_state_before = BOOKMARKS_EXPERIMENT_NONE; 75 ForceFinchBookmarkExperimentIfNeeded(flags_storage, 76 BOOKMARKS_EXPERIMENT_NONE); 77 } 78 79 // kEnhancedBookmarksExperiment flag could have values "", "1" and "0". 80 // "0" - user opted out. 81 bool opt_out = CommandLine::ForCurrentProcess()->GetSwitchValueASCII( 82 switches::kEnhancedBookmarksExperiment) == "0"; 83 84 BookmarksExperimentState bookmarks_experiment_new_state = 85 BOOKMARKS_EXPERIMENT_NONE; 86 87 if (IsEnhancedBookmarksExperimentEnabledFromFinch() && !user_signed_in) { 88 if (opt_out) { 89 // Experiment enabled but user opted out. 90 bookmarks_experiment_new_state = BOOKMARKS_EXPERIMENT_OPT_OUT_FROM_FINCH; 91 } else { 92 // Experiment enabled. 93 bookmarks_experiment_new_state = BOOKMARKS_EXPERIMENT_ENABLED_FROM_FINCH; 94 } 95 } else if (experiment_enabled_from_sync == BOOKMARKS_EXPERIMENT_ENABLED) { 96 // Experiment enabled from Chrome sync. 97 if (opt_out) { 98 // Experiment enabled but user opted out. 99 bookmarks_experiment_new_state = 100 BOOKMARKS_EXPERIMENT_ENABLED_USER_OPT_OUT; 101 } else { 102 // Experiment enabled. 103 bookmarks_experiment_new_state = BOOKMARKS_EXPERIMENT_ENABLED; 104 } 105 } else if (experiment_enabled_from_sync == BOOKMARKS_EXPERIMENT_NONE) { 106 // Experiment is not enabled from Chrome sync. 107 bookmarks_experiment_new_state = BOOKMARKS_EXPERIMENT_NONE; 108 } else if (bookmarks_experiment_state_before == 109 BOOKMARKS_EXPERIMENT_ENABLED) { 110 if (opt_out) { 111 // Experiment enabled but user opted out. 112 bookmarks_experiment_new_state = 113 BOOKMARKS_EXPERIMENT_ENABLED_USER_OPT_OUT; 114 } else { 115 bookmarks_experiment_new_state = BOOKMARKS_EXPERIMENT_ENABLED; 116 } 117 } else if (bookmarks_experiment_state_before == 118 BOOKMARKS_EXPERIMENT_ENABLED_USER_OPT_OUT) { 119 if (opt_out) { 120 bookmarks_experiment_new_state = 121 BOOKMARKS_EXPERIMENT_ENABLED_USER_OPT_OUT; 122 } else { 123 // User opted in again. 124 bookmarks_experiment_new_state = BOOKMARKS_EXPERIMENT_ENABLED; 125 } 126 } 127 128 UMA_HISTOGRAM_ENUMERATION("EnhancedBookmarks.SyncExperimentState", 129 bookmarks_experiment_new_state, 130 BOOKMARKS_EXPERIMENT_ENUM_SIZE); 131 user_prefs->SetInteger( 132 sync_driver::prefs::kEnhancedBookmarksExperimentEnabled, 133 bookmarks_experiment_new_state); 134 ForceFinchBookmarkExperimentIfNeeded(flags_storage, 135 bookmarks_experiment_new_state); 136 } 137 138 void ForceFinchBookmarkExperimentIfNeeded( 139 PrefService* flags_storage, 140 BookmarksExperimentState bookmarks_experiment_state) { 141 if (!flags_storage) 142 return; 143 ListPrefUpdate update(flags_storage, prefs::kEnabledLabsExperiments); 144 base::ListValue* experiments_list = update.Get(); 145 if (!experiments_list) 146 return; 147 size_t index; 148 if (bookmarks_experiment_state == BOOKMARKS_EXPERIMENT_NONE) { 149 experiments_list->Remove( 150 base::StringValue(switches::kManualEnhancedBookmarks), &index); 151 experiments_list->Remove( 152 base::StringValue(switches::kManualEnhancedBookmarksOptout), &index); 153 } else if (bookmarks_experiment_state == BOOKMARKS_EXPERIMENT_ENABLED) { 154 experiments_list->Remove( 155 base::StringValue(switches::kManualEnhancedBookmarksOptout), &index); 156 experiments_list->AppendIfNotPresent( 157 new base::StringValue(switches::kManualEnhancedBookmarks)); 158 } else if (bookmarks_experiment_state == 159 BOOKMARKS_EXPERIMENT_ENABLED_USER_OPT_OUT) { 160 experiments_list->Remove( 161 base::StringValue(switches::kManualEnhancedBookmarks), &index); 162 experiments_list->AppendIfNotPresent( 163 new base::StringValue(switches::kManualEnhancedBookmarksOptout)); 164 } 165 } 166 167 bool IsEnhancedBookmarksExperimentEnabled() { 168 CommandLine* command_line = CommandLine::ForCurrentProcess(); 169 if (command_line->HasSwitch(switches::kManualEnhancedBookmarks) || 170 command_line->HasSwitch(switches::kManualEnhancedBookmarksOptout)) { 171 return true; 172 } 173 174 return IsEnhancedBookmarksExperimentEnabledFromFinch(); 175 } 176 177 #if defined(OS_ANDROID) 178 bool IsEnhancedBookmarkImageFetchingEnabled() { 179 if (IsEnhancedBookmarksExperimentEnabled()) 180 return true; 181 182 // Salient images are collected from visited bookmarked pages even if the 183 // enhanced bookmark feature is turned off. This is to have some images 184 // available so that in the future, when the feature is turned on, the user 185 // experience is not a big list of flat colors. However as a precautionary 186 // measure it is possible to disable this collection of images from finch. 187 std::string disable_fetching = chrome_variations::GetVariationParamValue( 188 kFieldTrialName, "DisableImagesFetching"); 189 return disable_fetching.empty(); 190 } 191 #endif 192 193 bool IsEnableDomDistillerSet() { 194 if (CommandLine::ForCurrentProcess()-> 195 HasSwitch(switches::kEnableDomDistiller)) { 196 return true; 197 } 198 if (chrome_variations::GetVariationParamValue( 199 kFieldTrialName, "enable-dom-distiller") == "1") 200 return true; 201 202 return false; 203 } 204 205 bool IsEnableSyncArticlesSet() { 206 if (CommandLine::ForCurrentProcess()-> 207 HasSwitch(switches::kEnableSyncArticles)) { 208 return true; 209 } 210 if (chrome_variations::GetVariationParamValue( 211 kFieldTrialName, "enable-sync-articles") == "1") 212 return true; 213 214 return false; 215 } 216