blob: c820b47e4d97085e7781b79736cfc0591e7fbd3e [file] [log] [blame]
// Copyright 2019 The Chromium Authors. 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:html' as html;
import 'package:vm_service/vm_service.dart';
import '../debugger/debugger.dart';
import '../debugger/debugger_state.dart';
import '../ui/analytics.dart' as ga;
import '../ui/custom.dart';
import '../ui/elements.dart';
typedef URIDescriber = String Function(String uri);
int _breakpointComparator(Breakpoint a, Breakpoint b) {
ScriptRef getRef(dynamic location) {
if (location is SourceLocation) {
return location.script;
} else if (location is UnresolvedSourceLocation) {
return location.script;
} else {
return null;
}
}
int getPos(dynamic location) {
if (location is SourceLocation) {
return location.tokenPos ?? 0;
} else if (location is UnresolvedSourceLocation) {
return location.line ?? 0;
} else {
return 0;
}
}
// sort by script
final ScriptRef aRef = getRef(a.location);
final ScriptRef bRef = getRef(b.location);
final int compare = aRef.uri.compareTo(bRef.uri);
if (compare != 0) {
return compare;
}
// then sort by location
return getPos(a.location) - getPos(b.location);
}
class BreakpointsView implements CoreElementView {
BreakpointsView(this._breakpointsCountDiv, DebuggerState debuggerState,
URIDescriber uriDescriber) {
_items = SelectableList<Breakpoint>()
..flex()
..clazz('menu-item-bottom-border')
..clazz('debugger-items-list');
_items.setRenderer((Breakpoint breakpoint) {
final dynamic location = breakpoint.location;
final CoreElement element = li(c: 'list-item');
if (location is UnresolvedSourceLocation) {
element.text = uriDescriber(location.script.uri);
element.add(span(text: ' line ${location.line}', c: 'subtle'));
} else if (location is SourceLocation) {
element.text = uriDescriber(location.script.uri);
// Modify the rendering slightly asynchronously.
debuggerState.getScript(location.script).then((Script script) {
final SourcePosition pos =
debuggerState.calculatePosition(script, location.tokenPos);
element.add(span(text: ' line ${pos.line}', c: 'subtle'));
});
}
if (!breakpoint.resolved) {
element.add(span(text: ' (unresolved)', c: 'subtle'));
}
return element;
});
}
final CoreElement _breakpointsCountDiv;
SelectableList<Breakpoint> _items;
Stream<Breakpoint> get onDoubleClick => _items.onDoubleClick;
@override
CoreElement get element => _items;
Stream<Breakpoint> get onSelectionChanged => _items.onSelectionChanged;
void showBreakpoints(List<Breakpoint> breakpoints) {
breakpoints = breakpoints.toList();
breakpoints.sort(_breakpointComparator);
_items.setItems(breakpoints);
_breakpointsCountDiv.text = breakpoints.length.toString();
}
}
class BreakOnExceptionControl extends CoreElement {
BreakOnExceptionControl()
: super('div', classes: 'break-on-exceptions flex-no-wrap') {
final CoreElement unhandledExceptionsElement = CoreElement('input')
..setAttribute('type', 'checkbox');
_unhandledElement = unhandledExceptionsElement.element;
final CoreElement allExceptionsElement = CoreElement('input')
..setAttribute('type', 'checkbox');
_allElement = allExceptionsElement.element;
add([
span(text: 'Break on', c: 'strong'),
span(text: ' exceptions', c: 'strong optional-1000'),
span(text: ': ', c: 'strong'),
CoreElement('label')
..add(<CoreElement>[
unhandledExceptionsElement,
span(text: ' unhandled')
]),
CoreElement('label')
..add(<CoreElement>[
allExceptionsElement,
span(text: ' all'),
]),
]);
unhandledExceptionsElement.element.onChange.listen((_) {
ga.select(ga.debugger, ga.unhandledExceptions);
_pauseModeController.add(exceptionPauseMode);
});
allExceptionsElement.element.onChange.listen((_) {
ga.select(ga.debugger, ga.allExceptions);
if (_allElement.checked) {
unhandledExceptionsElement.enabled = false;
_unhandledElement.checked = true;
} else {
unhandledExceptionsElement.enabled = true;
}
_pauseModeController.add(exceptionPauseMode);
});
}
html.InputElement _unhandledElement;
html.InputElement _allElement;
final StreamController<String> _pauseModeController =
StreamController.broadcast();
/// See the string values for [ExceptionPauseMode].
Stream<String> get onPauseModeChanged => _pauseModeController.stream;
String get exceptionPauseMode {
if (_allElement.checked) {
return ExceptionPauseMode.kAll;
} else if (_unhandledElement.checked) {
return ExceptionPauseMode.kUnhandled;
} else {
return ExceptionPauseMode.kNone;
}
}
set exceptionPauseMode(final String value) {
if (value == ExceptionPauseMode.kAll) {
_allElement.checked = true;
_unhandledElement.checked = true;
_unhandledElement.setAttribute('disabled', '');
} else if (value == ExceptionPauseMode.kUnhandled) {
_allElement.checked = false;
_unhandledElement.checked = true;
_unhandledElement.attributes.remove('disabled');
} else {
_allElement.checked = false;
_unhandledElement.checked = false;
_unhandledElement.attributes.remove('disabled');
}
}
}