Chromium Code Reviews| Index: runtime/observatory/lib/src/elements/persistent_handles.dart |
| diff --git a/runtime/observatory/lib/src/elements/persistent_handles.dart b/runtime/observatory/lib/src/elements/persistent_handles.dart |
| index f6ccfaf433baedd8f1f0d31dd3d2b180b2700c09..54c4c1b636beeefcad7c7869826a21b4d0134a37 100644 |
| --- a/runtime/observatory/lib/src/elements/persistent_handles.dart |
| +++ b/runtime/observatory/lib/src/elements/persistent_handles.dart |
| @@ -6,173 +6,280 @@ library persitent_handles_page; |
| import 'dart:async'; |
| import 'dart:html'; |
| -import 'observatory_element.dart'; |
| -import 'package:observatory/app.dart'; |
| -import 'package:observatory/elements.dart'; |
| -import 'package:observatory/service.dart'; |
| -import 'package:polymer/polymer.dart'; |
| - |
| -class WeakPersistentHandlesSortedTable extends SortedTable { |
| - factory WeakPersistentHandlesSortedTable() { |
| - var columns = [ |
| - new SortedTableColumn.withFormatter('External Size', |
| - Utils.formatSize), |
| - new SortedTableColumn('Peer'), |
| - new SortedTableColumn('Finalizer Callback'), |
| - new SortedTableColumn(''), // Spacer column. |
| - new SortedTableColumn('Object'), |
| - ]; |
| - WeakPersistentHandlesSortedTable result = |
| - new WeakPersistentHandlesSortedTable._(columns); |
| - // Sort by external size. |
| - result.sortColumnIndex = 0; |
| - return result; |
| - } |
| +import 'package:observatory/models.dart' as M; |
| +import 'package:observatory/src/elements/containers/virtual_collection.dart'; |
| +import 'package:observatory/src/elements/helpers/any_ref.dart'; |
| +import 'package:observatory/src/elements/helpers/rendering_scheduler.dart'; |
| +import 'package:observatory/src/elements/helpers/tag.dart'; |
| +import 'package:observatory/src/elements/helpers/uris.dart'; |
| +import 'package:observatory/src/elements/instance_ref.dart'; |
| +import 'package:observatory/src/elements/nav/bar.dart'; |
| +import 'package:observatory/src/elements/nav/isolate_menu.dart'; |
| +import 'package:observatory/src/elements/nav/menu.dart'; |
| +import 'package:observatory/src/elements/nav/notify.dart'; |
| +import 'package:observatory/src/elements/nav/refresh.dart'; |
| +import 'package:observatory/src/elements/nav/top_menu.dart'; |
| +import 'package:observatory/src/elements/nav/vm_menu.dart'; |
| +import 'package:observatory/utils.dart'; |
| - WeakPersistentHandlesSortedTable._(columns) : super(columns); |
| +enum _SortingField { |
| + externalSize, |
| + peer, |
| + finalizerCallback |
| +} |
| - @override |
| - dynamic getSortKeyFor(int row, int col) { |
| - return super.getSortKeyFor(row, col); |
| - } |
| +enum _SortingDirection { |
| + ascending, |
| + descending |
| +} |
| - void update(List<ServiceMap> handles, HtmlElement tableBody) { |
| - clearRows(); |
| - for (ServiceMap handle in handles) { |
| - var row = [int.parse(handle['externalSize'], onError: (_) => 0), |
| - handle['peer'], |
| - handle['callbackSymbolName'] + |
| - '( ${handle['callbackAddress']} )', |
| - '', // Spacer column. |
| - handle['object']]; |
| - addRow(new SortedTableRow(row)); |
| - print(row); |
| - } |
| - sort(); |
| - _updateTableInDom(tableBody); |
| - } |
| +class PersistentHandlesPageElement extends HtmlElement implements Renderable { |
| + static const tag = |
| + const Tag<PersistentHandlesPageElement>('persistent-handles-page', |
| + dependencies: const [ |
| + InstanceRefElement.tag, |
| + NavBarElement.tag, |
| + NavTopMenuElement.tag, |
| + NavVMMenuElement.tag, |
| + NavIsolateMenuElement.tag, |
| + NavMenuElement.tag, |
| + NavRefreshElement.tag, |
| + NavNotifyElement.tag, |
| + VirtualCollectionElement.tag |
| + ]); |
| - void sortAndDisplay(HtmlElement tableBody) { |
| - sort(); |
| - _updateTableInDom(tableBody); |
| - } |
| + RenderingScheduler<PersistentHandlesPageElement> _r; |
| + Stream<RenderedEvent<PersistentHandlesPageElement>> get onRendered => |
| + _r.onRendered; |
| - void _updateTableInDom(HtmlElement tableBody) { |
| - assert(tableBody != null); |
| - // Resize DOM table. |
| - if (tableBody.children.length > sortedRows.length) { |
| - // Shrink the table. |
| - var deadRows = |
| - tableBody.children.length - sortedRows.length; |
| - for (var i = 0; i < deadRows; i++) { |
| - tableBody.children.removeLast(); |
| - } |
| - } else if (tableBody.children.length < sortedRows.length) { |
| - // Grow table. |
| - var newRows = sortedRows.length - tableBody.children.length; |
| - for (var i = 0; i < newRows; i++) { |
| - _addDomRow(tableBody); |
| - } |
| - } |
| - assert(tableBody.children.length == sortedRows.length); |
| - // Fill table. |
| - for (var i = 0; i < sortedRows.length; i++) { |
| - var rowIndex = sortedRows[i]; |
| - var tr = tableBody.children[i]; |
| - _fillDomRow(tr, rowIndex); |
| - } |
| - } |
| + M.VM _vm; |
| + M.IsolateRef _isolate; |
| + M.EventRepository _events; |
| + M.NotificationRepository _notifications; |
| + M.PersistentHandlesRepository _repository; |
| + M.InstanceRepository _instances; |
| + M.PersistentHandles _handles; |
| + _SortingField _sortingField = |
| + _SortingField.externalSize; |
| + _SortingDirection _sortingDirection = |
| + _SortingDirection.descending; |
| - void _addDomRow(HtmlElement tableBody) { |
| - // Add empty dom row. |
| - var tr = new TableRowElement(); |
| + M.VMRef get vm => _vm; |
| + M.IsolateRef get isolate => _isolate; |
| + M.NotificationRepository get notifications => _notifications; |
| - var cell; |
| + factory PersistentHandlesPageElement(M.VM vm, M.IsolateRef isolate, |
| + M.EventRepository events, |
| + M.NotificationRepository notifications, |
| + M.PersistentHandlesRepository repository, |
| + M.InstanceRepository instances, |
| + {RenderingQueue queue}) { |
| + assert(vm != null); |
| + assert(isolate != null); |
| + assert(events != null); |
| + assert(notifications != null); |
| + assert(repository != null); |
| + assert(instances != null); |
| + PersistentHandlesPageElement e = document.createElement(tag.name); |
| + e._r = new RenderingScheduler(e, queue: queue); |
| + e._vm = vm; |
| + e._isolate = isolate; |
| + e._events = events; |
| + e._notifications = notifications; |
| + e._repository = repository; |
| + e._instances = instances; |
| + return e; |
| + } |
| - cell = tr.insertCell(-1); |
| - cell = tr.insertCell(-1); |
| - cell = tr.insertCell(-1); |
| + PersistentHandlesPageElement.created() : super.created(); |
| - // Add spacer. |
| - cell = tr.insertCell(-1); |
| - cell.classes.add('left-border-spacer'); |
| + @override |
| + attached() { |
| + super.attached(); |
| + _r.enable(); |
| + _refresh(); |
| + } |
| - // Add class ref. |
| - cell = tr.insertCell(-1); |
| - AnyServiceRefElement objectRef = new Element.tag('any-service-ref'); |
| - cell.children.add(objectRef); |
| + @override |
| + detached() { |
| + super.detached(); |
| + _r.disable(notify: true); |
| + children = []; |
| + } |
| - // Add row to table. |
| - tableBody.children.add(tr); |
| + void render() { |
| + children = ([ |
| + new NavBarElement(queue: _r.queue) |
| + ..children = [ |
| + new NavTopMenuElement(queue: _r.queue), |
| + new NavVMMenuElement(_vm, _events, queue: _r.queue), |
| + new NavIsolateMenuElement(_isolate, _events, queue: _r.queue), |
| + new NavMenuElement('persistent handles', last: true, |
| + link: Uris.persistentHandles(_isolate), queue: _r.queue), |
| + new NavRefreshElement(queue: _r.queue) |
| + ..onRefresh.listen((_) => _refresh()), |
| + new NavNotifyElement(_notifications, queue: _r.queue) |
| + ] |
| + ]..addAll(_createPersistendHandlers()) |
| + ..add(new BRElement()) |
| + ..addAll(_createWeakPersistendHandlers())); |
| } |
| - void _fillDomRow(TableRowElement tr, int rowIndex) { |
| - var row = rows[rowIndex]; |
| + List<Element> _createPersistendHandlers() { |
|
Cutch
2016/08/25 22:27:28
It seems like it should be easy to merge _createPe
cbernaschina
2016/08/25 22:43:39
Done.
|
| + return [ |
| + new DivElement()..classes = const ['content-centered-big'] |
| + ..children = [ |
| + new HeadingElement.h1()..text = 'Persistent Handles', |
| + new HRElement(), |
| + ], |
| + new DivElement()..classes = const ['persistent-handles'] |
| + ..children = [ |
| + _handles == null |
| + ? (new HeadingElement.h2()..classes = const ['content-centered-big'] |
| + ..text = 'Loading...') |
| + : new VirtualCollectionElement( |
| + _createLine, |
| + _updateLine, |
| + items: _handles.elements, |
| + queue: _r.queue) |
| + ] |
| + ]; |
| + } |
| - for (var i = 0; i < row.values.length - 2; i++) { |
| - var cell = tr.children[i]; |
| - cell.title = row.values[i].toString(); |
| - cell.text = getFormattedValue(rowIndex, i); |
| - cell.style.paddingLeft = '1em'; |
| - cell.style.paddingRight = '1em'; |
| - } |
| + List<Element> _createWeakPersistendHandlers() { |
| + return [ |
| + new DivElement()..classes = const ['content-centered-big'] |
| + ..children = [ |
| + new HeadingElement.h1()..text = 'Weak Persistent Handles', |
| + new HRElement(), |
| + ], |
| + new DivElement()..classes = const ['weak-persistent-handles'] |
| + ..children = [ |
| + _handles == null |
| + ? (new HeadingElement.h2()..classes = const ['content-centered-big'] |
| + ..text = 'Loading...') |
| + : new VirtualCollectionElement( |
| + _createWeakLine, |
| + _updateWeakLine, |
| + createHeader: _createWeakHeader, |
| + items: _handles.weakElements.toList()..sort(_createSorter()), |
| + queue: _r.queue) |
| + ] |
| + ]; |
| + } |
| - final int objectIndex = row.values.length - 1; |
| - AnyServiceRefElement objectRef = tr.children[objectIndex].children[0]; |
| - objectRef.ref = row.values[objectIndex]; |
| + _createSorter() { |
| + var getter; |
| + switch (_sortingField) { |
| + case _SortingField.externalSize: |
| + getter = _getExternalSize; |
| + break; |
| + case _SortingField.peer: |
| + getter = _getPeer; |
| + break; |
| + case _SortingField.finalizerCallback: |
| + getter = _getFinalizerCallback; |
| + break; |
| + } |
| + switch (_sortingDirection) { |
| + case _SortingDirection.ascending: |
| + return (a, b) => getter(a).compareTo(getter(b)); |
| + case _SortingDirection.descending: |
| + return (a, b) => getter(b).compareTo(getter(a)); |
| + } |
| } |
| -} |
| + static Element _createLine() => |
| + new DivElement()..classes = const ['collection-item'] |
| + ..text = 'object'; |
| -@CustomTag('persistent-handles-page') |
| -class PersistentHandlesPageElement extends ObservatoryElement { |
| - PersistentHandlesPageElement.created() : super.created(); |
| + static Element _createWeakLine() => |
| + new DivElement() |
| + ..classes = const ['weak-item'] |
| + ..children = [ |
| + new SpanElement()..classes = const ['external-size'] |
| + ..text = '0B', |
| + new SpanElement()..classes = const ['peer'] |
| + ..text = '0x00000', |
| + new SpanElement()..classes = const ['object'], |
| + new SpanElement()..classes = const ['finalizer'] |
| + ..text = 'dart::Class::Method()' |
| + ]; |
| - @observable Isolate isolate; |
| - @observable var /*ObservableList | ServiceObject*/ persistentHandles; |
| - @observable var /*ObservableList | ServiceObject*/ weakPersistentHandles; |
| - @observable WeakPersistentHandlesSortedTable weakPersistentHandlesTable; |
| - var _weakPersistentHandlesTableBody; |
| + Element _createWeakHeader() => |
| + new DivElement() |
| + ..classes = const ['weak-item'] |
| + ..children = [ |
| + _createHeaderButton(const ['external-size'], 'External Size', |
| + _SortingField.externalSize, |
| + _SortingDirection.descending), |
| + _createHeaderButton(const ['peer'], 'Peer', |
| + _SortingField.peer, |
| + _SortingDirection.descending), |
| + new SpanElement()..classes = const ['object'] |
| + ..text = 'Object', |
| + _createHeaderButton(const ['finalizer'], 'Finalizer Callback', |
| + _SortingField.finalizerCallback, |
| + _SortingDirection.ascending) |
| + ]; |
| - void isolateChanged(oldValue) { |
| - if (isolate != null) { |
| - refresh(); |
| + ButtonElement _createHeaderButton(List<String> classes, |
| + String text, |
| + _SortingField field, |
| + _SortingDirection direction) => |
| + new ButtonElement()..classes = classes |
| + ..text = _sortingField != field ? text : |
| + _sortingDirection == _SortingDirection.ascending |
| + ? '$textâ–¼' : '$textâ–²' |
| + ..onClick.listen((_) => _setSorting(field, direction)); |
| + |
| + |
| + void _setSorting(_SortingField field, |
| + _SortingDirection defaultDirection) { |
| + if (_sortingField == field) { |
| + switch (_sortingDirection) { |
| + case _SortingDirection.descending: |
| + _sortingDirection = _SortingDirection.ascending; |
| + break; |
| + case _SortingDirection.ascending: |
| + _sortingDirection = _SortingDirection.descending; |
| + break; |
| + } |
| + } else { |
| + _sortingDirection = defaultDirection; |
| + _sortingField = field; |
| } |
| + _r.dirty(); |
| } |
| - @override |
| - void attached() { |
| - super.attached(); |
| - _weakPersistentHandlesTableBody = |
| - shadowRoot.querySelector('#weakPersistentHandlesTableBody'); |
| - weakPersistentHandlesTable = |
| - new WeakPersistentHandlesSortedTable(); |
| + void _updateWeakLine(Element e, M.WeakPersistentHandle item, |
| + index) { |
| + e.children[0].text = Utils.formatSize(_getExternalSize(item)); |
| + e.children[1].text = '${_getPeer(item)}'; |
| + e.children[2] = anyRef(_isolate, item.object, _instances, queue: _r.queue) |
| + ..classes = const ['object']; |
| + e.children[3]..text = '${_getFinalizerCallback(item)}' |
| + ..title = '${_getFinalizerCallback(item)}'; |
| } |
| - Future refresh() { |
| - return isolate.getPersistentHandles().then(_refreshView); |
| + void _updateLine(Element e, M.PersistentHandle item, |
| + index) { |
| + e.children = [ |
| + anyRef(_isolate, item.object, _instances, queue: _r.queue) |
| + ..classes = const ['object'] |
| + ]; |
| } |
| - _refreshView(/*ObservableList | ServiceObject*/ object) { |
| - persistentHandles = object['persistentHandles']; |
| - weakPersistentHandles = object['weakPersistentHandles']; |
| - weakPersistentHandlesTable.update( |
| - weakPersistentHandles, |
| - _weakPersistentHandlesTableBody); |
| + Future _refresh({bool gc: false, bool reset: false}) async { |
| + _handles = null; |
| + _r.dirty(); |
| + _handles = await _repository.get(_isolate); |
| + _r.dirty(); |
| } |
| - @observable void changeSort(Event e, var detail, Element target) { |
| - if (target is TableCellElement) { |
| - if (weakPersistentHandlesTable.sortColumnIndex != target.cellIndex) { |
| - weakPersistentHandlesTable.sortColumnIndex = target.cellIndex; |
| - weakPersistentHandlesTable.sortDescending = true; |
| - } else { |
| - weakPersistentHandlesTable.sortDescending = |
| - !weakPersistentHandlesTable.sortDescending; |
| - } |
| - weakPersistentHandlesTable.sortAndDisplay( |
| - _weakPersistentHandlesTableBody); |
| - } |
| - } |
| + static int _getExternalSize(M.WeakPersistentHandle h) => h.externalSize; |
| + static String _getPeer(M.WeakPersistentHandle h) => h.peer; |
| + static String _getFinalizerCallback(M.WeakPersistentHandle h) => |
| + '${h.callbackSymbolName} (${h.callbackAddress})'; |
| } |