接前一篇文章:Chromium源码由浅入深(二)
上一回说到了关键的“钥匙”:browserBridge.gpuInfo,本文就针对其进行深入探究。
先来看前半部分,browserBridge。
在content/browser/resources/gpu/gpu_internals.js中有以下代码:
- import './info_view.js';
-
- import {BrowserBridge} from './browser_bridge.js';
-
- // Injected script from C++ or test environments may reference `browserBridge`
- // as a property of the global object.
- window.browserBridge = new BrowserBridge();
-
- /**
- * Main entry point. called once the page has loaded.
- */
- function onLoad() {
- // Create the views.
- document.querySelector('info-view')
- .addBrowserBridgeListeners(window.browserBridge);
-
- // Because of inherent raciness (between the deprecated DevTools API which
- // telemtry uses to drive the relevant tests, and the asynchronous loading of
- // JS modules like this one) it's possible for telemetry tests to inject code
- // *before* `browserBridge` is set and the DOM is populated. This flag is used
- // to synchronize script injection by tests to prevent such races.
- window.gpuPagePopulated = true;
- }
-
- document.addEventListener('DOMContentLoaded', onLoad);
代码注释说得清楚:从C++或测试环境注入的脚本可能引用“browserBridge”作为全局对象的属性。
BrowserBridge类的定义在Cromium源码中一共有两处,一处在chrome/browser/resources/net_internals/browser_bridge.js中,另一处在content/browser/resources/gpu/browser_bridge.js中。由于上边代码中是“import {BrowserBridge} from './browser_bridge.js';”,也就是引用的是当前路径下的browser_bridge.js,即content/browser/resources/gpu/下的browser_bridge.js,也就是第二处定义。
代码如下:
- /**
- * 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.
- */
- export class BrowserBridge extends EventTarget {
- constructor() {
- super();
- this.nextRequestId_ = 0;
- this.pendingCallbacks_ = [];
- this.logMessages_ = [];
-
- // Tell c++ code that we are ready to receive GPU Info.
- chrome.send('browserBridgeInitialized');
- this.beginRequestClientInfo_();
- this.beginRequestLogMessages_();
- }
-
- dispatchEvent_(eventName) {
- this.dispatchEvent(
- new CustomEvent(eventName, {bubbles: true, composed: true}));
- }
-
- applySimulatedData_(data) {
- // set up things according to the simulated data
- this.gpuInfo_ = data.gpuInfo;
- this.clientInfo_ = data.clientInfo;
- this.logMessages_ = data.logMessages;
- this.dispatchEvent_('gpuInfoUpdate');
- this.dispatchEvent_('clientInfoChange');
- this.dispatchEvent_('logMessagesChange');
- }
-
- /**
- * Sends a message to the browser with specified args. The
- * browser will reply asynchronously via the provided callback.
- */
- callAsync(submessage, args, callback) {
- const requestId = this.nextRequestId_;
- this.nextRequestId_ += 1;
- this.pendingCallbacks_[requestId] = callback;
- if (!args) {
- chrome.send('callAsync', [requestId.toString(), submessage]);
- } else {
- const allArgs = [requestId.toString(), submessage].concat(args);
- chrome.send('callAsync', allArgs);
- }
- }
-
- /**
- * Called by gpu c++ code when client info is ready.
- */
- onCallAsyncReply(requestId, args) {
- if (this.pendingCallbacks_[requestId] === undefined) {
- throw new Error('requestId ' + requestId + ' is not pending');
- }
- const 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(gpuInfo) {
- this.gpuInfo_ = gpuInfo;
- this.dispatchEvent_('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_() {
- 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;
- this.dispatchEvent_('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_() {
- this.callAsync(
- 'requestLogMessages', undefined,
- (function(messages) {
- if (messages.length !== this.logMessages_.length) {
- this.logMessages_ = messages;
- this.dispatchEvent_('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() {
- for (const info of this.gpuInfo_.basicInfo) {
- if (info.description === 'Sandboxed') {
- return info.value;
- }
- }
- return false;
- }
- }
再来看后半部分,gpuInfo。
在上边content/browser/resources/gpu/gpu_internals.js的代码中有以下一段:
- /**
- * Main entry point. called once the page has loaded.
- */
- function onLoad() {
- // Create the views.
- document.querySelector('info-view')
- .addBrowserBridgeListeners(window.browserBridge);
addBrowserBridgeListeners函数在content/browser/resources/gpu/info_view.js中,代码如下:
- addBrowserBridgeListeners(browserBridge) {
- browserBridge.addEventListener(
- 'gpuInfoUpdate', this.refresh.bind(this, browserBridge));
- browserBridge.addEventListener(
- 'logMessagesChange', this.refresh.bind(this, browserBridge));
- browserBridge.addEventListener(
- 'clientInfoChange', this.refresh.bind(this, browserBridge));
- this.refresh(browserBridge);
- }
而'gpuInfoUpdate'这个关键字则有两处相关调用。一处在content/browser/resources/gpu/browser_bridge.js中,代码如下:
- applySimulatedData_(data) {
- // set up things according to the simulated data
- this.gpuInfo_ = data.gpuInfo;
- this.clientInfo_ = data.clientInfo;
- this.logMessages_ = data.logMessages;
- this.dispatchEvent_('gpuInfoUpdate');
- this.dispatchEvent_('clientInfoChange');
- this.dispatchEvent_('logMessagesChange');
- }
另一处也是在同文件中,代码如下:
- /**
- * Called from gpu c++ code when GPU Info is updated.
- */
- onGpuInfoUpdate(gpuInfo) {
- this.gpuInfo_ = gpuInfo;
- this.dispatchEvent_('gpuInfoUpdate');
- }
显然后者更为关键和常用。再次查找调用onGpuInfoUpdate函数的地方,只有一处,在content/browser/gpu/gpu_internals_ui.cc中,代码如下:
- void GpuMessageHandler::OnGpuInfoUpdate() {
- // Get GPU Info.
- const gpu::GPUInfo gpu_info = GpuDataManagerImpl::GetInstance()->GetGPUInfo();
- const gfx::GpuExtraInfo gpu_extra_info =
- GpuDataManagerImpl::GetInstance()->GetGpuExtraInfo();
- base::Value::Dict gpu_info_val = GetGpuInfo();
-
- // Add in blocklisting features
- base::Value::Dict feature_status;
- feature_status.Set("featureStatus", GetFeatureStatus());
- feature_status.Set("problems", GetProblems());
- base::Value::List workarounds;
- for (const auto& workaround : GetDriverBugWorkarounds())
- workarounds.Append(workaround);
- feature_status.Set("workarounds", std::move(workarounds));
- gpu_info_val.Set("featureStatus", std::move(feature_status));
- if (!GpuDataManagerImpl::GetInstance()->IsGpuProcessUsingHardwareGpu()) {
- const gpu::GPUInfo gpu_info_for_hardware_gpu =
- GpuDataManagerImpl::GetInstance()->GetGPUInfoForHardwareGpu();
- if (gpu_info_for_hardware_gpu.IsInitialized()) {
- base::Value::Dict feature_status_for_hardware_gpu;
- feature_status_for_hardware_gpu.Set("featureStatus",
- GetFeatureStatusForHardwareGpu());
- feature_status_for_hardware_gpu.Set("problems",
- GetProblemsForHardwareGpu());
- base::Value::List workarounds_for_hardware_gpu;
- for (const auto& workaround : GetDriverBugWorkaroundsForHardwareGpu())
- workarounds_for_hardware_gpu.Append(workaround);
- feature_status_for_hardware_gpu.Set(
- "workarounds", std::move(workarounds_for_hardware_gpu));
- gpu_info_val.Set("featureStatusForHardwareGpu",
- std::move(feature_status_for_hardware_gpu));
- const gpu::GpuFeatureInfo gpu_feature_info_for_hardware_gpu =
- GpuDataManagerImpl::GetInstance()->GetGpuFeatureInfoForHardwareGpu();
- base::Value::List gpu_info_for_hardware_gpu_val = GetBasicGpuInfo(
- gpu_info_for_hardware_gpu, gpu_feature_info_for_hardware_gpu,
- gfx::GpuExtraInfo{});
- gpu_info_val.Set("basicInfoForHardwareGpu",
- std::move(gpu_info_for_hardware_gpu_val));
- }
- }
- gpu_info_val.Set("compositorInfo", CompositorInfo());
- gpu_info_val.Set("gpuMemoryBufferInfo", GpuMemoryBufferInfo(gpu_extra_info));
- gpu_info_val.Set("displayInfo", GetDisplayInfo());
- gpu_info_val.Set("videoAcceleratorsInfo", GetVideoAcceleratorsInfo());
- gpu_info_val.Set("ANGLEFeatures", GetANGLEFeatures());
- gpu_info_val.Set("devicePerfInfo", GetDevicePerfInfo());
- gpu_info_val.Set("dawnInfo", GetDawnInfo());
-
- // Send GPU Info to javascript.
- web_ui()->CallJavascriptFunctionUnsafe("browserBridge.onGpuInfoUpdate",
- std::move(gpu_info_val));
- }
这个函数就是接下来要花大力气探究的核心函数。
欲知后事如何,且看下回分解。