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/nacl_ui.h"
      6 
      7 #include <string>
      8 #include <vector>
      9 
     10 #include "base/bind.h"
     11 #include "base/bind_helpers.h"
     12 #include "base/command_line.h"
     13 #include "base/file_util.h"
     14 #include "base/json/json_file_value_serializer.h"
     15 #include "base/memory/weak_ptr.h"
     16 #include "base/path_service.h"
     17 #include "base/strings/string16.h"
     18 #include "base/strings/string_number_conversions.h"
     19 #include "base/strings/utf_string_conversions.h"
     20 #include "base/threading/sequenced_worker_pool.h"
     21 #include "base/values.h"
     22 #include "chrome/browser/plugins/plugin_prefs.h"
     23 #include "chrome/browser/profiles/profile.h"
     24 #include "chrome/common/chrome_paths.h"
     25 #include "chrome/common/chrome_switches.h"
     26 #include "chrome/common/chrome_version_info.h"
     27 #include "chrome/common/url_constants.h"
     28 #include "content/public/browser/browser_thread.h"
     29 #include "content/public/browser/plugin_service.h"
     30 #include "content/public/browser/user_metrics.h"
     31 #include "content/public/browser/web_ui.h"
     32 #include "content/public/browser/web_ui_data_source.h"
     33 #include "content/public/browser/web_ui_message_handler.h"
     34 #include "content/public/common/webplugininfo.h"
     35 #include "grit/browser_resources.h"
     36 #include "grit/chromium_strings.h"
     37 #include "grit/generated_resources.h"
     38 #include "grit/theme_resources.h"
     39 #include "ui/base/l10n/l10n_util.h"
     40 #include "ui/base/resource/resource_bundle.h"
     41 
     42 #if defined(OS_WIN)
     43 #include "base/win/windows_version.h"
     44 #endif
     45 
     46 using content::BrowserThread;
     47 using content::PluginService;
     48 using content::UserMetricsAction;
     49 using content::WebUIMessageHandler;
     50 
     51 namespace {
     52 
     53 content::WebUIDataSource* CreateNaClUIHTMLSource() {
     54   content::WebUIDataSource* source =
     55       content::WebUIDataSource::Create(chrome::kChromeUINaClHost);
     56 
     57   source->SetUseJsonJSFormatV2();
     58   source->AddLocalizedString("loadingMessage", IDS_NACL_LOADING_MESSAGE);
     59   source->AddLocalizedString("naclLongTitle", IDS_NACL_TITLE_MESSAGE);
     60   source->SetJsonPath("strings.js");
     61   source->AddResourcePath("about_nacl.css", IDR_ABOUT_NACL_CSS);
     62   source->AddResourcePath("about_nacl.js", IDR_ABOUT_NACL_JS);
     63   source->SetDefaultResource(IDR_ABOUT_NACL_HTML);
     64   return source;
     65 }
     66 
     67 ////////////////////////////////////////////////////////////////////////////////
     68 //
     69 // NaClDomHandler
     70 //
     71 ////////////////////////////////////////////////////////////////////////////////
     72 
     73 // The handler for JavaScript messages for the about:flags page.
     74 class NaClDomHandler : public WebUIMessageHandler {
     75  public:
     76   NaClDomHandler();
     77   virtual ~NaClDomHandler();
     78 
     79   // WebUIMessageHandler implementation.
     80   virtual void RegisterMessages() OVERRIDE;
     81 
     82  private:
     83   // Callback for the "requestNaClInfo" message.
     84   void HandleRequestNaClInfo(const ListValue* args);
     85 
     86   // Callback for the NaCl plugin information.
     87   void OnGotPlugins(const std::vector<content::WebPluginInfo>& plugins);
     88 
     89   // A helper callback that receives the result of checking if PNaCl path
     90   // exists and checking the PNaCl |version|. |is_valid| is true if the PNaCl
     91   // path that was returned by PathService is valid, and false otherwise.
     92   void DidCheckPathAndVersion(const std::string* version, bool is_valid);
     93 
     94   // Called when enough information is gathered to return data back to the page.
     95   void MaybeRespondToPage();
     96 
     97   // Helper for MaybeRespondToPage -- called after enough information
     98   // is gathered.
     99   void PopulatePageInformation(DictionaryValue* naclInfo);
    100 
    101   // Returns whether the specified plugin is enabled.
    102   bool isPluginEnabled(size_t plugin_index);
    103 
    104   // Adds information regarding the operating system and chrome version to list.
    105   void AddOperatingSystemInfo(ListValue* list);
    106 
    107   // Adds the list of plugins for NaCl to list.
    108   void AddPluginList(ListValue* list);
    109 
    110   // Adds the information relevant to PNaCl (e.g., enablement, paths, version)
    111   // to the list.
    112   void AddPnaclInfo(ListValue* list);
    113 
    114   // Adds the information relevant to NaCl to list.
    115   void AddNaClInfo(ListValue* list);
    116 
    117   // Whether the page has requested data.
    118   bool page_has_requested_data_;
    119 
    120   // Whether the plugin information is ready.
    121   bool has_plugin_info_;
    122 
    123   // Whether PNaCl path was validated. PathService can return a path
    124   // that does not exists, so it needs to be validated.
    125   bool pnacl_path_validated_;
    126   bool pnacl_path_exists_;
    127   std::string pnacl_version_string_;
    128 
    129   // Factory for the creating refs in callbacks.
    130   base::WeakPtrFactory<NaClDomHandler> weak_ptr_factory_;
    131 
    132   DISALLOW_COPY_AND_ASSIGN(NaClDomHandler);
    133 };
    134 
    135 NaClDomHandler::NaClDomHandler()
    136     : page_has_requested_data_(false),
    137       has_plugin_info_(false),
    138       pnacl_path_validated_(false),
    139       pnacl_path_exists_(false),
    140       weak_ptr_factory_(this) {
    141   PluginService::GetInstance()->GetPlugins(base::Bind(
    142       &NaClDomHandler::OnGotPlugins, weak_ptr_factory_.GetWeakPtr()));
    143 }
    144 
    145 NaClDomHandler::~NaClDomHandler() {
    146 }
    147 
    148 void NaClDomHandler::RegisterMessages() {
    149   web_ui()->RegisterMessageCallback(
    150       "requestNaClInfo",
    151       base::Bind(&NaClDomHandler::HandleRequestNaClInfo,
    152                  base::Unretained(this)));
    153 }
    154 
    155 // Helper functions for collecting a list of key-value pairs that will
    156 // be displayed.
    157 void AddPair(ListValue* list,
    158              const base::string16& key,
    159              const base::string16& value) {
    160   DictionaryValue* results = new DictionaryValue();
    161   results->SetString("key", key);
    162   results->SetString("value", value);
    163   list->Append(results);
    164 }
    165 
    166 // Generate an empty data-pair which acts as a line break.
    167 void AddLineBreak(ListValue* list) {
    168   AddPair(list, ASCIIToUTF16(""), ASCIIToUTF16(""));
    169 }
    170 
    171 bool NaClDomHandler::isPluginEnabled(size_t plugin_index) {
    172   std::vector<content::WebPluginInfo> info_array;
    173   PluginService::GetInstance()->GetPluginInfoArray(
    174       GURL(), "application/x-nacl", false, &info_array, NULL);
    175   PluginPrefs* plugin_prefs =
    176       PluginPrefs::GetForProfile(Profile::FromWebUI(web_ui())).get();
    177   return (!info_array.empty() &&
    178           plugin_prefs->IsPluginEnabled(info_array[plugin_index]));
    179 }
    180 
    181 void NaClDomHandler::AddOperatingSystemInfo(ListValue* list) {
    182   // Obtain the Chrome version info.
    183   chrome::VersionInfo version_info;
    184   AddPair(list,
    185           l10n_util::GetStringUTF16(IDS_PRODUCT_NAME),
    186           ASCIIToUTF16(version_info.Version() + " (" +
    187                        chrome::VersionInfo::GetVersionStringModifier() + ")"));
    188 
    189   // OS version information.
    190   // TODO(jvoung): refactor this to share the extra windows labeling
    191   // with about:flash, or something.
    192   std::string os_label = version_info.OSType();
    193 #if defined(OS_WIN)
    194   base::win::OSInfo* os = base::win::OSInfo::GetInstance();
    195   switch (os->version()) {
    196     case base::win::VERSION_XP: os_label += " XP"; break;
    197     case base::win::VERSION_SERVER_2003:
    198       os_label += " Server 2003 or XP Pro 64 bit";
    199       break;
    200     case base::win::VERSION_VISTA: os_label += " Vista or Server 2008"; break;
    201     case base::win::VERSION_WIN7: os_label += " 7 or Server 2008 R2"; break;
    202     case base::win::VERSION_WIN8: os_label += " 8 or Server 2012"; break;
    203     default:  os_label += " UNKNOWN"; break;
    204   }
    205   os_label += " SP" + base::IntToString(os->service_pack().major);
    206   if (os->service_pack().minor > 0)
    207     os_label += "." + base::IntToString(os->service_pack().minor);
    208   if (os->architecture() == base::win::OSInfo::X64_ARCHITECTURE)
    209     os_label += " 64 bit";
    210 #endif
    211   AddPair(list,
    212           l10n_util::GetStringUTF16(IDS_ABOUT_VERSION_OS),
    213           ASCIIToUTF16(os_label));
    214   AddLineBreak(list);
    215 }
    216 
    217 void NaClDomHandler::AddPluginList(ListValue* list) {
    218   // Obtain the version of the NaCl plugin.
    219   std::vector<content::WebPluginInfo> info_array;
    220   PluginService::GetInstance()->GetPluginInfoArray(
    221       GURL(), "application/x-nacl", false, &info_array, NULL);
    222   base::string16 nacl_version;
    223   base::string16 nacl_key = ASCIIToUTF16("NaCl plugin");
    224   if (info_array.empty()) {
    225     AddPair(list, nacl_key, ASCIIToUTF16("Disabled"));
    226   } else {
    227     // Only the 0th plugin is used.
    228     nacl_version = info_array[0].version + ASCIIToUTF16(" ") +
    229         info_array[0].path.LossyDisplayName();
    230     if (!isPluginEnabled(0)) {
    231       nacl_version += ASCIIToUTF16(" (Disabled in profile prefs)");
    232     }
    233 
    234     AddPair(list, nacl_key, nacl_version);
    235 
    236     // Mark the rest as not used.
    237     for (size_t i = 1; i < info_array.size(); ++i) {
    238       nacl_version = info_array[i].version + ASCIIToUTF16(" ") +
    239           info_array[i].path.LossyDisplayName();
    240       nacl_version += ASCIIToUTF16(" (not used)");
    241       if (!isPluginEnabled(i)) {
    242         nacl_version += ASCIIToUTF16(" (Disabled in profile prefs)");
    243       }
    244       AddPair(list, nacl_key, nacl_version);
    245     }
    246   }
    247   AddLineBreak(list);
    248 }
    249 
    250 void NaClDomHandler::AddPnaclInfo(ListValue* list) {
    251   // Display whether PNaCl is enabled.
    252   base::string16 pnacl_enabled_string = ASCIIToUTF16("Enabled");
    253   if (!isPluginEnabled(0)) {
    254     pnacl_enabled_string = ASCIIToUTF16("Disabled in profile prefs");
    255   } else if (CommandLine::ForCurrentProcess()->HasSwitch(
    256                  switches::kDisablePnacl)) {
    257     pnacl_enabled_string = ASCIIToUTF16("Disabled by flag '--disable-pnacl'");
    258   }
    259   AddPair(list,
    260           ASCIIToUTF16("Portable Native Client (PNaCl)"),
    261           pnacl_enabled_string);
    262 
    263   // Obtain the version of the PNaCl translator.
    264   base::FilePath pnacl_path;
    265   bool got_path = PathService::Get(chrome::DIR_PNACL_COMPONENT, &pnacl_path);
    266   if (!got_path || pnacl_path.empty() || !pnacl_path_exists_) {
    267     AddPair(list,
    268             ASCIIToUTF16("PNaCl translator"),
    269             ASCIIToUTF16("Not installed"));
    270   } else {
    271     AddPair(list,
    272             ASCIIToUTF16("PNaCl translator path"),
    273             pnacl_path.LossyDisplayName());
    274     AddPair(list,
    275             ASCIIToUTF16("PNaCl translator version"),
    276             ASCIIToUTF16(pnacl_version_string_));
    277   }
    278   AddLineBreak(list);
    279 }
    280 
    281 void NaClDomHandler::AddNaClInfo(ListValue* list) {
    282   base::string16 nacl_enabled_string = ASCIIToUTF16("Disabled");
    283   if (isPluginEnabled(0) &&
    284       CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableNaCl)) {
    285     nacl_enabled_string = ASCIIToUTF16("Enabled by flag '--enable-nacl'");
    286   }
    287   AddPair(list,
    288           ASCIIToUTF16("Native Client (non-portable, outside web store)"),
    289           nacl_enabled_string);
    290   AddLineBreak(list);
    291 }
    292 
    293 void NaClDomHandler::HandleRequestNaClInfo(const ListValue* args) {
    294   page_has_requested_data_ = true;
    295   // Force re-validation of PNaCl's path in the next call to
    296   // MaybeRespondToPage(), in case PNaCl went from not-installed
    297   // to installed since the request.
    298   pnacl_path_validated_ = false;
    299   MaybeRespondToPage();
    300 }
    301 
    302 void NaClDomHandler::OnGotPlugins(
    303     const std::vector<content::WebPluginInfo>& plugins) {
    304   has_plugin_info_ = true;
    305   MaybeRespondToPage();
    306 }
    307 
    308 void NaClDomHandler::PopulatePageInformation(DictionaryValue* naclInfo) {
    309   DCHECK(pnacl_path_validated_);
    310   // Store Key-Value pairs of about-information.
    311   scoped_ptr<ListValue> list(new ListValue());
    312   // Display the operating system and chrome version information.
    313   AddOperatingSystemInfo(list.get());
    314   // Display the list of plugins serving NaCl.
    315   AddPluginList(list.get());
    316   // Display information relevant to PNaCl.
    317   AddPnaclInfo(list.get());
    318   // Display information relevant to NaCl (non-portable.
    319   AddNaClInfo(list.get());
    320   // naclInfo will take ownership of list, and clean it up on destruction.
    321   naclInfo->Set("naclInfo", list.release());
    322 }
    323 
    324 void NaClDomHandler::DidCheckPathAndVersion(const std::string* version,
    325                                             bool is_valid) {
    326   pnacl_path_validated_ = true;
    327   pnacl_path_exists_ = is_valid;
    328   pnacl_version_string_ = *version;
    329   MaybeRespondToPage();
    330 }
    331 
    332 void CheckVersion(const base::FilePath& pnacl_path, std::string* version) {
    333   base::FilePath pnacl_json_path =
    334       pnacl_path.AppendASCII("pnacl_public_pnacl_json");
    335   JSONFileValueSerializer serializer(pnacl_json_path);
    336   std::string error;
    337   scoped_ptr<base::Value> root(serializer.Deserialize(NULL, &error));
    338   if (!root || !root->IsType(base::Value::TYPE_DICTIONARY))
    339     return;
    340 
    341   // Now try to get the field. This may leave version empty if the
    342   // the "get" fails (no key, or wrong type).
    343   static_cast<base::DictionaryValue*>(root.get())->GetStringASCII(
    344       "pnacl-version", version);
    345 }
    346 
    347 bool CheckPathAndVersion(std::string* version) {
    348   base::FilePath pnacl_path;
    349   bool got_path = PathService::Get(chrome::DIR_PNACL_COMPONENT, &pnacl_path);
    350   if (got_path && !pnacl_path.empty() && base::PathExists(pnacl_path)) {
    351     CheckVersion(pnacl_path, version);
    352     return true;
    353   }
    354   return false;
    355 }
    356 
    357 void NaClDomHandler::MaybeRespondToPage() {
    358   // Don't reply until everything is ready.  The page will show a 'loading'
    359   // message until then.
    360   if (!page_has_requested_data_ || !has_plugin_info_)
    361     return;
    362 
    363   if (!pnacl_path_validated_) {
    364     std::string* version_string = new std::string;
    365     base::PostTaskAndReplyWithResult(
    366         BrowserThread::GetBlockingPool(),
    367         FROM_HERE,
    368         base::Bind(&CheckPathAndVersion, version_string),
    369         base::Bind(&NaClDomHandler::DidCheckPathAndVersion,
    370                    weak_ptr_factory_.GetWeakPtr(),
    371                    base::Owned(version_string)));
    372     return;
    373   }
    374 
    375   DictionaryValue naclInfo;
    376   PopulatePageInformation(&naclInfo);
    377   web_ui()->CallJavascriptFunction("nacl.returnNaClInfo", naclInfo);
    378 }
    379 
    380 }  // namespace
    381 
    382 ///////////////////////////////////////////////////////////////////////////////
    383 //
    384 // NaClUI
    385 //
    386 ///////////////////////////////////////////////////////////////////////////////
    387 
    388 NaClUI::NaClUI(content::WebUI* web_ui) : WebUIController(web_ui) {
    389   content::RecordAction(UserMetricsAction("ViewAboutNaCl"));
    390 
    391   web_ui->AddMessageHandler(new NaClDomHandler());
    392 
    393   // Set up the about:nacl source.
    394   Profile* profile = Profile::FromWebUI(web_ui);
    395   content::WebUIDataSource::Add(profile, CreateNaClUIHTMLSource());
    396 }
    397