// will NOT work
driver.defaultSession.sendCommand('Security.enable').then(_ =>{
driver.defaultSession.on('Security.securityStateChanged', state =>{/* ... */});})// WILL work! happy happy. :)
driver.defaultSession.on('Security.securityStateChanged', state =>{/* ... */});// event binding is synchronous
driver.defaultSession.sendCommand('Security.enable');
1.
2.
3.
4.
5.
6.
7.
8.
调试协议:阅读更好地调试协议Better debugging of the Protocol[4]。
{'pid':41904,// process ID
'tid':1295,// thread ID
'ts':1676836141,//timestampin microseconds
'ph':'X',// trace event type
'cat':'toplevel',// trace category from which this event came
'name':'MessageLoop::RunTask',// relatively human-readable description of the trace event
'dur':64,// duration of the task in microseconds
'args':{},// contains additional data such as frame when applicable
}
{
processEvents:[/* all trace events in the main process */],
mainThreadEvents:[/* all trace events on the main thread */],
timings:{
timeOrigin:0,// timeOrigin is always 0
msfirstContentfulPaint:150,// firstContentfulPaint timein ms after time origin
/* other key moments */
traceEnd:16420,// traceEnd timein ms after time origin
},
timestamps:{
timeOrigin:623000000,// timeOrigin timestampin microseconds, marks the start of the navigation of interest
firstContentfulPaint:623150000,// firstContentfulPaint timestampin microseconds
/* other key moments */
traceEnd:639420000,// traceEnd timestampin microseconds
},}
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
实现
Connecting to browser
Resetting state with about:blank
Navigate to about:blank
Benchmarking machine
Initializing…
Preparing target for navigation mode
Running defaultPass pass
Resetting state with about:blank
Navigate to about:blank
Preparing target for navigation
Cleaning origin data
Cleaning browser cache
Preparing network conditions
Beginning devtoolsLog and trace
Loading page & waiting for onload
Navigating to https:XXX
Gathering in-page: XXXXXXXX. (xN)
Gathering trace
Gathering devtoolsLog & network records
Gathering XXX (xN)
begin();|
→ runLighthouse();|
→ legacyNavigation();
async function legacyNavigation(url, flags ={}, configJSON, userConnection){//...
const connection = userConnection || new CriConnection(flags.port, flags.hostname);
const artifacts = await Runner.gather(()=>{
const requestedUrl = UrlUtils.normalizeUrl(url);
return Runner._gatherArtifactsFromBrowser(requestedUrl, options, connection);}, options);
return Runner.audit(artifacts, options);}
static async _gatherArtifactsFromBrowser(requestedUrl, runnerOpts, connection){//创建connection的Driver
const driver = runnerOpts.driverMock|| new Driver(connection);
const gatherOpts ={
driver,
requestedUrl,
settings: runnerOpts.config.settings,
computedCache: runnerOpts.computedCache,};
const artifacts = await GatherRunner.run(runnerOpts.config.passes, gatherOpts);
return artifacts;}/****** GatherRunner ****/
static async run(passConfigs, options){//1.Connecting to browser
//通过 Websocket 建立连接, 基于 Chrome Debugging Protocol 通信
// CDPSession 实例用于与 Chrome Devtools 协议的原生通信
await driver.connect();// 在 devtools/extension 案例中,我们在尝试清除状态时仍不能在站点上
// 所以我们首先导航到 about:blank,然后应用我们的仿真和设置
//2.Resetting state with about:blank &3.Navigating to blankPage
await GatherRunner.loadBlank(driver);//4. Benchmarking machine
const baseArtifacts = await GatherRunner.initializeBaseArtifacts(options);// ...processing benchmarkIndex
//5. Initializing…
await GatherRunner.setupDriver(driver, options);
let isFirstPass =true;// each pass
for (const passConfig of passConfigs){
const passContext ={
gatherMode:'navigation',
driver,
url: options.requestedUrl,
settings: options.settings,
passConfig,
baseArtifacts,
computedCache: options.computedCache,
LighthouseRunWarnings: baseArtifacts.LighthouseRunWarnings,};//Starting from about:blank, load the page and run gatherers for this pass.
const passResults = await GatherRunner.runPass(passContext);
Object.assign(artifacts, passResults.artifacts);// If we encountered a pageLoadError, don't try to keep loading the page in future passes. if (passResults.pageLoadError && passConfig.loadFailureMode === 'fatal') { baseArtifacts.PageLoadError = passResults.pageLoadError; break; } if (isFirstPass) { await GatherRunner.populateBaseArtifacts(passContext); isFirstPass = false; } } await GatherRunner.disposeDriver(driver, options); return finalizeArtifacts(baseArtifacts, artifacts); } catch (err) { // Clean up on error. Don't await so that the root error,not a disposal error,is shown.
GatherRunner.disposeDriver(driver, options);
throw err;}}
_connectToSocket(response){
const url = response.webSocketDebuggerUrl;
this._pageId= response.id;
return new Promise((resolve, reject)=>{
const ws = new WebSocket(url,{
perMessageDeflate:false,});
ws.on('open',()=>{
this._ws= ws;
resolve();});
ws.on('message', data => this.handleRawMessage(/** @type {string} */(data)));
ws.on('close', this.dispose.bind(this));
ws.on('error', reject);});}
static async setupDriver(driver, options){//...
await GatherRunner.assertNoSameOriginServiceWorkerClients(session, options.requestedUrl);//6. Preparing target for navigation mode,通过为全局 API 或错误处理启用协议域、仿真和新文档处理程序,准备在导航模式下分析的目标。
await prepare.prepareTargetForNavigationMode(driver, options.settings);}
static async runPass(passContext){//7. Running defaultPass pass
const gathererResults ={};
const {driver, passConfig}= passContext;//8.Resetting state with about:blank 9.Navigating to about:blankGo to about:blank
//set up
await GatherRunner.loadBlank(driver, passConfig.blankPage);//10.Preparing target for navigation ~13.Preparing network conditions
const {warnings}= await prepare.prepareTargetForIndividualNavigation(
driver.defaultSession,
passContext.settings,{
requestor: passContext.url,
disableStorageReset:!passConfig.useThrottling,
disableThrottling:!passConfig.useThrottling,
blockedUrlPatterns: passConfig.blockedUrlPatterns,});// run `startInstrumentation()/beforePass()` on gatherers.
passContext.LighthouseRunWarnings.push(...warnings);
await GatherRunner.beforePass(passContext, gathererResults);//14.Beginning devtoolsLog and trace,// await driver.beginDevtoolsLog(); await driver.beginTrace(settings);
await GatherRunner.beginRecording(passContext);//15.Loading page & waiting for onload ,16.Navigating to https:XXX
const {navigationError: possibleNavError}= await GatherRunner.loadPage(driver, passContext);//17.Gatheringin-page: XXXXXXXX,run `pass()` on gatherers.
await GatherRunner.pass(passContext, gathererResults);
const loadData = await GatherRunner.endRecording(passContext);//18.Gathering trace 19.Gathering devtoolsLog & network records
await emulation.clearThrottling(driver.defaultSession);//process page error
// If no error, save devtoolsLog and trace.
GatherRunner._addLoadDataToBaseArtifacts(passContext, loadData, passConfig.passName);//20.Gathering XXX. Run `afterPass()(stopInstrumentation -> getArtifact )` on gatherers and return collected artifacts.
await GatherRunner.afterPass(passContext, loadData, gathererResults);
const artifacts = GatherRunner.collectArtifacts(gathererResults);
return artifacts;}