| // 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 |