Home | History | Annotate | Download | only in webui
      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