diff --git a/changelog.md b/changelog.md index ec149a6..8ed2a8c 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,13 @@ # webkit_inspection_protocol.dart +## 0.7.0 +- Normalized all objects to expose a `json` field for raw access to the protocol information. +- Exposed Runtime.getProperties, Runtime.getHeapUsage, and Runtime.getIsolateId +- Exposed `DebuggerPausedEvent.hitBreakpoints` and `DebuggerPausedEvent.asyncStackTrace` +- Exposed `WipCallFrame.returnValue` +- Removed `WrappedWipEvent` (in favor of just using `WipEvent`) +- Removed `WipRemoteObject` (in favor of just using `RemoteObject`) + ## 0.6.0 - Add `onSend` and `onReceive` in `WipConnection` - Expose `onExecutionContextCreated`, `onExecutionContextDestroyed`, diff --git a/lib/src/console.dart b/lib/src/console.dart index 72d9e27..e1500d7 100644 --- a/lib/src/console.dart +++ b/lib/src/console.dart @@ -17,15 +17,15 @@ class WipConsole extends WipDomain { Stream get onMessage => eventStream( 'Console.messageAdded', - (WipEvent event) => new ConsoleMessageEvent(event)); + (WipEvent event) => new ConsoleMessageEvent(event.json)); Stream get onCleared => eventStream( 'Console.messagesCleared', - (WipEvent event) => new ConsoleClearedEvent(event)); + (WipEvent event) => new ConsoleClearedEvent(event.json)); } -class ConsoleMessageEvent extends WrappedWipEvent { - ConsoleMessageEvent(WipEvent event) : super(event); +class ConsoleMessageEvent extends WipEvent { + ConsoleMessageEvent(Map json) : super(json); Map get _message => params['message'] as Map; @@ -47,22 +47,22 @@ class ConsoleMessageEvent extends WrappedWipEvent { String toString() => text; } -class ConsoleClearedEvent extends WrappedWipEvent { - ConsoleClearedEvent(WipEvent event) : super(event); +class ConsoleClearedEvent extends WipEvent { + ConsoleClearedEvent(Map json) : super(json); } class WipConsoleCallFrame { - final Map _map; + final Map json; - WipConsoleCallFrame.fromMap(this._map); + WipConsoleCallFrame.fromMap(this.json); - int get columnNumber => _map['columnNumber'] as int; + int get columnNumber => json['columnNumber'] as int; - String get functionName => _map['functionName'] as String; + String get functionName => json['functionName'] as String; - int get lineNumber => _map['lineNumber'] as int; + int get lineNumber => json['lineNumber'] as int; - String get scriptId => _map['scriptId'] as String; + String get scriptId => json['scriptId'] as String; - String get url => _map['url'] as String; + String get url => json['url'] as String; } diff --git a/lib/src/debugger.dart b/lib/src/debugger.dart index 1c928cd..ae7cb62 100644 --- a/lib/src/debugger.dart +++ b/lib/src/debugger.dart @@ -22,10 +22,11 @@ class WipDebugger extends WipDomain { Future disable() => sendCommand('Debugger.disable'); - Future getScriptSource(String scriptId) async => - (await sendCommand('Debugger.getScriptSource', - params: {'scriptId': scriptId})) - .result['scriptSource'] as String; + Future getScriptSource(String scriptId) async { + return (await sendCommand('Debugger.getScriptSource', + params: {'scriptId': scriptId})) + .result['scriptSource'] as String; + } Future pause() => sendCommand('Debugger.pause'); @@ -142,19 +143,19 @@ class WipDebugger extends WipDomain { } } - Stream get onPaused => eventStream( - 'Debugger.paused', (WipEvent event) => new DebuggerPausedEvent(event)); + Stream get onPaused => eventStream('Debugger.paused', + (WipEvent event) => new DebuggerPausedEvent(event.json)); Stream get onGlobalObjectCleared => eventStream( 'Debugger.globalObjectCleared', (WipEvent event) => new GlobalObjectClearedEvent(event)); - Stream get onResumed => eventStream( - 'Debugger.resumed', (WipEvent event) => new DebuggerResumedEvent(event)); + Stream get onResumed => eventStream('Debugger.resumed', + (WipEvent event) => new DebuggerResumedEvent(event.json)); Stream get onScriptParsed => eventStream( 'Debugger.scriptParsed', - (WipEvent event) => new ScriptParsedEvent(event)); + (WipEvent event) => new ScriptParsedEvent(event.json)); Map get scripts => new UnmodifiableMapView(_scripts); } @@ -174,158 +175,177 @@ String _pauseStateToString(PauseState state) { enum PauseState { all, none, uncaught } -class ScriptParsedEvent extends WrappedWipEvent { - final WipScript script; +class ScriptParsedEvent extends WipEvent { + ScriptParsedEvent(Map json) : super(json); - ScriptParsedEvent(WipEvent event) - : this.script = new WipScript(event.params), - super(event); + WipScript get script => new WipScript(params); String toString() => script.toString(); } -class GlobalObjectClearedEvent extends WrappedWipEvent { - GlobalObjectClearedEvent(WipEvent event) : super(event); +class GlobalObjectClearedEvent extends WipEvent { + GlobalObjectClearedEvent(json) : super(json); } -class DebuggerResumedEvent extends WrappedWipEvent { - DebuggerResumedEvent(WipEvent event) : super(event); +class DebuggerResumedEvent extends WipEvent { + DebuggerResumedEvent(Map json) : super(json); } -class DebuggerPausedEvent extends WrappedWipEvent { - DebuggerPausedEvent(WipEvent event) : super(event); +/// Fired when the virtual machine stopped on breakpoint or exception or any +/// other stop criteria. +class DebuggerPausedEvent extends WipEvent { + DebuggerPausedEvent(Map json) : super(json); + + /// Call stack the virtual machine stopped on. + List getCallFrames() => (params['callFrames'] as List) + .map((frame) => new WipCallFrame(frame as Map)) + .toList(); + /// Pause reason. + /// + /// Allowed Values: ambiguous, assert, debugCommand, DOM, EventListener, + /// exception, instrumentation, OOM, other, promiseRejection, XHR. String get reason => params['reason'] as String; + /// Object containing break-specific auxiliary properties. Object get data => params['data']; - Iterable getCallFrames() => (params['callFrames'] as List) - .map((frame) => new WipCallFrame(frame as Map)); + /// Hit breakpoints IDs (optional). + List get hitBreakpoints { + if (params['hitBreakpoints'] == null) return null; + return (params['hitBreakpoints'] as List).cast(); + } + + /// Async stack trace, if any. + StackTrace get asyncStackTrace => params['asyncStackTrace'] == null + ? null + : StackTrace(params['asyncStackTrace']); String toString() => 'paused: ${reason}'; } class WipCallFrame { - final Map _map; + final Map json; - WipCallFrame(this._map); + WipCallFrame(this.json); - String get callFrameId => _map['callFrameId'] as String; + /// Call frame identifier. + /// + /// This identifier is only valid while the virtual machine is paused. + String get callFrameId => json['callFrameId'] as String; - String get functionName => _map['functionName'] as String; + /// Name of the JavaScript function called on this call frame. + String get functionName => json['functionName'] as String; + /// Location in the source code. WipLocation get location => - new WipLocation(_map['location'] as Map); + new WipLocation(json['location'] as Map); - WipRemoteObject get thisObject => - new WipRemoteObject(_map['this'] as Map); + /// JavaScript script name or url. + String get url => json['url'] as String; - Iterable getScopeChain() => (_map['scopeChain'] as List) + /// Scope chain for this call frame. + Iterable getScopeChain() => (json['scopeChain'] as List) .map((scope) => new WipScope(scope as Map)); + /// `this` object for this call frame. + RemoteObject get thisObject => + new RemoteObject(json['this'] as Map); + + /// The value being returned, if the function is at return point. + /// + /// (optional) + RemoteObject get returnValue { + return json.containsKey('returnValue') + ? new RemoteObject(json['returnValue'] as Map) + : null; + } + String toString() => '[${functionName}]'; } class WipLocation { - final Map _map; + final Map json; - WipLocation(this._map); + WipLocation(this.json); WipLocation.fromValues(String scriptId, int lineNumber, {int columnNumber}) - : _map = {} { - _map['scriptId'] = scriptId; - _map['lineNumber'] = lineNumber; + : json = {} { + json['scriptId'] = scriptId; + json['lineNumber'] = lineNumber; if (columnNumber != null) { - _map['columnNumber'] = columnNumber; + json['columnNumber'] = columnNumber; } } - String get scriptId => _map['scriptId']; + String get scriptId => json['scriptId']; - int get lineNumber => _map['lineNumber']; + int get lineNumber => json['lineNumber']; - int get columnNumber => _map['columnNumber']; + int get columnNumber => json['columnNumber']; Map toJsonMap() { - return _map; + return json; } String toString() => '[${scriptId}:${lineNumber}:${columnNumber}]'; } -class WipRemoteObject { - final Map _map; - - WipRemoteObject(this._map); - - String get className => _map['className'] as String; - - String get description => _map['description'] as String; - - String get objectId => _map['objectId'] as String; - - String get subtype => _map['subtype'] as String; - - String get type => _map['type'] as String; - - Object get value => _map['value']; -} - class WipScript { - final Map _map; + final Map json; - WipScript(this._map); + WipScript(this.json); - String get scriptId => _map['scriptId'] as String; + String get scriptId => json['scriptId'] as String; - String get url => _map['url'] as String; + String get url => json['url'] as String; - int get startLine => _map['startLine'] as int; + int get startLine => json['startLine'] as int; - int get startColumn => _map['startColumn'] as int; + int get startColumn => json['startColumn'] as int; - int get endLine => _map['endLine'] as int; + int get endLine => json['endLine'] as int; - int get endColumn => _map['endColumn'] as int; + int get endColumn => json['endColumn'] as int; - bool get isContentScript => _map['isContentScript'] as bool; + bool get isContentScript => json['isContentScript'] as bool; - String get sourceMapURL => _map['sourceMapURL'] as String; + String get sourceMapURL => json['sourceMapURL'] as String; String toString() => '[script ${scriptId}: ${url}]'; } class WipScope { - final Map _map; + final Map json; - WipScope(this._map); + WipScope(this.json); // "catch", "closure", "global", "local", "with" - String get scope => _map['type'] as String; + String get scope => json['type'] as String; /// Name of the scope, null if unnamed closure or global scope - String get name => _map['name'] as String; + String get name => json['name'] as String; /// Object representing the scope. For global and with scopes it represents /// the actual object; for the rest of the scopes, it is artificial transient /// object enumerating scope variables as its properties. - WipRemoteObject get object => - new WipRemoteObject(_map['object'] as Map); + RemoteObject get object => + new RemoteObject(json['object'] as Map); } class WipBreakLocation extends WipLocation { - WipBreakLocation(Map map) : super(map); + WipBreakLocation(Map json) : super(json); WipBreakLocation.fromValues(String scriptId, int lineNumber, {int columnNumber, String type}) : super.fromValues(scriptId, lineNumber, columnNumber: columnNumber) { if (type != null) { - _map['type'] = type; + json['type'] = type; } } /// Allowed Values: `debuggerStatement`, `call`, `return`. - String get type => _map['type']; + String get type => json['type']; } /// The response from [WipDebugger.setBreakpoint]. diff --git a/lib/src/dom.dart b/lib/src/dom.dart index e8929ff..d1fd1e5 100644 --- a/lib/src/dom.dart +++ b/lib/src/dom.dart @@ -118,14 +118,14 @@ class WipDom extends WipDomain { return resp.result['nodeId'] as int; } - Future resolveNode(int nodeId, {String objectGroup}) async { + Future resolveNode(int nodeId, {String objectGroup}) async { var params = {'nodeId': nodeId}; if (objectGroup != null) { params['objectGroup'] = objectGroup; } var resp = await sendCommand('DOM.resolveNode', params: params); - return new WipRemoteObject(resp.result['object'] as Map); + return new RemoteObject(resp.result['object'] as Map); } Future setAttributeValue(int nodeId, String name, String value) => @@ -156,64 +156,79 @@ class WipDom extends WipDomain { Stream get onAttributeModified => eventStream( 'DOM.attributeModified', - (WipEvent event) => new AttributeModifiedEvent(event)); + (WipEvent event) => new AttributeModifiedEvent(event.json)); + Stream get onAttributeRemoved => eventStream( 'DOM.attributeRemoved', - (WipEvent event) => new AttributeRemovedEvent(event)); + (WipEvent event) => new AttributeRemovedEvent(event.json)); + Stream get onCharacterDataModified => eventStream( 'DOM.characterDataModified', - (WipEvent event) => new CharacterDataModifiedEvent(event)); + (WipEvent event) => new CharacterDataModifiedEvent(event.json)); + Stream get onChildNodeCountUpdated => eventStream( 'DOM.childNodeCountUpdated', - (WipEvent event) => new ChildNodeCountUpdatedEvent(event)); + (WipEvent event) => new ChildNodeCountUpdatedEvent(event.json)); + Stream get onChildNodeInserted => eventStream( 'DOM.childNodeInserted', - (WipEvent event) => new ChildNodeInsertedEvent(event)); + (WipEvent event) => new ChildNodeInsertedEvent(event.json)); + Stream get onChildNodeRemoved => eventStream( 'DOM.childNodeRemoved', - (WipEvent event) => new ChildNodeRemovedEvent(event)); + (WipEvent event) => new ChildNodeRemovedEvent(event.json)); + Stream get onDocumentUpdated => eventStream( 'DOM.documentUpdated', - (WipEvent event) => new DocumentUpdatedEvent(event)); + (WipEvent event) => new DocumentUpdatedEvent(event.json)); + Stream get onSetChildNodes => eventStream( - 'DOM.setChildNodes', (WipEvent event) => new SetChildNodesEvent(event)); + 'DOM.setChildNodes', + (WipEvent event) => new SetChildNodesEvent(event.json)); } -class AttributeModifiedEvent extends WrappedWipEvent { - AttributeModifiedEvent(WipEvent event) : super(event); +class AttributeModifiedEvent extends WipEvent { + AttributeModifiedEvent(Map json) : super(json); int get nodeId => params['nodeId'] as int; + String get name => params['name'] as String; + String get value => params['value'] as String; } -class AttributeRemovedEvent extends WrappedWipEvent { - AttributeRemovedEvent(WipEvent event) : super(event); +class AttributeRemovedEvent extends WipEvent { + AttributeRemovedEvent(Map json) : super(json); int get nodeId => params['nodeId'] as int; + String get name => params['name'] as String; } -class CharacterDataModifiedEvent extends WrappedWipEvent { - CharacterDataModifiedEvent(WipEvent event) : super(event); +class CharacterDataModifiedEvent extends WipEvent { + CharacterDataModifiedEvent(Map json) : super(json); int get nodeId => params['nodeId'] as int; + String get characterData => params['characterData'] as String; } -class ChildNodeCountUpdatedEvent extends WrappedWipEvent { - ChildNodeCountUpdatedEvent(WipEvent event) : super(event); +class ChildNodeCountUpdatedEvent extends WipEvent { + ChildNodeCountUpdatedEvent(Map json) : super(json); int get nodeId => params['nodeId'] as int; + int get childNodeCount => params['childNodeCount'] as int; } -class ChildNodeInsertedEvent extends WrappedWipEvent { - ChildNodeInsertedEvent(WipEvent event) : super(event); +class ChildNodeInsertedEvent extends WipEvent { + ChildNodeInsertedEvent(Map json) : super(json); int get parentNodeId => params['parentNodeId'] as int; + int get previousNodeId => params['previousNodeId'] as int; Node _node; + Node get node { if (_node == null) { _node = new Node(params['node'] as Map); @@ -222,21 +237,23 @@ class ChildNodeInsertedEvent extends WrappedWipEvent { } } -class ChildNodeRemovedEvent extends WrappedWipEvent { - ChildNodeRemovedEvent(WipEvent event) : super(event); +class ChildNodeRemovedEvent extends WipEvent { + ChildNodeRemovedEvent(Map json) : super(json); int get parentNodeId => params['parentNodeId'] as int; + int get nodeId => params['nodeId'] as int; } -class DocumentUpdatedEvent extends WrappedWipEvent { - DocumentUpdatedEvent(WipEvent event) : super(event); +class DocumentUpdatedEvent extends WipEvent { + DocumentUpdatedEvent(Map json) : super(json); } -class SetChildNodesEvent extends WrappedWipEvent { - SetChildNodesEvent(WipEvent event) : super(event); +class SetChildNodesEvent extends WipEvent { + SetChildNodesEvent(Map json) : super(json); int get nodeId => params['parentId'] as int; + Iterable get nodes sync* { for (Map node in params['nodes']) { yield new Node(node as Map); @@ -255,6 +272,7 @@ class Node { Node(this._map); Map _attributes; + Map get attributes { if (_attributes == null && _map.containsKey('attributes')) { _attributes = _attributeListToMap((_map['attributes'] as List).cast()); @@ -265,6 +283,7 @@ class Node { int get childNodeCount => _map['childNodeCount'] as int; List _children; + List get children { if (_children == null && _map.containsKey('children')) { _children = new UnmodifiableListView((_map['children'] as List) diff --git a/lib/src/log.dart b/lib/src/log.dart index 772d453..6dc5b46 100644 --- a/lib/src/log.dart +++ b/lib/src/log.dart @@ -12,12 +12,12 @@ class WipLog extends WipDomain { Future disable() => sendCommand('Log.disable'); - Stream get onEntryAdded => - eventStream('Log.entryAdded', (WipEvent event) => new LogEntry(event)); + Stream get onEntryAdded => eventStream( + 'Log.entryAdded', (WipEvent event) => new LogEntry(event.json)); } -class LogEntry extends WrappedWipEvent { - LogEntry(WipEvent event) : super(event); +class LogEntry extends WipEvent { + LogEntry(Map json) : super(json); Map get _entry => params['entry'] as Map; diff --git a/lib/src/runtime.dart b/lib/src/runtime.dart index bd6d647..b802289 100644 --- a/lib/src/runtime.dart +++ b/lib/src/runtime.dart @@ -64,8 +64,8 @@ class WipRuntime extends WipDomain { /// object (int, String, double, bool). Future callFunctionOn( String functionDeclaration, { - List arguments, String objectId, + List arguments, bool returnByValue, int executionContextId, }) async { @@ -104,13 +104,57 @@ class WipRuntime extends WipDomain { } } + /// Returns the JavaScript heap usage. It is the total usage of the + /// corresponding isolate not scoped to a particular Runtime. + @experimental + Future getHeapUsage() async { + final WipResponse response = await sendCommand('Runtime.getHeapUsage'); + return HeapUsage(response.result); + } + + /// Returns the isolate id. + @experimental + Future 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> getProperties( + RemoteObject object, { + bool ownProperties, + }) async { + Map 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); + } else { + List locations = response.result['result']; + return List.from(locations.map((map) => PropertyDescriptor(map))); + } + } + Stream get onConsoleAPICalled => eventStream( 'Runtime.consoleAPICalled', - (WipEvent event) => new ConsoleAPIEvent(event)); + (WipEvent event) => new ConsoleAPIEvent(event.json)); Stream get onExceptionThrown => eventStream( 'Runtime.exceptionThrown', - (WipEvent event) => new ExceptionThrownEvent(event)); + (WipEvent event) => new ExceptionThrownEvent(event.json)); /// Issued when new execution context is created. Stream get onExecutionContextCreated => @@ -129,44 +173,43 @@ class WipRuntime extends WipDomain { 'Runtime.executionContextsCleared', (WipEvent event) => event); } -class ConsoleAPIEvent extends WrappedWipEvent { - ConsoleAPIEvent(WipEvent event) : super(event); +// TODO: stackTrace, StackTrace, Stack trace captured when the call was made. +class ConsoleAPIEvent extends WipEvent { + ConsoleAPIEvent(Map 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. + /// Call timestamp. num get timestamp => params['timestamp'] as num; /// Call arguments. List get args => (params['args'] as List) .map((m) => new RemoteObject(m as Map)) .toList(); - -// TODO: stackTrace, StackTrace, Stack trace captured when the call was made. } /// Description of an isolated world. class ExecutionContextDescription { - final Map map; + final Map json; - ExecutionContextDescription(this.map); + 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 => map['id'] as int; + int get id => json['id'] as int; /// Execution context origin. - String get origin => map['origin']; + String get origin => json['origin']; /// Human readable name describing given context. - String get name => map['name']; + String get name => json['name']; } -class ExceptionThrownEvent extends WrappedWipEvent { - ExceptionThrownEvent(WipEvent event) : super(event); +class ExceptionThrownEvent extends WipEvent { + ExceptionThrownEvent(Map json) : super(json); /// Timestamp of the exception. int get timestamp => params['timestamp'] as int; @@ -176,60 +219,59 @@ class ExceptionThrownEvent extends WrappedWipEvent { } class ExceptionDetails implements Exception { - final Map _map; - - ExceptionDetails(this._map); + final Map json; - Map get json => _map; + ExceptionDetails(this.json); /// Exception id. - int get exceptionId => _map['exceptionId'] as int; + int get exceptionId => json['exceptionId'] as int; /// Exception text, which should be used together with exception object when /// available. - String get text => _map['text'] as String; + String get text => json['text'] as String; /// Line number of the exception location (0-based). - int get lineNumber => _map['lineNumber'] as int; + int get lineNumber => json['lineNumber'] as int; /// Column number of the exception location (0-based). - int get columnNumber => _map['columnNumber'] as int; + 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 => _map['url'] as String; + String get url => json['url'] as String; /// Script ID of the exception location. @optional - String get scriptId => _map['scriptId'] as String; + String get scriptId => json['scriptId'] as String; /// JavaScript stack trace if available. @optional - StackTrace get stackTrace => _map['stackTrace'] == null + StackTrace get stackTrace => json['stackTrace'] == null ? null - : new StackTrace(_map['stackTrace'] as Map); + : new StackTrace(json['stackTrace'] as Map); /// Exception object if available. @optional - RemoteObject get exception => _map['exception'] == null + RemoteObject get exception => json['exception'] == null ? null - : new RemoteObject(_map['exception'] as Map); + : new RemoteObject(json['exception'] as Map); String toString() => '$text, $url, $scriptId, $lineNumber, $exception'; } +/// Call frames for assertions or error messages. class StackTrace { - final Map _map; + final Map json; - StackTrace(this._map); + 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 => _map['description'] as String; + String get description => json['description'] as String; - List get callFrames => (_map['callFrames'] as List) + List get callFrames => (json['callFrames'] as List) .map((m) => new CallFrame(m as Map)) .toList(); @@ -252,48 +294,113 @@ class StackTrace { String toString() => callFrames.map((f) => ' $f').join('\n'); } +/// Stack entry for runtime errors and assertions. class CallFrame { - final Map _map; + final Map json; - CallFrame(this._map); + CallFrame(this.json); /// JavaScript function name. - String get functionName => _map['functionName'] as String; + String get functionName => json['functionName'] as String; /// JavaScript script id. - String get scriptId => _map['scriptId'] as String; + String get scriptId => json['scriptId'] as String; /// JavaScript script name or url. - String get url => _map['url'] as String; + String get url => json['url'] as String; /// JavaScript script line number (0-based). - int get lineNumber => _map['lineNumber'] as int; + int get lineNumber => json['lineNumber'] as int; /// JavaScript script column number (0-based). - int get columnNumber => _map['columnNumber'] as int; + int get columnNumber => json['columnNumber'] as int; String toString() => '$functionName() ($url $lineNumber:$columnNumber)'; } /// Mirror object referencing original JavaScript object. class RemoteObject { - final Map _map; + final Map json; + + RemoteObject(this.json); - RemoteObject(this._map); + /// 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 type.object, function, undefined, string, number, boolean, symbol, - /// bigint. - String get type => _map['type'] 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 => _map['value']; + Object get value => json['value']; /// String representation of the object. (optional) - String get description => _map['description'] as String; + String get description => json['description'] as String; /// Unique object identifier (for non-primitive values). (optional) - String get objectId => _map['objectId'] as String; + 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 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 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']; +} diff --git a/lib/webkit_inspection_protocol.dart b/lib/webkit_inspection_protocol.dart index 3618974..1a0330e 100644 --- a/lib/webkit_inspection_protocol.dart +++ b/lib/webkit_inspection_protocol.dart @@ -239,12 +239,14 @@ class WipConnection { } class WipEvent { + final Map json; + final String method; final Map params; - WipEvent(Map map) - : method = map['method'] as String, - params = map['params'] as Map; + WipEvent(this.json) + : method = json['method'] as String, + params = json['params'] as Map; String toString() => 'WipEvent: $method($params)'; } @@ -322,18 +324,6 @@ abstract class WipDomain { } } -class WrappedWipEvent implements WipEvent { - final WipEvent _wrapped; - - WrappedWipEvent(this._wrapped); - - @override - String get method => _wrapped.method; - - @override - Map get params => _wrapped.params; -} - const _Experimental experimental = const _Experimental(); class _Experimental { diff --git a/pubspec.yaml b/pubspec.yaml index 76174a9..9dc68a5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: webkit_inspection_protocol -version: 0.6.0 +version: 0.7.0 description: A client for the Chrome DevTools Protocol (previously called the Webkit Inspection Protocol). homepage: https://github.com/google/webkit_inspection_protocol.dart diff --git a/test/data/runtime_test.html b/test/data/runtime_test.html new file mode 100644 index 0000000..3f9d708 --- /dev/null +++ b/test/data/runtime_test.html @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/test/dom_model_test.dart b/test/dom_model_test.dart index c7b63f1..762d128 100644 --- a/test/dom_model_test.dart +++ b/test/dom_model_test.dart @@ -124,6 +124,6 @@ void main() { expect(getAttributes, attributes); expect(bodyNode.attributes, attributes); - }); + }, skip: 'google/webkit_inspection_protocol.dart/issues/52'); }); } diff --git a/test/runtime_test.dart b/test/runtime_test.dart new file mode 100644 index 0000000..4f4b78a --- /dev/null +++ b/test/runtime_test.dart @@ -0,0 +1,96 @@ +// Copyright 2020 Google. All rights reserved. Use of this source code is +// governed by a BSD-style license that can be found in the LICENSE file. + +@TestOn('vm') +library wip.runtime_test; + +import 'dart:async'; + +import 'package:test/test.dart'; +import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart'; + +import 'test_setup.dart'; + +void main() { + group('WipRuntime', () { + WipRuntime runtime; + List subs = []; + + setUp(() async { + runtime = (await wipConnection).runtime; + }); + + tearDown(() async { + await runtime.disable(); + runtime = null; + + await closeConnection(); + subs.forEach((s) => s.cancel()); + subs.clear(); + }); + + test('getIsolateId', () async { + await runtime.enable(); + await navigateToPage('runtime_test.html'); + + expect(await runtime.getIsolateId(), isNotEmpty); + }); + + test('getHeapUsage', () async { + await runtime.enable(); + await navigateToPage('runtime_test.html'); + + HeapUsage usage = await runtime.getHeapUsage(); + + expect(usage.usedSize, greaterThan(0)); + expect(usage.totalSize, greaterThan(0)); + }); + + test('evaluate', () async { + await runtime.enable(); + await navigateToPage('runtime_test.html'); + + RemoteObject result = await runtime.evaluate('1+1'); + expect(result.type, 'number'); + expect(result.value, 2); + }); + + test('callFunctionOn', () async { + await runtime.enable(); + await navigateToPage('runtime_test.html'); + + RemoteObject console = await runtime.evaluate('console'); + RemoteObject result = await runtime.callFunctionOn( + ''' + function(msg) { + console.log(msg); + return msg; + }''', + objectId: console.objectId, + arguments: [ + 'foo', + ], + ); + + expect(result.type, 'string'); + expect(result.value, 'foo'); + }); + + test('getProperties', () async { + await runtime.enable(); + await navigateToPage('runtime_test.html'); + + RemoteObject console = await runtime.evaluate('console'); + + List properties = await runtime.getProperties( + console, + ownProperties: true, + ); + + expect(properties, isNotEmpty); + + PropertyDescriptor property = properties.first; + expect(property.name, isNotEmpty); + }); + }); +} diff --git a/test/test_setup.dart b/test/test_setup.dart index 5f478b2..5a5cfbe 100644 --- a/test/test_setup.dart +++ b/test/test_setup.dart @@ -34,6 +34,10 @@ Process _chromeDriver; /// Starts ChromeDriver and returns the listening port. Future _startChromeDriver() async { var chromeDriverPort = await findUnusedPort(); + + // Delay a small amount to allow us to close the above port. + await Future.delayed(const Duration(milliseconds: 25)); + try { var _exeExt = Platform.isWindows ? '.exe' : ''; _chromeDriver = await Process.start('chromedriver$_exeExt',