blob: a6bf418966d713d698697f1512c670152e9e4d74 [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview using private properties isn't a Closure violation in tests.
* @suppress {accessControls}
*/
let id = 0;
function nextId(prefix) {
return (prefix || '') + ++id;
}
SDKTestRunner.connectToPage = function(targetName, pageMock, makeMainTarget) {
const mockTarget = SDK.targetManager.createTarget(
nextId('mock-target-'), targetName, pageMock.capabilities(), params => pageMock.createConnection(params));
if (makeMainTarget) {
SDK.targetManager._targets = SDK.targetManager._targets.filter(target => target !== mockTarget);
SDK.targetManager._targets.unshift(mockTarget);
}
return mockTarget;
};
SDKTestRunner.PageMock = class {
constructor(url) {
this._url = url;
this._capabilities = SDK.Target.Capability.DOM | SDK.Target.Capability.JS | SDK.Target.Capability.Browser;
this._enabledDomains = new Set();
this._mainFrame =
{id: nextId(), loaderId: nextId(), mimeType: 'text/html', securityOrigin: this._url, url: this._url};
this._executionContexts = [];
this._executionContexts.push(this._createExecutionContext(this._mainFrame, false));
this._scripts = [];
this._scriptContents = new Map();
this._dispatchMap = {
'Debugger.enable': this._debuggerEnable,
'Debugger.getScriptSource': this._debuggerGetScriptSource,
'Debugger.setBlackboxPatterns': (id, params) => this._sendResponse(id, {}),
'Runtime.enable': this._runtimeEnable,
'Page.enable': this._pageEnable,
'Page.getResourceTree': this._pageGetResourceTree
};
}
capabilities() {
return this._capabilities;
}
disableDOMCapability() {
this._capabilities = this._capabilities & ~SDK.Target.Capability.DOM;
}
createConnection(params) {
this._enabledDomains.clear();
this._connection = new MockPageConnection(this, params);
return this._connection;
}
evalScript(url, content, isContentScript) {
const id = nextId();
content += '\n//# sourceURL=' + url;
this._scriptContents.set(id, content);
let context = this._executionContexts.find(context => context.auxData.isDefault !== isContentScript);
if (!context) {
context = this._createExecutionContext(this._mainFrame, isContentScript);
this._fireEvent('Runtime.executionContextCreated', {context: context});
}
const text = new TextUtils.Text(content);
const script = {
scriptId: id,
url: url,
startLine: 0,
startColumn: 0,
endLine: text.lineCount(),
endColumn: text.lineAt(text.lineCount()).length - 1,
executionContextId: context.id,
hash: String.hashCode(content),
executionContextAuxData: context.auxData,
sourceMapURL: '',
hasSourceURL: true,
isLiveEdit: false,
isModule: false,
length: content.length
};
this._scripts.push(script);
this._fireEvent('Debugger.scriptParsed', script);
}
reload() {
this._fireEvent('Page.frameStartedLoading', {frameId: this._mainFrame.id});
for (const context of this._executionContexts)
this._fireEvent('Runtime.executionContextDestroyed', {executionContextId: context.id});
this._scripts = [];
this._scriptContents.clear();
this._executionContexts = [];
this._fireEvent('Runtime.executionContextsCleared', {});
this._executionContexts.push(this._createExecutionContext(this._mainFrame, false));
for (const context of this._executionContexts)
this._fireEvent('Runtime.executionContextCreated', {context: context});
this._fireEvent('Page.frameNavigated', {frame: this._mainFrame});
this._fireEvent('Page.loadEventFired', {timestamp: Date.now() / 1000});
this._fireEvent('Page.frameStoppedLoading', {frameId: this._mainFrame.id});
this._fireEvent('Page.domContentEventFired', {timestamp: Date.now() / 1000});
}
close() {
if (this._connection) {
this._connection.disconnect();
this._connection = null;
}
}
_createExecutionContext(frame, isContentScript) {
return {
id: nextId(),
auxData: {isDefault: !isContentScript, frameId: frame.id},
origin: frame.securityOrigin,
name: ''
};
}
_debuggerEnable(id, params) {
this._enabledDomains.add('Debugger');
this._sendResponse(id, {});
for (const script of this._scripts)
this._fireEvent('Debugger.scriptParsed', script);
}
_debuggerGetScriptSource(id, params) {
if (!this._scriptContents.has(params.scriptId)) {
this._sendResponse(id, undefined, {message: 'Can\'t get script content for id ' + params.scriptId, code: 1});
return;
}
const result = {scriptSource: this._scriptContents.get(params.scriptId)};
this._sendResponse(id, result);
}
_runtimeEnable(id, params) {
this._enabledDomains.add('Runtime');
this._sendResponse(id, {});
for (const context of this._executionContexts)
this._fireEvent('Runtime.executionContextCreated', {context: context});
}
_pageEnable(id, params) {
this._enabledDomains.add('Page');
this._sendResponse(id, {});
}
_pageGetResourceTree(id, params) {
const result = {frameTree: {frame: this._mainFrame, resources: []}};
this._sendResponse(id, result);
}
_isSupportedDomain(methodName) {
const domain = methodName.split('.')[0];
if (domain === 'Page')
return !!(this._capabilities & SDK.Target.Capability.DOM);
return true;
}
_dispatch(id, methodName, params, message) {
const handler = (this._isSupportedDomain(methodName) ? this._dispatchMap[methodName] : null);
if (handler)
return handler.call(this, id, params);
this._sendResponse(
id, undefined,
{message: 'Can\'t handle command ' + methodName, code: Protocol.InspectorBackend.DevToolsStubErrorCode});
}
_sendResponse(id, result, error) {
const message = {id: id, result: result, error: error};
this._connection.sendMessageToDevTools(message);
}
_fireEvent(methodName, params) {
const domain = methodName.split('.')[0];
if (!this._enabledDomains.has(domain))
return;
const message = {method: methodName, params: params};
this._connection.sendMessageToDevTools(message);
}
};
MockPageConnection = class {
constructor(page, params) {
this._page = page;
this._onMessage = params.onMessage;
this._onDisconnect = params.onDisconnect;
}
sendMessageToDevTools(message) {
setTimeout(() => this._onMessage.call(null, JSON.stringify(message)), 0);
}
sendMessage(message) {
const json = JSON.parse(message);
this._page._dispatch(json.id, json.method, json.params, message);
}
disconnect() {
this._onDisconnect.call(null, 'force disconnect');
this._onDisconnect = null;
this._onMessage = null;
return Promise.resolve();
}
};