blob: b8022898c8870df0bb633dfd33c9dc76db4242ce [file] [log] [blame]
// Copyright 2015 Google. All rights reserved. Use of this source code is
// governed by a BSD-style license that can be found in the LICENSE file.
import 'dart:async';
import 'dart:math';
import '../webkit_inspection_protocol.dart';
class WipRuntime extends WipDomain {
WipRuntime(WipConnection connection) : super(connection);
/// Enables reporting of execution contexts creation by means of
/// executionContextCreated event. When the reporting gets enabled the event
/// will be sent immediately for each existing execution context.
Future<WipResponse> enable() => sendCommand('Runtime.enable');
/// Disables reporting of execution contexts creation.
Future<WipResponse> disable() => sendCommand('Runtime.disable');
/// Evaluates expression on global object.
///
/// - `returnByValue`: Whether the result is expected to be a JSON object that
/// should be sent by value.
/// - `contextId`: Specifies in which execution context to perform evaluation.
/// If the parameter is omitted the evaluation will be performed in the
/// context of the inspected page.
/// - `awaitPromise`: Whether execution should await for resulting value and
/// return once awaited promise is resolved.
Future<RemoteObject> evaluate(
String expression, {
bool returnByValue,
int contextId,
bool awaitPromise,
}) async {
Map<String, dynamic> params = {
'expression': expression,
};
if (returnByValue != null) {
params['returnByValue'] = returnByValue;
}
if (contextId != null) {
params['contextId'] = contextId;
}
if (awaitPromise != null) {
params['awaitPromise'] = awaitPromise;
}
final WipResponse response =
await sendCommand('Runtime.evaluate', params: params);
if (response.result.containsKey('exceptionDetails')) {
throw new ExceptionDetails(
response.result['exceptionDetails'] as Map<String, dynamic>);
} else {
return new RemoteObject(
response.result['result'] as Map<String, dynamic>);
}
}
/// Calls function with given declaration on the given object. Object group of
/// the result is inherited from the target object.
///
/// Each element in [arguments] must be either a [RemoteObject] or a primitive
/// object (int, String, double, bool).
Future<RemoteObject> callFunctionOn(
String functionDeclaration, {
String objectId,
List<dynamic> arguments,
bool returnByValue,
int executionContextId,
}) async {
Map<String, dynamic> params = {
'functionDeclaration': functionDeclaration,
};
if (objectId != null) {
params['objectId'] = objectId;
}
if (returnByValue != null) {
params['returnByValue'] = returnByValue;
}
if (executionContextId != null) {
params['executionContextId'] = executionContextId;
}
if (arguments != null) {
// Convert a list of RemoteObjects and primitive values to CallArguments.
params['arguments'] = arguments.map((dynamic value) {
if (value is RemoteObject) {
return {'objectId': value.objectId};
} else {
return {'value': value};
}
}).toList();
}
final WipResponse response =
await sendCommand('Runtime.callFunctionOn', params: params);
if (response.result.containsKey('exceptionDetails')) {
throw new ExceptionDetails(
response.result['exceptionDetails'] as Map<String, dynamic>);
} else {
return new RemoteObject(
response.result['result'] as Map<String, dynamic>);
}
}
/// Returns the JavaScript heap usage. It is the total usage of the
/// corresponding isolate not scoped to a particular Runtime.
@experimental
Future<HeapUsage> getHeapUsage() async {
final WipResponse response = await sendCommand('Runtime.getHeapUsage');
return HeapUsage(response.result);
}
/// Returns the isolate id.
@experimental
Future<String> getIsolateId() async {
return (await sendCommand('Runtime.getIsolateId')).result['id'] as String;
}
/// Returns properties of a given object. Object group of the result is
/// inherited from the target object.
///
/// objectId: Identifier of the object to return properties for.
///
/// ownProperties: If true, returns properties belonging only to the element
/// itself, not to its prototype chain.
Future<List<PropertyDescriptor>> getProperties(
RemoteObject object, {
bool ownProperties,
}) async {
Map<String, dynamic> params = {
'objectId': object.objectId,
};
if (ownProperties != null) {
params['ownProperties'] = ownProperties;
}
final WipResponse response =
await sendCommand('Runtime.getProperties', params: params);
if (response.result.containsKey('exceptionDetails')) {
throw new ExceptionDetails(
response.result['exceptionDetails'] as Map<String, dynamic>);
} else {
List locations = response.result['result'];
return List.from(locations.map((map) => PropertyDescriptor(map)));
}
}
Stream<ConsoleAPIEvent> get onConsoleAPICalled => eventStream(
'Runtime.consoleAPICalled',
(WipEvent event) => new ConsoleAPIEvent(event.json));
Stream<ExceptionThrownEvent> get onExceptionThrown => eventStream(
'Runtime.exceptionThrown',
(WipEvent event) => new ExceptionThrownEvent(event.json));
/// Issued when new execution context is created.
Stream<ExecutionContextDescription> get onExecutionContextCreated =>
eventStream(
'Runtime.executionContextCreated',
(WipEvent event) =>
new ExecutionContextDescription(event.params['context']));
/// Issued when execution context is destroyed.
Stream<String> get onExecutionContextDestroyed => eventStream(
'Runtime.executionContextDestroyed',
(WipEvent event) => event.params['executionContextId']);
/// Issued when all executionContexts were cleared in browser.
Stream get onExecutionContextsCleared => eventStream(
'Runtime.executionContextsCleared', (WipEvent event) => event);
}
// TODO: stackTrace, StackTrace, Stack trace captured when the call was made.
class ConsoleAPIEvent extends WipEvent {
ConsoleAPIEvent(Map<String, dynamic> json) : super(json);
/// Type of the call. Allowed values: log, debug, info, error, warning, dir,
/// dirxml, table, trace, clear, startGroup, startGroupCollapsed, endGroup,
/// assert, profile, profileEnd.
String get type => params['type'] as String;
/// Call timestamp.
num get timestamp => params['timestamp'] as num;
/// Call arguments.
List<RemoteObject> get args => (params['args'] as List)
.map((m) => new RemoteObject(m as Map<String, dynamic>))
.toList();
}
/// Description of an isolated world.
class ExecutionContextDescription {
final Map<String, dynamic> json;
ExecutionContextDescription(this.json);
/// Unique id of the execution context. It can be used to specify in which
/// execution context script evaluation should be performed.
int get id => json['id'] as int;
/// Execution context origin.
String get origin => json['origin'];
/// Human readable name describing given context.
String get name => json['name'];
}
class ExceptionThrownEvent extends WipEvent {
ExceptionThrownEvent(Map<String, dynamic> json) : super(json);
/// Timestamp of the exception.
int get timestamp => params['timestamp'] as int;
ExceptionDetails get exceptionDetails =>
new ExceptionDetails(params['exceptionDetails'] as Map<String, dynamic>);
}
class ExceptionDetails implements Exception {
final Map<String, dynamic> json;
ExceptionDetails(this.json);
/// Exception id.
int get exceptionId => json['exceptionId'] as int;
/// Exception text, which should be used together with exception object when
/// available.
String get text => json['text'] as String;
/// Line number of the exception location (0-based).
int get lineNumber => json['lineNumber'] as int;
/// Column number of the exception location (0-based).
int get columnNumber => json['columnNumber'] as int;
/// URL of the exception location, to be used when the script was not
/// reported.
@optional
String get url => json['url'] as String;
/// Script ID of the exception location.
@optional
String get scriptId => json['scriptId'] as String;
/// JavaScript stack trace if available.
@optional
StackTrace get stackTrace => json['stackTrace'] == null
? null
: new StackTrace(json['stackTrace'] as Map<String, dynamic>);
/// Exception object if available.
@optional
RemoteObject get exception => json['exception'] == null
? null
: new RemoteObject(json['exception'] as Map<String, dynamic>);
String toString() => '$text, $url, $scriptId, $lineNumber, $exception';
}
/// Call frames for assertions or error messages.
class StackTrace {
final Map<String, dynamic> json;
StackTrace(this.json);
/// String label of this stack trace. For async traces this may be a name of
/// the function that initiated the async call.
@optional
String get description => json['description'] as String;
List<CallFrame> get callFrames => (json['callFrames'] as List)
.map((m) => new CallFrame(m as Map<String, dynamic>))
.toList();
// TODO: parent, StackTrace, Asynchronous JavaScript stack trace that preceded
// this stack, if available.
List<String> printFrames() {
List<CallFrame> frames = callFrames;
int width = frames.fold(0, (int val, CallFrame frame) {
return max(val, frame.functionName.length);
});
return frames.map((CallFrame frame) {
return '${frame.functionName}()'.padRight(width + 2) +
' ${frame.url} ${frame.lineNumber}:${frame.columnNumber}';
}).toList();
}
String toString() => callFrames.map((f) => ' $f').join('\n');
}
/// Stack entry for runtime errors and assertions.
class CallFrame {
final Map<String, dynamic> json;
CallFrame(this.json);
/// JavaScript function name.
String get functionName => json['functionName'] as String;
/// JavaScript script id.
String get scriptId => json['scriptId'] as String;
/// JavaScript script name or url.
String get url => json['url'] as String;
/// JavaScript script line number (0-based).
int get lineNumber => json['lineNumber'] as int;
/// JavaScript script column number (0-based).
int get columnNumber => json['columnNumber'] as int;
String toString() => '$functionName() ($url $lineNumber:$columnNumber)';
}
/// Mirror object referencing original JavaScript object.
class RemoteObject {
final Map<String, dynamic> json;
RemoteObject(this.json);
/// Object type.
///
/// Allowed Values: object, function, undefined, string, number, boolean,
/// symbol, bigint, wasm.
String get type => json['type'] as String;
/// Object subtype hint. Specified for object or wasm type values only.
///
/// Allowed Values: array, null, node, regexp, date, map, set, weakmap,
/// weakset, iterator, generator, error, proxy, promise, typedarray,
/// arraybuffer, dataview, i32, i64, f32, f64, v128, anyref.
String get subtype => json['subtype'] as String;
/// Object class (constructor) name.
///
/// Specified for object type values only.
String get className => json['className'] as String;
/// Remote object value in case of primitive values or JSON values (if it was
/// requested). (optional)
Object get value => json['value'];
/// String representation of the object. (optional)
String get description => json['description'] as String;
/// Unique object identifier (for non-primitive values). (optional)
String get objectId => json['objectId'] as String;
@override
String toString() => '$type $value';
}
/// Returns the JavaScript heap usage. It is the total usage of the
/// corresponding isolate not scoped to a particular Runtime.
class HeapUsage {
final Map<String, dynamic> json;
HeapUsage(this.json);
/// Used heap size in bytes.
int get usedSize => json['usedSize'];
/// Allocated heap size in bytes.
int get totalSize => json['totalSize'];
@override
String toString() => '$usedSize of $totalSize';
}
/// Object property descriptor.
class PropertyDescriptor {
final Map<String, dynamic> json;
PropertyDescriptor(this.json);
/// Property name or symbol description.
String get name => json['name'];
/// The value associated with the property.
RemoteObject get value =>
json['value'] != null ? RemoteObject(json['value']) : null;
/// True if the value associated with the property may be changed (data
/// descriptors only).
bool get writable => json['writable'];
/// True if the type of this property descriptor may be changed and if the
/// property may be deleted from the corresponding object.
bool get configurable => json['configurable'];
/// True if this property shows up during enumeration of the properties on the
/// corresponding object.
bool get enumerable => json['enumerable'];
/// True if the result was thrown during the evaluation.
bool get wasThrown => json['wasThrown'];
/// True if the property is owned for the object.
bool get isOwn => json['isOwn'];
}