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/plugins/plugin_prefs.h"
24#include "chrome/browser/profiles/profile.h"
25#include "chrome/browser/ui/webui/crashes_ui.h"
26#include "chrome/common/chrome_version_info.h"
27#include "chrome/common/url_constants.h"
28#include "content/public/browser/gpu_data_manager.h"
29#include "content/public/browser/gpu_data_manager_observer.h"
30#include "content/public/browser/plugin_service.h"
31#include "content/public/browser/user_metrics.h"
32#include "content/public/browser/web_contents.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/content_constants.h"
37#include "content/public/common/webplugininfo.h"
38#include "gpu/config/gpu_info.h"
39#include "grit/browser_resources.h"
40#include "grit/chromium_strings.h"
41#include "grit/generated_resources.h"
42#include "grit/theme_resources.h"
43#include "ui/base/l10n/l10n_util.h"
44#include "ui/base/resource/resource_bundle.h"
45
46#if defined(OS_WIN)
47#include "base/win/windows_version.h"
48#endif
49
50using content::GpuDataManager;
51using content::PluginService;
52using content::UserMetricsAction;
53using content::WebContents;
54using content::WebUIMessageHandler;
55
56namespace {
57
58const char kFlashPlugin[] = "Flash plugin";
59
60content::WebUIDataSource* CreateFlashUIHTMLSource() {
61  content::WebUIDataSource* source =
62      content::WebUIDataSource::Create(chrome::kChromeUIFlashHost);
63
64  source->SetUseJsonJSFormatV2();
65  source->AddLocalizedString("loadingMessage", IDS_FLASH_LOADING_MESSAGE);
66  source->AddLocalizedString("flashLongTitle", IDS_FLASH_TITLE_MESSAGE);
67  source->SetJsonPath("strings.js");
68  source->AddResourcePath("about_flash.js", IDR_ABOUT_FLASH_JS);
69  source->SetDefaultResource(IDR_ABOUT_FLASH_HTML);
70  return source;
71}
72
73const int kTimeout = 8 * 1000;  // 8 seconds.
74
75////////////////////////////////////////////////////////////////////////////////
76//
77// FlashDOMHandler
78//
79////////////////////////////////////////////////////////////////////////////////
80
81// The handler for JavaScript messages for the about:flags page.
82class FlashDOMHandler : public WebUIMessageHandler,
83                        public CrashUploadList::Delegate,
84                        public content::GpuDataManagerObserver {
85 public:
86  FlashDOMHandler();
87  virtual ~FlashDOMHandler();
88
89  // WebUIMessageHandler implementation.
90  virtual void RegisterMessages() OVERRIDE;
91
92  // CrashUploadList::Delegate implementation.
93  virtual void OnUploadListAvailable() OVERRIDE;
94
95  // GpuDataManager::Observer implementation.
96  virtual void OnGpuInfoUpdate() OVERRIDE;
97
98  // Callback for the "requestFlashInfo" message.
99  void HandleRequestFlashInfo(const ListValue* args);
100
101  // Callback for the Flash plugin information.
102  void OnGotPlugins(const std::vector<content::WebPluginInfo>& plugins);
103
104 private:
105  // Called when we think we might have enough information to return data back
106  // to the page.
107  void MaybeRespondToPage();
108
109  // In certain cases we might not get called back from the GPU process so we
110  // set an upper limit on the time we wait. This function gets called when the
111  // time has passed. This actually doesn't prevent the rest of the information
112  // to appear later, the page will just reflow when more information becomes
113  // available.
114  void OnTimeout();
115
116  // A timer to keep track of when the data fetching times out.
117  base::OneShotTimer<FlashDOMHandler> timeout_;
118
119  // Crash list.
120  scoped_refptr<CrashUploadList> upload_list_;
121
122  // Whether the list of all crashes is available.
123  bool crash_list_available_;
124  // Whether the page has requested data.
125  bool page_has_requested_data_;
126  // Whether the GPU data has been collected.
127  bool has_gpu_info_;
128  // Whether the plugin information is ready.
129  bool has_plugin_info_;
130
131  base::WeakPtrFactory<FlashDOMHandler> weak_ptr_factory_;
132
133  DISALLOW_COPY_AND_ASSIGN(FlashDOMHandler);
134};
135
136FlashDOMHandler::FlashDOMHandler()
137    : crash_list_available_(false),
138      page_has_requested_data_(false),
139      has_gpu_info_(false),
140      has_plugin_info_(false),
141      weak_ptr_factory_(this) {
142        // Request Crash data asynchronously.
143  upload_list_ = CrashUploadList::Create(this);
144  upload_list_->LoadUploadListAsynchronously();
145
146  // Watch for changes in GPUInfo.
147  GpuDataManager::GetInstance()->AddObserver(this);
148
149  // Tell GpuDataManager it should have full GpuInfo. If the
150  // GPU process has not run yet, this will trigger its launch.
151  GpuDataManager::GetInstance()->RequestCompleteGpuInfoIfNeeded();
152
153  // GPU access might not be allowed at all, which will cause us not to get a
154  // call back.
155  if (!GpuDataManager::GetInstance()->GpuAccessAllowed(NULL))
156    OnGpuInfoUpdate();
157
158  PluginService::GetInstance()->GetPlugins(base::Bind(
159      &FlashDOMHandler::OnGotPlugins, weak_ptr_factory_.GetWeakPtr()));
160
161  // And lastly, we fire off a timer to make sure we never get stuck at the
162  // "Loading..." message.
163  timeout_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(kTimeout),
164                 this, &FlashDOMHandler::OnTimeout);
165}
166
167FlashDOMHandler::~FlashDOMHandler() {
168  GpuDataManager::GetInstance()->RemoveObserver(this);
169  upload_list_->ClearDelegate();
170}
171
172void FlashDOMHandler::RegisterMessages() {
173  web_ui()->RegisterMessageCallback("requestFlashInfo",
174      base::Bind(&FlashDOMHandler::HandleRequestFlashInfo,
175                 base::Unretained(this)));
176}
177
178void FlashDOMHandler::OnUploadListAvailable() {
179  crash_list_available_ = true;
180  MaybeRespondToPage();
181}
182
183void AddPair(ListValue* list,
184             const base::string16& key,
185             const base::string16& value) {
186  DictionaryValue* results = new DictionaryValue();
187  results->SetString("key", key);
188  results->SetString("value", value);
189  list->Append(results);
190}
191
192void AddPair(ListValue* list,
193             const base::string16& key,
194             const std::string& value) {
195  AddPair(list, key, ASCIIToUTF16(value));
196}
197
198void FlashDOMHandler::HandleRequestFlashInfo(const ListValue* args) {
199  page_has_requested_data_ = true;
200  MaybeRespondToPage();
201}
202
203void FlashDOMHandler::OnGpuInfoUpdate() {
204  has_gpu_info_ = true;
205  MaybeRespondToPage();
206}
207
208void FlashDOMHandler::OnGotPlugins(
209    const std::vector<content::WebPluginInfo>& plugins) {
210  has_plugin_info_ = true;
211  MaybeRespondToPage();
212}
213
214void FlashDOMHandler::OnTimeout() {
215  // We don't set page_has_requested_data_ because that is guaranteed to appear
216  // and we shouldn't be responding to the page before then.
217  has_gpu_info_ = true;
218  crash_list_available_ = true;
219  has_plugin_info_ = true;
220  MaybeRespondToPage();
221}
222
223void FlashDOMHandler::MaybeRespondToPage() {
224  // We don't reply until everything is ready. The page is showing a 'loading'
225  // message until then. If you add criteria to this list, please update the
226  // function OnTimeout() as well.
227  if (!page_has_requested_data_ || !crash_list_available_ || !has_gpu_info_ ||
228      !has_plugin_info_) {
229    return;
230  }
231
232  timeout_.Stop();
233
234  // This is code that runs only when the user types in about:flash. We don't
235  // need to jump through hoops to offload this to the IO thread.
236  base::ThreadRestrictions::ScopedAllowIO allow_io;
237
238  // Obtain the Chrome version info.
239  chrome::VersionInfo version_info;
240
241  ListValue* list = new ListValue();
242
243  // Chrome version information.
244  AddPair(list,
245          l10n_util::GetStringUTF16(IDS_PRODUCT_NAME),
246          version_info.Version() + " (" +
247          chrome::VersionInfo::GetVersionStringModifier() + ")");
248
249  // OS version information.
250  std::string os_label = version_info.OSType();
251#if defined(OS_WIN)
252  base::win::OSInfo* os = base::win::OSInfo::GetInstance();
253  switch (os->version()) {
254    case base::win::VERSION_XP: os_label += " XP"; break;
255    case base::win::VERSION_SERVER_2003:
256      os_label += " Server 2003 or XP Pro 64 bit";
257      break;
258    case base::win::VERSION_VISTA: os_label += " Vista or Server 2008"; break;
259    case base::win::VERSION_WIN7: os_label += " 7 or Server 2008 R2"; break;
260    case base::win::VERSION_WIN8: os_label += " 8 or Server 2012"; break;
261    default:  os_label += " UNKNOWN"; break;
262  }
263  os_label += " SP" + base::IntToString(os->service_pack().major);
264  if (os->service_pack().minor > 0)
265    os_label += "." + base::IntToString(os->service_pack().minor);
266  if (os->architecture() == base::win::OSInfo::X64_ARCHITECTURE)
267    os_label += " 64 bit";
268#endif
269  AddPair(list, l10n_util::GetStringUTF16(IDS_ABOUT_VERSION_OS), os_label);
270
271  // Obtain the version of the Flash plugins.
272  std::vector<content::WebPluginInfo> info_array;
273  PluginService::GetInstance()->GetPluginInfoArray(
274      GURL(), content::kFlashPluginSwfMimeType, false, &info_array, NULL);
275  if (info_array.empty()) {
276    AddPair(list, ASCIIToUTF16(kFlashPlugin), "Not installed");
277  } else {
278    PluginPrefs* plugin_prefs =
279        PluginPrefs::GetForProfile(Profile::FromWebUI(web_ui())).get();
280    bool found_enabled = false;
281    for (size_t i = 0; i < info_array.size(); ++i) {
282      base::string16 flash_version = info_array[i].version + ASCIIToUTF16(" ") +
283                               info_array[i].path.LossyDisplayName();
284      if (plugin_prefs->IsPluginEnabled(info_array[i])) {
285        // If we have already found an enabled Flash version, this one
286        // is not used.
287        if (found_enabled)
288          flash_version += ASCIIToUTF16(" (not used)");
289
290        found_enabled = true;
291      } else {
292        flash_version += ASCIIToUTF16(" (disabled)");
293      }
294      AddPair(list, ASCIIToUTF16(kFlashPlugin), flash_version);
295    }
296  }
297
298  // Crash information.
299  AddPair(list, base::string16(), "--- Crash data ---");
300  bool crash_reporting_enabled = CrashesUI::CrashReportingUIEnabled();
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 version"), gpu_info.gl_version);
368  AddPair(list, ASCIIToUTF16("GL_VENDOR"), gpu_info.gl_vendor);
369  AddPair(list, ASCIIToUTF16("GL_RENDERER"), gpu_info.gl_renderer);
370  AddPair(list, ASCIIToUTF16("GL_VERSION"), gpu_info.gl_version_string);
371  AddPair(list, ASCIIToUTF16("GL_EXTENSIONS"), gpu_info.gl_extensions);
372
373  DictionaryValue flashInfo;
374  flashInfo.Set("flashInfo", list);
375  web_ui()->CallJavascriptFunction("returnFlashInfo", flashInfo);
376}
377
378}  // namespace
379
380///////////////////////////////////////////////////////////////////////////////
381//
382// FlashUI
383//
384///////////////////////////////////////////////////////////////////////////////
385
386FlashUI::FlashUI(content::WebUI* web_ui) : WebUIController(web_ui) {
387  content::RecordAction(
388      UserMetricsAction("ViewAboutFlash"));
389
390  web_ui->AddMessageHandler(new FlashDOMHandler());
391
392  // Set up the about:flash source.
393  Profile* profile = Profile::FromWebUI(web_ui);
394  content::WebUIDataSource::Add(profile, CreateFlashUIHTMLSource());
395}
396
397// static
398base::RefCountedMemory* FlashUI::GetFaviconResourceBytes(
399      ui::ScaleFactor scale_factor) {
400  // Use the default icon for now.
401  return NULL;
402}
403