chromium-bidi
Advanced tools
Comparing version 0.5.4 to 0.5.5
@@ -46,3 +46,3 @@ "use strict"; | ||
}; | ||
constructor(bidiTransport, cdpConnection, browserCdpClient, selfTargetId, options, parser, logger) { | ||
constructor(bidiTransport, cdpConnection, browserCdpClient, selfTargetId, defaultUserContextId, options, parser, logger) { | ||
super(); | ||
@@ -54,3 +54,3 @@ this.#logger = logger; | ||
this.#eventManager = new EventManager_js_1.EventManager(this.#browsingContextStorage); | ||
this.#commandProcessor = new CommandProcessor_js_1.CommandProcessor(cdpConnection, browserCdpClient, this.#eventManager, selfTargetId, this.#browsingContextStorage, new RealmStorage_js_1.RealmStorage(), options?.acceptInsecureCerts ?? false, options?.sharedIdWithFrame ?? false, parser, this.#logger); | ||
this.#commandProcessor = new CommandProcessor_js_1.CommandProcessor(cdpConnection, browserCdpClient, this.#eventManager, selfTargetId, defaultUserContextId, this.#browsingContextStorage, new RealmStorage_js_1.RealmStorage(), options?.acceptInsecureCerts ?? false, options?.sharedIdWithFrame ?? false, parser, this.#logger); | ||
this.#eventManager.on("event" /* EventManagerEvents.Event */, ({ message, event }) => { | ||
@@ -67,3 +67,19 @@ this.emitOutgoingMessage(message, event); | ||
static async createAndStart(bidiTransport, cdpConnection, browserCdpClient, selfTargetId, options, parser, logger) { | ||
const server = new BidiServer(bidiTransport, cdpConnection, browserCdpClient, selfTargetId, options, parser, logger); | ||
// The default context is not exposed in Target.getBrowserContexts but can | ||
// be observed via Target.getTargets. To determine the default browser | ||
// context, we check which one is mentioned in Target.getTargets and not in | ||
// Target.getBrowserContexts. | ||
const [{ browserContextIds }, { targetInfos }] = await Promise.all([ | ||
browserCdpClient.sendCommand('Target.getBrowserContexts'), | ||
browserCdpClient.sendCommand('Target.getTargets'), | ||
]); | ||
let defaultUserContextId = 'default'; | ||
for (const info of targetInfos) { | ||
if (info.browserContextId && | ||
!browserContextIds.includes(info.browserContextId)) { | ||
defaultUserContextId = info.browserContextId; | ||
break; | ||
} | ||
} | ||
const server = new BidiServer(bidiTransport, cdpConnection, browserCdpClient, selfTargetId, defaultUserContextId, options, parser, logger); | ||
// Needed to get events about new targets. | ||
@@ -70,0 +86,0 @@ await browserCdpClient.sendCommand('Target.setDiscoverTargets', { |
@@ -39,5 +39,5 @@ /** | ||
#private; | ||
constructor(cdpConnection: CdpConnection, browserCdpClient: CdpClient, eventManager: EventManager, selfTargetId: string, browsingContextStorage: BrowsingContextStorage, realmStorage: RealmStorage, acceptInsecureCerts: boolean, sharedIdWithFrame: boolean, parser?: BidiCommandParameterParser, logger?: LoggerFn); | ||
constructor(cdpConnection: CdpConnection, browserCdpClient: CdpClient, eventManager: EventManager, selfTargetId: string, defaultUserContextId: string, browsingContextStorage: BrowsingContextStorage, realmStorage: RealmStorage, acceptInsecureCerts: boolean, sharedIdWithFrame: boolean, parser?: BidiCommandParameterParser, logger?: LoggerFn); | ||
processCommand(command: ChromiumBidi.Command): Promise<void>; | ||
} | ||
export {}; |
@@ -50,3 +50,3 @@ "use strict"; | ||
#logger; | ||
constructor(cdpConnection, browserCdpClient, eventManager, selfTargetId, browsingContextStorage, realmStorage, acceptInsecureCerts, sharedIdWithFrame, parser = new BidiNoOpParser_js_1.BidiNoOpParser(), logger) { | ||
constructor(cdpConnection, browserCdpClient, eventManager, selfTargetId, defaultUserContextId, browsingContextStorage, realmStorage, acceptInsecureCerts, sharedIdWithFrame, parser = new BidiNoOpParser_js_1.BidiNoOpParser(), logger) { | ||
super(); | ||
@@ -59,3 +59,3 @@ this.#parser = parser; | ||
this.#browserProcessor = new BrowserProcessor_js_1.BrowserProcessor(browserCdpClient); | ||
this.#browsingContextProcessor = new BrowsingContextProcessor_js_1.BrowsingContextProcessor(cdpConnection, browserCdpClient, selfTargetId, eventManager, browsingContextStorage, realmStorage, networkStorage, preloadScriptStorage, acceptInsecureCerts, sharedIdWithFrame, logger); | ||
this.#browsingContextProcessor = new BrowsingContextProcessor_js_1.BrowsingContextProcessor(cdpConnection, browserCdpClient, selfTargetId, eventManager, browsingContextStorage, realmStorage, networkStorage, preloadScriptStorage, acceptInsecureCerts, sharedIdWithFrame, defaultUserContextId, logger); | ||
this.#cdpProcessor = new CdpProcessor_js_1.CdpProcessor(browsingContextStorage, cdpConnection, browserCdpClient); | ||
@@ -80,2 +80,8 @@ this.#inputProcessor = new InputProcessor_js_1.InputProcessor(browsingContextStorage, realmStorage); | ||
return this.#browserProcessor.close(); | ||
case 'browser.createUserContext': | ||
return await this.#browserProcessor.createUserContext(); | ||
case 'browser.getUserContexts': | ||
return await this.#browserProcessor.getUserContexts(); | ||
case 'browser.removeUserContext': | ||
return await this.#browserProcessor.removeUserContext(command.params.userContext); | ||
// keep-sorted end | ||
@@ -82,0 +88,0 @@ // Browsing Context domain |
@@ -17,3 +17,3 @@ /** | ||
*/ | ||
import type { EmptyResult } from '../../../protocol/protocol.js'; | ||
import { type EmptyResult, type Browser } from '../../../protocol/protocol.js'; | ||
import type { CdpClient } from '../../BidiMapper.js'; | ||
@@ -24,2 +24,5 @@ export declare class BrowserProcessor { | ||
close(): EmptyResult; | ||
createUserContext(): Promise<Browser.CreateUserContextResult>; | ||
removeUserContext(userContext: Browser.UserContext): Promise<EmptyResult>; | ||
getUserContexts(): Promise<Browser.GetUserContextsResult>; | ||
} |
@@ -20,2 +20,3 @@ "use strict"; | ||
exports.BrowserProcessor = void 0; | ||
const protocol_js_1 = require("../../../protocol/protocol.js"); | ||
class BrowserProcessor { | ||
@@ -32,4 +33,43 @@ #browserCdpClient; | ||
} | ||
async createUserContext() { | ||
const context = await this.#browserCdpClient.sendCommand('Target.createBrowserContext'); | ||
return { | ||
userContext: context.browserContextId, | ||
}; | ||
} | ||
async removeUserContext(userContext) { | ||
if (userContext === 'default') { | ||
throw new protocol_js_1.InvalidArgumentException('`default` user context cannot be removed'); | ||
} | ||
try { | ||
await this.#browserCdpClient.sendCommand('Target.disposeBrowserContext', { | ||
browserContextId: userContext, | ||
}); | ||
} | ||
catch (err) { | ||
// https://source.chromium.org/chromium/chromium/src/+/main:content/browser/devtools/protocol/target_handler.cc;l=1424;drc=c686e8f4fd379312469fe018f5c390e9c8f20d0d | ||
if (err.message.startsWith('Failed to find context with id')) { | ||
throw new protocol_js_1.NoSuchUserContextException(err.message); | ||
} | ||
throw err; | ||
} | ||
return {}; | ||
} | ||
async getUserContexts() { | ||
const result = await this.#browserCdpClient.sendCommand('Target.getBrowserContexts'); | ||
return { | ||
userContexts: [ | ||
{ | ||
userContext: 'default', | ||
}, | ||
...result.browserContextIds.map((id) => { | ||
return { | ||
userContext: id, | ||
}; | ||
}), | ||
], | ||
}; | ||
} | ||
} | ||
exports.BrowserProcessor = BrowserProcessor; | ||
//# sourceMappingURL=BrowserProcessor.js.map |
@@ -28,4 +28,5 @@ /** | ||
static readonly LOGGER_PREFIX: "debug:browsingContext"; | ||
readonly userContext: string; | ||
private constructor(); | ||
static create(cdpTarget: CdpTarget, realmStorage: RealmStorage, id: BrowsingContext.BrowsingContext, parentId: BrowsingContext.BrowsingContext | null, eventManager: EventManager, browsingContextStorage: BrowsingContextStorage, sharedIdWithFrame: boolean, logger?: LoggerFn): BrowsingContextImpl; | ||
static create(cdpTarget: CdpTarget, realmStorage: RealmStorage, id: BrowsingContext.BrowsingContext, parentId: BrowsingContext.BrowsingContext | null, userContext: string, eventManager: EventManager, browsingContextStorage: BrowsingContextStorage, sharedIdWithFrame: boolean, logger?: LoggerFn): BrowsingContextImpl; | ||
static getTimestamp(): number; | ||
@@ -32,0 +33,0 @@ /** |
@@ -30,2 +30,3 @@ "use strict"; | ||
#id; | ||
userContext; | ||
/** | ||
@@ -57,3 +58,3 @@ * The ID of the parent browsing context. | ||
#logger; | ||
constructor(cdpTarget, realmStorage, id, parentId, eventManager, browsingContextStorage, sharedIdWithFrame, logger) { | ||
constructor(cdpTarget, realmStorage, id, parentId, userContext, eventManager, browsingContextStorage, sharedIdWithFrame, logger) { | ||
this.#cdpTarget = cdpTarget; | ||
@@ -63,2 +64,3 @@ this.#realmStorage = realmStorage; | ||
this.#parentId = parentId; | ||
this.userContext = userContext; | ||
this.#eventManager = eventManager; | ||
@@ -69,4 +71,4 @@ this.#browsingContextStorage = browsingContextStorage; | ||
} | ||
static create(cdpTarget, realmStorage, id, parentId, eventManager, browsingContextStorage, sharedIdWithFrame, logger) { | ||
const context = new BrowsingContextImpl(cdpTarget, realmStorage, id, parentId, eventManager, browsingContextStorage, sharedIdWithFrame, logger); | ||
static create(cdpTarget, realmStorage, id, parentId, userContext, eventManager, browsingContextStorage, sharedIdWithFrame, logger) { | ||
const context = new BrowsingContextImpl(cdpTarget, realmStorage, id, parentId, userContext, eventManager, browsingContextStorage, sharedIdWithFrame, logger); | ||
context.#initListeners(); | ||
@@ -216,2 +218,3 @@ browsingContextStorage.addContext(context); | ||
url: this.url, | ||
userContext: this.userContext, | ||
children: maxDepth > 0 | ||
@@ -218,0 +221,0 @@ ? this.directChildren.map((c) => c.serializeToBidiValue(maxDepth - 1, false)) |
@@ -12,3 +12,3 @@ import type { CdpClient } from '../../../cdp/CdpClient.js'; | ||
#private; | ||
constructor(cdpConnection: CdpConnection, browserCdpClient: CdpClient, selfTargetId: string, eventManager: EventManager, browsingContextStorage: BrowsingContextStorage, realmStorage: RealmStorage, networkStorage: NetworkStorage, preloadScriptStorage: PreloadScriptStorage, acceptInsecureCerts: boolean, sharedIdWithFrame: boolean, logger?: LoggerFn); | ||
constructor(cdpConnection: CdpConnection, browserCdpClient: CdpClient, selfTargetId: string, eventManager: EventManager, browsingContextStorage: BrowsingContextStorage, realmStorage: RealmStorage, networkStorage: NetworkStorage, preloadScriptStorage: PreloadScriptStorage, acceptInsecureCerts: boolean, sharedIdWithFrame: boolean, defaultUserContextId: string, logger?: LoggerFn); | ||
getTree(params: BrowsingContext.GetTreeParameters): BrowsingContext.GetTreeResult; | ||
@@ -15,0 +15,0 @@ create(params: BrowsingContext.CreateParameters): Promise<BrowsingContext.CreateResult>; |
@@ -20,4 +20,5 @@ "use strict"; | ||
#realmStorage; | ||
#defaultUserContextId; | ||
#logger; | ||
constructor(cdpConnection, browserCdpClient, selfTargetId, eventManager, browsingContextStorage, realmStorage, networkStorage, preloadScriptStorage, acceptInsecureCerts, sharedIdWithFrame, logger) { | ||
constructor(cdpConnection, browserCdpClient, selfTargetId, eventManager, browsingContextStorage, realmStorage, networkStorage, preloadScriptStorage, acceptInsecureCerts, sharedIdWithFrame, defaultUserContextId, logger) { | ||
this.#acceptInsecureCerts = acceptInsecureCerts; | ||
@@ -33,2 +34,3 @@ this.#cdpConnection = cdpConnection; | ||
this.#sharedIdWithFrame = sharedIdWithFrame; | ||
this.#defaultUserContextId = defaultUserContextId; | ||
this.#logger = logger; | ||
@@ -47,2 +49,3 @@ this.#setEventListeners(browserCdpClient); | ||
let referenceContext; | ||
let userContext = params.userContext ?? 'default'; | ||
if (params.referenceContext !== undefined) { | ||
@@ -53,18 +56,38 @@ referenceContext = this.#browsingContextStorage.getContext(params.referenceContext); | ||
} | ||
userContext = referenceContext.userContext; | ||
} | ||
let result; | ||
let newWindow = false; | ||
switch (params.type) { | ||
case "tab" /* BrowsingContext.CreateType.Tab */: | ||
result = await this.#browserCdpClient.sendCommand('Target.createTarget', { | ||
url: 'about:blank', | ||
newWindow: false, | ||
}); | ||
newWindow = false; | ||
break; | ||
case "window" /* BrowsingContext.CreateType.Window */: | ||
result = await this.#browserCdpClient.sendCommand('Target.createTarget', { | ||
url: 'about:blank', | ||
newWindow: true, | ||
}); | ||
newWindow = true; | ||
break; | ||
} | ||
if (userContext !== 'default') { | ||
const existingContexts = this.#browsingContextStorage | ||
.getAllContexts() | ||
.filter((context) => context.userContext === userContext); | ||
if (!existingContexts.length) { | ||
// If there are no contexts in the given user context, we need to set | ||
// newWindow to true as newWindow=false will be rejected. | ||
newWindow = true; | ||
} | ||
} | ||
let result; | ||
try { | ||
result = await this.#browserCdpClient.sendCommand('Target.createTarget', { | ||
url: 'about:blank', | ||
newWindow, | ||
browserContextId: userContext === 'default' ? undefined : userContext, | ||
}); | ||
} | ||
catch (err) { | ||
// https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/devtools/protocol/target_handler.cc;l=1;drc=e80392ac11e48a691f4309964cab83a3a59e01c8 | ||
if (err.message.startsWith('Failed to find browser context with id')) { | ||
throw new protocol_js_1.NoSuchUserContextException(`The context ${userContext} was not found`); | ||
} | ||
throw err; | ||
} | ||
// Wait for the new tab to be loaded to avoid race conditions in the | ||
@@ -187,3 +210,3 @@ // `browsingContext` events, when the `browsingContext.domContentLoaded` and | ||
if (parentBrowsingContext !== undefined) { | ||
BrowsingContextImpl_js_1.BrowsingContextImpl.create(parentBrowsingContext.cdpTarget, this.#realmStorage, params.frameId, params.parentFrameId, this.#eventManager, this.#browsingContextStorage, this.#sharedIdWithFrame, this.#logger); | ||
BrowsingContextImpl_js_1.BrowsingContextImpl.create(parentBrowsingContext.cdpTarget, this.#realmStorage, params.frameId, params.parentFrameId, parentBrowsingContext.userContext, this.#eventManager, this.#browsingContextStorage, this.#sharedIdWithFrame, this.#logger); | ||
} | ||
@@ -217,3 +240,6 @@ } | ||
// New context. | ||
BrowsingContextImpl_js_1.BrowsingContextImpl.create(cdpTarget, this.#realmStorage, targetInfo.targetId, null, this.#eventManager, this.#browsingContextStorage, this.#sharedIdWithFrame, this.#logger); | ||
BrowsingContextImpl_js_1.BrowsingContextImpl.create(cdpTarget, this.#realmStorage, targetInfo.targetId, null, targetInfo.browserContextId && | ||
targetInfo.browserContextId !== this.#defaultUserContextId | ||
? targetInfo.browserContextId | ||
: 'default', this.#eventManager, this.#browsingContextStorage, this.#sharedIdWithFrame, this.#logger); | ||
} | ||
@@ -220,0 +246,0 @@ return; |
@@ -54,9 +54,9 @@ /** | ||
count: number; | ||
"__#88814@#x": number; | ||
"__#88814@#y": number; | ||
"__#88814@#time": number; | ||
"__#88961@#x": number; | ||
"__#88961@#y": number; | ||
"__#88961@#time": number; | ||
compare(context: any): boolean; | ||
}; | ||
"__#88814@#DOUBLE_CLICK_TIME_MS": number; | ||
"__#88814@#MAX_DOUBLE_CLICK_RADIUS": number; | ||
"__#88961@#DOUBLE_CLICK_TIME_MS": number; | ||
"__#88961@#MAX_DOUBLE_CLICK_RADIUS": number; | ||
}; | ||
@@ -63,0 +63,0 @@ setClickCount(button: number, context: InstanceType<typeof PointerSource.ClickContext>): number; |
@@ -8,3 +8,3 @@ "use strict"; | ||
const ChannelProxy_js_1 = require("./ChannelProxy.js"); | ||
const SharedIdParser_js_1 = require("./SharedIdParser.js"); | ||
const SharedId_js_1 = require("./SharedId.js"); | ||
class Realm { | ||
@@ -112,3 +112,3 @@ #realmStorage; | ||
deepSerializedValue.sharedId = | ||
SharedIdParser_js_1.SharedIdParser.getSharedId(this.#getBrowsingContextId(navigableId), navigableId, bidiValue.backendNodeId, this.#sharedIdWithFrame); | ||
(0, SharedId_js_1.getSharedId)(this.#getBrowsingContextId(navigableId), navigableId, bidiValue.backendNodeId, this.#sharedIdWithFrame); | ||
delete bidiValue['backendNodeId']; | ||
@@ -351,3 +351,3 @@ } | ||
if ('sharedId' in localValue && localValue.sharedId) { | ||
const parsedSharedId = SharedIdParser_js_1.SharedIdParser.parseSharedId(localValue.sharedId); | ||
const parsedSharedId = (0, SharedId_js_1.parseSharedId)(localValue.sharedId); | ||
if (parsedSharedId === null) { | ||
@@ -354,0 +354,0 @@ throw new protocol_js_1.NoSuchNodeException(`SharedId "${localValue.sharedId}" was not found.`); |
@@ -25,4 +25,6 @@ "use strict"; | ||
.filter( | ||
// CDP's partition key is the source origin. | ||
(c) => c.partitionKey === undefined || | ||
// CDP's partition key is the source origin. If the request specifies the | ||
// `sourceOrigin` partition key, only cookies with the requested source origin | ||
// are returned. | ||
(c) => partitionKey.sourceOrigin === undefined || | ||
c.partitionKey === partitionKey.sourceOrigin) | ||
@@ -54,20 +56,26 @@ .map((c) => this.#cdpToBiDiCookie(c)) | ||
const browsingContextId = descriptor.context; | ||
const browsingContext = this.#browsingContextStorage.getContext(browsingContextId); | ||
const url = NetworkProcessor_js_1.NetworkProcessor.parseUrlString(browsingContext?.url ?? ''); | ||
// Cookie origin should not contain the port. | ||
// Origin `null` is a special case for local pages. | ||
const sourceOrigin = url.origin === 'null' ? url.origin : `${url.protocol}//${url.hostname}`; | ||
return { | ||
sourceOrigin, | ||
}; | ||
// Assert the browsing context exists. | ||
this.#browsingContextStorage.getContext(browsingContextId); | ||
// https://w3c.github.io/webdriver-bidi/#associated-storage-partition. | ||
// Each browsing context also has an associated storage partition, which is the | ||
// storage partition it uses to persist data. In Chromium it's a `BrowserContext` | ||
// which maps to BiDi `UserContext`. | ||
// TODO: extend with UserContext. | ||
return {}; | ||
} | ||
#expandStoragePartitionSpecByStorageKey(descriptor) { | ||
let sourceOrigin = undefined; | ||
if (descriptor.sourceOrigin !== undefined) { | ||
sourceOrigin = descriptor.sourceOrigin; | ||
const unsupportedPartitionKeys = new Map(); | ||
let sourceOrigin = descriptor.sourceOrigin; | ||
if (sourceOrigin !== undefined) { | ||
const url = NetworkProcessor_js_1.NetworkProcessor.parseUrlString(sourceOrigin); | ||
if (url.origin === 'null') { | ||
// Origin `null` is a special case for local pages. | ||
sourceOrigin = url.origin; | ||
} | ||
else { | ||
// Port is not supported in CDP Cookie's `partitionKey`, so it should be stripped | ||
// from the requested source origin. | ||
sourceOrigin = `${url.protocol}//${url.hostname}`; | ||
} | ||
} | ||
if (sourceOrigin === undefined) { | ||
throw new protocol_js_1.UnderspecifiedStoragePartitionException('"sourceOrigin" should be set'); | ||
} | ||
const unsupportedPartitionKeys = new Map(); | ||
// Partition spec is a storage partition. | ||
@@ -86,3 +94,3 @@ // Let partition key be partition spec. | ||
return { | ||
sourceOrigin, | ||
...(sourceOrigin === undefined ? {} : { sourceOrigin }), | ||
}; | ||
@@ -92,3 +100,3 @@ } | ||
if (partitionSpec === undefined) { | ||
throw new protocol_js_1.UnderspecifiedStoragePartitionException('partition should be set'); | ||
return {}; | ||
} | ||
@@ -115,3 +123,5 @@ if (partitionSpec.type === 'context') { | ||
// CDP's `partitionKey` is the BiDi's `partition.sourceOrigin`. | ||
partitionKey: partitionKey.sourceOrigin, | ||
...(partitionKey.sourceOrigin !== undefined && { | ||
partitionKey: partitionKey.sourceOrigin, | ||
}), | ||
...(params.cookie.expiry !== undefined && { | ||
@@ -118,0 +128,0 @@ expires: params.cookie.expiry, |
@@ -56,3 +56,3 @@ "use strict"; | ||
(0, WebSocketServer_js_1.debugInfo)('Launching BiDi server...'); | ||
WebSocketServer_js_1.WebSocketServer.run(port, channel, headless, verbose); | ||
new WebSocketServer_js_1.WebSocketServer(port, channel, headless, verbose); | ||
(0, WebSocketServer_js_1.debugInfo)('BiDi server launched'); | ||
@@ -59,0 +59,0 @@ } |
@@ -41,8 +41,7 @@ "use strict"; | ||
#cdpConnection; | ||
#mapperCdpClient; | ||
#bidiSession; | ||
static async create(cdpConnection, mapperTabSource, verbose, mapperOptions) { | ||
try { | ||
const mapperCdpClient = await this.#initMapper(cdpConnection, mapperTabSource, verbose, mapperOptions); | ||
return new MapperServerCdpConnection(cdpConnection, mapperCdpClient); | ||
const bidiSession = await this.#initMapper(cdpConnection, mapperTabSource, verbose, mapperOptions); | ||
return new MapperServerCdpConnection(cdpConnection, bidiSession); | ||
} | ||
@@ -54,14 +53,9 @@ catch (e) { | ||
} | ||
constructor(cdpConnection, mapperCdpClient) { | ||
constructor(cdpConnection, bidiSession) { | ||
this.#cdpConnection = cdpConnection; | ||
this.#mapperCdpClient = mapperCdpClient; | ||
this.#bidiSession = new SimpleTransport_js_1.SimpleTransport(async (message) => await this.#sendMessage(message)); | ||
this.#mapperCdpClient.on('Runtime.bindingCalled', this.#onBindingCalled); | ||
this.#mapperCdpClient.on('Runtime.consoleAPICalled', this.#onConsoleAPICalled); | ||
// Catch unhandled exceptions in the mapper. | ||
this.#mapperCdpClient.on('Runtime.exceptionThrown', this.#onRuntimeExceptionThrown); | ||
this.#bidiSession = bidiSession; | ||
} | ||
async #sendMessage(message) { | ||
static async #sendMessage(mapperCdpClient, message) { | ||
try { | ||
await this.#mapperCdpClient.sendCommand('Runtime.evaluate', { | ||
await mapperCdpClient.sendCommand('Runtime.evaluate', { | ||
expression: `onBidiMessage(${JSON.stringify(message)})`, | ||
@@ -80,5 +74,5 @@ }); | ||
} | ||
#onBindingCalled = (params) => { | ||
static #onBindingCalled = (params, bidiSession) => { | ||
if (params.name === 'sendBidiResponse') { | ||
this.#bidiSession.emit('message', params.payload); | ||
bidiSession.emit('message', params.payload); | ||
} | ||
@@ -89,3 +83,3 @@ else if (params.name === 'sendDebugMessage') { | ||
}; | ||
#onDebugMessage = (json) => { | ||
static #onDebugMessage = (json) => { | ||
try { | ||
@@ -103,6 +97,6 @@ const log = JSON.parse(json); | ||
}; | ||
#onConsoleAPICalled = (params) => { | ||
static #onConsoleAPICalled = (params) => { | ||
debugInfo('consoleAPICalled: %s %O', params.type, params.args.map((arg) => arg.value)); | ||
}; | ||
#onRuntimeExceptionThrown = (params) => { | ||
static #onRuntimeExceptionThrown = (params) => { | ||
debugInfo('exceptionThrown:', params); | ||
@@ -118,2 +112,9 @@ }; | ||
const mapperCdpClient = cdpConnection.getCdpClient(mapperSessionId); | ||
const bidiSession = new SimpleTransport_js_1.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'); | ||
@@ -142,3 +143,3 @@ await browserClient.sendCommand('Target.exposeDevToolsProtocol', { | ||
debugInternal('Mapper is launched!'); | ||
return mapperCdpClient; | ||
return bidiSession; | ||
} | ||
@@ -145,0 +146,0 @@ } |
@@ -6,9 +6,3 @@ import type { ChromeReleaseChannel } from '@puppeteer/browsers'; | ||
#private; | ||
/** | ||
* @param bidiPort Port to start ws server on. | ||
* @param channel | ||
* @param headless | ||
* @param verbose | ||
*/ | ||
static run(bidiPort: number, channel: ChromeReleaseChannel, headless: boolean, verbose: boolean): void; | ||
constructor(port: number, channel: ChromeReleaseChannel, headless: boolean, verbose: boolean); | ||
} |
@@ -56,201 +56,208 @@ "use strict"; | ||
class WebSocketServer { | ||
static #sessions = new Map(); | ||
/** | ||
* @param bidiPort Port to start ws server on. | ||
* @param channel | ||
* @param headless | ||
* @param verbose | ||
*/ | ||
static run(bidiPort, channel, headless, verbose) { | ||
const server = http_1.default.createServer(async (request, response) => { | ||
debugInternal(`${new Date().toString()} Received HTTP ${JSON.stringify(request.method)} request for ${JSON.stringify(request.url)}`); | ||
if (!request.url) { | ||
return response.end(404); | ||
} | ||
// https://w3c.github.io/webdriver-bidi/#transport, step 2. | ||
if (request.url === '/session') { | ||
const body = []; | ||
request | ||
.on('data', (chunk) => { | ||
body.push(chunk); | ||
}) | ||
.on('end', () => { | ||
const jsonBody = JSON.parse(Buffer.concat(body).toString()); | ||
response.writeHead(200, { | ||
'Content-Type': 'application/json;charset=utf-8', | ||
'Cache-Control': 'no-cache', | ||
}); | ||
const sessionId = (0, uuid_js_1.uuidv4)(); | ||
const session = { | ||
sessionId, | ||
// TODO: launch browser instance and set it to the session after WPT | ||
// tests clean up is switched to pure BiDi. | ||
browserInstancePromise: undefined, | ||
sessionOptions: { | ||
chromeOptions: this.#getChromeOptions(jsonBody.capabilities, channel, headless), | ||
mapperOptions: this.#getMapperOptions(jsonBody.capabilities), | ||
verbose, | ||
}, | ||
}; | ||
this.#sessions.set(sessionId, session); | ||
const webSocketUrl = `ws://localhost:${bidiPort}/session/${sessionId}`; | ||
debugInternal(`Session created. WebSocket URL: ${JSON.stringify(webSocketUrl)}.`); | ||
response.write(JSON.stringify({ | ||
value: { | ||
sessionId, | ||
capabilities: { | ||
webSocketUrl, | ||
}, | ||
}, | ||
})); | ||
return response.end(); | ||
#sessions = new Map(); | ||
#port; | ||
#channel; | ||
#headless; | ||
#verbose; | ||
#server; | ||
#wsServer; | ||
constructor(port, channel, headless, verbose) { | ||
this.#port = port; | ||
this.#channel = channel; | ||
this.#headless = headless; | ||
this.#verbose = verbose; | ||
this.#server = http_1.default.createServer(this.#onRequest.bind(this)); | ||
this.#wsServer = new websocket.server({ | ||
httpServer: this.#server, | ||
autoAcceptConnections: false, | ||
}); | ||
this.#wsServer.on('request', this.#onWsRequest.bind(this)); | ||
this.#server.listen(this.#port, () => { | ||
(0, exports.debugInfo)('BiDi server is listening on port', this.#port); | ||
}); | ||
} | ||
async #onRequest(request, response) { | ||
debugInternal(`Received HTTP ${JSON.stringify(request.method)} request for ${JSON.stringify(request.url)}`); | ||
if (!request.url) { | ||
return response.end(404); | ||
} | ||
// https://w3c.github.io/webdriver-bidi/#transport, step 2. | ||
if (request.url === '/session') { | ||
const body = await new Promise((resolve, reject) => { | ||
const bodyArray = []; | ||
request.on('data', (chunk) => { | ||
bodyArray.push(chunk); | ||
}); | ||
request.on('error', reject); | ||
request.on('end', () => { | ||
resolve(Buffer.concat(bodyArray)); | ||
}); | ||
}); | ||
// https://w3c.github.io/webdriver-bidi/#transport, step 3. | ||
const jsonBody = JSON.parse(body.toString()); | ||
response.writeHead(200, { | ||
'Content-Type': 'application/json;charset=utf-8', | ||
'Cache-Control': 'no-cache', | ||
}); | ||
const sessionId = (0, uuid_js_1.uuidv4)(); | ||
const session = { | ||
sessionId, | ||
// TODO: launch browser instance and set it to the session after WPT | ||
// tests clean up is switched to pure BiDi. | ||
browserInstancePromise: undefined, | ||
sessionOptions: { | ||
chromeOptions: this.#getChromeOptions(jsonBody.capabilities, this.#channel, this.#headless), | ||
mapperOptions: this.#getMapperOptions(jsonBody.capabilities), | ||
verbose: this.#verbose, | ||
}, | ||
}; | ||
this.#sessions.set(sessionId, session); | ||
const webSocketUrl = `ws://localhost:${this.#port}/session/${sessionId}`; | ||
debugInternal(`Session created. WebSocket URL: ${JSON.stringify(webSocketUrl)}.`); | ||
response.write(JSON.stringify({ | ||
value: { | ||
sessionId, | ||
capabilities: { | ||
webSocketUrl, | ||
}, | ||
}, | ||
})); | ||
return response.end(); | ||
} | ||
else if (request.url.startsWith('/session')) { | ||
debugInternal(`Unknown session command ${request.method ?? 'UNKNOWN METHOD'} request for ${request.url} with payload ${await this.#getHttpRequestPayload(request)}. 200 returned.`); | ||
response.writeHead(200, { | ||
'Content-Type': 'application/json;charset=utf-8', | ||
'Cache-Control': 'no-cache', | ||
}); | ||
response.write(JSON.stringify({ | ||
value: {}, | ||
})); | ||
return response.end(); | ||
} | ||
debugInternal(`Unknown ${request.method} request for ${JSON.stringify(request.url)} with payload ${await this.#getHttpRequestPayload(request)}. 404 returned.`); | ||
return response.end(404); | ||
} | ||
#onWsRequest(request) { | ||
// Session is set either by Classic or BiDi commands. | ||
let session; | ||
const requestSessionId = (request.resource ?? '').split('/').pop(); | ||
debugInternal(`new WS request received. Path: ${JSON.stringify(request.resourceURL.path)}, sessionId: ${JSON.stringify(requestSessionId)}`); | ||
if (requestSessionId !== '' && | ||
requestSessionId !== undefined && | ||
!this.#sessions.has(requestSessionId)) { | ||
debugInternal('Unknown session id:', requestSessionId); | ||
request.reject(); | ||
return; | ||
} | ||
const connection = request.accept(); | ||
session = this.#sessions.get(requestSessionId ?? ''); | ||
if (session !== undefined) { | ||
// BrowserInstance is created for each new WS connection, even for the | ||
// same SessionId. This is because WPT uses a single session for all the | ||
// tests, but cleans up tests using WebDriver Classic commands, which is | ||
// not implemented in this Mapper runner. | ||
// TODO: connect to an existing BrowserInstance instead. | ||
const sessionOptions = session.sessionOptions; | ||
session.browserInstancePromise = this.#closeBrowserInstanceIfLaunched(session) | ||
.then(async () => await this.#launchBrowserInstance(connection, sessionOptions)) | ||
.catch((e) => { | ||
(0, exports.debugInfo)('Error while creating session', e); | ||
connection.close(500, 'cannot create browser instance'); | ||
throw e; | ||
}); | ||
} | ||
connection.on('message', async (message) => { | ||
// If type is not text, return error. | ||
if (message.type !== 'utf8') { | ||
this.#respondWithError(connection, {}, "invalid argument" /* ErrorCode.InvalidArgument */, `not supported type (${message.type})`); | ||
return; | ||
} | ||
else if (request.url.startsWith('/session')) { | ||
debugInternal(`Unknown session command ${request.method ?? 'UNKNOWN METHOD'} request for ${request.url} with payload ${await WebSocketServer.#getHttpRequestPayload(request)}. 200 returned.`); | ||
response.writeHead(200, { | ||
'Content-Type': 'application/json;charset=utf-8', | ||
'Cache-Control': 'no-cache', | ||
}); | ||
response.write(JSON.stringify({ | ||
value: {}, | ||
})); | ||
const plainCommandData = message.utf8Data; | ||
if (debugRecv.enabled) { | ||
try { | ||
debugRecv(JSON.parse(plainCommandData)); | ||
} | ||
catch { | ||
debugRecv(plainCommandData); | ||
} | ||
} | ||
else { | ||
debugInternal(`Unknown ${JSON.stringify(request.method)} request for ${JSON.stringify(request.url)} with payload ${JSON.stringify(await WebSocketServer.#getHttpRequestPayload(request))}. 404 returned.`); | ||
response.writeHead(404); | ||
// Try to parse the message to handle some of BiDi commands. | ||
let parsedCommandData; | ||
try { | ||
parsedCommandData = JSON.parse(plainCommandData); | ||
} | ||
return response.end(); | ||
}); | ||
server.listen(bidiPort, () => { | ||
(0, exports.debugInfo)('BiDi server is listening on port', bidiPort); | ||
}); | ||
const wsServer = new websocket.server({ | ||
httpServer: server, | ||
autoAcceptConnections: false, | ||
}); | ||
wsServer.on('request', (request) => { | ||
// Session is set either by Classic or BiDi commands. | ||
let session; | ||
const requestSessionId = (request.resource ?? '').split('/').pop(); | ||
debugInternal(`new WS request received. Path: ${JSON.stringify(request.resourceURL.path)}, sessionId: ${JSON.stringify(requestSessionId)}`); | ||
if (requestSessionId !== '' && | ||
requestSessionId !== undefined && | ||
!this.#sessions.has(requestSessionId)) { | ||
debugInternal('Unknown session id:', requestSessionId); | ||
request.reject(); | ||
catch (e) { | ||
this.#respondWithError(connection, {}, "invalid argument" /* ErrorCode.InvalidArgument */, `Cannot parse data as JSON`); | ||
return; | ||
} | ||
const connection = request.accept(); | ||
session = this.#sessions.get(requestSessionId ?? ''); | ||
if (session !== undefined) { | ||
// BrowserInstance is created for each new WS connection, even for the | ||
// same SessionId. This is because WPT uses a single session for all the | ||
// tests, but cleans up tests using WebDriver Classic commands, which is | ||
// not implemented in this Mapper runner. | ||
// TODO: connect to an existing BrowserInstance instead. | ||
const sessionOptions = session.sessionOptions; | ||
session.browserInstancePromise = this.#closeBrowserInstanceIfLaunched(session) | ||
.then(async () => await this.#launchBrowserInstance(connection, sessionOptions)) | ||
.catch((e) => { | ||
(0, exports.debugInfo)('Error while creating session', e); | ||
connection.close(500, 'cannot create browser instance'); | ||
throw e; | ||
}); | ||
} | ||
connection.on('message', async (message) => { | ||
// If type is not text, return error. | ||
if (message.type !== 'utf8') { | ||
this.#respondWithError(connection, {}, "invalid argument" /* ErrorCode.InvalidArgument */, `not supported type (${message.type})`); | ||
// Handle creating new session. | ||
if (parsedCommandData.method === 'session.new') { | ||
if (session !== undefined) { | ||
(0, exports.debugInfo)('WS connection already have an associated session.'); | ||
this.#respondWithError(connection, plainCommandData, "session not created" /* ErrorCode.SessionNotCreated */, 'WS connection already have an associated session.'); | ||
return; | ||
} | ||
const plainCommandData = message.utf8Data; | ||
if (debugRecv.enabled) { | ||
try { | ||
debugRecv(JSON.parse(plainCommandData)); | ||
} | ||
catch { | ||
debugRecv(plainCommandData); | ||
} | ||
} | ||
// Try to parse the message to handle some of BiDi commands. | ||
let parsedCommandData; | ||
try { | ||
parsedCommandData = JSON.parse(plainCommandData); | ||
const sessionOptions = { | ||
chromeOptions: this.#getChromeOptions(parsedCommandData.params?.capabilities, this.#channel, this.#headless), | ||
mapperOptions: this.#getMapperOptions(parsedCommandData.params?.capabilities), | ||
verbose: this.#verbose, | ||
}; | ||
const browserInstance = await this.#launchBrowserInstance(connection, sessionOptions); | ||
const sessionId = (0, uuid_js_1.uuidv4)(); | ||
session = { | ||
sessionId, | ||
browserInstancePromise: Promise.resolve(browserInstance), | ||
sessionOptions, | ||
}; | ||
this.#sessions.set(sessionId, session); | ||
} | ||
catch (e) { | ||
this.#respondWithError(connection, {}, "invalid argument" /* ErrorCode.InvalidArgument */, `Cannot parse data as JSON`); | ||
(0, exports.debugInfo)('Error while creating session', e); | ||
this.#respondWithError(connection, plainCommandData, "session not created" /* ErrorCode.SessionNotCreated */, e?.message ?? 'Unknown error'); | ||
return; | ||
} | ||
// Handle creating new session. | ||
if (parsedCommandData.method === 'session.new') { | ||
if (session !== undefined) { | ||
(0, exports.debugInfo)('WS connection already have an associated session.'); | ||
this.#respondWithError(connection, plainCommandData, "session not created" /* ErrorCode.SessionNotCreated */, 'WS connection already have an associated session.'); | ||
return; | ||
} | ||
try { | ||
const sessionOptions = { | ||
chromeOptions: this.#getChromeOptions(parsedCommandData.params?.capabilities, channel, headless), | ||
mapperOptions: this.#getMapperOptions(parsedCommandData.params?.capabilities), | ||
verbose, | ||
}; | ||
const browserInstance = await this.#launchBrowserInstance(connection, sessionOptions); | ||
const sessionId = (0, uuid_js_1.uuidv4)(); | ||
session = { | ||
sessionId, | ||
browserInstancePromise: Promise.resolve(browserInstance), | ||
sessionOptions, | ||
}; | ||
this.#sessions.set(sessionId, session); | ||
} | ||
catch (e) { | ||
(0, exports.debugInfo)('Error while creating session', e); | ||
this.#respondWithError(connection, plainCommandData, "session not created" /* ErrorCode.SessionNotCreated */, e?.message ?? 'Unknown error'); | ||
return; | ||
} | ||
// TODO: extend with capabilities. | ||
this.#sendClientMessage({ | ||
id: parsedCommandData.id, | ||
type: 'success', | ||
result: { | ||
sessionId: session.sessionId, | ||
capabilities: {}, | ||
}, | ||
}, connection); | ||
return; | ||
} | ||
if (session === undefined) { | ||
(0, exports.debugInfo)('Session is not yet initialized.'); | ||
this.#respondWithError(connection, plainCommandData, "invalid session id" /* ErrorCode.InvalidSessionId */, 'Session is not yet initialized.'); | ||
return; | ||
} | ||
if (session.browserInstancePromise === undefined) { | ||
(0, exports.debugInfo)('Browser instance is not launched.'); | ||
this.#respondWithError(connection, plainCommandData, "invalid session id" /* ErrorCode.InvalidSessionId */, 'Browser instance is not launched.'); | ||
return; | ||
} | ||
const browserInstance = await session.browserInstancePromise; | ||
// Handle `browser.close` command. | ||
if (parsedCommandData.method === 'browser.close') { | ||
await browserInstance.close(); | ||
this.#sendClientMessage({ | ||
id: parsedCommandData.id, | ||
type: 'success', | ||
result: {}, | ||
}, connection); | ||
return; | ||
} | ||
// Forward all other commands to BiDi Mapper. | ||
await browserInstance.bidiSession().sendCommand(plainCommandData); | ||
}); | ||
connection.on('close', async () => { | ||
debugInternal(`${new Date().toString()} Peer ${connection.remoteAddress} disconnected.`); | ||
// TODO: don't close Browser instance to allow re-connecting to the session. | ||
await this.#closeBrowserInstanceIfLaunched(session); | ||
}); | ||
// TODO: extend with capabilities. | ||
this.#sendClientMessage({ | ||
id: parsedCommandData.id, | ||
type: 'success', | ||
result: { | ||
sessionId: session.sessionId, | ||
capabilities: {}, | ||
}, | ||
}, connection); | ||
return; | ||
} | ||
if (session === undefined) { | ||
(0, exports.debugInfo)('Session is not yet initialized.'); | ||
this.#respondWithError(connection, plainCommandData, "invalid session id" /* ErrorCode.InvalidSessionId */, 'Session is not yet initialized.'); | ||
return; | ||
} | ||
if (session.browserInstancePromise === undefined) { | ||
(0, exports.debugInfo)('Browser instance is not launched.'); | ||
this.#respondWithError(connection, plainCommandData, "invalid session id" /* ErrorCode.InvalidSessionId */, 'Browser instance is not launched.'); | ||
return; | ||
} | ||
const browserInstance = await session.browserInstancePromise; | ||
// Handle `browser.close` command. | ||
if (parsedCommandData.method === 'browser.close') { | ||
await browserInstance.close(); | ||
this.#sendClientMessage({ | ||
id: parsedCommandData.id, | ||
type: 'success', | ||
result: {}, | ||
}, connection); | ||
return; | ||
} | ||
// Forward all other commands to BiDi Mapper. | ||
await browserInstance.bidiSession().sendCommand(plainCommandData); | ||
}); | ||
connection.on('close', async () => { | ||
debugInternal(`Peer ${connection.remoteAddress} disconnected.`); | ||
// TODO: don't close Browser instance to allow re-connecting to the session. | ||
await this.#closeBrowserInstanceIfLaunched(session); | ||
}); | ||
} | ||
static async #closeBrowserInstanceIfLaunched(session) { | ||
async #closeBrowserInstanceIfLaunched(session) { | ||
if (session === undefined || session.browserInstancePromise === undefined) { | ||
@@ -263,3 +270,3 @@ return; | ||
} | ||
static #getMapperOptions(capabilities) { | ||
#getMapperOptions(capabilities) { | ||
const acceptInsecureCerts = capabilities?.alwaysMatch?.acceptInsecureCerts ?? false; | ||
@@ -269,3 +276,3 @@ const sharedIdWithFrame = capabilities?.alwaysMatch?.sharedIdWithFrame ?? false; | ||
} | ||
static #getChromeOptions(capabilities, channel, headless) { | ||
#getChromeOptions(capabilities, channel, headless) { | ||
const chromeCapabilities = capabilities?.alwaysMatch?.['goog:chromeOptions']; | ||
@@ -279,3 +286,3 @@ return { | ||
} | ||
static async #launchBrowserInstance(connection, sessionOptions) { | ||
async #launchBrowserInstance(connection, sessionOptions) { | ||
(0, exports.debugInfo)('Scheduling browser launch...'); | ||
@@ -290,3 +297,3 @@ const browserInstance = await BrowserInstance_js_1.BrowserInstance.run(sessionOptions.chromeOptions, sessionOptions.mapperOptions, sessionOptions.verbose); | ||
} | ||
static #sendClientMessageString(message, connection) { | ||
#sendClientMessageString(message, connection) { | ||
if (debugSend.enabled) { | ||
@@ -302,11 +309,11 @@ try { | ||
} | ||
static #sendClientMessage(object, connection) { | ||
#sendClientMessage(object, connection) { | ||
const json = JSON.stringify(object); | ||
return this.#sendClientMessageString(json, connection); | ||
} | ||
static #respondWithError(connection, plainCommandData, errorCode, errorMessage) { | ||
#respondWithError(connection, plainCommandData, errorCode, errorMessage) { | ||
const errorResponse = this.#getErrorResponse(plainCommandData, errorCode, errorMessage); | ||
void this.#sendClientMessage(errorResponse, connection); | ||
} | ||
static #getErrorResponse(plainCommandData, errorCode, errorMessage) { | ||
#getErrorResponse(plainCommandData, errorCode, errorMessage) { | ||
// XXX: this is bizarre per spec. We reparse the payload and | ||
@@ -330,3 +337,3 @@ // extract the ID, regardless of what kind of value it was. | ||
} | ||
static #getHttpRequestPayload(request) { | ||
#getHttpRequestPayload(request) { | ||
return new Promise((resolve, reject) => { | ||
@@ -333,0 +340,0 @@ let data = ''; |
@@ -62,2 +62,5 @@ /** | ||
} | ||
export declare class NoSuchUserContextException extends Exception { | ||
constructor(message: string, stacktrace?: string); | ||
} | ||
export declare class SessionNotCreatedException extends Exception { | ||
@@ -64,0 +67,0 @@ constructor(message: string, stacktrace?: string); |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.UnderspecifiedStoragePartitionException = exports.UnableToSetFileInputException = exports.UnableToSetCookieException = exports.NoSuchStoragePartitionException = exports.UnsupportedOperationException = exports.UnableToCloseBrowserException = exports.UnableToCaptureScreenException = exports.UnknownErrorException = exports.UnknownCommandException = exports.SessionNotCreatedException = exports.NoSuchScriptException = exports.NoSuchRequestException = exports.NoSuchNodeException = exports.NoSuchInterceptException = exports.NoSuchHistoryEntryException = exports.NoSuchHandleException = exports.NoSuchFrameException = exports.NoSuchElementException = exports.NoSuchAlertException = exports.MoveTargetOutOfBoundsException = exports.InvalidSessionIdException = exports.InvalidArgumentException = exports.Exception = void 0; | ||
exports.UnderspecifiedStoragePartitionException = exports.UnableToSetFileInputException = exports.UnableToSetCookieException = exports.NoSuchStoragePartitionException = exports.UnsupportedOperationException = exports.UnableToCloseBrowserException = exports.UnableToCaptureScreenException = exports.UnknownErrorException = exports.UnknownCommandException = exports.SessionNotCreatedException = exports.NoSuchUserContextException = exports.NoSuchScriptException = exports.NoSuchRequestException = exports.NoSuchNodeException = exports.NoSuchInterceptException = exports.NoSuchHistoryEntryException = exports.NoSuchHandleException = exports.NoSuchFrameException = exports.NoSuchElementException = exports.NoSuchAlertException = exports.MoveTargetOutOfBoundsException = exports.InvalidSessionIdException = exports.InvalidArgumentException = exports.Exception = void 0; | ||
class Exception { | ||
@@ -96,2 +96,8 @@ error; | ||
exports.NoSuchScriptException = NoSuchScriptException; | ||
class NoSuchUserContextException extends Exception { | ||
constructor(message, stacktrace) { | ||
super("no such user context" /* ErrorCode.NoSuchUserContext */, message, stacktrace); | ||
} | ||
} | ||
exports.NoSuchUserContextException = NoSuchUserContextException; | ||
class SessionNotCreatedException extends Exception { | ||
@@ -98,0 +104,0 @@ constructor(message, stacktrace) { |
@@ -70,2 +70,3 @@ /** | ||
NoSuchStoragePartition = "no such storage partition", | ||
NoSuchUserContext = "no such user context", | ||
SessionNotCreated = "session not created", | ||
@@ -203,4 +204,13 @@ UnableToCaptureScreen = "unable to capture screen", | ||
} | ||
export type BrowserCommand = Browser.Close; | ||
export type BrowserCommand = Browser.Close | Browser.CreateUserContext | Browser.GetUserContexts | Browser.RemoveUserContext; | ||
export type BrowserResult = Browser.CreateUserContextResult | Browser.GetUserContextsResult; | ||
export declare namespace Browser { | ||
type UserContext = string; | ||
} | ||
export declare namespace Browser { | ||
type UserContextInfo = { | ||
userContext: Browser.UserContext; | ||
}; | ||
} | ||
export declare namespace Browser { | ||
type Close = { | ||
@@ -211,2 +221,30 @@ method: 'browser.close'; | ||
} | ||
export declare namespace Browser { | ||
type CreateUserContext = { | ||
method: 'browser.createUserContext'; | ||
params: EmptyParams; | ||
}; | ||
} | ||
export declare namespace Browser { | ||
type CreateUserContextResult = Browser.UserContextInfo; | ||
} | ||
export declare namespace Browser { | ||
type GetUserContexts = { | ||
method: 'browser.getUserContexts'; | ||
params: EmptyParams; | ||
}; | ||
} | ||
export declare namespace Browser { | ||
type GetUserContextsResult = { | ||
userContexts: [Browser.UserContextInfo, ...Browser.UserContextInfo[]]; | ||
}; | ||
} | ||
export declare namespace Browser { | ||
type RemoveUserContext = { | ||
method: 'browser.removeUserContext'; | ||
params: { | ||
userContext: Browser.UserContext; | ||
}; | ||
}; | ||
} | ||
export type BrowsingContextCommand = BrowsingContext.Activate | BrowsingContext.CaptureScreenshot | BrowsingContext.Close | BrowsingContext.Create | BrowsingContext.GetTree | BrowsingContext.HandleUserPrompt | BrowsingContext.LocateNodes | BrowsingContext.Navigate | BrowsingContext.Print | BrowsingContext.Reload | BrowsingContext.SetViewport | BrowsingContext.TraverseHistory; | ||
@@ -223,5 +261,6 @@ export type BrowsingContextEvent = BrowsingContext.ContextCreated | BrowsingContext.ContextDestroyed | BrowsingContext.DomContentLoaded | BrowsingContext.DownloadWillBegin | BrowsingContext.FragmentNavigated | BrowsingContext.Load | BrowsingContext.NavigationAborted | BrowsingContext.NavigationFailed | BrowsingContext.NavigationStarted | BrowsingContext.UserPromptClosed | BrowsingContext.UserPromptOpened; | ||
type Info = { | ||
children: BrowsingContext.InfoList | null; | ||
context: BrowsingContext.BrowsingContext; | ||
url: string; | ||
children: BrowsingContext.InfoList | null; | ||
userContext: Browser.UserContext; | ||
parent?: BrowsingContext.BrowsingContext | null; | ||
@@ -367,2 +406,3 @@ }; | ||
background?: boolean; | ||
userContext?: Browser.UserContext | null; | ||
}; | ||
@@ -369,0 +409,0 @@ } |
{ | ||
"name": "chromium-bidi", | ||
"version": "0.5.4", | ||
"version": "0.5.5", | ||
"description": "An implementation of the WebDriver BiDi protocol for Chromium implemented as a JavaScript layer translating between BiDi and CDP, running inside a Chrome tab.", | ||
@@ -171,6 +171,7 @@ "scripts": { | ||
"@actions/core": "1.10.1", | ||
"@puppeteer/browsers": "1.9.0", | ||
"@puppeteer/browsers": "1.9.1", | ||
"@rollup/plugin-commonjs": "25.0.7", | ||
"@rollup/plugin-node-resolve": "15.2.3", | ||
"@rollup/plugin-terser": "0.4.4", | ||
"@rollup/wasm-node": "4.9.6", | ||
"@types/argparse": "2.0.14", | ||
@@ -182,8 +183,8 @@ "@types/chai": "4.3.11", | ||
"@types/node": "20.10.6", | ||
"@types/sinon": "17.0.2", | ||
"@types/sinon": "17.0.3", | ||
"@types/websocket": "1.0.10", | ||
"@types/ws": "8.5.10", | ||
"@types/yargs": "17.0.32", | ||
"@typescript-eslint/eslint-plugin": "6.17.0", | ||
"@typescript-eslint/parser": "6.17.0", | ||
"@typescript-eslint/eslint-plugin": "6.19.1", | ||
"@typescript-eslint/parser": "6.19.1", | ||
"argparse": "2.0.1", | ||
@@ -199,3 +200,3 @@ "chai": "4.3.10", | ||
"eslint-plugin-mocha": "10.2.0", | ||
"eslint-plugin-prettier": "5.1.2", | ||
"eslint-plugin-prettier": "5.1.3", | ||
"eslint-plugin-promise": "6.1.1", | ||
@@ -206,14 +207,14 @@ "gts": "5.2.0", | ||
"pkg-dir": "8.0.0", | ||
"prettier": "3.1.1", | ||
"prettier": "3.2.4", | ||
"rimraf": "5.0.5", | ||
"rollup": "3.29.4", | ||
"selenium-webdriver": "4.16.0", | ||
"rollup": "4.9.6", | ||
"selenium-webdriver": "4.17.0", | ||
"sinon": "17.0.1", | ||
"source-map-support": "0.5.21", | ||
"terser": "5.26.0", | ||
"terser": "5.27.0", | ||
"tslib": "2.6.2", | ||
"typescript": "5.3.3", | ||
"webdriverio": "8.27.0", | ||
"webdriverio": "8.29.1", | ||
"websocket": "1.0.34", | ||
"wireit": "0.14.1", | ||
"wireit": "0.14.3", | ||
"ws": "8.16.0", | ||
@@ -220,0 +221,0 @@ "yargs": "17.7.2", |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
4810848
94360
49