blob: 7a78d0bd1cf5fa34f3a448989e29ee28101bc770 [file] [log] [blame] [edit]
// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The SFC licenses this file
// to you 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.
const {
EvaluateResultType,
EvaluateResultSuccess,
EvaluateResultException,
ExceptionDetails,
} = require('./evaluateResult')
const { Message } = require('./scriptTypes')
const { RealmInfo, RealmType, WindowRealmInfo } = require('./realmInfo')
const { RemoteValue } = require('./protocolValue')
const { Source } = require('./scriptTypes')
const { WebDriverError } = require('../lib/error')
class ScriptManager {
constructor(driver) {
this._driver = driver
}
async init(browsingContextId) {
if (!(await this._driver.getCapabilities()).get('webSocketUrl')) {
throw Error('WebDriver instance must support BiDi protocol')
}
this.bidi = await this._driver.getBidi()
this._browsingContextId = browsingContextId
}
async disownRealmScript(realmId, handles) {
const params = {
method: 'script.disown',
params: {
handles: handles,
target: {
realm: realmId,
},
},
}
await this.bidi.send(params)
}
async disownBrowsingContextScript(browsingContextId, handles, sandbox = null) {
const params = {
method: 'script.disown',
params: {
handles: handles,
target: {
context: browsingContextId,
},
},
}
if (sandbox != null) {
params.params.target['sandbox'] = sandbox
}
await this.bidi.send(params)
}
async callFunctionInRealm(
realmId,
functionDeclaration,
awaitPromise,
argumentValueList = null,
thisParameter = null,
resultOwnership = null,
) {
const params = this.getCallFunctionParams(
'realm',
realmId,
null,
functionDeclaration,
awaitPromise,
argumentValueList,
thisParameter,
resultOwnership,
)
const command = {
method: 'script.callFunction',
params,
}
let response = await this.bidi.send(command)
return this.createEvaluateResult(response)
}
async callFunctionInBrowsingContext(
browsingContextId,
functionDeclaration,
awaitPromise,
argumentValueList = null,
thisParameter = null,
resultOwnership = null,
sandbox = null,
) {
const params = this.getCallFunctionParams(
'contextTarget',
browsingContextId,
sandbox,
functionDeclaration,
awaitPromise,
argumentValueList,
thisParameter,
resultOwnership,
)
const command = {
method: 'script.callFunction',
params,
}
const response = await this.bidi.send(command)
return this.createEvaluateResult(response)
}
async evaluateFunctionInRealm(realmId, expression, awaitPromise, resultOwnership = null) {
const params = this.getEvaluateParams('realm', realmId, null, expression, awaitPromise, resultOwnership)
const command = {
method: 'script.evaluate',
params,
}
let response = await this.bidi.send(command)
return this.createEvaluateResult(response)
}
async evaluateFunctionInBrowsingContext(
browsingContextId,
expression,
awaitPromise,
resultOwnership = null,
sandbox = null,
) {
const params = this.getEvaluateParams(
'contextTarget',
browsingContextId,
sandbox,
expression,
awaitPromise,
resultOwnership,
)
const command = {
method: 'script.evaluate',
params,
}
let response = await this.bidi.send(command)
return this.createEvaluateResult(response)
}
async addPreloadScript(functionDeclaration, argumentValueList = [], sandbox = null) {
const params = {
functionDeclaration: functionDeclaration,
arguments: argumentValueList,
sandbox: sandbox,
}
const command = {
method: 'script.addPreloadScript',
params,
}
let response = await this.bidi.send(command)
return response.result.script
}
async removePreloadScript(script) {
const params = { script: script }
const command = {
method: 'script.removePreloadScript',
params,
}
let response = await this.bidi.send(command)
if ('error' in response) {
throw new WebDriverError(response.error)
}
return response.result
}
getCallFunctionParams(
targetType,
id,
sandbox,
functionDeclaration,
awaitPromise,
argumentValueList = null,
thisParameter = null,
resultOwnership = null,
) {
const params = {
functionDeclaration: functionDeclaration,
awaitPromise: awaitPromise,
}
if (targetType === 'contextTarget') {
if (sandbox != null) {
params['target'] = { context: id, sandbox: sandbox }
} else {
params['target'] = { context: id }
}
} else {
params['target'] = { realm: id }
}
if (argumentValueList != null) {
let argumentParams = []
argumentValueList.forEach((argumentValue) => {
argumentParams.push(argumentValue.asMap())
})
params['arguments'] = argumentParams
}
if (thisParameter != null) {
params['this'] = thisParameter
}
if (resultOwnership != null) {
params['resultOwnership'] = resultOwnership
}
return params
}
getEvaluateParams(targetType, id, sandbox, expression, awaitPromise, resultOwnership = null) {
const params = {
expression: expression,
awaitPromise: awaitPromise,
}
if (targetType === 'contextTarget') {
if (sandbox != null) {
params['target'] = { context: id, sandbox: sandbox }
} else {
params['target'] = { context: id }
}
} else {
params['target'] = { realm: id }
}
if (resultOwnership != null) {
params['resultOwnership'] = resultOwnership
}
return params
}
createEvaluateResult(response) {
const type = response.result.type
const realmId = response.result.realm
let evaluateResult
if (type === EvaluateResultType.SUCCESS) {
const result = response.result.result
evaluateResult = new EvaluateResultSuccess(realmId, new RemoteValue(result))
} else {
const exceptionDetails = response.result.exceptionDetails
evaluateResult = new EvaluateResultException(realmId, new ExceptionDetails(exceptionDetails))
}
return evaluateResult
}
realmInfoMapper(realms) {
const realmsList = []
realms.forEach((realm) => {
realmsList.push(RealmInfo.fromJson(realm))
})
return realmsList
}
async getAllRealms() {
const command = {
method: 'script.getRealms',
params: {},
}
let response = await this.bidi.send(command)
return this.realmInfoMapper(response.result.realms)
}
async getRealmsByType(type) {
const command = {
method: 'script.getRealms',
params: { type: type },
}
let response = await this.bidi.send(command)
return this.realmInfoMapper(response.result.realms)
}
async getRealmsInBrowsingContext(browsingContext) {
const command = {
method: 'script.getRealms',
params: { context: browsingContext },
}
let response = await this.bidi.send(command)
return this.realmInfoMapper(response.result.realms)
}
async getRealmsInBrowsingContextByType(browsingContext, type) {
const command = {
method: 'script.getRealms',
params: { context: browsingContext, type: type },
}
let response = await this.bidi.send(command)
return this.realmInfoMapper(response.result.realms)
}
async onMessage(callback) {
await this.subscribeAndHandleEvent('script.message', callback)
}
async onRealmCreated(callback) {
await this.subscribeAndHandleEvent('script.realmCreated', callback)
}
async onRealmDestroyed(callback) {
await this.subscribeAndHandleEvent('script.realmDestroyed', callback)
}
async subscribeAndHandleEvent(eventType, callback) {
if (this._browsingContextIds != null) {
await this.bidi.subscribe(eventType, this._browsingContextIds)
} else {
await this.bidi.subscribe(eventType)
}
await this._on(callback)
}
async _on(callback) {
this.ws = await this.bidi.socket
this.ws.on('message', (event) => {
const { params } = JSON.parse(Buffer.from(event.toString()))
if (params) {
let response = null
if ('channel' in params) {
response = new Message(params.channel, new RemoteValue(params.data), new Source(params.source))
} else if ('realm' in params) {
if (params.type === RealmType.WINDOW) {
response = new WindowRealmInfo(params.realm, params.origin, params.type, params.context, params.sandbox)
} else if (params.realm !== null && params.type !== null) {
response = new RealmInfo(params.realm, params.origin, params.type)
} else if (params.realm !== null) {
response = params.realm
}
}
callback(response)
}
})
}
}
async function getScriptManagerInstance(browsingContextId, driver) {
let instance = new ScriptManager(driver)
await instance.init(browsingContextId)
return instance
}
module.exports = getScriptManagerInstance