blob: d1fd1e59c057ecc5c654f3a539f7266fd90ad526 [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:collection';
import '../webkit_inspection_protocol.dart';
/// Implementation of the
/// https://developer.chrome.com/devtools/docs/protocol/1.1/dom
class WipDom extends WipDomain {
WipDom(WipConnection connection) : super(connection);
Future<Map<String, String>> getAttributes(int nodeId) async {
WipResponse resp =
await sendCommand('DOM.getAttributes', params: {'nodeId': nodeId});
return _attributeListToMap((resp.result['attributes'] as List).cast());
}
Future<Node> getDocument() async =>
new Node((await sendCommand('DOM.getDocument')).result['root']
as Map<String, dynamic>);
Future<String> getOuterHtml(int nodeId) async =>
(await sendCommand('DOM.getOuterHTML', params: {'nodeId': nodeId}))
.result['root'] as String;
Future hideHighlight() => sendCommand('DOM.hideHighlight');
Future highlightNode(int nodeId,
{Rgba borderColor,
Rgba contentColor,
Rgba marginColor,
Rgba paddingColor,
bool showInfo}) {
var params = <String, dynamic>{'nodeId': nodeId, 'highlightConfig': {}};
if (borderColor != null) {
params['highlightConfig']['borderColor'] = borderColor;
}
if (contentColor != null) {
params['highlightConfig']['contentColor'] = contentColor;
}
if (marginColor != null) {
params['highlightConfig']['marginColor'] = marginColor;
}
if (paddingColor != null) {
params['highlightConfig']['paddingColor'] = paddingColor;
}
if (showInfo != null) {
params['highlightConfig']['showInfo'] = showInfo;
}
return sendCommand('DOM.highlightNode', params: params);
}
Future highlightRect(int x, int y, int width, int height,
{Rgba color, Rgba outlineColor}) {
var params = <String, dynamic>{
'x': x,
'y': y,
'width': width,
'height': height
};
if (color != null) {
params['color'] = color;
}
if (outlineColor != null) {
params['outlineColor'] = outlineColor;
}
return sendCommand('DOM.highlightRect', params: params);
}
Future<int> moveTo(int nodeId, int targetNodeId,
{int insertBeforeNodeId}) async {
var params = {'nodeId': nodeId, 'targetNodeId': targetNodeId};
if (insertBeforeNodeId != null) {
params['insertBeforeNodeId'] = insertBeforeNodeId;
}
var resp = await sendCommand('DOM.moveTo', params: params);
return resp.result['nodeId'] as int;
}
Future<int> querySelector(int nodeId, String selector) async {
var resp = await sendCommand('DOM.querySelector',
params: {'nodeId': nodeId, 'selector': selector});
return resp.result['nodeId'] as int;
}
Future<List<int>> querySelectorAll(int nodeId, String selector) async {
var resp = await sendCommand('DOM.querySelectorAll',
params: {'nodeId': nodeId, 'selector': selector});
return (resp.result['nodeIds'] as List).cast();
}
Future removeAttribute(int nodeId, String name) =>
sendCommand('DOM.removeAttribute',
params: {'nodeId': nodeId, 'name': name});
Future removeNode(int nodeId) =>
sendCommand('DOM.removeNode', params: {'nodeId': nodeId});
Future requestChildNodes(int nodeId) =>
sendCommand('DOM.requestChildNodes', params: {'nodeId': nodeId});
Future<int> requestNode(String objectId) async {
var resp =
await sendCommand('DOM.requestNode', params: {'objectId': objectId});
return resp.result['nodeId'] as int;
}
Future<RemoteObject> resolveNode(int nodeId, {String objectGroup}) async {
var params = <String, dynamic>{'nodeId': nodeId};
if (objectGroup != null) {
params['objectGroup'] = objectGroup;
}
var resp = await sendCommand('DOM.resolveNode', params: params);
return new RemoteObject(resp.result['object'] as Map<String, dynamic>);
}
Future setAttributeValue(int nodeId, String name, String value) =>
sendCommand('DOM.setAttributeValue',
params: {'nodeId': nodeId, 'name': name, 'value': value});
Future setAttributesAsText(int nodeId, String text, {String name}) {
var params = {'nodeId': nodeId, 'text': text};
if (name != null) {
params['name'] = name;
}
return sendCommand('DOM.setAttributeValue', params: params);
}
Future<int> setNodeName(int nodeId, String name) async {
var resp = await sendCommand('DOM.setNodeName',
params: {'nodeId': nodeId, 'name': name});
return resp.result['nodeId'] as int;
}
Future setNodeValue(int nodeId, String value) =>
sendCommand('DOM.setNodeValue',
params: {'nodeId': nodeId, 'value': value});
Future setOuterHtml(int nodeId, String outerHtml) =>
sendCommand('DOM.setOuterHTML',
params: {'nodeId': nodeId, 'outerHtml': outerHtml});
Stream<AttributeModifiedEvent> get onAttributeModified => eventStream(
'DOM.attributeModified',
(WipEvent event) => new AttributeModifiedEvent(event.json));
Stream<AttributeRemovedEvent> get onAttributeRemoved => eventStream(
'DOM.attributeRemoved',
(WipEvent event) => new AttributeRemovedEvent(event.json));
Stream<CharacterDataModifiedEvent> get onCharacterDataModified => eventStream(
'DOM.characterDataModified',
(WipEvent event) => new CharacterDataModifiedEvent(event.json));
Stream<ChildNodeCountUpdatedEvent> get onChildNodeCountUpdated => eventStream(
'DOM.childNodeCountUpdated',
(WipEvent event) => new ChildNodeCountUpdatedEvent(event.json));
Stream<ChildNodeInsertedEvent> get onChildNodeInserted => eventStream(
'DOM.childNodeInserted',
(WipEvent event) => new ChildNodeInsertedEvent(event.json));
Stream<ChildNodeRemovedEvent> get onChildNodeRemoved => eventStream(
'DOM.childNodeRemoved',
(WipEvent event) => new ChildNodeRemovedEvent(event.json));
Stream<DocumentUpdatedEvent> get onDocumentUpdated => eventStream(
'DOM.documentUpdated',
(WipEvent event) => new DocumentUpdatedEvent(event.json));
Stream<SetChildNodesEvent> get onSetChildNodes => eventStream(
'DOM.setChildNodes',
(WipEvent event) => new SetChildNodesEvent(event.json));
}
class AttributeModifiedEvent extends WipEvent {
AttributeModifiedEvent(Map<String, dynamic> 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 WipEvent {
AttributeRemovedEvent(Map<String, dynamic> json) : super(json);
int get nodeId => params['nodeId'] as int;
String get name => params['name'] as String;
}
class CharacterDataModifiedEvent extends WipEvent {
CharacterDataModifiedEvent(Map<String, dynamic> json) : super(json);
int get nodeId => params['nodeId'] as int;
String get characterData => params['characterData'] as String;
}
class ChildNodeCountUpdatedEvent extends WipEvent {
ChildNodeCountUpdatedEvent(Map<String, dynamic> json) : super(json);
int get nodeId => params['nodeId'] as int;
int get childNodeCount => params['childNodeCount'] as int;
}
class ChildNodeInsertedEvent extends WipEvent {
ChildNodeInsertedEvent(Map<String, dynamic> 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<String, dynamic>);
}
return _node;
}
}
class ChildNodeRemovedEvent extends WipEvent {
ChildNodeRemovedEvent(Map<String, dynamic> json) : super(json);
int get parentNodeId => params['parentNodeId'] as int;
int get nodeId => params['nodeId'] as int;
}
class DocumentUpdatedEvent extends WipEvent {
DocumentUpdatedEvent(Map<String, dynamic> json) : super(json);
}
class SetChildNodesEvent extends WipEvent {
SetChildNodesEvent(Map<String, dynamic> json) : super(json);
int get nodeId => params['parentId'] as int;
Iterable<Node> get nodes sync* {
for (Map node in params['nodes']) {
yield new Node(node as Map<String, dynamic>);
}
}
String toString() => 'SetChildNodes $nodeId: $nodes';
}
/// The backend keeps track of which DOM nodes have been sent,
/// will only send each node once, and will only send events
/// for nodes that have been sent.
class Node {
final Map<String, dynamic> _map;
Node(this._map);
Map<String, String> _attributes;
Map<String, String> get attributes {
if (_attributes == null && _map.containsKey('attributes')) {
_attributes = _attributeListToMap((_map['attributes'] as List).cast());
}
return _attributes;
}
int get childNodeCount => _map['childNodeCount'] as int;
List<Node> _children;
List<Node> get children {
if (_children == null && _map.containsKey('children')) {
_children = new UnmodifiableListView((_map['children'] as List)
.map((c) => new Node(c as Map<String, dynamic>)));
}
return _children;
}
Node get contentDocument {
if (_map.containsKey('contentDocument')) {
return new Node(_map['contentDocument'] as Map<String, dynamic>);
}
return null;
}
String get documentUrl => _map['documentURL'] as String;
String get internalSubset => _map['internalSubset'] as String;
String get localName => _map['localName'] as String;
String get name => _map['name'] as String;
int get nodeId => _map['nodeId'] as int;
String get nodeName => _map['nodeName'] as String;
int get nodeType => _map['nodeType'] as int;
String get nodeValue => _map['nodeValue'] as String;
String get publicId => _map['publicId'] as String;
String get systemId => _map['systemId'] as String;
String get value => _map['value'] as String;
String get xmlVersion => _map['xmlVersion'] as String;
String toString() => '$nodeName: $nodeId $attributes';
}
class Rgba {
final int a;
final int b;
final int r;
final int g;
Rgba(this.r, this.g, this.b, [this.a]);
Map toJson() {
var json = {'r': r, 'g': g, 'b': b};
if (a != null) {
json['a'] = a;
}
return json;
}
}
Map<String, String> _attributeListToMap(List<String> attrList) {
var attributes = <String, String>{};
for (int i = 0; i < attrList.length; i += 2) {
attributes[attrList[i]] = attrList[i + 1];
}
return new UnmodifiableMapView(attributes);
}