Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 part of app; | 5 part of app; |
| 6 | 6 |
| 7 /// A [Page] controls the user interface of Observatory. At any given time | 7 /// A [Page] controls the user interface of Observatory. At any given time |
| 8 /// one page will be the current page. Pages are registered at startup. | 8 /// one page will be the current page. Pages are registered at startup. |
| 9 /// When the user navigates within the application, each page is asked if it | 9 /// When the user navigates within the application, each page is asked if it |
| 10 /// can handle the current location, the first page to say yes, wins. | 10 /// can handle the current location, the first page to say yes, wins. |
| 11 abstract class Page extends Observable { | 11 abstract class Page extends Observable { |
| 12 final ObservatoryApplication app; | 12 final ObservatoryApplication app; |
| 13 | 13 |
| 14 @observable ObservatoryElement element; | 14 @observable ObservatoryElement element; |
| 15 @observable ObservableMap args; | 15 @observable ObservableMap args; |
| 16 | 16 |
| 17 Page(this.app); | 17 Page(this.app); |
| 18 | 18 |
| 19 /// Called when the page is installed, this callback must initialize | 19 /// Called when the page is installed, this callback must initialize |
| 20 /// [element]. | 20 /// [element]. |
| 21 void onInstall(); | 21 void onInstall(); |
| 22 | 22 |
| 23 /// Called when the page is uninstalled, this callback must clear | 23 /// Called when the page is uninstalled, this callback must clear |
| 24 /// [element]. | 24 /// [element]. |
| 25 void onUninstall() { | 25 void onUninstall() { |
| 26 element = null; | 26 element = null; |
| 27 } | 27 } |
| 28 | 28 |
| 29 /// Called when the page should update its state based on [url]. | 29 /// Called when the page should update its state based on [uri]. |
| 30 /// NOTE: Only called when the page is installed. | 30 /// NOTE: Only called when the page is installed. |
| 31 void visit(String url, Map argsMap) { | 31 void visit(Uri uri, Map argsMap) { |
| 32 args = toObservable(argsMap); | 32 args = toObservable(argsMap); |
| 33 _visit(url); | 33 _visit(uri); |
| 34 } | 34 } |
| 35 | 35 |
| 36 // Overridden by subclasses. | 36 // Overridden by subclasses. |
| 37 void _visit(String url); | 37 void _visit(Uri uri); |
| 38 | 38 |
| 39 /// Called to test whether this page can visit [url]. | 39 /// Called to test whether this page can visit [uri]. |
| 40 bool canVisit(String url); | 40 bool canVisit(Uri uri); |
| 41 } | 41 } |
| 42 | 42 |
| 43 /// A general service object viewer. | 43 /// A [SimplePage] matches a single uri path and displays a single element. |
| 44 class ServiceObjectPage extends Page { | 44 class SimplePage extends Page { |
| 45 ServiceObjectPage(app) : super(app); | 45 final String path; |
| 46 | |
| 47 void onInstall() { | |
| 48 if (element == null) { | |
| 49 /// Lazily create page. | |
| 50 element = new Element.tag('service-view'); | |
| 51 } | |
| 52 } | |
| 53 | |
| 54 void _visit(String url) { | |
| 55 assert(element != null); | |
| 56 assert(canVisit(url)); | |
| 57 if (url == '') { | |
| 58 // Nothing requested. | |
| 59 return; | |
| 60 } | |
| 61 /// Request url from VM and display it. | |
| 62 app.vm.getDeprecated(url).then((obj) { | |
| 63 ServiceObjectViewElement serviceElement = element; | |
| 64 serviceElement.object = obj; | |
| 65 }).catchError((e) { | |
| 66 Logger.root.severe('ServiceObjectPage visit error: $e'); | |
| 67 }); | |
| 68 } | |
| 69 | |
| 70 /// Catch all. | |
| 71 bool canVisit(String url) => true; | |
| 72 } | |
| 73 | |
| 74 class IsolateSuffixPage extends Page { | |
| 75 final String pagePrefix; | |
| 76 final String elementTagName; | 46 final String elementTagName; |
| 77 String _isolateId; | 47 SimplePage(this.path, this.elementTagName, app) : super(app); |
| 78 String get isolateId => _isolateId; | |
| 79 IsolateSuffixPage(this.pagePrefix, this.elementTagName, app) : super(app); | |
| 80 | 48 |
| 81 void onInstall() { | 49 void onInstall() { |
| 82 if (element == null) { | 50 if (element == null) { |
| 83 element = new Element.tag(elementTagName); | 51 element = new Element.tag(elementTagName); |
| 84 } | 52 } |
| 85 } | 53 } |
| 86 | 54 |
| 87 void _visit(String url) { | 55 void _visit(Uri uri) { |
| 88 assert(url != null); | 56 assert(uri != null); |
| 89 assert(canVisit(url)); | 57 assert(canVisit(uri)); |
| 90 _isolateId = url.substring(pagePrefix.length); | |
| 91 } | 58 } |
| 92 | 59 |
| 93 Future<Isolate> getIsolate() { | 60 Future<Isolate> getIsolate(Uri uri) { |
| 94 return app.vm.getIsolate(isolateId).catchError((e) { | 61 return app.vm.getIsolate(uri.queryParameters['isolateId']).catchError((e) { |
| 95 Logger.root.severe('$pagePrefix visit error: $e'); | 62 Logger.root.severe('$path visit error: $e'); |
| 96 return e; | 63 return e; |
| 97 }); | 64 }); |
| 98 } | 65 } |
| 99 | 66 |
| 100 bool canVisit(String url) => url.startsWith(pagePrefix); | 67 bool canVisit(Uri uri) => uri.path == path; |
| 68 } | |
| 69 | |
| 70 /// Error page for unrecognized paths. | |
| 71 class ErrorPage extends Page { | |
| 72 ErrorPage(app) : super(app); | |
| 73 | |
| 74 void onInstall() { | |
| 75 if (element == null) { | |
| 76 // Lazily create page. | |
| 77 element = new Element.tag('general-error'); | |
| 78 } | |
| 79 } | |
| 80 | |
| 81 void _visit(Uri uri) { | |
| 82 assert(element != null); | |
| 83 assert(canVisit(uri)); | |
| 84 | |
| 85 if (uri.path == '') { | |
| 86 // Nothing requested. | |
|
Cutch
2015/02/05 14:54:48
Should we display something in this case?
turnidge
2015/02/05 17:11:36
Removed.
| |
| 87 return; | |
| 88 } | |
| 89 | |
| 90 if (element != null) { | |
| 91 GeneralErrorElement serviceElement = element; | |
| 92 serviceElement.message = "Path '${uri.path}' not found"; | |
| 93 } | |
| 94 } | |
| 95 | |
| 96 /// Catch all. | |
| 97 bool canVisit(Uri uri) => true; | |
| 98 } | |
| 99 | |
| 100 /// Top-level vm info page. | |
| 101 class VMPage extends SimplePage { | |
| 102 VMPage(app) : super('vm', 'service-view', app); | |
| 103 | |
| 104 void _visit(Uri uri) { | |
| 105 super._visit(uri); | |
| 106 app.vm.reload().then((vm) { | |
| 107 if (element != null) { | |
| 108 ServiceObjectViewElement serviceElement = element; | |
| 109 serviceElement.object = vm; | |
| 110 } | |
| 111 }).catchError((e) { | |
| 112 Logger.root.severe('VMPage visit error: $e'); | |
| 113 }); | |
| 114 } | |
| 115 } | |
| 116 | |
| 117 class FlagsPage extends SimplePage { | |
| 118 FlagsPage(app) : super('flags', 'flag-list', app); | |
| 119 | |
| 120 void _visit(Uri uri) { | |
| 121 super._visit(uri); | |
| 122 app.vm.getFlagList().then((flags) { | |
| 123 if (element != null) { | |
| 124 FlagListElement serviceElement = element; | |
| 125 serviceElement.flagList = flags; | |
| 126 } | |
| 127 }).catchError((e) { | |
| 128 Logger.root.severe('FlagsPage visit error: $e'); | |
| 129 }); | |
| 130 } | |
| 131 } | |
| 132 | |
| 133 class InspectPage extends SimplePage { | |
| 134 InspectPage(app) : super('inspect', 'service-view', app); | |
| 135 | |
| 136 void _visit(Uri uri) { | |
| 137 super._visit(uri); | |
| 138 getIsolate(uri).then((isolate) { | |
| 139 var objectId = uri.queryParameters['objectId']; | |
| 140 if (objectId == null) { | |
| 141 isolate.reload().then(_visitObject); | |
| 142 } else { | |
| 143 isolate.getObject(objectId).then(_visitObject); | |
| 144 } | |
| 145 }); | |
| 146 } | |
| 147 | |
| 148 void _visitObject(obj) { | |
| 149 if (element != null) { | |
| 150 ServiceObjectViewElement serviceElement = element; | |
| 151 serviceElement.object = obj; | |
| 152 } | |
| 153 } | |
| 101 } | 154 } |
| 102 | 155 |
| 103 | 156 |
| 104 /// Class tree page. | 157 /// Class tree page. |
| 105 class ClassTreePage extends IsolateSuffixPage { | 158 class ClassTreePage extends SimplePage { |
| 106 ClassTreePage(app) : super('class-tree/', 'class-tree', app); | 159 ClassTreePage(app) : super('class-tree', 'class-tree', app); |
| 107 | 160 |
| 108 void _visit(String url) { | 161 void _visit(Uri uri) { |
| 109 super._visit(url); | 162 super._visit(uri); |
| 110 getIsolate().then((isolate) { | 163 getIsolate(uri).then((isolate) { |
| 111 if (element != null) { | 164 if (element != null) { |
| 112 /// Update the page. | 165 /// Update the page. |
| 113 ClassTreeElement page = element; | 166 ClassTreeElement page = element; |
| 114 page.isolate = isolate; | 167 page.isolate = isolate; |
| 115 } | 168 } |
| 116 }); | 169 }); |
| 117 } | 170 } |
| 118 } | 171 } |
| 119 | 172 |
| 120 class DebuggerPage extends IsolateSuffixPage { | 173 class DebuggerPage extends SimplePage { |
| 121 DebuggerPage(app) : super('debugger/', 'debugger-page', app); | 174 DebuggerPage(app) : super('debugger', 'debugger-page', app); |
| 122 | 175 |
| 123 void _visit(String url) { | 176 void _visit(Uri uri) { |
| 124 super._visit(url); | 177 super._visit(uri); |
| 125 getIsolate().then((isolate) { | 178 getIsolate(uri).then((isolate) { |
| 126 if (element != null) { | 179 if (element != null) { |
| 127 /// Update the page. | 180 /// Update the page. |
| 128 DebuggerPageElement page = element; | 181 DebuggerPageElement page = element; |
| 129 page.isolate = isolate; | 182 page.isolate = isolate; |
| 130 } | 183 } |
| 131 }); | 184 }); |
| 132 } | 185 } |
| 133 } | 186 } |
| 134 | 187 |
| 135 class CpuProfilerPage extends IsolateSuffixPage { | 188 class CpuProfilerPage extends SimplePage { |
| 136 CpuProfilerPage(app) : super('profiler/', 'cpu-profile', app); | 189 CpuProfilerPage(app) : super('profiler', 'cpu-profile', app); |
| 137 | 190 |
| 138 void _visit(String url) { | 191 void _visit(Uri uri) { |
| 139 super._visit(url); | 192 super._visit(uri); |
| 140 getIsolate().then((isolate) { | 193 getIsolate(uri).then((isolate) { |
| 141 if (element != null) { | 194 if (element != null) { |
| 142 /// Update the page. | 195 /// Update the page. |
| 143 CpuProfileElement page = element; | 196 CpuProfileElement page = element; |
| 144 page.isolate = isolate; | 197 page.isolate = isolate; |
| 145 } | 198 } |
| 146 }); | 199 }); |
| 147 } | 200 } |
| 148 } | 201 } |
| 149 | 202 |
| 150 class AllocationProfilerPage extends IsolateSuffixPage { | 203 class AllocationProfilerPage extends SimplePage { |
| 151 AllocationProfilerPage(app) | 204 AllocationProfilerPage(app) |
| 152 : super('allocation-profiler/', 'heap-profile', app); | 205 : super('allocation-profiler', 'heap-profile', app); |
| 153 | 206 |
| 154 void _visit(String url) { | 207 void _visit(Uri uri) { |
| 155 super._visit(url); | 208 super._visit(uri); |
| 156 getIsolate().then((isolate) { | 209 getIsolate(uri).then((isolate) { |
| 157 if (element != null) { | 210 if (element != null) { |
| 158 /// Update the page. | 211 /// Update the page. |
| 159 HeapProfileElement page = element; | 212 HeapProfileElement page = element; |
| 160 page.isolate = isolate; | 213 page.isolate = isolate; |
| 161 } | 214 } |
| 162 }); | 215 }); |
| 163 } | 216 } |
| 164 } | 217 } |
| 165 | 218 |
| 166 class HeapMapPage extends IsolateSuffixPage { | 219 class HeapMapPage extends SimplePage { |
| 167 HeapMapPage(app) : super('heap-map/', 'heap-map', app); | 220 HeapMapPage(app) : super('heap-map', 'heap-map', app); |
| 168 | 221 |
| 169 void _visit(String url) { | 222 void _visit(Uri uri) { |
| 170 super._visit(url); | 223 super._visit(uri); |
| 171 getIsolate().then((isolate) { | 224 getIsolate(uri).then((isolate) { |
| 172 if (element != null) { | 225 if (element != null) { |
| 173 /// Update the page. | 226 /// Update the page. |
| 174 HeapMapElement page = element; | 227 HeapMapElement page = element; |
| 175 page.isolate = isolate; | 228 page.isolate = isolate; |
| 176 } | 229 } |
| 177 }); | 230 }); |
| 178 } | 231 } |
| 179 } | 232 } |
| 180 | 233 |
| 181 class ErrorViewPage extends Page { | 234 class ErrorViewPage extends Page { |
| 182 ErrorViewPage(app) : super(app); | 235 ErrorViewPage(app) : super(app); |
| 183 | 236 |
| 184 void onInstall() { | 237 void onInstall() { |
| 185 if (element == null) { | 238 if (element == null) { |
| 186 /// Lazily create page. | 239 /// Lazily create page. |
| 187 element = new Element.tag('service-view'); | 240 element = new Element.tag('service-view'); |
| 188 } | 241 } |
| 189 } | 242 } |
| 190 | 243 |
| 191 void _visit(String url) { | 244 void _visit(Uri uri) { |
| 192 assert(element != null); | 245 assert(element != null); |
| 193 assert(canVisit(url)); | 246 assert(canVisit(uri)); |
| 194 (element as ServiceObjectViewElement).object = app.lastErrorOrException; | 247 (element as ServiceObjectViewElement).object = app.lastErrorOrException; |
| 195 } | 248 } |
| 196 | 249 |
| 197 bool canVisit(String url) => url.startsWith('error/'); | 250 // TODO(turnidge): How to test this page? |
| 251 bool canVisit(Uri uri) => uri.path.startsWith('error/'); | |
| 198 } | 252 } |
| 199 | 253 |
| 200 class VMConnectPage extends Page { | 254 class VMConnectPage extends Page { |
| 201 VMConnectPage(app) : super(app); | 255 VMConnectPage(app) : super(app); |
| 202 | 256 |
| 203 void onInstall() { | 257 void onInstall() { |
| 204 if (element == null) { | 258 if (element == null) { |
| 205 element = new Element.tag('vm-connect'); | 259 element = new Element.tag('vm-connect'); |
| 206 } | 260 } |
| 207 assert(element != null); | 261 assert(element != null); |
| 208 } | 262 } |
| 209 | 263 |
| 210 void _visit(String url) { | 264 void _visit(Uri uri) { |
| 211 assert(element != null); | 265 assert(element != null); |
| 212 assert(canVisit(url)); | 266 assert(canVisit(uri)); |
| 213 } | 267 } |
| 214 | 268 |
| 215 bool canVisit(String url) => url.startsWith('vm-connect/'); | 269 // TODO(turnidge): Update this to not have the trailing slash. |
| 270 bool canVisit(Uri uri) => uri.path.startsWith('vm-connect/'); | |
| 216 } | 271 } |
| 217 | 272 |
| 218 class MetricsPage extends Page { | 273 class MetricsPage extends Page { |
| 219 static RegExp _matcher = new RegExp(r'isolates/.*/metrics'); | |
| 220 static RegExp _isolateMatcher = new RegExp(r'isolates/.*/'); | |
| 221 | |
| 222 // Page state, retained as long as ObservatoryApplication. | 274 // Page state, retained as long as ObservatoryApplication. |
| 223 String selectedMetricId; | 275 String selectedMetricId; |
| 224 | 276 |
| 225 final Map<int, MetricPoller> pollers = new Map<int, MetricPoller>(); | 277 final Map<int, MetricPoller> pollers = new Map<int, MetricPoller>(); |
| 226 | 278 |
| 227 // 8 seconds, 4 seconds, 2 seconds, 1 second, and one hundred milliseconds. | 279 // 8 seconds, 4 seconds, 2 seconds, 1 second, and one hundred milliseconds. |
| 228 static final List<int> POLL_PERIODS = [8000, | 280 static final List<int> POLL_PERIODS = [8000, |
| 229 4000, | 281 4000, |
| 230 2000, | 282 2000, |
| 231 1000, | 283 1000, |
| (...skipping 27 matching lines...) Expand all Loading... | |
| 259 } | 311 } |
| 260 var poller = pollers[refreshPeriod]; | 312 var poller = pollers[refreshPeriod]; |
| 261 if (poller != null) { | 313 if (poller != null) { |
| 262 poller.metrics.add(metric); | 314 poller.metrics.add(metric); |
| 263 metric.poller = poller; | 315 metric.poller = poller; |
| 264 return; | 316 return; |
| 265 } | 317 } |
| 266 throw new FallThroughError(); | 318 throw new FallThroughError(); |
| 267 } | 319 } |
| 268 | 320 |
| 269 String _isolateId(String url) { | 321 void _visit(Uri uri) { |
| 270 // Grab isolate prefix. | |
| 271 String isolateId = _isolateMatcher.stringMatch(url); | |
| 272 // Remove the trailing slash. | |
| 273 return isolateId.substring(0, isolateId.length - 1); | |
| 274 } | |
| 275 | |
| 276 void _visit(String url) { | |
| 277 assert(element != null); | 322 assert(element != null); |
| 278 assert(canVisit(url)); | 323 assert(canVisit(uri)); |
| 279 app.vm.getIsolate(_isolateId(url)).then((i) { | 324 app.vm.getIsolate(uri.queryParameters['isolateId']).then((i) { |
| 280 (element as MetricsPageElement).isolate = i; | 325 (element as MetricsPageElement).isolate = i; |
| 281 }); | 326 }); |
| 282 } | 327 } |
| 283 | 328 |
| 284 bool canVisit(String url) => _matcher.hasMatch(url); | 329 bool canVisit(Uri uri) => uri.path == 'metrics'; |
| 285 } | 330 } |
| OLD | NEW |