1 // Copyright (c) 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/webui/flags_ui.h" 6 7 #include <string> 8 9 #include "base/bind.h" 10 #include "base/bind_helpers.h" 11 #include "base/memory/ref_counted_memory.h" 12 #include "base/prefs/pref_registry_simple.h" 13 #include "base/prefs/pref_service.h" 14 #include "base/strings/utf_string_conversions.h" 15 #include "base/values.h" 16 #include "chrome/browser/about_flags.h" 17 #include "chrome/browser/browser_process.h" 18 #include "chrome/browser/lifetime/application_lifetime.h" 19 #include "chrome/browser/pref_service_flags_storage.h" 20 #include "chrome/browser/profiles/profile.h" 21 #include "chrome/common/chrome_version_info.h" 22 #include "chrome/common/pref_names.h" 23 #include "chrome/common/url_constants.h" 24 #include "chrome/grit/chromium_strings.h" 25 #include "chrome/grit/generated_resources.h" 26 #include "content/public/browser/web_contents.h" 27 #include "content/public/browser/web_ui.h" 28 #include "content/public/browser/web_ui_data_source.h" 29 #include "content/public/browser/web_ui_message_handler.h" 30 #include "grit/browser_resources.h" 31 #include "grit/theme_resources.h" 32 #include "ui/base/l10n/l10n_util.h" 33 #include "ui/base/resource/resource_bundle.h" 34 35 #if defined(OS_CHROMEOS) 36 #include "base/sys_info.h" 37 #include "chrome/browser/chromeos/ownership/owner_settings_service_chromeos.h" 38 #include "chrome/browser/chromeos/ownership/owner_settings_service_chromeos_factory.h" 39 #include "chrome/browser/chromeos/settings/cros_settings.h" 40 #include "chrome/browser/chromeos/settings/owner_flags_storage.h" 41 #include "chromeos/dbus/dbus_thread_manager.h" 42 #include "chromeos/dbus/session_manager_client.h" 43 #include "components/pref_registry/pref_registry_syncable.h" 44 #include "components/user_manager/user_manager.h" 45 #endif 46 47 using content::WebContents; 48 using content::WebUIMessageHandler; 49 50 namespace { 51 52 content::WebUIDataSource* CreateFlagsUIHTMLSource() { 53 content::WebUIDataSource* source = 54 content::WebUIDataSource::Create(chrome::kChromeUIFlagsHost); 55 56 source->SetUseJsonJSFormatV2(); 57 source->AddLocalizedString("flagsLongTitle", IDS_FLAGS_LONG_TITLE); 58 source->AddLocalizedString("flagsTableTitle", IDS_FLAGS_TABLE_TITLE); 59 source->AddLocalizedString("flagsNoExperimentsAvailable", 60 IDS_FLAGS_NO_EXPERIMENTS_AVAILABLE); 61 source->AddLocalizedString("flagsWarningHeader", IDS_FLAGS_WARNING_HEADER); 62 source->AddLocalizedString("flagsBlurb", IDS_FLAGS_WARNING_TEXT); 63 source->AddLocalizedString("channelPromoBeta", 64 IDS_FLAGS_PROMOTE_BETA_CHANNEL); 65 source->AddLocalizedString("channelPromoDev", IDS_FLAGS_PROMOTE_DEV_CHANNEL); 66 source->AddLocalizedString("flagsUnsupportedTableTitle", 67 IDS_FLAGS_UNSUPPORTED_TABLE_TITLE); 68 source->AddLocalizedString("flagsNoUnsupportedExperiments", 69 IDS_FLAGS_NO_UNSUPPORTED_EXPERIMENTS); 70 source->AddLocalizedString("flagsNotSupported", IDS_FLAGS_NOT_AVAILABLE); 71 source->AddLocalizedString("flagsRestartNotice", IDS_FLAGS_RELAUNCH_NOTICE); 72 source->AddLocalizedString("flagsRestartButton", IDS_FLAGS_RELAUNCH_BUTTON); 73 source->AddLocalizedString("resetAllButton", IDS_FLAGS_RESET_ALL_BUTTON); 74 source->AddLocalizedString("disable", IDS_FLAGS_DISABLE); 75 source->AddLocalizedString("enable", IDS_FLAGS_ENABLE); 76 77 #if defined(OS_CHROMEOS) 78 if (!user_manager::UserManager::Get()->IsCurrentUserOwner() && 79 base::SysInfo::IsRunningOnChromeOS()) { 80 // Set the strings to show which user can actually change the flags. 81 std::string owner; 82 chromeos::CrosSettings::Get()->GetString(chromeos::kDeviceOwner, &owner); 83 source->AddString("ownerWarning", 84 l10n_util::GetStringFUTF16(IDS_SYSTEM_FLAGS_OWNER_ONLY, 85 base::UTF8ToUTF16(owner))); 86 } else { 87 // The warning will be only shown on ChromeOS, when the current user is not 88 // the owner. 89 source->AddString("ownerWarning", base::string16()); 90 } 91 #endif 92 93 source->SetJsonPath("strings.js"); 94 source->AddResourcePath("flags.js", IDR_FLAGS_JS); 95 source->SetDefaultResource(IDR_FLAGS_HTML); 96 return source; 97 } 98 99 //////////////////////////////////////////////////////////////////////////////// 100 // 101 // FlagsDOMHandler 102 // 103 //////////////////////////////////////////////////////////////////////////////// 104 105 // The handler for Javascript messages for the about:flags page. 106 class FlagsDOMHandler : public WebUIMessageHandler { 107 public: 108 FlagsDOMHandler() : access_(about_flags::kGeneralAccessFlagsOnly), 109 flags_experiments_requested_(false) { 110 } 111 virtual ~FlagsDOMHandler() {} 112 113 // Initializes the DOM handler with the provided flags storage and flags 114 // access. If there were flags experiments requested from javascript before 115 // this was called, it calls |HandleRequestFlagsExperiments| again. 116 void Init(about_flags::FlagsStorage* flags_storage, 117 about_flags::FlagAccess access); 118 119 // WebUIMessageHandler implementation. 120 virtual void RegisterMessages() OVERRIDE; 121 122 // Callback for the "requestFlagsExperiments" message. 123 void HandleRequestFlagsExperiments(const base::ListValue* args); 124 125 // Callback for the "enableFlagsExperiment" message. 126 void HandleEnableFlagsExperimentMessage(const base::ListValue* args); 127 128 // Callback for the "restartBrowser" message. Restores all tabs on restart. 129 void HandleRestartBrowser(const base::ListValue* args); 130 131 // Callback for the "resetAllFlags" message. 132 void HandleResetAllFlags(const base::ListValue* args); 133 134 private: 135 scoped_ptr<about_flags::FlagsStorage> flags_storage_; 136 about_flags::FlagAccess access_; 137 bool flags_experiments_requested_; 138 139 DISALLOW_COPY_AND_ASSIGN(FlagsDOMHandler); 140 }; 141 142 void FlagsDOMHandler::RegisterMessages() { 143 web_ui()->RegisterMessageCallback("requestFlagsExperiments", 144 base::Bind(&FlagsDOMHandler::HandleRequestFlagsExperiments, 145 base::Unretained(this))); 146 web_ui()->RegisterMessageCallback("enableFlagsExperiment", 147 base::Bind(&FlagsDOMHandler::HandleEnableFlagsExperimentMessage, 148 base::Unretained(this))); 149 web_ui()->RegisterMessageCallback("restartBrowser", 150 base::Bind(&FlagsDOMHandler::HandleRestartBrowser, 151 base::Unretained(this))); 152 web_ui()->RegisterMessageCallback("resetAllFlags", 153 base::Bind(&FlagsDOMHandler::HandleResetAllFlags, 154 base::Unretained(this))); 155 } 156 157 void FlagsDOMHandler::Init(about_flags::FlagsStorage* flags_storage, 158 about_flags::FlagAccess access) { 159 flags_storage_.reset(flags_storage); 160 access_ = access; 161 162 if (flags_experiments_requested_) 163 HandleRequestFlagsExperiments(NULL); 164 } 165 166 void FlagsDOMHandler::HandleRequestFlagsExperiments( 167 const base::ListValue* args) { 168 flags_experiments_requested_ = true; 169 // Bail out if the handler hasn't been initialized yet. The request will be 170 // handled after the initialization. 171 if (!flags_storage_) 172 return; 173 174 base::DictionaryValue results; 175 176 scoped_ptr<base::ListValue> supported_experiments(new base::ListValue); 177 scoped_ptr<base::ListValue> unsupported_experiments(new base::ListValue); 178 about_flags::GetFlagsExperimentsData(flags_storage_.get(), 179 access_, 180 supported_experiments.get(), 181 unsupported_experiments.get()); 182 results.Set("supportedExperiments", supported_experiments.release()); 183 results.Set("unsupportedExperiments", unsupported_experiments.release()); 184 results.SetBoolean("needsRestart", 185 about_flags::IsRestartNeededToCommitChanges()); 186 results.SetBoolean("showOwnerWarning", 187 access_ == about_flags::kGeneralAccessFlagsOnly); 188 189 #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_CHROMEOS) 190 chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel(); 191 results.SetBoolean("showBetaChannelPromotion", 192 channel == chrome::VersionInfo::CHANNEL_STABLE); 193 results.SetBoolean("showDevChannelPromotion", 194 channel == chrome::VersionInfo::CHANNEL_BETA); 195 #else 196 results.SetBoolean("showBetaChannelPromotion", false); 197 results.SetBoolean("showDevChannelPromotion", false); 198 #endif 199 web_ui()->CallJavascriptFunction("returnFlagsExperiments", results); 200 } 201 202 void FlagsDOMHandler::HandleEnableFlagsExperimentMessage( 203 const base::ListValue* args) { 204 DCHECK(flags_storage_); 205 DCHECK_EQ(2u, args->GetSize()); 206 if (args->GetSize() != 2) 207 return; 208 209 std::string experiment_internal_name; 210 std::string enable_str; 211 if (!args->GetString(0, &experiment_internal_name) || 212 !args->GetString(1, &enable_str)) 213 return; 214 215 about_flags::SetExperimentEnabled( 216 flags_storage_.get(), 217 experiment_internal_name, 218 enable_str == "true"); 219 } 220 221 void FlagsDOMHandler::HandleRestartBrowser(const base::ListValue* args) { 222 DCHECK(flags_storage_); 223 #if defined(OS_CHROMEOS) 224 // On ChromeOS be less intrusive and restart inside the user session after 225 // we apply the newly selected flags. 226 CommandLine user_flags(CommandLine::NO_PROGRAM); 227 about_flags::ConvertFlagsToSwitches(flags_storage_.get(), 228 &user_flags, 229 about_flags::kAddSentinels); 230 CommandLine::StringVector flags; 231 // argv[0] is the program name |CommandLine::NO_PROGRAM|. 232 flags.assign(user_flags.argv().begin() + 1, user_flags.argv().end()); 233 VLOG(1) << "Restarting to apply per-session flags..."; 234 chromeos::DBusThreadManager::Get() 235 ->GetSessionManagerClient() 236 ->SetFlagsForUser( 237 user_manager::UserManager::Get()->GetActiveUser()->email(), flags); 238 #endif 239 chrome::AttemptRestart(); 240 } 241 242 void FlagsDOMHandler::HandleResetAllFlags(const base::ListValue* args) { 243 DCHECK(flags_storage_); 244 about_flags::ResetAllFlags(flags_storage_.get()); 245 } 246 247 248 #if defined(OS_CHROMEOS) 249 // On ChromeOS verifying if the owner is signed in is async operation and only 250 // after finishing it the UI can be properly populated. This function is the 251 // callback for whether the owner is signed in. It will respectively pick the 252 // proper PrefService for the flags interface. 253 void FinishInitialization(base::WeakPtr<FlagsUI> flags_ui, 254 Profile* profile, 255 FlagsDOMHandler* dom_handler, 256 bool current_user_is_owner) { 257 // If the flags_ui has gone away, there's nothing to do. 258 if (!flags_ui) 259 return; 260 261 // On Chrome OS the owner can set system wide flags and other users can only 262 // set flags for their own session. 263 // Note that |dom_handler| is owned by the web ui that owns |flags_ui|, so 264 // it is still alive if |flags_ui| is. 265 if (current_user_is_owner) { 266 dom_handler->Init(new chromeos::about_flags::OwnerFlagsStorage( 267 profile->GetPrefs(), 268 chromeos::CrosSettings::Get()), 269 about_flags::kOwnerAccessToFlags); 270 } else { 271 dom_handler->Init( 272 new about_flags::PrefServiceFlagsStorage(profile->GetPrefs()), 273 about_flags::kGeneralAccessFlagsOnly); 274 } 275 } 276 #endif 277 278 } // namespace 279 280 /////////////////////////////////////////////////////////////////////////////// 281 // 282 // FlagsUI 283 // 284 /////////////////////////////////////////////////////////////////////////////// 285 286 FlagsUI::FlagsUI(content::WebUI* web_ui) 287 : WebUIController(web_ui), 288 weak_factory_(this) { 289 Profile* profile = Profile::FromWebUI(web_ui); 290 291 FlagsDOMHandler* handler = new FlagsDOMHandler(); 292 web_ui->AddMessageHandler(handler); 293 294 #if defined(OS_CHROMEOS) 295 chromeos::OwnerSettingsServiceChromeOS* service = 296 chromeos::OwnerSettingsServiceChromeOSFactory::GetForProfile(profile); 297 if (service) { 298 service->IsOwnerAsync(base::Bind( 299 &FinishInitialization, weak_factory_.GetWeakPtr(), profile, handler)); 300 } else { 301 FinishInitialization(weak_factory_.GetWeakPtr(), 302 profile, 303 handler, 304 false /* current_user_is_owner */); 305 } 306 #else 307 handler->Init(new about_flags::PrefServiceFlagsStorage( 308 g_browser_process->local_state()), 309 about_flags::kOwnerAccessToFlags); 310 #endif 311 312 // Set up the about:flags source. 313 content::WebUIDataSource::Add(profile, CreateFlagsUIHTMLSource()); 314 } 315 316 FlagsUI::~FlagsUI() { 317 } 318 319 // static 320 base::RefCountedMemory* FlagsUI::GetFaviconResourceBytes( 321 ui::ScaleFactor scale_factor) { 322 return ResourceBundle::GetSharedInstance(). 323 LoadDataResourceBytesForScale(IDR_FLAGS_FAVICON, scale_factor); 324 } 325 326 // static 327 void FlagsUI::RegisterPrefs(PrefRegistrySimple* registry) { 328 registry->RegisterListPref(prefs::kEnabledLabsExperiments); 329 } 330 331 #if defined(OS_CHROMEOS) 332 // static 333 void FlagsUI::RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) { 334 registry->RegisterListPref(prefs::kEnabledLabsExperiments, 335 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); 336 } 337 338 #endif 339