// Copyright (c) 2011 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. // // Copyright (c) 2011 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. cr.define('gpu', function() { /** * This class provides a 'bridge' for communicating between javascript and the * browser. When run outside of WebUI, e.g. as a regular webpage, it provides * synthetic data to assist in testing. * @constructor */ function BrowserBridge() { // If we are not running inside WebUI, output chrome.send messages // to the console to help with quick-iteration debugging. this.debugMode_ = (chrome.send === undefined && console.log); if (this.debugMode_) { var browserBridgeTests = document.createElement('script'); browserBridgeTests.src = 'browser_bridge_tests.js'; document.body.appendChild(browserBridgeTests); } this.nextRequestId_ = 0; this.pendingCallbacks_ = []; this.logMessages_ = []; // Tell c++ code that we are ready to receive GPU Info. if (!this.debugMode_) { chrome.send('browserBridgeInitialized'); this.beginRequestClientInfo_(); this.beginRequestLogMessages_(); } } BrowserBridge.prototype = { __proto__: cr.EventTarget.prototype, applySimulatedData_: function applySimulatedData(data) { // set up things according to the simulated data this.gpuInfo_ = data.gpuInfo; this.clientInfo_ = data.clientInfo; this.logMessages_ = data.logMessages; cr.dispatchSimpleEvent(this, 'gpuInfoUpdate'); cr.dispatchSimpleEvent(this, 'clientInfoChange'); cr.dispatchSimpleEvent(this, 'logMessagesChange'); }, /** * Returns true if the page is hosted inside Chrome WebUI * Helps have behavior conditional to emulate_webui.py */ get debugMode() { return this.debugMode_; }, /** * Sends a message to the browser with specified args. The * browser will reply asynchronously via the provided callback. */ callAsync: function(submessage, args, callback) { var requestId = this.nextRequestId_; this.nextRequestId_ += 1; this.pendingCallbacks_[requestId] = callback; if (!args) { chrome.send('callAsync', [requestId.toString(), submessage]); } else { var allArgs = [requestId.toString(), submessage].concat(args); chrome.send('callAsync', allArgs); } }, /** * Called by gpu c++ code when client info is ready. */ onCallAsyncReply: function(requestId, args) { if (this.pendingCallbacks_[requestId] === undefined) { throw new Error('requestId ' + requestId + ' is not pending'); } var callback = this.pendingCallbacks_[requestId]; callback(args); delete this.pendingCallbacks_[requestId]; }, /** * Get gpuInfo data. */ get gpuInfo() { return this.gpuInfo_; }, /** * Called from gpu c++ code when GPU Info is updated. */ onGpuInfoUpdate: function(gpuInfo) { this.gpuInfo_ = gpuInfo; cr.dispatchSimpleEvent(this, 'gpuInfoUpdate'); }, /** * This function begins a request for the ClientInfo. If it comes back * as undefined, then we will issue the request again in 250ms. */ beginRequestClientInfo_: function() { this.callAsync('requestClientInfo', undefined, (function(data) { if (data === undefined) { // try again in 250 ms window.setTimeout(this.beginRequestClientInfo_.bind(this), 250); } else { this.clientInfo_ = data; cr.dispatchSimpleEvent(this, 'clientInfoChange'); } }).bind(this)); }, /** * Returns information about the currently running Chrome build. */ get clientInfo() { return this.clientInfo_; }, /** * This function checks for new GPU_LOG messages. * If any are found, a refresh is triggered. */ beginRequestLogMessages_: function() { this.callAsync('requestLogMessages', undefined, (function(messages) { if (messages.length != this.logMessages_.length) { this.logMessages_ = messages; cr.dispatchSimpleEvent(this, 'logMessagesChange'); } // check again in 250 ms window.setTimeout(this.beginRequestLogMessages_.bind(this), 250); }).bind(this)); }, /** * Returns an array of log messages issued by the GPU process, if any. */ get logMessages() { return this.logMessages_; }, /** * Returns the value of the "Sandboxed" row. */ isSandboxedForTesting : function() { for (i = 0; i < this.gpuInfo_.basic_info.length; ++i) { var info = this.gpuInfo_.basic_info[i]; if (info.description == "Sandboxed") return info.value; } return false; } }; return { BrowserBridge: BrowserBridge }; }); // // 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. /** * @fileoverview This view displays information on the current GPU * hardware. Its primary usefulness is to allow users to copy-paste * their data in an easy to read format for bug reports. */ cr.define('gpu', function() { /** * Provides information on the GPU process and underlying graphics hardware. * @constructor * @extends {cr.ui.TabPanel} */ var InfoView = cr.ui.define(cr.ui.TabPanel); InfoView.prototype = { __proto__: cr.ui.TabPanel.prototype, decorate: function() { cr.ui.TabPanel.prototype.decorate.apply(this); browserBridge.addEventListener('gpuInfoUpdate', this.refresh.bind(this)); browserBridge.addEventListener('logMessagesChange', this.refresh.bind(this)); browserBridge.addEventListener('clientInfoChange', this.refresh.bind(this)); this.refresh(); }, /** * Updates the view based on its currently known data */ refresh: function(data) { // Client info if (browserBridge.clientInfo) { var clientInfo = browserBridge.clientInfo; this.setTable_('client-info', [ { description: 'Data exported', value: (new Date()).toLocaleString() }, { description: 'Chrome version', value: clientInfo.version }, { description: 'Operating system', value: clientInfo.operating_system }, { description: 'Software rendering list version', value: clientInfo.blacklist_version }, { description: 'Driver bug list version', value: clientInfo.driver_bug_list_version }, { description: 'ANGLE commit id', value: clientInfo.angle_commit_id }, { description: '2D graphics backend', value: clientInfo.graphics_backend }, { description: 'Command Line', value: clientInfo.command_line }]); } else { this.setText_('client-info', '... loading...'); } // Feature map var featureLabelMap = { '2d_canvas': 'Canvas', 'gpu_compositing': 'Compositing', 'webgl': 'WebGL', 'multisampling': 'WebGL multisampling', 'flash_3d': 'Flash', 'flash_stage3d': 'Flash Stage3D', 'flash_stage3d_baseline': 'Flash Stage3D Baseline profile', 'texture_sharing': 'Texture Sharing', 'video_decode': 'Video Decode', 'video_encode': 'Video Encode', 'panel_fitting': 'Panel Fitting', 'rasterization': 'Rasterization', 'multiple_raster_threads': 'Multiple Raster Threads', 'native_gpu_memory_buffers': 'Native GpuMemoryBuffers', 'vpx_decode': 'VPx Video Decode', 'webgl2': 'WebGL2', 'checker_imaging': 'CheckerImaging', }; var statusMap = { 'disabled_software': { 'label': 'Software only. Hardware acceleration disabled', 'class': 'feature-yellow' }, 'disabled_off': { 'label': 'Disabled', 'class': 'feature-red' }, 'disabled_off_ok': { 'label': 'Disabled', 'class': 'feature-yellow' }, 'unavailable_software': { 'label': 'Software only, hardware acceleration unavailable', 'class': 'feature-yellow' }, 'unavailable_off': { 'label': 'Unavailable', 'class': 'feature-red' }, 'unavailable_off_ok': { 'label': 'Unavailable', 'class': 'feature-yellow' }, 'enabled_readback': { 'label': 'Hardware accelerated but at reduced performance', 'class': 'feature-yellow' }, 'enabled_force': { 'label': 'Hardware accelerated on all pages', 'class': 'feature-green' }, 'enabled': { 'label': 'Hardware accelerated', 'class': 'feature-green' }, 'enabled_on': { 'label': 'Enabled', 'class': 'feature-green' }, 'enabled_force_on': { 'label': 'Force enabled', 'class': 'feature-green' }, }; // GPU info, basic var diagnosticsDiv = this.querySelector('.diagnostics'); var diagnosticsLoadingDiv = this.querySelector('.diagnostics-loading'); var featureStatusList = this.querySelector('.feature-status-list'); var problemsDiv = this.querySelector('.problems-div'); var problemsList = this.querySelector('.problems-list'); var workaroundsDiv = this.querySelector('.workarounds-div'); var workaroundsList = this.querySelector('.workarounds-list'); var gpuInfo = browserBridge.gpuInfo; var i; if (gpuInfo) { // Not using jstemplate here for blacklist status because we construct // href from data, which jstemplate can't seem to do. if (gpuInfo.featureStatus) { // feature status list featureStatusList.textContent = ''; for (var featureName in gpuInfo.featureStatus.featureStatus) { var featureStatus = gpuInfo.featureStatus.featureStatus[featureName]; var featureEl = document.createElement('li'); var nameEl = document.createElement('span'); if (!featureLabelMap[featureName]) console.log('Missing featureLabel for', featureName); nameEl.textContent = featureLabelMap[featureName] + ': '; featureEl.appendChild(nameEl); var statusEl = document.createElement('span'); var statusInfo = statusMap[featureStatus]; if (!statusInfo) { console.log('Missing status for ', featureStatus); statusEl.textContent = 'Unknown'; statusEl.className = 'feature-red'; } else { statusEl.textContent = statusInfo['label']; statusEl.className = statusInfo['class']; } featureEl.appendChild(statusEl); featureStatusList.appendChild(featureEl); } // problems list if (gpuInfo.featureStatus.problems.length) { problemsDiv.hidden = false; problemsList.textContent = ''; for (i = 0; i < gpuInfo.featureStatus.problems.length; i++) { var problem = gpuInfo.featureStatus.problems[i]; var problemEl = this.createProblemEl_(problem); problemsList.appendChild(problemEl); } } else { problemsDiv.hidden = true; } // driver bug workarounds list if (gpuInfo.featureStatus.workarounds.length) { workaroundsDiv.hidden = false; workaroundsList.textContent = ''; for (i = 0; i < gpuInfo.featureStatus.workarounds.length; i++) { var workaroundEl = document.createElement('li'); workaroundEl.textContent = gpuInfo.featureStatus.workarounds[i]; workaroundsList.appendChild(workaroundEl); } } else { workaroundsDiv.hidden = true; } } else { featureStatusList.textContent = ''; problemsList.hidden = true; workaroundsList.hidden = true; } if (gpuInfo.basic_info) this.setTable_('basic-info', gpuInfo.basic_info); else this.setTable_('basic-info', []); if (gpuInfo.compositorInfo) this.setTable_('compositor-info', gpuInfo.compositorInfo); else this.setTable_('compositor-info', []); if (gpuInfo.gpuMemoryBufferInfo) this.setTable_('gpu-memory-buffer-info', gpuInfo.gpuMemoryBufferInfo); else this.setTable_('gpu-memory-buffer-info', []); if (gpuInfo.diagnostics) { diagnosticsDiv.hidden = false; diagnosticsLoadingDiv.hidden = true; $('diagnostics-table').hidden = false; this.setTable_('diagnostics-table', gpuInfo.diagnostics); } else if (gpuInfo.diagnostics === null) { // gpu_internals.cc sets diagnostics to null when it is being loaded diagnosticsDiv.hidden = false; diagnosticsLoadingDiv.hidden = false; $('diagnostics-table').hidden = true; } else { diagnosticsDiv.hidden = true; } } else { this.setText_('basic-info', '... loading ...'); diagnosticsDiv.hidden = true; featureStatusList.textContent = ''; problemsDiv.hidden = true; } // Log messages jstProcess(new JsEvalContext({values: browserBridge.logMessages}), $('log-messages')); }, createProblemEl_: function(problem) { var problemEl; problemEl = document.createElement('li'); // Description of issue var desc = document.createElement('a'); desc.textContent = problem.description; problemEl.appendChild(desc); // Spacing ':' element if (problem.crBugs.length > 0) { var tmp = document.createElement('span'); tmp.textContent = ': '; problemEl.appendChild(tmp); } var nbugs = 0; var j; // crBugs for (j = 0; j < problem.crBugs.length; ++j) { if (nbugs > 0) { var tmp = document.createElement('span'); tmp.textContent = ', '; problemEl.appendChild(tmp); } var link = document.createElement('a'); var bugid = parseInt(problem.crBugs[j]); link.textContent = bugid; link.href = 'http://crbug.com/' + bugid; problemEl.appendChild(link); nbugs++; } if (problem.affectedGpuSettings.length > 0) { var brNode = document.createElement('br'); problemEl.appendChild(brNode); var iNode = document.createElement('i'); problemEl.appendChild(iNode); var headNode = document.createElement('span'); if (problem.tag == 'disabledFeatures') headNode.textContent = 'Disabled Features: '; else // problem.tag == 'workarounds' headNode.textContent = 'Applied Workarounds: '; iNode.appendChild(headNode); for (j = 0; j < problem.affectedGpuSettings.length; ++j) { if (j > 0) { var separateNode = document.createElement('span'); separateNode.textContent = ', '; iNode.appendChild(separateNode); } var nameNode = document.createElement('span'); if (problem.tag == 'disabledFeatures') nameNode.classList.add('feature-red'); else // problem.tag == 'workarounds' nameNode.classList.add('feature-yellow'); nameNode.textContent = problem.affectedGpuSettings[j]; iNode.appendChild(nameNode); } } return problemEl; }, setText_: function(outputElementId, text) { var peg = document.getElementById(outputElementId); peg.textContent = text; }, setTable_: function(outputElementId, inputData) { var template = jstGetTemplate('info-view-table-template'); jstProcess(new JsEvalContext({value: inputData}), template); var peg = document.getElementById(outputElementId); if (!peg) throw new Error('Node ' + outputElementId + ' not found'); peg.innerHTML = ''; peg.appendChild(template); } }; return { InfoView: InfoView }; }); var browserBridge; /** * Main entry point. called once the page has loaded. */ function onLoad() { browserBridge = new gpu.BrowserBridge(); // Create the views. cr.ui.decorate('#info-view', gpu.InfoView); } document.addEventListener('DOMContentLoaded', onLoad);