| // 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); |
| |
| List<CallFrame> get callFrames => (json['callFrames'] as List) |
| .map((m) => new CallFrame(m as Map<String, dynamic>)) |
| .toList(); |
| |
| /// 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; |
| |
| /// Asynchronous JavaScript stack trace that preceded this stack, if |
| /// available. |
| @optional |
| StackTrace get parent { |
| return json['callFrames'] == null ? null : StackTrace(json['callFrames']); |
| } |
| |
| 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. |
| /// |
| /// This class is for the 'runtime' domain. |
| 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']; |
| } |