Home | History | Annotate | Download | only in webui
      1 // Copyright (c) 2011 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/gpu_internals_ui.h"
      6 
      7 #include <algorithm>
      8 #include <string>
      9 #include <utility>
     10 #include <vector>
     11 
     12 #include "base/command_line.h"
     13 #include "base/file_util.h"
     14 #include "base/memory/singleton.h"
     15 #include "base/message_loop.h"
     16 #include "base/path_service.h"
     17 #include "base/scoped_ptr.h"
     18 #include "base/string_number_conversions.h"
     19 #include "base/string_piece.h"
     20 #include "base/utf_string_conversions.h"
     21 #include "base/values.h"
     22 #include "chrome/browser/browser_process.h"
     23 #include "chrome/browser/gpu_data_manager.h"
     24 #include "chrome/browser/io_thread.h"
     25 #include "chrome/browser/net/chrome_net_log.h"
     26 #include "chrome/browser/net/connection_tester.h"
     27 #include "chrome/browser/net/passive_log_collector.h"
     28 #include "chrome/browser/net/url_fixer_upper.h"
     29 #include "chrome/browser/platform_util.h"
     30 #include "chrome/browser/profiles/profile.h"
     31 #include "chrome/browser/ui/shell_dialogs.h"
     32 #include "chrome/browser/ui/webui/chrome_url_data_manager.h"
     33 #include "chrome/common/chrome_paths.h"
     34 #include "chrome/common/chrome_version_info.h"
     35 #include "chrome/common/jstemplate_builder.h"
     36 #include "chrome/common/url_constants.h"
     37 #include "content/browser/browser_thread.h"
     38 #include "content/browser/gpu_process_host.h"
     39 #include "content/browser/renderer_host/render_view_host.h"
     40 #include "content/browser/tab_contents/tab_contents.h"
     41 #include "content/browser/tab_contents/tab_contents_view.h"
     42 #include "content/browser/trace_controller.h"
     43 #include "grit/browser_resources.h"
     44 #include "grit/generated_resources.h"
     45 #include "net/base/escape.h"
     46 #include "net/url_request/url_request_context_getter.h"
     47 #include "ui/base/l10n/l10n_util.h"
     48 #include "ui/base/resource/resource_bundle.h"
     49 
     50 namespace {
     51 
     52 class GpuHTMLSource : public ChromeURLDataManager::DataSource {
     53  public:
     54   GpuHTMLSource();
     55 
     56   // Called when the network layer has requested a resource underneath
     57   // the path we registered.
     58   virtual void StartDataRequest(const std::string& path,
     59                                 bool is_incognito,
     60                                 int request_id);
     61   virtual std::string GetMimeType(const std::string&) const;
     62 
     63  private:
     64   ~GpuHTMLSource() {}
     65   DISALLOW_COPY_AND_ASSIGN(GpuHTMLSource);
     66 };
     67 
     68 // This class receives javascript messages from the renderer.
     69 // Note that the WebUI infrastructure runs on the UI thread, therefore all of
     70 // this class's methods are expected to run on the UI thread.
     71 class GpuMessageHandler
     72     : public WebUIMessageHandler,
     73       public SelectFileDialog::Listener,
     74       public base::SupportsWeakPtr<GpuMessageHandler>,
     75       public TraceSubscriber {
     76  public:
     77   GpuMessageHandler();
     78   virtual ~GpuMessageHandler();
     79 
     80   // WebUIMessageHandler implementation.
     81   virtual WebUIMessageHandler* Attach(WebUI* web_ui);
     82   virtual void RegisterMessages();
     83 
     84   // Mesages
     85   void OnBeginTracing(const ListValue* list);
     86   void OnEndTracingAsync(const ListValue* list);
     87   void OnBrowserBridgeInitialized(const ListValue* list);
     88   void OnCallAsync(const ListValue* list);
     89   void OnBeginRequestBufferPercentFull(const ListValue* list);
     90   void OnLoadTraceFile(const ListValue* list);
     91   void OnSaveTraceFile(const ListValue* list);
     92 
     93   // Submessages dispatched from OnCallAsync
     94   Value* OnRequestClientInfo(const ListValue* list);
     95   Value* OnRequestLogMessages(const ListValue* list);
     96 
     97   // SelectFileDialog::Listener implementation
     98   virtual void FileSelected(const FilePath& path, int index, void* params);
     99   virtual void FileSelectionCanceled(void* params);
    100 
    101   // Callbacks.
    102   void OnGpuInfoUpdate();
    103   void LoadTraceFileComplete(std::string* file_contents);
    104   void SaveTraceFileComplete();
    105 
    106   // TraceSubscriber implementation.
    107   virtual void OnEndTracingComplete();
    108   virtual void OnTraceDataCollected(const std::string& json_events);
    109   virtual void OnTraceBufferPercentFullReply(float percent_full);
    110 
    111   // Executes the javascript function |function_name| in the renderer, passing
    112   // it the argument |value|.
    113   void CallJavascriptFunction(const std::wstring& function_name,
    114                               const Value* value);
    115 
    116  private:
    117   DISALLOW_COPY_AND_ASSIGN(GpuMessageHandler);
    118 
    119   // Cache the Singleton for efficiency.
    120   GpuDataManager* gpu_data_manager_;
    121 
    122   Callback0::Type* gpu_info_update_callback_;
    123 
    124   scoped_refptr<SelectFileDialog> select_trace_file_dialog_;
    125   SelectFileDialog::Type select_trace_file_dialog_type_;
    126   scoped_ptr<std::string> trace_data_to_save_;
    127 
    128   bool trace_enabled_;
    129 };
    130 
    131 class TaskProxy : public base::RefCountedThreadSafe<TaskProxy> {
    132  public:
    133   explicit TaskProxy(const base::WeakPtr<GpuMessageHandler>& handler)
    134       : handler_(handler) {}
    135   void LoadTraceFileCompleteProxy(std::string* file_contents) {
    136     if (handler_)
    137       handler_->LoadTraceFileComplete(file_contents);
    138     delete file_contents;
    139   }
    140 
    141   void SaveTraceFileCompleteProxy() {
    142     if (handler_)
    143       handler_->SaveTraceFileComplete();
    144   }
    145 
    146  private:
    147   base::WeakPtr<GpuMessageHandler> handler_;
    148   friend class base::RefCountedThreadSafe<TaskProxy>;
    149   DISALLOW_COPY_AND_ASSIGN(TaskProxy);
    150 };
    151 
    152 ////////////////////////////////////////////////////////////////////////////////
    153 //
    154 // GpuHTMLSource
    155 //
    156 ////////////////////////////////////////////////////////////////////////////////
    157 
    158 GpuHTMLSource::GpuHTMLSource()
    159     : DataSource(chrome::kChromeUIGpuInternalsHost, MessageLoop::current()) {
    160 }
    161 
    162 void GpuHTMLSource::StartDataRequest(const std::string& path,
    163                                      bool is_incognito,
    164                                      int request_id) {
    165   DictionaryValue localized_strings;
    166   SetFontAndTextDirection(&localized_strings);
    167 
    168   base::StringPiece gpu_html(
    169       ResourceBundle::GetSharedInstance().GetRawDataResource(
    170           IDR_GPU_INTERNALS_HTML));
    171   std::string full_html(gpu_html.data(), gpu_html.size());
    172   jstemplate_builder::AppendJsonHtml(&localized_strings, &full_html);
    173   jstemplate_builder::AppendI18nTemplateSourceHtml(&full_html);
    174   jstemplate_builder::AppendI18nTemplateProcessHtml(&full_html);
    175   jstemplate_builder::AppendJsTemplateSourceHtml(&full_html);
    176 
    177 
    178   scoped_refptr<RefCountedBytes> html_bytes(new RefCountedBytes);
    179   html_bytes->data.resize(full_html.size());
    180   std::copy(full_html.begin(), full_html.end(), html_bytes->data.begin());
    181 
    182   SendResponse(request_id, html_bytes);
    183 }
    184 
    185 std::string GpuHTMLSource::GetMimeType(const std::string&) const {
    186   return "text/html";
    187 }
    188 
    189 ////////////////////////////////////////////////////////////////////////////////
    190 //
    191 // GpuMessageHandler
    192 //
    193 ////////////////////////////////////////////////////////////////////////////////
    194 
    195 GpuMessageHandler::GpuMessageHandler()
    196   : gpu_info_update_callback_(NULL)
    197   , trace_enabled_(false) {
    198   gpu_data_manager_ = GpuDataManager::GetInstance();
    199   DCHECK(gpu_data_manager_);
    200 }
    201 
    202 GpuMessageHandler::~GpuMessageHandler() {
    203   if (gpu_info_update_callback_) {
    204     gpu_data_manager_->RemoveGpuInfoUpdateCallback(gpu_info_update_callback_);
    205     delete gpu_info_update_callback_;
    206   }
    207 
    208   if (select_trace_file_dialog_)
    209     select_trace_file_dialog_->ListenerDestroyed();
    210 
    211   // If we are the current subscriber, this will result in ending tracing.
    212   TraceController::GetInstance()->CancelSubscriber(this);
    213 }
    214 
    215 WebUIMessageHandler* GpuMessageHandler::Attach(WebUI* web_ui) {
    216   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    217   WebUIMessageHandler* result = WebUIMessageHandler::Attach(web_ui);
    218   return result;
    219 }
    220 
    221 /* BrowserBridge.callAsync prepends a requestID to these messages. */
    222 void GpuMessageHandler::RegisterMessages() {
    223   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    224 
    225   web_ui_->RegisterMessageCallback(
    226       "beginTracing",
    227       NewCallback(this, &GpuMessageHandler::OnBeginTracing));
    228   web_ui_->RegisterMessageCallback(
    229       "endTracingAsync",
    230       NewCallback(this, &GpuMessageHandler::OnEndTracingAsync));
    231   web_ui_->RegisterMessageCallback(
    232       "browserBridgeInitialized",
    233       NewCallback(this, &GpuMessageHandler::OnBrowserBridgeInitialized));
    234   web_ui_->RegisterMessageCallback(
    235       "callAsync",
    236       NewCallback(this, &GpuMessageHandler::OnCallAsync));
    237   web_ui_->RegisterMessageCallback(
    238       "beginRequestBufferPercentFull",
    239       NewCallback(this, &GpuMessageHandler::OnBeginRequestBufferPercentFull));
    240   web_ui_->RegisterMessageCallback(
    241       "loadTraceFile",
    242       NewCallback(this, &GpuMessageHandler::OnLoadTraceFile));
    243   web_ui_->RegisterMessageCallback(
    244       "saveTraceFile",
    245       NewCallback(this, &GpuMessageHandler::OnSaveTraceFile));
    246 }
    247 
    248 void GpuMessageHandler::OnCallAsync(const ListValue* args) {
    249   DCHECK_GE(args->GetSize(), static_cast<size_t>(2));
    250   // unpack args into requestId, submessage and submessageArgs
    251   bool ok;
    252   Value* requestId;
    253   ok = args->Get(0, &requestId);
    254   DCHECK(ok);
    255 
    256   std::string submessage;
    257   ok = args->GetString(1, &submessage);
    258   DCHECK(ok);
    259 
    260   ListValue* submessageArgs = new ListValue();
    261   for (size_t i = 2; i < args->GetSize(); ++i) {
    262     Value* arg;
    263     ok = args->Get(i, &arg);
    264     DCHECK(ok);
    265 
    266     Value* argCopy = arg->DeepCopy();
    267     submessageArgs->Append(argCopy);
    268   }
    269 
    270   // call the submessage handler
    271   Value* ret = NULL;
    272   if (submessage == "requestClientInfo") {
    273     ret = OnRequestClientInfo(submessageArgs);
    274   } else if (submessage == "requestLogMessages") {
    275     ret = OnRequestLogMessages(submessageArgs);
    276   } else {  // unrecognized submessage
    277     NOTREACHED();
    278     delete submessageArgs;
    279     return;
    280   }
    281   delete submessageArgs;
    282 
    283   // call BrowserBridge.onCallAsyncReply with result
    284   if (ret) {
    285     web_ui_->CallJavascriptFunction("browserBridge.onCallAsyncReply",
    286         *requestId,
    287         *ret);
    288     delete ret;
    289   } else {
    290     web_ui_->CallJavascriptFunction("browserBridge.onCallAsyncReply",
    291         *requestId);
    292   }
    293 }
    294 
    295 void GpuMessageHandler::OnBeginRequestBufferPercentFull(const ListValue* list) {
    296   TraceController::GetInstance()->GetTraceBufferPercentFullAsync(this);
    297 }
    298 
    299 class ReadTraceFileTask : public Task {
    300  public:
    301   ReadTraceFileTask(TaskProxy* proxy, const FilePath& path)
    302       : proxy_(proxy)
    303       , path_(path) {}
    304 
    305   virtual void Run() {
    306     std::string* file_contents = new std::string();
    307     if (!file_util::ReadFileToString(path_, file_contents))
    308       return;
    309     BrowserThread::PostTask(
    310         BrowserThread::UI, FROM_HERE,
    311         NewRunnableMethod(proxy_.get(),
    312                           &TaskProxy::LoadTraceFileCompleteProxy,
    313                           file_contents));
    314   }
    315 
    316  private:
    317   scoped_refptr<TaskProxy> proxy_;
    318 
    319   // Path of the file to open.
    320   const FilePath path_;
    321 };
    322 
    323 class WriteTraceFileTask : public Task {
    324  public:
    325   WriteTraceFileTask(TaskProxy* proxy,
    326                      const FilePath& path,
    327                      std::string* contents)
    328       : proxy_(proxy)
    329       , path_(path)
    330       , contents_(contents) {}
    331 
    332   virtual void Run() {
    333     if (!file_util::WriteFile(path_, contents_->c_str(), contents_->size()))
    334       return;
    335     BrowserThread::PostTask(
    336         BrowserThread::UI, FROM_HERE,
    337         NewRunnableMethod(proxy_.get(),
    338                           &TaskProxy::SaveTraceFileCompleteProxy));
    339   }
    340 
    341  private:
    342   scoped_refptr<TaskProxy> proxy_;
    343 
    344   // Path of the file to save.
    345   const FilePath path_;
    346 
    347   // What to save
    348   scoped_ptr<std::string> contents_;
    349 };
    350 
    351 void GpuMessageHandler::FileSelected(
    352     const FilePath& path, int index, void* params) {
    353   if(select_trace_file_dialog_type_ == SelectFileDialog::SELECT_OPEN_FILE)
    354     BrowserThread::PostTask(
    355         BrowserThread::FILE, FROM_HERE,
    356         new ReadTraceFileTask(new TaskProxy(AsWeakPtr()), path));
    357   else
    358     BrowserThread::PostTask(
    359         BrowserThread::FILE, FROM_HERE,
    360         new WriteTraceFileTask(new TaskProxy(AsWeakPtr()), path,
    361                                trace_data_to_save_.release()));
    362   select_trace_file_dialog_.release();
    363 }
    364 
    365 void GpuMessageHandler::FileSelectionCanceled(void* params) {
    366   select_trace_file_dialog_.release();
    367   if(select_trace_file_dialog_type_ == SelectFileDialog::SELECT_OPEN_FILE)
    368     web_ui_->CallJavascriptFunction("tracingController.onLoadTraceFileCanceled");
    369   else
    370     web_ui_->CallJavascriptFunction("tracingController.onSaveTraceFileCanceled");
    371 }
    372 
    373 void GpuMessageHandler::OnLoadTraceFile(const ListValue* list) {
    374   // Only allow a single dialog at a time.
    375   if (select_trace_file_dialog_.get())
    376     return;
    377   select_trace_file_dialog_type_ = SelectFileDialog::SELECT_OPEN_FILE;
    378   select_trace_file_dialog_ = SelectFileDialog::Create(this);
    379   select_trace_file_dialog_->SelectFile(
    380       SelectFileDialog::SELECT_OPEN_FILE,
    381       string16(),
    382       FilePath(),
    383       NULL, 0, FILE_PATH_LITERAL(""), web_ui_->tab_contents(),
    384       web_ui_->tab_contents()->view()->GetTopLevelNativeWindow(), NULL);
    385 }
    386 
    387 void GpuMessageHandler::LoadTraceFileComplete(std::string* file_contents) {
    388   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    389   std::wstring javascript;
    390   javascript += L"tracingController.onLoadTraceFileComplete(";
    391   javascript += UTF8ToWide(*file_contents);
    392   javascript += L");";
    393 
    394   web_ui_->GetRenderViewHost()->ExecuteJavascriptInWebFrame(string16(),
    395       WideToUTF16Hack(javascript));
    396 }
    397 
    398 void GpuMessageHandler::OnSaveTraceFile(const ListValue* list) {
    399   // Only allow a single dialog at a time.
    400   if (select_trace_file_dialog_.get())
    401     return;
    402 
    403   DCHECK(list->GetSize() == 1);
    404 
    405   Value* tmp;
    406   list->Get(0, &tmp);
    407 
    408   std::string* trace_data = new std::string();
    409   bool ok = list->GetString(0, trace_data);
    410   DCHECK(ok);
    411   trace_data_to_save_.reset(trace_data);
    412 
    413   select_trace_file_dialog_type_ = SelectFileDialog::SELECT_SAVEAS_FILE;
    414   select_trace_file_dialog_ = SelectFileDialog::Create(this);
    415   select_trace_file_dialog_->SelectFile(
    416       SelectFileDialog::SELECT_SAVEAS_FILE,
    417       string16(),
    418       FilePath(),
    419       NULL, 0, FILE_PATH_LITERAL(""), web_ui_->tab_contents(),
    420       web_ui_->tab_contents()->view()->GetTopLevelNativeWindow(), NULL);
    421 }
    422 
    423 void GpuMessageHandler::SaveTraceFileComplete() {
    424   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    425   std::wstring javascript;
    426   web_ui_->CallJavascriptFunction("tracingController.onSaveTraceFileComplete");
    427 }
    428 
    429 void GpuMessageHandler::OnBrowserBridgeInitialized(const ListValue* args) {
    430   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    431 
    432   DCHECK(!gpu_info_update_callback_);
    433 
    434   // Watch for changes in GPUInfo
    435   gpu_info_update_callback_ =
    436       NewCallback(this, &GpuMessageHandler::OnGpuInfoUpdate);
    437   gpu_data_manager_->AddGpuInfoUpdateCallback(gpu_info_update_callback_);
    438 
    439   // Tell GpuDataManager it should have full GpuInfo. If the
    440   // Gpu process has not run yet, this will trigger its launch.
    441   gpu_data_manager_->RequestCompleteGpuInfoIfNeeded();
    442 
    443   // Run callback immediately in case the info is ready and no update in the
    444   // future.
    445   OnGpuInfoUpdate();
    446 }
    447 
    448 Value* GpuMessageHandler::OnRequestClientInfo(const ListValue* list) {
    449   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    450 
    451   DictionaryValue* dict = new DictionaryValue();
    452 
    453   chrome::VersionInfo version_info;
    454 
    455   if (!version_info.is_valid()) {
    456     DLOG(ERROR) << "Unable to create chrome::VersionInfo";
    457   } else {
    458     // We have everything we need to send the right values.
    459     dict->SetString("version", version_info.Version());
    460     dict->SetString("cl", version_info.LastChange());
    461     dict->SetString("version_mod",
    462         platform_util::GetVersionStringModifier());
    463     dict->SetString("official",
    464         l10n_util::GetStringUTF16(
    465             version_info.IsOfficialBuild() ?
    466             IDS_ABOUT_VERSION_OFFICIAL :
    467             IDS_ABOUT_VERSION_UNOFFICIAL));
    468 
    469     dict->SetString("command_line",
    470         CommandLine::ForCurrentProcess()->command_line_string());
    471   }
    472 
    473   dict->SetString("blacklist_version",
    474       GpuDataManager::GetInstance()->GetBlacklistVersion());
    475 
    476   return dict;
    477 }
    478 
    479 DictionaryValue* NewDescriptionValuePair(const std::string& desc,
    480     const std::string& value) {
    481   DictionaryValue* dict = new DictionaryValue();
    482   dict->SetString("description", desc);
    483   dict->SetString("value", value);
    484   return dict;
    485 }
    486 
    487 DictionaryValue* NewDescriptionValuePair(const std::string& desc,
    488     Value* value) {
    489   DictionaryValue* dict = new DictionaryValue();
    490   dict->SetString("description", desc);
    491   dict->Set("value", value);
    492   return dict;
    493 }
    494 
    495 #if defined(OS_WIN)
    496 // Output DxDiagNode tree as nested array of {description,value} pairs
    497 ListValue* DxDiagNodeToList(const DxDiagNode& node) {
    498   ListValue* list = new ListValue();
    499   for (std::map<std::string, std::string>::const_iterator it =
    500       node.values.begin();
    501       it != node.values.end();
    502       ++it) {
    503     list->Append(NewDescriptionValuePair(it->first, it->second));
    504   }
    505 
    506   for (std::map<std::string, DxDiagNode>::const_iterator it =
    507       node.children.begin();
    508       it != node.children.end();
    509       ++it) {
    510     ListValue* sublist = DxDiagNodeToList(it->second);
    511     list->Append(NewDescriptionValuePair(it->first, sublist));
    512   }
    513   return list;
    514 }
    515 
    516 #endif  // OS_WIN
    517 
    518 DictionaryValue* GpuInfoToDict(const GPUInfo& gpu_info) {
    519   ListValue* basic_info = new ListValue();
    520   basic_info->Append(NewDescriptionValuePair("Initialization time",
    521       base::Int64ToString(gpu_info.initialization_time.InMilliseconds())));
    522   basic_info->Append(NewDescriptionValuePair("Vendor Id",
    523       base::StringPrintf("0x%04x", gpu_info.vendor_id)));
    524   basic_info->Append(NewDescriptionValuePair("Device Id",
    525       base::StringPrintf("0x%04x", gpu_info.device_id)));
    526   basic_info->Append(NewDescriptionValuePair("Driver vendor",
    527       gpu_info.driver_vendor));
    528   basic_info->Append(NewDescriptionValuePair("Driver version",
    529       gpu_info.driver_version));
    530   basic_info->Append(NewDescriptionValuePair("Driver date",
    531       gpu_info.driver_date));
    532   basic_info->Append(NewDescriptionValuePair("Pixel shader version",
    533       gpu_info.pixel_shader_version));
    534   basic_info->Append(NewDescriptionValuePair("Vertex shader version",
    535       gpu_info.vertex_shader_version));
    536   basic_info->Append(NewDescriptionValuePair("GL version",
    537       gpu_info.gl_version));
    538   basic_info->Append(NewDescriptionValuePair("GL_VENDOR",
    539       gpu_info.gl_vendor));
    540   basic_info->Append(NewDescriptionValuePair("GL_RENDERER",
    541       gpu_info.gl_renderer));
    542   basic_info->Append(NewDescriptionValuePair("GL_VERSION",
    543       gpu_info.gl_version_string));
    544   basic_info->Append(NewDescriptionValuePair("GL_EXTENSIONS",
    545       gpu_info.gl_extensions));
    546 
    547   DictionaryValue* info = new DictionaryValue();
    548   info->Set("basic_info", basic_info);
    549 
    550 #if defined(OS_WIN)
    551   Value* dx_info;
    552   if (gpu_info.dx_diagnostics.children.size())
    553     dx_info = DxDiagNodeToList(gpu_info.dx_diagnostics);
    554   else
    555     dx_info = Value::CreateNullValue();
    556   info->Set("diagnostics", dx_info);
    557 #endif
    558 
    559   return info;
    560 }
    561 
    562 Value* GpuMessageHandler::OnRequestLogMessages(const ListValue*) {
    563   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    564 
    565   return gpu_data_manager_->log_messages().DeepCopy();
    566 }
    567 
    568 void GpuMessageHandler::OnGpuInfoUpdate() {
    569   const GPUInfo& gpu_info = gpu_data_manager_->gpu_info();
    570 
    571   // Get GPU Info.
    572   DictionaryValue* gpu_info_val = GpuInfoToDict(gpu_info);
    573 
    574   // Add in blacklisting features
    575   Value* feature_status = gpu_data_manager_->GetFeatureStatus();
    576   if (feature_status)
    577     gpu_info_val->Set("featureStatus", feature_status);
    578 
    579   // Send GPU Info to javascript.
    580   web_ui_->CallJavascriptFunction("browserBridge.onGpuInfoUpdate",
    581       *gpu_info_val);
    582 
    583   delete gpu_info_val;
    584 }
    585 
    586 void GpuMessageHandler::OnBeginTracing(const ListValue* args) {
    587   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    588   trace_enabled_ = true;
    589   // TODO(jbates) This may fail, but that's OK for current use cases.
    590   //              Ex: Multiple about:gpu traces can not trace simultaneously.
    591   // TODO(nduca) send feedback to javascript about whether or not BeginTracing
    592   //             was successful.
    593   TraceController::GetInstance()->BeginTracing(this);
    594 }
    595 
    596 void GpuMessageHandler::OnEndTracingAsync(const ListValue* list) {
    597   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    598 
    599   // TODO(nduca): fix javascript code to make sure trace_enabled_ is always true
    600   //              here. triggered a false condition by just clicking stop
    601   //              trace a few times when it was going slow, and maybe switching
    602   //              between tabs.
    603   if (trace_enabled_ &&
    604       !TraceController::GetInstance()->EndTracingAsync(this)) {
    605     // Set to false now, since it turns out we never were the trace subscriber.
    606     OnEndTracingComplete();
    607   }
    608 }
    609 
    610 void GpuMessageHandler::OnEndTracingComplete() {
    611   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    612   trace_enabled_ = false;
    613   web_ui_->CallJavascriptFunction("tracingController.onEndTracingComplete");
    614 }
    615 
    616 void GpuMessageHandler::OnTraceDataCollected(const std::string& json_events) {
    617   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    618   std::wstring javascript;
    619   javascript += L"tracingController.onTraceDataCollected(";
    620   javascript += UTF8ToWide(json_events);
    621   javascript += L");";
    622 
    623   web_ui_->GetRenderViewHost()->ExecuteJavascriptInWebFrame(string16(),
    624       WideToUTF16Hack(javascript));
    625 }
    626 
    627 void GpuMessageHandler::OnTraceBufferPercentFullReply(float percent_full) {
    628   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    629   web_ui_->CallJavascriptFunction(
    630       "tracingController.onRequestBufferPercentFullComplete",
    631       *scoped_ptr<Value>(Value::CreateDoubleValue(percent_full)));
    632 }
    633 
    634 }  // namespace
    635 
    636 
    637 ////////////////////////////////////////////////////////////////////////////////
    638 //
    639 // GpuInternalsUI
    640 //
    641 ////////////////////////////////////////////////////////////////////////////////
    642 
    643 GpuInternalsUI::GpuInternalsUI(TabContents* contents) : WebUI(contents) {
    644   AddMessageHandler((new GpuMessageHandler())->Attach(this));
    645 
    646   GpuHTMLSource* html_source = new GpuHTMLSource();
    647 
    648   // Set up the chrome://gpu/ source.
    649   contents->profile()->GetChromeURLDataManager()->AddDataSource(html_source);
    650 }
    651