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/values.h"
     21 #include "chrome/browser/plugins/plugin_prefs.h"
     22 #include "chrome/browser/profiles/profile.h"
     23 #include "chrome/common/chrome_paths.h"
     24 #include "chrome/common/chrome_switches.h"
     25 #include "chrome/common/chrome_version_info.h"
     26 #include "chrome/common/url_constants.h"
     27 #include "content/public/browser/browser_thread.h"
     28 #include "content/public/browser/plugin_service.h"
     29 #include "content/public/browser/user_metrics.h"
     30 #include "content/public/browser/web_ui.h"
     31 #include "content/public/browser/web_ui_data_source.h"
     32 #include "content/public/browser/web_ui_message_handler.h"
     33 #include "content/public/common/webplugininfo.h"
     34 #include "grit/browser_resources.h"
     35 #include "grit/chromium_strings.h"
     36 #include "grit/generated_resources.h"
     37 #include "grit/theme_resources.h"
     38 #include "ui/base/l10n/l10n_util.h"
     39 #include "ui/base/resource/resource_bundle.h"
     40 
     41 #if defined(OS_WIN)
     42 #include "base/win/windows_version.h"
     43 #endif
     44 
     45 using content::BrowserThread;
     46 using content::PluginService;
     47 using content::UserMetricsAction;
     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 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(bool* is_valid, std::string* version);
     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(DictionaryValue* naclInfo);
     99 
    100   // Factory for the creating refs in callbacks.
    101   base::WeakPtrFactory<NaClDomHandler> weak_ptr_factory_;
    102 
    103   // Returns whether the specified plugin is enabled.
    104   bool isPluginEnabled(size_t plugin_index);
    105 
    106   // Adds information regarding the operating system and chrome version to list.
    107   void AddOperatingSystemInfo(ListValue* list);
    108 
    109   // Adds the list of plugins for NaCl to list.
    110   void AddPluginList(ListValue* list);
    111 
    112   // Adds the information relevant to PNaCl (e.g., enablement, paths, version)
    113   // to the list.
    114   void AddPnaclInfo(ListValue* list);
    115 
    116   // Adds the information relevant to NaCl to list.
    117   void AddNaClInfo(ListValue* list);
    118 
    119   // Whether the page has requested data.
    120   bool page_has_requested_data_;
    121 
    122   // Whether the plugin information is ready.
    123   bool has_plugin_info_;
    124 
    125   // Whether PNaCl path was validated. PathService can return a path
    126   // that does not exists, so it needs to be validated.
    127   bool pnacl_path_validated_;
    128   bool pnacl_path_exists_;
    129   std::string pnacl_version_string_;
    130 
    131   DISALLOW_COPY_AND_ASSIGN(NaClDomHandler);
    132 };
    133 
    134 NaClDomHandler::NaClDomHandler()
    135     : weak_ptr_factory_(this),
    136       page_has_requested_data_(false),
    137       has_plugin_info_(false),
    138       pnacl_path_validated_(false),
    139       pnacl_path_exists_(false) {
    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(ListValue* list, const string16& key, const string16& value) {
    157   DictionaryValue* results = new DictionaryValue();
    158   results->SetString("key", key);
    159   results->SetString("value", value);
    160   list->Append(results);
    161 }
    162 
    163 // Generate an empty data-pair which acts as a line break.
    164 void AddLineBreak(ListValue* list) {
    165   AddPair(list, ASCIIToUTF16(""), ASCIIToUTF16(""));
    166 }
    167 
    168 bool NaClDomHandler::isPluginEnabled(size_t plugin_index) {
    169   std::vector<content::WebPluginInfo> info_array;
    170   PluginService::GetInstance()->GetPluginInfoArray(
    171       GURL(), "application/x-nacl", false, &info_array, NULL);
    172   PluginPrefs* plugin_prefs =
    173       PluginPrefs::GetForProfile(Profile::FromWebUI(web_ui())).get();
    174   return (!info_array.empty() &&
    175           plugin_prefs->IsPluginEnabled(info_array[plugin_index]));
    176 }
    177 
    178 void NaClDomHandler::AddOperatingSystemInfo(ListValue* list) {
    179   // Obtain the Chrome version info.
    180   chrome::VersionInfo version_info;
    181   AddPair(list,
    182           l10n_util::GetStringUTF16(IDS_PRODUCT_NAME),
    183           ASCIIToUTF16(version_info.Version() + " (" +
    184                        chrome::VersionInfo::GetVersionStringModifier() + ")"));
    185 
    186   // OS version information.
    187   // TODO(jvoung): refactor this to share the extra windows labeling
    188   // with about:flash, or something.
    189   std::string os_label = version_info.OSType();
    190 #if defined(OS_WIN)
    191   base::win::OSInfo* os = base::win::OSInfo::GetInstance();
    192   switch (os->version()) {
    193     case base::win::VERSION_XP: os_label += " XP"; break;
    194     case base::win::VERSION_SERVER_2003:
    195       os_label += " Server 2003 or XP Pro 64 bit";
    196       break;
    197     case base::win::VERSION_VISTA: os_label += " Vista or Server 2008"; break;
    198     case base::win::VERSION_WIN7: os_label += " 7 or Server 2008 R2"; break;
    199     case base::win::VERSION_WIN8: os_label += " 8 or Server 2012"; break;
    200     default:  os_label += " UNKNOWN"; break;
    201   }
    202   os_label += " SP" + base::IntToString(os->service_pack().major);
    203   if (os->service_pack().minor > 0)
    204     os_label += "." + base::IntToString(os->service_pack().minor);
    205   if (os->architecture() == base::win::OSInfo::X64_ARCHITECTURE)
    206     os_label += " 64 bit";
    207 #endif
    208   AddPair(list,
    209           l10n_util::GetStringUTF16(IDS_ABOUT_VERSION_OS),
    210           ASCIIToUTF16(os_label));
    211   AddLineBreak(list);
    212 }
    213 
    214 void NaClDomHandler::AddPluginList(ListValue* list) {
    215   // Obtain the version of the NaCl plugin.
    216   std::vector<content::WebPluginInfo> info_array;
    217   PluginService::GetInstance()->GetPluginInfoArray(
    218       GURL(), "application/x-nacl", false, &info_array, NULL);
    219   string16 nacl_version;
    220   string16 nacl_key = ASCIIToUTF16("NaCl plugin");
    221   if (info_array.empty()) {
    222     AddPair(list, nacl_key, ASCIIToUTF16("Disabled"));
    223   } else {
    224     // Only the 0th plugin is used.
    225     nacl_version = info_array[0].version + ASCIIToUTF16(" ") +
    226         info_array[0].path.LossyDisplayName();
    227     if (!isPluginEnabled(0)) {
    228       nacl_version += ASCIIToUTF16(" (Disabled in profile prefs)");
    229     }
    230 
    231     AddPair(list, nacl_key, nacl_version);
    232 
    233     // Mark the rest as not used.
    234     for (size_t i = 1; i < info_array.size(); ++i) {
    235       nacl_version = info_array[i].version + ASCIIToUTF16(" ") +
    236           info_array[i].path.LossyDisplayName();
    237       nacl_version += ASCIIToUTF16(" (not used)");
    238       if (!isPluginEnabled(i)) {
    239         nacl_version += ASCIIToUTF16(" (Disabled in profile prefs)");
    240       }
    241       AddPair(list, nacl_key, nacl_version);
    242     }
    243   }
    244   AddLineBreak(list);
    245 }
    246 
    247 void NaClDomHandler::AddPnaclInfo(ListValue* list) {
    248   // Display whether PNaCl is enabled.
    249   string16 pnacl_enabled_string = ASCIIToUTF16("Enabled");
    250   if (!isPluginEnabled(0)) {
    251     pnacl_enabled_string = ASCIIToUTF16("Disabled in profile prefs");
    252   } else if (!CommandLine::ForCurrentProcess()->HasSwitch(
    253                  switches::kEnablePnacl)) {
    254     pnacl_enabled_string = ASCIIToUTF16("Not enabled by flag '--enable-pnacl'");
    255   }
    256   AddPair(list,
    257           ASCIIToUTF16("Portable Native Client (PNaCl)"),
    258           pnacl_enabled_string);
    259 
    260   // Obtain the version of the PNaCl translator.
    261   base::FilePath pnacl_path;
    262   bool got_path = PathService::Get(chrome::DIR_PNACL_COMPONENT, &pnacl_path);
    263   if (!got_path || pnacl_path.empty() || !pnacl_path_exists_) {
    264     AddPair(list,
    265             ASCIIToUTF16("PNaCl translator"),
    266             ASCIIToUTF16("Not installed"));
    267   } else {
    268     AddPair(list,
    269             ASCIIToUTF16("PNaCl translator path"),
    270             pnacl_path.LossyDisplayName());
    271     AddPair(list,
    272             ASCIIToUTF16("PNaCl translator version"),
    273             ASCIIToUTF16(pnacl_version_string_));
    274   }
    275   AddLineBreak(list);
    276 }
    277 
    278 void NaClDomHandler::AddNaClInfo(ListValue* list) {
    279   string16 nacl_enabled_string = ASCIIToUTF16("Disabled");
    280   if (isPluginEnabled(0) &&
    281       CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableNaCl)) {
    282     nacl_enabled_string = ASCIIToUTF16("Enabled by flag '--enable-nacl'");
    283   }
    284   AddPair(list,
    285           ASCIIToUTF16("Native Client (non-portable, outside web store)"),
    286           nacl_enabled_string);
    287   AddLineBreak(list);
    288 }
    289 
    290 void NaClDomHandler::HandleRequestNaClInfo(const ListValue* args) {
    291   page_has_requested_data_ = true;
    292   // Force re-validation of PNaCl's path in the next call to
    293   // MaybeRespondToPage(), in case PNaCl went from not-installed
    294   // to installed since the request.
    295   pnacl_path_validated_ = false;
    296   MaybeRespondToPage();
    297 }
    298 
    299 void NaClDomHandler::OnGotPlugins(
    300     const std::vector<content::WebPluginInfo>& plugins) {
    301   has_plugin_info_ = true;
    302   MaybeRespondToPage();
    303 }
    304 
    305 void NaClDomHandler::PopulatePageInformation(DictionaryValue* naclInfo) {
    306   DCHECK(pnacl_path_validated_);
    307   // Store Key-Value pairs of about-information.
    308   scoped_ptr<ListValue> list(new ListValue());
    309   // Display the operating system and chrome version information.
    310   AddOperatingSystemInfo(list.get());
    311   // Display the list of plugins serving NaCl.
    312   AddPluginList(list.get());
    313   // Display information relevant to PNaCl.
    314   AddPnaclInfo(list.get());
    315   // Display information relevant to NaCl (non-portable.
    316   AddNaClInfo(list.get());
    317   // naclInfo will take ownership of list, and clean it up on destruction.
    318   naclInfo->Set("naclInfo", list.release());
    319 }
    320 
    321 void NaClDomHandler::DidCheckPathAndVersion(bool* is_valid,
    322                                             std::string* version) {
    323   pnacl_path_validated_ = true;
    324   pnacl_path_exists_ = *is_valid;
    325   pnacl_version_string_ = *version;
    326   MaybeRespondToPage();
    327 }
    328 
    329 void CheckVersion(const base::FilePath& pnacl_path, std::string* version) {
    330   base::FilePath pnacl_json_path =
    331       pnacl_path.Append(FILE_PATH_LITERAL("pnacl_public_pnacl_json"));
    332   JSONFileValueSerializer serializer(pnacl_json_path);
    333   std::string error;
    334   scoped_ptr<base::Value> root(serializer.Deserialize(NULL, &error));
    335   if (!root || !root->IsType(base::Value::TYPE_DICTIONARY))
    336     return;
    337 
    338   // Now try to get the field. This may leave version empty if the
    339   // the "get" fails (no key, or wrong type).
    340   static_cast<base::DictionaryValue*>(root.get())->
    341       GetStringASCII("pnacl-version", version);
    342 }
    343 
    344 void CheckPathAndVersion(bool* is_valid, std::string* version) {
    345   base::FilePath pnacl_path;
    346   bool got_path = PathService::Get(chrome::DIR_PNACL_COMPONENT, &pnacl_path);
    347   *is_valid = got_path && !pnacl_path.empty() && base::PathExists(pnacl_path);
    348   if (*is_valid)
    349     CheckVersion(pnacl_path, version);
    350 }
    351 
    352 void NaClDomHandler::MaybeRespondToPage() {
    353   // Don't reply until everything is ready.  The page will show a 'loading'
    354   // message until then.
    355   if (!page_has_requested_data_ || !has_plugin_info_)
    356     return;
    357 
    358   if (!pnacl_path_validated_) {
    359     bool* is_valid = new bool;
    360     std::string* version_string = new std::string;
    361     BrowserThread::PostBlockingPoolTaskAndReply(
    362         FROM_HERE,
    363         base::Bind(&CheckPathAndVersion, is_valid, version_string),
    364         base::Bind(&NaClDomHandler::DidCheckPathAndVersion,
    365                    weak_ptr_factory_.GetWeakPtr(),
    366                    base::Owned(is_valid),
    367                    base::Owned(version_string)));
    368     return;
    369   }
    370 
    371   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