moved to root
This commit is contained in:
@@ -0,0 +1,561 @@
|
||||
/*
|
||||
* Copyright (C) 2011 Google Inc. All rights reserved.
|
||||
* Copyright (C) 2013, 2015, 2016 Apple Inc. All rights reserved.
|
||||
* Copyright (C) 2014 University of Washington.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Google Inc. nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
InspectorBackendClass = class InspectorBackendClass
|
||||
{
|
||||
constructor()
|
||||
{
|
||||
this._registeredDomains = {};
|
||||
this._activeDomains = {};
|
||||
|
||||
this._customTracer = null;
|
||||
this._defaultTracer = new WI.LoggingProtocolTracer;
|
||||
this._activeTracers = [this._defaultTracer];
|
||||
|
||||
// FIXME: <https://webkit.org/b/213632> Web Inspector: release unused backend domains/events/commands once the debuggable type is known
|
||||
this._supportedDomainsForTargetType = new Multimap;
|
||||
this._supportedCommandParameters = new Map;
|
||||
this._supportedEventParameters = new Map;
|
||||
|
||||
WI.settings.protocolAutoLogMessages.addEventListener(WI.Setting.Event.Changed, this._startOrStopAutomaticTracing, this);
|
||||
WI.settings.protocolAutoLogTimeStats.addEventListener(WI.Setting.Event.Changed, this._startOrStopAutomaticTracing, this);
|
||||
this._startOrStopAutomaticTracing();
|
||||
|
||||
this.currentDispatchState = {
|
||||
event: null,
|
||||
request: null,
|
||||
response: null,
|
||||
};
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
// This should only be used for getting enum values (`InspectorBackend.Enum.Domain.Type.Item`).
|
||||
// Domain/Command/Event feature checking should use one of the `has*` functions below.
|
||||
get Enum()
|
||||
{
|
||||
// Enums should not be conditionally enabled by debuggable and/or target type.
|
||||
return this._registeredDomains;
|
||||
}
|
||||
|
||||
// It's still possible to set this flag on InspectorBackend to just
|
||||
// dump protocol traffic as it happens. For more complex uses of
|
||||
// protocol data, install a subclass of WI.ProtocolTracer.
|
||||
set dumpInspectorProtocolMessages(value)
|
||||
{
|
||||
// Implicitly cause automatic logging to start if it's allowed.
|
||||
WI.settings.protocolAutoLogMessages.value = value;
|
||||
|
||||
this._defaultTracer.dumpMessagesToConsole = value;
|
||||
}
|
||||
|
||||
get dumpInspectorProtocolMessages()
|
||||
{
|
||||
return WI.settings.protocolAutoLogMessages.value;
|
||||
}
|
||||
|
||||
set dumpInspectorTimeStats(value)
|
||||
{
|
||||
WI.settings.protocolAutoLogTimeStats.value = value;
|
||||
|
||||
if (!this.dumpInspectorProtocolMessages)
|
||||
this.dumpInspectorProtocolMessages = true;
|
||||
|
||||
this._defaultTracer.dumpTimingDataToConsole = value;
|
||||
}
|
||||
|
||||
get dumpInspectorTimeStats()
|
||||
{
|
||||
return WI.settings.protocolAutoLogTimeStats.value;
|
||||
}
|
||||
|
||||
set filterMultiplexingBackendInspectorProtocolMessages(value)
|
||||
{
|
||||
WI.settings.protocolFilterMultiplexingBackendMessages.value = value;
|
||||
|
||||
this._defaultTracer.filterMultiplexingBackend = value;
|
||||
}
|
||||
|
||||
get filterMultiplexingBackendInspectorProtocolMessages()
|
||||
{
|
||||
return WI.settings.protocolFilterMultiplexingBackendMessages.value;
|
||||
}
|
||||
|
||||
set customTracer(tracer)
|
||||
{
|
||||
console.assert(!tracer || tracer instanceof WI.ProtocolTracer, tracer);
|
||||
console.assert(!tracer || tracer !== this._defaultTracer, tracer);
|
||||
|
||||
// Bail early if no state change is to be made.
|
||||
if (!tracer && !this._customTracer)
|
||||
return;
|
||||
|
||||
if (tracer === this._customTracer)
|
||||
return;
|
||||
|
||||
if (tracer === this._defaultTracer)
|
||||
return;
|
||||
|
||||
if (this._customTracer)
|
||||
this._customTracer.logFinished();
|
||||
|
||||
this._customTracer = tracer;
|
||||
this._activeTracers = [this._defaultTracer];
|
||||
|
||||
if (this._customTracer) {
|
||||
this._customTracer.logStarted();
|
||||
this._activeTracers.push(this._customTracer);
|
||||
}
|
||||
}
|
||||
|
||||
get activeTracers()
|
||||
{
|
||||
return this._activeTracers;
|
||||
}
|
||||
|
||||
registerDomain(domainName, targetTypes)
|
||||
{
|
||||
targetTypes = targetTypes || WI.TargetType.all;
|
||||
for (let targetType of targetTypes)
|
||||
this._supportedDomainsForTargetType.add(targetType, domainName);
|
||||
|
||||
this._registeredDomains[domainName] = new InspectorBackend.Domain(domainName);
|
||||
}
|
||||
|
||||
registerVersion(domainName, version)
|
||||
{
|
||||
let domain = this._registeredDomains[domainName];
|
||||
domain.VERSION = version;
|
||||
}
|
||||
|
||||
registerEnum(qualifiedName, enumValues)
|
||||
{
|
||||
let [domainName, enumName] = qualifiedName.split(".");
|
||||
let domain = this._registeredDomains[domainName];
|
||||
domain._addEnum(enumName, enumValues);
|
||||
}
|
||||
|
||||
registerCommand(qualifiedName, targetTypes, callSignature, replySignature)
|
||||
{
|
||||
let [domainName, commandName] = qualifiedName.split(".");
|
||||
let domain = this._registeredDomains[domainName];
|
||||
domain._addCommand(targetTypes, new InspectorBackend.Command(qualifiedName, commandName, callSignature, replySignature));
|
||||
}
|
||||
|
||||
registerEvent(qualifiedName, targetTypes, signature)
|
||||
{
|
||||
let [domainName, eventName] = qualifiedName.split(".");
|
||||
let domain = this._registeredDomains[domainName];
|
||||
domain._addEvent(targetTypes, new InspectorBackend.Event(qualifiedName, eventName, signature));
|
||||
}
|
||||
|
||||
registerDispatcher(domainName, dispatcher)
|
||||
{
|
||||
let domain = this._registeredDomains[domainName];
|
||||
domain._addDispatcher(dispatcher);
|
||||
}
|
||||
|
||||
activateDomain(domainName, debuggableTypes)
|
||||
{
|
||||
// FIXME: <https://webkit.org/b/201150> Web Inspector: remove "extra domains" concept now that domains can be added based on the debuggable type
|
||||
|
||||
// Ask `WI.sharedApp` (if it exists) as it may have a different debuggable type if extra
|
||||
// domains were activated, which is the only other time this will be called.
|
||||
let currentDebuggableType = WI.sharedApp?.debuggableType || InspectorFrontendHost.debuggableInfo.debuggableType;
|
||||
|
||||
if (debuggableTypes && !debuggableTypes.includes(currentDebuggableType))
|
||||
return;
|
||||
|
||||
console.assert(domainName in this._registeredDomains);
|
||||
console.assert(!(domainName in this._activeDomains));
|
||||
|
||||
let domain = this._registeredDomains[domainName];
|
||||
this._activeDomains[domainName] = domain;
|
||||
|
||||
let supportedTargetTypes = WI.DebuggableType.supportedTargetTypes(currentDebuggableType);
|
||||
|
||||
for (let [targetType, command] of domain._supportedCommandsForTargetType) {
|
||||
if (!supportedTargetTypes.has(targetType))
|
||||
continue;
|
||||
|
||||
let parameters = this._supportedCommandParameters.get(command._qualifiedName);
|
||||
if (!parameters) {
|
||||
parameters = new Set;
|
||||
this._supportedCommandParameters.set(command._qualifiedName, parameters);
|
||||
}
|
||||
parameters.addAll(command._callSignature.map((item) => item.name));
|
||||
}
|
||||
|
||||
for (let [targetType, event] of domain._supportedEventsForTargetType) {
|
||||
if (!supportedTargetTypes.has(targetType))
|
||||
continue;
|
||||
|
||||
let parameters = this._supportedEventParameters.get(event._qualifiedName);
|
||||
if (!parameters) {
|
||||
parameters = new Set;
|
||||
this._supportedEventParameters.set(event._qualifiedName, parameters);
|
||||
}
|
||||
parameters.addAll(event._parameterNames);
|
||||
}
|
||||
}
|
||||
|
||||
dispatch(message)
|
||||
{
|
||||
InspectorBackend.backendConnection.dispatch(message);
|
||||
}
|
||||
|
||||
runAfterPendingDispatches(callback)
|
||||
{
|
||||
if (!WI.mainTarget) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME: Should this respect pending dispatches in all connections?
|
||||
WI.mainTarget.connection.runAfterPendingDispatches(callback);
|
||||
}
|
||||
|
||||
supportedDomainsForTargetType(type)
|
||||
{
|
||||
console.assert(WI.TargetType.all.includes(type), "Unknown target type", type);
|
||||
|
||||
return this._supportedDomainsForTargetType.get(type) || new Set;
|
||||
}
|
||||
|
||||
hasDomain(domainName)
|
||||
{
|
||||
console.assert(!domainName.includes(".") && !domainName.endsWith("Agent"));
|
||||
|
||||
return domainName in this._activeDomains;
|
||||
}
|
||||
|
||||
hasCommand(qualifiedName, parameterName)
|
||||
{
|
||||
console.assert(qualifiedName.includes(".") && !qualifiedName.includes("Agent."));
|
||||
|
||||
let parameters = this._supportedCommandParameters.get(qualifiedName);
|
||||
if (!parameters)
|
||||
return false;
|
||||
|
||||
return parameterName === undefined || parameters.has(parameterName);
|
||||
}
|
||||
|
||||
hasEvent(qualifiedName, parameterName)
|
||||
{
|
||||
console.assert(qualifiedName.includes(".") && !qualifiedName.includes("Agent."));
|
||||
|
||||
let parameters = this._supportedEventParameters.get(qualifiedName);
|
||||
if (!parameters)
|
||||
return false;
|
||||
|
||||
return parameterName === undefined || parameters.has(parameterName);
|
||||
}
|
||||
|
||||
getVersion(domainName)
|
||||
{
|
||||
console.assert(!domainName.includes(".") && !domainName.endsWith("Agent"));
|
||||
|
||||
let domain = this._activeDomains[domainName];
|
||||
if (domain && "VERSION" in domain)
|
||||
return domain.VERSION;
|
||||
|
||||
return -Infinity;
|
||||
}
|
||||
|
||||
invokeCommand(qualifiedName, targetType, connection, commandArguments, callback)
|
||||
{
|
||||
let [domainName, commandName] = qualifiedName.split(".");
|
||||
|
||||
let domain = this._activeDomains[domainName];
|
||||
return domain._invokeCommand(commandName, targetType, connection, commandArguments, callback);
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_makeAgent(domainName, target)
|
||||
{
|
||||
let domain = this._activeDomains[domainName];
|
||||
return domain._makeAgent(target);
|
||||
}
|
||||
|
||||
_startOrStopAutomaticTracing()
|
||||
{
|
||||
this._defaultTracer.dumpMessagesToConsole = this.dumpInspectorProtocolMessages;
|
||||
this._defaultTracer.dumpTimingDataToConsole = this.dumpTimingDataToConsole;
|
||||
this._defaultTracer.filterMultiplexingBackend = this.filterMultiplexingBackendInspectorProtocolMessages;
|
||||
}
|
||||
};
|
||||
|
||||
InspectorBackend = new InspectorBackendClass;
|
||||
|
||||
InspectorBackend.Domain = class InspectorBackendDomain
|
||||
{
|
||||
constructor(domainName)
|
||||
{
|
||||
this._domainName = domainName;
|
||||
|
||||
// Enums are stored directly on the Domain instance using their unqualified
|
||||
// type name as the property. Thus, callers can write: Domain.EnumType.
|
||||
|
||||
this._dispatcher = null;
|
||||
|
||||
// FIXME: <https://webkit.org/b/213632> Web Inspector: release unused backend domains/events/commands once the debuggable type is known
|
||||
this._supportedCommandsForTargetType = new Multimap;
|
||||
this._supportedEventsForTargetType = new Multimap;
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_addEnum(enumName, enumValues)
|
||||
{
|
||||
console.assert(!(enumName in this));
|
||||
this[enumName] = enumValues;
|
||||
}
|
||||
|
||||
_addCommand(targetTypes, command)
|
||||
{
|
||||
targetTypes = targetTypes || WI.TargetType.all;
|
||||
for (let type of targetTypes)
|
||||
this._supportedCommandsForTargetType.add(type, command);
|
||||
}
|
||||
|
||||
_addEvent(targetTypes, event)
|
||||
{
|
||||
targetTypes = targetTypes || WI.TargetType.all;
|
||||
for (let type of targetTypes)
|
||||
this._supportedEventsForTargetType.add(type, event);
|
||||
}
|
||||
|
||||
_addDispatcher(dispatcher)
|
||||
{
|
||||
console.assert(!this._dispatcher);
|
||||
this._dispatcher = dispatcher;
|
||||
}
|
||||
|
||||
_makeAgent(target)
|
||||
{
|
||||
let commands = this._supportedCommandsForTargetType.get(target.type) || new Set;
|
||||
let events = this._supportedEventsForTargetType.get(target.type) || new Set;
|
||||
return new InspectorBackend.Agent(target, commands, events, this._dispatcher);
|
||||
}
|
||||
|
||||
_invokeCommand(commandName, targetType, connection, commandArguments, callback)
|
||||
{
|
||||
let commands = this._supportedCommandsForTargetType.get(targetType);
|
||||
for (let command of commands) {
|
||||
if (command._commandName === commandName)
|
||||
return command._makeCallable(connection).invoke(commandArguments, callback);
|
||||
}
|
||||
|
||||
console.assert();
|
||||
}
|
||||
};
|
||||
|
||||
InspectorBackend.Agent = class InspectorBackendAgent
|
||||
{
|
||||
constructor(target, commands, events, dispatcher)
|
||||
{
|
||||
// Commands are stored directly on the Agent instance using their unqualified
|
||||
// method name as the property. Thus, callers can write: DomainAgent.commandName().
|
||||
for (let command of commands) {
|
||||
this[command._commandName] = command._makeCallable(target.connection);
|
||||
target._supportedCommandParameters.set(command._qualifiedName, command);
|
||||
}
|
||||
|
||||
this._events = {};
|
||||
for (let event of events) {
|
||||
this._events[event._eventName] = event;
|
||||
target._supportedEventParameters.set(event._qualifiedName, event);
|
||||
}
|
||||
|
||||
this._dispatcher = dispatcher ? new dispatcher(target) : null;
|
||||
}
|
||||
};
|
||||
|
||||
InspectorBackend.Dispatcher = class InspectorBackendDispatcher
|
||||
{
|
||||
constructor(target)
|
||||
{
|
||||
console.assert(target instanceof WI.Target);
|
||||
|
||||
this._target = target;
|
||||
}
|
||||
};
|
||||
|
||||
InspectorBackend.Command = class InspectorBackendCommand
|
||||
{
|
||||
constructor(qualifiedName, commandName, callSignature, replySignature)
|
||||
{
|
||||
this._qualifiedName = qualifiedName;
|
||||
this._commandName = commandName;
|
||||
this._callSignature = callSignature || [];
|
||||
this._replySignature = replySignature || [];
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_hasParameter(parameterName)
|
||||
{
|
||||
return this._callSignature.some((item) => item.name === parameterName);
|
||||
}
|
||||
|
||||
_makeCallable(connection)
|
||||
{
|
||||
let instance = new InspectorBackend.Callable(this, connection);
|
||||
|
||||
function callable() {
|
||||
console.assert(this instanceof InspectorBackend.Agent);
|
||||
return instance._invokeWithArguments.call(instance, Array.from(arguments));
|
||||
}
|
||||
callable._instance = instance;
|
||||
Object.setPrototypeOf(callable, InspectorBackend.Callable.prototype);
|
||||
return callable;
|
||||
}
|
||||
};
|
||||
|
||||
InspectorBackend.Event = class InspectorBackendEvent
|
||||
{
|
||||
constructor(qualifiedName, eventName, parameterNames)
|
||||
{
|
||||
this._qualifiedName = qualifiedName;
|
||||
this._eventName = eventName;
|
||||
this._parameterNames = parameterNames || [];
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_hasParameter(parameterName)
|
||||
{
|
||||
return this._parameterNames.includes(parameterName);
|
||||
}
|
||||
};
|
||||
|
||||
// InspectorBackend.Callable can't use ES6 classes because of its trampoline nature.
|
||||
// But we can use strict mode to get stricter handling of the code inside its functions.
|
||||
InspectorBackend.Callable = function(command, connection)
|
||||
{
|
||||
"use strict";
|
||||
|
||||
this._command = command;
|
||||
this._connection = connection;
|
||||
|
||||
this._instance = this;
|
||||
};
|
||||
|
||||
// As part of the workaround to make commands callable, these functions use `this._instance`.
|
||||
// `this` could refer to the callable trampoline, or the InspectorBackend.Callable instance.
|
||||
InspectorBackend.Callable.prototype = {
|
||||
__proto__: Function.prototype,
|
||||
|
||||
// Public
|
||||
|
||||
invoke(commandArguments, callback)
|
||||
{
|
||||
"use strict";
|
||||
|
||||
let command = this._instance._command;
|
||||
let connection = this._instance._connection;
|
||||
|
||||
function deliverFailure(message) {
|
||||
console.error(`Protocol Error: ${message}`);
|
||||
if (callback)
|
||||
setTimeout(callback.bind(null, message), 0);
|
||||
else
|
||||
return Promise.reject(new Error(message));
|
||||
}
|
||||
|
||||
if (typeof commandArguments !== "object")
|
||||
return deliverFailure(`invoke expects an object for command arguments but its type is '${typeof commandArguments}'.`);
|
||||
|
||||
let parameters = {};
|
||||
for (let {name, type, optional} of command._callSignature) {
|
||||
if (!(name in commandArguments) && !optional)
|
||||
return deliverFailure(`Missing argument '${name}' for command '${command._qualifiedName}'.`);
|
||||
|
||||
let value = commandArguments[name];
|
||||
if (optional && value === undefined)
|
||||
continue;
|
||||
|
||||
if (typeof value !== type)
|
||||
return deliverFailure(`Invalid type of argument '${name}' for command '${command._qualifiedName}' call. It must be '${type}' but it is '${typeof value}'.`);
|
||||
|
||||
parameters[name] = value;
|
||||
}
|
||||
|
||||
if (typeof callback === "function")
|
||||
connection._sendCommandToBackendWithCallback(command, parameters, callback);
|
||||
else
|
||||
return connection._sendCommandToBackendExpectingPromise(command, parameters);
|
||||
},
|
||||
|
||||
// Private
|
||||
|
||||
_invokeWithArguments(commandArguments)
|
||||
{
|
||||
"use strict";
|
||||
|
||||
let command = this._instance._command;
|
||||
let connection = this._instance._connection;
|
||||
let callback = typeof commandArguments.lastValue === "function" ? commandArguments.pop() : null;
|
||||
|
||||
function deliverFailure(message) {
|
||||
console.error(`Protocol Error: ${message}`);
|
||||
if (callback)
|
||||
setTimeout(callback.bind(null, message), 0);
|
||||
else
|
||||
return Promise.reject(new Error(message));
|
||||
}
|
||||
|
||||
let parameters = {};
|
||||
for (let {name, type, optional} of command._callSignature) {
|
||||
if (!commandArguments.length && !optional)
|
||||
return deliverFailure(`Invalid number of arguments for command '${command._qualifiedName}'.`);
|
||||
|
||||
let value = commandArguments.shift();
|
||||
if (optional && value === undefined)
|
||||
continue;
|
||||
|
||||
if (typeof value !== type)
|
||||
return deliverFailure(`Invalid type of argument '${name}' for command '${command._qualifiedName}' call. It must be '${type}' but it is '${typeof value}'.`);
|
||||
|
||||
parameters[name] = value;
|
||||
}
|
||||
|
||||
if (!callback && commandArguments.length === 1 && commandArguments[0] !== undefined)
|
||||
return deliverFailure(`Protocol Error: Optional callback argument for command '${command._qualifiedName}' call must be a function but its type is '${typeof commandArguments[0]}'.`);
|
||||
|
||||
if (callback)
|
||||
connection._sendCommandToBackendWithCallback(command, parameters, callback);
|
||||
else
|
||||
return connection._sendCommandToBackendExpectingPromise(command, parameters);
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user