145 lines
5.8 KiB
JavaScript
145 lines
5.8 KiB
JavaScript
/**
|
|
* Copyright 2021 Google LLC.
|
|
* Copyright (c) Microsoft Corporation.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
import debug from 'debug';
|
|
import { SimpleTransport } from './SimpleTransport.js';
|
|
const debugInternal = debug('bidi:mapper:internal');
|
|
const debugInfo = debug('bidi:mapper:info');
|
|
const debugOthers = debug('bidi:mapper:debug:others');
|
|
// Memorizes a debug creation
|
|
const loggers = new Map();
|
|
const getLogger = (type) => {
|
|
const prefix = `bidi:mapper:${type}`;
|
|
let logger = loggers.get(prefix);
|
|
if (!logger) {
|
|
logger = debug(prefix);
|
|
loggers.set(prefix, logger);
|
|
}
|
|
return logger;
|
|
};
|
|
export class MapperServerCdpConnection {
|
|
#cdpConnection;
|
|
#bidiSession;
|
|
static async create(cdpConnection, mapperTabSource, verbose) {
|
|
try {
|
|
const bidiSession = await this.#initMapper(cdpConnection, mapperTabSource, verbose);
|
|
return new MapperServerCdpConnection(cdpConnection, bidiSession);
|
|
}
|
|
catch (e) {
|
|
cdpConnection.close();
|
|
throw e;
|
|
}
|
|
}
|
|
constructor(cdpConnection, bidiSession) {
|
|
this.#cdpConnection = cdpConnection;
|
|
this.#bidiSession = bidiSession;
|
|
}
|
|
static async #sendMessage(mapperCdpClient, message) {
|
|
try {
|
|
await mapperCdpClient.sendCommand('Runtime.evaluate', {
|
|
expression: `onBidiMessage(${JSON.stringify(message)})`,
|
|
});
|
|
}
|
|
catch (error) {
|
|
debugInternal('Call to onBidiMessage failed', error);
|
|
}
|
|
}
|
|
close() {
|
|
this.#cdpConnection.close();
|
|
}
|
|
bidiSession() {
|
|
return this.#bidiSession;
|
|
}
|
|
static #onBindingCalled = (params, bidiSession) => {
|
|
if (params.name === 'sendBidiResponse') {
|
|
bidiSession.emit('message', params.payload);
|
|
}
|
|
else if (params.name === 'sendDebugMessage') {
|
|
this.#onDebugMessage(params.payload);
|
|
}
|
|
};
|
|
static #onDebugMessage = (json) => {
|
|
try {
|
|
const log = JSON.parse(json);
|
|
if (log.logType !== undefined && log.messages !== undefined) {
|
|
const logger = getLogger(log.logType);
|
|
logger(log.messages);
|
|
}
|
|
}
|
|
catch {
|
|
// Fall back to raw log in case of unknown
|
|
debugOthers(json);
|
|
}
|
|
};
|
|
static #onConsoleAPICalled = (params) => {
|
|
debugInfo('consoleAPICalled: %s %O', params.type, params.args.map((arg) => arg.value));
|
|
};
|
|
static #onRuntimeExceptionThrown = (params) => {
|
|
debugInfo('exceptionThrown:', params);
|
|
};
|
|
static async #initMapper(cdpConnection, mapperTabSource, verbose) {
|
|
debugInternal('Initializing Mapper.');
|
|
const browserClient = await cdpConnection.createBrowserSession();
|
|
// Run mapper in the first open tab.
|
|
const targets = (await cdpConnection.sendCommand('Target.getTargets', {}));
|
|
const mapperTabTargetId = targets.targetInfos.filter((target) => target.type === 'page')[0].targetId;
|
|
const { sessionId: mapperSessionId } = await browserClient.sendCommand('Target.attachToTarget', { targetId: mapperTabTargetId, flatten: true });
|
|
const mapperCdpClient = cdpConnection.getCdpClient(mapperSessionId);
|
|
// Click on the body to interact with the page in order to "beforeunload" being
|
|
// triggered when the tab is closed.
|
|
await mapperCdpClient.sendCommand('Runtime.evaluate', {
|
|
expression: 'document.body.click()',
|
|
userGesture: true,
|
|
});
|
|
// Create and activate new tab with a blank page.
|
|
await browserClient.sendCommand('Target.createTarget', {
|
|
url: 'about:blank',
|
|
});
|
|
const bidiSession = new SimpleTransport(async (message) => await this.#sendMessage(mapperCdpClient, message));
|
|
// Process responses from the mapper tab.
|
|
mapperCdpClient.on('Runtime.bindingCalled', (params) => this.#onBindingCalled(params, bidiSession));
|
|
// Forward console messages from the mapper tab.
|
|
mapperCdpClient.on('Runtime.consoleAPICalled', this.#onConsoleAPICalled);
|
|
// Catch unhandled exceptions in the mapper.
|
|
mapperCdpClient.on('Runtime.exceptionThrown', this.#onRuntimeExceptionThrown);
|
|
await mapperCdpClient.sendCommand('Runtime.enable');
|
|
await browserClient.sendCommand('Target.exposeDevToolsProtocol', {
|
|
bindingName: 'cdp',
|
|
targetId: mapperTabTargetId,
|
|
});
|
|
await mapperCdpClient.sendCommand('Runtime.addBinding', {
|
|
name: 'sendBidiResponse',
|
|
});
|
|
if (verbose) {
|
|
// Needed to request verbose logs from Mapper.
|
|
await mapperCdpClient.sendCommand('Runtime.addBinding', {
|
|
name: 'sendDebugMessage',
|
|
});
|
|
}
|
|
// Evaluate Mapper Tab sources in the tab.
|
|
await mapperCdpClient.sendCommand('Runtime.evaluate', {
|
|
expression: mapperTabSource,
|
|
});
|
|
// TODO: handle errors in all these evaluate calls!
|
|
await mapperCdpClient.sendCommand('Runtime.evaluate', {
|
|
expression: `window.runMapperInstance('${mapperTabTargetId}')`,
|
|
awaitPromise: true,
|
|
});
|
|
debugInternal('Mapper is launched!');
|
|
return bidiSession;
|
|
}
|
|
}
|
|
//# sourceMappingURL=MapperCdpConnection.js.map
|