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 47using content::WebContents; 48using content::WebUIMessageHandler; 49 50namespace { 51 52content::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. 106class 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 142void 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 157void 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 166void 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 202void 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 221void 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 242void 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. 253void 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 286FlagsUI::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 316FlagsUI::~FlagsUI() { 317} 318 319// static 320base::RefCountedMemory* FlagsUI::GetFaviconResourceBytes( 321 ui::ScaleFactor scale_factor) { 322 return ResourceBundle::GetSharedInstance(). 323 LoadDataResourceBytesForScale(IDR_FLAGS_FAVICON, scale_factor); 324} 325 326// static 327void FlagsUI::RegisterPrefs(PrefRegistrySimple* registry) { 328 registry->RegisterListPref(prefs::kEnabledLabsExperiments); 329} 330 331#if defined(OS_CHROMEOS) 332// static 333void FlagsUI::RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) { 334 registry->RegisterListPref(prefs::kEnabledLabsExperiments, 335 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); 336} 337 338#endif 339