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 "base/basictypes.h" 6 #include "base/logging.h" 7 #include "base/metrics/field_trial.h" 8 #include "base/strings/string_number_conversions.h" 9 #include "base/strings/string_split.h" 10 #include "base/strings/stringprintf.h" 11 #include "chrome/browser/extensions/extension_install_prompt_experiment.h" 12 #include "chrome/grit/generated_resources.h" 13 #include "ui/base/l10n/l10n_util.h" 14 15 namespace { 16 17 const char kExperimentName[] = "ExtensionPermissionDialog"; 18 const char kGroupPrefix[] = "Group"; 19 20 // Flags for groups. Not all combinations make sense. 21 // Refer to the UI screens at http://goo.gl/f2KzPj for those that do. 22 enum GroupFlag { 23 // No changes (Control group). 24 NONE = 0, 25 // Indicates that the experiment is text only. A text only experiment 26 // only adds an explanation text at the bottom of the permission dialog and 27 // modifies the text on accept/cancel buttons. 28 TEXT_ONLY = 1 << 0, 29 // Indicates that the experiment shows inline explanations for permissions. 30 INLINE_EXPLANATIONS = 1 << 1, 31 // Indicates that the experiment highlights permission text color. 32 SHOULD_HIGHLIGHT_TEXT = 1 << 2, 33 // Indicates that the experiment highlights permission text background. 34 SHOULD_HIGHLIGHT_BACKGROUND = 1 << 3, 35 // Indicates that the experiment highlights all permissions. 36 SHOULD_HIGHLIGHT_ALL_PERMISSIONS = 1 << 4, 37 // Indicates that the experiment puts a "show details" link in the UI. 38 SHOULD_SHOW_DETAILS_LINK = 1 << 5, 39 // Indicates that the experiment hides the permissions by default and the list 40 // can be expanded. 41 EXPANDABLE_PERMISSION_LIST = 1 << 6, 42 // Indicates that the experiment shows checkboxes for each permission. 43 SHOULD_SHOW_CHECKBOXES = 1 << 7 44 }; 45 46 // Flags for the actual experiment groups. These flags define what kind of 47 // UI changes each experiment group does. An experiment group may change 48 // multiple aspects of the extension install dialog (e.g. one of the groups 49 // show a details link and inline explanations for permissions). The control 50 // group doesn't change the UI. Text only groups add a text warning to the UI, 51 // with the text changing depending on the group number. Groups with inline 52 // explanations show detailed explanations for a subset of permissions. Some 53 // groups highlight the foreground or the background of permission texts. 54 // The flags reflect the UI screens at http://goo.gl/f2KzPj. 55 const unsigned int kGroupFlags[] = { 56 // Control group doesn't change the UI. 57 NONE, 58 // Adds "Do you trust this extension to use these privileges safely" text. 59 TEXT_ONLY, 60 // Adds "Extension can be malicious" text. 61 TEXT_ONLY, 62 // Adds "Are you sure you want to install" text. 63 TEXT_ONLY, 64 // Adds "Make sure these privileges make sense for this extension" text. 65 TEXT_ONLY, 66 // Adds "Do you trust this extension to perform these actions" text. 67 TEXT_ONLY, 68 // Adds inline explanations displayed by default. 69 INLINE_EXPLANATIONS, 70 // Adds expandable inline explanations with a "Show Details" link. 71 SHOULD_SHOW_DETAILS_LINK | INLINE_EXPLANATIONS, 72 // Adds expandable permission list with a "Show Permissions" link. 73 SHOULD_SHOW_DETAILS_LINK | EXPANDABLE_PERMISSION_LIST, 74 // Highlights text for risky permissions. 75 SHOULD_HIGHLIGHT_TEXT, 76 // Highlights background for risky permissions. 77 SHOULD_HIGHLIGHT_BACKGROUND, 78 // Highlights background for all permissions 79 SHOULD_HIGHLIGHT_BACKGROUND | SHOULD_HIGHLIGHT_ALL_PERMISSIONS, 80 // Displays checkboxes for all permissions. 81 SHOULD_SHOW_CHECKBOXES 82 }; 83 84 const size_t kGroupCount = arraysize(kGroupFlags); 85 86 // Parameters for text only experiments. 87 const struct TextParams { 88 int text_id; 89 int ok_text_id; 90 int cancel_text_id; 91 } kTextParams[] = { 92 { 93 IDS_EXTENSION_PROMPT_EXPERIMENT_EXPLANATION1, 94 IDS_EXTENSION_PROMPT_EXPERIMENT_INSTALL_BUTTON_TRUST, 95 IDS_CANCEL, 96 }, 97 { 98 IDS_EXTENSION_PROMPT_EXPERIMENT_EXPLANATION2, 99 IDS_EXTENSION_PROMPT_EXPERIMENT_INSTALL_BUTTON_YES, 100 IDS_EXTENSION_PROMPT_EXPERIMENT_INSTALL_BUTTON_NOPE 101 }, 102 { 103 IDS_EXTENSION_PROMPT_EXPERIMENT_EXPLANATION3, 104 IDS_EXTENSION_PROMPT_EXPERIMENT_INSTALL_BUTTON_SURE, 105 IDS_EXTENSION_PROMPT_EXPERIMENT_INSTALL_BUTTON_NOPE 106 }, 107 { 108 IDS_EXTENSION_PROMPT_EXPERIMENT_EXPLANATION4, 109 IDS_EXTENSION_PROMPT_INSTALL_BUTTON, 110 IDS_CANCEL 111 }, 112 { 113 IDS_EXTENSION_PROMPT_EXPERIMENT_EXPLANATION5, 114 IDS_EXTENSION_PROMPT_EXPERIMENT_INSTALL_BUTTON_TRUST2, 115 IDS_EXTENSION_PROMPT_EXPERIMENT_INSTALL_BUTTON_NOPE 116 } 117 }; 118 119 // Permission warnings in this list have inline explanation texts. 120 const struct PermissionExplanations { 121 int warning_msg_id; 122 int extra_explanation_id; 123 } kPermissionExplanations[] = { 124 { 125 IDS_EXTENSION_PROMPT_WARNING_FULL_ACCESS, 126 IDS_EXTENSION_PROMPT_WARNING_FULL_ACCESS_EXPLANATION 127 }, 128 { 129 IDS_EXTENSION_PROMPT_WARNING_ALL_HOSTS, 130 IDS_EXTENSION_PROMPT_WARNING_ALL_HOSTS_EXPLANATION 131 } 132 }; 133 134 // Permission warnings in this list are going to be highlighted. 135 // Note that the matching is done by string comparison, so this list must not 136 // contain any dynamic strings (e.g. permission for 3 hosts with the host list). 137 const int kHighlightedWarnings[] = { 138 IDS_EXTENSION_PROMPT_WARNING_FULL_ACCESS, 139 IDS_EXTENSION_PROMPT_WARNING_ALL_HOSTS, 140 IDS_EXTENSION_PROMPT_WARNING_BOOKMARKS, 141 IDS_EXTENSION_PROMPT_WARNING_CONTENT_SETTINGS, 142 IDS_EXTENSION_PROMPT_WARNING_HISTORY_WRITE, 143 IDS_EXTENSION_PROMPT_WARNING_INPUT, 144 IDS_EXTENSION_PROMPT_WARNING_MANAGEMENT, 145 IDS_EXTENSION_PROMPT_WARNING_HISTORY_READ, 146 IDS_EXTENSION_PROMPT_WARNING_DEBUGGER 147 }; 148 149 bool IsImportantWarning(const base::string16& message) { 150 for (size_t i = 0; i < arraysize(kHighlightedWarnings); ++i) { 151 if (message == l10n_util::GetStringUTF16(kHighlightedWarnings[i])) 152 return true; 153 } 154 return false; 155 } 156 157 } // namespace 158 159 ExtensionInstallPromptExperiment::ExtensionInstallPromptExperiment( 160 unsigned int group_id, unsigned int flags) 161 : group_id_(group_id), 162 flags_(flags) { 163 } 164 165 ExtensionInstallPromptExperiment::~ExtensionInstallPromptExperiment() { 166 } 167 168 // static 169 ExtensionInstallPromptExperiment* 170 ExtensionInstallPromptExperiment::ControlGroup() { 171 return new ExtensionInstallPromptExperiment(0, kGroupFlags[0]); 172 } 173 174 // static 175 ExtensionInstallPromptExperiment* 176 ExtensionInstallPromptExperiment::Find() { 177 base::FieldTrial* trial = base::FieldTrialList::Find(kExperimentName); 178 // Default is control group. 179 unsigned int group_id = 0; 180 if (trial) { 181 std::vector<std::string> tokens; 182 base::SplitString(trial->group_name().c_str(), '_', &tokens); 183 if (tokens.size() == 2 && tokens[0] == kGroupPrefix) { 184 base::StringToUint(tokens[1], &group_id); 185 if (group_id >= kGroupCount) 186 group_id = 0; 187 } 188 } 189 return new ExtensionInstallPromptExperiment(group_id, kGroupFlags[group_id]); 190 } 191 192 base::string16 ExtensionInstallPromptExperiment::GetExplanationText() const { 193 DCHECK(group_id_ > 0 && group_id_ - 1 < arraysize(kTextParams)); 194 return l10n_util::GetStringUTF16(kTextParams[group_id_ - 1].text_id); 195 } 196 197 base::string16 ExtensionInstallPromptExperiment::GetOkButtonText() const { 198 DCHECK(group_id_ > 0 && group_id_ - 1 < arraysize(kTextParams)); 199 return l10n_util::GetStringUTF16(kTextParams[group_id_ - 1].ok_text_id); 200 } 201 202 base::string16 ExtensionInstallPromptExperiment::GetCancelButtonText() const { 203 DCHECK(group_id_ > 0 && group_id_ - 1 < arraysize(kTextParams)); 204 return l10n_util::GetStringUTF16(kTextParams[group_id_ - 1].cancel_text_id); 205 } 206 207 bool ExtensionInstallPromptExperiment::text_only() const { 208 return (flags_ & TEXT_ONLY) != 0; 209 } 210 211 bool ExtensionInstallPromptExperiment::ShouldHighlightText( 212 const base::string16& message) const { 213 return (flags_ & SHOULD_HIGHLIGHT_TEXT) != 0 && IsImportantWarning(message); 214 } 215 216 bool ExtensionInstallPromptExperiment::ShouldHighlightBackground( 217 const base::string16& message) const { 218 return (flags_ & SHOULD_HIGHLIGHT_BACKGROUND) != 0 && 219 ((flags_ & SHOULD_HIGHLIGHT_ALL_PERMISSIONS) != 0 || 220 IsImportantWarning(message)); 221 } 222 223 bool ExtensionInstallPromptExperiment::show_details_link() const { 224 return (flags_ & SHOULD_SHOW_DETAILS_LINK) != 0; 225 } 226 227 bool ExtensionInstallPromptExperiment::show_checkboxes() const { 228 return (flags_ & SHOULD_SHOW_CHECKBOXES) != 0; 229 } 230 231 bool ExtensionInstallPromptExperiment::should_show_expandable_permission_list() 232 const { 233 return (flags_ & EXPANDABLE_PERMISSION_LIST) != 0; 234 } 235 236 bool ExtensionInstallPromptExperiment::should_show_inline_explanations() const { 237 return (flags_ & INLINE_EXPLANATIONS) != 0; 238 } 239 240 base::string16 ExtensionInstallPromptExperiment::GetInlineExplanation( 241 const base::string16& message) const { 242 for (size_t i = 0; i < arraysize(kPermissionExplanations); ++i) { 243 if (message == l10n_util::GetStringUTF16( 244 kPermissionExplanations[i].warning_msg_id)) { 245 return l10n_util::GetStringUTF16( 246 kPermissionExplanations[i].extra_explanation_id); 247 } 248 } 249 return base::string16(); 250 } 251