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