blob: 7acdb1c2e1e99b5a58818e46fa4c624b8b97d1d6 [file] [log] [blame]
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
part of app;
/// A [Pane] controls the user interface of Observatory. At any given time
/// one pane will be the current pane. Panes are registered at startup.
/// When the user navigates within the application, each pane is asked if it
/// can handle the current location, the first pane to say yes, wins.
abstract class Pane extends Observable {
final ObservatoryApplication app;
@observable ObservatoryElement element;
Pane(this.app);
/// Called when the pane is installed, this callback must initialize
/// [element].
void onInstall();
/// Called when the pane is uninstalled, this callback must clear
/// [element].
void onUninstall() {
element = null;
}
/// Called when the pane should update its state based on [url].
/// NOTE: Only called when the pane is installed.
void visit(String url);
/// Called to test whether this pane can visit [url].
bool canVisit(String url);
}
/// A general service object viewer.
class ServiceObjectPane extends Pane {
ServiceObjectPane(app) : super(app);
void onInstall() {
if (element == null) {
/// Lazily create pane.
element = new Element.tag('service-view');
}
}
void visit(String url) {
assert(element != null);
assert(canVisit(url));
/// Request url from VM and display it.
app.vm.get(url).then((obj) {
ServiceObjectViewElement pane = element;
pane.object = obj;
});
}
/// Catch all.
bool canVisit(String url) => true;
}
/// Class tree pane.
class ClassTreePane extends Pane {
static const _urlPrefix = 'class-tree/';
ClassTreePane(app) : super(app);
void onInstall() {
if (element == null) {
element = new Element.tag('class-tree');
}
}
void visit(String url) {
assert(element != null);
assert(canVisit(url));
// ClassTree urls are 'class-tree/isolate-id', chop off prefix, leaving
// isolate url.
url = url.substring(_urlPrefix.length);
/// Request the isolate url.
app.vm.get(url).then((i) {
if (element != null) {
/// Update the pane.
ClassTreeElement pane = element;
pane.isolate = i;
}
});
}
/// Catch all.
bool canVisit(String url) => url.startsWith(_urlPrefix);
}
class ErrorViewPane extends Pane {
ErrorViewPane(app) : super(app);
void onInstall() {
if (element == null) {
/// Lazily create pane.
element = new Element.tag('service-view');
}
}
void visit(String url) {
assert(element != null);
assert(canVisit(url));
(element as ServiceObjectViewElement).object = app.lastErrorOrException;
}
bool canVisit(String url) => url.startsWith('error/');
}
/// The observatory application. Instances of this are created and owned
/// by the observatory_application custom element.
class ObservatoryApplication extends Observable {
final _paneRegistry = new List<Pane>();
ServiceObjectPane _serviceObjectPane;
Pane _currentPane;
@observable final LocationManager locationManager;
@observable final VM vm;
@observable Isolate isolate;
@reflectable final ObservatoryApplicationElement rootElement;
@reflectable ServiceObject lastErrorOrException;
/// Any client-level arguments for viewing the current response.
@observable String args;
// TODO(turnidge): Make args a Map.
void _initOnce() {
_registerPanes();
vm.errors.stream.listen(_onError);
vm.exceptions.stream.listen(_onException);
location = locationManager;
locationManager._init(this);
}
void _registerPanes() {
if (_serviceObjectPane != null) {
// Already done.
return;
}
// Register ClassTreePane.
_paneRegistry.add(new ClassTreePane(this));
_paneRegistry.add(new ErrorViewPane(this));
// Note that ServiceObjectPane must be the last entry in the list as it is
// the catch all.
_serviceObjectPane = new ServiceObjectPane(this);
_paneRegistry.add(_serviceObjectPane);
}
void _onError(ServiceError error) {
lastErrorOrException = error;
_visit('error/');
}
void _onException(ServiceException exception) {
lastErrorOrException = exception;
_visit('error/');
}
void _visit(String url) {
for (var i = 0; i < _paneRegistry.length; i++) {
var pane = _paneRegistry[i];
if (pane.canVisit(url)) {
_installPane(pane);
pane.visit(url);
return;
}
}
throw new FallThroughError();
}
/// Set the Observatory application pane.
void _installPane(Pane pane) {
assert(pane != null);
print('Installing $pane');
if (_currentPane == pane) {
// Already isntalled.
return;
}
if (_currentPane != null) {
_currentPane.onUninstall();
}
pane.onInstall();
// Clear children.
rootElement.children.clear();
// Add new pane.
rootElement.children.add(pane.element);
// Remember pane.
_currentPane = pane;
}
ObservatoryApplication.devtools(this.rootElement) :
locationManager = new HashLocationManager(),
vm = new DartiumVM() {
_initOnce();
}
ObservatoryApplication(this.rootElement) :
locationManager = new HashLocationManager(),
vm = new HttpVM() {
_initOnce();
}
}
LocationManager location;