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