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/flash_ui.h" 6 7 #include <map> 8 #include <string> 9 #include <vector> 10 11 #include "base/bind.h" 12 #include "base/bind_helpers.h" 13 #include "base/i18n/time_formatting.h" 14 #include "base/memory/weak_ptr.h" 15 #include "base/strings/string16.h" 16 #include "base/strings/string_number_conversions.h" 17 #include "base/strings/stringprintf.h" 18 #include "base/strings/utf_string_conversions.h" 19 #include "base/threading/thread_restrictions.h" 20 #include "base/timer/timer.h" 21 #include "base/values.h" 22 #include "chrome/browser/crash_upload_list.h" 23 #include "chrome/browser/metrics/chrome_metrics_service_accessor.h" 24 #include "chrome/browser/plugins/plugin_prefs.h" 25 #include "chrome/browser/profiles/profile.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/gpu_data_manager.h" 31 #include "content/public/browser/gpu_data_manager_observer.h" 32 #include "content/public/browser/plugin_service.h" 33 #include "content/public/browser/user_metrics.h" 34 #include "content/public/browser/web_contents.h" 35 #include "content/public/browser/web_ui.h" 36 #include "content/public/browser/web_ui_data_source.h" 37 #include "content/public/browser/web_ui_message_handler.h" 38 #include "content/public/common/content_constants.h" 39 #include "content/public/common/webplugininfo.h" 40 #include "gpu/config/gpu_info.h" 41 #include "grit/browser_resources.h" 42 #include "ui/base/l10n/l10n_util.h" 43 44 #if defined(OS_WIN) 45 #include "base/win/windows_version.h" 46 #endif 47 48 using base::ASCIIToUTF16; 49 using base::UserMetricsAction; 50 using content::GpuDataManager; 51 using content::PluginService; 52 using content::WebContents; 53 using content::WebUIMessageHandler; 54 55 namespace { 56 57 const char kFlashPlugin[] = "Flash plugin"; 58 59 content::WebUIDataSource* CreateFlashUIHTMLSource() { 60 content::WebUIDataSource* source = 61 content::WebUIDataSource::Create(chrome::kChromeUIFlashHost); 62 63 source->SetUseJsonJSFormatV2(); 64 source->AddLocalizedString("loadingMessage", IDS_FLASH_LOADING_MESSAGE); 65 source->AddLocalizedString("flashLongTitle", IDS_FLASH_TITLE_MESSAGE); 66 source->SetJsonPath("strings.js"); 67 source->AddResourcePath("about_flash.js", IDR_ABOUT_FLASH_JS); 68 source->SetDefaultResource(IDR_ABOUT_FLASH_HTML); 69 return source; 70 } 71 72 const int kTimeout = 8 * 1000; // 8 seconds. 73 74 //////////////////////////////////////////////////////////////////////////////// 75 // 76 // FlashDOMHandler 77 // 78 //////////////////////////////////////////////////////////////////////////////// 79 80 // The handler for JavaScript messages for the about:flags page. 81 class FlashDOMHandler : public WebUIMessageHandler, 82 public CrashUploadList::Delegate, 83 public content::GpuDataManagerObserver { 84 public: 85 FlashDOMHandler(); 86 virtual ~FlashDOMHandler(); 87 88 // WebUIMessageHandler implementation. 89 virtual void RegisterMessages() OVERRIDE; 90 91 // CrashUploadList::Delegate implementation. 92 virtual void OnUploadListAvailable() OVERRIDE; 93 94 // GpuDataManager::Observer implementation. 95 virtual void OnGpuInfoUpdate() OVERRIDE; 96 97 // Callback for the "requestFlashInfo" message. 98 void HandleRequestFlashInfo(const base::ListValue* args); 99 100 // Callback for the Flash plugin information. 101 void OnGotPlugins(const std::vector<content::WebPluginInfo>& plugins); 102 103 private: 104 // Called when we think we might have enough information to return data back 105 // to the page. 106 void MaybeRespondToPage(); 107 108 // In certain cases we might not get called back from the GPU process so we 109 // set an upper limit on the time we wait. This function gets called when the 110 // time has passed. This actually doesn't prevent the rest of the information 111 // to appear later, the page will just reflow when more information becomes 112 // available. 113 void OnTimeout(); 114 115 // A timer to keep track of when the data fetching times out. 116 base::OneShotTimer<FlashDOMHandler> timeout_; 117 118 // Crash list. 119 scoped_refptr<CrashUploadList> upload_list_; 120 121 // Whether the list of all crashes is available. 122 bool crash_list_available_; 123 // Whether the page has requested data. 124 bool page_has_requested_data_; 125 // Whether the GPU data has been collected. 126 bool has_gpu_info_; 127 // Whether the plugin information is ready. 128 bool has_plugin_info_; 129 130 base::WeakPtrFactory<FlashDOMHandler> weak_ptr_factory_; 131 132 DISALLOW_COPY_AND_ASSIGN(FlashDOMHandler); 133 }; 134 135 FlashDOMHandler::FlashDOMHandler() 136 : crash_list_available_(false), 137 page_has_requested_data_(false), 138 has_gpu_info_(false), 139 has_plugin_info_(false), 140 weak_ptr_factory_(this) { 141 // Request Crash data asynchronously. 142 upload_list_ = CrashUploadList::Create(this); 143 upload_list_->LoadUploadListAsynchronously(); 144 145 // Watch for changes in GPUInfo. 146 GpuDataManager::GetInstance()->AddObserver(this); 147 148 // Tell GpuDataManager it should have full GpuInfo. If the 149 // GPU process has not run yet, this will trigger its launch. 150 GpuDataManager::GetInstance()->RequestCompleteGpuInfoIfNeeded(); 151 152 // GPU access might not be allowed at all, which will cause us not to get a 153 // call back. 154 if (!GpuDataManager::GetInstance()->GpuAccessAllowed(NULL)) 155 OnGpuInfoUpdate(); 156 157 PluginService::GetInstance()->GetPlugins(base::Bind( 158 &FlashDOMHandler::OnGotPlugins, weak_ptr_factory_.GetWeakPtr())); 159 160 // And lastly, we fire off a timer to make sure we never get stuck at the 161 // "Loading..." message. 162 timeout_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(kTimeout), 163 this, &FlashDOMHandler::OnTimeout); 164 } 165 166 FlashDOMHandler::~FlashDOMHandler() { 167 GpuDataManager::GetInstance()->RemoveObserver(this); 168 upload_list_->ClearDelegate(); 169 } 170 171 void FlashDOMHandler::RegisterMessages() { 172 web_ui()->RegisterMessageCallback("requestFlashInfo", 173 base::Bind(&FlashDOMHandler::HandleRequestFlashInfo, 174 base::Unretained(this))); 175 } 176 177 void FlashDOMHandler::OnUploadListAvailable() { 178 crash_list_available_ = true; 179 MaybeRespondToPage(); 180 } 181 182 void AddPair(base::ListValue* list, 183 const base::string16& key, 184 const base::string16& value) { 185 base::DictionaryValue* results = new base::DictionaryValue(); 186 results->SetString("key", key); 187 results->SetString("value", value); 188 list->Append(results); 189 } 190 191 void AddPair(base::ListValue* list, 192 const base::string16& key, 193 const std::string& value) { 194 AddPair(list, key, ASCIIToUTF16(value)); 195 } 196 197 void FlashDOMHandler::HandleRequestFlashInfo(const base::ListValue* args) { 198 page_has_requested_data_ = true; 199 MaybeRespondToPage(); 200 } 201 202 void FlashDOMHandler::OnGpuInfoUpdate() { 203 has_gpu_info_ = true; 204 MaybeRespondToPage(); 205 } 206 207 void FlashDOMHandler::OnGotPlugins( 208 const std::vector<content::WebPluginInfo>& plugins) { 209 has_plugin_info_ = true; 210 MaybeRespondToPage(); 211 } 212 213 void FlashDOMHandler::OnTimeout() { 214 // We don't set page_has_requested_data_ because that is guaranteed to appear 215 // and we shouldn't be responding to the page before then. 216 has_gpu_info_ = true; 217 crash_list_available_ = true; 218 has_plugin_info_ = true; 219 MaybeRespondToPage(); 220 } 221 222 void FlashDOMHandler::MaybeRespondToPage() { 223 // We don't reply until everything is ready. The page is showing a 'loading' 224 // message until then. If you add criteria to this list, please update the 225 // function OnTimeout() as well. 226 if (!page_has_requested_data_ || !crash_list_available_ || !has_gpu_info_ || 227 !has_plugin_info_) { 228 return; 229 } 230 231 timeout_.Stop(); 232 233 // This is code that runs only when the user types in about:flash. We don't 234 // need to jump through hoops to offload this to the IO thread. 235 base::ThreadRestrictions::ScopedAllowIO allow_io; 236 237 // Obtain the Chrome version info. 238 chrome::VersionInfo version_info; 239 240 base::ListValue* list = new base::ListValue(); 241 242 // Chrome version information. 243 AddPair(list, 244 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME), 245 version_info.Version() + " (" + 246 chrome::VersionInfo::GetVersionStringModifier() + ")"); 247 248 // OS version information. 249 std::string os_label = version_info.OSType(); 250 #if defined(OS_WIN) 251 base::win::OSInfo* os = base::win::OSInfo::GetInstance(); 252 switch (os->version()) { 253 case base::win::VERSION_XP: os_label += " XP"; break; 254 case base::win::VERSION_SERVER_2003: 255 os_label += " Server 2003 or XP Pro 64 bit"; 256 break; 257 case base::win::VERSION_VISTA: os_label += " Vista or Server 2008"; break; 258 case base::win::VERSION_WIN7: os_label += " 7 or Server 2008 R2"; break; 259 case base::win::VERSION_WIN8: os_label += " 8 or Server 2012"; break; 260 default: os_label += " UNKNOWN"; break; 261 } 262 os_label += " SP" + base::IntToString(os->service_pack().major); 263 if (os->service_pack().minor > 0) 264 os_label += "." + base::IntToString(os->service_pack().minor); 265 if (os->architecture() == base::win::OSInfo::X64_ARCHITECTURE) 266 os_label += " 64 bit"; 267 #endif 268 AddPair(list, l10n_util::GetStringUTF16(IDS_ABOUT_VERSION_OS), os_label); 269 270 // Obtain the version of the Flash plugins. 271 std::vector<content::WebPluginInfo> info_array; 272 PluginService::GetInstance()->GetPluginInfoArray( 273 GURL(), content::kFlashPluginSwfMimeType, false, &info_array, NULL); 274 if (info_array.empty()) { 275 AddPair(list, ASCIIToUTF16(kFlashPlugin), "Not installed"); 276 } else { 277 PluginPrefs* plugin_prefs = 278 PluginPrefs::GetForProfile(Profile::FromWebUI(web_ui())).get(); 279 bool found_enabled = false; 280 for (size_t i = 0; i < info_array.size(); ++i) { 281 base::string16 flash_version = info_array[i].version + ASCIIToUTF16(" ") + 282 info_array[i].path.LossyDisplayName(); 283 if (plugin_prefs->IsPluginEnabled(info_array[i])) { 284 // If we have already found an enabled Flash version, this one 285 // is not used. 286 if (found_enabled) 287 flash_version += ASCIIToUTF16(" (not used)"); 288 289 found_enabled = true; 290 } else { 291 flash_version += ASCIIToUTF16(" (disabled)"); 292 } 293 AddPair(list, ASCIIToUTF16(kFlashPlugin), flash_version); 294 } 295 } 296 297 // Crash information. 298 AddPair(list, base::string16(), "--- Crash data ---"); 299 bool crash_reporting_enabled = 300 ChromeMetricsServiceAccessor::IsCrashReportingEnabled(); 301 if (crash_reporting_enabled) { 302 std::vector<CrashUploadList::UploadInfo> crashes; 303 upload_list_->GetUploads(10, &crashes); 304 305 for (std::vector<CrashUploadList::UploadInfo>::iterator i = crashes.begin(); 306 i != crashes.end(); ++i) { 307 base::string16 crash_string(ASCIIToUTF16(i->id)); 308 crash_string += ASCIIToUTF16(" "); 309 crash_string += base::TimeFormatFriendlyDateAndTime(i->time); 310 AddPair(list, ASCIIToUTF16("crash id"), crash_string); 311 } 312 } else { 313 AddPair(list, ASCIIToUTF16("Crash Reporting"), 314 "Enable crash reporting to see crash IDs"); 315 AddPair(list, ASCIIToUTF16("For more details"), 316 chrome::kLearnMoreReportingURL); 317 } 318 319 // GPU information. 320 AddPair(list, base::string16(), "--- GPU information ---"); 321 gpu::GPUInfo gpu_info = GpuDataManager::GetInstance()->GetGPUInfo(); 322 323 std::string reason; 324 if (!GpuDataManager::GetInstance()->GpuAccessAllowed(&reason)) { 325 AddPair(list, ASCIIToUTF16("WARNING:"), 326 "GPU access is not allowed: " + reason); 327 } 328 #if defined(OS_WIN) 329 const gpu::DxDiagNode& node = gpu_info.dx_diagnostics; 330 for (std::map<std::string, gpu::DxDiagNode>::const_iterator it = 331 node.children.begin(); 332 it != node.children.end(); 333 ++it) { 334 for (std::map<std::string, std::string>::const_iterator it2 = 335 it->second.values.begin(); 336 it2 != it->second.values.end(); 337 ++it2) { 338 if (!it2->second.empty()) { 339 if (it2->first == "szDescription") { 340 AddPair(list, ASCIIToUTF16("Graphics card"), it2->second); 341 } else if (it2->first == "szDriverNodeStrongName") { 342 AddPair(list, ASCIIToUTF16("Driver name (strong)"), it2->second); 343 } else if (it2->first == "szDriverName") { 344 AddPair(list, ASCIIToUTF16("Driver display name"), it2->second); 345 } 346 } 347 } 348 } 349 #endif 350 351 AddPair(list, base::string16(), "--- GPU driver, more information ---"); 352 AddPair(list, 353 ASCIIToUTF16("Vendor Id"), 354 base::StringPrintf("0x%04x", gpu_info.gpu.vendor_id)); 355 AddPair(list, 356 ASCIIToUTF16("Device Id"), 357 base::StringPrintf("0x%04x", gpu_info.gpu.device_id)); 358 AddPair(list, ASCIIToUTF16("Driver vendor"), gpu_info.driver_vendor); 359 AddPair(list, ASCIIToUTF16("Driver version"), gpu_info.driver_version); 360 AddPair(list, ASCIIToUTF16("Driver date"), gpu_info.driver_date); 361 AddPair(list, 362 ASCIIToUTF16("Pixel shader version"), 363 gpu_info.pixel_shader_version); 364 AddPair(list, 365 ASCIIToUTF16("Vertex shader version"), 366 gpu_info.vertex_shader_version); 367 AddPair(list, ASCIIToUTF16("GL_VENDOR"), gpu_info.gl_vendor); 368 AddPair(list, ASCIIToUTF16("GL_RENDERER"), gpu_info.gl_renderer); 369 AddPair(list, ASCIIToUTF16("GL_VERSION"), gpu_info.gl_version); 370 AddPair(list, ASCIIToUTF16("GL_EXTENSIONS"), gpu_info.gl_extensions); 371 372 base::DictionaryValue flashInfo; 373 flashInfo.Set("flashInfo", list); 374 web_ui()->CallJavascriptFunction("returnFlashInfo", flashInfo); 375 } 376 377 } // namespace 378 379 /////////////////////////////////////////////////////////////////////////////// 380 // 381 // FlashUI 382 // 383 /////////////////////////////////////////////////////////////////////////////// 384 385 FlashUI::FlashUI(content::WebUI* web_ui) : WebUIController(web_ui) { 386 content::RecordAction( 387 UserMetricsAction("ViewAboutFlash")); 388 389 web_ui->AddMessageHandler(new FlashDOMHandler()); 390 391 // Set up the about:flash source. 392 Profile* profile = Profile::FromWebUI(web_ui); 393 content::WebUIDataSource::Add(profile, CreateFlashUIHTMLSource()); 394 } 395 396 // static 397 base::RefCountedMemory* FlashUI::GetFaviconResourceBytes( 398 ui::ScaleFactor scale_factor) { 399 // Use the default icon for now. 400 return NULL; 401 } 402