blob: 8fb3657bf54dfc3d48119586f5779cfa41937146 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/webui/gpu_internals_ui.h"
#include <string>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/command_line.h"
#include "base/i18n/time_formatting.h"
#include "base/string_number_conversions.h"
#include "base/stringprintf.h"
#include "base/sys_info.h"
#include "base/values.h"
#include "chrome/browser/crash_upload_list.h"
#include "chrome/browser/gpu_blacklist.h"
#include "chrome/browser/gpu_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/webui/chrome_web_ui_data_source.h"
#include "chrome/browser/ui/webui/crashes_ui.h"
#include "chrome/common/chrome_version_info.h"
#include "chrome/common/url_constants.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/gpu_data_manager.h"
#include "content/public/browser/gpu_data_manager_observer.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui.h"
#include "content/public/browser/web_ui_message_handler.h"
#include "grit/browser_resources.h"
#include "grit/generated_resources.h"
#include "third_party/angle/src/common/version.h"
#include "ui/base/l10n/l10n_util.h"
using content::BrowserThread;
using content::GpuDataManager;
using content::WebContents;
using content::WebUIMessageHandler;
namespace {
ChromeWebUIDataSource* CreateGpuHTMLSource() {
ChromeWebUIDataSource* source =
new ChromeWebUIDataSource(chrome::kChromeUIGpuInternalsHost);
source->set_json_path("strings.js");
source->add_resource_path("gpu_internals.js", IDR_GPU_INTERNALS_JS);
source->set_default_resource(IDR_GPU_INTERNALS_HTML);
return source;
}
// This class receives javascript messages from the renderer.
// Note that the WebUI infrastructure runs on the UI thread, therefore all of
// this class's methods are expected to run on the UI thread.
class GpuMessageHandler
: public WebUIMessageHandler,
public base::SupportsWeakPtr<GpuMessageHandler>,
public content::GpuDataManagerObserver,
public CrashUploadList::Delegate {
public:
GpuMessageHandler();
virtual ~GpuMessageHandler();
// WebUIMessageHandler implementation.
virtual void RegisterMessages() OVERRIDE;
// GpuDataManagerObserver implementation.
virtual void OnGpuInfoUpdate() OVERRIDE;
// CrashUploadList::Delegate implemenation.
virtual void OnCrashListAvailable() OVERRIDE;
// Messages
void OnBrowserBridgeInitialized(const ListValue* list);
void OnCallAsync(const ListValue* list);
// Submessages dispatched from OnCallAsync
Value* OnRequestClientInfo(const ListValue* list);
Value* OnRequestLogMessages(const ListValue* list);
Value* OnRequestCrashList(const ListValue* list);
// Executes the javascript function |function_name| in the renderer, passing
// it the argument |value|.
void CallJavascriptFunction(const std::wstring& function_name,
const Value* value);
private:
scoped_refptr<CrashUploadList> crash_list_;
bool crash_list_available_;
DISALLOW_COPY_AND_ASSIGN(GpuMessageHandler);
};
////////////////////////////////////////////////////////////////////////////////
//
// GpuMessageHandler
//
////////////////////////////////////////////////////////////////////////////////
GpuMessageHandler::GpuMessageHandler()
: crash_list_available_(false) {
crash_list_ = CrashUploadList::Create(this);
}
GpuMessageHandler::~GpuMessageHandler() {
GpuDataManager::GetInstance()->RemoveObserver(this);
crash_list_->ClearDelegate();
}
/* BrowserBridge.callAsync prepends a requestID to these messages. */
void GpuMessageHandler::RegisterMessages() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
crash_list_->LoadCrashListAsynchronously();
web_ui()->RegisterMessageCallback("browserBridgeInitialized",
base::Bind(&GpuMessageHandler::OnBrowserBridgeInitialized,
base::Unretained(this)));
web_ui()->RegisterMessageCallback("callAsync",
base::Bind(&GpuMessageHandler::OnCallAsync,
base::Unretained(this)));
}
void GpuMessageHandler::OnCallAsync(const ListValue* args) {
DCHECK_GE(args->GetSize(), static_cast<size_t>(2));
// unpack args into requestId, submessage and submessageArgs
bool ok;
Value* requestId;
ok = args->Get(0, &requestId);
DCHECK(ok);
std::string submessage;
ok = args->GetString(1, &submessage);
DCHECK(ok);
ListValue* submessageArgs = new ListValue();
for (size_t i = 2; i < args->GetSize(); ++i) {
Value* arg;
ok = args->Get(i, &arg);
DCHECK(ok);
Value* argCopy = arg->DeepCopy();
submessageArgs->Append(argCopy);
}
// call the submessage handler
Value* ret = NULL;
if (submessage == "requestClientInfo") {
ret = OnRequestClientInfo(submessageArgs);
} else if (submessage == "requestLogMessages") {
ret = OnRequestLogMessages(submessageArgs);
} else if (submessage == "requestCrashList") {
ret = OnRequestCrashList(submessageArgs);
} else { // unrecognized submessage
NOTREACHED();
delete submessageArgs;
return;
}
delete submessageArgs;
// call BrowserBridge.onCallAsyncReply with result
if (ret) {
web_ui()->CallJavascriptFunction("browserBridge.onCallAsyncReply",
*requestId,
*ret);
delete ret;
} else {
web_ui()->CallJavascriptFunction("browserBridge.onCallAsyncReply",
*requestId);
}
}
void GpuMessageHandler::OnBrowserBridgeInitialized(const ListValue* args) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
// Watch for changes in GPUInfo
GpuDataManager::GetInstance()->AddObserver(this);
// Tell GpuDataManager it should have full GpuInfo. If the
// Gpu process has not run yet, this will trigger its launch.
GpuDataManager::GetInstance()->RequestCompleteGpuInfoIfNeeded();
// Run callback immediately in case the info is ready and no update in the
// future.
OnGpuInfoUpdate();
}
Value* GpuMessageHandler::OnRequestClientInfo(const ListValue* list) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DictionaryValue* dict = new DictionaryValue();
chrome::VersionInfo version_info;
if (!version_info.is_valid()) {
DLOG(ERROR) << "Unable to create chrome::VersionInfo";
} else {
// We have everything we need to send the right values.
dict->SetString("version", version_info.Version());
dict->SetString("cl", version_info.LastChange());
dict->SetString("version_mod",
chrome::VersionInfo::GetVersionStringModifier());
dict->SetString("official",
l10n_util::GetStringUTF16(
version_info.IsOfficialBuild() ?
IDS_ABOUT_VERSION_OFFICIAL :
IDS_ABOUT_VERSION_UNOFFICIAL));
dict->SetString("command_line",
CommandLine::ForCurrentProcess()->GetCommandLineString());
}
dict->SetString("operating_system",
base::SysInfo::OperatingSystemName() + " " +
base::SysInfo::OperatingSystemVersion());
dict->SetString("angle_revision", base::UintToString(BUILD_REVISION));
#if defined(USE_SKIA)
dict->SetString("graphics_backend", "Skia");
#else
dict->SetString("graphics_backend", "Core Graphics");
#endif
dict->SetString("blacklist_version",
GpuBlacklist::GetInstance()->GetVersion());
return dict;
}
Value* GpuMessageHandler::OnRequestLogMessages(const ListValue*) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
return GpuDataManager::GetInstance()->GetLogMessages().DeepCopy();
}
Value* GpuMessageHandler::OnRequestCrashList(const ListValue*) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (!CrashesUI::CrashReportingEnabled()) {
// We need to return an empty list instead of NULL.
return new ListValue;
}
if (!crash_list_available_) {
// If we are still obtaining crash list, then return null so another
// request will be scheduled.
return NULL;
}
ListValue* list_value = new ListValue;
std::vector<CrashUploadList::CrashInfo> crashes;
crash_list_->GetUploadedCrashes(50, &crashes);
for (std::vector<CrashUploadList::CrashInfo>::iterator i = crashes.begin();
i != crashes.end(); ++i) {
DictionaryValue* crash = new DictionaryValue();
crash->SetString("id", i->crash_id);
crash->SetString("time",
base::TimeFormatFriendlyDateAndTime(i->crash_time));
list_value->Append(crash);
}
return list_value;
}
void GpuMessageHandler::OnGpuInfoUpdate() {
// Get GPU Info.
scoped_ptr<base::DictionaryValue> gpu_info_val(
gpu_util::GpuInfoAsDictionaryValue());
// Add in blacklisting features
Value* feature_status = gpu_util::GetFeatureStatus();
if (feature_status)
gpu_info_val->Set("featureStatus", feature_status);
// Send GPU Info to javascript.
web_ui()->CallJavascriptFunction("browserBridge.onGpuInfoUpdate",
*(gpu_info_val.get()));
}
void GpuMessageHandler::OnCrashListAvailable() {
crash_list_available_ = true;
}
} // namespace
////////////////////////////////////////////////////////////////////////////////
//
// GpuInternalsUI
//
////////////////////////////////////////////////////////////////////////////////
GpuInternalsUI::GpuInternalsUI(content::WebUI* web_ui)
: WebUIController(web_ui) {
web_ui->AddMessageHandler(new GpuMessageHandler());
// Set up the chrome://gpu-internals/ source.
Profile* profile = Profile::FromWebUI(web_ui);
profile->GetChromeURLDataManager()->AddDataSource(CreateGpuHTMLSource());
}