Version 2.11.0-199.0.dev

Merge commit 'f84bba289746ee865cbfa4fe577cfe9771621995' into 'dev'
diff --git a/.dart_tool/package_config.json b/.dart_tool/package_config.json
index 5ef3143..3137119 100644
--- a/.dart_tool/package_config.json
+++ b/.dart_tool/package_config.json
@@ -445,11 +445,22 @@
       "languageVersion": "2.2"
     },
     {
+      "name": "observatory_2",
+      "rootUri": "../runtime/observatory_2",
+      "packageUri": "lib/",
+      "languageVersion": "2.2"
+    },
+    {
       "name": "observatory_test_package",
       "rootUri": "../runtime/observatory/tests/service/observatory_test_package",
       "languageVersion": "2.7"
     },
     {
+      "name": "observatory_test_package_2",
+      "rootUri": "../runtime/observatory_2/tests/service_2/observatory_test_package_2",
+      "languageVersion": "2.7"
+    },
+    {
       "name": "package_config",
       "rootUri": "../third_party/pkg_tested/package_config",
       "packageUri": "lib/",
diff --git a/pkg/analysis_server/lib/src/edit/edit_domain.dart b/pkg/analysis_server/lib/src/edit/edit_domain.dart
index 570c266..b702179 100644
--- a/pkg/analysis_server/lib/src/edit/edit_domain.dart
+++ b/pkg/analysis_server/lib/src/edit/edit_domain.dart
@@ -42,9 +42,11 @@
 import 'package:analyzer/file_system/file_system.dart';
 import 'package:analyzer/source/line_info.dart';
 import 'package:analyzer/src/analysis_options/analysis_options_provider.dart';
+import 'package:analyzer/src/dart/analysis/analysis_context_collection.dart';
 import 'package:analyzer/src/dart/analysis/results.dart' as engine;
 import 'package:analyzer/src/dart/ast/utilities.dart';
 import 'package:analyzer/src/dart/scanner/scanner.dart' as engine;
+import 'package:analyzer/src/dart/sdk/sdk.dart';
 import 'package:analyzer/src/error/codes.dart' as engine;
 import 'package:analyzer/src/exception/exception.dart';
 import 'package:analyzer/src/generated/engine.dart' as engine;
@@ -105,15 +107,21 @@
         }
       }
 
-      var paths = <String>[];
-      for (var include in params.included) {
-        var resource = server.resourceProvider.getResource(include);
-        resource.collectDartFilePaths(paths);
-      }
-
       var workspace = DartChangeWorkspace(server.currentSessions);
       var processor = BulkFixProcessor(workspace);
-      var changeBuilder = await processor.fixErrorsInLibraries(paths);
+
+      String sdkPath;
+      var sdk = server.findSdk();
+      if (sdk is FolderBasedDartSdk) {
+        sdkPath = sdk.directory.path;
+      }
+      var collection = AnalysisContextCollectionImpl(
+        includedPaths: params.included,
+        resourceProvider: server.resourceProvider,
+        sdkPath: sdkPath,
+      );
+      var changeBuilder = await processor.fixErrors(collection.contexts);
+
       var response = EditBulkFixesResult(changeBuilder.sourceChange.edits)
           .toResponse(request.id);
       server.sendResponse(response);
@@ -1315,15 +1323,3 @@
 /// [_RefactoringManager] throws instances of this class internally to stop
 /// processing in a manager that was reset.
 class _ResetError {}
-
-extension ResourceExtension on Resource {
-  void collectDartFilePaths(List<String> paths) {
-    if (this is File && AnalysisEngine.isDartFileName(path)) {
-      paths.add(path);
-    } else if (this is Folder) {
-      for (var child in (this as Folder).getChildren()) {
-        child.collectDartFilePaths(paths);
-      }
-    }
-  }
-}
diff --git a/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart b/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart
index 5bd6ce2..0de1a9d 100644
--- a/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart
+++ b/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart
@@ -61,10 +61,12 @@
 import 'package:analysis_server/src/services/correction/fix.dart';
 import 'package:analysis_server/src/services/correction/fix_internal.dart';
 import 'package:analysis_server/src/services/linter/lint_names.dart';
+import 'package:analyzer/dart/analysis/analysis_context.dart';
 import 'package:analyzer/dart/analysis/results.dart';
 import 'package:analyzer/error/error.dart';
 import 'package:analyzer/source/error_processor.dart';
 import 'package:analyzer/src/error/codes.dart';
+import 'package:analyzer/src/generated/engine.dart' show AnalysisEngine;
 import 'package:analyzer/src/generated/source.dart';
 import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
 
@@ -341,16 +343,22 @@
   }
 
   /// Return a change builder that has been used to create fixes for the
-  /// diagnostics in the libraries at the given [libraryPaths].
-  Future<ChangeBuilder> fixErrorsInLibraries(List<String> libraryPaths) async {
-    for (var path in libraryPaths) {
-      var session = workspace.getSession(path);
-      var kind = await session.getSourceKind(path);
-      if (kind == SourceKind.LIBRARY) {
-        var libraryResult = await session.getResolvedLibrary(path);
-        await _fixErrorsInLibrary(libraryResult);
+  /// diagnostics in the libraries in the given [contexts].
+  Future<ChangeBuilder> fixErrors(List<AnalysisContext> contexts) async {
+    for (var context in contexts) {
+      for (var path in context.contextRoot.analyzedFiles()) {
+        if (!AnalysisEngine.isDartFileName(path)) {
+          continue;
+        }
+        var kind = await context.currentSession.getSourceKind(path);
+        if (kind != SourceKind.LIBRARY) {
+          continue;
+        }
+        var library = await context.currentSession.getResolvedLibrary(path);
+        await _fixErrorsInLibrary(library);
       }
     }
+
     return builder;
   }
 
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/data_driven.dart b/pkg/analysis_server/lib/src/services/correction/dart/data_driven.dart
index 234f33a..837da74 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/data_driven.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/data_driven.dart
@@ -5,6 +5,7 @@
 import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
 import 'package:analysis_server/src/services/correction/fix.dart';
 import 'package:analysis_server/src/services/correction/fix/data_driven/element_descriptor.dart';
+import 'package:analysis_server/src/services/correction/fix/data_driven/element_matcher.dart';
 import 'package:analysis_server/src/services/correction/fix/data_driven/transform.dart';
 import 'package:analysis_server/src/services/correction/fix/data_driven/transform_set.dart';
 import 'package:analysis_server/src/services/correction/fix/data_driven/transform_set_manager.dart';
@@ -33,8 +34,9 @@
         importedUris.add(Uri.parse(uri));
       }
     }
+    var matcher = ElementMatcher(importedUris: importedUris, name: name);
     for (var set in _availableTransformSetsForLibrary(library)) {
-      for (var transform in set.transformsFor(name, importedUris)) {
+      for (var transform in set.transformsFor(matcher)) {
         yield DataDrivenFix(transform);
       }
     }
@@ -42,27 +44,40 @@
 
   /// Return the name of the element that was changed.
   String get _name {
+    String nameFromParent(AstNode node) {
+      var parent = node.parent;
+      if (parent is MethodInvocation) {
+        return parent.methodName.name;
+      } else if (parent is InstanceCreationExpression) {
+        var constructorName = parent.constructorName;
+        if (constructorName.name != null) {
+          return constructorName.name.name;
+        }
+        return constructorName.type.name.name;
+      } else if (parent is ExtensionOverride) {
+        return parent.extensionName.name;
+      }
+      return null;
+    }
+
     var node = this.node;
     if (node is SimpleIdentifier) {
+      var parent = node.parent;
+      if (parent is Label && parent.parent is NamedExpression) {
+        // The parent of the named expression is an argument list. Because we
+        // don't represent parameters as elements, the element we need to match
+        // against is the invocation containing those arguments.
+        return nameFromParent(parent.parent.parent);
+      }
       return node.name;
     } else if (node is ConstructorName) {
       return node.name.name;
     } else if (node is NamedType) {
       return node.name.name;
     } else if (node is TypeArgumentList) {
-      var parent = node.parent;
-      if (parent is MethodInvocation) {
-        return parent.methodName.name;
-      } else if (parent is ExtensionOverride) {
-        return parent.extensionName.name;
-      }
+      return nameFromParent(node);
     } else if (node is ArgumentList) {
-      var parent = node.parent;
-      if (parent is MethodInvocation) {
-        return parent.methodName.name;
-      } else if (parent is ExtensionOverride) {
-        return parent.extensionName.name;
-      }
+      return nameFromParent(node);
     }
     return null;
   }
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/element_descriptor.dart b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/element_descriptor.dart
index 1b25a68..84b5dd0 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/element_descriptor.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/element_descriptor.dart
@@ -27,23 +27,4 @@
 
   /// Return `true` if the described element is a constructor.
   bool get isConstructor => _kind == 'constructor';
-
-  /// Return `true` if this descriptor matches an element with the given [name]
-  /// in a library that imports the [importedUris].
-  bool matches(String name, List<Uri> importedUris) {
-    var lastComponent = components.last;
-    if (lastComponent.isEmpty) {
-      if (components[components.length - 2] != name) {
-        return false;
-      }
-    } else if (lastComponent != name) {
-      return false;
-    }
-    for (var importedUri in importedUris) {
-      if (libraryUris.contains(importedUri)) {
-        return true;
-      }
-    }
-    return false;
-  }
 }
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/element_matcher.dart b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/element_matcher.dart
new file mode 100644
index 0000000..f18c300
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/element_matcher.dart
@@ -0,0 +1,41 @@
+// Copyright (c) 2020, 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.
+
+import 'package:analysis_server/src/services/correction/fix/data_driven/element_descriptor.dart';
+import 'package:meta/meta.dart';
+
+/// An object that can be used to determine whether an element is appropriate
+/// for a given reference.
+class ElementMatcher {
+  /// The URIs of the libraries that are imported in the library containing the
+  /// reference.
+  final List<Uri> importedUris;
+
+  /// The name of the element being referenced.
+  final String name;
+
+  /// Initialize a newly created matcher representing a reference to an element
+  /// with the given [name] in a library that imports the [importedUris].
+  ElementMatcher({@required this.importedUris, @required this.name});
+
+  /// Return `true` if this matcher matches the given [element].
+  bool matches(ElementDescriptor element) {
+    var components = element.components;
+    var lastComponent = components.last;
+    if (lastComponent.isEmpty) {
+      if (components[components.length - 2] != name) {
+        return false;
+      }
+    } else if (lastComponent != name) {
+      return false;
+    }
+    var libraryUris = element.libraryUris;
+    for (var importedUri in importedUris) {
+      if (libraryUris.contains(importedUri)) {
+        return true;
+      }
+    }
+    return false;
+  }
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform.dart b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform.dart
index 989b614..cae363d 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform.dart
@@ -4,6 +4,7 @@
 
 import 'package:analysis_server/src/services/correction/fix/data_driven/change.dart';
 import 'package:analysis_server/src/services/correction/fix/data_driven/element_descriptor.dart';
+import 'package:analysis_server/src/services/correction/fix/data_driven/element_matcher.dart';
 import 'package:meta/meta.dart';
 
 /// A description of a set of changes to a single element of the API.
@@ -29,9 +30,8 @@
       @required this.changes});
 
   /// Return `true` if this transform can be applied to fix an issue related to
-  /// an element with the given [name] in a library that imports the
-  /// [importedUris].
-  bool appliesTo(String name, List<Uri> importedUris) {
-    return element.matches(name, importedUris);
+  /// an element that matches the given [matcher].
+  bool appliesTo(ElementMatcher matcher) {
+    return matcher.matches(element);
   }
 }
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set.dart b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set.dart
index 5a966c6..df26be6 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set.dart
@@ -2,6 +2,7 @@
 // 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.
 
+import 'package:analysis_server/src/services/correction/fix/data_driven/element_matcher.dart';
 import 'package:analysis_server/src/services/correction/fix/data_driven/transform.dart';
 
 /// A set of transforms used to aid in the construction of fixes for issues
@@ -16,12 +17,11 @@
     _transforms.add(transform);
   }
 
-  /// Return a list of the transforms that apply for a reference to the given
-  /// [name] in a library that imports the [importedUris].
-  List<Transform> transformsFor(String name, List<Uri> importedUris) {
+  /// Return a list of the transforms that match the [matcher].
+  List<Transform> transformsFor(ElementMatcher matcher) {
     var result = <Transform>[];
     for (var transform in _transforms) {
-      if (transform.appliesTo(name, importedUris)) {
+      if (transform.appliesTo(matcher)) {
         result.add(transform);
       }
     }
diff --git a/pkg/analysis_server/test/edit/bulk_fixes_test.dart b/pkg/analysis_server/test/edit/bulk_fixes_test.dart
index cf1c6c7..18f2356 100644
--- a/pkg/analysis_server/test/edit/bulk_fixes_test.dart
+++ b/pkg/analysis_server/test/edit/bulk_fixes_test.dart
@@ -4,6 +4,7 @@
 
 import 'package:analysis_server/protocol/protocol_generated.dart';
 import 'package:analysis_server/src/edit/edit_domain.dart';
+import 'package:analyzer/src/generated/engine.dart';
 import 'package:analyzer_plugin/protocol/protocol_common.dart';
 import 'package:linter/src/rules.dart';
 import 'package:test/test.dart';
@@ -38,10 +39,77 @@
     super.setUp();
     registerLintRules();
     handler = EditDomainHandler(server);
+    createProject();
+  }
+
+  Future<void> test_annotateOverrides_excludedSubProject() async {
+    // Root project.
+    addAnalysisOptionsFile('''
+analyzer:
+  exclude:
+    - test/data/**
+''');
+
+    // Sub-project.
+    var subprojectRoot = '$projectPath/test/data/subproject';
+    newFile('$subprojectRoot/${AnalysisEngine.ANALYSIS_OPTIONS_YAML_FILE}',
+        content: '''
+linter:
+  rules:
+    - annotate_overrides
+''');
+
+    newFile('$subprojectRoot/${AnalysisEngine.PUBSPEC_YAML_FILE}', content: '''
+name: subproject
+''');
+
+    newFile('$subprojectRoot/test.dart', content: '''
+class A {
+  void f() {}
+}
+class B extends A {
+  void f() { }
+}
+''');
+
+    await assertNoEdits();
+  }
+
+  Future<void> test_annotateOverrides_subProject() async {
+    var subprojectRoot = '$projectPath/test/data/subproject';
+    newFile('$subprojectRoot/${AnalysisEngine.ANALYSIS_OPTIONS_YAML_FILE}',
+        content: '''
+linter:
+  rules:
+    - annotate_overrides
+''');
+
+    newFile('$subprojectRoot/${AnalysisEngine.PUBSPEC_YAML_FILE}', content: '''
+name: subproject
+''');
+
+    testFile = '$subprojectRoot/test.dart';
+    addTestFile('''
+class A {
+  void f() {}
+}
+class B extends A {
+  void f() { }
+}
+''');
+
+    await assertEditEquals('''
+class A {
+  void f() {}
+}
+class B extends A {
+  @override
+  void f() { }
+}
+''');
   }
 
   Future<void> test_unnecessaryNew() async {
-    createProject();
     addAnalysisOptionsFile('''
 linter:
   rules:
@@ -59,7 +127,6 @@
   }
 
   Future<void> test_unnecessaryNew_ignoredInOptions() async {
-    createProject();
     addAnalysisOptionsFile('''
 analyzer:
   errors:
@@ -76,7 +143,6 @@
   }
 
   Future<void> test_unnecessaryNew_ignoredInSource() async {
-    createProject();
     addAnalysisOptionsFile('''
 linter:
   rules:
@@ -91,7 +157,7 @@
   }
 
   Future<List<SourceFileEdit>> _getBulkEdits() async {
-    var request = EditBulkFixesParams([testFile]).toRequest('0');
+    var request = EditBulkFixesParams([projectPath]).toRequest('0');
     var response = await waitResponse(request);
     var result = EditBulkFixesResult.fromResponse(response);
     return result.edits;
diff --git a/pkg/analysis_server/test/integration/edit/bulk_fixes_test.dart b/pkg/analysis_server/test/integration/edit/bulk_fixes_test.dart
index 922c0c8..9b42331 100644
--- a/pkg/analysis_server/test/integration/edit/bulk_fixes_test.dart
+++ b/pkg/analysis_server/test/integration/edit/bulk_fixes_test.dart
@@ -2,6 +2,7 @@
 // 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.
 
+import 'package:analyzer/src/generated/engine.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
@@ -27,11 +28,23 @@
     standardAnalysisSetup();
   }
 
-  @failingTest
   Future<void> test_bulk_fix_override() async {
-    setupTarget();
+    writeFile(sourcePath(AnalysisEngine.ANALYSIS_OPTIONS_YAML_FILE), '''
+linter:
+  rules:
+    - annotate_overrides
+''');
+    writeFile(sourcePath('test.dart'), '''
+class A {
+  void f() {}
+}
+class B extends A {
+  void f() { }
+}
+''');
+    standardAnalysisSetup();
 
     var result = await sendEditBulkFixes([sourceDirectory.path]);
-    expect(result.edits.length, 1);
+    expect(result.edits, hasLength(1));
   }
 }
diff --git a/pkg/analysis_server/test/src/services/correction/fix/bulk/bulk_fix_processor.dart b/pkg/analysis_server/test/src/services/correction/fix/bulk/bulk_fix_processor.dart
index ef3a83a..bd51bc3 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/bulk/bulk_fix_processor.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/bulk/bulk_fix_processor.dart
@@ -58,7 +58,7 @@
     var tracker = DeclarationsTracker(MemoryByteStore(), resourceProvider);
     tracker.addContext(driver.analysisContext);
     var changeBuilder =
-        await BulkFixProcessor(workspace).fixErrorsInLibraries([testFile]);
+        await BulkFixProcessor(workspace).fixErrors([driver.analysisContext]);
     return changeBuilder.sourceChange;
   }
 
diff --git a/pkg/analysis_server/test/src/services/correction/fix/data_driven/rename_test.dart b/pkg/analysis_server/test/src/services/correction/fix/data_driven/rename_test.dart
index 0bd6479..c335a76 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/data_driven/rename_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/data_driven/rename_test.dart
@@ -15,6 +15,7 @@
     defineReflectiveTests(RenameConstructorTest);
     defineReflectiveTests(RenameExtensionTest);
     defineReflectiveTests(RenameFieldTest);
+    defineReflectiveTests(RenameGetterTest);
     defineReflectiveTests(RenameMethodTest);
     defineReflectiveTests(RenameMixinTest);
     defineReflectiveTests(RenameTopLevelFunctionTest);
@@ -741,6 +742,124 @@
 }
 
 @reflectiveTest
+class RenameGetterTest extends _AbstractRenameTest {
+  @override
+  String get _kind => 'getter';
+
+  Future<void> test_instance_nonReference_deprecated() async {
+    setPackageContent('''
+class C {
+  @deprecated
+  int get a => 0;
+  int get b => 1;
+}
+class D {
+  D({@deprecated int a; int c});
+}
+''');
+    setPackageData(_rename(['C', 'a'], 'b'));
+    await resolveTestUnit('''
+import '$importUri';
+
+D d = D(a: 2);
+''');
+    await assertNoFix();
+  }
+
+  Future<void> test_instance_reference_deprecated() async {
+    setPackageContent('''
+class C {
+  @deprecated
+  int get old => 0;
+  int get new => 1;
+}
+''');
+    setPackageData(_rename(['C', 'old'], 'new'));
+    await resolveTestUnit('''
+import '$importUri';
+
+void f(C c) {
+  c.old;
+}
+''');
+    await assertHasFix('''
+import '$importUri';
+
+void f(C c) {
+  c.new;
+}
+''');
+  }
+
+  Future<void> test_instance_reference_removed() async {
+    setPackageContent('''
+class C {
+  int get new => 1;
+}
+''');
+    setPackageData(_rename(['C', 'old'], 'new'));
+    await resolveTestUnit('''
+import '$importUri';
+
+void f(C c) {
+  c.old;
+}
+''');
+    await assertHasFix('''
+import '$importUri';
+
+void f(C c) {
+  c.new;
+}
+''');
+  }
+
+  Future<void> test_topLevel_reference_deprecated() async {
+    setPackageContent('''
+@deprecated
+int get old => 0;
+int get new => 1;
+''');
+    setPackageData(_rename(['old'], 'new'));
+    await resolveTestUnit('''
+import '$importUri';
+
+void f() {
+  old;
+}
+''');
+    await assertHasFix('''
+import '$importUri';
+
+void f() {
+  new;
+}
+''');
+  }
+
+  Future<void> test_topLevel_reference_removed() async {
+    setPackageContent('''
+int get new => 1;
+''');
+    setPackageData(_rename(['old'], 'new'));
+    await resolveTestUnit('''
+import '$importUri';
+
+void f() {
+  old;
+}
+''');
+    await assertHasFix('''
+import '$importUri';
+
+void f() {
+  new;
+}
+''', errorFilter: ignoreUnusedImport);
+  }
+}
+
+@reflectiveTest
 class RenameMethodTest extends _AbstractRenameTest {
   @override
   String get _kind => 'method';
diff --git a/pkg/analysis_server/test/src/services/correction/fix/data_driven/transform_set_parser_test.dart b/pkg/analysis_server/test/src/services/correction/fix/data_driven/transform_set_parser_test.dart
index 2be023e..a1341eb 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/data_driven/transform_set_parser_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/data_driven/transform_set_parser_test.dart
@@ -4,9 +4,11 @@
 
 import 'package:analysis_server/src/services/correction/fix/data_driven/add_type_parameter.dart';
 import 'package:analysis_server/src/services/correction/fix/data_driven/code_template.dart';
+import 'package:analysis_server/src/services/correction/fix/data_driven/element_matcher.dart';
 import 'package:analysis_server/src/services/correction/fix/data_driven/modify_parameters.dart';
 import 'package:analysis_server/src/services/correction/fix/data_driven/parameter_reference.dart';
 import 'package:analysis_server/src/services/correction/fix/data_driven/rename.dart';
+import 'package:analysis_server/src/services/correction/fix/data_driven/transform.dart';
 import 'package:analysis_server/src/services/correction/fix/data_driven/transform_set_error_code.dart';
 import 'package:analysis_server/src/services/correction/fix/data_driven/value_generator.dart';
 import 'package:matcher/matcher.dart';
@@ -46,7 +48,7 @@
             kind: 'argument'
             index: 1
 ''');
-    var transforms = result.transformsFor('f', uris);
+    var transforms = _transforms('f');
     expect(transforms, hasLength(1));
     var transform = transforms[0];
     expect(transform.title, 'Add');
@@ -82,7 +84,7 @@
       name: 'p'
       style: optional_positional
 ''');
-    var transforms = result.transformsFor('f', uris);
+    var transforms = _transforms('f');
     expect(transforms, hasLength(1));
     var transform = transforms[0];
     expect(transform.title, 'Add');
@@ -118,7 +120,7 @@
             kind: 'argument'
             index: 1
 ''');
-    var transforms = result.transformsFor('f', uris);
+    var transforms = _transforms('f');
     expect(transforms, hasLength(1));
     var transform = transforms[0];
     expect(transform.title, 'Add');
@@ -160,7 +162,7 @@
             kind: 'argument'
             index: 1
 ''');
-    var transforms = result.transformsFor('f', uris);
+    var transforms = _transforms('f');
     expect(transforms, hasLength(1));
     var transform = transforms[0];
     expect(transform.title, 'Add');
@@ -205,7 +207,7 @@
             kind: 'argument'
             index: 2
 ''');
-    var transforms = result.transformsFor('f', uris);
+    var transforms = _transforms('f');
     expect(transforms, hasLength(1));
     var transform = transforms[0];
     expect(transform.title, 'Add');
@@ -254,7 +256,7 @@
             uris: ['dart:core']
             name: 'String'
 ''');
-    var transforms = result.transformsFor('A', uris);
+    var transforms = _transforms('A');
     expect(transforms, hasLength(1));
     var transform = transforms[0];
     expect(transform.title, 'Add');
@@ -290,7 +292,7 @@
             kind: 'argument'
             name: 'p'
 ''');
-    var transforms = result.transformsFor('A', uris);
+    var transforms = _transforms('A');
     expect(transforms, hasLength(1));
     var transform = transforms[0];
     expect(transform.title, 'Add');
@@ -331,7 +333,7 @@
             kind: 'argument'
             index: 2
 ''');
-    var transforms = result.transformsFor('A', uris);
+    var transforms = _transforms('A');
     expect(transforms, hasLength(1));
     var transform = transforms[0];
     expect(transform.title, 'Add');
@@ -363,7 +365,7 @@
     getter: 'g'
   changes: []
 ''');
-    var transforms = result.transformsFor('g', uris);
+    var transforms = _transforms('g');
     expect(transforms, hasLength(1));
     var transform = transforms[0];
     expect(transform.title, 'Rename g');
@@ -382,7 +384,7 @@
     inMixin: 'A'
   changes: []
 ''');
-    var transforms = result.transformsFor('g', uris);
+    var transforms = _transforms('g');
     expect(transforms, hasLength(1));
     var transform = transforms[0];
     expect(transform.title, 'Rename g');
@@ -400,7 +402,7 @@
     getter: 'g'
   changes: []
 ''');
-    var transforms = result.transformsFor('g', uris);
+    var transforms = _transforms('g');
     expect(transforms, hasLength(1));
     var transform = transforms[0];
     expect(transform.title, 'Rename g');
@@ -419,7 +421,7 @@
     inClass: 'A'
   changes: []
 ''');
-    var transforms = result.transformsFor('m', uris);
+    var transforms = _transforms('m');
     expect(transforms, hasLength(1));
     var transform = transforms[0];
     expect(transform.title, 'Rename m');
@@ -437,7 +439,7 @@
     variable: 'v'
   changes: []
 ''');
-    var transforms = result.transformsFor('v', uris);
+    var transforms = _transforms('v');
     expect(transforms, hasLength(1));
     var transform = transforms[0];
     expect(transform.title, 'Rename v');
@@ -478,7 +480,7 @@
     - kind: 'removeParameter'
       name: 'p'
 ''');
-    var transforms = result.transformsFor('f', uris);
+    var transforms = _transforms('f');
     expect(transforms, hasLength(1));
     var transform = transforms[0];
     expect(transform.title, 'Remove');
@@ -504,7 +506,7 @@
     - kind: 'removeParameter'
       index: 0
 ''');
-    var transforms = result.transformsFor('f', uris);
+    var transforms = _transforms('f');
     expect(transforms, hasLength(1));
     var transform = transforms[0];
     expect(transform.title, 'Remove');
@@ -531,7 +533,7 @@
     - kind: 'rename'
       newName: 'B'
 ''');
-    var transforms = result.transformsFor('A', uris);
+    var transforms = _transforms('A');
     expect(transforms, hasLength(1));
     var transform = transforms[0];
     expect(transform.title, 'Rename A');
@@ -539,4 +541,10 @@
     var rename = transform.changes[0] as Rename;
     expect(rename.newName, 'B');
   }
+
+  ElementMatcher _matcher(String name) =>
+      ElementMatcher(importedUris: uris, name: name);
+
+  List<Transform> _transforms(String name) =>
+      result.transformsFor(_matcher(name));
 }
diff --git a/pkg/analyzer/lib/error/error.dart b/pkg/analyzer/lib/error/error.dart
index 8cd4882..ea02226 100644
--- a/pkg/analyzer/lib/error/error.dart
+++ b/pkg/analyzer/lib/error/error.dart
@@ -260,6 +260,8 @@
   CompileTimeErrorCode.LATE_FINAL_FIELD_WITH_CONST_CONSTRUCTOR,
   CompileTimeErrorCode.LATE_FINAL_LOCAL_ALREADY_ASSIGNED,
   CompileTimeErrorCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE,
+  CompileTimeErrorCode.MAIN_HAS_REQUIRED_NAMED_PARAMETERS,
+  CompileTimeErrorCode.MAIN_HAS_TOO_MANY_REQUIRED_POSITIONAL_PARAMETERS,
   CompileTimeErrorCode.MAIN_IS_NOT_FUNCTION,
   CompileTimeErrorCode.MAP_ENTRY_NOT_IN_MAP,
   CompileTimeErrorCode.MAP_KEY_TYPE_NOT_ASSIGNABLE,
diff --git a/pkg/analyzer/lib/src/dart/ast/ast.dart b/pkg/analyzer/lib/src/dart/ast/ast.dart
index 8fe624b..fdce821 100644
--- a/pkg/analyzer/lib/src/dart/ast/ast.dart
+++ b/pkg/analyzer/lib/src/dart/ast/ast.dart
@@ -4346,6 +4346,19 @@
 
   /// Return the kind of this parameter.
   ParameterKind get kind;
+
+  static void setDeclaredElement(
+    FormalParameter node,
+    ParameterElement element,
+  ) {
+    if (node is DefaultFormalParameter) {
+      setDeclaredElement(node.parameter, element);
+    } else if (node is SimpleFormalParameterImpl) {
+      node.declaredElement = element;
+    } else {
+      node.identifier.staticElement = element;
+    }
+  }
 }
 
 /// The formal parameter list of a method declaration, function declaration, or
diff --git a/pkg/analyzer/lib/src/dart/element/element.dart b/pkg/analyzer/lib/src/dart/element/element.dart
index 77c0646..9e0b07a 100644
--- a/pkg/analyzer/lib/src/dart/element/element.dart
+++ b/pkg/analyzer/lib/src/dart/element/element.dart
@@ -486,7 +486,13 @@
 
   ClassElementImpl.forLinkedNode(CompilationUnitElementImpl enclosing,
       Reference reference, AstNode linkedNode)
-      : super.forLinkedNode(enclosing, reference, linkedNode);
+      : super.forLinkedNode(enclosing, reference, linkedNode) {
+    if (linkedNode is ClassDeclaration) {
+      linkedNode.name.staticElement = this;
+    } else if (linkedNode is ClassTypeAlias) {
+      linkedNode.name.staticElement = this;
+    }
+  }
 
   @override
   List<PropertyAccessorElement> get accessors {
@@ -543,10 +549,9 @@
       _constructors = context.getConstructors(linkedNode).map((node) {
         var name = node.name?.name ?? '';
         var reference = containerRef.getChild(name);
-        if (reference.hasElementFor(node)) {
-          return reference.element as ConstructorElement;
-        }
-        return ConstructorElementImpl.forLinkedNode(this, reference, node);
+        var element = node.declaredElement;
+        element ??= ConstructorElementImpl.forLinkedNode(this, reference, node);
+        return element;
       }).toList();
 
       if (_constructors.isEmpty) {
@@ -783,10 +788,9 @@
           .map((node) {
         var name = node.name.name;
         var reference = containerRef.getChild(name);
-        if (reference.hasElementFor(node)) {
-          return reference.element as MethodElement;
-        }
-        return MethodElementImpl.forLinkedNode(this, reference, node);
+        var element = node.declaredElement as MethodElement;
+        element ??= MethodElementImpl.forLinkedNode(this, reference, node);
+        return element;
       }).toList();
     }
 
@@ -1240,9 +1244,10 @@
         super(null, -1);
 
   CompilationUnitElementImpl.forLinkedNode(LibraryElementImpl enclosingLibrary,
-      this.linkedContext, Reference reference, CompilationUnit linkedNode)
+      this.linkedContext, Reference reference, CompilationUnitImpl linkedNode)
       : super.forLinkedNode(enclosingLibrary, reference, linkedNode) {
     _nameOffset = -1;
+    linkedNode.declaredElement = this;
   }
 
   @override
@@ -1302,10 +1307,9 @@
       _enums = linkedNode.declarations.whereType<EnumDeclaration>().map((node) {
         var name = node.name.name;
         var reference = containerRef.getChild(name);
-        if (reference.hasElementFor(node)) {
-          return reference.element as EnumElementImpl;
-        }
-        return EnumElementImpl.forLinkedNode(this, reference, node);
+        var element = node.declaredElement;
+        element ??= EnumElementImpl.forLinkedNode(this, reference, node);
+        return element;
       }).toList();
     }
 
@@ -1334,13 +1338,9 @@
         if (node is ExtensionDeclaration) {
           var refName = linkedContext.getExtensionRefName(node);
           var reference = containerRef.getChild(refName);
-          if (reference.hasElementFor(node)) {
-            _extensions.add(reference.element);
-          } else {
-            _extensions.add(
-              ExtensionElementImpl.forLinkedNode(this, reference, node),
-            );
-          }
+          var element = node.declaredElement;
+          element ??= ExtensionElementImpl.forLinkedNode(this, reference, node);
+          _extensions.add(element);
         }
       }
       return _extensions;
@@ -1370,10 +1370,9 @@
           .map((node) {
         var name = node.name.name;
         var reference = containerRef.getChild(name);
-        if (reference.hasElementFor(node)) {
-          return reference.element as FunctionElementImpl;
-        }
-        return FunctionElementImpl.forLinkedNode(this, reference, node);
+        var element = node.declaredElement as FunctionElement;
+        element ??= FunctionElementImpl.forLinkedNode(this, reference, node);
+        return element;
       }).toList();
     }
     return _functions ?? const <FunctionElement>[];
@@ -1395,22 +1394,23 @@
     if (linkedNode != null) {
       CompilationUnit linkedNode = this.linkedNode;
       var containerRef = reference.getChild('@typeAlias');
-      return _typeAliases = linkedNode.declarations.where((node) {
-        return node is FunctionTypeAlias || node is GenericTypeAlias;
-      }).map((node) {
+      _typeAliases = <FunctionTypeAliasElement>[];
+      for (var node in linkedNode.declarations) {
         String name;
         if (node is FunctionTypeAlias) {
           name = node.name.name;
+        } else if (node is GenericTypeAlias) {
+          name = node.name.name;
         } else {
-          name = (node as GenericTypeAlias).name.name;
+          continue;
         }
 
         var reference = containerRef.getChild(name);
-        if (reference.hasElementFor(node)) {
-          return reference.element as GenericTypeAliasElementImpl;
-        }
-        return GenericTypeAliasElementImpl.forLinkedNode(this, reference, node);
-      }).toList();
+        var element = node.declaredElement as GenericTypeAliasElement;
+        element ??=
+            GenericTypeAliasElementImpl.forLinkedNode(this, reference, node);
+        _typeAliases.add(element);
+      }
     }
 
     return _typeAliases ?? const <FunctionTypeAliasElement>[];
@@ -1447,10 +1447,9 @@
       return _mixins = declarations.whereType<MixinDeclaration>().map((node) {
         var name = node.name.name;
         var reference = containerRef.getChild(name);
-        if (reference.hasElementFor(node)) {
-          return reference.element as MixinElementImpl;
-        }
-        return MixinElementImpl.forLinkedNode(this, reference, node);
+        var element = node.declaredElement as MixinElementImpl;
+        element ??= MixinElementImpl.forLinkedNode(this, reference, node);
+        return element;
       }).toList();
     }
 
@@ -1506,21 +1505,18 @@
       var containerRef = reference.getChild('@class');
       _types = <ClassElement>[];
       for (var node in linkedNode.declarations) {
-        String name;
         if (node is ClassDeclaration) {
-          name = node.name.name;
+          var name = node.name.name;
+          var reference = containerRef.getChild(name);
+          var element = node.declaredElement;
+          element ??= ClassElementImpl.forLinkedNode(this, reference, node);
+          _types.add(element);
         } else if (node is ClassTypeAlias) {
-          name = node.name.name;
-        } else {
-          continue;
-        }
-        var reference = containerRef.getChild(name);
-        if (reference.hasElementFor(node)) {
-          _types.add(reference.element);
-        } else {
-          _types.add(
-            ClassElementImpl.forLinkedNode(this, reference, node),
-          );
+          var name = node.name.name;
+          var reference = containerRef.getChild(name);
+          var element = node.declaredElement;
+          element ??= ClassElementImpl.forLinkedNode(this, reference, node);
+          _types.add(element);
         }
       }
       return _types;
@@ -1948,8 +1944,10 @@
   ConstructorElementImpl(String name, int offset) : super(name, offset);
 
   ConstructorElementImpl.forLinkedNode(ClassElementImpl enclosingClass,
-      Reference reference, ConstructorDeclaration linkedNode)
-      : super.forLinkedNode(enclosingClass, reference, linkedNode);
+      Reference reference, ConstructorDeclarationImpl linkedNode)
+      : super.forLinkedNode(enclosingClass, reference, linkedNode) {
+    linkedNode?.declaredElement = this;
+  }
 
   /// Return the constant initializers for this element, which will be empty if
   /// there are no initializers, or `null` if there was an error in the source.
@@ -3330,7 +3328,9 @@
 
   EnumElementImpl.forLinkedNode(CompilationUnitElementImpl enclosing,
       Reference reference, EnumDeclaration linkedNode)
-      : super.forLinkedNode(enclosing, reference, linkedNode);
+      : super.forLinkedNode(enclosing, reference, linkedNode) {
+    linkedNode.name.staticElement = this;
+  }
 
   @override
   List<PropertyAccessorElement> get accessors {
@@ -3548,7 +3548,13 @@
   /// Initialize using the given linked node.
   ExecutableElementImpl.forLinkedNode(
       ElementImpl enclosing, Reference reference, AstNode linkedNode)
-      : super.forLinkedNode(enclosing, reference, linkedNode);
+      : super.forLinkedNode(enclosing, reference, linkedNode) {
+    if (linkedNode is MethodDeclaration) {
+      linkedNode.name.staticElement = this;
+    } else if (linkedNode is FunctionDeclaration) {
+      linkedNode.name.staticElement = this;
+    }
+  }
 
   @override
   int get codeLength {
@@ -3901,8 +3907,10 @@
 
   /// Initialize using the given linked information.
   ExtensionElementImpl.forLinkedNode(CompilationUnitElementImpl enclosing,
-      Reference reference, AstNode linkedNode)
-      : super.forLinkedNode(enclosing, reference, linkedNode);
+      Reference reference, ExtensionDeclarationImpl linkedNode)
+      : super.forLinkedNode(enclosing, reference, linkedNode) {
+    linkedNode.declaredElement = this;
+  }
 
   @override
   List<PropertyAccessorElement> get accessors {
@@ -4032,10 +4040,9 @@
           .map((node) {
         var name = node.name.name;
         var reference = containerRef.getChild(name);
-        if (reference.hasElementFor(node)) {
-          return reference.element as MethodElement;
-        }
-        return MethodElementImpl.forLinkedNode(this, reference, node);
+        var element = node.declaredElement as MethodElement;
+        element ??= MethodElementImpl.forLinkedNode(this, reference, node);
+        return element;
       }).toList();
     }
     return _methods = const <MethodElement>[];
@@ -4360,7 +4367,9 @@
 
   FunctionElementImpl.forLinkedNode(ElementImpl enclosing, Reference reference,
       FunctionDeclaration linkedNode)
-      : super.forLinkedNode(enclosing, reference, linkedNode);
+      : super.forLinkedNode(enclosing, reference, linkedNode) {
+    linkedNode.name.staticElement = this;
+  }
 
   /// Initialize a newly created function element to have no name and the given
   /// [nameOffset]. This is used for function expressions, that have no name.
@@ -4605,7 +4614,13 @@
       CompilationUnitElementImpl enclosingUnit,
       Reference reference,
       AstNode linkedNode)
-      : super.forLinkedNode(enclosingUnit, reference, linkedNode);
+      : super.forLinkedNode(enclosingUnit, reference, linkedNode) {
+    if (linkedNode is FunctionTypeAlias) {
+      linkedNode.name.staticElement = this;
+    } else {
+      (linkedNode as GenericTypeAlias).name.staticElement = this;
+    }
+  }
 
   @override
   int get codeLength {
@@ -5682,7 +5697,9 @@
 
   MethodElementImpl.forLinkedNode(TypeParameterizedElementMixin enclosingClass,
       Reference reference, MethodDeclaration linkedNode)
-      : super.forLinkedNode(enclosingClass, reference, linkedNode);
+      : super.forLinkedNode(enclosingClass, reference, linkedNode) {
+    linkedNode.name.staticElement = this;
+  }
 
   @override
   MethodElement get declaration => this;
@@ -5793,7 +5810,9 @@
 
   MixinElementImpl.forLinkedNode(CompilationUnitElementImpl enclosing,
       Reference reference, MixinDeclaration linkedNode)
-      : super.forLinkedNode(enclosing, reference, linkedNode);
+      : super.forLinkedNode(enclosing, reference, linkedNode) {
+    linkedNode.name.staticElement = this;
+  }
 
   @override
   bool get isAbstract => true;
@@ -6327,7 +6346,9 @@
 
   ParameterElementImpl.forLinkedNode(
       ElementImpl enclosing, Reference reference, FormalParameter linkedNode)
-      : super.forLinkedNode(enclosing, reference, linkedNode);
+      : super.forLinkedNode(enclosing, reference, linkedNode) {
+    FormalParameterImpl.setDeclaredElement(linkedNode, this);
+  }
 
   factory ParameterElementImpl.forLinkedNodeFactory(
       ElementImpl enclosing, Reference reference, FormalParameter node) {
@@ -6550,10 +6571,10 @@
       return _typeParameters =
           typeParameters.typeParameters.map<TypeParameterElement>((node) {
         var reference = containerRef.getChild(node.name.name);
-        if (reference.hasElementFor(node)) {
-          return reference.element as TypeParameterElement;
-        }
-        return TypeParameterElementImpl.forLinkedNode(this, reference, node);
+        var element = node.declaredElement;
+        element ??=
+            TypeParameterElementImpl.forLinkedNode(this, reference, node);
+        return element;
       }).toList();
     }
 
@@ -6593,6 +6614,11 @@
     }
 
     return formalParameters.map((node) {
+      var element = node.declaredElement;
+      if (element != null) {
+        return element;
+      }
+
       if (node is DefaultFormalParameter) {
         NormalFormalParameter parameterNode = node.parameter;
         var name = parameterNode.identifier?.name ?? '';
@@ -7330,7 +7356,9 @@
 
   TypeParameterElementImpl.forLinkedNode(
       ElementImpl enclosing, Reference reference, TypeParameter linkedNode)
-      : super.forLinkedNode(enclosing, reference, linkedNode);
+      : super.forLinkedNode(enclosing, reference, linkedNode) {
+    linkedNode.name.staticElement = this;
+  }
 
   /// Initialize a newly created synthetic type parameter element to have the
   /// given [name], and with [synthetic] set to true.
diff --git a/pkg/analyzer/lib/src/error/codes.dart b/pkg/analyzer/lib/src/error/codes.dart
index 4c17cc04..3197fa7 100644
--- a/pkg/analyzer/lib/src/error/codes.dart
+++ b/pkg/analyzer/lib/src/error/codes.dart
@@ -5460,6 +5460,22 @@
           "The element type '{0}' can't be assigned to the list type '{1}'.",
           hasPublishedDocs: true);
 
+  static const CompileTimeErrorCode
+      MAIN_HAS_TOO_MANY_REQUIRED_POSITIONAL_PARAMETERS = CompileTimeErrorCode(
+    'MAIN_HAS_TOO_MANY_REQUIRED_POSITIONAL_PARAMETERS',
+    "The function 'main' can't have more than two required positional parameters.",
+    correction: "Try using a different name for the function, "
+        "or removing extra parameters.",
+  );
+
+  static const CompileTimeErrorCode MAIN_HAS_REQUIRED_NAMED_PARAMETERS =
+      CompileTimeErrorCode(
+    'MAIN_HAS_REQUIRED_NAMED_PARAMETERS',
+    "The function 'main' can't have any required named parameters.",
+    correction: "Try using a different name for the function, "
+        "or removing the 'required' modifier.",
+  );
+
   static const CompileTimeErrorCode MAIN_IS_NOT_FUNCTION = CompileTimeErrorCode(
     'MAIN_IS_NOT_FUNCTION',
     "The declaration named 'main' must be a function.",
diff --git a/pkg/analyzer/lib/src/generated/error_verifier.dart b/pkg/analyzer/lib/src/generated/error_verifier.dart
index 81e2c6a..5ad8d00 100644
--- a/pkg/analyzer/lib/src/generated/error_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/error_verifier.dart
@@ -3127,6 +3127,24 @@
       );
       return;
     }
+
+    var parameters = (element as FunctionElement).parameters;
+    var requiredPositional =
+        parameters.where((e) => e.isRequiredPositional).toList();
+
+    if (requiredPositional.length > 2) {
+      _errorReporter.reportErrorForNode(
+        CompileTimeErrorCode.MAIN_HAS_TOO_MANY_REQUIRED_POSITIONAL_PARAMETERS,
+        nameNode,
+      );
+    }
+
+    if (parameters.any((e) => e.isRequiredNamed)) {
+      _errorReporter.reportErrorForNode(
+        CompileTimeErrorCode.MAIN_HAS_REQUIRED_NAMED_PARAMETERS,
+        nameNode,
+      );
+    }
   }
 
   void _checkForMapTypeNotAssignable(SetOrMapLiteral literal) {
diff --git a/pkg/analyzer/lib/src/summary2/reference_resolver.dart b/pkg/analyzer/lib/src/summary2/reference_resolver.dart
index 013ec70..2844249 100644
--- a/pkg/analyzer/lib/src/summary2/reference_resolver.dart
+++ b/pkg/analyzer/lib/src/summary2/reference_resolver.dart
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'package:analyzer/dart/ast/ast.dart';
-import 'package:analyzer/dart/ast/token.dart';
 import 'package:analyzer/dart/ast/visitor.dart';
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/dart/element/nullability_suffix.dart';
@@ -67,11 +66,11 @@
     var outerScope = scope;
     var outerReference = reference;
 
-    var name = node.name.name;
-    reference = reference.getChild('@class').getChild(name);
-
-    ClassElementImpl element = reference.element;
-    node.name.staticElement = element;
+    var element = node.declaredElement as ClassElementImpl;
+    reference = element.reference;
+    element.accessors; // create elements
+    element.constructors; // create elements
+    element.methods; // create elements
 
     _createTypeParameterElements(node.typeParameters);
     scope = TypeParameterScope(scope, element.typeParameters);
@@ -104,11 +103,8 @@
     var outerScope = scope;
     var outerReference = reference;
 
-    var name = node.name.name;
-    reference = reference.getChild('@class').getChild(name);
-
-    ClassElementImpl element = reference.element;
-    node.name.staticElement = element;
+    var element = node.declaredElement as ClassElementImpl;
+    reference = element.reference;
 
     _createTypeParameterElements(node.typeParameters);
     scope = TypeParameterScope(scope, element.typeParameters);
@@ -135,15 +131,9 @@
     var outerScope = scope;
     var outerReference = reference;
 
-    var name = node.name?.name ?? '';
-    reference = reference.getChild('@constructor').getChild(name);
-
-    var element = ConstructorElementImpl.forLinkedNode(
-      outerReference.element,
-      reference,
-      node,
-    );
-    (node as ConstructorDeclarationImpl).declaredElement = element;
+    var element = node.declaredElement as ConstructorElementImpl;
+    reference = element.reference;
+    element.parameters; // create elements
 
     scope = TypeParameterScope(scope, element.typeParameters);
     LinkingNodeContext(node, scope);
@@ -181,11 +171,8 @@
     var outerScope = scope;
     var outerReference = reference;
 
-    var refName = LazyExtensionDeclaration.get(node).refName;
-    reference = reference.getChild('@extension').getChild(refName);
-
-    ExtensionElementImpl element = reference.element;
-    node.name?.staticElement = element;
+    var element = node.declaredElement as ExtensionElementImpl;
+    reference = element.reference;
 
     _createTypeParameterElements(node.typeParameters);
     scope = TypeParameterScope(scope, element.typeParameters);
@@ -219,16 +206,9 @@
     var outerScope = scope;
     var outerReference = reference;
 
-    var name = node.identifier.name;
-    reference = reference.getChild('@parameter').getChild(name);
-    reference.node = node;
-
-    var element = FieldFormalParameterElementImpl.forLinkedNode(
-      outerReference.element,
-      reference,
-      node,
-    );
-    node.identifier.staticElement = element;
+    var element = node.declaredElement as FieldFormalParameterElementImpl;
+    reference = element.reference;
+    element.parameters; // create elements
 
     _createTypeParameterElements(node.typeParameters);
     scope = TypeParameterScope(scope, element.typeParameters);
@@ -252,19 +232,9 @@
     var outerScope = scope;
     var outerReference = reference;
 
-    var container = '@function';
-    var propertyKeyword = node.propertyKeyword?.keyword;
-    if (propertyKeyword == Keyword.GET) {
-      container = '@getter';
-    } else if (propertyKeyword == Keyword.SET) {
-      container = '@setter';
-    }
-
-    var name = node.name.name;
-    reference = reference.getChild(container).getChild(name);
-
-    ExecutableElementImpl element = reference.element;
-    node.name.staticElement = element;
+    var element = node.declaredElement as ExecutableElementImpl;
+    reference = element.reference;
+    element.parameters; // create elements
 
     _createTypeParameterElements(node.functionExpression.typeParameters);
     scope = TypeParameterScope(outerScope, element.typeParameters);
@@ -289,11 +259,8 @@
     var outerScope = scope;
     var outerReference = reference;
 
-    var name = node.name.name;
-    reference = reference.getChild('@typeAlias').getChild(name);
-
-    GenericTypeAliasElementImpl element = reference.element;
-    node.name.staticElement = element;
+    var element = node.declaredElement as GenericTypeAliasElementImpl;
+    reference = element.reference;
 
     _createTypeParameterElements(node.typeParameters);
     scope = TypeParameterScope(outerScope, element.typeParameters);
@@ -301,8 +268,9 @@
     node.returnType?.accept(this);
     node.typeParameters?.accept(this);
 
-    reference = reference.getChild('@function');
-    reference.element = element;
+    var functionElement = element.function;
+    reference = functionElement.reference;
+    functionElement.parameters; // create elements
     node.parameters.accept(this);
 
     nodesToBuildType.addDeclaration(node);
@@ -316,16 +284,9 @@
     var outerScope = scope;
     var outerReference = reference;
 
-    var name = node.identifier.name;
-    reference = reference.getChild('@parameter').getChild(name);
-    reference.node = node;
-
-    var element = ParameterElementImpl.forLinkedNode(
-      outerReference.element,
-      reference,
-      node,
-    );
-    node.identifier.staticElement = element;
+    var element = node.declaredElement as ParameterElementImpl;
+    reference = element.reference;
+    element.parameters; // create elements
 
     _createTypeParameterElements(node.typeParameters);
     scope = TypeParameterScope(scope, element.typeParameters);
@@ -356,6 +317,7 @@
       node,
     );
     (node as GenericFunctionTypeImpl).declaredElement = element;
+    element.parameters; // create elements
 
     _createTypeParameterElements(node.typeParameters);
     scope = TypeParameterScope(outerScope, element.typeParameters);
@@ -378,11 +340,8 @@
     var outerScope = scope;
     var outerReference = reference;
 
-    var name = node.name.name;
-    reference = reference.getChild('@typeAlias').getChild(name);
-
-    GenericTypeAliasElementImpl element = reference.element;
-    node.name.staticElement = element;
+    var element = node.declaredElement as GenericTypeAliasElementImpl;
+    reference = element.reference;
 
     _createTypeParameterElements(node.typeParameters);
     scope = TypeParameterScope(outerScope, element.typeParameters);
@@ -405,23 +364,10 @@
     var outerScope = scope;
     var outerReference = reference;
 
-    var container = '@method';
-    var propertyKeyword = node.propertyKeyword?.keyword;
-    if (propertyKeyword == Keyword.GET) {
-      container = '@getter';
-    } else if (propertyKeyword == Keyword.SET) {
-      container = '@setter';
-    }
+    var element = node.declaredElement as ExecutableElementImpl;
+    reference = element.reference;
+    element.parameters; // create elements
 
-    var name = node.name.name;
-    reference = reference.getChild(container).getChild(name);
-
-    var element = MethodElementImpl.forLinkedNode(
-      outerReference.element,
-      reference,
-      node,
-    );
-    node.name.staticElement = element;
     _createTypeParameterElements(node.typeParameters);
 
     scope = TypeParameterScope(scope, element.typeParameters);
@@ -441,11 +387,11 @@
     var outerScope = scope;
     var outerReference = reference;
 
-    var name = node.name.name;
-    reference = reference.getChild('@mixin').getChild(name);
-
-    MixinElementImpl element = reference.element;
-    node.name.staticElement = element;
+    var element = node.declaredElement as MixinElementImpl;
+    reference = element.reference;
+    element.accessors; // create elements
+    element.constructors; // create elements
+    element.methods; // create elements
 
     _createTypeParameterElements(node.typeParameters);
     scope = TypeParameterScope(scope, element.typeParameters);
diff --git a/pkg/analyzer/test/src/dart/resolution/non_nullable_test.dart b/pkg/analyzer/test/src/dart/resolution/non_nullable_test.dart
index 08048f9..bbedc1c 100644
--- a/pkg/analyzer/test/src/dart/resolution/non_nullable_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/non_nullable_test.dart
@@ -322,7 +322,7 @@
 
   test_parameter_interfaceType_generic() async {
     await assertNoErrorsInCode('''
-main(List<int?>? a, List<int>? b, List<int?> c, List<int> d) {
+void f(List<int?>? a, List<int>? b, List<int?> c, List<int> d) {
 }
 ''');
 
diff --git a/pkg/analyzer/test/src/diagnostics/main_has_required_named_parameters_test.dart b/pkg/analyzer/test/src/diagnostics/main_has_required_named_parameters_test.dart
new file mode 100644
index 0000000..dab48b2
--- /dev/null
+++ b/pkg/analyzer/test/src/diagnostics/main_has_required_named_parameters_test.dart
@@ -0,0 +1,41 @@
+// Copyright (c) 2020, 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.
+
+import 'package:analyzer/src/error/codes.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../dart/resolution/context_collection_resolution.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(MainHasRequiredNamedParametersTest);
+    defineReflectiveTests(MainHasRequiredNamedParametersWithNullSafetyTest);
+  });
+}
+
+@reflectiveTest
+class MainHasRequiredNamedParametersTest extends PubPackageResolutionTest
+    with MainHasRequiredNamedParametersTestCases {}
+
+mixin MainHasRequiredNamedParametersTestCases on PubPackageResolutionTest {
+  test_namedOptional() async {
+    await resolveTestCode('''
+void main({int a = 0}) {}
+''');
+    assertNoErrorsInResult();
+  }
+}
+
+@reflectiveTest
+class MainHasRequiredNamedParametersWithNullSafetyTest
+    extends PubPackageResolutionTest
+    with WithNullSafetyMixin, MainHasRequiredNamedParametersTestCases {
+  test_namedRequired() async {
+    await assertErrorsInCode('''
+void main({required List<String> a}) {}
+''', [
+      error(CompileTimeErrorCode.MAIN_HAS_REQUIRED_NAMED_PARAMETERS, 5, 4),
+    ]);
+  }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/main_has_too_many_required_positional_parameters_test.dart b/pkg/analyzer/test/src/diagnostics/main_has_too_many_required_positional_parameters_test.dart
new file mode 100644
index 0000000..f965d4c
--- /dev/null
+++ b/pkg/analyzer/test/src/diagnostics/main_has_too_many_required_positional_parameters_test.dart
@@ -0,0 +1,111 @@
+// Copyright (c) 2020, 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.
+
+import 'package:analyzer/src/error/codes.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../dart/resolution/context_collection_resolution.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(MainHasTooManyRequiredPositionalParametersTest);
+    defineReflectiveTests(
+      MainHasTooManyRequiredPositionalParametersWithNullSafetyTest,
+    );
+  });
+}
+
+@reflectiveTest
+class MainHasTooManyRequiredPositionalParametersTest
+    extends PubPackageResolutionTest
+    with MainHasTooManyRequiredPositionalParametersTestCases {}
+
+mixin MainHasTooManyRequiredPositionalParametersTestCases
+    on PubPackageResolutionTest {
+  test_namedOptional_1() async {
+    await resolveTestCode('''
+void main({int a = 0}) {}
+''');
+    assertNoErrorsInResult();
+  }
+
+  test_positionalOptional_1() async {
+    await resolveTestCode('''
+void main([int a = 0]) {}
+''');
+    assertNoErrorsInResult();
+  }
+
+  test_positionalRequired_0() async {
+    await resolveTestCode('''
+void main() {}
+''');
+    assertNoErrorsInResult();
+  }
+
+  test_positionalRequired_1() async {
+    await resolveTestCode('''
+void main(args) {}
+''');
+    assertNoErrorsInResult();
+  }
+
+  test_positionalRequired_2() async {
+    await resolveTestCode('''
+void main(args, int a) {}
+''');
+    assertNoErrorsInResult();
+  }
+
+  test_positionalRequired_2_positionalOptional_1() async {
+    await resolveTestCode('''
+void main(args, int a, [int b = 0]) {}
+''');
+    assertNoErrorsInResult();
+  }
+
+  test_positionalRequired_3() async {
+    await resolveTestCode('''
+void main(args, int a, int b) {}
+''');
+    assertErrorsInResult(expectedErrorsByNullability(nullable: [
+      error(
+          CompileTimeErrorCode.MAIN_HAS_TOO_MANY_REQUIRED_POSITIONAL_PARAMETERS,
+          5,
+          4),
+    ], legacy: []));
+  }
+
+  test_positionalRequired_3_namedOptional_1() async {
+    await resolveTestCode('''
+void main(args, int a, int b, {int c = 0}) {}
+''');
+    assertErrorsInResult(expectedErrorsByNullability(nullable: [
+      error(
+          CompileTimeErrorCode.MAIN_HAS_TOO_MANY_REQUIRED_POSITIONAL_PARAMETERS,
+          5,
+          4),
+    ], legacy: []));
+  }
+}
+
+@reflectiveTest
+class MainHasTooManyRequiredPositionalParametersWithNullSafetyTest
+    extends PubPackageResolutionTest
+    with
+        WithNullSafetyMixin,
+        MainHasTooManyRequiredPositionalParametersTestCases {
+  test_positionalRequired_3_namedRequired_1() async {
+    await resolveTestCode('''
+void main(args, int a, int b, {required int c}) {}
+''');
+    assertErrorsInResult(expectedErrorsByNullability(nullable: [
+      error(CompileTimeErrorCode.MAIN_HAS_REQUIRED_NAMED_PARAMETERS, 5, 4),
+      error(
+          CompileTimeErrorCode.MAIN_HAS_TOO_MANY_REQUIRED_POSITIONAL_PARAMETERS,
+          5,
+          4),
+    ], legacy: []));
+  }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/test_all.dart b/pkg/analyzer/test/src/diagnostics/test_all.dart
index a6b7ec6..086f256 100644
--- a/pkg/analyzer/test/src/diagnostics/test_all.dart
+++ b/pkg/analyzer/test/src/diagnostics/test_all.dart
@@ -344,6 +344,10 @@
     as late_final_local_already_assigned;
 import 'list_element_type_not_assignable_test.dart'
     as list_element_type_not_assignable;
+import 'main_has_required_named_parameters_test.dart'
+    as main_has_required_named_parameters;
+import 'main_has_too_many_required_positional_parameters_test.dart'
+    as main_has_too_many_required_positional_parameters;
 import 'main_is_not_function_test.dart' as main_is_not_function;
 import 'map_entry_not_in_map_test.dart' as map_entry_not_in_map;
 import 'map_key_type_not_assignable_test.dart' as map_key_type_not_assignable;
@@ -873,6 +877,8 @@
     late_final_field_with_const_constructor.main();
     late_final_local_already_assigned.main();
     list_element_type_not_assignable.main();
+    main_has_required_named_parameters.main();
+    main_has_too_many_required_positional_parameters.main();
     main_is_not_function.main();
     map_entry_not_in_map.main();
     map_key_type_not_assignable.main();
diff --git a/pkg/dartdev/lib/src/commands/analyze.dart b/pkg/dartdev/lib/src/commands/analyze.dart
index 951b65a..6ea3dbe 100644
--- a/pkg/dartdev/lib/src/commands/analyze.dart
+++ b/pkg/dartdev/lib/src/commands/analyze.dart
@@ -3,7 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'dart:async';
-import 'dart:io';
+import 'dart:io' as io;
 
 import 'package:meta/meta.dart';
 import 'package:path/path.dart' as path;
@@ -43,29 +43,21 @@
 
     // find directory from argResults.rest
     var dir = argResults.rest.isEmpty
-        ? Directory.current
-        : Directory(argResults.rest.single);
+        ? io.Directory.current
+        : io.Directory(argResults.rest.single);
     if (!dir.existsSync()) {
       usageException("Directory doesn't exist: ${dir.path}");
     }
 
-    final Completer<void> analysisCompleter = Completer<void>();
     final List<AnalysisError> errors = <AnalysisError>[];
 
     var progress = log.progress('Analyzing ${path.basename(dir.path)}');
 
     final AnalysisServer server = AnalysisServer(
-      Directory(sdk.sdkPath),
+      io.Directory(sdk.sdkPath),
       [dir],
     );
 
-    StreamSubscription<bool> subscription;
-    subscription = server.onAnalyzing.listen((bool isAnalyzing) {
-      if (!isAnalyzing) {
-        analysisCompleter.complete();
-        subscription.cancel();
-      }
-    });
     server.onErrors.listen((FileAnalysisErrors fileErrors) {
       // Record the issues found (but filter out to do comments).
       errors.addAll(fileErrors.errors
@@ -73,15 +65,19 @@
     });
 
     await server.start();
-    // Completing the future in the callback can't fail.
-    //ignore: unawaited_futures
+
+    bool analysisFinished = false;
+
+    // ignore: unawaited_futures
     server.onExit.then((int exitCode) {
-      if (!analysisCompleter.isCompleted) {
-        analysisCompleter.completeError('analysis server exited: $exitCode');
+      if (!analysisFinished) {
+        io.exitCode = exitCode;
       }
     });
 
-    await analysisCompleter.future;
+    await server.analysisFinished;
+    analysisFinished = true;
+
     // todo (pq): consider server.shutdown() for cleaner dispose.
     await server.dispose();
     progress.finish(showTiming: true);
diff --git a/pkg/dartdev/lib/src/commands/analyze_impl.dart b/pkg/dartdev/lib/src/commands/analyze_impl.dart
index 1160d21..3f315b8 100644
--- a/pkg/dartdev/lib/src/commands/analyze_impl.dart
+++ b/pkg/dartdev/lib/src/commands/analyze_impl.dart
@@ -24,6 +24,7 @@
   Process _process;
   final StreamController<bool> _analyzingController =
       StreamController<bool>.broadcast();
+  Completer<bool> _analysisFinished = Completer();
   final StreamController<EditBulkFixesResult> _bulkFixesController =
       StreamController<EditBulkFixesResult>.broadcast();
   final StreamController<FileAnalysisErrors> _errorsController =
@@ -41,6 +42,11 @@
 
   Stream<bool> get onAnalyzing => _analyzingController.stream;
 
+  /// This future completes when we next receive an analysis finished event
+  /// (unless there's not current analysis and we've already received a complete
+  /// event, in which case this future immediately completes).
+  Future<bool> get analysisFinished => _analysisFinished.future;
+
   Stream<FileAnalysisErrors> get onErrors => _errorsController.stream;
 
   Stream<EditBulkFixesResult> get onBulkFixes => _bulkFixesController.stream;
@@ -58,7 +64,7 @@
 
     _process = await startDartProcess(sdk, command);
     // This callback hookup can't throw.
-    //ignore: unawaited_futures
+    // ignore: unawaited_futures
     _process.exitCode.whenComplete(() => _process = null);
 
     final Stream<String> errorStream = _process.stderr
@@ -86,6 +92,16 @@
       path.context.separator,
     );
 
+    onAnalyzing.listen((bool isAnalyzing) {
+      if (isAnalyzing && _analysisFinished.isCompleted) {
+        // Start a new completer, to be completed when we receive the
+        // corresponding analysis complete event.
+        _analysisFinished = Completer();
+      } else if (!isAnalyzing && !_analysisFinished.isCompleted) {
+        _analysisFinished.complete(true);
+      }
+    });
+
     _sendCommand('analysis.setAnalysisRoots', <String, dynamic>{
       'included': [dirPath],
       'excluded': <String>[]
diff --git a/pkg/test_runner/lib/src/compiler_configuration.dart b/pkg/test_runner/lib/src/compiler_configuration.dart
index 38f06ea..4698cc2 100644
--- a/pkg/test_runner/lib/src/compiler_configuration.dart
+++ b/pkg/test_runner/lib/src/compiler_configuration.dart
@@ -992,7 +992,7 @@
       "ffi_2",
       "language_2",
       "lib_2",
-      "service",
+      "service_2",
       "standalone_2"
     };
 
diff --git a/pkg/test_runner/lib/src/options.dart b/pkg/test_runner/lib/src/options.dart
index 7e6f18d..e0c78fc 100644
--- a/pkg/test_runner/lib/src/options.dart
+++ b/pkg/test_runner/lib/src/options.dart
@@ -24,9 +24,9 @@
   'utils',
   'lib_2',
   'analyze_library',
-  'service',
+  'service_2',
   'kernel',
-  'observatory_ui',
+  'observatory_ui_2',
   'ffi_2'
 ];
 
diff --git a/pkg/test_runner/lib/src/test_configurations.dart b/pkg/test_runner/lib/src/test_configurations.dart
index dbd8d79..36024d9 100644
--- a/pkg/test_runner/lib/src/test_configurations.dart
+++ b/pkg/test_runner/lib/src/test_configurations.dart
@@ -31,6 +31,8 @@
   Path('runtime/tests/vm'),
   Path('runtime/observatory/tests/service'),
   Path('runtime/observatory/tests/observatory_ui'),
+  Path('runtime/observatory_2/tests/service_2'),
+  Path('runtime/observatory_2/tests/observatory_ui_2'),
   Path('samples'),
   Path('samples-dev'),
   Path('tests/corelib'),
diff --git a/pkg/test_runner/lib/src/test_suite.dart b/pkg/test_runner/lib/src/test_suite.dart
index 25709e2..400f88b 100644
--- a/pkg/test_runner/lib/src/test_suite.dart
+++ b/pkg/test_runner/lib/src/test_suite.dart
@@ -583,7 +583,7 @@
       _enqueueStandardTest(testFile, expectationSet, onTest);
     } else if (configuration.runtime.isBrowser) {
       _enqueueBrowserTest(testFile, expectationSet, onTest);
-    } else if (suiteName == 'service') {
+    } else if (suiteName == 'service' || suiteName == 'service_2') {
       _enqueueServiceTest(testFile, expectationSet, onTest);
     } else {
       _enqueueStandardTest(testFile, expectationSet, onTest);
diff --git a/runtime/observatory_2/.gitignore b/runtime/observatory_2/.gitignore
new file mode 100644
index 0000000..c73eaff
--- /dev/null
+++ b/runtime/observatory_2/.gitignore
@@ -0,0 +1,5 @@
+bootstrap_css
+out
+build
+.pub
+.idea
diff --git a/runtime/observatory_2/BUILD.gn b/runtime/observatory_2/BUILD.gn
new file mode 100644
index 0000000..1762e1a
--- /dev/null
+++ b/runtime/observatory_2/BUILD.gn
@@ -0,0 +1,272 @@
+# Copyright 2015 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("../../build/dart/copy_tree.gni")
+import("../../build/dart/dart_action.gni")
+import("observatory_sources.gni")
+
+prebuilt_dart2js_action("build_observatory") {
+  visibility = [ ":copy_main_dart_js" ]
+
+  script = "web/main.dart"
+
+  # dart2js produces a .deps file, but it is not in a format that is understood
+  # by ninja, so we explicitly list all the sources here.
+  inputs = [ "../../.packages" ] + observatory_sources
+
+  output = "$target_gen_dir/observatory/web/main.dart.js"
+  outputs = [ output ]
+  if (is_debug) {
+    outputs += [ "$target_gen_dir/observatory/web/main.dart.js.map" ]
+  }
+
+  version_string = exec_script("../../tools/make_version.py",
+                               [
+                                 "--quiet",
+                                 "--no_git_hash",
+                               ],  # Arguments to the script
+                               "trim string",  # Input conversions
+                               [
+                                 "../../tools/VERSION",
+                                 "../tools/utils.py",
+                               ])  # Dependencies
+
+  args = [
+    "-DOBS_VER=${version_string}",
+    "-o",
+    rebase_path(output),
+    "--packages=" + rebase_path("../../.packages"),
+  ]
+  if (is_debug) {
+    args += [ "--enable-asserts" ]
+  } else {
+    args += [ "--minify" ]
+  }
+}
+
+# The rules here down to "deploy_observatory" copy files into place such that
+# they can be packaged into a tar file. These rules do the following copies:
+#
+# web/* ->
+#     $target_out_dir/observatory/deployed/web
+# $target_gen_dir/observatory/web/main.dart.js ->
+#     $target_out_dir/observatory/deployed/web/main.dart.js
+# ../../third_party/observatory_pub_packages/packages/$PACKAGE/lib/* ->
+#     $target_out_dir/observatory/deployed/web/packages/$PACKAGE
+# lib/* ->
+#     $target_out_dir/observatory/deployed/web/packages/observatory
+#
+# Files matching "observatory_ignore_patterns" are excluded.
+
+# Files matching these patterns are filtered out of the Observatory assets.
+observatory_ignore_patterns = [
+  # "\$sdk", this is the first element concatenated into the string below.
+  "*.concat.js",
+  "*.dart",
+  "*.log",
+  "*.precompiled.js",
+  "*.scriptUrls",
+  "*_buildLogs*",
+  "*~",
+  "CustomElements.*",
+  "HTMLImports.*",
+  "MutationObserver.*",
+  "ShadowDOM.*",
+  "bower.json",
+  "dart_support.*",
+  "interop_support.*",
+  "package.json",
+  "unittest*",
+]
+
+if (!is_debug) {
+  observatory_ignore_patterns += [ "*.map" ]
+}
+
+# The ignore_patterns entry in the scopes accepted by copy_trees() is a
+# string of comma delimited patterns.
+observatory_ignore_string = "\$sdk"
+foreach(pattern, observatory_ignore_patterns) {
+  observatory_ignore_string = "$observatory_ignore_string,$pattern"
+}
+
+copy_tree_specs = []
+
+copy_tree_specs += [
+  {
+    target = "copy_web_package"
+    visibility = [ ":deploy_observatory" ]
+    source = "web"
+    dest = "$target_out_dir/observatory/deployed/web"
+    ignore_patterns = observatory_ignore_string
+  },
+]
+
+copy_tree_specs += [
+  {
+    target = "copy_observatory_package"
+    visibility = [ ":deploy_observatory" ]
+    source = "lib"
+    dest = "$target_out_dir/observatory/deployed/web/packages/observatory"
+    ignore_patterns = observatory_ignore_string
+  },
+]
+
+# This is not a rule, rather, it generates rules with names of the form:
+#   "copy_$package_package" for the packages in observatory_pub_packages.
+copy_trees("copy_observatory_packages") {
+  sources = copy_tree_specs
+}
+
+copy("copy_main_dart_js") {
+  visibility = [ ":deploy_observatory" ]
+  deps = [ ":build_observatory" ]
+  sources = [ "$target_gen_dir/observatory/web/main.dart.js" ]
+  if (is_debug) {
+    sources += [ "$target_gen_dir/observatory/web/main.dart.js.map" ]
+  }
+  outputs = [ "$target_out_dir/observatory/deployed/web/{{source_file_part}}" ]
+}
+
+group("deploy_observatory") {
+  deps = [
+    ":copy_main_dart_js",
+    ":copy_observatory_package",
+    ":copy_web_package",
+  ]
+}
+
+template("observatory_archive") {
+  enable_compression = false
+  if (defined(invoker.compress) && invoker.compress) {
+    enable_compression = true
+  }
+  action(target_name) {
+    deps = [ ":deploy_observatory" ]
+
+    output_name = target_name
+
+    output = "$target_gen_dir/${output_name}.tar"
+    outputs = [ output ]
+
+    script = "../tools/create_archive.py"
+    args = [
+      "--tar_output",
+      rebase_path(output),
+      "--client_root",
+      rebase_path("$target_out_dir/observatory/deployed/web/"),
+    ]
+    if (enable_compression) {
+      args += [ "--compress" ]
+    }
+  }
+}
+
+observatory_archive("compressed_observatory_archive") {
+  compress = true
+}
+
+copy("copy_compressed_observatory_archive") {
+  archive_target = ":compressed_observatory_archive"
+  deps = [ archive_target ]
+  archive_dir = get_label_info(archive_target, "target_gen_dir")
+  archive_name = get_label_info(archive_target, "name")
+  archive_file = "${archive_dir}/${archive_name}.tar"
+  sources = [ archive_file ]
+  outputs = [ "$root_out_dir/${archive_name}.tar" ]
+}
+
+observatory_archive("observatory_archive") {
+  compress = false
+}
+
+copy("copy_observatory_archive") {
+  archive_target = ":observatory_archive"
+  deps = [ archive_target ]
+  archive_dir = get_label_info(archive_target, "target_gen_dir")
+  archive_name = get_label_info(archive_target, "name")
+  archive_file = "${archive_dir}/${archive_name}.tar"
+  sources = [ archive_file ]
+  outputs = [ "$root_out_dir/${archive_name}.tar" ]
+}
+
+# Generates a .cc file containing the bytes of the observatory archive in a C
+# array.
+#
+# Parameters:
+#  inner_namespace (required):
+#    The inner C++ namespace that the C array lives in.
+#
+#  outer_namespace (required):
+#    The outer C++ namespace that the C array lives in.
+#
+#  archive_file (required):
+#    The path to the observatory archive.
+#
+template("observatory_archive_source") {
+  assert(defined(invoker.inner_namespace),
+         "Need inner_namespace in $target_name")
+  assert(defined(invoker.outer_namespace),
+         "Need outer_namespace in $target_name")
+  assert(defined(invoker.archive_file), "Need archive_file in $target_name")
+
+  action(target_name) {
+    forward_variables_from(invoker, [ "deps" ])
+
+    inputs = [ invoker.archive_file ]
+
+    output = "$target_gen_dir/${target_name}.cc"
+    outputs = [ output ]
+
+    script = "../tools/create_archive.py"
+    args = [
+      "--tar_input",
+      rebase_path(invoker.archive_file),
+      "--output",
+      rebase_path(output),
+      "--outer_namespace",
+      invoker.outer_namespace,
+      "--inner_namespace",
+      invoker.inner_namespace,
+      "--name",
+      "observatory_assets_archive",
+    ]
+  }
+}
+
+observatory_archive_source("embedded_archive_observatory") {
+  outer_namespace = "dart"
+  inner_namespace = "observatory"
+
+  # TODO(zra): In a Fuchsia build, use a prebuilt Observatory archive.
+  archive_target = ":observatory_archive"
+  deps = [ archive_target ]
+  archive_dir = get_label_info(archive_target, "target_gen_dir")
+  archive_name = get_label_info(archive_target, "name")
+  archive_file = "${archive_dir}/${archive_name}.tar"
+}
+
+source_set("embedded_observatory_archive") {
+  deps = [ ":embedded_archive_observatory" ]
+
+  sources = [ rebase_path("$target_gen_dir/embedded_archive_observatory.cc") ]
+}
+
+observatory_archive_source("standalone_archive_observatory") {
+  outer_namespace = "dart"
+  inner_namespace = "bin"
+
+  # TODO(zra): In a Fuchsia build, use a prebuilt Observatory archive.
+  archive_target = ":compressed_observatory_archive"
+  deps = [ archive_target ]
+  archive_dir = get_label_info(archive_target, "target_gen_dir")
+  archive_name = get_label_info(archive_target, "name")
+  archive_file = "${archive_dir}/${archive_name}.tar"
+}
+
+source_set("standalone_observatory_archive") {
+  deps = [ ":standalone_archive_observatory" ]
+
+  sources = [ rebase_path("$target_gen_dir/standalone_archive_observatory.cc") ]
+}
diff --git a/runtime/observatory_2/HACKING.md b/runtime/observatory_2/HACKING.md
new file mode 100644
index 0000000..b645f9e
--- /dev/null
+++ b/runtime/observatory_2/HACKING.md
@@ -0,0 +1,174 @@
+# Hacking Observatory
+
+These instructions will guide you through the Observatory development and
+testing workflow.
+
+## SDK Setup & Build
+Getting ready to start.
+
+Before you start to hack on Observatory, follow the [instructions][build_sdk] to
+have a working environment in which you are able to build and test the Dart SDK.
+
+## Run existing tests
+Before hacking Observatory let's run the existing Observatory tests.
+We suggest to run all the test in __debug__ mode.
+
+First build the sdk in debug mode
+```
+$ ./tools/build.py --mode debug create_sdk
+```
+
+From the root of the sdk repository run:
+```
+$ ./tools/test.py -mdebug service
+```
+
+## Serve Observatory
+Observatory is built as part of building the sdk. Previously, it was recommended
+to run the Observatory using `pub serve` to avoid having to rebuild the sdk for
+each change to Observatory. However, `pub serve` was deprecated as part of the
+transition to Dart 2.0.
+
+[Issue #35678](https://github.com/dart-lang/sdk/issues/35678)
+tracks changes required to allow for `package:build_runner` to run Observatory
+without having to rebuild the sdk after each change.
+
+## Connect to a VM
+Start a Dart VM with the ``--observe`` flag (as explained in the
+[get started guide][observatory_get_started]) and connect your Observatory
+instance to that VM.
+
+Example script (file name ```clock.dart```):
+```dart
+import 'dart:async' show Timer, Duration;
+
+main() {
+  bool tick = true;
+  new Timer.periodic(const Duration(seconds: 1), (Timer t) {
+    print(tick ? 'tick' : 'tock');
+    tick = !tick;
+  });
+}
+```
+Start the script:
+```
+$ dart --disable-service-origin-check --observe clock.dart
+```
+
+## Code Reviews
+The development workflow of Dart (and Observatory) is based on code reviews.
+
+Follow the code review [instructions][code_review] to be able to successfully
+submit your code.
+
+The main reviewers for Observatory related CLs are:
+  - asiva
+  - bkonyi
+  - rmacnak
+
+## Write a new service test
+All the service tests are located in the ```tests/service``` folder.
+Test file names follow the convention ```<description>_test.dart```
+(e.g. ```a_brief_description_test.dart```).
+
+The test is generally structured in the following way.
+```dart
+import 'package:test/test.dart';
+
+main() {
+  // Some code that you need to test.
+  var a = 1 + 2;
+
+  // Some assertions to check the results.
+  expect(a, equal(3));
+}
+```
+See the official [test library][test_library] instructions;
+
+The ```test_helper.dart``` file expose some functions that allow to run a part
+of the code into another __VM__.
+
+To test synchronous operations:
+```dart
+import 'test_helper.dart';
+
+code() {
+  // Write the code you want to be execute into another VM.
+}
+
+var tests = [
+  // A series of tests that you want to run against the above code.
+  (Isolate isolate) async {
+    await isolate.reload();
+    // Use the isolate to communicate to the VM.
+  }
+];
+
+main(args) => runIsolateTestsSynchronous(args,
+                                        tests,
+                                        testeeConcurrent: code);
+```
+
+In order to test asynchronous operations:
+```dart
+import 'test_helper.dart';
+
+code() async {
+  // Write the asynchronous code you want to be execute into another VM.
+}
+
+var tests = [
+  // A series of tests that you want to run against the above code.
+  (Isolate isolate) async {
+    await isolate.reload();
+    // Use the isolate to communicate to the VM.
+  }
+];
+
+main(args) async => runIsolateTests(args,
+                                    tests,
+                                    testeeConcurrent: code);
+```
+
+Both ```runIsolateTests``` and ```runIsolateTestsSynchronous``` have the
+following named parameters:
+ - __testeeBefore__ (void()) a function that is going to be executed before
+the test
+ - __testeeConcurrent__ (void()) test that is going to be executed
+ - __pause_on_start__ (bool, default: false) pause the Isolate before the first
+instruction
+ - __pause_on_exit__ (bool, default: false) pause the Isolate after the last
+instruction
+ - __pause_on_unhandled_exceptions__ (bool, default: false) pause the Isolate at
+an unhandled exception
+ - __trace_service__ (bool, default: false) trace VM service requests
+ - __trace_compiler__ (bool, default: false) trace compiler operations
+ - __verbose_vm__ (bool, default: false) verbose logging
+
+
+Some common and reusable test are available from ```service_test_common.dart```:
+ - hasPausedFor
+   - hasStoppedAtBreakpoint
+   - hasStoppedWithUnhandledException
+   - hasStoppedAtExit
+   - hasPausedAtStartcode_review
+and utility functions:
+ - subscribeToStream
+ - cancelStreamSubscription
+ - asyncStepOver
+ - setBreakpointAtLine
+ - resumeIsolate
+ - resumeAndAwaitEvent
+ - resumeIsolateAndAwaitEvent
+ - stepOver
+ - getClassFromRootLib
+ - rootLibraryFieldValue
+
+## Run your tests
+See: __Run existing tests__
+
+[build_sdk]: https://github.com/dart-lang/sdk/wiki/Building "Building the Dart SDK"
+[open_observatory]: http://localhost:8080/ "Open Observatory"
+[observatory_get_started]: https://dart-lang.github.io/observatory/get-started.html "Observatory get started"
+[code_review]: https://github.com/dart-lang/sdk/wiki/Code-review-workflow-with-GitHub-and-reitveld "Code Review"
+[test_library]: https://pub.dartlang.org/packages/test "Test Library"
diff --git a/runtime/observatory_2/analysis_options.yaml b/runtime/observatory_2/analysis_options.yaml
new file mode 100644
index 0000000..6babf28
--- /dev/null
+++ b/runtime/observatory_2/analysis_options.yaml
@@ -0,0 +1,17 @@
+analyzer:
+  errors:
+    dead_code: ignore
+    unused_local_variable: ignore
+  exclude:
+    - tests/service/bad_reload/v2/main.dart
+    - tests/service/complex_reload/v2/main.dart
+    - tests/service/developer_extension_test.dart
+    - tests/service/evaluate_activation_in_method_class_test.dart
+    - tests/service/get_isolate_after_language_error_test.dart
+    - tests/service/get_user_level_retaining_path_rpc_test.dart
+    - tests/service/pause_on_unhandled_async_exceptions_test.dart
+
+linter:
+  rules:
+    - prefer_final_fields
+    - prefer_final_locals
diff --git a/runtime/observatory_2/bin/heap_snapshot.dart b/runtime/observatory_2/bin/heap_snapshot.dart
new file mode 100644
index 0000000..495c778
--- /dev/null
+++ b/runtime/observatory_2/bin/heap_snapshot.dart
@@ -0,0 +1,421 @@
+// Copyright (c) 2020, 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.
+
+// A way to test heap snapshot loading and analysis outside of Observatory, and
+// to handle snapshots that require more memory to analyze than is available in
+// a web browser.
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:io';
+import 'dart:typed_data';
+
+import 'package:observatory_2/object_graph.dart';
+
+Future<SnapshotGraph> load(String uri) async {
+  final ws = await WebSocket.connect(uri,
+      compression: CompressionOptions.compressionOff);
+
+  final getVM = new Completer<String>();
+  final reader = new SnapshotReader();
+
+  reader.onProgress.listen(print);
+
+  ws.listen((dynamic response) {
+    if (response is String) {
+      response = json.decode(response);
+      if (response['id'] == 1) {
+        getVM.complete(response['result']['isolates'][0]['id']);
+      }
+    } else if (response is List<int>) {
+      response = new Uint8List.fromList(response);
+      final dataOffset =
+          new ByteData.view(response.buffer).getUint32(0, Endian.little);
+      dynamic metadata = new Uint8List.view(response.buffer, 4, dataOffset - 4);
+      final data = new Uint8List.view(
+          response.buffer, dataOffset, response.length - dataOffset);
+      metadata = utf8.decode(metadata);
+      metadata = json.decode(metadata);
+      var event = metadata['params']['event'];
+      if (event['kind'] == 'HeapSnapshot') {
+        bool last = event['last'] == true;
+        reader.add(data);
+        if (last) {
+          reader.close();
+          ws.close();
+        }
+      }
+    }
+  });
+
+  ws.add(json.encode({
+    'jsonrpc': '2.0',
+    'method': 'getVM',
+    'params': {},
+    'id': 1,
+  }));
+
+  final String isolateId = await getVM.future;
+
+  ws.add(json.encode({
+    'jsonrpc': '2.0',
+    'method': 'streamListen',
+    'params': {'streamId': 'HeapSnapshot'},
+    'id': 2,
+  }));
+  ws.add(json.encode({
+    'jsonrpc': '2.0',
+    'method': 'requestHeapSnapshot',
+    'params': {'isolateId': isolateId},
+    'id': 3,
+  }));
+
+  return reader.done;
+}
+
+String makeData(dynamic root) {
+  // 'root' can be arbitrarily deep, so we can't directly represent it as a
+  // JSON tree, which cause a stack overflow here encoding it and in the JS
+  // engine decoding it. Instead we flatten the tree into a list of tuples with
+  // a parent pointer and re-inflate it in JS.
+  final indices = <dynamic, int>{};
+  final preorder = <dynamic>[];
+  preorder.add(root);
+
+  for (var index = 0; index < preorder.length; index++) {
+    final object = preorder[index];
+    preorder.addAll(object.children);
+  }
+
+  final flattened = <dynamic>[];
+  for (var index = 0; index < preorder.length; index++) {
+    final object = preorder[index];
+    indices[object] = index;
+
+    flattened.add(object.description);
+    flattened.add(object.klass.name);
+    flattened.add(object.retainedSize);
+    if (index == 0) {
+      flattened.add(null);
+    } else {
+      flattened.add(indices[object.parent] as int);
+    }
+  }
+
+  return json.encode(flattened);
+}
+
+var css = '''
+.treemapTile {
+    position: absolute;
+    box-sizing: border-box;
+    border: solid 1px;
+    font-size: 10;
+    text-align: center;
+    overflow: hidden;
+    white-space: nowrap;
+    cursor: default;
+}
+''';
+
+var js = '''
+function hash(string) {
+  // Jenkin's one_at_a_time.
+  let h = string.length;
+  for (let i = 0; i < string.length; i++) {
+    h += string.charCodeAt(i);
+    h += h << 10;
+    h ^= h >> 6;
+  }
+  h += h << 3;
+  h ^= h >> 11;
+  h += h << 15;
+  return h;
+}
+
+function color(string) {
+  let hue = hash(string) % 360;
+  return "hsl(" + hue + ",60%,60%)";
+}
+
+function prettySize(size) {
+  if (size < 1024) return size + "B";
+  size /= 1024;
+  if (size < 1024) return size.toFixed(1) + "KiB";
+  size /= 1024;
+  if (size < 1024) return size.toFixed(1) + "MiB";
+  size /= 1024;
+  return size.toFixed(1) + "GiB";
+}
+
+function prettyPercent(fraction) {
+  return (fraction * 100).toFixed(1);
+}
+
+function createTreemapTile(v, width, height, depth) {
+  let div = document.createElement("div");
+  div.className = "treemapTile";
+  div.style["background-color"] = color(v.type);
+  div.ondblclick = function(event) {
+    event.stopPropagation();
+    if (depth == 0) {
+      let dom = v.parent;
+      if (dom == undefined) {
+        // Already at root.
+      } else {
+        showDominatorTree(dom);  // Zoom out.
+      }
+    } else {
+      showDominatorTree(v);  // Zoom in.
+    }
+  };
+
+  let left = 0;
+  let top = 0;
+
+  const kPadding = 5;
+  const kBorder = 1;
+  left += kPadding - kBorder;
+  top += kPadding - kBorder;
+  width -= 2 * kPadding;
+  height -= 2 * kPadding;
+
+  let label = v.name + " [" + prettySize(v.size) + "]";
+  div.title = label;
+
+  if (width < 10 || height < 10) {
+    // Too small: don't render label or children.
+    return div;
+  }
+
+  div.appendChild(document.createTextNode(label));
+  const kLabelHeight = 9;
+  top += kLabelHeight;
+  height -= kLabelHeight;
+
+  if (depth > 2) {
+    // Too deep: don't render children.
+    return div;
+  }
+  if (width < 4 || height < 4) {
+    // Too small: don't render children.
+    return div;
+  }
+
+  let children = new Array();
+  v.children.forEach(function(c) {
+    // Size 0 children seem to confuse the layout algorithm (accumulating
+    // rounding errors?).
+    if (c.size > 0) {
+      children.push(c);
+    }
+  });
+  children.sort(function (a, b) {
+    return b.size - a.size;
+  });
+
+  const scale = width * height / v.size;
+
+  // Bruls M., Huizing K., van Wijk J.J. (2000) Squarified Treemaps. In: de
+  // Leeuw W.C., van Liere R. (eds) Data Visualization 2000. Eurographics.
+  // Springer, Vienna.
+  for (let rowStart = 0;  // Index of first child in the next row.
+       rowStart < children.length;) {
+    // Prefer wider rectangles, the better to fit text labels.
+    const GOLDEN_RATIO = 1.61803398875;
+    let verticalSplit = (width / height) > GOLDEN_RATIO;
+
+    let space;
+    if (verticalSplit) {
+      space = height;
+    } else {
+      space = width;
+    }
+
+    let rowMin = children[rowStart].size * scale;
+    let rowMax = rowMin;
+    let rowSum = 0;
+    let lastRatio = 0;
+
+    let rowEnd;  // One after index of last child in the next row.
+    for (rowEnd = rowStart; rowEnd < children.length; rowEnd++) {
+      let size = children[rowEnd].size * scale;
+      if (size < rowMin) rowMin = size;
+      if (size > rowMax) rowMax = size;
+      rowSum += size;
+
+      let ratio = Math.max((space * space * rowMax) / (rowSum * rowSum),
+                           (rowSum * rowSum) / (space * space * rowMin));
+      if ((lastRatio != 0) && (ratio > lastRatio)) {
+        // Adding the next child makes the aspect ratios worse: remove it and
+        // add the row.
+        rowSum -= size;
+        break;
+      }
+      lastRatio = ratio;
+    }
+
+    let rowLeft = left;
+    let rowTop = top;
+    let rowSpace = rowSum / space;
+
+    for (let i = rowStart; i < rowEnd; i++) {
+      let child = children[i];
+      let size = child.size * scale;
+
+      let childWidth;
+      let childHeight;
+      if (verticalSplit) {
+        childWidth = rowSpace;
+        childHeight = size / childWidth;
+      } else {
+        childHeight = rowSpace;
+        childWidth = size / childHeight;
+      }
+
+      let childDiv = createTreemapTile(child, childWidth, childHeight, depth + 1);
+      childDiv.style.left = rowLeft + "px";
+      childDiv.style.top = rowTop + "px";
+      // Oversize the final div by kBorder to make the borders overlap.
+      childDiv.style.width = (childWidth + kBorder) + "px";
+      childDiv.style.height = (childHeight + kBorder) + "px";
+      div.appendChild(childDiv);
+
+      if (verticalSplit)
+        rowTop += childHeight;
+      else
+        rowLeft += childWidth;
+    }
+
+    if (verticalSplit) {
+      left += rowSpace;
+      width -= rowSpace;
+    } else {
+      top += rowSpace;
+      height -= rowSpace;
+    }
+
+    rowStart = rowEnd;
+  }
+
+  return div;
+}
+
+function setBody(div) {
+  let body = document.body;
+  while (body.firstChild) {
+    body.removeChild(body.firstChild);
+  }
+  body.appendChild(div);
+}
+
+function showDominatorTree(v) {
+  let header = document.createElement("div");
+  header.textContent = "Dominator Tree";
+  header.title =
+    "Double click a box to zoom in.\\n" +
+    "Double click the outermost box to zoom out.";
+  header.className = "headerRow";
+  header.style["flex-grow"] = 0;
+  header.style["padding"] = "5px";
+  header.style["border-bottom"] = "solid 1px";
+
+  let content = document.createElement("div");
+  content.style["flex-basis"] = 0;
+  content.style["flex-grow"] = 1;
+
+  let column = document.createElement("div");
+  column.style["width"] = "100%";
+  column.style["height"] = "100%";
+  column.style["border"] = "solid 2px";
+  column.style["display"] = "flex";
+  column.style["flex-direction"] = "column";
+  column.appendChild(header);
+  column.appendChild(content);
+
+  setBody(column);
+
+  // Add the content div to the document first so the browser will calculate
+  // the available width and height.
+  let w = content.offsetWidth;
+  let h = content.offsetHeight;
+
+  let topTile = createTreemapTile(v, w, h, 0);
+  topTile.style.width = w;
+  topTile.style.height = h;
+  topTile.style.border = "none";
+  content.appendChild(topTile);
+}
+
+function inflateData(flattened) {
+  // 'root' can be arbitrarily deep, so we need to use an explicit stack
+  // instead of the call stack.
+  let nodes = new Array();
+  let i = 0;
+  while (i < flattened.length) {
+    let node = {
+      "name": flattened[i++],
+      "type": flattened[i++],
+      "size": flattened[i++],
+      "children": [],
+      "parent": null
+    };
+    nodes.push(node);
+
+    let parentIndex = flattened[i++];
+    if (parentIndex != null) {
+      let parent = nodes[parentIndex];
+      parent.children.push(node);
+      node.parent = parent;
+    }
+  }
+
+  return nodes[0];
+}
+
+var root = __DATA__;
+root = inflateData(root);
+
+showDominatorTree(root);
+''';
+
+var html = '''
+<html>
+  <head>
+    <title>Dart Heap Snapshot</title>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <style>$css</style>
+  </head>
+  <body>
+    <script>$js</script>
+  </body>
+</html>
+''';
+
+main(List<String> args) async {
+  if (args.length < 1) {
+    print('Usage: heap_snapshot.dart <vm-service-uri>');
+    exitCode = 1;
+    return;
+  }
+
+  var uri = Uri.parse(args[0]);
+  if (uri.scheme == 'http') {
+    uri = uri.replace(scheme: 'ws');
+  } else if (uri.scheme == 'https') {
+    uri = uri.replace(scheme: 'wss');
+  }
+  if (!uri.path.endsWith('/ws')) {
+    uri = uri.resolve('ws');
+  }
+
+  final snapshot = await load(uri.toString());
+
+  final dir = await Directory.systemTemp.createTemp('heap-snapshot');
+  final path = dir.path + '/merged-dominator.html';
+  final file = await File(path).create();
+  final tree = makeData(snapshot.mergedRoot);
+  await file.writeAsString(html.replaceAll('__DATA__', tree));
+  print('Wrote file://' + path);
+}
diff --git a/runtime/observatory_2/bin/shell.dart b/runtime/observatory_2/bin/shell.dart
new file mode 100644
index 0000000..f17f11e
--- /dev/null
+++ b/runtime/observatory_2/bin/shell.dart
@@ -0,0 +1,37 @@
+// Copyright (c) 2014, 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.
+
+library shell;
+
+import 'package:observatory_2/service_io.dart';
+
+import 'dart:io';
+
+// Simple demo for service_io library. Connects to localhost on the default
+// port, picks the first isolate, reads requests from stdin, and prints
+// results to stdout. Example session:
+// <<< isolate isolates/1071334835
+// >>> /classes/40
+// <<< {"type":"Class","id":"classes\/40","name":"num","user_name":"num",...
+// >>> /objects/0
+// >>> {"type":"Array","class":{"type":"@Class","id":"classes\/62",...
+
+void repl(VM vm, Isolate isolate, String lastResult) {
+  print(lastResult);
+  Map params = {
+    'objectId': stdin.readLineSync(),
+  };
+  isolate.invokeRpcNoUpgrade('getObject', params).then((Map result) {
+    repl(vm, isolate, result.toString());
+  });
+}
+
+void main() {
+  String addr = 'ws://localhost:8181/ws';
+  new WebSocketVM(new WebSocketVMTarget(addr)).load().then((serviceObject) {
+    VM vm = serviceObject;
+    Isolate isolate = vm.isolates.first;
+    repl(vm, isolate, 'isolate ${isolate.id}');
+  });
+}
diff --git a/runtime/observatory_2/lib/allocation_profile.dart b/runtime/observatory_2/lib/allocation_profile.dart
new file mode 100644
index 0000000..70e8bae
--- /dev/null
+++ b/runtime/observatory_2/lib/allocation_profile.dart
@@ -0,0 +1,10 @@
+// Copyright (c) 2015, 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.
+
+library allocation_profiler;
+
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/service.dart' as S;
+
+part 'src/allocation_profile/allocation_profile.dart';
diff --git a/runtime/observatory_2/lib/app.dart b/runtime/observatory_2/lib/app.dart
new file mode 100644
index 0000000..86de2ed
--- /dev/null
+++ b/runtime/observatory_2/lib/app.dart
@@ -0,0 +1,30 @@
+// Copyright (c) 2014, 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.
+
+library app;
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:html';
+
+import 'package:logging/logging.dart';
+import 'package:observatory_2/service_html.dart';
+import 'package:observatory_2/elements.dart';
+import 'package:observatory_2/src/elements/helpers/uris.dart';
+import 'package:observatory_2/event.dart';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/repositories.dart';
+import 'package:observatory_2/tracer.dart';
+import 'package:observatory_2/utils.dart';
+import 'package:stack_trace/stack_trace.dart';
+import 'package:usage/usage_html.dart';
+
+export 'package:observatory_2/utils.dart';
+
+part 'src/app/application.dart';
+part 'src/app/location_manager.dart';
+part 'src/app/notification.dart';
+part 'src/app/page.dart';
+part 'src/app/settings.dart';
+part 'src/app/view_model.dart';
diff --git a/runtime/observatory_2/lib/cli.dart b/runtime/observatory_2/lib/cli.dart
new file mode 100644
index 0000000..607c58a
--- /dev/null
+++ b/runtime/observatory_2/lib/cli.dart
@@ -0,0 +1,9 @@
+// Copyright (c) 2015, 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.
+
+library cli;
+
+import 'dart:async';
+
+part 'src/cli/command.dart';
diff --git a/runtime/observatory_2/lib/debugger.dart b/runtime/observatory_2/lib/debugger.dart
new file mode 100644
index 0000000..c6a8d27
--- /dev/null
+++ b/runtime/observatory_2/lib/debugger.dart
@@ -0,0 +1,12 @@
+// Copyright (c) 2015, 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.
+
+library debugger;
+
+import 'dart:async';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/service.dart';
+
+part 'src/debugger/debugger.dart';
+part 'src/debugger/debugger_location.dart';
diff --git a/runtime/observatory_2/lib/elements.dart b/runtime/observatory_2/lib/elements.dart
new file mode 100644
index 0000000..b4f60b7
--- /dev/null
+++ b/runtime/observatory_2/lib/elements.dart
@@ -0,0 +1,99 @@
+library observatory_elements;
+
+export 'package:observatory_2/src/elements/allocation_profile.dart';
+export 'package:observatory_2/src/elements/class_allocation_profile.dart';
+export 'package:observatory_2/src/elements/class_instances.dart';
+export 'package:observatory_2/src/elements/class_ref.dart';
+export 'package:observatory_2/src/elements/class_tree.dart';
+export 'package:observatory_2/src/elements/class_view.dart';
+export 'package:observatory_2/src/elements/code_ref.dart';
+export 'package:observatory_2/src/elements/code_view.dart';
+export 'package:observatory_2/src/elements/containers/search_bar.dart';
+export 'package:observatory_2/src/elements/containers/virtual_collection.dart';
+export 'package:observatory_2/src/elements/containers/virtual_tree.dart';
+export 'package:observatory_2/src/elements/context_ref.dart';
+export 'package:observatory_2/src/elements/context_view.dart';
+export 'package:observatory_2/src/elements/cpu_profile.dart';
+export 'package:observatory_2/src/elements/cpu_profile/virtual_tree.dart';
+export 'package:observatory_2/src/elements/cpu_profile_table.dart';
+export 'package:observatory_2/src/elements/curly_block.dart';
+export 'package:observatory_2/src/elements/debugger.dart';
+export 'package:observatory_2/src/elements/error_ref.dart';
+export 'package:observatory_2/src/elements/error_view.dart';
+export 'package:observatory_2/src/elements/eval_box.dart';
+export 'package:observatory_2/src/elements/field_ref.dart';
+export 'package:observatory_2/src/elements/field_view.dart';
+export 'package:observatory_2/src/elements/flag_list.dart';
+export 'package:observatory_2/src/elements/function_ref.dart';
+export 'package:observatory_2/src/elements/function_view.dart';
+export 'package:observatory_2/src/elements/general_error.dart';
+export 'package:observatory_2/src/elements/heap_map.dart';
+export 'package:observatory_2/src/elements/heap_snapshot.dart';
+export 'package:observatory_2/src/elements/process_snapshot.dart';
+export 'package:observatory_2/src/elements/helpers/rendering_queue.dart';
+export 'package:observatory_2/src/elements/icdata_ref.dart';
+export 'package:observatory_2/src/elements/icdata_view.dart';
+export 'package:observatory_2/src/elements/instance_ref.dart';
+export 'package:observatory_2/src/elements/instance_view.dart';
+export 'package:observatory_2/src/elements/isolate/counter_chart.dart';
+export 'package:observatory_2/src/elements/isolate/location.dart';
+export 'package:observatory_2/src/elements/isolate/run_state.dart';
+export 'package:observatory_2/src/elements/isolate/shared_summary.dart';
+export 'package:observatory_2/src/elements/isolate/summary.dart';
+export 'package:observatory_2/src/elements/isolate_reconnect.dart';
+export 'package:observatory_2/src/elements/isolate_ref.dart';
+export 'package:observatory_2/src/elements/isolate_view.dart';
+export 'package:observatory_2/src/elements/json_view.dart';
+export 'package:observatory_2/src/elements/library_ref.dart';
+export 'package:observatory_2/src/elements/library_view.dart';
+export 'package:observatory_2/src/elements/local_var_descriptors_ref.dart';
+export 'package:observatory_2/src/elements/logging.dart';
+export 'package:observatory_2/src/elements/megamorphiccache_ref.dart';
+export 'package:observatory_2/src/elements/megamorphiccache_view.dart';
+export 'package:observatory_2/src/elements/metric/details.dart';
+export 'package:observatory_2/src/elements/metric/graph.dart';
+export 'package:observatory_2/src/elements/metrics.dart';
+export 'package:observatory_2/src/elements/native_memory_profiler.dart';
+export 'package:observatory_2/src/elements/nav/class_menu.dart';
+export 'package:observatory_2/src/elements/nav/isolate_menu.dart';
+export 'package:observatory_2/src/elements/nav/library_menu.dart';
+export 'package:observatory_2/src/elements/nav/menu_item.dart';
+export 'package:observatory_2/src/elements/nav/notify.dart';
+export 'package:observatory_2/src/elements/nav/notify_event.dart';
+export 'package:observatory_2/src/elements/nav/notify_exception.dart';
+export 'package:observatory_2/src/elements/nav/refresh.dart';
+export 'package:observatory_2/src/elements/nav/top_menu.dart';
+export 'package:observatory_2/src/elements/nav/vm_menu.dart';
+export 'package:observatory_2/src/elements/object_common.dart';
+export 'package:observatory_2/src/elements/object_view.dart';
+export 'package:observatory_2/src/elements/objectpool_ref.dart';
+export 'package:observatory_2/src/elements/objectpool_view.dart';
+export 'package:observatory_2/src/elements/objectstore_view.dart';
+export 'package:observatory_2/src/elements/observatory_application.dart';
+export 'package:observatory_2/src/elements/pc_descriptors_ref.dart';
+export 'package:observatory_2/src/elements/persistent_handles.dart';
+export 'package:observatory_2/src/elements/ports.dart';
+export 'package:observatory_2/src/elements/sample_buffer_control.dart';
+export 'package:observatory_2/src/elements/script_inset.dart';
+export 'package:observatory_2/src/elements/script_ref.dart';
+export 'package:observatory_2/src/elements/script_view.dart';
+export 'package:observatory_2/src/elements/sentinel_value.dart';
+export 'package:observatory_2/src/elements/sentinel_view.dart';
+export 'package:observatory_2/src/elements/singletargetcache_ref.dart';
+export 'package:observatory_2/src/elements/singletargetcache_view.dart';
+export 'package:observatory_2/src/elements/source_inset.dart';
+export 'package:observatory_2/src/elements/source_link.dart';
+export 'package:observatory_2/src/elements/stack_trace_tree_config.dart';
+export 'package:observatory_2/src/elements/strongly_reachable_instances.dart';
+export 'package:observatory_2/src/elements/subtypetestcache_ref.dart';
+export 'package:observatory_2/src/elements/subtypetestcache_view.dart';
+export 'package:observatory_2/src/elements/timeline/dashboard.dart';
+export 'package:observatory_2/src/elements/timeline_page.dart';
+export 'package:observatory_2/src/elements/type_arguments_ref.dart';
+export 'package:observatory_2/src/elements/unknown_ref.dart';
+export 'package:observatory_2/src/elements/unlinkedcall_ref.dart';
+export 'package:observatory_2/src/elements/unlinkedcall_view.dart';
+export 'package:observatory_2/src/elements/view_footer.dart';
+export 'package:observatory_2/src/elements/vm_connect.dart';
+export 'package:observatory_2/src/elements/vm_connect_target.dart';
+export 'package:observatory_2/src/elements/vm_view.dart';
diff --git a/runtime/observatory_2/lib/event.dart b/runtime/observatory_2/lib/event.dart
new file mode 100644
index 0000000..67d89db
--- /dev/null
+++ b/runtime/observatory_2/lib/event.dart
@@ -0,0 +1,380 @@
+// Copyright (c) 2016, 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.
+
+import 'package:logging/logging.dart';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/service.dart' as S;
+
+class VMUpdateEvent implements M.VMUpdateEvent {
+  final DateTime timestamp;
+  final M.VMRef vm;
+  VMUpdateEvent(this.timestamp, this.vm) {
+    assert(timestamp != null);
+    assert(vm != null);
+  }
+}
+
+class IsolateStartEvent implements M.IsolateStartEvent {
+  final DateTime timestamp;
+  final M.IsolateRef isolate;
+  IsolateStartEvent(this.timestamp, this.isolate) {
+    assert(timestamp != null);
+    assert(isolate != null);
+  }
+}
+
+class IsolateRunnableEvent implements M.IsolateRunnableEvent {
+  final DateTime timestamp;
+  final M.IsolateRef isolate;
+  IsolateRunnableEvent(this.timestamp, this.isolate) {
+    assert(timestamp != null);
+    assert(isolate != null);
+  }
+}
+
+class IsolateExitEvent implements M.IsolateExitEvent {
+  final DateTime timestamp;
+  final M.IsolateRef isolate;
+  IsolateExitEvent(this.timestamp, this.isolate) {
+    assert(timestamp != null);
+    assert(isolate != null);
+  }
+}
+
+class IsolateUpdateEvent implements M.IsolateUpdateEvent {
+  final DateTime timestamp;
+  final M.IsolateRef isolate;
+  IsolateUpdateEvent(this.timestamp, this.isolate) {
+    assert(timestamp != null);
+    assert(isolate != null);
+  }
+}
+
+class IsolateReloadEvent implements M.IsolateReloadEvent {
+  final DateTime timestamp;
+  final M.IsolateRef isolate;
+  final M.ErrorRef error;
+  IsolateReloadEvent(this.timestamp, this.isolate, this.error) {
+    assert(timestamp != null);
+    assert(isolate != null);
+    assert(error != null);
+  }
+}
+
+class ServiceExtensionAddedEvent implements M.ServiceExtensionAddedEvent {
+  final DateTime timestamp;
+  final M.IsolateRef isolate;
+  final String extensionRPC;
+  ServiceExtensionAddedEvent(this.timestamp, this.isolate, this.extensionRPC) {
+    assert(timestamp != null);
+    assert(isolate != null);
+    assert(extensionRPC != null);
+  }
+}
+
+class DebuggerSettingsUpdateEvent implements M.DebuggerSettingsUpdateEvent {
+  final DateTime timestamp;
+  final M.IsolateRef isolate;
+  DebuggerSettingsUpdateEvent(this.timestamp, this.isolate) {
+    assert(timestamp != null);
+    assert(isolate != null);
+  }
+}
+
+class PauseStartEvent implements M.PauseStartEvent {
+  final DateTime timestamp;
+  final M.IsolateRef isolate;
+  PauseStartEvent(this.timestamp, this.isolate) {
+    assert(timestamp != null);
+    assert(isolate != null);
+  }
+}
+
+class PauseExitEvent implements M.PauseExitEvent {
+  final DateTime timestamp;
+  final M.IsolateRef isolate;
+  PauseExitEvent(this.timestamp, this.isolate) {
+    assert(timestamp != null);
+    assert(isolate != null);
+  }
+}
+
+class PauseBreakpointEvent implements M.PauseBreakpointEvent {
+  final DateTime timestamp;
+  final M.IsolateRef isolate;
+  final Iterable<M.Breakpoint> pauseBreakpoints;
+  final M.Frame topFrame;
+  final bool atAsyncSuspension;
+
+  /// [optional]
+  final M.Breakpoint breakpoint;
+  PauseBreakpointEvent(
+      this.timestamp,
+      this.isolate,
+      Iterable<M.Breakpoint> pauseBreakpoints,
+      this.topFrame,
+      this.atAsyncSuspension,
+      [this.breakpoint])
+      : pauseBreakpoints = new List.unmodifiable(pauseBreakpoints) {
+    assert(timestamp != null);
+    assert(isolate != null);
+    assert(pauseBreakpoints != null);
+    assert(topFrame != null);
+    assert(atAsyncSuspension != null);
+  }
+}
+
+class PauseInterruptedEvent implements M.PauseInterruptedEvent {
+  final DateTime timestamp;
+  final M.IsolateRef isolate;
+  final M.Frame topFrame;
+  final bool atAsyncSuspension;
+  PauseInterruptedEvent(
+      this.timestamp, this.isolate, this.topFrame, this.atAsyncSuspension) {
+    assert(timestamp != null);
+    assert(isolate != null);
+    assert(atAsyncSuspension != null);
+  }
+}
+
+class PausePostRequestEvent implements M.PausePostRequestEvent {
+  final DateTime timestamp;
+  final M.IsolateRef isolate;
+  final M.Frame topFrame;
+  final bool atAsyncSuspension;
+  PausePostRequestEvent(
+      this.timestamp, this.isolate, this.topFrame, this.atAsyncSuspension) {
+    assert(timestamp != null);
+    assert(isolate != null);
+    assert(atAsyncSuspension != null);
+  }
+}
+
+class PauseExceptionEvent implements M.PauseExceptionEvent {
+  final DateTime timestamp;
+  final M.IsolateRef isolate;
+  final M.Frame topFrame;
+  final M.InstanceRef exception;
+  PauseExceptionEvent(
+      this.timestamp, this.isolate, this.topFrame, this.exception) {
+    assert(timestamp != null);
+    assert(isolate != null);
+    assert(topFrame != null);
+    assert(exception != null);
+  }
+}
+
+class ResumeEvent implements M.ResumeEvent {
+  final DateTime timestamp;
+  final M.IsolateRef isolate;
+  final M.Frame topFrame;
+  ResumeEvent(this.timestamp, this.isolate, this.topFrame) {
+    assert(timestamp != null);
+    assert(isolate != null);
+  }
+}
+
+class BreakpointAddedEvent implements M.BreakpointAddedEvent {
+  final DateTime timestamp;
+  final M.IsolateRef isolate;
+  final M.Breakpoint breakpoint;
+  BreakpointAddedEvent(this.timestamp, this.isolate, this.breakpoint) {
+    assert(timestamp != null);
+    assert(isolate != null);
+    assert(breakpoint != null);
+  }
+}
+
+class BreakpointResolvedEvent implements M.BreakpointResolvedEvent {
+  final DateTime timestamp;
+  final M.IsolateRef isolate;
+  final M.Breakpoint breakpoint;
+  BreakpointResolvedEvent(this.timestamp, this.isolate, this.breakpoint) {
+    assert(timestamp != null);
+    assert(isolate != null);
+    assert(breakpoint != null);
+  }
+}
+
+class BreakpointRemovedEvent implements M.BreakpointRemovedEvent {
+  final DateTime timestamp;
+  final M.IsolateRef isolate;
+  final M.Breakpoint breakpoint;
+  BreakpointRemovedEvent(this.timestamp, this.isolate, this.breakpoint) {
+    assert(timestamp != null);
+    assert(isolate != null);
+    assert(breakpoint != null);
+  }
+}
+
+class InspectEvent implements M.InspectEvent {
+  final DateTime timestamp;
+  final M.IsolateRef isolate;
+  final M.InstanceRef inspectee;
+  InspectEvent(this.timestamp, this.isolate, this.inspectee) {
+    assert(timestamp != null);
+    assert(isolate != null);
+    assert(inspectee != null);
+  }
+}
+
+class NoneEvent implements M.NoneEvent {
+  final DateTime timestamp;
+  final M.IsolateRef isolate;
+  NoneEvent(this.timestamp, this.isolate) {
+    assert(timestamp != null);
+    assert(isolate != null);
+  }
+}
+
+class GCEvent implements M.GCEvent {
+  final DateTime timestamp;
+  final M.IsolateRef isolate;
+  GCEvent(this.timestamp, this.isolate) {
+    assert(timestamp != null);
+    assert(isolate != null);
+  }
+}
+
+class LoggingEvent implements M.LoggingEvent {
+  final DateTime timestamp;
+  final M.IsolateRef isolate;
+  final Map logRecord;
+  LoggingEvent(this.timestamp, this.isolate, this.logRecord) {
+    assert(timestamp != null);
+    assert(isolate != null);
+    assert(logRecord != null);
+  }
+}
+
+class ExtensionEvent implements M.ExtensionEvent {
+  final DateTime timestamp;
+  final M.IsolateRef isolate;
+  final String extensionKind;
+  final M.ExtensionData extensionData;
+  ExtensionEvent(
+      this.timestamp, this.isolate, this.extensionKind, this.extensionData) {
+    assert(timestamp != null);
+    assert(isolate != null);
+    assert(extensionKind != null);
+    assert(extensionData != null);
+  }
+}
+
+class TimelineEventsEvent implements M.TimelineEventsEvent {
+  final DateTime timestamp;
+  final M.IsolateRef isolate;
+  final Iterable<M.TimelineEvent> timelineEvents;
+  TimelineEventsEvent(
+      this.timestamp, this.isolate, Iterable<M.TimelineEvent> timelineEvents)
+      : timelineEvents = new List.unmodifiable(timelineEvents) {
+    assert(timestamp != null);
+    assert(isolate != null);
+    assert(timelineEvents != null);
+  }
+}
+
+class ConnectionClosedEvent implements M.ConnectionClosedEvent {
+  final DateTime timestamp;
+  final String reason;
+  ConnectionClosedEvent(this.timestamp, this.reason) {
+    assert(timestamp != null);
+    assert(reason != null);
+  }
+}
+
+class ServiceRegisteredEvent implements M.ServiceRegisteredEvent {
+  final DateTime timestamp;
+  final String service;
+  final String method;
+  final String alias;
+  ServiceRegisteredEvent(
+      this.timestamp, this.service, this.method, this.alias) {
+    assert(timestamp != null);
+    assert(service != null);
+    assert(method != null);
+    assert(alias != null);
+  }
+}
+
+class ServiceUnregisteredEvent implements M.ServiceUnregisteredEvent {
+  final DateTime timestamp;
+  final String service;
+  final String method;
+  ServiceUnregisteredEvent(this.timestamp, this.service, this.method) {
+    assert(timestamp != null);
+    assert(service != null);
+    assert(method != null);
+  }
+}
+
+M.Event createEventFromServiceEvent(S.ServiceEvent event) {
+  switch (event.kind) {
+    case S.ServiceEvent.kVMUpdate:
+      return new VMUpdateEvent(event.timestamp, event.vm);
+    case S.ServiceEvent.kIsolateStart:
+      return new IsolateStartEvent(event.timestamp, event.isolate);
+    case S.ServiceEvent.kIsolateRunnable:
+      return new IsolateRunnableEvent(event.timestamp, event.isolate);
+    case S.ServiceEvent.kIsolateUpdate:
+      return new IsolateUpdateEvent(event.timestamp, event.isolate);
+    case S.ServiceEvent.kIsolateReload:
+      return new IsolateReloadEvent(
+          event.timestamp, event.isolate, event.error);
+    case S.ServiceEvent.kIsolateExit:
+      return new IsolateExitEvent(event.timestamp, event.isolate);
+    case S.ServiceEvent.kBreakpointAdded:
+      return new BreakpointAddedEvent(
+          event.timestamp, event.isolate, event.breakpoint);
+    case S.ServiceEvent.kBreakpointResolved:
+      return new BreakpointResolvedEvent(
+          event.timestamp, event.isolate, event.breakpoint);
+    case S.ServiceEvent.kBreakpointRemoved:
+      return new BreakpointRemovedEvent(
+          event.timestamp, event.isolate, event.breakpoint);
+    case S.ServiceEvent.kDebuggerSettingsUpdate:
+      return new DebuggerSettingsUpdateEvent(event.timestamp, event.isolate);
+    case S.ServiceEvent.kResume:
+      return new ResumeEvent(event.timestamp, event.isolate, event.topFrame);
+    case S.ServiceEvent.kPauseStart:
+      return new PauseStartEvent(event.timestamp, event.isolate);
+    case S.ServiceEvent.kPauseExit:
+      return new PauseExitEvent(event.timestamp, event.isolate);
+    case S.ServiceEvent.kPausePostRequest:
+      return new PausePostRequestEvent(event.timestamp, event.isolate,
+          event.topFrame, event.atAsyncSuspension);
+    case S.ServiceEvent.kPauseBreakpoint:
+      return new PauseBreakpointEvent(
+          event.timestamp,
+          event.isolate,
+          event.pauseBreakpoints,
+          event.topFrame,
+          event.atAsyncSuspension,
+          event.breakpoint);
+    case S.Isolate.kLoggingStream:
+      return new LoggingEvent(event.timestamp, event.isolate, event.logRecord);
+    case S.ServiceEvent.kPauseInterrupted:
+      return new PauseInterruptedEvent(event.timestamp, event.isolate,
+          event.topFrame, event.atAsyncSuspension);
+    case S.ServiceEvent.kPauseException:
+      return new PauseExceptionEvent(
+          event.timestamp, event.isolate, event.topFrame, event.exception);
+    case S.ServiceEvent.kInspect:
+      return new InspectEvent(event.timestamp, event.isolate, event.inspectee);
+    case S.ServiceEvent.kGC:
+      return new GCEvent(event.timestamp, event.isolate);
+    case S.ServiceEvent.kServiceRegistered:
+      return new ServiceRegisteredEvent(
+          event.timestamp, event.service, event.method, event.alias);
+    case S.ServiceEvent.kServiceUnregistered:
+      return new ServiceUnregisteredEvent(
+          event.timestamp, event.service, event.method);
+    case S.ServiceEvent.kNone:
+      return new NoneEvent(event.timestamp, event.isolate);
+    default:
+      // Ignore unrecognized events.
+      Logger.root.severe('Unrecognized event: $event');
+      return null;
+  }
+}
diff --git a/runtime/observatory_2/lib/models.dart b/runtime/observatory_2/lib/models.dart
new file mode 100644
index 0000000..9ea69d9
--- /dev/null
+++ b/runtime/observatory_2/lib/models.dart
@@ -0,0 +1,99 @@
+// Copyright (c) 2016, 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.
+
+library models;
+
+import 'dart:async';
+import 'dart:typed_data';
+import 'package:observatory_2/object_graph.dart';
+
+part 'src/models/exceptions.dart';
+
+part 'src/models/objects/allocation_profile.dart';
+part 'src/models/objects/breakpoint.dart';
+part 'src/models/objects/class.dart';
+part 'src/models/objects/code.dart';
+part 'src/models/objects/context.dart';
+part 'src/models/objects/error.dart';
+part 'src/models/objects/event.dart';
+part 'src/models/objects/extension_data.dart';
+part 'src/models/objects/field.dart';
+part 'src/models/objects/flag.dart';
+part 'src/models/objects/frame.dart';
+part 'src/models/objects/function.dart';
+part 'src/models/objects/guarded.dart';
+part 'src/models/objects/heap_space.dart';
+part 'src/models/objects/icdata.dart';
+part 'src/models/objects/inbound_references.dart';
+part 'src/models/objects/instance.dart';
+part 'src/models/objects/isolate.dart';
+part 'src/models/objects/isolate_group.dart';
+part 'src/models/objects/library.dart';
+part 'src/models/objects/local_var_descriptors.dart';
+part 'src/models/objects/map_association.dart';
+part 'src/models/objects/megamorphiccache.dart';
+part 'src/models/objects/metric.dart';
+part 'src/models/objects/notification.dart';
+part 'src/models/objects/object.dart';
+part 'src/models/objects/objectpool.dart';
+part 'src/models/objects/objectstore.dart';
+part 'src/models/objects/pc_descriptors.dart';
+part 'src/models/objects/persistent_handles.dart';
+part 'src/models/objects/ports.dart';
+part 'src/models/objects/retaining_path.dart';
+part 'src/models/objects/sample_profile.dart';
+part 'src/models/objects/script.dart';
+part 'src/models/objects/sentinel.dart';
+part 'src/models/objects/service.dart';
+part 'src/models/objects/single_target_cache.dart';
+part 'src/models/objects/source_location.dart';
+part 'src/models/objects/subtype_test_cache.dart';
+part 'src/models/objects/target.dart';
+part 'src/models/objects/thread.dart';
+part 'src/models/objects/timeline.dart';
+part 'src/models/objects/timeline_event.dart';
+part 'src/models/objects/type_arguments.dart';
+part 'src/models/objects/unknown.dart';
+part 'src/models/objects/unlinked_call.dart';
+part 'src/models/objects/vm.dart';
+part 'src/models/objects/zone.dart';
+
+part 'src/models/repositories/allocation_profile.dart';
+part 'src/models/repositories/breakpoint.dart';
+part 'src/models/repositories/class.dart';
+part 'src/models/repositories/context.dart';
+part 'src/models/repositories/editor.dart';
+part 'src/models/repositories/eval.dart';
+part 'src/models/repositories/event.dart';
+part 'src/models/repositories/field.dart';
+part 'src/models/repositories/flag.dart';
+part 'src/models/repositories/function.dart';
+part 'src/models/repositories/heap_snapshot.dart';
+part 'src/models/repositories/icdata.dart';
+part 'src/models/repositories/inbound_references.dart';
+part 'src/models/repositories/instance.dart';
+part 'src/models/repositories/isolate.dart';
+part 'src/models/repositories/isolate_group.dart';
+part 'src/models/repositories/library.dart';
+part 'src/models/repositories/megamorphiccache.dart';
+part 'src/models/repositories/metric.dart';
+part 'src/models/repositories/notification.dart';
+part 'src/models/repositories/object.dart';
+part 'src/models/repositories/objectpool.dart';
+part 'src/models/repositories/objectstore.dart';
+part 'src/models/repositories/persistent_handles.dart';
+part 'src/models/repositories/ports.dart';
+part 'src/models/repositories/reachable_size.dart';
+part 'src/models/repositories/retained_size.dart';
+part 'src/models/repositories/retaining_path.dart';
+part 'src/models/repositories/sample_profile.dart';
+part 'src/models/repositories/script.dart';
+part 'src/models/repositories/single_target_cache.dart';
+part 'src/models/repositories/strongly_reachable_instances.dart';
+part 'src/models/repositories/subtype_test_cache.dart';
+part 'src/models/repositories/target.dart';
+part 'src/models/repositories/timeline.dart';
+part 'src/models/repositories/type_arguments.dart';
+part 'src/models/repositories/unlinked_call.dart';
+part 'src/models/repositories/vm.dart';
diff --git a/runtime/observatory_2/lib/object_graph.dart b/runtime/observatory_2/lib/object_graph.dart
new file mode 100644
index 0000000..0e891ce
--- /dev/null
+++ b/runtime/observatory_2/lib/object_graph.dart
@@ -0,0 +1,1634 @@
+// Copyright (c) 2014, 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.
+
+library object_graph;
+
+import 'dart:async';
+import 'dart:collection';
+import 'dart:convert';
+import 'dart:typed_data';
+
+/// Decodes and analyzes heap snapshots produced by the Dart VM.
+abstract class SnapshotReader {
+  factory SnapshotReader() => _SnapshotReader._new();
+
+  void add(Uint8List chunk);
+  Future<SnapshotGraph> close();
+
+  Future<SnapshotGraph> get done;
+  Stream<String> get onProgress;
+}
+
+class _SnapshotReader implements SnapshotReader {
+  bool _closed = false;
+  var _chunks = <Uint8List>[];
+  final _onProgress = new StreamController<String>.broadcast();
+  final _done = new Completer<SnapshotGraph>();
+
+  _SnapshotReader._new();
+
+  void add(Uint8List chunk) {
+    if (_closed) {
+      throw new StateError("Stream is closed");
+    }
+    _chunks.add(chunk);
+    _onProgress.add("Receiving snapshot chunk ${_chunks.length}...");
+
+    // TODO(rmacnak): Incremental loading.
+  }
+
+  Future<SnapshotGraph> close() {
+    if (_closed) {
+      throw new StateError("Stream is closed");
+    }
+    _closed = true;
+
+    var graph = new _SnapshotGraph._new();
+    var chunks = _chunks;
+    _chunks = null; // Let the binary chunks be GCable.
+    _done.complete(graph._load(chunks, _onProgress));
+    return _done.future;
+  }
+
+  Future<SnapshotGraph> get done => _done.future;
+  Stream<String> get onProgress => _onProgress.stream;
+}
+
+Uint8List _newUint8Array(int size) {
+  try {
+    return new Uint8List(size);
+  } on ArgumentError catch (e) {
+    // JS throws a misleading invalid argument error. Convert to a more user-friendly message.
+    throw new Exception(
+        "OutOfMemoryError: Not enough memory available to analyze the snapshot.");
+  }
+}
+
+Uint16List _newUint16Array(int size) {
+  try {
+    return new Uint16List(size);
+  } on ArgumentError catch (e) {
+    // JS throws a misleading invalid argument error. Convert to a more user-friendly message.
+    throw new Exception(
+        "OutOfMemoryError: Not enough memory available to analyze the snapshot.");
+  }
+}
+
+Uint32List _newUint32Array(int size) {
+  try {
+    return new Uint32List(size);
+  } on ArgumentError catch (e) {
+    // JS throws a misleading invalid argument error. Convert to a more user-friendly message.
+    throw new Exception(
+        "OutOfMemoryError: Not enough memory available to analyze the snapshot.");
+  }
+}
+
+class _ReadStream {
+  final List<Uint8List> _buffers;
+  Uint8List _currentBuffer = Uint8List(0);
+  int _bufferIndex = 0;
+  int _byteIndex = 0;
+
+  _ReadStream._new(this._buffers);
+
+  bool atEnd() {
+    return _bufferIndex >= _buffers.length &&
+        _byteIndex >= _currentBuffer.length;
+  }
+
+  int readByte() {
+    int i = _byteIndex;
+    Uint8List b = _currentBuffer;
+    if (i < b.length) {
+      int r = b[i];
+      _byteIndex = i + 1;
+      return r;
+    }
+
+    return _readByteSlowPath();
+  }
+
+  int _readByteSlowPath() {
+    int i = _byteIndex;
+    Uint8List b = _currentBuffer;
+    while (i >= b.length) {
+      if (_bufferIndex >= _buffers.length) {
+        throw new StateError("Attempt to read past the end of a stream");
+      }
+      b = _currentBuffer = _buffers[_bufferIndex++];
+      i = 0;
+    }
+    int r = b[i];
+    _byteIndex = i + 1;
+    return r;
+  }
+
+  /// Read one ULEB128 number.
+  int readUnsigned() {
+    int result = 0;
+    int shift = 0;
+    for (;;) {
+      int part = readByte();
+      result |= (part & 0x7F) << shift;
+      if ((part & 0x80) == 0) {
+        break;
+      }
+      shift += 7;
+    }
+    return result;
+  }
+
+  /// Read one SLEB128 number.
+  int readSigned() {
+    int result = 0;
+    int shift = 0;
+    for (;;) {
+      int part = readByte();
+      result |= (part & 0x7F) << shift;
+      shift += 7;
+      if ((part & 0x80) == 0) {
+        if ((part & 0x40) != 0) {
+          result |= (-1 << shift);
+        }
+        break;
+      }
+    }
+    return result;
+  }
+
+  double readFloat64() {
+    final bytes = _newUint8Array(8);
+    for (var i = 0; i < 8; i++) {
+      bytes[i] = readByte();
+    }
+    return new Float64List.view(bytes.buffer)[0];
+  }
+
+  String readUtf8() {
+    final len = readUnsigned();
+    final bytes = _newUint8Array(len);
+    for (var i = 0; i < len; i++) {
+      bytes[i] = readByte();
+    }
+    return new Utf8Codec(allowMalformed: true).decode(bytes);
+  }
+
+  String readLatin1() {
+    final len = readUnsigned();
+    final codeUnits = _newUint8Array(len);
+    for (var i = 0; i < len; i++) {
+      codeUnits[i] = readByte();
+    }
+    return new String.fromCharCodes(codeUnits);
+  }
+
+  String readUtf16() {
+    final len = readUnsigned();
+    final codeUnits = _newUint16Array(len);
+    for (var i = 0; i < len; i++) {
+      codeUnits[i] = readByte() | (readByte() << 8);
+    }
+    return new String.fromCharCodes(codeUnits);
+  }
+}
+
+// Node indices for the root and sentinel nodes. Note that using 0 as the
+// sentinel means a newly allocated typed array comes initialized with all
+// elements as the sentinel.
+const _ROOT = 1;
+const _SENTINEL = 0;
+
+/// An object in a heap snapshot.
+abstract class SnapshotObject {
+  // If this object has been obtained from [successors] or [predecessors], the
+  // name of slot. Otherwise, the empty string.
+  String get label;
+
+  // The value for primitives. Otherwise, the class name.
+  String get description;
+
+  /// [internalSize] + [externalSize].
+  int get shallowSize;
+
+  /// The number of bytes in the Dart heap occupied by this object. May be 0
+  /// for objects that are in another heap but referenced from the heap of
+  /// interest. May also be 0 for synthetic objects such as the root.
+  int get internalSize;
+
+  /// The sum of all external allocations associated with this object.
+  /// See Dart_NewFinalizableHandle and Dart_NewWeakPersistentHandle.
+  int get externalSize;
+
+  /// The [shallowSize] of this object, plus the retainedSize of all its
+  /// children in the dominator tree. This is the amount of memory that would
+  /// be freed if the last reference to this object was erased.
+  int get retainedSize;
+
+  SnapshotClass get klass;
+
+  /// The objects directly referenced by this object. The [SnapshotObject]s
+  /// returned by this iterable have their [label] set to name of the slot
+  /// if it is available.
+  Iterable<SnapshotObject> get successors;
+
+  /// The objects directly referencing this object. The [SnapshotObject]s
+  /// returned by this iterable have their [label] set to name of the slot
+  /// if it is available.
+  Iterable<SnapshotObject> get predecessors;
+
+  /// The immediate dominator of this object. For the root object, returns self.
+  ///
+  /// See https://en.wikipedia.org/wiki/Dominator_(graph_theory).
+  SnapshotObject get parent;
+
+  /// The objects for which this object is the immediate dominator.
+  ///
+  /// See https://en.wikipedia.org/wiki/Dominator_(graph_theory).
+  Iterable<SnapshotObject> get children;
+
+  /// An iterable containing only this object. For polymorphism with
+  /// SnapshotMergedDominators.
+  Iterable<SnapshotObject> get objects;
+}
+
+class _SnapshotObject implements SnapshotObject {
+  final int _id;
+  final _SnapshotGraph _graph;
+  final String label;
+
+  _SnapshotObject._new(this._id, this._graph, this.label);
+
+  bool operator ==(Object other) {
+    if (other is _SnapshotObject) {
+      return _id == other._id && _graph == other._graph;
+    }
+    return false;
+  }
+
+  int get hashCode => _id ^ _graph.hashCode;
+
+  int get shallowSize => internalSize + externalSize;
+  int get internalSize => _graph._internalSizes[_id];
+  int get externalSize => _graph._externalSizes[_id];
+  int get retainedSize => _graph._retainedSizes[_id];
+
+  String get description => _graph._describeObject(_id);
+  SnapshotClass get klass => _graph._classes[_graph._cids[_id]];
+
+  Iterable<SnapshotObject> get successors sync* {
+    final id = _id;
+    final cid = _graph._cids[id];
+    final startSuccIndex = _graph._firstSuccs[id];
+    final limitSuccIndex = _graph._firstSuccs[id + 1];
+    for (var nextSuccIndex = startSuccIndex;
+        nextSuccIndex < limitSuccIndex;
+        nextSuccIndex++) {
+      final index = nextSuccIndex - startSuccIndex;
+      final succId = _graph._succs[nextSuccIndex];
+      final name = _graph._edgeName(cid, index);
+      yield _SnapshotObject._new(succId, _graph, name);
+    }
+  }
+
+  Iterable<SnapshotObject> get predecessors sync* {
+    var firstSuccs = _graph._firstSuccs;
+    var succs = _graph._succs;
+    var id = _id;
+    var N = _graph._N;
+    for (var predId = 1; predId <= N; predId++) {
+      var base = firstSuccs[predId];
+      var limit = firstSuccs[predId + 1];
+      for (var i = base; i < limit; i++) {
+        if (succs[i] == id) {
+          var cid = _graph._cids[predId];
+          var name = _graph._edgeName(cid, i - base);
+          yield _SnapshotObject._new(predId, _graph, name);
+        }
+      }
+    }
+  }
+
+  SnapshotObject get parent {
+    if (_id == _ROOT) {
+      return this;
+    }
+    return _SnapshotObject._new(_graph._doms[_id], _graph, "");
+  }
+
+  Iterable<SnapshotObject> get children sync* {
+    var N = _graph._N;
+    var doms = _graph._doms;
+    var parentId = _id;
+    for (var childId = _ROOT; childId <= N; childId++) {
+      if (doms[childId] == parentId) {
+        yield _SnapshotObject._new(childId, _graph, "");
+      }
+    }
+  }
+
+  Iterable<SnapshotObject> get objects sync* {
+    yield this;
+  }
+}
+
+class _SyntheticSnapshotObject implements SnapshotObject {
+  String _description;
+  SnapshotClass _klass;
+  int _internalSize;
+  int _externalSize;
+  int _retainedSize;
+  List<SnapshotObject> _successors;
+  List<SnapshotObject> _predecessors;
+  SnapshotObject _parent;
+  List<SnapshotObject> _children;
+
+  String get label => null;
+  String get description => _description;
+  SnapshotClass get klass => _klass;
+
+  int get shallowSize => internalSize + externalSize;
+  int get internalSize => _internalSize;
+  int get externalSize => _externalSize;
+  int get retainedSize => _retainedSize;
+
+  Iterable<SnapshotObject> get successors => _successors;
+  Iterable<SnapshotObject> get predecessors => _predecessors;
+  SnapshotObject get parent => _parent;
+  Iterable<SnapshotObject> get children => _children;
+
+  Iterable<SnapshotObject> get objects sync* {
+    yield this;
+  }
+}
+
+/// A set of sibling objects in the graph's dominator tree that have the same
+/// class.
+abstract class SnapshotMergedDominator {
+  SnapshotClass get klass;
+
+  /// "n instances of Class".
+  String get description;
+
+  /// [internalSize] + [externalSize].
+  int get shallowSize;
+
+  /// The sum of [internalSize] for all objects in this set.
+  int get internalSize;
+
+  /// The sum of [externalSize] for all objects in this set.
+  int get externalSize;
+
+  /// The sum of [externalSize] for all objects in this set.
+  /// This is the amount of memory that would be freed if all references to
+  /// objects in this set were erased.
+  int get retainedSize;
+
+  /// The number of objects in this set. Polymorphic with
+  /// [SnapshotClass.instanceCount].
+  int get instanceCount;
+
+  SnapshotMergedDominator get parent;
+  Iterable<SnapshotMergedDominator> get children;
+
+  Iterable<SnapshotObject> get objects;
+}
+
+// A node in the dominator tree where siblings with the same class are merged.
+// That is, a set of objects with the same cid whose parent chains in the
+// dominator tree have the same cids at each level. [id_] is the representative
+// object of this set. The other members of the set are found by walking the
+// mergedDomNext links until finding the sentinel node or a node with a
+// different class.
+class _SnapshotMergedDominator implements SnapshotMergedDominator {
+  final int _id;
+  final _SnapshotGraph _graph;
+  final _SnapshotMergedDominator _parent;
+
+  _SnapshotMergedDominator._new(this._id, this._graph, this._parent);
+
+  bool operator ==(Object other) {
+    if (other is _SnapshotMergedDominator) {
+      return _id == other._id && _graph == other._graph;
+    }
+    return false;
+  }
+
+  int get hashCode => _id ^ _graph.hashCode;
+
+  String get description {
+    return _id == _ROOT
+        ? "Live Objects + External"
+        : "$instanceCount instances of ${klass.name}";
+  }
+
+  SnapshotClass get klass => _graph._classes[_graph._cids[_id]];
+
+  int get shallowSize => internalSize + externalSize;
+
+  int get internalSize {
+    var cids = _graph._cids;
+    var size = 0;
+    var sibling = _id;
+    while (sibling != _SENTINEL && cids[sibling] == cids[_id]) {
+      size += _graph._internalSizes[sibling];
+      sibling = _graph._mergedDomNext[sibling];
+    }
+    return size;
+  }
+
+  int get externalSize {
+    var cids = _graph._cids;
+    var size = 0;
+    var sibling = _id;
+    while (sibling != _SENTINEL && cids[sibling] == cids[_id]) {
+      size += _graph._externalSizes[sibling];
+      sibling = _graph._mergedDomNext[sibling];
+    }
+    return size;
+  }
+
+  int get retainedSize {
+    var cids = _graph._cids;
+    var size = 0;
+    var sibling = _id;
+    while (sibling != _SENTINEL && cids[sibling] == cids[_id]) {
+      size += _graph._retainedSizes[sibling];
+      sibling = _graph._mergedDomNext[sibling];
+    }
+    return size;
+  }
+
+  int get instanceCount {
+    var cids = _graph._cids;
+    var count = 0;
+    var sibling = _id;
+    while (sibling != _SENTINEL && cids[sibling] == cids[_id]) {
+      count++;
+      sibling = _graph._mergedDomNext[sibling];
+    }
+    return count;
+  }
+
+  Iterable<SnapshotObject> get objects sync* {
+    var cids = _graph._cids;
+    var sibling = _id;
+    while (sibling != _SENTINEL && cids[sibling] == cids[_id]) {
+      yield _SnapshotObject._new(sibling, _graph, "");
+      sibling = _graph._mergedDomNext[sibling];
+    }
+  }
+
+  SnapshotMergedDominator get parent => _parent ?? this;
+
+  Iterable<SnapshotMergedDominator> get children sync* {
+    var next = _graph._mergedDomNext;
+    var cids = _graph._cids;
+    var prev = _SENTINEL;
+    var child = _graph._mergedDomHead[_id];
+    // Walk the list of children and look for the representative objects, i.e.
+    // the first sibling of each cid.
+    while (child != _SENTINEL) {
+      if (prev == _SENTINEL || cids[prev] != cids[child]) {
+        yield _SnapshotMergedDominator._new(child, _graph, this);
+      }
+      prev = child;
+      child = next[child];
+    }
+  }
+}
+
+class _SyntheticSnapshotMergedDominator implements SnapshotMergedDominator {
+  String _description;
+  SnapshotClass _klass;
+  int _internalSize;
+  int _externalSize;
+  int _retainedSize;
+  List<SnapshotObject> _objects;
+  SnapshotMergedDominator _parent;
+  List<SnapshotMergedDominator> _children;
+
+  SnapshotClass get klass => _klass;
+  String get description => _description;
+  int get shallowSize => internalSize + externalSize;
+  int get internalSize => _internalSize;
+  int get externalSize => _externalSize;
+  int get retainedSize => _retainedSize;
+  int get instanceCount => _objects.length;
+  SnapshotMergedDominator get parent => _parent;
+  Iterable<SnapshotMergedDominator> get children => _children;
+  Iterable<SnapshotObject> get objects => _objects;
+}
+
+/// A class in a heap snapshot.
+abstract class SnapshotClass {
+  String get name;
+  String get qualifiedName;
+
+  int get shallowSize;
+  int get externalSize;
+  int get internalSize;
+  int get ownedSize;
+
+  int get instanceCount;
+  Iterable<SnapshotObject> get instances;
+}
+
+class _SnapshotClass implements SnapshotClass {
+  final _SnapshotGraph _graph;
+  final int _cid;
+  final String name;
+  String get qualifiedName => "$libUri $name";
+  final String libName;
+  final String libUri;
+  final Map<int, String> fields = new Map<int, String>();
+
+  int totalExternalSize = 0;
+  int totalInternalSize = 0;
+  int totalInstanceCount = 0;
+
+  int ownedSize = 0;
+
+  int liveExternalSize = 0;
+  int liveInternalSize = 0;
+  int liveInstanceCount = 0;
+
+  int get shallowSize => internalSize + externalSize;
+  int get internalSize => liveInternalSize;
+  int get externalSize => liveExternalSize;
+  int get instanceCount => liveInstanceCount;
+
+  Iterable<SnapshotObject> get instances sync* {
+    final N = _graph._N;
+    for (var id = 1; id <= N; id++) {
+      if (_graph._cids[id] == _cid && _graph._retainedSizes[id] > 0) {
+        yield _SnapshotObject._new(id, _graph, "");
+      }
+    }
+  }
+
+  _SnapshotClass._new(
+      this._graph, this._cid, this.name, this.libName, this.libUri);
+}
+
+/// The analyzed graph from a heap snapshot.
+abstract class SnapshotGraph {
+  String get description;
+
+  int get internalSize;
+  int get externalSize;
+  // [internalSize] + [externalSize]
+  int get size;
+
+  // The amount of memory reserved for the heap. [internalSize] will always be
+  // less than or equal to [capacity].
+  int get capacity;
+
+  Iterable<SnapshotClass> get classes;
+  Iterable<SnapshotObject> get objects;
+
+  SnapshotObject get root;
+  SnapshotObject get extendedRoot;
+  SnapshotMergedDominator get mergedRoot;
+  SnapshotMergedDominator get extendedMergedRoot;
+
+  // TODO: Insist that the client remember the chunks if needed? Always keeping
+  // this increasing the peak memory usage during analysis.
+  List<Uint8List> get chunks;
+}
+
+const _tagNone = 0;
+const _tagNull = 1;
+const _tagBool = 2;
+const _tagInt = 3;
+const _tagDouble = 4;
+const _tagLatin1 = 5;
+const _tagUtf16 = 6;
+const _tagLength = 7;
+const _tagName = 8;
+
+const _kSentinelName = "<omitted-object>";
+const _kRootName = "Live Objects + External";
+const _kUnknownFieldName = "<unknown>";
+
+class _SnapshotGraph implements SnapshotGraph {
+  List<Uint8List> _chunks;
+  List<Uint8List> get chunks => _chunks;
+
+  _SnapshotGraph._new();
+
+  String get description => _description;
+
+  int get size => _liveInternalSize + _liveExternalSize;
+  int get internalSize => _liveInternalSize;
+  int get externalSize => _liveExternalSize;
+  int get capacity => _capacity;
+
+  SnapshotObject get root => _SnapshotObject._new(_ROOT, this, "Root");
+  SnapshotMergedDominator get mergedRoot =>
+      _SnapshotMergedDominator._new(_ROOT, this, null);
+
+  SnapshotObject _extendedRoot;
+  SnapshotObject get extendedRoot {
+    if (_extendedRoot == null) {
+      _createExtended();
+    }
+    return _extendedRoot;
+  }
+
+  SnapshotMergedDominator _extendedMergedRoot;
+  SnapshotMergedDominator get extendedMergedRoot {
+    if (_extendedMergedRoot == null) {
+      _createExtended();
+    }
+    return _extendedMergedRoot;
+  }
+
+  void _createExtended() {
+    var capacity = new _SyntheticSnapshotObject();
+    var uncollected = new _SyntheticSnapshotObject();
+    var fragmentation = new _SyntheticSnapshotObject();
+    var live = root;
+    var mcapacity = new _SyntheticSnapshotMergedDominator();
+    var muncollected = new _SyntheticSnapshotMergedDominator();
+    var mfragmentation = new _SyntheticSnapshotMergedDominator();
+    var mlive = mergedRoot;
+
+    capacity._description = "Capacity + External";
+    capacity._klass = live.klass;
+    capacity._internalSize = _capacity;
+    capacity._externalSize = _totalExternalSize;
+    capacity._retainedSize = capacity._internalSize + capacity._externalSize;
+    capacity._successors = <SnapshotObject>[live, uncollected, fragmentation];
+    capacity._predecessors = <SnapshotObject>[];
+    capacity._children = <SnapshotObject>[live, uncollected, fragmentation];
+
+    mcapacity._description = "Capacity + External";
+    mcapacity._klass = mlive.klass;
+    mcapacity._internalSize = _capacity;
+    mcapacity._externalSize = _totalExternalSize;
+    mcapacity._retainedSize = mcapacity._internalSize + mcapacity._externalSize;
+    mcapacity._children = <SnapshotMergedDominator>[
+      mlive,
+      muncollected,
+      mfragmentation
+    ];
+    mcapacity._objects = <SnapshotObject>[capacity];
+
+    uncollected._description = "Uncollected Garbage";
+    uncollected._klass = live.klass;
+    uncollected._internalSize = _totalInternalSize - _liveInternalSize;
+    uncollected._externalSize = _totalExternalSize - _liveExternalSize;
+    uncollected._retainedSize =
+        uncollected._internalSize + uncollected._externalSize;
+    uncollected._successors = <SnapshotObject>[];
+    uncollected._predecessors = <SnapshotObject>[capacity];
+    uncollected._parent = capacity;
+    uncollected._children = <SnapshotObject>[];
+
+    muncollected._description = "Uncollected Garbage";
+    muncollected._klass = mlive.klass;
+    muncollected._internalSize = _totalInternalSize - _liveInternalSize;
+    muncollected._externalSize = _totalExternalSize - _liveExternalSize;
+    muncollected._retainedSize =
+        muncollected._internalSize + muncollected._externalSize;
+    muncollected._parent = mcapacity;
+    muncollected._children = <SnapshotMergedDominator>[];
+    muncollected._objects = <SnapshotObject>[uncollected];
+
+    fragmentation._description = "Free";
+    fragmentation._klass = live.klass;
+    fragmentation._internalSize = _capacity - _totalInternalSize;
+    fragmentation._externalSize = 0;
+    fragmentation._retainedSize = fragmentation._internalSize;
+    fragmentation._successors = <SnapshotObject>[];
+    fragmentation._predecessors = <SnapshotObject>[capacity];
+    fragmentation._parent = capacity;
+    fragmentation._children = <SnapshotObject>[];
+
+    mfragmentation._description = "Free";
+    mfragmentation._klass = mlive.klass;
+    mfragmentation._internalSize = _capacity - _totalInternalSize;
+    mfragmentation._externalSize = 0;
+    mfragmentation._retainedSize = mfragmentation._internalSize;
+    mfragmentation._parent = mcapacity;
+    mfragmentation._children = <SnapshotMergedDominator>[];
+    mfragmentation._objects = <SnapshotObject>[fragmentation];
+
+    _extendedRoot = capacity;
+    _extendedMergedRoot = mcapacity;
+  }
+
+  Iterable<SnapshotObject> get objects sync* {
+    final N = _N;
+    for (var id = 1; id <= N; id++) {
+      if (_retainedSizes[id] > 0) {
+        yield _SnapshotObject._new(id, this, "");
+      }
+    }
+  }
+
+  String _describeObject(int oid) {
+    if (oid == _SENTINEL) {
+      return _kSentinelName;
+    }
+    if (oid == _ROOT) {
+      return _kRootName;
+    }
+    var cls = _className(oid);
+    var data = _nonReferenceData[oid];
+    if (data == null) {
+      return cls;
+    } else {
+      return "$cls($data)";
+    }
+  }
+
+  String _className(int oid) {
+    var cid = _cids[oid];
+    var cls = _classes[cid];
+    if (cls == null) {
+      return "Class$cid";
+    }
+    return cls.name;
+  }
+
+  String _edgeName(int cid, int index) {
+    var c = _classes[cid];
+    if (c == null) {
+      return _kUnknownFieldName;
+    }
+    var n = c.fields[index];
+    if (n == null) {
+      return _kUnknownFieldName;
+    }
+    return n;
+  }
+
+  Iterable<SnapshotClass> get classes sync* {
+    for (final c in _classes) {
+      // Not all CIDs are occupied.
+      if (c != null) {
+        yield c;
+      }
+    }
+  }
+
+  Future<SnapshotGraph> _load(
+      List<Uint8List> chunks, StreamController<String> onProgress) async {
+    _chunks = chunks;
+    var stream = _ReadStream._new(chunks);
+    chunks = null;
+
+    // The phases of loading are placed in explicit `new Future(compuation)` so
+    // they will be deferred to the message loop. Ordinary async-await will only
+    // defer to the microtask loop.
+
+    onProgress.add("Loading classes...");
+    await new Future(() => _readClasses(stream));
+
+    onProgress.add("Loading objects...");
+    await new Future(() => _readObjects(stream));
+
+    onProgress.add("Loading external properties...");
+    await new Future(() => _readExternalProperties(stream));
+
+    stream = null;
+
+    onProgress.add("Compute class table...");
+    await new Future(() => _computeClassTable());
+
+    onProgress.add("Finding depth-first order...");
+    await new Future(() => _dfs());
+
+    onProgress.add("Finding predecessors...");
+    await new Future(() => _buildPredecessors());
+
+    onProgress.add("Finding dominators...");
+    await new Future(() => _buildDominators());
+
+    _semi = null;
+    _parent = null;
+
+    onProgress.add("Finding in-degree(1) groups...");
+    await new Future(() => _buildOwnedSizes());
+
+    _firstPreds = null;
+    _preds = null;
+
+    onProgress.add("Finding retained sizes...");
+    await new Future(() => _calculateRetainedSizes());
+
+    _vertex = null;
+
+    onProgress.add("Linking dominator tree children...");
+    await new Future(() => _linkDominatorChildren());
+
+    onProgress.add("Sorting dominator tree children...");
+    await new Future(() => _sortDominatorChildren());
+
+    onProgress.add("Merging dominator tree siblings...");
+    await new Future(() => _mergeDominatorSiblings());
+
+    onProgress.add("Loaded");
+    // We await here so SnapshotReader clients see all progress events before
+    // seeing the done future as completed.
+    await onProgress.close();
+
+    return this;
+  }
+
+  Uint8List _encoded;
+
+  String _description;
+
+  int _kStackCid;
+  int _kFieldCid;
+  int _numCids;
+  int _N; // Objects in the snapshot.
+  int _Nconnected; // Objects reachable from root.
+  int _E; // References in the snapshot.
+
+  int _capacity;
+  int _liveInternalSize;
+  int _liveExternalSize;
+  int _totalInternalSize;
+  int _totalExternalSize;
+
+  List<_SnapshotClass> _classes;
+
+  // Indexed by node id, with id 0 representing invalid/uninitialized.
+  // From snapshot.
+  List _nonReferenceData;
+  Uint16List _cids;
+  Uint32List _internalSizes;
+  Uint32List _externalSizes;
+  Uint32List _firstSuccs;
+  Uint32List _succs;
+
+  // Intermediates.
+  Uint32List _vertex;
+  Uint32List _parent;
+  Uint32List _semi;
+  Uint32List _firstPreds; // Offset into preds.
+  Uint32List _preds;
+
+  // Outputs.
+  Uint32List _doms;
+  Uint32List _retainedSizes;
+  Uint32List _mergedDomHead;
+  Uint32List _mergedDomNext;
+
+  void _readClasses(_ReadStream stream) {
+    for (var i = 0; i < 8; i++) {
+      stream.readByte(); // Magic value.
+    }
+    stream.readUnsigned(); // Flags
+    _description = stream.readUtf8();
+
+    _totalInternalSize = stream.readUnsigned();
+    _capacity = stream.readUnsigned();
+    _totalExternalSize = stream.readUnsigned();
+
+    var K = stream.readUnsigned();
+    var classes = new List<_SnapshotClass>.filled(K + 1, null);
+    classes[0] = _SnapshotClass._new(this, 0, "Root", "", "");
+
+    for (var cid = 1; cid <= K; cid++) {
+      int flags = stream.readUnsigned();
+      String name = stream.readUtf8();
+      String libName = stream.readUtf8();
+      String libUri = stream.readUtf8();
+      String reserved = stream.readUtf8();
+      final cls = _SnapshotClass._new(this, cid, name, libName, libUri);
+      int edgeCount = stream.readUnsigned();
+      for (int i = 0; i < edgeCount; i++) {
+        int flags = stream.readUnsigned();
+        int index = stream.readUnsigned();
+        String fieldName = stream.readUtf8();
+        String reserved = stream.readUtf8();
+        cls.fields[index] = fieldName;
+      }
+      classes[cid] = cls;
+    }
+
+    _numCids = K;
+    _classes = classes;
+  }
+
+  void _readObjects(_ReadStream stream) {
+    final E = stream.readUnsigned();
+    final N = stream.readUnsigned();
+
+    // The negative check accounts for int64 overflow in readUnsigned.
+    const maxUint32 = 0xFFFFFFFF;
+    if (N < 0 || N + 2 >= maxUint32) {
+      throw new Exception("Snapshot contains too many objects: $N");
+    }
+    if (E < 0 || E + 2 >= maxUint32) {
+      throw new Exception("Snapshot contains too many references: $E");
+    }
+
+    _N = N;
+    _E = E;
+
+    var internalSizes = _newUint32Array(N + 1);
+    var cids = _newUint16Array(N + 1);
+    var nonReferenceData = new List(N + 1);
+    var firstSuccs = _newUint32Array(N + 2);
+    var succs = _newUint32Array(E);
+    var eid = 0;
+    for (var oid = 1; oid <= N; oid++) {
+      var cid = stream.readUnsigned();
+      cids[oid] = cid;
+
+      var internalSize = stream.readUnsigned();
+      internalSizes[oid] = internalSize;
+
+      var nonReferenceDataTag = stream.readUnsigned();
+      switch (nonReferenceDataTag) {
+        case _tagNone:
+          break;
+        case _tagNull:
+          nonReferenceData[oid] = "null";
+          break;
+        case _tagBool:
+          nonReferenceData[oid] = stream.readByte() != 0;
+          break;
+        case _tagInt:
+          nonReferenceData[oid] = stream.readSigned();
+          break;
+        case _tagDouble:
+          nonReferenceData[oid] = stream.readFloat64();
+          break;
+        case _tagLatin1:
+          var len = stream.readUnsigned();
+          var str = stream.readLatin1();
+          if (str.length < len) {
+            nonReferenceData[oid] = '$str...';
+          } else {
+            nonReferenceData[oid] = str;
+          }
+          break;
+        case _tagUtf16:
+          int len = stream.readUnsigned();
+          var str = stream.readUtf16();
+          if (str.length < len) {
+            nonReferenceData[oid] = '$str...';
+          } else {
+            nonReferenceData[oid] = str;
+          }
+          break;
+        case _tagLength:
+          nonReferenceData[oid] = stream.readUnsigned(); // Length
+          break;
+        case _tagName:
+          nonReferenceData[oid] = stream.readUtf8(); // Name
+          break;
+        default:
+          throw "Unknown tag $nonReferenceDataTag";
+      }
+
+      firstSuccs[oid] = eid;
+      var referenceCount = stream.readUnsigned();
+      while (referenceCount > 0) {
+        var childOid = stream.readUnsigned();
+        succs[eid] = childOid;
+        eid++;
+        referenceCount--;
+      }
+    }
+    firstSuccs[N + 1] = eid;
+
+    assert(eid <= E);
+    _E = eid;
+    _internalSizes = internalSizes;
+    _cids = cids;
+    _nonReferenceData = nonReferenceData;
+    _firstSuccs = firstSuccs;
+    _succs = succs;
+  }
+
+  void _readExternalProperties(_ReadStream stream) {
+    final N = _N;
+    final externalPropertyCount = stream.readUnsigned();
+
+    final externalSizes = _newUint32Array(N + 1);
+    for (var i = 0; i < externalPropertyCount; i++) {
+      final oid = stream.readUnsigned();
+      final externalSize = stream.readUnsigned();
+      final name = stream.readUtf8();
+      externalSizes[oid] += externalSize;
+    }
+
+    _externalSizes = externalSizes;
+  }
+
+  void _computeClassTable() {
+    final N = _N;
+    final classes = _classes;
+    final cids = _cids;
+    final internalSizes = _internalSizes;
+    final externalSizes = _externalSizes;
+    var totalInternalSize = 0;
+    var totalExternalSize = 0;
+
+    for (var oid = 1; oid <= N; oid++) {
+      var internalSize = internalSizes[oid];
+      totalInternalSize += internalSize;
+
+      var externalSize = externalSizes[oid];
+      totalExternalSize += externalSize;
+
+      var cls = classes[cids[oid]];
+      cls.totalInternalSize += internalSize;
+      cls.totalExternalSize += externalSize;
+      cls.totalInstanceCount++;
+    }
+
+    _totalInternalSize = totalInternalSize;
+    _totalExternalSize = totalExternalSize;
+  }
+
+  void _dfs() {
+    final N = _N;
+    final firstSuccs = _firstSuccs;
+    final succs = _succs;
+
+    final stackNodes = _newUint32Array(N);
+    final stackCurrentEdgePos = _newUint32Array(N);
+
+    final vertex = _newUint32Array(N + 1);
+    final semi = _newUint32Array(N + 1);
+    final parent = _newUint32Array(N + 1);
+    var dfsNumber = 0;
+
+    var stackTop = 0;
+
+    // Push root.
+    stackNodes[0] = _ROOT;
+    stackCurrentEdgePos[0] = firstSuccs[_ROOT];
+
+    while (stackTop >= 0) {
+      var v = stackNodes[stackTop];
+      var edgePos = stackCurrentEdgePos[stackTop];
+
+      if (semi[v] == 0) {
+        // First visit.
+        dfsNumber++;
+        semi[v] = dfsNumber;
+        vertex[dfsNumber] = v;
+      }
+
+      if (edgePos < firstSuccs[v + 1]) {
+        var childId = succs[edgePos];
+        edgePos++;
+        stackCurrentEdgePos[stackTop] = edgePos;
+
+        if (childId == _SENTINEL) {
+          // Omitted target.
+        } else if (semi[childId] == 0) {
+          parent[childId] = v;
+
+          // Push child.
+          stackTop++;
+          stackNodes[stackTop] = childId;
+          stackCurrentEdgePos[stackTop] = firstSuccs[childId];
+        }
+      } else {
+        // Done with all children.
+        stackTop--;
+      }
+    }
+
+    if (dfsNumber != N) {
+      // This may happen in filtered snapshots.
+      print('Heap snapshot contains ${N - dfsNumber} unreachable nodes.');
+    }
+
+    assert(() {
+      for (var i = 1; i <= dfsNumber; i++) {
+        var v = vertex[i];
+        assert(semi[v] != _SENTINEL);
+      }
+      assert(parent[1] == _SENTINEL);
+      for (var i = 2; i <= dfsNumber; i++) {
+        var v = vertex[i];
+        assert(parent[v] != _SENTINEL);
+      }
+      return true;
+    }());
+
+    if (dfsNumber != N) {
+      // Remove successors of unconnected nodes
+      for (var i = _ROOT + 1; i <= N; i++) {
+        if (parent[i] == _SENTINEL) {
+          var startSuccIndex = firstSuccs[i];
+          var limitSuccIndex = firstSuccs[i + 1];
+          for (var succIndex = startSuccIndex;
+              succIndex < limitSuccIndex;
+              succIndex++) {
+            succs[succIndex] = _SENTINEL;
+          }
+        }
+      }
+    }
+
+    _Nconnected = dfsNumber;
+    _vertex = vertex;
+    _semi = semi;
+    _parent = parent;
+  }
+
+  void _buildPredecessors() {
+    final N = _N;
+    final Nconnected = _Nconnected;
+    final E = _E;
+    final firstSuccs = _firstSuccs;
+    final succs = _succs;
+
+    // This is first filled with the predecessor counts, then reused to hold the
+    // offset to the first predecessor (see alias below).
+    // + 1 because 0 is a sentinel
+    // + 1 so the number of predecessors can be found from the difference with
+    // the next node's offset.
+    final numPreds = _newUint32Array(N + 2);
+    final preds = _newUint32Array(E);
+
+    // Count predecessors of each node.
+    for (var succIndex = 0; succIndex < E; succIndex++) {
+      final succId = succs[succIndex];
+      if (succId != _SENTINEL) {
+        numPreds[succId]++;
+      }
+    }
+
+    // Assign indices into predecessors array.
+    final firstPreds = numPreds; // Alias.
+    final nextPreds = _newUint32Array(N + 1);
+    var predIndex = 0;
+    for (var i = 1; i <= N; i++) {
+      var thisPredIndex = predIndex;
+      predIndex += numPreds[i];
+      firstPreds[i] = thisPredIndex;
+      nextPreds[i] = thisPredIndex;
+    }
+    if (N == Nconnected) {
+      assert(predIndex == E);
+    }
+    firstPreds[N + 1] = predIndex; // Extra entry for cheap boundary detection.
+
+    // Fill predecessors array.
+    for (var i = 1; i <= N; i++) {
+      var startSuccIndex = firstSuccs[i];
+      var limitSuccIndex = firstSuccs[i + 1];
+      for (var succIndex = startSuccIndex;
+          succIndex < limitSuccIndex;
+          succIndex++) {
+        var succId = succs[succIndex];
+        if (succId != _SENTINEL) {
+          var predIndex = nextPreds[succId]++;
+          preds[predIndex] = i;
+        }
+      }
+    }
+
+    _firstPreds = firstPreds;
+    _preds = preds;
+  }
+
+  // Fold the size of any object with in-degree(1) into its parent.
+  // Requires the DFS numbering and predecessor lists.
+  void _buildOwnedSizes() {
+    final N = _N;
+    final Nconnected = _Nconnected;
+    final kStackCid = _kStackCid;
+    final kFieldCid = _kFieldCid;
+
+    final cids = _cids;
+    final internalSizes = _internalSizes;
+    final externalSizes = _externalSizes;
+    final vertex = _vertex;
+    final firstPreds = _firstPreds;
+    final preds = _preds;
+
+    final ownedSizes = _newUint32Array(N + 1);
+    for (var i = 1; i <= Nconnected; i++) {
+      final v = vertex[i];
+      ownedSizes[v] = internalSizes[v] + externalSizes[v];
+    }
+
+    for (var i = Nconnected; i > 1; i--) {
+      var w = vertex[i];
+      assert(w != _ROOT);
+
+      var onlyPred = _SENTINEL;
+
+      var startPred = firstPreds[w];
+      var limitPred = firstPreds[w + 1];
+      for (var predIndex = startPred; predIndex < limitPred; predIndex++) {
+        var v = preds[predIndex];
+        if (v == w) {
+          // Ignore self-predecessor.
+        } else if (onlyPred == _SENTINEL) {
+          onlyPred = v;
+        } else if (onlyPred == v) {
+          // Repeated predecessor.
+        } else {
+          // Multiple-predecessors.
+          onlyPred = _SENTINEL;
+          break;
+        }
+      }
+
+      // If this object has a single precessor which is not a Field, Stack or
+      // the root, blame its size against the precessor.
+      if ((onlyPred != _SENTINEL) &&
+          (onlyPred != _ROOT) &&
+          (cids[onlyPred] != kStackCid) &&
+          (cids[onlyPred] != kFieldCid)) {
+        assert(onlyPred != w);
+        ownedSizes[onlyPred] += ownedSizes[w];
+        ownedSizes[w] = 0;
+      }
+    }
+
+    // TODO(rmacnak): Maybe keep the per-objects sizes to be able to provide
+    // examples of large owners for each class.
+    final classes = _classes;
+    for (var i = 1; i <= Nconnected; i++) {
+      final v = vertex[i];
+      final cid = cids[v];
+      final cls = classes[cid];
+      cls.ownedSize += ownedSizes[v];
+    }
+  }
+
+  static int _eval(int v, Uint32List ancestor, Uint32List semi,
+      Uint32List label, Uint32List stackNode, Uint8List stackState) {
+    if (ancestor[v] == _SENTINEL) {
+      return label[v];
+    } else {
+      {
+        // Inlined 'compress' with an explicit stack to prevent JS stack
+        // overflow.
+        var top = 0;
+        stackNode[top] = v;
+        stackState[top] = 0;
+        while (top >= 0) {
+          var v = stackNode[top];
+          var state = stackState[top];
+          if (state == 0) {
+            assert(ancestor[v] != 0);
+            if (ancestor[ancestor[v]] != 0) {
+              stackState[top] = 1;
+              // Recurse with ancestor[v]
+              top++;
+              stackNode[top] = ancestor[v];
+              stackState[top] = 0;
+            } else {
+              top--;
+            }
+          } else {
+            assert(state == 1);
+            if (semi[label[ancestor[v]]] < semi[label[v]]) {
+              label[v] = label[ancestor[v]];
+            }
+            ancestor[v] = ancestor[ancestor[v]];
+            top--;
+          }
+        }
+      }
+
+      if (semi[label[ancestor[v]]] >= semi[label[v]]) {
+        return label[v];
+      } else {
+        return label[ancestor[v]];
+      }
+    }
+  }
+
+  // Note the version in the main text of Lengauer & Tarjan incorrectly
+  // uses parent instead of ancestor. The correct version is in Appendix B.
+  static void _link(int v, int w, Uint32List size, Uint32List label,
+      Uint32List semi, Uint32List child, Uint32List ancestor) {
+    assert(size[0] == 0);
+    assert(label[0] == 0);
+    assert(semi[0] == 0);
+    var s = w;
+    while (semi[label[w]] < semi[label[child[s]]]) {
+      if (size[s] + size[child[child[s]]] >= 2 * size[child[s]]) {
+        ancestor[child[s]] = s;
+        child[s] = child[child[s]];
+      } else {
+        size[child[s]] = size[s];
+        s = ancestor[s] = child[s];
+      }
+    }
+    label[s] = label[w];
+    size[v] = size[v] + size[w];
+    if (size[v] < 2 * size[w]) {
+      var tmp = s;
+      s = child[v];
+      child[v] = tmp;
+    }
+    while (s != 0) {
+      ancestor[s] = v;
+      s = child[s];
+    }
+  }
+
+  // T. Lengauer and R. E. Tarjan. "A Fast Algorithm for Finding Dominators
+  // in a Flowgraph."
+  void _buildDominators() {
+    final N = _N;
+    final Nconnected = _Nconnected;
+
+    final vertex = _vertex;
+    final semi = _semi;
+    final parent = _parent;
+    final firstPreds = _firstPreds;
+    final preds = _preds;
+
+    final dom = _newUint32Array(N + 1);
+
+    final ancestor = _newUint32Array(N + 1);
+    final label = _newUint32Array(N + 1);
+    for (var i = 1; i <= N; i++) {
+      label[i] = i;
+    }
+    final buckets = new List(N + 1);
+    final child = _newUint32Array(N + 1);
+    final size = _newUint32Array(N + 1);
+    for (var i = 1; i <= N; i++) {
+      size[i] = 1;
+    }
+    final stackNode = _newUint32Array(N + 1);
+    final stackState = _newUint8Array(N + 1);
+
+    for (var i = Nconnected; i > 1; i--) {
+      var w = vertex[i];
+      assert(w != _ROOT);
+
+      // Lengauer & Tarjan Step 2.
+      var startPred = firstPreds[w];
+      var limitPred = firstPreds[w + 1];
+      for (var predIndex = startPred; predIndex < limitPred; predIndex++) {
+        var v = preds[predIndex];
+        var u = _eval(v, ancestor, semi, label, stackNode, stackState);
+        if (semi[u] < semi[w]) {
+          semi[w] = semi[u];
+        }
+      }
+
+      // w.semi.bucket.add(w);
+      var tmp = vertex[semi[w]];
+      if (buckets[tmp] == null) {
+        buckets[tmp] = new List();
+      }
+      buckets[tmp].add(w);
+
+      _link(parent[w], w, size, label, semi, child, ancestor);
+
+      // Lengauer & Tarjan Step 3.
+      tmp = parent[w];
+      var bucket = buckets[tmp];
+      buckets[tmp] = null;
+      if (bucket != null) {
+        for (var v in bucket) {
+          var u = _eval(v, ancestor, semi, label, stackNode, stackState);
+          dom[v] = semi[u] < semi[v] ? u : parent[w];
+        }
+      }
+    }
+    for (var i = _ROOT; i <= N; i++) {
+      assert(buckets[i] == null);
+    }
+    // Lengauer & Tarjan Step 4.
+    for (var i = 2; i <= Nconnected; i++) {
+      var w = vertex[i];
+      if (dom[w] != vertex[semi[w]]) {
+        dom[w] = dom[dom[w]];
+      }
+    }
+
+    _doms = dom;
+  }
+
+  void _calculateRetainedSizes() {
+    final N = _N;
+    final Nconnected = _Nconnected;
+
+    var liveInternalSize = 0;
+    var liveExternalSize = 0;
+    final classes = _classes;
+    final cids = _cids;
+    final internalSizes = _internalSizes;
+    final externalSizes = _externalSizes;
+    final vertex = _vertex;
+    final doms = _doms;
+
+    // Sum internal and external sizes.
+    for (var i = 1; i <= Nconnected; i++) {
+      var v = vertex[i];
+      var internalSize = internalSizes[v];
+      var externalSize = externalSizes[v];
+      liveInternalSize += internalSize;
+      liveExternalSize += externalSize;
+
+      var cls = classes[cids[v]];
+      cls.liveInternalSize += internalSize;
+      cls.liveExternalSize += externalSize;
+      cls.liveInstanceCount++;
+    }
+
+    // Start with retained size as shallow size + external size. For reachable
+    // objects only; leave unreachable objects with a retained size of 0 so
+    // they can be filtered during graph iterations.
+    var retainedSizes = new Uint32List(N + 1);
+    assert(Nconnected <= N);
+    for (var i = 0; i <= Nconnected; i++) {
+      var v = vertex[i];
+      retainedSizes[v] = internalSizes[v] + externalSizes[v];
+    }
+
+    // In post order (bottom up), add retained size to dominator's retained
+    // size, skipping root.
+    for (var i = Nconnected; i > 1; i--) {
+      var v = vertex[i];
+      assert(v != _ROOT);
+      retainedSizes[doms[v]] += retainedSizes[v];
+    }
+
+    // Root retains everything.
+    assert(retainedSizes[_ROOT] == (liveInternalSize + liveExternalSize));
+
+    _retainedSizes = retainedSizes;
+    _liveInternalSize = liveInternalSize;
+    _liveExternalSize = liveExternalSize;
+
+    print("internal-garbage: ${_totalInternalSize - _liveInternalSize}");
+    print("external-garbage: ${_totalExternalSize - _liveExternalSize}");
+    print("fragmentation: ${_capacity - _totalInternalSize}");
+    assert(_liveInternalSize <= _totalInternalSize);
+    assert(_liveExternalSize <= _totalExternalSize);
+    assert(_totalInternalSize <= _capacity);
+  }
+
+  // Build linked lists of the children for each node in the dominator tree.
+  void _linkDominatorChildren() {
+    final N = _N;
+    final doms = _doms;
+    final head = _newUint32Array(N + 1);
+    final next = _newUint32Array(N + 1);
+
+    for (var child = _ROOT; child <= N; child++) {
+      var parent = doms[child];
+      next[child] = head[parent];
+      head[parent] = child;
+    }
+
+    _mergedDomHead = head;
+    _mergedDomNext = next;
+  }
+
+  // Merge the given lists according to the given key in ascending order.
+  // Returns the head of the merged list.
+  static int _mergeSorted(
+      int head1, int head2, Uint32List next, Uint16List key) {
+    var head = head1;
+    var beforeInsert = _SENTINEL;
+    var afterInsert = head1;
+    var startInsert = head2;
+
+    while (startInsert != _SENTINEL) {
+      while ((afterInsert != _SENTINEL) &&
+          (key[afterInsert] <= key[startInsert])) {
+        beforeInsert = afterInsert;
+        afterInsert = next[beforeInsert];
+      }
+
+      var endInsert = startInsert;
+      var peek = next[endInsert];
+
+      while ((peek != _SENTINEL) && (key[peek] < key[afterInsert])) {
+        endInsert = peek;
+        peek = next[endInsert];
+      }
+      assert(endInsert != _SENTINEL);
+
+      if (beforeInsert == _SENTINEL) {
+        head = startInsert;
+      } else {
+        next[beforeInsert] = startInsert;
+      }
+      next[endInsert] = afterInsert;
+
+      startInsert = peek;
+      beforeInsert = endInsert;
+    }
+
+    return head;
+  }
+
+  void _sortDominatorChildren() {
+    final N = _N;
+    final cids = _cids;
+    final head = _mergedDomHead;
+    final next = _mergedDomNext;
+
+    // Returns the new head of the sorted list.
+    int sort(int head) {
+      if (head == _SENTINEL) return _SENTINEL;
+      if (next[head] == _SENTINEL) return head;
+
+      // Find the middle of the list.
+      int head1 = head;
+      int slow = head;
+      int fast = head;
+      while (next[fast] != _SENTINEL && next[next[fast]] != _SENTINEL) {
+        slow = next[slow];
+        fast = next[next[fast]];
+      }
+
+      // Split the list in half.
+      int head2 = next[slow];
+      next[slow] = _SENTINEL;
+
+      // Recursively sort the sublists and merge.
+      assert(head1 != head2);
+      int newHead1 = sort(head1);
+      int newHead2 = sort(head2);
+      return _mergeSorted(newHead1, newHead2, next, cids);
+    }
+
+    // Sort all list of dominator tree children by cid.
+    for (var parent = _ROOT; parent <= N; parent++) {
+      head[parent] = sort(head[parent]);
+    }
+  }
+
+  void _mergeDominatorSiblings() {
+    var N = _N;
+    var cids = _cids;
+    var head = _mergedDomHead;
+    var next = _mergedDomNext;
+    var workStack = _newUint32Array(N);
+    var workStackTop = 0;
+
+    mergeChildrenAndSort(var parent1, var end) {
+      assert(parent1 != _SENTINEL);
+      if (next[parent1] == end) return;
+
+      // Find the middle of the list.
+      int slow = parent1;
+      int fast = parent1;
+      while (next[fast] != end && next[next[fast]] != end) {
+        slow = next[slow];
+        fast = next[next[fast]];
+      }
+
+      int parent2 = next[slow];
+
+      assert(parent2 != _SENTINEL);
+      assert(parent1 != parent2);
+      assert(cids[parent1] == cids[parent2]);
+
+      // Recursively sort the sublists.
+      mergeChildrenAndSort(parent1, parent2);
+      mergeChildrenAndSort(parent2, end);
+
+      // Merge sorted sublists.
+      head[parent1] = _mergeSorted(head[parent1], head[parent2], next, cids);
+
+      // Children moved to parent1.
+      head[parent2] = _SENTINEL;
+    }
+
+    // Push root.
+    workStack[workStackTop++] = _ROOT;
+
+    while (workStackTop > 0) {
+      var parent = workStack[--workStackTop];
+
+      var child = head[parent];
+      while (child != _SENTINEL) {
+        // Push child.
+        workStack[workStackTop++] = child;
+
+        // Find next sibling with a different cid.
+        var after = child;
+        while (after != _SENTINEL && cids[after] == cids[child]) {
+          after = next[after];
+        }
+
+        // From all the siblings between child and after, take their children,
+        // merge them and given to child.
+        mergeChildrenAndSort(child, after);
+
+        child = after;
+      }
+    }
+  }
+}
diff --git a/runtime/observatory_2/lib/repositories.dart b/runtime/observatory_2/lib/repositories.dart
new file mode 100644
index 0000000..35579ef
--- /dev/null
+++ b/runtime/observatory_2/lib/repositories.dart
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, 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.
+
+library repositories;
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:html';
+import 'dart:typed_data';
+import 'package:observatory_2/allocation_profile.dart';
+import 'package:observatory_2/sample_profile.dart';
+import 'package:observatory_2/object_graph.dart';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/service.dart' as S;
+import 'package:observatory_2/service_common.dart' as SC;
+import 'package:observatory_2/src/repositories/timeline_base.dart';
+
+part 'src/repositories/allocation_profile.dart';
+part 'src/repositories/breakpoint.dart';
+part 'src/repositories/class.dart';
+part 'src/repositories/context.dart';
+part 'src/repositories/editor.dart';
+part 'src/repositories/eval.dart';
+part 'src/repositories/event.dart';
+part 'src/repositories/field.dart';
+part 'src/repositories/flag.dart';
+part 'src/repositories/function.dart';
+part 'src/repositories/heap_snapshot.dart';
+part 'src/repositories/icdata.dart';
+part 'src/repositories/inbound_references.dart';
+part 'src/repositories/instance.dart';
+part 'src/repositories/isolate.dart';
+part 'src/repositories/isolate_group.dart';
+part 'src/repositories/library.dart';
+part 'src/repositories/megamorphiccache.dart';
+part 'src/repositories/metric.dart';
+part 'src/repositories/notification.dart';
+part 'src/repositories/object.dart';
+part 'src/repositories/objectpool.dart';
+part 'src/repositories/objectstore.dart';
+part 'src/repositories/persistent_handles.dart';
+part 'src/repositories/ports.dart';
+part 'src/repositories/reachable_size.dart';
+part 'src/repositories/retained_size.dart';
+part 'src/repositories/retaining_path.dart';
+part 'src/repositories/sample_profile.dart';
+part 'src/repositories/script.dart';
+part 'src/repositories/settings.dart';
+part 'src/repositories/single_target_cache.dart';
+part 'src/repositories/strongly_reachable_instances.dart';
+part 'src/repositories/subtype_test_cache.dart';
+part 'src/repositories/target.dart';
+part 'src/repositories/timeline.dart';
+part 'src/repositories/type_arguments.dart';
+part 'src/repositories/unlinked_call.dart';
+part 'src/repositories/vm.dart';
diff --git a/runtime/observatory_2/lib/sample_profile.dart b/runtime/observatory_2/lib/sample_profile.dart
new file mode 100644
index 0000000..adae259
--- /dev/null
+++ b/runtime/observatory_2/lib/sample_profile.dart
@@ -0,0 +1,13 @@
+// Copyright (c) 2015, 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.
+
+library sample_profiler;
+
+import 'dart:async';
+import 'package:logging/logging.dart';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/service.dart';
+import 'package:observatory_2/utils.dart';
+
+part 'src/sample_profile/sample_profile.dart';
diff --git a/runtime/observatory_2/lib/service.dart b/runtime/observatory_2/lib/service.dart
new file mode 100644
index 0000000..8544b93a
--- /dev/null
+++ b/runtime/observatory_2/lib/service.dart
@@ -0,0 +1,20 @@
+// Copyright (c) 2014, 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.
+
+library service;
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:typed_data';
+import 'dart:math' as math;
+
+import 'package:logging/logging.dart';
+import 'package:observatory_2/event.dart' show createEventFromServiceEvent;
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/object_graph.dart';
+import 'package:observatory_2/sample_profile.dart';
+import 'package:observatory_2/service_common.dart';
+import 'package:observatory_2/tracer.dart';
+
+part 'src/service/object.dart';
diff --git a/runtime/observatory_2/lib/service_common.dart b/runtime/observatory_2/lib/service_common.dart
new file mode 100644
index 0000000..b61f4a0
--- /dev/null
+++ b/runtime/observatory_2/lib/service_common.dart
@@ -0,0 +1,339 @@
+// Copyright (c) 2014, 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.
+
+library service_common;
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:typed_data';
+
+import 'package:logging/logging.dart';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/service.dart';
+
+// Export the service library.
+export 'package:observatory_2/service.dart';
+
+/// Description of a VM target.
+class WebSocketVMTarget implements M.Target {
+  // Last time this VM has been connected to.
+  int lastConnectionTime = 0;
+  bool get hasEverConnected => lastConnectionTime > 0;
+
+  // Chrome VM or standalone;
+  bool chrome = false;
+  bool get standalone => !chrome;
+
+  // User defined name.
+  String name;
+  // Network address of VM.
+  String networkAddress;
+
+  WebSocketVMTarget(this.networkAddress) {
+    name = networkAddress;
+  }
+
+  WebSocketVMTarget.fromMap(Map json) {
+    lastConnectionTime = json['lastConnectionTime'];
+    chrome = json['chrome'];
+    name = json['name'];
+    networkAddress = json['networkAddress'];
+    if (name == null) {
+      name = networkAddress;
+    }
+  }
+
+  Map toJson() {
+    return {
+      'lastConnectionTime': lastConnectionTime,
+      'chrome': chrome,
+      'name': name,
+      'networkAddress': networkAddress,
+    };
+  }
+}
+
+class _WebSocketRequest {
+  final String method;
+  final Map params;
+  final Completer<Map> completer;
+
+  _WebSocketRequest(this.method, this.params)
+      : completer = new Completer<Map>();
+}
+
+/// Minimal common interface for 'WebSocket' in [dart:io] and [dart:html].
+abstract class CommonWebSocket {
+  Future<void> connect(WebSocketVMTarget target, void onOpen(),
+      void onMessage(dynamic data), void onError(), void onClose());
+  bool get isOpen;
+  void send(dynamic data);
+  void close();
+  Future<ByteData> nonStringToByteData(dynamic data);
+}
+
+/// A [CommonWebSocketVM] communicates with a Dart VM over a CommonWebSocket.
+/// The Dart VM can be embedded in Chromium or standalone.
+abstract class CommonWebSocketVM extends VM {
+  final Completer _connected = new Completer();
+  final Completer<String> _disconnected = new Completer<String>();
+  final WebSocketVMTarget target;
+  final Map<String, _WebSocketRequest> _delayedRequests =
+      new Map<String, _WebSocketRequest>();
+  final Map<String, _WebSocketRequest> _pendingRequests =
+      new Map<String, _WebSocketRequest>();
+  int _requestSerial = 0;
+  bool _hasInitiatedConnect = false;
+  Utf8Decoder _utf8Decoder = const Utf8Decoder();
+
+  String get displayName => '${name}@${target.name}';
+
+  CommonWebSocket _webSocket;
+
+  CommonWebSocketVM(this.target, this._webSocket) {
+    assert(target != null);
+  }
+
+  void _notifyConnect() {
+    if (!_connected.isCompleted) {
+      Logger.root
+          .info('WebSocketVM connection opened: ${target.networkAddress}');
+      _connected.complete(this);
+    }
+  }
+
+  Future get onConnect => _connected.future;
+  bool get wasOrIsConnected => _connected.isCompleted;
+  bool get isConnected => wasOrIsConnected && !isDisconnected;
+  void _notifyDisconnect(String reason) {
+    if (!_disconnected.isCompleted) {
+      Logger.root
+          .info('WebSocketVM connection error: ${target.networkAddress}');
+      _disconnected.complete(reason);
+    }
+  }
+
+  Future<String> get onDisconnect => _disconnected.future;
+  bool get isDisconnected => _disconnected.isCompleted;
+
+  void disconnect({String reason: 'WebSocket closed'}) {
+    if (_hasInitiatedConnect) {
+      if (_webSocket != null) {
+        _webSocket.close();
+      }
+    }
+    // We don't need to cancel requests and notify here.  These
+    // functions will be called again when the onClose callback
+    // fires. However, we may have a better 'reason' string now, so
+    // let's take care of business.
+    _cancelAllRequests(reason);
+    _notifyDisconnect(reason);
+  }
+
+  Future<Map> invokeRpcRaw(String method, Map params) async {
+    if (!_hasInitiatedConnect) {
+      _hasInitiatedConnect = true;
+      try {
+        await _webSocket.connect(
+            target, _onOpen, _onMessage, _onError, _onClose);
+      } catch (_, stack) {
+        _webSocket = null;
+        var exception = new NetworkRpcException('WebSocket closed');
+        return new Future.error(exception, stack);
+      }
+    }
+    if (_disconnected.isCompleted) {
+      // This connection was closed already.
+      var exception = new NetworkRpcException(await onDisconnect);
+      return new Future.error(exception);
+    }
+    String serial = (_requestSerial++).toString();
+    var request = new _WebSocketRequest(method, params);
+    if ((_webSocket != null) && _webSocket.isOpen) {
+      // Already connected, send request immediately.
+      _sendRequest(serial, request);
+    } else {
+      // Not connected yet, add to delayed requests.
+      _delayedRequests[serial] = request;
+    }
+    return request.completer.future;
+  }
+
+  void _onClose() {
+    _cancelAllRequests('WebSocket closed');
+    _notifyDisconnect('WebSocket closed');
+  }
+
+  // WebSocket error event handler.
+  void _onError() {
+    // TODO(turnidge): The implementors of CommonWebSocket have more
+    // error information available.  Consider providing that here.
+    _cancelAllRequests('WebSocket closed due to error');
+    _notifyDisconnect('WebSocket closed due to error');
+  }
+
+  // WebSocket open event handler.
+  void _onOpen() {
+    target.lastConnectionTime = new DateTime.now().millisecondsSinceEpoch;
+    _sendAllDelayedRequests();
+    _notifyConnect();
+  }
+
+  Map _parseJSON(String message) {
+    var map;
+    try {
+      map = json.decode(message);
+    } catch (e, st) {
+      Logger.root.severe('Disconnecting: Error decoding message: $e\n$st');
+      disconnect(reason: 'Connection saw corrupt JSON message: $e');
+      return null;
+    }
+    if (map == null) {
+      Logger.root.severe("Disconnecting: Unable to decode 'null' message");
+      disconnect(reason: "Connection saw 'null' message");
+      return null;
+    }
+    return map;
+  }
+
+  void _onBinaryMessage(dynamic data) {
+    _webSocket.nonStringToByteData(data).then((ByteData bytes) {
+      var metadataOffset = 4;
+      var dataOffset = bytes.getUint32(0, Endian.little);
+      var metadataLength = dataOffset - metadataOffset;
+      var dataLength = bytes.lengthInBytes - dataOffset;
+      var metadata = _utf8Decoder.convert(new Uint8List.view(
+          bytes.buffer, bytes.offsetInBytes + metadataOffset, metadataLength));
+      var data = new Uint8List.view(
+          bytes.buffer, bytes.offsetInBytes + dataOffset, dataLength);
+      var map = _parseJSON(metadata);
+      if (map == null || map['method'] != 'streamNotify') {
+        return;
+      }
+      var event = map['params']['event'];
+      var streamId = map['params']['streamId'];
+      scheduleMicrotask(() {
+        postServiceEvent(streamId, event, data);
+      });
+    });
+  }
+
+  void _onStringMessage(String data) {
+    var map = _parseJSON(data);
+    if (map == null) {
+      return;
+    }
+
+    if (map['method'] == 'streamNotify') {
+      var event = map['params']['event'];
+      var streamId = map['params']['streamId'];
+      scheduleMicrotask(() {
+        postServiceEvent(streamId, event, null);
+      });
+      return;
+    }
+
+    // Extract serial and result.
+    var serial = map['id'];
+
+    // Complete request.
+    var request = _pendingRequests.remove(serial);
+    if (request == null) {
+      Logger.root.severe('Received unexpected message: ${map}');
+      return;
+    }
+    if (request.method != 'getTagProfile' &&
+        request.method != 'getIsolateMetric' &&
+        request.method != 'getVMMetric') {
+      Logger.root.info('RESPONSE [${serial}] ${request.method}');
+    }
+
+    var result = map['result'];
+    if (result != null) {
+      request.completer.complete(result);
+    } else {
+      var exception = new ServerRpcException.fromMap(map['error']);
+      request.completer.completeError(exception);
+    }
+  }
+
+  // WebSocket message event handler.
+  void _onMessage(dynamic data) {
+    if (data is! String) {
+      _onBinaryMessage(data);
+    } else {
+      _onStringMessage(data);
+    }
+  }
+
+  void _cancelRequests(
+      Map<String, _WebSocketRequest> requests, String message) {
+    requests.forEach((String serial, _WebSocketRequest request) {
+      var exception = new NetworkRpcException(message +
+          '(id: $serial method: ${request.method} params: ${request.params})');
+      request.completer.completeError(exception);
+    });
+    requests.clear();
+  }
+
+  /// Cancel all pending and delayed requests by completing them with an error.
+  void _cancelAllRequests(String reason) {
+    String message = 'Canceling request: $reason';
+    if (_pendingRequests.length > 0) {
+      Logger.root.info('Canceling all pending requests.');
+      _cancelRequests(_pendingRequests, message);
+    }
+    if (_delayedRequests.length > 0) {
+      Logger.root.info('Canceling all delayed requests.');
+      _cancelRequests(_delayedRequests, message);
+    }
+  }
+
+  /// Send all delayed requests.
+  void _sendAllDelayedRequests() {
+    assert(_webSocket.isOpen);
+    if (_delayedRequests.length == 0) {
+      return;
+    }
+    Logger.root.info('Sending all delayed requests.');
+    // Send all delayed requests.
+    _delayedRequests.forEach(_sendRequest);
+    // Clear all delayed requests.
+    _delayedRequests.clear();
+  }
+
+  /// Send the request over WebSocket.
+  void _sendRequest(String serial, _WebSocketRequest request) {
+    assert(_webSocket.isOpen);
+    // Mark request as pending.
+    assert(_pendingRequests.containsKey(serial) == false);
+    _pendingRequests[serial] = request;
+    var message;
+    // Encode message.
+    if (target.chrome) {
+      message = json.encode({
+        'id': int.parse(serial),
+        'method': 'Dart.observatoryQuery',
+        'params': {'id': serial, 'query': request.method}
+      });
+    } else {
+      message = json.encode({
+        'jsonrpc': '2.0',
+        'id': serial,
+        'method': request.method,
+        'params': request.params
+      });
+    }
+    if (request.method != 'getTagProfile' &&
+        request.method != 'getIsolateMetric' &&
+        request.method != 'getVMMetric') {
+      Logger.root.info(
+          'GET [${serial}] ${request.method}(${request.params}) from ${target.networkAddress}');
+    }
+    // Send message.
+    _webSocket.send(message);
+  }
+
+  String toString() => displayName;
+}
diff --git a/runtime/observatory_2/lib/service_html.dart b/runtime/observatory_2/lib/service_html.dart
new file mode 100644
index 0000000..bb06c69
--- /dev/null
+++ b/runtime/observatory_2/lib/service_html.dart
@@ -0,0 +1,79 @@
+// Copyright (c) 2014, 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.
+
+library service_html;
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:html';
+import 'dart:typed_data';
+
+import 'package:observatory_2/service_common.dart';
+
+// Export the service library.
+export 'package:observatory_2/service_common.dart';
+
+class _HtmlWebSocket implements CommonWebSocket {
+  WebSocket _webSocket;
+
+  Future<void> connect(WebSocketVMTarget target, void onOpen(),
+      void onMessage(dynamic data), void onError(), void onClose()) async {
+    // The VM service will attempt to redirect our websocket connection request
+    // to DDS, but the dart:html WebSocket doesn't follow redirects. Instead of
+    // relying on a redirect, we'll request the websocket URI from the service.
+
+    // TODO(bkonyi): re-enable when DDS is enabled. Currently causing Observatory
+    // failures when running with Flutter (see
+    // https://github.com/flutter/flutter/issues/64333)
+    /*Uri getWebSocketUriRequest = Uri.parse(target.networkAddress);
+    getWebSocketUriRequest =
+        getWebSocketUriRequest.replace(scheme: 'http', pathSegments: [
+      ...getWebSocketUriRequest.pathSegments.where((e) => e != 'ws'),
+      'getWebSocketTarget',
+    ]);
+    final response = json.decode(await HttpRequest.getString(
+      getWebSocketUriRequest.toString(),
+    ));
+    if (!response.containsKey('result') ||
+        !response['result'].containsKey('uri')) {
+      onError();
+      return;
+    }
+    target.networkAddress = response['result']['uri'];
+    */
+    _webSocket = new WebSocket(target.networkAddress);
+    _webSocket.onClose.listen((CloseEvent) => onClose());
+    _webSocket.onError.listen((Event) => onError());
+    _webSocket.onOpen.listen((Event) => onOpen());
+    _webSocket.onMessage.listen((MessageEvent event) => onMessage(event.data));
+  }
+
+  bool get isOpen => _webSocket.readyState == WebSocket.OPEN;
+
+  void send(dynamic data) {
+    _webSocket.send(data);
+  }
+
+  void close() {
+    _webSocket.close();
+  }
+
+  Future<ByteData> nonStringToByteData(dynamic data) {
+    assert(data is Blob);
+    FileReader fileReader = new FileReader();
+    fileReader.readAsArrayBuffer(data);
+    return fileReader.onLoadEnd.first.then((e) {
+      Uint8List result = fileReader.result as Uint8List;
+      return new ByteData.view(
+          result.buffer, result.offsetInBytes, result.length);
+    });
+  }
+}
+
+/// The [WebSocketVM] communicates with a Dart VM over WebSocket. The Dart VM
+/// can be embedded in Chromium or standalone. In the case of Chromium, we
+/// make the service requests via the Chrome Remote Debugging Protocol.
+class WebSocketVM extends CommonWebSocketVM {
+  WebSocketVM(WebSocketVMTarget target) : super(target, new _HtmlWebSocket());
+}
diff --git a/runtime/observatory_2/lib/service_io.dart b/runtime/observatory_2/lib/service_io.dart
new file mode 100644
index 0000000..6b5ad38d
--- /dev/null
+++ b/runtime/observatory_2/lib/service_io.dart
@@ -0,0 +1,58 @@
+// Copyright (c) 2014, 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.
+
+library service_io;
+
+import 'dart:async';
+import 'dart:io';
+import 'dart:typed_data';
+
+import 'package:logging/logging.dart';
+import 'package:observatory_2/service_common.dart';
+
+// Export the service library.
+export 'package:observatory_2/service_common.dart';
+
+class _IOWebSocket implements CommonWebSocket {
+  WebSocket _webSocket;
+
+  Future<void> connect(WebSocketVMTarget target, void onOpen(),
+      void onMessage(dynamic data), void onError(), void onClose()) async {
+    try {
+      _webSocket = await WebSocket.connect(target.networkAddress);
+      _webSocket.listen(onMessage,
+          onError: (dynamic) => onError(),
+          onDone: onClose,
+          cancelOnError: true);
+      onOpen();
+    } catch (_) {
+      onError();
+    }
+  }
+
+  bool get isOpen =>
+      (_webSocket != null) && (_webSocket.readyState == WebSocket.open);
+
+  void send(dynamic data) {
+    _webSocket.add(data);
+  }
+
+  void close() {
+    if (_webSocket != null) {
+      _webSocket.close();
+    }
+  }
+
+  Future<ByteData> nonStringToByteData(dynamic data) {
+    assert(data is Uint8List);
+    Logger.root.info('Binary data size in bytes: ${data.lengthInBytes}');
+    return new Future.sync(() =>
+        new ByteData.view(data.buffer, data.offsetInBytes, data.lengthInBytes));
+  }
+}
+
+/// The [WebSocketVM] communicates with a Dart VM over WebSocket.
+class WebSocketVM extends CommonWebSocketVM {
+  WebSocketVM(WebSocketVMTarget target) : super(target, new _IOWebSocket());
+}
diff --git a/runtime/observatory_2/lib/src/allocation_profile/allocation_profile.dart b/runtime/observatory_2/lib/src/allocation_profile/allocation_profile.dart
new file mode 100644
index 0000000..c1e7aa1
--- /dev/null
+++ b/runtime/observatory_2/lib/src/allocation_profile/allocation_profile.dart
@@ -0,0 +1,90 @@
+// Copyright (c) 2015, 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 allocation_profiler;
+
+class AllocationProfile implements M.AllocationProfile {
+  static const _lastServiceGC = 'dateLastServiceGC';
+  final DateTime lastServiceGC;
+  static const _lastAccumulatorReset = 'dateLastAccumulatorReset';
+  final DateTime lastAccumulatorReset;
+  final S.HeapSpace newSpace;
+  final S.HeapSpace oldSpace;
+  final S.HeapSpace totalSpace;
+  final Iterable<M.ClassHeapStats> members;
+
+  AllocationProfile(S.ServiceMap map, {Map/*<String, List<String>>*/ defaults})
+      : lastAccumulatorReset = _intString2DateTime(map[_lastAccumulatorReset]),
+        lastServiceGC = _intString2DateTime(map[_lastServiceGC]),
+        oldSpace = new S.HeapSpace()..update(map['_heaps']['old']),
+        newSpace = new S.HeapSpace()..update(map['_heaps']['new']),
+        totalSpace = new S.HeapSpace(),
+        members = _convertMembers(map['members'], defaults: defaults) {
+    totalSpace.add(oldSpace);
+    totalSpace.add(newSpace);
+  }
+
+  static DateTime _intString2DateTime(String milliseconds) {
+    if ((milliseconds == null) || milliseconds == '') {
+      return null;
+    }
+    return new DateTime.fromMillisecondsSinceEpoch(int.parse(milliseconds));
+  }
+
+  static ClassHeapStats _convertMember(/*S.ServiceMap*/ map) {
+    assert(map['type'] == 'ClassHeapStats');
+    return new ClassHeapStats(map);
+  }
+
+  static List<M.ClassHeapStats> _convertMembers(Iterable/*<S.ServiceMap>*/ raw,
+      {Map/*<String, List<String>>*/ defaults}) {
+    final List<M.ClassHeapStats> members =
+        raw.map<ClassHeapStats>(_convertMember).toList();
+    if (defaults == null) {
+      return members;
+    }
+    final Map<String, List<ClassHeapStats>> aliases =
+        new Map.fromIterable(defaults.keys, value: (_) => <ClassHeapStats>[]);
+    final Map<String, List<ClassHeapStats>> accumulators =
+        <String, List<ClassHeapStats>>{};
+    defaults.forEach((/*String*/ key, /*List<String>*/ values) {
+      final classes = aliases[key];
+      accumulators.addAll(new Map.fromIterable(values, value: (_) => classes));
+    });
+    final List<M.ClassHeapStats> result = <M.ClassHeapStats>[];
+    members.forEach((M.ClassHeapStats member) {
+      if (accumulators.containsKey(member.clazz.id)) {
+        accumulators[member.clazz.id].add(member);
+      } else {
+        result.add(member);
+      }
+    });
+    return result
+      ..addAll(
+          aliases.keys.map((key) => new ClassesHeapStats(key, aliases[key])));
+  }
+}
+
+class ClassHeapStats implements M.ClassHeapStats {
+  final S.Class clazz;
+  final String displayName = null;
+  final S.Allocations newSpace;
+  final S.Allocations oldSpace;
+
+  ClassHeapStats(Map map)
+      : clazz = map['class'],
+        oldSpace = new S.Allocations()..update(map['_old']),
+        newSpace = new S.Allocations()..update(map['_new']);
+}
+
+class ClassesHeapStats implements M.ClassHeapStats {
+  final S.Class clazz = null;
+  final String displayName;
+  final S.Allocations newSpace;
+  final S.Allocations oldSpace;
+
+  ClassesHeapStats(this.displayName, Iterable<ClassHeapStats> classes)
+      : oldSpace = new S.Allocations()..combine(classes.map((m) => m.oldSpace)),
+        newSpace = new S.Allocations()..combine(classes.map((m) => m.newSpace));
+}
diff --git a/runtime/observatory_2/lib/src/app/analytics.dart b/runtime/observatory_2/lib/src/app/analytics.dart
new file mode 100644
index 0000000..a34c2da
--- /dev/null
+++ b/runtime/observatory_2/lib/src/app/analytics.dart
@@ -0,0 +1,38 @@
+// Copyright (c) 2015, 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;
+
+// TODO(eernst): Use 'bool.fromEnvironment' below when possible;
+// for now we use a dual `defaultValue` rewrite.
+const _obsVer = (String.fromEnvironment('OBS_VER', defaultValue: '1') ==
+        String.fromEnvironment('OBS_VER', defaultValue: '2'))
+    ? String.fromEnvironment('OBS_VER')
+    : null;
+
+class Analytics {
+  static final _UA = 'UA-26406144-17';
+  static final _name = 'Observatory';
+  static final _version = _obsVer;
+  static final _googleAnalytics = new AnalyticsHtml(_UA, _name, _version);
+
+  static initialize() {
+    // We only send screen views. This is allowed without user permission.
+    // Note, before flipping this to be true we need a UI to allow users to
+    // control this.
+    _googleAnalytics.analyticsOpt = AnalyticsOpt.optOut;
+  }
+
+  /// Called whenever an Observatory page is viewed.
+  static Future reportPageView(Uri uri) {
+    // Only report analytics when running in JavaScript.
+    if (Utils.runningInJavaScript()) {
+      // The screen name is the uri's path. e.g. inspect, profile.
+      final screenName = uri.path;
+      return _googleAnalytics.sendScreenView(screenName);
+    } else {
+      return new Future.value(null);
+    }
+  }
+}
diff --git a/runtime/observatory_2/lib/src/app/application.dart b/runtime/observatory_2/lib/src/app/application.dart
new file mode 100644
index 0000000..768c3b4
--- /dev/null
+++ b/runtime/observatory_2/lib/src/app/application.dart
@@ -0,0 +1,294 @@
+// 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;
+
+/// The observatory application. Instances of this are created and owned
+/// by the observatory_application custom element.
+class ObservatoryApplication {
+  static ObservatoryApplication app;
+  final RenderingQueue queue = new RenderingQueue();
+  final TargetRepository targets = new TargetRepository(isConnectedVMTarget);
+  final EventRepository events = new EventRepository();
+  final NotificationRepository notifications = new NotificationRepository();
+  final _pageRegistry = <Page>[];
+  LocationManager _locationManager;
+  LocationManager get locationManager => _locationManager;
+  Page currentPage;
+  bool _vmConnected = false;
+  VM _vm;
+  VM get vm => _vm;
+
+  static bool isConnectedVMTarget(M.Target target) {
+    if (app._vm is CommonWebSocketVM) {
+      if ((app._vm as CommonWebSocketVM).target == target) {
+        return app._vm.isConnected;
+      }
+    }
+    return false;
+  }
+
+  _switchVM(VM newVM) {
+    final VM oldVM = _vm;
+
+    Logger.root.info('_switchVM from:${oldVM} to:${newVM}');
+
+    if (oldVM == newVM) {
+      // Do nothing.
+      return;
+    }
+
+    if (oldVM != null) {
+      print('disconnecting from the old VM ${oldVM}--');
+      // Disconnect from current VM.
+      stopGCEventListener();
+      notifications.deleteAll();
+      oldVM.disconnect();
+    }
+
+    if (newVM != null) {
+      // Mark that we haven't connected yet.
+      _vmConnected = false;
+      // On connect:
+      newVM.onConnect.then((_) async {
+        // We connected.
+        _vmConnected = true;
+        notifications.deleteDisconnectEvents();
+        await newVM.load();
+        // TODO(cbernaschina) smart connection of streams in the events object.
+        newVM.listenEventStream(VM.kVMStream, _onEvent);
+        newVM.listenEventStream(VM.kIsolateStream, _onEvent);
+        newVM.listenEventStream(VM.kDebugStream, _onEvent);
+        newVM.listenEventStream(VM.kServiceStream, _onEvent);
+      });
+      // On disconnect:
+      newVM.onDisconnect.then((String reason) {
+        if (this.vm != newVM) {
+          // This disconnect event occurred *after* a new VM was installed.
+          return;
+        }
+        // Let anyone looking at the targets know that we have disconnected
+        // from one.
+        targets.emitDisconnectEvent();
+        if (!_vmConnected) {
+          // Connection error. Navigate back to the connect page.
+          Logger.root.info('Connection failed, navigating to VM connect page.');
+          // Clear the vm.
+          _vm = null;
+          app.locationManager.go(Uris.vmConnect());
+        } else {
+          // Disconnect. Stay at the current page and push an a connection
+          // closed event.
+          Logger.root.info('Lost an existing connection to a VM');
+          events.add(new ConnectionClosedEvent(new DateTime.now(), reason));
+        }
+      });
+    }
+
+    _vm = newVM;
+  }
+
+  StreamSubscription _gcSubscription;
+  StreamSubscription _loggingSubscription;
+
+  Future startGCEventListener() async {
+    if (_gcSubscription != null || _vm == null) {
+      return;
+    }
+    _gcSubscription = await _vm.listenEventStream(VM.kGCStream, _onEvent);
+  }
+
+  Future startLoggingEventListener() async {
+    if (_loggingSubscription != null || _vm == null) {
+      return;
+    }
+    _loggingSubscription =
+        await _vm.listenEventStream(Isolate.kLoggingStream, _onEvent);
+  }
+
+  Future stopGCEventListener() async {
+    if (_gcSubscription == null) {
+      return;
+    }
+    _gcSubscription.cancel();
+    _gcSubscription = null;
+  }
+
+  Future stopLoggingEventListener() async {
+    if (_loggingSubscription == null) {
+      return;
+    }
+    _loggingSubscription.cancel();
+    _loggingSubscription = null;
+  }
+
+  final ObservatoryApplicationElement rootElement;
+
+  ServiceObject lastErrorOrException;
+
+  void _initOnce() {
+    assert(app == null);
+    app = this;
+    _registerPages();
+    // Visit the current page.
+    locationManager._visit();
+  }
+
+  void _deletePauseEvents(e) {
+    notifications.deletePauseEvents(isolate: e.isolate);
+  }
+
+  void _addNotification(M.Event e) {
+    notifications.add(new EventNotification(e));
+  }
+
+  void _onEvent(ServiceEvent event) {
+    assert(event.kind != ServiceEvent.kNone);
+    M.Event e = createEventFromServiceEvent(event);
+    if (e != null) {
+      events.add(e);
+    }
+  }
+
+  void _registerPages() {
+    _pageRegistry.add(new VMPage(this));
+    _pageRegistry.add(new FlagsPage(this));
+    _pageRegistry.add(new NativeMemoryProfilerPage(this));
+    _pageRegistry.add(new InspectPage(this));
+    _pageRegistry.add(new ClassTreePage(this));
+    _pageRegistry.add(new DebuggerPage(this));
+    _pageRegistry.add(new ObjectStorePage(this));
+    _pageRegistry.add(new CpuProfilerPage(this));
+    _pageRegistry.add(new TableCpuProfilerPage(this));
+    _pageRegistry.add(new AllocationProfilerPage(this));
+    _pageRegistry.add(new HeapMapPage(this));
+    _pageRegistry.add(new HeapSnapshotPage(this));
+    _pageRegistry.add(new VMConnectPage(this));
+    _pageRegistry.add(new IsolateReconnectPage(this));
+    _pageRegistry.add(new ErrorViewPage(this));
+    _pageRegistry.add(new MetricsPage(this));
+    _pageRegistry.add(new PersistentHandlesPage(this));
+    _pageRegistry.add(new PortsPage(this));
+    _pageRegistry.add(new LoggingPage(this));
+    _pageRegistry.add(new TimelinePage(this));
+    _pageRegistry.add(new TimelineDashboardPage(this));
+    _pageRegistry.add(new ProcessSnapshotPage(this));
+    // Note that ErrorPage must be the last entry in the list as it is
+    // the catch all.
+    _pageRegistry.add(new ErrorPage(this));
+  }
+
+  void _visit(Uri uri, Map internalArguments) {
+    if (internalArguments['trace'] != null) {
+      var traceArg = internalArguments['trace'];
+      if (traceArg == 'on') {
+        Tracer.start();
+      } else if (traceArg == 'off') {
+        Tracer.stop();
+      }
+    }
+    if (Tracer.current != null) {
+      Tracer.current.reset();
+    }
+    for (var i = 0; i < _pageRegistry.length; i++) {
+      var page = _pageRegistry[i];
+      if (page.canVisit(uri)) {
+        _installPage(page);
+        page.visit(uri, internalArguments);
+        return;
+      }
+    }
+    throw new ArgumentError.value(uri, 'uri');
+  }
+
+  /// Set the Observatory application page.
+  void _installPage(Page page) {
+    assert(page != null);
+    if (currentPage == page) {
+      // Already installed.
+      return;
+    }
+    if (currentPage != null) {
+      Logger.root.info('Uninstalling page: $currentPage');
+      currentPage.onUninstall();
+      // Clear children.
+      rootElement.children.clear();
+    }
+    Logger.root.info('Installing page: $page');
+    try {
+      page.onInstall();
+    } catch (e) {
+      Logger.root.severe('Failed to install page: $e');
+    }
+    // Add new page.
+    rootElement.children.add(page.element);
+
+    // Remember page.
+    currentPage = page;
+  }
+
+  ObservatoryApplication(this.rootElement) {
+    _locationManager = new LocationManager(this);
+    targets.onChange.listen((TargetChangeEvent e) {
+      if (e.disconnected) {
+        // We don't care about disconnected events.
+        return;
+      }
+      if (targets.current == null) {
+        _switchVM(null);
+      } else {
+        final bool currentTarget =
+            (_vm as WebSocketVM)?.target == targets.current;
+        final bool currentTargetConnected = (_vm != null) && _vm.isConnected;
+        if (!currentTarget || !currentTargetConnected) {
+          _switchVM(new WebSocketVM(targets.current));
+          _vm.onConnect.then((_) {
+            app.locationManager.go(Uris.vm());
+          });
+          _vm.load();
+        } else if (currentTargetConnected) {
+          app.locationManager.go(Uris.vm());
+        }
+      }
+    });
+
+    Logger.root.info('Setting initial target to ${targets.current.name}');
+    _switchVM(new WebSocketVM(targets.current));
+    _initOnce();
+
+    // delete pause events.
+    events.onIsolateExit.listen(_deletePauseEvents);
+    events.onResume.listen(_deletePauseEvents);
+    events.onPauseStart.listen(_deletePauseEvents);
+    events.onPauseExit.listen(_deletePauseEvents);
+    events.onPauseBreakpoint.listen(_deletePauseEvents);
+    events.onPauseInterrupted.listen(_deletePauseEvents);
+    events.onPauseException.listen(_deletePauseEvents);
+
+    // show notification for an event.
+    events.onIsolateReload.listen(_addNotification);
+    events.onPauseExit.listen(_addNotification);
+    events.onPauseBreakpoint.listen(_addNotification);
+    events.onPauseInterrupted.listen(_addNotification);
+    events.onPauseException.listen(_addNotification);
+    events.onInspect.listen(_addNotification);
+    events.onConnectionClosed.listen(_addNotification);
+  }
+
+  void handleException(e, st) {
+    if (e is ServerRpcException) {
+      if (e.code == ServerRpcException.kFeatureDisabled) return;
+      if (e.code == ServerRpcException.kIsolateMustBePaused) return;
+      if (e.code == ServerRpcException.kCannotAddBreakpoint) return;
+      Logger.root.fine('Dropping exception: ${e}\n${st}');
+    }
+
+    // TODO(turnidge): Report this failure via analytics.
+    Logger.root.warning('Caught exception: ${e}\n${st}');
+    notifications.add(new ExceptionNotification(e, stacktrace: st));
+  }
+
+  // This map keeps track of which curly-blocks have been expanded by the user.
+  Map<String, bool> expansions = {};
+}
diff --git a/runtime/observatory_2/lib/src/app/location_manager.dart b/runtime/observatory_2/lib/src/app/location_manager.dart
new file mode 100644
index 0000000..a0e1af1
--- /dev/null
+++ b/runtime/observatory_2/lib/src/app/location_manager.dart
@@ -0,0 +1,167 @@
+// 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;
+
+class LocationManager {
+  final ObservatoryApplication _app;
+
+  /// [internalArguments] are parameters specified after a '---' in the
+  /// application URL.
+  final Map<String, String> internalArguments = new Map<String, String>();
+
+  Uri _uri;
+
+  /// [uri] is the application uri. Application uris consist of a path and
+  /// the queryParameters map.
+  Uri get uri => _uri;
+
+  LocationManager(this._app) {
+    window.onPopState.listen(_onBrowserNavigation);
+    // Determine initial application path.
+    var applicationPath = '${window.location.hash}';
+    if ((window.location.hash == '') || (window.location.hash == '#')) {
+      // Observatory has loaded but no application path has been specified,
+      // use the default.
+      // By default we navigate to the VM page.
+      applicationPath = Uris.vm();
+    }
+    // Update current application path.
+    window.history
+        .replaceState(applicationPath, document.title, applicationPath);
+    _updateApplicationLocation(applicationPath);
+  }
+
+  bool getBoolParameter(String name, bool defaultValue) {
+    var value = uri.queryParameters[name];
+    if ("true" == value) return true;
+    if ("false" == value) return false;
+    return defaultValue;
+  }
+
+  /// Called whenever the browser changes the location bar (e.g. forward or
+  /// back button press).
+  void _onBrowserNavigation(PopStateEvent event) {
+    _updateApplicationLocation(window.location.hash);
+    _visit();
+  }
+
+  /// Given an application url, generate an href link.
+  String makeLink(String url) => '#$url';
+
+  /// Update the application location. After this function returns,
+  /// [uri] and [debugArguments] will be updated.
+  _updateApplicationLocation(String url) {
+    if (url == Uris.vmConnect()) {
+      // When we go to the vm-connect page, drop all notifications.
+      _app.notifications.deleteAll();
+    }
+
+    // Chop off leading '#'.
+    if (url.startsWith('#')) {
+      url = url.substring(1);
+    }
+    // Fall through handles '#/'
+    // Chop off leading '/'.
+    if (url.startsWith('/')) {
+      url = url.substring(1);
+    }
+    // Parse out debug arguments.
+    if (url.contains('---')) {
+      var chunks = url.split('---');
+      url = chunks[0];
+      if ((chunks.length > 1) && (chunks[1] != '')) {
+        internalArguments.clear();
+        try {
+          internalArguments.addAll(Uri.splitQueryString(chunks[1]));
+        } catch (e) {
+          Logger.root.warning('Could not parse debug arguments ${e}');
+        }
+      }
+    }
+    _uri = Uri.parse(url);
+  }
+
+  /// Add [url] to the browser history.
+  _addToBrowserHistory(String url) {
+    window.history.pushState(url, document.title, url);
+  }
+
+  /// Notify the current page that something has changed.
+  _visit() {
+    Chain.capture(() => _app._visit(_uri, internalArguments), onError: (e, st) {
+      if (e is IsolateNotFound) {
+        var newPath = ((_app.vm == null || _app.vm.isDisconnected)
+            ? '/vm-connect'
+            : '/isolate-reconnect');
+        var parameters = <String, dynamic>{};
+        parameters.addAll(_uri.queryParameters);
+        parameters['originalUri'] = _uri.toString();
+        parameters['isolateId'] = parameters['isolateId'];
+        var generatedUri = new Uri(path: newPath, queryParameters: parameters);
+        go(makeLink(generatedUri.toString()), true);
+        return;
+      }
+      // Surface any uncaught exceptions.
+      _app.handleException(e, st);
+    });
+  }
+
+  /// Navigate to [url].
+  void go(String url, [bool addToBrowserHistory = true]) {
+    if (addToBrowserHistory) {
+      _addToBrowserHistory(url);
+    }
+    _updateApplicationLocation(url);
+    _visit();
+  }
+
+  /// Starting with the current uri path and queryParameters, update
+  /// queryParameters present in [updateParameters], then generate a new uri
+  /// and navigate to that.
+  goReplacingParameters(Map updatedParameters,
+      [bool addToBrowserHistory = true]) {
+    go(makeLinkReplacingParameters(updatedParameters), addToBrowserHistory);
+  }
+
+  makeLinkReplacingParameters(Map updatedParameters) {
+    var parameters = new Map.from(_uri.queryParameters);
+    updatedParameters.forEach((k, v) {
+      parameters[k] = v;
+    });
+    // Ensure path starts with a slash.
+    var path = uri.path.startsWith('/') ? uri.path : '/${uri.path}';
+    var generatedUri = new Uri(path: path, queryParameters: parameters);
+    return makeLink(generatedUri.toString());
+  }
+
+  goForwardingParameters(String newPath, [bool addToBrowserHistory = true]) {
+    go(makeLinkForwardingParameters(newPath), addToBrowserHistory);
+  }
+
+  makeLinkForwardingParameters(String newPath) {
+    var parameters = _uri.queryParameters;
+    var generatedUri = new Uri(path: newPath, queryParameters: parameters);
+    return makeLink(generatedUri.toString());
+  }
+
+  /// Utility event handler when clicking on application url link.
+  void onGoto(MouseEvent event) {
+    if ((event.button > 0) ||
+        event.metaKey ||
+        event.ctrlKey ||
+        event.shiftKey ||
+        event.altKey) {
+      // Mouse event is not a left-click OR
+      // mouse event is a left-click with a modifier key:
+      // let browser handle.
+      return;
+    }
+    event.preventDefault();
+    // 'currentTarget' is the dom element that would process the event.
+    // If we use 'target' we might get an <em> element or somesuch.
+    Element target = event.currentTarget;
+    go(target.attributes['href']);
+  }
+}
diff --git a/runtime/observatory_2/lib/src/app/notification.dart b/runtime/observatory_2/lib/src/app/notification.dart
new file mode 100644
index 0000000..1f5f9d7
--- /dev/null
+++ b/runtime/observatory_2/lib/src/app/notification.dart
@@ -0,0 +1,18 @@
+// Copyright (c) 2016, 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;
+
+class ExceptionNotification implements M.ExceptionNotification {
+  final exception;
+
+  /// [optional]
+  final StackTrace stacktrace;
+  ExceptionNotification(this.exception, {this.stacktrace});
+}
+
+class EventNotification implements M.EventNotification {
+  final M.Event event;
+  EventNotification(this.event);
+}
diff --git a/runtime/observatory_2/lib/src/app/page.dart b/runtime/observatory_2/lib/src/app/page.dart
new file mode 100644
index 0000000..a366acf
--- /dev/null
+++ b/runtime/observatory_2/lib/src/app/page.dart
@@ -0,0 +1,1024 @@
+// Copyright (c) 2014, 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;
+
+final _allocationProfileRepository = new AllocationProfileRepository();
+final _breakpointRepository = new BreakpointRepository();
+final _classRepository = new ClassRepository();
+final _classSampleProfileRepository = new ClassSampleProfileRepository();
+final _contextRepository = new ContextRepository();
+final _evalRepository = new EvalRepository();
+final _fieldRepository = new FieldRepository();
+final _functionRepository = new FunctionRepository();
+final _heapSnapshotRepository = new HeapSnapshotRepository();
+final _icdataRepository = new ICDataRepository();
+final _inboundReferencesRepository = new InboundReferencesRepository();
+final _isolateSampleProfileRepository = new IsolateSampleProfileRepository();
+final _libraryRepository = new LibraryRepository();
+final _megamorphicCacheRepository = new MegamorphicCacheRepository();
+final _metricRepository = new MetricRepository();
+final _nativeMemorySampleProfileRepository =
+    new NativeMemorySampleProfileRepository();
+final _objectPoolRepository = new ObjectPoolRepository();
+final _objectRepository = new ObjectRepository();
+final _objectstoreRepository = new ObjectStoreRepository();
+final _persistentHandlesRepository = new PersistentHandlesRepository();
+final _portsRepository = new PortsRepository();
+final _scriptRepository = new ScriptRepository();
+final _singleTargetCacheRepository = new SingleTargetCacheRepository();
+final _stronglyReachangleInstancesRepository =
+    new StronglyReachableInstancesRepository();
+final _subtypeTestCacheRepository = new SubtypeTestCacheRepository();
+final _timelineRepository = new TimelineRepository();
+final _typeArgumentsRepository = new TypeArgumentsRepository();
+final _unlinkedCallRepository = new UnlinkedCallRepository();
+final _vmrepository = new VMRepository();
+
+class IsolateNotFound implements Exception {
+  String isolateId;
+  IsolateNotFound(this.isolateId);
+  String toString() => "IsolateNotFound: $isolateId";
+}
+
+RetainedSizeRepository _retainedSizeRepository = new RetainedSizeRepository();
+ReachableSizeRepository _reachableSizeRepository =
+    new ReachableSizeRepository();
+RetainingPathRepository _retainingPathRepository =
+    new RetainingPathRepository();
+
+/// A [Page] controls the user interface of Observatory. At any given time
+/// one page will be the current page. Pages are registered at startup.
+/// When the user navigates within the application, each page is asked if it
+/// can handle the current location, the first page to say yes, wins.
+abstract class Page {
+  final ObservatoryApplication app;
+  final Map<String, String> internalArguments = <String, String>{};
+  HtmlElement element;
+
+  Page(this.app);
+
+  /// Called when the page is installed, this callback must initialize
+  /// [element].
+  void onInstall();
+
+  /// Called when the page is uninstalled, this callback must clear
+  /// [element].
+  void onUninstall() {
+    element = null;
+  }
+
+  /// Called when the page should update its state based on [uri].
+  void visit(Uri uri, Map internalArguments) {
+    this.internalArguments.clear();
+    this.internalArguments.addAll(internalArguments);
+    _visit(uri);
+  }
+
+  // Overridden by subclasses.
+  void _visit(Uri uri);
+
+  /// Called to test whether this page can visit [uri].
+  bool canVisit(Uri uri);
+}
+
+/// A [MatchingPage] matches a single uri path.
+abstract class MatchingPage extends Page {
+  final String path;
+  MatchingPage(this.path, app) : super(app);
+
+  void _visit(Uri uri) {
+    assert(uri != null);
+    assert(canVisit(uri));
+  }
+
+  Future<Isolate> getIsolate(Uri uri) {
+    var isolateId = uri.queryParameters['isolateId'];
+    return app.vm.getIsolate(isolateId).then((isolate) {
+      if (isolate == null) {
+        throw new IsolateNotFound(isolateId);
+      }
+      return isolate;
+    });
+  }
+
+  EditorRepository getEditor(Uri uri) {
+    final editor = uri.queryParameters['editor'];
+    return new EditorRepository(app.vm, editor: editor);
+    return null;
+  }
+
+  bool canVisit(Uri uri) => uri.path == path;
+}
+
+/// A [SimplePage] matches a single uri path and displays a single element.
+class SimplePage extends MatchingPage {
+  final String elementTagName;
+  SimplePage(String path, this.elementTagName, app) : super(path, app);
+
+  void onInstall() {
+    if (element == null) {
+      element = new Element.tag(elementTagName);
+    }
+  }
+}
+
+/// Error page for unrecognized paths.
+class ErrorPage extends Page {
+  ErrorPage(app) : super(app);
+
+  void onInstall() {
+    if (element == null) {
+      // Lazily create page.
+      element =
+          new GeneralErrorElement(app.notifications, queue: app.queue).element;
+    }
+  }
+
+  void _visit(Uri uri) {
+    assert(element != null);
+    assert(canVisit(uri));
+
+    (element as GeneralErrorElement).message = "Path '${uri.path}' not found";
+  }
+
+  /// Catch all.
+  bool canVisit(Uri uri) => true;
+}
+
+/// Top-level vm info page.
+class VMPage extends MatchingPage {
+  VMPage(app) : super('vm', app);
+
+  final DivElement container = new DivElement();
+
+  void onInstall() {
+    if (element == null) {
+      element = container;
+    }
+    assert(element != null);
+  }
+
+  void _visit(Uri uri) {
+    super._visit(uri);
+    if (app.vm == null) {
+      Logger.root.severe('VMPage has no VM');
+      // Reroute to vm-connect.
+      app.locationManager.go(Uris.vmConnect());
+      return;
+    }
+    app.vm.reload().then((serviceObject) {
+      VM vm = serviceObject;
+      container.children = <Element>[
+        new VMViewElement(
+                vm,
+                _vmrepository,
+                app.events,
+                app.notifications,
+                new IsolateRepository(app.vm),
+                new IsolateGroupRepository(app.vm),
+                _scriptRepository,
+                queue: app.queue)
+            .element
+      ];
+    }).catchError((e, stack) {
+      Logger.root.severe('VMPage visit error: $e');
+      Logger.root.severe('Stack: $stack');
+      // Reroute to vm-connect.
+      app.locationManager.go(Uris.vmConnect());
+    });
+  }
+}
+
+class FlagsPage extends SimplePage {
+  FlagsPage(app) : super('flags', 'flag-list', app);
+
+  @override
+  onInstall() {
+    element = new FlagListElement(
+            app.vm, app.events, new FlagsRepository(app.vm), app.notifications,
+            queue: app.queue)
+        .element;
+  }
+
+  void _visit(Uri uri) {
+    super._visit(uri);
+  }
+}
+
+class NativeMemoryProfilerPage extends SimplePage {
+  NativeMemoryProfilerPage(app)
+      : super('native-memory-profile', 'native-memory-profile', app);
+  @override
+  onInstall() {
+    if (element == null) {
+      element = new NativeMemoryProfileElement(app.vm, app.events,
+              app.notifications, _nativeMemorySampleProfileRepository,
+              queue: app.queue)
+          .element;
+    }
+    assert(element != null);
+  }
+
+  void _visit(Uri uri) {
+    super._visit(uri);
+  }
+}
+
+class InspectPage extends MatchingPage {
+  InspectPage(app) : super('inspect', app);
+
+  final DivElement container = new DivElement();
+
+  void _visit(Uri uri) {
+    super._visit(uri);
+    getIsolate(uri).then((isolate) {
+      var objectId = uri.queryParameters['objectId'];
+      if (objectId == null) {
+        isolate.reload().then(_visitObject);
+      } else {
+        isolate.getObject(objectId).then(_visitObject);
+      }
+    });
+  }
+
+  void onInstall() {
+    if (element == null) {
+      element = container;
+    }
+    assert(element != null);
+  }
+
+  Future _visitObject(obj) async {
+    container.children = <Element>[];
+    await obj.reload();
+    if (obj is Class) {
+      container.children = <Element>[
+        new ClassViewElement(
+                app.vm,
+                obj.isolate,
+                obj,
+                app.events,
+                app.notifications,
+                _classRepository,
+                _retainedSizeRepository,
+                _reachableSizeRepository,
+                _inboundReferencesRepository,
+                _retainingPathRepository,
+                _fieldRepository,
+                _scriptRepository,
+                _objectRepository,
+                _evalRepository,
+                _stronglyReachangleInstancesRepository,
+                _classSampleProfileRepository,
+                queue: app.queue)
+            .element
+      ];
+    } else if (obj is Code) {
+      await obj.loadScript();
+      container.children = <Element>[
+        new CodeViewElement(
+                app.vm,
+                obj.isolate,
+                obj,
+                app.events,
+                app.notifications,
+                _retainedSizeRepository,
+                _reachableSizeRepository,
+                _inboundReferencesRepository,
+                _retainingPathRepository,
+                _objectRepository,
+                queue: app.queue)
+            .element
+      ];
+    } else if (obj is Context) {
+      container.children = <Element>[
+        new ContextViewElement(
+                app.vm,
+                obj.isolate,
+                obj,
+                app.events,
+                app.notifications,
+                _contextRepository,
+                _retainedSizeRepository,
+                _reachableSizeRepository,
+                _inboundReferencesRepository,
+                _retainingPathRepository,
+                _objectRepository,
+                queue: app.queue)
+            .element
+      ];
+    } else if (obj is DartError) {
+      container.children = <Element>[
+        new ErrorViewElement(app.notifications, obj, queue: app.queue).element
+      ];
+    } else if (obj is Field) {
+      container.children = <Element>[
+        new FieldViewElement(
+                app.vm,
+                obj.isolate,
+                obj,
+                app.events,
+                app.notifications,
+                _fieldRepository,
+                _classRepository,
+                _retainedSizeRepository,
+                _reachableSizeRepository,
+                _inboundReferencesRepository,
+                _retainingPathRepository,
+                _scriptRepository,
+                _objectRepository,
+                queue: app.queue)
+            .element
+      ];
+    } else if (obj is Instance) {
+      container.children = <Element>[
+        new InstanceViewElement(
+                app.vm,
+                obj.isolate,
+                obj,
+                app.events,
+                app.notifications,
+                _objectRepository,
+                _classRepository,
+                _retainedSizeRepository,
+                _reachableSizeRepository,
+                _inboundReferencesRepository,
+                _retainingPathRepository,
+                _scriptRepository,
+                _evalRepository,
+                _typeArgumentsRepository,
+                _breakpointRepository,
+                _functionRepository,
+                queue: app.queue)
+            .element
+      ];
+    } else if (obj is Isolate) {
+      container.children = <Element>[
+        new IsolateViewElement(
+                app.vm,
+                obj,
+                app.events,
+                app.notifications,
+                new IsolateRepository(app.vm),
+                _scriptRepository,
+                _functionRepository,
+                _libraryRepository,
+                _objectRepository,
+                _evalRepository,
+                queue: app.queue)
+            .element
+      ];
+    } else if (obj is ServiceFunction) {
+      container.children = <Element>[
+        new FunctionViewElement(
+                app.vm,
+                obj.isolate,
+                obj,
+                app.events,
+                app.notifications,
+                _functionRepository,
+                _classRepository,
+                _retainedSizeRepository,
+                _reachableSizeRepository,
+                _inboundReferencesRepository,
+                _retainingPathRepository,
+                _scriptRepository,
+                _objectRepository,
+                queue: app.queue)
+            .element
+      ];
+    } else if (obj is ICData) {
+      container.children = <Element>[
+        new ICDataViewElement(
+                app.vm,
+                obj.isolate,
+                obj,
+                app.events,
+                app.notifications,
+                _icdataRepository,
+                _retainedSizeRepository,
+                _reachableSizeRepository,
+                _inboundReferencesRepository,
+                _retainingPathRepository,
+                _objectRepository,
+                queue: app.queue)
+            .element
+      ];
+    } else if (obj is SingleTargetCache) {
+      container.children = <Element>[
+        new SingleTargetCacheViewElement(
+                app.vm,
+                obj.isolate,
+                obj,
+                app.events,
+                app.notifications,
+                _singleTargetCacheRepository,
+                _retainedSizeRepository,
+                _reachableSizeRepository,
+                _inboundReferencesRepository,
+                _retainingPathRepository,
+                _objectRepository,
+                queue: app.queue)
+            .element
+      ];
+    } else if (obj is SubtypeTestCache) {
+      container.children = <Element>[
+        new SubtypeTestCacheViewElement(
+                app.vm,
+                obj.isolate,
+                obj,
+                app.events,
+                app.notifications,
+                _subtypeTestCacheRepository,
+                _retainedSizeRepository,
+                _reachableSizeRepository,
+                _inboundReferencesRepository,
+                _retainingPathRepository,
+                _objectRepository,
+                queue: app.queue)
+            .element
+      ];
+    } else if (obj is UnlinkedCall) {
+      container.children = <Element>[
+        new UnlinkedCallViewElement(
+                app.vm,
+                obj.isolate,
+                obj,
+                app.events,
+                app.notifications,
+                _unlinkedCallRepository,
+                _retainedSizeRepository,
+                _reachableSizeRepository,
+                _inboundReferencesRepository,
+                _retainingPathRepository,
+                _objectRepository,
+                queue: app.queue)
+            .element
+      ];
+    } else if (obj is Library) {
+      container.children = <Element>[
+        new LibraryViewElement(
+                app.vm,
+                obj.isolate,
+                obj,
+                app.events,
+                app.notifications,
+                _libraryRepository,
+                _fieldRepository,
+                _retainedSizeRepository,
+                _reachableSizeRepository,
+                _inboundReferencesRepository,
+                _retainingPathRepository,
+                _scriptRepository,
+                _objectRepository,
+                _evalRepository,
+                queue: app.queue)
+            .element
+      ];
+    } else if (obj is MegamorphicCache) {
+      container.children = <Element>[
+        new MegamorphicCacheViewElement(
+                app.vm,
+                obj.isolate,
+                obj,
+                app.events,
+                app.notifications,
+                _megamorphicCacheRepository,
+                _retainedSizeRepository,
+                _reachableSizeRepository,
+                _inboundReferencesRepository,
+                _retainingPathRepository,
+                _objectRepository,
+                queue: app.queue)
+            .element
+      ];
+    } else if (obj is ObjectPool) {
+      container.children = <Element>[
+        new ObjectPoolViewElement(
+                app.vm,
+                obj.isolate,
+                obj,
+                app.events,
+                app.notifications,
+                _objectPoolRepository,
+                _retainedSizeRepository,
+                _reachableSizeRepository,
+                _inboundReferencesRepository,
+                _retainingPathRepository,
+                _objectRepository,
+                queue: app.queue)
+            .element
+      ];
+    } else if (obj is Script) {
+      var pos;
+      if (app.locationManager.internalArguments['pos'] != null) {
+        try {
+          pos = int.parse(app.locationManager.internalArguments['pos']);
+        } catch (_) {}
+      }
+      container.children = <Element>[
+        new ScriptViewElement(
+                app.vm,
+                obj.isolate,
+                obj,
+                app.events,
+                app.notifications,
+                _scriptRepository,
+                _retainedSizeRepository,
+                _reachableSizeRepository,
+                _inboundReferencesRepository,
+                _retainingPathRepository,
+                _objectRepository,
+                pos: pos,
+                queue: app.queue)
+            .element
+      ];
+    } else if (obj is HeapObject) {
+      container.children = <Element>[
+        new ObjectViewElement(
+                app.vm,
+                obj.isolate,
+                obj,
+                app.events,
+                app.notifications,
+                _objectRepository,
+                _retainedSizeRepository,
+                _reachableSizeRepository,
+                _inboundReferencesRepository,
+                _retainingPathRepository,
+                queue: app.queue)
+            .element
+      ];
+    } else if (obj is Sentinel) {
+      container.children = <Element>[
+        new SentinelViewElement(
+                app.vm, obj.isolate, obj, app.events, app.notifications,
+                queue: app.queue)
+            .element
+      ];
+    } else {
+      container.children = <Element>[
+        new JSONViewElement(obj, app.notifications, queue: app.queue).element
+      ];
+    }
+  }
+}
+
+/// Class tree page.
+class ClassTreePage extends SimplePage {
+  ClassTreePage(app) : super('class-tree', 'class-tree', app);
+
+  final DivElement container = new DivElement();
+
+  @override
+  void onInstall() {
+    element = container;
+  }
+
+  void _visit(Uri uri) {
+    super._visit(uri);
+    getIsolate(uri).then((isolate) {
+      container.children = <Element>[
+        new ClassTreeElement(app.vm, isolate, app.events, app.notifications,
+                _classRepository)
+            .element
+      ];
+    });
+  }
+}
+
+class DebuggerPage extends MatchingPage {
+  DebuggerPage(app) : super('debugger', app);
+
+  final DivElement container = new DivElement();
+
+  void _visit(Uri uri) {
+    super._visit(uri);
+    getIsolate(uri).then((isolate) async {
+      container.children = <Element>[
+        new DebuggerPageElement(
+                isolate, _objectRepository, _scriptRepository, app.events)
+            .element
+      ];
+    });
+  }
+
+  void onInstall() {
+    if (element == null) {
+      element = container;
+    }
+    assert(element != null);
+  }
+
+  @override
+  void onUninstall() {
+    super.onUninstall();
+    container.children = const [];
+  }
+}
+
+class ObjectStorePage extends MatchingPage {
+  ObjectStorePage(app) : super('object-store', app);
+
+  final DivElement container = new DivElement();
+
+  void _visit(Uri uri) {
+    super._visit(uri);
+    getIsolate(uri).then((isolate) async {
+      container.children = <Element>[
+        new ObjectStoreViewElement(isolate.vm, isolate, app.events,
+                app.notifications, _objectstoreRepository, _objectRepository)
+            .element
+      ];
+    });
+  }
+
+  void onInstall() {
+    if (element == null) {
+      element = container;
+    }
+    assert(element != null);
+  }
+}
+
+class CpuProfilerPage extends MatchingPage {
+  CpuProfilerPage(app) : super('profiler', app);
+
+  final DivElement container = new DivElement();
+
+  void _visit(Uri uri) {
+    super._visit(uri);
+    getIsolate(uri).then((isolate) {
+      container.children = <Element>[
+        new CpuProfileElement(isolate.vm, isolate, app.events,
+                app.notifications, _isolateSampleProfileRepository)
+            .element
+      ];
+    });
+  }
+
+  void onInstall() {
+    if (element == null) {
+      element = container;
+    }
+    assert(element != null);
+  }
+}
+
+class TableCpuProfilerPage extends MatchingPage {
+  TableCpuProfilerPage(app) : super('profiler-table', app);
+
+  final DivElement container = new DivElement();
+
+  void _visit(Uri uri) {
+    super._visit(uri);
+    getIsolate(uri).then((isolate) {
+      container.children = <Element>[
+        new CpuProfileTableElement(isolate.vm, isolate, app.events,
+                app.notifications, _isolateSampleProfileRepository)
+            .element
+      ];
+    });
+  }
+
+  void onInstall() {
+    if (element == null) {
+      element = container;
+    }
+    assert(element != null);
+  }
+}
+
+class AllocationProfilerPage extends MatchingPage {
+  AllocationProfilerPage(app) : super('allocation-profiler', app);
+
+  final DivElement container = new DivElement();
+
+  void _visit(Uri uri) {
+    super._visit(uri);
+    getIsolate(uri).then((isolate) {
+      container.children = <Element>[
+        new AllocationProfileElement(isolate.vm, isolate, app.events,
+                app.notifications, _allocationProfileRepository,
+                queue: app.queue)
+            .element
+      ];
+    });
+  }
+
+  void onInstall() {
+    if (element == null) {
+      element = container;
+    }
+    app.startGCEventListener();
+  }
+
+  @override
+  void onUninstall() {
+    super.onUninstall();
+    app.stopGCEventListener();
+    container.children = const [];
+  }
+}
+
+class PortsPage extends MatchingPage {
+  PortsPage(app) : super('ports', app);
+
+  final DivElement container = new DivElement();
+
+  void _visit(Uri uri) {
+    super._visit(uri);
+    getIsolate(uri).then((isolate) {
+      container.children = <Element>[
+        new PortsElement(isolate.vm, isolate, app.events, app.notifications,
+                _portsRepository, _objectRepository,
+                queue: app.queue)
+            .element
+      ];
+    });
+  }
+
+  void onInstall() {
+    if (element == null) {
+      element = container;
+    }
+  }
+}
+
+class PersistentHandlesPage extends MatchingPage {
+  PersistentHandlesPage(app) : super('persistent-handles', app);
+
+  final DivElement container = new DivElement();
+
+  void _visit(Uri uri) {
+    super._visit(uri);
+    getIsolate(uri).then((isolate) {
+      container.children = <Element>[
+        new PersistentHandlesPageElement(
+                isolate.vm,
+                isolate,
+                app.events,
+                app.notifications,
+                _persistentHandlesRepository,
+                _objectRepository,
+                queue: app.queue)
+            .element
+      ];
+    });
+  }
+
+  void onInstall() {
+    if (element == null) {
+      element = container;
+    }
+  }
+}
+
+class HeapMapPage extends MatchingPage {
+  HeapMapPage(app) : super('heap-map', app);
+
+  final DivElement container = new DivElement();
+
+  void _visit(Uri uri) {
+    super._visit(uri);
+    getIsolate(uri).then((isolate) {
+      container.children = <Element>[
+        new HeapMapElement(isolate.vm, isolate, app.events, app.notifications,
+                queue: app.queue)
+            .element
+      ];
+    });
+  }
+
+  void onInstall() {
+    if (element == null) {
+      element = container;
+    }
+  }
+}
+
+class HeapSnapshotPage extends MatchingPage {
+  HeapSnapshotPage(app) : super('heap-snapshot', app);
+
+  final DivElement container = new DivElement();
+
+  void _visit(Uri uri) {
+    super._visit(uri);
+    getIsolate(uri).then((isolate) {
+      container.children = <Element>[
+        new HeapSnapshotElement(isolate.vm, isolate, app.events,
+                app.notifications, _heapSnapshotRepository, _objectRepository,
+                queue: app.queue)
+            .element
+      ];
+    });
+  }
+
+  void onInstall() {
+    if (element == null) {
+      element = container;
+    }
+  }
+}
+
+class LoggingPage extends MatchingPage {
+  LoggingPage(app) : super('logging', app);
+
+  final DivElement container = new DivElement();
+
+  @override
+  void onInstall() {
+    element = container;
+    container.children = const [];
+    app.startLoggingEventListener();
+  }
+
+  @override
+  void onUninstall() {
+    super.onUninstall();
+    container.children = const [];
+    app.stopLoggingEventListener();
+  }
+
+  void _visit(Uri uri) {
+    assert(element != null);
+    assert(canVisit(uri));
+    getIsolate(uri).then((isolate) {
+      container.children = <Element>[
+        new LoggingPageElement(app.vm, isolate, app.events, app.notifications,
+                queue: app.queue)
+            .element
+      ];
+    });
+  }
+}
+
+class ErrorViewPage extends Page {
+  ErrorViewPage(app) : super(app);
+
+  void onInstall() {
+    element = new ErrorViewElement(
+            app.notifications, app.lastErrorOrException as DartError,
+            queue: app.queue)
+        .element;
+  }
+
+  void _visit(Uri uri) {
+    assert(element != null);
+    assert(canVisit(uri));
+  }
+
+  // TODO(turnidge): How to test this page?
+  bool canVisit(Uri uri) => uri.path == 'error';
+}
+
+class VMConnectPage extends Page {
+  VMConnectPage(app) : super(app);
+
+  void onInstall() {
+    if (element == null) {
+      element = new VMConnectElement(ObservatoryApplication.app.targets,
+              ObservatoryApplication.app.notifications,
+              queue: ObservatoryApplication.app.queue)
+          .element;
+    }
+    assert(element != null);
+  }
+
+  void _visit(Uri uri) {
+    assert(element != null);
+    assert(canVisit(uri));
+  }
+
+  bool canVisit(Uri uri) => uri.path == 'vm-connect';
+}
+
+class IsolateReconnectPage extends Page {
+  IsolateReconnectPage(app) : super(app);
+
+  final DivElement container = new DivElement();
+
+  void onInstall() {
+    element = container;
+  }
+
+  void _visit(Uri uri) {
+    app.vm.reload();
+    container.children = <Element>[
+      new IsolateReconnectElement(
+              app.vm,
+              app.events,
+              app.notifications,
+              uri.queryParameters['isolateId'],
+              Uri.parse(uri.queryParameters['originalUri']))
+          .element
+    ];
+    assert(element != null);
+    assert(canVisit(uri));
+  }
+
+  bool canVisit(Uri uri) => uri.path == 'isolate-reconnect';
+}
+
+class MetricsPage extends MatchingPage {
+  MetricsPage(app) : super('metrics', app);
+
+  final DivElement container = new DivElement();
+
+  Isolate lastIsolate;
+
+  void _visit(Uri uri) {
+    super._visit(uri);
+    getIsolate(uri).then((isolate) async {
+      lastIsolate = isolate;
+      container.children = const [];
+      await _metricRepository.startSampling(isolate);
+      container.children = <Element>[
+        new MetricsPageElement(isolate.vm, isolate, app.events,
+                app.notifications, _metricRepository,
+                queue: app.queue)
+            .element
+      ];
+    });
+  }
+
+  void onInstall() {
+    if (element == null) {
+      element = container;
+    }
+  }
+
+  @override
+  void onUninstall() {
+    super.onUninstall();
+    _metricRepository.stopSampling(lastIsolate);
+    container.children = const [];
+  }
+}
+
+class TimelinePage extends Page {
+  TimelinePage(app) : super(app);
+
+  void onInstall() {
+    element = new TimelinePageElement(
+            app.vm, _timelineRepository, app.events, app.notifications,
+            queue: app.queue)
+        .element;
+  }
+
+  void _visit(Uri uri) {
+    assert(canVisit(uri));
+  }
+
+  bool canVisit(Uri uri) => uri.path == 'timeline';
+}
+
+class TimelineDashboardPage extends Page {
+  TimelineDashboardPage(app) : super(app);
+
+  DivElement container = new DivElement();
+
+  void onInstall() {
+    if (element == null) {
+      element = container;
+    }
+  }
+
+  void _visit(Uri uri) {
+    assert(canVisit(uri));
+    app.vm.load().then((_) {
+      container.children = <Element>[
+        new TimelineDashboardElement(
+                app.vm, _timelineRepository, app.notifications,
+                queue: app.queue)
+            .element
+      ];
+    });
+  }
+
+  @override
+  void onUninstall() {
+    super.onUninstall();
+    container.children = const [];
+  }
+
+  bool canVisit(Uri uri) => uri.path == 'timeline-dashboard';
+}
+
+class ProcessSnapshotPage extends Page {
+  ProcessSnapshotPage(app) : super(app);
+
+  void onInstall() {
+    element = new ProcessSnapshotElement(app.vm, app.events, app.notifications,
+            queue: app.queue)
+        .element;
+  }
+
+  void _visit(Uri uri) {
+    assert(canVisit(uri));
+  }
+
+  bool canVisit(Uri uri) => uri.path == 'process-snapshot';
+}
diff --git a/runtime/observatory_2/lib/src/app/settings.dart b/runtime/observatory_2/lib/src/app/settings.dart
new file mode 100644
index 0000000..da6d8a8
--- /dev/null
+++ b/runtime/observatory_2/lib/src/app/settings.dart
@@ -0,0 +1,45 @@
+// Copyright (c) 2014, 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;
+
+/// Static settings database.
+class _Settings {
+  static Storage _storage = window.localStorage;
+
+  /// Associated [value] with [key]. [value] must be JSON encodable.
+  static void set(String key, dynamic value) {
+    _storage[key] = json.encode(value);
+  }
+
+  /// Get value associated with [key]. Return value will be a JSON encodable
+  /// object.
+  static dynamic get(String key) {
+    var value = _storage[key];
+    if (value == null) {
+      return null;
+    }
+    return json.decode(value);
+  }
+}
+
+/// A group of settings each prefixed with group name and a dot.
+class SettingsGroup {
+  /// Group name
+  final String group;
+
+  SettingsGroup(this.group);
+
+  String _fullKey(String key) => '$group.$key';
+
+  void set(String key, dynamic value) {
+    var fullKey = _fullKey(key);
+    _Settings.set(fullKey, value);
+  }
+
+  dynamic get(String key) {
+    var fullKey = _fullKey(key);
+    return _Settings.get(fullKey);
+  }
+}
diff --git a/runtime/observatory_2/lib/src/app/view_model.dart b/runtime/observatory_2/lib/src/app/view_model.dart
new file mode 100644
index 0000000..f210e13
--- /dev/null
+++ b/runtime/observatory_2/lib/src/app/view_model.dart
@@ -0,0 +1,104 @@
+// Copyright (c) 2014, 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;
+
+typedef String ValueFormatter(dynamic value);
+
+class SortedTableColumn {
+  static String toStringFormatter(dynamic v) {
+    return v != null ? v.toString() : '<null>';
+  }
+
+  final String label;
+  final ValueFormatter formatter;
+  SortedTableColumn.withFormatter(this.label, this.formatter);
+  SortedTableColumn(this.label) : formatter = toStringFormatter;
+}
+
+class SortedTableRow {
+  final List values;
+  SortedTableRow(this.values);
+}
+
+class SortedTable {
+  final List<SortedTableColumn> columns;
+  final List<SortedTableRow> rows = <SortedTableRow>[];
+  final List<int> sortedRows = [];
+
+  SortedTable(this.columns);
+
+  int _sortColumnIndex = 0;
+  set sortColumnIndex(var index) {
+    assert(index >= 0);
+    assert(index < columns.length);
+    _sortColumnIndex = index;
+  }
+
+  int get sortColumnIndex => _sortColumnIndex;
+  bool _sortDescending = true;
+  bool get sortDescending => _sortDescending;
+  set sortDescending(var descending) {
+    _sortDescending = descending;
+  }
+
+  dynamic getSortKeyFor(int row, int col) {
+    return rows[row].values[col];
+  }
+
+  int _sortFuncDescending(int i, int j) {
+    var a = getSortKeyFor(i, _sortColumnIndex);
+    var b = getSortKeyFor(j, _sortColumnIndex);
+    return b.compareTo(a);
+  }
+
+  int _sortFuncAscending(int i, int j) {
+    var a = getSortKeyFor(i, _sortColumnIndex);
+    var b = getSortKeyFor(j, _sortColumnIndex);
+    return a.compareTo(b);
+  }
+
+  void sort() {
+    assert(_sortColumnIndex >= 0);
+    assert(_sortColumnIndex < columns.length);
+    if (_sortDescending) {
+      sortedRows.sort(_sortFuncDescending);
+    } else {
+      sortedRows.sort(_sortFuncAscending);
+    }
+  }
+
+  void clearRows() {
+    rows.clear();
+    sortedRows.clear();
+  }
+
+  void addRow(SortedTableRow row) {
+    sortedRows.add(rows.length);
+    rows.add(row);
+  }
+
+  String getFormattedValue(int row, int column) {
+    var value = getValue(row, column);
+    var formatter = columns[column].formatter;
+    return formatter(value);
+  }
+
+  String getColumnLabel(int column) {
+    assert(column >= 0);
+    assert(column < columns.length);
+    // TODO(johnmccutchan): Move expander display decisions into html once
+    // tables and templates are better supported.
+    const arrowUp = '\u25BC';
+    const arrowDown = '\u25B2';
+    if (column != _sortColumnIndex) {
+      return columns[column].label + '\u2003';
+    }
+    return columns[column].label + (_sortDescending ? arrowUp : arrowDown);
+  }
+
+  dynamic getValue(int row, int column) {
+    return rows[row].values[column];
+  }
+}
diff --git a/runtime/observatory_2/lib/src/cli/command.dart b/runtime/observatory_2/lib/src/cli/command.dart
new file mode 100644
index 0000000..0a0fc42
--- /dev/null
+++ b/runtime/observatory_2/lib/src/cli/command.dart
@@ -0,0 +1,280 @@
+// Copyright (c) 2015, 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 cli;
+
+// Splits a line into a list of string args.  Each arg retains any
+// trailing whitespace so that we can reconstruct the original command
+// line from the pieces.
+List<String> _splitLine(String line) {
+  line = line.trimLeft();
+  var args = <String>[];
+  int pos = 0;
+  while (pos < line.length) {
+    int startPos = pos;
+
+    // Advance to end of word.
+    for (; pos < line.length && line[pos] != ' '; pos++);
+
+    // Advance to end of spaces.
+    for (; pos < line.length && line[pos] == ' '; pos++);
+
+    args.add(line.substring(startPos, pos));
+  }
+  return args;
+}
+
+// Concatenates the first 'count' args.
+String _concatArgs(List<String> args, int count) {
+  if (count == 0) {
+    return '';
+  }
+  return '${args.sublist(0, count).join('')}';
+}
+
+// Shared functionality for RootCommand and Command.
+abstract class _CommandBase {
+  _CommandBase(List<Command> children) {
+    assert(children != null);
+    _children.addAll(children);
+    for (var child in _children) {
+      child._parent = this;
+    }
+  }
+
+  // A command may optionally have sub-commands.
+  List<Command> _children = <Command>[];
+
+  _CommandBase _parent;
+  int get _depth => (_parent == null ? 0 : _parent._depth + 1);
+
+  // Override in subclasses to provide command-specific argument completion.
+  //
+  // Given a list of arguments to this command, provide a list of
+  // possible completions for those arguments.
+  Future<List<String>> complete(List<String> args) =>
+      new Future.value(<String>[]);
+
+  // Override in subclasses to provide command-specific execution.
+  Future run(List<String> args);
+
+  // Returns a list of local subcommands which match the args.
+  List<Command> _matchLocal(String argWithSpace, bool preferExact) {
+    var matches = <Command>[];
+    var arg = argWithSpace.trimRight();
+    for (var child in _children) {
+      if (child.name.startsWith(arg)) {
+        if (preferExact && ((child.name == arg) || (child.alias == arg))) {
+          return [child];
+        }
+        matches.add(child);
+      }
+    }
+    return matches;
+  }
+
+  // Returns the set of commands could be triggered by a list of
+  // arguments.
+  List<Command> _match(List<String> args, bool preferExact) {
+    if (args.isEmpty) {
+      return <Command>[];
+    }
+    bool lastArg = (args.length == 1);
+    var matches = _matchLocal(args[0], !lastArg || preferExact);
+    if (matches.isEmpty) {
+      return <Command>[];
+    } else if (matches.length == 1) {
+      var childMatches = matches[0]._match(args.sublist(1), preferExact);
+      if (childMatches.isEmpty) {
+        return matches;
+      } else {
+        return childMatches;
+      }
+    } else {
+      return matches;
+    }
+  }
+
+  // Builds a list of completions for this command.
+  Future<List<String>> _buildCompletions(
+      List<String> args, bool addEmptyString) {
+    return complete(args.sublist(_depth, args.length)).then((completions) {
+      if (addEmptyString &&
+          completions.isEmpty &&
+          args[args.length - 1] == '') {
+        // Special case allowance for an empty particle at the end of
+        // the command.
+        completions = <String>[''];
+      }
+      var prefix = _concatArgs(args, _depth);
+      return completions.map((str) => '${prefix}${str}').toList();
+    });
+  }
+}
+
+// The root of a tree of commands.
+class RootCommand extends _CommandBase {
+  RootCommand(List<Command> children, [List<String> history])
+      : this._(children, history ?? ['']);
+
+  RootCommand._(List<Command> children, List<String> history)
+      : history = history,
+        historyPos = history.length - 1,
+        super(children);
+
+  // Provides a list of possible completions for a line of text.
+  Future<List<String>> completeCommand(String line) {
+    var args = _splitLine(line);
+    bool showAll = line.endsWith(' ') || args.isEmpty;
+    if (showAll) {
+      // Adding an empty string to the end causes us to match all
+      // subcommands of the last command.
+      args.add('');
+    }
+    var commands = _match(args, false);
+    if (commands.isEmpty) {
+      // No matching commands.
+      return new Future.value(<String>[]);
+    }
+    int matchLen = commands[0]._depth;
+    if (matchLen < args.length) {
+      // We were able to find a command which matches a prefix of the
+      // args, but not the full list.
+      if (commands.length == 1) {
+        // The matching command is unique.  Attempt to provide local
+        // argument completion from the command.
+        return commands[0]._buildCompletions(args, true);
+      } else {
+        // An ambiguous prefix match leaves us nowhere.  The user is
+        // typing a bunch of stuff that we don't know how to complete.
+        return new Future.value(<String>[]);
+      }
+    }
+
+    // We have found a set of commands which match all of the args.
+    // Return the completion strings.
+    var prefix = _concatArgs(args, args.length - 1);
+    var completions =
+        commands.map((command) => '${prefix}${command.name} ').toList();
+    if (matchLen == args.length) {
+      // If we are showing all possiblities, also include local
+      // completions for the parent command.
+      return commands[0]
+          ._parent
+          ._buildCompletions(args, false)
+          .then((localCompletions) {
+        completions.addAll(localCompletions);
+        return completions;
+      });
+    }
+    return new Future.value(completions);
+  }
+
+  // Runs a command.
+  Future runCommand(String line) {
+    _historyAdvance(line);
+    var args = _splitLine(line);
+    var commands = _match(args, true);
+    if (commands.isEmpty) {
+      return new Future.error(new NoSuchCommandException(line));
+    } else if (commands.length == 1) {
+      return commands[0].run(args.sublist(commands[0]._depth));
+    } else {
+      return new Future.error(new AmbiguousCommandException(line, commands));
+    }
+  }
+
+  // Find all matching commands.  Useful for implementing help systems.
+  List<Command> matchCommand(List<String> args, bool preferExact) {
+    if (args.isEmpty) {
+      // Adding an empty string to the end causes us to match all
+      // subcommands of the last command.
+      args.add('');
+    }
+    return _match(args, preferExact);
+  }
+
+  // Command line history always contains one slot to hold the current
+  // line, so we start off with one entry.
+  List<String> history;
+  int historyPos;
+
+  String historyPrev(String line) {
+    if (historyPos == 0) {
+      return line;
+    }
+    history[historyPos] = line;
+    historyPos--;
+    return history[historyPos];
+  }
+
+  String historyNext(String line) {
+    if (historyPos == history.length - 1) {
+      return line;
+    }
+    history[historyPos] = line;
+    historyPos++;
+    return history[historyPos];
+  }
+
+  void _historyAdvance(String line) {
+    // Replace the last history line.
+    historyPos = history.length - 1;
+    history[historyPos] = line;
+
+    // Create an empty spot for the next line.
+    history.add('');
+    historyPos++;
+  }
+
+  Future run(List<String> args) {
+    throw 'should-not-execute-the-root-command';
+  }
+
+  toString() => 'RootCommand';
+}
+
+// A node in the command tree.
+abstract class Command extends _CommandBase {
+  Command(this.name, List<Command> children) : super(children);
+
+  final String name;
+  String alias;
+
+  String get fullName {
+    if (_parent is RootCommand) {
+      return name;
+    } else {
+      Command parent = _parent;
+      return '${parent.fullName} $name';
+    }
+  }
+
+  toString() => 'Command(${name})';
+}
+
+abstract class CommandException implements Exception {}
+
+class AmbiguousCommandException extends CommandException {
+  AmbiguousCommandException(this.command, this.matches);
+
+  final String command;
+  final List<Command> matches;
+
+  @override
+  String toString() {
+    List<String> matchNames =
+        matches.map((Command command) => '${command.fullName}').toList();
+    return "Command '$command' is ambiguous: $matchNames";
+  }
+}
+
+class NoSuchCommandException extends CommandException {
+  NoSuchCommandException(this.command);
+
+  final String command;
+
+  @override
+  String toString() => "No such command: '$command'";
+}
diff --git a/runtime/observatory_2/lib/src/debugger/debugger.dart b/runtime/observatory_2/lib/src/debugger/debugger.dart
new file mode 100644
index 0000000..fb297c7
--- /dev/null
+++ b/runtime/observatory_2/lib/src/debugger/debugger.dart
@@ -0,0 +1,14 @@
+// Copyright (c) 2015, 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 debugger;
+
+// TODO(turnidge): Move more of ObservatoryDebugger to this class.
+abstract class Debugger {
+  VM get vm;
+  Isolate get isolate;
+  M.ObjectRepository objects;
+  ServiceMap get stack;
+  int get currentFrame;
+}
diff --git a/runtime/observatory_2/lib/src/debugger/debugger_location.dart b/runtime/observatory_2/lib/src/debugger/debugger_location.dart
new file mode 100644
index 0000000..a186fd4
--- /dev/null
+++ b/runtime/observatory_2/lib/src/debugger/debugger_location.dart
@@ -0,0 +1,462 @@
+// Copyright (c) 2015, 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 debugger;
+
+class DebuggerLocation {
+  DebuggerLocation.file(this.script, this.line, this.col);
+  DebuggerLocation.func(this.function);
+  DebuggerLocation.error(this.errorMessage);
+
+  static RegExp sourceLocMatcher = new RegExp(r'^([^\d:][^:]+:)?(\d+)(:\d+)?');
+  static RegExp packageLocMatcher =
+      new RegExp(r'^package:([^\d:][^:]+:)?(\d+)(:\d+)?');
+  static RegExp functionMatcher = new RegExp(r'^([^.]+)([.][^.]+)?');
+
+  /// Parses a source location description.
+  ///
+  /// Formats:
+  ///   ''                     -  current position
+  ///   13                     -  line 13, current script
+  ///   13:20                  -  line 13, col 20, current script
+  ///   script.dart:13         -  line 13, script.dart
+  ///   script.dart:13:20      -  line 13, col 20, script.dart
+  ///   package:a/b.dart:13    -  line 13, "b.dart" in package "a".
+  ///   package:a/b.dart:13:20 -  line 13, col 20, "b.dart" in package "a".
+  ///   main                   -  function
+  ///   FormatException        -  constructor
+  ///   _SHA1._updateHash      -  method
+  static Future<DebuggerLocation> parse(Debugger debugger, String locDesc) {
+    if (locDesc == '') {
+      // Special case: '' means return current location.
+      return _currentLocation(debugger);
+    }
+
+    // Parse the location description.
+    var match = sourceLocMatcher.firstMatch(locDesc);
+    if (match != null) {
+      return _parseScriptLine(debugger, match);
+    }
+    match = packageLocMatcher.firstMatch(locDesc);
+    if (match != null) {
+      return _parseScriptLine(debugger, match, package: true);
+    }
+    match = functionMatcher.firstMatch(locDesc);
+    if (match != null) {
+      return _parseFunction(debugger, match);
+    }
+    return new Future.value(
+        new DebuggerLocation.error("Invalid source location '${locDesc}'"));
+  }
+
+  static Future<Frame> _currentFrame(Debugger debugger) async {
+    ServiceMap stack = debugger.stack;
+    if (stack == null || stack['frames'].length == 0) {
+      return null;
+    }
+    return stack['frames'][debugger.currentFrame];
+  }
+
+  static Future<DebuggerLocation> _currentLocation(Debugger debugger) async {
+    var frame = await _currentFrame(debugger);
+    if (frame == null) {
+      return new DebuggerLocation.error(
+          'A script must be provided when the stack is empty');
+    }
+    Script script = frame.location.script;
+    await script.load();
+    var line = script.tokenToLine(frame.location.tokenPos);
+    var col = script.tokenToCol(frame.location.tokenPos);
+    return new DebuggerLocation.file(script, line, col);
+  }
+
+  static Future<DebuggerLocation> _parseScriptLine(
+      Debugger debugger, Match match,
+      {bool package = false}) async {
+    var scriptName = match.group(1);
+    if (package) {
+      scriptName = "package:$scriptName";
+    }
+    if (scriptName != null) {
+      scriptName = scriptName.substring(0, scriptName.length - 1);
+    }
+    var lineStr = match.group(2);
+    assert(lineStr != null);
+    var colStr = match.group(3);
+    if (colStr != null) {
+      colStr = colStr.substring(1);
+    }
+    var line = int.tryParse(lineStr) ?? -1;
+    var col = (colStr != null ? int.tryParse(colStr) ?? -1 : null);
+    if (line == -1) {
+      return new Future.value(
+          new DebuggerLocation.error("Line '${lineStr}' must be an integer"));
+    }
+    if (col == -1) {
+      return new Future.value(
+          new DebuggerLocation.error("Column '${colStr}' must be an integer"));
+    }
+
+    if (scriptName != null) {
+      // Resolve the script.
+      Set<Script> scripts = await _lookupScript(debugger.isolate, scriptName);
+      if (scripts.length == 0) {
+        scripts =
+            await _lookupScript(debugger.isolate, scriptName, useUri: true);
+      }
+      if (scripts.length == 0) {
+        return new DebuggerLocation.error("Script '${scriptName}' not found");
+      } else if (scripts.length == 1) {
+        return new DebuggerLocation.file(scripts.single, line, col);
+      } else {
+        // TODO(turnidge): Allow the user to disambiguate.
+        return new DebuggerLocation.error(
+            "Script '${scriptName}' is ambiguous");
+      }
+    } else {
+      // No script provided.  Default to top of stack for now.
+      var frame = await _currentFrame(debugger);
+      if (frame == null) {
+        return new Future.value(new DebuggerLocation.error(
+            'A script must be provided when the stack is empty'));
+      }
+      Script script = frame.location.script;
+      await script.load();
+      return new DebuggerLocation.file(script, line, col);
+    }
+  }
+
+  static Future<Set<Script>> _lookupScript(Isolate isolate, String name,
+      {bool allowPrefix: false, bool useUri: false}) {
+    var pending = <Future>[];
+    for (var lib in isolate.libraries) {
+      if (!lib.loaded) {
+        pending.add(lib.load());
+      }
+    }
+    return Future.wait(pending).then((_) {
+      var matches = <Script>{};
+      for (var lib in isolate.libraries) {
+        for (var script in lib.scripts) {
+          final String haystack = useUri ? script.uri : script.name;
+          if (allowPrefix) {
+            if (haystack.startsWith(name)) {
+              matches.add(script);
+            }
+          } else {
+            if (name == haystack) {
+              matches.add(script);
+            }
+          }
+        }
+      }
+      return matches;
+    });
+  }
+
+  static List<ServiceFunction> _lookupFunction(Isolate isolate, String name,
+      {bool allowPrefix: false}) {
+    var matches = <ServiceFunction>[];
+    for (var lib in isolate.libraries) {
+      assert(lib.loaded);
+      for (var function in lib.functions) {
+        if (allowPrefix) {
+          if (function.name.startsWith(name)) {
+            matches.add(function);
+          }
+        } else {
+          if (name == function.name) {
+            matches.add(function);
+          }
+        }
+      }
+    }
+    return matches;
+  }
+
+  static Future<List<Class>> _lookupClass(Isolate isolate, String name,
+      {bool allowPrefix: false}) async {
+    if (isolate == null) {
+      return [];
+    }
+    var pending = <Future>[];
+    for (var lib in isolate.libraries) {
+      assert(lib.loaded);
+      for (var cls in lib.classes) {
+        if (!cls.loaded) {
+          pending.add(cls.load());
+        }
+      }
+    }
+    await Future.wait(pending);
+    var matches = <Class>[];
+    for (var lib in isolate.libraries) {
+      for (var cls in lib.classes) {
+        if (allowPrefix) {
+          if (cls.name.startsWith(name)) {
+            matches.add(cls);
+          }
+        } else {
+          if (name == cls.name) {
+            matches.add(cls);
+          }
+        }
+      }
+    }
+    return matches;
+  }
+
+  static ServiceFunction _getConstructor(Class cls, String name) {
+    for (var function in cls.functions) {
+      assert(cls.loaded);
+      if (name == function.name) {
+        return function;
+      }
+    }
+    return null;
+  }
+
+  // TODO(turnidge): This does not handle named functions which are
+  // inside of named functions, e.g. foo.bar.baz.
+  static Future<DebuggerLocation> _parseFunction(
+      Debugger debugger, Match match) {
+    Isolate isolate = debugger.isolate;
+    var base = match.group(1);
+    var qualifier = match.group(2);
+    assert(base != null);
+
+    return _lookupClass(isolate, base).then((classes) {
+      var functions = [];
+      if (qualifier == null) {
+        // Unqualified name is either a function or a constructor.
+        functions.addAll(_lookupFunction(isolate, base));
+
+        for (var cls in classes) {
+          // Look for a self-named constructor.
+          var constructor = _getConstructor(cls, cls.name);
+          if (constructor != null) {
+            functions.add(constructor);
+          }
+        }
+      } else {
+        // Qualified name.
+        var functionName = qualifier.substring(1);
+        for (var cls in classes) {
+          assert(cls.loaded);
+          for (var function in cls.functions) {
+            if (function.kind == M.FunctionKind.constructor) {
+              // Constructor names are class-qualified.
+              if (match.group(0) == function.name) {
+                functions.add(function);
+              }
+            } else {
+              if (functionName == function.name) {
+                functions.add(function);
+              }
+            }
+          }
+        }
+      }
+      if (functions.length == 0) {
+        return new DebuggerLocation.error(
+            "Function '${match.group(0)}' not found");
+      } else if (functions.length == 1) {
+        return new DebuggerLocation.func(functions[0]);
+      } else {
+        // TODO(turnidge): Allow the user to disambiguate.
+        return new DebuggerLocation.error(
+            "Function '${match.group(0)}' is ambiguous");
+      }
+    });
+  }
+
+  static RegExp partialSourceLocMatcher =
+      new RegExp(r'^([^\d:]?[^:]+[:]?)?(\d+)?([:]\d*)?');
+  static RegExp partialFunctionMatcher = new RegExp(r'^([^.]*)([.][^.]*)?');
+
+  /// Completes a partial source location description.
+  static Future<List<String>> complete(Debugger debugger, String locDesc) {
+    var pending = <Future<List<String>>>[];
+    var match = partialFunctionMatcher.firstMatch(locDesc);
+    if (match != null) {
+      pending.add(_completeFunction(debugger, match));
+    }
+
+    match = partialSourceLocMatcher.firstMatch(locDesc);
+    if (match != null) {
+      pending.add(_completeFile(debugger, match));
+    }
+
+    return Future.wait(pending).then((List<List<String>> responses) {
+      var completions = <String>[];
+      for (var response in responses) {
+        completions.addAll(response);
+      }
+      return completions;
+    });
+  }
+
+  static Future<List<String>> _completeFunction(
+      Debugger debugger, Match match) {
+    Isolate isolate = debugger.isolate;
+    var base = match.group(1);
+    var qualifier = match.group(2);
+    base = (base == null ? '' : base);
+
+    if (qualifier == null) {
+      return _lookupClass(isolate, base, allowPrefix: true).then((classes) {
+        var completions = <String>[];
+
+        // Complete top-level function names.
+        var functions = _lookupFunction(isolate, base, allowPrefix: true);
+        var funcNames = functions.map((f) => f.name).toList();
+        funcNames.sort();
+        completions.addAll(funcNames);
+
+        // Complete class names.
+        var classNames = classes.map((f) => f.name).toList();
+        classNames.sort();
+        completions.addAll(classNames);
+
+        return completions;
+      });
+    } else {
+      return _lookupClass(isolate, base, allowPrefix: false).then((classes) {
+        var completions = <String>[];
+        for (var cls in classes) {
+          for (var function in cls.functions) {
+            if (function.kind == M.FunctionKind.constructor) {
+              if (function.name.startsWith(match.group(0))) {
+                completions.add(function.name);
+              }
+            } else {
+              if (function.qualifiedName.startsWith(match.group(0))) {
+                completions.add(function.qualifiedName);
+              }
+            }
+          }
+        }
+        completions.sort();
+        return completions;
+      });
+    }
+  }
+
+  static bool _startsWithDigit(String s) {
+    return '0'.compareTo(s[0]) <= 0 && '9'.compareTo(s[0]) >= 0;
+  }
+
+  static Future<List<String>> _completeFile(
+      Debugger debugger, Match match) async {
+    var scriptName;
+    var scriptNameComplete = false;
+    var lineStr;
+    var lineStrComplete = false;
+    var colStr;
+    if (_startsWithDigit(match.group(1))) {
+      // CASE 1: We have matched a prefix of (lineStr:)(colStr)
+      var frame = await _currentFrame(debugger);
+      if (frame == null) {
+        return [];
+      }
+      scriptName = frame.location.script.name;
+      scriptNameComplete = true;
+      lineStr = match.group(1);
+      lineStr = (lineStr == null ? '' : lineStr);
+      if (lineStr.endsWith(':')) {
+        lineStr = lineStr.substring(0, lineStr.length - 1);
+        lineStrComplete = true;
+      }
+      colStr = match.group(2);
+      colStr = (colStr == null ? '' : colStr);
+    } else {
+      // CASE 2: We have matched a prefix of (scriptName:)(lineStr)(:colStr)
+      scriptName = match.group(1);
+      scriptName = (scriptName == null ? '' : scriptName);
+      if (scriptName.endsWith(':')) {
+        scriptName = scriptName.substring(0, scriptName.length - 1);
+        scriptNameComplete = true;
+      }
+      lineStr = match.group(2);
+      lineStr = (lineStr == null ? '' : lineStr);
+      colStr = match.group(3);
+      colStr = (colStr == null ? '' : colStr);
+      if (colStr.startsWith(':')) {
+        lineStrComplete = true;
+        colStr = colStr.substring(1);
+      }
+    }
+
+    if (!scriptNameComplete) {
+      // The script name is incomplete.  Complete it.
+      var scripts =
+          await _lookupScript(debugger.isolate, scriptName, allowPrefix: true);
+      var completions = <String>[];
+      for (var script in scripts) {
+        completions.add(script.name + ':');
+      }
+      completions.sort();
+      return completions;
+    } else {
+      // The script name is complete.  Look it up.
+      var scripts =
+          await _lookupScript(debugger.isolate, scriptName, allowPrefix: false);
+      if (scripts.isEmpty) {
+        return [];
+      }
+      var script = scripts.first;
+      await script.load();
+      if (!lineStrComplete) {
+        // Complete the line.
+        var sharedPrefix = '${script.name}:';
+        var completions = <String>[];
+        var report = await script.isolate
+            .getSourceReport([Isolate.kPossibleBreakpointsReport], script);
+        Set<int> possibleBpts = getPossibleBreakpointLines(report, script);
+        for (var line in possibleBpts) {
+          var currentLineStr = line.toString();
+          if (currentLineStr.startsWith(lineStr)) {
+            completions.add('${sharedPrefix}${currentLineStr} ');
+            completions.add('${sharedPrefix}${currentLineStr}:');
+          }
+        }
+        return completions;
+      } else {
+        // Complete the column.
+        int lineNum = int.parse(lineStr);
+        var scriptLine = script.getLine(lineNum);
+        var sharedPrefix = '${script.name}:${lineStr}:';
+        var completions = <String>[];
+        int maxCol = scriptLine.text.trimRight().runes.length;
+        for (int i = 1; i <= maxCol; i++) {
+          var currentColStr = i.toString();
+          if (currentColStr.startsWith(colStr)) {
+            completions.add('${sharedPrefix}${currentColStr} ');
+          }
+        }
+        return completions;
+      }
+    }
+  }
+
+  String toString() {
+    if (valid) {
+      if (function != null) {
+        return '${function.qualifiedName}';
+      } else if (col != null) {
+        return '${script.name}:${line}:${col}';
+      } else {
+        return '${script.name}:${line}';
+      }
+    }
+    return 'invalid source location (${errorMessage})';
+  }
+
+  Script script;
+  int line;
+  int col;
+  ServiceFunction function;
+  String errorMessage;
+  bool get valid => (errorMessage == null);
+}
diff --git a/runtime/observatory_2/lib/src/elements/allocation_profile.dart b/runtime/observatory_2/lib/src/elements/allocation_profile.dart
new file mode 100644
index 0000000..a233b73
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/allocation_profile.dart
@@ -0,0 +1,573 @@
+// Copyright (c) 2014, 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.
+
+import 'dart:async';
+import 'dart:html';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/class_ref.dart';
+import 'package:observatory_2/src/elements/containers/virtual_collection.dart';
+import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
+import 'package:observatory_2/src/elements/helpers/nav_menu.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/nav/isolate_menu.dart';
+import 'package:observatory_2/src/elements/nav/notify.dart';
+import 'package:observatory_2/src/elements/nav/refresh.dart';
+import 'package:observatory_2/src/elements/nav/top_menu.dart';
+import 'package:observatory_2/src/elements/nav/vm_menu.dart';
+import 'package:observatory_2/utils.dart';
+
+enum _SortingField {
+  newInstances,
+  newInternalSize,
+  newExternalSize,
+  newSize,
+  oldInstances,
+  oldInternalSize,
+  oldExternalSize,
+  oldSize,
+  instances,
+  internalSize,
+  externalSize,
+  size,
+  className,
+}
+
+enum _SortingDirection { ascending, descending }
+
+class AllocationProfileElement extends CustomElement implements Renderable {
+  RenderingScheduler<AllocationProfileElement> _r;
+
+  Stream<RenderedEvent<AllocationProfileElement>> get onRendered =>
+      _r.onRendered;
+
+  M.VM _vm;
+  M.IsolateRef _isolate;
+  M.EventRepository _events;
+  M.NotificationRepository _notifications;
+  M.AllocationProfileRepository _repository;
+  M.AllocationProfile _profile;
+  bool _autoRefresh = false;
+  bool _isCompacted = false;
+  StreamSubscription _gcSubscription;
+  _SortingField _sortingField = _SortingField.size;
+  _SortingDirection _sortingDirection = _SortingDirection.descending;
+
+  M.VMRef get vm => _vm;
+  M.IsolateRef get isolate => _isolate;
+  M.NotificationRepository get notifications => _notifications;
+
+  factory AllocationProfileElement(
+      M.VM vm,
+      M.IsolateRef isolate,
+      M.EventRepository events,
+      M.NotificationRepository notifications,
+      M.AllocationProfileRepository repository,
+      {RenderingQueue queue}) {
+    assert(vm != null);
+    assert(isolate != null);
+    assert(events != null);
+    assert(notifications != null);
+    assert(repository != null);
+    AllocationProfileElement e = new AllocationProfileElement.created();
+    e._r = new RenderingScheduler<AllocationProfileElement>(e, queue: queue);
+    e._vm = vm;
+    e._isolate = isolate;
+    e._events = events;
+    e._notifications = notifications;
+    e._repository = repository;
+    return e;
+  }
+
+  AllocationProfileElement.created() : super.created('allocation-profile');
+
+  @override
+  attached() {
+    super.attached();
+    _r.enable();
+    _refresh();
+    _gcSubscription = _events.onGCEvent.listen((e) {
+      if (_autoRefresh && (e.isolate.id == _isolate.id)) {
+        _refresh();
+      }
+    });
+  }
+
+  @override
+  detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+    _gcSubscription.cancel();
+  }
+
+  void render() {
+    children = <Element>[
+      navBar(<Element>[
+        new NavTopMenuElement(queue: _r.queue).element,
+        new NavVMMenuElement(_vm, _events, queue: _r.queue).element,
+        new NavIsolateMenuElement(_isolate, _events, queue: _r.queue).element,
+        navMenu('allocation profile'),
+        (new NavRefreshElement(
+                label: 'Download', disabled: _profile == null, queue: _r.queue)
+              ..onRefresh.listen((_) => _downloadCSV()))
+            .element,
+        (new NavRefreshElement(label: 'GC', queue: _r.queue)
+              ..onRefresh.listen((_) => _refresh(gc: true)))
+            .element,
+        (new NavRefreshElement(queue: _r.queue)
+              ..onRefresh.listen((_) => _refresh()))
+            .element,
+        new DivElement()
+          ..classes = ['nav-option']
+          ..children = <Element>[
+            new CheckboxInputElement()
+              ..id = 'allocation-profile-auto-refresh'
+              ..checked = _autoRefresh
+              ..onChange.listen((_) => _autoRefresh = !_autoRefresh),
+            new LabelElement()
+              ..htmlFor = 'allocation-profile-auto-refresh'
+              ..text = 'Auto-refresh on GC'
+          ],
+        new NavNotifyElement(_notifications, queue: _r.queue).element
+      ]),
+      new DivElement()
+        ..classes = ['content-centered-big']
+        ..children = <Element>[
+          new HeadingElement.h2()..text = 'Allocation Profile',
+          new HRElement()
+        ]
+    ];
+    if (_profile == null) {
+      children.addAll([
+        new DivElement()
+          ..classes = ['content-centered-big']
+          ..children = <Element>[new HeadingElement.h2()..text = 'Loading...']
+      ]);
+    } else {
+      children.addAll([
+        new DivElement()
+          ..classes = ['content-centered-big']
+          ..children = _isCompacted
+              ? []
+              : [
+                  new DivElement()
+                    ..classes = ['memberList']
+                    ..children = <Element>[
+                      new DivElement()
+                        ..classes = ['memberItem']
+                        ..children = <Element>[
+                          new DivElement()
+                            ..classes = ['memberName']
+                            ..text = 'last forced GC at',
+                          new DivElement()
+                            ..classes = ['memberValue']
+                            ..text = _profile.lastServiceGC == null
+                                ? '---'
+                                : '${_profile.lastServiceGC}',
+                        ],
+                    ],
+                  new HRElement(),
+                ],
+        new DivElement()
+          ..classes = ['content-centered-big', 'compactable']
+          ..children = <Element>[
+            new DivElement()
+              ..classes = ['heap-space', 'left']
+              ..children = _isCompacted
+                  ? [
+                      new HeadingElement.h2()
+                        ..text = 'New Generation '
+                            '(${_usedCaption(_profile.newSpace)})',
+                    ]
+                  : [
+                      new HeadingElement.h2()..text = 'New Generation',
+                      new BRElement(),
+                      new DivElement()
+                        ..classes = ['memberList']
+                        ..children = _createSpaceMembers(_profile.newSpace),
+                    ],
+            new DivElement()
+              ..classes = ['heap-space', 'left']
+              ..children = _isCompacted
+                  ? [
+                      new HeadingElement.h2()
+                        ..text = 'Old Generation '
+                            '(${_usedCaption(_profile.oldSpace)})',
+                    ]
+                  : [
+                      new HeadingElement.h2()..text = 'Old Generation',
+                      new BRElement(),
+                      new DivElement()
+                        ..classes = ['memberList']
+                        ..children = _createSpaceMembers(_profile.oldSpace),
+                    ],
+            new DivElement()
+              ..classes = ['heap-space', 'left']
+              ..children = _isCompacted
+                  ? [
+                      new HeadingElement.h2()
+                        ..text = 'Total '
+                            '(${_usedCaption(_profile.totalSpace)})',
+                    ]
+                  : [
+                      new HeadingElement.h2()..text = 'Total',
+                      new BRElement(),
+                      new DivElement()
+                        ..classes = ['memberList']
+                        ..children = _createSpaceMembers(_profile.totalSpace),
+                    ],
+            new ButtonElement()
+              ..classes = ['compact']
+              ..text = _isCompacted ? 'expand ▼' : 'compact ▲'
+              ..onClick.listen((_) {
+                _isCompacted = !_isCompacted;
+                _r.dirty();
+              }),
+            new HRElement()
+          ],
+        new DivElement()
+          ..classes = _isCompacted ? ['collection', 'expanded'] : ['collection']
+          ..children = <Element>[
+            new VirtualCollectionElement(
+                    _createCollectionLine, _updateCollectionLine,
+                    createHeader: _createCollectionHeader,
+                    search: _search,
+                    items: _profile.members.toList()..sort(_createSorter()),
+                    queue: _r.queue)
+                .element
+          ]
+      ]);
+    }
+  }
+
+  _createSorter() {
+    var getter;
+    switch (_sortingField) {
+      case _SortingField.newInternalSize:
+        getter = _getNewInternalSize;
+        break;
+      case _SortingField.newExternalSize:
+        getter = _getNewExternalSize;
+        break;
+      case _SortingField.newSize:
+        getter = _getNewSize;
+        break;
+      case _SortingField.newInstances:
+        getter = _getNewInstances;
+        break;
+      case _SortingField.oldInternalSize:
+        getter = _getOldInternalSize;
+        break;
+      case _SortingField.oldExternalSize:
+        getter = _getOldExternalSize;
+        break;
+      case _SortingField.oldSize:
+        getter = _getOldSize;
+        break;
+      case _SortingField.oldInstances:
+        getter = _getOldInstances;
+        break;
+      case _SortingField.internalSize:
+        getter = _getInternalSize;
+        break;
+      case _SortingField.externalSize:
+        getter = _getExternalSize;
+        break;
+      case _SortingField.size:
+        getter = _getSize;
+        break;
+      case _SortingField.instances:
+        getter = _getInstances;
+        break;
+      case _SortingField.className:
+        getter = (M.ClassHeapStats s) => s.clazz.name;
+        break;
+    }
+    switch (_sortingDirection) {
+      case _SortingDirection.ascending:
+        int sort(M.ClassHeapStats a, M.ClassHeapStats b) {
+          return getter(a).compareTo(getter(b));
+        }
+        return sort;
+      case _SortingDirection.descending:
+        int sort(M.ClassHeapStats a, M.ClassHeapStats b) {
+          return getter(b).compareTo(getter(a));
+        }
+        return sort;
+    }
+  }
+
+  static HtmlElement _createCollectionLine() => new DivElement()
+    ..classes = ['collection-item']
+    ..children = <Element>[
+      new SpanElement()
+        ..classes = ['bytes']
+        ..text = '0B',
+      new SpanElement()
+        ..classes = ['bytes']
+        ..text = '0B',
+      new SpanElement()
+        ..classes = ['bytes']
+        ..text = '0B',
+      new SpanElement()
+        ..classes = ['instances']
+        ..text = '0',
+      new SpanElement()
+        ..classes = ['bytes']
+        ..text = '0B',
+      new SpanElement()
+        ..classes = ['bytes']
+        ..text = '0B',
+      new SpanElement()
+        ..classes = ['bytes']
+        ..text = '0B',
+      new SpanElement()
+        ..classes = ['instances']
+        ..text = '0',
+      new SpanElement()
+        ..classes = ['bytes']
+        ..text = '0B',
+      new SpanElement()
+        ..classes = ['bytes']
+        ..text = '0B',
+      new SpanElement()
+        ..classes = ['bytes']
+        ..text = '0B',
+      new SpanElement()
+        ..classes = ['instances']
+        ..text = '0',
+      new SpanElement()..classes = ['name']
+    ];
+
+  List<HtmlElement> _createCollectionHeader() => [
+        new DivElement()
+          ..classes = ['collection-item']
+          ..children = <Element>[
+            new SpanElement()
+              ..classes = ['group']
+              ..text = 'New Generation',
+            new SpanElement()
+              ..classes = ['group']
+              ..text = 'Old Generation',
+            new SpanElement()
+              ..classes = ['group']
+              ..text = 'Total',
+            new SpanElement()
+              ..classes = ['group']
+              ..text = '',
+          ],
+        new DivElement()
+          ..classes = ['collection-item']
+          ..children = <Element>[
+            _createHeaderButton(const ['bytes'], 'Internal',
+                _SortingField.newInternalSize, _SortingDirection.descending),
+            _createHeaderButton(const ['bytes'], 'External',
+                _SortingField.newExternalSize, _SortingDirection.descending),
+            _createHeaderButton(const ['bytes'], 'Size', _SortingField.newSize,
+                _SortingDirection.descending),
+            _createHeaderButton(const ['instances'], 'Instances',
+                _SortingField.newInstances, _SortingDirection.descending),
+            _createHeaderButton(const ['bytes'], 'Internal',
+                _SortingField.oldInternalSize, _SortingDirection.descending),
+            _createHeaderButton(const ['bytes'], 'External',
+                _SortingField.oldExternalSize, _SortingDirection.descending),
+            _createHeaderButton(const ['bytes'], 'Size', _SortingField.oldSize,
+                _SortingDirection.descending),
+            _createHeaderButton(const ['instances'], 'Instances',
+                _SortingField.oldInstances, _SortingDirection.descending),
+            _createHeaderButton(const ['bytes'], 'Internal',
+                _SortingField.internalSize, _SortingDirection.descending),
+            _createHeaderButton(const ['bytes'], 'External',
+                _SortingField.externalSize, _SortingDirection.descending),
+            _createHeaderButton(const ['bytes'], 'Size', _SortingField.size,
+                _SortingDirection.descending),
+            _createHeaderButton(const ['instances'], 'Instances',
+                _SortingField.instances, _SortingDirection.descending),
+            _createHeaderButton(const ['name'], 'Class',
+                _SortingField.className, _SortingDirection.ascending)
+          ],
+      ];
+
+  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();
+  }
+
+  void _updateCollectionLine(Element e, itemDynamic, index) {
+    M.ClassHeapStats item = itemDynamic;
+    e.children[0].text = Utils.formatSize(_getNewInternalSize(item));
+    e.children[1].text = Utils.formatSize(_getNewExternalSize(item));
+    e.children[2].text = Utils.formatSize(_getNewSize(item));
+    e.children[3].text = '${_getNewInstances(item)}';
+    e.children[4].text = Utils.formatSize(_getOldInternalSize(item));
+    e.children[5].text = Utils.formatSize(_getOldExternalSize(item));
+    e.children[6].text = Utils.formatSize(_getOldSize(item));
+    e.children[7].text = '${_getOldInstances(item)}';
+    e.children[8].text = Utils.formatSize(_getInternalSize(item));
+    e.children[9].text = Utils.formatSize(_getExternalSize(item));
+    e.children[10].text = Utils.formatSize(_getSize(item));
+    e.children[11].text = '${_getInstances(item)}';
+    e.children[12] = new ClassRefElement(_isolate, item.clazz, queue: _r.queue)
+        .element
+      ..classes = ['name'];
+  }
+
+  bool _search(Pattern pattern, itemDynamic) {
+    M.ClassHeapStats item = itemDynamic;
+    return item.clazz.name.contains(pattern);
+  }
+
+  static String _usedCaption(M.HeapSpace space) =>
+      '${Utils.formatSize(space.used)}'
+      ' of '
+      '${Utils.formatSize(space.capacity)}';
+
+  static List<Element> _createSpaceMembers(M.HeapSpace space) {
+    final used = _usedCaption(space);
+    final ext = '${Utils.formatSize(space.external)}';
+    final collections = '${space.collections}';
+    final avgCollectionTime =
+        '${Utils.formatDurationInMilliseconds(space.avgCollectionTime)} ms';
+    final totalCollectionTime =
+        '${Utils.formatDurationInSeconds(space.totalCollectionTime)} secs';
+    final avgCollectionPeriod =
+        '${Utils.formatDurationInMilliseconds(space.avgCollectionPeriod)} ms';
+    return [
+      new DivElement()
+        ..classes = ['memberItem']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = 'used',
+          new DivElement()
+            ..classes = ['memberValue']
+            ..text = used
+        ],
+      new DivElement()
+        ..classes = ['memberItem']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = 'external',
+          new DivElement()
+            ..classes = ['memberValue']
+            ..text = ext
+        ],
+      new DivElement()
+        ..classes = ['memberItem']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = 'collections',
+          new DivElement()
+            ..classes = ['memberValue']
+            ..text = collections
+        ],
+      new DivElement()
+        ..classes = ['memberItem']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = 'average collection time',
+          new DivElement()
+            ..classes = ['memberValue']
+            ..text = avgCollectionTime
+        ],
+    ];
+  }
+
+  Future _refresh({bool gc: false, bool reset: false}) async {
+    _profile = null;
+    _r.dirty();
+    _profile = await _repository.get(_isolate, gc: gc, reset: reset);
+    _r.dirty();
+  }
+
+  void _downloadCSV() {
+    assert(_profile != null);
+    final header = [
+          '"New Internal"',
+          '"New External"',
+          '"New Size"',
+          '"New Instances"',
+          '"Old Internal"',
+          '"Old External"',
+          '"Old Size"',
+          '"Old Instances"',
+          '"Internal"',
+          '"External"',
+          '"Size"',
+          '"Instances"',
+          'Class'
+        ].join(',') +
+        '\n';
+    AnchorElement tl = document.createElement('a');
+    tl
+      ..attributes['href'] = 'data:text/plain;charset=utf-8,' +
+          Uri.encodeComponent(header +
+              (_profile.members.toList()..sort(_createSorter()))
+                  .map(_csvOut)
+                  .join('\n'))
+      ..attributes['download'] = 'heap-profile.csv'
+      ..click();
+  }
+
+  static _csvOut(M.ClassHeapStats s) {
+    return [
+      _getNewInternalSize(s),
+      _getNewExternalSize(s),
+      _getNewSize(s),
+      _getNewInstances(s),
+      _getOldInternalSize(s),
+      _getOldExternalSize(s),
+      _getOldSize(s),
+      _getOldInstances(s),
+      _getInternalSize(s),
+      _getExternalSize(s),
+      _getSize(s),
+      _getInstances(s),
+      s.clazz.name
+    ].join(',');
+  }
+
+  static int _getNewInstances(M.ClassHeapStats s) => s.newSpace.instances;
+  static int _getNewInternalSize(M.ClassHeapStats s) => s.newSpace.internalSize;
+  static int _getNewExternalSize(M.ClassHeapStats s) => s.newSpace.externalSize;
+  static int _getNewSize(M.ClassHeapStats s) => s.newSpace.size;
+  static int _getOldInstances(M.ClassHeapStats s) => s.oldSpace.instances;
+  static int _getOldInternalSize(M.ClassHeapStats s) => s.oldSpace.internalSize;
+  static int _getOldExternalSize(M.ClassHeapStats s) => s.oldSpace.externalSize;
+  static int _getOldSize(M.ClassHeapStats s) => s.oldSpace.size;
+  static int _getInstances(M.ClassHeapStats s) =>
+      s.newSpace.instances + s.oldSpace.instances;
+  static int _getInternalSize(M.ClassHeapStats s) =>
+      s.newSpace.internalSize + s.oldSpace.internalSize;
+  static int _getExternalSize(M.ClassHeapStats s) =>
+      s.newSpace.externalSize + s.oldSpace.externalSize;
+  static int _getSize(M.ClassHeapStats s) => s.newSpace.size + s.oldSpace.size;
+}
diff --git a/runtime/observatory_2/lib/src/elements/class_allocation_profile.dart b/runtime/observatory_2/lib/src/elements/class_allocation_profile.dart
new file mode 100644
index 0000000..219a00a
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/class_allocation_profile.dart
@@ -0,0 +1,120 @@
+// Copyright (c) 2016, 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.
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/cpu_profile/virtual_tree.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/sample_buffer_control.dart';
+import 'package:observatory_2/src/elements/stack_trace_tree_config.dart';
+
+class ClassAllocationProfileElement extends CustomElement
+    implements Renderable {
+  RenderingScheduler<ClassAllocationProfileElement> _r;
+
+  Stream<RenderedEvent<ClassAllocationProfileElement>> get onRendered =>
+      _r.onRendered;
+
+  M.VM _vm;
+  M.IsolateRef _isolate;
+  M.Class _cls;
+  M.ClassSampleProfileRepository _profiles;
+  Stream<M.SampleProfileLoadingProgressEvent> _progressStream;
+  M.SampleProfileLoadingProgress _progress;
+  M.SampleProfileTag _tag = M.SampleProfileTag.none;
+  ProfileTreeMode _mode = ProfileTreeMode.function;
+  M.ProfileTreeDirection _direction = M.ProfileTreeDirection.exclusive;
+
+  M.IsolateRef get isolate => _isolate;
+  M.Class get cls => _cls;
+
+  factory ClassAllocationProfileElement(M.VM vm, M.IsolateRef isolate,
+      M.Class cls, M.ClassSampleProfileRepository profiles,
+      {RenderingQueue queue}) {
+    assert(vm != null);
+    assert(isolate != null);
+    assert(cls != null);
+    assert(profiles != null);
+    ClassAllocationProfileElement e =
+        new ClassAllocationProfileElement.created();
+    e._r =
+        new RenderingScheduler<ClassAllocationProfileElement>(e, queue: queue);
+    e._vm = vm;
+    e._isolate = isolate;
+    e._cls = cls;
+    e._profiles = profiles;
+    return e;
+  }
+
+  ClassAllocationProfileElement.created()
+      : super.created('class-allocation-profile');
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+    _request();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    children = <Element>[];
+    _r.disable(notify: true);
+  }
+
+  void render() {
+    if (_progress == null) {
+      children = const [];
+      return;
+    }
+    final content = <HtmlElement>[
+      (new SampleBufferControlElement(_vm, _progress, _progressStream,
+              selectedTag: _tag, queue: _r.queue)
+            ..onTagChange.listen((e) {
+              _tag = e.element.selectedTag;
+              _request(forceFetch: true);
+            }))
+          .element
+    ];
+    if (_progress.status == M.SampleProfileLoadingStatus.loaded) {
+      CpuProfileVirtualTreeElement tree;
+      content.addAll([
+        new BRElement(),
+        (new StackTraceTreeConfigElement(
+                mode: _mode,
+                direction: _direction,
+                showFilter: false,
+                queue: _r.queue)
+              ..onModeChange.listen((e) {
+                _mode = tree.mode = e.element.mode;
+              })
+              ..onDirectionChange.listen((e) {
+                _direction = tree.direction = e.element.direction;
+              }))
+            .element,
+        new BRElement(),
+        (tree = new CpuProfileVirtualTreeElement(_isolate, _progress.profile,
+                queue: _r.queue))
+            .element
+      ]);
+    }
+    children = content;
+  }
+
+  Future _request({bool forceFetch: false}) async {
+    _progress = null;
+    _progressStream =
+        _profiles.get(_isolate, _cls, _tag, forceFetch: forceFetch);
+    _r.dirty();
+    _progress = (await _progressStream.first).progress;
+    _r.dirty();
+    if (M.isSampleProcessRunning(_progress.status)) {
+      _progress = (await _progressStream.last).progress;
+      _r.dirty();
+    }
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/class_instances.dart b/runtime/observatory_2/lib/src/elements/class_instances.dart
new file mode 100644
index 0000000..bc3e101
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/class_instances.dart
@@ -0,0 +1,194 @@
+// 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.
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/class_ref.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/inbound_references.dart';
+import 'package:observatory_2/src/elements/retaining_path.dart';
+import 'package:observatory_2/src/elements/sentinel_value.dart';
+import 'package:observatory_2/src/elements/strongly_reachable_instances.dart';
+import 'package:observatory_2/utils.dart';
+
+class ClassInstancesElement extends CustomElement implements Renderable {
+  RenderingScheduler<ClassInstancesElement> _r;
+
+  Stream<RenderedEvent<ClassInstancesElement>> get onRendered => _r.onRendered;
+
+  M.IsolateRef _isolate;
+  M.Class _cls;
+  M.RetainedSizeRepository _retainedSizes;
+  M.ReachableSizeRepository _reachableSizes;
+  M.StronglyReachableInstancesRepository _stronglyReachableInstances;
+  M.ObjectRepository _objects;
+  M.Guarded<M.Instance> _retainedSize = null;
+  bool _loadingRetainedBytes = false;
+  M.Guarded<M.Instance> _reachableSize = null;
+  bool _loadingReachableBytes = false;
+
+  M.IsolateRef get isolate => _isolate;
+  M.Class get cls => _cls;
+
+  factory ClassInstancesElement(
+      M.IsolateRef isolate,
+      M.Class cls,
+      M.RetainedSizeRepository retainedSizes,
+      M.ReachableSizeRepository reachableSizes,
+      M.StronglyReachableInstancesRepository stronglyReachableInstances,
+      M.ObjectRepository objects,
+      {RenderingQueue queue}) {
+    assert(isolate != null);
+    assert(cls != null);
+    assert(retainedSizes != null);
+    assert(reachableSizes != null);
+    assert(stronglyReachableInstances != null);
+    assert(objects != null);
+    ClassInstancesElement e = new ClassInstancesElement.created();
+    e._r = new RenderingScheduler<ClassInstancesElement>(e, queue: queue);
+    e._isolate = isolate;
+    e._cls = cls;
+    e._retainedSizes = retainedSizes;
+    e._reachableSizes = reachableSizes;
+    e._stronglyReachableInstances = stronglyReachableInstances;
+    e._objects = objects;
+    return e;
+  }
+
+  ClassInstancesElement.created() : super.created('class-instances');
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+  }
+
+  StronglyReachableInstancesElement _strong;
+
+  void render() {
+    _strong = _strong ??
+        new StronglyReachableInstancesElement(
+            _isolate, _cls, _stronglyReachableInstances, _objects,
+            queue: _r.queue);
+    final instanceCount = _cls.newSpace.instances + _cls.oldSpace.instances;
+    final size = Utils.formatSize(_cls.newSpace.size + _cls.oldSpace.size);
+    children = <Element>[
+      new DivElement()
+        ..classes = ['memberList']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = const ['memberItem']
+            ..children = <Element>[
+              new DivElement()
+                ..classes = const ['memberName']
+                ..text = 'currently allocated',
+              new DivElement()
+                ..classes = const ['memberValue']
+                ..text = 'count ${instanceCount} (shallow size ${size})'
+            ],
+          new DivElement()
+            ..classes = ['memberItem']
+            ..children = <Element>[
+              new DivElement()
+                ..classes = ['memberName']
+                ..text = 'strongly reachable ',
+              new DivElement()
+                ..classes = ['memberValue']
+                ..children = <Element>[_strong.element]
+            ],
+          new DivElement()
+            ..classes = ['memberItem']
+            ..title = 'Space reachable from this object, '
+                'excluding class references'
+            ..children = <Element>[
+              new DivElement()
+                ..classes = ['memberName']
+                ..text = 'Reachable size ',
+              new DivElement()
+                ..classes = ['memberValue']
+                ..children = _createReachableSizeValue()
+            ],
+          new DivElement()
+            ..classes = ['memberItem']
+            ..title = 'Space that would be reclaimed if references to this '
+                'object were replaced with null'
+            ..children = <Element>[
+              new DivElement()
+                ..classes = ['memberName']
+                ..text = 'Retained size ',
+              new DivElement()
+                ..classes = ['memberValue']
+                ..children = _createRetainedSizeValue()
+            ],
+        ]
+    ];
+  }
+
+  List<Element> _createReachableSizeValue() {
+    final content = <Element>[];
+    if (_reachableSize != null) {
+      if (_reachableSize.isSentinel) {
+        content.add(
+            new SentinelValueElement(_reachableSize.asSentinel, queue: _r.queue)
+                .element);
+      } else {
+        content.add(new SpanElement()
+          ..text = Utils.formatSize(
+              int.parse(_reachableSize.asValue.valueAsString)));
+      }
+    } else {
+      content.add(new SpanElement()..text = '...');
+    }
+    final button = new ButtonElement()
+      ..classes = ['reachable_size']
+      ..disabled = _loadingReachableBytes
+      ..text = '↺';
+    button.onClick.listen((_) async {
+      button.disabled = true;
+      _loadingReachableBytes = true;
+      _reachableSize = await _reachableSizes.get(_isolate, _cls.id);
+      _r.dirty();
+    });
+    content.add(button);
+    return content;
+  }
+
+  List<Element> _createRetainedSizeValue() {
+    final content = <Element>[];
+    if (_retainedSize != null) {
+      if (_retainedSize.isSentinel) {
+        content.add(
+            new SentinelValueElement(_retainedSize.asSentinel, queue: _r.queue)
+                .element);
+      } else {
+        content.add(new SpanElement()
+          ..text =
+              Utils.formatSize(int.parse(_retainedSize.asValue.valueAsString)));
+      }
+    } else {
+      content.add(new SpanElement()..text = '...');
+    }
+    final button = new ButtonElement()
+      ..classes = ['retained_size']
+      ..disabled = _loadingRetainedBytes
+      ..text = '↺';
+    button.onClick.listen((_) async {
+      button.disabled = true;
+      _loadingRetainedBytes = true;
+      _retainedSize = await _retainedSizes.get(_isolate, _cls.id);
+      _r.dirty();
+    });
+    content.add(button);
+    return content;
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/class_ref.dart b/runtime/observatory_2/lib/src/elements/class_ref.dart
new file mode 100644
index 0000000..e9f35b3
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/class_ref.dart
@@ -0,0 +1,57 @@
+// 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.
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/helpers/uris.dart';
+
+class ClassRefElement extends CustomElement implements Renderable {
+  RenderingScheduler<ClassRefElement> _r;
+
+  Stream<RenderedEvent<ClassRefElement>> get onRendered => _r.onRendered;
+
+  M.IsolateRef _isolate;
+  M.ClassRef _class;
+
+  M.IsolateRef get isolate => _isolate;
+  M.ClassRef get cls => _class;
+
+  factory ClassRefElement(M.IsolateRef isolate, M.ClassRef cls,
+      {RenderingQueue queue}) {
+    assert(cls != null);
+    ClassRefElement e = new ClassRefElement.created();
+    e._r = new RenderingScheduler<ClassRefElement>(e, queue: queue);
+    e._isolate = isolate;
+    e._class = cls;
+    return e;
+  }
+
+  ClassRefElement.created() : super.created('class-ref');
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+  }
+
+  void render() {
+    children = <Element>[
+      new AnchorElement(
+          href: (_isolate == null)
+              ? null
+              : Uris.inspect(_isolate, object: _class))
+        ..text = _class.name
+    ];
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/class_tree.dart b/runtime/observatory_2/lib/src/elements/class_tree.dart
new file mode 100644
index 0000000..68eb936
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/class_tree.dart
@@ -0,0 +1,188 @@
+// Copyright (c) 2014, 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.
+
+library class_tree_element;
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/class_ref.dart';
+import 'package:observatory_2/src/elements/containers/virtual_tree.dart';
+import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
+import 'package:observatory_2/src/elements/helpers/nav_menu.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/nav/isolate_menu.dart';
+import 'package:observatory_2/src/elements/nav/notify.dart';
+import 'package:observatory_2/src/elements/nav/top_menu.dart';
+import 'package:observatory_2/src/elements/nav/vm_menu.dart';
+
+class ClassTreeElement extends CustomElement implements Renderable {
+  RenderingScheduler<ClassTreeElement> _r;
+
+  Stream<RenderedEvent<ClassTreeElement>> get onRendered => _r.onRendered;
+
+  M.VMRef _vm;
+  M.IsolateRef _isolate;
+  M.EventRepository _events;
+  M.NotificationRepository _notifications;
+  M.ClassRepository _classes;
+  M.Class _object;
+  final _subclasses = <String, Iterable<M.Class>>{};
+  final _mixins = <String, List<M.Instance>>{};
+
+  factory ClassTreeElement(
+      M.VMRef vm,
+      M.IsolateRef isolate,
+      M.EventRepository events,
+      M.NotificationRepository notifications,
+      M.ClassRepository classes,
+      {RenderingQueue queue}) {
+    assert(vm != null);
+    assert(isolate != null);
+    assert(events != null);
+    assert(notifications != null);
+    assert(classes != null);
+    ClassTreeElement e = new ClassTreeElement.created();
+    e._r = new RenderingScheduler<ClassTreeElement>(e, queue: queue);
+    e._vm = vm;
+    e._isolate = isolate;
+    e._events = events;
+    e._notifications = notifications;
+    e._classes = classes;
+    return e;
+  }
+
+  ClassTreeElement.created() : super.created('class-tree');
+
+  @override
+  void attached() {
+    super.attached();
+    _refresh();
+    _r.enable();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    children = <Element>[];
+    _r.disable(notify: true);
+  }
+
+  VirtualTreeElement _tree;
+
+  void render() {
+    children = <Element>[
+      navBar(<Element>[
+        new NavTopMenuElement(queue: _r.queue).element,
+        new NavVMMenuElement(_vm, _events, queue: _r.queue).element,
+        new NavIsolateMenuElement(_isolate, _events, queue: _r.queue).element,
+        navMenu('class hierarchy'),
+        new NavNotifyElement(_notifications, queue: _r.queue).element
+      ]),
+      new DivElement()
+        ..classes = ['content-centered']
+        ..children = <Element>[
+          new HeadingElement.h1()
+            ..text = 'Class Hierarchy (${_subclasses.length})',
+          new BRElement(),
+          new HRElement(),
+          _object == null
+              ? (new HeadingElement.h2()..text = 'Loading...')
+              : _createTree()
+        ]
+    ];
+  }
+
+  Element _createTree() {
+    _tree = new VirtualTreeElement(_create, _update, _children,
+        items: [_object], search: _search, queue: _r.queue);
+    _tree.expand(_object, autoExpandSingleChildNodes: true);
+    return _tree.element;
+  }
+
+  Future _refresh() async {
+    _object = null;
+    _subclasses.clear();
+    _mixins.clear();
+    _object = await _register(await _classes.getObject(_isolate));
+    _r.dirty();
+  }
+
+  Future<M.Class> _register(M.Class cls) async {
+    _subclasses[cls.id] = await Future.wait(
+        (await Future.wait(cls.subclasses.map(_getActualChildrens)))
+            .expand((f) => f)
+            .map(_register));
+    return cls;
+  }
+
+  Future<Iterable<M.Class>> _getActualChildrens(M.ClassRef ref) async {
+    var cls = await _classes.get(_isolate, ref.id);
+    if (cls.isPatch) {
+      return const [];
+    }
+    if (cls.mixin == null) {
+      return [cls];
+    }
+    return (await Future.wait(cls.subclasses.map(_getActualChildrens)))
+        .expand((f) => f)
+          ..forEach((subcls) {
+            _mixins[subcls.id] = (_mixins[subcls.id] ?? [])..add(cls.mixin);
+          });
+  }
+
+  static HtmlElement _create(toggle) {
+    return new DivElement()
+      ..classes = ['class-tree-item']
+      ..children = <Element>[
+        new SpanElement()..classes = ['lines'],
+        new ButtonElement()
+          ..classes = ['expander']
+          ..onClick.listen((_) => toggle(autoToggleSingleChildNodes: true)),
+        new SpanElement()..classes = ['name']
+      ];
+  }
+
+  void _update(HtmlElement el, classDynamic, int index) {
+    M.Class cls = classDynamic;
+    virtualTreeUpdateLines(el.children[0], index);
+    if (cls.subclasses.isEmpty) {
+      el.children[1].text = '';
+    } else {
+      el.children[1].text = _tree.isExpanded(cls) ? '▼' : '►';
+    }
+    el.children[2].children = <Element>[
+      new ClassRefElement(_isolate, cls, queue: _r.queue).element
+    ];
+    if (_mixins[cls.id] != null) {
+      el.children[2].children.addAll(_createMixins(_mixins[cls.id]));
+    }
+  }
+
+  bool _search(Pattern pattern, classDynamic) {
+    M.Class cls = classDynamic;
+    return cls.name.contains(pattern);
+  }
+
+  List<Element> _createMixins(List<M.Instance> types) {
+    final children = types
+        .expand((type) => <Element>[
+              new SpanElement()..text = ', ',
+              type.typeClass == null
+                  ? (new SpanElement()..text = type.name.split('<').first)
+                  : new ClassRefElement(_isolate, type.typeClass,
+                          queue: _r.queue)
+                      .element
+            ])
+        .toList();
+    children.first.text = ' with ';
+    return children;
+  }
+
+  Iterable<M.Class> _children(classDynamic) {
+    M.Class cls = classDynamic;
+    return _subclasses[cls.id];
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/class_view.dart b/runtime/observatory_2/lib/src/elements/class_view.dart
new file mode 100644
index 0000000..4dd190e
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/class_view.dart
@@ -0,0 +1,495 @@
+// 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.
+
+library class_view_element;
+
+import 'dart:async';
+import 'dart:html';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/class_allocation_profile.dart';
+import 'package:observatory_2/src/elements/class_instances.dart';
+import 'package:observatory_2/src/elements/class_ref.dart';
+import 'package:observatory_2/src/elements/curly_block.dart';
+import 'package:observatory_2/src/elements/error_ref.dart';
+import 'package:observatory_2/src/elements/eval_box.dart';
+import 'package:observatory_2/src/elements/field_ref.dart';
+import 'package:observatory_2/src/elements/function_ref.dart';
+import 'package:observatory_2/src/elements/helpers/any_ref.dart';
+import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/instance_ref.dart';
+import 'package:observatory_2/src/elements/library_ref.dart';
+import 'package:observatory_2/src/elements/nav/class_menu.dart';
+import 'package:observatory_2/src/elements/nav/isolate_menu.dart';
+import 'package:observatory_2/src/elements/nav/notify.dart';
+import 'package:observatory_2/src/elements/nav/refresh.dart';
+import 'package:observatory_2/src/elements/nav/top_menu.dart';
+import 'package:observatory_2/src/elements/nav/vm_menu.dart';
+import 'package:observatory_2/src/elements/object_common.dart';
+import 'package:observatory_2/src/elements/source_inset.dart';
+import 'package:observatory_2/src/elements/source_link.dart';
+import 'package:observatory_2/src/elements/view_footer.dart';
+
+class ClassViewElement extends CustomElement implements Renderable {
+  RenderingScheduler<ClassViewElement> _r;
+
+  Stream<RenderedEvent<ClassViewElement>> get onRendered => _r.onRendered;
+
+  M.VM _vm;
+  M.IsolateRef _isolate;
+  M.EventRepository _events;
+  M.NotificationRepository _notifications;
+  M.Class _cls;
+  M.ClassRepository _classes;
+  M.RetainedSizeRepository _retainedSizes;
+  M.ReachableSizeRepository _reachableSizes;
+  M.InboundReferencesRepository _references;
+  M.RetainingPathRepository _retainingPaths;
+  M.StronglyReachableInstancesRepository _stronglyReachableInstances;
+  M.FieldRepository _fields;
+  M.ScriptRepository _scripts;
+  M.ObjectRepository _objects;
+  M.EvalRepository _eval;
+  M.ClassSampleProfileRepository _profiles;
+  Iterable<M.Field> _classFields;
+
+  M.VMRef get vm => _vm;
+  M.IsolateRef get isolate => _isolate;
+  M.NotificationRepository get notifications => _notifications;
+  M.Class get cls => _cls;
+
+  factory ClassViewElement(
+      M.VM vm,
+      M.IsolateRef isolate,
+      M.Class cls,
+      M.EventRepository events,
+      M.NotificationRepository notifications,
+      M.ClassRepository classes,
+      M.RetainedSizeRepository retainedSizes,
+      M.ReachableSizeRepository reachableSizes,
+      M.InboundReferencesRepository references,
+      M.RetainingPathRepository retainingPaths,
+      M.FieldRepository fields,
+      M.ScriptRepository scripts,
+      M.ObjectRepository objects,
+      M.EvalRepository eval,
+      M.StronglyReachableInstancesRepository stronglyReachable,
+      M.ClassSampleProfileRepository profiles,
+      {RenderingQueue queue}) {
+    assert(vm != null);
+    assert(isolate != null);
+    assert(events != null);
+    assert(notifications != null);
+    assert(cls != null);
+    assert(classes != null);
+    assert(retainedSizes != null);
+    assert(reachableSizes != null);
+    assert(references != null);
+    assert(retainingPaths != null);
+    assert(fields != null);
+    assert(scripts != null);
+    assert(objects != null);
+    assert(eval != null);
+    assert(stronglyReachable != null);
+    assert(profiles != null);
+    ClassViewElement e = new ClassViewElement.created();
+    e._r = new RenderingScheduler<ClassViewElement>(e, queue: queue);
+    e._vm = vm;
+    e._isolate = isolate;
+    e._events = events;
+    e._notifications = notifications;
+    e._cls = cls;
+    e._classes = classes;
+    e._retainedSizes = retainedSizes;
+    e._reachableSizes = reachableSizes;
+    e._references = references;
+    e._retainingPaths = retainingPaths;
+    e._fields = fields;
+    e._scripts = scripts;
+    e._objects = objects;
+    e._eval = eval;
+    e._stronglyReachableInstances = stronglyReachable;
+    e._profiles = profiles;
+    return e;
+  }
+
+  ClassViewElement.created() : super.created('class-view');
+
+  @override
+  attached() {
+    super.attached();
+    _r.enable();
+    _loadAdditionalData();
+  }
+
+  @override
+  detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+  }
+
+  ObjectCommonElement _common;
+  ClassInstancesElement _classInstances;
+  bool _loadProfile = false;
+
+  void render() {
+    _common = _common ??
+        new ObjectCommonElement(_isolate, _cls, _retainedSizes, _reachableSizes,
+            _references, _retainingPaths, _objects,
+            queue: _r.queue);
+    _classInstances = _classInstances ??
+        new ClassInstancesElement(_isolate, _cls, _retainedSizes,
+            _reachableSizes, _stronglyReachableInstances, _objects,
+            queue: _r.queue);
+    var header = '';
+    if (_cls.isAbstract) {
+      header += 'abstract ';
+    }
+    if (_cls.isPatch) {
+      header += 'patch ';
+    }
+    children = <Element>[
+      navBar(<Element>[
+        new NavTopMenuElement(queue: _r.queue).element,
+        new NavVMMenuElement(_vm, _events, queue: _r.queue).element,
+        new NavIsolateMenuElement(_isolate, _events, queue: _r.queue).element,
+        new NavClassMenuElement(_isolate, _cls, queue: _r.queue).element,
+        (new NavRefreshElement(
+                label: 'Refresh Allocation Profile', queue: _r.queue)
+              ..onRefresh.listen((e) {
+                e.element.disabled = true;
+                _loadProfile = true;
+                _r.dirty();
+              }))
+            .element,
+        (new NavRefreshElement(queue: _r.queue)
+              ..onRefresh.listen((e) {
+                e.element.disabled = true;
+                _common = null;
+                _classInstances = null;
+                _fieldsExpanded = null;
+                _functionsExpanded = null;
+                _refresh();
+              }))
+            .element,
+        new NavNotifyElement(_notifications, queue: _r.queue).element
+      ]),
+      new DivElement()
+        ..classes = ['content-centered-big']
+        ..children = <Element>[
+          new HeadingElement.h2()..text = '$header class ${_cls.name}',
+          new HRElement(),
+          _common.element,
+          new BRElement(),
+          new DivElement()
+            ..classes = ['memberList']
+            ..children = _createMembers(),
+          new DivElement()
+            ..children = _cls.error == null
+                ? const []
+                : [
+                    new HRElement(),
+                    new ErrorRefElement(_cls.error, queue: _r.queue).element
+                  ],
+          new HRElement(),
+          new EvalBoxElement(_isolate, _cls, _objects, _eval, queue: _r.queue)
+              .element,
+          new HRElement(),
+          new HeadingElement.h2()..text = 'Fields & Functions',
+          new DivElement()
+            ..classes = ['memberList']
+            ..children = _createElements(),
+          new HRElement(),
+          new HeadingElement.h2()..text = 'Instances',
+          new DivElement()..children = [_classInstances.element],
+          new HRElement(),
+          new HeadingElement.h2()..text = 'Allocations',
+          new DivElement()
+            ..classes = ['memberList']
+            ..children = <Element>[
+              new DivElement()
+                ..classes = ['memberName']
+                ..text = 'Tracing allocations?	',
+              new DivElement()
+                ..classes = ['memberValue']
+                ..children = _cls.traceAllocations
+                    ? [
+                        new SpanElement()..text = 'Yes ',
+                        new ButtonElement()
+                          ..text = 'disable'
+                          ..onClick.listen((e) async {
+                            (e.target as ButtonElement).disabled = true;
+                            await _profiles.disable(_isolate, _cls);
+                            _loadProfile = true;
+                            _refresh();
+                          })
+                      ]
+                    : [
+                        new SpanElement()..text = 'No ',
+                        new ButtonElement()
+                          ..text = 'enable'
+                          ..onClick.listen((e) async {
+                            (e.target as ButtonElement).disabled = true;
+                            await _profiles.enable(_isolate, _cls);
+                            _refresh();
+                          })
+                      ]
+            ],
+          new DivElement()
+            ..children = _loadProfile
+                ? [
+                    new ClassAllocationProfileElement(
+                            _vm, _isolate, _cls, _profiles,
+                            queue: _r.queue)
+                        .element
+                  ]
+                : const [],
+          new DivElement()
+            ..children = _cls.location != null
+                ? [
+                    new HRElement(),
+                    new SourceInsetElement(_isolate, _cls.location, _scripts,
+                            _objects, _events,
+                            queue: _r.queue)
+                        .element
+                  ]
+                : const [],
+          new HRElement(),
+          new ViewFooterElement(queue: _r.queue).element
+        ]
+    ];
+  }
+
+  bool _fieldsExpanded;
+  bool _functionsExpanded;
+
+  List<Element> _createMembers() {
+    final members = <Element>[];
+    if (_cls.library != null) {
+      members.add(new DivElement()
+        ..classes = ['memberItem']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = 'library',
+          new DivElement()
+            ..classes = ['memberValue']
+            ..children = <Element>[
+              new LibraryRefElement(_isolate, _cls.library, queue: _r.queue)
+                  .element
+            ]
+        ]);
+    }
+    if (_cls.location != null) {
+      members.add(new DivElement()
+        ..classes = ['memberItem']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = 'script',
+          new DivElement()
+            ..classes = ['memberValue']
+            ..children = <Element>[
+              new SourceLinkElement(_isolate, _cls.location, _scripts,
+                      queue: _r.queue)
+                  .element
+            ]
+        ]);
+    }
+    if (_cls.superclass != null) {
+      members.add(new DivElement()
+        ..classes = ['memberItem']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = 'superclass',
+          new DivElement()
+            ..classes = ['memberValue']
+            ..children = <Element>[
+              new ClassRefElement(_isolate, _cls.superclass, queue: _r.queue)
+                  .element
+            ]
+        ]);
+    }
+    if (_cls.superType != null) {
+      members.add(new DivElement()
+        ..classes = ['memberItem']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = 'supertype',
+          new DivElement()
+            ..classes = ['memberValue']
+            ..children = <Element>[
+              new InstanceRefElement(_isolate, _cls.superType, _objects,
+                      queue: _r.queue)
+                  .element
+            ]
+        ]);
+    }
+    if (cls.mixin != null) {
+      members.add(new DivElement()
+        ..classes = ['memberItem']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = 'mixin',
+          new DivElement()
+            ..classes = ['memberValue']
+            ..children = <Element>[
+              new InstanceRefElement(_isolate, _cls.mixin, _objects,
+                      queue: _r.queue)
+                  .element
+            ]
+        ]);
+    }
+    if (_cls.subclasses.length > 0) {
+      members.add(new DivElement()
+        ..classes = ['memberItem']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = 'extended by',
+          new DivElement()
+            ..classes = ['memberValue']
+            ..children = (_cls.subclasses
+                .expand((subcls) => <Element>[
+                      new ClassRefElement(_isolate, subcls, queue: _r.queue)
+                          .element,
+                      new SpanElement()..text = ', '
+                    ])
+                .toList()
+                  ..removeLast())
+        ]);
+    }
+
+    members.add(new BRElement());
+
+    if (_cls.interfaces.length > 0) {
+      members.add(new DivElement()
+        ..classes = ['memberItem']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = 'implements',
+          new DivElement()
+            ..classes = ['memberValue']
+            ..children = (_cls.interfaces
+                .expand((interf) => <Element>[
+                      new InstanceRefElement(_isolate, interf, _objects,
+                              queue: _r.queue)
+                          .element,
+                      new SpanElement()..text = ', '
+                    ])
+                .toList()
+                  ..removeLast())
+        ]);
+    }
+    if (_cls.name != _cls.vmName) {
+      members.add(new DivElement()
+        ..classes = ['memberItem']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = 'vm name',
+          new DivElement()
+            ..classes = ['memberValue']
+            ..text = '${_cls.vmName}'
+        ]);
+    }
+    return members;
+  }
+
+  List<Element> _createElements() {
+    final members = <Element>[];
+    if (_classFields != null && _classFields.isNotEmpty) {
+      final fields = _classFields.toList();
+      _fieldsExpanded = _fieldsExpanded ?? (fields.length <= 8);
+      members.add(new DivElement()
+        ..classes = ['memberItem']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = 'fields ${fields.length}',
+          new DivElement()
+            ..classes = ['memberValue']
+            ..children = <Element>[
+              (new CurlyBlockElement(expanded: _fieldsExpanded)
+                    ..onToggle
+                        .listen((e) => _fieldsExpanded = e.control.expanded)
+                    ..content = <Element>[
+                      new DivElement()
+                        ..classes = ['memberList']
+                        ..children = (fields
+                            .map<Element>((f) => new DivElement()
+                              ..classes = ['memberItem']
+                              ..children = <Element>[
+                                new DivElement()
+                                  ..classes = ['memberName']
+                                  ..children = <Element>[
+                                    new FieldRefElement(_isolate, f, _objects,
+                                            queue: _r.queue)
+                                        .element
+                                  ],
+                                new DivElement()
+                                  ..classes = ['memberValue']
+                                  ..children = f.staticValue == null
+                                      ? const []
+                                      : [
+                                          anyRef(
+                                              _isolate, f.staticValue, _objects,
+                                              queue: _r.queue)
+                                        ]
+                              ])
+                            .toList())
+                    ])
+                  .element
+            ]
+        ]);
+    }
+
+    if (_cls.functions.isNotEmpty) {
+      final functions = _cls.functions.toList();
+      _functionsExpanded = _functionsExpanded ?? (functions.length <= 8);
+      members.add(new DivElement()
+        ..classes = ['memberItem']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = 'functions (${functions.length})',
+          new DivElement()
+            ..classes = ['memberValue']
+            ..children = <Element>[
+              (new CurlyBlockElement(expanded: _functionsExpanded)
+                    ..onToggle
+                        .listen((e) => _functionsExpanded = e.control.expanded)
+                    ..content = (functions
+                        .map<Element>((f) => new DivElement()
+                          ..classes = ['indent']
+                          ..children = <Element>[
+                            new FunctionRefElement(_isolate, f, queue: _r.queue)
+                                .element
+                          ])
+                        .toList()))
+                  .element
+            ]
+        ]);
+    }
+    return members;
+  }
+
+  Future _refresh() async {
+    _cls = await _classes.get(_isolate, _cls.id);
+    await _loadAdditionalData();
+    _r.dirty();
+  }
+
+  Future _loadAdditionalData() async {
+    _classFields =
+        await Future.wait(_cls.fields.map((f) => _fields.get(_isolate, f.id)));
+    _r.dirty();
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/code_ref.dart b/runtime/observatory_2/lib/src/elements/code_ref.dart
new file mode 100644
index 0000000..e0c3ab8
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/code_ref.dart
@@ -0,0 +1,60 @@
+// 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.
+
+library code_ref_element;
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/models.dart' as M
+    show IsolateRef, CodeRef, isSyntheticCode;
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/helpers/uris.dart';
+
+class CodeRefElement extends CustomElement implements Renderable {
+  RenderingScheduler<CodeRefElement> _r;
+
+  Stream<RenderedEvent<CodeRefElement>> get onRendered => _r.onRendered;
+
+  M.IsolateRef _isolate;
+  M.CodeRef _code;
+
+  M.IsolateRef get isolate => _isolate;
+  M.CodeRef get code => _code;
+
+  factory CodeRefElement(M.IsolateRef isolate, M.CodeRef code,
+      {RenderingQueue queue}) {
+    assert(code != null);
+    CodeRefElement e = new CodeRefElement.created();
+    e._r = new RenderingScheduler<CodeRefElement>(e, queue: queue);
+    e._isolate = isolate;
+    e._code = code;
+    return e;
+  }
+
+  CodeRefElement.created() : super.created('code-ref');
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    children = <Element>[];
+    _r.disable(notify: true);
+  }
+
+  void render() {
+    children = <Element>[
+      new AnchorElement(
+          href: ((M.isSyntheticCode(_code.kind)) || (_isolate == null))
+              ? null
+              : Uris.inspect(_isolate, object: _code))
+        ..text = _code.name
+    ];
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/code_view.dart b/runtime/observatory_2/lib/src/elements/code_view.dart
new file mode 100644
index 0000000..5d27eba
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/code_view.dart
@@ -0,0 +1,642 @@
+// 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.
+
+library code_view_element;
+
+import 'dart:async';
+import 'dart:html';
+import 'package:observatory_2/sample_profile.dart';
+import 'package:observatory_2/service.dart' as S;
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/app.dart'
+    show SortedTable, SortedTableColumn, SortedTableRow;
+import 'package:observatory_2/src/elements/curly_block.dart';
+import 'package:observatory_2/src/elements/function_ref.dart';
+import 'package:observatory_2/src/elements/helpers/any_ref.dart';
+import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
+import 'package:observatory_2/src/elements/helpers/nav_menu.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/nav/class_menu.dart';
+import 'package:observatory_2/src/elements/nav/isolate_menu.dart';
+import 'package:observatory_2/src/elements/nav/notify.dart';
+import 'package:observatory_2/src/elements/nav/refresh.dart';
+import 'package:observatory_2/src/elements/nav/top_menu.dart';
+import 'package:observatory_2/src/elements/nav/vm_menu.dart';
+import 'package:observatory_2/src/elements/object_common.dart';
+import 'package:observatory_2/src/elements/objectpool_ref.dart';
+import 'package:observatory_2/utils.dart';
+
+class DisassemblyTable extends SortedTable {
+  DisassemblyTable(columns) : super(columns);
+}
+
+class InlineTable extends SortedTable {
+  InlineTable(columns) : super(columns);
+}
+
+class CodeViewElement extends CustomElement implements Renderable {
+  RenderingScheduler<CodeViewElement> _r;
+
+  Stream<RenderedEvent<CodeViewElement>> get onRendered => _r.onRendered;
+
+  M.VM _vm;
+  M.IsolateRef _isolate;
+  M.EventRepository _events;
+  M.NotificationRepository _notifications;
+  M.Code _code;
+  M.RetainedSizeRepository _retainedSizes;
+  M.ReachableSizeRepository _reachableSizes;
+  M.InboundReferencesRepository _references;
+  M.RetainingPathRepository _retainingPaths;
+  M.ObjectRepository _objects;
+  DisassemblyTable disassemblyTable;
+  InlineTable inlineTable;
+
+  static const kDisassemblyColumnIndex = 3;
+
+  M.VMRef get vm => _vm;
+  M.IsolateRef get isolate => _isolate;
+  M.NotificationRepository get notifications => _notifications;
+  M.Code get context => _code;
+
+  factory CodeViewElement(
+      M.VM vm,
+      M.IsolateRef isolate,
+      M.Code code,
+      M.EventRepository events,
+      M.NotificationRepository notifications,
+      M.RetainedSizeRepository retainedSizes,
+      M.ReachableSizeRepository reachableSizes,
+      M.InboundReferencesRepository references,
+      M.RetainingPathRepository retainingPaths,
+      M.ObjectRepository objects,
+      {RenderingQueue queue}) {
+    assert(vm != null);
+    assert(isolate != null);
+    assert(events != null);
+    assert(notifications != null);
+    assert(code != null);
+    assert(objects != null);
+    assert(retainedSizes != null);
+    assert(reachableSizes != null);
+    assert(references != null);
+    assert(retainingPaths != null);
+    CodeViewElement e = new CodeViewElement.created();
+    e._r = new RenderingScheduler<CodeViewElement>(e, queue: queue);
+    e._vm = vm;
+    e._isolate = isolate;
+    e._events = events;
+    e._notifications = notifications;
+    e._code = code;
+    e._objects = objects;
+    e._retainedSizes = retainedSizes;
+    e._reachableSizes = reachableSizes;
+    e._references = references;
+    e._retainingPaths = retainingPaths;
+    return e;
+  }
+
+  CodeViewElement.created() : super.created('code-view') {
+    var columns = [
+      new SortedTableColumn('Address'),
+      new SortedTableColumn('Inclusive'),
+      new SortedTableColumn('Exclusive'),
+      new SortedTableColumn('Disassembly'),
+      new SortedTableColumn('Objects'),
+    ];
+    disassemblyTable = new DisassemblyTable(columns);
+    columns = [
+      new SortedTableColumn('Address'),
+      new SortedTableColumn('Inclusive'),
+      new SortedTableColumn('Exclusive'),
+      new SortedTableColumn('Functions'),
+    ];
+    inlineTable = new InlineTable(columns);
+  }
+
+  @override
+  attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+  }
+
+  TableElement _disassemblyTable;
+  TableElement _inlineRangeTable;
+  Element _disassemblyTableBody;
+  Element _inlineRangeTableBody;
+
+  void render() {
+    if (_inlineRangeTable == null) {
+      _inlineRangeTable = new TableElement()..classes = ['table'];
+      _inlineRangeTable.createTHead().children = <Element>[
+        new TableRowElement()
+          ..children = <Element>[
+            document.createElement('th')
+              ..classes = ['address']
+              ..text = 'Address Range',
+            document.createElement('th')
+              ..classes = ['tick']
+              ..text = 'Inclusive',
+            document.createElement('th')
+              ..classes = ['tick']
+              ..text = 'Exclusive',
+            document.createElement('th')..text = 'Functions',
+          ]
+      ];
+      _inlineRangeTableBody = _inlineRangeTable.createTBody();
+      _inlineRangeTableBody.classes = ['monospace'];
+    }
+    if (_disassemblyTable == null) {
+      _disassemblyTable = new TableElement()..classes = ['table'];
+      _disassemblyTable.createTHead().children = <Element>[
+        new TableRowElement()
+          ..children = <Element>[
+            document.createElement('th')
+              ..classes = ['address']
+              ..text = 'Address Range',
+            document.createElement('th')
+              ..classes = ['tick']
+              ..title = 'Ticks with PC on the stack'
+              ..text = 'Inclusive',
+            document.createElement('th')
+              ..classes = ['tick']
+              ..title = 'Ticks with PC at top of stack'
+              ..text = 'Exclusive',
+            document.createElement('th')
+              ..classes = ['disassembly']
+              ..text = 'Disassembly',
+            document.createElement('th')
+              ..classes = ['object']
+              ..text = 'Object',
+          ]
+      ];
+      _disassemblyTableBody = _disassemblyTable.createTBody();
+      _disassemblyTableBody.classes = ['monospace'];
+    }
+    final inlinedFunctions = _code.inlinedFunctions.toList();
+    final S.Code code = _code as S.Code;
+    children = <Element>[
+      navBar(<Element>[
+        new NavTopMenuElement(queue: _r.queue).element,
+        new NavVMMenuElement(_vm, _events, queue: _r.queue).element,
+        new NavIsolateMenuElement(_isolate, _events, queue: _r.queue).element,
+        navMenu(_code.name),
+        (new NavRefreshElement(queue: _r.queue)
+              ..onRefresh.listen((e) async {
+                e.element.disabled = true;
+                _refresh();
+              }))
+            .element,
+        (new NavRefreshElement(label: 'refresh ticks', queue: _r.queue)
+              ..onRefresh.listen((e) async {
+                e.element.disabled = true;
+                _refreshTicks();
+              }))
+            .element,
+        new NavNotifyElement(_notifications, queue: _r.queue).element
+      ]),
+      new DivElement()
+        ..classes = ['content-centered-big']
+        ..children = <Element>[
+          new HeadingElement.h1()
+            ..text = (M.isDartCode(_code.kind) && _code.isOptimized)
+                ? 'Optimized code for ${_code.name}'
+                : 'Code for ${_code.name}',
+          new HRElement(),
+          new ObjectCommonElement(_isolate, _code, _retainedSizes,
+                  _reachableSizes, _references, _retainingPaths, _objects,
+                  queue: _r.queue)
+              .element,
+          new BRElement(),
+          new DivElement()
+            ..classes = ['memberList']
+            ..children = <Element>[
+              new DivElement()
+                ..classes = ['memberItem']
+                ..children = <Element>[
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..text = 'Kind',
+                  new DivElement()
+                    ..classes = ['memberValue']
+                    ..text = _codeKindToString(_code.kind)
+                ],
+              new DivElement()
+                ..classes = ['memberItem']
+                ..children = M.isDartCode(_code.kind)
+                    ? const []
+                    : [
+                        new DivElement()
+                          ..classes = ['memberName']
+                          ..text = 'Optimized',
+                        new DivElement()
+                          ..classes = ['memberValue']
+                          ..text = _code.isOptimized ? 'Yes' : 'No'
+                      ],
+              new DivElement()
+                ..classes = ['memberItem']
+                ..children = <Element>[
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..text = 'Function',
+                  new DivElement()
+                    ..classes = ['memberValue']
+                    ..children = <Element>[
+                      new FunctionRefElement(_isolate, _code.function,
+                              queue: _r.queue)
+                          .element
+                    ]
+                ],
+              new DivElement()
+                ..classes = ['memberItem']
+                ..children = code.profile == null
+                    ? const []
+                    : [
+                        new DivElement()
+                          ..classes = ['memberName']
+                          ..text = 'Inclusive',
+                        new DivElement()
+                          ..classes = ['memberValue']
+                          ..text = '${code.profile.formattedInclusiveTicks}'
+                      ],
+              new DivElement()
+                ..classes = ['memberItem']
+                ..children = code.profile == null
+                    ? const []
+                    : [
+                        new DivElement()
+                          ..classes = ['memberName']
+                          ..text = 'Exclusive',
+                        new DivElement()
+                          ..classes = ['memberValue']
+                          ..text = '${code.profile.formattedExclusiveTicks}'
+                      ],
+              new DivElement()
+                ..classes = ['memberItem']
+                ..children = <Element>[
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..text = 'Object pool',
+                  new DivElement()
+                    ..classes = ['memberValue']
+                    ..children = <Element>[
+                      new ObjectPoolRefElement(_isolate, _code.objectPool,
+                              queue: _r.queue)
+                          .element
+                    ]
+                ],
+              new DivElement()
+                ..classes = ['memberItem']
+                ..children = inlinedFunctions.isNotEmpty
+                    ? const []
+                    : [
+                        new DivElement()
+                          ..classes = ['memberName']
+                          ..text =
+                              'inlined functions (${inlinedFunctions.length})',
+                        new DivElement()
+                          ..classes = ['memberValue']
+                          ..children = <Element>[
+                            (new CurlyBlockElement(
+                                    expanded: inlinedFunctions.length < 8,
+                                    queue: _r.queue)
+                                  ..content = inlinedFunctions
+                                      .map<Element>((f) =>
+                                          new FunctionRefElement(_isolate, f,
+                                                  queue: _r.queue)
+                                              .element)
+                                      .toList())
+                                .element
+                          ]
+                      ]
+            ],
+          new HRElement(),
+          _inlineRangeTable,
+          new HRElement(),
+          _disassemblyTable
+        ],
+    ];
+    _updateDisassembly();
+    _updateInline();
+  }
+
+  Future _refresh() async {
+    S.Code code = _code as S.Code;
+    await code.reload();
+    _r.dirty();
+  }
+
+  Future _refreshTicks() async {
+    S.Code code = _code as S.Code;
+    final isolate = code.isolate;
+    S.ServiceMap response =
+        await isolate.invokeRpc('_getCpuProfile', {'tags': 'None'});
+    final cpuProfile = new SampleProfile();
+    await cpuProfile.load(isolate, response);
+    _r.dirty();
+  }
+
+  String _formattedAddress(S.CodeInstruction instruction) {
+    if (instruction.address == 0) {
+      return '';
+    }
+    return '0x${instruction.address.toRadixString(16)}';
+  }
+
+  String _formattedAddressRange(S.CodeInlineInterval interval) {
+    String start = interval.start.toRadixString(16);
+    String end = interval.end.toRadixString(16);
+    return '[0x$start, 0x$end)';
+  }
+
+  String _formattedInclusiveInterval(S.CodeInlineInterval interval) {
+    S.Code code = _code as S.Code;
+    if (code.profile == null) {
+      return '';
+    }
+    var intervalTick = code.profile.intervalTicks[interval.start];
+    if (intervalTick == null) {
+      return '';
+    }
+    // Don't show inclusive ticks if they are the same as exclusive ticks.
+    if (intervalTick.inclusiveTicks == intervalTick.exclusiveTicks) {
+      return '';
+    }
+    var pcent = Utils.formatPercent(
+        intervalTick.inclusiveTicks, code.profile.profile.sampleCount);
+    return '$pcent (${intervalTick.inclusiveTicks})';
+  }
+
+  String _formattedExclusiveInterval(S.CodeInlineInterval interval) {
+    S.Code code = _code as S.Code;
+    if (code.profile == null) {
+      return '';
+    }
+    var intervalTick = code.profile.intervalTicks[interval.start];
+    if (intervalTick == null) {
+      return '';
+    }
+    var pcent = Utils.formatPercent(
+        intervalTick.exclusiveTicks, code.profile.profile.sampleCount);
+    return '$pcent (${intervalTick.exclusiveTicks})';
+  }
+
+  String _formattedInclusive(S.CodeInstruction instruction) {
+    S.Code code = _code as S.Code;
+    if (code.profile == null) {
+      return '';
+    }
+    var tick = code.profile.addressTicks[instruction.address];
+    if (tick == null) {
+      return '';
+    }
+    // Don't show inclusive ticks if they are the same as exclusive ticks.
+    if (tick.inclusiveTicks == tick.exclusiveTicks) {
+      return '';
+    }
+    var pcent = Utils.formatPercent(
+        tick.inclusiveTicks, code.profile.profile.sampleCount);
+    return '$pcent (${tick.inclusiveTicks})';
+  }
+
+  String _formattedExclusive(S.CodeInstruction instruction) {
+    S.Code code = _code as S.Code;
+    if (code.profile == null) {
+      return '';
+    }
+    var tick = code.profile.addressTicks[instruction.address];
+    if (tick == null) {
+      return '';
+    }
+    var pcent = Utils.formatPercent(
+        tick.exclusiveTicks, code.profile.profile.sampleCount);
+    return '$pcent (${tick.exclusiveTicks})';
+  }
+
+  void _updateDisassemblyTable() {
+    S.Code code = _code as S.Code;
+    disassemblyTable.clearRows();
+    if (code == null) {
+      return;
+    }
+    for (S.CodeInstruction instruction in code.instructions) {
+      var row = [
+        _formattedAddress(instruction),
+        _formattedInclusive(instruction),
+        _formattedExclusive(instruction),
+        instruction.human,
+        instruction.object
+      ];
+      disassemblyTable.addRow(new SortedTableRow(row));
+    }
+  }
+
+  void _addDisassemblyDOMRow() {
+    var tableBody = _disassemblyTableBody;
+    assert(tableBody != null);
+    var tr = new TableRowElement();
+
+    var cell;
+
+    // Add new space.
+    cell = tr.insertCell(-1);
+    cell.classes.add('monospace');
+    cell = tr.insertCell(-1);
+    cell.classes.add('monospace');
+    cell = tr.insertCell(-1);
+    cell.classes.add('monospace');
+    cell = tr.insertCell(-1);
+    cell.classes.add('monospace');
+    cell = tr.insertCell(-1);
+    cell.classes.add('monospace');
+
+    tableBody.children.add(tr);
+  }
+
+  void _fillDisassemblyDOMRow(TableRowElement tr, int rowIndex) {
+    final row = disassemblyTable.rows[rowIndex];
+    final n = row.values.length;
+    for (var i = 0; i < n; i++) {
+      final cell = tr.children[i];
+      final content = row.values[i];
+      if (content is S.HeapObject) {
+        cell.children = <Element>[
+          anyRef(_isolate, content, _objects, queue: _r.queue)
+        ];
+      } else if (content != null) {
+        String text = '$content';
+        if (i == kDisassemblyColumnIndex) {
+          // Disassembly might be a comment. Reduce indentation, change styling,
+          // widen to span next column (which should be empty).
+          if (text.startsWith('        ;;')) {
+            cell.attributes['colspan'] = '2';
+            cell.classes.add('code-comment');
+            text = text.substring(6);
+          } else {
+            cell.attributes['colspan'] = '1';
+            cell.classes.remove('code-comment');
+          }
+        }
+        cell.text = text;
+      }
+    }
+  }
+
+  void _updateDisassemblyDOMTable() {
+    var tableBody = _disassemblyTableBody;
+    assert(tableBody != null);
+    // Resize DOM table.
+    if (tableBody.children.length > disassemblyTable.sortedRows.length) {
+      // Shrink the table.
+      var deadRows =
+          tableBody.children.length - disassemblyTable.sortedRows.length;
+      for (var i = 0; i < deadRows; i++) {
+        tableBody.children.removeLast();
+      }
+    } else if (tableBody.children.length < disassemblyTable.sortedRows.length) {
+      // Grow table.
+      var newRows =
+          disassemblyTable.sortedRows.length - tableBody.children.length;
+      for (var i = 0; i < newRows; i++) {
+        _addDisassemblyDOMRow();
+      }
+    }
+
+    assert(tableBody.children.length == disassemblyTable.sortedRows.length);
+
+    // Fill table.
+    var i = 0;
+    for (var tr in tableBody.children) {
+      var rowIndex = disassemblyTable.sortedRows[i];
+      _fillDisassemblyDOMRow(tr, rowIndex);
+      i++;
+    }
+  }
+
+  void _updateDisassembly() {
+    _updateDisassemblyTable();
+    _updateDisassemblyDOMTable();
+  }
+
+  void _updateInlineTable() {
+    inlineTable.clearRows();
+    S.Code code = _code as S.Code;
+    for (S.CodeInlineInterval interval in code.inlineIntervals) {
+      var row = [
+        interval,
+        _formattedInclusiveInterval(interval),
+        _formattedExclusiveInterval(interval),
+        interval.functions
+      ];
+      inlineTable.addRow(new SortedTableRow(row));
+    }
+  }
+
+  void _addInlineDOMRow() {
+    var tableBody = _inlineRangeTableBody;
+    assert(tableBody != null);
+    var tr = new TableRowElement();
+
+    var cell;
+
+    // Add new space.
+    cell = tr.insertCell(-1);
+    cell.classes.add('monospace');
+    cell = tr.insertCell(-1);
+    cell.classes.add('monospace');
+    cell = tr.insertCell(-1);
+    cell.classes.add('monospace');
+    cell = tr.insertCell(-1);
+
+    tableBody.children.add(tr);
+  }
+
+  void _fillInlineDOMRow(TableRowElement tr, int rowIndex) {
+    var row = inlineTable.rows[rowIndex];
+    var columns = row.values.length;
+    var addressRangeColumn = 0;
+    var functionsColumn = columns - 1;
+
+    {
+      var addressRangeCell = tr.children[addressRangeColumn];
+      var interval = row.values[addressRangeColumn];
+      var addressRangeString = _formattedAddressRange(interval);
+      var addressRangeElement = new SpanElement();
+      addressRangeElement.classes.add('monospace');
+      addressRangeElement.text = addressRangeString;
+      addressRangeCell.children.clear();
+      addressRangeCell.children.add(addressRangeElement);
+    }
+
+    for (var i = addressRangeColumn + 1; i < columns - 1; i++) {
+      var cell = tr.children[i];
+      cell.text = row.values[i].toString();
+    }
+    var functions = row.values[functionsColumn];
+    var functionsCell = tr.children[functionsColumn];
+    functionsCell.children.clear();
+    for (var func in functions) {
+      functionsCell.children
+          .add(new FunctionRefElement(_isolate, func, queue: _r.queue).element);
+      var gap = new SpanElement();
+      gap.style.minWidth = '1em';
+      gap.text = ' ';
+      functionsCell.children.add(gap);
+    }
+  }
+
+  void _updateInlineDOMTable() {
+    var tableBody = _inlineRangeTableBody;
+    // Resize DOM table.
+    if (tableBody.children.length > inlineTable.sortedRows.length) {
+      // Shrink the table.
+      var deadRows = tableBody.children.length - inlineTable.sortedRows.length;
+      for (var i = 0; i < deadRows; i++) {
+        tableBody.children.removeLast();
+      }
+    } else if (tableBody.children.length < inlineTable.sortedRows.length) {
+      // Grow table.
+      var newRows = inlineTable.sortedRows.length - tableBody.children.length;
+      for (var i = 0; i < newRows; i++) {
+        _addInlineDOMRow();
+      }
+    }
+    assert(tableBody.children.length == inlineTable.sortedRows.length);
+    // Fill table.
+    for (var i = 0; i < inlineTable.sortedRows.length; i++) {
+      var rowIndex = inlineTable.sortedRows[i];
+      var tr = tableBody.children[i];
+      _fillInlineDOMRow(tr, rowIndex);
+    }
+  }
+
+  void _updateInline() {
+    _updateInlineTable();
+    _updateInlineDOMTable();
+  }
+
+  static String _codeKindToString(M.CodeKind kind) {
+    switch (kind) {
+      case M.CodeKind.dart:
+        return 'dart';
+      case M.CodeKind.native:
+        return 'native';
+      case M.CodeKind.stub:
+        return 'stub';
+      case M.CodeKind.tag:
+        return 'tag';
+      case M.CodeKind.collected:
+        return 'collected';
+    }
+    throw new Exception('Unknown CodeKind ($kind)');
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/containers/search_bar.dart b/runtime/observatory_2/lib/src/elements/containers/search_bar.dart
new file mode 100644
index 0000000..f4d83c6
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/containers/search_bar.dart
@@ -0,0 +1,205 @@
+// Copyright (c) 2017, 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.
+
+import 'dart:async';
+import 'dart:html';
+import 'dart:math' as math;
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+
+class SearchResultSelected {
+  final SearchBarElement element;
+  final dynamic item;
+  SearchResultSelected(this.element, this.item);
+}
+
+typedef Iterable<dynamic> SearchBarSearchCallback(Pattern pattern);
+
+class SearchBarElement extends CustomElement implements Renderable {
+  RenderingScheduler<SearchBarElement> _r;
+
+  StreamController<SearchResultSelected> _onSearchResultSelected =
+      new StreamController<SearchResultSelected>.broadcast();
+
+  Stream<RenderedEvent<SearchBarElement>> get onRendered => _r.onRendered;
+  Stream<SearchResultSelected> get onSearchResultSelected =>
+      _onSearchResultSelected.stream;
+
+  StreamSubscription _onKeyDownSubscription;
+
+  Element _workspace;
+  SearchBarSearchCallback _search;
+  bool _isOpen;
+  bool _focusRequested = false;
+  String _lastValue = '';
+  List _results = const [];
+  int _current = 0;
+
+  bool get isOpen => _isOpen;
+  dynamic get current => _results.isNotEmpty ? _results[_current] : null;
+
+  set isOpen(bool value) {
+    if (!value) {
+      _input.value = '';
+      _lastValue = '';
+      if (_results.isNotEmpty) {
+        _results = const [];
+        _current = 0;
+        _triggerSearchResultSelected();
+      }
+    }
+    _isOpen = _r.checkAndReact(_isOpen, value);
+  }
+
+  factory SearchBarElement(SearchBarSearchCallback search,
+      {bool isOpen: false, Element workspace, RenderingQueue queue}) {
+    assert(search != null);
+    assert(isOpen != null);
+    SearchBarElement e = new SearchBarElement.created();
+    e._r = new RenderingScheduler<SearchBarElement>(e, queue: queue);
+    e._search = search;
+    e._isOpen = isOpen;
+    e._workspace = workspace;
+    return e;
+  }
+
+  SearchBarElement.created() : super.created('search-bar');
+
+  @override
+  attached() {
+    super.attached();
+    _r.enable();
+    _workspace?.tabIndex = 1;
+    _onKeyDownSubscription = (_workspace ?? window).onKeyDown.listen((e) {
+      if (e.key.toLowerCase() == 'f' &&
+          !e.shiftKey &&
+          !e.altKey &&
+          e.ctrlKey != e.metaKey) {
+        if (e.metaKey == window.navigator.platform.startsWith('Mac')) {
+          e.stopPropagation();
+          e.preventDefault();
+          isOpen = true;
+          _focusRequested = true;
+          _r.dirty();
+        }
+      }
+    });
+  }
+
+  @override
+  detached() {
+    super.detached();
+    _r.disable(notify: true);
+    _onKeyDownSubscription.cancel();
+  }
+
+  TextInputElement _input;
+  SpanElement _resultsArea;
+
+  void render() {
+    if (_input == null) {
+      _input = new TextInputElement()
+        ..onKeyPress.listen((e) {
+          if (e.keyCode == KeyCode.ENTER) {
+            if (_input.value == '') {
+              _lastValue = '';
+              if (_results.isNotEmpty) {
+                _results = const [];
+                _current = 0;
+                _triggerSearchResultSelected();
+                _r.dirty();
+              }
+            } else if (_input.value != _lastValue) {
+              _lastValue = _input.value;
+              _results = _doSearch(_input.value);
+              _current = 0;
+              _triggerSearchResultSelected();
+              _r.dirty();
+            } else {
+              if (e.shiftKey) {
+                _prev();
+              } else {
+                _next();
+              }
+            }
+          }
+        });
+      _resultsArea = new SpanElement();
+      children = <Element>[
+        _input,
+        _resultsArea,
+        new ButtonElement()
+          ..text = '❌'
+          ..onClick.listen((_) {
+            isOpen = false;
+          })
+      ];
+    }
+    _resultsArea.nodes = [
+      new ButtonElement()
+        ..text = '▲'
+        ..disabled = _results.isEmpty
+        ..onClick.listen((_) => _prev()),
+      new Text(
+          '${math.min(_current + 1, _results.length)} / ${_results.length}'),
+      new ButtonElement()
+        ..text = '▼'
+        ..disabled = _results.isEmpty
+        ..onClick.listen((_) => _next()),
+    ];
+    style.visibility = isOpen ? null : 'collapse';
+    if (_focusRequested) {
+      _input.focus();
+      _focusRequested = false;
+    }
+  }
+
+  void update() {
+    if (!isOpen || _lastValue == '') {
+      return;
+    }
+    final item = current;
+    _results = _doSearch(_lastValue);
+    _current = math.max(0, _results.indexOf(item));
+    _r.dirty();
+  }
+
+  List<dynamic> _doSearch(String value) =>
+      _search(new _CaseInsensitivePatternString(value)).toList(growable: false);
+
+  void _prev() {
+    if (_results.isEmpty) {
+      return;
+    }
+    _current = (_current + _results.length - 1) % _results.length;
+    _triggerSearchResultSelected();
+    _r.dirty();
+  }
+
+  void _next() {
+    if (_results.isEmpty) {
+      return;
+    }
+    _current = (_current + 1) % _results.length;
+    _triggerSearchResultSelected();
+    _r.dirty();
+  }
+
+  void _triggerSearchResultSelected() {
+    _onSearchResultSelected.add(new SearchResultSelected(this, current));
+  }
+}
+
+class _CaseInsensitivePatternString implements Pattern {
+  final String _pattern;
+
+  _CaseInsensitivePatternString(String pattern)
+      : this._pattern = pattern.toLowerCase();
+
+  Iterable<Match> allMatches(String string, [int start = 0]) =>
+      _pattern.allMatches(string.toLowerCase(), start);
+
+  Match matchAsPrefix(String string, [int start = 0]) =>
+      _pattern.matchAsPrefix(string.toLowerCase(), start);
+}
diff --git a/runtime/observatory_2/lib/src/elements/containers/virtual_collection.dart b/runtime/observatory_2/lib/src/elements/containers/virtual_collection.dart
new file mode 100644
index 0000000..28bc24d
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/containers/virtual_collection.dart
@@ -0,0 +1,259 @@
+// Copyright (c) 2016, 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.
+
+import 'dart:async';
+import 'dart:html';
+import 'dart:math' as math;
+import 'package:observatory_2/src/elements/containers/search_bar.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+
+typedef HtmlElement VirtualCollectionCreateCallback();
+typedef List<HtmlElement> VirtualCollectionHeaderCallback();
+typedef void VirtualCollectionUpdateCallback(
+    HtmlElement el, dynamic item, int index);
+typedef bool VirtualCollectionSearchCallback(Pattern pattern, dynamic item);
+
+class VirtualCollectionElement extends CustomElement implements Renderable {
+  RenderingScheduler<VirtualCollectionElement> _r;
+
+  Stream<RenderedEvent<VirtualCollectionElement>> get onRendered =>
+      _r.onRendered;
+
+  VirtualCollectionCreateCallback _create;
+  VirtualCollectionHeaderCallback _createHeader;
+  VirtualCollectionUpdateCallback _update;
+  VirtualCollectionSearchCallback _search;
+  double _itemHeight;
+  int _top;
+  double _height;
+  List _items;
+  StreamSubscription _onScrollSubscription;
+  StreamSubscription _onResizeSubscription;
+
+  List get items => _items;
+
+  set items(Iterable value) {
+    _items = new List.unmodifiable(value);
+    _top = null;
+    _searcher?.update();
+    _r.dirty();
+  }
+
+  factory VirtualCollectionElement(VirtualCollectionCreateCallback create,
+      VirtualCollectionUpdateCallback update,
+      {Iterable items: const [],
+      VirtualCollectionHeaderCallback createHeader,
+      VirtualCollectionSearchCallback search,
+      RenderingQueue queue}) {
+    assert(create != null);
+    assert(update != null);
+    assert(items != null);
+    VirtualCollectionElement e = new VirtualCollectionElement.created();
+    e._r = new RenderingScheduler<VirtualCollectionElement>(e, queue: queue);
+    e._create = create;
+    e._createHeader = createHeader;
+    e._update = update;
+    e._search = search;
+    e._items = new List.unmodifiable(items);
+    return e;
+  }
+
+  VirtualCollectionElement.created() : super.created('virtual-collection');
+
+  @override
+  attached() {
+    super.attached();
+    _r.enable();
+    _top = null;
+    _itemHeight = null;
+    _onScrollSubscription = _viewport.onScroll.listen(_onScroll);
+    _onResizeSubscription = window.onResize.listen(_onResize);
+  }
+
+  @override
+  detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = const [];
+    _onScrollSubscription.cancel();
+    _onResizeSubscription.cancel();
+  }
+
+  DivElement _header;
+  SearchBarElement _searcher;
+  final DivElement _viewport = new DivElement()
+    ..classes = ['viewport', 'container'];
+  final DivElement _spacer = new DivElement()..classes = ['spacer'];
+  final DivElement _buffer = new DivElement()..classes = ['buffer'];
+
+  static int safeFloor(double x) {
+    if (x.isNaN) return 0;
+    return x.floor();
+  }
+
+  static int safeCeil(double x) {
+    if (x.isNaN) return 0;
+    return x.ceil();
+  }
+
+  dynamic getItemFromElement(HtmlElement element) {
+    final el_index = _buffer.children.indexOf(element);
+    if (el_index < 0) {
+      return null;
+    }
+    final item_index =
+        _top + el_index - safeFloor(_buffer.children.length * _inverse_preload);
+    if (0 <= item_index && item_index < items.length) {
+      return _items[item_index];
+    }
+    return null;
+  }
+
+  /// The preloaded element before and after the visible area are:
+  /// 1/preload_size of the number of items in the visble area.
+  static const int _preload = 2;
+
+  /// L = length of all the elements loaded
+  /// l = length of the visible area
+  ///
+  /// L = l + 2 * l / _preload
+  /// l = L * _preload / (_preload + 2)
+  ///
+  /// tail = l / _preload = L * 1 / (_preload + 2) = L * _inverse_preload
+  static const double _inverse_preload = 1 / (_preload + 2);
+
+  var _takeIntoView;
+
+  void takeIntoView(item) {
+    _takeIntoView = item;
+    _r.dirty();
+  }
+
+  void render() {
+    if (children.isEmpty) {
+      children = <Element>[
+        _viewport
+          ..children = <Element>[
+            _spacer
+              ..children = <Element>[
+                _buffer..children = <Element>[_create()]
+              ],
+          ]
+      ];
+      if (_search != null) {
+        _searcher =
+            _searcher ?? new SearchBarElement(_doSearch, queue: _r.queue)
+              ..onSearchResultSelected.listen((e) {
+                takeIntoView(e.item);
+              });
+        children.insert(0, _searcher.element);
+      }
+      if (_createHeader != null) {
+        _header = new DivElement()
+          ..classes = ['header', 'container']
+          ..children = _createHeader();
+        children.insert(0, _header);
+        final rect = _header.getBoundingClientRect();
+        _header.classes.add('attached');
+        _viewport.style.top = '${rect.height}px';
+        final width = _header.children.fold(0.0, _foldWidth);
+        _buffer.style.minWidth = '${width}px';
+      }
+      _itemHeight = _buffer.children[0].getBoundingClientRect().height;
+      _height = getBoundingClientRect().height;
+    }
+
+    if (_takeIntoView != null) {
+      final index = items.indexOf(_takeIntoView);
+      if (index >= 0) {
+        final minScrollTop = _itemHeight * (index + 1) - _height;
+        final maxScrollTop = _itemHeight * index;
+        _viewport.scrollTop =
+            safeFloor((maxScrollTop - minScrollTop) / 2 + minScrollTop);
+      }
+      _takeIntoView = null;
+    }
+
+    final top = safeFloor(_viewport.scrollTop / _itemHeight);
+
+    _spacer.style.height = '${_itemHeight * (_items.length)}px';
+    final tail_length = safeCeil(_height / _itemHeight / _preload);
+    final length = tail_length * 2 + tail_length * _preload;
+
+    if (_buffer.children.length < length) {
+      while (_buffer.children.length != length) {
+        var e = _create();
+        e..style.display = 'hidden';
+        _buffer.children.add(e);
+      }
+      _top = null; // force update;
+    }
+
+    if ((_top == null) || ((top - _top).abs() >= tail_length)) {
+      _buffer.style.top = '${_itemHeight * (top - tail_length)}px';
+      int i = top - tail_length;
+      for (final HtmlElement e in _buffer.children) {
+        if (0 <= i && i < _items.length) {
+          e.style.display = null;
+          _update(e, _items[i], i);
+        } else {
+          e.style.display = 'hidden';
+        }
+        i++;
+      }
+      _top = top;
+    }
+
+    if (_searcher != null) {
+      final current = _searcher.current;
+      int i = _top - tail_length;
+      for (final HtmlElement e in _buffer.children) {
+        if (0 <= i && i < _items.length) {
+          if (_items[i] == current) {
+            e.classes.add('marked');
+          } else {
+            e.classes.remove('marked');
+          }
+        }
+        i++;
+      }
+    }
+    _updateHeader();
+  }
+
+  double _foldWidth(double value, Element child) {
+    return math.max(value, child.getBoundingClientRect().width);
+  }
+
+  void _updateHeader() {
+    if (_header != null) {
+      _header.style.left = '${-_viewport.scrollLeft}px';
+      final width = _buffer.getBoundingClientRect().width;
+      _header.children.last.style.width = '${width}px';
+    }
+  }
+
+  void _onScroll(_) {
+    _r.dirty();
+    // We anticipate the header in advance to avoid flickering
+    _updateHeader();
+  }
+
+  void _onResize(_) {
+    final newHeight = getBoundingClientRect().height;
+    if (newHeight > _height) {
+      _height = newHeight;
+      _r.dirty();
+    } else {
+      // Even if we are not updating the structure the computed size is going to
+      // change
+      _updateHeader();
+    }
+  }
+
+  Iterable<dynamic> _doSearch(Pattern search) {
+    return _items.where((item) => _search(search, item));
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/containers/virtual_tree.dart b/runtime/observatory_2/lib/src/elements/containers/virtual_tree.dart
new file mode 100644
index 0000000..ac5be43
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/containers/virtual_tree.dart
@@ -0,0 +1,183 @@
+// Copyright (c) 2016, 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.
+
+import 'dart:async';
+import 'dart:html';
+import 'dart:collection';
+import 'dart:math' as Math;
+import 'package:observatory_2/src/elements/containers/virtual_collection.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+
+typedef HtmlElement VirtualTreeCreateCallback(
+    toggle({bool autoToggleSingleChildNodes, bool autoToggleWholeTree}));
+typedef void VirtualTreeUpdateCallback(HtmlElement el, dynamic item, int depth);
+typedef Iterable<dynamic> VritualTreeGetChildrenCallback(dynamic value);
+typedef bool VirtualTreeSearchCallback(Pattern pattern, dynamic item);
+
+void virtualTreeUpdateLines(SpanElement element, int n) {
+  n = Math.max(0, n);
+  while (element.children.length > n) {
+    element.children.removeLast();
+  }
+  while (element.children.length < n) {
+    element.children.add(new SpanElement());
+  }
+}
+
+class VirtualTreeElement extends CustomElement implements Renderable {
+  RenderingScheduler<VirtualTreeElement> _r;
+
+  Stream<RenderedEvent<VirtualTreeElement>> get onRendered => _r.onRendered;
+
+  VritualTreeGetChildrenCallback _children;
+  List _items;
+  List _depths;
+  final Set _expanded = new Set();
+
+  List get items => _items;
+
+  set items(Iterable value) {
+    _items = new List.unmodifiable(value);
+    _expanded.clear();
+    _r.dirty();
+  }
+
+  factory VirtualTreeElement(VirtualTreeCreateCallback create,
+      VirtualTreeUpdateCallback update, VritualTreeGetChildrenCallback children,
+      {Iterable items: const [],
+      VirtualTreeSearchCallback search,
+      RenderingQueue queue}) {
+    assert(create != null);
+    assert(update != null);
+    assert(children != null);
+    assert(items != null);
+    VirtualTreeElement e = new VirtualTreeElement.created();
+    e._r = new RenderingScheduler<VirtualTreeElement>(e, queue: queue);
+    e._children = children;
+    e._collection = new VirtualCollectionElement(() {
+      var element;
+      return element = create((
+          {bool autoToggleSingleChildNodes: false,
+          bool autoToggleWholeTree: false}) {
+        var item = e._collection.getItemFromElement(element);
+        if (e.isExpanded(item)) {
+          e.collapse(item,
+              autoCollapseWholeTree: autoToggleWholeTree,
+              autoCollapseSingleChildNodes: autoToggleSingleChildNodes);
+        } else {
+          e.expand(item,
+              autoExpandWholeTree: autoToggleWholeTree,
+              autoExpandSingleChildNodes: autoToggleSingleChildNodes);
+        }
+      });
+    }, (HtmlElement el, dynamic item, int index) {
+      update(el, item, e._depths[index]);
+    }, search: search, queue: queue);
+    e._items = new List.unmodifiable(items);
+    return e;
+  }
+
+  VirtualTreeElement.created() : super.created('virtual-tree');
+
+  bool isExpanded(item) {
+    return _expanded.contains(item);
+  }
+
+  void expand(item,
+      {bool autoExpandSingleChildNodes: false,
+      bool autoExpandWholeTree: false}) {
+    if (_expanded.add(item)) _r.dirty();
+    if (autoExpandWholeTree) {
+      // The tree is potentially very deep, simple recursion can produce a
+      // Stack Overflow
+      Queue pendingNodes = new Queue();
+      pendingNodes.addAll(_children(item));
+      while (pendingNodes.isNotEmpty) {
+        final item = pendingNodes.removeFirst();
+        if (_expanded.add(item)) _r.dirty();
+        pendingNodes.addAll(_children(item));
+      }
+    } else if (autoExpandSingleChildNodes) {
+      var children = _children(item);
+      while (children.length == 1) {
+        _expanded.add(children.first);
+        children = _children(children.first);
+      }
+    }
+  }
+
+  void collapse(item,
+      {bool autoCollapseSingleChildNodes: false,
+      bool autoCollapseWholeTree: false}) {
+    if (_expanded.remove(item)) _r.dirty();
+    if (autoCollapseWholeTree) {
+      // The tree is potentially very deep, simple recursion can produce a
+      // Stack Overflow
+      Queue pendingNodes = new Queue();
+      pendingNodes.addAll(_children(item));
+      while (pendingNodes.isNotEmpty) {
+        final item = pendingNodes.removeFirst();
+        if (_expanded.remove(item)) _r.dirty();
+        pendingNodes.addAll(_children(item));
+      }
+    } else if (autoCollapseSingleChildNodes) {
+      var children = _children(item);
+      while (children.length == 1) {
+        _expanded.remove(children.first);
+        children = _children(children.first);
+      }
+    }
+  }
+
+  @override
+  attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = const [];
+  }
+
+  VirtualCollectionElement _collection;
+
+  void render() {
+    if (children.length == 0) {
+      children = <Element>[_collection.element];
+    }
+
+    final items = [];
+    final depths = new List.filled(_items.length, 0, growable: true);
+
+    {
+      final toDo = new Queue();
+
+      toDo.addAll(_items);
+      while (toDo.isNotEmpty) {
+        final item = toDo.removeFirst();
+
+        items.add(item);
+        if (isExpanded(item)) {
+          final children = _children(item);
+          children
+              .toList(growable: false)
+              .reversed
+              .forEach((c) => toDo.addFirst(c));
+          final depth = depths[items.length - 1];
+          depths.insertAll(
+              items.length, new List.filled(children.length, depth + 1));
+        }
+      }
+    }
+
+    _depths = depths;
+    _collection.items = items;
+
+    _r.waitFor([_collection.onRendered.first]);
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/context_ref.dart b/runtime/observatory_2/lib/src/elements/context_ref.dart
new file mode 100644
index 0000000..85531a2
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/context_ref.dart
@@ -0,0 +1,143 @@
+// 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.
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/curly_block.dart';
+import 'package:observatory_2/src/elements/helpers/any_ref.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/helpers/uris.dart';
+
+class ContextRefElement extends CustomElement implements Renderable {
+  RenderingScheduler<ContextRefElement> _r;
+
+  Stream<RenderedEvent<ContextRefElement>> get onRendered => _r.onRendered;
+
+  M.IsolateRef _isolate;
+  M.ContextRef _context;
+  M.ObjectRepository _objects;
+  M.Context _loadedContext;
+  bool _expandable;
+  bool _expanded = false;
+
+  M.IsolateRef get isolate => _isolate;
+  M.ContextRef get context => _context;
+
+  factory ContextRefElement(
+      M.IsolateRef isolate, M.ContextRef context, M.ObjectRepository objects,
+      {RenderingQueue queue, bool expandable: true}) {
+    assert(isolate != null);
+    assert(context != null);
+    assert(objects != null);
+    ContextRefElement e = new ContextRefElement.created();
+    e._r = new RenderingScheduler<ContextRefElement>(e, queue: queue);
+    e._isolate = isolate;
+    e._context = context;
+    e._objects = objects;
+    e._expandable = expandable;
+    return e;
+  }
+
+  ContextRefElement.created() : super.created('context-ref');
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+  }
+
+  Future _refresh() async {
+    _loadedContext = await _objects.get(_isolate, _context.id);
+    _r.dirty();
+  }
+
+  void render() {
+    var children = <HtmlElement>[
+      new AnchorElement(href: Uris.inspect(_isolate, object: _context))
+        ..children = <Element>[
+          new SpanElement()
+            ..classes = ['emphasize']
+            ..text = 'Context',
+          new SpanElement()..text = ' (${_context.length})',
+        ],
+    ];
+    if (_expandable) {
+      children.addAll([
+        new SpanElement()..text = ' ',
+        (new CurlyBlockElement(expanded: _expanded, queue: _r.queue)
+              ..content = <Element>[
+                new DivElement()
+                  ..classes = ['indent']
+                  ..children = _createValue()
+              ]
+              ..onToggle.listen((e) async {
+                _expanded = e.control.expanded;
+                if (_expanded) {
+                  e.control.disabled = true;
+                  await _refresh();
+                  e.control.disabled = false;
+                }
+              }))
+            .element
+      ]);
+    }
+    this.children = children;
+  }
+
+  List<Element> _createValue() {
+    if (_loadedContext == null) {
+      return [new SpanElement()..text = 'Loading...'];
+    }
+    var members = <Element>[];
+    if (_loadedContext.parentContext != null) {
+      members.add(new DivElement()
+        ..classes = ['memberItem']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = 'parent context',
+          new DivElement()
+            ..classes = ['memberName']
+            ..children = <Element>[
+              new ContextRefElement(
+                      _isolate, _loadedContext.parentContext, _objects,
+                      queue: _r.queue)
+                  .element
+            ]
+        ]);
+    }
+    if (_loadedContext.variables.isNotEmpty) {
+      var variables = _loadedContext.variables.toList();
+      for (var index = 0; index < variables.length; index++) {
+        var variable = variables[index];
+        members.add(new DivElement()
+          ..classes = ['memberItem']
+          ..children = <Element>[
+            new DivElement()
+              ..classes = ['memberName']
+              ..text = '[ $index ]',
+            new DivElement()
+              ..classes = ['memberName']
+              ..children = <Element>[
+                anyRef(_isolate, variable.value, _objects, queue: _r.queue)
+              ]
+          ]);
+      }
+    }
+    return [
+      new DivElement()
+        ..classes = ['memberList']
+        ..children = members
+    ];
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/context_view.dart b/runtime/observatory_2/lib/src/elements/context_view.dart
new file mode 100644
index 0000000..3b759eb
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/context_view.dart
@@ -0,0 +1,195 @@
+// 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.
+
+import 'dart:async';
+import 'dart:html';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/context_ref.dart';
+import 'package:observatory_2/src/elements/curly_block.dart';
+import 'package:observatory_2/src/elements/helpers/any_ref.dart';
+import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
+import 'package:observatory_2/src/elements/helpers/nav_menu.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/nav/class_menu.dart';
+import 'package:observatory_2/src/elements/nav/isolate_menu.dart';
+import 'package:observatory_2/src/elements/nav/notify.dart';
+import 'package:observatory_2/src/elements/nav/refresh.dart';
+import 'package:observatory_2/src/elements/nav/top_menu.dart';
+import 'package:observatory_2/src/elements/nav/vm_menu.dart';
+import 'package:observatory_2/src/elements/object_common.dart';
+import 'package:observatory_2/src/elements/view_footer.dart';
+
+class ContextViewElement extends CustomElement implements Renderable {
+  RenderingScheduler<ContextViewElement> _r;
+
+  Stream<RenderedEvent<ContextViewElement>> get onRendered => _r.onRendered;
+
+  M.VM _vm;
+  M.IsolateRef _isolate;
+  M.EventRepository _events;
+  M.NotificationRepository _notifications;
+  M.Context _context;
+  M.ContextRepository _contexts;
+  M.RetainedSizeRepository _retainedSizes;
+  M.ReachableSizeRepository _reachableSizes;
+  M.InboundReferencesRepository _references;
+  M.RetainingPathRepository _retainingPaths;
+  M.ObjectRepository _objects;
+
+  M.VMRef get vm => _vm;
+  M.IsolateRef get isolate => _isolate;
+  M.NotificationRepository get notifications => _notifications;
+  M.Context get context => _context;
+
+  factory ContextViewElement(
+      M.VM vm,
+      M.IsolateRef isolate,
+      M.Context context,
+      M.EventRepository events,
+      M.NotificationRepository notifications,
+      M.ContextRepository contexts,
+      M.RetainedSizeRepository retainedSizes,
+      M.ReachableSizeRepository reachableSizes,
+      M.InboundReferencesRepository references,
+      M.RetainingPathRepository retainingPaths,
+      M.ObjectRepository objects,
+      {RenderingQueue queue}) {
+    assert(vm != null);
+    assert(isolate != null);
+    assert(events != null);
+    assert(notifications != null);
+    assert(context != null);
+    assert(contexts != null);
+    assert(retainedSizes != null);
+    assert(reachableSizes != null);
+    assert(references != null);
+    assert(retainingPaths != null);
+    assert(objects != null);
+    ContextViewElement e = new ContextViewElement.created();
+    e._r = new RenderingScheduler<ContextViewElement>(e, queue: queue);
+    e._vm = vm;
+    e._isolate = isolate;
+    e._events = events;
+    e._notifications = notifications;
+    e._context = context;
+    e._contexts = contexts;
+    e._retainedSizes = retainedSizes;
+    e._reachableSizes = reachableSizes;
+    e._references = references;
+    e._retainingPaths = retainingPaths;
+    e._objects = objects;
+    return e;
+  }
+
+  ContextViewElement.created() : super.created('context-view');
+
+  @override
+  attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+  }
+
+  void render() {
+    var content = <Element>[
+      navBar(<Element>[
+        new NavTopMenuElement(queue: _r.queue).element,
+        new NavVMMenuElement(_vm, _events, queue: _r.queue).element,
+        new NavIsolateMenuElement(_isolate, _events, queue: _r.queue).element,
+        new NavClassMenuElement(_isolate, _context.clazz, queue: _r.queue)
+            .element,
+        navMenu('instance'),
+        (new NavRefreshElement(queue: _r.queue)
+              ..onRefresh.listen((e) async {
+                e.element.disabled = true;
+                _context = await _contexts.get(_isolate, _context.id);
+                _r.dirty();
+              }))
+            .element,
+        new NavNotifyElement(_notifications, queue: _r.queue).element
+      ]),
+      new DivElement()
+        ..classes = ['content-centered-big']
+        ..children = <Element>[
+          new HeadingElement.h2()..text = 'Context',
+          new HRElement(),
+          new ObjectCommonElement(_isolate, _context, _retainedSizes,
+                  _reachableSizes, _references, _retainingPaths, _objects,
+                  queue: _r.queue)
+              .element
+        ]
+    ];
+    if (_context.parentContext != null) {
+      content.addAll([
+        new BRElement(),
+        new DivElement()
+          ..classes = ['content-centered-big']
+          ..children = <Element>[
+            new DivElement()
+              ..classes = ['memberList']
+              ..children = <Element>[
+                new DivElement()
+                  ..classes = ['memberItem']
+                  ..children = <Element>[
+                    new DivElement()
+                      ..classes = ['memberName']
+                      ..text = 'parent context',
+                    new DivElement()
+                      ..classes = ['memberName']
+                      ..children = <Element>[
+                        new ContextRefElement(
+                                _isolate, _context.parentContext, _objects,
+                                queue: _r.queue)
+                            .element
+                      ]
+                  ]
+              ]
+          ]
+      ]);
+    }
+    content.add(new HRElement());
+    if (_context.variables.isNotEmpty) {
+      int index = 0;
+      content.addAll([
+        new DivElement()
+          ..classes = ['content-centered-big']
+          ..children = <Element>[
+            new SpanElement()..text = 'Variables ',
+            (new CurlyBlockElement(expanded: true, queue: _r.queue)
+                  ..content = <Element>[
+                    new DivElement()
+                      ..classes = ['memberList']
+                      ..children = _context.variables
+                          .map<Element>((variable) => new DivElement()
+                            ..classes = ['memberItem']
+                            ..children = <Element>[
+                              new DivElement()
+                                ..classes = ['memberName']
+                                ..text = '[ ${++index} ]',
+                              new DivElement()
+                                ..classes = ['memberName']
+                                ..children = <Element>[
+                                  anyRef(_isolate, variable.value, _objects,
+                                      queue: _r.queue)
+                                ]
+                            ])
+                          .toList()
+                  ])
+                .element
+          ]
+      ]);
+    }
+    content.add(new DivElement()
+      ..classes = ['content-centered-big']
+      ..children = <Element>[new ViewFooterElement(queue: _r.queue).element]);
+    children = content;
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/cpu_profile.dart b/runtime/observatory_2/lib/src/elements/cpu_profile.dart
new file mode 100644
index 0000000..57ad7e1
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/cpu_profile.dart
@@ -0,0 +1,169 @@
+// 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.
+
+library cpu_profile_element;
+
+import 'dart:async';
+import 'dart:html';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/cpu_profile/virtual_tree.dart';
+import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
+import 'package:observatory_2/src/elements/helpers/nav_menu.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/helpers/uris.dart';
+import 'package:observatory_2/src/elements/nav/isolate_menu.dart';
+import 'package:observatory_2/src/elements/nav/notify.dart';
+import 'package:observatory_2/src/elements/nav/refresh.dart';
+import 'package:observatory_2/src/elements/nav/top_menu.dart';
+import 'package:observatory_2/src/elements/nav/vm_menu.dart';
+import 'package:observatory_2/src/elements/sample_buffer_control.dart';
+import 'package:observatory_2/src/elements/stack_trace_tree_config.dart';
+
+class CpuProfileElement extends CustomElement implements Renderable {
+  RenderingScheduler<CpuProfileElement> _r;
+
+  Stream<RenderedEvent<CpuProfileElement>> get onRendered => _r.onRendered;
+
+  M.VM _vm;
+  M.IsolateRef _isolate;
+  M.EventRepository _events;
+  M.NotificationRepository _notifications;
+  M.IsolateSampleProfileRepository _profiles;
+  Stream<M.SampleProfileLoadingProgressEvent> _progressStream;
+  M.SampleProfileLoadingProgress _progress;
+  M.SampleProfileTag _tag = M.SampleProfileTag.none;
+  ProfileTreeMode _mode = ProfileTreeMode.function;
+  M.ProfileTreeDirection _direction = M.ProfileTreeDirection.exclusive;
+  String _filter = '';
+
+  M.IsolateRef get isolate => _isolate;
+  M.NotificationRepository get notifications => _notifications;
+  M.IsolateSampleProfileRepository get profiles => _profiles;
+  M.VMRef get vm => _vm;
+
+  factory CpuProfileElement(
+      M.VM vm,
+      M.IsolateRef isolate,
+      M.EventRepository events,
+      M.NotificationRepository notifications,
+      M.IsolateSampleProfileRepository profiles,
+      {RenderingQueue queue}) {
+    assert(vm != null);
+    assert(isolate != null);
+    assert(events != null);
+    assert(notifications != null);
+    assert(profiles != null);
+    CpuProfileElement e = new CpuProfileElement.created();
+    e._r = new RenderingScheduler<CpuProfileElement>(e, queue: queue);
+    e._vm = vm;
+    e._isolate = isolate;
+    e._events = events;
+    e._notifications = notifications;
+    e._profiles = profiles;
+    return e;
+  }
+
+  CpuProfileElement.created() : super.created('cpu-profile');
+
+  @override
+  attached() {
+    super.attached();
+    _r.enable();
+    _request();
+  }
+
+  @override
+  detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+  }
+
+  void render() {
+    var content = <Element>[
+      navBar(<Element>[
+        new NavTopMenuElement(queue: _r.queue).element,
+        new NavVMMenuElement(_vm, _events, queue: _r.queue).element,
+        new NavIsolateMenuElement(_isolate, _events, queue: _r.queue).element,
+        navMenu('cpu profile', link: Uris.cpuProfiler(_isolate)),
+        (new NavRefreshElement(queue: _r.queue)..onRefresh.listen(_refresh))
+            .element,
+        (new NavRefreshElement(label: 'Clear', queue: _r.queue)
+              ..onRefresh.listen(_clearCpuProfile))
+            .element,
+        new NavNotifyElement(_notifications, queue: _r.queue).element
+      ]),
+    ];
+    if (_progress == null) {
+      children = content;
+      return;
+    }
+    content.add((new SampleBufferControlElement(_vm, _progress, _progressStream,
+            selectedTag: _tag, queue: _r.queue)
+          ..onTagChange.listen((e) {
+            _tag = e.element.selectedTag;
+            _request();
+          }))
+        .element);
+    if (_progress.status == M.SampleProfileLoadingStatus.loaded) {
+      CpuProfileVirtualTreeElement tree;
+      content.addAll([
+        new BRElement(),
+        (new StackTraceTreeConfigElement(
+                mode: _mode,
+                direction: _direction,
+                filter: _filter,
+                queue: _r.queue)
+              ..onModeChange.listen((e) {
+                _mode = tree.mode = e.element.mode;
+              })
+              ..onFilterChange.listen((e) {
+                _filter = e.element.filter.trim();
+                tree.filters = _filter.isNotEmpty
+                    ? [
+                        (node) {
+                          return node.name.contains(_filter);
+                        }
+                      ]
+                    : const [];
+              })
+              ..onDirectionChange.listen((e) {
+                _direction = tree.direction = e.element.direction;
+              }))
+            .element,
+        new BRElement(),
+        (tree = new CpuProfileVirtualTreeElement(_isolate, _progress.profile,
+                queue: _r.queue))
+            .element
+      ]);
+    }
+    children = content;
+  }
+
+  Future _request({bool clear: false, bool forceFetch: false}) async {
+    _progress = null;
+    _progressStream =
+        _profiles.get(isolate, _tag, clear: clear, forceFetch: forceFetch);
+    _r.dirty();
+    _progress = (await _progressStream.first).progress;
+    _r.dirty();
+    if (M.isSampleProcessRunning(_progress.status)) {
+      _progress = (await _progressStream.last).progress;
+      _r.dirty();
+    }
+  }
+
+  Future _clearCpuProfile(RefreshEvent e) async {
+    e.element.disabled = true;
+    await _request(clear: true);
+    e.element.disabled = false;
+  }
+
+  Future _refresh(e) async {
+    e.element.disabled = true;
+    await _request(forceFetch: true);
+    e.element.disabled = false;
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/cpu_profile/virtual_tree.dart b/runtime/observatory_2/lib/src/elements/cpu_profile/virtual_tree.dart
new file mode 100644
index 0000000..2889650
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/cpu_profile/virtual_tree.dart
@@ -0,0 +1,308 @@
+// 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.
+
+import 'dart:async';
+import 'dart:html';
+import 'dart:math' as Math;
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/stack_trace_tree_config.dart'
+    show ProfileTreeMode;
+import 'package:observatory_2/src/elements/code_ref.dart';
+import 'package:observatory_2/src/elements/containers/virtual_tree.dart';
+import 'package:observatory_2/src/elements/function_ref.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/utils.dart';
+
+export 'package:observatory_2/src/elements/stack_trace_tree_config.dart'
+    show ProfileTreeMode;
+
+class CpuProfileVirtualTreeElement extends CustomElement implements Renderable {
+  RenderingScheduler<CpuProfileVirtualTreeElement> _r;
+
+  Stream<RenderedEvent<CpuProfileVirtualTreeElement>> get onRendered =>
+      _r.onRendered;
+
+  M.ProfileTreeDirection _direction;
+  ProfileTreeMode _mode;
+  M.SampleProfileType _type;
+  M.IsolateRef _isolate;
+  M.SampleProfile _profile;
+  Iterable<M.CallTreeNodeFilter> _filters;
+
+  M.ProfileTreeDirection get direction => _direction;
+  ProfileTreeMode get mode => _mode;
+  M.SampleProfileType get type => _type;
+  M.IsolateRef get isolate => _isolate;
+  M.SampleProfile get profile => _profile;
+  Iterable<M.CallTreeNodeFilter> get filters => _filters;
+
+  set direction(M.ProfileTreeDirection value) =>
+      _direction = _r.checkAndReact(_direction, value);
+  set mode(ProfileTreeMode value) => _mode = _r.checkAndReact(_mode, value);
+  set filters(Iterable<M.CallTreeNodeFilter> value) {
+    _filters = new List.unmodifiable(value);
+    _r.dirty();
+  }
+
+  factory CpuProfileVirtualTreeElement(Object owner, M.SampleProfile profile,
+      {ProfileTreeMode mode: ProfileTreeMode.function,
+      M.SampleProfileType type: M.SampleProfileType.cpu,
+      M.ProfileTreeDirection direction: M.ProfileTreeDirection.exclusive,
+      RenderingQueue queue}) {
+    assert(profile != null);
+    assert(mode != null);
+    assert(direction != null);
+    CpuProfileVirtualTreeElement e = new CpuProfileVirtualTreeElement.created();
+    e._r =
+        new RenderingScheduler<CpuProfileVirtualTreeElement>(e, queue: queue);
+    e._isolate = owner;
+    e._profile = profile;
+    e._mode = mode;
+    e._type = type;
+    e._direction = direction;
+    return e;
+  }
+
+  CpuProfileVirtualTreeElement.created()
+      : super.created('cpu-profile-virtual-tree');
+
+  @override
+  attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+  }
+
+  VirtualTreeElement _tree;
+
+  void render() {
+    var tree;
+    var create;
+    var update;
+    var search;
+    switch (type) {
+      case M.SampleProfileType.cpu:
+        create = _createCpuRow;
+        if (mode == ProfileTreeMode.code) {
+          update = _updateCpuCodeRow;
+          search = _searchCode;
+          tree = _profile.loadCodeTree(_direction);
+        } else if (mode == ProfileTreeMode.function) {
+          update = _updateCpuFunctionRow;
+          search = _searchFunction;
+          tree = _profile.loadFunctionTree(_direction);
+        } else {
+          throw new Exception('Unknown ProfileTreeMode: $mode');
+        }
+        break;
+      case M.SampleProfileType.memory:
+        create = _createMemoryRow;
+        if (mode == ProfileTreeMode.code) {
+          update = _updateMemoryCodeRow;
+          search = _searchCode;
+          tree = _profile.loadCodeTree(_direction);
+        } else if (mode == ProfileTreeMode.function) {
+          update = _updateMemoryFunctionRow;
+          search = _searchFunction;
+          tree = _profile.loadFunctionTree(_direction);
+        } else {
+          throw new Exception('Unknown ProfileTreeMode: $mode');
+        }
+        break;
+      default:
+        throw new Exception('Unknown SampleProfileType: $type');
+    }
+    if (filters != null) {
+      tree = filters.fold(tree, (tree, filter) {
+        return tree?.filtered(filter);
+      });
+    }
+    if (tree == null) {
+      children = <Element>[new HeadingElement.h1()..text = 'No Results'];
+      return;
+    }
+    _tree = new VirtualTreeElement(create, update, _getChildren,
+        items: tree.root.children, search: search, queue: _r.queue);
+    if (tree.root.children.length == 0) {
+      children = <Element>[
+        new DivElement()
+          ..classes = ['tree-item']
+          ..children = <Element>[new HeadingElement.h1()..text = 'No Samples']
+      ];
+      return;
+    } else if (tree.root.children.length == 1) {
+      _tree.expand(tree.root.children.first, autoExpandSingleChildNodes: true);
+    }
+    children = <Element>[_tree.element];
+  }
+
+  static HtmlElement _createCpuRow(toggle) {
+    return new DivElement()
+      ..classes = ['tree-item']
+      ..children = <Element>[
+        new SpanElement()
+          ..classes = ['inclusive']
+          ..title = 'global % on stack',
+        new SpanElement()
+          ..classes = ['exclusive']
+          ..title = 'global % executing',
+        new SpanElement()..classes = ['lines'],
+        new ButtonElement()
+          ..classes = ['expander']
+          ..onClick.listen((_) => toggle(autoToggleSingleChildNodes: true)),
+        new SpanElement()
+          ..classes = ['percentage']
+          ..title = 'tree node %',
+        new SpanElement()..classes = ['name']
+      ];
+  }
+
+  static HtmlElement _createMemoryRow(toggle) {
+    return new DivElement()
+      ..classes = ['tree-item']
+      ..children = <Element>[
+        new SpanElement()
+          ..classes = ['inclusive']
+          ..title = 'memory allocated from resulting calls: ',
+        new SpanElement()
+          ..classes = ['exclusive']
+          ..title = 'memory allocated during execution: ',
+        new SpanElement()..classes = ['lines'],
+        new ButtonElement()
+          ..classes = ['expander']
+          ..onClick.listen((_) => toggle(autoToggleSingleChildNodes: true)),
+        new SpanElement()
+          ..classes = ['percentage']
+          ..title = 'tree node %',
+        new SpanElement()..classes = ['name']
+      ];
+  }
+
+  static Iterable<M.CallTreeNode> _getChildren(nodeDynamic) {
+    M.CallTreeNode node = nodeDynamic;
+    return node.children;
+  }
+
+  static const String _expandedIcon = '▼';
+  static const String _collapsedIcon = '►';
+
+  void _updateCpuFunctionRow(HtmlElement element, itemDynamic, int depth) {
+    M.FunctionCallTreeNode item = itemDynamic;
+    element.children[0].text = Utils.formatPercentNormalized(
+        item.profileFunction.normalizedInclusiveTicks);
+    element.children[1].text = Utils.formatPercentNormalized(
+        item.profileFunction.normalizedExclusiveTicks);
+    _updateLines(element.children[2].children, depth);
+    if (item.children.isNotEmpty) {
+      element.children[3].text =
+          _tree.isExpanded(item) ? _expandedIcon : _collapsedIcon;
+    } else {
+      element.children[3].text = '';
+    }
+    element.children[4].text = Utils.formatPercentNormalized(item.percentage);
+    element.children[5] = (new FunctionRefElement(
+            _isolate, item.profileFunction.function,
+            queue: _r.queue)
+          ..classes = ['name'])
+        .element;
+  }
+
+  void _updateMemoryFunctionRow(HtmlElement element, itemDynamic, int depth) {
+    M.FunctionCallTreeNode item = itemDynamic;
+    element.children[0].text =
+        Utils.formatSize(item.inclusiveNativeAllocations);
+    element.children[0].title = 'memory allocated from resulting calls: '
+        '${item.inclusiveNativeAllocations}B';
+    element.children[1].text =
+        Utils.formatSize(item.exclusiveNativeAllocations);
+    element.children[1].title = 'memory allocated during execution: '
+        '${item.exclusiveNativeAllocations}B';
+    _updateLines(element.children[2].children, depth);
+    if (item.children.isNotEmpty) {
+      element.children[3].text =
+          _tree.isExpanded(item) ? _expandedIcon : _collapsedIcon;
+    } else {
+      element.children[3].text = '';
+    }
+    element.children[4].text = Utils.formatPercentNormalized(item.percentage);
+    element.children[5] = (new FunctionRefElement(
+            null, item.profileFunction.function,
+            queue: _r.queue)
+          ..classes = ['name'])
+        .element;
+  }
+
+  bool _searchFunction(Pattern pattern, itemDynamic) {
+    M.FunctionCallTreeNode item = itemDynamic;
+    return M
+        .getFunctionFullName(item.profileFunction.function)
+        .contains(pattern);
+  }
+
+  void _updateCpuCodeRow(HtmlElement element, itemDynamic, int depth) {
+    M.CodeCallTreeNode item = itemDynamic;
+    element.children[0].text = Utils.formatPercentNormalized(
+        item.profileCode.normalizedInclusiveTicks);
+    element.children[1].text = Utils.formatPercentNormalized(
+        item.profileCode.normalizedExclusiveTicks);
+    _updateLines(element.children[2].children, depth);
+    if (item.children.isNotEmpty) {
+      element.children[3].text =
+          _tree.isExpanded(item) ? _expandedIcon : _collapsedIcon;
+    } else {
+      element.children[3].text = '';
+    }
+    element.children[4].text = Utils.formatPercentNormalized(item.percentage);
+    element.children[5] =
+        (new CodeRefElement(_isolate, item.profileCode.code, queue: _r.queue)
+              ..classes = ['name'])
+            .element;
+  }
+
+  void _updateMemoryCodeRow(HtmlElement element, itemDynamic, int depth) {
+    M.CodeCallTreeNode item = itemDynamic;
+    element.children[0].text =
+        Utils.formatSize(item.inclusiveNativeAllocations);
+    element.children[0].title = 'memory allocated from resulting calls: '
+        '${item.inclusiveNativeAllocations}B';
+    element.children[1].text =
+        Utils.formatSize(item.exclusiveNativeAllocations);
+    element.children[1].title = 'memory allocated during execution: '
+        '${item.exclusiveNativeAllocations}B';
+    _updateLines(element.children[2].children, depth);
+    if (item.children.isNotEmpty) {
+      element.children[3].text =
+          _tree.isExpanded(item) ? _expandedIcon : _collapsedIcon;
+    } else {
+      element.children[3].text = '';
+    }
+    element.children[4].text = Utils.formatPercentNormalized(item.percentage);
+    element.children[5] =
+        (new CodeRefElement(null, item.profileCode.code, queue: _r.queue)
+              ..classes = ['name'])
+            .element;
+  }
+
+  bool _searchCode(Pattern pattern, itemDynamic) {
+    M.CodeCallTreeNode item = itemDynamic;
+    return item.profileCode.code.name.contains(pattern);
+  }
+
+  static _updateLines(List<Element> lines, int n) {
+    n = Math.max(0, n);
+    while (lines.length > n) {
+      lines.removeLast();
+    }
+    while (lines.length < n) {
+      lines.add(new SpanElement());
+    }
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/cpu_profile_table.dart b/runtime/observatory_2/lib/src/elements/cpu_profile_table.dart
new file mode 100644
index 0000000..c68d00a
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/cpu_profile_table.dart
@@ -0,0 +1,473 @@
+// 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.
+
+library cpu_profile_table_element;
+
+import 'dart:async';
+import 'dart:html';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/containers/virtual_collection.dart';
+import 'package:observatory_2/src/elements/cpu_profile/virtual_tree.dart';
+import 'package:observatory_2/src/elements/function_ref.dart';
+import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
+import 'package:observatory_2/src/elements/helpers/nav_menu.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/nav/isolate_menu.dart';
+import 'package:observatory_2/src/elements/nav/notify.dart';
+import 'package:observatory_2/src/elements/nav/refresh.dart';
+import 'package:observatory_2/src/elements/nav/top_menu.dart';
+import 'package:observatory_2/src/elements/nav/vm_menu.dart';
+import 'package:observatory_2/src/elements/sample_buffer_control.dart';
+import 'package:observatory_2/src/elements/stack_trace_tree_config.dart';
+import 'package:observatory_2/utils.dart';
+
+enum _Table { functions, caller, callee }
+
+enum _SortingField { exclusive, inclusive, caller, callee, method }
+
+enum _SortingDirection { ascending, descending }
+
+class CpuProfileTableElement extends CustomElement implements Renderable {
+  RenderingScheduler<CpuProfileTableElement> _r;
+
+  Stream<RenderedEvent<CpuProfileTableElement>> get onRendered => _r.onRendered;
+
+  M.VM _vm;
+  M.IsolateRef _isolate;
+  M.EventRepository _events;
+  M.NotificationRepository _notifications;
+  M.IsolateSampleProfileRepository _profiles;
+  Stream<M.SampleProfileLoadingProgressEvent> _progressStream;
+  M.SampleProfileLoadingProgress _progress;
+  final _sortingField = <_Table, _SortingField>{
+    _Table.functions: _SortingField.exclusive,
+    _Table.caller: _SortingField.caller,
+    _Table.callee: _SortingField.callee,
+  };
+  final _sortingDirection = <_Table, _SortingDirection>{
+    _Table.functions: _SortingDirection.descending,
+    _Table.caller: _SortingDirection.descending,
+    _Table.callee: _SortingDirection.descending,
+  };
+  String _filter = '';
+
+  M.IsolateRef get isolate => _isolate;
+  M.NotificationRepository get notifications => _notifications;
+  M.IsolateSampleProfileRepository get profiles => _profiles;
+  M.VMRef get vm => _vm;
+
+  factory CpuProfileTableElement(
+      M.VM vm,
+      M.IsolateRef isolate,
+      M.EventRepository events,
+      M.NotificationRepository notifications,
+      M.IsolateSampleProfileRepository profiles,
+      {RenderingQueue queue}) {
+    assert(vm != null);
+    assert(isolate != null);
+    assert(events != null);
+    assert(notifications != null);
+    assert(profiles != null);
+    CpuProfileTableElement e = new CpuProfileTableElement.created();
+    e._r = new RenderingScheduler<CpuProfileTableElement>(e, queue: queue);
+    e._vm = vm;
+    e._isolate = isolate;
+    e._events = events;
+    e._notifications = notifications;
+    e._profiles = profiles;
+    return e;
+  }
+
+  CpuProfileTableElement.created() : super.created('cpu-profile-table');
+
+  @override
+  attached() {
+    super.attached();
+    _r.enable();
+    _request();
+  }
+
+  @override
+  detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+  }
+
+  void render() {
+    var content = <Element>[
+      navBar(<Element>[
+        new NavTopMenuElement(queue: _r.queue).element,
+        new NavVMMenuElement(_vm, _events, queue: _r.queue).element,
+        new NavIsolateMenuElement(_isolate, _events, queue: _r.queue).element,
+        navMenu('cpu profile (table)'),
+        (new NavRefreshElement(queue: _r.queue)..onRefresh.listen(_refresh))
+            .element,
+        (new NavRefreshElement(label: 'Clear', queue: _r.queue)
+              ..onRefresh.listen(_clearCpuSamples))
+            .element,
+        new NavNotifyElement(_notifications, queue: _r.queue).element
+      ]),
+    ];
+    if (_progress == null) {
+      children = content;
+      return;
+    }
+    content.add(new SampleBufferControlElement(_vm, _progress, _progressStream,
+            showTag: false, queue: _r.queue)
+        .element);
+    if (_progress.status == M.SampleProfileLoadingStatus.loaded) {
+      content.add(new BRElement());
+      content.addAll(_createTables());
+      content.add(new BRElement());
+      content.addAll(_createTree());
+    }
+    children = content;
+  }
+
+  M.ProfileFunction _selected;
+  VirtualCollectionElement _functions;
+  VirtualCollectionElement _callers;
+  VirtualCollectionElement _callees;
+
+  List<Element> _createTables() {
+    _functions = _functions ??
+        new VirtualCollectionElement(_createFunction, _updateFunction,
+            createHeader: _createFunctionHeader,
+            search: _searchFunction,
+            queue: _r.queue);
+    // If there's no samples, don't populate the function list.
+    _functions.items = (_progress.profile.sampleCount != 0)
+        ? _progress.profile.functions.toList()
+        : []
+      ..sort(_createSorter(_Table.functions));
+    _functions.takeIntoView(_selected);
+    _callers = _callers ??
+        new VirtualCollectionElement(_createCaller, _updateCaller,
+            createHeader: _createCallerHeader,
+            search: _searchFunction,
+            queue: _r.queue);
+    _callees = _callees ??
+        new VirtualCollectionElement(_createCallee, _updateCallee,
+            createHeader: _createCalleeHeader,
+            search: _searchFunction,
+            queue: _r.queue);
+    if (_selected != null) {
+      _callers.items = _selected.callers.keys.toList()
+        ..sort(_createSorter(_Table.caller));
+      _callees.items = _selected.callees.keys.toList()
+        ..sort(_createSorter(_Table.callee));
+    } else {
+      _callers.items = const [];
+      _callees.items = const [];
+    }
+    return <Element>[
+      new DivElement()
+        ..classes = ['profile-trees']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['profile-trees-all']
+            ..children = <Element>[_functions.element],
+          new DivElement()
+            ..classes = ['profile-trees-current']
+            ..children = <Element>[
+              new DivElement()
+                ..classes = ['profile-trees-caller']
+                ..children = <Element>[_callers.element],
+              new DivElement()
+                ..classes = ['profile-trees-selected']
+                ..children = _selected == null
+                    ? [new SpanElement()..text = 'No element selected']
+                    : [
+                        new FunctionRefElement(_isolate, _selected.function,
+                                queue: _r.queue)
+                            .element
+                      ],
+              new DivElement()
+                ..classes = ['profile-trees-callee']
+                ..children = <Element>[_callees.element]
+            ]
+        ]
+    ];
+  }
+
+  HtmlElement _createFunction() {
+    final element = new DivElement()
+      ..classes = ['function-item']
+      ..children = <Element>[
+        new SpanElement()
+          ..classes = ['exclusive']
+          ..text = '0%',
+        new SpanElement()
+          ..classes = ['inclusive']
+          ..text = '0%',
+        new SpanElement()..classes = ['name']
+      ];
+    element.onClick.listen((e) {
+      if (e.target is AnchorElement) {
+        return;
+      }
+      _selected = _functions.getItemFromElement(element);
+      _r.dirty();
+    });
+    return element;
+  }
+
+  void _updateFunction(Element e, itemDynamic, int index) {
+    M.ProfileFunction item = itemDynamic;
+    if (item == _selected) {
+      e.classes = ['function-item', 'selected'];
+    } else {
+      e.classes = ['function-item'];
+    }
+    e.children[0].text = Utils.formatPercentNormalized(_getExclusiveT(item));
+    e.children[1].text = Utils.formatPercentNormalized(_getInclusiveT(item));
+    e.children[2].text = M.getFunctionFullName(item.function);
+  }
+
+  List<HtmlElement> _createFunctionHeader() => [
+        new DivElement()
+          ..classes = ['function-item']
+          ..children = <Element>[
+            _createHeaderButton(
+                const ['exclusive'],
+                'Execution(%)',
+                _Table.functions,
+                _SortingField.exclusive,
+                _SortingDirection.descending),
+            _createHeaderButton(
+                const ['inclusive'],
+                'Stack(%)',
+                _Table.functions,
+                _SortingField.inclusive,
+                _SortingDirection.descending),
+            _createHeaderButton(const ['name'], 'Method', _Table.functions,
+                _SortingField.method, _SortingDirection.ascending),
+          ]
+      ];
+
+  bool _searchFunction(Pattern pattern, itemDynamic) {
+    M.ProfileFunction item = itemDynamic;
+    return M.getFunctionFullName(item.function).contains(pattern);
+  }
+
+  void _setSorting(
+      _Table table, _SortingField field, _SortingDirection defaultDirection) {
+    if (_sortingField[table] == field) {
+      switch (_sortingDirection[table]) {
+        case _SortingDirection.descending:
+          _sortingDirection[table] = _SortingDirection.ascending;
+          break;
+        case _SortingDirection.ascending:
+          _sortingDirection[table] = _SortingDirection.descending;
+          break;
+      }
+    } else {
+      _sortingDirection[table] = defaultDirection;
+      _sortingField[table] = field;
+    }
+    _r.dirty();
+  }
+
+  HtmlElement _createCallee() {
+    final element = new DivElement()
+      ..classes = ['function-item']
+      ..children = <Element>[
+        new SpanElement()
+          ..classes = ['inclusive']
+          ..text = '0%',
+        new SpanElement()..classes = ['name']
+      ];
+    element.onClick.listen((e) {
+      if (e.target is AnchorElement) {
+        return;
+      }
+      _selected = _callees.getItemFromElement(element);
+      _r.dirty();
+    });
+    return element;
+  }
+
+  void _updateCallee(Element e, item, int index) {
+    e.children[0].text = Utils.formatPercentNormalized(_getCalleeT(item));
+    e.children[1].text = M.getFunctionFullName(item.function);
+  }
+
+  List<HtmlElement> _createCalleeHeader() => [
+        new DivElement()
+          ..classes = ['function-item']
+          ..children = <Element>[
+            _createHeaderButton(
+                const ['inclusive'],
+                'Callees(%)',
+                _Table.callee,
+                _SortingField.callee,
+                _SortingDirection.descending),
+            _createHeaderButton(const ['name'], 'Method', _Table.callee,
+                _SortingField.method, _SortingDirection.ascending),
+          ]
+      ];
+
+  HtmlElement _createCaller() {
+    final element = new DivElement()
+      ..classes = ['function-item']
+      ..children = <Element>[
+        new SpanElement()
+          ..classes = ['inclusive']
+          ..text = '0%',
+        new SpanElement()..classes = ['name']
+      ];
+    element.onClick.listen((e) {
+      if (e.target is AnchorElement) {
+        return;
+      }
+      _selected = _callers.getItemFromElement(element);
+      _r.dirty();
+    });
+    return element;
+  }
+
+  void _updateCaller(Element e, item, int index) {
+    e.children[0].text = Utils.formatPercentNormalized(_getCallerT(item));
+    e.children[1].text = M.getFunctionFullName(item.function);
+  }
+
+  List<HtmlElement> _createCallerHeader() => [
+        new DivElement()
+          ..classes = ['function-item']
+          ..children = <Element>[
+            _createHeaderButton(
+                const ['inclusive'],
+                'Callers(%)',
+                _Table.caller,
+                _SortingField.caller,
+                _SortingDirection.descending),
+            _createHeaderButton(const ['name'], 'Method', _Table.caller,
+                _SortingField.method, _SortingDirection.ascending),
+          ]
+      ];
+
+  ButtonElement _createHeaderButton(List<String> classes, String text,
+          _Table table, _SortingField field, _SortingDirection direction) =>
+      new ButtonElement()
+        ..classes = classes
+        ..text = _sortingField[table] != field
+            ? text
+            : _sortingDirection[table] == _SortingDirection.ascending
+                ? '$text▼'
+                : '$text▲'
+        ..onClick.listen((_) => _setSorting(table, field, direction));
+
+  List<Element> _createTree() {
+    CpuProfileVirtualTreeElement tree;
+    return [
+      (new StackTraceTreeConfigElement(
+              showMode: false,
+              showDirection: false,
+              mode: ProfileTreeMode.function,
+              direction: M.ProfileTreeDirection.exclusive,
+              filter: _filter,
+              queue: _r.queue)
+            ..onFilterChange.listen((e) {
+              _filter = e.element.filter.trim();
+              tree.filters = _filter.isNotEmpty
+                  ? [
+                      _filterTree,
+                      (node) {
+                        return node.name.contains(_filter);
+                      }
+                    ]
+                  : [_filterTree];
+            }))
+          .element,
+      new BRElement(),
+      (tree = new CpuProfileVirtualTreeElement(_isolate, _progress.profile,
+              mode: ProfileTreeMode.function,
+              direction: M.ProfileTreeDirection.exclusive,
+              queue: _r.queue)
+            ..filters = _filter.isNotEmpty
+                ? [
+                    _filterTree,
+                    (node) {
+                      return node.name.contains(_filter);
+                    }
+                  ]
+                : [_filterTree])
+          .element
+    ];
+  }
+
+  bool _filterTree(nodeDynamic) {
+    M.FunctionCallTreeNode node = nodeDynamic;
+    return node.profileFunction == _selected;
+  }
+
+  Future _request({bool clear: false, bool forceFetch: false}) async {
+    _progress = null;
+    _progressStream = _profiles.get(isolate, M.SampleProfileTag.vmOnly,
+        clear: clear, forceFetch: forceFetch);
+    _r.dirty();
+    _progress = (await _progressStream.first).progress;
+    _r.dirty();
+    if (M.isSampleProcessRunning(_progress.status)) {
+      _progress = (await _progressStream.last).progress;
+      _r.dirty();
+    }
+  }
+
+  Future _clearCpuSamples(RefreshEvent e) async {
+    e.element.disabled = true;
+    await _request(clear: true);
+    e.element.disabled = false;
+  }
+
+  Future _refresh(e) async {
+    e.element.disabled = true;
+    await _request(forceFetch: true);
+    e.element.disabled = false;
+  }
+
+  _createSorter(_Table table) {
+    var getter;
+    switch (_sortingField[table]) {
+      case _SortingField.exclusive:
+        getter = _getExclusiveT;
+        break;
+      case _SortingField.inclusive:
+        getter = _getInclusiveT;
+        break;
+      case _SortingField.callee:
+        getter = _getCalleeT;
+        break;
+      case _SortingField.caller:
+        getter = _getCallerT;
+        break;
+      case _SortingField.method:
+        getter = (M.ProfileFunction s) => M.getFunctionFullName(s.function);
+        break;
+    }
+    switch (_sortingDirection[table]) {
+      case _SortingDirection.ascending:
+        int sort(a, b) {
+          return getter(a).compareTo(getter(b));
+        }
+        return sort;
+      case _SortingDirection.descending:
+        int sort(a, b) {
+          return getter(b).compareTo(getter(a));
+        }
+        return sort;
+    }
+  }
+
+  static double _getExclusiveT(M.ProfileFunction f) =>
+      f.normalizedExclusiveTicks;
+  static double _getInclusiveT(M.ProfileFunction f) =>
+      f.normalizedInclusiveTicks;
+  double _getCalleeT(M.ProfileFunction f) =>
+      _selected.callees[f] / _selected.callees.values.reduce((a, b) => a + b);
+  double _getCallerT(M.ProfileFunction f) =>
+      _selected.callers[f] / _selected.callers.values.reduce((a, b) => a + b);
+}
diff --git a/runtime/observatory_2/lib/src/elements/css/shared.css b/runtime/observatory_2/lib/src/elements/css/shared.css
new file mode 100644
index 0000000..f8c0756
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/css/shared.css
@@ -0,0 +1,2875 @@
+/* Global styles */
+* {
+  margin: 0;
+  padding: 0;
+  box-sizing: border-box;
+}
+
+body {
+  padding-top: 56px;
+  color: #333;
+  font: 400 14px 'Montserrat', sans-serif;
+}
+
+button {
+  font: 400 14px 'Montserrat', sans-serif;
+}
+
+.content {
+  padding-left: 10%;
+  font: 400 14px 'Montserrat', sans-serif;
+}
+
+.content-centered {
+  padding-left: 10%;
+  padding-right: 10%;
+  font: 400 14px 'Montserrat', sans-serif;
+}
+
+.content-centered-big {
+  padding-left: 5%;
+  padding-right: 5%;
+  font: 400 14px 'Montserrat', sans-serif;
+}
+
+h1 {
+  font: 400 18px 'Montserrat', sans-serif;
+}
+
+h2 {
+  font: 400 16px 'Montserrat', sans-serif;
+}
+
+.memberList {
+  display: table;
+}
+
+.memberItem {
+  display: table-row;
+}
+
+.memberName, .memberValue {
+  display: table-cell;
+  vertical-align: top;
+  padding: 3px 0 3px 1em;
+  font: 400 14px 'Montserrat', sans-serif;
+}
+
+.memberSmall {
+  display: table-cell;
+  vertical-align: top;
+  padding: 3px 0 3px 1em;
+  font: 400 12px 'Montserrat', sans-serif;
+}
+
+.monospace {
+  font-family: consolas, courier, monospace;
+  font-size: 1em;
+  line-height: 1.2em;
+  white-space: nowrap;
+}
+
+a[href] {
+  color: #0489c3;
+  text-decoration: none;
+}
+
+a[href]:hover {
+  text-decoration: underline;
+}
+
+em {
+  color: inherit;
+  font-style: italic;
+}
+
+b {
+  color: inherit;
+  font-weight: bold;
+}
+
+hr {
+  margin-top: 20px;
+  margin-bottom: 20px;
+  border: 0;
+  border-top: 1px solid #eee;
+  height: 0;
+  box-sizing: content-box;
+}
+
+.list-group {
+  padding-left: 0;
+  margin-bottom: 20px;
+}
+
+.list-group-item {
+  position: relative;
+  display: block;
+  padding: 10px 15px;
+  margin-bottom: -1px;
+  background-color: #fff;
+}
+
+.list-group-item:first-child {
+  /* rounded top corners */
+  border-top-right-radius:4px;
+  border-top-left-radius:4px;
+}
+
+.list-group-item:last-child {
+  margin-bottom: 0;
+  /* rounded bottom corners */
+  border-bottom-right-radius: 4px;
+  border-bottom-left-radius:4px;
+}
+
+.full {
+  height: 100%;
+  width: 100;
+}
+
+.flex-row {
+  display: flex;
+  flex-direction: row;
+}
+
+/* Flex row container */
+.flex-row {
+  display: flex;
+  flex-direction: row;
+}
+
+.flex-row-wrap {
+  display: flex;
+  flex-direction: row;
+  flex-wrap: wrap;
+}
+
+.flex-row-wrap-right {
+  display: flex;
+  flex-direction: row;
+  flex-wrap: wrap;
+  justify-content: flex-end;
+}
+
+.flex-row-spaced {
+  display: flex;
+  flex-direction: row;
+  justify-content: space-between;
+  flex-wrap: wrap;
+}
+
+.inline-flex-row {
+  display: inline-flex;
+  flex-direction: row;
+}
+
+/* Flex column container */
+.flex-column {
+  display: flex;
+  flex-direction: column;
+}
+
+.flex-item-fit {
+  flex-grow: 1;
+  flex-shrink: 1;
+  flex-basis: auto;
+}
+
+.flex-item-no-shrink {
+  flex-grow: 0;
+  flex-shrink: 0;
+  flex-basis: auto;
+}
+
+.flex-item-fill {
+  flex-grow: 0;
+  flex-shrink: 1;  /* shrink when pressured */
+  flex-basis: 100%;  /* try and take 100% */
+}
+
+.flex-item-fixed-1-12 {
+  flex-grow: 0;
+  flex-shrink: 0;
+  flex-basis: 8.3%;
+}
+
+.flex-item-fixed-2-12 {
+  flex-grow: 0;
+  flex-shrink: 0;
+  flex-basis: 16.6%;
+}
+
+.flex-item-fixed-4-12 {
+  flex-grow: 0;
+  flex-shrink: 0;
+  flex-basis: 33.3333%;
+}
+
+.flex-item-fixed-6-12, .flex-item-50-percent {
+  flex-grow: 0;
+  flex-shrink: 0;
+  flex-basis: 50%;
+}
+
+.flex-item-fixed-8-12 {
+  flex-grow: 0;
+  flex-shrink: 0;
+  flex-basis: 66.6666%;
+}
+
+.flex-item-fixed-9-12 {
+  flex-grow: 0;
+  flex-shrink: 0;
+  flex-basis: 75%;
+}
+
+
+.flex-item-fixed-12-12 {
+  flex-grow: 0;
+  flex-shrink: 0;
+  flex-basis: 100%;
+}
+
+.flex-item-10-percent {
+  flex-grow: 0;
+  flex-shrink: 0;
+  flex-basis: 10%;
+}
+
+.flex-item-15-percent {
+  flex-grow: 0;
+  flex-shrink: 0;
+  flex-basis: 15%;
+}
+
+.flex-item-20-percent {
+  flex-grow: 0;
+  flex-shrink: 0;
+  flex-basis: 20%;
+}
+
+.flex-item-30-percent {
+  flex-grow: 0;
+  flex-shrink: 0;
+  flex-basis: 30%;
+}
+
+.flex-item-40-percent {
+  flex-grow: 0;
+  flex-shrink: 0;
+  flex-basis: 40%;
+}
+
+.flex-item-45-percent {
+  flex-grow: 0;
+  flex-shrink: 0;
+  flex-basis: 45%;
+}
+
+.flex-item-50-percent {
+  flex-grow: 0;
+  flex-shrink: 0;
+  flex-basis: 50%;
+}
+
+.flex-item-60-percent {
+  flex-grow: 0;
+  flex-shrink: 0;
+  flex-basis: 60%;
+}
+
+.flex-item-70-percent {
+  flex-grow: 0;
+  flex-shrink: 0;
+  flex-basis: 70%;
+}
+
+.flex-item-80-percent {
+  flex-grow: 0;
+  flex-shrink: 0;
+  flex-basis: 80%;
+}
+
+.flex-item-90-percent {
+  flex-grow: 0;
+  flex-shrink: 0;
+  flex-basis: 90%;
+}
+
+.flex-item-even {
+  flex-grow: 1;
+  flex-shrink: 0;
+  padding: 5px;
+}
+
+.indent {
+  margin-left: 1.5em;
+  font: 400 14px 'Montserrat', sans-serif;
+  line-height: 150%;
+}
+
+.well {
+  min-height: 20px;
+  padding: 19px;
+  margin-bottom: 20px;
+  background-color: #f5f5f5;
+  border: 1px solid #e3e3e3;
+  border-radius: 4px;
+  box-shadow: inset 0 1px 1px rgba(0,0,0,0.05);
+}
+
+.break-wrap {
+  word-wrap: break-word;
+}
+
+body.busy, body.busy * {
+  cursor: progress !important;
+}
+
+.pointer {
+  cursor: pointer;
+}
+
+.shadow {
+  box-shadow: 0 2px 10px 0 rgba(0, 0, 0, 0.16),
+              0 2px 5px 0 rgba(0, 0, 0, 0.26);
+}
+
+input.textbox {
+  width: 20em;
+  font: 400 16px 'Montserrat', sans-serif;
+}
+
+select, button, input {
+  border-radius: 0px;
+  border-style: solid;
+  border-width: 1px;
+}
+
+button.link {
+  background-color: transparent;
+  color: #0489c3;
+  border-style: none;
+  border-width: 0;
+}
+
+button.big {
+  font-size: 20px;
+}
+
+@-webkit-keyframes fadeIn {
+  0%   { opacity: 0; }
+  100% { opacity: 1; }
+}
+
+@-moz-keyframes fadeIn {
+  0%   { opacity: 0; }
+  100% { opacity: 1; }
+}
+
+@keyframes fadeIn {
+  0%   { opacity: 0; }
+  100% { opacity: 1; }
+}
+
+@-webkit-keyframes shake {
+  0%, 100% {
+    -webkit-transform: translate3d(0, 0, 0);
+    transform: translate3d(0, 0, 0);
+  }
+
+  10%, 30%, 50%, 70%, 90% {
+    -webkit-transform: translate3d(-10px, 0, 0);
+    transform: translate3d(-10px, 0, 0);
+  }
+
+  20%, 40%, 60%, 80% {
+    -webkit-transform: translate3d(10px, 0, 0);
+    transform: translate3d(10px, 0, 0);
+  }
+}
+
+@keyframes shake {
+  0%, 100% {
+    -webkit-transform: translate3d(0, 0, 0);
+    -ms-transform: translate3d(0, 0, 0);
+    transform: translate3d(0, 0, 0);
+  }
+
+  10%, 30%, 50%, 70%, 90% {
+    -webkit-transform: translate3d(-10px, 0, 0);
+    -ms-transform: translate3d(-10px, 0, 0);
+    transform: translate3d(-10px, 0, 0);
+  }
+
+  20%, 40%, 60%, 80% {
+    -webkit-transform: translate3d(10px, 0, 0);
+    -ms-transform: translate3d(10px, 0, 0);
+    transform: translate3d(10px, 0, 0);
+  }
+}
+
+.shake {
+  animation: shake 0.5s;
+  -webkit-animation: shake 0.5s;
+}
+
+/* allocation-profile */
+
+.allocation-profile .heap-space {
+  display: inline-block;
+  width: 33%;
+}
+
+.allocation-profile .heap-space.right,
+.allocation-profile .heap-space.right .memberList,
+.allocation-profile .heap-space.right .legend * {
+  direction: rtl;
+}
+
+.allocation-profile .compactable {
+  position: relative;
+}
+
+.allocation-profile .compact {
+  position: absolute;
+  bottom: 20px;
+  left: 50%;
+  width: 8em;
+  margin-left: -4em;
+}
+
+.allocation-profile .heap-space.right * {
+  direction: ltr;
+  text-align: right;
+}
+
+.allocation-profile div.chart {
+  display: block;
+  position: relative;
+  height: 150px;
+}
+.allocation-profile div.chart > div.host {
+  display: inline-block;
+  position: absolute;
+  bottom: 0px;
+  top: 0;
+}
+.allocation-profile div.chart > div.legend {
+  position: absolute;
+  width: 150px;
+  top: 25px;
+  bottom: 0;
+  overflow-y: auto;
+}
+.allocation-profile .heap-space.left div.host {
+  left: 200px;
+  width: 180px;
+}
+.allocation-profile .heap-space.right div.host {
+  right: 150px;
+  width: 180px;
+}
+.allocation-profile .heap-space.left div.legend {
+  left: 0;
+}
+.allocation-profile .heap-space.right div.legend {
+  right: 0;
+}
+
+.allocation-profile .collection {
+  height: 100%;
+}
+
+.allocation-profile .collection.expanded {
+  top: 160px;
+}
+
+.allocation-profile .container {
+  padding-left: 5%;
+  padding-right: 5%;
+}
+
+.allocation-profile .collection-item {
+  background-color: #FFFFFF;
+  box-sizing: border-box;
+  line-height: 20px;
+}
+
+.allocation-profile .collection-item:hover {
+  background-color: #d2e7fe;
+}
+
+.allocation-profile .header .collection-item:hover {
+  background-color: #FFFFFF;
+}
+
+.allocation-profile .header .collection-item:last-child {
+  margin-bottom: -3px;
+  border-bottom: solid 1px #AAAAAA;
+}
+
+.allocation-profile .header .collection-item span {
+  font-weight: bolder;
+}
+
+.allocation-profile .collection-item :nth-child(2n+2).group,
+.allocation-profile .collection-item :nth-child(8n+1),
+.allocation-profile .collection-item :nth-child(8n+2),
+.allocation-profile .collection-item :nth-child(8n+3),
+.allocation-profile .collection-item :nth-child(8n+4) {
+  background-color: #EEEEEE;
+}
+
+.allocation-profile .collection-item:hover :nth-child(2n+1).group,
+.allocation-profile .collection-item:hover :nth-child(8n+1),
+.allocation-profile .collection-item:hover :nth-child(8n+2),
+.allocation-profile .collection-item:hover :nth-child(8n+3),
+.allocation-profile .collection-item:hover :nth-child(8n+4) {
+  background-color: #afd5fd;
+}
+
+.allocation-profile .header .collection-item :nth-child(2n+2).group,
+.allocation-profile .header .collection-item :nth-child(8n+5),
+.allocation-profile .header .collection-item :nth-child(8n+6),
+.allocation-profile .header .collection-item :nth-child(8n+7),
+.allocation-profile .header .collection-item :nth-child(8n+8) {
+  background-color: #FFFFFF;
+}
+
+.allocation-profile .header .collection-item :nth-child(2n+1).group,
+.allocation-profile .header .collection-item :nth-child(8n+1),
+.allocation-profile .header .collection-item :nth-child(8n+2),
+.allocation-profile .header .collection-item :nth-child(8n+3),
+.allocation-profile .header .collection-item :nth-child(8n+4) {
+  background-color: #DDDDDD;
+}
+
+.allocation-profile .collection-item .group {
+  display: inline-block;
+  width: 24em;
+  text-align: center;
+  padding-right: 0.5em;
+  line-height: 20px;
+  border-right: solid 1px #AAAAAA;
+}
+
+.allocation-profile .collection-item .bytes {
+  display: inline-block;
+  width: 6em;
+  text-align: right;
+  line-height: 20px;
+  padding-right: 0.5em;
+}
+
+.allocation-profile .collection-item .instances {
+  display: inline-block;
+  width: 6em;
+  text-align: right;
+  padding-right: 0.5em;
+  line-height: 20px;
+  border-right: solid 1px #AAAAAA;
+}
+
+.allocation-profile .collection-item .name {
+  padding-left: 0.5em;
+  padding-right: 0.5em;
+  display: inline-block;
+}
+
+.allocation-profile .collection-item > button,
+.allocation-profile .collection-item > button:active {
+  background-color: transparent;
+  color: #0489c3;
+  border-style: none;
+}
+
+.allocation-profile .collection-item > button:hover {
+  text-decoration: underline;
+}
+
+/* class-ref */
+
+.class-ref > a[href]:hover {
+    text-decoration: underline;
+}
+.class-ref > a[href] {
+    color: #0489c3;
+    text-decoration: none;
+}
+
+/* code-ref */
+
+.code-ref > a[href]:hover {
+  text-decoration: underline;
+}
+.code-ref > a[href] {
+  color: #0489c3;
+  text-decoration: none;
+}
+.code-ref .emphasize {
+  font-style: italic;
+}
+
+
+/* class-tree */
+
+.class-tree {
+  position: relative;
+  display: block;
+  height: 100%;
+}
+
+.class-tree .virtual-tree {
+  position: absolute;
+  height: auto;
+  top: 60px;
+  bottom: 0;
+  left: 0;
+  right: 0;
+}
+
+.class-tree .virtual-tree .container {
+  padding-left: 10%;
+  padding-right: 10%;
+}
+
+.class-tree .virtual-tree .class-tree-item {
+  line-height: 25px;
+  height: 25px;
+}
+
+.class-tree .virtual-tree .class-tree-item .name {
+  margin-left: 0.5em;
+  margin-right: 0.5em;
+}
+
+/* code-view */
+
+.code-view .table {
+  table-layout: fixed;
+}
+
+.code-view th:nth-of-type(1),
+.code-view td:nth-of-type(1) {
+  min-width: 10em;
+  text-align: left;
+}
+
+.code-view th:nth-of-type(2),
+.code-view td:nth-of-type(2) {
+  min-width: 8em;
+  text-align: left;
+}
+
+.code-view th:nth-of-type(3),
+.code-view td:nth-of-type(3) {
+  min-width: 8em;
+  text-align: left;
+}
+
+.code-view th:nth-of-type(4),
+.code-view td:nth-of-type(4) {
+  text-align: left;
+  overflow: visible;
+  white-space: pre;
+  padding-right: 1em;
+  width: 1px;
+}
+
+.code-view th:nth-of-type(5),
+.code-view td:nth-of-type(5) {
+  text-align: left;
+  overflow: visible;
+}
+
+.code-view tr:hover > td {
+  background-color: #F4C7C3;
+}
+
+.code-view .code-comment {
+  color: grey;
+  font-style: italic;
+}
+
+/* context-ref */
+
+.context-ref > a[href]:hover {
+  text-decoration: underline;
+}
+.context-ref > a[href] {
+  color: #0489c3;
+  text-decoration: none;
+}
+.context-ref > a[href] * {
+  color: inherit;
+}
+.context-ref .emphasize {
+  font-style: italic;
+}
+
+
+/* cpu-profile */
+
+.cpu-profile {
+  position: relative;
+  display: block;
+  height: 100%;
+}
+
+.cpu-profile > .cpu-profile-virtual-tree {
+  position: absolute;
+  height: auto;
+  top: 320px;
+  bottom: 0;
+  left: 0;
+  right: 0;
+}
+
+/* cpu-profile-table */
+
+.cpu-profile-table {
+  position: relative;
+  display: block;
+  height: 100%;
+}
+.cpu-profile-table .cpu-profile-virtual-tree {
+  height: 100%;
+  min-height: 600px;
+  padding-top: 250px;
+  margin-top: -250px;
+}
+.cpu-profile-table .profile-trees {
+  vertical-align: text-top;
+  min-width: 100%;
+  height: 100%;
+  padding-top: 160px;
+  margin-top: -160px;
+  padding-bottom: 100px;
+  margin-bottom: -100px;
+  padding-left: 5%;
+  padding-right: 5%;
+  min-height: 600px;
+}
+.cpu-profile-table .profile-trees .virtual-collection {
+  height: 100%;
+  width: 100%;
+  border: solid 1px #888888;
+  box-shadow: 2px 2px 5px #888888;
+}
+.cpu-profile-table .profile-trees > .profile-trees-all {
+  vertical-align: text-top;
+  display: inline-block;
+  width: 50%;
+  height: 100%;
+  padding: 5px;
+}
+.cpu-profile-table .profile-trees > .profile-trees-current {
+  vertical-align: text-top;
+  display: inline-block;
+  width: 50%;
+  height: 100%;
+}
+.cpu-profile-table .profile-trees .profile-trees-caller {
+  vertical-align: text-top;
+  display: inline-block;
+  width: 100%;
+  height: 50%;
+  padding: 5px;
+  margin-top: -17px;
+  padding-top: 22px;
+}
+.cpu-profile-table .profile-trees .profile-trees-selected {
+  vertical-align: text-top;
+  display: block;
+  height: 24px;
+  line-height: 24px;
+  margin: 5px;
+  border: solid 1px #888888;
+  box-shadow: 2px 2px 5px #888888;
+  padding-left: 5px;
+  padding-right: 5px;
+}
+.cpu-profile-table .profile-trees .profile-trees-selected > * {
+  white-space: nowrap;
+  text-overflow: ellipsis;
+  overflow: hidden;
+  display: inline-block;
+  width: 100%;
+}
+.cpu-profile-table .profile-trees .profile-trees-callee {
+  vertical-align: text-top;
+  display: inline-block;
+  width: 100%;
+  height: 50%;
+  padding: 5px;
+  margin-bottom: -17px;
+  padding-bottom: 22px;
+}
+.cpu-profile-table .function-item {
+  box-sizing: border-box;
+  line-height: 20px;
+}
+.cpu-profile-table .header {
+  box-sizing: border-box;
+  line-height: 20px;
+}
+.cpu-profile-table .header .function-item:last-child {
+  margin-bottom: -3px;
+  border-bottom: solid 1px #AAAAAA;
+}
+.cpu-profile-table .function-item .inclusive,
+.cpu-profile-table .function-item .exclusive {
+  display: inline-block;
+  width: 7em;
+  text-align: right;
+  padding-right: 0.5em;
+  line-height: 20px;
+}
+.cpu-profile-table .buffer .function-item .inclusive {
+  background-color: #EEEEEE;
+}
+.cpu-profile-table .buffer .function-item.selected .inclusive {
+  background-color: #51a3fb;
+}
+.cpu-profile-table .buffer .function-item:hover .inclusive {
+  background-color: #afd5fd;
+}
+.cpu-profile-table .header .function-item .inclusive {
+  background-color: #DDDDDD;
+}
+.cpu-profile-table .buffer .function-item.selected {
+  background-color: #60abfb;
+}
+.cpu-profile-table .buffer .function-item:hover {
+  background-color: #d2e7fe;
+}
+.cpu-profile-table .function-item .exclusive {
+}
+.cpu-profile-table .function-item .name {
+  padding-left: 0.5em;
+  padding-left: 0.5em;
+}
+.cpu-profile-table .function-item > button,
+.cpu-profile-table .function-item > button:active {
+  background-color: transparent;
+  color: #0489c3;
+  border-style: none;
+}
+.cpu-profile-table .function-item > button:hover {
+  text-decoration: underline;
+}
+
+/* cpu-profile-virtual-tree */
+
+.cpu-profile-virtual-tree {
+  display: block;
+  height: 600px;
+}
+
+.cpu-profile-virtual-tree .tree-item {
+  box-sizing: border-box;
+  line-height: 30px;
+  height: 30px;
+  padding-left: 5%;
+  padding-right: 5%;
+}
+
+.cpu-profile-virtual-tree .tree-item > .inclusive,
+.cpu-profile-virtual-tree .tree-item > .exclusive,
+.cpu-profile-virtual-tree .tree-item > .percentage {
+  display: inline-block;
+  text-align: right;
+  width: 4em;
+  margin-left: 0.25em;
+  margin-right: 0.25em;
+}
+
+.cpu-profile-virtual-tree .tree-item > .exclusive {
+  margin-right: 1.5em;
+}
+
+.cpu-profile-virtual-tree .tree-item > .name {
+  display: inline;
+  margin-left: 0.5em;
+  margin-right: 0.5em;
+}
+
+/* curly-block */
+
+.curly-block span.curly-block {
+  color: #0489c3;
+  cursor: pointer;
+}
+.curly-block span.curly-block.disabled {
+  color: white;
+  cursor: wait;
+}
+
+/* debugger-console */
+
+.debugger-console {
+  display: block;
+  margin: 0px 20px 10px 20px;
+}
+.debugger-console .normal {
+  font: normal 14px consolas, courier, monospace;
+  white-space: pre;
+  line-height: 125%;
+}
+.debugger-console .bold {
+  font: bold 14px consolas, courier, monospace;
+  white-space: pre;
+  line-height: 125%;
+}
+.debugger-console .red {
+  font: normal 14px consolas, courier, monospace;
+  white-space: pre;
+  line-height: 125%;
+  color: red;
+}
+.debugger-console .green {
+  font: normal 14px consolas, courier, monospace;
+  white-space: pre;
+  line-height: 125%;
+  color: green;
+}
+.debugger-console .spacer {
+  height: 20px;
+}
+
+/* debugger-frame */
+
+.debugger-frame {
+  display: block;
+  position: relative;
+  padding: 5px;
+  border: 1px solid white;
+}
+.debugger-frame:hover {
+  border: 1px solid #e0e0e0;
+}
+.debugger-frame.shadow {
+  box-shadow:  0 2px 10px 0 rgba(0, 0, 0, 0.16),
+               0 2px 5px 0 rgba(0, 0, 0, 0.26);
+}
+.debugger-frame.causalFrame {
+  background-color: #D7CCC8;
+}
+.debugger-frame.current {
+  box-shadow:  0 2px 10px 0 rgba(0, 0, 0, 0.26),
+               0 2px 5px 0 rgba(0, 0, 0, 0.46);
+  border: 1px solid #444;
+}
+.debugger-frame > button {
+  display: block;
+  width: 100%;
+  text-align: left;
+  background-color: transparent;
+  border: none;
+}
+.debugger-frame .frameSummaryText {
+  display: inline-block;
+  padding: 5px;
+}
+.debugger-frame .frameId {
+  display: inline-block;
+  width: 60px;
+}
+.debugger-frame .frameExpander {
+  position: absolute;
+  right: 5px;
+  top: 5px;
+  display: none;
+}
+.debugger-frame:hover .frameExpander{
+  display: inline-block;
+}
+.debugger-frame .frameContractor {
+  position: absolute;
+  right: 5px;
+  bottom: 5px;
+  display: inline-block;
+}
+.debugger-frame .frameContractor > button {
+  background-color: transparent;
+  border: none;
+}
+.debugger-frame .flex-item-script {
+  flex-grow: 1;
+  flex-shrink: 1;
+  flex-basis: 765px;
+}
+.debugger-frame .flex-item-vars {
+  flex-grow: 5;
+  flex-shrink: 0;
+  flex-basis: 250px;
+  overflow-x: hidden;
+}
+.debugger-frame .frameVars {
+  position: relative;
+  top: 5px;
+  padding-left:2em;
+  padding-bottom: 5px;
+}
+
+/* debugger-input */
+
+.debugger-input .container {
+  height: 100%;
+  display: flex;
+  flex-direction: row;
+  justify-content: space-between;
+}
+.debugger-input .textBox {
+  flex: 1 1 auto;
+  margin: 20px;
+  padding: 5px;
+  font: 400 16px consolas, courier, monospace;
+  width: 95%;
+}
+.debugger-input .modalPrompt {
+  flex: 0 0 auto;
+  margin-top: 20px;
+  margin-left: 20px;
+  padding: 5px;
+  font: 400 16px consolas, courier, monospace;
+  color: red;
+}
+.debugger-input .modalPrompt.hidden {
+  display: none;
+}
+
+/* debugger-message */
+
+.debugger-message {
+  display: block;
+  position: relative;
+  padding: 5px;
+  border: 1px solid white;
+}
+.debugger-message:hover {
+  border: 1px solid #e0e0e0;
+}
+.debugger-message.shadow {
+  box-shadow:  0 2px 10px 0 rgba(0, 0, 0, 0.16),
+  0 2px 5px 0 rgba(0, 0, 0, 0.26);
+}
+.debugger-message.current {
+  box-shadow:  0 2px 10px 0 rgba(0, 0, 0, 0.26),
+  0 2px 5px 0 rgba(0, 0, 0, 0.46);
+  border: 1px solid #444;
+}
+.debugger-message > button {
+  display: block;
+  width: 100%;
+  text-align: left;
+  background-color: transparent;
+  border: none;
+}
+.debugger-message .messageSummaryText {
+  display: inline-block;
+  padding: 5px;
+}
+.debugger-message .messageId {
+  display: inline-block;
+  font-weight: bold;
+  width: 100px;
+}
+.debugger-message .messageExpander {
+  position: absolute;
+  right: 5px;
+  top: 5px;
+  display: none;
+}
+.debugger-message:hover .messageExpander {
+  display: inline-block;
+}
+.debugger-message.shadow:hover .messageExpander {
+  display: none;
+}
+.debugger-message .messageContractor {
+  position: absolute;
+  right: 5px;
+  bottom: 5px;
+  display: inline-block;
+}
+.debugger-message .flex-item-script {
+  flex-grow: 1;
+  flex-shrink: 1;
+  flex-basis: 765px;
+}
+.debugger-message .flex-item-vars {
+  flex-grow: 5;
+  flex-shrink: 0;
+  flex-basis: 225px;
+}
+
+/* debugger-page */
+
+.debugger-page {
+  height: 100%;
+}
+.debugger-page .variable {
+  height: 100%;
+  margin-bottom: -75px;
+  padding-bottom: 75px;
+}
+.debugger-page .stack {
+  flex: 0 0 auto;
+  overflow-y: auto;
+  height: 62%;
+}
+.debugger-page .splitter {
+  height: 0px;
+  margin: 0px;
+  font-size: 1px;
+  border-bottom: 1px solid #888;
+}
+.debugger-page .console {
+  flex: 1 1 auto;
+  overflow-x: auto;
+  overflow-y: auto;
+  height: 38%;
+}
+.debugger-page .commandline {
+  flex: 0 0 auto;
+}
+
+/* debugger-stack */
+
+.debugger-stack {
+  position: relative;
+}
+.debugger-stack .sampledMessage {
+  margin: 0px 20px 10px 20px;
+  font: 400 14px 'Montserrat', sans-serif;
+  line-height: 125%;
+}
+.debugger-stack .sampledMessage > button {
+  background-color: transparent;
+  border: none;
+  color: #0489c3;
+  text-decoration: none;
+  margin-right: 1em;
+}
+.debugger-stack .sampledMessage > button:hover {
+    text-decoration: underline;
+}
+.debugger-stack .splitter {
+  height: 0px;
+  margin: 0px;
+  font-size: 1px;
+  border-bottom: 1px dashed #888;
+}
+.debugger-stack .noMessages,
+.debugger-stack .noStack {
+  margin: 10px 0px 10px 25px;
+  font: bold 14px 'Montserrat', sans-serif;
+  line-height: 125%;
+}
+
+.debugger-stack .sampledMessage.hidden,
+.debugger-stack .noMessages.hidden,
+.debugger-stack .noStack.hidden {
+  display: none;
+}
+
+/* error-ref */
+
+.error-ref > pre {
+  background-color: #f5f5f5;
+  border: 1px solid #ccc;
+  padding-left: 10px;
+  padding-right: 10px;
+  font-family: consolas, courier, monospace;
+  font-size: 1em;
+  line-height: 1.2em;
+  white-space: pre;
+}
+
+/* eval-box */
+
+.eval-box a[href]:hover {
+    text-decoration: underline;
+}
+.eval-box a[href] {
+    color: #0489c3;
+    text-decoration: none;
+}
+.eval-box .quicks > button:hover {
+  background-color: transparent;
+  border: none;
+  text-decoration: underline;
+}
+.eval-box .quicks > button {
+  background-color: transparent;
+  border: none;
+  color: #0489c3;
+  padding: 0;
+  margin-right: 1em;
+  text-decoration: none;
+}
+.eval-box .emphasize {
+  font-style: italic;
+}
+.eval-box .indent {
+  margin-left: 1.5em;
+  font: 400 14px 'Montserrat', sans-serif;
+  line-height: 150%;
+}
+.eval-box .stackTraceBox {
+  margin-left: 1.5em;
+  background-color: #f5f5f5;
+  border: 1px solid #ccc;
+  padding: 10px;
+  font-family: consolas, courier, monospace;
+  font-size: 12px;
+  white-space: pre;
+  overflow-x: auto;
+}
+.eval-box .heading {
+  line-height: 30px;
+  position: relative;
+  box-sizing: border-box;
+  width: 100%;
+  min-width: 450px;
+  padding-right: 150px;
+}
+.eval-box .heading .textbox {
+  width: 100%;
+  min-width: 300px;
+}
+.eval-box .heading .buttons {
+  position: absolute;
+  top: 0;
+  right: 0px;
+}
+.eval-box .heading .buttons button{
+  margin-right: 1em;
+}
+.eval-box.historyExpr,
+.eval-box .historyValue {
+  vertical-align: text-top;
+  font: 400 14px 'Montserrat', sans-serif;
+}
+.eval-box .historyExpr button {
+  display: block;
+  color: black;
+  border: none;
+  background: none;
+  text-decoration: none;
+  padding: 6px 6px;
+  cursor: pointer;
+  white-space: pre-line;
+}
+.eval-box .historyExpr button:hover {
+  background-color: #fff3e3;
+}
+.eval-box .historyValue {
+  display: block;
+  padding: 6px 6px;
+}
+.eval-box .historyDelete button {
+  border: none;
+  background: none;
+}
+eval-box .historyDelete button:hover {
+  color: #BB3311;
+}
+
+/* flag-list */
+
+.flag-list .comment {
+  color: #aaa;
+}
+
+.flag-list .flag {
+  padding: 3px 0;
+}
+
+.flag-list .name {
+  font-weight: bold;
+  margin-right: 0.7em;
+}
+
+.flag-list .value {
+  margin-left: 0.7em;
+}
+
+/* function-ref */
+
+.function-ref > a[href]:hover {
+  text-decoration: underline;
+}
+.function-ref > a[href] {
+  color: #0489c3;
+  text-decoration: none;
+}
+.function-ref .emphasize {
+  font-style: italic;
+}
+
+
+/* heap-snapshot */
+
+.heap-snapshot .statusMessage {
+  font-size: 150%;
+  font-weight: bold;
+}
+.heap-snapshot .statusBox {
+  height: 100%;
+  padding: 1em;
+}
+.heap-snapshot .explanation {
+  display: block;
+  display: -webkit-box;
+  -webkit-line-clamp: 4;
+  -webkit-box-orient: vertical;
+  max-height: 80px;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+.heap-snapshot .virtual-tree {
+  position: absolute;
+  height: auto;
+  top: 250px;
+  bottom: 0;
+  left: 0;
+  right: 0;
+}
+.heap-snapshot .tree-item {
+  box-sizing: border-box;
+  line-height: 30px;
+  height: 30px;
+  padding-left: 5%;
+  padding-right: 5%;
+
+  display: flex;
+  flex-direction: row;
+}
+.heap-snapshot .tree-item > .size,
+.heap-snapshot .tree-item > .percentage {
+  text-align: right;
+  margin-left: 0.25em;
+  margin-right: 0.25em;
+  flex-basis: 5em;
+  flex-grow: 0;
+  flex-shrink: 0;
+}
+.heap-snapshot .tree-item > .edge {
+  margin-left: 0.25em;
+  margin-right: 0.25em;
+  flex-basis: 0;
+  flex-grow: 1;
+  flex-shrink: 1;
+}
+.heap-snapshot .tree-item > .name {
+  margin-left: 0.5em;
+  margin-right: 0.5em;
+  flex-basis: 0;
+  flex-grow: 4;
+  flex-shrink: 4;
+}
+.heap-snapshot .tree-item > .link {
+  margin-left: 0.25em;
+  margin-right: 0.25em;
+  flex-grow: 0;
+  flex-shrink: 0;
+}
+
+
+/* icdata-ref */
+
+.icdata-ref > a[href]:hover {
+  text-decoration: underline;
+}
+.icdata-ref > a[href] {
+  color: #0489c3;
+  text-decoration: none;
+}
+.icdata-ref > a[href] * {
+  color: inherit;
+}
+.icdata-ref .emphasize {
+  font-style: italic;
+}
+
+
+/* inbound-reference */
+
+.inbound-reference > a[href]:hover {
+  text-decoration: underline;
+}
+.inbound-reference > a[href] {
+  color: #0489c3;
+  text-decoration: none;
+}
+
+.inbound-reference .indent {
+  margin-left: 1.5em;
+  font: 400 14px 'Montserrat', sans-serif;
+  line-height: 150%;
+}
+.inbound-reference .stackTraceBox {
+  margin-left: 1.5em;
+  background-color: #f5f5f5;
+  border: 1px solid #ccc;
+  padding: 10px;
+  font-family: consolas, courier, monospace;
+  font-size: 12px;
+  white-space: pre;
+  overflow-x: auto;
+}
+
+/* instance-ref */
+
+.instance-ref > a[href]:hover {
+  text-decoration: underline;
+}
+.instance-ref > a[href] {
+  color: #0489c3;
+  text-decoration: none;
+}
+.instance-ref > a[href] * {
+  color: inherit;
+}
+.instance-ref .emphasize {
+  font-style: italic;
+}
+.instance-ref .indent {
+  margin-left: 1.5em;
+  font: 400 14px 'Montserrat', sans-serif;
+  line-height: 150%;
+}
+.instance-ref .stackTraceBox {
+  margin-left: 1.5em;
+  background-color: #f5f5f5;
+  border: 1px solid #ccc;
+  padding: 10px;
+  font-family: consolas, courier, monospace;
+  font-size: 12px;
+  white-space: pre;
+  overflow-x: auto;
+}
+
+/* isolate-counter-chart */
+
+.isolate-counter-chart {
+  display: block;
+  position: relative;
+  height: 300px;
+  min-width: 350px;
+}
+.isolate-counter-chart > div.host {
+  position: absolute;
+  left: 0;
+  bottom: 20px;
+  top: 5px;
+  right: 250px;
+}
+.isolate-counter-chart > div.legend {
+  position: absolute;
+  width: 250px;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  overflow-y: auto;
+}
+
+/* isolate-location */
+
+.isolate-location > span {
+  font-weight: bold;
+}
+.isolate-location > a[href] {
+  color: #0489c3;
+  text-decoration: none;
+}
+.isolate-location > a[href]:hover {
+  text-decoration: underline;
+}
+
+/* isolate-reconnect */
+
+.isolate-reconnect div.doubleSpaced {
+  line-height: 2em;
+}
+
+/* isolate-ref */
+
+.isolate-ref > a[href]:hover {
+  text-decoration: underline;
+}
+.isolate-ref > a[href] {
+  color: #0489c3;
+  text-decoration: none;
+}
+
+/* isolate-run-state */
+
+.isolate-run-state > span {
+  font-weight: bold;
+}
+
+/* isolate-shared-summary */
+
+.isolate-shared-summary {
+  display: block;
+}
+
+.isolate-ref-container {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  padding: 5px;
+  font-weight: bolder;
+}
+
+.isolate-state-container {
+  display: inline-block;
+}
+
+.isolate-shared-summary > .summary {
+  height: 350px;
+  position: relative;
+}
+.isolate-shared-summary .menu {
+  float: right;
+  top: 0;
+  right: 0;
+}
+.isolate-shared-summary .isolate-counter-chart {
+  position: absolute;
+  left: 0;
+  top: 0;
+  right: 230px;
+  clear: both;
+}
+.isolate-shared-summary .errorBox {
+  background-color: #f5f5f5;
+  border: 1px solid #ccc;
+  padding: 2em;
+  font-family: consolas, courier, monospace;
+  font-size: 1em;
+  line-height: 1.2em;
+  white-space: pre;
+}
+
+/* library-ref */
+
+.library-ref > a[href]:hover {
+  text-decoration: underline;
+}
+.library-ref > a[href] {
+  color: #0489c3;
+  text-decoration: none;
+}
+.library-ref .emphasize {
+  font-style: italic;
+}
+
+
+.nav-option {
+  float: right;
+  margin: 3px;
+  padding: 8px;
+}
+
+.nav-option label {
+  color: white;
+}
+
+/* logging-list */
+
+.logging-list .outlined {
+  -webkit-box-shadow: 0px 0px 2px 1px rgba(0,0,0,0.75);
+  -moz-box-shadow: 0px 0px 2px 1px rgba(0,0,0,0.75);
+  box-shadow: 0px 0px 2px 1px rgba(0,0,0,0.75);
+  margin: 4px;
+}
+.logging-list .logItem {
+  display: inline-block;
+  font: normal 14px consolas, courier, monospace;
+  white-space: pre;
+  line-height: 125%;
+  width: 100%;
+}
+.logging-list .level {
+  display: inline-block;
+  width: 5em;
+}
+.logging-list .time {
+  display: inline-block;
+  width: 12em;
+}
+.logging-list .FINEST {
+  background-color: #FAFAFA;
+}
+.logging-list .FINER {
+  background-color: #ECEFF1;
+}
+.logging-list .FINE {
+  background-color: #EFEBE9;
+}
+.logging-list .CONFIG {
+  background-color: #FFF3E0;
+}
+.logging-list .INFO {
+  background-color: #F1F8E9;
+}
+.logging-list .WARNING {
+  background-color: #FFE0B2;
+}
+.logging-list .SEVERE {
+  background-color: #FFCCBC;
+}
+.logging-list .SHOUT {
+  background-color: #FFCDD2;
+}
+
+/* memory-graph */
+
+.memory-graph .chart-legend-row,
+.memory-graph .chart-legend-row div {
+  display: inline;
+}
+
+.memory-graph .chart-legend-row div.chart-legend-color {
+  display: inline-block;
+  margin: auto 8px;
+}
+
+.memory-graph .chart-legend-row:nth-child(2n) div.chart-legend-color {
+  display: none;
+}
+
+/* megamorphic-cache-ref */
+
+.megamorphic-cache-ref > a[href]:hover {
+    text-decoration: underline;
+}
+.megamorphic-cache-ref > a[href] {
+    color: #0489c3;
+    text-decoration: none;
+}
+.megamorphic-cache-ref > a[href] * {
+    color: inherit;
+}
+.megamorphic-ref .emphasize {
+  font-style: italic;
+}
+
+/* dashboards common */
+
+.memory-dashboard,
+.timeline-dashboard {
+  display: block;
+  height: 100%;
+  margin-top: -30px;
+}
+
+.memory-profile > div > h1,
+.timeline-dashboard > div > h1 {
+  overflow: hidden;
+  border-bottom: 1px solid #d5d5d5;
+}
+
+.memory-dashboard button:disabled,
+.timeline-dashboard button:disabled,
+.memory-dashboard button:disabled:hover,
+.timeline-dashboard button:disabled:hover {
+  text-decoration: none;
+  background-color: #eee;
+  background-image: linear-gradient(#eee, #eee);
+  border: 1px solid #eee;
+  border-radius: 2px;
+  cursor: default;
+}
+
+.timeline-dashboard p {
+  margin-top: 0.4em;
+}
+
+.memory-dashboard button,
+.timeline-dashboard button,
+.memory-dashboard button:active,
+.timeline-dashboard button:active {
+  text-decoration: none;
+  background-color: #ddd;
+  background-image: linear-gradient(#eee, #ddd);
+  border: 1px solid #d5d5d5;
+  border-radius: 2px;
+  cursor: pointer;
+}
+
+.memory-dashboard button:hover,
+.timeline-dashboard button:hover {
+  text-decoration: none;
+  background-color: #ccc;
+  background-image: linear-gradient(#ddd, #ccc);
+  border-color: #c7c7c7;
+}
+
+.memory-profile .header_button,
+.timeline-dashboard .header_button {
+  padding: 3px 5px;
+  margin: 0px 1px;
+  min-width: 130px;
+}
+
+.memory-profile .header_button,
+.timeline-dashboard .header_button {
+  margin-left: 6px;
+}
+
+.header_button.left-pad {
+  margin-left: 75px;
+}
+
+.memory-profile .header_button:first-child,
+.timeline-dashboard .header_button:first-child {
+  margin-left: 30px;
+}
+
+.memory-profile .tab_buttons,
+.timeline-dashboard .tab_buttons {
+  position: relative;
+  top: 1px;
+  float: right;
+}
+
+.memory-profile .tab_buttons button,
+.timeline-dashboard .tab_buttons button {
+  padding: 5px 5px;
+  min-width: 100px
+}
+
+.memory-profile .tab_buttons button:not(:first-child),
+.timeline-dashboard .tab_buttons button:not(:first-child) {
+  border-top-left-radius: 0px;
+  border-bottom-left-radius: 0px;
+}
+
+.memory-profile .tab_buttons button:not(:last-child),
+.timeline-dashboard  .tab_buttons button:not(:last-child) {
+  border-top-right-radius: 0px;
+  border-bottom-right-radius: 0px;
+}
+
+
+/* memory-dashboard */
+
+.memory-dashboard .memory-graph {
+  height: 350px;
+}
+
+.memory-dashboard .memory-profile {
+  position: absolute;
+  bottom: 20px;
+  left: 0;
+  right: 0;
+  top: 300px;
+}
+
+.memory-dashboard .memory-profile .memory-allocations {
+  position: absolute;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  top: 39px;
+}
+
+.memory-dashboard .memory-profile .memory-snapshot {
+  position: absolute;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  top: 45px;
+}
+
+/* metric-graph */
+
+.metric-graph {
+  display: block;
+}
+
+.metric-graph .graph {
+  height: 100%;
+  margin-top: -30px;
+  padding: 20px;
+  padding-top: 60px;
+}
+
+.metric-graph .graph > div {
+  height: 100%;
+}
+
+/* memory-allocations */
+
+.memory-allocations .container {
+  padding-left: 5%;
+  padding-right: 5%;
+}
+
+.memory-allocations .collection-item {
+  background-color: #FFFFFF;
+  box-sizing: border-box;
+  line-height: 20px;
+}
+
+.memory-allocations .collection-item:hover {
+  background-color: #d2e7fe;
+}
+
+.memory-allocations .header .collection-item:hover {
+  background-color: #FFFFFF;
+}
+
+.memory-allocations .header .collection-item:last-child {
+  margin-bottom: -3px;
+  border-bottom: solid 1px #AAAAAA;
+}
+
+.memory-allocations .header .collection-item span {
+  font-weight: bolder;
+}
+
+.memory-allocations .collection-item :nth-child(2n+2).group,
+.memory-allocations .collection-item :nth-child(4n+3),
+.memory-allocations .collection-item :nth-child(4n+4) {
+  background-color: #EEEEEE;
+}
+
+.memory-allocations .collection-item:hover :nth-child(2n+2).group,
+.memory-allocations .collection-item:hover :nth-child(4n+3),
+.memory-allocations .collection-item:hover :nth-child(4n+4) {
+  background-color: #afd5fd;
+}
+
+.memory-allocations .header .collection-item :nth-child(2n+1).group,
+.memory-allocations .header .collection-item :nth-child(4n+1),
+.memory-allocations .header .collection-item :nth-child(4n+2) {
+  background-color: #FFFFFF;
+}
+
+.memory-allocations .header .collection-item :nth-child(2n+2).group,
+.memory-allocations .header .collection-item :nth-child(4n+3),
+.memory-allocations .header .collection-item :nth-child(4n+4) {
+  background-color: #DDDDDD;
+}
+
+.memory-allocations .collection-item .group {
+  display: inline-block;
+  width: 12em;
+  text-align: right;
+  padding-right: 0.5em;
+  line-height: 20px;
+  border-right: solid 1px #AAAAAA;
+}
+
+.memory-allocations .collection-item .bytes {
+  display: inline-block;
+  width: 6em;
+  text-align: right;
+  line-height: 20px;
+  padding-right: 0.5em;
+}
+
+.memory-allocations .collection-item .instances {
+  display: inline-block;
+  width: 6em;
+  text-align: right;
+  padding-right: 0.5em;
+  line-height: 20px;
+  border-right: solid 1px #AAAAAA;
+}
+
+.memory-allocations .collection-item .name {
+  padding-left: 0.5em;
+  padding-right: 0.5em;
+  display: inline-block;
+}
+
+.memory-allocations .collection-item > button,
+.memory-allocations .collection-item > button:active,
+.memory-allocations .collection-item .group button,
+.memory-allocations .collection-item .group button:active {
+  background: transparent;
+  color: #0489c3;
+  border: 0px none;
+  border-radius: 0px;
+  padding: 0;
+}
+
+.memory-allocations .collection-item > button:hover,
+.memory-allocations .collection-item .group button:hover {
+  text-decoration: underline;
+  border-radius: 0px;
+}
+
+
+/* memory-snapshot */
+
+.memory-snapshot .statusMessage {
+  font-size: 150%;
+  font-weight: bold;
+}
+.memory-snapshot .statusBox {
+  height: 100%;
+  padding: 1em;
+}
+.memory-snapshot .explanation {
+  display: block;
+  display: -webkit-box;
+  -webkit-line-clamp: 4;
+  -webkit-box-orient: vertical;
+  max-height: 80px;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+.memory-snapshot .virtual-tree {
+  position: absolute;
+  height: auto;
+  top: 50px;
+  bottom: 0;
+  left: 0;
+  right: 0;
+}
+.memory-snapshot .tree-item {
+  box-sizing: border-box;
+  line-height: 30px;
+  height: 30px;
+  padding-left: 5%;
+  padding-right: 5%;
+}
+.memory-snapshot .tree-item > .size,
+.memory-snapshot .tree-item > .percentage {
+  display: inline-block;
+  text-align: right;
+  width: 4em;
+  margin-left: 0.25em;
+  margin-right: 0.25em;
+}
+
+.memory-snapshot .tree-item > .name {
+  display: inline;
+  margin-left: 0.5em;
+}
+
+/* metric-graph */
+
+.metrics-page > div {
+  display: block;
+  height: 100%;
+}
+
+.metrics-page > div > .graph {
+  display: block;
+  height: 100%;
+  margin-top: -300px;
+  padding-top: 300px;
+}
+
+/* nav-bar */
+
+nav.nav-bar {
+  line-height: normal;
+  position: fixed;
+  top: 0;
+  width: 100%;
+  z-index: 1000;
+}
+nav.nav-bar > ul {
+  display: inline-table;
+  position: relative;
+  list-style: none;
+  padding-left: 0;
+  margin: 0;
+  width: 100%;
+  z-index: 1000;
+  font: 400 16px 'Montserrat', sans-serif;
+  color: white;
+  background-color: #0489c3;
+}
+
+/* nav-menu */
+li.nav-menu:before {
+  display: inline-block;
+  content: '>';
+  margin-left: 0.5em;
+  margin-right: 0.5em;
+}
+nav.nav-bar > ul *:first-child li.nav-menu:before,
+nav.nav-bar > ul .nav-reload > li:before {
+  content: '';
+  margin-left: 0;
+  margin-right: 0;
+}
+li.nav-menu, .nav-menu_label {
+  display: inline-block;
+}
+.nav-menu_label > a {
+  display: inline-block;
+  padding: 12px 8px;
+  color: White;
+  text-decoration: none;
+}
+.nav-menu_label:hover > a {
+  background: #455;
+}
+.nav-menu_label > ul {
+  display: none;
+  position: absolute;
+  top: 98%;
+  list-style: none;
+  margin: 0;
+  padding: 0;
+  width: auto;
+  z-index: 1000;
+  font: 400 16px 'Montserrat', sans-serif;
+  color: white;
+  background: #567;
+}
+.nav-menu_label > ul:after {
+  content: ""; clear: both; display: block;
+}
+.nav-menu_label:hover > ul {
+  display: block;
+}
+
+/* nav-menu-item */
+
+.nav-menu-item li.nav-menu-item {
+  float: none;
+  border-top: 1px solid #677;
+  border-bottom: 1px solid #556; position: relative;
+}
+.nav-menu-item li.nav-menu-item:hover {
+  background: #455;
+}
+.nav-menu-item li.nav-menu-item > a {
+  display: block;
+  padding: 12px 12px;
+  color: white;
+  text-decoration: none;
+}
+.nav-menu-item li.nav-menu-item > ul {
+  display: none;
+  position: absolute;
+  top:0;
+  left: 100%;
+  list-style: none;
+  padding: 0;
+  margin-left: 0;
+  width: auto;
+  z-index: 1000;
+  font: 400 16px 'Montserrat', sans-serif;
+  color: white;
+  background: #567;
+}
+.nav-menu-item li.nav-menu-item > ul:after {
+  content: ""; clear: both; display: block;
+}
+.nav-menu-item li.nav-menu-item:hover > ul {
+  display: block;
+}
+
+/* nav-notify */
+
+.nav-notify > div {
+  float: right;
+}
+.nav-notify > div > div {
+  display: block;
+  position: absolute;
+  top: 98%;
+  right: 0;
+  margin: 0;
+  padding: 0;
+  width: auto;
+  z-index: 1000;
+  background: none;
+}
+
+/* nav-exception & nav-event */
+
+.nav-exception > div, .nav-event > div {
+  position: relative;
+  padding: 16px;
+  margin-top: 10px;
+  margin-right: 10px;
+  padding-right: 25px;
+  width: 500px;
+  color: #ddd;
+  background: rgba(0,0,0,.6);
+  border: solid 2px white;
+  box-shadow: 0 0 5px black;
+  border-radius: 5px;
+  animation: fadein 1s;
+}
+
+.nav-exception *, .nav-event * {
+  color: #ddd;
+  font-size: 12px;
+}
+
+.nav-exception > div > a[href],
+.nav-event > div > a[href] {
+  color: white;
+  text-decoration: none;
+}
+
+.nav-exception > div > a[href]:hover,
+.nav-event > div > a[href]:hover {
+  text-decoration: underline;
+}
+
+.nav-exception > div > div {
+  margin-left:20px;
+  white-space: pre
+}
+
+.nav-exception > div > button, .nav-event > div > button {
+  background: transparent;
+  border: none;
+  position: absolute;
+  display: block;
+  top: 4px;
+  right: 4px;
+  height: 18px;
+  width: 18px;
+  line-height: 16px;
+  border-radius: 9px;
+  color: white;
+  font-size: 18px;
+  cursor: pointer;
+  text-align: center;
+}
+
+.nav-exception > div > button:hover, .nav-event > div > button:hover {
+  background: rgba(255,255,255,0.5);
+}
+
+/* nav-refresh */
+
+.nav-refresh > li > button {
+  color: #000;
+  margin: 3px;
+  padding: 8px;
+  border-width: 2px;
+  line-height: 13px;
+  font-size: 13px;
+  font: 400 'Montserrat', sans-serif;
+}
+.nav-refresh > li > button[disabled] {
+  color: #aaa;
+  cursor: wait;
+}
+.nav-refresh > li {
+  float: right;
+  margin: 0;
+}
+
+/* nav-refresh */
+
+.nav-reload > li > button {
+  color: #000;
+  margin: 3px;
+  padding: 8px;
+  border-width: 2px;
+  line-height: 13px;
+  font-size: 13px;
+  font: 400 'Montserrat', sans-serif;
+}
+.nav-reload ul > li {
+  display: block;
+  width: 100%;
+  padding: 2px;
+}
+.nav-reload ul > li > button {
+  display: inline-block;
+  padding: 8px;
+  width: 100%;
+}
+.nav-reload > li > button[disabled],
+.nav-reload ul > li > button[disabled] {
+  color: #aaa;
+  cursor: wait;
+}
+.nav-reload > li {
+  float: right;
+  margin: 0;
+}
+
+/* object-common && class-instances */
+
+.class-instances button:hover,
+.object-common button:hover {
+  background-color: transparent;
+  border: none;
+  text-decoration: underline;
+}
+.class-instances button,
+.object-common button {
+  background-color: transparent;
+  border: none;
+  color: #0489c3;
+  padding: 0;
+  margin: -8px 4px;
+  font-size: 20px;
+  text-decoration: none;
+}
+
+/* object-pool-ref */
+
+.object-pool-ref > a[href]:hover {
+  text-decoration: underline;
+}
+
+.object-pool-ref > a[href] {
+  color: #0489c3;
+  text-decoration: none;
+}
+.object-pool-ref > a[href] * {
+  color: inherit;
+}
+.object-pool-ref .emphasize {
+  font-style: italic;
+}
+
+/* object-pool-view */
+
+.object-pool-view .hexadecimal {
+  font-family: monospace;
+}
+
+/* observatory-application */
+
+.observatory-application {
+  display: block;
+  height: 100%;
+}
+
+.observatory-application > div {
+  display: block;
+  height: 100%;
+}
+
+/* persistent-handles-page */
+
+.persistent-handles-page {
+  display: block;
+  height: 100%;
+}
+.persistent-handles-page .persistent-handles,
+.persistent-handles-page .weak-persistent-handles {
+  margin-top: -70px;
+  padding-top: 70px;
+  height: 50%;
+}
+.persistent-handles-page .weak-item,
+.persistent-handles-page .collection-item {
+  box-sizing: border-box;
+  line-height: 20px;
+  padding-left: 5%;
+  padding-right: 5%;
+}
+.persistent-handles-page .header {
+  box-sizing: border-box;
+  line-height: 20px;
+}
+.persistent-handles-page .header .weak-item:last-child {
+  margin-bottom: -3px;
+  border-bottom: solid 1px #AAAAAA;
+}
+.persistent-handles-page .weak-item .external-size,
+.persistent-handles-page .weak-item .peer,
+.persistent-handles-page .weak-item .object {
+  display: inline-block;
+  width: 7em;
+  text-align: right;
+  padding-right: 0.5em;
+  line-height: 20px;
+}
+.persistent-handles-page .weak-item .peer {
+  width: 11em;
+  font-family: monospace;
+}
+.persistent-handles-page .weak-item .object {
+  text-align: left;
+  width: 25em;
+}
+.persistent-handles-page .buffer .weak-item:hover {
+  background-color: #d2e7fe;
+}
+.persistent-handles-page .weak-item .finalizer {
+  padding-left: 0.5em;
+}
+.persistent-handles-page .weak-item > button,
+.persistent-handles-page .weak-item > button:active {
+  background-color: transparent;
+  color: #0489c3;
+  border-style: none;
+}
+.persistent-handles-page .weak-item > button:hover {
+  text-decoration: underline;
+}
+
+/* ports-page */
+
+.ports-page .port-number {
+  font-weight: bold;
+}
+
+/* script-ref */
+
+.script-ref > a[href]:hover {
+  text-decoration: underline;
+}
+.script-ref > a[href] {
+  color: #0489c3;
+  text-decoration: none;
+}
+.script-ref .emphasize {
+  font-style: italic;
+}
+
+
+/* source-link */
+
+.source-link > a[href]:hover {
+    text-decoration: underline;
+}
+
+.source-link > a[href] {
+    color: #0489c3;
+    text-decoration: none;
+}
+
+
+/* observatory-application */
+
+.observatory-application {
+  display: block;
+  height: 100%;
+}
+
+.observatory-application > div {
+  display: block;
+  height: 100%;
+}
+
+/* sample-buffer-control */
+
+.sample-buffer-control {
+  white-space: nowrap;
+}
+
+.sample-buffer-control .statusMessage {
+  font-size: 150%;
+  font-weight: bold;
+}
+
+.sample-buffer-control .statusBox {
+  height: 100%;
+  padding: 1em;
+}
+
+.sample-buffer-control .center {
+  align-items: center;
+  justify-content: center;
+}
+
+.sample-buffer-control .notice {
+  background-color: #fcf8e3;
+}
+
+.sample-buffer-control .red {
+  background-color: #f2dede;
+}
+
+.sample-buffer-control .shadow {
+  box-shadow: 0 2px 10px 0 rgba(0, 0, 0, 0.16),
+              0 2px 5px 0 rgba(0, 0, 0, 0.26);
+}
+
+/* script-inset */
+
+.script-inset {
+  position: relative;
+}
+.script-inset button.refresh,
+.script-inset button.toggle-profile {
+  background-color: transparent;
+  padding: 0;
+  margin: 0;
+  border: none;
+  position: absolute;
+  display: inline-block;
+  top: 5px;
+}
+.script-inset button.refresh > svg,
+.script-inset button.toggle-profile > svg {
+  fill: #888888;
+}
+.script-inset button.refresh {
+  right: 5px;
+}
+.script-inset button.toggle-profile {
+  right: 30px;
+}
+.script-inset button.toggle-profile.enabled > svg {
+  fill: #BB3322;
+}
+.script-inset a {
+  color: #0489c3;
+  text-decoration: none;
+}
+.script-inset a:hover {
+  text-decoration: underline;
+}
+.script-inset .sourceInset {
+}
+.script-inset .sourceTable {
+  position: relative;
+  background-color: #f5f5f5;
+  border: 1px solid #ccc;
+  padding: 10px;
+  width: 100%;
+  box-sizing: border-box;
+  overflow-x: scroll;
+}
+.script-inset .sourceRow {
+  display: flex;
+  flex-direction: row;
+  width: 100%;
+}
+.script-inset .sourceItem,
+.script-inset .sourceItemCurrent {
+  vertical-align: top;
+  font: 400 14px consolas, courier, monospace;
+  line-height: 125%;
+  white-space: pre;
+  max-width: 0;
+}
+.script-inset .currentLine {
+  background-color: #fff;
+}
+.script-inset .currentCol {
+  background-color: #6cf;
+}
+.script-inset .hitsCurrent,
+.script-inset .hitsNone,
+.script-inset .hitsNotExecuted,
+.script-inset .hitsExecuted,
+.script-inset .hitsCompiled,
+.script-inset .hitsNotCompiled {
+  display: table-cell;
+  vertical-align: top;
+  font: 400 14px consolas, courier, monospace;
+  margin-left: 5px;
+  margin-right: 5px;
+  text-align: right;
+  color: #a8a8a8;
+}
+.script-inset .hitsCurrent {
+  background-color: #6cf;
+  color: black;
+}
+.script-inset .hitsNotExecuted {
+  background-color: #faa;
+}
+.script-inset .hitsExecuted {
+  background-color: #aea;
+}
+.script-inset .hitsCompiled {
+  background-color: #e0e0e0;
+}
+.script-inset .hitsNotCompiled {
+  background-color: #f0c5c5;
+}
+.script-inset .noCopy {}
+.script-inset .emptyBreakpoint,
+.script-inset .possibleBreakpoint,
+.script-inset .busyBreakpoint,
+.script-inset .unresolvedBreakpoint,
+.script-inset .resolvedBreakpoint  {
+  display: table-cell;
+  vertical-align: top;
+  font: 400 14px consolas, courier, monospace;
+  width: 1em;
+  text-align: center;
+  cursor: pointer;
+}
+.script-inset .possibleBreakpoint {
+  color: #e0e0e0;
+}
+.script-inset .possibleBreakpoint:hover {
+  color: white;
+  background-color: #777;
+}
+.script-inset .busyBreakpoint {
+  color: white;
+  background-color: black;
+  cursor: wait;
+}
+.script-inset .unresolvedBreakpoint {
+  color: white;
+  background-color: #cac;
+}
+.script-inset .resolvedBreakpoint {
+  color: white;
+  background-color: #e66;
+}
+.script-inset .unresolvedBreakAnnotation {
+  color: white;
+  background-color: #cac;
+}
+.script-inset .resolvedBreakAnnotation {
+  color: white;
+  background-color: #e66;
+}
+.script-inset .notSourceProfile,
+.script-inset .noProfile,
+.script-inset .coldProfile,
+.script-inset .mediumProfile,
+.script-inset .hotProfile {
+  display: table-cell;
+  vertical-align: top;
+  font: 400 14px consolas, courier, monospace;
+  width: 4em;
+  text-align: right;
+  cursor: pointer;
+  margin-left: 5px;
+  margin-right: 5px;
+}
+.script-inset .notSourceProfile {
+}
+.script-inset .noProfile {
+  background-color: #e0e0e0;
+}
+.script-inset .coldProfile {
+  background-color: #aea;
+}
+.script-inset .mediumProfile {
+  background-color: #fe9;
+}
+.script-inset .hotProfile {
+  background-color: #faa;
+}
+
+/* search-bar */
+
+.search-bar {
+  background: white;
+  padding: 5px;
+}
+.search-bar > input {
+  width: 300px;
+  line-height: 20px;
+  border-color: #DDDDDD;
+  padding-left: 2px;
+  padding-right: 2px;
+}
+
+.search-bar button:enabled,
+.search-bar button:enabled:hover,
+.search-bar button:disabled,
+.search-bar button:disabled:hover,
+.search-bar button:active {
+  border: none;
+  background: none;
+  background-color: transparent;
+  line-height: 20px;
+  margin-left: 0.5em;
+  margin-right: 0.5em;
+  outline: none;
+}
+
+.search-bar button:disabled,
+.search-bar button:disabled:hover {
+  color: #DDDDDD;
+}
+
+.search-bar button:enabled,
+.search-bar button:enabled:active {
+  color: #BBBBBB;
+}
+
+.search-bar button:enabled:hover {
+  color: #AAAAAA;
+  text-shadow: 1px 1px 2px #dddddd;
+}
+
+/* stack-trace-tree-config */
+
+.stack-trace-tree-config {
+  white-space: nowrap;
+}
+
+/* timeline-dashboard */
+
+.timeline-dashboard .iframe {
+  position: absolute;
+  top: 90px;
+  bottom: 0;
+  left: 0;
+  right: 0;
+}
+.timeline-dashboard h1 {
+  margin-bottom: 5px;
+}
+.timeline-dashboard .iframe iframe {
+  width: 100%;
+  height: 100%;
+  border: none;
+}
+
+/* timeline-page */
+
+.timeline-page .iframe {
+  height: 100%;
+  width: 100%;
+  margin-top: -120px;
+  padding-top: 120px;
+}
+.timeline-page .iframe iframe {
+  width: 100%;
+  height: 100%;
+  border: none;
+}
+
+/* type-arguments-ref */
+
+.type-arguments-ref a[href]:hover {
+  text-decoration: underline;
+}
+.type-arguments-ref a[href] {
+  color: #0489c3;
+  text-decoration: none;
+}
+.type-arguments-ref .emphasize {
+  font-style: italic;
+}
+
+
+/* token-stream-ref */
+
+.token-stream-ref a[href]:hover {
+  text-decoration: underline;
+}
+.token-stream-ref a[href] {
+  color: #0489c3;
+  text-decoration: none;
+}
+.token-stream-ref .emphasize {
+  font-style: italic;
+}
+
+
+/* unknown-ref */
+
+.unknown-ref > a[href]:hover {
+  text-decoration: underline;
+}
+.unknown-ref > a[href] {
+  color: #0489c3;
+  text-decoration: none;
+}
+.unknown-ref > a[href] * {
+  color: inherit;
+}
+.unknown-ref .emphasize {
+  font-style: italic;
+}
+
+
+/* view-footer */
+
+.view-footer {
+  padding: 1em;
+  padding-top: 10.3em;
+  float: right;
+  align-content: right;
+}
+
+.view-footer > a {
+  margin-bottom: 0.17em;
+  font-size: 90%;
+  float: right;
+  clear: both;
+  display: block;
+}
+
+/* virtual-collection */
+
+.virtual-collection {
+  position: relative;
+  display: block;
+  overflow-x: hidden;
+  width: 100%;
+  height: 100%;
+}
+
+.virtual-collection .viewport {
+  position: absolute;
+  display: block;
+  overflow-y: auto;
+  overflow-x: auto;
+  top: 0;
+  bottom: 0;
+  right: 0;
+  left: 0;
+}
+
+.virtual-collection .header {
+  background: white;
+  position: relative;
+  display: block;
+  z-index: +1;
+}
+
+.virtual-collection .search-bar {
+  position: relative;
+  display: block;
+  z-index: +1;
+  float: right;
+  top: 2px;
+  right: 25px;
+  border-bottom-left-radius: 5px;
+  border-bottom-right-radius: 5px;
+  box-shadow: 0px 1px 2px #AAAAAA;
+}
+
+.virtual-collection .spacer {
+  overflow: hidden;
+  background: transparent;
+  display: inline-block;
+  min-width: 100%;
+}
+
+.virtual-collection .buffer {
+  background: transparent;
+  position: relative;
+  display: inline-block;
+  min-width: 100%;
+}
+
+.virtual-collection .header > div,
+.virtual-collection .buffer > div {
+  white-space: nowrap;
+}
+
+.virtual-collection .header.attached > div,
+.virtual-collection .buffer > div {
+/*  display: inline-block;*/
+}
+
+.virtual-collection .buffer > div {
+  min-width: 100%;
+  white-space: nowrap;
+}
+
+.virtual-collection .marked,
+.virtual-collection .marked * {
+  background: yellow !important;
+}
+
+/* virtual-tree */
+
+.virtual-tree .expander,
+.virtual-tree .expander:hover,
+.virtual-tree .expander:active {
+  display: inline-block;
+  text-align: center;
+  font-weight: bold;
+  font-size: 18px;
+  width: 19px;
+  border: 0px none;
+  background: transparent;
+  padding: 0;
+}
+
+.virtual-tree .expander:focus {
+  outline: none;
+}
+
+.virtual-tree .lines,
+.virtual-tree .lines > span {
+  display: inline-block;
+  vertical-align: top;
+  height: 100%;
+  line-height: 100%;
+}
+
+.virtual-tree .lines:before,
+.virtual-tree .lines > span:before {
+  display: inline-block;
+  content: '';
+}
+
+.virtual-tree .lines > span {
+  width: 3px;
+  margin-left: 8px;
+  margin-right: 8px;
+}
+
+.virtual-tree .lines > span:nth-child(5n) {
+  background-color: #673AB7;
+}
+
+.virtual-tree .lines > span:nth-child(5n+1) {
+  background-color: #F44336;
+}
+
+.virtual-tree .lines > span:nth-child(5n+2) {
+  background-color: #4CAF50;
+}
+
+.virtual-tree .lines > span:nth-child(5n+3) {
+  background-color: #3F51B5;
+}
+
+.virtual-tree .lines > span:nth-child(5n+4) {
+  background-color: #FF9800;
+}
+
+/* vm-connect-target */
+
+.vm-connect-target > a {
+  color: #0489c3;
+  text-decoration: none;
+}
+
+.vm-connect-target > a:hover {
+  text-decoration: underline;
+}
+
+.vm-connect-target > button.delete-button {
+  margin-left: 0.28em;
+  padding: 4px;
+  background: transparent;
+  border: none !important;
+}
+
+.vm-connect-target > button.delete-button:hover {
+  background: #ff0000;
+}
+
+/* vm-connect */
+
+.vm-connect ul {
+  list-style-type: none;
+}
+
+.treemapTile {
+  position: absolute;
+  box-sizing: border-box;
+  border: solid 1px;
+  font-size: 10px;
+  text-align: center;
+  overflow: hidden;
+  white-space: nowrap;
+  cursor: default;
+}
diff --git a/runtime/observatory_2/lib/src/elements/curly_block.dart b/runtime/observatory_2/lib/src/elements/curly_block.dart
new file mode 100644
index 0000000..a5f2cc2
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/curly_block.dart
@@ -0,0 +1,102 @@
+// 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.
+
+library curly_block_element;
+
+import 'dart:async';
+import 'dart:html';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+
+class CurlyBlockToggleEvent {
+  final CurlyBlockElement control;
+
+  CurlyBlockToggleEvent(this.control);
+}
+
+class CurlyBlockElement extends CustomElement implements Renderable {
+  RenderingScheduler<CurlyBlockElement> _r;
+
+  final StreamController<CurlyBlockToggleEvent> _onToggle =
+      new StreamController<CurlyBlockToggleEvent>.broadcast();
+  Stream<CurlyBlockToggleEvent> get onToggle => _onToggle.stream;
+  Stream<RenderedEvent<CurlyBlockElement>> get onRendered => _r.onRendered;
+
+  bool _expanded;
+  bool _disabled;
+  Iterable<Element> _content = const [];
+
+  bool get expanded => _expanded;
+  bool get disabled => _disabled;
+  Iterable<Element> get content => _content;
+
+  set expanded(bool value) {
+    if (_expanded != value) _onToggle.add(new CurlyBlockToggleEvent(this));
+    _expanded = _r.checkAndReact(_expanded, value);
+  }
+
+  set disabled(bool value) => _disabled = _r.checkAndReact(_disabled, value);
+  set content(Iterable<Element> value) {
+    _content = value.toList();
+    _r.dirty();
+  }
+
+  factory CurlyBlockElement(
+      {bool expanded: false, bool disabled: false, RenderingQueue queue}) {
+    assert(expanded != null);
+    assert(disabled != null);
+    CurlyBlockElement e = new CurlyBlockElement.created();
+    e._r = new RenderingScheduler<CurlyBlockElement>(e, queue: queue);
+    e._expanded = expanded;
+    e._disabled = disabled;
+    return e;
+  }
+
+  CurlyBlockElement.created() : super.created('curly-block');
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+  }
+
+  void toggle() {
+    if (disabled) {
+      _r.scheduleNotification();
+      return;
+    }
+    expanded = !expanded;
+  }
+
+  void render() {
+    List<Element> content = <Element>[new SpanElement()..text = '{'];
+    SpanElement label = new SpanElement()
+      ..classes = disabled ? ['curly-block', 'disabled'] : ['curly-block']
+      ..innerHtml = expanded
+          ? '&nbsp;&nbsp;&#8863;&nbsp;&nbsp;'
+          : '&nbsp;&nbsp;&#8862;&nbsp;&nbsp;';
+    if (disabled) {
+      content.add(label);
+    } else {
+      content.add(new AnchorElement()
+        ..onClick.listen((_) {
+          toggle();
+        })
+        ..children = <Element>[label]);
+    }
+    if (expanded) {
+      content.add(new BRElement());
+      content.addAll(_content);
+    }
+    content.add(new SpanElement()..text = '}');
+    children = content;
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/debugger.dart b/runtime/observatory_2/lib/src/elements/debugger.dart
new file mode 100644
index 0000000..9ec8688
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/debugger.dart
@@ -0,0 +1,3372 @@
+// Copyright (c) 2014, 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.
+
+library debugger_page_element;
+
+import 'dart:async';
+import 'dart:html';
+import 'dart:math';
+import 'dart:svg';
+
+import 'package:logging/logging.dart';
+import 'package:observatory_2/app.dart';
+import 'package:observatory_2/cli.dart';
+import 'package:observatory_2/debugger.dart';
+import 'package:observatory_2/event.dart';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/service.dart' as S;
+import 'package:observatory_2/repositories.dart' as R;
+import 'package:observatory_2/src/elements/function_ref.dart';
+import 'package:observatory_2/src/elements/helpers/any_ref.dart';
+import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
+import 'package:observatory_2/src/elements/helpers/nav_menu.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/helpers/uris.dart';
+import 'package:observatory_2/src/elements/instance_ref.dart';
+import 'package:observatory_2/src/elements/nav/isolate_menu.dart';
+import 'package:observatory_2/src/elements/nav/notify.dart';
+import 'package:observatory_2/src/elements/nav/top_menu.dart';
+import 'package:observatory_2/src/elements/nav/vm_menu.dart';
+import 'package:observatory_2/src/elements/source_inset.dart';
+import 'package:observatory_2/src/elements/source_link.dart';
+
+// TODO(turnidge): Move Debugger, DebuggerCommand to debugger library.
+abstract class DebuggerCommand extends Command {
+  ObservatoryDebugger debugger;
+
+  DebuggerCommand(this.debugger, name, List<Command> children)
+      : super(name, children);
+
+  String get helpShort;
+  String get helpLong;
+}
+
+// TODO(turnidge): Rewrite HelpCommand so that it is a general utility
+// provided by the cli library.
+class HelpCommand extends DebuggerCommand {
+  HelpCommand(Debugger debugger)
+      : super(debugger, 'help', <Command>[
+          new HelpHotkeysCommand(debugger),
+        ]);
+
+  String _nameAndAlias(Command cmd) {
+    if (cmd.alias == null) {
+      return cmd.fullName;
+    } else {
+      return '${cmd.fullName}, ${cmd.alias}';
+    }
+  }
+
+  Future run(List<String> args) {
+    var con = debugger.console;
+    if (args.length == 0) {
+      // Print list of all top-level commands.
+      var commands = debugger.cmd.matchCommand(<String>[], false);
+      commands.sort((a, b) => a.name.compareTo(b.name));
+      con.print('List of commands:\n');
+      for (DebuggerCommand command in commands) {
+        con.print('${_nameAndAlias(command).padRight(12)} '
+            '- ${command.helpShort}');
+      }
+      con.print(
+          "\nFor more information on a specific command type 'help <command>'\n"
+          "For a list of hotkeys type 'help hotkeys'\n"
+          "\n"
+          "Command prefixes are accepted (e.g. 'h' for 'help')\n"
+          "Hit [TAB] to complete a command (try 'is[TAB][TAB]')\n"
+          "Hit [ENTER] to repeat the last command\n"
+          "Use up/down arrow for command history\n");
+      return new Future.value(null);
+    } else {
+      // Print any matching commands.
+      var commands = debugger.cmd.matchCommand(args, true);
+      commands.sort((a, b) => a.name.compareTo(b.name));
+      if (commands.isEmpty) {
+        var line = args.join(' ');
+        con.print("No command matches '${line}'");
+        return new Future.value(null);
+      }
+      con.print('');
+      for (DebuggerCommand command in commands) {
+        con.printBold(_nameAndAlias(command));
+        con.print(command.helpLong);
+
+        var newArgs = <String>[];
+        newArgs.addAll(args.take(args.length - 1));
+        newArgs.add(command.name);
+        newArgs.add('');
+        var subCommands = debugger.cmd.matchCommand(newArgs, false);
+        subCommands.remove(command);
+        if (subCommands.isNotEmpty) {
+          subCommands.sort((a, b) => a.name.compareTo(b.name));
+          con.print('Subcommands:\n');
+          for (DebuggerCommand subCommand in subCommands) {
+            con.print('    ${subCommand.fullName.padRight(16)} '
+                '- ${subCommand.helpShort}');
+          }
+          con.print('');
+        }
+      }
+      return new Future.value(null);
+    }
+  }
+
+  Future<List<String>> complete(List<String> args) {
+    var commands = debugger.cmd.matchCommand(args, false);
+    var result = commands.map((command) => '${command.fullName} ');
+    return new Future.value(result.toList());
+  }
+
+  String helpShort =
+      'List commands or provide details about a specific command';
+
+  String helpLong =
+      'List commands or provide details about a specific command.\n'
+      '\n'
+      'Syntax: help            - Show a list of all commands\n'
+      '        help <command>  - Help for a specific command\n';
+}
+
+class HelpHotkeysCommand extends DebuggerCommand {
+  HelpHotkeysCommand(Debugger debugger)
+      : super(debugger, 'hotkeys', <Command>[]);
+
+  Future run(List<String> args) {
+    var con = debugger.console;
+    con.print("List of hotkeys:\n"
+        "\n"
+        "[TAB]        - complete a command\n"
+        "[Up Arrow]   - history previous\n"
+        "[Down Arrow] - history next\n"
+        "\n"
+        "[Page Up]    - move up one frame\n"
+        "[Page Down]  - move down one frame\n"
+        "\n"
+        "[F7]         - continue execution of the current isolate\n"
+        "[Ctrl ;]     - pause execution of the current isolate\n"
+        "\n"
+        "[F8]         - toggle breakpoint at current location\n"
+        "[F9]         - next\n"
+        "[F10]        - step\n"
+        "\n");
+    return new Future.value(null);
+  }
+
+  String helpShort = 'Provide a list of hotkeys';
+
+  String helpLong = 'Provide a list of key hotkeys.\n'
+      '\n'
+      'Syntax: help hotkeys\n';
+}
+
+class PrintCommand extends DebuggerCommand {
+  PrintCommand(Debugger debugger) : super(debugger, 'print', <Command>[]) {
+    alias = 'p';
+  }
+
+  Future run(List<String> args) async {
+    if (args.length < 1) {
+      debugger.console.print('print expects arguments');
+      return;
+    }
+    if (debugger.currentFrame == null) {
+      debugger.console.print('No stack');
+      return;
+    }
+    var expression = args.join('');
+    var response =
+        await debugger.isolate.evalFrame(debugger.currentFrame, expression);
+    if (response is S.DartError) {
+      debugger.console.print(response.message);
+    } else if (response is S.Sentinel) {
+      debugger.console.print(response.valueAsString);
+    } else {
+      debugger.console.print('= ', newline: false);
+      debugger.console.printRef(debugger.isolate, response, debugger.objects);
+    }
+  }
+
+  String helpShort = 'Evaluate and print an expression in the current frame';
+
+  String helpLong = 'Evaluate and print an expression in the current frame.\n'
+      '\n'
+      'Syntax: print <expression>\n'
+      '        p <expression>\n';
+}
+
+class DownCommand extends DebuggerCommand {
+  DownCommand(Debugger debugger) : super(debugger, 'down', <Command>[]);
+
+  Future run(List<String> args) {
+    int count = 1;
+    if (args.length == 1) {
+      count = int.parse(args[0]);
+    } else if (args.length > 1) {
+      debugger.console.print('down expects 0 or 1 argument');
+      return new Future.value(null);
+    }
+    if (debugger.currentFrame == null) {
+      debugger.console.print('No stack');
+      return new Future.value(null);
+    }
+    try {
+      debugger.downFrame(count);
+      debugger.console.print('frame = ${debugger.currentFrame}');
+    } catch (e) {
+      debugger.console
+          .print('frame must be in range [${e.start}..${e.end - 1}]');
+    }
+    return new Future.value(null);
+  }
+
+  String helpShort = 'Move down one or more frames (hotkey: [Page Down])';
+
+  String helpLong = 'Move down one or more frames.\n'
+      '\n'
+      'Hotkey: [Page Down]\n'
+      '\n'
+      'Syntax: down\n'
+      '        down <count>\n';
+}
+
+class UpCommand extends DebuggerCommand {
+  UpCommand(Debugger debugger) : super(debugger, 'up', <Command>[]);
+
+  Future run(List<String> args) {
+    int count = 1;
+    if (args.length == 1) {
+      count = int.parse(args[0]);
+    } else if (args.length > 1) {
+      debugger.console.print('up expects 0 or 1 argument');
+      return new Future.value(null);
+    }
+    if (debugger.currentFrame == null) {
+      debugger.console.print('No stack');
+      return new Future.value(null);
+    }
+    try {
+      debugger.upFrame(count);
+      debugger.console.print('frame = ${debugger.currentFrame}');
+    } on RangeError catch (e) {
+      debugger.console
+          .print('frame must be in range [${e.start}..${e.end - 1}]');
+    }
+    return new Future.value(null);
+  }
+
+  String helpShort = 'Move up one or more frames (hotkey: [Page Up])';
+
+  String helpLong = 'Move up one or more frames.\n'
+      '\n'
+      'Hotkey: [Page Up]\n'
+      '\n'
+      'Syntax: up\n'
+      '        up <count>\n';
+}
+
+class FrameCommand extends DebuggerCommand {
+  FrameCommand(Debugger debugger) : super(debugger, 'frame', <Command>[]) {
+    alias = 'f';
+  }
+
+  Future run(List<String> args) {
+    int frame = 1;
+    if (args.length == 1) {
+      frame = int.parse(args[0]);
+    } else {
+      debugger.console.print('frame expects 1 argument');
+      return new Future.value(null);
+    }
+    if (debugger.currentFrame == null) {
+      debugger.console.print('No stack');
+      return new Future.value(null);
+    }
+    try {
+      debugger.currentFrame = frame;
+      debugger.console.print('frame = ${debugger.currentFrame}');
+    } on RangeError catch (e) {
+      debugger.console
+          .print('frame must be in range [${e.start}..${e.end - 1}]');
+    }
+    return new Future.value(null);
+  }
+
+  String helpShort = 'Set the current frame';
+
+  String helpLong = 'Set the current frame.\n'
+      '\n'
+      'Syntax: frame <number>\n'
+      '        f <count>\n';
+}
+
+class PauseCommand extends DebuggerCommand {
+  PauseCommand(Debugger debugger) : super(debugger, 'pause', <Command>[]);
+
+  Future run(List<String> args) {
+    return debugger.pause();
+  }
+
+  String helpShort = 'Pause the isolate (hotkey: [Ctrl ;])';
+
+  String helpLong = 'Pause the isolate.\n'
+      '\n'
+      'Hotkey: [Ctrl ;]\n'
+      '\n'
+      'Syntax: pause\n';
+}
+
+class ContinueCommand extends DebuggerCommand {
+  ContinueCommand(Debugger debugger)
+      : super(debugger, 'continue', <Command>[]) {
+    alias = 'c';
+  }
+
+  Future run(List<String> args) {
+    return debugger.resume();
+  }
+
+  String helpShort = 'Resume execution of the isolate (hotkey: [F7])';
+
+  String helpLong = 'Continue running the isolate.\n'
+      '\n'
+      'Hotkey: [F7]\n'
+      '\n'
+      'Syntax: continue\n'
+      '        c\n';
+}
+
+class SmartNextCommand extends DebuggerCommand {
+  SmartNextCommand(Debugger debugger) : super(debugger, 'next', <Command>[]) {
+    alias = 'n';
+  }
+
+  Future run(List<String> args) async {
+    return debugger.smartNext();
+  }
+
+  String helpShort =
+      'Continue running the isolate until it reaches the next source location '
+      'in the current function (hotkey: [F9])';
+
+  String helpLong =
+      'Continue running the isolate until it reaches the next source location '
+      'in the current function.\n'
+      '\n'
+      'Hotkey: [F9]\n'
+      '\n'
+      'Syntax: next\n';
+}
+
+class SyncNextCommand extends DebuggerCommand {
+  SyncNextCommand(Debugger debugger)
+      : super(debugger, 'next-sync', <Command>[]);
+
+  Future run(List<String> args) {
+    return debugger.syncNext();
+  }
+
+  String helpShort = 'Run until return/unwind to current activation.';
+
+  String helpLong =
+      'Continue running the isolate until control returns to the current '
+      'activation or one of its callers.\n'
+      '\n'
+      'Syntax: next-sync\n';
+}
+
+class AsyncNextCommand extends DebuggerCommand {
+  AsyncNextCommand(Debugger debugger)
+      : super(debugger, 'next-async', <Command>[]);
+
+  Future run(List<String> args) {
+    return debugger.asyncNext();
+  }
+
+  String helpShort = 'Step over await or yield';
+
+  String helpLong =
+      'Continue running the isolate until control returns to the current '
+      'activation of an async or async* function.\n'
+      '\n'
+      'Syntax: next-async\n';
+}
+
+class StepCommand extends DebuggerCommand {
+  StepCommand(Debugger debugger) : super(debugger, 'step', <Command>[]) {
+    alias = 's';
+  }
+
+  Future run(List<String> args) {
+    return debugger.step();
+  }
+
+  String helpShort =
+      'Continue running the isolate until it reaches the next source location'
+      ' (hotkey: [F10]';
+
+  String helpLong =
+      'Continue running the isolate until it reaches the next source '
+      'location.\n'
+      '\n'
+      'Hotkey: [F10]\n'
+      '\n'
+      'Syntax: step\n';
+}
+
+class RewindCommand extends DebuggerCommand {
+  RewindCommand(Debugger debugger) : super(debugger, 'rewind', <Command>[]);
+
+  Future run(List<String> args) async {
+    try {
+      int count = 1;
+      if (args.length == 1) {
+        count = int.tryParse(args[0]);
+        if (count == null || count < 1 || count >= debugger.stackDepth) {
+          debugger.console.print(
+              'Frame must be an int in bounds [1..${debugger.stackDepth - 1}]:'
+              ' saw ${args[0]}');
+          return;
+        }
+      } else if (args.length > 1) {
+        debugger.console.print('rewind expects 0 or 1 argument');
+        return;
+      }
+      await debugger.rewind(count);
+    } on S.ServerRpcException catch (e) {
+      if (e.code == S.ServerRpcException.kCannotResume) {
+        debugger.console.printRed(e.data['details']);
+      } else {
+        rethrow;
+      }
+    }
+  }
+
+  String helpShort = 'Rewind the stack to a previous frame';
+
+  String helpLong = 'Rewind the stack to a previous frame.\n'
+      '\n'
+      'Syntax: rewind\n'
+      '        rewind <count>\n';
+}
+
+class ReloadCommand extends DebuggerCommand {
+  final M.IsolateRepository _isolates;
+
+  ReloadCommand(Debugger debugger, this._isolates)
+      : super(debugger, 'reload', <Command>[]);
+
+  Future run(List<String> args) async {
+    try {
+      if (args.length > 0) {
+        debugger.console.print('reload expects no arguments');
+        return;
+      }
+      if (_isolates.reloadSourcesServices.isEmpty) {
+        await _isolates.reloadSources(debugger.isolate);
+      } else {
+        await _isolates.reloadSources(debugger.isolate,
+            service: _isolates.reloadSourcesServices.first);
+      }
+      debugger.console.print('reload complete');
+      await debugger.refreshStack();
+    } on S.ServerRpcException catch (e) {
+      if (e.code == S.ServerRpcException.kIsolateReloadBarred ||
+          e.code == S.ServerRpcException.kIsolateIsReloading) {
+        debugger.console.printRed(e.data['details']);
+      } else {
+        rethrow;
+      }
+    }
+  }
+
+  String helpShort = 'Reload the sources for the current isolate';
+
+  String helpLong = 'Reload the sources for the current isolate.\n'
+      '\n'
+      'Syntax: reload\n';
+}
+
+class ClsCommand extends DebuggerCommand {
+  ClsCommand(Debugger debugger) : super(debugger, 'cls', <Command>[]) {}
+
+  Future run(List<String> args) {
+    debugger.console.clear();
+    debugger.console.newline();
+    return new Future.value(null);
+  }
+
+  String helpShort = 'Clear the console';
+
+  String helpLong = 'Clear the console.\n'
+      '\n'
+      'Syntax: cls\n';
+}
+
+class LogCommand extends DebuggerCommand {
+  LogCommand(Debugger debugger) : super(debugger, 'log', <Command>[]);
+
+  Future run(List<String> args) async {
+    if (args.length == 0) {
+      debugger.console.print('Current log level: '
+          '${debugger._consolePrinter._minimumLogLevel.name}');
+      return new Future.value(null);
+    }
+    if (args.length > 1) {
+      debugger.console.print("log expects zero or one arguments");
+      return new Future.value(null);
+    }
+    var level = _findLevel(args[0]);
+    if (level == null) {
+      debugger.console.print('No such log level: ${args[0]}');
+      return new Future.value(null);
+    }
+    debugger._consolePrinter._minimumLogLevel = level;
+    debugger.console.print('Set log level to: ${level.name}');
+    return new Future.value(null);
+  }
+
+  Level _findLevel(String levelName) {
+    levelName = levelName.toUpperCase();
+    for (var level in Level.LEVELS) {
+      if (level.name == levelName) {
+        return level;
+      }
+    }
+    return null;
+  }
+
+  Future<List<String>> complete(List<String> args) {
+    if (args.length != 1) {
+      return new Future.value([args.join('')]);
+    }
+    var prefix = args[0].toUpperCase();
+    var result = <String>[];
+    for (var level in Level.LEVELS) {
+      if (level.name.startsWith(prefix)) {
+        result.add(level.name);
+      }
+    }
+    return new Future.value(result);
+  }
+
+  String helpShort = 'Control which log messages are displayed';
+
+  String helpLong =
+      'Get or set the minimum log level that should be displayed.\n'
+      '\n'
+      'Log levels (in ascending order): ALL, FINEST, FINER, FINE, CONFIG, '
+      'INFO, WARNING, SEVERE, SHOUT, OFF\n'
+      '\n'
+      'Default: OFF\n'
+      '\n'
+      'Syntax: log          '
+      '# Display the current minimum log level.\n'
+      '        log <level>  '
+      '# Set the minimum log level to <level>.\n'
+      '        log OFF      '
+      '# Display no log messages.\n'
+      '        log ALL      '
+      '# Display all log messages.\n';
+}
+
+class FinishCommand extends DebuggerCommand {
+  FinishCommand(Debugger debugger) : super(debugger, 'finish', <Command>[]);
+
+  Future run(List<String> args) {
+    if (debugger.isolatePaused()) {
+      var event = debugger.isolate.pauseEvent;
+      if (event is M.PauseStartEvent) {
+        debugger.console
+            .print("Type 'continue' [F7] or 'step' [F10] to start the isolate");
+        return new Future.value(null);
+      }
+      if (event is M.PauseExitEvent) {
+        debugger.console.print("Type 'continue' [F7] to exit the isolate");
+        return new Future.value(null);
+      }
+      return debugger.isolate.stepOut();
+    } else {
+      debugger.console.print('The program is already running');
+      return new Future.value(null);
+    }
+  }
+
+  String helpShort =
+      'Continue running the isolate until the current function exits';
+
+  String helpLong =
+      'Continue running the isolate until the current function exits.\n'
+      '\n'
+      'Syntax: finish\n';
+}
+
+class SetCommand extends DebuggerCommand {
+  SetCommand(Debugger debugger) : super(debugger, 'set', <Command>[]);
+
+  static var _boeValues = ['All', 'None', 'Unhandled'];
+  static var _boolValues = ['false', 'true'];
+
+  static var _options = {
+    'break-on-exception': [
+      _boeValues,
+      _setBreakOnException,
+      (debugger, _) => debugger.breakOnException
+    ],
+    'up-is-down': [
+      _boolValues,
+      _setUpIsDown,
+      (debugger, _) => debugger.upIsDown
+    ],
+    'causal-async-stacks': [
+      _boolValues,
+      _setCausalAsyncStacks,
+      (debugger, _) => debugger.saneAsyncStacks
+    ],
+    'awaiter-stacks': [
+      _boolValues,
+      _setAwaiterStacks,
+      (debugger, _) => debugger.awaiterStacks
+    ]
+  };
+
+  static Future _setBreakOnException(debugger, name, value) async {
+    var result = await debugger.isolate.setExceptionPauseMode(value);
+    if (result.isError) {
+      debugger.console.print(result.toString());
+    } else {
+      // Printing will occur elsewhere.
+      debugger.breakOnException = value;
+    }
+  }
+
+  static Future _setUpIsDown(debugger, name, value) async {
+    if (value == 'true') {
+      debugger.upIsDown = true;
+    } else {
+      debugger.upIsDown = false;
+    }
+    debugger.console.print('${name} = ${value}');
+  }
+
+  static Future _setCausalAsyncStacks(debugger, name, value) async {
+    if (value == 'true') {
+      debugger.causalAsyncStacks = true;
+    } else {
+      debugger.causalAsyncStacks = false;
+    }
+    debugger.refreshStack();
+    debugger.console.print('${name} = ${value}');
+  }
+
+  static Future _setAwaiterStacks(debugger, name, value) async {
+    debugger.awaiterStacks = (value == 'true');
+    debugger.refreshStack();
+    debugger.console.print('${name} == ${value}');
+  }
+
+  Future run(List<String> args) async {
+    if (args.length == 0) {
+      for (var name in _options.keys) {
+        dynamic getHandler = _options[name][2];
+        var value = await getHandler(debugger, name);
+        debugger.console.print("${name} = ${value}");
+      }
+    } else if (args.length == 1) {
+      var name = args[0].trim();
+      var optionInfo = _options[name];
+      if (optionInfo == null) {
+        debugger.console.print("unrecognized option: $name");
+        return;
+      } else {
+        dynamic getHandler = optionInfo[2];
+        var value = await getHandler(debugger, name);
+        debugger.console.print("${name} = ${value}");
+      }
+    } else if (args.length == 2) {
+      var name = args[0].trim();
+      var value = args[1].trim();
+      var optionInfo = _options[name];
+      if (optionInfo == null) {
+        debugger.console.print("unrecognized option: $name");
+        return;
+      }
+      dynamic validValues = optionInfo[0];
+      if (!validValues.contains(value)) {
+        debugger.console.print("'${value}' is not in ${validValues}");
+        return;
+      }
+      dynamic setHandler = optionInfo[1];
+      await setHandler(debugger, name, value);
+    } else {
+      debugger.console.print("set expects 0, 1, or 2 arguments");
+    }
+  }
+
+  Future<List<String>> complete(List<String> args) {
+    if (args.length < 1 || args.length > 2) {
+      return new Future.value([args.join('')]);
+    }
+    var result = <String>[];
+    if (args.length == 1) {
+      var prefix = args[0];
+      for (var option in _options.keys) {
+        if (option.startsWith(prefix)) {
+          result.add('${option} ');
+        }
+      }
+    }
+    if (args.length == 2) {
+      var name = args[0].trim();
+      var prefix = args[1];
+      var optionInfo = _options[name];
+      if (optionInfo != null) {
+        var validValues = optionInfo[0];
+        for (var value in validValues) {
+          if (value.startsWith(prefix)) {
+            result.add('${args[0]}${value} ');
+          }
+        }
+      }
+    }
+    return new Future.value(result);
+  }
+
+  String helpShort = 'Set a debugger option';
+
+  String helpLong = 'Set a debugger option.\n'
+      '\n'
+      'Known options:\n'
+      '  break-on-exception    # Should the debugger break on exceptions?\n'
+      "                        # ${_boeValues}\n"
+      '  up-is-down            # Reverse meaning of up/down commands?\n'
+      "                        # ${_boolValues}\n"
+      '\n'
+      'Syntax: set                    # Display all option settings\n'
+      '        set <option>           # Get current value for option\n'
+      '        set <option> <value>   # Set value for option';
+}
+
+class BreakCommand extends DebuggerCommand {
+  BreakCommand(Debugger debugger) : super(debugger, 'break', <Command>[]);
+
+  Future run(List<String> args) async {
+    if (args.length > 1) {
+      debugger.console.print('not implemented');
+      return;
+    }
+    var arg = (args.length == 0 ? '' : args[0]);
+    var loc = await DebuggerLocation.parse(debugger, arg);
+    if (loc.valid) {
+      if (loc.function != null) {
+        try {
+          await debugger.isolate.addBreakpointAtEntry(loc.function);
+        } on S.ServerRpcException catch (e) {
+          if (e.code == S.ServerRpcException.kCannotAddBreakpoint) {
+            debugger.console.print('Unable to set breakpoint at ${loc}');
+          } else {
+            rethrow;
+          }
+        }
+      } else {
+        assert(loc.script != null);
+        var script = loc.script;
+        await script.load();
+        if (loc.line < 1 || loc.line > script.lines.length) {
+          debugger.console.print(
+              'line number must be in range [1..${script.lines.length}]');
+          return;
+        }
+        try {
+          await debugger.isolate.addBreakpoint(script, loc.line, loc.col);
+        } on S.ServerRpcException catch (e) {
+          if (e.code == S.ServerRpcException.kCannotAddBreakpoint) {
+            debugger.console.print('Unable to set breakpoint at ${loc}');
+          } else {
+            rethrow;
+          }
+        }
+      }
+    } else {
+      debugger.console.print(loc.errorMessage);
+    }
+  }
+
+  Future<List<String>> complete(List<String> args) {
+    if (args.length != 1) {
+      return new Future.value([args.join('')]);
+    }
+    // TODO - fix DebuggerLocation complete
+    return new Future.value(DebuggerLocation.complete(debugger, args[0]));
+  }
+
+  String helpShort = 'Add a breakpoint by source location or function name'
+      ' (hotkey: [F8])';
+
+  String helpLong = 'Add a breakpoint by source location or function name.\n'
+      '\n'
+      'Hotkey: [F8]\n'
+      '\n'
+      'Syntax: break                       '
+      '# Break at the current position\n'
+      '        break <line>                '
+      '# Break at a line in the current script\n'
+      '                                    '
+      '  (e.g \'break 11\')\n'
+      '        break <line>:<col>          '
+      '# Break at a line:col in the current script\n'
+      '                                    '
+      '  (e.g \'break 11:8\')\n'
+      '        break <script>:<line>       '
+      '# Break at a line:col in a specific script\n'
+      '                                    '
+      '  (e.g \'break test.dart:11\')\n'
+      '        break <script>:<line>:<col> '
+      '# Break at a line:col in a specific script\n'
+      '                                    '
+      '  (e.g \'break test.dart:11:8\')\n'
+      '        break <function>            '
+      '# Break at the named function\n'
+      '                                    '
+      '  (e.g \'break main\' or \'break Class.someFunction\')\n';
+}
+
+class ClearCommand extends DebuggerCommand {
+  ClearCommand(Debugger debugger) : super(debugger, 'clear', <Command>[]);
+
+  Future run(List<String> args) async {
+    if (args.length > 1) {
+      debugger.console.print('not implemented');
+      return;
+    }
+    var arg = (args.length == 0 ? '' : args[0]);
+    var loc = await DebuggerLocation.parse(debugger, arg);
+    if (!loc.valid) {
+      debugger.console.print(loc.errorMessage);
+      return;
+    }
+    if (loc.function != null) {
+      debugger.console.print('Ignoring breakpoint at $loc: '
+          'Clearing function breakpoints not yet implemented');
+      return;
+    }
+
+    var script = loc.script;
+    if (loc.line < 1 || loc.line > script.lines.length) {
+      debugger.console
+          .print('line number must be in range [1..${script.lines.length}]');
+      return;
+    }
+    var lineInfo = script.getLine(loc.line);
+    var bpts = lineInfo.breakpoints;
+    var foundBreakpoint = false;
+    if (bpts != null) {
+      var bptList = bpts.toList();
+      for (var bpt in bptList) {
+        if (loc.col == null ||
+            loc.col == script.tokenToCol(bpt.location.tokenPos)) {
+          foundBreakpoint = true;
+          var result = await debugger.isolate.removeBreakpoint(bpt);
+          if (result is S.DartError) {
+            debugger.console.print(
+                'Error clearing breakpoint ${bpt.number}: ${result.message}');
+          }
+        }
+      }
+    }
+    if (!foundBreakpoint) {
+      debugger.console.print('No breakpoint found at ${loc}');
+    }
+  }
+
+  Future<List<String>> complete(List<String> args) {
+    if (args.length != 1) {
+      return new Future.value([args.join('')]);
+    }
+    return new Future.value(DebuggerLocation.complete(debugger, args[0]));
+  }
+
+  String helpShort = 'Remove a breakpoint by source location or function name'
+      ' (hotkey: [F8])';
+
+  String helpLong = 'Remove a breakpoint by source location or function name.\n'
+      '\n'
+      'Hotkey: [F8]\n'
+      '\n'
+      'Syntax: clear                       '
+      '# Clear at the current position\n'
+      '        clear <line>                '
+      '# Clear at a line in the current script\n'
+      '                                    '
+      '  (e.g \'clear 11\')\n'
+      '        clear <line>:<col>          '
+      '# Clear at a line:col in the current script\n'
+      '                                    '
+      '  (e.g \'clear 11:8\')\n'
+      '        clear <script>:<line>       '
+      '# Clear at a line:col in a specific script\n'
+      '                                    '
+      '  (e.g \'clear test.dart:11\')\n'
+      '        clear <script>:<line>:<col> '
+      '# Clear at a line:col in a specific script\n'
+      '                                    '
+      '  (e.g \'clear test.dart:11:8\')\n'
+      '        clear <function>            '
+      '# Clear at the named function\n'
+      '                                    '
+      '  (e.g \'clear main\' or \'clear Class.someFunction\')\n';
+}
+
+// TODO(turnidge): Add argument completion.
+class DeleteCommand extends DebuggerCommand {
+  DeleteCommand(Debugger debugger) : super(debugger, 'delete', <Command>[]);
+
+  Future run(List<String> args) {
+    if (args.length < 1) {
+      debugger.console.print('delete expects one or more arguments');
+      return new Future.value(null);
+    }
+    List toRemove = [];
+    for (var arg in args) {
+      int id = int.parse(arg);
+      var bptToRemove = null;
+      for (var bpt in debugger.isolate.breakpoints.values) {
+        if (bpt.number == id) {
+          bptToRemove = bpt;
+          break;
+        }
+      }
+      if (bptToRemove == null) {
+        debugger.console.print("Invalid breakpoint id '${id}'");
+        return new Future.value(null);
+      }
+      toRemove.add(bptToRemove);
+    }
+    List<Future> pending = [];
+    for (var bpt in toRemove) {
+      pending.add(debugger.isolate.removeBreakpoint(bpt));
+    }
+    return Future.wait(pending);
+  }
+
+  String helpShort = 'Remove a breakpoint by breakpoint id';
+
+  String helpLong = 'Remove a breakpoint by breakpoint id.\n'
+      '\n'
+      'Syntax: delete <bp-id>\n'
+      '        delete <bp-id> <bp-id> ...\n';
+}
+
+class InfoBreakpointsCommand extends DebuggerCommand {
+  InfoBreakpointsCommand(Debugger debugger)
+      : super(debugger, 'breakpoints', <Command>[]);
+
+  Future run(List<String> args) async {
+    if (debugger.isolate.breakpoints.isEmpty) {
+      debugger.console.print('No breakpoints');
+    }
+    List bpts = debugger.isolate.breakpoints.values.toList();
+    bpts.sort((a, b) => a.number - b.number);
+    for (var bpt in bpts) {
+      var bpId = bpt.number;
+      var locString = await bpt.location.toUserString();
+      if (!bpt.resolved) {
+        debugger.console.print('Future breakpoint ${bpId} at ${locString}');
+      } else {
+        debugger.console.print('Breakpoint ${bpId} at ${locString}');
+      }
+    }
+  }
+
+  String helpShort = 'List all breakpoints';
+
+  String helpLong = 'List all breakpoints.\n'
+      '\n'
+      'Syntax: info breakpoints\n';
+}
+
+class InfoFrameCommand extends DebuggerCommand {
+  InfoFrameCommand(Debugger debugger) : super(debugger, 'frame', <Command>[]);
+
+  Future run(List<String> args) {
+    if (args.length > 0) {
+      debugger.console.print('info frame expects no arguments');
+      return new Future.value(null);
+    }
+    debugger.console.print('frame = ${debugger.currentFrame}');
+    return new Future.value(null);
+  }
+
+  String helpShort = 'Show current frame';
+
+  String helpLong = 'Show current frame.\n'
+      '\n'
+      'Syntax: info frame\n';
+}
+
+class IsolateCommand extends DebuggerCommand {
+  IsolateCommand(Debugger debugger)
+      : super(debugger, 'isolate', <Command>[
+          new IsolateListCommand(debugger),
+          new IsolateNameCommand(debugger),
+        ]) {
+    alias = 'i';
+  }
+
+  Future run(List<String> args) {
+    if (args.length != 1) {
+      debugger.console.print('isolate expects one argument');
+      return new Future.value(null);
+    }
+    var arg = args[0].trim();
+    var num = int.tryParse(arg);
+
+    var candidate;
+    for (var isolate in debugger.vm.isolates) {
+      if (num != null && num == isolate.number) {
+        candidate = isolate;
+        break;
+      } else if (arg == isolate.name) {
+        if (candidate != null) {
+          debugger.console.print("Isolate identifier '${arg}' is ambiguous: "
+              'use the isolate number instead');
+          return new Future.value(null);
+        }
+        candidate = isolate;
+      }
+    }
+    if (candidate == null) {
+      debugger.console.print("Invalid isolate identifier '${arg}'");
+    } else {
+      if (candidate == debugger.isolate) {
+        debugger.console.print(
+            "Current isolate is already ${candidate.number} '${candidate.name}'");
+      } else {
+        new AnchorElement(href: Uris.debugger(candidate)).click();
+      }
+    }
+    return new Future.value(null);
+  }
+
+  Future<List<String>> complete(List<String> args) {
+    if (args.length != 1) {
+      return new Future.value([args.join('')]);
+    }
+    var result = <String>[];
+    for (var isolate in debugger.vm.isolates) {
+      var str = isolate.number.toString();
+      if (str.startsWith(args[0])) {
+        result.add('$str ');
+      }
+    }
+    for (var isolate in debugger.vm.isolates) {
+      if (isolate.name.startsWith(args[0])) {
+        result.add('${isolate.name} ');
+      }
+    }
+    return new Future.value(result);
+  }
+
+  String helpShort = 'Switch, list, rename, or reload isolates';
+
+  String helpLong = 'Switch the current isolate.\n'
+      '\n'
+      'Syntax: isolate <number>\n'
+      '        isolate <name>\n';
+}
+
+String _isolateRunState(S.Isolate isolate) {
+  if (isolate.paused) {
+    return 'paused';
+  } else if (isolate.running) {
+    return 'running';
+  } else if (isolate.idle) {
+    return 'idle';
+  } else {
+    return 'unknown';
+  }
+}
+
+class IsolateListCommand extends DebuggerCommand {
+  IsolateListCommand(Debugger debugger) : super(debugger, 'list', <Command>[]);
+
+  Future run(List<String> args) async {
+    if (debugger.vm == null) {
+      debugger.console.print("Internal error: vm has not been set");
+      return;
+    }
+
+    // Refresh all isolates first.
+    var pending = <Future>[];
+    for (var isolate in debugger.vm.isolates) {
+      pending.add(isolate.reload());
+    }
+    await Future.wait(pending);
+
+    const maxIdLen = 10;
+    const maxRunStateLen = 7;
+    var maxNameLen = 'NAME'.length;
+    for (var isolate in debugger.vm.isolates) {
+      maxNameLen = max(maxNameLen, isolate.name.length);
+    }
+    debugger.console.print("${'ID'.padLeft(maxIdLen, ' ')} "
+        "${'ORIGIN'.padLeft(maxIdLen, ' ')} "
+        "${'NAME'.padRight(maxNameLen, ' ')} "
+        "${'STATE'.padRight(maxRunStateLen, ' ')} "
+        "CURRENT");
+    for (var isolate in debugger.vm.isolates) {
+      String current = (isolate == debugger.isolate ? '*' : '');
+      debugger.console
+          .print("${isolate.number.toString().padLeft(maxIdLen, ' ')} "
+              "${isolate.originNumber.toString().padLeft(maxIdLen, ' ')} "
+              "${isolate.name.padRight(maxNameLen, ' ')} "
+              "${_isolateRunState(isolate).padRight(maxRunStateLen, ' ')} "
+              "${current}");
+    }
+    debugger.console.newline();
+  }
+
+  String helpShort = 'List all isolates';
+
+  String helpLong = 'List all isolates.\n'
+      '\n'
+      'Syntax: isolate list\n';
+}
+
+class IsolateNameCommand extends DebuggerCommand {
+  IsolateNameCommand(Debugger debugger) : super(debugger, 'name', <Command>[]);
+
+  Future run(List<String> args) {
+    if (args.length != 1) {
+      debugger.console.print('isolate name expects one argument');
+      return new Future.value(null);
+    }
+    return debugger.isolate.setName(args[0]);
+  }
+
+  String helpShort = 'Rename the current isolate';
+
+  String helpLong = 'Rename the current isolate.\n'
+      '\n'
+      'Syntax: isolate name <name>\n';
+}
+
+class InfoCommand extends DebuggerCommand {
+  InfoCommand(Debugger debugger)
+      : super(debugger, 'info', <Command>[
+          new InfoBreakpointsCommand(debugger),
+          new InfoFrameCommand(debugger)
+        ]);
+
+  Future run(List<String> args) {
+    debugger.console.print("'info' expects a subcommand (see 'help info')");
+    return new Future.value(null);
+  }
+
+  String helpShort = 'Show information on a variety of topics';
+
+  String helpLong = 'Show information on a variety of topics.\n'
+      '\n'
+      'Syntax: info <subcommand>\n';
+}
+
+class RefreshStackCommand extends DebuggerCommand {
+  RefreshStackCommand(Debugger debugger)
+      : super(debugger, 'stack', <Command>[]);
+
+  Future run(List<String> args) {
+    return debugger.refreshStack();
+  }
+
+  String helpShort = 'Refresh isolate stack';
+
+  String helpLong = 'Refresh isolate stack.\n'
+      '\n'
+      'Syntax: refresh stack\n';
+}
+
+class RefreshCommand extends DebuggerCommand {
+  RefreshCommand(Debugger debugger)
+      : super(debugger, 'refresh', <Command>[
+          new RefreshStackCommand(debugger),
+        ]);
+
+  Future run(List<String> args) {
+    debugger.console
+        .print("'refresh' expects a subcommand (see 'help refresh')");
+    return new Future.value(null);
+  }
+
+  String helpShort = 'Refresh debugging information of various sorts';
+
+  String helpLong = 'Refresh debugging information of various sorts.\n'
+      '\n'
+      'Syntax: refresh <subcommand>\n';
+}
+
+class VmListCommand extends DebuggerCommand {
+  VmListCommand(Debugger debugger) : super(debugger, 'list', <Command>[]);
+
+  Future run(List<String> args) async {
+    if (args.length > 0) {
+      debugger.console.print('vm list expects no arguments');
+      return;
+    }
+    if (debugger.vm == null) {
+      debugger.console.print("No connected VMs");
+      return;
+    }
+    // TODO(turnidge): Right now there is only one vm listed.
+    var vmList = [debugger.vm];
+
+    var maxAddrLen = 'ADDRESS'.length;
+    var maxNameLen = 'NAME'.length;
+
+    for (var vm in vmList) {
+      maxAddrLen = max(maxAddrLen, vm.target.networkAddress.length);
+      maxNameLen = max(maxNameLen, vm.name.length);
+    }
+
+    debugger.console.print("${'ADDRESS'.padRight(maxAddrLen, ' ')} "
+        "${'NAME'.padRight(maxNameLen, ' ')} "
+        "CURRENT");
+    for (var vm in vmList) {
+      String current = (vm == debugger.vm ? '*' : '');
+      debugger.console
+          .print("${vm.target.networkAddress.padRight(maxAddrLen, ' ')} "
+              "${vm.name.padRight(maxNameLen, ' ')} "
+              "${current}");
+    }
+  }
+
+  String helpShort = 'List all connected Dart virtual machines';
+
+  String helpLong = 'List all connected Dart virtual machines..\n'
+      '\n'
+      'Syntax: vm list\n';
+}
+
+class VmNameCommand extends DebuggerCommand {
+  VmNameCommand(Debugger debugger) : super(debugger, 'name', <Command>[]);
+
+  Future run(List<String> args) async {
+    if (args.length != 1) {
+      debugger.console.print('vm name expects one argument');
+      return;
+    }
+    if (debugger.vm == null) {
+      debugger.console.print('There is no current vm');
+      return;
+    }
+    await debugger.vm.setName(args[0]);
+  }
+
+  String helpShort = 'Rename the current Dart virtual machine';
+
+  String helpLong = 'Rename the current Dart virtual machine.\n'
+      '\n'
+      'Syntax: vm name <name>\n';
+}
+
+class VmCommand extends DebuggerCommand {
+  VmCommand(Debugger debugger)
+      : super(debugger, 'vm', <Command>[
+          new VmListCommand(debugger),
+          new VmNameCommand(debugger),
+        ]);
+
+  Future run(List<String> args) async {
+    debugger.console.print("'vm' expects a subcommand (see 'help vm')");
+  }
+
+  String helpShort = 'Manage a Dart virtual machine';
+
+  String helpLong = 'Manage a Dart virtual machine.\n'
+      '\n'
+      'Syntax: vm <subcommand>\n';
+}
+
+class _ConsoleStreamPrinter {
+  ObservatoryDebugger _debugger;
+
+  _ConsoleStreamPrinter(this._debugger);
+  Level _minimumLogLevel = Level.OFF;
+  String _savedStream;
+  String _savedIsolate;
+  String _savedLine;
+  List<String> _buffer = [];
+
+  void onEvent(String streamName, S.ServiceEvent event) {
+    if (event.kind == S.ServiceEvent.kLogging) {
+      // Check if we should print this log message.
+      if (event.logRecord['level'].value < _minimumLogLevel.value) {
+        return;
+      }
+    }
+    String isolateName = event.isolate.name;
+    // If we get a line from a different isolate/stream, flush
+    // any pending output, even if it is not newline-terminated.
+    if ((_savedIsolate != null && isolateName != _savedIsolate) ||
+        (_savedStream != null && streamName != _savedStream)) {
+      flush();
+    }
+    String data;
+    bool hasNewline;
+    if (event.kind == S.ServiceEvent.kLogging) {
+      data = event.logRecord["message"].valueAsString;
+      hasNewline = true;
+    } else {
+      data = event.bytesAsString;
+      hasNewline = data.endsWith('\n');
+    }
+    if (_savedLine != null) {
+      data = _savedLine + data;
+      _savedIsolate = null;
+      _savedStream = null;
+      _savedLine = null;
+    }
+    var lines = data.split('\n').where((line) => line != '').toList();
+    if (lines.isEmpty) {
+      return;
+    }
+    int limit = (hasNewline ? lines.length : lines.length - 1);
+    for (int i = 0; i < limit; i++) {
+      _buffer.add(_format(isolateName, streamName, lines[i]));
+    }
+    // If there is no newline, we save the last line of output for next time.
+    if (!hasNewline) {
+      _savedIsolate = isolateName;
+      _savedStream = streamName;
+      _savedLine = lines[lines.length - 1];
+    }
+  }
+
+  void flush() {
+    // If there is any saved output, flush it now.
+    if (_savedLine != null) {
+      _buffer.add(_format(_savedIsolate, _savedStream, _savedLine));
+      _savedIsolate = null;
+      _savedStream = null;
+      _savedLine = null;
+    }
+    if (_buffer.isNotEmpty) {
+      _debugger.console.printStdio(_buffer);
+      _buffer.clear();
+    }
+  }
+
+  String _format(String isolateName, String streamName, String line) {
+    return '${isolateName}:${streamName}> ${line}';
+  }
+}
+
+// Tracks the state for an isolate debugging session.
+class ObservatoryDebugger extends Debugger {
+  final SettingsGroup settings = new SettingsGroup('debugger');
+  RootCommand cmd;
+  DebuggerPageElement page;
+  DebuggerConsoleElement console;
+  DebuggerInputElement input;
+  DebuggerStackElement stackElement;
+  S.ServiceMap stack;
+  final S.Isolate isolate;
+  String breakOnException = "none"; // Last known setting.
+
+  int get currentFrame => _currentFrame;
+
+  void set currentFrame(int value) {
+    if (value != null && (value < 0 || value >= stackDepth)) {
+      throw new RangeError.range(value, 0, stackDepth);
+    }
+    _currentFrame = value;
+    if (stackElement != null) {
+      stackElement.setCurrentFrame(value);
+    }
+  }
+
+  int _currentFrame = null;
+
+  bool get upIsDown => _upIsDown;
+  void set upIsDown(bool value) {
+    settings.set('up-is-down', value);
+    _upIsDown = value;
+  }
+
+  bool _upIsDown;
+
+  bool get causalAsyncStacks => _causalAsyncStacks;
+  void set causalAsyncStacks(bool value) {
+    settings.set('causal-async-stacks', value);
+    _causalAsyncStacks = value;
+  }
+
+  bool _causalAsyncStacks;
+
+  bool get awaiterStacks => _awaiterStacks;
+  void set awaiterStacks(bool value) {
+    settings.set('awaiter-stacks', value);
+    _causalAsyncStacks = value;
+  }
+
+  bool _awaiterStacks;
+
+  static const String kAwaiterStackFrames = 'awaiterFrames';
+  static const String kAsyncCausalStackFrames = 'asyncCausalFrames';
+  static const String kStackFrames = 'frames';
+
+  void upFrame(int count) {
+    if (_upIsDown) {
+      currentFrame += count;
+    } else {
+      currentFrame -= count;
+    }
+  }
+
+  void downFrame(int count) {
+    if (_upIsDown) {
+      currentFrame -= count;
+    } else {
+      currentFrame += count;
+    }
+  }
+
+  int get stackDepth {
+    if (awaiterStacks) {
+      var awaiterStackFrames = stack[kAwaiterStackFrames];
+      if (awaiterStackFrames != null) {
+        return awaiterStackFrames.length;
+      }
+    }
+    if (causalAsyncStacks) {
+      var asyncCausalStackFrames = stack[kAsyncCausalStackFrames];
+      if (asyncCausalStackFrames != null) {
+        return asyncCausalStackFrames.length;
+      }
+    }
+    return stack[kStackFrames].length;
+  }
+
+  List get stackFrames {
+    if (awaiterStacks) {
+      var awaiterStackFrames = stack[kAwaiterStackFrames];
+      if (awaiterStackFrames != null) {
+        return awaiterStackFrames;
+      }
+    }
+    if (causalAsyncStacks) {
+      var asyncCausalStackFrames = stack[kAsyncCausalStackFrames];
+      if (asyncCausalStackFrames != null) {
+        return asyncCausalStackFrames;
+      }
+    }
+    return stack[kStackFrames] ?? [];
+  }
+
+  static final _history = [''];
+
+  ObservatoryDebugger(this.isolate) {
+    _loadSettings();
+    cmd = new RootCommand([
+      new AsyncNextCommand(this),
+      new BreakCommand(this),
+      new ClearCommand(this),
+      new ClsCommand(this),
+      new ContinueCommand(this),
+      new DeleteCommand(this),
+      new DownCommand(this),
+      new FinishCommand(this),
+      new FrameCommand(this),
+      new HelpCommand(this),
+      new InfoCommand(this),
+      new IsolateCommand(this),
+      new LogCommand(this),
+      new PauseCommand(this),
+      new PrintCommand(this),
+      new ReloadCommand(this, new R.IsolateRepository(this.isolate.vm)),
+      new RefreshCommand(this),
+      new RewindCommand(this),
+      new SetCommand(this),
+      new SmartNextCommand(this),
+      new StepCommand(this),
+      new SyncNextCommand(this),
+      new UpCommand(this),
+      new VmCommand(this),
+    ], _history);
+    _consolePrinter = new _ConsoleStreamPrinter(this);
+  }
+
+  void _loadSettings() {
+    _upIsDown = settings.get('up-is-down');
+    _causalAsyncStacks = settings.get('causal-async-stacks') ?? true;
+    _awaiterStacks = settings.get('awaiter-stacks') ?? true;
+  }
+
+  S.VM get vm => page.app.vm;
+
+  void init() {
+    console.printBold('Debugging isolate isolate ${isolate.number} '
+        '\'${isolate.name}\' ');
+    console.printBold('Type \'h\' for help');
+    // Wait a bit and if polymer still hasn't set up the isolate,
+    // report this to the user.
+    new Timer(const Duration(seconds: 1), () {
+      if (isolate == null) {
+        reportStatus();
+      }
+    });
+
+    if ((breakOnException != isolate.exceptionsPauseInfo) &&
+        (isolate.exceptionsPauseInfo != null)) {
+      breakOnException = isolate.exceptionsPauseInfo;
+    }
+
+    isolate.reload().then((serviceObject) {
+      S.Isolate response = serviceObject;
+      if (response.isSentinel) {
+        // The isolate has gone away.  The IsolateExit event will
+        // clear the isolate for the debugger page.
+        return;
+      }
+      // TODO(turnidge): Currently the debugger relies on all libs
+      // being loaded.  Fix this.
+      var pending = <Future>[];
+      for (var lib in response.libraries) {
+        if (!lib.loaded) {
+          pending.add(lib.load());
+        }
+      }
+      Future.wait(pending).then((_) {
+        refreshStack();
+      }).catchError((e) {
+        print("UNEXPECTED ERROR $e");
+        reportStatus();
+      });
+    });
+  }
+
+  Future refreshStack() async {
+    try {
+      if (isolate != null) {
+        await _refreshStack(isolate.pauseEvent);
+      }
+      flushStdio();
+      reportStatus();
+    } catch (e, st) {
+      console.printRed("Unexpected error in refreshStack: $e\n$st");
+    }
+  }
+
+  bool isolatePaused() {
+    // TODO(turnidge): Stop relying on the isolate to track the last
+    // pause event.  Since we listen to events directly in the
+    // debugger, this could introduce a race.
+    return isolate.status == M.IsolateStatus.paused;
+  }
+
+  void warnOutOfDate() {
+    // Wait a bit, then tell the user that the stack may be out of date.
+    new Timer(const Duration(seconds: 2), () {
+      if (!isolatePaused()) {
+        stackElement.isSampled = true;
+      }
+    });
+  }
+
+  Future<S.ServiceMap> _refreshStack(M.DebugEvent pauseEvent) {
+    return isolate.getStack().then((result) {
+      if (result.isSentinel) {
+        // The isolate has gone away.  The IsolateExit event will
+        // clear the isolate for the debugger page.
+        return;
+      }
+      stack = result;
+      stackElement.updateStack(stack, pauseEvent);
+      if (stack['frames'].length > 0) {
+        currentFrame = 0;
+      } else {
+        currentFrame = null;
+      }
+      input.focus();
+    });
+  }
+
+  void reportStatus() {
+    flushStdio();
+    if (isolate == null) {
+      console.print('No current isolate');
+    } else if (isolate.idle) {
+      console.print('Isolate is idle');
+    } else if (isolate.running) {
+      console.print("Isolate is running (type 'pause' to interrupt)");
+    } else if (isolate.pauseEvent != null) {
+      _reportPause(isolate.pauseEvent);
+    } else {
+      console.print('Isolate is in unknown state');
+    }
+    warnOutOfDate();
+  }
+
+  void _reportIsolateError(S.Isolate isolate, M.DebugEvent event) {
+    if (isolate == null) {
+      return;
+    }
+    S.DartError error = isolate.error;
+    if (error == null) {
+      return;
+    }
+    console.newline();
+    if (event is M.PauseExceptionEvent) {
+      console.printBold('Isolate will exit due to an unhandled exception:');
+    } else {
+      console.printBold('Isolate has exited due to an unhandled exception:');
+    }
+    console.print(error.message);
+    console.newline();
+    if (event is M.PauseExceptionEvent &&
+        (error.exception.isStackOverflowError ||
+            error.exception.isOutOfMemoryError)) {
+      console.printBold(
+          'When an unhandled stack overflow or OOM exception occurs, the VM '
+          'has run out of memory and cannot keep the stack alive while '
+          'paused.');
+    } else {
+      console.printBold("Type 'set break-on-exception Unhandled' to pause the"
+          " isolate when an unhandled exception occurs.");
+      console.printBold("You can make this the default by running with "
+          "--pause-isolates-on-unhandled-exceptions");
+    }
+  }
+
+  void _reportPause(M.DebugEvent event) {
+    if (event is M.NoneEvent) {
+      console.print("Paused until embedder makes the isolate runnable.");
+    } else if (event is M.PauseStartEvent) {
+      console.print("Paused at isolate start "
+          "(type 'continue' [F7] or 'step' [F10] to start the isolate')");
+    } else if (event is M.PauseExitEvent) {
+      console.print("Paused at isolate exit "
+          "(type 'continue' or [F7] to exit the isolate')");
+      _reportIsolateError(isolate, event);
+    } else if (event is M.PauseExceptionEvent) {
+      console.print("Paused at an unhandled exception "
+          "(type 'continue' or [F7] to exit the isolate')");
+      _reportIsolateError(isolate, event);
+    } else if (stack['frames'].length > 0) {
+      S.Frame frame = stack['frames'][0];
+      var script = frame.location.script;
+      script.load().then((_) {
+        var line = script.tokenToLine(frame.location.tokenPos);
+        var col = script.tokenToCol(frame.location.tokenPos);
+        if ((event is M.PauseBreakpointEvent) && (event.breakpoint != null)) {
+          var bpId = event.breakpoint.number;
+          console.print('Paused at breakpoint ${bpId} at '
+              '${script.name}:${line}:${col}');
+        } else if ((event is M.PauseExceptionEvent) &&
+            (event.exception != null)) {
+          console.print('Paused due to exception at '
+              '${script.name}:${line}:${col}');
+          // This seems to be missing if we are paused-at-exception after
+          // paused-at-isolate-exit. Maybe we shutdown part of the debugger too
+          // soon?
+          console.printRef(isolate, event.exception, objects);
+        } else {
+          console.print('Paused at ${script.name}:${line}:${col}');
+        }
+      });
+    } else {
+      console.print("Paused in message loop (type 'continue' or [F7] "
+          "to resume processing messages)");
+    }
+  }
+
+  Future _reportBreakpointEvent(S.ServiceEvent event) async {
+    var bpt = event.breakpoint;
+    var verb = null;
+    switch (event.kind) {
+      case S.ServiceEvent.kBreakpointAdded:
+        verb = 'added';
+        break;
+      case S.ServiceEvent.kBreakpointResolved:
+        verb = 'resolved';
+        break;
+      case S.ServiceEvent.kBreakpointRemoved:
+        verb = 'removed';
+        break;
+      default:
+        break;
+    }
+    var script = bpt.location.script;
+    await script.load();
+
+    var bpId = bpt.number;
+    var locString = await bpt.location.toUserString();
+    if (bpt.resolved) {
+      console.print('Breakpoint ${bpId} ${verb} at ${locString}');
+    } else {
+      console.print('Future breakpoint ${bpId} ${verb} at ${locString}');
+    }
+  }
+
+  void onEvent(S.ServiceEvent event) {
+    switch (event.kind) {
+      case S.ServiceEvent.kVMUpdate:
+        S.VM vm = event.owner;
+        console.print("VM ${vm.displayName} renamed to '${vm.name}'");
+        break;
+
+      case S.ServiceEvent.kIsolateStart:
+        {
+          S.Isolate iso = event.owner;
+          console.print("Isolate ${iso.number} '${iso.name}' has been created");
+        }
+        break;
+
+      case S.ServiceEvent.kIsolateExit:
+        {
+          S.Isolate iso = event.owner;
+          if (iso == isolate) {
+            console.print("The current isolate ${iso.number} '${iso.name}' "
+                "has exited");
+            var isolates = vm.isolates;
+            if (isolates.length > 0) {
+              var newIsolate = isolates.first;
+              new AnchorElement(href: Uris.debugger(newIsolate)).click();
+            } else {
+              new AnchorElement(href: Uris.vm()).click();
+            }
+          } else {
+            console.print("Isolate ${iso.number} '${iso.name}' has exited");
+          }
+        }
+        break;
+
+      case S.ServiceEvent.kDebuggerSettingsUpdate:
+        if (breakOnException != event.exceptions) {
+          breakOnException = event.exceptions;
+          console.print("Now pausing for exceptions: $breakOnException");
+        }
+        break;
+
+      case S.ServiceEvent.kIsolateUpdate:
+        S.Isolate iso = event.owner;
+        console.print("Isolate ${iso.number} renamed to '${iso.name}'");
+        break;
+
+      case S.ServiceEvent.kIsolateReload:
+        var reloadError = event.reloadError;
+        if (reloadError != null) {
+          console.print('Isolate reload failed: ${event.reloadError}');
+        } else {
+          console.print('Isolate reloaded.');
+        }
+        break;
+
+      case S.ServiceEvent.kPauseStart:
+      case S.ServiceEvent.kPauseExit:
+      case S.ServiceEvent.kPauseBreakpoint:
+      case S.ServiceEvent.kPauseInterrupted:
+      case S.ServiceEvent.kPauseException:
+        if (event.owner == isolate) {
+          var e = createEventFromServiceEvent(event);
+          _refreshStack(e).then((_) async {
+            flushStdio();
+            if (isolate != null) {
+              await isolate.reload();
+            }
+            _reportPause(e);
+          });
+        }
+        break;
+
+      case S.ServiceEvent.kResume:
+        if (event.owner == isolate) {
+          flushStdio();
+          console.print('Continuing...');
+        }
+        break;
+
+      case S.ServiceEvent.kBreakpointAdded:
+      case S.ServiceEvent.kBreakpointResolved:
+      case S.ServiceEvent.kBreakpointRemoved:
+        if (event.owner == isolate) {
+          _reportBreakpointEvent(event);
+        }
+        break;
+
+      case S.ServiceEvent.kIsolateRunnable:
+      case S.ServiceEvent.kHeapSnapshot:
+      case S.ServiceEvent.kGC:
+      case S.ServiceEvent.kInspect:
+        // Ignore.
+        break;
+
+      case S.ServiceEvent.kLogging:
+        _consolePrinter.onEvent(event.logRecord['level'].name, event);
+        break;
+
+      default:
+        console.print('Unrecognized event: $event');
+        break;
+    }
+  }
+
+  _ConsoleStreamPrinter _consolePrinter;
+
+  void flushStdio() {
+    _consolePrinter.flush();
+  }
+
+  void onStdout(S.ServiceEvent event) {
+    _consolePrinter.onEvent('stdout', event);
+  }
+
+  void onStderr(S.ServiceEvent event) {
+    _consolePrinter.onEvent('stderr', event);
+  }
+
+  static String _commonPrefix(String a, String b) {
+    int pos = 0;
+    while (pos < a.length && pos < b.length) {
+      if (a.codeUnitAt(pos) != b.codeUnitAt(pos)) {
+        break;
+      }
+      pos++;
+    }
+    return a.substring(0, pos);
+  }
+
+  static String _foldCompletions(List<String> values) {
+    if (values.length == 0) {
+      return '';
+    }
+    var prefix = values[0];
+    for (int i = 1; i < values.length; i++) {
+      prefix = _commonPrefix(prefix, values[i]);
+    }
+    return prefix;
+  }
+
+  Future<String> complete(String line) {
+    return cmd.completeCommand(line).then((completions) {
+      if (completions.length == 0) {
+        // No completions.  Leave the line alone.
+        return line;
+      } else if (completions.length == 1) {
+        // Unambiguous completion.
+        return completions[0];
+      } else {
+        // Ambiguous completion.
+        completions = completions.map((s) => s.trimRight()).toList();
+        console.printBold(completions.toString());
+        return _foldCompletions(completions);
+      }
+    });
+  }
+
+  // TODO(turnidge): Implement real command line history.
+  String lastCommand;
+
+  Future run(String command) {
+    if (command == '' && lastCommand != null) {
+      command = lastCommand;
+    }
+    console.printBold('\$ $command');
+    return cmd.runCommand(command).then((_) {
+      lastCommand = command;
+    }).catchError((e, s) {
+      console.printRed('Unable to execute command because the connection '
+          'to the VM has been closed');
+    }, test: (e) => e is S.NetworkRpcException).catchError((e, s) {
+      console.printRed(e.toString());
+    }, test: (e) => e is CommandException).catchError((e, s) {
+      if (s != null) {
+        console.printRed('Internal error: $e\n$s');
+      } else {
+        console.printRed('Internal error: $e\n');
+      }
+    });
+  }
+
+  String historyPrev(String command) {
+    return cmd.historyPrev(command);
+  }
+
+  String historyNext(String command) {
+    return cmd.historyNext(command);
+  }
+
+  Future pause() {
+    if (!isolatePaused()) {
+      return isolate.pause();
+    } else {
+      console.print('The program is already paused');
+      return new Future.value(null);
+    }
+  }
+
+  Future resume() {
+    if (isolatePaused()) {
+      return isolate.resume().then((_) {
+        warnOutOfDate();
+      });
+    } else {
+      console.print('The program must be paused');
+      return new Future.value(null);
+    }
+  }
+
+  Future toggleBreakpoint() async {
+    var loc = await DebuggerLocation.parse(this, '');
+    var script = loc.script;
+    var line = loc.line;
+    if (script != null && line != null) {
+      var bpts = script.getLine(line).breakpoints;
+      if (bpts == null || bpts.isEmpty) {
+        // Set a new breakpoint.
+        // TODO(turnidge): Set this breakpoint at current column.
+        await isolate.addBreakpoint(script, line);
+      } else {
+        // TODO(turnidge): Clear this breakpoint at current column.
+        var pending = <Future>[];
+        for (var bpt in bpts) {
+          pending.add(isolate.removeBreakpoint(bpt));
+        }
+        await Future.wait(pending);
+      }
+    }
+    return new Future.value(null);
+  }
+
+  Future smartNext() async {
+    if (isolatePaused()) {
+      M.AsyncSuspensionEvent event = isolate.pauseEvent;
+      if (event.atAsyncSuspension) {
+        return asyncNext();
+      } else {
+        return syncNext();
+      }
+    } else {
+      console.print('The program is already running');
+    }
+  }
+
+  Future asyncNext() async {
+    if (isolatePaused()) {
+      M.AsyncSuspensionEvent event = isolate.pauseEvent;
+      if (!event.atAsyncSuspension) {
+        console.print("No async continuation at this location");
+      } else {
+        return isolate.stepOverAsyncSuspension();
+      }
+    } else {
+      console.print('The program is already running');
+    }
+  }
+
+  Future syncNext() async {
+    if (isolatePaused()) {
+      var event = isolate.pauseEvent;
+      if (event is M.PauseStartEvent) {
+        console
+            .print("Type 'continue' [F7] or 'step' [F10] to start the isolate");
+        return null;
+      }
+      if (event is M.PauseExitEvent) {
+        console.print("Type 'continue' [F7] to exit the isolate");
+        return null;
+      }
+      return isolate.stepOver();
+    } else {
+      console.print('The program is already running');
+      return null;
+    }
+  }
+
+  Future step() {
+    if (isolatePaused()) {
+      var event = isolate.pauseEvent;
+      if (event is M.PauseExitEvent) {
+        console.print("Type 'continue' [F7] to exit the isolate");
+        return new Future.value(null);
+      }
+      return isolate.stepInto();
+    } else {
+      console.print('The program is already running');
+      return new Future.value(null);
+    }
+  }
+
+  Future rewind(int count) {
+    if (isolatePaused()) {
+      var event = isolate.pauseEvent;
+      if (event is M.PauseExitEvent) {
+        console.print("Type 'continue' [F7] to exit the isolate");
+        return new Future.value(null);
+      }
+      return isolate.rewind(count);
+    } else {
+      console.print('The program must be paused');
+      return new Future.value(null);
+    }
+  }
+}
+
+class DebuggerPageElement extends CustomElement implements Renderable {
+  S.Isolate _isolate;
+  ObservatoryDebugger _debugger;
+  M.ObjectRepository _objects;
+  M.ScriptRepository _scripts;
+  M.EventRepository _events;
+
+  factory DebuggerPageElement(S.Isolate isolate, M.ObjectRepository objects,
+      M.ScriptRepository scripts, M.EventRepository events) {
+    assert(isolate != null);
+    assert(objects != null);
+    assert(scripts != null);
+    assert(events != null);
+    final DebuggerPageElement e = new DebuggerPageElement.created();
+    final debugger = new ObservatoryDebugger(isolate);
+    debugger.page = e;
+    debugger.objects = objects;
+    e._isolate = isolate;
+    e._debugger = debugger;
+    e._objects = objects;
+    e._scripts = scripts;
+    e._events = events;
+    return e;
+  }
+
+  DebuggerPageElement.created() : super.created('debugger-page');
+
+  Future<StreamSubscription> _vmSubscriptionFuture;
+  Future<StreamSubscription> _isolateSubscriptionFuture;
+  Future<StreamSubscription> _debugSubscriptionFuture;
+  Future<StreamSubscription> _stdoutSubscriptionFuture;
+  Future<StreamSubscription> _stderrSubscriptionFuture;
+  Future<StreamSubscription> _logSubscriptionFuture;
+
+  ObservatoryApplication get app => ObservatoryApplication.app;
+
+  Timer _timer;
+
+  static final consoleElement = new DebuggerConsoleElement();
+
+  @override
+  void attached() {
+    super.attached();
+
+    final stackDiv = new DivElement()..classes = ['stack'];
+    final stackElement = new DebuggerStackElement(
+        _isolate, _debugger, stackDiv, _objects, _scripts, _events);
+    stackDiv.children = <Element>[stackElement.element];
+    final consoleDiv = new DivElement()
+      ..classes = ['console']
+      ..children = <Element>[consoleElement.element];
+    final commandElement = new DebuggerInputElement(_isolate, _debugger);
+    final commandDiv = new DivElement()
+      ..classes = ['commandline']
+      ..children = <Element>[commandElement.element];
+
+    children = <Element>[
+      navBar(<Element>[
+        new NavTopMenuElement(queue: app.queue).element,
+        new NavVMMenuElement(app.vm, app.events, queue: app.queue).element,
+        new NavIsolateMenuElement(_isolate, app.events, queue: app.queue)
+            .element,
+        navMenu('debugger'),
+        new NavNotifyElement(app.notifications,
+                notifyOnPause: false, queue: app.queue)
+            .element
+      ]),
+      new DivElement()
+        ..classes = ['variable']
+        ..children = <Element>[
+          stackDiv,
+          new DivElement()
+            ..children = <Element>[
+              new HRElement()..classes = ['splitter']
+            ],
+          consoleDiv,
+        ],
+      commandDiv
+    ];
+
+    DebuggerConsoleElement._scrollToBottom(consoleDiv);
+
+    // Wire the debugger object to the stack, console, and command line.
+    _debugger.stackElement = stackElement;
+    _debugger.console = consoleElement;
+    _debugger.input = commandElement;
+    _debugger.input._debugger = _debugger;
+    _debugger.init();
+
+    _vmSubscriptionFuture =
+        app.vm.listenEventStream(S.VM.kVMStream, _debugger.onEvent);
+    _isolateSubscriptionFuture =
+        app.vm.listenEventStream(S.VM.kIsolateStream, _debugger.onEvent);
+    _debugSubscriptionFuture =
+        app.vm.listenEventStream(S.VM.kDebugStream, _debugger.onEvent);
+    _stdoutSubscriptionFuture =
+        app.vm.listenEventStream(S.VM.kStdoutStream, _debugger.onStdout);
+    if (_stdoutSubscriptionFuture != null) {
+      // TODO(turnidge): How do we want to handle this in general?
+      _stdoutSubscriptionFuture.catchError((e, st) {
+        Logger.root.info('Failed to subscribe to stdout: $e\n$st\n');
+        _stdoutSubscriptionFuture = null;
+      });
+    }
+    _stderrSubscriptionFuture =
+        app.vm.listenEventStream(S.VM.kStderrStream, _debugger.onStderr);
+    if (_stderrSubscriptionFuture != null) {
+      // TODO(turnidge): How do we want to handle this in general?
+      _stderrSubscriptionFuture.catchError((e, st) {
+        Logger.root.info('Failed to subscribe to stderr: $e\n$st\n');
+        _stderrSubscriptionFuture = null;
+      });
+    }
+    _logSubscriptionFuture =
+        app.vm.listenEventStream(S.Isolate.kLoggingStream, _debugger.onEvent);
+    // Turn on the periodic poll timer for this page.
+    _timer = new Timer.periodic(const Duration(milliseconds: 100), (_) {
+      _debugger.flushStdio();
+    });
+
+    onClick.listen((event) {
+      // Random clicks should focus on the text box.  If the user selects
+      // a range, don't interfere.
+      var selection = window.getSelection();
+      if (selection == null ||
+          (selection.type != 'Range' && selection.type != 'text')) {
+        _debugger.input.focus();
+      }
+    });
+  }
+
+  @override
+  void render() {
+    /* nothing to do */
+  }
+
+  @override
+  void detached() {
+    _timer.cancel();
+    children = const [];
+    S.cancelFutureSubscription(_vmSubscriptionFuture);
+    _vmSubscriptionFuture = null;
+    S.cancelFutureSubscription(_isolateSubscriptionFuture);
+    _isolateSubscriptionFuture = null;
+    S.cancelFutureSubscription(_debugSubscriptionFuture);
+    _debugSubscriptionFuture = null;
+    S.cancelFutureSubscription(_stdoutSubscriptionFuture);
+    _stdoutSubscriptionFuture = null;
+    S.cancelFutureSubscription(_stderrSubscriptionFuture);
+    _stderrSubscriptionFuture = null;
+    S.cancelFutureSubscription(_logSubscriptionFuture);
+    _logSubscriptionFuture = null;
+    super.detached();
+  }
+}
+
+class DebuggerStackElement extends CustomElement implements Renderable {
+  S.Isolate _isolate;
+  M.ObjectRepository _objects;
+  M.ScriptRepository _scripts;
+  M.EventRepository _events;
+  Element _scroller;
+  DivElement _isSampled;
+  bool get isSampled => !_isSampled.classes.contains('hidden');
+  set isSampled(bool value) {
+    if (value != isSampled) {
+      _isSampled.classes.toggle('hidden');
+    }
+  }
+
+  DivElement _hasStack;
+  bool get hasStack => _hasStack.classes.contains('hidden');
+  set hasStack(bool value) {
+    if (value != hasStack) {
+      _hasStack.classes.toggle('hidden');
+    }
+  }
+
+  DivElement _hasMessages;
+  bool get hasMessages => _hasMessages.classes.contains('hidden');
+  set hasMessages(bool value) {
+    if (value != hasMessages) {
+      _hasMessages.classes.toggle('hidden');
+    }
+  }
+
+  UListElement _frameList;
+  UListElement _messageList;
+  int currentFrame;
+  ObservatoryDebugger _debugger;
+
+  factory DebuggerStackElement(
+      S.Isolate isolate,
+      ObservatoryDebugger debugger,
+      Element scroller,
+      M.ObjectRepository objects,
+      M.ScriptRepository scripts,
+      M.EventRepository events) {
+    assert(isolate != null);
+    assert(debugger != null);
+    assert(scroller != null);
+    assert(objects != null);
+    assert(scripts != null);
+    assert(events != null);
+    final DebuggerStackElement e = new DebuggerStackElement.created();
+    e._isolate = isolate;
+    e._debugger = debugger;
+    e._scroller = scroller;
+    e._objects = objects;
+    e._scripts = scripts;
+    e._events = events;
+
+    var btnPause;
+    var btnRefresh;
+    e.children = <Element>[
+      e._isSampled = new DivElement()
+        ..classes = ['sampledMessage', 'hidden']
+        ..children = <Element>[
+          new SpanElement()
+            ..text = 'The program is not paused. '
+                'The stack trace below may be out of date.',
+          new BRElement(),
+          new BRElement(),
+          btnPause = new ButtonElement()
+            ..text = '[Pause Isolate]'
+            ..onClick.listen((_) async {
+              btnPause.disabled = true;
+              try {
+                await debugger.isolate.pause();
+              } finally {
+                btnPause.disabled = false;
+              }
+            }),
+          btnRefresh = new ButtonElement()
+            ..text = '[Refresh Stack]'
+            ..onClick.listen((_) async {
+              btnRefresh.disabled = true;
+              try {
+                await debugger.refreshStack();
+              } finally {
+                btnRefresh.disabled = false;
+              }
+            }),
+          new BRElement(),
+          new BRElement(),
+          new HRElement()..classes = ['splitter']
+        ],
+      e._hasStack = new DivElement()
+        ..classes = ['noStack', 'hidden']
+        ..text = 'No stack',
+      e._frameList = new UListElement()..classes = ['list-group'],
+      new HRElement(),
+      e._hasMessages = new DivElement()
+        ..classes = ['noMessages', 'hidden']
+        ..text = 'No pending messages',
+      e._messageList = new UListElement()..classes = ['messageList']
+    ];
+    return e;
+  }
+
+  void render() {
+    /* nothing to do */
+  }
+
+  _addFrame(List frameList, S.Frame frameInfo) {
+    final frameElement = new DebuggerFrameElement(
+        _isolate, frameInfo, _scroller, _objects, _scripts, _events,
+        queue: app.queue);
+
+    if (frameInfo.index == currentFrame) {
+      frameElement.setCurrent(true);
+    } else {
+      frameElement.setCurrent(false);
+    }
+
+    var li = new LIElement();
+    li.classes.add('list-group-item');
+    li.children.insert(0, frameElement.element);
+
+    frameList.insert(0, li);
+  }
+
+  _addMessage(List messageList, S.ServiceMessage messageInfo) {
+    final messageElement = new DebuggerMessageElement(
+        _isolate, messageInfo, _objects, _scripts, _events,
+        queue: app.queue);
+
+    var li = new LIElement();
+    li.classes.add('list-group-item');
+    li.children.insert(0, messageElement.element);
+
+    messageList.add(li);
+  }
+
+  ObservatoryApplication get app => ObservatoryApplication.app;
+
+  void updateStackFrames(S.ServiceMap newStack) {
+    List frameElements = _frameList.children;
+    List newFrames;
+    if (_debugger.awaiterStacks &&
+        (newStack[ObservatoryDebugger.kAwaiterStackFrames] != null)) {
+      newFrames = newStack[ObservatoryDebugger.kAwaiterStackFrames];
+    } else if (_debugger.causalAsyncStacks &&
+        (newStack[ObservatoryDebugger.kAsyncCausalStackFrames] != null)) {
+      newFrames = newStack[ObservatoryDebugger.kAsyncCausalStackFrames];
+    } else {
+      newFrames = newStack[ObservatoryDebugger.kStackFrames];
+    }
+
+    // Remove any frames whose functions don't match, starting from
+    // bottom of stack.
+    int oldPos = frameElements.length - 1;
+    int newPos = newFrames.length - 1;
+    while (oldPos >= 0 && newPos >= 0) {
+      DebuggerFrameElement dbgFrameElement =
+          CustomElement.reverse(frameElements[oldPos].children[0]);
+      if (!dbgFrameElement.matchFrame(newFrames[newPos])) {
+        // The rest of the frame elements no longer match.  Remove them.
+        for (int i = 0; i <= oldPos; i++) {
+          // NOTE(turnidge): removeRange is missing, sadly.
+          frameElements.removeAt(0);
+        }
+        break;
+      }
+      oldPos--;
+      newPos--;
+    }
+
+    // Remove any extra frames.
+    if (frameElements.length > newFrames.length) {
+      // Remove old frames from the top of stack.
+      int removeCount = frameElements.length - newFrames.length;
+      for (int i = 0; i < removeCount; i++) {
+        frameElements.removeAt(0);
+      }
+    }
+
+    // Add any new frames.
+    int newCount = 0;
+    if (frameElements.length < newFrames.length) {
+      // Add new frames to the top of stack.
+      newCount = newFrames.length - frameElements.length;
+      for (int i = newCount - 1; i >= 0; i--) {
+        _addFrame(frameElements, newFrames[i]);
+      }
+    }
+    assert(frameElements.length == newFrames.length);
+
+    if (frameElements.isNotEmpty) {
+      for (int i = newCount; i < frameElements.length; i++) {
+        DebuggerFrameElement dbgFrameElement =
+            CustomElement.reverse(frameElements[i].children[0]);
+        dbgFrameElement.updateFrame(newFrames[i]);
+      }
+    }
+
+    hasStack = frameElements.isNotEmpty;
+  }
+
+  void updateStackMessages(S.ServiceMap newStack) {
+    List messageElements = _messageList.children;
+    List newMessages = newStack['messages'];
+
+    // Remove any extra message elements.
+    if (messageElements.length > newMessages.length) {
+      // Remove old messages from the front of the queue.
+      int removeCount = messageElements.length - newMessages.length;
+      for (int i = 0; i < removeCount; i++) {
+        messageElements.removeAt(0);
+      }
+    }
+
+    // Add any new messages to the tail of the queue.
+    int newStartingIndex = messageElements.length;
+    if (messageElements.length < newMessages.length) {
+      for (int i = newStartingIndex; i < newMessages.length; i++) {
+        _addMessage(messageElements, newMessages[i]);
+      }
+    }
+    assert(messageElements.length == newMessages.length);
+
+    if (messageElements.isNotEmpty) {
+      // Update old messages.
+      for (int i = 0; i < newStartingIndex; i++) {
+        DebuggerMessageElement e =
+            CustomElement.reverse(messageElements[i].children[0]);
+        e.updateMessage(newMessages[i]);
+      }
+    }
+
+    hasMessages = messageElements.isNotEmpty;
+  }
+
+  void updateStack(S.ServiceMap newStack, M.DebugEvent pauseEvent) {
+    updateStackFrames(newStack);
+    updateStackMessages(newStack);
+    isSampled = pauseEvent == null;
+  }
+
+  void setCurrentFrame(int value) {
+    currentFrame = value;
+    List frameElements = _frameList.children;
+    for (var frameElement in frameElements) {
+      DebuggerFrameElement dbgFrameElement =
+          CustomElement.reverse(frameElement.children[0]);
+      if (dbgFrameElement.frame.index == currentFrame) {
+        dbgFrameElement.setCurrent(true);
+      } else {
+        dbgFrameElement.setCurrent(false);
+      }
+    }
+  }
+
+  DebuggerStackElement.created() : super.created('debugger-stack');
+}
+
+class DebuggerFrameElement extends CustomElement implements Renderable {
+  RenderingScheduler<DebuggerFrameElement> _r;
+
+  Stream<RenderedEvent<DebuggerFrameElement>> get onRendered => _r.onRendered;
+
+  Element _scroller;
+  DivElement _varsDiv;
+  M.Isolate _isolate;
+  S.Frame _frame;
+  S.Frame get frame => _frame;
+  M.ObjectRepository _objects;
+  M.ScriptRepository _scripts;
+  M.EventRepository _events;
+
+  // Is this the current frame?
+  bool _current = false;
+
+  // Has this frame been pinned open?
+  bool _pinned = false;
+
+  bool _expanded = false;
+
+  void setCurrent(bool value) {
+    Future load = (_frame.function != null)
+        ? _frame.function.load()
+        : new Future.value(null);
+    load.then((func) {
+      _current = value;
+      if (_current) {
+        _expand();
+        scrollIntoView();
+      } else {
+        if (_pinned) {
+          _expand();
+        } else {
+          _unexpand();
+        }
+      }
+    });
+  }
+
+  factory DebuggerFrameElement(
+      M.Isolate isolate,
+      S.Frame frame,
+      Element scroller,
+      M.ObjectRepository objects,
+      M.ScriptRepository scripts,
+      M.EventRepository events,
+      {RenderingQueue queue}) {
+    assert(isolate != null);
+    assert(frame != null);
+    assert(scroller != null);
+    assert(objects != null);
+    assert(scripts != null);
+    assert(events != null);
+    final DebuggerFrameElement e = new DebuggerFrameElement.created();
+    e._r = new RenderingScheduler<DebuggerFrameElement>(e, queue: queue);
+    e._isolate = isolate;
+    e._frame = frame;
+    e._scroller = scroller;
+    e._objects = objects;
+    e._scripts = scripts;
+    e._events = events;
+    return e;
+  }
+
+  DebuggerFrameElement.created() : super.created('debugger-frame');
+
+  void render() {
+    if (_pinned) {
+      classes.add('shadow');
+    } else {
+      classes.remove('shadow');
+    }
+    if (_current) {
+      classes.add('current');
+    } else {
+      classes.remove('current');
+    }
+    if ((_frame.kind == M.FrameKind.asyncSuspensionMarker) ||
+        (_frame.kind == M.FrameKind.asyncCausal)) {
+      classes.add('causalFrame');
+    }
+    if (_frame.kind == M.FrameKind.asyncSuspensionMarker) {
+      final content = <Element>[
+        new SpanElement()..children = _createMarkerHeader(_frame.marker)
+      ];
+      children = content;
+      return;
+    }
+    ButtonElement expandButton;
+    final content = <Element>[
+      expandButton = new ButtonElement()
+        ..children = _createHeader()
+        ..onClick.listen((e) async {
+          if (e.target is AnchorElement) {
+            return;
+          }
+          expandButton.disabled = true;
+          await _toggleExpand();
+          expandButton.disabled = false;
+        })
+    ];
+    if (_expanded) {
+      final homeMethod = _frame.function.homeMethod;
+      String homeMethodName;
+      if ((homeMethod.dartOwner is S.Class) && homeMethod.isStatic) {
+        homeMethodName = '<class>';
+      } else if (homeMethod.dartOwner is S.Library) {
+        homeMethodName = '<library>';
+      }
+      ButtonElement collapseButton;
+      content.addAll([
+        new DivElement()
+          ..classes = ['frameDetails']
+          ..children = <Element>[
+            new DivElement()
+              ..classes = ['flex-row-wrap']
+              ..children = <Element>[
+                new DivElement()
+                  ..classes = ['flex-item-script']
+                  ..children = _frame.function?.location == null
+                      ? const []
+                      : [
+                          (new SourceInsetElement(
+                                  _isolate,
+                                  _frame.function.location,
+                                  _scripts,
+                                  _objects,
+                                  _events,
+                                  currentPos: _frame.location.tokenPos,
+                                  variables: _frame.variables,
+                                  inDebuggerContext: true,
+                                  queue: _r.queue))
+                              .element
+                        ],
+                new DivElement()
+                  ..classes = ['flex-item-vars']
+                  ..children = <Element>[
+                    _varsDiv = new DivElement()
+                      ..classes = ['memberList', 'frameVars']
+                      ..children = ([
+                        new DivElement()
+                          ..classes = ['memberItem']
+                          ..children = homeMethodName == null
+                              ? const []
+                              : [
+                                  new DivElement()
+                                    ..classes = ['memberName']
+                                    ..text = homeMethodName,
+                                  new DivElement()
+                                    ..classes = ['memberName']
+                                    ..children = <Element>[
+                                      anyRef(_isolate, homeMethod.dartOwner,
+                                          _objects,
+                                          queue: _r.queue)
+                                    ]
+                                ]
+                      ]..addAll(_frame.variables
+                          .map<Element>((v) => new DivElement()
+                            ..classes = ['memberItem']
+                            ..children = <Element>[
+                              new DivElement()
+                                ..classes = ['memberName']
+                                ..text = v.name,
+                              new DivElement()
+                                ..classes = ['memberName']
+                                ..children = <Element>[
+                                  anyRef(_isolate, v['value'], _objects,
+                                      queue: _r.queue)
+                                ]
+                            ])
+                          .toList()))
+                  ]
+              ],
+            new DivElement()
+              ..classes = ['frameContractor']
+              ..children = <Element>[
+                collapseButton = new ButtonElement()
+                  ..onClick.listen((e) async {
+                    collapseButton.disabled = true;
+                    await _toggleExpand();
+                    collapseButton.disabled = false;
+                  })
+                  ..children = <Element>[iconExpandLess.clone(true)]
+              ]
+          ]
+      ]);
+    }
+    children = content;
+  }
+
+  List<Element> _createMarkerHeader(String marker) {
+    final content = <Element>[
+      new DivElement()
+        ..classes = ['frameSummaryText']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['frameId']
+            ..text = 'Frame ${_frame.index}',
+          new SpanElement()..text = '$marker',
+        ]
+    ];
+    return [
+      new DivElement()
+        ..classes = ['frameSummary']
+        ..children = content
+    ];
+  }
+
+  List<Element> _createHeader() {
+    final content = <Element>[
+      new DivElement()
+        ..classes = ['frameSummaryText']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['frameId']
+            ..text = 'Frame ${_frame.index}',
+          new SpanElement()
+            ..children = _frame.function == null
+                ? const []
+                : [
+                    new FunctionRefElement(_isolate, _frame.function,
+                            queue: _r.queue)
+                        .element
+                  ],
+          new SpanElement()..text = ' ( ',
+          new SpanElement()
+            ..children = _frame.function?.location == null
+                ? const []
+                : [
+                    new SourceLinkElement(
+                            _isolate, _frame.function.location, _scripts,
+                            queue: _r.queue)
+                        .element
+                  ],
+          new SpanElement()..text = ' )'
+        ]
+    ];
+    if (!_expanded) {
+      content.add(new DivElement()
+        ..classes = ['frameExpander']
+        ..children = <Element>[iconExpandMore.clone(true)]);
+    }
+    return [
+      new DivElement()
+        ..classes = ['frameSummary']
+        ..children = content
+    ];
+  }
+
+  String makeExpandKey(String key) {
+    return '${_frame.function.qualifiedName}/${key}';
+  }
+
+  bool matchFrame(S.Frame newFrame) {
+    if (newFrame.kind != _frame.kind) {
+      return false;
+    }
+    if (newFrame.function == null) {
+      return frame.function == null;
+    }
+    return (newFrame.function.id == _frame.function.id &&
+        newFrame.location.script.id == frame.location.script.id);
+  }
+
+  void updateFrame(S.Frame newFrame) {
+    assert(matchFrame(newFrame));
+    _frame = newFrame;
+  }
+
+  S.Script get script => _frame.location.script;
+
+  int _varsTop(DivElement varsDiv) {
+    const minTop = 0;
+    if (varsDiv == null) {
+      return minTop;
+    }
+    final num paddingTop = document.body.contentEdge.top;
+    final Rectangle parent = varsDiv.parent.getBoundingClientRect();
+    final int varsHeight = varsDiv.clientHeight;
+    final int maxTop = (parent.height - varsHeight).toInt();
+    final int adjustedTop = (paddingTop - parent.top).toInt();
+    return max(minTop, min(maxTop, adjustedTop));
+  }
+
+  void _onScroll(event) {
+    if (!_expanded || _varsDiv == null) {
+      return;
+    }
+    String currentTop = _varsDiv.style.top;
+    int newTop = _varsTop(_varsDiv);
+    if (currentTop != newTop) {
+      _varsDiv.style.top = '${newTop}px';
+    }
+  }
+
+  void _expand() {
+    _expanded = true;
+    _subscribeToScroll();
+    _r.dirty();
+  }
+
+  void _unexpand() {
+    _expanded = false;
+    _unsubscribeToScroll();
+    _r.dirty();
+  }
+
+  StreamSubscription _scrollSubscription;
+  StreamSubscription _resizeSubscription;
+
+  void _subscribeToScroll() {
+    if (_scroller != null) {
+      if (_scrollSubscription == null) {
+        _scrollSubscription = _scroller.onScroll.listen(_onScroll);
+      }
+      if (_resizeSubscription == null) {
+        _resizeSubscription = window.onResize.listen(_onScroll);
+      }
+    }
+  }
+
+  void _unsubscribeToScroll() {
+    if (_scrollSubscription != null) {
+      _scrollSubscription.cancel();
+      _scrollSubscription = null;
+    }
+    if (_resizeSubscription != null) {
+      _resizeSubscription.cancel();
+      _resizeSubscription = null;
+    }
+  }
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+    if (_expanded) {
+      _subscribeToScroll();
+    }
+  }
+
+  void detached() {
+    _r.disable(notify: true);
+    super.detached();
+    _unsubscribeToScroll();
+  }
+
+  Future _toggleExpand() async {
+    await _frame.function.load();
+    _pinned = !_pinned;
+    if (_pinned) {
+      _expand();
+    } else {
+      _unexpand();
+    }
+  }
+}
+
+class DebuggerMessageElement extends CustomElement implements Renderable {
+  RenderingScheduler<DebuggerMessageElement> _r;
+
+  Stream<RenderedEvent<DebuggerMessageElement>> get onRendered => _r.onRendered;
+
+  S.Isolate _isolate;
+  S.ServiceMessage _message;
+  S.ServiceObject _preview;
+  M.ObjectRepository _objects;
+  M.ScriptRepository _scripts;
+  M.EventRepository _events;
+
+  // Is this the current message?
+  bool _current = false;
+
+  // Has this message been pinned open?
+  bool _pinned = false;
+
+  bool _expanded = false;
+
+  factory DebuggerMessageElement(
+      S.Isolate isolate,
+      S.ServiceMessage message,
+      M.ObjectRepository objects,
+      M.ScriptRepository scripts,
+      M.EventRepository events,
+      {RenderingQueue queue}) {
+    assert(isolate != null);
+    assert(message != null);
+    assert(objects != null);
+    assert(events != null);
+    final DebuggerMessageElement e = new DebuggerMessageElement.created();
+    e._r = new RenderingScheduler<DebuggerMessageElement>(e, queue: queue);
+    e._isolate = isolate;
+    e._message = message;
+    e._objects = objects;
+    e._scripts = scripts;
+    e._events = events;
+    return e;
+  }
+
+  DebuggerMessageElement.created() : super.created('debugger-message');
+
+  void render() {
+    if (_pinned) {
+      classes.add('shadow');
+    } else {
+      classes.remove('shadow');
+    }
+    if (_current) {
+      classes.add('current');
+    } else {
+      classes.remove('current');
+    }
+    ButtonElement expandButton;
+    final content = <Element>[
+      expandButton = new ButtonElement()
+        ..children = _createHeader()
+        ..onClick.listen((e) async {
+          if (e.target is AnchorElement) {
+            return;
+          }
+          expandButton.disabled = true;
+          await _toggleExpand();
+          expandButton.disabled = false;
+        })
+    ];
+    if (_expanded) {
+      ButtonElement collapseButton;
+      ButtonElement previewButton;
+      content.addAll([
+        new DivElement()
+          ..classes = ['messageDetails']
+          ..children = <Element>[
+            new DivElement()
+              ..classes = ['flex-row-wrap']
+              ..children = <Element>[
+                new DivElement()
+                  ..classes = ['flex-item-script']
+                  ..children = _message.handler == null
+                      ? const []
+                      : [
+                          new SourceInsetElement(
+                                  _isolate,
+                                  _message.handler.location,
+                                  _scripts,
+                                  _objects,
+                                  _events,
+                                  inDebuggerContext: true,
+                                  queue: _r.queue)
+                              .element
+                        ],
+                new DivElement()
+                  ..classes = ['flex-item-vars']
+                  ..children = <Element>[
+                    new DivElement()
+                      ..classes = ['memberItem']
+                      ..children = <Element>[
+                        new DivElement()..classes = ['memberName'],
+                        new DivElement()
+                          ..classes = ['memberValue']
+                          ..children = ([
+                            previewButton = new ButtonElement()
+                              ..text = 'preview'
+                              ..onClick.listen((_) {
+                                previewButton.disabled = true;
+                              })
+                          ]..addAll(_preview == null
+                              ? const []
+                              : [
+                                  anyRef(_isolate, _preview, _objects,
+                                      queue: _r.queue)
+                                ]))
+                      ]
+                  ]
+              ],
+            new DivElement()
+              ..classes = ['messageContractor']
+              ..children = <Element>[
+                collapseButton = new ButtonElement()
+                  ..onClick.listen((e) async {
+                    collapseButton.disabled = true;
+                    await _toggleExpand();
+                    collapseButton.disabled = false;
+                  })
+                  ..children = <Element>[iconExpandLess.clone(true)]
+              ]
+          ]
+      ]);
+    }
+    children = content;
+  }
+
+  void updateMessage(S.ServiceMessage message) {
+    assert(_message != null);
+    _message = message;
+    _r.dirty();
+  }
+
+  List<Element> _createHeader() {
+    final content = <Element>[
+      new DivElement()
+        ..classes = ['messageSummaryText']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['messageId']
+            ..text = 'message ${_message.index}',
+          new SpanElement()
+            ..children = _message.handler == null
+                ? const []
+                : [
+                    new FunctionRefElement(_isolate, _message.handler,
+                            queue: _r.queue)
+                        .element
+                  ],
+          new SpanElement()..text = ' ( ',
+          new SpanElement()
+            ..children = _message.location == null
+                ? const []
+                : [
+                    new SourceLinkElement(_isolate, _message.location, _scripts,
+                            queue: _r.queue)
+                        .element
+                  ],
+          new SpanElement()..text = ' )'
+        ]
+    ];
+    if (!_expanded) {
+      content.add(new DivElement()
+        ..classes = ['messageExpander']
+        ..children = <Element>[iconExpandMore.clone(true)]);
+    }
+    return [
+      new DivElement()
+        ..classes = ['messageSummary']
+        ..children = content
+    ];
+  }
+
+  void setCurrent(bool value) {
+    _current = value;
+    if (_current) {
+      _expanded = true;
+      scrollIntoView();
+      _r.dirty();
+    } else {
+      _expanded = _pinned;
+    }
+  }
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+  }
+
+  Future _toggleExpand() async {
+    var function = _message.handler;
+    if (function != null) {
+      await function.load();
+    }
+    _pinned = _pinned;
+    _expanded = true;
+    _r.dirty();
+  }
+
+  Future<S.ServiceObject> previewMessage(_) {
+    return _message.isolate.getObject(_message.messageObjectId).then((result) {
+      _preview = result;
+      return result;
+    });
+  }
+}
+
+class DebuggerConsoleElement extends CustomElement implements Renderable {
+  factory DebuggerConsoleElement() {
+    final DebuggerConsoleElement e = new DebuggerConsoleElement.created();
+    e.children = <Element>[new BRElement()];
+    return e;
+  }
+
+  DebuggerConsoleElement.created() : super.created('debugger-console');
+
+  /// Is [container] scrolled to the within [threshold] pixels of the bottom?
+  static bool _isScrolledToBottom(DivElement container, [int threshold = 2]) {
+    if (container == null) {
+      return false;
+    }
+    // scrollHeight -> complete height of element including scrollable area.
+    // clientHeight -> height of element on page.
+    // scrollTop -> how far is an element scrolled (from 0 to scrollHeight).
+    final distanceFromBottom =
+        container.scrollHeight - container.clientHeight - container.scrollTop;
+    const threshold = 2; // 2 pixel slop.
+    return distanceFromBottom <= threshold;
+  }
+
+  /// Scroll [container] so the bottom content is visible.
+  static _scrollToBottom(DivElement container) {
+    if (container == null) {
+      return;
+    }
+    // Adjust scroll so that the bottom of the content is visible.
+    container.scrollTop = container.scrollHeight - container.clientHeight;
+  }
+
+  void _append(HtmlElement span) {
+    bool autoScroll = _isScrolledToBottom(parent);
+    children.add(span);
+    if (autoScroll) {
+      _scrollToBottom(parent);
+    }
+  }
+
+  void print(String line, {bool newline: true}) {
+    var span = new SpanElement();
+    span.classes.add('normal');
+    span.appendText(line);
+    if (newline) {
+      span.appendText('\n');
+    }
+    _append(span);
+  }
+
+  void printBold(String line, {bool newline: true}) {
+    var span = new SpanElement();
+    span.classes.add('bold');
+    span.appendText(line);
+    if (newline) {
+      span.appendText('\n');
+    }
+    _append(span);
+  }
+
+  void printRed(String line, {bool newline: true}) {
+    var span = new SpanElement();
+    span.classes.add('red');
+    span.appendText(line);
+    if (newline) {
+      span.appendText('\n');
+    }
+    _append(span);
+  }
+
+  void printStdio(List<String> lines) {
+    bool autoScroll = _isScrolledToBottom(parent);
+    for (var line in lines) {
+      var span = new SpanElement();
+      span.classes.add('green');
+      span.appendText(line);
+      span.appendText('\n');
+      children.add(span);
+    }
+    if (autoScroll) {
+      _scrollToBottom(parent);
+    }
+  }
+
+  void printRef(S.Isolate isolate, S.Instance ref, M.ObjectRepository objects,
+      {bool newline: true}) {
+    _append(new InstanceRefElement(isolate, ref, objects, queue: app.queue)
+        .element);
+    if (newline) {
+      this.newline();
+    }
+  }
+
+  void newline() {
+    _append(new BRElement());
+  }
+
+  void clear() {
+    children.clear();
+  }
+
+  void render() {
+    /* nothing to do */
+  }
+
+  ObservatoryApplication get app => ObservatoryApplication.app;
+}
+
+class DebuggerInputElement extends CustomElement implements Renderable {
+  S.Isolate _isolate;
+  ObservatoryDebugger _debugger;
+  bool _busy = false;
+  final _modalPromptDiv = new DivElement()..classes = ['modalPrompt', 'hidden'];
+  final _textBox = new TextInputElement()
+    ..classes = ['textBox']
+    ..autofocus = true;
+  String get modalPrompt => _modalPromptDiv.text;
+  set modalPrompt(String value) {
+    if (_modalPromptDiv.text == '') {
+      _modalPromptDiv.classes.remove('hidden');
+    }
+    _modalPromptDiv.text = value;
+    if (_modalPromptDiv.text == '') {
+      _modalPromptDiv.classes.add('hidden');
+    }
+  }
+
+  String get text => _textBox.value;
+  set text(String value) => _textBox.value = value;
+  var modalCallback = null;
+
+  factory DebuggerInputElement(
+      S.Isolate isolate, ObservatoryDebugger debugger) {
+    final DebuggerInputElement e = new DebuggerInputElement.created();
+    e.children = <Element>[e._modalPromptDiv, e._textBox];
+    e._textBox.select();
+    e._textBox.onKeyDown.listen(e._onKeyDown);
+    return e;
+  }
+
+  DebuggerInputElement.created() : super.created('debugger-input');
+
+  void _onKeyDown(KeyboardEvent e) {
+    if (_busy) {
+      e.preventDefault();
+      return;
+    }
+    _busy = true;
+    if (modalCallback != null) {
+      if (e.keyCode == KeyCode.ENTER) {
+        var response = text;
+        modalCallback(response).whenComplete(() {
+          text = '';
+          _busy = false;
+        });
+      } else {
+        _busy = false;
+      }
+      return;
+    }
+    switch (e.keyCode) {
+      case KeyCode.TAB:
+        e.preventDefault();
+        int cursorPos = _textBox.selectionStart;
+        _debugger.complete(text.substring(0, cursorPos)).then((completion) {
+          text = completion + text.substring(cursorPos);
+          // TODO(turnidge): Move the cursor to the end of the
+          // completion, rather than the end of the string.
+        }).whenComplete(() {
+          _busy = false;
+        });
+        break;
+
+      case KeyCode.ENTER:
+        var command = text;
+        _debugger.run(command).whenComplete(() {
+          text = '';
+          _busy = false;
+        });
+        break;
+
+      case KeyCode.UP:
+        e.preventDefault();
+        text = _debugger.historyPrev(text);
+        _busy = false;
+        break;
+
+      case KeyCode.DOWN:
+        e.preventDefault();
+        text = _debugger.historyNext(text);
+        _busy = false;
+        break;
+
+      case KeyCode.PAGE_UP:
+        e.preventDefault();
+        try {
+          _debugger.upFrame(1);
+        } on RangeError catch (_) {
+          // Ignore.
+        }
+        _busy = false;
+        break;
+
+      case KeyCode.PAGE_DOWN:
+        e.preventDefault();
+        try {
+          _debugger.downFrame(1);
+        } on RangeError catch (_) {
+          // Ignore.
+        }
+        _busy = false;
+        break;
+
+      case KeyCode.F7:
+        e.preventDefault();
+        _debugger.resume().whenComplete(() {
+          _busy = false;
+        });
+        break;
+
+      case KeyCode.F8:
+        e.preventDefault();
+        _debugger.toggleBreakpoint().whenComplete(() {
+          _busy = false;
+        });
+        break;
+
+      case KeyCode.F9:
+        e.preventDefault();
+        _debugger.smartNext().whenComplete(() {
+          _busy = false;
+        });
+        break;
+
+      case KeyCode.F10:
+        e.preventDefault();
+        _debugger.step().whenComplete(() {
+          _busy = false;
+        });
+        break;
+
+      case KeyCode.SEMICOLON:
+        if (e.ctrlKey) {
+          e.preventDefault();
+          _debugger.console.printRed('^;');
+          _debugger.pause().whenComplete(() {
+            _busy = false;
+          });
+        } else {
+          _busy = false;
+        }
+        break;
+
+      default:
+        _busy = false;
+        break;
+    }
+  }
+
+  void enterMode(String prompt, callback) {
+    assert(modalPrompt == null);
+    modalPrompt = prompt;
+    modalCallback = callback;
+  }
+
+  void exitMode() {
+    assert(modalPrompt != null);
+    modalPrompt = null;
+    modalCallback = null;
+  }
+
+  void focus() {
+    _textBox.focus();
+  }
+
+  void render() {
+    // Nothing to do.
+  }
+}
+
+final SvgSvgElement iconExpandLess = new SvgSvgElement()
+  ..setAttribute('width', '24')
+  ..setAttribute('height', '24')
+  ..children = <Element>[
+    new PolygonElement()
+      ..setAttribute('points', '12,8 6,14 7.4,15.4 12,10.8 16.6,15.4 18,14 ')
+  ];
+
+final SvgSvgElement iconExpandMore = new SvgSvgElement()
+  ..setAttribute('width', '24')
+  ..setAttribute('height', '24')
+  ..children = <Element>[
+    new PolygonElement()
+      ..setAttribute('points', '16.6,8.6 12,13.2 7.4,8.6 6,10 12,16 18,10 ')
+  ];
+
+final SvgSvgElement iconChevronRight = new SvgSvgElement()
+  ..setAttribute('width', '24')
+  ..setAttribute('height', '24')
+  ..children = <Element>[
+    new PathElement()
+      ..setAttribute('d', 'M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z')
+  ];
+
+final SvgSvgElement iconChevronLeft = new SvgSvgElement()
+  ..setAttribute('width', '24')
+  ..setAttribute('height', '24')
+  ..children = <Element>[
+    new PathElement()
+      ..setAttribute('d', 'M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z')
+  ];
+
+final SvgSvgElement iconHorizontalThreeDot = new SvgSvgElement()
+  ..setAttribute('width', '24')
+  ..setAttribute('height', '24')
+  ..children = <Element>[
+    new PathElement()
+      ..setAttribute(
+          'd',
+          'M6 10c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 '
+              '2-2-.9-2-2-2zm12 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 '
+              '2-2-.9-2-2-2zm-6 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 '
+              '2-2-.9-2-2-2z')
+  ];
+
+final SvgSvgElement iconVerticalThreeDot = new SvgSvgElement()
+  ..setAttribute('width', '24')
+  ..setAttribute('height', '24')
+  ..children = <Element>[
+    new PathElement()
+      ..setAttribute(
+          'd',
+          'M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 '
+              '2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 '
+              '2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 '
+              '2-.9 2-2-.9-2-2-2z')
+  ];
+
+final SvgSvgElement iconInfo = new SvgSvgElement()
+  ..setAttribute('width', '24')
+  ..setAttribute('height', '24')
+  ..children = <Element>[
+    new PathElement()
+      ..setAttribute(
+          'd',
+          'M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 '
+              '10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z')
+  ];
+
+final SvgSvgElement iconInfoOutline = new SvgSvgElement()
+  ..setAttribute('width', '24')
+  ..setAttribute('height', '24')
+  ..children = <Element>[
+    new PathElement()
+      ..setAttribute(
+          'd',
+          'M11 17h2v-6h-2v6zm1-15C6.48 2 2 6.48 2 12s4.48 10 '
+              '10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 '
+              '0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zM11 '
+              '9h2V7h-2v2z')
+  ];
diff --git a/runtime/observatory_2/lib/src/elements/error_ref.dart b/runtime/observatory_2/lib/src/elements/error_ref.dart
new file mode 100644
index 0000000..47100de
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/error_ref.dart
@@ -0,0 +1,48 @@
+// Copyright (c) 2016, 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.
+
+library error_ref_element;
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/models.dart' show ErrorRef;
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+
+class ErrorRefElement extends CustomElement implements Renderable {
+  RenderingScheduler<ErrorRefElement> _r;
+
+  Stream<RenderedEvent<ErrorRefElement>> get onRendered => _r.onRendered;
+
+  ErrorRef _error;
+
+  ErrorRef get error => _error;
+
+  factory ErrorRefElement(ErrorRef error, {RenderingQueue queue}) {
+    assert(error != null);
+    ErrorRefElement e = new ErrorRefElement.created();
+    e._r = new RenderingScheduler<ErrorRefElement>(e, queue: queue);
+    e._error = error;
+    return e;
+  }
+
+  ErrorRefElement.created() : super.created('error-ref');
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    children = <Element>[];
+    _r.disable(notify: true);
+  }
+
+  void render() {
+    children = <Element>[new PreElement()..text = error.message];
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/error_view.dart b/runtime/observatory_2/lib/src/elements/error_view.dart
new file mode 100644
index 0000000..d4edfb2
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/error_view.dart
@@ -0,0 +1,87 @@
+// 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.
+
+library error_view_element;
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/nav/notify.dart';
+import 'package:observatory_2/src/elements/nav/top_menu.dart';
+import 'package:observatory_2/src/elements/view_footer.dart';
+
+class ErrorViewElement extends CustomElement implements Renderable {
+  RenderingScheduler<ErrorViewElement> _r;
+
+  Stream<RenderedEvent<ErrorViewElement>> get onRendered => _r.onRendered;
+
+  M.Error _error;
+  M.NotificationRepository _notifications;
+
+  M.Error get error => _error;
+
+  factory ErrorViewElement(
+      M.NotificationRepository notifications, M.Error error,
+      {RenderingQueue queue}) {
+    assert(error != null);
+    assert(notifications != null);
+    ErrorViewElement e = new ErrorViewElement.created();
+    e._r = new RenderingScheduler<ErrorViewElement>(e, queue: queue);
+    e._error = error;
+    e._notifications = notifications;
+    return e;
+  }
+
+  ErrorViewElement.created() : super.created('error-view');
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    children = <Element>[];
+    _r.disable(notify: true);
+  }
+
+  void render() {
+    children = <Element>[
+      navBar(<Element>[
+        new NavTopMenuElement(queue: _r.queue).element,
+        new NavNotifyElement(_notifications, queue: _r.queue).element
+      ]),
+      new DivElement()
+        ..classes = ['content-centered']
+        ..children = <Element>[
+          new HeadingElement.h1()
+            ..text = 'Error: ${_kindToString(_error.kind)}',
+          new BRElement(),
+          new DivElement()
+            ..classes = ['well']
+            ..children = <Element>[new PreElement()..text = error.message]
+        ],
+      new ViewFooterElement(queue: _r.queue).element
+    ];
+  }
+
+  static String _kindToString(M.ErrorKind kind) {
+    switch (kind) {
+      case M.ErrorKind.unhandledException:
+        return 'Unhandled Exception';
+      case M.ErrorKind.languageError:
+        return 'Language Error';
+      case M.ErrorKind.internalError:
+        return 'Internal Error';
+      case M.ErrorKind.terminationError:
+        return 'Termination Error';
+    }
+    throw new Exception('Unknown M.ErrorKind ($kind)');
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/eval_box.dart b/runtime/observatory_2/lib/src/elements/eval_box.dart
new file mode 100644
index 0000000..771ddf9
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/eval_box.dart
@@ -0,0 +1,207 @@
+// 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.
+
+library eval_box_element;
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/helpers/any_ref.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/instance_ref.dart';
+
+class EvalBoxElement extends CustomElement implements Renderable {
+  RenderingScheduler<EvalBoxElement> _r;
+
+  Stream<RenderedEvent<EvalBoxElement>> get onRendered => _r.onRendered;
+
+  M.IsolateRef _isolate;
+  M.ObjectRef _context;
+  M.ObjectRepository _objects;
+  M.EvalRepository _eval;
+  final _results = <_ExpressionDescription>[];
+  String _expression = '';
+  bool _multiline;
+  Iterable<String> _quickExpressions;
+
+  M.IsolateRef get isolate => _isolate;
+  M.ObjectRef get context => _context;
+
+  factory EvalBoxElement(M.IsolateRef isolate, M.ObjectRef context,
+      M.ObjectRepository objects, M.EvalRepository eval,
+      {bool multiline: false,
+      Iterable<String> quickExpressions: const [],
+      RenderingQueue queue}) {
+    assert(isolate != null);
+    assert(context != null);
+    assert(objects != null);
+    assert(eval != null);
+    assert(multiline != null);
+    assert(quickExpressions != null);
+    EvalBoxElement e = new EvalBoxElement.created();
+    e._r = new RenderingScheduler<EvalBoxElement>(e, queue: queue);
+    e._isolate = isolate;
+    e._context = context;
+    e._objects = objects;
+    e._eval = eval;
+    e._multiline = multiline;
+    e._quickExpressions = new List.unmodifiable(quickExpressions);
+    return e;
+  }
+
+  EvalBoxElement.created() : super.created('eval-box');
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+    _results.clear();
+  }
+
+  void render() {
+    children = <Element>[
+      new DivElement()
+        ..classes = ['quicks']
+        ..children = _quickExpressions
+            .map<Element>((q) => new ButtonElement()
+              ..text = q
+              ..onClick.listen((_) {
+                _expression = q;
+                _run();
+              }))
+            .toList(),
+      new DivElement()
+        ..classes = ['heading']
+        ..children = <Element>[
+          new FormElement()
+            ..autocomplete = 'on'
+            ..children = <Element>[
+              _multiline ? _createEvalTextArea() : _createEvalTextBox(),
+              new SpanElement()
+                ..classes = ['buttons']
+                ..children = <Element>[
+                  _createEvalButton(),
+                  _createMultilineCheckbox(),
+                  new SpanElement()..text = 'Multi-line'
+                ]
+            ]
+        ],
+      new TableElement()
+        ..children = _results.reversed
+            .map<Element>((result) => new TableRowElement()
+              ..children = <Element>[
+                new TableCellElement()
+                  ..classes = ['historyExpr']
+                  ..children = <Element>[
+                    new ButtonElement()
+                      ..text = result.expression
+                      ..onClick.listen((_) {
+                        _expression = result.expression;
+                        _r.dirty();
+                      })
+                  ],
+                new TableCellElement()
+                  ..classes = ['historyValue']
+                  ..children = <Element>[
+                    result.isPending
+                        ? (new SpanElement()..text = 'Pending...')
+                        : anyRef(_isolate, result.value, _objects,
+                            queue: _r.queue)
+                  ],
+                new TableCellElement()
+                  ..classes = ['historyDelete']
+                  ..children = <Element>[
+                    new ButtonElement()
+                      ..text = '✖ Remove'
+                      ..onClick.listen((_) {
+                        _results.remove(result);
+                        _r.dirty();
+                      })
+                  ]
+              ])
+            .toList()
+    ];
+  }
+
+  TextAreaElement _createEvalTextArea() {
+    var area = new TextAreaElement()
+      ..classes = ['textbox']
+      ..placeholder = 'evaluate an expression'
+      ..value = _expression
+      ..onKeyUp.where((e) => e.key == '\n').listen((e) {
+        e.preventDefault();
+        _run();
+      });
+    area.onInput.listen((e) {
+      _expression = area.value;
+    });
+    return area;
+  }
+
+  TextInputElement _createEvalTextBox() {
+    _expression = (_expression ?? '').split('\n')[0];
+    var textbox = new TextInputElement()
+      ..classes = ['textbox']
+      ..placeholder = 'evaluate an expression'
+      ..value = _expression
+      ..onKeyUp.where((e) => e.key == '\n').listen((e) {
+        e.preventDefault();
+        _run();
+      });
+    textbox.onInput.listen((e) {
+      _expression = textbox.value;
+    });
+    return textbox;
+  }
+
+  ButtonElement _createEvalButton() {
+    final button = new ButtonElement()
+      ..text = 'Evaluate'
+      ..onClick.listen((e) {
+        e.preventDefault();
+        _run();
+      });
+    return button;
+  }
+
+  CheckboxInputElement _createMultilineCheckbox() {
+    final checkbox = new CheckboxInputElement()..checked = _multiline;
+    checkbox.onClick.listen((e) {
+      e.preventDefault();
+      _multiline = checkbox.checked;
+      _r.dirty();
+    });
+    return checkbox;
+  }
+
+  Future _run() async {
+    if (_expression == null || _expression.isEmpty) return;
+    final expression = _expression;
+    _expression = null;
+    final result = new _ExpressionDescription.pending(expression);
+    _results.add(result);
+    _r.dirty();
+    final index = _results.indexOf(result);
+    _results[index] = new _ExpressionDescription(
+        expression, await _eval.evaluate(_isolate, _context, expression));
+    _r.dirty();
+  }
+}
+
+class _ExpressionDescription {
+  final String expression;
+  final M.ObjectRef value;
+  bool get isPending => value == null;
+
+  _ExpressionDescription(this.expression, this.value);
+  _ExpressionDescription.pending(this.expression) : value = null;
+}
diff --git a/runtime/observatory_2/lib/src/elements/field_ref.dart b/runtime/observatory_2/lib/src/elements/field_ref.dart
new file mode 100644
index 0000000..ad75c94
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/field_ref.dart
@@ -0,0 +1,90 @@
+// 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.
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/helpers/uris.dart';
+import 'package:observatory_2/src/elements/instance_ref.dart';
+
+class FieldRefElement extends CustomElement implements Renderable {
+  RenderingScheduler<FieldRefElement> _r;
+
+  Stream<RenderedEvent<FieldRefElement>> get onRendered => _r.onRendered;
+
+  M.IsolateRef _isolate;
+  M.FieldRef _field;
+  M.ObjectRepository _objects;
+  bool _expandable;
+
+  M.IsolateRef get isolate => _isolate;
+  M.FieldRef get field => _field;
+
+  factory FieldRefElement(
+      M.IsolateRef isolate, M.FieldRef field, M.ObjectRepository objects,
+      {RenderingQueue queue, bool expandable: true}) {
+    assert(isolate != null);
+    assert(field != null);
+    assert(objects != null);
+    FieldRefElement e = new FieldRefElement.created();
+    e._r = new RenderingScheduler<FieldRefElement>(e, queue: queue);
+    e._isolate = isolate;
+    e._field = field;
+    e._objects = objects;
+    e._expandable = expandable;
+    return e;
+  }
+
+  FieldRefElement.created() : super.created('field-ref');
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+  }
+
+  void render() {
+    var header = '';
+    if (_field.isStatic) {
+      if (_field.dartOwner is M.ClassRef) {
+        header += 'static ';
+      } else {
+        header += 'top-level ';
+      }
+    }
+    if (_field.isFinal) {
+      header += 'final ';
+    } else if (_field.isConst) {
+      header += 'const ';
+    } else if (_field.declaredType.name == 'dynamic') {
+      header += 'var ';
+    }
+    if (_field.declaredType.name == 'dynamic') {
+      children = <Element>[
+        new SpanElement()..text = header,
+        new AnchorElement(href: Uris.inspect(_isolate, object: _field))
+          ..text = _field.name
+      ];
+    } else {
+      children = <Element>[
+        new SpanElement()..text = header,
+        new InstanceRefElement(_isolate, _field.declaredType, _objects,
+                queue: _r.queue, expandable: _expandable)
+            .element,
+        new SpanElement()..text = ' ',
+        new AnchorElement(href: Uris.inspect(_isolate, object: _field))
+          ..text = _field.name
+      ];
+    }
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/field_view.dart b/runtime/observatory_2/lib/src/elements/field_view.dart
new file mode 100644
index 0000000..5e4979f
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/field_view.dart
@@ -0,0 +1,291 @@
+// 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.
+
+library field_view_element;
+
+import 'dart:async';
+import 'dart:html';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/class_ref.dart';
+import 'package:observatory_2/src/elements/curly_block.dart';
+import 'package:observatory_2/src/elements/helpers/any_ref.dart';
+import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
+import 'package:observatory_2/src/elements/helpers/nav_menu.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/nav/class_menu.dart';
+import 'package:observatory_2/src/elements/nav/isolate_menu.dart';
+import 'package:observatory_2/src/elements/nav/library_menu.dart';
+import 'package:observatory_2/src/elements/nav/notify.dart';
+import 'package:observatory_2/src/elements/nav/refresh.dart';
+import 'package:observatory_2/src/elements/nav/top_menu.dart';
+import 'package:observatory_2/src/elements/nav/vm_menu.dart';
+import 'package:observatory_2/src/elements/object_common.dart';
+import 'package:observatory_2/src/elements/script_inset.dart';
+import 'package:observatory_2/src/elements/source_link.dart';
+import 'package:observatory_2/src/elements/view_footer.dart';
+
+class FieldViewElement extends CustomElement implements Renderable {
+  RenderingScheduler<FieldViewElement> _r;
+
+  Stream<RenderedEvent<FieldViewElement>> get onRendered => _r.onRendered;
+
+  M.VM _vm;
+  M.IsolateRef _isolate;
+  M.EventRepository _events;
+  M.NotificationRepository _notifications;
+  M.Field _field;
+  M.LibraryRef _library;
+  M.FieldRepository _fields;
+  M.ClassRepository _classes;
+  M.RetainedSizeRepository _retainedSizes;
+  M.ReachableSizeRepository _reachableSizes;
+  M.InboundReferencesRepository _references;
+  M.RetainingPathRepository _retainingPaths;
+  M.ScriptRepository _scripts;
+  M.ObjectRepository _objects;
+
+  M.VMRef get vm => _vm;
+  M.IsolateRef get isolate => _isolate;
+  M.NotificationRepository get notifications => _notifications;
+  M.Field get field => _field;
+
+  factory FieldViewElement(
+      M.VM vm,
+      M.IsolateRef isolate,
+      M.Field field,
+      M.EventRepository events,
+      M.NotificationRepository notifications,
+      M.FieldRepository fields,
+      M.ClassRepository classes,
+      M.RetainedSizeRepository retainedSizes,
+      M.ReachableSizeRepository reachableSizes,
+      M.InboundReferencesRepository references,
+      M.RetainingPathRepository retainingPaths,
+      M.ScriptRepository scripts,
+      M.ObjectRepository objects,
+      {RenderingQueue queue}) {
+    assert(vm != null);
+    assert(isolate != null);
+    assert(events != null);
+    assert(notifications != null);
+    assert(field != null);
+    assert(fields != null);
+    assert(classes != null);
+    assert(retainedSizes != null);
+    assert(reachableSizes != null);
+    assert(references != null);
+    assert(retainingPaths != null);
+    assert(scripts != null);
+    assert(objects != null);
+    FieldViewElement e = new FieldViewElement.created();
+    e._r = new RenderingScheduler<FieldViewElement>(e, queue: queue);
+    e._vm = vm;
+    e._isolate = isolate;
+    e._events = events;
+    e._notifications = notifications;
+    e._field = field;
+    e._fields = fields;
+    e._classes = classes;
+    e._retainedSizes = retainedSizes;
+    e._reachableSizes = reachableSizes;
+    e._references = references;
+    e._retainingPaths = retainingPaths;
+    e._scripts = scripts;
+    e._objects = objects;
+    if (field.dartOwner is M.LibraryRef) {
+      e._library = field.dartOwner;
+    }
+    return e;
+  }
+
+  FieldViewElement.created() : super.created('field-view');
+
+  @override
+  attached() {
+    super.attached();
+    _r.enable();
+    _refresh();
+  }
+
+  @override
+  detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+  }
+
+  void render() {
+    var header = '';
+    if (_field.isStatic) {
+      if (_field.dartOwner is M.ClassRef) {
+        header += 'static ';
+      } else {
+        header += 'top-level ';
+      }
+    }
+    if (_field.isFinal) {
+      header += 'final ';
+    } else if (_field.isConst) {
+      header += 'const ';
+    }
+    if (_field.declaredType.name == 'dynamic') {
+      header += 'var';
+    } else {
+      header += _field.declaredType.name;
+    }
+    children = <Element>[
+      navBar(_createMenu()),
+      new DivElement()
+        ..classes = ['content-centered-big']
+        ..children = <Element>[
+          new HeadingElement.h2()..text = '$header ${field.name}',
+          new HRElement(),
+          new ObjectCommonElement(_isolate, _field, _retainedSizes,
+                  _reachableSizes, _references, _retainingPaths, _objects,
+                  queue: _r.queue)
+              .element,
+          new BRElement(),
+          new DivElement()
+            ..classes = ['memberList']
+            ..children = _createMembers(),
+          new HRElement(),
+          new DivElement()
+            ..children = _field.location == null
+                ? const []
+                : [
+                    new ScriptInsetElement(_isolate, _field.location.script,
+                            _scripts, _objects, _events,
+                            startPos: field.location.tokenPos,
+                            endPos: field.location.tokenPos,
+                            queue: _r.queue)
+                        .element
+                  ],
+          new ViewFooterElement(queue: _r.queue).element
+        ]
+    ];
+  }
+
+  List<Element> _createMenu() {
+    final menu = <Element>[
+      new NavTopMenuElement(queue: _r.queue).element,
+      new NavVMMenuElement(_vm, _events, queue: _r.queue).element,
+      new NavIsolateMenuElement(_isolate, _events, queue: _r.queue).element
+    ];
+    if (_library != null) {
+      menu.add(new NavLibraryMenuElement(_isolate, _library, queue: _r.queue)
+          .element);
+    } else if (_field.dartOwner is M.ClassRef) {
+      menu.add(
+          new NavClassMenuElement(_isolate, _field.dartOwner, queue: _r.queue)
+              .element);
+    }
+    menu.addAll(<Element>[
+      navMenu(_field.name),
+      (new NavRefreshElement(queue: _r.queue)
+            ..onRefresh.listen((e) {
+              e.element.disabled = true;
+              _refresh();
+            }))
+          .element,
+      new NavNotifyElement(_notifications, queue: _r.queue).element
+    ]);
+    return menu;
+  }
+
+  List<Element> _createMembers() {
+    final members = <Element>[
+      new DivElement()
+        ..classes = ['memberItem']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = 'owner',
+          new DivElement()
+            ..classes = ['memberName']
+            ..children = <Element>[
+              _field.dartOwner == null
+                  ? (new SpanElement()..text = '...')
+                  : anyRef(_isolate, _field.dartOwner, _objects,
+                      queue: _r.queue)
+            ]
+        ],
+      new DivElement()
+        ..classes = ['memberItem']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = 'script',
+          new DivElement()
+            ..classes = ['memberName']
+            ..children = <Element>[
+              new SourceLinkElement(_isolate, field.location, _scripts,
+                      queue: _r.queue)
+                  .element
+            ]
+        ]
+    ];
+    if (!_field.isStatic) {
+      members.add(new DivElement()
+        ..classes = ['memberItem']
+        ..title = 'The types observed for this field at runtime. '
+            'Fields that are observed to have a single type at runtime '
+            'or to never be null may allow for additional optimization.'
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = 'observed types',
+          new DivElement()
+            ..classes = ['memberName']
+            ..children = _createGuard()
+        ]);
+    }
+    if (_field.staticValue != null) {
+      members.add(new DivElement()
+        ..classes = ['memberItem']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = 'static value',
+          new DivElement()
+            ..classes = ['memberName']
+            ..children = <Element>[
+              anyRef(_isolate, _field.staticValue, _objects, queue: _r.queue)
+            ]
+        ]);
+    }
+    return members;
+  }
+
+  List<Element> _createGuard() {
+    final guard = <Element>[];
+    switch (_field.guardClassKind) {
+      case M.GuardClassKind.unknown:
+        guard.add(new SpanElement()..text = 'none');
+        break;
+      case M.GuardClassKind.dynamic:
+        guard.add(new SpanElement()..text = 'various');
+        break;
+      case M.GuardClassKind.single:
+        guard.add(
+            new ClassRefElement(_isolate, _field.guardClass, queue: _r.queue)
+                .element);
+        break;
+    }
+    guard.add(new SpanElement()
+      ..text =
+          _field.guardNullable ? '— null observed' : '— null not observed');
+    return guard;
+  }
+
+  Future _refresh() async {
+    _field = await _fields.get(_isolate, _field.id);
+    if (_field.dartOwner is M.LibraryRef) {
+      _library = _field.dartOwner;
+    } else if (_field.dartOwner is M.ClassRef) {
+      _library = (await _classes.get(_isolate, _field.dartOwner.id)).library;
+    }
+    _r.dirty();
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/flag_list.dart b/runtime/observatory_2/lib/src/elements/flag_list.dart
new file mode 100644
index 0000000..5f540fc
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/flag_list.dart
@@ -0,0 +1,145 @@
+// 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.
+
+library flag_list_element;
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
+import 'package:observatory_2/src/elements/helpers/nav_menu.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/helpers/uris.dart';
+import 'package:observatory_2/src/elements/nav/notify.dart';
+import 'package:observatory_2/src/elements/nav/refresh.dart';
+import 'package:observatory_2/src/elements/nav/top_menu.dart';
+import 'package:observatory_2/src/elements/nav/vm_menu.dart';
+import 'package:observatory_2/src/elements/view_footer.dart';
+
+class FlagListElement extends CustomElement implements Renderable {
+  RenderingScheduler<FlagListElement> _r;
+
+  Stream<RenderedEvent<FlagListElement>> get onRendered => _r.onRendered;
+
+  M.VMRef _vm;
+  M.EventRepository _events;
+  M.FlagsRepository _repository;
+  M.NotificationRepository _notifications;
+  Iterable<M.Flag> _flags;
+
+  M.VMRef get vm => _vm;
+
+  factory FlagListElement(M.VMRef vm, M.EventRepository events,
+      M.FlagsRepository repository, M.NotificationRepository notifications,
+      {RenderingQueue queue}) {
+    assert(vm != null);
+    assert(events != null);
+    assert(repository != null);
+    assert(notifications != null);
+    FlagListElement e = new FlagListElement.created();
+    e._r = new RenderingScheduler<FlagListElement>(e, queue: queue);
+    e._vm = vm;
+    e._events = events;
+    e._repository = repository;
+    e._notifications = notifications;
+    return e;
+  }
+
+  FlagListElement.created() : super.created('flag-list');
+
+  @override
+  void attached() {
+    super.attached();
+    _refresh();
+    _r.enable();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    children = <Element>[];
+    _r.disable(notify: true);
+  }
+
+  void render() {
+    final content = <Element>[];
+    if (_flags == null) {
+      content.add(new HeadingElement.h1()..text = 'Loading Flags...');
+    } else {
+      final modified = _flags.where(_isModified);
+      final unmodified = _flags.where(_isUnmodified);
+
+      if (modified.isNotEmpty) {
+        content.add(new HeadingElement.h1()..text = 'Modified Flags');
+        content.add(new BRElement());
+        content.addAll(modified.expand(_renderFlag));
+        content.add(new HRElement());
+      }
+
+      content.add(new HeadingElement.h1()..text = 'Unmodified Flags');
+      content.add(new BRElement());
+
+      if (unmodified.isEmpty) {
+        content.add(new HeadingElement.h2()..text = 'None');
+      } else {
+        content.addAll(unmodified.expand(_renderFlag));
+      }
+    }
+
+    children = <Element>[
+      navBar(<Element>[
+        new NavTopMenuElement(queue: _r.queue).element,
+        new NavVMMenuElement(_vm, _events, queue: _r.queue).element,
+        navMenu('flags', link: Uris.flags()),
+        (new NavRefreshElement(queue: _r.queue)
+              ..onRefresh.listen((e) async {
+                e.element.disabled = true;
+                try {
+                  await _refresh();
+                } finally {
+                  e.element.disabled = false;
+                }
+              }))
+            .element,
+        new NavNotifyElement(_notifications, queue: _r.queue).element
+      ]),
+      new DivElement()
+        ..classes = ['content-centered']
+        ..children = content,
+      new ViewFooterElement(queue: _r.queue).element
+    ];
+  }
+
+  Future _refresh() {
+    return _repository.list().then((flags) {
+      _flags = flags;
+      _r.dirty();
+    });
+  }
+
+  static bool _isModified(M.Flag flag) => flag.modified;
+  static bool _isUnmodified(M.Flag flag) => !flag.modified;
+
+  static List<Element> _renderFlag(M.Flag flag) {
+    return [
+      new SpanElement()
+        ..classes = ['comment']
+        ..text = '// ${flag.comment}',
+      new DivElement()
+        ..classes =
+            flag.modified ? ['flag', 'modified'] : ['flag', 'unmodified']
+        ..children = <Element>[
+          new SpanElement()
+            ..classes = ['name']
+            ..text = flag.name,
+          new SpanElement()..text = '=',
+          new SpanElement()
+            ..classes = ['value']
+            ..text = flag.valueAsString ?? 'NULL'
+        ],
+      new BRElement(),
+    ];
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/function_ref.dart b/runtime/observatory_2/lib/src/elements/function_ref.dart
new file mode 100644
index 0000000..27c270b
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/function_ref.dart
@@ -0,0 +1,95 @@
+// 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.
+
+library function_ref_element;
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/models.dart' as M
+    show
+        IsolateRef,
+        FunctionRef,
+        isSyntheticFunction,
+        ClassRef,
+        ObjectRef,
+        getFunctionFullName;
+import 'package:observatory_2/src/elements/class_ref.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/helpers/uris.dart';
+
+class FunctionRefElement extends CustomElement implements Renderable {
+  RenderingScheduler<FunctionRefElement> _r;
+
+  Stream<RenderedEvent<FunctionRefElement>> get onRendered => _r.onRendered;
+
+  M.IsolateRef _isolate;
+  M.FunctionRef _function;
+  bool _qualified;
+
+  M.IsolateRef get isolate => _isolate;
+  M.FunctionRef get function => _function;
+  bool get qualified => _qualified;
+
+  factory FunctionRefElement(M.IsolateRef isolate, M.FunctionRef function,
+      {bool qualified: true, RenderingQueue queue}) {
+    assert(function != null);
+    assert(qualified != null);
+    FunctionRefElement e = new FunctionRefElement.created();
+    e._r = new RenderingScheduler<FunctionRefElement>(e, queue: queue);
+    e._isolate = isolate;
+    e._function = function;
+    e._qualified = qualified;
+    return e;
+  }
+
+  FunctionRefElement.created() : super.created('function-ref');
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    children = <Element>[];
+    title = '';
+    _r.disable(notify: true);
+  }
+
+  void render() {
+    var content = <Element>[
+      new AnchorElement(
+          href: (M.isSyntheticFunction(_function.kind) || (_isolate == null))
+              ? null
+              : Uris.inspect(_isolate, object: _function))
+        ..text = _function.name
+    ];
+    if (qualified) {
+      M.ObjectRef owner = _function.dartOwner;
+      while (owner is M.FunctionRef) {
+        M.FunctionRef function = (owner as M.FunctionRef);
+        content.addAll([
+          new SpanElement()..text = '.',
+          new AnchorElement(
+              href: (M.isSyntheticFunction(function.kind) || (_isolate == null))
+                  ? null
+                  : Uris.inspect(_isolate, object: function))
+            ..text = function.name
+        ]);
+        owner = function.dartOwner;
+      }
+      if (owner is M.ClassRef) {
+        content.addAll([
+          new SpanElement()..text = '.',
+          new ClassRefElement(_isolate, owner, queue: _r.queue).element
+        ]);
+      }
+    }
+    children = content.reversed.toList(growable: false);
+    title = M.getFunctionFullName(_function);
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/function_view.dart b/runtime/observatory_2/lib/src/elements/function_view.dart
new file mode 100644
index 0000000..7077bc3
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/function_view.dart
@@ -0,0 +1,447 @@
+// 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.
+
+library function_view_element;
+
+import 'dart:async';
+import 'dart:html';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/class_ref.dart';
+import 'package:observatory_2/src/elements/code_ref.dart';
+import 'package:observatory_2/src/elements/curly_block.dart';
+import 'package:observatory_2/src/elements/field_ref.dart';
+import 'package:observatory_2/src/elements/instance_ref.dart';
+import 'package:observatory_2/src/elements/helpers/any_ref.dart';
+import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
+import 'package:observatory_2/src/elements/helpers/nav_menu.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/nav/class_menu.dart';
+import 'package:observatory_2/src/elements/nav/isolate_menu.dart';
+import 'package:observatory_2/src/elements/nav/library_menu.dart';
+import 'package:observatory_2/src/elements/nav/notify.dart';
+import 'package:observatory_2/src/elements/nav/refresh.dart';
+import 'package:observatory_2/src/elements/nav/top_menu.dart';
+import 'package:observatory_2/src/elements/nav/vm_menu.dart';
+import 'package:observatory_2/src/elements/object_common.dart';
+import 'package:observatory_2/src/elements/source_inset.dart';
+import 'package:observatory_2/src/elements/source_link.dart';
+import 'package:observatory_2/src/elements/view_footer.dart';
+
+class FunctionViewElement extends CustomElement implements Renderable {
+  RenderingScheduler<FunctionViewElement> _r;
+
+  Stream<RenderedEvent<FunctionViewElement>> get onRendered => _r.onRendered;
+
+  M.VM _vm;
+  M.IsolateRef _isolate;
+  M.EventRepository _events;
+  M.NotificationRepository _notifications;
+  M.ServiceFunction _function;
+  M.LibraryRef _library;
+  M.FunctionRepository _functions;
+  M.ClassRepository _classes;
+  M.RetainedSizeRepository _retainedSizes;
+  M.ReachableSizeRepository _reachableSizes;
+  M.InboundReferencesRepository _references;
+  M.RetainingPathRepository _retainingPaths;
+  M.ScriptRepository _scripts;
+  M.ObjectRepository _objects;
+
+  M.VMRef get vm => _vm;
+  M.IsolateRef get isolate => _isolate;
+  M.NotificationRepository get notifications => _notifications;
+  M.ServiceFunction get function => _function;
+
+  factory FunctionViewElement(
+      M.VM vm,
+      M.IsolateRef isolate,
+      M.ServiceFunction function,
+      M.EventRepository events,
+      M.NotificationRepository notifications,
+      M.FunctionRepository functions,
+      M.ClassRepository classes,
+      M.RetainedSizeRepository retainedSizes,
+      M.ReachableSizeRepository reachableSizes,
+      M.InboundReferencesRepository references,
+      M.RetainingPathRepository retainingPaths,
+      M.ScriptRepository scripts,
+      M.ObjectRepository objects,
+      {RenderingQueue queue}) {
+    assert(vm != null);
+    assert(isolate != null);
+    assert(events != null);
+    assert(notifications != null);
+    assert(function != null);
+    assert(functions != null);
+    assert(classes != null);
+    assert(retainedSizes != null);
+    assert(reachableSizes != null);
+    assert(references != null);
+    assert(retainingPaths != null);
+    assert(scripts != null);
+    assert(objects != null);
+    FunctionViewElement e = new FunctionViewElement.created();
+    e._r = new RenderingScheduler<FunctionViewElement>(e, queue: queue);
+    e._vm = vm;
+    e._isolate = isolate;
+    e._events = events;
+    e._notifications = notifications;
+    e._function = function;
+    e._functions = functions;
+    e._classes = classes;
+    e._retainedSizes = retainedSizes;
+    e._reachableSizes = reachableSizes;
+    e._references = references;
+    e._retainingPaths = retainingPaths;
+    e._scripts = scripts;
+    e._objects = objects;
+    if (function.dartOwner is M.LibraryRef) {
+      e._library = function.dartOwner;
+    }
+    return e;
+  }
+
+  FunctionViewElement.created() : super.created('function-view');
+
+  @override
+  attached() {
+    super.attached();
+    _r.enable();
+    _refresh();
+  }
+
+  @override
+  detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+  }
+
+  void render() {
+    children = <Element>[
+      navBar(_createMenu()),
+      new DivElement()
+        ..classes = ['content-centered-big']
+        ..children = <Element>[
+          new HeadingElement.h2()..text = 'Function ${_function.name}',
+          new HRElement(),
+          new ObjectCommonElement(_isolate, _function, _retainedSizes,
+                  _reachableSizes, _references, _retainingPaths, _objects,
+                  queue: _r.queue)
+              .element,
+          new BRElement(),
+          new DivElement()
+            ..classes = ['memberList']
+            ..children = _createMembers(),
+          new HRElement(),
+          new DivElement()
+            ..children = _function.location == null
+                ? const []
+                : [
+                    new SourceInsetElement(_isolate, _function.location,
+                            _scripts, _objects, _events,
+                            queue: _r.queue)
+                        .element
+                  ],
+          new ViewFooterElement(queue: _r.queue).element
+        ]
+    ];
+  }
+
+  List<Element> _createMenu() {
+    final menu = <Element>[
+      new NavTopMenuElement(queue: _r.queue).element,
+      new NavVMMenuElement(_vm, _events, queue: _r.queue).element,
+      new NavIsolateMenuElement(_isolate, _events, queue: _r.queue).element
+    ];
+    if (_library != null) {
+      menu.add(new NavLibraryMenuElement(_isolate, _library, queue: _r.queue)
+          .element);
+    } else if (_function.dartOwner is M.ClassRef) {
+      menu.add(new NavClassMenuElement(_isolate, _function.dartOwner,
+              queue: _r.queue)
+          .element);
+    }
+    menu.addAll(<Element>[
+      navMenu(_function.name),
+      (new NavRefreshElement(queue: _r.queue)
+            ..onRefresh.listen((e) {
+              e.element.disabled = true;
+              _refresh();
+            }))
+          .element,
+      new NavNotifyElement(_notifications, queue: _r.queue).element
+    ]);
+    return menu;
+  }
+
+  List<Element> _createMembers() {
+    final members = <Element>[
+      new DivElement()
+        ..classes = ['memberItem']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = 'kind',
+          new DivElement()
+            ..classes = ['memberName']
+            ..children = <Element>[
+              new SpanElement()
+                ..text = '${_function.isStatic ? "static " : ""}'
+                    '${_function.isConst ? "const " : ""}'
+                    '${_functionKindToString(_function.kind)}'
+            ]
+        ],
+      new DivElement()
+        ..classes = ['memberItem']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = 'owner',
+          new DivElement()
+            ..classes = ['memberName']
+            ..children = <Element>[
+              _function.dartOwner == null
+                  ? (new SpanElement()..text = '...')
+                  : anyRef(_isolate, _function.dartOwner, _objects,
+                      queue: _r.queue)
+            ]
+        ]
+    ];
+    if (_function.field != null) {
+      members.add(new DivElement()
+        ..classes = ['memberItem']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = 'script',
+          new DivElement()
+            ..classes = ['memberName']
+            ..children = <Element>[
+              new FieldRefElement(_isolate, _function.field, _objects,
+                      queue: _r.queue)
+                  .element
+            ]
+        ]);
+    }
+    members.add(new DivElement()
+      ..classes = ['memberItem']
+      ..children = <Element>[
+        new DivElement()
+          ..classes = ['memberName']
+          ..text = 'script',
+        new DivElement()
+          ..classes = ['memberName']
+          ..children = <Element>[
+            new SourceLinkElement(_isolate, _function.location, _scripts,
+                    queue: _r.queue)
+                .element
+          ]
+      ]);
+    if (_function.code != null) {
+      members.add(new DivElement()
+        ..classes = ['memberItem']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = 'current code',
+          new DivElement()
+            ..classes = ['memberName']
+            ..children = <Element>[
+              new CodeRefElement(_isolate, _function.code, queue: _r.queue)
+                  .element
+            ]
+        ]);
+    }
+    if (_function.unoptimizedCode != null) {
+      members.add(new DivElement()
+        ..classes = ['memberItem']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = 'unoptimized code',
+          new DivElement()
+            ..classes = ['memberName']
+            ..children = <Element>[
+              new CodeRefElement(_isolate, _function.unoptimizedCode,
+                      queue: _r.queue)
+                  .element,
+              new SpanElement()
+                ..title = 'This count is used to determine when a function '
+                    'will be optimized.  It is a combination of call '
+                    'counts and other factors.'
+                ..text = ' (usage count: ${function.usageCounter})'
+            ]
+        ]);
+    }
+    if (_function.bytecode != null) {
+      members.add(new DivElement()
+        ..classes = ['memberItem']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = 'bytecode',
+          new DivElement()
+            ..classes = ['memberName']
+            ..children = <Element>[
+              new CodeRefElement(_isolate, _function.bytecode, queue: _r.queue)
+                  .element,
+            ]
+        ]);
+    }
+    members.add(new DivElement()
+      ..classes = ['memberItem']
+      ..text = ' ');
+
+    if (_function.icDataArray != null) {
+      members.add(new DivElement()
+        ..classes = ['memberItem']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = 'ic data array',
+          new DivElement()
+            ..classes = ['memberName']
+            ..children = <Element>[
+              new InstanceRefElement(_isolate, _function.icDataArray, _objects,
+                      queue: _r.queue)
+                  .element
+            ]
+        ]);
+    }
+
+    members.addAll([
+      new DivElement()
+        ..classes = ['memberItem']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = 'deoptimizations',
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = '${_function.deoptimizations}'
+        ],
+      new DivElement()
+        ..classes = ['memberItem']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = 'optimizable',
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = _function.isOptimizable ? 'yes' : 'no'
+        ],
+      new DivElement()
+        ..classes = ['memberItem']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = 'inlinable',
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = _function.isInlinable ? 'yes' : 'no'
+        ],
+      new DivElement()
+        ..classes = ['memberItem']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = 'intrinsic',
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = _function.hasIntrinsic ? 'yes' : 'no'
+        ],
+      new DivElement()
+        ..classes = ['memberItem']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = 'recognized',
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = _function.isRecognized ? 'yes' : 'no'
+        ],
+      new DivElement()
+        ..classes = ['memberItem']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = 'native',
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = _function.isNative ? 'yes' : 'no'
+        ],
+      new DivElement()
+        ..classes = ['memberItem']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = 'vm name',
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = _function.vmName
+        ]
+    ]);
+    return members;
+  }
+
+  Future _refresh() async {
+    _function = await _functions.get(_isolate, _function.id);
+    if (_function.dartOwner is M.LibraryRef) {
+      _library = _function.dartOwner;
+    } else if (_function.dartOwner is M.ClassRef) {
+      _library = (await _classes.get(_isolate, _function.dartOwner.id)).library;
+    }
+    _r.dirty();
+  }
+
+  static String _functionKindToString(M.FunctionKind kind) {
+    switch (kind) {
+      case M.FunctionKind.regular:
+        return 'regular';
+      case M.FunctionKind.closure:
+        return 'closure';
+      case M.FunctionKind.implicitClosure:
+        return 'implicit closure';
+      case M.FunctionKind.getter:
+        return 'getter';
+      case M.FunctionKind.setter:
+        return 'setter';
+      case M.FunctionKind.constructor:
+        return 'constructor';
+      case M.FunctionKind.implicitGetter:
+        return 'implicit getter';
+      case M.FunctionKind.implicitSetter:
+        return 'implicit setter';
+      case M.FunctionKind.implicitStaticGetter:
+        return 'implicit static getter';
+      case M.FunctionKind.fieldInitializer:
+        return 'field initializer';
+      case M.FunctionKind.irregexpFunction:
+        return 'irregexp function';
+      case M.FunctionKind.methodExtractor:
+        return 'method extractor';
+      case M.FunctionKind.noSuchMethodDispatcher:
+        return 'noSuchMethod dispatcher';
+      case M.FunctionKind.invokeFieldDispatcher:
+        return 'invokeField dispatcher';
+      case M.FunctionKind.collected:
+        return 'collected';
+      case M.FunctionKind.native:
+        return 'native';
+      case M.FunctionKind.ffiTrampoline:
+        return 'ffi trampoline';
+      case M.FunctionKind.stub:
+        return 'stub';
+      case M.FunctionKind.tag:
+        return 'tag';
+      case M.FunctionKind.signatureFunction:
+        return 'signature function';
+      case M.FunctionKind.dynamicInvocationForwarder:
+        return 'dynamic invocation forwarder';
+    }
+    throw new Exception('Unknown Functionkind ($kind)');
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/general_error.dart b/runtime/observatory_2/lib/src/elements/general_error.dart
new file mode 100644
index 0000000..9a4f917
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/general_error.dart
@@ -0,0 +1,71 @@
+// Copyright (c) 2016, 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.
+
+library general_error_element;
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/nav/notify.dart';
+import 'package:observatory_2/src/elements/nav/top_menu.dart';
+
+class GeneralErrorElement extends CustomElement implements Renderable {
+  RenderingScheduler<GeneralErrorElement> _r;
+
+  Stream<RenderedEvent<GeneralErrorElement>> get onRendered => _r.onRendered;
+
+  M.NotificationRepository _notifications;
+  String _message;
+
+  String get message => _message;
+
+  set message(String value) => _message = _r.checkAndReact(_message, value);
+
+  factory GeneralErrorElement(M.NotificationRepository notifications,
+      {String message: '', RenderingQueue queue}) {
+    assert(notifications != null);
+    assert(message != null);
+    GeneralErrorElement e = new GeneralErrorElement.created();
+    e._r = new RenderingScheduler<GeneralErrorElement>(e, queue: queue);
+    e._message = message;
+    e._notifications = notifications;
+    return e;
+  }
+
+  GeneralErrorElement.created() : super.created('general-error');
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    children = <Element>[];
+    _r.disable(notify: true);
+  }
+
+  void render() {
+    children = <Element>[
+      navBar(<Element>[
+        new NavTopMenuElement(queue: _r.queue).element,
+        new NavNotifyElement(_notifications, queue: _r.queue).element
+      ]),
+      new DivElement()
+        ..classes = ['content-centered']
+        ..children = <Element>[
+          new HeadingElement.h1()..text = 'Error',
+          new BRElement(),
+          new DivElement()
+            ..classes = ['well']
+            ..text = message
+        ]
+    ];
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/heap_map.dart b/runtime/observatory_2/lib/src/elements/heap_map.dart
new file mode 100644
index 0000000..71aa6ca
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/heap_map.dart
@@ -0,0 +1,317 @@
+// Copyright (c) 2014, 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.
+
+library heap_map_element;
+
+import 'dart:async';
+import 'dart:html';
+import 'dart:math';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/service.dart' as S;
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
+import 'package:observatory_2/src/elements/helpers/nav_menu.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/helpers/uris.dart';
+import 'package:observatory_2/src/elements/nav/isolate_menu.dart';
+import 'package:observatory_2/src/elements/nav/notify.dart';
+import 'package:observatory_2/src/elements/nav/refresh.dart';
+import 'package:observatory_2/src/elements/nav/top_menu.dart';
+import 'package:observatory_2/src/elements/nav/vm_menu.dart';
+
+class HeapMapElement extends CustomElement implements Renderable {
+  RenderingScheduler<HeapMapElement> _r;
+
+  Stream<RenderedEvent<HeapMapElement>> get onRendered => _r.onRendered;
+
+  M.VM _vm;
+  M.IsolateRef _isolate;
+  M.EventRepository _events;
+  M.NotificationRepository _notifications;
+  M.VMRef get vm => _vm;
+  M.IsolateRef get isolate => _isolate;
+  M.NotificationRepository get notifications => _notifications;
+
+  factory HeapMapElement(M.VM vm, M.IsolateRef isolate,
+      M.EventRepository events, M.NotificationRepository notifications,
+      {RenderingQueue queue}) {
+    assert(vm != null);
+    assert(isolate != null);
+    assert(events != null);
+    assert(notifications != null);
+    HeapMapElement e = new HeapMapElement.created();
+    e._r = new RenderingScheduler<HeapMapElement>(e, queue: queue);
+    e._vm = vm;
+    e._isolate = isolate;
+    e._events = events;
+    e._notifications = notifications;
+    return e;
+  }
+
+  HeapMapElement.created() : super.created('heap-map');
+
+  @override
+  attached() {
+    super.attached();
+    _r.enable();
+    _refresh();
+  }
+
+  @override
+  detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+  }
+
+  CanvasElement _canvas;
+  var _fragmentationData;
+  double _pageHeight;
+  final _classIdToColor = {};
+  final _colorToClassId = {};
+  final _classIdToName = {};
+
+  static final _freeColor = [255, 255, 255, 255];
+  static final _pageSeparationColor = [0, 0, 0, 255];
+  static const _PAGE_SEPARATION_HEIGHT = 4;
+  // Many browsers will not display a very tall canvas.
+  // TODO(koda): Improve interface for huge heaps.
+  static const _MAX_CANVAS_HEIGHT = 6000;
+
+  String _status;
+  S.ServiceMap _fragmentation;
+
+  void render() {
+    if (_canvas == null) {
+      _canvas = new CanvasElement()
+        ..width = 1
+        ..height = 1
+        ..onMouseMove.listen(_handleMouseMove);
+    }
+
+    // Set hover text to describe the object under the cursor.
+    _canvas.title = _status;
+
+    children = <Element>[
+      navBar(<Element>[
+        new NavTopMenuElement(queue: _r.queue).element,
+        new NavVMMenuElement(_vm, _events, queue: _r.queue).element,
+        new NavIsolateMenuElement(_isolate, _events, queue: _r.queue).element,
+        navMenu('heap map'),
+        (new NavRefreshElement(label: 'Mark-Compact', queue: _r.queue)
+              ..onRefresh.listen((_) => _refresh(gc: "mark-compact")))
+            .element,
+        (new NavRefreshElement(label: 'Mark-Sweep', queue: _r.queue)
+              ..onRefresh.listen((_) => _refresh(gc: "mark-sweep")))
+            .element,
+        (new NavRefreshElement(label: 'Scavenge', queue: _r.queue)
+              ..onRefresh.listen((_) => _refresh(gc: "scavenge")))
+            .element,
+        (new NavRefreshElement(queue: _r.queue)
+              ..onRefresh.listen((_) => _refresh()))
+            .element,
+        (new NavNotifyElement(_notifications, queue: _r.queue)).element
+      ]),
+      new DivElement()
+        ..classes = ['content-centered-big']
+        ..children = <Element>[
+          new HeadingElement.h2()..text = _status,
+          new HRElement(),
+        ],
+      new DivElement()
+        ..classes = ['flex-row']
+        ..children = <Element>[_canvas]
+    ];
+  }
+
+  // Encode color as single integer, to enable using it as a map key.
+  int _packColor(Iterable<int> color) {
+    int packed = 0;
+    for (var component in color) {
+      packed = packed * 256 + component;
+    }
+    return packed;
+  }
+
+  void _addClass(int classId, String name, Iterable<int> color) {
+    _classIdToName[classId] = name.split('@')[0];
+    _classIdToColor[classId] = color;
+    _colorToClassId[_packColor(color)] = classId;
+  }
+
+  void _updateClassList(classList, int freeClassId) {
+    for (var member in classList['classes']) {
+      if (member is! S.Class) {
+        // TODO(turnidge): The printing for some of these non-class
+        // members is broken.  Fix this:
+        //
+        // Logger.root.info('$member');
+        print('Ignoring non-class in class list');
+        continue;
+      }
+      var classId = int.parse(member.id.split('/').last);
+      var color = _classIdToRGBA(classId);
+      _addClass(classId, member.name, color);
+    }
+    _addClass(freeClassId, 'Free', _freeColor);
+    _addClass(0, '', _pageSeparationColor);
+  }
+
+  Iterable<int> _classIdToRGBA(int classId) {
+    // TODO(koda): Pick random hue, but fixed saturation and value.
+    var rng = new Random(classId);
+    return [rng.nextInt(128), rng.nextInt(128), rng.nextInt(128), 255];
+  }
+
+  String _classNameAt(Point<num> point) {
+    var color = new PixelReference(_fragmentationData, point).color;
+    return _classIdToName[_colorToClassId[_packColor(color)]];
+  }
+
+  ObjectInfo _objectAt(Point<num> point) {
+    if (_fragmentation == null || _canvas == null) {
+      return null;
+    }
+    var pagePixels = _pageHeight * _fragmentationData.width;
+    var index = new PixelReference(_fragmentationData, point).index;
+    var pageIndex = index ~/ pagePixels;
+    num pageOffset = index % pagePixels;
+    var pages = _fragmentation['pages'];
+    if (pageIndex < 0 || pageIndex >= pages.length) {
+      return null;
+    }
+    // Scan the page to find start and size.
+    var page = pages[pageIndex];
+    var objects = page['objects'];
+    var offset = 0;
+    var size = 0;
+    for (var i = 0; i < objects.length; i += 2) {
+      size = objects[i];
+      offset += size;
+      if (offset > pageOffset) {
+        pageOffset = offset - size;
+        break;
+      }
+    }
+    return new ObjectInfo(
+        int.parse(page['objectStart']) +
+            pageOffset * _fragmentation['unitSizeBytes'],
+        size * _fragmentation['unitSizeBytes']);
+  }
+
+  void _handleMouseMove(MouseEvent event) {
+    var info = _objectAt(event.offset);
+    if (info == null) {
+      _status = '';
+      _r.dirty();
+      return;
+    }
+    var addressString = '${info.size}B @ 0x${info.address.toRadixString(16)}';
+    var className = _classNameAt(event.offset);
+    _status = (className == '') ? '-' : '$className $addressString';
+    _r.dirty();
+  }
+
+  void _updateFragmentationData() {
+    if (_fragmentation == null || _canvas == null) {
+      return;
+    }
+    _updateClassList(
+        _fragmentation['classList'], _fragmentation['freeClassId']);
+    var pages = _fragmentation['pages'];
+    var width = max(_canvas.parent.client.width, 1);
+    _pageHeight = _PAGE_SEPARATION_HEIGHT +
+        _fragmentation['pageSizeBytes'] ~/
+            _fragmentation['unitSizeBytes'] ~/
+            width;
+    var height = min(_pageHeight * pages.length, _MAX_CANVAS_HEIGHT);
+    _fragmentationData = _canvas.context2D.createImageData(width, height);
+    _canvas.width = _fragmentationData.width;
+    _canvas.height = _fragmentationData.height;
+    _renderPages(0);
+  }
+
+  // Renders and draws asynchronously, one page at a time to avoid
+  // blocking the UI.
+  void _renderPages(int startPage) {
+    var pages = _fragmentation['pages'];
+    _status = 'Loaded $startPage of ${pages.length} pages';
+    _r.dirty();
+    var startY = (startPage * _pageHeight).round();
+    var endY = startY + _pageHeight.round();
+    if (startPage >= pages.length || endY > _fragmentationData.height) {
+      return;
+    }
+    var pixel = new PixelReference(_fragmentationData, new Point(0, startY));
+    var objects = pages[startPage]['objects'];
+    for (var i = 0; i < objects.length; i += 2) {
+      var count = objects[i];
+      var classId = objects[i + 1];
+      var color = _classIdToColor[classId];
+      while (count-- > 0) {
+        pixel.color = color;
+        pixel = pixel.next();
+      }
+    }
+    while (pixel.point.y < endY) {
+      pixel.color = _pageSeparationColor;
+      pixel = pixel.next();
+    }
+    _canvas.context2D.putImageData(
+        _fragmentationData, 0, 0, 0, startY, _fragmentationData.width, endY);
+    // Continue with the next page, asynchronously.
+    new Future(() {
+      _renderPages(startPage + 1);
+    });
+  }
+
+  Future _refresh({String gc}) {
+    final isolate = _isolate as S.Isolate;
+    var params = {};
+    if (gc != null) {
+      params['gc'] = gc;
+    }
+    return isolate.invokeRpc('_getHeapMap', params).then((serviceObject) {
+      S.ServiceMap response = serviceObject;
+      assert(response['type'] == 'HeapMap');
+      _fragmentation = response;
+      _updateFragmentationData();
+    });
+  }
+}
+
+// A reference to a particular pixel of ImageData.
+class PixelReference {
+  final _data;
+  var _dataIndex;
+  static const NUM_COLOR_COMPONENTS = 4;
+
+  PixelReference(ImageData data, Point<num> point)
+      : _data = data,
+        _dataIndex = (point.y * data.width + point.x) * NUM_COLOR_COMPONENTS;
+
+  PixelReference._fromDataIndex(this._data, this._dataIndex);
+
+  Point<num> get point => new Point(index % _data.width, index ~/ _data.width);
+
+  void set color(Iterable<int> color) {
+    _data.data.setRange(_dataIndex, _dataIndex + NUM_COLOR_COMPONENTS, color);
+  }
+
+  Iterable<int> get color =>
+      _data.data.getRange(_dataIndex, _dataIndex + NUM_COLOR_COMPONENTS);
+
+  // Returns the next pixel in row-major order.
+  PixelReference next() => new PixelReference._fromDataIndex(
+      _data, _dataIndex + NUM_COLOR_COMPONENTS);
+
+  // The row-major index of this pixel.
+  int get index => _dataIndex ~/ NUM_COLOR_COMPONENTS;
+}
+
+class ObjectInfo {
+  final address;
+  final size;
+  ObjectInfo(this.address, this.size);
+}
diff --git a/runtime/observatory_2/lib/src/elements/heap_snapshot.dart b/runtime/observatory_2/lib/src/elements/heap_snapshot.dart
new file mode 100644
index 0000000..bfe8412
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/heap_snapshot.dart
@@ -0,0 +1,1529 @@
+// Copyright (c) 2015, 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.
+
+// 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.
+
+import 'dart:async';
+import 'dart:html';
+import 'dart:math' as Math;
+import 'dart:typed_data';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/object_graph.dart';
+import 'package:observatory_2/src/elements/class_ref.dart';
+import 'package:observatory_2/src/elements/containers/virtual_tree.dart';
+import 'package:observatory_2/src/elements/curly_block.dart';
+import 'package:observatory_2/src/elements/helpers/any_ref.dart';
+import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
+import 'package:observatory_2/src/elements/helpers/nav_menu.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/helpers/uris.dart';
+import 'package:observatory_2/src/elements/nav/isolate_menu.dart';
+import 'package:observatory_2/src/elements/nav/notify.dart';
+import 'package:observatory_2/src/elements/nav/refresh.dart';
+import 'package:observatory_2/src/elements/nav/top_menu.dart';
+import 'package:observatory_2/src/elements/nav/vm_menu.dart';
+import 'package:observatory_2/src/elements/tree_map.dart';
+import 'package:observatory_2/repositories.dart';
+import 'package:observatory_2/utils.dart';
+
+enum HeapSnapshotTreeMode {
+  classesTable,
+  classesTableDiff,
+  classesTreeMap,
+  classesTreeMapDiff,
+  dominatorTree,
+  dominatorTreeMap,
+  mergedDominatorTree,
+  mergedDominatorTreeDiff,
+  mergedDominatorTreeMap,
+  mergedDominatorTreeMapDiff,
+  ownershipTable,
+  ownershipTableDiff,
+  ownershipTreeMap,
+  ownershipTreeMapDiff,
+  predecessors,
+  successors,
+}
+
+// Note the order of these lists is reflected in the UI, and the first option
+// is the default.
+const viewModes = [
+  HeapSnapshotTreeMode.mergedDominatorTreeMap,
+  HeapSnapshotTreeMode.mergedDominatorTree,
+  HeapSnapshotTreeMode.dominatorTreeMap,
+  HeapSnapshotTreeMode.dominatorTree,
+  HeapSnapshotTreeMode.ownershipTreeMap,
+  HeapSnapshotTreeMode.ownershipTable,
+  HeapSnapshotTreeMode.classesTreeMap,
+  HeapSnapshotTreeMode.classesTable,
+  HeapSnapshotTreeMode.successors,
+  HeapSnapshotTreeMode.predecessors,
+];
+
+const diffModes = [
+  HeapSnapshotTreeMode.mergedDominatorTreeMapDiff,
+  HeapSnapshotTreeMode.mergedDominatorTreeDiff,
+  HeapSnapshotTreeMode.ownershipTreeMapDiff,
+  HeapSnapshotTreeMode.ownershipTableDiff,
+  HeapSnapshotTreeMode.classesTreeMapDiff,
+  HeapSnapshotTreeMode.classesTableDiff,
+];
+
+abstract class DiffTreeMap<T> extends TreeMap<T> {
+  int getSizeA(T node);
+  int getSizeB(T node);
+
+  // We need to sum gains and losses separately because they both contribute
+  // area to the tree map tiles, i.e., losses don't have negative area in the
+  // visualization. For this reason, common is not necessarily
+  // max(sizeA,sizeB)-min(sizeA,sizeB), gain is not necessarily
+  // abs(sizeB-sizeA), etc.
+  int getGain(T node);
+  int getLoss(T node);
+  int getCommon(T node);
+
+  String getName(T node);
+  String getType(T node);
+
+  int getArea(T node) => getCommon(node) + getGain(node) + getLoss(node);
+  String getLabel(T node) {
+    var name = getName(node);
+    var sizeA = Utils.formatSize(getSizeA(node));
+    var sizeB = Utils.formatSize(getSizeB(node));
+    return "$name [$sizeA → $sizeB]";
+  }
+
+  String getBackground(T node) {
+    int l = getLoss(node);
+    int c = getCommon(node);
+    int g = getGain(node);
+    int a = l + c + g;
+    if (a == 0) {
+      return "white";
+    }
+    // Stripes of green, white and red whose areas are poritional to loss, common and gain.
+    String stop1 = (l / a * 100).toString();
+    String stop2 = ((l + c) / a * 100).toString();
+    return "linear-gradient(to right, #66FF99 $stop1%, white $stop1% $stop2%, #FF6680 $stop2%)";
+  }
+}
+
+class DominatorTreeMap extends NormalTreeMap<SnapshotObject> {
+  HeapSnapshotElement element;
+  DominatorTreeMap(this.element);
+
+  int getSize(SnapshotObject node) => node.retainedSize;
+  String getType(SnapshotObject node) => node.klass.name;
+  String getName(SnapshotObject node) => node.description;
+  SnapshotObject getParent(SnapshotObject node) => node.parent;
+  Iterable<SnapshotObject> getChildren(SnapshotObject node) => node.children;
+  void onSelect(SnapshotObject node) {
+    element.selection = List.from(node.objects);
+    element._r.dirty();
+  }
+
+  void onDetails(SnapshotObject node) {
+    element.selection = List.from(node.objects);
+    element._mode = HeapSnapshotTreeMode.successors;
+    element._r.dirty();
+  }
+}
+
+class MergedDominatorTreeMap extends NormalTreeMap<SnapshotMergedDominator> {
+  HeapSnapshotElement element;
+  MergedDominatorTreeMap(this.element);
+
+  int getSize(SnapshotMergedDominator node) => node.retainedSize;
+  String getType(SnapshotMergedDominator node) => node.klass.name;
+  String getName(SnapshotMergedDominator node) => node.description;
+  SnapshotMergedDominator getParent(SnapshotMergedDominator node) =>
+      node.parent;
+  Iterable<SnapshotMergedDominator> getChildren(SnapshotMergedDominator node) =>
+      node.children;
+  void onSelect(SnapshotMergedDominator node) {
+    element.mergedSelection = node;
+    element._r.dirty();
+  }
+
+  void onDetails(SnapshotMergedDominator node) {
+    element.selection = List.from(node.objects);
+    element._mode = HeapSnapshotTreeMode.successors;
+    element._r.dirty();
+  }
+}
+
+class MergedDominatorDiffTreeMap extends DiffTreeMap<MergedDominatorDiff> {
+  HeapSnapshotElement element;
+  MergedDominatorDiffTreeMap(this.element);
+
+  int getSizeA(MergedDominatorDiff node) => node.retainedSizeA;
+  int getSizeB(MergedDominatorDiff node) => node.retainedSizeB;
+  int getGain(MergedDominatorDiff node) => node.retainedGain;
+  int getLoss(MergedDominatorDiff node) => node.retainedLoss;
+  int getCommon(MergedDominatorDiff node) => node.retainedCommon;
+
+  String getType(MergedDominatorDiff node) => node.name;
+  String getName(MergedDominatorDiff node) => "instances of ${node.name}";
+  MergedDominatorDiff getParent(MergedDominatorDiff node) => node.parent;
+  Iterable<MergedDominatorDiff> getChildren(MergedDominatorDiff node) =>
+      node.children;
+  void onSelect(MergedDominatorDiff node) {
+    element.mergedDiffSelection = node;
+    element._r.dirty();
+  }
+
+  void onDetails(MergedDominatorDiff node) {
+    element._snapshotA = element._snapshotB;
+    element.selection = node.objectsB;
+    element._mode = HeapSnapshotTreeMode.successors;
+    element._r.dirty();
+  }
+}
+
+// Using `null` to represent the root.
+class ClassesShallowTreeMap extends NormalTreeMap<SnapshotClass> {
+  HeapSnapshotElement element;
+  SnapshotGraph snapshot;
+
+  ClassesShallowTreeMap(this.element, this.snapshot);
+
+  int getSize(SnapshotClass node) =>
+      node == null ? snapshot.size : node.shallowSize;
+  String getType(SnapshotClass node) => node == null ? "Classes" : node.name;
+  String getName(SnapshotClass node) => node == null
+      ? "${snapshot.classes.length} classes"
+      : "${node.instanceCount} instances of ${node.name}";
+
+  SnapshotClass getParent(SnapshotClass node) => null;
+  Iterable<SnapshotClass> getChildren(SnapshotClass node) =>
+      node == null ? snapshot.classes : <SnapshotClass>[];
+  void onSelect(SnapshotClass node) {}
+  void onDetails(SnapshotClass node) {
+    element.selection = List.from(node.instances);
+    element._mode = HeapSnapshotTreeMode.successors;
+    element._r.dirty();
+  }
+}
+
+// Using `null` to represent the root.
+class ClassesShallowDiffTreeMap extends DiffTreeMap<SnapshotClassDiff> {
+  HeapSnapshotElement element;
+  List<SnapshotClassDiff> classes;
+
+  ClassesShallowDiffTreeMap(this.element, this.classes);
+
+  int getSizeA(SnapshotClassDiff node) {
+    if (node != null) return node.shallowSizeA;
+    int s = 0;
+    for (var cls in classes) s += cls.shallowSizeA;
+    return s;
+  }
+
+  int getSizeB(SnapshotClassDiff node) {
+    if (node != null) return node.shallowSizeB;
+    int s = 0;
+    for (var cls in classes) s += cls.shallowSizeB;
+    return s;
+  }
+
+  int getGain(SnapshotClassDiff node) {
+    if (node != null) return node.shallowSizeGain;
+    int s = 0;
+    for (var cls in classes) s += cls.shallowSizeGain;
+    return s;
+  }
+
+  int getLoss(SnapshotClassDiff node) {
+    if (node != null) return node.shallowSizeLoss;
+    int s = 0;
+    for (var cls in classes) s += cls.shallowSizeLoss;
+    return s;
+  }
+
+  int getCommon(SnapshotClassDiff node) {
+    if (node != null) return node.shallowSizeCommon;
+    int s = 0;
+    for (var cls in classes) s += cls.shallowSizeCommon;
+    return s;
+  }
+
+  String getType(SnapshotClassDiff node) =>
+      node == null ? "Classes" : node.name;
+  String getName(SnapshotClassDiff node) =>
+      node == null ? "${classes.length} classes" : "instances of ${node.name}";
+  SnapshotClassDiff getParent(SnapshotClassDiff node) => null;
+  Iterable<SnapshotClassDiff> getChildren(SnapshotClassDiff node) =>
+      node == null ? classes : <SnapshotClassDiff>[];
+  void onSelect(SnapshotClassDiff node) {}
+  void onDetails(SnapshotClassDiff node) {
+    element._snapshotA = element._snapshotB;
+    element.selection = node.objectsB;
+    element._mode = HeapSnapshotTreeMode.successors;
+    element._r.dirty();
+  }
+}
+
+// Using `null` to represent the root.
+class ClassesOwnershipTreeMap extends NormalTreeMap<SnapshotClass> {
+  HeapSnapshotElement element;
+  SnapshotGraph snapshot;
+
+  ClassesOwnershipTreeMap(this.element, this.snapshot);
+
+  int getSize(SnapshotClass node) =>
+      node == null ? snapshot.size : node.ownedSize;
+  String getType(SnapshotClass node) => node == null ? "classes" : node.name;
+  String getName(SnapshotClass node) => node == null
+      ? "${snapshot.classes.length} Classes"
+      : "${node.instanceCount} instances of ${node.name}";
+  SnapshotClass getParent(SnapshotClass node) => null;
+  Iterable<SnapshotClass> getChildren(SnapshotClass node) =>
+      node == null ? snapshot.classes : <SnapshotClass>[];
+  void onSelect(SnapshotClass node) {}
+  void onDetails(SnapshotClass node) {
+    element.selection = List.from(node.instances);
+    element._mode = HeapSnapshotTreeMode.successors;
+    element._r.dirty();
+  }
+}
+
+// Using `null` to represent the root.
+class ClassesOwnershipDiffTreeMap extends DiffTreeMap<SnapshotClassDiff> {
+  HeapSnapshotElement element;
+  List<SnapshotClassDiff> classes;
+
+  ClassesOwnershipDiffTreeMap(this.element, this.classes);
+
+  int getSizeA(SnapshotClassDiff node) {
+    if (node != null) return node.ownedSizeA;
+    int s = 0;
+    for (var cls in classes) s += cls.ownedSizeA;
+    return s;
+  }
+
+  int getSizeB(SnapshotClassDiff node) {
+    if (node != null) return node.ownedSizeB;
+    int s = 0;
+    for (var cls in classes) s += cls.ownedSizeB;
+    return s;
+  }
+
+  int getGain(SnapshotClassDiff node) {
+    if (node != null) return node.ownedSizeGain;
+    int s = 0;
+    for (var cls in classes) s += cls.ownedSizeGain;
+    return s;
+  }
+
+  int getLoss(SnapshotClassDiff node) {
+    if (node != null) return node.ownedSizeLoss;
+    int s = 0;
+    for (var cls in classes) s += cls.ownedSizeLoss;
+    return s;
+  }
+
+  int getCommon(SnapshotClassDiff node) {
+    if (node != null) return node.ownedSizeCommon;
+    int s = 0;
+    for (var cls in classes) s += cls.ownedSizeCommon;
+    return s;
+  }
+
+  String getType(SnapshotClassDiff node) =>
+      node == null ? "Classes" : node.name;
+  String getName(SnapshotClassDiff node) =>
+      node == null ? "${classes.length} classes" : "instances of ${node.name}";
+  SnapshotClassDiff getParent(SnapshotClassDiff node) => null;
+  Iterable<SnapshotClassDiff> getChildren(SnapshotClassDiff node) =>
+      node == null ? classes : <SnapshotClassDiff>[];
+  void onSelect(SnapshotClassDiff node) {}
+  void onDetails(SnapshotClassDiff node) {
+    element._snapshotA = element._snapshotB;
+    element.selection = node.objectsB;
+    element._mode = HeapSnapshotTreeMode.successors;
+    element._r.dirty();
+  }
+}
+
+class SnapshotClassDiff {
+  SnapshotClass _a;
+  SnapshotClass _b;
+
+  int get shallowSizeA => _a == null ? 0 : _a.shallowSize;
+  int get ownedSizeA => _a == null ? 0 : _a.ownedSize;
+  int get instanceCountA => _a == null ? 0 : _a.instanceCount;
+
+  int get shallowSizeB => _b == null ? 0 : _b.shallowSize;
+  int get ownedSizeB => _b == null ? 0 : _b.ownedSize;
+  int get instanceCountB => _b == null ? 0 : _b.instanceCount;
+
+  int get shallowSizeDiff => shallowSizeB - shallowSizeA;
+  int get ownedSizeDiff => ownedSizeB - ownedSizeA;
+  int get instanceCountDiff => instanceCountB - instanceCountA;
+
+  int get shallowSizeGain =>
+      shallowSizeB > shallowSizeA ? shallowSizeB - shallowSizeA : 0;
+  int get ownedSizeGain =>
+      ownedSizeB > ownedSizeA ? ownedSizeB - ownedSizeA : 0;
+  int get shallowSizeLoss =>
+      shallowSizeA > shallowSizeB ? shallowSizeA - shallowSizeB : 0;
+  int get ownedSizeLoss =>
+      ownedSizeA > ownedSizeB ? ownedSizeA - ownedSizeB : 0;
+  int get shallowSizeCommon =>
+      shallowSizeB > shallowSizeA ? shallowSizeA : shallowSizeB;
+  int get ownedSizeCommon => ownedSizeB > ownedSizeA ? ownedSizeA : ownedSizeB;
+
+  String get name => _a == null ? _b.name : _a.name;
+
+  List<SnapshotObject> get objectsA =>
+      _a == null ? <SnapshotObject>[] : _a.instances.toList();
+  List<SnapshotObject> get objectsB =>
+      _b == null ? <SnapshotObject>[] : _b.instances.toList();
+
+  static List<SnapshotClassDiff> from(
+      SnapshotGraph graphA, SnapshotGraph graphB) {
+    // Matching classes by SnapshotClass.qualifiedName.
+    var classesB = new Map<String, SnapshotClass>();
+    var classesDiff = <SnapshotClassDiff>[];
+    for (var classB in graphB.classes) {
+      classesB[classB.qualifiedName] = classB;
+    }
+    for (var classA in graphA.classes) {
+      var classDiff = new SnapshotClassDiff();
+      var qualifiedName = classA.qualifiedName;
+      var name = classA.name;
+      classDiff._a = classA;
+      var classB = classesB[qualifiedName];
+      if (classB != null) {
+        classesB.remove(qualifiedName);
+        classDiff._b = classB;
+      }
+      classesDiff.add(classDiff);
+    }
+    for (var classB in classesB.values) {
+      var classDiff = new SnapshotClassDiff();
+      classDiff._b = classB;
+      classesDiff.add(classDiff);
+    }
+    return classesDiff;
+  }
+}
+
+class MergedDominatorDiff {
+  SnapshotMergedDominator _a;
+  SnapshotMergedDominator _b;
+  MergedDominatorDiff parent;
+  List<MergedDominatorDiff> children;
+  int retainedGain = -1;
+  int retainedLoss = -1;
+  int retainedCommon = -1;
+
+  int get shallowSizeA => _a == null ? 0 : _a.shallowSize;
+  int get retainedSizeA => _a == null ? 0 : _a.retainedSize;
+  int get instanceCountA => _a == null ? 0 : _a.instanceCount;
+
+  int get shallowSizeB => _b == null ? 0 : _b.shallowSize;
+  int get retainedSizeB => _b == null ? 0 : _b.retainedSize;
+  int get instanceCountB => _b == null ? 0 : _b.instanceCount;
+
+  int get shallowSizeDiff => shallowSizeB - shallowSizeA;
+  int get retainedSizeDiff => retainedSizeB - retainedSizeA;
+  int get instanceCountDiff => instanceCountB - instanceCountA;
+
+  String get name => _a == null ? _b.klass.name : _a.klass.name;
+
+  List<SnapshotObject> get objectsA =>
+      _a == null ? <SnapshotObject>[] : _a.objects.toList();
+  List<SnapshotObject> get objectsB =>
+      _b == null ? <SnapshotObject>[] : _b.objects.toList();
+
+  static MergedDominatorDiff from(
+      SnapshotMergedDominator a, SnapshotMergedDominator b) {
+    var root = new MergedDominatorDiff();
+    root._a = a;
+    root._b = b;
+
+    // We must use an explicit stack instead of the call stack because the
+    // dominator tree can be arbitrarily deep. We need to compute the full
+    // tree to compute areas, so we do this eagerly to avoid having to
+    // repeatedly test for initialization.
+    var worklist = <MergedDominatorDiff>[];
+    worklist.add(root);
+    // Compute children top-down.
+    for (var i = 0; i < worklist.length; i++) {
+      worklist[i]._computeChildren(worklist);
+    }
+    // Compute area botton-up.
+    for (var i = worklist.length - 1; i >= 0; i--) {
+      worklist[i]._computeArea();
+    }
+
+    return root;
+  }
+
+  void _computeChildren(List<MergedDominatorDiff> worklist) {
+    assert(children == null);
+    children = <MergedDominatorDiff>[];
+
+    // Matching children by MergedObjectVertex.klass.qualifiedName.
+    final childrenB = <String, SnapshotMergedDominator>{};
+    if (_b != null)
+      for (var childB in _b.children) {
+        childrenB[childB.klass.qualifiedName] = childB;
+      }
+    if (_a != null)
+      for (var childA in _a.children) {
+        var childDiff = new MergedDominatorDiff();
+        childDiff.parent = this;
+        childDiff._a = childA;
+        var qualifiedName = childA.klass.qualifiedName;
+        var childB = childrenB[qualifiedName];
+        if (childB != null) {
+          childrenB.remove(qualifiedName);
+          childDiff._b = childB;
+        }
+        children.add(childDiff);
+        worklist.add(childDiff);
+      }
+    for (var childB in childrenB.values) {
+      var childDiff = new MergedDominatorDiff();
+      childDiff.parent = this;
+      childDiff._b = childB;
+      children.add(childDiff);
+      worklist.add(childDiff);
+    }
+
+    if (children.length == 0) {
+      // Compress.
+      children = const <MergedDominatorDiff>[];
+    }
+  }
+
+  void _computeArea() {
+    int g = 0;
+    int l = 0;
+    int c = 0;
+    for (var child in children) {
+      g += child.retainedGain;
+      l += child.retainedLoss;
+      c += child.retainedCommon;
+    }
+    int d = shallowSizeDiff;
+    if (d > 0) {
+      g += d;
+      c += shallowSizeA;
+    } else {
+      l -= d;
+      c += shallowSizeB;
+    }
+    assert(retainedSizeA + g - l == retainedSizeB);
+    retainedGain = g;
+    retainedLoss = l;
+    retainedCommon = c;
+  }
+}
+
+class HeapSnapshotElement extends CustomElement implements Renderable {
+  RenderingScheduler<HeapSnapshotElement> _r;
+
+  Stream<RenderedEvent<HeapSnapshotElement>> get onRendered => _r.onRendered;
+
+  M.VM _vm;
+  M.IsolateRef _isolate;
+  M.EventRepository _events;
+  M.NotificationRepository _notifications;
+  M.HeapSnapshotRepository _snapshots;
+  M.ObjectRepository _objects;
+  SnapshotReader _reader;
+  String _status;
+  List<SnapshotGraph> _loadedSnapshots = <SnapshotGraph>[];
+  SnapshotGraph _snapshotA;
+  SnapshotGraph _snapshotB;
+  HeapSnapshotTreeMode _mode = HeapSnapshotTreeMode.mergedDominatorTreeMap;
+  M.IsolateRef get isolate => _isolate;
+  M.NotificationRepository get notifications => _notifications;
+  M.HeapSnapshotRepository get profiles => _snapshots;
+  M.VMRef get vm => _vm;
+
+  List<SnapshotObject> selection;
+  SnapshotMergedDominator mergedSelection;
+  MergedDominatorDiff mergedDiffSelection;
+
+  factory HeapSnapshotElement(
+      M.VM vm,
+      M.IsolateRef isolate,
+      M.EventRepository events,
+      M.NotificationRepository notifications,
+      M.HeapSnapshotRepository snapshots,
+      M.ObjectRepository objects,
+      {RenderingQueue queue}) {
+    assert(vm != null);
+    assert(isolate != null);
+    assert(events != null);
+    assert(notifications != null);
+    assert(snapshots != null);
+    assert(objects != null);
+    HeapSnapshotElement e = new HeapSnapshotElement.created();
+    e._r = new RenderingScheduler<HeapSnapshotElement>(e, queue: queue);
+    e._vm = vm;
+    e._isolate = isolate;
+    e._events = events;
+    e._notifications = notifications;
+    e._snapshots = snapshots;
+    e._objects = objects;
+    return e;
+  }
+
+  HeapSnapshotElement.created() : super.created('heap-snapshot');
+
+  @override
+  attached() {
+    super.attached();
+    _r.enable();
+    _refresh();
+  }
+
+  @override
+  detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+  }
+
+  void render() {
+    final content = <Element>[
+      navBar(<Element>[
+        new NavTopMenuElement(queue: _r.queue).element,
+        new NavVMMenuElement(_vm, _events, queue: _r.queue).element,
+        new NavIsolateMenuElement(_isolate, _events, queue: _r.queue).element,
+        navMenu('heap snapshot'),
+        (new NavRefreshElement(queue: _r.queue)
+              ..disabled = _reader != null
+              ..onRefresh.listen((e) {
+                _refresh();
+              }))
+            .element,
+        (new NavRefreshElement(label: 'save', queue: _r.queue)
+              ..disabled = _reader != null
+              ..onRefresh.listen((e) {
+                _save();
+              }))
+            .element,
+        (new NavRefreshElement(label: 'load', queue: _r.queue)
+              ..disabled = _reader != null
+              ..onRefresh.listen((e) {
+                _load();
+              }))
+            .element,
+        new NavNotifyElement(_notifications, queue: _r.queue).element
+      ]),
+    ];
+    if (_reader != null) {
+      // Loading
+      content.addAll(_createStatusMessage('Loading snapshot...',
+          description: _status, progress: 1));
+    } else if (_snapshotA != null) {
+      // Loaded
+      content.addAll(_createReport());
+    }
+    children = content;
+  }
+
+  _refresh() {
+    _reader = null;
+    _snapshotLoading(_snapshots.get(isolate));
+  }
+
+  _save() {
+    var blob = new Blob(_snapshotA.chunks, 'application/octet-stream');
+    var blobUrl = Url.createObjectUrl(blob);
+    var link = new AnchorElement();
+    link.href = blobUrl;
+    var now = new DateTime.now();
+    link.download = 'dart-heap-${now.year}-${now.month}-${now.day}.bin';
+    link.click();
+  }
+
+  _load() {
+    var input = new InputElement();
+    input.type = 'file';
+    input.multiple = false;
+    input.onChange.listen((event) {
+      var file = input.files[0];
+      var reader = new FileReader();
+      reader.onLoad.listen((event) async {
+        var encoded = <Uint8List>[reader.result];
+        var snapshotReader = new SnapshotReader();
+        _snapshotLoading(snapshotReader);
+        snapshotReader.add(reader.result);
+        snapshotReader.close();
+      });
+      reader.readAsArrayBuffer(file);
+    });
+    input.click();
+  }
+
+  _snapshotLoading(SnapshotReader reader) async {
+    _status = '';
+    _reader = reader;
+    reader.onProgress.listen((String status) {
+      _status = status;
+      _r.dirty();
+    });
+    _snapshotLoaded(await reader.done);
+  }
+
+  _snapshotLoaded(SnapshotGraph snapshot) {
+    _reader = null;
+    _loadedSnapshots.add(snapshot);
+    _snapshotA = snapshot;
+    _snapshotB = snapshot;
+    selection = null;
+    mergedSelection = null;
+    mergedDiffSelection = null;
+    _r.dirty();
+  }
+
+  static List<Element> _createStatusMessage(String message,
+      {String description: '', double progress: 0.0}) {
+    return [
+      new DivElement()
+        ..classes = ['content-centered-big']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['statusBox', 'shadow', 'center']
+            ..children = <Element>[
+              new DivElement()
+                ..classes = ['statusMessage']
+                ..text = message,
+              new DivElement()
+                ..classes = ['statusDescription']
+                ..text = description,
+              new DivElement()
+                ..style.background = '#0489c3'
+                ..style.width = '$progress%'
+                ..style.height = '15px'
+                ..style.borderRadius = '4px'
+            ]
+        ]
+    ];
+  }
+
+  VirtualTreeElement _tree;
+
+  void _createTreeMap<T>(List<HtmlElement> report, TreeMap<T> treemap, T root) {
+    final content = new DivElement();
+    content.style.border = '1px solid black';
+    content.style.width = '100%';
+    content.style.height = '100%';
+    content.text = 'Performing layout...';
+    Timer.run(() {
+      // Generate the treemap after the content div has been added to the
+      // document so that we can ask the browser how much space is
+      // available for treemap layout.
+      treemap.showIn(root, content);
+    });
+
+    final text =
+        'Double-click a tile to zoom in. Double-click the outermost tile to zoom out. Right-click a tile to inspect its objects.';
+    report.addAll([
+      new DivElement()
+        ..classes = ['content-centered-big', 'explanation']
+        ..text = text,
+      new DivElement()
+        ..classes = ['content-centered-big']
+        ..style.width = '100%'
+        ..style.height = '100%'
+        ..children = [content]
+    ]);
+  }
+
+  List<Element> _createReport() {
+    var report = <HtmlElement>[
+      new DivElement()
+        ..classes = ['content-centered-big']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberList']
+            ..children = <Element>[
+              new DivElement()
+                ..classes = ['memberItem']
+                ..children = <Element>[
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..text = 'Snapshot A',
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..children = _createSnapshotSelectA()
+                ],
+              new DivElement()
+                ..classes = ['memberItem']
+                ..children = <Element>[
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..text = 'Snapshot B',
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..children = _createSnapshotSelectB()
+                ],
+              new DivElement()
+                ..classes = ['memberItem']
+                ..children = <Element>[
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..text = (_snapshotA == _snapshotB) ? 'View ' : 'Compare ',
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..children = _createModeSelect()
+                ]
+            ]
+        ],
+    ];
+    switch (_mode) {
+      case HeapSnapshotTreeMode.dominatorTree:
+        if (selection == null) {
+          selection = List.from(_snapshotA.extendedRoot.objects);
+        }
+        _tree = new VirtualTreeElement(
+            _createDominator, _updateDominator, _getChildrenDominator,
+            items: selection, queue: _r.queue);
+        if (selection.length == 1) {
+          _tree.expand(selection.first);
+        }
+        final text = 'In a heap dominator tree, an object X is a parent of '
+            'object Y if every path from the root to Y goes through '
+            'X. This allows you to find "choke points" that are '
+            'holding onto a lot of memory. If an object becomes '
+            'garbage, all its children in the dominator tree become '
+            'garbage as well.';
+        report.addAll([
+          new DivElement()
+            ..classes = ['content-centered-big', 'explanation']
+            ..text = text,
+          _tree.element
+        ]);
+        break;
+      case HeapSnapshotTreeMode.dominatorTreeMap:
+        if (selection == null) {
+          selection = List.from(_snapshotA.extendedRoot.objects);
+        }
+        _createTreeMap(report, new DominatorTreeMap(this), selection.first);
+        break;
+      case HeapSnapshotTreeMode.mergedDominatorTree:
+        _tree = new VirtualTreeElement(_createMergedDominator,
+            _updateMergedDominator, _getChildrenMergedDominator,
+            items: _getChildrenMergedDominator(_snapshotA.extendedMergedRoot),
+            queue: _r.queue);
+        _tree.expand(_snapshotA.extendedMergedRoot);
+        final text = 'A heap dominator tree, where siblings with the same class'
+            ' have been merged into a single node.';
+        report.addAll([
+          new DivElement()
+            ..classes = ['content-centered-big', 'explanation']
+            ..text = text,
+          _tree.element
+        ]);
+        break;
+      case HeapSnapshotTreeMode.mergedDominatorTreeDiff:
+        var root = MergedDominatorDiff.from(
+            _snapshotA.mergedRoot, _snapshotB.mergedRoot);
+        _tree = new VirtualTreeElement(_createMergedDominatorDiff,
+            _updateMergedDominatorDiff, _getChildrenMergedDominatorDiff,
+            items: _getChildrenMergedDominatorDiff(root), queue: _r.queue);
+        _tree.expand(root);
+        final text = 'A heap dominator tree, where siblings with the same class'
+            ' have been merged into a single node.';
+        report.addAll([
+          new DivElement()
+            ..classes = ['content-centered-big', 'explanation']
+            ..text = text,
+          _tree.element
+        ]);
+        break;
+      case HeapSnapshotTreeMode.mergedDominatorTreeMap:
+        if (mergedSelection == null) {
+          mergedSelection = _snapshotA.extendedMergedRoot;
+        }
+        _createTreeMap(
+            report, new MergedDominatorTreeMap(this), mergedSelection);
+        break;
+      case HeapSnapshotTreeMode.mergedDominatorTreeMapDiff:
+        if (mergedDiffSelection == null) {
+          mergedDiffSelection = MergedDominatorDiff.from(
+              _snapshotA.mergedRoot, _snapshotB.mergedRoot);
+        }
+        _createTreeMap(
+            report, new MergedDominatorDiffTreeMap(this), mergedDiffSelection);
+        break;
+      case HeapSnapshotTreeMode.ownershipTable:
+        final items = _snapshotA.classes.where((c) => c.ownedSize > 0).toList();
+        items.sort((a, b) => b.ownedSize - a.ownedSize);
+        _tree = new VirtualTreeElement(
+            _createOwnership, _updateOwnership, _getChildrenOwnership,
+            items: items, queue: _r.queue);
+        _tree.expand(_snapshotA.root);
+        final text = 'An object X is said to "own" object Y if X is the only '
+            'object that references Y, or X owns the only object that '
+            'references Y. In particular, objects "own" the space of any '
+            'unshared lists or maps they reference.';
+        report.addAll([
+          new DivElement()
+            ..classes = ['content-centered-big', 'explanation']
+            ..text = text,
+          _tree.element
+        ]);
+        break;
+      case HeapSnapshotTreeMode.ownershipTableDiff:
+        final items = SnapshotClassDiff.from(_snapshotA, _snapshotB);
+        items.sort((a, b) => b.ownedSizeB - a.ownedSizeB);
+        items.sort((a, b) => b.ownedSizeA - a.ownedSizeA);
+        items.sort((a, b) => b.ownedSizeDiff - a.ownedSizeDiff);
+        _tree = new VirtualTreeElement(_createOwnershipDiff,
+            _updateOwnershipDiff, _getChildrenOwnershipDiff,
+            items: items, queue: _r.queue);
+        _tree.expand(_snapshotA.root);
+        final text = 'An object X is said to "own" object Y if X is the only '
+            'object that references Y, or X owns the only object that '
+            'references Y. In particular, objects "own" the space of any '
+            'unshared lists or maps they reference.';
+        report.addAll([
+          new DivElement()
+            ..classes = ['content-centered-big', 'explanation']
+            ..text = text,
+          _tree.element
+        ]);
+        break;
+      case HeapSnapshotTreeMode.ownershipTreeMap:
+        _createTreeMap(
+            report, new ClassesOwnershipTreeMap(this, _snapshotA), null);
+        break;
+      case HeapSnapshotTreeMode.ownershipTreeMapDiff:
+        final items = SnapshotClassDiff.from(_snapshotA, _snapshotB);
+        _createTreeMap(
+            report, new ClassesOwnershipDiffTreeMap(this, items), null);
+        break;
+      case HeapSnapshotTreeMode.successors:
+        if (selection == null) {
+          selection = List.from(_snapshotA.root.objects);
+        }
+        _tree = new VirtualTreeElement(
+            _createSuccessor, _updateSuccessor, _getChildrenSuccessor,
+            items: selection, queue: _r.queue);
+        if (selection.length == 1) {
+          _tree.expand(selection.first);
+        }
+        final text = '';
+        report.addAll([
+          new DivElement()
+            ..classes = ['content-centered-big', 'explanation']
+            ..text = text,
+          _tree.element
+        ]);
+        break;
+      case HeapSnapshotTreeMode.predecessors:
+        if (selection == null) {
+          selection = List.from(_snapshotA.root.objects);
+        }
+        _tree = new VirtualTreeElement(
+            _createPredecessor, _updatePredecessor, _getChildrenPredecessor,
+            items: selection, queue: _r.queue);
+        if (selection.length == 1) {
+          _tree.expand(selection.first);
+        }
+        final text = '';
+        report.addAll([
+          new DivElement()
+            ..classes = ['content-centered-big', 'explanation']
+            ..text = text,
+          _tree.element
+        ]);
+        break;
+      case HeapSnapshotTreeMode.classesTable:
+        final items = _snapshotA.classes.toList();
+        items.sort((a, b) => b.shallowSize - a.shallowSize);
+        _tree = new VirtualTreeElement(
+            _createClass, _updateClass, _getChildrenClass,
+            items: items, queue: _r.queue);
+        report.add(_tree.element);
+        break;
+      case HeapSnapshotTreeMode.classesTableDiff:
+        final items = SnapshotClassDiff.from(_snapshotA, _snapshotB);
+        items.sort((a, b) => b.shallowSizeB - a.shallowSizeB);
+        items.sort((a, b) => b.shallowSizeA - a.shallowSizeA);
+        items.sort((a, b) => b.shallowSizeDiff - a.shallowSizeDiff);
+        _tree = new VirtualTreeElement(
+            _createClassDiff, _updateClassDiff, _getChildrenClassDiff,
+            items: items, queue: _r.queue);
+        report.add(_tree.element);
+        break;
+      case HeapSnapshotTreeMode.classesTreeMap:
+        _createTreeMap(
+            report, new ClassesShallowTreeMap(this, _snapshotA), null);
+        break;
+
+      case HeapSnapshotTreeMode.classesTreeMapDiff:
+        final items = SnapshotClassDiff.from(_snapshotA, _snapshotB);
+        _createTreeMap(
+            report, new ClassesShallowDiffTreeMap(this, items), null);
+        break;
+      default:
+        break;
+    }
+    return report;
+  }
+
+  static HtmlElement _createDominator(toggle) {
+    return new DivElement()
+      ..classes = ['tree-item']
+      ..children = <Element>[
+        new SpanElement()
+          ..classes = ['percentage']
+          ..title = 'percentage of heap being retained',
+        new SpanElement()
+          ..classes = ['size']
+          ..title = 'retained size',
+        new SpanElement()..classes = ['lines'],
+        new ButtonElement()
+          ..classes = ['expander']
+          ..onClick.listen((_) => toggle(autoToggleSingleChildNodes: true)),
+        new SpanElement()..classes = ['name'],
+        new AnchorElement()
+          ..classes = ['link']
+          ..text = "[inspect]",
+        new AnchorElement()
+          ..classes = ['link']
+          ..text = "[incoming]",
+        new AnchorElement()
+          ..classes = ['link']
+          ..text = "[dominator-map]",
+      ];
+  }
+
+  static HtmlElement _createSuccessor(toggle) {
+    return new DivElement()
+      ..classes = ['tree-item']
+      ..children = <Element>[
+        new SpanElement()..classes = ['lines'],
+        new ButtonElement()
+          ..classes = ['expander']
+          ..onClick.listen((_) => toggle(autoToggleSingleChildNodes: true)),
+        new SpanElement()
+          ..classes = ['size']
+          ..title = 'retained size',
+        new SpanElement()
+          ..classes = ['edge']
+          ..title = 'name of outgoing field',
+        new SpanElement()..classes = ['name'],
+        new AnchorElement()
+          ..classes = ['link']
+          ..text = "[incoming]",
+        new AnchorElement()
+          ..classes = ['link']
+          ..text = "[dominator-tree]",
+        new AnchorElement()
+          ..classes = ['link']
+          ..text = "[dominator-map]",
+      ];
+  }
+
+  static HtmlElement _createPredecessor(toggle) {
+    return new DivElement()
+      ..classes = ['tree-item']
+      ..children = <Element>[
+        new SpanElement()..classes = ['lines'],
+        new ButtonElement()
+          ..classes = ['expander']
+          ..onClick.listen((_) => toggle(autoToggleSingleChildNodes: true)),
+        new SpanElement()
+          ..classes = ['size']
+          ..title = 'retained size',
+        new SpanElement()
+          ..classes = ['edge']
+          ..title = 'name of incoming field',
+        new SpanElement()..classes = ['name'],
+        new SpanElement()
+          ..classes = ['link']
+          ..text = "[inspect]",
+        new AnchorElement()
+          ..classes = ['link']
+          ..text = "[dominator-tree]",
+        new AnchorElement()
+          ..classes = ['link']
+          ..text = "[dominator-map]",
+      ];
+  }
+
+  static HtmlElement _createMergedDominator(toggle) {
+    return new DivElement()
+      ..classes = ['tree-item']
+      ..children = <Element>[
+        new SpanElement()
+          ..classes = ['percentage']
+          ..title = 'percentage of heap being retained',
+        new SpanElement()
+          ..classes = ['size']
+          ..title = 'retained size',
+        new SpanElement()..classes = ['lines'],
+        new ButtonElement()
+          ..classes = ['expander']
+          ..onClick.listen((_) => toggle(autoToggleSingleChildNodes: true)),
+        new SpanElement()..classes = ['name']
+      ];
+  }
+
+  static HtmlElement _createMergedDominatorDiff(toggle) {
+    return new DivElement()
+      ..classes = ['tree-item']
+      ..children = <Element>[
+        new SpanElement()
+          ..classes = ['percentage']
+          ..title = 'percentage of heap being retained',
+        new SpanElement()
+          ..classes = ['size']
+          ..title = 'retained size A',
+        new SpanElement()
+          ..classes = ['percentage']
+          ..title = 'percentage of heap being retained',
+        new SpanElement()
+          ..classes = ['size']
+          ..title = 'retained size B',
+        new SpanElement()
+          ..classes = ['size']
+          ..title = 'retained size change',
+        new SpanElement()..classes = ['lines'],
+        new ButtonElement()
+          ..classes = ['expander']
+          ..onClick.listen((_) => toggle(autoToggleSingleChildNodes: true)),
+        new SpanElement()..classes = ['name']
+      ];
+  }
+
+  static HtmlElement _createOwnership(toggle) {
+    return new DivElement()
+      ..classes = ['tree-item']
+      ..children = <Element>[
+        new SpanElement()
+          ..classes = ['percentage']
+          ..title = 'percentage of heap owned',
+        new SpanElement()
+          ..classes = ['size']
+          ..title = 'owned size',
+        new SpanElement()..classes = ['name']
+      ];
+  }
+
+  static HtmlElement _createOwnershipDiff(toggle) {
+    return new DivElement()
+      ..classes = ['tree-item']
+      ..children = <Element>[
+        new SpanElement()
+          ..classes = ['percentage']
+          ..title = 'percentage of heap owned A',
+        new SpanElement()
+          ..classes = ['size']
+          ..title = 'owned size A',
+        new SpanElement()
+          ..classes = ['percentage']
+          ..title = 'percentage of heap owned B',
+        new SpanElement()
+          ..classes = ['size']
+          ..title = 'owned size B',
+        new SpanElement()
+          ..classes = ['size']
+          ..title = 'owned size change',
+        new SpanElement()..classes = ['name']
+      ];
+  }
+
+  static HtmlElement _createClass(toggle) {
+    return new DivElement()
+      ..classes = ['tree-item']
+      ..children = <Element>[
+        new SpanElement()
+          ..classes = ['percentage']
+          ..title = 'percentage of heap owned',
+        new SpanElement()
+          ..classes = ['size']
+          ..title = 'shallow size',
+        new SpanElement()
+          ..classes = ['size']
+          ..title = 'instance count',
+        new SpanElement()..classes = ['name']
+      ];
+  }
+
+  static HtmlElement _createClassDiff(toggle) {
+    return new DivElement()
+      ..classes = ['tree-item']
+      ..children = <Element>[
+        new SpanElement()
+          ..classes = ['size']
+          ..title = 'shallow size A',
+        new SpanElement()
+          ..classes = ['size']
+          ..title = 'instance count A',
+        new SpanElement()
+          ..classes = ['size']
+          ..title = 'shallow size B',
+        new SpanElement()
+          ..classes = ['size']
+          ..title = 'instance count B',
+        new SpanElement()
+          ..classes = ['size']
+          ..title = 'shallow size diff',
+        new SpanElement()
+          ..classes = ['size']
+          ..title = 'instance count diff',
+        new SpanElement()..classes = ['name']
+      ];
+  }
+
+  static const int kMaxChildren = 100;
+  static const int kMinRetainedSize = 4096;
+
+  static Iterable _getChildrenDominator(nodeDynamic) {
+    SnapshotObject node = nodeDynamic;
+    final list = node.children
+        .where((child) => child.retainedSize >= kMinRetainedSize)
+        .toList();
+    list.sort((a, b) => b.retainedSize - a.retainedSize);
+    return list.take(kMaxChildren).toList();
+  }
+
+  static Iterable _getChildrenSuccessor(nodeDynamic) {
+    SnapshotObject node = nodeDynamic;
+    return node.successors.take(kMaxChildren).toList();
+  }
+
+  static Iterable _getChildrenPredecessor(nodeDynamic) {
+    SnapshotObject node = nodeDynamic;
+    return node.predecessors.take(kMaxChildren).toList();
+  }
+
+  static Iterable _getChildrenMergedDominator(nodeDynamic) {
+    SnapshotMergedDominator node = nodeDynamic;
+    final list = node.children
+        .where((child) => child.retainedSize >= kMinRetainedSize)
+        .toList();
+    list.sort((a, b) => b.retainedSize - a.retainedSize);
+    return list.take(kMaxChildren).toList();
+  }
+
+  static Iterable _getChildrenMergedDominatorDiff(nodeDynamic) {
+    MergedDominatorDiff node = nodeDynamic;
+    final list = node.children
+        .where((child) =>
+            child.retainedSizeA >= kMinRetainedSize ||
+            child.retainedSizeB >= kMinRetainedSize)
+        .toList();
+    list.sort((a, b) => b.retainedSizeDiff - a.retainedSizeDiff);
+    return list.take(kMaxChildren).toList();
+  }
+
+  static Iterable _getChildrenOwnership(item) {
+    return const [];
+  }
+
+  static Iterable _getChildrenOwnershipDiff(item) {
+    return const [];
+  }
+
+  static Iterable _getChildrenClass(item) {
+    return const [];
+  }
+
+  static Iterable _getChildrenClassDiff(item) {
+    return const [];
+  }
+
+  void _updateDominator(HtmlElement element, nodeDynamic, int depth) {
+    SnapshotObject node = nodeDynamic;
+    element.children[0].text = Utils.formatPercentNormalized(
+        node.retainedSize * 1.0 / _snapshotA.size);
+    element.children[1].text = Utils.formatSize(node.retainedSize);
+    _updateLines(element.children[2].children, depth);
+    if (_getChildrenDominator(node).isNotEmpty) {
+      element.children[3].text = _tree.isExpanded(node) ? '▼' : '►';
+    } else {
+      element.children[3].text = '';
+    }
+    element.children[4].text = node.description;
+    element.children[5].onClick.listen((_) {
+      selection = List.from(node.objects);
+      _mode = HeapSnapshotTreeMode.successors;
+      _r.dirty();
+    });
+    element.children[6].onClick.listen((_) {
+      selection = List.from(node.objects);
+      _mode = HeapSnapshotTreeMode.predecessors;
+      _r.dirty();
+    });
+    element.children[7].onClick.listen((_) {
+      selection = List.from(node.objects);
+      _mode = HeapSnapshotTreeMode.dominatorTreeMap;
+      _r.dirty();
+    });
+  }
+
+  void _updateSuccessor(HtmlElement element, nodeDynamic, int depth) {
+    SnapshotObject node = nodeDynamic;
+    _updateLines(element.children[0].children, depth);
+    if (_getChildrenSuccessor(node).isNotEmpty) {
+      element.children[1].text = _tree.isExpanded(node) ? '▼' : '►';
+    } else {
+      element.children[1].text = '';
+    }
+    element.children[2].text = Utils.formatSize(node.retainedSize);
+    element.children[3].text = node.label;
+    element.children[4].text = node.description;
+    element.children[5].onClick.listen((_) {
+      selection = List.from(node.objects);
+      _mode = HeapSnapshotTreeMode.predecessors;
+      _r.dirty();
+    });
+    element.children[6].onClick.listen((_) {
+      selection = List.from(node.objects);
+      _mode = HeapSnapshotTreeMode.dominatorTree;
+      _r.dirty();
+    });
+    element.children[7].onClick.listen((_) {
+      selection = List.from(node.objects);
+      _mode = HeapSnapshotTreeMode.dominatorTreeMap;
+      _r.dirty();
+    });
+  }
+
+  void _updatePredecessor(HtmlElement element, nodeDynamic, int depth) {
+    SnapshotObject node = nodeDynamic;
+    _updateLines(element.children[0].children, depth);
+    if (_getChildrenSuccessor(node).isNotEmpty) {
+      element.children[1].text = _tree.isExpanded(node) ? '▼' : '►';
+    } else {
+      element.children[1].text = '';
+    }
+    element.children[2].text = Utils.formatSize(node.retainedSize);
+    element.children[3].text = node.label;
+    element.children[4].text = node.description;
+    element.children[5].onClick.listen((_) {
+      selection = List.from(node.objects);
+      _mode = HeapSnapshotTreeMode.successors;
+      _r.dirty();
+    });
+    element.children[6].onClick.listen((_) {
+      selection = List.from(node.objects);
+      _mode = HeapSnapshotTreeMode.dominatorTree;
+      _r.dirty();
+    });
+    element.children[7].onClick.listen((_) {
+      selection = List.from(node.objects);
+      _mode = HeapSnapshotTreeMode.dominatorTreeMap;
+      _r.dirty();
+    });
+  }
+
+  void _updateMergedDominator(HtmlElement element, nodeDynamic, int depth) {
+    SnapshotMergedDominator node = nodeDynamic;
+    element.children[0].text = Utils.formatPercentNormalized(
+        node.retainedSize * 1.0 / _snapshotA.size);
+    element.children[1].text = Utils.formatSize(node.retainedSize);
+    _updateLines(element.children[2].children, depth);
+    if (_getChildrenMergedDominator(node).isNotEmpty) {
+      element.children[3].text = _tree.isExpanded(node) ? '▼' : '►';
+    } else {
+      element.children[3].text = '';
+    }
+    element.children[4]
+      ..text = '${node.instanceCount} instances of ${node.klass.name}';
+  }
+
+  void _updateMergedDominatorDiff(HtmlElement element, nodeDynamic, int depth) {
+    MergedDominatorDiff node = nodeDynamic;
+    element.children[0].text = Utils.formatPercentNormalized(
+        node.retainedSizeA * 1.0 / _snapshotA.size);
+    element.children[1].text = Utils.formatSize(node.retainedSizeA);
+    element.children[2].text = Utils.formatPercentNormalized(
+        node.retainedSizeB * 1.0 / _snapshotB.size);
+    element.children[3].text = Utils.formatSize(node.retainedSizeB);
+    element.children[4].text = (node.retainedSizeDiff > 0 ? '+' : '') +
+        Utils.formatSize(node.retainedSizeDiff);
+    element.children[4].style.color =
+        node.retainedSizeDiff > 0 ? "red" : "green";
+    _updateLines(element.children[5].children, depth);
+    if (_getChildrenMergedDominatorDiff(node).isNotEmpty) {
+      element.children[6].text = _tree.isExpanded(node) ? '▼' : '►';
+    } else {
+      element.children[6].text = '';
+    }
+    element.children[7]
+      ..text =
+          '${node.instanceCountA} → ${node.instanceCountB} instances of ${node.name}';
+  }
+
+  void _updateOwnership(HtmlElement element, nodeDynamic, int depth) {
+    SnapshotClass node = nodeDynamic;
+    element.children[0].text =
+        Utils.formatPercentNormalized(node.ownedSize * 1.0 / _snapshotA.size);
+    element.children[1].text = Utils.formatSize(node.ownedSize);
+    element.children[2].text = node.name;
+  }
+
+  void _updateOwnershipDiff(HtmlElement element, nodeDynamic, int depth) {
+    SnapshotClassDiff node = nodeDynamic;
+    element.children[0].text =
+        Utils.formatPercentNormalized(node.ownedSizeA * 1.0 / _snapshotA.size);
+    element.children[1].text = Utils.formatSize(node.ownedSizeA);
+    element.children[2].text =
+        Utils.formatPercentNormalized(node.ownedSizeB * 1.0 / _snapshotB.size);
+    element.children[3].text = Utils.formatSize(node.ownedSizeB);
+    element.children[4].text = (node.ownedSizeDiff > 0 ? "+" : "") +
+        Utils.formatSize(node.ownedSizeDiff);
+    element.children[4].style.color = node.ownedSizeDiff > 0 ? "red" : "green";
+    element.children[5].text = node.name;
+  }
+
+  void _updateClass(HtmlElement element, nodeDynamic, int depth) {
+    SnapshotClass node = nodeDynamic;
+    element.children[0].text =
+        Utils.formatPercentNormalized(node.shallowSize * 1.0 / _snapshotA.size);
+    element.children[1].text = Utils.formatSize(node.shallowSize);
+    element.children[2].text = node.instanceCount.toString();
+    element.children[3].text = node.name;
+  }
+
+  void _updateClassDiff(HtmlElement element, nodeDynamic, int depth) {
+    SnapshotClassDiff node = nodeDynamic;
+    element.children[0].text = Utils.formatSize(node.shallowSizeA);
+    element.children[1].text = node.instanceCountA.toString();
+    element.children[2].text = Utils.formatSize(node.shallowSizeB);
+    element.children[3].text = node.instanceCountB.toString();
+    element.children[4].text = (node.shallowSizeDiff > 0 ? "+" : "") +
+        Utils.formatSize(node.shallowSizeDiff);
+    element.children[4].style.color =
+        node.shallowSizeDiff > 0 ? "red" : "green";
+    element.children[5].text = (node.instanceCountDiff > 0 ? "+" : "") +
+        node.instanceCountDiff.toString();
+    element.children[5].style.color =
+        node.instanceCountDiff > 0 ? "red" : "green";
+    element.children[6].text = node.name;
+  }
+
+  static _updateLines(List<Element> lines, int n) {
+    n = Math.max(0, n);
+    while (lines.length > n) {
+      lines.removeLast();
+    }
+    while (lines.length < n) {
+      lines.add(new SpanElement());
+    }
+  }
+
+  static String modeToString(HeapSnapshotTreeMode mode) {
+    switch (mode) {
+      case HeapSnapshotTreeMode.dominatorTree:
+        return 'Dominators (tree)';
+      case HeapSnapshotTreeMode.dominatorTreeMap:
+        return 'Dominators (treemap)';
+      case HeapSnapshotTreeMode.mergedDominatorTree:
+      case HeapSnapshotTreeMode.mergedDominatorTreeDiff:
+        return 'Dominators (tree, siblings merged by class)';
+      case HeapSnapshotTreeMode.mergedDominatorTreeMap:
+      case HeapSnapshotTreeMode.mergedDominatorTreeMapDiff:
+        return 'Dominators (treemap, siblings merged by class)';
+      case HeapSnapshotTreeMode.ownershipTable:
+      case HeapSnapshotTreeMode.ownershipTableDiff:
+        return 'Ownership (table)';
+      case HeapSnapshotTreeMode.ownershipTreeMap:
+      case HeapSnapshotTreeMode.ownershipTreeMapDiff:
+        return 'Ownership (treemap)';
+      case HeapSnapshotTreeMode.classesTable:
+      case HeapSnapshotTreeMode.classesTableDiff:
+        return 'Classes (table)';
+      case HeapSnapshotTreeMode.classesTreeMap:
+      case HeapSnapshotTreeMode.classesTreeMapDiff:
+        return 'Classes (treemap)';
+      case HeapSnapshotTreeMode.successors:
+        return 'Successors / outgoing references';
+      case HeapSnapshotTreeMode.predecessors:
+        return 'Predecessors / incoming references';
+    }
+    throw new Exception('Unknown HeapSnapshotTreeMode: $mode');
+  }
+
+  List<Element> _createModeSelect() {
+    var s;
+    var modes = _snapshotA == _snapshotB ? viewModes : diffModes;
+    if (!modes.contains(_mode)) {
+      _mode = modes[0];
+      _r.dirty();
+    }
+    return [
+      s = new SelectElement()
+        ..classes = ['analysis-select']
+        ..value = modeToString(_mode)
+        ..children = modes.map((mode) {
+          return new OptionElement(
+              value: modeToString(mode), selected: _mode == mode)
+            ..text = modeToString(mode);
+        }).toList(growable: false)
+        ..onChange.listen((_) {
+          _mode = modes[s.selectedIndex];
+          _r.dirty();
+        })
+    ];
+  }
+
+  String snapshotToString(snapshot) {
+    if (snapshot == null) return "None";
+    return snapshot.description +
+        " " +
+        Utils.formatSize(snapshot.capacity + snapshot.externalSize);
+  }
+
+  List<Element> _createSnapshotSelectA() {
+    var s;
+    return [
+      s = new SelectElement()
+        ..classes = ['analysis-select']
+        ..value = snapshotToString(_snapshotA)
+        ..children = _loadedSnapshots.map((snapshot) {
+          return new OptionElement(
+              value: snapshotToString(snapshot),
+              selected: _snapshotA == snapshot)
+            ..text = snapshotToString(snapshot);
+        }).toList(growable: false)
+        ..onChange.listen((_) {
+          _snapshotA = _loadedSnapshots[s.selectedIndex];
+          selection = null;
+          mergedSelection = null;
+          mergedDiffSelection = null;
+          _r.dirty();
+        })
+    ];
+  }
+
+  List<Element> _createSnapshotSelectB() {
+    var s;
+    return [
+      s = new SelectElement()
+        ..classes = ['analysis-select']
+        ..value = snapshotToString(_snapshotB)
+        ..children = _loadedSnapshots.map((snapshot) {
+          return new OptionElement(
+              value: snapshotToString(snapshot),
+              selected: _snapshotB == snapshot)
+            ..text = snapshotToString(snapshot);
+        }).toList(growable: false)
+        ..onChange.listen((_) {
+          _snapshotB = _loadedSnapshots[s.selectedIndex];
+          selection = null;
+          mergedSelection = null;
+          mergedDiffSelection = null;
+          _r.dirty();
+        })
+    ];
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/helpers/any_ref.dart b/runtime/observatory_2/lib/src/elements/helpers/any_ref.dart
new file mode 100644
index 0000000..1bcfcea
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/helpers/any_ref.dart
@@ -0,0 +1,98 @@
+// 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.
+
+import 'dart:html';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/class_ref.dart';
+import 'package:observatory_2/src/elements/code_ref.dart';
+import 'package:observatory_2/src/elements/context_ref.dart';
+import 'package:observatory_2/src/elements/error_ref.dart';
+import 'package:observatory_2/src/elements/field_ref.dart';
+import 'package:observatory_2/src/elements/function_ref.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/uris.dart';
+import 'package:observatory_2/src/elements/icdata_ref.dart';
+import 'package:observatory_2/src/elements/instance_ref.dart';
+import 'package:observatory_2/src/elements/library_ref.dart';
+import 'package:observatory_2/src/elements/local_var_descriptors_ref.dart';
+import 'package:observatory_2/src/elements/megamorphiccache_ref.dart';
+import 'package:observatory_2/src/elements/objectpool_ref.dart';
+import 'package:observatory_2/src/elements/pc_descriptors_ref.dart';
+import 'package:observatory_2/src/elements/script_ref.dart';
+import 'package:observatory_2/src/elements/sentinel_value.dart';
+import 'package:observatory_2/src/elements/singletargetcache_ref.dart';
+import 'package:observatory_2/src/elements/subtypetestcache_ref.dart';
+import 'package:observatory_2/src/elements/type_arguments_ref.dart';
+import 'package:observatory_2/src/elements/unknown_ref.dart';
+import 'package:observatory_2/src/elements/unlinkedcall_ref.dart';
+
+Element anyRef(M.IsolateRef isolate, ref, M.ObjectRepository objects,
+    {RenderingQueue queue, bool expandable: true}) {
+  if (ref == null) {
+    return new SpanElement()..text = "???";
+  }
+  if (ref is M.Guarded) {
+    if (ref.isSentinel) {
+      return anyRef(isolate, ref.asSentinel, objects,
+          queue: queue, expandable: expandable);
+    } else {
+      return anyRef(isolate, ref.asValue, objects,
+          queue: queue, expandable: expandable);
+    }
+  } else if (ref is M.ObjectRef) {
+    if (ref is M.ClassRef) {
+      return new ClassRefElement(isolate, ref, queue: queue).element;
+    } else if (ref is M.CodeRef) {
+      return new CodeRefElement(isolate, ref, queue: queue).element;
+    } else if (ref is M.ContextRef) {
+      return new ContextRefElement(isolate, ref, objects,
+              queue: queue, expandable: expandable)
+          .element;
+    } else if (ref is M.Error) {
+      return new ErrorRefElement(ref, queue: queue).element;
+    } else if (ref is M.FieldRef) {
+      return new FieldRefElement(isolate, ref, objects,
+              queue: queue, expandable: expandable)
+          .element;
+    } else if (ref is M.FunctionRef) {
+      return new FunctionRefElement(isolate, ref, queue: queue).element;
+    } else if (ref is M.ICDataRef) {
+      return new ICDataRefElement(isolate, ref, queue: queue).element;
+    } else if (ref is M.InstanceRef) {
+      return new InstanceRefElement(isolate, ref, objects,
+              queue: queue, expandable: expandable)
+          .element;
+    } else if (ref is M.LibraryRef) {
+      return new LibraryRefElement(isolate, ref, queue: queue).element;
+    } else if (ref is M.LocalVarDescriptorsRef) {
+      return new LocalVarDescriptorsRefElement(isolate, ref, queue: queue)
+          .element;
+    } else if (ref is M.MegamorphicCacheRef) {
+      return new MegamorphicCacheRefElement(isolate, ref, queue: queue).element;
+    } else if (ref is M.ObjectPoolRef) {
+      return new ObjectPoolRefElement(isolate, ref, queue: queue).element;
+    } else if (ref is M.PcDescriptorsRef) {
+      return new PcDescriptorsRefElement(isolate, ref, queue: queue).element;
+    } else if (ref is M.ScriptRef) {
+      return new ScriptRefElement(isolate, ref, queue: queue).element;
+    } else if (ref is M.SingleTargetCacheRef) {
+      return new SingleTargetCacheRefElement(isolate, ref, queue: queue)
+          .element;
+    } else if (ref is M.SubtypeTestCacheRef) {
+      return new SubtypeTestCacheRefElement(isolate, ref, queue: queue).element;
+    } else if (ref is M.TypeArgumentsRef) {
+      return new TypeArgumentsRefElement(isolate, ref, queue: queue).element;
+    } else if (ref is M.UnknownObjectRef) {
+      return new UnknownObjectRefElement(isolate, ref, queue: queue).element;
+    } else if (ref is M.UnlinkedCallRef) {
+      return new UnlinkedCallRefElement(isolate, ref, queue: queue).element;
+    } else {
+      return new AnchorElement(href: Uris.inspect(isolate, object: ref))
+        ..text = 'object';
+    }
+  } else if (ref is M.Sentinel) {
+    return new SentinelValueElement(ref, queue: queue).element;
+  }
+  throw new Exception('Unknown ref type (${ref.runtimeType})');
+}
diff --git a/runtime/observatory_2/lib/src/elements/helpers/custom_element.dart b/runtime/observatory_2/lib/src/elements/helpers/custom_element.dart
new file mode 100644
index 0000000..2af0d6c
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/helpers/custom_element.dart
@@ -0,0 +1,87 @@
+// Copyright (c) 2016, 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.
+
+import 'dart:async';
+import 'dart:html';
+
+HtmlElement element(CustomElement e) => e.element;
+
+class CustomElement {
+  static Expando reverseElements = new Expando();
+  static CustomElement reverse(HtmlElement element) => reverseElements[element];
+
+  static List<CustomElement> toBeAttached = <CustomElement>[];
+  static void drainAttached() {
+    // Send 'attached' to elements that have been attached to the document.
+    bool fired = false;
+    var connectedElements = toBeAttached
+        .where((CustomElement element) => element.element.isConnected)
+        .toList();
+    for (CustomElement element in connectedElements) {
+      toBeAttached.remove(element);
+      element.attached();
+      fired = true;
+    }
+
+    if (toBeAttached.isEmpty) {
+      return; // Done.
+    }
+
+    if (fired) {
+      // The 'attached' events above may have scheduled microtasks that will
+      // will add more CustomElements to be document, e.g. 'render'.
+      scheduleMicrotask(() => drainAttached());
+    }
+
+    while (!toBeAttached.isEmpty) {
+      // Either this element will never be attached or it will be attached
+      // after a turn of the outer event loop. Fire 'attached' in case it is
+      // the latter, since firing it out of order is preferrable to not firing
+      // it at all.
+      CustomElement element = toBeAttached.removeLast();
+      print("Warning: created but not in document: $element");
+      element.attached();
+    }
+  }
+
+  final HtmlElement element;
+  CustomElement.created(String elementClass)
+      : element = document.createElement("shadow") {
+    reverseElements[element] = this;
+    element.classes = [elementClass];
+
+    if (toBeAttached.isEmpty) {
+      scheduleMicrotask(() => drainAttached());
+    }
+    toBeAttached.add(this);
+  }
+
+  void attached() {}
+  void detached() {}
+
+  Element get parent => element.parent;
+
+  List<Element> get children => element.children;
+  set children(List<Element> c) => element.children = c;
+
+  CssClassSet get classes => element.classes;
+  set classes(dynamic c) => element.classes = c;
+
+  String get title => element.title;
+  set title(String t) => element.title = t;
+
+  String get text => element.text;
+  set text(String t) => element.text = t;
+
+  CssStyleDeclaration get style => element.style;
+
+  ElementStream<MouseEvent> get onClick => element.onClick;
+
+  Rectangle getBoundingClientRect() => element.getBoundingClientRect();
+
+  List<Node> getElementsByClassName(String c) =>
+      element.getElementsByClassName(c);
+
+  void scrollIntoView() => element.scrollIntoView();
+}
diff --git a/runtime/observatory_2/lib/src/elements/helpers/nav_bar.dart b/runtime/observatory_2/lib/src/elements/helpers/nav_bar.dart
new file mode 100644
index 0000000..050041b
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/helpers/nav_bar.dart
@@ -0,0 +1,14 @@
+// Copyright (c) 2016, 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.
+
+import 'dart:html';
+
+Element navBar(Iterable<Element> content) {
+  assert(content != null);
+  return document.createElement('nav')
+    ..classes = ['nav-bar']
+    ..children = <Element>[
+      new UListElement()..children = content,
+    ];
+}
diff --git a/runtime/observatory_2/lib/src/elements/helpers/nav_menu.dart b/runtime/observatory_2/lib/src/elements/helpers/nav_menu.dart
new file mode 100644
index 0000000..02cf8ef
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/helpers/nav_menu.dart
@@ -0,0 +1,20 @@
+// Copyright (c) 2016, 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.
+
+import 'dart:html';
+
+navMenu(String label, {String link, Iterable<Element> content: const []}) {
+  assert(label != null);
+  assert(content != null);
+  return new LIElement()
+    ..classes = ['nav-menu']
+    ..children = <Element>[
+      new SpanElement()
+        ..classes = ['nav-menu_label']
+        ..children = <Element>[
+          new AnchorElement(href: link)..text = label,
+          new UListElement()..children = content
+        ]
+    ];
+}
diff --git a/runtime/observatory_2/lib/src/elements/helpers/rendering_queue.dart b/runtime/observatory_2/lib/src/elements/helpers/rendering_queue.dart
new file mode 100644
index 0000000..e244619
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/helpers/rendering_queue.dart
@@ -0,0 +1,87 @@
+// Copyright (c) 2016, 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.
+
+import 'dart:html';
+import 'dart:collection';
+import 'dart:async';
+
+/// A generic rendering task that can be scheduled.
+abstract class RenderingTask {
+  /// Rendering synchronous callback.
+  void render();
+}
+
+/// A generic synchronization system for rendering operations.
+abstract class RenderingBarrier {
+  /// Future to the next synchronization barrier (ms from application start).
+  Future<num> get next;
+}
+
+/// Synchronization system based on the AnimationFrame.
+class NextAnimationFrameBarrier implements RenderingBarrier {
+  Future<num> get next => window.animationFrame;
+}
+
+/// MOCK synchronization system for manual barrier triggering.
+class RenderingBarrierMock implements RenderingBarrier {
+  final StreamController<num> _stream = new StreamController<num>.broadcast();
+  num _ms = 0;
+
+  Future<num> get next => _stream.stream.first;
+
+  /// Trigger the next barrier with an optional numer of ms elapsed.
+  void triggerRenderingBarrier({num step: 20}) {
+    assert(step != null);
+    _stream.add(_ms += step);
+  }
+}
+
+/// RenderingTask queuing and synchronization system.
+class RenderingQueue {
+  final RenderingBarrier _barrier;
+  final Queue<RenderingTask> _queue = new Queue<RenderingTask>();
+
+  bool get isEmpty => _queue.isEmpty;
+  bool get isNotEmpty => _queue.isNotEmpty;
+
+  /// Creates a RenderingQueue with the default synchronization barrier.
+  RenderingQueue() : this.fromBarrier(new NextAnimationFrameBarrier());
+
+  /// Creates a RenderingQueue with a custom synchronization barrier.
+  RenderingQueue.fromBarrier(this._barrier) {
+    assert(this._barrier != null);
+  }
+
+  /// Add a task to the queue.
+  /// If the current rendering phase is running it will be executed during this
+  /// rendering cycle, otherwise it will be queued for the next one.
+  void enqueue(RenderingTask r, {bool waitForBarrier: true}) {
+    assert(r != null);
+    final wasEmpty = _queue.isEmpty;
+    _queue.addLast(r);
+    // If no task are in the queue there is no rendering phase scheduled.
+    if (wasEmpty) {
+      if (waitForBarrier) {
+        _render();
+      } else {
+        // We schedule the _renderLoop as a microtask to allow the
+        // scheduleRendering method to terminate, due to the fact that it is
+        // generally invoked from inside a HtmlElement.attached method
+        scheduleMicrotask(_renderLoop);
+      }
+    }
+  }
+
+  Future _render() async {
+    await _barrier.next;
+    _renderLoop();
+  }
+
+  void _renderLoop() {
+    while (_queue.isNotEmpty) {
+      _queue.first.render();
+      _queue.removeFirst();
+    }
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/helpers/rendering_scheduler.dart b/runtime/observatory_2/lib/src/elements/helpers/rendering_scheduler.dart
new file mode 100644
index 0000000..107664b0
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/helpers/rendering_scheduler.dart
@@ -0,0 +1,141 @@
+// Copyright (c) 2016, 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.
+
+import 'dart:async';
+
+import 'package:observatory_2/src/elements/helpers/rendering_queue.dart';
+export 'package:observatory_2/src/elements/helpers/rendering_queue.dart';
+
+/// A generic renderable object.
+abstract class Renderable {
+  void render();
+}
+
+/// Event related to a Renderable rendering phase.
+class RenderedEvent<T> {
+  /// Renderable to which the event is related
+  final T element;
+
+  /// Is another rendering scheduled for this element.
+  final bool otherRenderScheduled;
+
+  RenderedEvent(this.element, this.otherRenderScheduled) {
+    assert(element != null);
+    assert(otherRenderScheduled != null);
+  }
+}
+
+/// Scheduler for rendering operations.
+class RenderingScheduler<T extends Renderable> implements RenderingTask {
+  bool _enabled = false;
+  bool _dirty = false;
+  bool _renderingScheduled = false;
+  bool _notificationScheduled = false;
+  bool _waitForBarrier = false;
+
+  /// Element managed by this scheduler.
+  final T element;
+
+  /// Queue used for rendering operations.
+  final RenderingQueue queue;
+
+  final List<Future> _wait = <Future>[];
+
+  /// Does the element need a new rendering cycle.
+  bool get isDirty => _dirty;
+
+  /// Is the scheduler enabled.
+  bool get isEnabled => _enabled;
+
+  final StreamController<RenderedEvent<T>> _onRendered =
+      new StreamController<RenderedEvent<T>>.broadcast();
+  Stream<RenderedEvent<T>> get onRendered => _onRendered.stream;
+
+  /// Creates a new scheduler for an element.
+  /// If no queue is provided it will create a new default configured queue.
+  factory RenderingScheduler(T element, {RenderingQueue queue}) {
+    assert(element != null);
+    if (queue == null) {
+      queue = new RenderingQueue();
+    }
+    return new RenderingScheduler<T>._(element, queue);
+  }
+
+  RenderingScheduler._(this.element, this.queue);
+
+  /// Enable the scheduler.
+  /// New dirty or schedule request will be considered.
+  void enable() {
+    if (_enabled) return;
+    _enabled = true;
+    scheduleRendering();
+  }
+
+  /// Disable the scheduler.
+  /// New dirty or schedule request will be discarded.
+  /// [optional] notify: send a final RenderEvent.
+  void disable({bool notify: false}) {
+    assert(notify != null);
+    if (!_enabled) return;
+    _enabled = false;
+    if (notify) scheduleNotification();
+  }
+
+  /// Set the object as dirty. A rendering will be scheduled.
+  void dirty() {
+    if (_dirty) return;
+    _dirty = true;
+    scheduleRendering();
+  }
+
+  /// Checks for modification during attribute set.
+  /// If value changes a new rendering is scheduled.
+  /// set attr(T v) => _attr = _r.checkAndReact(_attr, v);
+  T checkAndReact<T>(T oldValue, T newValue) {
+    if (oldValue != newValue)
+      dirty();
+    else
+      scheduleNotification();
+    return newValue;
+  }
+
+  /// Schedules a new rendering phase.
+  void scheduleRendering() {
+    if (_renderingScheduled) return;
+    if (!_enabled) return;
+    queue.enqueue(this, waitForBarrier: _waitForBarrier);
+    _waitForBarrier = true;
+    _renderingScheduled = true;
+  }
+
+  /// Renders the element (if the scheduler is enabled).
+  /// It will clear the dirty flag.
+  void render() {
+    _renderingScheduled = false;
+    if (!_enabled) return;
+    _dirty = false;
+    _wait.clear();
+    element.render();
+    scheduleNotification();
+    if (_dirty) scheduleRendering();
+  }
+
+  /// Schedules a notification.
+  void scheduleNotification() {
+    if (_notificationScheduled) return;
+    _notify();
+    _notificationScheduled = true;
+  }
+
+  void waitFor(Iterable<Future> it) {
+    _wait.addAll(it);
+  }
+
+  Future _notify() async {
+    await Future.wait(_wait);
+    _wait.clear();
+    _onRendered.add(new RenderedEvent<T>(element, _dirty));
+    _notificationScheduled = false;
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/helpers/tag.dart b/runtime/observatory_2/lib/src/elements/helpers/tag.dart
new file mode 100644
index 0000000..65c269a
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/helpers/tag.dart
@@ -0,0 +1,97 @@
+// Copyright (c) 2016, 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.
+
+import 'dart:async';
+import 'dart:html';
+
+HtmlElement element(CustomElement e) => e.element;
+
+class CustomElement {
+  static Expando reverseElements = new Expando();
+  static CustomElement reverse(HtmlElement element) => reverseElements[element];
+
+  static List<CustomElement> toBeAttached = <CustomElement>[];
+  static void drainAttached() {
+    // Send 'attached' to elements that have been attached to the document.
+    bool fired = false;
+    var connectedElements = toBeAttached
+        .where((CustomElement element) => element.element.isConnected)
+        .toList();
+    for (CustomElement element in connectedElements) {
+      toBeAttached.remove(element);
+      element.attached();
+      fired = true;
+    }
+
+    if (toBeAttached.isEmpty) {
+      return; // Done.
+    }
+
+    if (fired) {
+      // The 'attached' events above may have scheduled microtasks that will
+      // will add more CustomElements to be document, e.g. 'render'.
+      scheduleMicrotask(() => drainAttached());
+    }
+
+    while (!toBeAttached.isEmpty) {
+      // Either this element will never be attached or it will be attached
+      // after a turn of the outer event loop. Fire 'attached' in case it is
+      // the latter, since firing it out of order is preferrable to not firing
+      // it at all.
+      CustomElement element = toBeAttached.removeLast();
+      print("Warning: created but not in document: $element");
+      element.attached();
+    }
+  }
+
+  final HtmlElement element;
+  CustomElement.created(Tag tag) : element = document.createElement("shadow") {
+    reverseElements[element] = this;
+    element.classes = [tag.name];
+
+    if (toBeAttached.isEmpty) {
+      scheduleMicrotask(() => drainAttached());
+    }
+    toBeAttached.add(this);
+  }
+
+  void attached() {}
+  void detached() {}
+
+  Element get parent => element.parent;
+
+  List<Element> get children => element.children;
+  set children(List<Element> c) => element.children = c;
+
+  CssClassSet get classes => element.classes;
+  set classes(dynamic c) => element.classes = c;
+
+  String get title => element.title;
+  set title(String t) => element.title = t;
+
+  String get text => element.text;
+  set text(String t) => element.text = t;
+
+  CssStyleDeclaration get style => element.style;
+
+  ElementStream<MouseEvent> get onClick => element.onClick;
+
+  Rectangle getBoundingClientRect() => element.getBoundingClientRect();
+
+  List<Node> getElementsByClassName(String c) =>
+      element.getElementsByClassName(c);
+
+  void scrollIntoView() => element.scrollIntoView();
+}
+
+/// Utility class for Custom Tags registration.
+class Tag<T extends CustomElement> {
+  /// Tag name.
+  final String name;
+
+  /// Dependent tags that need to be registred for this tag to work properly.
+  final Iterable<Tag> dependencies;
+
+  const Tag(this.name, {this.dependencies: const []});
+}
diff --git a/runtime/observatory_2/lib/src/elements/helpers/uris.dart b/runtime/observatory_2/lib/src/elements/helpers/uris.dart
new file mode 100644
index 0000000..c9e3ade
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/helpers/uris.dart
@@ -0,0 +1,52 @@
+// Copyright (c) 2016, 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.
+
+import 'package:observatory_2/models.dart' as M;
+
+/// Utility class for URIs generation.
+abstract class Uris {
+  static String _isolatePage(String path, M.IsolateRef isolate,
+      {M.ObjectRef object}) {
+    final parameters = {'isolateId': isolate.id};
+    if (object != null) parameters['objectId'] = object.id;
+    return '#' + new Uri(path: path, queryParameters: parameters).toString();
+  }
+
+  static String allocationProfiler(M.IsolateRef isolate) =>
+      _isolatePage('/allocation-profiler', isolate);
+  static String classTree(M.IsolateRef isolate) =>
+      _isolatePage('/class-tree', isolate);
+  static String cpuProfiler(M.IsolateRef isolate) =>
+      _isolatePage('/profiler', isolate);
+  static String cpuProfilerTable(M.IsolateRef isolate) =>
+      _isolatePage('/profiler-table', isolate);
+  static String debugger(M.IsolateRef isolate) =>
+      _isolatePage('/debugger', isolate);
+  static String flags() => '#/flags';
+  static String heapMap(M.IsolateRef isolate) =>
+      _isolatePage('/heap-map', isolate);
+  static String heapSnapshot(M.IsolateRef isolate) =>
+      _isolatePage('/heap-snapshot', isolate);
+  static String inspect(M.IsolateRef isolate, {M.ObjectRef object, int pos}) {
+    if (pos == null) {
+      return _isolatePage('/inspect', isolate, object: object);
+    }
+    return _isolatePage('/inspect', isolate, object: object) + '---pos=${pos}';
+  }
+
+  static String logging(M.IsolateRef isolate) =>
+      _isolatePage('/logging', isolate);
+  static String metrics(M.IsolateRef isolate) =>
+      _isolatePage('/metrics', isolate);
+  static String nativeMemory() => '#/native-memory-profile';
+  static String processSnapshot() => '#/process-snapshot';
+  static String objectStore(M.IsolateRef isolate) =>
+      _isolatePage('/object-store', isolate);
+  static String persistentHandles(M.IsolateRef isolate) =>
+      _isolatePage('/persistent-handles', isolate);
+  static String ports(M.IsolateRef isolate) => _isolatePage('/ports', isolate);
+  static String timeline() => '#/timeline';
+  static String vm() => '#/vm';
+  static String vmConnect() => '#/vm-connect';
+}
diff --git a/runtime/observatory_2/lib/src/elements/icdata_ref.dart b/runtime/observatory_2/lib/src/elements/icdata_ref.dart
new file mode 100644
index 0000000..1ee4082
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/icdata_ref.dart
@@ -0,0 +1,60 @@
+// 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.
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/models.dart' as M show IsolateRef, ICDataRef;
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/helpers/uris.dart';
+
+class ICDataRefElement extends CustomElement implements Renderable {
+  RenderingScheduler<ICDataRefElement> _r;
+
+  Stream<RenderedEvent<ICDataRefElement>> get onRendered => _r.onRendered;
+
+  M.IsolateRef _isolate;
+  M.ICDataRef _icdata;
+
+  M.IsolateRef get isolate => _isolate;
+  M.ICDataRef get icdata => _icdata;
+
+  factory ICDataRefElement(M.IsolateRef isolate, M.ICDataRef icdata,
+      {RenderingQueue queue}) {
+    assert(isolate != null);
+    assert(icdata != null);
+    ICDataRefElement e = new ICDataRefElement.created();
+    e._r = new RenderingScheduler<ICDataRefElement>(e, queue: queue);
+    e._isolate = isolate;
+    e._icdata = icdata;
+    return e;
+  }
+
+  ICDataRefElement.created() : super.created('icdata-ref');
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+  }
+
+  void render() {
+    children = <Element>[
+      new AnchorElement(href: Uris.inspect(_isolate, object: _icdata))
+        ..children = <Element>[
+          new SpanElement()
+            ..classes = ['emphasize']
+            ..text = 'ICData',
+          new SpanElement()..text = ' (${_icdata.selector})'
+        ]
+    ];
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/icdata_view.dart b/runtime/observatory_2/lib/src/elements/icdata_view.dart
new file mode 100644
index 0000000..8018897
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/icdata_view.dart
@@ -0,0 +1,189 @@
+// 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.
+
+import 'dart:async';
+import 'dart:html';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/curly_block.dart';
+import 'package:observatory_2/src/elements/helpers/any_ref.dart';
+import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
+import 'package:observatory_2/src/elements/helpers/nav_menu.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/nav/isolate_menu.dart';
+import 'package:observatory_2/src/elements/nav/notify.dart';
+import 'package:observatory_2/src/elements/nav/refresh.dart';
+import 'package:observatory_2/src/elements/nav/top_menu.dart';
+import 'package:observatory_2/src/elements/nav/vm_menu.dart';
+import 'package:observatory_2/src/elements/object_common.dart';
+import 'package:observatory_2/src/elements/view_footer.dart';
+
+class ICDataViewElement extends CustomElement implements Renderable {
+  RenderingScheduler<ICDataViewElement> _r;
+
+  Stream<RenderedEvent<ICDataViewElement>> get onRendered => _r.onRendered;
+
+  M.VM _vm;
+  M.IsolateRef _isolate;
+  M.EventRepository _events;
+  M.NotificationRepository _notifications;
+  M.ICData _icdata;
+  M.ICDataRepository _icdatas;
+  M.RetainedSizeRepository _retainedSizes;
+  M.ReachableSizeRepository _reachableSizes;
+  M.InboundReferencesRepository _references;
+  M.RetainingPathRepository _retainingPaths;
+  M.ObjectRepository _objects;
+
+  M.VMRef get vm => _vm;
+  M.IsolateRef get isolate => _isolate;
+  M.NotificationRepository get notifications => _notifications;
+  M.ICData get icdata => _icdata;
+
+  factory ICDataViewElement(
+      M.VM vm,
+      M.IsolateRef isolate,
+      M.ICData icdata,
+      M.EventRepository events,
+      M.NotificationRepository notifications,
+      M.ICDataRepository icdatas,
+      M.RetainedSizeRepository retainedSizes,
+      M.ReachableSizeRepository reachableSizes,
+      M.InboundReferencesRepository references,
+      M.RetainingPathRepository retainingPaths,
+      M.ObjectRepository objects,
+      {RenderingQueue queue}) {
+    assert(vm != null);
+    assert(isolate != null);
+    assert(events != null);
+    assert(notifications != null);
+    assert(icdata != null);
+    assert(icdatas != null);
+    assert(retainedSizes != null);
+    assert(reachableSizes != null);
+    assert(references != null);
+    assert(retainingPaths != null);
+    assert(objects != null);
+    ICDataViewElement e = new ICDataViewElement.created();
+    e._r = new RenderingScheduler<ICDataViewElement>(e, queue: queue);
+    e._vm = vm;
+    e._isolate = isolate;
+    e._events = events;
+    e._notifications = notifications;
+    e._icdata = icdata;
+    e._icdatas = icdatas;
+    e._retainedSizes = retainedSizes;
+    e._reachableSizes = reachableSizes;
+    e._references = references;
+    e._retainingPaths = retainingPaths;
+    e._objects = objects;
+    return e;
+  }
+
+  ICDataViewElement.created() : super.created('icdata-view');
+
+  @override
+  attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+  }
+
+  void render() {
+    children = <Element>[
+      navBar(<Element>[
+        new NavTopMenuElement(queue: _r.queue).element,
+        new NavVMMenuElement(_vm, _events, queue: _r.queue).element,
+        new NavIsolateMenuElement(_isolate, _events, queue: _r.queue).element,
+        navMenu('icdata'),
+        (new NavRefreshElement(queue: _r.queue)
+              ..onRefresh.listen((e) async {
+                e.element.disabled = true;
+                _icdata = await _icdatas.get(_isolate, _icdata.id);
+                _r.dirty();
+              }))
+            .element,
+        new NavNotifyElement(_notifications, queue: _r.queue).element
+      ]),
+      new DivElement()
+        ..classes = ['content-centered-big']
+        ..children = <Element>[
+          new HeadingElement.h2()..text = 'ICData',
+          new HRElement(),
+          new ObjectCommonElement(_isolate, _icdata, _retainedSizes,
+                  _reachableSizes, _references, _retainingPaths, _objects,
+                  queue: _r.queue)
+              .element,
+          new DivElement()
+            ..classes = ['memberList']
+            ..children = <Element>[
+              new DivElement()
+                ..classes = ['memberItem']
+                ..children = <Element>[
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..text = 'selector',
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..text = _icdata.selector
+                ],
+              new DivElement()
+                ..classes = ['memberItem']
+                ..children = <Element>[
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..text = 'owner',
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..children = <Element>[
+                      _icdata.dartOwner == null
+                          ? (new SpanElement()..text = '<none>')
+                          : anyRef(_isolate, _icdata.dartOwner, _objects,
+                              queue: _r.queue)
+                    ]
+                ],
+              new DivElement()
+                ..classes = ['memberItem']
+                ..children = <Element>[
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..text = 'argumentsDescriptor',
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..children = <Element>[
+                      _icdata.argumentsDescriptor == null
+                          ? (new SpanElement()..text = '<none>')
+                          : anyRef(
+                              _isolate, _icdata.argumentsDescriptor, _objects,
+                              queue: _r.queue)
+                    ]
+                ],
+              new DivElement()
+                ..classes = ['memberItem']
+                ..children = <Element>[
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..text = 'entries',
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..children = <Element>[
+                      _icdata.entries == null
+                          ? (new SpanElement()..text = '<none>')
+                          : anyRef(_isolate, _icdata.entries, _objects,
+                              queue: _r.queue)
+                    ]
+                ]
+            ],
+          new HRElement(),
+          new ViewFooterElement(queue: _r.queue).element
+        ]
+    ];
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/img/chromium_icon.png b/runtime/observatory_2/lib/src/elements/img/chromium_icon.png
new file mode 100644
index 0000000..a467bca
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/img/chromium_icon.png
Binary files differ
diff --git a/runtime/observatory_2/lib/src/elements/img/dart_icon.png b/runtime/observatory_2/lib/src/elements/img/dart_icon.png
new file mode 100644
index 0000000..6341f65
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/img/dart_icon.png
Binary files differ
diff --git a/runtime/observatory_2/lib/src/elements/img/isolate_icon.png b/runtime/observatory_2/lib/src/elements/img/isolate_icon.png
new file mode 100644
index 0000000..a739a54
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/img/isolate_icon.png
Binary files differ
diff --git a/runtime/observatory_2/lib/src/elements/inbound_references.dart b/runtime/observatory_2/lib/src/elements/inbound_references.dart
new file mode 100644
index 0000000..2f93dc8
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/inbound_references.dart
@@ -0,0 +1,118 @@
+// Copyright (c) 2016, 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.
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/curly_block.dart';
+import 'package:observatory_2/src/elements/instance_ref.dart';
+import 'package:observatory_2/src/elements/helpers/any_ref.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+
+class InboundReferencesElement extends CustomElement implements Renderable {
+  RenderingScheduler<InboundReferencesElement> _r;
+
+  Stream<RenderedEvent<InboundReferencesElement>> get onRendered =>
+      _r.onRendered;
+
+  M.IsolateRef _isolate;
+  M.ObjectRef _object;
+  M.InboundReferencesRepository _references;
+  M.ObjectRepository _objects;
+  M.InboundReferences _inbounds;
+  bool _expanded = false;
+
+  M.IsolateRef get isolate => _isolate;
+  M.ObjectRef get object => _object;
+
+  factory InboundReferencesElement(M.IsolateRef isolate, M.ObjectRef object,
+      M.InboundReferencesRepository references, M.ObjectRepository objects,
+      {RenderingQueue queue}) {
+    assert(isolate != null);
+    assert(object != null);
+    assert(references != null);
+    assert(objects != null);
+    InboundReferencesElement e = new InboundReferencesElement.created();
+    e._r = new RenderingScheduler<InboundReferencesElement>(e, queue: queue);
+    e._isolate = isolate;
+    e._object = object;
+    e._references = references;
+    e._objects = objects;
+    return e;
+  }
+
+  InboundReferencesElement.created() : super.created('inbound-references');
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    children = <Element>[];
+    _r.disable(notify: true);
+  }
+
+  void render() {
+    final curlyBlock =
+        new CurlyBlockElement(expanded: _expanded, queue: _r.queue)
+          ..content = _createContent()
+          ..onToggle.listen((e) async {
+            _expanded = e.control.expanded;
+            if (_expanded) {
+              e.control.disabled = true;
+              await _refresh();
+              e.control.disabled = false;
+            }
+          });
+    children = <Element>[curlyBlock.element];
+    _r.waitFor([curlyBlock.onRendered.first]);
+  }
+
+  Future _refresh() async {
+    _inbounds = await _references.get(_isolate, _object.id);
+    _r.dirty();
+  }
+
+  List<Element> _createContent() {
+    if (_inbounds == null) {
+      return const [];
+    }
+    return _inbounds.elements.map<Element>(_createItem).toList();
+  }
+
+  Element _createItem(M.InboundReference reference) {
+    final content = <Element>[];
+
+    if (reference.parentField != null) {
+      content.addAll([
+        new SpanElement()..text = 'referenced by ',
+        anyRef(_isolate, reference.parentField, _objects, queue: _r.queue),
+        new SpanElement()..text = ' of '
+      ]);
+    } else if (reference.parentListIndex != null) {
+      content.add(new SpanElement()
+        ..text = 'referenced by [ ${reference.parentListIndex} ] of ');
+    } else if (reference.parentWordOffset != null) {
+      content.add(new SpanElement()
+        ..text = 'referenced by offset ${reference.parentWordOffset} of ');
+    }
+
+    content.addAll([
+      anyRef(_isolate, reference.source, _objects, queue: _r.queue),
+      new InboundReferencesElement(
+              _isolate, reference.source, _references, _objects,
+              queue: _r.queue)
+          .element
+    ]);
+
+    return new DivElement()
+      ..classes = ['indent']
+      ..children = content;
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/instance_ref.dart b/runtime/observatory_2/lib/src/elements/instance_ref.dart
new file mode 100644
index 0000000..51f3616
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/instance_ref.dart
@@ -0,0 +1,342 @@
+// 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.
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/curly_block.dart';
+import 'package:observatory_2/src/elements/field_ref.dart';
+import 'package:observatory_2/src/elements/helpers/any_ref.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/helpers/uris.dart';
+import 'package:observatory_2/utils.dart';
+
+class InstanceRefElement extends CustomElement implements Renderable {
+  RenderingScheduler<InstanceRefElement> _r;
+
+  Stream<RenderedEvent<InstanceRefElement>> get onRendered => _r.onRendered;
+
+  M.IsolateRef _isolate;
+  M.InstanceRef _instance;
+  M.ObjectRepository _objects;
+  M.Instance _loadedInstance;
+  bool _expandable;
+  bool _expanded = false;
+
+  M.IsolateRef get isolate => _isolate;
+  M.InstanceRef get instance => _instance;
+
+  factory InstanceRefElement(
+      M.IsolateRef isolate, M.InstanceRef instance, M.ObjectRepository objects,
+      {RenderingQueue queue, bool expandable: true}) {
+    assert(isolate != null);
+    assert(instance != null);
+    assert(objects != null);
+    InstanceRefElement e = new InstanceRefElement.created();
+    e._r = new RenderingScheduler<InstanceRefElement>(e, queue: queue);
+    e._isolate = isolate;
+    e._instance = instance;
+    e._objects = objects;
+    e._expandable = expandable;
+    return e;
+  }
+
+  InstanceRefElement.created() : super.created('instance-ref');
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    children = <Element>[];
+    _r.disable(notify: true);
+  }
+
+  void render() {
+    final content = _createLink();
+
+    if (_expandable && _hasValue()) {
+      content.addAll([
+        new SpanElement()..text = ' ',
+        (new CurlyBlockElement(expanded: _expanded, queue: _r.queue)
+              ..content = <Element>[
+                new DivElement()
+                  ..classes = ['indent']
+                  ..children = _createValue()
+              ]
+              ..onToggle.listen((e) async {
+                _expanded = e.control.expanded;
+                if (_expanded) {
+                  e.control.disabled = true;
+                  await _refresh();
+                  e.control.disabled = false;
+                }
+              }))
+            .element
+      ]);
+    }
+
+    children = content;
+  }
+
+  Future _refresh() async {
+    _loadedInstance = await _objects.get(_isolate, _instance.id);
+    _r.dirty();
+  }
+
+  List<Element> _createShowMoreButton() {
+    if (_loadedInstance.count == null) {
+      return [];
+    }
+    final count = _loadedInstance.count;
+    final button = new ButtonElement()..text = 'show next ${count}';
+    button.onClick.listen((_) async {
+      button.disabled = true;
+      _loadedInstance = await _objects.get(_isolate, _instance.id);
+      _r.dirty();
+    });
+    return [button];
+  }
+
+  List<Element> _createLink() {
+    switch (_instance.kind) {
+      case M.InstanceKind.vNull:
+      case M.InstanceKind.bool:
+      case M.InstanceKind.int:
+      case M.InstanceKind.double:
+      case M.InstanceKind.float32x4:
+      case M.InstanceKind.float64x2:
+      case M.InstanceKind.int32x4:
+        return [
+          new AnchorElement(href: Uris.inspect(_isolate, object: _instance))
+            ..text = _instance.valueAsString
+        ];
+      case M.InstanceKind.string:
+        return [
+          new AnchorElement(href: Uris.inspect(_isolate, object: _instance))
+            ..text = Utils.formatStringAsLiteral(
+                _instance.valueAsString, _instance.valueAsStringIsTruncated)
+        ];
+      case M.InstanceKind.type:
+      case M.InstanceKind.typeRef:
+      case M.InstanceKind.typeParameter:
+        return [
+          new AnchorElement(href: Uris.inspect(_isolate, object: _instance))
+            ..text = _instance.name
+        ];
+      case M.InstanceKind.closure:
+        return [
+          new AnchorElement(href: Uris.inspect(_isolate, object: _instance))
+            ..children = <Element>[
+              new SpanElement()
+                ..classes = ['emphasize']
+                ..text = 'Closure',
+              new SpanElement()..text = ' (${_instance.closureFunction.name})'
+            ]
+        ];
+      case M.InstanceKind.regExp:
+        return [
+          new AnchorElement(href: Uris.inspect(_isolate, object: _instance))
+            ..children = <Element>[
+              new SpanElement()
+                ..classes = ['emphasize']
+                ..text = _instance.clazz.name,
+              new SpanElement()..text = ' (${_instance.pattern.valueAsString})'
+            ]
+        ];
+      case M.InstanceKind.stackTrace:
+        return [
+          new AnchorElement(href: Uris.inspect(_isolate, object: _instance))
+            ..children = <Element>[
+              new SpanElement()
+                ..classes = ['emphasize']
+                ..text = _instance.clazz.name,
+            ]
+        ];
+      case M.InstanceKind.plainInstance:
+        return [
+          new AnchorElement(href: Uris.inspect(_isolate, object: _instance))
+            ..classes = ['emphasize']
+            ..text = _instance.clazz.name
+        ];
+      case M.InstanceKind.list:
+      case M.InstanceKind.map:
+      case M.InstanceKind.uint8ClampedList:
+      case M.InstanceKind.uint8List:
+      case M.InstanceKind.uint16List:
+      case M.InstanceKind.uint32List:
+      case M.InstanceKind.uint64List:
+      case M.InstanceKind.int8List:
+      case M.InstanceKind.int16List:
+      case M.InstanceKind.int32List:
+      case M.InstanceKind.int64List:
+      case M.InstanceKind.float32List:
+      case M.InstanceKind.float64List:
+      case M.InstanceKind.int32x4List:
+      case M.InstanceKind.float32x4List:
+      case M.InstanceKind.float64x2List:
+        return [
+          new AnchorElement(href: Uris.inspect(_isolate, object: _instance))
+            ..children = <Element>[
+              new SpanElement()
+                ..classes = ['emphasize']
+                ..text = _instance.clazz.name,
+              new SpanElement()..text = ' (${_instance.length})'
+            ]
+        ];
+      case M.InstanceKind.mirrorReference:
+        return [
+          new AnchorElement(href: Uris.inspect(_isolate, object: _instance))
+            ..classes = ['emphasize']
+            ..text = _instance.clazz.name
+        ];
+      case M.InstanceKind.weakProperty:
+        return [
+          new AnchorElement(href: Uris.inspect(_isolate, object: _instance))
+            ..classes = ['emphasize']
+            ..text = _instance.clazz.name
+        ];
+    }
+    throw new Exception('Unknown InstanceKind: ${_instance.kind}');
+  }
+
+  bool _hasValue() {
+    switch (_instance.kind) {
+      case M.InstanceKind.closure:
+      case M.InstanceKind.plainInstance:
+      case M.InstanceKind.mirrorReference:
+      case M.InstanceKind.stackTrace:
+      case M.InstanceKind.weakProperty:
+        return true;
+      case M.InstanceKind.list:
+      case M.InstanceKind.map:
+      case M.InstanceKind.uint8ClampedList:
+      case M.InstanceKind.uint8List:
+      case M.InstanceKind.uint16List:
+      case M.InstanceKind.uint32List:
+      case M.InstanceKind.uint64List:
+      case M.InstanceKind.int8List:
+      case M.InstanceKind.int16List:
+      case M.InstanceKind.int32List:
+      case M.InstanceKind.int64List:
+      case M.InstanceKind.float32List:
+      case M.InstanceKind.float64List:
+      case M.InstanceKind.int32x4List:
+      case M.InstanceKind.float32x4List:
+      case M.InstanceKind.float64x2List:
+        return _instance.length > 0;
+      default:
+        return false;
+    }
+  }
+
+  List<Element> _createValue() {
+    if (_loadedInstance == null) {
+      return [new SpanElement()..text = 'Loading...'];
+    }
+    switch (_instance.kind) {
+      case M.InstanceKind.closure:
+        {
+          var members = <Element>[];
+          if (_loadedInstance.closureFunction != null) {
+            members.add(new DivElement()
+              ..children = <Element>[
+                new SpanElement()..text = 'function = ',
+                anyRef(_isolate, _loadedInstance.closureFunction, _objects,
+                    queue: _r.queue)
+              ]);
+          }
+          if (_loadedInstance.closureContext != null) {
+            members.add(new DivElement()
+              ..children = <Element>[
+                new SpanElement()..text = 'context = ',
+                anyRef(_isolate, _loadedInstance.closureContext, _objects,
+                    queue: _r.queue)
+              ]);
+          }
+          return members;
+        }
+      case M.InstanceKind.plainInstance:
+        return _loadedInstance.fields
+            .map<Element>((f) => new DivElement()
+              ..children = <Element>[
+                new FieldRefElement(_isolate, f.decl, _objects, queue: _r.queue)
+                    .element,
+                new SpanElement()..text = ' = ',
+                anyRef(_isolate, f.value, _objects, queue: _r.queue)
+              ])
+            .toList();
+      case M.InstanceKind.list:
+        var index = 0;
+        return _loadedInstance.elements
+            .map<Element>((element) => new DivElement()
+              ..children = <Element>[
+                new SpanElement()..text = '[ ${index++} ] : ',
+                anyRef(_isolate, element, _objects, queue: _r.queue)
+              ])
+            .toList()
+              ..addAll(_createShowMoreButton());
+      case M.InstanceKind.map:
+        return _loadedInstance.associations
+            .map<Element>((association) => new DivElement()
+              ..children = <Element>[
+                new SpanElement()..text = '[ ',
+                anyRef(_isolate, association.key, _objects, queue: _r.queue),
+                new SpanElement()..text = ' ] : ',
+                anyRef(_isolate, association.value, _objects, queue: _r.queue)
+              ])
+            .toList()
+              ..addAll(_createShowMoreButton());
+      case M.InstanceKind.uint8ClampedList:
+      case M.InstanceKind.uint8List:
+      case M.InstanceKind.uint16List:
+      case M.InstanceKind.uint32List:
+      case M.InstanceKind.uint64List:
+      case M.InstanceKind.int8List:
+      case M.InstanceKind.int16List:
+      case M.InstanceKind.int32List:
+      case M.InstanceKind.int64List:
+      case M.InstanceKind.float32List:
+      case M.InstanceKind.float64List:
+      case M.InstanceKind.int32x4List:
+      case M.InstanceKind.float32x4List:
+      case M.InstanceKind.float64x2List:
+        var index = 0;
+        return _loadedInstance.typedElements
+            .map<Element>((e) => new DivElement()..text = '[ ${index++} ] : $e')
+            .toList()
+              ..addAll(_createShowMoreButton());
+      case M.InstanceKind.mirrorReference:
+        return [
+          new SpanElement()..text = '<referent> : ',
+          anyRef(_isolate, _loadedInstance.referent, _objects, queue: _r.queue)
+        ];
+      case M.InstanceKind.stackTrace:
+        return [
+          new DivElement()
+            ..classes = ['stackTraceBox']
+            ..text = _instance.valueAsString
+        ];
+      case M.InstanceKind.weakProperty:
+        return [
+          new SpanElement()..text = '<key> : ',
+          new InstanceRefElement(_isolate, _loadedInstance.key, _objects,
+                  queue: _r.queue)
+              .element,
+          new BRElement(),
+          new SpanElement()..text = '<value> : ',
+          new InstanceRefElement(_isolate, _loadedInstance.value, _objects,
+                  queue: _r.queue)
+              .element,
+        ];
+      default:
+        return [];
+    }
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/instance_view.dart b/runtime/observatory_2/lib/src/elements/instance_view.dart
new file mode 100644
index 0000000..1b78e7d
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/instance_view.dart
@@ -0,0 +1,527 @@
+// 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.
+
+library instance_view_element;
+
+import 'dart:async';
+import 'dart:html';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/class_ref.dart';
+import 'package:observatory_2/src/elements/context_ref.dart';
+import 'package:observatory_2/src/elements/curly_block.dart';
+import 'package:observatory_2/src/elements/eval_box.dart';
+import 'package:observatory_2/src/elements/field_ref.dart';
+import 'package:observatory_2/src/elements/function_ref.dart';
+import 'package:observatory_2/src/elements/helpers/any_ref.dart';
+import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
+import 'package:observatory_2/src/elements/helpers/nav_menu.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/instance_ref.dart';
+import 'package:observatory_2/src/elements/nav/class_menu.dart';
+import 'package:observatory_2/src/elements/nav/isolate_menu.dart';
+import 'package:observatory_2/src/elements/nav/library_menu.dart';
+import 'package:observatory_2/src/elements/nav/notify.dart';
+import 'package:observatory_2/src/elements/nav/refresh.dart';
+import 'package:observatory_2/src/elements/nav/top_menu.dart';
+import 'package:observatory_2/src/elements/nav/vm_menu.dart';
+import 'package:observatory_2/src/elements/object_common.dart';
+import 'package:observatory_2/src/elements/source_inset.dart';
+import 'package:observatory_2/src/elements/source_link.dart';
+import 'package:observatory_2/src/elements/view_footer.dart';
+import 'package:observatory_2/utils.dart';
+
+class InstanceViewElement extends CustomElement implements Renderable {
+  RenderingScheduler<InstanceViewElement> _r;
+
+  Stream<RenderedEvent<InstanceViewElement>> get onRendered => _r.onRendered;
+
+  M.VM _vm;
+  M.IsolateRef _isolate;
+  M.EventRepository _events;
+  M.NotificationRepository _notifications;
+  M.Instance _instance;
+  M.LibraryRef _library;
+  M.ObjectRepository _objects;
+  M.ClassRepository _classes;
+  M.RetainedSizeRepository _retainedSizes;
+  M.ReachableSizeRepository _reachableSizes;
+  M.InboundReferencesRepository _references;
+  M.RetainingPathRepository _retainingPaths;
+  M.ScriptRepository _scripts;
+  M.EvalRepository _eval;
+  M.TypeArguments _typeArguments;
+  M.TypeArgumentsRepository _arguments;
+  M.BreakpointRepository _breakpoints;
+  M.FunctionRepository _functions;
+  M.SourceLocation _location;
+
+  M.VMRef get vm => _vm;
+  M.IsolateRef get isolate => _isolate;
+  M.NotificationRepository get notifications => _notifications;
+  M.Instance get instance => _instance;
+
+  factory InstanceViewElement(
+      M.VM vm,
+      M.IsolateRef isolate,
+      M.Instance instance,
+      M.EventRepository events,
+      M.NotificationRepository notifications,
+      M.ObjectRepository objects,
+      M.ClassRepository classes,
+      M.RetainedSizeRepository retainedSizes,
+      M.ReachableSizeRepository reachableSizes,
+      M.InboundReferencesRepository references,
+      M.RetainingPathRepository retainingPaths,
+      M.ScriptRepository scripts,
+      M.EvalRepository eval,
+      M.TypeArgumentsRepository arguments,
+      M.BreakpointRepository breakpoints,
+      M.FunctionRepository functions,
+      {RenderingQueue queue}) {
+    assert(vm != null);
+    assert(isolate != null);
+    assert(events != null);
+    assert(notifications != null);
+    assert(instance != null);
+    assert(objects != null);
+    assert(classes != null);
+    assert(retainedSizes != null);
+    assert(reachableSizes != null);
+    assert(references != null);
+    assert(retainingPaths != null);
+    assert(scripts != null);
+    assert(eval != null);
+    assert(arguments != null);
+    assert(breakpoints != null);
+    assert(functions != null);
+    InstanceViewElement e = new InstanceViewElement.created();
+    e._r = new RenderingScheduler<InstanceViewElement>(e, queue: queue);
+    e._vm = vm;
+    e._isolate = isolate;
+    e._events = events;
+    e._notifications = notifications;
+    e._instance = instance;
+    e._objects = objects;
+    e._classes = classes;
+    e._retainedSizes = retainedSizes;
+    e._reachableSizes = reachableSizes;
+    e._references = references;
+    e._retainingPaths = retainingPaths;
+    e._scripts = scripts;
+    e._eval = eval;
+    e._arguments = arguments;
+    e._breakpoints = breakpoints;
+    e._functions = functions;
+    return e;
+  }
+
+  InstanceViewElement.created() : super.created('instance-view');
+
+  @override
+  attached() {
+    super.attached();
+    _r.enable();
+    _loadExtraData();
+  }
+
+  @override
+  detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+  }
+
+  void render() {
+    final content = <Element>[
+      new HeadingElement.h2()
+        ..text = M.isAbstractType(_instance.kind)
+            ? 'type ${_instance.name}'
+            : 'instance of ${_instance.clazz.name}',
+      new HRElement(),
+      new ObjectCommonElement(_isolate, _instance, _retainedSizes,
+              _reachableSizes, _references, _retainingPaths, _objects,
+              queue: _r.queue)
+          .element,
+      new BRElement(),
+      new DivElement()
+        ..classes = ['memberList']
+        ..children = _createMembers(),
+      new HRElement(),
+      new EvalBoxElement(_isolate, _instance, _objects, _eval,
+              quickExpressions: const ['toString()', 'runtimeType'],
+              queue: _r.queue)
+          .element
+    ];
+    if (_location != null) {
+      content.addAll([
+        new HRElement(),
+        new SourceInsetElement(_isolate, _location, _scripts, _objects, _events,
+                queue: _r.queue)
+            .element
+      ]);
+    }
+    content.addAll(
+        [new HRElement(), new ViewFooterElement(queue: _r.queue).element]);
+    children = <Element>[
+      navBar(_createMenu()),
+      new DivElement()
+        ..classes = ['content-centered-big']
+        ..children = content
+    ];
+  }
+
+  List<Element> _createMenu() {
+    final menu = <Element>[
+      new NavTopMenuElement(queue: _r.queue).element,
+      new NavVMMenuElement(_vm, _events, queue: _r.queue).element,
+      new NavIsolateMenuElement(_isolate, _events, queue: _r.queue).element
+    ];
+    if (_library != null) {
+      menu.add(new NavLibraryMenuElement(_isolate, _library, queue: _r.queue)
+          .element);
+    }
+    menu.addAll(<Element>[
+      new NavClassMenuElement(_isolate, _instance.clazz, queue: _r.queue)
+          .element,
+      navMenu('instance'),
+      (new NavRefreshElement(queue: _r.queue)
+            ..onRefresh.listen((e) {
+              e.element.disabled = true;
+              _refresh();
+            }))
+          .element,
+      new NavNotifyElement(_notifications, queue: _r.queue).element
+    ]);
+    return menu;
+  }
+
+  Element memberHalf(String cssClass, dynamic half) {
+    var result = new DivElement()..classes = [cssClass];
+    if (half is String) {
+      result.text = half;
+    } else {
+      result.children = <Element>[
+        anyRef(_isolate, half, _objects, queue: _r.queue)
+      ];
+    }
+    return result;
+  }
+
+  Element member(dynamic name, dynamic value) {
+    return new DivElement()
+      ..classes = ['memberItem']
+      ..children = <Element>[
+        memberHalf('memberName', name),
+        memberHalf('memberValue', value),
+      ];
+  }
+
+  List<Element> _createMembers() {
+    final members = <Element>[];
+    if (_instance.valueAsString != null) {
+      if (_instance.kind == M.InstanceKind.string) {
+        members.add(member(
+            'value as literal',
+            Utils.formatStringAsLiteral(
+                _instance.valueAsString, _instance.valueAsStringIsTruncated)));
+      } else {
+        members.add(member('value', _instance.valueAsString));
+      }
+    }
+    if (_instance.typeClass != null) {
+      members.add(member('type class', _instance.typeClass));
+    }
+    if (_typeArguments != null && _typeArguments.types.isNotEmpty) {
+      members.add(new DivElement()
+        ..classes = ['memberItem']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = 'type arguments',
+          new DivElement()
+            ..classes = ['memberValue']
+            ..children = ([new SpanElement()..text = '< ']
+              ..addAll(_typeArguments.types.expand((type) => [
+                    new InstanceRefElement(_isolate, type, _objects,
+                            queue: _r.queue)
+                        .element,
+                    new SpanElement()..text = ', '
+                  ]))
+              ..removeLast()
+              ..add(new SpanElement()..text = ' >'))
+        ]);
+    }
+    if (_instance.parameterizedClass != null) {
+      members.add(member('parameterized class', _instance.parameterizedClass));
+    }
+    if (_instance.parameterIndex != null) {
+      members.add(member('parameter index', '${_instance.parameterIndex}'));
+    }
+    if (_instance.targetType != null) {
+      members.add(member('target type', _instance.targetType));
+    }
+    if (_instance.bound != null) {
+      members.add(member('bound', _instance.bound));
+    }
+    if (_instance.closureFunction != null) {
+      members.add(member('closure function', _instance.closureFunction));
+    }
+    if (_instance.closureContext != null) {
+      members.add(member('closure context', _instance.closureContext));
+    }
+    if (_instance.kind == M.InstanceKind.closure) {
+      ButtonElement btn;
+      members.add(new DivElement()
+        ..classes = ['memberItem']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = 'closure breakpoint',
+          new DivElement()
+            ..classes = ['memberValue']
+            ..children = <Element>[
+              btn = new ButtonElement()
+                ..text = _instance.activationBreakpoint == null
+                    ? 'break on activation'
+                    : 'remove'
+                ..onClick.listen((_) {
+                  btn.disabled = true;
+                  _toggleBreakpoint();
+                })
+            ]
+        ]);
+    }
+
+    if (_instance.nativeFields != null && _instance.nativeFields.isNotEmpty) {
+      int i = 0;
+      members.add(new DivElement()
+        ..classes = ['memberItem']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = 'native fields (${_instance.nativeFields.length})',
+          new DivElement()
+            ..classes = ['memberName']
+            ..children = <Element>[
+              (new CurlyBlockElement(
+                      expanded: _instance.nativeFields.length <= 100,
+                      queue: _r.queue)
+                    ..content = <Element>[
+                      new DivElement()
+                        ..classes = ['memberList']
+                        ..children = _instance.nativeFields
+                            .map<Element>(
+                                (f) => member('[ ${i++} ]', '[ ${f.value} ]'))
+                            .toList()
+                    ])
+                  .element
+            ]
+        ]);
+    }
+
+    if (_instance.fields != null && _instance.fields.isNotEmpty) {
+      final fields = _instance.fields.toList();
+      members.add(new DivElement()
+        ..classes = ['memberItem']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = 'fields (${fields.length})',
+          new DivElement()
+            ..classes = ['memberName']
+            ..children = <Element>[
+              (new CurlyBlockElement(
+                      expanded: fields.length <= 100, queue: _r.queue)
+                    ..content = <Element>[
+                      new DivElement()
+                        ..classes = ['memberList']
+                        ..children = fields
+                            .map<Element>((f) => member(f.decl, f.value))
+                            .toList()
+                    ])
+                  .element
+            ]
+        ]);
+    }
+
+    if (_instance.elements != null && _instance.elements.isNotEmpty) {
+      final elements = _instance.elements.toList();
+      int i = 0;
+      members.add(new DivElement()
+        ..classes = ['memberItem']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = 'elements (${_instance.length})',
+          new DivElement()
+            ..classes = ['memberValue']
+            ..children = <Element>[
+              (new CurlyBlockElement(
+                      expanded: elements.length <= 100, queue: _r.queue)
+                    ..content = <Element>[
+                      new DivElement()
+                        ..classes = ['memberList']
+                        ..children = elements
+                            .map<Element>(
+                                (element) => member('[ ${i++} ]', element))
+                            .toList()
+                    ])
+                  .element
+            ]
+        ]);
+      if (_instance.length != elements.length) {
+        members.add(member(
+            '...', '${_instance.length - elements.length} omitted elements'));
+      }
+    }
+
+    if (_instance.associations != null && _instance.associations.isNotEmpty) {
+      final associations = _instance.associations.toList();
+      members.add(new DivElement()
+        ..classes = ['memberItem']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = 'associations (${_instance.length})',
+          new DivElement()
+            ..classes = ['memberName']
+            ..children = <Element>[
+              (new CurlyBlockElement(
+                      expanded: associations.length <= 100, queue: _r.queue)
+                    ..content = <Element>[
+                      new DivElement()
+                        ..classes = ['memberList']
+                        ..children = associations
+                            .map<Element>((a) => new DivElement()
+                              ..classes = ['memberItem']
+                              ..children = <Element>[
+                                new DivElement()
+                                  ..classes = ['memberName']
+                                  ..children = <Element>[
+                                    new SpanElement()..text = '[ ',
+                                    anyRef(_isolate, a.key, _objects,
+                                        queue: _r.queue),
+                                    new SpanElement()..text = ' ]',
+                                  ],
+                                new DivElement()
+                                  ..classes = ['memberValue']
+                                  ..children = <Element>[
+                                    anyRef(_isolate, a.value, _objects,
+                                        queue: _r.queue)
+                                  ]
+                              ])
+                            .toList()
+                    ])
+                  .element
+            ]
+        ]);
+      if (_instance.length != associations.length) {
+        members.add(member('...',
+            '${_instance.length - associations.length} omitted elements'));
+      }
+    }
+
+    if (_instance.typedElements != null && _instance.typedElements.isNotEmpty) {
+      final typedElements = _instance.typedElements.toList();
+      int i = 0;
+      members.add(new DivElement()
+        ..classes = ['memberItem']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = 'elements (${_instance.length})',
+          new DivElement()
+            ..classes = ['memberValue']
+            ..children = <Element>[
+              (new CurlyBlockElement(
+                      expanded: typedElements.length <= 100, queue: _r.queue)
+                    ..content = <Element>[
+                      new DivElement()
+                        ..classes = ['memberList']
+                        ..children = typedElements
+                            .map<Element>((e) => member('[ ${i++} ]', '$e'))
+                            .toList()
+                    ])
+                  .element
+            ]
+        ]);
+      if (_instance.length != typedElements.length) {
+        members.add(member('...',
+            '${_instance.length - typedElements.length} omitted elements'));
+      }
+    }
+
+    if (_instance.kind == M.InstanceKind.regExp) {
+      members.add(member('pattern', _instance.pattern));
+      members.add(
+          member('isCaseSensitive', _instance.isCaseSensitive ? 'yes' : 'no'));
+      members.add(member('isMultiLine', _instance.isMultiLine ? 'yes' : 'no'));
+      if (_instance.oneByteFunction != null) {
+        members.add(member('oneByteFunction', _instance.oneByteFunction));
+      }
+      if (_instance.twoByteFunction != null) {
+        members.add(member('twoByteFunction', _instance.twoByteFunction));
+      }
+      if (_instance.externalOneByteFunction != null) {
+        members.add(member(
+            'externalOneByteFunction', _instance.externalOneByteFunction));
+      }
+      if (_instance.externalTwoByteFunction != null) {
+        members.add(member(
+            'externalTwoByteFunction', _instance.externalTwoByteFunction));
+      }
+      if (_instance.oneByteBytecode != null) {
+        members.add(member('oneByteBytecode', _instance.oneByteBytecode));
+      }
+      if (_instance.twoByteBytecode != null) {
+        members.add(member('twoByteBytecode', _instance.twoByteBytecode));
+      }
+    }
+
+    if (_instance.kind == M.InstanceKind.mirrorReference) {
+      members.add(member('referent', _instance.referent));
+    }
+
+    if (_instance.kind == M.InstanceKind.weakProperty) {
+      members.add(member('key', _instance.key));
+      members.add(member('value', _instance.value));
+    }
+
+    return members;
+  }
+
+  Future _refresh() async {
+    _instance = await _objects.get(_isolate, _instance.id);
+    await _loadExtraData();
+    _r.dirty();
+  }
+
+  Future _loadExtraData() async {
+    _library = (await _classes.get(_isolate, _instance.clazz.id)).library;
+    if (_instance.typeArguments != null) {
+      _typeArguments =
+          await _arguments.get(_isolate, _instance.typeArguments.id);
+    } else {
+      _typeArguments = null;
+    }
+    if (_instance.closureFunction != null) {
+      _location = (await _functions.get(_isolate, _instance.closureFunction.id))
+          .location;
+    } else if (_instance.typeClass != null) {
+      _location =
+          (await _classes.get(_isolate, _instance.typeClass.id)).location;
+    }
+    _r.dirty();
+  }
+
+  Future _toggleBreakpoint() async {
+    if (_instance.activationBreakpoint == null) {
+      await _breakpoints.addOnActivation(_isolate, _instance);
+    } else {
+      await _breakpoints.remove(_isolate, _instance.activationBreakpoint);
+    }
+    await _refresh();
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/isolate/counter_chart.dart b/runtime/observatory_2/lib/src/elements/isolate/counter_chart.dart
new file mode 100644
index 0000000..0464570
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/isolate/counter_chart.dart
@@ -0,0 +1,65 @@
+// Copyright (c) 2016, 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.
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+
+class IsolateCounterChartElement extends CustomElement implements Renderable {
+  RenderingScheduler<IsolateCounterChartElement> _r;
+
+  Stream<RenderedEvent<IsolateCounterChartElement>> get onRendered =>
+      _r.onRendered;
+
+  Map _counters;
+  StreamSubscription _subscription;
+
+  factory IsolateCounterChartElement(Map counters, {RenderingQueue queue}) {
+    assert(counters != null);
+    IsolateCounterChartElement e = new IsolateCounterChartElement.created();
+    e._r = new RenderingScheduler<IsolateCounterChartElement>(e, queue: queue);
+    e._counters = counters;
+    return e;
+  }
+
+  IsolateCounterChartElement.created() : super.created('isolate-counter-chart');
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+    _subscription = window.onResize.listen((_) => _r.dirty());
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    children = <Element>[];
+    _r.disable(notify: true);
+    _subscription.cancel();
+  }
+
+  void render() {
+    var members = <Element>[];
+    _counters.forEach((key, value) {
+      members.add(new DivElement()
+        ..classes = ['memberItem']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = key,
+          new DivElement()
+            ..classes = ['memberValue']
+            ..text = value,
+        ]);
+    });
+
+    children = <Element>[
+      new DivElement()
+        ..classes = ['memberList']
+        ..children = members
+    ];
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/isolate/location.dart b/runtime/observatory_2/lib/src/elements/isolate/location.dart
new file mode 100644
index 0000000..a71ee86
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/isolate/location.dart
@@ -0,0 +1,119 @@
+// Copyright (c) 2016, 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.
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/function_ref.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/source_link.dart';
+
+class IsolateLocationElement extends CustomElement implements Renderable {
+  RenderingScheduler<IsolateLocationElement> _r;
+
+  Stream<RenderedEvent<IsolateLocationElement>> get onRendered => _r.onRendered;
+
+  M.Isolate _isolate;
+  M.EventRepository _events;
+  M.ScriptRepository _scripts;
+  StreamSubscription _debugSubscription;
+  StreamSubscription _isolateSubscription;
+
+  factory IsolateLocationElement(
+      M.Isolate isolate, M.EventRepository events, M.ScriptRepository scripts,
+      {RenderingQueue queue}) {
+    assert(isolate != null);
+    assert(events != null);
+    assert(scripts != null);
+    IsolateLocationElement e = new IsolateLocationElement.created();
+    e._r = new RenderingScheduler<IsolateLocationElement>(e, queue: queue);
+    e._isolate = isolate;
+    e._events = events;
+    e._scripts = scripts;
+    return e;
+  }
+
+  IsolateLocationElement.created() : super.created('isolate-location');
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+    _debugSubscription = _events.onDebugEvent.listen(_eventListener);
+    _isolateSubscription = _events.onIsolateEvent.listen(_eventListener);
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    children = <Element>[];
+    _r.disable(notify: true);
+    _debugSubscription.cancel();
+    _isolateSubscription.cancel();
+  }
+
+  void render() {
+    switch (_isolate.status) {
+      case M.IsolateStatus.loading:
+        children = <Element>[new SpanElement()..text = 'not yet runnable'];
+        break;
+      case M.IsolateStatus.running:
+        children = <Element>[
+          new SpanElement()..text = 'at ',
+          new FunctionRefElement(
+                  _isolate, M.topFrame(_isolate.pauseEvent).function,
+                  queue: _r.queue)
+              .element,
+          new SpanElement()..text = ' (',
+          new SourceLinkElement(
+                  _isolate, M.topFrame(_isolate.pauseEvent).location, _scripts,
+                  queue: _r.queue)
+              .element,
+          new SpanElement()..text = ') '
+        ];
+        break;
+      case M.IsolateStatus.paused:
+        if (_isolate.pauseEvent is M.PauseStartEvent) {
+          children = <Element>[new SpanElement()..text = 'at isolate start'];
+        } else if (_isolate.pauseEvent is M.PauseExitEvent) {
+          children = <Element>[new SpanElement()..text = 'at isolate exit'];
+        } else if (_isolate.pauseEvent is M.NoneEvent) {
+          children = <Element>[new SpanElement()..text = 'not yet runnable'];
+        } else {
+          final content = <Element>[];
+          if (_isolate.pauseEvent is M.PauseBreakpointEvent) {
+            content.add(new SpanElement()..text = 'by breakpoint');
+          } else if (_isolate.pauseEvent is M.PauseExceptionEvent) {
+            content.add(new SpanElement()..text = 'by exception');
+          }
+          if (M.topFrame(_isolate.pauseEvent) != null) {
+            content.addAll([
+              new SpanElement()..text = ' at ',
+              new FunctionRefElement(
+                      _isolate, M.topFrame(_isolate.pauseEvent).function,
+                      queue: _r.queue)
+                  .element,
+              new SpanElement()..text = ' (',
+              new SourceLinkElement(_isolate,
+                      M.topFrame(_isolate.pauseEvent).location, _scripts,
+                      queue: _r.queue)
+                  .element,
+              new SpanElement()..text = ') '
+            ]);
+          }
+          children = content;
+        }
+        break;
+      default:
+        children = const [];
+    }
+  }
+
+  void _eventListener(e) {
+    if (e.isolate.id == _isolate.id) {
+      _r.dirty();
+    }
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/isolate/run_state.dart b/runtime/observatory_2/lib/src/elements/isolate/run_state.dart
new file mode 100644
index 0000000..c87692a
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/isolate/run_state.dart
@@ -0,0 +1,78 @@
+// Copyright (c) 2016, 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.
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+
+class IsolateRunStateElement extends CustomElement implements Renderable {
+  RenderingScheduler<IsolateRunStateElement> _r;
+
+  Stream<RenderedEvent<IsolateRunStateElement>> get onRendered => _r.onRendered;
+
+  M.Isolate _isolate;
+  M.EventRepository _events;
+  StreamSubscription _debugSubscription;
+  StreamSubscription _isolateSubscription;
+
+  factory IsolateRunStateElement(M.Isolate isolate, M.EventRepository events,
+      {RenderingQueue queue}) {
+    assert(isolate != null);
+    assert(events != null);
+    IsolateRunStateElement e = new IsolateRunStateElement.created();
+    e._r = new RenderingScheduler<IsolateRunStateElement>(e, queue: queue);
+    e._isolate = isolate;
+    e._events = events;
+    return e;
+  }
+
+  IsolateRunStateElement.created() : super.created('isolate-run-state');
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+    _debugSubscription = _events.onDebugEvent.listen(_eventListener);
+    _isolateSubscription = _events.onIsolateEvent.listen(_eventListener);
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    children = <Element>[];
+    _r.disable(notify: true);
+    _debugSubscription.cancel();
+    _isolateSubscription.cancel();
+  }
+
+  void render() {
+    switch (_isolate.status) {
+      case M.IsolateStatus.loading:
+        children = <Element>[new SpanElement()..text = 'loading... '];
+        break;
+      case M.IsolateStatus.running:
+        children = <Element>[new SpanElement()..text = 'running '];
+        break;
+      case M.IsolateStatus.idle:
+        children = <Element>[new SpanElement()..text = 'idle '];
+        break;
+      case M.IsolateStatus.paused:
+        children = <Element>[
+          new SpanElement()
+            ..title = '${_isolate.pauseEvent.timestamp}'
+            ..text = 'paused '
+        ];
+        break;
+    }
+  }
+
+  void _eventListener(e) {
+    if (e.isolate.id == _isolate.id) {
+      _isolate = e.isolate;
+      _r.dirty();
+    }
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/isolate/shared_summary.dart b/runtime/observatory_2/lib/src/elements/isolate/shared_summary.dart
new file mode 100644
index 0000000..b9d5e58
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/isolate/shared_summary.dart
@@ -0,0 +1,177 @@
+// Copyright (c) 2016, 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.
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/utils.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/helpers/uris.dart';
+import 'package:observatory_2/src/elements/isolate/counter_chart.dart';
+
+class IsolateSharedSummaryElement extends CustomElement implements Renderable {
+  RenderingScheduler<IsolateSharedSummaryElement> _r;
+
+  Stream<RenderedEvent<IsolateSharedSummaryElement>> get onRendered =>
+      _r.onRendered;
+
+  M.Isolate _isolate;
+  M.EventRepository _events;
+  StreamSubscription _isolateSubscription;
+
+  factory IsolateSharedSummaryElement(
+      M.Isolate isolate, M.EventRepository events,
+      {RenderingQueue queue}) {
+    assert(isolate != null);
+    assert(events != null);
+    IsolateSharedSummaryElement e = new IsolateSharedSummaryElement.created();
+    e._r = new RenderingScheduler<IsolateSharedSummaryElement>(e, queue: queue);
+    e._isolate = isolate;
+    e._events = events;
+    return e;
+  }
+
+  IsolateSharedSummaryElement.created()
+      : super.created('isolate-shared-summary');
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+    _isolateSubscription = _events.onIsolateEvent.listen(_eventListener);
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    children = <Element>[];
+    _r.disable(notify: true);
+    _isolateSubscription.cancel();
+  }
+
+  void render() {
+    final newHeapUsed = Utils.formatSize(_isolate.newSpace.used);
+    final newHeapCapacity = Utils.formatSize(_isolate.newSpace.capacity);
+    final oldHeapUsed = Utils.formatSize(_isolate.oldSpace.used);
+    final oldHeapCapacity = Utils.formatSize(_isolate.oldSpace.capacity);
+    final content = <Element>[
+      new DivElement()
+        ..classes = ['menu']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberList']
+            ..children = <Element>[
+              new DivElement()
+                ..classes = ['memberItem']
+                ..children = <Element>[
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..text = 'new heap',
+                  new DivElement()
+                    ..classes = ['memberValue']
+                    ..text = '$newHeapUsed of $newHeapCapacity',
+                ],
+              new DivElement()
+                ..classes = ['memberItem']
+                ..children = <Element>[
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..text = 'old heap',
+                  new DivElement()
+                    ..classes = ['memberValue']
+                    ..text = '$oldHeapUsed of $oldHeapCapacity',
+                ]
+            ],
+          new BRElement(),
+          new DivElement()
+            ..children = <Element>[
+              new SpanElement()..text = 'see ',
+              new AnchorElement(href: Uris.debugger(_isolate))
+                ..text = 'debugger'
+            ],
+          new DivElement()
+            ..children = <Element>[
+              new SpanElement()..text = 'see ',
+              new AnchorElement(href: Uris.classTree(_isolate))
+                ..text = 'class hierarchy'
+            ],
+          new DivElement()
+            ..children = <Element>[
+              new SpanElement()..text = 'see ',
+              new AnchorElement(href: Uris.cpuProfiler(_isolate))
+                ..text = 'cpu profile'
+            ],
+          new DivElement()
+            ..children = <Element>[
+              new SpanElement()..text = 'see ',
+              new AnchorElement(href: Uris.cpuProfilerTable(_isolate))
+                ..text = 'cpu profile (table)'
+            ],
+          new DivElement()
+            ..children = <Element>[
+              new SpanElement()..text = 'see ',
+              new AnchorElement(href: Uris.allocationProfiler(_isolate))
+                ..text = 'allocation profile'
+            ],
+          new DivElement()
+            ..children = <Element>[
+              new SpanElement()..text = 'see ',
+              new AnchorElement(href: Uris.heapSnapshot(_isolate))
+                ..text = 'heap snapshot'
+            ],
+          new DivElement()
+            ..children = <Element>[
+              new SpanElement()..text = 'see ',
+              new AnchorElement(href: Uris.heapMap(_isolate))..text = 'heap map'
+            ],
+          new DivElement()
+            ..children = <Element>[
+              new SpanElement()..text = 'see ',
+              new AnchorElement(href: Uris.metrics(_isolate))..text = 'metrics'
+            ],
+          new DivElement()
+            ..children = <Element>[
+              new SpanElement()..text = 'see ',
+              new AnchorElement(href: Uris.persistentHandles(_isolate))
+                ..text = 'persistent handles'
+            ],
+          new DivElement()
+            ..children = <Element>[
+              new SpanElement()..text = 'see ',
+              new AnchorElement(href: Uris.ports(_isolate))..text = 'ports'
+            ],
+          new DivElement()
+            ..children = <Element>[
+              new SpanElement()..text = 'see ',
+              new AnchorElement(href: Uris.logging(_isolate))..text = 'logging'
+            ]
+        ],
+      new IsolateCounterChartElement(_isolate.counters, queue: _r.queue).element
+    ];
+    if (_isolate.error != null) {
+      children = <Element>[
+        new PreElement()
+          ..classes = ['errorBox']
+          ..text = _isolate.error.message,
+        new DivElement()
+          ..classes = ['summary']
+          ..children = content
+      ];
+    } else {
+      children = <Element>[
+        new DivElement()
+          ..classes = ['summary']
+          ..children = content
+      ];
+    }
+  }
+
+  void _eventListener(e) {
+    if (e.isolate.id == _isolate.id) {
+      _isolate = e.isolate;
+      _r.dirty();
+    }
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/isolate/summary.dart b/runtime/observatory_2/lib/src/elements/isolate/summary.dart
new file mode 100644
index 0000000..2c3f156
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/isolate/summary.dart
@@ -0,0 +1,180 @@
+// Copyright (c) 2016, 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.
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/utils.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/helpers/uris.dart';
+import 'package:observatory_2/src/elements/isolate_ref.dart';
+import 'package:observatory_2/src/elements/isolate/location.dart';
+import 'package:observatory_2/src/elements/isolate/run_state.dart';
+import 'package:observatory_2/src/elements/isolate/shared_summary.dart';
+
+class IsolateSummaryElement extends CustomElement implements Renderable {
+  RenderingScheduler<IsolateSummaryElement> _r;
+
+  Stream<RenderedEvent<IsolateSummaryElement>> get onRendered => _r.onRendered;
+
+  M.IsolateRef _isolate;
+  M.EventRepository _events;
+  M.IsolateRepository _isolates;
+  M.ScriptRepository _scripts;
+  M.Isolate _loadedIsolate;
+
+  factory IsolateSummaryElement(
+      M.IsolateRef isolate,
+      M.IsolateRepository isolates,
+      M.EventRepository events,
+      M.ScriptRepository scripts,
+      {RenderingQueue queue}) {
+    assert(isolate != null);
+    assert(isolates != null);
+    assert(events != null);
+    assert(scripts != null);
+    IsolateSummaryElement e = new IsolateSummaryElement.created();
+    e._r = new RenderingScheduler<IsolateSummaryElement>(e, queue: queue);
+    e._isolate = isolate;
+    e._isolates = isolates;
+    e._events = events;
+    e._scripts = scripts;
+    return e;
+  }
+
+  IsolateSummaryElement.created() : super.created('isolate-summary');
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+    _load();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    children = <Element>[];
+    _r.disable(notify: true);
+  }
+
+  void render() {
+    if (_loadedIsolate == null) {
+      children = <Element>[
+        new SpanElement()..text = 'loading ',
+        new IsolateRefElement(_isolate, _events, queue: _r.queue).element
+      ];
+    } else {
+      children = <Element>[
+        linkAndStatusRow(),
+        new BRElement(),
+        memoryRow(),
+        new BRElement(),
+        toolsRow(),
+      ];
+    }
+  }
+
+  Element linkAndStatusRow() {
+    return new DivElement()
+      ..classes = ['flex-row-wrap']
+      ..children = <Element>[
+        new DivElement()
+          ..classes = ['isolate-ref-container']
+          ..children = <Element>[
+            new IsolateRefElement(_isolate, _events, queue: _r.queue).element
+          ],
+        new DivElement()..style.flex = '1',
+        new DivElement()
+          ..classes = ['flex-row', 'isolate-state-container']
+          ..children = <Element>[
+            new IsolateRunStateElement(_isolate, _events, queue: _r.queue)
+                .element,
+            new IsolateLocationElement(_isolate, _events, _scripts,
+                    queue: _r.queue)
+                .element,
+            new SpanElement()..text = ' [',
+            new AnchorElement(href: Uris.debugger(_isolate))..text = 'debug',
+            new SpanElement()..text = ']'
+          ]
+      ];
+  }
+
+  Element memoryRow() {
+    final isolate = _isolate as M.Isolate;
+    final newHeapUsed = Utils.formatSize(isolate.newSpace.used);
+    final newHeapCapacity = Utils.formatSize(isolate.newSpace.capacity);
+    final oldHeapUsed = Utils.formatSize(isolate.oldSpace.used);
+    final oldHeapCapacity = Utils.formatSize(isolate.oldSpace.capacity);
+    final heapUsed =
+        Utils.formatSize(isolate.newSpace.used + isolate.oldSpace.used);
+    final heapCapacity =
+        Utils.formatSize(isolate.newSpace.capacity + isolate.oldSpace.capacity);
+    return new DivElement()
+      ..classes = ['flex-row-wrap-right']
+      ..children = <Element>[
+        new DivElement()
+          ..style.padding = '5px'
+          ..text = 'new-space $newHeapUsed of $newHeapCapacity',
+        new DivElement()
+          ..style.padding = '5px'
+          ..text = '/',
+        new DivElement()
+          ..style.padding = '5px'
+          ..text = 'old-space $oldHeapUsed of $oldHeapCapacity',
+        new DivElement()
+          ..style.padding = '5px'
+          ..text = '/',
+        new DivElement()
+          ..style.padding = '5px'
+          ..text = 'heap $heapUsed of $heapCapacity',
+      ];
+  }
+
+  Element toolsRow() {
+    return new DivElement()
+      ..classes = ['flex-row-spaced']
+      ..children = <Element>[
+        new AnchorElement(href: Uris.debugger(_isolate))
+          ..classes = ['flex-item-even']
+          ..text = 'debugger',
+        new AnchorElement(href: Uris.classTree(_isolate))
+          ..classes = ['flex-item-even']
+          ..text = 'class hierarchy',
+        new AnchorElement(href: Uris.cpuProfiler(_isolate))
+          ..classes = ['flex-item-even']
+          ..text = 'cpu profile',
+        new AnchorElement(href: Uris.cpuProfilerTable(_isolate))
+          ..classes = ['flex-item-even']
+          ..text = 'cpu profile (table)',
+        new AnchorElement(href: Uris.allocationProfiler(_isolate))
+          ..classes = ['flex-item-even']
+          ..text = 'allocation profile',
+        new AnchorElement(href: Uris.heapSnapshot(_isolate))
+          ..classes = ['flex-item-even']
+          ..text = 'heap snapshot',
+        new AnchorElement(href: Uris.heapMap(_isolate))
+          ..classes = ['flex-item-even']
+          ..text = 'heap map',
+        new AnchorElement(href: Uris.metrics(_isolate))
+          ..classes = ['flex-item-even']
+          ..text = 'metrics',
+        new AnchorElement(href: Uris.persistentHandles(_isolate))
+          ..classes = ['flex-item-even']
+          ..text = 'persistent handles',
+        new AnchorElement(href: Uris.ports(_isolate))
+          ..classes = ['flex-item-even']
+          ..text = 'ports',
+        new AnchorElement(href: Uris.logging(_isolate))
+          ..classes = ['flex-item-even']
+          ..text = 'logging',
+      ];
+  }
+
+  Future _load() async {
+    _loadedIsolate = await _isolates.get(_isolate);
+    _r.dirty();
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/isolate_reconnect.dart b/runtime/observatory_2/lib/src/elements/isolate_reconnect.dart
new file mode 100644
index 0000000..f4b9e23
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/isolate_reconnect.dart
@@ -0,0 +1,110 @@
+// Copyright (c) 2014, 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.
+
+library isolate_reconnect_element;
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/helpers/uris.dart';
+import 'package:observatory_2/src/elements/nav/notify.dart';
+import 'package:observatory_2/src/elements/nav/top_menu.dart';
+import 'package:observatory_2/src/elements/view_footer.dart';
+
+class IsolateReconnectElement extends CustomElement implements Renderable {
+  RenderingScheduler<IsolateReconnectElement> _r;
+
+  Stream<RenderedEvent<IsolateReconnectElement>> get onRendered =>
+      _r.onRendered;
+
+  M.VM _vm;
+  String _missing;
+  Uri _uri;
+  M.EventRepository _events;
+  StreamSubscription _subscription;
+
+  M.VM get vm => _vm;
+  String get missing => _missing;
+  Uri get uri => _uri;
+
+  M.NotificationRepository _notifications;
+  factory IsolateReconnectElement(M.VM vm, M.EventRepository events,
+      M.NotificationRepository notifications, String missing, Uri uri,
+      {RenderingQueue queue}) {
+    assert(vm != null);
+    assert(events != null);
+    assert(missing != null);
+    assert(uri != null);
+    assert(notifications != null);
+    IsolateReconnectElement e = new IsolateReconnectElement.created();
+    e._r = new RenderingScheduler<IsolateReconnectElement>(e, queue: queue);
+    e._vm = vm;
+    e._events = events;
+    e._missing = missing;
+    e._uri = uri;
+    e._notifications = notifications;
+    return e;
+  }
+
+  IsolateReconnectElement.created() : super.created('isolate-reconnect');
+
+  @override
+  void attached() {
+    super.attached();
+    _subscription = _events.onVMUpdate.listen((e) {
+      _vm = e.vm;
+      _r.dirty();
+    });
+    _r.enable();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    children = <Element>[];
+    _r.disable(notify: true);
+    _subscription.cancel();
+  }
+
+  void render() {
+    children = <Element>[
+      navBar(<Element>[
+        new NavTopMenuElement(queue: _r.queue).element,
+        new NavNotifyElement(_notifications, queue: _r.queue).element
+      ]),
+      new DivElement()
+        ..classes = ['content-centered']
+        ..children = <Element>[
+          new HeadingElement.h1()..text = 'Isolate $_missing no longer exists',
+          new HRElement(),
+          new BRElement(),
+          new DivElement()
+            ..classes = ['memberList']
+            ..children = (_vm.isolates.map<Element>((isolate) {
+              final query = new Map<String, dynamic>.from(_uri.queryParameters);
+              query['isolateId'] = isolate.id;
+              final href = new Uri(path: _uri.path, queryParameters: query);
+              return new DivElement()
+                ..classes = ['memberItem', 'doubleSpaced']
+                ..children = <Element>[
+                  new SpanElement()..text = 'Continue in ',
+                  new AnchorElement(href: '#$href')
+                    ..classes = ['isolate-link']
+                    ..text = '${isolate.id} (${isolate.name})'
+                ];
+            }).toList()
+              ..add(new DivElement()
+                ..classes = ['memberItem', 'doubleSpaced']
+                ..children = <Element>[
+                  new SpanElement()..text = 'Go to ',
+                  new AnchorElement(href: Uris.vm())..text = 'isolates summary',
+                ]))
+        ],
+      new ViewFooterElement(queue: _r.queue).element
+    ];
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/isolate_ref.dart b/runtime/observatory_2/lib/src/elements/isolate_ref.dart
new file mode 100644
index 0000000..ceebf22
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/isolate_ref.dart
@@ -0,0 +1,66 @@
+// 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.
+
+library isolate_ref_element;
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/models.dart' as M show IsolateRef, EventRepository;
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/helpers/uris.dart';
+
+class IsolateRefElement extends CustomElement implements Renderable {
+  RenderingScheduler<IsolateRefElement> _r;
+
+  Stream<RenderedEvent<IsolateRefElement>> get onRendered => _r.onRendered;
+
+  M.IsolateRef _isolate;
+  M.EventRepository _events;
+  StreamSubscription _updatesSubscription;
+
+  M.IsolateRef get isolate => _isolate;
+
+  factory IsolateRefElement(M.IsolateRef isolate, M.EventRepository events,
+      {RenderingQueue queue}) {
+    assert(isolate != null);
+    assert(events != null);
+    IsolateRefElement e = new IsolateRefElement.created();
+    e._r = new RenderingScheduler<IsolateRefElement>(e, queue: queue);
+    e._isolate = isolate;
+    e._events = events;
+    return e;
+  }
+
+  IsolateRefElement.created() : super.created('isolate-ref');
+
+  @override
+  void attached() {
+    super.attached();
+    _updatesSubscription = _events.onIsolateUpdate
+        .where((e) => e.isolate.id == isolate.id)
+        .listen((e) {
+      _isolate = e.isolate;
+      _r.dirty();
+    });
+    _r.enable();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    children = <Element>[];
+    _r.disable(notify: true);
+    _updatesSubscription.cancel();
+  }
+
+  void render() {
+    final isolateType = isolate.isSystemIsolate ? 'System Isolate' : 'Isolate';
+    children = <Element>[
+      new AnchorElement(href: Uris.inspect(isolate))
+        ..text = '$isolateType ${isolate.number} (${isolate.name})'
+        ..classes = ['isolate-ref']
+    ];
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/isolate_view.dart b/runtime/observatory_2/lib/src/elements/isolate_view.dart
new file mode 100644
index 0000000..269f207
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/isolate_view.dart
@@ -0,0 +1,402 @@
+// 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.
+
+library isolate_view_element;
+
+import 'dart:async';
+import 'dart:html';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/curly_block.dart';
+import 'package:observatory_2/src/elements/eval_box.dart';
+import 'package:observatory_2/src/elements/function_ref.dart';
+import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/helpers/uris.dart';
+import 'package:observatory_2/src/elements/isolate/location.dart';
+import 'package:observatory_2/src/elements/isolate/run_state.dart';
+import 'package:observatory_2/src/elements/isolate/shared_summary.dart';
+import 'package:observatory_2/src/elements/library_ref.dart';
+import 'package:observatory_2/src/elements/nav/class_menu.dart';
+import 'package:observatory_2/src/elements/nav/isolate_menu.dart';
+import 'package:observatory_2/src/elements/nav/notify.dart';
+import 'package:observatory_2/src/elements/nav/refresh.dart';
+import 'package:observatory_2/src/elements/nav/reload.dart';
+import 'package:observatory_2/src/elements/nav/top_menu.dart';
+import 'package:observatory_2/src/elements/nav/vm_menu.dart';
+import 'package:observatory_2/src/elements/script_inset.dart';
+import 'package:observatory_2/src/elements/source_inset.dart';
+import 'package:observatory_2/src/elements/view_footer.dart';
+import 'package:observatory_2/utils.dart';
+
+class IsolateViewElement extends CustomElement implements Renderable {
+  RenderingScheduler<IsolateViewElement> _r;
+
+  Stream<RenderedEvent<IsolateViewElement>> get onRendered => _r.onRendered;
+
+  M.VM _vm;
+  M.Isolate _isolate;
+  M.EventRepository _events;
+  M.NotificationRepository _notifications;
+  M.IsolateRepository _isolates;
+  M.ScriptRepository _scripts;
+  M.FunctionRepository _functions;
+  M.LibraryRepository _libraries;
+  M.ObjectRepository _objects;
+  M.EvalRepository _eval;
+  M.ServiceFunction _function;
+  M.ScriptRef _rootScript;
+  StreamSubscription _subscription;
+
+  M.VMRef get vm => _vm;
+  M.Isolate get isolate => _isolate;
+  M.NotificationRepository get notifications => _notifications;
+
+  factory IsolateViewElement(
+      M.VM vm,
+      M.Isolate isolate,
+      M.EventRepository events,
+      M.NotificationRepository notifications,
+      M.IsolateRepository isolates,
+      M.ScriptRepository scripts,
+      M.FunctionRepository functions,
+      M.LibraryRepository libraries,
+      M.ObjectRepository objects,
+      M.EvalRepository eval,
+      {RenderingQueue queue}) {
+    assert(vm != null);
+    assert(isolate != null);
+    assert(events != null);
+    assert(notifications != null);
+    assert(isolates != null);
+    assert(scripts != null);
+    assert(functions != null);
+    assert(objects != null);
+    assert(eval != null);
+    assert(libraries != null);
+    IsolateViewElement e = new IsolateViewElement.created();
+    e._r = new RenderingScheduler<IsolateViewElement>(e, queue: queue);
+    e._vm = vm;
+    e._isolate = isolate;
+    e._events = events;
+    e._notifications = notifications;
+    e._isolates = isolates;
+    e._scripts = scripts;
+    e._functions = functions;
+    e._objects = objects;
+    e._eval = eval;
+    e._libraries = libraries;
+    return e;
+  }
+
+  IsolateViewElement.created() : super.created('isolate-view');
+
+  @override
+  attached() {
+    super.attached();
+    _r.enable();
+    _loadExtraData();
+    _subscription = _events.onIsolateUpdate.listen((e) {
+      if (e.isolate.id == _isolate) {
+        _isolate = isolate;
+        _r.dirty();
+      }
+    });
+  }
+
+  @override
+  detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+    _subscription.cancel();
+  }
+
+  void render() {
+    final uptime = new DateTime.now().difference(_isolate.startTime);
+    final libraries = _isolate.libraries.toList();
+    final List<M.Thread> threads = _isolate.threads;
+    children = <Element>[
+      navBar(<Element>[
+        new NavTopMenuElement(queue: _r.queue).element,
+        new NavVMMenuElement(_vm, _events, queue: _r.queue).element,
+        new NavIsolateMenuElement(_isolate, _events, queue: _r.queue).element,
+        (new NavReloadElement(_isolate, _isolates, _events, queue: _r.queue)
+              ..onReload.listen((_) async {
+                _isolate = await _isolates.get(_isolate);
+                await _loadExtraData();
+                _r.dirty();
+              }))
+            .element,
+        (new NavRefreshElement(queue: _r.queue)
+              ..onRefresh.listen((e) async {
+                e.element.disabled = true;
+                _isolate = await _isolates.get(_isolate);
+                await _loadExtraData();
+                _r.dirty();
+              }))
+            .element,
+        new NavNotifyElement(_notifications, queue: _r.queue).element
+      ]),
+      new DivElement()
+        ..classes = ['content-centered-big']
+        ..children = <Element>[
+          new HeadingElement.h2()..text = 'Isolate ${_isolate.name}',
+          new BRElement(),
+          new DivElement()
+            ..classes = ['flex-row']
+            ..children = <Element>[
+              new DivElement()..style.flex = '1',
+              new DivElement()
+                ..children = <Element>[
+                  new IsolateRunStateElement(_isolate, _events, queue: _r.queue)
+                      .element,
+                  new IsolateLocationElement(_isolate, _events, _scripts,
+                          queue: _r.queue)
+                      .element,
+                  new SpanElement()..text = ' [',
+                  new AnchorElement(href: Uris.debugger(_isolate))
+                    ..text = 'debug',
+                  new SpanElement()..text = ']'
+                ]
+            ],
+          new DivElement()
+            ..children = _function != null
+                ? [
+                    new BRElement(),
+                    (new SourceInsetElement(_isolate, _function.location,
+                            _scripts, _objects, _events,
+                            currentPos: M
+                                .topFrame(isolate.pauseEvent)
+                                .location
+                                .tokenPos,
+                            queue: _r.queue)
+                          ..classes = ['header_inset'])
+                        .element
+                  ]
+                : const [],
+          new HRElement(),
+          new IsolateSharedSummaryElement(_isolate, _events, queue: _r.queue)
+              .element,
+          new HRElement(),
+          new DivElement()
+            ..classes = ['memberList']
+            ..children = <Element>[
+              new DivElement()
+                ..classes = ['memberItem']
+                ..children = <Element>[
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..text = 'started at',
+                  new DivElement()
+                    ..classes = ['memberValue']
+                    ..text = '${_isolate.startTime}'
+                ],
+              new DivElement()
+                ..classes = ['memberItem']
+                ..children = <Element>[
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..text = 'uptime',
+                  new DivElement()
+                    ..classes = ['memberValue']
+                    ..text = '$uptime'
+                ],
+              new DivElement()
+                ..classes = ['memberItem']
+                ..children = <Element>[
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..text = 'root library',
+                  new DivElement()
+                    ..classes = ['memberValue']
+                    ..children = <Element>[
+                      _isolate.rootLibrary == null
+                          ? (new SpanElement()..text = 'loading...')
+                          : new LibraryRefElement(
+                                  _isolate, _isolate.rootLibrary,
+                                  queue: _r.queue)
+                              .element
+                    ]
+                ],
+              new DivElement()
+                ..classes = ['memberItem']
+                ..children = _isolate.entry != null
+                    ? [
+                        new DivElement()
+                          ..classes = ['memberName']
+                          ..text = 'entry',
+                        new DivElement()
+                          ..classes = ['memberValue']
+                          ..children = <Element>[
+                            new FunctionRefElement(_isolate, _isolate.entry,
+                                    queue: _r.queue)
+                                .element
+                          ]
+                      ]
+                    : const [],
+              new DivElement()
+                ..classes = ['memberItem']
+                ..children = <Element>[
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..text = 'isolate id',
+                  new DivElement()
+                    ..classes = ['memberValue']
+                    ..text = '${_isolate.number}'
+                ],
+              new DivElement()
+                ..classes = ['memberItem']
+                ..children = <Element>[
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..text = 'service protocol extensions',
+                  new DivElement()
+                    ..classes = ['memberValue']
+                    ..text = '${_isolate.extensionRPCs}'
+                ],
+              new DivElement()
+                ..classes = ['memberItem']
+                ..children = <Element>[
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..text = 'allocated zone handle count',
+                  new DivElement()
+                    ..classes = ['memberValue']
+                    ..text = '${_isolate.numZoneHandles}'
+                ],
+              new DivElement()
+                ..classes = ['memberItem']
+                ..children = <Element>[
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..text = 'allocated scoped handle count',
+                  new DivElement()
+                    ..classes = ['memberValue']
+                    ..text = '${_isolate.numScopedHandles}'
+                ],
+              new DivElement()
+                ..classes = ['memberItem']
+                ..children = <Element>[
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..text = 'object store',
+                  new DivElement()
+                    ..classes = ['memberValue']
+                    ..children = <Element>[
+                      new AnchorElement(href: Uris.objectStore(_isolate))
+                        ..text = 'object store'
+                    ]
+                ],
+              new DivElement()
+                ..classes = ['memberItem']
+                ..children = <Element>[
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..text = 'zone capacity high watermark'
+                    ..title = '''The maximum amount of native zone memory
+                    allocated by the isolate over it\'s life.''',
+                  new DivElement()
+                    ..classes = ['memberValue']
+                    ..text = Utils.formatSize(_isolate.zoneHighWatermark)
+                    ..title = '${_isolate.zoneHighWatermark}B'
+                ],
+              new BRElement(),
+              new DivElement()
+                ..classes = ['memberItem']
+                ..children = <Element>[
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..text = 'libraries (${libraries.length})',
+                  new DivElement()
+                    ..classes = ['memberValue']
+                    ..children = <Element>[
+                      (new CurlyBlockElement(queue: _r.queue)
+                            ..content = libraries
+                                .map<Element>((l) => new DivElement()
+                                  ..children = <Element>[
+                                    new LibraryRefElement(_isolate, l,
+                                            queue: _r.queue)
+                                        .element
+                                  ])
+                                .toList())
+                          .element
+                    ]
+                ],
+              new DivElement()
+                ..classes = ['memberItem']
+                ..children = <Element>[
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..text = 'threads (${threads.length})',
+                  new DivElement()
+                    ..classes = ['memberValue']
+                    ..children = <Element>[
+                      (new CurlyBlockElement(queue: _r.queue)
+                            ..content =
+                                threads.map<Element>(_populateThreadInfo))
+                          .element
+                    ]
+                ]
+            ],
+          new HRElement(),
+          new EvalBoxElement(_isolate, _isolate.rootLibrary, _objects, _eval,
+                  queue: _r.queue)
+              .element,
+          new DivElement()
+            ..children = _rootScript != null
+                ? [
+                    new HRElement(),
+                    new ScriptInsetElement(
+                            _isolate, _rootScript, _scripts, _objects, _events,
+                            queue: _r.queue)
+                        .element
+                  ]
+                : const [],
+          new HRElement(),
+          new ViewFooterElement(queue: _r.queue).element
+        ]
+    ];
+  }
+
+  DivElement _populateThreadInfo(M.Thread t) {
+    return new DivElement()
+      ..classes = ['indent']
+      ..children = <Element>[
+        new SpanElement()..text = '${t.id} ',
+        (new CurlyBlockElement(queue: _r.queue)
+              ..content = <Element>[
+                new DivElement()
+                  ..classes = ['indent']
+                  ..text = 'kind ${t.kindString}',
+                new DivElement()
+                  ..classes = ['indent']
+                  ..title = '${t.zoneHighWatermark}B'
+                  ..text = 'zone capacity high watermark '
+                      '${Utils.formatSize(t.zoneHighWatermark)}',
+                new DivElement()
+                  ..classes = ['indent']
+                  ..title = '${t.zoneCapacity}B'
+                  ..text = 'current zone capacity ' +
+                      '${Utils.formatSize(t.zoneCapacity)}',
+              ])
+            .element
+      ];
+  }
+
+  Future _loadExtraData() async {
+    _function = null;
+    _rootScript = null;
+    final frame = M.topFrame(_isolate.pauseEvent);
+    if (frame != null) {
+      _function = await _functions.get(_isolate, frame.function.id);
+    }
+    if (_isolate.rootLibrary != null) {
+      final rootLibrary =
+          await _libraries.get(_isolate, _isolate.rootLibrary.id);
+      _rootScript = rootLibrary.rootScript;
+    }
+    _r.dirty();
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/json_view.dart b/runtime/observatory_2/lib/src/elements/json_view.dart
new file mode 100644
index 0000000..f003d60
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/json_view.dart
@@ -0,0 +1,148 @@
+// 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.
+
+library json_view_element;
+
+import 'dart:async';
+import 'dart:html';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/nav/notify.dart';
+import 'package:observatory_2/src/elements/nav/top_menu.dart';
+import 'package:observatory_2/src/elements/view_footer.dart';
+
+class JSONViewElement extends CustomElement implements Renderable {
+  RenderingScheduler<JSONViewElement> _r;
+
+  Stream<RenderedEvent<JSONViewElement>> get onRendered => _r.onRendered;
+
+  M.NotificationRepository _notifications;
+  Map _map;
+
+  M.NotificationRepository get notifications => _notifications;
+  Map get map => _map;
+
+  factory JSONViewElement(Map map, M.NotificationRepository notifications,
+      {RenderingQueue queue}) {
+    assert(notifications != null);
+    assert(map != null);
+    JSONViewElement e = new JSONViewElement.created();
+    e._r = new RenderingScheduler<JSONViewElement>(e, queue: queue);
+    e._notifications = notifications;
+    e._map = map;
+    return e;
+  }
+
+  JSONViewElement.created() : super.created('json-view');
+
+  @override
+  attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+  }
+
+  void render() {
+    children = <Element>[
+      navBar(<Element>[
+        new NavTopMenuElement(queue: _r.queue).element,
+        new NavNotifyElement(_notifications, queue: _r.queue).element
+      ]),
+      new DivElement()
+        ..classes = ['content-centered-big']
+        ..children = <Element>[
+          new HeadingElement.h2()..text = 'Object',
+          new HRElement(),
+          new PreElement()..text = JSONPretty.stringify(_map),
+          new HRElement(),
+          new ViewFooterElement(queue: _r.queue).element
+        ]
+    ];
+  }
+}
+
+class JSONPretty {
+  JSONPretty._();
+
+  static String stringify(Map map) => new JSONPretty._()._stringify(map);
+
+  String _stringify(Map map) {
+    _buffer.clear();
+    _buffer.write('{\n');
+    _printMap(map, 0);
+    _buffer.write('}\n');
+    return _buffer.toString();
+  }
+
+  void _printMap(Map map, int depth) {
+    if (_seen.contains(map)) {
+      return;
+    }
+    _seen.add(map);
+    for (var k in map.keys) {
+      var v = map[k];
+      if (v is Map) {
+        _writeIndent(depth);
+        _buffer.write('"$k": {\n');
+        _printMap(v, depth + 1);
+        _writeIndent(depth);
+        _buffer.write('}\n');
+      } else if (v is List) {
+        _writeIndent(depth);
+        _buffer.write('"$k": [\n');
+        _printList(v, depth + 1);
+        _writeIndent(depth);
+        _buffer.write(']\n');
+      } else {
+        _writeIndent(depth);
+        _buffer.write('"$k": $v');
+        _buffer.write('\n');
+      }
+    }
+    _seen.remove(map);
+  }
+
+  void _printList(List list, int depth) {
+    if (_seen.contains(list)) {
+      return;
+    }
+    _seen.add(list);
+    for (var v in list) {
+      if (v is Map) {
+        _writeIndent(depth);
+        _buffer.write('{\n');
+        _printMap(v, depth + 1);
+        _writeIndent(depth);
+        _buffer.write('}\n');
+      } else if (v is List) {
+        _writeIndent(depth);
+        _buffer.write('[\n');
+        _printList(v, depth + 1);
+        _writeIndent(depth);
+        _buffer.write(']\n');
+      } else {
+        _writeIndent(depth);
+        _buffer.write(v);
+        _buffer.write('\n');
+      }
+    }
+    _seen.remove(list);
+  }
+
+  void _writeIndent(int depth) {
+    const tab = '  '; // 2 spaces.
+    _buffer.write(tab * depth);
+  }
+
+  final _buffer = new StringBuffer();
+  final _seen = new Set();
+}
diff --git a/runtime/observatory_2/lib/src/elements/library_ref.dart b/runtime/observatory_2/lib/src/elements/library_ref.dart
new file mode 100644
index 0000000..83d58cc
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/library_ref.dart
@@ -0,0 +1,58 @@
+// 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.
+
+library library_ref_element;
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/models.dart' as M show IsolateRef, LibraryRef;
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/helpers/uris.dart';
+
+class LibraryRefElement extends CustomElement implements Renderable {
+  RenderingScheduler<LibraryRefElement> _r;
+
+  Stream<RenderedEvent<LibraryRefElement>> get onRendered => _r.onRendered;
+
+  M.IsolateRef _isolate;
+  M.LibraryRef _library;
+
+  M.IsolateRef get isolate => _isolate;
+  M.LibraryRef get library => _library;
+
+  factory LibraryRefElement(M.IsolateRef isolate, M.LibraryRef library,
+      {RenderingQueue queue}) {
+    assert(isolate != null);
+    assert(library != null);
+    LibraryRefElement e = new LibraryRefElement.created();
+    e._r = new RenderingScheduler<LibraryRefElement>(e, queue: queue);
+    e._isolate = isolate;
+    e._library = library;
+    return e;
+  }
+
+  LibraryRefElement.created() : super.created('library-ref');
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+  }
+
+  void render() {
+    final name = _library.name;
+    children = <Element>[
+      new AnchorElement(href: Uris.inspect(_isolate, object: _library))
+        ..text = (name == null || name.isEmpty) ? 'unnamed' : name
+    ];
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/library_view.dart b/runtime/observatory_2/lib/src/elements/library_view.dart
new file mode 100644
index 0000000..b1c9656
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/library_view.dart
@@ -0,0 +1,333 @@
+// 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.
+
+library library_view_element;
+
+import 'dart:async';
+import 'dart:html';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/class_ref.dart';
+import 'package:observatory_2/src/elements/curly_block.dart';
+import 'package:observatory_2/src/elements/eval_box.dart';
+import 'package:observatory_2/src/elements/field_ref.dart';
+import 'package:observatory_2/src/elements/function_ref.dart';
+import 'package:observatory_2/src/elements/helpers/any_ref.dart';
+import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/library_ref.dart';
+import 'package:observatory_2/src/elements/nav/isolate_menu.dart';
+import 'package:observatory_2/src/elements/nav/library_menu.dart';
+import 'package:observatory_2/src/elements/nav/notify.dart';
+import 'package:observatory_2/src/elements/nav/refresh.dart';
+import 'package:observatory_2/src/elements/nav/top_menu.dart';
+import 'package:observatory_2/src/elements/nav/vm_menu.dart';
+import 'package:observatory_2/src/elements/object_common.dart';
+import 'package:observatory_2/src/elements/script_ref.dart';
+import 'package:observatory_2/src/elements/script_inset.dart';
+import 'package:observatory_2/src/elements/view_footer.dart';
+
+class LibraryViewElement extends CustomElement implements Renderable {
+  RenderingScheduler<LibraryViewElement> _r;
+
+  Stream<RenderedEvent<LibraryViewElement>> get onRendered => _r.onRendered;
+
+  M.VM _vm;
+  M.IsolateRef _isolate;
+  M.EventRepository _events;
+  M.NotificationRepository _notifications;
+  M.Library _library;
+  M.LibraryRepository _libraries;
+  M.FieldRepository _fields;
+  M.RetainedSizeRepository _retainedSizes;
+  M.ReachableSizeRepository _reachableSizes;
+  M.InboundReferencesRepository _references;
+  M.RetainingPathRepository _retainingPaths;
+  M.ScriptRepository _scripts;
+  M.ObjectRepository _objects;
+  M.EvalRepository _eval;
+  Iterable<M.Field> _variables;
+
+  M.VMRef get vm => _vm;
+  M.IsolateRef get isolate => _isolate;
+  M.NotificationRepository get notifications => _notifications;
+  M.Library get library => _library;
+
+  factory LibraryViewElement(
+      M.VM vm,
+      M.IsolateRef isolate,
+      M.Library library,
+      M.EventRepository events,
+      M.NotificationRepository notifications,
+      M.LibraryRepository libraries,
+      M.FieldRepository fields,
+      M.RetainedSizeRepository retainedSizes,
+      M.ReachableSizeRepository reachableSizes,
+      M.InboundReferencesRepository references,
+      M.RetainingPathRepository retainingPaths,
+      M.ScriptRepository scripts,
+      M.ObjectRepository objects,
+      M.EvalRepository eval,
+      {RenderingQueue queue}) {
+    assert(vm != null);
+    assert(isolate != null);
+    assert(events != null);
+    assert(notifications != null);
+    assert(library != null);
+    assert(libraries != null);
+    assert(fields != null);
+    assert(retainedSizes != null);
+    assert(reachableSizes != null);
+    assert(references != null);
+    assert(retainingPaths != null);
+    assert(scripts != null);
+    assert(objects != null);
+    assert(eval != null);
+    LibraryViewElement e = new LibraryViewElement.created();
+    e._r = new RenderingScheduler<LibraryViewElement>(e, queue: queue);
+    e._vm = vm;
+    e._isolate = isolate;
+    e._events = events;
+    e._notifications = notifications;
+    e._library = library;
+    e._libraries = libraries;
+    e._fields = fields;
+    e._retainedSizes = retainedSizes;
+    e._reachableSizes = reachableSizes;
+    e._references = references;
+    e._retainingPaths = retainingPaths;
+    e._scripts = scripts;
+    e._objects = objects;
+    e._eval = eval;
+    return e;
+  }
+
+  LibraryViewElement.created() : super.created('library-view');
+
+  @override
+  attached() {
+    super.attached();
+    _r.enable();
+    _refresh();
+  }
+
+  @override
+  detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+  }
+
+  void render() {
+    final rootScript = library.rootScript;
+
+    children = <Element>[
+      navBar(<Element>[
+        new NavTopMenuElement(queue: _r.queue).element,
+        new NavVMMenuElement(_vm, _events, queue: _r.queue).element,
+        new NavIsolateMenuElement(_isolate, _events, queue: _r.queue).element,
+        new NavLibraryMenuElement(_isolate, _library, queue: _r.queue).element,
+        (new NavRefreshElement(queue: _r.queue)
+              ..onRefresh.listen((e) async {
+                e.element.disabled = true;
+                _refresh();
+              }))
+            .element,
+        new NavNotifyElement(_notifications, queue: _r.queue).element
+      ]),
+      new DivElement()
+        ..classes = ['content-centered-big']
+        ..children = <Element>[
+          new HeadingElement.h2()..text = 'Library',
+          new HRElement(),
+          new ObjectCommonElement(_isolate, _library, _retainedSizes,
+                  _reachableSizes, _references, _retainingPaths, _objects,
+                  queue: _r.queue)
+              .element,
+          new DivElement()
+            ..classes = ['memberList']
+            ..children = <Element>[
+              new DivElement()
+                ..classes = ['memberItem']
+                ..children = <Element>[
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..text = 'uri',
+                  new DivElement()
+                    ..classes = ['memberValue']
+                    ..text = _library.uri
+                ],
+              new DivElement()
+                ..classes = ['memberItem']
+                ..children = <Element>[
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..text = 'vm name',
+                  new DivElement()
+                    ..classes = ['memberValue']
+                    ..text = _library.vmName
+                ]
+            ],
+          new HRElement(),
+          new EvalBoxElement(_isolate, _library, _objects, _eval,
+                  queue: _r.queue)
+              .element,
+          new HRElement(),
+          _createDependencies(),
+          new BRElement(),
+          _createScripts(),
+          new BRElement(),
+          _createClasses(),
+          new BRElement(),
+          _createVariables(),
+          new BRElement(),
+          _createFunctions(),
+          if (rootScript != null) ...[
+            new HRElement(),
+            new ScriptInsetElement(
+                    _isolate, rootScript, _scripts, _objects, _events,
+                    queue: _r.queue)
+                .element
+          ],
+          new HRElement(),
+          new ViewFooterElement(queue: _r.queue).element
+        ]
+    ];
+  }
+
+  Future _refresh() async {
+    _library = await _libraries.get(_isolate, _library.id);
+    _variables = null;
+    _r.dirty();
+    _variables = await Future.wait(
+        _library.variables.map((field) => _fields.get(_isolate, field.id)));
+    _r.dirty();
+  }
+
+  Element _createDependencies() {
+    if (_library.dependencies.isEmpty) {
+      return new SpanElement();
+    }
+    final dependencies = _library.dependencies.toList();
+    return new DivElement()
+      ..children = <Element>[
+        new SpanElement()..text = 'dependencies (${dependencies.length}) ',
+        (new CurlyBlockElement(queue: _r.queue)
+              ..content = dependencies
+                  .map<Element>((d) => new DivElement()
+                    ..classes = ['indent']
+                    ..children = <Element>[
+                      new SpanElement()
+                        ..text = d.isImport ? 'import ' : 'export ',
+                      new LibraryRefElement(_isolate, d.target, queue: _r.queue)
+                          .element,
+                      new SpanElement()
+                        ..text = d.prefix == null ? '' : ' as ${d.prefix}',
+                      new SpanElement()..text = d.isDeferred ? ' deferred' : '',
+                    ])
+                  .toList())
+            .element
+      ];
+  }
+
+  Element _createScripts() {
+    if (_library.scripts.isEmpty) {
+      return new SpanElement();
+    }
+    final scripts = _library.scripts.toList();
+    return new DivElement()
+      ..children = <Element>[
+        new SpanElement()..text = 'scripts (${scripts.length}) ',
+        (new CurlyBlockElement(queue: _r.queue)
+              ..content = scripts
+                  .map<Element>((s) => new DivElement()
+                    ..classes = ['indent']
+                    ..children = <Element>[
+                      new ScriptRefElement(_isolate, s, queue: _r.queue).element
+                    ])
+                  .toList())
+            .element
+      ];
+  }
+
+  Element _createClasses() {
+    if (_library.classes.isEmpty) {
+      return new SpanElement();
+    }
+    final classes = _library.classes.toList();
+    return new DivElement()
+      ..children = <Element>[
+        new SpanElement()..text = 'classes (${classes.length}) ',
+        (new CurlyBlockElement(queue: _r.queue)
+              ..content = classes
+                  .map<Element>((c) => new DivElement()
+                    ..classes = ['indent']
+                    ..children = <Element>[
+                      new ClassRefElement(_isolate, c, queue: _r.queue).element
+                    ])
+                  .toList())
+            .element
+      ];
+  }
+
+  Element _createVariables() {
+    if (_library.variables.isEmpty) {
+      return new SpanElement();
+    }
+    final variables = _library.variables.toList();
+    return new DivElement()
+      ..children = <Element>[
+        new SpanElement()..text = 'variables (${variables.length}) ',
+        (new CurlyBlockElement(queue: _r.queue)
+              ..content = <Element>[
+                _variables == null
+                    ? (new SpanElement()..text = 'loading...')
+                    : (new DivElement()
+                      ..classes = ['indent', 'memberList']
+                      ..children = _variables
+                          .map<Element>((f) => new DivElement()
+                            ..classes = ['memberItem']
+                            ..children = <Element>[
+                              new DivElement()
+                                ..classes = ['memberName']
+                                ..children = <Element>[
+                                  new FieldRefElement(_isolate, f, _objects,
+                                          queue: _r.queue)
+                                      .element
+                                ],
+                              new DivElement()
+                                ..classes = ['memberValue']
+                                ..children = <Element>[
+                                  new SpanElement()..text = ' = ',
+                                  anyRef(_isolate, f.staticValue, _objects,
+                                      queue: _r.queue)
+                                ]
+                            ])
+                          .toList())
+              ])
+            .element
+      ];
+  }
+
+  Element _createFunctions() {
+    if (_library.functions.isEmpty) {
+      return new SpanElement();
+    }
+    final functions = _library.functions.toList();
+    return new DivElement()
+      ..children = <Element>[
+        new SpanElement()..text = 'functions (${functions.length}) ',
+        (new CurlyBlockElement(queue: _r.queue)
+              ..content = functions
+                  .map<Element>((f) => new DivElement()
+                    ..classes = ['indent']
+                    ..children = <Element>[
+                      new FunctionRefElement(_isolate, f, queue: _r.queue)
+                          .element
+                    ])
+                  .toList())
+            .element
+      ];
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/local_var_descriptors_ref.dart b/runtime/observatory_2/lib/src/elements/local_var_descriptors_ref.dart
new file mode 100644
index 0000000..7bf512a
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/local_var_descriptors_ref.dart
@@ -0,0 +1,64 @@
+// 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.
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/models.dart' as M
+    show IsolateRef, LocalVarDescriptorsRef;
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/helpers/uris.dart';
+
+class LocalVarDescriptorsRefElement extends CustomElement
+    implements Renderable {
+  RenderingScheduler<LocalVarDescriptorsRefElement> _r;
+
+  Stream<RenderedEvent<LocalVarDescriptorsRefElement>> get onRendered =>
+      _r.onRendered;
+
+  M.IsolateRef _isolate;
+  M.LocalVarDescriptorsRef _localVar;
+
+  M.IsolateRef get isolate => _isolate;
+  M.LocalVarDescriptorsRef get localVar => _localVar;
+
+  factory LocalVarDescriptorsRefElement(
+      M.IsolateRef isolate, M.LocalVarDescriptorsRef localVar,
+      {RenderingQueue queue}) {
+    assert(isolate != null);
+    assert(localVar != null);
+    LocalVarDescriptorsRefElement e =
+        new LocalVarDescriptorsRefElement.created();
+    e._r =
+        new RenderingScheduler<LocalVarDescriptorsRefElement>(e, queue: queue);
+    e._isolate = isolate;
+    e._localVar = localVar;
+    return e;
+  }
+
+  LocalVarDescriptorsRefElement.created() : super.created('var-ref');
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+  }
+
+  void render() {
+    final text = (_localVar.name == null || _localVar.name == '')
+        ? 'LocalVarDescriptors'
+        : _localVar.name;
+    children = <Element>[
+      new AnchorElement(href: Uris.inspect(_isolate, object: _localVar))
+        ..text = text
+    ];
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/logging.dart b/runtime/observatory_2/lib/src/elements/logging.dart
new file mode 100644
index 0000000..dd922a2
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/logging.dart
@@ -0,0 +1,115 @@
+// Copyright (c) 2015, 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.
+
+library logging_page;
+
+import 'dart:async';
+import 'dart:html';
+import 'package:logging/logging.dart';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
+import 'package:observatory_2/src/elements/helpers/nav_menu.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/logging_list.dart';
+import 'package:observatory_2/src/elements/nav/class_menu.dart';
+import 'package:observatory_2/src/elements/nav/isolate_menu.dart';
+import 'package:observatory_2/src/elements/nav/notify.dart';
+import 'package:observatory_2/src/elements/nav/refresh.dart';
+import 'package:observatory_2/src/elements/nav/top_menu.dart';
+import 'package:observatory_2/src/elements/nav/vm_menu.dart';
+import 'package:observatory_2/src/elements/view_footer.dart';
+
+class LoggingPageElement extends CustomElement implements Renderable {
+  RenderingScheduler<LoggingPageElement> _r;
+
+  Stream<RenderedEvent<LoggingPageElement>> get onRendered => _r.onRendered;
+
+  M.VM _vm;
+  M.IsolateRef _isolate;
+  M.EventRepository _events;
+  M.NotificationRepository _notifications;
+  Level _level = Level.ALL;
+
+  M.VMRef get vm => _vm;
+  M.IsolateRef get isolate => _isolate;
+  M.NotificationRepository get notifications => _notifications;
+
+  factory LoggingPageElement(M.VM vm, M.IsolateRef isolate,
+      M.EventRepository events, M.NotificationRepository notifications,
+      {RenderingQueue queue}) {
+    assert(vm != null);
+    assert(isolate != null);
+    assert(events != null);
+    assert(notifications != null);
+    LoggingPageElement e = new LoggingPageElement.created();
+    e._r = new RenderingScheduler<LoggingPageElement>(e, queue: queue);
+    e._vm = vm;
+    e._isolate = isolate;
+    e._events = events;
+    e._notifications = notifications;
+    return e;
+  }
+
+  LoggingPageElement.created() : super.created('logging-page');
+
+  @override
+  attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+  }
+
+  LoggingListElement _logs;
+
+  void render() {
+    _logs = _logs ?? new LoggingListElement(_isolate, _events);
+    _logs.level = _level;
+    children = <Element>[
+      navBar(<Element>[
+        new NavTopMenuElement(queue: _r.queue).element,
+        new NavVMMenuElement(_vm, _events, queue: _r.queue).element,
+        new NavIsolateMenuElement(_isolate, _events, queue: _r.queue).element,
+        navMenu('logging'),
+        (new NavRefreshElement(label: 'clear', queue: _r.queue)
+              ..onRefresh.listen((e) async {
+                e.element.disabled = true;
+                _logs = null;
+                _r.dirty();
+              }))
+            .element,
+        new NavNotifyElement(_notifications, queue: _r.queue).element
+      ]),
+      new DivElement()
+        ..classes = ['content-centered-big']
+        ..children = <Element>[
+          new HeadingElement.h2()..text = 'Logging',
+          new SpanElement()..text = 'Show messages with severity ',
+          _createLevelSelector(),
+          new HRElement(),
+          _logs.element
+        ]
+    ];
+  }
+
+  Element _createLevelSelector() {
+    var s = new SelectElement()
+      ..value = _level.name
+      ..children = Level.LEVELS.map((level) {
+        return new OptionElement(value: level.name, selected: _level == level)
+          ..text = level.name;
+      }).toList(growable: false);
+    s.onChange.listen((_) {
+      _level = Level.LEVELS[s.selectedIndex];
+      _r.dirty();
+    });
+    return s;
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/logging_list.dart b/runtime/observatory_2/lib/src/elements/logging_list.dart
new file mode 100644
index 0000000..bb160964
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/logging_list.dart
@@ -0,0 +1,84 @@
+// Copyright (c) 2015, 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.
+
+import 'dart:async';
+import 'dart:html';
+import 'package:logging/logging.dart';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/utils.dart';
+
+class LoggingListElement extends CustomElement implements Renderable {
+  RenderingScheduler<LoggingListElement> _r;
+
+  Stream<RenderedEvent<LoggingListElement>> get onRendered => _r.onRendered;
+
+  M.IsolateRef _isolate;
+  M.EventRepository _events;
+  StreamSubscription _subscription;
+  Level _level = Level.ALL;
+  final _logs = <Map>[];
+
+  M.IsolateRef get isolate => _isolate;
+  Level get level => _level;
+
+  set level(Level value) => _level = _r.checkAndReact(_level, value);
+
+  factory LoggingListElement(M.IsolateRef isolate, M.EventRepository events,
+      {RenderingQueue queue}) {
+    assert(isolate != null);
+    assert(events != null);
+    LoggingListElement e = new LoggingListElement.created();
+    e._r = new RenderingScheduler<LoggingListElement>(e, queue: queue);
+    e._isolate = isolate;
+    e._events = events;
+    return e;
+  }
+
+  LoggingListElement.created() : super.created('logging-list');
+
+  @override
+  attached() {
+    super.attached();
+    _r.enable();
+    _subscription = _events.onLoggingEvent.listen((e) {
+      if (e.isolate.id == _isolate.id) {
+        _logs.add(e.logRecord);
+        if (_shouldBeVisible(_logs.last)) {
+          _r.dirty();
+        }
+      }
+    });
+  }
+
+  @override
+  detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+    _subscription.cancel();
+  }
+
+  void render() {
+    children = _logs
+        .where(_shouldBeVisible)
+        .map<Element>((logRecord) => new DivElement()
+          ..classes = ['logItem', logRecord['level'].name]
+          ..children = <Element>[
+            new SpanElement()
+              ..classes = ['level']
+              ..text = logRecord['level'].name,
+            new SpanElement()
+              ..classes = ['time']
+              ..text = Utils.formatDateTime(logRecord['time']),
+            new SpanElement()
+              ..classes = ['message']
+              ..text = logRecord["message"].valueAsString
+          ])
+        .toList();
+  }
+
+  bool _shouldBeVisible(Map record) => _level.compareTo(record['level']) <= 0;
+}
diff --git a/runtime/observatory_2/lib/src/elements/megamorphiccache_ref.dart b/runtime/observatory_2/lib/src/elements/megamorphiccache_ref.dart
new file mode 100644
index 0000000..f38b59a
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/megamorphiccache_ref.dart
@@ -0,0 +1,63 @@
+// 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.
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/models.dart' as M
+    show IsolateRef, MegamorphicCacheRef;
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/helpers/uris.dart';
+
+class MegamorphicCacheRefElement extends CustomElement implements Renderable {
+  RenderingScheduler<MegamorphicCacheRefElement> _r;
+
+  Stream<RenderedEvent<MegamorphicCacheRefElement>> get onRendered =>
+      _r.onRendered;
+
+  M.IsolateRef _isolate;
+  M.MegamorphicCacheRef _cache;
+
+  M.IsolateRef get isolate => _isolate;
+  M.MegamorphicCacheRef get cache => _cache;
+
+  factory MegamorphicCacheRefElement(
+      M.IsolateRef isolate, M.MegamorphicCacheRef cache,
+      {RenderingQueue queue}) {
+    assert(isolate != null);
+    assert(cache != null);
+    MegamorphicCacheRefElement e = new MegamorphicCacheRefElement.created();
+    e._r = new RenderingScheduler<MegamorphicCacheRefElement>(e, queue: queue);
+    e._isolate = isolate;
+    e._cache = cache;
+    return e;
+  }
+
+  MegamorphicCacheRefElement.created() : super.created('megamorphic-cache-ref');
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+  }
+
+  void render() {
+    children = <Element>[
+      new AnchorElement(href: Uris.inspect(_isolate, object: _cache))
+        ..children = <Element>[
+          new SpanElement()
+            ..classes = ['emphasize']
+            ..text = 'MegarmorphicCache',
+          new SpanElement()..text = ' (${_cache.selector})'
+        ]
+    ];
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/megamorphiccache_view.dart b/runtime/observatory_2/lib/src/elements/megamorphiccache_view.dart
new file mode 100644
index 0000000..71bb435
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/megamorphiccache_view.dart
@@ -0,0 +1,185 @@
+// Copyright (c) 2015, 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.
+
+library megamorphiccache_view;
+
+import 'dart:async';
+import 'dart:html';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/context_ref.dart';
+import 'package:observatory_2/src/elements/curly_block.dart';
+import 'package:observatory_2/src/elements/helpers/any_ref.dart';
+import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
+import 'package:observatory_2/src/elements/helpers/nav_menu.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/nav/isolate_menu.dart';
+import 'package:observatory_2/src/elements/nav/notify.dart';
+import 'package:observatory_2/src/elements/nav/refresh.dart';
+import 'package:observatory_2/src/elements/nav/top_menu.dart';
+import 'package:observatory_2/src/elements/nav/vm_menu.dart';
+import 'package:observatory_2/src/elements/object_common.dart';
+import 'package:observatory_2/src/elements/view_footer.dart';
+
+class MegamorphicCacheViewElement extends CustomElement implements Renderable {
+  RenderingScheduler<MegamorphicCacheViewElement> _r;
+
+  Stream<RenderedEvent<MegamorphicCacheViewElement>> get onRendered =>
+      _r.onRendered;
+
+  M.VM _vm;
+  M.IsolateRef _isolate;
+  M.EventRepository _events;
+  M.NotificationRepository _notifications;
+  M.MegamorphicCache _cache;
+  M.MegamorphicCacheRepository _caches;
+  M.RetainedSizeRepository _retainedSizes;
+  M.ReachableSizeRepository _reachableSizes;
+  M.InboundReferencesRepository _references;
+  M.RetainingPathRepository _retainingPaths;
+  M.ObjectRepository _objects;
+
+  M.VMRef get vm => _vm;
+  M.IsolateRef get isolate => _isolate;
+  M.NotificationRepository get notifications => _notifications;
+  M.MegamorphicCache get cache => _cache;
+
+  factory MegamorphicCacheViewElement(
+      M.VM vm,
+      M.IsolateRef isolate,
+      M.MegamorphicCache cache,
+      M.EventRepository events,
+      M.NotificationRepository notifications,
+      M.MegamorphicCacheRepository caches,
+      M.RetainedSizeRepository retainedSizes,
+      M.ReachableSizeRepository reachableSizes,
+      M.InboundReferencesRepository references,
+      M.RetainingPathRepository retainingPaths,
+      M.ObjectRepository objects,
+      {RenderingQueue queue}) {
+    assert(vm != null);
+    assert(isolate != null);
+    assert(events != null);
+    assert(notifications != null);
+    assert(cache != null);
+    assert(caches != null);
+    assert(retainedSizes != null);
+    assert(reachableSizes != null);
+    assert(references != null);
+    assert(retainingPaths != null);
+    assert(objects != null);
+    MegamorphicCacheViewElement e = new MegamorphicCacheViewElement.created();
+    e._r = new RenderingScheduler<MegamorphicCacheViewElement>(e, queue: queue);
+    e._vm = vm;
+    e._isolate = isolate;
+    e._events = events;
+    e._notifications = notifications;
+    e._cache = cache;
+    e._caches = caches;
+    e._retainedSizes = retainedSizes;
+    e._reachableSizes = reachableSizes;
+    e._references = references;
+    e._retainingPaths = retainingPaths;
+    e._objects = objects;
+    return e;
+  }
+
+  MegamorphicCacheViewElement.created()
+      : super.created('megamorphiccache-view');
+
+  @override
+  attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+  }
+
+  void render() {
+    children = <Element>[
+      navBar(<Element>[
+        new NavTopMenuElement(queue: _r.queue).element,
+        new NavVMMenuElement(_vm, _events, queue: _r.queue).element,
+        new NavIsolateMenuElement(_isolate, _events, queue: _r.queue).element,
+        navMenu('megamorphic inline cache'),
+        (new NavRefreshElement(queue: _r.queue)
+              ..onRefresh.listen((e) async {
+                e.element.disabled = true;
+                _cache = await _caches.get(_isolate, _cache.id);
+                _r.dirty();
+              }))
+            .element,
+        new NavNotifyElement(_notifications, queue: _r.queue).element
+      ]),
+      new DivElement()
+        ..classes = ['content-centered-big']
+        ..children = <Element>[
+          new HeadingElement.h2()..text = 'Megamorphic Cache',
+          new HRElement(),
+          new ObjectCommonElement(_isolate, _cache, _retainedSizes,
+                  _reachableSizes, _references, _retainingPaths, _objects,
+                  queue: _r.queue)
+              .element,
+          new BRElement(),
+          new DivElement()
+            ..classes = ['memberList']
+            ..children = <Element>[
+              new DivElement()
+                ..classes = ['memberItem']
+                ..children = <Element>[
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..text = 'selector',
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..text = '${_cache.selector}'
+                ],
+              new DivElement()
+                ..classes = ['memberItem']
+                ..children = <Element>[
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..text = 'mask',
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..text = '${_cache.mask}'
+                ],
+              new DivElement()
+                ..classes = ['memberItem']
+                ..children = <Element>[
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..text = 'buckets',
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..children = <Element>[
+                      anyRef(_isolate, _cache.buckets, _objects,
+                          queue: _r.queue)
+                    ]
+                ],
+              new DivElement()
+                ..classes = ['memberItem']
+                ..children = <Element>[
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..text = 'argumentsDescriptor',
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..children = <Element>[
+                      anyRef(_isolate, _cache.argumentsDescriptor, _objects,
+                          queue: _r.queue)
+                    ]
+                ]
+            ],
+          new HRElement(),
+          new ViewFooterElement(queue: _r.queue).element
+        ]
+    ];
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/metric/details.dart b/runtime/observatory_2/lib/src/elements/metric/details.dart
new file mode 100644
index 0000000..b2d4eb2
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/metric/details.dart
@@ -0,0 +1,168 @@
+// Copyright (c) 2016, 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.
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+
+class MetricDetailsElement extends CustomElement implements Renderable {
+  RenderingScheduler<MetricDetailsElement> _r;
+
+  Stream<RenderedEvent<MetricDetailsElement>> get onRendered => _r.onRendered;
+
+  M.IsolateRef _isolate;
+  M.Metric _metric;
+  M.MetricRepository _metrics;
+
+  M.IsolateRef get isolate => _isolate;
+  M.Metric get metric => _metric;
+
+  factory MetricDetailsElement(
+      M.IsolateRef isolate, M.Metric metric, M.MetricRepository metrics,
+      {RenderingQueue queue}) {
+    assert(isolate != null);
+    assert(metric != null);
+    assert(metrics != null);
+    MetricDetailsElement e = new MetricDetailsElement.created();
+    e._r = new RenderingScheduler<MetricDetailsElement>(e, queue: queue);
+    e._isolate = isolate;
+    e._metric = metric;
+    e._metrics = metrics;
+    return e;
+  }
+
+  MetricDetailsElement.created() : super.created('metric-details');
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+  }
+
+  void render() {
+    children = <Element>[
+      new DivElement()
+        ..classes = ['memberList']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberItem']
+            ..children = <Element>[
+              new DivElement()
+                ..classes = ['memberName']
+                ..text = 'name',
+              new DivElement()
+                ..classes = ['memberValue']
+                ..text = _metric.name,
+            ],
+          new DivElement()
+            ..classes = ['memberItem']
+            ..children = <Element>[
+              new DivElement()
+                ..classes = ['memberName']
+                ..text = 'description',
+              new DivElement()
+                ..classes = ['memberValue']
+                ..text = _metric.description,
+            ],
+          new DivElement()
+            ..classes = ['memberItem']
+            ..children = <Element>[
+              new DivElement()
+                ..classes = ['memberName']
+                ..text = 'refresh rate',
+              new DivElement()
+                ..classes = ['memberValue']
+                ..children = _createRefreshRateSelect(),
+            ],
+          new DivElement()
+            ..classes = ['memberItem']
+            ..children = <Element>[
+              new DivElement()
+                ..classes = ['memberName']
+                ..text = 'buffer size',
+              new DivElement()
+                ..classes = ['memberValue']
+                ..children = _createBufferSizeSelect(),
+            ]
+        ]
+    ];
+  }
+
+  List<Element> _createRefreshRateSelect() {
+    final current = _metrics.getSamplingRate(_isolate, _metric);
+    var s;
+    return [
+      s = new SelectElement()
+        ..value = _rateToString(current)
+        ..children = M.MetricSamplingRate.values.map((rate) {
+          return new OptionElement(
+              value: _rateToString(current), selected: current == rate)
+            ..text = _rateToString(rate);
+        }).toList(growable: false)
+        ..onChange.listen((_) {
+          _metrics.setSamplingRate(
+              _isolate, _metric, M.MetricSamplingRate.values[s.selectedIndex]);
+          _r.dirty();
+        })
+    ];
+  }
+
+  List<Element> _createBufferSizeSelect() {
+    final current = _metrics.getBufferSize(_isolate, _metric);
+    var s;
+    return [
+      s = new SelectElement()
+        ..value = _sizeToString(current)
+        ..children = M.MetricBufferSize.values.map((rate) {
+          return new OptionElement(
+              value: _sizeToString(current), selected: current == rate)
+            ..text = _sizeToString(rate);
+        }).toList(growable: false)
+        ..onChange.listen((_) {
+          _metrics.setBufferSize(
+              _isolate, _metric, M.MetricBufferSize.values[s.selectedIndex]);
+          _r.dirty();
+        })
+    ];
+  }
+
+  static String _rateToString(M.MetricSamplingRate rate) {
+    switch (rate) {
+      case M.MetricSamplingRate.off:
+        return 'Never';
+      case M.MetricSamplingRate.e100ms:
+        return 'Ten times per second';
+      case M.MetricSamplingRate.e1s:
+        return 'Once a second';
+      case M.MetricSamplingRate.e2s:
+        return 'Every two seconds';
+      case M.MetricSamplingRate.e4s:
+        return 'Every four seconds';
+      case M.MetricSamplingRate.e8s:
+        return 'Every eight seconds';
+    }
+    throw new Exception('Unknown MetricSamplingRate ($rate)');
+  }
+
+  static String _sizeToString(M.MetricBufferSize size) {
+    switch (size) {
+      case M.MetricBufferSize.n10samples:
+        return '10';
+      case M.MetricBufferSize.n100samples:
+        return '100';
+      case M.MetricBufferSize.n1000samples:
+        return '1000';
+    }
+    throw new Exception('Unknown MetricSamplingRate ($size)');
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/metric/graph.dart b/runtime/observatory_2/lib/src/elements/metric/graph.dart
new file mode 100644
index 0000000..4120020
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/metric/graph.dart
@@ -0,0 +1,117 @@
+// Copyright (c) 2016, 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.
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+
+class MetricGraphElement extends CustomElement implements Renderable {
+  RenderingScheduler<MetricGraphElement> _r;
+
+  Stream<RenderedEvent<MetricGraphElement>> get onRendered => _r.onRendered;
+
+  M.IsolateRef _isolate;
+  M.Metric _metric;
+  M.MetricRepository _metrics;
+  Timer _timer;
+
+  M.IsolateRef get isolate => _isolate;
+  M.Metric get metric => _metric;
+
+  factory MetricGraphElement(
+      M.IsolateRef isolate, M.Metric metric, M.MetricRepository metrics,
+      {RenderingQueue queue}) {
+    assert(isolate != null);
+    assert(metric != null);
+    assert(metrics != null);
+    MetricGraphElement e = new MetricGraphElement.created();
+    e._r = new RenderingScheduler<MetricGraphElement>(e, queue: queue);
+    e._isolate = isolate;
+    e._metric = metric;
+    e._metrics = metrics;
+    return e;
+  }
+
+  MetricGraphElement.created() : super.created('metric-graph');
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+    _timer = new Timer.periodic(const Duration(seconds: 1), (_) => _r.dirty());
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+    _timer.cancel();
+  }
+
+  void render() {
+    final min = _metrics.getMinValue(_isolate, _metric);
+    final max = _metrics.getMaxValue(_isolate, _metric);
+    final rows = _metrics
+        .getSamples(_isolate, _metric)
+        .map((s) => [s.time.millisecondsSinceEpoch, s.value])
+        .toList();
+    final current = rows.last.last;
+
+    var message = 'current: $current';
+    if (min != null) {
+      message = 'min: $min, $message';
+    }
+    if (max != null) {
+      message = message + ', max: $max';
+    }
+
+    final host = new DivElement();
+    children = <Element>[
+      new DivElement()
+        ..classes = ['memberList']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberItem']
+            ..children = min == null
+                ? const []
+                : [
+                    new DivElement()
+                      ..classes = ['memberName']
+                      ..text = 'min',
+                    new DivElement()
+                      ..classes = ['memberValue']
+                      ..text = '$min'
+                  ],
+          new DivElement()
+            ..classes = ['memberItem']
+            ..children = <Element>[
+              new DivElement()
+                ..classes = ['memberName']
+                ..text = 'current',
+              new DivElement()
+                ..classes = ['memberValue']
+                ..text = '$current'
+            ],
+          new DivElement()
+            ..classes = ['memberItem']
+            ..children = max == null
+                ? const []
+                : [
+                    new DivElement()
+                      ..classes = ['memberName']
+                      ..text = 'max',
+                    new DivElement()
+                      ..classes = ['memberValue']
+                      ..text = '$max'
+                  ]
+        ],
+    ];
+    if (rows.length <= 1) {
+      return;
+    }
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/metrics.dart b/runtime/observatory_2/lib/src/elements/metrics.dart
new file mode 100644
index 0000000..b75d7ca
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/metrics.dart
@@ -0,0 +1,159 @@
+// Copyright (c) 2014, 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.
+
+library metrics;
+
+import 'dart:async';
+import 'dart:html';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
+import 'package:observatory_2/src/elements/helpers/nav_menu.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/metric/details.dart';
+import 'package:observatory_2/src/elements/metric/graph.dart';
+import 'package:observatory_2/src/elements/nav/isolate_menu.dart';
+import 'package:observatory_2/src/elements/nav/notify.dart';
+import 'package:observatory_2/src/elements/nav/refresh.dart';
+import 'package:observatory_2/src/elements/nav/top_menu.dart';
+import 'package:observatory_2/src/elements/nav/vm_menu.dart';
+
+class MetricsPageElement extends CustomElement implements Renderable {
+  RenderingScheduler<MetricsPageElement> _r;
+
+  Stream<RenderedEvent<MetricsPageElement>> get onRendered => _r.onRendered;
+
+  M.VM _vm;
+  M.IsolateRef _isolate;
+  M.EventRepository _events;
+  M.NotificationRepository _notifications;
+  M.MetricRepository _metrics;
+  List<M.Metric> _available;
+  M.Metric _selected;
+
+  M.VMRef get vm => _vm;
+  M.IsolateRef get isolate => _isolate;
+  M.NotificationRepository get notifications => _notifications;
+
+  factory MetricsPageElement(
+      M.VM vm,
+      M.IsolateRef isolate,
+      M.EventRepository events,
+      M.NotificationRepository notifications,
+      M.MetricRepository metrics,
+      {RenderingQueue queue}) {
+    assert(vm != null);
+    assert(isolate != null);
+    assert(events != null);
+    assert(notifications != null);
+    MetricsPageElement e = new MetricsPageElement.created();
+    e._r = new RenderingScheduler<MetricsPageElement>(e, queue: queue);
+    e._vm = vm;
+    e._isolate = isolate;
+    e._events = events;
+    e._notifications = notifications;
+    e._metrics = metrics;
+    return e;
+  }
+
+  MetricsPageElement.created() : super.created('metrics-page');
+
+  @override
+  attached() {
+    super.attached();
+    _r.enable();
+    _refresh();
+  }
+
+  @override
+  detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+  }
+
+  void render() {
+    children = <Element>[
+      navBar(<Element>[
+        new NavTopMenuElement(queue: _r.queue).element,
+        new NavVMMenuElement(_vm, _events, queue: _r.queue).element,
+        new NavIsolateMenuElement(_isolate, _events, queue: _r.queue).element,
+        navMenu('metrics'),
+        (new NavRefreshElement(queue: _r.queue)
+              ..onRefresh.listen((e) {
+                e.element.disabled = true;
+                _refresh();
+              }))
+            .element,
+        new NavNotifyElement(_notifications, queue: _r.queue).element
+      ]),
+      new DivElement()
+        ..classes = ['content-centered-big']
+        ..children = <Element>[
+          new HeadingElement.h2()..text = 'Metrics',
+          new HRElement(),
+          new DivElement()
+            ..classes = ['memberList']
+            ..children = <Element>[
+              new DivElement()
+                ..classes = ['memberItem']
+                ..children = <Element>[
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..text = 'Metric',
+                  new DivElement()
+                    ..classes = ['memberValue']
+                    ..children = _available == null
+                        ? [new SpanElement()..text = 'Loading..']
+                        : _createMetricSelect()
+                ]
+            ],
+          new HRElement(),
+          new DivElement()
+            ..children = _selected == null
+                ? const []
+                : [
+                    new MetricDetailsElement(_isolate, _selected, _metrics,
+                            queue: _r.queue)
+                        .element
+                  ],
+          new HRElement(),
+          new DivElement()
+            ..classes = ['graph']
+            ..children = _selected == null
+                ? const []
+                : [
+                    new MetricGraphElement(_isolate, _selected, _metrics,
+                            queue: _r.queue)
+                        .element
+                  ]
+        ],
+    ];
+  }
+
+  Future _refresh() async {
+    _available = (await _metrics.list(_isolate)).toList();
+    if (!_available.contains(_selected)) {
+      _selected = _available.first;
+    }
+    _r.dirty();
+  }
+
+  List<Element> _createMetricSelect() {
+    var s;
+    return [
+      s = new SelectElement()
+        ..value = _selected.name
+        ..children = _available.map((metric) {
+          return new OptionElement(
+              value: metric.name, selected: _selected == metric)
+            ..text = metric.name;
+        }).toList(growable: false)
+        ..onChange.listen((_) {
+          _selected = _available[s.selectedIndex];
+          _r.dirty();
+        })
+    ];
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/native_memory_profiler.dart b/runtime/observatory_2/lib/src/elements/native_memory_profiler.dart
new file mode 100644
index 0000000..5eae524
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/native_memory_profiler.dart
@@ -0,0 +1,157 @@
+// 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.
+
+library native_memory_profile;
+
+import 'dart:async';
+import 'dart:html';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/cpu_profile/virtual_tree.dart';
+import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
+import 'package:observatory_2/src/elements/helpers/nav_menu.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/helpers/uris.dart';
+import 'package:observatory_2/src/elements/nav/notify.dart';
+import 'package:observatory_2/src/elements/nav/refresh.dart';
+import 'package:observatory_2/src/elements/nav/top_menu.dart';
+import 'package:observatory_2/src/elements/nav/vm_menu.dart';
+import 'package:observatory_2/src/elements/sample_buffer_control.dart';
+import 'package:observatory_2/src/elements/stack_trace_tree_config.dart';
+
+class NativeMemoryProfileElement extends CustomElement implements Renderable {
+  RenderingScheduler<NativeMemoryProfileElement> _r;
+
+  Stream<RenderedEvent<NativeMemoryProfileElement>> get onRendered =>
+      _r.onRendered;
+
+  M.VM _vm;
+  M.EventRepository _events;
+  M.NotificationRepository _notifications;
+  M.NativeMemorySampleProfileRepository _profiles;
+  Stream<M.SampleProfileLoadingProgressEvent> _progressStream;
+  M.SampleProfileLoadingProgress _progress;
+  M.SampleProfileTag _tag = M.SampleProfileTag.none;
+  ProfileTreeMode _mode = ProfileTreeMode.function;
+  M.ProfileTreeDirection _direction = M.ProfileTreeDirection.exclusive;
+  String _filter = '';
+
+  M.NotificationRepository get notifications => _notifications;
+  M.NativeMemorySampleProfileRepository get profiles => _profiles;
+
+  factory NativeMemoryProfileElement(
+      M.VM vm,
+      M.EventRepository events,
+      M.NotificationRepository notifications,
+      M.NativeMemorySampleProfileRepository profiles,
+      {RenderingQueue queue}) {
+    assert(vm != null);
+    assert(events != null);
+    assert(notifications != null);
+    assert(profiles != null);
+    NativeMemoryProfileElement e = new NativeMemoryProfileElement.created();
+    e._r = new RenderingScheduler<NativeMemoryProfileElement>(e, queue: queue);
+    e._vm = vm;
+    e._events = events;
+    e._notifications = notifications;
+    e._profiles = profiles;
+    return e;
+  }
+
+  NativeMemoryProfileElement.created() : super.created('native-memory-profile');
+
+  @override
+  attached() {
+    super.attached();
+    _r.enable();
+    _request();
+  }
+
+  @override
+  detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+  }
+
+  void render() {
+    var content = <Element>[
+      navBar(<Element>[
+        new NavTopMenuElement(queue: _r.queue).element,
+        new NavVMMenuElement(_vm, _events, queue: _r.queue).element,
+        navMenu('native memory profile', link: Uris.nativeMemory()),
+        (new NavRefreshElement(queue: _r.queue)..onRefresh.listen(_refresh))
+            .element,
+        new NavNotifyElement(_notifications, queue: _r.queue).element
+      ]),
+    ];
+    if (_progress == null) {
+      children = content;
+      return;
+    }
+    content.add((new SampleBufferControlElement(_vm, _progress, _progressStream,
+            selectedTag: _tag, queue: _r.queue)
+          ..onTagChange.listen((e) {
+            _tag = e.element.selectedTag;
+            _request();
+          }))
+        .element);
+    if (_progress.status == M.SampleProfileLoadingStatus.loaded) {
+      CpuProfileVirtualTreeElement tree;
+      content.addAll([
+        new BRElement(),
+        (new StackTraceTreeConfigElement(
+                mode: _mode,
+                direction: _direction,
+                filter: _filter,
+                queue: _r.queue)
+              ..onModeChange.listen((e) {
+                _mode = tree.mode = e.element.mode;
+              })
+              ..onFilterChange.listen((e) {
+                _filter = e.element.filter.trim();
+                tree.filters = _filter.isNotEmpty
+                    ? [
+                        (node) {
+                          return node.name.contains(_filter);
+                        }
+                      ]
+                    : const [];
+              })
+              ..onDirectionChange.listen((e) {
+                _direction = tree.direction = e.element.direction;
+              }))
+            .element,
+        new BRElement(),
+        (tree = new CpuProfileVirtualTreeElement(null, _progress.profile,
+                queue: _r.queue, type: M.SampleProfileType.memory))
+            .element,
+      ]);
+    }
+    children = content;
+  }
+
+  Future _request({bool forceFetch: false}) async {
+    if (forceFetch) {
+      for (M.Isolate isolate in _vm.isolates) {
+        await isolate.collectAllGarbage();
+      }
+    }
+    _progress = null;
+    _progressStream = _profiles.get(_vm, _tag, forceFetch: forceFetch);
+    _r.dirty();
+    _progress = (await _progressStream.first).progress;
+    _r.dirty();
+    if (M.isSampleProcessRunning(_progress.status)) {
+      _progress = (await _progressStream.last).progress;
+      _r.dirty();
+    }
+  }
+
+  Future _refresh(e) async {
+    e.element.disabled = true;
+    await _request(forceFetch: true);
+    e.element.disabled = false;
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/nav/class_menu.dart b/runtime/observatory_2/lib/src/elements/nav/class_menu.dart
new file mode 100644
index 0000000..3198130
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/nav/class_menu.dart
@@ -0,0 +1,63 @@
+// Copyright (c) 2016, 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.
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/models.dart' as M show IsolateRef, ClassRef;
+import 'package:observatory_2/src/elements/helpers/nav_menu.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/helpers/uris.dart';
+
+class NavClassMenuElement extends CustomElement implements Renderable {
+  RenderingScheduler<NavClassMenuElement> _r;
+
+  Stream<RenderedEvent<NavClassMenuElement>> get onRendered => _r.onRendered;
+
+  M.IsolateRef _isolate;
+  M.ClassRef _cls;
+  Iterable<Element> _content = const [];
+
+  M.IsolateRef get isolate => _isolate;
+  M.ClassRef get cls => _cls;
+  Iterable<Element> get content => _content;
+
+  set content(Iterable<Element> value) {
+    _content = value.toList();
+    _r.dirty();
+  }
+
+  factory NavClassMenuElement(M.IsolateRef isolate, M.ClassRef cls,
+      {RenderingQueue queue}) {
+    assert(isolate != null);
+    assert(cls != null);
+    NavClassMenuElement e = new NavClassMenuElement.created();
+    e._r = new RenderingScheduler<NavClassMenuElement>(e, queue: queue);
+    e._isolate = isolate;
+    e._cls = cls;
+    return e;
+  }
+
+  NavClassMenuElement.created() : super.created('nav-class-menu');
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    children = <Element>[];
+    _r.disable(notify: true);
+  }
+
+  void render() {
+    children = <Element>[
+      navMenu(cls.name,
+          content: _content, link: Uris.inspect(isolate, object: cls))
+    ];
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/nav/isolate_menu.dart b/runtime/observatory_2/lib/src/elements/nav/isolate_menu.dart
new file mode 100644
index 0000000..f2de56d
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/nav/isolate_menu.dart
@@ -0,0 +1,107 @@
+// Copyright (c) 2016, 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.
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/models.dart' as M show IsolateRef, EventRepository;
+import 'package:observatory_2/src/elements/helpers/nav_menu.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/helpers/uris.dart';
+import 'package:observatory_2/src/elements/nav/menu_item.dart';
+
+class NavIsolateMenuElement extends CustomElement implements Renderable {
+  RenderingScheduler<NavIsolateMenuElement> _r;
+
+  Stream<RenderedEvent<NavIsolateMenuElement>> get onRendered => _r.onRendered;
+
+  M.IsolateRef _isolate;
+  M.EventRepository _events;
+  StreamSubscription _updatesSubscription;
+  Iterable<Element> _content = const [];
+
+  M.IsolateRef get isolate => _isolate;
+  Iterable<Element> get content => _content;
+
+  set content(Iterable<Element> value) {
+    _content = value.toList();
+    _r.dirty();
+  }
+
+  factory NavIsolateMenuElement(M.IsolateRef isolate, M.EventRepository events,
+      {RenderingQueue queue}) {
+    assert(isolate != null);
+    assert(events != null);
+    NavIsolateMenuElement e = new NavIsolateMenuElement.created();
+    e._r = new RenderingScheduler<NavIsolateMenuElement>(e, queue: queue);
+    e._isolate = isolate;
+    e._events = events;
+    return e;
+  }
+
+  NavIsolateMenuElement.created() : super.created('nav-isolate-menu');
+
+  @override
+  void attached() {
+    super.attached();
+    _updatesSubscription = _events.onIsolateUpdate
+        .where((e) => e.isolate.id == isolate.id)
+        .listen((e) {
+      _isolate = e.isolate;
+      _r.dirty();
+    });
+    _r.enable();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    children = <Element>[];
+    _r.disable(notify: true);
+    assert(_updatesSubscription != null);
+    _updatesSubscription.cancel();
+    _updatesSubscription = null;
+  }
+
+  void render() {
+    final content = <Element>[
+      new NavMenuItemElement('debugger',
+              queue: _r.queue, link: Uris.debugger(isolate))
+          .element,
+      new NavMenuItemElement('class hierarchy',
+              queue: _r.queue, link: Uris.classTree(isolate))
+          .element,
+      new NavMenuItemElement('cpu profile',
+              queue: _r.queue, link: Uris.cpuProfiler(isolate))
+          .element,
+      new NavMenuItemElement('cpu profile (table)',
+              queue: _r.queue, link: Uris.cpuProfilerTable(isolate))
+          .element,
+      new NavMenuItemElement('allocation profile',
+              queue: _r.queue, link: Uris.allocationProfiler(isolate))
+          .element,
+      new NavMenuItemElement('heap snapshot',
+              queue: _r.queue, link: Uris.heapSnapshot(isolate))
+          .element,
+      new NavMenuItemElement('heap map',
+              queue: _r.queue, link: Uris.heapMap(isolate))
+          .element,
+      new NavMenuItemElement('metrics',
+              queue: _r.queue, link: Uris.metrics(isolate))
+          .element,
+      new NavMenuItemElement('persistent handles',
+              queue: _r.queue, link: Uris.persistentHandles(isolate))
+          .element,
+      new NavMenuItemElement('ports',
+              queue: _r.queue, link: Uris.ports(isolate))
+          .element,
+      new NavMenuItemElement('logging',
+              queue: _r.queue, link: Uris.logging(isolate))
+          .element,
+    ]..addAll(_content);
+    children = <Element>[
+      navMenu(isolate.name, content: content, link: Uris.inspect(isolate))
+    ];
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/nav/library_menu.dart b/runtime/observatory_2/lib/src/elements/nav/library_menu.dart
new file mode 100644
index 0000000..83208cc
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/nav/library_menu.dart
@@ -0,0 +1,64 @@
+// Copyright (c) 2016, 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.
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/models.dart' as M show IsolateRef, LibraryRef;
+import 'package:observatory_2/src/elements/helpers/nav_menu.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/helpers/uris.dart';
+
+class NavLibraryMenuElement extends CustomElement implements Renderable {
+  RenderingScheduler<NavLibraryMenuElement> _r;
+
+  Stream<RenderedEvent<NavLibraryMenuElement>> get onRendered => _r.onRendered;
+
+  M.IsolateRef _isolate;
+  M.LibraryRef _library;
+  Iterable<Element> _content = const [];
+
+  M.IsolateRef get isolate => _isolate;
+  M.LibraryRef get library => _library;
+  Iterable<Element> get content => _content;
+
+  set content(Iterable<Element> value) {
+    _content = value.toList();
+    _r.dirty();
+  }
+
+  factory NavLibraryMenuElement(M.IsolateRef isolate, M.LibraryRef library,
+      {RenderingQueue queue}) {
+    assert(isolate != null);
+    assert(library != null);
+    NavLibraryMenuElement e = new NavLibraryMenuElement.created();
+    e._r = new RenderingScheduler<NavLibraryMenuElement>(e, queue: queue);
+    e._isolate = isolate;
+    e._library = library;
+    return e;
+  }
+
+  NavLibraryMenuElement.created() : super.created('nav-library-menu');
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    children = <Element>[];
+    _r.disable(notify: true);
+  }
+
+  void render() {
+    children = <Element>[
+      navMenu(library.name,
+          content: _content,
+          link: Uris.inspect(isolate, object: library).toString())
+    ];
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/nav/menu_item.dart b/runtime/observatory_2/lib/src/elements/nav/menu_item.dart
new file mode 100644
index 0000000..6350335
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/nav/menu_item.dart
@@ -0,0 +1,65 @@
+// Copyright (c) 2016, 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.
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+
+class NavMenuItemElement extends CustomElement implements Renderable {
+  RenderingScheduler<NavMenuItemElement> _r;
+
+  Stream<RenderedEvent<NavMenuItemElement>> get onRendered => _r.onRendered;
+
+  String _label;
+  String _link;
+  Iterable<Element> _content = const <Element>[];
+
+  String get label => _label;
+  String get link => _link;
+  Iterable<Element> get content => _content;
+
+  set label(String value) => _label = _r.checkAndReact(_label, value);
+  set link(String value) => _link = _r.checkAndReact(_link, value);
+  set content(Iterable<Element> value) {
+    _content = value.toList();
+    _r.dirty();
+  }
+
+  factory NavMenuItemElement(String label,
+      {String link, RenderingQueue queue}) {
+    assert(label != null);
+    NavMenuItemElement e = new NavMenuItemElement.created();
+    e._r = new RenderingScheduler<NavMenuItemElement>(e, queue: queue);
+    e._label = label;
+    e._link = link;
+    return e;
+  }
+
+  NavMenuItemElement.created() : super.created('nav-menu-item');
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+  }
+
+  void render() {
+    children = <Element>[
+      new LIElement()
+        ..classes = ['nav-menu-item']
+        ..children = <Element>[
+          new AnchorElement(href: link)..text = label,
+          new UListElement()..children = _content
+        ]
+    ];
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/nav/notify.dart b/runtime/observatory_2/lib/src/elements/nav/notify.dart
new file mode 100644
index 0000000..ba57035
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/nav/notify.dart
@@ -0,0 +1,92 @@
+// Copyright (c) 2016, 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.
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/nav/notify_event.dart';
+import 'package:observatory_2/src/elements/nav/notify_exception.dart';
+
+class NavNotifyElement extends CustomElement implements Renderable {
+  RenderingScheduler<NavNotifyElement> _r;
+
+  Stream<RenderedEvent<NavNotifyElement>> get onRendered => _r.onRendered;
+
+  M.NotificationRepository _repository;
+  StreamSubscription _subscription;
+
+  bool _notifyOnPause;
+
+  bool get notifyOnPause => _notifyOnPause;
+
+  set notifyOnPause(bool value) =>
+      _notifyOnPause = _r.checkAndReact(_notifyOnPause, value);
+
+  factory NavNotifyElement(M.NotificationRepository repository,
+      {bool notifyOnPause: true, RenderingQueue queue}) {
+    assert(repository != null);
+    assert(notifyOnPause != null);
+    NavNotifyElement e = new NavNotifyElement.created();
+    e._r = new RenderingScheduler<NavNotifyElement>(e, queue: queue);
+    e._repository = repository;
+    e._notifyOnPause = notifyOnPause;
+    return e;
+  }
+
+  NavNotifyElement.created() : super.created('nav-notify');
+
+  @override
+  void attached() {
+    super.attached();
+    _subscription = _repository.onChange.listen((_) => _r.dirty());
+    _r.enable();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    children = <Element>[];
+    _r.disable(notify: true);
+    _subscription.cancel();
+  }
+
+  void render() {
+    children = <Element>[
+      new DivElement()
+        ..children = <Element>[
+          new DivElement()
+            ..children = _repository
+                .list()
+                .where(_filter)
+                .map<Element>(_toElement)
+                .toList()
+        ]
+    ];
+  }
+
+  bool _filter(M.Notification notification) {
+    if (!_notifyOnPause && notification is M.EventNotification) {
+      return notification.event is! M.PauseEvent;
+    }
+    return true;
+  }
+
+  Element _toElement(M.Notification notification) {
+    if (notification is M.EventNotification) {
+      return (new NavNotifyEventElement(notification.event, queue: _r.queue)
+            ..onDelete.listen((_) => _repository.delete(notification)))
+          .element;
+    } else if (notification is M.ExceptionNotification) {
+      return (new NavNotifyExceptionElement(notification.exception,
+              stacktrace: notification.stacktrace, queue: _r.queue)
+            ..onDelete.listen((_) => _repository.delete(notification)))
+          .element;
+    } else {
+      assert(false);
+      return new DivElement()..text = 'Invalid Notification Type';
+    }
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/nav/notify_event.dart b/runtime/observatory_2/lib/src/elements/nav/notify_event.dart
new file mode 100644
index 0000000..3c8308b
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/nav/notify_event.dart
@@ -0,0 +1,228 @@
+// Copyright (c) 2016, 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.
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/helpers/uris.dart';
+
+class EventDeleteEvent {
+  final M.Event event;
+  EventDeleteEvent(this.event);
+}
+
+class NavNotifyEventElement extends CustomElement implements Renderable {
+  RenderingScheduler<NavNotifyEventElement> _r;
+
+  Stream<RenderedEvent<NavNotifyEventElement>> get onRendered => _r.onRendered;
+
+  final StreamController<EventDeleteEvent> _onDelete =
+      new StreamController<EventDeleteEvent>.broadcast();
+  Stream<EventDeleteEvent> get onDelete => _onDelete.stream;
+
+  M.Event _event;
+
+  M.Event get event => _event;
+
+  factory NavNotifyEventElement(M.Event event, {RenderingQueue queue}) {
+    assert(event != null);
+    NavNotifyEventElement e = new NavNotifyEventElement.created();
+    e._r = new RenderingScheduler<NavNotifyEventElement>(e, queue: queue);
+    e._event = event;
+    return e;
+  }
+
+  NavNotifyEventElement.created() : super.created('nav-event');
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    children = <Element>[];
+    _r.disable(notify: true);
+  }
+
+  void render() {
+    children = <Element>[];
+    List<Element> content;
+    if (event is M.PauseStartEvent) {
+      content = _managePauseStartEvent(event as M.PauseStartEvent);
+    } else if (event is M.PauseExitEvent) {
+      content = _managePauseExitEvent(event as M.PauseExitEvent);
+    } else if (event is M.PauseBreakpointEvent) {
+      content = _managePauseBreakpointEvent(event as M.PauseBreakpointEvent);
+    } else if (event is M.PauseInterruptedEvent) {
+      content = _managePauseInterruptedEvent(event as M.PauseInterruptedEvent);
+    } else if (event is M.PauseExceptionEvent) {
+      content = _managePauseExceptionEvent(event as M.PauseExceptionEvent);
+    } else if (event is M.NoneEvent) {
+      content = _manageNoneEvent(event as M.NoneEvent);
+    } else if (event is M.ConnectionClosedEvent) {
+      content = _manageConnectionClosedEvent(event as M.ConnectionClosedEvent);
+    } else if (event is M.InspectEvent) {
+      content = _manageInspectEvent(event as M.InspectEvent);
+    } else if (event is M.IsolateReloadEvent) {
+      content = _manageIsolateReloadEvent(event as M.IsolateReloadEvent);
+    } else {
+      return;
+    }
+    children = <Element>[
+      new DivElement()
+        ..children = <Element>[]
+        ..children.addAll(content)
+        ..children.add(new ButtonElement()
+          ..innerHtml = '&times;'
+          ..onClick.map(_toEvent).listen(_delete))
+    ];
+  }
+
+  static List<Element> _managePauseStartEvent(M.PauseStartEvent event) {
+    return [
+      new SpanElement()..text = 'Isolate ',
+      new AnchorElement(href: Uris.inspect(event.isolate))
+        ..text = event.isolate.name,
+      new SpanElement()..text = ' is paused at isolate start',
+      new BRElement(),
+      new BRElement(),
+      new SpanElement()..text = '[',
+      new AnchorElement(href: Uris.debugger(event.isolate))..text = 'debug',
+      new SpanElement()..text = ']'
+    ];
+  }
+
+  static List<Element> _managePauseExitEvent(M.PauseExitEvent event) {
+    return [
+      new SpanElement()..text = 'Isolate ',
+      new AnchorElement(href: Uris.inspect(event.isolate))
+        ..text = event.isolate.name,
+      new SpanElement()..text = ' is paused at isolate exit',
+      new BRElement(),
+      new BRElement(),
+      new SpanElement()..text = '[',
+      new AnchorElement(href: Uris.debugger(event.isolate))..text = 'debug',
+      new SpanElement()..text = ']'
+    ];
+  }
+
+  static List<Element> _managePauseBreakpointEvent(
+      M.PauseBreakpointEvent event) {
+    String message = ' is paused';
+    if (event.breakpoint != null) {
+      message += ' at breakpoint ${event.breakpoint.number}';
+    }
+    return [
+      new SpanElement()..text = 'Isolate ',
+      new AnchorElement(href: Uris.inspect(event.isolate))
+        ..text = event.isolate.name,
+      new SpanElement()..text = message,
+      new BRElement(),
+      new BRElement(),
+      new SpanElement()..text = '[',
+      new AnchorElement(href: Uris.debugger(event.isolate))..text = 'debug',
+      new SpanElement()..text = ']'
+    ];
+  }
+
+  static List<Element> _managePauseInterruptedEvent(
+      M.PauseInterruptedEvent event) {
+    return [
+      new SpanElement()..text = 'Isolate ',
+      new AnchorElement(href: Uris.inspect(event.isolate))
+        ..text = event.isolate.name,
+      new SpanElement()..text = ' is paused',
+      new BRElement(),
+      new BRElement(),
+      new SpanElement()..text = '[',
+      new AnchorElement(href: Uris.debugger(event.isolate))..text = 'debug',
+      new SpanElement()..text = ']'
+    ];
+  }
+
+  static List<Element> _managePauseExceptionEvent(M.PauseExceptionEvent event) {
+    return [
+      new SpanElement()..text = 'Isolate ',
+      new AnchorElement(href: Uris.inspect(event.isolate))
+        ..text = event.isolate.name,
+      new SpanElement()..text = ' is paused due to exception',
+      new BRElement(),
+      new BRElement(),
+      new SpanElement()..text = '[',
+      new AnchorElement(href: Uris.debugger(event.isolate))..text = 'debug',
+      new SpanElement()..text = ']'
+    ];
+  }
+
+  static List<Element> _manageNoneEvent(M.NoneEvent event) {
+    return [
+      new SpanElement()..text = 'Isolate ',
+      new AnchorElement(href: Uris.inspect(event.isolate))
+        ..text = event.isolate.name,
+      new SpanElement()..text = ' is paused',
+      new BRElement(),
+      new BRElement(),
+      new SpanElement()..text = '[',
+      new AnchorElement(href: Uris.debugger(event.isolate))..text = 'debug',
+      new SpanElement()..text = ']'
+    ];
+  }
+
+  static List<Element> _manageConnectionClosedEvent(
+      M.ConnectionClosedEvent event) {
+    return [
+      new SpanElement()..text = 'Disconnected from VM: ${event.reason}',
+      new BRElement(),
+      new BRElement(),
+      new SpanElement()..text = '[',
+      new AnchorElement(href: Uris.vmConnect())..text = 'Connect to a VM',
+      new SpanElement()..text = ']'
+    ];
+  }
+
+  static List<Element> _manageInspectEvent(M.InspectEvent event) {
+    return [
+      new SpanElement()..text = 'Inspect ${event.inspectee.id}',
+      new BRElement(), new BRElement(), new SpanElement()..text = '[',
+      new AnchorElement(
+          href: Uris.inspect(event.isolate, object: event.inspectee))
+        ..text = 'Inspect',
+      new SpanElement()..text = ']'
+      // TODO(cbernaschina) add InstanceRefElement back.
+      //new InstanceRefElement()..instance = event.inspectee
+    ];
+  }
+
+  static List<Element> _manageIsolateReloadEvent(M.IsolateReloadEvent event) {
+    if (event.error != null) {
+      return [
+        new SpanElement()..text = 'Isolate reload failed:',
+        new BRElement(),
+        new BRElement(),
+        new DivElement()
+          ..classes = ["indent", "error"]
+          ..text = event.error.message.toString()
+      ];
+    } else {
+      return [new SpanElement()..text = 'Isolate reload'];
+    }
+  }
+
+  EventDeleteEvent _toEvent(_) {
+    return new EventDeleteEvent(_event);
+  }
+
+  void _delete(EventDeleteEvent e) {
+    _onDelete.add(e);
+  }
+
+  void delete() {
+    _onDelete.add(new EventDeleteEvent(_event));
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/nav/notify_exception.dart b/runtime/observatory_2/lib/src/elements/nav/notify_exception.dart
new file mode 100644
index 0000000..37b6457
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/nav/notify_exception.dart
@@ -0,0 +1,129 @@
+// Copyright (c) 2016, 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.
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/helpers/uris.dart';
+import 'package:observatory_2/models.dart' show ConnectionException;
+
+class ExceptionDeleteEvent {
+  final dynamic exception;
+  final StackTrace stacktrace;
+
+  ExceptionDeleteEvent(this.exception, {this.stacktrace});
+}
+
+class NavNotifyExceptionElement extends CustomElement implements Renderable {
+  RenderingScheduler<NavNotifyExceptionElement> _r;
+
+  Stream<RenderedEvent<NavNotifyExceptionElement>> get onRendered =>
+      _r.onRendered;
+
+  final StreamController<ExceptionDeleteEvent> _onDelete =
+      new StreamController<ExceptionDeleteEvent>.broadcast();
+  Stream<ExceptionDeleteEvent> get onDelete => _onDelete.stream;
+
+  dynamic _exception;
+  StackTrace _stacktrace;
+
+  dynamic get exception => _exception;
+  StackTrace get stacktrace => _stacktrace;
+
+  factory NavNotifyExceptionElement(dynamic exception,
+      {StackTrace stacktrace: null, RenderingQueue queue}) {
+    assert(exception != null);
+    NavNotifyExceptionElement e = new NavNotifyExceptionElement.created();
+    e._r = new RenderingScheduler<NavNotifyExceptionElement>(e, queue: queue);
+    e._exception = exception;
+    e._stacktrace = stacktrace;
+    return e;
+  }
+
+  NavNotifyExceptionElement.created() : super.created('nav-exception');
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    children = <Element>[];
+    _r.disable(notify: true);
+  }
+
+  void render() {
+    if (exception is ConnectionException) {
+      renderConnectionException();
+    } else {
+      renderGenericException();
+    }
+  }
+
+  void renderConnectionException() {
+    children = <Element>[
+      new DivElement()
+        ..children = <Element>[
+          new SpanElement()
+            ..text = 'The request cannot be completed because the '
+                'VM is currently disconnected',
+          new BRElement(),
+          new BRElement(),
+          new SpanElement()..text = '[',
+          new AnchorElement(href: Uris.vmConnect())
+            ..text = 'Connect to a different VM',
+          new SpanElement()..text = ']',
+          new ButtonElement()
+            ..innerHtml = '&times;'
+            ..onClick.map(_toEvent).listen(_delete)
+        ]
+    ];
+  }
+
+  void renderGenericException() {
+    List<Element> content;
+    content = <Element>[
+      new SpanElement()..text = 'Unexpected exception:',
+      new BRElement(),
+      new BRElement(),
+      new DivElement()..text = exception.toString(),
+      new BRElement()
+    ];
+    if (stacktrace != null) {
+      content.addAll(<Element>[
+        new SpanElement()..text = 'StackTrace:',
+        new BRElement(),
+        new BRElement(),
+        new DivElement()..text = stacktrace.toString(),
+        new BRElement()
+      ]);
+    }
+    content.addAll(<Element>[
+      new SpanElement()..text = '[',
+      new AnchorElement(href: Uris.vmConnect())
+        ..text = 'Connect to a different VM',
+      new SpanElement()..text = ']',
+      new ButtonElement()
+        ..innerHtml = '&times;'
+        ..onClick.map(_toEvent).listen(_delete)
+    ]);
+    children = <Element>[new DivElement()..children = content];
+  }
+
+  ExceptionDeleteEvent _toEvent(_) {
+    return new ExceptionDeleteEvent(exception, stacktrace: stacktrace);
+  }
+
+  void _delete(ExceptionDeleteEvent e) {
+    _onDelete.add(e);
+  }
+
+  void delete() {
+    _onDelete.add(new ExceptionDeleteEvent(exception, stacktrace: stacktrace));
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/nav/refresh.dart b/runtime/observatory_2/lib/src/elements/nav/refresh.dart
new file mode 100644
index 0000000..42e9ce6
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/nav/refresh.dart
@@ -0,0 +1,84 @@
+// Copyright (c) 2016, 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.
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+
+class RefreshEvent {
+  final NavRefreshElement element;
+  RefreshEvent(this.element);
+}
+
+class NavRefreshElement extends CustomElement implements Renderable {
+  RenderingScheduler<NavRefreshElement> _r;
+
+  Stream<RenderedEvent<NavRefreshElement>> get onRendered => _r.onRendered;
+
+  final StreamController<RefreshEvent> _onRefresh =
+      new StreamController<RefreshEvent>.broadcast();
+  Stream<RefreshEvent> get onRefresh => _onRefresh.stream;
+
+  bool _disabled;
+  String _label;
+
+  bool get disabled => _disabled;
+  String get label => _label;
+
+  set disabled(bool value) => _disabled = _r.checkAndReact(_disabled, value);
+  set label(String value) => _label = _r.checkAndReact(_label, value);
+
+  factory NavRefreshElement(
+      {String label: 'Refresh', bool disabled: false, RenderingQueue queue}) {
+    assert(label != null);
+    assert(disabled != null);
+    NavRefreshElement e = new NavRefreshElement.created();
+    e._r = new RenderingScheduler<NavRefreshElement>(e, queue: queue);
+    e._label = label;
+    e._disabled = disabled;
+    return e;
+  }
+
+  NavRefreshElement.created() : super.created('nav-refresh');
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    children = <Element>[];
+    _r.disable(notify: true);
+  }
+
+  void render() {
+    children = <Element>[
+      new LIElement()
+        ..children = <Element>[
+          new ButtonElement()
+            ..text = label
+            ..disabled = disabled
+            ..onClick.map(_toEvent).listen(_refresh)
+        ]
+    ];
+  }
+
+  RefreshEvent _toEvent(_) {
+    return new RefreshEvent(this);
+  }
+
+  void _refresh(RefreshEvent e) {
+    if (_disabled) return;
+    _onRefresh.add(e);
+  }
+
+  void refresh() {
+    if (_disabled) return;
+    _refresh(new RefreshEvent(this));
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/nav/reload.dart b/runtime/observatory_2/lib/src/elements/nav/reload.dart
new file mode 100644
index 0000000..2f578a3
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/nav/reload.dart
@@ -0,0 +1,105 @@
+// Copyright (c) 2017, 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.
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/models.dart' as M
+    show IsolateRef, IsolateRepository, EventRepository;
+import 'package:observatory_2/src/elements/helpers/nav_menu.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+
+class ReloadEvent {
+  final NavReloadElement element;
+  ReloadEvent(this.element);
+}
+
+class NavReloadElement extends CustomElement implements Renderable {
+  RenderingScheduler<NavReloadElement> _r;
+
+  Stream<RenderedEvent<NavReloadElement>> get onRendered => _r.onRendered;
+
+  final StreamController<ReloadEvent> _onReload =
+      new StreamController<ReloadEvent>.broadcast();
+  Stream<ReloadEvent> get onReload => _onReload.stream;
+
+  M.IsolateRef _isolate;
+  M.IsolateRepository _isolates;
+  M.EventRepository _events;
+  StreamSubscription _sub;
+  bool _disabled = false;
+
+  factory NavReloadElement(M.IsolateRef isolate, M.IsolateRepository isolates,
+      M.EventRepository events,
+      {RenderingQueue queue}) {
+    assert(isolate != null);
+    assert(isolates != null);
+    assert(events != null);
+    NavReloadElement e = new NavReloadElement.created();
+    e._r = new RenderingScheduler<NavReloadElement>(e, queue: queue);
+    e._isolate = isolate;
+    e._isolates = isolates;
+    e._events = events;
+    return e;
+  }
+
+  NavReloadElement.created() : super.created('nav-reload');
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+    _sub = _events.onServiceEvent.listen((_) => _r.dirty());
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    children = <Element>[];
+    _sub.cancel();
+    _sub = null;
+    _r.disable(notify: true);
+  }
+
+  void render() {
+    final children = <Element>[];
+    if (_isolates.reloadSourcesServices.isEmpty) {
+      children.add(new LIElement()
+        ..children = <Element>[
+          new ButtonElement()
+            ..text = 'Reload Source'
+            ..disabled = _disabled
+            ..onClick.listen((_) => _reload())
+        ]);
+    } else if (_isolates.reloadSourcesServices.length == 1) {
+      children.add(new LIElement()
+        ..children = <Element>[
+          new ButtonElement()
+            ..text = 'Reload Source'
+            ..disabled = _disabled
+            ..onClick
+                .listen((_) => _reload(_isolates.reloadSourcesServices.single))
+        ]);
+    } else {
+      final content = _isolates.reloadSourcesServices.map((s) => new LIElement()
+        ..children = <Element>[
+          new ButtonElement()
+            ..text = s.alias
+            ..disabled = _disabled
+            ..onClick.listen((_) => _reload(s))
+        ]);
+      children.add(navMenu('Reload Source', content: content));
+    }
+    this.children = children;
+  }
+
+  Future _reload([service]) async {
+    _disabled = true;
+    _r.dirty();
+    await _isolates.reloadSources(_isolate, service: service);
+    _disabled = false;
+    _r.dirty();
+    _onReload.add(new ReloadEvent(this));
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/nav/top_menu.dart b/runtime/observatory_2/lib/src/elements/nav/top_menu.dart
new file mode 100644
index 0000000..65ba440
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/nav/top_menu.dart
@@ -0,0 +1,56 @@
+// Copyright (c) 2016, 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.
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/src/elements/helpers/nav_menu.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/helpers/uris.dart';
+import 'package:observatory_2/src/elements/nav/menu_item.dart';
+
+class NavTopMenuElement extends CustomElement implements Renderable {
+  RenderingScheduler<NavTopMenuElement> _r;
+
+  Stream<RenderedEvent<NavTopMenuElement>> get onRendered => _r.onRendered;
+
+  Iterable<Element> _content = const <Element>[];
+
+  Iterable<Element> get content => _content;
+
+  set content(Iterable<Element> value) {
+    _content = value.toList();
+    _r.dirty();
+  }
+
+  factory NavTopMenuElement({RenderingQueue queue}) {
+    NavTopMenuElement e = new NavTopMenuElement.created();
+    e._r = new RenderingScheduler<NavTopMenuElement>(e, queue: queue);
+    return e;
+  }
+
+  NavTopMenuElement.created() : super.created('nav-top-menu');
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+  }
+
+  void render() {
+    final content = (<Element>[
+      new NavMenuItemElement('Connect to a VM', link: Uris.vmConnect()).element,
+    ]..addAll(_content));
+    children = <Element>[
+      navMenu('Observatory', link: Uris.vm(), content: content)
+    ];
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/nav/vm_menu.dart b/runtime/observatory_2/lib/src/elements/nav/vm_menu.dart
new file mode 100644
index 0000000..6426ba4
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/nav/vm_menu.dart
@@ -0,0 +1,74 @@
+// Copyright (c) 2016, 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.
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/models.dart' as M show VM, EventRepository;
+import 'package:observatory_2/src/elements/helpers/nav_menu.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/helpers/uris.dart';
+import 'package:observatory_2/src/elements/nav/menu_item.dart';
+
+class NavVMMenuElement extends CustomElement implements Renderable {
+  RenderingScheduler<NavVMMenuElement> _r;
+
+  Stream<RenderedEvent<NavVMMenuElement>> get onRendered => _r.onRendered;
+
+  M.VM _vm;
+  M.EventRepository _events;
+  StreamSubscription _updatesSubscription;
+  Iterable<Element> _content = const [];
+
+  M.VM get vm => _vm;
+  Iterable<Element> get content => _content;
+
+  set content(Iterable<Element> value) {
+    _content = value.toList();
+    _r.dirty();
+  }
+
+  factory NavVMMenuElement(M.VM vm, M.EventRepository events,
+      {RenderingQueue queue}) {
+    assert(vm != null);
+    assert(events != null);
+    NavVMMenuElement e = new NavVMMenuElement.created();
+    e._r = new RenderingScheduler<NavVMMenuElement>(e, queue: queue);
+    e._vm = vm;
+    e._events = events;
+    return e;
+  }
+
+  NavVMMenuElement.created() : super.created('nav-vm-menu');
+
+  @override
+  void attached() {
+    super.attached();
+    _updatesSubscription = _events.onVMUpdate.listen((e) {
+      _vm = e.vm;
+      _r.dirty();
+    });
+    _r.enable();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    children = <Element>[];
+    _r.disable(notify: true);
+    _updatesSubscription.cancel();
+  }
+
+  void render() {
+    final content = (_vm.isolates.map<Element>((isolate) {
+      return new NavMenuItemElement(isolate.name,
+              queue: _r.queue, link: Uris.inspect(isolate))
+          .element;
+    }).toList()
+      ..addAll(_content));
+    children = <Element>[
+      navMenu(vm.displayName, link: Uris.vm(), content: content)
+    ];
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/object_common.dart b/runtime/observatory_2/lib/src/elements/object_common.dart
new file mode 100644
index 0000000..9d896a3
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/object_common.dart
@@ -0,0 +1,226 @@
+// 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.
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/class_ref.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/inbound_references.dart';
+import 'package:observatory_2/src/elements/retaining_path.dart';
+import 'package:observatory_2/src/elements/sentinel_value.dart';
+import 'package:observatory_2/utils.dart';
+
+class ObjectCommonElement extends CustomElement implements Renderable {
+  RenderingScheduler<ObjectCommonElement> _r;
+
+  Stream<RenderedEvent<ObjectCommonElement>> get onRendered => _r.onRendered;
+
+  M.IsolateRef _isolate;
+  M.Object _object;
+  M.RetainedSizeRepository _retainedSizes;
+  M.ReachableSizeRepository _reachableSizes;
+  M.InboundReferencesRepository _references;
+  M.RetainingPathRepository _retainingPaths;
+  M.ObjectRepository _objects;
+  M.Guarded<M.Instance> _retainedSize = null;
+  bool _loadingRetainedBytes = false;
+  M.Guarded<M.Instance> _reachableSize = null;
+  bool _loadingReachableBytes = false;
+
+  M.IsolateRef get isolate => _isolate;
+  M.Object get object => _object;
+
+  factory ObjectCommonElement(
+      M.IsolateRef isolate,
+      M.Object object,
+      M.RetainedSizeRepository retainedSizes,
+      M.ReachableSizeRepository reachableSizes,
+      M.InboundReferencesRepository references,
+      M.RetainingPathRepository retainingPaths,
+      M.ObjectRepository objects,
+      {RenderingQueue queue}) {
+    assert(isolate != null);
+    assert(object != null);
+    assert(retainedSizes != null);
+    assert(reachableSizes != null);
+    assert(references != null);
+    assert(retainingPaths != null);
+    assert(objects != null);
+    ObjectCommonElement e = new ObjectCommonElement.created();
+    e._r = new RenderingScheduler<ObjectCommonElement>(e, queue: queue);
+    e._isolate = isolate;
+    e._object = object;
+    e._retainedSizes = retainedSizes;
+    e._reachableSizes = reachableSizes;
+    e._references = references;
+    e._objects = objects;
+    e._retainingPaths = retainingPaths;
+    return e;
+  }
+
+  ObjectCommonElement.created() : super.created('object-common');
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+  }
+
+  RetainingPathElement _path;
+  InboundReferencesElement _inbounds;
+
+  void render() {
+    _path = _path ??
+        new RetainingPathElement(_isolate, _object, _retainingPaths, _objects,
+            queue: _r.queue);
+    _inbounds = _inbounds ??
+        new InboundReferencesElement(_isolate, _object, _references, _objects,
+            queue: _r.queue);
+    children = <Element>[
+      new DivElement()
+        ..classes = ['memberList']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberItem']
+            ..children = <Element>[
+              new DivElement()
+                ..classes = ['memberName']
+                ..text = 'Class ',
+              new DivElement()
+                ..classes = ['memberValue']
+                ..children = <Element>[
+                  _object.clazz == null
+                      ? (new SpanElement()..text = '...')
+                      : new ClassRefElement(_isolate, _object.clazz,
+                              queue: _r.queue)
+                          .element
+                ]
+            ],
+          new DivElement()
+            ..classes = ['memberItem']
+            ..title = 'Space for this object in memory'
+            ..children = <Element>[
+              new DivElement()
+                ..classes = ['memberName']
+                ..text = 'Shallow size ',
+              new DivElement()
+                ..classes = ['memberValue']
+                ..text = Utils.formatSize(_object.size ?? 0)
+            ],
+          new DivElement()
+            ..classes = ['memberItem']
+            ..title = 'Space reachable from this object, '
+                'excluding class references'
+            ..children = <Element>[
+              new DivElement()
+                ..classes = ['memberName']
+                ..text = 'Reachable size ',
+              new DivElement()
+                ..classes = ['memberValue']
+                ..children = _createReachableSizeValue()
+            ],
+          new DivElement()
+            ..classes = ['memberItem']
+            ..title = 'Space that would be reclaimed if references to this '
+                'object were replaced with null'
+            ..children = <Element>[
+              new DivElement()
+                ..classes = ['memberName']
+                ..text = 'Retained size ',
+              new DivElement()
+                ..classes = ['memberValue']
+                ..children = _createRetainedSizeValue()
+            ],
+          new DivElement()
+            ..classes = ['memberItem']
+            ..children = <Element>[
+              new DivElement()
+                ..classes = ['memberName']
+                ..text = 'Retaining path ',
+              new DivElement()
+                ..classes = ['memberValue']
+                ..children = <Element>[_path.element]
+            ],
+          new DivElement()
+            ..classes = ['memberItem']
+            ..title = 'Objects which directly reference this object'
+            ..children = <Element>[
+              new DivElement()
+                ..classes = ['memberName']
+                ..text = 'Inbound references ',
+              new DivElement()
+                ..classes = ['memberValue']
+                ..children = <Element>[_inbounds.element]
+            ]
+        ]
+    ];
+  }
+
+  List<Element> _createReachableSizeValue() {
+    final content = <Element>[];
+    if (_reachableSize != null) {
+      if (_reachableSize.isSentinel) {
+        content.add(
+            new SentinelValueElement(_reachableSize.asSentinel, queue: _r.queue)
+                .element);
+      } else {
+        content.add(new SpanElement()
+          ..text = Utils.formatSize(
+              int.parse(_reachableSize.asValue.valueAsString)));
+      }
+    } else {
+      content.add(new SpanElement()..text = '...');
+    }
+    final button = new ButtonElement()
+      ..classes = ['reachable_size']
+      ..disabled = _loadingReachableBytes
+      ..text = '↺';
+    button.onClick.listen((_) async {
+      button.disabled = true;
+      _loadingReachableBytes = true;
+      _reachableSize = await _reachableSizes.get(_isolate, _object.id);
+      _r.dirty();
+    });
+    content.add(button);
+    return content;
+  }
+
+  List<Element> _createRetainedSizeValue() {
+    final content = <Element>[];
+    if (_retainedSize != null) {
+      if (_retainedSize.isSentinel) {
+        content.add(
+            new SentinelValueElement(_retainedSize.asSentinel, queue: _r.queue)
+                .element);
+      } else {
+        content.add(new SpanElement()
+          ..text =
+              Utils.formatSize(int.parse(_retainedSize.asValue.valueAsString)));
+      }
+    } else {
+      content.add(new SpanElement()..text = '...');
+    }
+    final button = new ButtonElement()
+      ..classes = ['retained_size']
+      ..disabled = _loadingRetainedBytes
+      ..text = '↺';
+    button.onClick.listen((_) async {
+      button.disabled = true;
+      _loadingRetainedBytes = true;
+      _retainedSize = await _retainedSizes.get(_isolate, _object.id);
+      _r.dirty();
+    });
+    content.add(button);
+    return content;
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/object_view.dart b/runtime/observatory_2/lib/src/elements/object_view.dart
new file mode 100644
index 0000000..ee41d78
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/object_view.dart
@@ -0,0 +1,125 @@
+// 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.
+
+import 'dart:async';
+import 'dart:html';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/context_ref.dart';
+import 'package:observatory_2/src/elements/curly_block.dart';
+import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
+import 'package:observatory_2/src/elements/helpers/nav_menu.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/nav/isolate_menu.dart';
+import 'package:observatory_2/src/elements/nav/notify.dart';
+import 'package:observatory_2/src/elements/nav/refresh.dart';
+import 'package:observatory_2/src/elements/nav/top_menu.dart';
+import 'package:observatory_2/src/elements/nav/vm_menu.dart';
+import 'package:observatory_2/src/elements/object_common.dart';
+import 'package:observatory_2/src/elements/view_footer.dart';
+
+class ObjectViewElement extends CustomElement implements Renderable {
+  RenderingScheduler<ObjectViewElement> _r;
+
+  Stream<RenderedEvent<ObjectViewElement>> get onRendered => _r.onRendered;
+
+  M.VM _vm;
+  M.IsolateRef _isolate;
+  M.EventRepository _events;
+  M.NotificationRepository _notifications;
+  M.Object _object;
+  M.ObjectRepository _objects;
+  M.RetainedSizeRepository _retainedSizes;
+  M.ReachableSizeRepository _reachableSizes;
+  M.InboundReferencesRepository _references;
+  M.RetainingPathRepository _retainingPaths;
+
+  M.VMRef get vm => _vm;
+  M.IsolateRef get isolate => _isolate;
+  M.NotificationRepository get notifications => _notifications;
+  M.Context get object => _object;
+
+  factory ObjectViewElement(
+      M.VM vm,
+      M.IsolateRef isolate,
+      M.Object object,
+      M.EventRepository events,
+      M.NotificationRepository notifications,
+      M.ObjectRepository objects,
+      M.RetainedSizeRepository retainedSizes,
+      M.ReachableSizeRepository reachableSizes,
+      M.InboundReferencesRepository references,
+      M.RetainingPathRepository retainingPaths,
+      {RenderingQueue queue}) {
+    assert(vm != null);
+    assert(isolate != null);
+    assert(events != null);
+    assert(notifications != null);
+    assert(object != null);
+    assert(objects != null);
+    assert(retainedSizes != null);
+    assert(reachableSizes != null);
+    assert(references != null);
+    assert(retainingPaths != null);
+    ObjectViewElement e = new ObjectViewElement.created();
+    e._r = new RenderingScheduler<ObjectViewElement>(e, queue: queue);
+    e._vm = vm;
+    e._isolate = isolate;
+    e._events = events;
+    e._notifications = notifications;
+    e._object = object;
+    e._objects = objects;
+    e._retainedSizes = retainedSizes;
+    e._reachableSizes = reachableSizes;
+    e._references = references;
+    e._retainingPaths = retainingPaths;
+    return e;
+  }
+
+  ObjectViewElement.created() : super.created('object-view');
+
+  @override
+  attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+  }
+
+  void render() {
+    children = <Element>[
+      navBar(<Element>[
+        new NavTopMenuElement(queue: _r.queue).element,
+        new NavVMMenuElement(_vm, _events, queue: _r.queue).element,
+        new NavIsolateMenuElement(_isolate, _events, queue: _r.queue).element,
+        navMenu('object'),
+        (new NavRefreshElement(queue: _r.queue)
+              ..onRefresh.listen((e) async {
+                e.element.disabled = true;
+                _object = await _objects.get(_isolate, _object.id);
+                _r.dirty();
+              }))
+            .element,
+        new NavNotifyElement(_notifications, queue: _r.queue).element
+      ]),
+      new DivElement()
+        ..classes = ['content-centered-big']
+        ..children = <Element>[
+          new HeadingElement.h2()..text = 'Object',
+          new HRElement(),
+          new ObjectCommonElement(_isolate, _object, _retainedSizes,
+                  _reachableSizes, _references, _retainingPaths, _objects,
+                  queue: _r.queue)
+              .element,
+          new HRElement(),
+          new ViewFooterElement(queue: _r.queue).element
+        ]
+    ];
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/objectpool_ref.dart b/runtime/observatory_2/lib/src/elements/objectpool_ref.dart
new file mode 100644
index 0000000..28d89b4
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/objectpool_ref.dart
@@ -0,0 +1,60 @@
+// 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.
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/models.dart' as M show IsolateRef, ObjectPoolRef;
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/helpers/uris.dart';
+
+class ObjectPoolRefElement extends CustomElement implements Renderable {
+  RenderingScheduler<ObjectPoolRefElement> _r;
+
+  Stream<RenderedEvent<ObjectPoolRefElement>> get onRendered => _r.onRendered;
+
+  M.IsolateRef _isolate;
+  M.ObjectPoolRef _pool;
+
+  M.IsolateRef get isolate => _isolate;
+  M.ObjectPoolRef get pool => _pool;
+
+  factory ObjectPoolRefElement(M.IsolateRef isolate, M.ObjectPoolRef pool,
+      {RenderingQueue queue}) {
+    assert(isolate != null);
+    assert(pool != null);
+    ObjectPoolRefElement e = new ObjectPoolRefElement.created();
+    e._r = new RenderingScheduler<ObjectPoolRefElement>(e, queue: queue);
+    e._isolate = isolate;
+    e._pool = pool;
+    return e;
+  }
+
+  ObjectPoolRefElement.created() : super.created('object-pool-ref');
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+  }
+
+  void render() {
+    children = <Element>[
+      new AnchorElement(href: Uris.inspect(_isolate, object: _pool))
+        ..children = <Element>[
+          new SpanElement()
+            ..classes = ['emphasize']
+            ..text = 'ObjectPool',
+          new SpanElement()..text = ' (${_pool.length})'
+        ]
+    ];
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/objectpool_view.dart b/runtime/observatory_2/lib/src/elements/objectpool_view.dart
new file mode 100644
index 0000000..a99091a
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/objectpool_view.dart
@@ -0,0 +1,167 @@
+// Copyright (c) 2015, 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.
+
+library objectpool_view;
+
+import 'dart:async';
+import 'dart:html';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/context_ref.dart';
+import 'package:observatory_2/src/elements/curly_block.dart';
+import 'package:observatory_2/src/elements/helpers/any_ref.dart';
+import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
+import 'package:observatory_2/src/elements/helpers/nav_menu.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/nav/isolate_menu.dart';
+import 'package:observatory_2/src/elements/nav/notify.dart';
+import 'package:observatory_2/src/elements/nav/refresh.dart';
+import 'package:observatory_2/src/elements/nav/top_menu.dart';
+import 'package:observatory_2/src/elements/nav/vm_menu.dart';
+import 'package:observatory_2/src/elements/object_common.dart';
+import 'package:observatory_2/src/elements/view_footer.dart';
+
+class ObjectPoolViewElement extends CustomElement implements Renderable {
+  RenderingScheduler<ObjectPoolViewElement> _r;
+
+  Stream<RenderedEvent<ObjectPoolViewElement>> get onRendered => _r.onRendered;
+
+  M.VM _vm;
+  M.IsolateRef _isolate;
+  M.EventRepository _events;
+  M.NotificationRepository _notifications;
+  M.ObjectPool _pool;
+  M.ObjectPoolRepository _pools;
+  M.RetainedSizeRepository _retainedSizes;
+  M.ReachableSizeRepository _reachableSizes;
+  M.InboundReferencesRepository _references;
+  M.RetainingPathRepository _retainingPaths;
+  M.ObjectRepository _objects;
+
+  M.VMRef get vm => _vm;
+  M.IsolateRef get isolate => _isolate;
+  M.NotificationRepository get notifications => _notifications;
+  M.ObjectPoolRef get pool => _pool;
+
+  factory ObjectPoolViewElement(
+      M.VM vm,
+      M.IsolateRef isolate,
+      M.ObjectPool pool,
+      M.EventRepository events,
+      M.NotificationRepository notifications,
+      M.ObjectPoolRepository pools,
+      M.RetainedSizeRepository retainedSizes,
+      M.ReachableSizeRepository reachableSizes,
+      M.InboundReferencesRepository references,
+      M.RetainingPathRepository retainingPaths,
+      M.ObjectRepository objects,
+      {RenderingQueue queue}) {
+    assert(vm != null);
+    assert(isolate != null);
+    assert(events != null);
+    assert(notifications != null);
+    assert(pool != null);
+    assert(pools != null);
+    assert(retainedSizes != null);
+    assert(reachableSizes != null);
+    assert(references != null);
+    assert(retainingPaths != null);
+    assert(objects != null);
+    ObjectPoolViewElement e = new ObjectPoolViewElement.created();
+    e._r = new RenderingScheduler<ObjectPoolViewElement>(e, queue: queue);
+    e._vm = vm;
+    e._isolate = isolate;
+    e._events = events;
+    e._notifications = notifications;
+    e._pool = pool;
+    e._pools = pools;
+    e._retainedSizes = retainedSizes;
+    e._reachableSizes = reachableSizes;
+    e._references = references;
+    e._retainingPaths = retainingPaths;
+    e._objects = objects;
+    return e;
+  }
+
+  ObjectPoolViewElement.created() : super.created('object-pool-view');
+
+  @override
+  attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+  }
+
+  void render() {
+    children = <Element>[
+      navBar(<Element>[
+        new NavTopMenuElement(queue: _r.queue).element,
+        new NavVMMenuElement(_vm, _events, queue: _r.queue).element,
+        new NavIsolateMenuElement(_isolate, _events, queue: _r.queue).element,
+        navMenu('instance'),
+        (new NavRefreshElement(queue: _r.queue)
+              ..onRefresh.listen((e) async {
+                e.element.disabled = true;
+                _pool = await _pools.get(_isolate, _pool.id);
+                _r.dirty();
+              }))
+            .element,
+        new NavNotifyElement(_notifications, queue: _r.queue).element
+      ]),
+      new DivElement()
+        ..classes = ['content-centered-big']
+        ..children = <Element>[
+          new HeadingElement.h2()..text = 'ObjectPool',
+          new HRElement(),
+          new ObjectCommonElement(_isolate, _pool, _retainedSizes,
+                  _reachableSizes, _references, _retainingPaths, _objects,
+                  queue: _r.queue)
+              .element,
+          new HRElement(),
+          new HeadingElement.h3()..text = 'entries (${_pool.entries.length})',
+          new DivElement()
+            ..classes = ['memberList']
+            ..children = _pool.entries
+                .map<Element>((entry) => new DivElement()
+                  ..classes = ['memberItem']
+                  ..children = <Element>[
+                    new DivElement()
+                      ..classes = ['memberName', 'hexadecimal']
+                      ..text = '[PP+0x${entry.offset.toRadixString(16)}]',
+                    new DivElement()
+                      ..classes = ['memberName']
+                      ..children = _createEntry(entry)
+                  ])
+                .toList(),
+          new HRElement(),
+          new ViewFooterElement(queue: _r.queue).element
+        ]
+    ];
+  }
+
+  List<Element> _createEntry(M.ObjectPoolEntry entry) {
+    switch (entry.kind) {
+      case M.ObjectPoolEntryKind.nativeEntryData:
+      case M.ObjectPoolEntryKind.object:
+        return [anyRef(_isolate, entry.asObject, _objects, queue: _r.queue)];
+      case M.ObjectPoolEntryKind.immediate:
+        return [
+          new SpanElement()
+            ..text = 'Immediate 0x${entry.asInteger.toRadixString(16)}'
+        ];
+      case M.ObjectPoolEntryKind.nativeEntry:
+        return [
+          new SpanElement()
+            ..text = 'NativeEntry 0x${entry.asInteger.toRadixString(16)}'
+        ];
+    }
+    throw new Exception('Unknown ObjectPoolEntryKind (${entry.kind})');
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/objectstore_view.dart b/runtime/observatory_2/lib/src/elements/objectstore_view.dart
new file mode 100644
index 0000000..864ca59
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/objectstore_view.dart
@@ -0,0 +1,130 @@
+// 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.
+
+library objectstore_view_element;
+
+import 'dart:async';
+import 'dart:html';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/helpers/any_ref.dart';
+import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/instance_ref.dart';
+import 'package:observatory_2/src/elements/nav/isolate_menu.dart';
+import 'package:observatory_2/src/elements/nav/notify.dart';
+import 'package:observatory_2/src/elements/nav/refresh.dart';
+import 'package:observatory_2/src/elements/nav/top_menu.dart';
+import 'package:observatory_2/src/elements/nav/vm_menu.dart';
+import 'package:observatory_2/src/elements/view_footer.dart';
+
+class ObjectStoreViewElement extends CustomElement implements Renderable {
+  RenderingScheduler<ObjectStoreViewElement> _r;
+
+  Stream<RenderedEvent<ObjectStoreViewElement>> get onRendered => _r.onRendered;
+
+  M.VM _vm;
+  M.IsolateRef _isolate;
+  M.EventRepository _events;
+  M.NotificationRepository _notifications;
+  M.ObjectStore _store;
+  M.ObjectStoreRepository _stores;
+  M.ObjectRepository _objects;
+
+  M.VMRef get vm => _vm;
+  M.IsolateRef get isolate => _isolate;
+  M.NotificationRepository get notifications => _notifications;
+
+  factory ObjectStoreViewElement(
+      M.VM vm,
+      M.IsolateRef isolate,
+      M.EventRepository events,
+      M.NotificationRepository notifications,
+      M.ObjectStoreRepository stores,
+      M.ObjectRepository objects,
+      {RenderingQueue queue}) {
+    assert(vm != null);
+    assert(isolate != null);
+    assert(events != null);
+    assert(notifications != null);
+    assert(stores != null);
+    assert(objects != null);
+    ObjectStoreViewElement e = new ObjectStoreViewElement.created();
+    e._r = new RenderingScheduler<ObjectStoreViewElement>(e, queue: queue);
+    e._vm = vm;
+    e._isolate = isolate;
+    e._events = events;
+    e._notifications = notifications;
+    e._stores = stores;
+    e._objects = objects;
+    return e;
+  }
+
+  ObjectStoreViewElement.created() : super.created('objectstore-view');
+
+  @override
+  attached() {
+    super.attached();
+    _r.enable();
+    _refresh();
+  }
+
+  @override
+  detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+  }
+
+  void render() {
+    final fields = _store?.fields?.toList(growable: false);
+    children = <Element>[
+      navBar(<Element>[
+        new NavTopMenuElement(queue: _r.queue).element,
+        new NavVMMenuElement(_vm, _events, queue: _r.queue).element,
+        new NavIsolateMenuElement(_isolate, _events, queue: _r.queue).element,
+        (new NavRefreshElement(disabled: _store == null, queue: _r.queue)
+              ..onRefresh.listen((e) => _refresh()))
+            .element,
+        (new NavNotifyElement(_notifications, queue: _r.queue).element)
+      ]),
+      new DivElement()
+        ..classes = ['content-centered-big']
+        ..children = <Element>[
+          new HeadingElement.h1()
+            ..text = fields == null
+                ? 'Object Store'
+                : 'Object Store (${fields.length})',
+          new HRElement(),
+          fields == null
+              ? (new HeadingElement.h2()..text = 'Loading...')
+              : (new DivElement()
+                ..classes = ['memberList']
+                ..children = fields
+                    .map<Element>((field) => new DivElement()
+                      ..classes = ['memberItem']
+                      ..children = <Element>[
+                        new DivElement()
+                          ..classes = ['memberName']
+                          ..text = field.name,
+                        new DivElement()
+                          ..classes = ['memberValue']
+                          ..children = <Element>[
+                            anyRef(_isolate, field.value, _objects,
+                                queue: _r.queue)
+                          ]
+                      ])
+                    .toList()),
+          new ViewFooterElement(queue: _r.queue).element
+        ]
+    ];
+  }
+
+  Future _refresh() async {
+    _store = null;
+    _r.dirty();
+    _store = await _stores.get(_isolate);
+    _r.dirty();
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/observatory_application.dart b/runtime/observatory_2/lib/src/elements/observatory_application.dart
new file mode 100644
index 0000000..83af0a8
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/observatory_application.dart
@@ -0,0 +1,24 @@
+// 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.
+
+library observatory_application_element;
+
+import 'package:observatory_2/app.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+
+/// Main application tag. Responsible for instantiating an instance of
+/// [ObservatoryApplication] which is passed declaratively to all child
+/// elements.
+class ObservatoryApplicationElement extends CustomElement {
+  ObservatoryApplication app;
+
+  ObservatoryApplicationElement.created()
+      : super.created('observatory-application');
+
+  @override
+  void attached() {
+    super.attached();
+    app = new ObservatoryApplication(this);
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/pc_descriptors_ref.dart b/runtime/observatory_2/lib/src/elements/pc_descriptors_ref.dart
new file mode 100644
index 0000000..16cca1d
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/pc_descriptors_ref.dart
@@ -0,0 +1,60 @@
+// 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.
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/models.dart' as M show IsolateRef, PcDescriptorsRef;
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/helpers/uris.dart';
+
+class PcDescriptorsRefElement extends CustomElement implements Renderable {
+  RenderingScheduler<PcDescriptorsRefElement> _r;
+
+  Stream<RenderedEvent<PcDescriptorsRefElement>> get onRendered =>
+      _r.onRendered;
+
+  M.IsolateRef _isolate;
+  M.PcDescriptorsRef _descriptors;
+
+  M.IsolateRef get isolate => _isolate;
+  M.PcDescriptorsRef get descriptors => _descriptors;
+
+  factory PcDescriptorsRefElement(
+      M.IsolateRef isolate, M.PcDescriptorsRef descriptors,
+      {RenderingQueue queue}) {
+    assert(isolate != null);
+    assert(descriptors != null);
+    PcDescriptorsRefElement e = new PcDescriptorsRefElement.created();
+    e._r = new RenderingScheduler<PcDescriptorsRefElement>(e, queue: queue);
+    e._isolate = isolate;
+    e._descriptors = descriptors;
+    return e;
+  }
+
+  PcDescriptorsRefElement.created() : super.created('pc-ref');
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+  }
+
+  void render() {
+    final text = (_descriptors.name == null || _descriptors.name == '')
+        ? 'PcDescriptors'
+        : _descriptors.name;
+    children = <Element>[
+      new AnchorElement(href: Uris.inspect(_isolate, object: _descriptors))
+        ..text = text
+    ];
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/persistent_handles.dart b/runtime/observatory_2/lib/src/elements/persistent_handles.dart
new file mode 100644
index 0000000..b85ad5f
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/persistent_handles.dart
@@ -0,0 +1,261 @@
+// Copyright (c) 2015, 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.
+
+library persitent_handles_page;
+
+import 'dart:async';
+import 'dart:html';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/containers/virtual_collection.dart';
+import 'package:observatory_2/src/elements/helpers/any_ref.dart';
+import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
+import 'package:observatory_2/src/elements/helpers/nav_menu.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/instance_ref.dart';
+import 'package:observatory_2/src/elements/nav/isolate_menu.dart';
+import 'package:observatory_2/src/elements/nav/notify.dart';
+import 'package:observatory_2/src/elements/nav/refresh.dart';
+import 'package:observatory_2/src/elements/nav/top_menu.dart';
+import 'package:observatory_2/src/elements/nav/vm_menu.dart';
+import 'package:observatory_2/utils.dart';
+
+enum _SortingField { externalSize, peer, finalizerCallback }
+
+enum _SortingDirection { ascending, descending }
+
+class PersistentHandlesPageElement extends CustomElement implements Renderable {
+  RenderingScheduler<PersistentHandlesPageElement> _r;
+
+  Stream<RenderedEvent<PersistentHandlesPageElement>> get onRendered =>
+      _r.onRendered;
+
+  M.VM _vm;
+  M.IsolateRef _isolate;
+  M.EventRepository _events;
+  M.NotificationRepository _notifications;
+  M.PersistentHandlesRepository _repository;
+  M.ObjectRepository _objects;
+  M.PersistentHandles _handles;
+  _SortingField _sortingField = _SortingField.externalSize;
+  _SortingDirection _sortingDirection = _SortingDirection.descending;
+
+  M.VMRef get vm => _vm;
+  M.IsolateRef get isolate => _isolate;
+  M.NotificationRepository get notifications => _notifications;
+
+  factory PersistentHandlesPageElement(
+      M.VM vm,
+      M.IsolateRef isolate,
+      M.EventRepository events,
+      M.NotificationRepository notifications,
+      M.PersistentHandlesRepository repository,
+      M.ObjectRepository objects,
+      {RenderingQueue queue}) {
+    assert(vm != null);
+    assert(isolate != null);
+    assert(events != null);
+    assert(notifications != null);
+    assert(repository != null);
+    assert(objects != null);
+    PersistentHandlesPageElement e = new PersistentHandlesPageElement.created();
+    e._r =
+        new RenderingScheduler<PersistentHandlesPageElement>(e, queue: queue);
+    e._vm = vm;
+    e._isolate = isolate;
+    e._events = events;
+    e._notifications = notifications;
+    e._repository = repository;
+    e._objects = objects;
+    return e;
+  }
+
+  PersistentHandlesPageElement.created()
+      : super.created('persistent-handles-page');
+
+  @override
+  attached() {
+    super.attached();
+    _r.enable();
+    _refresh();
+  }
+
+  @override
+  detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+  }
+
+  void render() {
+    children = <Element>[
+      navBar(<Element>[
+        new NavTopMenuElement(queue: _r.queue).element,
+        new NavVMMenuElement(_vm, _events, queue: _r.queue).element,
+        new NavIsolateMenuElement(_isolate, _events, queue: _r.queue).element,
+        navMenu('persistent handles'),
+        (new NavRefreshElement(queue: _r.queue)
+              ..onRefresh.listen((_) => _refresh()))
+            .element,
+        new NavNotifyElement(_notifications, queue: _r.queue).element
+      ])
+    ]
+      ..addAll(_createHandlers('Persistent Handles',
+          _handles?.elements?.toList(), _createLine, _updateLine))
+      ..add(new BRElement())
+      ..addAll(_createHandlers(
+          'Weak Persistent Handles',
+          _handles == null
+              ? null
+              : (_handles.weakElements.toList()..sort(_createSorter())),
+          _createWeakLine,
+          _updateWeakLine,
+          createHeader: _createWeakHeader));
+  }
+
+  List<Element> _createHandlers(String name, List items, create, update,
+      {createHeader}) {
+    return [
+      new DivElement()
+        ..classes = ['content-centered-big']
+        ..children = <Element>[
+          new HeadingElement.h1()
+            ..text = items == null ? '$name' : '$name (${items.length})',
+          new HRElement(),
+        ],
+      new DivElement()
+        ..classes = ['persistent-handles']
+        ..children = <Element>[
+          items == null
+              ? (new HeadingElement.h2()
+                ..classes = ['content-centered-big']
+                ..text = 'Loading...')
+              : new VirtualCollectionElement(create, update,
+                      items: items, createHeader: createHeader, queue: _r.queue)
+                  .element
+        ]
+    ];
+  }
+
+  _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:
+        int sort(M.WeakPersistentHandle a, M.WeakPersistentHandle b) {
+          return getter(a).compareTo(getter(b));
+        }
+        return sort;
+      case _SortingDirection.descending:
+        int sort(M.WeakPersistentHandle a, M.WeakPersistentHandle b) {
+          return getter(b).compareTo(getter(a));
+        }
+        return sort;
+    }
+  }
+
+  static HtmlElement _createLine() => new DivElement()
+    ..classes = ['collection-item']
+    ..text = 'object';
+
+  static HtmlElement _createWeakLine() => new DivElement()
+    ..classes = ['weak-item']
+    ..children = <Element>[
+      new SpanElement()
+        ..classes = ['external-size']
+        ..text = '0B',
+      new SpanElement()
+        ..classes = ['peer']
+        ..text = '0x00000',
+      new SpanElement()..classes = ['object'],
+      new SpanElement()
+        ..classes = ['finalizer']
+        ..text = 'dart::Class::Method()'
+    ];
+
+  List<HtmlElement> _createWeakHeader() => [
+        new DivElement()
+          ..classes = ['weak-item']
+          ..children = <Element>[
+            _createHeaderButton(const ['external-size'], 'External Size',
+                _SortingField.externalSize, _SortingDirection.descending),
+            _createHeaderButton(const ['peer'], 'Peer', _SortingField.peer,
+                _SortingDirection.descending),
+            new SpanElement()
+              ..classes = ['object']
+              ..text = 'Object',
+            _createHeaderButton(const ['finalizer'], 'Finalizer Callback',
+                _SortingField.finalizerCallback, _SortingDirection.ascending)
+          ]
+      ];
+
+  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();
+  }
+
+  void _updateWeakLine(Element e, itemDynamic, index) {
+    M.WeakPersistentHandle item = itemDynamic;
+    e.children[0].text = Utils.formatSize(_getExternalSize(item));
+    e.children[1].text = '${_getPeer(item)}';
+    e.children[2] = anyRef(_isolate, item.object, _objects, queue: _r.queue)
+      ..classes = ['object'];
+    e.children[3]
+      ..text = '${_getFinalizerCallback(item)}'
+      ..title = '${_getFinalizerCallback(item)}';
+  }
+
+  void _updateLine(Element e, itemDynamic, index) {
+    M.PersistentHandle item = itemDynamic;
+    e.children = <Element>[
+      anyRef(_isolate, item.object, _objects, queue: _r.queue)
+        ..classes = ['object']
+    ];
+  }
+
+  Future _refresh({bool gc: false, bool reset: false}) async {
+    _handles = null;
+    _r.dirty();
+    _handles = await _repository.get(_isolate);
+    _r.dirty();
+  }
+
+  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})';
+}
diff --git a/runtime/observatory_2/lib/src/elements/ports.dart b/runtime/observatory_2/lib/src/elements/ports.dart
new file mode 100644
index 0000000..e8e4a49
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/ports.dart
@@ -0,0 +1,140 @@
+// Copyright (c) 2015, 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.
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/helpers/any_ref.dart';
+import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
+import 'package:observatory_2/src/elements/helpers/nav_menu.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/instance_ref.dart';
+import 'package:observatory_2/src/elements/nav/isolate_menu.dart';
+import 'package:observatory_2/src/elements/nav/notify.dart';
+import 'package:observatory_2/src/elements/nav/refresh.dart';
+import 'package:observatory_2/src/elements/nav/top_menu.dart';
+import 'package:observatory_2/src/elements/nav/vm_menu.dart';
+import 'package:observatory_2/src/elements/view_footer.dart';
+
+class PortsElement extends CustomElement implements Renderable {
+  RenderingScheduler<PortsElement> _r;
+
+  Stream<RenderedEvent<PortsElement>> get onRendered => _r.onRendered;
+
+  M.VM _vm;
+  M.IsolateRef _isolate;
+  M.EventRepository _events;
+  M.NotificationRepository _notifications;
+  M.PortsRepository _ports;
+  M.ObjectRepository _objects;
+  M.Ports _isolatePorts;
+
+  M.IsolateRef get isolate => _isolate;
+  M.NotificationRepository get notifications => _notifications;
+  M.PortsRepository get ports => _ports;
+  M.VMRef get vm => _vm;
+
+  factory PortsElement(
+      M.VM vm,
+      M.IsolateRef isolate,
+      M.EventRepository events,
+      M.NotificationRepository notifications,
+      M.PortsRepository ports,
+      M.ObjectRepository objects,
+      {RenderingQueue queue}) {
+    assert(vm != null);
+    assert(isolate != null);
+    assert(events != null);
+    assert(notifications != null);
+    assert(ports != null);
+    assert(objects != null);
+    PortsElement e = new PortsElement.created();
+    e._r = new RenderingScheduler<PortsElement>(e, queue: queue);
+    e._vm = vm;
+    e._isolate = isolate;
+    e._events = events;
+    e._notifications = notifications;
+    e._ports = ports;
+    e._objects = objects;
+    return e;
+  }
+
+  PortsElement.created() : super.created('ports-page');
+
+  int get portCount {
+    return _isolatePorts == null ? 0 : _isolatePorts.elements.length;
+  }
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+    _refresh();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    children = <Element>[];
+    _r.disable(notify: true);
+  }
+
+  void render() {
+    children = <Element>[
+      navBar(<Element>[
+        new NavTopMenuElement(queue: _r.queue).element,
+        new NavVMMenuElement(_vm, _events, queue: _r.queue).element,
+        new NavIsolateMenuElement(_isolate, _events, queue: _r.queue).element,
+        navMenu('ports'),
+        (new NavRefreshElement(queue: _r.queue)
+              ..onRefresh.listen((_) => _refresh()))
+            .element,
+        new NavNotifyElement(_notifications, queue: _r.queue).element
+      ]),
+      new DivElement()
+        ..classes = ['content-centered']
+        ..children = <Element>[
+          new HeadingElement.h1()..text = 'Ports ($portCount)',
+          new HRElement(),
+          new BRElement(),
+          new DivElement()..children = _createList(),
+        ],
+      new ViewFooterElement(queue: _r.queue).element
+    ];
+  }
+
+  List<Element> _createList() {
+    if (_isolatePorts == null) {
+      return const [];
+    }
+    int i = 0;
+    return _isolatePorts.elements
+        .map<Element>((port) => new DivElement()
+          ..classes = ['memberItem']
+          ..children = <Element>[
+            new DivElement()
+              ..classes = ['memberName']
+              ..children = <Element>[
+                new SpanElement()
+                  ..classes = ['port-number']
+                  ..text = '[ ${++i} ] ',
+                new SpanElement()..text = '${port.name}'
+              ],
+            new DivElement()
+              ..classes = ['memberValue']
+              ..children = <Element>[
+                anyRef(_isolate, port.handler, _objects, queue: _r.queue)
+              ]
+          ])
+        .toList();
+  }
+
+  Future _refresh() async {
+    _isolatePorts = null;
+    _r.dirty();
+    _isolatePorts = await _ports.get(_isolate);
+    _r.dirty();
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/process_snapshot.dart b/runtime/observatory_2/lib/src/elements/process_snapshot.dart
new file mode 100644
index 0000000..82593da
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/process_snapshot.dart
@@ -0,0 +1,279 @@
+// Copyright (c) 2020, 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.
+
+import 'dart:async';
+import 'dart:html';
+import 'dart:convert';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/object_graph.dart';
+import 'package:observatory_2/src/elements/class_ref.dart';
+import 'package:observatory_2/src/elements/containers/virtual_tree.dart';
+import 'package:observatory_2/src/elements/curly_block.dart';
+import 'package:observatory_2/src/elements/helpers/any_ref.dart';
+import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
+import 'package:observatory_2/src/elements/helpers/nav_menu.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/helpers/uris.dart';
+import 'package:observatory_2/src/elements/nav/isolate_menu.dart';
+import 'package:observatory_2/src/elements/nav/notify.dart';
+import 'package:observatory_2/src/elements/nav/refresh.dart';
+import 'package:observatory_2/src/elements/nav/top_menu.dart';
+import 'package:observatory_2/src/elements/nav/vm_menu.dart';
+import 'package:observatory_2/src/elements/tree_map.dart';
+import 'package:observatory_2/repositories.dart';
+import 'package:observatory_2/utils.dart';
+
+class ProcessItemTreeMap extends NormalTreeMap<Map> {
+  ProcessSnapshotElement element;
+  ProcessItemTreeMap(this.element);
+
+  int getSize(Map node) => node["size"];
+  String getType(Map node) => node["name"];
+  String getName(Map node) => node["name"];
+  String getTooltip(Map node) => getLabel(node) + "\n" + node["description"];
+  Map getParent(Map node) => node["parent"];
+  Iterable<Map> getChildren(Map node) => new List<Map>.from(node["children"]);
+  void onSelect(Map node) {
+    element.selection = node;
+    element._r.dirty();
+  }
+
+  void onDetails(Map node) {}
+}
+
+class ProcessSnapshotElement extends CustomElement implements Renderable {
+  RenderingScheduler<ProcessSnapshotElement> _r;
+
+  Stream<RenderedEvent<ProcessSnapshotElement>> get onRendered => _r.onRendered;
+
+  M.VM _vm;
+  M.EventRepository _events;
+  M.NotificationRepository _notifications;
+  M.NotificationRepository get notifications => _notifications;
+  M.VMRef get vm => _vm;
+
+  List<Map> _loadedSnapshots = <Map>[];
+  Map selection;
+  Map _snapshotA;
+  Map _snapshotB;
+
+  factory ProcessSnapshotElement(
+      M.VM vm, M.EventRepository events, M.NotificationRepository notifications,
+      {RenderingQueue queue}) {
+    assert(vm != null);
+    assert(events != null);
+    assert(notifications != null);
+    ProcessSnapshotElement e = new ProcessSnapshotElement.created();
+    e._r = new RenderingScheduler<ProcessSnapshotElement>(e, queue: queue);
+    e._vm = vm;
+    e._events = events;
+    e._notifications = notifications;
+    return e;
+  }
+
+  ProcessSnapshotElement.created() : super.created('process-snapshot');
+
+  @override
+  attached() {
+    super.attached();
+    _r.enable();
+    _refresh();
+  }
+
+  @override
+  detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+  }
+
+  void render() {
+    final content = <Element>[
+      navBar(<Element>[
+        new NavTopMenuElement(queue: _r.queue).element,
+        new NavVMMenuElement(_vm, _events, queue: _r.queue).element,
+        navMenu('process snapshot'),
+        (new NavRefreshElement(queue: _r.queue)
+              ..onRefresh.listen((e) {
+                _refresh();
+              }))
+            .element,
+        (new NavRefreshElement(label: 'save', queue: _r.queue)
+              ..disabled = _snapshotA == null
+              ..onRefresh.listen((e) {
+                _save();
+              }))
+            .element,
+        (new NavRefreshElement(label: 'load', queue: _r.queue)
+              ..onRefresh.listen((e) {
+                _load();
+              }))
+            .element,
+        new NavNotifyElement(_notifications, queue: _r.queue).element
+      ]),
+    ];
+    if (_snapshotA == null) {
+      // Loading
+      content.add(new SpanElement()..text = "Loading");
+    } else {
+      // Loaded
+      content.addAll(_createReport());
+    }
+    children = content;
+  }
+
+  _refresh() async {
+    Map snapshot =
+        await (vm as dynamic).invokeRpcNoUpgrade("getProcessMemoryUsage", {});
+    _snapshotLoaded(snapshot);
+  }
+
+  _save() {
+    var blob = new Blob([jsonEncode(_snapshotA)], 'application/json');
+    var blobUrl = Url.createObjectUrl(blob);
+    var link = new AnchorElement();
+    link.href = blobUrl;
+    var now = new DateTime.now();
+    link.download = 'dart-process-${now.year}-${now.month}-${now.day}.json';
+    link.click();
+  }
+
+  _load() {
+    var input = new InputElement();
+    input.type = 'file';
+    input.multiple = false;
+    input.onChange.listen((event) {
+      var file = input.files[0];
+      var reader = new FileReader();
+      reader.onLoad.listen((event) async {
+        _snapshotLoaded(jsonDecode(reader.result));
+      });
+      reader.readAsText(file);
+    });
+    input.click();
+  }
+
+  _snapshotLoaded(Map snapshot) {
+    _loadedSnapshots.add(snapshot);
+    _snapshotA = snapshot;
+    _snapshotB = snapshot;
+    _r.dirty();
+  }
+
+  void _createTreeMap<T>(List<HtmlElement> report, TreeMap<T> treemap, T root) {
+    final content = new DivElement();
+    content.style.border = '1px solid black';
+    content.style.width = '100%';
+    content.style.height = '100%';
+    content.text = 'Performing layout...';
+    Timer.run(() {
+      // Generate the treemap after the content div has been added to the
+      // document so that we can ask the browser how much space is
+      // available for treemap layout.
+      treemap.showIn(root, content);
+    });
+
+    final text =
+        'Double-click a tile to zoom in. Double-click the outermost tile to '
+        'zoom out. Process memory that is not further subdivided is non-Dart '
+        'memory not known to the VM.';
+    report.addAll([
+      new DivElement()
+        ..classes = ['content-centered-big', 'explanation']
+        ..text = text,
+      new DivElement()
+        ..classes = ['content-centered-big']
+        ..style.width = '100%'
+        ..style.height = '100%'
+        ..children = [content]
+    ]);
+  }
+
+  List<Element> _createReport() {
+    var report = <HtmlElement>[
+      new DivElement()
+        ..classes = ['content-centered-big']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberList']
+            ..children = <Element>[
+              new DivElement()
+                ..classes = ['memberItem']
+                ..children = <Element>[
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..text = 'Snapshot A',
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..children = _createSnapshotSelectA()
+                ],
+              // TODO(rmacnak): Diffing.
+              // new DivElement()
+              //  ..classes = ['memberItem']
+              //  ..children = <Element>[
+              //    new DivElement()
+              //      ..classes = ['memberName']
+              //      ..text = 'Snapshot B',
+              //    new DivElement()
+              //      ..classes = ['memberName']
+              //      ..children = _createSnapshotSelectB()
+              //  ],
+            ]
+        ],
+    ];
+    if (selection == null) {
+      selection = _snapshotA["root"];
+    }
+    _createTreeMap(report, new ProcessItemTreeMap(this), selection);
+    return report;
+  }
+
+  String snapshotToString(snapshot) {
+    if (snapshot == null) return "None";
+    return snapshot["root"]["name"] +
+        " " +
+        Utils.formatSize(snapshot["root"]["size"]);
+  }
+
+  List<Element> _createSnapshotSelectA() {
+    var s;
+    return [
+      s = new SelectElement()
+        ..classes = ['analysis-select']
+        ..value = snapshotToString(_snapshotA)
+        ..children = _loadedSnapshots.map((snapshot) {
+          return new OptionElement(
+              value: snapshotToString(snapshot),
+              selected: _snapshotA == snapshot)
+            ..text = snapshotToString(snapshot);
+        }).toList(growable: false)
+        ..onChange.listen((_) {
+          _snapshotA = _loadedSnapshots[s.selectedIndex];
+          selection = null;
+          _r.dirty();
+        })
+    ];
+  }
+
+  List<Element> _createSnapshotSelectB() {
+    var s;
+    return [
+      s = new SelectElement()
+        ..classes = ['analysis-select']
+        ..value = snapshotToString(_snapshotB)
+        ..children = _loadedSnapshots.map((snapshot) {
+          return new OptionElement(
+              value: snapshotToString(snapshot),
+              selected: _snapshotB == snapshot)
+            ..text = snapshotToString(snapshot);
+        }).toList(growable: false)
+        ..onChange.listen((_) {
+          _snapshotB = _loadedSnapshots[s.selectedIndex];
+          selection = null;
+          _r.dirty();
+        })
+    ];
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/retaining_path.dart b/runtime/observatory_2/lib/src/elements/retaining_path.dart
new file mode 100644
index 0000000..9ba99e5
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/retaining_path.dart
@@ -0,0 +1,127 @@
+// Copyright (c) 2016, 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.
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/curly_block.dart';
+import 'package:observatory_2/src/elements/instance_ref.dart';
+import 'package:observatory_2/src/elements/helpers/any_ref.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+
+class RetainingPathElement extends CustomElement implements Renderable {
+  RenderingScheduler<RetainingPathElement> _r;
+
+  Stream<RenderedEvent<RetainingPathElement>> get onRendered => _r.onRendered;
+
+  M.IsolateRef _isolate;
+  M.ObjectRef _object;
+  M.RetainingPathRepository _retainingPaths;
+  M.ObjectRepository _objects;
+  M.RetainingPath _path;
+  bool _expanded = false;
+
+  M.IsolateRef get isolate => _isolate;
+  M.ObjectRef get object => _object;
+
+  factory RetainingPathElement(M.IsolateRef isolate, M.ObjectRef object,
+      M.RetainingPathRepository retainingPaths, M.ObjectRepository objects,
+      {RenderingQueue queue}) {
+    assert(isolate != null);
+    assert(object != null);
+    assert(retainingPaths != null);
+    assert(objects != null);
+    RetainingPathElement e = new RetainingPathElement.created();
+    e._r = new RenderingScheduler<RetainingPathElement>(e, queue: queue);
+    e._isolate = isolate;
+    e._object = object;
+    e._retainingPaths = retainingPaths;
+    e._objects = objects;
+    return e;
+  }
+
+  RetainingPathElement.created() : super.created('retaining-path');
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    children = <Element>[];
+    _r.disable(notify: true);
+  }
+
+  void render() {
+    final curlyBlock =
+        new CurlyBlockElement(expanded: _expanded, queue: _r.queue)
+          ..content = _createContent()
+          ..onToggle.listen((e) async {
+            _expanded = e.control.expanded;
+            if (_expanded) {
+              e.control.disabled = true;
+              await _refresh();
+              e.control.disabled = false;
+            }
+          });
+    children = <Element>[curlyBlock.element];
+    _r.waitFor([curlyBlock.onRendered.first]);
+  }
+
+  Future _refresh() async {
+    _path = null;
+    _path = await _retainingPaths.get(_isolate, _object.id);
+    _r.dirty();
+  }
+
+  List<Element> _createContent() {
+    if (_path == null) {
+      return [new SpanElement()..text = 'Loading'];
+    }
+
+    var elements = <Element>[];
+    bool first = true;
+    for (var item in _path.elements) {
+      elements.add(_createItem(item, first));
+      first = false;
+    }
+    elements.add(_createGCRootItem(_path.gcRootType));
+    return elements;
+  }
+
+  Element _createItem(M.RetainingPathItem item, bool first) {
+    final content = <Element>[];
+
+    if (first) {
+      // No prefix.
+    } else if (item.parentField != null) {
+      content
+          .add(new SpanElement()..text = 'retained by ${item.parentField} of ');
+    } else if (item.parentListIndex != null) {
+      content.add(new SpanElement()
+        ..text = 'retained by [ ${item.parentListIndex} ] of ');
+    } else if (item.parentWordOffset != null) {
+      content.add(new SpanElement()
+        ..text = 'retained by offset ${item.parentWordOffset} of ');
+    } else {
+      content.add(new SpanElement()..text = 'retained by ');
+    }
+
+    content.add(anyRef(_isolate, item.source, _objects, queue: _r.queue));
+
+    return new DivElement()
+      ..classes = ['indent']
+      ..children = content;
+  }
+
+  Element _createGCRootItem(String gcRootType) {
+    return new DivElement()
+      ..classes = ['indent']
+      ..text = 'retained by a GC root ($gcRootType)';
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/sample_buffer_control.dart b/runtime/observatory_2/lib/src/elements/sample_buffer_control.dart
new file mode 100644
index 0000000..05ca174
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/sample_buffer_control.dart
@@ -0,0 +1,270 @@
+// 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.
+
+import 'dart:async';
+import 'dart:html';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/utils.dart';
+
+class SampleBufferControlChangedElement {
+  final SampleBufferControlElement element;
+  SampleBufferControlChangedElement(this.element);
+}
+
+class SampleBufferControlElement extends CustomElement implements Renderable {
+  RenderingScheduler<SampleBufferControlElement> _r;
+
+  Stream<RenderedEvent<SampleBufferControlElement>> get onRendered =>
+      _r.onRendered;
+
+  StreamController<SampleBufferControlChangedElement> _onTagChange =
+      new StreamController<SampleBufferControlChangedElement>.broadcast();
+  Stream<SampleBufferControlChangedElement> get onTagChange =>
+      _onTagChange.stream;
+
+  M.VM _vm;
+  Stream<M.SampleProfileLoadingProgressEvent> _progressStream;
+  M.SampleProfileLoadingProgress _progress;
+  M.SampleProfileTag _tag;
+  bool _showTag = false;
+  bool _profileVM = false;
+  StreamSubscription _subscription;
+
+  M.SampleProfileLoadingProgress get progress => _progress;
+  M.SampleProfileTag get selectedTag => _tag;
+  bool get showTag => _showTag;
+  bool get profileVM => _profileVM;
+
+  set selectedTag(M.SampleProfileTag value) =>
+      _tag = _r.checkAndReact(_tag, value);
+  set showTag(bool value) => _showTag = _r.checkAndReact(_showTag, value);
+  set profileVM(bool value) => _profileVM = _r.checkAndReact(_profileVM, value);
+
+  factory SampleBufferControlElement(
+      M.VM vm,
+      M.SampleProfileLoadingProgress progress,
+      Stream<M.SampleProfileLoadingProgressEvent> progressStream,
+      {M.SampleProfileTag selectedTag: M.SampleProfileTag.none,
+      bool showTag: true,
+      RenderingQueue queue}) {
+    assert(progress != null);
+    assert(progressStream != null);
+    assert(selectedTag != null);
+    assert(showTag != null);
+    SampleBufferControlElement e = new SampleBufferControlElement.created();
+    e._r = new RenderingScheduler<SampleBufferControlElement>(e, queue: queue);
+    e._vm = vm;
+    e._progress = progress;
+    e._progressStream = progressStream;
+    e._tag = selectedTag;
+    e._showTag = showTag;
+    return e;
+  }
+
+  SampleBufferControlElement.created() : super.created('sample-buffer-control');
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+    _subscription = _progressStream.listen((e) {
+      _progress = e.progress;
+      _r.dirty();
+    });
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = const [];
+    _subscription.cancel();
+  }
+
+  void render() {
+    var content = <Element>[
+      new HeadingElement.h2()..text = 'Sample buffer',
+      new HRElement()
+    ];
+    switch (_progress.status) {
+      case M.SampleProfileLoadingStatus.fetching:
+        content.addAll(_createStatusMessage('Fetching profile from VM...'));
+        break;
+      case M.SampleProfileLoadingStatus.loading:
+        content.addAll(_createStatusMessage('Loading profile...',
+            progress: _progress.progress));
+        break;
+      case M.SampleProfileLoadingStatus.disabled:
+        content.addAll(_createDisabledMessage());
+        break;
+      case M.SampleProfileLoadingStatus.loaded:
+        content.addAll(_createStatusReport());
+        break;
+    }
+    children = <Element>[
+      new DivElement()
+        ..classes = ['content-centered-big']
+        ..children = content
+    ];
+  }
+
+  static List<Element> _createStatusMessage(String message,
+      {double progress: 0.0}) {
+    return [
+      new DivElement()
+        ..classes = ['statusBox', 'shadow', 'center']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['statusMessage']
+            ..text = message,
+          new DivElement()
+            ..style.background = '#0489c3'
+            ..style.width = '$progress%'
+            ..style.height = '15px'
+            ..style.borderRadius = '4px'
+        ]
+    ];
+  }
+
+  List<Element> _createDisabledMessage() {
+    return [
+      new DivElement()
+        ..classes = ['statusBox' 'shadow' 'center']
+        ..children = <Element>[
+          new DivElement()
+            ..children = <Element>[
+              new HeadingElement.h1()..text = 'Profiling is disabled',
+              new BRElement(),
+              new DivElement()
+                ..innerHtml = 'Perhaps the <b>profile</b> '
+                    'flag has been disabled for this VM.',
+              new BRElement(),
+              new ButtonElement()
+                ..text = 'Enable profiler'
+                ..onClick.listen((_) {
+                  _enableProfiler();
+                })
+            ]
+        ]
+    ];
+  }
+
+  List<Element> _createStatusReport() {
+    final fetchT = Utils.formatDurationInSeconds(_progress.fetchingTime);
+    final loadT = Utils.formatDurationInSeconds(_progress.loadingTime);
+    final sampleCount = _progress.profile.sampleCount;
+    final refreshT = new DateTime.now();
+    final maxStackDepth = _progress.profile.maxStackDepth;
+    final sampleRate = _progress.profile.sampleRate.toStringAsFixed(0);
+    final timeSpan = _progress.profile.sampleCount == 0
+        ? '0s'
+        : Utils.formatTimePrecise(_progress.profile.timeSpan);
+
+    var content = <Element>[
+      new DivElement()
+        ..classes = ['memberItem']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = 'Refreshed at',
+          new DivElement()
+            ..classes = ['memberValue']
+            ..text = '$refreshT (fetched in ${fetchT}s) (loaded in ${loadT}s)'
+        ],
+      new DivElement()
+        ..classes = ['memberItem']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = 'Profile contains ',
+          new DivElement()
+            ..classes = ['memberValue']
+            ..text = '$sampleCount samples (spanning $timeSpan)'
+        ],
+      new DivElement()
+        ..classes = ['memberItem']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = 'Sampling',
+          new DivElement()
+            ..classes = ['memberValue']
+            ..text = '$maxStackDepth stack frames @ ${sampleRate}Hz'
+        ],
+    ];
+    if (_showTag) {
+      content.add(new DivElement()
+        ..classes = ['memberItem']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = 'Tag Order',
+          new DivElement()
+            ..classes = ['memberValue']
+            ..children = _createTagSelect()
+        ]);
+    }
+    return [
+      new DivElement()
+        ..classes = ['memberList']
+        ..children = content
+    ];
+  }
+
+  List<Element> _createTagSelect() {
+    var values = M.SampleProfileTag.values;
+    if (!_profileVM) {
+      values = const [
+        M.SampleProfileTag.userOnly,
+        M.SampleProfileTag.vmOnly,
+        M.SampleProfileTag.none
+      ];
+    }
+    var s;
+    return [
+      s = new SelectElement()
+        ..classes = ['tag-select']
+        ..value = tagToString(_tag)
+        ..children = values.map((tag) {
+          return new OptionElement(
+              value: tagToString(tag), selected: _tag == tag)
+            ..text = tagToString(tag);
+        }).toList(growable: false)
+        ..onChange.listen((_) {
+          _tag = values[s.selectedIndex];
+        })
+        ..onChange.map(_toEvent).listen(_triggerModeChange),
+    ];
+  }
+
+  static String tagToString(M.SampleProfileTag tag) {
+    switch (tag) {
+      case M.SampleProfileTag.userVM:
+        return 'User > VM';
+      case M.SampleProfileTag.userOnly:
+        return 'User';
+      case M.SampleProfileTag.vmUser:
+        return 'VM > User';
+      case M.SampleProfileTag.vmOnly:
+        return 'VM';
+      case M.SampleProfileTag.none:
+        return 'None';
+    }
+    throw new Exception('Unknown tagToString');
+  }
+
+  SampleBufferControlChangedElement _toEvent(_) {
+    return new SampleBufferControlChangedElement(this);
+  }
+
+  void _enableProfiler() {
+    _vm.enableProfiler().then((_) {
+      _triggerModeChange(_toEvent(null));
+    });
+  }
+
+  void _triggerModeChange(e) => _onTagChange.add(e);
+}
diff --git a/runtime/observatory_2/lib/src/elements/script_inset.dart b/runtime/observatory_2/lib/src/elements/script_inset.dart
new file mode 100644
index 0000000..c4a0094
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/script_inset.dart
@@ -0,0 +1,1403 @@
+// 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.
+
+library script_inset_element;
+
+import 'dart:async';
+import 'dart:html';
+import 'dart:svg';
+import 'package:observatory_2/app.dart';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/service.dart' as S;
+import 'package:observatory_2/src/elements/helpers/any_ref.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/helpers/uris.dart';
+import 'package:observatory_2/utils.dart';
+
+class ScriptInsetElement extends CustomElement implements Renderable {
+  RenderingScheduler<ScriptInsetElement> _r;
+
+  Stream<RenderedEvent<ScriptInsetElement>> get onRendered => _r.onRendered;
+
+  M.IsolateRef _isolate;
+  M.ScriptRef _script;
+  M.Script _loadedScript;
+  M.ScriptRepository _scripts;
+  M.ObjectRepository _objects;
+  M.EventRepository _events;
+  StreamSubscription _subscription;
+  int _startPos;
+  int _endPos;
+  int _currentPos;
+  bool _inDebuggerContext;
+  Iterable _variables;
+
+  M.IsolateRef get isolate => _isolate;
+  M.ScriptRef get script => _script;
+
+  factory ScriptInsetElement(
+      M.IsolateRef isolate,
+      M.ScriptRef script,
+      M.ScriptRepository scripts,
+      M.ObjectRepository objects,
+      M.EventRepository events,
+      {int startPos,
+      int endPos,
+      int currentPos,
+      bool inDebuggerContext: false,
+      Iterable variables: const [],
+      RenderingQueue queue}) {
+    assert(isolate != null);
+    assert(script != null);
+    assert(scripts != null);
+    assert(objects != null);
+    assert(events != null);
+    assert(inDebuggerContext != null);
+    assert(variables != null);
+    ScriptInsetElement e = new ScriptInsetElement.created();
+    e._r = new RenderingScheduler<ScriptInsetElement>(e, queue: queue);
+    e._isolate = isolate;
+    e._script = script;
+    e._scripts = scripts;
+    e._objects = objects;
+    e._events = events;
+    e._startPos = startPos;
+    e._endPos = endPos;
+    e._currentPos = currentPos;
+    e._inDebuggerContext = inDebuggerContext;
+    e._variables = new List.unmodifiable(variables);
+    return e;
+  }
+
+  ScriptInsetElement.created() : super.created('script-inset');
+
+  bool get noSource => _startPos == -1 || _loadedScript.source == null;
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+    _subscription = _events.onDebugEvent
+        .where((e) => e is M.BreakpointEvent)
+        .map((e) => (e as M.BreakpointEvent).breakpoint)
+        .listen((M.Breakpoint b) async {
+      final M.Location loc = b.location;
+      int line;
+      if (loc.script.id == script.id) {
+        if (loc.tokenPos != null) {
+          line = _loadedScript.tokenToLine(loc.tokenPos);
+        } else {
+          line = (loc as dynamic).line;
+        }
+      } else {
+        try {
+          line = (loc as dynamic).line;
+        } on NoSuchMethodError {
+          if (loc.tokenPos != null) {
+            M.Script scriptUsed = await _scripts.get(_isolate, loc.script.id);
+            line = scriptUsed.tokenToLine(loc.tokenPos);
+          }
+        }
+      }
+      if ((line == null) || ((line >= _startLine) && (line <= _endLine))) {
+        _r.dirty();
+      }
+    });
+    _refresh();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    children = <Element>[];
+    _r.disable(notify: true);
+    _subscription.cancel();
+  }
+
+  void render() {
+    if (_loadedScript == null) {
+      children = <Element>[new SpanElement()..text = 'Loading...'];
+    } else if (noSource) {
+      children = <Element>[new SpanElement()..text = 'No source'];
+    } else {
+      final table = linesTable();
+      var firstBuild = false;
+      if (container == null) {
+        // Indirect to avoid deleting the style element.
+        container = new DivElement();
+
+        firstBuild = true;
+      }
+      children = <Element>[container];
+      container.children.clear();
+      container.children.add(table);
+      _makeCssClassUncopyable(table, "noCopy");
+      if (firstBuild) {
+        _scrollToCurrentPos();
+      }
+    }
+  }
+
+  Future _refresh() async {
+    _loadedScript = await _scripts.get(_isolate, _script.id);
+    await _refreshSourceReport();
+    await _computeAnnotations();
+    _r.dirty();
+  }
+
+  ButtonElement _refreshButton;
+  ButtonElement _toggleProfileButton;
+
+  int _currentLine;
+  int _currentCol;
+  int _startLine;
+  int _endLine;
+
+  Map/*<int, List<S.ServiceMap>>*/ _rangeMap = {};
+  Set _callSites = new Set<S.CallSite>();
+  Set _possibleBreakpointLines = new Set<int>();
+  Map<int, ScriptLineProfile> _profileMap = {};
+
+  var _annotations = [];
+  var _annotationsCursor;
+
+  bool _includeProfile = false;
+
+  String makeLineClass(int line) {
+    return 'script-inset-line-$line';
+  }
+
+  void _scrollToCurrentPos() {
+    var lines = getElementsByClassName(makeLineClass(_currentLine));
+    if (lines.length > 0) {
+      (lines[0] as dynamic).scrollIntoView();
+    }
+  }
+
+  Element a(String text) => new AnchorElement()..text = text;
+  Element span(String text) => new SpanElement()..text = text;
+
+  Element hitsCurrent(Element element) {
+    element.classes.add('hitsCurrent');
+    element.title = "";
+    return element;
+  }
+
+  Element hitsUnknown(Element element) {
+    element.classes.add('hitsNone');
+    element.title = "";
+    return element;
+  }
+
+  Element hitsNotExecuted(Element element) {
+    element.classes.add('hitsNotExecuted');
+    element.title = "Line did not execute";
+    return element;
+  }
+
+  Element hitsExecuted(Element element) {
+    element.classes.add('hitsExecuted');
+    element.title = "Line did execute";
+    return element;
+  }
+
+  Element hitsCompiled(Element element) {
+    element.classes.add('hitsCompiled');
+    element.title = "Line in compiled function";
+    return element;
+  }
+
+  Element hitsNotCompiled(Element element) {
+    element.classes.add('hitsNotCompiled');
+    element.title = "Line in uncompiled function";
+    return element;
+  }
+
+  Element container;
+
+  // Build _rangeMap and _callSites from a source report.
+  Future _refreshSourceReport() async {
+    if (noSource) return;
+
+    var reports = [
+      S.Isolate.kCallSitesReport,
+      S.Isolate.kPossibleBreakpointsReport
+    ];
+    if (_includeProfile) {
+      reports.add(S.Isolate.kProfileReport);
+    }
+    S.Isolate isolate = _isolate as S.Isolate;
+    dynamic sourceReport =
+        await isolate.getSourceReport(reports, script, _startPos, _endPos);
+    _possibleBreakpointLines =
+        S.getPossibleBreakpointLines(sourceReport, script);
+    _rangeMap.clear();
+    _callSites.clear();
+    _profileMap.clear();
+    for (var range in sourceReport['ranges']) {
+      int startLine = _loadedScript.tokenToLine(range['startPos']);
+      int endLine = _loadedScript.tokenToLine(range['endPos']);
+      // TODO(turnidge): Track down the root cause of null startLine/endLine.
+      if ((startLine != null) && (endLine != null)) {
+        for (var line = startLine; line <= endLine; line++) {
+          var rangeList = _rangeMap[line];
+          if (rangeList == null) {
+            _rangeMap[line] = [range];
+          } else {
+            rangeList.add(range);
+          }
+        }
+      }
+      if (_includeProfile && range['profile'] != null) {
+        List positions = range['profile']['positions'];
+        List exclusiveTicks = range['profile']['exclusiveTicks'];
+        List inclusiveTicks = range['profile']['inclusiveTicks'];
+        int sampleCount = range['profile']['metadata']['sampleCount'];
+        assert(positions.length == exclusiveTicks.length);
+        assert(positions.length == inclusiveTicks.length);
+        for (int i = 0; i < positions.length; i++) {
+          if (positions[i] is String) {
+            // String positions are classifying token positions.
+            // TODO(johnmccutchan): Add classifier data to UI.
+            continue;
+          }
+          int line = _loadedScript.tokenToLine(positions[i]);
+          ScriptLineProfile lineProfile = _profileMap[line];
+          if (lineProfile == null) {
+            lineProfile = new ScriptLineProfile(line, sampleCount);
+            _profileMap[line] = lineProfile;
+          }
+          lineProfile.process(exclusiveTicks[i], inclusiveTicks[i]);
+        }
+      }
+      if (range['compiled']) {
+        var rangeCallSites = range['callSites'];
+        if (rangeCallSites != null) {
+          for (var callSiteMap in rangeCallSites) {
+            _callSites.add(new S.CallSite.fromMap(callSiteMap, script));
+          }
+        }
+      }
+    }
+  }
+
+  Future _computeAnnotations() async {
+    if (noSource) return;
+
+    _startLine = (_startPos != null
+        ? _loadedScript.tokenToLine(_startPos)
+        : 1 + _loadedScript.lineOffset);
+    _currentLine =
+        (_currentPos != null ? _loadedScript.tokenToLine(_currentPos) : null);
+    _currentCol =
+        (_currentPos != null ? (_loadedScript.tokenToCol(_currentPos)) : null);
+    if (_currentCol != null) {
+      _currentCol--; // make this 0-based.
+    }
+
+    S.Script script = _loadedScript as S.Script;
+
+    _endLine = (_endPos != null
+        ? _loadedScript.tokenToLine(_endPos)
+        : script.lines.length + _loadedScript.lineOffset);
+
+    if (_startLine == null || _endLine == null) {
+      return;
+    }
+
+    _annotations.clear();
+
+    addCurrentExecutionAnnotation();
+    addBreakpointAnnotations();
+
+    if (!_inDebuggerContext && script.library != null) {
+      await loadDeclarationsOfLibrary(script.library);
+      addLibraryAnnotations();
+      addDependencyAnnotations();
+      addPartAnnotations();
+      addClassAnnotations();
+      addFieldAnnotations();
+      addFunctionAnnotations();
+      addCallSiteAnnotations();
+    }
+
+    addLocalVariableAnnotations();
+
+    _annotations.sort();
+  }
+
+  void addCurrentExecutionAnnotation() {
+    if (_currentLine != null) {
+      var a = new CurrentExecutionAnnotation(_isolate, _objects, _r.queue);
+      a.line = _currentLine;
+      a.columnStart = _currentCol;
+      S.Script script = _loadedScript as S.Script;
+      var length = script.guessTokenLength(_currentLine, _currentCol);
+      if (length == null) {
+        length = 1;
+      }
+      a.columnStop = _currentCol + length;
+      _annotations.add(a);
+    }
+  }
+
+  void addBreakpointAnnotations() {
+    S.Script script = _loadedScript as S.Script;
+    for (var line = _startLine; line <= _endLine; line++) {
+      var bpts = script.getLine(line).breakpoints;
+      if (bpts != null) {
+        for (var bpt in bpts) {
+          if (bpt.location != null) {
+            _annotations.add(
+                new BreakpointAnnotation(_isolate, _objects, _r.queue, bpt));
+          }
+        }
+      }
+    }
+  }
+
+  Future loadDeclarationsOfLibrary(S.Library lib) {
+    return lib.load().then((serviceObject) {
+      S.Library lib = serviceObject;
+      var loads = <Future>[];
+      for (var func in lib.functions) {
+        loads.add(func.load());
+      }
+      for (var field in lib.variables) {
+        loads.add(field.load());
+      }
+      for (var cls in lib.classes) {
+        loads.add(loadDeclarationsOfClass(cls));
+      }
+      return Future.wait(loads);
+    });
+  }
+
+  Future loadDeclarationsOfClass(S.Class cls) {
+    return cls.load().then((serviceObject) {
+      S.Class cls = serviceObject;
+      var loads = <Future>[];
+      for (var func in cls.functions) {
+        loads.add(func.load());
+      }
+      for (var field in cls.fields) {
+        loads.add(field.load());
+      }
+      return Future.wait(loads);
+    });
+  }
+
+  void addLibraryAnnotations() {
+    S.Script script = _loadedScript as S.Script;
+    for (S.ScriptLine line in script.lines) {
+      // TODO(rmacnak): Use a real scanner.
+      var pattern = new RegExp("library ${script.library.name}");
+      var match = pattern.firstMatch(line.text);
+      if (match != null) {
+        var anno = new LibraryAnnotation(
+            _isolate,
+            _objects,
+            _r.queue,
+            _loadedScript.library,
+            Uris.inspect(isolate, object: _loadedScript.library));
+        anno.line = line.line;
+        anno.columnStart = match.start + 8;
+        anno.columnStop = match.end;
+        _annotations.add(anno);
+      }
+      // TODO(rmacnak): Use a real scanner.
+      pattern = new RegExp("part of ${script.library.name}");
+      match = pattern.firstMatch(line.text);
+      if (match != null) {
+        var anno = new LibraryAnnotation(
+            _isolate,
+            _objects,
+            _r.queue,
+            _loadedScript.library,
+            Uris.inspect(isolate, object: _loadedScript.library));
+        anno.line = line.line;
+        anno.columnStart = match.start + 8;
+        anno.columnStop = match.end;
+        _annotations.add(anno);
+      }
+    }
+  }
+
+  M.Library resolveDependency(String relativeUri) {
+    S.Script script = _loadedScript as S.Script;
+    // This isn't really correct: we need to ask the embedder to do the
+    // uri canonicalization for us, but Observatory isn't in a position
+    // to invoke the library tag handler. Handle the most common cases.
+    var targetUri = Uri.parse(_loadedScript.library.uri).resolve(relativeUri);
+    for (M.Library l in script.isolate.libraries) {
+      if (targetUri.toString() == l.uri) {
+        return l;
+      }
+    }
+    if (targetUri.scheme == 'package') {
+      var targetUriString = "packages/${targetUri.path}";
+      for (M.Library l in script.isolate.libraries) {
+        if (targetUriString == l.uri) {
+          return l;
+        }
+      }
+    }
+
+    print("Could not resolve library dependency: $relativeUri");
+    return null;
+  }
+
+  void addDependencyAnnotations() {
+    S.Script script = _loadedScript as S.Script;
+    // TODO(rmacnak): Use a real scanner.
+    var patterns = [
+      new RegExp("import '(.*)'"),
+      new RegExp('import "(.*)"'),
+      new RegExp("export '(.*)'"),
+      new RegExp('export "(.*)"'),
+    ];
+    for (S.ScriptLine line in script.lines) {
+      for (var pattern in patterns) {
+        var match = pattern.firstMatch(line.text);
+        if (match != null) {
+          M.Library target = resolveDependency(match[1]);
+          if (target != null) {
+            var anno = new LibraryAnnotation(_isolate, _objects, _r.queue,
+                target, Uris.inspect(isolate, object: target));
+            anno.line = line.line;
+            anno.columnStart = match.start + 8;
+            anno.columnStop = match.end - 1;
+            _annotations.add(anno);
+          }
+        }
+      }
+    }
+  }
+
+  M.Script resolvePart(String relativeUri) {
+    S.Script script = _loadedScript as S.Script;
+    var rootUri = Uri.parse(script.library.uri);
+    if (rootUri.scheme == 'dart') {
+      // The relative paths from dart:* libraries to their parts are not valid.
+      rootUri = Uri.parse(script.library.uri + '/');
+    }
+    var targetUri = rootUri.resolve(relativeUri);
+    for (M.Script s in script.library.scripts) {
+      if (targetUri.toString() == s.uri) {
+        return s;
+      }
+    }
+    print("Could not resolve part: $relativeUri");
+    return null;
+  }
+
+  void addPartAnnotations() {
+    S.Script script = _loadedScript as S.Script;
+    // TODO(rmacnak): Use a real scanner.
+    var patterns = [
+      new RegExp("part '(.*)'"),
+      new RegExp('part "(.*)"'),
+    ];
+    for (S.ScriptLine line in script.lines) {
+      for (var pattern in patterns) {
+        var match = pattern.firstMatch(line.text);
+        if (match != null) {
+          S.Script part = resolvePart(match[1]);
+          if (part != null) {
+            var anno = new PartAnnotation(_isolate, _objects, _r.queue, part,
+                Uris.inspect(isolate, object: part));
+            anno.line = line.line;
+            anno.columnStart = match.start + 6;
+            anno.columnStop = match.end - 1;
+            _annotations.add(anno);
+          }
+        }
+      }
+    }
+  }
+
+  void addClassAnnotations() {
+    S.Script script = _loadedScript as S.Script;
+    for (var cls in script.library.classes) {
+      if ((cls.location != null) && (cls.location.script == script)) {
+        var a = new ClassDeclarationAnnotation(_isolate, _objects, _r.queue,
+            cls, Uris.inspect(isolate, object: cls));
+        _annotations.add(a);
+      }
+    }
+  }
+
+  void addFieldAnnotations() {
+    S.Script script = _loadedScript as S.Script;
+    for (var field in script.library.variables) {
+      if ((field.location != null) && (field.location.script == script)) {
+        var a = new FieldDeclarationAnnotation(_isolate, _objects, _r.queue,
+            field, Uris.inspect(isolate, object: field));
+        _annotations.add(a);
+      }
+    }
+    for (var cls in script.library.classes) {
+      for (var field in cls.fields) {
+        if ((field.location != null) && (field.location.script == script)) {
+          var a = new FieldDeclarationAnnotation(_isolate, _objects, _r.queue,
+              field, Uris.inspect(isolate, object: field));
+          _annotations.add(a);
+        }
+      }
+    }
+  }
+
+  void addFunctionAnnotations() {
+    S.Script script = _loadedScript as S.Script;
+    for (var func in script.library.functions) {
+      if ((func.location != null) &&
+          (func.location.script == script) &&
+          (func.kind != M.FunctionKind.implicitGetter) &&
+          (func.kind != M.FunctionKind.implicitSetter)) {
+        // We annotate a field declaration with the field instead of the
+        // implicit getter or setter.
+        var a = new FunctionDeclarationAnnotation(_isolate, _objects, _r.queue,
+            func, Uris.inspect(isolate, object: func));
+        _annotations.add(a);
+      }
+    }
+    for (var cls in script.library.classes) {
+      S.Script script = _loadedScript as S.Script;
+      for (var func in cls.functions) {
+        if ((func.location != null) &&
+            (func.location.script == script) &&
+            (func.kind != M.FunctionKind.implicitGetter) &&
+            (func.kind != M.FunctionKind.implicitSetter)) {
+          // We annotate a field declaration with the field instead of the
+          // implicit getter or setter.
+          var a = new FunctionDeclarationAnnotation(_isolate, _objects,
+              _r.queue, func, Uris.inspect(isolate, object: func));
+          _annotations.add(a);
+        }
+      }
+    }
+  }
+
+  void addCallSiteAnnotations() {
+    for (var callSite in _callSites) {
+      _annotations
+          .add(new CallSiteAnnotation(_isolate, _objects, _r.queue, callSite));
+    }
+  }
+
+  void addLocalVariableAnnotations() {
+    S.Script script = _loadedScript as S.Script;
+    // We have local variable information.
+    if (_variables != null) {
+      // For each variable.
+      for (var variable in _variables) {
+        // Find variable usage locations.
+        var locations = script.scanForLocalVariableLocations(
+            variable['name'], variable['_tokenPos'], variable['_endTokenPos']);
+
+        // Annotate locations.
+        for (var location in locations) {
+          _annotations.add(new LocalVariableAnnotation(
+              _isolate, _objects, _r.queue, location, variable['value']));
+        }
+      }
+    }
+  }
+
+  ButtonElement _newRefreshButton() {
+    var button = new ButtonElement();
+    button.classes = ['refresh'];
+    button.onClick.listen((_) async {
+      button.disabled = true;
+      await _refresh();
+      button.disabled = false;
+    });
+    button.title = 'Refresh coverage';
+    button.children = <Element>[_iconRefresh.clone(true)];
+    return button;
+  }
+
+  ButtonElement _newToggleProfileButton() {
+    ButtonElement button = new ButtonElement();
+    button.classes =
+        _includeProfile ? ['toggle-profile', 'enabled'] : ['toggle-profile'];
+    button.title = 'Toggle CPU profile information';
+    button.onClick.listen((_) async {
+      _includeProfile = !_includeProfile;
+      button.classes.toggle('enabled');
+      button.disabled = true;
+      _refresh();
+      button.disabled = false;
+    });
+    button.children = <Element>[_iconWhatsHot.clone(true)];
+    return button;
+  }
+
+  Element linesTable() {
+    S.Script script = _loadedScript as S.Script;
+    var table = new DivElement();
+    table.classes.add("sourceTable");
+
+    _refreshButton = _newRefreshButton();
+    _toggleProfileButton = _newToggleProfileButton();
+    table.append(_refreshButton);
+    table.append(_toggleProfileButton);
+
+    if (_startLine == null || _endLine == null) {
+      return table;
+    }
+
+    var endLine = (_endPos != null
+        ? _loadedScript.tokenToLine(_endPos)
+        : script.lines.length + _loadedScript.lineOffset);
+    var lineNumPad = endLine.toString().length;
+
+    _annotationsCursor = 0;
+
+    int blankLineCount = 0;
+    for (int i = _startLine; i <= _endLine; i++) {
+      var line = script.getLine(i);
+      if (line.isBlank) {
+        // Try to introduce ellipses if there are 4 or more contiguous
+        // blank lines.
+        blankLineCount++;
+      } else {
+        if (blankLineCount > 0) {
+          int firstBlank = i - blankLineCount;
+          int lastBlank = i - 1;
+          if (blankLineCount < 4) {
+            // Too few blank lines for an ellipsis.
+            for (int j = firstBlank; j <= lastBlank; j++) {
+              table.append(lineElement(script.getLine(j), lineNumPad));
+            }
+          } else {
+            // Add an ellipsis for the skipped region.
+            table.append(lineElement(script.getLine(firstBlank), lineNumPad));
+            table.append(lineElement(null, lineNumPad));
+            table.append(lineElement(script.getLine(lastBlank), lineNumPad));
+          }
+          blankLineCount = 0;
+        }
+        table.append(lineElement(line, lineNumPad));
+      }
+    }
+
+    return table;
+  }
+
+  // Assumes annotations are sorted.
+  Annotation nextAnnotationOnLine(int line) {
+    if (_annotationsCursor >= _annotations.length) return null;
+    var annotation = _annotations[_annotationsCursor];
+
+    // Fast-forward past any annotations before the first line that
+    // we are displaying.
+    while (annotation.line < line) {
+      _annotationsCursor++;
+      if (_annotationsCursor >= _annotations.length) return null;
+      annotation = _annotations[_annotationsCursor];
+    }
+
+    // Next annotation is for a later line, don't advance past it.
+    if (annotation.line != line) return null;
+    _annotationsCursor++;
+    return annotation;
+  }
+
+  Element lineElement(S.ScriptLine line, int lineNumPad) {
+    var e = new DivElement();
+    e.classes.add("sourceRow");
+    e.append(lineBreakpointElement(line));
+    e.append(lineNumberElement(line, lineNumPad));
+    if (_includeProfile) {
+      e.append(lineProfileElement(line, false));
+      e.append(lineProfileElement(line, true));
+    }
+    e.append(lineSourceElement(line));
+    return e;
+  }
+
+  Element lineProfileElement(S.ScriptLine line, bool self) {
+    var e = span('');
+    e.classes.add('noCopy');
+    if (self) {
+      e.title = 'Self %';
+    } else {
+      e.title = 'Total %';
+    }
+
+    if (line == null) {
+      e.classes.add('notSourceProfile');
+      e.text = nbsp;
+      return e;
+    }
+
+    var ranges = _rangeMap[line.line];
+    if ((ranges == null) || ranges.isEmpty) {
+      e.classes.add('notSourceProfile');
+      e.text = nbsp;
+      return e;
+    }
+
+    ScriptLineProfile lineProfile = _profileMap[line.line];
+    if (lineProfile == null) {
+      e.classes.add('noProfile');
+      e.text = nbsp;
+      return e;
+    }
+
+    if (self) {
+      e.text = lineProfile.formattedSelfTicks;
+    } else {
+      e.text = lineProfile.formattedTotalTicks;
+    }
+
+    if (lineProfile.isHot(self)) {
+      e.classes.add('hotProfile');
+    } else if (lineProfile.isMedium(self)) {
+      e.classes.add('mediumProfile');
+    } else {
+      e.classes.add('coldProfile');
+    }
+
+    return e;
+  }
+
+  Element lineBreakpointElement(S.ScriptLine line) {
+    var e = new DivElement();
+    if (line == null || !_possibleBreakpointLines.contains(line.line)) {
+      e.classes.add('noCopy');
+      e.classes.add("emptyBreakpoint");
+      e.text = nbsp;
+      return e;
+    }
+
+    e.text = 'B';
+    var busy = false;
+    void update() {
+      e.classes.clear();
+      e.classes.add('noCopy');
+      if (busy) {
+        e.classes.add("busyBreakpoint");
+      } else if (line.breakpoints != null) {
+        bool resolved = false;
+        for (var bpt in line.breakpoints) {
+          if (bpt.resolved) {
+            resolved = true;
+            break;
+          }
+        }
+        if (resolved) {
+          e.classes.add("resolvedBreakpoint");
+        } else {
+          e.classes.add("unresolvedBreakpoint");
+        }
+      } else {
+        e.classes.add("possibleBreakpoint");
+      }
+    }
+
+    e.onClick.listen((event) {
+      if (busy) {
+        return;
+      }
+      busy = true;
+      if (line.breakpoints == null) {
+        // No breakpoint.  Add it.
+        line.script.isolate
+            .addBreakpoint(line.script, line.line)
+            .catchError((e, st) {
+          if (e is! S.ServerRpcException ||
+              (e as S.ServerRpcException).code !=
+                  S.ServerRpcException.kCannotAddBreakpoint) {
+            ObservatoryApplication.app.handleException(e, st);
+          }
+        }).whenComplete(() {
+          busy = false;
+          update();
+        });
+      } else {
+        // Existing breakpoint.  Remove it.
+        List<Future> pending = [];
+        for (var bpt in line.breakpoints) {
+          pending.add(line.script.isolate.removeBreakpoint(bpt));
+        }
+        Future.wait(pending).then((_) {
+          busy = false;
+          update();
+        });
+      }
+      update();
+    });
+    update();
+    return e;
+  }
+
+  Element lineNumberElement(S.ScriptLine line, int lineNumPad) {
+    var lineNumber = line == null ? "..." : line.line;
+    var e =
+        span("$nbsp${lineNumber.toString().padLeft(lineNumPad, nbsp)}$nbsp");
+    e.classes.add('noCopy');
+    if (lineNumber == _currentLine) {
+      hitsCurrent(e);
+      return e;
+    }
+    var ranges = _rangeMap[lineNumber];
+    if ((ranges == null) || ranges.isEmpty) {
+      // This line is not code.
+      hitsUnknown(e);
+      return e;
+    }
+    bool compiled = true;
+    bool hasCallInfo = false;
+    bool executed = false;
+    for (var range in ranges) {
+      if (range['compiled']) {
+        for (var callSite in range['callSites']) {
+          var callLine = line.script.tokenToLine(callSite['tokenPos']);
+          if (lineNumber == callLine) {
+            // The call site is on the current line.
+            hasCallInfo = true;
+            for (var cacheEntry in callSite['cacheEntries']) {
+              if (cacheEntry['count'] > 0) {
+                // If any call site on the line has been executed, we
+                // mark the line as executed.
+                executed = true;
+                break;
+              }
+            }
+          }
+        }
+      } else {
+        // If any range isn't compiled, show the line as not compiled.
+        // This is necessary so that nested functions appear to be uncompiled.
+        compiled = false;
+      }
+    }
+    if (executed) {
+      hitsExecuted(e);
+    } else if (hasCallInfo) {
+      hitsNotExecuted(e);
+    } else if (compiled) {
+      hitsCompiled(e);
+    } else {
+      hitsNotCompiled(e);
+    }
+    return e;
+  }
+
+  Element lineSourceElement(S.ScriptLine line) {
+    var e = new DivElement();
+    e.classes.add("sourceItem");
+
+    if (line != null) {
+      e.classes.add(makeLineClass(line.line));
+      if (line.line == _currentLine) {
+        e.classes.add("currentLine");
+      }
+
+      var position = 0;
+      consumeUntil(var stop) {
+        if (stop <= position) {
+          return null; // Empty gap between annotations/boundries.
+        }
+        if (stop > line.text.length) {
+          // Approximated token length can run past the end of the line.
+          stop = line.text.length;
+        }
+
+        var chunk = line.text.substring(position, stop);
+        var chunkNode = span(chunk);
+        e.append(chunkNode);
+        position = stop;
+        return chunkNode;
+      }
+
+      // TODO(rmacnak): Tolerate overlapping annotations.
+      var annotation;
+      while ((annotation = nextAnnotationOnLine(line.line)) != null) {
+        consumeUntil(annotation.columnStart);
+        annotation.applyStyleTo(consumeUntil(annotation.columnStop));
+      }
+      consumeUntil(line.text.length);
+    }
+
+    // So blank lines are included when copying script to the clipboard.
+    e.append(span('\n'));
+
+    return e;
+  }
+
+  /// Exclude nodes from being copied, for example the line numbers and
+  /// breakpoint toggles in script insets. Must be called after [root]'s
+  /// children have been added, and only supports one node at a time.
+  static void _makeCssClassUncopyable(Element root, String className) {
+    var noCopyNodes = root.getElementsByClassName(className);
+    for (HtmlElement node in noCopyNodes) {
+      node.style.setProperty('-moz-user-select', 'none');
+      node.style.setProperty('-khtml-user-select', 'none');
+      node.style.setProperty('-webkit-user-select', 'none');
+      node.style.setProperty('-ms-user-select', 'none');
+      node.style.setProperty('user-select', 'none');
+    }
+    root.onCopy.listen((event) {
+      // Mark the nodes as hidden before the copy happens, then mark them as
+      // visible on the next event loop turn.
+      for (HtmlElement node in noCopyNodes) {
+        node.style.visibility = 'hidden';
+      }
+      Timer.run(() {
+        for (HtmlElement node in noCopyNodes) {
+          node.style.visibility = 'visible';
+        }
+      });
+    });
+  }
+}
+
+const nbsp = "\u00A0";
+
+void addInfoBox(Element content, Function infoBoxGenerator) {
+  var infoBox;
+  var show = false;
+  var originalBackground = content.style.backgroundColor;
+  buildInfoBox() {
+    infoBox = infoBoxGenerator();
+    infoBox.style.position = 'absolute';
+    infoBox.style.padding = '1em';
+    infoBox.style.border = 'solid black 2px';
+    infoBox.style.zIndex = '10';
+    infoBox.style.backgroundColor = 'white';
+    infoBox.style.cursor = 'auto';
+    // Don't inherit pre formatting from the script lines.
+    infoBox.style.whiteSpace = 'normal';
+    content.append(infoBox);
+  }
+
+  content.onClick.listen((event) {
+    show = !show;
+    if (infoBox == null) buildInfoBox(); // Created lazily on the first click.
+    infoBox.style.display = show ? 'block' : 'none';
+    content.style.backgroundColor = show ? 'white' : originalBackground;
+  });
+
+  // Causes infoBox to be positioned relative to the bottom-left of content.
+  content.style.display = 'inline-block';
+  content.style.cursor = 'pointer';
+}
+
+void addLink(Element content, String target) {
+  // Ick, destructive but still compatible with also adding an info box.
+  var a = new AnchorElement(href: target);
+  a.text = content.text;
+  content.text = '';
+  content.append(a);
+}
+
+abstract class Annotation implements Comparable<Annotation> {
+  M.IsolateRef _isolate;
+  M.ObjectRepository _objects;
+  RenderingQueue queue;
+  int line;
+  int columnStart;
+  int columnStop;
+  int get priority;
+
+  Annotation(this._isolate, this._objects, this.queue);
+
+  void applyStyleTo(element);
+
+  int compareTo(Annotation other) {
+    if (line == other.line) {
+      if (columnStart == other.columnStart) {
+        return priority.compareTo(other.priority);
+      }
+      return columnStart.compareTo(other.columnStart);
+    }
+    return line.compareTo(other.line);
+  }
+
+  Element table() {
+    var e = new DivElement();
+    e.style.display = "table";
+    e.style.color = "#333";
+    e.style.font = "400 14px 'Montserrat', sans-serif";
+    return e;
+  }
+
+  Element row([content]) {
+    var e = new DivElement();
+    e.style.display = "table-row";
+    if (content is String) e.text = content;
+    if (content is Element) e.children.add(content);
+    return e;
+  }
+
+  Element cell(content) {
+    var e = new DivElement();
+    e.style.display = "table-cell";
+    e.style.padding = "3px";
+    if (content is String) e.text = content;
+    if (content is Element) e.children.add(content);
+    return e;
+  }
+
+  Element serviceRef(object) {
+    return anyRef(_isolate, object, _objects, queue: queue);
+  }
+}
+
+class CurrentExecutionAnnotation extends Annotation {
+  int priority = 0; // highest priority.
+
+  CurrentExecutionAnnotation(
+      M.IsolateRef isolate, M.ObjectRepository objects, RenderingQueue queue)
+      : super(isolate, objects, queue);
+
+  void applyStyleTo(element) {
+    if (element == null) {
+      return; // TODO(rmacnak): Handling overlapping annotations.
+    }
+    element.classes.add("currentCol");
+    element.title = "Current execution";
+  }
+}
+
+class BreakpointAnnotation extends Annotation {
+  M.Breakpoint bpt;
+  int priority = 1;
+
+  BreakpointAnnotation(M.IsolateRef isolate, M.ObjectRepository objects,
+      RenderingQueue queue, this.bpt)
+      : super(isolate, objects, queue) {
+    S.Script script = bpt.location.script;
+    var location = bpt.location;
+    if (location.tokenPos != null) {
+      var pos = location.tokenPos;
+      line = script.tokenToLine(pos);
+      columnStart = script.tokenToCol(pos) - 1; // tokenToCol is 1-origin.
+    } else if (location is M.UnresolvedSourceLocation) {
+      line = location.line;
+      columnStart = location.column;
+      if (columnStart == null) {
+        columnStart = 0;
+      }
+    }
+    var length = script.guessTokenLength(line, columnStart);
+    if (length == null) {
+      length = 1;
+    }
+    columnStop = columnStart + length;
+  }
+
+  void applyStyleTo(element) {
+    if (element == null) {
+      return; // TODO(rmacnak): Handling overlapping annotations.
+    }
+    S.Script script = bpt.location.script;
+    var pos = bpt.location.tokenPos;
+    int line = script.tokenToLine(pos);
+    int column = script.tokenToCol(pos);
+    if (bpt.resolved) {
+      element.classes.add("resolvedBreakAnnotation");
+    } else {
+      element.classes.add("unresolvedBreakAnnotation");
+    }
+    element.title = "Breakpoint ${bpt.number} at ${line}:${column}";
+  }
+}
+
+class LibraryAnnotation extends Annotation {
+  S.Library target;
+  String url;
+  int priority = 2;
+
+  LibraryAnnotation(M.IsolateRef isolate, M.ObjectRepository objects,
+      RenderingQueue queue, this.target, this.url)
+      : super(isolate, objects, queue);
+
+  void applyStyleTo(element) {
+    if (element == null) {
+      return; // TODO(rmacnak): Handling overlapping annotations.
+    }
+    element.title = "library ${target.uri}";
+    addLink(element, url);
+  }
+}
+
+class PartAnnotation extends Annotation {
+  S.Script part;
+  String url;
+  int priority = 2;
+
+  PartAnnotation(M.IsolateRef isolate, M.ObjectRepository objects,
+      RenderingQueue queue, this.part, this.url)
+      : super(isolate, objects, queue);
+
+  void applyStyleTo(element) {
+    if (element == null) {
+      return; // TODO(rmacnak): Handling overlapping annotations.
+    }
+    element.title = "script ${part.uri}";
+    addLink(element, url);
+  }
+}
+
+class LocalVariableAnnotation extends Annotation {
+  final value;
+  int priority = 2;
+
+  LocalVariableAnnotation(M.IsolateRef isolate, M.ObjectRepository objects,
+      RenderingQueue queue, S.LocalVarLocation location, this.value)
+      : super(isolate, objects, queue) {
+    line = location.line;
+    columnStart = location.column;
+    columnStop = location.endColumn;
+  }
+
+  void applyStyleTo(element) {
+    if (element == null) {
+      return; // TODO(rmacnak): Handling overlapping annotations.
+    }
+    element.style.fontWeight = "bold";
+    element.title = "${value.shortName}";
+  }
+}
+
+class CallSiteAnnotation extends Annotation {
+  S.CallSite callSite;
+  int priority = 2;
+
+  CallSiteAnnotation(M.IsolateRef isolate, M.ObjectRepository objects,
+      RenderingQueue queue, this.callSite)
+      : super(isolate, objects, queue) {
+    line = callSite.line;
+    columnStart = callSite.column - 1; // Call site is 1-origin.
+    var tokenLength = callSite.script.guessTokenLength(line, columnStart);
+    if (tokenLength == null) {
+      tokenLength = callSite.name.length; // Approximate.
+      if (callSite.name.startsWith("get:") || callSite.name.startsWith("set:"))
+        tokenLength -= 4;
+    }
+    columnStop = columnStart + tokenLength;
+  }
+
+  void applyStyleTo(element) {
+    if (element == null) {
+      return; // TODO(rmacnak): Handling overlapping annotations.
+    }
+    element.style.fontWeight = "bold";
+    element.title = "Call site: ${callSite.name}";
+
+    addInfoBox(element, () {
+      var details = table();
+      if (callSite.entries.isEmpty) {
+        details.append(row('Call of "${callSite.name}" did not execute'));
+      } else {
+        var r = row();
+        r.append(cell("Container"));
+        r.append(cell("Count"));
+        r.append(cell("Target"));
+        details.append(r);
+
+        for (var entry in callSite.entries) {
+          var r = row();
+          if (entry.receiver == null) {
+            r.append(cell(""));
+          } else {
+            r.append(cell(serviceRef(entry.receiver)));
+          }
+          r.append(cell(entry.count.toString()));
+          r.append(cell(serviceRef(entry.target)));
+          details.append(r);
+        }
+      }
+      return details;
+    });
+  }
+}
+
+abstract class DeclarationAnnotation extends Annotation {
+  String url;
+  int priority = 2;
+
+  DeclarationAnnotation(M.IsolateRef isolate, M.ObjectRepository objects,
+      RenderingQueue queue, decl, this.url)
+      : super(isolate, objects, queue) {
+    assert(decl.loaded);
+    S.SourceLocation location = decl.location;
+    if (location == null) {
+      line = 0;
+      columnStart = 0;
+      columnStop = 0;
+      return;
+    }
+
+    S.Script script = location.script;
+    line = script.tokenToLine(location.tokenPos);
+    columnStart = script.tokenToCol(location.tokenPos);
+    if ((line == null) || (columnStart == null)) {
+      line = 0;
+      columnStart = 0;
+      columnStop = 0;
+    } else {
+      columnStart--; // 1-origin -> 0-origin.
+
+      // The method's token position is at the beginning of the method
+      // declaration, which may be a return type annotation, metadata, static
+      // modifier, etc. Try to scan forward to position this annotation on the
+      // function's name instead.
+      var lineSource = script.getLine(line).text;
+      var betterStart = lineSource.indexOf(decl.name, columnStart);
+      if (betterStart != -1) {
+        columnStart = betterStart;
+      }
+      columnStop = columnStart + decl.name.length;
+    }
+  }
+}
+
+class ClassDeclarationAnnotation extends DeclarationAnnotation {
+  S.Class klass;
+
+  ClassDeclarationAnnotation(M.IsolateRef isolate, M.ObjectRepository objects,
+      RenderingQueue queue, S.Class cls, String url)
+      : klass = cls,
+        super(isolate, objects, queue, cls, url);
+
+  void applyStyleTo(element) {
+    if (element == null) {
+      return; // TODO(rmacnak): Handling overlapping annotations.
+    }
+    element.title = "class ${klass.name}";
+    addLink(element, url);
+  }
+}
+
+class FieldDeclarationAnnotation extends DeclarationAnnotation {
+  S.Field field;
+
+  FieldDeclarationAnnotation(M.IsolateRef isolate, M.ObjectRepository objects,
+      RenderingQueue queue, S.Field fld, String url)
+      : field = fld,
+        super(isolate, objects, queue, fld, url);
+
+  void applyStyleTo(element) {
+    if (element == null) {
+      return; // TODO(rmacnak): Handling overlapping annotations.
+    }
+    var tooltip = "field ${field.name}";
+    element.title = tooltip;
+    addLink(element, url);
+  }
+}
+
+class FunctionDeclarationAnnotation extends DeclarationAnnotation {
+  S.ServiceFunction function;
+
+  FunctionDeclarationAnnotation(
+      M.IsolateRef isolate,
+      M.ObjectRepository objects,
+      RenderingQueue queue,
+      S.ServiceFunction func,
+      String url)
+      : function = func,
+        super(isolate, objects, queue, func, url);
+
+  void applyStyleTo(element) {
+    if (element == null) {
+      return; // TODO(rmacnak): Handling overlapping annotations.
+    }
+    var tooltip = "method ${function.name}";
+    if (function.isOptimizable == false) {
+      tooltip += "\nUnoptimizable!";
+    }
+    if (function.isInlinable == false) {
+      tooltip += "\nNot inlinable!";
+    }
+    if (function.deoptimizations > 0) {
+      tooltip += "\nDeoptimized ${function.deoptimizations} times!";
+    }
+    element.title = tooltip;
+
+    if (function.isOptimizable == false ||
+        function.isInlinable == false ||
+        function.deoptimizations > 0) {
+      element.style.backgroundColor = "#EEA7A7"; // Low-saturation red.
+    }
+
+    addLink(element, url);
+  }
+}
+
+class ScriptLineProfile {
+  ScriptLineProfile(this.line, this.sampleCount);
+
+  static const kHotThreshold = 0.05; // 5%.
+  static const kMediumThreshold = 0.02; // 2%.
+
+  final int line;
+  final int sampleCount;
+
+  int selfTicks = 0;
+  int totalTicks = 0;
+
+  void process(int exclusive, int inclusive) {
+    selfTicks += exclusive;
+    totalTicks += inclusive;
+  }
+
+  String get formattedSelfTicks {
+    return Utils.formatPercent(selfTicks, sampleCount);
+  }
+
+  String get formattedTotalTicks {
+    return Utils.formatPercent(totalTicks, sampleCount);
+  }
+
+  double _percent(bool self) {
+    if (sampleCount == 0) {
+      return 0.0;
+    }
+    if (self) {
+      return selfTicks / sampleCount;
+    } else {
+      return totalTicks / sampleCount;
+    }
+  }
+
+  bool isHot(bool self) => _percent(self) > kHotThreshold;
+  bool isMedium(bool self) => _percent(self) > kMediumThreshold;
+}
+
+final SvgSvgElement _iconRefresh = new SvgSvgElement()
+  ..setAttribute('width', '24')
+  ..setAttribute('height', '24')
+  ..children = <Element>[
+    new PathElement()
+      ..setAttribute(
+          'd',
+          'M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-7.99 '
+              '3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 '
+              '7.73-6h-2.08c-.82 2.33-3.04 4-5.65 4-3.31 '
+              '0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 '
+              '1.78L13 11h7V4l-2.35 2.35z')
+  ];
+
+final SvgSvgElement _iconWhatsHot = new SvgSvgElement()
+  ..setAttribute('width', '24')
+  ..setAttribute('height', '24')
+  ..children = <Element>[
+    new PathElement()
+      ..setAttribute(
+          'd',
+          'M13.5.67s.74 2.65.74 4.8c0 2.06-1.35 3.73-3.41 '
+              '3.73-2.07 0-3.63-1.67-3.63-3.73l.03-.36C5.21 7.51 '
+              '4 10.62 4 14c0 4.42 3.58 8 8 8s8-3.58 8-8C20 8.61 '
+              '17.41 3.8 13.5.67zM11.71 19c-1.78 '
+              '0-3.22-1.4-3.22-3.14 0-1.62 1.05-2.76 2.81-3.12 '
+              '1.77-.36 3.6-1.21 4.62-2.58.39 1.29.59 2.65.59 '
+              '4.04 0 2.65-2.15 4.8-4.8 4.8z')
+  ];
diff --git a/runtime/observatory_2/lib/src/elements/script_ref.dart b/runtime/observatory_2/lib/src/elements/script_ref.dart
new file mode 100644
index 0000000..02bf797
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/script_ref.dart
@@ -0,0 +1,63 @@
+// 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.
+
+library script_ref_element;
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/models.dart' as M show IsolateRef, ScriptRef;
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/helpers/uris.dart';
+
+class ScriptRefElement extends CustomElement implements Renderable {
+  RenderingScheduler<ScriptRefElement> _r;
+
+  Stream<RenderedEvent<ScriptRefElement>> get onRendered => _r.onRendered;
+
+  M.IsolateRef _isolate;
+  M.ScriptRef _script;
+
+  M.IsolateRef get isolate => _isolate;
+  M.ScriptRef get script => _script;
+
+  factory ScriptRefElement(M.IsolateRef isolate, M.ScriptRef script,
+      {RenderingQueue queue}) {
+    assert(isolate != null);
+    assert(script != null);
+    ScriptRefElement e = new ScriptRefElement.created();
+    e._r = new RenderingScheduler<ScriptRefElement>(e, queue: queue);
+    e._isolate = isolate;
+    e._script = script;
+    return e;
+  }
+
+  ScriptRefElement.created() : super.created('script-ref');
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    children = <Element>[];
+    _r.disable(notify: true);
+  }
+
+  void render() {
+    var displayUri = script.uri.split('/').last;
+    if (displayUri.isEmpty) {
+      displayUri = 'N/A';
+    }
+
+    children = <Element>[
+      new AnchorElement(href: Uris.inspect(isolate, object: script))
+        ..title = script.uri
+        ..text = displayUri
+    ];
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/script_view.dart b/runtime/observatory_2/lib/src/elements/script_view.dart
new file mode 100644
index 0000000..349aabc
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/script_view.dart
@@ -0,0 +1,156 @@
+// 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.
+
+library script_view;
+
+import 'dart:async';
+import 'dart:html';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/context_ref.dart';
+import 'package:observatory_2/src/elements/curly_block.dart';
+import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
+import 'package:observatory_2/src/elements/helpers/nav_menu.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/nav/isolate_menu.dart';
+import 'package:observatory_2/src/elements/nav/library_menu.dart';
+import 'package:observatory_2/src/elements/nav/notify.dart';
+import 'package:observatory_2/src/elements/nav/refresh.dart';
+import 'package:observatory_2/src/elements/nav/top_menu.dart';
+import 'package:observatory_2/src/elements/nav/vm_menu.dart';
+import 'package:observatory_2/src/elements/object_common.dart';
+import 'package:observatory_2/src/elements/script_inset.dart';
+import 'package:observatory_2/src/elements/view_footer.dart';
+
+class ScriptViewElement extends CustomElement implements Renderable {
+  RenderingScheduler<ScriptViewElement> _r;
+
+  Stream<RenderedEvent<ScriptViewElement>> get onRendered => _r.onRendered;
+
+  M.VM _vm;
+  M.IsolateRef _isolate;
+  M.EventRepository _events;
+  M.NotificationRepository _notifications;
+  M.Script _script;
+  M.ScriptRepository _scripts;
+  M.RetainedSizeRepository _retainedSizes;
+  M.ReachableSizeRepository _reachableSizes;
+  M.InboundReferencesRepository _references;
+  M.RetainingPathRepository _retainingPaths;
+  M.ObjectRepository _objects;
+  int _pos;
+
+  M.VMRef get vm => _vm;
+  M.IsolateRef get isolate => _isolate;
+  M.NotificationRepository get notifications => _notifications;
+  M.Script get script => _script;
+
+  factory ScriptViewElement(
+      M.VM vm,
+      M.IsolateRef isolate,
+      M.Script script,
+      M.EventRepository events,
+      M.NotificationRepository notifications,
+      M.ScriptRepository scripts,
+      M.RetainedSizeRepository retainedSizes,
+      M.ReachableSizeRepository reachableSizes,
+      M.InboundReferencesRepository references,
+      M.RetainingPathRepository retainingPaths,
+      M.ObjectRepository objects,
+      {int pos,
+      RenderingQueue queue}) {
+    assert(vm != null);
+    assert(isolate != null);
+    assert(events != null);
+    assert(notifications != null);
+    assert(script != null);
+    assert(scripts != null);
+    assert(retainedSizes != null);
+    assert(reachableSizes != null);
+    assert(references != null);
+    assert(retainingPaths != null);
+    assert(objects != null);
+    ScriptViewElement e = new ScriptViewElement.created();
+    e._r = new RenderingScheduler<ScriptViewElement>(e, queue: queue);
+    e._vm = vm;
+    e._isolate = isolate;
+    e._events = events;
+    e._notifications = notifications;
+    e._script = script;
+    e._scripts = scripts;
+    e._retainedSizes = retainedSizes;
+    e._reachableSizes = reachableSizes;
+    e._references = references;
+    e._retainingPaths = retainingPaths;
+    e._objects = objects;
+    e._pos = pos;
+    return e;
+  }
+
+  ScriptViewElement.created() : super.created('script-view');
+
+  @override
+  attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+  }
+
+  void render() {
+    children = <Element>[
+      navBar(<Element>[
+        new NavTopMenuElement(queue: _r.queue).element,
+        new NavVMMenuElement(_vm, _events, queue: _r.queue).element,
+        new NavIsolateMenuElement(_isolate, _events, queue: _r.queue).element,
+        new NavLibraryMenuElement(_isolate, _script.library, queue: _r.queue)
+            .element,
+        navMenu('object'),
+        (new NavRefreshElement(queue: _r.queue)
+              ..onRefresh.listen((e) async {
+                e.element.disabled = true;
+                _script = await _scripts.get(_isolate, _script.id);
+                _r.dirty();
+              }))
+            .element,
+        new NavNotifyElement(_notifications, queue: _r.queue).element
+      ]),
+      new DivElement()
+        ..classes = ['content-centered-big']
+        ..children = <Element>[
+          new HeadingElement.h2()..text = 'Script',
+          new HRElement(),
+          new ObjectCommonElement(_isolate, _script, _retainedSizes,
+                  _reachableSizes, _references, _retainingPaths, _objects,
+                  queue: _r.queue)
+              .element,
+          new BRElement(),
+          new DivElement()
+            ..classes = ['memberList']
+            ..children = <Element>[
+              new DivElement()
+                ..classes = ['memberItem']
+                ..children = <Element>[
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..text = 'load time',
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..text = '${_script.loadTime}'
+                ],
+            ],
+          new HRElement(),
+          new ScriptInsetElement(_isolate, _script, _scripts, _objects, _events,
+                  currentPos: _pos, queue: _r.queue)
+              .element,
+          new ViewFooterElement(queue: _r.queue).element
+        ]
+    ];
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/sentinel_value.dart b/runtime/observatory_2/lib/src/elements/sentinel_value.dart
new file mode 100644
index 0000000..c29c6c6
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/sentinel_value.dart
@@ -0,0 +1,69 @@
+// 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.
+
+import 'dart:async';
+
+import 'package:observatory_2/models.dart' as M show Sentinel, SentinelKind;
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+
+class SentinelValueElement extends CustomElement implements Renderable {
+  RenderingScheduler<SentinelValueElement> _r;
+
+  Stream<RenderedEvent<SentinelValueElement>> get onRendered => _r.onRendered;
+
+  M.Sentinel _sentinel;
+
+  M.Sentinel get sentinel => _sentinel;
+
+  factory SentinelValueElement(M.Sentinel sentinel, {RenderingQueue queue}) {
+    assert(sentinel != null);
+    SentinelValueElement e = new SentinelValueElement.created();
+    e._r = new RenderingScheduler<SentinelValueElement>(e, queue: queue);
+    e._sentinel = sentinel;
+    return e;
+  }
+
+  SentinelValueElement.created() : super.created('sentinel-value');
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    _r.disable(notify: true);
+    text = '';
+    title = '';
+  }
+
+  void render() {
+    text = _sentinel.valueAsString;
+    title = _sentinelKindToDescription(_sentinel.kind);
+  }
+
+  static String _sentinelKindToDescription(M.SentinelKind kind) {
+    switch (kind) {
+      case M.SentinelKind.collected:
+        return 'This object has been reclaimed by the garbage collector.';
+      case M.SentinelKind.expired:
+        return 'The handle to this object has expired. '
+            'Consider refreshing the page.';
+      case M.SentinelKind.notInitialized:
+        return 'This object will be initialized once it is accessed by '
+            'the program.';
+      case M.SentinelKind.initializing:
+        return 'This object is currently being initialized.';
+      case M.SentinelKind.optimizedOut:
+        return 'This object is no longer needed and has been removed by the '
+            'optimizing compiler.';
+      case M.SentinelKind.free:
+        return '';
+    }
+    throw new Exception('Unknown SentinelKind: $kind');
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/sentinel_view.dart b/runtime/observatory_2/lib/src/elements/sentinel_view.dart
new file mode 100644
index 0000000..5628e78
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/sentinel_view.dart
@@ -0,0 +1,111 @@
+// 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.
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
+import 'package:observatory_2/src/elements/helpers/nav_menu.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/nav/isolate_menu.dart';
+import 'package:observatory_2/src/elements/nav/notify.dart';
+import 'package:observatory_2/src/elements/nav/top_menu.dart';
+import 'package:observatory_2/src/elements/nav/vm_menu.dart';
+import 'package:observatory_2/src/elements/view_footer.dart';
+
+class SentinelViewElement extends CustomElement implements Renderable {
+  RenderingScheduler<SentinelViewElement> _r;
+
+  Stream<RenderedEvent<SentinelViewElement>> get onRendered => _r.onRendered;
+
+  M.VM _vm;
+  M.IsolateRef _isolate;
+  M.Sentinel _sentinel;
+  M.EventRepository _events;
+  M.NotificationRepository _notifications;
+
+  M.Sentinel get sentinel => _sentinel;
+
+  factory SentinelViewElement(
+      M.VM vm,
+      M.IsolateRef isolate,
+      M.Sentinel sentinel,
+      M.EventRepository events,
+      M.NotificationRepository notifications,
+      {RenderingQueue queue}) {
+    assert(vm != null);
+    assert(isolate != null);
+    assert(sentinel != null);
+    assert(events != null);
+    assert(notifications != null);
+    SentinelViewElement e = new SentinelViewElement.created();
+    e._r = new RenderingScheduler<SentinelViewElement>(e, queue: queue);
+    e._vm = vm;
+    e._isolate = isolate;
+    e._sentinel = sentinel;
+    e._events = events;
+    e._notifications = notifications;
+    return e;
+  }
+
+  SentinelViewElement.created() : super.created('sentinel-view');
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    _r.disable(notify: true);
+    text = '';
+    title = '';
+  }
+
+  void render() {
+    children = <Element>[
+      navBar(<Element>[
+        new NavTopMenuElement(queue: _r.queue).element,
+        new NavVMMenuElement(_vm, _events, queue: _r.queue).element,
+        new NavIsolateMenuElement(_isolate, _events, queue: _r.queue).element,
+        navMenu('sentinel'),
+        new NavNotifyElement(_notifications, queue: _r.queue).element
+      ]),
+      new DivElement()
+        ..classes = ['content-centered-big']
+        ..children = <Element>[
+          new HeadingElement.h2()
+            ..text = 'Sentinel: #{_sentinel.valueAsString}',
+          new HRElement(),
+          new DivElement()..text = _sentinelKindToDescription(_sentinel.kind),
+          new HRElement(),
+          new ViewFooterElement(queue: _r.queue).element
+        ]
+    ];
+  }
+
+  static String _sentinelKindToDescription(M.SentinelKind kind) {
+    switch (kind) {
+      case M.SentinelKind.collected:
+        return 'This object has been reclaimed by the garbage collector.';
+      case M.SentinelKind.expired:
+        return 'The handle to this object has expired. '
+            'Consider refreshing the page.';
+      case M.SentinelKind.notInitialized:
+        return 'This object will be initialized once it is accessed by '
+            'the program.';
+      case M.SentinelKind.initializing:
+        return 'This object is currently being initialized.';
+      case M.SentinelKind.optimizedOut:
+        return 'This object is no longer needed and has been removed by the '
+            'optimizing compiler.';
+      case M.SentinelKind.free:
+        return '';
+    }
+    throw new Exception('Unknown SentinelKind: $kind');
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/singletargetcache_ref.dart b/runtime/observatory_2/lib/src/elements/singletargetcache_ref.dart
new file mode 100644
index 0000000..f47d008
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/singletargetcache_ref.dart
@@ -0,0 +1,65 @@
+// Copyright (c) 2017, 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.
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/models.dart' as M
+    show IsolateRef, SingleTargetCacheRef;
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/helpers/uris.dart';
+
+class SingleTargetCacheRefElement extends CustomElement implements Renderable {
+  RenderingScheduler<SingleTargetCacheRefElement> _r;
+
+  Stream<RenderedEvent<SingleTargetCacheRefElement>> get onRendered =>
+      _r.onRendered;
+
+  M.IsolateRef _isolate;
+  M.SingleTargetCacheRef _singleTargetCache;
+
+  M.IsolateRef get isolate => _isolate;
+  M.SingleTargetCacheRef get singleTargetCache => _singleTargetCache;
+
+  factory SingleTargetCacheRefElement(
+      M.IsolateRef isolate, M.SingleTargetCacheRef singleTargetCache,
+      {RenderingQueue queue}) {
+    assert(isolate != null);
+    assert(singleTargetCache != null);
+    SingleTargetCacheRefElement e = new SingleTargetCacheRefElement.created();
+    e._r = new RenderingScheduler<SingleTargetCacheRefElement>(e, queue: queue);
+    e._isolate = isolate;
+    e._singleTargetCache = singleTargetCache;
+    return e;
+  }
+
+  SingleTargetCacheRefElement.created()
+      : super.created('singletargetcache-ref');
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+  }
+
+  void render() {
+    children = <Element>[
+      new AnchorElement(
+          href: Uris.inspect(_isolate, object: _singleTargetCache))
+        ..children = <Element>[
+          new SpanElement()
+            ..classes = ['emphasize']
+            ..text = 'SingleTargetCache',
+          new SpanElement()..text = ' (${_singleTargetCache.target.name})'
+        ]
+    ];
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/singletargetcache_view.dart b/runtime/observatory_2/lib/src/elements/singletargetcache_view.dart
new file mode 100644
index 0000000..b8d3224
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/singletargetcache_view.dart
@@ -0,0 +1,178 @@
+// Copyright (c) 2017, 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.
+
+import 'dart:async';
+import 'dart:html';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/curly_block.dart';
+import 'package:observatory_2/src/elements/helpers/any_ref.dart';
+import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
+import 'package:observatory_2/src/elements/helpers/nav_menu.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/nav/isolate_menu.dart';
+import 'package:observatory_2/src/elements/nav/notify.dart';
+import 'package:observatory_2/src/elements/nav/refresh.dart';
+import 'package:observatory_2/src/elements/nav/top_menu.dart';
+import 'package:observatory_2/src/elements/nav/vm_menu.dart';
+import 'package:observatory_2/src/elements/object_common.dart';
+import 'package:observatory_2/src/elements/view_footer.dart';
+
+class SingleTargetCacheViewElement extends CustomElement implements Renderable {
+  RenderingScheduler<SingleTargetCacheViewElement> _r;
+
+  Stream<RenderedEvent<SingleTargetCacheViewElement>> get onRendered =>
+      _r.onRendered;
+
+  M.VM _vm;
+  M.IsolateRef _isolate;
+  M.EventRepository _events;
+  M.NotificationRepository _notifications;
+  M.SingleTargetCache _singleTargetCache;
+  M.SingleTargetCacheRepository _singleTargetCaches;
+  M.RetainedSizeRepository _retainedSizes;
+  M.ReachableSizeRepository _reachableSizes;
+  M.InboundReferencesRepository _references;
+  M.RetainingPathRepository _retainingPaths;
+  M.ObjectRepository _objects;
+
+  M.VMRef get vm => _vm;
+  M.IsolateRef get isolate => _isolate;
+  M.NotificationRepository get notifications => _notifications;
+  M.SingleTargetCache get singleTargetCache => _singleTargetCache;
+
+  factory SingleTargetCacheViewElement(
+      M.VM vm,
+      M.IsolateRef isolate,
+      M.SingleTargetCache singleTargetCache,
+      M.EventRepository events,
+      M.NotificationRepository notifications,
+      M.SingleTargetCacheRepository singleTargetCaches,
+      M.RetainedSizeRepository retainedSizes,
+      M.ReachableSizeRepository reachableSizes,
+      M.InboundReferencesRepository references,
+      M.RetainingPathRepository retainingPaths,
+      M.ObjectRepository objects,
+      {RenderingQueue queue}) {
+    assert(vm != null);
+    assert(isolate != null);
+    assert(events != null);
+    assert(notifications != null);
+    assert(singleTargetCache != null);
+    assert(singleTargetCaches != null);
+    assert(retainedSizes != null);
+    assert(reachableSizes != null);
+    assert(references != null);
+    assert(retainingPaths != null);
+    assert(objects != null);
+    SingleTargetCacheViewElement e = new SingleTargetCacheViewElement.created();
+    e._r =
+        new RenderingScheduler<SingleTargetCacheViewElement>(e, queue: queue);
+    e._vm = vm;
+    e._isolate = isolate;
+    e._events = events;
+    e._notifications = notifications;
+    e._singleTargetCache = singleTargetCache;
+    e._singleTargetCaches = singleTargetCaches;
+    e._retainedSizes = retainedSizes;
+    e._reachableSizes = reachableSizes;
+    e._references = references;
+    e._retainingPaths = retainingPaths;
+    e._objects = objects;
+    return e;
+  }
+
+  SingleTargetCacheViewElement.created()
+      : super.created(
+          'singletargetcache-view',
+        );
+
+  @override
+  attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+  }
+
+  void render() {
+    children = <Element>[
+      navBar(<Element>[
+        new NavTopMenuElement(queue: _r.queue).element,
+        new NavVMMenuElement(_vm, _events, queue: _r.queue).element,
+        new NavIsolateMenuElement(_isolate, _events, queue: _r.queue).element,
+        navMenu('singleTargetCache'),
+        (new NavRefreshElement(queue: _r.queue)
+              ..onRefresh.listen((e) async {
+                e.element.disabled = true;
+                _singleTargetCache = await _singleTargetCaches.get(
+                    _isolate, _singleTargetCache.id);
+                _r.dirty();
+              }))
+            .element,
+        new NavNotifyElement(_notifications, queue: _r.queue).element
+      ]),
+      new DivElement()
+        ..classes = ['content-centered-big']
+        ..children = <Element>[
+          new HeadingElement.h2()..text = 'SingleTargetCache',
+          new HRElement(),
+          new ObjectCommonElement(_isolate, _singleTargetCache, _retainedSizes,
+                  _reachableSizes, _references, _retainingPaths, _objects,
+                  queue: _r.queue)
+              .element,
+          new DivElement()
+            ..classes = ['memberList']
+            ..children = <Element>[
+              new DivElement()
+                ..classes = ['memberItem']
+                ..children = <Element>[
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..text = 'target',
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..children = <Element>[
+                      anyRef(_isolate, _singleTargetCache.target, _objects,
+                          queue: _r.queue)
+                    ]
+                ],
+              new DivElement()
+                ..classes = ['memberItem']
+                ..children = <Element>[
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..text = 'lowerLimit',
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..children = <Element>[
+                      new SpanElement()
+                        ..text = _singleTargetCache.lowerLimit.toString()
+                    ]
+                ],
+              new DivElement()
+                ..classes = ['memberItem']
+                ..children = <Element>[
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..text = 'upperLimit',
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..children = <Element>[
+                      new SpanElement()
+                        ..text = _singleTargetCache.upperLimit.toString()
+                    ]
+                ]
+            ],
+          new HRElement(),
+          new ViewFooterElement(queue: _r.queue).element
+        ]
+    ];
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/source_inset.dart b/runtime/observatory_2/lib/src/elements/source_inset.dart
new file mode 100644
index 0000000..ac94f30
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/source_inset.dart
@@ -0,0 +1,89 @@
+// 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.
+
+library source_inset_element;
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/script_inset.dart';
+
+class SourceInsetElement extends CustomElement implements Renderable {
+  RenderingScheduler<SourceInsetElement> _r;
+
+  Stream<RenderedEvent<SourceInsetElement>> get onRendered => _r.onRendered;
+
+  M.IsolateRef _isolate;
+  M.SourceLocation _location;
+  M.ScriptRepository _scripts;
+  M.ObjectRepository _objects;
+  M.EventRepository _events;
+  int _currentPos;
+  bool _inDebuggerContext;
+  Iterable _variables;
+
+  M.IsolateRef get isolate => _isolate;
+  M.SourceLocation get location => _location;
+
+  factory SourceInsetElement(
+      M.IsolateRef isolate,
+      M.SourceLocation location,
+      M.ScriptRepository scripts,
+      M.ObjectRepository objects,
+      M.EventRepository events,
+      {int currentPos,
+      bool inDebuggerContext: false,
+      Iterable variables: const [],
+      RenderingQueue queue}) {
+    assert(isolate != null);
+    assert(location != null);
+    assert(scripts != null);
+    assert(objects != null);
+    assert(events != null);
+    assert(inDebuggerContext != null);
+    assert(variables != null);
+    SourceInsetElement e = new SourceInsetElement.created();
+    e._r = new RenderingScheduler<SourceInsetElement>(e, queue: queue);
+    e._isolate = isolate;
+    e._location = location;
+    e._scripts = scripts;
+    e._objects = objects;
+    e._events = events;
+    e._currentPos = currentPos;
+    e._inDebuggerContext = inDebuggerContext;
+    e._variables = variables;
+    return e;
+  }
+
+  SourceInsetElement.created() : super.created('source-inset');
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    children = <Element>[];
+    _r.disable(notify: true);
+  }
+
+  void render() {
+    children = <Element>[
+      new ScriptInsetElement(
+              _isolate, _location.script, _scripts, _objects, _events,
+              startPos: _location.tokenPos,
+              endPos: _location.endTokenPos,
+              currentPos: _currentPos,
+              inDebuggerContext: _inDebuggerContext,
+              variables: _variables,
+              queue: _r.queue)
+          .element
+    ];
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/source_link.dart b/runtime/observatory_2/lib/src/elements/source_link.dart
new file mode 100644
index 0000000..8820b50
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/source_link.dart
@@ -0,0 +1,90 @@
+// Copyright (c) 2016, 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.
+
+library source_link_element;
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/models.dart'
+    show IsolateRef, SourceLocation, Script, ScriptRepository;
+import 'package:observatory_2/service.dart' as S;
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/helpers/uris.dart';
+
+class SourceLinkElement extends CustomElement implements Renderable {
+  RenderingScheduler<SourceLinkElement> _r;
+
+  Stream<RenderedEvent<SourceLinkElement>> get onRendered => _r.onRendered;
+
+  IsolateRef _isolate;
+  SourceLocation _location;
+  Script _script;
+  ScriptRepository _repository;
+
+  IsolateRef get isolate => _isolate;
+  SourceLocation get location => _location;
+
+  factory SourceLinkElement(
+      IsolateRef isolate, SourceLocation location, ScriptRepository repository,
+      {RenderingQueue queue}) {
+    assert(isolate != null);
+    assert(location != null);
+    SourceLinkElement e = new SourceLinkElement.created();
+    e._r = new RenderingScheduler<SourceLinkElement>(e, queue: queue);
+    e._isolate = isolate;
+    e._location = location;
+    e._repository = repository;
+    return e;
+  }
+
+  SourceLinkElement.created() : super.created('source-link');
+
+  @override
+  void attached() {
+    super.attached();
+    _repository.get(_isolate, _location.script.id).then((script) {
+      _script = script;
+      _r.dirty();
+    }, onError: (e) {
+      // The script object has expired, likely due to a hot reload.
+      (_isolate as S.Isolate).getScripts().then((scripts) {
+        for (final script in scripts) {
+          if (script.uri == _location.script.uri) {
+            _script = script;
+            _r.dirty();
+            return;
+          }
+        }
+        // Rethrow the original exception if we can't find a match.
+        throw e;
+      });
+    });
+    _r.enable();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    children = <Element>[];
+    _r.disable(notify: true);
+  }
+
+  Future render() async {
+    if (_script == null) {
+      children = <Element>[new SpanElement()..text = '<LOADING>'];
+    } else {
+      String label = _script.uri.split('/').last;
+      int token = _location.tokenPos;
+      int line = _script.tokenToLine(token);
+      int column = _script.tokenToCol(token);
+      children = <Element>[
+        new AnchorElement(
+            href: Uris.inspect(isolate, object: _script, pos: token))
+          ..title = _script.uri
+          ..text = '${label}:${line}:${column}'
+      ];
+    }
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/stack_trace_tree_config.dart b/runtime/observatory_2/lib/src/elements/stack_trace_tree_config.dart
new file mode 100644
index 0000000..6e32334
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/stack_trace_tree_config.dart
@@ -0,0 +1,262 @@
+// 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.
+
+import 'dart:async';
+import 'dart:html';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+
+enum ProfileTreeMode {
+  code,
+  function,
+}
+
+class StackTraceTreeConfigChangedEvent {
+  final StackTraceTreeConfigElement element;
+  StackTraceTreeConfigChangedEvent(this.element);
+}
+
+class StackTraceTreeConfigElement extends CustomElement implements Renderable {
+  RenderingScheduler<StackTraceTreeConfigElement> _r;
+
+  Stream<RenderedEvent<StackTraceTreeConfigElement>> get onRendered =>
+      _r.onRendered;
+
+  StreamController<StackTraceTreeConfigChangedEvent> _onModeChange =
+      new StreamController<StackTraceTreeConfigChangedEvent>.broadcast();
+  StreamController<StackTraceTreeConfigChangedEvent> _onDirectionChange =
+      new StreamController<StackTraceTreeConfigChangedEvent>.broadcast();
+  StreamController<StackTraceTreeConfigChangedEvent> _onFilterChange =
+      new StreamController<StackTraceTreeConfigChangedEvent>.broadcast();
+  Stream<StackTraceTreeConfigChangedEvent> get onModeChange =>
+      _onModeChange.stream;
+  Stream<StackTraceTreeConfigChangedEvent> get onDirectionChange =>
+      _onDirectionChange.stream;
+  Stream<StackTraceTreeConfigChangedEvent> get onFilterChange =>
+      _onFilterChange.stream;
+
+  bool _showMode;
+  bool _showDirection;
+  bool _showFilter;
+  ProfileTreeMode _mode;
+  M.ProfileTreeDirection _direction;
+  String _filter;
+
+  bool get showMode => _showMode;
+  bool get showDirection => _showDirection;
+  bool get showFilter => _showFilter;
+  ProfileTreeMode get mode => _mode;
+  M.ProfileTreeDirection get direction => _direction;
+  String get filter => _filter;
+
+  set showMode(bool value) => _showMode = _r.checkAndReact(_showMode, value);
+  set showDirection(bool value) =>
+      _showDirection = _r.checkAndReact(_showDirection, value);
+  set showFilter(bool value) =>
+      _showFilter = _r.checkAndReact(_showFilter, value);
+  set mode(ProfileTreeMode value) => _mode = _r.checkAndReact(_mode, value);
+  set direction(M.ProfileTreeDirection value) =>
+      _direction = _r.checkAndReact(_direction, value);
+  set filter(String value) => _filter = _r.checkAndReact(_filter, value);
+
+  factory StackTraceTreeConfigElement(
+      {bool showMode: true,
+      bool showDirection: true,
+      bool showFilter: true,
+      String filter: '',
+      ProfileTreeMode mode: ProfileTreeMode.function,
+      M.ProfileTreeDirection direction: M.ProfileTreeDirection.exclusive,
+      RenderingQueue queue}) {
+    assert(showMode != null);
+    assert(showDirection != null);
+    assert(showFilter != null);
+    assert(mode != null);
+    assert(direction != null);
+    assert(filter != null);
+    StackTraceTreeConfigElement e = new StackTraceTreeConfigElement.created();
+    e._r = new RenderingScheduler<StackTraceTreeConfigElement>(e, queue: queue);
+    e._showMode = showMode;
+    e._showDirection = showDirection;
+    e._showFilter = showFilter;
+    e._mode = mode;
+    e._direction = direction;
+    e._filter = filter;
+    return e;
+  }
+
+  StackTraceTreeConfigElement.created()
+      : super.created('stack-trace-tree-config');
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = const [];
+  }
+
+  void render() {
+    children = <Element>[
+      new DivElement()
+        ..classes = ['content-centered-big']
+        ..children = <Element>[
+          new HeadingElement.h2()..text = 'Tree display',
+          new HRElement(),
+          new DivElement()
+            ..classes = ['row']
+            ..children = <Element>[
+              new DivElement()
+                ..classes = ['memberList']
+                ..children = _createMembers()
+            ]
+        ]
+    ];
+  }
+
+  List<Element> _createMembers() {
+    var members = <Element>[];
+    if (_showMode) {
+      members.add(new DivElement()
+        ..classes = ['memberItem']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = 'Mode',
+          new DivElement()
+            ..classes = ['memberValue']
+            ..children = _createModeSelect()
+        ]);
+    }
+    if (_showDirection) {
+      members.add(new DivElement()
+        ..classes = ['memberItem']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = 'Call Tree Direction',
+          new SpanElement()
+            ..classes = ['memberValue']
+            ..children = _createDirectionSelect()
+        ]);
+    }
+    if (showFilter) {
+      members.add(new DivElement()
+        ..classes = ['memberItem']
+        ..children = <Element>[
+          new DivElement()
+            ..classes = ['memberName']
+            ..text = 'Call Tree Filter'
+            ..title = 'case-sensitive substring match',
+          new SpanElement()
+            ..classes = ['memberValue']
+            ..children = _createFilter()
+        ]);
+    }
+    return members;
+  }
+
+  String get modeDescription {
+    if (_mode == ProfileTreeMode.function) {
+      return 'Inlined frames expanded.';
+    } else {
+      return 'Inlined frames not expanded.';
+    }
+  }
+
+  List<Element> _createModeSelect() {
+    var s;
+    return [
+      s = new SelectElement()
+        ..classes = ['mode-select']
+        ..value = modeToString(_mode)
+        ..children = ProfileTreeMode.values.map((mode) {
+          return new OptionElement(
+              value: modeToString(mode), selected: _mode == mode)
+            ..text = modeToString(mode);
+        }).toList(growable: false)
+        ..onChange.listen((_) {
+          _mode = ProfileTreeMode.values[s.selectedIndex];
+          _r.dirty();
+        })
+        ..onChange.map(_toEvent).listen(_triggerModeChange),
+      new SpanElement()..text = ' $modeDescription'
+    ];
+  }
+
+  String get directionDescription {
+    if (_direction == M.ProfileTreeDirection.inclusive) {
+      return 'Tree is rooted at "main". Child nodes are callees.';
+    } else {
+      return 'Tree is rooted at top-of-stack. Child nodes are callers.';
+    }
+  }
+
+  List<Element> _createDirectionSelect() {
+    var s;
+    return [
+      s = new SelectElement()
+        ..classes = ['direction-select']
+        ..value = directionToString(_direction)
+        ..children = M.ProfileTreeDirection.values.map((direction) {
+          return new OptionElement(
+              value: directionToString(direction),
+              selected: _direction == direction)
+            ..text = directionToString(direction);
+        }).toList(growable: false)
+        ..onChange.listen((_) {
+          _direction = M.ProfileTreeDirection.values[s.selectedIndex];
+          _r.dirty();
+        })
+        ..onChange.map(_toEvent).listen(_triggerDirectionChange),
+      new SpanElement()..text = ' $directionDescription'
+    ];
+  }
+
+  List<Element> _createFilter() {
+    var t;
+    return [
+      t = new TextInputElement()
+        ..placeholder = 'Search filter'
+        ..value = filter
+        ..onChange.listen((_) {
+          _filter = t.value;
+        })
+        ..onChange.map(_toEvent).listen(_triggerFilterChange)
+    ];
+  }
+
+  static String modeToString(ProfileTreeMode mode) {
+    switch (mode) {
+      case ProfileTreeMode.code:
+        return 'Code';
+      case ProfileTreeMode.function:
+        return 'Function';
+    }
+    throw new Exception('Unknown ProfileTreeMode');
+  }
+
+  static String directionToString(M.ProfileTreeDirection direction) {
+    switch (direction) {
+      case M.ProfileTreeDirection.inclusive:
+        return 'Top down';
+      case M.ProfileTreeDirection.exclusive:
+        return 'Bottom up';
+    }
+    throw new Exception('Unknown ProfileTreeDirection');
+  }
+
+  StackTraceTreeConfigChangedEvent _toEvent(_) {
+    return new StackTraceTreeConfigChangedEvent(this);
+  }
+
+  void _triggerModeChange(e) => _onModeChange.add(e);
+  void _triggerDirectionChange(e) => _onDirectionChange.add(e);
+  void _triggerFilterChange(e) => _onFilterChange.add(e);
+}
diff --git a/runtime/observatory_2/lib/src/elements/strongly_reachable_instances.dart b/runtime/observatory_2/lib/src/elements/strongly_reachable_instances.dart
new file mode 100644
index 0000000..45e11e8
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/strongly_reachable_instances.dart
@@ -0,0 +1,120 @@
+// Copyright (c) 2016, 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.
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/curly_block.dart';
+import 'package:observatory_2/src/elements/instance_ref.dart';
+import 'package:observatory_2/src/elements/helpers/any_ref.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+
+class StronglyReachableInstancesElement extends CustomElement
+    implements Renderable {
+  RenderingScheduler<StronglyReachableInstancesElement> _r;
+
+  Stream<RenderedEvent<StronglyReachableInstancesElement>> get onRendered =>
+      _r.onRendered;
+
+  M.IsolateRef _isolate;
+  M.ClassRef _cls;
+  M.StronglyReachableInstancesRepository _stronglyReachableInstances;
+  M.ObjectRepository _objects;
+  M.InstanceSet _result;
+  bool _expanded = false;
+
+  M.IsolateRef get isolate => _isolate;
+  M.ClassRef get cls => _cls;
+
+  factory StronglyReachableInstancesElement(
+      M.IsolateRef isolate,
+      M.ClassRef cls,
+      M.StronglyReachableInstancesRepository stronglyReachable,
+      M.ObjectRepository objects,
+      {RenderingQueue queue}) {
+    assert(isolate != null);
+    assert(cls != null);
+    assert(stronglyReachable != null);
+    assert(objects != null);
+    StronglyReachableInstancesElement e =
+        new StronglyReachableInstancesElement.created();
+    e._r = new RenderingScheduler<StronglyReachableInstancesElement>(e,
+        queue: queue);
+    e._isolate = isolate;
+    e._cls = cls;
+    e._stronglyReachableInstances = stronglyReachable;
+    e._objects = objects;
+    return e;
+  }
+
+  StronglyReachableInstancesElement.created()
+      : super.created('strongly-reachable-instances');
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    children = <Element>[];
+    _r.disable(notify: true);
+  }
+
+  void render() {
+    children = <Element>[
+      (new CurlyBlockElement(expanded: _expanded, queue: _r.queue)
+            ..content = _createContent()
+            ..onToggle.listen((e) async {
+              _expanded = e.control.expanded;
+              e.control.disabled = true;
+              await _refresh();
+              e.control.disabled = false;
+            }))
+          .element
+    ];
+  }
+
+  Future _refresh() async {
+    _result = null;
+    _result = await _stronglyReachableInstances.get(_isolate, _cls);
+    _r.dirty();
+  }
+
+  List<Element> _createContent() {
+    if (_result == null) {
+      return [new SpanElement()..text = 'Loading...'];
+    }
+    final content = _result.instances
+        .map<Element>((sample) => new DivElement()
+          ..children = <Element>[
+            anyRef(_isolate, sample, _objects, queue: _r.queue)
+          ])
+        .toList();
+    content.add(new DivElement()
+      ..children = ([]
+        ..addAll(_createShowMoreButton())
+        ..add(new SpanElement()..text = ' of total ${_result.count}')));
+    return content;
+  }
+
+  List<Element> _createShowMoreButton() {
+    final samples = _result.instances.toList();
+    if (samples.length == _result.count) {
+      return [];
+    }
+    final count = samples.length;
+    final button = new ButtonElement()..text = 'show next ${count}';
+    button.onClick.listen((_) async {
+      button.disabled = true;
+      _result = await _stronglyReachableInstances.get(_isolate, _cls,
+          limit: count * 2);
+      _r.dirty();
+    });
+    return [button];
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/subtypetestcache_ref.dart b/runtime/observatory_2/lib/src/elements/subtypetestcache_ref.dart
new file mode 100644
index 0000000..6cb5a25
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/subtypetestcache_ref.dart
@@ -0,0 +1,62 @@
+// Copyright (c) 2017, 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.
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/models.dart' as M
+    show IsolateRef, SubtypeTestCacheRef;
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/helpers/uris.dart';
+
+class SubtypeTestCacheRefElement extends CustomElement implements Renderable {
+  RenderingScheduler<SubtypeTestCacheRefElement> _r;
+
+  Stream<RenderedEvent<SubtypeTestCacheRefElement>> get onRendered =>
+      _r.onRendered;
+
+  M.IsolateRef _isolate;
+  M.SubtypeTestCacheRef _subtypeTestCache;
+
+  M.IsolateRef get isolate => _isolate;
+  M.SubtypeTestCacheRef get subtypeTestCache => _subtypeTestCache;
+
+  factory SubtypeTestCacheRefElement(
+      M.IsolateRef isolate, M.SubtypeTestCacheRef subtypeTestCache,
+      {RenderingQueue queue}) {
+    assert(isolate != null);
+    assert(subtypeTestCache != null);
+    SubtypeTestCacheRefElement e = new SubtypeTestCacheRefElement.created();
+    e._r = new RenderingScheduler<SubtypeTestCacheRefElement>(e, queue: queue);
+    e._isolate = isolate;
+    e._subtypeTestCache = subtypeTestCache;
+    return e;
+  }
+
+  SubtypeTestCacheRefElement.created() : super.created('subtypetestcache-ref');
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+  }
+
+  void render() {
+    children = <Element>[
+      new AnchorElement(href: Uris.inspect(_isolate, object: _subtypeTestCache))
+        ..children = <Element>[
+          new SpanElement()
+            ..classes = ['emphasize']
+            ..text = 'SubtypeTestCache',
+        ]
+    ];
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/subtypetestcache_view.dart b/runtime/observatory_2/lib/src/elements/subtypetestcache_view.dart
new file mode 100644
index 0000000..ba42732
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/subtypetestcache_view.dart
@@ -0,0 +1,149 @@
+// Copyright (c) 2017, 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.
+
+import 'dart:async';
+import 'dart:html';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/curly_block.dart';
+import 'package:observatory_2/src/elements/helpers/any_ref.dart';
+import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
+import 'package:observatory_2/src/elements/helpers/nav_menu.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/nav/isolate_menu.dart';
+import 'package:observatory_2/src/elements/nav/notify.dart';
+import 'package:observatory_2/src/elements/nav/refresh.dart';
+import 'package:observatory_2/src/elements/nav/top_menu.dart';
+import 'package:observatory_2/src/elements/nav/vm_menu.dart';
+import 'package:observatory_2/src/elements/object_common.dart';
+import 'package:observatory_2/src/elements/view_footer.dart';
+
+class SubtypeTestCacheViewElement extends CustomElement implements Renderable {
+  RenderingScheduler<SubtypeTestCacheViewElement> _r;
+
+  Stream<RenderedEvent<SubtypeTestCacheViewElement>> get onRendered =>
+      _r.onRendered;
+
+  M.VM _vm;
+  M.IsolateRef _isolate;
+  M.EventRepository _events;
+  M.NotificationRepository _notifications;
+  M.SubtypeTestCache _subtypeTestCache;
+  M.SubtypeTestCacheRepository _subtypeTestCaches;
+  M.RetainedSizeRepository _retainedSizes;
+  M.ReachableSizeRepository _reachableSizes;
+  M.InboundReferencesRepository _references;
+  M.RetainingPathRepository _retainingPaths;
+  M.ObjectRepository _objects;
+
+  M.VMRef get vm => _vm;
+  M.IsolateRef get isolate => _isolate;
+  M.NotificationRepository get notifications => _notifications;
+  M.SubtypeTestCache get subtypeTestCache => _subtypeTestCache;
+
+  factory SubtypeTestCacheViewElement(
+      M.VM vm,
+      M.IsolateRef isolate,
+      M.SubtypeTestCache subtypeTestCache,
+      M.EventRepository events,
+      M.NotificationRepository notifications,
+      M.SubtypeTestCacheRepository subtypeTestCaches,
+      M.RetainedSizeRepository retainedSizes,
+      M.ReachableSizeRepository reachableSizes,
+      M.InboundReferencesRepository references,
+      M.RetainingPathRepository retainingPaths,
+      M.ObjectRepository objects,
+      {RenderingQueue queue}) {
+    assert(vm != null);
+    assert(isolate != null);
+    assert(events != null);
+    assert(notifications != null);
+    assert(subtypeTestCache != null);
+    assert(subtypeTestCaches != null);
+    assert(retainedSizes != null);
+    assert(reachableSizes != null);
+    assert(references != null);
+    assert(retainingPaths != null);
+    assert(objects != null);
+    SubtypeTestCacheViewElement e = new SubtypeTestCacheViewElement.created();
+    e._r = new RenderingScheduler<SubtypeTestCacheViewElement>(e, queue: queue);
+    e._vm = vm;
+    e._isolate = isolate;
+    e._events = events;
+    e._notifications = notifications;
+    e._subtypeTestCache = subtypeTestCache;
+    e._subtypeTestCaches = subtypeTestCaches;
+    e._retainedSizes = retainedSizes;
+    e._reachableSizes = reachableSizes;
+    e._references = references;
+    e._retainingPaths = retainingPaths;
+    e._objects = objects;
+    return e;
+  }
+
+  SubtypeTestCacheViewElement.created()
+      : super.created('subtypetestcache-view');
+
+  @override
+  attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+  }
+
+  void render() {
+    children = <Element>[
+      navBar(<Element>[
+        new NavTopMenuElement(queue: _r.queue).element,
+        new NavVMMenuElement(_vm, _events, queue: _r.queue).element,
+        new NavIsolateMenuElement(_isolate, _events, queue: _r.queue).element,
+        navMenu('subtypeTestCache'),
+        (new NavRefreshElement(queue: _r.queue)
+              ..onRefresh.listen((e) async {
+                e.element.disabled = true;
+                _subtypeTestCache = await _subtypeTestCaches.get(
+                    _isolate, _subtypeTestCache.id);
+                _r.dirty();
+              }))
+            .element,
+        new NavNotifyElement(_notifications, queue: _r.queue).element
+      ]),
+      new DivElement()
+        ..classes = ['content-centered-big']
+        ..children = <Element>[
+          new HeadingElement.h2()..text = 'SubtypeTestCache',
+          new HRElement(),
+          new ObjectCommonElement(_isolate, _subtypeTestCache, _retainedSizes,
+                  _reachableSizes, _references, _retainingPaths, _objects,
+                  queue: _r.queue)
+              .element,
+          new DivElement()
+            ..classes = ['memberList']
+            ..children = <Element>[
+              new DivElement()
+                ..classes = ['memberItem']
+                ..children = <Element>[
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..text = 'cache',
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..children = <Element>[
+                      anyRef(_isolate, _subtypeTestCache.cache, _objects,
+                          queue: _r.queue)
+                    ]
+                ]
+            ],
+          new HRElement(),
+          new ViewFooterElement(queue: _r.queue).element
+        ]
+    ];
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/timeline/dashboard.dart b/runtime/observatory_2/lib/src/elements/timeline/dashboard.dart
new file mode 100644
index 0000000..8826324
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/timeline/dashboard.dart
@@ -0,0 +1,229 @@
+// Copyright (c) 2017, 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.
+
+/// This page is not directly reachable from the main Observatory ui.
+/// It is mainly mented to be used from editors as an integrated tool.
+///
+/// This page mainly targeting developers and not VM experts, so concepts like
+/// timeline streams are hidden away.
+///
+/// The page exposes two views over the timeline data.
+/// Both of them are filtered based on the optional argument `mode`.
+/// See [_TimelineView] for the explanation of the two possible values.
+
+import 'dart:async';
+import 'dart:html';
+import 'dart:convert';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/nav/notify.dart';
+
+/// The two possible views are available.
+/// * `string`
+///   The events are just filtered by `mode` and maintain their original
+///   timestamp.
+/// * `frame`
+///   The events are organized by frame.
+///   The events are shifted in order to give a high level view of the
+///   computation involved in a frame.
+///   The frame are concatenated one after the other taking care of not
+///   overlapping the related events.
+enum _TimelineView { strict, frame }
+
+class TimelineDashboardElement extends CustomElement implements Renderable {
+  RenderingScheduler<TimelineDashboardElement> _r;
+
+  Stream<RenderedEvent<TimelineDashboardElement>> get onRendered =>
+      _r.onRendered;
+
+  M.VM _vm;
+  M.TimelineRepository _repository;
+  M.NotificationRepository _notifications;
+  M.TimelineFlags _flags;
+  _TimelineView _view = _TimelineView.strict;
+
+  M.VM get vm => _vm;
+  M.NotificationRepository get notifications => _notifications;
+
+  factory TimelineDashboardElement(M.VM vm, M.TimelineRepository repository,
+      M.NotificationRepository notifications,
+      {RenderingQueue queue}) {
+    assert(vm != null);
+    assert(repository != null);
+    assert(notifications != null);
+    TimelineDashboardElement e = new TimelineDashboardElement.created();
+    e._r = new RenderingScheduler<TimelineDashboardElement>(e, queue: queue);
+    e._vm = vm;
+    e._repository = repository;
+    e._notifications = notifications;
+    if (vm.embedder == 'Flutter') {
+      e._view = _TimelineView.frame;
+    }
+    return e;
+  }
+
+  TimelineDashboardElement.created() : super.created('timeline-dashboard');
+
+  @override
+  attached() {
+    super.attached();
+    _r.enable();
+    _refresh();
+  }
+
+  @override
+  detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+  }
+
+  IFrameElement _frame;
+  DivElement _content;
+
+  void render() {
+    if (_frame == null) {
+      _frame = new IFrameElement();
+    }
+    if (_content == null) {
+      _content = new DivElement()..classes = ['content-centered-big'];
+    }
+    _frame.src = _makeFrameUrl();
+    _content.children = <Element>[
+      new HeadingElement.h2()
+        ..nodes = ([new Text("Timeline View")]
+          ..addAll(_createButtons())
+          ..addAll(_createTabs())),
+      new ParagraphElement()
+        ..text = (_view == _TimelineView.frame
+            ? 'Logical view of the computation involved in each frame '
+                '(timestamps may not be preserved)'
+            : 'Sequence of events generated during the execution '
+                '(timestamps are preserved)')
+    ];
+    if (children.isEmpty) {
+      children = <Element>[
+        navBar(<Element>[
+          new NavNotifyElement(_notifications, queue: _r.queue).element
+        ]),
+        _content,
+        new DivElement()
+          ..classes = ['iframe']
+          ..children = <Element>[_frame]
+      ];
+    }
+  }
+
+  List<Node> _createButtons() {
+    if (_flags == null) {
+      return [new Text('Loading')];
+    }
+    if (_suggestedProfile(_flags.profiles).streams.any((s) => !s.isRecorded)) {
+      return [
+        new ButtonElement()
+          ..classes = ['header_button']
+          ..text = 'Enable'
+          ..title = 'The Timeline is not fully enabled, click to enable'
+          ..onClick.listen((e) => _enable()),
+      ];
+    }
+    return [
+      new ButtonElement()
+        ..classes = ['header_button']
+        ..text = 'Load from VM'
+        ..title = 'Load the timeline'
+        ..onClick.listen((e) => _refresh()),
+      new ButtonElement()
+        ..classes = ['header_button']
+        ..text = 'Reset Timeline'
+        ..title = 'Reset the current timeline'
+        ..onClick.listen((e) => _clear()),
+      new ButtonElement()
+        ..classes = ['header_button', 'left-pad']
+        ..text = 'Save to File…'
+        ..title = 'Save the current Timeline to file'
+        ..onClick.listen((e) => _save()),
+      new ButtonElement()
+        ..classes = ['header_button']
+        ..text = 'Load from File…'
+        ..title = 'Load a saved timeline from file'
+        ..onClick.listen((e) => _load()),
+    ];
+  }
+
+  List<Element> _createTabs() {
+    if (_vm.embedder != 'Flutter') {
+      return const [];
+    }
+    return [
+      new SpanElement()
+        ..classes = ['tab_buttons']
+        ..children = <Element>[
+          new ButtonElement()
+            ..text = 'Frame View'
+            ..title = 'Logical view of the computation involved in each frame\n'
+                'Timestamps may not be preserved'
+            ..disabled = _view == _TimelineView.frame
+            ..onClick.listen((_) {
+              _view = _TimelineView.frame;
+              _r.dirty();
+            }),
+          new ButtonElement()
+            ..text = 'Time View'
+            ..title = 'Sequence of events generated during the execution\n'
+                'Timestamps are preserved'
+            ..disabled = _view == _TimelineView.strict
+            ..onClick.listen((_) {
+              _view = _TimelineView.strict;
+              _r.dirty();
+            }),
+        ]
+    ];
+  }
+
+  String _makeFrameUrl() {
+    final String mode = 'basic';
+    final String view = _view == _TimelineView.frame ? 'frame' : 'strict';
+    return 'timeline.html#mode=$mode&view=$view';
+  }
+
+  M.TimelineProfile _suggestedProfile(Iterable<M.TimelineProfile> profiles) {
+    return profiles
+        .where((profile) => profile.name == 'Flutter Developer')
+        .single;
+  }
+
+  Future _enable() async {
+    await _repository.setRecordedStreams(
+        vm, _suggestedProfile(_flags.profiles).streams);
+    _refresh();
+  }
+
+  Future _refresh() async {
+    _flags = await _repository.getFlags(vm);
+    _r.dirty();
+    final traceData =
+        Map<String, dynamic>.from(await _repository.getTimeline(vm));
+    return _postMessage('refresh', traceData);
+  }
+
+  Future _clear() async {
+    await _repository.clear(_vm);
+    return _postMessage('clear');
+  }
+
+  Future _save() => _postMessage('save');
+
+  Future _load() => _postMessage('load');
+
+  Future _postMessage(String method,
+      [Map<String, dynamic> params = const <String, dynamic>{}]) async {
+    var message = {'method': method, 'params': params};
+    _frame.contentWindow
+        .postMessage(json.encode(message), window.location.href);
+    return null;
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/timeline_page.dart b/runtime/observatory_2/lib/src/elements/timeline_page.dart
new file mode 100644
index 0000000..d16c393
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/timeline_page.dart
@@ -0,0 +1,323 @@
+// Copyright (c) 2015, 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.
+
+library timeline_page_element;
+
+import 'dart:async';
+import 'dart:html';
+import 'dart:convert';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
+import 'package:observatory_2/src/elements/helpers/nav_menu.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/helpers/uris.dart';
+import 'package:observatory_2/src/elements/nav/notify.dart';
+import 'package:observatory_2/src/elements/nav/refresh.dart';
+import 'package:observatory_2/src/elements/nav/top_menu.dart';
+import 'package:observatory_2/src/elements/nav/vm_menu.dart';
+
+class TimelinePageElement extends CustomElement implements Renderable {
+  RenderingScheduler<TimelinePageElement> _r;
+
+  Stream<RenderedEvent<TimelinePageElement>> get onRendered => _r.onRendered;
+
+  M.VM _vm;
+  M.TimelineRepository _repository;
+  M.EventRepository _events;
+  M.NotificationRepository _notifications;
+  M.TimelineRecorder _recorder;
+  Set<M.TimelineStream> _availableStreams;
+  Set<M.TimelineStream> _recordedStreams;
+  Set<M.TimelineProfile> _profiles;
+
+  M.VM get vm => _vm;
+  M.NotificationRepository get notifications => _notifications;
+
+  factory TimelinePageElement(M.VM vm, M.TimelineRepository repository,
+      M.EventRepository events, M.NotificationRepository notifications,
+      {RenderingQueue queue}) {
+    assert(vm != null);
+    assert(repository != null);
+    assert(events != null);
+    assert(notifications != null);
+    TimelinePageElement e = new TimelinePageElement.created();
+    e._r = new RenderingScheduler<TimelinePageElement>(e, queue: queue);
+    e._vm = vm;
+    e._repository = repository;
+    e._events = events;
+    e._notifications = notifications;
+    return e;
+  }
+
+  TimelinePageElement.created() : super.created('timeline-page');
+
+  @override
+  attached() {
+    super.attached();
+    _r.enable();
+    _updateRecorderUI();
+  }
+
+  @override
+  detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+  }
+
+  IFrameElement _frame;
+  DivElement _content;
+
+  bool get usingVMRecorder =>
+      _recorder.name != "Fuchsia" &&
+      _recorder.name != "Systrace" &&
+      _recorder.name != "Macos";
+
+  void render() {
+    if (_frame == null) {
+      _frame = new IFrameElement()..src = 'timeline.html';
+      _frame.onLoad.listen((event) {
+        _refresh();
+      });
+    }
+    if (_content == null) {
+      _content = new DivElement()..classes = ['content-centered-big'];
+    }
+    _content.children = <Element>[
+      new HeadingElement.h1()..text = 'Timeline settings',
+      _recorder == null
+          ? (new DivElement()..text = 'Loading...')
+          : (new DivElement()
+            ..classes = ['memberList']
+            ..children = <Element>[
+              new DivElement()
+                ..classes = ['memberItem']
+                ..children = <Element>[
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..text = 'Recorder:',
+                  new DivElement()
+                    ..classes = ['memberValue']
+                    ..text = _recorder.name
+                ],
+              new DivElement()
+                ..classes = ['memberItem']
+                ..children = <Element>[
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..text = 'Recorded Streams Profile:',
+                  new DivElement()
+                    ..classes = ['memberValue']
+                    ..children = _createProfileSelect()
+                ],
+              new DivElement()
+                ..classes = ['memberItem']
+                ..children = <Element>[
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..text = 'Recorded Streams:',
+                  new DivElement()
+                    ..classes = ['memberValue']
+                    ..children = _availableStreams
+                        .map<Element>(_makeStreamToggle)
+                        .toList()
+                ]
+            ])
+    ];
+
+    children = <Element>[
+      navBar(<Element>[
+        new NavTopMenuElement(queue: _r.queue).element,
+        new NavVMMenuElement(vm, _events, queue: _r.queue).element,
+        navMenu('timeline', link: Uris.timeline()),
+        (new NavRefreshElement(queue: _r.queue)
+              ..onRefresh.listen((e) async {
+                e.element.disabled = true;
+                await _refresh();
+                e.element.disabled = !usingVMRecorder;
+              }))
+            .element,
+        (new NavRefreshElement(label: 'clear', queue: _r.queue)
+              ..onRefresh.listen((e) async {
+                e.element.disabled = true;
+                await _clear();
+                e.element.disabled = !usingVMRecorder;
+              }))
+            .element,
+        (new NavRefreshElement(label: 'save', queue: _r.queue)
+              ..onRefresh.listen((e) async {
+                e.element.disabled = true;
+                await _save();
+                e.element.disabled = !usingVMRecorder;
+              }))
+            .element,
+        (new NavRefreshElement(label: 'load', queue: _r.queue)
+              ..onRefresh.listen((e) async {
+                e.element.disabled = true;
+                await _load();
+                e.element.disabled = !usingVMRecorder;
+              }))
+            .element,
+        new NavNotifyElement(_notifications, queue: _r.queue).element
+      ]),
+      _content,
+      _createIFrameOrMessage(),
+    ];
+  }
+
+  HtmlElement _createIFrameOrMessage() {
+    if (_recorder == null) {
+      return new DivElement()
+        ..classes = ['content-centered-big']
+        ..text = 'Loading...';
+    }
+
+    if (_recorder.name == "Fuchsia") {
+      return new DivElement()
+        ..classes = ['content-centered-big']
+        ..children = <Element>[
+          new BRElement(),
+          new SpanElement()
+            ..text =
+                "This VM is forwarding timeline events to Fuchsia's system tracing. See the ",
+          new AnchorElement()
+            ..text = "Fuchsia Tracing Usage Guide"
+            ..href = "https://fuchsia.dev/fuchsia-src/development/tracing",
+          new SpanElement()..text = ".",
+        ];
+    }
+
+    if (_recorder.name == "Systrace") {
+      return new DivElement()
+        ..classes = ['content-centered-big']
+        ..children = <Element>[
+          new BRElement(),
+          new SpanElement()
+            ..text =
+                "This VM is forwarding timeline events to Android's systrace. See the ",
+          new AnchorElement()
+            ..text = "systrace usage guide"
+            ..href =
+                "https://developer.android.com/studio/command-line/systrace",
+          new SpanElement()..text = ".",
+        ];
+    }
+
+    if (_recorder.name == "Macos") {
+      return new DivElement()
+        ..classes = ['content-centered-big']
+        ..children = <Element>[
+          new BRElement(),
+          new SpanElement()
+            ..text =
+                "This VM is forwarding timeline events to macOS's Unified Logging. "
+                    "To track these events, open 'Instruments' and add the 'os_signpost' Filter. See the ",
+          new AnchorElement()
+            ..text = "Instruments Usage Guide"
+            ..href = "https://help.apple.com/instruments",
+          new SpanElement()..text = ".",
+        ];
+    }
+
+    return new DivElement()
+      ..classes = ['iframe']
+      ..children = <Element>[_frame];
+  }
+
+  List<Element> _createProfileSelect() {
+    return [
+      new SpanElement()
+        ..children = (_profiles.expand((profile) {
+          return <Element>[
+            new ButtonElement()
+              ..text = profile.name
+              ..onClick.listen((_) {
+                _applyPreset(profile);
+              }),
+            new SpanElement()..text = ' - '
+          ];
+        }).toList()
+          ..removeLast())
+    ];
+  }
+
+  Future _refresh() async {
+    _postMessage('loading');
+    final traceData = await _repository.getTimeline(vm);
+    return _postMessage('refresh', traceData);
+  }
+
+  Future _clear() async {
+    await _repository.clear(vm);
+    return _postMessage('clear');
+  }
+
+  Future _save() async {
+    return _postMessage('save');
+  }
+
+  Future _load() async {
+    return _postMessage('load');
+  }
+
+  Future _postMessage(String method,
+      [Map<String, dynamic> params = const <String, dynamic>{}]) async {
+    if (_frame.contentWindow == null) {
+      return null;
+    }
+    var message = {'method': method, 'params': params};
+    _frame.contentWindow
+        .postMessage(json.encode(message), window.location.href);
+    return null;
+  }
+
+  void _applyPreset(M.TimelineProfile profile) {
+    _recordedStreams = new Set<M.TimelineStream>.from(profile.streams);
+    _applyStreamChanges();
+    _updateRecorderUI();
+  }
+
+  Future _updateRecorderUI() async {
+    // Grab the current timeline flags.
+    final M.TimelineFlags flags = await _repository.getFlags(vm);
+    // Grab the recorder name.
+    _recorder = flags.recorder;
+    // Update the set of available streams.
+    _availableStreams = new Set<M.TimelineStream>.from(flags.streams);
+    // Update the set of recorded streams.
+    _recordedStreams = new Set<M.TimelineStream>.from(
+        flags.streams.where((s) => s.isRecorded));
+    // Update the set of presets.
+    _profiles = new Set<M.TimelineProfile>.from(flags.profiles);
+    // Refresh the UI.
+    _r.dirty();
+  }
+
+  Element _makeStreamToggle(M.TimelineStream stream) {
+    LabelElement label = new LabelElement();
+    label.style.paddingLeft = '8px';
+    SpanElement span = new SpanElement();
+    span.text = stream.name;
+    InputElement checkbox = new InputElement();
+    checkbox.onChange.listen((_) {
+      if (checkbox.checked) {
+        _recordedStreams.add(stream);
+      } else {
+        _recordedStreams.remove(stream);
+      }
+      _applyStreamChanges();
+      _updateRecorderUI();
+    });
+    checkbox.type = 'checkbox';
+    checkbox.checked = _recordedStreams.contains(stream);
+    label.children.add(checkbox);
+    label.children.add(span);
+    return label;
+  }
+
+  Future _applyStreamChanges() {
+    return _repository.setRecordedStreams(vm, _recordedStreams);
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/tree_map.dart b/runtime/observatory_2/lib/src/elements/tree_map.dart
new file mode 100644
index 0000000..1ebc789
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/tree_map.dart
@@ -0,0 +1,193 @@
+// Copyright (c) 2019, 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.
+
+import 'dart:html';
+import 'dart:math' as Math;
+import 'package:observatory_2/utils.dart';
+
+abstract class TreeMap<T> {
+  int getArea(T node);
+  String getBackground(T node);
+  String getLabel(T node);
+  String getTooltip(T node) => getLabel(node);
+  T getParent(T node);
+  Iterable<T> getChildren(T node);
+  void onSelect(T node);
+  void onDetails(T node);
+
+  void showIn(T node, DivElement content) {
+    final w = content.offsetWidth.toDouble();
+    final h = content.offsetHeight.toDouble();
+    final topTile = _createTreemapTile(node, w, h, 0, content);
+    topTile.style.width = "${w}px";
+    topTile.style.height = "${h}px";
+    topTile.style.border = "none";
+    content.children = [topTile];
+  }
+
+  Element _createTreemapTile(
+      T node, double width, double height, int depth, DivElement content) {
+    final div = new DivElement();
+    div.className = "treemapTile";
+    div.style.background = getBackground(node);
+    div.onDoubleClick.listen((event) {
+      event.stopPropagation();
+      if (depth == 0) {
+        onSelect(getParent(node)); // Zoom out.
+      } else {
+        onSelect(node); // Zoom in.
+      }
+    });
+    div.onContextMenu.listen((event) {
+      event.stopPropagation();
+      onDetails(node);
+    });
+
+    double left = 0.0;
+    double top = 0.0;
+
+    const kPadding = 5;
+    const kBorder = 1;
+    left += kPadding - kBorder;
+    top += kPadding - kBorder;
+    width -= 2 * kPadding;
+    height -= 2 * kPadding;
+
+    div.title = getTooltip(node);
+
+    if (width < 10 || height < 10) {
+      // Too small: don't render label or children.
+      return div;
+    }
+
+    div.append(new SpanElement()..text = getLabel(node));
+    const kLabelHeight = 9.0;
+    top += kLabelHeight;
+    height -= kLabelHeight;
+
+    if (depth > 2) {
+      // Too deep: don't render children.
+      return div;
+    }
+    if (width < 4 || height < 4) {
+      // Too small: don't render children.
+      return div;
+    }
+
+    final children = <T>[];
+    for (T c in getChildren(node)) {
+      // Size 0 children seem to confuse the layout algorithm (accumulating
+      // rounding errors?).
+      if (getArea(c) > 0) {
+        children.add(c);
+      }
+    }
+    children.sort((a, b) => getArea(b) - getArea(a));
+
+    final double scale = width * height / getArea(node);
+
+    // Bruls M., Huizing K., van Wijk J.J. (2000) Squarified Treemaps. In: de
+    // Leeuw W.C., van Liere R. (eds) Data Visualization 2000. Eurographics.
+    // Springer, Vienna.
+    for (int rowStart = 0; // Index of first child in the next row.
+        rowStart < children.length;) {
+      // Prefer wider rectangles, the better to fit text labels.
+      const double GOLDEN_RATIO = 1.61803398875;
+      final bool verticalSplit = (width / height) > GOLDEN_RATIO;
+
+      double space;
+      if (verticalSplit) {
+        space = height;
+      } else {
+        space = width;
+      }
+
+      double rowMin = getArea(children[rowStart]) * scale;
+      double rowMax = rowMin;
+      double rowSum = 0.0;
+      double lastRatio = 0.0;
+
+      int rowEnd; // One after index of last child in the next row.
+      for (rowEnd = rowStart; rowEnd < children.length; rowEnd++) {
+        double size = getArea(children[rowEnd]) * scale;
+        if (size < rowMin) rowMin = size;
+        if (size > rowMax) rowMax = size;
+        rowSum += size;
+
+        double ratio = Math.max((space * space * rowMax) / (rowSum * rowSum),
+            (rowSum * rowSum) / (space * space * rowMin));
+        if ((lastRatio != 0) && (ratio > lastRatio)) {
+          // Adding the next child makes the aspect ratios worse: remove it and
+          // add the row.
+          rowSum -= size;
+          break;
+        }
+        lastRatio = ratio;
+      }
+
+      double rowLeft = left;
+      double rowTop = top;
+      double rowSpace = rowSum / space;
+
+      for (int i = rowStart; i < rowEnd; i++) {
+        T child = children[i];
+        double size = getArea(child) * scale;
+
+        double childWidth;
+        double childHeight;
+        if (verticalSplit) {
+          childWidth = rowSpace;
+          childHeight = size / childWidth;
+        } else {
+          childHeight = rowSpace;
+          childWidth = size / childHeight;
+        }
+
+        Element childDiv = _createTreemapTile(
+            child, childWidth, childHeight, depth + 1, content);
+        childDiv.style.left = "${rowLeft}px";
+        childDiv.style.top = "${rowTop}px";
+        // Oversize the final div by kBorder to make the borders overlap.
+        childDiv.style.width = "${childWidth + kBorder}px";
+        childDiv.style.height = "${childHeight + kBorder}px";
+        div.append(childDiv);
+
+        if (verticalSplit)
+          rowTop += childHeight;
+        else
+          rowLeft += childWidth;
+      }
+
+      if (verticalSplit) {
+        left += rowSpace;
+        width -= rowSpace;
+      } else {
+        top += rowSpace;
+        height -= rowSpace;
+      }
+
+      rowStart = rowEnd;
+    }
+
+    return div;
+  }
+}
+
+abstract class NormalTreeMap<T> extends TreeMap<T> {
+  int getSize(T node);
+  String getName(T node);
+  String getType(T node);
+
+  int getArea(T node) => getSize(node);
+  String getLabel(T node) {
+    String name = getName(node);
+    String size = Utils.formatSize(getSize(node));
+    return "$name [$size]";
+  }
+
+  String getBackground(T node) {
+    int hue = getType(node).hashCode % 360;
+    return "hsl($hue,60%,60%)";
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/type_arguments_ref.dart b/runtime/observatory_2/lib/src/elements/type_arguments_ref.dart
new file mode 100644
index 0000000..a8cbd8d
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/type_arguments_ref.dart
@@ -0,0 +1,59 @@
+// 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.
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/helpers/uris.dart';
+
+class TypeArgumentsRefElement extends CustomElement implements Renderable {
+  RenderingScheduler<TypeArgumentsRefElement> _r;
+
+  Stream<RenderedEvent<TypeArgumentsRefElement>> get onRendered =>
+      _r.onRendered;
+
+  M.IsolateRef _isolate;
+  M.TypeArgumentsRef _arguments;
+
+  M.IsolateRef get isolate => _isolate;
+  M.TypeArgumentsRef get arguments => _arguments;
+
+  factory TypeArgumentsRefElement(M.IsolateRef isolate, M.TypeArgumentsRef args,
+      {RenderingQueue queue}) {
+    assert(isolate != null);
+    assert(args != null);
+    TypeArgumentsRefElement e = new TypeArgumentsRefElement.created();
+    e._r = new RenderingScheduler<TypeArgumentsRefElement>(e, queue: queue);
+    e._isolate = isolate;
+    e._arguments = args;
+    return e;
+  }
+
+  TypeArgumentsRefElement.created() : super.created('type-arguments-ref');
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+  }
+
+  void render() {
+    final text = (_arguments.name == null || _arguments.name == '')
+        ? 'TypeArguments'
+        : _arguments.name;
+    children = <Element>[
+      new AnchorElement(href: Uris.inspect(_isolate, object: _arguments))
+        ..text = text
+    ];
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/unknown_ref.dart b/runtime/observatory_2/lib/src/elements/unknown_ref.dart
new file mode 100644
index 0000000..092bd3b
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/unknown_ref.dart
@@ -0,0 +1,57 @@
+// 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.
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/models.dart' as M show IsolateRef, UnknownObjectRef;
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/helpers/uris.dart';
+
+class UnknownObjectRefElement extends CustomElement implements Renderable {
+  RenderingScheduler<UnknownObjectRefElement> _r;
+
+  Stream<RenderedEvent<UnknownObjectRefElement>> get onRendered =>
+      _r.onRendered;
+
+  M.IsolateRef _isolate;
+  M.UnknownObjectRef _obj;
+
+  M.IsolateRef get isolate => _isolate;
+  M.UnknownObjectRef get obj => _obj;
+
+  factory UnknownObjectRefElement(M.IsolateRef isolate, M.UnknownObjectRef obj,
+      {RenderingQueue queue}) {
+    assert(isolate != null);
+    assert(obj != null);
+    UnknownObjectRefElement e = new UnknownObjectRefElement.created();
+    e._r = new RenderingScheduler<UnknownObjectRefElement>(e, queue: queue);
+    e._isolate = isolate;
+    e._obj = obj;
+    return e;
+  }
+
+  UnknownObjectRefElement.created() : super.created('unknown-ref');
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+  }
+
+  void render() {
+    children = <Element>[
+      new AnchorElement(href: Uris.inspect(_isolate, object: _obj))
+        ..classes = ['emphasize']
+        ..text = _obj.vmType
+    ];
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/unlinkedcall_ref.dart b/runtime/observatory_2/lib/src/elements/unlinkedcall_ref.dart
new file mode 100644
index 0000000..e305f1c
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/unlinkedcall_ref.dart
@@ -0,0 +1,61 @@
+// Copyright (c) 2017, 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.
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/models.dart' as M show IsolateRef, UnlinkedCallRef;
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/helpers/uris.dart';
+
+class UnlinkedCallRefElement extends CustomElement implements Renderable {
+  RenderingScheduler<UnlinkedCallRefElement> _r;
+
+  Stream<RenderedEvent<UnlinkedCallRefElement>> get onRendered => _r.onRendered;
+
+  M.IsolateRef _isolate;
+  M.UnlinkedCallRef _unlinkedcall;
+
+  M.IsolateRef get isolate => _isolate;
+  M.UnlinkedCallRef get unlinkedcall => _unlinkedcall;
+
+  factory UnlinkedCallRefElement(
+      M.IsolateRef isolate, M.UnlinkedCallRef unlinkedcall,
+      {RenderingQueue queue}) {
+    assert(isolate != null);
+    assert(unlinkedcall != null);
+    UnlinkedCallRefElement e = new UnlinkedCallRefElement.created();
+    e._r = new RenderingScheduler<UnlinkedCallRefElement>(e, queue: queue);
+    e._isolate = isolate;
+    e._unlinkedcall = unlinkedcall;
+    return e;
+  }
+
+  UnlinkedCallRefElement.created() : super.created('unlinkedcall-ref');
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+  }
+
+  void render() {
+    children = <Element>[
+      new AnchorElement(href: Uris.inspect(_isolate, object: _unlinkedcall))
+        ..children = <Element>[
+          new SpanElement()
+            ..classes = ['emphasize']
+            ..text = 'UnlinkedCall',
+          new SpanElement()..text = ' (${_unlinkedcall.selector})'
+        ]
+    ];
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/unlinkedcall_view.dart b/runtime/observatory_2/lib/src/elements/unlinkedcall_view.dart
new file mode 100644
index 0000000..76c29c0
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/unlinkedcall_view.dart
@@ -0,0 +1,161 @@
+// Copyright (c) 2017, 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.
+
+import 'dart:async';
+import 'dart:html';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/curly_block.dart';
+import 'package:observatory_2/src/elements/helpers/any_ref.dart';
+import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
+import 'package:observatory_2/src/elements/helpers/nav_menu.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/nav/isolate_menu.dart';
+import 'package:observatory_2/src/elements/nav/notify.dart';
+import 'package:observatory_2/src/elements/nav/refresh.dart';
+import 'package:observatory_2/src/elements/nav/top_menu.dart';
+import 'package:observatory_2/src/elements/nav/vm_menu.dart';
+import 'package:observatory_2/src/elements/object_common.dart';
+import 'package:observatory_2/src/elements/view_footer.dart';
+
+class UnlinkedCallViewElement extends CustomElement implements Renderable {
+  RenderingScheduler<UnlinkedCallViewElement> _r;
+
+  Stream<RenderedEvent<UnlinkedCallViewElement>> get onRendered =>
+      _r.onRendered;
+
+  M.VM _vm;
+  M.IsolateRef _isolate;
+  M.EventRepository _events;
+  M.NotificationRepository _notifications;
+  M.UnlinkedCall _unlinkedcall;
+  M.UnlinkedCallRepository _unlinkedcalls;
+  M.RetainedSizeRepository _retainedSizes;
+  M.ReachableSizeRepository _reachableSizes;
+  M.InboundReferencesRepository _references;
+  M.RetainingPathRepository _retainingPaths;
+  M.ObjectRepository _objects;
+
+  M.VMRef get vm => _vm;
+  M.IsolateRef get isolate => _isolate;
+  M.NotificationRepository get notifications => _notifications;
+  M.UnlinkedCall get unlinkedcall => _unlinkedcall;
+
+  factory UnlinkedCallViewElement(
+      M.VM vm,
+      M.IsolateRef isolate,
+      M.UnlinkedCall unlinkedcall,
+      M.EventRepository events,
+      M.NotificationRepository notifications,
+      M.UnlinkedCallRepository unlinkedcalls,
+      M.RetainedSizeRepository retainedSizes,
+      M.ReachableSizeRepository reachableSizes,
+      M.InboundReferencesRepository references,
+      M.RetainingPathRepository retainingPaths,
+      M.ObjectRepository objects,
+      {RenderingQueue queue}) {
+    assert(vm != null);
+    assert(isolate != null);
+    assert(events != null);
+    assert(notifications != null);
+    assert(unlinkedcall != null);
+    assert(unlinkedcalls != null);
+    assert(retainedSizes != null);
+    assert(reachableSizes != null);
+    assert(references != null);
+    assert(retainingPaths != null);
+    assert(objects != null);
+    UnlinkedCallViewElement e = new UnlinkedCallViewElement.created();
+    e._r = new RenderingScheduler<UnlinkedCallViewElement>(e, queue: queue);
+    e._vm = vm;
+    e._isolate = isolate;
+    e._events = events;
+    e._notifications = notifications;
+    e._unlinkedcall = unlinkedcall;
+    e._unlinkedcalls = unlinkedcalls;
+    e._retainedSizes = retainedSizes;
+    e._reachableSizes = reachableSizes;
+    e._references = references;
+    e._retainingPaths = retainingPaths;
+    e._objects = objects;
+    return e;
+  }
+
+  UnlinkedCallViewElement.created() : super.created('unlinkedcall-view');
+
+  @override
+  attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+  }
+
+  void render() {
+    children = <Element>[
+      navBar(<Element>[
+        new NavTopMenuElement(queue: _r.queue).element,
+        new NavVMMenuElement(_vm, _events, queue: _r.queue).element,
+        new NavIsolateMenuElement(_isolate, _events, queue: _r.queue).element,
+        navMenu('unlinkedcall'),
+        (new NavRefreshElement(queue: _r.queue)
+              ..onRefresh.listen((e) async {
+                e.element.disabled = true;
+                _unlinkedcall =
+                    await _unlinkedcalls.get(_isolate, _unlinkedcall.id);
+                _r.dirty();
+              }))
+            .element,
+        new NavNotifyElement(_notifications, queue: _r.queue).element
+      ]),
+      new DivElement()
+        ..classes = ['content-centered-big']
+        ..children = <Element>[
+          new HeadingElement.h2()..text = 'UnlinkedCall',
+          new HRElement(),
+          new ObjectCommonElement(_isolate, _unlinkedcall, _retainedSizes,
+                  _reachableSizes, _references, _retainingPaths, _objects,
+                  queue: _r.queue)
+              .element,
+          new DivElement()
+            ..classes = ['memberList']
+            ..children = <Element>[
+              new DivElement()
+                ..classes = ['memberItem']
+                ..children = <Element>[
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..text = 'selector',
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..text = _unlinkedcall.selector
+                ],
+              new DivElement()
+                ..classes = ['memberItem']
+                ..children = <Element>[
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..text = 'argumentsDescriptor',
+                  new DivElement()
+                    ..classes = ['memberName']
+                    ..children = <Element>[
+                      _unlinkedcall.argumentsDescriptor == null
+                          ? (new SpanElement()..text = '<none>')
+                          : anyRef(_isolate, _unlinkedcall.argumentsDescriptor,
+                              _objects,
+                              queue: _r.queue)
+                    ]
+                ]
+            ],
+          new HRElement(),
+          new ViewFooterElement(queue: _r.queue).element
+        ]
+    ];
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/view_footer.dart b/runtime/observatory_2/lib/src/elements/view_footer.dart
new file mode 100644
index 0000000..d3fddb7
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/view_footer.dart
@@ -0,0 +1,49 @@
+// Copyright (c) 2015, 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.
+
+library view_footer_element;
+
+import 'dart:html';
+import 'dart:async';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+
+class ViewFooterElement extends CustomElement implements Renderable {
+  RenderingScheduler<ViewFooterElement> _r;
+
+  Stream<RenderedEvent<ViewFooterElement>> get onRendered => _r.onRendered;
+
+  factory ViewFooterElement({RenderingQueue queue}) {
+    ViewFooterElement e = new ViewFooterElement.created();
+    e._r = new RenderingScheduler<ViewFooterElement>(e, queue: queue);
+    return e;
+  }
+
+  ViewFooterElement.created() : super.created('view-footer');
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    children = <Element>[];
+    _r.disable(notify: true);
+  }
+
+  void render() {
+    children = <Element>[
+      new AnchorElement()
+        ..href = 'https://dart-lang.github.io/observatory/'
+        ..text = 'View documentation',
+      new AnchorElement()
+        ..href =
+            'https://github.com/dart-lang/sdk/issues/new?title=Observatory:&amp;body=Observatory%20Feedback'
+        ..text = 'File a bug report'
+    ];
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/vm_connect.dart b/runtime/observatory_2/lib/src/elements/vm_connect.dart
new file mode 100644
index 0000000..fe30bc9
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/vm_connect.dart
@@ -0,0 +1,168 @@
+// Copyright (c) 2014, 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.
+
+library vm_connect_element;
+
+import 'dart:async';
+import 'dart:html';
+
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/nav/notify.dart';
+import 'package:observatory_2/src/elements/nav/top_menu.dart';
+import 'package:observatory_2/src/elements/view_footer.dart';
+import 'package:observatory_2/src/elements/vm_connect_target.dart';
+
+class VMConnectElement extends CustomElement implements Renderable {
+  RenderingScheduler<VMConnectElement> _r;
+
+  Stream<RenderedEvent<VMConnectElement>> get onRendered => _r.onRendered;
+
+  M.NotificationRepository _notifications;
+  M.TargetRepository _targets;
+  StreamSubscription _targetsSubscription;
+
+  String _address;
+
+  factory VMConnectElement(
+      M.TargetRepository targets, M.NotificationRepository notifications,
+      {String address: '', RenderingQueue queue}) {
+    assert(address != null);
+    assert(notifications != null);
+    assert(targets != null);
+    VMConnectElement e = new VMConnectElement.created();
+    e._r = new RenderingScheduler<VMConnectElement>(e, queue: queue);
+    e._address = address;
+    e._notifications = notifications;
+    e._targets = targets;
+    return e;
+  }
+
+  VMConnectElement.created() : super.created('vm-connect');
+
+  @override
+  void attached() {
+    super.attached();
+    _targetsSubscription = _targets.onChange.listen((_) => _r.dirty());
+    _r.enable();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    children = <Element>[];
+    _r.disable(notify: true);
+    _targetsSubscription.cancel();
+  }
+
+  void render() {
+    final host = window.location.hostname;
+    final port = window.location.port;
+    children = <Element>[
+      navBar(<Element>[
+        new NavTopMenuElement(queue: _r.queue).element,
+        new NavNotifyElement(_notifications, queue: _r.queue).element
+      ]),
+      new DivElement()
+        ..classes = ['content-centered']
+        ..children = <Element>[
+          new HeadingElement.h1()..text = 'Connect to a Dart VM',
+          new HRElement(),
+          new BRElement(),
+          new DivElement()
+            ..classes = ['flex-row']
+            ..children = <Element>[
+              new DivElement()
+                ..classes = ['flex-item-40-percent']
+                ..children = <Element>[
+                  new HeadingElement.h2()..text = 'Connect over WebSocket',
+                  new BRElement(),
+                  new UListElement()
+                    ..children = _targets.list().map<Element>((target) {
+                      final bool current = _targets.isConnectedVMTarget(target);
+                      return new LIElement()
+                        ..children = <Element>[
+                          (new VMConnectTargetElement(target,
+                                  current: current, queue: _r.queue)
+                                ..onConnect.listen(_connect)
+                                ..onDelete.listen(_delete))
+                              .element
+                        ];
+                    }).toList(),
+                  new HRElement(),
+                  new FormElement()
+                    ..autocomplete = 'on'
+                    ..children = <Element>[
+                      _createAddressBox(),
+                      new SpanElement()..text = ' ',
+                      new ButtonElement()
+                        ..classes = ['vm_connect']
+                        ..text = 'Connect'
+                        ..onClick.listen((e) {
+                          e.preventDefault();
+                          _createAndConnect();
+                        }),
+                    ],
+                  new BRElement(),
+                  new PreElement()
+                    ..classes = ['well']
+                    ..text = 'Run Standalone with: \'--observe\'',
+                ],
+              new DivElement()..classes = ['flex-item-20-percent'],
+            ],
+        ],
+      new ViewFooterElement(queue: _r.queue).element
+    ];
+  }
+
+  TextInputElement _createAddressBox() {
+    var textbox = new TextInputElement()
+      ..classes = ['textbox']
+      ..placeholder = 'http://127.0.0.1:8181/...'
+      ..value = _address
+      ..onKeyUp.where((e) => e.key == '\n').listen((e) {
+        e.preventDefault();
+        _createAndConnect();
+      });
+    textbox.onInput.listen((e) {
+      _address = textbox.value;
+    });
+    return textbox;
+  }
+
+  void _createAndConnect() {
+    if (_address == null || _address.isEmpty) return;
+    String normalizedNetworkAddress = _normalizeStandaloneAddress(_address);
+    _targets.add(normalizedNetworkAddress);
+    var target = _targets.find(normalizedNetworkAddress);
+    assert(target != null);
+    _targets.setCurrent(target);
+    // the navigation to the VM page is done in the ObservatoryApplication
+  }
+
+  void _connect(TargetEvent e) {
+    _targets.setCurrent(e.target);
+  }
+
+  void _delete(TargetEvent e) => _targets.delete(e.target);
+
+  static String _normalizeStandaloneAddress(String networkAddress) {
+    if (!networkAddress.startsWith('http') &&
+        !networkAddress.startsWith('ws')) {
+      networkAddress = 'http://$networkAddress';
+    }
+    try {
+      Uri uri = Uri.parse(networkAddress);
+      if (uri.path.endsWith('/ws')) {
+        return 'ws://${uri.authority}${uri.path}';
+      }
+      return 'ws://${uri.authority}${uri.path}/ws';
+    } catch (e) {
+      print('caught exception with: $networkAddress -- $e');
+      return networkAddress;
+    }
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/vm_connect_target.dart b/runtime/observatory_2/lib/src/elements/vm_connect_target.dart
new file mode 100644
index 0000000..2b90b81
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/vm_connect_target.dart
@@ -0,0 +1,100 @@
+// Copyright (c) 2016, 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.
+
+import 'dart:async';
+import 'dart:html';
+import 'package:observatory_2/models.dart' as M show Target;
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+
+class TargetEvent {
+  final M.Target target;
+
+  TargetEvent(this.target);
+}
+
+class VMConnectTargetElement extends CustomElement implements Renderable {
+  RenderingScheduler<VMConnectTargetElement> _r;
+
+  Stream<RenderedEvent<VMConnectTargetElement>> get onRendered => _r.onRendered;
+
+  final StreamController<TargetEvent> _onConnect =
+      new StreamController<TargetEvent>.broadcast();
+  Stream<TargetEvent> get onConnect => _onConnect.stream;
+  final StreamController<TargetEvent> _onDelete =
+      new StreamController<TargetEvent>.broadcast();
+  Stream<TargetEvent> get onDelete => _onDelete.stream;
+
+  M.Target _target;
+  bool _current;
+
+  M.Target get target => _target;
+  bool get current => _current;
+
+  factory VMConnectTargetElement(M.Target target,
+      {bool current: false, RenderingQueue queue}) {
+    assert(target != null);
+    assert(current != null);
+    VMConnectTargetElement e = new VMConnectTargetElement.created();
+    e._r = new RenderingScheduler<VMConnectTargetElement>(e, queue: queue);
+    e._target = target;
+    e._current = current;
+    return e;
+  }
+
+  VMConnectTargetElement.created() : super.created('vm-connect-target');
+
+  @override
+  void attached() {
+    super.attached();
+    _r.enable();
+  }
+
+  @override
+  void detached() {
+    super.detached();
+    children = <Element>[];
+    _r.disable(notify: true);
+  }
+
+  void connect() {
+    _connect(new TargetEvent(target));
+  }
+
+  void delete() {
+    _delete(new TargetEvent(target));
+  }
+
+  void render() {
+    children = <Element>[
+      new AnchorElement()
+        ..text = current ? '${target.name} (Connected)' : '${target.name}'
+        ..onClick.where(_filter).map(_toEvent).listen(_connect),
+      new ButtonElement()
+        ..text = '✖ Remove'
+        ..classes = ['delete-button']
+        ..onClick.map(_toEvent).listen(_delete)
+    ];
+  }
+
+  void _connect(TargetEvent e) {
+    _onConnect.add(e);
+  }
+
+  void _delete(TargetEvent e) {
+    _onDelete.add(e);
+  }
+
+  TargetEvent _toEvent(_) {
+    return new TargetEvent(target);
+  }
+
+  static bool _filter(MouseEvent event) {
+    return !(event.button > 0 ||
+        event.metaKey ||
+        event.ctrlKey ||
+        event.shiftKey ||
+        event.altKey);
+  }
+}
diff --git a/runtime/observatory_2/lib/src/elements/vm_view.dart b/runtime/observatory_2/lib/src/elements/vm_view.dart
new file mode 100644
index 0000000..014cd50
--- /dev/null
+++ b/runtime/observatory_2/lib/src/elements/vm_view.dart
@@ -0,0 +1,367 @@
+// 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.
+
+library vm_view_element;
+
+import 'dart:async';
+import 'dart:html';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
+import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
+import 'package:observatory_2/src/elements/helpers/custom_element.dart';
+import 'package:observatory_2/src/elements/helpers/uris.dart';
+import 'package:observatory_2/src/elements/isolate/summary.dart';
+import 'package:observatory_2/src/elements/nav/notify.dart';
+import 'package:observatory_2/src/elements/nav/refresh.dart';
+import 'package:observatory_2/src/elements/nav/top_menu.dart';
+import 'package:observatory_2/src/elements/nav/vm_menu.dart';
+import 'package:observatory_2/src/elements/view_footer.dart';
+import 'package:observatory_2/utils.dart';
+
+class VMViewElement extends CustomElement implements Renderable {
+  RenderingScheduler<VMViewElement> _r;
+
+  Stream<RenderedEvent<VMViewElement>> get onRendered => _r.onRendered;
+
+  M.VM _vm;
+  M.VMRepository _vms;
+  M.EventRepository _events;
+  M.NotificationRepository _notifications;
+  M.IsolateRepository _isolates;
+  M.IsolateGroupRepository _isolateGroups;
+  M.ScriptRepository _scripts;
+  StreamSubscription _vmSubscription;
+  StreamSubscription _startSubscription;
+  StreamSubscription _exitSubscription;
+
+  M.VMRef get vm => _vm;
+  M.NotificationRepository get notifications => _notifications;
+
+  factory VMViewElement(
+      M.VM vm,
+      M.VMRepository vms,
+      M.EventRepository events,
+      M.NotificationRepository notifications,
+      M.IsolateRepository isolates,
+      M.IsolateGroupRepository isolateGroups,
+      M.ScriptRepository scripts,
+      {RenderingQueue queue}) {
+    assert(vm != null);
+    assert(vms != null);
+    assert(events != null);
+    assert(notifications != null);
+    assert(isolates != null);
+    assert(scripts != null);
+    VMViewElement e = new VMViewElement.created();
+    e._r = new RenderingScheduler<VMViewElement>(e, queue: queue);
+    e._vm = vm;
+    e._vms = vms;
+    e._events = events;
+    e._notifications = notifications;
+    e._isolates = isolates;
+    e._isolateGroups = isolateGroups;
+    e._scripts = scripts;
+    return e;
+  }
+
+  VMViewElement.created() : super.created('vm-view');
+
+  @override
+  attached() {
+    super.attached();
+    _r.enable();
+    _vmSubscription = _events.onVMUpdate.listen((e) {
+      _vm = e.vm;
+      _r.dirty();
+    });
+    _startSubscription = _events.onIsolateStart.listen((_) => _r.dirty());
+    _exitSubscription = _events.onIsolateExit.listen((_) => _r.dirty());
+    _loadExtraData();
+  }
+
+  @override
+  detached() {
+    super.detached();
+    _r.disable(notify: true);
+    children = <Element>[];
+    _vmSubscription.cancel();
+    _startSubscription.cancel();
+    _exitSubscription.cancel();
+  }
+
+  Future _loadExtraData() async {
+    for (var group in _vm.isolateGroups) {
+      await _isolateGroups.get(group);
+    }
+    for (var group in _vm.systemIsolateGroups) {
+      await _isolateGroups.get(group);
+    }
+    _r.dirty();
+  }
+
+  void render() {
+    children = <Element>[
+      navBar(<Element>[
+        new NavTopMenuElement(queue: _r.queue).element,
+        new NavVMMenuElement(_vm, _events, queue: _r.queue).element,
+        (new NavRefreshElement(queue: _r.queue)
+              ..onRefresh.listen((e) async {
+                e.element.disabled = true;
+                _vm = await _vms.get(_vm);
+                _loadExtraData();
+                _r.dirty();
+              }))
+            .element,
+        new NavNotifyElement(_notifications, queue: _r.queue).element
+      ]),
+      describeProcess(),
+      describeVM(),
+      describeIsolateGroups(),
+      describeSystemIsolateGroups(),
+      new ViewFooterElement(queue: _r.queue).element
+    ];
+  }
+
+  Element describeProcess() {
+    return new DivElement()
+      ..classes = ['content-centered-big']
+      ..children = <HtmlElement>[
+        new HeadingElement.h1()..text = 'Process',
+        new DivElement()
+          ..classes = ['memberList']
+          ..children = <Element>[
+            new DivElement()
+              ..classes = ['memberItem']
+              ..children = <Element>[
+                new DivElement()
+                  ..classes = ['memberName']
+                  ..text = 'pid',
+                new DivElement()
+                  ..classes = ['memberValue']
+                  ..text = '${_vm.pid}'
+              ],
+            new DivElement()
+              ..classes = ['memberItem']
+              ..children = <Element>[
+                new DivElement()
+                  ..classes = ['memberName']
+                  ..text = 'current memory'
+                  ..title =
+                      'current value of the resident set size of the process running this VM',
+                new DivElement()
+                  ..classes = ['memberValue']
+                  ..text = _vm.currentRSS != null
+                      ? Utils.formatSize(_vm.currentRSS)
+                      : "unavailable"
+              ],
+            new DivElement()
+              ..classes = ['memberItem']
+              ..children = <Element>[
+                new DivElement()
+                  ..classes = ['memberName']
+                  ..text = 'peak memory'
+                  ..title =
+                      'highest value of the resident set size of the process running this VM',
+                new DivElement()
+                  ..classes = ['memberValue']
+                  ..text = _vm.maxRSS != null
+                      ? Utils.formatSize(_vm.maxRSS)
+                      : "unavailable"
+              ],
+            new DivElement()
+              ..classes = ['memberItem']
+              ..children = <Element>[
+                new DivElement()
+                  ..classes = ['memberName']
+                  ..text = 'malloc memory',
+                new DivElement()
+                  ..classes = ['memberValue']
+                  ..text = _vm.heapAllocatedMemoryUsage != null
+                      ? Utils.formatSize(_vm.heapAllocatedMemoryUsage)
+                      : 'unavailable'
+                  ..title = _vm.heapAllocatedMemoryUsage != null
+                      ? '${_vm.heapAllocatedMemoryUsage} bytes'
+                      : null
+              ],
+            new DivElement()
+              ..classes = ['memberItem']
+              ..children = <Element>[
+                new DivElement()
+                  ..classes = ['memberName']
+                  ..text = 'malloc allocation count',
+                new DivElement()
+                  ..classes = ['memberValue']
+                  ..text = _vm.heapAllocationCount != null
+                      ? '${_vm.heapAllocationCount}'
+                      : 'unavailable'
+              ],
+            new DivElement()
+              ..classes = ['memberItem']
+              ..children = <Element>[
+                new DivElement()
+                  ..classes = ['memberName']
+                  ..children = <Element>[
+                    new SpanElement()..text = 'view ',
+                    new AnchorElement(href: Uris.nativeMemory())
+                      ..text = 'malloc profile'
+                  ],
+                new DivElement()
+                  ..classes = ['memberName']
+                  ..children = <Element>[
+                    new SpanElement()..text = 'view ',
+                    new AnchorElement(href: Uris.processSnapshot())
+                      ..text = 'process memory'
+                  ]
+              ]
+          ],
+        new BRElement(),
+      ];
+  }
+
+  Element describeVM() {
+    final uptime = new DateTime.now().difference(_vm.startTime);
+    return new DivElement()
+      ..classes = ['content-centered-big']
+      ..children = <HtmlElement>[
+        new HeadingElement.h1()..text = 'VM',
+        new DivElement()
+          ..classes = ['memberList']
+          ..children = <Element>[
+            new DivElement()
+              ..classes = ['memberItem']
+              ..children = <Element>[
+                new DivElement()
+                  ..classes = ['memberName']
+                  ..text = 'name',
+                new DivElement()
+                  ..classes = ['memberValue']
+                  ..text = _vm.displayName
+              ],
+            new DivElement()
+              ..classes = ['memberItem']
+              ..children = <Element>[
+                new DivElement()
+                  ..classes = ['memberName']
+                  ..text = 'version',
+                new DivElement()
+                  ..classes = ['memberValue']
+                  ..text = _vm.version
+              ],
+            new DivElement()
+              ..classes = ['memberItem']
+              ..children = <Element>[
+                new DivElement()
+                  ..classes = ['memberName']
+                  ..text = 'embedder',
+                new DivElement()
+                  ..classes = ['memberValue']
+                  ..text = _vm.embedder ?? "UNKNOWN"
+              ],
+            new DivElement()
+              ..classes = ['memberItem']
+              ..children = <Element>[
+                new DivElement()
+                  ..classes = ['memberName']
+                  ..text = 'current memory'
+                  ..title = 'current amount of memory consumed by the Dart VM',
+                new DivElement()
+                  ..classes = ['memberValue']
+                  ..text = _vm.currentMemory != null
+                      ? Utils.formatSize(_vm.currentMemory)
+                      : "unavailable"
+              ],
+            new DivElement()
+              ..classes = ['memberItem']
+              ..children = <Element>[
+                new DivElement()
+                  ..classes = ['memberName']
+                  ..text = 'started at',
+                new DivElement()
+                  ..classes = ['memberValue']
+                  ..text = '${_vm.startTime}'
+              ],
+            new DivElement()
+              ..classes = ['memberItem']
+              ..children = <Element>[
+                new DivElement()
+                  ..classes = ['memberName']
+                  ..text = 'uptime',
+                new DivElement()
+                  ..classes = ['memberValue']
+                  ..text = '$uptime'
+              ],
+            new DivElement()
+              ..classes = ['memberItem']
+              ..children = <Element>[
+                new DivElement()
+                  ..classes = ['memberName']
+                  ..text = 'refreshed at',
+                new DivElement()
+                  ..classes = ['memberValue']
+                  ..text = '${new DateTime.now()}'
+              ],
+            new BRElement(),
+            new DivElement()
+              ..classes = ['memberItem']
+              ..children = <Element>[
+                new DivElement()
+                  ..classes = ['memberName']
+                  ..children = <Element>[
+                    new SpanElement()..text = 'see ',
+                    new AnchorElement(href: Uris.flags())..text = 'flags'
+                  ],
+                new DivElement()
+                  ..classes = ['memberValue']
+                  ..children = <Element>[
+                    new SpanElement()..text = 'view ',
+                    new AnchorElement(href: Uris.timeline())..text = 'timeline'
+                  ]
+              ],
+          ],
+        new BRElement(),
+      ];
+  }
+
+  Element describeIsolateGroups() {
+    final isolateGroups = _vm.isolateGroups.toList();
+    return new DivElement()
+      ..children = isolateGroups.map(describeIsolateGroup).toList();
+  }
+
+  Element describeSystemIsolateGroups() {
+    final isolateGroups = _vm.systemIsolateGroups.toList();
+    return new DivElement()
+      ..children = isolateGroups.map(describeIsolateGroup).toList();
+  }
+
+  Element describeIsolateGroup(M.IsolateGroupRef group) {
+    final isolateType =
+        group.isSystemIsolateGroup ? 'System Isolate' : 'Isolate';
+    final isolates = (group as M.IsolateGroup).isolates;
+    return new DivElement()
+      ..classes = ['content-centered-big']
+      ..children = <Element>[
+        new HRElement(),
+        new HeadingElement.h1()
+          ..text = "$isolateType Group ${group.number} (${group.name})",
+        new LIElement()
+          ..classes = ['list-group-item']
+          ..children = <Element>[
+            new UListElement()
+              ..classes = ['list-group']
+              ..children = isolates.map(describeIsolate).toList(),
+          ],
+      ];
+  }
+
+  Element describeIsolate(M.IsolateRef isolate) {
+    return new LIElement()
+      ..classes = ['list-group-item']
+      ..children = <Element>[
+        new IsolateSummaryElement(isolate, _isolates, _events, _scripts,
+                queue: _r.queue)
+            .element
+      ];
+  }
+}
diff --git a/runtime/observatory_2/lib/src/models/exceptions.dart b/runtime/observatory_2/lib/src/models/exceptions.dart
new file mode 100644
index 0000000..81a7ba7
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/exceptions.dart
@@ -0,0 +1,49 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class BasicException implements Exception {
+  String get message;
+}
+
+abstract class ConnectionException implements BasicException {}
+
+abstract class ResponseException implements BasicException {}
+
+abstract class RequestException implements BasicException {}
+
+abstract class ParseErrorException implements RequestException {}
+
+abstract class InvalidRequestException implements RequestException {}
+
+abstract class MethodNotFoundException implements RequestException {}
+
+abstract class InvalidParamsException implements RequestException {}
+
+abstract class InternalErrorException implements RequestException {}
+
+abstract class FeatureDisabledException implements RequestException {}
+
+abstract class CannotAddBreakpointException implements RequestException {}
+
+abstract class StreamAlreadySubscribedException implements RequestException {}
+
+abstract class StreamNotSubscribedException implements RequestException {}
+
+abstract class IsolateMustBeRunnableException implements RequestException {}
+
+abstract class IsolateMustBePausedException implements RequestException {}
+
+abstract class IsolateIsReloadingException implements RequestException {}
+
+abstract class FileSystemAlreadyExistsException implements RequestException {}
+
+abstract class FileSystemDoesNotExistException implements RequestException {}
+
+abstract class FileDoesNotExistException implements RequestException {}
+
+abstract class IsolateReloadFailedException implements RequestException {}
+
+abstract class UnknownException implements RequestException {}
diff --git a/runtime/observatory_2/lib/src/models/objects/allocation_profile.dart b/runtime/observatory_2/lib/src/models/objects/allocation_profile.dart
new file mode 100644
index 0000000..f9aa5a6
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/objects/allocation_profile.dart
@@ -0,0 +1,31 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class AllocationProfile {
+  DateTime get lastServiceGC;
+  DateTime get lastAccumulatorReset;
+  HeapSpace get newSpace;
+  HeapSpace get oldSpace;
+  HeapSpace get totalSpace;
+  Iterable<ClassHeapStats> get members;
+}
+
+abstract class ClassHeapStats {
+  /// [Optional] at least one between clazz and displayName should be non null
+  ClassRef get clazz;
+
+  /// [Optional] at least one between clazz and displayName should be non null
+  String get displayName;
+  Allocations get newSpace;
+  Allocations get oldSpace;
+}
+
+abstract class Allocations {
+  int instances = 0;
+  int internalSize = 0;
+  int externalSize = 0;
+  int size = 0;
+}
diff --git a/runtime/observatory_2/lib/src/models/objects/breakpoint.dart b/runtime/observatory_2/lib/src/models/objects/breakpoint.dart
new file mode 100644
index 0000000..28ed56c
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/objects/breakpoint.dart
@@ -0,0 +1,21 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class Breakpoint extends Object {
+  /// A number identifying this breakpoint to the user.
+  int get number;
+
+  /// Has this breakpoint been assigned to a specific program location?
+  bool get resolved;
+
+  /// [optional]Is this a breakpoint that was added synthetically as part of a
+  /// step OverAsyncSuspension resume command?
+  bool get isSyntheticAsyncContinuation;
+
+  /// SourceLocation when breakpoint is resolved, UnresolvedSourceLocation
+  /// when a breakpoint is not resolved.
+  Location get location;
+}
diff --git a/runtime/observatory_2/lib/src/models/objects/class.dart b/runtime/observatory_2/lib/src/models/objects/class.dart
new file mode 100644
index 0000000..47a1a34
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/objects/class.dart
@@ -0,0 +1,73 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class ClassRef extends ObjectRef {
+  /// The name of this class.
+  String get name;
+}
+
+abstract class Class extends Object implements ClassRef {
+  /// The error which occurred during class finalization, if it exists.
+  /// [optional]
+  ErrorRef get error;
+
+  /// Is this an abstract class?
+  bool get isAbstract;
+
+  /// Is this a const class?
+  bool get isConst;
+
+  /// [internal]
+  bool get isPatch;
+
+  /// [optional] The library which contains this class.
+  LibraryRef get library;
+
+  /// [optional] The location of this class in the source code.
+  SourceLocation get location;
+
+  /// [optional] The superclass of this class, if any.
+  ClassRef get superclass;
+
+  /// [optional]The supertype for this class, if any.
+  ///
+  /// The value will be of the kind: Type.
+  InstanceRef get superType;
+
+  /// A list of interface types for this class.
+  ///
+  /// The values will be of the kind: Type.
+  Iterable<InstanceRef> get interfaces;
+
+  /// The mixin type for this class, if any.
+  ///
+  /// [optional] The value will be of the kind: Type.
+  InstanceRef get mixin;
+
+  /// A list of fields in this class. Does not include fields from
+  /// superclasses.
+  Iterable<FieldRef> get fields;
+
+  /// A list of functions in this class. Does not include functions
+  /// from superclasses.
+  Iterable<FunctionRef> get functions;
+
+  // A list of subclasses of this class.
+  Iterable<ClassRef> get subclasses;
+
+  bool get hasAllocations;
+  bool get hasNoAllocations;
+
+  Allocations get newSpace;
+  Allocations get oldSpace;
+
+  bool get traceAllocations;
+}
+
+abstract class InstanceSet {
+  int get count;
+  Iterable<ObjectRef> get instances;
+}
diff --git a/runtime/observatory_2/lib/src/models/objects/code.dart b/runtime/observatory_2/lib/src/models/objects/code.dart
new file mode 100644
index 0000000..12d84df
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/objects/code.dart
@@ -0,0 +1,36 @@
+// Copyright (c) 2016, 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 models;
+
+enum CodeKind { dart, native, stub, tag, collected }
+
+bool isSyntheticCode(CodeKind kind) {
+  switch (kind) {
+    case CodeKind.collected:
+    case CodeKind.native:
+    case CodeKind.tag:
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool isDartCode(CodeKind kind) => !isSyntheticCode(kind);
+
+abstract class CodeRef extends ObjectRef {
+  /// The name of this class.
+  String get name;
+
+  // What kind of code object is this?
+  CodeKind get kind;
+
+  bool get isOptimized;
+}
+
+abstract class Code extends Object implements CodeRef {
+  FunctionRef get function;
+  ObjectPoolRef get objectPool;
+  Iterable<FunctionRef> get inlinedFunctions;
+}
diff --git a/runtime/observatory_2/lib/src/models/objects/context.dart b/runtime/observatory_2/lib/src/models/objects/context.dart
new file mode 100644
index 0000000..7378086
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/objects/context.dart
@@ -0,0 +1,22 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class ContextRef extends ObjectRef {
+  /// The number of variables in this context.
+  int get length;
+}
+
+abstract class Context extends Object implements ContextRef {
+  /// [optional] The enclosing context for this context.
+  Context get parentContext;
+
+  // The variables in this context object.
+  Iterable<ContextElement> get variables;
+}
+
+abstract class ContextElement {
+  Guarded<InstanceRef> get value;
+}
diff --git a/runtime/observatory_2/lib/src/models/objects/error.dart b/runtime/observatory_2/lib/src/models/objects/error.dart
new file mode 100644
index 0000000..56eff8b
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/objects/error.dart
@@ -0,0 +1,27 @@
+// Copyright (c) 2016, 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 models;
+
+enum ErrorKind {
+  /// The isolate has encountered an unhandled Dart exception.
+  unhandledException,
+
+  /// The isolate has encountered a Dart language error in the program.
+  languageError,
+
+  /// The isolate has encountered an internal error. These errors should be
+  /// reported as bugs.
+  internalError,
+
+  /// The isolate has been terminated by an external source.
+  terminationError
+}
+
+abstract class ErrorRef extends ObjectRef {
+  ErrorKind get kind;
+  String get message;
+}
+
+abstract class Error extends Object implements ErrorRef {}
diff --git a/runtime/observatory_2/lib/src/models/objects/event.dart b/runtime/observatory_2/lib/src/models/objects/event.dart
new file mode 100644
index 0000000..24ed7e3
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/objects/event.dart
@@ -0,0 +1,210 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class Event {
+  /// The timestamp (in milliseconds since the epoch) associated with this
+  /// event. For some isolate pause events, the timestamp is from when the
+  /// isolate was paused. For other events, the timestamp is from when the
+  /// event was created.
+  DateTime get timestamp;
+  static bool isPauseEvent(Event event) {
+    return event is PauseStartEvent ||
+        event is PauseExitEvent ||
+        event is PauseBreakpointEvent ||
+        event is PauseInterruptedEvent ||
+        event is PauseExceptionEvent ||
+        event is PausePostRequestEvent ||
+        event is NoneEvent;
+  }
+}
+
+abstract class VMEvent extends Event {
+  /// The vm with which this event is associated.
+  VMRef get vm;
+}
+
+abstract class VMUpdateEvent extends VMEvent {}
+
+abstract class IsolateEvent extends Event {
+  /// The isolate with which this event is associated.
+  IsolateRef get isolate;
+}
+
+abstract class IsolateStartEvent extends IsolateEvent {}
+
+abstract class IsolateRunnableEvent extends IsolateEvent {}
+
+abstract class IsolateExitEvent extends IsolateEvent {}
+
+abstract class IsolateUpdateEvent extends IsolateEvent {}
+
+abstract class IsolateReloadEvent extends IsolateEvent {
+  ErrorRef get error;
+}
+
+abstract class ServiceExtensionAddedEvent extends IsolateEvent {
+  /// The RPC name of the extension that was added.
+  String get extensionRPC;
+}
+
+abstract class DebugEvent extends Event {
+  /// The isolate with which this event is associated.
+  IsolateRef get isolate;
+}
+
+abstract class BreakpointEvent extends DebugEvent {
+  /// [optional] The breakpoint associated with this event.
+  Breakpoint get breakpoint;
+}
+
+abstract class PauseEvent extends DebugEvent {}
+
+abstract class AsyncSuspensionEvent extends PauseEvent {
+  /// Is the isolate paused at an await, yield, or yield* statement?
+  bool get atAsyncSuspension;
+}
+
+abstract class DebuggerSettingsUpdateEvent extends DebugEvent {}
+
+abstract class PauseStartEvent extends PauseEvent {}
+
+abstract class PauseExitEvent extends PauseEvent {}
+
+abstract class PauseBreakpointEvent extends AsyncSuspensionEvent {
+  /// [optional] The breakpoint at which we are currently paused.
+  Breakpoint get breakpoint;
+
+  /// The list of breakpoints at which we are currently paused
+  /// for a PauseBreakpoint event.
+  ///
+  /// This list may be empty. For example, while single-stepping, the
+  /// VM sends a PauseBreakpoint event with no breakpoints.
+  ///
+  /// If there is more than one breakpoint set at the program position,
+  /// then all of them will be provided.
+  Iterable<Breakpoint> get pauseBreakpoints;
+
+  /// The top stack frame associated with this event.
+  Frame get topFrame;
+}
+
+abstract class PauseInterruptedEvent extends AsyncSuspensionEvent {
+  /// [optional] The top stack frame associated with this event. There will be
+  /// no top frame if the isolate is idle (waiting in the message loop).
+  Frame get topFrame;
+}
+
+abstract class PausePostRequestEvent extends AsyncSuspensionEvent {
+  /// [optional] The top stack frame associated with this event. There will be
+  /// no top frame if the isolate is idle (waiting in the message loop).
+  Frame get topFrame;
+}
+
+abstract class PauseExceptionEvent extends PauseEvent {
+  /// The top stack frame associated with this event.
+  Frame get topFrame;
+
+  /// The exception associated with this event
+  InstanceRef get exception;
+}
+
+abstract class ResumeEvent extends DebugEvent {
+  /// [optional] The top stack frame associated with this event. It is provided
+  /// at all times except for the initial resume event that is delivered when an
+  /// isolate begins execution.
+  Frame get topFrame;
+}
+
+abstract class BreakpointAddedEvent extends BreakpointEvent {}
+
+abstract class BreakpointResolvedEvent extends BreakpointEvent {}
+
+abstract class BreakpointRemovedEvent extends BreakpointEvent {}
+
+abstract class InspectEvent extends DebugEvent {
+  /// The argument passed to dart:developer.inspect.
+  InstanceRef get inspectee;
+}
+
+abstract class NoneEvent extends PauseEvent {}
+
+abstract class GCEvent extends Event {
+  /// The isolate with which this event is associated.
+  IsolateRef get isolate;
+}
+
+abstract class ExtensionEvent extends Event {
+  /// The isolate with which this event is associated.
+  IsolateRef get isolate;
+
+  /// The extension event kind.
+  String get extensionKind;
+
+  /// The extension event data.
+  ExtensionData get extensionData;
+}
+
+abstract class LoggingEvent extends Event {
+  /// The isolate with which this event is associated.
+  IsolateRef get isolate;
+
+  // TODO(cbernaschina) objectify
+  Map get logRecord;
+}
+
+abstract class TimelineEventsEvent extends Event {
+  /// The isolate with which this event is associated.
+  IsolateRef get isolate;
+
+  /// An array of TimelineEvents
+  Iterable<TimelineEvent> get timelineEvents;
+}
+
+abstract class ConnectionClosedEvent extends Event {
+  /// The reason of the closed connection
+  String get reason;
+}
+
+Frame topFrame(DebugEvent event) {
+  if (event is PauseBreakpointEvent) {
+    return event.topFrame;
+  }
+  if (event is PauseInterruptedEvent) {
+    return event.topFrame;
+  }
+  if (event is PauseExceptionEvent) {
+    return event.topFrame;
+  }
+  if (event is ResumeEvent) {
+    return event.topFrame;
+  }
+  return null;
+}
+
+bool isAtAsyncSuspension(DebugEvent event) {
+  if (event is PauseBreakpointEvent) {
+    return event.atAsyncSuspension;
+  }
+  if (event is PauseInterruptedEvent) {
+    return event.atAsyncSuspension;
+  }
+  return false;
+}
+
+abstract class ServiceEvent extends Event {
+  /// The identifier of the service
+  String get service;
+
+  /// The JSON-RPC 2.0 Method that identifes this instance
+  String get method;
+}
+
+abstract class ServiceRegisteredEvent extends ServiceEvent {
+  /// The alias associated with this new instance
+  String get alias;
+}
+
+abstract class ServiceUnregisteredEvent extends ServiceEvent {}
diff --git a/runtime/observatory_2/lib/src/models/objects/extension_data.dart b/runtime/observatory_2/lib/src/models/objects/extension_data.dart
new file mode 100644
index 0000000..80a0012
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/objects/extension_data.dart
@@ -0,0 +1,7 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class ExtensionData implements Map<String, dynamic> {}
diff --git a/runtime/observatory_2/lib/src/models/objects/field.dart b/runtime/observatory_2/lib/src/models/objects/field.dart
new file mode 100644
index 0000000..ca59328c
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/objects/field.dart
@@ -0,0 +1,43 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class FieldRef extends ObjectRef {
+  /// The name of this field.
+  String get name;
+
+  /// The owner of this field, which can be either a Library or a
+  /// Class.
+  ObjectRef get dartOwner;
+
+  /// The declared type of this field.
+  ///
+  /// The value will always be of one of the kinds:
+  /// Type, TypeRef, TypeParameter.
+  InstanceRef get declaredType;
+
+  /// Is this field const?
+  bool get isConst;
+
+  /// Is this field final?
+  bool get isFinal;
+
+  /// Is this field static?
+  bool get isStatic;
+}
+
+enum GuardClassKind { unknown, single, dynamic }
+
+abstract class Field extends Object implements FieldRef {
+  /// [optional] The value of this field, if the field is static.
+  ObjectRef get staticValue;
+
+  /// [optional] The location of this field in the source code.
+  SourceLocation get location;
+
+  GuardClassKind get guardClassKind;
+  ClassRef get guardClass;
+  bool get guardNullable;
+}
diff --git a/runtime/observatory_2/lib/src/models/objects/flag.dart b/runtime/observatory_2/lib/src/models/objects/flag.dart
new file mode 100644
index 0000000..3813f88
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/objects/flag.dart
@@ -0,0 +1,21 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class Flag {
+  /// The name of the flag.
+  String get name;
+
+  /// A description of the flag.
+  String get comment;
+
+  /// Has this flag been modified from its default setting?
+  bool get modified;
+
+  /// The value of this flag as a string. [optional]
+  ///
+  /// If this property is absent, then the value of the flag was NULL.
+  String get valueAsString;
+}
diff --git a/runtime/observatory_2/lib/src/models/objects/frame.dart b/runtime/observatory_2/lib/src/models/objects/frame.dart
new file mode 100644
index 0000000..12140f6
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/objects/frame.dart
@@ -0,0 +1,14 @@
+// Copyright (c) 2016, 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 models;
+
+enum FrameKind { regular, asyncCausal, asyncSuspensionMarker, asyncActivation }
+
+abstract class Frame {
+  FrameKind get kind;
+  String get marker;
+  FunctionRef get function;
+  SourceLocation get location;
+}
diff --git a/runtime/observatory_2/lib/src/models/objects/function.dart b/runtime/observatory_2/lib/src/models/objects/function.dart
new file mode 100644
index 0000000..82d3d7f
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/objects/function.dart
@@ -0,0 +1,103 @@
+// Copyright (c) 2016, 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 models;
+
+enum FunctionKind {
+  regular,
+  closure,
+  implicitClosure,
+  getter,
+  setter,
+  constructor,
+  implicitGetter,
+  implicitSetter,
+  implicitStaticGetter,
+  fieldInitializer,
+  irregexpFunction,
+  methodExtractor,
+  noSuchMethodDispatcher,
+  invokeFieldDispatcher,
+  collected,
+  native,
+  ffiTrampoline,
+  stub,
+  tag,
+  signatureFunction,
+  dynamicInvocationForwarder
+}
+
+bool isSyntheticFunction(FunctionKind kind) {
+  switch (kind) {
+    case FunctionKind.collected:
+    case FunctionKind.native:
+    case FunctionKind.stub:
+    case FunctionKind.tag:
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool isDartFunction(FunctionKind kind) => !isSyntheticFunction(kind);
+bool isStubFunction(FunctionKind kind) => kind == FunctionKind.stub;
+bool hasDartCode(FunctionKind kind) =>
+    isDartFunction(kind) || isStubFunction(kind);
+
+String getFunctionFullName(FunctionRef function) {
+  var content = <String>[function.name];
+  ObjectRef owner = function.dartOwner;
+  while (owner is FunctionRef) {
+    FunctionRef function = (owner as FunctionRef);
+    content.add(function.name);
+    owner = function.dartOwner;
+  }
+  if (owner is ClassRef) {
+    content.add(owner.name);
+  }
+  return content.reversed.join('.');
+}
+
+abstract class FunctionRef extends ObjectRef {
+  /// The name of this class.
+  String get name;
+
+  /// The owner of this function, which can be a LibraryRef, ClassRef,
+  /// or a FunctionRef.
+  ObjectRef get dartOwner; // owner
+
+  /// Is this function static?
+  bool get isStatic;
+
+  /// Is this function const?
+  bool get isConst;
+
+  /// The kind of the function.
+  FunctionKind get kind;
+}
+
+abstract class ServiceFunction extends Object implements FunctionRef {
+  /// The location of this function in the source code. [optional]
+  SourceLocation get location;
+
+  /// The compiled code associated with this function. [optional]
+  CodeRef get code;
+
+  /// [optional]
+  CodeRef get unoptimizedCode;
+
+  /// [optional]
+  CodeRef get bytecode;
+
+  /// [optional]
+  FieldRef get field;
+  int get usageCounter;
+  InstanceRef get icDataArray;
+  int get deoptimizations;
+  bool get isOptimizable;
+  bool get isInlinable;
+  bool get hasIntrinsic;
+  bool get isRecognized;
+  bool get isNative;
+}
diff --git a/runtime/observatory_2/lib/src/models/objects/guarded.dart b/runtime/observatory_2/lib/src/models/objects/guarded.dart
new file mode 100644
index 0000000..82bf1ee
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/objects/guarded.dart
@@ -0,0 +1,12 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class Guarded<T> {
+  bool get isValue;
+  bool get isSentinel;
+  Sentinel get asSentinel;
+  T get asValue;
+}
diff --git a/runtime/observatory_2/lib/src/models/objects/heap_space.dart b/runtime/observatory_2/lib/src/models/objects/heap_space.dart
new file mode 100644
index 0000000..cf62fbf
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/objects/heap_space.dart
@@ -0,0 +1,15 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class HeapSpace {
+  int get used;
+  int get capacity;
+  int get collections;
+  int get external;
+  Duration get avgCollectionTime;
+  Duration get totalCollectionTime;
+  Duration get avgCollectionPeriod;
+}
diff --git a/runtime/observatory_2/lib/src/models/objects/icdata.dart b/runtime/observatory_2/lib/src/models/objects/icdata.dart
new file mode 100644
index 0000000..d975301
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/objects/icdata.dart
@@ -0,0 +1,15 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class ICDataRef extends ObjectRef {
+  String get selector;
+}
+
+abstract class ICData extends Object implements ICDataRef {
+  ObjectRef get dartOwner;
+  InstanceRef get argumentsDescriptor;
+  InstanceRef get entries;
+}
diff --git a/runtime/observatory_2/lib/src/models/objects/inbound_references.dart b/runtime/observatory_2/lib/src/models/objects/inbound_references.dart
new file mode 100644
index 0000000..ea18744
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/objects/inbound_references.dart
@@ -0,0 +1,22 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class InboundReferences {
+  Iterable<InboundReference> get elements;
+}
+
+abstract class InboundReference {
+  ObjectRef get source;
+
+  /// [optional]
+  ObjectRef get parentField;
+
+  /// [optional]
+  int get parentListIndex;
+
+  /// [optional]
+  int get parentWordOffset;
+}
diff --git a/runtime/observatory_2/lib/src/models/objects/instance.dart b/runtime/observatory_2/lib/src/models/objects/instance.dart
new file mode 100644
index 0000000..66965e9
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/objects/instance.dart
@@ -0,0 +1,455 @@
+// Copyright (c) 2016, 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 models;
+
+enum InstanceKind {
+  /// A general instance of the Dart class Object.
+  plainInstance,
+
+  /// null instance.
+  vNull,
+
+  /// true or false.
+  bool,
+
+  /// An instance of the Dart class double.
+  double,
+
+  /// An instance of the Dart class int.
+  int,
+
+  /// An instance of the Dart class String.
+  string,
+
+  /// An instance of the built-in VM List implementation. User-defined
+  /// Lists will be PlainInstance.
+  list,
+
+  /// An instance of the built-in VM Map implementation. User-defined
+  /// Maps will be PlainInstance.
+  map,
+
+  /// Vector instance kinds.
+  float32x4,
+
+  /// Vector instance kinds.
+  float64x2,
+
+  /// Vector instance kinds.
+  int32x4,
+
+  /// An instance of the built-in VM TypedData implementations. User-defined
+  /// TypedDatas will be PlainInstance.
+  uint8ClampedList,
+
+  /// An instance of the built-in VM TypedData implementations. User-defined
+  /// TypedDatas will be PlainInstance.
+  uint8List,
+
+  /// An instance of the built-in VM TypedData implementations. User-defined
+  /// TypedDatas will be PlainInstance.
+  uint16List,
+
+  /// An instance of the built-in VM TypedData implementations. User-defined
+  /// TypedDatas will be PlainInstance.
+  uint32List,
+
+  /// An instance of the built-in VM TypedData implementations. User-defined
+  /// TypedDatas will be PlainInstance.
+  uint64List,
+
+  /// An instance of the built-in VM TypedData implementations. User-defined
+  /// TypedDatas will be PlainInstance.
+  int8List,
+
+  /// An instance of the built-in VM TypedData implementations. User-defined
+  /// TypedDatas will be PlainInstance.
+  int16List,
+
+  /// An instance of the built-in VM TypedData implementations. User-defined
+  /// TypedDatas will be PlainInstance.
+  int32List,
+
+  /// An instance of the built-in VM TypedData implementations. User-defined
+  /// TypedDatas will be PlainInstance.
+  int64List,
+
+  /// An instance of the built-in VM TypedData implementations. User-defined
+  /// TypedDatas will be PlainInstance.
+  float32List,
+
+  /// An instance of the built-in VM TypedData implementations. User-defined
+  /// TypedDatas will be PlainInstance.
+  float64List,
+
+  /// An instance of the built-in VM TypedData implementations. User-defined
+  /// TypedDatas will be PlainInstance.
+  int32x4List,
+
+  /// An instance of the built-in VM TypedData implementations. User-defined
+  /// TypedDatas will be PlainInstance.
+  float32x4List,
+
+  /// An instance of the built-in VM TypedData implementations. User-defined
+  /// TypedDatas will be PlainInstance.
+  float64x2List,
+
+  /// An instance of the Dart class StackTrace.
+  stackTrace,
+
+  /// An instance of the built-in VM Closure implementation. User-defined
+  /// Closures will be PlainInstance.
+  closure,
+
+  /// An instance of the Dart class MirrorReference.
+  mirrorReference,
+
+  /// An instance of the Dart class RegExp.
+  regExp,
+
+  /// An instance of the Dart class WeakProperty.
+  weakProperty,
+
+  /// An instance of the Dart class Type.
+  type,
+
+  /// An instance of the Dart class TypeParameter.
+  typeParameter,
+
+  /// An instance of the Dart class TypeRef.
+  typeRef,
+}
+
+bool isTypedData(InstanceKind kind) {
+  switch (kind) {
+    case InstanceKind.uint8ClampedList:
+    case InstanceKind.uint8List:
+    case InstanceKind.uint16List:
+    case InstanceKind.uint32List:
+    case InstanceKind.uint64List:
+    case InstanceKind.int8List:
+    case InstanceKind.int16List:
+    case InstanceKind.int32List:
+    case InstanceKind.int64List:
+    case InstanceKind.float32List:
+    case InstanceKind.float64List:
+    case InstanceKind.int32x4List:
+    case InstanceKind.float32x4List:
+    case InstanceKind.float64x2List:
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool isSimdValue(InstanceKind kind) {
+  switch (kind) {
+    case InstanceKind.float32x4:
+    case InstanceKind.float64x2:
+    case InstanceKind.int32x4:
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool isAbstractType(InstanceKind kind) {
+  switch (kind) {
+    case InstanceKind.type:
+    case InstanceKind.typeRef:
+    case InstanceKind.typeParameter:
+      return true;
+    default:
+      return false;
+  }
+}
+
+abstract class InstanceRef extends ObjectRef {
+  /// What kind of instance is this?
+  InstanceKind get kind;
+
+  /// Instance references always include their class.
+  ClassRef get clazz;
+
+  /// [optional] The value of this instance as a string.
+  ///
+  /// Provided for the instance kinds:
+  ///   Null (null)
+  ///   Bool (true or false)
+  ///   Double (suitable for passing to Double.parse())
+  ///   Int (suitable for passing to int.parse())
+  ///   String (value may be truncated)
+  ///   Float32x4
+  ///   Float64x2
+  ///   Int32x4
+  ///   StackTrace
+  String get valueAsString;
+
+  /// [optional] The valueAsString for String references may be truncated. If so,
+  /// this property is added with the value 'true'.
+  ///
+  /// New code should use 'length' and 'count' instead.
+  bool get valueAsStringIsTruncated;
+
+  /// [optional] The length of a List or the number of associations in a Map or
+  /// the number of codeunits in a String.
+  ///
+  /// Provided for instance kinds:
+  ///   String
+  ///   List
+  ///   Map
+  ///   Uint8ClampedList
+  ///   Uint8List
+  ///   Uint16List
+  ///   Uint32List
+  ///   Uint64List
+  ///   Int8List
+  ///   Int16List
+  ///   Int32List
+  ///   Int64List
+  ///   Float32List
+  ///   Float64List
+  ///   Int32x4List
+  ///   Float32x4List
+  ///   Float64x2List
+  int get length;
+
+  /// [optional] The name of a Type instance.
+  ///
+  /// Provided for instance kinds:
+  ///   Type
+  String get name;
+
+  /// [optional] The corresponding Class if this Type is canonical.
+  ///
+  /// Provided for instance kinds:
+  ///   Type
+  ClassRef get typeClass;
+
+  /// [optional] The parameterized class of a type parameter:
+  ///
+  /// Provided for instance kinds:
+  ///   TypeParameter
+  ClassRef get parameterizedClass;
+
+  /// [optional] The pattern of a RegExp instance.
+  ///
+  /// The pattern is always an instance of kind String.
+  ///
+  /// Provided for instance kinds:
+  ///   RegExp
+  InstanceRef get pattern;
+
+  /// [optional] The function associated with a Closure instance.
+  ///
+  /// Provided for instance kinds:
+  ///   Closure
+  FunctionRef get closureFunction;
+
+  /// [optional] The context associated with a Closure instance.
+  ///
+  /// Provided for instance kinds:
+  ///   Closure
+  ContextRef get closureContext;
+}
+
+abstract class Instance extends Object implements InstanceRef {
+  /// [optional] The index of the first element or association or codeunit
+  /// returned. This is only provided when it is non-zero.
+  ///
+  /// Provided for instance kinds:
+  ///   String
+  ///   List
+  ///   Map
+  ///   Uint8ClampedList
+  ///   Uint8List
+  ///   Uint16List
+  ///   Uint32List
+  ///   Uint64List
+  ///   Int8List
+  ///   Int16List
+  ///   Int32List
+  ///   Int64List
+  ///   Float32List
+  ///   Float64List
+  ///   Int32x4List
+  ///   Float32x4List
+  ///   Float64x2List
+  int get offset;
+
+  /// [optional] The number of elements or associations or codeunits returned.
+  /// This is only provided when it is less than length.
+  ///
+  /// Provided for instance kinds:
+  ///   String
+  ///   List
+  ///   Map
+  ///   Uint8ClampedList
+  ///   Uint8List
+  ///   Uint16List
+  ///   Uint32List
+  ///   Uint64List
+  ///   Int8List
+  ///   Int16List
+  ///   Int32List
+  ///   Int64List
+  ///   Float32List
+  ///   Float64List
+  ///   Int32x4List
+  ///   Float32x4List
+  ///   Float64x2List
+  int get count;
+
+  /// [optional] The elements of a TypedData instance.
+  ///
+  /// Provided for instance kinds:
+  ///   Uint8ClampedList
+  ///   Uint8List
+  ///   Uint16List
+  ///   Uint32List
+  ///   Uint64List
+  ///   Int8List
+  ///   Int16List
+  ///   Int32List
+  ///   Int64List
+  ///   Float32List
+  ///   Float64List
+  ///   Int32x4List
+  ///   Float32x4List
+  ///   Float64x2List
+  List<dynamic> get typedElements;
+
+  /// [optional] The native fields of this Instance.
+  Iterable<NativeField> get nativeFields;
+
+  /// [optional] The fields of this Instance.
+  Iterable<BoundField> get fields;
+
+  /// [optional] The elements of a List instance.
+  ///
+  /// Provided for instance kinds:
+  ///   List
+  Iterable<Guarded<ObjectRef>> get elements;
+  // It should be:
+  // Iterable<Guarded<InstanceRef>> get elements;
+  // In some situations we obtain lists of non Instances
+
+  /// [optional] The elements of a Map instance.
+  ///
+  /// Provided for instance kinds:
+  ///   Map
+  Iterable<MapAssociation> get associations;
+
+  /// [optional] The key for a WeakProperty instance.
+  ///
+  /// Provided for instance kinds:
+  ///   WeakProperty
+  InstanceRef get key;
+
+  /// [optional] The key for a WeakProperty instance.
+  ///
+  /// Provided for instance kinds:
+  ///   WeakProperty
+  InstanceRef get value;
+
+  /// [optional] The referent of a MirrorReference instance.
+  ///
+  /// Provided for instance kinds:
+  ///   MirrorReference
+  ObjectRef get referent;
+
+  /// [optional] The type arguments for this type.
+  ///
+  /// Provided for instance kinds:
+  ///   Type
+  TypeArgumentsRef get typeArguments;
+
+  /// [optional] The index of a TypeParameter instance.
+  ///
+  /// Provided for instance kinds:
+  ///   TypeParameter
+  int get parameterIndex;
+
+  /// [optional] The referent of a TypeRef instance.
+  ///
+  /// The value will always be of one of the kinds:
+  /// Type, TypeRef, TypeParameter.
+  ///
+  /// Provided for instance kinds:
+  ///   TypeRef
+  InstanceRef get targetType;
+
+  /// [optional] The bound of a TypeParameter.
+  ///
+  /// The value will always be of one of the kinds:
+  /// Type, TypeRef, TypeParameter.
+  ///
+  /// Provided for instance kinds:
+  ///   TypeParameter
+  InstanceRef get bound;
+
+  /// [optional]
+  ///
+  /// Provided for instance kinds:
+  ///   Closure
+  Breakpoint get activationBreakpoint;
+
+  /// [optional]
+  ///
+  /// Provided for instance kinds:
+  ///   RegExp
+  bool get isCaseSensitive;
+
+  /// [optional]
+  ///
+  /// Provided for instance kinds:
+  ///   RegExp
+  bool get isMultiLine;
+
+  /// [optional]
+  ///
+  /// Provided for instance kinds:
+  ///   RegExp
+  FunctionRef get oneByteFunction;
+
+  /// [optional]
+  ///
+  /// Provided for instance kinds:
+  ///   RegExp
+  FunctionRef get twoByteFunction;
+
+  /// [optional]
+  ///
+  /// Provided for instance kinds:
+  ///   RegExp
+  FunctionRef get externalOneByteFunction;
+
+  /// [optional]
+  ///
+  /// Provided for instance kinds:
+  ///   RegExp
+  FunctionRef get externalTwoByteFunction;
+
+  /// [optional]
+  ///
+  /// Provided for instance kinds:
+  ///   RegExp
+  InstanceRef get oneByteBytecode;
+
+  /// [optional]
+  ///
+  /// Provided for instance kinds:
+  ///   RegExp
+  InstanceRef get twoByteBytecode;
+}
+
+abstract class BoundField {
+  FieldRef get decl;
+  Guarded<InstanceRef> get value;
+}
+
+abstract class NativeField {
+  int get value;
+}
diff --git a/runtime/observatory_2/lib/src/models/objects/isolate.dart b/runtime/observatory_2/lib/src/models/objects/isolate.dart
new file mode 100644
index 0000000..a3c063a
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/objects/isolate.dart
@@ -0,0 +1,89 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class IsolateRef {
+  /// The id which is passed to the getIsolate RPC to reload this
+  /// isolate.
+  String get id;
+
+  /// A numeric id for this isolate, represented as a string. Unique.
+  int get number;
+
+  /// A name identifying this isolate. Not guaranteed to be unique.
+  String get name;
+
+  /// True if the isolate is a system isolate which is not running user code.
+  bool get isSystemIsolate;
+
+  /// Trigger a full GC, collecting all unreachable or weakly reachable objects.
+  Future collectAllGarbage();
+}
+
+enum IsolateStatus { loading, idle, running, paused }
+
+abstract class Isolate extends IsolateRef {
+  /// The time that the VM started in milliseconds since the epoch.
+  DateTime get startTime;
+
+  /// Is the isolate in a runnable state?
+  bool get runnable;
+
+  /// The number of live ports for this isolate.
+  //int get livePorts;
+
+  /// Will this isolate pause when exiting?
+  //bool get pauseOnExit;
+
+  /// The last pause event delivered to the isolate. If the isolate is
+  /// running, this will be a resume event.
+  Event get pauseEvent;
+
+  /// [optional] The root library for this isolate.
+  ///
+  /// Guaranteed to be initialized when the IsolateRunnable event fires.
+  LibraryRef get rootLibrary;
+
+  /// A list of all libraries for this isolate.
+  ///
+  /// Guaranteed to be initialized when the IsolateRunnable event fires.
+  Iterable<LibraryRef> get libraries;
+
+  /// A list of all breakpoints for this isolate.
+  //Iterable<Breakpoint> get breakpoints;
+
+  /// [optional] The error that is causing this isolate to exit, if applicable.
+  Error get error;
+
+  /// The list of threads associated with this isolate.
+  Iterable<Thread> get threads;
+
+  /// The maximum amount of zone memory in bytes allocated by the isolate in
+  /// all threads at a given time. Calculated using the high watermarks of each
+  /// thread alive when a thread is unscheduled.
+  int get zoneHighWatermark;
+
+  /// The number of zone handles currently held by this isolate.
+  int get numZoneHandles;
+
+  /// The number of scoped handles currently held by this isolate.
+  int get numScopedHandles;
+
+  /// The current pause on exception mode for this isolate.
+  //ExceptionPauseMode get exceptionPauseMode;
+
+  /// [optional] The list of service extension RPCs that are registered for this
+  /// isolate, if any.
+  Iterable<String> get extensionRPCs;
+
+  Map get counters;
+  HeapSpace get newSpace;
+  HeapSpace get oldSpace;
+
+  IsolateStatus get status;
+
+  /// [optional]
+  FunctionRef get entry;
+}
diff --git a/runtime/observatory_2/lib/src/models/objects/isolate_group.dart b/runtime/observatory_2/lib/src/models/objects/isolate_group.dart
new file mode 100644
index 0000000..7b7a83c
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/objects/isolate_group.dart
@@ -0,0 +1,24 @@
+// Copyright (c) 2019, 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 models;
+
+abstract class IsolateGroupRef {
+  /// The id which is passed to the getIsolateGroup RPC to reload this
+  /// isolate group.
+  String get id;
+
+  /// A numeric id for this isolate group, represented as a string. Unique.
+  int get number;
+
+  /// A name identifying this isolate group. Not guaranteed to be unique.
+  String get name;
+
+  bool get isSystemIsolateGroup;
+}
+
+abstract class IsolateGroup extends IsolateGroupRef {
+  /// A list of all isolates in this isolate group.
+  Iterable<IsolateRef> get isolates;
+}
diff --git a/runtime/observatory_2/lib/src/models/objects/library.dart b/runtime/observatory_2/lib/src/models/objects/library.dart
new file mode 100644
index 0000000..83bab54
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/objects/library.dart
@@ -0,0 +1,45 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class LibraryRef extends ObjectRef {
+  /// The name of this library.
+  String get name;
+
+  /// The uri of this library.
+  String get uri;
+}
+
+abstract class Library extends Object implements LibraryRef {
+  /// Is this library debuggable? Default true.
+  //bool get debuggable;
+
+  /// A list of the imports for this library.
+  Iterable<LibraryDependency> get dependencies;
+
+  /// A list of the scripts which constitute this library.
+  Iterable<ScriptRef> get scripts;
+
+  /// A list of the top-level variables in this library.
+  Iterable<FieldRef> get variables;
+
+  /// A list of the top-level functions in this library.
+  Iterable<FunctionRef> get functions;
+
+  /// A list of all classes in this library.
+  Iterable<ClassRef> get classes;
+
+  ScriptRef get rootScript;
+  String get vmName;
+}
+
+abstract class LibraryDependency {
+  bool get isImport;
+  bool get isDeferred;
+  LibraryRef get target;
+
+  /// [optional]
+  String get prefix;
+}
diff --git a/runtime/observatory_2/lib/src/models/objects/local_var_descriptors.dart b/runtime/observatory_2/lib/src/models/objects/local_var_descriptors.dart
new file mode 100644
index 0000000..5643b80
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/objects/local_var_descriptors.dart
@@ -0,0 +1,10 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class LocalVarDescriptorsRef extends ObjectRef {
+  /// [optional]
+  String get name;
+}
diff --git a/runtime/observatory_2/lib/src/models/objects/map_association.dart b/runtime/observatory_2/lib/src/models/objects/map_association.dart
new file mode 100644
index 0000000..9f786f9
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/objects/map_association.dart
@@ -0,0 +1,10 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class MapAssociation {
+  Guarded<InstanceRef> get key;
+  Guarded<InstanceRef> get value;
+}
diff --git a/runtime/observatory_2/lib/src/models/objects/megamorphiccache.dart b/runtime/observatory_2/lib/src/models/objects/megamorphiccache.dart
new file mode 100644
index 0000000..74657de
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/objects/megamorphiccache.dart
@@ -0,0 +1,16 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class MegamorphicCacheRef extends ObjectRef {
+  String get selector;
+}
+
+abstract class MegamorphicCache extends Object implements MegamorphicCacheRef {
+  String get selector;
+  int get mask;
+  InstanceRef get buckets;
+  InstanceRef get argumentsDescriptor;
+}
diff --git a/runtime/observatory_2/lib/src/models/objects/metric.dart b/runtime/observatory_2/lib/src/models/objects/metric.dart
new file mode 100644
index 0000000..fd8f7ac
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/objects/metric.dart
@@ -0,0 +1,16 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class Metric {
+  String get id;
+  String get name;
+  String get description;
+}
+
+abstract class MetricSample {
+  double get value;
+  DateTime get time;
+}
diff --git a/runtime/observatory_2/lib/src/models/objects/notification.dart b/runtime/observatory_2/lib/src/models/objects/notification.dart
new file mode 100644
index 0000000..1f46766
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/objects/notification.dart
@@ -0,0 +1,18 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class Notification {}
+
+abstract class ExceptionNotification implements Notification {
+  get exception;
+
+  /// [optional]
+  StackTrace get stacktrace;
+}
+
+abstract class EventNotification implements Notification {
+  Event get event;
+}
diff --git a/runtime/observatory_2/lib/src/models/objects/object.dart b/runtime/observatory_2/lib/src/models/objects/object.dart
new file mode 100644
index 0000000..044072f
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/objects/object.dart
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class ObjectRef {
+  /// A unique identifier for an Object.
+  String get id;
+}
+
+abstract class Object implements ObjectRef {
+  /// [optional] If an object is allocated in the Dart heap, it will have
+  /// a corresponding class object.
+  ///
+  /// The class of a non-instance is not a Dart class, but is instead
+  /// an internal vm object.
+  ///
+  /// Moving an Object into or out of the heap is considered a
+  /// backwards compatible change for types other than Instance.
+  ClassRef get clazz;
+
+  /// [optional] The size of this object in the heap.
+  ///
+  /// If an object is not heap-allocated, then this field is omitted.
+  ///
+  /// Note that the size can be zero for some objects. In the current
+  /// VM implementation, this occurs for small integers, which are
+  /// stored entirely within their object pointers.
+  int get size;
+
+  String get vmName;
+}
+
+abstract class RetainingObject {
+  int get retainedSize;
+  ObjectRef get object;
+}
diff --git a/runtime/observatory_2/lib/src/models/objects/objectpool.dart b/runtime/observatory_2/lib/src/models/objects/objectpool.dart
new file mode 100644
index 0000000..ba65961
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/objects/objectpool.dart
@@ -0,0 +1,22 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class ObjectPoolRef extends ObjectRef {
+  int get length;
+}
+
+abstract class ObjectPool extends Object implements ObjectPoolRef {
+  Iterable<ObjectPoolEntry> get entries;
+}
+
+enum ObjectPoolEntryKind { object, immediate, nativeEntryData, nativeEntry }
+
+abstract class ObjectPoolEntry {
+  int get offset;
+  ObjectPoolEntryKind get kind;
+  ObjectRef get asObject;
+  int get asInteger;
+}
diff --git a/runtime/observatory_2/lib/src/models/objects/objectstore.dart b/runtime/observatory_2/lib/src/models/objects/objectstore.dart
new file mode 100644
index 0000000..694f858
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/objects/objectstore.dart
@@ -0,0 +1,14 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class ObjectStore {
+  Iterable<NamedField> get fields;
+}
+
+abstract class NamedField {
+  String get name;
+  ObjectRef get value;
+}
diff --git a/runtime/observatory_2/lib/src/models/objects/pc_descriptors.dart b/runtime/observatory_2/lib/src/models/objects/pc_descriptors.dart
new file mode 100644
index 0000000..fea4ce6
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/objects/pc_descriptors.dart
@@ -0,0 +1,10 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class PcDescriptorsRef extends ObjectRef {
+  /// [optional]
+  String get name;
+}
diff --git a/runtime/observatory_2/lib/src/models/objects/persistent_handles.dart b/runtime/observatory_2/lib/src/models/objects/persistent_handles.dart
new file mode 100644
index 0000000..ff81ff2
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/objects/persistent_handles.dart
@@ -0,0 +1,21 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class PersistentHandles {
+  Iterable<PersistentHandle> get elements;
+  Iterable<WeakPersistentHandle> get weakElements;
+}
+
+abstract class PersistentHandle {
+  ObjectRef get object;
+}
+
+abstract class WeakPersistentHandle implements PersistentHandle {
+  int get externalSize;
+  String get peer;
+  String get callbackSymbolName;
+  String get callbackAddress;
+}
diff --git a/runtime/observatory_2/lib/src/models/objects/ports.dart b/runtime/observatory_2/lib/src/models/objects/ports.dart
new file mode 100644
index 0000000..209357e
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/objects/ports.dart
@@ -0,0 +1,14 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class Ports {
+  Iterable<Port> get elements;
+}
+
+abstract class Port {
+  String get name;
+  ObjectRef get handler;
+}
diff --git a/runtime/observatory_2/lib/src/models/objects/retaining_path.dart b/runtime/observatory_2/lib/src/models/objects/retaining_path.dart
new file mode 100644
index 0000000..360d81d
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/objects/retaining_path.dart
@@ -0,0 +1,24 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class RetainingPath {
+  Iterable<RetainingPathItem> get elements;
+
+  String get gcRootType;
+}
+
+abstract class RetainingPathItem {
+  ObjectRef get source;
+
+  /// [optional]
+  String get parentField;
+
+  /// [optional]
+  int get parentListIndex;
+
+  /// [optional]
+  int get parentWordOffset;
+}
diff --git a/runtime/observatory_2/lib/src/models/objects/sample_profile.dart b/runtime/observatory_2/lib/src/models/objects/sample_profile.dart
new file mode 100644
index 0000000..582e571
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/objects/sample_profile.dart
@@ -0,0 +1,76 @@
+// Copyright (c) 2016, 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 models;
+
+enum ProfileTreeDirection { inclusive, exclusive }
+
+abstract class SampleProfile {
+  int get sampleCount;
+  int get maxStackDepth;
+  double get sampleRate;
+  double get timeSpan;
+  List<ProfileCode> get codes;
+  List<ProfileFunction> get functions;
+
+  FunctionCallTree loadFunctionTree(ProfileTreeDirection direction);
+  CodeCallTree loadCodeTree(ProfileTreeDirection direction);
+}
+
+abstract class Profile {
+  double get normalizedExclusiveTicks;
+  double get normalizedInclusiveTicks;
+  void clearTicks();
+  void tickTag();
+}
+
+abstract class ProfileCode extends Profile {
+  CodeRef get code;
+  Map<ProfileCode, int> get callers;
+  Map<ProfileCode, int> get callees;
+}
+
+abstract class ProfileFunction extends Profile {
+  FunctionRef get function;
+  String get resolvedUrl;
+  Map<ProfileFunction, int> get callers;
+  Map<ProfileFunction, int> get callees;
+}
+
+typedef bool CallTreeNodeFilter(CallTreeNode);
+
+abstract class CallTree {
+  CallTree filtered(CallTreeNodeFilter filter);
+}
+
+abstract class CodeCallTree extends CallTree {
+  CodeCallTreeNode get root;
+  CodeCallTree filtered(CallTreeNodeFilter filter);
+}
+
+abstract class FunctionCallTree extends CallTree {
+  FunctionCallTreeNode get root;
+  FunctionCallTree filtered(CallTreeNodeFilter filter);
+}
+
+abstract class CallTreeNode {
+  double get percentage;
+  int get count;
+  int get inclusiveNativeAllocations;
+  int get exclusiveNativeAllocations;
+  Iterable<CallTreeNode> get children;
+  void sortChildren();
+
+  void tick(Map sample, {bool exclusive = false});
+}
+
+abstract class CodeCallTreeNode extends CallTreeNode {
+  ProfileCode get profileCode;
+  Iterable<CodeCallTreeNode> get children;
+}
+
+abstract class FunctionCallTreeNode extends CallTreeNode {
+  ProfileFunction get profileFunction;
+  Iterable<FunctionCallTreeNode> get children;
+}
diff --git a/runtime/observatory_2/lib/src/models/objects/script.dart b/runtime/observatory_2/lib/src/models/objects/script.dart
new file mode 100644
index 0000000..df4b63b
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/objects/script.dart
@@ -0,0 +1,28 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class ScriptRef extends ObjectRef {
+  /// The uri from which this script was loaded.
+  String get uri;
+}
+
+abstract class Script extends Object implements ScriptRef {
+  /// The library which owns this script.
+  LibraryRef get library;
+
+  /// The source code for this script. For certain built-in scripts,
+  /// this may be reconstructed without source comments.
+  String get source;
+
+  DateTime get loadTime;
+  int get firstTokenPos;
+  int get lastTokenPos;
+  int get lineOffset;
+  int get columnOffset;
+
+  int tokenToLine(int token);
+  int tokenToCol(int token);
+}
diff --git a/runtime/observatory_2/lib/src/models/objects/sentinel.dart b/runtime/observatory_2/lib/src/models/objects/sentinel.dart
new file mode 100644
index 0000000..ca6747a
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/objects/sentinel.dart
@@ -0,0 +1,33 @@
+// Copyright (c) 2016, 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 models;
+
+enum SentinelKind {
+  /// Indicates that the object referred to has been collected by the GC.
+  collected,
+
+  /// Indicates that an object id has expired.
+  expired,
+
+  /// Indicates that a variable or field has not been initialized.
+  notInitialized,
+
+  /// Indicates that a variable or field is in the process of being initialized.
+  initializing,
+
+  /// Indicates that a variable has been eliminated by the optimizing compiler.
+  optimizedOut,
+
+  /// Reserved for future use.
+  free,
+}
+
+abstract class Sentinel {
+  /// What kind of sentinel is this?
+  SentinelKind get kind;
+
+  /// A reasonable string representation of this sentinel.
+  String get valueAsString;
+}
diff --git a/runtime/observatory_2/lib/src/models/objects/service.dart b/runtime/observatory_2/lib/src/models/objects/service.dart
new file mode 100644
index 0000000..75d7522
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/objects/service.dart
@@ -0,0 +1,9 @@
+// Copyright (c) 2017, 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 models;
+
+abstract class Service {
+  String get alias;
+}
diff --git a/runtime/observatory_2/lib/src/models/objects/single_target_cache.dart b/runtime/observatory_2/lib/src/models/objects/single_target_cache.dart
new file mode 100644
index 0000000..5178e7a
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/objects/single_target_cache.dart
@@ -0,0 +1,15 @@
+// Copyright (c) 2017, 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 models;
+
+abstract class SingleTargetCacheRef extends ObjectRef {
+  Code get target;
+}
+
+abstract class SingleTargetCache extends Object
+    implements SingleTargetCacheRef {
+  int get lowerLimit;
+  int get upperLimit;
+}
diff --git a/runtime/observatory_2/lib/src/models/objects/source_location.dart b/runtime/observatory_2/lib/src/models/objects/source_location.dart
new file mode 100644
index 0000000..8a03f13
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/objects/source_location.dart
@@ -0,0 +1,35 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class Location {
+  /// The script containing the source location.
+  ScriptRef get script;
+
+  /// [optional] The first token of the location.
+  int get tokenPos;
+}
+
+abstract class SourceLocation implements Location {
+  /// The first token of the location.
+  int get tokenPos;
+
+  /// [optional] The last token of the location if this is a range.
+  int get endTokenPos;
+}
+
+abstract class UnresolvedSourceLocation implements Location {
+  /// [optional] The uri of the script containing the source location if the
+  /// script has yet to be loaded.
+  String get scriptUri;
+
+  /// [optional] An approximate line number for the source location. This may
+  /// change when the location is resolved.
+  int get line;
+
+  /// [optional] An approximate column number for the source location. This may
+  /// change when the location is resolved.
+  int get column;
+}
diff --git a/runtime/observatory_2/lib/src/models/objects/subtype_test_cache.dart b/runtime/observatory_2/lib/src/models/objects/subtype_test_cache.dart
new file mode 100644
index 0000000..cf56939
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/objects/subtype_test_cache.dart
@@ -0,0 +1,11 @@
+// Copyright (c) 2017, 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 models;
+
+abstract class SubtypeTestCacheRef extends ObjectRef {}
+
+abstract class SubtypeTestCache extends Object implements SubtypeTestCacheRef {
+  InstanceRef get cache;
+}
diff --git a/runtime/observatory_2/lib/src/models/objects/target.dart b/runtime/observatory_2/lib/src/models/objects/target.dart
new file mode 100644
index 0000000..a58673f
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/objects/target.dart
@@ -0,0 +1,9 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class Target {
+  String get name;
+}
diff --git a/runtime/observatory_2/lib/src/models/objects/thread.dart b/runtime/observatory_2/lib/src/models/objects/thread.dart
new file mode 100644
index 0000000..8d16a8c
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/objects/thread.dart
@@ -0,0 +1,31 @@
+// Copyright (c) 2016, 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 models;
+
+enum ThreadKind {
+  unknownTask,
+  mutatorTask,
+  compilerTask,
+  sweeperTask,
+  markerTask,
+  finalizerTask
+}
+
+abstract class Thread {
+  /// The id associated with the thread on creation.
+  String get id;
+
+  /// The task type associated with the thread.
+  ThreadKind get kind;
+
+  String get kindString;
+
+  /// The maximum amount of zone memory in bytes allocated by a thread at a
+  /// given time throughout the entire life of the thread.
+  int get zoneHighWatermark;
+
+  /// The current Zone capacity available to this thread.
+  int get zoneCapacity;
+}
diff --git a/runtime/observatory_2/lib/src/models/objects/timeline.dart b/runtime/observatory_2/lib/src/models/objects/timeline.dart
new file mode 100644
index 0000000..ae5e7b2
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/objects/timeline.dart
@@ -0,0 +1,25 @@
+// Copyright (c) 2017, 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 models;
+
+abstract class TimelineRecorder {
+  String get name;
+}
+
+abstract class TimelineStream {
+  String get name;
+  bool get isRecorded;
+}
+
+abstract class TimelineProfile {
+  String get name;
+  Iterable<TimelineStream> get streams;
+}
+
+abstract class TimelineFlags {
+  TimelineRecorder get recorder;
+  Iterable<TimelineStream> get streams;
+  Iterable<TimelineProfile> get profiles;
+}
diff --git a/runtime/observatory_2/lib/src/models/objects/timeline_event.dart b/runtime/observatory_2/lib/src/models/objects/timeline_event.dart
new file mode 100644
index 0000000..61bf444
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/objects/timeline_event.dart
@@ -0,0 +1,7 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class TimelineEvent implements Map<String, dynamic> {}
diff --git a/runtime/observatory_2/lib/src/models/objects/type_arguments.dart b/runtime/observatory_2/lib/src/models/objects/type_arguments.dart
new file mode 100644
index 0000000..a3614dd
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/objects/type_arguments.dart
@@ -0,0 +1,18 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class TypeArgumentsRef extends ObjectRef {
+  /// A name for this type argument list.
+  String get name;
+}
+
+abstract class TypeArguments extends Object implements TypeArgumentsRef {
+  /// A list of types.
+  ///
+  /// The value will always be one of the kinds:
+  /// Type, TypeRef, TypeParameter.
+  Iterable<InstanceRef> get types;
+}
diff --git a/runtime/observatory_2/lib/src/models/objects/unknown.dart b/runtime/observatory_2/lib/src/models/objects/unknown.dart
new file mode 100644
index 0000000..3faeed0
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/objects/unknown.dart
@@ -0,0 +1,9 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class UnknownObjectRef extends ObjectRef {
+  String get vmType;
+}
diff --git a/runtime/observatory_2/lib/src/models/objects/unlinked_call.dart b/runtime/observatory_2/lib/src/models/objects/unlinked_call.dart
new file mode 100644
index 0000000..ed07750
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/objects/unlinked_call.dart
@@ -0,0 +1,13 @@
+// Copyright (c) 2017, 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 models;
+
+abstract class UnlinkedCallRef extends ObjectRef {
+  String get selector;
+}
+
+abstract class UnlinkedCall extends Object implements UnlinkedCallRef {
+  InstanceRef get argumentsDescriptor;
+}
diff --git a/runtime/observatory_2/lib/src/models/objects/vm.dart b/runtime/observatory_2/lib/src/models/objects/vm.dart
new file mode 100644
index 0000000..b69d744
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/objects/vm.dart
@@ -0,0 +1,60 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class VMRef {
+  /// A name identifying this vm. Not guaranteed to be unique.
+  String get name;
+
+  /// [Not actually from the apis]
+  /// A name used to identify the VM in the UI.
+  String get displayName;
+}
+
+abstract class VM implements VMRef {
+  /// Word length on target architecture (e.g. 32, 64).
+  int get architectureBits;
+
+  /// The CPU we are generating code for.
+  String get targetCPU;
+
+  /// The CPU we are actually running on.
+  String get hostCPU;
+
+  /// The Dart VM version string.
+  String get version;
+
+  String get embedder;
+
+  /// The amount of memory currently allocated by native code in zones.
+  int get nativeZoneMemoryUsage;
+
+  /// The process id for the VM.
+  int get pid;
+
+  /// The current amount of native heap allocated memory within the VM.
+  int get heapAllocatedMemoryUsage;
+
+  /// The current number of allocations on the native heap within the VM.
+  int get heapAllocationCount;
+
+  int get currentMemory;
+  int get maxRSS;
+  int get currentRSS;
+
+  /// The time that the VM started in milliseconds since the epoch.
+  ///
+  /// Suitable to pass to DateTime.fromMillisecondsSinceEpoch.
+  DateTime get startTime;
+
+  // A list of isolates running in the VM.
+  Iterable<IsolateRef> get isolates;
+  Iterable<IsolateRef> get systemIsolates;
+  Iterable<IsolateGroupRef> get isolateGroups;
+  Iterable<IsolateGroupRef> get systemIsolateGroups;
+
+  /// Enable the sampling profiler.
+  Future enableProfiler();
+}
diff --git a/runtime/observatory_2/lib/src/models/objects/zone.dart b/runtime/observatory_2/lib/src/models/objects/zone.dart
new file mode 100644
index 0000000..51840cb
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/objects/zone.dart
@@ -0,0 +1,14 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class Zone {
+  /// The total amount of memory in bytes allocated in the zone, including
+  /// memory that is not actually being used.
+  int get capacity;
+
+  /// The total amount of memory in bytes actually used in the zone.
+  int get used;
+}
diff --git a/runtime/observatory_2/lib/src/models/repositories/allocation_profile.dart b/runtime/observatory_2/lib/src/models/repositories/allocation_profile.dart
new file mode 100644
index 0000000..ecb127b
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/repositories/allocation_profile.dart
@@ -0,0 +1,10 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class AllocationProfileRepository {
+  Future<AllocationProfile> get(IsolateRef isolate,
+      {bool gc: false, bool reset: false, bool combine: false});
+}
diff --git a/runtime/observatory_2/lib/src/models/repositories/breakpoint.dart b/runtime/observatory_2/lib/src/models/repositories/breakpoint.dart
new file mode 100644
index 0000000..1727dd3
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/repositories/breakpoint.dart
@@ -0,0 +1,10 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class BreakpointRepository {
+  Future addOnActivation(IsolateRef isolate, Instance closure);
+  Future remove(IsolateRef isolate, Breakpoint breakpoint);
+}
diff --git a/runtime/observatory_2/lib/src/models/repositories/class.dart b/runtime/observatory_2/lib/src/models/repositories/class.dart
new file mode 100644
index 0000000..3695527
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/repositories/class.dart
@@ -0,0 +1,10 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class ClassRepository {
+  Future<Class> getObject(IsolateRef isolate);
+  Future<Class> get(IsolateRef isolate, String id);
+}
diff --git a/runtime/observatory_2/lib/src/models/repositories/context.dart b/runtime/observatory_2/lib/src/models/repositories/context.dart
new file mode 100644
index 0000000..1c3fcf9
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/repositories/context.dart
@@ -0,0 +1,9 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class ContextRepository {
+  Future<Context> get(IsolateRef isolate, String id);
+}
diff --git a/runtime/observatory_2/lib/src/models/repositories/editor.dart b/runtime/observatory_2/lib/src/models/repositories/editor.dart
new file mode 100644
index 0000000..aef7199
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/repositories/editor.dart
@@ -0,0 +1,15 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class EditorRepository {
+  bool get isAvailable;
+
+  Future openClass(IsolateRef isolate, ClassRef clazz);
+  Future openField(IsolateRef isolate, FieldRef clazz);
+  Future openFunction(IsolateRef isolate, FunctionRef clazz);
+  Future openObject(IsolateRef isolate, ObjectRef clazz);
+  Future openSourceLocation(IsolateRef isolate, SourceLocation location);
+}
diff --git a/runtime/observatory_2/lib/src/models/repositories/eval.dart b/runtime/observatory_2/lib/src/models/repositories/eval.dart
new file mode 100644
index 0000000..2bd4c61
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/repositories/eval.dart
@@ -0,0 +1,11 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class EvalRepository {
+  Future<ObjectRef> evaluate(
+      IsolateRef isolate, ObjectRef context, String expression,
+      {bool disableBreakpoints: false});
+}
diff --git a/runtime/observatory_2/lib/src/models/repositories/event.dart b/runtime/observatory_2/lib/src/models/repositories/event.dart
new file mode 100644
index 0000000..ae5367e
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/repositories/event.dart
@@ -0,0 +1,37 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class EventRepository {
+  Stream<Event> get onEvent;
+  Stream<VMEvent> get onVMEvent;
+  Stream<VMUpdateEvent> get onVMUpdate;
+  Stream<IsolateEvent> get onIsolateEvent;
+  Stream<IsolateStartEvent> get onIsolateStart;
+  Stream<IsolateRunnableEvent> get onIsolateRunnable;
+  Stream<IsolateExitEvent> get onIsolateExit;
+  Stream<IsolateUpdateEvent> get onIsolateUpdate;
+  Stream<IsolateReloadEvent> get onIsolateReload;
+  Stream<ServiceExtensionAddedEvent> get onServiceExtensionAdded;
+  Stream<DebugEvent> get onDebugEvent;
+  Stream<PauseStartEvent> get onPauseStart;
+  Stream<PauseExitEvent> get onPauseExit;
+  Stream<PauseBreakpointEvent> get onPauseBreakpoint;
+  Stream<PauseInterruptedEvent> get onPauseInterrupted;
+  Stream<PauseExceptionEvent> get onPauseException;
+  Stream<ResumeEvent> get onResume;
+  Stream<BreakpointAddedEvent> get onBreakpointAdded;
+  Stream<BreakpointResolvedEvent> get onBreakpointResolved;
+  Stream<BreakpointRemovedEvent> get onBreakpointRemoved;
+  Stream<InspectEvent> get onInspect;
+  Stream<GCEvent> get onGCEvent;
+  Stream<LoggingEvent> get onLoggingEvent;
+  Stream<ExtensionEvent> get onExtensionEvent;
+  Stream<TimelineEventsEvent> get onTimelineEvents;
+  Stream<ConnectionClosedEvent> get onConnectionClosed;
+  Stream<ServiceEvent> get onServiceEvent;
+  Stream<ServiceRegisteredEvent> get onServiceRegistered;
+  Stream<ServiceUnregisteredEvent> get onServiceUnregistered;
+}
diff --git a/runtime/observatory_2/lib/src/models/repositories/field.dart b/runtime/observatory_2/lib/src/models/repositories/field.dart
new file mode 100644
index 0000000..6892b17
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/repositories/field.dart
@@ -0,0 +1,9 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class FieldRepository {
+  Future<Field> get(IsolateRef isolate, String id);
+}
diff --git a/runtime/observatory_2/lib/src/models/repositories/flag.dart b/runtime/observatory_2/lib/src/models/repositories/flag.dart
new file mode 100644
index 0000000..358b933
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/repositories/flag.dart
@@ -0,0 +1,9 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class FlagsRepository {
+  Future<Iterable<Flag>> list();
+}
diff --git a/runtime/observatory_2/lib/src/models/repositories/function.dart b/runtime/observatory_2/lib/src/models/repositories/function.dart
new file mode 100644
index 0000000..56298d7
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/repositories/function.dart
@@ -0,0 +1,9 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class FunctionRepository {
+  Future<ServiceFunction> get(IsolateRef isolate, String id);
+}
diff --git a/runtime/observatory_2/lib/src/models/repositories/heap_snapshot.dart b/runtime/observatory_2/lib/src/models/repositories/heap_snapshot.dart
new file mode 100644
index 0000000..3bc6305
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/repositories/heap_snapshot.dart
@@ -0,0 +1,9 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class HeapSnapshotRepository {
+  SnapshotReader get(IsolateRef isolate);
+}
diff --git a/runtime/observatory_2/lib/src/models/repositories/icdata.dart b/runtime/observatory_2/lib/src/models/repositories/icdata.dart
new file mode 100644
index 0000000..7b3b752
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/repositories/icdata.dart
@@ -0,0 +1,9 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class ICDataRepository {
+  Future<ICData> get(IsolateRef isolate, String id);
+}
diff --git a/runtime/observatory_2/lib/src/models/repositories/inbound_references.dart b/runtime/observatory_2/lib/src/models/repositories/inbound_references.dart
new file mode 100644
index 0000000..09cdcca
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/repositories/inbound_references.dart
@@ -0,0 +1,9 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class InboundReferencesRepository {
+  Future<InboundReferences> get(IsolateRef isolate, String id);
+}
diff --git a/runtime/observatory_2/lib/src/models/repositories/instance.dart b/runtime/observatory_2/lib/src/models/repositories/instance.dart
new file mode 100644
index 0000000..4da307f
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/repositories/instance.dart
@@ -0,0 +1,9 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class InstanceRepository {
+  Future<Instance> get(IsolateRef isolate, String id, {int count});
+}
diff --git a/runtime/observatory_2/lib/src/models/repositories/isolate.dart b/runtime/observatory_2/lib/src/models/repositories/isolate.dart
new file mode 100644
index 0000000..98c5976
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/repositories/isolate.dart
@@ -0,0 +1,11 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class IsolateRepository {
+  Iterable<Service> get reloadSourcesServices;
+  Future<Isolate> get(IsolateRef isolate);
+  Future reloadSources(IsolateRef isolate, {Service service});
+}
diff --git a/runtime/observatory_2/lib/src/models/repositories/isolate_group.dart b/runtime/observatory_2/lib/src/models/repositories/isolate_group.dart
new file mode 100644
index 0000000..ccf2422
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/repositories/isolate_group.dart
@@ -0,0 +1,9 @@
+// Copyright (c) 2019, 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 models;
+
+abstract class IsolateGroupRepository {
+  Future<IsolateGroup> get(IsolateGroupRef isolateGroup);
+}
diff --git a/runtime/observatory_2/lib/src/models/repositories/library.dart b/runtime/observatory_2/lib/src/models/repositories/library.dart
new file mode 100644
index 0000000..7e68be7
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/repositories/library.dart
@@ -0,0 +1,9 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class LibraryRepository {
+  Future<Library> get(IsolateRef isolate, String id);
+}
diff --git a/runtime/observatory_2/lib/src/models/repositories/megamorphiccache.dart b/runtime/observatory_2/lib/src/models/repositories/megamorphiccache.dart
new file mode 100644
index 0000000..cfddc73
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/repositories/megamorphiccache.dart
@@ -0,0 +1,9 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class MegamorphicCacheRepository {
+  Future<MegamorphicCache> get(IsolateRef isolate, String id);
+}
diff --git a/runtime/observatory_2/lib/src/models/repositories/metric.dart b/runtime/observatory_2/lib/src/models/repositories/metric.dart
new file mode 100644
index 0000000..ee10fdb
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/repositories/metric.dart
@@ -0,0 +1,20 @@
+// Copyright (c) 2016, 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 models;
+
+enum MetricBufferSize { n10samples, n100samples, n1000samples }
+
+enum MetricSamplingRate { off, e100ms, e1s, e2s, e4s, e8s }
+
+abstract class MetricRepository {
+  Future<Iterable<Metric>> list(IsolateRef isolate);
+  void setSamplingRate(IsolateRef isolate, Metric metric, MetricSamplingRate r);
+  MetricSamplingRate getSamplingRate(IsolateRef isolate, Metric metric);
+  void setBufferSize(IsolateRef isolate, Metric metric, MetricBufferSize r);
+  MetricBufferSize getBufferSize(IsolateRef isolate, Metric metric);
+  Iterable<MetricSample> getSamples(IsolateRef isolate, Metric metric);
+  double getMinValue(IsolateRef isolate, Metric metric);
+  double getMaxValue(IsolateRef isolate, Metric metric);
+}
diff --git a/runtime/observatory_2/lib/src/models/repositories/notification.dart b/runtime/observatory_2/lib/src/models/repositories/notification.dart
new file mode 100644
index 0000000..c9c7824
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/repositories/notification.dart
@@ -0,0 +1,16 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class NotificationChangeEvent {
+  NotificationRepository get repository;
+}
+
+abstract class NotificationRepository {
+  Stream<NotificationChangeEvent> get onChange;
+  Iterable<Notification> list();
+  void delete(Notification notification);
+  void deleteAll();
+}
diff --git a/runtime/observatory_2/lib/src/models/repositories/object.dart b/runtime/observatory_2/lib/src/models/repositories/object.dart
new file mode 100644
index 0000000..3350db4
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/repositories/object.dart
@@ -0,0 +1,9 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class ObjectRepository {
+  Future<Object> get(IsolateRef isolate, String id, {int count});
+}
diff --git a/runtime/observatory_2/lib/src/models/repositories/objectpool.dart b/runtime/observatory_2/lib/src/models/repositories/objectpool.dart
new file mode 100644
index 0000000..36f51ba
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/repositories/objectpool.dart
@@ -0,0 +1,9 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class ObjectPoolRepository {
+  Future<ObjectPool> get(IsolateRef isolate, String id);
+}
diff --git a/runtime/observatory_2/lib/src/models/repositories/objectstore.dart b/runtime/observatory_2/lib/src/models/repositories/objectstore.dart
new file mode 100644
index 0000000..ee1735c
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/repositories/objectstore.dart
@@ -0,0 +1,9 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class ObjectStoreRepository {
+  Future<ObjectStore> get(IsolateRef isolate);
+}
diff --git a/runtime/observatory_2/lib/src/models/repositories/persistent_handles.dart b/runtime/observatory_2/lib/src/models/repositories/persistent_handles.dart
new file mode 100644
index 0000000..62d6323
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/repositories/persistent_handles.dart
@@ -0,0 +1,9 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class PersistentHandlesRepository {
+  Future<PersistentHandles> get(IsolateRef isolate);
+}
diff --git a/runtime/observatory_2/lib/src/models/repositories/ports.dart b/runtime/observatory_2/lib/src/models/repositories/ports.dart
new file mode 100644
index 0000000..bbb8c80
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/repositories/ports.dart
@@ -0,0 +1,9 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class PortsRepository {
+  Future<Ports> get(IsolateRef isolate);
+}
diff --git a/runtime/observatory_2/lib/src/models/repositories/reachable_size.dart b/runtime/observatory_2/lib/src/models/repositories/reachable_size.dart
new file mode 100644
index 0000000..fc1292fb
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/repositories/reachable_size.dart
@@ -0,0 +1,9 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class ReachableSizeRepository {
+  Future<Guarded<Instance>> get(IsolateRef isolate, String id);
+}
diff --git a/runtime/observatory_2/lib/src/models/repositories/retained_size.dart b/runtime/observatory_2/lib/src/models/repositories/retained_size.dart
new file mode 100644
index 0000000..3b4fe8e
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/repositories/retained_size.dart
@@ -0,0 +1,9 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class RetainedSizeRepository {
+  Future<Guarded<Instance>> get(IsolateRef isolate, String id);
+}
diff --git a/runtime/observatory_2/lib/src/models/repositories/retaining_path.dart b/runtime/observatory_2/lib/src/models/repositories/retaining_path.dart
new file mode 100644
index 0000000..1999846
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/repositories/retaining_path.dart
@@ -0,0 +1,9 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class RetainingPathRepository {
+  Future<RetainingPath> get(IsolateRef isolate, String id);
+}
diff --git a/runtime/observatory_2/lib/src/models/repositories/sample_profile.dart b/runtime/observatory_2/lib/src/models/repositories/sample_profile.dart
new file mode 100644
index 0000000..4a21ed8
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/repositories/sample_profile.dart
@@ -0,0 +1,52 @@
+// Copyright (c) 2016, 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 models;
+
+enum SampleProfileTag { userVM, userOnly, vmUser, vmOnly, none }
+
+enum SampleProfileLoadingStatus { disabled, fetching, loading, loaded }
+
+enum SampleProfileType { cpu, memory }
+
+bool isSampleProcessRunning(SampleProfileLoadingStatus status) {
+  switch (status) {
+    case SampleProfileLoadingStatus.fetching:
+    case SampleProfileLoadingStatus.loading:
+      return true;
+    default:
+      return false;
+  }
+}
+
+abstract class SampleProfileLoadingProgressEvent {
+  SampleProfileLoadingProgress get progress;
+}
+
+abstract class SampleProfileLoadingProgress {
+  SampleProfileLoadingStatus get status;
+  double get progress;
+  Duration get fetchingTime;
+  Duration get loadingTime;
+  SampleProfile get profile;
+}
+
+abstract class ClassSampleProfileRepository {
+  Stream<SampleProfileLoadingProgressEvent> get(
+      IsolateRef isolate, ClassRef cls, SampleProfileTag tag,
+      {bool clear: false, bool forceFetch: false});
+  Future enable(IsolateRef isolate, ClassRef cls);
+  Future disable(IsolateRef isolate, ClassRef cls);
+}
+
+abstract class IsolateSampleProfileRepository {
+  Stream<SampleProfileLoadingProgressEvent> get(
+      IsolateRef isolate, SampleProfileTag tag,
+      {bool clear: false, bool forceFetch: false});
+}
+
+abstract class NativeMemorySampleProfileRepository {
+  Stream<SampleProfileLoadingProgressEvent> get(VM vm, SampleProfileTag tag,
+      {bool clear: false, bool forceFetch: false});
+}
diff --git a/runtime/observatory_2/lib/src/models/repositories/script.dart b/runtime/observatory_2/lib/src/models/repositories/script.dart
new file mode 100644
index 0000000..3ba2dc3
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/repositories/script.dart
@@ -0,0 +1,9 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class ScriptRepository {
+  Future<Script> get(IsolateRef isolate, String id);
+}
diff --git a/runtime/observatory_2/lib/src/models/repositories/single_target_cache.dart b/runtime/observatory_2/lib/src/models/repositories/single_target_cache.dart
new file mode 100644
index 0000000..bc4a767
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/repositories/single_target_cache.dart
@@ -0,0 +1,9 @@
+// Copyright (c) 2017, 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 models;
+
+abstract class SingleTargetCacheRepository {
+  Future<SingleTargetCache> get(IsolateRef isolate, String id);
+}
diff --git a/runtime/observatory_2/lib/src/models/repositories/strongly_reachable_instances.dart b/runtime/observatory_2/lib/src/models/repositories/strongly_reachable_instances.dart
new file mode 100644
index 0000000..c0449f8
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/repositories/strongly_reachable_instances.dart
@@ -0,0 +1,9 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class StronglyReachableInstancesRepository {
+  Future<InstanceSet> get(IsolateRef isolate, ClassRef cls, {int limit: 100});
+}
diff --git a/runtime/observatory_2/lib/src/models/repositories/subtype_test_cache.dart b/runtime/observatory_2/lib/src/models/repositories/subtype_test_cache.dart
new file mode 100644
index 0000000..1a03805
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/repositories/subtype_test_cache.dart
@@ -0,0 +1,9 @@
+// Copyright (c) 2017, 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 models;
+
+abstract class SubtypeTestCacheRepository {
+  Future<SubtypeTestCache> get(IsolateRef isolate, String id);
+}
diff --git a/runtime/observatory_2/lib/src/models/repositories/target.dart b/runtime/observatory_2/lib/src/models/repositories/target.dart
new file mode 100644
index 0000000..2c2db92
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/repositories/target.dart
@@ -0,0 +1,21 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class TargetChangeEvent {
+  TargetRepository get repository;
+}
+
+abstract class TargetRepository {
+  Stream<TargetChangeEvent> get onChange;
+
+  Target get current;
+  Iterable<Target> list();
+  void add(String address);
+  void setCurrent(Target t);
+  void delete(Target o);
+  Target find(String networkAddress);
+  bool isConnectedVMTarget(Target target);
+}
diff --git a/runtime/observatory_2/lib/src/models/repositories/timeline.dart b/runtime/observatory_2/lib/src/models/repositories/timeline.dart
new file mode 100644
index 0000000..3d528c9
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/repositories/timeline.dart
@@ -0,0 +1,12 @@
+// Copyright (c) 2017, 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 models;
+
+abstract class TimelineRepository {
+  Future<TimelineFlags> getFlags(VMRef ref);
+  Future setRecordedStreams(VMRef ref, Iterable<TimelineStream> streams);
+  Future clear(VMRef ref);
+  Future<Map> getTimeline(VMRef ref);
+}
diff --git a/runtime/observatory_2/lib/src/models/repositories/type_arguments.dart b/runtime/observatory_2/lib/src/models/repositories/type_arguments.dart
new file mode 100644
index 0000000..dee8170
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/repositories/type_arguments.dart
@@ -0,0 +1,9 @@
+// Copyright (c) 2016, 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 models;
+
+abstract class TypeArgumentsRepository {
+  Future<TypeArguments> get(IsolateRef isolate, String id);
+}
diff --git a/runtime/observatory_2/lib/src/models/repositories/unlinked_call.dart b/runtime/observatory_2/lib/src/models/repositories/unlinked_call.dart
new file mode 100644
index 0000000..fd60305
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/repositories/unlinked_call.dart
@@ -0,0 +1,9 @@
+// Copyright (c) 2017, 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 models;
+
+abstract class UnlinkedCallRepository {
+  Future<UnlinkedCall> get(IsolateRef isolate, String id);
+}
diff --git a/runtime/observatory_2/lib/src/models/repositories/vm.dart b/runtime/observatory_2/lib/src/models/repositories/vm.dart
new file mode 100644
index 0000000..fd646bc
--- /dev/null
+++ b/runtime/observatory_2/lib/src/models/repositories/vm.dart
@@ -0,0 +1,9 @@
+// Copyright (c) 2017, 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 models;
+
+abstract class VMRepository {
+  Future<VM> get(VMRef ref);
+}
diff --git a/runtime/observatory_2/lib/src/repositories/allocation_profile.dart b/runtime/observatory_2/lib/src/repositories/allocation_profile.dart
new file mode 100644
index 0000000..365a6fc
--- /dev/null
+++ b/runtime/observatory_2/lib/src/repositories/allocation_profile.dart
@@ -0,0 +1,41 @@
+// Copyright (c) 2016, 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 repositories;
+
+class AllocationProfileRepository implements M.AllocationProfileRepository {
+  static const _api = '_getAllocationProfile';
+  static const _defaultsApi = '_getDefaultClassesAliases';
+
+  Future<M.AllocationProfile> get(M.IsolateRef i,
+      {bool gc: false, bool reset: false, bool combine: false}) async {
+    assert(gc != null);
+    assert(reset != null);
+    S.Isolate isolate = i as S.Isolate;
+    assert(isolate != null);
+    var params = {};
+    if (gc) {
+      params['gc'] = 'true';
+    }
+    if (reset) {
+      params['reset'] = true;
+    }
+    final dynamic response = await isolate.invokeRpc(_api, params);
+    Map defaults;
+    if (combine) {
+      defaults = await isolate.vm.invokeRpcNoUpgrade(_defaultsApi, {});
+      defaults = defaults['map'];
+    }
+    isolate.updateHeapsFromMap(response['_heaps']);
+    for (S.ServiceMap clsAllocations in response['members']) {
+      S.Class cls = clsAllocations['class'];
+      if (cls == null) {
+        continue;
+      }
+      cls.newSpace.update(clsAllocations['_new']);
+      cls.oldSpace.update(clsAllocations['_old']);
+    }
+    return new AllocationProfile(response, defaults: defaults);
+  }
+}
diff --git a/runtime/observatory_2/lib/src/repositories/breakpoint.dart b/runtime/observatory_2/lib/src/repositories/breakpoint.dart
new file mode 100644
index 0000000..99a66ca
--- /dev/null
+++ b/runtime/observatory_2/lib/src/repositories/breakpoint.dart
@@ -0,0 +1,19 @@
+// Copyright (c) 2016, 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 repositories;
+
+class BreakpointRepository extends M.BreakpointRepository {
+  Future addOnActivation(M.IsolateRef i, M.Instance closure) async {
+    S.Isolate isolate = i as S.Isolate;
+    assert(isolate != null);
+    await isolate.addBreakOnActivation(closure);
+  }
+
+  Future remove(M.IsolateRef i, M.Breakpoint breakpoint) async {
+    S.Isolate isolate = i as S.Isolate;
+    assert(isolate != null);
+    await isolate.removeBreakpoint(breakpoint);
+  }
+}
diff --git a/runtime/observatory_2/lib/src/repositories/class.dart b/runtime/observatory_2/lib/src/repositories/class.dart
new file mode 100644
index 0000000..5f8f391
--- /dev/null
+++ b/runtime/observatory_2/lib/src/repositories/class.dart
@@ -0,0 +1,19 @@
+// Copyright (c) 2016, 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 repositories;
+
+class ClassRepository extends M.ClassRepository {
+  Future<M.Class> getObject(M.IsolateRef i) {
+    final isolate = i as S.Isolate;
+    assert(isolate != null);
+    return isolate.getClassHierarchy();
+  }
+
+  Future<M.Class> get(M.IsolateRef i, String id) async {
+    final isolate = i as S.Isolate;
+    assert(isolate != null);
+    return (await isolate.getObject(id)) as S.Class;
+  }
+}
diff --git a/runtime/observatory_2/lib/src/repositories/context.dart b/runtime/observatory_2/lib/src/repositories/context.dart
new file mode 100644
index 0000000..a861f6a
--- /dev/null
+++ b/runtime/observatory_2/lib/src/repositories/context.dart
@@ -0,0 +1,13 @@
+// Copyright (c) 2016, 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 repositories;
+
+class ContextRepository extends M.ContextRepository {
+  Future<M.Context> get(M.IsolateRef i, String id) async {
+    S.Isolate isolate = i as S.Isolate;
+    assert(isolate != null);
+    return (await isolate.getObject(id)) as S.Context;
+  }
+}
diff --git a/runtime/observatory_2/lib/src/repositories/editor.dart b/runtime/observatory_2/lib/src/repositories/editor.dart
new file mode 100644
index 0000000..d58cb36
--- /dev/null
+++ b/runtime/observatory_2/lib/src/repositories/editor.dart
@@ -0,0 +1,97 @@
+// Copyright (c) 2016, 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 repositories;
+
+class EditorRepository extends M.EditorRepository {
+  final S.VM _vm;
+  final String _editor;
+
+  bool get isAvailable => _getService() != null;
+
+  EditorRepository(S.VM vm, {String editor})
+      : _vm = vm,
+        _editor = editor {
+    assert(_vm != null);
+  }
+
+  S.Service _getService() {
+    Iterable<M.Service> services =
+        _vm.services.where((s) => s.service == 'openSourceLocation');
+    if (_editor != null) {
+      services = services.where((s) => s.alias == _editor);
+    }
+    if (services.isNotEmpty) {
+      return services.first;
+    }
+    return null;
+  }
+
+  Future openClass(M.IsolateRef i, M.ClassRef c) async {
+    S.Class clazz = c as S.Class;
+    assert(clazz != null);
+    if (!clazz.loaded) {
+      await clazz.load();
+    }
+    if (clazz.location == null) {
+      return new Future.value();
+    }
+    return await openSourceLocation(i, clazz.location);
+  }
+
+  Future openField(M.IsolateRef i, M.FieldRef f) async {
+    S.Field field = f as S.Field;
+    assert(field != null);
+    if (!field.loaded) {
+      await field.load();
+    }
+    if (field.location == null) {
+      return new Future.value();
+    }
+    return await openSourceLocation(i, field.location);
+  }
+
+  Future openFunction(M.IsolateRef i, M.FunctionRef f) async {
+    S.ServiceFunction field = f as S.ServiceFunction;
+    assert(field != null);
+    if (!field.loaded) {
+      await field.load();
+    }
+    if (field.location == null) {
+      return new Future.value();
+    }
+    return await openSourceLocation(i, field.location);
+  }
+
+  Future openObject(M.IsolateRef i, M.ObjectRef o) async {
+    assert(o != null);
+    if (o is M.ClassRef) {
+      return await openClass(i, o);
+    }
+    if (o is M.InstanceRef) {
+      return await openClass(i, o.clazz);
+    }
+    if (o is M.FieldRef) {
+      return await openField(i, o);
+    }
+    if (o is M.FunctionRef) {
+      return await openFunction(i, o);
+    }
+    if (o is M.InstanceRef) {
+      if (o.closureFunction != null) {
+        return await openFunction(i, o.closureFunction);
+      }
+      return await openClass(i, o.clazz);
+    }
+    return new Future.value();
+  }
+
+  Future openSourceLocation(M.IsolateRef i, M.SourceLocation l) async {
+    final isolate = i as S.Isolate;
+    assert(isolate != null);
+    assert(l != null);
+    return await isolate.invokeRpc(_getService().method,
+        {'scriptId': l.script.id, 'tokenPos': l.tokenPos});
+  }
+}
diff --git a/runtime/observatory_2/lib/src/repositories/eval.dart b/runtime/observatory_2/lib/src/repositories/eval.dart
new file mode 100644
index 0000000..57ab1ec
--- /dev/null
+++ b/runtime/observatory_2/lib/src/repositories/eval.dart
@@ -0,0 +1,18 @@
+// Copyright (c) 2016, 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 repositories;
+
+class EvalRepository extends M.EvalRepository {
+  Future<M.ObjectRef> evaluate(M.IsolateRef i, M.ObjectRef o, String e,
+      {bool disableBreakpoints: false}) async {
+    S.Isolate isolate = i as S.Isolate;
+    S.ServiceObject object = o as S.HeapObject;
+    assert(isolate != null);
+    assert(object != null);
+    assert(e != null);
+    return await isolate.eval(object, e,
+        disableBreakpoints: disableBreakpoints);
+  }
+}
diff --git a/runtime/observatory_2/lib/src/repositories/event.dart b/runtime/observatory_2/lib/src/repositories/event.dart
new file mode 100644
index 0000000..39313a9
--- /dev/null
+++ b/runtime/observatory_2/lib/src/repositories/event.dart
@@ -0,0 +1,139 @@
+// Copyright (c) 2016, 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 repositories;
+
+class EventRepository implements M.EventRepository {
+  final StreamController<M.Event> _onEvent;
+  Stream<M.Event> get onEvent => _onEvent.stream;
+
+  final Stream<M.VMEvent> onVMEvent;
+  final Stream<M.VMUpdateEvent> onVMUpdate;
+  final Stream<M.IsolateEvent> onIsolateEvent;
+  final Stream<M.IsolateStartEvent> onIsolateStart;
+  final Stream<M.IsolateRunnableEvent> onIsolateRunnable;
+  final Stream<M.IsolateExitEvent> onIsolateExit;
+  final Stream<M.IsolateUpdateEvent> onIsolateUpdate;
+  final Stream<M.IsolateReloadEvent> onIsolateReload;
+  final Stream<M.ServiceExtensionAddedEvent> onServiceExtensionAdded;
+  final Stream<M.DebugEvent> onDebugEvent;
+  final Stream<M.PauseStartEvent> onPauseStart;
+  final Stream<M.PauseExitEvent> onPauseExit;
+  final Stream<M.PauseBreakpointEvent> onPauseBreakpoint;
+  final Stream<M.PauseInterruptedEvent> onPauseInterrupted;
+  final Stream<M.PauseExceptionEvent> onPauseException;
+  final Stream<M.ResumeEvent> onResume;
+  final Stream<M.BreakpointAddedEvent> onBreakpointAdded;
+  final Stream<M.BreakpointResolvedEvent> onBreakpointResolved;
+  final Stream<M.BreakpointRemovedEvent> onBreakpointRemoved;
+  final Stream<M.InspectEvent> onInspect;
+  final Stream<M.GCEvent> onGCEvent;
+  final Stream<M.LoggingEvent> onLoggingEvent;
+  final Stream<M.ExtensionEvent> onExtensionEvent;
+  final Stream<M.TimelineEventsEvent> onTimelineEvents;
+  final Stream<M.ConnectionClosedEvent> onConnectionClosed;
+  final Stream<M.ServiceEvent> onServiceEvent;
+  final Stream<M.ServiceRegisteredEvent> onServiceRegistered;
+  final Stream<M.ServiceUnregisteredEvent> onServiceUnregistered;
+
+  static Stream<T> where<T extends M.Event>(
+      Stream<M.Event> stream, bool predicate(M.Event event)) {
+    var controller = new StreamController<T>.broadcast();
+    stream.listen(
+      (M.Event event) {
+        if (predicate(event)) {
+          controller.add(event as T);
+        }
+      },
+      onError: (error) => controller.addError(error),
+      onDone: () => controller.close(),
+    );
+    return controller.stream;
+  }
+
+  EventRepository() : this._(new StreamController<M.Event>.broadcast());
+
+  EventRepository._(StreamController<M.Event> controller)
+      : this.__(
+            controller,
+            where<M.VMEvent>(controller.stream, (e) => e is M.VMEvent),
+            where<M.IsolateEvent>(
+                controller.stream, (e) => e is M.IsolateEvent),
+            where<M.DebugEvent>(controller.stream, (e) => e is M.DebugEvent),
+            where<M.GCEvent>(controller.stream, (e) => e is M.GCEvent),
+            where<M.LoggingEvent>(
+                controller.stream, (e) => e is M.LoggingEvent),
+            where<M.ExtensionEvent>(
+                controller.stream, (e) => e is M.ExtensionEvent),
+            where<M.TimelineEventsEvent>(
+                controller.stream, (e) => e is M.TimelineEventsEvent),
+            where<M.ConnectionClosedEvent>(
+                controller.stream, (e) => e is M.ConnectionClosedEvent),
+            where<M.ServiceEvent>(
+                controller.stream, (e) => e is M.ServiceEvent));
+
+  EventRepository.__(
+      StreamController<M.Event> controller,
+      Stream<M.VMEvent> onVMEvent,
+      Stream<M.IsolateEvent> onIsolateEvent,
+      Stream<M.DebugEvent> onDebugEvent,
+      Stream<M.GCEvent> onGCEvent,
+      Stream<M.LoggingEvent> onLoggingEvent,
+      Stream<M.ExtensionEvent> onExtensionEvent,
+      Stream<M.TimelineEventsEvent> onTimelineEvents,
+      Stream<M.ConnectionClosedEvent> onConnectionClosed,
+      Stream<M.ServiceEvent> onServiceEvent)
+      : _onEvent = controller,
+        onVMEvent = onVMEvent,
+        onVMUpdate =
+            where<M.VMUpdateEvent>(onVMEvent, (e) => e is M.VMUpdateEvent),
+        onIsolateEvent = onIsolateEvent,
+        onIsolateStart = where<M.IsolateStartEvent>(
+            onIsolateEvent, (e) => e is M.IsolateStartEvent),
+        onIsolateRunnable = where<M.IsolateRunnableEvent>(
+            onIsolateEvent, (e) => e is M.IsolateRunnableEvent),
+        onIsolateExit = where<M.IsolateExitEvent>(
+            onIsolateEvent, (e) => e is M.IsolateExitEvent),
+        onIsolateUpdate = where<M.IsolateUpdateEvent>(
+            onIsolateEvent, (e) => e is M.IsolateUpdateEvent),
+        onIsolateReload = where<M.IsolateReloadEvent>(
+            onIsolateEvent, (e) => e is M.IsolateReloadEvent),
+        onServiceExtensionAdded = where<M.ServiceExtensionAddedEvent>(
+            onIsolateEvent, (e) => e is M.ServiceExtensionAddedEvent),
+        onDebugEvent = onDebugEvent,
+        onPauseStart = where<M.PauseStartEvent>(
+            onDebugEvent, (e) => e is M.PauseStartEvent),
+        onPauseExit =
+            where<M.PauseExitEvent>(onDebugEvent, (e) => e is M.PauseExitEvent),
+        onPauseBreakpoint = where<M.PauseBreakpointEvent>(
+            onDebugEvent, (e) => e is M.PauseBreakpointEvent),
+        onPauseInterrupted = where<M.PauseInterruptedEvent>(
+            onDebugEvent, (e) => e is M.PauseInterruptedEvent),
+        onPauseException = where<M.PauseExceptionEvent>(
+            onDebugEvent, (e) => e is M.PauseExceptionEvent),
+        onResume =
+            where<M.ResumeEvent>(onDebugEvent, (e) => e is M.ResumeEvent),
+        onBreakpointAdded = where<M.BreakpointAddedEvent>(
+            onDebugEvent, (e) => e is M.BreakpointAddedEvent),
+        onBreakpointResolved = where<M.BreakpointResolvedEvent>(
+            onDebugEvent, (e) => e is M.BreakpointResolvedEvent),
+        onBreakpointRemoved = where<M.BreakpointRemovedEvent>(
+            onDebugEvent, (e) => e is M.BreakpointRemovedEvent),
+        onInspect =
+            where<M.InspectEvent>(onDebugEvent, (e) => e is M.InspectEvent),
+        onGCEvent = onGCEvent,
+        onLoggingEvent = onLoggingEvent,
+        onExtensionEvent = onExtensionEvent,
+        onTimelineEvents = onTimelineEvents,
+        onConnectionClosed = onConnectionClosed,
+        onServiceEvent = onServiceEvent,
+        onServiceRegistered = where<M.ServiceRegisteredEvent>(
+            onServiceEvent, (e) => e is M.ServiceRegisteredEvent),
+        onServiceUnregistered = where<M.ServiceUnregisteredEvent>(
+            onServiceEvent, (e) => e is M.ServiceUnregisteredEvent);
+
+  void add(M.Event e) {
+    _onEvent.add(e);
+  }
+}
diff --git a/runtime/observatory_2/lib/src/repositories/field.dart b/runtime/observatory_2/lib/src/repositories/field.dart
new file mode 100644
index 0000000..932a84d
--- /dev/null
+++ b/runtime/observatory_2/lib/src/repositories/field.dart
@@ -0,0 +1,13 @@
+// Copyright (c) 2016, 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 repositories;
+
+class FieldRepository extends M.FieldRepository {
+  Future<M.Field> get(M.IsolateRef i, String id) async {
+    S.Isolate isolate = i as S.Isolate;
+    assert(isolate != null);
+    return (await isolate.getObject(id)) as S.Field;
+  }
+}
diff --git a/runtime/observatory_2/lib/src/repositories/flag.dart b/runtime/observatory_2/lib/src/repositories/flag.dart
new file mode 100644
index 0000000..13ab784
--- /dev/null
+++ b/runtime/observatory_2/lib/src/repositories/flag.dart
@@ -0,0 +1,36 @@
+// Copyright (c) 2016, 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 repositories;
+
+class Flag implements M.Flag {
+  final String name;
+  final String comment;
+  final bool modified;
+  final String valueAsString;
+  Flag(this.name, this.comment, this.modified, this.valueAsString) {
+    assert(name != null);
+    assert(comment != null);
+    assert(modified != null);
+  }
+}
+
+class FlagsRepository implements M.FlagsRepository {
+  final S.VM vm;
+
+  FlagsRepository(this.vm);
+
+  Future<Iterable<Flag>> list() async {
+    var result = <Flag>[];
+    for (var map in ((await vm.getFlagList()) as S.ServiceMap)['flags']) {
+      result.add(_toFlag(map));
+    }
+    return result;
+  }
+
+  static Flag _toFlag(Map map) {
+    return new Flag(
+        map['name'], map['comment'], map['modified'], map['valueAsString']);
+  }
+}
diff --git a/runtime/observatory_2/lib/src/repositories/function.dart b/runtime/observatory_2/lib/src/repositories/function.dart
new file mode 100644
index 0000000..68527c5
--- /dev/null
+++ b/runtime/observatory_2/lib/src/repositories/function.dart
@@ -0,0 +1,13 @@
+// Copyright (c) 2016, 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 repositories;
+
+class FunctionRepository extends M.FunctionRepository {
+  Future<M.ServiceFunction> get(M.IsolateRef i, String id) async {
+    S.Isolate isolate = i as S.Isolate;
+    assert(isolate != null);
+    return (await isolate.getObject(id)) as S.ServiceFunction;
+  }
+}
diff --git a/runtime/observatory_2/lib/src/repositories/heap_snapshot.dart b/runtime/observatory_2/lib/src/repositories/heap_snapshot.dart
new file mode 100644
index 0000000..c574d17
--- /dev/null
+++ b/runtime/observatory_2/lib/src/repositories/heap_snapshot.dart
@@ -0,0 +1,13 @@
+// Copyright (c) 2016, 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 repositories;
+
+class HeapSnapshotRepository implements M.HeapSnapshotRepository {
+  SnapshotReader get(M.IsolateRef i) {
+    S.Isolate isolate = i as S.Isolate;
+    assert(isolate != null);
+    return isolate.fetchHeapSnapshot();
+  }
+}
diff --git a/runtime/observatory_2/lib/src/repositories/icdata.dart b/runtime/observatory_2/lib/src/repositories/icdata.dart
new file mode 100644
index 0000000..dd02f66
--- /dev/null
+++ b/runtime/observatory_2/lib/src/repositories/icdata.dart
@@ -0,0 +1,13 @@
+// Copyright (c) 2016, 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 repositories;
+
+class ICDataRepository extends M.ICDataRepository {
+  Future<M.ICData> get(M.IsolateRef i, String id) async {
+    S.Isolate isolate = i as S.Isolate;
+    assert(isolate != null);
+    return (await isolate.getObject(id)) as S.ICData;
+  }
+}
diff --git a/runtime/observatory_2/lib/src/repositories/inbound_references.dart b/runtime/observatory_2/lib/src/repositories/inbound_references.dart
new file mode 100644
index 0000000..a9b63e0
--- /dev/null
+++ b/runtime/observatory_2/lib/src/repositories/inbound_references.dart
@@ -0,0 +1,15 @@
+// Copyright (c) 2016, 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 repositories;
+
+class InboundReferencesRepository implements M.InboundReferencesRepository {
+  Future<M.InboundReferences> get(M.IsolateRef i, String id) async {
+    S.Isolate isolate = i as S.Isolate;
+    assert(isolate != null);
+    final response = await isolate
+        .invokeRpc('getInboundReferences', {'targetId': id, 'limit': 100});
+    return new S.InboundReferences(response);
+  }
+}
diff --git a/runtime/observatory_2/lib/src/repositories/instance.dart b/runtime/observatory_2/lib/src/repositories/instance.dart
new file mode 100644
index 0000000..b62b140
--- /dev/null
+++ b/runtime/observatory_2/lib/src/repositories/instance.dart
@@ -0,0 +1,15 @@
+// Copyright (c) 2016, 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 repositories;
+
+class InstanceRepository extends M.InstanceRepository {
+  Future<M.Instance> get(M.IsolateRef i, String id,
+      {int count: S.kDefaultFieldLimit}) async {
+    S.Isolate isolate = i as S.Isolate;
+    assert(isolate != null);
+    assert(count != null);
+    return (await isolate.getObject(id, count: count)) as S.Instance;
+  }
+}
diff --git a/runtime/observatory_2/lib/src/repositories/isolate.dart b/runtime/observatory_2/lib/src/repositories/isolate.dart
new file mode 100644
index 0000000..68b8ae1
--- /dev/null
+++ b/runtime/observatory_2/lib/src/repositories/isolate.dart
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, 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 repositories;
+
+class IsolateRepository extends M.IsolateRepository {
+  final S.VM _vm;
+
+  Iterable<M.Service> get reloadSourcesServices =>
+      _vm.services.where((S.Service s) => s.service == 'reloadSources');
+
+  IsolateRepository(this._vm) {
+    assert(_vm != null);
+  }
+
+  Future<M.Isolate> get(M.IsolateRef i) async {
+    S.Isolate isolate = i as S.Isolate;
+    assert(isolate != null);
+    try {
+      await isolate.reload();
+    } on SC.NetworkRpcException catch (_) {
+      /* ignore */
+    }
+    return isolate;
+  }
+
+  Future reloadSources(M.IsolateRef i, {M.Service service}) async {
+    if (service == null) {
+      S.Isolate isolate = i as S.Isolate;
+      assert(isolate != null);
+      await isolate.reloadSources();
+    } else {
+      S.Service srv = service as S.Service;
+      assert(srv != null);
+      await _vm.invokeRpcNoUpgrade(srv.method, {'isolateId': i.id});
+    }
+  }
+}
diff --git a/runtime/observatory_2/lib/src/repositories/isolate_group.dart b/runtime/observatory_2/lib/src/repositories/isolate_group.dart
new file mode 100644
index 0000000..fabc9d7
--- /dev/null
+++ b/runtime/observatory_2/lib/src/repositories/isolate_group.dart
@@ -0,0 +1,22 @@
+// Copyright (c) 2019, 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 repositories;
+
+class IsolateGroupRepository extends M.IsolateGroupRepository {
+  final S.VM _vm;
+
+  IsolateGroupRepository(this._vm) : assert(_vm != null);
+
+  Future<M.IsolateGroup> get(M.IsolateGroupRef i) async {
+    S.IsolateGroup isolateGroup = i as S.IsolateGroup;
+    assert(isolateGroup != null);
+    try {
+      await isolateGroup.reload();
+    } on SC.NetworkRpcException catch (_) {
+      /* ignore */
+    }
+    return isolateGroup;
+  }
+}
diff --git a/runtime/observatory_2/lib/src/repositories/library.dart b/runtime/observatory_2/lib/src/repositories/library.dart
new file mode 100644
index 0000000..74d8b48
--- /dev/null
+++ b/runtime/observatory_2/lib/src/repositories/library.dart
@@ -0,0 +1,15 @@
+// Copyright (c) 2016, 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 repositories;
+
+class LibraryRepository extends M.LibraryRepository {
+  LibraryRepository();
+
+  Future<M.Library> get(M.IsolateRef i, String id) async {
+    S.Isolate isolate = i as S.Isolate;
+    assert(isolate != null);
+    return (await isolate.getObject(id)) as S.Library;
+  }
+}
diff --git a/runtime/observatory_2/lib/src/repositories/megamorphiccache.dart b/runtime/observatory_2/lib/src/repositories/megamorphiccache.dart
new file mode 100644
index 0000000..8672e32
--- /dev/null
+++ b/runtime/observatory_2/lib/src/repositories/megamorphiccache.dart
@@ -0,0 +1,13 @@
+// Copyright (c) 2016, 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 repositories;
+
+class MegamorphicCacheRepository extends M.MegamorphicCacheRepository {
+  Future<M.MegamorphicCache> get(M.IsolateRef i, String id) async {
+    S.Isolate isolate = i as S.Isolate;
+    assert(isolate != null);
+    return (await isolate.getObject(id)) as S.MegamorphicCache;
+  }
+}
diff --git a/runtime/observatory_2/lib/src/repositories/metric.dart b/runtime/observatory_2/lib/src/repositories/metric.dart
new file mode 100644
index 0000000..2d90187
--- /dev/null
+++ b/runtime/observatory_2/lib/src/repositories/metric.dart
@@ -0,0 +1,212 @@
+// Copyright (c) 2016, 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 repositories;
+
+class Metric implements M.Metric {
+  final String id;
+  String get name => internal.name;
+  String get description => internal.description;
+  final internal;
+
+  Metric(this.id, this.internal);
+}
+
+class MetricSample implements M.MetricSample {
+  final double value;
+  final DateTime time = new DateTime.now();
+  MetricSample(this.value);
+}
+
+class MetricRepository implements M.MetricRepository {
+  final Map<S.Isolate, Map<Metric, List<M.MetricSample>>> _samples =
+      <S.Isolate, Map<Metric, List<M.MetricSample>>>{};
+  final Map<S.Isolate, Map<Metric, int>> _rates =
+      <S.Isolate, Map<Metric, int>>{};
+  final Map<S.Isolate, Map<Metric, int>> _sizes =
+      <S.Isolate, Map<Metric, int>>{};
+  Timer _timer;
+  int count = 0;
+
+  Future<Iterable<Metric>> list(M.IsolateRef i) async {
+    S.Isolate isolate = i as S.Isolate;
+    assert(isolate != null);
+    if (_samples.containsKey(isolate)) {
+      return _samples[isolate].keys;
+    }
+    return const [];
+  }
+
+  Future startSampling(M.IsolateRef i) async {
+    S.Isolate isolate = i as S.Isolate;
+    assert(isolate != null);
+    if (!_samples.containsKey(isolate)) {
+      await isolate.refreshMetrics();
+      final samples = _samples[isolate] = <Metric, List<M.MetricSample>>{};
+      final rates = _rates[isolate] = <Metric, int>{};
+      final sizes = _sizes[isolate] = <Metric, int>{};
+      final metrics = []
+        ..addAll(isolate.dartMetrics.keys
+            .map((name) => new Metric(name, isolate.dartMetrics[name]))
+            .toList())
+        ..addAll(isolate.nativeMetrics.keys
+            .map((name) => new Metric(name, isolate.nativeMetrics[name]))
+            .toList());
+      for (final metric in metrics) {
+        samples[metric] = [new MetricSample(metric.internal.value)];
+        rates[metric] = _rateToInteger(M.MetricSamplingRate.off);
+        sizes[metric] = _sizeToInteger(M.MetricBufferSize.n100samples);
+      }
+      if (_samples.length == 1) {
+        count = 0;
+        _timer = new Timer.periodic(new Duration(milliseconds: 100), _update);
+      }
+    }
+  }
+
+  Future stopSampling(M.IsolateRef isolate) async {
+    if (_samples.containsKey(isolate)) {
+      _samples.remove(isolate);
+      _rates.remove(isolate);
+      _sizes.remove(isolate);
+      if (_samples.isEmpty) {
+        _timer.cancel();
+      }
+    }
+  }
+
+  M.MetricSamplingRate getSamplingRate(M.IsolateRef i, M.Metric m) {
+    if (_rates.containsKey(i)) {
+      final metrics = _rates[i];
+      if (metrics.containsKey(m)) {
+        switch (metrics[m]) {
+          case 0:
+            return M.MetricSamplingRate.off;
+          case 1:
+            return M.MetricSamplingRate.e100ms;
+          case 10:
+            return M.MetricSamplingRate.e1s;
+          case 20:
+            return M.MetricSamplingRate.e2s;
+          case 40:
+            return M.MetricSamplingRate.e4s;
+          case 80:
+            return M.MetricSamplingRate.e8s;
+        }
+      }
+    }
+    throw new Exception('Sampling for isolate ${i.id} is not started');
+  }
+
+  void setSamplingRate(M.IsolateRef i, M.Metric m, M.MetricSamplingRate r) {
+    if (_rates.containsKey(i)) {
+      final metrics = _rates[i];
+      if (metrics.containsKey(m)) {
+        metrics[m] = _rateToInteger(r);
+      }
+    } else {
+      throw new Exception('Sampling for isolate ${i.id} is not started');
+    }
+  }
+
+  M.MetricBufferSize getBufferSize(M.IsolateRef i, M.Metric m) {
+    if (_sizes.containsKey(i)) {
+      final metrics = _sizes[i];
+      if (metrics.containsKey(m)) {
+        switch (metrics[m]) {
+          case 10:
+            return M.MetricBufferSize.n10samples;
+          case 100:
+            return M.MetricBufferSize.n100samples;
+          case 1000:
+            return M.MetricBufferSize.n1000samples;
+        }
+      }
+    }
+    throw new Exception('Sampling for isolate ${i.id} is not started');
+  }
+
+  void setBufferSize(M.IsolateRef i, M.Metric m, M.MetricBufferSize s) {
+    if (_sizes.containsKey(i)) {
+      final metrics = _sizes[i];
+      if (metrics.containsKey(m)) {
+        metrics[m] = _sizeToInteger(s);
+      }
+    } else {
+      throw new Exception('Sampling for isolate ${i.id} is not started');
+    }
+  }
+
+  static int _rateToInteger(M.MetricSamplingRate r) {
+    switch (r) {
+      case M.MetricSamplingRate.off:
+        return 0;
+      case M.MetricSamplingRate.e100ms:
+        return 1;
+      case M.MetricSamplingRate.e1s:
+        return 10;
+      case M.MetricSamplingRate.e2s:
+        return 20;
+      case M.MetricSamplingRate.e4s:
+        return 40;
+      case M.MetricSamplingRate.e8s:
+        return 80;
+    }
+    throw new Exception('Unknown MetricSamplingRate ($r)');
+  }
+
+  static int _sizeToInteger(M.MetricBufferSize s) {
+    switch (s) {
+      case M.MetricBufferSize.n10samples:
+        return 10;
+      case M.MetricBufferSize.n100samples:
+        return 100;
+      case M.MetricBufferSize.n1000samples:
+        return 1000;
+    }
+    throw new Exception('Unknown MetricBufferSize ($s)');
+  }
+
+  Iterable<M.MetricSample> getSamples(M.IsolateRef i, M.Metric m) {
+    if (_samples.containsKey(i)) {
+      final metrics = _samples[i];
+      if (metrics.containsKey(m)) {
+        return metrics[m];
+      }
+    }
+    return null;
+  }
+
+  double getMinValue(M.IsolateRef i, M.Metric m) {
+    Metric metric = m as Metric;
+    assert(metric != null);
+    return metric.internal.min;
+  }
+
+  double getMaxValue(M.IsolateRef i, M.Metric m) {
+    Metric metric = m as Metric;
+    assert(metric != null);
+    return metric.internal.max;
+  }
+
+  void _update(_) {
+    for (final isolate in _rates.keys) {
+      final metrics = _rates[isolate];
+      for (final metric in metrics.keys) {
+        final rate = metrics[metric];
+        if (rate != 0 && count % rate == 0) {
+          final size = _sizes[isolate][metric];
+          final samples = _samples[isolate][metric];
+          metric.internal.reload().then((m) {
+            if (samples.length >= size) {
+              samples.removeRange(0, samples.length - size + 1);
+            }
+            samples.add(new MetricSample(m.value));
+          });
+        }
+      }
+    }
+    ++count;
+  }
+}
diff --git a/runtime/observatory_2/lib/src/repositories/notification.dart b/runtime/observatory_2/lib/src/repositories/notification.dart
new file mode 100644
index 0000000..19eb99e
--- /dev/null
+++ b/runtime/observatory_2/lib/src/repositories/notification.dart
@@ -0,0 +1,75 @@
+// Copyright (c) 2016, 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 repositories;
+
+class NotificationChangeEvent implements M.NotificationChangeEvent {
+  final NotificationRepository repository;
+  NotificationChangeEvent(this.repository);
+}
+
+class NotificationRepository implements M.NotificationRepository {
+  final List<M.Notification> _list = <M.Notification>[];
+
+  final StreamController<M.NotificationChangeEvent> _onChange =
+      new StreamController<M.NotificationChangeEvent>.broadcast();
+  Stream<M.NotificationChangeEvent> get onChange => _onChange.stream;
+
+  void add(M.Notification notification) {
+    assert(notification != null);
+    _list.add(notification);
+    _notify();
+  }
+
+  Iterable<M.Notification> list() => _list;
+
+  void delete(M.Notification notification) {
+    if (_list.remove(notification)) _notify();
+  }
+
+  void deleteAll() {
+    if (_list.isNotEmpty) {
+      _list.clear();
+      _notify();
+    }
+  }
+
+  NotificationRepository();
+
+  void _notify() {
+    _onChange.add(new NotificationChangeEvent(this));
+  }
+
+  void deleteWhere(bool test(M.Notification element)) {
+    int length = _list.length;
+    _list.removeWhere(test);
+    if (_list.length != length) _notify();
+  }
+
+  void deletePauseEvents({M.Isolate isolate}) {
+    if (isolate == null) {
+      deleteWhere((notification) {
+        return notification is M.EventNotification &&
+            notification.event is M.PauseEvent;
+      });
+    } else {
+      deleteWhere((notification) {
+        if (notification is M.EventNotification) {
+          var event = notification.event;
+          if (event is M.PauseEvent) {
+            return event.isolate == isolate;
+          }
+        }
+        return false;
+      });
+    }
+  }
+
+  void deleteDisconnectEvents() {
+    deleteWhere((notification) {
+      return notification is M.EventNotification &&
+          notification.event is M.ConnectionClosedEvent;
+    });
+  }
+}
diff --git a/runtime/observatory_2/lib/src/repositories/object.dart b/runtime/observatory_2/lib/src/repositories/object.dart
new file mode 100644
index 0000000..aaab258
--- /dev/null
+++ b/runtime/observatory_2/lib/src/repositories/object.dart
@@ -0,0 +1,16 @@
+// Copyright (c) 2016, 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 repositories;
+
+class ObjectRepository extends M.ObjectRepository {
+  ObjectRepository();
+  Future<M.Object> get(M.IsolateRef i, String id,
+      {int count: S.kDefaultFieldLimit}) async {
+    S.Isolate isolate = i as S.Isolate;
+    assert(isolate != null);
+    assert(count != null);
+    return (await isolate.getObject(id, count: count)) as S.HeapObject;
+  }
+}
diff --git a/runtime/observatory_2/lib/src/repositories/objectpool.dart b/runtime/observatory_2/lib/src/repositories/objectpool.dart
new file mode 100644
index 0000000..e626f88
--- /dev/null
+++ b/runtime/observatory_2/lib/src/repositories/objectpool.dart
@@ -0,0 +1,13 @@
+// Copyright (c) 2016, 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 repositories;
+
+class ObjectPoolRepository extends M.ObjectPoolRepository {
+  Future<M.ObjectPool> get(M.IsolateRef i, String id) async {
+    S.Isolate isolate = i as S.Isolate;
+    assert(isolate != null);
+    return (await isolate.getObject(id)) as S.ObjectPool;
+  }
+}
diff --git a/runtime/observatory_2/lib/src/repositories/objectstore.dart b/runtime/observatory_2/lib/src/repositories/objectstore.dart
new file mode 100644
index 0000000..7456d9a
--- /dev/null
+++ b/runtime/observatory_2/lib/src/repositories/objectstore.dart
@@ -0,0 +1,13 @@
+// Copyright (c) 2016, 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 repositories;
+
+class ObjectStoreRepository implements M.ObjectStoreRepository {
+  Future<M.ObjectStore> get(M.IsolateRef i) async {
+    S.Isolate isolate = i as S.Isolate;
+    assert(isolate != null);
+    return isolate.getObjectStore();
+  }
+}
diff --git a/runtime/observatory_2/lib/src/repositories/persistent_handles.dart b/runtime/observatory_2/lib/src/repositories/persistent_handles.dart
new file mode 100644
index 0000000..db94cbc
--- /dev/null
+++ b/runtime/observatory_2/lib/src/repositories/persistent_handles.dart
@@ -0,0 +1,14 @@
+// Copyright (c) 2016, 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 repositories;
+
+class PersistentHandlesRepository implements M.PersistentHandlesRepository {
+  Future<M.PersistentHandles> get(M.IsolateRef i) async {
+    S.Isolate isolate = i as S.Isolate;
+    assert(isolate != null);
+    final response = await isolate.invokeRpc('_getPersistentHandles', {});
+    return new S.PersistentHandles(response);
+  }
+}
diff --git a/runtime/observatory_2/lib/src/repositories/ports.dart b/runtime/observatory_2/lib/src/repositories/ports.dart
new file mode 100644
index 0000000..324ef74
--- /dev/null
+++ b/runtime/observatory_2/lib/src/repositories/ports.dart
@@ -0,0 +1,14 @@
+// Copyright (c) 2016, 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 repositories;
+
+class PortsRepository implements M.PortsRepository {
+  Future<M.Ports> get(M.IsolateRef i) async {
+    S.Isolate isolate = i as S.Isolate;
+    assert(isolate != null);
+    final response = await isolate.invokeRpc('_getPorts', {});
+    return new S.Ports(response);
+  }
+}
diff --git a/runtime/observatory_2/lib/src/repositories/reachable_size.dart b/runtime/observatory_2/lib/src/repositories/reachable_size.dart
new file mode 100644
index 0000000..d25d359
--- /dev/null
+++ b/runtime/observatory_2/lib/src/repositories/reachable_size.dart
@@ -0,0 +1,15 @@
+// Copyright (c) 2016, 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 repositories;
+
+class ReachableSizeRepository implements M.ReachableSizeRepository {
+  Future<M.Guarded<M.Instance>> get(M.IsolateRef i, String id) async {
+    S.Isolate isolate = i as S.Isolate;
+    assert(isolate != null);
+    final response =
+        await isolate.invokeRpc('_getReachableSize', {'targetId': id});
+    return new S.Guarded<S.Instance>(response);
+  }
+}
diff --git a/runtime/observatory_2/lib/src/repositories/retained_size.dart b/runtime/observatory_2/lib/src/repositories/retained_size.dart
new file mode 100644
index 0000000..6d3cef5
--- /dev/null
+++ b/runtime/observatory_2/lib/src/repositories/retained_size.dart
@@ -0,0 +1,15 @@
+// Copyright (c) 2016, 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 repositories;
+
+class RetainedSizeRepository implements M.RetainedSizeRepository {
+  Future<M.Guarded<M.Instance>> get(M.IsolateRef i, String id) async {
+    S.Isolate isolate = i as S.Isolate;
+    assert(isolate != null);
+    final response =
+        await isolate.invokeRpc('_getRetainedSize', {'targetId': id});
+    return new S.Guarded<S.Instance>(response);
+  }
+}
diff --git a/runtime/observatory_2/lib/src/repositories/retaining_path.dart b/runtime/observatory_2/lib/src/repositories/retaining_path.dart
new file mode 100644
index 0000000..cfe4c38
--- /dev/null
+++ b/runtime/observatory_2/lib/src/repositories/retaining_path.dart
@@ -0,0 +1,15 @@
+// Copyright (c) 2016, 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 repositories;
+
+class RetainingPathRepository implements M.RetainingPathRepository {
+  Future<M.RetainingPath> get(M.IsolateRef i, String id) async {
+    S.Isolate isolate = i as S.Isolate;
+    assert(isolate != null);
+    final response = await isolate
+        .invokeRpc('getRetainingPath', {'targetId': id, 'limit': 100});
+    return new S.RetainingPath(response);
+  }
+}
diff --git a/runtime/observatory_2/lib/src/repositories/sample_profile.dart b/runtime/observatory_2/lib/src/repositories/sample_profile.dart
new file mode 100644
index 0000000..944eaba
--- /dev/null
+++ b/runtime/observatory_2/lib/src/repositories/sample_profile.dart
@@ -0,0 +1,174 @@
+// Copyright (c) 2016, 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 repositories;
+
+class SampleProfileLoadingProgressEvent
+    implements M.SampleProfileLoadingProgressEvent {
+  final SampleProfileLoadingProgress progress;
+  SampleProfileLoadingProgressEvent(this.progress);
+}
+
+class SampleProfileLoadingProgress extends M.SampleProfileLoadingProgress {
+  StreamController<SampleProfileLoadingProgressEvent> _onProgress =
+      StreamController<SampleProfileLoadingProgressEvent>.broadcast();
+  Stream<SampleProfileLoadingProgressEvent> get onProgress =>
+      _onProgress.stream;
+
+  final S.ServiceObjectOwner owner;
+  final S.Class cls;
+  final M.SampleProfileTag tag;
+  final bool clear;
+  final M.SampleProfileType type;
+
+  M.SampleProfileLoadingStatus _status = M.SampleProfileLoadingStatus.fetching;
+  double _progress = 0.0;
+  final _fetchingTime = Stopwatch();
+  final _loadingTime = Stopwatch();
+  SampleProfile _profile;
+
+  M.SampleProfileLoadingStatus get status => _status;
+  double get progress => _progress;
+  Duration get fetchingTime => _fetchingTime.elapsed;
+  Duration get loadingTime => _loadingTime.elapsed;
+  SampleProfile get profile => _profile;
+
+  SampleProfileLoadingProgress(this.owner, this.tag, this.clear,
+      {this.type: M.SampleProfileType.cpu, this.cls}) {
+    _run();
+  }
+
+  Future _run() async {
+    _fetchingTime.start();
+    try {
+      if (clear && (type == M.SampleProfileType.cpu)) {
+        await owner.invokeRpc('clearCpuSamples', {});
+      }
+
+      var response;
+      if (type == M.SampleProfileType.cpu) {
+        response = cls != null
+            ? await cls.getAllocationSamples()
+            : await owner.invokeRpc('getCpuSamples', {'_code': true});
+      } else if (type == M.SampleProfileType.memory) {
+        assert(owner is M.VM);
+        response = await owner
+            .invokeRpc('_getNativeAllocationSamples', {'_code': true});
+      } else {
+        throw Exception('Unknown M.SampleProfileType: $type');
+      }
+
+      _fetchingTime.stop();
+      _loadingTime.start();
+      _status = M.SampleProfileLoadingStatus.loading;
+      _triggerOnProgress();
+
+      SampleProfile profile = SampleProfile();
+      Stream<double> progress = profile.loadProgress(owner, response);
+      progress.listen((value) {
+        _progress = value;
+        _triggerOnProgress();
+      });
+
+      await progress.drain();
+
+      profile.tagOrder = tag;
+      profile.buildFunctionCallerAndCallees();
+      _profile = profile;
+
+      _loadingTime.stop();
+      _status = M.SampleProfileLoadingStatus.loaded;
+      _triggerOnProgress();
+    } catch (e) {
+      if (e is S.ServerRpcException) {
+        if (e.code == S.ServerRpcException.kFeatureDisabled) {
+          _status = M.SampleProfileLoadingStatus.disabled;
+          _triggerOnProgress();
+        }
+      }
+      rethrow;
+    } finally {
+      _onProgress.close();
+    }
+  }
+
+  void _triggerOnProgress() {
+    _onProgress.add(SampleProfileLoadingProgressEvent(this));
+  }
+
+  void reuse(M.SampleProfileTag t) {
+    _profile.tagOrder = t;
+    final onProgress =
+        StreamController<SampleProfileLoadingProgressEvent>.broadcast();
+    Timer.run(() {
+      onProgress.add(SampleProfileLoadingProgressEvent(this));
+      onProgress.close();
+    });
+    _onProgress = onProgress;
+  }
+}
+
+class IsolateSampleProfileRepository
+    implements M.IsolateSampleProfileRepository {
+  SampleProfileLoadingProgress _last;
+
+  Stream<SampleProfileLoadingProgressEvent> get(
+      M.IsolateRef i, M.SampleProfileTag t,
+      {bool clear: false, bool forceFetch: false}) {
+    assert(clear != null);
+    assert(forceFetch != null);
+    S.Isolate isolate = i as S.Isolate;
+    assert(isolate != null);
+    if ((_last != null) && !clear && !forceFetch && (_last.owner == isolate)) {
+      _last.reuse(t);
+    } else {
+      _last = SampleProfileLoadingProgress(isolate, t, clear);
+    }
+    return _last.onProgress;
+  }
+}
+
+class ClassSampleProfileRepository implements M.ClassSampleProfileRepository {
+  Stream<SampleProfileLoadingProgressEvent> get(
+      covariant M.Isolate i, M.ClassRef c, M.SampleProfileTag t,
+      {bool clear: false, bool forceFetch: false}) {
+    S.Isolate isolate = i as S.Isolate;
+    S.Class cls = c as S.Class;
+    assert(isolate != null);
+    assert(cls != null);
+    return SampleProfileLoadingProgress(isolate, t, false, cls: cls).onProgress;
+  }
+
+  Future enable(M.IsolateRef i, M.ClassRef c) {
+    S.Class cls = c as S.Class;
+    assert(cls != null);
+    return cls.setTraceAllocations(true);
+  }
+
+  Future disable(M.IsolateRef i, M.ClassRef c) {
+    S.Class cls = c as S.Class;
+    assert(cls != null);
+    return cls.setTraceAllocations(false);
+  }
+}
+
+class NativeMemorySampleProfileRepository
+    implements M.NativeMemorySampleProfileRepository {
+  SampleProfileLoadingProgress _last;
+
+  Stream<SampleProfileLoadingProgressEvent> get(M.VM vm, M.SampleProfileTag t,
+      {bool forceFetch: false, bool clear: false}) {
+    assert(forceFetch != null);
+    S.VM owner = vm as S.VM;
+    assert(owner != null);
+
+    if ((_last != null) && (_last.profile != null) && !forceFetch) {
+      _last.reuse(t);
+    } else {
+      _last = SampleProfileLoadingProgress(owner, t, false,
+          type: M.SampleProfileType.memory);
+    }
+    return _last.onProgress;
+  }
+}
diff --git a/runtime/observatory_2/lib/src/repositories/script.dart b/runtime/observatory_2/lib/src/repositories/script.dart
new file mode 100644
index 0000000..e0fea2d
--- /dev/null
+++ b/runtime/observatory_2/lib/src/repositories/script.dart
@@ -0,0 +1,14 @@
+// Copyright (c) 2016, 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 repositories;
+
+class ScriptRepository implements M.ScriptRepository {
+  Future<M.Script> get(M.IsolateRef i, String id) async {
+    S.Isolate isolate = i as S.Isolate;
+    assert(i != null);
+    assert(id != null);
+    return (await isolate.getObject(id)) as M.Script;
+  }
+}
diff --git a/runtime/observatory_2/lib/src/repositories/settings.dart b/runtime/observatory_2/lib/src/repositories/settings.dart
new file mode 100644
index 0000000..5cfffc0
--- /dev/null
+++ b/runtime/observatory_2/lib/src/repositories/settings.dart
@@ -0,0 +1,45 @@
+// Copyright (c) 2016, 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 repositories;
+
+/// Static settings database.
+class _Settings {
+  static Storage _storage = window.localStorage;
+
+  /// Associated [value] with [key]. [value] must be JSON encodable.
+  static void set(String key, dynamic value) {
+    _storage[key] = json.encode(value);
+  }
+
+  /// Get value associated with [key]. Return value will be a JSON encodable
+  /// object.
+  static dynamic get(String key) {
+    var value = _storage[key];
+    if (value == null) {
+      return null;
+    }
+    return json.decode(value);
+  }
+}
+
+/// A group of settings each prefixed with group name and a dot.
+class SettingsRepository {
+  /// Group name
+  final String group;
+
+  SettingsRepository(this.group);
+
+  String _fullKey(String key) => '$group.$key';
+
+  void set(String key, dynamic value) {
+    var fullKey = _fullKey(key);
+    _Settings.set(fullKey, value);
+  }
+
+  dynamic get(String key) {
+    var fullKey = _fullKey(key);
+    return _Settings.get(fullKey);
+  }
+}
diff --git a/runtime/observatory_2/lib/src/repositories/single_target_cache.dart b/runtime/observatory_2/lib/src/repositories/single_target_cache.dart
new file mode 100644
index 0000000..745a062
--- /dev/null
+++ b/runtime/observatory_2/lib/src/repositories/single_target_cache.dart
@@ -0,0 +1,13 @@
+// Copyright (c) 2017, 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 repositories;
+
+class SingleTargetCacheRepository extends M.SingleTargetCacheRepository {
+  Future<M.SingleTargetCache> get(M.IsolateRef i, String id) async {
+    S.Isolate isolate = i as S.Isolate;
+    assert(isolate != null);
+    return (await isolate.getObject(id)) as S.SingleTargetCache;
+  }
+}
diff --git a/runtime/observatory_2/lib/src/repositories/strongly_reachable_instances.dart b/runtime/observatory_2/lib/src/repositories/strongly_reachable_instances.dart
new file mode 100644
index 0000000..a015928
--- /dev/null
+++ b/runtime/observatory_2/lib/src/repositories/strongly_reachable_instances.dart
@@ -0,0 +1,18 @@
+// Copyright (c) 2016, 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 repositories;
+
+class StronglyReachableInstancesRepository
+    implements M.StronglyReachableInstancesRepository {
+  Future<M.InstanceSet> get(M.IsolateRef i, M.ClassRef c,
+      {int limit: 100}) async {
+    S.Isolate isolate = i as S.Isolate;
+    S.Class cls = c as S.Class;
+    assert(isolate != null);
+    assert(cls != null);
+    assert(limit != null);
+    return (await isolate.getInstances(cls, limit)) as S.InstanceSet;
+  }
+}
diff --git a/runtime/observatory_2/lib/src/repositories/subtype_test_cache.dart b/runtime/observatory_2/lib/src/repositories/subtype_test_cache.dart
new file mode 100644
index 0000000..a5fd708
--- /dev/null
+++ b/runtime/observatory_2/lib/src/repositories/subtype_test_cache.dart
@@ -0,0 +1,13 @@
+// Copyright (c) 2017, 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 repositories;
+
+class SubtypeTestCacheRepository extends M.SubtypeTestCacheRepository {
+  Future<M.SubtypeTestCache> get(M.IsolateRef i, String id) async {
+    S.Isolate isolate = i as S.Isolate;
+    assert(isolate != null);
+    return (await isolate.getObject(id)) as S.SubtypeTestCache;
+  }
+}
diff --git a/runtime/observatory_2/lib/src/repositories/target.dart b/runtime/observatory_2/lib/src/repositories/target.dart
new file mode 100644
index 0000000..4fb1fb1
--- /dev/null
+++ b/runtime/observatory_2/lib/src/repositories/target.dart
@@ -0,0 +1,129 @@
+// Copyright (c) 2016, 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 repositories;
+
+typedef bool IsConnectedVMTargetDelegate(M.Target target);
+
+class TargetChangeEvent implements M.TargetChangeEvent {
+  final TargetRepository repository;
+  final bool disconnected;
+  TargetChangeEvent(this.repository, [this.disconnected = false]);
+}
+
+class TargetRepository implements M.TargetRepository {
+  static const _historyKey = 'history';
+
+  final StreamController<TargetChangeEvent> _onChange;
+  final Stream<TargetChangeEvent> onChange;
+  final SettingsRepository _settings = new SettingsRepository('targetManager');
+
+  final List<SC.WebSocketVMTarget> _list = <SC.WebSocketVMTarget>[];
+  SC.WebSocketVMTarget current;
+  final IsConnectedVMTargetDelegate _isConnectedVMTarget;
+
+  factory TargetRepository(IsConnectedVMTargetDelegate isConnectedVMTarget) {
+    var controller = new StreamController<TargetChangeEvent>();
+    var stream = controller.stream.asBroadcastStream();
+    return new TargetRepository._(isConnectedVMTarget, controller, stream);
+  }
+
+  TargetRepository._(this._isConnectedVMTarget, this._onChange, this.onChange) {
+    _restore();
+    final defaultAddress = _networkAddressOfDefaultTarget();
+    var defaultTarget = find(defaultAddress);
+    // Add the default address if it doesn't already exist.
+    if (defaultTarget == null) {
+      defaultTarget = new SC.WebSocketVMTarget(defaultAddress);
+      _list.insert(0, defaultTarget);
+    }
+    // Set the current target to the default target.
+    current = defaultTarget;
+  }
+
+  void add(String address) {
+    if (find(address) != null) {
+      return;
+    }
+    _list.insert(0, new SC.WebSocketVMTarget(address));
+    _onChange.add(new TargetChangeEvent(this));
+    _store();
+  }
+
+  Iterable<M.Target> list() => _list.toList();
+
+  void setCurrent(M.Target t) {
+    SC.WebSocketVMTarget target = t as SC.WebSocketVMTarget;
+    if (!_list.contains(target)) {
+      return;
+    }
+    current = target;
+    current.lastConnectionTime = new DateTime.now().millisecondsSinceEpoch;
+    _onChange.add(new TargetChangeEvent(this));
+    _store();
+  }
+
+  void emitDisconnectEvent() {
+    _onChange.add(new TargetChangeEvent(this, true));
+  }
+
+  void delete(o) {
+    if (_list.remove(o)) {
+      if (o == current) {
+        current = null;
+      }
+      _onChange.add(new TargetChangeEvent(this));
+      _store();
+    }
+  }
+
+  /// Read settings from data store.
+  void _restore() {
+    _list.clear();
+    var loaded = _settings.get(_historyKey);
+    if (loaded == null) {
+      return;
+    }
+    for (var i in loaded) {
+      _list.add(new SC.WebSocketVMTarget.fromMap(i));
+    }
+    _list.sort((SC.WebSocketVMTarget a, SC.WebSocketVMTarget b) {
+      return b.lastConnectionTime.compareTo(a.lastConnectionTime);
+    });
+  }
+
+  /// After making a change, update settings.
+  void _store() {
+    _settings.set(_historyKey, _list);
+  }
+
+  /// Find by networkAddress.
+  SC.WebSocketVMTarget find(String networkAddress) {
+    for (SC.WebSocketVMTarget item in _list) {
+      if (item.networkAddress == networkAddress) {
+        return item;
+      }
+    }
+    return null;
+  }
+
+  static String _networkAddressOfDefaultTarget() {
+    // It is possible to override the default port and host by adding extra
+    // query parameters:
+    // http://localhost:8080?override-port=8181
+    // http://localhost:8080?override-port=8181&override-host=10.0.0.2
+    final Uri serverAddress = Uri.parse(window.location.toString());
+    final String port = serverAddress.queryParameters['override-port'];
+    final String host = serverAddress.queryParameters['override-host'];
+    final Uri wsAddress = new Uri(
+      scheme: 'ws',
+      host: host ?? serverAddress.host,
+      port: int.tryParse(port ?? '') ?? serverAddress.port,
+      path: serverAddress.path.isEmpty ? '/ws' : serverAddress.path + 'ws',
+    );
+    return wsAddress.toString();
+  }
+
+  bool isConnectedVMTarget(M.Target target) => _isConnectedVMTarget(target);
+}
diff --git a/runtime/observatory_2/lib/src/repositories/timeline.dart b/runtime/observatory_2/lib/src/repositories/timeline.dart
new file mode 100644
index 0000000..7c0e4a7
--- /dev/null
+++ b/runtime/observatory_2/lib/src/repositories/timeline.dart
@@ -0,0 +1,79 @@
+// Copyright (c) 2017, 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 repositories;
+
+class TimelineRepository extends TimelineRepositoryBase
+    implements M.TimelineRepository {
+  static const _kStackFrames = 'stackFrames';
+  static const _kTraceEvents = 'traceEvents';
+
+  Future<M.TimelineFlags> getFlags(M.VMRef ref) async {
+    S.VM vm = ref as S.VM;
+    S.ServiceMap response = await vm.invokeRpc('getVMTimelineFlags', {});
+    return new S.TimelineFlags(response);
+  }
+
+  Future setRecordedStreams(M.VMRef ref, Iterable<M.TimelineStream> streams) {
+    S.VM vm = ref as S.VM;
+    assert(vm != null);
+    return vm.invokeRpc('setVMTimelineFlags', {
+      'recordedStreams': '[${streams.map((s) => s.name).join(', ')}]',
+    });
+  }
+
+  Future clear(M.VMRef ref) {
+    S.VM vm = ref as S.VM;
+    return vm.invokeRpc('clearVMTimeline', {});
+  }
+
+  Future<void> _formatSamples(
+      M.Isolate isolate, Map traceObject, S.ServiceMap cpuSamples) async {
+    const kRootFrameId = 0;
+    final profile = SampleProfile();
+    await profile.load(isolate as S.ServiceObjectOwner, cpuSamples);
+    final trie = profile.loadFunctionTree(M.ProfileTreeDirection.inclusive);
+    final root = trie.root;
+    int nextId = kRootFrameId;
+    processFrame(FunctionCallTreeNode current, FunctionCallTreeNode parent) {
+      int id = nextId;
+      ++nextId;
+      current.frameId = id;
+      // Skip the root.
+      if (id != kRootFrameId) {
+        final function = current.profileFunction.function;
+        final key = '${isolate.id}-$id';
+        traceObject[_kStackFrames][key] = {
+          'category': 'Dart',
+          'name': function.qualifiedName,
+          'resolvedUrl': current.profileFunction.resolvedUrl,
+          if (parent != null && parent.frameId != kRootFrameId)
+            'parent': '${isolate.id}-${parent.frameId}',
+        };
+      }
+
+      for (final child in current.children) {
+        processFrame(child, current);
+      }
+    }
+
+    processFrame(root, null);
+
+    for (final sample in profile.samples) {
+      FunctionCallTreeNode trie = sample[SampleProfile.kTimelineFunctionTrie];
+
+      if (trie.frameId != kRootFrameId) {
+        traceObject[_kTraceEvents].add({
+          'ph': 'P', // kind = sample event
+          'name': '', // Blank to keep about:tracing happy
+          'pid': profile.pid,
+          'tid': sample['tid'],
+          'ts': sample['timestamp'],
+          'cat': 'Dart',
+          'sf': '${isolate.id}-${trie.frameId}',
+        });
+      }
+    }
+  }
+}
diff --git a/runtime/observatory_2/lib/src/repositories/timeline_base.dart b/runtime/observatory_2/lib/src/repositories/timeline_base.dart
new file mode 100644
index 0000000..43a7e29
--- /dev/null
+++ b/runtime/observatory_2/lib/src/repositories/timeline_base.dart
@@ -0,0 +1,113 @@
+// Copyright (c) 2019, 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
+
+import 'dart:async';
+import 'package:logging/logging.dart';
+import 'package:observatory_2/sample_profile.dart';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/service.dart' as S;
+
+class TimelineRepositoryBase {
+  static const _kStackFrames = 'stackFrames';
+  static const _kTraceEvents = 'traceEvents';
+  static const kTimeOriginMicros = 'timeOriginMicros';
+  static const kTimeExtentMicros = 'timeExtentMicros';
+
+  Future<void> _formatSamples(M.Isolate isolate, Map traceObject,
+      Future<S.ServiceObject> cpuSamples) async {
+    const kRootFrameId = 0;
+    final profile = SampleProfile();
+    await profile.load(isolate as S.ServiceObjectOwner, await cpuSamples);
+    final trie = profile.loadFunctionTree(M.ProfileTreeDirection.inclusive);
+    final root = trie.root;
+    int nextId = kRootFrameId;
+    processFrame(FunctionCallTreeNode current, FunctionCallTreeNode parent) {
+      int id = nextId;
+      ++nextId;
+      current.frameId = id;
+      // Skip the root.
+      if (id != kRootFrameId) {
+        final function = current.profileFunction.function;
+        final key = '${isolate.id}-$id';
+        traceObject[_kStackFrames][key] = {
+          'category': 'Dart',
+          'name': function.qualifiedName,
+          'resolvedUrl': current.profileFunction.resolvedUrl,
+          if (parent != null && parent.frameId != kRootFrameId)
+            'parent': '${isolate.id}-${parent.frameId}',
+        };
+      }
+
+      for (final child in current.children) {
+        processFrame(child, current);
+      }
+    }
+
+    processFrame(root, null);
+
+    for (final sample in profile.samples) {
+      FunctionCallTreeNode trie = sample[SampleProfile.kTimelineFunctionTrie];
+
+      if (trie.frameId != kRootFrameId) {
+        traceObject[_kTraceEvents].add({
+          'ph': 'P', // kind = sample event
+          'name': '', // Blank to keep about:tracing happy
+          'pid': profile.pid,
+          'tid': sample['tid'],
+          'ts': sample['timestamp'],
+          'cat': 'Dart',
+          'sf': '${isolate.id}-${trie.frameId}',
+        });
+      }
+    }
+  }
+
+  Future<Map> getCpuProfileTimeline(M.VMRef ref,
+      {int timeOriginMicros, int timeExtentMicros}) async {
+    final S.VM vm = ref as S.VM;
+    final traceObject = <String, dynamic>{
+      _kStackFrames: {},
+      _kTraceEvents: [],
+    };
+
+    await Future.wait(vm.isolates.map((isolate) {
+      final samples = vm.invokeRpc('getCpuSamples', {
+        'isolateId': isolate.id,
+        if (timeOriginMicros != null) kTimeOriginMicros: timeOriginMicros,
+        if (timeExtentMicros != null) kTimeExtentMicros: timeExtentMicros,
+      });
+      return _formatSamples(isolate, traceObject, samples);
+    }));
+
+    return traceObject;
+  }
+
+  Future<Map> getTimeline(M.VMRef ref) async {
+    final S.VM vm = ref as S.VM;
+    final S.ServiceMap vmTimelineResponse =
+        await vm.invokeRpc('getVMTimeline', {});
+    final timeOriginMicros = vmTimelineResponse[kTimeOriginMicros];
+    final timeExtentMicros = vmTimelineResponse[kTimeExtentMicros];
+    var traceObject = <String, dynamic>{
+      _kStackFrames: {},
+      _kTraceEvents: [],
+    };
+    try {
+      final cpuProfile = await getCpuProfileTimeline(
+        vm,
+        timeOriginMicros: timeOriginMicros,
+        timeExtentMicros: timeExtentMicros,
+      );
+      traceObject = cpuProfile;
+    } on S.ServerRpcException catch (e) {
+      if (e.code != S.ServerRpcException.kFeatureDisabled) {
+        rethrow;
+      }
+      Logger.root.info(
+          "CPU profiler is disabled. Creating timeline without CPU profile.");
+    }
+    traceObject[_kTraceEvents].addAll(vmTimelineResponse[_kTraceEvents]);
+    return traceObject;
+  }
+}
diff --git a/runtime/observatory_2/lib/src/repositories/type_arguments.dart b/runtime/observatory_2/lib/src/repositories/type_arguments.dart
new file mode 100644
index 0000000..c84f548
--- /dev/null
+++ b/runtime/observatory_2/lib/src/repositories/type_arguments.dart
@@ -0,0 +1,14 @@
+// Copyright (c) 2016, 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 repositories;
+
+class TypeArgumentsRepository implements M.TypeArgumentsRepository {
+  Future<M.TypeArguments> get(M.IsolateRef i, String id) async {
+    S.Isolate isolate = i as S.Isolate;
+    assert(i != null);
+    assert(id != null);
+    return (await isolate.getObject(id)) as M.TypeArguments;
+  }
+}
diff --git a/runtime/observatory_2/lib/src/repositories/unlinked_call.dart b/runtime/observatory_2/lib/src/repositories/unlinked_call.dart
new file mode 100644
index 0000000..6f2119c
--- /dev/null
+++ b/runtime/observatory_2/lib/src/repositories/unlinked_call.dart
@@ -0,0 +1,13 @@
+// Copyright (c) 2017, 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 repositories;
+
+class UnlinkedCallRepository extends M.UnlinkedCallRepository {
+  Future<M.UnlinkedCall> get(M.IsolateRef i, String id) async {
+    S.Isolate isolate = i as S.Isolate;
+    assert(isolate != null);
+    return (await isolate.getObject(id)) as S.UnlinkedCall;
+  }
+}
diff --git a/runtime/observatory_2/lib/src/repositories/vm.dart b/runtime/observatory_2/lib/src/repositories/vm.dart
new file mode 100644
index 0000000..514e374
--- /dev/null
+++ b/runtime/observatory_2/lib/src/repositories/vm.dart
@@ -0,0 +1,14 @@
+// Copyright (c) 2017, 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 repositories;
+
+class VMRepository implements M.VMRepository {
+  Future<M.VM> get(M.VMRef ref) async {
+    S.VM vm = ref as S.VM;
+    assert(vm != null);
+    await vm.reload();
+    return vm;
+  }
+}
diff --git a/runtime/observatory_2/lib/src/sample_profile/sample_profile.dart b/runtime/observatory_2/lib/src/sample_profile/sample_profile.dart
new file mode 100644
index 0000000..48667cc
--- /dev/null
+++ b/runtime/observatory_2/lib/src/sample_profile/sample_profile.dart
@@ -0,0 +1,1255 @@
+// Copyright (c) 2015, 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 sample_profiler;
+
+abstract class CallTreeNode<NodeT extends M.CallTreeNode>
+    implements M.CallTreeNode {
+  final List<NodeT> children;
+  int get count => _count;
+  int _count = 0;
+  int inclusiveNativeAllocations = 0;
+  int exclusiveNativeAllocations = 0;
+  double get percentage => _percentage;
+  double _percentage = 0.0;
+  final attributes = <String>{};
+
+  // Used for building timeline
+  int frameId = null;
+  int parentId = null;
+
+  // Either a ProfileCode or a ProfileFunction.
+  Object get profileData;
+  String get name;
+
+  CallTreeNode(this.children,
+      [this._count = 0,
+      this.inclusiveNativeAllocations = 0,
+      this.exclusiveNativeAllocations = 0]) {}
+
+  NodeT getChild(int index);
+
+  void sortChildren() {
+    children.sort((a, b) => b.count - a.count);
+    children.forEach((NodeT child) => child.sortChildren());
+  }
+
+  void tick(Map sample, {bool exclusive = false}) {
+    ++_count;
+    if (SampleProfile._isNativeAllocationSample(sample)) {
+      final allocationSize = sample[SampleProfile._kNativeAllocationSizeBytes];
+      if (exclusive) {
+        exclusiveNativeAllocations += allocationSize;
+      }
+      inclusiveNativeAllocations += allocationSize;
+    }
+  }
+}
+
+class CodeCallTreeNode extends CallTreeNode<CodeCallTreeNode>
+    implements M.CodeCallTreeNode {
+  final ProfileCode profileCode;
+  final SampleProfile profile;
+
+  Object get profileData => profileCode;
+
+  String get name => profileCode.code.name;
+
+  final attributes = <String>{};
+  CodeCallTreeNode(this.profileCode, int count, int inclusiveNativeAllocations,
+      int exclusiveNativeAllocations)
+      : profile = profileCode.profile,
+        super(<CodeCallTreeNode>[], count, inclusiveNativeAllocations,
+            exclusiveNativeAllocations) {
+    attributes.addAll(profileCode.attributes);
+  }
+
+  CodeCallTreeNode.fromIndex(this.profile, int tableIndex)
+      : profileCode = profile.codes[tableIndex],
+        super(<CodeCallTreeNode>[]);
+
+  CodeCallTreeNode getChild(int codeTableIndex) {
+    final length = children.length;
+    int i = 0;
+    while (i < length) {
+      final child = children[i];
+      final childTableIndex = child.profileCode.tableIndex;
+      if (childTableIndex == codeTableIndex) {
+        return child;
+      }
+      if (childTableIndex > codeTableIndex) {
+        break;
+      }
+      ++i;
+    }
+    final child = CodeCallTreeNode.fromIndex(profile, codeTableIndex);
+    if (i < length) {
+      children.insert(i, child);
+    } else {
+      children.add(child);
+    }
+    return child;
+  }
+}
+
+class CallTree<NodeT extends CallTreeNode> {
+  final bool inclusive;
+  final NodeT root;
+
+  CallTree(this.inclusive, this.root);
+}
+
+class CodeCallTree extends CallTree<CodeCallTreeNode>
+    implements M.CodeCallTree {
+  CodeCallTree(bool inclusive, CodeCallTreeNode root) : super(inclusive, root) {
+    if ((root.inclusiveNativeAllocations != null) &&
+        (root.inclusiveNativeAllocations != 0)) {
+      _setCodeMemoryPercentage(null, root);
+    } else {
+      _setCodePercentage(null, root);
+    }
+  }
+
+  CodeCallTree filtered(CallTreeNodeFilter filter) {
+    final treeFilter = _FilteredCodeCallTreeBuilder(filter, this);
+    treeFilter.build();
+    if ((treeFilter.filtered.root.inclusiveNativeAllocations != null) &&
+        (treeFilter.filtered.root.inclusiveNativeAllocations != 0)) {
+      _setCodeMemoryPercentage(null, treeFilter.filtered.root);
+    } else {
+      _setCodePercentage(null, treeFilter.filtered.root);
+    }
+    return treeFilter.filtered;
+  }
+
+  _setCodePercentage(CodeCallTreeNode parent, CodeCallTreeNode node) {
+    assert(node != null);
+    var parentPercentage = 1.0;
+    var parentCount = node.count;
+    if (parent != null) {
+      parentPercentage = parent._percentage;
+      parentCount = parent.count;
+    }
+    if (inclusive) {
+      node._percentage = parentPercentage * (node.count / parentCount);
+    } else {
+      node._percentage = (node.count / parentCount);
+    }
+    for (var child in node.children) {
+      _setCodePercentage(node, child);
+    }
+  }
+
+  _setCodeMemoryPercentage(CodeCallTreeNode parent, CodeCallTreeNode node) {
+    assert(node != null);
+    var parentPercentage = 1.0;
+    var parentMemory = node.inclusiveNativeAllocations;
+    if (parent != null) {
+      parentPercentage = parent._percentage;
+      parentMemory = parent.inclusiveNativeAllocations;
+    }
+    if (inclusive) {
+      node._percentage =
+          parentPercentage * (node.inclusiveNativeAllocations / parentMemory);
+    } else {
+      node._percentage = (node.inclusiveNativeAllocations / parentMemory);
+    }
+    for (var child in node.children) {
+      _setCodeMemoryPercentage(node, child);
+    }
+    node.children.sort((a, b) {
+      return b.inclusiveNativeAllocations - a.inclusiveNativeAllocations;
+    });
+  }
+
+  _recordCallerAndCalleesInner(
+      CodeCallTreeNode caller, CodeCallTreeNode callee) {
+    if (caller != null) {
+      caller.profileCode._recordCallee(callee.profileCode, callee.count);
+      callee.profileCode._recordCaller(caller.profileCode, caller.count);
+    }
+
+    for (var child in callee.children) {
+      _recordCallerAndCalleesInner(callee, child);
+    }
+  }
+
+  _recordCallerAndCallees() {
+    for (var child in root.children) {
+      _recordCallerAndCalleesInner(null, child);
+    }
+  }
+}
+
+class FunctionCallTreeNodeCode {
+  final ProfileCode code;
+  final int ticks;
+  FunctionCallTreeNodeCode(this.code, this.ticks);
+}
+
+class FunctionCallTreeNode extends CallTreeNode<FunctionCallTreeNode>
+    implements M.FunctionCallTreeNode {
+  final ProfileFunction profileFunction;
+  final SampleProfile profile;
+  final codes = <FunctionCallTreeNodeCode>[];
+  int _totalCodeTicks = 0;
+  int get totalCodesTicks => _totalCodeTicks;
+
+  String get name => M.getFunctionFullName(profileFunction.function);
+  Object get profileData => profileFunction;
+
+  FunctionCallTreeNode(this.profileFunction, int count,
+      inclusiveNativeAllocations, exclusiveNativeAllocations)
+      : profile = profileFunction.profile,
+        super(<FunctionCallTreeNode>[], count, inclusiveNativeAllocations,
+            exclusiveNativeAllocations) {
+    profileFunction._addKindBasedAttributes(attributes);
+  }
+
+  FunctionCallTreeNode.fromIndex(this.profile, int tableIndex)
+      : profileFunction = profile.functions[tableIndex],
+        super(<FunctionCallTreeNode>[]);
+
+  FunctionCallTreeNode getChild(int functionTableIndex) {
+    final length = children.length;
+    int i = 0;
+    while (i < length) {
+      final child = children[i];
+      final childTableIndex = child.profileFunction.tableIndex;
+      if (childTableIndex == functionTableIndex) {
+        return child;
+      }
+      if (childTableIndex > functionTableIndex) {
+        break;
+      }
+      ++i;
+    }
+    final child = FunctionCallTreeNode.fromIndex(profile, functionTableIndex);
+    if (i < length) {
+      children.insert(i, child);
+    } else {
+      children.add(child);
+    }
+    return child;
+  }
+}
+
+/// Predicate filter function. Returns true if path from root to [node] and all
+/// of [node]'s children should be added to the filtered tree.
+typedef CallTreeNodeFilter = bool Function(CallTreeNode node);
+
+/// Build a filter version of a FunctionCallTree.
+abstract class _FilteredCallTreeBuilder<NodeT extends CallTreeNode> {
+  /// The filter.
+  final CallTreeNodeFilter filter;
+
+  /// The unfiltered tree.
+  final CallTree _unfilteredTree;
+
+  /// The filtered tree (construct by [build]).
+  final CallTree filtered;
+  final List _currentPath = [];
+
+  /// Construct a filtered tree builder using [filter] and [tree].
+  _FilteredCallTreeBuilder(this.filter, CallTree tree, this.filtered)
+      : _unfilteredTree = tree;
+
+  /// Build the filtered tree.
+  build() {
+    assert(filtered != null);
+    assert(filter != null);
+    assert(_unfilteredTree != null);
+    _descend(_unfilteredTree.root);
+  }
+
+  CallTreeNode _findInChildren(CallTreeNode current, CallTreeNode needle) {
+    for (var child in current.children) {
+      if ((child as CallTreeNode).profileData == needle.profileData) {
+        return child;
+      }
+    }
+    return null;
+  }
+
+  NodeT _copyNode(NodeT node);
+
+  /// Add all nodes in [_currentPath].
+  FunctionCallTreeNode _addCurrentPath() {
+    FunctionCallTreeNode current = filtered.root;
+    // Tree root is always the first element of the current path.
+    assert(_unfilteredTree.root == _currentPath[0]);
+    // Assert that unfiltered tree's root and filtered tree's root are different.
+    assert(_unfilteredTree.root != current);
+    for (var i = 1; i < _currentPath.length; i++) {
+      // toAdd is from the unfiltered tree.
+      var toAdd = _currentPath[i];
+      // See if we already have a node for toAdd in the filtered tree.
+      var child = _findInChildren(current, toAdd);
+      if (child == null) {
+        // New node.
+        child = _copyNode(toAdd);
+        current.children.add(child);
+      }
+      current = child;
+    }
+    return current;
+  }
+
+  /// Starting at [current] append [next] and all of [next]'s sub-trees
+  _appendTree(CallTreeNode current, CallTreeNode next) {
+    if (next == null) {
+      return;
+    }
+    var child = _findInChildren(current, next);
+    if (child == null) {
+      child = _copyNode(next);
+      current.children.add(child);
+    }
+    current = child;
+    for (var nextChild in next.children) {
+      _appendTree(current, nextChild);
+    }
+  }
+
+  /// Add path from root to [child], [child], and all of [child]'s sub-trees
+  /// to filtered tree.
+  _addTree(CallTreeNode child) {
+    var current = _addCurrentPath();
+    _appendTree(current, child);
+  }
+
+  /// Descend further into the tree. [current] is from the unfiltered tree.
+  _descend(CallTreeNode current) {
+    if (current == null) {
+      return;
+    }
+    _currentPath.add(current);
+
+    if (filter(current)) {
+      // Filter matched.
+      if (current.children.length == 0) {
+        // Have no children. Add this path.
+        _addTree(null);
+      } else {
+        // Add all child trees.
+        for (var child in current.children) {
+          _addTree(child);
+        }
+      }
+    } else {
+      // Did not match, descend to each child.
+      for (var child in current.children) {
+        _descend(child);
+      }
+    }
+
+    var last = _currentPath.removeLast();
+    assert(current == last);
+  }
+}
+
+class _FilteredFunctionCallTreeBuilder
+    extends _FilteredCallTreeBuilder<FunctionCallTreeNode> {
+  _FilteredFunctionCallTreeBuilder(
+      CallTreeNodeFilter filter, FunctionCallTree tree)
+      : super(
+            filter,
+            tree,
+            FunctionCallTree(
+                tree.inclusive,
+                FunctionCallTreeNode(
+                    tree.root.profileData,
+                    tree.root.count,
+                    tree.root.inclusiveNativeAllocations,
+                    tree.root.exclusiveNativeAllocations)));
+
+  _copyNode(FunctionCallTreeNode node) {
+    return FunctionCallTreeNode(node.profileData, node.count,
+        node.inclusiveNativeAllocations, node.exclusiveNativeAllocations);
+  }
+}
+
+class _FilteredCodeCallTreeBuilder
+    extends _FilteredCallTreeBuilder<CodeCallTreeNode> {
+  _FilteredCodeCallTreeBuilder(CallTreeNodeFilter filter, CodeCallTree tree)
+      : super(
+            filter,
+            tree,
+            CodeCallTree(
+                tree.inclusive,
+                CodeCallTreeNode(
+                    tree.root.profileData,
+                    tree.root.count,
+                    tree.root.inclusiveNativeAllocations,
+                    tree.root.exclusiveNativeAllocations)));
+
+  _copyNode(CodeCallTreeNode node) {
+    return CodeCallTreeNode(node.profileData, node.count,
+        node.inclusiveNativeAllocations, node.exclusiveNativeAllocations);
+  }
+}
+
+class FunctionCallTree extends CallTree<FunctionCallTreeNode>
+    implements M.FunctionCallTree {
+  FunctionCallTree(bool inclusive, FunctionCallTreeNode root)
+      : super(inclusive, root) {
+    if ((root.inclusiveNativeAllocations != null) &&
+        (root.inclusiveNativeAllocations != 0)) {
+      _setFunctionMemoryPercentage(null, root);
+    } else {
+      _setFunctionPercentage(null, root);
+    }
+  }
+
+  FunctionCallTree filtered(CallTreeNodeFilter filter) {
+    final treeFilter = _FilteredFunctionCallTreeBuilder(filter, this);
+    treeFilter.build();
+    if ((treeFilter.filtered.root.inclusiveNativeAllocations != null) &&
+        (treeFilter.filtered.root.inclusiveNativeAllocations != 0)) {
+      _setFunctionMemoryPercentage(null, treeFilter.filtered.root);
+    } else {
+      _setFunctionPercentage(null, treeFilter.filtered.root);
+    }
+    return treeFilter.filtered;
+  }
+
+  void _setFunctionPercentage(
+      FunctionCallTreeNode parent, FunctionCallTreeNode node) {
+    assert(node != null);
+    var parentPercentage = 1.0;
+    var parentCount = node.count;
+    if (parent != null) {
+      parentPercentage = parent._percentage;
+      parentCount = parent.count;
+    }
+    if (inclusive) {
+      node._percentage = parentPercentage * (node.count / parentCount);
+    } else {
+      node._percentage = (node.count / parentCount);
+    }
+    for (var child in node.children) {
+      _setFunctionPercentage(node, child);
+    }
+  }
+
+  void _setFunctionMemoryPercentage(
+      FunctionCallTreeNode parent, FunctionCallTreeNode node) {
+    assert(node != null);
+    var parentPercentage = 1.0;
+    var parentMemory = node.inclusiveNativeAllocations;
+    if (parent != null) {
+      parentPercentage = parent._percentage;
+      parentMemory = parent.inclusiveNativeAllocations;
+    }
+    if (inclusive) {
+      node._percentage =
+          parentPercentage * (node.inclusiveNativeAllocations / parentMemory);
+    } else {
+      node._percentage = (node.inclusiveNativeAllocations / parentMemory);
+    }
+    for (var child in node.children) {
+      _setFunctionMemoryPercentage(node, child);
+    }
+    node.children.sort((a, b) {
+      return b.inclusiveNativeAllocations - a.inclusiveNativeAllocations;
+    });
+  }
+
+  _markFunctionCallsInner(
+      FunctionCallTreeNode caller, FunctionCallTreeNode callee) {
+    if (caller != null) {
+      caller.profileFunction
+          ._recordCallee(callee.profileFunction, callee.count);
+      callee.profileFunction
+          ._recordCaller(caller.profileFunction, caller.count);
+    }
+    for (var child in callee.children) {
+      _markFunctionCallsInner(callee, child);
+    }
+  }
+
+  _markFunctionCalls() {
+    for (var child in root.children) {
+      _markFunctionCallsInner(null, child);
+    }
+  }
+}
+
+class CodeTick {
+  final int exclusiveTicks;
+  final int inclusiveTicks;
+  CodeTick(this.exclusiveTicks, this.inclusiveTicks);
+}
+
+class InlineIntervalTick {
+  final int startAddress;
+  int _inclusiveTicks = 0;
+  int get inclusiveTicks => _inclusiveTicks;
+  int _exclusiveTicks = 0;
+  int get exclusiveTicks => _exclusiveTicks;
+  InlineIntervalTick(this.startAddress);
+}
+
+class ProfileCode implements M.ProfileCode {
+  final int tableIndex;
+  final SampleProfile profile;
+  final Code code;
+  int exclusiveTicks;
+  int inclusiveTicks;
+  double normalizedExclusiveTicks = 0.0;
+  double normalizedInclusiveTicks = 0.0;
+  final addressTicks = <int, CodeTick>{};
+  final intervalTicks = <int, InlineIntervalTick>{};
+  String formattedInclusiveTicks = '';
+  String formattedExclusiveTicks = '';
+  String formattedExclusivePercent = '';
+  final Set<String> attributes = <String>{};
+  final callers = <ProfileCode, int>{};
+  final callees = <ProfileCode, int>{};
+
+  void _processTicks(List<dynamic> profileTicks) {
+    assert(profileTicks != null);
+    assert((profileTicks.length % 3) == 0);
+    for (var i = 0; i < profileTicks.length; i += 3) {
+      // TODO(observatory): Address is not necessarily representable as a JS
+      // integer.
+      var address = int.parse(profileTicks[i] as String, radix: 16);
+      var exclusive = profileTicks[i + 1] as int;
+      var inclusive = profileTicks[i + 2] as int;
+      var tick = CodeTick(exclusive, inclusive);
+      addressTicks[address] = tick;
+
+      var interval = code.findInterval(address);
+      if (interval != null) {
+        var intervalTick = intervalTicks[interval.start];
+        if (intervalTick == null) {
+          // Insert into map.
+          intervalTick = InlineIntervalTick(interval.start);
+          intervalTicks[interval.start] = intervalTick;
+        }
+        intervalTick._inclusiveTicks += inclusive;
+        intervalTick._exclusiveTicks += exclusive;
+      }
+    }
+  }
+
+  void clearTicks() {
+    exclusiveTicks = 0;
+    inclusiveTicks = 0;
+    normalizedExclusiveTicks = 0;
+    normalizedInclusiveTicks = 0;
+    formattedInclusiveTicks = '';
+    formattedExclusiveTicks = '';
+    formattedExclusivePercent = '';
+  }
+
+  ProfileCode.fromMap(this.tableIndex, this.profile, this.code, Map data) {
+    assert(profile != null);
+    assert(code != null);
+
+    code.profile = this;
+
+    if (code.kind == M.CodeKind.stub) {
+      attributes.add('stub');
+    } else if (code.kind == M.CodeKind.dart) {
+      if (code.isNative) {
+        attributes.add('ffi'); // Not to be confused with a C function.
+      } else {
+        attributes.add('dart');
+      }
+      if (code.hasIntrinsic) {
+        attributes.add('intrinsic');
+      }
+      if (code.isOptimized) {
+        attributes.add('optimized');
+      } else {
+        attributes.add('unoptimized');
+      }
+    } else if (code.kind == M.CodeKind.tag) {
+      attributes.add('tag');
+    } else if (code.kind == M.CodeKind.native) {
+      attributes.add('native');
+    }
+    inclusiveTicks = data['inclusiveTicks'];
+    exclusiveTicks = data['exclusiveTicks'];
+
+    normalizedExclusiveTicks = exclusiveTicks / profile.sampleCount;
+
+    normalizedInclusiveTicks = inclusiveTicks / profile.sampleCount;
+
+    var ticks = data['ticks'];
+    if (ticks != null) {
+      _processTicks(ticks);
+    }
+
+    formattedExclusivePercent =
+        Utils.formatPercent(exclusiveTicks, profile.sampleCount);
+
+    formattedInclusiveTicks =
+        '${Utils.formatPercent(inclusiveTicks, profile.sampleCount)} '
+        '($inclusiveTicks)';
+
+    formattedExclusiveTicks =
+        '${Utils.formatPercent(exclusiveTicks, profile.sampleCount)} '
+        '($exclusiveTicks)';
+  }
+
+  _recordCaller(ProfileCode caller, int count) {
+    var r = callers[caller];
+    if (r == null) {
+      r = 0;
+    }
+    callers[caller] = r + count;
+  }
+
+  _recordCallee(ProfileCode callee, int count) {
+    var r = callees[callee];
+    if (r == null) {
+      r = 0;
+    }
+    callees[callee] = r + count;
+  }
+
+  void _normalizeTicks() {
+    normalizedExclusiveTicks = exclusiveTicks / profile.sampleCount;
+    normalizedInclusiveTicks = inclusiveTicks / profile.sampleCount;
+
+    formattedExclusivePercent =
+        Utils.formatPercent(exclusiveTicks, profile.sampleCount);
+
+    formattedInclusiveTicks =
+        '${Utils.formatPercent(inclusiveTicks, profile.sampleCount)} '
+        '($inclusiveTicks)';
+
+    formattedExclusiveTicks =
+        '${Utils.formatPercent(exclusiveTicks, profile.sampleCount)} '
+        '($exclusiveTicks)';
+  }
+
+  void tickTag() {
+    // All functions *except* those for tags are ticked in the VM while
+    // generating the CpuSamples response.
+    if (code.kind != M.CodeKind.tag) {
+      throw StateError('Only tags should be ticked. '
+          'Attempted to tick: ${code.name}');
+    }
+    ++exclusiveTicks;
+    ++inclusiveTicks;
+    _normalizeTicks();
+  }
+}
+
+class ProfileFunction implements M.ProfileFunction {
+  final int tableIndex;
+  final SampleProfile profile;
+  final ServiceFunction function;
+  final String resolvedUrl;
+  final callers = <ProfileFunction, int>{};
+  final callees = <ProfileFunction, int>{};
+
+  // Absolute ticks:
+  int exclusiveTicks = 0;
+  int inclusiveTicks = 0;
+
+  // Global percentages:
+  double normalizedExclusiveTicks = 0.0;
+  double normalizedInclusiveTicks = 0.0;
+
+  String formattedInclusiveTicks = '';
+  String formattedExclusiveTicks = '';
+  String formattedExclusivePercent = '';
+  final attributes = <String>{};
+
+  void clearTicks() {
+    exclusiveTicks = 0;
+    inclusiveTicks = 0;
+    normalizedExclusiveTicks = 0;
+    normalizedInclusiveTicks = 0;
+    formattedInclusiveTicks = '';
+    formattedExclusiveTicks = '';
+    formattedExclusivePercent = '';
+  }
+
+  void _addKindBasedAttributes(Set<String> attribs) {
+    if (function.kind == M.FunctionKind.tag) {
+      attribs.add('tag');
+    } else if (function.kind == M.FunctionKind.stub) {
+      attribs.add('stub');
+    } else if (function.kind == M.FunctionKind.native) {
+      attribs.add('native');
+    } else if (M.isSyntheticFunction(function.kind)) {
+      attribs.add('synthetic');
+    } else if (function.isNative) {
+      attribs.add('ffi'); // Not to be confused with a C function.
+    } else {
+      attribs.add('dart');
+    }
+    if (function.hasIntrinsic == true) {
+      attribs.add('intrinsic');
+    }
+  }
+
+  ProfileFunction.fromMap(
+      this.tableIndex, this.profile, this.function, Map data)
+      : resolvedUrl = data['resolvedUrl'] {
+    function.profile = this;
+    _addKindBasedAttributes(attributes);
+    exclusiveTicks = data['exclusiveTicks'];
+    inclusiveTicks = data['inclusiveTicks'];
+    _normalizeTicks();
+  }
+
+  _recordCaller(ProfileFunction caller, int count) {
+    var r = callers[caller];
+    if (r == null) {
+      r = 0;
+    }
+    callers[caller] = r + count;
+  }
+
+  _recordCallee(ProfileFunction callee, int count) {
+    var r = callees[callee];
+    if (r == null) {
+      r = 0;
+    }
+    callees[callee] = r + count;
+  }
+
+  void _normalizeTicks() {
+    normalizedExclusiveTicks = exclusiveTicks / profile.sampleCount;
+    normalizedInclusiveTicks = inclusiveTicks / profile.sampleCount;
+
+    formattedExclusivePercent =
+        Utils.formatPercent(exclusiveTicks, profile.sampleCount);
+
+    formattedInclusiveTicks =
+        '${Utils.formatPercent(inclusiveTicks, profile.sampleCount)} '
+        '($inclusiveTicks)';
+
+    formattedExclusiveTicks =
+        '${Utils.formatPercent(exclusiveTicks, profile.sampleCount)} '
+        '($exclusiveTicks)';
+  }
+
+  void tickTag() {
+    // All functions *except* those for functions are ticked in the VM while
+    // generating the CpuSamples response.
+    if (function.kind != M.FunctionKind.tag) {
+      throw StateError('Only tags should be ticked. '
+          'Attempted to tick: ${function.name}');
+    }
+    ++exclusiveTicks;
+    ++inclusiveTicks;
+    _normalizeTicks();
+  }
+}
+
+class SampleProfile extends M.SampleProfile {
+  Isolate isolate;
+
+  int sampleCount = 0;
+  int samplePeriod = 0;
+  double sampleRate = 0.0;
+  int pid = 0;
+  int maxStackDepth = 0;
+
+  double timeSpan = 0.0;
+
+  M.SampleProfileTag tagOrder = M.SampleProfileTag.none;
+
+  final _functionTagMapping = <String, int>{};
+  final _codeTagMapping = <String, int>{};
+
+  final List samples = [];
+  final codes = <ProfileCode>[];
+  bool _builtCodeCalls = false;
+  final functions = <ProfileFunction>[];
+  bool _builtFunctionCalls = false;
+
+  static const String _kCode = 'code';
+  static const String _kCodes = '_codes';
+  static const String _kCodeStack = '_codeStack';
+  static const String _kFunction = 'function';
+  static const String _kFunctions = 'functions';
+  static const String _kNativeAllocationSizeBytes =
+      '_nativeAllocationSizeBytes';
+  static const String _kPid = 'pid';
+  static const String _kSampleCount = 'sampleCount';
+  static const String _kSamplePeriod = 'samplePeriod';
+  static const String _kSamples = 'samples';
+  static const String _kStack = 'stack';
+  static const String _kMaxStackDepth = 'maxStackDepth';
+  static const String _kTimeSpan = 'timespan';
+  static const String _kUserTag = 'userTag';
+  static const String _kVmTag = 'vmTag';
+
+  // Names of special tag types.
+  static const String kNativeTag = 'Native';
+  static const String kRootTag = 'Root';
+  static const String kRuntimeTag = 'Runtime';
+  static const String kTruncatedTag = '[Truncated]';
+
+  // Used to stash trie information in samples for timeline processing.
+  static const String kTimelineFunctionTrie = 'timelineFunctionTrie';
+
+  CodeCallTree loadCodeTree(M.ProfileTreeDirection direction) {
+    switch (direction) {
+      case M.ProfileTreeDirection.inclusive:
+        return _loadCodeTree(true);
+      case M.ProfileTreeDirection.exclusive:
+        return _loadCodeTree(false);
+    }
+    throw Exception('Unknown ProfileTreeDirection');
+  }
+
+  FunctionCallTree loadFunctionTree(M.ProfileTreeDirection direction) {
+    switch (direction) {
+      case M.ProfileTreeDirection.inclusive:
+        return _loadFunctionTree(true);
+      case M.ProfileTreeDirection.exclusive:
+        return _loadFunctionTree(false);
+    }
+    throw Exception('Unknown ProfileTreeDirection');
+  }
+
+  buildCodeCallerAndCallees() {
+    if (_builtCodeCalls) {
+      return;
+    }
+    _builtCodeCalls = true;
+    var tree = loadCodeTree(M.ProfileTreeDirection.inclusive);
+    tree._recordCallerAndCallees();
+  }
+
+  buildFunctionCallerAndCallees() {
+    if (_builtFunctionCalls) {
+      return;
+    }
+    _builtFunctionCalls = true;
+    var tree = loadFunctionTree(M.ProfileTreeDirection.inclusive);
+    tree._markFunctionCalls();
+  }
+
+  clear() {
+    pid = -1;
+    sampleCount = 0;
+    samplePeriod = 0;
+    sampleRate = 0.0;
+    maxStackDepth = 0;
+    timeSpan = 0.0;
+    codes.clear();
+    functions.clear();
+    _builtCodeCalls = false;
+    _builtFunctionCalls = false;
+  }
+
+  Future load(ServiceObjectOwner owner, ServiceMap profile) async {
+    await loadProgress(owner, profile).drain();
+  }
+
+  static Future sleep([Duration duration = const Duration(microseconds: 0)]) =>
+      Future.delayed(duration);
+
+  Future _loadCommon(ServiceObjectOwner owner, ServiceMap profile,
+      [StreamController<double> progress]) async {
+    final watch = Stopwatch();
+    watch.start();
+    int count = 0;
+    var needToUpdate = () {
+      if (progress == null) {
+        return false;
+      }
+      count++;
+      if (((count % 256) == 0) && (watch.elapsedMilliseconds > 16)) {
+        watch.reset();
+        return true;
+      }
+      return false;
+    };
+    var signal = (double p) {
+      if (progress == null) {
+        return null;
+      }
+      progress.add(p);
+      return sleep();
+    };
+    try {
+      clear();
+      progress?.add(0.0);
+      if (profile == null) {
+        return;
+      }
+
+      if ((owner != null) && (owner is Isolate)) {
+        isolate = owner;
+        isolate.resetCachedProfileData();
+      }
+
+      pid = profile[_kPid];
+      sampleCount = profile[_kSampleCount];
+      samplePeriod = profile[_kSamplePeriod];
+      sampleRate = (Duration.microsecondsPerSecond / samplePeriod);
+      maxStackDepth = profile[_kMaxStackDepth];
+      timeSpan = profile[_kTimeSpan];
+
+      num length = 0;
+
+      if (profile.containsKey(_kCodes)) {
+        length += profile[_kCodes].length;
+      }
+      length += profile[_kFunctions].length;
+
+      // Process code table.
+      int tableIndex = 0;
+      if (profile.containsKey(_kCodes)) {
+        for (var codeRegion in profile[_kCodes]) {
+          if (needToUpdate()) {
+            await signal(count * 100.0 / length);
+          }
+          Code code = codeRegion[_kCode];
+          assert(code != null);
+          codes.add(ProfileCode.fromMap(tableIndex, this, code, codeRegion));
+          ++tableIndex;
+        }
+      }
+      // Process function table.
+      tableIndex = 0;
+      for (var profileFunction in profile[_kFunctions]) {
+        if (needToUpdate()) {
+          await signal(count * 100 / length);
+        }
+        ServiceFunction function = profileFunction[_kFunction];
+        assert(function != null);
+        functions.add(ProfileFunction.fromMap(
+            tableIndex, this, function, profileFunction));
+        ++tableIndex;
+      }
+      if (profile.containsKey(_kCodes)) {
+        _buildCodeTagMapping();
+      }
+
+      _buildFunctionTagMapping();
+
+      samples.addAll(profile[_kSamples]);
+    } finally {
+      progress?.close();
+    }
+  }
+
+  Stream<double> loadProgress(ServiceObjectOwner owner, ServiceMap profile) {
+    Logger.root.info('sampling counters ${profile['_counters']}');
+
+    var progress = StreamController<double>.broadcast();
+
+    _loadCommon(owner, profile, progress);
+    return progress.stream;
+  }
+
+  // Helpers for reading optional flags from a sample.
+  static bool _isNativeEntryTag(Map sample) =>
+      sample.containsKey('nativeEntryTag');
+  static bool _isRuntimeEntryTag(Map sample) =>
+      sample.containsKey('runtimeEntryTag');
+  static bool _isTruncated(Map sample) => sample.containsKey('truncated');
+  static bool _isNativeAllocationSample(Map sample) =>
+      sample.containsKey('_nativeAllocationSizeBytes');
+
+  int _getProfileFunctionTagIndex(String tag) {
+    if (_functionTagMapping.containsKey(tag)) {
+      return _functionTagMapping[tag];
+    }
+    throw ArgumentError('$tag is not a valid tag!');
+  }
+
+  int _getProfileCodeTagIndex(String tag) {
+    if (_codeTagMapping.containsKey(tag)) {
+      return _codeTagMapping[tag];
+    }
+    throw ArgumentError('$tag is not a valid tag!');
+  }
+
+  void _buildFunctionTagMapping() {
+    for (int i = 0; i < functions.length; ++i) {
+      final function = functions[i].function;
+      if (function.kind == M.FunctionKind.tag) {
+        _functionTagMapping[function.name] = i;
+      }
+    }
+  }
+
+  void _buildCodeTagMapping() {
+    for (int i = 0; i < codes.length; ++i) {
+      final code = codes[i].code;
+      if (code.kind == M.CodeKind.tag) {
+        _codeTagMapping[code.name] = i;
+      }
+    }
+  }
+
+  void _clearProfileFunctionTagTicks() =>
+      _functionTagMapping.forEach((String name, int tableIndex) {
+        // Truncated tag is ticked in the VM, so don't clear it.
+        if (name != kTruncatedTag) {
+          functions[tableIndex].clearTicks();
+        }
+      });
+
+  void _clearProfileCodeTagTicks() =>
+      _codeTagMapping.forEach((String name, int tableIndex) {
+        // Truncated tag is ticked in the VM, so don't clear it.
+        if (name != kTruncatedTag) {
+          codes[tableIndex].clearTicks();
+        }
+      });
+
+  NodeT _appendUserTag<NodeT extends CallTreeNode>(
+      String userTag, NodeT current, Map sample) {
+    bool isCode = (current is CodeCallTreeNode);
+    try {
+      final tableIndex = isCode
+          ? _getProfileCodeTagIndex(userTag)
+          : _getProfileFunctionTagIndex(userTag);
+      current = current.getChild(tableIndex);
+      current.tick(sample);
+    } catch (_) {/* invalid tag */} finally {
+      return current;
+    }
+  }
+
+  NodeT _appendTruncatedTag<NodeT extends CallTreeNode>(
+      NodeT current, Map sample) {
+    final isCode = (current is CodeCallTreeNode);
+    try {
+      final tableIndex = isCode
+          ? _getProfileCodeTagIndex(kTruncatedTag)
+          : _getProfileFunctionTagIndex(kTruncatedTag);
+      current = current.getChild(tableIndex);
+      current.tick(sample);
+      // We don't need to tick the tag itself since this is done in the VM for
+      // the truncated tag, unlike other VM and user tags.
+    } catch (_) {/* invalid tag */} finally {
+      return current;
+    }
+  }
+
+  FunctionCallTreeNode _getFunctionTagAndTick(
+      String tag, FunctionCallTreeNode current, Map sample) {
+    try {
+      final tableIndex = _getProfileFunctionTagIndex(tag);
+      current = current.getChild(tableIndex);
+      current.tick(sample);
+    } catch (_) {/* invalid tag */} finally {
+      return current;
+    }
+  }
+
+  CodeCallTreeNode _getCodeTagAndTick(
+      String tag, CodeCallTreeNode current, Map sample) {
+    try {
+      final tableIndex = _getProfileCodeTagIndex(tag);
+      current = current.getChild(tableIndex);
+      current.tick(sample);
+    } catch (_) {/* invalid tag */} finally {
+      return current;
+    }
+  }
+
+  NodeT _getTagAndTick<NodeT extends CallTreeNode>(
+      String tag, NodeT current, Map sample) {
+    if (current is FunctionCallTreeNode) {
+      return _getFunctionTagAndTick(tag, current, sample) as NodeT;
+    } else if (current is CodeCallTreeNode) {
+      return _getCodeTagAndTick(tag, current, sample) as NodeT;
+    }
+    throw ArgumentError('Unexpected tree type: $NodeT');
+  }
+
+  NodeT _appendVMTag<NodeT extends CallTreeNode>(
+      String vmTag, NodeT current, Map sample) {
+    if (_isNativeEntryTag(sample)) {
+      return _getTagAndTick(kNativeTag, current, sample);
+    } else if (_isRuntimeEntryTag(sample)) {
+      return _getTagAndTick(kRuntimeTag, current, sample);
+    } else {
+      return _getTagAndTick(vmTag, current, sample);
+    }
+  }
+
+  NodeT _appendSpecificNativeRuntimeEntryVMTag<NodeT extends CallTreeNode>(
+      NodeT current, Map sample) {
+    // Only Native and Runtime entries have a second VM tag.
+    if (!_isNativeEntryTag(sample) && !_isRuntimeEntryTag(sample)) {
+      return current;
+    }
+    final vmTag = sample[_kVmTag];
+    return _getTagAndTick(vmTag, current, sample);
+  }
+
+  NodeT _appendVMTags<NodeT extends CallTreeNode>(
+      String vmTag, NodeT current, Map sample) {
+    current = _appendVMTag(vmTag, current, sample);
+    current = _appendSpecificNativeRuntimeEntryVMTag(current, sample);
+    return current;
+  }
+
+  void _tickTags(String vmTag, String userTag, bool tickCode) {
+    if (tickCode) {
+      final vmTagIndex = _getProfileCodeTagIndex(vmTag);
+      codes[vmTagIndex].tickTag();
+
+      final userTagIndex = _getProfileCodeTagIndex(userTag);
+      codes[userTagIndex].tickTag();
+    } else {
+      final vmTagIndex = _getProfileFunctionTagIndex(vmTag);
+      functions[vmTagIndex].tickTag();
+
+      final userTagIndex = _getProfileFunctionTagIndex(userTag);
+      functions[userTagIndex].tickTag();
+    }
+  }
+
+  NodeT _appendTags<NodeT extends CallTreeNode>(
+      String vmTag, String userTag, NodeT current, Map sample) {
+    final tickCode = (current is M.CodeCallTreeNode);
+    _tickTags(vmTag, userTag, tickCode);
+    if (tagOrder == M.SampleProfileTag.none) {
+      return current;
+    }
+    // User first.
+    if (tagOrder == M.SampleProfileTag.userVM ||
+        tagOrder == M.SampleProfileTag.userOnly) {
+      current = _appendUserTag(userTag, current, sample);
+      // Only user.
+      if (tagOrder == M.SampleProfileTag.userOnly) {
+        return current;
+      }
+      return _appendVMTags(vmTag, current, sample);
+    }
+
+    // VM first.
+    current = _appendVMTags(vmTag, current, sample);
+    // Only VM.
+    if (tagOrder == M.SampleProfileTag.vmOnly) {
+      return current;
+    }
+    return _appendUserTag(userTag, current, sample);
+  }
+
+  NodeT _processFrame<NodeT extends CallTreeNode>(NodeT parent, int sampleIndex,
+      Map sample, List<int> stack, int frameIndex, bool inclusive) {
+    final child = parent.getChild(stack[frameIndex]);
+    child.tick(sample, exclusive: (frameIndex == 0));
+    return child;
+  }
+
+  FunctionCallTreeNode buildFunctionTrie(bool inclusive) {
+    final root = FunctionCallTreeNode.fromIndex(
+        this, _getProfileFunctionTagIndex(kRootTag));
+
+    for (int sampleIndex = 0; sampleIndex < samples.length; ++sampleIndex) {
+      final sample = samples[sampleIndex];
+      FunctionCallTreeNode current = root;
+      // Tick the root for each sample as we always visit the root node.
+      root.tick(sample);
+
+      // VM + User tags.
+      final vmTag = sample[_kVmTag];
+      final userTag = sample[_kUserTag];
+      final stack = sample[_kStack].cast<int>();
+      current = _appendTags(vmTag, userTag, current, sample);
+
+      if (inclusive) {
+        if (_isTruncated(sample)) {
+          current = _appendTruncatedTag(current, sample);
+        }
+        for (int frameIndex = stack.length - 1; frameIndex >= 0; --frameIndex) {
+          current = _processFrame(
+              current, sampleIndex, sample, stack, frameIndex, true);
+        }
+
+        // Used by the timeline to find the root of each sample.
+        sample[kTimelineFunctionTrie] = current;
+      } else {
+        for (int frameIndex = 0; frameIndex < stack.length; ++frameIndex) {
+          current = _processFrame(
+              current, sampleIndex, sample, stack, frameIndex, false);
+        }
+
+        if (_isTruncated(sample)) {
+          current = _appendTruncatedTag(current, sample);
+        }
+      }
+    }
+    return root;
+  }
+
+  CodeCallTreeNode buildCodeTrie(bool inclusive) {
+    final root =
+        CodeCallTreeNode.fromIndex(this, _getProfileCodeTagIndex(kRootTag));
+
+    for (int sampleIndex = 0; sampleIndex < samples.length; ++sampleIndex) {
+      final sample = samples[sampleIndex];
+
+      CodeCallTreeNode current = root;
+      // Tick the root for each sample as we always visit the root node.
+      root.tick(sample);
+
+      // VM + User tags.
+      final vmTag = sample[_kVmTag];
+      final userTag = sample[_kUserTag];
+      final stack = sample[_kCodeStack].cast<int>();
+      current = _appendTags(vmTag, userTag, current, sample);
+
+      if (inclusive) {
+        if (_isTruncated(sample)) {
+          current = _appendTruncatedTag(current, sample);
+        }
+        for (int frameIndex = stack.length - 1; frameIndex >= 0; --frameIndex) {
+          current = _processFrame(
+              current, sampleIndex, sample, stack, frameIndex, true);
+        }
+      } else {
+        for (int frameIndex = 0; frameIndex < stack.length; ++frameIndex) {
+          current = _processFrame(
+              current, sampleIndex, sample, stack, frameIndex, false);
+        }
+
+        if (_isTruncated(sample)) {
+          current = _appendTruncatedTag(current, sample);
+        }
+      }
+    }
+    return root;
+  }
+
+  FunctionCallTree _loadFunctionTree(bool inclusive) {
+    // Since we're only ticking tag functions when building the trie, we need
+    // to clean up ticks from previous tree builds.
+    _clearProfileFunctionTagTicks();
+
+    // Read the tree, returns the root node.
+    final root = buildFunctionTrie(inclusive);
+    root.sortChildren();
+    return FunctionCallTree(inclusive, root);
+  }
+
+  CodeCallTree _loadCodeTree(bool inclusive) {
+    // Since we're only ticking tag code when building the trie, we need
+    // to clean up ticks from previous tree builds.
+    _clearProfileCodeTagTicks();
+
+    // Read the tree, returns the root node.
+    final root = buildCodeTrie(inclusive);
+    root.sortChildren();
+    return CodeCallTree(inclusive, root);
+  }
+
+  int approximateMillisecondsForCount(count) {
+    return (count * samplePeriod) ~/ Duration.microsecondsPerMillisecond;
+  }
+
+  double approximateSecondsForCount(count) {
+    return (count * samplePeriod) / Duration.microsecondsPerSecond;
+  }
+}
diff --git a/runtime/observatory_2/lib/src/service/object.dart b/runtime/observatory_2/lib/src/service/object.dart
new file mode 100644
index 0000000..046513d
--- /dev/null
+++ b/runtime/observatory_2/lib/src/service/object.dart
@@ -0,0 +1,4926 @@
+// Copyright (c) 2014, 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 service;
+
+// Some value smaller than the object ring, so requesting a large array
+// doesn't result in an expired ref because the elements lapped it in the
+// object ring.
+const int kDefaultFieldLimit = 100;
+
+/// Helper function for canceling a Future<StreamSubscription>.
+Future cancelFutureSubscription(
+    Future<StreamSubscription> subscriptionFuture) async {
+  if (subscriptionFuture != null) {
+    var subscription = await subscriptionFuture;
+    return subscription.cancel();
+  } else {
+    return null;
+  }
+}
+
+/// An RpcException represents an exceptional event that happened
+/// while invoking an rpc.
+abstract class RpcException implements Exception, M.BasicException {
+  RpcException(this.message);
+
+  String message;
+}
+
+/// A ServerRpcException represents an error returned by the VM.
+class ServerRpcException extends RpcException implements M.RequestException {
+  /// A list of well-known server error codes.
+  static const kParseError = -32700;
+  static const kInvalidRequest = -32600;
+  static const kMethodNotFound = -32601;
+  static const kInvalidParams = -32602;
+  static const kInternalError = -32603;
+  static const kFeatureDisabled = 100;
+  static const kCannotAddBreakpoint = 102;
+  static const kStreamAlreadySubscribed = 103;
+  static const kStreamNotSubscribed = 104;
+  static const kIsolateMustBeRunnable = 105;
+  static const kIsolateMustBePaused = 106;
+  static const kCannotResume = 107;
+  static const kIsolateIsReloading = 108;
+  static const kIsolateReloadBarred = 109;
+  static const kIsolateMustHaveReloaded = 110;
+  static const kServiceAlreadyRegistered = 111;
+  static const kServiceDisappeared = 112;
+  static const kExpressionCompilationError = 113;
+
+  static const kFileSystemAlreadyExists = 1001;
+  static const kFileSystemDoesNotExist = 1002;
+  static const kFileDoesNotExist = 1003;
+
+  int code;
+  Map data;
+
+  static _getMessage(Map errorMap) {
+    Map data = errorMap['data'];
+    if (data != null && data['details'] != null) {
+      return data['details'];
+    } else {
+      return errorMap['message'];
+    }
+  }
+
+  ServerRpcException.fromMap(Map errorMap) : super(_getMessage(errorMap)) {
+    code = errorMap['code'];
+    data = errorMap['data'];
+  }
+
+  String toString() => 'ServerRpcException(${message})';
+}
+
+/// A NetworkRpcException is used to indicate that an rpc has
+/// been canceled due to network error.
+class NetworkRpcException extends RpcException
+    implements M.ConnectionException {
+  NetworkRpcException(String message) : super(message);
+
+  String toString() => 'NetworkRpcException(${message})';
+}
+
+Future<ServiceObject> ignoreNetworkErrors(Object error,
+    [ServiceObject resultOnNetworkError = null]) {
+  if (error is NetworkRpcException) {
+    return new Future.value(resultOnNetworkError);
+  }
+  return new Future.error(error);
+}
+
+class MalformedResponseRpcException extends RpcException {
+  MalformedResponseRpcException(String message, this.response) : super(message);
+
+  Map response;
+
+  String toString() => 'MalformedResponseRpcException(${message})';
+}
+
+/// A [ServiceObject] represents a persistent object within the vm.
+abstract class ServiceObject implements M.ObjectRef {
+  static int LexicalSortName(ServiceObject o1, ServiceObject o2) {
+    return o1.name.compareTo(o2.name);
+  }
+
+  List<T> removeDuplicatesAndSortLexical<T extends ServiceObject>(
+      List<T> list) {
+    return list.toSet().toList()..sort(LexicalSortName);
+  }
+
+  /// The owner of this [ServiceObject].  This can be an [Isolate], a
+  /// [VM], or null.
+  ServiceObjectOwner get owner => _owner;
+  ServiceObjectOwner _owner;
+
+  /// The [VM] which owns this [ServiceObject].
+  VM get vm => _owner.vm;
+
+  /// The [Isolate] which owns this [ServiceObject].  May be null.
+  Isolate get isolate => _owner.isolate;
+
+  /// The id of this object.
+  String get id => _id;
+  String _id;
+
+  /// The user-level type of this object.
+  String get type => _type;
+  String _type;
+
+  /// The vm type of this object.
+  String get vmType => _vmType;
+  String _vmType;
+
+  bool get isICData => vmType == 'ICData';
+  bool get isMegamorphicCache => vmType == 'MegamorphicCache';
+  bool get isInstructions => vmType == 'Instructions';
+  bool get isObjectPool => vmType == 'ObjectPool';
+  bool get isContext => type == 'Context';
+  bool get isError => type == 'Error';
+  bool get isInstance => type == 'Instance';
+  bool get isSentinel => type == 'Sentinel';
+  bool get isMessage => type == 'Message';
+
+  // Kinds of Instance.
+  bool get isAbstractType => false;
+  bool get isNull => false;
+  bool get isBool => false;
+  bool get isDouble => false;
+  bool get isString => false;
+  bool get isInt => false;
+  bool get isList => false;
+  bool get isMap => false;
+  bool get isTypedData => false;
+  bool get isRegExp => false;
+  bool get isMirrorReference => false;
+  bool get isWeakProperty => false;
+  bool get isClosure => false;
+  bool get isStackTrace => false;
+  bool get isSimdValue => false;
+  bool get isPlainInstance => false;
+
+  /// Has this object been fully loaded?
+  bool get loaded => _loaded;
+  bool _loaded = false;
+  // TODO(turnidge): Make loaded observable and get rid of loading
+  // from Isolate.
+
+  /// Is this object cacheable?  That is, is it impossible for the [id]
+  /// of this object to change?
+  bool _canCache;
+  bool get canCache => _canCache;
+
+  /// Is this object immutable after it is [loaded]?
+  bool get immutable => false;
+
+  String name;
+  String vmName;
+
+  /// Creates an empty [ServiceObject].
+  ServiceObject._empty(this._owner);
+
+  /// Creates a [ServiceObject] initialized from [map].
+  factory ServiceObject._fromMap(ServiceObjectOwner owner, Map map) {
+    if (map == null) {
+      return null;
+    }
+    if (!_isServiceMap(map)) {
+      Logger.root.severe('Malformed service object: $map');
+    }
+    assert(_isServiceMap(map));
+    var type = _stripRef(map['type']);
+    var vmType = map['_vmType'] != null ? map['_vmType'] : type;
+    var obj = null;
+    assert(type != 'VM');
+    switch (type) {
+      case 'Breakpoint':
+        obj = new Breakpoint._empty(owner);
+        break;
+      case 'Class':
+        obj = new Class._empty(owner);
+        break;
+      case 'Code':
+        obj = new Code._empty(owner);
+        break;
+      case 'Context':
+        obj = new Context._empty(owner);
+        break;
+      case 'Counter':
+        obj = new ServiceMetric._empty(owner);
+        break;
+      case 'Error':
+        obj = new DartError._empty(owner);
+        break;
+      case 'Field':
+        obj = new Field._empty(owner);
+        break;
+      case 'Frame':
+        obj = new Frame._empty(owner);
+        break;
+      case 'Function':
+      case 'NativeFunction':
+        obj = new ServiceFunction._empty(owner);
+        break;
+      case 'Gauge':
+        obj = new ServiceMetric._empty(owner);
+        break;
+      case 'Isolate':
+        obj = new Isolate._empty(owner.vm);
+        break;
+      case 'IsolateGroup':
+        obj = new IsolateGroup._empty(owner.vm);
+        break;
+      case 'Library':
+        obj = new Library._empty(owner);
+        break;
+      case 'Message':
+        obj = new ServiceMessage._empty(owner);
+        break;
+      case 'SourceLocation':
+        obj = new SourceLocation._empty(owner);
+        break;
+      case '_Thread':
+        obj = new Thread._empty(owner);
+        break;
+      case 'UnresolvedSourceLocation':
+        obj = new UnresolvedSourceLocation._empty(owner);
+        break;
+      case 'Object':
+        switch (vmType) {
+          case 'ICData':
+            obj = new ICData._empty(owner);
+            break;
+          case 'LocalVarDescriptors':
+            obj = new LocalVarDescriptors._empty(owner);
+            break;
+          case 'MegamorphicCache':
+            obj = new MegamorphicCache._empty(owner);
+            break;
+          case 'ObjectPool':
+            obj = new ObjectPool._empty(owner);
+            break;
+          case 'PcDescriptors':
+            obj = new PcDescriptors._empty(owner);
+            break;
+          case 'SingleTargetCache':
+            obj = new SingleTargetCache._empty(owner);
+            break;
+          case 'SubtypeTestCache':
+            obj = new SubtypeTestCache._empty(owner);
+            break;
+          case 'UnlinkedCall':
+            obj = new UnlinkedCall._empty(owner);
+            break;
+        }
+        break;
+      case 'Event':
+        obj = new ServiceEvent._empty(owner);
+        break;
+      case 'Script':
+        obj = new Script._empty(owner);
+        break;
+      case 'Socket':
+        obj = new Socket._empty(owner);
+        break;
+      case 'Sentinel':
+        obj = new Sentinel._empty(owner);
+        break;
+      case 'InstanceSet':
+        obj = new InstanceSet._empty(owner);
+        break;
+      case 'TypeArguments':
+        obj = new TypeArguments._empty(owner);
+        break;
+      case 'Instance':
+        obj = new Instance._empty(owner);
+        break;
+      default:
+        break;
+    }
+    if (obj == null) {
+      obj = new ServiceMap._empty(owner);
+    }
+    obj.updateFromServiceMap(map);
+    return obj;
+  }
+
+  /// If [this] was created from a reference, load the full object
+  /// from the service by calling [reload]. Else, return [this].
+  Future<ServiceObject> load() {
+    if (loaded) {
+      return new Future.value(this);
+    }
+    // Call reload which will fill in the entire object.
+    return reload();
+  }
+
+  Future<ServiceObject> _inProgressReload;
+
+  Future<Map> _fetchDirect({int count: kDefaultFieldLimit}) {
+    Map params = {
+      'objectId': id,
+      'count': count,
+    };
+    return isolate.invokeRpcNoUpgrade('getObject', params);
+  }
+
+  /// Reload [this]. Returns a future which completes to [this] or
+  /// an exception.
+  Future<ServiceObject> reload({int count: kDefaultFieldLimit}) {
+    // TODO(turnidge): Checking for a null id should be part of the
+    // "immutable" check.
+    bool hasId = (id != null) && (id != '');
+    bool isVM = this is VM;
+    // We should always reload the VM.
+    // We can't reload objects without an id.
+    // We shouldn't reload an immutable and already loaded object.
+    bool skipLoad = !isVM && (!hasId || (immutable && loaded));
+    if (skipLoad) {
+      return new Future.value(this);
+    }
+    if (_inProgressReload == null) {
+      var completer = new Completer<ServiceObject>();
+      _inProgressReload = completer.future;
+      _fetchDirect(count: count).then((Map map) {
+        var mapType = _stripRef(map['type']);
+        if (mapType == 'Sentinel') {
+          // An object may have been collected, etc.
+          completer.complete(new ServiceObject._fromMap(owner, map));
+        } else {
+          // TODO(turnidge): Check for vmType changing as well?
+          assert(mapType == _type);
+          updateFromServiceMap(map);
+          completer.complete(this);
+        }
+      }).catchError((e, st) {
+        Logger.root.severe("Unable to reload object: $e\n$st");
+        _inProgressReload = null;
+        completer.completeError(e, st);
+      }).whenComplete(() {
+        // This reload is complete.
+        _inProgressReload = null;
+      });
+    }
+    return _inProgressReload;
+  }
+
+  /// Update [this] using [map] as a source. [map] can be a reference.
+  void updateFromServiceMap(Map map) {
+    assert(_isServiceMap(map));
+
+    // Don't allow the type to change on an object update.
+    var mapIsRef = _hasRef(map['type']);
+    var mapType = _stripRef(map['type']);
+    assert(_type == null || _type == mapType);
+
+    _canCache = map['fixedId'] == true;
+    if (_id != null && _id != map['id']) {
+      // It is only safe to change an id when the object isn't cacheable.
+      assert(!canCache);
+    }
+    _id = map['id'];
+
+    _type = mapType;
+
+    // When the response specifies a specific vmType, use it.
+    // Otherwise the vmType of the response is the same as the 'user'
+    // type.
+    if (map.containsKey('_vmType')) {
+      _vmType = _stripRef(map['_vmType']);
+    } else {
+      _vmType = _type;
+    }
+
+    _update(map, mapIsRef);
+  }
+
+  // Updates internal state from [map]. [map] can be a reference.
+  void _update(Map map, bool mapIsRef);
+
+  // Helper that can be passed to .catchError that ignores the error.
+  _ignoreError(error, stackTrace) {
+    // do nothing.
+  }
+}
+
+abstract class HeapObject extends ServiceObject implements M.Object {
+  Class clazz;
+  int size;
+  int retainedSize;
+
+  HeapObject._empty(ServiceObjectOwner owner) : super._empty(owner);
+
+  void _update(Map map, bool mapIsRef) {
+    if (map['class'] != null) {
+      // Sent with refs for some types. Load it if available, but don't clobber
+      // it with null for kinds that only send if for full responses.
+      clazz = map['class'];
+    }
+
+    // Load the full class object if the isolate is runnable.
+    if (clazz != null) {
+      if (clazz.isolate.runnable) {
+        // No one awaits on this request so we silence any network errors
+        // that occur here but forward other errors.
+        clazz.load().catchError((error) => ignoreNetworkErrors(error, clazz));
+      }
+    }
+
+    if (mapIsRef) {
+      return;
+    }
+    size = map['size'];
+  }
+}
+
+class RetainingObject implements M.RetainingObject {
+  int get retainedSize => object.retainedSize;
+  final HeapObject object;
+  RetainingObject(this.object);
+}
+
+abstract class ServiceObjectOwner extends ServiceObject {
+  /// Creates an empty [ServiceObjectOwner].
+  ServiceObjectOwner._empty(ServiceObjectOwner owner) : super._empty(owner);
+
+  /// Builds a [ServiceObject] corresponding to the [id] from [map].
+  /// The result may come from the cache.  The result will not necessarily
+  /// be [loaded].
+  ServiceObject getFromMap(Map map);
+
+  Future<ServiceObject> invokeRpc(String method, Map params);
+}
+
+abstract class Location implements M.Location {
+  Script get script;
+  int get tokenPos;
+  Future<int> getLine();
+  Future<int> getColumn();
+  Future<String> toUserString();
+}
+
+/// A [SourceLocation] represents a location or range in the source code.
+class SourceLocation extends ServiceObject
+    implements Location, M.SourceLocation {
+  Script script;
+  int tokenPos;
+  int endTokenPos;
+
+  Future<int> getLine() async {
+    await script.load();
+    return script.tokenToLine(tokenPos);
+  }
+
+  Future<int> getColumn() async {
+    await script.load();
+    return script.tokenToCol(tokenPos);
+  }
+
+  SourceLocation._empty(ServiceObject owner) : super._empty(owner);
+
+  void _update(Map map, bool mapIsRef) {
+    assert(!mapIsRef);
+    _upgradeCollection(map, owner);
+    script = map['script'];
+    tokenPos = map['tokenPos'];
+    endTokenPos = map['endTokenPos'];
+
+    assert(script != null && tokenPos != null);
+  }
+
+  Future<String> toUserString() async {
+    int line = await getLine();
+    int column = await getColumn();
+    return '${script.name}:${line}:${column}';
+  }
+
+  String toString() {
+    if (endTokenPos == null) {
+      return '${script.name}:token(${tokenPos})';
+    } else {
+      return '${script.name}:tokens(${tokenPos}-${endTokenPos})';
+    }
+  }
+}
+
+/// An [UnresolvedSourceLocation] represents a location in the source
+// code which has not been precisely mapped to a token position.
+class UnresolvedSourceLocation extends ServiceObject
+    implements Location, M.UnresolvedSourceLocation {
+  Script script;
+  String scriptUri;
+  int line;
+  int column;
+  int tokenPos;
+
+  Future<int> getLine() async {
+    if (tokenPos != null) {
+      await script.load();
+      return script.tokenToLine(tokenPos);
+    } else {
+      return line;
+    }
+  }
+
+  Future<int> getColumn() async {
+    if (tokenPos != null) {
+      await script.load();
+      return script.tokenToCol(tokenPos);
+    } else {
+      return column;
+    }
+  }
+
+  UnresolvedSourceLocation._empty(ServiceObject owner) : super._empty(owner);
+
+  void _update(Map map, bool mapIsRef) {
+    assert(!mapIsRef);
+    _upgradeCollection(map, owner);
+    script = map['script'];
+    scriptUri = map['scriptUri'];
+    line = map['line'];
+    column = map['column'];
+    tokenPos = map['tokenPos'];
+
+    assert(script != null || scriptUri != null);
+    assert(line != null || tokenPos != null);
+  }
+
+  Future<String> toUserString() async {
+    StringBuffer sb = new StringBuffer();
+
+    int line = await getLine();
+    int column = await getColumn();
+
+    if (script != null) {
+      sb.write('${script.name}:');
+    } else {
+      sb.write('${scriptUri}:');
+    }
+    if (column != null) {
+      sb.write('${line}:${column}');
+    } else {
+      sb.write('${line}');
+    }
+    return sb.toString();
+  }
+
+  String toString() {
+    StringBuffer sb = new StringBuffer();
+    if (script != null) {
+      sb.write('${script.name}:');
+    } else {
+      sb.write('${scriptUri}:');
+    }
+    if (tokenPos != null) {
+      sb.write('token(${tokenPos})');
+    } else if (column != null) {
+      sb.write('${line}:${column}');
+    } else {
+      sb.write('${line}');
+    }
+    sb.write('[unresolved]');
+    return sb.toString();
+  }
+}
+
+class _EventStreamState {
+  VM _vm;
+  String streamId;
+
+  Function _onDone;
+
+  // A list of all subscribed controllers for this stream.
+  List _controllers = [];
+
+  // Completes when the listen rpc is finished.
+  Future _listenFuture;
+
+  // Completes when then cancel rpc is finished.
+  Future _cancelFuture;
+
+  _EventStreamState(this._vm, this.streamId, this._onDone);
+
+  Future _cancelController(StreamController controller) {
+    _controllers.remove(controller);
+    if (_controllers.isEmpty) {
+      assert(_listenFuture != null);
+      _listenFuture = null;
+      _cancelFuture = _vm._streamCancel(streamId);
+      _cancelFuture.then((_) {
+        if (_controllers.isEmpty) {
+          // No new listeners showed up during cancelation.
+          _onDone();
+        }
+      }).catchError((e) {
+        /* ignore */
+      });
+    }
+    // No need to wait for _cancelFuture here.
+    return new Future.value(null);
+  }
+
+  Future<Stream<ServiceEvent>> addStream() async {
+    var controller;
+    controller = new StreamController<ServiceEvent>(
+        onCancel: () => _cancelController(controller));
+    _controllers.add(controller);
+    if (_cancelFuture != null) {
+      try {
+        await _cancelFuture;
+      } on NetworkRpcException catch (_) {/* ignore */}
+    }
+    if (_listenFuture == null) {
+      _listenFuture = _vm._streamListen(streamId);
+    }
+    try {
+      await _listenFuture;
+    } on NetworkRpcException catch (_) {/* ignore */}
+    return controller.stream;
+  }
+
+  void addEvent(ServiceEvent event) {
+    for (var controller in _controllers) {
+      controller.add(event);
+    }
+  }
+}
+
+/// State for a VM being inspected.
+abstract class VM extends ServiceObjectOwner implements M.VM {
+  VM get vm => this;
+  Isolate get isolate => null;
+  WebSocketVMTarget get target;
+
+  // TODO(turnidge): The connection should not be stored in the VM object.
+  bool get isDisconnected;
+  bool get isConnected;
+
+  // Used for verbose logging.
+  bool verbose = false;
+
+  // TODO(johnmccutchan): Ensure that isolates do not end up in _cache.
+  Map<String, ServiceObject> _cache = new Map<String, ServiceObject>();
+  final Map<String, Isolate> _isolateCache = <String, Isolate>{};
+  final Map<String, IsolateGroup> _isolateGroupCache = <String, IsolateGroup>{};
+
+  // The list of live isolates, ordered by isolate start time.
+  final List<Isolate> isolates = <Isolate>[];
+  final List<Isolate> systemIsolates = <Isolate>[];
+
+  final List<IsolateGroup> isolateGroups = <IsolateGroup>[];
+  final List<IsolateGroup> systemIsolateGroups = <IsolateGroup>[];
+
+  final List<Service> services = <Service>[];
+
+  String version = 'unknown';
+  String hostCPU;
+  String targetCPU;
+  String embedder;
+  int architectureBits;
+  bool assertsEnabled = false;
+  bool typeChecksEnabled = false;
+  int nativeZoneMemoryUsage = 0;
+  int pid = 0;
+  int heapAllocatedMemoryUsage = 0;
+  int heapAllocationCount = 0;
+  int currentMemory;
+  int maxRSS;
+  int currentRSS;
+  bool profileVM = false;
+  DateTime startTime;
+  DateTime refreshTime;
+  Duration get upTime {
+    if (startTime == null) {
+      return null;
+    }
+    return (new DateTime.now().difference(startTime));
+  }
+
+  VM() : super._empty(null) {
+    updateFromServiceMap({'name': 'vm', 'type': '@VM'});
+  }
+
+  void postServiceEvent(String streamId, Map response, Uint8List data) {
+    var map = response;
+    assert(!map.containsKey('_data'));
+    if (data != null) {
+      map['_data'] = data;
+    }
+    if (map['type'] != 'Event') {
+      Logger.root.severe("Expected 'Event' but found '${map['type']}'");
+      return;
+    }
+
+    var eventIsolate = map['isolate'];
+    var event;
+    if (eventIsolate == null) {
+      event = new ServiceObject._fromMap(vm, map);
+    } else {
+      // getFromMap creates the Isolate if it hasn't been seen already.
+      var isolate = getFromMap(map['isolate']);
+      event = new ServiceObject._fromMap(isolate, map);
+      if (event.kind == ServiceEvent.kIsolateExit) {
+        _isolateCache.remove(isolate.id);
+        _buildIsolateList();
+      }
+      if (event.kind == ServiceEvent.kIsolateRunnable) {
+        // Force reload once the isolate becomes runnable so that we
+        // update the root library.
+        isolate.reload();
+      }
+    }
+    var eventStream = _eventStreams[streamId];
+    if (eventStream != null) {
+      eventStream.addEvent(event);
+    } else {
+      Logger.root.warning("Ignoring unexpected event on stream '${streamId}'");
+    }
+  }
+
+  int _compareIsolates(Isolate a, Isolate b) {
+    var aStart = a.startTime;
+    var bStart = b.startTime;
+    if (aStart == null) {
+      if (bStart == null) {
+        return 0;
+      } else {
+        return 1;
+      }
+    }
+    if (bStart == null) {
+      return -1;
+    }
+    return aStart.compareTo(bStart);
+  }
+
+  void _buildIsolateList() {
+    var isolateList =
+        _isolateCache.values.where((i) => !i.isSystemIsolate).toList();
+    isolateList.sort(_compareIsolates);
+    isolates.clear();
+    isolates.addAll(isolateList);
+
+    var systemIsolateList =
+        _isolateCache.values.where((i) => i.isSystemIsolate).toList();
+    systemIsolateList.sort(_compareIsolates);
+    systemIsolates.clear();
+    systemIsolates.addAll(systemIsolateList);
+  }
+
+  void _removeDeadIsolates(List newIsolates) {
+    // Build a set of new isolates.
+    var newIsolateSet = new Set();
+    newIsolates.forEach((iso) => newIsolateSet.add(iso.id));
+
+    // Remove any old isolates which no longer exist.
+    List toRemove = [];
+    _isolateCache.forEach((id, _) {
+      if (!newIsolateSet.contains(id)) {
+        toRemove.add(id);
+      }
+    });
+    toRemove.forEach((id) => _isolateCache.remove(id));
+    _buildIsolateList();
+  }
+
+  static final String _isolateIdPrefix = 'isolates/';
+  static final String _isolateGroupIdPrefix = 'isolateGroups/';
+
+  ServiceObject getFromMap(Map map) {
+    if (map == null) {
+      return null;
+    }
+    var type = _stripRef(map['type']);
+    if (type == 'VM') {
+      // Update this VM object.
+      updateFromServiceMap(map);
+      return this;
+    }
+
+    String id = map['id'];
+    if ((id != null)) {
+      if (id.startsWith(_isolateIdPrefix)) {
+        // Check cache.
+        var isolate = _isolateCache[id];
+        if (isolate == null) {
+          // Add new isolate to the cache.
+          isolate = ServiceObject._fromMap(this, map);
+          _isolateCache[id] = isolate;
+          _buildIsolateList();
+
+          // Eagerly load the isolate.
+          isolate.load().catchError((e, stack) {
+            Logger.root.info('Eagerly loading an isolate failed: $e\n$stack');
+          });
+        } else {
+          isolate.updateFromServiceMap(map);
+        }
+        return isolate;
+      }
+      if (id.startsWith(_isolateGroupIdPrefix)) {
+        // Check cache.
+        var isolateGroup = _isolateGroupCache[id];
+        if (isolateGroup == null) {
+          // Add new isolate to the cache.
+          isolateGroup = new ServiceObject._fromMap(this, map);
+          _isolateGroupCache[id] = isolateGroup;
+          _buildIsolateGroupList();
+
+          // Eagerly load the isolate.
+          isolateGroup.load().catchError((e, stack) {
+            Logger.root
+                .info('Eagerly loading an isolate group failed: $e\n$stack');
+          });
+        } else {
+          isolateGroup.updateFromServiceMap(map);
+        }
+        return isolateGroup;
+      }
+    }
+
+    // Build the object from the map directly.
+    return new ServiceObject._fromMap(this, map);
+  }
+
+  // Note that this function does not reload the isolate if it found
+  // in the cache.
+  Future<Isolate> getIsolate(String isolateId) {
+    if (!loaded) {
+      // Trigger a VM load, then get the isolate.
+      return load().then((_) => getIsolate(isolateId)).catchError(_ignoreError);
+    }
+    return new Future.value(_isolateCache[isolateId]);
+  }
+
+  int _compareIsolateGroups(IsolateGroup a, IsolateGroup b) {
+    return a.id.compareTo(b.id);
+  }
+
+  void _buildIsolateGroupList() {
+    final isolateGroupList = _isolateGroupCache.values
+        .where((g) => !g.isSystemIsolateGroup)
+        .toList();
+    isolateGroupList.sort(_compareIsolateGroups);
+    isolateGroups.clear();
+    isolateGroups.addAll(isolateGroupList);
+
+    final systemIsolateGroupList =
+        _isolateGroupCache.values.where((g) => g.isSystemIsolateGroup).toList();
+    systemIsolateGroupList.sort(_compareIsolateGroups);
+    systemIsolateGroups.clear();
+    systemIsolateGroups.addAll(systemIsolateGroupList);
+  }
+
+  void _removeDeadIsolateGroups(List newIsolateGroups) {
+    // Build a set of new isolates.
+    final Set newIsolateGroupSet =
+        newIsolateGroups.map((iso) => iso.id).toSet();
+
+    // Remove any old isolates which no longer exist.
+    _isolateGroupCache.removeWhere((id, _) => !newIsolateGroupSet.contains(id));
+    _buildIsolateGroupList();
+  }
+
+  // Implemented in subclass.
+  Future<Map> invokeRpcRaw(String method, Map params);
+
+  Future<Map> invokeRpcNoUpgrade(String method, Map params) {
+    return invokeRpcRaw(method, params).then<Map>((Map response) {
+      var map = response;
+      if (Tracer.current != null) {
+        Tracer.current
+            .trace("Received response for ${method}/${params}}", map: map);
+      }
+      if (!_isServiceMap(map)) {
+        var exception = new MalformedResponseRpcException(
+            "Response is missing the 'type' field", map);
+        return new Future.error(exception);
+      }
+      return new Future<Map>.value(map);
+    }).catchError((e) {
+      // Errors pass through.
+      return new Future<Map>.error(e);
+    });
+  }
+
+  Future<ServiceObject> invokeRpc(String method, Map params) {
+    return invokeRpcNoUpgrade(method, params)
+        .then<ServiceObject>((Map response) {
+      var obj = new ServiceObject._fromMap(this, response);
+      if ((obj != null) && obj.canCache) {
+        String objId = obj.id;
+        _cache.putIfAbsent(objId, () => obj);
+      }
+      return obj;
+    }).catchError((e) {
+      return new Future<ServiceObject>.error(e);
+    });
+  }
+
+  void _dispatchEventToIsolate(ServiceEvent event) {
+    var isolate = event.isolate;
+    if (isolate != null) {
+      isolate._onEvent(event);
+    }
+  }
+
+  void _updateService(ServiceEvent event) {
+    switch (event.kind) {
+      case ServiceEvent.kServiceRegistered:
+        services.add(new Service(event.alias, event.method, event.service));
+        break;
+      case ServiceEvent.kServiceUnregistered:
+        services.removeWhere((s) => s.method == event.method);
+        break;
+    }
+  }
+
+  Future<Map> _fetchDirect({int count: kDefaultFieldLimit}) async {
+    if (!loaded) {
+      // The vm service relies on these events to keep the VM and
+      // Isolate types up to date.
+      try {
+        await listenEventStream(kVMStream, _dispatchEventToIsolate);
+        await listenEventStream(kIsolateStream, _dispatchEventToIsolate);
+        await listenEventStream(kDebugStream, _dispatchEventToIsolate);
+        await listenEventStream(kHeapSnapshotStream, _dispatchEventToIsolate);
+        await listenEventStream(kServiceStream, _updateService);
+      } on NetworkRpcException catch (_) {
+        // ignore network errors here.
+      }
+    }
+    return await invokeRpcNoUpgrade('getVM', {});
+  }
+
+  Future setName(String newName) {
+    return invokeRpc('setVMName', {'name': newName});
+  }
+
+  Future<ServiceObject> getFlagList() {
+    return invokeRpc('getFlagList', {});
+  }
+
+  Future enableProfiler() {
+    return invokeRpc("_enableProfiler", {});
+  }
+
+  Future<ServiceObject> _streamListen(String streamId) {
+    Map params = {
+      'streamId': streamId,
+    };
+    // Ignore network errors on stream listen.
+    return invokeRpc('streamListen', params)
+        .catchError((e) => ignoreNetworkErrors(e));
+  }
+
+  Future<ServiceObject> _streamCancel(String streamId) {
+    Map params = {
+      'streamId': streamId,
+    };
+    // Ignore network errors on stream cancel.
+    return invokeRpc('streamCancel', params)
+        .catchError((e) => ignoreNetworkErrors(e));
+  }
+
+  // A map from stream id to event stream state.
+  Map<String, _EventStreamState> _eventStreams = {};
+
+  // Well-known stream ids.
+  static const kVMStream = 'VM';
+  static const kIsolateStream = 'Isolate';
+  static const kTimelineStream = 'Timeline';
+  static const kDebugStream = 'Debug';
+  static const kGCStream = 'GC';
+  static const kStdoutStream = 'Stdout';
+  static const kStderrStream = 'Stderr';
+  static const kHeapSnapshotStream = 'HeapSnapshot';
+  static const kServiceStream = 'Service';
+
+  /// Returns a single-subscription Stream object for a VM event stream.
+  Future<Stream<ServiceEvent>> getEventStream(String streamId) async {
+    var eventStream = _eventStreams.putIfAbsent(
+        streamId,
+        () => new _EventStreamState(
+            this, streamId, () => _eventStreams.remove(streamId)));
+    Stream<ServiceEvent> stream = await eventStream.addStream();
+    return stream;
+  }
+
+  /// Helper function for listening to an event stream.
+  Future<StreamSubscription> listenEventStream(
+      String streamId, Function function) async {
+    var stream = await getEventStream(streamId);
+    return stream.listen(function);
+  }
+
+  /// Force the VM to disconnect.
+  void disconnect();
+
+  /// Completes when the VM first connects.
+  Future get onConnect;
+
+  /// Completes when the VM disconnects or there was an error connecting.
+  Future<String> get onDisconnect;
+
+  void _update(Map map, bool mapIsRef) {
+    name = map['name'];
+    vmName = map.containsKey('_vmName') ? map['_vmName'] : name;
+    if (mapIsRef) {
+      return;
+    }
+    // Note that upgrading the collection creates any isolates in the
+    // isolate list which are new.
+    _upgradeCollection(map, vm);
+
+    _loaded = true;
+    version = map['version'];
+    hostCPU = map['hostCPU'];
+    targetCPU = map['targetCPU'];
+    architectureBits = map['architectureBits'];
+    int startTimeMillis = map['startTime'];
+    startTime = new DateTime.fromMillisecondsSinceEpoch(startTimeMillis);
+    refreshTime = new DateTime.now();
+    if (map['_nativeZoneMemoryUsage'] != null) {
+      nativeZoneMemoryUsage = map['_nativeZoneMemoryUsage'];
+    }
+    pid = map['pid'];
+    heapAllocatedMemoryUsage = map['_heapAllocatedMemoryUsage'];
+    heapAllocationCount = map['_heapAllocationCount'];
+    embedder = map['_embedder'];
+    currentMemory = map['_currentMemory'];
+    maxRSS = map['_maxRSS'];
+    currentRSS = map['_currentRSS'];
+    profileVM = map['_profilerMode'] == 'VM';
+    assertsEnabled = map['_assertsEnabled'];
+    typeChecksEnabled = map['_typeChecksEnabled'];
+    _removeDeadIsolates([
+      ...map['isolates'],
+      ...map['systemIsolates'],
+    ]);
+    _removeDeadIsolateGroups([
+      ...map['isolateGroups'],
+      ...map['systemIsolateGroups'],
+    ]);
+  }
+
+  // Reload all isolates.
+  Future reloadIsolates() {
+    var reloads = <Future>[];
+    for (var isolate in isolates) {
+      var reload = isolate.reload().catchError((e) {
+        Logger.root.info('Bulk reloading of isolates failed: $e');
+      });
+      reloads.add(reload);
+    }
+    return Future.wait(reloads);
+  }
+}
+
+/// Snapshot in time of tag counters.
+class TagProfileSnapshot {
+  final double seconds;
+  final List<int> counters;
+  int get sum => _sum;
+  int _sum = 0;
+  TagProfileSnapshot(this.seconds, int countersLength)
+      : counters = new List<int>.filled(countersLength, 0);
+
+  /// Set [counters] and update [sum].
+  void set(List<int> counters) {
+    this.counters.setAll(0, counters);
+    for (var i = 0; i < this.counters.length; i++) {
+      _sum += this.counters[i];
+    }
+  }
+
+  /// Set [counters] with the delta from [counters] to [old_counters]
+  /// and update [sum].
+  void delta(List<int> counters, List<int> old_counters) {
+    for (var i = 0; i < this.counters.length; i++) {
+      this.counters[i] = counters[i] - old_counters[i];
+      _sum += this.counters[i];
+    }
+  }
+
+  /// Update [counters] with new maximum values seen in [counters].
+  void max(List<int> counters) {
+    for (var i = 0; i < counters.length; i++) {
+      var c = counters[i];
+      this.counters[i] = this.counters[i] > c ? this.counters[i] : c;
+    }
+  }
+
+  /// Zero [counters].
+  void zero() {
+    for (var i = 0; i < counters.length; i++) {
+      counters[i] = 0;
+    }
+  }
+}
+
+class TagProfile {
+  final List<String> names = <String>[];
+  final List<TagProfileSnapshot> snapshots = <TagProfileSnapshot>[];
+  double get updatedAtSeconds => _seconds;
+  double _seconds;
+  TagProfileSnapshot _maxSnapshot;
+  int _historySize;
+  int _countersLength = 0;
+
+  TagProfile(this._historySize);
+
+  void _processTagProfile(double seconds, Map tagProfile) {
+    _seconds = seconds;
+    var counters = tagProfile['counters'];
+    if (names.length == 0) {
+      // Initialization.
+      names.addAll(tagProfile['names']);
+      _countersLength = tagProfile['counters'].length;
+      for (var i = 0; i < _historySize; i++) {
+        var snapshot = new TagProfileSnapshot(0.0, _countersLength);
+        snapshot.zero();
+        snapshots.add(snapshot);
+      }
+      // The counters monotonically grow, keep track of the maximum value.
+      _maxSnapshot = new TagProfileSnapshot(0.0, _countersLength);
+      _maxSnapshot.set(counters);
+      return;
+    }
+    var snapshot = new TagProfileSnapshot(seconds, _countersLength);
+    // We snapshot the delta from the current counters to the maximum counter
+    // values.
+    snapshot.delta(counters, _maxSnapshot.counters);
+    _maxSnapshot.max(counters);
+    snapshots.add(snapshot);
+    // Only keep _historySize snapshots.
+    if (snapshots.length > _historySize) {
+      snapshots.removeAt(0);
+    }
+  }
+}
+
+class InboundReferences implements M.InboundReferences {
+  final Iterable<InboundReference> elements;
+
+  InboundReferences(ServiceMap map)
+      : this.elements = map['references']
+            .map<InboundReference>((rmap) => new InboundReference(rmap))
+            .toList();
+}
+
+class InboundReference implements M.InboundReference {
+  final ServiceObject /*HeapObject*/ source;
+  final HeapObject parentField;
+  final int parentListIndex;
+  final int parentWordOffset;
+
+  InboundReference(Map map)
+      : source = map['source'],
+        parentField = map['parentField'],
+        parentListIndex = map['parentListIndex'],
+        parentWordOffset = map['_parentWordOffset'];
+}
+
+class RetainingPath implements M.RetainingPath {
+  final Iterable<RetainingPathItem> elements;
+  final String gcRootType;
+
+  RetainingPath(ServiceMap map)
+      : this.elements = map['elements']
+            .map<RetainingPathItem>((rmap) => new RetainingPathItem(rmap))
+            .toList(),
+        this.gcRootType = map['gcRootType'];
+}
+
+class RetainingPathItem implements M.RetainingPathItem {
+  final ServiceObject /*HeapObject*/ source;
+  final String parentField;
+  final int parentListIndex;
+  final int parentWordOffset;
+
+  RetainingPathItem(Map map)
+      : source = map['value'],
+        parentField = map['parentField'],
+        parentListIndex = map['parentListIndex'],
+        parentWordOffset = map['_parentWordOffset'];
+}
+
+class Ports implements M.Ports {
+  final Iterable<Port> elements;
+
+  Ports(ServiceMap map)
+      : this.elements =
+            map['ports'].map<Port>((rmap) => new Port(rmap)).toList();
+}
+
+class Port implements M.Port {
+  final String name;
+  final HeapObject handler;
+
+  Port(ServiceMap map)
+      : name = map['name'],
+        handler = map['handler'];
+}
+
+class PersistentHandles implements M.PersistentHandles {
+  final Iterable<PersistentHandle> elements;
+  final Iterable<WeakPersistentHandle> weakElements;
+
+  PersistentHandles(ServiceMap map)
+      : this.elements = map['persistentHandles']
+            .map<PersistentHandle>((rmap) => new PersistentHandle(rmap))
+            .toList(),
+        this.weakElements = map['weakPersistentHandles']
+            .map<WeakPersistentHandle>((rmap) => new WeakPersistentHandle(rmap))
+            .toList();
+}
+
+class PersistentHandle implements M.PersistentHandle {
+  final HeapObject object;
+
+  PersistentHandle(ServiceMap map) : object = map['object'];
+}
+
+class WeakPersistentHandle implements M.WeakPersistentHandle {
+  final int externalSize;
+  final String peer;
+  final String callbackSymbolName;
+  final String callbackAddress;
+  final HeapObject object;
+
+  WeakPersistentHandle(ServiceMap map)
+      : externalSize = int.parse(map['externalSize']),
+        peer = map['peer'],
+        callbackSymbolName = map['callbackSymbolName'],
+        callbackAddress = map['callbackAddress'],
+        object = map['object'];
+}
+
+class HeapSpace implements M.HeapSpace {
+  int used = 0;
+  int capacity = 0;
+  int external = 0;
+  int collections = 0;
+  double totalCollectionTimeInSeconds = 0.0;
+  double averageCollectionPeriodInMillis = 0.0;
+
+  Duration get avgCollectionTime {
+    final mcs = totalCollectionTimeInSeconds *
+        Duration.microsecondsPerSecond /
+        math.max(collections, 1);
+    return new Duration(microseconds: mcs.ceil());
+  }
+
+  Duration get totalCollectionTime {
+    final mcs = totalCollectionTimeInSeconds * Duration.microsecondsPerSecond;
+    return new Duration(microseconds: mcs.ceil());
+  }
+
+  Duration get avgCollectionPeriod {
+    final mcs =
+        averageCollectionPeriodInMillis * Duration.microsecondsPerMillisecond;
+    return new Duration(microseconds: mcs.ceil());
+  }
+
+  void update(Map heapMap) {
+    used = heapMap['used'];
+    capacity = heapMap['capacity'];
+    external = heapMap['external'];
+    collections = heapMap['collections'];
+    totalCollectionTimeInSeconds = heapMap['time'];
+    averageCollectionPeriodInMillis = heapMap['avgCollectionPeriodMillis'];
+  }
+
+  void add(HeapSpace other) {
+    used += other.used;
+    capacity += other.capacity;
+    external += other.external;
+    collections += other.collections;
+    totalCollectionTimeInSeconds += other.totalCollectionTimeInSeconds;
+    if (collections == 0) {
+      averageCollectionPeriodInMillis = 0.0;
+    } else {
+      averageCollectionPeriodInMillis =
+          (totalCollectionTimeInSeconds / collections) * 1000.0;
+    }
+  }
+}
+
+class IsolateGroup extends ServiceObjectOwner implements M.IsolateGroup {
+  IsolateGroup._empty(ServiceObjectOwner owner)
+      : assert(owner is VM),
+        super._empty(owner);
+
+  @override
+  void _update(Map map, bool mapIsRef) {
+    _upgradeCollection(map, vm);
+    name = map['name'];
+    vmName = map.containsKey('_vmName') ? map['_vmName'] : name;
+    number = int.tryParse(map['number']);
+    isSystemIsolateGroup = map['isSystemIsolateGroup'];
+    if (mapIsRef) {
+      return;
+    }
+    _loaded = true;
+    isolates.clear();
+    for (var isolate in map['isolates']) {
+      isolates.add(isolate);
+    }
+    isolates.sort(ServiceObject.LexicalSortName);
+    vm._buildIsolateGroupList();
+  }
+
+  @override
+  ServiceObject getFromMap(Map map) {
+    if (map == null) {
+      return null;
+    }
+    final mapType = _stripRef(map['type']);
+    if (mapType == 'IsolateGroup') {
+      // There are sometimes isolate group refs in ServiceEvents.
+      return vm.getFromMap(map);
+    }
+    String mapId = map['id'];
+    var obj = (mapId != null) ? _cache[mapId] : null;
+    if (obj != null) {
+      obj.updateFromServiceMap(map);
+      return obj;
+    }
+    // Build the object from the map directly.
+    obj = new ServiceObject._fromMap(this, map);
+    if ((obj != null) && obj.canCache) {
+      _cache[mapId] = obj;
+    }
+    return obj;
+  }
+
+  Future<Map> invokeRpcNoUpgrade(String method, Map params) {
+    params['isolateGroupId'] = id;
+    return vm.invokeRpcNoUpgrade(method, params);
+  }
+
+  Future<ServiceObject> invokeRpc(String method, Map params) {
+    return invokeRpcNoUpgrade(method, params)
+        .then((Map response) => getFromMap(response));
+  }
+
+  @override
+  Future<Map> _fetchDirect({int count: kDefaultFieldLimit}) {
+    Map params = {
+      'isolateGroupId': id,
+    };
+    return vm.invokeRpcNoUpgrade('getIsolateGroup', params);
+  }
+
+  @override
+  final List<Isolate> isolates = <Isolate>[];
+
+  @override
+  int number;
+
+  bool isSystemIsolateGroup;
+
+  final Map<String, ServiceObject> _cache = Map<String, ServiceObject>();
+}
+
+/// State for a running isolate.
+class Isolate extends ServiceObjectOwner implements M.Isolate {
+  static const kLoggingStream = 'Logging';
+  static const kExtensionStream = 'Extension';
+
+  VM get vm => owner;
+  Isolate get isolate => this;
+  int number;
+  int originNumber;
+  DateTime startTime;
+  Duration get upTime {
+    if (startTime == null) {
+      return null;
+    }
+    return (new DateTime.now().difference(startTime));
+  }
+
+  Map counters = {};
+
+  void _updateRunState() {
+    topFrame = M.topFrame(pauseEvent);
+    paused = (pauseEvent != null && !(pauseEvent is M.ResumeEvent));
+    running = (!paused && topFrame != null);
+    idle = (!paused && topFrame == null);
+  }
+
+  M.DebugEvent pauseEvent = null;
+  bool paused = false;
+  bool running = false;
+  bool idle = false;
+  bool loading = true;
+  bool runnable = false;
+  bool ioEnabled = false;
+  M.IsolateStatus get status {
+    if (paused) {
+      return M.IsolateStatus.paused;
+    }
+    if (running) {
+      return M.IsolateStatus.running;
+    }
+    if (idle) {
+      return M.IsolateStatus.idle;
+    }
+    return M.IsolateStatus.loading;
+  }
+
+  final List<String> extensionRPCs = <String>[];
+
+  Map<String, ServiceObject> _cache = new Map<String, ServiceObject>();
+  final TagProfile tagProfile = new TagProfile(20);
+
+  Isolate._empty(ServiceObjectOwner owner) : super._empty(owner) {
+    assert(owner is VM);
+  }
+
+  void resetCachedProfileData() {
+    _cache.values.forEach((value) {
+      if (value is Code) {
+        Code code = value;
+        code.profile = null;
+      } else if (value is ServiceFunction) {
+        ServiceFunction function = value;
+        function.profile = null;
+      }
+    });
+  }
+
+  static const kCallSitesReport = '_CallSites';
+  static const kPossibleBreakpointsReport = 'PossibleBreakpoints';
+  static const kProfileReport = '_Profile';
+
+  Future<ServiceObject> getSourceReport(List<String> report_kinds,
+      [Script script, int startPos, int endPos]) {
+    var params = <String, dynamic>{'reports': report_kinds};
+    if (script != null) {
+      params['scriptId'] = script.id;
+    }
+    if (startPos != null) {
+      params['tokenPos'] = startPos;
+    }
+    if (endPos != null) {
+      params['endTokenPos'] = endPos;
+    }
+    return invokeRpc('getSourceReport', params);
+  }
+
+  Future<ServiceMap> reloadSources(
+      {String rootLibUri, String packagesUri, bool pause}) {
+    Map<String, dynamic> params = <String, dynamic>{};
+    if (rootLibUri != null) {
+      params['rootLibUri'] = rootLibUri;
+    }
+    if (packagesUri != null) {
+      params['packagesUri'] = packagesUri;
+    }
+    if (pause != null) {
+      params['pause'] = pause;
+    }
+    return invokeRpc('reloadSources', params).then((result) {
+      _cache.clear();
+      return result as ServiceMap;
+    });
+  }
+
+  void _handleIsolateReloadEvent(ServiceEvent event) {
+    if (event.reloadError == null) {
+      _cache.clear();
+    }
+  }
+
+  Future collectAllGarbage() {
+    return invokeRpc('_collectAllGarbage', {});
+  }
+
+  /// Fetches and builds the class hierarchy for this isolate. Returns the
+  /// Object class object.
+  Future<Class> getClassHierarchy() async {
+    var classRefs = await invokeRpc('getClassList', {});
+    var classes = await _loadClasses(classRefs);
+    return _buildClassHierarchy(classes);
+  }
+
+  Future<ServiceObject> getPorts() {
+    return invokeRpc('_getPorts', {});
+  }
+
+  Future<ServiceObject> getPersistentHandles() {
+    return invokeRpc('_getPersistentHandles', {});
+  }
+
+  /// Given the class list, loads each class.
+  Future<List<Class>> _loadClasses(ServiceMap classList) {
+    assert(classList.type == 'ClassList');
+    var futureClasses = <Future<Class>>[];
+    for (var cls in classList['classes']) {
+      // Skip over non-class classes.
+      if (cls is Class) {
+        futureClasses.add(cls.load().then<Class>((_) => cls));
+      }
+    }
+    return Future.wait(futureClasses);
+  }
+
+  /// Builds the class hierarchy and returns the Object class.
+  Future<Class> _buildClassHierarchy(List<Class> classes) {
+    rootClasses.clear();
+    objectClass = null;
+    for (var cls in classes) {
+      if (cls.superclass == null) {
+        rootClasses.add(cls);
+      }
+      if ((cls.vmName == 'Object') &&
+          (cls.isPatch == false) &&
+          (cls.library.uri == 'dart:core')) {
+        objectClass = cls;
+      }
+    }
+    assert(objectClass != null);
+    return new Future.value(objectClass);
+  }
+
+  ServiceObject getFromMap(Map map) {
+    if (map == null) {
+      return null;
+    }
+    var mapType = _stripRef(map['type']);
+    if (mapType == 'Isolate') {
+      // There are sometimes isolate refs in ServiceEvents.
+      return vm.getFromMap(map);
+    }
+    String mapId = map['id'];
+    var obj = (mapId != null) ? _cache[mapId] : null;
+    if (obj != null) {
+      obj.updateFromServiceMap(map);
+      return obj;
+    }
+    // Build the object from the map directly.
+    obj = new ServiceObject._fromMap(this, map);
+    if ((obj != null) && obj.canCache) {
+      _cache[mapId] = obj;
+    }
+    return obj;
+  }
+
+  Future<Map> invokeRpcNoUpgrade(String method, Map params) {
+    params['isolateId'] = id;
+    return vm.invokeRpcNoUpgrade(method, params);
+  }
+
+  Future<ServiceObject> invokeRpc(String method, Map params) {
+    return invokeRpcNoUpgrade(method, params).then((Map response) {
+      return getFromMap(response);
+    });
+  }
+
+  Future<ServiceObject> getObject(String objectId,
+      {bool reload: true, int count: kDefaultFieldLimit}) {
+    assert(objectId != null && objectId != '');
+    var obj = _cache[objectId];
+    if (obj != null) {
+      if (reload) {
+        return obj.reload(count: count);
+      }
+      // Returned cached object.
+      return new Future.value(obj);
+    }
+    Map params = {
+      'objectId': objectId,
+      'count': count,
+    };
+    return isolate.invokeRpc('getObject', params);
+  }
+
+  Future<List<Script>> getScripts() async {
+    final response = await invokeRpc('getScripts', {}) as ServiceMap;
+    assert(response.type == 'ScriptList');
+    return response['scripts'].cast<Script>();
+  }
+
+  Future<Map> _fetchDirect({int count: kDefaultFieldLimit}) async {
+    return invokeRpcNoUpgrade('getIsolate', {});
+  }
+
+  Class objectClass;
+  final rootClasses = <Class>[];
+
+  Library rootLibrary;
+  List<Library> libraries = <Library>[];
+  Frame topFrame;
+
+  String name;
+  String vmName;
+  ServiceFunction entry;
+
+  final HeapSpace newSpace = new HeapSpace();
+  final HeapSpace oldSpace = new HeapSpace();
+
+  String fileAndLine;
+
+  DartError error;
+  SnapshotReader _snapshotFetch;
+
+  List<Thread> get threads => _threads;
+  final List<Thread> _threads = <Thread>[];
+
+  int get zoneHighWatermark => _zoneHighWatermark;
+  int _zoneHighWatermark = 0;
+
+  int get numZoneHandles => _numZoneHandles;
+  int _numZoneHandles;
+
+  int get numScopedHandles => _numScopedHandles;
+  int _numScopedHandles;
+
+  bool isSystemIsolate;
+
+  void _loadHeapSnapshot(ServiceEvent event) {
+    if (_snapshotFetch == null) {
+      // No outstanding snapshot request. Presumably another client asked for a
+      // snapshot.
+      Logger.root.info("Dropping unsolicited heap snapshot chunk");
+      return;
+    }
+
+    // Occasionally these actually arrive out of order.
+    _snapshotFetch.add(event.data);
+    if (event.lastChunk) {
+      _snapshotFetch.close();
+      _snapshotFetch = null;
+    }
+  }
+
+  SnapshotReader fetchHeapSnapshot() {
+    if (_snapshotFetch == null) {
+      _snapshotFetch = new SnapshotReader();
+      // isolate.vm.streamListen('HeapSnapshot');
+      isolate.invokeRpcNoUpgrade('requestHeapSnapshot', {});
+    }
+    return _snapshotFetch;
+  }
+
+  void updateHeapsFromMap(Map map) {
+    newSpace.update(map['new']);
+    oldSpace.update(map['old']);
+  }
+
+  void _update(Map map, bool mapIsRef) {
+    name = map['name'];
+    vmName = map.containsKey('_vmName') ? map['_vmName'] : name;
+    number = int.tryParse(map['number']);
+    isSystemIsolate = map['isSystemIsolate'];
+    if (mapIsRef) {
+      return;
+    }
+    _loaded = true;
+    loading = false;
+    runnable = map['runnable'] == true;
+    _upgradeCollection(map, isolate);
+    originNumber = int.tryParse(map['_originNumber']);
+    rootLibrary = map['rootLib'];
+    if (map['entry'] != null) {
+      entry = map['entry'];
+    }
+    var savedStartTime = startTime;
+    int startTimeInMillis = map['startTime'];
+    startTime = new DateTime.fromMillisecondsSinceEpoch(startTimeInMillis);
+    var countersMap = map['_tagCounters'];
+    if (countersMap != null) {
+      var names = countersMap['names'];
+      var counts = countersMap['counters'];
+      assert(names.length == counts.length);
+      var sum = 0;
+      for (var i = 0; i < counts.length; i++) {
+        sum += counts[i];
+      }
+      var _counters = {};
+      if (sum == 0) {
+        for (var i = 0; i < names.length; i++) {
+          _counters[names[i]] = '0.0%';
+        }
+      } else {
+        for (var i = 0; i < names.length; i++) {
+          _counters[names[i]] =
+              (counts[i] / sum * 100.0).toStringAsFixed(2) + '%';
+        }
+      }
+      counters = _counters;
+    }
+
+    updateHeapsFromMap(map['_heaps']);
+    _updateBreakpoints(map['breakpoints']);
+    if (map['_debuggerSettings'] != null) {
+      exceptionsPauseInfo = map['_debuggerSettings']['_exceptions'];
+    } else {
+      exceptionsPauseInfo = "none";
+    }
+
+    var newPauseEvent = map['pauseEvent'];
+    assert((pauseEvent == null) ||
+        (newPauseEvent == null) ||
+        !newPauseEvent.timestamp.isBefore(pauseEvent.timestamp));
+    pauseEvent = createEventFromServiceEvent(newPauseEvent);
+    _updateRunState();
+    error = map['error'];
+
+    libraries.clear();
+    for (Library l in map['libraries']) libraries.add(l);
+    libraries.sort(ServiceObject.LexicalSortName);
+    if (savedStartTime == null) {
+      vm._buildIsolateList();
+    }
+
+    extensionRPCs.clear();
+    if (map['extensionRPCs'] != null) {
+      for (String e in map['extensionRPCs']) extensionRPCs.add(e);
+    }
+
+    threads.clear();
+    if (map['_threads'] != null) {
+      for (Thread t in map['_threads']) threads.add(t);
+    }
+
+    int currentZoneHighWatermark = 0;
+    for (var i = 0; i < threads.length; i++) {
+      currentZoneHighWatermark += threads[i].zoneHighWatermark;
+    }
+
+    if (currentZoneHighWatermark > _zoneHighWatermark) {
+      _zoneHighWatermark = currentZoneHighWatermark;
+    }
+
+    _numZoneHandles = map['_numZoneHandles'];
+    _numScopedHandles = map['_numScopedHandles'];
+  }
+
+  Future<TagProfile> updateTagProfile() {
+    return isolate.invokeRpcNoUpgrade('_getTagProfile', {}).then((Map map) {
+      var seconds = new DateTime.now().millisecondsSinceEpoch / 1000.0;
+      tagProfile._processTagProfile(seconds, map);
+      return tagProfile;
+    });
+  }
+
+  Map<int, Breakpoint> breakpoints = <int, Breakpoint>{};
+  String exceptionsPauseInfo;
+
+  void _updateBreakpoints(List newBpts) {
+    // Build a set of new breakpoints.
+    var newBptSet = new Set();
+    newBpts.forEach((bpt) => newBptSet.add(bpt.number));
+
+    // Remove any old breakpoints which no longer exist.
+    List toRemove = [];
+    breakpoints.forEach((key, _) {
+      if (!newBptSet.contains(key)) {
+        toRemove.add(key);
+      }
+    });
+    toRemove.forEach((key) => breakpoints.remove(key));
+
+    // Add all new breakpoints.
+    newBpts.forEach((bpt) => (breakpoints[bpt.number] = bpt));
+  }
+
+  void _addBreakpoint(Breakpoint bpt) {
+    breakpoints[bpt.number] = bpt;
+  }
+
+  void _removeBreakpoint(Breakpoint bpt) {
+    breakpoints.remove(bpt.number);
+    bpt.remove();
+  }
+
+  void _onEvent(ServiceEvent event) {
+    switch (event.kind) {
+      case ServiceEvent.kIsolateStart:
+      case ServiceEvent.kIsolateRunnable:
+      case ServiceEvent.kIsolateExit:
+      case ServiceEvent.kInspect:
+        // Handled elsewhere.
+        break;
+      case ServiceEvent.kIsolateReload:
+        _handleIsolateReloadEvent(event);
+        break;
+      case ServiceEvent.kBreakpointAdded:
+        _addBreakpoint(event.breakpoint);
+        break;
+
+      case ServiceEvent.kIsolateUpdate:
+      case ServiceEvent.kBreakpointResolved:
+      case ServiceEvent.kDebuggerSettingsUpdate:
+        // Update occurs as side-effect of caching.
+        break;
+
+      case ServiceEvent.kBreakpointRemoved:
+        _removeBreakpoint(event.breakpoint);
+        break;
+
+      case ServiceEvent.kPauseStart:
+      case ServiceEvent.kPauseExit:
+      case ServiceEvent.kPauseBreakpoint:
+      case ServiceEvent.kPauseInterrupted:
+      case ServiceEvent.kPauseException:
+      case ServiceEvent.kPausePostRequest:
+      case ServiceEvent.kNone:
+      case ServiceEvent.kResume:
+        assert((pauseEvent == null) ||
+            !event.timestamp.isBefore(pauseEvent.timestamp));
+        pauseEvent = createEventFromServiceEvent(event);
+        _updateRunState();
+        break;
+
+      case ServiceEvent.kHeapSnapshot:
+        _loadHeapSnapshot(event);
+        break;
+
+      case ServiceEvent.kGC:
+        // Ignore GC events for now.
+        break;
+
+      default:
+        // Log unexpected events.
+        Logger.root.severe('Unexpected event: $event');
+        break;
+    }
+  }
+
+  Future<Breakpoint> addBreakpoint(Script script, int line, [int col]) {
+    Map params = {
+      'scriptId': script.id,
+      'line': line,
+    };
+    if (col != null) {
+      params['column'] = col;
+    }
+    return invokeRpc('addBreakpoint', params)
+        .then((result) => result as Breakpoint);
+  }
+
+  Future<Breakpoint> addBreakpointByScriptUri(String uri, int line, [int col]) {
+    Map params = {
+      'scriptUri': uri,
+      'line': line.toString(),
+    };
+    if (col != null) {
+      params['column'] = col.toString();
+    }
+    return invokeRpc('addBreakpointWithScriptUri', params)
+        .then((result) => result as Breakpoint);
+  }
+
+  Future<Breakpoint> addBreakpointAtEntry(ServiceFunction function) {
+    return invokeRpc('addBreakpointAtEntry', {'functionId': function.id})
+        .then((result) => result as Breakpoint);
+  }
+
+  Future<Breakpoint> addBreakOnActivation(Instance closure) {
+    return invokeRpc('_addBreakpointAtActivation', {'objectId': closure.id})
+        .then((result) => result as Breakpoint);
+  }
+
+  Future removeBreakpoint(Breakpoint bpt) {
+    return invokeRpc('removeBreakpoint', {'breakpointId': bpt.id});
+  }
+
+  Future pause() {
+    return invokeRpc('pause', {});
+  }
+
+  Future resume() {
+    return invokeRpc('resume', {});
+  }
+
+  Future stepInto() {
+    return invokeRpc('resume', {'step': 'Into'});
+  }
+
+  Future stepOver() {
+    return invokeRpc('resume', {'step': 'Over'});
+  }
+
+  Future stepOverAsyncSuspension() {
+    return invokeRpc('resume', {'step': 'OverAsyncSuspension'});
+  }
+
+  Future stepOut() {
+    return invokeRpc('resume', {'step': 'Out'});
+  }
+
+  Future rewind(int count) {
+    return invokeRpc('resume', {'step': 'Rewind', 'frameIndex': count});
+  }
+
+  Future setName(String newName) {
+    return invokeRpc('setName', {'name': newName});
+  }
+
+  Future setExceptionPauseMode(String mode) {
+    return invokeRpc('setExceptionPauseMode', {'mode': mode});
+  }
+
+  Future<ServiceMap> getStack() {
+    return invokeRpc('getStack', {}).then((response) => response as ServiceMap);
+  }
+
+  Future<ObjectStore> getObjectStore() {
+    return invokeRpcNoUpgrade('_getObjectStore', {}).then((map) {
+      ObjectStore objectStore = new ObjectStore._empty(this);
+      objectStore._update(map, false);
+      return objectStore;
+    });
+  }
+
+  Future<ServiceObject> invoke(ServiceObject target, String selector,
+      [List<ServiceObject> arguments = const <ServiceObject>[]]) {
+    Map params = {
+      'targetId': target.id,
+      'selector': selector,
+      'argumentIds': arguments.map((arg) => arg.id).toList(),
+    };
+    return invokeRpc('invoke', params);
+  }
+
+  Future<ServiceObject> eval(ServiceObject target, String expression,
+      {Map<String, ServiceObject> scope, bool disableBreakpoints: false}) {
+    Map params = {
+      'targetId': target.id,
+      'expression': expression,
+      'disableBreakpoints': disableBreakpoints,
+    };
+    if (scope != null) {
+      Map<String, String> scopeWithIds = new Map();
+      scope.forEach((String name, ServiceObject object) {
+        scopeWithIds[name] = object.id;
+      });
+      params["scope"] = scopeWithIds;
+    }
+    return invokeRpc('evaluate', params);
+  }
+
+  Future<ServiceObject> evalFrame(int frameIndex, String expression,
+      {Map<String, ServiceObject> scope,
+      bool disableBreakpoints: false}) async {
+    Map params = {
+      'frameIndex': frameIndex,
+      'expression': expression,
+      'disableBreakpoints': disableBreakpoints,
+    };
+    if (scope != null) {
+      Map<String, String> scopeWithIds = new Map();
+      scope.forEach((String name, ServiceObject object) {
+        scopeWithIds[name] = object.id;
+      });
+      params["scope"] = scopeWithIds;
+    }
+
+    try {
+      return await invokeRpc('evaluateInFrame', params);
+    } on ServerRpcException catch (error) {
+      if (error.code == ServerRpcException.kExpressionCompilationError) {
+        Map map = {
+          'type': 'Error',
+          'message': error.data.toString(),
+          'kind': 'LanguageError',
+          'exception': null,
+          'stacktrace': null,
+        };
+        return new ServiceObject._fromMap(null, map);
+      } else
+        rethrow;
+    }
+  }
+
+  Future<ServiceObject> getReachableSize(ServiceObject target) {
+    Map params = {
+      'targetId': target.id,
+    };
+    return invokeRpc('_getReachableSize', params);
+  }
+
+  Future<ServiceObject> getRetainedSize(ServiceObject target) {
+    Map params = {
+      'targetId': target.id,
+    };
+    return invokeRpc('_getRetainedSize', params);
+  }
+
+  Future<ServiceObject> getRetainingPath(ServiceObject target, var limit) {
+    Map params = {
+      'targetId': target.id,
+      'limit': limit.toString(),
+    };
+    return invokeRpc('getRetainingPath', params);
+  }
+
+  Future<ServiceObject> getInboundReferences(ServiceObject target, var limit) {
+    Map params = {
+      'targetId': target.id,
+      'limit': limit.toString(),
+    };
+    return invokeRpc('getInboundReferences', params);
+  }
+
+  Future<ServiceObject> getTypeArgumentsList(bool onlyWithInstantiations) {
+    Map params = {
+      'onlyWithInstantiations': onlyWithInstantiations,
+    };
+    return invokeRpc('_getTypeArgumentsList', params);
+  }
+
+  Future<ServiceObject> getInstances(Class cls, var limit) {
+    Map params = {
+      'objectId': cls.id,
+      'limit': limit.toString(),
+    };
+    return invokeRpc('getInstances', params);
+  }
+
+  final Map<String, ServiceMetric> dartMetrics = <String, ServiceMetric>{};
+
+  final Map<String, ServiceMetric> nativeMetrics = <String, ServiceMetric>{};
+
+  Future<Map<String, ServiceMetric>> _refreshMetrics(
+      String metricType, Map<String, ServiceMetric> metricsMap) {
+    return invokeRpc('_getIsolateMetricList', {'type': metricType})
+        .then((dynamic result) {
+      // Clear metrics map.
+      metricsMap.clear();
+      // Repopulate metrics map.
+      var metrics = result['metrics'];
+      for (var metric in metrics) {
+        metricsMap[metric.id] = metric;
+      }
+      return metricsMap;
+    });
+  }
+
+  Future<Map<String, ServiceMetric>> refreshDartMetrics() {
+    return _refreshMetrics('Dart', dartMetrics);
+  }
+
+  Future<Map<String, ServiceMetric>> refreshNativeMetrics() {
+    return _refreshMetrics('Native', nativeMetrics);
+  }
+
+  Future refreshMetrics() {
+    return Future.wait([refreshDartMetrics(), refreshNativeMetrics()]);
+  }
+
+  String toString() => "Isolate($name)";
+}
+
+class NamedField implements M.NamedField {
+  final String name;
+  final M.ObjectRef value;
+  NamedField(this.name, this.value);
+}
+
+class ObjectStore extends ServiceObject implements M.ObjectStore {
+  List<NamedField> fields = <NamedField>[];
+
+  ObjectStore._empty(ServiceObjectOwner owner) : super._empty(owner);
+
+  void _update(Map map, bool mapIsRef) {
+    // Extract full properties.
+    _upgradeCollection(map, isolate);
+
+    if (mapIsRef) {
+      return;
+    }
+
+    fields.clear();
+    map['fields'].forEach((key, value) {
+      fields.add(new NamedField(key, value));
+    });
+    _loaded = true;
+  }
+}
+
+/// A [ServiceObject] which implements [Map].
+class ServiceMap extends ServiceObject
+    implements Map<String, dynamic>, M.UnknownObjectRef {
+  final Map<String, dynamic> _map = {};
+  static String objectIdRingPrefix = 'objects/';
+
+  bool get immutable => false;
+
+  ServiceMap._empty(ServiceObjectOwner owner) : super._empty(owner);
+
+  void _update(Map map, bool mapIsRef) {
+    _loaded = !mapIsRef;
+
+    _upgradeCollection(map, owner);
+    // TODO(turnidge): Currently _map.clear() prevents us from
+    // upgrading an already upgraded submap.  Is clearing really the
+    // right thing to do here?
+    _map.clear();
+    _map.addAll(map);
+
+    name = _map['name'];
+    vmName = (_map.containsKey('_vmName') ? _map['_vmName'] : name);
+  }
+
+  // TODO(turnidge): These are temporary until we have a proper root
+  // object for all dart heap objects.
+  int get size => _map['size'];
+  int get clazz => _map['class'];
+
+  // Forward Map interface calls.
+  void addAll(Map other) => _map.addAll(other);
+  void clear() => _map.clear();
+  bool containsValue(v) => _map.containsValue(v);
+  bool containsKey(k) => _map.containsKey(k);
+  void forEach(Function f) => _map.forEach(f);
+  putIfAbsent(key, Function ifAbsent) => _map.putIfAbsent(key, ifAbsent);
+  void remove(key) => _map.remove(key);
+  operator [](k) => _map[k];
+  operator []=(k, v) => _map[k] = v;
+  bool get isEmpty => _map.isEmpty;
+  bool get isNotEmpty => _map.isNotEmpty;
+  Iterable<String> get keys => _map.keys;
+  Iterable get values => _map.values;
+  int get length => _map.length;
+
+  // Suppress compile-time error about missing Map methods.
+  noSuchMethod(_) => throw "Unimplemented ServiceMap method";
+
+  String toString() => "ServiceMap($_map)";
+}
+
+M.ErrorKind stringToErrorKind(String value) {
+  switch (value) {
+    case 'UnhandledException':
+      return M.ErrorKind.unhandledException;
+    case 'LanguageError':
+      return M.ErrorKind.unhandledException;
+    case 'InternalError':
+      return M.ErrorKind.internalError;
+    case 'TerminationError':
+      return M.ErrorKind.terminationError;
+  }
+  var message = 'Unrecognized error kind: $value';
+  Logger.root.severe(message);
+  throw new ArgumentError(message);
+}
+
+/// A [DartError] is peered to a Dart Error object.
+class DartError extends HeapObject implements M.Error {
+  DartError._empty(ServiceObject owner) : super._empty(owner);
+
+  M.ErrorKind kind;
+  String message;
+  Instance exception;
+  Instance stacktrace;
+
+  void _update(Map map, bool mapIsRef) {
+    _upgradeCollection(map, owner);
+    super._update(map, mapIsRef);
+
+    message = map['message'];
+    kind = stringToErrorKind(map['kind']);
+    exception = map['exception'];
+    stacktrace = map['stacktrace'];
+    name = 'DartError($message)';
+    vmName = name;
+  }
+
+  String toString() => 'DartError($message)';
+}
+
+Level _findLogLevel(int value) {
+  for (var level in Level.LEVELS) {
+    if (level.value == value) {
+      return level;
+    }
+  }
+  return new Level('$value', value);
+}
+
+/// A [ServiceEvent] is an asynchronous event notification from the vm.
+class ServiceEvent extends ServiceObject {
+  /// The possible 'kind' values.
+  static const kVMUpdate = 'VMUpdate';
+  static const kVMFlagUpdate = 'VMFlagUpdate';
+  static const kIsolateStart = 'IsolateStart';
+  static const kIsolateRunnable = 'IsolateRunnable';
+  static const kIsolateExit = 'IsolateExit';
+  static const kIsolateUpdate = 'IsolateUpdate';
+  static const kIsolateReload = 'IsolateReload';
+  static const kIsolateSpawn = 'IsolateSpawn';
+  static const kServiceExtensionAdded = 'ServiceExtensionAdded';
+  static const kPauseStart = 'PauseStart';
+  static const kPauseExit = 'PauseExit';
+  static const kPauseBreakpoint = 'PauseBreakpoint';
+  static const kPauseInterrupted = 'PauseInterrupted';
+  static const kPauseException = 'PauseException';
+  static const kPausePostRequest = 'PausePostRequest';
+  static const kNone = 'None';
+  static const kResume = 'Resume';
+  static const kBreakpointAdded = 'BreakpointAdded';
+  static const kBreakpointResolved = 'BreakpointResolved';
+  static const kBreakpointRemoved = 'BreakpointRemoved';
+  static const kHeapSnapshot = 'HeapSnapshot';
+  static const kGC = 'GC';
+  static const kInspect = 'Inspect';
+  static const kDebuggerSettingsUpdate = '_DebuggerSettingsUpdate';
+  static const kConnectionClosed = 'ConnectionClosed';
+  static const kLogging = 'Logging';
+  static const kExtension = 'Extension';
+  static const kTimelineEvents = 'TimelineEvents';
+  static const kTimelineStreamSubscriptionsUpdate =
+      'TimelineStreamSubscriptionsUpdate';
+  static const kServiceRegistered = 'ServiceRegistered';
+  static const kServiceUnregistered = 'ServiceUnregistered';
+  static const kDartDevelopmentServiceConnected =
+      'DartDevelopmentServiceConnected';
+
+  ServiceEvent._empty(ServiceObjectOwner owner) : super._empty(owner);
+
+  ServiceEvent.connectionClosed(this.reason) : super._empty(null) {
+    kind = kConnectionClosed;
+  }
+
+  String kind;
+  DateTime timestamp;
+  String flag;
+  String newValue;
+  List<M.Breakpoint> pauseBreakpoints;
+  Breakpoint breakpoint;
+  Frame topFrame;
+  DartError error;
+  String extensionRPC;
+  Instance exception;
+  DartError reloadError;
+  bool atAsyncSuspension;
+  Instance inspectee;
+  Uint8List data;
+  int count;
+  String reason;
+  String exceptions;
+  String bytesAsString;
+  Map logRecord;
+  String extensionKind;
+  Map extensionData;
+  List timelineEvents;
+  List<String> updatedStreams;
+  String spawnToken;
+  String spawnError;
+  String editor;
+  ServiceObject object;
+  String method;
+  String service;
+  String alias;
+  String message;
+  Uri uri;
+
+  bool lastChunk;
+
+  bool get isPauseEvent {
+    return (kind == kPauseStart ||
+        kind == kPauseExit ||
+        kind == kPauseBreakpoint ||
+        kind == kPauseInterrupted ||
+        kind == kPauseException ||
+        kind == kPausePostRequest ||
+        kind == kNone);
+  }
+
+  void _update(Map map, bool mapIsRef) {
+    _loaded = true;
+    _upgradeCollection(map, owner);
+
+    assert(map['isolate'] == null || owner == map['isolate']);
+    timestamp = new DateTime.fromMillisecondsSinceEpoch(map['timestamp']);
+    kind = map['kind'];
+    name = 'ServiceEvent $kind';
+    vmName = name;
+    if (map['breakpoint'] != null) {
+      breakpoint = map['breakpoint'];
+    }
+    if (map['pauseBreakpoints'] != null) {
+      pauseBreakpoints = new List<Breakpoint>.from(map['pauseBreakpoints']);
+      if (pauseBreakpoints.length > 0) {
+        breakpoint = pauseBreakpoints[0];
+      }
+    } else {
+      pauseBreakpoints = const [];
+    }
+    if (map['error'] != null) {
+      error = map['error'];
+    }
+    if (map['extensionRPC'] != null) {
+      extensionRPC = map['extensionRPC'];
+    }
+    topFrame = map['topFrame'];
+    if (map['exception'] != null) {
+      exception = map['exception'];
+    }
+    atAsyncSuspension = map['atAsyncSuspension'] != null;
+    if (map['inspectee'] != null) {
+      inspectee = map['inspectee'];
+    }
+    if (map['_data'] != null) {
+      data = map['_data'];
+    }
+    lastChunk = map['last'] ?? false;
+    if (map['count'] != null) {
+      count = map['count'];
+    }
+    reloadError = map['reloadError'];
+    if (map['_debuggerSettings'] != null &&
+        map['_debuggerSettings']['_exceptions'] != null) {
+      exceptions = map['_debuggerSettings']['_exceptions'];
+    }
+    if (map['bytes'] != null) {
+      var bytes = base64Decode(map['bytes']);
+      bytesAsString = utf8.decode(bytes);
+    }
+    if (map['logRecord'] != null) {
+      logRecord = map['logRecord'];
+      logRecord['time'] =
+          new DateTime.fromMillisecondsSinceEpoch(logRecord['time']);
+      logRecord['level'] = _findLogLevel(logRecord['level']);
+    }
+    if (map['extensionKind'] != null) {
+      extensionKind = map['extensionKind'];
+      extensionData = map['extensionData'];
+    }
+    if (map['timelineEvents'] != null) {
+      timelineEvents = map['timelineEvents'];
+    }
+    if (map['updatedStreams'] != null) {
+      updatedStreams = map['updatedStreams'].cast<String>();
+    }
+    if (map['spawnToken'] != null) {
+      spawnToken = map['spawnToken'];
+    }
+    if (map['spawnError'] != null) {
+      spawnError = map['spawnError'];
+    }
+    if (map['editor'] != null) {
+      editor = map['editor'];
+    }
+    if (map['object'] != null) {
+      object = map['object'];
+    }
+    if (map['service'] != null) {
+      service = map['service'];
+    }
+    if (map['method'] != null) {
+      method = map['method'];
+    }
+    if (map['alias'] != null) {
+      alias = map['alias'];
+    }
+    if (map['flag'] != null) {
+      flag = map['flag'];
+    }
+    if (map['newValue'] != null) {
+      newValue = map['newValue'];
+    }
+    if (map['message'] != null) {
+      message = map['message'];
+    }
+    if (map['uri'] != null) {
+      uri = Uri.parse(map['uri']);
+    }
+  }
+
+  String toString() {
+    var ownerName = owner.id != null ? owner.id.toString() : owner.name;
+    if (data == null) {
+      return "ServiceEvent(owner='${ownerName}', kind='${kind}', "
+          "time=${timestamp})";
+    } else {
+      return "ServiceEvent(owner='${ownerName}', kind='${kind}', "
+          "data.lengthInBytes=${data.lengthInBytes}, time=${timestamp})";
+    }
+  }
+}
+
+class Breakpoint extends ServiceObject implements M.Breakpoint {
+  Breakpoint._empty(ServiceObjectOwner owner) : super._empty(owner);
+
+  final M.ClassRef clazz = null;
+  final int size = null;
+
+  // TODO(turnidge): Add state to track if a breakpoint has been
+  // removed from the program.  Remove from the cache when deleted.
+  bool get immutable => false;
+
+  // A unique integer identifier for this breakpoint.
+  int number;
+
+  // Either SourceLocation or UnresolvedSourceLocation.
+  Location location;
+
+  // The breakpoint is in a file which is not yet loaded.
+  bool latent;
+
+  // The breakpoint has been assigned to a final source location.
+  bool resolved;
+
+  // The breakpoint was synthetically created as part of an
+  // 'OverAsyncContinuation' resume request.
+  bool isSyntheticAsyncContinuation;
+
+  void _update(Map map, bool mapIsRef) {
+    _loaded = true;
+    _upgradeCollection(map, owner);
+
+    var newNumber = map['breakpointNumber'];
+    // number never changes.
+    assert((number == null) || (number == newNumber));
+    number = newNumber;
+    resolved = map['resolved'];
+
+    var oldLocation = location;
+    var newLocation = map['location'];
+    if (oldLocation is UnresolvedSourceLocation &&
+        newLocation is SourceLocation) {
+      // Breakpoint has been resolved.  Remove old breakpoint.
+      var oldScript = oldLocation.script;
+      if (oldScript != null && oldScript.loaded) {
+        oldScript._removeBreakpoint(this);
+      }
+    }
+    location = newLocation;
+    var newScript = location.script;
+    if (newScript != null && newScript.loaded) {
+      newScript._addBreakpoint(this);
+    }
+
+    isSyntheticAsyncContinuation = map['isSyntheticAsyncContinuation'] != null;
+
+    assert(resolved || location is UnresolvedSourceLocation);
+  }
+
+  void remove() {
+    location.script._removeBreakpoint(this);
+  }
+
+  String toString() {
+    if (number != null) {
+      if (isSyntheticAsyncContinuation) {
+        return 'Synthetic Async Continuation Breakpoint ${number}';
+      } else {
+        return 'Breakpoint ${number} at ${location}';
+      }
+    } else {
+      return 'Uninitialized breakpoint';
+    }
+  }
+}
+
+class LibraryDependency implements M.LibraryDependency {
+  final bool isImport;
+  final bool isDeferred;
+  final String prefix;
+  final Library target;
+
+  bool get isExport => !isImport;
+
+  LibraryDependency._(this.isImport, this.isDeferred, this.prefix, this.target);
+
+  static _fromMap(map) => new LibraryDependency._(
+      map["isImport"], map["isDeferred"], map["prefix"], map["target"]);
+}
+
+class Library extends HeapObject implements M.Library {
+  String uri;
+  final List<LibraryDependency> dependencies = <LibraryDependency>[];
+  final List<Script> scripts = <Script>[];
+  final List<Class> classes = <Class>[];
+  final List<Field> variables = <Field>[];
+  final List<ServiceFunction> functions = <ServiceFunction>[];
+  bool _debuggable;
+  bool get debuggable => _debuggable;
+  bool get immutable => false;
+
+  bool isDart(String libraryName) {
+    return uri == 'dart:$libraryName';
+  }
+
+  Library._empty(ServiceObjectOwner owner) : super._empty(owner);
+
+  void _update(Map map, bool mapIsRef) {
+    _upgradeCollection(map, isolate);
+    super._update(map, mapIsRef);
+
+    uri = map['uri'];
+    var shortUri = uri;
+    if (uri.startsWith('file://') || uri.startsWith('http://')) {
+      shortUri = uri.substring(uri.lastIndexOf('/') + 1);
+    }
+    name = map['name'];
+    if (name.isEmpty) {
+      // When there is no name for a library, use the shortUri.
+      name = shortUri;
+    }
+    vmName = (map.containsKey('_vmName') ? map['_vmName'] : name);
+    if (mapIsRef) {
+      return;
+    }
+    _loaded = true;
+    _debuggable = map['debuggable'];
+    dependencies.clear();
+    for (var dependency in map["dependencies"]) {
+      dependencies.add(LibraryDependency._fromMap(dependency));
+    }
+    scripts.clear();
+    scripts.addAll(
+        removeDuplicatesAndSortLexical(new List<Script>.from(map['scripts'])));
+    classes.clear();
+    for (Class c in map['classes']) classes.add(c);
+    classes.sort(ServiceObject.LexicalSortName);
+    variables.clear();
+    for (Field v in map['variables']) variables.add(v);
+    variables.sort(ServiceObject.LexicalSortName);
+    functions.clear();
+    for (ServiceFunction f in map['functions']) functions.add(f);
+    functions.sort(ServiceObject.LexicalSortName);
+  }
+
+  Future<ServiceObject> evaluate(String expression,
+      {Map<String, ServiceObject> scope, bool disableBreakpoints: false}) {
+    return isolate.eval(this, expression,
+        scope: scope, disableBreakpoints: disableBreakpoints);
+  }
+
+  Script get rootScript {
+    for (Script script in scripts) {
+      if (script.uri == uri) return script;
+    }
+    return null;
+  }
+
+  String toString() => "Library($uri)";
+}
+
+class Allocations implements M.Allocations {
+  // Indexes into VM provided array. (see vm/class_table.h).
+
+  int instances = 0;
+  int internalSize = 0;
+  int externalSize = 0;
+  int size = 0;
+
+  void update(List stats) {
+    instances = stats[0];
+    internalSize = stats[1];
+    externalSize = stats[2];
+    size = internalSize + externalSize;
+  }
+
+  void combine(Iterable<Allocations> allocations) {
+    instances = allocations.fold(0, (v, a) => v + a.instances);
+    internalSize = allocations.fold(0, (v, a) => v + a.internalSize);
+    externalSize = allocations.fold(0, (v, a) => v + a.externalSize);
+    size = allocations.fold(0, (v, a) => v + a.size);
+  }
+
+  bool get empty => size == 0;
+  bool get notEmpty => size != 0;
+}
+
+class Class extends HeapObject implements M.Class {
+  Library library;
+
+  bool isAbstract;
+  bool isConst;
+  bool isFinalized;
+  bool isPatch;
+  bool isImplemented;
+
+  SourceLocation location;
+
+  DartError error;
+
+  final Allocations newSpace = new Allocations();
+  final Allocations oldSpace = new Allocations();
+
+  bool get hasAllocations => newSpace.notEmpty || oldSpace.notEmpty;
+  bool get hasNoAllocations => newSpace.empty && oldSpace.empty;
+  bool traceAllocations = false;
+  final List<Field> fields = <Field>[];
+  final List<ServiceFunction> functions = <ServiceFunction>[];
+
+  Class superclass;
+  final List<Instance> interfaces = <Instance>[];
+  final List<Class> subclasses = <Class>[];
+
+  Instance superType;
+  Instance mixin;
+
+  bool get immutable => false;
+
+  Class._empty(ServiceObjectOwner owner) : super._empty(owner);
+
+  void _update(Map map, bool mapIsRef) {
+    _upgradeCollection(map, isolate);
+    super._update(map, mapIsRef);
+
+    name = map['name'];
+    vmName = (map.containsKey('_vmName') ? map['_vmName'] : name);
+    if (vmName == '::') {
+      name = 'top-level-class'; // Better than ''
+    }
+    var idPrefix = "classes/";
+    assert(id.startsWith(idPrefix));
+
+    if (mapIsRef) {
+      return;
+    }
+
+    // We are fully loaded.
+    _loaded = true;
+
+    // Extract full properties.
+    _upgradeCollection(map, isolate);
+
+    // Some builtin classes aren't associated with a library.
+    if (map['library'] is Library) {
+      library = map['library'];
+    } else {
+      library = null;
+    }
+
+    location = map['location'];
+    isAbstract = map['abstract'];
+    isConst = map['const'];
+    isFinalized = map['_finalized'];
+    isPatch = map['_patch'];
+    isImplemented = map['_implemented'];
+
+    subclasses.clear();
+    for (Class c in map['subclasses']) subclasses.add(c);
+    subclasses.sort(ServiceObject.LexicalSortName);
+
+    interfaces.clear();
+    for (Instance i in map['interfaces']) interfaces.add(i);
+    interfaces.sort(ServiceObject.LexicalSortName);
+
+    fields.clear();
+    for (Field f in map['fields']) fields.add(f);
+    fields.sort(ServiceObject.LexicalSortName);
+
+    functions.clear();
+    for (ServiceFunction f in map['functions']) functions.add(f);
+    functions.sort(ServiceObject.LexicalSortName);
+
+    superclass = map['super'];
+    // Work-around Object not tracking its subclasses in the VM.
+    if (superclass != null && superclass.name == "Object") {
+      superclass._addSubclass(this);
+    }
+    superType = map['superType'];
+    mixin = map['mixin'];
+
+    error = map['error'];
+
+    traceAllocations =
+        (map['_traceAllocations'] != null) ? map['_traceAllocations'] : false;
+  }
+
+  void _addSubclass(Class subclass) {
+    if (subclasses.contains(subclass)) {
+      return;
+    }
+    subclasses.add(subclass);
+    subclasses.sort(ServiceObject.LexicalSortName);
+  }
+
+  Future<ServiceObject> evaluate(String expression,
+      {Map<String, ServiceObject> scope, disableBreakpoints: false}) {
+    return isolate.eval(this, expression,
+        scope: scope, disableBreakpoints: disableBreakpoints);
+  }
+
+  Future<ServiceObject> setTraceAllocations(bool enable) {
+    return isolate.invokeRpc('_setTraceClassAllocation', {
+      'enable': enable,
+      'classId': id,
+    });
+  }
+
+  Future<ServiceObject> getAllocationSamples() {
+    var params = {
+      'classId': id,
+    };
+    return isolate.invokeRpc('_getAllocationSamples', params);
+  }
+
+  String toString() => 'Class($vmName)';
+}
+
+M.InstanceKind stringToInstanceKind(String s) {
+  switch (s) {
+    case 'PlainInstance':
+      return M.InstanceKind.plainInstance;
+    case 'Null':
+      return M.InstanceKind.vNull;
+    case 'Bool':
+      return M.InstanceKind.bool;
+    case 'Double':
+      return M.InstanceKind.double;
+    case 'Int':
+      return M.InstanceKind.int;
+    case 'String':
+      return M.InstanceKind.string;
+    case 'List':
+      return M.InstanceKind.list;
+    case 'Map':
+      return M.InstanceKind.map;
+    case 'Float32x4':
+      return M.InstanceKind.float32x4;
+    case 'Float64x2':
+      return M.InstanceKind.float64x2;
+    case 'Int32x4':
+      return M.InstanceKind.int32x4;
+    case 'Uint8ClampedList':
+      return M.InstanceKind.uint8ClampedList;
+    case 'Uint8List':
+      return M.InstanceKind.uint8List;
+    case 'Uint16List':
+      return M.InstanceKind.uint16List;
+    case 'Uint32List':
+      return M.InstanceKind.uint32List;
+    case 'Uint64List':
+      return M.InstanceKind.uint64List;
+    case 'Int8List':
+      return M.InstanceKind.int8List;
+    case 'Int16List':
+      return M.InstanceKind.int16List;
+    case 'Int32List':
+      return M.InstanceKind.int32List;
+    case 'Int64List':
+      return M.InstanceKind.int64List;
+    case 'Float32List':
+      return M.InstanceKind.float32List;
+    case 'Float64List':
+      return M.InstanceKind.float64List;
+    case 'Int32x4List':
+      return M.InstanceKind.int32x4List;
+    case 'Float32x4List':
+      return M.InstanceKind.float32x4List;
+    case 'Float64x2List':
+      return M.InstanceKind.float64x2List;
+    case 'StackTrace':
+      return M.InstanceKind.stackTrace;
+    case 'Closure':
+      return M.InstanceKind.closure;
+    case 'MirrorReference':
+      return M.InstanceKind.mirrorReference;
+    case 'RegExp':
+      return M.InstanceKind.regExp;
+    case 'WeakProperty':
+      return M.InstanceKind.weakProperty;
+    case 'Type':
+      return M.InstanceKind.type;
+    case 'TypeParameter':
+      return M.InstanceKind.typeParameter;
+    case 'TypeRef':
+      return M.InstanceKind.typeRef;
+  }
+  var message = 'Unrecognized instance kind: $s';
+  Logger.root.severe(message);
+  throw new ArgumentError(message);
+}
+
+class Guarded<T extends ServiceObject> implements M.Guarded<T> {
+  bool get isValue => asValue != null;
+  bool get isSentinel => asSentinel != null;
+  final Sentinel asSentinel;
+  final T asValue;
+
+  factory Guarded(ServiceObject obj) {
+    if (obj is Sentinel) {
+      return new Guarded.fromSentinel(obj);
+    } else if (obj is T) {
+      return new Guarded.fromValue(obj);
+    }
+    throw new Exception('${obj.type} is neither Sentinel or $T');
+  }
+
+  Guarded.fromSentinel(this.asSentinel) : asValue = null;
+  Guarded.fromValue(this.asValue) : asSentinel = null;
+}
+
+class BoundField implements M.BoundField {
+  final Field decl;
+  final Guarded<Instance> value;
+  BoundField(this.decl, value) : value = new Guarded(value);
+}
+
+class NativeField implements M.NativeField {
+  final int value;
+  NativeField(this.value);
+}
+
+class MapAssociation implements M.MapAssociation {
+  final Guarded<Instance> key;
+  final Guarded<Instance> value;
+  MapAssociation(key, value)
+      : key = new Guarded(key),
+        value = new Guarded(value);
+}
+
+class Instance extends HeapObject implements M.Instance {
+  M.InstanceKind kind;
+  String valueAsString; // If primitive.
+  bool valueAsStringIsTruncated;
+  ServiceFunction closureFunction; // If a closure.
+  Context closureContext; // If a closure.
+  int length; // If a List, Map or TypedData.
+  int count;
+  int offset;
+  Instance pattern; // If a RegExp.
+
+  String name;
+  Class typeClass;
+  Class parameterizedClass;
+  TypeArguments typeArguments;
+  int parameterIndex;
+  Instance targetType;
+  Instance bound;
+
+  Iterable<BoundField> fields;
+  var nativeFields;
+  Iterable<Guarded<HeapObject>> elements; // If a List.
+  Iterable<MapAssociation> associations; // If a Map.
+  List<dynamic> typedElements; // If a TypedData.
+  HeapObject referent; // If a MirrorReference.
+  Instance key; // If a WeakProperty.
+  Instance value; // If a WeakProperty.
+  Breakpoint activationBreakpoint; // If a Closure.
+  ServiceFunction oneByteFunction; // If a RegExp.
+  ServiceFunction twoByteFunction; // If a RegExp.
+  ServiceFunction externalOneByteFunction; // If a RegExp.
+  ServiceFunction externalTwoByteFunction; // If a RegExp.
+  Instance oneByteBytecode; // If a RegExp.
+  Instance twoByteBytecode; // If a RegExp.
+  bool isCaseSensitive; // If a RegExp.
+  bool isMultiLine; // If a RegExp.
+
+  bool get isAbstractType => M.isAbstractType(kind);
+  bool get isNull => kind == M.InstanceKind.vNull;
+  bool get isBool => kind == M.InstanceKind.bool;
+  bool get isDouble => kind == M.InstanceKind.double;
+  bool get isString => kind == M.InstanceKind.string;
+  bool get isInt => kind == M.InstanceKind.int;
+  bool get isList => kind == M.InstanceKind.list;
+  bool get isMap => kind == M.InstanceKind.map;
+  bool get isTypedData {
+    return M.isTypedData(kind);
+  }
+
+  bool get isSimdValue {
+    return M.isSimdValue(kind);
+  }
+
+  bool get isRegExp => kind == M.InstanceKind.regExp;
+  bool get isMirrorReference => kind == M.InstanceKind.mirrorReference;
+  bool get isWeakProperty => kind == M.InstanceKind.weakProperty;
+  bool get isClosure => kind == M.InstanceKind.closure;
+  bool get isStackTrace => kind == M.InstanceKind.stackTrace;
+  bool get isStackOverflowError {
+    if (clazz == null) {
+      return false;
+    }
+    if (clazz.library == null) {
+      return false;
+    }
+    return (clazz.name == 'StackOverflowError') && clazz.library.isDart('core');
+  }
+
+  bool get isOutOfMemoryError {
+    if (clazz == null) {
+      return false;
+    }
+    if (clazz.library == null) {
+      return false;
+    }
+    return (clazz.name == 'OutOfMemoryError') && clazz.library.isDart('core');
+  }
+
+  // TODO(turnidge): Is this properly backwards compatible when new
+  // instance kinds are added?
+  bool get isPlainInstance => kind == 'PlainInstance';
+
+  Instance._empty(ServiceObjectOwner owner) : super._empty(owner);
+
+  void _update(Map map, bool mapIsRef) {
+    // Extract full properties.1
+    _upgradeCollection(map, isolate);
+    super._update(map, mapIsRef);
+
+    kind = stringToInstanceKind(map['kind']);
+    valueAsString = map['valueAsString'];
+    // Coerce absence to false.
+    valueAsStringIsTruncated = map['valueAsStringIsTruncated'] == true;
+    closureFunction = map['closureFunction'];
+    name = map['name'];
+    length = map['length'];
+    pattern = map['pattern'];
+    typeClass = map['typeClass'];
+
+    final context = map['closureContext'];
+    if (context is Context) {
+      closureContext = context;
+    } else if (context != null) {
+      assert(context is Instance && context.isNull);
+    }
+
+    if (mapIsRef) {
+      return;
+    }
+
+    count = map['count'];
+    offset = map['offset'];
+    isCaseSensitive = map['isCaseSensitive'];
+    isMultiLine = map['isMultiLine'];
+    bool isCompiled = map['_oneByteFunction'] is ServiceFunction;
+    oneByteFunction = isCompiled ? map['_oneByteFunction'] : null;
+    twoByteFunction = isCompiled ? map['_twoByteFunction'] : null;
+    externalOneByteFunction =
+        isCompiled ? map['_externalOneByteFunction'] : null;
+    externalTwoByteFunction =
+        isCompiled ? map['_externalTwoByteFunction'] : null;
+    oneByteBytecode = map['_oneByteBytecode'];
+    twoByteBytecode = map['_twoByteBytecode'];
+
+    if (map['fields'] != null) {
+      var fields = <BoundField>[];
+      for (var f in map['fields']) {
+        fields.add(new BoundField(f['decl'], f['value']));
+      }
+      this.fields = fields;
+    } else {
+      fields = null;
+    }
+    if (map['_nativeFields'] != null) {
+      nativeFields = map['_nativeFields']
+          .map<NativeField>((f) => new NativeField(f['value']))
+          .toList();
+    } else {
+      nativeFields = null;
+    }
+    if (map['elements'] != null) {
+      // Should be:
+      // elements = map['elements'].map((e) => new Guarded<Instance>(e)).toList();
+      // some times we obtain object that are not InstanceRef
+      var localElements = <Guarded<HeapObject>>[];
+      for (var element in map['elements']) {
+        localElements.add(new Guarded<HeapObject>(element));
+      }
+      elements = localElements;
+    } else {
+      elements = null;
+    }
+    if (map['associations'] != null) {
+      associations = map['associations']
+          .map<MapAssociation>((a) => new MapAssociation(a['key'], a['value']))
+          .toList();
+    } else {
+      associations = null;
+    }
+    ;
+    if (map['bytes'] != null) {
+      Uint8List bytes = base64Decode(map['bytes']);
+      switch (map['kind']) {
+        case "Uint8ClampedList":
+          typedElements = bytes.buffer.asUint8ClampedList();
+          break;
+        case "Uint8List":
+          typedElements = bytes.buffer.asUint8List();
+          break;
+        case "Uint16List":
+          typedElements = bytes.buffer.asUint16List();
+          break;
+        case "Uint32List":
+          typedElements = bytes.buffer.asUint32List();
+          break;
+        case "Uint64List":
+          typedElements = bytes.buffer.asUint64List();
+          break;
+        case "Int8List":
+          typedElements = bytes.buffer.asInt8List();
+          break;
+        case "Int16List":
+          typedElements = bytes.buffer.asInt16List();
+          break;
+        case "Int32List":
+          typedElements = bytes.buffer.asInt32List();
+          break;
+        case "Int64List":
+          typedElements = bytes.buffer.asInt64List();
+          break;
+        case "Float32List":
+          typedElements = bytes.buffer.asFloat32List();
+          break;
+        case "Float64List":
+          typedElements = bytes.buffer.asFloat64List();
+          break;
+        case "Int32x4List":
+          typedElements = bytes.buffer.asInt32x4List();
+          break;
+        case "Float32x4List":
+          typedElements = bytes.buffer.asFloat32x4List();
+          break;
+        case "Float64x2List":
+          typedElements = bytes.buffer.asFloat64x2List();
+          break;
+      }
+    } else {
+      typedElements = null;
+    }
+    parameterizedClass = map['parameterizedClass'];
+    typeArguments = map['typeArguments'];
+    parameterIndex = map['parameterIndex'];
+    targetType = map['targetType'];
+    bound = map['bound'];
+
+    referent = map['mirrorReferent'];
+    key = map['propertyKey'];
+    value = map['propertyValue'];
+    activationBreakpoint = map['_activationBreakpoint'];
+
+    // We are fully loaded.
+    _loaded = true;
+  }
+
+  String get shortName {
+    if (isClosure) {
+      return closureFunction.qualifiedName;
+    }
+    if (valueAsString != null) {
+      return valueAsString;
+    }
+    return 'a ${clazz.name}';
+  }
+
+  Future<ServiceObject> evaluate(String expression,
+      {Map<String, ServiceObject> scope, bool disableBreakpoints: false}) {
+    return isolate.eval(this, expression,
+        scope: scope, disableBreakpoints: disableBreakpoints);
+  }
+
+  String toString() => 'Instance($shortName)';
+}
+
+class Context extends HeapObject implements M.Context {
+  Context parentContext;
+  int length;
+  Iterable<ContextElement> variables;
+
+  Context._empty(ServiceObjectOwner owner) : super._empty(owner);
+
+  void _update(Map map, bool mapIsRef) {
+    // Extract full properties.
+    _upgradeCollection(map, isolate);
+    super._update(map, mapIsRef);
+
+    length = map['length'];
+    parentContext = map['parent'];
+
+    if (mapIsRef) {
+      return;
+    }
+
+    if (map['variables'] == null) {
+      variables = <ContextElement>[];
+    } else {
+      var localVariables = <ContextElement>[];
+      for (var element in map['variables']) {
+        localVariables.add(new ContextElement(element));
+      }
+      variables = localVariables;
+    }
+
+    // We are fully loaded.
+    _loaded = true;
+  }
+
+  String toString() => 'Context($length)';
+}
+
+class ContextElement extends M.ContextElement {
+  final Guarded<Instance> value;
+
+  ContextElement(Map map) : value = new Guarded<Instance>(map['value']);
+}
+
+M.FunctionKind stringToFunctionKind(String value) {
+  switch (value) {
+    case 'RegularFunction':
+      return M.FunctionKind.regular;
+    case 'ClosureFunction':
+      return M.FunctionKind.closure;
+    case 'ImplicitClosureFunction':
+      return M.FunctionKind.implicitClosure;
+    case 'GetterFunction':
+      return M.FunctionKind.getter;
+    case 'SetterFunction':
+      return M.FunctionKind.setter;
+    case 'Constructor':
+      return M.FunctionKind.constructor;
+    case 'ImplicitGetter':
+      return M.FunctionKind.implicitGetter;
+    case 'ImplicitSetter':
+      return M.FunctionKind.implicitSetter;
+    case 'ImplicitStaticGetter':
+      return M.FunctionKind.implicitStaticGetter;
+    case 'FieldInitializer':
+      return M.FunctionKind.fieldInitializer;
+    case 'IrregexpFunction':
+      return M.FunctionKind.irregexpFunction;
+    case 'MethodExtractor':
+      return M.FunctionKind.methodExtractor;
+    case 'NoSuchMethodDispatcher':
+      return M.FunctionKind.noSuchMethodDispatcher;
+    case 'InvokeFieldDispatcher':
+      return M.FunctionKind.invokeFieldDispatcher;
+    case 'Collected':
+      return M.FunctionKind.collected;
+    case 'Native':
+      return M.FunctionKind.native;
+    case 'FfiTrampoline':
+      return M.FunctionKind.ffiTrampoline;
+    case 'Stub':
+      return M.FunctionKind.stub;
+    case 'Tag':
+      return M.FunctionKind.tag;
+    case 'SignatureFunction':
+      return M.FunctionKind.signatureFunction;
+    case 'DynamicInvocationForwarder':
+      return M.FunctionKind.dynamicInvocationForwarder;
+  }
+  var message = 'Unrecognized function kind: $value';
+  Logger.root.severe(message);
+  throw new ArgumentError(message);
+}
+
+class ServiceFunction extends HeapObject implements M.ServiceFunction {
+  // owner is a Library, Class, or ServiceFunction.
+  M.ObjectRef dartOwner;
+  Library library;
+  bool isStatic;
+  bool isConst;
+  SourceLocation location;
+  Code code;
+  Code unoptimizedCode;
+  Code bytecode;
+  bool isOptimizable;
+  bool isInlinable;
+  bool hasIntrinsic;
+  bool isRecognized;
+  bool isNative;
+  M.FunctionKind kind;
+  int deoptimizations;
+  String qualifiedName;
+  int usageCounter;
+  bool isDart;
+  ProfileFunction profile;
+  Instance icDataArray;
+  Field field;
+
+  bool get immutable => false;
+
+  ServiceFunction._empty(ServiceObject owner) : super._empty(owner);
+
+  void _update(Map map, bool mapIsRef) {
+    _upgradeCollection(map, owner);
+    super._update(map, mapIsRef);
+
+    name = map['name'];
+    vmName = (map.containsKey('_vmName') ? map['_vmName'] : name);
+
+    dartOwner = map['owner'];
+    kind = stringToFunctionKind(map['_kind']);
+    isDart = M.isDartFunction(kind);
+
+    if (dartOwner is ServiceFunction) {
+      ServiceFunction ownerFunction = dartOwner;
+      library = ownerFunction.library;
+      qualifiedName = "${ownerFunction.qualifiedName}.${name}";
+    } else if (dartOwner is Class) {
+      Class ownerClass = dartOwner;
+      library = ownerClass.library;
+      qualifiedName = "${ownerClass.name}.${name}";
+    } else {
+      library = dartOwner;
+      qualifiedName = name;
+    }
+
+    hasIntrinsic = map['_intrinsic'];
+    isNative = map['_native'];
+
+    if (mapIsRef) {
+      return;
+    }
+
+    _loaded = true;
+    isStatic = map['static'];
+    isConst = map['const'];
+    location = map['location'];
+    code = map['code'];
+    isOptimizable = map['_optimizable'];
+    isInlinable = map['_inlinable'];
+    isRecognized = map['_recognized'];
+    unoptimizedCode = map['_unoptimizedCode'];
+    bytecode = map['_bytecode'];
+    deoptimizations = map['_deoptimizations'];
+    usageCounter = map['_usageCounter'];
+    icDataArray = map['_icDataArray'];
+    field = map['_field'];
+  }
+
+  ServiceFunction get homeMethod {
+    var m = this;
+    while (m.dartOwner is ServiceFunction) {
+      m = m.dartOwner;
+    }
+    return m;
+  }
+
+  String toString() {
+    return "ServiceFunction($qualifiedName)";
+  }
+}
+
+M.SentinelKind stringToSentinelKind(String s) {
+  switch (s) {
+    case 'Collected':
+      return M.SentinelKind.collected;
+    case 'Expired':
+      return M.SentinelKind.expired;
+    case 'NotInitialized':
+      return M.SentinelKind.notInitialized;
+    case 'BeingInitialized':
+      return M.SentinelKind.initializing;
+    case 'OptimizedOut':
+      return M.SentinelKind.optimizedOut;
+    case 'Free':
+      return M.SentinelKind.free;
+  }
+  var message = 'Unrecognized sentinel kind: $s';
+  Logger.root.severe(message);
+  throw new ArgumentError(message);
+}
+
+class Sentinel extends ServiceObject implements M.Sentinel {
+  M.SentinelKind kind;
+  String valueAsString;
+
+  Sentinel._empty(ServiceObjectOwner owner) : super._empty(owner);
+
+  void _update(Map map, bool mapIsRef) {
+    // Extract full properties.
+    _upgradeCollection(map, isolate);
+
+    kind = stringToSentinelKind(map['kind']);
+    valueAsString = map['valueAsString'];
+    _loaded = true;
+  }
+
+  String toString() => 'Sentinel($kind)';
+  String get shortName => valueAsString;
+}
+
+class Thread extends ServiceObject implements M.Thread {
+  M.ThreadKind get kind => _kind;
+  M.ThreadKind _kind;
+  String get kindString => _kindString;
+  String _kindString;
+  int get zoneHighWatermark => _zoneHighWatermark;
+  int _zoneHighWatermark;
+  int get zoneCapacity => _zoneCapacity;
+  int _zoneCapacity;
+
+  Thread._empty(ServiceObjectOwner owner) : super._empty(owner);
+
+  void _update(Map map, bool mapIsRef) {
+    String rawKind = map['kind'];
+
+    switch (rawKind) {
+      case "kUnknownTask":
+        _kind = M.ThreadKind.unknownTask;
+        _kindString = 'unknown';
+        break;
+      case "kMutatorTask":
+        _kind = M.ThreadKind.mutatorTask;
+        _kindString = 'mutator';
+        break;
+      case "kCompilerTask":
+        _kind = M.ThreadKind.compilerTask;
+        _kindString = 'compiler';
+        break;
+      case "kSweeperTask":
+        _kind = M.ThreadKind.sweeperTask;
+        _kindString = 'sweeper';
+        break;
+      case "kMarkerTask":
+        _kind = M.ThreadKind.markerTask;
+        _kindString = 'marker';
+        break;
+      default:
+        assert(false);
+    }
+
+    _zoneHighWatermark = int.parse(map['_zoneHighWatermark']);
+    _zoneCapacity = int.parse(map['_zoneCapacity']);
+  }
+}
+
+class Zone implements M.Zone {
+  int get capacity => _capacity;
+  int _capacity;
+  int get used => _used;
+  int _used;
+
+  Zone(this._capacity, this._used);
+}
+
+class Field extends HeapObject implements M.Field {
+  // Library or Class.
+  HeapObject dartOwner;
+  Library library;
+  Instance declaredType;
+  bool isStatic;
+  bool isFinal;
+  bool isConst;
+  ServiceObject staticValue;
+  String name;
+  String vmName;
+
+  bool guardNullable;
+  M.GuardClassKind guardClassKind;
+  Class guardClass;
+  String guardLength;
+  SourceLocation location;
+
+  Field._empty(ServiceObjectOwner owner) : super._empty(owner);
+
+  void _update(Map map, bool mapIsRef) {
+    // Extract full properties.
+    _upgradeCollection(map, isolate);
+    super._update(map, mapIsRef);
+
+    name = map['name'];
+    vmName = (map.containsKey('_vmName') ? map['_vmName'] : name);
+    dartOwner = map['owner'];
+    declaredType = map['declaredType'];
+    isStatic = map['static'];
+    isFinal = map['final'];
+    isConst = map['const'];
+
+    if (dartOwner is Class) {
+      Class ownerClass = dartOwner;
+      library = ownerClass.library;
+    } else {
+      library = dartOwner;
+    }
+
+    if (mapIsRef) {
+      return;
+    }
+    staticValue = map['staticValue'];
+
+    guardNullable = map['_guardNullable'];
+    if (map['_guardClass'] is Class) {
+      guardClass = map['_guardClass'];
+      guardClassKind = M.GuardClassKind.single;
+    } else {
+      switch (map['_guardClass']) {
+        case 'various':
+          guardClassKind = M.GuardClassKind.dynamic;
+          break;
+        case 'unknown':
+        default:
+          guardClassKind = M.GuardClassKind.unknown;
+          break;
+      }
+    }
+
+    guardLength = map['_guardLength'];
+    location = map['location'];
+    _loaded = true;
+  }
+
+  String toString() => 'Field(${dartOwner.name}.$name)';
+}
+
+class ScriptLine {
+  final Script script;
+  final int line;
+  final String text;
+  Set<Breakpoint> breakpoints;
+
+  ScriptLine(this.script, this.line, this.text);
+
+  bool get isBlank {
+    return text.isEmpty || text.trim().isEmpty;
+  }
+
+  bool _isTrivial = null;
+  bool get isTrivial {
+    if (_isTrivial == null) {
+      _isTrivial = _isTrivialLine(text);
+    }
+    return _isTrivial;
+  }
+
+  static bool _isTrivialToken(String token) {
+    if (token == 'else') {
+      return true;
+    }
+    for (var c in token.split('')) {
+      switch (c) {
+        case '{':
+        case '}':
+        case '(':
+        case ')':
+        case ';':
+          break;
+        default:
+          return false;
+      }
+    }
+    return true;
+  }
+
+  static bool _isTrivialLine(String text) {
+    if (text.trimLeft().startsWith('//')) {
+      return true;
+    }
+    var wsTokens = text.split(new RegExp(r"(\s)+"));
+    for (var wsToken in wsTokens) {
+      var tokens = wsToken.split(new RegExp(r"(\b)"));
+      for (var token in tokens) {
+        if (!_isTrivialToken(token)) {
+          return false;
+        }
+      }
+    }
+    return true;
+  }
+
+  void addBreakpoint(Breakpoint bpt) {
+    if (breakpoints == null) {
+      breakpoints = new Set<Breakpoint>();
+    }
+    breakpoints.add(bpt);
+  }
+
+  void removeBreakpoint(Breakpoint bpt) {
+    assert(breakpoints != null && breakpoints.contains(bpt));
+    breakpoints.remove(bpt);
+    if (breakpoints.isEmpty) {
+      breakpoints = null;
+    }
+  }
+}
+
+class CallSite {
+  final String name;
+  // TODO(turnidge): Use SourceLocation here instead.
+  final Script script;
+  final int tokenPos;
+  final List<CallSiteEntry> entries;
+
+  CallSite(this.name, this.script, this.tokenPos, this.entries);
+
+  int get line => script.tokenToLine(tokenPos);
+  int get column => script.tokenToCol(tokenPos);
+
+  int get aggregateCount {
+    var count = 0;
+    for (var entry in entries) {
+      count += entry.count;
+    }
+    return count;
+  }
+
+  factory CallSite.fromMap(Map siteMap, Script script) {
+    var name = siteMap['name'];
+    var tokenPos = siteMap['tokenPos'];
+    var entries = <CallSiteEntry>[];
+    for (var entryMap in siteMap['cacheEntries']) {
+      entries.add(new CallSiteEntry.fromMap(entryMap));
+    }
+    return new CallSite(name, script, tokenPos, entries);
+  }
+
+  bool operator ==(Object other) {
+    if (other is CallSite) {
+      return (script == other.script) && (tokenPos == other.tokenPos);
+    }
+    return false;
+  }
+
+  int get hashCode => (script.hashCode << 8) | tokenPos;
+
+  String toString() => "CallSite($name, $tokenPos)";
+}
+
+class CallSiteEntry {
+  final /* Class | Library */ receiver;
+  final int count;
+  final ServiceFunction target;
+
+  CallSiteEntry(this.receiver, this.count, this.target);
+
+  factory CallSiteEntry.fromMap(Map entryMap) {
+    return new CallSiteEntry(
+        entryMap['receiver'], entryMap['count'], entryMap['target']);
+  }
+
+  String toString() => "CallSiteEntry(${receiver.name}, $count)";
+}
+
+/// The location of a local variable reference in a script.
+class LocalVarLocation {
+  final int line;
+  final int column;
+  final int endColumn;
+  LocalVarLocation(this.line, this.column, this.endColumn);
+}
+
+class Script extends HeapObject implements M.Script {
+  final lines = <ScriptLine>[];
+  String uri;
+  String kind;
+  DateTime loadTime;
+  int firstTokenPos;
+  int lastTokenPos;
+  int lineOffset;
+  int columnOffset;
+  Library library;
+
+  String source;
+
+  bool get immutable => true;
+
+  String _shortUri;
+
+  Script._empty(ServiceObjectOwner owner) : super._empty(owner);
+
+  /// Retrieves line number [line] if it exists.
+  ScriptLine getLine(int line) {
+    assert(_loaded);
+    assert(line >= 1);
+    var index = (line - lineOffset - 1);
+    if (lines.length < index) {
+      return null;
+    }
+    return lines[line - lineOffset - 1];
+  }
+
+  /// This function maps a token position to a line number.
+  /// The VM considers the first line to be line 1.
+  int tokenToLine(int tokenPos) => _tokenToLine[tokenPos];
+  Map _tokenToLine = {};
+
+  /// This function maps a token position to a column number.
+  /// The VM considers the first column to be column 1.
+  int tokenToCol(int tokenPos) => _tokenToCol[tokenPos];
+  Map _tokenToCol = {};
+
+  int guessTokenLength(int line, int column) {
+    String source = getLine(line).text;
+
+    var pos = column;
+    if (pos >= source.length) {
+      return null;
+    }
+
+    var c = source.codeUnitAt(pos);
+    if (c == 123) return 1; // { - Map literal
+
+    if (c == 91) return 1; // [ - List literal, index, index assignment
+
+    if (c == 40) return 1; // ( - Closure call
+
+    if (_isOperatorChar(c)) {
+      while (++pos < source.length && _isOperatorChar(source.codeUnitAt(pos)));
+      return pos - column;
+    }
+
+    if (_isInitialIdentifierChar(c)) {
+      while (
+          ++pos < source.length && _isIdentifierChar(source.codeUnitAt(pos)));
+      return pos - column;
+    }
+
+    return null;
+  }
+
+  static bool _isOperatorChar(int c) {
+    switch (c) {
+      case 25: // %
+      case 26: // &
+      case 42: // *
+      case 43: // +
+      case 45: // -:
+      case 47: // /
+      case 60: // <
+      case 61: // =
+      case 62: // >
+      case 94: // ^
+      case 124: // |
+      case 126: // ~
+        return true;
+      default:
+        return false;
+    }
+  }
+
+  static bool _isInitialIdentifierChar(int c) {
+    if (c >= 65 && c <= 90) return true; // Upper
+    if (c >= 97 && c <= 122) return true; // Lower
+    if (c == 95) return true; // Underscore
+    if (c == 36) return true; // Dollar
+    return false;
+  }
+
+  static bool _isIdentifierChar(int c) {
+    if (_isInitialIdentifierChar(c)) return true;
+    return c >= 48 && c <= 57; // Digit
+  }
+
+  void _update(Map map, bool mapIsRef) {
+    _upgradeCollection(map, isolate);
+    super._update(map, mapIsRef);
+
+    uri = map['uri'];
+    kind = map['_kind'];
+    _shortUri = uri.substring(uri.lastIndexOf('/') + 1);
+    name = _shortUri;
+    vmName = uri;
+    if (mapIsRef) {
+      return;
+    }
+    _loaded = true;
+    int loadTimeMillis = map['_loadTime'];
+    loadTime = new DateTime.fromMillisecondsSinceEpoch(loadTimeMillis);
+    lineOffset = map['lineOffset'];
+    columnOffset = map['columnOffset'];
+    _parseTokenPosTable(map['tokenPosTable']);
+    source = map['source'];
+    _processSource(map['source']);
+    library = map['library'];
+  }
+
+  void _parseTokenPosTable(List table) {
+    if (table == null) {
+      return;
+    }
+    _tokenToLine.clear();
+    _tokenToCol.clear();
+    firstTokenPos = null;
+    lastTokenPos = null;
+    var lineSet = new Set();
+
+    for (List line in table) {
+      // Each entry begins with a line number...
+      int lineNumber = line[0];
+      lineSet.add(lineNumber);
+      for (var pos = 1; pos < line.length; pos += 2) {
+        // ...and is followed by (token offset, col number) pairs.
+        int tokenOffset = line[pos];
+        int colNumber = line[pos + 1];
+        if (firstTokenPos == null) {
+          // Mark first token position.
+          firstTokenPos = tokenOffset;
+          lastTokenPos = tokenOffset;
+        } else {
+          // Keep track of max and min token positions.
+          firstTokenPos =
+              (firstTokenPos <= tokenOffset) ? firstTokenPos : tokenOffset;
+          lastTokenPos =
+              (lastTokenPos >= tokenOffset) ? lastTokenPos : tokenOffset;
+        }
+        _tokenToLine[tokenOffset] = lineNumber;
+        _tokenToCol[tokenOffset] = colNumber;
+      }
+    }
+  }
+
+  void _processSource(String source) {
+    if (source == null) {
+      return;
+    }
+    var sourceLines = source.split('\n');
+    if (sourceLines.length == 0) {
+      return;
+    }
+    lines.clear();
+    Logger.root.info('Adding ${sourceLines.length} source lines for ${uri}');
+    for (var i = 0; i < sourceLines.length; i++) {
+      lines.add(new ScriptLine(this, i + lineOffset + 1, sourceLines[i]));
+    }
+    for (var bpt in isolate.breakpoints.values) {
+      if (bpt.location.script == this) {
+        _addBreakpoint(bpt);
+      }
+    }
+  }
+
+  // Note, this may return source beyond the token length if [guessTokenLength]
+  // fails.
+  String getToken(int tokenPos) {
+    final int line = tokenToLine(tokenPos);
+    int column = tokenToCol(tokenPos);
+    if ((line == null) || (column == null)) {
+      return null;
+    }
+    // Line and column numbers start at 1 in the VM.
+    column -= 1;
+    String sourceLine = getLine(line)?.text;
+    if (sourceLine == null) {
+      return null;
+    }
+    final int length = guessTokenLength(line, column);
+    if (length == null) {
+      return sourceLine.substring(column);
+    } else {
+      return sourceLine.substring(column, column + length);
+    }
+  }
+
+  void _addBreakpoint(Breakpoint bpt) {
+    var line;
+    if (bpt.location.tokenPos != null) {
+      line = tokenToLine(bpt.location.tokenPos);
+    } else {
+      UnresolvedSourceLocation loc = bpt.location;
+      line = loc.line;
+    }
+    getLine(line)?.addBreakpoint(bpt);
+  }
+
+  void _removeBreakpoint(Breakpoint bpt) {
+    var line;
+    if (bpt.location.tokenPos != null) {
+      line = tokenToLine(bpt.location.tokenPos);
+    } else {
+      UnresolvedSourceLocation loc = bpt.location;
+      line = loc.line;
+    }
+    if (line != null) {
+      getLine(line)?.removeBreakpoint(bpt);
+    }
+  }
+
+  List<LocalVarLocation> scanLineForLocalVariableLocations(Pattern pattern,
+      String name, String lineContents, int lineNumber, int columnOffset) {
+    var r = <LocalVarLocation>[];
+
+    pattern.allMatches(lineContents).forEach((Match match) {
+      // We have a match but our regular expression may have matched extra
+      // characters on either side of the name. Tighten the location.
+      var nameStart = match.input.indexOf(name, match.start);
+      var column = nameStart + columnOffset;
+      var endColumn = column + name.length;
+      var localVarLocation =
+          new LocalVarLocation(lineNumber, column, endColumn);
+      r.add(localVarLocation);
+    });
+
+    return r;
+  }
+
+  List<LocalVarLocation> scanForLocalVariableLocations(
+      String name, int tokenPos, int endTokenPos) {
+    // A pattern that matches:
+    // start of line OR non-(alpha numeric OR period) character followed by
+    // name followed by
+    // a non-alpha numerc character.
+    //
+    // NOTE: This pattern can over match on both ends. This is corrected for
+    // [scanLineForLocalVariableLocationse].
+    var pattern = new RegExp("(^|[^A-Za-z0-9\.])$name[^A-Za-z0-9]");
+
+    // Result.
+    var r = <LocalVarLocation>[];
+
+    // Limits.
+    final lastLine = tokenToLine(endTokenPos);
+    if (lastLine == null) {
+      return r;
+    }
+
+    var lastColumn = tokenToCol(endTokenPos);
+    if (lastColumn == null) {
+      return r;
+    }
+    // Current scan position.
+    var line = tokenToLine(tokenPos);
+    if (line == null) {
+      return r;
+    }
+    var column = tokenToCol(tokenPos);
+    if (column == null) {
+      return r;
+    }
+
+    // Move back by name length.
+    // TODO(johnmccutchan): Fix LocalVarDescriptor to set column before the
+    // identifier name.
+    column = math.max(0, column - name.length);
+
+    var lineContents;
+
+    if (line == lastLine) {
+      // Only one line.
+      if (!getLine(line).isTrivial) {
+        // TODO(johnmccutchan): end token pos -> column can lie for snapshotted
+        // code. e.g.:
+        // io_sink.dart source line 23 ends at column 39
+        // io_sink.dart snapshotted source line 23 ends at column 35.
+        lastColumn = math.min(getLine(line).text.length, lastColumn);
+        lineContents = getLine(line).text.substring(column, lastColumn - 1);
+        return scanLineForLocalVariableLocations(
+            pattern, name, lineContents, line, column);
+      }
+    }
+
+    // Scan first line.
+    if (!getLine(line).isTrivial) {
+      lineContents = getLine(line).text.substring(column);
+      r.addAll(scanLineForLocalVariableLocations(
+          pattern, name, lineContents, line++, column));
+    }
+
+    // Scan middle lines.
+    while (line < (lastLine - 1)) {
+      if (getLine(line).isTrivial) {
+        line++;
+        continue;
+      }
+      lineContents = getLine(line).text;
+      r.addAll(scanLineForLocalVariableLocations(
+          pattern, name, lineContents, line++, 0));
+    }
+
+    // Scan last line.
+    if (!getLine(line).isTrivial) {
+      // TODO(johnmccutchan): end token pos -> column can lie for snapshotted
+      // code. e.g.:
+      // io_sink.dart source line 23 ends at column 39
+      // io_sink.dart snapshotted source line 23 ends at column 35.
+      lastColumn = math.min(getLine(line).text.length, lastColumn);
+      lineContents = getLine(line).text.substring(0, lastColumn - 1);
+      r.addAll(scanLineForLocalVariableLocations(
+          pattern, name, lineContents, line, 0));
+    }
+    return r;
+  }
+}
+
+class PcDescriptor {
+  final int pcOffset;
+  final int deoptId;
+  final int tokenPos;
+  final int tryIndex;
+  final String kind;
+  Script script;
+  String formattedLine;
+  PcDescriptor(
+      this.pcOffset, this.deoptId, this.tokenPos, this.tryIndex, this.kind);
+
+  String formattedDeoptId() {
+    if (deoptId == -1) {
+      return 'N/A';
+    }
+    return deoptId.toString();
+  }
+
+  String formattedTokenPos() {
+    if (tokenPos == -1) {
+      return '';
+    }
+    return tokenPos.toString();
+  }
+
+  void processScript(Script script) {
+    this.script = null;
+    if (tokenPos == -1) {
+      return;
+    }
+    var line = script.tokenToLine(tokenPos);
+    if (line == null) {
+      return;
+    }
+    this.script = script;
+    var scriptLine = script.getLine(line);
+    formattedLine = scriptLine.text;
+  }
+}
+
+class PcDescriptors extends ServiceObject implements M.PcDescriptorsRef {
+  Class clazz;
+  int size;
+  bool get immutable => true;
+  final List<PcDescriptor> descriptors = <PcDescriptor>[];
+
+  PcDescriptors._empty(ServiceObjectOwner owner) : super._empty(owner) {}
+
+  void _update(Map m, bool mapIsRef) {
+    if (mapIsRef) {
+      return;
+    }
+    _upgradeCollection(m, isolate);
+    clazz = m['class'];
+    size = m['size'];
+    descriptors.clear();
+    for (var descriptor in m['members']) {
+      var pcOffset = int.parse(descriptor['pcOffset'], radix: 16);
+      var deoptId = descriptor['deoptId'];
+      var tokenPos = descriptor['tokenPos'];
+      var tryIndex = descriptor['tryIndex'];
+      var kind = descriptor['kind'].trim();
+      descriptors
+          .add(new PcDescriptor(pcOffset, deoptId, tokenPos, tryIndex, kind));
+    }
+  }
+}
+
+class LocalVarDescriptor implements M.LocalVarDescriptorsRef {
+  final String id;
+  final String name;
+  final int index;
+  final int declarationPos;
+  final int beginPos;
+  final int endPos;
+  final int scopeId;
+  final String kind;
+
+  LocalVarDescriptor(this.id, this.name, this.index, this.declarationPos,
+      this.beginPos, this.endPos, this.scopeId, this.kind);
+}
+
+class LocalVarDescriptors extends ServiceObject {
+  Class clazz;
+  int size;
+  bool get immutable => true;
+  final List<LocalVarDescriptor> descriptors = <LocalVarDescriptor>[];
+  LocalVarDescriptors._empty(ServiceObjectOwner owner) : super._empty(owner);
+
+  void _update(Map m, bool mapIsRef) {
+    if (mapIsRef) {
+      return;
+    }
+    _upgradeCollection(m, isolate);
+    clazz = m['class'];
+    size = m['size'];
+    descriptors.clear();
+    for (var descriptor in m['members']) {
+      var id = descriptor['name'];
+      var name = descriptor['name'];
+      var index = descriptor['index'];
+      var declarationPos = descriptor['declarationTokenPos'];
+      var beginPos = descriptor['scopeStartTokenPos'];
+      var endPos = descriptor['scopeEndTokenPos'];
+      var scopeId = descriptor['scopeId'];
+      var kind = descriptor['kind'].trim();
+      descriptors.add(new LocalVarDescriptor(
+          id, name, index, declarationPos, beginPos, endPos, scopeId, kind));
+    }
+  }
+}
+
+class ObjectPool extends HeapObject implements M.ObjectPool {
+  bool get immutable => false;
+
+  int length;
+  List<ObjectPoolEntry> entries;
+
+  ObjectPool._empty(ServiceObjectOwner owner) : super._empty(owner);
+
+  void _update(Map map, bool mapIsRef) {
+    _upgradeCollection(map, isolate);
+    super._update(map, mapIsRef);
+
+    length = map['length'];
+    if (mapIsRef) {
+      return;
+    }
+    entries = map['_entries']
+        .map<ObjectPoolEntry>((map) => new ObjectPoolEntry(map))
+        .toList();
+  }
+}
+
+class ObjectPoolEntry implements M.ObjectPoolEntry {
+  final int offset;
+  final M.ObjectPoolEntryKind kind;
+  final M.ObjectRef asObject;
+  final int asInteger;
+
+  factory ObjectPoolEntry(map) {
+    M.ObjectPoolEntryKind kind = stringToObjectPoolEntryKind(map['kind']);
+    int offset = map['offset'];
+    switch (kind) {
+      case M.ObjectPoolEntryKind.nativeEntryData:
+      case M.ObjectPoolEntryKind.object:
+        return new ObjectPoolEntry._fromObject(map['value'], offset);
+      default:
+        return new ObjectPoolEntry._fromInteger(kind, map['value'], offset);
+    }
+  }
+
+  ObjectPoolEntry._fromObject(this.asObject, this.offset)
+      : kind = M.ObjectPoolEntryKind.object,
+        asInteger = null;
+
+  ObjectPoolEntry._fromInteger(this.kind, this.asInteger, this.offset)
+      : asObject = null;
+}
+
+M.ObjectPoolEntryKind stringToObjectPoolEntryKind(String kind) {
+  switch (kind) {
+    case 'Object':
+      return M.ObjectPoolEntryKind.object;
+    case 'Immediate':
+      return M.ObjectPoolEntryKind.immediate;
+    case 'NativeEntryData':
+      return M.ObjectPoolEntryKind.nativeEntryData;
+    case 'NativeFunction':
+    case 'NativeFunctionWrapper':
+      return M.ObjectPoolEntryKind.nativeEntry;
+  }
+  throw new Exception('Unknown ObjectPoolEntryKind ($kind)');
+}
+
+class ICData extends HeapObject implements M.ICData {
+  HeapObject dartOwner;
+  String selector;
+  Instance argumentsDescriptor;
+  Instance entries;
+
+  bool get immutable => false;
+
+  ICData._empty(ServiceObjectOwner owner) : super._empty(owner);
+
+  void _update(Map map, bool mapIsRef) {
+    _upgradeCollection(map, isolate);
+    super._update(map, mapIsRef);
+
+    dartOwner = map['_owner'];
+    selector = map['_selector'];
+    if (mapIsRef) {
+      return;
+    }
+    argumentsDescriptor = map['_argumentsDescriptor'];
+    entries = map['_entries'];
+  }
+}
+
+class UnlinkedCall extends HeapObject implements M.UnlinkedCall {
+  String selector;
+  Instance argumentsDescriptor;
+
+  bool get immutable => false;
+
+  UnlinkedCall._empty(ServiceObjectOwner owner) : super._empty(owner);
+
+  void _update(Map map, bool mapIsRef) {
+    _upgradeCollection(map, isolate);
+    super._update(map, mapIsRef);
+
+    selector = map['_selector'];
+    if (mapIsRef) {
+      return;
+    }
+    argumentsDescriptor = map['_argumentsDescriptor'];
+  }
+}
+
+class SingleTargetCache extends HeapObject implements M.SingleTargetCache {
+  Code target;
+  int lowerLimit;
+  int upperLimit;
+
+  bool get immutable => false;
+
+  SingleTargetCache._empty(ServiceObjectOwner owner) : super._empty(owner);
+
+  void _update(Map map, bool mapIsRef) {
+    _upgradeCollection(map, isolate);
+    super._update(map, mapIsRef);
+
+    target = map['_target'];
+    if (mapIsRef) {
+      return;
+    }
+    lowerLimit = map['_lowerLimit'];
+    upperLimit = map['_upperLimit'];
+  }
+}
+
+class SubtypeTestCache extends HeapObject implements M.SubtypeTestCache {
+  Instance cache;
+
+  bool get immutable => false;
+
+  SubtypeTestCache._empty(ServiceObjectOwner owner) : super._empty(owner);
+
+  void _update(Map map, bool mapIsRef) {
+    _upgradeCollection(map, isolate);
+    super._update(map, mapIsRef);
+
+    if (mapIsRef) {
+      return;
+    }
+    cache = map['_cache'];
+  }
+}
+
+class TypeArguments extends HeapObject implements M.TypeArguments {
+  HeapObject dartOwner;
+  String name;
+  Iterable<Instance> types;
+
+  TypeArguments._empty(ServiceObjectOwner owner) : super._empty(owner);
+
+  void _update(Map map, bool mapIsRef) {
+    _upgradeCollection(map, isolate);
+    super._update(map, mapIsRef);
+
+    dartOwner = map['_owner'];
+    name = map['name'];
+    if (mapIsRef) {
+      return;
+    }
+    types = new List<Instance>.from(map['types']);
+  }
+}
+
+class InstanceSet extends HeapObject implements M.InstanceSet {
+  HeapObject dartOwner;
+  int count;
+  Iterable<HeapObject> instances;
+
+  InstanceSet._empty(ServiceObjectOwner owner) : super._empty(owner);
+
+  void _update(Map map, bool mapIsRef) {
+    _upgradeCollection(map, isolate);
+    super._update(map, mapIsRef);
+
+    if (mapIsRef) {
+      return;
+    }
+    count = map['totalCount'];
+    instances = new List<HeapObject>.from(map['instances']);
+  }
+}
+
+class MegamorphicCache extends HeapObject implements M.MegamorphicCache {
+  int mask;
+  Instance buckets;
+  String selector;
+  Instance argumentsDescriptor;
+
+  bool get immutable => false;
+
+  MegamorphicCache._empty(ServiceObjectOwner owner) : super._empty(owner);
+
+  void _update(Map map, bool mapIsRef) {
+    _upgradeCollection(map, isolate);
+    super._update(map, mapIsRef);
+
+    selector = map['_selector'];
+    if (mapIsRef) {
+      return;
+    }
+
+    mask = map['_mask'];
+    buckets = map['_buckets'];
+    argumentsDescriptor = map['_argumentsDescriptor'];
+  }
+}
+
+class CodeInstruction {
+  final int address;
+  final int pcOffset;
+  final String machine;
+  final String human;
+  final ServiceObject object;
+  CodeInstruction jumpTarget;
+  List<PcDescriptor> descriptors = <PcDescriptor>[];
+
+  CodeInstruction(
+      this.address, this.pcOffset, this.machine, this.human, this.object);
+
+  bool get isComment => address == 0;
+  bool get hasDescriptors => descriptors.length > 0;
+
+  bool _isJumpInstruction() {
+    return human.startsWith('j');
+  }
+
+  int _getJumpAddress() {
+    assert(_isJumpInstruction());
+    var chunks = human.split(' ');
+    if (chunks.length != 2) {
+      // We expect jump instructions to be of the form 'j.. address'.
+      return 0;
+    }
+    var address = chunks[1];
+    if (address.startsWith('0x')) {
+      // Chop off the 0x.
+      address = address.substring(2);
+    }
+    try {
+      return int.parse(address, radix: 16);
+    } catch (_) {
+      return 0;
+    }
+  }
+
+  void _resolveJumpTarget(
+      List<CodeInstruction> instructionsByAddressOffset, int startAddress) {
+    if (!_isJumpInstruction()) {
+      return;
+    }
+    int address = _getJumpAddress();
+    if (address == 0) {
+      return;
+    }
+    var relativeAddress = address - startAddress;
+    if (relativeAddress < 0) {
+      Logger.root.warning('Bad address resolving jump target $relativeAddress');
+      return;
+    }
+    if (relativeAddress >= instructionsByAddressOffset.length) {
+      Logger.root.warning('Bad address resolving jump target $relativeAddress');
+      return;
+    }
+    jumpTarget = instructionsByAddressOffset[relativeAddress];
+  }
+}
+
+M.CodeKind stringToCodeKind(String s) {
+  if (s == 'Native') {
+    return M.CodeKind.native;
+  } else if (s == 'Dart') {
+    return M.CodeKind.dart;
+  } else if (s == 'Collected') {
+    return M.CodeKind.collected;
+  } else if (s == 'Tag') {
+    return M.CodeKind.tag;
+  } else if (s == 'Stub') {
+    return M.CodeKind.stub;
+  }
+  var message = 'Unrecognized code kind: $s';
+  Logger.root.severe(message);
+  throw new ArgumentError(message);
+}
+
+class CodeInlineInterval {
+  final int start;
+  final int end;
+  final List<ServiceFunction> functions = <ServiceFunction>[];
+  bool contains(int pc) => (pc >= start) && (pc < end);
+  CodeInlineInterval(this.start, this.end);
+}
+
+class Code extends HeapObject implements M.Code {
+  M.CodeKind kind;
+  ObjectPool objectPool;
+  ServiceFunction function;
+  Script script;
+  bool isOptimized;
+  bool hasIntrinsic;
+  bool isNative;
+
+  int startAddress = 0;
+  int endAddress = 0;
+  final instructions = <CodeInstruction>[];
+  List<CodeInstruction> instructionsByAddressOffset;
+
+  ProfileCode profile;
+  final List<CodeInlineInterval> inlineIntervals = <CodeInlineInterval>[];
+  final List<ServiceFunction> inlinedFunctions = <ServiceFunction>[];
+
+  bool get immutable => true;
+
+  Code._empty(ServiceObjectOwner owner) : super._empty(owner);
+
+  void _updateDescriptors(Script script) {
+    this.script = script;
+    for (var instruction in instructions) {
+      for (var descriptor in instruction.descriptors) {
+        descriptor.processScript(script);
+      }
+    }
+  }
+
+  Future loadScript() {
+    if (script != null) {
+      // Already done.
+      return null;
+    }
+    if (kind != M.CodeKind.dart) {
+      return null;
+    }
+    if (function == null) {
+      return null;
+    }
+    if ((function.location == null) || (function.location.script == null)) {
+      // Attempt to load the function.
+      return function.load().then((func) {
+        var script = function.location.script;
+        if (script == null) {
+          // Function doesn't have an associated script.
+          return null;
+        }
+        // Load the script and then update descriptors.
+        return script.load().then((_) => _updateDescriptors(script));
+      });
+    }
+    {
+      // Load the script and then update descriptors.
+      var script = function.location.script;
+      return script.load().then((_) => _updateDescriptors(script));
+    }
+  }
+
+  /// Reload [this]. Returns a future which completes to [this] or an
+  /// exception.
+  Future<ServiceObject> reload({int count: kDefaultFieldLimit}) {
+    assert(kind != null);
+    if (isDartCode) {
+      // We only reload Dart code.
+      return super.reload(count: count);
+    }
+    return new Future.value(this);
+  }
+
+  void _update(Map m, bool mapIsRef) {
+    name = m['name'];
+    vmName = (m.containsKey('_vmName') ? m['_vmName'] : name);
+    isOptimized = m['_optimized'];
+    kind = stringToCodeKind(m['kind']);
+    hasIntrinsic = m['_intrinsic'];
+    isNative = m['_native'];
+    if (mapIsRef) {
+      return;
+    }
+    _loaded = true;
+    startAddress = int.parse(m['_startAddress'], radix: 16);
+    endAddress = int.parse(m['_endAddress'], radix: 16);
+    function = isolate.getFromMap(m['function']);
+    objectPool = isolate.getFromMap(m['_objectPool']);
+    var disassembly = m['_disassembly'];
+    if (disassembly != null) {
+      _processDisassembly(disassembly);
+    }
+    var descriptors = m['_descriptors'];
+    if (descriptors != null) {
+      descriptors = descriptors['members'];
+      _processDescriptors(descriptors);
+    }
+    hasDisassembly = (instructions.length != 0) && (kind == M.CodeKind.dart);
+    inlinedFunctions.clear();
+    var inlinedFunctionsTable = m['_inlinedFunctions'];
+    var inlinedIntervals = m['_inlinedIntervals'];
+    if (inlinedFunctionsTable != null) {
+      // Iterate and upgrade each ServiceFunction.
+      for (var i = 0; i < inlinedFunctionsTable.length; i++) {
+        // Upgrade each function and set it back in the list.
+        var func = isolate.getFromMap(inlinedFunctionsTable[i]);
+        inlinedFunctionsTable[i] = func;
+        if (!inlinedFunctions.contains(func)) {
+          inlinedFunctions.add(func);
+        }
+      }
+    }
+    if ((inlinedIntervals == null) || (inlinedFunctionsTable == null)) {
+      // No inline information.
+      inlineIntervals.clear();
+      return;
+    }
+    _processInline(inlinedFunctionsTable, inlinedIntervals);
+
+    _upgradeCollection(m, isolate);
+    super._update(m, mapIsRef);
+  }
+
+  CodeInlineInterval findInterval(int pc) {
+    for (var i = 0; i < inlineIntervals.length; i++) {
+      var interval = inlineIntervals[i];
+      if (interval.contains(pc)) {
+        return interval;
+      }
+    }
+    return null;
+  }
+
+  void _processInline(List/*<ServiceFunction>*/ inlinedFunctionsTable,
+      List/*<List<int>>*/ inlinedIntervals) {
+    for (var i = 0; i < inlinedIntervals.length; i++) {
+      var inlinedInterval = inlinedIntervals[i];
+      var start = inlinedInterval[0] + startAddress;
+      var end = inlinedInterval[1] + startAddress;
+      var codeInlineInterval = new CodeInlineInterval(start, end);
+      for (var i = 2; i < inlinedInterval.length - 1; i++) {
+        var inline_id = inlinedInterval[i];
+        if (inline_id < 0) {
+          continue;
+        }
+        var function = inlinedFunctionsTable[inline_id];
+        codeInlineInterval.functions.add(function);
+      }
+      inlineIntervals.add(codeInlineInterval);
+    }
+  }
+
+  bool hasDisassembly = false;
+
+  void _processDisassembly(List disassembly) {
+    assert(disassembly != null);
+    instructions.clear();
+    instructionsByAddressOffset = new List(endAddress - startAddress);
+
+    assert((disassembly.length % 4) == 0);
+    for (var i = 0; i < disassembly.length; i += 4) {
+      var address = 0; // Assume code comment.
+      var machine = disassembly[i + 1];
+      var human = disassembly[i + 2];
+      var object = disassembly[i + 3];
+      if (object != null) {
+        object = new ServiceObject._fromMap(owner, object);
+      }
+      var pcOffset = 0;
+      if (disassembly[i] != null) {
+        // Not a code comment, extract address.
+        address = int.parse(disassembly[i], radix: 16);
+        pcOffset = address - startAddress;
+      }
+      var instruction =
+          new CodeInstruction(address, pcOffset, machine, human, object);
+      instructions.add(instruction);
+      if (disassembly[i] != null) {
+        // Not a code comment.
+        instructionsByAddressOffset[pcOffset] = instruction;
+      }
+    }
+    for (var instruction in instructions) {
+      instruction._resolveJumpTarget(instructionsByAddressOffset, startAddress);
+    }
+  }
+
+  void _processDescriptors(List descriptors) {
+    for (Map descriptor in descriptors) {
+      var pcOffset = int.parse(descriptor['pcOffset'], radix: 16);
+      var address = startAddress + pcOffset;
+      var deoptId = descriptor['deoptId'];
+      var tokenPos = descriptor['tokenPos'];
+      var tryIndex = descriptor['tryIndex'];
+      var kind = descriptor['kind'].trim();
+
+      var instruction = instructionsByAddressOffset[address - startAddress];
+      if (instruction != null) {
+        instruction.descriptors
+            .add(new PcDescriptor(pcOffset, deoptId, tokenPos, tryIndex, kind));
+      } else {
+        Logger.root.warning(
+            'Could not find instruction with pc descriptor address: $address');
+      }
+    }
+  }
+
+  /// Returns true if [address] is contained inside [this].
+  bool contains(int address) {
+    return (address >= startAddress) && (address < endAddress);
+  }
+
+  bool get isDartCode => (kind == M.CodeKind.dart) || (kind == M.CodeKind.stub);
+
+  String toString() => 'Code($kind, $name)';
+}
+
+class SocketKind {
+  final _value;
+  const SocketKind._internal(this._value);
+  String toString() => '$_value';
+
+  static SocketKind fromString(String s) {
+    if (s == 'Listening') {
+      return Listening;
+    } else if (s == 'Normal') {
+      return Normal;
+    } else if (s == 'Pipe') {
+      return Pipe;
+    } else if (s == 'Internal') {
+      return Internal;
+    }
+    var message = 'Unrecognized socket kind: $s';
+    Logger.root.warning(message);
+    throw new ArgumentError(message);
+  }
+
+  static const Listening = const SocketKind._internal('Listening');
+  static const Normal = const SocketKind._internal('Normal');
+  static const Pipe = const SocketKind._internal('Pipe');
+  static const Internal = const SocketKind._internal('Internal');
+}
+
+/// A snapshot of statistics associated with a [Socket].
+class SocketStats {
+  final int bytesRead;
+  final int bytesWritten;
+  final int readCalls;
+  final int writeCalls;
+  final int available;
+
+  SocketStats(this.bytesRead, this.bytesWritten, this.readCalls,
+      this.writeCalls, this.available);
+}
+
+/// A peer to a Socket in dart:io. Sockets can represent network sockets or
+/// OS pipes. Each socket is owned by another ServceObject, for example,
+/// a process or an HTTP server.
+class Socket extends ServiceObject {
+  Socket._empty(ServiceObjectOwner owner) : super._empty(owner);
+
+  ServiceObject socketOwner;
+
+  bool get isPipe => (kind == SocketKind.Pipe);
+
+  SocketStats latest;
+  SocketStats previous;
+
+  SocketKind kind;
+
+  String protocol = '';
+
+  bool readClosed = false;
+  bool writeClosed = false;
+  bool closing = false;
+
+  /// Listening for connections.
+  bool listening = false;
+
+  int fd;
+
+  String localAddress;
+  int localPort;
+  String remoteAddress;
+  int remotePort;
+
+  // Updates internal state from [map]. [map] can be a reference.
+  void _update(Map map, bool mapIsRef) {
+    name = map['name'];
+    vmName = map['name'];
+
+    kind = SocketKind.fromString(map['kind']);
+
+    if (mapIsRef) {
+      return;
+    }
+
+    _loaded = true;
+
+    _upgradeCollection(map, isolate);
+
+    readClosed = map['readClosed'];
+    writeClosed = map['writeClosed'];
+    closing = map['closing'];
+    listening = map['listening'];
+
+    protocol = map['protocol'];
+
+    localAddress = map['localAddress'];
+    localPort = map['localPort'];
+    remoteAddress = map['remoteAddress'];
+    remotePort = map['remotePort'];
+
+    fd = map['fd'];
+    socketOwner = map['owner'];
+  }
+}
+
+class ServiceMetric extends ServiceObject implements M.Metric {
+  ServiceMetric._empty(ServiceObjectOwner owner) : super._empty(owner) {}
+
+  bool get immutable => false;
+
+  Future<Map> _fetchDirect({int count: kDefaultFieldLimit}) {
+    assert(owner is Isolate);
+    return isolate.invokeRpcNoUpgrade('_getIsolateMetric', {'metricId': id});
+  }
+
+  String description;
+  double value = 0.0;
+  // Only a gauge has a non-null min and max.
+  double min;
+  double max;
+
+  bool get isGauge => (min != null) && (max != null);
+
+  void _update(Map map, bool mapIsRef) {
+    name = map['name'];
+    description = map['description'];
+    vmName = map['name'];
+    value = map['value'];
+    min = map['min'];
+    max = map['max'];
+  }
+
+  String toString() => "ServiceMetric($_id)";
+}
+
+Future<Null> printFrames(List frames) async {
+  for (int i = 0; i < frames.length; i++) {
+    final Frame frame = frames[i];
+    String frameText = await frame.toUserString();
+    print('#${i.toString().padLeft(3)}: $frameText');
+  }
+}
+
+class Frame extends ServiceObject implements M.Frame {
+  M.FrameKind kind = M.FrameKind.regular;
+  int index;
+  ServiceFunction function;
+  SourceLocation location;
+  Code code;
+  List<ServiceMap> variables = <ServiceMap>[];
+  String marker;
+
+  Frame._empty(ServiceObject owner) : super._empty(owner);
+
+  void _update(Map map, bool mapIsRef) {
+    assert(!mapIsRef);
+    _loaded = true;
+    _upgradeCollection(map, owner);
+    this.kind = _fromString(map['kind']);
+    this.marker = map['marker'];
+    this.index = map['index'];
+    this.function = map['function'];
+    this.location = map['location'];
+    this.code = map['code'];
+    if (map['vars'] == null) {
+      this.variables = <ServiceMap>[];
+    } else {
+      this.variables = new List<ServiceMap>.from(map['vars']);
+    }
+  }
+
+  M.FrameKind _fromString(String frameKind) {
+    if (frameKind == null) {
+      return M.FrameKind.regular;
+    }
+    switch (frameKind) {
+      case 'Regular':
+        return M.FrameKind.regular;
+      case 'AsyncCausal':
+        return M.FrameKind.asyncCausal;
+      case 'AsyncSuspensionMarker':
+        return M.FrameKind.asyncSuspensionMarker;
+      case 'AsyncActivation':
+        return M.FrameKind.asyncActivation;
+      default:
+        throw new UnsupportedError('Unknown FrameKind: $frameKind');
+    }
+  }
+
+  String toString() {
+    if (function != null) {
+      return "Frame([$kind] ${function.qualifiedName} $location)";
+    } else if (location != null) {
+      return "Frame([$kind] $location)";
+    } else {
+      return "Frame([$kind])";
+    }
+  }
+
+  Future<String> toUserString() async {
+    if (function != null) {
+      return "Frame([$kind] ${function.qualifiedName} "
+          "${await location.toUserString()})";
+    } else if (location != null) {
+      return "Frame([$kind] ${await location.toUserString()}";
+    } else {
+      return "Frame([$kind])";
+    }
+  }
+}
+
+class ServiceMessage extends ServiceObject {
+  int index;
+  String messageObjectId;
+  int size;
+  ServiceFunction handler;
+  SourceLocation location;
+
+  ServiceMessage._empty(ServiceObject owner) : super._empty(owner);
+
+  void _update(Map map, bool mapIsRef) {
+    assert(!mapIsRef);
+    _loaded = true;
+    _upgradeCollection(map, owner);
+    this.messageObjectId = map['messageObjectId'];
+    this.index = map['index'];
+    this.size = map['size'];
+    this.handler = map['handler'];
+    this.location = map['location'];
+  }
+}
+
+// Helper function to extract possible breakpoint locations from a
+// SourceReport for some script.
+Set<int> getPossibleBreakpointLines(ServiceMap report, Script script) {
+  var result = new Set<int>();
+  int scriptIndex;
+  int numScripts = report['scripts'].length;
+  for (scriptIndex = 0; scriptIndex < numScripts; scriptIndex++) {
+    if (report['scripts'][scriptIndex].id == script.id) {
+      break;
+    }
+  }
+  if (scriptIndex == numScripts) {
+    return result;
+  }
+  if (script.source == null) {
+    return result;
+  }
+  var ranges = report['ranges'];
+  if (ranges != null) {
+    for (var range in ranges) {
+      if (range['scriptIndex'] != scriptIndex) {
+        continue;
+      }
+      if (range['compiled']) {
+        var possibleBpts = range['possibleBreakpoints'];
+        if (possibleBpts != null) {
+          for (var tokenPos in possibleBpts) {
+            result.add(script.tokenToLine(tokenPos));
+          }
+        }
+      } else {
+        int startLine = script.tokenToLine(range['startPos']);
+        int endLine = script.tokenToLine(range['endPos']);
+        for (int line = startLine; line <= endLine; line++) {
+          if (!script.getLine(line).isTrivial) {
+            result.add(line);
+          }
+        }
+      }
+    }
+  }
+  return result;
+}
+
+// Returns true if [map] is a service map. i.e. it has the following keys:
+// 'id' and a 'type'.
+bool _isServiceMap(Map m) {
+  return (m != null) && (m['type'] != null);
+}
+
+bool _hasRef(String type) => type.startsWith('@');
+String _stripRef(String type) => (_hasRef(type) ? type.substring(1) : type);
+
+/// Recursively upgrades all [ServiceObject]s inside [collection] which must
+/// be an [Map] or an [List]. Upgraded elements will be
+/// associated with [vm] and [isolate].
+void _upgradeCollection(collection, ServiceObjectOwner owner) {
+  if (collection is ServiceMap) {
+    return; // Already upgraded.
+  }
+
+  if (collection is Map) {
+    _upgradeMap(collection, owner);
+  } else if (collection is List) {
+    _upgradeList(collection, owner);
+  }
+}
+
+void _upgradeMap(Map map, ServiceObjectOwner owner) {
+  map.forEach((k, v) {
+    if ((v is Map) && _isServiceMap(v)) {
+      map[k] = owner.getFromMap(v);
+    } else if (v is List) {
+      _upgradeList(v, owner);
+    } else if (v is Map) {
+      _upgradeMap(v, owner);
+    }
+  });
+}
+
+void _upgradeList(List list, ServiceObjectOwner owner) {
+  if (list is Uint8List) {
+    // Nothing to upgrade; avoid slowly visiting every byte
+    // of large binary responses.
+    return;
+  }
+
+  for (var i = 0; i < list.length; i++) {
+    var v = list[i];
+    if ((v is Map) && _isServiceMap(v)) {
+      list[i] = owner.getFromMap(v);
+    } else if (v is List) {
+      _upgradeList(v, owner);
+    } else if (v is Map) {
+      _upgradeMap(v, owner);
+    }
+  }
+}
+
+class Service implements M.Service {
+  final String alias;
+  final String method;
+  final String service;
+
+  Service(this.alias, this.method, this.service) {
+    assert(this.alias != null);
+    assert(this.method != null);
+    assert(this.service != null);
+  }
+}
+
+class TimelineRecorder implements M.TimelineRecorder {
+  final String name;
+  const TimelineRecorder(this.name);
+}
+
+class TimelineStream implements M.TimelineStream {
+  final String name;
+  final bool isRecorded;
+  const TimelineStream(this.name, this.isRecorded);
+}
+
+class TimelineProfile implements M.TimelineProfile {
+  final String name;
+  final Iterable<TimelineStream> streams;
+  const TimelineProfile(this.name, this.streams);
+}
+
+class TimelineFlags implements M.TimelineFlags {
+  // Dart developers care about the following streams:
+  static final Set<String> _dart =
+      new Set<String>.from(const <String>['GC', 'Compiler', 'Dart']);
+
+  // Dart developers care about the following streams:
+  static final Set<String> _flutter =
+      new Set<String>.from(const <String>['GC', 'Dart', 'Embedder']);
+
+  // VM developers care about the following streams:
+  static final Set<String> _vm = new Set<String>.from(const <String>[
+    'GC',
+    'Compiler',
+    'Dart',
+    'Debugger',
+    'Embedder',
+    'Isolate',
+    'VM',
+  ]);
+
+  final TimelineRecorder recorder;
+  final List<TimelineStream> streams;
+  final List<TimelineProfile> profiles;
+
+  factory TimelineFlags(ServiceMap response) {
+    assert(response['type'] == 'TimelineFlags');
+
+    assert(response['recorderName'] != null);
+    final TimelineRecorder recorder =
+        new TimelineRecorder(response['recorderName']);
+
+    assert(response['recordedStreams'] != null);
+    final Set<String> recorded =
+        new Set<String>.from(response['recordedStreams']);
+
+    assert(response['availableStreams'] != null);
+    final List<TimelineStream> streams = response['availableStreams']
+        .map<TimelineStream>((/*String*/ name) =>
+            new TimelineStream(name, recorded.contains(name)))
+        .toList();
+
+    final List<TimelineProfile> profiles = [
+      const TimelineProfile('None', const []),
+      new TimelineProfile('Dart Developer',
+          streams.where((s) => _dart.contains(s.name)).toList()),
+      new TimelineProfile('Flutter Developer',
+          streams.where((s) => _flutter.contains(s.name)).toList()),
+      new TimelineProfile(
+          'VM Developer', streams.where((s) => _vm.contains(s.name)).toList()),
+      new TimelineProfile('All', streams),
+    ];
+
+    return new TimelineFlags._(recorder, streams, profiles);
+  }
+
+  const TimelineFlags._(this.recorder, this.streams, this.profiles);
+}
diff --git a/runtime/observatory_2/lib/tracer.dart b/runtime/observatory_2/lib/tracer.dart
new file mode 100644
index 0000000..3a18cfd
--- /dev/null
+++ b/runtime/observatory_2/lib/tracer.dart
@@ -0,0 +1,97 @@
+// 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.
+
+library tracer;
+
+import 'dart:async';
+
+import 'package:logging/logging.dart';
+
+_deepCopy(src) {
+  if (src is Map) {
+    var dest = {};
+    src.forEach((key, val) {
+      dest[key] = _deepCopy(val);
+    });
+    return dest;
+  } else if (src is List) {
+    var dest = [];
+    src.forEach((val) {
+      dest.add(_deepCopy(val));
+    });
+    return dest;
+  } else {
+    return src;
+  }
+}
+
+class TraceEvent {
+  TraceEvent.msg(this._time, this.message, Map originalMap) {
+    map = _deepCopy(originalMap);
+  }
+
+  String get timeStamp => "T+${_time}us";
+
+  String toString() {
+    return "[${timeStamp}] ${message}";
+  }
+
+  int _time;
+  String message;
+  Map map;
+}
+
+class Tracer {
+  // The current global tracer.
+  static Tracer get current => _current;
+
+  static Tracer _current;
+
+  static void start() {
+    if (_current == null) {
+      _current = new Tracer();
+    }
+  }
+
+  static void stop() {
+    if (_current != null) {
+      _current.cancel();
+      _current = null;
+    }
+  }
+
+  // The tracer subscribes to all logging events.
+  StreamSubscription loggerSub = null;
+
+  // The start time for the current request.
+  Stopwatch _time;
+
+  // A list of all tracing events for thre current request.
+  List<TraceEvent> events = <TraceEvent>[];
+
+  Tracer() {
+    _time = new Stopwatch();
+    _time.start();
+    loggerSub = Logger.root.onRecord.listen((LogRecord rec) {
+      // Echo all log messages to the trace.
+      trace('${rec.level.name}: ${rec.message}');
+    });
+    reset();
+  }
+
+  void cancel() {
+    loggerSub.cancel();
+  }
+
+  void reset() {
+    _time.reset();
+    events.clear();
+  }
+
+  TraceEvent trace(String message, {Map map: null}) {
+    var event = new TraceEvent.msg(_time.elapsedMicroseconds, message, map);
+    events.add(event);
+    return event;
+  }
+}
diff --git a/runtime/observatory_2/lib/utils.dart b/runtime/observatory_2/lib/utils.dart
new file mode 100644
index 0000000..821e88f
--- /dev/null
+++ b/runtime/observatory_2/lib/utils.dart
@@ -0,0 +1,309 @@
+// Copyright (c) 2014, 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.
+
+library utils;
+
+import 'dart:async';
+import 'dart:math';
+
+enum DurationComponent {
+  Days,
+  Hours,
+  Minutes,
+  Seconds,
+  Milliseconds,
+  Microseconds
+}
+
+class Utils {
+  static String formatPercentNormalized(double x) {
+    var percent = 100.0 * x;
+    return '${percent.toStringAsFixed(2)}%';
+  }
+
+  static String formatPercent(num a, num total) {
+    return formatPercentNormalized(a / total);
+  }
+
+  static String zeroPad(int value, int pad) {
+    String prefix = "";
+    while (pad > 1) {
+      int pow10 = pow(10, pad - 1);
+      if (value < pow10) {
+        prefix = prefix + "0";
+      }
+      pad--;
+    }
+    return "${prefix}${value}";
+  }
+
+  static String formatCommaSeparated(int v) {
+    const COMMA_EVERY = 1000;
+    if (v < COMMA_EVERY) {
+      return v.toString();
+    }
+    var mod = v % COMMA_EVERY;
+    v ~/= COMMA_EVERY;
+    var r = '${zeroPad(mod, 3)}';
+    while (v > COMMA_EVERY) {
+      mod = v % COMMA_EVERY;
+      r = '${zeroPad(mod, 3)},$r';
+      v ~/= COMMA_EVERY;
+    }
+    if (v != 0) {
+      r = '$v,$r';
+    }
+    return r;
+  }
+
+  static String formatTimePrecise(double time) {
+    if (time == null) {
+      return "-";
+    }
+    const millisPerSecond = 1000;
+
+    var millis = (time * millisPerSecond).round();
+    return formatTimeMilliseconds(millis);
+  }
+
+  static String formatTimeMilliseconds(int millis) {
+    const millisPerHour = 60 * 60 * 1000;
+    const millisPerMinute = 60 * 1000;
+    const millisPerSecond = 1000;
+
+    var hours = millis ~/ millisPerHour;
+    millis = millis % millisPerHour;
+
+    var minutes = millis ~/ millisPerMinute;
+    millis = millis % millisPerMinute;
+
+    var seconds = millis ~/ millisPerSecond;
+    millis = millis % millisPerSecond;
+
+    if (hours > 0) {
+      return ("${zeroPad(hours, 2)}"
+          ":${zeroPad(minutes, 2)}"
+          ":${zeroPad(seconds, 2)}"
+          ".${zeroPad(millis, 3)}");
+    } else if (minutes > 0) {
+      return ("${zeroPad(minutes, 2)}"
+          ":${zeroPad(seconds, 2)}"
+          ".${zeroPad(millis, 3)}");
+    } else {
+      return ("${zeroPad(seconds, 2)}"
+          ".${zeroPad(millis, 3)}");
+    }
+  }
+
+  static String formatSize(bytesDynamic) {
+    int bytes = bytesDynamic.toInt();
+    int absBytes = bytes >= 0 ? bytes : -bytes;
+    const int digits = 1;
+    const int bytesPerKB = 1024;
+    const int bytesPerMB = 1024 * bytesPerKB;
+    const int bytesPerGB = 1024 * bytesPerMB;
+    const int bytesPerTB = 1024 * bytesPerGB;
+
+    if (absBytes < bytesPerKB) {
+      return "${bytes}B";
+    } else if (absBytes < bytesPerMB) {
+      return "${(bytes / bytesPerKB).toStringAsFixed(digits)}KB";
+    } else if (absBytes < bytesPerGB) {
+      return "${(bytes / bytesPerMB).toStringAsFixed(digits)}MB";
+    } else if (absBytes < bytesPerTB) {
+      return "${(bytes / bytesPerGB).toStringAsFixed(digits)}GB";
+    } else {
+      return "${(bytes / bytesPerTB).toStringAsFixed(digits)}TB";
+    }
+  }
+
+  static String formatTime(double time) {
+    if (time == null) {
+      return "-";
+    }
+    const millisPerHour = 60 * 60 * 1000;
+    const millisPerMinute = 60 * 1000;
+    const millisPerSecond = 1000;
+
+    var millis = (time * millisPerSecond).round();
+
+    var hours = millis ~/ millisPerHour;
+    millis = millis % millisPerHour;
+
+    var minutes = millis ~/ millisPerMinute;
+    millis = millis % millisPerMinute;
+
+    var seconds = millis ~/ millisPerSecond;
+
+    if (hours != 0) {
+      return '${hours}h ${minutes}m ${seconds}s';
+    }
+    if (minutes != 0) {
+      return '${minutes}m ${seconds}s';
+    }
+    return '${seconds}s';
+  }
+
+  static String formatDateTime(DateTime now) {
+    return '${now.year}-${now.month}-${now.day} '
+        '${now.hour.toString().padLeft(2)}:'
+        '${now.minute.toString().padLeft(2)}:'
+        '${now.second.toString().padLeft(2)}';
+  }
+
+  static String formatDuration(Duration duration,
+      {DurationComponent precision = DurationComponent.Microseconds,
+      String future = '',
+      String past = 'ago'}) {
+    var value = duration.inMicroseconds.abs();
+    switch (precision) {
+      case DurationComponent.Days:
+        value = (value / Duration.microsecondsPerDay).round();
+        break;
+      case DurationComponent.Hours:
+        value = (value / Duration.microsecondsPerHour).round();
+        break;
+      case DurationComponent.Minutes:
+        value = (value / Duration.microsecondsPerMinute).round();
+        break;
+      case DurationComponent.Seconds:
+        value = (value / Duration.microsecondsPerSecond).round();
+        break;
+      case DurationComponent.Milliseconds:
+        value = (value / Duration.microsecondsPerMillisecond).round();
+        break;
+      case DurationComponent.Microseconds:
+        break;
+    }
+    final components = <String>[];
+    if (duration.isNegative) {
+      if (!past.isEmpty) {
+        components.add(past);
+      }
+    } else {
+      if (!future.isEmpty) {
+        components.add(future);
+      }
+    }
+    switch (precision) {
+      case DurationComponent.Microseconds:
+        components.add('${value % Duration.microsecondsPerMillisecond}μs');
+        value = (value / Duration.microsecondsPerMillisecond).floor();
+        if (value != 0) {
+          continue Milliseconds;
+        }
+        break;
+      Milliseconds:
+      case DurationComponent.Milliseconds:
+        components.add('${value % Duration.millisecondsPerSecond}ms');
+        value = (value / Duration.millisecondsPerSecond).floor();
+        if (value != 0) {
+          continue Seconds;
+        }
+        break;
+      Seconds:
+      case DurationComponent.Seconds:
+        components.add('${value % Duration.secondsPerMinute}s');
+        value = (value / Duration.secondsPerMinute).floor();
+        ;
+        if (value != 0) {
+          continue Minutes;
+        }
+        break;
+      Minutes:
+      case DurationComponent.Minutes:
+        components.add('${value % Duration.minutesPerHour}m');
+        value = (value / Duration.minutesPerHour).floor();
+        if (value != 0) {
+          continue Hours;
+        }
+        break;
+      Hours:
+      case DurationComponent.Hours:
+        components.add('${value % Duration.hoursPerDay}h');
+        value = (value / Duration.hoursPerDay).floor();
+        if (value != 0) {
+          continue Days;
+        }
+        break;
+      Days:
+      case DurationComponent.Days:
+        components.add('${value}d');
+    }
+    return components.reversed.join(' ');
+  }
+
+  static String formatSeconds(double x) {
+    return x.toStringAsFixed(2);
+  }
+
+  static String formatMillis(double x) {
+    return x.toStringAsFixed(2);
+  }
+
+  static String formatDurationInSeconds(Duration x) =>
+      formatSeconds(x.inMicroseconds / Duration.microsecondsPerSecond);
+
+  static String formatDurationInMilliseconds(Duration x) =>
+      formatMillis(x.inMicroseconds / Duration.microsecondsPerMillisecond);
+
+  static bool runningInJavaScript() => identical(1.0, 1);
+
+  static formatStringAsLiteral(String value, [bool wasTruncated = false]) {
+    var result = <int>[];
+    result.add("'".codeUnitAt(0));
+    for (int codeUnit in value.codeUnits) {
+      if (codeUnit == '\n'.codeUnitAt(0))
+        result.addAll('\\n'.codeUnits);
+      else if (codeUnit == '\r'.codeUnitAt(0))
+        result.addAll('\\r'.codeUnits);
+      else if (codeUnit == '\f'.codeUnitAt(0))
+        result.addAll('\\f'.codeUnits);
+      else if (codeUnit == '\b'.codeUnitAt(0))
+        result.addAll('\\b'.codeUnits);
+      else if (codeUnit == '\t'.codeUnitAt(0))
+        result.addAll('\\t'.codeUnits);
+      else if (codeUnit == '\v'.codeUnitAt(0))
+        result.addAll('\\v'.codeUnits);
+      else if (codeUnit == '\$'.codeUnitAt(0))
+        result.addAll('\\\$'.codeUnits);
+      else if (codeUnit == '\\'.codeUnitAt(0))
+        result.addAll('\\\\'.codeUnits);
+      else if (codeUnit == "'".codeUnitAt(0))
+        result.addAll("'".codeUnits);
+      else if (codeUnit < 32) {
+        var escapeSequence = "\\u" + codeUnit.toRadixString(16).padLeft(4, "0");
+        result.addAll(escapeSequence.codeUnits);
+      } else
+        result.add(codeUnit);
+    }
+    if (wasTruncated) {
+      result.addAll("...".codeUnits);
+    } else {
+      result.add("'".codeUnitAt(0));
+    }
+    return new String.fromCharCodes(result);
+  }
+}
+
+/// A [Task] that can be scheduled on the Dart event queue.
+class Task {
+  Timer _timer;
+  final Function callback;
+
+  Task(this.callback);
+
+  /// Queue [this] to run on the next Dart event queue pump. Does nothing
+  /// if [this] is already queued.
+  queue() {
+    if (_timer != null) {
+      // Already scheduled.
+      return;
+    }
+    _timer = new Timer(Duration.zero, () {
+      _timer = null;
+      callback();
+    });
+  }
+}
diff --git a/runtime/observatory_2/observatory_sources.gni b/runtime/observatory_2/observatory_sources.gni
new file mode 100644
index 0000000..e78a449
--- /dev/null
+++ b/runtime/observatory_2/observatory_sources.gni
@@ -0,0 +1,284 @@
+# Copyright (c) 2017, 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.
+
+# DO NOT EDIT. This file is generated by update_sources.py in this directory.
+
+# This file contains all dart, css, and html sources for Observatory.
+observatory_sources = [
+  "lib/allocation_profile.dart",
+  "lib/app.dart",
+  "lib/cli.dart",
+  "lib/debugger.dart",
+  "lib/elements.dart",
+  "lib/event.dart",
+  "lib/models.dart",
+  "lib/object_graph.dart",
+  "lib/repositories.dart",
+  "lib/sample_profile.dart",
+  "lib/service.dart",
+  "lib/service_common.dart",
+  "lib/service_html.dart",
+  "lib/service_io.dart",
+  "lib/src/allocation_profile/allocation_profile.dart",
+  "lib/src/app/application.dart",
+  "lib/src/app/location_manager.dart",
+  "lib/src/app/notification.dart",
+  "lib/src/app/page.dart",
+  "lib/src/app/settings.dart",
+  "lib/src/app/view_model.dart",
+  "lib/src/cli/command.dart",
+  "lib/src/debugger/debugger.dart",
+  "lib/src/debugger/debugger_location.dart",
+  "lib/src/elements/allocation_profile.dart",
+  "lib/src/elements/class_allocation_profile.dart",
+  "lib/src/elements/class_instances.dart",
+  "lib/src/elements/class_ref.dart",
+  "lib/src/elements/class_tree.dart",
+  "lib/src/elements/class_view.dart",
+  "lib/src/elements/code_ref.dart",
+  "lib/src/elements/code_view.dart",
+  "lib/src/elements/containers/search_bar.dart",
+  "lib/src/elements/containers/virtual_collection.dart",
+  "lib/src/elements/containers/virtual_tree.dart",
+  "lib/src/elements/context_ref.dart",
+  "lib/src/elements/context_view.dart",
+  "lib/src/elements/cpu_profile.dart",
+  "lib/src/elements/cpu_profile/virtual_tree.dart",
+  "lib/src/elements/cpu_profile_table.dart",
+  "lib/src/elements/css/shared.css",
+  "lib/src/elements/curly_block.dart",
+  "lib/src/elements/debugger.dart",
+  "lib/src/elements/error_ref.dart",
+  "lib/src/elements/error_view.dart",
+  "lib/src/elements/eval_box.dart",
+  "lib/src/elements/field_ref.dart",
+  "lib/src/elements/field_view.dart",
+  "lib/src/elements/flag_list.dart",
+  "lib/src/elements/function_ref.dart",
+  "lib/src/elements/function_view.dart",
+  "lib/src/elements/general_error.dart",
+  "lib/src/elements/heap_map.dart",
+  "lib/src/elements/heap_snapshot.dart",
+  "lib/src/elements/helpers/any_ref.dart",
+  "lib/src/elements/helpers/custom_element.dart",
+  "lib/src/elements/helpers/nav_bar.dart",
+  "lib/src/elements/helpers/nav_menu.dart",
+  "lib/src/elements/helpers/rendering_queue.dart",
+  "lib/src/elements/helpers/rendering_scheduler.dart",
+  "lib/src/elements/helpers/uris.dart",
+  "lib/src/elements/icdata_ref.dart",
+  "lib/src/elements/icdata_view.dart",
+  "lib/src/elements/img/chromium_icon.png",
+  "lib/src/elements/img/dart_icon.png",
+  "lib/src/elements/img/isolate_icon.png",
+  "lib/src/elements/inbound_references.dart",
+  "lib/src/elements/instance_ref.dart",
+  "lib/src/elements/instance_view.dart",
+  "lib/src/elements/isolate/counter_chart.dart",
+  "lib/src/elements/isolate/location.dart",
+  "lib/src/elements/isolate/run_state.dart",
+  "lib/src/elements/isolate/shared_summary.dart",
+  "lib/src/elements/isolate/summary.dart",
+  "lib/src/elements/isolate_reconnect.dart",
+  "lib/src/elements/isolate_ref.dart",
+  "lib/src/elements/isolate_view.dart",
+  "lib/src/elements/json_view.dart",
+  "lib/src/elements/library_ref.dart",
+  "lib/src/elements/library_view.dart",
+  "lib/src/elements/local_var_descriptors_ref.dart",
+  "lib/src/elements/logging.dart",
+  "lib/src/elements/logging_list.dart",
+  "lib/src/elements/megamorphiccache_ref.dart",
+  "lib/src/elements/megamorphiccache_view.dart",
+  "lib/src/elements/metric/details.dart",
+  "lib/src/elements/metric/graph.dart",
+  "lib/src/elements/metrics.dart",
+  "lib/src/elements/native_memory_profiler.dart",
+  "lib/src/elements/nav/class_menu.dart",
+  "lib/src/elements/nav/isolate_menu.dart",
+  "lib/src/elements/nav/library_menu.dart",
+  "lib/src/elements/nav/menu_item.dart",
+  "lib/src/elements/nav/notify.dart",
+  "lib/src/elements/nav/notify_event.dart",
+  "lib/src/elements/nav/notify_exception.dart",
+  "lib/src/elements/nav/refresh.dart",
+  "lib/src/elements/nav/reload.dart",
+  "lib/src/elements/nav/top_menu.dart",
+  "lib/src/elements/nav/vm_menu.dart",
+  "lib/src/elements/object_common.dart",
+  "lib/src/elements/object_view.dart",
+  "lib/src/elements/objectpool_ref.dart",
+  "lib/src/elements/objectpool_view.dart",
+  "lib/src/elements/objectstore_view.dart",
+  "lib/src/elements/observatory_application.dart",
+  "lib/src/elements/pc_descriptors_ref.dart",
+  "lib/src/elements/persistent_handles.dart",
+  "lib/src/elements/ports.dart",
+  "lib/src/elements/process_snapshot.dart",
+  "lib/src/elements/retaining_path.dart",
+  "lib/src/elements/sample_buffer_control.dart",
+  "lib/src/elements/script_inset.dart",
+  "lib/src/elements/script_ref.dart",
+  "lib/src/elements/script_view.dart",
+  "lib/src/elements/sentinel_value.dart",
+  "lib/src/elements/sentinel_view.dart",
+  "lib/src/elements/singletargetcache_ref.dart",
+  "lib/src/elements/singletargetcache_view.dart",
+  "lib/src/elements/source_inset.dart",
+  "lib/src/elements/source_link.dart",
+  "lib/src/elements/stack_trace_tree_config.dart",
+  "lib/src/elements/strongly_reachable_instances.dart",
+  "lib/src/elements/subtypetestcache_ref.dart",
+  "lib/src/elements/subtypetestcache_view.dart",
+  "lib/src/elements/timeline/dashboard.dart",
+  "lib/src/elements/timeline_page.dart",
+  "lib/src/elements/tree_map.dart",
+  "lib/src/elements/type_arguments_ref.dart",
+  "lib/src/elements/unknown_ref.dart",
+  "lib/src/elements/unlinkedcall_ref.dart",
+  "lib/src/elements/unlinkedcall_view.dart",
+  "lib/src/elements/view_footer.dart",
+  "lib/src/elements/vm_connect.dart",
+  "lib/src/elements/vm_connect_target.dart",
+  "lib/src/elements/vm_view.dart",
+  "lib/src/models/exceptions.dart",
+  "lib/src/models/objects/allocation_profile.dart",
+  "lib/src/models/objects/breakpoint.dart",
+  "lib/src/models/objects/class.dart",
+  "lib/src/models/objects/code.dart",
+  "lib/src/models/objects/context.dart",
+  "lib/src/models/objects/error.dart",
+  "lib/src/models/objects/event.dart",
+  "lib/src/models/objects/extension_data.dart",
+  "lib/src/models/objects/field.dart",
+  "lib/src/models/objects/flag.dart",
+  "lib/src/models/objects/frame.dart",
+  "lib/src/models/objects/function.dart",
+  "lib/src/models/objects/guarded.dart",
+  "lib/src/models/objects/heap_space.dart",
+  "lib/src/models/objects/icdata.dart",
+  "lib/src/models/objects/inbound_references.dart",
+  "lib/src/models/objects/instance.dart",
+  "lib/src/models/objects/isolate.dart",
+  "lib/src/models/objects/isolate_group.dart",
+  "lib/src/models/objects/library.dart",
+  "lib/src/models/objects/local_var_descriptors.dart",
+  "lib/src/models/objects/map_association.dart",
+  "lib/src/models/objects/megamorphiccache.dart",
+  "lib/src/models/objects/metric.dart",
+  "lib/src/models/objects/notification.dart",
+  "lib/src/models/objects/object.dart",
+  "lib/src/models/objects/objectpool.dart",
+  "lib/src/models/objects/objectstore.dart",
+  "lib/src/models/objects/pc_descriptors.dart",
+  "lib/src/models/objects/persistent_handles.dart",
+  "lib/src/models/objects/ports.dart",
+  "lib/src/models/objects/retaining_path.dart",
+  "lib/src/models/objects/sample_profile.dart",
+  "lib/src/models/objects/script.dart",
+  "lib/src/models/objects/sentinel.dart",
+  "lib/src/models/objects/service.dart",
+  "lib/src/models/objects/single_target_cache.dart",
+  "lib/src/models/objects/source_location.dart",
+  "lib/src/models/objects/subtype_test_cache.dart",
+  "lib/src/models/objects/target.dart",
+  "lib/src/models/objects/thread.dart",
+  "lib/src/models/objects/timeline.dart",
+  "lib/src/models/objects/timeline_event.dart",
+  "lib/src/models/objects/type_arguments.dart",
+  "lib/src/models/objects/unknown.dart",
+  "lib/src/models/objects/unlinked_call.dart",
+  "lib/src/models/objects/vm.dart",
+  "lib/src/models/objects/zone.dart",
+  "lib/src/models/repositories/allocation_profile.dart",
+  "lib/src/models/repositories/breakpoint.dart",
+  "lib/src/models/repositories/class.dart",
+  "lib/src/models/repositories/context.dart",
+  "lib/src/models/repositories/editor.dart",
+  "lib/src/models/repositories/eval.dart",
+  "lib/src/models/repositories/event.dart",
+  "lib/src/models/repositories/field.dart",
+  "lib/src/models/repositories/flag.dart",
+  "lib/src/models/repositories/function.dart",
+  "lib/src/models/repositories/heap_snapshot.dart",
+  "lib/src/models/repositories/icdata.dart",
+  "lib/src/models/repositories/inbound_references.dart",
+  "lib/src/models/repositories/instance.dart",
+  "lib/src/models/repositories/isolate.dart",
+  "lib/src/models/repositories/isolate_group.dart",
+  "lib/src/models/repositories/library.dart",
+  "lib/src/models/repositories/megamorphiccache.dart",
+  "lib/src/models/repositories/metric.dart",
+  "lib/src/models/repositories/notification.dart",
+  "lib/src/models/repositories/object.dart",
+  "lib/src/models/repositories/objectpool.dart",
+  "lib/src/models/repositories/objectstore.dart",
+  "lib/src/models/repositories/persistent_handles.dart",
+  "lib/src/models/repositories/ports.dart",
+  "lib/src/models/repositories/reachable_size.dart",
+  "lib/src/models/repositories/retained_size.dart",
+  "lib/src/models/repositories/retaining_path.dart",
+  "lib/src/models/repositories/sample_profile.dart",
+  "lib/src/models/repositories/script.dart",
+  "lib/src/models/repositories/single_target_cache.dart",
+  "lib/src/models/repositories/strongly_reachable_instances.dart",
+  "lib/src/models/repositories/subtype_test_cache.dart",
+  "lib/src/models/repositories/target.dart",
+  "lib/src/models/repositories/timeline.dart",
+  "lib/src/models/repositories/type_arguments.dart",
+  "lib/src/models/repositories/unlinked_call.dart",
+  "lib/src/models/repositories/vm.dart",
+  "lib/src/repositories/allocation_profile.dart",
+  "lib/src/repositories/breakpoint.dart",
+  "lib/src/repositories/class.dart",
+  "lib/src/repositories/context.dart",
+  "lib/src/repositories/editor.dart",
+  "lib/src/repositories/eval.dart",
+  "lib/src/repositories/event.dart",
+  "lib/src/repositories/field.dart",
+  "lib/src/repositories/flag.dart",
+  "lib/src/repositories/function.dart",
+  "lib/src/repositories/heap_snapshot.dart",
+  "lib/src/repositories/icdata.dart",
+  "lib/src/repositories/inbound_references.dart",
+  "lib/src/repositories/instance.dart",
+  "lib/src/repositories/isolate.dart",
+  "lib/src/repositories/isolate_group.dart",
+  "lib/src/repositories/library.dart",
+  "lib/src/repositories/megamorphiccache.dart",
+  "lib/src/repositories/metric.dart",
+  "lib/src/repositories/notification.dart",
+  "lib/src/repositories/object.dart",
+  "lib/src/repositories/objectpool.dart",
+  "lib/src/repositories/objectstore.dart",
+  "lib/src/repositories/persistent_handles.dart",
+  "lib/src/repositories/ports.dart",
+  "lib/src/repositories/reachable_size.dart",
+  "lib/src/repositories/retained_size.dart",
+  "lib/src/repositories/retaining_path.dart",
+  "lib/src/repositories/sample_profile.dart",
+  "lib/src/repositories/script.dart",
+  "lib/src/repositories/settings.dart",
+  "lib/src/repositories/single_target_cache.dart",
+  "lib/src/repositories/strongly_reachable_instances.dart",
+  "lib/src/repositories/subtype_test_cache.dart",
+  "lib/src/repositories/target.dart",
+  "lib/src/repositories/timeline.dart",
+  "lib/src/repositories/timeline_base.dart",
+  "lib/src/repositories/type_arguments.dart",
+  "lib/src/repositories/unlinked_call.dart",
+  "lib/src/repositories/vm.dart",
+  "lib/src/sample_profile/sample_profile.dart",
+  "lib/src/service/object.dart",
+  "lib/tracer.dart",
+  "lib/utils.dart",
+  "web/favicon.ico",
+  "web/index.html",
+  "web/main.dart",
+  "web/third_party/trace_viewer_full.html",
+  "web/third_party/webcomponents.min.js",
+  "web/timeline.html",
+  "web/timeline.js",
+  "web/timeline_message_handler.js",
+]
diff --git a/runtime/observatory_2/pubspec.yaml b/runtime/observatory_2/pubspec.yaml
new file mode 100644
index 0000000..efd6952
--- /dev/null
+++ b/runtime/observatory_2/pubspec.yaml
@@ -0,0 +1,11 @@
+name: observatory
+environment:
+  sdk: '>=2.2.2 <3.0.0'
+
+dependencies:
+  usage: 'any'
+
+dev_dependencies:
+  build_runner: '>=1.6.2 <2.0.0'
+  build_web_compilers: '>=2.6.1 <3.0.0'
+
diff --git a/runtime/observatory_2/tests/observatory_ui_2/app_test.dart b/runtime/observatory_2/tests/observatory_ui_2/app_test.dart
new file mode 100644
index 0000000..5a841ce
--- /dev/null
+++ b/runtime/observatory_2/tests/observatory_ui_2/app_test.dart
@@ -0,0 +1,20 @@
+// Copyright (c) 2019, 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.
+
+import 'dart:html';
+import 'package:logging/logging.dart';
+import 'package:observatory_2/elements.dart';
+import 'package:stack_trace/stack_trace.dart';
+
+main() async {
+  Chain.capture(() async {
+    Logger.root.level = Level.INFO;
+    Logger.root.onRecord.listen((LogRecord rec) {
+      print('${rec.level.name}: ${rec.time}: ${rec.message}');
+    });
+    Logger.root.info('Starting Observatory');
+    document.body.children
+        .insert(0, new ObservatoryApplicationElement.created().element);
+  });
+}
diff --git a/runtime/observatory_2/tests/observatory_ui_2/observatory_ui.status b/runtime/observatory_2/tests/observatory_ui_2/observatory_ui.status
new file mode 100644
index 0000000..10260fd
--- /dev/null
+++ b/runtime/observatory_2/tests/observatory_ui_2/observatory_ui.status
@@ -0,0 +1,12 @@
+# Copyright (c) 2016, 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.
+
+[ !$browser ]
+*: SkipByDesign
+
+# custom elements are not supported on old browsers, we don't
+# intend for observatory to work on old browser versions, so
+# skipping.
+[ $runtime == ff || $runtime == ie10 || $runtime == ie11 || $runtime == safari ]
+*: SkipByDesign
diff --git a/runtime/observatory_2/tests/service_2/add_breakpoint_rpc_kernel_test.dart b/runtime/observatory_2/tests/service_2/add_breakpoint_rpc_kernel_test.dart
new file mode 100644
index 0000000..6434d69
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/add_breakpoint_rpc_kernel_test.dart
@@ -0,0 +1,138 @@
+// Copyright (c) 2017, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+import 'dart:async';
+
+const int LINE_A = 23;
+const int LINE_B = 25;
+
+int value = 0;
+
+int incValue(int amount) {
+  value += amount;
+  return amount;
+}
+
+Future testMain() async {
+  incValue(incValue(1)); // line A.
+
+  incValue(incValue(1)); // line B.
+}
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+
+  // Test future breakpoints.
+  (Isolate isolate) async {
+    var rootLib = isolate.rootLibrary;
+    await rootLib.load();
+    var script = rootLib.scripts[0];
+
+    // Future breakpoint.
+    var futureBpt1 = await isolate.addBreakpoint(script, LINE_A);
+    expect(futureBpt1.number, equals(1));
+    expect(futureBpt1.resolved, isFalse);
+    expect(await futureBpt1.location.getLine(), equals(LINE_A));
+    expect(await futureBpt1.location.getColumn(), equals(null));
+
+    // Future breakpoint with specific column.
+    var futureBpt2 = await isolate.addBreakpoint(script, LINE_A, 3);
+    expect(futureBpt2.number, equals(2));
+    expect(futureBpt2.resolved, isFalse);
+    expect(await futureBpt2.location.getLine(), equals(LINE_A));
+    expect(await futureBpt2.location.getColumn(), equals(3));
+
+    int resolvedCount =
+        await resumeAndCountResolvedBreakpointsUntilPause(isolate);
+
+    // After resolution the breakpoints have assigned line & column.
+    expect(resolvedCount, equals(2));
+    expect(futureBpt1.resolved, isTrue);
+    expect(await futureBpt1.location.getLine(), equals(LINE_A));
+    expect(await futureBpt1.location.getColumn(), equals(12));
+    expect(futureBpt2.resolved, isTrue);
+    expect(await futureBpt2.location.getLine(), equals(LINE_A));
+    expect(await futureBpt2.location.getColumn(), equals(3));
+
+    // The first breakpoint hits before value is modified.
+    Instance result = await rootLib.evaluate('value');
+    expect(result.valueAsString, equals('0'));
+
+    isolate.resume();
+    await hasStoppedAtBreakpoint(isolate);
+
+    // The second breakpoint hits after value has been modified once.
+    result = await rootLib.evaluate('value');
+    expect(result.valueAsString, equals('1'));
+
+    // Remove the breakpoints.
+    expect(
+        (await isolate.removeBreakpoint(futureBpt1)).type, equals('Success'));
+    expect(
+        (await isolate.removeBreakpoint(futureBpt2)).type, equals('Success'));
+  },
+
+  // Test resolution of column breakpoints.
+  (Isolate isolate) async {
+    var script = isolate.rootLibrary.scripts[0];
+    // Try all columns, including some columns that are too big.
+    for (int col = 1; col <= 50; col++) {
+      var bpt = await isolate.addBreakpoint(script, LINE_A, col);
+      expect(bpt.resolved, isTrue);
+      int resolvedLine = await bpt.location.getLine();
+      int resolvedCol = await bpt.location.getColumn();
+      print('$LINE_A:${col} -> ${resolvedLine}:${resolvedCol}');
+      if (col <= 12) {
+        expect(resolvedLine, equals(LINE_A));
+        expect(resolvedCol, equals(3));
+      } else if (col <= 36) {
+        expect(resolvedLine, equals(LINE_A));
+        expect(resolvedCol, equals(12));
+      } else {
+        expect(resolvedLine, equals(LINE_B));
+        expect(resolvedCol, equals(12));
+      }
+      expect((await isolate.removeBreakpoint(bpt)).type, equals('Success'));
+    }
+
+    // Make sure that a zero column is an error.
+    var caughtException = false;
+    try {
+      await isolate.addBreakpoint(script, 20, 0);
+      expect(false, isTrue, reason: 'Unreachable');
+    } on ServerRpcException catch (e) {
+      caughtException = true;
+      expect(e.code, equals(ServerRpcException.kInvalidParams));
+      expect(e.message, "addBreakpoint: invalid 'column' parameter: 0");
+    }
+    expect(caughtException, isTrue);
+  },
+];
+
+Future<int> resumeAndCountResolvedBreakpointsUntilPause(Isolate isolate) async {
+  var stream = await isolate.vm.getEventStream(VM.kDebugStream);
+  Completer completer = new Completer();
+  var subscription;
+  int resolvedCount = 0;
+  subscription = stream.listen((ServiceEvent event) async {
+    if (event.kind == ServiceEvent.kBreakpointResolved) {
+      resolvedCount++;
+    }
+    if (event.kind == ServiceEvent.kPauseBreakpoint) {
+      subscription.cancel();
+      completer.complete();
+    }
+  });
+  await isolate.resume();
+  await completer.future;
+  return resolvedCount;
+}
+
+main(args) => runIsolateTests(args, tests,
+    testeeConcurrent: testMain, pause_on_start: true);
diff --git a/runtime/observatory_2/tests/service_2/allocations_test.dart b/runtime/observatory_2/tests/service_2/allocations_test.dart
new file mode 100644
index 0000000..3fb6ce4
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/allocations_test.dart
@@ -0,0 +1,33 @@
+// Copyright (c) 2014, 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.
+
+library allocations_test;
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+
+class Foo {}
+
+// Prevent TFA from removing this static field to ensure the objects are kept
+// alive, so the allocation stats will report them via the service api.
+@pragma('vm:entry-point')
+List<Foo> foos;
+
+void script() {
+  foos = [new Foo(), new Foo(), new Foo()];
+}
+
+var tests = <IsolateTest>[
+  (Isolate isolate) async {
+    var profile = await isolate.invokeRpcNoUpgrade('_getAllocationProfile', {});
+    var classHeapStats = profile['members'].singleWhere((stats) {
+      return stats['class']['name'] == 'Foo';
+    });
+    expect(classHeapStats['instancesCurrent'], equals(3));
+    expect(classHeapStats['instancesAccumulated'], equals(3));
+  },
+];
+
+main(args) => runIsolateTests(args, tests, testeeBefore: script);
diff --git a/runtime/observatory_2/tests/service_2/async_generator_breakpoint_test.dart b/runtime/observatory_2/tests/service_2/async_generator_breakpoint_test.dart
new file mode 100644
index 0000000..1abcb9f
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/async_generator_breakpoint_test.dart
@@ -0,0 +1,96 @@
+// Copyright (c) 2015, 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.
+// VMOptions=--verbose-debug
+// VMOptions=--verbose-debug --stacktrace-every=55 --stress-async-stacks
+
+import 'dart:async';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+
+printSync() {
+  print('sync'); // Line 13
+}
+
+printAsync() async {
+  await null;
+  print('async'); // Line 18
+}
+
+printAsyncStar() async* {
+  await null;
+  print('async*'); // Line 23
+}
+
+printSyncStar() sync* {
+  print('sync*'); // Line 27
+}
+
+var testerReady = false;
+testeeDo() {
+  // We block here rather than allowing the isolate to enter the
+  // paused-on-exit state before the tester gets a chance to set
+  // the breakpoints because we need the event loop to remain
+  // operational for the async bodies to run.
+  print('testee waiting');
+  while (!testerReady);
+
+  printSync();
+  var future = printAsync();
+  var stream = printAsyncStar();
+  var iterator = printSyncStar();
+
+  print('middle'); // Line 44
+
+  future.then((v) => print(v));
+  stream.toList();
+  iterator.toList();
+}
+
+Future testAsync(Isolate isolate) async {
+  await isolate.rootLibrary.load();
+  var script = isolate.rootLibrary.scripts[0];
+
+  var bp1 = await isolate.addBreakpoint(script, 13);
+  expect(bp1, isNotNull);
+  expect(bp1 is Breakpoint, isTrue);
+  var bp2 = await isolate.addBreakpoint(script, 18);
+  expect(bp2, isNotNull);
+  expect(bp2 is Breakpoint, isTrue);
+  var bp3 = await isolate.addBreakpoint(script, 23);
+  expect(bp3, isNotNull);
+  expect(bp3 is Breakpoint, isTrue);
+  var bp4 = await isolate.addBreakpoint(script, 27);
+  expect(bp4, isNotNull);
+  expect(bp4 is Breakpoint, isTrue);
+  var bp5 = await isolate.addBreakpoint(script, 44);
+  print("BP5 - $bp5");
+  expect(bp5, isNotNull);
+  expect(bp5 is Breakpoint, isTrue);
+
+  var hits = [];
+
+  isolate.rootLibrary.evaluate('testerReady = true').then((result) {
+    print(result);
+    expect((result as Instance).valueAsString, equals('true'));
+  });
+
+  var stream = await isolate.vm.getEventStream(VM.kDebugStream);
+  await for (ServiceEvent event in stream) {
+    if (event.kind == ServiceEvent.kPauseBreakpoint) {
+      var bp = event.breakpoint;
+      print('Hit $bp');
+      hits.add(bp);
+      await isolate.resume();
+
+      if (hits.length == 5) break;
+    }
+  }
+
+  expect(hits, equals([bp1, bp5, bp4, bp2, bp3]));
+}
+
+var tests = <IsolateTest>[testAsync];
+
+main(args) => runIsolateTests(args, tests, testeeConcurrent: testeeDo);
diff --git a/runtime/observatory_2/tests/service_2/async_next_regession_18877_test.dart b/runtime/observatory_2/tests/service_2/async_next_regession_18877_test.dart
new file mode 100644
index 0000000..8b56841
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/async_next_regession_18877_test.dart
@@ -0,0 +1,53 @@
+// Copyright (c) 2018, 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.
+// VMOptions=--verbose_debug
+
+import 'dart:developer';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+const int LINE_A = 23;
+const int LINE_B = 24;
+const int LINE_C = 25;
+
+foo() async {}
+
+doAsync(stop) async {
+  // Flutter issue 18877:
+  // If a closure is defined in the context of an async method, stepping over
+  // an await causes the implicit breakpoint to be set for that closure instead
+  // of the async_op, resulting in the debugger falling through.
+  final baz = () => print('doAsync($stop) done!');
+  if (stop) debugger();
+  await foo(); // Line A.
+  await foo(); // Line B.
+  await foo(); // Line C.
+  baz();
+  return null;
+}
+
+testMain() {
+  // With two runs of doAsync floating around, async step should only cause
+  // us to stop in the run we started in.
+  doAsync(false);
+  doAsync(true);
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_A),
+  stepOver, // foo()
+  stoppedAtLine(LINE_A),
+  asyncNext,
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_B),
+  stepOver, // foo()
+  stoppedAtLine(LINE_B),
+  asyncNext,
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_C),
+  resumeIsolate,
+];
+
+main(args) => runIsolateTests(args, tests, testeeConcurrent: testMain);
diff --git a/runtime/observatory_2/tests/service_2/async_next_test.dart b/runtime/observatory_2/tests/service_2/async_next_test.dart
new file mode 100644
index 0000000..f717e0a
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/async_next_test.dart
@@ -0,0 +1,45 @@
+// Copyright (c) 2015, 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.
+// VMOptions=--verbose_debug
+
+import 'dart:developer';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+const int LINE_A = 18;
+const int LINE_B = 19;
+const int LINE_C = 20;
+
+foo() async {}
+
+doAsync(stop) async {
+  if (stop) debugger();
+  await foo(); // Line A.
+  await foo(); // Line B.
+  await foo(); // Line C.
+  return null;
+}
+
+testMain() {
+  // With two runs of doAsync floating around, async step should only cause
+  // us to stop in the run we started in.
+  doAsync(false);
+  doAsync(true);
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_A),
+  stepOver, // foo()
+  asyncNext,
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_B),
+  stepOver, // foo()
+  asyncNext,
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_C),
+  resumeIsolate,
+];
+
+main(args) => runIsolateTests(args, tests, testeeConcurrent: testMain);
diff --git a/runtime/observatory_2/tests/service_2/async_scope_test.dart b/runtime/observatory_2/tests/service_2/async_scope_test.dart
new file mode 100644
index 0000000..4e6815b
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/async_scope_test.dart
@@ -0,0 +1,72 @@
+// Copyright (c) 2015, 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.
+
+import 'dart:async';
+import 'dart:developer';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+const int LINE_A = 20;
+const int LINE_B = 26;
+
+foo() {}
+
+doAsync(param1) async {
+  var local1 = param1 + 1;
+  foo(); // Line A.
+  await local1;
+}
+
+doAsyncStar(param2) async* {
+  var local2 = param2 + 1;
+  foo(); // Line B.
+  yield local2;
+}
+
+testeeDo() {
+  debugger();
+
+  doAsync(1).then((_) {
+    doAsyncStar(1).listen((_) {});
+  });
+}
+
+Future checkAsyncVarDescriptors(Isolate isolate) async {
+  ServiceMap stack = await isolate.getStack();
+  expect(stack.type, equals('Stack'));
+  expect(stack['frames'].length, greaterThanOrEqualTo(1));
+  Frame frame = stack['frames'][0];
+  var vars = frame.variables.map((v) => v['name']).join(' ');
+  expect(vars, equals('param1 local1')); // no :async_op et al
+}
+
+Future checkAsyncStarVarDescriptors(Isolate isolate) async {
+  ServiceMap stack = await isolate.getStack();
+  expect(stack.type, equals('Stack'));
+  expect(stack['frames'].length, greaterThanOrEqualTo(1));
+  Frame frame = stack['frames'][0];
+  var vars = frame.variables.map((v) => v['name']).join(' ');
+  expect(vars, equals('param2 local2')); // no :async_op et al
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint, // debugger()
+  setBreakpointAtLine(LINE_A),
+  setBreakpointAtLine(LINE_B),
+  resumeIsolate,
+
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_A),
+  checkAsyncVarDescriptors,
+  resumeIsolate,
+
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_B),
+  checkAsyncStarVarDescriptors,
+  resumeIsolate,
+];
+
+main(args) => runIsolateTests(args, tests, testeeConcurrent: testeeDo);
diff --git a/runtime/observatory_2/tests/service_2/async_single_step_exception_test.dart b/runtime/observatory_2/tests/service_2/async_single_step_exception_test.dart
new file mode 100644
index 0000000..fdc490e
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/async_single_step_exception_test.dart
@@ -0,0 +1,83 @@
+// Copyright (c) 2017, 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.
+// VMOptions=--async-debugger --verbose-debug --no-causal-async-stacks --lazy-async-stacks
+// VMOptions=--async-debugger --verbose-debug --causal-async-stacks --no-lazy-async-stacks
+
+import 'dart:developer';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+const LINE_A = 20;
+const LINE_B = 21;
+const LINE_C = 27;
+const LINE_D = 29;
+const LINE_E = 32;
+const LINE_F = 35;
+const LINE_G = 37;
+
+helper() async {
+  print('helper'); // LINE_A.
+  throw 'a'; // LINE_B.
+  return null;
+}
+
+testMain() async {
+  debugger();
+  print('mmmmm'); // LINE_C.
+  try {
+    await helper(); // LINE_D.
+  } catch (e) {
+    // arrive here on error.
+    print('error: $e'); // LINE_E.
+  } finally {
+    // arrive here in both cases.
+    print('foo'); // LINE_F.
+  }
+  print('z'); // LINE_G.
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_C), // print mmmm
+  smartNext,
+
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_D), // await helper
+  stepInto,
+
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_A), // print helper
+  smartNext,
+
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_B), // throw a
+  smartNext,
+
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(23), // } (weird dispatching)
+  smartNext,
+
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_D), // await helper (weird dispatching)
+  smartNext,
+
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_E), // print(error)
+  smartNext,
+
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_E), // print(error) (weird finally dispatching)
+  smartNext,
+
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_F), // print(foo)
+  smartNext,
+
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_G), // print(z)
+  resumeIsolate
+];
+
+main(args) => runIsolateTests(args, tests,
+    testeeConcurrent: testMain, extraArgs: extraDebuggingArgs);
diff --git a/runtime/observatory_2/tests/service_2/async_single_step_into_test.dart b/runtime/observatory_2/tests/service_2/async_single_step_into_test.dart
new file mode 100644
index 0000000..8d8417f
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/async_single_step_into_test.dart
@@ -0,0 +1,47 @@
+// Copyright (c) 2017, 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.
+// VMOptions=--async-debugger --verbose-debug --no-causal-async-stacks --lazy-async-stacks
+// VMOptions=--async-debugger --verbose-debug --causal-async-stacks --no-lazy-async-stacks
+
+import 'dart:developer';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+const LINE_A = 17;
+const LINE_B = 18;
+const LINE_C = 23;
+const LINE_D = 24;
+
+helper() async {
+  print('helper'); // LINE_A.
+  print('foobar'); // LINE_B.
+}
+
+testMain() {
+  debugger();
+  print('mmmmm'); // LINE_C.
+  helper(); // LINE_D.
+  print('z');
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_C),
+  stepOver, // print.
+
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_D),
+  stepInto,
+
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_A),
+  stepOver, // print.
+
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_B),
+  resumeIsolate
+];
+
+main(args) => runIsolateTestsSynchronous(args, tests,
+    testeeConcurrent: testMain, extraArgs: extraDebuggingArgs);
diff --git a/runtime/observatory_2/tests/service_2/async_single_step_out_test.dart b/runtime/observatory_2/tests/service_2/async_single_step_out_test.dart
new file mode 100644
index 0000000..2b51e24
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/async_single_step_out_test.dart
@@ -0,0 +1,60 @@
+// Copyright (c) 2017, 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.
+// VMOptions=--async-debugger --verbose-debug --no-causal-async-stacks --lazy-async-stacks
+// VMOptions=--async-debugger --verbose-debug --causal-async-stacks --no-lazy-async-stacks
+
+import 'dart:developer';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+const LINE_A = 18;
+const LINE_B = 19;
+const LINE_C = 24;
+const LINE_D = 25;
+const LINE_E = 26;
+
+helper() async {
+  print('helper'); // LINE_A.
+  return null; // LINE_B.
+}
+
+testMain() async {
+  debugger();
+  print('mmmmm'); // LINE_C.
+  await helper(); // LINE_D.
+  print('z'); // LINE_E.
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_C), // print mmmm
+  stepOver,
+
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_D), // await helper
+  stepInto,
+
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_A), // print.
+  stepOver,
+
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_B), // return null.
+  stepInto, // exit helper via a single step.
+
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(20), // return null (weird dispatching)
+  stepInto, // exit helper via a single step.
+
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(25), // await helper (weird dispatching)
+  smartNext,
+
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_E), // arrive after the await.
+  resumeIsolate
+];
+
+main(args) => runIsolateTests(args, tests,
+    testeeConcurrent: testMain, extraArgs: extraDebuggingArgs);
diff --git a/runtime/observatory_2/tests/service_2/async_star_single_step_into_test.dart b/runtime/observatory_2/tests/service_2/async_star_single_step_into_test.dart
new file mode 100644
index 0000000..65a784d
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/async_star_single_step_into_test.dart
@@ -0,0 +1,73 @@
+// Copyright (c) 2017, 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.
+// VMOptions=--async-debugger --verbose-debug --no-causal-async-stacks --lazy-async-stacks
+// VMOptions=--async-debugger --verbose-debug --causal-async-stacks --no-lazy-async-stacks
+
+import 'dart:developer';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+const LINE_A = 19;
+const LINE_B = 20;
+const LINE_C = 24;
+const LINE_D = 27;
+const LINE_E = 33;
+const LINE_F = 34;
+
+foobar() async* {
+  yield 1; // LINE_A.
+  yield 2; // LINE_B.
+}
+
+helper() async {
+  print('helper'); // LINE_C.
+  await for (var i in foobar()) {
+    debugger();
+    print('loop'); // LINE_D.
+  }
+}
+
+testMain() {
+  debugger();
+  print('mmmmm'); // LINE_E.
+  helper(); // LINE_F.
+  print('z');
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_E),
+  stepOver, // print.
+
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_F),
+  stepInto,
+
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_C),
+  stepOver, // print.
+
+  hasStoppedAtBreakpoint,
+  stepInto,
+
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_A),
+  // Resume here to exit the generator function.
+  // TODO(johnmccutchan): Implement support for step-out of async functions.
+  resumeIsolate,
+
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_D),
+  stepOver, // print.
+
+  hasStoppedAtBreakpoint,
+  stepInto,
+
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_B),
+  resumeIsolate,
+];
+
+main(args) => runIsolateTestsSynchronous(args, tests,
+    testeeConcurrent: testMain, extraArgs: extraDebuggingArgs);
diff --git a/runtime/observatory_2/tests/service_2/async_star_step_out_test.dart b/runtime/observatory_2/tests/service_2/async_star_step_out_test.dart
new file mode 100644
index 0000000..fca9d16
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/async_star_step_out_test.dart
@@ -0,0 +1,103 @@
+// Copyright (c) 2017, 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.
+// VMOptions=--async-debugger --verbose-debug --no-causal-async-stacks --lazy-async-stacks
+// VMOptions=--async-debugger --verbose-debug --causal-async-stacks --no-lazy-async-stacks
+
+import 'dart:developer';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+const LINE_A = 22;
+const LINE_B = 23;
+const LINE_C = 27;
+const LINE_D = 30;
+const LINE_E = 37;
+const LINE_F = 38;
+const LINE_G = 39;
+const LINE_H = 28;
+const LINE_I = 32;
+
+foobar() async* {
+  yield 1; // LINE_A.
+  yield 2; // LINE_B.
+}
+
+helper() async {
+  print('helper'); // LINE_C.
+  await for (var i in foobar()) /* LINE_H */ {
+    debugger();
+    print('loop'); // LINE_D.
+  }
+  return null; // LINE_I.
+}
+
+testMain() {
+  debugger();
+  print('mmmmm'); // LINE_E.
+  helper(); // LINE_F.
+  print('z'); // LINE_G.
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_E),
+  stepOver, // print.
+
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_F),
+  stepInto,
+
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_C),
+  stepOver, // print.
+
+  hasStoppedAtBreakpoint,
+  stepInto,
+
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_A),
+  stepOut, // step out of generator.
+
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_H), // await for.
+  stepInto,
+
+  hasStoppedAtBreakpoint, // debugger().
+  stepInto,
+
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_D), // print.
+  stepInto,
+
+  hasStoppedAtBreakpoint, // await for.
+  stepInto,
+
+  hasStoppedAtBreakpoint, // back in generator.
+  stoppedAtLine(LINE_B),
+  stepOut, // step out of generator.
+
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_H), // await for.
+  stepInto,
+
+  hasStoppedAtBreakpoint, // debugger().
+  stepInto,
+
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_D), // print.
+  stepInto,
+
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_H), // await for.
+  stepInto,
+
+  hasStoppedAtBreakpoint,
+  stepOut, // step out of generator.
+
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_I), // return null.
+];
+
+main(args) => runIsolateTestsSynchronous(args, tests,
+    testeeConcurrent: testMain, extraArgs: extraDebuggingArgs);
diff --git a/runtime/observatory_2/tests/service_2/async_step_out_test.dart b/runtime/observatory_2/tests/service_2/async_step_out_test.dart
new file mode 100644
index 0000000..f22f8d8
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/async_step_out_test.dart
@@ -0,0 +1,57 @@
+// Copyright (c) 2017, 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.
+// VMOptions=--async-debugger --verbose-debug --no-causal-async-stacks --lazy-async-stacks
+// VMOptions=--async-debugger --verbose-debug --causal-async-stacks --no-lazy-async-stacks
+
+import 'dart:developer';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+const LINE_A = 19;
+const LINE_B = 20;
+const LINE_C = 21;
+const LINE_D = 26;
+const LINE_E = 27;
+const LINE_F = 28;
+
+helper() async {
+  await null; // LINE_A.
+  print('helper'); // LINE_B.
+  print('foobar'); // LINE_C.
+}
+
+testMain() async {
+  debugger();
+  print('mmmmm'); // LINE_D.
+  await helper(); // LINE_E.
+  print('z'); // LINE_F.
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_D),
+  stepOver, // print.
+
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_E),
+  stepInto,
+
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_A),
+  asyncNext,
+
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_B),
+  stepOver, // print.
+
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_C),
+  stepOut, // out of helper to awaiter testMain.
+
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_F),
+];
+
+main(args) => runIsolateTests(args, tests,
+    testeeConcurrent: testMain, extraArgs: extraDebuggingArgs);
diff --git a/runtime/observatory_2/tests/service_2/auth_token_test.dart b/runtime/observatory_2/tests/service_2/auth_token_test.dart
new file mode 100644
index 0000000..12a8836
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/auth_token_test.dart
@@ -0,0 +1,56 @@
+// Copyright (c) 2016, 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.
+
+import 'dart:async';
+import 'dart:developer';
+import 'dart:io' as io;
+import 'package:expect/expect.dart';
+import 'package:observatory_2/service_io.dart' as S;
+import 'package:test/test.dart';
+import 'test_helper.dart';
+
+Future<Null> testeeBefore() async {
+  print('testee before');
+  print(await Service.getInfo());
+  // Start the web server.
+  ServiceProtocolInfo info = await Service.controlWebServer(enable: true);
+  Expect.isNotNull(info.serverUri);
+  // Ensure that we have the auth token in the path segments.
+  Expect.isTrue(info.serverUri.pathSegments.length > 1);
+  // Sanity check the length of the auth token.
+  Expect.isTrue(info.serverUri.pathSegments[0].length > 8);
+
+  // Try connecting to the server without the auth token, it should throw
+  // an exception.
+  var port = info.serverUri.port;
+  var url = Uri.parse('http://localhost:$port');
+  var httpClient = new io.HttpClient();
+  try {
+    var request = await httpClient.getUrl(url);
+    fail('expected exception');
+  } catch (e) {
+    // Expected
+  }
+
+  // Try connecting to the server with the auth token, it should succeed.
+  try {
+    var request = await httpClient.getUrl(info.serverUri);
+  } catch (e) {
+    fail('could not connect');
+  }
+}
+
+var tests = <IsolateTest>[
+  (S.Isolate isolate) async {
+    await isolate.reload();
+    // Just getting here means that the testee enabled the service protocol
+    // web server.
+  }
+];
+
+main(args) => runIsolateTests(args, tests,
+    testeeBefore: testeeBefore,
+    // the testee is responsible for starting the
+    // web server.
+    testeeControlsServer: true);
diff --git a/runtime/observatory_2/tests/service_2/awaiter_async_stack_contents_2_test.dart b/runtime/observatory_2/tests/service_2/awaiter_async_stack_contents_2_test.dart
new file mode 100644
index 0000000..be22e8b
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/awaiter_async_stack_contents_2_test.dart
@@ -0,0 +1,79 @@
+// Copyright (c) 2018, 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.
+// VMOptions=--async-debugger --verbose-debug --no-causal-async-stacks --lazy-async-stacks
+// VMOptions=--async-debugger --verbose-debug --causal-async-stacks --no-lazy-async-stacks
+
+import 'dart:developer';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+const LINE_A = 28;
+const LINE_B = 34;
+const LINE_C = 38;
+
+notCalled() async {
+  await null;
+  await null;
+  await null;
+  await null;
+}
+
+foobar() async {
+  await null;
+  debugger();
+  print('foobar'); // LINE_A.
+}
+
+helper() async {
+  await null;
+  print('helper');
+  await foobar(); // LINE_B.
+}
+
+testMain() async {
+  helper(); // LINE_C.
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_A),
+  (Isolate isolate) async {
+    // Verify awaiter stack trace is the current frame + the awaiter.
+    ServiceMap stack = await isolate.getStack();
+    expect(stack['awaiterFrames'], isNotNull);
+    List awaiterFrames = stack['awaiterFrames'];
+    for (final v in awaiterFrames) {
+      print(v);
+    }
+
+    if (useCausalAsyncStacks) {
+      expect(awaiterFrames.length, greaterThanOrEqualTo(4));
+      // Awaiter frame.
+      expect(await awaiterFrames[0].toUserString(),
+          stringContainsInOrder(['foobar', '.dart:${LINE_A}']));
+      // Awaiter frame.
+      expect(await awaiterFrames[1].toUserString(),
+          stringContainsInOrder(['helper', '.dart:${LINE_B}']));
+      // Suspension point.
+      expect(awaiterFrames[2].kind, equals(M.FrameKind.asyncSuspensionMarker));
+      // Causal frame.
+      expect(await awaiterFrames[3].toUserString(),
+          stringContainsInOrder(['testMain', '.dart:${LINE_C}']));
+    } else {
+      expect(awaiterFrames.length, greaterThanOrEqualTo(2));
+      // Awaiter frame.
+      expect(await awaiterFrames[0].toUserString(),
+          stringContainsInOrder(['foobar', '.dart:${LINE_A}']));
+      // Awaiter frame.
+      expect(await awaiterFrames[1].toUserString(),
+          stringContainsInOrder(['helper', '.dart:${LINE_B}']));
+    }
+  },
+];
+
+main(args) => runIsolateTestsSynchronous(args, tests,
+    testeeConcurrent: testMain, extraArgs: extraDebuggingArgs);
diff --git a/runtime/observatory_2/tests/service_2/awaiter_async_stack_contents_test.dart b/runtime/observatory_2/tests/service_2/awaiter_async_stack_contents_test.dart
new file mode 100644
index 0000000..5f18945
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/awaiter_async_stack_contents_test.dart
@@ -0,0 +1,83 @@
+// Copyright (c) 2017, 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.
+// VMOptions=--async-debugger --verbose-debug --no-causal-async-stacks --lazy-async-stacks
+// VMOptions=--async-debugger --verbose-debug --causal-async-stacks --no-lazy-async-stacks
+
+import 'dart:developer';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+const LINE_C = 22;
+const LINE_A = 28;
+const LINE_B = 34;
+const LINE_D = 29;
+
+foobar() async {
+  await null;
+  debugger();
+  print('foobar'); // LINE_C.
+}
+
+helper() async {
+  await null;
+  debugger();
+  print('helper'); // LINE_A.
+  await foobar(); // LINE_D
+}
+
+testMain() {
+  debugger();
+  helper(); // LINE_B.
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_B),
+  (Isolate isolate) async {
+    ServiceMap stack = await isolate.getStack();
+    // No awaiter frames because we are in a completely synchronous stack.
+    expect(stack['awaiterFrames'], isNull);
+  },
+  resumeIsolate,
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_A),
+  resumeIsolate,
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_C),
+  (Isolate isolate) async {
+    // Verify awaiter stack trace is the current frame + the awaiter.
+    ServiceMap stack = await isolate.getStack();
+    expect(stack['awaiterFrames'], isNotNull);
+    List awaiterFrames = stack['awaiterFrames'];
+    if (useCausalAsyncStacks) {
+      expect(awaiterFrames.length, greaterThanOrEqualTo(4));
+      // Awaiter frame.
+      expect(await awaiterFrames[0].toUserString(),
+          stringContainsInOrder(['foobar', '.dart:$LINE_C']));
+      // Awaiter frame.
+      expect(await awaiterFrames[1].toUserString(),
+          stringContainsInOrder(['helper', '.dart:$LINE_D']));
+      // Suspension point.
+      expect(awaiterFrames[2].kind, equals(M.FrameKind.asyncSuspensionMarker));
+      // Causal frame.
+      expect(await awaiterFrames[3].toUserString(),
+          stringContainsInOrder(['testMain', '.dart:$LINE_B']));
+    } else {
+      expect(awaiterFrames.length, greaterThanOrEqualTo(2));
+      // Awaiter frame.
+      expect(await awaiterFrames[0].toUserString(),
+          stringContainsInOrder(['foobar', '.dart:$LINE_C']));
+      // Awaiter frame.
+      expect(await awaiterFrames[1].toUserString(),
+          stringContainsInOrder(['helper', '.dart:$LINE_D']));
+      // "helper" is not await'ed.
+    }
+  },
+];
+
+main(args) => runIsolateTestsSynchronous(args, tests,
+    testeeConcurrent: testMain, extraArgs: extraDebuggingArgs);
diff --git a/runtime/observatory_2/tests/service_2/bad_reload/v1/main.dart b/runtime/observatory_2/tests/service_2/bad_reload/v1/main.dart
new file mode 100644
index 0000000..8b3a14b
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/bad_reload/v1/main.dart
@@ -0,0 +1,12 @@
+// Copyright (c) 2017, 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.
+
+import 'dart:isolate';
+
+test() => 'apple';
+
+main() {
+  RawReceivePort keepAlive = new RawReceivePort();
+  print('spawned isolate running');
+}
diff --git a/runtime/observatory_2/tests/service_2/bad_reload/v2/main.dart b/runtime/observatory_2/tests/service_2/bad_reload/v2/main.dart
new file mode 100644
index 0000000..e698487
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/bad_reload/v2/main.dart
@@ -0,0 +1,13 @@
+// Copyright (c) 2017, 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.
+
+import 'dart:isolate';
+import 'library_isnt_here_man';
+
+test() => 'apple';
+
+main() {
+  RawReceivePort keepAlive = new RawReceivePort();
+  print('spawned isolate running');
+}
diff --git a/runtime/observatory_2/tests/service_2/bad_reload_test.dart b/runtime/observatory_2/tests/service_2/bad_reload_test.dart
new file mode 100644
index 0000000..1aad2dc
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/bad_reload_test.dart
@@ -0,0 +1,79 @@
+// Copyright (c) 2017, 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.
+
+import 'test_helper.dart';
+import 'dart:async';
+import 'dart:developer';
+import 'dart:isolate' as I;
+import 'dart:io';
+import 'service_test_common.dart';
+import 'package:observatory_2/service.dart';
+import 'package:path/path.dart' as path;
+import 'package:test/test.dart';
+
+// Chop off the file name.
+String baseDirectory = path.dirname(Platform.script.path) + '/';
+
+Uri baseUri = Platform.script.replace(path: baseDirectory);
+Uri spawnUri = baseUri.resolveUri(Uri.parse('bad_reload/v1/main.dart'));
+Uri v2Uri = baseUri.resolveUri(Uri.parse('bad_reload/v2/main.dart'));
+
+testMain() async {
+  print(baseUri);
+  debugger(); // Stop here.
+  // Spawn the child isolate.
+  I.Isolate isolate = await I.Isolate.spawnUri(spawnUri, [], null);
+  print(isolate);
+  debugger();
+}
+
+Future<String> invokeTest(Isolate isolate) async {
+  await isolate.reload();
+  Library lib = isolate.rootLibrary;
+  await lib.load();
+  Instance result = await lib.evaluate('test()');
+  expect(result.isString, isTrue);
+  return result.valueAsString;
+}
+
+var tests = <IsolateTest>[
+  // Stopped at 'debugger' statement.
+  hasStoppedAtBreakpoint,
+  // Resume the isolate into the while loop.
+  resumeIsolate,
+  // Stop at 'debugger' statement.
+  hasStoppedAtBreakpoint,
+  (Isolate mainIsolate) async {
+    // Grab the VM.
+    VM vm = mainIsolate.vm;
+    await vm.reloadIsolates();
+    expect(vm.isolates.length, 2);
+
+    // Find the spawned isolate.
+    Isolate spawnedIsolate =
+        vm.isolates.firstWhere((Isolate i) => i != mainIsolate);
+    expect(spawnedIsolate, isNotNull);
+
+    // Invoke test in v1.
+    String v1 = await invokeTest(spawnedIsolate);
+    expect(v1, 'apple');
+
+    // Reload to v2.
+    var response = await spawnedIsolate.reloadSources(
+      rootLibUri: v2Uri.toString(),
+    );
+    // Observe that it failed.
+    expect(response['success'], isFalse);
+    List notices = response['details']['notices'];
+    expect(notices.length, equals(1));
+    Map<String, dynamic> reasonForCancelling = notices[0];
+    expect(reasonForCancelling['type'], equals('ReasonForCancelling'));
+    expect(reasonForCancelling['message'], contains('library_isnt_here_man'));
+
+    String v2 = await invokeTest(spawnedIsolate);
+    expect(v2, 'apple');
+  }
+];
+
+main(args) => runIsolateTests(args, tests, testeeConcurrent: testMain);
diff --git a/runtime/observatory_2/tests/service_2/bad_web_socket_address_test.dart b/runtime/observatory_2/tests/service_2/bad_web_socket_address_test.dart
new file mode 100644
index 0000000..58a6881
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/bad_web_socket_address_test.dart
@@ -0,0 +1,28 @@
+// Copyright (c) 2014, 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.
+
+import 'package:logging/logging.dart';
+import "package:observatory_2/service_io.dart";
+import 'package:test/test.dart';
+
+void testBadWebSocket() async {
+  var vm = new WebSocketVM(new WebSocketVMTarget('ws://karatekid/ws'));
+
+  dynamic error;
+  try {
+    await vm.load();
+  } catch (e) {
+    error = e;
+  }
+  expect(error, isA<NetworkRpcException>());
+}
+
+main() async {
+  Logger.root.level = Level.INFO;
+  Logger.root.onRecord.listen((LogRecord rec) {
+    print('${rec.level.name}: ${rec.time}: ${rec.message}');
+  });
+
+  await test('bad web socket address', testBadWebSocket);
+}
diff --git a/runtime/observatory_2/tests/service_2/break_on_activation_test.dart b/runtime/observatory_2/tests/service_2/break_on_activation_test.dart
new file mode 100644
index 0000000..6b3dfb0
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/break_on_activation_test.dart
@@ -0,0 +1,205 @@
+// Copyright (c) 2015, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+
+genRepeater(value) {
+  return () => print(value);
+}
+
+genRepeaterNamed(value) {
+  return ({x, y}) => print(value);
+}
+
+var r1;
+var r2;
+var r3;
+
+var r1_named;
+var r2_named;
+var r3_named;
+
+void testeeSetup() {
+  // These closures have the same function.
+  r1 = genRepeater('r1');
+  r2 = genRepeater('r2');
+  r3 = genRepeater('r3');
+
+  // These closures have the same function.
+  r1_named = genRepeaterNamed('r1_named');
+  r2_named = genRepeaterNamed('r2_named');
+  r3_named = genRepeaterNamed('r3_named');
+}
+
+void testeeDo() {
+  r1();
+  r2();
+  r3();
+}
+
+void testeeDoNamed() {
+  r1_named(y: 'Not a closure', x: 'Not a closure');
+  r2_named(y: 'Not a closure', x: 'Not a closure');
+  r3_named(y: 'Not a closure', x: 'Not a closure');
+}
+
+var tests = <IsolateTest>[
+  (Isolate isolate) async {
+    Library rootLib = await isolate.rootLibrary.load();
+
+    var breaksHit = 0;
+
+    var subscriptionFuture =
+        isolate.vm.listenEventStream(VM.kDebugStream, (ServiceEvent event) {
+      if (event.kind == ServiceEvent.kPauseBreakpoint) {
+        print("Hit breakpoint ${event.breakpoint}");
+        breaksHit++;
+        isolate.resume();
+      }
+    });
+
+    valueOfField(String name) async {
+      var field = rootLib.variables.singleWhere((v) => v.name == name);
+      await field.load();
+      return field.staticValue as Instance;
+    }
+
+    var r1Ref = await valueOfField('r1');
+
+    var bpt1 = await isolate.addBreakOnActivation(r1Ref);
+    print("Added breakpoint $bpt1");
+    expect(bpt1 is Breakpoint, isTrue);
+    expect(breaksHit, equals(0));
+    await r1Ref.reload();
+    expect(r1Ref.activationBreakpoint, equals(bpt1));
+    print("testeeDo()");
+    var res = await rootLib.evaluate("testeeDo()");
+    expect(res is Instance, isTrue); // Not error.
+    expect(breaksHit, equals(1));
+
+    await isolate.removeBreakpoint(bpt1);
+    print("Removed breakpoint $bpt1");
+    print("testeeDo()");
+    await r1Ref.reload();
+    expect(r1Ref.activationBreakpoint, equals(null));
+    res = await rootLib.evaluate("testeeDo()");
+    expect(res is Instance, isTrue); // Not error.
+    expect(breaksHit, equals(1));
+
+    await cancelFutureSubscription(subscriptionFuture);
+  },
+  (Isolate isolate) async {
+    Library rootLib = await isolate.rootLibrary.load();
+
+    var breaksHit = 0;
+
+    var subscriptionFuture =
+        isolate.vm.listenEventStream(VM.kDebugStream, (ServiceEvent event) {
+      if (event.kind == ServiceEvent.kPauseBreakpoint) {
+        print("Hit breakpoint ${event.breakpoint}");
+        breaksHit++;
+        isolate.resume();
+      }
+    });
+
+    valueOfField(String name) async {
+      var field = rootLib.variables.singleWhere((v) => v.name == name);
+      await field.load();
+      return field.staticValue as Instance;
+    }
+
+    var r1Ref = await valueOfField('r1_named');
+
+    var bpt1 = await isolate.addBreakOnActivation(r1Ref);
+    print("Added breakpoint $bpt1");
+    expect(bpt1 is Breakpoint, isTrue);
+    expect(breaksHit, equals(0));
+    await r1Ref.reload();
+    expect(r1Ref.activationBreakpoint, equals(bpt1));
+    print("testeeDoNamed()");
+    var res = await rootLib.evaluate("testeeDoNamed()");
+    expect(res is Instance, isTrue); // Not error.
+    expect(breaksHit, equals(1));
+
+    await isolate.removeBreakpoint(bpt1);
+    print("Removed breakpoint $bpt1");
+    await r1Ref.reload();
+    expect(r1Ref.activationBreakpoint, equals(null));
+    print("testeeDoNamed()");
+    res = await rootLib.evaluate("testeeDoNamed()");
+    expect(res is Instance, isTrue); // Not error.
+    expect(breaksHit, equals(1));
+
+    await cancelFutureSubscription(subscriptionFuture);
+  },
+  (Isolate isolate) async {
+    Library rootLib = await isolate.rootLibrary.load();
+
+    var breaksHit = 0;
+
+    var subscriptionFuture =
+        isolate.vm.listenEventStream(VM.kDebugStream, (ServiceEvent event) {
+      if (event.kind == ServiceEvent.kPauseBreakpoint) {
+        print("Hit breakpoint ${event.breakpoint}");
+        breaksHit++;
+        isolate.resume();
+      }
+    });
+
+    valueOfField(String name) async {
+      var field = rootLib.variables.singleWhere((v) => v.name == name);
+      await field.load();
+      return field.staticValue as Instance;
+    }
+
+    var r1Ref = await valueOfField('r1');
+    var r2Ref = await valueOfField('r2');
+
+    var bpt1 = await isolate.addBreakOnActivation(r1Ref);
+    print("Added breakpoint $bpt1");
+    expect(bpt1 is Breakpoint, isTrue);
+    expect(breaksHit, equals(0));
+    await r1Ref.reload();
+    expect(r1Ref.activationBreakpoint, equals(bpt1));
+    print("testeeDo()");
+    var res = await rootLib.evaluate("testeeDo()");
+    expect(res is Instance, isTrue); // Not error.
+    expect(breaksHit, equals(1));
+
+    var bpt2 = await isolate.addBreakOnActivation(r2Ref);
+    print("Added breakpoint $bpt2");
+    expect(bpt2 is Breakpoint, isTrue);
+    expect(breaksHit, equals(1));
+    await r2Ref.reload();
+    expect(r2Ref.activationBreakpoint, equals(bpt2));
+    print("testeeDo()");
+    res = await rootLib.evaluate("testeeDo()");
+    expect(res is Instance, isTrue); // Not error.
+    expect(breaksHit, equals(3));
+
+    await isolate.removeBreakpoint(bpt1);
+    print("Removed breakpoint $bpt1");
+    await r1Ref.reload();
+    expect(r1Ref.activationBreakpoint, equals(null));
+    print("testeeDo()");
+    res = await rootLib.evaluate("testeeDo()");
+    expect(res is Instance, isTrue); // Not error.
+    expect(breaksHit, equals(4));
+
+    await isolate.removeBreakpoint(bpt2);
+    print("Removed breakpoint $bpt2");
+    await r2Ref.reload();
+    expect(r2Ref.activationBreakpoint, equals(null));
+    print("testeeDo()");
+    res = await rootLib.evaluate("testeeDo()");
+    expect(res is Instance, isTrue); // Not error.
+    expect(breaksHit, equals(4));
+
+    await cancelFutureSubscription(subscriptionFuture);
+  },
+];
+
+main(args) => runIsolateTests(args, tests, testeeBefore: testeeSetup);
diff --git a/runtime/observatory_2/tests/service_2/break_on_async_function_test.dart b/runtime/observatory_2/tests/service_2/break_on_async_function_test.dart
new file mode 100644
index 0000000..caa9fd6
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/break_on_async_function_test.dart
@@ -0,0 +1,45 @@
+// Copyright (c) 2019, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+import 'dart:developer';
+
+const int LINE_A = 13;
+
+Future<String> testFunction() async {
+  await new Future.delayed(new Duration(milliseconds: 1));
+  return "Done";
+}
+
+testMain() async {
+  debugger();
+  var str = await testFunction();
+  print(str);
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+
+// Add breakpoint at the entry of async function
+  (Isolate isolate) async {
+    Library rootLib = await isolate.rootLibrary.load();
+    var function =
+        rootLib.functions.singleWhere((f) => f.name == 'testFunction');
+
+    var bpt = await isolate.addBreakpointAtEntry(function);
+    expect(bpt is Breakpoint, isTrue);
+    print(bpt);
+  },
+
+  resumeIsolate,
+
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_A),
+  resumeIsolate,
+];
+
+main(args) => runIsolateTests(args, tests, testeeConcurrent: testMain);
diff --git a/runtime/observatory_2/tests/service_2/break_on_default_constructor_test.dart b/runtime/observatory_2/tests/service_2/break_on_default_constructor_test.dart
new file mode 100644
index 0000000..160db2f
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/break_on_default_constructor_test.dart
@@ -0,0 +1,76 @@
+// Copyright (c) 2017, 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.
+
+import 'dart:async';
+
+import 'package:observatory_2/debugger.dart';
+import 'package:observatory_2/service.dart' as S;
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+class Foo {}
+
+code() {
+  new Foo();
+}
+
+class TestDebugger extends Debugger {
+  TestDebugger(this.isolate, this.stack);
+
+  VM get vm => isolate.vm;
+  Isolate isolate;
+  ServiceMap stack;
+  int currentFrame = 0;
+}
+
+Future<Debugger> initDebugger(Isolate isolate) {
+  return isolate.getStack().then((stack) {
+    return new TestDebugger(isolate, stack);
+  });
+}
+
+List<String> stops = [];
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  // Load the isolate's libraries
+  (Isolate isolate) async {
+    for (var lib in isolate.libraries) {
+      await lib.load();
+    }
+  },
+
+  (Isolate isolate) async {
+    var debugger = await initDebugger(isolate);
+    var loc = await DebuggerLocation.parse(debugger, 'Foo');
+
+    if (loc.valid) {
+      if (loc.function != null) {
+        try {
+          await debugger.isolate.addBreakpointAtEntry(loc.function);
+        } on S.ServerRpcException catch (e) {
+          if (e.code == S.ServerRpcException.kCannotAddBreakpoint) {
+            // Expected
+          } else {
+            fail("Got unexpected error $e");
+          }
+        }
+      } else {
+        fail("Expected to find function");
+      }
+    } else {
+      fail("Expected to find function");
+    }
+
+    await isolate.resume();
+  }
+];
+
+main(args) {
+  runIsolateTestsSynchronous(args, tests,
+      testeeConcurrent: code, pause_on_start: true, pause_on_exit: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/break_on_function_test.dart b/runtime/observatory_2/tests/service_2/break_on_function_test.dart
new file mode 100644
index 0000000..a8d148c
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/break_on_function_test.dart
@@ -0,0 +1,55 @@
+// Copyright (c) 2015, 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.
+// VMOptions=--verbose_debug
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+import 'dart:developer';
+
+const int LINE_A = 14;
+
+testFunction(flag) {
+  // Line A.
+  if (flag) {
+    print("Yes");
+  } else {
+    print("No");
+  }
+}
+
+testMain() {
+  debugger();
+  testFunction(true);
+  testFunction(false);
+  print("Done");
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+
+// Add breakpoint
+  (Isolate isolate) async {
+    Library rootLib = await isolate.rootLibrary.load();
+    var function =
+        rootLib.functions.singleWhere((f) => f.name == 'testFunction');
+
+    var bpt = await isolate.addBreakpointAtEntry(function);
+    expect(bpt is Breakpoint, isTrue);
+    print(bpt);
+  },
+
+  resumeIsolate,
+
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_A),
+  resumeIsolate,
+
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_A),
+  resumeIsolate,
+];
+
+main(args) => runIsolateTests(args, tests, testeeConcurrent: testMain);
diff --git a/runtime/observatory_2/tests/service_2/breakpoint_async_break_test.dart b/runtime/observatory_2/tests/service_2/breakpoint_async_break_test.dart
new file mode 100644
index 0000000..a2cc75f
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/breakpoint_async_break_test.dart
@@ -0,0 +1,67 @@
+// Copyright (c) 2019, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+import 'dart:async';
+
+const int LINE = 17;
+
+// Issue: https://github.com/dart-lang/sdk/issues/36622
+Future<void> testMain() async {
+  for (int i = 0; i < 2; i++) {
+    if (i > 0) {
+      break; // breakpoint here
+    }
+    await Future.delayed(Duration(seconds: 1));
+  }
+}
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+
+  // Test future breakpoints.
+  (Isolate isolate) async {
+    var rootLib = isolate.rootLibrary;
+    await rootLib.load();
+    var script = rootLib.scripts[0];
+
+    // Future breakpoint.
+    var futureBpt = await isolate.addBreakpoint(script, LINE);
+    expect(futureBpt.number, 1);
+    expect(futureBpt.resolved, isFalse);
+    expect(await futureBpt.location.getLine(), LINE);
+    expect(await futureBpt.location.getColumn(), null);
+
+    var stream = await isolate.vm.getEventStream(VM.kDebugStream);
+    Completer completer = new Completer();
+    var subscription;
+    var resolvedCount = 0;
+    subscription = stream.listen((ServiceEvent event) async {
+      if (event.kind == ServiceEvent.kBreakpointResolved) {
+        resolvedCount++;
+      }
+      if (event.kind == ServiceEvent.kPauseBreakpoint) {
+        subscription.cancel();
+        completer.complete();
+      }
+    });
+    await isolate.resume();
+    await hasStoppedAtBreakpoint(isolate);
+
+    // After resolution the breakpoints have assigned line & column.
+    expect(resolvedCount, 1);
+    expect(futureBpt.resolved, isTrue);
+    expect(await futureBpt.location.getLine(), LINE);
+    expect(await futureBpt.location.getColumn(), 7);
+
+    // Remove the breakpoints.
+    expect((await isolate.removeBreakpoint(futureBpt)).type, 'Success');
+  },
+];
+
+main(args) => runIsolateTests(args, tests,
+    testeeConcurrent: testMain, pause_on_start: true);
diff --git a/runtime/observatory_2/tests/service_2/breakpoint_in_package_parts_class_file_uri_test.dart b/runtime/observatory_2/tests/service_2/breakpoint_in_package_parts_class_file_uri_test.dart
new file mode 100644
index 0000000..03a57f6
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/breakpoint_in_package_parts_class_file_uri_test.dart
@@ -0,0 +1,43 @@
+// Copyright (c) 2019, 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.
+
+library breakpoint_in_parts_class;
+
+import 'dart:io' show Platform;
+
+import 'package:observatory_test_package_2/has_part.dart' as hasPart;
+import 'package:path/path.dart' as path;
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+// Chop off the file name.
+String baseDirectory = path.dirname(Platform.script.path) + '/';
+Uri baseUri = Platform.script.replace(path: baseDirectory);
+Uri breakpointFile = baseUri.resolve('observatory_test_package/the_part.dart');
+const String shortFile = "the_part.dart";
+
+const int LINE = 87;
+
+code() {
+  hasPart.main();
+}
+
+List<String> stops = [];
+
+List<String> expected = [
+  "$shortFile:${LINE + 0}:5", // on 'print'
+  "$shortFile:${LINE + 1}:3" // on class ending '}'
+];
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  setBreakpointAtUriAndLine(breakpointFile.toString(), LINE),
+  runStepThroughProgramRecordingStops(stops),
+  checkRecordedStops(stops, expected)
+];
+
+main(args) {
+  runIsolateTestsSynchronous(args, tests,
+      testeeConcurrent: code, pause_on_start: true, pause_on_exit: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/breakpoint_in_package_parts_class_test.dart b/runtime/observatory_2/tests/service_2/breakpoint_in_package_parts_class_test.dart
new file mode 100644
index 0000000..8aec1ff
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/breakpoint_in_package_parts_class_test.dart
@@ -0,0 +1,36 @@
+// Copyright (c) 2019, 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.
+
+library breakpoint_in_parts_class;
+
+import 'package:observatory_test_package_2/has_part.dart' as hasPart;
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+const int LINE = 87;
+const String breakpointFile = "package:observatory_test_package_2/the_part.dart";
+const String shortFile = "the_part.dart";
+
+code() {
+  hasPart.main();
+}
+
+List<String> stops = [];
+
+List<String> expected = [
+  "$shortFile:${LINE + 0}:5", // on 'print'
+  "$shortFile:${LINE + 1}:3" // on class ending '}'
+];
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  setBreakpointAtUriAndLine(breakpointFile, LINE),
+  runStepThroughProgramRecordingStops(stops),
+  checkRecordedStops(stops, expected)
+];
+
+main(args) {
+  runIsolateTestsSynchronous(args, tests,
+      testeeConcurrent: code, pause_on_start: true, pause_on_exit: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/breakpoint_in_parts_class_part.dart b/runtime/observatory_2/tests/service_2/breakpoint_in_parts_class_part.dart
new file mode 100644
index 0000000..4855332
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/breakpoint_in_parts_class_part.dart
@@ -0,0 +1,91 @@
+// Copyright (c) 2017, 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 breakpoint_in_parts_class;
+
+void foo() {
+  print("lalala");
+}
+
+class Foo1 {
+  final foo;
+
+  Foo1(this.foo) {
+    print("hello from foo!");
+  }
+}
+
+class Foo2 {
+  final foo;
+
+  Foo2(this.foo) {
+    print("hello from foo!");
+  }
+}
+
+class Foo3 {
+  final foo;
+
+  Foo3(this.foo) {
+    print("hello from foo!");
+  }
+}
+
+class Foo4 {
+  final foo;
+
+  Foo4(this.foo) {
+    print("hello from foo!");
+  }
+}
+
+class Foo5 {
+  final foo;
+
+  Foo5(this.foo) {
+    print("hello from foo!");
+  }
+}
+
+class Foo6 {
+  final foo;
+
+  Foo6(this.foo) {
+    print("hello from foo!");
+  }
+}
+
+class Foo7 {
+  final foo;
+
+  Foo7(this.foo) {
+    print("hello from foo!");
+  }
+}
+
+class Foo8 {
+  final foo;
+
+  Foo8(this.foo) {
+    print("hello from foo!");
+  }
+}
+
+class Foo9 {
+  final foo;
+
+  Foo9(this.foo) {
+    print("hello from foo!");
+  }
+}
+
+class Foo10 {
+  final foo;
+
+  Foo10(this.foo) {
+    print("hello from foo!");
+  }
+}
+
+var foo2 = foo() as dynamic;
diff --git a/runtime/observatory_2/tests/service_2/breakpoint_in_parts_class_test.dart b/runtime/observatory_2/tests/service_2/breakpoint_in_parts_class_test.dart
new file mode 100644
index 0000000..77786a2
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/breakpoint_in_parts_class_test.dart
@@ -0,0 +1,37 @@
+// Copyright (c) 2017, 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.
+
+library breakpoint_in_parts_class;
+
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+part 'breakpoint_in_parts_class_part.dart';
+
+const int LINE = 87;
+const String file = "breakpoint_in_parts_class_part.dart";
+
+code() {
+  Foo10 foo = new Foo10("Foo!");
+  print(foo);
+}
+
+List<String> stops = [];
+
+List<String> expected = [
+  "$file:${LINE+0}:5", // on 'print'
+  "$file:${LINE+1}:3" // on class ending '}'
+];
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  setBreakpointAtUriAndLine(file, LINE),
+  runStepThroughProgramRecordingStops(stops),
+  checkRecordedStops(stops, expected)
+];
+
+main(args) {
+  runIsolateTestsSynchronous(args, tests,
+      testeeConcurrent: code, pause_on_start: true, pause_on_exit: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/breakpoint_non_debuggable_library_test.dart b/runtime/observatory_2/tests/service_2/breakpoint_non_debuggable_library_test.dart
new file mode 100644
index 0000000..c2dde98
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/breakpoint_non_debuggable_library_test.dart
@@ -0,0 +1,77 @@
+// Copyright (c) 2019, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:observatory_test_package_2/has_part.dart' as test_pkg;
+import 'package:test/test.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+const String file = 'package:observatory_test_package_2/has_part.dart';
+// print() within fooz()
+const int LINE_A = 15;
+// print() within barz()
+const int LINE_B = 11;
+
+testMain() {
+  test_pkg.fooz();
+}
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  (Isolate isolate) async {
+    // Mark 'package:observatory_test_package_2/has_part.dart' as not debuggable.
+    await isolate.reload();
+    Library has_part =
+        isolate.libraries.firstWhere((Library library) => library.uri == file);
+    await has_part.load();
+    expect(has_part.debuggable, true);
+    // SetBreakpoint before setting library to non-debuggable.
+    // Breakpoints are allowed to be set (before marking library as
+    // non-debuggable) but are not hit when running (after marking library
+    // as non-debuggable).
+    Script script =
+        has_part.scripts.firstWhere((Script script) => script.uri == file);
+    Breakpoint bpt = await isolate.addBreakpoint(script, LINE_A);
+    print("Breakpoint is $bpt");
+    expect(bpt, isNotNull);
+    expect(bpt is Breakpoint, isTrue);
+
+    // Set breakpoint and check later that this breakpoint won't be added if
+    // the library is non-debuggable.
+    bpt = await isolate.addBreakpoint(script, LINE_B);
+    print("Breakpoint is $bpt");
+    expect(bpt, isNotNull);
+    expect(bpt is Breakpoint, isTrue);
+    await script.reload();
+    // Remove breakpoint.
+    var res = await isolate.removeBreakpoint(bpt);
+    expect(res.type, 'Success');
+
+    var setDebugParams = {
+      'libraryId': has_part.id,
+      'isDebuggable': false,
+    };
+    Map<String, dynamic> result = await isolate.invokeRpcNoUpgrade(
+        'setLibraryDebuggable', setDebugParams);
+    expect(result['type'], 'Success');
+    await has_part.reload();
+    expect(has_part.debuggable, false);
+    print('$has_part is debuggable: ${has_part.debuggable}');
+
+    // Breakpoints are not allowed to set on non-debuggable libraries.
+    try {
+      await isolate.addBreakpoint(script, LINE_B);
+    } catch (e) {
+      expect(e is ServerRpcException, true);
+      expect(e.code == ServerRpcException.kCannotAddBreakpoint, true);
+      print("Set Breakpoint to non-debuggable library is not allowed");
+    }
+  },
+  resumeIsolate,
+  hasStoppedAtExit,
+];
+
+main(args) => runIsolateTests(args, tests,
+    testeeConcurrent: testMain, pause_on_start: true, pause_on_exit: true);
diff --git a/runtime/observatory_2/tests/service_2/breakpoint_on_if_null_1_test.dart b/runtime/observatory_2/tests/service_2/breakpoint_on_if_null_1_test.dart
new file mode 100644
index 0000000..2d28590
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/breakpoint_on_if_null_1_test.dart
@@ -0,0 +1,50 @@
+// Copyright (c) 2019, 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.
+
+library breakpoint_in_parts_class;
+
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+const int LINE = 18;
+const String file = "breakpoint_on_if_null_1_test.dart";
+
+code() {
+  foo(42);
+}
+
+foo(dynamic args) {
+  if (args == null) {
+    print("was null");
+  }
+  if (args != null) {
+    print("was not null");
+  }
+  if (args == 42) {
+    print("was 42!");
+  }
+}
+
+List<String> stops = [];
+
+List<String> expected = [
+  "$file:${LINE + 0}:12", // on '=='
+  "$file:${LINE + 3}:12", // on '!='
+  "$file:${LINE + 4}:5", // on 'print'
+  "$file:${LINE + 6}:12", // on '=='
+  "$file:${LINE + 7}:5", // on 'print'
+  "$file:${LINE + 9}:1", // on ending '}'
+];
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  setBreakpointAtUriAndLine(file, LINE),
+  runStepThroughProgramRecordingStops(stops),
+  checkRecordedStops(stops, expected)
+];
+
+main(args) {
+  runIsolateTestsSynchronous(args, tests,
+      testeeConcurrent: code, pause_on_start: true, pause_on_exit: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/breakpoint_on_if_null_2_test.dart b/runtime/observatory_2/tests/service_2/breakpoint_on_if_null_2_test.dart
new file mode 100644
index 0000000..5eb37e5
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/breakpoint_on_if_null_2_test.dart
@@ -0,0 +1,53 @@
+// Copyright (c) 2019, 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.
+
+library breakpoint_in_parts_class;
+
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+const int LINE = 21;
+const String file = "breakpoint_on_if_null_2_test.dart";
+
+dynamic compareWithMe = 43;
+
+code() {
+  compareWithMe = null;
+  foo(42);
+}
+
+foo(dynamic args) {
+  if (args == compareWithMe) {
+    print("was null");
+  }
+  if (args != compareWithMe) {
+    print("was not null");
+  }
+  if (args == 42) {
+    print("was 42!");
+  }
+}
+
+List<String> stops = [];
+
+List<String> expected = [
+  "$file:${LINE + 0}:12", // on '=='
+  "$file:${LINE + 3}:12", // on '!='
+  "$file:${LINE + 4}:5", // on 'print'
+  "$file:${LINE + 6}:12", // on '=='
+  "$file:${LINE + 7}:5", // on 'print'
+  "$file:${LINE + 9}:1", // on ending '}'
+];
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  setBreakpointAtUriAndLine(file, LINE),
+  runStepThroughProgramRecordingStops(stops),
+  checkRecordedStops(stops, expected)
+];
+
+main(args) {
+  runIsolateTestsSynchronous(args, tests,
+      testeeConcurrent: code, pause_on_start: true, pause_on_exit: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/breakpoint_on_if_null_3_test.dart b/runtime/observatory_2/tests/service_2/breakpoint_on_if_null_3_test.dart
new file mode 100644
index 0000000..a4c1fde
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/breakpoint_on_if_null_3_test.dart
@@ -0,0 +1,51 @@
+// Copyright (c) 2019, 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.
+
+library breakpoint_in_parts_class;
+
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+const int LINE = 17;
+const String file = "breakpoint_on_if_null_3_test.dart";
+
+code() {
+  foo(42);
+}
+
+foo(dynamic args) {
+  if (args == null) {
+    print("was null");
+  }
+  if (args != null) {
+    print("was not null");
+  }
+  if (args == 42) {
+    print("was 42!");
+  }
+}
+
+List<String> stops = [];
+
+List<String> expected = [
+  "$file:${LINE + 0}:13", // on 'args'
+  "$file:${LINE + 1}:12", // on '=='
+  "$file:${LINE + 4}:12", // on '!='
+  "$file:${LINE + 5}:5", // on 'print'
+  "$file:${LINE + 7}:12", // on '=='
+  "$file:${LINE + 8}:5", // on 'print'
+  "$file:${LINE + 10}:1", // on ending '}'
+];
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  setBreakpointAtUriAndLine(file, LINE),
+  runStepThroughProgramRecordingStops(stops),
+  checkRecordedStops(stops, expected)
+];
+
+main(args) {
+  runIsolateTestsSynchronous(args, tests,
+      testeeConcurrent: code, pause_on_start: true, pause_on_exit: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/breakpoint_on_if_null_4_test.dart b/runtime/observatory_2/tests/service_2/breakpoint_on_if_null_4_test.dart
new file mode 100644
index 0000000..7b9ffe1
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/breakpoint_on_if_null_4_test.dart
@@ -0,0 +1,54 @@
+// Copyright (c) 2019, 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.
+
+library breakpoint_in_parts_class;
+
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+const int LINE = 20;
+const String file = "breakpoint_on_if_null_4_test.dart";
+
+dynamic compareWithMe = 43;
+
+code() {
+  compareWithMe = null;
+  foo(42);
+}
+
+foo(dynamic args) {
+  if (args == compareWithMe) {
+    print("was null");
+  }
+  if (args != compareWithMe) {
+    print("was not null");
+  }
+  if (args == 42) {
+    print("was 42!");
+  }
+}
+
+List<String> stops = [];
+
+List<String> expected = [
+  "$file:${LINE + 0}:13", // on 'args'
+  "$file:${LINE + 1}:12", // on '=='
+  "$file:${LINE + 4}:12", // on '!='
+  "$file:${LINE + 5}:5", // on 'print'
+  "$file:${LINE + 7}:12", // on '=='
+  "$file:${LINE + 8}:5", // on 'print'
+  "$file:${LINE + 10}:1", // on ending '}'
+];
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  setBreakpointAtUriAndLine(file, LINE),
+  runStepThroughProgramRecordingStops(stops),
+  checkRecordedStops(stops, expected)
+];
+
+main(args) {
+  runIsolateTestsSynchronous(args, tests,
+      testeeConcurrent: code, pause_on_start: true, pause_on_exit: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/breakpoint_partfile_test.dart b/runtime/observatory_2/tests/service_2/breakpoint_partfile_test.dart
new file mode 100644
index 0000000..e0ad0fa
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/breakpoint_partfile_test.dart
@@ -0,0 +1,37 @@
+// Copyright (c) 2019, 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.
+
+library breakpoint_in_parts_class;
+
+import 'package:observatory_test_package_2/has_part.dart' as hasPart;
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+const int LINE = 8;
+const String breakpointFile =
+    "package:observatory_test_package_2/the_part_2.dart";
+const String shortFile = "the_part_2.dart";
+
+code() {
+  hasPart.bar();
+}
+
+List<String> stops = [];
+
+List<String> expected = [
+  "$shortFile:${LINE + 0}:3", // on 'print'
+  "$shortFile:${LINE + 1}:1" // on class ending '}'
+];
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  setBreakpointAtUriAndLine(breakpointFile, LINE),
+  runStepThroughProgramRecordingStops(stops),
+  checkRecordedStops(stops, expected)
+];
+
+main(args) {
+  runIsolateTestsSynchronous(args, tests,
+      testeeConcurrent: code, pause_on_start: true, pause_on_exit: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/breakpoint_two_args_checked_test.dart b/runtime/observatory_2/tests/service_2/breakpoint_two_args_checked_test.dart
new file mode 100644
index 0000000..b05b736
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/breakpoint_two_args_checked_test.dart
@@ -0,0 +1,70 @@
+// Copyright (c) 2016, 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.
+// VMOptions=--verbose_debug
+
+// This test was mostly interesting for DBC, which needed to patch two bytecodes
+// to create a breakpoint for fast Smi ops.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+import 'dart:developer';
+
+const int LINE_A = 26;
+const int LINE_B = 27;
+const int LINE_C = 28;
+
+class NotGeneric {}
+
+testeeMain() {
+  var x = new List(1);
+  var y = 7;
+  debugger();
+  print("Statement");
+  x[0] = 3; // Line A.
+  x is NotGeneric; // Line B.
+  y & 4; // Line C.
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+
+// Add breakpoints.
+  (Isolate isolate) async {
+    Library rootLib = await isolate.rootLibrary.load();
+    var script = rootLib.scripts[0];
+
+    var bpt1 = await isolate.addBreakpoint(script, LINE_A);
+    print(bpt1);
+    expect(bpt1.resolved, isTrue);
+    expect(await bpt1.location.getLine(), equals(LINE_A));
+
+    var bpt2 = await isolate.addBreakpoint(script, LINE_B);
+    print(bpt2);
+    expect(bpt2.resolved, isTrue);
+    expect(await bpt2.location.getLine(), equals(LINE_B));
+
+    var bpt3 = await isolate.addBreakpoint(script, LINE_C);
+    print(bpt3);
+    expect(bpt3.resolved, isTrue);
+    expect(await bpt3.location.getLine(), equals(LINE_C));
+  },
+
+  resumeIsolate,
+
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_A),
+  resumeIsolate,
+
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_B),
+  resumeIsolate,
+
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_C),
+  resumeIsolate,
+];
+
+main(args) => runIsolateTests(args, tests, testeeConcurrent: testeeMain);
diff --git a/runtime/observatory_2/tests/service_2/breakpoints_with_mixin_lib1.dart b/runtime/observatory_2/tests/service_2/breakpoints_with_mixin_lib1.dart
new file mode 100644
index 0000000..3c43715
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/breakpoints_with_mixin_lib1.dart
@@ -0,0 +1,7 @@
+// Copyright (c) 2020, 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.
+
+import "breakpoints_with_mixin_lib3.dart";
+
+class Test1 extends Object with Foo {}
diff --git a/runtime/observatory_2/tests/service_2/breakpoints_with_mixin_lib2.dart b/runtime/observatory_2/tests/service_2/breakpoints_with_mixin_lib2.dart
new file mode 100644
index 0000000..c054176
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/breakpoints_with_mixin_lib2.dart
@@ -0,0 +1,7 @@
+// Copyright (c) 2020, 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.
+
+import "breakpoints_with_mixin_lib3.dart";
+
+class Test2 extends Object with Foo {}
diff --git a/runtime/observatory_2/tests/service_2/breakpoints_with_mixin_lib3.dart b/runtime/observatory_2/tests/service_2/breakpoints_with_mixin_lib3.dart
new file mode 100644
index 0000000..a7329de
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/breakpoints_with_mixin_lib3.dart
@@ -0,0 +1,15 @@
+// Copyright (c) 2020, 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.
+
+class Foo {
+  foo() {
+    print("I should be breakable!");
+  }
+}
+
+class Bar {
+  bar() {
+    print("I should be breakable too!");
+  }
+}
diff --git a/runtime/observatory_2/tests/service_2/breakpoints_with_mixin_test.dart b/runtime/observatory_2/tests/service_2/breakpoints_with_mixin_test.dart
new file mode 100644
index 0000000..05830b3
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/breakpoints_with_mixin_test.dart
@@ -0,0 +1,57 @@
+// Copyright (c) 2020, 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.
+
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+import "breakpoints_with_mixin_lib1.dart";
+import "breakpoints_with_mixin_lib2.dart";
+import "breakpoints_with_mixin_lib3.dart";
+
+const String testFilename = "breakpoints_with_mixin_test.dart";
+const int testCodeLineStart = 18;
+const String lib3Filename = "breakpoints_with_mixin_lib3.dart";
+const int lib3Bp1 = 7;
+const int lib3Bp2 = 13;
+
+void code() {
+  Test1 test1 = new Test1();
+  test1.foo();
+  Test2 test2 = new Test2();
+  test2.foo();
+  Foo foo = new Foo();
+  foo.foo();
+  Bar bar = new Bar();
+  bar.bar();
+  test1.foo();
+  test2.foo();
+  foo.foo();
+  bar.bar();
+}
+
+List<String> stops = [];
+
+List<String> expected = [
+  "$lib3Filename:$lib3Bp1:5 ($testFilename:${testCodeLineStart + 2}:9)",
+  "$lib3Filename:$lib3Bp1:5 ($testFilename:${testCodeLineStart + 4}:9)",
+  "$lib3Filename:$lib3Bp1:5 ($testFilename:${testCodeLineStart + 6}:7)",
+  "$lib3Filename:$lib3Bp2:5 ($testFilename:${testCodeLineStart + 8}:7)",
+  "$lib3Filename:$lib3Bp1:5 ($testFilename:${testCodeLineStart + 9}:9)",
+  "$lib3Filename:$lib3Bp1:5 ($testFilename:${testCodeLineStart + 10}:9)",
+  "$lib3Filename:$lib3Bp1:5 ($testFilename:${testCodeLineStart + 11}:7)",
+  "$lib3Filename:$lib3Bp2:5 ($testFilename:${testCodeLineStart + 12}:7)",
+];
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  setBreakpointAtUriAndLine(lib3Filename, lib3Bp1),
+  setBreakpointAtUriAndLine(lib3Filename, lib3Bp2),
+  resumeProgramRecordingStops(stops, true),
+  checkRecordedStops(stops, expected)
+];
+
+main(args) {
+  runIsolateTestsSynchronous(args, tests,
+      testeeConcurrent: code, pause_on_start: true, pause_on_exit: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/caching_test.dart b/runtime/observatory_2/tests/service_2/caching_test.dart
new file mode 100644
index 0000000..f89a2c0
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/caching_test.dart
@@ -0,0 +1,24 @@
+// Copyright (c) 2015, 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.
+
+// If caching is working properly, the coverage data will go into the same
+// Script object from which we requested coverage data, instead of a new
+// Script object.
+
+library caching_test;
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+
+var tests = <IsolateTest>[
+  (Isolate isolate) async {
+    Library lib = await isolate.rootLibrary.load();
+    Script script = await lib.scripts.single.load();
+    Script script2 = await isolate.getObject(script.id);
+    expect(identical(script, script2), isTrue);
+  },
+];
+
+main(args) => runIsolateTests(args, tests);
diff --git a/runtime/observatory_2/tests/service_2/capture_stdio_test.dart b/runtime/observatory_2/tests/service_2/capture_stdio_test.dart
new file mode 100644
index 0000000..dc81e5a
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/capture_stdio_test.dart
@@ -0,0 +1,80 @@
+// Copyright (c) 2015, 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.
+
+import 'dart:async';
+import 'dart:developer';
+import 'dart:io';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+void test() {
+  debugger();
+  stdout.write('stdout');
+
+  debugger();
+  print('print');
+
+  debugger();
+  stderr.write('stderr');
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  (Isolate isolate) async {
+    Completer completer = new Completer();
+    var stdoutSub;
+    stdoutSub = await isolate.vm.listenEventStream(VM.kStdoutStream,
+        (ServiceEvent event) {
+      expect(event.kind, equals('WriteEvent'));
+      expect(event.bytesAsString, equals('stdout'));
+      stdoutSub.cancel().then((_) {
+        completer.complete();
+      });
+    });
+    await isolate.resume();
+    await completer.future;
+  },
+  hasStoppedAtBreakpoint,
+  (Isolate isolate) async {
+    Completer completer = new Completer();
+    var stdoutSub;
+    int eventNumber = 1;
+    stdoutSub = await isolate.vm.listenEventStream(VM.kStdoutStream,
+        (ServiceEvent event) {
+      expect(event.kind, equals('WriteEvent'));
+      if (eventNumber == 1) {
+        expect(event.bytesAsString, equals('print'));
+      } else if (eventNumber == 2) {
+        expect(event.bytesAsString, equals('\n'));
+        stdoutSub.cancel().then((_) {
+          completer.complete();
+        });
+      } else {
+        expect(true, false);
+      }
+      eventNumber++;
+    });
+    await isolate.resume();
+    await completer.future;
+  },
+  hasStoppedAtBreakpoint,
+  (Isolate isolate) async {
+    Completer completer = new Completer();
+    var stderrSub;
+    stderrSub = await isolate.vm.listenEventStream(VM.kStderrStream,
+        (ServiceEvent event) {
+      expect(event.kind, equals('WriteEvent'));
+      expect(event.bytesAsString, equals('stderr'));
+      stderrSub.cancel().then((_) {
+        completer.complete();
+      });
+    });
+    await isolate.resume();
+    await completer.future;
+  },
+];
+
+main(args) => runIsolateTests(args, tests, testeeConcurrent: test);
diff --git a/runtime/observatory_2/tests/service_2/causal_async_stack_contents_test.dart b/runtime/observatory_2/tests/service_2/causal_async_stack_contents_test.dart
new file mode 100644
index 0000000..067887d
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/causal_async_stack_contents_test.dart
@@ -0,0 +1,85 @@
+// Copyright (c) 2017, 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.
+// VMOptions=--no-causal-async-stacks --lazy-async-stacks --verbose_debug
+// VMOptions=--causal-async-stacks --no-lazy-async-stacks --verbose_debug
+
+import 'dart:developer';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+const LINE_C = 20;
+const LINE_A = 26;
+const LINE_B = 32;
+
+foobar() {
+  debugger();
+  print('foobar'); // LINE_C.
+}
+
+helper() async {
+  await 0; // force async gap
+  debugger();
+  print('helper'); // LINE_A.
+  foobar();
+}
+
+testMain() {
+  debugger();
+  helper(); // LINE_B.
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_B),
+  (Isolate isolate) async {
+    ServiceMap stack = await isolate.getStack();
+    // No causal frames because we are in a completely synchronous stack.
+    expect(stack['asyncCausalFrames'], isNull);
+  },
+  resumeIsolate,
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_A),
+  (Isolate isolate) async {
+    ServiceMap stack = await isolate.getStack();
+    // Has causal frames (we are inside an async function)
+    expect(stack['asyncCausalFrames'], isNotNull);
+    var asyncStack = stack['asyncCausalFrames'];
+    if (useCausalAsyncStacks) {
+      expect(asyncStack[0].toString(), contains('helper'));
+      expect(asyncStack[1].kind, equals(M.FrameKind.asyncSuspensionMarker));
+      expect(asyncStack[2].toString(), contains('testMain'));
+    } else {
+      expect(asyncStack[0].toString(), contains('helper'));
+      // "helper" is not await'ed.
+    }
+  },
+  resumeIsolate,
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_C),
+  (Isolate isolate) async {
+    ServiceMap stack = await isolate.getStack();
+    // Has causal frames (we are inside a function called by an async function)
+    expect(stack['asyncCausalFrames'], isNotNull);
+    var asyncStack = stack['asyncCausalFrames'];
+    if (useCausalAsyncStacks) {
+      expect(asyncStack[0].toString(), contains('foobar'));
+      expect(asyncStack[1].toString(), contains('helper'));
+      expect(asyncStack[2].kind, equals(M.FrameKind.asyncSuspensionMarker));
+      expect(asyncStack[3].toString(), contains('testMain'));
+      expect(await asyncStack[0].location.toUserString(), contains('.dart:20'));
+      expect(await asyncStack[1].location.toUserString(), contains('.dart:27'));
+      expect(await asyncStack[3].location.toUserString(), contains('.dart:32'));
+    } else {
+      expect(asyncStack[0].toString(), contains('foobar'));
+      expect(asyncStack[1].toString(), contains('helper'));
+      // "helper" is not await'ed.
+    }
+  },
+];
+
+main(args) => runIsolateTestsSynchronous(args, tests,
+    testeeConcurrent: testMain, extraArgs: extraDebuggingArgs);
diff --git a/runtime/observatory_2/tests/service_2/causal_async_stack_presence_test.dart b/runtime/observatory_2/tests/service_2/causal_async_stack_presence_test.dart
new file mode 100644
index 0000000..c37359e
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/causal_async_stack_presence_test.dart
@@ -0,0 +1,60 @@
+// Copyright (c) 2017, 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.
+// VMOptions=--no-causal-async-stacks --lazy-async-stacks --verbose_debug
+// VMOptions=--causal-async-stacks --no-lazy-async-stacks --verbose_debug
+
+import 'dart:developer';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+const LINE_C = 19;
+const LINE_A = 24;
+const LINE_B = 30;
+
+foobar() {
+  debugger();
+  print('foobar'); // LINE_C.
+}
+
+helper() async {
+  debugger();
+  print('helper'); // LINE_A.
+  foobar();
+}
+
+testMain() {
+  debugger();
+  helper(); // LINE_B.
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_B),
+  (Isolate isolate) async {
+    ServiceMap stack = await isolate.getStack();
+    // No causal frames because we are in a completely synchronous stack.
+    expect(stack['asyncCausalFrames'], isNull);
+  },
+  resumeIsolate,
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_A),
+  (Isolate isolate) async {
+    ServiceMap stack = await isolate.getStack();
+    // Has causal frames (we are inside an async function)
+    expect(stack['asyncCausalFrames'], isNotNull);
+  },
+  resumeIsolate,
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_C),
+  (Isolate isolate) async {
+    ServiceMap stack = await isolate.getStack();
+    // Has causal frames (we are inside a function called by an async function)
+    expect(stack['asyncCausalFrames'], isNotNull);
+  },
+];
+
+main(args) => runIsolateTestsSynchronous(args, tests,
+    testeeConcurrent: testMain, extraArgs: extraDebuggingArgs);
diff --git a/runtime/observatory_2/tests/service_2/causal_async_star_stack_contents_test.dart b/runtime/observatory_2/tests/service_2/causal_async_star_stack_contents_test.dart
new file mode 100644
index 0000000..4474624
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/causal_async_star_stack_contents_test.dart
@@ -0,0 +1,106 @@
+// Copyright (c) 2017, 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.
+// VMOptions=--no-causal-async-stacks --lazy-async-stacks --verbose_debug
+// VMOptions=--causal-async-stacks --no-lazy-async-stacks --verbose_debug
+
+import 'dart:developer';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+const LINE_A = 29;
+const LINE_B = 21;
+const LINE_C = 23;
+
+foobar() async* {
+  await 0; // force async gap
+  debugger();
+  yield 1; // LINE_B.
+  debugger();
+  yield 2; // LINE_C.
+}
+
+helper() async {
+  await 0; // force async gap
+  debugger();
+  print('helper'); // LINE_A.
+  await for (var i in foobar()) {
+    print('helper $i');
+  }
+}
+
+testMain() {
+  helper();
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_A),
+  (Isolate isolate) async {
+    ServiceMap stack = await isolate.getStack();
+    // No causal frames because we are in a completely synchronous stack.
+    expect(stack['asyncCausalFrames'], isNotNull);
+    var asyncStack = stack['asyncCausalFrames'];
+    if (useCausalAsyncStacks) {
+      expect(asyncStack.length, greaterThanOrEqualTo(3));
+      expect(asyncStack[0].toString(), contains('helper'));
+      expect(asyncStack[1].kind, equals(M.FrameKind.asyncSuspensionMarker));
+      expect(asyncStack[2].toString(), contains('testMain'));
+    } else {
+      expect(asyncStack.length, greaterThanOrEqualTo(1));
+      expect(asyncStack[0].toString(), contains('helper'));
+      // helper isn't awaited.
+    }
+  },
+  resumeIsolate,
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_B),
+  (Isolate isolate) async {
+    ServiceMap stack = await isolate.getStack();
+    // Has causal frames (we are inside an async function)
+    expect(stack['asyncCausalFrames'], isNotNull);
+    var asyncStack = stack['asyncCausalFrames'];
+    expect(asyncStack.length, greaterThanOrEqualTo(3));
+    expect(asyncStack[0].toString(), contains('foobar'));
+    expect(asyncStack[1].kind, equals(M.FrameKind.asyncSuspensionMarker));
+    expect(asyncStack[2].toString(), contains('helper'));
+    expect(asyncStack[3].kind, equals(M.FrameKind.asyncSuspensionMarker));
+    if (useCausalAsyncStacks) {
+      // helper isn't awaited.
+      expect(asyncStack[4].toString(), contains('testMain'));
+    }
+  },
+  resumeIsolate,
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_C),
+  (Isolate isolate) async {
+    ServiceMap stack = await isolate.getStack();
+    // Has causal frames (we are inside a function called by an async function)
+    expect(stack['asyncCausalFrames'], isNotNull);
+    var asyncStack = stack['asyncCausalFrames'];
+    print('async:');
+    await printFrames(asyncStack);
+    print('sync:');
+    await printFrames(stack['frames']);
+    expect(asyncStack.length, greaterThanOrEqualTo(4));
+    expect(asyncStack[0].toString(), contains('foobar'));
+    expect(
+        await asyncStack[0].location.toUserString(), contains('.dart:$LINE_C'));
+    expect(asyncStack[1].kind, equals(M.FrameKind.asyncSuspensionMarker));
+    expect(asyncStack[2].toString(), contains('helper'));
+    expect(await asyncStack[2].location.toUserString(), contains('.dart:30'));
+    expect(asyncStack[3].kind, equals(M.FrameKind.asyncSuspensionMarker));
+    if (useCausalAsyncStacks) {
+      // helper isn't awaited.
+      expect(asyncStack.length, greaterThanOrEqualTo(5));
+      expect(asyncStack[4].toString(), contains('testMain'));
+      expect(await asyncStack[4].location.toUserString(), contains('.dart:36'));
+    }
+  },
+];
+
+main(args) => runIsolateTestsSynchronous(args, tests,
+    testeeConcurrent: testMain, extraArgs: extraDebuggingArgs);
diff --git a/runtime/observatory_2/tests/service_2/causal_async_star_stack_presence_test.dart b/runtime/observatory_2/tests/service_2/causal_async_star_stack_presence_test.dart
new file mode 100644
index 0000000..db19b2c
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/causal_async_star_stack_presence_test.dart
@@ -0,0 +1,63 @@
+// Copyright (c) 2017, 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.
+// VMOptions=--no-causal-async-stacks --lazy-async-stacks --verbose_debug
+// VMOptions=--causal-async-stacks --no-lazy-async-stacks --verbose_debug
+
+import 'dart:developer';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+const LINE_A = 26;
+const LINE_B = 19;
+const LINE_C = 21;
+
+foobar() async* {
+  debugger();
+  yield 1; // LINE_B.
+  debugger();
+  yield 2; // LINE_C.
+}
+
+helper() async {
+  debugger();
+  print('helper'); // LINE_A.
+  await for (var i in foobar()) {
+    print('helper $i');
+  }
+}
+
+testMain() {
+  helper();
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_A),
+  (Isolate isolate) async {
+    ServiceMap stack = await isolate.getStack();
+    // No causal frames because we are in a completely synchronous stack.
+    expect(stack['asyncCausalFrames'], isNotNull);
+  },
+  resumeIsolate,
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_B),
+  (Isolate isolate) async {
+    ServiceMap stack = await isolate.getStack();
+    // Has causal frames (we are inside an async function)
+    expect(stack['asyncCausalFrames'], isNotNull);
+  },
+  resumeIsolate,
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_C),
+  (Isolate isolate) async {
+    ServiceMap stack = await isolate.getStack();
+    // Has causal frames (we are inside a function called by an async function)
+    expect(stack['asyncCausalFrames'], isNotNull);
+  },
+];
+
+main(args) => runIsolateTestsSynchronous(args, tests,
+    testeeConcurrent: testMain, extraArgs: extraDebuggingArgs);
diff --git a/runtime/observatory_2/tests/service_2/client_name_rpc_test.dart b/runtime/observatory_2/tests/service_2/client_name_rpc_test.dart
new file mode 100644
index 0000000..6d0f8e5
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/client_name_rpc_test.dart
@@ -0,0 +1,70 @@
+// Copyright (c) 2020, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+
+import 'test_helper.dart';
+
+var tests = <VMTest>[
+  (VM vm) async {
+    final defaultClientName = 'client1';
+    final clientName = 'agent-007';
+    var result = await vm.invokeRpcNoUpgrade('getClientName', {});
+    expect(result['type'], 'ClientName');
+    expect(result['name'], defaultClientName);
+
+    // Set the name for this client.
+    result = await vm.invokeRpcNoUpgrade(
+      'setClientName',
+      {
+        'name': clientName,
+      },
+    );
+    expect(result['type'], 'Success');
+
+    // Check it was set properly.
+    result = await vm.invokeRpcNoUpgrade('getClientName', {});
+    expect(result['type'], 'ClientName');
+    expect(result['name'], clientName);
+
+    // Check clearing works properly.
+    result = await vm.invokeRpcNoUpgrade(
+      'setClientName',
+      {
+        'name': '',
+      },
+    );
+    expect(result['type'], 'Success');
+
+    result = await vm.invokeRpcNoUpgrade('getClientName', {});
+    expect(result['type'], 'ClientName');
+    expect(result['name'], defaultClientName);
+  },
+  // Try to set an invalid agent name for this client.
+  (VM vm) async {
+    try {
+      await vm.invokeRpcNoUpgrade(
+        'setClientName',
+        {
+          'name': 42,
+        },
+      );
+      fail('Successfully set invalid client name');
+    } on ServerRpcException catch (e) {/* expected */}
+  },
+  // Missing parameters.
+  (VM vm) async {
+    try {
+      await vm.invokeRpcNoUpgrade('setClientName', {});
+      fail('Successfully set name with no type');
+    } on ServerRpcException catch (e) {/* expected */}
+  },
+];
+
+main(args) async => runVMTests(
+      args,
+      tests,
+      enableService: false,
+    );
diff --git a/runtime/observatory_2/tests/service_2/client_resume_approvals_approve_then_disconnect_test.dart b/runtime/observatory_2/tests/service_2/client_resume_approvals_approve_then_disconnect_test.dart
new file mode 100644
index 0000000..b33fc3f
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/client_resume_approvals_approve_then_disconnect_test.dart
@@ -0,0 +1,70 @@
+// Copyright (c) 2020, 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.
+
+import 'dart:async';
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+
+import 'client_resume_approvals_common.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+void fooBar() {
+  int i = 0;
+  print(i);
+}
+
+WebSocketVM client1;
+WebSocketVM client2;
+
+final test = <IsolateTest>[
+  hasPausedAtStart,
+  (Isolate isolate) async {
+    client1 = await createClient(isolate.owner);
+    await setRequireApprovalForResume(
+      client1,
+      isolate,
+      pauseOnStart: true,
+      pauseOnExit: true,
+    );
+    client2 = await createClient(
+      isolate.owner,
+      clientName: otherClientName,
+    );
+    await setRequireApprovalForResume(
+      client2,
+      isolate,
+      pauseOnStart: true,
+      pauseOnExit: true,
+    );
+
+    // Give resume approval for client1 to ensure approval state is cleaned up
+    // properly when both client1 and client2 have disconnected.
+    await resume(client1, isolate);
+    expect(await isPausedAtStart(isolate), true);
+
+    // Once client1 is disconnected, we should still be paused.
+    client1.disconnect();
+    expect(await isPausedAtStart(isolate), true);
+
+    // Once client2 disconnects, there are no clients which require resume
+    // approval. Since there were no resume requests made by clients which are
+    // still connected, the isolate remains paused.
+    client2.disconnect();
+    expect(await isPausedAtStart(isolate), true);
+
+    await isolate.resume();
+  },
+  hasStoppedAtExit,
+];
+
+Future<void> main(args) => runIsolateTests(
+      args,
+      test,
+      testeeConcurrent: fooBar,
+      pause_on_start: true,
+      pause_on_exit: true,
+      enableService: false,
+    );
diff --git a/runtime/observatory_2/tests/service_2/client_resume_approvals_common.dart b/runtime/observatory_2/tests/service_2/client_resume_approvals_common.dart
new file mode 100644
index 0000000..fb60217
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/client_resume_approvals_common.dart
@@ -0,0 +1,85 @@
+// Copyright (c) 2020, 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.
+
+import 'dart:async';
+
+import 'package:observatory_2/service_io.dart';
+import 'service_test_common.dart';
+
+const String clientName = 'TestClient';
+const String otherClientName = 'OtherTestClient';
+
+Future<void> setClientName(WebSocketVM client, String name) async =>
+    await client.invokeRpc('setClientName', {'name': name});
+
+Future<WebSocketVM> createClient(WebSocketVM vm,
+    {String clientName: clientName}) async {
+  final client = WebSocketVM(vm.target);
+  await client.load();
+  await setClientName(client, clientName);
+  return client;
+}
+
+Future<void> setRequireApprovalForResume(
+  WebSocketVM vm,
+  Isolate isolate, {
+  bool pauseOnStart: false,
+  bool pauseOnExit: false,
+  bool pauseOnReload: false,
+}) async {
+  int pauseTypeMask = 0;
+  if (pauseOnStart) {
+    pauseTypeMask |= 1;
+  }
+  if (pauseOnReload) {
+    pauseTypeMask |= 2;
+  }
+  if (pauseOnExit) {
+    pauseTypeMask |= 4;
+  }
+  await vm.invokeRpc('requirePermissionToResume', {
+    'isolateId': isolate.id,
+    'pauseTypeMask': pauseTypeMask,
+    'onPauseStart': pauseOnStart,
+    'onPauseReload': pauseOnReload,
+    'onPauseExit': pauseOnExit,
+  });
+}
+
+Future<void> resume(WebSocketVM vm, Isolate isolate) async =>
+    await vm.invokeRpc('resume', {
+      'isolateId': isolate.id,
+    });
+
+Future<bool> isPausedAtStart(Isolate isolate) async {
+  await isolate.reload();
+  return ((isolate.pauseEvent != null) &&
+      isEventOfKind(isolate.pauseEvent, ServiceEvent.kPauseStart));
+}
+
+Future<bool> isPausedAtExit(Isolate isolate) async {
+  await isolate.reload();
+  return ((isolate.pauseEvent != null) &&
+      isEventOfKind(isolate.pauseEvent, ServiceEvent.kPauseExit));
+}
+
+Future<bool> isPausedPostRequest(Isolate isolate) async {
+  await isolate.reload();
+  return ((isolate.pauseEvent != null) &&
+      isEventOfKind(isolate.pauseEvent, ServiceEvent.kPausePostRequest));
+}
+
+Future<void> waitForResume(Isolate isolate) async {
+  final completer = Completer<bool>();
+  isolate.vm.getEventStream(VM.kDebugStream).then((stream) {
+    var subscription;
+    subscription = stream.listen((ServiceEvent event) {
+      if (event.kind == ServiceEvent.kResume) {
+        subscription.cancel();
+        completer.complete();
+      }
+    });
+  });
+  return completer.future;
+}
diff --git a/runtime/observatory_2/tests/service_2/client_resume_approvals_disconnect_test.dart b/runtime/observatory_2/tests/service_2/client_resume_approvals_disconnect_test.dart
new file mode 100644
index 0000000..f59ca97
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/client_resume_approvals_disconnect_test.dart
@@ -0,0 +1,68 @@
+// Copyright (c) 2020, 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.
+
+import 'dart:async';
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+
+import 'client_resume_approvals_common.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+void fooBar() {
+  int i = 0;
+  print(i);
+}
+
+WebSocketVM client1;
+WebSocketVM client2;
+
+final test = <IsolateTest>[
+  // Multiple clients, disconnect client awaiting approval.
+  hasPausedAtStart,
+  (Isolate isolate) async {
+    client1 = await createClient(isolate.owner);
+    await setRequireApprovalForResume(
+      client1,
+      isolate,
+      pauseOnStart: true,
+      pauseOnExit: true,
+    );
+    client2 = await createClient(
+      isolate.owner,
+      clientName: otherClientName,
+    );
+    await setRequireApprovalForResume(
+      client2,
+      isolate,
+      pauseOnStart: true,
+      pauseOnExit: true,
+    );
+
+    // Send a resume request on the test client so we'll resume once the other
+    // clients which require approval disconnect.
+    await isolate.resume();
+    expect(await isPausedAtStart(isolate), true);
+
+    // Once client1 is disconnected, we should still be paused.
+    client1.disconnect();
+    expect(await isPausedAtStart(isolate), true);
+
+    // Once client2 disconnects, there are no clients which require resume
+    // approval. Ensure we resume immediately so we don't deadlock waiting for
+    // approvals from disconnected clients.
+    client2.disconnect();
+  },
+  hasStoppedAtExit,
+];
+
+Future<void> main(args) => runIsolateTests(
+      args,
+      test,
+      testeeConcurrent: fooBar,
+      pause_on_start: true,
+      pause_on_exit: true,
+      enableService: false,
+    );
diff --git a/runtime/observatory_2/tests/service_2/client_resume_approvals_identical_names_test.dart b/runtime/observatory_2/tests/service_2/client_resume_approvals_identical_names_test.dart
new file mode 100644
index 0000000..ba4faa4
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/client_resume_approvals_identical_names_test.dart
@@ -0,0 +1,50 @@
+// Copyright (c) 2020, 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.
+
+import 'dart:async';
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+
+import 'client_resume_approvals_common.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+void fooBar() {
+  int i = 0;
+  print(i);
+}
+
+WebSocketVM client1;
+WebSocketVM client2;
+
+final sameClientNamesTest = <IsolateTest>[
+  // Multiple clients, same client names.
+  (Isolate isolate) async {
+    final resumeFuture = waitForResume(isolate);
+
+    client1 = await createClient(isolate.owner);
+    await setRequireApprovalForResume(
+      client1,
+      isolate,
+      pauseOnStart: true,
+      pauseOnExit: true,
+    );
+    client2 = await createClient(isolate.owner);
+
+    expect(await isPausedAtStart(isolate), true);
+    await resume(client2, isolate);
+    await resumeFuture;
+  },
+  hasStoppedAtExit,
+];
+
+Future<void> main(args) => runIsolateTests(
+      args,
+      sameClientNamesTest,
+      testeeConcurrent: fooBar,
+      pause_on_start: true,
+      pause_on_exit: true,
+      enableService: false,
+    );
diff --git a/runtime/observatory_2/tests/service_2/client_resume_approvals_multiple_names_test.dart b/runtime/observatory_2/tests/service_2/client_resume_approvals_multiple_names_test.dart
new file mode 100644
index 0000000..818a0a2
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/client_resume_approvals_multiple_names_test.dart
@@ -0,0 +1,68 @@
+// Copyright (c) 2020, 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.
+
+import 'dart:async';
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+
+import 'client_resume_approvals_common.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+void fooBar() {
+  int i = 0;
+  print(i);
+}
+
+WebSocketVM client1;
+WebSocketVM client2;
+WebSocketVM client3;
+
+final multipleClientNamesTest = <IsolateTest>[
+  // Multiple clients, different client names.
+  (Isolate isolate) async {
+    client1 = await createClient(isolate.owner);
+    await setRequireApprovalForResume(
+      client1,
+      isolate,
+      pauseOnStart: true,
+      pauseOnExit: true,
+    );
+    client2 = await createClient(
+      isolate.owner,
+      clientName: otherClientName,
+    );
+    client3 = await createClient(isolate.owner, clientName: 'DummyClient');
+
+    final resumeFuture = waitForResume(isolate);
+    expect(await isPausedAtStart(isolate), true);
+    await resume(client2, isolate);
+    expect(await isPausedAtStart(isolate), true);
+    await resume(client1, isolate);
+    await resumeFuture;
+    expect(await isPausedAtStart(isolate), false);
+  },
+  hasStoppedAtExit,
+  (Isolate isolate) async {
+    await setRequireApprovalForResume(
+      client2,
+      isolate,
+      pauseOnExit: true,
+    );
+    await resume(client1, isolate);
+    expect(await isPausedAtExit(isolate), true);
+    await resume(client2, isolate);
+    await waitForTargetVMExit(isolate.vm);
+  },
+];
+
+Future<void> main(args) => runIsolateTests(
+      args,
+      multipleClientNamesTest,
+      testeeConcurrent: fooBar,
+      pause_on_start: true,
+      pause_on_exit: true,
+      enableService: false,
+    );
diff --git a/runtime/observatory_2/tests/service_2/client_resume_approvals_name_change_test.dart b/runtime/observatory_2/tests/service_2/client_resume_approvals_name_change_test.dart
new file mode 100644
index 0000000..bedf87f
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/client_resume_approvals_name_change_test.dart
@@ -0,0 +1,61 @@
+// Copyright (c) 2020, 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.
+
+import 'dart:async';
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+
+import 'client_resume_approvals_common.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+void fooBar() {
+  int i = 0;
+  print(i);
+}
+
+WebSocketVM client1;
+WebSocketVM client2;
+WebSocketVM client3;
+
+final nameChangeTest = <IsolateTest>[
+  // Remove required approvals via name change.
+  (Isolate isolate) async {
+    final resumeFuture = waitForResume(isolate);
+
+    // Create two clients with the same name.
+    client1 = await createClient(isolate.owner);
+    client2 = await createClient(isolate.owner);
+    await setRequireApprovalForResume(
+      client1,
+      isolate,
+      pauseOnStart: true,
+      pauseOnExit: true,
+    );
+    client3 = await createClient(isolate.owner, clientName: otherClientName);
+
+    // Check that client3 can't resume the isolate on its own.
+    expect(await isPausedAtStart(isolate), true);
+    await resume(client3, isolate);
+    expect(await isPausedAtStart(isolate), true);
+
+    // Change the name of client1. Since client2 has the same name that client1
+    // originally had, the service still requires approval to resume the
+    // isolate.
+    await setClientName(client1, 'foobar');
+    expect(await isPausedAtStart(isolate), true);
+    await setClientName(client2, 'baz');
+  },
+  hasStoppedAtExit,
+];
+
+Future<void> main(args) => runIsolateTests(
+      args,
+      nameChangeTest,
+      testeeConcurrent: fooBar,
+      pause_on_start: true,
+      pause_on_exit: true,
+      enableService: false,
+    );
diff --git a/runtime/observatory_2/tests/service_2/client_resume_approvals_reload_test.dart b/runtime/observatory_2/tests/service_2/client_resume_approvals_reload_test.dart
new file mode 100644
index 0000000..b77ec16
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/client_resume_approvals_reload_test.dart
@@ -0,0 +1,69 @@
+// Copyright (c) 2020, 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.
+
+import 'dart:async';
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+
+import 'client_resume_approvals_common.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+void fooBar() {
+  int i = 0;
+  while (true) {
+    i++;
+  }
+  print(i);
+}
+
+WebSocketVM client1;
+WebSocketVM client2;
+
+final hotReloadTest = <IsolateTest>[
+  // Multiple clients, hot reload approval.
+  (Isolate isolate) async {
+    final resumeFuture = waitForResume(isolate);
+
+    client1 = await createClient(isolate.owner);
+    await setRequireApprovalForResume(
+      client1,
+      isolate,
+      pauseOnReload: true,
+    );
+    client2 = await createClient(
+      isolate.owner,
+      clientName: otherClientName,
+    );
+    await setRequireApprovalForResume(
+      client2,
+      isolate,
+      pauseOnReload: true,
+    );
+  },
+  // Paused on start, resume.
+  resumeIsolate,
+  // Reload and then pause.
+  reloadSources(true),
+  hasStoppedPostRequest,
+  (Isolate isolate) async {
+    // Check that client2 can't resume the isolate on its own.
+    expect(await isPausedPostRequest(isolate), true);
+    await resume(client2, isolate);
+    expect(await isPausedPostRequest(isolate), true);
+    final resumeFuture = waitForResume(isolate);
+    await resume(client1, isolate);
+    await resumeFuture;
+    expect(await isPausedPostRequest(isolate), false);
+  },
+];
+
+Future<void> main(args) => runIsolateTests(
+      args,
+      hotReloadTest,
+      testeeConcurrent: fooBar,
+      pause_on_start: true,
+      enableService: false,
+    );
diff --git a/runtime/observatory_2/tests/service_2/code_test.dart b/runtime/observatory_2/tests/service_2/code_test.dart
new file mode 100644
index 0000000..a369d3f
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/code_test.dart
@@ -0,0 +1,82 @@
+// Copyright (c) 2015, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+import 'dart:async';
+
+int counter = 0;
+
+void funcB() {
+  counter++; // line 13
+  if (counter % 100000000 == 0) {
+    print(counter);
+  }
+}
+
+void funcA() {
+  funcB();
+}
+
+void testFunction() {
+  while (true) {
+    funcA();
+  }
+}
+
+var tests = <IsolateTest>[
+// Go to breakpoint at line 13.
+  (Isolate isolate) async {
+    await isolate.rootLibrary.load();
+    // Set up a listener to wait for breakpoint events.
+    Completer completer = new Completer();
+    isolate.vm.getEventStream(VM.kDebugStream).then((stream) {
+      var subscription;
+      subscription = stream.listen((ServiceEvent event) {
+        if (event.kind == ServiceEvent.kPauseBreakpoint) {
+          print('Breakpoint reached');
+          subscription.cancel();
+          completer.complete();
+        }
+      });
+    });
+
+    // Add the breakpoint.
+    var script = isolate.rootLibrary.scripts[0];
+    var line = 13;
+    isolate.addBreakpoint(script, line);
+    await completer.future; // Wait for breakpoint reached.
+  },
+
+// Inspect code objects for top two frames.
+  (Isolate isolate) async {
+    ServiceMap stack = await isolate.getStack();
+    // Make sure we are in the right place.
+    expect(stack.type, equals('Stack'));
+    expect(stack['frames'].length, greaterThanOrEqualTo(3));
+    var frame0 = stack['frames'][0];
+    var frame1 = stack['frames'][1];
+    print(frame0);
+    expect(frame0.function.name, equals('funcB'));
+    expect(frame1.function.name, equals('funcA'));
+    var codeId0 = frame0.code.id;
+    var codeId1 = frame1.code.id;
+
+    List tests = <IsolateTest>[];
+    // Load code from frame 0.
+    Code code = await isolate.getObject(codeId0);
+    expect(code.type, equals('Code'));
+    expect(code.function.name, equals('funcB'));
+    expect(code.hasDisassembly, equals(true));
+
+    // Load code from frame 0.
+    code = await isolate.getObject(codeId1);
+    expect(code.type, equals('Code'));
+    expect(code.function.name, equals('funcA'));
+    expect(code.hasDisassembly, equals(true));
+  },
+];
+
+main(args) => runIsolateTests(args, tests, testeeConcurrent: testFunction);
diff --git a/runtime/observatory_2/tests/service_2/collect_all_garbage_test.dart b/runtime/observatory_2/tests/service_2/collect_all_garbage_test.dart
new file mode 100644
index 0000000..48589e9
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/collect_all_garbage_test.dart
@@ -0,0 +1,17 @@
+// Copyright (c) 2017, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+
+import 'test_helper.dart';
+
+var tests = <IsolateTest>[
+  (Isolate isolate) async {
+    var result = await isolate.invokeRpcNoUpgrade('_collectAllGarbage', {});
+    expect(result['type'], equals('Success'));
+  },
+];
+
+main(args) async => runIsolateTests(args, tests);
diff --git a/runtime/observatory_2/tests/service_2/column_breakpoint_test.dart b/runtime/observatory_2/tests/service_2/column_breakpoint_test.dart
new file mode 100644
index 0000000..b474d6d
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/column_breakpoint_test.dart
@@ -0,0 +1,38 @@
+// Copyright (c) 2019, 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.
+
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+code() {
+  var b = [1, 2].map((i) => i == 0).toList();
+  print(b.length);
+}
+
+const int LINE = 9;
+const int COLUMN = 29;
+const String shortFile = "column_breakpoint_test.dart";
+const String breakpointFile =
+    "package:observatory_test_package_2/column_breakpoint_test.dart";
+
+List<String> stops = [];
+
+List<String> expected = [
+  "$shortFile:${LINE + 0}:23", // on 'i == 0'
+  "$shortFile:${LINE + 0}:23", // iterate twice
+  "$shortFile:${LINE + 1}:3" //on 'b.length'
+];
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  setBreakpointAtLineColumn(LINE, COLUMN), // on 'i == 0'
+  setBreakpointAtLineColumn(LINE + 1, 9), // on 'b.length'
+  resumeProgramRecordingStops(stops, false),
+  checkRecordedStops(stops, expected)
+];
+
+main(args) {
+  runIsolateTestsSynchronous(args, tests,
+      testeeConcurrent: code, pause_on_start: true, pause_on_exit: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/command_test.dart b/runtime/observatory_2/tests/service_2/command_test.dart
new file mode 100644
index 0000000..23769c0
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/command_test.dart
@@ -0,0 +1,233 @@
+// Copyright (c) 2015, 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.
+
+import 'dart:async';
+
+import 'package:observatory_2/cli.dart';
+import 'package:test/test.dart';
+
+class TestCommand extends Command {
+  TestCommand(this.out, name, children) : super(name, children);
+  StringBuffer out;
+
+  Future run(List<String> args) {
+    out.write('executing ${name}(${args})\n');
+    return new Future.value(null);
+  }
+}
+
+class TestCompleteCommand extends Command {
+  TestCompleteCommand(this.out, name, children) : super(name, children);
+  StringBuffer out;
+
+  Future<List<String>> complete(List<String> args) {
+    var possibles = ['one ', 'two ', 'three '];
+    return new Future.value(
+        possibles.where((possible) => possible.startsWith(args[0])).toList());
+  }
+
+  Future run(List<String> args) {
+    out.write('executing ${name}(${args})\n');
+    return new Future.value(null);
+  }
+}
+
+void testCommandComplete() {
+  RootCommand cmd = new RootCommand([
+    new TestCommand(null, 'alpha', <Command>[]),
+    new TestCommand(null, 'game', <Command>[
+      new TestCommand(null, 'checkers', <Command>[]),
+      new TestCommand(null, 'chess', <Command>[])
+    ]),
+    new TestCommand(null, 'gamera', <Command>[
+      new TestCommand(null, 'london', <Command>[]),
+      new TestCommand(null, 'tokyo', <Command>[]),
+      new TestCommand(null, 'topeka', <Command>[])
+    ]),
+    new TestCompleteCommand(
+        null, 'count', <Command>[new TestCommand(null, 'chocula', <Command>[])])
+  ]);
+
+  // Show all commands.
+  cmd.completeCommand('').then((result) {
+    expect(result, equals(['alpha ', 'game ', 'gamera ', 'count ']));
+  });
+
+  // Substring completion.
+  cmd.completeCommand('al').then((result) {
+    expect(result, equals(['alpha ']));
+  });
+
+  // Full string completion.
+  cmd.completeCommand('alpha').then((result) {
+    expect(result, equals(['alpha ']));
+  });
+
+  // Extra space, no subcommands.
+  cmd.completeCommand('alpha ').then((result) {
+    expect(result, equals(['alpha ']));
+  });
+
+  // Ambiguous completion.
+  cmd.completeCommand('g').then((result) {
+    expect(result, equals(['game ', 'gamera ']));
+  });
+
+  // Ambiguous completion, exact match not preferred.
+  cmd.completeCommand('game').then((result) {
+    expect(result, equals(['game ', 'gamera ']));
+  });
+
+  // Show all subcommands.
+  cmd.completeCommand('gamera ').then((result) {
+    expect(
+        result, equals(['gamera london ', 'gamera tokyo ', 'gamera topeka ']));
+  });
+
+  // Subcommand completion.
+  cmd.completeCommand('gamera l').then((result) {
+    expect(result, equals(['gamera london ']));
+  });
+
+  // Extra space, with subcommand.
+  cmd.completeCommand('gamera london ').then((result) {
+    expect(result, equals(['gamera london ']));
+  });
+
+  // Ambiguous subcommand completion.
+  cmd.completeCommand('gamera t').then((result) {
+    expect(result, equals(['gamera tokyo ', 'gamera topeka ']));
+  });
+
+  // Ambiguous subcommand completion with substring prefix.
+  // Note that the prefix is left alone.
+  cmd.completeCommand('gamer t').then((result) {
+    expect(result, equals(['gamer tokyo ', 'gamer topeka ']));
+  });
+
+  // Ambiguous but exact prefix is preferred.
+  cmd.completeCommand('game chec').then((result) {
+    expect(result, equals(['game checkers ']));
+  });
+
+  // Ambiguous non-exact prefix means no matches.
+  cmd.completeCommand('gam chec').then((result) {
+    expect(result, equals([]));
+  });
+
+  // Locals + subcommands, show all.
+  cmd.completeCommand('count ').then((result) {
+    expect(result,
+        equals(['count chocula ', 'count one ', 'count two ', 'count three ']));
+  });
+
+  // Locals + subcommands, single local match.
+  cmd.completeCommand('count th').then((result) {
+    expect(result, equals(['count three ']));
+  });
+
+  // Locals + subcommands, ambiguous local match.
+  cmd.completeCommand('count t').then((result) {
+    expect(result, equals(['count two ', 'count three ']));
+  });
+
+  // Locals + subcommands, single command match.
+  cmd.completeCommand('co choc').then((result) {
+    expect(result, equals(['co chocula ']));
+  });
+
+  // We gobble spare spaces in the prefix but not elsewhere.
+  cmd.completeCommand('    game    chec').then((result) {
+    expect(result, equals(['game    checkers ']));
+  });
+}
+
+testCommandRunSimple() async {
+  // Run a simple command.
+  StringBuffer out = new StringBuffer();
+  RootCommand cmd =
+      new RootCommand([new TestCommand(out, 'alpha', <Command>[])]);
+
+  // Full name dispatch works.  Argument passing works.
+  await cmd.runCommand('alpha dog');
+  expect(out.toString(), contains('executing alpha([dog])\n'));
+  out.clear();
+  // Substring dispatch works.
+  await cmd.runCommand('al cat mouse');
+  expect(out.toString(), contains('executing alpha([cat , mouse])\n'));
+}
+
+testCommandRunSubcommand() async {
+  // Run a simple command.
+  StringBuffer out = new StringBuffer();
+  RootCommand cmd = new RootCommand([
+    new TestCommand(out, 'alpha', [
+      new TestCommand(out, 'beta', <Command>[]),
+      new TestCommand(out, 'gamma', <Command>[])
+    ])
+  ]);
+
+  await cmd.runCommand('a b');
+  expect(out.toString(), equals('executing beta([])\n'));
+  out.clear();
+  await cmd.runCommand('alpha g ');
+  expect(out.toString(), equals('executing gamma([])\n'));
+}
+
+testCommandRunNotFound() async {
+  // Run a simple command.
+  StringBuffer out = new StringBuffer();
+  RootCommand cmd =
+      new RootCommand([new TestCommand(out, 'alpha', <Command>[])]);
+
+  dynamic e;
+  try {
+    await cmd.runCommand('goose');
+  } catch (ex) {
+    e = ex;
+  }
+  expect(e.toString(), equals("No such command: 'goose'"));
+}
+
+testCommandRunAmbiguous() async {
+  // Run a simple command.
+  StringBuffer out = new StringBuffer();
+  RootCommand cmd = new RootCommand([
+    new TestCommand(out, 'alpha', <Command>[]),
+    new TestCommand(out, 'ankle', <Command>[])
+  ]);
+
+  dynamic e;
+  try {
+    await cmd.runCommand('a 55');
+  } catch (ex) {
+    e = ex;
+  }
+  expect(e.toString(), equals("Command 'a 55' is ambiguous: [alpha, ankle]"));
+  out.clear();
+
+  await cmd.runCommand('ankl 55');
+  expect(out.toString(), equals('executing ankle([55])\n'));
+}
+
+testCommandRunAlias() async {
+  // Run a simple command.
+  StringBuffer out = new StringBuffer();
+  var aliasCmd = new TestCommand(out, 'alpha', <Command>[]);
+  aliasCmd.alias = 'a';
+  RootCommand cmd =
+      new RootCommand([aliasCmd, new TestCommand(out, 'ankle', <Command>[])]);
+
+  await cmd.runCommand('a 55');
+  expect(out.toString(), equals('executing alpha([55])\n'));
+}
+
+main() {
+  test('command completion test suite', testCommandComplete);
+  test('run a simple command', testCommandRunSimple);
+  test('run a subcommand', testCommandRunSubcommand);
+  test('run a command which is not found', testCommandRunNotFound);
+  test('run a command which is ambiguous', testCommandRunAmbiguous);
+  test('run a command using an alias', testCommandRunAlias);
+}
diff --git a/runtime/observatory_2/tests/service_2/complex_reload/v1/main.dart b/runtime/observatory_2/tests/service_2/complex_reload/v1/main.dart
new file mode 100644
index 0000000..043b9b9
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/complex_reload/v1/main.dart
@@ -0,0 +1,12 @@
+// Copyright (c) 2016, 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.
+
+import 'dart:isolate';
+
+test() => 'apple';
+
+main() {
+  RawReceivePort keepAlive = new RawReceivePort();
+  print('spawned isolate running');
+}
diff --git a/runtime/observatory_2/tests/service_2/complex_reload/v2/.gitignore b/runtime/observatory_2/tests/service_2/complex_reload/v2/.gitignore
new file mode 100644
index 0000000..1d24550
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/complex_reload/v2/.gitignore
@@ -0,0 +1 @@
+!packages
diff --git a/runtime/observatory_2/tests/service_2/complex_reload/v2/foobar_lib/foo.dart b/runtime/observatory_2/tests/service_2/complex_reload/v2/foobar_lib/foo.dart
new file mode 100644
index 0000000..8981163
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/complex_reload/v2/foobar_lib/foo.dart
@@ -0,0 +1 @@
+fooLib() => 'fooLib';
diff --git a/runtime/observatory_2/tests/service_2/complex_reload/v2/main.dart b/runtime/observatory_2/tests/service_2/complex_reload/v2/main.dart
new file mode 100644
index 0000000..2088634
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/complex_reload/v2/main.dart
@@ -0,0 +1,7 @@
+// Copyright (c) 2016, 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.
+
+import 'package:foobar/foo.dart';
+
+test() => fooLib();
diff --git a/runtime/observatory_2/tests/service_2/complex_reload/v2/packages b/runtime/observatory_2/tests/service_2/complex_reload/v2/packages
new file mode 100644
index 0000000..9d477cd
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/complex_reload/v2/packages
@@ -0,0 +1 @@
+foobar:foobar_lib/
diff --git a/runtime/observatory_2/tests/service_2/complex_reload/v3/main.dart b/runtime/observatory_2/tests/service_2/complex_reload/v3/main.dart
new file mode 100644
index 0000000..01756a99
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/complex_reload/v3/main.dart
@@ -0,0 +1,5 @@
+// Copyright (c) 2016, 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.
+
+test() => 'cabbage';
diff --git a/runtime/observatory_2/tests/service_2/complex_reload_test.dart b/runtime/observatory_2/tests/service_2/complex_reload_test.dart
new file mode 100644
index 0000000..4e63b8d
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/complex_reload_test.dart
@@ -0,0 +1,88 @@
+// Copyright (c) 2016, 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.
+
+import 'test_helper.dart';
+import 'dart:async';
+import 'dart:developer';
+import 'dart:isolate' as I;
+import 'dart:io';
+import 'service_test_common.dart';
+import 'package:observatory_2/service.dart';
+import 'package:path/path.dart' as path;
+import 'package:test/test.dart';
+
+// Chop off the file name.
+String baseDirectory = path.dirname(Platform.script.path) + '/';
+
+Uri baseUri = Platform.script.replace(path: baseDirectory);
+Uri spawnUri = baseUri.resolveUri(Uri.parse('complex_reload/v1/main.dart'));
+Uri v2Uri = baseUri.resolveUri(Uri.parse('complex_reload/v2/main.dart'));
+Uri v3Uri = baseUri.resolveUri(Uri.parse('complex_reload/v3/main.dart'));
+Uri v2PackagesUri = baseUri.resolveUri(Uri.parse('complex_reload/v2/packages'));
+
+testMain() async {
+  print(baseUri);
+  debugger(); // Stop here.
+  // Spawn the child isolate.
+  I.Isolate isolate = await I.Isolate.spawnUri(spawnUri, [], null);
+  print(isolate);
+  debugger();
+}
+
+Future<String> invokeTest(Isolate isolate) async {
+  await isolate.reload();
+  Library lib = isolate.rootLibrary;
+  await lib.load();
+  Instance result = await lib.evaluate('test()');
+  expect(result.isString, isTrue);
+  return result.valueAsString;
+}
+
+var tests = <IsolateTest>[
+  // Stopped at 'debugger' statement.
+  hasStoppedAtBreakpoint,
+  // Resume the isolate into the while loop.
+  resumeIsolate,
+  // Stop at 'debugger' statement.
+  hasStoppedAtBreakpoint,
+  (Isolate mainIsolate) async {
+    // Grab the VM.
+    VM vm = mainIsolate.vm;
+    await vm.reloadIsolates();
+    expect(vm.isolates.length, 2);
+
+    // Find the spawned isolate.
+    Isolate spawnedIsolate =
+        vm.isolates.firstWhere((Isolate i) => i != mainIsolate);
+    expect(spawnedIsolate, isNotNull);
+
+    // Invoke test in v1.
+    String v1 = await invokeTest(spawnedIsolate);
+    expect(v1, 'apple');
+
+    // Reload to v2.
+    var response = await spawnedIsolate.reloadSources(
+      rootLibUri: v2Uri.toString(),
+      packagesUri: v2PackagesUri.toString(),
+    );
+    print(response);
+    expect(response['success'], isTrue);
+
+    // Invoke test in v2.
+    String v2 = await invokeTest(spawnedIsolate);
+    expect(v2, 'fooLib');
+
+    // Reload to v3.
+    response = await spawnedIsolate.reloadSources(
+      rootLibUri: v3Uri.toString(),
+    );
+    expect(response['success'], isTrue);
+
+    // Invoke test in v3.
+    String v3 = await invokeTest(spawnedIsolate);
+    expect(v3, 'cabbage');
+  }
+];
+
+main(args) => runIsolateTests(args, tests, testeeConcurrent: testMain);
diff --git a/runtime/observatory_2/tests/service_2/contexts_test.dart b/runtime/observatory_2/tests/service_2/contexts_test.dart
new file mode 100644
index 0000000..fb40ffa
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/contexts_test.dart
@@ -0,0 +1,117 @@
+// Copyright (c) 2014, 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.
+
+library inbound_references_test;
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+
+// Make sure these variables are not removed by the tree shaker.
+@pragma("vm:entry-point")
+var cleanBlock;
+@pragma("vm:entry-point")
+var copyingBlock;
+@pragma("vm:entry-point")
+var fullBlock;
+@pragma("vm:entry-point")
+var fullBlockWithChain;
+
+Function genCleanBlock() {
+  block(x) => x;
+  return block;
+}
+
+Function genCopyingBlock() {
+  final x = 'I could be copied into the block';
+  block() => x;
+  return block;
+}
+
+Function genFullBlock() {
+  var x = 42; // I must captured in a context.
+  block() => x;
+  x++;
+  return block;
+}
+
+Function genFullBlockWithChain() {
+  var x = 420; // I must captured in a context.
+  outerBlock() {
+    var y = 4200;
+    innerBlock() => x + y;
+    y++;
+    return innerBlock;
+  }
+
+  x++;
+  return outerBlock();
+}
+
+void script() {
+  cleanBlock = genCleanBlock();
+  copyingBlock = genCopyingBlock();
+  fullBlock = genFullBlock();
+  fullBlockWithChain = genFullBlockWithChain();
+}
+
+var tests = <IsolateTest>[
+  (Isolate isolate) async {
+    Library lib = await isolate.rootLibrary.load();
+    Field field = lib.variables.singleWhere((v) => v.name == 'cleanBlock');
+    await field.load();
+    Instance block = await field.staticValue.load();
+    expect(block.isClosure, isTrue);
+    expect(block.closureContext, isNull);
+  },
+  (Isolate isolate) async {
+    Library lib = await isolate.rootLibrary.load();
+    Field field = lib.variables.singleWhere((v) => v.name == 'copyingBlock');
+    await field.load();
+    Instance block = await field.staticValue.load();
+    expect(block.isClosure, isTrue);
+    expect(block.closureContext.isContext, isTrue);
+    expect(block.closureContext.length, equals(1));
+    Context ctxt = await block.closureContext.load();
+    expect(ctxt.variables.single.value.asValue.isString, isTrue);
+    expect(ctxt.variables.single.value.asValue.valueAsString,
+        equals('I could be copied into the block'));
+    expect(ctxt.parentContext, isNull);
+  },
+  (Isolate isolate) async {
+    Library lib = await isolate.rootLibrary.load();
+    Field field = lib.variables.singleWhere((v) => v.name == 'fullBlock');
+    await field.load();
+    Instance block = await field.staticValue.load();
+    expect(block.isClosure, isTrue);
+    expect(block.closureContext.isContext, isTrue);
+    expect(block.closureContext.length, equals(1));
+    Context ctxt = await block.closureContext.load();
+    expect(ctxt.variables.single.value.asValue.isInt, isTrue);
+    expect(ctxt.variables.single.value.asValue.valueAsString, equals('43'));
+    expect(ctxt.parentContext, isNull);
+  },
+  (Isolate isolate) async {
+    Library lib = await isolate.rootLibrary.load();
+    Field field =
+        lib.variables.singleWhere((v) => v.name == 'fullBlockWithChain');
+    await field.load();
+    Instance block = await field.staticValue.load();
+    expect(block.isClosure, isTrue);
+    expect(block.closureContext.isContext, isTrue);
+    expect(block.closureContext.length, equals(1));
+    Context ctxt = await block.closureContext.load();
+    expect(ctxt.variables.single.value.asValue.isInt, isTrue);
+    expect(ctxt.variables.single.value.asValue.valueAsString, equals('4201'));
+    expect(ctxt.parentContext.isContext, isTrue);
+    expect(ctxt.parentContext.length, equals(1));
+    Context outerCtxt = await ctxt.parentContext.load();
+    expect(outerCtxt.variables.single.value.asValue.isInt, isTrue);
+    expect(
+        outerCtxt.variables.single.value.asValue.valueAsString, equals('421'));
+    expect(outerCtxt.parentContext, isNull);
+  },
+];
+
+main(args) => runIsolateTests(args, tests, testeeBefore: script);
diff --git a/runtime/observatory_2/tests/service_2/coverage_const_field_async_closure_test.dart b/runtime/observatory_2/tests/service_2/coverage_const_field_async_closure_test.dart
new file mode 100644
index 0000000..f2c21ce
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/coverage_const_field_async_closure_test.dart
@@ -0,0 +1,76 @@
+// Copyright (c) 2019, 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.
+
+import 'dart:developer';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+const int LINE_A = 15; // LINE_A - 4
+const int LINE_B = 23; // LINE_A - 3
+
+class Bar {
+  static const String field = "field"; // LINE_A
+}
+
+Future<String> fooAsync(int x) async {
+  if (x == 42) {
+    return '*' * x;
+  }
+  return List.generate(x, (_) => 'xyzzy').join(' ');
+} // LINE_B
+
+void testFunction() async {
+  await new Future.delayed(Duration(milliseconds: 500));
+  fooAsync(42).then((_) {});
+  debugger();
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  (Isolate isolate) async {
+    var stack = await isolate.getStack();
+
+    // Make sure we are in the right place.
+    expect(stack.type, 'Stack');
+    expect(stack['frames'].length, greaterThanOrEqualTo(1));
+    // Async closure of testFunction
+    expect(stack['frames'][0].function.name, 'async_op');
+
+    var root = isolate.rootLibrary;
+    await root.load();
+    Script script = root.scripts.first;
+    await script.load();
+
+    var params = {
+      'reports': ['Coverage'],
+      'scriptId': script.id,
+      'forceCompile': true
+    };
+    var report = await isolate.invokeRpcNoUpgrade('getSourceReport', params);
+    List<dynamic> ranges = report['ranges'];
+
+    int match = 0;
+    for (var range in ranges) {
+      for (int i in range["coverage"]["hits"]) {
+        int line = script.tokenToLine(i);
+        if (line == null) {
+          throw FormatException('token ${i} was missing source location');
+        }
+        // Check LINE.
+        if (line == LINE_A || line == LINE_A - 3 || line == LINE_A - 4) {
+          match = match + 1;
+        }
+        // _clearAsyncThreadStackTrace should have an invalid token position.
+        expect(line, isNot(LINE_B));
+      }
+    }
+    // Neither LINE nor Bar.field should be added into coverage.
+    expect(match, 0);
+  },
+  resumeIsolate
+];
+
+main(args) => runIsolateTests(args, tests, testeeConcurrent: testFunction);
diff --git a/runtime/observatory_2/tests/service_2/coverage_leaf_function_test.dart b/runtime/observatory_2/tests/service_2/coverage_leaf_function_test.dart
new file mode 100644
index 0000000..25587aa
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/coverage_leaf_function_test.dart
@@ -0,0 +1,114 @@
+// Copyright (c) 2017, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+import 'service_test_common.dart';
+import 'dart:developer';
+
+String leafFunction() {
+  return "some constant";
+}
+
+void testFunction() {
+  debugger();
+  leafFunction();
+  debugger();
+}
+
+bool allRangesCompiled(coverage) {
+  for (int i = 0; i < coverage['ranges'].length; i++) {
+    if (!coverage['ranges'][i]['compiled']) {
+      return false;
+    }
+  }
+  return true;
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  (Isolate isolate) async {
+    var stack = await isolate.getStack();
+
+    // Make sure we are in the right place.
+    expect(stack.type, equals('Stack'));
+    expect(stack['frames'].length, greaterThanOrEqualTo(1));
+    expect(stack['frames'][0].function.name, equals('testFunction'));
+
+    var root = isolate.rootLibrary;
+    await root.load();
+    var func = root.functions.singleWhere((f) => f.name == 'leafFunction');
+    await func.load();
+
+    var expectedRange = {
+      'scriptIndex': 0,
+      'startPos': 384,
+      'endPos': 434,
+      'compiled': true,
+      'coverage': {
+        'hits': [],
+        'misses': [384]
+      }
+    };
+
+    var params = {
+      'reports': ['Coverage'],
+      'scriptId': func.location.script.id,
+      'tokenPos': func.location.tokenPos,
+      'endTokenPos': func.location.endTokenPos,
+      'forceCompile': true
+    };
+    var report = await isolate.invokeRpcNoUpgrade('getSourceReport', params);
+    expect(report['type'], equals('SourceReport'));
+    expect(report['ranges'].length, 1);
+    expect(report['ranges'][0], equals(expectedRange));
+    expect(report['scripts'].length, 1);
+    expect(report['scripts'][0]['uri'],
+        endsWith('coverage_leaf_function_test.dart'));
+  },
+  resumeIsolate,
+  hasStoppedAtBreakpoint,
+  (Isolate isolate) async {
+    var stack = await isolate.getStack();
+
+    // Make sure we are in the right place.
+    expect(stack.type, equals('Stack'));
+    expect(stack['frames'].length, greaterThanOrEqualTo(1));
+    expect(stack['frames'][0].function.name, equals('testFunction'));
+
+    var root = isolate.rootLibrary;
+    await root.load();
+    var func = root.functions.singleWhere((f) => f.name == 'leafFunction');
+    await func.load();
+
+    var expectedRange = {
+      'scriptIndex': 0,
+      'startPos': 384,
+      'endPos': 434,
+      'compiled': true,
+      'coverage': {
+        'hits': [384],
+        'misses': []
+      }
+    };
+
+    var params = {
+      'reports': ['Coverage'],
+      'scriptId': func.location.script.id,
+      'tokenPos': func.location.tokenPos,
+      'endTokenPos': func.location.endTokenPos,
+      'forceCompile': true
+    };
+    var report = await isolate.invokeRpcNoUpgrade('getSourceReport', params);
+    expect(report['type'], equals('SourceReport'));
+    expect(report['ranges'].length, 1);
+    expect(report['ranges'][0], equals(expectedRange));
+    expect(report['scripts'].length, 1);
+    expect(report['scripts'][0]['uri'],
+        endsWith('coverage_leaf_function_test.dart'));
+  },
+];
+
+main(args) => runIsolateTests(args, tests, testeeConcurrent: testFunction);
diff --git a/runtime/observatory_2/tests/service_2/coverage_optimized_function_test.dart b/runtime/observatory_2/tests/service_2/coverage_optimized_function_test.dart
new file mode 100644
index 0000000..d99072d
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/coverage_optimized_function_test.dart
@@ -0,0 +1,66 @@
+// Copyright (c) 2017, 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.
+// VMOptions=--no_background_compilation --optimization_counter_threshold=10
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+import 'service_test_common.dart';
+import 'dart:developer';
+
+String optimizedFunction() {
+  return 5.toString() + 3.toString();
+}
+
+void testFunction() {
+  for (var i = 0; i < 20; i++) {
+    optimizedFunction();
+  }
+  debugger();
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  (Isolate isolate) async {
+    var stack = await isolate.getStack();
+
+    // Make sure we are in the right place.
+    expect(stack.type, equals('Stack'));
+    expect(stack['frames'].length, greaterThanOrEqualTo(1));
+    expect(stack['frames'][0].function.name, equals('testFunction'));
+
+    var root = isolate.rootLibrary;
+    await root.load();
+    var func = root.functions.singleWhere((f) => f.name == 'optimizedFunction');
+    await func.load();
+
+    var expectedRange = {
+      'scriptIndex': 0,
+      'startPos': 461,
+      'endPos': 528,
+      'compiled': true,
+      'coverage': {
+        'hits': [461, 501, 512, 516],
+        'misses': []
+      }
+    };
+
+    var params = {
+      'reports': ['Coverage'],
+      'scriptId': func.location.script.id,
+      'tokenPos': func.location.tokenPos,
+      'endTokenPos': func.location.endTokenPos,
+      'forceCompile': true
+    };
+    var report = await isolate.invokeRpcNoUpgrade('getSourceReport', params);
+    expect(report['type'], equals('SourceReport'));
+    expect(report['ranges'].length, 1);
+    expect(report['ranges'][0], equals(expectedRange));
+    expect(report['scripts'].length, 1);
+    expect(report['scripts'][0]['uri'],
+        endsWith('coverage_optimized_function_test.dart'));
+  },
+];
+
+main(args) => runIsolateTests(args, tests, testeeConcurrent: testFunction);
diff --git a/runtime/observatory_2/tests/service_2/dds_log_history_size_gigantic_test.dart b/runtime/observatory_2/tests/service_2/dds_log_history_size_gigantic_test.dart
new file mode 100644
index 0000000..717cbddec
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/dds_log_history_size_gigantic_test.dart
@@ -0,0 +1,83 @@
+// Copyright (c) 2020, 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.
+
+import 'dart:async';
+import 'dart:developer';
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+Future testMain() async {
+  // Log a total of 30 messages
+  for (int i = 1; i <= maxLogHistorySize + 10; ++i) {
+    log('All work and no play makes Ben a dull boy ($i)');
+  }
+  debugger();
+}
+
+const maxLogHistorySize = 100000;
+
+Future setLogHistorySize(Isolate isolate, int size) async {
+  return await isolate.invokeRpcNoUpgrade('setLogHistorySize', {
+    'size': size,
+  });
+}
+
+Future<int> getLogHistorySize(Isolate isolate) async {
+  final result = await isolate.invokeRpcNoUpgrade('getLogHistorySize', {});
+  expect(result['type'], 'Size');
+  return result['size'];
+}
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  (Isolate isolate) async {
+    final initialSize = await getLogHistorySize(isolate);
+    try {
+      await setLogHistorySize(isolate, maxLogHistorySize + 1);
+    } on ServerRpcException catch (e) {
+      expect(e.message, "'size' must be less than $maxLogHistorySize");
+    }
+    expect(await getLogHistorySize(isolate), initialSize);
+  },
+  (Isolate isolate) async {
+    final result = await setLogHistorySize(isolate, maxLogHistorySize);
+    expect(result['type'], 'Success');
+    expect(await getLogHistorySize(isolate), maxLogHistorySize);
+  },
+  resumeIsolate,
+  hasStoppedAtBreakpoint,
+  (Isolate isolate) async {
+    print("Starting step 6");
+    final completer = Completer<void>();
+
+    await Future.delayed(const Duration(seconds: 1));
+
+    int i = 11;
+    await subscribeToStream(isolate.vm, 'Logging', (event) async {
+      expect(
+        event.logRecord['message'].valueAsString,
+        'All work and no play makes Ben a dull boy ($i)',
+      );
+      i++;
+
+      if (i == maxLogHistorySize + 10) {
+        await cancelStreamSubscription('Logging');
+        completer.complete();
+      }
+    });
+    await completer.future;
+  },
+];
+
+main(args) => runIsolateTests(
+      args,
+      tests,
+      enableService: false, // DDS specific feature
+      testeeConcurrent: testMain,
+      pause_on_start: true,
+      pause_on_exit: true,
+    );
diff --git a/runtime/observatory_2/tests/service_2/dds_log_history_size_simple_test.dart b/runtime/observatory_2/tests/service_2/dds_log_history_size_simple_test.dart
new file mode 100644
index 0000000..6287ffb
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/dds_log_history_size_simple_test.dart
@@ -0,0 +1,98 @@
+// Copyright (c) 2020, 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.
+
+import 'dart:async';
+import 'dart:developer';
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'client_resume_approvals_common.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+Future testMain() async {
+  // Log a total of 9 messages
+  for (int i = 1; i <= 9; ++i) {
+    log('log$i');
+  }
+  debugger();
+  log('log10');
+}
+
+Future setLogHistorySize(Isolate isolate, int size) async {
+  return await isolate.invokeRpcNoUpgrade('setLogHistorySize', {
+    'size': size,
+  });
+}
+
+Future<int> getLogHistorySize(Isolate isolate) async {
+  final result = await isolate.invokeRpcNoUpgrade('getLogHistorySize', {});
+  expect(result['type'], 'Size');
+  return result['size'];
+}
+
+var tests = <IsolateTest>[
+  isPausedAtStart,
+  resumeIsolate,
+  (Isolate isolate) async {
+    // Check that resizing does the right thing.
+    final result = await setLogHistorySize(isolate, 10);
+    expect(result['type'], 'Success');
+    expect(await getLogHistorySize(isolate), 10);
+
+    final completer = Completer<void>();
+
+    int i = 1;
+    await subscribeToStream(isolate.vm, 'Logging', (event) async {
+      expect(event.logRecord['message'].valueAsString, 'log$i');
+      i++;
+
+      if (i == 10) {
+        await cancelStreamSubscription('Logging');
+        completer.complete();
+      } else if (i > 10) {
+        fail('Too many log messages');
+      }
+    });
+    await completer.future;
+  },
+  (Isolate isolate) async {
+    // Resize to be smaller
+    final result = await setLogHistorySize(isolate, 5);
+    expect(result['type'], 'Success');
+    expect(await getLogHistorySize(isolate), 5);
+  },
+  resumeIsolate,
+  (Isolate isolate) async {
+    final completer = Completer<void>();
+
+    // Create a new client as we want to get log messages from the entire
+    // history buffer.
+    final client = await createClient(isolate.vm);
+
+    int i = 6;
+    await subscribeToStream(client, 'Logging', (event) async {
+      expect(event.logRecord['message'].valueAsString, 'log$i');
+      i++;
+
+      if (i == 11) {
+        await cancelStreamSubscription('Logging');
+        completer.complete();
+      } else if (i > 11) {
+        fail('Too many log messages');
+      }
+    });
+    await completer.future;
+    await client.disconnect();
+  },
+];
+
+main(args) => runIsolateTests(
+      args,
+      tests,
+      enableService: false, // DDS specific feature
+      testeeConcurrent: testMain,
+      pause_on_start: true,
+      pause_on_exit: true,
+    );
diff --git a/runtime/observatory_2/tests/service_2/dds_log_history_size_test.dart b/runtime/observatory_2/tests/service_2/dds_log_history_size_test.dart
new file mode 100644
index 0000000..ce1a51e
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/dds_log_history_size_test.dart
@@ -0,0 +1,91 @@
+// Copyright (c) 2020, 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.
+
+import 'dart:async';
+import 'dart:developer';
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+Future testMain() async {
+  // Initial logging history should be 0, so these messages won't be buffered.
+  log('log1');
+  log('log2');
+
+  // Setting the log history length does not apply retroactively.
+  debugger();
+
+  // Log a total of 30 messages
+  for (int i = 3; i <= 30; ++i) {
+    log('log$i');
+  }
+}
+
+Future setLogHistorySize(Isolate isolate, int size) async {
+  return await isolate.invokeRpcNoUpgrade('setLogHistorySize', {
+    'size': size,
+  });
+}
+
+Future<int> getLogHistorySize(Isolate isolate) async {
+  final result = await isolate.invokeRpcNoUpgrade('getLogHistorySize', {});
+  expect(result['type'], 'Size');
+  return result['size'];
+}
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  (Isolate isolate) async {
+    final result = await setLogHistorySize(isolate, 0);
+    expect(result['type'], 'Success');
+    expect(await getLogHistorySize(isolate), 0);
+  },
+  resumeIsolate,
+  hasStoppedAtBreakpoint,
+  (Isolate isolate) async {
+    final result = await setLogHistorySize(isolate, 20);
+    expect(await getLogHistorySize(isolate), 20);
+    expect(result['type'], 'Success');
+  },
+  resumeIsolate,
+  (Isolate isolate) async {
+    final completer = Completer<void>();
+
+    await Future.delayed(const Duration(seconds: 1));
+
+    // With the log history set to 20, the first log message should be 'log11'
+    int i = 11;
+    await subscribeToStream(isolate.vm, 'Logging', (event) async {
+      expect(event.logRecord['message'].valueAsString, 'log$i');
+      i++;
+
+      if (i == 30) {
+        await cancelStreamSubscription('Logging');
+        completer.complete();
+      }
+    });
+    await completer.future;
+  },
+  (Isolate isolate) async {
+    try {
+      // Try to set an invalid history size
+      await setLogHistorySize(isolate, -1);
+      fail('Successfully set invalid size');
+    } on ServerRpcException catch (e) {
+      expect(e.message, "'size' must be greater or equal to zero");
+    }
+    expect(await getLogHistorySize(isolate), 20);
+  }
+];
+
+main(args) => runIsolateTests(
+      args,
+      tests,
+      enableService: false, // DDS specific feature
+      testeeConcurrent: testMain,
+      pause_on_start: true,
+      pause_on_exit: true,
+    );
diff --git a/runtime/observatory_2/tests/service_2/debugger_inspect_test.dart b/runtime/observatory_2/tests/service_2/debugger_inspect_test.dart
new file mode 100644
index 0000000..26213fa
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/debugger_inspect_test.dart
@@ -0,0 +1,39 @@
+// Copyright (c) 2015, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+import 'dart:async';
+import 'dart:developer';
+
+class Point {
+  int x, y;
+  Point(this.x, this.y);
+}
+
+void testeeDo() {
+  inspect(new Point(3, 4));
+}
+
+var tests = <IsolateTest>[
+  (Isolate isolate) async {
+    Completer completer = new Completer();
+    var stream = await isolate.vm.getEventStream(VM.kDebugStream);
+    var subscription;
+    subscription = stream.listen((ServiceEvent event) {
+      if (event.kind == ServiceEvent.kInspect) {
+        expect(event.inspectee.clazz.name, equals('Point'));
+        subscription.cancel();
+        completer.complete();
+      }
+    });
+
+    // Start listening for events first.
+    await isolate.rootLibrary.evaluate('testeeDo()');
+    return completer.future;
+  },
+];
+
+main(args) => runIsolateTests(args, tests);
diff --git a/runtime/observatory_2/tests/service_2/debugger_location_second_test.dart b/runtime/observatory_2/tests/service_2/debugger_location_second_test.dart
new file mode 100644
index 0000000..4f27730
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/debugger_location_second_test.dart
@@ -0,0 +1,208 @@
+// Copyright (c) 2016, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:observatory_2/debugger.dart';
+import 'package:test/test.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+import 'dart:async';
+import 'dart:developer';
+
+const int LINE_A = 21;
+const int LINE_B = 110;
+const int LINE_C = 11;
+
+void testFunction() {
+  int i = 0;
+  while (i == 0) {
+    debugger();
+    print('loop'); // Line A.
+    print('loop');
+  }
+}
+
+class TestDebugger extends Debugger {
+  TestDebugger(this.isolate, this.stack);
+
+  VM get vm => isolate.vm;
+  Isolate isolate;
+  ServiceMap stack;
+  int currentFrame = 0;
+}
+
+void debugger_location_dummy_function() {}
+
+class DebuggerLocationTestFoo {
+  DebuggerLocationTestFoo(this.field);
+  DebuggerLocationTestFoo.named();
+
+  void method() {}
+  void madness() {}
+
+  int field;
+}
+
+class DebuggerLocationTestBar {}
+
+Future<Debugger> initDebugger(Isolate isolate) {
+  return isolate.getStack().then((stack) {
+    return new TestDebugger(isolate, stack);
+  });
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+
+// Load the isolate's libraries
+  (Isolate isolate) async {
+    for (var lib in isolate.libraries) {
+      await lib.load();
+    }
+  },
+
+// Parse method
+  (Isolate isolate) async {
+    var debugger = await initDebugger(isolate);
+    var loc = await DebuggerLocation.parse(
+        debugger, 'DebuggerLocationTestFoo.method');
+    expect(loc.valid, isTrue);
+    expect(loc.toString(), equals('DebuggerLocationTestFoo.method'));
+  },
+
+// Parse method
+  (Isolate isolate) async {
+    var debugger = await initDebugger(isolate);
+    var loc = await DebuggerLocation.parse(
+        debugger, 'DebuggerLocationTestFoo.field=');
+    expect(loc.valid, isTrue);
+    expect(loc.toString(), equals('DebuggerLocationTestFoo.field='));
+  },
+
+// Parse bad method
+  (Isolate isolate) async {
+    var debugger = await initDebugger(isolate);
+    var loc = await DebuggerLocation.parse(
+        debugger, 'DebuggerLocationTestFoo.missing');
+    expect(loc.valid, isFalse);
+    expect(
+        loc.toString(),
+        equals('invalid source location '
+            '(Function \'DebuggerLocationTestFoo.missing\' not found)'));
+  },
+
+// Complete function + script
+  (Isolate isolate) async {
+    var debugger = await initDebugger(isolate);
+    var completions = await DebuggerLocation.complete(debugger, 'debugger_loc');
+    expect(
+        completions.toString(),
+        equals('[debugger_location_dummy_function,'
+            ' debugger_location.dart:,'
+            ' debugger_location_second_test.dart:]'));
+  },
+
+// Complete class
+  (Isolate isolate) async {
+    var debugger = await initDebugger(isolate);
+    var completions =
+        await DebuggerLocation.complete(debugger, 'DebuggerLocationTe');
+    expect(
+        completions.toString(),
+        equals('[DebuggerLocationTestBar,'
+            ' DebuggerLocationTestFoo]'));
+  },
+
+// No completions: unqualified name
+  (Isolate isolate) async {
+    var debugger = await initDebugger(isolate);
+    var completions =
+        await DebuggerLocation.complete(debugger, 'debugger_locXYZZY');
+    expect(completions.toString(), equals('[]'));
+  },
+
+// Complete method
+  (Isolate isolate) async {
+    var debugger = await initDebugger(isolate);
+    var completions =
+        await DebuggerLocation.complete(debugger, 'DebuggerLocationTestFoo.m');
+    expect(
+        completions.toString(),
+        equals('[DebuggerLocationTestFoo.madness,'
+            ' DebuggerLocationTestFoo.method]'));
+  },
+
+// No completions: qualified name
+  (Isolate isolate) async {
+    var debugger = await initDebugger(isolate);
+    var completions =
+        await DebuggerLocation.complete(debugger, 'DebuggerLocationTestFoo.q');
+    expect(completions.toString(), equals('[]'));
+  },
+
+// Complete script
+  (Isolate isolate) async {
+    var debugger = await initDebugger(isolate);
+    var completions = await DebuggerLocation.complete(
+        debugger, 'debugger_location_second_te');
+    expect(completions.toString(),
+        equals('[debugger_location_second_test.dart:]'));
+  },
+
+// Complete script:line
+  (Isolate isolate) async {
+    var debugger = await initDebugger(isolate);
+    var completions = await DebuggerLocation.complete(
+        debugger, 'debugger_location_second_test.dart:11');
+    expect(
+        completions.toString(),
+        equals('[debugger_location_second_test.dart:${LINE_B + 0} ,'
+            ' debugger_location_second_test.dart:${LINE_B + 0}:,'
+            ' debugger_location_second_test.dart:${LINE_B + 1} ,'
+            ' debugger_location_second_test.dart:${LINE_B + 1}:,'
+            ' debugger_location_second_test.dart:${LINE_B + 2} ,'
+            ' debugger_location_second_test.dart:${LINE_B + 2}:,'
+            ' debugger_location_second_test.dart:${LINE_B + 3} ,'
+            ' debugger_location_second_test.dart:${LINE_B + 3}:,'
+            ' debugger_location_second_test.dart:${LINE_B + 4} ,'
+            ' debugger_location_second_test.dart:${LINE_B + 4}:,'
+            ' debugger_location_second_test.dart:${LINE_B + 5} ,'
+            ' debugger_location_second_test.dart:${LINE_B + 5}:,'
+            ' debugger_location_second_test.dart:${LINE_B + 8} ,'
+            ' debugger_location_second_test.dart:${LINE_B + 8}:,'
+            ' debugger_location_second_test.dart:${LINE_B + 9} ,'
+            ' debugger_location_second_test.dart:${LINE_B + 9}:]'));
+  },
+
+// Complete script:line:col
+  (Isolate isolate) async {
+    var debugger = await initDebugger(isolate);
+    var completions = await DebuggerLocation.complete(
+        debugger, 'debugger_location_second_test.dart:$LINE_C:2');
+    expect(
+        completions.toString(),
+        equals('[debugger_location_second_test.dart:$LINE_C:2 ,'
+            ' debugger_location_second_test.dart:$LINE_C:20 ,'
+            ' debugger_location_second_test.dart:$LINE_C:21 ,'
+            ' debugger_location_second_test.dart:$LINE_C:22 ,'
+            ' debugger_location_second_test.dart:$LINE_C:23 ,'
+            ' debugger_location_second_test.dart:$LINE_C:24 ]'));
+  },
+
+// Complete without the script name.
+  (Isolate isolate) async {
+    var debugger = await initDebugger(isolate);
+    var completions = await DebuggerLocation.complete(debugger, '$LINE_C:2');
+    expect(
+        completions.toString(),
+        equals('[debugger_location_second_test.dart:$LINE_C:2 ,'
+            ' debugger_location_second_test.dart:$LINE_C:20 ,'
+            ' debugger_location_second_test.dart:$LINE_C:21 ,'
+            ' debugger_location_second_test.dart:$LINE_C:22 ,'
+            ' debugger_location_second_test.dart:$LINE_C:23 ,'
+            ' debugger_location_second_test.dart:$LINE_C:24 ]'));
+  },
+];
+
+main(args) => runIsolateTests(args, tests, testeeConcurrent: testFunction);
diff --git a/runtime/observatory_2/tests/service_2/debugger_location_test.dart b/runtime/observatory_2/tests/service_2/debugger_location_test.dart
new file mode 100644
index 0000000..4ecadb2
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/debugger_location_test.dart
@@ -0,0 +1,154 @@
+// Copyright (c) 2015, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:observatory_2/debugger.dart';
+import 'package:test/test.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+import 'dart:async';
+import 'dart:developer';
+
+const int LINE_A = 21;
+const int LINE_B = 111;
+const int LINE_C = 11;
+
+void testFunction() {
+  int i = 0;
+  while (i == 0) {
+    debugger();
+    print('loop'); // Line A.
+    print('loop');
+  }
+}
+
+class TestDebugger extends Debugger {
+  TestDebugger(this.isolate, this.stack);
+
+  VM get vm => isolate.vm;
+  Isolate isolate;
+  ServiceMap stack;
+  int currentFrame = 0;
+}
+
+void debugger_location_dummy_function() {}
+
+class DebuggerLocationTestFoo {
+  DebuggerLocationTestFoo(this.field);
+  DebuggerLocationTestFoo.named();
+
+  void method() {}
+  void madness() {}
+
+  int field;
+}
+
+class DebuggerLocationTestBar {}
+
+Future<Debugger> initDebugger(Isolate isolate) {
+  return isolate.getStack().then((stack) {
+    return new TestDebugger(isolate, stack);
+  });
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+
+// Parse '' => current position
+  (Isolate isolate) async {
+    var debugger = await initDebugger(isolate);
+    var loc = await DebuggerLocation.parse(debugger, '');
+    expect(loc.valid, isTrue);
+    expect(loc.toString(), equals('debugger_location_test.dart:$LINE_A:5'));
+  },
+
+// Parse line
+  (Isolate isolate) async {
+    var debugger = await initDebugger(isolate);
+    var loc = await DebuggerLocation.parse(debugger, '18');
+    expect(loc.valid, isTrue);
+    expect(loc.toString(), equals('debugger_location_test.dart:18'));
+  },
+
+// Parse line + col
+  (Isolate isolate) async {
+    var debugger = await initDebugger(isolate);
+    var loc = await DebuggerLocation.parse(debugger, '16:11');
+    expect(loc.valid, isTrue);
+    expect(loc.toString(), equals('debugger_location_test.dart:16:11'));
+  },
+
+// Parse script + line
+  (Isolate isolate) async {
+    var debugger = await initDebugger(isolate);
+    var loc = await DebuggerLocation.parse(
+        debugger, 'debugger_location_test.dart:16');
+    expect(loc.valid, isTrue);
+    expect(loc.toString(), equals('debugger_location_test.dart:16'));
+  },
+
+// Parse script + line + col
+  (Isolate isolate) async {
+    var debugger = await initDebugger(isolate);
+    var loc = await DebuggerLocation.parse(
+        debugger, 'debugger_location_test.dart:16:11');
+    expect(loc.valid, isTrue);
+    expect(loc.toString(), equals('debugger_location_test.dart:16:11'));
+  },
+
+// Parse bad script
+  (Isolate isolate) async {
+    var debugger = await initDebugger(isolate);
+    var loc = await DebuggerLocation.parse(debugger, 'bad.dart:15');
+    expect(loc.valid, isFalse);
+    expect(loc.toString(),
+        equals('invalid source location (Script \'bad.dart\' not found)'));
+  },
+
+// Parse function
+  (Isolate isolate) async {
+    var debugger = await initDebugger(isolate);
+    var loc = await DebuggerLocation.parse(debugger, 'testFunction');
+    expect(loc.valid, isTrue);
+    expect(loc.toString(), equals('testFunction'));
+  },
+
+// Parse bad function
+  (Isolate isolate) async {
+    var debugger = await initDebugger(isolate);
+    var loc = await DebuggerLocation.parse(debugger, 'doesNotReallyExist');
+    expect(loc.valid, isFalse);
+    expect(
+        loc.toString(),
+        equals(
+            'invalid source location (Function \'doesNotReallyExist\' not found)'));
+  },
+
+// Parse constructor
+  (Isolate isolate) async {
+    var debugger = await initDebugger(isolate);
+    var loc = await DebuggerLocation.parse(debugger, 'DebuggerLocationTestFoo');
+    expect(loc.valid, isTrue);
+    // TODO(turnidge): Printing a constructor currently adds
+    // another class qualifier at the front.  Do we want to change
+    // this to be more consistent?
+    expect(loc.toString(),
+        equals('DebuggerLocationTestFoo.DebuggerLocationTestFoo'));
+  },
+
+// Parse named constructor
+  (Isolate isolate) async {
+    var debugger = await initDebugger(isolate);
+    var loc =
+        await DebuggerLocation.parse(debugger, 'DebuggerLocationTestFoo.named');
+    expect(loc.valid, isTrue);
+    // TODO(turnidge): Printing a constructor currently adds
+    // another class qualifier at the front.  Do we want to change
+    // this to be more consistent?
+    expect(loc.toString(),
+        equals('DebuggerLocationTestFoo.DebuggerLocationTestFoo.named'));
+  },
+];
+
+main(args) => runIsolateTests(args, tests, testeeConcurrent: testFunction);
diff --git a/runtime/observatory_2/tests/service_2/debugging_inlined_finally_test.dart b/runtime/observatory_2/tests/service_2/debugging_inlined_finally_test.dart
new file mode 100644
index 0000000..149f610
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/debugging_inlined_finally_test.dart
@@ -0,0 +1,133 @@
+// Copyright (c) 2015, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+import 'dart:developer';
+
+const int LINE_A = 23;
+const int LINE_B = 26;
+const int LINE_C = 29;
+
+testFunction() {
+  debugger();
+  var a;
+  try {
+    var b;
+    try {
+      for (int i = 0; i < 10; i++) {
+        var x = () => i + a + b;
+        return x; // LINE_A
+      }
+    } finally {
+      b = 10; // LINE_B
+    }
+  } finally {
+    a = 1; // LINE_C
+  }
+}
+
+testMain() {
+  var f = testFunction();
+  expect(f(), equals(11));
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+
+// Add breakpoint
+  (Isolate isolate) async {
+    await isolate.rootLibrary.load();
+
+    var script = isolate.rootLibrary.scripts[0];
+    await script.load();
+
+    // Add 3 breakpoints.
+    {
+      var result = await isolate.addBreakpoint(script, LINE_A);
+      expect(result is Breakpoint, isTrue);
+      Breakpoint bpt = result;
+      expect(bpt.type, equals('Breakpoint'));
+      expect(bpt.location.script.id, equals(script.id));
+      expect(bpt.location.script.tokenToLine(bpt.location.tokenPos),
+          equals(LINE_A));
+      expect(isolate.breakpoints.length, equals(1));
+    }
+
+    {
+      var result = await isolate.addBreakpoint(script, LINE_B);
+      expect(result is Breakpoint, isTrue);
+      Breakpoint bpt = result;
+      expect(bpt.type, equals('Breakpoint'));
+      expect(bpt.location.script.id, equals(script.id));
+      expect(bpt.location.script.tokenToLine(bpt.location.tokenPos),
+          equals(LINE_B));
+      expect(isolate.breakpoints.length, equals(2));
+    }
+
+    {
+      var result = await isolate.addBreakpoint(script, LINE_C);
+      expect(result is Breakpoint, isTrue);
+      Breakpoint bpt = result;
+      expect(bpt.type, equals('Breakpoint'));
+      expect(bpt.location.script.id, equals(script.id));
+      expect(bpt.location.script.tokenToLine(bpt.location.tokenPos),
+          equals(LINE_C));
+      expect(isolate.breakpoints.length, equals(3));
+    }
+
+    // Wait for breakpoint events.
+  },
+
+  resumeIsolate,
+
+  hasStoppedAtBreakpoint,
+
+// We are at the breakpoint on line LINE_A.
+  (Isolate isolate) async {
+    ServiceMap stack = await isolate.getStack();
+    expect(stack.type, equals('Stack'));
+    expect(stack['frames'].length, greaterThanOrEqualTo(1));
+
+    Script script = stack['frames'][0].location.script;
+    expect(script.tokenToLine(stack['frames'][0].location.tokenPos),
+        equals(LINE_A));
+  },
+
+  resumeIsolate,
+
+  hasStoppedAtBreakpoint,
+
+// We are at the breakpoint on line LINE_B.
+  (Isolate isolate) async {
+    ServiceMap stack = await isolate.getStack();
+    expect(stack.type, equals('Stack'));
+    expect(stack['frames'].length, greaterThanOrEqualTo(1));
+
+    Script script = stack['frames'][0].location.script;
+    expect(script.tokenToLine(stack['frames'][0].location.tokenPos),
+        equals(LINE_B));
+  },
+
+  resumeIsolate,
+
+  hasStoppedAtBreakpoint,
+
+// We are at the breakpoint on line LINE_C.
+  (Isolate isolate) async {
+    ServiceMap stack = await isolate.getStack();
+    expect(stack.type, equals('Stack'));
+    expect(stack['frames'].length, greaterThanOrEqualTo(1));
+
+    Script script = stack['frames'][0].location.script;
+    expect(script.tokenToLine(stack['frames'][0].location.tokenPos),
+        equals(LINE_C));
+  },
+
+  resumeIsolate,
+];
+
+main(args) => runIsolateTests(args, tests, testeeConcurrent: testMain);
diff --git a/runtime/observatory_2/tests/service_2/debugging_test.dart b/runtime/observatory_2/tests/service_2/debugging_test.dart
new file mode 100644
index 0000000..15daf6b
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/debugging_test.dart
@@ -0,0 +1,209 @@
+// Copyright (c) 2015, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+import 'dart:async';
+
+int counter = 0;
+
+void periodicTask(_) {
+  counter++;
+  counter++; // Line 15.  We set our breakpoint here.
+  counter++;
+  if (counter % 300 == 0) {
+    print('counter = $counter');
+  }
+}
+
+void startTimer() {
+  new Timer.periodic(const Duration(milliseconds: 10), periodicTask);
+}
+
+var tests = <IsolateTest>[
+// Pause
+  (Isolate isolate) async {
+    Completer completer = new Completer();
+    var stream = await isolate.vm.getEventStream(VM.kDebugStream);
+    var subscription;
+    subscription = stream.listen((ServiceEvent event) {
+      if (event.kind == ServiceEvent.kPauseInterrupted) {
+        subscription.cancel();
+        completer.complete();
+      }
+    });
+    isolate.pause();
+    await completer.future;
+  },
+
+// Resume
+  (Isolate isolate) async {
+    Completer completer = new Completer();
+    var stream = await isolate.vm.getEventStream(VM.kDebugStream);
+    var subscription;
+    subscription = stream.listen((ServiceEvent event) {
+      if (event.kind == ServiceEvent.kResume) {
+        subscription.cancel();
+        completer.complete();
+      }
+    });
+    isolate.resume();
+    await completer.future;
+  },
+
+// Add breakpoint
+  (Isolate isolate) async {
+    await isolate.rootLibrary.load();
+
+    // Set up a listener to wait for breakpoint events.
+    Completer completer = new Completer();
+    var stream = await isolate.vm.getEventStream(VM.kDebugStream);
+    var subscription;
+    subscription = stream.listen((ServiceEvent event) {
+      if (event.kind == ServiceEvent.kPauseBreakpoint) {
+        print('Breakpoint reached');
+        subscription.cancel();
+        completer.complete();
+      }
+    });
+
+    var script = isolate.rootLibrary.scripts[0];
+    await script.load();
+
+    // Add the breakpoint.
+    var result = await isolate.addBreakpoint(script, 15);
+    expect(result is Breakpoint, isTrue);
+    Breakpoint bpt = result;
+    expect(bpt.type, equals('Breakpoint'));
+    expect(bpt.location.script.id, equals(script.id));
+    expect(bpt.location.script.tokenToLine(bpt.location.tokenPos), equals(15));
+    expect(isolate.breakpoints.length, equals(1));
+
+    await completer.future; // Wait for breakpoint events.
+  },
+
+// We are at the breakpoint on line 15.
+  (Isolate isolate) async {
+    ServiceMap stack = await isolate.getStack();
+    expect(stack.type, equals('Stack'));
+    expect(stack['frames'].length, greaterThanOrEqualTo(1));
+
+    Script script = stack['frames'][0].location.script;
+    expect(script.name, endsWith('debugging_test.dart'));
+    expect(
+        script.tokenToLine(stack['frames'][0].location.tokenPos), equals(15));
+  },
+
+// Stepping
+  (Isolate isolate) async {
+    // Set up a listener to wait for breakpoint events.
+    Completer completer = new Completer();
+    var stream = await isolate.vm.getEventStream(VM.kDebugStream);
+    var subscription;
+    subscription = stream.listen((ServiceEvent event) {
+      if (event.kind == ServiceEvent.kPauseBreakpoint) {
+        print('Breakpoint reached');
+        subscription.cancel();
+        completer.complete();
+      }
+    });
+
+    await isolate.stepOver();
+    await completer.future; // Wait for breakpoint events.
+  },
+
+// We are now at line 16.
+  (Isolate isolate) async {
+    ServiceMap stack = await isolate.getStack();
+    expect(stack.type, equals('Stack'));
+    expect(stack['frames'].length, greaterThanOrEqualTo(1));
+
+    Script script = stack['frames'][0].location.script;
+    expect(script.name, endsWith('debugging_test.dart'));
+    expect(
+        script.tokenToLine(stack['frames'][0].location.tokenPos), equals(16));
+  },
+
+// Remove breakpoint
+  (Isolate isolate) async {
+    // Set up a listener to wait for breakpoint events.
+    Completer completer = new Completer();
+    var stream = await isolate.vm.getEventStream(VM.kDebugStream);
+    var subscription;
+    subscription = stream.listen((ServiceEvent event) {
+      if (event.kind == ServiceEvent.kBreakpointRemoved) {
+        print('Breakpoint removed');
+        expect(isolate.breakpoints.length, equals(0));
+        subscription.cancel();
+        completer.complete();
+      }
+    });
+
+    expect(isolate.breakpoints.length, equals(1));
+    var bpt = isolate.breakpoints.values.first;
+    await isolate.removeBreakpoint(bpt);
+    await completer.future;
+  },
+
+// Resume
+  (Isolate isolate) async {
+    Completer completer = new Completer();
+    var stream = await isolate.vm.getEventStream(VM.kDebugStream);
+    var subscription;
+    subscription = stream.listen((ServiceEvent event) {
+      if (event.kind == ServiceEvent.kResume) {
+        subscription.cancel();
+        completer.complete();
+      }
+    });
+    isolate.resume();
+    await completer.future;
+  },
+
+// Add breakpoint at function entry
+  (Isolate isolate) async {
+    // Set up a listener to wait for breakpoint events.
+    Completer completer = new Completer();
+    var stream = await isolate.vm.getEventStream(VM.kDebugStream);
+    var subscription;
+    subscription = stream.listen((ServiceEvent event) {
+      if (event.kind == ServiceEvent.kPauseBreakpoint) {
+        print('Breakpoint reached');
+        subscription.cancel();
+        completer.complete();
+      }
+    });
+
+    // Find a specific function.
+    ServiceFunction function = isolate.rootLibrary.functions
+        .firstWhere((f) => f.name == 'periodicTask');
+    expect(function, isNotNull);
+
+    // Add the breakpoint at function entry
+    var result = await isolate.addBreakpointAtEntry(function);
+    expect(result is Breakpoint, isTrue);
+    Breakpoint bpt = result;
+    expect(bpt.type, equals('Breakpoint'));
+    expect(bpt.location.script.name, equals('debugging_test.dart'));
+    expect(bpt.location.script.tokenToLine(bpt.location.tokenPos), equals(12));
+    expect(isolate.breakpoints.length, equals(1));
+
+    await completer.future; // Wait for breakpoint events.
+  },
+
+// We are now at line 13.
+  (Isolate isolate) async {
+    ServiceMap stack = await isolate.getStack();
+    expect(stack.type, equals('Stack'));
+    expect(stack['frames'].length, greaterThanOrEqualTo(1));
+
+    Script script = stack['frames'][0].location.script;
+    expect(script.name, endsWith('debugging_test.dart'));
+    expect(
+        script.tokenToLine(stack['frames'][0].location.tokenPos), equals(12));
+  },
+];
+
+main(args) => runIsolateTests(args, tests, testeeBefore: startTimer);
diff --git a/runtime/observatory_2/tests/service_2/deferred_library.dart b/runtime/observatory_2/tests/service_2/deferred_library.dart
new file mode 100644
index 0000000..9a40be6
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/deferred_library.dart
@@ -0,0 +1,18 @@
+// Copyright (c) 2015, 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.
+
+library deferred_library;
+
+int value = 0;
+
+int decValue(int amount) {
+  value -= amount;
+  return amount;
+}
+
+void deferredTest() {
+  decValue(decValue(1)); // line 15
+
+  decValue(decValue(1)); // line 17
+}
diff --git a/runtime/observatory_2/tests/service_2/dev_fs_http_put_test.dart b/runtime/observatory_2/tests/service_2/dev_fs_http_put_test.dart
new file mode 100644
index 0000000..e0f48ec
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/dev_fs_http_put_test.dart
@@ -0,0 +1,98 @@
+// Copyright (c) 2016, 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.
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:io';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+
+Future<String> readResponse(HttpClientResponse response) {
+  var completer = new Completer<String>();
+  var contents = new StringBuffer();
+  response.cast<List<int>>().transform(utf8.decoder).listen((String data) {
+    contents.write(data);
+  }, onDone: () => completer.complete(contents.toString()));
+  return completer.future;
+}
+
+var tests = <VMTest>[
+  // Write a file with the ? character in the filename.
+  (VM vm) async {
+    var fsId = 'test';
+    var filePath = '/foo/bar.dat';
+    var fileContents = [0, 1, 2, 3, 4, 5, 6, 255];
+    var fileContentsBase64 = base64Encode(fileContents);
+
+    var result;
+    // Create DevFS.
+    result = await vm.invokeRpcNoUpgrade('_createDevFS', {'fsName': fsId});
+    expect(result['type'], equals('FileSystem'));
+    expect(result['name'], equals(fsId));
+    expect(result['uri'], isA<String>());
+
+    // Write the file by issuing an HTTP PUT.
+    HttpClient client = new HttpClient();
+    HttpClientRequest request =
+        await client.putUrl(Uri.parse(serviceHttpAddress));
+    request.headers.add('dev_fs_name', fsId);
+    request.headers.add('dev_fs_path', filePath);
+    request.add(gzip.encode([9]));
+    HttpClientResponse response = await request.close();
+    String responseBody = await readResponse(response);
+    result = jsonDecode(responseBody);
+    expect(result['result']['type'], equals('Success'));
+
+    // Trigger an error by issuing an HTTP PUT.
+    request = await client.putUrl(Uri.parse(serviceHttpAddress));
+    request.headers.add('dev_fs_name', fsId);
+    // omit the 'dev_fs_path' parameter.
+    request.write(gzip.encode(fileContents));
+    response = await request.close();
+    responseBody = await readResponse(response);
+    result = jsonDecode(responseBody);
+    Map error = result['error']['data'];
+    expect(error, isNotNull);
+    expect(error['details'].contains("expects the 'path' parameter"), isTrue);
+
+    // Write the file again but this time with the true file contents.
+    client = new HttpClient();
+    request = await client.putUrl(Uri.parse(serviceHttpAddress));
+    request.headers.add('dev_fs_name', fsId);
+    request.headers.add('dev_fs_path', filePath);
+    request.add(gzip.encode(fileContents));
+    response = await request.close();
+    responseBody = await readResponse(response);
+    result = jsonDecode(responseBody);
+    expect(result['result']['type'], equals('Success'));
+
+    // Close the HTTP client.
+    client.close();
+
+    // Read the file back.
+    result = await vm.invokeRpcNoUpgrade('_readDevFSFile', {
+      'fsName': fsId,
+      'path': filePath,
+    });
+    expect(result['type'], equals('FSFile'));
+    expect(result['fileContents'], equals(fileContentsBase64));
+
+    // List all the files in the file system.
+    result = await vm.invokeRpcNoUpgrade('_listDevFSFiles', {
+      'fsName': fsId,
+    });
+    expect(result['type'], equals('FSFileList'));
+    expect(result['files'].length, equals(1));
+    expect(result['files'][0]['name'], equals(filePath));
+
+    // Delete DevFS.
+    result = await vm.invokeRpcNoUpgrade('_deleteDevFS', {
+      'fsName': fsId,
+    });
+    expect(result['type'], equals('Success'));
+  },
+];
+
+main(args) async => runVMTests(args, tests);
diff --git a/runtime/observatory_2/tests/service_2/dev_fs_http_put_weird_char_test.dart b/runtime/observatory_2/tests/service_2/dev_fs_http_put_weird_char_test.dart
new file mode 100644
index 0000000..ea9ed44
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/dev_fs_http_put_weird_char_test.dart
@@ -0,0 +1,99 @@
+// Copyright (c) 2016, 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.
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:io';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+
+Future<String> readResponse(HttpClientResponse response) {
+  var completer = new Completer<String>();
+  var contents = new StringBuffer();
+  response.cast<List<int>>().transform(utf8.decoder).listen((String data) {
+    contents.write(data);
+  }, onDone: () => completer.complete(contents.toString()));
+  return completer.future;
+}
+
+var tests = <VMTest>[
+  // Write a file with the \r character in the filename.
+  (VM vm) async {
+    var fsId = 'test';
+    var filePath = '/foo/b\rar.dart';
+    var filePathBase64 = base64Encode(utf8.encode(filePath));
+    var fileContents = [0, 1, 2, 3, 4, 5, 6, 255];
+    var fileContentsBase64 = base64Encode(fileContents);
+
+    var result;
+    // Create DevFS.
+    result = await vm.invokeRpcNoUpgrade('_createDevFS', {'fsName': fsId});
+    expect(result['type'], equals('FileSystem'));
+    expect(result['name'], equals(fsId));
+    expect(result['uri'], isA<String>());
+
+    // Write the file by issuing an HTTP PUT.
+    HttpClient client = new HttpClient();
+    HttpClientRequest request =
+        await client.putUrl(Uri.parse(serviceHttpAddress));
+    request.headers.add('dev_fs_name', fsId);
+    request.headers.add('dev_fs_path_b64', filePathBase64);
+    request.add(gzip.encode([9]));
+    HttpClientResponse response = await request.close();
+    String responseBody = await readResponse(response);
+    result = jsonDecode(responseBody);
+    expect(result['result']['type'], equals('Success'));
+
+    // Trigger an error by issuing an HTTP PUT.
+    request = await client.putUrl(Uri.parse(serviceHttpAddress));
+    request.headers.add('dev_fs_name', fsId);
+    // omit the 'dev_fs_path' parameter.
+    request.write(gzip.encode(fileContents));
+    response = await request.close();
+    responseBody = await readResponse(response);
+    result = jsonDecode(responseBody);
+    Map error = result['error']['data'];
+    expect(error, isNotNull);
+    expect(error['details'].contains("expects the 'path' parameter"), isTrue);
+
+    // Write the file again but this time with the true file contents.
+    client = new HttpClient();
+    request = await client.putUrl(Uri.parse(serviceHttpAddress));
+    request.headers.add('dev_fs_name', fsId);
+    request.headers.add('dev_fs_path_b64', filePathBase64);
+    request.add(gzip.encode(fileContents));
+    response = await request.close();
+    responseBody = await readResponse(response);
+    result = jsonDecode(responseBody);
+    expect(result['result']['type'], equals('Success'));
+
+    // Close the HTTP client.
+    client.close();
+
+    // Read the file back.
+    result = await vm.invokeRpcNoUpgrade('_readDevFSFile', {
+      'fsName': fsId,
+      'path': filePath,
+    });
+    expect(result['type'], equals('FSFile'));
+    expect(result['fileContents'], equals(fileContentsBase64));
+
+    // List all the files in the file system.
+    result = await vm.invokeRpcNoUpgrade('_listDevFSFiles', {
+      'fsName': fsId,
+    });
+    expect(result['type'], equals('FSFileList'));
+    expect(result['files'].length, equals(1));
+    expect(result['files'][0]['name'], equals(filePath));
+
+    // Delete DevFS.
+    result = await vm.invokeRpcNoUpgrade('_deleteDevFS', {
+      'fsName': fsId,
+    });
+    expect(result['type'], equals('Success'));
+  },
+];
+
+main(args) async => runVMTests(args, tests);
diff --git a/runtime/observatory_2/tests/service_2/dev_fs_test.dart b/runtime/observatory_2/tests/service_2/dev_fs_test.dart
new file mode 100644
index 0000000..1cab8e5
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/dev_fs_test.dart
@@ -0,0 +1,151 @@
+// Copyright (c) 2016, 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.
+
+import 'dart:convert';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+
+var tests = <VMTest>[
+  (VM vm) async {
+    var result = await vm.invokeRpcNoUpgrade('_listDevFS', {});
+    expect(result['type'], equals('FileSystemList'));
+    expect(result['fsNames'].toString(), equals("[]"));
+
+    var params = {'fsName': 'alpha'};
+    result = await vm.invokeRpcNoUpgrade('_createDevFS', params);
+    expect(result['type'], equals('FileSystem'));
+    expect(result['name'], equals('alpha'));
+    expect(result['uri'], isA<String>());
+
+    result = await vm.invokeRpcNoUpgrade('_listDevFS', {});
+    expect(result['type'], equals('FileSystemList'));
+    expect(result['fsNames'].toString(), equals('[alpha]'));
+
+    bool caughtException;
+    try {
+      await vm.invokeRpcNoUpgrade('_createDevFS', params);
+      expect(false, isTrue, reason: 'Unreachable');
+    } on ServerRpcException catch (e) {
+      caughtException = true;
+      expect(e.code, equals(ServerRpcException.kFileSystemAlreadyExists));
+      expect(e.message, "_createDevFS: file system 'alpha' already exists");
+    }
+    expect(caughtException, isTrue);
+
+    result = await vm.invokeRpcNoUpgrade('_deleteDevFS', params);
+    expect(result['type'], equals('Success'));
+
+    result = await vm.invokeRpcNoUpgrade('_listDevFS', {});
+    expect(result['type'], equals('FileSystemList'));
+    expect(result['fsNames'].toString(), equals("[]"));
+
+    caughtException = false;
+    try {
+      await vm.invokeRpcNoUpgrade('_deleteDevFS', params);
+      expect(false, isTrue, reason: 'Unreachable');
+    } on ServerRpcException catch (e) {
+      caughtException = true;
+      expect(e.code, equals(ServerRpcException.kFileSystemDoesNotExist));
+      expect(e.message, "_deleteDevFS: file system 'alpha' does not exist");
+    }
+    expect(caughtException, isTrue);
+  },
+  (VM vm) async {
+    var fsId = 'banana';
+    var filePath = '/foo/bar.dat';
+    var fileContents = base64Encode(utf8.encode('fileContents'));
+
+    var result;
+    // Create DevFS.
+    result = await vm.invokeRpcNoUpgrade('_createDevFS', {'fsName': fsId});
+    expect(result['type'], equals('FileSystem'));
+    expect(result['name'], equals(fsId));
+    expect(result['uri'], isA<String>());
+
+    bool caughtException = false;
+    try {
+      await vm.invokeRpcNoUpgrade('_readDevFSFile', {
+        'fsName': fsId,
+        'path': filePath,
+      });
+      expect(false, isTrue, reason: 'Unreachable');
+    } on ServerRpcException catch (e) {
+      caughtException = true;
+      expect(e.code, equals(ServerRpcException.kFileDoesNotExist));
+      expect(e.message, startsWith("_readDevFSFile: FileSystemException: "));
+    }
+    expect(caughtException, isTrue);
+
+    // Write a file.
+    result = await vm.invokeRpcNoUpgrade('_writeDevFSFile',
+        {'fsName': fsId, 'path': filePath, 'fileContents': fileContents});
+    expect(result['type'], equals('Success'));
+
+    // Read the file back.
+    result = await vm.invokeRpcNoUpgrade('_readDevFSFile', {
+      'fsName': fsId,
+      'path': filePath,
+    });
+    expect(result['type'], equals('FSFile'));
+    expect(result['fileContents'], equals(fileContents));
+
+    // The leading '/' is optional.
+    result = await vm.invokeRpcNoUpgrade('_readDevFSFile', {
+      'fsName': fsId,
+      'path': filePath.substring(1),
+    });
+    expect(result['type'], equals('FSFile'));
+    expect(result['fileContents'], equals(fileContents));
+
+    // Read a file outside of the fs.
+    caughtException = false;
+    try {
+      await vm.invokeRpcNoUpgrade('_readDevFSFile', {
+        'fsName': fsId,
+        'path': '../foo',
+      });
+      expect(false, isTrue, reason: 'Unreachable');
+    } on ServerRpcException catch (e) {
+      caughtException = true;
+      expect(e.code, equals(ServerRpcException.kInvalidParams));
+      expect(e.message, "_readDevFSFile: invalid 'path' parameter: ../foo");
+    }
+    expect(caughtException, isTrue);
+
+    // Write a set of files.
+    result = await vm.invokeRpcNoUpgrade('_writeDevFSFiles', {
+      'fsName': fsId,
+      'files': [
+        ['/a', base64Encode(utf8.encode('a_contents'))],
+        ['/b', base64Encode(utf8.encode('b_contents'))]
+      ]
+    });
+    expect(result['type'], equals('Success'));
+
+    // Read one of the files back.
+    result = await vm.invokeRpcNoUpgrade('_readDevFSFile', {
+      'fsName': fsId,
+      'path': '/b',
+    });
+    expect(result['type'], equals('FSFile'));
+    expect(result['fileContents'],
+        equals(base64Encode(utf8.encode('b_contents'))));
+
+    // List all the files in the file system.
+    result = await vm.invokeRpcNoUpgrade('_listDevFSFiles', {
+      'fsName': fsId,
+    });
+    expect(result['type'], equals('FSFileList'));
+    expect(result['files'].length, equals(3));
+
+    // Delete DevFS.
+    result = await vm.invokeRpcNoUpgrade('_deleteDevFS', {
+      'fsName': fsId,
+    });
+    expect(result['type'], equals('Success'));
+  },
+];
+
+main(args) async => runVMTests(args, tests);
diff --git a/runtime/observatory_2/tests/service_2/dev_fs_uri_test.dart b/runtime/observatory_2/tests/service_2/dev_fs_uri_test.dart
new file mode 100644
index 0000000..27cc9a4
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/dev_fs_uri_test.dart
@@ -0,0 +1,112 @@
+// Copyright (c) 2016, 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.
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:io';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+
+Future<String> readResponse(HttpClientResponse response) {
+  var completer = new Completer<String>();
+  var contents = new StringBuffer();
+  response.cast<List<int>>().transform(utf8.decoder).listen((String data) {
+    contents.write(data);
+  }, onDone: () => completer.complete(contents.toString()));
+  return completer.future;
+}
+
+var tests = <VMTest>[
+  // Write a file with the ? character in the filename.
+  (VM vm) async {
+    var fsId = 'test';
+    // NOTE: When using the URI encoding scheme, paths cannot be absolute.
+    var filePath = 'foo/bar.dat';
+    var fileUri = Uri.parse(filePath);
+    var fileUriBase64 = base64Encode(utf8.encode(fileUri.toString()));
+    var fileContents = [0, 1, 2, 3, 4, 5, 6, 255];
+    var fileContentsBase64 = base64Encode(fileContents);
+
+    var filePath2 = 'baz/boo.dat';
+    var fileUri2 = Uri.parse(filePath2);
+
+    var result;
+    // Create DevFS.
+    result = await vm.invokeRpcNoUpgrade('_createDevFS', {'fsName': fsId});
+    expect(result['type'], equals('FileSystem'));
+    expect(result['name'], equals(fsId));
+    expect(result['uri'], isA<String>());
+
+    // Write the file by issuing an HTTP PUT.
+    HttpClient client = new HttpClient();
+    HttpClientRequest request =
+        await client.putUrl(Uri.parse(serviceHttpAddress));
+    request.headers.add('dev_fs_name', fsId);
+    request.headers.add('dev_fs_uri_b64', fileUriBase64);
+    request.add(gzip.encode([9]));
+    HttpClientResponse response = await request.close();
+    String responseBody = await readResponse(response);
+    result = jsonDecode(responseBody);
+    print(result);
+    expect(result['result']['type'], equals('Success'));
+
+    // Trigger an error by issuing an HTTP PUT.
+    request = await client.putUrl(Uri.parse(serviceHttpAddress));
+    request.headers.add('dev_fs_name', fsId);
+    // omit the 'dev_fs_path' parameter.
+    request.write(gzip.encode(fileContents));
+    response = await request.close();
+    responseBody = await readResponse(response);
+    result = jsonDecode(responseBody);
+    Map error = result['error']['data'];
+    expect(error, isNotNull);
+    expect(error['details'].contains("expects the 'path' parameter"), isTrue);
+
+    // Write the file again but this time with the true file contents.
+    client = new HttpClient();
+    request = await client.putUrl(Uri.parse(serviceHttpAddress));
+    request.headers.add('dev_fs_name', fsId);
+    request.headers.add('dev_fs_uri_b64', fileUriBase64);
+    request.add(gzip.encode(fileContents));
+    response = await request.close();
+    responseBody = await readResponse(response);
+    result = jsonDecode(responseBody);
+    expect(result['result']['type'], equals('Success'));
+
+    // Close the HTTP client.
+    client.close();
+
+    // Read the file back.
+    result = await vm.invokeRpcNoUpgrade('_readDevFSFile', {
+      'fsName': fsId,
+      'uri': fileUri.toString(),
+    });
+    expect(result['type'], equals('FSFile'));
+    expect(result['fileContents'], equals(fileContentsBase64));
+
+    // Write a second file via URI.
+    result = await vm.invokeRpcNoUpgrade('_writeDevFSFile', {
+      'fsName': fsId,
+      'uri': fileUri2.toString(),
+      'fileContents': fileContentsBase64
+    });
+
+    // Read the second file back.
+    result = await vm.invokeRpcNoUpgrade('_readDevFSFile', {
+      'fsName': fsId,
+      'uri': fileUri2.toString(),
+    });
+    expect(result['type'], equals('FSFile'));
+    expect(result['fileContents'], equals(fileContentsBase64));
+
+    // Delete DevFS.
+    result = await vm.invokeRpcNoUpgrade('_deleteDevFS', {
+      'fsName': fsId,
+    });
+    expect(result['type'], equals('Success'));
+  },
+];
+
+main(args) async => runVMTests(args, tests);
diff --git a/runtime/observatory_2/tests/service_2/dev_fs_weird_char_test.dart b/runtime/observatory_2/tests/service_2/dev_fs_weird_char_test.dart
new file mode 100644
index 0000000..999f84a
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/dev_fs_weird_char_test.dart
@@ -0,0 +1,53 @@
+// Copyright (c) 2016, 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.
+
+import 'dart:convert';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+
+var tests = <VMTest>[
+  // Write a file with the ? character in the filename.
+  (VM vm) async {
+    var fsId = 'test';
+    var filePath = '/foo/bar?dat';
+    var fileContents = base64Encode(utf8.encode('fileContents'));
+
+    var result;
+    // Create DevFS.
+    result = await vm.invokeRpcNoUpgrade('_createDevFS', {'fsName': fsId});
+    expect(result['type'], equals('FileSystem'));
+    expect(result['name'], equals(fsId));
+    expect(result['uri'], isA<String>());
+
+    // Write the file.
+    result = await vm.invokeRpcNoUpgrade('_writeDevFSFile',
+        {'fsName': fsId, 'path': filePath, 'fileContents': fileContents});
+    expect(result['type'], equals('Success'));
+
+    // Read the file back.
+    result = await vm.invokeRpcNoUpgrade('_readDevFSFile', {
+      'fsName': fsId,
+      'path': filePath,
+    });
+    expect(result['type'], equals('FSFile'));
+    expect(result['fileContents'], equals(fileContents));
+
+    // List all the files in the file system.
+    result = await vm.invokeRpcNoUpgrade('_listDevFSFiles', {
+      'fsName': fsId,
+    });
+    expect(result['type'], equals('FSFileList'));
+    expect(result['files'].length, equals(1));
+    expect(result['files'][0]['name'], equals('/foo/bar?dat'));
+
+    // Delete DevFS.
+    result = await vm.invokeRpcNoUpgrade('_deleteDevFS', {
+      'fsName': fsId,
+    });
+    expect(result['type'], equals('Success'));
+  },
+];
+
+main(args) async => runVMTests(args, tests);
diff --git a/runtime/observatory_2/tests/service_2/developer_extension_test.dart b/runtime/observatory_2/tests/service_2/developer_extension_test.dart
new file mode 100644
index 0000000..61cab8f
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/developer_extension_test.dart
@@ -0,0 +1,117 @@
+// Copyright (c) 2015, 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.
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:developer';
+import 'package:expect/expect.dart';
+import 'package:observatory_2/service_io.dart';
+import 'package:observatory_2/sample_profile.dart';
+import 'package:test/test.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+Future<ServiceExtensionResponse> Handler(String method, Map paremeters) {
+  print('Invoked extension: $method');
+  switch (method) {
+    case 'ext..delay':
+      Completer c = new Completer<ServiceExtensionResponse>();
+      new Timer(new Duration(seconds: 1), () {
+        c.complete(new ServiceExtensionResponse.result(jsonEncode({
+          'type': '_delayedType',
+          'method': method,
+          'parameters': paremeters,
+        })));
+      });
+      return c.future;
+    case 'ext..error':
+      return new Future<ServiceExtensionResponse>.value(
+          new ServiceExtensionResponse.error(
+              ServiceExtensionResponse.extensionErrorMin, 'My error detail.'));
+    case 'ext..exception':
+      throw "I always throw!";
+    case 'ext..success':
+      return new Future<ServiceExtensionResponse>.value(
+          new ServiceExtensionResponse.result(jsonEncode({
+        'type': '_extensionType',
+        'method': method,
+        'parameters': paremeters,
+      })));
+  }
+}
+
+void test() {
+  registerExtension('ext..delay', Handler);
+  debugger();
+  postEvent('ALPHA', {'cat': 'dog'});
+  debugger();
+  registerExtension('ext..error', Handler);
+  registerExtension('ext..exception', Handler);
+  registerExtension('ext..success', Handler);
+  bool exceptionThrown = false;
+  try {
+    registerExtension('ext..delay', Handler);
+  } catch (e) {
+    exceptionThrown = true;
+  }
+  // This check is running in the target process so we can't used package:test.
+  Expect.isTrue(exceptionThrown);
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  (Isolate isolate) async {
+    await isolate.load();
+    // Note: extensions other than those is this test might already be
+    // registered by core libraries.
+    expect(isolate.extensionRPCs, contains('ext..delay'));
+    expect(isolate.extensionRPCs, isNot(contains('ext..error')));
+    expect(isolate.extensionRPCs, isNot(contains('ext..exception')));
+    expect(isolate.extensionRPCs, isNot(contains('ext..success')));
+  },
+  resumeIsolateAndAwaitEvent(Isolate.kExtensionStream, (ServiceEvent event) {
+    expect(event.kind, equals(ServiceEvent.kExtension));
+    expect(event.extensionKind, equals('ALPHA'));
+    expect(event.extensionData, isA<Map>());
+    expect(event.extensionData['cat'], equals('dog'));
+  }),
+  hasStoppedAtBreakpoint,
+  resumeIsolateAndAwaitEvent(VM.kIsolateStream, (ServiceEvent event) {
+    // Check that we received an event when '__error' was registered.
+    expect(event.kind, equals(ServiceEvent.kServiceExtensionAdded));
+    expect(event.extensionRPC, equals('ext..error'));
+  }),
+  // Initial.
+  (Isolate isolate) async {
+    var result;
+
+    result = await isolate.invokeRpcNoUpgrade('ext..delay', {});
+    expect(result['type'], equals('_delayedType'));
+    expect(result['method'], equals('ext..delay'));
+    expect(result['parameters']['isolateId'], isNotNull);
+
+    try {
+      await isolate.invokeRpcNoUpgrade('ext..error', {});
+    } on ServerRpcException catch (e, st) {
+      expect(e.code, equals(ServiceExtensionResponse.extensionErrorMin));
+      expect(e.message, equals('My error detail.'));
+    }
+
+    try {
+      await isolate.invokeRpcNoUpgrade('ext..exception', {});
+    } on ServerRpcException catch (e, st) {
+      expect(e.code, equals(ServiceExtensionResponse.extensionError));
+      expect(e.message.startsWith('I always throw!\n'), isTrue);
+    }
+
+    result =
+        await isolate.invokeRpcNoUpgrade('ext..success', {'apple': 'banana'});
+    expect(result['type'], equals('_extensionType'));
+    expect(result['method'], equals('ext..success'));
+    expect(result['parameters']['isolateId'], isNotNull);
+    expect(result['parameters']['apple'], equals('banana'));
+  },
+];
+
+main(args) async => runIsolateTests(args, tests, testeeConcurrent: test);
diff --git a/runtime/observatory_2/tests/service_2/developer_server_control_test.dart b/runtime/observatory_2/tests/service_2/developer_server_control_test.dart
new file mode 100644
index 0000000..62889e4
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/developer_server_control_test.dart
@@ -0,0 +1,84 @@
+// Copyright (c) 2014, 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.
+
+import 'dart:async';
+import 'dart:developer';
+import 'package:expect/expect.dart';
+import 'package:observatory_2/service_io.dart' as S;
+import 'test_helper.dart';
+
+int majorVersion;
+int minorVersion;
+Uri serverUri;
+
+Future<Null> testeeBefore() async {
+  print('testee before');
+  // First grab the URL where the observatory is listening on and the
+  // service protocol version numbers. We expect the URL to be null as
+  // the server has not been started yet.
+  ServiceProtocolInfo info = await Service.getInfo();
+  majorVersion = info.majorVersion;
+  minorVersion = info.minorVersion;
+  serverUri = info.serverUri;
+  Expect.isNull(info.serverUri);
+  {
+    // Now, start the web server and store the URI which is expected to be
+    // non NULL in the top level variable.
+    ServiceProtocolInfo info =
+        await Service.controlWebServer(enable: true, silenceOutput: true);
+    Expect.equals(info.majorVersion, majorVersion);
+    Expect.equals(info.minorVersion, minorVersion);
+    Expect.isNotNull(info.serverUri);
+    serverUri = info.serverUri;
+  }
+  {
+    // Now try starting the web server again, this should just return the
+    // existing state without any change (port number does not change).
+    ServiceProtocolInfo info = await Service.controlWebServer(enable: true);
+    Expect.equals(info.majorVersion, majorVersion);
+    Expect.equals(info.minorVersion, minorVersion);
+    Expect.equals(info.serverUri, serverUri);
+  }
+  {
+    // Try turning off the web server, this should turn off the server and
+    // the Uri returned should be null.
+    ServiceProtocolInfo info = await Service.controlWebServer(enable: false);
+    Expect.equals(info.majorVersion, majorVersion);
+    Expect.equals(info.minorVersion, minorVersion);
+    Expect.isNull(info.serverUri);
+  }
+  {
+    // Try turning off the web server again, this should be a nop
+    // and the Uri returned should be null.
+    ServiceProtocolInfo info = await Service.controlWebServer(enable: false);
+    Expect.equals(info.majorVersion, majorVersion);
+    Expect.equals(info.minorVersion, minorVersion);
+    Expect.isNull(info.serverUri);
+  }
+  {
+    // Start the web server again for the test below.
+    ServiceProtocolInfo info = await Service.controlWebServer(enable: true);
+    majorVersion = info.majorVersion;
+    minorVersion = info.minorVersion;
+    serverUri = info.serverUri;
+    Expect.equals(info.majorVersion, majorVersion);
+    Expect.equals(info.minorVersion, minorVersion);
+    Expect.equals(info.serverUri, serverUri);
+  }
+}
+
+var tests = <IsolateTest>[
+  (S.Isolate isolate) async {
+    await isolate.reload();
+    // Just getting here means that the testee enabled the service protocol
+    // web server.
+    Expect.equals(true, true);
+  }
+];
+
+main(args) => runIsolateTests(args, tests,
+    testeeBefore: testeeBefore,
+    // the testee is responsible for starting the
+    // web server.
+    testeeControlsServer: true);
diff --git a/runtime/observatory_2/tests/service_2/developer_service_get_isolate_id_test.dart b/runtime/observatory_2/tests/service_2/developer_service_get_isolate_id_test.dart
new file mode 100644
index 0000000..eb65cdc
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/developer_service_get_isolate_id_test.dart
@@ -0,0 +1,92 @@
+// Copyright (c) 2016, 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.
+
+import 'dart:async';
+import 'dart:developer' as dev;
+import 'dart:isolate' as Core;
+
+import 'package:observatory_2/service_io.dart' as Service;
+import 'package:test/test.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+// testee state.
+String selfId;
+Core.Isolate childIsolate;
+String childId;
+
+void spawnEntry(int i) {
+  dev.debugger();
+}
+
+Future testeeMain() async {
+  dev.debugger();
+  // Spawn an isolate.
+  childIsolate = await Core.Isolate.spawn(spawnEntry, 0);
+  // Assign the id for this isolate and it's child to strings so they can
+  // be read by the tester.
+  selfId = dev.Service.getIsolateID(Core.Isolate.current);
+  childId = dev.Service.getIsolateID(childIsolate);
+  dev.debugger();
+}
+
+@pragma("vm:entry-point")
+getSelfId() => selfId;
+
+@pragma("vm:entry-point")
+getChildId() => childId;
+
+// tester state:
+Service.Isolate initialIsolate;
+Service.Isolate localChildIsolate;
+
+var tests = <VMTest>[
+  (Service.VM vm) async {
+    // Sanity check.
+    expect(vm.isolates.length, 1);
+    initialIsolate = vm.isolates[0];
+    await hasStoppedAtBreakpoint(initialIsolate);
+    // Resume.
+    await initialIsolate.resume();
+  },
+  (Service.VM vm) async {
+    // Initial isolate has paused at second debugger call.
+    await hasStoppedAtBreakpoint(initialIsolate);
+  },
+  (Service.VM vm) async {
+    // Reload the VM.
+    await vm.reload();
+
+    // Grab the child isolate.
+    localChildIsolate =
+        vm.isolates.firstWhere((Service.Isolate i) => i != initialIsolate);
+    expect(localChildIsolate, isNotNull);
+
+    // Reload the initial isolate.
+    await initialIsolate.reload();
+
+    // Grab the root library.
+    Service.Library rootLbirary = await initialIsolate.rootLibrary.load();
+
+    // Grab self id.
+    Service.Instance localSelfId =
+        await initialIsolate.invoke(rootLbirary, 'getSelfId');
+
+    // Check that the id reported from dart:developer matches the id reported
+    // from the service protocol.
+    expect(localSelfId.isString, true);
+    expect(initialIsolate.id, equals(localSelfId.valueAsString));
+
+    // Grab the child isolate's id.
+    Service.Instance localChildId =
+        await initialIsolate.invoke(rootLbirary, 'getChildId');
+
+    // Check that the id reported from dart:developer matches the id reported
+    // from the service protocol.
+    expect(localChildId.isString, true);
+    expect(localChildIsolate.id, equals(localChildId.valueAsString));
+  }
+];
+
+main(args) async => runVMTests(args, tests, testeeConcurrent: testeeMain);
diff --git a/runtime/observatory_2/tests/service_2/dominator_tree_vm_test.dart b/runtime/observatory_2/tests/service_2/dominator_tree_vm_test.dart
new file mode 100644
index 0000000..9cb5ad5
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/dominator_tree_vm_test.dart
@@ -0,0 +1,164 @@
+// Copyright (c) 2015, 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.
+// VMOptions=
+// VMOptions=--use_compactor
+// VMOptions=--use_compactor --force_evacuation
+
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+
+// small example from [Lenguaer & Tarjan 1979]
+class R {
+  // All fields are marked with @pragma("vm:entry-point")
+  // in order to make sure they are not removed by the tree shaker
+  // even though they are never read.
+  @pragma("vm:entry-point")
+  var x;
+  @pragma("vm:entry-point")
+  var y;
+  @pragma("vm:entry-point")
+  var z;
+}
+
+class A {
+  @pragma("vm:entry-point")
+  var x;
+}
+
+class B {
+  @pragma("vm:entry-point")
+  var x;
+  @pragma("vm:entry-point")
+  var y;
+  @pragma("vm:entry-point")
+  var z;
+}
+
+class C {
+  @pragma("vm:entry-point")
+  var x;
+  @pragma("vm:entry-point")
+  var y;
+}
+
+class D {
+  @pragma("vm:entry-point")
+  var x;
+}
+
+class E {
+  @pragma("vm:entry-point")
+  var x;
+}
+
+class F {
+  @pragma("vm:entry-point")
+  var x;
+}
+
+class G {
+  @pragma("vm:entry-point")
+  var x;
+  @pragma("vm:entry-point")
+  var y;
+}
+
+class H {
+  @pragma("vm:entry-point")
+  var x;
+  @pragma("vm:entry-point")
+  var y;
+}
+
+class I {
+  @pragma("vm:entry-point")
+  var x;
+}
+
+class J {
+  @pragma("vm:entry-point")
+  var x;
+}
+
+class K {
+  @pragma("vm:entry-point")
+  var x;
+  @pragma("vm:entry-point")
+  var y;
+}
+
+class L {
+  @pragma("vm:entry-point")
+  var x;
+}
+
+var r;
+
+buildGraph() {
+  r = new R();
+  var a = new A();
+  var b = new B();
+  var c = new C();
+  var d = new D();
+  var e = new E();
+  var f = new F();
+  var g = new G();
+  var h = new H();
+  var i = new I();
+  var j = new J();
+  var k = new K();
+  var l = new L();
+
+  r.x = a;
+  r.y = b;
+  r.z = c;
+  a.x = d;
+  b.x = a;
+  b.y = d;
+  b.z = e;
+  c.x = f;
+  c.y = g;
+  d.x = l;
+  e.x = h;
+  f.x = i;
+  g.x = i;
+  g.y = j;
+  h.x = e;
+  h.y = k;
+  i.x = k;
+  j.x = i;
+  k.x = i;
+  k.y = r;
+  l.x = h;
+}
+
+var tests = <IsolateTest>[
+  (Isolate isolate) async {
+    final graph = await isolate.fetchHeapSnapshot().done;
+
+    node(String className) {
+      return graph.objects.singleWhere((v) => v.klass.name == className);
+    }
+
+    expect(node('I').parent, equals(node('R')));
+    expect(node('K').parent, equals(node('R')));
+    expect(node('C').parent, equals(node('R')));
+    expect(node('H').parent, equals(node('R')));
+    expect(node('E').parent, equals(node('R')));
+    expect(node('A').parent, equals(node('R')));
+    expect(node('D').parent, equals(node('R')));
+    expect(node('B').parent, equals(node('R')));
+
+    expect(node('F').parent, equals(node('C')));
+    expect(node('G').parent, equals(node('C')));
+    expect(node('J').parent, equals(node('G')));
+    expect(node('L').parent, equals(node('D')));
+
+    expect(node('R'), isNotNull); // The field.
+  },
+];
+
+main(args) => runIsolateTests(args, tests, testeeBefore: buildGraph);
diff --git a/runtime/observatory_2/tests/service_2/dominator_tree_vm_with_double_field_test.dart b/runtime/observatory_2/tests/service_2/dominator_tree_vm_with_double_field_test.dart
new file mode 100644
index 0000000..965d4f0
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/dominator_tree_vm_with_double_field_test.dart
@@ -0,0 +1,176 @@
+// Copyright (c) 2020, 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.
+// VMOptions=
+// VMOptions=--use_compactor
+// VMOptions=--use_compactor --force_evacuation
+
+import 'dart:typed_data';
+
+import 'package:expect/expect.dart';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+
+double getDoubleWithHeapObjectTag() {
+  final bd = ByteData(8);
+  bd.setUint64(0, 0x8000000000000001, Endian.host);
+  final double v = bd.getFloat64(0, Endian.host);
+  return v;
+}
+
+// small example from [Lenguaer & Tarjan 1979]
+class R {
+  final double fld = getDoubleWithHeapObjectTag();
+  // Fields are marked with @pragma("vm:entry-point")
+  // in order to make sure they are not removed by the tree shaker
+  // even though they are never read.
+  @pragma("vm:entry-point")
+  var x;
+  @pragma("vm:entry-point")
+  var y;
+  @pragma("vm:entry-point")
+  var z;
+}
+
+class A {
+  @pragma("vm:entry-point")
+  var x;
+}
+
+class B {
+  @pragma("vm:entry-point")
+  var x;
+  @pragma("vm:entry-point")
+  var y;
+  @pragma("vm:entry-point")
+  var z;
+}
+
+class C {
+  @pragma("vm:entry-point")
+  var x;
+  @pragma("vm:entry-point")
+  var y;
+}
+
+class D {
+  @pragma("vm:entry-point")
+  var x;
+}
+
+class E {
+  @pragma("vm:entry-point")
+  var x;
+}
+
+class F {
+  @pragma("vm:entry-point")
+  var x;
+}
+
+class G {
+  @pragma("vm:entry-point")
+  var x;
+  @pragma("vm:entry-point")
+  var y;
+}
+
+class H {
+  @pragma("vm:entry-point")
+  var x;
+  @pragma("vm:entry-point")
+  var y;
+}
+
+class I {
+  @pragma("vm:entry-point")
+  var x;
+}
+
+class J {
+  @pragma("vm:entry-point")
+  var x;
+}
+
+class K {
+  @pragma("vm:entry-point")
+  var x;
+  @pragma("vm:entry-point")
+  var y;
+}
+
+class L {
+  @pragma("vm:entry-point")
+  var x;
+}
+
+var r;
+
+buildGraph() {
+  r = new R();
+  var a = new A();
+  var b = new B();
+  var c = new C();
+  var d = new D();
+  var e = new E();
+  var f = new F();
+  var g = new G();
+  var h = new H();
+  var i = new I();
+  var j = new J();
+  var k = new K();
+  var l = new L();
+
+  r.x = a;
+  r.y = b;
+  r.z = c;
+  a.x = d;
+  b.x = a;
+  b.y = d;
+  b.z = e;
+  c.x = f;
+  c.y = g;
+  d.x = l;
+  e.x = h;
+  f.x = i;
+  g.x = i;
+  g.y = j;
+  h.x = e;
+  h.y = k;
+  i.x = k;
+  j.x = i;
+  k.x = i;
+  k.y = r;
+  l.x = h;
+
+  Expect.equals(r.fld, getDoubleWithHeapObjectTag());
+}
+
+var tests = <IsolateTest>[
+  (Isolate isolate) async {
+    final graph = await isolate.fetchHeapSnapshot().done;
+
+    node(String className) {
+      return graph.objects.singleWhere((v) => v.klass.name == className);
+    }
+
+    expect(node('I').parent, equals(node('R')));
+    expect(node('K').parent, equals(node('R')));
+    expect(node('C').parent, equals(node('R')));
+    expect(node('H').parent, equals(node('R')));
+    expect(node('E').parent, equals(node('R')));
+    expect(node('A').parent, equals(node('R')));
+    expect(node('D').parent, equals(node('R')));
+    expect(node('B').parent, equals(node('R')));
+
+    expect(node('F').parent, equals(node('C')));
+    expect(node('G').parent, equals(node('C')));
+    expect(node('J').parent, equals(node('G')));
+    expect(node('L').parent, equals(node('D')));
+
+    expect(node('R'), isNotNull); // The field.
+  },
+];
+
+main(args) => runIsolateTests(args, tests, testeeBefore: buildGraph);
diff --git a/runtime/observatory_2/tests/service_2/echo_test.dart b/runtime/observatory_2/tests/service_2/echo_test.dart
new file mode 100644
index 0000000..bccb96e
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/echo_test.dart
@@ -0,0 +1,40 @@
+// Copyright (c) 2014, 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.
+
+import 'dart:async';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+
+var tests = <IsolateTest>[
+  (Isolate isolate) =>
+      isolate.vm.invokeRpcNoUpgrade('_echo', {'text': 'hello'}).then((result) {
+        expect(result['type'], equals('_EchoResponse'));
+        expect(result['text'], equals('hello'));
+      }),
+  (Isolate isolate) =>
+      isolate.invokeRpcNoUpgrade('_echo', {'text': 'hello'}).then((result) {
+        expect(result['type'], equals('_EchoResponse'));
+        expect(result['text'], equals('hello'));
+      }),
+  (Isolate isolate) async {
+    Completer completer = new Completer();
+    var stream = await isolate.vm.getEventStream('_Echo');
+    var subscription;
+    subscription = stream.listen((ServiceEvent event) {
+      assert(event.kind == '_Echo');
+      expect(event.data.lengthInBytes, equals(3));
+      expect(event.data[0], equals(0));
+      expect(event.data[1], equals(128));
+      expect(event.data[2], equals(255));
+      subscription.cancel();
+      completer.complete();
+    });
+
+    await isolate.invokeRpc('_triggerEchoEvent', {'text': 'hello'});
+    await completer.future;
+  },
+];
+
+main(args) => runIsolateTests(args, tests);
diff --git a/runtime/observatory_2/tests/service_2/enable_service_port_fallback_positive_test.dart b/runtime/observatory_2/tests/service_2/enable_service_port_fallback_positive_test.dart
new file mode 100644
index 0000000..e50fc0a
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/enable_service_port_fallback_positive_test.dart
@@ -0,0 +1,40 @@
+// Copyright (c) 2020, 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.
+
+import 'dart:io';
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+
+// Tests that the --enable-service-port-fallback flag works correctly by trying to bind to
+// a port that is available.
+var tests = <VMTest>[
+  (VM vm) async {
+    // Correctly bound to provided port.
+    expect(Uri.parse(serviceHttpAddress).port == selectedPort, true);
+  }
+];
+
+int selectedPort = 0;
+
+main(args) async {
+  selectedPort = await _getUnusedPort();
+  await runVMTests(
+    args, tests,
+    enable_service_port_fallback: true,
+    // Choose a port number that should always be open.
+    port: selectedPort,
+    extraArgs: [],
+    // TODO(bkonyi): investigate failure.
+    enableDds: false,
+  );
+}
+
+Future<int> _getUnusedPort() async {
+  var socket = await ServerSocket.bind(InternetAddress.anyIPv4, 0);
+  var port = socket.port;
+  await socket.close();
+  return port;
+}
diff --git a/runtime/observatory_2/tests/service_2/enable_service_port_fallback_test.dart b/runtime/observatory_2/tests/service_2/enable_service_port_fallback_test.dart
new file mode 100644
index 0000000..0c89d5c
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/enable_service_port_fallback_test.dart
@@ -0,0 +1,29 @@
+// Copyright (c) 2020, 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.
+
+import 'dart:io';
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+
+// Tests that the --enable-service-port-fallback flag works correctly by trying to bind to
+// a port that is unsupported.
+var tests = <VMTest>[
+  (VM vm) async {
+    // Did not bind to provided port.
+    expect(Uri.parse(serviceHttpAddress).port != portNumber, true);
+    await socket.close();
+  }
+];
+
+ServerSocket socket;
+int portNumber = -1;
+
+main(args) async {
+  socket = await ServerSocket.bind(InternetAddress.loopbackIPv4, 0);
+  portNumber = socket.port;
+  return runVMTests(args, tests,
+      enable_service_port_fallback: true, port: portNumber, extraArgs: []);
+}
diff --git a/runtime/observatory_2/tests/service_2/eval_internal_class_test.dart b/runtime/observatory_2/tests/service_2/eval_internal_class_test.dart
new file mode 100644
index 0000000..19a83e4
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/eval_internal_class_test.dart
@@ -0,0 +1,53 @@
+// Copyright (c) 2016, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+
+var tests = <IsolateTest>[
+  (Isolate isolate) async {
+    Library root = await isolate.rootLibrary.load();
+
+    Class classLibrary = await root.clazz.load();
+    print(classLibrary);
+    {
+      bool caughtExceptions = false;
+      try {
+        dynamic result = await classLibrary.evaluate('3 + 4');
+        print(result);
+      } on ServerRpcException catch (e) {
+        expect(e.toString(), contains('can be evaluated only'));
+        caughtExceptions = true;
+      }
+      expect(caughtExceptions, isTrue);
+    }
+
+    Class classClass = await classLibrary.clazz.load();
+    print(classClass);
+    {
+      bool caughtExceptions = false;
+      try {
+        dynamic result = await classClass.evaluate('3 + 4');
+        print(result);
+      } on ServerRpcException catch (e) {
+        expect(e.toString(), contains('can be evaluated only'));
+        caughtExceptions = true;
+      }
+      expect(caughtExceptions, isTrue);
+    }
+
+    Instance someArray = await root.evaluate("new List(2)");
+    print(someArray);
+    expect(someArray is Instance, isTrue);
+    Class classArray = await someArray.clazz.load();
+    print(classArray);
+    dynamic result = await classArray.evaluate('3 + 4');
+    print(result);
+    expect(result is Instance, isTrue);
+    expect(result.valueAsString, equals('7'));
+  },
+];
+
+main(args) => runIsolateTests(args, tests);
diff --git a/runtime/observatory_2/tests/service_2/eval_regression_flutter20255_test.dart b/runtime/observatory_2/tests/service_2/eval_regression_flutter20255_test.dart
new file mode 100644
index 0000000..0ef9d6a
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/eval_regression_flutter20255_test.dart
@@ -0,0 +1,108 @@
+// Copyright (c) 2018, 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.
+
+import 'dart:async';
+import 'dart:developer';
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+class Base<T> {
+  String field;
+
+  Base(this.field);
+  String foo() => 'Base-$field';
+}
+
+class Sub<T> extends Base<T> {
+  String field;
+
+  Sub(this.field) : super(field);
+  String foo() {
+    debugger();
+    return 'Sub-$field';
+  }
+}
+
+class ISub<T> implements Base<T> {
+  String field;
+
+  ISub(this.field);
+  String foo() => 'ISub-$field';
+}
+
+class Box<T> {
+  T value;
+
+  @pragma('vm:never-inline')
+  void setValue(T value) {
+    this.value = value;
+  }
+}
+
+final objects = <Base>[
+  new Base<int>('b'),
+  new Sub<double>('a'),
+  new ISub<bool>('c')
+];
+
+String triggerTypeTestingStubGeneration() {
+  final Box<Object> box = new Box<Base>();
+  for (int i = 0; i < 1000000; ++i) {
+    box.setValue(objects.last);
+  }
+  return 'tts-generated';
+}
+
+void testFunction() {
+  // Triggers the debugger, which will evaluate an expression in the context of
+  // [Sub<double>], which will make a subclass of [Base<T>].
+  print(objects[1].foo());
+
+  triggerTypeTestingStubGeneration();
+
+  // Triggers the debugger, which will evaluate an expression in the context of
+  // [Sub<double>], which will make a subclass of [Base<T>].
+  print(objects[1].foo());
+}
+
+Future triggerEvaluation(Isolate isolate) async {
+  ServiceMap stack = await isolate.getStack();
+
+  // Make sure we are in the right place.
+  expect(stack.type, equals('Stack'));
+  expect(stack['frames'].length, greaterThanOrEqualTo(2));
+  expect(stack['frames'][0].function.name, equals('foo'));
+  expect(stack['frames'][0].function.dartOwner.name, equals('Sub'));
+
+  // Trigger an evaluation, which will create a new subclass of Base<T>.
+  final dynamic result =
+      await isolate.evalFrame(0, 'this.field + " world \$T"');
+  if (result is DartError) {
+    throw 'Got an error "$result", expected result of expression evaluation.';
+  }
+  expect(result.valueAsString, equals('a world double'));
+
+  // Trigger an optimization of a type testing stub (and usage of it).
+  final dynamic result2 =
+      await isolate.evalFrame(0, 'triggerTypeTestingStubGeneration()');
+  if (result2 is DartError) {
+    throw 'Got an error "$result", expected result of expression evaluation.';
+  }
+  expect(result2.valueAsString, equals('tts-generated'));
+}
+
+final testSteps = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  triggerEvaluation,
+  resumeIsolate,
+  hasStoppedAtBreakpoint,
+  triggerEvaluation,
+  resumeIsolate,
+];
+
+main(args) => runIsolateTests(args, testSteps, testeeConcurrent: testFunction);
diff --git a/runtime/observatory_2/tests/service_2/eval_skip_breakpoint.dart b/runtime/observatory_2/tests/service_2/eval_skip_breakpoint.dart
new file mode 100644
index 0000000..3647689
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/eval_skip_breakpoint.dart
@@ -0,0 +1,45 @@
+// Copyright (c) 2019, 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.
+// VMOptions=--verbose_debug
+
+import 'dart:developer';
+
+import 'package:observatory_2/service_io.dart';
+
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+const int LINE_A = 21;
+const int LINE_B = 16;
+
+bar() {
+  print('bar');
+}
+
+testMain() {
+  debugger();
+  bar();
+  print("Done");
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_A),
+// Add breakpoint
+  setBreakpointAtLine(LINE_B),
+// Evaluate 'bar()'
+  (Isolate isolate) async {
+    final lib = isolate.rootLibrary;
+    await lib.evaluate('bar()', disableBreakpoints: true);
+  },
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_A),
+  resumeIsolate,
+
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_B),
+  resumeIsolate,
+];
+
+main(args) => runIsolateTests(args, tests, testeeConcurrent: testMain);
diff --git a/runtime/observatory_2/tests/service_2/eval_test.dart b/runtime/observatory_2/tests/service_2/eval_test.dart
new file mode 100644
index 0000000..15b21be
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/eval_test.dart
@@ -0,0 +1,106 @@
+// Copyright (c) 2015, 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.
+
+import 'dart:developer';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+int globalVar = 100;
+
+class MyClass {
+  static int staticVar = 1000;
+
+  static void method(int value) {
+    debugger();
+  }
+}
+
+class _MyClass {
+  void foo() {
+    debugger();
+  }
+}
+
+void testFunction() {
+  int i = 0;
+  while (true) {
+    if (++i % 100000000 == 0) {
+      MyClass.method(10000);
+      (new _MyClass()).foo();
+    }
+  }
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+
+// Evaluate against library, class, and instance.
+  (Isolate isolate) async {
+    ServiceMap stack = await isolate.getStack();
+
+    // Make sure we are in the right place.
+    expect(stack.type, equals('Stack'));
+    expect(stack['frames'].length, greaterThanOrEqualTo(2));
+    expect(stack['frames'][0].function.name, equals('method'));
+    expect(stack['frames'][0].function.dartOwner.name, equals('MyClass'));
+
+    var lib = isolate.rootLibrary;
+    var cls = stack['frames'][0].function.dartOwner;
+    var instance = stack['frames'][0].variables[0]['value'];
+
+    dynamic result = await lib.evaluate('globalVar + 5');
+    print(result);
+    expect(result.valueAsString, equals('105'));
+
+    await expectError(() => lib.evaluate('globalVar + staticVar + 5'));
+
+    result = await cls.evaluate('globalVar + staticVar + 5');
+    print(result);
+    expect(result.valueAsString, equals('1105'));
+
+    await expectError(() => cls.evaluate('this + 5'));
+
+    result = await instance.evaluate('this + 5');
+    print(result);
+    expect(result.valueAsString, equals('10005'));
+
+    await expectError(() => instance.evaluate('this + frog'));
+  },
+  resumeIsolate,
+  hasStoppedAtBreakpoint,
+  (Isolate isolate) async {
+    ServiceMap stack = await isolate.getStack();
+
+    // Make sure we are in the right place.
+    expect(stack.type, equals('Stack'));
+    expect(stack['frames'].length, greaterThanOrEqualTo(2));
+    expect(stack['frames'][0].function.name, equals('foo'));
+    expect(stack['frames'][0].function.dartOwner.name, equals('_MyClass'));
+
+    var cls = stack['frames'][0].function.dartOwner;
+
+    dynamic result = await cls.evaluate("1+1");
+    print(result);
+    expect(result.valueAsString, equals("2"));
+  }
+];
+
+expectError(func) async {
+  bool gotException = false;
+  dynamic result;
+  try {
+    result = await func();
+    expect(result.type, equals('Error')); // dart1 semantics
+  } on ServerRpcException catch (e) {
+    expect(e.code, equals(ServerRpcException.kExpressionCompilationError));
+    gotException = true;
+  }
+  if (result?.type != 'Error') {
+    expect(gotException, true); // dart2 semantics
+  }
+}
+
+main(args) => runIsolateTests(args, tests, testeeConcurrent: testFunction);
diff --git a/runtime/observatory_2/tests/service_2/evaluate_activation_in_method_class_other.dart b/runtime/observatory_2/tests/service_2/evaluate_activation_in_method_class_other.dart
new file mode 100644
index 0000000..bf070a1
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/evaluate_activation_in_method_class_other.dart
@@ -0,0 +1,29 @@
+// Copyright (c) 2016, 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.
+
+import 'dart:developer';
+
+var topLevel = "OtherLibrary";
+
+class Superclass {
+  var _instVar = 'Superclass';
+  var instVar = 'Superclass';
+  method() => 'Superclass';
+  static staticMethod() => 'Superclass';
+  suppress_warning() => _instVar;
+}
+
+class Klass extends Superclass {
+  var _instVar = 'Klass';
+  var instVar = 'Klass';
+  method() => 'Klass';
+  static staticMethod() => 'Klass';
+
+  test() {
+    var _local = 'Klass';
+    debugger();
+    // Suppress unused variable warning.
+    print(_local);
+  }
+}
diff --git a/runtime/observatory_2/tests/service_2/evaluate_activation_in_method_class_test.dart b/runtime/observatory_2/tests/service_2/evaluate_activation_in_method_class_test.dart
new file mode 100644
index 0000000..6af1450
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/evaluate_activation_in_method_class_test.dart
@@ -0,0 +1,84 @@
+// Copyright (c) 2016, 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.
+
+// Tests that expressions evaluated in a frame see the same scope as the
+// frame's method.
+
+import 'dart:async';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+import 'evaluate_activation_in_method_class_other.dart';
+
+var topLevel = "TestLibrary";
+
+class Subclass extends Superclass with Klass {
+  var _instVar = 'Subclass';
+  var instVar = 'Subclass';
+  method() => 'Subclass';
+  static staticMethod() => 'Subclass';
+  suppress_warning() => _instVar;
+}
+
+testeeDo() {
+  var obj = new Subclass();
+  obj.test();
+}
+
+Future testerDo(Isolate isolate) async {
+  await hasStoppedAtBreakpoint(isolate);
+
+  // Make sure we are in the right place.
+  var stack = await isolate.getStack();
+  var topFrame = 0;
+  expect(stack.type, equals('Stack'));
+  expect(stack['frames'][topFrame].function.name, equals('test'));
+  expect(stack['frames'][topFrame].function.dartOwner.name,
+      equals('Superclass&Klass'));
+
+  Instance result;
+
+  result = await isolate.evalFrame(topFrame, '_local');
+  print(result);
+  expect(result.valueAsString, equals('Klass'));
+
+  result = await isolate.evalFrame(topFrame, '_instVar');
+  print(result);
+  expect(result.valueAsString, equals('Klass'));
+
+  result = await isolate.evalFrame(topFrame, 'instVar');
+  print(result);
+  expect(result.valueAsString, equals('Subclass'));
+
+  result = await isolate.evalFrame(topFrame, 'method()');
+  print(result);
+  expect(result.valueAsString, equals('Subclass'));
+
+  result = await isolate.evalFrame(topFrame, 'super._instVar');
+  print(result);
+  expect(result.valueAsString, equals('Superclass'));
+
+  result = await isolate.evalFrame(topFrame, 'super.instVar');
+  print(result);
+  expect(result.valueAsString, equals('Superclass'));
+
+  result = await isolate.evalFrame(topFrame, 'super.method()');
+  print(result);
+  expect(result.valueAsString, equals('Superclass'));
+
+  result = await isolate.evalFrame(topFrame, 'staticMethod()');
+  print(result);
+  expect(result.valueAsString, equals('Klass'));
+
+  // function.Owner verus function.Origin
+  // The mixin of Superclass is in _other.dart and the mixin
+  // application is in _test.dart.
+  result = await isolate.evalFrame(topFrame, 'topLevel');
+  print(result);
+  expect(result.valueAsString, equals('OtherLibrary'));
+}
+
+main(args) => runIsolateTests(args, [testerDo], testeeConcurrent: testeeDo);
diff --git a/runtime/observatory_2/tests/service_2/evaluate_activation_test.dart b/runtime/observatory_2/tests/service_2/evaluate_activation_test.dart
new file mode 100644
index 0000000..7967aae
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/evaluate_activation_test.dart
@@ -0,0 +1,269 @@
+// Copyright (c) 2015, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+
+import 'dart:async';
+import 'dart:math' as math;
+
+breakHere() {}
+
+use(dynamic v) => v;
+
+class C {
+  var instVar = 1;
+  static var classVar = 2;
+
+  method(methodParam) {
+    var methodTemp = 4;
+    use(methodTemp);
+    [5].forEach((outerParam) {
+      var outerTemp = 6;
+      use(outerTemp);
+      [7].forEach((innerParam) {
+        var innerTemp = 8;
+        use(innerTemp);
+        breakHere();
+      });
+    });
+  }
+
+  static method2(methodParam) {
+    var methodTemp = 4;
+    use(methodTemp);
+    [5].forEach((outerParam) {
+      var outerTemp = 6;
+      use(outerTemp);
+      [7].forEach((innerParam) {
+        var innerTemp = 8;
+        use(innerTemp);
+        breakHere();
+      });
+    });
+  }
+
+  method3(methodParam) {
+    var methodTemp = 4;
+    use(methodTemp);
+    breakHere();
+  }
+
+  static var closureWithReturnedHome;
+  method4(methodParam) {
+    var methodTemp = 4;
+    use(methodTemp);
+    [5].forEach((outerParam) {
+      var outerTemp = 6;
+      use(outerTemp);
+      closureWithReturnedHome = (innerParam) {
+        var innerTemp = 8;
+        use(innerTemp);
+        breakHere();
+      };
+    });
+  }
+}
+
+Future testMethod(Isolate isolate) async {
+  // silence analyzer.
+  expect(math.sqrt(4), equals(2));
+  Library rootLib = await isolate.rootLibrary.load();
+  ServiceFunction function =
+      rootLib.functions.singleWhere((f) => f.name == 'breakHere');
+  Breakpoint bpt = await isolate.addBreakpointAtEntry(function);
+  print("Breakpoint: $bpt");
+  expect(bpt is Breakpoint, isTrue);  // I.e, not null.
+
+  bool hitBreakpoint = false;
+  var stream = await isolate.vm.getEventStream(VM.kDebugStream);
+  var sub;
+  sub = stream.listen((ServiceEvent event) async {
+    print("Event $event");
+    if (event.kind == ServiceEvent.kPauseBreakpoint) {
+      var frameNumber = 1, r;
+      r = await isolate.evalFrame(frameNumber, '123');  /// instance: ok
+      expect(r.valueAsString, equals('123'));  /// instance: continued
+      r = await isolate.evalFrame(frameNumber, 'this');  /// scope: ok
+      expect(r.clazz.name, equals('C'));  /// scope: continued
+      r = await isolate.evalFrame(frameNumber, 'instVar');  /// instance: continued
+      expect(r.valueAsString, equals('1'));  /// instance: continued
+      r = await isolate.evalFrame(frameNumber, 'classVar'); /// instance: continued
+      expect(r.valueAsString, equals('2'));  /// instance: continued
+      r = await isolate.evalFrame(frameNumber, 'methodParam');  /// scope: continued
+      expect(r.valueAsString, equals('3'));  /// scope: continued
+      r = await isolate.evalFrame(frameNumber, 'methodTemp');  /// scope: continued
+      expect(r.valueAsString, equals('4'));  /// scope: continued
+      r = await isolate.evalFrame(frameNumber, 'outerParam');  /// scope: continued
+      expect(r.valueAsString, equals('5'));  /// scope: continued
+      r = await isolate.evalFrame(frameNumber, 'outerTemp');  /// scope: continued
+      expect(r.valueAsString, equals('6'));  /// scope: continued
+      r = await isolate.evalFrame(frameNumber, 'innerParam');  /// instance: continued
+      expect(r.valueAsString, equals('7'));  /// instance: continued
+      r = await isolate.evalFrame(frameNumber, 'innerTemp');  /// instance: continued
+      expect(r.valueAsString, equals('8'));  /// instance: continued
+      r = await isolate.evalFrame(frameNumber, 'math.sqrt');  /// instance: continued
+      expect(r.isClosure, isTrue);  /// instance: continued
+
+      hitBreakpoint = true;
+      sub.cancel();  // Do not handle events for the other tests.
+      isolate.resume();
+    }
+  });
+
+  var result = await rootLib.evaluate('new C().method(3)');
+  print("Result $result");
+  expect(hitBreakpoint, isTrue);
+}
+
+Future testMethod2(Isolate isolate) async {
+  Library rootLib = await isolate.rootLibrary.load();
+  ServiceFunction function =
+      rootLib.functions.singleWhere((f) => f.name == 'breakHere');
+  Breakpoint bpt = await isolate.addBreakpointAtEntry(function);
+  print("Breakpoint: $bpt");
+  expect(bpt is Breakpoint, isTrue);  // I.e, not null.
+
+  bool hitBreakpoint = false;
+  var stream = await isolate.vm.getEventStream(VM.kDebugStream);
+  var sub;
+  sub = stream.listen((ServiceEvent event) async {
+    print("Event $event");
+    if (event.kind == ServiceEvent.kPauseBreakpoint) {
+      var frameNumber = 1, r;
+      r = await isolate.evalFrame(frameNumber, '123');
+      expect(r.valueAsString, equals('123'));
+      r = await isolate.evalFrame(frameNumber, 'this');
+      expect(r is DartError, isTrue);
+      r = await isolate.evalFrame(frameNumber, 'instVar');
+      expect(r is DartError, isTrue);
+      r = await isolate.evalFrame(frameNumber, 'classVar');
+      expect(r.valueAsString, equals('2'));
+      r = await isolate.evalFrame(frameNumber, 'methodParam');
+      expect(r.valueAsString, equals('3'));  /// scope: continued
+      r = await isolate.evalFrame(frameNumber, 'methodTemp');
+      expect(r.valueAsString, equals('4'));  /// scope: continued
+      r = await isolate.evalFrame(frameNumber, 'outerParam');
+      expect(r.valueAsString, equals('5'));  /// scope: continued
+      r = await isolate.evalFrame(frameNumber, 'outerTemp');
+      expect(r.valueAsString, equals('6'));  /// scope: continued
+      r = await isolate.evalFrame(frameNumber, 'innerParam');
+      expect(r.valueAsString, equals('7'));
+      r = await isolate.evalFrame(frameNumber, 'innerTemp');
+      expect(r.valueAsString, equals('8'));
+      r = await isolate.evalFrame(frameNumber, 'math.sqrt');
+      expect(r.isClosure, isTrue);
+
+      hitBreakpoint = true;
+      sub.cancel();  // Do not handle events for the other tests.
+      isolate.resume();
+    }
+  });
+
+  var result = await rootLib.evaluate('C.method2(3)');
+  print("Result $result");
+  expect(hitBreakpoint, isTrue);
+}
+
+Future testMethod3(Isolate isolate) async {
+  Library rootLib = await isolate.rootLibrary.load();
+  ServiceFunction function =
+      rootLib.functions.singleWhere((f) => f.name == 'breakHere');
+  Breakpoint bpt = await isolate.addBreakpointAtEntry(function);
+  print("Breakpoint: $bpt");
+  expect(bpt is Breakpoint, isTrue);  // I.e, not null.
+
+  bool hitBreakpoint = false;
+  var stream = await isolate.vm.getEventStream(VM.kDebugStream);
+  var sub;
+  sub = stream.listen((ServiceEvent event) async {
+    print("Event $event");
+    if (event.kind == ServiceEvent.kPauseBreakpoint) {
+      var frameNumber = 1, r;
+      r = await isolate.evalFrame(frameNumber, '123');
+      expect(r.valueAsString, equals('123'));
+      r = await isolate.evalFrame(frameNumber, 'this');
+      expect(r.clazz.name, equals('C'));
+      r = await isolate.evalFrame(frameNumber, 'instVar');
+      expect(r.valueAsString, equals('1'));
+      r = await isolate.evalFrame(frameNumber, 'classVar');
+      expect(r.valueAsString, equals('2'));
+      r = await isolate.evalFrame(frameNumber, 'methodParam');
+      expect(r.valueAsString, equals('3'));
+      r = await isolate.evalFrame(frameNumber, 'methodTemp');
+      expect(r.valueAsString, equals('4'));
+      r = await isolate.evalFrame(frameNumber, 'math.sqrt');
+      expect(r.isClosure, isTrue);
+
+      hitBreakpoint = true;
+      sub.cancel();  // Do not handle events for the other tests.
+      isolate.resume();
+    }
+  });
+
+  var result = await rootLib.evaluate('new C().method3(3)');
+  print("Result $result");
+  expect(hitBreakpoint, isTrue);
+}
+
+
+Future testMethod4(Isolate isolate) async {
+  Library rootLib = await isolate.rootLibrary.load();
+  ServiceFunction function =
+      rootLib.functions.singleWhere((f) => f.name == 'breakHere');
+  Breakpoint bpt = await isolate.addBreakpointAtEntry(function);
+  print("Breakpoint: $bpt");
+  expect(bpt is Breakpoint, isTrue);  // I.e, not null.
+
+  bool hitBreakpoint = false;
+  var stream = await isolate.vm.getEventStream(VM.kDebugStream);
+  var sub;
+  sub = stream.listen((ServiceEvent event) async {
+    print("Event $event");
+    if (event.kind == ServiceEvent.kPauseBreakpoint) {
+      var frameNumber = 1, r;
+      r = await isolate.evalFrame(frameNumber, '123');  /// instance: continued
+      expect(r.valueAsString, equals('123'));  /// instance: continued
+      r = await isolate.evalFrame(frameNumber, 'this');  /// scope: continued
+      expect(r.clazz.name, equals('C'));  /// scope: continued
+      r = await isolate.evalFrame(frameNumber, 'instVar');  /// instance: continued
+      expect(r.valueAsString, equals('1'));  /// instance: continued
+      r = await isolate.evalFrame(frameNumber, 'classVar');  /// instance: continued
+      expect(r.valueAsString, equals('2'));  /// instance: continued
+      r = await isolate.evalFrame(frameNumber, 'methodParam');  /// scope: continued
+      expect(r.valueAsString, equals('3'));  /// scope: continued
+      r = await isolate.evalFrame(frameNumber, 'methodTemp');  /// scope: continued
+      expect(r.valueAsString, equals('4'));  /// scope: continued
+      r = await isolate.evalFrame(frameNumber, 'outerParam');  /// scope: continued
+      expect(r.valueAsString, equals('5'));  /// scope: continued
+      r = await isolate.evalFrame(frameNumber, 'outerTemp');  /// scope: continued
+      expect(r.valueAsString, equals('6'));  /// scope: continued
+      r = await isolate.evalFrame(frameNumber, 'innerParam');  /// instance: continued
+      expect(r.valueAsString, equals('7'));  /// instance: continued
+      r = await isolate.evalFrame(frameNumber, 'innerTemp');  /// instance: continued
+      expect(r.valueAsString, equals('8'));  /// instance: continued
+      r = await isolate.evalFrame(frameNumber, 'math.sqrt');  /// instance: continued
+      expect(r.isClosure, isTrue);  /// instance: continued
+
+      hitBreakpoint = true;
+      sub.cancel();  // Do not handle events for the other tests.
+      isolate.resume();
+    }
+  });
+
+  var result = await rootLib.evaluate(
+      '(){ new C().method4(3); C.closureWithReturnedHome(7); }()');
+  print("Result $result");
+  expect(hitBreakpoint, isTrue);
+}
+
+var tests = <IsolateTest>[
+  testMethod,
+  testMethod2,
+  testMethod3,
+  testMethod4,
+];
+
+main(args) => runIsolateTests(args, tests);
diff --git a/runtime/observatory_2/tests/service_2/evaluate_async_closure_test.dart b/runtime/observatory_2/tests/service_2/evaluate_async_closure_test.dart
new file mode 100644
index 0000000..a68c64a
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/evaluate_async_closure_test.dart
@@ -0,0 +1,28 @@
+// Copyright (c) 2018, 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.
+
+import 'dart:async';
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+
+import 'test_helper.dart';
+
+var tests = <IsolateTest>[
+  (Isolate isolate) async {
+    // Silence analyzer on 'dart:async' import.
+    expect(new Future.value(0) != null, isTrue);
+    String test = "(){ "
+        "  var k = () { return Future.value(3); }; "
+        "  var w = () async { return await k(); }; "
+        "  return w(); "
+        "}()";
+    Library lib = await isolate.rootLibrary.load();
+
+    var result = await lib.evaluate(test);
+    expect("$result", equals("Instance(a _Future)"));
+  },
+];
+
+main(args) => runIsolateTests(args, tests);
diff --git a/runtime/observatory_2/tests/service_2/evaluate_class_type_parameters_test.dart b/runtime/observatory_2/tests/service_2/evaluate_class_type_parameters_test.dart
new file mode 100644
index 0000000..05bf1c4
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/evaluate_class_type_parameters_test.dart
@@ -0,0 +1,58 @@
+// Copyright (c) 2018, 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.
+
+import 'dart:developer';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+class A<T> {
+  void foo() {
+    debugger();
+  }
+}
+
+class B<S> extends A<int> {
+  void bar() {
+    debugger();
+  }
+}
+
+testFunction() {
+  var v = new B<String>();
+  v.bar();
+  v.foo();
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  (Isolate isolate) async {
+    // Make sure we are in the right place.
+    var stack = await isolate.getStack();
+    var topFrame = 0;
+    expect(stack.type, equals('Stack'));
+    expect(await stack['frames'][topFrame].location.getLine(), 20);
+
+    Instance result = await isolate.evalFrame(topFrame, '"\$S"');
+    print(result);
+    expect(result.valueAsString, equals("String"));
+  },
+  resumeIsolate,
+  hasStoppedAtBreakpoint,
+  (Isolate isolate) async {
+    // Make sure we are in the right place.
+    var stack = await isolate.getStack();
+    var topFrame = 0;
+    expect(stack.type, equals('Stack'));
+    expect(await stack['frames'][topFrame].location.getLine(), 14);
+
+    Instance result = await isolate.evalFrame(topFrame, '"\$T"');
+    print(result);
+    expect(result.valueAsString, equals("int"));
+  },
+  resumeIsolate,
+];
+
+main(args) => runIsolateTests(args, tests, testeeConcurrent: testFunction);
diff --git a/runtime/observatory_2/tests/service_2/evaluate_function_type_parameters_test.dart b/runtime/observatory_2/tests/service_2/evaluate_function_type_parameters_test.dart
new file mode 100644
index 0000000..1ee95d1
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/evaluate_function_type_parameters_test.dart
@@ -0,0 +1,137 @@
+// Copyright (c) 2018, 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.
+
+import 'dart:developer';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+topLevel<S>() {
+  debugger();
+
+  void inner1<TBool, TString, TDouble, TInt>(TInt x) {
+    debugger();
+  }
+
+  inner1<bool, String, double, int>(3);
+
+  void inner2() {
+    debugger();
+  }
+
+  inner2();
+}
+
+class A {
+  foo<T, S>() {
+    debugger();
+  }
+
+  bar<T>(T t) {
+    debugger();
+  }
+}
+
+void testMain() {
+  topLevel<String>();
+  (new A()).foo<int, bool>();
+  (new A()).bar<dynamic>(42);
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  (Isolate isolate) async {
+    // Make sure we are in the right place.
+    var stack = await isolate.getStack();
+    var topFrame = 0;
+    expect(stack.type, equals('Stack'));
+    expect(await stack['frames'][topFrame].location.getLine(), 14);
+
+    Instance result = await isolate.evalFrame(topFrame, "S.toString()");
+    print(result);
+    expect(result.valueAsString, equals("String"));
+  },
+  resumeIsolate,
+  hasStoppedAtBreakpoint,
+  (Isolate isolate) async {
+    // Make sure we are in the right place.
+    var stack = await isolate.getStack();
+    var topFrame = 0;
+    expect(stack.type, equals('Stack'));
+    expect(await stack['frames'][topFrame].location.getLine(), 16);
+
+    Instance result = await isolate.evalFrame(topFrame, "TBool.toString()");
+    print(result);
+    expect(result.valueAsString, equals("bool"));
+
+    result = await isolate.evalFrame(topFrame, "TString.toString()");
+    print(result);
+    expect(result.valueAsString, equals("String"));
+
+    result = await isolate.evalFrame(topFrame, "TDouble.toString()");
+    print(result);
+    expect(result.valueAsString, equals("double"));
+
+    result = await isolate.evalFrame(topFrame, "TInt.toString()");
+    print(result);
+    expect(result.valueAsString, equals("int"));
+
+    result = await isolate.evalFrame(topFrame, "S.toString()");
+    print(result);
+    expect(result.valueAsString, equals("String"));
+
+    result = await isolate.evalFrame(topFrame, "x");
+    print(result);
+    expect(result.valueAsString, equals("3"));
+  },
+  resumeIsolate,
+  hasStoppedAtBreakpoint,
+  (Isolate isolate) async {
+    // Make sure we are in the right place.
+    var stack = await isolate.getStack();
+    var topFrame = 0;
+    expect(stack.type, equals('Stack'));
+    expect(await stack['frames'][topFrame].location.getLine(), 22);
+
+    Instance result = await isolate.evalFrame(topFrame, "S.toString()");
+    print(result);
+    expect(result.valueAsString, equals("String"));
+  },
+  resumeIsolate,
+  hasStoppedAtBreakpoint,
+  (Isolate isolate) async {
+    // Make sure we are in the right place.
+    var stack = await isolate.getStack();
+    var topFrame = 0;
+    expect(stack.type, equals('Stack'));
+    expect(await stack['frames'][topFrame].location.getLine(), 30);
+
+    Instance result = await isolate.evalFrame(topFrame, "T.toString()");
+    print(result);
+    expect(result.valueAsString, equals("int"));
+
+    result = await isolate.evalFrame(topFrame, "S.toString()");
+    print(result);
+    expect(result.valueAsString, equals("bool"));
+  },
+  resumeIsolate,
+  hasStoppedAtBreakpoint,
+  (Isolate isolate) async {
+    // Make sure we are in the right place.
+    var stack = await isolate.getStack();
+    var topFrame = 0;
+    expect(stack.type, equals('Stack'));
+    expect(await stack['frames'][topFrame].location.getLine(), 34);
+
+    Instance result = await isolate.evalFrame(topFrame, "T.toString()");
+    print(result);
+    expect(result.valueAsString, equals("dynamic"));
+    result = await isolate.evalFrame(topFrame, "t");
+    print(result);
+    expect(result.valueAsString, equals("42"));
+  },
+];
+
+main(args) => runIsolateTests(args, tests, testeeConcurrent: testMain);
diff --git a/runtime/observatory_2/tests/service_2/evaluate_in_async_activation_test.dart b/runtime/observatory_2/tests/service_2/evaluate_in_async_activation_test.dart
new file mode 100644
index 0000000..5dd6681
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/evaluate_in_async_activation_test.dart
@@ -0,0 +1,50 @@
+// Copyright (c) 2016, 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.
+
+import 'dart:async';
+import 'dart:developer';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+testFunction() async {
+  var x = 3;
+  var y = 4;
+  debugger();
+  var z = await new Future(() => x + y);
+  debugger();
+  return z;
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  (Isolate isolate) async {
+    // Make sure we are in the right place.
+    var stack = await isolate.getStack();
+    var topFrame = 0;
+    expect(stack.type, equals('Stack'));
+    expect(await stack['frames'][topFrame].location.getLine(), 16);
+
+    Instance result = await isolate.evalFrame(topFrame, "x");
+    print(result);
+    expect(result.valueAsString, equals("3"));
+  },
+  resumeIsolate,
+  hasStoppedAtBreakpoint,
+  (Isolate isolate) async {
+    // Make sure we are in the right place.
+    var stack = await isolate.getStack();
+    var topFrame = 0;
+    expect(stack.type, equals('Stack'));
+    expect(await stack['frames'][topFrame].location.getLine(), 18);
+
+    Instance result = await isolate.evalFrame(topFrame, "z");
+    print(result);
+    expect(result.valueAsString, equals("7"));
+  },
+  resumeIsolate,
+];
+
+main(args) => runIsolateTests(args, tests, testeeConcurrent: testFunction);
diff --git a/runtime/observatory_2/tests/service_2/evaluate_in_async_star_activation_test.dart b/runtime/observatory_2/tests/service_2/evaluate_in_async_star_activation_test.dart
new file mode 100644
index 0000000..9d1309a
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/evaluate_in_async_star_activation_test.dart
@@ -0,0 +1,54 @@
+// Copyright (c) 2016, 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.
+
+import 'dart:developer';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+generator() async* {
+  var x = 3;
+  var y = 4;
+  debugger();
+  yield y;
+  var z = x + y;
+  debugger();
+  yield z;
+}
+
+testFunction() async {
+  await for (var ignored in generator());
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  (Isolate isolate) async {
+    // Make sure we are in the right place.
+    var stack = await isolate.getStack();
+    var topFrame = 0;
+    expect(stack.type, equals('Stack'));
+    expect(await stack['frames'][topFrame].location.getLine(), 15);
+
+    Instance result = await isolate.evalFrame(topFrame, "x");
+    print(result);
+    expect(result.valueAsString, equals("3"));
+  },
+  resumeIsolate,
+  hasStoppedAtBreakpoint,
+  (Isolate isolate) async {
+    // Make sure we are in the right place.
+    var stack = await isolate.getStack();
+    var topFrame = 0;
+    expect(stack.type, equals('Stack'));
+    expect(await stack['frames'][topFrame].location.getLine(), 18);
+
+    Instance result = await isolate.evalFrame(topFrame, "z");
+    print(result);
+    expect(result.valueAsString, equals("7"));
+  },
+  resumeIsolate,
+];
+
+main(args) => runIsolateTests(args, tests, testeeConcurrent: testFunction);
diff --git a/runtime/observatory_2/tests/service_2/evaluate_in_frame_rpc_test.dart b/runtime/observatory_2/tests/service_2/evaluate_in_frame_rpc_test.dart
new file mode 100644
index 0000000..88d6df2
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/evaluate_in_frame_rpc_test.dart
@@ -0,0 +1,44 @@
+// Copyright (c) 2015, 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.
+
+import 'dart:developer';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+void method(int value, _) {
+  debugger();
+}
+
+void testFunction() {
+  int i = 0;
+  while (true) {
+    if (++i % 100000000 == 0) {
+      method(10000, 50);
+    }
+  }
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+
+// Evaluate against library, class, and instance.
+  (Isolate isolate) async {
+    var result;
+    result = await isolate.evalFrame(0, 'value');
+    expect(result.valueAsString, equals('10000'));
+
+    result = await isolate.evalFrame(0, '_');
+    expect(result.valueAsString, equals('50'));
+
+    result = await isolate.evalFrame(0, 'value + _');
+    expect(result.valueAsString, equals('10050'));
+
+    result = await isolate.evalFrame(1, 'i');
+    expect(result.valueAsString, equals('100000000'));
+  },
+];
+
+main(args) => runIsolateTests(args, tests, testeeConcurrent: testFunction);
diff --git a/runtime/observatory_2/tests/service_2/evaluate_in_frame_with_scope_test.dart b/runtime/observatory_2/tests/service_2/evaluate_in_frame_with_scope_test.dart
new file mode 100644
index 0000000..0f98b20
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/evaluate_in_frame_with_scope_test.dart
@@ -0,0 +1,90 @@
+// Copyright (c) 2017, 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.
+
+import 'dart:developer';
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+var thing1;
+var thing2;
+
+testeeMain() {
+  thing1 = 3;
+  thing2 = 4;
+  foo(42, 1984);
+}
+
+foo(x, y) {
+  var local = x + y;
+  debugger();
+  return local;
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  (Isolate isolate) async {
+    // Make sure we are in the right place.
+    var stack = await isolate.getStack();
+    expect(stack.type, equals('Stack'));
+    expect(stack['frames'].length, greaterThanOrEqualTo(1));
+    expect(stack['frames'][0].function.name, equals('foo'));
+
+    Library lib = await isolate.rootLibrary.load();
+    Field thing1Field =
+        await lib.variables.singleWhere((v) => v.name == "thing1").load();
+    var thing1 = thing1Field.staticValue;
+    print(thing1);
+    Field thing2Field =
+        await lib.variables.singleWhere((v) => v.name == "thing2").load();
+    var thing2 = thing2Field.staticValue;
+    print(thing2);
+
+    ServiceObject result = await isolate.evalFrame(0, "x + y + a + b",
+        scope: <String, ServiceObject>{"a": thing1, "b": thing2});
+    expect(result, isA<Instance>());
+    print(result);
+    expect((result as Instance).valueAsString, equals('2033'));
+
+    result = await isolate.evalFrame(0, "local + a + b",
+        scope: <String, ServiceObject>{"a": thing1, "b": thing2});
+    expect(result, isA<Instance>());
+    print(result);
+    expect((result as Instance).valueAsString, equals('2033'));
+
+    // Note the eval's scope is shadowing the locals' scope.
+    result = await isolate.evalFrame(0, "x + y",
+        scope: <String, ServiceObject>{"x": thing1, "y": thing2});
+    expect(result, isA<Instance>());
+    print(result);
+    expect((result as Instance).valueAsString, equals('7'));
+
+    bool didThrow = false;
+    try {
+      await lib.evaluate("x + y",
+          scope: <String, ServiceObject>{"x": lib, "y": lib});
+    } catch (e) {
+      didThrow = true;
+      expect(e.toString(),
+          contains("Cannot evaluate against a VM-internal object"));
+    }
+    expect(didThrow, isTrue);
+
+    didThrow = false;
+    try {
+      result = await lib.evaluate("x + y",
+          scope: <String, ServiceObject>{"not&an&identifier": thing1});
+      print(result);
+    } catch (e) {
+      didThrow = true;
+      expect(e.toString(), contains("invalid 'scope' parameter"));
+    }
+    expect(didThrow, isTrue);
+  },
+];
+
+main(args) => runIsolateTests(args, tests, testeeConcurrent: testeeMain);
diff --git a/runtime/observatory_2/tests/service_2/evaluate_in_sync_star_activation_test.dart b/runtime/observatory_2/tests/service_2/evaluate_in_sync_star_activation_test.dart
new file mode 100644
index 0000000..54bc903
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/evaluate_in_sync_star_activation_test.dart
@@ -0,0 +1,54 @@
+// Copyright (c) 2016, 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.
+
+import 'dart:developer';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+generator() sync* {
+  var x = 3;
+  var y = 4;
+  debugger();
+  yield y;
+  var z = x + y;
+  debugger();
+  yield z;
+}
+
+testFunction() {
+  for (var ignored in generator());
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  (Isolate isolate) async {
+    // Make sure we are in the right place.
+    var stack = await isolate.getStack();
+    var topFrame = 0;
+    expect(stack.type, equals('Stack'));
+    expect(await stack['frames'][topFrame].location.getLine(), 15);
+
+    Instance result = await isolate.evalFrame(topFrame, "x");
+    print(result);
+    expect(result.valueAsString, equals("3"));
+  },
+  resumeIsolate,
+  hasStoppedAtBreakpoint,
+  (Isolate isolate) async {
+    // Make sure we are in the right place.
+    var stack = await isolate.getStack();
+    var topFrame = 0;
+    expect(stack.type, equals('Stack'));
+    expect(await stack['frames'][topFrame].location.getLine(), 18);
+
+    Instance result = await isolate.evalFrame(topFrame, "z");
+    print(result);
+    expect(result.valueAsString, equals("7"));
+  },
+  resumeIsolate,
+];
+
+main(args) => runIsolateTests(args, tests, testeeConcurrent: testFunction);
diff --git a/runtime/observatory_2/tests/service_2/evaluate_with_escaping_closure_test.dart b/runtime/observatory_2/tests/service_2/evaluate_with_escaping_closure_test.dart
new file mode 100644
index 0000000..64ecacf
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/evaluate_with_escaping_closure_test.dart
@@ -0,0 +1,29 @@
+// Copyright (c) 2019, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+
+dynamic escapedClosure;
+
+testeeMain() {}
+
+var tests = <IsolateTest>[
+  (Isolate isolate) async {
+    Library lib = await isolate.rootLibrary.load();
+
+    Instance result = await lib.evaluate("escapedClosure = (x, y) => x + y");
+    print(result);
+    expect(result.clazz.name, startsWith('_Closure'));
+
+    for (var i = 0; i < 100; i++) {
+      result = await lib.evaluate("escapedClosure(3, 4)");
+      print(result);
+      expect(result.valueAsString, equals('7'));
+    }
+  },
+];
+
+main(args) => runIsolateTests(args, tests, testeeBefore: testeeMain);
diff --git a/runtime/observatory_2/tests/service_2/evaluate_with_scope_test.dart b/runtime/observatory_2/tests/service_2/evaluate_with_scope_test.dart
new file mode 100644
index 0000000..f714823
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/evaluate_with_scope_test.dart
@@ -0,0 +1,58 @@
+// Copyright (c) 2017, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+
+var thing1;
+var thing2;
+
+testeeMain() {
+  thing1 = 3;
+  thing2 = 4;
+}
+
+var tests = <IsolateTest>[
+  (Isolate isolate) async {
+    Library lib = await isolate.rootLibrary.load();
+    Field thing1Field =
+        await lib.variables.singleWhere((v) => v.name == "thing1").load();
+    var thing1 = thing1Field.staticValue;
+    print(thing1);
+    Field thing2Field =
+        await lib.variables.singleWhere((v) => v.name == "thing2").load();
+    var thing2 = thing2Field.staticValue;
+    print(thing2);
+
+    Instance result = await lib.evaluate("x + y",
+        scope: <String, ServiceObject>{"x": thing1, "y": thing2});
+    expect(result.valueAsString, equals('7'));
+
+    bool didThrow = false;
+    try {
+      result = await lib.evaluate("x + y",
+          scope: <String, ServiceObject>{"x": lib, "y": lib});
+      print(result);
+    } catch (e) {
+      didThrow = true;
+      expect(e.toString(),
+          contains("Cannot evaluate against a VM-internal object"));
+    }
+    expect(didThrow, isTrue);
+
+    didThrow = false;
+    try {
+      result = await lib.evaluate("x + y",
+          scope: <String, ServiceObject>{"not&an&identifier": thing1});
+      print(result);
+    } catch (e) {
+      didThrow = true;
+      expect(e.toString(), contains("invalid 'scope' parameter"));
+    }
+    expect(didThrow, isTrue);
+  },
+];
+
+main(args) => runIsolateTests(args, tests, testeeBefore: testeeMain);
diff --git a/runtime/observatory_2/tests/service_2/external_service_asynchronous_invocation_test.dart b/runtime/observatory_2/tests/service_2/external_service_asynchronous_invocation_test.dart
new file mode 100644
index 0000000..5fc82c9
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/external_service_asynchronous_invocation_test.dart
@@ -0,0 +1,171 @@
+// Copyright (c) 2017, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+import 'dart:io' show WebSocket;
+import 'dart:convert' show jsonDecode, jsonEncode;
+import 'dart:async' show Future, Stream, StreamController;
+
+var tests = <IsolateTest>[
+  (Isolate isolate) async {
+    VM vm = isolate.owner;
+
+    final serviceEvents =
+        (await vm.getEventStream('Service')).asBroadcastStream();
+
+    WebSocket _socket =
+        await WebSocket.connect((vm as WebSocketVM).target.networkAddress);
+
+    final socket = new StreamController();
+
+    // Avoid to manually encode and decode messages from the stream
+    Stream<String> stream = socket.stream.map(jsonEncode);
+    stream.cast<Object>().pipe(_socket);
+    dynamic _decoder(dynamic obj) {
+      return jsonDecode(obj);
+    }
+
+    final client = _socket.map(_decoder).asBroadcastStream();
+
+    const successServiceName = 'successService';
+    const errorServiceName = 'errorService';
+    const serviceAlias = 'serviceAlias';
+    const paramKey = 'pkey';
+    const paramValue = 'pvalue';
+    const resultKey = 'rkey';
+    const resultValue = 'rvalue';
+    const errorCode = 5000;
+    const errorKey = 'ekey';
+    const errorValue = 'evalue';
+    const repetition = 5;
+
+    socket.add({
+      'jsonrpc': '2.0',
+      'id': 1,
+      'method': 'registerService',
+      'params': {'service': successServiceName, 'alias': serviceAlias}
+    });
+
+    // Avoid flaky test.
+    // We cannot assume the order in which two messages will arrive
+    // from two different sockets
+    await Future.wait([client.first, serviceEvents.first]);
+
+    // Registering second service
+    socket.add({
+      'jsonrpc': '2.0',
+      'id': 1,
+      'method': 'registerService',
+      'params': {'service': errorServiceName, 'alias': serviceAlias}
+    });
+
+    // Avoid flaky test.
+    // We cannot assume the order in which two messages will arrive
+    // from two different sockets
+    await Future.wait([client.first, serviceEvents.first]);
+
+    // Testing parallel invocation of service which succeeds
+    {
+      final results = new List<Future<Map>>.generate(repetition, (iteration) {
+        final end = iteration.toString();
+        return vm.invokeRpcRaw(
+            vm.services.first.method, {paramKey + end: paramValue + end});
+      });
+      final requests = await (client.take(repetition).toList());
+
+      final completions = requests.map((final request) {
+        final iteration = requests.indexOf(request);
+        final end = iteration.toString();
+
+        // check requests while they arrive
+        expect(request, contains('id'));
+        expect(request['id'], isNotNull);
+        expect(request['method'], equals(successServiceName));
+        expect(request['params'], isNotNull);
+        expect(request['params'][paramKey + end], equals(paramValue + end));
+
+        // answer later
+        return () => socket.add({
+              'jsonrpc': '2.0',
+              'id': request['id'],
+              'result': {resultKey + end: resultValue + end}
+            });
+      }).toList();
+      // random order
+      completions.shuffle();
+      // answer out of order
+      completions.forEach((complete) => complete());
+
+      final responses = await Future.wait(results);
+      responses.forEach((final response) {
+        final iteration = responses.indexOf(response);
+        final end = iteration.toString();
+
+        expect(response, isNotNull);
+        expect(response[resultKey + end], equals(resultValue + end));
+      });
+    }
+
+    // Testing parallel invocation of service which fails
+    {
+      final results = new List<Future<Map>>.generate(repetition, (iteration) {
+        final end = iteration.toString();
+        return vm.invokeRpcRaw(
+            vm.services[1].method, {paramKey + end: paramValue + end});
+      });
+      final requests = await (client.take(repetition).toList());
+
+      final completions = requests.map((final request) {
+        final iteration = requests.indexOf(request);
+        final end = iteration.toString();
+
+        // check requests while they arrive
+        expect(request, contains('id'));
+        expect(request['id'], isNotNull);
+        expect(request['method'], equals(errorServiceName));
+        expect(request['params'], isNotNull);
+        expect(request['params'][paramKey + end], equals(paramValue + end));
+
+        // answer later
+        return () => socket.add({
+              'jsonrpc': '2.0',
+              'id': request['id'],
+              'error': {
+                'code': errorCode + iteration,
+                'data': {errorKey + end: errorValue + end},
+                'message': 'error message',
+              }
+            });
+      }).toList();
+      // random order
+      completions.shuffle();
+      // answer out of order
+      completions.forEach((complete) => complete());
+
+      final errors = await Future.wait(results.map((future) {
+        return future.then<dynamic>((_) {
+          expect(false, isTrue, reason: 'shouldn\'t get here');
+        }).catchError((e) => e);
+      }));
+      errors.forEach((dynamic error) {
+        final iteration = errors.indexOf(error);
+        final end = iteration.toString();
+
+        expect(error, isNotNull);
+        expect(error.code, equals(errorCode + iteration));
+        expect(error.data, isNotNull);
+        expect(error.data[errorKey + end], equals(errorValue + end));
+      });
+    }
+
+    await socket.close();
+  },
+];
+
+main(args) => runIsolateTests(
+      args,
+      tests,
+    );
diff --git a/runtime/observatory_2/tests/service_2/external_service_disappear_test.dart b/runtime/observatory_2/tests/service_2/external_service_disappear_test.dart
new file mode 100644
index 0000000..d9658ef
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/external_service_disappear_test.dart
@@ -0,0 +1,95 @@
+// Copyright (c) 2017, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+import 'dart:io' show WebSocket;
+import 'dart:convert' show jsonDecode, jsonEncode;
+import 'dart:async' show Future, Stream, StreamController;
+
+var tests = <IsolateTest>[
+  (Isolate isolate) async {
+    VM vm = isolate.owner;
+
+    final serviceEvents =
+        (await vm.getEventStream('Service')).asBroadcastStream();
+
+    WebSocket _socket =
+        await WebSocket.connect((vm as WebSocketVM).target.networkAddress);
+
+    final socket = new StreamController();
+
+    // Avoid to manually encode and decode messages from the stream
+    Stream<String> stream = socket.stream.map(jsonEncode);
+    stream.cast<Object>().pipe(_socket);
+    dynamic _decoder(dynamic obj) {
+      return jsonDecode(obj);
+    }
+
+    final client = _socket.map(_decoder).asBroadcastStream();
+
+    // Note: keep this in sync with sdk/lib/vmservice.dart
+    const kServiceDisappeared = 112;
+    const kServiceDisappeared_Msg = 'Service has disappeared';
+
+    const serviceName = 'disapearService';
+    const serviceAlias = 'serviceAlias';
+    const paramKey = 'pkey';
+    const paramValue = 'pvalue';
+    const repetition = 5;
+
+    socket.add({
+      'jsonrpc': '2.0',
+      'id': 1,
+      'method': 'registerService',
+      'params': {'service': serviceName, 'alias': serviceAlias}
+    });
+
+    // Avoid flaky test.
+    // We cannot assume the order in which two messages will arrive
+    // from two different sockets
+    await Future.wait([client.first, serviceEvents.first]);
+
+    // Testing invocation of service which disappear
+    {
+      final results = new List<Future<Map>>.generate(repetition, (iteration) {
+        final end = iteration.toString();
+        return vm.invokeRpcRaw(
+            vm.services.first.method, {paramKey + end: paramValue + end});
+      });
+      final requests = await (client.take(repetition).toList());
+
+      requests.forEach((final request) {
+        final iteration = requests.indexOf(request);
+        final end = iteration.toString();
+
+        // check requests while they arrive
+        expect(request, contains('id'));
+        expect(request['id'], isNotNull);
+        expect(request['method'], equals(serviceName));
+        expect(request['params'], isNotNull);
+        expect(request['params'][paramKey + end], equals(paramValue + end));
+      });
+
+      await socket.close();
+
+      await Future.wait(results.map((future) {
+        return future.then((_) {
+          expect(false, isTrue, reason: 'shouldn\'t get here');
+        }).catchError((Object error_object) {
+          ServerRpcException error = error_object as ServerRpcException;
+          expect(error, isNotNull);
+          expect(error.code, equals(kServiceDisappeared));
+          expect(error.message, equals(kServiceDisappeared_Msg));
+        });
+      }));
+    }
+  },
+];
+
+main(args) => runIsolateTests(
+      args,
+      tests,
+    );
diff --git a/runtime/observatory_2/tests/service_2/external_service_notification_invocation_test.dart b/runtime/observatory_2/tests/service_2/external_service_notification_invocation_test.dart
new file mode 100644
index 0000000..47b202e
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/external_service_notification_invocation_test.dart
@@ -0,0 +1,89 @@
+// Copyright (c) 2017, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+import 'dart:io' show WebSocket;
+import 'dart:convert' show jsonDecode, jsonEncode;
+import 'dart:async' show Future, Stream, StreamController;
+
+var tests = <IsolateTest>[
+  (Isolate isolate) async {
+    VM vm = isolate.owner;
+
+    final serviceEvents =
+        (await vm.getEventStream('Service')).asBroadcastStream();
+
+    WebSocket _socket =
+        await WebSocket.connect((vm as WebSocketVM).target.networkAddress);
+    WebSocket _socket_invoker =
+        await WebSocket.connect((vm as WebSocketVM).target.networkAddress);
+
+    final socket = new StreamController<Map>();
+    final socket_invoker = new StreamController<Map>();
+
+    // Avoid to manually encode and decode messages from the stream
+    Stream<String> socket_stream = socket.stream.map(jsonEncode);
+    socket_stream.cast<Object>().pipe(_socket);
+    Stream<String> socket_invoker_stream =
+        socket_invoker.stream.map(jsonEncode);
+    socket_invoker_stream.cast<Object>().pipe(_socket_invoker);
+    dynamic _decoder(dynamic obj) {
+      return jsonDecode(obj);
+    }
+
+    final client = _socket.map(_decoder).asBroadcastStream();
+    final client_invoker = _socket_invoker.map(_decoder).asBroadcastStream();
+
+    const serviceName = 'successService';
+    const serviceAlias = 'serviceAlias';
+    const paramKey = 'pkey';
+    const paramValue = 'pvalue';
+    const repetition = 5;
+
+    socket.add({
+      'jsonrpc': '2.0',
+      'id': 1,
+      'method': 'registerService',
+      'params': {'service': serviceName, 'alias': serviceAlias}
+    });
+
+    // Avoid flaky test.
+    // We cannot assume the order in which two messages will arrive
+    // from two different sockets
+    await Future.wait([client.first, serviceEvents.first]);
+
+    client_invoker.first.then((_) {
+      expect(false, isTrue, reason: 'shouldn\'t get here');
+    }).catchError((_) => null);
+
+    // Testing serial invocation of service which succedes
+    for (var iteration = 0; iteration < repetition; iteration++) {
+      final end = iteration.toString();
+      socket_invoker.add({
+        'jsonrpc': '2.0',
+        'method': vm.services.first.method,
+        'params': {paramKey + end: paramValue + end}
+      });
+      final request = await client.first;
+
+      expect(request, contains('id'));
+      expect(request['id'], isNotNull);
+      expect(request['method'], equals(serviceName));
+      expect(request['params'], isNotNull);
+      expect(request['params'][paramKey + end], equals(paramValue + end));
+
+      socket.add({'jsonrpc': '2.0', 'id': request['id'], 'result': {}});
+    }
+
+    await socket.close();
+    await socket_invoker.close();
+  },
+];
+
+main(args) => runIsolateTests(
+      args,
+      tests,
+    );
diff --git a/runtime/observatory_2/tests/service_2/external_service_registration_test.dart b/runtime/observatory_2/tests/service_2/external_service_registration_test.dart
new file mode 100644
index 0000000..0356dcb
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/external_service_registration_test.dart
@@ -0,0 +1,129 @@
+// Copyright (c) 2017, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+import 'dart:io' show WebSocket;
+import 'dart:convert' show jsonDecode, jsonEncode;
+import 'dart:async' show Future, Stream, StreamController;
+
+var tests = <IsolateTest>[
+  (Isolate isolate) async {
+    VM vm = isolate.owner;
+
+    final serviceEvents =
+        (await vm.getEventStream('Service')).asBroadcastStream();
+
+    expect(vm.services, isEmpty,
+        reason: 'No service should be registered at startup');
+
+    WebSocket _socket =
+        await WebSocket.connect((vm as WebSocketVM).target.networkAddress);
+
+    final socket = new StreamController();
+
+    // Avoid to manually encode and decode messages from the stream
+    Stream<String> socket_stream = socket.stream.map(jsonEncode);
+    socket_stream.cast<Object>().pipe(_socket);
+    dynamic _decoder(dynamic obj) {
+      return jsonDecode(obj);
+    }
+
+    final client = _socket.map(_decoder).asBroadcastStream();
+
+    // Note: keep this in sync with sdk/lib/vmservice.dart
+    const kServiceAlreadyRegistered = 111;
+    const kServiceAlreadyRegistered_Msg = 'Service already registered';
+
+    const serviceName = 'serviceName';
+    const serviceAlias = 'serviceAlias';
+
+    {
+      // Registering first service
+      socket.add({
+        'jsonrpc': '2.0',
+        'id': 1,
+        'method': 'registerService',
+        'params': {'service': serviceName, 'alias': serviceAlias}
+      });
+
+      // Avoid flaky test.
+      // We cannot assume the order in which two messages will arrive
+      // from two different sockets
+      final message =
+          (await Future.wait([client.first, serviceEvents.first])).first;
+
+      expect(message['id'], equals(1),
+          reason: 'Should answer with the same id');
+      expect(message['result'], isNotEmpty);
+      expect(message['result']['type'], equals('Success'));
+
+      expect(vm.services, isNotEmpty);
+      expect(vm.services.length, equals(1));
+      expect(vm.services.first.service, equals(serviceName));
+      expect(vm.services.first.method, isNotEmpty);
+      expect(vm.services.first.alias, equals(serviceAlias));
+    }
+
+    {
+      // Registering second service
+      socket.add({
+        'jsonrpc': '2.0',
+        'id': 1,
+        'method': 'registerService',
+        'params': {'service': serviceName + '2', 'alias': serviceAlias + '2'}
+      });
+
+      // Avoid flaky test.
+      // We cannot assume the order in which two messages will arrive
+      // from two different sockets
+      final message =
+          (await Future.wait([client.first, serviceEvents.first])).first;
+
+      expect(message['id'], equals(1),
+          reason: 'Should answer with the same id');
+      expect(message['result'], isNotEmpty);
+      expect(message['result']['type'], equals('Success'));
+
+      expect(vm.services, isNotEmpty);
+      expect(vm.services.length, equals(2));
+      expect(vm.services[1].service, equals(serviceName + '2'));
+      expect(vm.services[1].method, isNotEmpty);
+      expect(vm.services[1].alias, equals(serviceAlias + '2'));
+    }
+
+    {
+      // Double registering first service
+      socket.add({
+        'jsonrpc': '2.0',
+        'id': 1,
+        'method': 'registerService',
+        'params': {'service': serviceName, 'alias': serviceAlias}
+      });
+
+      final message = await client.first;
+
+      expect(message['id'], equals(1),
+          reason: 'Should answer with the same id');
+      expect(message['error'], isNotEmpty);
+      expect(message['error']['code'], equals(kServiceAlreadyRegistered));
+      expect(
+          message['error']['message'], equals(kServiceAlreadyRegistered_Msg));
+    }
+
+    // Avoid flaky test.
+    // We cannot assume the order in which two messages will arrive
+    // from two different sockets
+    await Future.wait([socket.close(), serviceEvents.take(2).last]);
+
+    expect(vm.services, isEmpty,
+        reason: 'Should unregister services when client disconnects');
+  },
+];
+
+main(args) => runIsolateTests(
+      args,
+      tests,
+    );
diff --git a/runtime/observatory_2/tests/service_2/external_service_registration_via_notification_test.dart b/runtime/observatory_2/tests/service_2/external_service_registration_via_notification_test.dart
new file mode 100644
index 0000000..927d801
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/external_service_registration_via_notification_test.dart
@@ -0,0 +1,109 @@
+// Copyright (c) 2017, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+import 'dart:io' show WebSocket;
+import 'dart:convert' show jsonDecode, jsonEncode;
+import 'dart:async' show Future, Stream, StreamController;
+
+var tests = <IsolateTest>[
+  (Isolate isolate) async {
+    VM vm = isolate.owner;
+
+    final serviceEvents =
+        (await vm.getEventStream('Service')).asBroadcastStream();
+
+    expect(vm.services, isEmpty,
+        reason: 'No service should be registered at startup');
+
+    WebSocket _socket =
+        await WebSocket.connect((vm as WebSocketVM).target.networkAddress);
+
+    final socket = new StreamController();
+
+    // Avoid to manually encode and decode messages from the stream
+    Stream<String> stream = socket.stream.map(jsonEncode);
+    stream.cast<Object>().pipe(_socket);
+    dynamic _decoder(dynamic obj) {
+      return jsonDecode(obj);
+    }
+
+    final client = _socket.map(_decoder).asBroadcastStream();
+
+    // Note: keep this in sync with sdk/lib/vmservice.dart
+    const kServiceAlreadyRegistered = 111;
+    const kServiceAlreadyRegistered_Msg = 'Service already registered';
+
+    const serviceName = 'serviceName';
+    const serviceAlias = 'serviceAlias';
+
+    {
+      // Registering first service
+      socket.add({
+        'jsonrpc': '2.0',
+        'method': 'registerService',
+        'params': {'service': serviceName, 'alias': serviceAlias}
+      });
+
+      await serviceEvents.first;
+
+      expect(vm.services, isNotEmpty);
+      expect(vm.services.length, equals(1));
+      expect(vm.services.first.service, equals(serviceName));
+      expect(vm.services.first.method, isNotEmpty);
+      expect(vm.services.first.alias, equals(serviceAlias));
+    }
+
+    {
+      // Registering second service
+      socket.add({
+        'jsonrpc': '2.0',
+        'method': 'registerService',
+        'params': {'service': serviceName + '2', 'alias': serviceAlias + '2'}
+      });
+
+      await serviceEvents.first;
+
+      expect(vm.services, isNotEmpty);
+      expect(vm.services.length, equals(2));
+      expect(vm.services[1].service, equals(serviceName + '2'));
+      expect(vm.services[1].method, isNotEmpty);
+      expect(vm.services[1].alias, equals(serviceAlias + '2'));
+    }
+
+    {
+      // Double registering first service
+      socket.add({
+        'jsonrpc': '2.0',
+        'id': 1,
+        'method': 'registerService',
+        'params': {'service': serviceName, 'alias': serviceAlias}
+      });
+
+      final message = await client.first;
+
+      expect(message['id'], equals(1),
+          reason: 'Should answer with the same id');
+      expect(message['error'], isNotEmpty);
+      expect(message['error']['code'], equals(kServiceAlreadyRegistered));
+      expect(
+          message['error']['message'], equals(kServiceAlreadyRegistered_Msg));
+    }
+
+    // Avoid flaky test.
+    // We cannot assume the order in which two messages will arrive
+    // from two different sockets
+    await Future.wait([socket.close(), serviceEvents.take(2).last]);
+
+    expect(vm.services, isEmpty,
+        reason: 'Should unregister services when client disconnects');
+  },
+];
+
+main(args) => runIsolateTests(
+      args,
+      tests,
+    );
diff --git a/runtime/observatory_2/tests/service_2/external_service_synchronous_invocation_test.dart b/runtime/observatory_2/tests/service_2/external_service_synchronous_invocation_test.dart
new file mode 100644
index 0000000..33dffe1
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/external_service_synchronous_invocation_test.dart
@@ -0,0 +1,133 @@
+// Copyright (c) 2017, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+import 'dart:io' show WebSocket;
+import 'dart:convert' show jsonDecode, jsonEncode;
+import 'dart:async' show Future, Stream, StreamController;
+
+var tests = <IsolateTest>[
+  (Isolate isolate) async {
+    VM vm = isolate.owner;
+
+    final serviceEvents =
+        (await vm.getEventStream('Service')).asBroadcastStream();
+
+    WebSocket _socket =
+        await WebSocket.connect((vm as WebSocketVM).target.networkAddress);
+
+    final socket = new StreamController();
+
+    // Avoid to manually encode and decode messages from the stream
+    Stream<String> stream = socket.stream.map(jsonEncode);
+    stream.cast<Object>().pipe(_socket);
+    dynamic _decoder(dynamic obj) {
+      return jsonDecode(obj);
+    }
+
+    final client = _socket.map(_decoder).asBroadcastStream();
+
+    const successServiceName = 'successService';
+    const errorServiceName = 'errorService';
+    const serviceAlias = 'serviceAlias';
+    const paramKey = 'pkey';
+    const paramValue = 'pvalue';
+    const resultKey = 'rkey';
+    const resultValue = 'rvalue';
+    const errorCode = 5000;
+    const errorKey = 'ekey';
+    const errorValue = 'evalue';
+    const repetition = 5;
+
+    socket.add({
+      'jsonrpc': '2.0',
+      'id': 1,
+      'method': 'registerService',
+      'params': {'service': successServiceName, 'alias': serviceAlias}
+    });
+
+    // Avoid flaky test.
+    // We cannot assume the order in which two messages will arrive
+    // from two different sockets
+    await Future.wait([client.first, serviceEvents.first]);
+
+    // Registering second service
+    socket.add({
+      'jsonrpc': '2.0',
+      'id': 1,
+      'method': 'registerService',
+      'params': {'service': errorServiceName, 'alias': serviceAlias}
+    });
+
+    // Avoid flaky test.
+    // We cannot assume the order in which two messages will arrive
+    // from two different sockets
+    await Future.wait([client.first, serviceEvents.first]);
+
+    // Testing serial invocation of service which succeeds
+    for (var iteration = 0; iteration < repetition; iteration++) {
+      final end = iteration.toString();
+      final result = vm.invokeRpcRaw(
+          vm.services.first.method, {paramKey + end: paramValue + end});
+      final request = await client.first;
+
+      expect(request, contains('id'));
+      expect(request['id'], isNotNull);
+      expect(request['method'], equals(successServiceName));
+      expect(request['params'], isNotNull);
+      expect(request['params'][paramKey + end], equals(paramValue + end));
+
+      socket.add({
+        'jsonrpc': '2.0',
+        'id': request['id'],
+        'result': {resultKey + end: resultValue + end}
+      });
+
+      final response = await result;
+
+      expect(response, isNotNull);
+      expect(response[resultKey + end], equals(resultValue + end));
+    }
+
+    // Testing serial invocation of service which fails
+    for (var iteration = 0; iteration < repetition; iteration++) {
+      final end = iteration.toString();
+      final result = vm.invokeRpcRaw(
+          vm.services[1].method, {paramKey + end: paramValue + end});
+      final request = await client.first;
+
+      expect(request, contains('id'));
+      expect(request['id'], isNotNull);
+      expect(request['method'], equals(errorServiceName));
+      expect(request['params'], isNotNull);
+      expect(request['params'][paramKey + end], equals(paramValue + end));
+
+      socket.add({
+        'jsonrpc': '2.0',
+        'id': request['id'],
+        'error': {
+          'code': errorCode + iteration,
+          'data': {errorKey + end: errorValue + end},
+          'message': 'error message',
+        }
+      });
+
+      try {
+        final response = await result;
+        expect(false, isTrue, reason: 'shouldn\'t get here');
+      } on ServerRpcException catch (e) {
+        expect(e.code, equals(errorCode + iteration));
+        expect(e.data, isNotNull);
+        expect(e.data[errorKey + end], equals(errorValue + end));
+      }
+    }
+  },
+];
+
+main(args) => runIsolateTests(
+      args,
+      tests,
+    );
diff --git a/runtime/observatory_2/tests/service_2/field_script_other.dart b/runtime/observatory_2/tests/service_2/field_script_other.dart
new file mode 100644
index 0000000..ed9681e
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/field_script_other.dart
@@ -0,0 +1,7 @@
+// Copyright (c) 2017, 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 field_script_test;
+
+var otherField = 42;
diff --git a/runtime/observatory_2/tests/service_2/field_script_test.dart b/runtime/observatory_2/tests/service_2/field_script_test.dart
new file mode 100644
index 0000000..e83c57f
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/field_script_test.dart
@@ -0,0 +1,42 @@
+// Copyright (c) 2017, 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.
+
+library field_script_test;
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+part 'field_script_other.dart';
+
+code() {
+  print(otherField);
+}
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  (Isolate isolate) async {
+    Library lib = await isolate.rootLibrary.load();
+    var fields = lib.variables;
+    expect(fields.length, 2);
+    print(lib.variables);
+    for (Field f in fields) {
+      await f.load();
+      String locationString = await f.location.toUserString();
+      if (f.name == "tests") {
+        expect(locationString, "field_script_test.dart:18:5");
+      } else if (f.name == "otherField") {
+        expect(locationString, "field_script_other.dart:7:5");
+      } else {
+        fail("Unexpected field: ${f.name}");
+      }
+    }
+  }
+];
+
+main(args) {
+  runIsolateTestsSynchronous(args, tests,
+      testeeConcurrent: code, pause_on_start: true, pause_on_exit: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/file_service_test.dart b/runtime/observatory_2/tests/service_2/file_service_test.dart
new file mode 100644
index 0000000..be3b3cf
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/file_service_test.dart
@@ -0,0 +1,105 @@
+// Copyright (c) 2015, 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.
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:developer';
+import 'dart:io' as io;
+import 'package:expect/expect.dart';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+
+Future setupFiles() async {
+  final dir = await io.Directory.systemTemp.createTemp('file_service');
+  var writingFile;
+  var readingFile;
+
+  void closeDown() {
+    if (writingFile != null) {
+      writingFile.closeSync();
+    }
+    if (readingFile != null) {
+      readingFile.closeSync();
+    }
+    dir.deleteSync(recursive: true);
+  }
+
+  Future<ServiceExtensionResponse> cleanup(ignored_a, ignored_b) {
+    closeDown();
+    final result = jsonEncode({'type': 'foobar'});
+    return Future.value(ServiceExtensionResponse.result(result));
+  }
+
+  Future<ServiceExtensionResponse> setup(ignored_a, ignored_b) async {
+    try {
+      final filePath = dir.path + io.Platform.pathSeparator + "file";
+      final f = io.File(filePath);
+      writingFile = await f.open(mode: io.FileMode.write);
+      await writingFile.writeByte(42);
+      await writingFile.writeByte(42);
+      await writingFile.writeByte(42);
+
+      final file = io.File.fromUri(io.Platform.script);
+      readingFile = await file.open();
+      await readingFile.readByte();
+      await readingFile.readByte();
+      await readingFile.readByte();
+      await readingFile.readByte();
+      await readingFile.readByte();
+
+      // The utility functions should close the files after them, so we
+      // don't expect the calls below to result in open files.
+      final writeTemp = dir.path + io.Platform.pathSeparator + "other_file";
+      final utilFile = io.File(writeTemp);
+      await utilFile.writeAsString('foobar');
+      final readTemp = io.File(writeTemp);
+      final result = await readTemp.readAsString();
+      Expect.equals(result, 'foobar');
+    } catch (e) {
+      closeDown();
+      throw e;
+    }
+    final result = jsonEncode({'type': 'foobar'});
+    return Future.value(ServiceExtensionResponse.result(result));
+  }
+
+  registerExtension('ext.dart.io.cleanup', cleanup);
+  registerExtension('ext.dart.io.setup', setup);
+}
+
+var fileTests = <IsolateTest>[
+  (Isolate isolate) async {
+    await isolate.invokeRpcNoUpgrade('ext.dart.io.setup', {});
+    try {
+      final result =
+          await isolate.invokeRpcNoUpgrade('ext.dart.io.getOpenFiles', {});
+      expect(result['type'], equals('OpenFileList'));
+      expect(result['files'].length, equals(2));
+      final writing = await isolate.invokeRpcNoUpgrade(
+          'ext.dart.io.getOpenFileById', {'id': result['files'][0]['id']});
+
+      expect(writing['readBytes'], equals(0));
+      expect(writing['readCount'], equals(0));
+      expect(writing['writeCount'], equals(3));
+      expect(writing['writeBytes'], equals(3));
+      expect(writing['lastWriteTime'], greaterThan(0));
+      expect(writing['lastReadTime'], equals(0));
+
+      final reading = await isolate.invokeRpcNoUpgrade(
+          'ext.dart.io.getOpenFileById', {'id': result['files'][1]['id']});
+
+      expect(reading['readBytes'], equals(5));
+      expect(reading['readCount'], equals(5));
+      expect(reading['writeCount'], equals(0));
+      expect(reading['writeBytes'], equals(0));
+      expect(reading['lastWriteTime'], equals(0));
+      expect(reading['lastReadTime'], greaterThan(0));
+    } finally {
+      await isolate.invokeRpcNoUpgrade('ext.dart.io.cleanup', {});
+    }
+  },
+];
+
+main(args) async => runIsolateTests(args, fileTests, testeeBefore: setupFiles);
diff --git a/runtime/observatory_2/tests/service_2/gc_test.dart b/runtime/observatory_2/tests/service_2/gc_test.dart
new file mode 100644
index 0000000..014f661
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/gc_test.dart
@@ -0,0 +1,42 @@
+// Copyright (c) 2014, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'test_helper.dart';
+
+import 'dart:async';
+
+void script() {
+  var grow;
+  grow = (int iterations, int size, Duration duration) {
+    if (iterations <= 0) {
+      return;
+    }
+    new List<int>.filled(size, 0);
+    new Timer(duration, () => grow(iterations - 1, size, duration));
+  };
+  grow(100, 1 << 24, new Duration(seconds: 1));
+}
+
+var tests = <IsolateTest>[
+  (Isolate isolate) {
+    Completer completer = new Completer();
+    // Expect at least this many GC events.
+    int gcCountdown = 3;
+    isolate.vm.getEventStream(VM.kGCStream).then((stream) {
+      var subscription;
+      subscription = stream.listen((ServiceEvent event) {
+        assert(event.kind == ServiceEvent.kGC);
+        print('Received GC event');
+        if (--gcCountdown == 0) {
+          subscription.cancel();
+          completer.complete();
+        }
+      });
+    });
+    return completer.future;
+  },
+];
+
+main(args) => runIsolateTests(args, tests, testeeConcurrent: script);
diff --git a/runtime/observatory_2/tests/service_2/get_allocation_profile_public_rpc_test.dart b/runtime/observatory_2/tests/service_2/get_allocation_profile_public_rpc_test.dart
new file mode 100644
index 0000000..c63b282
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/get_allocation_profile_public_rpc_test.dart
@@ -0,0 +1,132 @@
+// Copyright (c) 2019, 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.
+
+import 'dart:async';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+
+import 'test_helper.dart';
+
+Future sleep(int milliseconds) {
+  Completer completer = new Completer();
+  Duration duration = new Duration(milliseconds: milliseconds);
+  new Timer(duration, () => completer.complete());
+  return completer.future;
+}
+
+var tests = <IsolateTest>[
+  (Isolate isolate) async {
+    var params = {};
+    var result =
+        await isolate.invokeRpcNoUpgrade('getAllocationProfile', params);
+    expect(result['type'], equals('AllocationProfile'));
+    expect(result.containsKey('dateLastAccumulatorReset'), isFalse);
+    expect(result.containsKey('dateLastServiceGC'), isFalse);
+    expect(result.containsKey('_heaps'), isFalse);
+    expect(result['members'].length, isPositive);
+
+    var member = result['members'][0];
+    expect(member['type'], equals('ClassHeapStats'));
+    expect(member.containsKey('_new'), isFalse);
+    expect(member.containsKey('_old'), isFalse);
+    expect(member.containsKey('_promotedInstances'), isFalse);
+    expect(member.containsKey('_promotedBytes'), isFalse);
+    expect(member.containsKey('instancesAccumulated'), isTrue);
+    expect(member.containsKey('instancesCurrent'), isTrue);
+    expect(member.containsKey('bytesCurrent'), isTrue);
+    expect(member.containsKey('accumulatedSize'), isTrue);
+
+    // reset.
+    params = {
+      'reset': 'true',
+    };
+    result = await isolate.invokeRpcNoUpgrade('getAllocationProfile', params);
+    expect(result['type'], equals('AllocationProfile'));
+    var firstReset = result['dateLastAccumulatorReset'];
+    expect(firstReset, isA<String>());
+    expect(result.containsKey('dateLastServiceGC'), isFalse);
+    expect(result.containsKey('_heaps'), isFalse);
+    expect(result['members'].length, isPositive);
+
+    member = result['members'][0];
+    expect(member['type'], equals('ClassHeapStats'));
+    expect(member.containsKey('_new'), isFalse);
+    expect(member.containsKey('_old'), isFalse);
+    expect(member.containsKey('_promotedInstances'), isFalse);
+    expect(member.containsKey('_promotedBytes'), isFalse);
+    expect(member.containsKey('instancesAccumulated'), isTrue);
+    expect(member.containsKey('instancesCurrent'), isTrue);
+    expect(member.containsKey('bytesCurrent'), isTrue);
+    expect(member.containsKey('accumulatedSize'), isTrue);
+
+    await sleep(1000);
+
+    result = await isolate.invokeRpcNoUpgrade('getAllocationProfile', params);
+    var secondReset = result['dateLastAccumulatorReset'];
+    expect(secondReset, isNot(equals(firstReset)));
+
+    // gc.
+    params = {
+      'gc': 'true',
+    };
+    result = await isolate.invokeRpcNoUpgrade('getAllocationProfile', params);
+    expect(result['type'], equals('AllocationProfile'));
+    expect(result['dateLastAccumulatorReset'], equals(secondReset));
+    var firstGC = result['dateLastServiceGC'];
+    expect(firstGC, isA<String>());
+    expect(result.containsKey('_heaps'), isFalse);
+    expect(result['members'].length, isPositive);
+
+    member = result['members'][0];
+    expect(member['type'], equals('ClassHeapStats'));
+    expect(member.containsKey('_new'), isFalse);
+    expect(member.containsKey('_old'), isFalse);
+    expect(member.containsKey('_promotedInstances'), isFalse);
+    expect(member.containsKey('_promotedBytes'), isFalse);
+    expect(member.containsKey('instancesAccumulated'), isTrue);
+    expect(member.containsKey('instancesCurrent'), isTrue);
+    expect(member.containsKey('bytesCurrent'), isTrue);
+    expect(member.containsKey('accumulatedSize'), isTrue);
+
+    await sleep(1000);
+
+    result = await isolate.invokeRpcNoUpgrade('getAllocationProfile', params);
+    var secondGC = result['dateLastAccumulatorReset'];
+    expect(secondGC, isNot(equals(firstGC)));
+  },
+  (Isolate isolate) async {
+    var params = {
+      'reset': 'banana',
+    };
+    bool caughtException;
+    try {
+      await isolate.invokeRpcNoUpgrade('getAllocationProfile', params);
+      expect(false, isTrue, reason: 'Unreachable');
+    } on ServerRpcException catch (e) {
+      caughtException = true;
+      expect(e.code, equals(ServerRpcException.kInvalidParams));
+      expect(e.data['details'],
+          "getAllocationProfile: invalid \'reset\' parameter: banana");
+    }
+    expect(caughtException, isTrue);
+  },
+  (Isolate isolate) async {
+    var params = {
+      'gc': 'banana',
+    };
+    bool caughtException;
+    try {
+      await isolate.invokeRpcNoUpgrade('getAllocationProfile', params);
+      expect(false, isTrue, reason: 'Unreachable');
+    } on ServerRpcException catch (e) {
+      caughtException = true;
+      expect(e.code, equals(ServerRpcException.kInvalidParams));
+      expect(e.data['details'],
+          "getAllocationProfile: invalid \'gc\' parameter: banana");
+    }
+    expect(caughtException, isTrue);
+  },
+];
+
+main(args) async => runIsolateTests(args, tests);
diff --git a/runtime/observatory_2/tests/service_2/get_allocation_profile_rpc_test.dart b/runtime/observatory_2/tests/service_2/get_allocation_profile_rpc_test.dart
new file mode 100644
index 0000000..a589331
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/get_allocation_profile_rpc_test.dart
@@ -0,0 +1,132 @@
+// Copyright (c) 2015, 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.
+
+import 'dart:async';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+
+import 'test_helper.dart';
+
+Future sleep(int milliseconds) {
+  Completer completer = new Completer();
+  Duration duration = new Duration(milliseconds: milliseconds);
+  new Timer(duration, () => completer.complete());
+  return completer.future;
+}
+
+var tests = <IsolateTest>[
+  (Isolate isolate) async {
+    var params = {};
+    var result =
+        await isolate.invokeRpcNoUpgrade('_getAllocationProfile', params);
+    expect(result['type'], equals('AllocationProfile'));
+    expect(result.containsKey('dateLastAccumulatorReset'), isFalse);
+    expect(result.containsKey('dateLastServiceGC'), isFalse);
+    expect(result['_heaps'].length, isPositive);
+    expect(result['_heaps']['new']['type'], equals('HeapSpace'));
+    expect(result['_heaps']['old']['type'], equals('HeapSpace'));
+    expect(result['members'].length, isPositive);
+
+    var member = result['members'][0];
+    expect(member['type'], equals('ClassHeapStats'));
+    expect(member.containsKey('_new'), isTrue);
+    expect(member.containsKey('_old'), isTrue);
+    expect(member.containsKey('instancesAccumulated'), isTrue);
+    expect(member.containsKey('instancesCurrent'), isTrue);
+    expect(member.containsKey('bytesCurrent'), isTrue);
+    expect(member.containsKey('accumulatedSize'), isTrue);
+
+    // reset.
+    params = {
+      'reset': 'true',
+    };
+    result = await isolate.invokeRpcNoUpgrade('_getAllocationProfile', params);
+    expect(result['type'], equals('AllocationProfile'));
+    var firstReset = result['dateLastAccumulatorReset'];
+    expect(firstReset, isA<String>());
+    expect(result.containsKey('dateLastServiceGC'), isFalse);
+    expect(result['_heaps'].length, isPositive);
+    expect(result['_heaps']['new']['type'], equals('HeapSpace'));
+    expect(result['_heaps']['old']['type'], equals('HeapSpace'));
+    expect(result['members'].length, isPositive);
+
+    member = result['members'][0];
+    expect(member['type'], equals('ClassHeapStats'));
+    expect(member.containsKey('_new'), isTrue);
+    expect(member.containsKey('_old'), isTrue);
+    expect(member.containsKey('instancesAccumulated'), isTrue);
+    expect(member.containsKey('instancesCurrent'), isTrue);
+    expect(member.containsKey('bytesCurrent'), isTrue);
+    expect(member.containsKey('accumulatedSize'), isTrue);
+
+    await sleep(1000);
+
+    result = await isolate.invokeRpcNoUpgrade('_getAllocationProfile', params);
+    var secondReset = result['dateLastAccumulatorReset'];
+    expect(secondReset, isNot(equals(firstReset)));
+
+    // gc.
+    params = {
+      'gc': 'true',
+    };
+    result = await isolate.invokeRpcNoUpgrade('_getAllocationProfile', params);
+    expect(result['type'], equals('AllocationProfile'));
+    expect(result['dateLastAccumulatorReset'], equals(secondReset));
+    var firstGC = result['dateLastServiceGC'];
+    expect(firstGC, isA<String>());
+    expect(result['_heaps'].length, isPositive);
+    expect(result['_heaps']['new']['type'], equals('HeapSpace'));
+    expect(result['_heaps']['old']['type'], equals('HeapSpace'));
+    expect(result['members'].length, isPositive);
+
+    member = result['members'][0];
+    expect(member['type'], equals('ClassHeapStats'));
+    expect(member.containsKey('_new'), isTrue);
+    expect(member.containsKey('_old'), isTrue);
+    expect(member.containsKey('instancesAccumulated'), isTrue);
+    expect(member.containsKey('instancesCurrent'), isTrue);
+    expect(member.containsKey('bytesCurrent'), isTrue);
+    expect(member.containsKey('accumulatedSize'), isTrue);
+
+    await sleep(1000);
+
+    result = await isolate.invokeRpcNoUpgrade('_getAllocationProfile', params);
+    var secondGC = result['dateLastAccumulatorReset'];
+    expect(secondGC, isNot(equals(firstGC)));
+  },
+  (Isolate isolate) async {
+    var params = {
+      'reset': 'banana',
+    };
+    bool caughtException;
+    try {
+      await isolate.invokeRpcNoUpgrade('_getAllocationProfile', params);
+      expect(false, isTrue, reason: 'Unreachable');
+    } on ServerRpcException catch (e) {
+      caughtException = true;
+      expect(e.code, equals(ServerRpcException.kInvalidParams));
+      expect(e.data['details'],
+          "_getAllocationProfile: invalid \'reset\' parameter: banana");
+    }
+    expect(caughtException, isTrue);
+  },
+  (Isolate isolate) async {
+    var params = {
+      'gc': 'banana',
+    };
+    bool caughtException;
+    try {
+      await isolate.invokeRpcNoUpgrade('_getAllocationProfile', params);
+      expect(false, isTrue, reason: 'Unreachable');
+    } on ServerRpcException catch (e) {
+      caughtException = true;
+      expect(e.code, equals(ServerRpcException.kInvalidParams));
+      expect(e.data['details'],
+          "_getAllocationProfile: invalid \'gc\' parameter: banana");
+    }
+    expect(caughtException, isTrue);
+  },
+];
+
+main(args) async => runIsolateTests(args, tests);
diff --git a/runtime/observatory_2/tests/service_2/get_allocation_samples_test.dart b/runtime/observatory_2/tests/service_2/get_allocation_samples_test.dart
new file mode 100644
index 0000000..87eebfd
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/get_allocation_samples_test.dart
@@ -0,0 +1,91 @@
+// Copyright (c) 2015, 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.
+
+import 'dart:developer';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/service_io.dart';
+import 'package:observatory_2/sample_profile.dart';
+import 'package:test/test.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+class Foo {
+  Foo() {
+    print('new Foo');
+  }
+}
+
+void test() {
+  debugger();
+  // Toggled on.
+  debugger();
+  debugger();
+  // Allocation.
+  new Foo();
+  debugger();
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+
+  // Initial.
+  (Isolate isolate) async {
+    // Verify initial state of 'Foo'.
+    var fooClass = await getClassFromRootLib(isolate, 'Foo');
+    expect(fooClass, isNotNull);
+    expect(fooClass.name, equals('Foo'));
+    print(fooClass.id);
+    expect(fooClass.traceAllocations, isFalse);
+    await fooClass.setTraceAllocations(true);
+    await fooClass.reload();
+    expect(fooClass.traceAllocations, isTrue);
+  },
+
+  resumeIsolate,
+  hasStoppedAtBreakpoint,
+  // Extra debugger stop, continue to allow the allocation stubs to be switched
+  // over. This is a bug but low priority.
+  resumeIsolate,
+  hasStoppedAtBreakpoint,
+
+  // Allocation profile.
+  (Isolate isolate) async {
+    var fooClass = await getClassFromRootLib(isolate, 'Foo');
+    await fooClass.reload();
+    expect(fooClass.traceAllocations, isTrue);
+    dynamic profileResponse = await fooClass.getAllocationSamples();
+    expect(profileResponse, isNotNull);
+    expect(profileResponse['type'], equals('_CpuProfile'));
+    await fooClass.setTraceAllocations(false);
+    await fooClass.reload();
+    expect(fooClass.traceAllocations, isFalse);
+    SampleProfile cpuProfile = new SampleProfile();
+    await cpuProfile.load(isolate, profileResponse);
+    cpuProfile.buildCodeCallerAndCallees();
+    cpuProfile.buildFunctionCallerAndCallees();
+    var tree = cpuProfile.loadCodeTree(M.ProfileTreeDirection.exclusive);
+    var node = tree.root;
+    var expected = [
+      'Root',
+      'DRT_AllocateObject',
+      '[Stub] Allocate Foo',
+      'test',
+      'test',
+      '_Closure.call'
+    ];
+    for (var i = 0; i < expected.length; i++) {
+      expect(node.profileCode.code.name, equals(expected[i]));
+      // Depth first traversal.
+      if (node.children.length == 0) {
+        node = null;
+      } else {
+        node = node.children[0];
+      }
+      expect(node, isNotNull);
+    }
+  },
+  resumeIsolate,
+];
+
+main(args) async => runIsolateTests(args, tests, testeeConcurrent: test);
diff --git a/runtime/observatory_2/tests/service_2/get_client_name_rpc_test.dart b/runtime/observatory_2/tests/service_2/get_client_name_rpc_test.dart
new file mode 100644
index 0000000..6f29c9c
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/get_client_name_rpc_test.dart
@@ -0,0 +1,45 @@
+// Copyright (c) 2020, 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.
+
+import 'dart:async';
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+
+import 'test_helper.dart';
+
+void fooBar() {}
+
+Future<String> getClientName(Isolate isolate) async {
+  final result = await isolate.vm.invokeRpcNoUpgrade('getClientName', {});
+  return result['name'];
+}
+
+Future<void> setClientName(Isolate isolate, String name) async =>
+    await isolate.vm.invokeRpcNoUpgrade('setClientName', {
+      'name': name,
+    });
+
+final test = <IsolateTest>[
+  (Isolate isolate) async {
+    // Each client has a default name based on the order of connection to the
+    // service.
+    expect(await getClientName(isolate), 'client1');
+
+    // Set a custom client name and check it was set properly.
+    await setClientName(isolate, 'foobar');
+    expect(await getClientName(isolate), 'foobar');
+
+    // Clear the client name and check that we're using the default again.
+    await setClientName(isolate, '');
+    expect(await getClientName(isolate), 'client1');
+  },
+];
+
+Future<void> main(args) => runIsolateTests(
+      args,
+      test,
+      testeeBefore: fooBar,
+      enableService: false,
+    );
diff --git a/runtime/observatory_2/tests/service_2/get_cpu_profile_timeline_rpc_test.dart b/runtime/observatory_2/tests/service_2/get_cpu_profile_timeline_rpc_test.dart
new file mode 100644
index 0000000..40db79e
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/get_cpu_profile_timeline_rpc_test.dart
@@ -0,0 +1,62 @@
+// Copyright (c) 2015, 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.
+
+import 'package:observatory_2/src/repositories/timeline_base.dart';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+
+import 'test_helper.dart';
+
+fib(n) {
+  if (n < 0) return 0;
+  if (n == 0) return 1;
+  return fib(n - 1) + fib(n - 2);
+}
+
+testeeDo() {
+  print("Testee doing something.");
+  fib(30);
+  print("Testee did something.");
+}
+
+Future checkTimeline(VM vm) async {
+  var result = await TimelineRepositoryBase().getCpuProfileTimeline(vm);
+  var isString = isA<String>();
+  var isInt = isA<int>();
+  Map frames = result['stackFrames'];
+  expect(frames.length, greaterThan(10), reason: "Should have many samples");
+  for (Map frame in frames.values) {
+    expect(frame['category'], isString);
+    expect(frame['name'], isString);
+    if (frame['resolvedUrl'] != null) {
+      expect(frame['resolvedUrl'], isString);
+    }
+    if (frame['parent'] != null) {
+      expect(frames.containsKey(frame['parent']), isTrue);
+    }
+  }
+
+  List events = result['traceEvents'];
+  expect(events.length, greaterThan(10), reason: "Should have many samples");
+  for (Map event in events) {
+    expect(event['ph'], equals('P'));
+    expect(event['pid'], isInt);
+    expect(event['tid'], isInt);
+    expect(event['ts'], isInt);
+    expect(event['cat'], equals("Dart"));
+    expect(frames.containsKey(event['sf']), isTrue);
+  }
+}
+
+var tests = <VMTest>[
+  (VM vm) => checkTimeline(vm),
+];
+
+var vmArgs = [
+  '--profiler=true',
+  '--profile-vm=false', // So this also works with KBC.
+];
+
+main(args) async =>
+    runVMTests(args, tests, testeeBefore: testeeDo, extraArgs: vmArgs);
diff --git a/runtime/observatory_2/tests/service_2/get_cpu_samples_rpc_test.dart b/runtime/observatory_2/tests/service_2/get_cpu_samples_rpc_test.dart
new file mode 100644
index 0000000..d121b2b
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/get_cpu_samples_rpc_test.dart
@@ -0,0 +1,62 @@
+// Copyright (c) 2019, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+
+import 'test_helper.dart';
+
+fib(n) {
+  if (n < 0) return 0;
+  if (n == 0) return 1;
+  return fib(n - 1) + fib(n - 2);
+}
+
+testeeDo() {
+  print("Testee doing something.");
+  fib(30);
+  print("Testee did something.");
+}
+
+Future checkSamples(Isolate isolate) async {
+  final result =
+      await isolate.invokeRpcNoUpgrade('getCpuSamples', {'_code': true});
+  expect(result['type'], equals('CpuSamples'));
+
+  final isString = isA<String>();
+  final isInt = isA<int>();
+  final isList = isA<List>();
+  final functions = result['functions'];
+  expect(functions.length, greaterThan(10),
+      reason: "Should have many functions");
+  final codes = result['_codes'];
+  expect(functions.length, greaterThan(10),
+      reason: "Should have many code objects");
+
+  final samples = result['samples'];
+  expect(samples.length, greaterThan(10), reason: "Should have many samples");
+  final sample = samples.first;
+  expect(sample['tid'], isInt);
+  expect(sample['timestamp'], isInt);
+  if (sample.containsKey('vmTag')) {
+    expect(sample['vmTag'], isString);
+  }
+  if (sample.containsKey('userTag')) {
+    expect(sample['userTag'], isString);
+  }
+  expect(sample['stack'], isList);
+  expect(sample['_codeStack'], isList);
+}
+
+var tests = <IsolateTest>[
+  (Isolate i) => checkSamples(i),
+];
+
+var vmArgs = [
+  '--profiler=true',
+  '--profile-vm=false', // So this also works with KBC.
+];
+
+main(args) async =>
+    runIsolateTests(args, tests, testeeBefore: testeeDo, extraArgs: vmArgs);
diff --git a/runtime/observatory_2/tests/service_2/get_flag_list_rpc_test.dart b/runtime/observatory_2/tests/service_2/get_flag_list_rpc_test.dart
new file mode 100644
index 0000000..b70e4e4
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/get_flag_list_rpc_test.dart
@@ -0,0 +1,140 @@
+// Copyright (c) 2015, 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.
+
+import 'dart:async';
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+
+import 'test_helper.dart';
+
+Future getFlagValue(VM vm, String flagName) async {
+  var result = await vm.invokeRpcNoUpgrade('getFlagList', {});
+  expect(result['type'], equals('FlagList'));
+  final flags = result['flags'];
+  for (final flag in flags) {
+    if (flag['name'] == flagName) {
+      return flag['valueAsString'];
+    }
+  }
+}
+
+var tests = <VMTest>[
+  (VM vm) async {
+    var result = await vm.invokeRpcNoUpgrade('getFlagList', {});
+    expect(result['type'], equals('FlagList'));
+    // TODO(turnidge): Make this test a bit beefier.
+  },
+
+  // Modify a flag which does not exist.
+  (VM vm) async {
+    var params = {
+      'name': 'does_not_really_exist',
+      'value': 'true',
+    };
+    var result = await vm.invokeRpcNoUpgrade('setFlag', params);
+    expect(result['type'], equals('Error'));
+    expect(result['message'], equals('Cannot set flag: flag not found'));
+  },
+
+  // Modify a flag with the wrong value type.
+  (VM vm) async {
+    var params = {
+      'name': 'pause_isolates_on_start',
+      'value': 'not-a-boolean',
+    };
+    var result = await vm.invokeRpcNoUpgrade('setFlag', params);
+    expect(result['type'], equals('Error'));
+    expect(result['message'], equals('Cannot set flag: invalid value'));
+  },
+
+  // Modify a flag with the right value type.
+  (VM vm) async {
+    var params = {
+      'name': 'pause_isolates_on_start',
+      'value': 'false',
+    };
+    var result = await vm.invokeRpcNoUpgrade('setFlag', params);
+    expect(result['type'], equals('Success'));
+  },
+
+  // Modify a flag which cannot be set at runtime.
+  (VM vm) async {
+    var params = {
+      'name': 'random_seed',
+      'value': '42',
+    };
+    var result = await vm.invokeRpcNoUpgrade('setFlag', params);
+    expect(result['type'], equals('Error'));
+    expect(
+        result['message'], equals('Cannot set flag: cannot change at runtime'));
+  },
+
+  // Modify the profile_period at runtime.
+  (VM vm) async {
+    final kProfilePeriod = 'profile_period';
+    final kValue = 100;
+    expect(await getFlagValue(vm, kProfilePeriod), '1000');
+    final params = {
+      'name': '$kProfilePeriod',
+      'value': '$kValue',
+    };
+    final completer = Completer();
+    final stream = await vm.getEventStream(VM.kVMStream);
+    var subscription;
+    subscription = stream.listen((ServiceEvent event) {
+      if (event.kind == ServiceEvent.kVMFlagUpdate) {
+        expect(event.owner.type, 'VM');
+        expect(event.flag, kProfilePeriod);
+        expect(event.newValue, kValue.toString());
+        subscription.cancel();
+        completer.complete();
+      }
+    });
+    final result = await vm.invokeRpcNoUpgrade('setFlag', params);
+    expect(result['type'], equals('Success'));
+    await completer.future;
+    expect(await getFlagValue(vm, kProfilePeriod), kValue.toString());
+  },
+
+  // Start and stop the profiler at runtime.
+  (VM vm) async {
+    final kProfiler = 'profiler';
+    if (await getFlagValue(vm, kProfiler) == 'false') {
+      // Either in release or product modes and the profiler is disabled.
+      return;
+    }
+    final params = {
+      'name': kProfiler,
+      'value': 'false',
+    };
+
+    var result = await vm.invokeRpcNoUpgrade('setFlag', params);
+    expect(result['type'], equals('Success'));
+    expect(await getFlagValue(vm, kProfiler), 'false');
+    try {
+      // Arbitrary RPC which checks whether or not the profiler is enabled.
+      await vm.isolates.first.invokeRpcNoUpgrade('getCpuSamples', {});
+      fail('Profiler is disabled and request should fail');
+    } on ServerRpcException catch (_) {/* Expected */}
+
+    // Clear CPU samples.
+    result = await vm.isolates.first.invokeRpcNoUpgrade('clearCpuSamples', {});
+    expect(result['type'], equals('Success'));
+
+    params['value'] = 'true';
+    result = await vm.invokeRpcNoUpgrade('setFlag', params);
+    expect(result['type'], equals('Success'));
+    expect(await getFlagValue(vm, kProfiler), 'true');
+
+    try {
+      // Arbitrary RPC which checks whether or not the profiler is enabled.
+      result = await vm.isolates.first.invokeRpcNoUpgrade('getCpuSamples', {});
+    } on ServerRpcException catch (e) {
+      fail('Profiler is enabled and request should succeed. Error:\n$e');
+    }
+  },
+];
+
+main(args) async => runVMTests(args, tests);
diff --git a/runtime/observatory_2/tests/service_2/get_heap_map_rpc_test.dart b/runtime/observatory_2/tests/service_2/get_heap_map_rpc_test.dart
new file mode 100644
index 0000000..ffb754b
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/get_heap_map_rpc_test.dart
@@ -0,0 +1,65 @@
+// Copyright (c) 2015, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+
+import 'test_helper.dart';
+
+var tests = <IsolateTest>[
+  (Isolate isolate) async {
+    var params = {};
+    var result = await isolate.invokeRpcNoUpgrade('_getHeapMap', params);
+    expect(result['type'], equals('HeapMap'));
+    expect(result['freeClassId'], isPositive);
+    expect(result['unitSizeBytes'], isPositive);
+    expect(result['pageSizeBytes'], isPositive);
+    expect(result['classList'], isNotNull);
+    expect(result['pages'].length, isPositive);
+    expect(result['pages'][0]['objectStart'], isA<String>());
+    expect(result['pages'][0]['objects'].length, isPositive);
+    expect(result['pages'][0]['objects'][0], isPositive);
+  },
+  (Isolate isolate) async {
+    var params = {'gc': 'scavenge'};
+    var result = await isolate.invokeRpcNoUpgrade('_getHeapMap', params);
+    expect(result['type'], equals('HeapMap'));
+    expect(result['freeClassId'], isPositive);
+    expect(result['unitSizeBytes'], isPositive);
+    expect(result['pageSizeBytes'], isPositive);
+    expect(result['classList'], isNotNull);
+    expect(result['pages'].length, isPositive);
+    expect(result['pages'][0]['objectStart'], isA<String>());
+    expect(result['pages'][0]['objects'].length, isPositive);
+    expect(result['pages'][0]['objects'][0], isPositive);
+  },
+  (Isolate isolate) async {
+    var params = {'gc': 'mark-sweep'};
+    var result = await isolate.invokeRpcNoUpgrade('_getHeapMap', params);
+    expect(result['type'], equals('HeapMap'));
+    expect(result['freeClassId'], isPositive);
+    expect(result['unitSizeBytes'], isPositive);
+    expect(result['pageSizeBytes'], isPositive);
+    expect(result['classList'], isNotNull);
+    expect(result['pages'].length, isPositive);
+    expect(result['pages'][0]['objectStart'], isA<String>());
+    expect(result['pages'][0]['objects'].length, isPositive);
+    expect(result['pages'][0]['objects'][0], isPositive);
+  },
+  (Isolate isolate) async {
+    var params = {'gc': 'mark-compact'};
+    var result = await isolate.invokeRpcNoUpgrade('_getHeapMap', params);
+    expect(result['type'], equals('HeapMap'));
+    expect(result['freeClassId'], isPositive);
+    expect(result['unitSizeBytes'], isPositive);
+    expect(result['pageSizeBytes'], isPositive);
+    expect(result['classList'], isNotNull);
+    expect(result['pages'].length, isPositive);
+    expect(result['pages'][0]['objectStart'], isA<String>());
+    expect(result['pages'][0]['objects'].length, isPositive);
+    expect(result['pages'][0]['objects'][0], isPositive);
+  },
+];
+
+main(args) async => runIsolateTests(args, tests);
diff --git a/runtime/observatory_2/tests/service_2/get_instances_rpc_test.dart b/runtime/observatory_2/tests/service_2/get_instances_rpc_test.dart
new file mode 100644
index 0000000..7a6047d
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/get_instances_rpc_test.dart
@@ -0,0 +1,76 @@
+// Copyright (c) 2015, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+
+import 'test_helper.dart';
+
+class _TestClass {
+  _TestClass(this.x, this.y);
+  // Make sure these fields are not removed by the tree shaker.
+  @pragma("vm:entry-point")
+  var x;
+  @pragma("vm:entry-point")
+  var y;
+}
+
+var global;
+
+void warmup() {
+  global = new _TestClass(new _TestClass(1, 2), null);
+}
+
+@pragma("vm:entry-point")
+getGlobal() => global;
+
+invoke(Isolate isolate, String selector) async {
+  Map params = {
+    'targetId': isolate.rootLibrary.id,
+    'selector': selector,
+    'argumentIds': <String>[],
+  };
+  return await isolate.invokeRpcNoUpgrade('invoke', params);
+}
+
+var tests = <IsolateTest>[
+  (Isolate isolate) async {
+    var obj = await invoke(isolate, 'getGlobal');
+    var params = {
+      'objectId': obj['class']['id'],
+      'limit': 4,
+    };
+    var result = await isolate.invokeRpcNoUpgrade('getInstances', params);
+    expect(result['type'], equals('InstanceSet'));
+    expect(result['totalCount'], equals(2));
+    expect(result['instances'].length, equals(2));
+    expect(result['instances'][0]['type'], equals('@Instance'));
+
+    // Limit is respected.
+    params = {
+      'objectId': obj['class']['id'],
+      'limit': 1,
+    };
+    result = await isolate.invokeRpcNoUpgrade('getInstances', params);
+    expect(result['type'], equals('InstanceSet'));
+    expect(result['totalCount'], equals(2));
+    expect(result['instances'].length, equals(1));
+    expect(result['instances'][0]['type'], equals('@Instance'));
+
+    // Try an object ID that isn't a class ID
+    params = {
+      'objectId': isolate.rootLibrary.id,
+      'limit': 1,
+    };
+    try {
+      await isolate.invokeRpcNoUpgrade('getInstances', params);
+    } on ServerRpcException catch (_) {
+      // Success.
+    } catch (e) {
+      fail('Failed with exception: $e');
+    }
+  },
+];
+
+main(args) async => runIsolateTests(args, tests, testeeBefore: warmup);
diff --git a/runtime/observatory_2/tests/service_2/get_isolate_after_async_error_test.dart b/runtime/observatory_2/tests/service_2/get_isolate_after_async_error_test.dart
new file mode 100644
index 0000000..eb79fdf
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/get_isolate_after_async_error_test.dart
@@ -0,0 +1,24 @@
+// Copyright (c) 2016, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+doThrow() async {
+  throw "oh no"; // Line 13.
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtExit,
+  (Isolate isolate) async {
+    await isolate.reload();
+    expect(isolate.error, isNotNull);
+    expect(isolate.error.message.contains('oh no'), isTrue);
+  }
+];
+
+main(args) async => runIsolateTests(args, tests,
+    pause_on_exit: true, testeeConcurrent: doThrow);
diff --git a/runtime/observatory_2/tests/service_2/get_isolate_after_language_error_test.dart b/runtime/observatory_2/tests/service_2/get_isolate_after_language_error_test.dart
new file mode 100644
index 0000000..88a7f89
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/get_isolate_after_language_error_test.dart
@@ -0,0 +1,30 @@
+// Copyright (c) 2016, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+var x;
+
+doThrow() {
+  if (x) while {
+  };
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtExit,
+
+  (Isolate isolate) async {
+    await isolate.reload();
+    expect(isolate.error, isNotNull);
+    expect(isolate.error.message.contains("'(' expected"), isTrue);
+  }
+];
+
+main(args) => runIsolateTestsSynchronous(args,
+                                         tests,
+                                         pause_on_exit: true,
+                                         testeeConcurrent: doThrow);
diff --git a/runtime/observatory_2/tests/service_2/get_isolate_after_stack_overflow_error_test.dart b/runtime/observatory_2/tests/service_2/get_isolate_after_stack_overflow_error_test.dart
new file mode 100644
index 0000000..6b76c6b
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/get_isolate_after_stack_overflow_error_test.dart
@@ -0,0 +1,29 @@
+// Copyright (c) 2016, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+// Non tailable recursive function that should trigger a Stack Overflow.
+num factorialGrowth([num n = 1]) {
+  return factorialGrowth(n + 1) * n;
+}
+
+void nonTailableRecursion() {
+  factorialGrowth();
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtExit,
+  (Isolate isolate) async {
+    await isolate.reload();
+    expect(isolate.error, isNotNull);
+    expect(isolate.error.message.contains('Stack Overflow'), isTrue);
+  }
+];
+
+main(args) async => runIsolateTests(args, tests,
+    pause_on_exit: true, testeeConcurrent: nonTailableRecursion);
diff --git a/runtime/observatory_2/tests/service_2/get_isolate_after_sync_error_test.dart b/runtime/observatory_2/tests/service_2/get_isolate_after_sync_error_test.dart
new file mode 100644
index 0000000..fe72721
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/get_isolate_after_sync_error_test.dart
@@ -0,0 +1,24 @@
+// Copyright (c) 2016, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+doThrow() {
+  throw "oh no"; // Line 13.
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtExit,
+  (Isolate isolate) async {
+    await isolate.reload();
+    expect(isolate.error, isNotNull);
+    expect(isolate.error.message.contains('oh no'), isTrue);
+  }
+];
+
+main(args) => runIsolateTestsSynchronous(args, tests,
+    pause_on_exit: true, testeeConcurrent: doThrow);
diff --git a/runtime/observatory_2/tests/service_2/get_isolate_group_memory_usage.dart b/runtime/observatory_2/tests/service_2/get_isolate_group_memory_usage.dart
new file mode 100644
index 0000000..b5559ba
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/get_isolate_group_memory_usage.dart
@@ -0,0 +1,64 @@
+// Copyright (c) 2019, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+
+import 'test_helper.dart';
+
+var tests = <VMTest>[
+  (VM vm) async {
+    final params = {
+      'isolateGroupId': vm.isolateGroups.first.id,
+    };
+    final result =
+        await vm.invokeRpcNoUpgrade('getIsolateGroupMemoryUsage', params);
+    expect(result['type'], equals('MemoryUsage'));
+    expect(result['heapUsage'], isPositive);
+    expect(result['heapCapacity'], isPositive);
+    expect(result['externalUsage'], isPositive);
+  },
+  (VM vm) async {
+    final params = {
+      'isolateGroupId': 'badid',
+    };
+    bool caughtException;
+    try {
+      await vm.invokeRpcNoUpgrade('getIsolateGroupMemoryUsage', params);
+      expect(false, isTrue, reason: 'Unreachable');
+    } on ServerRpcException catch (e) {
+      caughtException = true;
+      expect(e.code, equals(ServerRpcException.kInvalidParams));
+      expect(e.message,
+          "getIsolateGroupMemoryUsage: invalid 'isolateGroupId' parameter: badid");
+    }
+    expect(caughtException, isTrue);
+  },
+
+  // Plausible isolate group id, not found.
+  (VM vm) async {
+    final params = {
+      'isolateGroupId': 'isolateGroups/9999999999',
+    };
+    final result =
+        await vm.invokeRpcNoUpgrade('getIsolateGroupMemoryUsage', params);
+    expect(result['type'], equals('Sentinel'));
+    expect(result['kind'], equals('Expired'));
+    expect(result['valueAsString'], equals('<expired>'));
+  },
+
+  // isolate id was passed instead of isolate group id.
+  (VM vm) async {
+    final params = {
+      'isolateId': 'isolates/9999999999',
+    };
+    final result =
+        await vm.invokeRpcNoUpgrade('getIsolateGroupMemoryUsage', params);
+    expect(result['type'], equals('Sentinel'));
+    expect(result['kind'], equals('Expired'));
+    expect(result['valueAsString'], equals('<expired>'));
+  },
+];
+
+main(args) async => runVMTests(args, tests);
diff --git a/runtime/observatory_2/tests/service_2/get_isolate_rpc_test.dart b/runtime/observatory_2/tests/service_2/get_isolate_rpc_test.dart
new file mode 100644
index 0000000..f09eb04
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/get_isolate_rpc_test.dart
@@ -0,0 +1,64 @@
+// Copyright (c) 2015, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+
+import 'test_helper.dart';
+
+var tests = <VMTest>[
+  (VM vm) async {
+    var params = {
+      'isolateId': vm.isolates.first.id,
+    };
+    var result = await vm.invokeRpcNoUpgrade('getIsolate', params);
+    expect(result['type'], equals('Isolate'));
+    expect(result['id'], startsWith('isolates/'));
+    expect(result['number'], isA<String>());
+    expect(result['isSystemIsolate'], isFalse);
+    expect(result['_originNumber'], equals(result['number']));
+    expect(result['startTime'], isPositive);
+    expect(result['livePorts'], isPositive);
+    expect(result['pauseOnExit'], isFalse);
+    expect(result['pauseEvent']['type'], equals('Event'));
+    expect(result['error'], isNull);
+    expect(result['_numZoneHandles'], isPositive);
+    expect(result['_numScopedHandles'], isPositive);
+    expect(result['rootLib']['type'], equals('@Library'));
+    expect(result['libraries'].length, isPositive);
+    expect(result['libraries'][0]['type'], equals('@Library'));
+    expect(result['breakpoints'].length, isZero);
+    expect(result['_heaps']['new']['type'], equals('HeapSpace'));
+    expect(result['_heaps']['old']['type'], equals('HeapSpace'));
+  },
+
+  (VM vm) async {
+    var params = {
+      'isolateId': 'badid',
+    };
+    bool caughtException;
+    try {
+      await vm.invokeRpcNoUpgrade('getIsolate', params);
+      expect(false, isTrue, reason: 'Unreachable');
+    } on ServerRpcException catch (e) {
+      caughtException = true;
+      expect(e.code, equals(ServerRpcException.kInvalidParams));
+      expect(e.message, "getIsolate: invalid 'isolateId' parameter: badid");
+    }
+    expect(caughtException, isTrue);
+  },
+
+  // Plausible isolate id, not found.
+  (VM vm) async {
+    var params = {
+      'isolateId': 'isolates/9999999999',
+    };
+    var result = await vm.invokeRpcNoUpgrade('getIsolate', params);
+    expect(result['type'], equals('Sentinel'));
+    expect(result['kind'], equals('Collected'));
+    expect(result['valueAsString'], equals('<collected>'));
+  },
+];
+
+main(args) async => runVMTests(args, tests);
diff --git a/runtime/observatory_2/tests/service_2/get_memory_usage.dart b/runtime/observatory_2/tests/service_2/get_memory_usage.dart
new file mode 100644
index 0000000..9a8eff4
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/get_memory_usage.dart
@@ -0,0 +1,49 @@
+// Copyright (c) 2019, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+
+import 'test_helper.dart';
+
+var tests = <VMTest>[
+  (VM vm) async {
+    var params = {
+      'isolateId': vm.isolates.first.id,
+    };
+    var result = await vm.invokeRpcNoUpgrade('getMemoryUsage', params);
+    expect(result['type'], equals('MemoryUsage'));
+    expect(result['heapUsage'], isPositive);
+    expect(result['heapCapacity'], isPositive);
+    expect(result['externalUsage'], isPositive);
+  },
+  (VM vm) async {
+    var params = {
+      'isolateId': 'badid',
+    };
+    bool caughtException;
+    try {
+      await vm.invokeRpcNoUpgrade('getMemoryUsage', params);
+      expect(false, isTrue, reason: 'Unreachable');
+    } on ServerRpcException catch (e) {
+      caughtException = true;
+      expect(e.code, equals(ServerRpcException.kInvalidParams));
+      expect(e.message, "getMemoryUsage: invalid 'isolateId' parameter: badid");
+    }
+    expect(caughtException, isTrue);
+  },
+
+  // Plausible isolate id, not found.
+  (VM vm) async {
+    var params = {
+      'isolateId': 'isolates/9999999999',
+    };
+    var result = await vm.invokeRpcNoUpgrade('getMemoryUsage', params);
+    expect(result['type'], equals('Sentinel'));
+    expect(result['kind'], equals('Collected'));
+    expect(result['valueAsString'], equals('<collected>'));
+  },
+];
+
+main(args) async => runVMTests(args, tests);
diff --git a/runtime/observatory_2/tests/service_2/get_native_allocation_samples_test.dart b/runtime/observatory_2/tests/service_2/get_native_allocation_samples_test.dart
new file mode 100644
index 0000000..ad7d7d5
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/get_native_allocation_samples_test.dart
@@ -0,0 +1,84 @@
+// Copyright (c) 2017, 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.
+
+// VMOptions=--profiler_native_memory
+
+import 'package:observatory_2/sample_profile.dart';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+
+void verifyHelper(var root, bool exclusive) {
+  if (root.children.length == 0) {
+    return;
+  }
+
+  if (!(root is FunctionCallTreeNode)) {
+    print('${root.profileCode.code.name}');
+  } else {
+    print('${root.profileFunction.function.name}');
+  }
+
+  int inclusiveAllocations = 0;
+  int exclusiveAllocations = 0;
+
+  for (int i = 0; i < root.children.length; i++) {
+    inclusiveAllocations += root.children[i].inclusiveNativeAllocations;
+    exclusiveAllocations += root.children[i].exclusiveNativeAllocations;
+  }
+
+  int rootMemory;
+  if (exclusive) {
+    rootMemory = root.inclusiveNativeAllocations + exclusiveAllocations;
+  } else {
+    rootMemory =
+        root.inclusiveNativeAllocations - root.exclusiveNativeAllocations;
+  }
+
+  expect(inclusiveAllocations == rootMemory, isTrue);
+  for (int i = 0; i < root.children.length; i++) {
+    verifyHelper(root.children[i], exclusive);
+  }
+}
+
+void verify(var tree, bool exclusive) {
+  var node = tree.root;
+  expect(node, isNotNull);
+  expect(node.children.length >= 0, isTrue);
+
+  for (int i = 0; i < node.children.length; i++) {
+    verifyHelper(node.children[i], exclusive);
+  }
+}
+
+var tests = <VMTest>[
+  // Verify inclusive tries.
+  (VM vm) async {
+    var response =
+        await vm.invokeRpc('_getNativeAllocationSamples', {'_code': true});
+    SampleProfile cpuProfile = new SampleProfile();
+    await cpuProfile.load(vm, response);
+    var codeTree = cpuProfile.loadCodeTree(M.ProfileTreeDirection.inclusive);
+    var functionTree =
+        cpuProfile.loadFunctionTree(M.ProfileTreeDirection.inclusive);
+    verify(codeTree, false);
+    verify(functionTree, false);
+  },
+  // Verify exclusive tries.
+  (VM vm) async {
+    var response =
+        await vm.invokeRpc('_getNativeAllocationSamples', {'_code': true});
+    SampleProfile cpuProfile = new SampleProfile();
+    await cpuProfile.load(vm, response);
+    var codeTreeExclusive =
+        cpuProfile.loadCodeTree(M.ProfileTreeDirection.exclusive);
+    var functionTreeExclusive =
+        cpuProfile.loadFunctionTree(M.ProfileTreeDirection.exclusive);
+    verify(codeTreeExclusive, true);
+    verify(functionTreeExclusive, true);
+  }
+];
+
+main(args) async => runVMTests(args, tests);
diff --git a/runtime/observatory_2/tests/service_2/get_object_rpc_test.dart b/runtime/observatory_2/tests/service_2/get_object_rpc_test.dart
new file mode 100644
index 0000000..36b9c61
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/get_object_rpc_test.dart
@@ -0,0 +1,1006 @@
+// Copyright (c) 2015, 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.
+
+library get_object_rpc_test;
+
+import 'dart:typed_data';
+import 'dart:convert' show base64Decode;
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+class _DummyClass {
+  static var dummyVar = 11;
+  final List<String> dummyList = new List<String>.filled(20, null);
+  void dummyFunction() {}
+}
+
+class _DummySubClass extends _DummyClass {}
+
+void warmup() {
+  // Silence analyzer.
+  new _DummySubClass();
+  new _DummyClass().dummyFunction();
+}
+
+@pragma("vm:entry-point")
+getChattanooga() => "Chattanooga";
+
+@pragma("vm:entry-point")
+getList() => [3, 2, 1];
+
+@pragma("vm:entry-point")
+getMap() => {"x": 3, "y": 4, "z": 5};
+
+@pragma("vm:entry-point")
+getUint8List() => uint8List;
+
+@pragma("vm:entry-point")
+getUint64List() => uint64List;
+
+@pragma("vm:entry-point")
+getDummyClass() => new _DummyClass();
+
+invoke(Isolate isolate, String selector) async {
+  Map params = {
+    'targetId': isolate.rootLibrary.id,
+    'selector': selector,
+    'argumentIds': <String>[],
+  };
+  return await isolate.invokeRpcNoUpgrade('invoke', params);
+}
+
+var uint8List = new Uint8List.fromList([3, 2, 1]);
+var uint64List = new Uint64List.fromList([3, 2, 1]);
+
+var tests = <IsolateTest>[
+  // null object.
+  (Isolate isolate) async {
+    var params = {
+      'objectId': 'objects/null',
+    };
+    var result = await isolate.invokeRpcNoUpgrade('getObject', params);
+    expect(result['type'], equals('Instance'));
+    expect(result['kind'], equals('Null'));
+    expect(result['id'], equals('objects/null'));
+    expect(result['valueAsString'], equals('null'));
+    expect(result['class']['type'], equals('@Class'));
+    expect(result['class']['name'], equals('Null'));
+    expect(result['size'], isPositive);
+  },
+
+  // bool object.
+  (Isolate isolate) async {
+    var params = {
+      'objectId': 'objects/bool-true',
+    };
+    var result = await isolate.invokeRpcNoUpgrade('getObject', params);
+    expect(result['type'], equals('Instance'));
+    expect(result['kind'], equals('Bool'));
+    expect(result['id'], equals('objects/bool-true'));
+    expect(result['valueAsString'], equals('true'));
+    expect(result['class']['type'], equals('@Class'));
+    expect(result['class']['name'], equals('bool'));
+    expect(result['size'], isPositive);
+    expect(result['fields'], isEmpty);
+  },
+
+  // int object.
+  (Isolate isolate) async {
+    var params = {
+      'objectId': 'objects/int-123',
+    };
+    var result = await isolate.invokeRpcNoUpgrade('getObject', params);
+    expect(result['type'], equals('Instance'));
+    expect(result['kind'], equals('Int'));
+    expect(result['_vmType'], equals('Smi'));
+    expect(result['id'], equals('objects/int-123'));
+    expect(result['valueAsString'], equals('123'));
+    expect(result['class']['type'], equals('@Class'));
+    expect(result['class']['name'], equals('_Smi'));
+    expect(result['size'], isZero);
+    expect(result['fields'], isEmpty);
+  },
+
+  // A string
+  (Isolate isolate) async {
+    // Call eval to get a Dart list.
+    var evalResult = await invoke(isolate, 'getChattanooga');
+    var params = {
+      'objectId': evalResult['id'],
+    };
+    var result = await isolate.invokeRpcNoUpgrade('getObject', params);
+    expect(result['type'], equals('Instance'));
+    expect(result['kind'], equals('String'));
+    expect(result['_vmType'], equals('String'));
+    expect(result['id'], startsWith('objects/'));
+    expect(result['valueAsString'], equals('Chattanooga'));
+    expect(result['class']['type'], equals('@Class'));
+    expect(result['class']['name'], equals('_OneByteString'));
+    expect(result['size'], isPositive);
+    expect(result['fields'], isEmpty);
+    expect(result['length'], equals(11));
+    expect(result['offset'], isNull);
+    expect(result['count'], isNull);
+  },
+
+  // String prefix.
+  (Isolate isolate) async {
+    // Call eval to get a Dart list.
+    var evalResult = await invoke(isolate, 'getChattanooga');
+    var params = {
+      'objectId': evalResult['id'],
+      'count': 4,
+    };
+    var result = await isolate.invokeRpcNoUpgrade('getObject', params);
+    expect(result['type'], equals('Instance'));
+    expect(result['kind'], equals('String'));
+    expect(result['_vmType'], equals('String'));
+    expect(result['id'], startsWith('objects/'));
+    expect(result['valueAsString'], equals('Chat'));
+    expect(result['class']['type'], equals('@Class'));
+    expect(result['class']['name'], equals('_OneByteString'));
+    expect(result['size'], isPositive);
+    expect(result['fields'], isEmpty);
+    expect(result['length'], equals(11));
+    expect(result['offset'], isNull);
+    expect(result['count'], equals(4));
+  },
+
+  // String subrange.
+  (Isolate isolate) async {
+    // Call eval to get a Dart list.
+    var evalResult = await invoke(isolate, 'getChattanooga');
+    var params = {
+      'objectId': evalResult['id'],
+      'offset': 4,
+      'count': 6,
+    };
+    var result = await isolate.invokeRpcNoUpgrade('getObject', params);
+    expect(result['type'], equals('Instance'));
+    expect(result['kind'], equals('String'));
+    expect(result['_vmType'], equals('String'));
+    expect(result['id'], startsWith('objects/'));
+    expect(result['valueAsString'], equals('tanoog'));
+    expect(result['class']['type'], equals('@Class'));
+    expect(result['class']['name'], equals('_OneByteString'));
+    expect(result['size'], isPositive);
+    expect(result['fields'], isEmpty);
+    expect(result['length'], equals(11));
+    expect(result['offset'], equals(4));
+    expect(result['count'], equals(6));
+  },
+
+  // String with wacky offset.
+  (Isolate isolate) async {
+    // Call eval to get a Dart list.
+    var evalResult = await invoke(isolate, 'getChattanooga');
+    var params = {
+      'objectId': evalResult['id'],
+      'offset': 100,
+      'count': 2,
+    };
+    var result = await isolate.invokeRpcNoUpgrade('getObject', params);
+    expect(result['type'], equals('Instance'));
+    expect(result['kind'], equals('String'));
+    expect(result['_vmType'], equals('String'));
+    expect(result['id'], startsWith('objects/'));
+    expect(result['valueAsString'], equals(''));
+    expect(result['class']['type'], equals('@Class'));
+    expect(result['class']['name'], equals('_OneByteString'));
+    expect(result['size'], isPositive);
+    expect(result['fields'], isEmpty);
+    expect(result['length'], equals(11));
+    expect(result['offset'], equals(11));
+    expect(result['count'], equals(0));
+  },
+
+  // A built-in List.
+  (Isolate isolate) async {
+    // Call eval to get a Dart list.
+    var evalResult = await invoke(isolate, 'getList');
+    var params = {
+      'objectId': evalResult['id'],
+    };
+    var result = await isolate.invokeRpcNoUpgrade('getObject', params);
+    expect(result['type'], equals('Instance'));
+    expect(result['kind'], equals('List'));
+    expect(result['_vmType'], equals('GrowableObjectArray'));
+    expect(result['id'], startsWith('objects/'));
+    expect(result['valueAsString'], isNull);
+    expect(result['class']['type'], equals('@Class'));
+    expect(result['class']['name'], equals('_GrowableList'));
+    expect(result['size'], isPositive);
+    expect(result['fields'], isEmpty);
+    expect(result['length'], equals(3));
+    expect(result['offset'], isNull);
+    expect(result['count'], isNull);
+    expect(result['elements'].length, equals(3));
+    expect(result['elements'][0]['type'], equals('@Instance'));
+    expect(result['elements'][0]['kind'], equals('Int'));
+    expect(result['elements'][0]['valueAsString'], equals('3'));
+    expect(result['elements'][1]['type'], equals('@Instance'));
+    expect(result['elements'][1]['kind'], equals('Int'));
+    expect(result['elements'][1]['valueAsString'], equals('2'));
+    expect(result['elements'][2]['type'], equals('@Instance'));
+    expect(result['elements'][2]['kind'], equals('Int'));
+    expect(result['elements'][2]['valueAsString'], equals('1'));
+  },
+
+  // List prefix.
+  (Isolate isolate) async {
+    // Call eval to get a Dart list.
+    var evalResult = await invoke(isolate, 'getList');
+    var params = {
+      'objectId': evalResult['id'],
+      'count': 2,
+    };
+    var result = await isolate.invokeRpcNoUpgrade('getObject', params);
+    expect(result['type'], equals('Instance'));
+    expect(result['kind'], equals('List'));
+    expect(result['_vmType'], equals('GrowableObjectArray'));
+    expect(result['id'], startsWith('objects/'));
+    expect(result['valueAsString'], isNull);
+    expect(result['class']['type'], equals('@Class'));
+    expect(result['class']['name'], equals('_GrowableList'));
+    expect(result['size'], isPositive);
+    expect(result['fields'], isEmpty);
+    expect(result['length'], equals(3));
+    expect(result['offset'], isNull);
+    expect(result['count'], equals(2));
+    expect(result['elements'].length, equals(2));
+    expect(result['elements'][0]['type'], equals('@Instance'));
+    expect(result['elements'][0]['kind'], equals('Int'));
+    expect(result['elements'][0]['valueAsString'], equals('3'));
+    expect(result['elements'][1]['type'], equals('@Instance'));
+    expect(result['elements'][1]['kind'], equals('Int'));
+    expect(result['elements'][1]['valueAsString'], equals('2'));
+  },
+
+  // List suffix.
+  (Isolate isolate) async {
+    // Call eval to get a Dart list.
+    var evalResult = await invoke(isolate, 'getList');
+    var params = {
+      'objectId': evalResult['id'],
+      'offset': 2,
+      'count': 2,
+    };
+    var result = await isolate.invokeRpcNoUpgrade('getObject', params);
+    expect(result['type'], equals('Instance'));
+    expect(result['kind'], equals('List'));
+    expect(result['_vmType'], equals('GrowableObjectArray'));
+    expect(result['id'], startsWith('objects/'));
+    expect(result['valueAsString'], isNull);
+    expect(result['class']['type'], equals('@Class'));
+    expect(result['class']['name'], equals('_GrowableList'));
+    expect(result['size'], isPositive);
+    expect(result['fields'], isEmpty);
+    expect(result['length'], equals(3));
+    expect(result['offset'], equals(2));
+    expect(result['count'], equals(1));
+    expect(result['elements'].length, equals(1));
+    expect(result['elements'][0]['type'], equals('@Instance'));
+    expect(result['elements'][0]['kind'], equals('Int'));
+    expect(result['elements'][0]['valueAsString'], equals('1'));
+  },
+
+  // List with wacky offset.
+  (Isolate isolate) async {
+    // Call eval to get a Dart list.
+    var evalResult = await invoke(isolate, 'getList');
+    var params = {
+      'objectId': evalResult['id'],
+      'offset': 100,
+      'count': 2,
+    };
+    var result = await isolate.invokeRpcNoUpgrade('getObject', params);
+    expect(result['type'], equals('Instance'));
+    expect(result['kind'], equals('List'));
+    expect(result['_vmType'], equals('GrowableObjectArray'));
+    expect(result['id'], startsWith('objects/'));
+    expect(result['valueAsString'], isNull);
+    expect(result['class']['type'], equals('@Class'));
+    expect(result['class']['name'], equals('_GrowableList'));
+    expect(result['size'], isPositive);
+    expect(result['fields'], isEmpty);
+    expect(result['length'], equals(3));
+    expect(result['offset'], equals(3));
+    expect(result['count'], equals(0));
+    expect(result['elements'], isEmpty);
+  },
+
+  // A built-in Map.
+  (Isolate isolate) async {
+    // Call eval to get a Dart map.
+    var evalResult = await invoke(isolate, 'getMap');
+    var params = {
+      'objectId': evalResult['id'],
+    };
+    var result = await isolate.invokeRpcNoUpgrade('getObject', params);
+    expect(result['type'], equals('Instance'));
+    expect(result['kind'], equals('Map'));
+    expect(result['_vmType'], equals('LinkedHashMap'));
+    expect(result['id'], startsWith('objects/'));
+    expect(result['valueAsString'], isNull);
+    expect(result['class']['type'], equals('@Class'));
+    expect(result['class']['name'], equals('_InternalLinkedHashMap'));
+    expect(result['size'], isPositive);
+    expect(result['fields'], isEmpty);
+    expect(result['length'], equals(3));
+    expect(result['offset'], isNull);
+    expect(result['count'], isNull);
+    expect(result['associations'].length, equals(3));
+    expect(result['associations'][0]['key']['type'], equals('@Instance'));
+    expect(result['associations'][0]['key']['kind'], equals('String'));
+    expect(result['associations'][0]['key']['valueAsString'], equals('x'));
+    expect(result['associations'][0]['value']['type'], equals('@Instance'));
+    expect(result['associations'][0]['value']['kind'], equals('Int'));
+    expect(result['associations'][0]['value']['valueAsString'], equals('3'));
+    expect(result['associations'][1]['key']['type'], equals('@Instance'));
+    expect(result['associations'][1]['key']['kind'], equals('String'));
+    expect(result['associations'][1]['key']['valueAsString'], equals('y'));
+    expect(result['associations'][1]['value']['type'], equals('@Instance'));
+    expect(result['associations'][1]['value']['kind'], equals('Int'));
+    expect(result['associations'][1]['value']['valueAsString'], equals('4'));
+    expect(result['associations'][2]['key']['type'], equals('@Instance'));
+    expect(result['associations'][2]['key']['kind'], equals('String'));
+    expect(result['associations'][2]['key']['valueAsString'], equals('z'));
+    expect(result['associations'][2]['value']['type'], equals('@Instance'));
+    expect(result['associations'][2]['value']['kind'], equals('Int'));
+    expect(result['associations'][2]['value']['valueAsString'], equals('5'));
+  },
+
+  // Map prefix.
+  (Isolate isolate) async {
+    // Call eval to get a Dart map.
+    var evalResult = await invoke(isolate, 'getMap');
+    var params = {
+      'objectId': evalResult['id'],
+      'count': 2,
+    };
+    var result = await isolate.invokeRpcNoUpgrade('getObject', params);
+    expect(result['type'], equals('Instance'));
+    expect(result['kind'], equals('Map'));
+    expect(result['_vmType'], equals('LinkedHashMap'));
+    expect(result['id'], startsWith('objects/'));
+    expect(result['valueAsString'], isNull);
+    expect(result['class']['type'], equals('@Class'));
+    expect(result['class']['name'], equals('_InternalLinkedHashMap'));
+    expect(result['size'], isPositive);
+    expect(result['fields'], isEmpty);
+    expect(result['length'], equals(3));
+    expect(result['offset'], isNull);
+    expect(result['count'], equals(2));
+    expect(result['associations'].length, equals(2));
+    expect(result['associations'][0]['key']['type'], equals('@Instance'));
+    expect(result['associations'][0]['key']['kind'], equals('String'));
+    expect(result['associations'][0]['key']['valueAsString'], equals('x'));
+    expect(result['associations'][0]['value']['type'], equals('@Instance'));
+    expect(result['associations'][0]['value']['kind'], equals('Int'));
+    expect(result['associations'][0]['value']['valueAsString'], equals('3'));
+    expect(result['associations'][1]['key']['type'], equals('@Instance'));
+    expect(result['associations'][1]['key']['kind'], equals('String'));
+    expect(result['associations'][1]['key']['valueAsString'], equals('y'));
+    expect(result['associations'][1]['value']['type'], equals('@Instance'));
+    expect(result['associations'][1]['value']['kind'], equals('Int'));
+    expect(result['associations'][1]['value']['valueAsString'], equals('4'));
+  },
+
+  // Map suffix.
+  (Isolate isolate) async {
+    // Call eval to get a Dart map.
+    var evalResult = await invoke(isolate, 'getMap');
+    var params = {
+      'objectId': evalResult['id'],
+      'offset': 2,
+      'count': 2,
+    };
+    var result = await isolate.invokeRpcNoUpgrade('getObject', params);
+    expect(result['type'], equals('Instance'));
+    expect(result['kind'], equals('Map'));
+    expect(result['_vmType'], equals('LinkedHashMap'));
+    expect(result['id'], startsWith('objects/'));
+    expect(result['valueAsString'], isNull);
+    expect(result['class']['type'], equals('@Class'));
+    expect(result['class']['name'], equals('_InternalLinkedHashMap'));
+    expect(result['size'], isPositive);
+    expect(result['fields'], isEmpty);
+    expect(result['length'], equals(3));
+    expect(result['offset'], equals(2));
+    expect(result['count'], equals(1));
+    expect(result['associations'].length, equals(1));
+    expect(result['associations'][0]['key']['type'], equals('@Instance'));
+    expect(result['associations'][0]['key']['kind'], equals('String'));
+    expect(result['associations'][0]['key']['valueAsString'], equals('z'));
+    expect(result['associations'][0]['value']['type'], equals('@Instance'));
+    expect(result['associations'][0]['value']['kind'], equals('Int'));
+    expect(result['associations'][0]['value']['valueAsString'], equals('5'));
+  },
+
+  // Map with wacky offset
+  (Isolate isolate) async {
+    // Call eval to get a Dart map.
+    var evalResult = await invoke(isolate, 'getMap');
+    var params = {
+      'objectId': evalResult['id'],
+      'offset': 100,
+      'count': 2,
+    };
+    var result = await isolate.invokeRpcNoUpgrade('getObject', params);
+    expect(result['type'], equals('Instance'));
+    expect(result['kind'], equals('Map'));
+    expect(result['_vmType'], equals('LinkedHashMap'));
+    expect(result['id'], startsWith('objects/'));
+    expect(result['valueAsString'], isNull);
+    expect(result['class']['type'], equals('@Class'));
+    expect(result['class']['name'], equals('_InternalLinkedHashMap'));
+    expect(result['size'], isPositive);
+    expect(result['fields'], isEmpty);
+    expect(result['length'], equals(3));
+    expect(result['offset'], equals(3));
+    expect(result['count'], equals(0));
+    expect(result['associations'], isEmpty);
+  },
+
+  // Uint8List.
+  (Isolate isolate) async {
+    // Call eval to get a Dart list.
+    var evalResult = await invoke(isolate, 'getUint8List');
+    var params = {
+      'objectId': evalResult['id'],
+    };
+    var result = await isolate.invokeRpcNoUpgrade('getObject', params);
+    expect(result['type'], equals('Instance'));
+    expect(result['kind'], equals('Uint8List'));
+    expect(result['_vmType'], equals('TypedData'));
+    expect(result['id'], startsWith('objects/'));
+    expect(result['valueAsString'], isNull);
+    expect(result['class']['type'], equals('@Class'));
+    expect(result['class']['name'], equals('_Uint8List'));
+    expect(result['size'], isPositive);
+    expect(result['fields'], isEmpty);
+    expect(result['length'], equals(3));
+    expect(result['offset'], isNull);
+    expect(result['count'], isNull);
+    expect(result['bytes'], equals('AwIB'));
+    Uint8List bytes = base64Decode(result['bytes']);
+    expect(bytes.buffer.asUint8List().toString(), equals('[3, 2, 1]'));
+  },
+
+  // Uint8List prefix.
+  (Isolate isolate) async {
+    // Call eval to get a Dart list.
+    var evalResult = await invoke(isolate, 'getUint8List');
+    var params = {
+      'objectId': evalResult['id'],
+      'count': 2,
+    };
+    var result = await isolate.invokeRpcNoUpgrade('getObject', params);
+    expect(result['type'], equals('Instance'));
+    expect(result['kind'], equals('Uint8List'));
+    expect(result['_vmType'], equals('TypedData'));
+    expect(result['id'], startsWith('objects/'));
+    expect(result['valueAsString'], isNull);
+    expect(result['class']['type'], equals('@Class'));
+    expect(result['class']['name'], equals('_Uint8List'));
+    expect(result['size'], isPositive);
+    expect(result['fields'], isEmpty);
+    expect(result['length'], equals(3));
+    expect(result['offset'], isNull);
+    expect(result['count'], equals(2));
+    expect(result['bytes'], equals('AwI='));
+    Uint8List bytes = base64Decode(result['bytes']);
+    expect(bytes.buffer.asUint8List().toString(), equals('[3, 2]'));
+  },
+
+  // Uint8List suffix.
+  (Isolate isolate) async {
+    // Call eval to get a Dart list.
+    var evalResult = await invoke(isolate, 'getUint8List');
+    var params = {
+      'objectId': evalResult['id'],
+      'offset': 2,
+      'count': 2,
+    };
+    var result = await isolate.invokeRpcNoUpgrade('getObject', params);
+    expect(result['type'], equals('Instance'));
+    expect(result['kind'], equals('Uint8List'));
+    expect(result['_vmType'], equals('TypedData'));
+    expect(result['id'], startsWith('objects/'));
+    expect(result['valueAsString'], isNull);
+    expect(result['class']['type'], equals('@Class'));
+    expect(result['class']['name'], equals('_Uint8List'));
+    expect(result['size'], isPositive);
+    expect(result['fields'], isEmpty);
+    expect(result['length'], equals(3));
+    expect(result['offset'], equals(2));
+    expect(result['count'], equals(1));
+    expect(result['bytes'], equals('AQ=='));
+    Uint8List bytes = base64Decode(result['bytes']);
+    expect(bytes.buffer.asUint8List().toString(), equals('[1]'));
+  },
+
+  // Uint8List with wacky offset.
+  (Isolate isolate) async {
+    // Call eval to get a Dart list.
+    var evalResult = await invoke(isolate, 'getUint8List');
+    var params = {
+      'objectId': evalResult['id'],
+      'offset': 100,
+      'count': 2,
+    };
+    var result = await isolate.invokeRpcNoUpgrade('getObject', params);
+    expect(result['type'], equals('Instance'));
+    expect(result['kind'], equals('Uint8List'));
+    expect(result['_vmType'], equals('TypedData'));
+    expect(result['id'], startsWith('objects/'));
+    expect(result['valueAsString'], isNull);
+    expect(result['class']['type'], equals('@Class'));
+    expect(result['class']['name'], equals('_Uint8List'));
+    expect(result['size'], isPositive);
+    expect(result['fields'], isEmpty);
+    expect(result['length'], equals(3));
+    expect(result['offset'], equals(3));
+    expect(result['count'], equals(0));
+    expect(result['bytes'], equals(''));
+  },
+
+  // Uint64List.
+  (Isolate isolate) async {
+    // Call eval to get a Dart list.
+    var evalResult = await invoke(isolate, 'getUint64List');
+    var params = {
+      'objectId': evalResult['id'],
+    };
+    var result = await isolate.invokeRpcNoUpgrade('getObject', params);
+    expect(result['type'], equals('Instance'));
+    expect(result['kind'], equals('Uint64List'));
+    expect(result['_vmType'], equals('TypedData'));
+    expect(result['id'], startsWith('objects/'));
+    expect(result['valueAsString'], isNull);
+    expect(result['class']['type'], equals('@Class'));
+    expect(result['class']['name'], equals('_Uint64List'));
+    expect(result['size'], isPositive);
+    expect(result['fields'], isEmpty);
+    expect(result['length'], equals(3));
+    expect(result['offset'], isNull);
+    expect(result['count'], isNull);
+    expect(result['bytes'], equals('AwAAAAAAAAACAAAAAAAAAAEAAAAAAAAA'));
+    Uint8List bytes = base64Decode(result['bytes']);
+    expect(bytes.buffer.asUint64List().toString(), equals('[3, 2, 1]'));
+  },
+
+  // Uint64List prefix.
+  (Isolate isolate) async {
+    // Call eval to get a Dart list.
+    var evalResult = await invoke(isolate, 'getUint64List');
+    var params = {
+      'objectId': evalResult['id'],
+      'count': 2,
+    };
+    var result = await isolate.invokeRpcNoUpgrade('getObject', params);
+    expect(result['type'], equals('Instance'));
+    expect(result['kind'], equals('Uint64List'));
+    expect(result['_vmType'], equals('TypedData'));
+    expect(result['id'], startsWith('objects/'));
+    expect(result['valueAsString'], isNull);
+    expect(result['class']['type'], equals('@Class'));
+    expect(result['class']['name'], equals('_Uint64List'));
+    expect(result['size'], isPositive);
+    expect(result['fields'], isEmpty);
+    expect(result['length'], equals(3));
+    expect(result['offset'], isNull);
+    expect(result['count'], equals(2));
+    expect(result['bytes'], equals('AwAAAAAAAAACAAAAAAAAAA=='));
+    Uint8List bytes = base64Decode(result['bytes']);
+    expect(bytes.buffer.asUint64List().toString(), equals('[3, 2]'));
+  },
+
+  // Uint64List suffix.
+  (Isolate isolate) async {
+    // Call eval to get a Dart list.
+    var evalResult = await invoke(isolate, 'getUint64List');
+    var params = {
+      'objectId': evalResult['id'],
+      'offset': 2,
+      'count': 2,
+    };
+    var result = await isolate.invokeRpcNoUpgrade('getObject', params);
+    expect(result['type'], equals('Instance'));
+    expect(result['kind'], equals('Uint64List'));
+    expect(result['_vmType'], equals('TypedData'));
+    expect(result['id'], startsWith('objects/'));
+    expect(result['valueAsString'], isNull);
+    expect(result['class']['type'], equals('@Class'));
+    expect(result['class']['name'], equals('_Uint64List'));
+    expect(result['size'], isPositive);
+    expect(result['fields'], isEmpty);
+    expect(result['length'], equals(3));
+    expect(result['offset'], equals(2));
+    expect(result['count'], equals(1));
+    expect(result['bytes'], equals('AQAAAAAAAAA='));
+    Uint8List bytes = base64Decode(result['bytes']);
+    expect(bytes.buffer.asUint64List().toString(), equals('[1]'));
+  },
+
+  // Uint64List with wacky offset.
+  (Isolate isolate) async {
+    // Call eval to get a Dart list.
+    var evalResult = await invoke(isolate, 'getUint64List');
+    var params = {
+      'objectId': evalResult['id'],
+      'offset': 100,
+      'count': 2,
+    };
+    var result = await isolate.invokeRpcNoUpgrade('getObject', params);
+    expect(result['type'], equals('Instance'));
+    expect(result['kind'], equals('Uint64List'));
+    expect(result['_vmType'], equals('TypedData'));
+    expect(result['id'], startsWith('objects/'));
+    expect(result['valueAsString'], isNull);
+    expect(result['class']['type'], equals('@Class'));
+    expect(result['class']['name'], equals('_Uint64List'));
+    expect(result['size'], isPositive);
+    expect(result['fields'], isEmpty);
+    expect(result['length'], equals(3));
+    expect(result['offset'], equals(3));
+    expect(result['count'], equals(0));
+    expect(result['bytes'], equals(''));
+  },
+
+  // An expired object.
+  (Isolate isolate) async {
+    var params = {
+      'objectId': 'objects/99999999',
+    };
+    var result = await isolate.invokeRpcNoUpgrade('getObject', params);
+    expect(result['type'], equals('Sentinel'));
+    expect(result['kind'], startsWith('Expired'));
+    expect(result['valueAsString'], equals('<expired>'));
+    expect(result['class'], isNull);
+    expect(result['size'], isNull);
+  },
+
+  // library.
+  (Isolate isolate) async {
+    var params = {
+      'objectId': isolate.rootLibrary.id,
+    };
+    var result = await isolate.invokeRpcNoUpgrade('getObject', params);
+    expect(result['type'], equals('Library'));
+    expect(result['id'], startsWith('libraries/'));
+    expect(result['name'], equals('get_object_rpc_test'));
+    expect(result['uri'], startsWith('file:'));
+    expect(result['uri'], endsWith('get_object_rpc_test.dart'));
+    expect(result['debuggable'], equals(true));
+    expect(result['dependencies'].length, isPositive);
+    expect(result['dependencies'][0]['target']['type'], equals('@Library'));
+    expect(result['scripts'].length, isPositive);
+    expect(result['scripts'][0]['type'], equals('@Script'));
+    expect(result['variables'].length, isPositive);
+    expect(result['variables'][0]['type'], equals('@Field'));
+    expect(result['functions'].length, isPositive);
+    expect(result['functions'][0]['type'], equals('@Function'));
+    expect(result['classes'].length, isPositive);
+    expect(result['classes'][0]['type'], equals('@Class'));
+  },
+
+  // invalid library.
+  (Isolate isolate) async {
+    var params = {
+      'objectId': 'libraries/9999999',
+    };
+    bool caughtException;
+    try {
+      await isolate.invokeRpcNoUpgrade('getObject', params);
+      expect(false, isTrue, reason: 'Unreachable');
+    } on ServerRpcException catch (e) {
+      caughtException = true;
+      expect(e.code, equals(ServerRpcException.kInvalidParams));
+      expect(e.message,
+          "getObject: invalid 'objectId' parameter: libraries/9999999");
+    }
+    expect(caughtException, isTrue);
+  },
+
+  // script.
+  (Isolate isolate) async {
+    // Get the library first.
+    var params = {
+      'objectId': isolate.rootLibrary.id,
+    };
+    var libResult = await isolate.invokeRpcNoUpgrade('getObject', params);
+    // Get the first script.
+    params = {
+      'objectId': libResult['scripts'][0]['id'],
+    };
+    var result = await isolate.invokeRpcNoUpgrade('getObject', params);
+    expect(result['type'], equals('Script'));
+    expect(result['id'], startsWith('libraries/'));
+    expect(result['uri'], startsWith('file:'));
+    expect(result['uri'], endsWith('get_object_rpc_test.dart'));
+    expect(result['_kind'], equals('kernel'));
+    expect(result['library']['type'], equals('@Library'));
+    expect(result['source'], startsWith('// Copyright (c)'));
+    expect(result['tokenPosTable'].length, isPositive);
+    expect(result['tokenPosTable'][0], isA<List>());
+    expect(result['tokenPosTable'][0].length, isPositive);
+    expect(result['tokenPosTable'][0][0], isA<int>());
+  },
+
+  // invalid script.
+  (Isolate isolate) async {
+    var params = {
+      'objectId': 'scripts/9999999',
+    };
+    bool caughtException;
+    try {
+      await isolate.invokeRpcNoUpgrade('getObject', params);
+      expect(false, isTrue, reason: 'Unreachable');
+    } on ServerRpcException catch (e) {
+      caughtException = true;
+      expect(e.code, equals(ServerRpcException.kInvalidParams));
+      expect(e.message,
+          "getObject: invalid 'objectId' parameter: scripts/9999999");
+    }
+    expect(caughtException, isTrue);
+  },
+
+  // class
+  (Isolate isolate) async {
+    // Call eval to get a class id.
+    var evalResult = await invoke(isolate, 'getDummyClass');
+    var params = {
+      'objectId': evalResult['class']['id'],
+    };
+    var result = await isolate.invokeRpcNoUpgrade('getObject', params);
+    expect(result['type'], equals('Class'));
+    expect(result['id'], startsWith('classes/'));
+    expect(result['name'], equals('_DummyClass'));
+    expect(result['_vmName'], startsWith('_DummyClass@'));
+    expect(result['abstract'], equals(false));
+    expect(result['const'], equals(false));
+    expect(result['_finalized'], equals(true));
+    expect(result['_implemented'], equals(false));
+    expect(result['_patch'], equals(false));
+    expect(result['library']['type'], equals('@Library'));
+    expect(result['location']['type'], equals('SourceLocation'));
+    expect(result['super']['type'], equals('@Class'));
+    expect(result['interfaces'].length, isZero);
+    expect(result['fields'].length, isPositive);
+    expect(result['fields'][0]['type'], equals('@Field'));
+    expect(result['functions'].length, isPositive);
+    expect(result['functions'][0]['type'], equals('@Function'));
+    expect(result['subclasses'].length, isPositive);
+    expect(result['subclasses'][0]['type'], equals('@Class'));
+  },
+
+  // invalid class.
+  (Isolate isolate) async {
+    var params = {
+      'objectId': 'classes/9999999',
+    };
+    bool caughtException;
+    try {
+      await isolate.invokeRpcNoUpgrade('getObject', params);
+      expect(false, isTrue, reason: 'Unreachable');
+    } on ServerRpcException catch (e) {
+      caughtException = true;
+      expect(e.code, equals(ServerRpcException.kInvalidParams));
+      expect(e.message,
+          "getObject: invalid 'objectId' parameter: classes/9999999");
+    }
+    expect(caughtException, isTrue);
+  },
+
+  // type.
+  (Isolate isolate) async {
+    // Call eval to get a class id.
+    var evalResult = await invoke(isolate, 'getDummyClass');
+    var id = "${evalResult['class']['id']}/types/0";
+    var params = {
+      'objectId': id,
+    };
+    var result = await isolate.invokeRpcNoUpgrade('getObject', params);
+    expect(result['type'], equals('Instance'));
+    expect(result['kind'], equals('Type'));
+    expect(result['id'], equals(id));
+    expect(result['class']['type'], equals('@Class'));
+    expect(result['class']['name'], equals('_Type'));
+    expect(result['size'], isPositive);
+    expect(result['fields'], isEmpty);
+    expect(result['typeClass']['type'], equals('@Class'));
+    expect(result['typeClass']['name'], equals('_DummyClass'));
+  },
+
+  // invalid type.
+  (Isolate isolate) async {
+    var evalResult = await invoke(isolate, 'getDummyClass');
+    var id = "${evalResult['class']['id']}/types/9999999";
+    var params = {
+      'objectId': id,
+    };
+    bool caughtException;
+    try {
+      await isolate.invokeRpcNoUpgrade('getObject', params);
+      expect(false, isTrue, reason: 'Unreachable');
+    } on ServerRpcException catch (e) {
+      caughtException = true;
+      expect(e.code, equals(ServerRpcException.kInvalidParams));
+      expect(
+          e.message, startsWith("getObject: invalid 'objectId' parameter: "));
+    }
+    expect(caughtException, isTrue);
+  },
+
+  // function.
+  (Isolate isolate) async {
+    // Call eval to get a class id.
+    var evalResult = await invoke(isolate, 'getDummyClass');
+    var id = "${evalResult['class']['id']}/functions/dummyFunction";
+    var params = {
+      'objectId': id,
+    };
+    var result = await isolate.invokeRpcNoUpgrade('getObject', params);
+    expect(result['type'], equals('Function'));
+    expect(result['id'], equals(id));
+    expect(result['name'], equals('dummyFunction'));
+    expect(result['_kind'], equals('RegularFunction'));
+    expect(result['static'], equals(false));
+    expect(result['const'], equals(false));
+    expect(result['location']['type'], equals('SourceLocation'));
+    expect(result['code']['type'], equals('@Code'));
+    expect(result['_optimizable'], equals(true));
+    expect(result['_inlinable'], equals(true));
+    expect(result['_usageCounter'], isPositive);
+    expect(result['_optimizedCallSiteCount'], isZero);
+    expect(result['_deoptimizations'], isZero);
+  },
+
+  // invalid function.
+  (Isolate isolate) async {
+    // Call eval to get a class id.
+    var evalResult = await invoke(isolate, 'getDummyClass');
+    var id = "${evalResult['class']['id']}/functions/invalid";
+    var params = {
+      'objectId': id,
+    };
+    bool caughtException;
+    try {
+      await isolate.invokeRpcNoUpgrade('getObject', params);
+      expect(false, isTrue, reason: 'Unreachable');
+    } on ServerRpcException catch (e) {
+      caughtException = true;
+      expect(e.code, equals(ServerRpcException.kInvalidParams));
+      expect(
+          e.message, startsWith("getObject: invalid 'objectId' parameter: "));
+    }
+    expect(caughtException, isTrue);
+  },
+
+  // field
+  (Isolate isolate) async {
+    // Call eval to get a class id.
+    var evalResult = await invoke(isolate, 'getDummyClass');
+    var id = "${evalResult['class']['id']}/fields/dummyVar";
+    var params = {
+      'objectId': id,
+    };
+    var result = await isolate.invokeRpcNoUpgrade('getObject', params);
+    expect(result['type'], equals('Field'));
+    expect(result['id'], equals(id));
+    expect(result['name'], equals('dummyVar'));
+    expect(result['const'], equals(false));
+    expect(result['static'], equals(true));
+    expect(result['final'], equals(false));
+    expect(result['location']['type'], equals('SourceLocation'));
+    expect(result['staticValue']['valueAsString'], equals('11'));
+    expect(result['_guardNullable'], isNotNull);
+    expect(result['_guardClass'], isNotNull);
+    expect(result['_guardLength'], isNotNull);
+  },
+
+  // field with guards
+  (Isolate isolate) async {
+    var result = await isolate.vm.invokeRpcNoUpgrade('getFlagList', {});
+    var use_field_guards = false;
+    for (var flag in result['flags']) {
+      if (flag['name'] == 'use_field_guards') {
+        use_field_guards = flag['valueAsString'] == 'true';
+        break;
+      }
+    }
+    if (!use_field_guards) {
+      return; // skip the test if guards are not enabled
+    }
+
+    // Call eval to get a class id.
+    var evalResult = await invoke(isolate, 'getDummyClass');
+    var id = "${evalResult['class']['id']}/fields/dummyList";
+    var params = {
+      'objectId': id,
+    };
+    result = await isolate.invokeRpcNoUpgrade('getObject', params);
+    expect(result['type'], equals('Field'));
+    expect(result['id'], equals(id));
+    expect(result['name'], equals('dummyList'));
+    expect(result['const'], equals(false));
+    expect(result['static'], equals(false));
+    expect(result['final'], equals(true));
+    expect(result['location']['type'], equals('SourceLocation'));
+    expect(result['_guardNullable'], isNotNull);
+    expect(result['_guardClass'], isNotNull);
+    expect(result['_guardLength'], equals('20'));
+  },
+
+  // invalid field.
+  (Isolate isolate) async {
+    // Call eval to get a class id.
+    var evalResult = await invoke(isolate, 'getDummyClass');
+    var id = "${evalResult['class']['id']}/fields/mythicalField";
+    var params = {
+      'objectId': id,
+    };
+    bool caughtException;
+    try {
+      await isolate.invokeRpcNoUpgrade('getObject', params);
+      expect(false, isTrue, reason: 'Unreachable');
+    } on ServerRpcException catch (e) {
+      caughtException = true;
+      expect(e.code, equals(ServerRpcException.kInvalidParams));
+      expect(
+          e.message, startsWith("getObject: invalid 'objectId' parameter: "));
+    }
+    expect(caughtException, isTrue);
+  },
+
+  // code.
+  (Isolate isolate) async {
+    // Call eval to get a class id.
+    var evalResult = await invoke(isolate, 'getDummyClass');
+    var funcId = "${evalResult['class']['id']}/functions/dummyFunction";
+    var params = {
+      'objectId': funcId,
+    };
+    var funcResult = await isolate.invokeRpcNoUpgrade('getObject', params);
+    params = {
+      'objectId': funcResult['code']['id'],
+    };
+    var result = await isolate.invokeRpcNoUpgrade('getObject', params);
+    expect(result['type'], equals('Code'));
+    expect(result['name'], endsWith('_DummyClass.dummyFunction'));
+    expect(result['_vmName'], endsWith('dummyFunction'));
+    expect(result['kind'], equals('Dart'));
+    expect(result['_optimized'], isA<bool>());
+    expect(result['function']['type'], equals('@Function'));
+    expect(result['_startAddress'], isA<String>());
+    expect(result['_endAddress'], isA<String>());
+    expect(result['_objectPool'], isNotNull);
+    expect(result['_disassembly'], isNotNull);
+    expect(result['_descriptors'], isNotNull);
+    expect(result['_inlinedFunctions'], anyOf([isNull, isA<List>()]));
+    expect(result['_inlinedIntervals'], anyOf([isNull, isA<List>()]));
+  },
+
+  // invalid code.
+  (Isolate isolate) async {
+    var params = {
+      'objectId': 'code/0',
+    };
+    bool caughtException;
+    try {
+      await isolate.invokeRpcNoUpgrade('getObject', params);
+      expect(false, isTrue, reason: 'Unreachable');
+    } on ServerRpcException catch (e) {
+      caughtException = true;
+      expect(e.code, equals(ServerRpcException.kInvalidParams));
+      expect(e.message, "getObject: invalid 'objectId' parameter: code/0");
+    }
+    expect(caughtException, isTrue);
+  },
+];
+
+main(args) async => runIsolateTests(args, tests, testeeBefore: warmup);
diff --git a/runtime/observatory_2/tests/service_2/get_object_store_rpc_test.dart b/runtime/observatory_2/tests/service_2/get_object_store_rpc_test.dart
new file mode 100644
index 0000000..616c5ab
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/get_object_store_rpc_test.dart
@@ -0,0 +1,32 @@
+// Copyright (c) 2016, 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.
+
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+void testeeMain() {}
+
+bool isClosureFunctionsList(NamedField field) {
+  return field.name == 'closure_functions_';
+}
+
+var tests = <IsolateTest>[
+// Get object_store.
+  (Isolate isolate) async {
+    var object_store = await isolate.getObjectStore();
+    expect(object_store.runtimeType, equals(ObjectStore));
+    // Sanity check.
+    expect(object_store.fields.length, greaterThanOrEqualTo(1));
+    // Checking Closures.
+    var single = object_store.fields.singleWhere(isClosureFunctionsList);
+    expect(single, isNotNull);
+    var value = single.value as Instance;
+    expect(value.isList, isTrue);
+  }
+];
+
+main(args) => runIsolateTestsSynchronous(args, tests, testeeBefore: testeeMain);
diff --git a/runtime/observatory_2/tests/service_2/get_ports_rpc_test.dart b/runtime/observatory_2/tests/service_2/get_ports_rpc_test.dart
new file mode 100644
index 0000000..be65e05
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/get_ports_rpc_test.dart
@@ -0,0 +1,53 @@
+// Copyright (c) 2015, 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.
+
+library get_ports_rpc_test;
+
+import 'dart:isolate' hide Isolate;
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+
+import 'test_helper.dart';
+
+var port1;
+var port2;
+
+void warmup() {
+  port1 = new RawReceivePort(null);
+  port2 = new RawReceivePort((_) {});
+}
+
+int countHandlerMatches(ports, matcher) {
+  var matches = 0;
+  for (var port in ports) {
+    if (matcher(port['handler'])) {
+      matches++;
+    }
+  }
+  return matches;
+}
+
+bool nullMatcher(handler) {
+  return handler.isNull;
+}
+
+bool closureMatcher(handler) {
+  return handler.isClosure;
+}
+
+var tests = <IsolateTest>[
+  (Isolate isolate) async {
+    dynamic result = await isolate.invokeRpc('_getPorts', {});
+    expect(result['type'], equals('_Ports'));
+    expect(result['ports'], isList);
+    var ports = result['ports'];
+    // There are at least two ports: the two created in warm up. Some OSes
+    // will have other ports open but we do not try and test for these.
+    expect(ports.length, greaterThanOrEqualTo(2));
+    expect(countHandlerMatches(ports, nullMatcher), greaterThanOrEqualTo(1));
+    expect(countHandlerMatches(ports, closureMatcher), greaterThanOrEqualTo(1));
+  },
+];
+
+main(args) async => runIsolateTests(args, tests, testeeBefore: warmup);
diff --git a/runtime/observatory_2/tests/service_2/get_process_memory_usage_rpc_test.dart b/runtime/observatory_2/tests/service_2/get_process_memory_usage_rpc_test.dart
new file mode 100644
index 0000000..efd056e
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/get_process_memory_usage_rpc_test.dart
@@ -0,0 +1,28 @@
+// Copyright (c) 2020, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+
+import 'test_helper.dart';
+
+var tests = <VMTest>[
+  (VM vm) async {
+    var result = await vm.invokeRpcNoUpgrade("getProcessMemoryUsage", {});
+    expect(result['type'], equals('ProcessMemoryUsage'));
+    checkProcessMemoryItem(item) {
+      expect(item['name'], isA<String>());
+      expect(item['description'], isA<String>());
+      expect(item['size'], isA<int>());
+      expect(item['size'], greaterThanOrEqualTo(0));
+      for (var child in item['children']) {
+        checkProcessMemoryItem(child);
+      }
+    }
+
+    checkProcessMemoryItem(result['root']);
+  },
+];
+
+main(args) async => runVMTests(args, tests);
diff --git a/runtime/observatory_2/tests/service_2/get_retained_size_rpc_test.dart b/runtime/observatory_2/tests/service_2/get_retained_size_rpc_test.dart
new file mode 100644
index 0000000..edc5c78
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/get_retained_size_rpc_test.dart
@@ -0,0 +1,77 @@
+// Copyright (c) 2015, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+
+import 'test_helper.dart';
+
+class _TestClass {
+  _TestClass(this.x, this.y);
+  // Make sure these fields are not removed by the tree shaker.
+  @pragma("vm:entry-point")
+  var x;
+  @pragma("vm:entry-point")
+  var y;
+}
+
+@pragma("vm:entry-point")
+var myVar;
+
+@pragma("vm:entry-point")
+invoke1() => myVar = new _TestClass(null, null);
+
+@pragma("vm:entry-point")
+invoke2() => myVar = new _TestClass(new _TestClass(null, null), null);
+
+invoke(Isolate isolate, String selector) async {
+  Map params = {
+    'targetId': isolate.rootLibrary.id,
+    'selector': selector,
+    'argumentIds': <String>[],
+  };
+  return await isolate.invokeRpcNoUpgrade('invoke', params);
+}
+
+var tests = <IsolateTest>[
+  (Isolate isolate) async {
+    // One instance of _TestClass retained.
+    var evalResult = await invoke(isolate, 'invoke1');
+    var params = {
+      'targetId': evalResult['id'],
+    };
+    var result = await isolate.invokeRpcNoUpgrade('_getRetainedSize', params);
+    expect(result['type'], equals('@Instance'));
+    expect(result['kind'], equals('Int'));
+    int value1 = int.parse(result['valueAsString']);
+    expect(value1, isPositive);
+
+    // Two instances of _TestClass retained.
+    evalResult = await invoke(isolate, 'invoke2');
+    params = {
+      'targetId': evalResult['id'],
+    };
+    result = await isolate.invokeRpcNoUpgrade('_getRetainedSize', params);
+    expect(result['type'], equals('@Instance'));
+    expect(result['kind'], equals('Int'));
+    int value2 = int.parse(result['valueAsString']);
+    expect(value2, isPositive);
+
+    // Size has doubled.
+    expect(value2, equals(2 * value1));
+
+    // Get the retained size for class _TestClass.
+    params = {
+      'targetId': evalResult['class']['id'],
+    };
+    result = await isolate.invokeRpcNoUpgrade('_getRetainedSize', params);
+    expect(result['type'], equals('@Instance'));
+    expect(result['kind'], equals('Int'));
+    int value3 = int.parse(result['valueAsString']);
+    expect(value3, isPositive);
+    expect(value3, equals(value2));
+  },
+];
+
+main(args) async => runIsolateTests(args, tests);
diff --git a/runtime/observatory_2/tests/service_2/get_retaining_path_rpc_test.dart b/runtime/observatory_2/tests/service_2/get_retaining_path_rpc_test.dart
new file mode 100644
index 0000000..2bbc008
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/get_retaining_path_rpc_test.dart
@@ -0,0 +1,211 @@
+// Copyright (c) 2015, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+
+import 'test_helper.dart';
+
+class _TestClass {
+  _TestClass();
+  // Make sure these fields are not removed by the tree shaker.
+  @pragma("vm:entry-point")
+  var x;
+  @pragma("vm:entry-point")
+  var y;
+}
+
+var target1 = new _TestClass();
+var target2 = new _TestClass();
+var target3 = new _TestClass();
+var target4 = new _TestClass();
+var target5 = new _TestClass();
+@pragma("vm:entry-point") // Prevent obfuscation
+var globalObject = new _TestClass();
+@pragma("vm:entry-point") // Prevent obfuscation
+var globalList = new List(100);
+@pragma("vm:entry-point") // Prevent obfuscation
+var globalMap1 = new Map();
+@pragma("vm:entry-point") // Prevent obfuscation
+var globalMap2 = new Map();
+
+void warmup() {
+  globalObject.x = target1;
+  globalObject.y = target2;
+  globalList[12] = target3;
+  globalMap1['key'] = target4;
+  globalMap2[target5] = 'value';
+}
+
+@pragma("vm:entry-point")
+getGlobalObject() => globalObject;
+
+@pragma("vm:entry-point")
+takeTarget1() {
+  var tmp = target1;
+  target1 = null;
+  return tmp;
+}
+
+@pragma("vm:entry-point")
+takeTarget2() {
+  var tmp = target2;
+  target2 = null;
+  return tmp;
+}
+
+@pragma("vm:entry-point")
+takeTarget3() {
+  var tmp = target3;
+  target3 = null;
+  return tmp;
+}
+
+@pragma("vm:entry-point")
+takeTarget4() {
+  var tmp = target4;
+  target4 = null;
+  return tmp;
+}
+
+@pragma("vm:entry-point")
+takeTarget5() {
+  var tmp = target5;
+  target5 = null;
+  return tmp;
+}
+
+@pragma("vm:entry-point")
+getTrue() => true;
+
+invoke(Isolate isolate, String selector) async {
+  Map params = {
+    'targetId': isolate.rootLibrary.id,
+    'selector': selector,
+    'argumentIds': <String>[],
+  };
+  return await isolate.invokeRpcNoUpgrade('invoke', params);
+}
+
+var tests = <IsolateTest>[
+  // simple path
+  (Isolate isolate) async {
+    var obj = await invoke(isolate, 'getGlobalObject');
+    var params = {
+      'targetId': obj['id'],
+      'limit': 100,
+    };
+    var result = await isolate.invokeRpcNoUpgrade('getRetainingPath', params);
+    expect(result['gcRootType'], 'user global');
+    expect(result['elements'].length, equals(2));
+    expect(result['elements'][1]['value']['name'], equals('globalObject'));
+  },
+
+  // missing limit.
+  (Isolate isolate) async {
+    var obj = await invoke(isolate, 'getGlobalObject');
+    var params = {
+      'targetId': obj['id'],
+    };
+    bool caughtException;
+    try {
+      await isolate.invokeRpcNoUpgrade('getRetainingPath', params);
+      expect(false, isTrue, reason: 'Unreachable');
+    } on ServerRpcException catch (e) {
+      caughtException = true;
+      expect(e.code, equals(ServerRpcException.kInvalidParams));
+      expect(e.data['details'],
+          "getRetainingPath expects the \'limit\' parameter");
+    }
+    expect(caughtException, isTrue);
+  },
+
+  (Isolate isolate) async {
+    var target1 = await invoke(isolate, 'takeTarget1');
+    var params = {
+      'targetId': target1['id'],
+      'limit': 100,
+    };
+    var result = await isolate.invokeRpcNoUpgrade('getRetainingPath', params);
+    expect(result['type'], equals('RetainingPath'));
+    expect(result['gcRootType'], 'user global');
+    expect(result['elements'].length, equals(3));
+    expect(result['elements'][1]['parentField'], equals('x'));
+    expect(result['elements'][2]['value']['name'], equals('globalObject'));
+  },
+
+  (Isolate isolate) async {
+    var target2 = await invoke(isolate, 'takeTarget2');
+    var params = {
+      'targetId': target2['id'],
+      'limit': 100,
+    };
+    var result = await isolate.invokeRpcNoUpgrade('getRetainingPath', params);
+    expect(result['type'], equals('RetainingPath'));
+    expect(result['gcRootType'], 'user global');
+    expect(result['elements'].length, equals(3));
+    expect(result['elements'][1]['parentField'], equals('y'));
+    expect(result['elements'][2]['value']['name'], equals('globalObject'));
+  },
+
+  (Isolate isolate) async {
+    var target3 = await invoke(isolate, 'takeTarget3');
+    var params = {
+      'targetId': target3['id'],
+      'limit': 100,
+    };
+    var result = await isolate.invokeRpcNoUpgrade('getRetainingPath', params);
+    expect(result['type'], equals('RetainingPath'));
+    expect(result['gcRootType'], 'user global');
+    expect(result['elements'].length, equals(3));
+    expect(result['elements'][1]['parentListIndex'], equals(12));
+    expect(result['elements'][2]['value']['name'], equals('globalList'));
+  },
+
+  (Isolate isolate) async {
+    var target4 = await invoke(isolate, 'takeTarget4');
+    var params = {
+      'targetId': target4['id'],
+      'limit': 100,
+    };
+    var result = await isolate.invokeRpcNoUpgrade('getRetainingPath', params);
+    expect(result['type'], equals('RetainingPath'));
+    expect(result['gcRootType'], 'user global');
+    expect(result['elements'].length, equals(3));
+    expect(
+        result['elements'][1]['parentMapKey']['valueAsString'], equals('key'));
+    expect(result['elements'][2]['value']['name'], equals('globalMap1'));
+  },
+
+  (Isolate isolate) async {
+    var target5 = await invoke(isolate, 'takeTarget5');
+    var params = {
+      'targetId': target5['id'],
+      'limit': 100,
+    };
+    var result = await isolate.invokeRpcNoUpgrade('getRetainingPath', params);
+    expect(result['type'], equals('RetainingPath'));
+    expect(result['elements'].length, equals(3));
+    expect(result['elements'][1]['parentMapKey']['class']['name'],
+        equals('_TestClass'));
+    expect(result['elements'][2]['value']['name'], equals('globalMap2'));
+  },
+
+  // object store
+  (Isolate isolate) async {
+    var obj = await invoke(isolate, 'getTrue');
+    var params = {
+      'targetId': obj['id'],
+      'limit': 100,
+    };
+    var result = await isolate.invokeRpcNoUpgrade('getRetainingPath', params);
+    expect(
+        result['gcRootType'] == 'class table' ||
+            result['gcRootType'] == 'isolate_object store',
+        true);
+    expect(result['elements'].length, 0);
+  },
+];
+
+main(args) async => runIsolateTests(args, tests, testeeBefore: warmup);
diff --git a/runtime/observatory_2/tests/service_2/get_scripts_rpc_test.dart b/runtime/observatory_2/tests/service_2/get_scripts_rpc_test.dart
new file mode 100644
index 0000000..e778135
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/get_scripts_rpc_test.dart
@@ -0,0 +1,49 @@
+// Copyright (c) 2018, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+
+import 'test_helper.dart';
+
+var tests = <VMTest>[
+  (VM vm) async {
+    var params = {
+      'isolateId': vm.isolates.first.id,
+    };
+    var result = await vm.invokeRpcNoUpgrade('getScripts', params);
+    expect(result['type'], equals('ScriptList'));
+    expect(result['scripts'].length, isPositive);
+    expect(result['scripts'][0]['type'], equals('@Script'));
+  },
+
+  (VM vm) async {
+    var params = {
+      'isolateId': 'badid',
+    };
+    bool caughtException;
+    try {
+      await vm.invokeRpcNoUpgrade('getScripts', params);
+      expect(false, isTrue, reason: 'Unreachable');
+    } on ServerRpcException catch (e) {
+      caughtException = true;
+      expect(e.code, equals(ServerRpcException.kInvalidParams));
+      expect(e.message, "getScripts: invalid 'isolateId' parameter: badid");
+    }
+    expect(caughtException, isTrue);
+  },
+
+  // Plausible isolate id, not found.
+  (VM vm) async {
+    var params = {
+      'isolateId': 'isolates/9999999999',
+    };
+    var result = await vm.invokeRpcNoUpgrade('getScripts', params);
+    expect(result['type'], equals('Sentinel'));
+    expect(result['kind'], equals('Collected'));
+    expect(result['valueAsString'], equals('<collected>'));
+  },
+];
+
+main(args) async => runVMTests(args, tests);
diff --git a/runtime/observatory_2/tests/service_2/get_source_report_test.dart b/runtime/observatory_2/tests/service_2/get_source_report_test.dart
new file mode 100644
index 0000000..984de51
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/get_source_report_test.dart
@@ -0,0 +1,199 @@
+// Copyright (c) 2016, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+import 'service_test_common.dart';
+import 'dart:developer';
+
+int globalVar = 100;
+
+class MyClass {
+  static void myFunction(int value) {
+    if (value < 0) {
+      print("negative");
+    } else {
+      print("positive");
+    }
+    debugger();
+  }
+
+  static void otherFunction(int value) {
+    if (value < 0) {
+      print("otherFunction <");
+    } else {
+      print("otherFunction >=");
+    }
+  }
+}
+
+void testFunction() {
+  MyClass.otherFunction(-100);
+  MyClass.myFunction(10000);
+}
+
+class MyConstClass {
+  const MyConstClass();
+  static const MyConstClass instance = null ?? const MyConstClass();
+
+  void foo() {
+    debugger();
+  }
+}
+
+void testFunction2() {
+  MyConstClass.instance.foo();
+}
+
+bool allRangesCompiled(coverage) {
+  for (int i = 0; i < coverage['ranges'].length; i++) {
+    if (!coverage['ranges'][i]['compiled']) {
+      return false;
+    }
+  }
+  return true;
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  (Isolate isolate) async {
+    var stack = await isolate.getStack();
+
+    // Make sure we are in the right place.
+    expect(stack.type, equals('Stack'));
+    expect(stack['frames'].length, greaterThanOrEqualTo(2));
+    expect(stack['frames'][0].function.name, equals('myFunction'));
+    expect(stack['frames'][0].function.dartOwner.name, equals('MyClass'));
+
+    var func = stack['frames'][0].function;
+    expect(func.name, equals('myFunction'));
+    await func.load();
+
+    var expectedRange = {
+      'scriptIndex': 0,
+      'startPos': 432,
+      'endPos': 576,
+      'compiled': true,
+      'coverage': {
+        'hits': [432, 482, 533, 562],
+        'misses': [495],
+      }
+    };
+
+    // Full script
+    var params = {
+      'reports': ['Coverage'],
+      'scriptId': func.location.script.id
+    };
+    var coverage = await isolate.invokeRpcNoUpgrade('getSourceReport', params);
+    final numRanges = coverage['ranges'].length;
+    expect(coverage['type'], equals('SourceReport'));
+
+    expect(numRanges, equals(12));
+    expect(coverage['ranges'][0], equals(expectedRange));
+    expect(coverage['scripts'].length, 1);
+    expect(
+        coverage['scripts'][0]['uri'], endsWith('get_source_report_test.dart'));
+    expect(allRangesCompiled(coverage), isFalse);
+
+    // Force compilation.
+    params = {
+      'reports': ['Coverage'],
+      'scriptId': func.location.script.id,
+      'forceCompile': true
+    };
+    coverage = await isolate.invokeRpcNoUpgrade('getSourceReport', params);
+    expect(coverage['type'], equals('SourceReport'));
+    expect(coverage['ranges'].length, numRanges);
+    expect(allRangesCompiled(coverage), isTrue);
+
+    // One function
+    params = {
+      'reports': ['Coverage'],
+      'scriptId': func.location.script.id,
+      'tokenPos': func.location.tokenPos,
+      'endTokenPos': func.location.endTokenPos
+    };
+    coverage = await isolate.invokeRpcNoUpgrade('getSourceReport', params);
+    expect(coverage['type'], equals('SourceReport'));
+    expect(coverage['ranges'].length, 1);
+    expect(coverage['ranges'][0], equals(expectedRange));
+    expect(coverage['scripts'].length, 1);
+    expect(
+        coverage['scripts'][0]['uri'], endsWith('get_source_report_test.dart'));
+
+    // Full isolate
+    params = {
+      'reports': ['Coverage']
+    };
+    coverage = await isolate.invokeRpcNoUpgrade('getSourceReport', params);
+    expect(coverage['type'], equals('SourceReport'));
+    expect(coverage['ranges'].length, greaterThan(1));
+    expect(coverage['scripts'].length, greaterThan(1));
+
+    // Full isolate
+    params = {
+      'reports': ['Coverage'],
+      'forceCompile': true
+    };
+    coverage = await isolate.invokeRpcNoUpgrade('getSourceReport', params);
+    expect(coverage['type'], equals('SourceReport'));
+    expect(coverage['ranges'].length, greaterThan(1));
+    expect(coverage['scripts'].length, greaterThan(1));
+
+    // Multiple reports (make sure enum list parameter parsing works).
+    params = {
+      'reports': ['_CallSites', 'Coverage', 'PossibleBreakpoints'],
+      'scriptId': func.location.script.id,
+      'tokenPos': func.location.tokenPos,
+      'endTokenPos': func.location.endTokenPos
+    };
+    coverage = await isolate.invokeRpcNoUpgrade('getSourceReport', params);
+    expect(coverage['type'], equals('SourceReport'));
+    expect(coverage['ranges'].length, 1);
+    var range = coverage['ranges'][0];
+    expect(range.containsKey('callSites'), isTrue);
+    expect(range.containsKey('coverage'), isTrue);
+    expect(range.containsKey('possibleBreakpoints'), isTrue);
+
+    // missing scriptId with tokenPos.
+    bool caughtException = false;
+    try {
+      params = {
+        'reports': ['Coverage'],
+        'tokenPos': func.location.tokenPos
+      };
+      coverage = await isolate.invokeRpcNoUpgrade('getSourceReport', params);
+    } on ServerRpcException catch (e) {
+      caughtException = true;
+      expect(e.code, equals(ServerRpcException.kInvalidParams));
+      expect(
+          e.message,
+          "getSourceReport: the 'tokenPos' parameter requires the "
+          "\'scriptId\' parameter");
+    }
+    expect(caughtException, isTrue);
+
+    // missing scriptId with endTokenPos.
+    caughtException = false;
+    try {
+      params = {
+        'reports': ['Coverage'],
+        'endTokenPos': func.location.endTokenPos
+      };
+      coverage = await isolate.invokeRpcNoUpgrade('getSourceReport', params);
+    } on ServerRpcException catch (e) {
+      caughtException = true;
+      expect(e.code, equals(ServerRpcException.kInvalidParams));
+      expect(
+          e.message,
+          "getSourceReport: the 'endTokenPos' parameter requires the "
+          "\'scriptId\' parameter");
+    }
+    expect(caughtException, isTrue);
+  },
+];
+
+main(args) => runIsolateTests(args, tests, testeeConcurrent: testFunction);
diff --git a/runtime/observatory_2/tests/service_2/get_source_report_with_mixin_lib1.dart b/runtime/observatory_2/tests/service_2/get_source_report_with_mixin_lib1.dart
new file mode 100644
index 0000000..c9d5a02
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/get_source_report_with_mixin_lib1.dart
@@ -0,0 +1,15 @@
+// Copyright (c) 2019, 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.
+
+mixin A {
+  void foo() {
+    print('foo');
+  }
+}
+
+mixin B {
+  void bar() {
+    print('bar');
+  }
+}
diff --git a/runtime/observatory_2/tests/service_2/get_source_report_with_mixin_lib2.dart b/runtime/observatory_2/tests/service_2/get_source_report_with_mixin_lib2.dart
new file mode 100644
index 0000000..616df73
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/get_source_report_with_mixin_lib2.dart
@@ -0,0 +1,7 @@
+// Copyright (c) 2019, 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.
+
+import "get_source_report_with_mixin_lib1.dart";
+
+class Test1 with A {}
diff --git a/runtime/observatory_2/tests/service_2/get_source_report_with_mixin_lib3.dart b/runtime/observatory_2/tests/service_2/get_source_report_with_mixin_lib3.dart
new file mode 100644
index 0000000..c9f49a9
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/get_source_report_with_mixin_lib3.dart
@@ -0,0 +1,7 @@
+// Copyright (c) 2019, 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.
+
+import "get_source_report_with_mixin_lib1.dart";
+
+class Test2 with B {}
diff --git a/runtime/observatory_2/tests/service_2/get_source_report_with_mixin_test.dart b/runtime/observatory_2/tests/service_2/get_source_report_with_mixin_test.dart
new file mode 100644
index 0000000..281b7b7
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/get_source_report_with_mixin_test.dart
@@ -0,0 +1,98 @@
+// Copyright (c) 2019, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+import 'service_test_common.dart';
+import 'dart:developer';
+
+import "get_source_report_with_mixin_lib2.dart";
+import "get_source_report_with_mixin_lib3.dart";
+
+const String lib1Filename = "get_source_report_with_mixin_lib1";
+const String lib3Filename = "get_source_report_with_mixin_lib3";
+
+void testFunction() {
+  final Test1 test1 = new Test1();
+  test1.foo();
+  final Test2 test2 = new Test2();
+  test2.bar();
+  debugger();
+  print("done");
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  (Isolate isolate) async {
+    final stack = await isolate.getStack();
+
+    // Make sure we are in the right place.
+    expect(stack.type, equals('Stack'));
+    expect(stack['frames'].length, greaterThanOrEqualTo(1));
+    expect(stack['frames'][0].function.name, equals('testFunction'));
+
+    final List<Script> scripts = await isolate.getScripts();
+    Script foundScript;
+    for (Script script in scripts) {
+      if (script.uri.contains(lib1Filename)) {
+        foundScript = script;
+        break;
+      }
+    }
+
+    Set<int> hits;
+    {
+      // Get report for everything; then collect for lib1.
+      final Map<String, Object> params = {
+        'reports': ['Coverage'],
+      };
+      final coverage =
+          await isolate.invokeRpcNoUpgrade('getSourceReport', params);
+      hits = getHitsForLib1(coverage, lib1Filename);
+      expect(hits.length, greaterThanOrEqualTo(2));
+      print(hits);
+    }
+    {
+      // Now get report for the lib1 only.
+      final Map<String, Object> params = {
+        'reports': ['Coverage'],
+        'scriptId': foundScript.id
+      };
+      final coverage =
+          await isolate.invokeRpcNoUpgrade('getSourceReport', params);
+      final Set<int> localHits = getHitsForLib1(coverage, lib1Filename);
+      expect(localHits.length, equals(hits.length));
+      expect(hits.toList()..sort(), equals(localHits.toList()..sort()));
+      print(localHits);
+    }
+  },
+];
+
+Set<int> getHitsForLib1(Map coverage, String uriContains) {
+  final List scripts = coverage["scripts"];
+  final Set<int> scriptIdsWanted = {};
+  for (int i = 0; i < scripts.length; i++) {
+    final Map script = scripts[i];
+    final String scriptUri = script["uri"];
+    if (scriptUri.contains(uriContains)) {
+      scriptIdsWanted.add(i);
+    }
+  }
+  final List ranges = coverage["ranges"];
+  final Set<int> hits = {};
+  for (int i = 0; i < ranges.length; i++) {
+    final Map range = ranges[i];
+    if (scriptIdsWanted.contains(range["scriptIndex"])) {
+      if (range["coverage"] != null) {
+        for (int hit in range["coverage"]["hits"]) {
+          hits.add(hit);
+        }
+      }
+    }
+  }
+  return hits;
+}
+
+main(args) => runIsolateTests(args, tests, testeeConcurrent: testFunction);
diff --git a/runtime/observatory_2/tests/service_2/get_stack_rpc_test.dart b/runtime/observatory_2/tests/service_2/get_stack_rpc_test.dart
new file mode 100644
index 0000000..887cb1b
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/get_stack_rpc_test.dart
@@ -0,0 +1,92 @@
+// Copyright (c) 2015, 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.
+
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+import 'dart:async';
+import 'dart:isolate' as isolate;
+import 'dart:developer' as developer;
+
+int counter = 0;
+const stoppedAtLine = 24;
+var port = new isolate.RawReceivePort(msgHandler);
+
+// This name is used in a test below.
+void msgHandler(_) {}
+
+void periodicTask(_) {
+  port.sendPort.send(34);
+  developer.debugger(message: "fo", when: true); // We will be at the next line.
+  counter++;
+  if (counter % 300 == 0) {
+    print('counter = $counter');
+  }
+}
+
+void startTimer() {
+  new Timer.periodic(const Duration(milliseconds: 10), periodicTask);
+}
+
+var tests = <IsolateTest>[
+// Initial data fetch and verify we've hit the breakpoint.
+  (Isolate isolate) async {
+    await isolate.rootLibrary.load();
+    var script = isolate.rootLibrary.scripts[0];
+    await script.load();
+    await hasStoppedAtBreakpoint(isolate);
+    // Sanity check.
+    expect(isolate.pauseEvent is M.PauseBreakpointEvent, isTrue);
+  },
+
+// Get stack
+  (Isolate isolate) async {
+    var stack = await isolate.getStack();
+    expect(stack.type, equals('Stack'));
+
+    // Sanity check.
+    expect(stack['frames'].length, greaterThanOrEqualTo(1));
+    Script script = stack['frames'][0].location.script;
+    expect(script.tokenToLine(stack['frames'][0].location.tokenPos),
+        equals(stoppedAtLine));
+
+    // Iterate over frames.
+    var frameDepth = 0;
+    for (var frame in stack['frames']) {
+      print('checking frame $frameDepth');
+      expect(frame.type, equals('Frame'));
+      expect(frame.index, equals(frameDepth++));
+      expect(frame.code.type, equals('Code'));
+      expect(frame.function.type, equals('Function'));
+      expect(frame.location.type, equals('SourceLocation'));
+    }
+
+    // Sanity check.
+    expect(stack['messages'].length, greaterThanOrEqualTo(1));
+
+    // Iterate over messages.
+    var messageDepth = 0;
+    // objectId of message to be handled by msgHandler.
+    var msgHandlerObjectId;
+    for (var message in stack['messages']) {
+      print('checking message $messageDepth');
+      expect(message.index, equals(messageDepth++));
+      expect(message.size, greaterThanOrEqualTo(0));
+      expect(message.handler.type, equals('Function'));
+      expect(message.location.type, equals('SourceLocation'));
+      if (message.handler.name.contains('msgHandler')) {
+        msgHandlerObjectId = message.messageObjectId;
+      }
+    }
+    expect(msgHandlerObjectId, isNotNull);
+
+    // Get object.
+    Instance object = await isolate.getObject(msgHandlerObjectId);
+    expect(object.valueAsString, equals('34'));
+  }
+];
+
+main(args) => runIsolateTests(args, tests, testeeBefore: startTimer);
diff --git a/runtime/observatory_2/tests/service_2/get_supported_protocols_test.dart b/runtime/observatory_2/tests/service_2/get_supported_protocols_test.dart
new file mode 100644
index 0000000..d17583b
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/get_supported_protocols_test.dart
@@ -0,0 +1,32 @@
+// Copyright (c) 2020, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+
+import 'test_helper.dart';
+
+final tests = <VMTest>[
+  (VM vm) async {
+    final result = await vm.invokeRpcNoUpgrade('getSupportedProtocols', {});
+    expect(result['type'], equals('ProtocolList'));
+    final List<Map> protocols =
+        result['protocols'].cast<Map<String, dynamic>>();
+    expect(protocols.length, useDds ? 2 : 1);
+
+    final expectedProtocols = <String>{
+      'VM Service',
+      if (useDds) 'DDS',
+    };
+
+    for (final protocol in protocols) {
+      final protocolName = protocol['protocolName'];
+      expect(expectedProtocols.contains(protocolName), isTrue);
+      expect(protocol['major'] > 0, isTrue);
+      expect(protocol['minor'] >= 0, isTrue);
+    }
+  },
+];
+
+main(args) async => runVMTests(args, tests);
diff --git a/runtime/observatory_2/tests/service_2/get_user_level_retaining_path_rpc_test.dart b/runtime/observatory_2/tests/service_2/get_user_level_retaining_path_rpc_test.dart
new file mode 100644
index 0000000..45c8941
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/get_user_level_retaining_path_rpc_test.dart
@@ -0,0 +1,78 @@
+// Copyright (c) 2015, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+
+import 'test_helper.dart';
+
+class _TestClass {
+  _TestClass();
+  var x;
+  var y;
+}
+
+class _TestConst {
+  const _TestConst();
+}
+
+_TopLevelClosure() {}
+
+var x;
+var fn;
+
+void warmup() {
+  x = const _TestConst();
+  fn = _TopLevelClosure;
+}
+
+@pragma("vm:entry-point")
+getX() => x;
+
+@pragma("vm:entry-point")
+getFn() => fn;
+
+invoke(Isolate isolate, String selector) async {
+  Map params = {
+    'targetId': isolate.rootLibrary.id,
+    'selector': selector,
+    'argumentIds': <String>[],
+  };
+  return await isolate.invokeRpcNoUpgrade('invoke', params);
+}
+
+var tests = <IsolateTest>[
+  // Expect a simple path through variable x instead of long path filled
+  // with VM objects
+  (Isolate isolate) async {
+    var target1 = await invoke(isolate, 'getX');
+    var params = {
+      'targetId': target1['id'],
+      'limit': 100,
+    };
+    var result = await isolate.invokeRpcNoUpgrade('getRetainingPath', params);
+    expect(result['type'], equals('RetainingPath'));
+    expect(result['elements'].length, equals(2));
+    expect(
+        result['elements'][0]['value']['class']['name'], equals('_TestConst'));
+    expect(result['elements'][1]['value']['name'], equals('x'));
+  },
+
+  // Expect a simple path through variable fn instead of long path filled
+  // with VM objects
+  (Isolate isolate) async {
+    var target2 = await invoke(isolate, 'getFn');
+    var params = {
+      'targetId': target2['id'],
+      'limit': 100,
+    };
+    var result = await isolate.invokeRpcNoUpgrade('getRetainingPath', params);
+    expect(result['type'], equals('RetainingPath'));
+    expect(result['elements'].length, equals(2));
+    expect(result['elements'][0]['value']['class']['name'], equals('_Closure'));
+    expect(result['elements'][1]['value']['name'], equals('fn'));
+  }
+];
+
+main(args) async => runIsolateTests(args, tests, testeeBefore: warmup);
diff --git a/runtime/observatory_2/tests/service_2/get_version_rpc_test.dart b/runtime/observatory_2/tests/service_2/get_version_rpc_test.dart
new file mode 100644
index 0000000..468e979
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/get_version_rpc_test.dart
@@ -0,0 +1,21 @@
+// Copyright (c) 2015, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+
+import 'test_helper.dart';
+
+var tests = <VMTest>[
+  (VM vm) async {
+    var result = await vm.invokeRpcNoUpgrade('getVersion', {});
+    expect(result['type'], equals('Version'));
+    expect(result['major'], equals(3));
+    expect(result['minor'], equals(39));
+    expect(result['_privateMajor'], equals(0));
+    expect(result['_privateMinor'], equals(0));
+  },
+];
+
+main(args) async => runVMTests(args, tests);
diff --git a/runtime/observatory_2/tests/service_2/get_vm_rpc_test.dart b/runtime/observatory_2/tests/service_2/get_vm_rpc_test.dart
new file mode 100644
index 0000000..6fff547
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/get_vm_rpc_test.dart
@@ -0,0 +1,32 @@
+// Copyright (c) 2015, 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.
+// VMOptions=--vm-name=Walter
+
+import 'dart:io';
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+
+import 'test_helper.dart';
+
+var tests = <VMTest>[
+  (VM vm) async {
+    var result = await vm.invokeRpcNoUpgrade('getVM', {});
+    expect(result['type'], equals('VM'));
+    expect(result['name'], equals('Walter'));
+    expect(result['architectureBits'], isPositive);
+    expect(result['targetCPU'], isA<String>());
+    expect(result['hostCPU'], isA<String>());
+    expect(result['operatingSystem'], Platform.operatingSystem);
+    expect(result['version'], isA<String>());
+    expect(result['pid'], isA<int>());
+    expect(result['startTime'], isPositive);
+    expect(result['isolates'].length, isPositive);
+    expect(result['isolates'][0]['type'], equals('@Isolate'));
+    expect(result['isolateGroups'].length, isPositive);
+    expect(result['isolateGroups'][0]['type'], equals('@IsolateGroup'));
+  },
+];
+
+main(args) async => runVMTests(args, tests);
diff --git a/runtime/observatory_2/tests/service_2/get_vm_timeline_micros_rpc_test.dart b/runtime/observatory_2/tests/service_2/get_vm_timeline_micros_rpc_test.dart
new file mode 100644
index 0000000..7b849e6
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/get_vm_timeline_micros_rpc_test.dart
@@ -0,0 +1,18 @@
+// Copyright (c) 2019, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+
+import 'test_helper.dart';
+
+var tests = <VMTest>[
+  (VM vm) async {
+    var result = await vm.invokeRpcNoUpgrade('getVMTimelineMicros', {});
+    expect(result['type'], equals('Timestamp'));
+    expect(result['timestamp'], isPositive);
+  },
+];
+
+main(args) async => runVMTests(args, tests);
diff --git a/runtime/observatory_2/tests/service_2/get_vm_timeline_rpc_test.dart b/runtime/observatory_2/tests/service_2/get_vm_timeline_rpc_test.dart
new file mode 100644
index 0000000..8dc5899
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/get_vm_timeline_rpc_test.dart
@@ -0,0 +1,193 @@
+// Copyright (c) 2015, 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.
+
+import 'dart:developer';
+import 'dart:io';
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+
+import 'test_helper.dart';
+
+primeTimeline() {
+  Timeline.startSync('apple');
+  Timeline.instantSync('ISYNC', arguments: {'fruit': 'banana'});
+  Timeline.finishSync();
+  TimelineTask parentTask = TimelineTask.withTaskId(42);
+  TimelineTask task = TimelineTask(parent: parentTask, filterKey: 'testFilter');
+  task.start('TASK1', arguments: {'task1-start-key': 'task1-start-value'});
+  task.instant('ITASK',
+      arguments: {'task1-instant-key': 'task1-instant-value'});
+  task.finish(arguments: {'task1-finish-key': 'task1-finish-value'});
+
+  Flow flow = Flow.begin(id: 123);
+  Timeline.startSync('peach', flow: flow);
+  Timeline.finishSync();
+  Timeline.startSync('watermelon', flow: Flow.step(flow.id));
+  Timeline.finishSync();
+  Timeline.startSync('pear', flow: Flow.end(flow.id));
+  Timeline.finishSync();
+}
+
+List filterForDartEvents(List events) {
+  return events.where((event) => event['cat'] == 'Dart').toList();
+}
+
+bool mapContains(Map map, Map submap) {
+  for (var key in submap.keys) {
+    if (map[key] != submap[key]) {
+      return false;
+    }
+  }
+  return true;
+}
+
+bool eventsContains(List events, String phase, String name, [Map arguments]) {
+  for (Map event in events) {
+    if ((event['ph'] == phase) && (event['name'] == name)) {
+      if (arguments == null) {
+        return true;
+      } else if (mapContains(event['args'], arguments)) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+int timeOrigin(List events) {
+  if (events.length == 0) {
+    return 0;
+  }
+  int smallest = events[0]['ts'];
+  for (var i = 0; i < events.length; i++) {
+    Map event = events[i];
+    if (event['ts'] < smallest) {
+      smallest = event['ts'];
+    }
+  }
+  return smallest;
+}
+
+int timeDuration(List events, int timeOrigin) {
+  if (events.length == 0) {
+    return 0;
+  }
+  int biggestDuration = events[0]['ts'] - timeOrigin;
+  for (var i = 0; i < events.length; i++) {
+    Map event = events[i];
+    int duration = event['ts'] - timeOrigin;
+    if (duration > biggestDuration) {
+      biggestDuration = duration;
+    }
+  }
+  return biggestDuration;
+}
+
+void allEventsHaveIsolateNumber(List events) {
+  for (Map event in events) {
+    if (event['ph'] == 'M') {
+      // Skip meta-data events.
+      continue;
+    }
+    if (event['name'] == 'Runnable' && event['ph'] == 'i') {
+      // Skip Runnable events which don't have an isolate.
+      continue;
+    }
+    if (event['cat'] == 'VM') {
+      // Skip VM category events which don't have an isolate.
+      continue;
+    }
+    if (event['cat'] == 'API') {
+      // Skip API category events which sometimes don't have an isolate.
+      continue;
+    }
+    if (event['cat'] == 'Embedder' &&
+        (event['name'] == 'DFE::ReadScript' ||
+            event['name'] == 'CreateIsolateGroupAndSetupHelper')) {
+      continue;
+    }
+    Map arguments = event['args'];
+    expect(arguments, isA<Map>());
+    expect(arguments['isolateGroupId'], isA<String>());
+    if (event['cat'] != 'GC') {
+      expect(arguments['isolateId'], isA<String>());
+    }
+  }
+}
+
+var tests = <VMTest>[
+  (VM vm) async {
+    Map result = await vm.invokeRpcNoUpgrade('getVMTimeline', {});
+    expect(result['type'], equals('Timeline'));
+    expect(result['traceEvents'], isA<List>());
+    final int numEvents = result['traceEvents'].length;
+    List dartEvents = filterForDartEvents(result['traceEvents']);
+    expect(dartEvents.length, greaterThanOrEqualTo(11));
+    allEventsHaveIsolateNumber(dartEvents);
+    allEventsHaveIsolateNumber(result['traceEvents']);
+    expect(
+        eventsContains(dartEvents, 'i', 'ISYNC', {'fruit': 'banana'}), isTrue);
+    expect(eventsContains(dartEvents, 'B', 'apple'), isTrue);
+    expect(eventsContains(dartEvents, 'E', 'apple'), isTrue);
+    expect(
+        eventsContains(dartEvents, 'b', 'TASK1', {
+          'filterKey': 'testFilter',
+          'task1-start-key': 'task1-start-value',
+          'parentId': 42.toRadixString(16)
+        }),
+        isTrue);
+    expect(
+        eventsContains(dartEvents, 'e', 'TASK1', {
+          'filterKey': 'testFilter',
+          'task1-finish-key': 'task1-finish-value',
+        }),
+        isTrue);
+    expect(
+        eventsContains(dartEvents, 'n', 'ITASK', {
+          'filterKey': 'testFilter',
+          'task1-instant-key': 'task1-instant-value',
+        }),
+        isTrue);
+    expect(eventsContains(dartEvents, 'q', 'ITASK'), isFalse);
+    expect(eventsContains(dartEvents, 'B', 'peach'), isTrue);
+    expect(eventsContains(dartEvents, 'E', 'peach'), isTrue);
+    expect(eventsContains(dartEvents, 'B', 'watermelon'), isTrue);
+    expect(eventsContains(dartEvents, 'E', 'watermelon'), isTrue);
+    expect(eventsContains(dartEvents, 'B', 'pear'), isTrue);
+    expect(eventsContains(dartEvents, 'E', 'pear'), isTrue);
+    expect(eventsContains(dartEvents, 's', '123'), isTrue);
+    expect(eventsContains(dartEvents, 't', '123'), isTrue);
+    expect(eventsContains(dartEvents, 'f', '123'), isTrue);
+    // Calculate the time Window of Dart events.
+    int origin = timeOrigin(dartEvents);
+    int extent = timeDuration(dartEvents, origin);
+    // Query for the timeline with the time window for Dart events.
+    result = await vm.invokeRpcNoUpgrade('getVMTimeline',
+        {'timeOriginMicros': origin, 'timeExtentMicros': extent});
+    // Verify that we received fewer events than before.
+    expect(result['traceEvents'].length, lessThan(numEvents));
+    // Verify that we have the same number of Dart events.
+    List dartEvents2 = filterForDartEvents(result['traceEvents']);
+    expect(dartEvents2.length, dartEvents.length);
+  },
+];
+
+main(List<String> args) async {
+  // Running the subprocesses of this particular test in opt counter mode
+  // will cause it to be slow and cause many compilations.
+  //
+  // Together with "--complete-timeline" this will create a huge number of
+  // timeline events which can, on ia32, cause the process to hit OOM.
+  //
+  // So we filter out that particular argument.
+  final executableArgs = Platform.executableArguments
+      .where((String arg) => !arg.contains('optimization-counter-threshold'))
+      .toList();
+
+  await runVMTests(args, tests,
+      testeeBefore: primeTimeline,
+      extraArgs: ['--complete-timeline'],
+      executableArgs: executableArgs);
+}
diff --git a/runtime/observatory_2/tests/service_2/get_zone_memory_info_rpc_test.dart b/runtime/observatory_2/tests/service_2/get_zone_memory_info_rpc_test.dart
new file mode 100644
index 0000000..dd1d671
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/get_zone_memory_info_rpc_test.dart
@@ -0,0 +1,31 @@
+// Copyright (c) 2016, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+
+import 'test_helper.dart';
+
+var tests = <VMTest>[
+  (VM vm) async {
+    // Just iterate over all the isolates to confirm they have
+    // the correct fields needed to examine zone memory usage.
+    for (Isolate isolate in new List.from(vm.isolates)) {
+      await isolate.reload();
+      expect(isolate.zoneHighWatermark, isA<int>());
+      expect(isolate.threads, isNotNull);
+      List<Thread> threads = isolate.threads;
+
+      for (Thread thread in threads) {
+        expect(thread.type, equals('_Thread'));
+        expect(thread.id, isNotNull);
+        expect(thread.kind, isNotNull);
+        expect(thread.zoneHighWatermark, isA<int>());
+        expect(thread.zoneCapacity, isA<int>());
+      }
+    }
+  },
+];
+
+main(args) async => runVMTests(args, tests);
diff --git a/runtime/observatory_2/tests/service_2/http_auth_get_isolate_rpc_test.dart b/runtime/observatory_2/tests/service_2/http_auth_get_isolate_rpc_test.dart
new file mode 100644
index 0000000..261fa26
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/http_auth_get_isolate_rpc_test.dart
@@ -0,0 +1,14 @@
+// Copyright (c) 2019, 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.
+
+import 'http_get_isolate_rpc_common.dart';
+import 'test_helper.dart';
+
+main(args) {
+  runIsolateTests(args, tests,
+      testeeBefore: testeeBefore,
+      // the testee is responsible for starting the
+      // web server.
+      testeeControlsServer: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/http_auth_get_vm_rpc_test.dart b/runtime/observatory_2/tests/service_2/http_auth_get_vm_rpc_test.dart
new file mode 100644
index 0000000..5484c5e
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/http_auth_get_vm_rpc_test.dart
@@ -0,0 +1,14 @@
+// Copyright (c) 2019, 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.
+
+import 'http_get_vm_rpc_common.dart';
+import 'test_helper.dart';
+
+main(args) {
+  runIsolateTests(args, tests,
+      testeeBefore: testeeBefore,
+      // the testee is responsible for starting the
+      // web server.
+      testeeControlsServer: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/http_enable_timeline_logging_service_test.dart b/runtime/observatory_2/tests/service_2/http_enable_timeline_logging_service_test.dart
new file mode 100644
index 0000000..1c1b0b0
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/http_enable_timeline_logging_service_test.dart
@@ -0,0 +1,65 @@
+// Copyright (c) 2019, 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.
+
+import 'dart:async';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+const String kSetHttpEnableTimelineLogging =
+    'ext.dart.io.setHttpEnableTimelineLogging';
+const String kGetHttpEnableTimelineLogging =
+    'ext.dart.io.getHttpEnableTimelineLogging';
+Future<void> setup() async {}
+
+var tests = <IsolateTest>[
+  (Isolate isolate) async {
+    await isolate.load();
+    // Ensure all HTTP service extensions are registered.
+    expect(isolate.extensionRPCs.length, greaterThanOrEqualTo(2));
+    expect(
+        isolate.extensionRPCs.contains(kGetHttpEnableTimelineLogging), isTrue);
+    expect(
+        isolate.extensionRPCs.contains(kSetHttpEnableTimelineLogging), isTrue);
+  },
+  (Isolate isolate) async {
+    await isolate.load();
+    var response =
+        await isolate.invokeRpcNoUpgrade(kGetHttpEnableTimelineLogging, {});
+    expect(response['type'], 'HttpTimelineLoggingState');
+    expect(response['enabled'], false);
+
+    response = await isolate
+        .invokeRpcNoUpgrade(kSetHttpEnableTimelineLogging, {'enable': true});
+    expect(response['type'], 'Success');
+
+    response =
+        await isolate.invokeRpcNoUpgrade(kGetHttpEnableTimelineLogging, {});
+    expect(response['type'], 'HttpTimelineLoggingState');
+    expect(response['enabled'], true);
+
+    response = await isolate
+        .invokeRpcNoUpgrade(kSetHttpEnableTimelineLogging, {'enable': false});
+    expect(response['type'], 'Success');
+
+    response =
+        await isolate.invokeRpcNoUpgrade(kGetHttpEnableTimelineLogging, {});
+    expect(response['type'], 'HttpTimelineLoggingState');
+    expect(response['enabled'], false);
+  },
+  (Isolate isolate) async {
+    // Bad argument.
+    try {
+      await isolate
+          .invokeRpcNoUpgrade(kSetHttpEnableTimelineLogging, {'enable': 'foo'});
+    } catch (e) {/* expected */}
+    // Missing argument.
+    try {
+      await isolate.invokeRpcNoUpgrade(kSetHttpEnableTimelineLogging, {});
+    } catch (e) {/* expected */}
+  },
+];
+
+main(args) async => runIsolateTests(args, tests, testeeBefore: setup);
diff --git a/runtime/observatory_2/tests/service_2/http_get_isolate_group_rpc_common.dart b/runtime/observatory_2/tests/service_2/http_get_isolate_group_rpc_common.dart
new file mode 100644
index 0000000..84418d2
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/http_get_isolate_group_rpc_common.dart
@@ -0,0 +1,83 @@
+// Copyright (c) 2019, 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.
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:developer';
+import 'dart:io' as io;
+import 'package:expect/expect.dart';
+import 'package:observatory_2/service_io.dart' as S;
+import 'test_helper.dart';
+
+Future<String> getIsolateGroupId(
+    io.HttpClient httpClient, Uri serverUri) async {
+  // Build the request.
+  final pathSegments = <String>[]..addAll(serverUri.pathSegments);
+  const method = 'getVM';
+  if (pathSegments.isNotEmpty) {
+    pathSegments[pathSegments.length - 1] = method;
+  } else {
+    pathSegments.add(method);
+  }
+  final requestUri = serverUri.replace(pathSegments: pathSegments);
+  final request = await httpClient.getUrl(requestUri);
+  final Map response = await (await request.close())
+      .cast<List<int>>()
+      .transform(utf8.decoder)
+      .transform(json.decoder)
+      .first;
+  final result = response['result'];
+  return result['isolateGroups'][0]['id'];
+}
+
+Future<Null> testeeBefore() async {
+  print('testee before');
+  print(await Service.getInfo());
+  // Start the web server.
+  final ServiceProtocolInfo info = await Service.controlWebServer(enable: true);
+  Expect.isNotNull(info.serverUri);
+  final httpClient = new io.HttpClient();
+
+  // Build the request.
+  final params = <String, String>{
+    'isolateGroupId': await getIsolateGroupId(httpClient, info.serverUri),
+  };
+
+  const method = 'getIsolateGroup';
+  final pathSegments = <String>[]..addAll(info.serverUri.pathSegments);
+  if (pathSegments.isNotEmpty) {
+    pathSegments[pathSegments.length - 1] = method;
+  } else {
+    pathSegments.add(method);
+  }
+  final requestUri = info.serverUri
+      .replace(pathSegments: pathSegments, queryParameters: params);
+
+  try {
+    final request = await httpClient.getUrl(requestUri);
+    final response = await request.close();
+    final Map jsonResponse = await response
+        .cast<List<int>>()
+        .transform(utf8.decoder)
+        .transform(json.decoder)
+        .first;
+    final result = jsonResponse['result'];
+    Expect.equals(result['type'], 'IsolateGroup');
+    Expect.isTrue(result['id'].startsWith('isolateGroups/'));
+    Expect.type<String>(result['number']);
+    Expect.isFalse(result['isSystemIsolateGroup']);
+    Expect.isTrue(result['isolates'].length > 0);
+    Expect.equals(result['isolates'][0]['type'], '@Isolate');
+  } catch (e) {
+    Expect.fail('invalid request: $e');
+  }
+}
+
+var tests = <IsolateTest>[
+  (S.Isolate isolate) async {
+    await isolate.reload();
+    // Just getting here means that the testee enabled the service protocol
+    // web server.
+  }
+];
diff --git a/runtime/observatory_2/tests/service_2/http_get_isolate_group_rpc_test.dart b/runtime/observatory_2/tests/service_2/http_get_isolate_group_rpc_test.dart
new file mode 100644
index 0000000..eb382b1
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/http_get_isolate_group_rpc_test.dart
@@ -0,0 +1,14 @@
+// Copyright (c) 2019, 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.
+
+import 'http_get_isolate_group_rpc_common.dart';
+import 'test_helper.dart';
+
+main(args) {
+  runIsolateTests(args, tests,
+      testeeBefore: testeeBefore,
+      // the testee is responsible for starting the
+      // web server.
+      testeeControlsServer: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/http_get_isolate_rpc_common.dart b/runtime/observatory_2/tests/service_2/http_get_isolate_rpc_common.dart
new file mode 100644
index 0000000..7f5c869
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/http_get_isolate_rpc_common.dart
@@ -0,0 +1,93 @@
+// Copyright (c) 2019, 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.
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:developer';
+import 'dart:io' as io;
+import 'package:expect/expect.dart';
+import 'package:observatory_2/service_io.dart' as S;
+import 'test_helper.dart';
+
+Future<String> getIsolateId(io.HttpClient httpClient, Uri serverUri) async {
+  // Build the request.
+  final pathSegments = <String>[]..addAll(serverUri.pathSegments);
+  String method = 'getVM';
+  if (pathSegments.isNotEmpty) {
+    pathSegments[pathSegments.length - 1] = method;
+  } else {
+    pathSegments.add(method);
+  }
+  final requestUri = serverUri.replace(pathSegments: pathSegments);
+  var request = await httpClient.getUrl(requestUri);
+  Map response = await (await request.close())
+      .cast<List<int>>()
+      .transform(utf8.decoder)
+      .transform(json.decoder)
+      .first;
+  Map result = response['result'];
+  return result['isolates'][0]['id'];
+}
+
+Future<Null> testeeBefore() async {
+  print('testee before');
+  print(await Service.getInfo());
+  // Start the web server.
+  ServiceProtocolInfo info = await Service.controlWebServer(enable: true);
+  Expect.isNotNull(info.serverUri);
+  var httpClient = new io.HttpClient();
+
+  // Build the request.
+  final params = <String, String>{
+    'isolateId': await getIsolateId(httpClient, info.serverUri),
+  };
+
+  String method = 'getIsolate';
+  final pathSegments = <String>[]..addAll(info.serverUri.pathSegments);
+  if (pathSegments.isNotEmpty) {
+    pathSegments[pathSegments.length - 1] = method;
+  } else {
+    pathSegments.add(method);
+  }
+  final requestUri = info.serverUri
+      .replace(pathSegments: pathSegments, queryParameters: params);
+
+  try {
+    var request = await httpClient.getUrl(requestUri);
+    Map response = await (await request.close())
+        .cast<List<int>>()
+        .transform(utf8.decoder)
+        .transform(json.decoder)
+        .first;
+    Map result = response['result'];
+    Expect.equals(result['type'], 'Isolate');
+    Expect.isTrue(result['id'].startsWith('isolates/'));
+    Expect.type<String>(result['number']);
+    Expect.equals(result['_originNumber'], result['number']);
+    Expect.isTrue(result['startTime'] > 0);
+    Expect.isTrue(result['livePorts'] > 0);
+    Expect.isFalse(result['pauseOnExit']);
+    Expect.equals(result['pauseEvent']['type'], 'Event');
+    Expect.isNull(result['error']);
+    Expect.isTrue(result['_numZoneHandles'] > 0);
+    Expect.isTrue(result['_numScopedHandles'] > 0);
+    Expect.equals(result['rootLib']['type'], '@Library');
+    Expect.isTrue(result['libraries'].length > 0);
+    Expect.equals(result['libraries'][0]['type'], '@Library');
+    Expect.equals(result['breakpoints'].length, 0);
+    Expect.equals(result['_heaps']['new']['type'], 'HeapSpace');
+    Expect.equals(result['_heaps']['old']['type'], 'HeapSpace');
+    Expect.equals(result['isolate_group']['type'], '@IsolateGroup');
+  } catch (e) {
+    Expect.fail('invalid request: $e');
+  }
+}
+
+var tests = <IsolateTest>[
+  (S.Isolate isolate) async {
+    await isolate.reload();
+    // Just getting here means that the testee enabled the service protocol
+    // web server.
+  }
+];
diff --git a/runtime/observatory_2/tests/service_2/http_get_isolate_rpc_test.dart b/runtime/observatory_2/tests/service_2/http_get_isolate_rpc_test.dart
new file mode 100644
index 0000000..261fa26
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/http_get_isolate_rpc_test.dart
@@ -0,0 +1,14 @@
+// Copyright (c) 2019, 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.
+
+import 'http_get_isolate_rpc_common.dart';
+import 'test_helper.dart';
+
+main(args) {
+  runIsolateTests(args, tests,
+      testeeBefore: testeeBefore,
+      // the testee is responsible for starting the
+      // web server.
+      testeeControlsServer: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/http_get_vm_rpc_common.dart b/runtime/observatory_2/tests/service_2/http_get_vm_rpc_common.dart
new file mode 100644
index 0000000..f43b121
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/http_get_vm_rpc_common.dart
@@ -0,0 +1,62 @@
+// Copyright (c) 2019, 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.
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:developer';
+import 'dart:io' as io;
+import 'package:expect/expect.dart';
+import 'package:observatory_2/service_io.dart' as S;
+import 'test_helper.dart';
+
+Future<Null> testeeBefore() async {
+  print('testee before');
+  print(await Service.getInfo());
+  // Start the web server.
+  ServiceProtocolInfo info = await Service.controlWebServer(enable: true);
+  Expect.isNotNull(info.serverUri);
+  var httpClient = new io.HttpClient();
+
+  // Build the request.
+  final pathSegments = <String>[]..addAll(info.serverUri.pathSegments);
+  String method = 'getVM';
+  if (pathSegments.isNotEmpty) {
+    pathSegments[pathSegments.length - 1] = method;
+  } else {
+    pathSegments.add(method);
+  }
+  final requestUri = info.serverUri.replace(pathSegments: pathSegments);
+
+  try {
+    var request = await httpClient.getUrl(requestUri);
+    Map response = await (await request.close())
+        .cast<List<int>>()
+        .transform(utf8.decoder)
+        .transform(json.decoder)
+        .first;
+    Map result = response['result'];
+    Expect.equals(result['type'], 'VM');
+    Expect.equals(result['name'], 'vm');
+    Expect.isTrue(result['architectureBits'] > 0);
+    Expect.type<String>(result['targetCPU']);
+    Expect.type<String>(result['hostCPU']);
+    Expect.type<String>(result['version']);
+    Expect.type<int>(result['pid']);
+    Expect.isTrue(result['startTime'] > 0);
+    Expect.isTrue(result['isolates'].length > 0);
+    Expect.equals(result['isolates'][0]['type'], '@Isolate');
+    Expect.isTrue(result['isolateGroups'].length > 0);
+    Expect.equals(result['isolateGroups'][0]['type'], '@IsolateGroup');
+  } catch (e) {
+    Expect.fail('invalid request: $e');
+  }
+}
+
+var tests = <IsolateTest>[
+  (S.Isolate isolate) async {
+    await isolate.reload();
+    // Just getting here means that the testee enabled the service protocol
+    // web server.
+  }
+];
diff --git a/runtime/observatory_2/tests/service_2/http_get_vm_rpc_test.dart b/runtime/observatory_2/tests/service_2/http_get_vm_rpc_test.dart
new file mode 100644
index 0000000..5484c5e
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/http_get_vm_rpc_test.dart
@@ -0,0 +1,14 @@
+// Copyright (c) 2019, 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.
+
+import 'http_get_vm_rpc_common.dart';
+import 'test_helper.dart';
+
+main(args) {
+  runIsolateTests(args, tests,
+      testeeBefore: testeeBefore,
+      // the testee is responsible for starting the
+      // web server.
+      testeeControlsServer: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/implicit_getter_setter_test.dart b/runtime/observatory_2/tests/service_2/implicit_getter_setter_test.dart
new file mode 100644
index 0000000..39115bc
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/implicit_getter_setter_test.dart
@@ -0,0 +1,68 @@
+// Copyright (c) 2015, 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.
+
+library implicit_getter_setter_test;
+
+import 'dart:async';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+
+class A {
+  double field = 0.0;
+}
+
+script() {
+  for (int i = 0; i < 10; i++) {
+    new A();
+  }
+}
+
+Future testGetter(Isolate isolate) async {
+  Library rootLibrary = await isolate.rootLibrary.load();
+  expect(rootLibrary.classes.length, equals(1));
+  Class classA = await rootLibrary.classes[0].load();
+  expect(classA.name, equals('A'));
+  // Find getter.
+  ServiceFunction getterFunc;
+  for (ServiceFunction function in classA.functions) {
+    if (function.name == 'field') {
+      getterFunc = function;
+      break;
+    }
+  }
+  expect(getterFunc, isNotNull);
+  await getterFunc.load();
+  Field field = await getterFunc.field.load();
+  expect(field, isNotNull);
+  expect(field.name, equals('field'));
+  Class classDouble = await field.guardClass.load();
+  expect(classDouble.name, equals('_Double'));
+}
+
+Future testSetter(Isolate isolate) async {
+  Library rootLibrary = await isolate.rootLibrary.load();
+  expect(rootLibrary.classes.length, equals(1));
+  Class classA = await rootLibrary.classes[0].load();
+  expect(classA.name, equals('A'));
+  // Find setter.
+  ServiceFunction setterFunc;
+  for (ServiceFunction function in classA.functions) {
+    if (function.name == 'field=') {
+      setterFunc = function;
+      break;
+    }
+  }
+  expect(setterFunc, isNotNull);
+  await setterFunc.load();
+  Field field = await setterFunc.field.load();
+  expect(field, isNotNull);
+  expect(field.name, equals('field'));
+  Class classDouble = await field.guardClass.load();
+  expect(classDouble.name, equals('_Double'));
+}
+
+var tests = <IsolateTest>[testGetter, testSetter];
+
+main(args) => runIsolateTests(args, tests, testeeBefore: script);
diff --git a/runtime/observatory_2/tests/service_2/inbound_references_test.dart b/runtime/observatory_2/tests/service_2/inbound_references_test.dart
new file mode 100644
index 0000000..6053042
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/inbound_references_test.dart
@@ -0,0 +1,56 @@
+// Copyright (c) 2014, 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.
+
+library inbound_references_test;
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+
+class Node {
+  // Make sure this field is not removed by the tree shaker.
+  @pragma("vm:entry-point")
+  var edge;
+}
+
+class Edge {}
+
+var n, e, array;
+
+void script() {
+  n = new Node();
+  e = new Edge();
+  n.edge = e;
+  array = new List(2);
+  array[0] = n;
+  array[1] = e;
+}
+
+var tests = <IsolateTest>[
+  (Isolate isolate) async {
+    Library lib = await isolate.rootLibrary.load();
+    Field field = lib.variables.where((v) => v.name == 'e').single;
+    await field.load();
+    Instance e = field.staticValue;
+    ServiceMap response = await isolate.getInboundReferences(e, 100);
+    List references = response['references'];
+    hasReferenceSuchThat(predicate) {
+      expect(references.any(predicate), isTrue);
+    }
+
+    // Assert e is referenced by at least n, array, and the top-level
+    // field e.
+    hasReferenceSuchThat((r) =>
+        r['parentField'] != null &&
+        r['parentField'].name == 'edge' &&
+        r['source'].isInstance &&
+        r['source'].clazz.name == 'Node');
+    hasReferenceSuchThat(
+        (r) => r['parentListIndex'] == 1 && r['source'].isList);
+    hasReferenceSuchThat(
+        (r) => r['source'] is Field && r['source'].name == 'e');
+  }
+];
+
+main(args) => runIsolateTests(args, tests, testeeBefore: script);
diff --git a/runtime/observatory_2/tests/service_2/instance_field_order_rpc_test.dart b/runtime/observatory_2/tests/service_2/instance_field_order_rpc_test.dart
new file mode 100644
index 0000000..6bc8bdc
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/instance_field_order_rpc_test.dart
@@ -0,0 +1,62 @@
+// Copyright (c) 2016, 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.
+
+library get_object_rpc_test;
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+
+class Super {
+  // Make sure these fields are not removed by the tree shaker.
+  @pragma("vm:entry-point")
+  var z = 1;
+  @pragma("vm:entry-point")
+  var y = 2;
+}
+
+class Sub extends Super {
+  @pragma("vm:entry-point")
+  var y = 3;
+  @pragma("vm:entry-point")
+  var x = 4;
+}
+
+@pragma("vm:entry-point")
+getSub() => new Sub();
+
+invoke(Isolate isolate, String selector) async {
+  Map params = {
+    'targetId': isolate.rootLibrary.id,
+    'selector': selector,
+    'argumentIds': <String>[],
+  };
+  return await isolate.invokeRpcNoUpgrade('invoke', params);
+}
+
+var tests = <IsolateTest>[
+  (Isolate isolate) async {
+    // Call eval to get a Dart list.
+    var evalResult = await invoke(isolate, 'getSub');
+    var params = {
+      'objectId': evalResult['id'],
+    };
+    var result = await isolate.invokeRpcNoUpgrade('getObject', params);
+    print(result);
+    expect(result['type'], equals('Instance'));
+    expect(result['kind'], equals('PlainInstance'));
+    expect(result['class']['name'], equals('Sub'));
+    expect(result['size'], isPositive);
+    expect(result['fields'][0]['decl']['name'], 'z');
+    expect(result['fields'][0]['value']['valueAsString'], '1');
+    expect(result['fields'][1]['decl']['name'], 'y');
+    expect(result['fields'][1]['value']['valueAsString'], '2');
+    expect(result['fields'][2]['decl']['name'], 'y');
+    expect(result['fields'][2]['value']['valueAsString'], '3');
+    expect(result['fields'][3]['decl']['name'], 'x');
+    expect(result['fields'][3]['value']['valueAsString'], '4');
+  },
+];
+
+main(args) async => runIsolateTests(args, tests);
diff --git a/runtime/observatory_2/tests/service_2/invoke_skip_breakpoint.dart b/runtime/observatory_2/tests/service_2/invoke_skip_breakpoint.dart
new file mode 100644
index 0000000..100f9d7
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/invoke_skip_breakpoint.dart
@@ -0,0 +1,50 @@
+// Copyright (c) 2019, 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.
+
+import 'dart:developer';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+const int LINE_A = 21;
+const int LINE_B = 16;
+
+bar() {
+  print('bar');
+  return 'bar';
+}
+
+testMain() {
+  debugger();
+  bar();
+  print("Done");
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_A),
+  setBreakpointAtLine(LINE_B),
+  (Isolate isolate) async {
+    Library lib = isolate.rootLibrary;
+    await lib.load();
+
+    dynamic result = await isolate.invokeRpc("invoke", {
+      "targetId": lib.id,
+      "selector": "bar",
+      "argumentIds": [],
+      "disableBreakpoints": true
+    });
+    print(result);
+    expect(result.valueAsString, equals('bar'));
+  },
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_A),
+  resumeIsolate,
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_B),
+  resumeIsolate,
+];
+
+main(args) => runIsolateTests(args, tests, testeeConcurrent: testMain);
diff --git a/runtime/observatory_2/tests/service_2/invoke_test.dart b/runtime/observatory_2/tests/service_2/invoke_test.dart
new file mode 100644
index 0000000..e888a65
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/invoke_test.dart
@@ -0,0 +1,128 @@
+// Copyright (c) 2018, 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.
+
+import 'dart:developer';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+libraryFunction() => "foobar1";
+
+class Klass {
+  @pragma('vm:entry-point')
+  static classFunction(x) => "foobar2" + x;
+  @pragma('vm:entry-point')
+  instanceFunction(x, y) => "foobar3" + x + y;
+}
+
+var instance;
+
+@pragma('vm:entry-point')
+var apple;
+@pragma('vm:entry-point')
+var banana;
+
+void testFunction() {
+  instance = new Klass();
+  apple = "apple";
+  banana = "banana";
+  debugger();
+}
+
+@pragma('vm:entry-point')
+void foo() {
+  print('foobar');
+}
+
+@pragma('vm:entry-point')
+void invokeFunction(Function func) {
+  func();
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  (Isolate isolate) async {
+    Library lib = isolate.rootLibrary;
+    await lib.load();
+    final fooFunc = lib.functions.singleWhere((func) => func.name == "foo");
+    Class cls = lib.classes.singleWhere((cls) => cls.name == "Klass");
+    Field field =
+        lib.variables.singleWhere((field) => field.name == "instance");
+    await field.load();
+    Instance instance = field.staticValue;
+    field = lib.variables.singleWhere((field) => field.name == "apple");
+    await field.load();
+    Instance apple = field.staticValue;
+    field = lib.variables.singleWhere((field) => field.name == "banana");
+    await field.load();
+    Instance banana = field.staticValue;
+
+    dynamic result = await isolate.invokeRpc("invoke",
+        {"targetId": lib.id, "selector": "libraryFunction", "argumentIds": []});
+    print(result);
+    expect(result.valueAsString, equals('foobar1'));
+
+    result = await isolate.invokeRpc("invoke", {
+      "targetId": cls.id,
+      "selector": "classFunction",
+      "argumentIds": [apple.id]
+    });
+    print(result);
+    expect(result.valueAsString, equals('foobar2apple'));
+
+    result = await isolate.invokeRpc("invoke", {
+      "targetId": instance.id,
+      "selector": "instanceFunction",
+      "argumentIds": [apple.id, banana.id]
+    });
+    print(result);
+    expect(result.valueAsString, equals('foobar3applebanana'));
+
+    // Wrong arity.
+    await expectError(
+        () => isolate.invokeRpc("invoke", {
+              "targetId": instance.id,
+              "selector": "instanceFunction",
+              "argumentIds": [apple.id]
+            }),
+        ServerRpcException.kExpressionCompilationError);
+
+    // Non-instance argument.
+    await expectError(
+        () => isolate.invokeRpc("invoke", {
+              "targetId": lib.id,
+              "selector": "invokeFunction",
+              "argumentIds": [fooFunc.id]
+            }),
+        ServerRpcException.kInvalidParams);
+
+    // No such target.
+    await expectError(
+        () => isolate.invokeRpc("invoke", {
+              "targetId": instance.id,
+              "selector": "functionDoesNotExist",
+              "argumentIds": [apple.id]
+            }),
+        ServerRpcException.kExpressionCompilationError);
+  },
+  resumeIsolate,
+];
+
+expectError(func, code) async {
+  bool gotException = false;
+  dynamic result;
+  try {
+    result = await func();
+    expect(result.type, equals('Error')); // dart1 semantics
+  } on ServerRpcException catch (e) {
+    expect(e.code, code);
+    gotException = true;
+  }
+  if (result?.type != 'Error') {
+    expect(gotException, true); // dart2 semantics
+  }
+}
+
+main(args) => runIsolateTests(args, tests, testeeConcurrent: testFunction);
diff --git a/runtime/observatory_2/tests/service_2/isolate_lifecycle_test.dart b/runtime/observatory_2/tests/service_2/isolate_lifecycle_test.dart
new file mode 100644
index 0000000..7d38631
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/isolate_lifecycle_test.dart
@@ -0,0 +1,132 @@
+// Copyright (c) 2015, 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.
+
+import 'dart:async';
+import 'dart:developer';
+import 'dart:isolate' as I;
+
+import 'package:observatory_2/service_io.dart';
+import 'package:observatory_2/models.dart' as M;
+import 'package:test/test.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+final spawnCount = 4;
+final resumeCount = spawnCount ~/ 2;
+final isolates = [];
+
+void spawnEntry(int i) {}
+
+Future during() async {
+  debugger();
+  // Spawn spawnCount long lived isolates.
+  for (var i = 0; i < spawnCount; i++) {
+    var isolate = await I.Isolate.spawn(spawnEntry, i);
+    isolates.add(isolate);
+  }
+  print('spawned all isolates');
+}
+
+int numPaused(vm) {
+  int paused = 0;
+  for (var isolate in vm.isolates) {
+    if (isolate.paused && isolate.pauseEvent is M.PauseExitEvent) {
+      paused++;
+    }
+  }
+  return paused;
+}
+
+var tests = <VMTest>[
+  (VM vm) async {
+    expect(vm.isolates.length, 1);
+    await hasStoppedAtBreakpoint(vm.isolates[0]);
+  },
+  (VM vm) async {
+    Completer completer = new Completer();
+    var stream = await vm.getEventStream(VM.kIsolateStream);
+    var subscription;
+    int startCount = 0;
+    int runnableCount = 0;
+    subscription = stream.listen((ServiceEvent event) {
+      if (event.kind == ServiceEvent.kIsolateStart) {
+        startCount++;
+      }
+      if (event.kind == ServiceEvent.kIsolateRunnable) {
+        runnableCount++;
+      }
+      if (runnableCount == spawnCount) {
+        subscription.cancel();
+        completer.complete(null);
+      }
+    });
+    expect(vm.isolates.length, 1);
+    vm.isolates[0].resume();
+    await completer.future;
+    expect(startCount, spawnCount);
+    expect(runnableCount, spawnCount);
+    expect(vm.isolates.length, spawnCount + 1);
+  },
+  (VM vm) async {
+    // Load each isolate.
+    for (var isolate in vm.isolates) {
+      await isolate.load();
+    }
+  },
+  (VM vm) async {
+    Completer completer = new Completer();
+    var stream = await vm.getEventStream(VM.kDebugStream);
+    if (numPaused(vm) < (spawnCount + 1)) {
+      var subscription;
+      subscription = stream.listen((ServiceEvent event) {
+        if (event.kind == ServiceEvent.kPauseExit) {
+          if (numPaused(vm) == (spawnCount + 1)) {
+            subscription.cancel();
+            completer.complete(null);
+          }
+        }
+      });
+      await completer.future;
+    }
+    expect(numPaused(vm), spawnCount + 1);
+  },
+  (VM vm) async {
+    var resumedReceived = 0;
+    Completer completer = new Completer();
+    var stream = await vm.getEventStream(VM.kIsolateStream);
+    var subscription;
+    subscription = stream.listen((ServiceEvent event) {
+      if (event.kind == ServiceEvent.kIsolateExit) {
+        resumedReceived++;
+        if (resumedReceived >= resumeCount) {
+          subscription.cancel();
+          completer.complete(null);
+        }
+      }
+    });
+
+    // Resume a subset of the isolates.
+    var resumesIssued = 0;
+    var isolateList = vm.isolates.toList();
+    for (var isolate in isolateList) {
+      if (isolate.name.endsWith('main')) {
+        continue;
+      }
+      try {
+        resumesIssued++;
+        await isolate.resume();
+      } catch (_) {}
+      if (resumesIssued == resumeCount) {
+        break;
+      }
+    }
+    await completer.future;
+  },
+  (VM vm) async {
+    expect(numPaused(vm), spawnCount + 1 - resumeCount);
+  },
+];
+
+main(args) async =>
+    runVMTests(args, tests, testeeConcurrent: during, pause_on_exit: true);
diff --git a/runtime/observatory_2/tests/service_2/issue_25465_test.dart b/runtime/observatory_2/tests/service_2/issue_25465_test.dart
new file mode 100644
index 0000000..87c6986
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/issue_25465_test.dart
@@ -0,0 +1,73 @@
+// Copyright (c) 2015, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+import 'service_test_common.dart';
+import 'dart:async';
+
+const int LINE_A = 16;
+const int LINE_B = 17;
+
+testMain() {
+  var foo; // line A
+  foo = 42; // line B
+  print(foo);
+}
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+
+  // Add breakpoints at line 11 and line 12.
+  (Isolate isolate) async {
+    var rootLib = isolate.rootLibrary;
+    await rootLib.load();
+    var script = rootLib.scripts[0];
+
+    var bpt1 = await isolate.addBreakpoint(script, LINE_A);
+    var bpt2 = await isolate.addBreakpoint(script, LINE_B);
+    expect(await bpt1.location.getLine(), equals(LINE_A));
+    expect(await bpt2.location.getLine(), equals(LINE_B));
+
+    var stream = await isolate.vm.getEventStream(VM.kDebugStream);
+    Completer completer = new Completer();
+    var subscription;
+    var breakCount = 0;
+    subscription = stream.listen((ServiceEvent event) async {
+      if (event.kind == ServiceEvent.kPauseBreakpoint) {
+        breakCount++;
+        print('break count is $breakCount');
+        if (breakCount == 1) {
+          // We are stopped at breakpoint 1.
+          expect(event.breakpoint.number, equals(bpt1.number));
+
+          // Remove both breakpoints
+          var result = await isolate.removeBreakpoint(bpt1);
+          expect(result.type, equals("Success"));
+
+          result = await isolate.removeBreakpoint(bpt2);
+          expect(result.type, equals("Success"));
+
+          isolate.stepOver();
+        } else {
+          // No breakpoint.
+          expect(event.breakpoint, isNull);
+
+          // We expect the next step to take us to line B.
+          var stack = await isolate.getStack();
+          expect(await stack['frames'][0].location.getLine(), equals(LINE_B));
+
+          subscription.cancel();
+          completer.complete(null);
+        }
+      }
+    });
+    isolate.resume();
+    await completer.future;
+  },
+];
+
+main(args) => runIsolateTests(args, tests,
+    testeeConcurrent: testMain, pause_on_start: true);
diff --git a/runtime/observatory_2/tests/service_2/issue_27238_test.dart b/runtime/observatory_2/tests/service_2/issue_27238_test.dart
new file mode 100644
index 0000000..60c7497
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/issue_27238_test.dart
@@ -0,0 +1,46 @@
+// Copyright (c) 2015, 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.
+// VMOptions=--verbose_debug
+
+import 'service_test_common.dart';
+import 'dart:async';
+import 'test_helper.dart';
+import 'dart:developer';
+
+const int LINE_A = 19;
+const int LINE_B = 22;
+const int LINE_C = 23;
+const int LINE_D = 25;
+const int LINE_E = 26;
+
+testMain() async {
+  debugger();
+  Future future1 = new Future.value(); // LINE_A.
+  Future future2 = new Future.value();
+
+  await future1; // LINE_B.
+  await future2; // LINE_C.
+
+  print('foo1'); // LINE_D.
+  print('foo2'); // LINE_E.
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_A),
+  smartNext,
+  hasStoppedAtBreakpoint,
+  smartNext,
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_B),
+  smartNext,
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_C),
+  smartNext,
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_D),
+  resumeIsolate,
+];
+
+main(args) => runIsolateTests(args, tests, testeeConcurrent: testMain);
diff --git a/runtime/observatory_2/tests/service_2/issue_27287_test.dart b/runtime/observatory_2/tests/service_2/issue_27287_test.dart
new file mode 100644
index 0000000..872ad12
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/issue_27287_test.dart
@@ -0,0 +1,31 @@
+// Copyright (c) 2016, 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.
+// VMOptions=--verbose_debug
+
+import 'service_test_common.dart';
+import 'test_helper.dart';
+import 'dart:developer';
+
+const int LINE_A = 17;
+const int LINE_B = 18;
+
+var libVariable;
+
+testMain() {
+  debugger();
+  print("Before"); // LINE_A
+  libVariable = 0; // LINE_B
+  print("and after");
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_A),
+  stepOver,
+  // Check that debugger stops at assignment to top-level variable.
+  stoppedAtLine(LINE_B),
+  resumeIsolate,
+];
+
+main(args) => runIsolateTests(args, tests, testeeConcurrent: testMain);
diff --git a/runtime/observatory_2/tests/service_2/issue_30555_test.dart b/runtime/observatory_2/tests/service_2/issue_30555_test.dart
new file mode 100644
index 0000000..00c34c9
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/issue_30555_test.dart
@@ -0,0 +1,141 @@
+// Copyright (c) 2017, 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.
+
+import 'dart:async';
+import 'dart:developer';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+import "dart:isolate" as dart;
+
+void isolate(dart.SendPort port) {
+  dart.RawReceivePort receive = new dart.RawReceivePort((_) {
+    debugger();
+    throw new Exception();
+  });
+  port.send(receive.sendPort);
+}
+
+void test() {
+  dart.RawReceivePort receive = new dart.RawReceivePort((port) {
+    debugger();
+    port.send(null);
+    debugger();
+    port.send(null);
+    debugger();
+  });
+  dart.Isolate.spawn(isolate, receive.sendPort);
+}
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  (Isolate isolate) async {
+    int step = 0;
+    var completer = new Completer();
+    var sub;
+    final Isolate firstIsolate = isolate;
+    print("First isolate is: ${firstIsolate.id}");
+    Isolate secondIsolate;
+    sub = await isolate.vm.listenEventStream(VM.kDebugStream, (ServiceEvent c) {
+      print("Event ${c.kind} on ${c.isolate.id}");
+      switch (step) {
+        case 0:
+          expect(c.kind, equals(ServiceEvent.kResume),
+              reason: "First isolate should resume");
+          expect(c.isolate.id, equals(firstIsolate.id),
+              reason: "First isolate should resume");
+          break;
+        case 1:
+          expect(c.kind, equals(ServiceEvent.kPauseStart),
+              reason: "Second isolate should pause on start");
+          expect(c.isolate.id, equals(isolate.vm.isolates[1].id),
+              reason: "Second isolate should pause on start");
+          secondIsolate = c.isolate;
+          print("Second isolate is: ${secondIsolate.id}");
+          print("Resuming second isolate");
+          secondIsolate.resume();
+          break;
+        case 2:
+          expect(c.kind, equals(ServiceEvent.kResume),
+              reason: "Second isolate should resume");
+          expect(c.isolate.id, equals(secondIsolate.id),
+              reason: "Second isolate should resume");
+          break;
+        case 3:
+          expect(c.kind, equals(ServiceEvent.kPauseBreakpoint),
+              reason: "First isolate should stop at debugger()");
+          expect(c.isolate.id, equals(firstIsolate.id),
+              reason: "First isolate should stop at debugger()");
+          print("Resuming first isolate");
+          firstIsolate.resume();
+          break;
+        case 4:
+          expect(c.kind, equals(ServiceEvent.kResume),
+              reason: "First isolate should resume (1)");
+          expect(c.isolate.id, equals(firstIsolate.id),
+              reason: "First isolate should resume (1)");
+          break;
+        case 5:
+          expect(c.kind, equals(ServiceEvent.kPauseBreakpoint),
+              reason: "First & Second isolate should stop at debugger()");
+          break;
+        case 6:
+          expect(c.kind, equals(ServiceEvent.kPauseBreakpoint),
+              reason: "First & Second isolate should stop at debugger()");
+          print("Resuming second isolate");
+          secondIsolate.resume();
+          break;
+        case 7:
+          expect(c.kind, equals(ServiceEvent.kResume),
+              reason: "Second isolate should resume before the exception");
+          expect(c.isolate.id, equals(secondIsolate.id),
+              reason: "Second isolate should resume before the exception");
+          break;
+        case 8:
+          expect(c.kind, equals(ServiceEvent.kPauseExit),
+              reason: "Second isolate should exit at the exception");
+          expect(c.isolate.id, equals(secondIsolate.id),
+              reason: "Second isolate should exit at the exception");
+          print("Resuming first isolate");
+          firstIsolate.resume();
+          break;
+        case 9:
+          expect(c.kind, equals(ServiceEvent.kResume),
+              reason: "First isolate should resume after the exception");
+          expect(c.isolate.id, equals(firstIsolate.id),
+              reason: "First isolate should resume after the exception");
+          break;
+        case 10:
+          expect(c.isolate.id, equals(firstIsolate.id),
+              reason: "First "
+                  "isolate should stop at debugger() after exception.\n"
+                  "Probably the second resumed even though it was not expect "
+                  "to do it.");
+          expect(c.kind, equals(ServiceEvent.kPauseBreakpoint),
+              reason: "First isolate should stop at debugger() after "
+                  "exception.");
+          completer.complete();
+          break;
+        default:
+          fail("Shouldn't get here, the second isolate resumed even though it "
+              "was not expect to do it");
+          break;
+      }
+      step++;
+    });
+    print("Resuming first isolate");
+    firstIsolate.resume();
+    await completer.future;
+    // We wait 1 second to account for delays in the service protocol.
+    // A late message can still arrive.
+    await new Future.delayed(const Duration(seconds: 1));
+    // No fails, tear down the stream.
+    sub.cancel();
+  }
+];
+
+main(args) async => runIsolateTests(args, tests,
+    pause_on_start: true, pause_on_exit: true, testeeConcurrent: test);
diff --git a/runtime/observatory_2/tests/service_2/kill_paused_test.dart b/runtime/observatory_2/tests/service_2/kill_paused_test.dart
new file mode 100644
index 0000000..1e9e346
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/kill_paused_test.dart
@@ -0,0 +1,29 @@
+// Copyright (c) 2015, 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.
+
+import 'package:observatory_2/service_common.dart';
+import 'package:test/test.dart';
+
+import 'test_helper.dart';
+import 'dart:developer';
+import 'service_test_common.dart';
+
+testMain() async {
+  debugger(); // Stop here.
+  print('1');
+  while (true) {}
+}
+
+var tests = <IsolateTest>[
+  // Stopped at 'debugger' statement.
+  hasStoppedAtBreakpoint,
+  // Kill the app
+  (Isolate isolate) async {
+    Map<String, dynamic> params = <String, dynamic>{};
+    ServiceObject result = await isolate.invokeRpc('kill', params);
+    expect(result.type, equals('Success'));
+  }
+];
+
+main(args) async => runIsolateTests(args, tests, testeeConcurrent: testMain);
diff --git a/runtime/observatory_2/tests/service_2/kill_running_test.dart b/runtime/observatory_2/tests/service_2/kill_running_test.dart
new file mode 100644
index 0000000..5f5846d
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/kill_running_test.dart
@@ -0,0 +1,25 @@
+// Copyright (c) 2015, 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.
+
+import 'package:observatory_2/service_common.dart';
+import 'package:test/test.dart';
+
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+testMain() async {
+  print('1');
+  while (true) {}
+}
+
+var tests = <IsolateTest>[
+  // Kill the app
+  (Isolate isolate) async {
+    Map<String, dynamic> params = <String, dynamic>{};
+    ServiceObject result = await isolate.invokeRpc('kill', params);
+    expect(result.type, equals('Success'));
+  }
+];
+
+main(args) async => runIsolateTests(args, tests, testeeConcurrent: testMain);
diff --git a/runtime/observatory_2/tests/service_2/library_dependency_test.dart b/runtime/observatory_2/tests/service_2/library_dependency_test.dart
new file mode 100644
index 0000000..a34a95d
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/library_dependency_test.dart
@@ -0,0 +1,46 @@
+// Copyright (c) 2015, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+
+export 'dart:collection';
+import 'dart:mirrors' as mirrors;
+import 'dart:convert' deferred as convert;
+
+var tests = <IsolateTest>[
+  (Isolate isolate) async {
+    await isolate.load();
+    Library lib = isolate.rootLibrary;
+    await lib.load();
+
+    // Avoid unused import warning from the analyzer.
+    mirrors.currentMirrorSystem();
+
+    importOf(String uri) {
+      return lib.dependencies.singleWhere((dep) => dep.target.uri == uri);
+    }
+
+    expect(importOf("dart:collection").isImport, isFalse);
+    expect(importOf("dart:collection").isExport, isTrue);
+    expect(importOf("dart:collection").isDeferred, isFalse);
+    expect(importOf("dart:collection").prefix, equals(null));
+
+    expect(importOf("dart:mirrors").isImport, isTrue);
+    expect(importOf("dart:mirrors").isExport, isFalse);
+    expect(importOf("dart:mirrors").isDeferred, isFalse);
+    expect(importOf("dart:mirrors").prefix, equals("mirrors"));
+
+    expect(importOf("dart:convert").isImport, isTrue);
+    expect(importOf("dart:convert").isExport, isFalse);
+    expect(importOf("dart:convert").isDeferred, isTrue);
+    expect(importOf("dart:convert").prefix, equals("convert"));
+  },
+  (Isolate isolate) async {
+    return convert.loadLibrary();
+  }
+];
+
+main(args) => runIsolateTests(args, tests);
diff --git a/runtime/observatory_2/tests/service_2/local_variable_declaration_test.dart b/runtime/observatory_2/tests/service_2/local_variable_declaration_test.dart
new file mode 100644
index 0000000..4a05a02
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/local_variable_declaration_test.dart
@@ -0,0 +1,112 @@
+// Copyright (c) 2016, 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.
+// VMOptions=--verbose_debug
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+import 'dart:developer';
+
+testParameters(int jjjj, int oooo, [int hhhh, int nnnn]) {
+  debugger();
+}
+
+testMain() {
+  int xxx, yyyy, zzzzz;
+  for (int i = 0; i < 1; i++) {
+    var foo = () {};
+    debugger();
+  }
+  var bar = () {
+    print(xxx);
+    print(yyyy);
+    debugger();
+  };
+  bar();
+  testParameters(0, 0);
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  stoppedInFunction('testMain'),
+  (Isolate isolate) async {
+    var stack = await isolate.getStack();
+    expect(stack.type, equals('Stack'));
+    expect(stack['frames'].length, greaterThanOrEqualTo(1));
+    // Grab the top frame.
+    Frame frame = stack['frames'][0];
+    // Grab the script.
+    Script script = frame.location.script;
+    await script.load();
+
+    // Ensure that the token at each declaration position is the name of the
+    // variable.
+    for (var variable in frame.variables) {
+      final int declarationTokenPos = variable['declarationTokenPos'];
+      final String name = variable['name'];
+      final String token = script.getToken(declarationTokenPos);
+      // When running from an appjit snapshot, sources aren't available so the returned token will
+      // be null.
+      if (token != null) {
+        expect(name, token);
+      }
+    }
+  },
+  resumeIsolate,
+  hasStoppedAtBreakpoint,
+  // We have stopped in the anonymous closure assigned to bar. Verify that
+  // variables captured in the context have valid declaration positions.
+  (Isolate isolate) async {
+    var stack = await isolate.getStack();
+    expect(stack.type, equals('Stack'));
+    expect(stack['frames'].length, greaterThanOrEqualTo(1));
+    // Grab the top frame.
+    Frame frame = stack['frames'][0];
+    // Grab the script.
+    Script script = frame.location.script;
+    await script.load();
+    print(frame);
+    expect(frame.variables.length, greaterThanOrEqualTo(1));
+    for (var variable in frame.variables) {
+      final int declarationTokenPos = variable['declarationTokenPos'];
+      final String name = variable['name'];
+      final String token = script.getToken(declarationTokenPos);
+      // When running from an appjit snapshot, sources aren't available so the returned token will
+      // be null.
+      if (token != null) {
+        expect(name, token);
+      }
+    }
+  },
+  resumeIsolate,
+  hasStoppedAtBreakpoint,
+  stoppedInFunction('testParameters'),
+  (Isolate isolate) async {
+    var stack = await isolate.getStack();
+    expect(stack.type, equals('Stack'));
+    expect(stack['frames'].length, greaterThanOrEqualTo(1));
+    // Grab the top frame.
+    Frame frame = stack['frames'][0];
+    // Grab the script.
+    Script script = frame.location.script;
+    await script.load();
+
+    // Ensure that the token at each declaration position is the name of the
+    // variable.
+    expect(frame.variables.length, greaterThanOrEqualTo(1));
+    for (var variable in frame.variables) {
+      final int declarationTokenPos = variable['declarationTokenPos'];
+      final String name = variable['name'];
+      final String token = script.getToken(declarationTokenPos);
+      // When running from an appjit snapshot, sources aren't available so the returned token will
+      // be null.
+      if (token != null) {
+        expect(name, token);
+      }
+    }
+  }
+];
+
+main(args) => runIsolateTests(args, tests, testeeConcurrent: testMain);
diff --git a/runtime/observatory_2/tests/service_2/local_variable_in_awaiter_async_frame_test.dart b/runtime/observatory_2/tests/service_2/local_variable_in_awaiter_async_frame_test.dart
new file mode 100644
index 0000000..dd34e63
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/local_variable_in_awaiter_async_frame_test.dart
@@ -0,0 +1,34 @@
+// Copyright (c) 2020, 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.
+
+import 'dart:developer';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+const int LINE_A = 14;
+
+Future<String> testFunction(String caption) async {
+  await Future.delayed(Duration(milliseconds: 1));
+  return caption;
+}
+
+testMain() async {
+  debugger();
+  var str = await testFunction('The caption');
+  print(str);
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  setBreakpointAtLine(LINE_A),
+  resumeIsolate,
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_A),
+  hasLocalVarInTopAwaiterStackFrame('caption'),
+  resumeIsolate,
+];
+
+main(args) => runIsolateTests(args, tests, testeeConcurrent: testMain);
diff --git a/runtime/observatory_2/tests/service_2/logging_test.dart b/runtime/observatory_2/tests/service_2/logging_test.dart
new file mode 100644
index 0000000..dac2746
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/logging_test.dart
@@ -0,0 +1,53 @@
+// Copyright (c) 2015, 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.
+
+import 'dart:developer' as developer;
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'package:logging/logging.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+void init() {
+  Logger.root.level = Level.ALL;
+  Logger.root.onRecord.listen((logRecord) {
+    developer.log(logRecord.message,
+        time: logRecord.time,
+        sequenceNumber: logRecord.sequenceNumber,
+        level: logRecord.level.value,
+        name: logRecord.loggerName,
+        zone: null,
+        error: logRecord.error,
+        stackTrace: logRecord.stackTrace);
+  });
+}
+
+void run() {
+  developer.debugger();
+  Logger.root.fine('Hey Buddy!');
+  developer.debugger();
+  Logger.root.info('YES');
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  resumeIsolateAndAwaitEvent(Isolate.kLoggingStream, (ServiceEvent event) {
+    expect(event.kind, equals(ServiceEvent.kLogging));
+    expect(event.logRecord['sequenceNumber'], equals(0));
+    expect(event.logRecord['message'].valueAsString, equals('Hey Buddy!'));
+    expect(event.logRecord['level'], equals(Level.FINE));
+    expect(event.logRecord['time'], isA<DateTime>());
+  }),
+  hasStoppedAtBreakpoint,
+  resumeIsolateAndAwaitEvent(Isolate.kLoggingStream, (ServiceEvent event) {
+    expect(event.kind, equals(ServiceEvent.kLogging));
+    expect(event.logRecord['sequenceNumber'], equals(1));
+    expect(event.logRecord['level'], equals(Level.INFO));
+    expect(event.logRecord['message'].valueAsString, equals('YES'));
+    expect(event.logRecord['time'], isA<DateTime>());
+  }),
+];
+
+main(args) =>
+    runIsolateTests(args, tests, testeeBefore: init, testeeConcurrent: run);
diff --git a/runtime/observatory_2/tests/service_2/malformed_test.dart b/runtime/observatory_2/tests/service_2/malformed_test.dart
new file mode 100644
index 0000000..8a3d886
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/malformed_test.dart
@@ -0,0 +1,47 @@
+// Copyright (c) 2014, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+
+var tests = <IsolateTest>[
+  (Isolate isolate) async {
+    bool caughtException;
+    try {
+      await isolate.invokeRpc('_respondWithMalformedObject', {});
+      expect(false, isTrue, reason: 'Unreachable');
+    } on MalformedResponseRpcException catch (e) {
+      caughtException = true;
+      expect(e.message, equals("Response is missing the 'type' field"));
+    }
+    expect(caughtException, isTrue);
+  },
+
+  // Do this test last... it kills the vm connection.
+  (Isolate isolate) async {
+    bool caughtException;
+    try {
+      await isolate.invokeRpc('_respondWithMalformedJson', {});
+      expect(false, isTrue, reason: 'Unreachable');
+    } on NetworkRpcException catch (e) {
+      caughtException = true;
+      expect(
+          e.message,
+          startsWith("Canceling request: "
+              "Connection saw corrupt JSON message: "
+              "FormatException: Unexpected character"));
+    }
+    expect(caughtException, isTrue);
+  },
+];
+
+main(args) => runIsolateTests(
+      args,
+      tests,
+      // This test hangs with DDS as package:json_rpc_2 can't parse the JSON
+      // response and is unable to determine the request ID, so the malformed
+      // JSON request will never complete.
+      enableDds: false,
+    );
diff --git a/runtime/observatory_2/tests/service_2/metrics_test.dart b/runtime/observatory_2/tests/service_2/metrics_test.dart
new file mode 100644
index 0000000..91b57fa
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/metrics_test.dart
@@ -0,0 +1,48 @@
+// Copyright (c) 2014, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+
+import 'dart:developer';
+
+void script() {
+  var counter = new Counter('a.b.c', 'description');
+  Metrics.register(counter);
+  counter.value = 1234.5;
+}
+
+var tests = <IsolateTest>[
+  (Isolate isolate) async {
+    Map metrics = await isolate.refreshDartMetrics();
+    expect(metrics.length, equals(1));
+    var counter = metrics['metrics/a.b.c'];
+    expect(counter.name, equals('a.b.c'));
+    expect(counter.value, equals(1234.5));
+  },
+  (Isolate isolate) async {
+    var params = {'metricId': 'metrics/a.b.c'};
+    ServiceMetric counter =
+        await isolate.invokeRpc('_getIsolateMetric', params);
+    expect(counter.name, equals('a.b.c'));
+    expect(counter.value, equals(1234.5));
+  },
+  (Isolate isolate) async {
+    bool caughtException;
+    try {
+      await isolate
+          .invokeRpc('_getIsolateMetric', {'metricId': 'metrics/a.b.d'});
+      expect(false, isTrue, reason: 'Unreachable');
+    } on ServerRpcException catch (e) {
+      caughtException = true;
+      expect(e.code, equals(ServerRpcException.kInvalidParams));
+      expect(e.message,
+          "_getIsolateMetric: invalid 'metricId' parameter: metrics/a.b.d");
+    }
+    expect(caughtException, isTrue);
+  },
+];
+
+main(args) => runIsolateTests(args, tests, testeeBefore: script);
diff --git a/runtime/observatory_2/tests/service_2/mirror_references_test.dart b/runtime/observatory_2/tests/service_2/mirror_references_test.dart
new file mode 100644
index 0000000..6699130
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/mirror_references_test.dart
@@ -0,0 +1,47 @@
+// Copyright (c) 2014, 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.
+
+library vm_references_test;
+
+import 'dart:mirrors';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+
+class Foo {}
+
+Foo foo;
+var /*MirrorReference*/ ref;
+
+void script() {
+  foo = new Foo();
+  ClassMirror fooClassMirror = reflectClass(Foo);
+  InstanceMirror fooClassMirrorMirror = reflect(fooClassMirror);
+  LibraryMirror libmirrors = fooClassMirrorMirror.type.owner;
+  ref = reflect(fooClassMirror)
+      .getField(MirrorSystem.getSymbol('_reflectee', libmirrors))
+      .reflectee;
+}
+
+var tests = <IsolateTest>[
+  (Isolate isolate) async {
+    Library lib = await isolate.rootLibrary.load();
+    Field fooField = lib.variables.singleWhere((v) => v.name == 'foo');
+    await fooField.load();
+    Instance foo = fooField.staticValue;
+    Field refField = lib.variables.singleWhere((v) => v.name == 'ref');
+    await refField.load();
+    Instance ref = refField.staticValue;
+
+    expect(foo.isMirrorReference, isFalse);
+    expect(ref.isMirrorReference, isTrue);
+    expect(ref.referent, isNull);
+    Instance loadedRef = await ref.load();
+    expect(loadedRef.referent, isNotNull);
+    expect(loadedRef.referent.name, equals('Foo'));
+    expect(loadedRef.referent, equals(foo.clazz));
+  },
+];
+
+main(args) => runIsolateTests(args, tests, testeeBefore: script);
diff --git a/runtime/observatory_2/tests/service_2/mixin_break_class1.dart b/runtime/observatory_2/tests/service_2/mixin_break_class1.dart
new file mode 100644
index 0000000..e490fdd
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/mixin_break_class1.dart
@@ -0,0 +1,10 @@
+library mixin_step_class1;
+
+import 'mixin_break_mixin_class.dart';
+
+class Hello1 extends Object with HelloMixin {
+  void speak() {
+    sayHello();
+    print(" - Hello1");
+  }
+}
diff --git a/runtime/observatory_2/tests/service_2/mixin_break_class2.dart b/runtime/observatory_2/tests/service_2/mixin_break_class2.dart
new file mode 100644
index 0000000..cc6a454
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/mixin_break_class2.dart
@@ -0,0 +1,10 @@
+library mixin_step_class2;
+
+import 'mixin_break_mixin_class.dart';
+
+class Hello2 extends Object with HelloMixin {
+  void speak() {
+    sayHello();
+    print(" - Hello2");
+  }
+}
diff --git a/runtime/observatory_2/tests/service_2/mixin_break_mixin_class.dart b/runtime/observatory_2/tests/service_2/mixin_break_mixin_class.dart
new file mode 100644
index 0000000..e19a7a8
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/mixin_break_mixin_class.dart
@@ -0,0 +1,7 @@
+library mixin_step_mixin_class;
+
+class HelloMixin {
+  void sayHello() {
+    print("Hello!");
+  }
+}
diff --git a/runtime/observatory_2/tests/service_2/mixin_break_test.dart b/runtime/observatory_2/tests/service_2/mixin_break_test.dart
new file mode 100644
index 0000000..f32e87a
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/mixin_break_test.dart
@@ -0,0 +1,50 @@
+// Copyright (c) 2017, 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.
+
+library mixin_step_test;
+
+import 'dart:developer';
+
+import 'mixin_break_class1.dart';
+import 'mixin_break_class2.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+const String file = "mixin_break_mixin_class.dart";
+
+int codeRuns = 0;
+
+code() {
+  if (++codeRuns > 1) {
+    print("Calling debugger!");
+    debugger();
+  }
+  Hello1 a = new Hello1();
+  Hello2 b = new Hello2();
+  a.speak();
+  b.speak();
+
+  print("Both now compiled");
+}
+
+List<String> stops = [];
+List<String> expected = [
+  "$file:5:5 (mixin_break_class1.dart:7:5)",
+  "$file:5:5 (mixin_break_class2.dart:7:5)",
+];
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  setBreakpointAtUriAndLine(file, 5),
+  resumeProgramRecordingStops(stops, true),
+  checkRecordedStops(stops, expected),
+];
+
+main(args) {
+  runIsolateTests(args, tests,
+      testeeBefore: code,
+      testeeConcurrent: code,
+      pause_on_start: false,
+      pause_on_exit: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/native_metrics_test.dart b/runtime/observatory_2/tests/service_2/native_metrics_test.dart
new file mode 100644
index 0000000..ffc167d
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/native_metrics_test.dart
@@ -0,0 +1,51 @@
+// Copyright (c) 2015, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+
+import 'dart:developer';
+
+void script() {
+  var counter = new Counter('a.b.c', 'description');
+  Metrics.register(counter);
+  counter.value = 1234.5;
+}
+
+var tests = <IsolateTest>[
+  (Isolate isolate) async {
+    Map metrics = await isolate.refreshNativeMetrics();
+    expect(metrics.length, greaterThan(1));
+    expect(metrics.length, greaterThan(1));
+    var foundOldHeapCapacity =
+        metrics.values.any((m) => m.name == 'heap.old.capacity');
+    expect(foundOldHeapCapacity, equals(true));
+  },
+  (Isolate isolate) async {
+    var params = {'metricId': 'metrics/native/heap.old.used'};
+    ServiceMetric counter =
+        await isolate.invokeRpc('_getIsolateMetric', params);
+    expect(counter.type, equals('Counter'));
+    expect(counter.name, equals('heap.old.used'));
+  },
+  (Isolate isolate) async {
+    bool caughtException;
+    try {
+      await isolate.invokeRpc(
+          '_getIsolateMetric', {'metricId': 'metrics/native/doesnotexist'});
+      expect(false, isTrue, reason: 'Unreachable');
+    } on ServerRpcException catch (e) {
+      caughtException = true;
+      expect(e.code, equals(ServerRpcException.kInvalidParams));
+      expect(
+          e.message,
+          "_getIsolateMetric: invalid 'metricId' "
+          "parameter: metrics/native/doesnotexist");
+    }
+    expect(caughtException, isTrue);
+  },
+];
+
+main(args) => runIsolateTests(args, tests, testeeBefore: script);
diff --git a/runtime/observatory_2/tests/service_2/network_profiling_test.dart b/runtime/observatory_2/tests/service_2/network_profiling_test.dart
new file mode 100644
index 0000000..5c27795
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/network_profiling_test.dart
@@ -0,0 +1,215 @@
+// Copyright (c) 2019, 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.
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:developer';
+import 'dart:io' as io;
+import 'dart:isolate';
+
+import 'package:expect/expect.dart';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+const String content = 'some random content';
+const String udpContent = 'aghfkjdb';
+const String kClearSocketProfileRPC = 'ext.dart.io.clearSocketProfile';
+const String kGetSocketProfileRPC = 'ext.dart.io.getSocketProfile';
+const String kGetVersionRPC = 'ext.dart.io.getVersion';
+const String kPauseSocketProfilingRPC = 'ext.dart.io.pauseSocketProfiling';
+const String kStartSocketProfilingRPC = 'ext.dart.io.startSocketProfiling';
+const String localhost = '127.0.0.1';
+
+Future<void> setup() async {}
+
+Future<void> socketTest() async {
+  // Socket
+  var serverSocket = await io.ServerSocket.bind(localhost, 0);
+  var socket = await io.Socket.connect(localhost, serverSocket.port);
+  socket.write(content);
+  await socket.flush();
+  await socket.destroy();
+
+  // rawDatagram
+  final doneCompleter = Completer<void>();
+  var server = await io.RawDatagramSocket.bind(localhost, 0);
+  server.listen((io.RawSocketEvent event) {
+    if (event == io.RawSocketEvent.read) {
+      io.Datagram dg = server.receive();
+      if (!doneCompleter.isCompleted) {
+        doneCompleter.complete();
+      }
+    }
+  });
+  var client = await io.RawDatagramSocket.bind(localhost, 0);
+  client.send(utf8.encoder.convert(udpContent), io.InternetAddress(localhost),
+      server.port);
+  client.send([1, 2, 3], io.InternetAddress(localhost), server.port);
+
+  // Wait for datagram to arrive.
+  await doneCompleter.future;
+  // Post finish event
+  postEvent('socketTest', {'socket': 'test'});
+}
+
+Future<void> checkFinishEvent(ServiceEvent event) {
+  expect(event.kind, equals(ServiceEvent.kExtension));
+  expect(event.extensionKind, equals('socketTest'));
+  expect(event.extensionData, isA<Map>());
+  expect(event.extensionData['socket'], equals('test'));
+}
+
+var tests = <IsolateTest>[
+  (Isolate isolate) async {
+    await isolate.load();
+    // Ensure all network profiling service extensions are registered.
+    expect(isolate.extensionRPCs.length, greaterThanOrEqualTo(5));
+    expect(isolate.extensionRPCs.contains(kClearSocketProfileRPC), isTrue);
+    expect(isolate.extensionRPCs.contains(kGetVersionRPC), isTrue);
+    expect(isolate.extensionRPCs.contains(kPauseSocketProfilingRPC), isTrue);
+    expect(isolate.extensionRPCs.contains(kStartSocketProfilingRPC), isTrue);
+    expect(isolate.extensionRPCs.contains(kPauseSocketProfilingRPC), isTrue);
+  },
+
+  // Test getSocketProfiler
+  (Isolate isolate) async {
+    await isolate.load();
+    Library lib = isolate.rootLibrary;
+    await lib.load();
+
+    var response = await isolate.invokeRpcNoUpgrade(kGetSocketProfileRPC, {});
+    expect(response['type'], 'SocketProfile');
+    // returns an empty list in 'sockets'
+    expect(response['sockets'].length, 0);
+  },
+
+  // Test getSocketProfile and startSocketProfiling
+  (Isolate isolate) async {
+    await isolate.load();
+    Library lib = isolate.rootLibrary;
+    await lib.load();
+
+    var response =
+        await isolate.invokeRpcNoUpgrade(kStartSocketProfilingRPC, {});
+    expect(response['type'], 'Success');
+
+    // Check whether socketTest has finished.
+    Completer completer = Completer();
+    var sub;
+    sub = await isolate.vm.listenEventStream(Isolate.kExtensionStream,
+        (ServiceEvent event) {
+      checkFinishEvent(event);
+      sub.cancel();
+      completer.complete();
+    });
+
+    dynamic result = await isolate.invokeRpc("invoke",
+        {"targetId": lib.id, "selector": "socketTest", "argumentIds": []});
+    await completer.future;
+
+    response = await isolate.invokeRpcNoUpgrade(kGetSocketProfileRPC, {});
+    expect(response['type'], 'SocketProfile');
+    var stats = response['sockets'];
+    // 1 tcp socket, 2 udp datagrams
+    expect(stats.length, 3);
+    stats.forEach((socket) {
+      expect(socket['address'], contains(localhost));
+      Expect.type<int>(socket['startTime']);
+      Expect.type<int>(socket['id']);
+      Expect.type<int>(socket['port']);
+      if (socket['socketType'] == 'tcp') {
+        expect(socket['writeBytes'], content.length);
+        expect(socket.containsKey('lastWriteTime'), true);
+        expect(socket['lastWriteTime'] > 0, true);
+      } else {
+        // 2 udp sockets, one of them is writing and the other is listening.
+        expect(socket['socketType'], 'udp');
+        if (socket['readBytes'] == 0) {
+          // [1, 2, 3] was sent.
+          expect(socket['writeBytes'], 3 + udpContent.length);
+          expect(socket.containsKey('lastWriteTime'), true);
+          expect(socket['lastWriteTime'] > 0, true);
+          expect(socket.containsKey('lastReadTime'), false);
+        } else {
+          // [1, 2, 3] was sent.
+          expect(socket['writeBytes'], 0);
+          expect(socket['readBytes'], 3 + udpContent.length);
+          expect(socket.containsKey('lastWriteTime'), false);
+          expect(socket.containsKey('lastReadTime'), true);
+          expect(socket['lastReadTime'] > 0, true);
+        }
+      }
+    });
+
+    // run 99 more times and check we have 100 sockets statistic.
+    for (int i = 0; i < 99; i++) {
+      completer = Completer();
+      sub = await isolate.vm.listenEventStream(Isolate.kExtensionStream,
+          (ServiceEvent event) {
+        checkFinishEvent(event);
+        sub.cancel();
+        completer.complete();
+      });
+      dynamic result = await isolate.invokeRpc("invoke",
+          {"targetId": lib.id, "selector": "socketTest", "argumentIds": []});
+      await completer.future;
+    }
+
+    response = await isolate.invokeRpcNoUpgrade(kGetSocketProfileRPC, {});
+    expect(response['type'], 'SocketProfile');
+    // 1 tcp socket, 2 udp datagrams
+    expect(response['sockets'].length, 3 * 100);
+  },
+
+  // Test clearSocketProfiler
+  (Isolate isolate) async {
+    await isolate.load();
+    Library lib = isolate.rootLibrary;
+    await lib.load();
+
+    var response = await isolate.invokeRpcNoUpgrade(kClearSocketProfileRPC, {});
+    expect(response['type'], 'Success');
+
+    response = await isolate.invokeRpcNoUpgrade(kGetSocketProfileRPC, {});
+    expect(response['type'], 'SocketProfile');
+    expect(response['sockets'].length, 0);
+  },
+
+  // Test pauseSocketProfiling
+  (Isolate isolate) async {
+    await isolate.load();
+    Library lib = isolate.rootLibrary;
+    await lib.load();
+
+    var response =
+        await isolate.invokeRpcNoUpgrade(kStartSocketProfilingRPC, {});
+    expect(response['type'], 'Success');
+
+    response = await isolate.invokeRpcNoUpgrade(kPauseSocketProfilingRPC, {});
+    expect(response['type'], 'Success');
+
+    // Check whether socketTest has finished.
+    Completer completer = Completer();
+    var sub;
+    sub = await isolate.vm.listenEventStream(Isolate.kExtensionStream,
+        (ServiceEvent event) {
+      checkFinishEvent(event);
+      sub.cancel();
+      completer.complete();
+    });
+
+    dynamic result = await isolate.invokeRpc("invoke",
+        {"targetId": lib.id, "selector": "socketTest", "argumentIds": []});
+    await completer.future;
+
+    response = await isolate.invokeRpcNoUpgrade(kGetSocketProfileRPC, {});
+    expect(response['type'], 'SocketProfile');
+    expect(response['sockets'].length, 0);
+  }
+];
+
+main(args) async => runIsolateTests(args, tests, testeeBefore: setup);
diff --git a/runtime/observatory_2/tests/service_2/next_through_assign_call_test.dart b/runtime/observatory_2/tests/service_2/next_through_assign_call_test.dart
new file mode 100644
index 0000000..3b815d2
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/next_through_assign_call_test.dart
@@ -0,0 +1,61 @@
+// Copyright (c) 2017, 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.
+
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+const int LINE_A = 12;
+const String file = "next_through_assign_call_test.dart";
+
+code() {
+  int a;
+  int b;
+  a = b = foo();
+  print(a);
+  print(b);
+  a = foo();
+  print(a);
+  int d = foo();
+  print(d);
+  int e = foo(), f, g = foo();
+  print(e);
+  print(f);
+  print(g);
+}
+
+foo() {
+  return 42;
+}
+
+List<String> stops = [];
+List<String> expected = [
+  "$file:${LINE_A+0}:7", // on variable 'a'
+  "$file:${LINE_A+1}:7", // on variable 'b'
+  "$file:${LINE_A+2}:11", // on call to 'foo'
+  "$file:${LINE_A+3}:3", // on call to 'print'
+  "$file:${LINE_A+4}:3", // on call to 'print'
+  "$file:${LINE_A+5}:7", // on call to 'foo'
+  "$file:${LINE_A+6}:3", // on call to 'print'
+  "$file:${LINE_A+7}:11", // on call to 'foo'
+  "$file:${LINE_A+8}:3", // on call to 'print'
+  "$file:${LINE_A+9}:11", // on first call to 'foo'
+  "$file:${LINE_A+9}:18", // on variable 'f'
+  "$file:${LINE_A+9}:25", // on second call to 'foo'
+  "$file:${LINE_A+10}:3", // on call to 'print'
+  "$file:${LINE_A+11}:3", // on call to 'print'
+  "$file:${LINE_A+12}:3", // on call to 'print'
+  "$file:${LINE_A+13}:1" // on ending '}'
+];
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  setBreakpointAtLine(LINE_A),
+  runStepThroughProgramRecordingStops(stops),
+  checkRecordedStops(stops, expected)
+];
+
+main(args) {
+  runIsolateTestsSynchronous(args, tests,
+      testeeConcurrent: code, pause_on_start: true, pause_on_exit: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/next_through_assign_int_test.dart b/runtime/observatory_2/tests/service_2/next_through_assign_int_test.dart
new file mode 100644
index 0000000..c57fd84
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/next_through_assign_int_test.dart
@@ -0,0 +1,57 @@
+// Copyright (c) 2017, 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.
+
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+const int LINE_A = 12;
+const String file = "next_through_assign_int_test.dart";
+
+code() {
+  int a;
+  int b;
+  a = b = 42;
+  print(a);
+  print(b);
+  a = 42;
+  print(a);
+  int d = 42;
+  print(d);
+  int e = 41, f, g = 42;
+  print(e);
+  print(f);
+  print(g);
+}
+
+List<String> stops = [];
+List<String> expected = [
+  "$file:${LINE_A+0}:7", // on variable 'a'
+  "$file:${LINE_A+1}:7", // on variable 'b'
+  "$file:${LINE_A+2}:7", // on 'b'
+  "$file:${LINE_A+3}:3", // on call to 'print'
+  "$file:${LINE_A+4}:3", // on call to 'print'
+  "$file:${LINE_A+5}:3", // on 'a'
+  "$file:${LINE_A+6}:3", // on call to 'print'
+  "$file:${LINE_A+7}:9", // on '='
+  "$file:${LINE_A+8}:3", // on call to 'print'
+  "$file:${LINE_A+9}:9", // on first '='
+  "$file:${LINE_A+9}:15", // on 'f'
+  "$file:${LINE_A+9}:20", // on second '='
+  "$file:${LINE_A+10}:3", // on call to 'print'
+  "$file:${LINE_A+11}:3", // on call to 'print'
+  "$file:${LINE_A+12}:3", // on call to 'print'
+  "$file:${LINE_A+13}:1" // on ending '}'
+];
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  setBreakpointAtLine(LINE_A),
+  runStepThroughProgramRecordingStops(stops),
+  checkRecordedStops(stops, expected)
+];
+
+main(args) {
+  runIsolateTestsSynchronous(args, tests,
+      testeeConcurrent: code, pause_on_start: true, pause_on_exit: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/next_through_await_for_test.dart b/runtime/observatory_2/tests/service_2/next_through_await_for_test.dart
new file mode 100644
index 0000000..734c1f1
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/next_through_await_for_test.dart
@@ -0,0 +1,69 @@
+// Copyright (c) 2019, 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.
+
+import 'dart:async';
+
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+const int LINE = 14;
+const String file = "next_through_await_for_test.dart";
+
+code() async {
+  int count = 0;
+  await for (var num in naturalsTo(2)) {
+    print(num);
+    count++;
+  }
+}
+
+Stream<int> naturalsTo(int n) async* {
+  int k = 0;
+  while (k < n) {
+    k++;
+    yield k;
+  }
+  yield 42;
+}
+
+List<String> stops = [];
+List<String> expected = [
+  "$file:${LINE + 0}:13", // on '='
+  "$file:${LINE + 1}:25", // on 'naturalsTo'
+
+  // Iteration #1
+  "$file:${LINE + 1}:3", // on 'await'
+  "$file:${LINE + 1}:40", // on '{'
+  "$file:${LINE + 2}:5", // on 'print'
+  "$file:${LINE + 3}:10", // on '++'
+
+  // Iteration #2
+  "$file:${LINE + 1}:3", // on 'await'
+  "$file:${LINE + 1}:40", // on '{'
+  "$file:${LINE + 2}:5", // on 'print'
+  "$file:${LINE + 3}:10", // on '++'
+
+  // Iteration #3
+  "$file:${LINE + 1}:3", // on 'await'
+  "$file:${LINE + 1}:40", // on '{'
+  "$file:${LINE + 2}:5", // on 'print'
+  "$file:${LINE + 3}:10", // on '++'
+
+  // Done
+  "$file:${LINE + 1}:3", // on 'await'
+  "$file:${LINE + 5}:1"
+];
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  setBreakpointAtLine(LINE),
+  runStepThroughProgramRecordingStops(stops),
+  checkRecordedStops(stops, expected,
+      debugPrint: true, debugPrintFile: file, debugPrintLine: LINE)
+];
+
+main(args) {
+  runIsolateTestsSynchronous(args, tests,
+      testeeConcurrent: code, pause_on_start: true, pause_on_exit: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/next_through_call_on_field_in_class_test.dart b/runtime/observatory_2/tests/service_2/next_through_call_on_field_in_class_test.dart
new file mode 100644
index 0000000..8f5eef8
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/next_through_call_on_field_in_class_test.dart
@@ -0,0 +1,47 @@
+// Copyright (c) 2017, 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.
+
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+const int LINE_A = 11;
+const String file = "next_through_call_on_field_in_class_test.dart";
+
+code() {
+  var foo = new Foo();
+  foo.foo = foo.fooMethod;
+  foo.fooMethod();
+  foo.foo();
+}
+
+class Foo {
+  var foo;
+
+  void fooMethod() {
+    print("Hello from fooMethod");
+  }
+}
+
+List<String> stops = [];
+List<String> expected = [
+  "$file:${LINE_A+0}:5", // after "code", i.e. on "("
+  "$file:${LINE_A+1}:17", // on "Foo"
+  "$file:${LINE_A+2}:17", // on "fooMethod"
+  "$file:${LINE_A+2}:7", // on "foo"
+  "$file:${LINE_A+3}:7", // on "fooMethod"
+  "$file:${LINE_A+4}:7", // on "foo"
+  "$file:${LINE_A+5}:1" // on ending '}'
+];
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  setBreakpointAtLine(LINE_A),
+  runStepThroughProgramRecordingStops(stops),
+  checkRecordedStops(stops, expected)
+];
+
+main(args) {
+  runIsolateTestsSynchronous(args, tests,
+      testeeConcurrent: code, pause_on_start: true, pause_on_exit: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/next_through_call_on_field_test.dart b/runtime/observatory_2/tests/service_2/next_through_call_on_field_test.dart
new file mode 100644
index 0000000..c47cd82
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/next_through_call_on_field_test.dart
@@ -0,0 +1,42 @@
+// Copyright (c) 2017, 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.
+
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+const int LINE_A = 13;
+const String file = "next_through_call_on_field_test.dart";
+
+var foo;
+
+code() {
+  foo = fooMethod;
+  fooMethod();
+  foo();
+}
+
+void fooMethod() {
+  print("Hello from fooMethod");
+}
+
+List<String> stops = [];
+List<String> expected = [
+  "$file:${LINE_A+0}:5", // after "code", i.e. on "("
+  "$file:${LINE_A+1}:3", // on "foo"
+  "$file:${LINE_A+2}:3", // on "fooMethod"
+  "$file:${LINE_A+3}:6", // after "foo" (on invisible ".call")
+  "$file:${LINE_A+4}:1" // on ending '}'
+];
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  setBreakpointAtLine(LINE_A),
+  runStepThroughProgramRecordingStops(stops),
+  checkRecordedStops(stops, expected)
+];
+
+main(args) {
+  runIsolateTestsSynchronous(args, tests,
+      testeeConcurrent: code, pause_on_start: true, pause_on_exit: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/next_through_call_on_static_field_in_class_test.dart b/runtime/observatory_2/tests/service_2/next_through_call_on_static_field_in_class_test.dart
new file mode 100644
index 0000000..d4b351f
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/next_through_call_on_static_field_in_class_test.dart
@@ -0,0 +1,44 @@
+// Copyright (c) 2017, 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.
+
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+const int LINE_A = 11;
+const String file = "next_through_call_on_static_field_in_class_test.dart";
+
+code() {
+  Foo.foo = Foo.fooMethod;
+  Foo.fooMethod();
+  Foo.foo();
+}
+
+class Foo {
+  static var foo;
+
+  static void fooMethod() {
+    print("Hello from fooMethod");
+  }
+}
+
+List<String> stops = [];
+List<String> expected = [
+  "$file:${LINE_A+0}:5", // after "code", i.e. on "("
+  "$file:${LINE_A+1}:7", // on "foo"
+  "$file:${LINE_A+2}:7", // on "fooMethod"
+  "$file:${LINE_A+3}:10", // after "foo" (on invisible ".call")
+  "$file:${LINE_A+4}:1" // on ending '}'
+];
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  setBreakpointAtLine(LINE_A),
+  runStepThroughProgramRecordingStops(stops),
+  checkRecordedStops(stops, expected)
+];
+
+main(args) {
+  runIsolateTestsSynchronous(args, tests,
+      testeeConcurrent: code, pause_on_start: true, pause_on_exit: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/next_through_catch_test.dart b/runtime/observatory_2/tests/service_2/next_through_catch_test.dart
new file mode 100644
index 0000000..3942e0f
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/next_through_catch_test.dart
@@ -0,0 +1,49 @@
+// Copyright (c) 2017, 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.
+
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+const int LINE_A = 12;
+const String file = "next_through_catch_test.dart";
+
+code() {
+  try {
+    var value = "world";
+    throw "Hello, $value";
+  } catch (e, st) {
+    print(e);
+    print(st);
+  }
+  try {
+    throw "Hello, world";
+  } catch (e, st) {
+    print(e);
+    print(st);
+  }
+}
+
+List<String> stops = [];
+List<String> expected = [
+  "$file:${LINE_A+1}:15", // on '='
+  "$file:${LINE_A+2}:26", // after last '"' (i.e. before ';')
+  "$file:${LINE_A+4}:5", // on call to 'print'
+  "$file:${LINE_A+5}:5", // on call to 'print'
+  "$file:${LINE_A+8}:5", // on 'throw'
+  "$file:${LINE_A+10}:5", // on call to 'print'
+  "$file:${LINE_A+11}:5", // on call to 'print'
+  "$file:${LINE_A+13}:1" // on ending '}'
+];
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  setBreakpointAtLine(LINE_A),
+  runStepThroughProgramRecordingStops(stops),
+  checkRecordedStops(stops, expected)
+];
+
+main(args) {
+  runIsolateTestsSynchronous(args, tests,
+      testeeConcurrent: code, pause_on_start: true, pause_on_exit: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/next_through_closure_test.dart b/runtime/observatory_2/tests/service_2/next_through_closure_test.dart
new file mode 100644
index 0000000..0c7d8ae
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/next_through_closure_test.dart
@@ -0,0 +1,40 @@
+// Copyright (c) 2017, 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.
+
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+const int LINE_A = 14;
+const String file = "next_through_closure_test.dart";
+
+codeXYZ(int i) {
+  var x = () =>
+      // some comment here to allow this formatting
+      i * i; // LINE_A
+  return x();
+}
+
+code() {
+  codeXYZ(42);
+}
+
+List<String> stops = [];
+List<String> expected = [
+  "$file:${LINE_A+0}:9", // on '*'
+  "$file:${LINE_A+0}:7", // on first 'i'
+  "$file:${LINE_A+1}:3", // on 'return'
+  "$file:${LINE_A+6}:1" // on ending '}'
+];
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  setBreakpointAtLine(LINE_A),
+  runStepThroughProgramRecordingStops(stops),
+  checkRecordedStops(stops, expected)
+];
+
+main(args) {
+  runIsolateTestsSynchronous(args, tests,
+      testeeConcurrent: code, pause_on_start: true, pause_on_exit: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/next_through_create_list_and_map_test.dart b/runtime/observatory_2/tests/service_2/next_through_create_list_and_map_test.dart
new file mode 100644
index 0000000..523ef13
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/next_through_create_list_and_map_test.dart
@@ -0,0 +1,91 @@
+// Copyright (c) 2017, 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.
+
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+const int LINE_A = 12;
+const String file = "next_through_create_list_and_map_test.dart";
+
+code() {
+  List<int> myList = [
+    1234567890,
+    1234567891,
+    1234567892,
+    1234567893,
+    1234567894
+  ];
+  List<int> myConstList = const [
+    1234567890,
+    1234567891,
+    1234567892,
+    1234567893,
+    1234567894
+  ];
+  Map<int, int> myMap = {
+    1: 42,
+    2: 43,
+    33242344: 432432432,
+    443243232: 543242454
+  };
+  Map<int, int> myConstMap = const {
+    1: 42,
+    2: 43,
+    33242344: 432432432,
+    443243232: 543242454
+  };
+  print(myList);
+  print(myConstList);
+  int lookup = myMap[1];
+  print(lookup);
+  print(myMap);
+  print(myConstMap);
+  print(myMap[2]);
+}
+
+List<String> stops = [];
+List<String> expected = [
+  // Initialize list (on '[')
+  "$file:${LINE_A+0}:22",
+
+  // Initialize const list (on '=')
+  "$file:${LINE_A+7}:25",
+
+  // Initialize map (on '{')
+  "$file:${LINE_A+14}:25",
+
+  // Initialize const map (on '=')
+  "$file:${LINE_A+20}:28",
+
+  // Prints (on call to 'print')
+  "$file:${LINE_A+26}:3",
+  "$file:${LINE_A+27}:3",
+
+  // Lookup (on '[')
+  "$file:${LINE_A+28}:21",
+
+  // Prints (on call to 'print')
+  "$file:${LINE_A+29}:3",
+  "$file:${LINE_A+30}:3",
+  "$file:${LINE_A+31}:3",
+
+  // Lookup (on '[') + print (on call to 'print')
+  "$file:${LINE_A+32}:14",
+  "$file:${LINE_A+32}:3",
+
+  // End (on ending '}')
+  "$file:${LINE_A+33}:1"
+];
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  setBreakpointAtLine(LINE_A),
+  runStepThroughProgramRecordingStops(stops),
+  checkRecordedStops(stops, expected)
+];
+
+main(args) {
+  runIsolateTestsSynchronous(args, tests,
+      testeeConcurrent: code, pause_on_start: true, pause_on_exit: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/next_through_for_each_loop_test.dart b/runtime/observatory_2/tests/service_2/next_through_for_each_loop_test.dart
new file mode 100644
index 0000000..bce2c73
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/next_through_for_each_loop_test.dart
@@ -0,0 +1,60 @@
+// Copyright (c) 2017, 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.
+
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+const int LINE = 12;
+const String file = "next_through_for_each_loop_test.dart";
+
+code() {
+  List<int> data = [1, 2, 3, 4];
+  for (int datapoint in data) {
+    print(datapoint);
+  }
+}
+
+List<String> stops = [];
+List<String> expected = [
+  // Initialize data (on '[')
+  "$file:${LINE+0}:20",
+
+  // An iteration of the loop is "data", "{", then inside loop
+  // (on call to 'print')
+  "$file:${LINE+1}:25",
+  "$file:${LINE+1}:31",
+  "$file:${LINE+2}:5",
+
+  // Iteration 2
+  "$file:${LINE+1}:25",
+  "$file:${LINE+1}:31",
+  "$file:${LINE+2}:5",
+
+  // Iteration 3
+  "$file:${LINE+1}:25",
+  "$file:${LINE+1}:31",
+  "$file:${LINE+2}:5",
+
+  // Iteration 4
+  "$file:${LINE+1}:25",
+  "$file:${LINE+1}:31",
+  "$file:${LINE+2}:5",
+
+  // End: Apparently we go to data again, then on the final "}"
+  "$file:${LINE+1}:25",
+  "$file:${LINE+4}:1"
+];
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  setBreakpointAtLine(LINE),
+  runStepThroughProgramRecordingStops(stops),
+  checkRecordedStops(stops, expected,
+      debugPrint: true, debugPrintFile: file, debugPrintLine: LINE)
+];
+
+main(args) {
+  runIsolateTestsSynchronous(args, tests,
+      testeeConcurrent: code, pause_on_start: true, pause_on_exit: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/next_through_for_loop_with_break_and_continue_test.dart b/runtime/observatory_2/tests/service_2/next_through_for_loop_with_break_and_continue_test.dart
new file mode 100644
index 0000000..f794803
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/next_through_for_loop_with_break_and_continue_test.dart
@@ -0,0 +1,74 @@
+// Copyright (c) 2017, 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.
+
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+const int LINE_A = 12;
+const String file = "next_through_for_loop_with_break_and_continue_test.dart";
+
+code() {
+  int count = 0;
+  for (int i = 0; i < 42; ++i) {
+    if (i == 2) {
+      continue;
+    }
+    if (i == 3) {
+      break;
+    }
+    count++;
+  }
+  print(count);
+}
+
+List<String> stops = [];
+List<String> expected = [
+  // Initialization (on '='), loop start (on '='),
+  // first iteration (on '<', on '==', on '==', on '++')
+  "$file:${LINE_A+0}:13",
+  "$file:${LINE_A+1}:14",
+  "$file:${LINE_A+1}:21",
+  "$file:${LINE_A+2}:11",
+  "$file:${LINE_A+5}:11",
+  "$file:${LINE_A+8}:10",
+
+  // Second iteration of loop: Full run
+  // (on '++', on '<', on '==', on '==', on '++')
+  "$file:${LINE_A+1}:27",
+  "$file:${LINE_A+1}:21",
+  "$file:${LINE_A+2}:11",
+  "$file:${LINE_A+5}:11",
+  "$file:${LINE_A+8}:10",
+
+  // Third iteration of loop: continue
+  // (on '++', on '<', on '==', on 'continue')
+  "$file:${LINE_A+1}:27",
+  "$file:${LINE_A+1}:21",
+  "$file:${LINE_A+2}:11",
+  "$file:${LINE_A+3}:7",
+
+  // Forth iteration of loop: break
+  // (on '++', on '<', on '==' on '==', on 'break')
+  "$file:${LINE_A+1}:27",
+  "$file:${LINE_A+1}:21",
+  "$file:${LINE_A+2}:11",
+  "$file:${LINE_A+5}:11",
+  "$file:${LINE_A+6}:7",
+
+  // End (on call to 'print' and on ending '}')
+  "$file:${LINE_A+10}:3",
+  "$file:${LINE_A+11}:1"
+];
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  setBreakpointAtLine(LINE_A),
+  runStepThroughProgramRecordingStops(stops),
+  checkRecordedStops(stops, expected)
+];
+
+main(args) {
+  runIsolateTestsSynchronous(args, tests,
+      testeeConcurrent: code, pause_on_start: true, pause_on_exit: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/next_through_function_expression_test.dart b/runtime/observatory_2/tests/service_2/next_through_function_expression_test.dart
new file mode 100644
index 0000000..ef33331
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/next_through_function_expression_test.dart
@@ -0,0 +1,41 @@
+// Copyright (c) 2017, 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.
+
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+const int LINE_A = 11;
+const String file = "next_through_function_expression_test.dart";
+
+codeXYZ(int i) {
+  innerOne() {
+    return i * i;
+  }
+
+  return innerOne();
+}
+
+code() {
+  codeXYZ(42);
+}
+
+List<String> stops = [];
+List<String> expected = [
+  "$file:${LINE_A+0}:13", // on 'i' in 'codeXYZ(int i)'
+  "$file:${LINE_A+1}:3", // on 'innerOne'
+  "$file:${LINE_A+5}:18", // on '(', i.e. after 'innerOne' call
+  "$file:${LINE_A+5}:3" // on 'return'
+];
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  setBreakpointAtLine(LINE_A),
+  runStepThroughProgramRecordingStops(stops),
+  checkRecordedStops(stops, expected)
+];
+
+main(args) {
+  runIsolateTestsSynchronous(args, tests,
+      testeeConcurrent: code, pause_on_start: true, pause_on_exit: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/next_through_implicit_call_test.dart b/runtime/observatory_2/tests/service_2/next_through_implicit_call_test.dart
new file mode 100644
index 0000000..35762fa
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/next_through_implicit_call_test.dart
@@ -0,0 +1,60 @@
+// Copyright (c) 2017, 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.
+
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+const int LINE = 18;
+const String file = "next_through_implicit_call_test.dart";
+
+int _fooCallNumber = 0;
+foo() {
+  ++_fooCallNumber;
+  print("Foo call #$_fooCallNumber!");
+}
+
+code() {
+  foo();
+  (foo)();
+  var a = [foo];
+  a[0]();
+  (a[0])();
+  var b = [
+    [foo, foo]
+  ];
+  b[0][1]();
+  (b[0][1])();
+}
+
+List<String> stops = [];
+List<String> expected = [
+  "$file:${LINE+0}:3", // on 'foo'
+  "$file:${LINE+1}:8", // on '(' (in '()')
+  "$file:${LINE+2}:11", // on '['
+  "$file:${LINE+3}:4", // on '['
+  "$file:${LINE+3}:7", // on '('
+  "$file:${LINE+4}:5", // on '['
+  "$file:${LINE+4}:9", // on '(' (in '()')
+  "$file:${LINE+6}:5", // on '[' (inner one)
+  "$file:${LINE+5}:11", // on '[' (outer one)
+  "$file:${LINE+8}:4", // on first '['
+  "$file:${LINE+8}:7", // on second '['
+  "$file:${LINE+8}:10", // on '('
+  "$file:${LINE+9}:5", // on first '['
+  "$file:${LINE+9}:8", // on second '['
+  "$file:${LINE+9}:12", // on '(' (in '()')
+  "$file:${LINE+10}:1", // on ending '}'
+];
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  setBreakpointAtLine(LINE),
+  runStepThroughProgramRecordingStops(stops),
+  checkRecordedStops(stops, expected)
+];
+
+main(args) {
+  runIsolateTestsSynchronous(args, tests,
+      testeeConcurrent: code, pause_on_start: true, pause_on_exit: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/next_through_is_and_as_test.dart b/runtime/observatory_2/tests/service_2/next_through_is_and_as_test.dart
new file mode 100644
index 0000000..886be73
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/next_through_is_and_as_test.dart
@@ -0,0 +1,66 @@
+// Copyright (c) 2017, 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.
+
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+const int LINE_A = 12;
+const String file = "next_through_is_and_as_test.dart";
+
+code() {
+  var i = 42.42;
+  var hex = 0x42;
+  if (i is int) {
+    print("i is int");
+    int x = i as int;
+    if (x.isEven) {
+      print("it's even even!");
+    } else {
+      print("but it's not even even!");
+    }
+  }
+  if (i is! int) {
+    print("i is not int");
+  }
+  if (hex is int) {
+    print("hex is int");
+    int x = hex as dynamic;
+    if (x.isEven) {
+      print("it's even even!");
+    } else {
+      print("but it's not even even!");
+    }
+  }
+  if (hex is! int) {
+    print("hex is not int");
+  }
+}
+
+List<String> stops = [];
+List<String> expected = [
+  "$file:${LINE_A+0}:9", // on '='
+  "$file:${LINE_A+1}:11", // on '"'
+  "$file:${LINE_A+2}:9", // on 'is'
+  "$file:${LINE_A+11}:9", // on 'is!'
+  "$file:${LINE_A+12}:5", // on call to 'print'
+  "$file:${LINE_A+14}:11", // in 'is'
+  "$file:${LINE_A+15}:5", // on call to 'print'
+  "$file:${LINE_A+16}:11", // on 'as'
+  "$file:${LINE_A+17}:11", // on 'isEven'
+  "$file:${LINE_A+18}:7", // on call to 'print'
+  "$file:${LINE_A+23}:11", // on 'is!'
+  "$file:${LINE_A+26}:1" // on ending '}'
+];
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  setBreakpointAtLine(LINE_A),
+  runStepThroughProgramRecordingStops(stops),
+  checkRecordedStops(stops, expected)
+];
+
+main(args) {
+  runIsolateTestsSynchronous(args, tests,
+      testeeConcurrent: code, pause_on_start: true, pause_on_exit: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/next_through_multi_catch_test.dart b/runtime/observatory_2/tests/service_2/next_through_multi_catch_test.dart
new file mode 100644
index 0000000..855d6b6
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/next_through_multi_catch_test.dart
@@ -0,0 +1,42 @@
+// Copyright (c) 2017, 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.
+
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+const int LINE = 12;
+const String file = "next_through_multi_catch_test.dart";
+
+code() {
+  try {
+    throw "Boom!";
+  } on StateError {
+    print("StateError");
+  } on ArgumentError catch (e) {
+    print("ArgumentError: $e");
+  } catch (e) {
+    print(e);
+  }
+}
+
+List<String> stops = [];
+List<String> expected = [
+  "$file:${LINE+1}:5", // on 'throw'
+  "$file:${LINE+2}:5", // on 'on'
+  "$file:${LINE+4}:5", // on 'on'
+  "$file:${LINE+7}:5", // on 'print'
+  "$file:${LINE+9}:1", // on ending '}'
+];
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  setBreakpointAtLine(LINE),
+  runStepThroughProgramRecordingStops(stops),
+  checkRecordedStops(stops, expected)
+];
+
+main(args) {
+  runIsolateTestsSynchronous(args, tests,
+      testeeConcurrent: code, pause_on_start: true, pause_on_exit: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/next_through_new_test.dart b/runtime/observatory_2/tests/service_2/next_through_new_test.dart
new file mode 100644
index 0000000..115f707
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/next_through_new_test.dart
@@ -0,0 +1,37 @@
+// Copyright (c) 2017, 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.
+
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+const int LINE_A = 11;
+const String file = "next_through_new_test.dart";
+
+code() {
+  var x = new Foo();
+  return x;
+}
+
+class Foo {
+  var x;
+}
+
+List<String> stops = [];
+List<String> expected = [
+  "$file:${LINE_A+0}:5", // on '(' in 'code()'
+  "$file:${LINE_A+1}:15", // on 'Foo'
+  "$file:${LINE_A+2}:3" // on 'return'
+];
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  setBreakpointAtLine(LINE_A),
+  runStepThroughProgramRecordingStops(stops),
+  checkRecordedStops(stops, expected)
+];
+
+main(args) {
+  runIsolateTestsSynchronous(args, tests,
+      testeeConcurrent: code, pause_on_start: true, pause_on_exit: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/next_through_operator_bracket_on_super_test.dart b/runtime/observatory_2/tests/service_2/next_through_operator_bracket_on_super_test.dart
new file mode 100644
index 0000000..b67ae51
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/next_through_operator_bracket_on_super_test.dart
@@ -0,0 +1,50 @@
+// Copyright (c) 2017, 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.
+
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+const int LINE = 22;
+const String file = "next_through_operator_bracket_on_super_test.dart";
+
+class Class2 {
+  operator [](index) => index;
+
+  code() {
+    this[42];
+    return this[42];
+  }
+}
+
+class Class3 extends Class2 {
+  code() {
+    super[42];
+    return super[42];
+  }
+}
+
+code() {
+  Class3 c = new Class3();
+  c[42];
+  c.code();
+}
+
+List<String> stops = [];
+List<String> expected = [
+  "$file:${LINE+0}:10", // on '['
+  "$file:${LINE+1}:17", // on '['
+  "$file:${LINE+1}:5", // on 'return'
+];
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  setBreakpointAtLine(LINE),
+  runStepThroughProgramRecordingStops(stops),
+  checkRecordedStops(stops, expected)
+];
+
+main(args) {
+  runIsolateTestsSynchronous(args, tests,
+      testeeConcurrent: code, pause_on_start: true, pause_on_exit: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/next_through_operator_bracket_on_this_test.dart b/runtime/observatory_2/tests/service_2/next_through_operator_bracket_on_this_test.dart
new file mode 100644
index 0000000..acd3082
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/next_through_operator_bracket_on_this_test.dart
@@ -0,0 +1,43 @@
+// Copyright (c) 2017, 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.
+
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+const int LINE = 15;
+const String file = "next_through_operator_bracket_on_this_test.dart";
+
+class Class2 {
+  operator [](index) => index;
+
+  code() {
+    this[42];
+    return this[42];
+  }
+}
+
+code() {
+  Class2 c = new Class2();
+  c[42];
+  c.code();
+}
+
+List<String> stops = [];
+List<String> expected = [
+  "$file:${LINE+0}:9", // on '['
+  "$file:${LINE+1}:16", // on '['
+  "$file:${LINE+1}:5", // on 'return'
+];
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  setBreakpointAtLine(LINE),
+  runStepThroughProgramRecordingStops(stops),
+  checkRecordedStops(stops, expected)
+];
+
+main(args) {
+  runIsolateTestsSynchronous(args, tests,
+      testeeConcurrent: code, pause_on_start: true, pause_on_exit: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/next_through_operator_bracket_test.dart b/runtime/observatory_2/tests/service_2/next_through_operator_bracket_test.dart
new file mode 100644
index 0000000..164479b
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/next_through_operator_bracket_test.dart
@@ -0,0 +1,44 @@
+// Copyright (c) 2017, 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.
+
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+const int LINE = 21;
+const String file = "next_through_operator_bracket_test.dart";
+
+class Class2 {
+  operator [](index) => index;
+
+  code() {
+    this[42];
+    return this[42];
+  }
+}
+
+code() {
+  Class2 c = new Class2();
+  c[42];
+  c.code();
+}
+
+List<String> stops = [];
+List<String> expected = [
+  "$file:${LINE+0}:18", // after 'new', on 'Class2'
+  "$file:${LINE+1}:4", // on '['
+  "$file:${LINE+2}:5", // on 'code'
+  "$file:${LINE+3}:1" // on ending '}'
+];
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  setBreakpointAtLine(LINE),
+  runStepThroughProgramRecordingStops(stops),
+  checkRecordedStops(stops, expected)
+];
+
+main(args) {
+  runIsolateTestsSynchronous(args, tests,
+      testeeConcurrent: code, pause_on_start: true, pause_on_exit: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/next_through_simple_async_test.dart b/runtime/observatory_2/tests/service_2/next_through_simple_async_test.dart
new file mode 100644
index 0000000..5a3ac87
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/next_through_simple_async_test.dart
@@ -0,0 +1,54 @@
+// Copyright (c) 2017, 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.
+
+// VMOptions=--no-causal-async-stacks --lazy-async-stacks
+// VMOptions=--causal-async-stacks --no-lazy-async-stacks
+
+import 'test_helper.dart';
+import 'service_test_common.dart';
+import 'dart:io';
+
+const int LINE_A = 16;
+const String file = "next_through_simple_async_test.dart";
+
+// line A is at the "code() async" line
+code() async {
+  File f = new File(Platform.script.toFilePath());
+  DateTime modified = await f.lastModified();
+  bool exists = await f.exists();
+  print("Modified: $modified; exists: $exists");
+  foo();
+}
+
+foo() {
+  print("Hello from Foo!");
+}
+
+List<String> stops = [];
+List<String> expected = [
+  "$file:${LINE_A+0}:5", // on '(' in code()'
+  "$file:${LINE_A+1}:30", // on 'script'
+  "$file:${LINE_A+1}:37", // on 'toFilePath'
+  "$file:${LINE_A+1}:16", // on File
+  "$file:${LINE_A+2}:31", // on 'lastModified'
+  "$file:${LINE_A+2}:23", // on 'await'
+  "$file:${LINE_A+3}:25", // on 'exists'
+  "$file:${LINE_A+3}:17", // on 'await'
+  "$file:${LINE_A+4}:47", // on ')', i.e. before ';'
+  "$file:${LINE_A+4}:3", // on call to 'print'
+  "$file:${LINE_A+5}:3", // on call to 'foo'
+  "$file:${LINE_A+6}:1" // ending '}'
+];
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  setBreakpointAtLine(LINE_A),
+  runStepThroughProgramRecordingStops(stops),
+  checkRecordedStops(stops, expected)
+];
+
+main(args) {
+  runIsolateTestsSynchronous(args, tests,
+      testeeConcurrent: code, pause_on_start: true, pause_on_exit: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/next_through_simple_async_with_returns_test.dart b/runtime/observatory_2/tests/service_2/next_through_simple_async_with_returns_test.dart
new file mode 100644
index 0000000..68dca68
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/next_through_simple_async_with_returns_test.dart
@@ -0,0 +1,50 @@
+// Copyright (c) 2017, 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.
+
+// VMOptions=--no-causal-async-stacks --lazy-async-stacks
+// VMOptions=--causal-async-stacks --no-lazy-async-stacks
+
+import 'test_helper.dart';
+import 'service_test_common.dart';
+import 'dart:io';
+
+const int LINE_A = 16;
+const String file = "next_through_simple_async_with_returns_test.dart";
+
+// line A is at the "code() async" line
+code() async {
+  File f = new File(Platform.script.toFilePath());
+  bool exists = await f.exists();
+  if (exists) {
+    return 42;
+  }
+  foo();
+}
+
+foo() {
+  print("Hello from Foo!");
+}
+
+List<String> stops = [];
+List<String> expected = [
+  "$file:${LINE_A+0}:5", // on '(' in 'code()'
+  "$file:${LINE_A+1}:30", // on 'script'
+  "$file:${LINE_A+1}:37", // on 'toFilePath'
+  "$file:${LINE_A+1}:16", // on 'File'
+  "$file:${LINE_A+2}:25", // on 'exists'
+  "$file:${LINE_A+2}:17", // on 'await'
+  "$file:${LINE_A+4}:5" // on 'return'
+];
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  setBreakpointAtLine(LINE_A),
+  runStepThroughProgramRecordingStops(stops),
+  checkRecordedStops(stops, expected)
+];
+
+main(args) {
+  runIsolateTestsSynchronous(args, tests,
+      testeeConcurrent: code, pause_on_start: true, pause_on_exit: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/next_through_simple_linear_2_test.dart b/runtime/observatory_2/tests/service_2/next_through_simple_linear_2_test.dart
new file mode 100644
index 0000000..25bfbd9
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/next_through_simple_linear_2_test.dart
@@ -0,0 +1,42 @@
+// Copyright (c) 2017, 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.
+
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+const int LINE_A = 15;
+const String file = "next_through_simple_linear_2_test.dart";
+
+code() {
+  var a = getA();
+  var b = getB();
+
+  print("Hello, World!"); // LINE_A
+  print("a: $a; b: $b");
+  print("Goodbye, world!");
+}
+
+getA() => true;
+getB() => 42;
+
+List<String> stops = [];
+List<String> expected = [
+  "$file:$LINE_A:3", // on call to 'print'
+  "$file:${LINE_A+1}:23", // on ')', i.e. before ';'
+  "$file:${LINE_A+1}:3", // on call to 'print'
+  "$file:${LINE_A+2}:3", // on call to 'print'
+  "$file:${LINE_A+3}:1" // on ending '}'
+];
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  setBreakpointAtLine(LINE_A),
+  runStepThroughProgramRecordingStops(stops),
+  checkRecordedStops(stops, expected)
+];
+
+main(args) {
+  runIsolateTestsSynchronous(args, tests,
+      testeeConcurrent: code, pause_on_start: true, pause_on_exit: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/next_through_simple_linear_test.dart b/runtime/observatory_2/tests/service_2/next_through_simple_linear_test.dart
new file mode 100644
index 0000000..ae4943e
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/next_through_simple_linear_test.dart
@@ -0,0 +1,35 @@
+// Copyright (c) 2017, 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.
+
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+const int LINE_A = 12;
+const String file = "next_through_simple_linear_test.dart";
+
+code() {
+  print("Hello, World!"); // LINE_A
+  print("Stop here too!");
+  print("Goodbye, world!");
+}
+
+List<String> stops = [];
+List<String> expected = [
+  "$file:${LINE_A+0}:3", // on call to 'print'
+  "$file:${LINE_A+1}:3", // on call to 'print'
+  "$file:${LINE_A+2}:3", // on call to 'print'
+  "$file:${LINE_A+3}:1" // on ending '}'
+];
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  setBreakpointAtLine(LINE_A),
+  runStepThroughProgramRecordingStops(stops),
+  checkRecordedStops(stops, expected)
+];
+
+main(args) {
+  runIsolateTestsSynchronous(args, tests,
+      testeeConcurrent: code, pause_on_start: true, pause_on_exit: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/object_graph_vm_test.dart b/runtime/observatory_2/tests/service_2/object_graph_vm_test.dart
new file mode 100644
index 0000000..d1cb655
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/object_graph_vm_test.dart
@@ -0,0 +1,150 @@
+// Copyright (c) 2014, 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.
+
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/object_graph.dart';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+
+class Foo {
+  // Make sure these fields are not removed by the tree shaker.
+  @pragma("vm:entry-point")
+  dynamic left;
+  @pragma("vm:entry-point")
+  dynamic right;
+}
+
+Foo r;
+
+List lst;
+
+void script() {
+  // Create 3 instances of Foo, with out-degrees
+  // 0 (for b), 1 (for a), and 2 (for staticFoo).
+  r = new Foo();
+  var a = new Foo();
+  var b = new Foo();
+  r.left = a;
+  r.right = b;
+  a.left = b;
+
+  lst = new List(2);
+  lst[0] = lst; // Self-loop.
+  // Larger than any other fixed-size list in a fresh heap.
+  lst[1] = new List(1234569);
+}
+
+var tests = <IsolateTest>[
+  (Isolate isolate) async {
+    var graph = await isolate.fetchHeapSnapshot().done;
+
+    Iterable<SnapshotObject> foos =
+        graph.objects.where((SnapshotObject obj) => obj.klass.name == "Foo");
+    expect(foos.length, equals(3));
+
+    SnapshotObject bVertex = foos.singleWhere((SnapshotObject obj) {
+      List<SnapshotObject> successors = obj.successors.toList();
+      return successors[0].klass.name == "Null" &&
+          successors[1].klass.name == "Null";
+    });
+    SnapshotObject aVertex = foos.singleWhere((SnapshotObject obj) {
+      List<SnapshotObject> successors = obj.successors.toList();
+      return successors[0].klass.name == "Foo" &&
+          successors[1].klass.name == "Null";
+    });
+    SnapshotObject rVertex = foos.singleWhere((SnapshotObject obj) {
+      List<SnapshotObject> successors = obj.successors.toList();
+      return successors[0].klass.name == "Foo" &&
+          successors[1].klass.name == "Foo";
+    });
+
+    // TODO(koda): Check actual byte sizes.
+
+    expect(aVertex.retainedSize, equals(aVertex.shallowSize));
+    expect(bVertex.retainedSize, equals(bVertex.shallowSize));
+    expect(
+        rVertex.retainedSize,
+        equals(
+            aVertex.shallowSize + bVertex.shallowSize + rVertex.shallowSize));
+
+    List<SnapshotObject> lists = new List.from(
+        graph.objects.where((SnapshotObject obj) => obj.klass.name == '_List'));
+    expect(lists.length >= 2, isTrue);
+    // Order by decreasing retained size.
+    lists.sort((u, v) => v.retainedSize - u.retainedSize);
+    SnapshotObject first = lists[0];
+    expect(first.successors.length, greaterThanOrEqualTo(2));
+    SnapshotObject second = lists[1];
+    expect(second.successors.length, greaterThanOrEqualTo(1234569));
+    // Check that the short list retains more than the long list inside.
+    // and specifically, that it retains exactly itself + the long one.
+    expect(first.retainedSize, equals(first.shallowSize + second.shallowSize));
+
+    // Verify sizes of classes are the appropriates sums of their instances.
+    // This also verifies that the class instance iterators are visiting the
+    // correct set of objects (e.g., not including dead objects).
+    for (SnapshotClass klass in graph.classes) {
+      int shallowSum = 0;
+      int internalSum = 0;
+      int externalSum = 0;
+      for (SnapshotObject instance in klass.instances) {
+        if (instance == graph.root) {
+          // The root may have 0 self size.
+          expect(instance.internalSize, greaterThanOrEqualTo(0));
+          expect(instance.externalSize, greaterThanOrEqualTo(0));
+          expect(instance.shallowSize, greaterThanOrEqualTo(0));
+        } else {
+          // All other objects are heap objects with positive size.
+          expect(instance.internalSize, greaterThan(0));
+          expect(instance.externalSize, greaterThanOrEqualTo(0));
+          expect(instance.shallowSize, greaterThan(0));
+        }
+        expect(instance.retainedSize, greaterThan(0));
+        expect(instance.shallowSize,
+            equals(instance.internalSize + instance.externalSize));
+        shallowSum += instance.shallowSize;
+        internalSum += instance.internalSize;
+        externalSum += instance.externalSize;
+      }
+      expect(shallowSum, equals(klass.shallowSize));
+      expect(internalSum, equals(klass.internalSize));
+      expect(externalSum, equals(klass.externalSize));
+      expect(
+          klass.shallowSize, equals(klass.internalSize + klass.externalSize));
+    }
+
+    // Verify sizes of the overall graph are the appropriates sums of all
+    // instances. This also verifies that the all instances iterator is visiting
+    // the correct set of objects (e.g., not including dead objects).
+    int shallowSum = 0;
+    int internalSum = 0;
+    int externalSum = 0;
+    for (SnapshotObject instance in graph.objects) {
+      if (instance == graph.root) {
+        // The root may have 0 self size.
+        expect(instance.internalSize, greaterThanOrEqualTo(0));
+        expect(instance.externalSize, greaterThanOrEqualTo(0));
+        expect(instance.shallowSize, greaterThanOrEqualTo(0));
+      } else {
+        // All other objects are heap objects with positive size.
+        expect(instance.internalSize, greaterThan(0));
+        expect(instance.externalSize, greaterThanOrEqualTo(0));
+        expect(instance.shallowSize, greaterThan(0));
+      }
+      expect(instance.retainedSize, greaterThan(0));
+      expect(instance.shallowSize,
+          equals(instance.internalSize + instance.externalSize));
+      shallowSum += instance.shallowSize;
+      internalSum += instance.internalSize;
+      externalSum += instance.externalSize;
+    }
+    expect(shallowSum, equals(graph.size));
+    expect(internalSum, equals(graph.internalSize));
+    expect(externalSum, equals(graph.externalSize));
+    expect(graph.size, equals(graph.internalSize + graph.externalSize));
+  },
+];
+
+main(args) => runIsolateTests(args, tests, testeeBefore: script);
diff --git a/runtime/observatory_2/tests/service_2/observatory_assets_test.dart b/runtime/observatory_2/tests/service_2/observatory_assets_test.dart
new file mode 100644
index 0000000..249acbb
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/observatory_assets_test.dart
@@ -0,0 +1,29 @@
+// Copyright (c) 2015, 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.
+
+import 'dart:io';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+
+import 'test_helper.dart';
+
+var tests = <VMTest>[
+  (VM vm) async {
+    // Simple heartbeat test to ensure we can fetch Observatory resources.
+    var heartBeatUrl =
+        serviceHttpAddress + '/third_party/trace_viewer_full.html';
+    print('Trying to fetch $heartBeatUrl');
+    HttpClient client = new HttpClient();
+    HttpClientRequest request = await client.getUrl(Uri.parse(heartBeatUrl));
+    HttpClientResponse response = await request.close();
+    expect(response.statusCode, 200);
+    await response.drain();
+  }
+];
+
+main(args) async => runVMTests(
+      args, tests,
+      // TODO(bkonyi): DDS doesn't forward Observatory assets properly yet.
+      enableDds: false,
+    );
diff --git a/runtime/observatory_2/tests/service_2/observatory_test_package_2/.packages b/runtime/observatory_2/tests/service_2/observatory_test_package_2/.packages
new file mode 100644
index 0000000..684be4e
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/observatory_test_package_2/.packages
@@ -0,0 +1 @@
+observatory_test_package:.
\ No newline at end of file
diff --git a/runtime/observatory_2/tests/service_2/observatory_test_package_2/has_part.dart b/runtime/observatory_2/tests/service_2/observatory_test_package_2/has_part.dart
new file mode 100644
index 0000000..f59bb93
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/observatory_test_package_2/has_part.dart
@@ -0,0 +1,22 @@
+// Copyright (c) 2019, 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.
+
+library has_part;
+
+part 'the_part.dart';
+part 'the_part_2.dart';
+
+barz() {
+  print('in bar!');
+}
+
+fooz() {
+  print('in foo!');
+  bar();
+}
+
+main() {
+  Foo10 foo = new Foo10("Foo!");
+  print(foo);
+}
diff --git a/runtime/observatory_2/tests/service_2/observatory_test_package_2/pubspec.yaml b/runtime/observatory_2/tests/service_2/observatory_test_package_2/pubspec.yaml
new file mode 100644
index 0000000..db61a01f
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/observatory_test_package_2/pubspec.yaml
@@ -0,0 +1,4 @@
+name: observatory_test_package
+publish_to: none
+environment:
+  sdk: '^2.7.0'
diff --git a/runtime/observatory_2/tests/service_2/observatory_test_package_2/the_part.dart b/runtime/observatory_2/tests/service_2/observatory_test_package_2/the_part.dart
new file mode 100644
index 0000000..d8545f8
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/observatory_test_package_2/the_part.dart
@@ -0,0 +1,91 @@
+// Copyright (c) 2019, 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 has_part;
+
+void foo() {
+  print("lalala");
+}
+
+class Foo1 {
+  final foo;
+
+  Foo1(this.foo) {
+    print("hello from foo!");
+  }
+}
+
+class Foo2 {
+  final foo;
+
+  Foo2(this.foo) {
+    print("hello from foo!");
+  }
+}
+
+class Foo3 {
+  final foo;
+
+  Foo3(this.foo) {
+    print("hello from foo!");
+  }
+}
+
+class Foo4 {
+  final foo;
+
+  Foo4(this.foo) {
+    print("hello from foo!");
+  }
+}
+
+class Foo5 {
+  final foo;
+
+  Foo5(this.foo) {
+    print("hello from foo!");
+  }
+}
+
+class Foo6 {
+  final foo;
+
+  Foo6(this.foo) {
+    print("hello from foo!");
+  }
+}
+
+class Foo7 {
+  final foo;
+
+  Foo7(this.foo) {
+    print("hello from foo!");
+  }
+}
+
+class Foo8 {
+  final foo;
+
+  Foo8(this.foo) {
+    print("hello from foo!");
+  }
+}
+
+class Foo9 {
+  final foo;
+
+  Foo9(this.foo) {
+    print("hello from foo!");
+  }
+}
+
+class Foo10 {
+  final foo;
+
+  Foo10(this.foo) {
+    print("hello from foo!");
+  }
+}
+
+var foo2 = foo() as dynamic;
diff --git a/runtime/observatory_2/tests/service_2/observatory_test_package_2/the_part_2.dart b/runtime/observatory_2/tests/service_2/observatory_test_package_2/the_part_2.dart
new file mode 100644
index 0000000..c38df9c
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/observatory_test_package_2/the_part_2.dart
@@ -0,0 +1,9 @@
+// Copyright (c) 2019, 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 has_part;
+
+void bar() {
+  print('Should break here');
+}
diff --git a/runtime/observatory_2/tests/service_2/parameters_in_scope_at_entry_test.dart b/runtime/observatory_2/tests/service_2/parameters_in_scope_at_entry_test.dart
new file mode 100644
index 0000000..d6c2d7b
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/parameters_in_scope_at_entry_test.dart
@@ -0,0 +1,72 @@
+// Copyright (c) 2015, 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.
+// VMOptions=--verbose_debug
+
+import 'package:observatory_2/service_io.dart';
+import 'test_helper.dart';
+import 'dart:developer';
+import 'package:test/test.dart';
+
+import 'service_test_common.dart';
+
+const int LINE_A = 30;
+const int LINE_B = 34;
+
+foo(param) {
+  return param;
+}
+
+fooClosure() {
+  theClosureFunction(param) {
+    return param;
+  }
+
+  return theClosureFunction;
+}
+
+testMain() {
+  debugger();
+  foo("in-scope"); // Line A.
+
+  var f = fooClosure();
+  debugger();
+  f("in-scope"); // Line B.
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_A),
+  (isolate) => isolate.stepInto(),
+  hasStoppedAtBreakpoint,
+  (isolate) async {
+    var stack = await isolate.getStack();
+    Frame top = stack['frames'][0];
+    print(top);
+    expect(top.function.name, equals("foo"));
+    print(top.variables);
+    expect(top.variables.length, equals(1));
+    var param = top.variables[0];
+    expect(param['name'], equals("param"));
+    expect(param['value'].valueAsString, equals("in-scope"));
+  },
+  resumeIsolate,
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_B),
+  (isolate) => isolate.stepInto(),
+  hasStoppedAtBreakpoint,
+  (isolate) async {
+    var stack = await isolate.getStack();
+    Frame top = stack['frames'][0];
+    print(top);
+    expect(top.function.name, equals("theClosureFunction"));
+    print(top.variables);
+    expect(top.variables.length, equals(1));
+    var param = top.variables[0];
+    expect(param['name'], equals("param"));
+    expect(param['value'].valueAsString, equals("in-scope"));
+  },
+  resumeIsolate,
+];
+
+main(args) => runIsolateTests(args, tests, testeeConcurrent: testMain);
diff --git a/runtime/observatory_2/tests/service_2/pause_idle_isolate_test.dart b/runtime/observatory_2/tests/service_2/pause_idle_isolate_test.dart
new file mode 100644
index 0000000..06b0689
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/pause_idle_isolate_test.dart
@@ -0,0 +1,52 @@
+// Copyright (c) 2015, 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.
+
+import 'dart:developer';
+import 'dart:io';
+import 'dart:isolate' show ReceivePort;
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+var receivePort;
+
+void testMain() {
+  receivePort = new ReceivePort();
+  debugger();
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  (Isolate isolate) async {
+    print('Resuming...');
+    await isolate.resume();
+
+    // Wait for the isolate to become idle.  We detect this by querying
+    // the stack until it becomes empty.
+    var frameCount;
+    do {
+      var stack = await isolate.getStack();
+      frameCount = stack['frames'].length;
+      print('Frames: $frameCount');
+      sleep(const Duration(milliseconds: 10));
+    } while (frameCount > 0);
+    print('Isolate is idle.');
+    await isolate.reload();
+    expect(isolate.pauseEvent is M.ResumeEvent, isTrue);
+
+    // Make sure that the isolate receives an interrupt even when it is
+    // idle. (https://github.com/dart-lang/sdk/issues/24349)
+    var interruptFuture = hasPausedFor(isolate, ServiceEvent.kPauseInterrupted);
+    print('Pausing...');
+    await isolate.pause();
+    await interruptFuture;
+  },
+];
+
+main(args) => runIsolateTests(args, tests,
+    testeeConcurrent: testMain,
+    verbose_vm: true,
+    extraArgs: ['--trace-service', '--trace-service-verbose']);
diff --git a/runtime/observatory_2/tests/service_2/pause_on_exceptions_test.dart b/runtime/observatory_2/tests/service_2/pause_on_exceptions_test.dart
new file mode 100644
index 0000000..fa506c9
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/pause_on_exceptions_test.dart
@@ -0,0 +1,105 @@
+// Copyright (c) 2015, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+import 'dart:async';
+
+doThrow() {
+  throw "TheException"; // Line 13.
+}
+
+doCaught() {
+  try {
+    doThrow();
+  } catch (e) {}
+  return "end of doCaught";
+}
+
+doUncaught() {
+  doThrow();
+  return "end of doUncaught";
+}
+
+var tests = <IsolateTest>[
+  (Isolate isolate) async {
+    Library lib = await isolate.rootLibrary.reload();
+
+    var onPaused = null;
+    var onResume = null;
+
+    var stream = await isolate.vm.getEventStream(VM.kDebugStream);
+    var subscription;
+    subscription = stream.listen((ServiceEvent event) {
+      print("Event $event");
+      if (event.kind == ServiceEvent.kPauseException) {
+        if (onPaused == null) throw "Unexpected pause event $event";
+        var t = onPaused;
+        onPaused = null;
+        t.complete(event);
+      }
+      if (event.kind == ServiceEvent.kResume) {
+        if (onResume == null) throw "Unexpected resume event $event";
+        var t = onResume;
+        onResume = null;
+        t.complete(event);
+      }
+    });
+
+    test(String pauseMode, String expression, bool shouldPause,
+        bool shouldBeCaught) async {
+      print("Evaluating $expression with pause on $pauseMode exception");
+
+      expect((await isolate.setExceptionPauseMode(pauseMode)) is DartError,
+          isFalse);
+
+      var t;
+      if (shouldPause) {
+        t = new Completer();
+        onPaused = t;
+      }
+      var fres = lib.evaluate(expression);
+      if (shouldPause) {
+        await t.future;
+
+        var stack = await isolate.getStack();
+        expect(stack['frames'][0].function.name, equals('doThrow'));
+        // Ugh, no .line. expect(stack['frames'][0].location.line, equals(17));
+
+        t = new Completer();
+        onResume = t;
+        isolate.resume();
+        await t.future;
+      }
+
+      dynamic res = await fres;
+      print(res);
+      if (shouldBeCaught) {
+        expect(res.isInstance, isTrue);
+        expect(res.isString, isTrue);
+        expect(res.valueAsString, equals("end of doCaught"));
+      } else {
+        expect(res.isError, isTrue);
+        await res.load(); // Weird?
+        expect(res.exception.isInstance, isTrue);
+        expect(res.exception.isString, isTrue);
+        expect(res.exception.valueAsString, equals("TheException"));
+      }
+    }
+
+    await test("All", "doCaught()", true, true);
+    await test("All", "doUncaught()", true, false);
+
+    await test("Unhandled", "doCaught()", false, true);
+    await test("Unhandled", "doUncaught()", true, false);
+
+    await test("None", "doCaught()", false, true);
+    await test("None", "doUncaught()", false, false);
+
+    subscription.cancel();
+  },
+];
+
+main(args) => runIsolateTests(args, tests);
diff --git a/runtime/observatory_2/tests/service_2/pause_on_start_and_exit_test.dart b/runtime/observatory_2/tests/service_2/pause_on_start_and_exit_test.dart
new file mode 100644
index 0000000..b4a1dd9
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/pause_on_start_and_exit_test.dart
@@ -0,0 +1,86 @@
+// Copyright (c) 2015, 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.
+
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+import 'dart:async';
+
+void testMain() {
+  print('Hello');
+}
+
+var tests = <IsolateTest>[
+  (Isolate isolate) async {
+    print('Getting stream...');
+    Completer completer = new Completer();
+    var stream = await isolate.vm.getEventStream(VM.kDebugStream);
+    print('Subscribing...');
+    var subscription;
+    subscription = stream.listen((ServiceEvent event) {
+      if (event.kind == ServiceEvent.kPauseStart) {
+        print('Received $event');
+        subscription.cancel();
+        completer.complete();
+      } else {
+        print('Ignoring event $event');
+      }
+    });
+    print('Subscribed.  Pause event is ${isolate.pauseEvent}');
+
+    if (isolate.pauseEvent != null && isolate.pauseEvent is M.PauseStartEvent) {
+      // Wait for the isolate to hit PauseStart.
+      subscription.cancel();
+      print('Subscription cancelled.');
+    } else {
+      print('Waiting for pause start event.');
+      await completer.future;
+    }
+    print('Done waiting for pause event.');
+
+    // Grab the timestamp.
+    var pausetime1 = isolate.pauseEvent.timestamp;
+    expect(pausetime1, isNotNull);
+    // Reload the isolate.
+    await isolate.reload();
+    // Verify that it is the same.
+    expect(pausetime1.millisecondsSinceEpoch,
+        equals(isolate.pauseEvent.timestamp.millisecondsSinceEpoch));
+
+    completer = new Completer();
+    stream = await isolate.vm.getEventStream(VM.kDebugStream);
+    subscription = stream.listen((ServiceEvent event) {
+      if (event.kind == ServiceEvent.kPauseExit) {
+        print('Received PauseExit');
+        subscription.cancel();
+        completer.complete();
+      }
+    });
+
+    print('Resuming...');
+    isolate.resume();
+
+    // Wait for the isolate to hit PauseExit.
+    await completer.future;
+
+    // Grab the timestamp.
+    var pausetime2 = isolate.pauseEvent.timestamp;
+    expect(pausetime2, isNotNull);
+    // Reload the isolate.
+    await isolate.reload();
+    // Verify that it is the same.
+    expect(pausetime2.millisecondsSinceEpoch,
+        equals(isolate.pauseEvent.timestamp.millisecondsSinceEpoch));
+    expect(pausetime2.millisecondsSinceEpoch,
+        greaterThan(pausetime1.millisecondsSinceEpoch));
+  },
+];
+
+main(args) => runIsolateTests(args, tests,
+    testeeConcurrent: testMain,
+    pause_on_start: true,
+    pause_on_exit: true,
+    verbose_vm: true,
+    extraArgs: ['--trace-service', '--trace-service-verbose']);
diff --git a/runtime/observatory_2/tests/service_2/pause_on_start_and_exit_with_child_test.dart b/runtime/observatory_2/tests/service_2/pause_on_start_and_exit_with_child_test.dart
new file mode 100644
index 0000000..c2f9bde
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/pause_on_start_and_exit_with_child_test.dart
@@ -0,0 +1,116 @@
+// Copyright (c) 2018, 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.
+
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+import 'dart:async';
+import 'dart:isolate' as isolate;
+
+void child(message) {
+  print("Child got initial message");
+  message.send(null);
+}
+
+void testMain() {
+  var port = new isolate.RawReceivePort();
+  port.handler = (message) {
+    print("Parent got response");
+    port.close();
+  };
+
+  isolate.Isolate.spawn(child, port.sendPort);
+}
+
+var tests = <IsolateTest>[
+  (Isolate isolate) async {
+    VM vm = isolate.vm;
+
+    print('Getting stream...');
+    Completer completer = new Completer();
+    var stream = await vm.getEventStream(VM.kDebugStream);
+    print('Subscribing...');
+    var subscription;
+    subscription = stream.listen((ServiceEvent event) {
+      if (event.isolate == isolate && event.kind == ServiceEvent.kPauseStart) {
+        print('Received $event');
+        subscription.cancel();
+        completer.complete();
+      } else {
+        print('Ignoring event $event');
+      }
+    });
+    print('Subscribed.  Pause event is ${isolate.pauseEvent}');
+
+    if (isolate.pauseEvent != null && isolate.pauseEvent is M.PauseStartEvent) {
+      // Wait for the isolate to hit PauseStart.
+      subscription.cancel();
+      print('Subscription cancelled.');
+    } else {
+      print('Waiting for pause start event.');
+      await completer.future;
+    }
+    print('Done waiting for pause event.');
+
+    expect(isolate.pauseEvent, isNotNull);
+    expect(isolate.pauseEvent is M.PauseStartEvent, isTrue);
+
+    print("Disabling pause_isolates_on_start");
+    var params = {
+      'name': 'pause_isolates_on_start',
+      'value': 'false',
+    };
+    var result = await vm.invokeRpcNoUpgrade('setFlag', params);
+    expect(result['type'], equals('Success'));
+
+    print("Disabling pause_isolates_on_exit");
+    params = {
+      'name': 'pause_isolates_on_exit',
+      'value': 'false',
+    };
+    result = await vm.invokeRpcNoUpgrade('setFlag', params);
+    expect(result['type'], equals('Success'));
+
+    completer = new Completer();
+    stream = await vm.getEventStream(VM.kDebugStream);
+    subscription = stream.listen((ServiceEvent event) {
+      if (event.isolate == isolate && event.kind == ServiceEvent.kPauseExit) {
+        print('Received PauseExit');
+        subscription.cancel();
+        completer.complete();
+      }
+    });
+
+    print('Resuming at start...');
+    isolate.resume();
+
+    // Wait for the isolate to hit PauseExit.
+    await completer.future;
+
+    expect(isolate.pauseEvent, isNotNull);
+    expect(isolate.pauseEvent is M.PauseExitEvent, isTrue);
+
+    print('Resuming at exit...');
+    isolate.resume();
+
+    // Nothing else keeping the VM around. In particular, the child isolate
+    // won't pause on exit.
+    await vm.onDisconnect;
+  },
+];
+
+main(args) => runIsolateTests(
+      args, tests,
+      testeeConcurrent: testMain,
+      pause_on_start: true,
+      pause_on_exit: true,
+      verbose_vm: true,
+      extraArgs: [
+        '--trace-service',
+        '--trace-service-verbose',
+      ],
+      // TODO(bkonyi): investigate failure.
+      enableDds: false,
+    );
diff --git a/runtime/observatory_2/tests/service_2/pause_on_start_then_step_test.dart b/runtime/observatory_2/tests/service_2/pause_on_start_then_step_test.dart
new file mode 100644
index 0000000..c80ad79
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/pause_on_start_then_step_test.dart
@@ -0,0 +1,94 @@
+// Copyright (c) 2015, 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.
+
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+import 'dart:async';
+
+void testMain() {
+  print('Hello');
+}
+
+var tests = <IsolateTest>[
+  (Isolate isolate) async {
+    print('Getting stream...');
+    Completer completer = new Completer();
+    var stream = await isolate.vm.getEventStream(VM.kDebugStream);
+    print('Subscribing...');
+    var subscription;
+    subscription = stream.listen((ServiceEvent event) {
+      if (event.kind == ServiceEvent.kPauseStart) {
+        print('Received $event');
+        subscription.cancel();
+        completer.complete();
+      } else {
+        print('Ignoring event $event');
+      }
+    });
+    print('Subscribed.  Pause event is ${isolate.pauseEvent}');
+
+    if (isolate.pauseEvent != null && isolate.pauseEvent is M.PauseStartEvent) {
+      // Wait for the isolate to hit PauseStart.
+      subscription.cancel();
+      print('Subscription cancelled.');
+    } else {
+      print('Waiting for pause start event.');
+      await completer.future;
+    }
+    print('Done waiting for pause event.');
+
+    // Grab the timestamp.
+    var pausetime1 = isolate.pauseEvent.timestamp;
+    expect(pausetime1, isNotNull);
+    // Reload the isolate.
+    await isolate.reload();
+    // Verify that it is the same.
+    expect(pausetime1.millisecondsSinceEpoch,
+        equals(isolate.pauseEvent.timestamp.millisecondsSinceEpoch));
+
+    completer = new Completer();
+    stream = await isolate.vm.getEventStream(VM.kDebugStream);
+    subscription = stream.listen((ServiceEvent event) {
+      if (event.kind == ServiceEvent.kPauseBreakpoint) {
+        print('Received PauseBreakpoint');
+        subscription.cancel();
+        completer.complete();
+      }
+      print('Got ${event.kind}');
+    });
+
+    print('Stepping...');
+    isolate.stepInto();
+
+    // Wait for the isolate to hit PauseBreakpoint.
+    print('Waiting for PauseBreakpoint');
+    await completer.future;
+
+    // Grab the timestamp.
+    print('Getting pausevent timestamp');
+    var pausetime2 = isolate.pauseEvent.timestamp;
+    expect(pausetime2, isNotNull);
+    // Reload the isolate.
+    print('Reloading isolate');
+    await isolate.reload();
+    print('Reload finished');
+    // Verify that it is the same.
+    expect(pausetime2.millisecondsSinceEpoch,
+        equals(isolate.pauseEvent.timestamp.millisecondsSinceEpoch));
+
+    expect(pausetime2.millisecondsSinceEpoch,
+        greaterThan(pausetime1.millisecondsSinceEpoch));
+
+    print('Test finished');
+  },
+];
+
+main(args) => runIsolateTests(args, tests,
+    testeeConcurrent: testMain,
+    pause_on_start: true,
+    pause_on_exit: true,
+    verbose_vm: true,
+    extraArgs: ['--trace-service', '--trace-service-verbose']);
diff --git a/runtime/observatory_2/tests/service_2/pause_on_unhandled_async_exceptions2_test.dart b/runtime/observatory_2/tests/service_2/pause_on_unhandled_async_exceptions2_test.dart
new file mode 100644
index 0000000..fa4bf52
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/pause_on_unhandled_async_exceptions2_test.dart
@@ -0,0 +1,66 @@
+// Copyright (c) 2017, 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.
+// VMOptions=--async_debugger --no-causal-async-stacks --lazy-async-stacks
+// VMOptions=--async_debugger --causal-async-stacks --no-lazy-async-stacks
+// VMOptions=--async_debugger --no-causal-async-stacks --lazy-async-stacks --optimization-counter-threshold=5
+// VMOptions=--async_debugger --causal-async-stacks --no-lazy-async-stacks --optimization-counter-threshold=5
+
+import 'package:observatory_2/service_io.dart';
+import 'package:observatory_2/models.dart' as M;
+import 'package:test/test.dart';
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+const LINE_A = 37;
+
+class Foo {}
+
+doThrow() {
+  throw "TheException"; // Line 13.
+  return "end of doThrow";
+}
+
+asyncThrower() async {
+  await 0; // force async gap
+  doThrow();
+}
+
+testeeMain() async {
+  // Trigger optimization via OSR.
+  var s = 0;
+  for (var i = 0; i < 100; i++) {
+    s += i;
+  }
+  print(s);
+  // No try ... catch.
+  await asyncThrower(); // LINE_A
+}
+
+var tests = <IsolateTest>[
+  hasStoppedWithUnhandledException,
+  (Isolate isolate) async {
+    print("We stopped!");
+    var stack = await isolate.getStack();
+    expect(stack['asyncCausalFrames'], isNotNull);
+    var asyncStack = stack['asyncCausalFrames'];
+    expect(asyncStack.length, greaterThanOrEqualTo(4));
+    expect(asyncStack[0].toString(), contains('doThrow'));
+    expect(asyncStack[1].toString(), contains('asyncThrower'));
+    expect(asyncStack[2].kind, equals(M.FrameKind.asyncSuspensionMarker));
+    expect(asyncStack[3].toString(), contains('testeeMain'));
+    expect(
+        await asyncStack[3].location.toUserString(), contains('.dart:$LINE_A'));
+  }
+];
+
+main(args) => runIsolateTests(
+      args,
+      tests,
+      pause_on_unhandled_exceptions: true,
+      testeeConcurrent: testeeMain,
+      extraArgs: extraDebuggingArgs,
+      // TODO(bkonyi): causes ASSERT in debug mode, unrelated to DDS.
+      // See https://github.com/dart-lang/sdk/issues/41379.
+      enableDds: false,
+    );
diff --git a/runtime/observatory_2/tests/service_2/pause_on_unhandled_async_exceptions3_test.dart b/runtime/observatory_2/tests/service_2/pause_on_unhandled_async_exceptions3_test.dart
new file mode 100644
index 0000000..ea5a7cc
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/pause_on_unhandled_async_exceptions3_test.dart
@@ -0,0 +1,62 @@
+// Copyright (c) 2019, 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.
+
+// Verify that debugger can stop on an unhandled exception thrown from async
+// function. Regression test for https://dartbug.com/38697.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+const LINE_A = 18;
+const LINE_B = 23;
+const LINE_C = 26;
+
+throwException() async {
+  throw 'exception'; // LINE_A
+}
+
+testeeMain() async {
+  try {
+    await throwException(); // LINE_B
+  } finally {
+    try {
+      await throwException(); // LINE_C
+    } finally {}
+  }
+}
+
+var tests = <IsolateTest>[
+  hasStoppedWithUnhandledException,
+  stoppedAtLine(LINE_A),
+  (Isolate isolate) async {
+    print("Stopped!");
+    var stack = await isolate.getStack();
+    expect(stack['frames'][0].function.toString(), contains('throwException'));
+  },
+  resumeIsolate,
+  hasStoppedWithUnhandledException,
+  (Isolate isolate) async {
+    var stack = await isolate.getStack();
+    print(stack['frames'][0]);
+    // await in testeeMain
+    expect(await stack['frames'][0].location.toUserString(),
+        contains('.dart:${LINE_B}'));
+  },
+  resumeIsolate,
+  hasStoppedWithUnhandledException,
+  stoppedAtLine(LINE_A),
+  resumeIsolate,
+  hasStoppedWithUnhandledException,
+  (Isolate isolate) async {
+    var stack = await isolate.getStack();
+    print(stack['frames'][0]);
+    expect(await stack['frames'][0].location.toUserString(),
+        contains('.dart:${LINE_C}'));
+  }
+];
+
+main(args) => runIsolateTests(args, tests,
+    pause_on_unhandled_exceptions: true, testeeConcurrent: testeeMain);
diff --git a/runtime/observatory_2/tests/service_2/pause_on_unhandled_async_exceptions_test.dart b/runtime/observatory_2/tests/service_2/pause_on_unhandled_async_exceptions_test.dart
new file mode 100644
index 0000000..da16126
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/pause_on_unhandled_async_exceptions_test.dart
@@ -0,0 +1,69 @@
+// Copyright (c) 2017, 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.
+// VMOptions=--async_debugger --no-causal-async-stacks --lazy-async-stacks
+// VMOptions=--async_debugger --causal-async-stacks --no-lazy-async-stacks
+
+import 'package:observatory_2/service_io.dart';
+import 'package:observatory_2/models.dart' as M;
+import 'package:test/test.dart';
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+const LINE_A = 36;
+
+class Foo {}
+
+doThrow() {
+  throw "TheException"; // Line 13.
+  return "end of doThrow";
+}
+
+asyncThrower() async {
+  await 0; // force async gap
+  doThrow();
+}
+
+testeeMain() async {
+  try {
+    // caught.
+    try {
+      await asyncThrower();
+    } catch (e) {}
+
+    // uncaught.
+    try {
+      await asyncThrower(); // LINE_A.
+    } on double catch (e) {}
+  } on Foo catch (e) {}
+}
+
+var tests = <IsolateTest>[
+  hasStoppedWithUnhandledException,
+  (Isolate isolate) async {
+    print("We stopped!");
+    var stack = await isolate.getStack();
+    expect(stack['asyncCausalFrames'], isNotNull);
+    var asyncStack = stack['asyncCausalFrames'];
+    if (useCausalAsyncStacks) {
+      expect(asyncStack.length, greaterThanOrEqualTo(4));
+      expect(asyncStack[0].toString(), contains('doThrow'));
+      expect(asyncStack[1].toString(), contains('asyncThrower'));
+      expect(asyncStack[2].kind, equals(M.FrameKind.asyncSuspensionMarker));
+      expect(asyncStack[3].toString(), contains('testeeMain'));
+      // We've stopped at LINE_A.
+      expect(await asyncStack[3].location.toUserString(),
+          contains('.dart:$LINE_A'));
+    } else {
+      expect(asyncStack.length, greaterThanOrEqualTo(2));
+      expect(asyncStack[0].toString(), contains('doThrow'));
+      expect(asyncStack[1].toString(), contains('asyncThrower'));
+      // There was no await'er for "doThrow()".
+    }
+  }
+];
+
+main(args) => runIsolateTests(args, tests,
+    pause_on_unhandled_exceptions: true,
+    testeeConcurrent: testeeMain,
+    extraArgs: extraDebuggingArgs);
diff --git a/runtime/observatory_2/tests/service_2/pause_on_unhandled_exceptions_test.dart b/runtime/observatory_2/tests/service_2/pause_on_unhandled_exceptions_test.dart
new file mode 100644
index 0000000..eeb81e6
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/pause_on_unhandled_exceptions_test.dart
@@ -0,0 +1,25 @@
+// Copyright (c) 2015, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+doThrow() {
+  throw "TheException"; // Line 13.
+  return "end of doThrow";
+}
+
+var tests = <IsolateTest>[
+  hasStoppedWithUnhandledException,
+  (Isolate isolate) async {
+    print("We stopped!");
+    var stack = await isolate.getStack();
+    expect(stack['frames'][0].function.name, equals('doThrow'));
+  }
+];
+
+main(args) => runIsolateTestsSynchronous(args, tests,
+    pause_on_unhandled_exceptions: true, testeeConcurrent: doThrow);
diff --git a/runtime/observatory_2/tests/service_2/positive_token_pos_test.dart b/runtime/observatory_2/tests/service_2/positive_token_pos_test.dart
new file mode 100644
index 0000000..ed4d46e
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/positive_token_pos_test.dart
@@ -0,0 +1,43 @@
+// Copyright (c) 2016, 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.
+// VMOptions=--verbose_debug
+
+import 'dart:developer';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+helper() async {
+  // Line 12, Col 16 is the open brace.
+}
+
+testMain() {
+  debugger();
+  helper(); // Line 18, Col 3 is the position of the function call.
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(18),
+  stepInto,
+  (Isolate isolate) async {
+    ServiceMap stack = await isolate.getStack();
+    expect(stack['frames'].length, greaterThan(2));
+
+    // We used to return a negative token position for this frame.
+    // See issue #27128.
+    var frame = stack['frames'][0];
+    expect(frame.function.qualifiedName, equals('helper.async_op'));
+    expect(await frame.location.getLine(), equals(14));
+    expect(await frame.location.getColumn(), equals(1));
+
+    frame = stack['frames'][1];
+    expect(frame.function.name, equals('testMain'));
+    expect(await frame.location.getLine(), equals(18));
+    expect(await frame.location.getColumn(), equals(3));
+  }
+];
+
+main(args) => runIsolateTests(args, tests, testeeConcurrent: testMain);
diff --git a/runtime/observatory_2/tests/service_2/process_service_test.dart b/runtime/observatory_2/tests/service_2/process_service_test.dart
new file mode 100644
index 0000000..b008313
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/process_service_test.dart
@@ -0,0 +1,141 @@
+// Copyright (c) 2015, 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.
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:developer';
+import 'dart:io' as io;
+
+import 'package:observatory_2/service_io.dart';
+import 'package:path/path.dart' as path;
+import 'package:test/test.dart';
+
+import 'test_helper.dart';
+
+final dartJITBinary = path.join(path.dirname(io.Platform.resolvedExecutable),
+    'dart' + path.extension(io.Platform.resolvedExecutable));
+
+Future setupProcesses() async {
+  final dir = await io.Directory.systemTemp.createTemp('file_service');
+
+  final args = [
+    ...io.Platform.executableArguments,
+    '--pause_isolates_on_start',
+    io.Platform.script.toFilePath(),
+  ];
+  io.Process process1;
+  io.Process process2;
+  io.Process process3;
+
+  void closeDown() {
+    if (process1 != null) {
+      process1.kill();
+    }
+    if (process2 != null) {
+      process2.kill();
+    }
+    if (process3 != null) {
+      process3.kill();
+    }
+    dir.deleteSync(recursive: true);
+  }
+
+  Future<ServiceExtensionResponse> cleanup(ignored_a, ignored_b) {
+    closeDown();
+    final result = jsonEncode({'type': 'foobar'});
+    return Future.value(ServiceExtensionResponse.result(result));
+  }
+
+  Future<ServiceExtensionResponse> setup(ignored_a, ignored_b) async {
+    try {
+      process1 = await io.Process.start(io.Platform.executable, args);
+      process2 =
+          await io.Process.start(io.Platform.executable, args..add('foobar'));
+      final codeFilePath = dir.path + io.Platform.pathSeparator + "other_file";
+      final codeFile = io.File(codeFilePath);
+      await codeFile.writeAsString('''
+          import "dart:io";
+
+          void main() async {
+            await stdin.drain();
+          }
+          ''');
+      process3 = await io.Process.start(
+          dartJITBinary, [...io.Platform.executableArguments, codeFilePath]);
+    } catch (_) {
+      closeDown();
+      rethrow;
+    }
+
+    final result = jsonEncode({
+      'type': 'foobar',
+      'pids': [process1.pid, process2.pid, process3.pid]
+    });
+    return Future.value(ServiceExtensionResponse.result(result));
+  }
+
+  Future<ServiceExtensionResponse> closeStdin(ignored_a, ignored_b) {
+    process3.stdin.close();
+    return process3.exitCode.then<ServiceExtensionResponse>((int exit) {
+      final result = jsonEncode({'type': 'foobar'});
+      return ServiceExtensionResponse.result(result);
+    });
+  }
+
+  registerExtension('ext.dart.io.cleanup', cleanup);
+  registerExtension('ext.dart.io.setup', setup);
+  registerExtension('ext.dart.io.closeStdin', closeStdin);
+}
+
+final processTests = <IsolateTest>[
+  // Initial.
+  (Isolate isolate) async {
+    final setup = await isolate.invokeRpcNoUpgrade('ext.dart.io.setup', {});
+    try {
+      var all = await isolate
+          .invokeRpcNoUpgrade('ext.dart.io.getSpawnedProcesses', {});
+      expect(all['type'], equals('SpawnedProcessList'));
+
+      expect(all['processes'].length, equals(3));
+
+      final first = await isolate.invokeRpcNoUpgrade(
+          'ext.dart.io.getSpawnedProcessById',
+          {'id': all['processes'][0]['id']});
+      expect(first['name'], io.Platform.executable);
+      expect(first['pid'], equals(setup['pids'][0]));
+      expect(first['arguments'].contains('foobar'), isFalse);
+      expect(first['startedAt'], greaterThan(0));
+
+      final second = await isolate.invokeRpcNoUpgrade(
+          'ext.dart.io.getSpawnedProcessById',
+          {'id': all['processes'][1]['id']});
+      expect(second['name'], io.Platform.executable);
+      expect(second['pid'], equals(setup['pids'][1]));
+      expect(second['arguments'].contains('foobar'), isTrue);
+      expect(second['pid'] != first['pid'], isTrue);
+      expect(second['startedAt'], greaterThan(0));
+      expect(second['startedAt'], greaterThanOrEqualTo(first['startedAt']));
+
+      final third = await isolate.invokeRpcNoUpgrade(
+          'ext.dart.io.getSpawnedProcessById',
+          {'id': all['processes'][2]['id']});
+      expect(third['name'], dartJITBinary);
+      expect(third['pid'], equals(setup['pids'][2]));
+      expect(third['pid'] != first['pid'], isTrue);
+      expect(third['pid'] != second['pid'], isTrue);
+      expect(third['startedAt'], greaterThanOrEqualTo(second['startedAt']));
+
+      await isolate.invokeRpcNoUpgrade('ext.dart.io.closeStdin', {});
+      all = await isolate
+          .invokeRpcNoUpgrade('ext.dart.io.getSpawnedProcesses', {});
+      expect(all['type'], equals('SpawnedProcessList'));
+      expect(all['processes'].length, equals(2));
+    } finally {
+      await isolate.invokeRpcNoUpgrade('ext.dart.io.cleanup', {});
+    }
+  },
+];
+
+main(args) async =>
+    runIsolateTests(args, processTests, testeeBefore: setupProcesses);
diff --git a/runtime/observatory_2/tests/service_2/reachable_size_test.dart b/runtime/observatory_2/tests/service_2/reachable_size_test.dart
new file mode 100644
index 0000000..f92962d
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/reachable_size_test.dart
@@ -0,0 +1,74 @@
+// Copyright (c) 2015, 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.
+
+import 'dart:async';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+class Pair {
+  // Make sure these fields are not removed by the tree shaker.
+  @pragma("vm:entry-point")
+  var x;
+  @pragma("vm:entry-point")
+  var y;
+}
+
+var p1;
+var p2;
+
+buildGraph() {
+  p1 = new Pair();
+  p2 = new Pair();
+
+  // Adds to both reachable and retained size.
+  p1.x = new List();
+  p2.x = new List();
+
+  // Adds to reachable size only.
+  p1.y = p2.y = new List();
+}
+
+Future<int> getReachableSize(ServiceObject obj) async {
+  Instance size = await obj.isolate.getReachableSize(obj);
+  return int.parse(size.valueAsString);
+}
+
+Future<int> getRetainedSize(ServiceObject obj) async {
+  Instance size = await obj.isolate.getRetainedSize(obj);
+  return int.parse(size.valueAsString);
+}
+
+var tests = <IsolateTest>[
+  (Isolate isolate) async {
+    Instance p1 = await rootLibraryFieldValue(isolate, "p1");
+    Instance p2 = await rootLibraryFieldValue(isolate, "p2");
+
+    // In general, shallow <= retained <= reachable. In this program,
+    // 0 < shallow < retained < reachable.
+
+    int p1_shallow = p1.size;
+    int p1_retained = await getRetainedSize(p1);
+    int p1_reachable = await getReachableSize(p1);
+
+    expect(0, lessThan(p1_shallow));
+    expect(p1_shallow, lessThan(p1_retained));
+    expect(p1_retained, lessThan(p1_reachable));
+
+    int p2_shallow = p2.size;
+    int p2_retained = await getRetainedSize(p2);
+    int p2_reachable = await getReachableSize(p2);
+
+    expect(0, lessThan(p2_shallow));
+    expect(p2_shallow, lessThan(p2_retained));
+    expect(p2_retained, lessThan(p2_reachable));
+
+    expect(p1_shallow, equals(p2_shallow));
+    expect(p1_retained, equals(p2_retained));
+    expect(p1_reachable, equals(p2_reachable));
+  },
+];
+
+main(args) => runIsolateTests(args, tests, testeeBefore: buildGraph);
diff --git a/runtime/observatory_2/tests/service_2/regexp_function_test.dart b/runtime/observatory_2/tests/service_2/regexp_function_test.dart
new file mode 100644
index 0000000..73f95b9
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/regexp_function_test.dart
@@ -0,0 +1,62 @@
+// Copyright (c) 2016, 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.
+// VMOptions=
+// VMOptions=--interpret_irregexp
+
+import 'package:expect/expect.dart';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+
+// Make sure these variables are not removed by the tree shaker.
+@pragma("vm:entry-point")
+var regex0;
+@pragma("vm:entry-point")
+var regex;
+
+void script() {
+  // Check the internal NUL doesn't trip up the name scrubbing in the vm.
+  regex0 = new RegExp("with internal \u{0} NUL");
+  regex = new RegExp(r"(\w+)");
+  String str = "Parse my string";
+  Iterable<Match> matches = regex.allMatches(str); // Run to generate bytecode.
+  Expect.equals(matches.length, 3);
+}
+
+var tests = <IsolateTest>[
+  (Isolate isolate) async {
+    Library lib = isolate.rootLibrary;
+    await lib.load();
+
+    Field field0 = lib.variables.singleWhere((v) => v.name == 'regex0');
+    await field0.load(); // No crash due to embedded NUL.
+
+    Field field = lib.variables.singleWhere((v) => v.name == 'regex');
+    await field.load();
+    Instance regex = field.staticValue;
+    expect(regex.isInstance, isTrue);
+    expect(regex.isRegExp, isTrue);
+    await regex.load();
+
+    if (regex.oneByteFunction == null) {
+      // Running with interpreted regexp.
+      var b1 = await regex.oneByteBytecode.load();
+      expect(b1.isTypedData, isTrue);
+      var b2 = await regex.twoByteBytecode.load();
+      expect(b2.isTypedData, isFalse); // No two-byte string subject was used.
+    } else {
+      // Running with compiled regexp.
+      var f1 = await regex.oneByteFunction.load();
+      expect(f1 is ServiceFunction, isTrue);
+      var f2 = await regex.twoByteFunction.load();
+      expect(f2 is ServiceFunction, isTrue);
+      var f3 = await regex.externalOneByteFunction.load();
+      expect(f3 is ServiceFunction, isTrue);
+      var f4 = await regex.externalTwoByteFunction.load();
+      expect(f4 is ServiceFunction, isTrue);
+    }
+  }
+];
+
+main(args) => runIsolateTests(args, tests, testeeBefore: script);
diff --git a/runtime/observatory_2/tests/service_2/regress_28443_test.dart b/runtime/observatory_2/tests/service_2/regress_28443_test.dart
new file mode 100644
index 0000000..ddc48fa
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/regress_28443_test.dart
@@ -0,0 +1,56 @@
+// Copyright (c) 2017, 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.
+
+import 'dart:async';
+import 'test_helper.dart';
+import 'service_test_common.dart';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+
+const int LINE_A = 28, LINE_B = 33;
+
+class VMServiceClient {
+  VMServiceClient(this.x);
+  close() => new Future.microtask(() => print("close"));
+  var x;
+}
+
+collect() async {
+  var uri = "abc";
+  var vmService;
+  await new Future.microtask(() async {
+    try {
+      vmService = new VMServiceClient(uri);
+      await new Future.microtask(() => throw new TimeoutException("here"));
+    } on dynamic {
+      vmService.close();
+      rethrow; // LINE_A
+    }
+  });
+}
+
+test_code() async {
+  try {
+    await collect();
+  } on TimeoutException {
+    print("ok");
+  }
+}
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  markDartColonLibrariesDebuggable,
+  setBreakpointAtLine(LINE_B),
+  resumeIsolate,
+  hasStoppedAtBreakpoint,
+  setBreakpointAtLine(LINE_A),
+  resumeIsolate,
+  hasStoppedAtBreakpoint,
+  stepOut,
+  stoppedAtLine(LINE_B),
+  resumeIsolate
+];
+
+main(args) => runIsolateTestsSynchronous(args, tests,
+    testeeConcurrent: test_code, pause_on_start: true, pause_on_exit: false);
diff --git a/runtime/observatory_2/tests/service_2/regress_28980_test.dart b/runtime/observatory_2/tests/service_2/regress_28980_test.dart
new file mode 100644
index 0000000..3bdf993
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/regress_28980_test.dart
@@ -0,0 +1,88 @@
+// Copyright (c) 2017, 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.
+
+import 'dart:async';
+import 'test_helper.dart';
+import 'service_test_common.dart';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+
+const int LINE_A = 19, LINE_B = 38;
+
+var _lock;
+var _lockEnabled = true;
+
+String flutterRoot = "abc";
+
+foo(a, b, c, d) {
+  return new A(); // LINE_A
+}
+
+class A {
+  Future lock() => new Future.microtask(() => print("lock"));
+  String path = "path";
+}
+
+class FileSystemException {}
+
+Future<Null> test_code() async {
+  if (!_lockEnabled) return null;
+  assert(_lock == null);
+  _lock = foo(flutterRoot, 'bin', 'cache', 'lockfile');
+  bool locked = false;
+  bool printed = false;
+  while (!locked) {
+    try {
+      await _lock.lock();
+      locked = true; // LINE_B
+    } on FileSystemException {
+      if (!printed) {
+        print('Print path: ${_lock.path}');
+        print('Just another line...');
+        printed = true;
+      }
+      await new Future<Null>.delayed(const Duration(milliseconds: 50));
+    }
+  }
+}
+
+Future stepThroughProgram(Isolate isolate) async {
+  Completer completer = new Completer();
+  int pauseEventsSeen = 0;
+
+  await subscribeToStream(isolate.vm, VM.kDebugStream,
+      (ServiceEvent event) async {
+    if (event.kind == ServiceEvent.kPauseBreakpoint) {
+      // We are paused: Step further.
+      pauseEventsSeen++;
+      isolate.stepInto();
+    } else if (event.kind == ServiceEvent.kPauseExit) {
+      // We are at the exit: The test is done.
+      expect(pauseEventsSeen > 20, true,
+          reason: "Saw only $pauseEventsSeen pause events.");
+      await cancelStreamSubscription(VM.kDebugStream);
+      completer.complete();
+    }
+  });
+  isolate.resume();
+  return completer.future;
+}
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  markDartColonLibrariesDebuggable,
+  setBreakpointAtLine(LINE_A),
+  resumeIsolate,
+  hasStoppedAtBreakpoint,
+  setBreakpointAtLine(LINE_B),
+  resumeIsolate,
+  hasStoppedAtBreakpoint,
+  stepInto,
+  stepInto,
+  stepInto,
+  resumeIsolate,
+];
+
+main(args) => runIsolateTestsSynchronous(args, tests,
+    testeeConcurrent: test_code, pause_on_start: true, pause_on_exit: false);
diff --git a/runtime/observatory_2/tests/service_2/regress_34841_lib.dart b/runtime/observatory_2/tests/service_2/regress_34841_lib.dart
new file mode 100644
index 0000000..60eb3e4
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/regress_34841_lib.dart
@@ -0,0 +1,10 @@
+// Copyright (c) 2019, 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.
+
+class Foo {
+  String baz() => StackTrace.current.toString();
+  final String foo = () {
+    return StackTrace.current.toString();
+  }();
+}
diff --git a/runtime/observatory_2/tests/service_2/regress_34841_test.dart b/runtime/observatory_2/tests/service_2/regress_34841_test.dart
new file mode 100644
index 0000000..d3c208a
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/regress_34841_test.dart
@@ -0,0 +1,69 @@
+// Copyright (c) 2019, 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.
+
+// While it's not (currently) necessary, add some noise here to push down token
+// positions in this file compared to the file regress_34841_lib.dart.
+// This is to ensure that any possible tokens in that file are just comments
+// (i.e. not actual) positions in this file.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+import 'service_test_common.dart';
+import 'dart:developer';
+import 'regress_34841_lib.dart';
+
+class Bar extends Object with Foo {}
+
+void testFunction() {
+  Bar bar = new Bar();
+  print(bar.foo);
+  print(bar.baz());
+  debugger();
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  (Isolate isolate) async {
+    var stack = await isolate.getStack();
+
+    // Make sure we are in the right place.
+    expect(stack.type, equals('Stack'));
+    expect(stack['frames'].length, greaterThanOrEqualTo(1));
+    expect(stack['frames'][0].function.name, equals('testFunction'));
+
+    var root = isolate.rootLibrary;
+    await root.load();
+    Script script = root.scripts.first;
+    await script.load();
+
+    var params = {
+      'reports': ['Coverage'],
+      'scriptId': script.id,
+      'forceCompile': true
+    };
+    var report = await isolate.invokeRpcNoUpgrade('getSourceReport', params);
+    List<dynamic> ranges = report['ranges'];
+    List<int> coveragePlaces = <int>[];
+    for (var range in ranges) {
+      for (int i in range["coverage"]["hits"]) {
+        coveragePlaces.add(i);
+      }
+      for (int i in range["coverage"]["misses"]) {
+        coveragePlaces.add(i);
+      }
+    }
+
+    // Make sure we can translate it all.
+    for (int place in coveragePlaces) {
+      int line = script.tokenToLine(place);
+      int column = script.tokenToCol(place);
+      if (line == null || column == null) {
+        throw "Token $place translated to $line:$column";
+      }
+    }
+  },
+];
+
+main(args) => runIsolateTests(args, tests, testeeConcurrent: testFunction);
diff --git a/runtime/observatory_2/tests/service_2/reload_sources_test.dart b/runtime/observatory_2/tests/service_2/reload_sources_test.dart
new file mode 100644
index 0000000..b2ff5a9
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/reload_sources_test.dart
@@ -0,0 +1,40 @@
+// Copyright (c) 2015, 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.
+
+import 'test_helper.dart';
+import 'dart:developer';
+import 'service_test_common.dart';
+
+testMain() {
+  debugger(); // Stop here.
+  print('1');
+  while (true) {}
+}
+
+var tests = <IsolateTest>[
+  // Stopped at 'debugger' statement.
+  hasStoppedAtBreakpoint,
+  // Reload sources and request to pause post reload. The pause request will be
+  // ignored because we are already paused at a breakpoint.
+  reloadSources(true),
+  // Ensure that we are still stopped at a breakpoint.
+  hasStoppedAtBreakpoint,
+  // Resume the isolate into the while loop.
+  resumeIsolate,
+  // Reload sources and request to pause post reload. The pause request will
+  // be respected because we are not already paused.
+  reloadSources(true),
+  // Ensure that we are paused post reload request.
+  hasStoppedPostRequest,
+  // Resume the isolate.
+  resumeIsolate,
+  // Verify that it is running.
+  isolateIsRunning,
+  // Reload sources and do not request to pause post reload.
+  reloadSources(false),
+  // Verify that it is running.
+  isolateIsRunning,
+];
+
+main(args) => runIsolateTests(args, tests, testeeConcurrent: testMain);
diff --git a/runtime/observatory_2/tests/service_2/rewind_optimized_out_test.dart b/runtime/observatory_2/tests/service_2/rewind_optimized_out_test.dart
new file mode 100644
index 0000000..34901ec
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/rewind_optimized_out_test.dart
@@ -0,0 +1,81 @@
+// Copyright (c) 2016, 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.
+
+import 'dart:developer';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+int LINE_A = 31;
+int LINE_B = 36;
+int LINE_C = 39;
+int LINE_D = 43;
+
+int global = 0;
+
+@pragma('vm:never-inline')
+b3(x) {
+  int sum = 0;
+  try {
+    for (int i = 0; i < x; i++) {
+      sum += x;
+    }
+  } catch (e) {
+    print("caught $e");
+  }
+  if (global >= 100) {
+    debugger();
+  }
+  global = global + 1; // Line A
+  return sum;
+}
+
+@pragma('vm:prefer-inline')
+b2(x) => b3(x); // Line B
+
+@pragma('vm:prefer-inline')
+b1(x) => b2(x); // Line C
+
+test() {
+  while (true) {
+    b1(10000); // Line D
+  }
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_A),
+  (Isolate isolate) async {
+    // We are at our breakpoint with global=100.
+    Instance result = await isolate.rootLibrary.evaluate('global');
+    print('global is $result');
+    expect(result.type, equals('Instance'));
+    expect(result.valueAsString, equals('100'));
+
+    // Rewind the top stack frame.
+    bool caughtException;
+    try {
+      result = await isolate.rewind(1);
+      expect(false, isTrue, reason: 'Unreachable');
+    } on ServerRpcException catch (e) {
+      caughtException = true;
+      expect(e.code, equals(ServerRpcException.kCannotResume));
+      expect(
+          e.message,
+          startsWith('Cannot rewind to frame 1 due to conflicting compiler '
+              'optimizations. Run the vm with --no-prune-dead-locals '
+              'to disallow these optimizations. Next valid rewind '
+              'frame is '));
+    }
+    expect(caughtException, isTrue);
+  },
+];
+
+main(args) => runIsolateTests(args, tests, testeeConcurrent: test, extraArgs: [
+      '--trace-rewind',
+      '--prune-dead-locals',
+      '--no-background-compilation',
+      '--optimization-counter-threshold=10'
+    ]);
diff --git a/runtime/observatory_2/tests/service_2/rewind_test.dart b/runtime/observatory_2/tests/service_2/rewind_test.dart
new file mode 100644
index 0000000..06b6f50
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/rewind_test.dart
@@ -0,0 +1,140 @@
+// Copyright (c) 2016, 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.
+
+import 'dart:developer';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+int LINE_A = 31;
+int LINE_B = 36;
+int LINE_C = 39;
+int LINE_D = 43;
+
+int global = 0;
+
+@pragma('vm:never-inline')
+b3(x) {
+  int sum = 0;
+  try {
+    for (int i = 0; i < x; i++) {
+      sum += x;
+    }
+  } catch (e) {
+    print("caught $e");
+  }
+  if (global >= 100) {
+    debugger();
+  }
+  global = global + 1; // Line A
+  return sum;
+}
+
+@pragma('vm:prefer-inline')
+b2(x) => b3(x); // Line B
+
+@pragma('vm:prefer-inline')
+b1(x) => b2(x); // Line C
+
+test() {
+  while (true) {
+    b1(10000); // Line D
+  }
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_A),
+  (Isolate isolate) async {
+    // We are not able to rewind frame 0.
+    bool caughtException;
+    try {
+      await isolate.rewind(0);
+      expect(false, isTrue, reason: 'Unreachable');
+    } on ServerRpcException catch (e) {
+      caughtException = true;
+      expect(e.code, equals(ServerRpcException.kCannotResume));
+      expect(e.message, 'Frame must be in bounds [1..8]: saw 0');
+    }
+    expect(caughtException, isTrue);
+  },
+  (Isolate isolate) async {
+    // We are not able to rewind frame 13.
+    bool caughtException;
+    try {
+      await isolate.rewind(13);
+      expect(false, isTrue, reason: 'Unreachable');
+    } on ServerRpcException catch (e) {
+      caughtException = true;
+      expect(e.code, equals(ServerRpcException.kCannotResume));
+      expect(e.message, 'Frame must be in bounds [1..8]: saw 13');
+    }
+    expect(caughtException, isTrue);
+  },
+  (Isolate isolate) async {
+    // We are at our breakpoint with global=100.
+    Instance result = await isolate.rootLibrary.evaluate('global');
+    print('global is $result');
+    expect(result.type, equals('Instance'));
+    expect(result.valueAsString, equals('100'));
+
+    // Rewind the top stack frame.
+    var result2 = await isolate.rewind(1);
+    expect(result2['type'], equals('Success'));
+  },
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_B),
+  (Isolate isolate) async {
+    var result = await isolate.resume();
+    expect(result['type'], equals('Success'));
+  },
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_A),
+  (Isolate isolate) async {
+    // global still is equal to 100.  We did not execute "global++".
+    Instance result = await isolate.rootLibrary.evaluate('global');
+    print('global is $result');
+    expect(result.type, equals('Instance'));
+    expect(result.valueAsString, equals('100'));
+
+    // Rewind up to 'test'/
+    var result2 = await isolate.rewind(3);
+    expect(result2['type'], equals('Success'));
+  },
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_D),
+  (Isolate isolate) async {
+    // Reset global to 0 and start again.
+    Instance result = await isolate.rootLibrary.evaluate('global=0');
+    print('set global to $result');
+    expect(result.type, equals('Instance'));
+    expect(result.valueAsString, equals('0'));
+
+    var result2 = await isolate.resume();
+    expect(result2['type'], equals('Success'));
+  },
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_A),
+  (Isolate isolate) async {
+    // We are at our breakpoint with global=100.
+    Instance result = await isolate.rootLibrary.evaluate('global');
+    print('global is $result');
+    expect(result.type, equals('Instance'));
+    expect(result.valueAsString, equals('100'));
+
+    // Rewind the top 2 stack frames.
+    var result2 = await isolate.rewind(2);
+    expect(result2['type'], equals('Success'));
+  },
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_C),
+];
+
+main(args) => runIsolateTests(args, tests, testeeConcurrent: test, extraArgs: [
+      '--trace-rewind',
+      '--no-prune-dead-locals',
+      '--no-background-compilation',
+      '--optimization-counter-threshold=10'
+    ]);
diff --git a/runtime/observatory_2/tests/service_2/service_2.status b/runtime/observatory_2/tests/service_2/service_2.status
new file mode 100644
index 0000000..c16feab
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/service_2.status
@@ -0,0 +1,73 @@
+# Copyright (c) 2014, 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.
+# Flaky failures
+dds_log_history_size_gigantic_test: Pass, Slow # Involves processing lots of logs
+field_script_test: Pass, RuntimeError
+get_allocation_samples_test: Pass, RuntimeError # Inconsistent stack trace
+get_isolate_rpc_test: Pass, RuntimeError # Issue 29324
+isolate_lifecycle_test: Pass, RuntimeError # Issue 24174
+pause_on_exceptions_test: Pass, RuntimeError # Issue 33049
+pause_on_start_and_exit_with_child_test: Pass, RuntimeError # Issue 33049
+reload_sources_test: Pass, Slow # Reload is slow on the bots
+valid_source_locations_test: Pass, Slow # Generally slow, even in release-x64.
+
+[ $arch == arm ]
+process_service_test: Pass, Fail # Issue 24344
+
+# Test uses service API and relies on correct class names
+[ $builder_tag == obfuscated ]
+dominator_tree_vm_test: SkipByDesign
+dominator_tree_vm_with_double_field_test: SkipByDesign
+
+# Tests with known analyzer issues
+[ $compiler == dart2analyzer ]
+developer_extension_test: SkipByDesign
+get_isolate_after_language_error_test: SkipByDesign
+
+# Service protocol is not supported in product mode.
+[ $mode == product ]
+*: SkipByDesign
+
+[ $system == android ]
+string_escaping_test: Skip # Issue http://dartbug.com/42094
+
+[ $system == windows ]
+*: Slow
+async_generator_breakpoint_test: Skip # Issue 29145
+dev_fs_http_put_weird_char_test: Skip # Windows disallows carriage returns in paths
+dev_fs_weird_char_test: Skip # Windows disallows question mark in paths
+
+[ $compiler == none && $runtime == vm ]
+evaluate_activation_test/instance: RuntimeError # http://dartbug.com/20047
+evaluate_activation_test/scope: RuntimeError # http://dartbug.com/20047
+
+[ $compiler == none && $runtime == vm && $system == fuchsia ]
+*: Skip # Not yet triaged.
+
+[ $mode == debug && $system == windows ]
+dds_log_history_size_gigantic_test: Skip # Too slow: dartbug.com/41825
+
+[ $mode == debug && $system == windows && $checked ]
+async_scope_test: Pass, Slow
+
+[ $mode == debug && ($arch == simarm || $arch == simarm64) ]
+*: SkipSlow
+
+# These tests are slow on simulators.
+[ $arch == simarm || $arch == simarm64 ]
+*: Pass, Slow
+
+# All tests use dart:io
+[ $compiler == dart2js || $browser ]
+*: SkipByDesign
+
+[ $compiler == dartk || $compiler == dartkp ]
+rewind_test: Pass, Slow
+
+[ $system != linux || $arch != ia32 && $arch != x64 ]
+get_native_allocation_samples_test: Skip # Unsupported.
+
+# Skip all service tests because random reloads interfere.
+[ $hot_reload || $hot_reload_rollback ]
+*: SkipByDesign # The service tests should run without being reloaded.
diff --git a/runtime/observatory_2/tests/service_2/service_2_kernel.status b/runtime/observatory_2/tests/service_2/service_2_kernel.status
new file mode 100644
index 0000000..ce97313
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/service_2_kernel.status
@@ -0,0 +1,304 @@
+# Copyright (c) 2017, 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.
+
+[ $compiler == app_jitk ]
+add_breakpoint_rpc_kernel_test: SkipByDesign # No incremental compiler available.
+async_generator_breakpoint_test: SkipByDesign # No incremental compiler available.
+bad_reload_test: RuntimeError
+break_on_activation_test: SkipByDesign # No incremental compiler available.
+complex_reload_test: RuntimeError
+debugger_inspect_test: SkipByDesign # No incremental compiler available.
+debugger_location_second_test: RuntimeError
+eval_internal_class_test: SkipByDesign # No incremental compiler available.
+eval_regression_flutter20255_test: SkipByDesign # No incremental compiler available.
+eval_test: SkipByDesign # No incremental compiler available.
+evaluate_activation_in_method_class_test: RuntimeError # Issue 35505
+evaluate_activation_test: SkipByDesign # No incremental compiler available.
+evaluate_async_closure_test: SkipByDesign # No incremental compiler available.
+evaluate_class_type_parameters_test: SkipByDesign # No incremental compiler available.
+evaluate_function_type_parameters_test: SkipByDesign # No incremental compiler available.
+evaluate_in_async_activation_test: SkipByDesign # No incremental compiler available.
+evaluate_in_async_star_activation_test: SkipByDesign # No incremental compiler available.
+evaluate_in_frame_rpc_test: SkipByDesign # No incremental compiler available.
+evaluate_in_frame_with_scope_test: SkipByDesign # No incremental compiler available.
+evaluate_in_sync_star_activation_test: SkipByDesign # No incremental compiler available.
+evaluate_with_escaping_closure_test: SkipByDesign # No incremental compiler available.
+evaluate_with_scope_test: SkipByDesign # No incremental compiler available.
+pause_on_exceptions_test: SkipByDesign # No incremental compiler available.
+rewind_optimized_out_test: SkipByDesign # No incremental compiler available.
+rewind_test: SkipByDesign # No incremental compiler available.
+
+[ $compiler == dartkp ]
+add_breakpoint_rpc_kernel_test: SkipByDesign
+async_generator_breakpoint_test: SkipByDesign
+async_next_regession_18877_test: Skip, Timeout
+async_next_test: Skip, Timeout
+async_scope_test: Skip, Timeout
+async_single_step_exception_test: Skip, Timeout
+async_single_step_into_test: Skip, Timeout
+async_single_step_out_test: Skip, Timeout
+async_star_single_step_into_test: Skip, Timeout
+async_star_step_out_test: Skip, Timeout
+async_step_out_test: Skip, Timeout
+awaiter_async_stack_contents_2_test: Skip, Timeout
+awaiter_async_stack_contents_test: Skip, Timeout
+bad_reload_test: SkipByDesign
+break_on_activation_test: SkipByDesign
+break_on_async_function_test: Skip, Timeout
+break_on_default_constructor_test: SkipByDesign
+break_on_function_test: Skip, Timeout
+breakpoint_async_break_test: SkipByDesign
+breakpoint_in_package_parts_class_file_uri_test: SkipByDesign
+breakpoint_in_package_parts_class_test: SkipByDesign
+breakpoint_in_parts_class_test: SkipByDesign
+breakpoint_non_debuggable_library_test: SkipByDesign
+breakpoint_on_if_null_1_test: SkipByDesign
+breakpoint_on_if_null_2_test: SkipByDesign
+breakpoint_on_if_null_3_test: SkipByDesign
+breakpoint_on_if_null_4_test: SkipByDesign
+breakpoint_partfile_test: SkipByDesign
+breakpoint_two_args_checked_test: Skip, Timeout
+breakpoints_with_mixin_test: SkipByDesign # Debugger is disabled in AOT mode.
+capture_stdio_test: Skip, Timeout
+causal_async_stack_contents_test: Skip, Timeout
+causal_async_stack_presence_test: Skip, Timeout
+causal_async_star_stack_contents_test: Skip, Timeout
+causal_async_star_stack_presence_test: Skip, Timeout
+client_resume_approvals_reload_test: SkipByDesign # Compiler is disabled in AOT mode.
+code_test: SkipByDesign
+column_breakpoint_test: SkipByDesign
+complex_reload_test: SkipByDesign
+coverage_const_field_async_closure_test: Skip, Timeout
+coverage_leaf_function_test: Skip, Timeout
+coverage_optimized_function_test: Skip, Timeout
+dds_log_history_size_*: Skip, Timeout
+debugger_inspect_test: SkipByDesign
+debugger_location_second_test: Skip, Timeout
+debugger_location_test: Skip, Timeout
+debugging_inlined_finally_test: Skip, Timeout
+debugging_test: SkipByDesign
+dev_fs_spawn_test: SkipByDesign
+developer_extension_test: Skip, Timeout
+developer_service_get_isolate_id_test: Skip, Timeout
+eval_internal_class_test: SkipByDesign
+eval_regression_flutter20255_test: Skip, Timeout
+eval_test: Skip, Timeout
+evaluate_activation_in_method_class_test: Skip, Timeout
+evaluate_activation_test: SkipByDesign
+evaluate_async_closure_test: SkipByDesign
+evaluate_class_type_parameters_test: Skip, Timeout
+evaluate_function_type_parameters_test: Skip, Timeout
+evaluate_in_async_activation_test: Skip, Timeout
+evaluate_in_async_star_activation_test: Skip, Timeout
+evaluate_in_frame_rpc_test: Skip, Timeout
+evaluate_in_frame_with_scope_test: Skip, Timeout
+evaluate_in_sync_star_activation_test: Skip, Timeout
+evaluate_with_escaping_closure_test: SkipByDesign
+evaluate_with_scope_test: SkipByDesign
+field_script_test: SkipByDesign
+get_allocation_samples_test: Skip, Timeout
+get_isolate_after_language_error_test: CompileTimeError
+get_object_rpc_test: SkipByDesign
+get_source_report_test: Skip, Timeout
+get_source_report_with_mixin_test: Skip, Timeout
+get_stack_rpc_test: Skip, Timeout
+implicit_getter_setter_test: SkipByDesign
+invoke_test: Skip, Timeout
+isolate_lifecycle_test: SkipByDesign
+issue_25465_test: SkipByDesign
+issue_27238_test: Skip, Timeout
+issue_27287_test: Skip, Timeout
+issue_30555_test: SkipByDesign
+kill_paused_test: Skip, Timeout
+library_dependency_test: CompileTimeError
+local_variable_declaration_test: Skip, Timeout
+local_variable_in_awaiter_async_frame_test: Skip, Timeout
+logging_test: Skip, Timeout
+mirror_references_test: CompileTimeError
+mixin_break_test: Skip, Timeout
+network_profiling_test: Skip, Timeout
+next_through_assign_call_test: SkipByDesign
+next_through_assign_int_test: SkipByDesign
+next_through_await_for_test: SkipByDesign
+next_through_call_on_field_in_class_test: SkipByDesign
+next_through_call_on_field_test: SkipByDesign
+next_through_call_on_static_field_in_class_test: SkipByDesign
+next_through_catch_test: SkipByDesign
+next_through_closure_test: SkipByDesign
+next_through_create_list_and_map_test: SkipByDesign
+next_through_for_each_loop_test: SkipByDesign
+next_through_for_loop_with_break_and_continue_test: SkipByDesign
+next_through_function_expression_test: SkipByDesign
+next_through_implicit_call_test: SkipByDesign
+next_through_is_and_as_test: SkipByDesign
+next_through_multi_catch_test: SkipByDesign
+next_through_new_test: SkipByDesign
+next_through_operator_bracket_on_super_test: SkipByDesign
+next_through_operator_bracket_on_this_test: SkipByDesign
+next_through_operator_bracket_test: SkipByDesign
+next_through_simple_async_test: SkipByDesign
+next_through_simple_async_with_returns_test: SkipByDesign
+next_through_simple_linear_2_test: SkipByDesign
+next_through_simple_linear_test: SkipByDesign
+parameters_in_scope_at_entry_test: Skip, Timeout
+pause_idle_isolate_test: Skip, Timeout
+pause_on_exceptions_test: SkipByDesign
+pause_on_start_then_step_test: SkipByDesign
+pause_on_unhandled_async_exceptions2_test: SkipByDesign
+pause_on_unhandled_async_exceptions3_test: SkipByDesign
+pause_on_unhandled_async_exceptions_test: SkipByDesign
+pause_on_unhandled_exceptions_test: SkipByDesign
+positive_token_pos_test: Skip, Timeout
+regress_28443_test: SkipByDesign
+regress_28980_test: SkipByDesign
+regress_34841_test: Skip, Timeout
+reload_sources_test: Skip, Timeout
+rewind_optimized_out_test: Skip, Timeout
+rewind_test: Skip, Timeout
+set_library_debuggable_test: Skip, Timeout
+simple_reload_test: SkipByDesign
+steal_breakpoint_test: SkipByDesign
+step_into_async_no_await_test: Skip, Timeout
+step_over_await_test: Skip, Timeout
+step_test: SkipByDesign
+step_through_arithmetic_test: SkipByDesign
+step_through_constructor_calls_test: SkipByDesign
+step_through_constructor_test: SkipByDesign
+step_through_for_each_sync_star_2_test: SkipByDesign
+step_through_for_each_sync_star_test: SkipByDesign
+step_through_function_2_test: SkipByDesign
+step_through_function_test: SkipByDesign
+step_through_getter_test: SkipByDesign
+step_through_mixin_from_sdk_test: SkipByDesign
+step_through_property_get_test: SkipByDesign
+step_through_property_set_test: SkipByDesign
+step_through_setter_test: SkipByDesign
+step_through_switch_test: SkipByDesign
+step_through_switch_with_continue_test: SkipByDesign
+valid_source_locations_test: Skip, Timeout
+vm_timeline_flags_test: Skip, Timeout
+weak_properties_test: CompileTimeError
+yield_positions_with_finally_test: SkipByDesign
+
+[ $fasta ]
+get_isolate_after_language_error_test: CompileTimeError
+
+[ $arch == arm64 && $compiler == dartk ]
+coverage_optimized_function_test: Pass, Slow
+
+[ $arch == arm64 && $compiler == dartk && $mode == release ]
+get_object_rpc_test: Pass, RuntimeError # Please triage / issue #27806
+
+[ $arch == ia32 && $builder_tag == optimization_counter_threshold ]
+eval_regression_flutter20255_test: SkipSlow # Timeout
+get_vm_timeline_rpc_test: Pass, Slow
+rewind_optimized_out_test: SkipSlow # Timeout
+
+[ $arch == ia32 && $compiler == dartk ]
+valid_source_locations_test: Skip # Issue 34736, too slow.
+
+[ $arch != simarm && $arch != simarm64 && $compiler == dartk ]
+complex_reload_test: RuntimeError
+
+[ $compiler == dartkb && ($builder_tag == bytecode_interpreter || $builder_tag == bytecode_interpreter_nnbd || $builder_tag == bytecode_mixed || $builder_tag == bytecode_mixed_nnbd) ]
+*: Skip # There are still timeouts in the interpreter and mixed modes which cause infra timeouts.
+
+[ $mode == debug && $system == windows ]
+debugger_location_second_test: Skip, Timeout
+debugger_location_test: Skip, Timeout
+
+[ $mode == debug && ($compiler == dartk || $compiler == dartkb) ]
+isolate_lifecycle_test: Skip # Flaky.
+pause_idle_isolate_test: Skip # Flaky
+rewind_optimized_out_test: Pass, Slow
+
+[ $system == windows && ($compiler == dartk || $compiler == dartkb) ]
+add_breakpoint_rpc_kernel_test: Skip # Timeout
+break_on_default_constructor_test: Skip # Issues 32137 and 32138.
+breakpoint_in_parts_class_test: Skip # Timeout
+coverage_optimized_function_test: Skip # Timeout
+field_script_test: Skip # Timeout
+get_vm_timeline_rpc_test: Skip # Issue 32137.
+issue_25465_test: Skip # Issues 32137 and 32138.
+issue_30555_test: Skip # Issues 32137 and 32138.
+next_through_assign_call_test: Skip # Issues 32137 and 32138.
+next_through_assign_int_test: Skip # Issues 32137 and 32138.
+next_through_call_on_field_in_class_test: Skip # Issues 32137 and 32138.
+next_through_call_on_field_test: Skip # Issues 32137 and 32138.
+next_through_call_on_static_field_in_class_test: Skip # Timeout
+next_through_catch_test: Skip # Issues 32137 and 32138.
+next_through_closure_test: Skip # Timeout
+next_through_create_list_and_map_test: Skip # Issues 32137 and 32138.
+next_through_for_each_loop_test: Skip # Timeout
+next_through_for_loop_with_break_and_continue_test: Skip # Timeout
+next_through_function_expression_test: Skip # Issues 32137 and 32138.
+next_through_implicit_call_test: Skip # Timeout
+next_through_is_and_as_test: Skip # Issues 32137 and 32138.
+next_through_multi_catch_test: Skip # Issues 32137 and 32138.
+next_through_new_test: Skip # Timeout
+next_through_operator_bracket_on_super_test: Skip # Timeout
+next_through_operator_bracket_on_this_test: Skip # Timeout
+next_through_operator_bracket_test: Skip # Timeout
+next_through_simple_async_test: Skip # Timeout
+next_through_simple_async_with_returns_test: Skip # Issues 32137 and 32138.
+next_through_simple_linear_2_test: Skip # Issues 32137 and 32138.
+next_through_simple_linear_test: Skip # Issues 32137 and 32138.
+pause_idle_isolate_test: Skip # Issues 32137 and 32138.
+pause_on_start_and_exit_test: Skip # Issues 32225, 32138.
+pause_on_start_and_exit_with_child_test: Skip # Timeout
+pause_on_start_then_step_test: Skip # Issue 32225, 32138.
+regress_28443_test: Skip # Times out.
+regress_28980_test: Skip # Issues 32137 and 32138.
+reload_sources_test: RuntimeError
+set_vm_name_rpc_test: Skip # Times out. Issue 32137.
+step_test: Skip # Issues 32137 and 32138.
+step_through_constructor_calls_test: Skip # Timeout
+step_through_constructor_test: Skip # Timeout
+step_through_function_2_test: Skip # Issues 32137 and 32138.
+step_through_function_test: Skip # Issues 32137 and 32138.
+step_through_getter_test: Skip # Timeout
+step_through_property_get_test: Skip # Times out. Issue 32137.
+step_through_property_set_test: Skip # Timeout
+step_through_setter_test: Skip # Issues 32137 and 32138.
+step_through_switch_test: Skip # Times out. Issue 32137.
+step_through_switch_with_continue_test: Skip # Times out. Issue 32137.
+
+[ ($arch == simarm || $arch == simarm64) && ($compiler == dartk || $compiler == dartkb) ]
+add_breakpoint_rpc_kernel_test: RuntimeError # Issue #34736
+async_generator_breakpoint_test: SkipByDesign # No incremental compiler available.
+bad_reload_test: Skip # Times out on sim architectures, also RuntimeError.
+break_on_activation_test: RuntimeError # Issue #34736
+breakpoint_in_package_parts_class_file_uri_test: RuntimeError # Issue #34736
+complex_reload_test: Skip # Times out on sim architectures, also RuntimeError.
+dds_log_history_size_gigantic_test: SkipSlow # Involves hundreds of thousands of log messages
+debugger_inspect_test: RuntimeError, Timeout # Issue #34736
+eval_internal_class_test: RuntimeError # Issue #34736
+eval_regression_flutter20255_test: SkipByDesign # No incremental compiler available.
+eval_test: RuntimeError # Issue #34736
+evaluate_activation_test/none: RuntimeError # Issue #34736
+evaluate_async_closure_test: RuntimeError # Issue #34736
+evaluate_class_type_parameters_test: RuntimeError # Issue 34736
+evaluate_function_type_parameters_test: RuntimeError # Issue 34736
+evaluate_in_async_activation_test: RuntimeError # Issue #34736
+evaluate_in_async_star_activation_test: RuntimeError # Issue #34736
+evaluate_in_frame_rpc_test: RuntimeError # Issue #34736
+evaluate_in_frame_with_scope_test: RuntimeError # Issue #34736
+evaluate_in_sync_star_activation_test: SkipByDesign # No incremental compiler available.
+evaluate_with_scope_test: RuntimeError # Issue #34736
+pause_on_exceptions_test: RuntimeError, Timeout # Issue #34736
+reload_sources_test: Skip # Times out.
+rewind_optimized_out_test: RuntimeError # Issue #34736
+rewind_test: Pass, RuntimeError
+set_name_rpc_test: RuntimeError # Please triage.
+simple_reload_test: RuntimeError, Timeout
+valid_source_locations_test: Skip # Issue 34736, too slow.
+
+[ $compiler == dartk || $compiler == dartkb ]
+bad_reload_test: RuntimeError # Issue 34025
+coverage_optimized_function_test: Pass, Slow
+evaluate_activation_in_method_class_test: RuntimeError # Issue 35505
+evaluate_activation_test/instance: RuntimeError # http://dartbug.com/20047
+evaluate_activation_test/scope: RuntimeError # http://dartbug.com/20047
+get_source_report_test: RuntimeError # Should pass again when constant evaluation is relanded, see http://dartbug.com/36600
+pause_on_unhandled_async_exceptions2_test: Pass, Slow
diff --git a/runtime/observatory_2/tests/service_2/service_test_common.dart b/runtime/observatory_2/tests/service_2/service_test_common.dart
new file mode 100644
index 0000000..4373982
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/service_test_common.dart
@@ -0,0 +1,620 @@
+// Copyright (c) 2016, 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.
+
+library service_test_common;
+
+import 'dart:async';
+import 'dart:io' show Platform;
+import 'package:dds/dds.dart';
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/service_common.dart';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+
+typedef Future IsolateTest(Isolate isolate);
+typedef Future VMTest(VM vm);
+typedef Future DDSTest(VM vm, DartDevelopmentService dds);
+typedef void ServiceEventHandler(ServiceEvent event);
+
+Map<String, StreamSubscription> streamSubscriptions = {};
+
+Future subscribeToStream(
+    VM vm, String streamName, ServiceEventHandler onEvent) async {
+  assert(streamSubscriptions[streamName] == null);
+
+  Stream<ServiceEvent> stream = await vm.getEventStream(streamName);
+  StreamSubscription subscription = stream.listen(onEvent);
+  streamSubscriptions[streamName] = subscription;
+}
+
+Future cancelStreamSubscription(String streamName) async {
+  StreamSubscription subscription = streamSubscriptions[streamName];
+  subscription.cancel();
+  streamSubscriptions.remove(streamName);
+}
+
+Future smartNext(Isolate isolate) async {
+  print('smartNext');
+  if (isolate.status == M.IsolateStatus.paused) {
+    dynamic event = isolate.pauseEvent;
+    if (event.atAsyncSuspension) {
+      return asyncNext(isolate);
+    } else {
+      return syncNext(isolate);
+    }
+  } else {
+    throw 'The program is already running';
+  }
+}
+
+Future asyncNext(Isolate isolate) async {
+  print('asyncNext');
+  if (isolate.status == M.IsolateStatus.paused) {
+    dynamic event = isolate.pauseEvent;
+    if (!event.atAsyncSuspension) {
+      throw 'No async continuation at this location';
+    } else {
+      return isolate.stepOverAsyncSuspension();
+    }
+  } else {
+    throw 'The program is already running';
+  }
+}
+
+Future syncNext(Isolate isolate) async {
+  print('syncNext');
+  if (isolate.status == M.IsolateStatus.paused) {
+    return isolate.stepOver();
+  } else {
+    throw 'The program is already running';
+  }
+}
+
+Future asyncStepOver(Isolate isolate) async {
+  final Completer pausedAtSyntheticBreakpoint = new Completer();
+  StreamSubscription subscription;
+
+  // Cancel the subscription.
+  cancelSubscription() {
+    if (subscription != null) {
+      subscription.cancel();
+      subscription = null;
+    }
+  }
+
+  // Complete futures with with error.
+  completeError(error) {
+    if (!pausedAtSyntheticBreakpoint.isCompleted) {
+      pausedAtSyntheticBreakpoint.completeError(error);
+    }
+  }
+
+  // Subscribe to the debugger event stream.
+  Stream<ServiceEvent> stream;
+  try {
+    stream = await isolate.vm.getEventStream(VM.kDebugStream);
+  } catch (e) {
+    completeError(e);
+    return pausedAtSyntheticBreakpoint.future;
+  }
+
+  Breakpoint syntheticBreakpoint;
+
+  subscription = stream.listen((ServiceEvent event) async {
+    // Synthetic breakpoint add event. This is the first event we will
+    // receive.
+    bool isAdd = (event.kind == ServiceEvent.kBreakpointAdded) &&
+        (event.breakpoint.isSyntheticAsyncContinuation) &&
+        (event.owner == isolate);
+    // Resume after synthetic breakpoint added. This is the second event
+    // we will receive.
+    bool isResume = (event.kind == ServiceEvent.kResume) &&
+        (syntheticBreakpoint != null) &&
+        (event.owner == isolate);
+    // Paused at synthetic breakpoint. This is the third event we will
+    // receive.
+    bool isPaused = (event.kind == ServiceEvent.kPauseBreakpoint) &&
+        (syntheticBreakpoint != null) &&
+        (event.breakpoint == syntheticBreakpoint);
+    if (isAdd) {
+      syntheticBreakpoint = event.breakpoint;
+    } else if (isResume) {
+    } else if (isPaused) {
+      pausedAtSyntheticBreakpoint.complete(isolate);
+      syntheticBreakpoint = null;
+      cancelSubscription();
+    }
+  });
+
+  // Issue the step OverAwait command.
+  try {
+    await isolate.stepOverAsyncSuspension();
+  } catch (e) {
+    // This can fail when another client issued the same resume command
+    // or another client has moved the isolate forward.
+    cancelSubscription();
+    completeError(e);
+  }
+
+  return pausedAtSyntheticBreakpoint.future;
+}
+
+bool isEventOfKind(M.Event event, String kind) {
+  switch (kind) {
+    case ServiceEvent.kPauseBreakpoint:
+      return event is M.PauseBreakpointEvent;
+    case ServiceEvent.kPauseException:
+      return event is M.PauseExceptionEvent;
+    case ServiceEvent.kPauseExit:
+      return event is M.PauseExitEvent;
+    case ServiceEvent.kPauseStart:
+      return event is M.PauseStartEvent;
+    case ServiceEvent.kPausePostRequest:
+      return event is M.PausePostRequestEvent;
+    default:
+      return false;
+  }
+}
+
+Future hasPausedFor(Isolate isolate, String kind) {
+  // Set up a listener to wait for breakpoint events.
+  Completer completer = new Completer();
+  isolate.vm.getEventStream(VM.kDebugStream).then((stream) {
+    var subscription;
+    subscription = stream.listen((ServiceEvent event) {
+      if ((isolate == event.isolate) && (event.kind == kind)) {
+        if (completer != null) {
+          // Reload to update isolate.pauseEvent.
+          print('Paused with $kind');
+          subscription.cancel();
+          completer.complete(isolate.reload());
+          completer = null;
+        }
+      }
+    });
+
+    // Pause may have happened before we subscribed.
+    isolate.reload().then((_) {
+      if ((isolate.pauseEvent != null) &&
+          isEventOfKind(isolate.pauseEvent, kind)) {
+        // Already waiting at a breakpoint.
+        if (completer != null) {
+          print('Paused with $kind');
+          subscription.cancel();
+          completer.complete(isolate);
+          completer = null;
+        }
+      }
+    });
+  });
+
+  return completer.future; // Will complete when breakpoint hit.
+}
+
+Future hasStoppedAtBreakpoint(Isolate isolate) {
+  return hasPausedFor(isolate, ServiceEvent.kPauseBreakpoint);
+}
+
+Future hasStoppedPostRequest(Isolate isolate) {
+  return hasPausedFor(isolate, ServiceEvent.kPausePostRequest);
+}
+
+Future hasStoppedWithUnhandledException(Isolate isolate) {
+  return hasPausedFor(isolate, ServiceEvent.kPauseException);
+}
+
+Future hasStoppedAtExit(Isolate isolate) {
+  return hasPausedFor(isolate, ServiceEvent.kPauseExit);
+}
+
+Future hasPausedAtStart(Isolate isolate) {
+  return hasPausedFor(isolate, ServiceEvent.kPauseStart);
+}
+
+Future markDartColonLibrariesDebuggable(Isolate isolate) async {
+  await isolate.reload();
+  for (Library lib in isolate.libraries) {
+    await lib.load();
+    if (lib.uri.startsWith('dart:') && !lib.uri.startsWith('dart:_')) {
+      var setDebugParams = {
+        'libraryId': lib.id,
+        'isDebuggable': true,
+      };
+      Map<String, dynamic> result = await isolate.invokeRpcNoUpgrade(
+          'setLibraryDebuggable', setDebugParams);
+    }
+  }
+  return isolate;
+}
+
+IsolateTest reloadSources([bool pause = false]) {
+  return (Isolate isolate) async {
+    Map<String, dynamic> params = <String, dynamic>{};
+    if (pause) {
+      params['pause'] = pause;
+    }
+    return isolate.invokeRpc('reloadSources', params);
+  };
+}
+
+// Currying is your friend.
+IsolateTest setBreakpointAtLine(int line) {
+  return (Isolate isolate) async {
+    print("Setting breakpoint for line $line");
+    Library lib = await isolate.rootLibrary.load();
+    Script script = lib.scripts.firstWhere((s) => s.uri == lib.uri);
+
+    Breakpoint bpt = await isolate.addBreakpoint(script, line);
+    print("Breakpoint is $bpt");
+    expect(bpt, isNotNull);
+    expect(bpt is Breakpoint, isTrue);
+  };
+}
+
+IsolateTest setBreakpointAtLineColumn(int line, int column) {
+  return (Isolate isolate) async {
+    print("Setting breakpoint for line $line column $column");
+    Library lib = await isolate.rootLibrary.load();
+    Script script = lib.scripts.firstWhere((s) => s.uri == lib.uri);
+
+    Breakpoint bpt = await isolate.addBreakpoint(script, line, column);
+    print("Breakpoint is $bpt");
+    expect(bpt, isNotNull);
+    expect(bpt is Breakpoint, isTrue);
+  };
+}
+
+IsolateTest setBreakpointAtUriAndLine(String uri, int line) {
+  return (Isolate isolate) async {
+    print("Setting breakpoint for line $line in $uri");
+    Breakpoint bpt = await isolate.addBreakpointByScriptUri(uri, line);
+    print("Breakpoint is $bpt");
+    expect(bpt, isNotNull);
+    expect(bpt is Breakpoint, isTrue);
+  };
+}
+
+IsolateTest stoppedAtLine(int line) {
+  return (Isolate isolate) async {
+    print("Checking we are at line $line");
+
+    // Make sure that the isolate has stopped.
+    await isolate.reload();
+    expect(isolate.pauseEvent is! M.ResumeEvent, isTrue);
+
+    ServiceMap stack = await isolate.getStack();
+    expect(stack.type, equals('Stack'));
+
+    List frames = stack['frames'];
+    expect(frames.length, greaterThanOrEqualTo(1));
+
+    Frame top = frames[0];
+    Script script = await top.location.script.load();
+    int actualLine = script.tokenToLine(top.location.tokenPos);
+    if (actualLine != line) {
+      StringBuffer sb = new StringBuffer();
+      sb.write("Expected to be at line $line but actually at line $actualLine");
+      sb.write("\nFull stack trace:\n");
+      for (Frame f in stack['frames']) {
+        sb.write(" $f [${await f.location.getLine()}]\n");
+      }
+      throw sb.toString();
+    } else {
+      print('Program is stopped at line: $line');
+    }
+  };
+}
+
+IsolateTest stoppedInFunction(String functionName,
+    {bool contains: false, bool includeOwner: false}) {
+  return (Isolate isolate) async {
+    print("Checking we are in function: $functionName");
+
+    ServiceMap stack = await isolate.getStack();
+    expect(stack.type, equals('Stack'));
+
+    List frames = stack['frames'];
+    expect(frames.length, greaterThanOrEqualTo(1));
+
+    Frame topFrame = frames[0];
+    ServiceFunction function = await topFrame.function.load();
+    String name = function.name;
+    if (includeOwner) {
+      ServiceFunction owner =
+          await (function.dartOwner as ServiceObject).load();
+      name = '${owner.name}.$name';
+    }
+    final bool matches =
+        contains ? name.contains(functionName) : name == functionName;
+    if (!matches) {
+      StringBuffer sb = new StringBuffer();
+      sb.write("Expected to be in function $functionName but "
+          "actually in function $name");
+      sb.write("\nFull stack trace:\n");
+      for (Frame f in frames) {
+        await f.function.load();
+        await (f.function.dartOwner as ServiceObject).load();
+        String name = f.function.name;
+        String ownerName = (f.function.dartOwner as ServiceObject).name;
+        sb.write(" $f [$name] [$ownerName]\n");
+      }
+      throw sb.toString();
+    } else {
+      print('Program is stopped in function: $functionName');
+    }
+  };
+}
+
+IsolateTest hasLocalVarInTopAwaiterStackFrame(String varName) {
+  return (Isolate isolate) async {
+    print("Checking we have variable '$varName' in the top frame");
+
+    // Make sure that the isolate has stopped.
+    await isolate.reload();
+    expect(isolate.pauseEvent is! M.ResumeEvent, isTrue);
+
+    final ServiceMap stack = await isolate.getStack();
+    expect(stack.type, equals('Stack'));
+
+    final List frames = stack['awaiterFrames'];
+    expect(frames.length, greaterThanOrEqualTo(1));
+
+    final Frame top = frames[0];
+    for (final variable in top.variables) {
+      if (variable.name == varName) {
+        return;
+      }
+    }
+    final sb = StringBuffer();
+    sb.write("Expected to find $varName in top awaiter stack frame, found ");
+    if (top.variables.isEmpty) {
+      sb.write("no variables\n");
+    } else {
+      sb.write("these instead:\n");
+      for (var variable in top.variables) {
+        sb.write("\t${variable.name}\n");
+      }
+    }
+    throw sb.toString();
+  };
+}
+
+Future resumeIsolate(Isolate isolate) {
+  Completer completer = new Completer();
+  isolate.vm.getEventStream(VM.kDebugStream).then((stream) {
+    var subscription;
+    subscription = stream.listen((ServiceEvent event) {
+      if (event.kind == ServiceEvent.kResume) {
+        subscription.cancel();
+        completer.complete();
+      }
+    });
+  });
+  isolate.resume();
+  return completer.future;
+}
+
+Future resumeAndAwaitEvent(Isolate isolate, stream, onEvent) async {
+  Completer completer = new Completer();
+  var sub;
+  sub = await isolate.vm.listenEventStream(stream, (ServiceEvent event) {
+    var r = onEvent(event);
+    if (r is! Future) {
+      r = new Future.value(r);
+    }
+    r.then((x) => sub.cancel().then((_) {
+          completer.complete();
+        }));
+  });
+  await isolate.resume();
+  return completer.future;
+}
+
+IsolateTest resumeIsolateAndAwaitEvent(stream, onEvent) {
+  return (Isolate isolate) async =>
+      resumeAndAwaitEvent(isolate, stream, onEvent);
+}
+
+Future stepOver(Isolate isolate) async {
+  await isolate.stepOver();
+  return hasStoppedAtBreakpoint(isolate);
+}
+
+Future stepInto(Isolate isolate) async {
+  await isolate.stepInto();
+  return hasStoppedAtBreakpoint(isolate);
+}
+
+Future stepOut(Isolate isolate) async {
+  await isolate.stepOut();
+  return hasStoppedAtBreakpoint(isolate);
+}
+
+Future isolateIsRunning(Isolate isolate) async {
+  await isolate.reload();
+  expect(isolate.running, true);
+}
+
+Future<Class> getClassFromRootLib(Isolate isolate, String className) async {
+  Library rootLib = await isolate.rootLibrary.load();
+  for (Class cls in rootLib.classes) {
+    if (cls.name == className) {
+      return cls;
+    }
+  }
+  return null;
+}
+
+Future<Instance> rootLibraryFieldValue(
+    Isolate isolate, String fieldName) async {
+  Library rootLib = await isolate.rootLibrary.load();
+  Field field = rootLib.variables.singleWhere((v) => v.name == fieldName);
+  await field.load();
+  Instance value = field.staticValue;
+  await value.load();
+  return value;
+}
+
+IsolateTest runStepThroughProgramRecordingStops(List<String> recordStops) {
+  return (Isolate isolate) async {
+    Completer completer = new Completer();
+
+    await subscribeToStream(isolate.vm, VM.kDebugStream,
+        (ServiceEvent event) async {
+      if (event.kind == ServiceEvent.kPauseBreakpoint) {
+        await isolate.reload();
+        // We are paused: Step further.
+        Frame frame = isolate.topFrame;
+        recordStops.add(await frame.location.toUserString());
+        if (event.atAsyncSuspension) {
+          isolate.stepOverAsyncSuspension();
+        } else {
+          isolate.stepOver();
+        }
+      } else if (event.kind == ServiceEvent.kPauseExit) {
+        // We are at the exit: The test is done.
+        await cancelStreamSubscription(VM.kDebugStream);
+        completer.complete();
+      }
+    });
+    isolate.resume();
+    return completer.future;
+  };
+}
+
+IsolateTest resumeProgramRecordingStops(
+    List<String> recordStops, bool includeCaller) {
+  return (Isolate isolate) async {
+    Completer completer = new Completer();
+
+    await subscribeToStream(isolate.vm, VM.kDebugStream,
+        (ServiceEvent event) async {
+      if (event.kind == ServiceEvent.kPauseBreakpoint) {
+        await isolate.reload();
+        // We are paused: Resume after recording.
+        ServiceMap stack = await isolate.getStack();
+        expect(stack.type, equals('Stack'));
+        List frames = stack['frames'];
+        expect(frames.length, greaterThanOrEqualTo(2));
+        Frame frame = frames[0];
+        String brokeAt = await frame.location.toUserString();
+        if (includeCaller) {
+          frame = frames[1];
+          String calledFrom = await frame.location.toUserString();
+          recordStops.add("$brokeAt ($calledFrom)");
+        } else {
+          recordStops.add(brokeAt);
+        }
+
+        isolate.resume();
+      } else if (event.kind == ServiceEvent.kPauseExit) {
+        // We are at the exit: The test is done.
+        await cancelStreamSubscription(VM.kDebugStream);
+        completer.complete();
+      }
+    });
+    print("Resuming!");
+    isolate.resume();
+    return completer.future;
+  };
+}
+
+IsolateTest runStepIntoThroughProgramRecordingStops(List<String> recordStops) {
+  return (Isolate isolate) async {
+    Completer completer = new Completer();
+
+    await subscribeToStream(isolate.vm, VM.kDebugStream,
+        (ServiceEvent event) async {
+      if (event.kind == ServiceEvent.kPauseBreakpoint) {
+        await isolate.reload();
+        // We are paused: Step into further.
+        Frame frame = isolate.topFrame;
+        recordStops.add(await frame.location.toUserString());
+        isolate.stepInto();
+      } else if (event.kind == ServiceEvent.kPauseExit) {
+        // We are at the exit: The test is done.
+        await cancelStreamSubscription(VM.kDebugStream);
+        completer.complete();
+      }
+    });
+    isolate.resume();
+    return completer.future;
+  };
+}
+
+IsolateTest checkRecordedStops(
+    List<String> recordStops, List<String> expectedStops,
+    {bool removeDuplicates = false,
+    bool debugPrint = false,
+    String debugPrintFile,
+    int debugPrintLine}) {
+  return (Isolate isolate) async {
+    if (debugPrint) {
+      for (int i = 0; i < recordStops.length; i++) {
+        String line = recordStops[i];
+        String output = line;
+        int firstColon = line.indexOf(":");
+        int lastColon = line.lastIndexOf(":");
+        if (debugPrintFile != null &&
+            debugPrintLine != null &&
+            firstColon > 0 &&
+            lastColon > 0) {
+          int lineNumber = int.parse(line.substring(firstColon + 1, lastColon));
+          int relativeLineNumber = lineNumber - debugPrintLine;
+          var columnNumber = line.substring(lastColon + 1);
+          var file = line.substring(0, firstColon);
+          if (file == debugPrintFile) {
+            output = '\$file:\${LINE+$relativeLineNumber}:$columnNumber';
+          }
+        }
+        String comma = i == recordStops.length - 1 ? "" : ",";
+        print('"$output"$comma');
+      }
+    }
+    if (removeDuplicates) {
+      recordStops = removeAdjacentDuplicates(recordStops);
+      expectedStops = removeAdjacentDuplicates(expectedStops);
+    }
+
+    // Single stepping in interpreted bytecode may record extra stops.
+    // Allow the extra ones as long as the expected ones are recorded.
+    int i = 0;
+    int j = 0;
+    while (i < recordStops.length && j < expectedStops.length) {
+      if (recordStops[i] != expectedStops[j]) {
+        // Check if recordStops[i] is an extra stop.
+        int k = i + 1;
+        while (k < recordStops.length && recordStops[k] != expectedStops[j]) {
+          k++;
+        }
+        if (k < recordStops.length) {
+          // Allow and ignore extra recorded stops from i to k-1.
+          i = k;
+        } else {
+          // This will report an error.
+          expect(recordStops[i], expectedStops[j]);
+        }
+      }
+      i++;
+      j++;
+    }
+
+    expect(recordStops.length >= expectedStops.length, true,
+        reason: "Expects at least ${expectedStops.length} breaks, "
+            "got ${recordStops.length}.");
+  };
+}
+
+List<String> removeAdjacentDuplicates(List<String> fromList) {
+  List<String> result = <String>[];
+  String latestLine;
+  for (String s in fromList) {
+    if (s == latestLine) continue;
+    latestLine = s;
+    result.add(s);
+  }
+  return result;
+}
+
+Future<void> waitForTargetVMExit(VM vm) async => await vm.onDisconnect;
diff --git a/runtime/observatory_2/tests/service_2/set_library_debuggable_rpc_test.dart b/runtime/observatory_2/tests/service_2/set_library_debuggable_rpc_test.dart
new file mode 100644
index 0000000..8059cf9a
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/set_library_debuggable_rpc_test.dart
@@ -0,0 +1,59 @@
+// Copyright (c) 2015, 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.
+
+library set_library_debuggable_rpc_test;
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+
+import 'test_helper.dart';
+
+var tests = <IsolateTest>[
+  (Isolate isolate) async {
+    var result;
+
+    // debuggable defaults to true.
+    var getObjectParams = {
+      'objectId': isolate.rootLibrary.id,
+    };
+    result = await isolate.invokeRpcNoUpgrade('getObject', getObjectParams);
+    expect(result['debuggable'], equals(true));
+
+    // Change debuggable to false.
+    var setDebugParams = {
+      'libraryId': isolate.rootLibrary.id,
+      'isDebuggable': false,
+    };
+    result = await isolate.invokeRpcNoUpgrade(
+        'setLibraryDebuggable', setDebugParams);
+    expect(result['type'], equals('Success'));
+
+    // Verify.
+    result = await isolate.invokeRpcNoUpgrade('getObject', getObjectParams);
+    expect(result['debuggable'], equals(false));
+  },
+
+  // invalid library.
+  (Isolate isolate) async {
+    var params = {
+      'libraryId': 'libraries/9999999',
+      'isDebuggable': false,
+    };
+    bool caughtException;
+    try {
+      await isolate.invokeRpcNoUpgrade('setLibraryDebuggable', params);
+      expect(false, isTrue, reason: 'Unreachable');
+    } on ServerRpcException catch (e) {
+      caughtException = true;
+      expect(e.code, equals(ServerRpcException.kInvalidParams));
+      expect(
+          e.message,
+          "setLibraryDebuggable: "
+          "invalid 'libraryId' parameter: libraries/9999999");
+    }
+    expect(caughtException, isTrue);
+  },
+];
+
+main(args) async => runIsolateTests(args, tests);
diff --git a/runtime/observatory_2/tests/service_2/set_library_debuggable_test.dart b/runtime/observatory_2/tests/service_2/set_library_debuggable_test.dart
new file mode 100644
index 0000000..89a8b52
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/set_library_debuggable_test.dart
@@ -0,0 +1,65 @@
+// Copyright (c) 2016, 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.
+
+library set_library_debuggable_test;
+
+import 'dart:developer';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+const LINE_A = 19;
+const LINE_B = 20;
+const LINE_C = 21;
+
+testMain() async {
+  debugger();
+  print('hi'); // LINE_A.
+  print('yep'); // LINE_B.
+  print('zoo'); // LINE_C.
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  markDartColonLibrariesDebuggable,
+  (Isolate isolate) async {
+    await isolate.reload();
+    Library dartCore = isolate.libraries
+        .firstWhere((Library library) => library.uri == 'dart:core');
+    await dartCore.reload();
+    expect(dartCore.debuggable, equals(true));
+  },
+  stoppedInFunction('testMain', contains: true, includeOwner: true),
+  stoppedAtLine(LINE_A),
+  stepInto,
+  stoppedInFunction('print'),
+  stepOut,
+  stoppedInFunction('testMain', contains: true, includeOwner: true),
+  stoppedAtLine(LINE_B),
+  (Isolate isolate) async {
+    // Mark 'dart:core' as not debuggable.
+    await isolate.reload();
+    Library dartCore = isolate.libraries
+        .firstWhere((Library library) => library.uri == 'dart:core');
+    await dartCore.load();
+    expect(dartCore.debuggable, equals(true));
+    var setDebugParams = {
+      'libraryId': dartCore.id,
+      'isDebuggable': false,
+    };
+    Map<String, dynamic> result = await isolate.invokeRpcNoUpgrade(
+        'setLibraryDebuggable', setDebugParams);
+    expect(result['type'], equals('Success'));
+    await dartCore.reload();
+    expect(dartCore.debuggable, equals(false));
+  },
+  stoppedInFunction('testMain', contains: true, includeOwner: true),
+  stoppedAtLine(LINE_B),
+  stepInto,
+  stoppedInFunction('testMain', contains: true, includeOwner: true),
+  stoppedAtLine(LINE_C),
+];
+
+main(args) async => runIsolateTests(args, tests, testeeConcurrent: testMain);
diff --git a/runtime/observatory_2/tests/service_2/set_name_rpc_test.dart b/runtime/observatory_2/tests/service_2/set_name_rpc_test.dart
new file mode 100644
index 0000000..f6ffc4e
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/set_name_rpc_test.dart
@@ -0,0 +1,34 @@
+// Copyright (c) 2015, 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.
+// VMOptions=--vm-name=Walter
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+import 'dart:async';
+
+var tests = <IsolateTest>[
+  (Isolate isolate) async {
+    expect(isolate.name == 'main', isTrue);
+    Completer completer = new Completer();
+    var stream = await isolate.vm.getEventStream(VM.kIsolateStream);
+    var subscription;
+    subscription = stream.listen((ServiceEvent event) {
+      if (event.kind == ServiceEvent.kIsolateUpdate) {
+        expect(event.owner.type, equals('Isolate'));
+        expect(event.owner.name, equals('Barbara'));
+        subscription.cancel();
+        completer.complete();
+      }
+    });
+
+    var result = await isolate.setName('Barbara');
+    expect(result.type, equals('Success'));
+
+    await completer.future;
+    expect(isolate.name, equals('Barbara'));
+  }
+];
+
+main(args) async => runIsolateTests(args, tests);
diff --git a/runtime/observatory_2/tests/service_2/set_vm_name_rpc_test.dart b/runtime/observatory_2/tests/service_2/set_vm_name_rpc_test.dart
new file mode 100644
index 0000000..e15b005
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/set_vm_name_rpc_test.dart
@@ -0,0 +1,36 @@
+// Copyright (c) 2015, 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.
+// VMOptions=--vm-name=Walter
+
+import 'dart:async';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+
+var tests = <VMTest>[
+  (VM vm) async {
+    expect(vm.name, equals('Walter'));
+
+    Completer completer = new Completer();
+    var stream = await vm.getEventStream(VM.kVMStream);
+    var subscription;
+    subscription = stream.listen((ServiceEvent event) {
+      if (event.kind == ServiceEvent.kVMUpdate) {
+        expect(event.owner.type, equals('VM'));
+        expect(event.owner.name, equals('Barbara'));
+        subscription.cancel();
+        completer.complete();
+      }
+    });
+
+    var result = await vm.setName('Barbara');
+    expect(result.type, equals('Success'));
+
+    await completer.future;
+    expect(vm.name, equals('Barbara'));
+  },
+];
+
+main(args) async => runVMTests(args, tests,
+    extraArgs: ['--trace-service', '--trace-service-verbose']);
diff --git a/runtime/observatory_2/tests/service_2/simple_reload/v1/main.dart b/runtime/observatory_2/tests/service_2/simple_reload/v1/main.dart
new file mode 100644
index 0000000..887ded3
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/simple_reload/v1/main.dart
@@ -0,0 +1,15 @@
+// Copyright (c) 2018, 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.
+
+import 'dart:isolate';
+
+test() => 'apple';
+
+class Foo<T> {}
+
+main() {
+  RawReceivePort keepAlive = new RawReceivePort();
+  new Foo<String>();
+  print('spawned isolate running');
+}
diff --git a/runtime/observatory_2/tests/service_2/simple_reload/v2/main.dart b/runtime/observatory_2/tests/service_2/simple_reload/v2/main.dart
new file mode 100644
index 0000000..0be97ce
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/simple_reload/v2/main.dart
@@ -0,0 +1,14 @@
+// Copyright (c) 2018, 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.
+
+import 'dart:isolate';
+
+test() => 'orange';
+
+class Foo<T> {}
+
+main() {
+  RawReceivePort keepAlive = new RawReceivePort();
+  print('spawned isolate running');
+}
diff --git a/runtime/observatory_2/tests/service_2/simple_reload_test.dart b/runtime/observatory_2/tests/service_2/simple_reload_test.dart
new file mode 100644
index 0000000..9fbfebc
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/simple_reload_test.dart
@@ -0,0 +1,76 @@
+// Copyright (c) 2018, 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.
+//
+// OtherResources=simple_reload/v1/main.dart simple_reload/v2/main.dart
+
+import 'test_helper.dart';
+import 'dart:async';
+import 'dart:developer';
+import 'dart:isolate' as I;
+import 'dart:io';
+import 'service_test_common.dart';
+import 'package:observatory_2/service.dart';
+import 'package:path/path.dart' as path;
+import 'package:test/test.dart';
+
+// Chop off the file name.
+String baseDirectory = path.dirname(Platform.script.path) + '/';
+
+Uri baseUri = Platform.script.replace(path: baseDirectory);
+Uri spawnUri = baseUri.resolveUri(Uri.parse('simple_reload/v1/main.dart'));
+Uri v2Uri = baseUri.resolveUri(Uri.parse('simple_reload/v2/main.dart'));
+
+testMain() async {
+  print(baseUri);
+  debugger(); // Stop here.
+  // Spawn the child isolate.
+  I.Isolate isolate = await I.Isolate.spawnUri(spawnUri, [], null);
+  print(isolate);
+  debugger();
+}
+
+Future<String> invokeTest(Isolate isolate) async {
+  await isolate.reload();
+  Library lib = isolate.rootLibrary;
+  await lib.load();
+  Instance result = await lib.evaluate('test()');
+  expect(result.isString, isTrue);
+  return result.valueAsString;
+}
+
+var tests = <IsolateTest>[
+  // Stopped at 'debugger' statement.
+  hasStoppedAtBreakpoint,
+  // Resume the isolate into the while loop.
+  resumeIsolate,
+  // Stop at 'debugger' statement.
+  hasStoppedAtBreakpoint,
+  (Isolate mainIsolate) async {
+    // Grab the VM.
+    VM vm = mainIsolate.vm;
+    await vm.reloadIsolates();
+    expect(vm.isolates.length, 2);
+
+    // Find the spawned isolate.
+    Isolate spawnedIsolate =
+        vm.isolates.firstWhere((Isolate i) => i != mainIsolate);
+    expect(spawnedIsolate, isNotNull);
+
+    // Invoke test in v1.
+    String v1 = await invokeTest(spawnedIsolate);
+    expect(v1, 'apple');
+
+    // Reload to v2.
+    var response = await spawnedIsolate.reloadSources(
+      rootLibUri: v2Uri.toString(),
+    );
+    // Observe that it succeed.
+    expect(response['success'], isTrue);
+
+    String v2 = await invokeTest(spawnedIsolate);
+    expect(v2, 'orange');
+  }
+];
+
+main(args) => runIsolateTests(args, tests, testeeConcurrent: testMain);
diff --git a/runtime/observatory_2/tests/service_2/steal_breakpoint_test.dart b/runtime/observatory_2/tests/service_2/steal_breakpoint_test.dart
new file mode 100644
index 0000000..9da34e8
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/steal_breakpoint_test.dart
@@ -0,0 +1,79 @@
+// Copyright (c) 2015, 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.
+// VMOptions=--steal-breakpoints
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+import 'dart:async';
+
+int counter = 0;
+
+void periodicTask(_) {
+  counter++; // Line 14.  We set our breakpoint here.
+  if (counter % 1000 == 0) {
+    print('counter = $counter');
+  }
+}
+
+void startTimer() {
+  new Timer.periodic(const Duration(milliseconds: 10), periodicTask);
+}
+
+var tests = <IsolateTest>[
+// Add a breakpoint and wait for it to be reached.
+  (Isolate isolate) async {
+    await isolate.rootLibrary.load();
+
+    // Set up a listener to wait for breakpoint events.
+    Completer completer = new Completer();
+    var stream = await isolate.vm.getEventStream(VM.kDebugStream);
+    var subscription;
+    subscription = stream.listen((ServiceEvent event) {
+      if (event.kind == ServiceEvent.kPauseBreakpoint) {
+        print('Isolate paused at breakpoint');
+        subscription.cancel();
+        completer.complete();
+      }
+    });
+
+    // Add the breakpoint.
+    var script = isolate.rootLibrary.scripts[0];
+    var result = await isolate.addBreakpoint(script, 14);
+    expect(result is Breakpoint, isTrue);
+
+    await completer.future; // Wait for breakpoint event to fire.
+  },
+
+// We are at the breakpoint on line 14.
+  (Isolate isolate) async {
+    ServiceMap stack = await isolate.getStack();
+    expect(stack.type, equals('Stack'));
+    expect(stack['frames'].length, greaterThanOrEqualTo(1));
+
+    Script script = stack['frames'][0].location.script;
+    await script.load();
+    expect(script.name, endsWith('steal_breakpoint_test.dart'));
+    expect(
+        script.tokenToLine(stack['frames'][0].location.tokenPos), equals(14));
+  },
+
+// Resume
+  (Isolate isolate) async {
+    Completer completer = new Completer();
+    var stream = await isolate.vm.getEventStream(VM.kDebugStream);
+    var subscription;
+    subscription = stream.listen((ServiceEvent event) {
+      if (event.kind == ServiceEvent.kResume) {
+        print('Isolate resumed');
+        subscription.cancel();
+        completer.complete();
+      }
+    });
+    isolate.resume();
+    await completer.future;
+  },
+];
+
+main(args) => runIsolateTests(args, tests, testeeBefore: startTimer);
diff --git a/runtime/observatory_2/tests/service_2/step_into_async_no_await_test.dart b/runtime/observatory_2/tests/service_2/step_into_async_no_await_test.dart
new file mode 100644
index 0000000..23cf4df
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/step_into_async_no_await_test.dart
@@ -0,0 +1,35 @@
+// Copyright (c) 2015, 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.
+// VMOptions=--verbose_debug
+
+import 'test_helper.dart';
+import 'dart:developer';
+import 'service_test_common.dart';
+
+const int LINE_A = 20;
+
+// :async_op will not be captured in this function because it never needs to
+// reschedule it.
+asyncWithoutAwait() async {
+  print("asyncWithoutAwait");
+}
+
+testMain() {
+  debugger();
+  asyncWithoutAwait(); // Line A.
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_A),
+  (isolate) => isolate.stepInto(),
+  hasStoppedAtBreakpoint,
+  (isolate) => isolate.getStack(), // Should not crash.
+  // TODO(rmacnak): stoppedAtLine(12)
+  // This doesn't happen because asyncWithoutAwait is marked undebuggable.
+  // Probably needs to change to support async-step-into.
+  resumeIsolate,
+];
+
+main(args) => runIsolateTests(args, tests, testeeConcurrent: testMain);
diff --git a/runtime/observatory_2/tests/service_2/step_over_await_test.dart b/runtime/observatory_2/tests/service_2/step_over_await_test.dart
new file mode 100644
index 0000000..8cdf1b5
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/step_over_await_test.dart
@@ -0,0 +1,47 @@
+// Copyright (c) 2015, 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.
+// VMOptions=--verbose_debug
+
+import 'dart:async';
+import 'dart:developer';
+
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+import 'package:observatory_2/models.dart' as M;
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+
+const int LINE_A = 22;
+const int LINE_B = 24;
+
+// This tests the asyncNext command.
+asyncFunction() async {
+  debugger();
+  print('a'); // LINE_A
+  await new Future.delayed(new Duration(seconds: 2));
+  print('b'); // LINE_B
+}
+
+testMain() {
+  asyncFunction();
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_A),
+  stepOver, // At new Duration().
+  stepOver, // At new Future.delayed().
+  stepOver, // At async.
+  // Check that we are at the async statement
+  (Isolate isolate) async {
+    expect(M.isAtAsyncSuspension(isolate.pauseEvent), isTrue);
+  },
+  asyncNext,
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE_B),
+  resumeIsolate,
+];
+
+main(args) => runIsolateTests(args, tests, testeeConcurrent: testMain);
diff --git a/runtime/observatory_2/tests/service_2/step_test.dart b/runtime/observatory_2/tests/service_2/step_test.dart
new file mode 100644
index 0000000..94903f6
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/step_test.dart
@@ -0,0 +1,47 @@
+// Copyright (c) 2017, 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.
+
+import 'dart:async';
+import 'test_helper.dart';
+import 'service_test_common.dart';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+
+const int LINE_A = 14;
+
+code() {
+  var x = {}; // LINE_A
+}
+
+Future stepThroughProgram(Isolate isolate) async {
+  Completer completer = new Completer();
+  int pauseEventsSeen = 0;
+
+  await subscribeToStream(isolate.vm, VM.kDebugStream,
+      (ServiceEvent event) async {
+    if (event.kind == ServiceEvent.kPauseBreakpoint) {
+      // We are paused: Step further.
+      pauseEventsSeen++;
+      isolate.stepInto();
+    } else if (event.kind == ServiceEvent.kPauseExit) {
+      // We are at the exit: The test is done.
+      expect(pauseEventsSeen > 20, true,
+          reason: "Saw only $pauseEventsSeen pause events.");
+      await cancelStreamSubscription(VM.kDebugStream);
+      completer.complete();
+    }
+  });
+  isolate.resume();
+  return completer.future;
+}
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  markDartColonLibrariesDebuggable,
+  setBreakpointAtLine(LINE_A),
+  stepThroughProgram
+];
+
+main(args) => runIsolateTestsSynchronous(args, tests,
+    testeeConcurrent: code, pause_on_start: true, pause_on_exit: true);
diff --git a/runtime/observatory_2/tests/service_2/step_through_arithmetic_test.dart b/runtime/observatory_2/tests/service_2/step_through_arithmetic_test.dart
new file mode 100644
index 0000000..6c2c1d8
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/step_through_arithmetic_test.dart
@@ -0,0 +1,51 @@
+// Copyright (c) 2017, 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.
+
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+const int LINE = 11;
+const String file = "step_through_arithmetic_test.dart";
+
+code() {
+  print(1 + 2);
+  print((1 + 2) / 2);
+  print(1 + 2 * 3);
+  print((1 + 2) * 3);
+}
+
+List<String> stops = [];
+List<String> expected = [
+  "$file:${LINE+0}:5", // after 'code'
+
+  "$file:${LINE+1}:11", // on '+'
+  "$file:${LINE+1}:3", // on 'print'
+
+  "$file:${LINE+2}:12", // on '+'
+  "$file:${LINE+2}:17", // on '/'
+  "$file:${LINE+2}:3", // on 'print'
+
+  "$file:${LINE+3}:15", // on '*'
+  "$file:${LINE+3}:11", // on '+'
+  "$file:${LINE+3}:3", // on 'print'
+
+  "$file:${LINE+4}:12", // on '+'
+  "$file:${LINE+4}:17", // on '*'
+  "$file:${LINE+4}:3", // on 'print'
+
+  "$file:${LINE+5}:1" // on ending '}'
+];
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  setBreakpointAtLine(LINE),
+  runStepIntoThroughProgramRecordingStops(stops),
+  checkRecordedStops(stops, expected,
+      debugPrint: true, debugPrintFile: file, debugPrintLine: LINE)
+];
+
+main(args) {
+  runIsolateTestsSynchronous(args, tests,
+      testeeConcurrent: code, pause_on_start: true, pause_on_exit: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/step_through_constructor_calls_test.dart b/runtime/observatory_2/tests/service_2/step_through_constructor_calls_test.dart
new file mode 100644
index 0000000..a78fb57
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/step_through_constructor_calls_test.dart
@@ -0,0 +1,75 @@
+// Copyright (c) 2017, 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.
+
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+const int LINE = 12;
+const String file = "step_through_constructor_calls_test.dart";
+
+code() {
+  Foo foo1 = new Foo();
+  print(foo1.x);
+  Foo foo2 = new Foo.named();
+  print(foo2.x);
+  Foo foo3 = const Foo();
+  print(foo3.x);
+  Foo foo4 = const Foo.named();
+  print(foo4.x);
+  Foo foo5 = new Foo.named2(1, 2, 3);
+  print(foo5.x);
+}
+
+class Foo {
+  final int x;
+
+  const Foo() : x = 1;
+
+  const Foo.named() : x = 2;
+
+  const Foo.named2(int aaaaaaaa, int bbbbbbbbbb, int ccccccccccccc)
+      : x = aaaaaaaa + bbbbbbbbbb + ccccccccccccc;
+}
+
+List<String> stops = [];
+List<String> expected = [
+  "$file:${LINE+0}:18", // on 'Foo'
+  "$file:${LINE+15}:12", // on '(' in 'const Foo() : x = 1;'
+  "$file:${LINE+15}:22", // on ';' in same line
+  "$file:${LINE+1}:14", // on 'x'
+  "$file:${LINE+1}:3", // on print
+  "$file:${LINE+2}:18", // on 'Foo'
+  "$file:${LINE+17}:18", // on '(' in 'const Foo.named() : x = 2;'
+  "$file:${LINE+17}:28", // on ';' in same line
+  "$file:${LINE+3}:14", // on 'x'
+  "$file:${LINE+3}:3", // on print
+  "$file:${LINE+4}:12", // on '='
+  "$file:${LINE+5}:14", // on 'x'
+  "$file:${LINE+5}:3", // on print
+  "$file:${LINE+6}:12", // on '='
+  "$file:${LINE+7}:14", // on 'x'
+  "$file:${LINE+7}:3", // on print
+  "$file:${LINE+8}:18", // on 'Foo'
+  "$file:${LINE+19}:54", // on 'ccccccccccccc'
+  "$file:${LINE+20}:22", // on first '+'
+  "$file:${LINE+20}:35", // on second '+'
+  "$file:${LINE+20}:50", // on ';'
+  "$file:${LINE+9}:14", // on 'x'
+  "$file:${LINE+9}:3", // on print
+  "$file:${LINE+10}:1" // on ending '}'
+];
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  setBreakpointAtLine(LINE),
+  runStepIntoThroughProgramRecordingStops(stops),
+  // removeDuplicates: Source-based debugging stops on the ';'
+  // in the constructors twice. Kernel does not. For now we'll accept that.
+  checkRecordedStops(stops, expected, removeDuplicates: true)
+];
+
+main(args) {
+  runIsolateTestsSynchronous(args, tests,
+      testeeConcurrent: code, pause_on_start: true, pause_on_exit: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/step_through_constructor_test.dart b/runtime/observatory_2/tests/service_2/step_through_constructor_test.dart
new file mode 100644
index 0000000..42fc5d6
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/step_through_constructor_test.dart
@@ -0,0 +1,38 @@
+// Copyright (c) 2017, 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.
+
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+const int LINE = 17;
+const String file = "step_through_constructor_test.dart";
+
+code() {
+  new Foo();
+}
+
+class Foo {
+  Foo() {
+    print("Hello from Foo!");
+  }
+}
+
+List<String> stops = [];
+List<String> expected = [
+  "$file:${LINE+0}:5", // on 'print'
+  "$file:${LINE+1}:3", // on ending '}'
+];
+
+var tests = [
+  hasPausedAtStart,
+  setBreakpointAtLine(LINE),
+  runStepIntoThroughProgramRecordingStops(stops),
+  checkRecordedStops(stops, expected,
+      debugPrint: true, debugPrintFile: file, debugPrintLine: LINE)
+];
+
+main(args) {
+  runIsolateTestsSynchronous(args, tests,
+      testeeConcurrent: code, pause_on_start: true, pause_on_exit: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/step_through_for_each_sync_star_2_test.dart b/runtime/observatory_2/tests/service_2/step_through_for_each_sync_star_2_test.dart
new file mode 100644
index 0000000..16ee4c9
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/step_through_for_each_sync_star_2_test.dart
@@ -0,0 +1,65 @@
+// Copyright (c) 2018, 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.
+
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+const int LINE = 11;
+const String file = "step_through_for_each_sync_star_2_test.dart";
+
+code() {
+  for (int datapoint in generator()) {
+    print(datapoint);
+  }
+}
+
+generator() sync* {
+  var x = 3;
+  var y = 4;
+  yield x;
+  yield x + y;
+}
+
+List<String> stops = [];
+List<String> expected = [
+  "$file:${LINE + 0}:5", // after 'code'
+  "$file:${LINE + 1}:25", // on 'generator' (in 'for' line)
+
+  "$file:${LINE + 6}:10", // after 'generator' (definition line)
+  "$file:${LINE + 7}:9", // on '=' in 'x = 3'
+  "$file:${LINE + 8}:9", // on '=' in 'y = 4'
+  "$file:${LINE + 9}:3", // on yield
+
+  "$file:${LINE + 1}:38", // on '{' in 'for' line
+  "$file:${LINE + 1}:12", // on 'datapoint'
+  "$file:${LINE + 2}:5", // on 'print'
+  "$file:${LINE + 1}:25", // on 'generator' (in 'for' line)
+
+  "$file:${LINE + 6}:10", // after 'generator' (definition line)
+  "$file:${LINE + 10}:11", // on '+' in 'x + y'
+  "$file:${LINE + 10}:3", // on yield
+
+  "$file:${LINE + 1}:38", // on '{' in 'for' line
+  "$file:${LINE + 1}:12", // on 'datapoint'
+  "$file:${LINE + 2}:5", // on 'print'
+  "$file:${LINE + 1}:25", // on 'generator' (in 'for' line)
+
+  "$file:${LINE + 6}:10", // after 'generator' (definition line)
+  "$file:${LINE + 11}:1", // on ending '}' of 'generator'
+
+  "$file:${LINE + 4}:1", // on ending '}' of 'code''
+];
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  setBreakpointAtLine(LINE),
+  runStepIntoThroughProgramRecordingStops(stops),
+  checkRecordedStops(stops, expected,
+      debugPrint: true, debugPrintFile: file, debugPrintLine: LINE)
+];
+
+main(args) {
+  runIsolateTestsSynchronous(args, tests,
+      testeeConcurrent: code, pause_on_start: true, pause_on_exit: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/step_through_for_each_sync_star_test.dart b/runtime/observatory_2/tests/service_2/step_through_for_each_sync_star_test.dart
new file mode 100644
index 0000000..676c5e0
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/step_through_for_each_sync_star_test.dart
@@ -0,0 +1,66 @@
+// Copyright (c) 2018, 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.
+
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+const int LINE = 11;
+const String file = "step_through_for_each_sync_star_test.dart";
+
+code() {
+  for (int datapoint in generator()) {
+    print(datapoint);
+  }
+}
+
+generator() sync* {
+  var x = 3;
+  var y = 4;
+  yield y;
+  var z = x + y;
+  yield z;
+}
+
+List<String> stops = [];
+List<String> expected = [
+  "$file:${LINE + 0}:5", // after 'code'
+  "$file:${LINE + 1}:25", // on 'generator' (in 'for' line)
+
+  "$file:${LINE + 6}:10", // after 'generator' (definition line)
+  "$file:${LINE + 7}:9", // on '=' in 'x = 3'
+  "$file:${LINE + 8}:9", // on '=' in 'y = 4'
+  "$file:${LINE + 9}:3", // on yield
+
+  "$file:${LINE + 1}:38", // on '{' in 'for' line
+  "$file:${LINE + 1}:12", // on 'datapoint'
+  "$file:${LINE + 2}:5", // on 'print'
+  "$file:${LINE + 1}:25", // on 'generator' (in 'for' line)
+
+  "$file:${LINE + 6}:10", // after 'generator' (definition line)
+  "$file:${LINE + 10}:13", // on '+' in 'z = x + y'
+  "$file:${LINE + 11}:3", // on yield
+
+  "$file:${LINE + 1}:38", // on '{' in 'for' line
+  "$file:${LINE + 1}:12", // on 'datapoint'
+  "$file:${LINE + 2}:5", // on 'print'
+  "$file:${LINE + 1}:25", // on 'generator' (in 'for' line)
+
+  "$file:${LINE + 6}:10", // after 'generator' (definition line)
+  "$file:${LINE + 12}:1", // on ending '}' of 'generator'
+
+  "$file:${LINE + 4}:1", // on ending '}' of 'code''
+];
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  setBreakpointAtLine(LINE),
+  runStepIntoThroughProgramRecordingStops(stops),
+  checkRecordedStops(stops, expected,
+      debugPrint: true, debugPrintFile: file, debugPrintLine: LINE)
+];
+
+main(args) {
+  runIsolateTestsSynchronous(args, tests,
+      testeeConcurrent: code, pause_on_start: true, pause_on_exit: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/step_through_function_2_test.dart b/runtime/observatory_2/tests/service_2/step_through_function_2_test.dart
new file mode 100644
index 0000000..b6b6ff5
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/step_through_function_2_test.dart
@@ -0,0 +1,84 @@
+// Copyright (c) 2017, 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.
+
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+const int LINE = 11;
+const String file = "step_through_function_2_test.dart";
+
+code() {
+  Bar bar = new Bar();
+  bar.barXYZ1(42);
+  bar.barXYZ2(42);
+  fooXYZ1(42);
+  fooXYZ2(42);
+}
+
+// ignore: unused_element
+int _xyz = -1;
+
+fooXYZ1(int i) {
+  _xyz = i - 1;
+}
+
+fooXYZ2(int i) {
+  _xyz = i;
+}
+
+class Bar {
+  int _xyz = -1;
+
+  barXYZ1(int i) {
+    _xyz = i - 1;
+  }
+
+  barXYZ2(int i) {
+    _xyz = i;
+  }
+
+  get barXYZ => _xyz + 1;
+}
+
+List<String> stops = [];
+List<String> expected = [
+  "$file:${LINE + 0}:5", // after 'code'
+  "$file:${LINE + 1}:17", // on 'Bar'
+
+  "$file:${LINE + 2}:7", // on 'barXYZ1'
+  "$file:${LINE + 22}:15", // on 'i'
+  "$file:${LINE + 23}:14", // on '-'
+  "$file:${LINE + 23}:5", // on '_xyz'
+  "$file:${LINE + 24}:3", // on '}'
+
+  "$file:${LINE + 3}:7", // on 'barXYZ2'
+  "$file:${LINE + 26}:15", // on 'i'
+  "$file:${LINE + 27}:5", // on '_xyz'
+  "$file:${LINE + 28}:3", // on '}'
+
+  "$file:${LINE + 4}:3", // on 'fooXYZ1'
+  "$file:${LINE + 11}:13", // on 'i'
+  "$file:${LINE + 12}:12", // on '-'
+  "$file:${LINE + 13}:1", // on '}'
+
+  "$file:${LINE + 5}:3", // on 'fooXYZ2'
+  "$file:${LINE + 15}:13", // on 'i'
+  "$file:${LINE + 16}:3", // on '_xyz'
+  "$file:${LINE + 17}:1", // on '}'
+
+  "$file:${LINE + 6}:1" // on ending '}'
+];
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  setBreakpointAtLine(LINE),
+  runStepIntoThroughProgramRecordingStops(stops),
+  checkRecordedStops(stops, expected,
+      debugPrint: true, debugPrintFile: file, debugPrintLine: LINE)
+];
+
+main(args) {
+  runIsolateTestsSynchronous(args, tests,
+      testeeConcurrent: code, pause_on_start: true, pause_on_exit: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/step_through_function_test.dart b/runtime/observatory_2/tests/service_2/step_through_function_test.dart
new file mode 100644
index 0000000..d206c3a
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/step_through_function_test.dart
@@ -0,0 +1,108 @@
+// Copyright (c) 2017, 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.
+
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+const int LINE = 11;
+const String file = "step_through_function_test.dart";
+
+code() {
+  Bar bar = new Bar();
+  print(bar.barXYZ1());
+  print(bar.barXYZ2(4, 2));
+  print(bar.barXYZ3());
+  print(bar.barXYZ4(4, 2));
+  print(fooXYZ1());
+  print(fooXYZ2(4, 2));
+  print(fooXYZ3());
+  print(fooXYZ4(4, 2));
+}
+
+fooXYZ1 /**/ () => "fooXYZ";
+fooXYZ2 /**/ (int i, int j) => "fooXYZ${i}${j}";
+fooXYZ3 /**/ () {
+  return "fooXYZ";
+}
+
+fooXYZ4 /**/ (int i, int j) {
+  return "fooXYZ${i}${j}";
+}
+
+class Bar {
+  barXYZ1 /**/ () => "barXYZ";
+  barXYZ2 /**/ (int i, int j) => "barXYZ${i}${j}";
+  barXYZ3 /**/ () {
+    return "barXYZ";
+  }
+
+  barXYZ4 /**/ (int i, int j) {
+    return "barXYZ${i}${j}";
+  }
+}
+
+List<String> stops = [];
+List<String> expected = [
+  "$file:${LINE+0}:5", // after 'code'
+  "$file:${LINE+1}:17", // on 'Bar'
+
+  "$file:${LINE+2}:13", // on 'barXYZ1'
+  "$file:${LINE+23}:16", // after 'barXYZ1', i.e. on '('
+  "$file:${LINE+23}:22", // on first '"'
+  "$file:${LINE+2}:3", // on 'print'
+
+  "$file:${LINE+3}:13", // on 'barXYZ2'
+  "$file:${LINE+24}:28", // on 'j'
+  "$file:${LINE+24}:50", // after last '"', i.e. on ';'
+  "$file:${LINE+24}:34", // on first '"'
+  "$file:${LINE+3}:3", // on 'print'
+
+  "$file:${LINE+4}:13", // on 'barXYZ3'
+  "$file:${LINE+25}:16", // after 'barXYZ3', i.e. on '('
+  "$file:${LINE+26}:5", // on 'return'
+  "$file:${LINE+4}:3", // on 'print'
+
+  "$file:${LINE+5}:13", // on 'barXYZ4'
+  "$file:${LINE+29}:28", // on 'j'
+  "$file:${LINE+30}:28", // after last '"', i.e. on ';'
+  "$file:${LINE+30}:5", // on 'return'
+  "$file:${LINE+5}:3", // on 'print'
+
+  "$file:${LINE+6}:9", // on 'fooXYZ1'
+  "$file:${LINE+12}:14", // after 'fooXYZ1', i.e. on '('
+  "$file:${LINE+12}:20", // on first '"'
+  "$file:${LINE+6}:3", // on 'print'
+
+  "$file:${LINE+7}:9", // on 'fooXYZ2'
+  "$file:${LINE+13}:26", // on 'j'
+  "$file:${LINE+13}:48", // after last '"', i.e. on ';'
+  "$file:${LINE+13}:32", // on first '"'
+  "$file:${LINE+7}:3", // on 'print'
+
+  "$file:${LINE+8}:9", // on 'fooXYZ3'
+  "$file:${LINE+14}:14", // after 'fooXYZ3', i.e. on '('
+  "$file:${LINE+15}:3", // on 'return'
+  "$file:${LINE+8}:3", // on 'print'
+
+  "$file:${LINE+9}:9", // on 'fooXYZ4'
+  "$file:${LINE+18}:26", // on 'j'
+  "$file:${LINE+19}:26", // after last '"', i.e. on ';'
+  "$file:${LINE+19}:3", // on 'return'
+  "$file:${LINE+9}:3", // on 'print'
+
+  "$file:${LINE+10}:1" // on ending '}'
+];
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  setBreakpointAtLine(LINE),
+  runStepIntoThroughProgramRecordingStops(stops),
+  checkRecordedStops(stops, expected,
+      debugPrint: true, debugPrintFile: file, debugPrintLine: LINE)
+];
+
+main(args) {
+  runIsolateTestsSynchronous(args, tests,
+      testeeConcurrent: code, pause_on_start: true, pause_on_exit: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/step_through_getter_test.dart b/runtime/observatory_2/tests/service_2/step_through_getter_test.dart
new file mode 100644
index 0000000..f0160c2
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/step_through_getter_test.dart
@@ -0,0 +1,78 @@
+// Copyright (c) 2017, 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.
+
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+const int LINE = 11;
+const String file = "step_through_getter_test.dart";
+
+code() {
+  Bar bar = new Bar();
+  print(bar.barXYZ);
+  print(bar.barXYZ2);
+  print(fooXYZ);
+  print(fooXYZ2);
+}
+
+get fooXYZ => "fooXYZ";
+
+get fooXYZ2 {
+  int i = 42;
+  return "Hello, $i!";
+}
+
+class Bar {
+  get barXYZ => "barXYZ";
+
+  get barXYZ2 {
+    int i = 42;
+    return "Hello, $i!";
+  }
+}
+
+List<String> stops = [];
+List<String> expected = [
+  "$file:${LINE+0}:5", // after 'code'
+  "$file:${LINE+1}:17", // on 'Bar'
+
+  "$file:${LINE+2}:13", // on 'barXYZ'
+  "$file:${LINE+16}:14", // on '=>' in 'get barXYZ => "barXYZ";'
+  "$file:${LINE+16}:17", // on first '"'
+  "$file:${LINE+2}:3", // on 'print'
+
+  "$file:${LINE+3}:13", // on 'barXYZ2'
+  "$file:${LINE+18}:15", // on '{' in 'get barXYZ2 {'
+  "$file:${LINE+19}:11", // on '='
+  "$file:${LINE+20}:24", // after '"', i.e. on ';'
+  "$file:${LINE+20}:5", // on 'return'
+  "$file:${LINE+3}:3", // on 'print'
+
+  "$file:${LINE+4}:9", // on 'fooXYZ'
+  "$file:${LINE+8}:12", // on '=>' in 'get fooXYZ => "fooXYZ";'
+  "$file:${LINE+8}:15", // on first '"'
+  "$file:${LINE+4}:3", // on 'print'
+
+  "$file:${LINE+5}:9", // on 'fooXYZ2'
+  "$file:${LINE+10}:13", // on '{'
+  "$file:${LINE+11}:9", // on '='
+  "$file:${LINE+12}:22", // after '"', i.e. on ';'
+  "$file:${LINE+12}:3", // on 'return'
+  "$file:${LINE+5}:3", // on 'print'
+
+  "$file:${LINE+6}:1" // on ending '}'
+];
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  setBreakpointAtLine(LINE),
+  runStepIntoThroughProgramRecordingStops(stops),
+  checkRecordedStops(stops, expected,
+      debugPrint: true, debugPrintFile: file, debugPrintLine: LINE)
+];
+
+main(args) {
+  runIsolateTestsSynchronous(args, tests,
+      testeeConcurrent: code, pause_on_start: true, pause_on_exit: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/step_through_mixin_from_sdk_test.dart b/runtime/observatory_2/tests/service_2/step_through_mixin_from_sdk_test.dart
new file mode 100644
index 0000000..fc075a0
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/step_through_mixin_from_sdk_test.dart
@@ -0,0 +1,65 @@
+// Copyright (c) 2018, 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.
+
+import 'test_helper.dart';
+import 'service_test_common.dart';
+import 'dart:collection';
+
+const int LINE = 13;
+const String file = "step_through_mixin_from_sdk_test.dart";
+
+code() {
+  Foo foo = new Foo();
+  if (foo.contains(43)) {
+    print("Contains 43!");
+  } else {
+    print("Doesn't contain 43!");
+  }
+}
+
+class Foo extends Object with ListMixin<int> {
+  @override
+  int length = 1;
+
+  @override
+  int operator [](int index) {
+    return 42;
+  }
+
+  @override
+  void operator []=(int index, int value) {}
+}
+
+List<String> stops = [];
+List<String> expected = [
+  "$file:${LINE + 0}:17", // on "Foo" (in "new Foo()")
+  "$file:${LINE + 1}:11", // on "="
+  "list.dart:124:25", // on parameter to "contains"
+  "list.dart:125:23", // on "length" in "this.length"
+  "list.dart:126:16", // on "=" in "i = 0"
+  "list.dart:126:23", // on "<" in "i < length"
+  "list.dart:127:15", // on "[" in "this[i]"
+  "$file:${LINE + 13}:23", // on parameter in "operator []"
+  "$file:${LINE + 14}:5", // on "return"
+  "list.dart:127:19", // on "=="
+  "list.dart:128:26", // on "length" in "this.length"
+  "list.dart:128:18", // on "!="
+  "list.dart:126:34", // on "++" in "i++"
+  "list.dart:126:23", // on "<" in "i < length"
+  "list.dart:132:5", // on "return"
+  "$file:${LINE + 4}:5", // on "print"
+  "$file:${LINE + 6}:1" // on ending '}'
+];
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  setBreakpointAtLine(LINE),
+  runStepIntoThroughProgramRecordingStops(stops),
+  checkRecordedStops(stops, expected, removeDuplicates: true)
+];
+
+main(args) {
+  runIsolateTestsSynchronous(args, tests,
+      testeeConcurrent: code, pause_on_start: true, pause_on_exit: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/step_through_property_get_test.dart b/runtime/observatory_2/tests/service_2/step_through_property_get_test.dart
new file mode 100644
index 0000000..e312723
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/step_through_property_get_test.dart
@@ -0,0 +1,78 @@
+// Copyright (c) 2017, 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.
+
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+const int LINE = 33;
+const String file = "step_through_property_get_test.dart";
+
+code() {
+  Bar bar = new Bar();
+  bar.doStuff();
+}
+
+class Foo {
+  final List<String> data1;
+
+  Foo() : data1 = ["a", "b", "c"];
+
+  void doStuff() {
+    print(data1);
+    print(data1[1]);
+  }
+}
+
+class Bar extends Foo {
+  final List<String> data2;
+
+  Bar() : data2 = ["d", "e", "f"];
+
+  void doStuff() {
+    print(data2);
+    print(data2[1]);
+
+    print(data1);
+    print(data1[1]);
+
+    print(super.data1);
+    print(super.data1[1]);
+  }
+}
+
+List<String> stops = [];
+List<String> expected = [
+  "$file:${LINE+0}:11", // on 'data2'
+  "$file:${LINE+0}:5", // on 'print'
+  "$file:${LINE+1}:11", // on 'data2'
+  "$file:${LINE+1}:16", // on '['
+  "$file:${LINE+1}:5", // on 'print'
+
+  "$file:${LINE+3}:11", // on 'data1'
+  "$file:${LINE+3}:5", // on 'print'
+  "$file:${LINE+4}:11", // on 'data1'
+  "$file:${LINE+4}:16", // on '['
+  "$file:${LINE+4}:5", // on 'print'
+
+  "$file:${LINE+6}:17", // on 'data1'
+  "$file:${LINE+6}:5", // on 'print'
+  "$file:${LINE+7}:17", // on 'data1'
+  "$file:${LINE+7}:22", // on '['
+  "$file:${LINE+7}:5", // on 'print'
+
+  "$file:${LINE+8}:3" // on ending '}'
+];
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  setBreakpointAtLine(LINE),
+  runStepIntoThroughProgramRecordingStops(stops),
+  checkRecordedStops(stops, expected,
+      debugPrint: true, debugPrintFile: file, debugPrintLine: LINE)
+];
+
+main(args) {
+  runIsolateTestsSynchronous(args, tests,
+      testeeConcurrent: code, pause_on_start: true, pause_on_exit: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/step_through_property_set_test.dart b/runtime/observatory_2/tests/service_2/step_through_property_set_test.dart
new file mode 100644
index 0000000..fab872b
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/step_through_property_set_test.dart
@@ -0,0 +1,78 @@
+// Copyright (c) 2017, 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.
+
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+const int LINE = 33;
+const String file = "step_through_property_set_test.dart";
+
+code() {
+  Bar bar = new Bar();
+  bar.doStuff();
+}
+
+class Foo {
+  final List<String> data1;
+
+  Foo() : data1 = ["a", "b", "c"];
+
+  void doStuff() {
+    data1[1] = 'x';
+    print(data1[1]);
+  }
+}
+
+class Bar extends Foo {
+  final List<String> data2;
+
+  Bar() : data2 = ["d", "e", "f"];
+
+  void doStuff() {
+    data2[1] = '1';
+    print(data2[1]);
+
+    data1[1] = '2';
+    print(data1[1]);
+
+    super.data1[1] = '42';
+    print(super.data1[1]);
+  }
+}
+
+List<String> stops = [];
+List<String> expected = [
+  "$file:${LINE+0}:5", // on 'data2'
+  "$file:${LINE+0}:10", // on '['
+  "$file:${LINE+1}:11", // on 'data2'
+  "$file:${LINE+1}:16", // on '['
+  "$file:${LINE+1}:5", // on 'print'
+
+  "$file:${LINE+3}:5", // on 'data1'
+  "$file:${LINE+3}:10", // on '['
+  "$file:${LINE+4}:11", // on 'data1'
+  "$file:${LINE+4}:16", // on '['
+  "$file:${LINE+4}:5", // on 'print'
+
+  "$file:${LINE+6}:11", // on 'data1'
+  "$file:${LINE+6}:16", // on '['
+  "$file:${LINE+7}:17", // on 'data1'
+  "$file:${LINE+7}:22", // on '['
+  "$file:${LINE+7}:5", // on 'print'
+
+  "$file:${LINE+8}:3" // on ending '}'
+];
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  setBreakpointAtLine(LINE),
+  runStepIntoThroughProgramRecordingStops(stops),
+  checkRecordedStops(stops, expected,
+      debugPrint: true, debugPrintFile: file, debugPrintLine: LINE)
+];
+
+main(args) {
+  runIsolateTestsSynchronous(args, tests,
+      testeeConcurrent: code, pause_on_start: true, pause_on_exit: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/step_through_setter_test.dart b/runtime/observatory_2/tests/service_2/step_through_setter_test.dart
new file mode 100644
index 0000000..aa40fb1
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/step_through_setter_test.dart
@@ -0,0 +1,64 @@
+// Copyright (c) 2017, 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.
+
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+const int LINE = 11;
+const String file = "step_through_setter_test.dart";
+
+code() {
+  Bar bar = new Bar();
+  bar.barXYZ = 42;
+  fooXYZ = 42;
+}
+
+// ignore: unused_element
+int _xyz = -1;
+
+set fooXYZ(int i) {
+  _xyz = i - 1;
+}
+
+class Bar {
+  int _xyz = -1;
+
+  set barXYZ(int i) {
+    _xyz = i - 1;
+  }
+
+  get barXYZ => _xyz + 1;
+}
+
+List<String> stops = [];
+List<String> expected = [
+  "$file:${LINE + 0}:5", // after 'code'
+  "$file:${LINE + 1}:17", // on 'Bar'
+
+  "$file:${LINE + 2}:7", // on 'barXYZ'
+  "$file:${LINE + 16}:18", // on 'i'
+  "$file:${LINE + 17}:14", // on '-'
+  "$file:${LINE + 17}:5", // on '_xyz'
+  "$file:${LINE + 18}:3", // on '}'
+
+  "$file:${LINE + 3}:3", // on 'fooXYZ'
+  "$file:${LINE + 9}:16", // on 'i'
+  "$file:${LINE + 10}:12", // on '-'
+  "$file:${LINE + 11}:1", // on '}'
+
+  "$file:${LINE + 4}:1" // on ending '}'
+];
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  setBreakpointAtLine(LINE),
+  runStepIntoThroughProgramRecordingStops(stops),
+  checkRecordedStops(stops, expected,
+      debugPrint: true, debugPrintFile: file, debugPrintLine: LINE)
+];
+
+main(args) {
+  runIsolateTestsSynchronous(args, tests,
+      testeeConcurrent: code, pause_on_start: true, pause_on_exit: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/step_through_switch_test.dart b/runtime/observatory_2/tests/service_2/step_through_switch_test.dart
new file mode 100644
index 0000000..bd65fe7
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/step_through_switch_test.dart
@@ -0,0 +1,82 @@
+// Copyright (c) 2017, 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.
+
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+const int LINE = 11;
+const String file = "step_through_switch_test.dart";
+
+code() {
+  code2('a');
+  code2('b');
+  code2('c');
+  code2('d');
+}
+
+code2(String key) {
+  switch (key) {
+    case "a":
+      print("a!");
+      break;
+    case "b":
+    case "c":
+      print("b or c!");
+      break;
+    default:
+      print("neither a, b or c...");
+  }
+}
+
+List<String> stops = [];
+List<String> expected = [
+  "$file:${LINE+0}:5", // after 'code'
+
+  "$file:${LINE+1}:3", // on 'code2'
+  "$file:${LINE+7}:14", // on 'key'
+  "$file:${LINE+9}:10", // on first '"' on 'case "a"' line
+  "$file:${LINE+10}:7", // on 'print'
+  "$file:${LINE+11}:7", // on 'break'
+  "$file:${LINE+19}:1", // on '}'
+
+  "$file:${LINE+2}:3", // on 'code2'
+  "$file:${LINE+7}:14", // on 'key'
+  "$file:${LINE+9}:10", // on first '"' on 'case "a"' line
+  "$file:${LINE+12}:10", // on first '"' on 'case "b"' line
+  "$file:${LINE+14}:7", // on 'print'
+  "$file:${LINE+15}:7", // on 'break'
+  "$file:${LINE+19}:1", // on '}'
+
+  "$file:${LINE+3}:3", // on 'code2'
+  "$file:${LINE+7}:14", // on 'key'
+  "$file:${LINE+9}:10", // on first '"' on 'case "a"' line
+  "$file:${LINE+12}:10", // on first '"' on 'case "b"' line
+  "$file:${LINE+13}:10", // on first '"' on 'case "c"' line
+  "$file:${LINE+14}:7", // on 'print'
+  "$file:${LINE+15}:7", // on 'break'
+  "$file:${LINE+19}:1", // on '}'
+
+  "$file:${LINE+4}:3", // on 'code2'
+  "$file:${LINE+7}:14", // on 'key'
+  "$file:${LINE+9}:10", // on first '"' on 'case "a"' line
+  "$file:${LINE+12}:10", // on first '"' on 'case "b"' line
+  "$file:${LINE+13}:10", // on first '"' on 'case "c"' line
+  "$file:${LINE+17}:7", // on 'print'
+  "$file:${LINE+19}:1", // on '}'
+
+  "$file:${LINE+5}:1" // on ending '}'
+];
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  setBreakpointAtLine(LINE),
+  runStepIntoThroughProgramRecordingStops(stops),
+  checkRecordedStops(stops, expected,
+      debugPrint: true, debugPrintFile: file, debugPrintLine: LINE)
+];
+
+main(args) {
+  runIsolateTestsSynchronous(args, tests,
+      testeeConcurrent: code, pause_on_start: true, pause_on_exit: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/step_through_switch_with_continue_test.dart b/runtime/observatory_2/tests/service_2/step_through_switch_with_continue_test.dart
new file mode 100644
index 0000000..d9afbb4
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/step_through_switch_with_continue_test.dart
@@ -0,0 +1,58 @@
+// Copyright (c) 2017, 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.
+
+import 'test_helper.dart';
+import 'service_test_common.dart';
+
+const int LINE = 11;
+const String file = "step_through_switch_with_continue_test.dart";
+
+code() {
+  switch (switchOnMe.length) {
+    case 0:
+      print("(got 0!");
+      continue label;
+    label:
+    case 1:
+      print("Got 0 or 1!");
+      break;
+    case 2:
+      print("Got 2!");
+      break;
+    default:
+      print("Got lost!");
+  }
+}
+
+List<String> switchOnMe = [];
+List<String> stops = [];
+List<String> expected = [
+  "$file:${LINE+0}:5", // after 'code'
+
+  "$file:${LINE+1}:11", // on switchOnMe
+  "$file:${LINE+17}:27", // on switchOnMe initializer starting '['
+  "$file:${LINE+1}:22", // on length
+
+  "$file:${LINE+2}:10", // on 0
+  "$file:${LINE+3}:7", // on print
+  "$file:${LINE+4}:7", // on continue
+
+  "$file:${LINE+7}:7", // on print
+  "$file:${LINE+8}:7", // on break
+
+  "$file:${LINE+15}:1" // on ending '}'
+];
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  setBreakpointAtLine(LINE),
+  runStepIntoThroughProgramRecordingStops(stops),
+  checkRecordedStops(stops, expected,
+      debugPrint: true, debugPrintFile: file, debugPrintLine: LINE)
+];
+
+main(args) {
+  runIsolateTestsSynchronous(args, tests,
+      testeeConcurrent: code, pause_on_start: true, pause_on_exit: true);
+}
diff --git a/runtime/observatory_2/tests/service_2/stream_subscription_test.dart b/runtime/observatory_2/tests/service_2/stream_subscription_test.dart
new file mode 100644
index 0000000..145827f
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/stream_subscription_test.dart
@@ -0,0 +1,58 @@
+// Copyright (c) 2020, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+
+import 'test_helper.dart';
+
+Future streamListen(VM vm, String streamId) async =>
+    await vm.invokeRpcNoUpgrade(
+      'streamListen',
+      {
+        'streamId': streamId,
+      },
+    );
+
+Future streamCancel(VM vm, String streamId) async =>
+    await vm.invokeRpcNoUpgrade(
+      'streamCancel',
+      {
+        'streamId': streamId,
+      },
+    );
+
+var tests = <VMTest>[
+  // Check double subscription fails.
+  (VM vm) async {
+    await streamListen(vm, '_Echo');
+    try {
+      await streamListen(vm, '_Echo');
+      fail('Subscribed to stream twice');
+    } on ServerRpcException catch (e) {
+      expect(e.message, 'Stream already subscribed');
+    }
+  },
+  // Check double cancellation fails.
+  (VM vm) async {
+    await streamCancel(vm, '_Echo');
+    try {
+      await streamCancel(vm, '_Echo');
+      fail('Double cancellation of stream successful');
+    } on ServerRpcException catch (e) {
+      expect(e.message, 'Stream not subscribed');
+    }
+  },
+  // Check subscription to invalid stream fails.
+  (VM vm) async {
+    try {
+      await streamListen(vm, 'Foo');
+      fail('Subscribed to invalid stream');
+    } on ServerRpcException catch (e) {
+      expect(e.message, "streamListen: invalid 'streamId' parameter: Foo");
+    }
+  }
+];
+
+main(args) => runVMTests(args, tests);
diff --git a/runtime/observatory_2/tests/service_2/string_escaping_test.dart b/runtime/observatory_2/tests/service_2/string_escaping_test.dart
new file mode 100644
index 0000000..6a9c829
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/string_escaping_test.dart
@@ -0,0 +1,93 @@
+// Copyright (c) 2014, 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.
+
+library string_escaping_test;
+
+import 'dart:async';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+
+var ascii;
+var latin1;
+var unicode;
+var hebrew;
+var singleQuotes;
+var doubleQuotes;
+var newLines;
+var tabs;
+var suggrogatePairs;
+var nullInTheMiddle;
+var escapedUnicodeEscape;
+var longStringEven;
+var longStringOdd;
+var malformedWithLeadSurrogate;
+var malformedWithTrailSurrogate;
+
+void script() {
+  ascii = "Hello, World!";
+  latin1 = "blåbærgrød";
+  unicode = "Îñţérñåţîöñåļîžåţîờñ";
+  hebrew = "שלום רב שובך צפורה נחמדת"; // Right-to-left text.
+  singleQuotes = "'One,' he said.";
+  doubleQuotes = '"Two," he said.';
+  newLines = "Windows\r\nSmalltalk\rUnix\n";
+  tabs = "One\tTwo\tThree";
+  suggrogatePairs = "1𝄞2𝄞𝄞3𝄞𝄞𝄞";
+  nullInTheMiddle = "There are four\u0000 words.";
+  escapedUnicodeEscape = "Should not be A: \\u0041";
+
+  // A surrogate pair will cross the preferred truncation boundry.
+  longStringEven = "..";
+  for (int i = 0; i < 512; i++) longStringEven += "𝄞";
+  longStringOdd = ".";
+  for (int i = 0; i < 512; i++) longStringOdd += "𝄞";
+
+  malformedWithLeadSurrogate = "before" + "𝄞"[0] + "after";
+  malformedWithTrailSurrogate = "before" + "𝄞"[1] + "after";
+}
+
+Future testStrings(Isolate isolate) async {
+  Library lib = isolate.rootLibrary;
+  await lib.load();
+  for (var variable in lib.variables) {
+    await variable.load();
+  }
+
+  expectFullString(String varName, String varValueAsString) {
+    Field field = lib.variables.singleWhere((v) => v.name == varName);
+    Instance value = field.staticValue;
+    expect(value.valueAsString, equals(varValueAsString));
+    expect(value.valueAsStringIsTruncated, isFalse);
+  }
+
+  expectTruncatedString(String varName, String varValueAsString) {
+    Field field = lib.variables.singleWhere((v) => v.name == varName);
+    Instance value = field.staticValue;
+    expect(varValueAsString, startsWith(value.valueAsString));
+    expect(value.valueAsStringIsTruncated, isTrue);
+  }
+
+  script(); // Need to initialize variables in the testing isolate.
+  expectFullString('ascii', ascii);
+  expectFullString('latin1', latin1);
+  expectFullString('unicode', unicode);
+  expectFullString('hebrew', hebrew);
+  expectFullString('singleQuotes', singleQuotes);
+  expectFullString('doubleQuotes', doubleQuotes);
+  expectFullString('newLines', newLines);
+  expectFullString('tabs', tabs);
+  expectFullString('suggrogatePairs', suggrogatePairs);
+  expectFullString('nullInTheMiddle', nullInTheMiddle);
+  expectTruncatedString('longStringEven', longStringEven);
+  expectTruncatedString('longStringOdd', longStringOdd);
+  expectFullString('malformedWithLeadSurrogate', malformedWithLeadSurrogate);
+  expectFullString('malformedWithTrailSurrogate', malformedWithTrailSurrogate);
+}
+
+var tests = <IsolateTest>[
+  testStrings,
+];
+
+main(args) => runIsolateTests(args, tests, testeeBefore: script);
diff --git a/runtime/observatory_2/tests/service_2/test_helper.dart b/runtime/observatory_2/tests/service_2/test_helper.dart
new file mode 100644
index 0000000..18f345e
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/test_helper.dart
@@ -0,0 +1,681 @@
+// 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.
+
+library test_helper;
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:io';
+import 'package:dds/dds.dart';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'service_test_common.dart';
+export 'service_test_common.dart' show DDSTest, IsolateTest, VMTest;
+
+/// Whether to use causal async stacks (if not we use lazy async stacks).
+const bool useCausalAsyncStacks =
+    const bool.fromEnvironment('dart.developer.causal_async_stacks');
+
+/// Determines whether DDS is enabled for this test run.
+const bool useDds = const bool.fromEnvironment('USE_DDS');
+
+/// The extra arguments to use
+const List<String> extraDebuggingArgs = useCausalAsyncStacks
+    ? const ['--causal-async-stacks', '--no-lazy-async-stacks']
+    : const ['--no-causal-async-stacks', '--lazy-async-stacks'];
+
+/// Will be set to the http address of the VM's service protocol before
+/// any tests are invoked.
+String serviceHttpAddress;
+String serviceWebsocketAddress;
+
+const String _TESTEE_ENV_KEY = 'SERVICE_TEST_TESTEE';
+const Map<String, String> _TESTEE_SPAWN_ENV = const {_TESTEE_ENV_KEY: 'true'};
+bool _isTestee() {
+  return Platform.environment.containsKey(_TESTEE_ENV_KEY);
+}
+
+const String _SKY_SHELL_ENV_KEY = 'SERVICE_TEST_SKY_SHELL';
+bool _shouldLaunchSkyShell() {
+  return Platform.environment.containsKey(_SKY_SHELL_ENV_KEY);
+}
+
+String _skyShellPath() {
+  return Platform.environment[_SKY_SHELL_ENV_KEY];
+}
+
+class _ServiceTesteeRunner {
+  Future run(
+      {testeeBefore(): null,
+      testeeConcurrent(): null,
+      bool pause_on_start: false,
+      bool pause_on_exit: false}) async {
+    if (!pause_on_start) {
+      if (testeeBefore != null) {
+        final result = testeeBefore();
+        if (result is Future) {
+          await result;
+        }
+      }
+      print(''); // Print blank line to signal that testeeBefore has run.
+    }
+    if (testeeConcurrent != null) {
+      final result = testeeConcurrent();
+      if (result is Future) {
+        await result;
+      }
+    }
+    if (!pause_on_exit) {
+      // Wait around for the process to be killed.
+      stdin.first.then((_) => exit(0));
+    }
+  }
+
+  void runSync(
+      {void testeeBeforeSync(): null,
+      void testeeConcurrentSync(): null,
+      bool pause_on_start: false,
+      bool pause_on_exit: false}) {
+    if (!pause_on_start) {
+      if (testeeBeforeSync != null) {
+        testeeBeforeSync();
+      }
+      print(''); // Print blank line to signal that testeeBefore has run.
+    }
+    if (testeeConcurrentSync != null) {
+      testeeConcurrentSync();
+    }
+    if (!pause_on_exit) {
+      // Wait around for the process to be killed.
+      stdin.first.then((_) => exit(0));
+    }
+  }
+}
+
+class _ServiceTesteeLauncher {
+  Process process;
+  final List<String> args;
+  Future<void> get exited => _processCompleter.future;
+  final _processCompleter = Completer<void>();
+  bool killedByTester = false;
+
+  _ServiceTesteeLauncher() : args = [Platform.script.toFilePath()] {}
+
+  // Spawn the testee process.
+  Future<Process> _spawnProcess(
+      bool pause_on_start,
+      bool pause_on_exit,
+      bool pause_on_unhandled_exceptions,
+      bool enable_service_port_fallback,
+      bool testeeControlsServer,
+      Uri serviceInfoUri,
+      int port,
+      List<String> extraArgs,
+      List<String> executableArgs) {
+    assert(pause_on_start != null);
+    assert(pause_on_exit != null);
+    assert(pause_on_unhandled_exceptions != null);
+    assert(testeeControlsServer != null);
+
+    if (_shouldLaunchSkyShell()) {
+      return _spawnSkyProcess(
+          pause_on_start,
+          pause_on_exit,
+          pause_on_unhandled_exceptions,
+          testeeControlsServer,
+          extraArgs,
+          executableArgs);
+    } else {
+      return _spawnDartProcess(
+          pause_on_start,
+          pause_on_exit,
+          pause_on_unhandled_exceptions,
+          enable_service_port_fallback,
+          testeeControlsServer,
+          serviceInfoUri,
+          port,
+          extraArgs,
+          executableArgs);
+    }
+  }
+
+  Future<Process> _spawnDartProcess(
+      bool pause_on_start,
+      bool pause_on_exit,
+      bool pause_on_unhandled_exceptions,
+      bool enable_service_port_fallback,
+      bool testeeControlsServer,
+      Uri serviceInfoUri,
+      int port,
+      List<String> extraArgs,
+      List<String> executableArgs) {
+    assert(!_shouldLaunchSkyShell());
+
+    final String dartExecutable = Platform.executable;
+
+    final fullArgs = <String>[
+      '--disable-dart-dev',
+    ];
+    if (pause_on_start) {
+      fullArgs.add('--pause-isolates-on-start');
+    }
+    if (pause_on_exit) {
+      fullArgs.add('--pause-isolates-on-exit');
+    }
+    if (enable_service_port_fallback) {
+      fullArgs.add('--enable_service_port_fallback');
+    }
+    fullArgs.add('--write-service-info=$serviceInfoUri');
+
+    if (pause_on_unhandled_exceptions) {
+      fullArgs.add('--pause-isolates-on-unhandled-exceptions');
+    }
+    fullArgs.add('--profiler');
+    if (extraArgs != null) {
+      fullArgs.addAll(extraArgs);
+    }
+    fullArgs.addAll(executableArgs);
+    if (!testeeControlsServer) {
+      fullArgs.add('--enable-vm-service:$port');
+    }
+    fullArgs.addAll(args);
+
+    return _spawnCommon(dartExecutable, fullArgs, <String, String>{});
+  }
+
+  Future<Process> _spawnSkyProcess(
+      bool pause_on_start,
+      bool pause_on_exit,
+      bool pause_on_unhandled_exceptions,
+      bool testeeControlsServer,
+      List<String> extraArgs,
+      List<String> executableArgs) {
+    assert(_shouldLaunchSkyShell());
+
+    final String dartExecutable = _skyShellPath();
+
+    final dartFlags = <String>[];
+    final fullArgs = <String>[];
+    if (pause_on_start) {
+      dartFlags.add('--pause_isolates_on_start');
+      fullArgs.add('--start-paused');
+    }
+    if (pause_on_exit) {
+      dartFlags.add('--pause_isolates_on_exit');
+    }
+    if (pause_on_unhandled_exceptions) {
+      dartFlags.add('--pause_isolates_on_unhandled_exceptions');
+    }
+    dartFlags.add('--profiler');
+    // Override mirrors.
+    dartFlags.add('--enable_mirrors=true');
+    if (extraArgs != null) {
+      fullArgs.addAll(extraArgs);
+    }
+    fullArgs.addAll(executableArgs);
+    if (!testeeControlsServer) {
+      fullArgs.add('--observatory-port=0');
+    }
+    fullArgs.add('--dart-flags=${dartFlags.join(' ')}');
+    fullArgs.addAll(args);
+
+    return _spawnCommon(dartExecutable, fullArgs, <String, String>{});
+  }
+
+  Future<Process> _spawnCommon(String executable, List<String> arguments,
+      Map<String, String> dartEnvironment) {
+    final environment = _TESTEE_SPAWN_ENV;
+    final bashEnvironment = new StringBuffer();
+    environment.forEach((k, v) => bashEnvironment.write("$k=$v "));
+    if (dartEnvironment != null) {
+      dartEnvironment.forEach((k, v) {
+        arguments.insert(0, '-D$k=$v');
+      });
+    }
+    print('** Launching $bashEnvironment$executable ${arguments.join(' ')}');
+    return Process.start(executable, arguments, environment: environment);
+  }
+
+  Future<Uri> launch(
+      bool pause_on_start,
+      bool pause_on_exit,
+      bool pause_on_unhandled_exceptions,
+      bool enable_service_port_fallback,
+      bool testeeControlsServer,
+      int port,
+      List<String> extraArgs,
+      List<String> executableArgs) async {
+    final completer = new Completer<Uri>();
+    final serviceInfoDir =
+        await Directory.systemTemp.createTemp('dart_service');
+    final serviceInfoUri = serviceInfoDir.uri.resolve('service_info.json');
+    final serviceInfoFile = await File.fromUri(serviceInfoUri).create();
+    _spawnProcess(
+            pause_on_start,
+            pause_on_exit,
+            pause_on_unhandled_exceptions,
+            enable_service_port_fallback,
+            testeeControlsServer,
+            serviceInfoUri,
+            port,
+            extraArgs,
+            executableArgs)
+        .then((p) async {
+      process = p;
+      Uri uri;
+      final blankCompleter = Completer();
+      bool blankLineReceived = false;
+      process.stdout
+          .transform(utf8.decoder)
+          .transform(new LineSplitter())
+          .listen((line) {
+        if (!blankLineReceived && (pause_on_start || line == '')) {
+          // Received blank line.
+          blankLineReceived = true;
+          blankCompleter.complete();
+        }
+        print('>testee>out> $line');
+      });
+      process.stderr
+          .transform(utf8.decoder)
+          .transform(new LineSplitter())
+          .listen((line) {
+        print('>testee>err> ${line.trim()}');
+      });
+      process.exitCode.then((exitCode) async {
+        await serviceInfoDir.delete(recursive: true);
+        if ((exitCode != 0) && !killedByTester) {
+          throw "Testee exited with $exitCode";
+        }
+        print("** Process exited");
+        _processCompleter.complete();
+      });
+
+      // Wait for the blank line which signals that we're ready to run.
+      await blankCompleter.future;
+      while ((await serviceInfoFile.length()) <= 5) {
+        await Future.delayed(Duration(milliseconds: 50));
+      }
+      final content = await serviceInfoFile.readAsString();
+      final infoJson = json.decode(content);
+      String rawUri = infoJson['uri'];
+
+      // If rawUri ends with a /, Uri.parse will include an empty string as the
+      // last path segment. Make sure it's not there to ensure we have a
+      // consistent Uri.
+      if (rawUri.endsWith('/')) {
+        rawUri = rawUri.substring(0, rawUri.length - 1);
+      }
+      uri = Uri.parse(rawUri);
+      completer.complete(uri);
+    });
+    return completer.future;
+  }
+
+  void requestExit() {
+    if (process != null) {
+      print('** Killing script');
+      if (process.kill()) {
+        killedByTester = true;
+      }
+    }
+  }
+}
+
+void setupAddresses(Uri serverAddress) {
+  serviceWebsocketAddress =
+      'ws://${serverAddress.authority}${serverAddress.path}/ws';
+  serviceHttpAddress = 'http://${serverAddress.authority}${serverAddress.path}';
+}
+
+class _ServiceTesterRunner {
+  void run({
+    List<String> mainArgs,
+    List<String> extraArgs,
+    List<String> executableArgs,
+    List<DDSTest> ddsTests,
+    List<IsolateTest> isolateTests,
+    List<VMTest> vmTests,
+    bool pause_on_start: false,
+    bool pause_on_exit: false,
+    bool verbose_vm: false,
+    bool pause_on_unhandled_exceptions: false,
+    bool enable_service_port_fallback: false,
+    bool testeeControlsServer: false,
+    bool enableDds: true,
+    bool enableService: true,
+    int port = 0,
+  }) {
+    if (executableArgs == null) {
+      executableArgs = Platform.executableArguments;
+    }
+    DartDevelopmentService dds;
+    WebSocketVM vm;
+    _ServiceTesteeLauncher process;
+    bool testsDone = false;
+
+    ignoreLateException(Function f) async {
+      try {
+        await f();
+      } catch (error, stackTrace) {
+        if (testsDone) {
+          print('Ignoring late exception during process exit:\n'
+              '$error\n$stackTrace');
+        } else {
+          rethrow;
+        }
+      }
+    }
+
+    setUp(
+      () => ignoreLateException(
+        () async {
+          process = _ServiceTesteeLauncher();
+          await process
+              .launch(
+                  pause_on_start,
+                  pause_on_exit,
+                  pause_on_unhandled_exceptions,
+                  enable_service_port_fallback,
+                  testeeControlsServer,
+                  port,
+                  extraArgs,
+                  executableArgs)
+              .then((Uri serverAddress) async {
+            if (mainArgs.contains("--gdb")) {
+              final pid = process.process.pid;
+              final wait = new Duration(seconds: 10);
+              print("Testee has pid $pid, waiting $wait before continuing");
+              sleep(wait);
+            }
+            if (useDds) {
+              dds = await DartDevelopmentService.startDartDevelopmentService(
+                  serverAddress);
+              setupAddresses(dds.uri);
+            } else {
+              setupAddresses(serverAddress);
+            }
+            print('** Signaled to run test queries on $serviceHttpAddress'
+                ' (${useDds ? "DDS" : "VM Service"})');
+            vm =
+                new WebSocketVM(new WebSocketVMTarget(serviceWebsocketAddress));
+            print('Loading VM...');
+            await vm.load();
+            print('Done loading VM');
+          });
+        },
+      ),
+    );
+
+    tearDown(
+      () => ignoreLateException(
+        () async {
+          if (useDds) {
+            await dds?.shutdown();
+          }
+          process.requestExit();
+        },
+      ),
+    );
+
+    final name = Platform.script.pathSegments.last;
+    runTest(String name) {
+      test(
+        '$name (${useDds ? 'DDS' : 'VM Service'})',
+        () => ignoreLateException(
+          () async {
+            // Run vm tests.
+            if (vmTests != null) {
+              int testIndex = 1;
+              final totalTests = vmTests.length;
+              for (var test in vmTests) {
+                vm.verbose = verbose_vm;
+                print('Running $name [$testIndex/$totalTests]');
+                testIndex++;
+                await test(vm);
+              }
+            }
+
+            // Run dds tests.
+            if (ddsTests != null) {
+              int testIndex = 1;
+              final totalTests = ddsTests.length;
+              for (var test in ddsTests) {
+                vm.verbose = verbose_vm;
+                print('Running $name [$testIndex/$totalTests]');
+                testIndex++;
+                await test(vm, dds);
+              }
+            }
+
+            // Run isolate tests.
+            if (isolateTests != null) {
+              final isolate = await getFirstIsolate(vm);
+              int testIndex = 1;
+              final totalTests = isolateTests.length;
+              for (var test in isolateTests) {
+                vm.verbose = verbose_vm;
+                print('Running $name [$testIndex/$totalTests]');
+                testIndex++;
+                await test(isolate);
+              }
+            }
+
+            print('All service tests completed successfully.');
+            testsDone = true;
+          },
+        ),
+        // Some service tests run fairly long (e.g., valid_source_locations_test).
+        timeout: Timeout.none,
+      );
+    }
+
+    if ((useDds && !enableDds) || (!useDds && ddsTests != null)) {
+      print('Skipping DDS run for $name');
+      return;
+    }
+    if (!useDds && !enableService) {
+      print('Skipping VM Service run for $name');
+      return;
+    }
+    runTest(name);
+  }
+
+  Future<Isolate> getFirstIsolate(WebSocketVM vm) async {
+    if (vm.isolates.isNotEmpty) {
+      final isolate = await vm.isolates.first.load();
+      if (isolate is Isolate) {
+        return isolate;
+      }
+    }
+
+    Completer completer = new Completer();
+    vm.getEventStream(VM.kIsolateStream).then((stream) {
+      var subscription;
+      subscription = stream.listen((ServiceEvent event) async {
+        if (completer == null) {
+          subscription.cancel();
+          return;
+        }
+        if (event.kind == ServiceEvent.kIsolateRunnable) {
+          if (vm.isolates.isNotEmpty) {
+            vm.isolates.first.load().then((result) {
+              if (result is Isolate) {
+                subscription.cancel();
+                completer.complete(result);
+                completer = null;
+              }
+            });
+          }
+        }
+      });
+    });
+
+    // The isolate may have started before we subscribed.
+    if (vm.isolates.isNotEmpty) {
+      vm.isolates.first.reload().then((result) async {
+        if (completer != null && result is Isolate) {
+          completer.complete(result);
+          completer = null;
+        }
+      });
+    }
+    return await completer.future;
+  }
+}
+
+/// Runs [tests] in sequence, each of which should take an [Isolate] and
+/// return a [Future]. Code for setting up state can run before and/or
+/// concurrently with the tests. Uses [mainArgs] to determine whether
+/// to run tests or testee in this invocation of the script.
+Future runIsolateTests(List<String> mainArgs, List<IsolateTest> tests,
+    {testeeBefore(),
+    testeeConcurrent(),
+    bool pause_on_start: false,
+    bool pause_on_exit: false,
+    bool verbose_vm: false,
+    bool pause_on_unhandled_exceptions: false,
+    bool testeeControlsServer: false,
+    bool enableDds: true,
+    bool enableService: true,
+    List<String> extraArgs}) async {
+  assert(!pause_on_start || testeeBefore == null);
+  if (_isTestee()) {
+    new _ServiceTesteeRunner().run(
+        testeeBefore: testeeBefore,
+        testeeConcurrent: testeeConcurrent,
+        pause_on_start: pause_on_start,
+        pause_on_exit: pause_on_exit);
+  } else {
+    new _ServiceTesterRunner().run(
+      mainArgs: mainArgs,
+      extraArgs: extraArgs,
+      isolateTests: tests,
+      pause_on_start: pause_on_start,
+      pause_on_exit: pause_on_exit,
+      verbose_vm: verbose_vm,
+      pause_on_unhandled_exceptions: pause_on_unhandled_exceptions,
+      testeeControlsServer: testeeControlsServer,
+      enableDds: enableDds,
+      enableService: enableService,
+    );
+  }
+}
+
+/// Runs [tests] in sequence, each of which should take an [Isolate] and
+/// return a [Future]. Code for setting up state can run before and/or
+/// concurrently with the tests. Uses [mainArgs] to determine whether
+/// to run tests or testee in this invocation of the script.
+///
+/// This is a special version of this test harness specifically for the
+/// pause_on_unhandled_exceptions_test, which cannot properly function
+/// in an async context (because exceptions are *always* handled in async
+/// functions).
+void runIsolateTestsSynchronous(List<String> mainArgs, List<IsolateTest> tests,
+    {void testeeBefore(),
+    void testeeConcurrent(),
+    bool pause_on_start: false,
+    bool pause_on_exit: false,
+    bool verbose_vm: false,
+    bool pause_on_unhandled_exceptions: false,
+    List<String> extraArgs}) {
+  assert(!pause_on_start || testeeBefore == null);
+  if (_isTestee()) {
+    new _ServiceTesteeRunner().runSync(
+        testeeBeforeSync: testeeBefore,
+        testeeConcurrentSync: testeeConcurrent,
+        pause_on_start: pause_on_start,
+        pause_on_exit: pause_on_exit);
+  } else {
+    new _ServiceTesterRunner().run(
+        mainArgs: mainArgs,
+        extraArgs: extraArgs,
+        isolateTests: tests,
+        pause_on_start: pause_on_start,
+        pause_on_exit: pause_on_exit,
+        verbose_vm: verbose_vm,
+        pause_on_unhandled_exceptions: pause_on_unhandled_exceptions);
+  }
+}
+
+/// Runs [tests] in sequence, each of which should take a [VM] and
+/// return a [Future]. Code for setting up state can run before and/or
+/// concurrently with the tests. Uses [mainArgs] to determine whether
+/// to run tests or testee in this invocation of the script.
+Future runVMTests(List<String> mainArgs, List<VMTest> tests,
+    {testeeBefore(),
+    testeeConcurrent(),
+    bool pause_on_start: false,
+    bool pause_on_exit: false,
+    bool verbose_vm: false,
+    bool pause_on_unhandled_exceptions: false,
+    bool enable_service_port_fallback: false,
+    bool enableDds: true,
+    bool enableService: true,
+    int port = 0,
+    List<String> extraArgs,
+    List<String> executableArgs}) async {
+  if (_isTestee()) {
+    new _ServiceTesteeRunner().run(
+        testeeBefore: testeeBefore,
+        testeeConcurrent: testeeConcurrent,
+        pause_on_start: pause_on_start,
+        pause_on_exit: pause_on_exit);
+  } else {
+    new _ServiceTesterRunner().run(
+      mainArgs: mainArgs,
+      extraArgs: extraArgs,
+      executableArgs: executableArgs,
+      vmTests: tests,
+      pause_on_start: pause_on_start,
+      pause_on_exit: pause_on_exit,
+      verbose_vm: verbose_vm,
+      pause_on_unhandled_exceptions: pause_on_unhandled_exceptions,
+      enable_service_port_fallback: enable_service_port_fallback,
+      enableDds: enableDds,
+      enableService: enableService,
+      port: port,
+    );
+  }
+}
+
+/// Runs [tests] in sequence, each of which should take a [VM] and
+/// [DartDevelopmentService] and return a [Future]. Code for setting up state
+/// can run before and/or concurrently with the tests. Uses [mainArgs] to
+/// determine whether to run tests or testee in this invocation of the
+/// script.
+Future runDDSTests(List<String> mainArgs, List<DDSTest> tests,
+    {testeeBefore(),
+    testeeConcurrent(),
+    bool pause_on_start: false,
+    bool pause_on_exit: false,
+    bool verbose_vm: false,
+    bool pause_on_unhandled_exceptions: false,
+    bool enable_service_port_fallback: false,
+    int port = 0,
+    List<String> extraArgs,
+    List<String> executableArgs}) async {
+  if (_isTestee()) {
+    new _ServiceTesteeRunner().run(
+        testeeBefore: testeeBefore,
+        testeeConcurrent: testeeConcurrent,
+        pause_on_start: pause_on_start,
+        pause_on_exit: pause_on_exit);
+  } else {
+    new _ServiceTesterRunner().run(
+      mainArgs: mainArgs,
+      extraArgs: extraArgs,
+      executableArgs: executableArgs,
+      ddsTests: tests,
+      pause_on_start: pause_on_start,
+      pause_on_exit: pause_on_exit,
+      verbose_vm: verbose_vm,
+      pause_on_unhandled_exceptions: pause_on_unhandled_exceptions,
+      enable_service_port_fallback: enable_service_port_fallback,
+      enableDds: true,
+      enableService: false,
+      port: port,
+    );
+  }
+}
diff --git a/runtime/observatory_2/tests/service_2/timeline_leak_test.dart b/runtime/observatory_2/tests/service_2/timeline_leak_test.dart
new file mode 100644
index 0000000..03f848e
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/timeline_leak_test.dart
@@ -0,0 +1,23 @@
+// Copyright (c) 2017, 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.
+// VMOptions=--timeline-recorder=ring --timeline-streams=Dart
+
+import 'dart:developer';
+
+main() {
+  for (var i = 0; i < 100000; i++) {
+    // OneByteString, ASCII
+    Timeline.startSync('ASCII', arguments: {'arg': 'ASCII'});
+    Timeline.finishSync();
+
+    // OneByteString, Latin1
+    Timeline.startSync('blåbærgrød', arguments: {'arg': 'blåbærgrød'});
+    Timeline.finishSync();
+
+    // TwoByteString
+    Timeline.startSync('Îñţérñåţîöñåļîžåţîờñ',
+        arguments: {'arg': 'Îñţérñåţîöñåļîžåţîờñ'});
+    Timeline.finishSync();
+  }
+}
diff --git a/runtime/observatory_2/tests/service_2/type_arguments_test.dart b/runtime/observatory_2/tests/service_2/type_arguments_test.dart
new file mode 100644
index 0000000..91c97e1
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/type_arguments_test.dart
@@ -0,0 +1,46 @@
+// Copyright (c) 2015, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+
+var tests = <IsolateTest>[
+  (Isolate isolate) =>
+      isolate.getTypeArgumentsList(false).then((dynamic allTypeArgs) {
+        var allTypeArgsTableSize =
+            allTypeArgs['canonicalTypeArgumentsTableSize'];
+        var allTypeArgsTableUsed =
+            allTypeArgs['canonicalTypeArgumentsTableUsed'];
+        var allTypeArgsList = allTypeArgs['typeArguments'];
+        expect(allTypeArgsList, isNotNull);
+        // Check size >= used.
+        expect(
+            allTypeArgsTableSize, greaterThanOrEqualTo(allTypeArgsTableUsed));
+        return isolate
+            .getTypeArgumentsList(true)
+            .then((dynamic instantiatedTypeArgs) {
+          var instantiatedTypeArgsTableSize =
+              instantiatedTypeArgs['canonicalTypeArgumentsTableSize'];
+          var instantiatedTypeArgsTableUsed =
+              instantiatedTypeArgs['canonicalTypeArgumentsTableUsed'];
+          // Check size >= used.
+          expect(instantiatedTypeArgsTableSize,
+              greaterThanOrEqualTo(instantiatedTypeArgsTableUsed));
+          // Check that |instantiated| <= |all|
+          var instantiatedTypeArgsList = instantiatedTypeArgs['typeArguments'];
+          expect(instantiatedTypeArgsList, isNotNull);
+          expect(allTypeArgsList.length,
+              greaterThanOrEqualTo(instantiatedTypeArgsList.length));
+          // Check that we can 'get' this object again.
+          var firstType = allTypeArgsList[0];
+          return isolate.getObject(firstType.id).then((ServiceObject object) {
+            TypeArguments type = object;
+            expect(firstType.name, type.name);
+          });
+        });
+      }),
+];
+
+main(args) => runIsolateTests(args, tests);
diff --git a/runtime/observatory_2/tests/service_2/typed_data_test.dart b/runtime/observatory_2/tests/service_2/typed_data_test.dart
new file mode 100644
index 0000000..3de01a8
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/typed_data_test.dart
@@ -0,0 +1,137 @@
+// Copyright (c) 2015, 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.
+
+library typed_data_test;
+
+import 'dart:typed_data';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+
+var int8List;
+var int16List;
+var int32List;
+var int64List;
+
+var uint8List;
+var uint16List;
+var uint32List;
+var uint64List;
+var uint8ClampedList;
+
+var float32List;
+var float64List;
+
+var int32x4;
+var float32x4;
+var float64x2;
+var int32x4List;
+var float32x4List;
+var float64x2List;
+
+void script() {
+  int8List = new Int8List(2);
+  int8List[0] = -1;
+  int8List[1] = -2;
+  int16List = new Int16List(2);
+  int16List[0] = -3;
+  int16List[1] = -4;
+  int32List = new Int32List(2);
+  int32List[0] = -5;
+  int32List[1] = -6;
+  int64List = new Int64List(2);
+  int64List[0] = -7;
+  int64List[1] = -8;
+
+  uint8List = new Uint8List(2);
+  uint8List[0] = 1;
+  uint8List[1] = 2;
+  uint16List = new Uint16List(2);
+  uint16List[0] = 3;
+  uint16List[1] = 4;
+  uint32List = new Uint32List(2);
+  uint32List[0] = 5;
+  uint32List[1] = 6;
+  uint64List = new Uint64List(2);
+  uint64List[0] = 7;
+  uint64List[1] = 8;
+  uint8ClampedList = new Uint8ClampedList(2);
+  uint8ClampedList[0] = 9;
+  uint8ClampedList[1] = 10;
+
+  float32List = new Float32List(2);
+  float32List[0] = 4.25;
+  float32List[1] = 8.50;
+  float64List = new Float64List(2);
+  float64List[0] = 16.25;
+  float64List[1] = 32.50;
+
+  int32x4 = new Int32x4(1, 2, 3, 4);
+  float32x4 = new Float32x4(1.0, 2.0, 4.0, 8.0);
+  float64x2 = new Float64x2(16.0, 32.0);
+  int32x4List = new Int32x4List(2);
+  float32x4List = new Float32x4List(2);
+  float64x2List = new Float64x2List(2);
+}
+
+var tests = <IsolateTest>[
+  (Isolate isolate) async {
+    script();
+    Library lib = await isolate.rootLibrary.load();
+
+    // Pre-load all the fields so we don't use await below and get better
+    // stacktraces.
+    for (var v in lib.variables) {
+      await v.load();
+      await v.staticValue.load();
+    }
+
+    expectTypedData(name, expectedValue) {
+      var variable = lib.variables.singleWhere((v) => v.name == name);
+      var actualValue = (variable.staticValue as Instance).typedElements;
+      if (expectedValue is Int32x4List) {
+        expect(actualValue.length, equals(expectedValue.length));
+        for (var i = 0; i < actualValue.length; i++) {
+          expect(actualValue[i].x, equals(expectedValue[i].x));
+          expect(actualValue[i].y, equals(expectedValue[i].y));
+          expect(actualValue[i].z, equals(expectedValue[i].z));
+          expect(actualValue[i].w, equals(expectedValue[i].w));
+        }
+      } else if (expectedValue is Float32x4List) {
+        expect(actualValue.length, equals(expectedValue.length));
+        for (var i = 0; i < actualValue.length; i++) {
+          expect(actualValue[i].x, equals(expectedValue[i].x));
+          expect(actualValue[i].y, equals(expectedValue[i].y));
+          expect(actualValue[i].z, equals(expectedValue[i].z));
+          expect(actualValue[i].w, equals(expectedValue[i].w));
+        }
+      } else if (expectedValue is Float64x2List) {
+        expect(actualValue.length, equals(expectedValue.length));
+        for (var i = 0; i < actualValue.length; i++) {
+          expect(actualValue[i].x, equals(expectedValue[i].x));
+          expect(actualValue[i].y, equals(expectedValue[i].y));
+        }
+      } else {
+        expect(actualValue, equals(expectedValue));
+      }
+    }
+
+    expectTypedData("int8List", int8List);
+    expectTypedData("int16List", int16List);
+    expectTypedData("int32List", int32List);
+    expectTypedData("int64List", int64List);
+    expectTypedData("uint8List", uint8List);
+    expectTypedData("uint16List", uint16List);
+    expectTypedData("uint32List", uint32List);
+    expectTypedData("uint64List", uint64List);
+    expectTypedData("uint8ClampedList", uint8ClampedList);
+    expectTypedData("float32List", float32List);
+    expectTypedData("float64List", float64List);
+    expectTypedData("int32x4List", int32x4List);
+    expectTypedData("float32x4List", float32x4List);
+    expectTypedData("float64x2List", float64x2List);
+  },
+];
+
+main(args) => runIsolateTests(args, tests, testeeBefore: script);
diff --git a/runtime/observatory_2/tests/service_2/valid_source_locations_test.dart b/runtime/observatory_2/tests/service_2/valid_source_locations_test.dart
new file mode 100644
index 0000000..79f20cc
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/valid_source_locations_test.dart
@@ -0,0 +1,90 @@
+// Copyright (c) 2018, 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.
+
+import 'dart:async';
+import 'dart:developer';
+
+import 'package:observatory_2/service_io.dart';
+
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+void testFunction() {
+  debugger();
+}
+
+Future validateLocation(Location location, Object object) async {
+  if (location == null) return;
+  if (location.tokenPos < 0) return;
+  if (location.script.uri == 'dart:_internal-patch/class_id_fasta.dart') {
+    // Injected fields from this script cannot be reloaded.
+    return;
+  }
+
+  // Ensure the script is loaded.
+  final Script script = await location.script.load();
+
+  // Use the more low-level functions.
+  final line = script.tokenToLine(location.tokenPos);
+  if (line == null) {
+    throw 'missing location for $object in script ${script.uri}';
+  }
+  script.getLine(line);
+  script.tokenToCol(location.tokenPos);
+
+  // Use the helper functions.
+  await location.getLine();
+  await location.getColumn();
+}
+
+Future validateFieldLocation(Field field) async {
+  field = await field.load();
+  await validateLocation(field.location, field);
+}
+
+Future validateFunctionLocation(ServiceFunction fun) async {
+  fun = await fun.load();
+  await validateLocation(fun.location, fun);
+}
+
+Future validateClassLocation(Class klass) async {
+  klass = await klass.load();
+  await validateLocation(klass.location, klass);
+
+  for (Field field in klass.fields) {
+    await validateFieldLocation(field);
+  }
+  for (ServiceFunction fun in klass.functions) {
+    await validateFunctionLocation(fun);
+  }
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  (Isolate isolate) async {
+    // Force everything to be compiled.
+    final params = {
+      'reports': ['Coverage'],
+      'forceCompile': true
+    };
+    await isolate.invokeRpcNoUpgrade('getSourceReport', params);
+
+    // Loop over all libraries, classes, functions and fields to ensure .
+    for (Library lib in isolate.libraries) {
+      lib = await lib.load();
+
+      for (Field field in lib.variables) {
+        await validateFieldLocation(field);
+      }
+      for (ServiceFunction fun in lib.functions) {
+        await validateFunctionLocation(fun);
+      }
+      for (Class klass in lib.classes) {
+        await validateClassLocation(klass);
+      }
+    }
+  },
+];
+
+main(args) => runIsolateTests(args, tests, testeeConcurrent: testFunction);
diff --git a/runtime/observatory_2/tests/service_2/verify_http_timeline_test.dart b/runtime/observatory_2/tests/service_2/verify_http_timeline_test.dart
new file mode 100644
index 0000000..a4ca036
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/verify_http_timeline_test.dart
@@ -0,0 +1,330 @@
+// Copyright (c) 2019, 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.
+// VMOptions=--timeline_streams=Dart
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:io';
+import 'dart:math';
+import 'dart:typed_data';
+import 'package:expect/expect.dart';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+
+import 'test_helper.dart';
+
+final rng = Random();
+
+// Enable to test redirects.
+const shouldTestRedirects = false;
+
+const maxRequestDelayMs = 3000;
+const maxResponseDelayMs = 500;
+const serverShutdownDelayMs = 2000;
+
+void randomlyAddCookie(HttpResponse response) {
+  if (rng.nextInt(3) == 0) {
+    response.cookies.add(Cookie('Cookie-Monster', 'Me-want-cookie!'));
+  }
+}
+
+Future<bool> randomlyRedirect(HttpServer server, HttpResponse response) async {
+  if (shouldTestRedirects && rng.nextInt(5) == 0) {
+    final redirectUri = Uri(host: 'www.google.com', port: 80);
+    response.redirect(redirectUri);
+    return true;
+  }
+  return false;
+}
+
+// Execute HTTP requests with random delays so requests have some overlap. This
+// way we can be certain that timeline events are matching up properly even when
+// connections are interrupted or can't be established.
+Future<void> executeWithRandomDelay(Function f) =>
+    Future<void>.delayed(Duration(milliseconds: rng.nextInt(maxRequestDelayMs)))
+        .then((_) async {
+      try {
+        await f();
+      } on HttpException catch (_) {} on SocketException catch (_) {} on StateError catch (_) {} on OSError catch (_) {}
+    });
+
+Uri randomlyAddRequestParams(Uri uri) {
+  const possiblePathSegments = <String>['foo', 'bar', 'baz', 'foobar'];
+  final segmentSubset =
+      possiblePathSegments.sublist(0, rng.nextInt(possiblePathSegments.length));
+  uri = uri.replace(pathSegments: segmentSubset);
+  if (rng.nextInt(3) == 0) {
+    uri = uri.replace(queryParameters: {
+      'foo': 'bar',
+      'year': '2019',
+    });
+  }
+  return uri;
+}
+
+Future<HttpServer> startServer() async {
+  final server = await HttpServer.bind(InternetAddress.loopbackIPv4, 0);
+  server.listen((request) async {
+    final response = request.response;
+    response.write(request.method);
+    randomlyAddCookie(response);
+    if (await randomlyRedirect(server, response)) {
+      // Redirect calls close() on the response.
+      return;
+    }
+    // Randomly delay response.
+    await Future.delayed(
+        Duration(milliseconds: rng.nextInt(maxResponseDelayMs)));
+    response.close();
+  });
+  return server;
+}
+
+Future<void> testMain() async {
+  // Ensure there's a chance some requests will be interrupted.
+  Expect.isTrue(maxRequestDelayMs > serverShutdownDelayMs);
+  Expect.isTrue(maxResponseDelayMs < serverShutdownDelayMs);
+
+  final server = await startServer();
+  HttpClient.enableTimelineLogging = true;
+  final client = HttpClient();
+  final requests = <Future>[];
+  final address =
+      Uri(scheme: 'http', host: server.address.host, port: server.port);
+
+  // HTTP DELETE
+  for (int i = 0; i < 10; ++i) {
+    final future = executeWithRandomDelay(() async {
+      final r = await client.deleteUrl(randomlyAddRequestParams(address));
+      final string = 'DELETE $address';
+      r.headers.add(HttpHeaders.contentLengthHeader, string.length);
+      r.write(string);
+      final response = await r.close();
+      response.listen((_) {});
+    });
+    requests.add(future);
+  }
+
+  // HTTP GET
+  for (int i = 0; i < 10; ++i) {
+    final future = executeWithRandomDelay(() async {
+      final r = await client.getUrl(randomlyAddRequestParams(address));
+      final response = await r.close();
+      await response.drain();
+    });
+    requests.add(future);
+  }
+  // HTTP HEAD
+  for (int i = 0; i < 10; ++i) {
+    final future = executeWithRandomDelay(() async {
+      final r = await client.headUrl(randomlyAddRequestParams(address));
+      await r.close();
+    });
+    requests.add(future);
+  }
+
+  // HTTP CONNECT
+  for (int i = 0; i < 10; ++i) {
+    final future = executeWithRandomDelay(() async {
+      final r =
+          await client.openUrl('connect', randomlyAddRequestParams(address));
+      await r.close();
+    });
+    requests.add(future);
+  }
+
+  // HTTP PATCH
+  for (int i = 0; i < 10; ++i) {
+    final future = executeWithRandomDelay(() async {
+      final r = await client.patchUrl(randomlyAddRequestParams(address));
+      final response = await r.close();
+      response.listen(null);
+    });
+    requests.add(future);
+  }
+
+  // HTTP POST
+  for (int i = 0; i < 10; ++i) {
+    final future = executeWithRandomDelay(() async {
+      final r = await client.postUrl(randomlyAddRequestParams(address));
+      r.add(Uint8List.fromList([0, 1, 2]));
+      await r.close();
+    });
+    requests.add(future);
+  }
+
+  // HTTP PUT
+  for (int i = 0; i < 10; ++i) {
+    final future = executeWithRandomDelay(() async {
+      final r = await client.putUrl(randomlyAddRequestParams(address));
+      await r.close();
+    });
+    requests.add(future);
+  }
+
+  // Purposefully close server before some connections can be made to ensure
+  // that refused / interrupted connections correctly create finish timeline
+  // events.
+  await Future.delayed(Duration(milliseconds: serverShutdownDelayMs));
+  await server.close();
+
+  // Ensure all requests complete before finishing.
+  await Future.wait(requests);
+}
+
+bool isStartEvent(Map event) => (event['ph'] == 'b');
+bool isFinishEvent(Map event) => (event['ph'] == 'e');
+
+bool hasCompletedEvents(List traceEvents) {
+  final events = <String, int>{};
+  for (final event in traceEvents) {
+    final id = event['id'];
+    events.putIfAbsent(id, () => 0);
+    if (isStartEvent(event)) {
+      events[id]++;
+    } else if (isFinishEvent(event)) {
+      events[id]--;
+    }
+  }
+  bool valid = true;
+  events.forEach((id, count) {
+    if (count != 0) {
+      valid = false;
+    }
+  });
+  return valid;
+}
+
+List filterEventsByName(List traceEvents, String name) =>
+    traceEvents.where((e) => e['name'].contains(name)).toList();
+
+List filterEventsByIdAndName(List traceEvents, String id, String name) =>
+    traceEvents
+        .where((e) => e['id'] == id && e['name'].contains(name))
+        .toList();
+
+void hasValidHttpConnections(List traceEvents) {
+  final events = filterEventsByName(traceEvents, 'HTTP Connection');
+  expect(hasCompletedEvents(events), isTrue);
+}
+
+void validateHttpStartEvent(Map event, String method) {
+  expect(event.containsKey('args'), isTrue);
+  final args = event['args'];
+  expect(args.containsKey('method'), isTrue);
+  expect(args['method'], method);
+  expect(args['filterKey'], 'HTTP/client');
+  expect(args.containsKey('uri'), isTrue);
+}
+
+void validateHttpFinishEvent(Map event) {
+  expect(event.containsKey('args'), isTrue);
+  final args = event['args'];
+  expect(args['filterKey'], 'HTTP/client');
+  if (!args.containsKey('error')) {
+    expect(args.containsKey('requestHeaders'), isTrue);
+    expect(args['requestHeaders'] != null, isTrue);
+    expect(args.containsKey('compressionState'), isTrue);
+    expect(args.containsKey('connectionInfo'), isTrue);
+    expect(args.containsKey('contentLength'), isTrue);
+    expect(args.containsKey('cookies'), isTrue);
+    expect(args.containsKey('responseHeaders'), isTrue);
+    expect(args.containsKey('isRedirect'), isTrue);
+    expect(args.containsKey('persistentConnection'), isTrue);
+    expect(args.containsKey('reasonPhrase'), isTrue);
+    expect(args.containsKey('redirects'), isTrue);
+    expect(args.containsKey('statusCode'), isTrue);
+    // If proxyInfo is non-null, uri and port _must_ be non-null.
+    if (args.containsKey('proxyInfo')) {
+      final proxyInfo = args['proxyInfo'];
+      expect(proxyInfo.containsKey('uri'), isTrue);
+      expect(proxyInfo.containsKey('port'), isTrue);
+    }
+  }
+}
+
+void hasValidHttpRequests(List traceEvents, String method) {
+  var events = filterEventsByName(traceEvents, 'HTTP CLIENT $method');
+  for (final event in events) {
+    if (isStartEvent(event)) {
+      validateHttpStartEvent(event, method);
+      // Check body of request has been sent and recorded correctly.
+      if (method == 'DELETE' || method == 'POST') {
+        final id = event['id'];
+        final bodyEvent =
+            filterEventsByIdAndName(traceEvents, id, 'Request body');
+        // Due to randomness, it doesn't guarantee to have the timeline events.
+        if (bodyEvent.length == 1) {
+          if (method == 'POST') {
+            // add() was used
+            Expect.listEquals(
+                <int>[0, 1, 2], bodyEvent[0]['args']['encodedData']);
+          } else {
+            // write() was used.
+            Expect.isTrue(
+                bodyEvent[0]['args']['data'].startsWith('$method http'));
+          }
+        }
+      }
+    } else if (isFinishEvent(event)) {
+      validateHttpFinishEvent(event);
+    } else {
+      fail('unexpected event type: ${event["ph"]}');
+    }
+  }
+
+  // Check response body matches string stored in the map.
+  events = filterEventsByName(traceEvents, 'HTTP CLIENT response of $method');
+  if (method == 'DELETE') {
+    // It called listen().
+    expect(hasCompletedEvents(events), isTrue);
+  }
+  for (final event in events) {
+    // Each response will be associated with a request.
+    if (isFinishEvent(event)) {
+      continue;
+    }
+    final id = event['id'];
+    final data = filterEventsByIdAndName(traceEvents, id, 'Response body');
+    if (data.length != 0) {
+      Expect.equals(1, data.length);
+      Expect.listEquals(utf8.encode(method), data[0]['args']['data']);
+    }
+  }
+}
+
+void hasValidHttpCONNECTs(List traceEvents) =>
+    hasValidHttpRequests(traceEvents, 'CONNECT');
+void hasValidHttpDELETEs(List traceEvents) =>
+    hasValidHttpRequests(traceEvents, 'DELETE');
+void hasValidHttpGETs(List traceEvents) =>
+    hasValidHttpRequests(traceEvents, 'GET');
+void hasValidHttpHEADs(List traceEvents) =>
+    hasValidHttpRequests(traceEvents, 'HEAD');
+void hasValidHttpPATCHs(List traceEvents) =>
+    hasValidHttpRequests(traceEvents, 'PATCH');
+void hasValidHttpPOSTs(List traceEvents) =>
+    hasValidHttpRequests(traceEvents, 'POST');
+void hasValidHttpPUTs(List traceEvents) =>
+    hasValidHttpRequests(traceEvents, 'PUT');
+
+var tests = <IsolateTest>[
+  (Isolate isolate) async {
+    final result = await isolate.vm.invokeRpcNoUpgrade('getVMTimeline', {});
+    expect(result['type'], 'Timeline');
+    expect(result.containsKey('traceEvents'), isTrue);
+    final traceEvents = result['traceEvents'];
+    expect(traceEvents.length > 0, isTrue);
+    hasValidHttpConnections(traceEvents);
+    hasValidHttpCONNECTs(traceEvents);
+    hasValidHttpDELETEs(traceEvents);
+    hasValidHttpGETs(traceEvents);
+    hasValidHttpHEADs(traceEvents);
+    hasValidHttpPATCHs(traceEvents);
+    hasValidHttpPOSTs(traceEvents);
+    hasValidHttpPUTs(traceEvents);
+  },
+];
+
+main(args) async => runIsolateTests(args, tests, testeeBefore: testMain);
diff --git a/runtime/observatory_2/tests/service_2/vm_service_dds_test.dart b/runtime/observatory_2/tests/service_2/vm_service_dds_test.dart
new file mode 100644
index 0000000..d309fd8
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/vm_service_dds_test.dart
@@ -0,0 +1,35 @@
+// Copyright (c) 2020, 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.
+
+import 'package:dds/dds.dart';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+
+import 'test_helper.dart';
+
+final tests = <DDSTest>[
+  (VM vm, DartDevelopmentService dds) async {
+    final client = WebSocketVM(
+      WebSocketVMTarget(
+        dds.wsUri.toString(),
+      ),
+    );
+    final result = await client.invokeRpcNoUpgrade('getSupportedProtocols', {});
+    final protocols = result['protocols'];
+    expect(protocols.length, 2);
+    bool supportsVmProtocol = false;
+    bool supportsDdsProtocol = false;
+    for (final protocol in protocols) {
+      if (protocol['protocolName'] == 'VM Service') {
+        supportsVmProtocol = true;
+      } else if (protocol['protocolName'] == 'DDS') {
+        supportsDdsProtocol = true;
+      }
+    }
+    expect(supportsVmProtocol, true);
+    expect(supportsDdsProtocol, true);
+  }
+];
+
+main(args) async => runDDSTests(args, tests);
diff --git a/runtime/observatory_2/tests/service_2/vm_test.dart b/runtime/observatory_2/tests/service_2/vm_test.dart
new file mode 100644
index 0000000..ef69f35
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/vm_test.dart
@@ -0,0 +1,25 @@
+// Copyright (c) 2015, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+
+var tests = <IsolateTest>[
+  (Isolate isolate) {
+    VM vm = isolate.owner;
+    expect(vm.targetCPU, isNotNull);
+    expect(vm.architectureBits == 32 || vm.architectureBits == 64, isTrue);
+    expect(vm.embedder, equals("Dart VM"));
+    expect(vm.currentMemory, isNotNull);
+    expect(vm.currentMemory, greaterThan(0));
+    expect(vm.currentRSS, isNotNull);
+    expect(vm.currentRSS, greaterThan(0));
+    expect(vm.maxRSS, isNotNull);
+    expect(vm.maxRSS, greaterThan(0));
+    expect(vm.maxRSS, greaterThanOrEqualTo(vm.currentRSS));
+  },
+];
+
+main(args) => runIsolateTests(args, tests);
diff --git a/runtime/observatory_2/tests/service_2/vm_timeline_events_test.dart b/runtime/observatory_2/tests/service_2/vm_timeline_events_test.dart
new file mode 100644
index 0000000..bd6a9ed
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/vm_timeline_events_test.dart
@@ -0,0 +1,81 @@
+// Copyright (c) 2016, 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.
+
+import 'dart:async';
+import 'dart:developer';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+primeDartTimeline() async {
+  while (true) {
+    Timeline.startSync('apple');
+    Timeline.finishSync();
+    // Give the VM a chance to send the timeline events. This test is
+    // significantly slower if we loop without yielding control after each
+    // iteration.
+    await Future.delayed(const Duration(milliseconds: 1));
+  }
+}
+
+bool isDart(event) => event['cat'] == 'Dart';
+
+List filterEvents(List events, filter) {
+  return events.where(filter).toList();
+}
+
+Completer completer;
+int eventCount;
+
+onTimelineEvent(ServiceEvent event) {
+  if (event.kind != ServiceEvent.kTimelineEvents) {
+    return;
+  }
+  eventCount++;
+  expect(filterEvents(event.timelineEvents, isDart).length, greaterThan(0));
+  if (eventCount == 5) {
+    completer.complete(eventCount);
+  }
+}
+
+var tests = <IsolateTest>[
+  (Isolate isolate) async {
+    // Clear global state.
+    eventCount = 0;
+    completer = Completer<void>();
+  },
+  (Isolate isolate) async {
+    // Subscribe to the Timeline stream.
+    await subscribeToStream(isolate.vm, VM.kTimelineStream, onTimelineEvent);
+  },
+  (Isolate isolate) async {
+    // Ensure we don't get any events before enabling Dart.
+    await new Future.delayed(new Duration(seconds: 5));
+    expect(eventCount, 0);
+  },
+  (Isolate isolate) async {
+    // Get the flags.
+    Map flags = await isolate.vm.invokeRpcNoUpgrade('getVMTimelineFlags', {});
+    expect(flags['type'], 'TimelineFlags');
+    // Confirm that 'Dart' is available.
+    expect(flags['availableStreams'].contains('Dart'), isTrue);
+    // Confirm that nothing is being recorded.
+    expect(flags['recordedStreams'].length, equals(0));
+  },
+  (Isolate isolate) async {
+    // Enable the Dart category.
+    await isolate.vm.invokeRpcNoUpgrade('setVMTimelineFlags', {
+      "recordedStreams": ["Dart"]
+    });
+  },
+  (Isolate isolate) async {
+    // Wait to receive events.
+    await completer.future;
+    cancelStreamSubscription(VM.kTimelineStream);
+  },
+];
+
+main(args) async =>
+    runIsolateTests(args, tests, testeeConcurrent: primeDartTimeline);
diff --git a/runtime/observatory_2/tests/service_2/vm_timeline_flags_test.dart b/runtime/observatory_2/tests/service_2/vm_timeline_flags_test.dart
new file mode 100644
index 0000000..71a5534
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/vm_timeline_flags_test.dart
@@ -0,0 +1,131 @@
+// Copyright (c) 2016, 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.
+
+import 'dart:async';
+import 'dart:developer';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+primeDartTimeline() {
+  while (true) {
+    Timeline.startSync('apple');
+    Timeline.finishSync();
+    debugger();
+  }
+}
+
+bool isDart(event) => event['cat'] == 'Dart';
+bool isMetaData(event) => event['ph'] == 'M';
+bool isNotMetaData(event) => !isMetaData(event);
+bool isNotDartAndMetaData(event) => !isDart(event) && !isMetaData(event);
+
+List filterEvents(List events, filter) {
+  return events.where(filter).toList();
+}
+
+int dartEventCount;
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  (Isolate isolate) async {
+    // Get the flags.
+    Map flags = await isolate.vm.invokeRpcNoUpgrade('getVMTimelineFlags', {});
+    expect(flags['type'], 'TimelineFlags');
+    // Confirm that 'Dart' is available.
+    expect(flags['availableStreams'].contains('Dart'), isTrue);
+    // Confirm that nothing is being recorded.
+    expect(flags['recordedStreams'].length, equals(0));
+  },
+  (Isolate isolate) async {
+    // Get the timeline.
+    Map result = await isolate.vm.invokeRpcNoUpgrade('getVMTimeline', {});
+    expect(result['type'], equals('Timeline'));
+    expect(result['traceEvents'], isA<List>());
+    // Confirm that it as no non-meta data events.
+    expect(filterEvents(result['traceEvents'], isNotMetaData).length, 0);
+  },
+  (Isolate isolate) async {
+    final completer = Completer<void>();
+    await subscribeToStream(isolate.vm, 'Timeline', (event) async {
+      expect(event.kind, ServiceEvent.kTimelineStreamSubscriptionsUpdate);
+      expect(event.updatedStreams.length, 1);
+      expect(event.updatedStreams.first, 'Dart');
+      await cancelStreamSubscription('Timeline');
+      completer.complete();
+    });
+    // Enable the Dart category.
+    await isolate.vm.invokeRpcNoUpgrade('setVMTimelineFlags', {
+      "recordedStreams": ["Dart"]
+    });
+    await completer.future;
+  },
+  (Isolate isolate) async {
+    // Get the flags.
+    Map flags = await isolate.vm.invokeRpcNoUpgrade('getVMTimelineFlags', {});
+    expect(flags['type'], 'TimelineFlags');
+    // Confirm that only Dart is being recorded.
+    expect(flags['recordedStreams'].length, equals(1));
+    expect(flags['recordedStreams'].contains('Dart'), isTrue);
+    print(flags);
+  },
+  resumeIsolate,
+  hasStoppedAtBreakpoint,
+  (Isolate isolate) async {
+    // Get the timeline.
+    Map result = await isolate.vm.invokeRpcNoUpgrade('getVMTimeline', {});
+    expect(result['type'], equals('Timeline'));
+    expect(result['traceEvents'], isA<List>());
+    print(result['traceEvents']);
+    // Confirm that Dart events are added.
+    expect(filterEvents(result['traceEvents'], isDart).length, greaterThan(0));
+    // Confirm that zero non-Dart events are added.
+    expect(filterEvents(result['traceEvents'], isNotDartAndMetaData).length,
+        equals(0));
+  },
+  hasStoppedAtBreakpoint,
+  (Isolate isolate) async {
+    final completer = Completer<void>();
+    await subscribeToStream(isolate.vm, 'Timeline', (event) async {
+      expect(event.kind, ServiceEvent.kTimelineStreamSubscriptionsUpdate);
+      expect(event.updatedStreams.length, 0);
+      await cancelStreamSubscription('Timeline');
+      completer.complete();
+    });
+
+    // Disable the Dart category.
+    await isolate.vm
+        .invokeRpcNoUpgrade('setVMTimelineFlags', {"recordedStreams": []});
+    // Grab the timeline and remember the number of Dart events.
+    Map result = await isolate.vm.invokeRpcNoUpgrade('getVMTimeline', {});
+    expect(result['type'], equals('Timeline'));
+    expect(result['traceEvents'], isA<List>());
+    dartEventCount = filterEvents(result['traceEvents'], isDart).length;
+
+    await completer.future;
+  },
+  (Isolate isolate) async {
+    // Get the flags.
+    Map flags = await isolate.vm.invokeRpcNoUpgrade('getVMTimelineFlags', {});
+    expect(flags['type'], 'TimelineFlags');
+    // Confirm that 'Dart' is not being recorded.
+    expect(flags['recordedStreams'].length, equals(0));
+  },
+  resumeIsolate,
+  hasStoppedAtBreakpoint,
+  (Isolate isolate) async {
+    // Grab the timeline and verify that we haven't added any new Dart events.
+    Map result = await isolate.vm.invokeRpcNoUpgrade('getVMTimeline', {});
+    expect(result['type'], equals('Timeline'));
+    expect(result['traceEvents'], isA<List>());
+    expect(filterEvents(result['traceEvents'], isDart).length, dartEventCount);
+    // Confirm that zero non-Dart events are added.
+    expect(filterEvents(result['traceEvents'], isNotDartAndMetaData).length,
+        equals(0));
+  },
+];
+
+main(args) async =>
+    runIsolateTests(args, tests, testeeConcurrent: primeDartTimeline);
diff --git a/runtime/observatory_2/tests/service_2/weak_properties_test.dart b/runtime/observatory_2/tests/service_2/weak_properties_test.dart
new file mode 100644
index 0000000..f12387a
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/weak_properties_test.dart
@@ -0,0 +1,67 @@
+// Copyright (c) 2014, 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.
+
+library vm_references_test;
+
+import 'dart:mirrors';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+
+class Foo {}
+
+class Bar {}
+
+var expando;
+var key;
+var value;
+var weak_property;
+
+void script() {
+  expando = new Expando('some debug name');
+  key = new Foo();
+  value = new Bar();
+  expando[key] = value;
+
+  InstanceMirror expandoMirror = reflect(expando);
+  LibraryMirror libcore = expandoMirror.type.owner;
+
+  var entries = expandoMirror
+      .getField(MirrorSystem.getSymbol('_data', libcore))
+      .reflectee;
+  weak_property = entries.singleWhere((e) => e != null);
+  print(weak_property);
+}
+
+var tests = <IsolateTest>[
+  (Isolate isolate) async {
+    Library lib = await isolate.rootLibrary.load();
+    Field keyField = lib.variables.singleWhere((v) => v.name == 'key');
+    await keyField.load();
+    Instance key = keyField.staticValue;
+    Field valueField = lib.variables.singleWhere((v) => v.name == 'value');
+    await valueField.load();
+    Instance value = valueField.staticValue;
+    Field propField =
+        lib.variables.singleWhere((v) => v.name == 'weak_property');
+    await propField.load();
+    Instance prop = propField.staticValue;
+
+    expect(key.isWeakProperty, isFalse);
+    expect(value.isWeakProperty, isFalse);
+    expect(prop.isWeakProperty, isTrue);
+    expect(prop.key, isNull);
+    expect(prop.value, isNull);
+    Instance loadedProp = await prop.load();
+    // Object ids are not canonicalized, so we rely on the key and value
+    // being the sole instances of their classes to test we got the objects
+    // we expect.
+    expect(loadedProp.key, isNotNull);
+    expect(loadedProp.key.clazz, equals(key.clazz));
+    expect(loadedProp.value, isNotNull);
+    expect(loadedProp.value.clazz, equals(value.clazz));
+  },
+];
+
+main(args) => runIsolateTests(args, tests, testeeBefore: script);
diff --git a/runtime/observatory_2/tests/service_2/yield_positions_with_finally_test.dart b/runtime/observatory_2/tests/service_2/yield_positions_with_finally_test.dart
new file mode 100644
index 0000000..47d4c38
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/yield_positions_with_finally_test.dart
@@ -0,0 +1,212 @@
+// Copyright (c) 2019, 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.
+import 'dart:developer';
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+
+const int LINE = 106;
+const int LINE_A = 23;
+const int LINE_B = 36;
+const int LINE_C = 47;
+const int LINE_D = 61;
+const int LINE_E = 78;
+const int LINE_F = 93;
+
+// break statement
+Stream<int> testBreak() async* {
+  for (int t = 0; t < 10; t++) {
+    try {
+      if (t == 1) break;
+      await throwException(); // LINE_A
+    } catch (e) {} finally {
+      yield t;
+    }
+  }
+}
+
+// return statement
+Stream<int> testReturn() async* {
+  for (int t = 0; t < 10; t++) {
+    try {
+      yield t;
+      if (t == 1) return;
+      await throwException(); // LINE_B
+    } catch (e) {} finally {
+      yield t;
+    }
+  }
+}
+
+// Multiple functions
+Stream<int> testMultipleFunctions() async* {
+  try {
+    yield 0;
+    await throwException(); // LINE_C
+  } catch (e) {} finally {
+    yield 1;
+  }
+}
+
+// continue statement
+Stream<int> testContinueSwitch() async* {
+  int currentState = 0;
+  switch (currentState) {
+    case 0:
+      {
+        try {
+          if (currentState == 1) continue label;
+          await throwException(); // LINE_D
+        } catch (e) {} finally {
+          yield 0;
+        }
+        yield 1;
+        break;
+      }
+    label:
+    case 1:
+      break;
+  }
+}
+
+Stream<int> testNestFinally() async* {
+  int i = 0;
+  try {
+    if (i == 1) return;
+    await throwException(); //LINE_E
+  } catch (e) {} finally {
+    try {
+      yield i;
+    } finally {
+      yield 1;
+    }
+    yield 1;
+  }
+}
+
+Stream<int> testAsyncClosureInFinally() async* {
+  int i = 0;
+  try {
+    if (i == 1) return;
+    await throwException(); //LINE_F
+  } catch (e) {} finally {
+    inner() async {
+      await Future.delayed(Duration(milliseconds: 10));
+    }
+
+    await inner;
+    yield 1;
+  }
+}
+
+Future<void> throwException() async {
+  await Future.delayed(Duration(milliseconds: 10));
+  throw new Exception(""); // LINE
+}
+
+code() async {
+  await for (var x in testBreak()) {}
+  await for (var x in testReturn()) {}
+  await for (var x in testMultipleFunctions()) {}
+  await for (var x in testContinueSwitch()) {}
+  await for (var x in testNestFinally()) {}
+  await for (var x in testAsyncClosureInFinally()) {}
+}
+
+var tests = <IsolateTest>[
+  hasPausedAtStart,
+  setBreakpointAtLine(LINE),
+  resumeIsolate,
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE),
+  (Isolate isolate) async {
+    // test break statement
+    ServiceMap stack = await isolate.getStack();
+    expect(stack['awaiterFrames'], isNotNull);
+    expect(stack['awaiterFrames'].length, greaterThanOrEqualTo(2));
+
+    // Check second top frame contains correct line number
+    Script script = stack['awaiterFrames'][1].location.script;
+    expect(script.tokenToLine(stack['awaiterFrames'][1].location.tokenPos),
+        equals(LINE_A));
+  },
+  resumeIsolate,
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE),
+  (Isolate isolate) async {
+    // test return statement
+    ServiceMap stack = await isolate.getStack();
+    expect(stack['awaiterFrames'], isNotNull);
+    expect(stack['awaiterFrames'].length, greaterThanOrEqualTo(2));
+
+    // Check second top frame contains correct line number
+    Script script = stack['awaiterFrames'][1].location.script;
+    expect(script.tokenToLine(stack['awaiterFrames'][1].location.tokenPos),
+        equals(LINE_B));
+  },
+  resumeIsolate,
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE),
+  (Isolate isolate) async {
+    // test break statement
+    ServiceMap stack = await isolate.getStack();
+    expect(stack['awaiterFrames'], isNotNull);
+    expect(stack['awaiterFrames'].length, greaterThanOrEqualTo(2));
+
+    // Check second top frame contains correct line number
+    Script script = stack['awaiterFrames'][1].location.script;
+    expect(script.tokenToLine(stack['awaiterFrames'][1].location.tokenPos),
+        equals(LINE_C));
+  },
+  resumeIsolate,
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE),
+  (Isolate isolate) async {
+    // test break statement
+    ServiceMap stack = await isolate.getStack();
+    expect(stack['awaiterFrames'], isNotNull);
+    expect(stack['awaiterFrames'].length, greaterThanOrEqualTo(2));
+
+    // Check second top frame contains correct line number
+    Script script = stack['awaiterFrames'][1].location.script;
+    expect(script.tokenToLine(stack['awaiterFrames'][1].location.tokenPos),
+        equals(LINE_D));
+  },
+  resumeIsolate,
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE),
+  (Isolate isolate) async {
+    // test nested finally statement
+    ServiceMap stack = await isolate.getStack();
+    expect(stack['awaiterFrames'], isNotNull);
+    expect(stack['awaiterFrames'].length, greaterThanOrEqualTo(2));
+
+    // Check second top frame contains correct line number
+    Script script = stack['awaiterFrames'][1].location.script;
+    expect(script.tokenToLine(stack['awaiterFrames'][1].location.tokenPos),
+        equals(LINE_E));
+  },
+  resumeIsolate,
+  hasStoppedAtBreakpoint,
+  stoppedAtLine(LINE),
+  (Isolate isolate) async {
+    // test async closure within finally block
+    ServiceMap stack = await isolate.getStack();
+    expect(stack['awaiterFrames'], isNotNull);
+    expect(stack['awaiterFrames'].length, greaterThanOrEqualTo(2));
+
+    // Check second top frame contains correct line number
+    Script script = stack['awaiterFrames'][1].location.script;
+    expect(script.tokenToLine(stack['awaiterFrames'][1].location.tokenPos),
+        equals(LINE_F));
+  },
+  resumeIsolate,
+  hasStoppedAtExit
+];
+
+main(args) {
+  runIsolateTestsSynchronous(args, tests,
+      testeeConcurrent: code, pause_on_start: true, pause_on_exit: true);
+}
diff --git a/runtime/observatory_2/tests/ui/inspector.dart b/runtime/observatory_2/tests/ui/inspector.dart
new file mode 100644
index 0000000..7849381
--- /dev/null
+++ b/runtime/observatory_2/tests/ui/inspector.dart
@@ -0,0 +1,272 @@
+// Copyright (c) 2014, 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.
+
+// See inspector.txt for expected behavior.
+
+library manual_inspector_test;
+
+import 'dart:isolate';
+import 'dart:mirrors';
+import 'dart:developer';
+import 'dart:typed_data';
+
+part 'inspector_part.dart';
+
+var libraryField;
+var node;
+var uninitialized = new Object();
+
+extractPrivateField(obj, name) {
+  return reflect(obj)
+      .getField(MirrorSystem.getSymbol(name, reflect(obj).type.owner))
+      .reflectee;
+}
+
+class A<T> {}
+
+class B<S extends num> {}
+
+class S {}
+
+class M {}
+
+class MA extends S with M {}
+
+class Node {
+  static var classField;
+
+  var nullable;
+  var mixedType;
+  var array;
+  var bigint;
+  var blockClean;
+  var blockCopying;
+  var blockFull;
+  var blockFullWithChain;
+  var boundedType;
+  var capability;
+  var counter;
+  var expando;
+  var float32x4;
+  var float64;
+  var float64x2;
+  var gauge;
+  var growableList;
+  var int32x4;
+  var isolate;
+  var map;
+  var mint;
+  var mirrorClass;
+  var mirrorClosure;
+  var mirrorInstance;
+  var mirrorReference;
+  var portReceive;
+  var portSend;
+  var regex;
+  var smi;
+  var stacktrace;
+  var string;
+  var stringLatin1;
+  var stringSnowflake;
+  var stringUnicode;
+  var stringHebrew;
+  var stringTrebleClef;
+  var theFalse;
+  var theNull;
+  var theTrue;
+  var type;
+  var typeParameter;
+  var typedData;
+  var userTag;
+  var weakProperty;
+
+  genStackTrace() {
+    try {
+      num.parse(',');
+    } catch (e, s) {
+      return s;
+    }
+  }
+
+  genCleanBlock() {
+    block(x) => x;
+    return block;
+  }
+
+  genCopyingBlock() {
+    final x = 'I could be copied down';
+    block() => x;
+    return block;
+  }
+
+  genFullBlock() {
+    var x = 0;
+    block() => x++;
+    return block;
+  }
+
+  genFullBlockWithChain() {
+    var x = 0;
+    outer() {
+      var y = 0;
+      block() => x++ + y++;
+      return block;
+    }
+
+    return outer;
+  }
+
+  f(int x) {
+    ++x;
+    return x;
+  }
+
+  static staticMain() {
+    node.main();
+  }
+
+  main() {
+    print("Started main");
+
+    f(9);
+
+    nullable = 1;
+    nullable = null;
+    nullable = 1;
+    mixedType = 1;
+    mixedType = "2";
+    mixedType = false;
+
+    array = new List(3);
+    array[0] = 1;
+    array[1] = 2;
+    array[2] = 3;
+    bigint = 1 << 65;
+    blockClean = genCleanBlock();
+    blockCopying = genCopyingBlock();
+    blockFull = genFullBlock();
+    blockFullWithChain = genFullBlockWithChain();
+    boundedType = extractPrivateField(
+        reflect(new B<int>()).type.typeVariables.single, '_reflectee');
+    counter = new Counter("CounterName", "Counter description");
+    expando = new Expando("expando-name");
+    expando[array] = 'The weakly associated value';
+    float32x4 = new Float32x4(0.0, -1.0, 3.14, 2e28);
+    float64 = 3.14;
+    float64x2 = new Float64x2(0.0, 3.14);
+    gauge = new Gauge("GaugeName", "Gauge description", 0.0, 100.0);
+    growableList = new List();
+    int32x4 = new Int32x4(0, 1, 10, 11);
+    map = {
+      "x-key": "x-value",
+      "y-key": "y-value",
+      "removed-key": "removed-value"
+    };
+    map.remove("removed-key");
+    mint = 1 << 32;
+    mirrorClass = reflectClass(Object);
+    mirrorClosure = reflect(blockFull);
+    mirrorInstance = reflect("a reflectee");
+    mirrorReference = extractPrivateField(mirrorClass, '_reflectee');
+    portReceive = new RawReceivePort();
+    portSend = portReceive.sendPort;
+    regex = new RegExp("a*b+c");
+    smi = 7;
+    stacktrace = genStackTrace();
+    string = "Hello $smi ${smi.runtimeType}";
+    stringLatin1 = "blåbærgrød";
+    stringSnowflake = "❄";
+    stringUnicode = "Îñţérñåţîöñåļîžåţîờñ";
+    stringHebrew = "שלום רב שובך צפורה נחמדת"; // An example of Right-to-Left.
+    stringTrebleClef = "𝄞"; // An example of a surrogate pair.
+    theFalse = false;
+    theNull = null;
+    theTrue = true;
+    type = String;
+    typeParameter =
+        extractPrivateField(reflectClass(A).typeVariables.single, '_reflectee');
+    typedData = extractPrivateField(new ByteData(64), '_typedData');
+    userTag = new UserTag("Example tag name");
+    weakProperty =
+        extractPrivateField(expando, '_data').firstWhere((e) => e != null);
+
+    Isolate.spawn(secondMain, "Hello2").then((otherIsolate) {
+      isolate = otherIsolate;
+      portSend = otherIsolate.controlPort;
+      capability = otherIsolate.terminateCapability;
+    });
+    Isolate.spawn(secondMain, "Hello3").then((otherIsolate) {
+      isolate = otherIsolate;
+      portSend = otherIsolate.controlPort;
+      capability = otherIsolate.terminateCapability;
+    });
+
+    print("Finished main");
+    busy();
+  }
+
+  busy() {
+    var localVar = 0;
+    while (true) {
+      localVar = (localVar + 1) & 0xFFFF;
+    }
+  }
+}
+
+secondMain(msg) {
+  print("Hello from second isolate");
+}
+
+var typed;
+
+class Typed {
+  var float32List = new Float32List(16);
+  var float64List = new Float64List(16);
+
+  var int32x4 = new Int32x4(1, 2, 3, 4);
+  var float32x4 = new Float32x4.zero();
+  var float64x2 = new Float64x2.zero();
+  var int32x4List = new Int32x4List(16);
+  var float32x4List = new Float32x4List(16);
+  var float64x2List = new Float64x2List(16);
+
+  var int8List = new Int8List(8);
+  var int16List = new Int16List(8);
+  var int32List = new Int32List(8);
+  var int64List = new Int64List(8);
+  var uint8List = new Uint8List(8);
+  var uint16List = new Uint16List(8);
+  var uint32List = new Uint32List(8);
+  var uint64List = new Uint64List(8);
+  var uint8ClampedList = new Uint8ClampedList(8);
+
+  var byteBuffer = new Uint8List(8).buffer;
+  var byteBuffer2 = new Float32List(8).buffer;
+
+  var byteData = new ByteData(8);
+
+  Typed() {
+    float32List[0] = 3.14;
+    int8List[0] = 5;
+  }
+
+  Typed._named() {
+    float32List[0] = 3.14;
+    int8List[0] = 5;
+  }
+}
+
+main() {
+  libraryField = 'Library field value';
+  Node.classField = 'Class field value';
+  typed = new Typed();
+  node = new Node();
+  Node.staticMain();
+}
+
+class C {
+  static doPrint() {
+    print("Original");
+  }
+}
diff --git a/runtime/observatory_2/tests/ui/inspector.txt b/runtime/observatory_2/tests/ui/inspector.txt
new file mode 100644
index 0000000..0f99c300
--- /dev/null
+++ b/runtime/observatory_2/tests/ui/inspector.txt
@@ -0,0 +1,5 @@
+0. Run dart --observe inspector.dart
+1. Visit isolate 'root'
+2. Visit library manual_inspector_test
+3. Expand 'variables'
+4. Each should display a reasonable value, not a blank nor an 'Unknown service ref'
diff --git a/runtime/observatory_2/tests/ui/inspector_part.dart b/runtime/observatory_2/tests/ui/inspector_part.dart
new file mode 100644
index 0000000..06589e3
--- /dev/null
+++ b/runtime/observatory_2/tests/ui/inspector_part.dart
@@ -0,0 +1,17 @@
+// Copyright (c) 2015, 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 manual_inspector_test;
+
+functionInPart() {}
+
+set explicitSetter(x) {}
+
+get explicitGetter {}
+
+class D {
+  set explicitSetter(x) {}
+
+  get explicitGetter {}
+}
diff --git a/runtime/observatory_2/tests/ui/log.dart b/runtime/observatory_2/tests/ui/log.dart
new file mode 100644
index 0000000..aaebbe2
--- /dev/null
+++ b/runtime/observatory_2/tests/ui/log.dart
@@ -0,0 +1,27 @@
+// Copyright (c) 2015, 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.
+
+import 'dart:async';
+import 'dart:developer' as developer;
+import 'package:logging/logging.dart';
+
+main() {
+  Logger.root.level = Level.ALL;
+  Logger.root.onRecord.listen((logRecord) {
+    developer.log(logRecord.message,
+        time: logRecord.time,
+        sequenceNumber: logRecord.sequenceNumber,
+        level: logRecord.level.value,
+        name: logRecord.loggerName,
+        zone: null,
+        error: logRecord.error,
+        stackTrace: logRecord.stackTrace);
+  });
+  new Timer.periodic(new Duration(seconds: 1), (t) {
+    Logger.root.info('INFO MESSAGE');
+  });
+  new Timer.periodic(new Duration(seconds: 1), (t) {
+    Logger.root.fine('FINE MESSAGE');
+  });
+}
diff --git a/runtime/observatory_2/tests/ui/log.txt b/runtime/observatory_2/tests/ui/log.txt
new file mode 100644
index 0000000..0f64917
--- /dev/null
+++ b/runtime/observatory_2/tests/ui/log.txt
@@ -0,0 +1,6 @@
+0. Run dart --observe log.dart
+1. Visit main isolate's logging page.
+2. You should see 'INFO MESSAGE' and 'FINE MESSAGE'.
+3. Adjust the level to be 'INFO' and see that 'FINE' messages are hidden.
+4. Adjust the level to be 'FINE' and see that all messages are displayed.
+5. Adjust the level to be 'SHOUT' and see that no messages are displayed.
diff --git a/runtime/observatory_2/tests/ui/retainingPath.dart b/runtime/observatory_2/tests/ui/retainingPath.dart
new file mode 100644
index 0000000..0a2c30f
--- /dev/null
+++ b/runtime/observatory_2/tests/ui/retainingPath.dart
@@ -0,0 +1,17 @@
+// Copyright (c) 2014, 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.
+
+// See reatiningPath.txt for expected behavior.
+
+class Foo {
+  var a;
+  var b;
+  Foo(this.a, this.b);
+}
+
+main() {
+  var list = new List<Foo>.filled(10, null);
+  list[5] = new Foo(42.toString(), new Foo(87.toString(), 17.toString()));
+  while (true) {}
+}
diff --git a/runtime/observatory_2/tests/ui/retainingPath.txt b/runtime/observatory_2/tests/ui/retainingPath.txt
new file mode 100644
index 0000000..e4666a8
--- /dev/null
+++ b/runtime/observatory_2/tests/ui/retainingPath.txt
@@ -0,0 +1,18 @@
+0.
+dart --observe retainingPath.dart
+1.
+isolate 'root'
+2.
+library 'retainingPath.dart'
+3.
+class 'Foo'
+4.
+under 'instances', find 'strongly reachable' list; it should contain 2 elements, one of which should have a field containing "87"
+5.
+instance "87"
+6.
+find 'retaining path'; it should have length 4, and be annotated as follows:
+ "87" in var a
+ Foo in var b
+ Foo at list index 5 of
+ _List(10)
diff --git a/runtime/observatory_2/tool/ensure_dartfmt.sh b/runtime/observatory_2/tool/ensure_dartfmt.sh
new file mode 100755
index 0000000..6aa50e9
--- /dev/null
+++ b/runtime/observatory_2/tool/ensure_dartfmt.sh
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+dart_files=$(find lib web -name "*.dart")
+[ -z "$dart_files" ] && exit 0
+
+unformatted=$(dartfmt -n $dart_files)
+[ -z "$unformatted" ] && exit 0
+
+# Some files are not dartfmt'd. Print message and fail.
+echo >&2 "dart files must be formatted with dartfmt. Please run:"
+for fn in $unformatted; do
+  echo >&2 "  dartfmt -w $PWD/$fn"
+done
+
+exit 1
diff --git a/runtime/observatory_2/update_sources.py b/runtime/observatory_2/update_sources.py
new file mode 100755
index 0000000..39f6ad3
--- /dev/null
+++ b/runtime/observatory_2/update_sources.py
@@ -0,0 +1,49 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2016, 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.
+
+# Updates the list of Observatory source files.
+
+import os
+import sys
+from datetime import date
+
+
+def getDir(rootdir, target):
+    sources = []
+    for root, subdirs, files in os.walk(rootdir):
+        subdirs.sort()
+        files.sort()
+        for f in files:
+            sources.append(root + '/' + f)
+    return sources
+
+
+HEADER = """# Copyright (c) 2017, 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.
+
+# DO NOT EDIT. This file is generated by update_sources.py in this directory.
+
+# This file contains all dart, css, and html sources for Observatory.
+"""
+
+
+def main():
+    with open('observatory_sources.gni', 'w') as target:
+        target.write(HEADER)
+        target.write('observatory_sources = [\n')
+        sources = []
+        for rootdir in ['lib', 'web']:
+            sources.extend(getDir(rootdir, target))
+        sources.sort()
+        for s in sources:
+            if (s[-9:] != 'README.md'):
+                target.write('  "' + s + '",\n')
+        target.write(']\n')
+
+
+if __name__ == "__main__":
+    main()
diff --git a/runtime/observatory_2/web/favicon.ico b/runtime/observatory_2/web/favicon.ico
new file mode 100644
index 0000000..c861395
--- /dev/null
+++ b/runtime/observatory_2/web/favicon.ico
Binary files differ
diff --git a/runtime/observatory_2/web/index.html b/runtime/observatory_2/web/index.html
new file mode 100644
index 0000000..161e866
--- /dev/null
+++ b/runtime/observatory_2/web/index.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html style="height: 100%">
+<head>
+  <meta charset="utf-8">
+  <title>Dart VM Observatory</title>
+  <link rel="stylesheet" href="packages/observatory/src/elements/css/shared.css">
+  <script defer src="main.dart.js"></script>
+</head>
+<body style="height: 100%">
+</body>
+</html>
diff --git a/runtime/observatory_2/web/main.dart b/runtime/observatory_2/web/main.dart
new file mode 100644
index 0000000..72e2190
--- /dev/null
+++ b/runtime/observatory_2/web/main.dart
@@ -0,0 +1,20 @@
+// Copyright (c) 2014, 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.
+
+import 'dart:html';
+import 'package:logging/logging.dart';
+import 'package:observatory_2/elements.dart';
+import 'package:stack_trace/stack_trace.dart';
+
+main() async {
+  Chain.capture(() async {
+    Logger.root.level = Level.INFO;
+    Logger.root.onRecord.listen((LogRecord rec) {
+      print('${rec.level.name}: ${rec.time}: ${rec.message}');
+    });
+    Logger.root.info('Starting Observatory');
+    document.body.children
+        .insert(0, new ObservatoryApplicationElement.created().element);
+  });
+}
diff --git a/runtime/observatory_2/web/third_party/README.md b/runtime/observatory_2/web/third_party/README.md
new file mode 100644
index 0000000..ba94574
--- /dev/null
+++ b/runtime/observatory_2/web/third_party/README.md
@@ -0,0 +1,4 @@
+This directory contains a vulcanized version of trace-viewer:
+
+https://github.com/catapult-project/catapult/wiki/Embedding-Trace-Viewer
+
diff --git a/runtime/observatory_2/web/third_party/trace_viewer_full.html b/runtime/observatory_2/web/third_party/trace_viewer_full.html
new file mode 100644
index 0000000..d815ec5
--- /dev/null
+++ b/runtime/observatory_2/web/third_party/trace_viewer_full.html
@@ -0,0 +1,10442 @@
+<!DOCTYPE html>
+<html>
+  <head i18n-values="dir:textdirection;">
+  <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
+  <meta http-equiv="origin-trial" content="AnYuQDtUf6OrWCmR9Okd67JhWVTbmnRedvPi1TEvAxac8+1p6o9q08FoDO6oCbLD0xEqev+SkZFiIhFSzlY9HgUAAABxeyJvcmlnaW4iOiJodHRwczovL2dvb2dsZXVzZXJjb250ZW50LmNvbTo0NDMiLCJmZWF0dXJlIjoiV2ViQ29tcG9uZW50c1YwIiwiZXhwaXJ5IjoxNjA0NjE0NTM4LCJpc1N1YmRvbWFpbiI6dHJ1ZX0=">
+  <meta http-equiv="origin-trial" content="AkFXw3wHnOs/XXYqFXpc3diDLrRFd9PTgGs/gs43haZmngI/u1g8L4bDnSKLZkB6fecjmjTwcAMQFCpWMAoHSQEAAAB8eyJvcmlnaW4iOiJodHRwczovL2Nocm9taXVtLWJ1aWxkLXN0YXRzLmFwcHNwb3QuY29tOjQ0MyIsImZlYXR1cmUiOiJXZWJDb21wb25lbnRzVjAiLCJleHBpcnkiOjE2MTIyMjM5OTksImlzU3ViZG9tYWluIjp0cnVlfQ==">
+  <meta http-equiv="origin-trial" content="AtQY4wpX9+nj+Vn27cTgygzIPbtB2WoAoMQR5jK9mCm/H2gRIDH6MmGVAaziv9XnYTDKjhBnQYtecbTiIHCQiAIAAACEeyJvcmlnaW4iOiJodHRwczovL2Nocm9taXVtLWJ1aWxkLXN0YXRzLXN0YWdpbmcuYXBwc3BvdC5jb206NDQzIiwiZmVhdHVyZSI6IldlYkNvbXBvbmVudHNWMCIsImV4cGlyeSI6MTYxMjIyMzk5OSwiaXNTdWJkb21haW4iOnRydWV9">
+<template id="overlay-template">
+  <style>
+    overlay-mask {
+      left: 0;
+      padding: 8px;
+      position: absolute;
+      top: 0;
+      z-index: 1000;
+      font-family: sans-serif;
+      -webkit-justify-content: center;
+      background: rgba(0, 0, 0, 0.8);
+      display: flex;
+      height: 100%;
+      left: 0;
+      position: fixed;
+      top: 0;
+      width: 100%;
+    }
+    overlay-mask:focus {
+      outline: none;
+    }
+    overlay-vertical-centering-container {
+      -webkit-justify-content: center;
+      flex-direction: column;
+      display: flex;
+    }
+    overlay-frame {
+      z-index: 1100;
+      background: rgb(255, 255, 255);
+      border: 1px solid #ccc;
+      margin: 75px;
+      display: flex;
+      flex-direction: column;
+      min-height: 0;
+    }
+    title-bar {
+      -webkit-align-items: center;
+      flex-direction: row;
+      border-bottom: 1px solid #ccc;
+      background-color: #ddd;
+      display: flex;
+      padding: 5px;
+      flex: 0 0 auto;
+    }
+    title {
+      display: inline;
+      font-weight: bold;
+      flex: 1 1 auto;
+    }
+    close-button {
+      -webkit-align-self: flex-end;
+      border: 1px solid #eee;
+      background-color: #999;
+      font-size: 10pt;
+      font-weight: bold;
+      padding: 2px;
+      text-align: center;
+      width: 16px;
+    }
+    close-button:hover {
+      background-color: #ddd;
+      border-color: black;
+      cursor: pointer;
+    }
+    overlay-content {
+      display: flex;
+      flex: 1 1 auto;
+      flex-direction: column;
+      overflow-y: auto;
+      padding: 10px;
+      min-width: 300px;
+      min-height: 0;
+    }
+    button-bar {
+      -webkit-align-items: baseline;
+      border-top: 1px solid #ccc;
+      display: flex;
+      flex: 0 0 auto;
+      flex-direction: row-reverse;
+      padding: 4px;
+    }
+  </style>
+
+  <overlay-mask>
+    <overlay-vertical-centering-container>
+      <overlay-frame>
+        <title-bar>
+          <title></title>
+          <close-button>✕</close-button>
+        </title-bar>
+        <overlay-content>
+          <content></content>
+        </overlay-content>
+        <button-bar></button-bar>
+      </overlay-frame>
+    </overlay-vertical-centering-container>
+  </overlay-mask>
+</template><dom-module id="tr-ui-a-analysis-link">
+  <template>
+    <style>
+    :host {
+      display: inline;
+      cursor: pointer;
+      cursor: pointer;
+      white-space: nowrap;
+    }
+    a {
+      text-decoration: underline;
+    }
+    </style>
+    <a href="{{href}}" on-click="onClicked_" on-mouseenter="onMouseEnter_" on-mouseleave="onMouseLeave_"><slot></slot></a>
+
+  </template>
+</dom-module><dom-module id="tr-ui-b-table">
+  <template>
+    <style>
+      :host {
+        display: flex;
+        flex-direction: column;
+      }
+
+      table {
+        flex: 1 1 auto;
+        align-self: stretch;
+        border-collapse: separate;
+        border-spacing: 0;
+        border-width: 0;
+        -webkit-user-select: initial;
+      }
+
+      tr > td {
+        padding: 2px 4px 2px 4px;
+        vertical-align: top;
+      }
+
+      table > tbody:focus {
+        outline: none;
+      }
+      table > tbody:focus[selection-mode="row"] > tr[selected],
+      table > tbody:focus[selection-mode="cell"] > tr > td[selected],
+      table > tbody:focus > tr.empty-row > td {
+        outline: 1px dotted #666666;
+        outline-offset: -1px;
+      }
+
+      button.toggle-button {
+        height: 15px;
+        line-height: 60%;
+        vertical-align: middle;
+        width: 100%;
+      }
+
+      button > * {
+        height: 15px;
+        vertical-align: middle;
+      }
+
+      td.button-column {
+        width: 30px;
+      }
+
+      table > thead > tr > td.sensitive:hover {
+        background-color: #fcfcfc;
+      }
+
+      table > thead > tr > td {
+        font-weight: bold;
+        text-align: left;
+
+        background-color: #eee;
+        white-space: nowrap;
+        overflow: hidden;
+        text-overflow: ellipsis;
+
+        border-top: 1px solid #ffffff;
+        border-bottom: 1px solid #aaa;
+      }
+
+      table > tfoot {
+        background-color: #eee;
+        font-weight: bold;
+      }
+
+      /* Light row and cell highlight. */
+      table > tbody[row-highlight-style="light"] > tr[selected],
+      table > tbody[cell-highlight-style="light"] > tr > td[selected] {
+        background-color: rgb(213, 236, 229);  /* light turquoise */
+      }
+      table > tbody[row-highlight-style="light"] >
+          tr:not(.empty-row):not([selected]):hover,
+      table > tbody[cell-highlight-style="light"] >
+          tr:not(.empty-row):not([selected]) > td:hover {
+        background-color: #f6f6f6;  /* light grey */
+      }
+
+      /* Dark row and cell highlight. */
+      table > tbody[row-highlight-style="dark"] > tr[selected],
+      table > tbody[cell-highlight-style="dark"] > tr > td[selected] {
+        background-color: rgb(103, 199, 165);  /* turquoise */
+      }
+      table > tbody[row-highlight-style="dark"] >
+          tr:not(.empty-row):not([selected]):hover,
+      table > tbody[cell-highlight-style="dark"] >
+          tr:not(.empty-row):not([selected]) > td:hover {
+        background-color: #e6e6e6;  /* grey */
+      }
+      table > tbody[row-highlight-style="dark"] > tr:hover[selected],
+      table > tbody[cell-highlight-style="dark"] > tr[selected] > td:hover {
+        background-color: rgb(171, 217, 202);  /* semi-light turquoise */
+      }
+
+      table > colgroup > col[selected] {
+        background-color: #e6e6e6;  /* grey */
+      }
+
+      table > tbody > tr.empty-row > td {
+        color: #666;
+        font-style: italic;
+        text-align: center;
+      }
+
+      table > tbody.has-footer > tr:last-child > td {
+        border-bottom: 1px solid #aaa;
+      }
+
+      table > tfoot > tr:first-child > td {
+        border-top: 1px solid #ffffff;
+      }
+
+      :host([zebra]) table tbody tr:nth-child(even) {
+        background-color: #f4f4f4;
+      }
+
+      expand-button {
+        -webkit-user-select: none;
+        cursor: pointer;
+        margin-right: 3px;
+        font-size: smaller;
+        height: 1rem;
+      }
+
+      expand-button.button-expanded {
+        transform: rotate(90deg);
+      }
+    </style>
+    <table>
+      <colgroup id="cols">
+      </colgroup>
+      <thead id="head">
+      </thead>
+      <tbody id="body">
+      </tbody>
+      <tfoot id="foot">
+      </tfoot>
+    </table>
+  </template>
+</dom-module><dom-module id="tr-ui-b-table-header-cell">
+  <template>
+  <style>
+    :host {
+      -webkit-user-select: none;
+      display: flex;
+    }
+
+    span {
+      flex: 0 1 auto;
+    }
+
+    #side {
+      -webkit-user-select: none;
+      flex: 0 0 auto;
+      padding-left: 2px;
+      padding-right: 2px;
+      vertical-align: top;
+      font-size: 15px;
+      font-family: sans-serif;
+      line-height: 85%;
+      margin-left: 5px;
+    }
+
+    #side.disabled {
+      color: rgb(140, 140, 140);
+    }
+
+    #title:empty, #side:empty {
+      display: none;
+    }
+  </style>
+
+    <span id="title"></span>
+    <span id="side"></span>
+  </template>
+</dom-module><dom-module id="tr-v-ui-scalar-context-controller">
+  <template></template>
+</dom-module><dom-module id="tr-v-ui-scalar-span">
+  <template>
+    <style>
+    :host {
+      display: flex;
+      flex-direction: row;
+      justify-content: flex-end;
+      position: relative;
+      /* Limit the sparkline's negative z-index to the span only. */
+      isolation: isolate;
+    }
+
+    :host(.left-align) {
+      justify-content: flex-start;
+    }
+
+    :host(.inline) {
+      display: inline-flex;
+    }
+
+    #sparkline {
+      width: 0%;
+      position: absolute;
+      bottom: 0;
+      display: none;
+      height: 100%;
+      background-color: hsla(216, 100%, 94.5%, .75);
+      border-color: hsl(216, 100%, 89%);
+      box-sizing: border-box;
+      z-index: -1;
+    }
+    #sparkline.positive {
+      border-right-style: solid;
+      /* The border width must be kept in sync with buildSparklineStyle_(). */
+      border-right-width: 1px;
+    }
+    #sparkline:not(.positive) {
+      border-left-style: solid;
+      /* The border width must be kept in sync with buildSparklineStyle_(). */
+      border-left-width: 1px;
+    }
+    #sparkline.better {
+      background-color: hsla(115, 100%, 93%, .75);
+      border-color: hsl(118, 60%, 80%);
+    }
+    #sparkline.worse {
+      background-color: hsla(0, 100%, 88%, .75);
+      border-color: hsl(0, 100%, 80%);
+    }
+
+    #content {
+      white-space: nowrap;
+    }
+    #content, #significance, #warning {
+      flex-grow: 0;
+    }
+    #content.better {
+      color: green;
+    }
+    #content.worse {
+      color: red;
+    }
+
+    #significance svg {
+      margin-left: 4px;
+      display: none;
+      height: 1em;
+      vertical-align: text-top;
+      stroke-width: 4;
+      fill: rgba(0, 0, 0, 0);
+    }
+    #significance #insignificant {
+      stroke: black;
+    }
+    #significance #significantly_better {
+      stroke: green;
+    }
+    #significance #significantly_worse {
+      stroke: red;
+    }
+
+    #warning {
+      display: none;
+      margin-left: 4px;
+      height: 1em;
+      vertical-align: text-top;
+      stroke-width: 0;
+    }
+    #warning path {
+      fill: rgb(255, 185, 185);
+    }
+    #warning rect {
+      fill: red;
+    }
+    </style>
+
+    <span id="sparkline"></span>
+
+    <span id="content"></span>
+
+    <span id="significance">
+      
+      <svg id="insignificant" viewBox="0 0 128 128">
+        <circle cx="64" cy="64" r="60"></circle>
+        <circle cx="44" cy="44" r="4"></circle>
+        <circle cx="84" cy="44" r="4"></circle>
+        <line x1="36" x2="92" y1="80" y2="80"></line>
+      </svg>
+
+      
+      <svg id="significantly_better" viewBox="0 0 128 128">
+        <circle cx="64" cy="64" r="60"></circle>
+        <circle cx="44" cy="44" r="4"></circle>
+        <circle cx="84" cy="44" r="4"></circle>
+        <path d="M 28 64 Q 64 128 100 64"></path>
+      </svg>
+
+      
+      <svg id="significantly_worse" viewBox="0 0 128 128">
+        <circle cx="64" cy="64" r="60"></circle>
+        <circle cx="44" cy="44" r="4"></circle>
+        <circle cx="84" cy="44" r="4"></circle>
+        <path d="M 36 96 Q 64 48 92 96"></path>
+      </svg>
+    </span>
+
+    <svg id="warning" viewBox="0 0 128 128">
+      <path d="M 64 0 L 128 128 L 0 128 L 64 0"></path>
+      <rect height="84" width="8" x="60" y="0"></rect>
+      <rect height="24" width="8" x="60" y="100"></rect>
+    </svg>
+  </template>
+</dom-module><dom-module id="tr-ui-a-generic-object-view">
+  <template>
+    <style>
+    :host {
+      display: block;
+      font-family: monospace;
+    }
+    </style>
+    <div id="content">
+    </div>
+  </template>
+</dom-module><dom-module id="tr-ui-a-generic-object-view-with-label">
+  <template>
+    <style>
+    :host {
+      display: block;
+    }
+    </style>
+  </template>
+</dom-module><dom-module id="tr-ui-b-drag-handle">
+  <template>
+    <style>
+    :host {
+      -webkit-user-select: none;
+      box-sizing: border-box;
+      display: block;
+    }
+
+    :host(.horizontal-drag-handle) {
+      background-image: -webkit-gradient(linear,
+                                         0 0, 0 100%,
+                                         from(#E5E5E5),
+                                         to(#D1D1D1));
+      border-bottom: 1px solid #8e8e8e;
+      border-top: 1px solid white;
+      cursor: ns-resize;
+      flex: 0 0 auto;
+      height: 7px;
+      position: relative;
+    }
+
+    :host(.vertical-drag-handle) {
+      background-image: -webkit-gradient(linear,
+                                         0 0, 100% 0,
+                                         from(#E5E5E5),
+                                         to(#D1D1D1));
+      border-left: 1px solid white;
+      border-right: 1px solid #8e8e8e;
+      cursor: ew-resize;
+      flex: 0 0 auto;
+      position: relative;
+      width: 7px;
+    }
+    </style>
+    <div></div>
+  </template>
+</dom-module><dom-module id="tv-ui-b-hotkey-controller">
+  <template>
+    <div></div>
+  </template>
+</dom-module><dom-module id="tr-ui-b-info-bar">
+  <template>
+    <style>
+    :host {
+      align-items: center;
+      flex: 0 0 auto;
+      background-color: rgb(252, 235, 162);
+      border-bottom: 1px solid #A3A3A3;
+      border-left: 1px solid white;
+      border-right: 1px solid #A3A3A3;
+      border-top: 1px solid white;
+      display: flex;
+      min-height: 26px;
+      padding: 0 3px 0 3px;
+    }
+
+    :host([hidden]) {
+      display: none !important;
+    }
+
+    #message { flex: 1 1 auto; }
+    </style>
+
+    <span id="message"></span>
+    <span id="buttons"></span>
+  </template>
+</dom-module><dom-module id="tr-ui-b-mouse-mode-icon">
+  <template>
+    <style>
+    :host {
+      display: block;
+      background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAChCAYAAACbBNzvAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABV0RVh0Q3JlYXRpb24gVGltZQA3LzE2LzEzRNEKUwAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAA9aSURBVHic7V1rTFvl//+UrgUmZWMpbLa6cLErwpYxkqLGkjAG88WSbmumGUllvlmAJctMRtybvlHrLXiJUekMIZuYSCL5gS+EuLIXGEGjqCsllCEW6xQECgzWG7S05/+C/zkp9LTn0gsL6ych9JzznOdzPj19Luf5PN/nCN59913ixRdfRFdXFxLx/2GDgCAIYmpqCoWFhUjE/4cNae+99x4AIFH/Hzak7nDqDu+wOyyw2WzEdl9EMpG23ReQbKQE73Q8coJ3bfcFWK1W/Pbbb/D7/UhLi/37DwaDEIvFKC8vR0lJSdjxbRVstVoxPDyMxx9/HAUFBcjMzIRAIOCdXzAYhNvtht1ux/DwMACEid5WwSMjI3jyySdRXFwMsVgMoVAYk2CCIJCZmYns7GyMjo5iZGQkPoKXl5exd+9e3hdGIhgMIj8/H5mZmRCJRIyCyQ5NJBAEgUAgAKFQiIKCAiwsLISl4VxoHA4H+vv74Xa7uZ4aBqFQiOzsbIhEIojFYojFYohEItq/8fFxXLlyBUtLSxHThOaxZ88eCIXC2AWPj48DAH799deYBaelpUEoFLL6++qrrwAAH3zwAav0YrGYthLkJHh6ehpzc3MAgPn5eUxPT8csWiAQMJbboaEhmM1mAIDFYsHQ0BDvPDkJtlgsYdt+v59LFrxw/fr1sG2Xy8UrL06C6+vrw7bFYjEvYi747rvvwrYlEgmvvDjV0g6HI+p2ohBP3qh32OFwoLe3l1VGvb29sNvtvC8kFCMjI9DpdKzS6nQ6mEwm1nnTPg/7/X6MjY1hcnKS/VX+P/bu3YuysjLk5uYypv36669x8uRJZGRkQCQSwev1oqOjAz09PZx5CwsLcenSJRw+fBh+vx+rq6swmUx46aWXNqWjvcMDAwO8xAIbnZKBgQFeNXhzczMvscBGp6S5uRk//vhj1HS0grVaLYqLi3kRy+Vy1NXVRe0RRcKNGzeg0Wh48apUKnR1daG6ujpqOtpKy+VyQa1Wo6SkBLdv38aFCxeoY5988gn1+fLly9TnL774ApWVlXjiiSfgdDqxtrbG+aJ9Ph/0ej3OnDkDvV6PW7duUceOHDlCfR4dHaU+v/DCC7h27RrUajWcTidWV1ejctAKJggCKysryMzMhE6nw+zsLO3Joft1Oh0ePHiApaUlduqi8BYVFaGvr48Vb19fHyfeqM2Sz+dj3QTEs4lKJC+njsfWJoptkxUrtjZRbJssOnASXFtbG3U7UXjrrbeibnMBJ8FZWVkoKysDABQUFCArK4s3MRcoFArqrlZXV0OhUPDOi5Ngn8+Hw4cPQyqV4tlnn4XP5+NNTIIgmH0An8+HV155BUqlEq+++ior3kAgQLuf84jH2toajh8/jvX1da6n0sLj8SAjI4MxHUEQ+PTTT1nlSRAEHjx4QHtsW8e0RCIR7HY79uzZE/GOcEUgEEAgEMDff/8NkUgUdnxbBR85cgRmsxkCgQD5+fkRh2XYIhAI4P79+5iamoLD4cCxY8fC0myr4KeeegoCgQBWqxVzc3NIS0uLedQyGAxi165dKC8vR1FRUVialHu405ESvNPxyAlOuYfJRMo9fFjdw3iBq3vIBDbu4bYK3uoextKtJEH2yWNyD8nyEG8wuYcffvgha3cxru6h3W5Hf39/QoyzaE6fyWRCQ0MDZ+MsLu7h8vIyent7sby8zIk8VkxNTUGn08Fms8UlP04Nn9/vR39/f9w8JLZwu91obGzk5CFFAq+Wfnh4mDKok4mWlha0trbGlAfvrs3k5CQGBgaSYoiHoqenB1evXk2OIb4VDocDJpMp6eXaYrGgsbGRV7mOufPq8XgwMDCQ9HI9NzeHq1evci7XvDseUqkUWq0W6enpCAaDcDqd8Hq9fLNjDaVSiRs3bkAikfDi5XSHxWIxampqAAALCwsYGhrC7Ows5ufnEypWIpHAYDAAACYmJnD9+nXevJwEnzp1CjKZDBUVFQCAsbGxpJTfjz76CFVVVWhqagIAdHR08G6XWQuuqanB7t274fV6UVpaiuzsbAAbTzyJhMFggEKhgNfrRX19PWQyGQDAaDTyyo+V4JqaGshkMsricLlcOH78OICNCWp8p0cwwWAwoKqqahPvG2+8AWDji+7u7uacJyvBMpksrKxkZWVR0yLGxsY4E7NBVVVVGK9CoaCmRXR0dHDOk5VguorB5/OhoqICYrE4YZ2PSLxXrlyBRCLhNcE1pufh1dVVXLx4EWlpaRGnJzCBjXtId87g4GBU3ri5h1uJ5+fnY8mCtXvIhTflHoYg5R4mEyn3MAl45KyWlOCdjkdOcMo9TCZS7mHKPeSGhLmH5LBOrAGXXN1DcliHrgdFgsk95CzYbrfDbDbD7/ejrKwstpmtNO5hJJhMJrS2tsLtdqOpqQlarTZi2mjuIWvBfr8fZrN50/iz2WzG9PQ0nn/+edonEzZgij10uVwwGo2bxp+NRiOGhobw+uuv005hjtk9JENz6AbbyWCuRESp2Ww2NDc30w62WywW6HQ6zoOIrO5wbm4uzp8/j5WVFXR2dm46VldXh3379mF5eTku86dDUVxcjK6uLthstrClqrq6unDo0CHOvKwE+/1+LC4uUqG0oZiYmIhaicQCkvfu3bthxwYGBnhVmpy6NnSD7kxxQvEA3Zo+fIsQJ8F040j379/nRcwFdF4037FwToLphkUXFxd5EXMB3chkUgQ7nc6wfT6fL+Gm+H///Re2z+Vy8TLFGSut/v5+RsPsm2++AbDR84pXLFNDQwPjelxnz54FsBFK+/nnn7PKl/EOa7VaVmHvYrE4au+HK27evMkq7F0ikeDmzZus82UU7HK5qG8yGs6ePct73gUdfD4f2tvbGdO1t7dzaocZBRMEAaFQSBnhdKipqYFQKORlm0TjzcvLo4xwOhgMBuTl5XHiZVVp+f1+yGQy2iDq4uJiyGSyhFRcfr8fVVVVtEHUGo0GVVVVnHlZ19JerxdqtRpSqZTaJ5VKoVarEzrdwev1Qq/XQ6lUUvuUSiX0ej0vXk7N0srKCjQaDbXmjUajwcrKCmfSULD5Oa6srKCtrQ0SiQQSiQRtbW2MvHFzD0MrsXhUUmzdw9BKjKmSiqt7SBBE3Conru4hOa8kWqBnyj3cgl0EQcQ0cMYWW3kIgkiKe7iVV2C1Won09PSYxLCB1+tFZmYmtb22tobt4E1LBimATaQAkiKWjveR85ZSgnc6Uu5hMpFyD1PuITekYg/ZxB52dXXFTMo2n1D38NSpU7zjDEP/yHzisnJpIsBm5dJ45rntgpONuITTJirctqWlJabjdGAUvNUEp0NouxcvtLa2MgZhmUwmzqKjCrbb7aw9HC5pmWAymVivb2kymTgFe0RslrbeNTa1rtlshkgkQn5+PusL2Iqtd42NdWM0GpGVlYWTJ08ypo14h/nGI8Uax8Q3XJbteREFV1ZW8iLmex6Ja9euJfS8iD9puVyOmpoa3L59G8DmVUq3glzNlAzoimVgvrq6GmlpadDr9QA2r1K6FeRqpmRAFxveiIK9Xi8VZ/jLL78whulUVFTELJbkJeMMjUYjI29TUxNrsQBDX5qMM4w0qE2iuLgYpaWlcXMPyThDphWMNRoN6uvrOfGyskvVanXUNGq1Oq5WKclL/qwjQa/Xc+Zl1dNi8nFi9ZeSyZvqS0erjbmAbT6kT7X1lQp8QeYTyasKE8w3aJJvPh6PBwRBYGZmJi68MzMzqdjDUDx67mEsFxwrUrGHSUCqWdrpSAne6dix7uFzzz1HW0s/FO7h/v37UVBQgMceeyxm99DlcsFut2NwcBACgSDsnTHb7h4ePHgQxcXFcTPTMjIyIJFIcOfOHfz+++8Pl2DSPSTftxQv93DXrl0oKirCnTt3wtIwFhq62aputxtms5maCR8pHROEQiEkEgntew/X1tbC3mu4tLSE9vZ2nD9/njZd6Pn79u3jHoo3OTmJsbExnDlzBsDGWLXdbqcNoent7YVCocChQ4dYh+VFij3s7u5GR0cH9YWaTCbcunVr0yMkmfbChQvQarXQarVUWF4wGER6ejp7wdPT0zCbzfB4PJv2R7NT/H4/rFYrJicnUVZWxnowPtTpGxoagtFoDAsIi2anuN1ufPnll+ju7salS5dw4sQJKk+64hH2FTgcDgwPD4eJZQu/3w+bzcZ5JSSLxYL333+fNvqNDdxuN3p6ehjPDxMsl8tjjkw5ceIENfOVLVQqFd58882YeA0GA7WiWiSECfb5fPjpp58AbKyBx/bCpVIp6urqAADff/895wf6tbU1fPbZZwCAjz/+mPHCSSiVSsr3eueddxh5aWtpMrwuJyeH9cuczp07R5UZvktO/fnnnwCAY8eOoa+vj9U5nZ2d1CsH2fhaUZulwcFB1kGNi4uLjK/gYwuDwcCJ9+2332add9RmyW63w+12Q6FQIC8vD5cvX8bCwgI19VcqlcJms8HhcGBycjJuSz6aTCbMzs5Cq9Xi6NGjGB0dxcTEBJxOJyQSCZRKJUZGRjAyMoL//e9/jBFsoaAVLJfLKZvD4XBQ37ZEItlUph0OB238gVwu5ySQhEqlopo+i8VCtbsymWxTmb579y6t46BSqRg5aAXX1tbi22+/DZvY5XQ6aQMuQyGVSlFbW8trgb6WlhY0NDRgYmJi0/6ZmRnGYVylUomWlhbGeGbaMuzxeKDRaKhVDdkgOzsblZWVOHfuHO82fH19HW1tbWhqamL9ul2ZTIbXXnsNnZ2drN7yFfFFjy6XC6WlpVCpVFhaWsK///5LVfnz8/PIy8sDAOzevRu5ubnIycmBx+OJKZ6YIAj4fD7U19ejsbERf/zxB4aHhykrdHx8HE8//TQAYP/+/VAqlVAoFJx4I1ZapGiyrBw4cAD37t2DXC7HgQMHAGx0QXNycrC+vh63VR5Cecnw3J6eHqhUKpSXlwPY6OI+88wzALiHxnN6PPz555/D9h08eJATIR/Qzd9gE/FKh9SYFlvI5XKqPMUCrlFuKpUKp0+fZkwXDAZp93MSLBaLUVJSgqNHjyIjIwNerzfmOR0ul4sx9lAikeD06dN4+eWXIZVKGXnj5h5evHgRXq8XHo+Hd9MTCpFIhHv37iEnJydqp/+HH36A1+uFy+VirKTi6h7Gug7tVpDuIUEQKCwsjOge/vPPP6zyCwQCWF5exl9//YX5+Xla93DbzTSbzQar1Yr19fW4uoclJSUp9xB4BJullOCdjkdO8P8BGCQ0hnF1DxUAAAAASUVORK5CYII=);
+      width: 27px;
+      height: 30px;
+    }
+    :host.active {
+      cursor: auto;
+    }
+    </style>
+  </template>
+</dom-module><dom-module id="tr-ui-b-mouse-mode-selector">
+  <template>
+    <style>
+    :host {
+
+      -webkit-user-drag: element;
+      -webkit-user-select: none;
+
+      background: #DDD;
+      border: 1px solid #BBB;
+      border-radius: 4px;
+      box-shadow: 0 1px 2px rgba(0,0,0,0.2);
+      left: calc(100% - 120px);
+      position: absolute;
+      top: 100px;
+      user-select: none;
+      width: 29px;
+      z-index: 20;
+    }
+
+    .drag-handle {
+      background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAChCAYAAACbBNzvAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABV0RVh0Q3JlYXRpb24gVGltZQA3LzE2LzEzRNEKUwAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAA9aSURBVHic7V1rTFvl//+UrgUmZWMpbLa6cLErwpYxkqLGkjAG88WSbmumGUllvlmAJctMRtybvlHrLXiJUekMIZuYSCL5gS+EuLIXGEGjqCsllCEW6xQECgzWG7S05/+C/zkp9LTn0gsL6ych9JzznOdzPj19Luf5PN/nCN59913ixRdfRFdXFxLx/2GDgCAIYmpqCoWFhUjE/4cNae+99x4AIFH/Hzak7nDqDu+wOyyw2WzEdl9EMpG23ReQbKQE73Q8coJ3bfcFWK1W/Pbbb/D7/UhLi/37DwaDEIvFKC8vR0lJSdjxbRVstVoxPDyMxx9/HAUFBcjMzIRAIOCdXzAYhNvtht1ux/DwMACEid5WwSMjI3jyySdRXFwMsVgMoVAYk2CCIJCZmYns7GyMjo5iZGQkPoKXl5exd+9e3hdGIhgMIj8/H5mZmRCJRIyCyQ5NJBAEgUAgAKFQiIKCAiwsLISl4VxoHA4H+vv74Xa7uZ4aBqFQiOzsbIhEIojFYojFYohEItq/8fFxXLlyBUtLSxHThOaxZ88eCIXC2AWPj48DAH799deYBaelpUEoFLL6++qrrwAAH3zwAav0YrGYthLkJHh6ehpzc3MAgPn5eUxPT8csWiAQMJbboaEhmM1mAIDFYsHQ0BDvPDkJtlgsYdt+v59LFrxw/fr1sG2Xy8UrL06C6+vrw7bFYjEvYi747rvvwrYlEgmvvDjV0g6HI+p2ohBP3qh32OFwoLe3l1VGvb29sNvtvC8kFCMjI9DpdKzS6nQ6mEwm1nnTPg/7/X6MjY1hcnKS/VX+P/bu3YuysjLk5uYypv36669x8uRJZGRkQCQSwev1oqOjAz09PZx5CwsLcenSJRw+fBh+vx+rq6swmUx46aWXNqWjvcMDAwO8xAIbnZKBgQFeNXhzczMvscBGp6S5uRk//vhj1HS0grVaLYqLi3kRy+Vy1NXVRe0RRcKNGzeg0Wh48apUKnR1daG6ujpqOtpKy+VyQa1Wo6SkBLdv38aFCxeoY5988gn1+fLly9TnL774ApWVlXjiiSfgdDqxtrbG+aJ9Ph/0ej3OnDkDvV6PW7duUceOHDlCfR4dHaU+v/DCC7h27RrUajWcTidWV1ejctAKJggCKysryMzMhE6nw+zsLO3Joft1Oh0ePHiApaUlduqi8BYVFaGvr48Vb19fHyfeqM2Sz+dj3QTEs4lKJC+njsfWJoptkxUrtjZRbJssOnASXFtbG3U7UXjrrbeibnMBJ8FZWVkoKysDABQUFCArK4s3MRcoFArqrlZXV0OhUPDOi5Ngn8+Hw4cPQyqV4tlnn4XP5+NNTIIgmH0An8+HV155BUqlEq+++ior3kAgQLuf84jH2toajh8/jvX1da6n0sLj8SAjI4MxHUEQ+PTTT1nlSRAEHjx4QHtsW8e0RCIR7HY79uzZE/GOcEUgEEAgEMDff/8NkUgUdnxbBR85cgRmsxkCgQD5+fkRh2XYIhAI4P79+5iamoLD4cCxY8fC0myr4KeeegoCgQBWqxVzc3NIS0uLedQyGAxi165dKC8vR1FRUVialHu405ESvNPxyAlOuYfJRMo9fFjdw3iBq3vIBDbu4bYK3uoextKtJEH2yWNyD8nyEG8wuYcffvgha3cxru6h3W5Hf39/QoyzaE6fyWRCQ0MDZ+MsLu7h8vIyent7sby8zIk8VkxNTUGn08Fms8UlP04Nn9/vR39/f9w8JLZwu91obGzk5CFFAq+Wfnh4mDKok4mWlha0trbGlAfvrs3k5CQGBgaSYoiHoqenB1evXk2OIb4VDocDJpMp6eXaYrGgsbGRV7mOufPq8XgwMDCQ9HI9NzeHq1evci7XvDseUqkUWq0W6enpCAaDcDqd8Hq9fLNjDaVSiRs3bkAikfDi5XSHxWIxampqAAALCwsYGhrC7Ows5ufnEypWIpHAYDAAACYmJnD9+nXevJwEnzp1CjKZDBUVFQCAsbGxpJTfjz76CFVVVWhqagIAdHR08G6XWQuuqanB7t274fV6UVpaiuzsbAAbTzyJhMFggEKhgNfrRX19PWQyGQDAaDTyyo+V4JqaGshkMsricLlcOH78OICNCWp8p0cwwWAwoKqqahPvG2+8AWDji+7u7uacJyvBMpksrKxkZWVR0yLGxsY4E7NBVVVVGK9CoaCmRXR0dHDOk5VguorB5/OhoqICYrE4YZ2PSLxXrlyBRCLhNcE1pufh1dVVXLx4EWlpaRGnJzCBjXtId87g4GBU3ri5h1uJ5+fnY8mCtXvIhTflHoYg5R4mEyn3MAl45KyWlOCdjkdOcMo9TCZS7mHKPeSGhLmH5LBOrAGXXN1DcliHrgdFgsk95CzYbrfDbDbD7/ejrKwstpmtNO5hJJhMJrS2tsLtdqOpqQlarTZi2mjuIWvBfr8fZrN50/iz2WzG9PQ0nn/+edonEzZgij10uVwwGo2bxp+NRiOGhobw+uuv005hjtk9JENz6AbbyWCuRESp2Ww2NDc30w62WywW6HQ6zoOIrO5wbm4uzp8/j5WVFXR2dm46VldXh3379mF5eTku86dDUVxcjK6uLthstrClqrq6unDo0CHOvKwE+/1+LC4uUqG0oZiYmIhaicQCkvfu3bthxwYGBnhVmpy6NnSD7kxxQvEA3Zo+fIsQJ8F040j379/nRcwFdF4037FwToLphkUXFxd5EXMB3chkUgQ7nc6wfT6fL+Gm+H///Re2z+Vy8TLFGSut/v5+RsPsm2++AbDR84pXLFNDQwPjelxnz54FsBFK+/nnn7PKl/EOa7VaVmHvYrE4au+HK27evMkq7F0ikeDmzZus82UU7HK5qG8yGs6ePct73gUdfD4f2tvbGdO1t7dzaocZBRMEAaFQSBnhdKipqYFQKORlm0TjzcvLo4xwOhgMBuTl5XHiZVVp+f1+yGQy2iDq4uJiyGSyhFRcfr8fVVVVtEHUGo0GVVVVnHlZ19JerxdqtRpSqZTaJ5VKoVarEzrdwev1Qq/XQ6lUUvuUSiX0ej0vXk7N0srKCjQaDbXmjUajwcrKCmfSULD5Oa6srKCtrQ0SiQQSiQRtbW2MvHFzD0MrsXhUUmzdw9BKjKmSiqt7SBBE3Conru4hOa8kWqBnyj3cgl0EQcQ0cMYWW3kIgkiKe7iVV2C1Won09PSYxLCB1+tFZmYmtb22tobt4E1LBimATaQAkiKWjveR85ZSgnc6Uu5hMpFyD1PuITekYg/ZxB52dXXFTMo2n1D38NSpU7zjDEP/yHzisnJpIsBm5dJ45rntgpONuITTJirctqWlJabjdGAUvNUEp0NouxcvtLa2MgZhmUwmzqKjCrbb7aw9HC5pmWAymVivb2kymTgFe0RslrbeNTa1rtlshkgkQn5+PusL2Iqtd42NdWM0GpGVlYWTJ08ypo14h/nGI8Uax8Q3XJbteREFV1ZW8iLmex6Ja9euJfS8iD9puVyOmpoa3L59G8DmVUq3glzNlAzoimVgvrq6GmlpadDr9QA2r1K6FeRqpmRAFxveiIK9Xi8VZ/jLL78whulUVFTELJbkJeMMjUYjI29TUxNrsQBDX5qMM4w0qE2iuLgYpaWlcXMPyThDphWMNRoN6uvrOfGyskvVanXUNGq1Oq5WKclL/qwjQa/Xc+Zl1dNi8nFi9ZeSyZvqS0erjbmAbT6kT7X1lQp8QeYTyasKE8w3aJJvPh6PBwRBYGZmJi68MzMzqdjDUDx67mEsFxwrUrGHSUCqWdrpSAne6dix7uFzzz1HW0s/FO7h/v37UVBQgMceeyxm99DlcsFut2NwcBACgSDsnTHb7h4ePHgQxcXFcTPTMjIyIJFIcOfOHfz+++8Pl2DSPSTftxQv93DXrl0oKirCnTt3wtIwFhq62aputxtms5maCR8pHROEQiEkEgntew/X1tbC3mu4tLSE9vZ2nD9/njZd6Pn79u3jHoo3OTmJsbExnDlzBsDGWLXdbqcNoent7YVCocChQ4dYh+VFij3s7u5GR0cH9YWaTCbcunVr0yMkmfbChQvQarXQarVUWF4wGER6ejp7wdPT0zCbzfB4PJv2R7NT/H4/rFYrJicnUVZWxnowPtTpGxoagtFoDAsIi2anuN1ufPnll+ju7salS5dw4sQJKk+64hH2FTgcDgwPD4eJZQu/3w+bzcZ5JSSLxYL333+fNvqNDdxuN3p6ehjPDxMsl8tjjkw5ceIENfOVLVQqFd58882YeA0GA7WiWiSECfb5fPjpp58AbKyBx/bCpVIp6urqAADff/895wf6tbU1fPbZZwCAjz/+mPHCSSiVSsr3eueddxh5aWtpMrwuJyeH9cuczp07R5UZvktO/fnnnwCAY8eOoa+vj9U5nZ2d1CsH2fhaUZulwcFB1kGNi4uLjK/gYwuDwcCJ9+2332add9RmyW63w+12Q6FQIC8vD5cvX8bCwgI19VcqlcJms8HhcGBycjJuSz6aTCbMzs5Cq9Xi6NGjGB0dxcTEBJxOJyQSCZRKJUZGRjAyMoL//e9/jBFsoaAVLJfLKZvD4XBQ37ZEItlUph0OB238gVwu5ySQhEqlopo+i8VCtbsymWxTmb579y6t46BSqRg5aAXX1tbi22+/DZvY5XQ6aQMuQyGVSlFbW8trgb6WlhY0NDRgYmJi0/6ZmRnGYVylUomWlhbGeGbaMuzxeKDRaKhVDdkgOzsblZWVOHfuHO82fH19HW1tbWhqamL9ul2ZTIbXXnsNnZ2drN7yFfFFjy6XC6WlpVCpVFhaWsK///5LVfnz8/PIy8sDAOzevRu5ubnIycmBx+OJKZ6YIAj4fD7U19ejsbERf/zxB4aHhykrdHx8HE8//TQAYP/+/VAqlVAoFJx4I1ZapGiyrBw4cAD37t2DXC7HgQMHAGx0QXNycrC+vh63VR5Cecnw3J6eHqhUKpSXlwPY6OI+88wzALiHxnN6PPz555/D9h08eJATIR/Qzd9gE/FKh9SYFlvI5XKqPMUCrlFuKpUKp0+fZkwXDAZp93MSLBaLUVJSgqNHjyIjIwNerzfmOR0ul4sx9lAikeD06dN4+eWXIZVKGXnj5h5evHgRXq8XHo+Hd9MTCpFIhHv37iEnJydqp/+HH36A1+uFy+VirKTi6h7Gug7tVpDuIUEQKCwsjOge/vPPP6zyCwQCWF5exl9//YX5+Xla93DbzTSbzQar1Yr19fW4uoclJSUp9xB4BJullOCdjkdO8P8BGCQ0hnF1DxUAAAAASUVORK5CYII=) 2px 3px no-repeat;
+      background-repeat: no-repeat;
+      border-bottom: 1px solid #BCBCBC;
+      cursor: move;
+      display: block;
+      height: 13px;
+      width: 27px;
+    }
+
+    .tool-button {
+      background-position: center center;
+      background-repeat: no-repeat;
+      border-bottom: 1px solid #BCBCBC;
+      border-top: 1px solid #F1F1F1;
+      cursor: pointer;
+    }
+
+    .buttons > .tool-button:last-child {
+      border-bottom: none;
+    }
+
+    </style>
+    <div class="drag-handle"></div>
+    <div class="buttons">
+    </div>
+  </template>
+</dom-module><dom-module id="tr-ui-e-chrome-cc-display-item-list-item">
+  <template>
+    <style>
+      :host {
+        border-bottom: 1px solid #555;
+        display: block;
+        font-size: 12px;
+        padding: 3px 5px;
+      }
+
+      :host(:hover) {
+        background-color: #f0f0f0;
+        cursor: pointer;
+      }
+
+      .header {
+        font-weight: bold;
+        margin: 2px 0;
+      }
+
+      .header > .extra {
+        background-color: #777;
+        border-radius: 4px;
+        color: white;
+        margin: 0 6px;
+        text-decoration: none;
+        padding: 2px 4px;
+      }
+
+      .raw-details {
+        white-space: pre-wrap;
+      }
+
+      .details > dl {
+        margin: 0;
+      }
+
+      :host(:not([selected])) .details {
+        display: none;
+      }
+    </style>
+    <div class="header">
+      {{name}}
+      <template if="{{_computeIfSKP(richDetails)}}" is="dom-if">
+        <a class="extra" download="drawing.skp" href$="{{_computeHref(richDetails)}}" on-click="{{stopPropagation}}">SKP</a>
+      </template>
+    </div>
+    <div class="details">
+      <template if="{{rawDetails}}" is="dom-if">
+        <div class="raw-details">{{rawDetails}}</div>
+      </template>
+      <template if="{{richDetails}}" is="dom-if">
+        <dl>
+          <template if="{{richDetails.visualRect}}" is="dom-if">
+            <dt>Visual rect</dt>
+            <dd>{{richDetails.visualRect.x}},{{richDetails.visualRect.y}}
+                {{richDetails.visualRect.width}}×{{richDetails.visualRect.height}}
+            </dd>
+          </template>
+        </dl>
+      </template>
+    </div>
+  </template>
+
+</dom-module><template id="tr-ui-e-chrome-cc-display-item-debugger-template">
+  <left-panel>
+    <display-item-info>
+      <header>
+        <span class="title">Display Item List</span>
+        <span class="size"></span>
+        <div class="export">
+          <input class="dlfilename" type="text" value="displayitemlist.json"/>
+          <button class="dlexport">Export display item list</button>
+        </div>
+        <div class="export">
+          <input class="skpfilename" type="text" value="skpicture.skp"/>
+          <button class="skpexport">Export list as SkPicture</button>
+        </div>
+      </header>
+    </display-item-info>
+  </left-panel>
+  <right-panel>
+    <raster-area>
+      <canvas-scroller>
+        <canvas></canvas>
+      </canvas-scroller>
+    </raster-area>
+  </right-panel>
+</template><template id="quad-stack-view-template">
+  <style>
+  #chrome-left {
+    background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMcAAABICAYAAABC4+HLAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAB3RJTUUH3QcNFyMmV/Pm9QAAIABJREFUeNrtvXmwXdd13vlbe9/7BgzEQAIcQAIEQYKjSAokLVlOW5Fk2nLKmqx0J2Wp0k652h13uiy5XYqdwU7sSnckpZ1yV3U75apU4kos27Elu9NlyRXZjiiRomSTIiWZs0hwHsABJIY33rPX6j/W2ueed3DvAyDKKoGFW0UCeO/ec/fZZ+29v7XWt74lAIuLi7tXV1f/raq+zcy2AogIZsbpvrqfMzNE5IS/1/fVn5sZKaUTrtX9/v7nT+fn9e/1e052X/3r1THWa3R/37+miKCq7c+mjW/a+F/P57vj6/45bayn+wzXs4n+794Q9nP8+PHdS0tL31LVmfpGVQU4YSInGUb/YfZvpn+zp/LQu4Y27X31d933nurkq+qaa08yotO55npG0v2O+r1/XZ9fb2FMWoD9Oe5+pju//e+fdP3u83+j2I+89NJLn11dXf1bdSCTJnnSSpz2+/VWZ/8m+w+g/zD616yT2P9733BOZ5f4dhbCevPQHet63zVtV3y9n1/v/k9nZ562SNY7Gd5o9iPPP//8qxVKrQdL+hOy3qqdNEnTjv1JA+vuRpMGvd7kn8oCqded9B2THuJ6u/Kk7+vuiNOgQH8OX+/np813/376O/CkU2EavDwVWPiGsp9nn33WJt3ItF2ne2xOe2jTHuTJMOS0He1UcG33791JmWQYkzB6dyfp7tynsktPG8/Jdv2TGcLpfH7Sc5m0EKZBsPV+tp4PMe39bwj7efrpp229G5u2O3WPplN1cE/XQZsENybtnNN2pv4x3N1Fpu2S/SO6j6fXgz6n4gRPGmMfR7/ez/cXd/1798Tsfr4PMU52Oq4Hp95I9jPor7ZJ+G7STlEnvN7gesfXpB2tH5lZzynrO07Txtb92aQTY9rv+3i1v4jqv5umOSEq0r9O3/iqEUx6MPXnqjpxrk73812oMQmP968zyUj68zPp+U1bxG80+5GnnnrKpkVxTiWUuN4q7+96/YFXp6pvANN8hD7MmRbF6O7200KR9ed9CDbpSF4v6jIJtnQjQdPGOylK9p34/HowaFL0Z73IUNex7Z5Gk3bkN6L9yBNPPGHdY3fayu3uSP0dqH62uyP0w4XrDWo957gPEfqf78e4p4U8+0Y86R6711pvAUyL3vTvd9ou238Q/Xn4dj4/Cd6d7BlMC532534S9OnO8xvVfuTxxx+39RJlk/DtpAGc6k6hquScp+7EkyIn0+LV60Ufpu2q05zN/sOYFIfvP8CT5VEmGWN/h5w0zm/38+sl7/r3drLntt58rzdXbyT7kccee8z6O2b3JnLO6zpjk47nkyVg1pu07muas9b3CaZh4f5uPMn4Sikn7Jj9RTEJMnQfVHdck4x3Wt5i0qL6dj8/6WQ5GcSYBiEn+STrhT/fqPYzmJYxrRcopax5eH18Oi38WI2ulLImYTPNMavv716z/93rRXUmOZXVgZ5kePX7+hPeN5xJTmx3MdXf9zHyM888w8LCwgn30IUQ0xzWSYvhVD4/LarTzpWBpOl+zqRQ9lqjE2DCtbH2x9MW3XA45JxzzmHnzp0njYp9r9jPoH75Gkekc8SZ2ZpjrH/Ez8wMSSmHMY4YjZp2MDnniVGT/sPvRhxmZ2fJOWHmxj0ajU7AtvV6k4727gSklMg5M4jdq6iyuro69bv799fNptYF0X3vJKjz8MMPMz+/gWuvuYatW7eScgIEwTADEwEUAZDkBgtuYONlCCJgAuZ/N5QkCcP8avFzUH8fsZgNEoJJLAakc+2TjENi90RQjGSCJm1/hwlmgmRFFIwEYoiNxyPxvYZ07gVKUzh8+DD333cfRZXLLrvsBLxfjbl76pyO/ZRS1thq325O137k4YcftvUSOf1Ufdco/uwLX+LOv7ibZ194EYBdF+zkB956C+98+99ARE64ue6XqyqDwaDdGZqm4Qtf/DK3f+UveO7QS2uu944f/IH2WpNwdp2U/oT8+W23c8dX7+K5GN9FF+zkb7zlZt71jh9cswNPw8uTsPU0h19VeeSRR7j55lvYumUzK6MCpqTs9p2AAiRLmChWBBIIiqZEMkVUMAQTJZtQSCCKkDE0/h+7twkKpCSYxrhVMTGyCYogohRLCGvHoYD0xyGKScIUpC5AVSQl/0ACaxeCkJJhakDCTJEEiKAmDMx8XSdAY6lZQjHmZoa89NLL3Pv1r3PVVVeesDH3T+FTtZ/uguhu8v3o36naj4ggjzzyiPXhwtRjOf6+tLjEP//4r3HOuRfw5psPsOeSXQA8+dQz3Pu1ezl2+BC//I9+jvn5uXWjDfW1uLjIr37y19m8/fzJ13vlBf75L/48c3Oza3aWadSP5eUVfuUT/2bd6/3yL/xvbNgwv2Y3qbtOF0J2MfN6ka7nnnuOvZfuZcfO8xitKnloFBXEBHGLc4MTQwVEDeIkyAqa/Pdh9z5vaqgkUuz8akYGVATEHOYYiCSUQtJqkCDJsJJIvXFYNRIzLGWQQqqLEiOhqKS6gnzhqJ9cJplsiiXBSnfBJF957TEoJBKYYskwFUSgWCKnBkmZp59+mpdfepmdO3eu2USn+V/r2c/JWAX9CN/J7KdNiD744IO2nqM0Cff+01/9P7js6gP8d29/C5detJNtmzYC8OrxBZ547kVu/+JfcPDBe/iXv/xPkCnkvHalm/HPTvV6v/SP25vs3mB3fKurI37pX36cfdesf73HHriH//2X/3Fr/NOSTZMyzn0n0sx47LHH+JEf+REWFhd8pzcliRtyBVbFYlcTN0bfpoWEYiaxENTtjOQwByOZ7+r+b/zacY5YICvH/iDmBurjmzQOKMlIWkPThpohkuN0iwWI+YrNGkdeQswwcbhlWEAzw8wXazZDJfsYMP84ghXzxSHip5rB/IY5/sv/+0dc96Y3rdmA2uz0YDA1EHIqDNv1KDAVvk2yn64vOujHlqdlJ+vv/+wLX2JuywVcfOkeXj2ywGtHn0C1Hov+uUsu3cNzzz/Hf7vtdm5959snRknq6wtfvOOUr/fnX7yDH37n29fccBdG5Zy57fYvs2HrqV7vdm59x9vXJeqtx6WqD+T555/nyiv3s7y8TMLhSgLMElkURx+KENi+7uzi0EgtIUCi+OmSwIpjmYTSAIN6uiSDkkAKQgp/IgON+yaGnxIBz/rjcPckj30LU5I5rCsJsiYsafgjCbXEUIwiiqq4e1J9FjVfNCioYMlPC/eJIFuisTiN0oBkhllBcmJlaYnL9+/n0KFD7Nixg5xza6hPP/00S0tLzM7Mho/lfpGicW/hyyCQAv75Nuw+UOwi/o7WmXLfClhYOMaWLVvZtWtXG7TpRibrMx/0V1j34XcdT4DBYMA933yQnRdeymhUOHZsCZFEqrurORRZHRV2XrCLr33jft596zsZjUbtiuzGqQeDAXd//T52Xrj3lK53zzce4G/d+k6WlpfXOF5jSAhf+8YD7DjF8d3zjQf50VvfRdM0LYzqv/pHcH9napqGF154gb/59rdz7PhxTPCdNSliisYuK5rjIRsWPyeJQyGhWhyNCEn9sbrPIGRJmBRfeCb+kEXQwDZG49AFIYmh4kvmhHGYISTEGl9YBimPoZypvx8VJA3R5IurMcdrSTrjLuGjGJCNpJnGlCwWp6CRMLIoMCBhFJPYIAxNxjVXX83v//7vs337dnLONE1DzpmXX36Zt73tB1g8fhwzh3OIObyrp60IWp9XNlBfRtkCPqWIM9T5x+GhDIQN8/O88srLfPWrX+WWW245IeLVPvvubt49biZRMTDj6MISGzdt9i81YTjIzM/OMjc7w3AwANwp27hpM0cWln0iOt9RowruSAlHFpZP43pLJxAB68lnZuSUOXJa41tCIuQ7jYBWf9fnP5kZo9GIlZUVLrzwQpaXVzxihGHJEE1ucdlIkgOwKMncj5Ds0SjfZd2R9re7AeWkGOFUhuOrrd+jFDPMEkJ1XGPhxdY+cRzZARPJfR9Jiqm/P2wONKHJwJRs6jt0Su5nWHJfQj2IYBQIp14xBkI47OE/BVyUFI6/KCk5zJOSGY1W2bFjB03TrOGtzQyHNKNRnTGQghWjWInxGI0phvtyNOZg0GAU86hmlMYw9c9qMYyCjgpHjx9ndmYD3//Wt3LPPfdM9FtUlYGqUko5IbzVdUi7WHw4M8vc3CxzczNsmnejq6HSphSWVlYBWF2ZY2Z2tt2tuwuw/ruUwszs6V2vuxi6TlYd48zM6V+vC8/qYqgnZT861Y+dP/bYo/zoj/4Yo3o8u1PgoVRJiPqJBRkRo6C+oxchSaGIxC5uJHEfwDdqN3xTg+wRKXd2EyRIBppjy/fLY02CWCzTxuHX91MAEfdPNJESqBopFcwyJurAqg3jWpx6DqkExVIiNwIDQa1BAWRAQiE5XExJ/URCyQgFIZlB9rk8cOAAt912G/v3728jiMOZGVQDEShoSUhuEM2U5CecFHWIGbAzlwZJghRDs0AJ2FVdu2wUMxI+XyqFpjF27drF0aNH2bRpU7txt455fcjVuCrE6Ds6DkdW2bF9C1lg49wsG+ZmOWfjHNu3bGL7lk1s2TjPpvlZNszOkMTYsW0LWvSEHbhraDu2nfr1ztu6haa3uLqn0qhpOO+0rncOTWcy+vmMesLVxVgXdimFpmligWbmZgZtLN8vFmFZbbBGHfdSwo9whxot8ZAdMydzTG9aUDGKGlZ8QaiGU6wGVtDSUChIY6j6gqOBTHPScZj5qVHUoAg0DaYlIIWhlj2qFUhBDUwLNH4tMCgKZqRSGMwO+PM//VOGgznPe2jDYGbIvfd8g5mZAapCMcEEv6cK8RpFLLFp06Z2Lqvt7dmzh4cfeRBTQ1E04GXBEG187pLSqNKYbyBm0IQda6MoDUbB1DwQUvyE1tJgKFqM1dJw6Z5Lefzxx1vb7B4EqbtSJjmmXYjVNIXrr7mCI68dZmaQmJ8dsu2cTezYtpkd2zaz9ZyNzM8OmRlkjr52mBuu2c/qaHRCZGcMSxpuuGb/qV/v2isYxfW6GdFqtE3TcMNpjq8mGbs+xyRSX520GhMvpfDC889z7XXXsdKsYMV8t7fA3ChYJmWgGKkIlh3SWeQEwJDkp0UJKKIioGNXW9R3PnKKEK+E32BYDlxvUMTQzEnHIREQSCQaMSRn9+dlvKOmMUr3aFRKcco43JIUicWU+G+3fYHf/c+/x6c+9R+ZGQ6ZmZ3jtz/1Kf7PX/vX3HPvvTHaQsYgKUnFo9C5oBirKytcdeVVvPjii+1zEBGOHTvGxk0bfXGabyxGQ1GHmaYB4YqRLDYIIXyw4vDQ/HoJQ61BTHyPKeZ3aMbxhQXm5+dPSDCaGamPt7pQZRJL8qYbrmP56KscPnwYEZgZJAbZ/5sZZMA4fPgVlo++yoEbrqXCtq4Bdv2bm9/8JpaPvXZq17v+2hNgTXcxN03DzQeuP+Xx3XLg+hNoGN1Togsxu4umnijPv/AC+6/YTxlZZIo1YJIf5yLmBpeFMhCwEg67J8QkVacyRe66eLg1aRtcUVFSgmzFsx3uWSKSkWIUibiSpcD1648DMU/ggTvP6r5PskhrmEMfRFEJKBcZfJPkjq4nQTA13vk338mHfuJDfOXOr/J7v/t7/M7v/A53fvlOfuqnfoqbbjhA8di1/2nZr5kU0YQlhz7XvukannrqqTW2snXrVpYXFrBmBH5+OBnA/CRxP0NJVjySZoo2DrLcbhu0eDTORONnxde3FUQLqoVmtMreS/fwzDPPnOBe5J/+6Z/+F/1dvZ9V7BqHiHDDtVdy51f/ktVRw9ZzNpMkMRo1HD16jAce/hbPPv0k/+N//941Wcr1CoNuvO4q7vjKetd7gr/3t98zkXJ8QpTJjBuuu5IvTxnf/Q9/i+effpIPf/DHJiqO9EPX/Yhd9UuWl5fZMD/ProsupJhDBEniOzaCWMakuNMsjp0znhzTSv0wRbL4yYCQyWgliJhTMzKZRty3cNhDJNgMY0ACz66H333ScRSHVSnCrZbdfzFpc4okFLHsvkEkBE0E6YSPfXxQrHDF/suZnZ3jttu+wHPPPcv73vdefuiHfpiVZrlNbLYJy4Hfm9uSn4jaFF47coScUuvnbd26lccOPsa27eehxXd/JO7LQAZgJRZ84+epZM8JeYwtIaKIRZpGxXNFLTvMIuye2LRxE48++ig7d+5c48/KPffcY5O4+11nvOsj1N/Pz2/ggYe/xaNPPUcTGHc4GLBvz0Vcc8U+VlZXpkrgTCrPrNf71pPPnnC9a6+8gqWlxTUOUx1T/VmfGbphw0buf+gRHn3yudavaMe3/3JWVpZPYOXW+6vX7CYcu9GUpmm47777+OAHP+h4NxYlSdr8gOGOY45TwCpIsRQwxkjqxi7iECCJY3MBj91L8viXKSlFrN7iG6SyrOp1OaVxEAlB1EPFyTzSVCkjmgSp2XGNPALBO2kMy0JW8YhW8VNpODvLp//g03zjG/diCDfeeAN/+8c/yOrqClgOLpZgA8NGKU6vOI0QhMzK8iL/9fOf58orr2QwGJBz5v777+etb/l+jh096rAzCNApbhMqRItTRVKHGBmcF6CYkSUjWlr+pNNrIodiwlNPP8WuXbvWJKoHXew+GAwYjUYnxPS78d9q3EtLi+zfdym3HLiBuVlP1qyurPLakSMsryxPrNfuhnL7hLKFhePs33cpN9/4Jubm58BgeWWFI0eOsLBwfM3i7BrytLrlhYXjXL1/H993043MzsyAwMrKKseOHWNxcWEq6a3PzO0nSFWV0WjE7OwsMzOzLC8teagTQ5w8FVljZ8B6bD/Ig2YkUaz4I1Tx06Sh+E4cxuIZcHdAU8Ak0+T2ihtWzYSj1NThScfhYM4dbne6fVcV8bCx5zpicanvvO2qix+bepSrFMgizM7O8h8/9Z/46p1f4f0f+HEA/ugP/5CVpRU+/KEPsTxa8XAxhpRUM6C+IFViDgqbNp3Tnso153HhhRfyyuGXyGmGOjtJxfliqYbFPX+hpiQKWIoNB1CFQYrTsqGIRLTKT+xk0ChA4Yr9+3ng/vvZu3dvaw+D7mmxsrLCYDBY44TWf3eNsJsPeeWVV9aVdekvvm7Uql88tLq6yksvvzy1sH+aSkh9NU3T+k0iwuLiIouLi+0J2K8zmERP7+Z2qvPdz3EcOnSI6667jtXVZTQZ0pgf81KZrNWgAuNWrlJSSolEWPL9WqWGOt2eJSlaguJhvusnEc/yV0ygRkkpiH+QRSnCScfhnCl1smM44BVIdVnBnnFOEfpMiBVUnMxYeWFZ3FP6/z77x9x5x528//0f4F3vfAdigpbCZ/7wM1yyezdveetbnL8lCbNC5cAUJ7d4SFoSS6Nlrrnmap555ll27tzJcDjk3HPP5eDBg1x2+RU0qytgQol5dNaDopactoLFCVyQLKhCSua+hQTzWD33YwKpcUaA/8ztbBRRs/bk6OPsLkTRoHj3C/Yn1Rv0/ZJJBSarq6troEr3c/XPmvnuQ7FJmfu+sMAkI+/WpPQTndMURGqCr8/6rD8/dOgQ73nPezh27HhEYzzk6Md6pX8bFbAIhonDJKhoxWLXTwFp1NdPY8EgFzT8Dv+AOwbOrjWPgKXKbfLo1CmNo15HPHFmUhgTVQh+lOOWLM641aCFWEtbj+cgyo/+yLvZtnUb3//Wt7G6OkIwfviHb2Xnzgu48c3Xs7K86idNzTGUoLlLxUdOiMwI1159NX/5l3exbdu29jkuLi4yPzvL8dUVSoNDtDjJLKBRI0YmkqXOcEQSFI2cShKkLowSSUlLkU+CZMbi4iLnbt/O8vIyMzMzbkt33nmnTaqK6lZx1aOuX7vcx+yTanq7MKpbfNR1quvu3F8wfQp5d7ev4+v6Al3o0/eX1hMHm1aLPEl8YWFhgZWVZd7+gz/IatOEPzDwya8bdXLoQwnqglR6OBFNcqhDOLbq22dEIiM513iUR8woyZ32XJ3sFDukuPtSKhnxFMbRJgZjx0ymIIM2CWkBO6xS4FNk7cVQC1jia6UNh1rOfgKotgnLFGOWDkFRTZyuUmodSaX1BNoYCF+548vMDGeYn59nZmYGVeXwK4fZef4FqFkEH2owISElnil+X77Ak/PQLBYzYNKQbNDys2rEziJQkFDO2bKVu+6+i71797q9dxNp/d247yfUnMC00Gw3kdNNltXPTitb7VZ91YRQn6zY/96+L1TDq30nvY6l+2fNldSxdU/Mfji3C+1WVlZ45JFHeOtb3sZodTWIbL4raTAKa8UFxTlOTlfxZJRU34DkcXuLRG6p4VdAszu+QZZTBSkOY6zu/MUJWaYRTTuNcfhxlaIOQ+Ik8ARhqZBNPOyMJFLkFDTGX0wpJUCYiI+ztaHY7ASsGRuemS+iZCCqEbiKMKv6ovRxKbccuIWDBw+2lBIR4YVDLzAzHJLQCF1bhzZSPKnZEjiDvqLmi5sCyfMeJpU640466uPT5Pe4PFohDTLD4dARQ3e3rYbdzRB3F0mfqj0pD9CFL12sXiM+1ZDrd9WfdSejv+C6pMWukXezmv3/uhCpe63uoqvjrYuq6WHOetp1v3N+fp65+TnMMpTShjOt3QE9ROvYPI5/83oKlRL1FIrzNSRyAJXFamBNLexzjJ78mqq+YFJxACZ4dvB0xqFBFycpUMhmlBw0k6CxWnJDdlqKnwR+gezcrmD+WkR+tN1/jUJARRM/tSg+1mSU8K80KCGkgiEeoFAfkqkyt2kD8/PzLVlVVbn22mu57YtfYLUUNm7cgBYfmgUb2BduHJfFKBRnAqRIXBZnKIuCNMWTirFo0eKUEwEdGcuLy2MbuP32260LfU6m0zRNm3Q9XdZazDIajRgOh+2C6Auk9X2e9dQpJtU+96HSYDA4IYk5TVh4Te1w+Br9U+PFF1/kyquuYu/eS50KkiQoHtLmCHJEhGosnRrPD6IgOaIl5rAJ8YSYJoWSUSnk5Bwqq5gjJUyLR4tybhm8vkA4rXFIMmiEkqSlswseyclSTxL3XzyRCGLF5QaiZLZSw2t+JuHObaJuAuo8KLF6i/V/Dgu1pk+C1hEOcRLP8D/1zFM89NBDnH/++QyHQy91Hgx44IEHKKUwPz9PaZq4txpVq5WINZIXLoJGwZa4RyZtrNzvQVGSed3LzOwsKQm7du0aEw+7jmyfaDiJRtENuU2Td+z/vMvd6i6++u8uhOpHlyoEqousr3LXvYd+sq7eU9c3miSjWRdJ9WO6i7DuYIcOHeLHP/B+ji0skSWyA6kWKKU2x13LUn3HcuydUoSjgk6NJqwUkNziYMtK1hTwSONKvggk+WJJgbFNGswyScopj6MN+yZjkEAbQwYNlMwwfKKSPN8S9u9JNcmIRj1HkByliEfGRoKm5KzxONMkxpCjTEDw7L1FWUESpWgIX2SLkoKoGMzC/iuu4Mtf/jI7duxobWJ5eZnLLrusjXh2Swb69tO3iYpQuqWw1fftRkyHw+GaIM2gL0ZQv7juntN0nLoZ9a5D3GXdttTfyHr2F0QdcH8xdk+P6kt0F0w3RNyv0OtH37rXn8TA7YsorK6unlBPXEphYWGByy+7jMWlZa+YK8kd5sDqKejfRkNmgBaPubvwgNKUQYxRIZnvxil2VC3+WREnFOILysSDrKoCNAgShU/J687l9MeRygCNYqriTA7PyquzcX0z953fiIRMtnEJbQ7elnrQQHMhaaIBp8cHLPOKkUqV0VYvQsy8ZiVqQ8Tpu2OonmBlZYX9+/dz5MgRtmzZsqaMtm8bw+FwzabaZ23X1+zs7Bok008kT5JYSl0j74ZtR6PRGojV3fFreLOLxfs+S5f+XXfe6mtMKputi6DrVPfpIX1fon5n15/o+g2T9GHrOJaXl9fkbUoprTJJHWddwE3T8MQTT/COH3oXpSmRqnP6tyexvKRUUMQG7luY1GgqiSF5UDynkSzwdZSamkQxj4dXsyWyQE7uvFrUwWrKEIVPOqgV36c/Do3TS6VGsiLWr2PlkAxYKo5zaiYcozHncGlAGEsgJUUdObhn4ZAmp2Acx2JHpBO50tZvMrE2ny1RHKXA277/bRw8eHCNXX237Sd1C4e6cKceMd2sdI3ydJ31SYXsdYDd1djdyfuwqgt3BoPBCSJjNRFZrzccDtes+vWUUvqJwvr+4XC4Jsxcd4+6+6SUGI1GHD16lAcffJD/4e/8HZaPL3nVWXCSPLTpLB1LbqopZGsQT4aliB5pyaTAtwWQQfAhtJCDqaqRlCtBabBhwnKJIiOLTDfQSOQrTn8czsNIHhUL6J0HOGwzJxUWEZJKsDIEy4ZJ9ipDrUojGg67JwuCKxwejuc1LIfJB8YXEY9WRZGXImQN1i+GpuSnWTGWV5b48Ic/zNfvvZejR4+uQTffLfuR27/0pdhCiAL6MUmM4J7Uyq5WmiU0kmqEo2oj1Z9JyLVU3GqRFfU5Cp+ge52uDx+7UJ3kVgFJWPO++pska+Vqqq+FdcbT+S4i4tJqRdXQUCSU3JeTljM1HA64+qorWS4N2VJ8jQYBLpMoQUWHAUKDix9U+ptj/cBI4nymAEvxQBwe+XXjHlJBtdIQ05hwh6JZSPo6xtFm68f3i4IFnZycQhBhnJF3H1yD4hIlsCpjxq6M6+NpqTIhAySKFKfiD5K11A93xI0qFlRTqV42HLkhEyQJDz74wASxD9pn1SGutQteqM+acRBhLBI2wZ7Hw2+t6/lDh2woQhG8drkaazUUBI00ewpqDClR1EXGqiZRq2IR0jE5HM+avZWITzsTMqInEb2oC0BDoCxJ8IoiopCCy+OsS6c1iPiR7xFFI6dQvqhiCjHlLfwQN6Lx/Xssp5iQrBpK5JJbdqrXSYiF1kegDM8ZBDkvplIl5igHLSMoH9XZFIOSa2WdeXVbZGpdWMfxuVRHH39fLFvPVai87nH4JsDaZ6WG5SBFVl6X1PmHsV5QhEQcCZcsAAAWiUlEQVTN/3S+VfIipBosE0FLzWRnf1Z4Vtp9J/WAXcpRvBVUrprIi/vGxpG2yOWf5FkJRdx+Bh6DeN32nCRKFyV2No1Yd12ViguMpZRiB/AEVor4u0VM2+LYN/Hj2LO6cXhGFVjoVDjetnqsBMUnDuVURS1IpOw7TqP12K8Lw5Nm7vA5dUDVs8MSnl8hwpKhzKfqIgWu3RScHgtjSw4l6s6SgtWKuhqHU9OkzbYWMyx1ggPm7FZJyZ1UBIsyToschguG+HcXxZN+kdmuQVdNJRJw1jlVtS2W+k6MQ8W8bDcMMhWjSfgmY8Vza6o+P8Hd0wjFWlQG1mNc8OfqGWev2WgipKzqBuf+T4kyFB9f0TzOktdEqLoWlpl4HQaN86LsVJ+VeaTvO2jPg6B6erRDIIdR13oD/02s+uQTSJvrdfwpUTBjA2sTR9IINlCkyWiuzM/sD0DMSS0mTkqzhKbiANpo2aClEXLc2LhYP7Kfgb/rSSvWtMk2y7G7hbSHVUigtcjIKMUX60iEQQOWa/DU0BIs2ahRdqLOd2aOihZee+UwRYsbQ3a2qmbIxb1hC1U3oQ1ZjRm7GnkFEXKLIYmEn4zRRYp6kXofFYIEydHLB4OK0RmHf5eChOYVY2q81edWhdrCc3B4GBC3as3Fs0rFoaDXllQYowEVfcMiiJh10Yt2TqzkTGE/GeS7OkeDFD5CSfFnOFxSAjRKwKIUxklGvC4TGRSk8aIXk8bLO1NyxuQgao6roYaRWlSEWZhiIlFaAw+tpMANKeHx8Ip5Ww5NPDj1YnpPPDmFuoqMWRz1VfAMgvgnhpVwxIrn5Er2IqEkvjMnySjFT6SUnX/0HZij44tHWVkdccnu3Zx9fe+/Btr4DuvUBW1hjiTfL1IpNAKDyNiqFefN+Kbv8Wp1LaVKoSdi89Iq7/lRlc0jKJqsfW9JNi7cJ3mMPRwlrUzTtoYldokorjZxcKniO4e6DIWvfMVLSXODufU7wcE8yVZq2FDHO3xj1SeSVr0jWUE1ofL65shILC6tsG/fZW3M/ezre/uVkBJVZo5HCacxyDruuJkTzqzSHrK4WFqFKWLkyOWk6kTWLHllZhYP3UXZekRliFj4uHorBSFMzOPdFllaB8w4F0Y8sqJVXdzEaxnCaTXxMkpxBVn/uqSh9FcimuEOutQQRUrOdkU8vBo+kNcCvP45SiI0zejswjiTFodLODaUCJ21YbzgpKSICnn9rbSliCYRprOE5OTOoLg2kJHIUQYq2aMKOVVpRtpoeKoymVLpy0FbSA66UjinxRLJ7RfLGUWcyyMOzCLC6pg4uUaTmKDZa4fropFU2miNk3BaXgdSwqlLige1amVdcvr2654j9zfOvs4gWEVxVW2rNc2iHg7P7qiJiDujppTqtBSw1CDmcXRWidqA8LOtuAYTTlOQKOUZkwIrv8ZFugbqWqzSOulxZBQNOU+HLSkcNi3GAEHzyIPDGkxRF0cKCqpiKaT7i7rwWBX6ipNINbtoQHJGJjmFbEsVFNOWkWq8zjkadRzDs68zBVa5wQ2DgpAkObOsRFSiCsdJxdgZyKHm4OFbBhG4SZW373FzHUR7lKBGWIT2UieLOtTIaUQtmvsblT7txDUlkzRXIqUnk5LnHyQWBknIqDvFklxVQ2sCLBYdCcmGWnJJTvFQoRYhDYKBKhGxyQRPKLVhz29njlxMwDVaObs2zjBYFUmdxqzF3yI1l5DaTKSiaEkgrhhHEmaGmc2bNjM7mHF4o5HOi2qvXEJu3/DC/uAQEU53FokkWxDGUtVX9TLHpDkUx+tWPBYTm8kDl6jJngjy/GotAfUQclRTen11VMah47BdUUgpBJ6DFaCUEAwzJGVmN8yxYdM8m+Y3QM7Vg4kkkTE7nJ06R5VHZHEAnV0bZxysiqysefioiDCIWmQstbyYZMllKkMndX5mA//3b/w//MnnPsett97K//qz/wuriwbZd+IaXUo11m8pdFIjc12MJJGbiOIUzFzvtR1P01bOEUS9lDOPPPQQr7z6Kju2n8cVV1zuSStxaUxyiCfXa5iHgEuQ5VxCMORhUE/IVapQUGFTSqwsL/E7v/uf+eY3v86RI0eYGQ65/PLLee973su1N1xPWVnh2OICn/+jz/P+D3wgAgedOTJXRS8mDCIjXSkjZ19nCqyKrKMUT+J5mt4CK9MamAZRKhnMzczyG//2N3jowQe56aab+PrX7yUxJCWLCJLDnMoZwlwNIqc4naQySR1Mlcp5CQl8SSn8F2lT+W5YnpRqSmHvnktJOfHoY4+ShkFYyzkSSNYqjbcyXuKEEq1Z+6iuz4RAcpw6szNz/Pmf/lf+3k/+JN969GG2bd/Gvn37uPiSSzh+fIGPf/IT/Itf+iWOHDvGRz/yc+Q8OHGOYuJUPNTbWGkTY2dfZ9DiKADFG5aIppYe4KJi2qrsIQ2iwuzcLP/+t/4D9993H9u2bUO1cPPNN6Ml5F5qWNZLgl260Wruo6qMp7arllrxgFHtHyFgxeVUUggwN5W8KL7INm3eiKJs2LQRBQ5+63FyErSx4PxUiFfpGR4CdqFwRTUFT6j4Yo6SycEg8cd//F/49Gf+kBuuvx5B2LZ1G9deey2X7N5N0YZ9+/axuLTEz/7sz7Jnz+4WgnbnyKNmrhiokS23s7DqzINViaalbZQcNGXR0AbKThxIgllhbm6WT/32b3P3XXezY8cOzIwtW7byD3/mH7K4shzdiYxG8IRfKzwfLMiiYeAaxfnFI0ollMilbY4HRaNqLXnmXDJWCkVgzyWX8sSTjzOcmWPzhs0cOX6EJ558kt2790TysJCCJtBUVTypQoBGyRp98ELmrHgTl8OHD/MHf/Bp9u3bx2g04qMf/Qh7du9meWWZLENKafh3/+Hf8/xzz3HFFVcE6zeoJDFHLvDhVBlVF1FGcoSlzxrcmeVz2ABSoYTSRAlYoCl7D4eggc8Mh3zmM3/A7bffwfnnnw/Azp07+djHPkajDefMDUNhI1rwBllNVVlcWvRdNFid3quCwP7aGo5ZioYr3gekcnA8cqWklMMHSly+7woee+IgOQ3YumULh189zLPPPsPFF+9qWxRr66iH6oc60SxriBCrO82ShJQGfPozf8TevXs5duwYv/iLv8imTedw7PhxhEQjixxfXOa+b/4V5+04b1xGGwVHqXK7teZSSnTZqnUTejaSe+YtDu82mkU6HYEyYh5gFVNSHvC5P/kTPv/5P+Oiiy5sDeOhhx7i3e9+d0igyLgntYybtm/cuJFf/79+nXM2nxPKEwnJ2tJKUu0BIerZZIWmKdx11x1ITuOWXLjgGSHfLyS2bN3Cls3nUFTZunUbrx0+zPPPvsCFF1zoY8rR6kqcqGgaogiR6fYwrUfWBnOzPPLwg2zffi6X7buM7du2szJaDSq28OLLr/LRj3yEiy++mKNHj3p8S4RmtQkWLeHZV3GxqvAXGFNbZvnZ15myOEwsIq+1j0EmpdKqSKDG7Pw8n/7MZ9izZ8+a6r9zzz2X8847b90vKKXhi1/8Eu99z4+5+28lEhgS7EpXscgpuzyKKK+8dIiLd+9hkMQ1YtMIs2FIygS1pOo6hR9hZLZu386hFw6x6+JdjJrGe3lHEZEnxx37ayTzarPHJmU2JGNpyWVZLrrgQlaa1SBOehRr1wUX8NnPfg60RPbeN4Dl0QrLxxfbXuKo530oNm4qGYIHdhZXnWGLwxVhnJEq4lDDosREjZShrK5y1VVXsbS0dNpfsLo6YveuXWhxDySJdy8ySSRV1LIr1WlpT565jZs5fuwIaWbW9Y0sk5JGHsPFYES974KKMDDH+0X9NGmaEs0nvejHlFbqsu19h4euNdiBRYW5uVnX2F1aIqsXz2jxSpOl0SrLr7ziaRJlXAVnRm6VA6tgW/FkYuuE51pOefZ1Ri0OCrkMKGmsnySB2ZNAo0JZXuEjH/kon/zkJ9bUY59//vlcffVV3tpM2sLTtrZPzNiydQs33ngTy6tLkSj0uolkng/IOHFPa2mjGlu3biJnf2+tx0gdSYFg/XPs+AJZ1DsUkVhZXuDSSy9DKYg5M9ijYt4FqaiQcnFNpWxYyd5ZVYSiDeeffz5NU7j77rv50E98iIWlBVqhm5JdtsYysxtmKKPG+wCKK3VX9JSTM38tNFqrOksKn+fs68x5ydPPPF116sPochSF+C5emyFIRGQ+8YmPt7W8zz77HH//7/8kb37zAe+akw1TbwxZBbUV7yCkVW81HOycDG0kmh5KW8stVVM1VUHxCO9aFMvgnKnXXn2Nlw4dYtu554IVFldX2Lt7D6Vx/ydngvIuURIqQYyM8leSJzsju52ScMcdt/OlL93O4uIiBw68mb/7d3+C5ZXlqGly3+uVl17iV37lV7nxxhv4n/7B/8xoZTWKZizyi937SO6UR4vjFw49z00HbjprdWdKnsOakFUxjQ6exYnehdDmHtdooIWPfexjlJDc2bXrIn7zN3+Tv/rmN5zuPYrrFHXcrRaG6Ht+MYNSEFXKyHMSpp4LMNShkXmyT83afm+VKFi1/I6+eoRnn3uGrdu3oRiLSyvsufgSmlGlo9decNCkWn9hjKJGo4QAcqNOFUmmFFPe8a53ISJs3ryZ2277Iv/q4/+Kl156iY0bNoHC5z77x/yjX/gFzr/gfP7irr9kNg+pnQHaA7VoKwEKhjVB3bez2fEz7uR48qknLJNbdQ9LtaC4qkDUckZvmSXJM9Sf/OQnWxmUgwcP8lu/9VssLi4g5CASWtCVkq+TWmgURfFAiHpVXaZg9YpHtCza9bbyjeIwaSYP+NrXv8auCy6ClFg8vsieS/d4F9bIp2RxkWLNtLXX0Zpi3M8uKPBaO8DEybi0uMSv/ZtfY252luXlZR5//HEWFhYYDAZccsklbN++nYWFBd73vvdx0803U7Q5YY6INsaVGZ+Sy8+8+PwhDhw4cNbqzqSTo2BRqFOiFtuL8FMIFFA0+jQ03p8tZX7+536e5RWP7uw4b4d/Pqjpg2gmoqqIGk2IhlkqFINGjSZOBFXvEJpKQa2BQqhIBPtcFGu8GaIUb86+aX4TBeP48aPs3rMbbUY0USCF+omgRKticzHjohpL1JeHJqUpTl+36HmtpmyY38A/+6f/hAsvuojRaMT+/fu5+aabuf6GG5ifn2eQB/zMz/wDvu+W76OUZuIcWaijazFUCqWRXlHX2dcZcXI88eTjZpJoJYTa5iJjwYFKwo7MhwscZKGMCnd/7S7edMONbJyb94hTkii2DwVwAaxBZNCWt0rkARSviZBilARZw1hTLcgfh4UsKvEkpPEXFo6zcdNm12K1VjmrrfKr2lOefmg1WECdS+b6JjZuqFgpLHhgYn7DPMeOH+eRRx7m6NGjzM7NcsnFl7D3sr0cO7rg2XCxqXOU1JuqJFwsLQ0yh154nptuOutznDmL44knDPHqORt4Ew/VHPUXIeyg4pSLUK3TkHMPcaiqTeFwJVH14d2g1ZyqHmL1Xq0aelUaurBZooVCrdgLmJNrfTmtTlGKL9boA6HiDUwkh8SPiod9XUCD1EQ31VSbqsQCiSYsYhJtvdplBGY0SRhaFc2JTqbqQoGCYqc4RzUhmtOAF188C6vOLFhFzYr7jq5BIdeooZBUQg3PXD+1lOiyGBhe3dFurGqQatvpE6JKLjXeQ6HVEKoOdpwQxXMY3qXHG40UDGk80lTEe+URkvtaqScWPSrCnyBgVJXsMUtobtqGj973O8iUNCGr0zj3KciNqr7gBhqkkLZ3hYXBW0uzP5U5MhOn47SaT2dfZ8zi0Ej21cahRLzes9niESh1yEEJdZCIHCW1tr2UiF+H6Nmg0RWxrbQTV6zTKmxEkPQ6X1xlHNUKAwnZzuR1TqUEEz3V0m9DdeCD01Atz3jVYTSalOLYvwRd3YoLOlSVESxakdnIGzhqRJWiM5IFrUXR1z1HVb3x7OsMSgIGEhmTG6L/AsGXkoznMGosn+QVdC01PYWyRwqZ+mjxS9u1xHddBMvFdYdLaRuyCDky8jXWFO1QrLiUTklRo+Rqikkt1MktZEejM1UIK9RbkFQV1r1iRC17UZVEaxcpURUYcEqhiPsz0nj0rKr6IfK650gH5ezaONNODqnE1xAIIKIsVVXDosmIR1b94edsnUIoGxtRLm1OoVXwVWtVDnNTF03VeM1eUpqsbdjYRKKvsg0zDmUkKSKlI1Zcuw+NW+VWX8ePIRdSbheLjFwNBD8NBiWa1BOOe/gG5rMShUoBM78Dc+Slv+msxZ1RPkfoQbmYQY3qgI4E1QYzx+Zq3uAU9SyzVawfjd2LetMUxXtEW/SK8B50OT4T6iMaHY5QShVIEPH6h+TizVWlRIMoWFTRIjTi/kkJyCYWbFtTirijXlXNVUucHrX/t2vrWiNOlykuueNhZ1opTO/zEi20NNqhvc450qawYX4Df3XffWsoOGdf38PRqscOPmYpFG1ShlSyc5kyUQvurXSlNBGFicBU1F20WlPquk2VLlLFvzUUsVPrRIdvUCKqU6nudY83acNg1tK9MySvIHRIpeTk6iWErGfkMaM/orcPQ1rdbvdtShrXkVeyjCilqrkXVzqR6NtXhcorn+t1z1HxnNIrr77KyspS9LUIXtkayFUZPbV1g7WdWaPXfCsKYViwVySawXTeYIzJjq3av7Tq7bWPNxEKr+OQWrIs2p6I1umjN+011oSvrSfCyOrvpBaGhSp7R4e3+px0tXbbga+9le/WHMljjx70pHg4qSqCyMhLSU1c87XqzVpoCIbUjSuXgKYQcDbI4vKZskauvmrcgmhGJSRzglflSt/RtsBcMsc0t4TDksbibN4ZKSrwxJCiHnatRVFt96DIrkdttySX1K+ZE4v0dTXmFCdUFonmMcEOiJyMiLbq79+xOZJOo4ToAe6+j7WLX6r5mDe7SUGt8QaQHgk0Fd94skb9irR+XKp6KTIOr0t0lR1InKhrxkGba5LiRNRstZeGz1OTooePpjVtjMfPKuar6kNXKqq6ovugfVZEi4BoU2AaWsUhB0vQ1uJ5EJWX3605annVIkKREp1Ds3cBjRZZIeCHiIuU1V3FzBscinn72kQJ/K2RSIwFIJVHFUVNRNutCN820SvDdbAij5E1yIdGrhQMIWTjSyvCTU7MRqjUhZ4tdi6NZF9oZsVNSLZxf47sLYNTZMyl+hrRMyKpRueMyHN8p+coKqBU1GnuGCWVttNq7R8jql6LbnGaRu9AojamCm1HcUtUZDZutBqSQeoVj2hBtbgGcJETx4GXAljxZ+bs6WjtXIxRSLYq6gvDAqKGwkpSIoEM0gQnwSo1SZgp3tO8RBsJgnemMVduG+NnpbUeJ/Fdn6OBVme0hmKJTp9tljlk4iWq8qLfRirxuTJuQqPRJqgemVrPqugYlELbqf62WHSASuIdf1o2cNDMq9+SQqbexp2anKBo0fsiGMSR3EvW0ERfDRkYTSPef1oEHakLrJVEoYE09Aw+CVd/tKCwZ3IqSBn4Qygh+fnXNEeSBt8T4zj7rMZzNPjKV75KbbLT9idogVhIeNZjrdvaqsrsmESuo9Mjq6NCMq61DvwvXdzr35GihUBtKmNRm60hNh05OMfHMZQkqdN2rYtvg9LRJiSqhm0kO10BoZUBSiYtDBtLhNSuFFVwOnlo+K9xjhLfG+M4+6zGc/T/A8/G/snZpSWJAAAAAElFTkSuQmCC);
+    display: none;
+  }
+  #chrome-mid {
+    background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAABICAYAAADRa1RpAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAB3RJTUUH3QcNFycE5v9iFQAAAQtJREFUOMvtkjGSWzEMQx/0eYrM3v8k3vgqycalSwlI8Ufyl3OBFMtGIgUCIEd6PB6RBEASqvfONSrJXrDNbNkQ8ywA2y/SmayW+ZIESTsiyQsxo40xmMS2aUmYbheHpCVd0+UqJGGMsey3mUyldoUvlY3D9rIN0K7Wbe/WbZ+y1yWtaVtrp3VJzAEX6ZVjc2p7b2mtnYhNdl6m05rwtfV/ltx7XypJTpXeO7Y5juOlchzHaWxyrJmuhLapqgIJONv05+srThBgiQpBTSRwGOr3rwccgWHUhJ7P5/YNlbd/2XiL78L/WajP240AQUihfnx84EDJjCHKHjTAbkimQDgBjAJ1/3kHAgEk/gL71AHEWVXPGQAAAABJRU5ErkJggg==);
+    display: none;
+  }
+  #chrome-right {
+    background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACYAAABICAYAAACaw4eEAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAB3RJTUUH3QcNFyghKmOqnQAADE1JREFUaN6dmsuyZsdRhb/M2uf07bREYDykPeIleAMibNx92i9BgEPBgyB5xlvgtgI8VDNBI41xhGkpQowERgqw3H0ue1cuBlm1T/3Vu4XNiWj9l12XrMyVK1fWL/v6668lCXdHEt/1Z2YnnyUhCTPbX8dn45pmRkR81z7/XUr59Pz8/K8ePnz47/bVV19pnDhu0t+Pmx0Z+Pv8zWv1/eZnZ2dntw8ePPizZXw4bj5/P3vq6G/eePZiX9fd9/Xng6/reg78/dInzxPG9+/auH83GjEbPUahj6m1Hoa6v1/X9c+XPrlP7INqrfuru7+10WzUkUHvOtTojPF1mPdHSzdqPPXo5vm046bdq0fhGr+bvXZk6OgAM2OZBx7hZD7hnCzbtp149Wid0YOjx+eE6t8tMzb659Ebkg5PPY8ZvXpEQWNCzck2M4H3BWeM1Fr31/6+GziPmTefM3tcYzQoIt4a3+cso2EzhsYTzAAdw9M9M3rviPv683dl/Oi9pdZKKeVk4piVRyDu1NI3mCtARFBKeWeGbtt2yHV9HXdnGUMyGjSfZq4K42ajYbPXx836XjO+jsj3rawcFx5dPgK8bzJ6eGbzI8yO3j4yaMToiWF98fl0c4bNSXBEJ/Ozd1HSEY8BLGOIxlONeCqlnHyWtGNoxteRMX38uP44fkyyPnfpp58zqy/s7jsGj0rOEcvPVaMD/sj4I/zWWllmMB/VviOwHumv+dkRGc9EOtOUu6fHZteOGBtDN/+NeJwPNRsxl54RU3PIO4x827a3wNwfdr45kib92WhAf9+fHem1I7FZa31rr+WIr45kzrjZsixvZWHHYcfqXFHGctM9ta7ridcigmVZWNf1DvyllN2wkatmHIxCby7kYzbPOD2qFCN39efrut55rE8YM3I+8VENHPFVa2VZlkOSdXe2bTuhmHdl+W5ox8T8YCbD/l2t9YQqRiNGjx8l1JEamVXKri56doyTuzfGhWd+OyLJjsNRlo+eHaX63Iy8ldnjQn3hbmA/yagGusfG7JwrxZytcxMyjpnH77VyPEEP65iVs5tntp4ldp8zlrG+x8z2Y9L1f91Jy+zeccGZn0Zv9nFHTH500BGbM6HOojMiWEZQf1cN7Aut68qyLCdeGFN+xuRYJ7tXu5fetU9EZCiPOp8xm8bTzLqpe2jkoDnzxjCOa8/VZByzzG7t8gQ4eT+GdO4Be0kZDTgq5kea/0g0RgS+rushNkbg93o6aqeejUeNR/fcUWmaqWLbtn39MdGWGcRHUrcb17E1jhszq3tvxNCsJuaE6VGZMbeMKTrL6LGelVL2k41jx6zuRbknSS9BI7WMdDRTxLi3z+VkDl3/7vb29oS3xhoZESdZOm4whrW/7/NHT83UtNze3u6c1I06Ozs7wdjc7PaQzsV8JNSOp7k97IDvtDPDYTdsvts6Pz8/MXCsm2PD2g/Tm+Vx0bHZHTNvjMyRyh2pajk/P0cIZEAHLLgXQLg5ckDCAFsKCwtIeHHAQGAmSnEkMAyZMBkin4lc3jBEM4a7MZgo7mBGhLD/+M1/qiCqDJflIjICYbknjlEtQEl81cBDYIaUi3aDwoEQ7mABuFMjcHOMQHLMRLSDhhlFQk4+k9IhLggZBREeVLN+NNwNCAhRwjGMimGyPJlA3owyIwiKEltWjTBHNchIGpLleIS5ITNKQHVDYRiBGUQI/83X/0XUyorhm2EKAsvT1IqFgwusgglCWARV3SuGmdNchwgiRHWQagcHIqCNJ7whJ6AI20AeUJ3A0ilP/vQJ33zzDdvNDbWkO91oAwphrah7wVGG1cHMqSHkggiwDJthmAcgjIIVg5rfWc1h2AZ7AgBLpMElMpQCUyOSX/3rr/j+9/+EGoEQTgKxKnDADRROmCiWySJBeILbMCxENVhwBISCnldm4EBEeiQRk1AJs/Y5ER2q7BX03v17SQnumDeXRqXgDaSA1cSdIExQDM+UgtoArTyMIjABJUPt4S2hRHEIgbdstV5LI4OusDvDMgMNqw3sHqi0HPcMotyRNqp5ArnmRrkLuBm4kHmjDAeEDMICk2PFMwomqjI2xYSHsJIUUnxoeBO7rdQUJ2qeJk8SLfdLGtgWCouEVzFUG7NXMAXVG1YqyDdMhSDgFuTpabUEiUguUw3AiAafbhoR4EtmpJknKArgytMaBHBmIozEIQ41M1dK7ySGEvxQ8NoI1w2WFh0XlsUaFYilJ5zhpuGKwBxXeygIqxlrE6Ih1wKPgi8L799/QGcJo4M5o9oYDfcKUZJmEFdX12zrikh2xwwrQA2KOeqETRlCGaKaUFXLpjQwy5Elu4dzflb4uw8/5MXP/wEsE6ORVX8hbVRzTVcN4ic/ec4HH3zA7XaTC1sQtZUXAm98Z7I7uvjii8+5ePw4pUiwu7TXuogM3cX7j/jhX/yIJz948gf/NPjll1/yy1/+E//z299RCGrL+AxI8krQfhk5Ab+6LmrGyDA1dvfkqOvXNzy7fMonn7w8umjafabmsDuowPPnz3nz5joLiN9VCwIqJDGHweixV59/weNHF4itZSMJbGq61kg3h3N2fs7D9x7jIdTwIzw3tCxrZo560U5U8frNFdu6URWJS8RmRukto3smv07uxwJrMa9uLDJCG1ZKI87AWJBvhEOsG9WEhSVcWBtu1A615da2kboiPaRW4hSRcBGEClhg0cTDycWdJR1XgUdkrN2hRqslGapydo+fffgRL37+Ir1opzrrJHZDAiB49vySv/3gp9zcRiqLCpsrjSLrnpQ27KH8/ItXPHz4PtRbRMoTajrBw6Hk4o8vLvjhj/6SH/w/wf/xx//I629/u9fPjkxLIZfVwmLwWBhQqUqgU1NZlCrkQVRwGW9urrl89pRPXr78gw27vHzO9dVVI2cIOYVIGHkrYXVDUQaPvXrFo4tHbFV7dnkjzGT+5BjXwnK/cPHovcRLI9hME3ZeM2+HtRwQAVdXb1ivr6ldzfYC3sSnPFAUZHW+HE7WtqamZL07avrcnYgKKtR6m/VKQTR9n0JQjZj7KqD2LCLY2h4quqsKNUWA5BQPatjAY1hTpuAO2iqlGLV1EQJ8C87vnfOzjz7ixS8+5vf93y+sFeZnl5f89K//htttw1bAW5d05rAK90awjOD//BUPHtynblmInXStyUHJR3jw3sV7/PjpU548eXJArvZ/gv/Fx7/g9bfftug4NfVKa7byd8pN9ZT5I9rFSM/wSPFXrOn5Tby5vubp0x/z8uU/t1Jx5/H9v3b3/q4YGJfPLrl+c0Pde8lgEWxN0znG1jG6e+zfXnHvwQNETdmMINqlSEeZJ1Dvn93j4uJiL+6jv8TQO9L6lya9f/fta26228wodVwZboFU2gLbqbqglZLarzTbdpvBEhWxNJI1bq5uuV6/SRCHt35AyAwPo5aKZzlIHRb5SqTR1nRSnitQtC4phNlyqvlTppRUlmZEQJizhCErbYSa57J8SNkLRm3s7RV54AHymjK9cYjUyg+wqV8XRCtfdzea+IZiFIoSsFKBEm1SE26SpXZCeDh7g9P64R4SrU2ZkC1btea5TMDsqCJ5UfUuZwO1BlnZ6tkgrWWWqjOgqhJmsLWa2dowsKZK0nuKlMWokWWBoBIeiJpZF6CqhtnMdHSHW6PdZLfijjISu2HX11dEjURrTza3BtymzaLV5NZwEGQYW4ekaLdCkXSDRCkidr2n/XKGUlOKjxc6oXZN0H4ZefXrVxQ3atTsjD1lkJpIDNEwlSCRZ53rp4zViNiQtqwEStHT1YoUOaclSY1MmmjXCelNz2Q1T5L/7LPPYDEePXqYNa0ENHnd7xeKKUFiAO2HBM97DZMoS1prMmQLrqCE8uZHIgVDNAFpFEW7BnGKWQtnYJ6GOmL54+99D0JEzfT1alRzikHtda+1/4nsxk/VqQZmlXXzJMUiqFu7nrJMe8v2LhteteuAvEcrVqk1m+Owdn9h7ZYSE6WAIrkjPCVIFua8s0jhWHfhZ5YZZ6rZNxoplZp3clg2uUSKAcmwYpgqUs1iFI5Z4rr3mliq3IVqVDbwM9CGkao1rN1IR6F4xepCEFht1wAhIKjRNH0Dv6ym5lHrEQw8JSlUtapghHJ+qiK13OyZ6yyf/sunSYqyVuPavVVq3bvSgrKxcKVGU7/s1U5ovXz1W5v9ftPVet68cbSehRo65ZNfUuB/AWHLchVUWJtFAAAAAElFTkSuQmCC);
+    display: none;
+  }
+  </style>
+
+  <div id="header"></div>
+  <input id="stacking-distance-slider" max="400" min="1" step="1" type="range"/>
+  
+  <div id="canvas-scroller">
+    <canvas id="canvas"></canvas>
+  </div>
+  <img id="chrome-left"/>
+  <img id="chrome-mid"/>
+  <img id="chrome-right"/>
+</template><template id="tr-ui-e-chrome-cc-layer-tree-quad-stack-view-template">
+  <style>
+  #input-event {
+    background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAMnwAADJ8BPja39wAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAyNSURBVHic7Z1PTCPXHcc/4wWWVbJN2cJSLVqiQJuGpoIGEVWReoBNIlIF5RCRSysOK9EbksUeOHLIIQcULbLEEYk7oqduD6gSRoqUEyK7dCOabOHghCiAE/JntQtesHt4fuM3z2+MZzy2x8ZfaTTjN+Px4/fh9/7Pb6xMJkND4VGk2hloyKkGkJCpASRkagAJmRpAQqYGkJCpASRkaqp2BvzKsizf3w1z38sKc+ZUaQCuAFeB57P7q4AF/Kxsj4GnLrfL+6PDYofQAskCaAJ6gJeB6+QAFOvZpwgwPwOHwCNgN5uu/+H252raJHRALMu6ggDwCtALNAf8E88QUL5AAHqSTVcNUTU4oQBiWVYzMIiA0E3lGhtp4CsEnPtACgFDGqXiYKoKxLKsCPAaMIwojlzV1tZGV1cXHR0ddHR00N7ebh93dHQAcHh4aG/JZNI+3tvb4+jo6LzsPAY+QYA5Ix9KBsoPpmpALMt6BXgTaHe7pre3l5GREUZGRujv7/fdsspkMmxtbRGPx4nH4+zs7BS6/HtgHfgvOW9xeE05bVZxIJZldQNvATf1c5FIhMHBQYaHh7l16xbd3d1lyUMikWBtbY319XU2NzdJp9Omy74B1oAEAoa8yIZTDttVDIhlWZeB94Dfm86Pjo4SjUbLBsFNiUSCWCzG6uqq2yVfAv9CNKHTlNlbKgLEsqxrwF+BX+nnhoaGuHPnDv39/WXPRyFtbW1x9+5dNjY2TKePgBXgOwQUFUyg3lJ2IJZl9QAfAK1qek9PD9PT04yMjJT1970qHo8zPz/P7u6ufuoE+CewQw6Kw2OCsGVZgViW9SdgFNGLBqC1tZWZmRnGx8eJRMI5lJZOp1lZWWFubo7j42P1VAZR4W8gWmJn5KBAAEVYWYBkm7PvIvoWtjo7O1lYWKCvry/w3yyHtre3mZqaYn9/Xz/1EPg3ot+iQslQIpTAgWRh/A0x5GFrYGCAWCxGe7trKzeUSiaTRKNRHjx4oJ/6CvgHoigLDEo5yox30WCMjY2xtLRUczAA2tvbWVpaYmxsTD91E3gbMbTTBFxCFM0WYPntMwXqIdk64x3lM9FolMnJycB+o5paXFwkFovplfcniDrlNLvJXr4vTwnMQ7KtqVE1rZ5gAExOThKNRvXkPyMGQaWXlOQpgQDJ9jM+QGlNjY2N1RUMqcnJSb34shClwnVE8aVCAY9QSi6ysj3wv6N0+gYGBlhaWqKlpaWke4dVqVSK27dv6xX9j8AyYpDyGaL4svsqxdo5CA95DwVGZ2cnsVisbmEAtLS0EIvF6OzsVJNfQIzRlVTJlwQkO1Boj021traysLBQk60pr2pvb2dhYYHWVscAxEuI1pcKJYIHKKV6yFvqh5mZmZrp9AWhvr4+ZmZm9OQ3MAMpSr6BZOcz7CH0np4exsfH/d6uZjU+Pk5Pj6PbdR34LT69xBeQbG/8TTVteno6tGNT5VQkEmF6elpPfh24TK7VFaFIKH4t+BrKTN/Q0FDoRm0rqZGREYaGhtSkXyDqVs9Fl2cg2QUJw2ranTt3vN6m7mSwwR8R68dULzm31eXHQwZRFiSMjo5WfXIpDOrv72d01DFQcQXoQ3hI0V7iB8gr9pcjEdNQwoVVNBrV69EXcanccfEST0Cyi9jsSe/BwcGKz4GHWd3d3QwOOqaAOoDnMFfuRnn1kJfV7wwPD3v8ev1Ls4mF+Ac2FVsW5C8aLxpI9ou/U9Nu3brlOcP1LoNNbuJej+R5ihcPaQJ+Iz/09vY2iiuDuru76e3tVZN+jeiTyFHggsWWFyA9KAufL3K/4zxptrkE3MClYkcDUxQQU3HVAOIug226yHlIXvNXrUe8eEiHPGhra2v0PQqov7+ftrY2NekFzEVWSXWI3Rns6uoq6ZGyepdlWXR1dalJrRTwEFVegFyVB3L5f0Pu0mzUirC1CsPoJcUCuYLyGFkDyPnSbBQhB8VUZNm99nOBZC+8qqZdhBnBUmWw0RXMQHx5iOPpprB5yMbGBp999lm1s+GQwUZXKFBUSRULxOEhYQNy//59Hj58WO1sOOQCpGAfBOoESBhVwENMm61in/cOXRt3f3+f09NTAH766SdaWlrY29sDoLm5mevXr1cze25y9QypYoH8rH44PDwsIU/B6KOPPrLzcXBwQCQS4dNPPwXgxo0bfPzxx9XMnslGJ7h7hkX2GZOaBRKLxezjxcVFLl++zMTERBVz5JTBRseGy3zXIaEDEna5eAgENIX7WP2QTCaL/NrFlcFG0kMKLvIttsh6ilg83ATh85D3338/dGNrmo3SiAXYuvLgeImX9Rj4peHHqq5r165VOwt50mx0gjkqhJT92cvgol2P7O3thSa+VBiVyWTsJnhWsv4wBrZR5QWIjfzo6IitrS0vebxQ2tra0oPdPCbfQ4ze4gXII/VDPB73k9cLIYNtDnACUJ9td8gLkF2UiqkBxF2abc6AJOboD3lQzgWi1BWnCCgA7OzskEgk/Oa5bpVIJPTwT9+RCymoe4jvIkt+8Qs1cW1tzVem61kGm8jiKk1+gIE8eV25+Ihc3CjW19c9fr3+pdkkgwCiwsiL+oDyUKhXIE8QISUA2NzcbBRbihKJBJubm2rSD4h4KLLuOMMQRUiVn9XvdrGVTqcdg3wXXbFYTI9Op3qHuqlQHCoKSNadJNH7KGNbq6urjT4Jou+hRaVLIUoTE4zA6hD5Q5+oCXfv3vVxm/qSwQY7iG6C9BAZByWv6auOevgBIr3ke5mwsbFxofsl8XhcDw34BPgaYXg1KI0p6JlDRQPRiq0zRGQ1W/Pz827RPeta6XSa+fl5Pfl/5LxC3QrCAP9P4WYQcW2/kQm7u7usrKz4vF3tamVlRY/P+CPwLTlvcANiDN/kCYjiJXLv6AXNzc2xvb3t5ZY1re3tbebm5vRk2Vc7JReExgTDqFI8JIMIMvylTDw+PmZqaupCzCgmk0mmpqb0IJkHiLpV9Ypn5MA4oJimMDwD0eqSDCLIsD3WvL+/TzQaJZVKeb11zSiVShGNRvXgmE+Az8kVU8+UrSjvgNKCz8jxmaeIIMNyEoYHDx4wOztbwq3DrdnZWT1W1imi5XmCE0YKlyLLbYLPFxDlZhLKd4ggw/aJe/fusbi46Of2odbi4iL37t1TkzLAfxAzqmc4PcPkIQVVqofIfRrREVpXL4jFYnUFRQbB1PQIMZsqYaSUraiWlaqSQvxlV3rIFd2XEIsm/gL8Qb1ubGyMDz/8sGajzKVSKWZnZ3XPANHs/xxh+BSiyDrObifkirCiiisIDogK5TIwjvY6ijoMpHwEbCJAPCMHQIWhxl4sKmxsEEEwwQmlCQHlbeBV9do6CjX+DbBNDobqHSYYRQfCLDnimKEZfJbN0CpiENLOxf7+PhMTEywvL4d6mCWdTrO8vMzExIQOI4Pod31OPowTzHWHpz80kMjWyqpB6SXSU5oRQYbfARwVSA2+ruIU0ZrSK/ATnEBky8oxqlusnQMLNa4VXRa5Sr4JEYdwDPG8tkM18kKXJ+TmgWQ/Q3qDDsNTJa4r6NjvkA/lEsJTnkdEMX3J9N0Qv/LoAFFEyRaTbFFJGPK4ZBhQntdVgDuUZkTr6w2E1zgUspeC/YjoY3yPczgkZdhk568kGFC+F7qAE4qsU2S90owIpfo6ImCkUVV6bd4TxHzGtzgnmNThEN0rHK0pSngFUtleeeQCRa1XmhHN41eBAcRDka6qwIslU4jRhq/Jn8tQh0HUitttWtb3YvRyv4MKck8MyUeCZRGmeosMGPkiIshNpR72yCCW6hwgFiTI1pE0tDS6abDQ87BIMarEW9rAGUFNNot1MHL/HCIs3k1E8K9LAWfpDDEYepDd5Lopdc5b9Qx9r14nx/EgABhQASCQ109RizAdjApH9vhvIOJNvYCIFyJjhhSjNLlm6WMEgCS5tbbqAjbTlKsKwwTCHmCtmfcY2j/khCL3auwPNXyRGqOwifzQRq2IYk7dwDl8cYwwpjoqrRrSDYYKpdCaqpLrC5Oq8S5c+xCzx+hwTJtbEBdT3aMbUBpVXWvrtsnz+op1CNArVFXlbdEu3mICowJS9+cBsR/Exx2IaQG0af1tHggI1itUVft96vahsi/kOabPxQCRe93IaW3TAVQMhFRVgdiZMIORexOgQiDkXv3DdAObPMYIgAqBkAoFECmtJ+4Gp9Ax2rEORe51w+sQ7OOK17FhAqLKBY567AbBTSY4rsfVsktogagqACfvUpd0tz/SkR4GW9QEEFVBhtAI499ec0DqXf8H8f4X10jf2YAAAAAASUVORK5CYII=);
+    display: none;
+  }
+  </style>
+  <img id="input-event"/>
+</template><template id="tr-ui-e-chrome-cc-picture-debugger-template">
+  <left-panel>
+    <picture-info>
+      <div>
+        <span class="title">Skia Picture</span>
+        <span class="size"></span>
+      </div>
+      <div>
+        <input class="filename" type="text" value="skpicture.skp"/>
+        <button class="export">Export</button>
+      </div>
+    </picture-info>
+  </left-panel>
+  <right-panel>
+    <tr-ui-e-chrome-cc-picture-ops-chart-view>
+    </tr-ui-e-chrome-cc-picture-ops-chart-view>
+    <raster-area><canvas></canvas></raster-area>
+  </right-panel>
+</template><dom-module id="tr-ui-a-stack-frame">
+  <template>
+    <style>
+    :host {
+      display: flex;
+      flex-direction: row;
+      align-items: center;
+      font-size: 12px;
+    }
+    </style>
+    <tr-ui-b-table id="table"></tr-ui-b-table>
+  </template>
+</dom-module><dom-module id="tr-ui-a-single-event-sub-view">
+  <template>
+    <style>
+    :host {
+      display: flex;
+      flex: 0 1;
+      flex-direction: column;
+    }
+    #table {
+      flex: 0 1 auto;
+      align-self: stretch;
+      font-size: 12px;
+    }
+    </style>
+    <tr-ui-b-table id="table">
+    </tr-ui-b-table>
+  </template>
+</dom-module><dom-module id="tr-ui-e-chrome-cc-raster-task-view">
+  <template>
+    <style>
+    :host {
+      display: flex;
+      flex-direction: column;
+    }
+    #heading {
+      flex: 0 0 auto;
+    }
+    tr-ui-b-table {
+      font-size: 12px;
+    }
+    </style>
+
+    <div id="heading">
+      Rasterization costs in
+      <tr-ui-a-analysis-link id="link"></tr-ui-a-analysis-link>
+    </div>
+    <tr-ui-b-table id="content"></tr-ui-b-table>
+  </template>
+</dom-module><dom-module id="tr-ui-e-chrome-codesearch">
+  <template>
+    <style>
+      :host {
+        white-space: nowrap;
+      }
+      #codesearchLink {
+        font-size: x-small;
+        margin-left: 20px;
+        text-decoration: none;
+      }
+    </style>
+    <a id="codesearchLink" on-click="onClick" target="_blank">🔍</a>
+  </template>
+</dom-module><style>
+.tr-ui-e-chrome-gpu-state-snapshot-view{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAAZiS0dEAEwATABMYqp3KAAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB90JCQsBMCH7ZqYAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAUElEQVRYw+3WwQkAIAiF4Vc0hTO5/wiuURvYIcQOv1cRPhDlDXffSsrMsrYiQi/zU80FAACAVX3nt3lWAABA/x+ovnPyAAAA5AHyAAAA3wMOd34Xd+lsglgAAAAASUVORK5CYII=);display:flex;overflow:auto}.tr-ui-e-chrome-gpu-state-snapshot-view img{display:block;margin:16px auto 16px auto}
+</style><dom-module id="tr-ui-a-layout-tree-sub-view">
+  <template>
+    <style>
+    tr-ui-b-table {
+      font-size: 12px;
+    }
+    </style>
+    <div id="content"></div>
+  </template>
+</dom-module><template id="tr-ui-e-img-image-snapshot-view-template">
+  <style>
+    .image-info {
+      margin-bottom: 5px;
+    }
+
+    .image-info .title {
+      font-weight: bold;
+      margin-left: 5px;
+      margin-right: 5px;
+    }
+
+    .image-info .size {
+      margin-right: 5px;
+    }
+
+    .image-container {
+      min-height: 100px;
+      min-width: 200px;
+      overflow: auto;
+    }
+  </style>
+
+  <div class="image-info">
+    <span class="title">Image</span>
+    <span class="size">(unknown)</span>
+    <span class="instructions">
+      [ Drag with mouse to zoom in and out ]
+    </span>
+  </div>
+  <div class="image-container">
+    <img alt="Image snapshot"/>
+  </div>
+</template><dom-module id="tr-ui-e-s-frame-data-side-panel">
+  <template>
+    <style>
+    :host {
+      display: flex;
+      width: 600px;
+      flex-direction: column;
+    }
+    table-container {
+      display: flex;
+      overflow: auto;
+      font-size: 12px;
+    }
+    </style>
+    <div>
+      Organize by:
+      <select id="select">
+        <option value="none">None</option>
+        <option value="tree">Frame Tree</option>
+      </select>
+    </div>
+    <table-container>
+      <tr-ui-b-table id="table"></tr-ui-b-table>
+    </table-container>
+  </template>
+</dom-module><dom-module id="tr-ui-b-chart-legend-key">
+  <template>
+    <style>
+      #checkbox {
+        margin: 0;
+        visibility: hidden;
+        vertical-align: text-top;
+      }
+      #label, #link {
+        white-space: nowrap;
+        text-overflow: ellipsis;
+        overflow: hidden;
+        display: inline-block;
+      }
+    </style>
+
+    <input checked="" id="checkbox" type="checkbox"/>
+    <tr-ui-a-analysis-link id="link"></tr-ui-a-analysis-link>
+    <label id="label"></label>
+  </template>
+</dom-module><template id="chart-base-template">
+  <svg> 
+    <g id="chart-area" xmlns="http://www.w3.org/2000/svg">
+      <g class="x axis"></g>
+      <g class="y axis"></g>
+      <text id="title"></text>
+    </g>
+  </svg>
+</template><dom-module id="tr-ui-e-s-input-latency-side-panel">
+  <template>
+    <style>
+    :host {
+      flex-direction: column;
+      display: flex;
+    }
+    toolbar {
+      flex: 0 0 auto;
+      border-bottom: 1px solid black;
+      display: flex;
+    }
+    result-area {
+      flex: 1 1 auto;
+      display: block;
+      min-height: 0;
+      overflow-y: auto;
+    }
+    </style>
+
+    <toolbar id="toolbar"></toolbar>
+    <result-area id="result_area"></result-area>
+  </template>
+</dom-module><dom-module id="tr-ui-b-heading">
+  <template>
+    <style>
+    :host {
+      background-color: rgb(243, 245, 247);
+      border-right: 1px solid #8e8e8e;
+      display: block;
+      height: 100%;
+      margin: 0;
+      padding: 0 5px 0 0;
+    }
+
+    heading {
+      display: block;
+      overflow-x: hidden;
+      text-align: left;
+      white-space: nowrap;
+    }
+
+    #arrow {
+      flex: 0 0 auto;
+      font-family: sans-serif;
+      margin-left: 5px;
+      margin-right: 5px;
+      width: 8px;
+    }
+
+    #link, #heading_content {
+      display: none;
+    }
+    </style>
+    <heading id="heading" on-click="onHeadingDivClicked_">
+      <span id="arrow"></span>
+      <span id="heading_content"></span>
+      <tr-ui-a-analysis-link id="link"></tr-ui-a-analysis-link>
+    </heading>
+  </template>
+</dom-module><style>
+.track-button{background-color:rgba(255,255,255,0.5);border:1px solid rgba(0,0,0,0.1);color:rgba(0,0,0,0.2);font-size:10px;height:12px;text-align:center;width:12px}.track-button:hover{background-color:rgba(255,255,255,1.0);border:1px solid rgba(0,0,0,0.5);box-shadow:0 0 .05em rgba(0,0,0,0.4);color:rgba(0,0,0,1)}.track-close-button{left:2px;position:absolute;top:2px}.track-collapse-button{left:3px;position:absolute;top:2px}
+</style><style>
+.object-instance-track{height:18px}
+</style><style>
+.tr-ui-e-system-stats-instance-track{height:500px}.tr-ui-e-system-stats-instance-track ul{list-style:none;list-style-position:outside;margin:0;overflow:hidden}
+</style><style>
+.tr-ui-e-system-stats-snapshot-view .subhead{font-size:small;padding-bottom:10px}.tr-ui-e-system-stats-snapshot-view ul{background-position:0 5px;background-repeat:no-repeat;cursor:pointer;font-family:monospace;list-style:none;margin:0;padding-left:15px}.tr-ui-e-system-stats-snapshot-view li{background-position:0 5px;background-repeat:no-repeat;cursor:pointer;list-style:none;margin:0;padding-left:15px}
+</style><dom-module id="tr-ui-e-v8-gc-objects-stats-table">
+  <template>
+    <style>
+    tr-ui-b-table {
+      flex: 0 0 auto;
+      align-self: stretch;
+      margin-top: 1em;
+      font-size: 12px;
+    }
+    .diff {
+      display: inline-block;
+      margin-top: 1em;
+      margin-left: 0.8em;
+    }
+    </style>
+    <div class="diff" id="diffOption">
+      Diff
+    </div>
+    <tr-ui-b-table id="diffTable"></tr-ui-b-table>
+    <tr-ui-b-table id="table"></tr-ui-b-table>
+  </template>
+</dom-module><dom-module id="tr-ui-e-multi-v8-gc-stats-thread-slice-sub-view">
+  <template>
+    <style>
+    </style>
+    <tr-ui-e-v8-gc-objects-stats-table id="gcObjectsStats">
+    </tr-ui-e-v8-gc-objects-stats-table>
+  </template>
+</dom-module><dom-module id="tr-ui-e-v8-ic-stats-table">
+  <template>
+    <style>
+    tr-ui-b-table {
+      flex: 0 0 auto;
+      align-self: stretch;
+      margin-top: 1em;
+      font-size: 12px;
+    }
+    #total {
+      margin-top: 1em;
+      margin-left: 0.8em;
+    }
+    #groupOption {
+      display: inline-block;
+      margin-top: 1em;
+      margin-left: 0.8em;
+    }
+    </style>
+    <div style="padding-right: 200px">
+      <div style="float:right;  border-style: solid; border-width: 1px; padding:20px">
+        X no feedback<br/>
+        0 uninitialized<br/>
+        . premonomorphic<br/>
+        1 monomorphic<br/>
+        ^ recompute handler<br/>
+        P polymorphic<br/>
+        N megamorphic<br/>
+        G generic
+      </div>
+    </div>
+    <div id="total">
+    </div>
+    <div id="groupOption">
+      Group Key
+    </div>
+    <tr-ui-b-table id="table"></tr-ui-b-table>
+  </template>
+</dom-module><dom-module id="tr-ui-e-multi-v8-ic-stats-thread-slice-sub-view">
+  <template>
+    <tr-ui-e-v8-ic-stats-table id="table">
+    </tr-ui-e-v8-ic-stats-table>
+  </template>
+</dom-module><dom-module id="tr-ui-e-v8-runtime-call-stats-table">
+  <template>
+    <style>
+    #table, #blink_rcs_table {
+      flex: 0 0 auto;
+      align-self: stretch;
+      margin-top: 1em;
+      font-size: 12px;
+    }
+
+    #v8_rcs_heading, #blink_rcs_heading {
+        padding-top: 1em;
+        font-size: 18px;
+    }
+    </style>
+    <h1 id="v8_rcs_heading"></h1>
+    <tr-ui-b-table id="table"></tr-ui-b-table>
+    <h1 id="blink_rcs_heading"></h1>
+    <tr-ui-b-table id="blink_rcs_table"></tr-ui-b-table>
+  </template>
+</dom-module><dom-module id="tr-ui-e-multi-v8-thread-slice-sub-view">
+  <template>
+    <tr-ui-a-multi-thread-slice-sub-view id="content"></tr-ui-a-multi-thread-slice-sub-view>
+    <tr-ui-e-v8-runtime-call-stats-table id="runtimeCallStats"></tr-ui-e-v8-runtime-call-stats-table>
+  </template>
+</dom-module><dom-module id="tr-ui-e-single-v8-gc-stats-thread-slice-sub-view">
+  <template>
+    <tr-ui-a-single-event-sub-view id="content"></tr-ui-a-single-event-sub-view>
+    <tr-ui-e-v8-gc-objects-stats-table id="gcObjectsStats"></tr-ui-e-v8-gc-objects-stats-table>
+  </template>
+</dom-module><dom-module id="tr-ui-e-single-v8-ic-stats-thread-slice-sub-view">
+  <template>
+    <tr-ui-e-v8-ic-stats-table id="table">
+    </tr-ui-e-v8-ic-stats-table>
+  </template>
+</dom-module><dom-module id="tr-ui-e-single-v8-thread-slice-sub-view">
+  <template>
+    <tr-ui-a-single-thread-slice-sub-view id="content"></tr-ui-a-single-thread-slice-sub-view>
+    <tr-ui-e-v8-runtime-call-stats-table id="runtimeCallStats"></tr-ui-e-v8-runtime-call-stats-table>
+  </template>
+</dom-module><dom-module id="tr-ui-a-alert-sub-view">
+  <template>
+    <style>
+    :host {
+      display: flex;
+      flex-direction: column;
+    }
+    #table {
+      flex: 1 1 auto;
+      align-self: stretch;
+      font-size: 12px;
+    }
+    </style>
+    <tr-ui-b-table id="table">
+    </tr-ui-b-table>
+  </template>
+</dom-module><dom-module id="tr-ui-b-tab-view">
+  <template>
+    <style>
+      :host {
+        display: flex;
+        flex-direction: column;
+      }
+
+      #selection_description, #tabs {
+        font-size: 12px;
+      }
+
+      #selection_description {
+        display: inline-block;
+        font-weight: bold;
+        margin: 9px 0px 4px 20px;
+      }
+
+      #tabs {
+        flex: 0 0 auto;
+        border-top: 1px solid #8e8e8e;
+        border-bottom: 1px solid #8e8e8e;
+        background-color: #ececec;
+        overflow: hidden;
+        margin: 0;
+      }
+
+      #tabs input[type=radio] {
+        display: none;
+      }
+
+      #tabs tab label {
+        cursor: pointer;
+        display: inline-block;
+        border: 1px solid #ececec;
+        margin: 5px 0px 0px 15px;
+        padding: 3px 10px 3px 10px;
+      }
+
+      #tabs tab label span {
+        font-weight: bold;
+      }
+
+      #tabs:focus input[type=radio]:checked ~ label {
+        outline: dotted 1px #8e8e8e;
+        outline-offset: -2px;
+      }
+
+      #tabs input[type=radio]:checked ~ label {
+        background-color: white;
+        border: 1px solid #8e8e8e;
+        border-bottom: 1px solid white;
+      }
+
+      #subView {
+        flex: 1 1 auto;
+        min-width: 0;
+        display: flex;
+      }
+
+      #subView > * {
+        flex: 1 1 auto;
+        min-width: 0;
+      }
+    </style>
+    <div hidden="[[tabsHidden]]" id="tabs">
+      <label id="selection_description">[[label_]]</label>
+      <template is="dom-repeat" items="[[subViews_]]">
+        <tab>
+          <input checked="[[isChecked_(item)]]" id$="[[computeRadioId_(item)]]" name="tabs" on-change="onTabChanged_" type="radio"/>
+          <label for$="[[computeRadioId_(item)]]">
+            <template if="[[item.tabIcon]]" is="dom-if">
+              <span style$="[[item.tabIcon.style]]">[[item.tabIcon.text]]</span>
+            </template>
+            [[item.tabLabel]]
+          </label>
+        </tab>
+      </template>
+    </div>
+    <div id="subView"></div>
+    <slot>
+    </slot>
+  </template>
+</dom-module><dom-module id="tr-ui-a-memory-dump-heap-details-breakdown-view">
+  <template>
+    <tr-ui-b-tab-view id="tabs"></tr-ui-b-tab-view>
+  </template>
+</dom-module><dom-module id="tr-ui-a-memory-dump-heap-details-breakdown-view-tab">
+  <template>
+    <tr-v-ui-scalar-context-controller></tr-v-ui-scalar-context-controller>
+    <tr-ui-b-info-bar hidden="" id="info"></tr-ui-b-info-bar>
+    <tr-ui-b-table id="table"></tr-ui-b-table>
+  </template>
+</dom-module><dom-module id="tr-ui-a-memory-dump-heap-details-path-view">
+  <template>
+    <style>
+      :host {
+        display: flex;
+        flex-direction: column;
+      }
+    </style>
+    <tr-v-ui-scalar-context-controller></tr-v-ui-scalar-context-controller>
+    <tr-ui-b-table id="table"></tr-ui-b-table>
+  </template>
+</dom-module><dom-module id="tr-ui-a-memory-dump-heap-details-pane">
+  <template>
+    <style>
+      :host {
+        display: flex;
+        flex-direction: column;
+      }
+
+      #header {
+        flex: 0 0 auto;
+        display: flex;
+        flex-direction: row;
+        align-items: center;
+
+        background-color: #eee;
+        border-bottom: 1px solid #8e8e8e;
+        border-top: 1px solid white;
+      }
+
+      #label {
+        flex: 1 1 auto;
+        padding: 8px;
+        font-size: 15px;
+        font-weight: bold;
+      }
+
+      #view_mode_container {
+        display: none;
+        flex: 0 0 auto;
+        padding: 5px;
+        font-size: 15px;
+      }
+
+      #contents {
+        flex: 1 0 auto;
+        align-self: stretch;
+        font-size: 12px;
+      }
+
+      #info_text {
+        padding: 8px;
+        color: #666;
+        font-style: italic;
+        text-align: center;
+      }
+
+      #split_view {
+        display: none;  /* Hide until memory allocator dumps are set. */
+        flex: 1 0 auto;
+        align-self: stretch;
+        flex-direction: row;
+      }
+
+      #path_view {
+        width: 50%;
+      }
+
+      #breakdown_view {
+        flex: 1 1 auto;
+        width: 0;
+      }
+
+      #path_view, #breakdown_view {
+        overflow-x: auto;  /* Show scrollbar if necessary. */
+      }
+    </style>
+    <div id="header">
+      <div id="label">Heap details</div>
+      <div id="view_mode_container">
+        <span>View mode:</span>
+        
+      </div>
+    </div>
+    <div id="contents">
+      <tr-ui-b-info-bar hidden="" id="info_bar">
+      </tr-ui-b-info-bar>
+
+      <div id="info_text">No heap dump selected</div>
+
+      <div id="split_view">
+        <tr-ui-a-memory-dump-heap-details-path-view id="path_view">
+        </tr-ui-a-memory-dump-heap-details-path-view>
+        <tr-ui-b-drag-handle id="drag_handle"></tr-ui-b-drag-handle>
+        <tr-ui-a-memory-dump-heap-details-breakdown-view id="breakdown_view">
+        </tr-ui-a-memory-dump-heap-details-breakdown-view>
+      </div>
+    </div>
+  </template>
+</dom-module><dom-module id="tr-ui-a-memory-dump-allocator-details-pane">
+  <template>
+    <style>
+      :host {
+        display: flex;
+        flex-direction: column;
+      }
+
+      #label {
+        flex: 0 0 auto;
+        padding: 8px;
+
+        background-color: #eee;
+        border-bottom: 1px solid #8e8e8e;
+        border-top: 1px solid white;
+
+        font-size:  15px;
+        font-weight: bold;
+      }
+
+      #contents {
+        flex: 1 0 auto;
+        align-self: stretch;
+        font-size: 12px;
+      }
+
+      #info_text {
+        padding: 8px;
+        color: #666;
+        font-style: italic;
+        text-align: center;
+      }
+
+      #table {
+        display: none;  /* Hide until memory allocator dumps are set. */
+        flex: 1 0 auto;
+        align-self: stretch;
+        font-size: 12px;
+      }
+    </style>
+    <div id="label">Component details</div>
+    <div id="contents">
+      <div id="info_text">No memory allocator dump selected</div>
+      <tr-ui-b-table id="table"></tr-ui-b-table>
+    </div>
+  </template>
+</dom-module><dom-module id="tr-ui-a-memory-dump-vm-regions-details-pane">
+  <template>
+    <style>
+      :host {
+        display: flex;
+        flex-direction: column;
+      }
+
+      #label {
+        flex: 0 0 auto;
+        padding: 8px;
+
+        background-color: #eee;
+        border-bottom: 1px solid #8e8e8e;
+        border-top: 1px solid white;
+
+        font-size:  15px;
+        font-weight: bold;
+      }
+
+      #contents {
+        flex: 1 0 auto;
+        align-self: stretch;
+        font-size: 12px;
+      }
+
+      #info_text {
+        padding: 8px;
+        color: #666;
+        font-style: italic;
+        text-align: center;
+      }
+
+      #table {
+        display: none;  /* Hide until memory dumps are set. */
+        flex: 1 0 auto;
+        align-self: stretch;
+        font-size: 12px;
+      }
+    </style>
+    <div id="label">Memory maps</div>
+    <div id="contents">
+      <div id="info_text">No memory maps selected</div>
+      <tr-ui-b-table id="table"></tr-ui-b-table>
+    </div>
+  </template>
+</dom-module><dom-module id="tr-ui-b-color-legend">
+  <template>
+    <style>
+    :host {
+      display: inline-block;
+    }
+
+    #square {
+      font-size: 150%;  /* Make the square bigger. */
+      line-height: 0%;  /* Prevent the square from increasing legend height. */
+    }
+    </style>
+    <span id="square"></span>
+    <span id="label"></span>
+  </template>
+</dom-module><dom-module id="tr-ui-b-view-specific-brushing-state">
+  <template></template>
+</dom-module><dom-module id="tr-ui-a-memory-dump-overview-pane">
+  <template>
+    <style>
+      :host {
+        display: flex;
+        flex-direction: column;
+      }
+
+      #label {
+        flex: 0 0 auto;
+        padding: 8px;
+
+        background-color: #eee;
+        border-bottom: 1px solid #8e8e8e;
+        border-top: 1px solid white;
+
+        font-size:  15px;
+        font-weight: bold;
+      }
+
+      #label a {
+        font-weight: normal;
+        float: right;
+      }
+
+      #contents {
+        flex: 1 0 auto;
+        align-self: stretch;
+        font-size: 12px;
+        overflow: auto;
+      }
+
+      #info_text {
+        padding: 8px;
+        color: #666;
+        font-style: italic;
+        text-align: center;
+      }
+
+      #table {
+        display: none;  /* Hide until memory dumps are set. */
+        flex: 1 0 auto;
+        align-self: stretch;
+        font-size: 12px;
+      }
+    </style>
+    <tr-ui-b-view-specific-brushing-state id="state" view-id="analysis.memory_dump_overview_pane">
+    </tr-ui-b-view-specific-brushing-state>
+    <div id="label">Overview <a href="https://chromium.googlesource.com/chromium/src/+/master/docs/memory-infra">Help</a></div>
+    <div id="contents">
+      <div id="info_text">No memory memory dumps selected</div>
+      <tr-ui-b-table id="table"></tr-ui-b-table>
+    </div>
+  </template>
+</dom-module><dom-module id="tr-ui-a-memory-dump-header-pane">
+  <template>
+    <style>
+      :host {
+        display: flex;
+        flex-direction: row;
+        align-items: center;
+
+        background-color: #d0d0d0;
+        border-bottom: 1px solid #8e8e8e;
+        border-top: 1px solid white;
+      }
+
+      #label {
+        flex: 1 1 auto;
+        padding: 6px;
+        font-size: 15px;
+      }
+
+      #aggregation_mode_container {
+        display: none;
+        flex: 0 0 auto;
+        padding: 5px;
+        font-size: 15px;
+      }
+    </style>
+    
+    <div id="label"></div>
+    <div id="aggregation_mode_container">
+      <span>Metric aggregation:</span>
+      
+    </div>
+  </template>
+</dom-module><dom-module id="tr-ui-a-stacked-pane-view">
+  <template>
+    <style>
+    :host {
+      display: flex;
+      flex-direction: column;
+    }
+
+    #pane_container > * {
+      flex: 0 0 auto;
+    }
+    </style>
+    <div id="pane_container">
+    </div>
+  </template>
+</dom-module><dom-module id="tr-ui-a-container-memory-dump-sub-view">
+  <template>
+    <style>
+    tr-ui-b-table {
+      font-size: 12px;
+    }
+    </style>
+    <div id="content"></div>
+  </template>
+</dom-module><dom-module id="tr-ui-a-counter-sample-sub-view">
+  <template>
+    <style>
+    :host {
+      display: flex;
+      flex-direction: column;
+    }
+    tr-ui-b-table {
+      font-size: 12px;
+    }
+    </style>
+    <tr-ui-b-table id="table"></tr-ui-b-table>
+  </template>
+</dom-module><dom-module id="tr-ui-a-multi-event-summary-table">
+  <template>
+    <style>
+    :host {
+      display: flex;
+    }
+    #table {
+      flex: 1 1 auto;
+      align-self: stretch;
+      font-size: 12px;
+    }
+    </style>
+    <tr-ui-b-table id="table">
+    </tr-ui-b-table>
+    
+  </template>
+</dom-module><dom-module id="tr-ui-a-selection-summary-table">
+  <template>
+    <style>
+    :host {
+      display: flex;
+    }
+    #table {
+      flex: 1 1 auto;
+      align-self: stretch;
+      font-size: 12px;
+    }
+    </style>
+    <tr-ui-b-table id="table">
+    </tr-ui-b-table>
+    
+  </template>
+</dom-module><dom-module id="tr-ui-b-radio-picker">
+  <template>
+    <style>
+    :host([vertical]) #container {
+      flex-direction: column;
+    }
+    :host(:not[vertical]) #container {
+      flex-direction: row;
+    }
+    #container {
+      display: flex;
+    }
+    #container > div {
+      padding-left: 1em;
+      padding-bottom: 0.5em;
+    }
+    </style>
+    <div id="container"></div>
+  </template>
+</dom-module><dom-module id="tr-v-ui-breakdown-span">
+  <template>
+    <style>
+    :host {
+      display: flex;
+      flex-direction: column;
+    }
+    #table_container {
+      display: flex;
+      flex: 0 0 auto;
+    }
+    #table {
+      max-height: 150px;
+      overflow-y: auto;
+    }
+    </style>
+
+    <div id="empty">(empty)</div>
+    <div id="table_container">
+      <div id="container"></div>
+      <span>
+        <tr-ui-b-table id="table"></tr-ui-b-table>
+      </span>
+    </div>
+  </template>
+</dom-module><dom-module id="tr-v-ui-collected-related-event-set-span">
+</dom-module><dom-module id="tr-v-ui-date-range-span">
+  <template>
+    <content></content>
+  </template>
+</dom-module><dom-module id="tr-v-ui-generic-set-span">
+  <template>
+    <style>
+      a {
+        display: block;
+      }
+    </style>
+
+    <tr-ui-a-generic-object-view id="generic"></tr-ui-a-generic-object-view>
+
+    <div id="links"></div>
+  </template>
+</dom-module><dom-module id="tr-v-ui-related-event-set-span">
+</dom-module><dom-module id="tr-v-ui-scalar-diagnostic-span">
+  <template>
+    <tr-v-ui-scalar-span id="scalar"></tr-v-ui-scalar-span>
+  </template>
+</dom-module><dom-module id="tr-v-ui-unmergeable-diagnostic-set-span">
+</dom-module><dom-module id="tr-v-ui-diagnostic-map-table">
+  <template>
+    <tr-ui-b-table id="table"></tr-ui-b-table>
+  </template>
+</dom-module><dom-module id="tr-v-ui-scalar-map-table">
+  <template>
+    <tr-ui-b-table id="table"></tr-ui-b-table>
+  </template>
+</dom-module><dom-module id="tr-v-ui-histogram-span">
+  <template>
+    <style>
+    #container {
+      display: flex;
+      flex-direction: row;
+      justify-content: space-between;
+    }
+    #chart {
+      flex-grow: 1;
+      display: none;
+    }
+    #drag_handle, #diagnostics_tab_templates {
+      display: none;
+    }
+    #chart svg {
+      display: block;
+    }
+    #stats_container {
+      overflow-y: auto;
+    }
+    </style>
+
+    <div id="container">
+      <div id="chart"></div>
+      <div id="stats_container">
+        <tr-v-ui-scalar-map-table id="stats"></tr-v-ui-scalar-map-table>
+      </div>
+    </div>
+    <tr-ui-b-drag-handle id="drag_handle"></tr-ui-b-drag-handle>
+
+    <tr-ui-b-tab-view id="diagnostics"></tr-ui-b-tab-view>
+
+    <div id="diagnostics_tab_templates">
+      <tr-v-ui-diagnostic-map-table id="metric_diagnostics"></tr-v-ui-diagnostic-map-table>
+
+      <tr-v-ui-diagnostic-map-table id="metadata_diagnostics"></tr-v-ui-diagnostic-map-table>
+
+      <div id="sample_diagnostics_container">
+        <div id="merge_sample_diagnostics_container">
+          <input checked="" id="merge_sample_diagnostics" on-change="updateDiagnostics_" type="checkbox"/>
+          <label for="merge_sample_diagnostics">Merge Sample Diagnostics</label>
+        </div>
+        <tr-v-ui-diagnostic-map-table id="sample_diagnostics"></tr-v-ui-diagnostic-map-table>
+      </div>
+    </div>
+  </template>
+</dom-module><dom-module id="tr-ui-a-multi-event-sub-view">
+  <template>
+    <style>
+    :host {
+      display: flex;
+      overflow: auto;
+    }
+    #content {
+      display: flex;
+      flex-direction: column;
+      flex: 0 1 auto;
+      align-self: stretch;
+    }
+    #content > * {
+      flex: 0 0 auto;
+      align-self: stretch;
+    }
+    #histogramContainer {
+      display: flex;
+    }
+
+    tr-ui-a-multi-event-summary-table {
+      border-bottom: 1px solid #aaa;
+    }
+
+    tr-ui-a-selection-summary-table  {
+      margin-top: 1.25em;
+      border-top: 1px solid #aaa;
+      background-color: #eee;
+      font-weight: bold;
+      margin-bottom: 1.25em;
+      border-bottom: 1px solid #aaa;
+    }
+    </style>
+    <div id="content">
+      <tr-ui-a-multi-event-summary-table id="eventSummaryTable">
+      </tr-ui-a-multi-event-summary-table>
+      <tr-ui-a-selection-summary-table id="selectionSummaryTable">
+      </tr-ui-a-selection-summary-table>
+      <tr-ui-b-radio-picker id="radioPicker">
+      </tr-ui-b-radio-picker>
+      <div id="histogramContainer">
+        <tr-v-ui-histogram-span id="histogramSpan">
+        </tr-v-ui-histogram-span>
+      </div>
+    </div>
+  </template>
+</dom-module><dom-module id="tr-ui-a-related-events">
+  <template>
+    <style>
+    :host {
+      display: flex;
+      flex-direction: column;
+    }
+    #table {
+      flex: 1 1 auto;
+      align-self: stretch;
+      font-size: 12px;
+    }
+    </style>
+    <tr-ui-b-table id="table"></tr-ui-b-table>
+  </template>
+</dom-module><dom-module id="tr-ui-a-multi-async-slice-sub-view">
+  <template>
+    <style>
+    :host {
+      display: flex;
+    }
+    #container {
+      display: flex;
+      flex: 1 1 auto;
+    }
+    #events {
+      margin-left: 8px;
+      flex: 0 1 200px;
+    }
+    </style>
+    <div id="container">
+      <tr-ui-a-multi-event-sub-view id="content"></tr-ui-a-multi-event-sub-view>
+      <div id="events">
+        <tr-ui-a-related-events id="relatedEvents"></tr-ui-a-related-events>
+      </div>
+    </div>
+  </template>
+</dom-module><dom-module id="tr-ui-a-multi-cpu-slice-sub-view">
+  <template>
+    <style>
+    :host {
+      display: flex;
+    }
+    #content {
+      flex: 1 1 auto;
+    }
+    </style>
+    <tr-ui-a-multi-event-sub-view id="content"></tr-ui-a-multi-event-sub-view>
+  </template>
+</dom-module><dom-module id="tr-ui-a-multi-flow-event-sub-view">
+  <template>
+    <style>
+    :host {
+      display: flex;
+    }
+    </style>
+    <tr-ui-a-multi-event-sub-view id="content"></tr-ui-a-multi-event-sub-view>
+  </template>
+</dom-module><dom-module id="tr-ui-a-multi-instant-event-sub-view">
+  <template>
+    <style>
+    :host {
+      display: block;
+    }
+    </style>
+    <div id="content"></div>
+  </template>
+</dom-module><dom-module id="tr-ui-a-multi-object-sub-view">
+  <template>
+    <style>
+    :host {
+      display: flex;
+      font-size: 12px;
+    }
+    </style>
+    <tr-ui-b-table id="content"></tr-ui-b-table>
+  </template>
+</dom-module><dom-module id="tr-ui-a-frame-power-usage-chart">
+  <template>
+    <div id="content"></div>
+  </template>
+</dom-module><dom-module id="tr-ui-a-power-sample-summary-table">
+  <template>
+    <style>
+    tr-ui-b-table {
+      font-size: 12px;
+    }
+    </style>
+    <tr-ui-b-table id="table"></tr-ui-b-table>
+  </template>
+</dom-module><dom-module id="tr-ui-a-multi-power-sample-sub-view">
+  <template>
+    <style>
+    :host {
+      display: flex;
+      flex-direction: row;
+    }
+    #tables {
+      display: flex;
+      flex-direction: column;
+      width: 50%;
+    }
+    #chart {
+      width: 50%;
+    }
+    </style>
+    <div id="tables">
+      <tr-ui-a-power-sample-summary-table id="summaryTable">
+      </tr-ui-a-power-sample-summary-table>
+    </div>
+    <tr-ui-a-frame-power-usage-chart id="chart">
+    </tr-ui-a-frame-power-usage-chart>
+  </template>
+</dom-module><dom-module id="tr-ui-a-multi-sample-sub-view">
+  <template>
+    <style>
+    :host { display: block; }
+    #control {
+      background-color: #e6e6e6;
+      background-image: -webkit-gradient(linear, 0 0, 0 100%,
+                                         from(#E5E5E5), to(#D1D1D1));
+      flex: 0 0 auto;
+      overflow-x: auto;
+    }
+    #control::-webkit-scrollbar { height: 0px; }
+    #control {
+      font-size: 12px;
+      display: flex;
+      flex-direction: row;
+      align-items: stretch;
+      margin: 1px;
+      margin-right: 2px;
+    }
+    tr-ui-b-table {
+      font-size: 12px;
+    }
+    </style>
+    <div id="control">
+      Sample View Option
+    </div>
+    <tr-ui-b-table id="table">
+    </tr-ui-b-table>
+  </template>
+</dom-module><dom-module id="tr-ui-a-multi-thread-slice-sub-view">
+  <template>
+    <style>
+    :host {
+      display: flex;
+    }
+    #content {
+      display: flex;
+      flex: 1 1 auto;
+      min-width: 0;
+    }
+    #content > tr-ui-a-related-events {
+      margin-left: 8px;
+      flex: 0 1 200px;
+    }
+    </style>
+    <div id="content"></div>
+  </template>
+</dom-module><dom-module id="tr-ui-a-multi-thread-time-slice-sub-view">
+  <template>
+    <style>
+    :host {
+      display: flex;
+    }
+    #content {
+      flex: 1 1 auto;
+      min-width: 0;
+    }
+    </style>
+    <tr-ui-a-multi-event-sub-view id="content"></tr-ui-a-multi-event-sub-view>
+  </template>
+</dom-module><dom-module id="tr-ui-a-user-expectation-related-samples-table">
+  <template>
+    <style>
+    #table {
+      flex: 1 1 auto;
+      align-self: stretch;
+      font-size: 12px;
+    }
+    </style>
+    <tr-ui-b-table id="table"></tr-ui-b-table>
+  </template>
+</dom-module><dom-module id="tr-ui-a-multi-user-expectation-sub-view">
+  <template>
+    <style>
+    :host {
+      display: flex;
+      flex: 1 1 auto;
+    }
+    #events {
+      margin-left: 8px;
+      flex: 0 1 200px;
+    }
+    </style>
+    <tr-ui-a-multi-event-sub-view id="realView"></tr-ui-a-multi-event-sub-view>
+    <div id="events">
+      <tr-ui-a-user-expectation-related-samples-table id="relatedSamples"></tr-ui-a-user-expectation-related-samples-table>
+    </div>
+  </template>
+</dom-module><dom-module id="tr-ui-a-single-async-slice-sub-view">
+  <template>
+    <style>
+    :host {
+      display: flex;
+      flex-direction: row;
+    }
+    #events {
+      display:flex;
+      flex-direction: column;
+    }
+    </style>
+    <tr-ui-a-single-event-sub-view id="content"></tr-ui-a-single-event-sub-view>
+    <div id="events">
+      <tr-ui-a-related-events id="relatedEvents"></tr-ui-a-related-events>
+    </div>
+  </template>
+</dom-module><dom-module id="tr-ui-a-single-cpu-slice-sub-view">
+  <template>
+    <style>
+    table {
+      border-collapse: collapse;
+      border-width: 0;
+      margin-bottom: 25px;
+      width: 100%;
+    }
+
+    table tr > td:first-child {
+      padding-left: 2px;
+    }
+
+    table tr > td {
+      padding: 2px 4px 2px 4px;
+      vertical-align: text-top;
+      width: 150px;
+    }
+
+    table td td {
+      padding: 0 0 0 0;
+      width: auto;
+    }
+    tr {
+      vertical-align: top;
+    }
+
+    tr:nth-child(2n+0) {
+      background-color: #e2e2e2;
+    }
+    </style>
+    <table>
+      <tbody><tr>
+        <td>Running process:</td><td id="process-name"></td>
+      </tr>
+      <tr>
+        <td>Running thread:</td><td id="thread-name"></td>
+      </tr>
+      <tr>
+        <td>Start:</td>
+        <td>
+          <tr-v-ui-scalar-span id="start">
+          </tr-v-ui-scalar-span>
+        </td>
+      </tr>
+      <tr>
+        <td>Duration:</td>
+        <td>
+          <tr-v-ui-scalar-span id="duration">
+          </tr-v-ui-scalar-span>
+        </td>
+      </tr>
+      <tr>
+        <td>Active slices:</td><td id="running-thread"></td>
+      </tr>
+      <tr>
+        <td>Args:</td>
+        <td>
+          <tr-ui-a-generic-object-view id="args">
+          </tr-ui-a-generic-object-view>
+        </td>
+      </tr>
+    </tbody></table>
+  </template>
+</dom-module><dom-module id="tr-ui-a-single-flow-event-sub-view">
+  <template>
+    <style>
+    :host {
+      display: block;
+    }
+    </style>
+    <tr-ui-a-single-event-sub-view id="singleEventSubView">
+    </tr-ui-a-single-event-sub-view>
+  </template>
+</dom-module><dom-module id="tr-ui-a-single-frame-sub-view">
+  <template>
+    <style>
+    :host {
+      display: flex;
+      flex-direction: column;
+    }
+    #asv {
+      flex: 0 0 auto;
+      align-self: stretch;
+    }
+    </style>
+    <tr-ui-a-alert-sub-view id="asv">
+    </tr-ui-a-alert-sub-view>
+  </template>
+</dom-module><dom-module id="tr-ui-a-single-instant-event-sub-view">
+  <template>
+    <style>
+    :host {
+      display: block;
+    }
+    </style>
+    <div id="content"></div>
+  </template>
+</dom-module><dom-module id="tr-ui-a-single-object-instance-sub-view">
+  <template>
+    <style>
+    :host {
+      display: block;
+    }
+
+    #snapshots > * {
+      display: block;
+    }
+
+    :host {
+      overflow: auto;
+      display: block;
+    }
+
+    * {
+      -webkit-user-select: text;
+    }
+
+    .title {
+      border-bottom: 1px solid rgb(128, 128, 128);
+      font-size: 110%;
+      font-weight: bold;
+    }
+
+    td, th {
+      font-family: monospace;
+      vertical-align: top;
+    }
+    </style>
+    <div id="content"></div>
+  </template>
+</dom-module><dom-module id="tr-ui-a-single-object-snapshot-sub-view">
+  <template>
+    <style>
+    #args {
+      white-space: pre;
+    }
+
+    :host {
+      overflow: auto;
+      display: flex;
+    }
+
+    ::content * {
+      -webkit-user-select: text;
+    }
+
+    ::content .title {
+      border-bottom: 1px solid rgb(128, 128, 128);
+      font-size: 110%;
+      font-weight: bold;
+    }
+
+    ::content td, th {
+      font-family: monospace;
+      vertical-align: top;
+    }
+    </style>
+    <slot></slot>
+  </template>
+</dom-module><dom-module id="tr-ui-a-power-sample-table">
+  <template>
+    <style>
+    :host {
+      display: flex;
+      font-size: 12px;
+    }
+    </style>
+    <tr-ui-b-table id="table"></tr-ui-b-table>
+  </template>
+</dom-module><dom-module id="tr-ui-a-single-power-sample-sub-view">
+  <template>
+    <style>
+    :host { display: block; }
+    </style>
+    <tr-ui-a-power-sample-table id="samplesTable">
+    </tr-ui-a-power-sample-table>
+  </template>
+</dom-module><dom-module id="tr-ui-a-single-sample-sub-view">
+  <template>
+    <style>
+    :host {
+      display: flex;
+      font-size: 12px;
+    }
+    </style>
+    <tr-ui-b-table id="content"></tr-ui-b-table>
+  </template>
+</dom-module><dom-module id="tr-ui-a-single-thread-slice-sub-view">
+  <template>
+    <style>
+    :host {
+      display: flex;
+      flex-direction: row;
+    }
+    #events {
+      display: flex;
+      flex-direction: column;
+    }
+
+    </style>
+    <tr-ui-a-single-event-sub-view id="content"></tr-ui-a-single-event-sub-view>
+    <div id="events">
+      <tr-ui-a-related-events id="relatedEvents">
+      </tr-ui-a-related-events>
+    </div>
+  </template>
+</dom-module><dom-module id="tr-ui-a-single-thread-time-slice-sub-view">
+  <template>
+    <style>
+    table {
+      border-collapse: collapse;
+      border-width: 0;
+      margin-bottom: 25px;
+      width: 100%;
+    }
+
+    table tr > td:first-child {
+      padding-left: 2px;
+    }
+
+    table tr > td {
+      padding: 2px 4px 2px 4px;
+      vertical-align: text-top;
+      width: 150px;
+    }
+
+    table td td {
+      padding: 0 0 0 0;
+      width: auto;
+    }
+    tr {
+      vertical-align: top;
+    }
+
+    tr:nth-child(2n+0) {
+      background-color: #e2e2e2;
+    }
+    </style>
+    <table>
+      <tbody><tr>
+        <td>Running process:</td><td id="process-name"></td>
+      </tr>
+      <tr>
+        <td>Running thread:</td><td id="thread-name"></td>
+      </tr>
+      <tr>
+        <td>State:</td>
+        <td><b><span id="state"></span></b></td>
+      </tr>
+      <tr>
+        <td>Start:</td>
+        <td>
+          <tr-v-ui-scalar-span id="start">
+          </tr-v-ui-scalar-span>
+        </td>
+      </tr>
+      <tr>
+        <td>Duration:</td>
+        <td>
+          <tr-v-ui-scalar-span id="duration">
+          </tr-v-ui-scalar-span>
+        </td>
+      </tr>
+
+      <tr>
+        <td>On CPU:</td><td id="on-cpu"></td>
+      </tr>
+
+      <tr>
+        <td>Running instead:</td><td id="running-instead"></td>
+      </tr>
+
+      <tr>
+        <td>Args:</td><td id="args"></td>
+      </tr>
+    </tbody></table>
+  </template>
+</dom-module><dom-module id="tr-ui-a-single-user-expectation-sub-view">
+  <template>
+    <style>
+    :host {
+      display: flex;
+      flex-direction: row;
+    }
+    #events {
+      display: flex;
+      flex-direction: column;
+    }
+    </style>
+    <tr-ui-a-single-event-sub-view id="realView"></tr-ui-a-single-event-sub-view>
+    <div id="events">
+      <tr-ui-a-user-expectation-related-samples-table id="relatedSamples"></tr-ui-a-user-expectation-related-samples-table>
+    </div>
+  </template>
+</dom-module><dom-module id="tr-ui-a-analysis-view">
+  <template>
+    <style>
+      :host {
+        background-color: white;
+        display: flex;
+        flex-direction: column;
+        height: 275px;
+        overflow: auto;
+      }
+
+      :host(.tall-mode) {
+        height: 525px;
+      }
+    </style>
+    <slot></slot>
+  </template>
+</dom-module><dom-module id="tr-ui-b-dropdown">
+  <template>
+    <style>
+    button {
+      @apply --dropdown-button;
+    }
+    button.open {
+      @apply --dropdown-button-open;
+    }
+    dialog {
+      position: absolute;
+      margin: 0;
+      padding: 1em;
+      border: 1px solid darkgrey;
+      @apply --dropdown-dialog;
+    }
+    </style>
+
+    <button id="button" on-tap="open">[[label]]</button>
+
+    <dialog id="dialog" on-cancel="close" on-tap="onDialogTap_">
+      <slot></slot>
+    </dialog>
+  </template>
+</dom-module><dom-module id="tr-ui-b-info-bar-group">
+  <template>
+    <style>
+    :host {
+      flex: 0 0 auto;
+      flex-direction: column;
+      display: flex;
+    }
+    </style>
+    <div id="messages"></div>
+  </template>
+</dom-module><dom-module id="tr-ui-b-toolbar-button">
+  <template>
+    <style>
+    :host {
+      display: flex;
+      background-color: #f8f8f8;
+      border: 1px solid rgba(0, 0, 0, 0.5);
+      color: rgba(0,0,0,0.8);
+      justify-content: center;
+      align-self: stretch;
+      min-width: 23px;
+    }
+
+    :host(:hover) {
+      background-color: rgba(255, 255, 255, 1.0);
+      border-color: rgba(0, 0, 0, 0.8);
+      box-shadow: 0 0 .05em rgba(0, 0, 0, 0.4);
+      color: rgba(0, 0, 0, 1);
+    }
+
+    #aligner {
+      display: flex;
+      flex: 0 0 auto;
+      align-self: center;
+    }
+    </style>
+    <div id="aligner">
+      <slot></slot>
+    </div>
+  </template>
+</dom-module><style>
+.drawing-container{display:inline;overflow:auto;overflow-x:hidden;position:relative}.drawing-container-canvas{display:block;pointer-events:none;position:absolute;top:0}
+</style><style>
+.letter-dot-track {
+  height: 18px;
+}
+</style><style>
+.chart-track {
+  height: 30px;
+  position: relative;
+}
+</style><style>
+.cpu-usage-track {
+  height: 90px;
+}
+</style><style>
+.power-series-track {
+  height: 90px;
+}
+</style><style>
+.spacing-track{height:4px}
+</style><style>
+.rect-track{height:18px}
+</style><style>
+.thread-track{flex-direction:column;display:flex;position:relative}
+</style><style>
+.process-track-header{display:flex;flex:0 0 auto;background-image:-webkit-gradient(linear,0 0,100% 0,from(#E5E5E5),to(#D1D1D1));border-bottom:1px solid #8e8e8e;border-top:1px solid white;font-size:75%}.process-track-name{flex-grow:1}.process-track-name:before{content:'\25B8';padding:0 5px}.process-track-base.expanded .process-track-name:before{content:'\25BE'}.process-track-close{color:black;border:1px solid transparent;padding:0px 2px}.process-track-close:hover{border:1px solid grey}
+</style><style>
+.model-track {
+  flex-grow: 1;
+}
+</style><style>
+.x-axis-track {
+  height: 12px;
+}
+
+.x-axis-track.tall-mode {
+  height: 30px;
+}
+</style><dom-module id="tr-ui-timeline-track-view">
+  <template>
+    <style>
+    :host {
+      flex-direction: column;
+      display: flex;
+      position: relative;
+    }
+
+    :host ::content * {
+      -webkit-user-select: none;
+      cursor: default;
+    }
+
+    #drag_box {
+      background-color: rgba(0, 0, 255, 0.25);
+      border: 1px solid rgb(0, 0, 96);
+      font-size: 75%;
+      position: fixed;
+    }
+
+    #hint_text {
+      position: absolute;
+      bottom: 6px;
+      right: 6px;
+      font-size: 8pt;
+    }
+    </style>
+    <slot></slot>
+
+    <div id="drag_box"></div>
+    <div id="hint_text"></div>
+
+    <tv-ui-b-hotkey-controller id="hotkey_controller">
+    </tv-ui-b-hotkey-controller>
+  </template>
+</dom-module><dom-module id="tr-ui-find-control">
+  <template>
+    <style>
+      :host {
+        -webkit-user-select: none;
+        display: flex;
+        position: relative;
+      }
+      input {
+        -webkit-user-select: auto;
+        background-color: #f8f8f8;
+        border: 1px solid rgba(0, 0, 0, 0.5);
+        box-sizing: border-box;
+        margin: 0;
+        padding: 0;
+        width: 170px;
+      }
+      input:focus {
+        background-color: white;
+      }
+      tr-ui-b-toolbar-button {
+        border-left: none;
+        margin: 0;
+      }
+      #hitCount {
+        left: 0;
+        opacity: 0.25;
+        pointer-events: none;
+        position: absolute;
+        text-align: right;
+        top: 2px;
+        width: 167px;
+        z-index: 1;
+      }
+      #spinner {
+        visibility: hidden;
+        width: 8px;
+        height: 8px;
+        left: 154px;
+        pointer-events: none;
+        position: absolute;
+        top: 4px;
+        z-index: 1;
+
+        border: 2px solid transparent;
+        border-bottom: 2px solid rgba(0, 0, 0, 0.5);
+        border-right: 2px solid rgba(0, 0, 0, 0.5);
+        border-radius: 50%;
+      }
+      @keyframes spin { 100% { transform: rotate(360deg); } }
+    </style>
+
+    <input id="filter" on-blur="filterBlur" on-focus="filterFocus" on-input="filterTextChanged" on-keydown="filterKeyDown" on-mouseup="filterMouseUp" type="text"/>
+    <div id="spinner"></div>
+    <tr-ui-b-toolbar-button on-click="findPrevious">
+      ←
+    </tr-ui-b-toolbar-button>
+    <tr-ui-b-toolbar-button on-click="findNext">
+      →
+    </tr-ui-b-toolbar-button>
+    <div id="hitCount">0 of 0</div>
+  </template>
+</dom-module><dom-module id="tr-ui-scripting-control">
+  <template>
+    <style>
+      :host {
+        flex: 1 1 auto;
+      }
+      .root {
+        font-family: monospace;
+        cursor: text;
+
+        padding: 2px;
+        margin: 2px;
+        border: 1px solid rgba(0, 0, 0, 0.5);
+        background: white;
+
+        height: 100px;
+        overflow-y: auto;
+
+        transition-property: opacity, height, padding, margin;
+        transition-duration: .2s;
+        transition-timing-function: ease-out;
+      }
+      .hidden {
+        margin-top: 0px;
+        margin-bottom: 0px;
+        padding-top: 0px;
+        padding-bottom: 0px;
+        height: 0px;
+        opacity: 0;
+      }
+      .focused {
+        outline: auto 5px -webkit-focus-ring-color;
+      }
+      #history {
+        -webkit-user-select: text;
+        color: #777;
+      }
+      #promptContainer {
+        display: flex;
+      }
+      #promptMark {
+        width: 1em;
+        color: #468;
+      }
+      #prompt {
+        flex: 1;
+        width: 100%;
+        border: none !important;
+        background-color: inherit !important;
+        font: inherit !important;
+        text-overflow: clip !important;
+        text-decoration: none !important;
+      }
+      #prompt:focus {
+        outline: none;
+      }
+    </style>
+
+    <div class="root hidden" id="root" on-focus="onConsoleFocus" tabindex="0">
+      <div id="history"></div>
+      <div id="promptContainer">
+        <span id="promptMark">&gt;</span>
+        <input id="prompt" on-blur="onConsoleBlur" on-keydown="promptKeyDown" on-keypress="promptKeyPress" type="text"/>
+       </div>
+    </div>
+  </template>
+</dom-module><dom-module id="tr-ui-side-panel-container">
+  <template>
+    <style>
+    :host {
+      align-items: stretch;
+      display: flex;
+      background-color: white;
+    }
+
+    :host([expanded]) > #side_panel_drag_handle,
+    :host([expanded]) > active-panel-container {
+      flex: 1 1 auto;
+      border-left: 1px solid black;
+      display: flex;
+    }
+
+    :host(:not([expanded])) > #side_panel_drag_handle,
+    :host(:not([expanded])) > active-panel-container {
+      display: none;
+    }
+
+    active-panel-container {
+      display: flex;
+    }
+
+    tab-strip {
+      flex: 0 0 auto;
+      flex-direction: column;
+      -webkit-user-select: none;
+      background-color: rgb(236, 236, 236);
+      border-left: 1px solid black;
+      cursor: default;
+      display: flex;
+      min-width: 18px; /* workaround for flexbox and writing-mode mixing bug */
+      padding: 10px 0 10px 0;
+      font-size: 12px;
+    }
+
+    tab-strip > tab-strip-label {
+      flex-shrink: 0;
+      -webkit-writing-mode: vertical-rl;
+      white-space: nowrap;
+      display: inline;
+      margin-right: 1px;
+      min-height: 20px;
+      padding: 15px 3px 15px 1px;
+    }
+
+    tab-strip >
+        tab-strip-label:not([enabled]) {
+      color: rgb(128, 128, 128);
+    }
+
+    tab-strip > tab-strip-label[selected] {
+      background-color: white;
+      border: 1px solid rgb(163, 163, 163);
+      border-left: none;
+      padding: 14px 2px 14px 1px;
+    }
+
+    #active_panel_container {
+      overflow: auto;
+    }
+    </style>
+
+    <tr-ui-b-drag-handle id="side_panel_drag_handle"></tr-ui-b-drag-handle>
+    <active-panel-container id="active_panel_container">
+    </active-panel-container>
+    <tab-strip id="tab_strip"></tab-strip>
+  </template>
+</dom-module><dom-module id="tr-ui-timeline-view-help-overlay">
+  <template>
+    <style>
+    :host {
+      flex: 1 1 auto;
+      flex-direction: row;
+      display: flex;
+      width: 700px;
+    }
+    .column {
+      width: 50%;
+    }
+    h2 {
+      font-size: 1.2em;
+      margin: 0;
+      margin-top: 5px;
+      text-align: center;
+    }
+    h3 {
+      margin: 0;
+      margin-left: 126px;
+      margin-top: 10px;
+    }
+    .pair {
+      flex: 1 1 auto;
+      flex-direction: row;
+      display: flex;
+    }
+    .command {
+      font-family: monospace;
+      margin-right: 5px;
+      text-align: right;
+      width: 150px;
+    }
+    .action {
+      font-size: 0.9em;
+      text-align: left;
+      width: 200px;
+    }
+    tr-ui-b-mouse-mode-icon {
+      border: 1px solid #888;
+      border-radius: 3px;
+      box-shadow: inset 0 0 2px rgba(0,0,0,0.3);
+      display: inline-block;
+      margin-right: 1px;
+      position: relative;
+      top: 4px;
+    }
+    .mouse-mode-icon.pan-mode {
+      background-position: -1px -11px;
+    }
+    .mouse-mode-icon.select-mode {
+      background-position: -1px -41px;
+    }
+    .mouse-mode-icon.zoom-mode {
+      background-position: -1px -71px;
+    }
+    .mouse-mode-icon.timing-mode {
+      background-position: -1px -101px;
+    }
+    </style>
+    <div class="column left">
+      <h2>Navigation</h2>
+      <div class="pair">
+        <div class="command">w/s</div>
+        <div class="action">Zoom in/out (+shift: faster)</div>
+      </div>
+
+      <div class="pair">
+        <div class="command">a/d</div>
+        <div class="action">Pan left/right (+shift: faster)</div>
+      </div>
+
+      <div class="pair">
+        <div class="command">→/shift-TAB</div>
+        <div class="action">Select previous event</div>
+      </div>
+
+      <div class="pair">
+        <div class="command">←/TAB</div>
+        <div class="action">Select next event</div>
+      </div>
+
+      <h2>Mouse Controls</h2>
+      <div class="pair">
+        <div class="command">click</div>
+        <div class="action">Select event</div>
+      </div>
+      <div class="pair">
+        <div class="command">alt-mousewheel</div>
+        <div class="action">Zoom in/out</div>
+      </div>
+
+      <h3>
+        <tr-ui-b-mouse-mode-icon mode-name="SELECTION"></tr-ui-b-mouse-mode-icon>
+        Select mode
+      </h3>
+      <div class="pair">
+        <div class="command">drag</div>
+        <div class="action">Box select</div>
+      </div>
+
+      <div class="pair">
+        <div class="command"><span class="mod"></span>-click/drag</div>
+        <div class="action">Add events to the current selection</div>
+      </div>
+
+      <div class="pair">
+        <div class="command">double click</div>
+        <div class="action">Select all events with same title</div>
+      </div>
+
+      <h3>
+        <tr-ui-b-mouse-mode-icon mode-name="PANSCAN"></tr-ui-b-mouse-mode-icon>
+        Pan mode
+      </h3>
+      <div class="pair">
+        <div class="command">drag</div>
+        <div class="action">Pan the view</div>
+      </div>
+
+      <h3>
+        <tr-ui-b-mouse-mode-icon mode-name="ZOOM"></tr-ui-b-mouse-mode-icon>
+        Zoom mode
+      </h3>
+      <div class="pair">
+        <div class="command">drag</div>
+        <div class="action">Zoom in/out by dragging up/down</div>
+      </div>
+
+      <h3>
+        <tr-ui-b-mouse-mode-icon mode-name="TIMING"></tr-ui-b-mouse-mode-icon>
+        Timing mode
+      </h3>
+      <div class="pair">
+        <div class="command">drag</div>
+        <div class="action">Create or move markers</div>
+      </div>
+
+      <div class="pair">
+        <div class="command">double click</div>
+        <div class="action">Set marker range to slice</div>
+      </div>
+    </div>
+
+    <div class="column right">
+      <h2>General</h2>
+      <div class="pair">
+        <div class="command">1-4</div>
+        <div class="action">Switch mouse mode</div>
+      </div>
+
+      <div class="pair">
+        <div class="command">shift</div>
+        <div class="action">Hold for temporary select</div>
+      </div>
+
+      <div class="pair">
+        <div class="command">space</div>
+        <div class="action">Hold for temporary pan</div>
+      </div>
+
+      <div class="pair">
+        <div class="command">/</div>
+        <div class="action">Search</div>
+      </div>
+
+      <div class="pair">
+        <div class="command">enter</div>
+        <div class="action">Step through search results</div>
+      </div>
+
+      <div class="pair">
+        <div class="command">f</div>
+        <div class="action">Zoom into selection</div>
+      </div>
+
+      <div class="pair">
+        <div class="command">z/0</div>
+        <div class="action">Reset zoom and pan</div>
+      </div>
+
+      <div class="pair">
+        <div class="command">g/G</div>
+        <div class="action">Toggle 60hz grid</div>
+      </div>
+
+      <div class="pair">
+        <div class="command">v</div>
+        <div class="action">Highlight VSync</div>
+      </div>
+
+      <div class="pair">
+        <div class="command">h</div>
+        <div class="action">Toggle low/high details</div>
+      </div>
+
+      <div class="pair">
+        <div class="command">m</div>
+        <div class="action">Mark current selection</div>
+      </div>
+
+      <div class="pair">
+        <div class="command">p</div>
+        <div class="action">Select power samples over current selection interval</div>
+      </div>
+
+      <div class="pair">
+        <div class="command">`</div>
+        <div class="action">Show or hide the scripting console</div>
+      </div>
+
+      <div class="pair">
+        <div class="command">?</div>
+        <div class="action">Show help</div>
+      </div>
+    </div>
+  </template>
+</dom-module><dom-module id="tr-ui-timeline-view-metadata-overlay">
+  <template>
+    <style>
+    :host {
+      width: 700px;
+
+      overflow: auto;
+    }
+    </style>
+    <tr-ui-b-table id="table"></tr-ui-b-table>
+  </template>
+</dom-module><dom-module id="tr-ui-timeline-view">
+  <template>
+    <style>
+    :host {
+      flex-direction: column;
+      cursor: default;
+      display: flex;
+      font-family: sans-serif;
+      padding: 0;
+    }
+
+    #control {
+      background-color: #e6e6e6;
+      background-image: -webkit-gradient(linear, 0 0, 0 100%,
+          from(#E5E5E5), to(#D1D1D1));
+      flex: 0 0 auto;
+      overflow-x: auto;
+    }
+
+    #control::-webkit-scrollbar { height: 0px; }
+
+    #control > #bar {
+      font-size: 12px;
+      display: flex;
+      flex-direction: row;
+      margin: 1px;
+    }
+
+    #control > #bar > #title {
+      display: flex;
+      align-items: center;
+      padding-left: 8px;
+      padding-right: 8px;
+      flex: 1 1 auto;
+      overflow: hidden;
+      white-space: nowrap;
+    }
+
+    #control > #bar > #left_controls,
+    #control > #bar > #right_controls {
+      display: flex;
+      flex-direction: row;
+      align-items: stretch;
+      flex-shrink: 0;
+    }
+
+    #control > #bar > #left_controls > * { margin-right: 2px; }
+    #control > #bar > #right_controls > * { margin-left: 2px; }
+    #control > #collapsing_controls { display: flex; }
+
+    middle-container {
+      flex: 1 1 auto;
+      flex-direction: row;
+      border-bottom: 1px solid #8e8e8e;
+      display: flex;
+      min-height: 0;
+    }
+
+    middle-container ::content track-view-container {
+      flex: 1 1 auto;
+      display: flex;
+      min-height: 0;
+      min-width: 0;
+      overflow-x: hidden;
+    }
+
+    middle-container ::content track-view-container > * { flex: 1 1 auto; }
+    middle-container > x-timeline-view-side-panel-container { flex: 0 0 auto; }
+    tr-ui-b-drag-handle { flex: 0 0 auto; }
+    tr-ui-a-analysis-view { flex: 0 0 auto; }
+
+    tr-ui-b-dropdown {
+      --dropdown-button: {
+        -webkit-appearance: none;
+        align-items: normal;
+        background-color: rgb(248, 248, 248);
+        border: 1px solid rgba(0, 0, 0, 0.5);
+        box-sizing: content-box;
+        color: rgba(0, 0, 0, 0.8);
+        font-family: sans-serif;
+        font-size: 12px;
+        padding: 2px 5px;
+      }
+    }
+    </style>
+
+    <tv-ui-b-hotkey-controller id="hkc"></tv-ui-b-hotkey-controller>
+    <div id="control">
+      <div id="bar">
+        <div id="left_controls"></div>
+        <div id="title">^_^</div>
+        <div id="right_controls">
+          <tr-ui-b-dropdown id="flow_event_filter_dropdown" label="Flow events"></tr-ui-b-dropdown>
+          <tr-ui-b-dropdown id="process_filter_dropdown" label="Processes"></tr-ui-b-dropdown>
+          <tr-ui-b-toolbar-button id="view_metadata_button">
+            M
+          </tr-ui-b-toolbar-button>
+          <tr-ui-b-dropdown id="view_options_dropdown" label="View Options"></tr-ui-b-dropdown>
+          <tr-ui-find-control id="view_find_control"></tr-ui-find-control>
+          <tr-ui-b-toolbar-button id="view_console_button">
+            »
+          </tr-ui-b-toolbar-button>
+          <tr-ui-b-toolbar-button id="view_help_button">
+            ?
+          </tr-ui-b-toolbar-button>
+        </div>
+      </div>
+      <div id="collapsing_controls"></div>
+      <tr-ui-b-info-bar-group id="import-warnings">
+      </tr-ui-b-info-bar-group>
+      <tr-ui-b-info-bar-group id="polyfill-warning">
+      </tr-ui-b-info-bar-group>
+    </div>
+    <middle-container>
+      <slot></slot>
+
+      <tr-ui-side-panel-container id="side_panel_container">
+      </tr-ui-side-panel-container>
+    </middle-container>
+    <tr-ui-b-drag-handle id="drag_handle"></tr-ui-b-drag-handle>
+    <tr-ui-a-analysis-view id="analysis"></tr-ui-a-analysis-view>
+
+    <tr-v-ui-preferred-display-unit id="display_unit">
+    </tr-v-ui-preferred-display-unit>
+  </template>
+</dom-module><dom-module id="tr-ui-b-grouping-table">
+  <template>
+    <style>
+    :host {
+      display: flex;
+    }
+    #table {
+      flex: 1 1 auto;
+      font-size: 12px;
+    }
+    </style>
+    <tr-ui-b-table id="table"></tr-ui-b-table>
+  </template>
+</dom-module><dom-module id="tr-ui-b-grouping-table-groupby-picker">
+  <template>
+    <style>
+    #container {
+      display: flex;
+    }
+    #container *:not(:first-child) {
+      padding-left: 3px;
+      border-left: 1px solid black;
+      margin-left: 3px;
+    }
+    </style>
+
+    <div id="container"></div>
+  </template>
+</dom-module><dom-module id="tr-ui-b-grouping-table-groupby-picker-group">
+  <template>
+    <style>
+    :host {
+      white-space: nowrap;
+    }
+    #left, #right {
+      user-select: none;
+      cursor: pointer;
+    }
+    </style>
+
+    <span id="left" on-click="moveLeft_">◀</span>
+    <input id="enabled" on-change="onEnableChanged_" type="checkbox"/>
+    <label for="enabled" id="label"></label>
+    <span id="right" on-click="moveRight_">▶</span>
+  </template>
+</dom-module><dom-module id="tr-ui-sp-file-size-stats-side-panel">
+  <template>
+    <style>
+    :host {
+      display: flex;
+      flex-direction: column;
+    }
+    toolbar {
+      align-items: center;
+      background-color: rgb(236, 236, 236);
+      border-bottom: 1px solid #8e8e8e;
+      display: flex;
+      flex-direction: row;
+      flex-direction: row;
+      flex: 0 0 auto;
+      font-size: 12px;
+      padding: 0 10px 0 10px;
+    }
+    table-container {
+      display: flex;
+      min-height: 0px;
+      overflow-y: auto;
+    }
+    </style>
+
+    <toolbar>
+      <span><b>Group by:</b></span>
+      <tr-ui-b-grouping-table-groupby-picker id="picker">
+      </tr-ui-b-grouping-table-groupby-picker>
+    </toolbar>
+    <table-container>
+      <tr-ui-b-grouping-table id="table"></tr-ui-b-grouping-table>
+    </table-container>
+  </template>
+</dom-module><dom-module id="tr-v-ui-histogram-set-controls-export">
+  <template>
+    <style>
+    :host {
+      display: grid;
+      grid-gap: 1em;
+      grid-template-rows: auto auto;
+      grid-template-columns: auto auto;
+    }
+    button {
+      -webkit-appearance: none;
+      border: 0;
+      font-size: initial;
+      padding: 5px;
+    }
+    </style>
+
+    <button on-tap="exportRawCsv_">raw CSV</button>
+    <button on-tap="exportRawJson_">raw JSON</button>
+    <button on-tap="exportMergedCsv_">merged CSV</button>
+    <button on-tap="exportMergedJson_">merged JSON</button>
+  </template>
+</dom-module><dom-module id="tr-v-ui-histogram-set-controls">
+  <template>
+    <style>
+    :host {
+      display: block;
+    }
+
+    #help, #feedback {
+      display: none;
+      margin-left: 20px;
+    }
+
+    #search_container {
+      display: inline-flex;
+      margin-right: 20px;
+      padding-bottom: 1px;
+      border-bottom: 1px solid darkgrey;
+    }
+
+    #search {
+      border: 0;
+      max-width: 20em;
+      outline: none;
+    }
+
+    #clear_search {
+      visibility: hidden;
+      height: 1em;
+      stroke: black;
+      stroke-width: 16;
+    }
+
+    #controls {
+      white-space: nowrap;
+    }
+
+    #show_overview, #hide_overview {
+      height: 1em;
+      margin-right: 20px;
+    }
+
+    #show_overview {
+      stroke: blue;
+      stroke-width: 16;
+    }
+
+    #show_overview:hover {
+      background: blue;
+      stroke: white;
+    }
+
+    #hide_overview {
+      display: none;
+      stroke-width: 18;
+      stroke: black;
+    }
+
+    #hide_overview:hover {
+      background: black;
+      stroke: white;
+    }
+
+    #reference_display_label {
+      display: none;
+      margin-right: 20px;
+    }
+
+    #alpha, #alpha_slider_container {
+      display: none;
+    }
+
+    #alpha {
+      margin-right: 20px;
+    }
+
+    #alpha_slider_container {
+      background: white;
+      border: 1px solid black;
+      flex-direction: column;
+      padding: 0.5em;
+      position: absolute;
+      z-index: 10; /* scalar-span uses z-index :-( */
+    }
+
+    #alpha_slider {
+      -webkit-appearance: slider-vertical;
+      align-self: center;
+      height: 200px;
+      width: 30px;
+    }
+
+    #statistic {
+      display: none;
+      margin-right: 20px;
+    }
+
+    #show_visualization {
+      margin-right: 20px;
+    }
+
+    #export {
+      margin-right: 20px;
+    }
+    </style>
+
+    <div id="controls">
+      <span id="search_container">
+        <input id="search" placeholder="Find Histogram name" value="{{searchQuery::keyup}}"/>
+        <svg id="clear_search" on-tap="clearSearch_" viewBox="0 0 128 128">
+          <g>
+          <title>Clear search</title>
+          <line x1="28" x2="100" y1="28" y2="100"></line>
+          <line x1="28" x2="100" y1="100" y2="28"></line>
+          </g>
+        </svg>
+      </span>
+
+      <svg id="show_overview" on-tap="toggleOverviewLineCharts_" viewBox="0 0 128 128">
+        <g>
+        <title>Show overview charts</title>
+        <line x1="19" x2="49" y1="109" y2="49"></line>
+        <line x1="49" x2="79" y1="49" y2="79"></line>
+        <line x1="79" x2="109" y1="79" y2="19"></line>
+        </g>
+      </svg>
+      <svg id="hide_overview" on-tap="toggleOverviewLineCharts_" viewBox="0 0 128 128">
+        <g>
+        <title>Hide overview charts</title>
+        <line x1="28" x2="100" y1="28" y2="100"></line>
+        <line x1="28" x2="100" y1="100" y2="28"></line>
+        </g>
+      </svg>
+
+      <select id="reference_display_label" value="{{referenceDisplayLabel::change}}">
+        <option value="">Select a reference column</option>
+      </select>
+
+      <button id="alpha" on-tap="openAlphaSlider_">α=[[alphaString]]</button>
+      <div id="alpha_slider_container">
+        <input id="alpha_slider" max="18" min="0" on-blur="closeAlphaSlider_" on-input="updateAlpha_" type="range" value="{{alphaIndex::change}}"/>
+      </div>
+
+      <select id="statistic" value="{{displayStatisticName::change}}">
+      </select>
+
+      <button id="show_visualization" on-tap="loadVisualization_">Visualize</button>
+
+      <tr-ui-b-dropdown label="Export">
+        <tr-v-ui-histogram-set-controls-export>
+        </tr-v-ui-histogram-set-controls-export>
+      </tr-ui-b-dropdown>
+
+      <input checked="{{showAll::change}}" id="show_all" title="When unchecked, less important histograms are hidden." type="checkbox"/>
+      <label for="show_all" title="When unchecked, less important histograms are hidden.">Show all</label>
+
+      <a id="help">Help</a>
+      <a id="feedback">Feedback</a>
+    </div>
+
+    <tr-ui-b-grouping-table-groupby-picker id="picker">
+    </tr-ui-b-grouping-table-groupby-picker>
+  </template>
+</dom-module><dom-module id="tr-v-ui-histogram-set-table-cell">
+  <template>
+    <style>
+    #histogram_container {
+      display: flex;
+      flex-direction: row;
+    }
+
+    #missing, #empty, #unmergeable, #scalar {
+      flex-grow: 1;
+    }
+
+    #open_histogram, #close_histogram, #open_histogram svg, #close_histogram svg {
+      height: 1em;
+    }
+
+    #open_histogram svg {
+      margin-left: 4px;
+      stroke-width: 0;
+      stroke: blue;
+      fill: blue;
+    }
+    :host(:hover) #open_histogram svg {
+      background: blue;
+      stroke: white;
+      fill: white;
+    }
+
+    #scalar {
+      flex-grow: 1;
+      white-space: nowrap;
+    }
+
+    #histogram {
+      flex-grow: 1;
+    }
+
+    #close_histogram svg line {
+      stroke-width: 18;
+      stroke: black;
+    }
+    #close_histogram:hover svg {
+      background: black;
+    }
+    #close_histogram:hover svg line {
+      stroke: white;
+    }
+
+    #overview_container {
+      display: none;
+    }
+    </style>
+
+    <div id="histogram_container">
+      <span id="missing">(missing)</span>
+      <span id="empty">(empty)</span>
+      <span id="unmergeable">(unmergeable)</span>
+
+      <tr-v-ui-scalar-span id="scalar" on-click="openHistogram_"></tr-v-ui-scalar-span>
+
+      <span id="open_histogram" on-click="openHistogram_">
+        <svg viewBox="0 0 128 128">
+          <rect height="16" width="32" x="16" y="24"></rect>
+          <rect height="16" width="96" x="16" y="56"></rect>
+          <rect height="16" width="64" x="16" y="88"></rect>
+        </svg>
+      </span>
+
+      <span id="histogram"></span>
+
+      <span id="close_histogram" on-click="closeHistogram_">
+        <svg viewBox="0 0 128 128">
+          <line x1="28" x2="100" y1="28" y2="100"></line>
+          <line x1="28" x2="100" y1="100" y2="28"></line>
+        </svg>
+      </span>
+    </div>
+
+    <div id="overview_container">
+    </div>
+  </template>
+</dom-module><dom-module id="tr-v-ui-histogram-set-table-name-cell">
+  <template>
+    <style>
+    #name_container {
+      display: flex;
+    }
+
+    #name {
+      overflow: hidden;
+      white-space: nowrap;
+      text-overflow: ellipsis;
+    }
+
+    #show_overview, #hide_overview, #show_overview svg, #hide_overview svg {
+      height: 1em;
+      margin-left: 5px;
+    }
+
+    #show_overview svg {
+      stroke: blue;
+      stroke-width: 16;
+    }
+
+    #show_overview:hover svg {
+      background: blue;
+      stroke: white;
+    }
+
+    #hide_overview {
+      display: none;
+    }
+
+    #hide_overview svg {
+      stroke-width: 18;
+      stroke: black;
+    }
+
+    #hide_overview:hover svg {
+      background: black;
+      stroke: white;
+    }
+
+    #open_histograms, #close_histograms, #open_histograms svg, #close_histograms svg {
+      height: 1em;
+    }
+
+    #close_histograms {
+      display: none;
+    }
+
+    #open_histograms svg {
+      margin-left: 4px;
+      stroke-width: 0;
+      stroke: blue;
+      fill: blue;
+    }
+    #open_histograms:hover svg {
+      background: blue;
+      stroke: white;
+      fill: white;
+    }
+
+    #close_histograms line {
+      stroke-width: 18;
+      stroke: black;
+    }
+    #close_histograms:hover {
+      background: black;
+    }
+    #close_histograms:hover line {
+      stroke: white;
+    }
+
+    #overview_container {
+      display: none;
+    }
+    </style>
+
+    <div id="name_container">
+      <span id="name"></span>
+
+      <span id="show_overview" on-click="showOverview_">
+        <svg viewBox="0 0 128 128">
+          <line x1="19" x2="49" y1="109" y2="49"></line>
+          <line x1="49" x2="79" y1="49" y2="79"></line>
+          <line x1="79" x2="109" y1="79" y2="19"></line>
+        </svg>
+      </span>
+
+      <span id="hide_overview" on-click="hideOverview_">
+        <svg viewBox="0 0 128 128">
+          <line x1="28" x2="100" y1="28" y2="100"></line>
+          <line x1="28" x2="100" y1="100" y2="28"></line>
+        </svg>
+      </span>
+
+      <span id="open_histograms" on-click="openHistograms_">
+        <svg viewBox="0 0 128 128">
+          <rect height="16" width="32" x="16" y="24"></rect>
+          <rect height="16" width="96" x="16" y="56"></rect>
+          <rect height="16" width="64" x="16" y="88"></rect>
+        </svg>
+      </span>
+
+      <span id="close_histograms" on-click="closeHistograms_">
+        <svg viewBox="0 0 128 128">
+          <line x1="28" x2="100" y1="28" y2="100"></line>
+          <line x1="28" x2="100" y1="100" y2="28"></line>
+        </svg>
+      </span>
+    </div>
+
+    <div id="overview_container">
+    </div>
+  </template>
+</dom-module><dom-module id="tr-v-ui-histogram-set-table">
+  <template>
+    <style>
+    :host {
+      min-height: 0px;
+      overflow: auto;
+    }
+    #table {
+      margin-top: 5px;
+    }
+    </style>
+
+    <tr-ui-b-table id="table">
+  </tr-ui-b-table></template>
+</dom-module><dom-module id="tr-v-ui-metrics-visualization">
+  <template>
+    <style>
+      button {
+        padding: 5px;
+        font-size: 14px;
+      }
+
+      .text_input {
+        width: 50px;
+        padding: 4px;
+        font-size: 14px;
+      }
+
+      .error {
+        color: red;
+        display: none;
+      }
+
+      .container {
+        position: relative;
+        display: inline-block;
+        margin-left: 15px;
+      }
+
+      #title {
+        font-size: 20px;
+        font-weight: bold;
+        padding-bottom: 5px;
+      }
+
+      #selectors {
+        display: block;
+        padding-bottom: 10px;
+      }
+
+      #search_page {
+        width: 200px;
+        margin-left: 30px;
+      }
+
+      #close {
+        display: none;
+        vertical-align: top;
+      }
+
+      #close svg{
+        height: 1em;
+      }
+
+      #close svg line {
+        stroke-width: 18;
+        stroke: black;
+      }
+
+      #close:hover svg {
+        background: black;
+      }
+
+      #close:hover svg line {
+        stroke: white;
+      }
+    </style>
+      <span class="container" id="aggregateContainer">
+      </span>
+      <span class="container" id="pageByPageContainer">
+        <span id="selectors">
+          <span id="percentile_label">Percentile Range:</span>
+          <input class="text_input" id="start" placeholder="0"/>
+          <input class="text_input" id="end" placeholder="100"/>
+          <button id="filter" on-tap="filterByPercentile_">Filter</button>
+          <input class="text_input" id="search_page" placeholder="Page Name"/>
+          <button id="search" on-tap="searchByPage_">Search</button>
+          <span class="error" id="search_error">Sorry, could not find that page!</span>
+        </span>
+      </span>
+      <div display="block" id="submetricsContainer">
+        <span id="close">
+          <svg viewBox="0 0 128 128">
+            <line x1="28" x2="100" y1="28" y2="100"></line>
+            <line x1="28" x2="100" y1="100" y2="28"></line>
+          </svg>
+        </span>
+      </div>
+  </template>
+</dom-module><dom-module id="tr-v-ui-raster-visualization">
+  <template>
+    <style>
+      button {
+        padding: 5px;
+        font-size: 14px;
+      }
+      .error {
+        color: red;
+        display: none;
+      }
+
+      .text_input {
+        width: 200px;
+        padding: 4px;
+        font-size: 14px;
+      }
+
+      .selector_container{
+        padding: 5px;
+      }
+
+      #search {
+        display: inline-block;
+        padding-bottom: 10px;
+      }
+
+      #search_page {
+        width: 200px;
+      }
+
+      #pageSelector {
+        display: inline-block;
+        font-size: 12pt;
+      }
+
+      #close {
+        display: none;
+        vertical-align: top;
+      }
+
+      #close svg{
+        height: 1em;
+      }
+
+      #close svg line {
+        stroke-width: 18;
+        stroke: black;
+      }
+
+      #close:hover svg {
+        background: black;
+      }
+
+      #close:hover svg line {
+        stroke: white;
+      }
+    </style>
+    <span id="aggregateContainer">
+      <div>
+        <div class="selector_container">
+          <span id="select_page_label">Individual Page Results:</span>
+          <select id="pageSelector">
+            <option id="select_page" value="">Select a page</option>
+          </select>
+        </div>
+        <div class="selector_container">
+          <div id="search_page_label">Search for a page:</div>
+          <input class="text_input" id="search_page" placeholder="Page Name"/>
+          <button id="search_button">Search</button>
+          <div class="error" id="search_error">Sorry, could not find that page!</div>
+        </div>
+      </div>
+    </span>
+    <span id="pageContainer">
+      <span id="close">
+          <svg viewBox="0 0 128 128">
+            <line x1="28" x2="100" y1="28" y2="100"></line>
+            <line x1="28" x2="100" y1="100" y2="28"></line>
+          </svg>
+        </span>
+      </span>
+  </template>
+</dom-module><meta charset="utf-8"/><dom-module id="tr-v-ui-visualizations-data-container">
+  <template>
+    <style>
+      .error {
+        color: red;
+        display: none;
+      }
+
+      .sample{
+        display: none;
+      }
+
+      .subtitle{
+        font-size: 20px;
+        font-weight: bold;
+        padding-bottom: 5px;
+      }
+
+      .description{
+        font-size: 15px;
+        padding-bottom: 5px;
+      }
+
+      #title {
+        font-size: 30px;
+        font-weight: bold;
+        padding-bottom: 5px;
+      }
+    </style>
+    <div id="title">Visualizations</div>
+    <div class="error" id="data_error">Invalid data provided.</div>
+    <div id="pipeline_per_frame_container">
+      <div class="subtitle">Graphics Pipeline and Raster Tasks</div>
+      <div class="description">
+        When raster tasks are completed in comparison to the rest of the graphics pipeline.<br/>
+        Only pages where raster tasks are completed after beginFrame is issued are included.
+      </div>
+      <tr-v-ui-raster-visualization id="rasterVisualization">
+      </tr-v-ui-raster-visualization>
+    </div>
+    <div id="metrics_container">
+      <div class="subtitle">Metrics</div>
+      <div class="description">Total amount of time taken for the indicated metrics.</div>
+      <tr-v-ui-metrics-visualization class="sample" id="metricsVisualization">
+      </tr-v-ui-metrics-visualization>
+    </div>
+  </template>
+</dom-module><dom-module id="tr-v-ui-histogram-set-view">
+  <template>
+    <style>
+    :host {
+      font-family: sans-serif;
+    }
+
+    #zero {
+      color: red;
+      /* histogram-set-table is used by both metrics-side-panel and results.html.
+       * This font-size rule has no effect in results.html, but improves
+       * legibility in the metrics-side-panel, which sets font-size in order to
+       * make this table denser.
+       */
+      font-size: initial;
+    }
+
+    #container {
+      display: none;
+    }
+
+    #visualizations{
+      display: none;
+    }
+    </style>
+
+    <div id="zero">zero Histograms</div>
+
+    <div id="container">
+      <tr-v-ui-histogram-set-controls id="controls">
+      </tr-v-ui-histogram-set-controls>
+
+      <tr-v-ui-visualizations-data-container id="visualizations">
+      </tr-v-ui-visualizations-data-container>
+
+      <tr-v-ui-histogram-set-table id="table"></tr-v-ui-histogram-set-table>
+    </div>
+  </template>
+</dom-module><dom-module id="tr-ui-sp-metrics-side-panel">
+  <template>
+    <style>
+    :host {
+      display: flex;
+      flex-direction: column;
+    }
+    div#error {
+      color: red;
+    }
+    #results {
+      font-size: 12px;
+    }
+    </style>
+
+    <top-left-controls id="top_left_controls"></top-left-controls>
+
+    <tr-v-ui-histogram-set-view id="results"></tr-v-ui-histogram-set-view>
+
+    <div id="error"></div>
+  </template>
+</dom-module><dom-module id="tr-ui-e-s-alerts-side-panel">
+  <template>
+    <style>
+    :host {
+      display: block;
+      width: 250px;
+    }
+    #content {
+      flex-direction: column;
+      display: flex;
+    }
+    tr-ui-b-table {
+      font-size: 12px;
+    }
+    </style>
+
+    <div id="content">
+      <toolbar id="toolbar"></toolbar>
+      <result-area id="result_area"></result-area>
+    </div>
+  </template>
+</dom-module><script>
+
+// Copyright 2015 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.
+
+/* WARNING: This file is auto generated.
+ *
+ * Do not edit directly.
+ */
+
+'use strict';if(!window.CustomElements||window.CustomElements.hasNative){if(window.Polymer){throw new Error('Cannot proceed. Polymer already present.');}
+window.Polymer={};window.Polymer.dom='shadow';}
+(function(){function resolve(){document.body.removeAttribute('unresolved');}
+if(window.WebComponents){addEventListener('WebComponentsReady',resolve);}else{if(document.readyState==='interactive'||document.readyState==='complete'){resolve();}else{addEventListener('DOMContentLoaded',resolve);}}}());window.Polymer={Settings:function(){var settings=window.Polymer||{};if(!settings.noUrlSettings){var parts=location.search.slice(1).split('&');for(var i=0,o;i<parts.length&&(o=parts[i]);i++){o=o.split('=');o[0]&&(settings[o[0]]=o[1]||true);}}
+settings.wantShadow=settings.dom==='shadow';settings.hasShadow=Boolean(Element.prototype.createShadowRoot);settings.nativeShadow=settings.hasShadow&&!window.ShadowDOMPolyfill;settings.useShadow=settings.wantShadow&&settings.hasShadow;settings.hasNativeImports=Boolean('import'in document.createElement('link'));settings.useNativeImports=settings.hasNativeImports;settings.useNativeCustomElements=!window.CustomElements||window.CustomElements.useNative;settings.useNativeShadow=settings.useShadow&&settings.nativeShadow;settings.usePolyfillProto=!settings.useNativeCustomElements&&!Object.__proto__;settings.hasNativeCSSProperties=!navigator.userAgent.match(/AppleWebKit\/601|Edge\/15/)&&window.CSS&&CSS.supports&&CSS.supports('box-shadow','0 0 0 var(--foo)');settings.useNativeCSSProperties=settings.hasNativeCSSProperties&&settings.lazyRegister&&settings.useNativeCSSProperties;settings.isIE=navigator.userAgent.match('Trident');settings.passiveTouchGestures=settings.passiveTouchGestures||false;return settings;}()};(function(){var userPolymer=window.Polymer;window.Polymer=function(prototype){if(typeof prototype==='function'){prototype=prototype.prototype;}
+if(!prototype){prototype={};}
+prototype=desugar(prototype);var customCtor=prototype===prototype.constructor.prototype?prototype.constructor:null;var options={prototype:prototype};if(prototype.extends){options.extends=prototype.extends;}
+Polymer.telemetry._registrate(prototype);var ctor=document.registerElement(prototype.is,options);return customCtor||ctor;};var desugar=function(prototype){var base=Polymer.Base;if(prototype.extends){base=Polymer.Base._getExtendedPrototype(prototype.extends);}
+prototype=Polymer.Base.chainObject(prototype,base);prototype.registerCallback();return prototype;};if(userPolymer){for(var i in userPolymer){Polymer[i]=userPolymer[i];}}
+Polymer.Class=function(prototype){if(!prototype.factoryImpl){prototype.factoryImpl=function(){};}
+return desugar(prototype).constructor;};}());Polymer.telemetry={registrations:[],_regLog:function(prototype){console.log('['+prototype.is+']: registered');},_registrate:function(prototype){this.registrations.push(prototype);Polymer.log&&this._regLog(prototype);},dumpRegistrations:function(){this.registrations.forEach(this._regLog);}};Object.defineProperty(window,'currentImport',{enumerable:true,configurable:true,get:function(){return(document._currentScript||document.currentScript||{}).ownerDocument;}});Polymer.RenderStatus={_ready:false,_callbacks:[],whenReady:function(cb){if(this._ready){cb();}else{this._callbacks.push(cb);}},_makeReady:function(){this._ready=true;for(var i=0;i<this._callbacks.length;i++){this._callbacks[i]();}
+this._callbacks=[];},_catchFirstRender:function(){requestAnimationFrame(function(){Polymer.RenderStatus._makeReady();});},_afterNextRenderQueue:[],_waitingNextRender:false,afterNextRender:function(element,fn,args){this._watchNextRender();this._afterNextRenderQueue.push([element,fn,args]);},hasRendered:function(){return this._ready;},_watchNextRender:function(){if(!this._waitingNextRender){this._waitingNextRender=true;var fn=function(){Polymer.RenderStatus._flushNextRender();};if(!this._ready){this.whenReady(fn);}else{requestAnimationFrame(fn);}}},_flushNextRender:function(){var self=this;setTimeout(function(){self._flushRenderCallbacks(self._afterNextRenderQueue);self._afterNextRenderQueue=[];self._waitingNextRender=false;});},_flushRenderCallbacks:function(callbacks){for(var i=0,h;i<callbacks.length;i++){h=callbacks[i];h[1].apply(h[0],h[2]||Polymer.nar);}}};if(window.HTMLImports){HTMLImports.whenReady(function(){Polymer.RenderStatus._catchFirstRender();});}else{Polymer.RenderStatus._catchFirstRender();}
+Polymer.ImportStatus=Polymer.RenderStatus;Polymer.ImportStatus.whenLoaded=Polymer.ImportStatus.whenReady;(function(){'use strict';var settings=Polymer.Settings;Polymer.Base={__isPolymerInstance__:true,_addFeature:function(feature){this.mixin(this,feature);},registerCallback:function(){if(settings.lazyRegister==='max'){if(this.beforeRegister){this.beforeRegister();}}else{this._desugarBehaviors();for(var i=0,b;i<this.behaviors.length;i++){b=this.behaviors[i];if(b.beforeRegister){b.beforeRegister.call(this);}}
+if(this.beforeRegister){this.beforeRegister();}}
+this._registerFeatures();if(!settings.lazyRegister){this.ensureRegisterFinished();}},createdCallback:function(){if(settings.disableUpgradeEnabled){if(this.hasAttribute('disable-upgrade')){this._propertySetter=disableUpgradePropertySetter;this._configValue=null;this.__data__={};return;}else{this.__hasInitialized=true;}}
+this.__initialize();},__initialize:function(){if(!this.__hasRegisterFinished){this._ensureRegisterFinished(this.__proto__);}
+Polymer.telemetry.instanceCount++;this.root=this;for(var i=0,b;i<this.behaviors.length;i++){b=this.behaviors[i];if(b.created){b.created.call(this);}}
+if(this.created){this.created();}
+this._initFeatures();},ensureRegisterFinished:function(){this._ensureRegisterFinished(this);},_ensureRegisterFinished:function(proto){if(proto.__hasRegisterFinished!==proto.is||!proto.is){if(settings.lazyRegister==='max'){proto._desugarBehaviors();for(var i=0,b;i<proto.behaviors.length;i++){b=proto.behaviors[i];if(b.beforeRegister){b.beforeRegister.call(proto);}}}
+proto.__hasRegisterFinished=proto.is;if(proto._finishRegisterFeatures){proto._finishRegisterFeatures();}
+for(var j=0,pb;j<proto.behaviors.length;j++){pb=proto.behaviors[j];if(pb.registered){pb.registered.call(proto);}}
+if(proto.registered){proto.registered();}
+if(settings.usePolyfillProto&&proto!==this){proto.extend(this,proto);}}},attachedCallback:function(){var self=this;Polymer.RenderStatus.whenReady(function(){self.isAttached=true;for(var i=0,b;i<self.behaviors.length;i++){b=self.behaviors[i];if(b.attached){b.attached.call(self);}}
+if(self.attached){self.attached();}});},detachedCallback:function(){var self=this;Polymer.RenderStatus.whenReady(function(){self.isAttached=false;for(var i=0,b;i<self.behaviors.length;i++){b=self.behaviors[i];if(b.detached){b.detached.call(self);}}
+if(self.detached){self.detached();}});},attributeChangedCallback:function(name,oldValue,newValue){this._attributeChangedImpl(name);for(var i=0,b;i<this.behaviors.length;i++){b=this.behaviors[i];if(b.attributeChanged){b.attributeChanged.call(this,name,oldValue,newValue);}}
+if(this.attributeChanged){this.attributeChanged(name,oldValue,newValue);}},_attributeChangedImpl:function(name){this._setAttributeToProperty(this,name);},extend:function(target,source){if(target&&source){var n$=Object.getOwnPropertyNames(source);for(var i=0,n;i<n$.length&&(n=n$[i]);i++){this.copyOwnProperty(n,source,target);}}
+return target||source;},mixin:function(target,source){for(var i in source){target[i]=source[i];}
+return target;},copyOwnProperty:function(name,source,target){var pd=Object.getOwnPropertyDescriptor(source,name);if(pd){Object.defineProperty(target,name,pd);}},_logger:function(level,args){if(args.length===1&&Array.isArray(args[0])){args=args[0];}
+switch(level){case'log':case'warn':case'error':console[level].apply(console,args);break;}},_log:function(){var args=Array.prototype.slice.call(arguments,0);this._logger('log',args);},_warn:function(){var args=Array.prototype.slice.call(arguments,0);this._logger('warn',args);},_error:function(){var args=Array.prototype.slice.call(arguments,0);this._logger('error',args);},_logf:function(){return this._logPrefix.concat(this.is).concat(Array.prototype.slice.call(arguments,0));}};Polymer.Base._logPrefix=function(){var color=window.chrome&&!/edge/i.test(navigator.userAgent)||/firefox/i.test(navigator.userAgent);return color?['%c[%s::%s]:','font-weight: bold; background-color:#EEEE00;']:['[%s::%s]:'];}();Polymer.Base.chainObject=function(object,inherited){if(object&&inherited&&object!==inherited){if(!Object.__proto__){object=Polymer.Base.extend(Object.create(inherited),object);}
+object.__proto__=inherited;}
+return object;};Polymer.Base=Polymer.Base.chainObject(Polymer.Base,HTMLElement.prototype);Polymer.BaseDescriptors={};var disableUpgradePropertySetter;if(settings.disableUpgradeEnabled){disableUpgradePropertySetter=function(property,value){this.__data__[property]=value;};var origAttributeChangedCallback=Polymer.Base.attributeChangedCallback;Polymer.Base.attributeChangedCallback=function(name,oldValue,newValue){if(!this.__hasInitialized&&name==='disable-upgrade'){this.__hasInitialized=true;this._propertySetter=Polymer.Bind._modelApi._propertySetter;this._configValue=Polymer.Base._configValue;this.__initialize();}
+origAttributeChangedCallback.call(this,name,oldValue,newValue);};}
+if(window.CustomElements){Polymer.instanceof=CustomElements.instanceof;}else{Polymer.instanceof=function(obj,ctor){return obj instanceof ctor;};}
+Polymer.isInstance=function(obj){return Boolean(obj&&obj.__isPolymerInstance__);};Polymer.telemetry.instanceCount=0;}());(function(){var modules={};var lcModules={};var findModule=function(id){return modules[id]||lcModules[id.toLowerCase()];};var DomModule=function(){return document.createElement('dom-module');};DomModule.prototype=Object.create(HTMLElement.prototype);Polymer.Base.mixin(DomModule.prototype,{createdCallback:function(){this.register();},register:function(id){id=id||this.id||this.getAttribute('name')||this.getAttribute('is');if(id){this.id=id;modules[id]=this;lcModules[id.toLowerCase()]=this;}},import:function(id,selector){if(id){var m=findModule(id);if(!m){forceDomModulesUpgrade();m=findModule(id);}
+if(m&&selector){m=m.querySelector(selector);}
+return m;}}});Object.defineProperty(DomModule.prototype,'constructor',{value:DomModule,configurable:true,writable:true});var cePolyfill=window.CustomElements&&!CustomElements.useNative;document.registerElement('dom-module',DomModule);function forceDomModulesUpgrade(){if(cePolyfill){var script=document._currentScript||document.currentScript;var doc=script&&script.ownerDocument||document;var modules=doc.querySelectorAll('dom-module');for(var i=modules.length-1,m;i>=0&&(m=modules[i]);i--){if(m.__upgraded__){return;}else{CustomElements.upgrade(m);}}}}}());Polymer.Base._addFeature({_prepIs:function(){if(!this.is){var module=(document._currentScript||document.currentScript).parentNode;if(module.localName==='dom-module'){var id=module.id||module.getAttribute('name')||module.getAttribute('is');this.is=id;}}
+if(this.is){this.is=this.is.toLowerCase();}}});Polymer.Base._addFeature({behaviors:[],_desugarBehaviors:function(){if(this.behaviors.length){this.behaviors=this._desugarSomeBehaviors(this.behaviors);}},_desugarSomeBehaviors:function(behaviors){var behaviorSet=[];behaviors=this._flattenBehaviorsList(behaviors);for(var i=behaviors.length-1;i>=0;i--){var b=behaviors[i];if(behaviorSet.indexOf(b)===-1){this._mixinBehavior(b);behaviorSet.unshift(b);}}
+return behaviorSet;},_flattenBehaviorsList:function(behaviors){var flat=[];for(var i=0;i<behaviors.length;i++){var b=behaviors[i];if(b instanceof Array){flat=flat.concat(this._flattenBehaviorsList(b));}else if(b){flat.push(b);}else{this._warn(this._logf('_flattenBehaviorsList','behavior is null, check for missing or 404 import'));}}
+return flat;},_mixinBehavior:function(b){var n$=Object.getOwnPropertyNames(b);var useAssignment=b._noAccessors;for(var i=0,n;i<n$.length&&(n=n$[i]);i++){if(!Polymer.Base._behaviorProperties[n]&&!this.hasOwnProperty(n)){if(useAssignment){this[n]=b[n];}else{this.copyOwnProperty(n,b,this);}}}},_prepBehaviors:function(){this._prepFlattenedBehaviors(this.behaviors);},_prepFlattenedBehaviors:function(behaviors){for(var i=0,l=behaviors.length;i<l;i++){this._prepBehavior(behaviors[i]);}
+this._prepBehavior(this);},_marshalBehaviors:function(){for(var i=0;i<this.behaviors.length;i++){this._marshalBehavior(this.behaviors[i]);}
+this._marshalBehavior(this);}});Polymer.Base._behaviorProperties={hostAttributes:true,beforeRegister:true,registered:true,properties:true,observers:true,listeners:true,created:true,attached:true,detached:true,attributeChanged:true,ready:true,_noAccessors:true};Polymer.Base._addFeature({_getExtendedPrototype:function(tag){return this._getExtendedNativePrototype(tag);},_nativePrototypes:{},_getExtendedNativePrototype:function(tag){var p=this._nativePrototypes[tag];if(!p){p=Object.create(this.getNativePrototype(tag));var p$=Object.getOwnPropertyNames(Polymer.Base);for(var i=0,n;i<p$.length&&(n=p$[i]);i++){if(!Polymer.BaseDescriptors[n]){p[n]=Polymer.Base[n];}}
+Object.defineProperties(p,Polymer.BaseDescriptors);this._nativePrototypes[tag]=p;}
+return p;},getNativePrototype:function(tag){return Object.getPrototypeOf(document.createElement(tag));}});Polymer.Base._addFeature({_prepConstructor:function(){this._factoryArgs=this.extends?[this.extends,this.is]:[this.is];var ctor=function(){return this._factory(arguments);};if(this.hasOwnProperty('extends')){ctor.extends=this.extends;}
+Object.defineProperty(this,'constructor',{value:ctor,writable:true,configurable:true});ctor.prototype=this;},_factory:function(args){var elt=document.createElement.apply(document,this._factoryArgs);if(this.factoryImpl){this.factoryImpl.apply(elt,args);}
+return elt;}});Polymer.nob=Object.create(null);Polymer.Base._addFeature({getPropertyInfo:function(property){var info=this._getPropertyInfo(property,this.properties);if(!info){for(var i=0;i<this.behaviors.length;i++){info=this._getPropertyInfo(property,this.behaviors[i].properties);if(info){return info;}}}
+return info||Polymer.nob;},_getPropertyInfo:function(property,properties){var p=properties&&properties[property];if(typeof p==='function'){p=properties[property]={type:p};}
+if(p){p.defined=true;}
+return p;},_prepPropertyInfo:function(){this._propertyInfo={};for(var i=0;i<this.behaviors.length;i++){this._addPropertyInfo(this._propertyInfo,this.behaviors[i].properties);}
+this._addPropertyInfo(this._propertyInfo,this.properties);this._addPropertyInfo(this._propertyInfo,this._propertyEffects);},_addPropertyInfo:function(target,source){if(source){var t,s;for(var i in source){t=target[i];s=source[i];if(i[0]==='_'&&!s.readOnly){continue;}
+if(!target[i]){target[i]={type:typeof s==='function'?s:s.type,readOnly:s.readOnly,attribute:Polymer.CaseMap.camelToDashCase(i)};}else{if(!t.type){t.type=s.type;}
+if(!t.readOnly){t.readOnly=s.readOnly;}}}}}});(function(){var propertiesDesc={configurable:true,writable:true,enumerable:true,value:{}};Polymer.BaseDescriptors.properties=propertiesDesc;Object.defineProperty(Polymer.Base,'properties',propertiesDesc);}());Polymer.CaseMap={_caseMap:{},_rx:{dashToCamel:/-[a-z]/g,camelToDash:/([A-Z])/g},dashToCamelCase:function(dash){return this._caseMap[dash]||(this._caseMap[dash]=dash.indexOf('-')<0?dash:dash.replace(this._rx.dashToCamel,function(m){return m[1].toUpperCase();}));},camelToDashCase:function(camel){return this._caseMap[camel]||(this._caseMap[camel]=camel.replace(this._rx.camelToDash,'-$1').toLowerCase());}};Polymer.Base._addFeature({_addHostAttributes:function(attributes){if(!this._aggregatedAttributes){this._aggregatedAttributes={};}
+if(attributes){this.mixin(this._aggregatedAttributes,attributes);}},_marshalHostAttributes:function(){if(this._aggregatedAttributes){this._applyAttributes(this,this._aggregatedAttributes);}},_applyAttributes:function(node,attr$){for(var n in attr$){if(!this.hasAttribute(n)&&n!=='class'){var v=attr$[n];this.serializeValueToAttribute(v,n,this);}}},_marshalAttributes:function(){this._takeAttributesToModel(this);},_takeAttributesToModel:function(model){if(this.hasAttributes()){for(var i in this._propertyInfo){var info=this._propertyInfo[i];if(this.hasAttribute(info.attribute)){this._setAttributeToProperty(model,info.attribute,i,info);}}}},_setAttributeToProperty:function(model,attribute,property,info){if(!this._serializing){property=property||Polymer.CaseMap.dashToCamelCase(attribute);info=info||this._propertyInfo&&this._propertyInfo[property];if(info&&!info.readOnly){var v=this.getAttribute(attribute);model[property]=this.deserialize(v,info.type);}}},_serializing:false,reflectPropertyToAttribute:function(property,attribute,value){this._serializing=true;value=value===undefined?this[property]:value;this.serializeValueToAttribute(value,attribute||Polymer.CaseMap.camelToDashCase(property));this._serializing=false;},serializeValueToAttribute:function(value,attribute,node){var str=this.serialize(value);node=node||this;if(str===undefined){node.removeAttribute(attribute);}else{node.setAttribute(attribute,str);}},deserialize:function(value,type){switch(type){case Number:value=Number(value);break;case Boolean:value=value!=null;break;case Object:try{value=JSON.parse(value);}catch(x){}
+break;case Array:try{value=JSON.parse(value);}catch(x){value=null;console.warn('Polymer::Attributes: couldn`t decode Array as JSON');}
+break;case Date:value=new Date(value);break;case String:default:break;}
+return value;},serialize:function(value){switch(typeof value){case'boolean':return value?'':undefined;case'object':if(value instanceof Date){return value.toString();}else if(value){try{return JSON.stringify(value);}catch(x){return'';}}
+default:return value!=null?value:undefined;}}});Polymer.version="1.11.3";Polymer.Base._addFeature({_registerFeatures:function(){this._prepIs();this._prepBehaviors();this._prepConstructor();this._prepPropertyInfo();},_prepBehavior:function(b){this._addHostAttributes(b.hostAttributes);},_marshalBehavior:function(b){},_initFeatures:function(){this._marshalHostAttributes();this._marshalBehaviors();}});(function(){function resolveCss(cssText,ownerDocument){return cssText.replace(CSS_URL_RX,function(m,pre,url,post){return pre+'\''+resolve(url.replace(/["']/g,''),ownerDocument)+'\''+post;});}
+function resolveAttrs(element,ownerDocument){for(var name in URL_ATTRS){var a$=URL_ATTRS[name];for(var i=0,l=a$.length,a,at,v;i<l&&(a=a$[i]);i++){if(name==='*'||element.localName===name){at=element.attributes[a];v=at&&at.value;if(v&&v.search(BINDING_RX)<0){at.value=a==='style'?resolveCss(v,ownerDocument):resolve(v,ownerDocument);}}}}}
+function resolve(url,ownerDocument){if(url&&ABS_URL.test(url)){return url;}
+var resolver=getUrlResolver(ownerDocument);resolver.href=url;return resolver.href||url;}
+var tempDoc;var tempDocBase;function resolveUrl(url,baseUri){if(!tempDoc){tempDoc=document.implementation.createHTMLDocument('temp');tempDocBase=tempDoc.createElement('base');tempDoc.head.appendChild(tempDocBase);}
+tempDocBase.href=baseUri;return resolve(url,tempDoc);}
+function getUrlResolver(ownerDocument){return ownerDocument.body.__urlResolver||(ownerDocument.body.__urlResolver=ownerDocument.createElement('a'));}
+function pathFromUrl(url){return url.substring(0,url.lastIndexOf('/')+1);}
+var CSS_URL_RX=/(url\()([^)]*)(\))/g;var URL_ATTRS={'*':['href','src','style','url'],form:['action']};var ABS_URL=/(^\/)|(^#)|(^[\w-\d]*:)/;var BINDING_RX=/\{\{|\[\[/;Polymer.ResolveUrl={resolveCss:resolveCss,resolveAttrs:resolveAttrs,resolveUrl:resolveUrl,pathFromUrl:pathFromUrl};Polymer.rootPath=Polymer.Settings.rootPath||pathFromUrl(document.baseURI||window.location.href);}());Polymer.Base._addFeature({_prepTemplate:function(){var module;if(this._template===undefined){module=Polymer.DomModule.import(this.is);this._template=module&&module.querySelector('template');}
+if(module){var assetPath=module.getAttribute('assetpath')||'';var importURL=Polymer.ResolveUrl.resolveUrl(assetPath,module.ownerDocument.baseURI);this._importPath=Polymer.ResolveUrl.pathFromUrl(importURL);}else{this._importPath='';}
+if(this._template&&this._template.hasAttribute('is')){this._warn(this._logf('_prepTemplate','top-level Polymer template '+'must not be a type-extension, found',this._template,'Move inside simple <template>.'));}
+if(this._template&&!this._template.content&&window.HTMLTemplateElement&&HTMLTemplateElement.decorate){HTMLTemplateElement.decorate(this._template);}},_stampTemplate:function(){if(this._template){this.root=this.instanceTemplate(this._template);}},instanceTemplate:function(template){var dom=document.importNode(template._content||template.content,true);return dom;}});(function(){var baseAttachedCallback=Polymer.Base.attachedCallback;var baseDetachedCallback=Polymer.Base.detachedCallback;Polymer.Base._addFeature({_hostStack:[],ready:function(){},_registerHost:function(host){this.dataHost=host=host||Polymer.Base._hostStack[Polymer.Base._hostStack.length-1];if(host&&host._clients){host._clients.push(this);}
+this._clients=null;this._clientsReadied=false;},_beginHosting:function(){Polymer.Base._hostStack.push(this);if(!this._clients){this._clients=[];}},_endHosting:function(){Polymer.Base._hostStack.pop();},_tryReady:function(){this._readied=false;if(this._canReady()){this._ready();}},_canReady:function(){return!this.dataHost||this.dataHost._clientsReadied;},_ready:function(){this._beforeClientsReady();if(this._template){this._setupRoot();this._readyClients();}
+this._clientsReadied=true;this._clients=null;this._afterClientsReady();this._readySelf();},_readyClients:function(){this._beginDistribute();var c$=this._clients;if(c$){for(var i=0,l=c$.length,c;i<l&&(c=c$[i]);i++){c._ready();}}
+this._finishDistribute();},_readySelf:function(){for(var i=0,b;i<this.behaviors.length;i++){b=this.behaviors[i];if(b.ready){b.ready.call(this);}}
+if(this.ready){this.ready();}
+this._readied=true;if(this._attachedPending){this._attachedPending=false;this.attachedCallback();}},_beforeClientsReady:function(){},_afterClientsReady:function(){},_beforeAttached:function(){},attachedCallback:function(){if(this._readied){this._beforeAttached();baseAttachedCallback.call(this);}else{this._attachedPending=true;}},detachedCallback:function(){if(this._readied){baseDetachedCallback.call(this);}else{this._attachedPending=false;}}});}());Polymer.ArraySplice=function(){function newSplice(index,removed,addedCount){return{index:index,removed:removed,addedCount:addedCount};}
+var EDIT_LEAVE=0;var EDIT_UPDATE=1;var EDIT_ADD=2;var EDIT_DELETE=3;function ArraySplice(){}
+ArraySplice.prototype={calcEditDistances:function(current,currentStart,currentEnd,old,oldStart,oldEnd){var rowCount=oldEnd-oldStart+1;var columnCount=currentEnd-currentStart+1;var distances=new Array(rowCount);for(var i=0;i<rowCount;i++){distances[i]=new Array(columnCount);distances[i][0]=i;}
+for(var j=0;j<columnCount;j++)
+distances[0][j]=j;for(i=1;i<rowCount;i++){for(j=1;j<columnCount;j++){if(this.equals(current[currentStart+j-1],old[oldStart+i-1]))
+distances[i][j]=distances[i-1][j-1];else{var north=distances[i-1][j]+1;var west=distances[i][j-1]+1;distances[i][j]=north<west?north:west;}}}
+return distances;},spliceOperationsFromEditDistances:function(distances){var i=distances.length-1;var j=distances[0].length-1;var current=distances[i][j];var edits=[];while(i>0||j>0){if(i==0){edits.push(EDIT_ADD);j--;continue;}
+if(j==0){edits.push(EDIT_DELETE);i--;continue;}
+var northWest=distances[i-1][j-1];var west=distances[i-1][j];var north=distances[i][j-1];var min;if(west<north)
+min=west<northWest?west:northWest;else
+min=north<northWest?north:northWest;if(min==northWest){if(northWest==current){edits.push(EDIT_LEAVE);}else{edits.push(EDIT_UPDATE);current=northWest;}
+i--;j--;}else if(min==west){edits.push(EDIT_DELETE);i--;current=west;}else{edits.push(EDIT_ADD);j--;current=north;}}
+edits.reverse();return edits;},calcSplices:function(current,currentStart,currentEnd,old,oldStart,oldEnd){var prefixCount=0;var suffixCount=0;var minLength=Math.min(currentEnd-currentStart,oldEnd-oldStart);if(currentStart==0&&oldStart==0)
+prefixCount=this.sharedPrefix(current,old,minLength);if(currentEnd==current.length&&oldEnd==old.length)
+suffixCount=this.sharedSuffix(current,old,minLength-prefixCount);currentStart+=prefixCount;oldStart+=prefixCount;currentEnd-=suffixCount;oldEnd-=suffixCount;if(currentEnd-currentStart==0&&oldEnd-oldStart==0)
+return[];if(currentStart==currentEnd){var splice=newSplice(currentStart,[],0);while(oldStart<oldEnd)
+splice.removed.push(old[oldStart++]);return[splice];}else if(oldStart==oldEnd)
+return[newSplice(currentStart,[],currentEnd-currentStart)];var ops=this.spliceOperationsFromEditDistances(this.calcEditDistances(current,currentStart,currentEnd,old,oldStart,oldEnd));splice=undefined;var splices=[];var index=currentStart;var oldIndex=oldStart;for(var i=0;i<ops.length;i++){switch(ops[i]){case EDIT_LEAVE:if(splice){splices.push(splice);splice=undefined;}
+index++;oldIndex++;break;case EDIT_UPDATE:if(!splice)
+splice=newSplice(index,[],0);splice.addedCount++;index++;splice.removed.push(old[oldIndex]);oldIndex++;break;case EDIT_ADD:if(!splice)
+splice=newSplice(index,[],0);splice.addedCount++;index++;break;case EDIT_DELETE:if(!splice)
+splice=newSplice(index,[],0);splice.removed.push(old[oldIndex]);oldIndex++;break;}}
+if(splice){splices.push(splice);}
+return splices;},sharedPrefix:function(current,old,searchLength){for(var i=0;i<searchLength;i++)
+if(!this.equals(current[i],old[i]))
+return i;return searchLength;},sharedSuffix:function(current,old,searchLength){var index1=current.length;var index2=old.length;var count=0;while(count<searchLength&&this.equals(current[--index1],old[--index2]))
+count++;return count;},calculateSplices:function(current,previous){return this.calcSplices(current,0,current.length,previous,0,previous.length);},equals:function(currentValue,previousValue){return currentValue===previousValue;}};return new ArraySplice();}();Polymer.domInnerHTML=function(){var escapeAttrRegExp=/[&\u00A0"]/g;var escapeDataRegExp=/[&\u00A0<>]/g;function escapeReplace(c){switch(c){case'&':return'&amp;';case'<':return'&lt;';case'>':return'&gt;';case'"':return'&quot;';case'\xA0':return'&nbsp;';}}
+function escapeAttr(s){return s.replace(escapeAttrRegExp,escapeReplace);}
+function escapeData(s){return s.replace(escapeDataRegExp,escapeReplace);}
+function makeSet(arr){var set={};for(var i=0;i<arr.length;i++){set[arr[i]]=true;}
+return set;}
+var voidElements=makeSet(['area','base','br','col','command','embed','hr','img','input','keygen','link','meta','param','source','track','wbr']);var plaintextParents=makeSet(['style','script','xmp','iframe','noembed','noframes','plaintext','noscript']);function getOuterHTML(node,parentNode,composed){switch(node.nodeType){case Node.ELEMENT_NODE:var tagName=node.localName;var s='<'+tagName;var attrs=node.attributes;for(var i=0,attr;attr=attrs[i];i++){s+=' '+attr.name+'="'+escapeAttr(attr.value)+'"';}
+s+='>';if(voidElements[tagName]){return s;}
+return s+getInnerHTML(node,composed)+'</'+tagName+'>';case Node.TEXT_NODE:var data=node.data;if(parentNode&&plaintextParents[parentNode.localName]){return data;}
+return escapeData(data);case Node.COMMENT_NODE:return'<!--'+node.data+'-->';default:console.error(node);throw new Error('not implemented');}}
+function getInnerHTML(node,composed){if(node instanceof HTMLTemplateElement)
+node=node.content;var s='';var c$=Polymer.dom(node).childNodes;for(var i=0,l=c$.length,child;i<l&&(child=c$[i]);i++){s+=getOuterHTML(child,node,composed);}
+return s;}
+return{getInnerHTML:getInnerHTML};}();(function(){'use strict';var nativeInsertBefore=Element.prototype.insertBefore;var nativeAppendChild=Element.prototype.appendChild;var nativeRemoveChild=Element.prototype.removeChild;Polymer.TreeApi={arrayCopyChildNodes:function(parent){var copy=[],i=0;for(var n=parent.firstChild;n;n=n.nextSibling){copy[i++]=n;}
+return copy;},arrayCopyChildren:function(parent){var copy=[],i=0;for(var n=parent.firstElementChild;n;n=n.nextElementSibling){copy[i++]=n;}
+return copy;},arrayCopy:function(a$){var l=a$.length;var copy=new Array(l);for(var i=0;i<l;i++){copy[i]=a$[i];}
+return copy;}};Polymer.TreeApi.Logical={hasParentNode:function(node){return Boolean(node.__dom&&node.__dom.parentNode);},hasChildNodes:function(node){return Boolean(node.__dom&&node.__dom.childNodes!==undefined);},getChildNodes:function(node){return this.hasChildNodes(node)?this._getChildNodes(node):node.childNodes;},_getChildNodes:function(node){if(!node.__dom.childNodes){node.__dom.childNodes=[];for(var n=node.__dom.firstChild;n;n=n.__dom.nextSibling){node.__dom.childNodes.push(n);}}
+return node.__dom.childNodes;},getParentNode:function(node){return node.__dom&&node.__dom.parentNode!==undefined?node.__dom.parentNode:node.parentNode;},getFirstChild:function(node){return node.__dom&&node.__dom.firstChild!==undefined?node.__dom.firstChild:node.firstChild;},getLastChild:function(node){return node.__dom&&node.__dom.lastChild!==undefined?node.__dom.lastChild:node.lastChild;},getNextSibling:function(node){return node.__dom&&node.__dom.nextSibling!==undefined?node.__dom.nextSibling:node.nextSibling;},getPreviousSibling:function(node){return node.__dom&&node.__dom.previousSibling!==undefined?node.__dom.previousSibling:node.previousSibling;},getFirstElementChild:function(node){return node.__dom&&node.__dom.firstChild!==undefined?this._getFirstElementChild(node):node.firstElementChild;},_getFirstElementChild:function(node){var n=node.__dom.firstChild;while(n&&n.nodeType!==Node.ELEMENT_NODE){n=n.__dom.nextSibling;}
+return n;},getLastElementChild:function(node){return node.__dom&&node.__dom.lastChild!==undefined?this._getLastElementChild(node):node.lastElementChild;},_getLastElementChild:function(node){var n=node.__dom.lastChild;while(n&&n.nodeType!==Node.ELEMENT_NODE){n=n.__dom.previousSibling;}
+return n;},getNextElementSibling:function(node){return node.__dom&&node.__dom.nextSibling!==undefined?this._getNextElementSibling(node):node.nextElementSibling;},_getNextElementSibling:function(node){var n=node.__dom.nextSibling;while(n&&n.nodeType!==Node.ELEMENT_NODE){n=n.__dom.nextSibling;}
+return n;},getPreviousElementSibling:function(node){return node.__dom&&node.__dom.previousSibling!==undefined?this._getPreviousElementSibling(node):node.previousElementSibling;},_getPreviousElementSibling:function(node){var n=node.__dom.previousSibling;while(n&&n.nodeType!==Node.ELEMENT_NODE){n=n.__dom.previousSibling;}
+return n;},saveChildNodes:function(node){if(!this.hasChildNodes(node)){node.__dom=node.__dom||{};node.__dom.firstChild=node.firstChild;node.__dom.lastChild=node.lastChild;node.__dom.childNodes=[];for(var n=node.firstChild;n;n=n.nextSibling){n.__dom=n.__dom||{};n.__dom.parentNode=node;node.__dom.childNodes.push(n);n.__dom.nextSibling=n.nextSibling;n.__dom.previousSibling=n.previousSibling;}}},recordInsertBefore:function(node,container,ref_node){container.__dom.childNodes=null;if(node.nodeType===Node.DOCUMENT_FRAGMENT_NODE){for(var n=node.firstChild;n;n=n.nextSibling){this._linkNode(n,container,ref_node);}}else{this._linkNode(node,container,ref_node);}},_linkNode:function(node,container,ref_node){node.__dom=node.__dom||{};container.__dom=container.__dom||{};if(ref_node){ref_node.__dom=ref_node.__dom||{};}
+node.__dom.previousSibling=ref_node?ref_node.__dom.previousSibling:container.__dom.lastChild;if(node.__dom.previousSibling){node.__dom.previousSibling.__dom.nextSibling=node;}
+node.__dom.nextSibling=ref_node||null;if(node.__dom.nextSibling){node.__dom.nextSibling.__dom.previousSibling=node;}
+node.__dom.parentNode=container;if(ref_node){if(ref_node===container.__dom.firstChild){container.__dom.firstChild=node;}}else{container.__dom.lastChild=node;if(!container.__dom.firstChild){container.__dom.firstChild=node;}}
+container.__dom.childNodes=null;},recordRemoveChild:function(node,container){node.__dom=node.__dom||{};container.__dom=container.__dom||{};if(node===container.__dom.firstChild){container.__dom.firstChild=node.__dom.nextSibling;}
+if(node===container.__dom.lastChild){container.__dom.lastChild=node.__dom.previousSibling;}
+var p=node.__dom.previousSibling;var n=node.__dom.nextSibling;if(p){p.__dom.nextSibling=n;}
+if(n){n.__dom.previousSibling=p;}
+node.__dom.parentNode=node.__dom.previousSibling=node.__dom.nextSibling=undefined;container.__dom.childNodes=null;}};Polymer.TreeApi.Composed={getChildNodes:function(node){return Polymer.TreeApi.arrayCopyChildNodes(node);},getParentNode:function(node){return node.parentNode;},clearChildNodes:function(node){node.textContent='';},insertBefore:function(parentNode,newChild,refChild){return nativeInsertBefore.call(parentNode,newChild,refChild||null);},appendChild:function(parentNode,newChild){return nativeAppendChild.call(parentNode,newChild);},removeChild:function(parentNode,node){return nativeRemoveChild.call(parentNode,node);}};}());Polymer.DomApi=function(){'use strict';var Settings=Polymer.Settings;var TreeApi=Polymer.TreeApi;var DomApi=function(node){this.node=needsToWrap?DomApi.wrap(node):node;};var needsToWrap=Settings.hasShadow&&!Settings.nativeShadow;DomApi.wrap=window.wrap?window.wrap:function(node){return node;};DomApi.prototype={flush:function(){Polymer.dom.flush();},deepContains:function(node){if(this.node.contains(node)){return true;}
+var n=node;var doc=node.ownerDocument;while(n&&n!==doc&&n!==this.node){n=Polymer.dom(n).parentNode||n.host;}
+return n===this.node;},queryDistributedElements:function(selector){var c$=this.getEffectiveChildNodes();var list=[];for(var i=0,l=c$.length,c;i<l&&(c=c$[i]);i++){if(c.nodeType===Node.ELEMENT_NODE&&DomApi.matchesSelector.call(c,selector)){list.push(c);}}
+return list;},getEffectiveChildNodes:function(){var list=[];var c$=this.childNodes;for(var i=0,l=c$.length,c;i<l&&(c=c$[i]);i++){if(c.localName===CONTENT){var d$=dom(c).getDistributedNodes();for(var j=0;j<d$.length;j++){list.push(d$[j]);}}else{list.push(c);}}
+return list;},observeNodes:function(callback){if(callback){if(!this.observer){this.observer=this.node.localName===CONTENT?new DomApi.DistributedNodesObserver(this):new DomApi.EffectiveNodesObserver(this);}
+return this.observer.addListener(callback);}},unobserveNodes:function(handle){if(this.observer){this.observer.removeListener(handle);}},notifyObserver:function(){if(this.observer){this.observer.notify();}},_query:function(matcher,node,halter){node=node||this.node;var list=[];this._queryElements(TreeApi.Logical.getChildNodes(node),matcher,halter,list);return list;},_queryElements:function(elements,matcher,halter,list){for(var i=0,l=elements.length,c;i<l&&(c=elements[i]);i++){if(c.nodeType===Node.ELEMENT_NODE){if(this._queryElement(c,matcher,halter,list)){return true;}}}},_queryElement:function(node,matcher,halter,list){var result=matcher(node);if(result){list.push(node);}
+if(halter&&halter(result)){return result;}
+this._queryElements(TreeApi.Logical.getChildNodes(node),matcher,halter,list);}};var CONTENT=DomApi.CONTENT='content';var dom=DomApi.factory=function(node){node=node||document;if(!node.__domApi){node.__domApi=new DomApi.ctor(node);}
+return node.__domApi;};DomApi.hasApi=function(node){return Boolean(node.__domApi);};DomApi.ctor=DomApi;Polymer.dom=function(obj,patch){if(obj instanceof Event){return Polymer.EventApi.factory(obj);}else{return DomApi.factory(obj,patch);}};var p=Element.prototype;DomApi.matchesSelector=p.matches||p.matchesSelector||p.mozMatchesSelector||p.msMatchesSelector||p.oMatchesSelector||p.webkitMatchesSelector;return DomApi;}();(function(){'use strict';var Settings=Polymer.Settings;var DomApi=Polymer.DomApi;var dom=DomApi.factory;var TreeApi=Polymer.TreeApi;var getInnerHTML=Polymer.domInnerHTML.getInnerHTML;var CONTENT=DomApi.CONTENT;if(Settings.useShadow){return;}
+var nativeCloneNode=Element.prototype.cloneNode;var nativeImportNode=Document.prototype.importNode;Polymer.Base.mixin(DomApi.prototype,{_lazyDistribute:function(host){if(host.shadyRoot&&host.shadyRoot._distributionClean){host.shadyRoot._distributionClean=false;Polymer.dom.addDebouncer(host.debounce('_distribute',host._distributeContent));}},appendChild:function(node){return this.insertBefore(node);},insertBefore:function(node,ref_node){if(ref_node&&TreeApi.Logical.getParentNode(ref_node)!==this.node){throw Error('The ref_node to be inserted before is not a child '+'of this node');}
+if(node.nodeType!==Node.DOCUMENT_FRAGMENT_NODE){var parent=TreeApi.Logical.getParentNode(node);if(parent){if(DomApi.hasApi(parent)){dom(parent).notifyObserver();}
+this._removeNode(node);}else{this._removeOwnerShadyRoot(node);}}
+if(!this._addNode(node,ref_node)){if(ref_node){ref_node=ref_node.localName===CONTENT?this._firstComposedNode(ref_node):ref_node;}
+var container=this.node._isShadyRoot?this.node.host:this.node;if(ref_node){TreeApi.Composed.insertBefore(container,node,ref_node);}else{TreeApi.Composed.appendChild(container,node);}}
+this.notifyObserver();return node;},_addNode:function(node,ref_node){var root=this.getOwnerRoot();if(root){var ipAdded=this._maybeAddInsertionPoint(node,this.node);if(!root._invalidInsertionPoints){root._invalidInsertionPoints=ipAdded;}
+this._addNodeToHost(root.host,node);}
+if(TreeApi.Logical.hasChildNodes(this.node)){TreeApi.Logical.recordInsertBefore(node,this.node,ref_node);}
+var handled=this._maybeDistribute(node)||this.node.shadyRoot;if(handled){if(node.nodeType===Node.DOCUMENT_FRAGMENT_NODE){while(node.firstChild){TreeApi.Composed.removeChild(node,node.firstChild);}}else{var parent=TreeApi.Composed.getParentNode(node);if(parent){TreeApi.Composed.removeChild(parent,node);}}}
+return handled;},removeChild:function(node){if(TreeApi.Logical.getParentNode(node)!==this.node){throw Error('The node to be removed is not a child of this node: '+node);}
+if(!this._removeNode(node)){var container=this.node._isShadyRoot?this.node.host:this.node;var parent=TreeApi.Composed.getParentNode(node);if(container===parent){TreeApi.Composed.removeChild(container,node);}}
+this.notifyObserver();return node;},_removeNode:function(node){var logicalParent=TreeApi.Logical.hasParentNode(node)&&TreeApi.Logical.getParentNode(node);var distributed;var root=this._ownerShadyRootForNode(node);if(logicalParent){distributed=dom(node)._maybeDistributeParent();TreeApi.Logical.recordRemoveChild(node,logicalParent);if(root&&this._removeDistributedChildren(root,node)){root._invalidInsertionPoints=true;this._lazyDistribute(root.host);}}
+this._removeOwnerShadyRoot(node);if(root){this._removeNodeFromHost(root.host,node);}
+return distributed;},replaceChild:function(node,ref_node){this.insertBefore(node,ref_node);this.removeChild(ref_node);return node;},_hasCachedOwnerRoot:function(node){return Boolean(node._ownerShadyRoot!==undefined);},getOwnerRoot:function(){return this._ownerShadyRootForNode(this.node);},_ownerShadyRootForNode:function(node){if(!node){return;}
+var root=node._ownerShadyRoot;if(root===undefined){if(node._isShadyRoot){root=node;}else{var parent=TreeApi.Logical.getParentNode(node);if(parent){root=parent._isShadyRoot?parent:this._ownerShadyRootForNode(parent);}else{root=null;}}
+if(root||document.documentElement.contains(node)){node._ownerShadyRoot=root;}}
+return root;},_maybeDistribute:function(node){var fragContent=node.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&!node.__noContent&&dom(node).querySelector(CONTENT);var wrappedContent=fragContent&&TreeApi.Logical.getParentNode(fragContent).nodeType!==Node.DOCUMENT_FRAGMENT_NODE;var hasContent=fragContent||node.localName===CONTENT;if(hasContent){var root=this.getOwnerRoot();if(root){this._lazyDistribute(root.host);}}
+var needsDist=this._nodeNeedsDistribution(this.node);if(needsDist){this._lazyDistribute(this.node);}
+return needsDist||hasContent&&!wrappedContent;},_maybeAddInsertionPoint:function(node,parent){var added;if(node.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&!node.__noContent){var c$=dom(node).querySelectorAll(CONTENT);for(var i=0,n,np,na;i<c$.length&&(n=c$[i]);i++){np=TreeApi.Logical.getParentNode(n);if(np===node){np=parent;}
+na=this._maybeAddInsertionPoint(n,np);added=added||na;}}else if(node.localName===CONTENT){TreeApi.Logical.saveChildNodes(parent);TreeApi.Logical.saveChildNodes(node);added=true;}
+return added;},_updateInsertionPoints:function(host){var i$=host.shadyRoot._insertionPoints=dom(host.shadyRoot).querySelectorAll(CONTENT);for(var i=0,c;i<i$.length;i++){c=i$[i];TreeApi.Logical.saveChildNodes(c);TreeApi.Logical.saveChildNodes(TreeApi.Logical.getParentNode(c));}},_nodeNeedsDistribution:function(node){return node&&node.shadyRoot&&DomApi.hasInsertionPoint(node.shadyRoot);},_addNodeToHost:function(host,node){if(host._elementAdd){host._elementAdd(node);}},_removeNodeFromHost:function(host,node){if(host._elementRemove){host._elementRemove(node);}},_removeDistributedChildren:function(root,container){var hostNeedsDist;var ip$=root._insertionPoints;for(var i=0;i<ip$.length;i++){var content=ip$[i];if(this._contains(container,content)){var dc$=dom(content).getDistributedNodes();for(var j=0;j<dc$.length;j++){hostNeedsDist=true;var node=dc$[j];var parent=TreeApi.Composed.getParentNode(node);if(parent){TreeApi.Composed.removeChild(parent,node);}}}}
+return hostNeedsDist;},_contains:function(container,node){while(node){if(node==container){return true;}
+node=TreeApi.Logical.getParentNode(node);}},_removeOwnerShadyRoot:function(node){if(this._hasCachedOwnerRoot(node)){var c$=TreeApi.Logical.getChildNodes(node);for(var i=0,l=c$.length,n;i<l&&(n=c$[i]);i++){this._removeOwnerShadyRoot(n);}}
+node._ownerShadyRoot=undefined;},_firstComposedNode:function(content){var n$=dom(content).getDistributedNodes();for(var i=0,l=n$.length,n,p$;i<l&&(n=n$[i]);i++){p$=dom(n).getDestinationInsertionPoints();if(p$[p$.length-1]===content){return n;}}},querySelector:function(selector){var result=this._query(function(n){return DomApi.matchesSelector.call(n,selector);},this.node,function(n){return Boolean(n);})[0];return result||null;},querySelectorAll:function(selector){return this._query(function(n){return DomApi.matchesSelector.call(n,selector);},this.node);},getDestinationInsertionPoints:function(){return this.node._destinationInsertionPoints||[];},getDistributedNodes:function(){return this.node._distributedNodes||[];},_clear:function(){while(this.childNodes.length){this.removeChild(this.childNodes[0]);}},setAttribute:function(name,value){this.node.setAttribute(name,value);this._maybeDistributeParent();},removeAttribute:function(name){this.node.removeAttribute(name);this._maybeDistributeParent();},_maybeDistributeParent:function(){if(this._nodeNeedsDistribution(this.parentNode)){this._lazyDistribute(this.parentNode);return true;}},cloneNode:function(deep){var n=nativeCloneNode.call(this.node,false);if(deep){var c$=this.childNodes;var d=dom(n);for(var i=0,nc;i<c$.length;i++){nc=dom(c$[i]).cloneNode(true);d.appendChild(nc);}}
+return n;},importNode:function(externalNode,deep){var doc=this.node instanceof Document?this.node:this.node.ownerDocument;var n=nativeImportNode.call(doc,externalNode,false);if(deep){var c$=TreeApi.Logical.getChildNodes(externalNode);var d=dom(n);for(var i=0,nc;i<c$.length;i++){nc=dom(doc).importNode(c$[i],true);d.appendChild(nc);}}
+return n;},_getComposedInnerHTML:function(){return getInnerHTML(this.node,true);}});Object.defineProperties(DomApi.prototype,{activeElement:{get:function(){var active=document.activeElement;if(!active){return null;}
+var isShadyRoot=!!this.node._isShadyRoot;if(this.node!==document){if(!isShadyRoot){return null;}
+if(this.node.host===active||!this.node.host.contains(active)){return null;}}
+var activeRoot=dom(active).getOwnerRoot();while(activeRoot&&activeRoot!==this.node){active=activeRoot.host;activeRoot=dom(active).getOwnerRoot();}
+if(this.node===document){return activeRoot?null:active;}else{return activeRoot===this.node?active:null;}},configurable:true},childNodes:{get:function(){var c$=TreeApi.Logical.getChildNodes(this.node);return Array.isArray(c$)?c$:TreeApi.arrayCopyChildNodes(this.node);},configurable:true},children:{get:function(){if(TreeApi.Logical.hasChildNodes(this.node)){return Array.prototype.filter.call(this.childNodes,function(n){return n.nodeType===Node.ELEMENT_NODE;});}else{return TreeApi.arrayCopyChildren(this.node);}},configurable:true},parentNode:{get:function(){return TreeApi.Logical.getParentNode(this.node);},configurable:true},firstChild:{get:function(){return TreeApi.Logical.getFirstChild(this.node);},configurable:true},lastChild:{get:function(){return TreeApi.Logical.getLastChild(this.node);},configurable:true},nextSibling:{get:function(){return TreeApi.Logical.getNextSibling(this.node);},configurable:true},previousSibling:{get:function(){return TreeApi.Logical.getPreviousSibling(this.node);},configurable:true},firstElementChild:{get:function(){return TreeApi.Logical.getFirstElementChild(this.node);},configurable:true},lastElementChild:{get:function(){return TreeApi.Logical.getLastElementChild(this.node);},configurable:true},nextElementSibling:{get:function(){return TreeApi.Logical.getNextElementSibling(this.node);},configurable:true},previousElementSibling:{get:function(){return TreeApi.Logical.getPreviousElementSibling(this.node);},configurable:true},textContent:{get:function(){var nt=this.node.nodeType;if(nt===Node.TEXT_NODE||nt===Node.COMMENT_NODE){return this.node.textContent;}else{var tc=[];for(var i=0,cn=this.childNodes,c;c=cn[i];i++){if(c.nodeType!==Node.COMMENT_NODE){tc.push(c.textContent);}}
+return tc.join('');}},set:function(text){var nt=this.node.nodeType;if(nt===Node.TEXT_NODE||nt===Node.COMMENT_NODE){this.node.textContent=text;}else{this._clear();if(text){this.appendChild(document.createTextNode(text));}}},configurable:true},innerHTML:{get:function(){var nt=this.node.nodeType;if(nt===Node.TEXT_NODE||nt===Node.COMMENT_NODE){return null;}else{return getInnerHTML(this.node);}},set:function(text){var nt=this.node.nodeType;if(nt!==Node.TEXT_NODE||nt!==Node.COMMENT_NODE){this._clear();var d=document.createElement('div');d.innerHTML=text;var c$=TreeApi.arrayCopyChildNodes(d);for(var i=0;i<c$.length;i++){this.appendChild(c$[i]);}}},configurable:true}});DomApi.hasInsertionPoint=function(root){return Boolean(root&&root._insertionPoints.length);};}());(function(){'use strict';var Settings=Polymer.Settings;var TreeApi=Polymer.TreeApi;var DomApi=Polymer.DomApi;if(!Settings.useShadow){return;}
+Polymer.Base.mixin(DomApi.prototype,{querySelectorAll:function(selector){return TreeApi.arrayCopy(this.node.querySelectorAll(selector));},getOwnerRoot:function(){var n=this.node;while(n){if(n.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&n.host){return n;}
+n=n.parentNode;}},importNode:function(externalNode,deep){var doc=this.node instanceof Document?this.node:this.node.ownerDocument;return doc.importNode(externalNode,deep);},getDestinationInsertionPoints:function(){var n$=this.node.getDestinationInsertionPoints&&this.node.getDestinationInsertionPoints();return n$?TreeApi.arrayCopy(n$):[];},getDistributedNodes:function(){var n$=this.node.getDistributedNodes&&this.node.getDistributedNodes();return n$?TreeApi.arrayCopy(n$):[];}});Object.defineProperties(DomApi.prototype,{activeElement:{get:function(){var node=DomApi.wrap(this.node);var activeElement=node.activeElement;return node.contains(activeElement)?activeElement:null;},configurable:true},childNodes:{get:function(){return TreeApi.arrayCopyChildNodes(this.node);},configurable:true},children:{get:function(){return TreeApi.arrayCopyChildren(this.node);},configurable:true},textContent:{get:function(){return this.node.textContent;},set:function(value){return this.node.textContent=value;},configurable:true},innerHTML:{get:function(){return this.node.innerHTML;},set:function(value){return this.node.innerHTML=value;},configurable:true}});var forwardMethods=function(m$){for(var i=0;i<m$.length;i++){forwardMethod(m$[i]);}};var forwardMethod=function(method){DomApi.prototype[method]=function(){return this.node[method].apply(this.node,arguments);};};forwardMethods(['cloneNode','appendChild','insertBefore','removeChild','replaceChild','setAttribute','removeAttribute','querySelector']);var forwardProperties=function(f$){for(var i=0;i<f$.length;i++){forwardProperty(f$[i]);}};var forwardProperty=function(name){Object.defineProperty(DomApi.prototype,name,{get:function(){return this.node[name];},configurable:true});};forwardProperties(['parentNode','firstChild','lastChild','nextSibling','previousSibling','firstElementChild','lastElementChild','nextElementSibling','previousElementSibling']);}());Polymer.Base.mixin(Polymer.dom,{_flushGuard:0,_FLUSH_MAX:100,_needsTakeRecords:!Polymer.Settings.useNativeCustomElements,_debouncers:[],_staticFlushList:[],_finishDebouncer:null,flush:function(){this._flushGuard=0;this._prepareFlush();while(this._debouncers.length&&this._flushGuard<this._FLUSH_MAX){while(this._debouncers.length){this._debouncers.shift().complete();}
+if(this._finishDebouncer){this._finishDebouncer.complete();}
+this._prepareFlush();this._flushGuard++;}
+if(this._flushGuard>=this._FLUSH_MAX){console.warn('Polymer.dom.flush aborted. Flush may not be complete.');}},_prepareFlush:function(){if(this._needsTakeRecords){CustomElements.takeRecords();}
+for(var i=0;i<this._staticFlushList.length;i++){this._staticFlushList[i]();}},addStaticFlush:function(fn){this._staticFlushList.push(fn);},removeStaticFlush:function(fn){var i=this._staticFlushList.indexOf(fn);if(i>=0){this._staticFlushList.splice(i,1);}},addDebouncer:function(debouncer){this._debouncers.push(debouncer);this._finishDebouncer=Polymer.Debounce(this._finishDebouncer,this._finishFlush);},_finishFlush:function(){Polymer.dom._debouncers=[];}});Polymer.EventApi=function(){'use strict';var DomApi=Polymer.DomApi.ctor;var Settings=Polymer.Settings;DomApi.Event=function(event){this.event=event;};if(Settings.useShadow){DomApi.Event.prototype={get rootTarget(){return this.event.path[0];},get localTarget(){return this.event.target;},get path(){var path=this.event.path;if(!Array.isArray(path)){path=Array.prototype.slice.call(path);}
+return path;}};}else{DomApi.Event.prototype={get rootTarget(){return this.event.target;},get localTarget(){var current=this.event.currentTarget;var currentRoot=current&&Polymer.dom(current).getOwnerRoot();var p$=this.path;for(var i=0;i<p$.length;i++){if(Polymer.dom(p$[i]).getOwnerRoot()===currentRoot){return p$[i];}}},get path(){if(!this.event._path){var path=[];var current=this.rootTarget;while(current){path.push(current);var insertionPoints=Polymer.dom(current).getDestinationInsertionPoints();if(insertionPoints.length){for(var i=0;i<insertionPoints.length-1;i++){path.push(insertionPoints[i]);}
+current=insertionPoints[insertionPoints.length-1];}else{current=Polymer.dom(current).parentNode||current.host;}}
+path.push(window);this.event._path=path;}
+return this.event._path;}};}
+var factory=function(event){if(!event.__eventApi){event.__eventApi=new DomApi.Event(event);}
+return event.__eventApi;};return{factory:factory};}();(function(){'use strict';var DomApi=Polymer.DomApi.ctor;var useShadow=Polymer.Settings.useShadow;Object.defineProperty(DomApi.prototype,'classList',{get:function(){if(!this._classList){this._classList=new DomApi.ClassList(this);}
+return this._classList;},configurable:true});DomApi.ClassList=function(host){this.domApi=host;this.node=host.node;};DomApi.ClassList.prototype={add:function(){this.node.classList.add.apply(this.node.classList,arguments);this._distributeParent();},remove:function(){this.node.classList.remove.apply(this.node.classList,arguments);this._distributeParent();},toggle:function(){this.node.classList.toggle.apply(this.node.classList,arguments);this._distributeParent();},_distributeParent:function(){if(!useShadow){this.domApi._maybeDistributeParent();}},contains:function(){return this.node.classList.contains.apply(this.node.classList,arguments);}};}());(function(){'use strict';var DomApi=Polymer.DomApi.ctor;var Settings=Polymer.Settings;DomApi.EffectiveNodesObserver=function(domApi){this.domApi=domApi;this.node=this.domApi.node;this._listeners=[];};DomApi.EffectiveNodesObserver.prototype={addListener:function(callback){if(!this._isSetup){this._setup();this._isSetup=true;}
+var listener={fn:callback,_nodes:[]};this._listeners.push(listener);this._scheduleNotify();return listener;},removeListener:function(handle){var i=this._listeners.indexOf(handle);if(i>=0){this._listeners.splice(i,1);handle._nodes=[];}
+if(!this._hasListeners()){this._cleanup();this._isSetup=false;}},_setup:function(){this._observeContentElements(this.domApi.childNodes);},_cleanup:function(){this._unobserveContentElements(this.domApi.childNodes);},_hasListeners:function(){return Boolean(this._listeners.length);},_scheduleNotify:function(){if(this._debouncer){this._debouncer.stop();}
+this._debouncer=Polymer.Debounce(this._debouncer,this._notify);this._debouncer.context=this;Polymer.dom.addDebouncer(this._debouncer);},notify:function(){if(this._hasListeners()){this._scheduleNotify();}},_notify:function(){this._beforeCallListeners();this._callListeners();},_beforeCallListeners:function(){this._updateContentElements();},_updateContentElements:function(){this._observeContentElements(this.domApi.childNodes);},_observeContentElements:function(elements){for(var i=0,n;i<elements.length&&(n=elements[i]);i++){if(this._isContent(n)){n.__observeNodesMap=n.__observeNodesMap||new WeakMap();if(!n.__observeNodesMap.has(this)){n.__observeNodesMap.set(this,this._observeContent(n));}}}},_observeContent:function(content){var self=this;var h=Polymer.dom(content).observeNodes(function(){self._scheduleNotify();});h._avoidChangeCalculation=true;return h;},_unobserveContentElements:function(elements){for(var i=0,n,h;i<elements.length&&(n=elements[i]);i++){if(this._isContent(n)){h=n.__observeNodesMap.get(this);if(h){Polymer.dom(n).unobserveNodes(h);n.__observeNodesMap.delete(this);}}}},_isContent:function(node){return node.localName==='content';},_callListeners:function(){var o$=this._listeners;var nodes=this._getEffectiveNodes();for(var i=0,o;i<o$.length&&(o=o$[i]);i++){var info=this._generateListenerInfo(o,nodes);if(info||o._alwaysNotify){this._callListener(o,info);}}},_getEffectiveNodes:function(){return this.domApi.getEffectiveChildNodes();},_generateListenerInfo:function(listener,newNodes){if(listener._avoidChangeCalculation){return true;}
+var oldNodes=listener._nodes;var info={target:this.node,addedNodes:[],removedNodes:[]};var splices=Polymer.ArraySplice.calculateSplices(newNodes,oldNodes);for(var i=0,s;i<splices.length&&(s=splices[i]);i++){for(var j=0,n;j<s.removed.length&&(n=s.removed[j]);j++){info.removedNodes.push(n);}}
+for(i=0,s;i<splices.length&&(s=splices[i]);i++){for(j=s.index;j<s.index+s.addedCount;j++){info.addedNodes.push(newNodes[j]);}}
+listener._nodes=newNodes;if(info.addedNodes.length||info.removedNodes.length){return info;}},_callListener:function(listener,info){return listener.fn.call(this.node,info);},enableShadowAttributeTracking:function(){}};if(Settings.useShadow){var baseSetup=DomApi.EffectiveNodesObserver.prototype._setup;var baseCleanup=DomApi.EffectiveNodesObserver.prototype._cleanup;Polymer.Base.mixin(DomApi.EffectiveNodesObserver.prototype,{_setup:function(){if(!this._observer){var self=this;this._mutationHandler=function(mxns){if(mxns&&mxns.length){self._scheduleNotify();}};this._observer=new MutationObserver(this._mutationHandler);this._boundFlush=function(){self._flush();};Polymer.dom.addStaticFlush(this._boundFlush);this._observer.observe(this.node,{childList:true});}
+baseSetup.call(this);},_cleanup:function(){this._observer.disconnect();this._observer=null;this._mutationHandler=null;Polymer.dom.removeStaticFlush(this._boundFlush);baseCleanup.call(this);},_flush:function(){if(this._observer){this._mutationHandler(this._observer.takeRecords());}},enableShadowAttributeTracking:function(){if(this._observer){this._makeContentListenersAlwaysNotify();this._observer.disconnect();this._observer.observe(this.node,{childList:true,attributes:true,subtree:true});var root=this.domApi.getOwnerRoot();var host=root&&root.host;if(host&&Polymer.dom(host).observer){Polymer.dom(host).observer.enableShadowAttributeTracking();}}},_makeContentListenersAlwaysNotify:function(){for(var i=0,h;i<this._listeners.length;i++){h=this._listeners[i];h._alwaysNotify=h._isContentListener;}}});}}());(function(){'use strict';var DomApi=Polymer.DomApi.ctor;var Settings=Polymer.Settings;DomApi.DistributedNodesObserver=function(domApi){DomApi.EffectiveNodesObserver.call(this,domApi);};DomApi.DistributedNodesObserver.prototype=Object.create(DomApi.EffectiveNodesObserver.prototype);Polymer.Base.mixin(DomApi.DistributedNodesObserver.prototype,{_setup:function(){},_cleanup:function(){},_beforeCallListeners:function(){},_getEffectiveNodes:function(){return this.domApi.getDistributedNodes();}});if(Settings.useShadow){Polymer.Base.mixin(DomApi.DistributedNodesObserver.prototype,{_setup:function(){if(!this._observer){var root=this.domApi.getOwnerRoot();var host=root&&root.host;if(host){var self=this;this._observer=Polymer.dom(host).observeNodes(function(){self._scheduleNotify();});this._observer._isContentListener=true;if(this._hasAttrSelect()){Polymer.dom(host).observer.enableShadowAttributeTracking();}}}},_hasAttrSelect:function(){var select=this.node.getAttribute('select');return select&&select.match(/[[.]+/);},_cleanup:function(){var root=this.domApi.getOwnerRoot();var host=root&&root.host;if(host){Polymer.dom(host).unobserveNodes(this._observer);}
+this._observer=null;}});}}());(function(){var DomApi=Polymer.DomApi;var TreeApi=Polymer.TreeApi;Polymer.Base._addFeature({_prepShady:function(){this._useContent=this._useContent||Boolean(this._template);},_setupShady:function(){this.shadyRoot=null;if(!this.__domApi){this.__domApi=null;}
+if(!this.__dom){this.__dom=null;}
+if(!this._ownerShadyRoot){this._ownerShadyRoot=undefined;}},_poolContent:function(){if(this._useContent){TreeApi.Logical.saveChildNodes(this);}},_setupRoot:function(){if(this._useContent){this._createLocalRoot();if(!this.dataHost){upgradeLogicalChildren(TreeApi.Logical.getChildNodes(this));}}},_createLocalRoot:function(){this.shadyRoot=this.root;this.shadyRoot._distributionClean=false;this.shadyRoot._hasDistributed=false;this.shadyRoot._isShadyRoot=true;this.shadyRoot._dirtyRoots=[];var i$=this.shadyRoot._insertionPoints=!this._notes||this._notes._hasContent?this.shadyRoot.querySelectorAll('content'):[];TreeApi.Logical.saveChildNodes(this.shadyRoot);for(var i=0,c;i<i$.length;i++){c=i$[i];TreeApi.Logical.saveChildNodes(c);TreeApi.Logical.saveChildNodes(c.parentNode);}
+this.shadyRoot.host=this;},distributeContent:function(updateInsertionPoints){if(this.shadyRoot){this.shadyRoot._invalidInsertionPoints=this.shadyRoot._invalidInsertionPoints||updateInsertionPoints;var host=getTopDistributingHost(this);Polymer.dom(this)._lazyDistribute(host);}},_distributeContent:function(){if(this._useContent&&!this.shadyRoot._distributionClean){if(this.shadyRoot._invalidInsertionPoints){Polymer.dom(this)._updateInsertionPoints(this);this.shadyRoot._invalidInsertionPoints=false;}
+this._beginDistribute();this._distributeDirtyRoots();this._finishDistribute();}},_beginDistribute:function(){if(this._useContent&&DomApi.hasInsertionPoint(this.shadyRoot)){this._resetDistribution();this._distributePool(this.shadyRoot,this._collectPool());}},_distributeDirtyRoots:function(){var c$=this.shadyRoot._dirtyRoots;for(var i=0,l=c$.length,c;i<l&&(c=c$[i]);i++){c._distributeContent();}
+this.shadyRoot._dirtyRoots=[];},_finishDistribute:function(){if(this._useContent){this.shadyRoot._distributionClean=true;if(DomApi.hasInsertionPoint(this.shadyRoot)){this._composeTree();notifyContentObservers(this.shadyRoot);}else{if(!this.shadyRoot._hasDistributed){TreeApi.Composed.clearChildNodes(this);this.appendChild(this.shadyRoot);}else{var children=this._composeNode(this);this._updateChildNodes(this,children);}}
+if(!this.shadyRoot._hasDistributed){notifyInitialDistribution(this);}
+this.shadyRoot._hasDistributed=true;}},elementMatches:function(selector,node){node=node||this;return DomApi.matchesSelector.call(node,selector);},_resetDistribution:function(){var children=TreeApi.Logical.getChildNodes(this);for(var i=0;i<children.length;i++){var child=children[i];if(child._destinationInsertionPoints){child._destinationInsertionPoints=undefined;}
+if(isInsertionPoint(child)){clearDistributedDestinationInsertionPoints(child);}}
+var root=this.shadyRoot;var p$=root._insertionPoints;for(var j=0;j<p$.length;j++){p$[j]._distributedNodes=[];}},_collectPool:function(){var pool=[];var children=TreeApi.Logical.getChildNodes(this);for(var i=0;i<children.length;i++){var child=children[i];if(isInsertionPoint(child)){pool.push.apply(pool,child._distributedNodes);}else{pool.push(child);}}
+return pool;},_distributePool:function(node,pool){var p$=node._insertionPoints;for(var i=0,l=p$.length,p;i<l&&(p=p$[i]);i++){this._distributeInsertionPoint(p,pool);maybeRedistributeParent(p,this);}},_distributeInsertionPoint:function(content,pool){var anyDistributed=false;for(var i=0,l=pool.length,node;i<l;i++){node=pool[i];if(!node){continue;}
+if(this._matchesContentSelect(node,content)){distributeNodeInto(node,content);pool[i]=undefined;anyDistributed=true;}}
+if(!anyDistributed){var children=TreeApi.Logical.getChildNodes(content);for(var j=0;j<children.length;j++){distributeNodeInto(children[j],content);}}},_composeTree:function(){this._updateChildNodes(this,this._composeNode(this));var p$=this.shadyRoot._insertionPoints;for(var i=0,l=p$.length,p,parent;i<l&&(p=p$[i]);i++){parent=TreeApi.Logical.getParentNode(p);if(!parent._useContent&&parent!==this&&parent!==this.shadyRoot){this._updateChildNodes(parent,this._composeNode(parent));}}},_composeNode:function(node){var children=[];var c$=TreeApi.Logical.getChildNodes(node.shadyRoot||node);for(var i=0;i<c$.length;i++){var child=c$[i];if(isInsertionPoint(child)){var distributedNodes=child._distributedNodes;for(var j=0;j<distributedNodes.length;j++){var distributedNode=distributedNodes[j];if(isFinalDestination(child,distributedNode)){children.push(distributedNode);}}}else{children.push(child);}}
+return children;},_updateChildNodes:function(container,children){var composed=TreeApi.Composed.getChildNodes(container);var splices=Polymer.ArraySplice.calculateSplices(children,composed);for(var i=0,d=0,s;i<splices.length&&(s=splices[i]);i++){for(var j=0,n;j<s.removed.length&&(n=s.removed[j]);j++){if(TreeApi.Composed.getParentNode(n)===container){TreeApi.Composed.removeChild(container,n);}
+composed.splice(s.index+d,1);}
+d-=s.addedCount;}
+for(var i=0,s,next;i<splices.length&&(s=splices[i]);i++){next=composed[s.index];for(j=s.index,n;j<s.index+s.addedCount;j++){n=children[j];TreeApi.Composed.insertBefore(container,n,next);composed.splice(j,0,n);}}},_matchesContentSelect:function(node,contentElement){var select=contentElement.getAttribute('select');if(!select){return true;}
+select=select.trim();if(!select){return true;}
+if(!(node instanceof Element)){return false;}
+var validSelectors=/^(:not\()?[*.#[a-zA-Z_|]/;if(!validSelectors.test(select)){return false;}
+return this.elementMatches(select,node);},_elementAdd:function(){},_elementRemove:function(){}});var domHostDesc={get:function(){var root=Polymer.dom(this).getOwnerRoot();return root&&root.host;},configurable:true};Object.defineProperty(Polymer.Base,'domHost',domHostDesc);Polymer.BaseDescriptors.domHost=domHostDesc;function distributeNodeInto(child,insertionPoint){insertionPoint._distributedNodes.push(child);var points=child._destinationInsertionPoints;if(!points){child._destinationInsertionPoints=[insertionPoint];}else{points.push(insertionPoint);}}
+function clearDistributedDestinationInsertionPoints(content){var e$=content._distributedNodes;if(e$){for(var i=0;i<e$.length;i++){var d=e$[i]._destinationInsertionPoints;if(d){d.splice(d.indexOf(content)+1,d.length);}}}}
+function maybeRedistributeParent(content,host){var parent=TreeApi.Logical.getParentNode(content);if(parent&&parent.shadyRoot&&DomApi.hasInsertionPoint(parent.shadyRoot)&&parent.shadyRoot._distributionClean){parent.shadyRoot._distributionClean=false;host.shadyRoot._dirtyRoots.push(parent);}}
+function isFinalDestination(insertionPoint,node){var points=node._destinationInsertionPoints;return points&&points[points.length-1]===insertionPoint;}
+function isInsertionPoint(node){return node.localName=='content';}
+function getTopDistributingHost(host){while(host&&hostNeedsRedistribution(host)){host=host.domHost;}
+return host;}
+function hostNeedsRedistribution(host){var c$=TreeApi.Logical.getChildNodes(host);for(var i=0,c;i<c$.length;i++){c=c$[i];if(c.localName&&c.localName==='content'){return host.domHost;}}}
+function notifyContentObservers(root){for(var i=0,c;i<root._insertionPoints.length;i++){c=root._insertionPoints[i];if(DomApi.hasApi(c)){Polymer.dom(c).notifyObserver();}}}
+function notifyInitialDistribution(host){if(DomApi.hasApi(host)){Polymer.dom(host).notifyObserver();}}
+var needsUpgrade=window.CustomElements&&!CustomElements.useNative;function upgradeLogicalChildren(children){if(needsUpgrade&&children){for(var i=0;i<children.length;i++){CustomElements.upgrade(children[i]);}}}}());if(Polymer.Settings.useShadow){Polymer.Base._addFeature({_poolContent:function(){},_beginDistribute:function(){},distributeContent:function(){},_distributeContent:function(){},_finishDistribute:function(){},_createLocalRoot:function(){this.createShadowRoot();this.shadowRoot.appendChild(this.root);this.root=this.shadowRoot;}});}Polymer.Async={_currVal:0,_lastVal:0,_callbacks:[],_twiddleContent:0,_twiddle:document.createTextNode(''),run:function(callback,waitTime){if(waitTime>0){return~setTimeout(callback,waitTime);}else{this._twiddle.textContent=this._twiddleContent++;this._callbacks.push(callback);return this._currVal++;}},cancel:function(handle){if(handle<0){clearTimeout(~handle);}else{var idx=handle-this._lastVal;if(idx>=0){if(!this._callbacks[idx]){throw'invalid async handle: '+handle;}
+this._callbacks[idx]=null;}}},_atEndOfMicrotask:function(){var len=this._callbacks.length;for(var i=0;i<len;i++){var cb=this._callbacks[i];if(cb){try{cb();}catch(e){i++;this._callbacks.splice(0,i);this._lastVal+=i;this._twiddle.textContent=this._twiddleContent++;throw e;}}}
+this._callbacks.splice(0,len);this._lastVal+=len;}};new window.MutationObserver(function(){Polymer.Async._atEndOfMicrotask();}).observe(Polymer.Async._twiddle,{characterData:true});Polymer.Debounce=function(){var Async=Polymer.Async;var Debouncer=function(context){this.context=context;var self=this;this.boundComplete=function(){self.complete();};};Debouncer.prototype={go:function(callback,wait){var h;this.finish=function(){Async.cancel(h);};h=Async.run(this.boundComplete,wait);this.callback=callback;},stop:function(){if(this.finish){this.finish();this.finish=null;this.callback=null;}},complete:function(){if(this.finish){var callback=this.callback;this.stop();callback.call(this.context);}}};function debounce(debouncer,callback,wait){if(debouncer){debouncer.stop();}else{debouncer=new Debouncer(this);}
+debouncer.go(callback,wait);return debouncer;}
+return debounce;}();Polymer.Base._addFeature({_setupDebouncers:function(){this._debouncers={};},debounce:function(jobName,callback,wait){return this._debouncers[jobName]=Polymer.Debounce.call(this,this._debouncers[jobName],callback,wait);},isDebouncerActive:function(jobName){var debouncer=this._debouncers[jobName];return!!(debouncer&&debouncer.finish);},flushDebouncer:function(jobName){var debouncer=this._debouncers[jobName];if(debouncer){debouncer.complete();}},cancelDebouncer:function(jobName){var debouncer=this._debouncers[jobName];if(debouncer){debouncer.stop();}}});Polymer.DomModule=document.createElement('dom-module');Polymer.Base._addFeature({_registerFeatures:function(){this._prepIs();this._prepBehaviors();this._prepConstructor();this._prepTemplate();this._prepShady();this._prepPropertyInfo();},_prepBehavior:function(b){this._addHostAttributes(b.hostAttributes);},_initFeatures:function(){this._registerHost();if(this._template){this._poolContent();this._beginHosting();this._stampTemplate();this._endHosting();}
+this._marshalHostAttributes();this._setupDebouncers();this._marshalBehaviors();this._tryReady();},_marshalBehavior:function(b){}});(function(){Polymer.nar=[];var disableUpgradeEnabled=Polymer.Settings.disableUpgradeEnabled;Polymer.Annotations={parseAnnotations:function(template,stripWhiteSpace){var list=[];var content=template._content||template.content;this._parseNodeAnnotations(content,list,stripWhiteSpace||template.hasAttribute('strip-whitespace'));return list;},_parseNodeAnnotations:function(node,list,stripWhiteSpace){return node.nodeType===Node.TEXT_NODE?this._parseTextNodeAnnotation(node,list):this._parseElementAnnotations(node,list,stripWhiteSpace);},_bindingRegex:function(){var IDENT='(?:'+'[a-zA-Z_$][\\w.:$\\-*]*'+')';var NUMBER='(?:'+'[-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?'+')';var SQUOTE_STRING='(?:'+'\'(?:[^\'\\\\]|\\\\.)*\''+')';var DQUOTE_STRING='(?:'+'"(?:[^"\\\\]|\\\\.)*"'+')';var STRING='(?:'+SQUOTE_STRING+'|'+DQUOTE_STRING+')';var ARGUMENT='(?:'+IDENT+'|'+NUMBER+'|'+STRING+'\\s*'+')';var ARGUMENTS='(?:'+ARGUMENT+'(?:,\\s*'+ARGUMENT+')*'+')';var ARGUMENT_LIST='(?:'+'\\(\\s*'+'(?:'+ARGUMENTS+'?'+')'+'\\)\\s*'+')';var BINDING='('+IDENT+'\\s*'+ARGUMENT_LIST+'?'+')';var OPEN_BRACKET='(\\[\\[|{{)'+'\\s*';var CLOSE_BRACKET='(?:]]|}})';var NEGATE='(?:(!)\\s*)?';var EXPRESSION=OPEN_BRACKET+NEGATE+BINDING+CLOSE_BRACKET;return new RegExp(EXPRESSION,'g');}(),_parseBindings:function(text){var re=this._bindingRegex;var parts=[];var lastIndex=0;var m;while((m=re.exec(text))!==null){if(m.index>lastIndex){parts.push({literal:text.slice(lastIndex,m.index)});}
+var mode=m[1][0];var negate=Boolean(m[2]);var value=m[3].trim();var customEvent,notifyEvent,colon;if(mode=='{'&&(colon=value.indexOf('::'))>0){notifyEvent=value.substring(colon+2);value=value.substring(0,colon);customEvent=true;}
+parts.push({compoundIndex:parts.length,value:value,mode:mode,negate:negate,event:notifyEvent,customEvent:customEvent});lastIndex=re.lastIndex;}
+if(lastIndex&&lastIndex<text.length){var literal=text.substring(lastIndex);if(literal){parts.push({literal:literal});}}
+if(parts.length){return parts;}},_literalFromParts:function(parts){var s='';for(var i=0;i<parts.length;i++){var literal=parts[i].literal;s+=literal||'';}
+return s;},_parseTextNodeAnnotation:function(node,list){var parts=this._parseBindings(node.textContent);if(parts){node.textContent=this._literalFromParts(parts)||' ';var annote={bindings:[{kind:'text',name:'textContent',parts:parts,isCompound:parts.length!==1}]};list.push(annote);return annote;}},_parseElementAnnotations:function(element,list,stripWhiteSpace){var annote={bindings:[],events:[]};if(element.localName==='content'){list._hasContent=true;}
+this._parseChildNodesAnnotations(element,annote,list,stripWhiteSpace);if(element.attributes){this._parseNodeAttributeAnnotations(element,annote,list);if(this.prepElement){this.prepElement(element);}}
+if(annote.bindings.length||annote.events.length||annote.id){list.push(annote);}
+return annote;},_parseChildNodesAnnotations:function(root,annote,list,stripWhiteSpace){if(root.firstChild){var node=root.firstChild;var i=0;while(node){var next=node.nextSibling;if(node.localName==='template'&&!node.hasAttribute('preserve-content')){this._parseTemplate(node,i,list,annote,stripWhiteSpace);}
+if(node.localName=='slot'){node=this._replaceSlotWithContent(node);}
+if(node.nodeType===Node.TEXT_NODE){var n=next;while(n&&n.nodeType===Node.TEXT_NODE){node.textContent+=n.textContent;next=n.nextSibling;root.removeChild(n);n=next;}
+if(stripWhiteSpace&&!node.textContent.trim()){root.removeChild(node);i--;}}
+if(node.parentNode){var childAnnotation=this._parseNodeAnnotations(node,list,stripWhiteSpace);if(childAnnotation){childAnnotation.parent=annote;childAnnotation.index=i;}}
+node=next;i++;}}},_replaceSlotWithContent:function(slot){var content=slot.ownerDocument.createElement('content');while(slot.firstChild){content.appendChild(slot.firstChild);}
+var attrs=slot.attributes;for(var i=0;i<attrs.length;i++){var attr=attrs[i];content.setAttribute(attr.name,attr.value);}
+var name=slot.getAttribute('name');if(name){content.setAttribute('select','[slot=\''+name+'\']');}
+slot.parentNode.replaceChild(content,slot);return content;},_parseTemplate:function(node,index,list,parent,stripWhiteSpace){var content=document.createDocumentFragment();content._notes=this.parseAnnotations(node,stripWhiteSpace);content.appendChild(node.content);list.push({bindings:Polymer.nar,events:Polymer.nar,templateContent:content,parent:parent,index:index});},_parseNodeAttributeAnnotations:function(node,annotation){var attrs=Array.prototype.slice.call(node.attributes);for(var i=attrs.length-1,a;a=attrs[i];i--){var n=a.name;var v=a.value;var b;if(n.slice(0,3)==='on-'){node.removeAttribute(n);annotation.events.push({name:n.slice(3),value:v});}else if(b=this._parseNodeAttributeAnnotation(node,n,v)){annotation.bindings.push(b);}else if(n==='id'){annotation.id=v;}}},_parseNodeAttributeAnnotation:function(node,name,value){var parts=this._parseBindings(value);if(parts){var origName=name;var kind='property';if(name[name.length-1]=='$'){name=name.slice(0,-1);kind='attribute';}
+var literal=this._literalFromParts(parts);if(literal&&kind=='attribute'){node.setAttribute(name,literal);}
+if(node.localName==='input'&&origName==='value'){node.setAttribute(origName,'');}
+if(disableUpgradeEnabled&&origName==='disable-upgrade$'){node.setAttribute(name,'');}
+node.removeAttribute(origName);var propertyName=Polymer.CaseMap.dashToCamelCase(name);if(kind==='property'){name=propertyName;}
+return{kind:kind,name:name,propertyName:propertyName,parts:parts,literal:literal,isCompound:parts.length!==1};}},findAnnotatedNode:function(root,annote){var parent=annote.parent&&Polymer.Annotations.findAnnotatedNode(root,annote.parent);if(parent){for(var n=parent.firstChild,i=0;n;n=n.nextSibling){if(annote.index===i++){return n;}}}else{return root;}}};}());Polymer.Path={root:function(path){var dotIndex=path.indexOf('.');if(dotIndex===-1){return path;}
+return path.slice(0,dotIndex);},isDeep:function(path){return path.indexOf('.')!==-1;},isAncestor:function(base,path){return base.indexOf(path+'.')===0;},isDescendant:function(base,path){return path.indexOf(base+'.')===0;},translate:function(base,newBase,path){return newBase+path.slice(base.length);},matches:function(base,wildcard,path){return base===path||this.isAncestor(base,path)||Boolean(wildcard)&&this.isDescendant(base,path);}};Polymer.Base._addFeature({_prepAnnotations:function(){if(!this._template){this._notes=[];}else{var self=this;Polymer.Annotations.prepElement=function(element){self._prepElement(element);};if(this._template._content&&this._template._content._notes){this._notes=this._template._content._notes;}else{this._notes=Polymer.Annotations.parseAnnotations(this._template);this._processAnnotations(this._notes);}
+Polymer.Annotations.prepElement=null;}},_processAnnotations:function(notes){for(var i=0;i<notes.length;i++){var note=notes[i];for(var j=0;j<note.bindings.length;j++){var b=note.bindings[j];for(var k=0;k<b.parts.length;k++){var p=b.parts[k];if(!p.literal){var signature=this._parseMethod(p.value);if(signature){p.signature=signature;}else{p.model=Polymer.Path.root(p.value);}}}}
+if(note.templateContent){this._processAnnotations(note.templateContent._notes);var pp=note.templateContent._parentProps=this._discoverTemplateParentProps(note.templateContent._notes);var bindings=[];for(var prop in pp){var name='_parent_'+prop;bindings.push({index:note.index,kind:'property',name:name,propertyName:name,parts:[{mode:'{',model:prop,value:prop}]});}
+note.bindings=note.bindings.concat(bindings);}}},_discoverTemplateParentProps:function(notes){var pp={};for(var i=0,n;i<notes.length&&(n=notes[i]);i++){for(var j=0,b$=n.bindings,b;j<b$.length&&(b=b$[j]);j++){for(var k=0,p$=b.parts,p;k<p$.length&&(p=p$[k]);k++){if(p.signature){var args=p.signature.args;for(var kk=0;kk<args.length;kk++){var model=args[kk].model;if(model){pp[model]=true;}}
+if(p.signature.dynamicFn){pp[p.signature.method]=true;}}else{if(p.model){pp[p.model]=true;}}}}
+if(n.templateContent){var tpp=n.templateContent._parentProps;Polymer.Base.mixin(pp,tpp);}}
+return pp;},_prepElement:function(element){Polymer.ResolveUrl.resolveAttrs(element,this._template.ownerDocument);},_findAnnotatedNode:Polymer.Annotations.findAnnotatedNode,_marshalAnnotationReferences:function(){if(this._template){this._marshalIdNodes();this._marshalAnnotatedNodes();this._marshalAnnotatedListeners();}},_configureAnnotationReferences:function(){var notes=this._notes;var nodes=this._nodes;for(var i=0;i<notes.length;i++){var note=notes[i];var node=nodes[i];this._configureTemplateContent(note,node);this._configureCompoundBindings(note,node);}},_configureTemplateContent:function(note,node){if(note.templateContent){node._content=note.templateContent;}},_configureCompoundBindings:function(note,node){var bindings=note.bindings;for(var i=0;i<bindings.length;i++){var binding=bindings[i];if(binding.isCompound){var storage=node.__compoundStorage__||(node.__compoundStorage__={});var parts=binding.parts;var literals=new Array(parts.length);for(var j=0;j<parts.length;j++){literals[j]=parts[j].literal;}
+var name=binding.name;storage[name]=literals;if(binding.literal&&binding.kind=='property'){if(node._configValue){node._configValue(name,binding.literal);}else{node[name]=binding.literal;}}}}},_marshalIdNodes:function(){this.$={};for(var i=0,l=this._notes.length,a;i<l&&(a=this._notes[i]);i++){if(a.id){this.$[a.id]=this._findAnnotatedNode(this.root,a);}}},_marshalAnnotatedNodes:function(){if(this._notes&&this._notes.length){var r=new Array(this._notes.length);for(var i=0;i<this._notes.length;i++){r[i]=this._findAnnotatedNode(this.root,this._notes[i]);}
+this._nodes=r;}},_marshalAnnotatedListeners:function(){for(var i=0,l=this._notes.length,a;i<l&&(a=this._notes[i]);i++){if(a.events&&a.events.length){var node=this._findAnnotatedNode(this.root,a);for(var j=0,e$=a.events,e;j<e$.length&&(e=e$[j]);j++){this.listen(node,e.name,e.value);}}}}});Polymer.Base._addFeature({listeners:{},_listenListeners:function(listeners){var node,name,eventName;for(eventName in listeners){if(eventName.indexOf('.')<0){node=this;name=eventName;}else{name=eventName.split('.');node=this.$[name[0]];name=name[1];}
+this.listen(node,name,listeners[eventName]);}},listen:function(node,eventName,methodName){var handler=this._recallEventHandler(this,eventName,node,methodName);if(!handler){handler=this._createEventHandler(node,eventName,methodName);}
+if(handler._listening){return;}
+this._listen(node,eventName,handler);handler._listening=true;},_boundListenerKey:function(eventName,methodName){return eventName+':'+methodName;},_recordEventHandler:function(host,eventName,target,methodName,handler){var hbl=host.__boundListeners;if(!hbl){hbl=host.__boundListeners=new WeakMap();}
+var bl=hbl.get(target);if(!bl){bl={};if(!Polymer.Settings.isIE||target!=window){hbl.set(target,bl);}}
+var key=this._boundListenerKey(eventName,methodName);bl[key]=handler;},_recallEventHandler:function(host,eventName,target,methodName){var hbl=host.__boundListeners;if(!hbl){return;}
+var bl=hbl.get(target);if(!bl){return;}
+var key=this._boundListenerKey(eventName,methodName);return bl[key];},_createEventHandler:function(node,eventName,methodName){var host=this;var handler=function(e){if(host[methodName]){host[methodName](e,e.detail);}else{host._warn(host._logf('_createEventHandler','listener method `'+methodName+'` not defined'));}};handler._listening=false;this._recordEventHandler(host,eventName,node,methodName,handler);return handler;},unlisten:function(node,eventName,methodName){var handler=this._recallEventHandler(this,eventName,node,methodName);if(handler){this._unlisten(node,eventName,handler);handler._listening=false;}},_listen:function(node,eventName,handler){node.addEventListener(eventName,handler);},_unlisten:function(node,eventName,handler){node.removeEventListener(eventName,handler);}});(function(){'use strict';var wrap=Polymer.DomApi.wrap;var HAS_NATIVE_TA=typeof document.head.style.touchAction==='string';var GESTURE_KEY='__polymerGestures';var HANDLED_OBJ='__polymerGesturesHandled';var TOUCH_ACTION='__polymerGesturesTouchAction';var TAP_DISTANCE=25;var TRACK_DISTANCE=5;var TRACK_LENGTH=2;var MOUSE_TIMEOUT=2500;var MOUSE_EVENTS=['mousedown','mousemove','mouseup','click'];var MOUSE_WHICH_TO_BUTTONS=[0,1,4,2];var MOUSE_HAS_BUTTONS=function(){try{return new MouseEvent('test',{buttons:1}).buttons===1;}catch(e){return false;}}();function isMouseEvent(name){return MOUSE_EVENTS.indexOf(name)>-1;}
+var SUPPORTS_PASSIVE=false;(function(){try{var opts=Object.defineProperty({},'passive',{get:function(){SUPPORTS_PASSIVE=true;}});window.addEventListener('test',null,opts);window.removeEventListener('test',null,opts);}catch(e){}}());function PASSIVE_TOUCH(eventName){if(isMouseEvent(eventName)||eventName==='touchend'){return;}
+if(HAS_NATIVE_TA&&SUPPORTS_PASSIVE&&Polymer.Settings.passiveTouchGestures){return{passive:true};}}
+var IS_TOUCH_ONLY=navigator.userAgent.match(/iP(?:[oa]d|hone)|Android/);var mouseCanceller=function(mouseEvent){var sc=mouseEvent.sourceCapabilities;if(sc&&!sc.firesTouchEvents){return;}
+mouseEvent[HANDLED_OBJ]={skip:true};if(mouseEvent.type==='click'){var path=Polymer.dom(mouseEvent).path;if(path){for(var i=0;i<path.length;i++){if(path[i]===POINTERSTATE.mouse.target){return;}}}
+mouseEvent.preventDefault();mouseEvent.stopPropagation();}};function setupTeardownMouseCanceller(setup){var events=IS_TOUCH_ONLY?['click']:MOUSE_EVENTS;for(var i=0,en;i<events.length;i++){en=events[i];if(setup){document.addEventListener(en,mouseCanceller,true);}else{document.removeEventListener(en,mouseCanceller,true);}}}
+function ignoreMouse(ev){if(!POINTERSTATE.mouse.mouseIgnoreJob){setupTeardownMouseCanceller(true);}
+var unset=function(){setupTeardownMouseCanceller();POINTERSTATE.mouse.target=null;POINTERSTATE.mouse.mouseIgnoreJob=null;};POINTERSTATE.mouse.target=Polymer.dom(ev).rootTarget;POINTERSTATE.mouse.mouseIgnoreJob=Polymer.Debounce(POINTERSTATE.mouse.mouseIgnoreJob,unset,MOUSE_TIMEOUT);}
+function hasLeftMouseButton(ev){var type=ev.type;if(!isMouseEvent(type)){return false;}
+if(type==='mousemove'){var buttons=ev.buttons===undefined?1:ev.buttons;if(ev instanceof window.MouseEvent&&!MOUSE_HAS_BUTTONS){buttons=MOUSE_WHICH_TO_BUTTONS[ev.which]||0;}
+return Boolean(buttons&1);}else{var button=ev.button===undefined?0:ev.button;return button===0;}}
+function isSyntheticClick(ev){if(ev.type==='click'){if(ev.detail===0){return true;}
+var t=Gestures.findOriginalTarget(ev);var bcr=t.getBoundingClientRect();var x=ev.pageX,y=ev.pageY;return!(x>=bcr.left&&x<=bcr.right&&(y>=bcr.top&&y<=bcr.bottom));}
+return false;}
+var POINTERSTATE={mouse:{target:null,mouseIgnoreJob:null},touch:{x:0,y:0,id:-1,scrollDecided:false}};function firstTouchAction(ev){var path=Polymer.dom(ev).path;var ta='auto';for(var i=0,n;i<path.length;i++){n=path[i];if(n[TOUCH_ACTION]){ta=n[TOUCH_ACTION];break;}}
+return ta;}
+function trackDocument(stateObj,movefn,upfn){stateObj.movefn=movefn;stateObj.upfn=upfn;document.addEventListener('mousemove',movefn);document.addEventListener('mouseup',upfn);}
+function untrackDocument(stateObj){document.removeEventListener('mousemove',stateObj.movefn);document.removeEventListener('mouseup',stateObj.upfn);stateObj.movefn=null;stateObj.upfn=null;}
+document.addEventListener('touchend',ignoreMouse,SUPPORTS_PASSIVE?{passive:true}:false);var Gestures={gestures:{},recognizers:[],deepTargetFind:function(x,y){var node=document.elementFromPoint(x,y);var next=node;while(next&&next.shadowRoot){next=next.shadowRoot.elementFromPoint(x,y);if(next){node=next;}}
+return node;},findOriginalTarget:function(ev){if(ev.path){return ev.path[0];}
+return ev.target;},handleNative:function(ev){var handled;var type=ev.type;var node=wrap(ev.currentTarget);var gobj=node[GESTURE_KEY];if(!gobj){return;}
+var gs=gobj[type];if(!gs){return;}
+if(!ev[HANDLED_OBJ]){ev[HANDLED_OBJ]={};if(type.slice(0,5)==='touch'){var t=ev.changedTouches[0];if(type==='touchstart'){if(ev.touches.length===1){POINTERSTATE.touch.id=t.identifier;}}
+if(POINTERSTATE.touch.id!==t.identifier){return;}
+if(!HAS_NATIVE_TA){if(type==='touchstart'||type==='touchmove'){Gestures.handleTouchAction(ev);}}}}
+handled=ev[HANDLED_OBJ];if(handled.skip){return;}
+var recognizers=Gestures.recognizers;for(var i=0,r;i<recognizers.length;i++){r=recognizers[i];if(gs[r.name]&&!handled[r.name]){if(r.flow&&r.flow.start.indexOf(ev.type)>-1&&r.reset){r.reset();}}}
+for(i=0,r;i<recognizers.length;i++){r=recognizers[i];if(gs[r.name]&&!handled[r.name]){handled[r.name]=true;r[type](ev);}}},handleTouchAction:function(ev){var t=ev.changedTouches[0];var type=ev.type;if(type==='touchstart'){POINTERSTATE.touch.x=t.clientX;POINTERSTATE.touch.y=t.clientY;POINTERSTATE.touch.scrollDecided=false;}else if(type==='touchmove'){if(POINTERSTATE.touch.scrollDecided){return;}
+POINTERSTATE.touch.scrollDecided=true;var ta=firstTouchAction(ev);var prevent=false;var dx=Math.abs(POINTERSTATE.touch.x-t.clientX);var dy=Math.abs(POINTERSTATE.touch.y-t.clientY);if(!ev.cancelable){}else if(ta==='none'){prevent=true;}else if(ta==='pan-x'){prevent=dy>dx;}else if(ta==='pan-y'){prevent=dx>dy;}
+if(prevent){ev.preventDefault();}else{Gestures.prevent('track');}}},add:function(node,evType,handler){node=wrap(node);var recognizer=this.gestures[evType];var deps=recognizer.deps;var name=recognizer.name;var gobj=node[GESTURE_KEY];if(!gobj){node[GESTURE_KEY]=gobj={};}
+for(var i=0,dep,gd;i<deps.length;i++){dep=deps[i];if(IS_TOUCH_ONLY&&isMouseEvent(dep)&&dep!=='click'){continue;}
+gd=gobj[dep];if(!gd){gobj[dep]=gd={_count:0};}
+if(gd._count===0){node.addEventListener(dep,this.handleNative,PASSIVE_TOUCH(dep));}
+gd[name]=(gd[name]||0)+1;gd._count=(gd._count||0)+1;}
+node.addEventListener(evType,handler);if(recognizer.touchAction){this.setTouchAction(node,recognizer.touchAction);}},remove:function(node,evType,handler){node=wrap(node);var recognizer=this.gestures[evType];var deps=recognizer.deps;var name=recognizer.name;var gobj=node[GESTURE_KEY];if(gobj){for(var i=0,dep,gd;i<deps.length;i++){dep=deps[i];gd=gobj[dep];if(gd&&gd[name]){gd[name]=(gd[name]||1)-1;gd._count=(gd._count||1)-1;if(gd._count===0){node.removeEventListener(dep,this.handleNative,PASSIVE_TOUCH(dep));}}}}
+node.removeEventListener(evType,handler);},register:function(recog){this.recognizers.push(recog);for(var i=0;i<recog.emits.length;i++){this.gestures[recog.emits[i]]=recog;}},findRecognizerByEvent:function(evName){for(var i=0,r;i<this.recognizers.length;i++){r=this.recognizers[i];for(var j=0,n;j<r.emits.length;j++){n=r.emits[j];if(n===evName){return r;}}}
+return null;},setTouchAction:function(node,value){if(HAS_NATIVE_TA){node.style.touchAction=value;}
+node[TOUCH_ACTION]=value;},fire:function(target,type,detail){var ev=Polymer.Base.fire(type,detail,{node:target,bubbles:true,cancelable:true});if(ev.defaultPrevented){var preventer=detail.preventer||detail.sourceEvent;if(preventer&&preventer.preventDefault){preventer.preventDefault();}}},prevent:function(evName){var recognizer=this.findRecognizerByEvent(evName);if(recognizer.info){recognizer.info.prevent=true;}},resetMouseCanceller:function(){if(POINTERSTATE.mouse.mouseIgnoreJob){POINTERSTATE.mouse.mouseIgnoreJob.complete();}}};Gestures.register({name:'downup',deps:['mousedown','touchstart','touchend'],flow:{start:['mousedown','touchstart'],end:['mouseup','touchend']},emits:['down','up'],info:{movefn:null,upfn:null},reset:function(){untrackDocument(this.info);},mousedown:function(e){if(!hasLeftMouseButton(e)){return;}
+var t=Gestures.findOriginalTarget(e);var self=this;var movefn=function movefn(e){if(!hasLeftMouseButton(e)){self.fire('up',t,e);untrackDocument(self.info);}};var upfn=function upfn(e){if(hasLeftMouseButton(e)){self.fire('up',t,e);}
+untrackDocument(self.info);};trackDocument(this.info,movefn,upfn);this.fire('down',t,e);},touchstart:function(e){this.fire('down',Gestures.findOriginalTarget(e),e.changedTouches[0],e);},touchend:function(e){this.fire('up',Gestures.findOriginalTarget(e),e.changedTouches[0],e);},fire:function(type,target,event,preventer){Gestures.fire(target,type,{x:event.clientX,y:event.clientY,sourceEvent:event,preventer:preventer,prevent:function(e){return Gestures.prevent(e);}});}});Gestures.register({name:'track',touchAction:'none',deps:['mousedown','touchstart','touchmove','touchend'],flow:{start:['mousedown','touchstart'],end:['mouseup','touchend']},emits:['track'],info:{x:0,y:0,state:'start',started:false,moves:[],addMove:function(move){if(this.moves.length>TRACK_LENGTH){this.moves.shift();}
+this.moves.push(move);},movefn:null,upfn:null,prevent:false},reset:function(){this.info.state='start';this.info.started=false;this.info.moves=[];this.info.x=0;this.info.y=0;this.info.prevent=false;untrackDocument(this.info);},hasMovedEnough:function(x,y){if(this.info.prevent){return false;}
+if(this.info.started){return true;}
+var dx=Math.abs(this.info.x-x);var dy=Math.abs(this.info.y-y);return dx>=TRACK_DISTANCE||dy>=TRACK_DISTANCE;},mousedown:function(e){if(!hasLeftMouseButton(e)){return;}
+var t=Gestures.findOriginalTarget(e);var self=this;var movefn=function movefn(e){var x=e.clientX,y=e.clientY;if(self.hasMovedEnough(x,y)){self.info.state=self.info.started?e.type==='mouseup'?'end':'track':'start';if(self.info.state==='start'){Gestures.prevent('tap');}
+self.info.addMove({x:x,y:y});if(!hasLeftMouseButton(e)){self.info.state='end';untrackDocument(self.info);}
+self.fire(t,e);self.info.started=true;}};var upfn=function upfn(e){if(self.info.started){movefn(e);}
+untrackDocument(self.info);};trackDocument(this.info,movefn,upfn);this.info.x=e.clientX;this.info.y=e.clientY;},touchstart:function(e){var ct=e.changedTouches[0];this.info.x=ct.clientX;this.info.y=ct.clientY;},touchmove:function(e){var t=Gestures.findOriginalTarget(e);var ct=e.changedTouches[0];var x=ct.clientX,y=ct.clientY;if(this.hasMovedEnough(x,y)){if(this.info.state==='start'){Gestures.prevent('tap');}
+this.info.addMove({x:x,y:y});this.fire(t,ct);this.info.state='track';this.info.started=true;}},touchend:function(e){var t=Gestures.findOriginalTarget(e);var ct=e.changedTouches[0];if(this.info.started){this.info.state='end';this.info.addMove({x:ct.clientX,y:ct.clientY});this.fire(t,ct,e);}},fire:function(target,touch,preventer){var secondlast=this.info.moves[this.info.moves.length-2];var lastmove=this.info.moves[this.info.moves.length-1];var dx=lastmove.x-this.info.x;var dy=lastmove.y-this.info.y;var ddx,ddy=0;if(secondlast){ddx=lastmove.x-secondlast.x;ddy=lastmove.y-secondlast.y;}
+return Gestures.fire(target,'track',{state:this.info.state,x:touch.clientX,y:touch.clientY,dx:dx,dy:dy,ddx:ddx,ddy:ddy,sourceEvent:touch,preventer:preventer,hover:function(){return Gestures.deepTargetFind(touch.clientX,touch.clientY);}});}});Gestures.register({name:'tap',deps:['mousedown','click','touchstart','touchend'],flow:{start:['mousedown','touchstart'],end:['click','touchend']},emits:['tap'],info:{x:NaN,y:NaN,prevent:false},reset:function(){this.info.x=NaN;this.info.y=NaN;this.info.prevent=false;},save:function(e){this.info.x=e.clientX;this.info.y=e.clientY;},mousedown:function(e){if(hasLeftMouseButton(e)){this.save(e);}},click:function(e){if(hasLeftMouseButton(e)){this.forward(e);}},touchstart:function(e){this.save(e.changedTouches[0],e);},touchend:function(e){this.forward(e.changedTouches[0],e);},forward:function(e,preventer){var dx=Math.abs(e.clientX-this.info.x);var dy=Math.abs(e.clientY-this.info.y);var t=Gestures.findOriginalTarget(e);if(isNaN(dx)||isNaN(dy)||dx<=TAP_DISTANCE&&dy<=TAP_DISTANCE||isSyntheticClick(e)){if(!this.info.prevent){Gestures.fire(t,'tap',{x:e.clientX,y:e.clientY,sourceEvent:e,preventer:preventer});}}}});var DIRECTION_MAP={x:'pan-x',y:'pan-y',none:'none',all:'auto'};Polymer.Base._addFeature({_setupGestures:function(){this.__polymerGestures=null;},_listen:function(node,eventName,handler){if(Gestures.gestures[eventName]){Gestures.add(node,eventName,handler);}else{node.addEventListener(eventName,handler);}},_unlisten:function(node,eventName,handler){if(Gestures.gestures[eventName]){Gestures.remove(node,eventName,handler);}else{node.removeEventListener(eventName,handler);}},setScrollDirection:function(direction,node){node=node||this;Gestures.setTouchAction(node,DIRECTION_MAP[direction]||'auto');}});Polymer.Gestures=Gestures;}());(function(){'use strict';Polymer.Base._addFeature({$$:function(slctr){return Polymer.dom(this.root).querySelector(slctr);},toggleClass:function(name,bool,node){node=node||this;if(arguments.length==1){bool=!node.classList.contains(name);}
+if(bool){Polymer.dom(node).classList.add(name);}else{Polymer.dom(node).classList.remove(name);}},toggleAttribute:function(name,bool,node){node=node||this;if(arguments.length==1){bool=!node.hasAttribute(name);}
+if(bool){Polymer.dom(node).setAttribute(name,'');}else{Polymer.dom(node).removeAttribute(name);}},classFollows:function(name,toElement,fromElement){if(fromElement){Polymer.dom(fromElement).classList.remove(name);}
+if(toElement){Polymer.dom(toElement).classList.add(name);}},attributeFollows:function(name,toElement,fromElement){if(fromElement){Polymer.dom(fromElement).removeAttribute(name);}
+if(toElement){Polymer.dom(toElement).setAttribute(name,'');}},getEffectiveChildNodes:function(){return Polymer.dom(this).getEffectiveChildNodes();},getEffectiveChildren:function(){var list=Polymer.dom(this).getEffectiveChildNodes();return list.filter(function(n){return n.nodeType===Node.ELEMENT_NODE;});},getEffectiveTextContent:function(){var cn=this.getEffectiveChildNodes();var tc=[];for(var i=0,c;c=cn[i];i++){if(c.nodeType!==Node.COMMENT_NODE){tc.push(Polymer.dom(c).textContent);}}
+return tc.join('');},queryEffectiveChildren:function(slctr){var e$=Polymer.dom(this).queryDistributedElements(slctr);return e$&&e$[0];},queryAllEffectiveChildren:function(slctr){return Polymer.dom(this).queryDistributedElements(slctr);},getContentChildNodes:function(slctr){var content=Polymer.dom(this.root).querySelector(slctr||'content');return content?Polymer.dom(content).getDistributedNodes():[];},getContentChildren:function(slctr){return this.getContentChildNodes(slctr).filter(function(n){return n.nodeType===Node.ELEMENT_NODE;});},fire:function(type,detail,options){options=options||Polymer.nob;var node=options.node||this;detail=detail===null||detail===undefined?{}:detail;var bubbles=options.bubbles===undefined?true:options.bubbles;var cancelable=Boolean(options.cancelable);var useCache=options._useCache;var event=this._getEvent(type,bubbles,cancelable,useCache);event.detail=detail;if(useCache){this.__eventCache[type]=null;}
+node.dispatchEvent(event);if(useCache){this.__eventCache[type]=event;}
+return event;},__eventCache:{},_getEvent:function(type,bubbles,cancelable,useCache){var event=useCache&&this.__eventCache[type];if(!event||(event.bubbles!=bubbles||event.cancelable!=cancelable)){event=new Event(type,{bubbles:Boolean(bubbles),cancelable:cancelable});}
+return event;},async:function(callback,waitTime){var self=this;return Polymer.Async.run(function(){callback.call(self);},waitTime);},cancelAsync:function(handle){Polymer.Async.cancel(handle);},arrayDelete:function(path,item){var index;if(Array.isArray(path)){index=path.indexOf(item);if(index>=0){return path.splice(index,1);}}else{var arr=this._get(path);index=arr.indexOf(item);if(index>=0){return this.splice(path,index,1);}}},transform:function(transform,node){node=node||this;node.style.webkitTransform=transform;node.style.transform=transform;},translate3d:function(x,y,z,node){node=node||this;this.transform('translate3d('+x+','+y+','+z+')',node);},importHref:function(href,onload,onerror,optAsync){var link=document.createElement('link');link.rel='import';link.href=href;var list=Polymer.Base.importHref.imported=Polymer.Base.importHref.imported||{};var cached=list[link.href];var imprt=cached||link;var self=this;var loadListener=function(e){e.target.__firedLoad=true;e.target.removeEventListener('load',loadListener);e.target.removeEventListener('error',errorListener);return onload.call(self,e);};var errorListener=function(e){e.target.__firedError=true;e.target.removeEventListener('load',loadListener);e.target.removeEventListener('error',errorListener);return onerror.call(self,e);};if(onload){imprt.addEventListener('load',loadListener);}
+if(onerror){imprt.addEventListener('error',errorListener);}
+if(cached){if(cached.__firedLoad){cached.dispatchEvent(new Event('load'));}
+if(cached.__firedError){cached.dispatchEvent(new Event('error'));}}else{list[link.href]=link;optAsync=Boolean(optAsync);if(optAsync){link.setAttribute('async','');}
+document.head.appendChild(link);}
+return imprt;},create:function(tag,props){var elt=document.createElement(tag);if(props){for(var n in props){elt[n]=props[n];}}
+return elt;},isLightDescendant:function(node){return this!==node&&this.contains(node)&&Polymer.dom(this).getOwnerRoot()===Polymer.dom(node).getOwnerRoot();},isLocalDescendant:function(node){return this.root===Polymer.dom(node).getOwnerRoot();}});if(!Polymer.Settings.useNativeCustomElements){var importHref=Polymer.Base.importHref;Polymer.Base.importHref=function(href,onload,onerror,optAsync){CustomElements.ready=false;var loadFn=function(e){CustomElements.upgradeDocumentTree(document);CustomElements.ready=true;if(onload){return onload.call(this,e);}};return importHref.call(this,href,loadFn,onerror,optAsync);};}}());Polymer.Bind={prepareModel:function(model){Polymer.Base.mixin(model,this._modelApi);},_modelApi:{_notifyChange:function(source,event,value){value=value===undefined?this[source]:value;event=event||Polymer.CaseMap.camelToDashCase(source)+'-changed';this.fire(event,{value:value},{bubbles:false,cancelable:false,_useCache:Polymer.Settings.eventDataCache||!Polymer.Settings.isIE});},_propertySetter:function(property,value,effects,fromAbove){var old=this.__data__[property];if(old!==value&&(old===old||value===value)){this.__data__[property]=value;if(typeof value=='object'){this._clearPath(property);}
+if(this._propertyChanged){this._propertyChanged(property,value,old);}
+if(effects){this._effectEffects(property,value,effects,old,fromAbove);}}
+return old;},__setProperty:function(property,value,quiet,node){node=node||this;var effects=node._propertyEffects&&node._propertyEffects[property];if(effects){node._propertySetter(property,value,effects,quiet);}else if(node[property]!==value){node[property]=value;}},_effectEffects:function(property,value,effects,old,fromAbove){for(var i=0,l=effects.length,fx;i<l&&(fx=effects[i]);i++){fx.fn.call(this,property,this[property],fx.effect,old,fromAbove);}},_clearPath:function(path){for(var prop in this.__data__){if(Polymer.Path.isDescendant(path,prop)){this.__data__[prop]=undefined;}}}},ensurePropertyEffects:function(model,property){if(!model._propertyEffects){model._propertyEffects={};}
+var fx=model._propertyEffects[property];if(!fx){fx=model._propertyEffects[property]=[];}
+return fx;},addPropertyEffect:function(model,property,kind,effect){var fx=this.ensurePropertyEffects(model,property);var propEffect={kind:kind,effect:effect,fn:Polymer.Bind['_'+kind+'Effect']};fx.push(propEffect);return propEffect;},createBindings:function(model){var fx$=model._propertyEffects;if(fx$){for(var n in fx$){var fx=fx$[n];fx.sort(this._sortPropertyEffects);this._createAccessors(model,n,fx);}}},_sortPropertyEffects:function(){var EFFECT_ORDER={'compute':0,'annotation':1,'annotatedComputation':2,'reflect':3,'notify':4,'observer':5,'complexObserver':6,'function':7};return function(a,b){return EFFECT_ORDER[a.kind]-EFFECT_ORDER[b.kind];};}(),_createAccessors:function(model,property,effects){var defun={get:function(){return this.__data__[property];}};var setter=function(value){this._propertySetter(property,value,effects);};var info=model.getPropertyInfo&&model.getPropertyInfo(property);if(info&&info.readOnly){if(!info.computed){model['_set'+this.upper(property)]=setter;}}else{defun.set=setter;}
+Object.defineProperty(model,property,defun);},upper:function(name){return name[0].toUpperCase()+name.substring(1);},_addAnnotatedListener:function(model,index,property,path,event,negated){if(!model._bindListeners){model._bindListeners=[];}
+var fn=this._notedListenerFactory(property,path,Polymer.Path.isDeep(path),negated);var eventName=event||Polymer.CaseMap.camelToDashCase(property)+'-changed';model._bindListeners.push({index:index,property:property,path:path,changedFn:fn,event:eventName});},_isEventBogus:function(e,target){return e.path&&e.path[0]!==target;},_notedListenerFactory:function(property,path,isStructured,negated){return function(target,value,targetPath){if(targetPath){var newPath=Polymer.Path.translate(property,path,targetPath);this._notifyPath(newPath,value);}else{value=target[property];if(negated){value=!value;}
+if(!isStructured){this[path]=value;}else{if(this.__data__[path]!=value){this.set(path,value);}}}};},prepareInstance:function(inst){inst.__data__=Object.create(null);},setupBindListeners:function(inst){var b$=inst._bindListeners;for(var i=0,l=b$.length,info;i<l&&(info=b$[i]);i++){var node=inst._nodes[info.index];this._addNotifyListener(node,inst,info.event,info.changedFn);}},_addNotifyListener:function(element,context,event,changedFn){element.addEventListener(event,function(e){return context._notifyListener(changedFn,e);});}};Polymer.Base.mixin(Polymer.Bind,{_shouldAddListener:function(effect){return effect.name&&effect.kind!='attribute'&&effect.kind!='text'&&!effect.isCompound&&effect.parts[0].mode==='{';},_annotationEffect:function(source,value,effect){if(source!=effect.value){value=this._get(effect.value);this.__data__[effect.value]=value;}
+this._applyEffectValue(effect,value);},_reflectEffect:function(source,value,effect){this.reflectPropertyToAttribute(source,effect.attribute,value);},_notifyEffect:function(source,value,effect,old,fromAbove){if(!fromAbove){this._notifyChange(source,effect.event,value);}},_functionEffect:function(source,value,fn,old,fromAbove){fn.call(this,source,value,old,fromAbove);},_observerEffect:function(source,value,effect,old){var fn=this[effect.method];if(fn){fn.call(this,value,old);}else{this._warn(this._logf('_observerEffect','observer method `'+effect.method+'` not defined'));}},_complexObserverEffect:function(source,value,effect){var fn=this[effect.method];if(fn){var args=Polymer.Bind._marshalArgs(this.__data__,effect,source,value);if(args){fn.apply(this,args);}}else if(effect.dynamicFn){}else{this._warn(this._logf('_complexObserverEffect','observer method `'+effect.method+'` not defined'));}},_computeEffect:function(source,value,effect){var fn=this[effect.method];if(fn){var args=Polymer.Bind._marshalArgs(this.__data__,effect,source,value);if(args){var computedvalue=fn.apply(this,args);this.__setProperty(effect.name,computedvalue);}}else if(effect.dynamicFn){}else{this._warn(this._logf('_computeEffect','compute method `'+effect.method+'` not defined'));}},_annotatedComputationEffect:function(source,value,effect){var computedHost=this._rootDataHost||this;var fn=computedHost[effect.method];if(fn){var args=Polymer.Bind._marshalArgs(this.__data__,effect,source,value);if(args){var computedvalue=fn.apply(computedHost,args);this._applyEffectValue(effect,computedvalue);}}else if(effect.dynamicFn){}else{computedHost._warn(computedHost._logf('_annotatedComputationEffect','compute method `'+effect.method+'` not defined'));}},_marshalArgs:function(model,effect,path,value){var values=[];var args=effect.args;var bailoutEarly=args.length>1||effect.dynamicFn;for(var i=0,l=args.length;i<l;i++){var arg=args[i];var name=arg.name;var v;if(arg.literal){v=arg.value;}else if(path===name){v=value;}else{v=model[name];if(v===undefined&&arg.structured){v=Polymer.Base._get(name,model);}}
+if(bailoutEarly&&v===undefined){return;}
+if(arg.wildcard){var matches=Polymer.Path.isAncestor(path,name);values[i]={path:matches?path:name,value:matches?value:v,base:v};}else{values[i]=v;}}
+return values;}});Polymer.Base._addFeature({_addPropertyEffect:function(property,kind,effect){var prop=Polymer.Bind.addPropertyEffect(this,property,kind,effect);prop.pathFn=this['_'+prop.kind+'PathEffect'];},_prepEffects:function(){Polymer.Bind.prepareModel(this);this._addAnnotationEffects(this._notes);},_prepBindings:function(){Polymer.Bind.createBindings(this);},_addPropertyEffects:function(properties){if(properties){for(var p in properties){var prop=properties[p];if(prop.observer){this._addObserverEffect(p,prop.observer);}
+if(prop.computed){prop.readOnly=true;this._addComputedEffect(p,prop.computed);}
+if(prop.notify){this._addPropertyEffect(p,'notify',{event:Polymer.CaseMap.camelToDashCase(p)+'-changed'});}
+if(prop.reflectToAttribute){var attr=Polymer.CaseMap.camelToDashCase(p);if(attr[0]==='-'){this._warn(this._logf('_addPropertyEffects','Property '+p+' cannot be reflected to attribute '+attr+' because "-" is not a valid starting attribute name. Use a lowercase first letter for the property instead.'));}else{this._addPropertyEffect(p,'reflect',{attribute:attr});}}
+if(prop.readOnly){Polymer.Bind.ensurePropertyEffects(this,p);}}}},_addComputedEffect:function(name,expression){var sig=this._parseMethod(expression);var dynamicFn=sig.dynamicFn;for(var i=0,arg;i<sig.args.length&&(arg=sig.args[i]);i++){this._addPropertyEffect(arg.model,'compute',{method:sig.method,args:sig.args,trigger:arg,name:name,dynamicFn:dynamicFn});}
+if(dynamicFn){this._addPropertyEffect(sig.method,'compute',{method:sig.method,args:sig.args,trigger:null,name:name,dynamicFn:dynamicFn});}},_addObserverEffect:function(property,observer){this._addPropertyEffect(property,'observer',{method:observer,property:property});},_addComplexObserverEffects:function(observers){if(observers){for(var i=0,o;i<observers.length&&(o=observers[i]);i++){this._addComplexObserverEffect(o);}}},_addComplexObserverEffect:function(observer){var sig=this._parseMethod(observer);if(!sig){throw new Error('Malformed observer expression \''+observer+'\'');}
+var dynamicFn=sig.dynamicFn;for(var i=0,arg;i<sig.args.length&&(arg=sig.args[i]);i++){this._addPropertyEffect(arg.model,'complexObserver',{method:sig.method,args:sig.args,trigger:arg,dynamicFn:dynamicFn});}
+if(dynamicFn){this._addPropertyEffect(sig.method,'complexObserver',{method:sig.method,args:sig.args,trigger:null,dynamicFn:dynamicFn});}},_addAnnotationEffects:function(notes){for(var i=0,note;i<notes.length&&(note=notes[i]);i++){var b$=note.bindings;for(var j=0,binding;j<b$.length&&(binding=b$[j]);j++){this._addAnnotationEffect(binding,i);}}},_addAnnotationEffect:function(note,index){if(Polymer.Bind._shouldAddListener(note)){Polymer.Bind._addAnnotatedListener(this,index,note.name,note.parts[0].value,note.parts[0].event,note.parts[0].negate);}
+for(var i=0;i<note.parts.length;i++){var part=note.parts[i];if(part.signature){this._addAnnotatedComputationEffect(note,part,index);}else if(!part.literal){if(note.kind==='attribute'&&note.name[0]==='-'){this._warn(this._logf('_addAnnotationEffect','Cannot set attribute '+note.name+' because "-" is not a valid attribute starting character'));}else{this._addPropertyEffect(part.model,'annotation',{kind:note.kind,index:index,name:note.name,propertyName:note.propertyName,value:part.value,isCompound:note.isCompound,compoundIndex:part.compoundIndex,event:part.event,customEvent:part.customEvent,negate:part.negate});}}}},_addAnnotatedComputationEffect:function(note,part,index){var sig=part.signature;if(sig.static){this.__addAnnotatedComputationEffect('__static__',index,note,part,null);}else{for(var i=0,arg;i<sig.args.length&&(arg=sig.args[i]);i++){if(!arg.literal){this.__addAnnotatedComputationEffect(arg.model,index,note,part,arg);}}
+if(sig.dynamicFn){this.__addAnnotatedComputationEffect(sig.method,index,note,part,null);}}},__addAnnotatedComputationEffect:function(property,index,note,part,trigger){this._addPropertyEffect(property,'annotatedComputation',{index:index,isCompound:note.isCompound,compoundIndex:part.compoundIndex,kind:note.kind,name:note.name,negate:part.negate,method:part.signature.method,args:part.signature.args,trigger:trigger,dynamicFn:part.signature.dynamicFn});},_parseMethod:function(expression){var m=expression.match(/([^\s]+?)\(([\s\S]*)\)/);if(m){var sig={method:m[1],static:true};if(this.getPropertyInfo(sig.method)!==Polymer.nob){sig.static=false;sig.dynamicFn=true;}
+if(m[2].trim()){var args=m[2].replace(/\\,/g,'&comma;').split(',');return this._parseArgs(args,sig);}else{sig.args=Polymer.nar;return sig;}}},_parseArgs:function(argList,sig){sig.args=argList.map(function(rawArg){var arg=this._parseArg(rawArg);if(!arg.literal){sig.static=false;}
+return arg;},this);return sig;},_parseArg:function(rawArg){var arg=rawArg.trim().replace(/&comma;/g,',').replace(/\\(.)/g,'$1');var a={name:arg};var fc=arg[0];if(fc==='-'){fc=arg[1];}
+if(fc>='0'&&fc<='9'){fc='#';}
+switch(fc){case'\'':case'"':a.value=arg.slice(1,-1);a.literal=true;break;case'#':a.value=Number(arg);a.literal=true;break;}
+if(!a.literal){a.model=Polymer.Path.root(arg);a.structured=Polymer.Path.isDeep(arg);if(a.structured){a.wildcard=arg.slice(-2)=='.*';if(a.wildcard){a.name=arg.slice(0,-2);}}}
+return a;},_marshalInstanceEffects:function(){Polymer.Bind.prepareInstance(this);if(this._bindListeners){Polymer.Bind.setupBindListeners(this);}},_applyEffectValue:function(info,value){var node=this._nodes[info.index];var property=info.name;value=this._computeFinalAnnotationValue(node,property,value,info);if(info.kind=='attribute'){this.serializeValueToAttribute(value,property,node);}else{var pinfo=node._propertyInfo&&node._propertyInfo[property];if(pinfo&&pinfo.readOnly){return;}
+this.__setProperty(property,value,Polymer.Settings.suppressBindingNotifications,node);}},_computeFinalAnnotationValue:function(node,property,value,info){if(info.negate){value=!value;}
+if(info.isCompound){var storage=node.__compoundStorage__[property];storage[info.compoundIndex]=value;value=storage.join('');}
+if(info.kind!=='attribute'){if(property==='className'){value=this._scopeElementClass(node,value);}
+if(property==='textContent'||node.localName=='input'&&property=='value'){value=value==undefined?'':value;}}
+return value;},_executeStaticEffects:function(){if(this._propertyEffects&&this._propertyEffects.__static__){this._effectEffects('__static__',null,this._propertyEffects.__static__);}}});(function(){var usePolyfillProto=Polymer.Settings.usePolyfillProto;var avoidInstanceProperties=Boolean(Object.getOwnPropertyDescriptor(document.documentElement,'properties'));Polymer.Base._addFeature({_setupConfigure:function(initialConfig){this._config={};this._handlers=[];this._aboveConfig=null;if(initialConfig){for(var i in initialConfig){if(initialConfig[i]!==undefined){this._config[i]=initialConfig[i];}}}},_marshalAttributes:function(){this._takeAttributesToModel(this._config);},_attributeChangedImpl:function(name){var model=this._clientsReadied?this:this._config;this._setAttributeToProperty(model,name);},_configValue:function(name,value){var info=this._propertyInfo[name];if(!info||!info.readOnly){this._config[name]=value;}},_beforeClientsReady:function(){this._configure();},_configure:function(){this._configureAnnotationReferences();this._configureInstanceProperties();this._aboveConfig=this.mixin({},this._config);var config={};for(var i=0;i<this.behaviors.length;i++){this._configureProperties(this.behaviors[i].properties,config);}
+this._configureProperties(avoidInstanceProperties?this.__proto__.properties:this.properties,config);this.mixin(config,this._aboveConfig);this._config=config;if(this._clients&&this._clients.length){this._distributeConfig(this._config);}},_configureInstanceProperties:function(){for(var i in this._propertyEffects){if(!usePolyfillProto&&this.hasOwnProperty(i)){this._configValue(i,this[i]);delete this[i];}}},_configureProperties:function(properties,config){for(var i in properties){var c=properties[i];if(c.value!==undefined){var value=c.value;if(typeof value=='function'){value=value.call(this,this._config);}
+config[i]=value;}}},_distributeConfig:function(config){var fx$=this._propertyEffects;if(fx$){for(var p in config){var fx=fx$[p];if(fx){for(var i=0,l=fx.length,x;i<l&&(x=fx[i]);i++){if(x.kind==='annotation'){var node=this._nodes[x.effect.index];var name=x.effect.propertyName;var isAttr=x.effect.kind=='attribute';var hasEffect=node._propertyEffects&&node._propertyEffects[name];if(node._configValue&&(hasEffect||!isAttr)){var value=p===x.effect.value?config[p]:this._get(x.effect.value,config);value=this._computeFinalAnnotationValue(node,name,value,x.effect);if(isAttr){value=node.deserialize(this.serialize(value),node._propertyInfo[name].type);}
+node._configValue(name,value);}}}}}}},_afterClientsReady:function(){this.importPath=this._importPath;this.rootPath=Polymer.rootPath;this._executeStaticEffects();this._applyConfig(this._config,this._aboveConfig);this._flushHandlers();},_applyConfig:function(config,aboveConfig){for(var n in config){if(this[n]===undefined){this.__setProperty(n,config[n],n in aboveConfig);}}},_notifyListener:function(fn,e){if(!Polymer.Bind._isEventBogus(e,e.target)){var value,path;if(e.detail){value=e.detail.value;path=e.detail.path;}
+if(!this._clientsReadied){this._queueHandler([fn,e.target,value,path]);}else{return fn.call(this,e.target,value,path);}}},_queueHandler:function(args){this._handlers.push(args);},_flushHandlers:function(){var h$=this._handlers;for(var i=0,l=h$.length,h;i<l&&(h=h$[i]);i++){h[0].call(this,h[1],h[2],h[3]);}
+this._handlers=[];}});}());(function(){'use strict';var Path=Polymer.Path;Polymer.Base._addFeature({notifyPath:function(path,value,fromAbove){var info={};var v=this._get(path,this,info);if(arguments.length===1){value=v;}
+if(info.path){this._notifyPath(info.path,value,fromAbove);}},_notifyPath:function(path,value,fromAbove){var old=this._propertySetter(path,value);if(old!==value&&(old===old||value===value)){this._pathEffector(path,value);if(!fromAbove){this._notifyPathUp(path,value);}
+return true;}},_getPathParts:function(path){if(Array.isArray(path)){var parts=[];for(var i=0;i<path.length;i++){var args=path[i].toString().split('.');for(var j=0;j<args.length;j++){parts.push(args[j]);}}
+return parts;}else{return path.toString().split('.');}},set:function(path,value,root){var prop=root||this;var parts=this._getPathParts(path);var array;var last=parts[parts.length-1];if(parts.length>1){for(var i=0;i<parts.length-1;i++){var part=parts[i];if(array&&part[0]=='#'){prop=Polymer.Collection.get(array).getItem(part);}else{prop=prop[part];if(array&&parseInt(part,10)==part){parts[i]=Polymer.Collection.get(array).getKey(prop);}}
+if(!prop){return;}
+array=Array.isArray(prop)?prop:null;}
+if(array){var coll=Polymer.Collection.get(array);var old,key;if(last[0]=='#'){key=last;old=coll.getItem(key);last=array.indexOf(old);coll.setItem(key,value);}else if(parseInt(last,10)==last){old=prop[last];key=coll.getKey(old);parts[i]=key;coll.setItem(key,value);}}
+prop[last]=value;if(!root){this._notifyPath(parts.join('.'),value);}}else{prop[path]=value;}},get:function(path,root){return this._get(path,root);},_get:function(path,root,info){var prop=root||this;var parts=this._getPathParts(path);var array;for(var i=0;i<parts.length;i++){if(!prop){return;}
+var part=parts[i];if(array&&part[0]=='#'){prop=Polymer.Collection.get(array).getItem(part);}else{prop=prop[part];if(info&&array&&parseInt(part,10)==part){parts[i]=Polymer.Collection.get(array).getKey(prop);}}
+array=Array.isArray(prop)?prop:null;}
+if(info){info.path=parts.join('.');}
+return prop;},_pathEffector:function(path,value){var model=Path.root(path);var fx$=this._propertyEffects&&this._propertyEffects[model];if(fx$){for(var i=0,fx;i<fx$.length&&(fx=fx$[i]);i++){var fxFn=fx.pathFn;if(fxFn){fxFn.call(this,path,value,fx.effect);}}}
+if(this._boundPaths){this._notifyBoundPaths(path,value);}},_annotationPathEffect:function(path,value,effect){if(Path.matches(effect.value,false,path)){Polymer.Bind._annotationEffect.call(this,path,value,effect);}else if(!effect.negate&&Path.isDescendant(effect.value,path)){var node=this._nodes[effect.index];if(node&&node._notifyPath){var newPath=Path.translate(effect.value,effect.name,path);node._notifyPath(newPath,value,true);}}},_complexObserverPathEffect:function(path,value,effect){if(Path.matches(effect.trigger.name,effect.trigger.wildcard,path)){Polymer.Bind._complexObserverEffect.call(this,path,value,effect);}},_computePathEffect:function(path,value,effect){if(Path.matches(effect.trigger.name,effect.trigger.wildcard,path)){Polymer.Bind._computeEffect.call(this,path,value,effect);}},_annotatedComputationPathEffect:function(path,value,effect){if(Path.matches(effect.trigger.name,effect.trigger.wildcard,path)){Polymer.Bind._annotatedComputationEffect.call(this,path,value,effect);}},linkPaths:function(to,from){this._boundPaths=this._boundPaths||{};if(from){this._boundPaths[to]=from;}else{this.unlinkPaths(to);}},unlinkPaths:function(path){if(this._boundPaths){delete this._boundPaths[path];}},_notifyBoundPaths:function(path,value){for(var a in this._boundPaths){var b=this._boundPaths[a];if(Path.isDescendant(a,path)){this._notifyPath(Path.translate(a,b,path),value);}else if(Path.isDescendant(b,path)){this._notifyPath(Path.translate(b,a,path),value);}}},_notifyPathUp:function(path,value){var rootName=Path.root(path);var dashCaseName=Polymer.CaseMap.camelToDashCase(rootName);var eventName=dashCaseName+this._EVENT_CHANGED;this.fire(eventName,{path:path,value:value},{bubbles:false,_useCache:Polymer.Settings.eventDataCache||!Polymer.Settings.isIE});},_EVENT_CHANGED:'-changed',notifySplices:function(path,splices){var info={};var array=this._get(path,this,info);this._notifySplices(array,info.path,splices);},_notifySplices:function(array,path,splices){var change={keySplices:Polymer.Collection.applySplices(array,splices),indexSplices:splices};var splicesPath=path+'.splices';this._notifyPath(splicesPath,change);this._notifyPath(path+'.length',array.length);this.__data__[splicesPath]={keySplices:null,indexSplices:null};},_notifySplice:function(array,path,index,added,removed){this._notifySplices(array,path,[{index:index,addedCount:added,removed:removed,object:array,type:'splice'}]);},push:function(path){var info={};var array=this._get(path,this,info);var args=Array.prototype.slice.call(arguments,1);var len=array.length;var ret=array.push.apply(array,args);if(args.length){this._notifySplice(array,info.path,len,args.length,[]);}
+return ret;},pop:function(path){var info={};var array=this._get(path,this,info);var hadLength=Boolean(array.length);var args=Array.prototype.slice.call(arguments,1);var ret=array.pop.apply(array,args);if(hadLength){this._notifySplice(array,info.path,array.length,0,[ret]);}
+return ret;},splice:function(path,start){var info={};var array=this._get(path,this,info);if(start<0){start=array.length-Math.floor(-start);}else{start=Math.floor(start);}
+if(!start){start=0;}
+var args=Array.prototype.slice.call(arguments,1);var ret=array.splice.apply(array,args);var addedCount=Math.max(args.length-2,0);if(addedCount||ret.length){this._notifySplice(array,info.path,start,addedCount,ret);}
+return ret;},shift:function(path){var info={};var array=this._get(path,this,info);var hadLength=Boolean(array.length);var args=Array.prototype.slice.call(arguments,1);var ret=array.shift.apply(array,args);if(hadLength){this._notifySplice(array,info.path,0,0,[ret]);}
+return ret;},unshift:function(path){var info={};var array=this._get(path,this,info);var args=Array.prototype.slice.call(arguments,1);var ret=array.unshift.apply(array,args);if(args.length){this._notifySplice(array,info.path,0,args.length,[]);}
+return ret;},prepareModelNotifyPath:function(model){this.mixin(model,{fire:Polymer.Base.fire,_getEvent:Polymer.Base._getEvent,__eventCache:Polymer.Base.__eventCache,notifyPath:Polymer.Base.notifyPath,_get:Polymer.Base._get,_EVENT_CHANGED:Polymer.Base._EVENT_CHANGED,_notifyPath:Polymer.Base._notifyPath,_notifyPathUp:Polymer.Base._notifyPathUp,_pathEffector:Polymer.Base._pathEffector,_annotationPathEffect:Polymer.Base._annotationPathEffect,_complexObserverPathEffect:Polymer.Base._complexObserverPathEffect,_annotatedComputationPathEffect:Polymer.Base._annotatedComputationPathEffect,_computePathEffect:Polymer.Base._computePathEffect,_notifyBoundPaths:Polymer.Base._notifyBoundPaths,_getPathParts:Polymer.Base._getPathParts});}});}());Polymer.Base._addFeature({resolveUrl:function(url){return Polymer.ResolveUrl.resolveUrl(url,this._importPath);}});Polymer.CssParse=function(){return{parse:function(text){text=this._clean(text);return this._parseCss(this._lex(text),text);},_clean:function(cssText){return cssText.replace(this._rx.comments,'').replace(this._rx.port,'');},_lex:function(text){var root={start:0,end:text.length};var n=root;for(var i=0,l=text.length;i<l;i++){switch(text[i]){case this.OPEN_BRACE:if(!n.rules){n.rules=[];}
+var p=n;var previous=p.rules[p.rules.length-1];n={start:i+1,parent:p,previous:previous};p.rules.push(n);break;case this.CLOSE_BRACE:n.end=i+1;n=n.parent||root;break;}}
+return root;},_parseCss:function(node,text){var t=text.substring(node.start,node.end-1);node.parsedCssText=node.cssText=t.trim();if(node.parent){var ss=node.previous?node.previous.end:node.parent.start;t=text.substring(ss,node.start-1);t=this._expandUnicodeEscapes(t);t=t.replace(this._rx.multipleSpaces,' ');t=t.substring(t.lastIndexOf(';')+1);var s=node.parsedSelector=node.selector=t.trim();node.atRule=s.indexOf(this.AT_START)===0;if(node.atRule){if(s.indexOf(this.MEDIA_START)===0){node.type=this.types.MEDIA_RULE;}else if(s.match(this._rx.keyframesRule)){node.type=this.types.KEYFRAMES_RULE;node.keyframesName=node.selector.split(this._rx.multipleSpaces).pop();}}else{if(s.indexOf(this.VAR_START)===0){node.type=this.types.MIXIN_RULE;}else{node.type=this.types.STYLE_RULE;}}}
+var r$=node.rules;if(r$){for(var i=0,l=r$.length,r;i<l&&(r=r$[i]);i++){this._parseCss(r,text);}}
+return node;},_expandUnicodeEscapes:function(s){return s.replace(/\\([0-9a-f]{1,6})\s/gi,function(){var code=arguments[1],repeat=6-code.length;while(repeat--){code='0'+code;}
+return'\\'+code;});},stringify:function(node,preserveProperties,text){text=text||'';var cssText='';if(node.cssText||node.rules){var r$=node.rules;if(r$&&!this._hasMixinRules(r$)){for(var i=0,l=r$.length,r;i<l&&(r=r$[i]);i++){cssText=this.stringify(r,preserveProperties,cssText);}}else{cssText=preserveProperties?node.cssText:this.removeCustomProps(node.cssText);cssText=cssText.trim();if(cssText){cssText='  '+cssText+'\n';}}}
+if(cssText){if(node.selector){text+=node.selector+' '+this.OPEN_BRACE+'\n';}
+text+=cssText;if(node.selector){text+=this.CLOSE_BRACE+'\n\n';}}
+return text;},_hasMixinRules:function(rules){return rules[0].selector.indexOf(this.VAR_START)===0;},removeCustomProps:function(cssText){cssText=this.removeCustomPropAssignment(cssText);return this.removeCustomPropApply(cssText);},removeCustomPropAssignment:function(cssText){return cssText.replace(this._rx.customProp,'').replace(this._rx.mixinProp,'');},removeCustomPropApply:function(cssText){return cssText.replace(this._rx.mixinApply,'').replace(this._rx.varApply,'');},types:{STYLE_RULE:1,KEYFRAMES_RULE:7,MEDIA_RULE:4,MIXIN_RULE:1000},OPEN_BRACE:'{',CLOSE_BRACE:'}',_rx:{comments:/\/\*[^*]*\*+([^\/*][^*]*\*+)*\//gim,port:/@import[^;]*;/gim,customProp:/(?:^[^;\-\s}]+)?--[^;{}]*?:[^{};]*?(?:[;\n]|$)/gim,mixinProp:/(?:^[^;\-\s}]+)?--[^;{}]*?:[^{};]*?{[^}]*?}(?:[;\n]|$)?/gim,mixinApply:/@apply\s*\(?[^);]*\)?\s*(?:[;\n]|$)?/gim,varApply:/[^;:]*?:[^;]*?var\([^;]*\)(?:[;\n]|$)?/gim,keyframesRule:/^@[^\s]*keyframes/,multipleSpaces:/\s+/g},VAR_START:'--',MEDIA_START:'@media',AT_START:'@'};}();Polymer.StyleUtil=function(){var settings=Polymer.Settings;return{unscopedStyleImports:new WeakMap(),SHADY_UNSCOPED_ATTR:'shady-unscoped',NATIVE_VARIABLES:Polymer.Settings.useNativeCSSProperties,MODULE_STYLES_SELECTOR:'style, link[rel=import][type~=css], template',INCLUDE_ATTR:'include',toCssText:function(rules,callback){if(typeof rules==='string'){rules=this.parser.parse(rules);}
+if(callback){this.forEachRule(rules,callback);}
+return this.parser.stringify(rules,this.NATIVE_VARIABLES);},forRulesInStyles:function(styles,styleRuleCallback,keyframesRuleCallback){if(styles){for(var i=0,l=styles.length,s;i<l&&(s=styles[i]);i++){this.forEachRuleInStyle(s,styleRuleCallback,keyframesRuleCallback);}}},forActiveRulesInStyles:function(styles,styleRuleCallback,keyframesRuleCallback){if(styles){for(var i=0,l=styles.length,s;i<l&&(s=styles[i]);i++){this.forEachRuleInStyle(s,styleRuleCallback,keyframesRuleCallback,true);}}},rulesForStyle:function(style){if(!style.__cssRules&&style.textContent){style.__cssRules=this.parser.parse(style.textContent);}
+return style.__cssRules;},isKeyframesSelector:function(rule){return rule.parent&&rule.parent.type===this.ruleTypes.KEYFRAMES_RULE;},forEachRuleInStyle:function(style,styleRuleCallback,keyframesRuleCallback,onlyActiveRules){var rules=this.rulesForStyle(style);var styleCallback,keyframeCallback;if(styleRuleCallback){styleCallback=function(rule){styleRuleCallback(rule,style);};}
+if(keyframesRuleCallback){keyframeCallback=function(rule){keyframesRuleCallback(rule,style);};}
+this.forEachRule(rules,styleCallback,keyframeCallback,onlyActiveRules);},forEachRule:function(node,styleRuleCallback,keyframesRuleCallback,onlyActiveRules){if(!node){return;}
+var skipRules=false;if(onlyActiveRules){if(node.type===this.ruleTypes.MEDIA_RULE){var matchMedia=node.selector.match(this.rx.MEDIA_MATCH);if(matchMedia){if(!window.matchMedia(matchMedia[1]).matches){skipRules=true;}}}}
+if(node.type===this.ruleTypes.STYLE_RULE){styleRuleCallback(node);}else if(keyframesRuleCallback&&node.type===this.ruleTypes.KEYFRAMES_RULE){keyframesRuleCallback(node);}else if(node.type===this.ruleTypes.MIXIN_RULE){skipRules=true;}
+var r$=node.rules;if(r$&&!skipRules){for(var i=0,l=r$.length,r;i<l&&(r=r$[i]);i++){this.forEachRule(r,styleRuleCallback,keyframesRuleCallback,onlyActiveRules);}}},applyCss:function(cssText,moniker,target,contextNode){var style=this.createScopeStyle(cssText,moniker);return this.applyStyle(style,target,contextNode);},applyStyle:function(style,target,contextNode){target=target||document.head;var after=contextNode&&contextNode.nextSibling||target.firstChild;this.__lastHeadApplyNode=style;return target.insertBefore(style,after);},createScopeStyle:function(cssText,moniker){var style=document.createElement('style');if(moniker){style.setAttribute('scope',moniker);}
+style.textContent=cssText;return style;},__lastHeadApplyNode:null,applyStylePlaceHolder:function(moniker){var placeHolder=document.createComment(' Shady DOM styles for '+moniker+' ');var after=this.__lastHeadApplyNode?this.__lastHeadApplyNode.nextSibling:null;var scope=document.head;scope.insertBefore(placeHolder,after||scope.firstChild);this.__lastHeadApplyNode=placeHolder;return placeHolder;},cssFromModules:function(moduleIds,warnIfNotFound){var modules=moduleIds.trim().split(/\s+/);var cssText='';for(var i=0;i<modules.length;i++){cssText+=this.cssFromModule(modules[i],warnIfNotFound);}
+return cssText;},cssFromModule:function(moduleId,warnIfNotFound){var m=Polymer.DomModule.import(moduleId);if(m&&!m._cssText){m._cssText=this.cssFromElement(m);}
+if(!m&&warnIfNotFound){console.warn('Could not find style data in module named',moduleId);}
+return m&&m._cssText||'';},cssFromElement:function(element){var cssText='';var content=element.content||element;var e$=Polymer.TreeApi.arrayCopy(content.querySelectorAll(this.MODULE_STYLES_SELECTOR));for(var i=0,e;i<e$.length;i++){e=e$[i];if(e.localName==='template'){if(!e.hasAttribute('preserve-content')){cssText+=this.cssFromElement(e);}}else{if(e.localName==='style'){var include=e.getAttribute(this.INCLUDE_ATTR);if(include){cssText+=this.cssFromModules(include,true);}
+e=e.__appliedElement||e;e.parentNode.removeChild(e);var css=this.resolveCss(e.textContent,element.ownerDocument);if(!settings.useNativeShadow&&e.hasAttribute(this.SHADY_UNSCOPED_ATTR)){e.textContent=css;document.head.insertBefore(e,document.head.firstChild);}else{cssText+=css;}}else if(e.import&&e.import.body){var importCss=this.resolveCss(e.import.body.textContent,e.import);if(!settings.useNativeShadow&&e.hasAttribute(this.SHADY_UNSCOPED_ATTR)){if(!this.unscopedStyleImports.has(e.import)){this.unscopedStyleImports.set(e.import,true);var importStyle=document.createElement('style');importStyle.setAttribute(this.SHADY_UNSCOPED_ATTR,'');importStyle.textContent=importCss;document.head.insertBefore(importStyle,document.head.firstChild);}}else{cssText+=importCss;}}}}
+return cssText;},styleIncludesToTemplate:function(targetTemplate){var styles=targetTemplate.content.querySelectorAll('style[include]');for(var i=0,s;i<styles.length;i++){s=styles[i];s.parentNode.insertBefore(this._includesToFragment(s.getAttribute('include')),s);}},_includesToFragment:function(styleIncludes){var includeArray=styleIncludes.trim().split(' ');var frag=document.createDocumentFragment();for(var i=0;i<includeArray.length;i++){var t=Polymer.DomModule.import(includeArray[i],'template');if(t){this._addStylesToFragment(frag,t.content);}}
+return frag;},_addStylesToFragment:function(frag,source){var s$=source.querySelectorAll('style');for(var i=0,s;i<s$.length;i++){s=s$[i];var include=s.getAttribute('include');if(include){frag.appendChild(this._includesToFragment(include));}
+if(s.textContent){frag.appendChild(s.cloneNode(true));}}},isTargetedBuild:function(buildType){return settings.useNativeShadow?buildType==='shadow':buildType==='shady';},cssBuildTypeForModule:function(module){var dm=Polymer.DomModule.import(module);if(dm){return this.getCssBuildType(dm);}},getCssBuildType:function(element){return element.getAttribute('css-build');},_findMatchingParen:function(text,start){var level=0;for(var i=start,l=text.length;i<l;i++){switch(text[i]){case'(':level++;break;case')':if(--level===0){return i;}
+break;}}
+return-1;},processVariableAndFallback:function(str,callback){var start=str.indexOf('var(');if(start===-1){return callback(str,'','','');}
+var end=this._findMatchingParen(str,start+3);var inner=str.substring(start+4,end);var prefix=str.substring(0,start);var suffix=this.processVariableAndFallback(str.substring(end+1),callback);var comma=inner.indexOf(',');if(comma===-1){return callback(prefix,inner.trim(),'',suffix);}
+var value=inner.substring(0,comma).trim();var fallback=inner.substring(comma+1).trim();return callback(prefix,value,fallback,suffix);},rx:{VAR_ASSIGN:/(?:^|[;\s{]\s*)(--[\w-]*?)\s*:\s*(?:([^;{]*)|{([^}]*)})(?:(?=[;\s}])|$)/gi,MIXIN_MATCH:/(?:^|\W+)@apply\s*\(?([^);\n]*)\)?/gi,VAR_CONSUMED:/(--[\w-]+)\s*([:,;)]|$)/gi,ANIMATION_MATCH:/(animation\s*:)|(animation-name\s*:)/,MEDIA_MATCH:/@media[^(]*(\([^)]*\))/,IS_VAR:/^--/,BRACKETED:/\{[^}]*\}/g,HOST_PREFIX:'(?:^|[^.#[:])',HOST_SUFFIX:'($|[.:[\\s>+~])'},resolveCss:Polymer.ResolveUrl.resolveCss,parser:Polymer.CssParse,ruleTypes:Polymer.CssParse.types};}();Polymer.StyleTransformer=function(){var styleUtil=Polymer.StyleUtil;var settings=Polymer.Settings;var api={dom:function(node,scope,useAttr,shouldRemoveScope){this._transformDom(node,scope||'',useAttr,shouldRemoveScope);},_transformDom:function(node,selector,useAttr,shouldRemoveScope){if(node.setAttribute){this.element(node,selector,useAttr,shouldRemoveScope);}
+var c$=Polymer.dom(node).childNodes;for(var i=0;i<c$.length;i++){this._transformDom(c$[i],selector,useAttr,shouldRemoveScope);}},element:function(element,scope,useAttr,shouldRemoveScope){if(useAttr){if(shouldRemoveScope){element.removeAttribute(SCOPE_NAME);}else{element.setAttribute(SCOPE_NAME,scope);}}else{if(scope){if(element.classList){if(shouldRemoveScope){element.classList.remove(SCOPE_NAME);element.classList.remove(scope);}else{element.classList.add(SCOPE_NAME);element.classList.add(scope);}}else if(element.getAttribute){var c=element.getAttribute(CLASS);if(shouldRemoveScope){if(c){element.setAttribute(CLASS,c.replace(SCOPE_NAME,'').replace(scope,''));}}else{element.setAttribute(CLASS,(c?c+' ':'')+SCOPE_NAME+' '+scope);}}}}},elementStyles:function(element,callback){var styles=element._styles;var cssText='';var cssBuildType=element.__cssBuild;var passthrough=settings.useNativeShadow||cssBuildType==='shady';var cb;if(passthrough){var self=this;cb=function(rule){rule.selector=self._slottedToContent(rule.selector);rule.selector=rule.selector.replace(ROOT,':host > *');rule.selector=self._dirShadowTransform(rule.selector);if(callback){callback(rule);}};}
+for(var i=0,l=styles.length,s;i<l&&(s=styles[i]);i++){var rules=styleUtil.rulesForStyle(s);cssText+=passthrough?styleUtil.toCssText(rules,cb):this.css(rules,element.is,element.extends,callback,element._scopeCssViaAttr)+'\n\n';}
+return cssText.trim();},css:function(rules,scope,ext,callback,useAttr){var hostScope=this._calcHostScope(scope,ext);scope=this._calcElementScope(scope,useAttr);var self=this;return styleUtil.toCssText(rules,function(rule){if(!rule.isScoped){self.rule(rule,scope,hostScope);rule.isScoped=true;}
+if(callback){callback(rule,scope,hostScope);}});},_calcElementScope:function(scope,useAttr){if(scope){return useAttr?CSS_ATTR_PREFIX+scope+CSS_ATTR_SUFFIX:CSS_CLASS_PREFIX+scope;}else{return'';}},_calcHostScope:function(scope,ext){return ext?'[is='+scope+']':scope;},rule:function(rule,scope,hostScope){this._transformRule(rule,this._transformComplexSelector,scope,hostScope);},_transformRule:function(rule,transformer,scope,hostScope){rule.selector=rule.transformedSelector=this._transformRuleCss(rule,transformer,scope,hostScope);},_splitSelectorList:function(selector){var parts=[];var part='';for(var i=0;i>=0&&i<selector.length;i++){if(selector[i]==='('){var end=styleUtil._findMatchingParen(selector,i);part+=selector.slice(i,end+1);i=end;}else if(selector[i]===COMPLEX_SELECTOR_SEP){parts.push(part);part='';}else{part+=selector[i];}}
+if(part){parts.push(part);}
+if(parts.length===0){parts.push(selector);}
+return parts;},_transformRuleCss:function(rule,transformer,scope,hostScope){var p$=this._splitSelectorList(rule.selector);if(!styleUtil.isKeyframesSelector(rule)){for(var i=0,l=p$.length,p;i<l&&(p=p$[i]);i++){p$[i]=transformer.call(this,p,scope,hostScope);}}
+return p$.join(COMPLEX_SELECTOR_SEP);},_ensureScopedDir:function(s){var m=s.match(DIR_PAREN);if(m&&m[1]===''&&m[0].length===s.length){s='*'+s;}
+return s;},_additionalDirSelectors:function(dir,after,prefix){if(!dir||!after){return'';}
+prefix=prefix||'';return COMPLEX_SELECTOR_SEP+prefix+' '+dir+' '+after;},_transformComplexSelector:function(selector,scope,hostScope){var stop=false;var hostContext=false;var dir=false;var self=this;selector=selector.trim();selector=this._slottedToContent(selector);selector=selector.replace(ROOT,':host > *');selector=selector.replace(CONTENT_START,HOST+' $1');selector=this._ensureScopedDir(selector);selector=selector.replace(SIMPLE_SELECTOR_SEP,function(m,c,s){if(!stop){var info=self._transformCompoundSelector(s,c,scope,hostScope);stop=stop||info.stop;hostContext=hostContext||info.hostContext;dir=dir||info.dir;c=info.combinator;s=info.value;}else{s=s.replace(SCOPE_JUMP,' ');}
+return c+s;});if(hostContext){selector=selector.replace(HOST_CONTEXT_PAREN,function(m,pre,paren,post){var replacement=pre+paren+' '+hostScope+post+COMPLEX_SELECTOR_SEP+' '+pre+hostScope+paren+post;if(dir){replacement+=self._additionalDirSelectors(paren,post,hostScope);}
+return replacement;});}
+return selector;},_transformDir:function(s){s=s.replace(HOST_DIR,HOST_DIR_REPLACE);s=s.replace(DIR_PAREN,DIR_REPLACE);return s;},_transformCompoundSelector:function(selector,combinator,scope,hostScope){var jumpIndex=selector.search(SCOPE_JUMP);var hostContext=false;var dir=false;if(selector.match(DIR_PAREN)){selector=this._transformDir(selector);dir=true;}
+if(selector.indexOf(HOST_CONTEXT)>=0){hostContext=true;}else if(selector.indexOf(HOST)>=0){selector=this._transformHostSelector(selector,hostScope);}else if(jumpIndex!==0){selector=scope?this._transformSimpleSelector(selector,scope):selector;}
+if(selector.indexOf(CONTENT)>=0){combinator='';}
+var stop;if(jumpIndex>=0){selector=selector.replace(SCOPE_JUMP,' ');stop=true;}
+return{value:selector,combinator:combinator,stop:stop,hostContext:hostContext,dir:dir};},_transformSimpleSelector:function(selector,scope){var p$=selector.split(PSEUDO_PREFIX);p$[0]+=scope;return p$.join(PSEUDO_PREFIX);},_transformHostSelector:function(selector,hostScope){var m=selector.match(HOST_PAREN);var paren=m&&m[2].trim()||'';if(paren){if(!paren[0].match(SIMPLE_SELECTOR_PREFIX)){var typeSelector=paren.split(SIMPLE_SELECTOR_PREFIX)[0];if(typeSelector===hostScope){return paren;}else{return SELECTOR_NO_MATCH;}}else{return selector.replace(HOST_PAREN,function(m,host,paren){return hostScope+paren;});}}else{return selector.replace(HOST,hostScope);}},documentRule:function(rule){rule.selector=rule.parsedSelector;this.normalizeRootSelector(rule);if(!settings.useNativeShadow){this._transformRule(rule,this._transformDocumentSelector);}},normalizeRootSelector:function(rule){rule.selector=rule.selector.replace(ROOT,'html');var parts=this._splitSelectorList(rule.selector);parts=parts.filter(function(part){return!part.match(HOST_OR_HOST_GT_STAR);});rule.selector=parts.join(COMPLEX_SELECTOR_SEP);},_transformDocumentSelector:function(selector){return this._transformComplexSelector(selector,SCOPE_DOC_SELECTOR);},_slottedToContent:function(cssText){return cssText.replace(SLOTTED_PAREN,CONTENT+'> $1');},_dirShadowTransform:function(selector){if(!selector.match(/:dir\(/)){return selector;}
+return this._splitSelectorList(selector).map(function(s){s=this._ensureScopedDir(s);s=this._transformDir(s);var m=HOST_CONTEXT_PAREN.exec(s);if(m){s+=this._additionalDirSelectors(m[2],m[3],'');}
+return s;},this).join(COMPLEX_SELECTOR_SEP);},SCOPE_NAME:'style-scope'};var SCOPE_NAME=api.SCOPE_NAME;var SCOPE_DOC_SELECTOR=':not(['+SCOPE_NAME+'])'+':not(.'+SCOPE_NAME+')';var COMPLEX_SELECTOR_SEP=',';var SIMPLE_SELECTOR_SEP=/(^|[\s>+~]+)((?:\[.+?\]|[^\s>+~=\[])+)/g;var SIMPLE_SELECTOR_PREFIX=/[[.:#*]/;var HOST=':host';var ROOT=':root';var HOST_PAREN=/(:host)(?:\(((?:\([^)(]*\)|[^)(]*)+?)\))/;var HOST_CONTEXT=':host-context';var HOST_CONTEXT_PAREN=/(.*)(?::host-context)(?:\(((?:\([^)(]*\)|[^)(]*)+?)\))(.*)/;var CONTENT='::content';var SCOPE_JUMP=/::content|::shadow|\/deep\//;var CSS_CLASS_PREFIX='.';var CSS_ATTR_PREFIX='['+SCOPE_NAME+'~=';var CSS_ATTR_SUFFIX=']';var PSEUDO_PREFIX=':';var CLASS='class';var CONTENT_START=new RegExp('^('+CONTENT+')');var SELECTOR_NO_MATCH='should_not_match';var SLOTTED_PAREN=/(?:::slotted)(?:\(((?:\([^)(]*\)|[^)(]*)+?)\))/g;var HOST_OR_HOST_GT_STAR=/:host(?:\s*>\s*\*)?/;var DIR_PAREN=/(.*):dir\((ltr|rtl)\)/;var DIR_REPLACE=':host-context([dir="$2"]) $1';var HOST_DIR=/:host\(:dir\((rtl|ltr)\)\)/g;var HOST_DIR_REPLACE=':host-context([dir="$1"])';return api;}();Polymer.StyleExtends=function(){var styleUtil=Polymer.StyleUtil;return{hasExtends:function(cssText){return Boolean(cssText.match(this.rx.EXTEND));},transform:function(style){var rules=styleUtil.rulesForStyle(style);var self=this;styleUtil.forEachRule(rules,function(rule){self._mapRuleOntoParent(rule);if(rule.parent){var m;while(m=self.rx.EXTEND.exec(rule.cssText)){var extend=m[1];var extendor=self._findExtendor(extend,rule);if(extendor){self._extendRule(rule,extendor);}}}
+rule.cssText=rule.cssText.replace(self.rx.EXTEND,'');});return styleUtil.toCssText(rules,function(rule){if(rule.selector.match(self.rx.STRIP)){rule.cssText='';}},true);},_mapRuleOntoParent:function(rule){if(rule.parent){var map=rule.parent.map||(rule.parent.map={});var parts=rule.selector.split(',');for(var i=0,p;i<parts.length;i++){p=parts[i];map[p.trim()]=rule;}
+return map;}},_findExtendor:function(extend,rule){return rule.parent&&rule.parent.map&&rule.parent.map[extend]||this._findExtendor(extend,rule.parent);},_extendRule:function(target,source){if(target.parent!==source.parent){this._cloneAndAddRuleToParent(source,target.parent);}
+target.extends=target.extends||[];target.extends.push(source);source.selector=source.selector.replace(this.rx.STRIP,'');source.selector=(source.selector&&source.selector+',\n')+target.selector;if(source.extends){source.extends.forEach(function(e){this._extendRule(target,e);},this);}},_cloneAndAddRuleToParent:function(rule,parent){rule=Object.create(rule);rule.parent=parent;if(rule.extends){rule.extends=rule.extends.slice();}
+parent.rules.push(rule);},rx:{EXTEND:/@extends\(([^)]*)\)\s*?;/gim,STRIP:/%[^,]*$/}};}();Polymer.ApplyShim=function(){'use strict';var styleUtil=Polymer.StyleUtil;var MIXIN_MATCH=styleUtil.rx.MIXIN_MATCH;var VAR_ASSIGN=styleUtil.rx.VAR_ASSIGN;var BAD_VAR=/var\(\s*(--[^,]*),\s*(--[^)]*)\)/g;var APPLY_NAME_CLEAN=/;\s*/m;var INITIAL_INHERIT=/^\s*(initial)|(inherit)\s*$/;var MIXIN_VAR_SEP='_-_';var mixinMap={};function mapSet(name,props){name=name.trim();mixinMap[name]={properties:props,dependants:{}};}
+function mapGet(name){name=name.trim();return mixinMap[name];}
+function replaceInitialOrInherit(property,value){var match=INITIAL_INHERIT.exec(value);if(match){if(match[1]){value=ApplyShim._getInitialValueForProperty(property);}else{value='apply-shim-inherit';}}
+return value;}
+function cssTextToMap(text){var props=text.split(';');var property,value;var out={};for(var i=0,p,sp;i<props.length;i++){p=props[i];if(p){sp=p.split(':');if(sp.length>1){property=sp[0].trim();value=replaceInitialOrInherit(property,sp.slice(1).join(':'));out[property]=value;}}}
+return out;}
+function invalidateMixinEntry(mixinEntry){var currentProto=ApplyShim.__currentElementProto;var currentElementName=currentProto&&currentProto.is;for(var elementName in mixinEntry.dependants){if(elementName!==currentElementName){mixinEntry.dependants[elementName].__applyShimInvalid=true;}}}
+function produceCssProperties(matchText,propertyName,valueProperty,valueMixin){if(valueProperty){styleUtil.processVariableAndFallback(valueProperty,function(prefix,value){if(value&&mapGet(value)){valueMixin='@apply '+value+';';}});}
+if(!valueMixin){return matchText;}
+var mixinAsProperties=consumeCssProperties(valueMixin);var prefix=matchText.slice(0,matchText.indexOf('--'));var mixinValues=cssTextToMap(mixinAsProperties);var combinedProps=mixinValues;var mixinEntry=mapGet(propertyName);var oldProps=mixinEntry&&mixinEntry.properties;if(oldProps){combinedProps=Object.create(oldProps);combinedProps=Polymer.Base.mixin(combinedProps,mixinValues);}else{mapSet(propertyName,combinedProps);}
+var out=[];var p,v;var needToInvalidate=false;for(p in combinedProps){v=mixinValues[p];if(v===undefined){v='initial';}
+if(oldProps&&!(p in oldProps)){needToInvalidate=true;}
+out.push(propertyName+MIXIN_VAR_SEP+p+': '+v);}
+if(needToInvalidate){invalidateMixinEntry(mixinEntry);}
+if(mixinEntry){mixinEntry.properties=combinedProps;}
+if(valueProperty){prefix=matchText+';'+prefix;}
+return prefix+out.join('; ')+';';}
+function fixVars(matchText,varA,varB){return'var('+varA+','+'var('+varB+'))';}
+function atApplyToCssProperties(mixinName,fallbacks){mixinName=mixinName.replace(APPLY_NAME_CLEAN,'');var vars=[];var mixinEntry=mapGet(mixinName);if(!mixinEntry){mapSet(mixinName,{});mixinEntry=mapGet(mixinName);}
+if(mixinEntry){var currentProto=ApplyShim.__currentElementProto;if(currentProto){mixinEntry.dependants[currentProto.is]=currentProto;}
+var p,parts,f;for(p in mixinEntry.properties){f=fallbacks&&fallbacks[p];parts=[p,': var(',mixinName,MIXIN_VAR_SEP,p];if(f){parts.push(',',f);}
+parts.push(')');vars.push(parts.join(''));}}
+return vars.join('; ');}
+function consumeCssProperties(text){var m;while(m=MIXIN_MATCH.exec(text)){var matchText=m[0];var mixinName=m[1];var idx=m.index;var applyPos=idx+matchText.indexOf('@apply');var afterApplyPos=idx+matchText.length;var textBeforeApply=text.slice(0,applyPos);var textAfterApply=text.slice(afterApplyPos);var defaults=cssTextToMap(textBeforeApply);var replacement=atApplyToCssProperties(mixinName,defaults);text=[textBeforeApply,replacement,textAfterApply].join('');MIXIN_MATCH.lastIndex=idx+replacement.length;}
+return text;}
+var ApplyShim={_measureElement:null,_map:mixinMap,_separator:MIXIN_VAR_SEP,transform:function(styles,elementProto){this.__currentElementProto=elementProto;styleUtil.forRulesInStyles(styles,this._boundFindDefinitions);styleUtil.forRulesInStyles(styles,this._boundFindApplications);if(elementProto){elementProto.__applyShimInvalid=false;}
+this.__currentElementProto=null;},_findDefinitions:function(rule){var cssText=rule.parsedCssText;cssText=cssText.replace(BAD_VAR,fixVars);cssText=cssText.replace(VAR_ASSIGN,produceCssProperties);rule.cssText=cssText;if(rule.selector===':root'){rule.selector=':host > *';}},_findApplications:function(rule){rule.cssText=consumeCssProperties(rule.cssText);},transformRule:function(rule){this._findDefinitions(rule);this._findApplications(rule);},_getInitialValueForProperty:function(property){if(!this._measureElement){this._measureElement=document.createElement('meta');this._measureElement.style.all='initial';document.head.appendChild(this._measureElement);}
+return window.getComputedStyle(this._measureElement).getPropertyValue(property);}};ApplyShim._boundTransformRule=ApplyShim.transformRule.bind(ApplyShim);ApplyShim._boundFindDefinitions=ApplyShim._findDefinitions.bind(ApplyShim);ApplyShim._boundFindApplications=ApplyShim._findApplications.bind(ApplyShim);return ApplyShim;}();(function(){var prepElement=Polymer.Base._prepElement;var nativeShadow=Polymer.Settings.useNativeShadow;var styleUtil=Polymer.StyleUtil;var styleTransformer=Polymer.StyleTransformer;var styleExtends=Polymer.StyleExtends;var applyShim=Polymer.ApplyShim;var settings=Polymer.Settings;Polymer.Base._addFeature({_prepElement:function(element){if(this._encapsulateStyle&&this.__cssBuild!=='shady'){styleTransformer.element(element,this.is,this._scopeCssViaAttr);}
+prepElement.call(this,element);},_prepStyles:function(){if(this._encapsulateStyle===undefined){this._encapsulateStyle=!nativeShadow;}
+if(!nativeShadow){this._scopeStyle=styleUtil.applyStylePlaceHolder(this.is);}
+this.__cssBuild=styleUtil.cssBuildTypeForModule(this.is);},_prepShimStyles:function(){if(this._template){var hasTargetedCssBuild=styleUtil.isTargetedBuild(this.__cssBuild);if(settings.useNativeCSSProperties&&this.__cssBuild==='shadow'&&hasTargetedCssBuild){if(settings.preserveStyleIncludes){styleUtil.styleIncludesToTemplate(this._template);}
+return;}
+this._styles=this._styles||this._collectStyles();if(settings.useNativeCSSProperties&&!this.__cssBuild){applyShim.transform(this._styles,this);}
+var cssText=settings.useNativeCSSProperties&&hasTargetedCssBuild?this._styles.length&&this._styles[0].textContent.trim():styleTransformer.elementStyles(this);this._prepStyleProperties();if(!this._needsStyleProperties()&&cssText){styleUtil.applyCss(cssText,this.is,nativeShadow?this._template.content:null,this._scopeStyle);}}else{this._styles=[];}},_collectStyles:function(){var styles=[];var cssText='',m$=this.styleModules;if(m$){for(var i=0,l=m$.length,m;i<l&&(m=m$[i]);i++){cssText+=styleUtil.cssFromModule(m);}}
+cssText+=styleUtil.cssFromModule(this.is);var p=this._template&&this._template.parentNode;if(this._template&&(!p||p.id.toLowerCase()!==this.is)){cssText+=styleUtil.cssFromElement(this._template);}
+if(cssText){var style=document.createElement('style');style.textContent=cssText;if(styleExtends.hasExtends(style.textContent)){cssText=styleExtends.transform(style);}
+styles.push(style);}
+return styles;},_elementAdd:function(node){if(this._encapsulateStyle){if(node.__styleScoped){node.__styleScoped=false;}else{styleTransformer.dom(node,this.is,this._scopeCssViaAttr);}}},_elementRemove:function(node){if(this._encapsulateStyle){styleTransformer.dom(node,this.is,this._scopeCssViaAttr,true);}},scopeSubtree:function(container,shouldObserve){if(nativeShadow){return;}
+var self=this;var scopify=function(node){if(node.nodeType===Node.ELEMENT_NODE){var className=node.getAttribute('class');node.setAttribute('class',self._scopeElementClass(node,className));var n$=node.querySelectorAll('*');for(var i=0,n;i<n$.length&&(n=n$[i]);i++){className=n.getAttribute('class');n.setAttribute('class',self._scopeElementClass(n,className));}}};scopify(container);if(shouldObserve){var mo=new MutationObserver(function(mxns){for(var i=0,m;i<mxns.length&&(m=mxns[i]);i++){if(m.addedNodes){for(var j=0;j<m.addedNodes.length;j++){scopify(m.addedNodes[j]);}}}});mo.observe(container,{childList:true,subtree:true});return mo;}}});}());Polymer.StyleProperties=function(){'use strict';var matchesSelector=Polymer.DomApi.matchesSelector;var styleUtil=Polymer.StyleUtil;var styleTransformer=Polymer.StyleTransformer;var IS_IE=navigator.userAgent.match('Trident');var settings=Polymer.Settings;return{decorateStyles:function(styles,scope){var self=this,props={},keyframes=[],ruleIndex=0;var scopeSelector=styleTransformer._calcHostScope(scope.is,scope.extends);styleUtil.forRulesInStyles(styles,function(rule,style){self.decorateRule(rule);rule.index=ruleIndex++;self.whenHostOrRootRule(scope,rule,style,function(info){if(rule.parent.type===styleUtil.ruleTypes.MEDIA_RULE){scope.__notStyleScopeCacheable=true;}
+if(info.isHost){var hostContextOrFunction=info.selector.split(' ').some(function(s){return s.indexOf(scopeSelector)===0&&s.length!==scopeSelector.length;});scope.__notStyleScopeCacheable=scope.__notStyleScopeCacheable||hostContextOrFunction;}});self.collectPropertiesInCssText(rule.propertyInfo.cssText,props);},function onKeyframesRule(rule){keyframes.push(rule);});styles._keyframes=keyframes;var names=[];for(var i in props){names.push(i);}
+return names;},decorateRule:function(rule){if(rule.propertyInfo){return rule.propertyInfo;}
+var info={},properties={};var hasProperties=this.collectProperties(rule,properties);if(hasProperties){info.properties=properties;rule.rules=null;}
+info.cssText=this.collectCssText(rule);rule.propertyInfo=info;return info;},collectProperties:function(rule,properties){var info=rule.propertyInfo;if(info){if(info.properties){Polymer.Base.mixin(properties,info.properties);return true;}}else{var m,rx=this.rx.VAR_ASSIGN;var cssText=rule.parsedCssText;var value;var any;while(m=rx.exec(cssText)){value=(m[2]||m[3]).trim();if(value!=='inherit'){properties[m[1].trim()]=value;}
+any=true;}
+return any;}},collectCssText:function(rule){return this.collectConsumingCssText(rule.parsedCssText);},collectConsumingCssText:function(cssText){return cssText.replace(this.rx.BRACKETED,'').replace(this.rx.VAR_ASSIGN,'');},collectPropertiesInCssText:function(cssText,props){var m;while(m=this.rx.VAR_CONSUMED.exec(cssText)){var name=m[1];if(m[2]!==':'){props[name]=true;}}},reify:function(props){var names=Object.getOwnPropertyNames(props);for(var i=0,n;i<names.length;i++){n=names[i];props[n]=this.valueForProperty(props[n],props);}},valueForProperty:function(property,props){if(property){if(property.indexOf(';')>=0){property=this.valueForProperties(property,props);}else{var self=this;var fn=function(prefix,value,fallback,suffix){var propertyValue=self.valueForProperty(props[value],props);if(!propertyValue||propertyValue==='initial'){propertyValue=self.valueForProperty(props[fallback]||fallback,props)||fallback;}else if(propertyValue==='apply-shim-inherit'){propertyValue='inherit';}
+return prefix+(propertyValue||'')+suffix;};property=styleUtil.processVariableAndFallback(property,fn);}}
+return property&&property.trim()||'';},valueForProperties:function(property,props){var parts=property.split(';');for(var i=0,p,m;i<parts.length;i++){if(p=parts[i]){this.rx.MIXIN_MATCH.lastIndex=0;m=this.rx.MIXIN_MATCH.exec(p);if(m){p=this.valueForProperty(props[m[1]],props);}else{var colon=p.indexOf(':');if(colon!==-1){var pp=p.substring(colon);pp=pp.trim();pp=this.valueForProperty(pp,props)||pp;p=p.substring(0,colon)+pp;}}
+parts[i]=p&&p.lastIndexOf(';')===p.length-1?p.slice(0,-1):p||'';}}
+return parts.join(';');},applyProperties:function(rule,props){var output='';if(!rule.propertyInfo){this.decorateRule(rule);}
+if(rule.propertyInfo.cssText){output=this.valueForProperties(rule.propertyInfo.cssText,props);}
+rule.cssText=output;},applyKeyframeTransforms:function(rule,keyframeTransforms){var input=rule.cssText;var output=rule.cssText;if(rule.hasAnimations==null){rule.hasAnimations=this.rx.ANIMATION_MATCH.test(input);}
+if(rule.hasAnimations){var transform;if(rule.keyframeNamesToTransform==null){rule.keyframeNamesToTransform=[];for(var keyframe in keyframeTransforms){transform=keyframeTransforms[keyframe];output=transform(input);if(input!==output){input=output;rule.keyframeNamesToTransform.push(keyframe);}}}else{for(var i=0;i<rule.keyframeNamesToTransform.length;++i){transform=keyframeTransforms[rule.keyframeNamesToTransform[i]];input=transform(input);}
+output=input;}}
+rule.cssText=output;},propertyDataFromStyles:function(styles,element){var props={},self=this;var o=[];styleUtil.forActiveRulesInStyles(styles,function(rule){if(!rule.propertyInfo){self.decorateRule(rule);}
+var selectorToMatch=rule.transformedSelector||rule.parsedSelector;if(element&&rule.propertyInfo.properties&&selectorToMatch){if(matchesSelector.call(element,selectorToMatch)){self.collectProperties(rule,props);addToBitMask(rule.index,o);}}});return{properties:props,key:o};},_rootSelector:/:root|:host\s*>\s*\*/,_checkRoot:function(hostScope,selector){return Boolean(selector.match(this._rootSelector))||hostScope==='html'&&selector.indexOf('html')>-1;},whenHostOrRootRule:function(scope,rule,style,callback){if(!rule.propertyInfo){self.decorateRule(rule);}
+if(!rule.propertyInfo.properties){return;}
+var hostScope=scope.is?styleTransformer._calcHostScope(scope.is,scope.extends):'html';var parsedSelector=rule.parsedSelector;var isRoot=this._checkRoot(hostScope,parsedSelector);var isHost=!isRoot&&parsedSelector.indexOf(':host')===0;var cssBuild=scope.__cssBuild||style.__cssBuild;if(cssBuild==='shady'){isRoot=parsedSelector===hostScope+' > *.'+hostScope||parsedSelector.indexOf('html')>-1;isHost=!isRoot&&parsedSelector.indexOf(hostScope)===0;}
+if(!isRoot&&!isHost){return;}
+var selectorToMatch=hostScope;if(isHost){if(settings.useNativeShadow&&!rule.transformedSelector){rule.transformedSelector=styleTransformer._transformRuleCss(rule,styleTransformer._transformComplexSelector,scope.is,hostScope);}
+selectorToMatch=rule.transformedSelector||rule.parsedSelector;}
+if(isRoot&&hostScope==='html'){selectorToMatch=rule.transformedSelector||rule.parsedSelector;}
+callback({selector:selectorToMatch,isHost:isHost,isRoot:isRoot});},hostAndRootPropertiesForScope:function(scope){var hostProps={},rootProps={},self=this;styleUtil.forActiveRulesInStyles(scope._styles,function(rule,style){self.whenHostOrRootRule(scope,rule,style,function(info){var element=scope._element||scope;if(matchesSelector.call(element,info.selector)){if(info.isHost){self.collectProperties(rule,hostProps);}else{self.collectProperties(rule,rootProps);}}});});return{rootProps:rootProps,hostProps:hostProps};},transformStyles:function(element,properties,scopeSelector){var self=this;var hostSelector=styleTransformer._calcHostScope(element.is,element.extends);var rxHostSelector=element.extends?'\\'+hostSelector.slice(0,-1)+'\\]':hostSelector;var hostRx=new RegExp(this.rx.HOST_PREFIX+rxHostSelector+this.rx.HOST_SUFFIX);var keyframeTransforms=this._elementKeyframeTransforms(element,scopeSelector);return styleTransformer.elementStyles(element,function(rule){self.applyProperties(rule,properties);if(!settings.useNativeShadow&&!Polymer.StyleUtil.isKeyframesSelector(rule)&&rule.cssText){self.applyKeyframeTransforms(rule,keyframeTransforms);self._scopeSelector(rule,hostRx,hostSelector,element._scopeCssViaAttr,scopeSelector);}});},_elementKeyframeTransforms:function(element,scopeSelector){var keyframesRules=element._styles._keyframes;var keyframeTransforms={};if(!settings.useNativeShadow&&keyframesRules){for(var i=0,keyframesRule=keyframesRules[i];i<keyframesRules.length;keyframesRule=keyframesRules[++i]){this._scopeKeyframes(keyframesRule,scopeSelector);keyframeTransforms[keyframesRule.keyframesName]=this._keyframesRuleTransformer(keyframesRule);}}
+return keyframeTransforms;},_keyframesRuleTransformer:function(keyframesRule){return function(cssText){return cssText.replace(keyframesRule.keyframesNameRx,keyframesRule.transformedKeyframesName);};},_scopeKeyframes:function(rule,scopeId){rule.keyframesNameRx=new RegExp('\\b'+rule.keyframesName+'(?!\\B|-)','g');rule.transformedKeyframesName=rule.keyframesName+'-'+scopeId;rule.transformedSelector=rule.transformedSelector||rule.selector;rule.selector=rule.transformedSelector.replace(rule.keyframesName,rule.transformedKeyframesName);},_hasDirOrHostContext:function(parsedSelector){return/:host-context|:dir/.test(parsedSelector);},_scopeSelector:function(rule,hostRx,hostSelector,viaAttr,scopeId){rule.transformedSelector=rule.transformedSelector||rule.selector;var selector=rule.transformedSelector;var scope=styleTransformer._calcElementScope(scopeId,viaAttr);var hostScope=styleTransformer._calcElementScope(hostSelector,viaAttr);var parts=selector.split(',');var isDirOrHostContextSelector=this._hasDirOrHostContext(rule.parsedSelector);for(var i=0,l=parts.length,p;i<l&&(p=parts[i]);i++){parts[i]=p.match(hostRx)?p.replace(hostSelector,scope):isDirOrHostContextSelector?p.replace(hostScope,scope+' '+hostScope):scope+' '+p;}
+rule.selector=parts.join(',');},applyElementScopeSelector:function(element,selector,old,viaAttr){var c=viaAttr?element.getAttribute(styleTransformer.SCOPE_NAME):element.getAttribute('class')||'';var v=old?c.replace(old,selector):(c?c+' ':'')+this.XSCOPE_NAME+' '+selector;if(c!==v){if(viaAttr){element.setAttribute(styleTransformer.SCOPE_NAME,v);}else{element.setAttribute('class',v);}}},applyElementStyle:function(element,properties,selector,style){var cssText=style?style.textContent||'':this.transformStyles(element,properties,selector);var s=element._customStyle;if(s&&!settings.useNativeShadow&&s!==style){s._useCount--;if(s._useCount<=0&&s.parentNode){s.parentNode.removeChild(s);}}
+if(settings.useNativeShadow){if(element._customStyle){element._customStyle.textContent=cssText;style=element._customStyle;}else if(cssText){style=styleUtil.applyCss(cssText,selector,element.root,element._scopeStyle);}}else{if(!style){if(cssText){style=styleUtil.applyCss(cssText,selector,null,element._scopeStyle);}}else if(!style.parentNode){if(IS_IE&&cssText.indexOf('@media')>-1){style.textContent=cssText;}
+styleUtil.applyStyle(style,null,element._scopeStyle);}}
+if(style){style._useCount=style._useCount||0;if(element._customStyle!=style){style._useCount++;}
+element._customStyle=style;}
+return style;},mixinCustomStyle:function(props,customStyle){var v;for(var i in customStyle){v=customStyle[i];if(v||v===0){props[i]=v;}}},updateNativeStyleProperties:function(element,properties){var oldPropertyNames=element.__customStyleProperties;if(oldPropertyNames){for(var i=0;i<oldPropertyNames.length;i++){element.style.removeProperty(oldPropertyNames[i]);}}
+var propertyNames=[];for(var p in properties){if(properties[p]!==null){element.style.setProperty(p,properties[p]);propertyNames.push(p);}}
+element.__customStyleProperties=propertyNames;},rx:styleUtil.rx,XSCOPE_NAME:'x-scope'};function addToBitMask(n,bits){var o=parseInt(n/32);var v=1<<n%32;bits[o]=(bits[o]||0)|v;}}();(function(){Polymer.StyleCache=function(){this.cache={};};Polymer.StyleCache.prototype={MAX:100,store:function(is,data,keyValues,keyStyles){data.keyValues=keyValues;data.styles=keyStyles;var s$=this.cache[is]=this.cache[is]||[];s$.push(data);if(s$.length>this.MAX){s$.shift();}},retrieve:function(is,keyValues,keyStyles){var cache=this.cache[is];if(cache){for(var i=cache.length-1,data;i>=0;i--){data=cache[i];if(keyStyles===data.styles&&this._objectsEqual(keyValues,data.keyValues)){return data;}}}},clear:function(){this.cache={};},_objectsEqual:function(target,source){var t,s;for(var i in target){t=target[i],s=source[i];if(!(typeof t==='object'&&t?this._objectsStrictlyEqual(t,s):t===s)){return false;}}
+if(Array.isArray(target)){return target.length===source.length;}
+return true;},_objectsStrictlyEqual:function(target,source){return this._objectsEqual(target,source)&&this._objectsEqual(source,target);}};}());Polymer.StyleDefaults=function(){var styleProperties=Polymer.StyleProperties;var StyleCache=Polymer.StyleCache;var nativeVariables=Polymer.Settings.useNativeCSSProperties;var api={_styles:[],_properties:null,customStyle:{},_styleCache:new StyleCache(),_element:Polymer.DomApi.wrap(document.documentElement),addStyle:function(style){this._styles.push(style);this._properties=null;},get _styleProperties(){if(!this._properties){styleProperties.decorateStyles(this._styles,this);this._styles._scopeStyleProperties=null;this._properties=styleProperties.hostAndRootPropertiesForScope(this).rootProps;styleProperties.mixinCustomStyle(this._properties,this.customStyle);styleProperties.reify(this._properties);}
+return this._properties;},hasStyleProperties:function(){return Boolean(this._properties);},_needsStyleProperties:function(){},_computeStyleProperties:function(){return this._styleProperties;},updateStyles:function(properties){this._properties=null;if(properties){Polymer.Base.mixin(this.customStyle,properties);}
+this._styleCache.clear();for(var i=0,s;i<this._styles.length;i++){s=this._styles[i];s=s.__importElement||s;s._apply();}
+if(nativeVariables){styleProperties.updateNativeStyleProperties(document.documentElement,this.customStyle);}}};return api;}();(function(){'use strict';var serializeValueToAttribute=Polymer.Base.serializeValueToAttribute;var propertyUtils=Polymer.StyleProperties;var styleTransformer=Polymer.StyleTransformer;var styleDefaults=Polymer.StyleDefaults;var nativeShadow=Polymer.Settings.useNativeShadow;var nativeVariables=Polymer.Settings.useNativeCSSProperties;Polymer.Base._addFeature({_prepStyleProperties:function(){if(!nativeVariables){this._ownStylePropertyNames=this._styles&&this._styles.length?propertyUtils.decorateStyles(this._styles,this):null;}},customStyle:null,getComputedStyleValue:function(property){if(!nativeVariables&&!this._styleProperties){this._computeStyleProperties();}
+return!nativeVariables&&this._styleProperties&&this._styleProperties[property]||getComputedStyle(this).getPropertyValue(property);},_setupStyleProperties:function(){this.customStyle={};this._styleCache=null;this._styleProperties=null;this._scopeSelector=null;this._ownStyleProperties=null;this._customStyle=null;},_needsStyleProperties:function(){return Boolean(!nativeVariables&&this._ownStylePropertyNames&&this._ownStylePropertyNames.length);},_validateApplyShim:function(){if(this.__applyShimInvalid){Polymer.ApplyShim.transform(this._styles,this.__proto__);var cssText=styleTransformer.elementStyles(this);if(nativeShadow){var templateStyle=this._template.content.querySelector('style');if(templateStyle){templateStyle.textContent=cssText;}}else{var shadyStyle=this._scopeStyle&&this._scopeStyle.nextSibling;if(shadyStyle){shadyStyle.textContent=cssText;}}}},_beforeAttached:function(){if((!this._scopeSelector||this.__stylePropertiesInvalid)&&this._needsStyleProperties()){this.__stylePropertiesInvalid=false;this._updateStyleProperties();}},_findStyleHost:function(){var e=this,root;while(root=Polymer.dom(e).getOwnerRoot()){if(Polymer.isInstance(root.host)){return root.host;}
+e=root.host;}
+return styleDefaults;},_updateStyleProperties:function(){var info,scope=this._findStyleHost();if(!scope._styleProperties){scope._computeStyleProperties();}
+if(!scope._styleCache){scope._styleCache=new Polymer.StyleCache();}
+var scopeData=propertyUtils.propertyDataFromStyles(scope._styles,this);var scopeCacheable=!this.__notStyleScopeCacheable;if(scopeCacheable){scopeData.key.customStyle=this.customStyle;info=scope._styleCache.retrieve(this.is,scopeData.key,this._styles);}
+var scopeCached=Boolean(info);if(scopeCached){this._styleProperties=info._styleProperties;}else{this._computeStyleProperties(scopeData.properties);}
+this._computeOwnStyleProperties();if(!scopeCached){info=styleCache.retrieve(this.is,this._ownStyleProperties,this._styles);}
+var globalCached=Boolean(info)&&!scopeCached;var style=this._applyStyleProperties(info);if(!scopeCached){style=style&&nativeShadow?style.cloneNode(true):style;info={style:style,_scopeSelector:this._scopeSelector,_styleProperties:this._styleProperties};if(scopeCacheable){scopeData.key.customStyle={};this.mixin(scopeData.key.customStyle,this.customStyle);scope._styleCache.store(this.is,info,scopeData.key,this._styles);}
+if(!globalCached){styleCache.store(this.is,Object.create(info),this._ownStyleProperties,this._styles);}}},_computeStyleProperties:function(scopeProps){var scope=this._findStyleHost();if(!scope._styleProperties){scope._computeStyleProperties();}
+var props=Object.create(scope._styleProperties);var hostAndRootProps=propertyUtils.hostAndRootPropertiesForScope(this);this.mixin(props,hostAndRootProps.hostProps);scopeProps=scopeProps||propertyUtils.propertyDataFromStyles(scope._styles,this).properties;this.mixin(props,scopeProps);this.mixin(props,hostAndRootProps.rootProps);propertyUtils.mixinCustomStyle(props,this.customStyle);propertyUtils.reify(props);this._styleProperties=props;},_computeOwnStyleProperties:function(){var props={};for(var i=0,n;i<this._ownStylePropertyNames.length;i++){n=this._ownStylePropertyNames[i];props[n]=this._styleProperties[n];}
+this._ownStyleProperties=props;},_scopeCount:0,_applyStyleProperties:function(info){var oldScopeSelector=this._scopeSelector;this._scopeSelector=info?info._scopeSelector:this.is+'-'+this.__proto__._scopeCount++;var style=propertyUtils.applyElementStyle(this,this._styleProperties,this._scopeSelector,info&&info.style);if(!nativeShadow){propertyUtils.applyElementScopeSelector(this,this._scopeSelector,oldScopeSelector,this._scopeCssViaAttr);}
+return style;},serializeValueToAttribute:function(value,attribute,node){node=node||this;if(attribute==='class'&&!nativeShadow){var host=node===this?this.domHost||this.dataHost:this;if(host){value=host._scopeElementClass(node,value);}}
+node=this.shadyRoot&&this.shadyRoot._hasDistributed?Polymer.dom(node):node;serializeValueToAttribute.call(this,value,attribute,node);},_scopeElementClass:function(element,selector){if(!nativeShadow&&!this._scopeCssViaAttr){selector=(selector?selector+' ':'')+SCOPE_NAME+' '+this.is+(element._scopeSelector?' '+XSCOPE_NAME+' '+element._scopeSelector:'');}
+return selector;},updateStyles:function(properties){if(properties){this.mixin(this.customStyle,properties);}
+if(nativeVariables){propertyUtils.updateNativeStyleProperties(this,this.customStyle);}else{if(this.isAttached){if(this._needsStyleProperties()){this._updateStyleProperties();}else{this._styleProperties=null;}}else{this.__stylePropertiesInvalid=true;}
+if(this._styleCache){this._styleCache.clear();}
+this._updateRootStyles();}},_updateRootStyles:function(root){root=root||this.root;var c$=Polymer.dom(root)._query(function(e){return e.shadyRoot||e.shadowRoot;});for(var i=0,l=c$.length,c;i<l&&(c=c$[i]);i++){if(c.updateStyles){c.updateStyles();}}}});Polymer.updateStyles=function(properties){styleDefaults.updateStyles(properties);Polymer.Base._updateRootStyles(document);};var styleCache=new Polymer.StyleCache();Polymer.customStyleCache=styleCache;var SCOPE_NAME=styleTransformer.SCOPE_NAME;var XSCOPE_NAME=propertyUtils.XSCOPE_NAME;}());Polymer.Base._addFeature({_registerFeatures:function(){this._prepIs();if(this.factoryImpl){this._prepConstructor();}
+this._prepStyles();},_finishRegisterFeatures:function(){this._prepTemplate();this._prepShimStyles();this._prepAnnotations();this._prepEffects();this._prepBehaviors();this._prepPropertyInfo();this._prepBindings();this._prepShady();},_prepBehavior:function(b){this._addPropertyEffects(b.properties);this._addComplexObserverEffects(b.observers);this._addHostAttributes(b.hostAttributes);},_initFeatures:function(){this._setupGestures();this._setupConfigure(this.__data__);this._setupStyleProperties();this._setupDebouncers();this._setupShady();this._registerHost();if(this._template){this._validateApplyShim();this._poolContent();this._beginHosting();this._stampTemplate();this._endHosting();this._marshalAnnotationReferences();}
+this._marshalInstanceEffects();this._marshalBehaviors();this._marshalHostAttributes();this._marshalAttributes();this._tryReady();},_marshalBehavior:function(b){if(b.listeners){this._listenListeners(b.listeners);}}});(function(){var propertyUtils=Polymer.StyleProperties;var styleUtil=Polymer.StyleUtil;var cssParse=Polymer.CssParse;var styleDefaults=Polymer.StyleDefaults;var styleTransformer=Polymer.StyleTransformer;var applyShim=Polymer.ApplyShim;var debounce=Polymer.Debounce;var settings=Polymer.Settings;var updateDebouncer;Polymer({is:'custom-style',extends:'style',_template:null,properties:{include:String},ready:function(){this.__appliedElement=this.__appliedElement||this;this.__cssBuild=styleUtil.getCssBuildType(this);if(this.__appliedElement!==this){this.__appliedElement.__cssBuild=this.__cssBuild;}
+if(this.ownerDocument!==window.document&&this.__appliedElement===this){document.head.appendChild(this);}
+this._tryApply();},attached:function(){this._tryApply();},_tryApply:function(){if(!this._appliesToDocument){if(this.parentNode&&this.parentNode.localName!=='dom-module'){this._appliesToDocument=true;var e=this.__appliedElement;if(!settings.useNativeCSSProperties){this.__needsUpdateStyles=styleDefaults.hasStyleProperties();styleDefaults.addStyle(e);}
+if(e.textContent||this.include){this._apply(true);}else{var self=this;var observer=new MutationObserver(function(){observer.disconnect();self._apply(true);});observer.observe(e,{childList:true});}}}},_updateStyles:function(){Polymer.updateStyles();},_apply:function(initialApply){var e=this.__appliedElement;if(this.include){e.textContent=styleUtil.cssFromModules(this.include,true)+e.textContent;}
+if(!e.textContent){return;}
+var buildType=this.__cssBuild;var targetedBuild=styleUtil.isTargetedBuild(buildType);if(settings.useNativeCSSProperties&&targetedBuild){return;}
+var styleRules=styleUtil.rulesForStyle(e);if(!targetedBuild){styleUtil.forEachRule(styleRules,function(rule){styleTransformer.documentRule(rule);});if(settings.useNativeCSSProperties&&!buildType){applyShim.transform([e]);}}
+if(settings.useNativeCSSProperties){e.textContent=styleUtil.toCssText(styleRules);}else{var self=this;var fn=function fn(){self._flushCustomProperties();};if(initialApply){Polymer.RenderStatus.whenReady(fn);}else{fn();}}},_flushCustomProperties:function(){if(this.__needsUpdateStyles){this.__needsUpdateStyles=false;updateDebouncer=debounce(updateDebouncer,this._updateStyles);}else{this._applyCustomProperties();}},_applyCustomProperties:function(){var element=this.__appliedElement;this._computeStyleProperties();var props=this._styleProperties;var rules=styleUtil.rulesForStyle(element);if(!rules){return;}
+element.textContent=styleUtil.toCssText(rules,function(rule){var css=rule.cssText=rule.parsedCssText;if(rule.propertyInfo&&rule.propertyInfo.cssText){css=cssParse.removeCustomPropAssignment(css);rule.cssText=propertyUtils.valueForProperties(css,props);}});}});}());Polymer.Templatizer={properties:{__hideTemplateChildren__:{observer:'_showHideChildren'}},_instanceProps:Polymer.nob,_parentPropPrefix:'_parent_',templatize:function(template){this._templatized=template;if(!template._content){template._content=template.content;}
+if(template._content._ctor){this.ctor=template._content._ctor;this._prepParentProperties(this.ctor.prototype,template);return;}
+var archetype=Object.create(Polymer.Base);this._customPrepAnnotations(archetype,template);this._prepParentProperties(archetype,template);archetype._prepEffects();this._customPrepEffects(archetype);archetype._prepBehaviors();archetype._prepPropertyInfo();archetype._prepBindings();archetype._notifyPathUp=this._notifyPathUpImpl;archetype._scopeElementClass=this._scopeElementClassImpl;archetype.listen=this._listenImpl;archetype._showHideChildren=this._showHideChildrenImpl;archetype.__setPropertyOrig=this.__setProperty;archetype.__setProperty=this.__setPropertyImpl;var _constructor=this._constructorImpl;var ctor=function TemplateInstance(model,host){_constructor.call(this,model,host);};ctor.prototype=archetype;archetype.constructor=ctor;template._content._ctor=ctor;this.ctor=ctor;},_getRootDataHost:function(){return this.dataHost&&this.dataHost._rootDataHost||this.dataHost;},_showHideChildrenImpl:function(hide){var c=this._children;for(var i=0;i<c.length;i++){var n=c[i];if(Boolean(hide)!=Boolean(n.__hideTemplateChildren__)){if(n.nodeType===Node.TEXT_NODE){if(hide){n.__polymerTextContent__=n.textContent;n.textContent='';}else{n.textContent=n.__polymerTextContent__;}}else if(n.style){if(hide){n.__polymerDisplay__=n.style.display;n.style.display='none';}else{n.style.display=n.__polymerDisplay__;}}}
+n.__hideTemplateChildren__=hide;}},__setPropertyImpl:function(property,value,fromAbove,node){if(node&&node.__hideTemplateChildren__&&property=='textContent'){property='__polymerTextContent__';}
+this.__setPropertyOrig(property,value,fromAbove,node);},_debounceTemplate:function(fn){Polymer.dom.addDebouncer(this.debounce('_debounceTemplate',fn));},_flushTemplates:function(){Polymer.dom.flush();},_customPrepEffects:function(archetype){var parentProps=archetype._parentProps;for(var prop in parentProps){archetype._addPropertyEffect(prop,'function',this._createHostPropEffector(prop));}
+for(prop in this._instanceProps){archetype._addPropertyEffect(prop,'function',this._createInstancePropEffector(prop));}},_customPrepAnnotations:function(archetype,template){var t=archetype._template=document.createElement('template');var c=t._content=template._content;if(!c._notes){var rootDataHost=archetype._rootDataHost;if(rootDataHost){Polymer.Annotations.prepElement=function(){rootDataHost._prepElement();};}
+c._notes=Polymer.Annotations.parseAnnotations(template);Polymer.Annotations.prepElement=null;this._processAnnotations(c._notes);}
+archetype._notes=c._notes;archetype._parentProps=c._parentProps;},_prepParentProperties:function(archetype,template){var parentProps=this._parentProps=archetype._parentProps;if(this._forwardParentProp&&parentProps){var proto=archetype._parentPropProto;var prop;if(!proto){for(prop in this._instanceProps){delete parentProps[prop];}
+proto=archetype._parentPropProto=Object.create(null);if(template!=this){Polymer.Bind.prepareModel(proto);Polymer.Base.prepareModelNotifyPath(proto);}
+for(prop in parentProps){var parentProp=this._parentPropPrefix+prop;var effects=[{kind:'function',effect:this._createForwardPropEffector(prop),fn:Polymer.Bind._functionEffect},{kind:'notify',fn:Polymer.Bind._notifyEffect,effect:{event:Polymer.CaseMap.camelToDashCase(parentProp)+'-changed'}}];proto._propertyEffects=proto._propertyEffects||{};proto._propertyEffects[parentProp]=effects;Polymer.Bind._createAccessors(proto,parentProp,effects);}}
+var self=this;if(template!=this){Polymer.Bind.prepareInstance(template);template._forwardParentProp=function(source,value){self._forwardParentProp(source,value);};}
+this._extendTemplate(template,proto);template._pathEffector=function(path,value,fromAbove){return self._pathEffectorImpl(path,value,fromAbove);};}},_createForwardPropEffector:function(prop){return function(source,value){this._forwardParentProp(prop,value);};},_createHostPropEffector:function(prop){var prefix=this._parentPropPrefix;return function(source,value){this.dataHost._templatized[prefix+prop]=value;};},_createInstancePropEffector:function(prop){return function(source,value,old,fromAbove){if(!fromAbove){this.dataHost._forwardInstanceProp(this,prop,value);}};},_extendTemplate:function(template,proto){var n$=Object.getOwnPropertyNames(proto);if(proto._propertySetter){template._propertySetter=proto._propertySetter;}
+for(var i=0,n;i<n$.length&&(n=n$[i]);i++){var val=template[n];if(val&&n=='_propertyEffects'){var pe=Polymer.Base.mixin({},val);template._propertyEffects=Polymer.Base.mixin(pe,proto._propertyEffects);}else{var pd=Object.getOwnPropertyDescriptor(proto,n);Object.defineProperty(template,n,pd);if(val!==undefined){template._propertySetter(n,val);}}}},_showHideChildren:function(hidden){},_forwardInstancePath:function(inst,path,value){},_forwardInstanceProp:function(inst,prop,value){},_notifyPathUpImpl:function(path,value){var dataHost=this.dataHost;var root=Polymer.Path.root(path);dataHost._forwardInstancePath.call(dataHost,this,path,value);if(root in dataHost._parentProps){dataHost._templatized._notifyPath(dataHost._parentPropPrefix+path,value);}},_pathEffectorImpl:function(path,value,fromAbove){if(this._forwardParentPath){if(path.indexOf(this._parentPropPrefix)===0){var subPath=path.substring(this._parentPropPrefix.length);var model=Polymer.Path.root(subPath);if(model in this._parentProps){this._forwardParentPath(subPath,value);}}}
+Polymer.Base._pathEffector.call(this._templatized,path,value,fromAbove);},_constructorImpl:function(model,host){this._rootDataHost=host._getRootDataHost();this._setupConfigure(model);this._registerHost(host);this._beginHosting();this.root=this.instanceTemplate(this._template);this.root.__noContent=!this._notes._hasContent;this.root.__styleScoped=true;this._endHosting();this._marshalAnnotatedNodes();this._marshalInstanceEffects();this._marshalAnnotatedListeners();var children=[];for(var n=this.root.firstChild;n;n=n.nextSibling){children.push(n);n._templateInstance=this;}
+this._children=children;if(host.__hideTemplateChildren__){this._showHideChildren(true);}
+this._tryReady();},_listenImpl:function(node,eventName,methodName){var model=this;var host=this._rootDataHost;var handler=host._createEventHandler(node,eventName,methodName);var decorated=function(e){e.model=model;handler(e);};host._listen(node,eventName,decorated);},_scopeElementClassImpl:function(node,value){var host=this._rootDataHost;if(host){return host._scopeElementClass(node,value);}
+return value;},stamp:function(model){model=model||{};if(this._parentProps){var templatized=this._templatized;for(var prop in this._parentProps){if(model[prop]===undefined){model[prop]=templatized[this._parentPropPrefix+prop];}}}
+return new this.ctor(model,this);},modelForElement:function(el){var model;while(el){if(model=el._templateInstance){if(model.dataHost!=this){el=model.dataHost;}else{return model;}}else{el=el.parentNode;}}}};Polymer({is:'dom-template',extends:'template',_template:null,behaviors:[Polymer.Templatizer],ready:function(){this.templatize(this);}});Polymer._collections=new WeakMap();Polymer.Collection=function(userArray){Polymer._collections.set(userArray,this);this.userArray=userArray;this.store=userArray.slice();this.initMap();};Polymer.Collection.prototype={constructor:Polymer.Collection,initMap:function(){var omap=this.omap=new WeakMap();var pmap=this.pmap={};var s=this.store;for(var i=0;i<s.length;i++){var item=s[i];if(item&&typeof item=='object'){omap.set(item,i);}else{pmap[item]=i;}}},add:function(item){var key=this.store.push(item)-1;if(item&&typeof item=='object'){this.omap.set(item,key);}else{this.pmap[item]=key;}
+return'#'+key;},removeKey:function(key){if(key=this._parseKey(key)){this._removeFromMap(this.store[key]);delete this.store[key];}},_removeFromMap:function(item){if(item&&typeof item=='object'){this.omap.delete(item);}else{delete this.pmap[item];}},remove:function(item){var key=this.getKey(item);this.removeKey(key);return key;},getKey:function(item){var key;if(item&&typeof item=='object'){key=this.omap.get(item);}else{key=this.pmap[item];}
+if(key!=undefined){return'#'+key;}},getKeys:function(){return Object.keys(this.store).map(function(key){return'#'+key;});},_parseKey:function(key){if(key&&key[0]=='#'){return key.slice(1);}},setItem:function(key,item){if(key=this._parseKey(key)){var old=this.store[key];if(old){this._removeFromMap(old);}
+if(item&&typeof item=='object'){this.omap.set(item,key);}else{this.pmap[item]=key;}
+this.store[key]=item;}},getItem:function(key){if(key=this._parseKey(key)){return this.store[key];}},getItems:function(){var items=[],store=this.store;for(var key in store){items.push(store[key]);}
+return items;},_applySplices:function(splices){var keyMap={},key;for(var i=0,s;i<splices.length&&(s=splices[i]);i++){s.addedKeys=[];for(var j=0;j<s.removed.length;j++){key=this.getKey(s.removed[j]);keyMap[key]=keyMap[key]?null:-1;}
+for(j=0;j<s.addedCount;j++){var item=this.userArray[s.index+j];key=this.getKey(item);key=key===undefined?this.add(item):key;keyMap[key]=keyMap[key]?null:1;s.addedKeys.push(key);}}
+var removed=[];var added=[];for(key in keyMap){if(keyMap[key]<0){this.removeKey(key);removed.push(key);}
+if(keyMap[key]>0){added.push(key);}}
+return[{removed:removed,added:added}];}};Polymer.Collection.get=function(userArray){return Polymer._collections.get(userArray)||new Polymer.Collection(userArray);};Polymer.Collection.applySplices=function(userArray,splices){var coll=Polymer._collections.get(userArray);return coll?coll._applySplices(splices):null;};Polymer({is:'dom-repeat',extends:'template',_template:null,properties:{items:{type:Array},as:{type:String,value:'item'},indexAs:{type:String,value:'index'},sort:{type:Function,observer:'_sortChanged'},filter:{type:Function,observer:'_filterChanged'},observe:{type:String,observer:'_observeChanged'},delay:Number,renderedItemCount:{type:Number,notify:!Polymer.Settings.suppressTemplateNotifications,readOnly:true},initialCount:{type:Number,observer:'_initializeChunking'},targetFramerate:{type:Number,value:20},notifyDomChange:{type:Boolean},_targetFrameTime:{type:Number,computed:'_computeFrameTime(targetFramerate)'}},behaviors:[Polymer.Templatizer],observers:['_itemsChanged(items.*)'],created:function(){this._instances=[];this._pool=[];this._limit=Infinity;var self=this;this._boundRenderChunk=function(){self._renderChunk();};},detached:function(){this.__isDetached=true;for(var i=0;i<this._instances.length;i++){this._detachInstance(i);}},attached:function(){if(this.__isDetached){this.__isDetached=false;var refNode;var parentNode=Polymer.dom(this).parentNode;if(parentNode.localName==this.is){refNode=parentNode;parentNode=Polymer.dom(parentNode).parentNode;}else{refNode=this;}
+var parent=Polymer.dom(parentNode);for(var i=0;i<this._instances.length;i++){this._attachInstance(i,parent,refNode);}}},ready:function(){this._instanceProps={__key__:true};this._instanceProps[this.as]=true;this._instanceProps[this.indexAs]=true;if(!this.ctor){this.templatize(this);}},_sortChanged:function(sort){var dataHost=this._getRootDataHost();this._sortFn=sort&&(typeof sort=='function'?sort:function(){return dataHost[sort].apply(dataHost,arguments);});this._needFullRefresh=true;if(this.items){this._debounceTemplate(this._render);}},_filterChanged:function(filter){var dataHost=this._getRootDataHost();this._filterFn=filter&&(typeof filter=='function'?filter:function(){return dataHost[filter].apply(dataHost,arguments);});this._needFullRefresh=true;if(this.items){this._debounceTemplate(this._render);}},_computeFrameTime:function(rate){return Math.ceil(1000/rate);},_initializeChunking:function(){if(this.initialCount){this._limit=this.initialCount;this._chunkCount=this.initialCount;this._lastChunkTime=performance.now();}},_tryRenderChunk:function(){if(this.items&&this._limit<this.items.length){this.debounce('renderChunk',this._requestRenderChunk);}},_requestRenderChunk:function(){requestAnimationFrame(this._boundRenderChunk);},_renderChunk:function(){var currChunkTime=performance.now();var ratio=this._targetFrameTime/(currChunkTime-this._lastChunkTime);this._chunkCount=Math.round(this._chunkCount*ratio)||1;this._limit+=this._chunkCount;this._lastChunkTime=currChunkTime;this._debounceTemplate(this._render);},_observeChanged:function(){this._observePaths=this.observe&&this.observe.replace('.*','.').split(' ');},_itemsChanged:function(change){if(change.path=='items'){if(Array.isArray(this.items)){this.collection=Polymer.Collection.get(this.items);}else if(!this.items){this.collection=null;}else{this._error(this._logf('dom-repeat','expected array for `items`,'+' found',this.items));}
+this._keySplices=[];this._indexSplices=[];this._needFullRefresh=true;this._initializeChunking();this._debounceTemplate(this._render);}else if(change.path=='items.splices'){this._keySplices=this._keySplices.concat(change.value.keySplices);this._indexSplices=this._indexSplices.concat(change.value.indexSplices);this._debounceTemplate(this._render);}else{var subpath=change.path.slice(6);this._forwardItemPath(subpath,change.value);this._checkObservedPaths(subpath);}},_checkObservedPaths:function(path){if(this._observePaths){path=path.substring(path.indexOf('.')+1);var paths=this._observePaths;for(var i=0;i<paths.length;i++){if(path.indexOf(paths[i])===0){this._needFullRefresh=true;if(this.delay){this.debounce('render',this._render,this.delay);}else{this._debounceTemplate(this._render);}
+return;}}}},render:function(){this._needFullRefresh=true;this._debounceTemplate(this._render);this._flushTemplates();},_render:function(){if(this._needFullRefresh){this._applyFullRefresh();this._needFullRefresh=false;}else if(this._keySplices.length){if(this._sortFn){this._applySplicesUserSort(this._keySplices);}else{if(this._filterFn){this._applyFullRefresh();}else{this._applySplicesArrayOrder(this._indexSplices);}}}else{}
+this._keySplices=[];this._indexSplices=[];var keyToIdx=this._keyToInstIdx={};for(var i=this._instances.length-1;i>=0;i--){var inst=this._instances[i];if(inst.isPlaceholder&&i<this._limit){inst=this._insertInstance(i,inst.__key__);}else if(!inst.isPlaceholder&&i>=this._limit){inst=this._downgradeInstance(i,inst.__key__);}
+keyToIdx[inst.__key__]=i;if(!inst.isPlaceholder){inst.__setProperty(this.indexAs,i,true);}}
+this._pool.length=0;this._setRenderedItemCount(this._instances.length);if(!Polymer.Settings.suppressTemplateNotifications||this.notifyDomChange){this.fire('dom-change');}
+this._tryRenderChunk();},_applyFullRefresh:function(){var c=this.collection;var keys;if(this._sortFn){keys=c?c.getKeys():[];}else{keys=[];var items=this.items;if(items){for(var i=0;i<items.length;i++){keys.push(c.getKey(items[i]));}}}
+var self=this;if(this._filterFn){keys=keys.filter(function(a){return self._filterFn(c.getItem(a));});}
+if(this._sortFn){keys.sort(function(a,b){return self._sortFn(c.getItem(a),c.getItem(b));});}
+for(i=0;i<keys.length;i++){var key=keys[i];var inst=this._instances[i];if(inst){inst.__key__=key;if(!inst.isPlaceholder&&i<this._limit){inst.__setProperty(this.as,c.getItem(key),true);}}else if(i<this._limit){this._insertInstance(i,key);}else{this._insertPlaceholder(i,key);}}
+for(var j=this._instances.length-1;j>=i;j--){this._detachAndRemoveInstance(j);}},_numericSort:function(a,b){return a-b;},_applySplicesUserSort:function(splices){var c=this.collection;var keyMap={};var key;for(var i=0,s;i<splices.length&&(s=splices[i]);i++){for(var j=0;j<s.removed.length;j++){key=s.removed[j];keyMap[key]=keyMap[key]?null:-1;}
+for(j=0;j<s.added.length;j++){key=s.added[j];keyMap[key]=keyMap[key]?null:1;}}
+var removedIdxs=[];var addedKeys=[];for(key in keyMap){if(keyMap[key]===-1){removedIdxs.push(this._keyToInstIdx[key]);}
+if(keyMap[key]===1){addedKeys.push(key);}}
+if(removedIdxs.length){removedIdxs.sort(this._numericSort);for(i=removedIdxs.length-1;i>=0;i--){var idx=removedIdxs[i];if(idx!==undefined){this._detachAndRemoveInstance(idx);}}}
+var self=this;if(addedKeys.length){if(this._filterFn){addedKeys=addedKeys.filter(function(a){return self._filterFn(c.getItem(a));});}
+addedKeys.sort(function(a,b){return self._sortFn(c.getItem(a),c.getItem(b));});var start=0;for(i=0;i<addedKeys.length;i++){start=this._insertRowUserSort(start,addedKeys[i]);}}},_insertRowUserSort:function(start,key){var c=this.collection;var item=c.getItem(key);var end=this._instances.length-1;var idx=-1;while(start<=end){var mid=start+end>>1;var midKey=this._instances[mid].__key__;var cmp=this._sortFn(c.getItem(midKey),item);if(cmp<0){start=mid+1;}else if(cmp>0){end=mid-1;}else{idx=mid;break;}}
+if(idx<0){idx=end+1;}
+this._insertPlaceholder(idx,key);return idx;},_applySplicesArrayOrder:function(splices){for(var i=0,s;i<splices.length&&(s=splices[i]);i++){for(var j=0;j<s.removed.length;j++){this._detachAndRemoveInstance(s.index);}
+for(j=0;j<s.addedKeys.length;j++){this._insertPlaceholder(s.index+j,s.addedKeys[j]);}}},_detachInstance:function(idx){var inst=this._instances[idx];if(!inst.isPlaceholder){for(var i=0;i<inst._children.length;i++){var el=inst._children[i];Polymer.dom(inst.root).appendChild(el);}
+return inst;}},_attachInstance:function(idx,parent,refNode){var inst=this._instances[idx];if(!inst.isPlaceholder){parent.insertBefore(inst.root,refNode);}},_detachAndRemoveInstance:function(idx){var inst=this._detachInstance(idx);if(inst){this._pool.push(inst);}
+this._instances.splice(idx,1);},_insertPlaceholder:function(idx,key){this._instances.splice(idx,0,{isPlaceholder:true,__key__:key});},_stampInstance:function(idx,key){var model={__key__:key};model[this.as]=this.collection.getItem(key);model[this.indexAs]=idx;return this.stamp(model);},_insertInstance:function(idx,key){var inst=this._pool.pop();if(inst){inst.__setProperty(this.as,this.collection.getItem(key),true);inst.__setProperty('__key__',key,true);}else{inst=this._stampInstance(idx,key);}
+var beforeRow=this._instances[idx+1];var beforeNode=beforeRow&&!beforeRow.isPlaceholder?beforeRow._children[0]:this;var parentNode=Polymer.dom(this).parentNode;if(parentNode.localName==this.is){if(beforeNode==this){beforeNode=parentNode;}
+parentNode=Polymer.dom(parentNode).parentNode;}
+Polymer.dom(parentNode).insertBefore(inst.root,beforeNode);this._instances[idx]=inst;return inst;},_downgradeInstance:function(idx,key){var inst=this._detachInstance(idx);if(inst){this._pool.push(inst);}
+inst={isPlaceholder:true,__key__:key};this._instances[idx]=inst;return inst;},_showHideChildren:function(hidden){for(var i=0;i<this._instances.length;i++){if(!this._instances[i].isPlaceholder)
+this._instances[i]._showHideChildren(hidden);}},_forwardInstanceProp:function(inst,prop,value){if(prop==this.as){var idx;if(this._sortFn||this._filterFn){idx=this.items.indexOf(this.collection.getItem(inst.__key__));}else{idx=inst[this.indexAs];}
+this.set('items.'+idx,value);}},_forwardInstancePath:function(inst,path,value){if(path.indexOf(this.as+'.')===0){this._notifyPath('items.'+inst.__key__+'.'+path.slice(this.as.length+1),value);}},_forwardParentProp:function(prop,value){var i$=this._instances;for(var i=0,inst;i<i$.length&&(inst=i$[i]);i++){if(!inst.isPlaceholder){inst.__setProperty(prop,value,true);}}},_forwardParentPath:function(path,value){var i$=this._instances;for(var i=0,inst;i<i$.length&&(inst=i$[i]);i++){if(!inst.isPlaceholder){inst._notifyPath(path,value,true);}}},_forwardItemPath:function(path,value){if(this._keyToInstIdx){var dot=path.indexOf('.');var key=path.substring(0,dot<0?path.length:dot);var idx=this._keyToInstIdx[key];var inst=this._instances[idx];if(inst&&!inst.isPlaceholder){if(dot>=0){path=this.as+'.'+path.substring(dot+1);inst._notifyPath(path,value,true);}else{inst.__setProperty(this.as,value,true);}}}},itemForElement:function(el){var instance=this.modelForElement(el);return instance&&instance[this.as];},keyForElement:function(el){var instance=this.modelForElement(el);return instance&&instance.__key__;},indexForElement:function(el){var instance=this.modelForElement(el);return instance&&instance[this.indexAs];}});Polymer({is:'array-selector',_template:null,properties:{items:{type:Array,observer:'clearSelection'},multi:{type:Boolean,value:false,observer:'clearSelection'},selected:{type:Object,notify:true},selectedItem:{type:Object,notify:true},toggle:{type:Boolean,value:false}},clearSelection:function(){if(Array.isArray(this.selected)){for(var i=0;i<this.selected.length;i++){this.unlinkPaths('selected.'+i);}}else{this.unlinkPaths('selected');this.unlinkPaths('selectedItem');}
+if(this.multi){if(!this.selected||this.selected.length){this.selected=[];this._selectedColl=Polymer.Collection.get(this.selected);}}else{this.selected=null;this._selectedColl=null;}
+this.selectedItem=null;},isSelected:function(item){if(this.multi){return this._selectedColl.getKey(item)!==undefined;}else{return this.selected==item;}},deselect:function(item){if(this.multi){if(this.isSelected(item)){var skey=this._selectedColl.getKey(item);this.arrayDelete('selected',item);this.unlinkPaths('selected.'+skey);}}else{this.selected=null;this.selectedItem=null;this.unlinkPaths('selected');this.unlinkPaths('selectedItem');}},select:function(item){var icol=Polymer.Collection.get(this.items);var key=icol.getKey(item);if(this.multi){if(this.isSelected(item)){if(this.toggle){this.deselect(item);}}else{this.push('selected',item);var skey=this._selectedColl.getKey(item);this.linkPaths('selected.'+skey,'items.'+key);}}else{if(this.toggle&&item==this.selected){this.deselect();}else{this.selected=item;this.selectedItem=item;this.linkPaths('selected','items.'+key);this.linkPaths('selectedItem','items.'+key);}}}});Polymer({is:'dom-if',extends:'template',_template:null,properties:{'if':{type:Boolean,value:false,observer:'_queueRender'},restamp:{type:Boolean,value:false,observer:'_queueRender'},notifyDomChange:{type:Boolean}},behaviors:[Polymer.Templatizer],_queueRender:function(){this._debounceTemplate(this._render);},detached:function(){var parentNode=this.parentNode;if(parentNode&&parentNode.localName==this.is){parentNode=Polymer.dom(parentNode).parentNode;}
+if(!parentNode||parentNode.nodeType==Node.DOCUMENT_FRAGMENT_NODE&&(!Polymer.Settings.hasShadow||!(parentNode instanceof ShadowRoot))){this._teardownInstance();}},attached:function(){if(this.if&&this.ctor){this.async(this._ensureInstance);}},render:function(){this._flushTemplates();},_render:function(){if(this.if){if(!this.ctor){this.templatize(this);}
+this._ensureInstance();this._showHideChildren();}else if(this.restamp){this._teardownInstance();}
+if(!this.restamp&&this._instance){this._showHideChildren();}
+if(this.if!=this._lastIf){if(!Polymer.Settings.suppressTemplateNotifications||this.notifyDomChange){this.fire('dom-change');}
+this._lastIf=this.if;}},_ensureInstance:function(){var refNode;var parentNode=Polymer.dom(this).parentNode;if(parentNode&&parentNode.localName==this.is){refNode=parentNode;parentNode=Polymer.dom(parentNode).parentNode;}else{refNode=this;}
+if(parentNode){if(!this._instance){this._instance=this.stamp();var root=this._instance.root;Polymer.dom(parentNode).insertBefore(root,refNode);}else{var c$=this._instance._children;if(c$&&c$.length){var lastChild=Polymer.dom(refNode).previousSibling;if(lastChild!==c$[c$.length-1]){for(var i=0,n;i<c$.length&&(n=c$[i]);i++){Polymer.dom(parentNode).insertBefore(n,refNode);}}}}}},_teardownInstance:function(){if(this._instance){var c$=this._instance._children;if(c$&&c$.length){var parent=Polymer.dom(Polymer.dom(c$[0]).parentNode);for(var i=0,n;i<c$.length&&(n=c$[i]);i++){parent.removeChild(n);}}
+this._instance=null;}},_showHideChildren:function(){var hidden=this.__hideTemplateChildren__||!this.if;if(this._instance){this._instance._showHideChildren(hidden);}},_forwardParentProp:function(prop,value){if(this._instance){this._instance.__setProperty(prop,value,true);}},_forwardParentPath:function(path,value){if(this._instance){this._instance._notifyPath(path,value,true);}}});Polymer({is:'dom-bind',properties:{notifyDomChange:{type:Boolean}},extends:'template',_template:null,created:function(){var self=this;Polymer.RenderStatus.whenReady(function(){if(document.readyState=='loading'){document.addEventListener('DOMContentLoaded',function(){self._markImportsReady();});}else{self._markImportsReady();}});},_ensureReady:function(){if(!this._readied){this._readySelf();}},_markImportsReady:function(){this._importsReady=true;this._ensureReady();},_registerFeatures:function(){this._prepConstructor();},_insertChildren:function(){var refNode;var parentNode=Polymer.dom(this).parentNode;if(parentNode.localName==this.is){refNode=parentNode;parentNode=Polymer.dom(parentNode).parentNode;}else{refNode=this;}
+Polymer.dom(parentNode).insertBefore(this.root,refNode);},_removeChildren:function(){if(this._children){for(var i=0;i<this._children.length;i++){this.root.appendChild(this._children[i]);}}},_initFeatures:function(){},_scopeElementClass:function(element,selector){if(this.dataHost){return this.dataHost._scopeElementClass(element,selector);}else{return selector;}},_configureInstanceProperties:function(){},_prepConfigure:function(){var config={};for(var prop in this._propertyEffects){config[prop]=this[prop];}
+var setupConfigure=this._setupConfigure;this._setupConfigure=function(){setupConfigure.call(this,config);};},attached:function(){if(this._importsReady){this.render();}},detached:function(){this._removeChildren();},render:function(){this._ensureReady();if(!this._children){this._template=this;this._prepAnnotations();this._prepEffects();this._prepBehaviors();this._prepConfigure();this._prepBindings();this._prepPropertyInfo();Polymer.Base._initFeatures.call(this);this._children=Polymer.TreeApi.arrayCopyChildNodes(this.root);}
+this._insertChildren();if(!Polymer.Settings.suppressTemplateNotifications||this.notifyDomChange){this.fire('dom-change');}}});'use strict';if(!window.CustomElements||window.CustomElements.hasNative){if(!Polymer.Settings.useNativeShadow){tr.showPanic('Polymer error','base should use native shadow when possible.');}}'use strict';const global=this.window||this.global;this.tr=(function(){if(global.tr)return global.tr;function exportPath(name){const parts=name.split('.');let cur=global;for(let part;parts.length&&(part=parts.shift());){if(part in cur){cur=cur[part];}else{cur=cur[part]={};}}
+return cur;}
+function isExported(name){const parts=name.split('.');let cur=global;for(let part;parts.length&&(part=parts.shift());){if(part in cur){cur=cur[part];}else{return false;}}
+return true;}
+function isDefined(name){const parts=name.split('.');let curObject=global;for(let i=0;i<parts.length;i++){const partName=parts[i];const nextObject=curObject[partName];if(nextObject===undefined)return false;curObject=nextObject;}
+return true;}
+let panicElement=undefined;const rawPanicMessages=[];function showPanicElementIfNeeded(){if(panicElement)return;const panicOverlay=document.createElement('div');panicOverlay.style.backgroundColor='white';panicOverlay.style.border='3px solid red';panicOverlay.style.boxSizing='border-box';panicOverlay.style.color='black';panicOverlay.style.display='flex';panicOverlay.style.height='100%';panicOverlay.style.left=0;panicOverlay.style.padding='8px';panicOverlay.style.position='fixed';panicOverlay.style.top=0;panicOverlay.style.webkitFlexDirection='column';panicOverlay.style.width='100%';panicElement=document.createElement('div');panicElement.style.webkitFlex='1 1 auto';panicElement.style.overflow='auto';panicOverlay.appendChild(panicElement);if(!document.body){setTimeout(function(){document.body.appendChild(panicOverlay);},150);}else{document.body.appendChild(panicOverlay);}}
+function showPanic(panicTitle,panicDetails){if(tr.isHeadless){if(panicDetails instanceof Error)throw panicDetails;throw new Error('Panic: '+panicTitle+':\n'+panicDetails);}
+if(panicDetails instanceof Error){panicDetails=panicDetails.stack;}
+showPanicElementIfNeeded();const panicMessageEl=document.createElement('div');panicMessageEl.innerHTML='<h2 id="message"></h2>'+'<pre id="details"></pre>';panicMessageEl.querySelector('#message').textContent=panicTitle;panicMessageEl.querySelector('#details').textContent=panicDetails;panicElement.appendChild(panicMessageEl);rawPanicMessages.push({title:panicTitle,details:panicDetails});}
+function hasPanic(){return rawPanicMessages.length!==0;}
+function getPanicText(){return rawPanicMessages.map(function(msg){return msg.title;}).join(', ');}
+function exportTo(namespace,fn){const obj=exportPath(namespace);const exports=fn();for(const propertyName in exports){const propertyDescriptor=Object.getOwnPropertyDescriptor(exports,propertyName);if(propertyDescriptor){Object.defineProperty(obj,propertyName,propertyDescriptor);}}}
+function initialize(){if(global.isVinn){tr.isVinn=true;}else if(global.process&&global.process.versions.node){tr.isNode=true;}else{tr.isVinn=false;tr.isNode=false;tr.doc=document;tr.isMac=/Mac/.test(navigator.platform);tr.isWindows=/Win/.test(navigator.platform);tr.isChromeOS=/CrOS/.test(navigator.userAgent);tr.isLinux=/Linux/.test(navigator.userAgent);}
+tr.isHeadless=tr.isVinn||tr.isNode;}
+return{initialize,exportTo,isExported,isDefined,showPanic,hasPanic,getPanicText,};})();tr.initialize();'use strict';tr.exportTo('tr.b',function(){function EventTarget(){}
+EventTarget.decorate=function(target){for(const k in EventTarget.prototype){if(k==='decorate')continue;const v=EventTarget.prototype[k];if(typeof v!=='function')continue;target[k]=v;}};EventTarget.prototype={addEventListener(type,handler){if(!this.listeners_){this.listeners_=Object.create(null);}
+if(!(type in this.listeners_)){this.listeners_[type]=[handler];}else{const handlers=this.listeners_[type];if(handlers.indexOf(handler)<0){handlers.push(handler);}}},removeEventListener(type,handler){if(!this.listeners_)return;if(type in this.listeners_){const handlers=this.listeners_[type];const index=handlers.indexOf(handler);if(index>=0){if(handlers.length===1){delete this.listeners_[type];}else{handlers.splice(index,1);}}}},dispatchEvent(event){if(!this.listeners_)return true;event.__defineGetter__('target',()=>this);const realPreventDefault=event.preventDefault;event.preventDefault=function(){realPreventDefault.call(this);this.rawReturnValue=false;};const type=event.type;let prevented=0;if(type in this.listeners_){const handlers=this.listeners_[type].concat();for(let i=0,handler;handler=handlers[i];i++){if(handler.handleEvent){prevented|=handler.handleEvent.call(handler,event)===false;}else{prevented|=handler.call(this,event)===false;}}}
+return!prevented&&event.rawReturnValue;},async dispatchAsync(event){if(!this.listeners_)return true;const listeners=this.listeners_[event.type];if(listeners===undefined)return;await Promise.all(listeners.slice().map(listener=>{if(listener.handleEvent){return listener.handleEvent.call(listener,event);}
+return listener.call(this,event);}));},hasEventListener(type){return(this.listeners_!==undefined&&this.listeners_[type]!==undefined);}};return{EventTarget,};});'use strict';tr.exportTo('tr.b',function(){function RegisteredTypeInfo(constructor,metadata){this.constructor=constructor;this.metadata=metadata;}
+const BASIC_REGISTRY_MODE='BASIC_REGISTRY_MODE';const TYPE_BASED_REGISTRY_MODE='TYPE_BASED_REGISTRY_MODE';const ALL_MODES={BASIC_REGISTRY_MODE:true,TYPE_BASED_REGISTRY_MODE:true};function ExtensionRegistryOptions(mode){if(mode===undefined){throw new Error('Mode is required');}
+if(!ALL_MODES[mode]){throw new Error('Not a mode.');}
+this.mode_=mode;this.defaultMetadata_={};this.defaultConstructor_=undefined;this.defaultTypeInfo_=undefined;this.frozen_=false;}
+ExtensionRegistryOptions.prototype={freeze(){if(this.frozen_){throw new Error('Frozen');}
+this.frozen_=true;},get mode(){return this.mode_;},get defaultMetadata(){return this.defaultMetadata_;},set defaultMetadata(defaultMetadata){if(this.frozen_){throw new Error('Frozen');}
+this.defaultMetadata_=defaultMetadata;this.defaultTypeInfo_=undefined;},get defaultConstructor(){return this.defaultConstructor_;},set defaultConstructor(defaultConstructor){if(this.frozen_){throw new Error('Frozen');}
+this.defaultConstructor_=defaultConstructor;this.defaultTypeInfo_=undefined;},get defaultTypeInfo(){if(this.defaultTypeInfo_===undefined&&this.defaultConstructor_){this.defaultTypeInfo_=new RegisteredTypeInfo(this.defaultConstructor,this.defaultMetadata);}
+return this.defaultTypeInfo_;},validateConstructor(constructor){if(!this.mandatoryBaseClass)return;let curProto=constructor.prototype.__proto__;let ok=false;while(curProto){if(curProto===this.mandatoryBaseClass.prototype){ok=true;break;}
+curProto=curProto.__proto__;}
+if(!ok){throw new Error(constructor+'must be subclass of '+registry);}}};return{BASIC_REGISTRY_MODE,TYPE_BASED_REGISTRY_MODE,ExtensionRegistryOptions,RegisteredTypeInfo,};});'use strict';tr.exportTo('tr.b',function(){let Event;if(tr.isHeadless){function HeadlessEvent(type,opt_bubbles,opt_preventable){this.type=type;this.bubbles=(opt_bubbles!==undefined?!!opt_bubbles:false);this.cancelable=(opt_preventable!==undefined?!!opt_preventable:false);this.defaultPrevented=false;this.cancelBubble=false;}
+HeadlessEvent.prototype={preventDefault(){this.defaultPrevented=true;},stopPropagation(){this.cancelBubble=true;}};Event=HeadlessEvent;}else{function TrEvent(type,opt_bubbles,opt_preventable){const e=tr.doc.createEvent('Event');e.initEvent(type,!!opt_bubbles,!!opt_preventable);e.__proto__=global.Event.prototype;return e;}
+TrEvent.prototype={__proto__:global.Event.prototype};Event=TrEvent;}
+function dispatchSimpleEvent(target,type,opt_bubbles,opt_cancelable,opt_fields){const e=new tr.b.Event(type,opt_bubbles,opt_cancelable);Object.assign(e,opt_fields);return target.dispatchEvent(e);}
+async function dispatchSimpleEventAsync(target,type,opt_fields){const e=new tr.b.Event(type,false,false);Object.assign(e,opt_fields);return await target.dispatchAsync(e);}
+return{Event,dispatchSimpleEvent,dispatchSimpleEventAsync,};});'use strict';tr.exportTo('tr.b',function(){const RegisteredTypeInfo=tr.b.RegisteredTypeInfo;const ExtensionRegistryOptions=tr.b.ExtensionRegistryOptions;function decorateBasicExtensionRegistry(registry,extensionRegistryOptions){const savedStateStack=[];registry.registeredTypeInfos_=[];registry.register=function(constructor,opt_metadata){if(registry.findIndexOfRegisteredConstructor(constructor)!==undefined){throw new Error('Handler already registered for '+constructor);}
+extensionRegistryOptions.validateConstructor(constructor);const metadata={};for(const k in extensionRegistryOptions.defaultMetadata){metadata[k]=extensionRegistryOptions.defaultMetadata[k];}
+if(opt_metadata){for(const k in opt_metadata){metadata[k]=opt_metadata[k];}}
+const typeInfo=new RegisteredTypeInfo(constructor,metadata);let e=new tr.b.Event('will-register');e.typeInfo=typeInfo;registry.dispatchEvent(e);registry.registeredTypeInfos_.push(typeInfo);e=new tr.b.Event('registry-changed');registry.dispatchEvent(e);};registry.pushCleanStateBeforeTest=function(){savedStateStack.push(registry.registeredTypeInfos_);registry.registeredTypeInfos_=[];const e=new tr.b.Event('registry-changed');registry.dispatchEvent(e);};registry.popCleanStateAfterTest=function(){registry.registeredTypeInfos_=savedStateStack[0];savedStateStack.splice(0,1);const e=new tr.b.Event('registry-changed');registry.dispatchEvent(e);};registry.findIndexOfRegisteredConstructor=function(constructor){for(let i=0;i<registry.registeredTypeInfos_.length;i++){if(registry.registeredTypeInfos_[i].constructor===constructor){return i;}}
+return undefined;};registry.unregister=function(constructor){const foundIndex=registry.findIndexOfRegisteredConstructor(constructor);if(foundIndex===undefined){throw new Error(constructor+' not registered');}
+registry.registeredTypeInfos_.splice(foundIndex,1);const e=new tr.b.Event('registry-changed');registry.dispatchEvent(e);};registry.getAllRegisteredTypeInfos=function(){return registry.registeredTypeInfos_;};registry.findTypeInfo=function(constructor){const foundIndex=this.findIndexOfRegisteredConstructor(constructor);if(foundIndex!==undefined){return this.registeredTypeInfos_[foundIndex];}
+return undefined;};registry.findTypeInfoMatching=function(predicate,opt_this){opt_this=opt_this?opt_this:undefined;for(let i=0;i<registry.registeredTypeInfos_.length;++i){const typeInfo=registry.registeredTypeInfos_[i];if(predicate.call(opt_this,typeInfo)){return typeInfo;}}
+return extensionRegistryOptions.defaultTypeInfo;};registry.findTypeInfoWithName=function(name){if(typeof(name)!=='string'){throw new Error('Name is not a string.');}
+const typeInfo=registry.findTypeInfoMatching(function(ti){return ti.constructor.name===name;});if(typeInfo)return typeInfo;return undefined;};}
+return{_decorateBasicExtensionRegistry:decorateBasicExtensionRegistry};});'use strict';tr.exportTo('tr.b',function(){const categoryPartsFor={};function getCategoryParts(category){let parts=categoryPartsFor[category];if(parts!==undefined)return parts;parts=category.split(',');categoryPartsFor[category]=parts;return parts;}
+return{getCategoryParts,};});'use strict';tr.exportTo('tr.b',function(){const getCategoryParts=tr.b.getCategoryParts;const RegisteredTypeInfo=tr.b.RegisteredTypeInfo;const ExtensionRegistryOptions=tr.b.ExtensionRegistryOptions;function decorateTypeBasedExtensionRegistry(registry,extensionRegistryOptions){const savedStateStack=[];registry.registeredTypeInfos_=[];registry.categoryPartToTypeInfoMap_=new Map();registry.typeNameToTypeInfoMap_=new Map();registry.register=function(constructor,metadata){extensionRegistryOptions.validateConstructor(constructor);const typeInfo=new RegisteredTypeInfo(constructor,metadata||extensionRegistryOptions.defaultMetadata);typeInfo.typeNames=[];typeInfo.categoryParts=[];if(metadata&&metadata.typeName){typeInfo.typeNames.push(metadata.typeName);}
+if(metadata&&metadata.typeNames){typeInfo.typeNames.push.apply(typeInfo.typeNames,metadata.typeNames);}
+if(metadata&&metadata.categoryParts){typeInfo.categoryParts.push.apply(typeInfo.categoryParts,metadata.categoryParts);}
+if(typeInfo.typeNames.length===0&&typeInfo.categoryParts.length===0){throw new Error('typeName or typeNames must be provided');}
+typeInfo.typeNames.forEach(function(typeName){if(registry.typeNameToTypeInfoMap_.has(typeName)){throw new Error('typeName '+typeName+' already registered');}});typeInfo.categoryParts.forEach(function(categoryPart){if(registry.categoryPartToTypeInfoMap_.has(categoryPart)){throw new Error('categoryPart '+categoryPart+' already registered');}});let e=new tr.b.Event('will-register');e.typeInfo=typeInfo;registry.dispatchEvent(e);typeInfo.typeNames.forEach(function(typeName){registry.typeNameToTypeInfoMap_.set(typeName,typeInfo);});typeInfo.categoryParts.forEach(function(categoryPart){registry.categoryPartToTypeInfoMap_.set(categoryPart,typeInfo);});registry.registeredTypeInfos_.push(typeInfo);e=new tr.b.Event('registry-changed');registry.dispatchEvent(e);};registry.pushCleanStateBeforeTest=function(){savedStateStack.push({registeredTypeInfos:registry.registeredTypeInfos_,typeNameToTypeInfoMap:registry.typeNameToTypeInfoMap_,categoryPartToTypeInfoMap:registry.categoryPartToTypeInfoMap_});registry.registeredTypeInfos_=[];registry.typeNameToTypeInfoMap_=new Map();registry.categoryPartToTypeInfoMap_=new Map();const e=new tr.b.Event('registry-changed');registry.dispatchEvent(e);};registry.popCleanStateAfterTest=function(){const state=savedStateStack[0];savedStateStack.splice(0,1);registry.registeredTypeInfos_=state.registeredTypeInfos;registry.typeNameToTypeInfoMap_=state.typeNameToTypeInfoMap;registry.categoryPartToTypeInfoMap_=state.categoryPartToTypeInfoMap;const e=new tr.b.Event('registry-changed');registry.dispatchEvent(e);};registry.unregister=function(constructor){let typeInfoIndex=-1;for(let i=0;i<registry.registeredTypeInfos_.length;i++){if(registry.registeredTypeInfos_[i].constructor===constructor){typeInfoIndex=i;break;}}
+if(typeInfoIndex===-1){throw new Error(constructor+' not registered');}
+const typeInfo=registry.registeredTypeInfos_[typeInfoIndex];registry.registeredTypeInfos_.splice(typeInfoIndex,1);typeInfo.typeNames.forEach(function(typeName){registry.typeNameToTypeInfoMap_.delete(typeName);});typeInfo.categoryParts.forEach(function(categoryPart){registry.categoryPartToTypeInfoMap_.delete(categoryPart);});const e=new tr.b.Event('registry-changed');registry.dispatchEvent(e);};registry.getTypeInfo=function(category,typeName){if(category){const categoryParts=getCategoryParts(category);for(let i=0;i<categoryParts.length;i++){const categoryPart=categoryParts[i];const typeInfo=registry.categoryPartToTypeInfoMap_.get(categoryPart);if(typeInfo!==undefined)return typeInfo;}}
+const typeInfo=registry.typeNameToTypeInfoMap_.get(typeName);if(typeInfo!==undefined)return typeInfo;return extensionRegistryOptions.defaultTypeInfo;};registry.getConstructor=function(category,typeName){const typeInfo=registry.getTypeInfo(category,typeName);if(typeInfo)return typeInfo.constructor;return undefined;};}
+return{_decorateTypeBasedExtensionRegistry:decorateTypeBasedExtensionRegistry};});'use strict';tr.exportTo('tr.b',function(){const URL_REGEX=/^(https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b|file:\/\/)([-a-zA-Z0-9@:%_\+.~#?&//=]*)$/;function deepCopy(value){if(!(value instanceof Object)){if(value===undefined||value===null)return value;if(typeof value==='string')return value.substring();if(typeof value==='boolean')return value;if(typeof value==='number')return value;throw new Error('Unrecognized: '+typeof value);}
+const object=value;if(object instanceof Array){const res=new Array(object.length);for(let i=0;i<object.length;i++){res[i]=deepCopy(object[i]);}
+return res;}
+if(object.__proto__!==Object.prototype){throw new Error('Can only clone simple types');}
+const res={};for(const key in object){res[key]=deepCopy(object[key]);}
+return res;}
+function normalizeException(e){if(e===undefined||e===null){return{typeName:'UndefinedError',message:'Unknown: null or undefined exception',stack:'Unknown'};}
+if(typeof(e)==='string'){return{typeName:'StringError',message:e,stack:[e]};}
+let typeName;if(e.name){typeName=e.name;}else if(e.constructor){if(e.constructor.name){typeName=e.constructor.name;}else{typeName='AnonymousError';}}else{typeName='ErrorWithNoConstructor';}
+const msg=e.message?e.message:'Unknown';return{typeName,message:msg,stack:e.stack?e.stack:[msg]};}
+function stackTraceAsString(){return new Error().stack+'';}
+function stackTrace(){let stack=stackTraceAsString();stack=stack.split('\n');return stack.slice(2);}
+function getUsingPath(path,fromDict){const parts=path.split('.');let cur=fromDict;for(let part;parts.length&&(part=parts.shift());){if(!parts.length){return cur[part];}else if(part in cur){cur=cur[part];}else{return undefined;}}
+return undefined;}
+function formatDate(date){return date.toISOString().replace('T',' ').slice(0,19);}
+function numberToJson(n){if(isNaN(n))return'NaN';if(n===Infinity)return'Infinity';if(n===-Infinity)return'-Infinity';return n;}
+function numberFromJson(n){if(n==='NaN'||n===null)return NaN;if(n==='Infinity')return Infinity;if(n==='-Infinity')return-Infinity;return n;}
+function runLengthEncoding(ary){const encodedArray=[];for(const element of ary){if(encodedArray.length===0||encodedArray[encodedArray.length-1].value!==element){encodedArray.push({value:element,count:1,});}else{encodedArray[encodedArray.length-1].count+=1;}}
+return encodedArray;}
+function isUrl(s){return typeof(s)==='string'&&s.match(URL_REGEX)!==null;}
+function getOnlyElement(iterable){const iterator=iterable[Symbol.iterator]();const firstIteration=iterator.next();if(firstIteration.done){throw new Error('getOnlyElement was passed an empty iterable.');}
+const secondIteration=iterator.next();if(!secondIteration.done){throw new Error('getOnlyElement was passed an iterable with multiple elements.');}
+return firstIteration.value;}
+function getFirstElement(iterable){const iterator=iterable[Symbol.iterator]();const result=iterator.next();if(result.done){throw new Error('getFirstElement was passed an empty iterable.');}
+return result.value;}
+function compareArrays(x,y,elementCmp){const minLength=Math.min(x.length,y.length);let i;for(i=0;i<minLength;i++){const tmp=elementCmp(x[i],y[i]);if(tmp)return tmp;}
+if(x.length===y.length)return 0;if(x[i]===undefined)return-1;return 1;}
+function groupIntoMap(ary,callback,opt_this,opt_arrayConstructor){const arrayConstructor=opt_arrayConstructor||Array;const results=new Map();for(const element of ary){const key=callback.call(opt_this,element);let items=results.get(key);if(items===undefined){items=new arrayConstructor();results.set(key,items);}
+items.push(element);}
+return results;}
+function inPlaceFilter(array,predicate,opt_this){opt_this=opt_this||this;let nextPosition=0;for(let i=0;i<array.length;i++){if(!predicate.call(opt_this,array[i],i))continue;if(nextPosition<i){array[nextPosition]=array[i];}
+nextPosition++;}
+if(nextPosition<array.length){array.length=nextPosition;}}
+function invertArrayOfDicts(array,opt_dictGetter,opt_this){opt_this=opt_this||this;const result={};for(let i=0;i<array.length;i++){const item=array[i];if(item===undefined)continue;const dict=opt_dictGetter?opt_dictGetter.call(opt_this,item):item;if(dict===undefined)continue;for(const key in dict){let valueList=result[key];if(valueList===undefined){result[key]=valueList=new Array(array.length);}
+valueList[i]=dict[key];}}
+return result;}
+function setsEqual(a,b){if(!(a instanceof Set)||!(b instanceof Set))return false;if(a.size!==b.size)return false;for(const x of a){if(!b.has(x))return false;}
+return true;}
+function findLowIndexInSortedArray(ary,mapFn,loVal){if(ary.length===0)return 1;let low=0;let high=ary.length-1;let i;let comparison;let hitPos=-1;while(low<=high){i=Math.floor((low+high)/2);comparison=mapFn(ary[i])-loVal;if(comparison<0){low=i+1;continue;}else if(comparison>0){high=i-1;continue;}else{hitPos=i;high=i-1;}}
+return hitPos!==-1?hitPos:low;}
+function findIndexInSortedIntervals(ary,mapLoFn,mapWidthFn,loVal){const first=findLowIndexInSortedArray(ary,mapLoFn,loVal);if(first===0){if(loVal>=mapLoFn(ary[0])&&loVal<mapLoFn(ary[0])+mapWidthFn(ary[0],0)){return 0;}
+return-1;}
+if(first<ary.length){if(loVal>=mapLoFn(ary[first])&&loVal<mapLoFn(ary[first])+mapWidthFn(ary[first],first)){return first;}
+if(loVal>=mapLoFn(ary[first-1])&&loVal<mapLoFn(ary[first-1])+
+mapWidthFn(ary[first-1],first-1)){return first-1;}
+return ary.length;}
+if(first===ary.length){if(loVal>=mapLoFn(ary[first-1])&&loVal<mapLoFn(ary[first-1])+
+mapWidthFn(ary[first-1],first-1)){return first-1;}
+return ary.length;}
+return ary.length;}
+function findIndexInSortedClosedIntervals(ary,mapLoFn,mapHiFn,val){const i=findLowIndexInSortedArray(ary,mapLoFn,val);if(i===0){if(val>=mapLoFn(ary[0],0)&&val<=mapHiFn(ary[0],0)){return 0;}
+return-1;}
+if(i<ary.length){if(val>=mapLoFn(ary[i-1],i-1)&&val<=mapHiFn(ary[i-1],i-1)){return i-1;}
+if(val>=mapLoFn(ary[i],i)&&val<=mapHiFn(ary[i],i)){return i;}
+return ary.length;}
+if(i===ary.length){if(val>=mapLoFn(ary[i-1],i-1)&&val<=mapHiFn(ary[i-1],i-1)){return i-1;}
+return ary.length;}
+return ary.length;}
+function iterateOverIntersectingIntervals(ary,mapLoFn,mapWidthFn,loVal,hiVal,cb){if(ary.length===0)return;if(loVal>hiVal)return;let i=findLowIndexInSortedArray(ary,mapLoFn,loVal);if(i===-1){return;}
+if(i>0){const hi=mapLoFn(ary[i-1])+mapWidthFn(ary[i-1],i-1);if(hi>=loVal){cb(ary[i-1],i-1);}}
+if(i===ary.length){return;}
+for(let n=ary.length;i<n;i++){const lo=mapLoFn(ary[i]);if(lo>=hiVal)break;cb(ary[i],i);}}
+function findClosestElementInSortedArray(ary,mapFn,val,maxDiff){if(ary.length===0)return null;let aftIdx=findLowIndexInSortedArray(ary,mapFn,val);const befIdx=aftIdx>0?aftIdx-1:0;if(aftIdx===ary.length)aftIdx-=1;const befDiff=Math.abs(val-mapFn(ary[befIdx]));const aftDiff=Math.abs(val-mapFn(ary[aftIdx]));if(befDiff>maxDiff&&aftDiff>maxDiff)return null;const idx=befDiff<aftDiff?befIdx:aftIdx;return ary[idx];}
+function findClosestIntervalInSortedIntervals(ary,mapLoFn,mapHiFn,val,maxDiff){if(ary.length===0)return null;let idx=findLowIndexInSortedArray(ary,mapLoFn,val);if(idx>0)idx-=1;const hiInt=ary[idx];let loInt=hiInt;if(val>mapHiFn(hiInt)&&idx+1<ary.length){loInt=ary[idx+1];}
+const loDiff=Math.abs(val-mapLoFn(loInt));const hiDiff=Math.abs(val-mapHiFn(hiInt));if(loDiff>maxDiff&&hiDiff>maxDiff)return null;if(loDiff<hiDiff)return loInt;return hiInt;}
+function findFirstTrueIndexInSortedArray(array,test){let i0=0;let i1=array.length;while(i0<i1){const i=Math.trunc((i0+i1)/2);if(test(array[i])){i1=i;}else{i0=i+1;}}
+return i1;}
+return{compareArrays,deepCopy,findClosestElementInSortedArray,findClosestIntervalInSortedIntervals,findFirstTrueIndexInSortedArray,findIndexInSortedClosedIntervals,findIndexInSortedIntervals,findLowIndexInSortedArray,formatDate,getFirstElement,getOnlyElement,getUsingPath,groupIntoMap,inPlaceFilter,invertArrayOfDicts,isUrl,iterateOverIntersectingIntervals,normalizeException,numberFromJson,numberToJson,runLengthEncoding,setsEqual,stackTrace,stackTraceAsString,};});'use strict';tr.exportTo('tr.b',function(){function decorateExtensionRegistry(registry,registryOptions){if(registry.register){throw new Error('Already has registry');}
+registryOptions.freeze();if(registryOptions.mode===tr.b.BASIC_REGISTRY_MODE){tr.b._decorateBasicExtensionRegistry(registry,registryOptions);}else if(registryOptions.mode===tr.b.TYPE_BASED_REGISTRY_MODE){tr.b._decorateTypeBasedExtensionRegistry(registry,registryOptions);}else{throw new Error('Unrecognized mode');}
+if(registry.addEventListener===undefined){tr.b.EventTarget.decorate(registry);}}
+return{decorateExtensionRegistry,};});'use strict';tr.exportTo('tr.importer',function(){function Importer(){}
+Importer.prototype={__proto__:Object.prototype,get importerName(){return'Importer';},isTraceDataContainer(){return false;},extractSubtraces(){return[];},importClockSyncMarkers(){},importEvents(){},importSampleData(){},finalizeImport(){}};const options=new tr.b.ExtensionRegistryOptions(tr.b.BASIC_REGISTRY_MODE);options.defaultMetadata={};options.mandatoryBaseClass=Importer;tr.b.decorateExtensionRegistry(Importer,options);Importer.findImporterFor=function(eventData){const typeInfo=Importer.findTypeInfoMatching(function(ti){return ti.constructor.canImport(eventData);});if(typeInfo){return typeInfo.constructor;}
+return undefined;};return{Importer,};});'use strict';tr.exportTo('tr.e.importer.gcloud_trace',function(){function GcloudTraceImporter(model,eventData){this.importPriority=2;this.eventData_=eventData;}
+GcloudTraceImporter.canImport=function(eventData){if(typeof(eventData)!=='string'&&!(eventData instanceof String)){return false;}
+const normalizedEventData=eventData.slice(0,20).replace(/\s/g,'');if(normalizedEventData.length<14)return false;return normalizedEventData.slice(0,14)==='{"projectId":"';};GcloudTraceImporter.prototype={__proto__:tr.importer.Importer.prototype,get importerName(){return'GcloudTraceImporter';},extractSubtraces(){const traceEvents=this.createEventsForTrace();return traceEvents?[traceEvents]:[];},createEventsForTrace(){const events=[];const trace=JSON.parse(this.eventData_);const spanLength=trace.spans.length;for(let i=0;i<spanLength;i++){events.push(this.createEventForSpan(trace.traceId,trace.spans[i]));}
+return{'traceEvents':events};},createEventForSpan(traceId,span){let newArgs={};if(span.labels){newArgs=JSON.parse(JSON.stringify(span.labels));}
+newArgs['Span ID']=span.spanId;newArgs['Start Time']=span.startTime;newArgs['End Time']=span.endTime;if(span.parentSpanId){newArgs['Parent Span ID']=span.parentSpanId;}
+return{name:span.name,args:newArgs,pid:traceId,ts:Date.parse(span.startTime)*1000,dur:(Date.parse(span.endTime)-Date.parse(span.startTime))*1000,cat:'tracespan',tid:traceId,ph:'X'};}};tr.importer.Importer.register(GcloudTraceImporter);return{GcloudTraceImporter,};});'use strict';tr.exportTo('tr.b.math',function(){function convertEventsToRanges(events){return events.map(function(event){return tr.b.math.Range.fromExplicitRange(event.start,event.end);});}
+function mergeRanges(inRanges,mergeThreshold,mergeFunction){const remainingEvents=inRanges.slice();remainingEvents.sort(function(x,y){return x.min-y.min;});if(remainingEvents.length<=1){const merged=[];if(remainingEvents.length===1){merged.push(mergeFunction(remainingEvents));}
+return merged;}
+const mergedEvents=[];let currentMergeBuffer=[];let rightEdge;function beginMerging(){currentMergeBuffer.push(remainingEvents[0]);remainingEvents.splice(0,1);rightEdge=currentMergeBuffer[0].max;}
+function flushCurrentMergeBuffer(){if(currentMergeBuffer.length===0)return;mergedEvents.push(mergeFunction(currentMergeBuffer));currentMergeBuffer=[];if(remainingEvents.length!==0)beginMerging();}
+beginMerging();while(remainingEvents.length){const currentEvent=remainingEvents[0];const distanceFromRightEdge=currentEvent.min-rightEdge;if(distanceFromRightEdge<mergeThreshold){rightEdge=Math.max(rightEdge,currentEvent.max);remainingEvents.splice(0,1);currentMergeBuffer.push(currentEvent);continue;}
+flushCurrentMergeBuffer();}
+flushCurrentMergeBuffer();return mergedEvents;}
+function findEmptyRangesBetweenRanges(inRanges,opt_totalRange){if(opt_totalRange&&opt_totalRange.isEmpty)opt_totalRange=undefined;const emptyRanges=[];if(!inRanges.length){if(opt_totalRange)emptyRanges.push(opt_totalRange);return emptyRanges;}
+inRanges=inRanges.slice();inRanges.sort(function(x,y){return x.min-y.min;});if(opt_totalRange&&(opt_totalRange.min<inRanges[0].min)){emptyRanges.push(tr.b.math.Range.fromExplicitRange(opt_totalRange.min,inRanges[0].min));}
+inRanges.forEach(function(range,index){for(let otherIndex=0;otherIndex<inRanges.length;++otherIndex){if(index===otherIndex)continue;const other=inRanges[otherIndex];if(other.min>range.max){emptyRanges.push(tr.b.math.Range.fromExplicitRange(range.max,other.min));return;}
+if(other.max>range.max){return;}}
+if(opt_totalRange&&(range.max<opt_totalRange.max)){emptyRanges.push(tr.b.math.Range.fromExplicitRange(range.max,opt_totalRange.max));}});return emptyRanges;}
+return{convertEventsToRanges,findEmptyRangesBetweenRanges,mergeRanges,};});!function(t,n){if("object"==typeof exports&&"object"==typeof module)module.exports=n();else if("function"==typeof define&&define.amd)define(n);else{var r=n();for(var a in r)("object"==typeof exports?exports:t)[a]=r[a]}}(this,function(){return function(t){function n(a){if(r[a])return r[a].exports;var e=r[a]={exports:{},id:a,loaded:!1};return t[a].call(e.exports,e,e.exports,n),e.loaded=!0,e.exports}var r={};return n.m=t,n.c=r,n.p="",n(0)}([function(t,n,r){n.glMatrix=r(1),n.mat2=r(2),n.mat2d=r(3),n.mat3=r(4),n.mat4=r(5),n.quat=r(6),n.vec2=r(9),n.vec3=r(7),n.vec4=r(8)},function(t,n,r){var a={};a.EPSILON=1e-6,a.ARRAY_TYPE="undefined"!=typeof Float32Array?Float32Array:Array,a.RANDOM=Math.random,a.setMatrixArrayType=function(t){GLMAT_ARRAY_TYPE=t};var e=Math.PI/180;a.toRadian=function(t){return t*e},t.exports=a},function(t,n,r){var a=r(1),e={};e.create=function(){var t=new a.ARRAY_TYPE(4);return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t},e.clone=function(t){var n=new a.ARRAY_TYPE(4);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=t[3],n},e.copy=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t},e.identity=function(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t},e.transpose=function(t,n){if(t===n){var r=n[1];t[1]=n[2],t[2]=r}else t[0]=n[0],t[1]=n[2],t[2]=n[1],t[3]=n[3];return t},e.invert=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=r*u-e*a;return o?(o=1/o,t[0]=u*o,t[1]=-a*o,t[2]=-e*o,t[3]=r*o,t):null},e.adjoint=function(t,n){var r=n[0];return t[0]=n[3],t[1]=-n[1],t[2]=-n[2],t[3]=r,t},e.determinant=function(t){return t[0]*t[3]-t[2]*t[1]},e.multiply=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=r[0],c=r[1],f=r[2],s=r[3];return t[0]=a*i+u*c,t[1]=e*i+o*c,t[2]=a*f+u*s,t[3]=e*f+o*s,t},e.mul=e.multiply,e.rotate=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=Math.sin(r),c=Math.cos(r);return t[0]=a*c+u*i,t[1]=e*c+o*i,t[2]=a*-i+u*c,t[3]=e*-i+o*c,t},e.scale=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=r[0],c=r[1];return t[0]=a*i,t[1]=e*i,t[2]=u*c,t[3]=o*c,t},e.fromRotation=function(t,n){var r=Math.sin(n),a=Math.cos(n);return t[0]=a,t[1]=r,t[2]=-r,t[3]=a,t},e.fromScaling=function(t,n){return t[0]=n[0],t[1]=0,t[2]=0,t[3]=n[1],t},e.str=function(t){return"mat2("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+")"},e.frob=function(t){return Math.sqrt(Math.pow(t[0],2)+Math.pow(t[1],2)+Math.pow(t[2],2)+Math.pow(t[3],2))},e.LDU=function(t,n,r,a){return t[2]=a[2]/a[0],r[0]=a[0],r[1]=a[1],r[3]=a[3]-t[2]*r[1],[t,n,r]},t.exports=e},function(t,n,r){var a=r(1),e={};e.create=function(){var t=new a.ARRAY_TYPE(6);return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t[4]=0,t[5]=0,t},e.clone=function(t){var n=new a.ARRAY_TYPE(6);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=t[3],n[4]=t[4],n[5]=t[5],n},e.copy=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t[4]=n[4],t[5]=n[5],t},e.identity=function(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t[4]=0,t[5]=0,t},e.invert=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=n[4],i=n[5],c=r*u-a*e;return c?(c=1/c,t[0]=u*c,t[1]=-a*c,t[2]=-e*c,t[3]=r*c,t[4]=(e*i-u*o)*c,t[5]=(a*o-r*i)*c,t):null},e.determinant=function(t){return t[0]*t[3]-t[1]*t[2]},e.multiply=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],c=n[5],f=r[0],s=r[1],h=r[2],M=r[3],l=r[4],v=r[5];return t[0]=a*f+u*s,t[1]=e*f+o*s,t[2]=a*h+u*M,t[3]=e*h+o*M,t[4]=a*l+u*v+i,t[5]=e*l+o*v+c,t},e.mul=e.multiply,e.rotate=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],c=n[5],f=Math.sin(r),s=Math.cos(r);return t[0]=a*s+u*f,t[1]=e*s+o*f,t[2]=a*-f+u*s,t[3]=e*-f+o*s,t[4]=i,t[5]=c,t},e.scale=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],c=n[5],f=r[0],s=r[1];return t[0]=a*f,t[1]=e*f,t[2]=u*s,t[3]=o*s,t[4]=i,t[5]=c,t},e.translate=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],c=n[5],f=r[0],s=r[1];return t[0]=a,t[1]=e,t[2]=u,t[3]=o,t[4]=a*f+u*s+i,t[5]=e*f+o*s+c,t},e.fromRotation=function(t,n){var r=Math.sin(n),a=Math.cos(n);return t[0]=a,t[1]=r,t[2]=-r,t[3]=a,t[4]=0,t[5]=0,t},e.fromScaling=function(t,n){return t[0]=n[0],t[1]=0,t[2]=0,t[3]=n[1],t[4]=0,t[5]=0,t},e.fromTranslation=function(t,n){return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t[4]=n[0],t[5]=n[1],t},e.str=function(t){return"mat2d("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+", "+t[4]+", "+t[5]+")"},e.frob=function(t){return Math.sqrt(Math.pow(t[0],2)+Math.pow(t[1],2)+Math.pow(t[2],2)+Math.pow(t[3],2)+Math.pow(t[4],2)+Math.pow(t[5],2)+1)},t.exports=e},function(t,n,r){var a=r(1),e={};e.create=function(){var t=new a.ARRAY_TYPE(9);return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=1,t[5]=0,t[6]=0,t[7]=0,t[8]=1,t},e.fromMat4=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[4],t[4]=n[5],t[5]=n[6],t[6]=n[8],t[7]=n[9],t[8]=n[10],t},e.clone=function(t){var n=new a.ARRAY_TYPE(9);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=t[3],n[4]=t[4],n[5]=t[5],n[6]=t[6],n[7]=t[7],n[8]=t[8],n},e.copy=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t[4]=n[4],t[5]=n[5],t[6]=n[6],t[7]=n[7],t[8]=n[8],t},e.identity=function(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=1,t[5]=0,t[6]=0,t[7]=0,t[8]=1,t},e.transpose=function(t,n){if(t===n){var r=n[1],a=n[2],e=n[5];t[1]=n[3],t[2]=n[6],t[3]=r,t[5]=n[7],t[6]=a,t[7]=e}else t[0]=n[0],t[1]=n[3],t[2]=n[6],t[3]=n[1],t[4]=n[4],t[5]=n[7],t[6]=n[2],t[7]=n[5],t[8]=n[8];return t},e.invert=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=n[4],i=n[5],c=n[6],f=n[7],s=n[8],h=s*o-i*f,M=-s*u+i*c,l=f*u-o*c,v=r*h+a*M+e*l;return v?(v=1/v,t[0]=h*v,t[1]=(-s*a+e*f)*v,t[2]=(i*a-e*o)*v,t[3]=M*v,t[4]=(s*r-e*c)*v,t[5]=(-i*r+e*u)*v,t[6]=l*v,t[7]=(-f*r+a*c)*v,t[8]=(o*r-a*u)*v,t):null},e.adjoint=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=n[4],i=n[5],c=n[6],f=n[7],s=n[8];return t[0]=o*s-i*f,t[1]=e*f-a*s,t[2]=a*i-e*o,t[3]=i*c-u*s,t[4]=r*s-e*c,t[5]=e*u-r*i,t[6]=u*f-o*c,t[7]=a*c-r*f,t[8]=r*o-a*u,t},e.determinant=function(t){var n=t[0],r=t[1],a=t[2],e=t[3],u=t[4],o=t[5],i=t[6],c=t[7],f=t[8];return n*(f*u-o*c)+r*(-f*e+o*i)+a*(c*e-u*i)},e.multiply=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],c=n[5],f=n[6],s=n[7],h=n[8],M=r[0],l=r[1],v=r[2],m=r[3],p=r[4],d=r[5],A=r[6],R=r[7],w=r[8];return t[0]=M*a+l*o+v*f,t[1]=M*e+l*i+v*s,t[2]=M*u+l*c+v*h,t[3]=m*a+p*o+d*f,t[4]=m*e+p*i+d*s,t[5]=m*u+p*c+d*h,t[6]=A*a+R*o+w*f,t[7]=A*e+R*i+w*s,t[8]=A*u+R*c+w*h,t},e.mul=e.multiply,e.translate=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],c=n[5],f=n[6],s=n[7],h=n[8],M=r[0],l=r[1];return t[0]=a,t[1]=e,t[2]=u,t[3]=o,t[4]=i,t[5]=c,t[6]=M*a+l*o+f,t[7]=M*e+l*i+s,t[8]=M*u+l*c+h,t},e.rotate=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],c=n[5],f=n[6],s=n[7],h=n[8],M=Math.sin(r),l=Math.cos(r);return t[0]=l*a+M*o,t[1]=l*e+M*i,t[2]=l*u+M*c,t[3]=l*o-M*a,t[4]=l*i-M*e,t[5]=l*c-M*u,t[6]=f,t[7]=s,t[8]=h,t},e.scale=function(t,n,r){var a=r[0],e=r[1];return t[0]=a*n[0],t[1]=a*n[1],t[2]=a*n[2],t[3]=e*n[3],t[4]=e*n[4],t[5]=e*n[5],t[6]=n[6],t[7]=n[7],t[8]=n[8],t},e.fromTranslation=function(t,n){return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=1,t[5]=0,t[6]=n[0],t[7]=n[1],t[8]=1,t},e.fromRotation=function(t,n){var r=Math.sin(n),a=Math.cos(n);return t[0]=a,t[1]=r,t[2]=0,t[3]=-r,t[4]=a,t[5]=0,t[6]=0,t[7]=0,t[8]=1,t},e.fromScaling=function(t,n){return t[0]=n[0],t[1]=0,t[2]=0,t[3]=0,t[4]=n[1],t[5]=0,t[6]=0,t[7]=0,t[8]=1,t},e.fromMat2d=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=0,t[3]=n[2],t[4]=n[3],t[5]=0,t[6]=n[4],t[7]=n[5],t[8]=1,t},e.fromQuat=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=r+r,i=a+a,c=e+e,f=r*o,s=a*o,h=a*i,M=e*o,l=e*i,v=e*c,m=u*o,p=u*i,d=u*c;return t[0]=1-h-v,t[3]=s-d,t[6]=M+p,t[1]=s+d,t[4]=1-f-v,t[7]=l-m,t[2]=M-p,t[5]=l+m,t[8]=1-f-h,t},e.normalFromMat4=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=n[4],i=n[5],c=n[6],f=n[7],s=n[8],h=n[9],M=n[10],l=n[11],v=n[12],m=n[13],p=n[14],d=n[15],A=r*i-a*o,R=r*c-e*o,w=r*f-u*o,q=a*c-e*i,Y=a*f-u*i,g=e*f-u*c,y=s*m-h*v,x=s*p-M*v,P=s*d-l*v,E=h*p-M*m,T=h*d-l*m,b=M*d-l*p,D=A*b-R*T+w*E+q*P-Y*x+g*y;return D?(D=1/D,t[0]=(i*b-c*T+f*E)*D,t[1]=(c*P-o*b-f*x)*D,t[2]=(o*T-i*P+f*y)*D,t[3]=(e*T-a*b-u*E)*D,t[4]=(r*b-e*P+u*x)*D,t[5]=(a*P-r*T-u*y)*D,t[6]=(m*g-p*Y+d*q)*D,t[7]=(p*w-v*g-d*R)*D,t[8]=(v*Y-m*w+d*A)*D,t):null},e.str=function(t){return"mat3("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+", "+t[4]+", "+t[5]+", "+t[6]+", "+t[7]+", "+t[8]+")"},e.frob=function(t){return Math.sqrt(Math.pow(t[0],2)+Math.pow(t[1],2)+Math.pow(t[2],2)+Math.pow(t[3],2)+Math.pow(t[4],2)+Math.pow(t[5],2)+Math.pow(t[6],2)+Math.pow(t[7],2)+Math.pow(t[8],2))},t.exports=e},function(t,n,r){var a=r(1),e={};e.create=function(){var t=new a.ARRAY_TYPE(16);return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=1,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=1,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t},e.clone=function(t){var n=new a.ARRAY_TYPE(16);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=t[3],n[4]=t[4],n[5]=t[5],n[6]=t[6],n[7]=t[7],n[8]=t[8],n[9]=t[9],n[10]=t[10],n[11]=t[11],n[12]=t[12],n[13]=t[13],n[14]=t[14],n[15]=t[15],n},e.copy=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t[4]=n[4],t[5]=n[5],t[6]=n[6],t[7]=n[7],t[8]=n[8],t[9]=n[9],t[10]=n[10],t[11]=n[11],t[12]=n[12],t[13]=n[13],t[14]=n[14],t[15]=n[15],t},e.identity=function(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=1,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=1,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t},e.transpose=function(t,n){if(t===n){var r=n[1],a=n[2],e=n[3],u=n[6],o=n[7],i=n[11];t[1]=n[4],t[2]=n[8],t[3]=n[12],t[4]=r,t[6]=n[9],t[7]=n[13],t[8]=a,t[9]=u,t[11]=n[14],t[12]=e,t[13]=o,t[14]=i}else t[0]=n[0],t[1]=n[4],t[2]=n[8],t[3]=n[12],t[4]=n[1],t[5]=n[5],t[6]=n[9],t[7]=n[13],t[8]=n[2],t[9]=n[6],t[10]=n[10],t[11]=n[14],t[12]=n[3],t[13]=n[7],t[14]=n[11],t[15]=n[15];return t},e.invert=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=n[4],i=n[5],c=n[6],f=n[7],s=n[8],h=n[9],M=n[10],l=n[11],v=n[12],m=n[13],p=n[14],d=n[15],A=r*i-a*o,R=r*c-e*o,w=r*f-u*o,q=a*c-e*i,Y=a*f-u*i,g=e*f-u*c,y=s*m-h*v,x=s*p-M*v,P=s*d-l*v,E=h*p-M*m,T=h*d-l*m,b=M*d-l*p,D=A*b-R*T+w*E+q*P-Y*x+g*y;return D?(D=1/D,t[0]=(i*b-c*T+f*E)*D,t[1]=(e*T-a*b-u*E)*D,t[2]=(m*g-p*Y+d*q)*D,t[3]=(M*Y-h*g-l*q)*D,t[4]=(c*P-o*b-f*x)*D,t[5]=(r*b-e*P+u*x)*D,t[6]=(p*w-v*g-d*R)*D,t[7]=(s*g-M*w+l*R)*D,t[8]=(o*T-i*P+f*y)*D,t[9]=(a*P-r*T-u*y)*D,t[10]=(v*Y-m*w+d*A)*D,t[11]=(h*w-s*Y-l*A)*D,t[12]=(i*x-o*E-c*y)*D,t[13]=(r*E-a*x+e*y)*D,t[14]=(m*R-v*q-p*A)*D,t[15]=(s*q-h*R+M*A)*D,t):null},e.adjoint=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=n[4],i=n[5],c=n[6],f=n[7],s=n[8],h=n[9],M=n[10],l=n[11],v=n[12],m=n[13],p=n[14],d=n[15];return t[0]=i*(M*d-l*p)-h*(c*d-f*p)+m*(c*l-f*M),t[1]=-(a*(M*d-l*p)-h*(e*d-u*p)+m*(e*l-u*M)),t[2]=a*(c*d-f*p)-i*(e*d-u*p)+m*(e*f-u*c),t[3]=-(a*(c*l-f*M)-i*(e*l-u*M)+h*(e*f-u*c)),t[4]=-(o*(M*d-l*p)-s*(c*d-f*p)+v*(c*l-f*M)),t[5]=r*(M*d-l*p)-s*(e*d-u*p)+v*(e*l-u*M),t[6]=-(r*(c*d-f*p)-o*(e*d-u*p)+v*(e*f-u*c)),t[7]=r*(c*l-f*M)-o*(e*l-u*M)+s*(e*f-u*c),t[8]=o*(h*d-l*m)-s*(i*d-f*m)+v*(i*l-f*h),t[9]=-(r*(h*d-l*m)-s*(a*d-u*m)+v*(a*l-u*h)),t[10]=r*(i*d-f*m)-o*(a*d-u*m)+v*(a*f-u*i),t[11]=-(r*(i*l-f*h)-o*(a*l-u*h)+s*(a*f-u*i)),t[12]=-(o*(h*p-M*m)-s*(i*p-c*m)+v*(i*M-c*h)),t[13]=r*(h*p-M*m)-s*(a*p-e*m)+v*(a*M-e*h),t[14]=-(r*(i*p-c*m)-o*(a*p-e*m)+v*(a*c-e*i)),t[15]=r*(i*M-c*h)-o*(a*M-e*h)+s*(a*c-e*i),t},e.determinant=function(t){var n=t[0],r=t[1],a=t[2],e=t[3],u=t[4],o=t[5],i=t[6],c=t[7],f=t[8],s=t[9],h=t[10],M=t[11],l=t[12],v=t[13],m=t[14],p=t[15],d=n*o-r*u,A=n*i-a*u,R=n*c-e*u,w=r*i-a*o,q=r*c-e*o,Y=a*c-e*i,g=f*v-s*l,y=f*m-h*l,x=f*p-M*l,P=s*m-h*v,E=s*p-M*v,T=h*p-M*m;return d*T-A*E+R*P+w*x-q*y+Y*g},e.multiply=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],c=n[5],f=n[6],s=n[7],h=n[8],M=n[9],l=n[10],v=n[11],m=n[12],p=n[13],d=n[14],A=n[15],R=r[0],w=r[1],q=r[2],Y=r[3];return t[0]=R*a+w*i+q*h+Y*m,t[1]=R*e+w*c+q*M+Y*p,t[2]=R*u+w*f+q*l+Y*d,t[3]=R*o+w*s+q*v+Y*A,R=r[4],w=r[5],q=r[6],Y=r[7],t[4]=R*a+w*i+q*h+Y*m,t[5]=R*e+w*c+q*M+Y*p,t[6]=R*u+w*f+q*l+Y*d,t[7]=R*o+w*s+q*v+Y*A,R=r[8],w=r[9],q=r[10],Y=r[11],t[8]=R*a+w*i+q*h+Y*m,t[9]=R*e+w*c+q*M+Y*p,t[10]=R*u+w*f+q*l+Y*d,t[11]=R*o+w*s+q*v+Y*A,R=r[12],w=r[13],q=r[14],Y=r[15],t[12]=R*a+w*i+q*h+Y*m,t[13]=R*e+w*c+q*M+Y*p,t[14]=R*u+w*f+q*l+Y*d,t[15]=R*o+w*s+q*v+Y*A,t},e.mul=e.multiply,e.translate=function(t,n,r){var a,e,u,o,i,c,f,s,h,M,l,v,m=r[0],p=r[1],d=r[2];return n===t?(t[12]=n[0]*m+n[4]*p+n[8]*d+n[12],t[13]=n[1]*m+n[5]*p+n[9]*d+n[13],t[14]=n[2]*m+n[6]*p+n[10]*d+n[14],t[15]=n[3]*m+n[7]*p+n[11]*d+n[15]):(a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],c=n[5],f=n[6],s=n[7],h=n[8],M=n[9],l=n[10],v=n[11],t[0]=a,t[1]=e,t[2]=u,t[3]=o,t[4]=i,t[5]=c,t[6]=f,t[7]=s,t[8]=h,t[9]=M,t[10]=l,t[11]=v,t[12]=a*m+i*p+h*d+n[12],t[13]=e*m+c*p+M*d+n[13],t[14]=u*m+f*p+l*d+n[14],t[15]=o*m+s*p+v*d+n[15]),t},e.scale=function(t,n,r){var a=r[0],e=r[1],u=r[2];return t[0]=n[0]*a,t[1]=n[1]*a,t[2]=n[2]*a,t[3]=n[3]*a,t[4]=n[4]*e,t[5]=n[5]*e,t[6]=n[6]*e,t[7]=n[7]*e,t[8]=n[8]*u,t[9]=n[9]*u,t[10]=n[10]*u,t[11]=n[11]*u,t[12]=n[12],t[13]=n[13],t[14]=n[14],t[15]=n[15],t},e.rotate=function(t,n,r,e){var u,o,i,c,f,s,h,M,l,v,m,p,d,A,R,w,q,Y,g,y,x,P,E,T,b=e[0],D=e[1],L=e[2],_=Math.sqrt(b*b+D*D+L*L);return Math.abs(_)<a.EPSILON?null:(_=1/_,b*=_,D*=_,L*=_,u=Math.sin(r),o=Math.cos(r),i=1-o,c=n[0],f=n[1],s=n[2],h=n[3],M=n[4],l=n[5],v=n[6],m=n[7],p=n[8],d=n[9],A=n[10],R=n[11],w=b*b*i+o,q=D*b*i+L*u,Y=L*b*i-D*u,g=b*D*i-L*u,y=D*D*i+o,x=L*D*i+b*u,P=b*L*i+D*u,E=D*L*i-b*u,T=L*L*i+o,t[0]=c*w+M*q+p*Y,t[1]=f*w+l*q+d*Y,t[2]=s*w+v*q+A*Y,t[3]=h*w+m*q+R*Y,t[4]=c*g+M*y+p*x,t[5]=f*g+l*y+d*x,t[6]=s*g+v*y+A*x,t[7]=h*g+m*y+R*x,t[8]=c*P+M*E+p*T,t[9]=f*P+l*E+d*T,t[10]=s*P+v*E+A*T,t[11]=h*P+m*E+R*T,n!==t&&(t[12]=n[12],t[13]=n[13],t[14]=n[14],t[15]=n[15]),t)},e.rotateX=function(t,n,r){var a=Math.sin(r),e=Math.cos(r),u=n[4],o=n[5],i=n[6],c=n[7],f=n[8],s=n[9],h=n[10],M=n[11];return n!==t&&(t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t[12]=n[12],t[13]=n[13],t[14]=n[14],t[15]=n[15]),t[4]=u*e+f*a,t[5]=o*e+s*a,t[6]=i*e+h*a,t[7]=c*e+M*a,t[8]=f*e-u*a,t[9]=s*e-o*a,t[10]=h*e-i*a,t[11]=M*e-c*a,t},e.rotateY=function(t,n,r){var a=Math.sin(r),e=Math.cos(r),u=n[0],o=n[1],i=n[2],c=n[3],f=n[8],s=n[9],h=n[10],M=n[11];return n!==t&&(t[4]=n[4],t[5]=n[5],t[6]=n[6],t[7]=n[7],t[12]=n[12],t[13]=n[13],t[14]=n[14],t[15]=n[15]),t[0]=u*e-f*a,t[1]=o*e-s*a,t[2]=i*e-h*a,t[3]=c*e-M*a,t[8]=u*a+f*e,t[9]=o*a+s*e,t[10]=i*a+h*e,t[11]=c*a+M*e,t},e.rotateZ=function(t,n,r){var a=Math.sin(r),e=Math.cos(r),u=n[0],o=n[1],i=n[2],c=n[3],f=n[4],s=n[5],h=n[6],M=n[7];return n!==t&&(t[8]=n[8],t[9]=n[9],t[10]=n[10],t[11]=n[11],t[12]=n[12],t[13]=n[13],t[14]=n[14],t[15]=n[15]),t[0]=u*e+f*a,t[1]=o*e+s*a,t[2]=i*e+h*a,t[3]=c*e+M*a,t[4]=f*e-u*a,t[5]=s*e-o*a,t[6]=h*e-i*a,t[7]=M*e-c*a,t},e.fromTranslation=function(t,n){return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=1,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=1,t[11]=0,t[12]=n[0],t[13]=n[1],t[14]=n[2],t[15]=1,t},e.fromScaling=function(t,n){return t[0]=n[0],t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=n[1],t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=n[2],t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t},e.fromRotation=function(t,n,r){var e,u,o,i=r[0],c=r[1],f=r[2],s=Math.sqrt(i*i+c*c+f*f);return Math.abs(s)<a.EPSILON?null:(s=1/s,i*=s,c*=s,f*=s,e=Math.sin(n),u=Math.cos(n),o=1-u,t[0]=i*i*o+u,t[1]=c*i*o+f*e,t[2]=f*i*o-c*e,t[3]=0,t[4]=i*c*o-f*e,t[5]=c*c*o+u,t[6]=f*c*o+i*e,t[7]=0,t[8]=i*f*o+c*e,t[9]=c*f*o-i*e,t[10]=f*f*o+u,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t)},e.fromXRotation=function(t,n){var r=Math.sin(n),a=Math.cos(n);return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=a,t[6]=r,t[7]=0,t[8]=0,t[9]=-r,t[10]=a,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t},e.fromYRotation=function(t,n){var r=Math.sin(n),a=Math.cos(n);return t[0]=a,t[1]=0,t[2]=-r,t[3]=0,t[4]=0,t[5]=1,t[6]=0,t[7]=0,t[8]=r,t[9]=0,t[10]=a,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t},e.fromZRotation=function(t,n){var r=Math.sin(n),a=Math.cos(n);return t[0]=a,t[1]=r,t[2]=0,t[3]=0,t[4]=-r,t[5]=a,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=1,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t},e.fromRotationTranslation=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=a+a,c=e+e,f=u+u,s=a*i,h=a*c,M=a*f,l=e*c,v=e*f,m=u*f,p=o*i,d=o*c,A=o*f;return t[0]=1-(l+m),t[1]=h+A,t[2]=M-d,t[3]=0,t[4]=h-A,t[5]=1-(s+m),t[6]=v+p,t[7]=0,t[8]=M+d,t[9]=v-p,t[10]=1-(s+l),t[11]=0,t[12]=r[0],t[13]=r[1],t[14]=r[2],t[15]=1,t},e.fromRotationTranslationScale=function(t,n,r,a){var e=n[0],u=n[1],o=n[2],i=n[3],c=e+e,f=u+u,s=o+o,h=e*c,M=e*f,l=e*s,v=u*f,m=u*s,p=o*s,d=i*c,A=i*f,R=i*s,w=a[0],q=a[1],Y=a[2];return t[0]=(1-(v+p))*w,t[1]=(M+R)*w,t[2]=(l-A)*w,t[3]=0,t[4]=(M-R)*q,t[5]=(1-(h+p))*q,t[6]=(m+d)*q,t[7]=0,t[8]=(l+A)*Y,t[9]=(m-d)*Y,t[10]=(1-(h+v))*Y,t[11]=0,t[12]=r[0],t[13]=r[1],t[14]=r[2],t[15]=1,t},e.fromRotationTranslationScaleOrigin=function(t,n,r,a,e){var u=n[0],o=n[1],i=n[2],c=n[3],f=u+u,s=o+o,h=i+i,M=u*f,l=u*s,v=u*h,m=o*s,p=o*h,d=i*h,A=c*f,R=c*s,w=c*h,q=a[0],Y=a[1],g=a[2],y=e[0],x=e[1],P=e[2];return t[0]=(1-(m+d))*q,t[1]=(l+w)*q,t[2]=(v-R)*q,t[3]=0,t[4]=(l-w)*Y,t[5]=(1-(M+d))*Y,t[6]=(p+A)*Y,t[7]=0,t[8]=(v+R)*g,t[9]=(p-A)*g,t[10]=(1-(M+m))*g,t[11]=0,t[12]=r[0]+y-(t[0]*y+t[4]*x+t[8]*P),t[13]=r[1]+x-(t[1]*y+t[5]*x+t[9]*P),t[14]=r[2]+P-(t[2]*y+t[6]*x+t[10]*P),t[15]=1,t},e.fromQuat=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=r+r,i=a+a,c=e+e,f=r*o,s=a*o,h=a*i,M=e*o,l=e*i,v=e*c,m=u*o,p=u*i,d=u*c;return t[0]=1-h-v,t[1]=s+d,t[2]=M-p,t[3]=0,t[4]=s-d,t[5]=1-f-v,t[6]=l+m,t[7]=0,t[8]=M+p,t[9]=l-m,t[10]=1-f-h,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t},e.frustum=function(t,n,r,a,e,u,o){var i=1/(r-n),c=1/(e-a),f=1/(u-o);return t[0]=2*u*i,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=2*u*c,t[6]=0,t[7]=0,t[8]=(r+n)*i,t[9]=(e+a)*c,t[10]=(o+u)*f,t[11]=-1,t[12]=0,t[13]=0,t[14]=o*u*2*f,t[15]=0,t},e.perspective=function(t,n,r,a,e){var u=1/Math.tan(n/2),o=1/(a-e);return t[0]=u/r,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=u,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=(e+a)*o,t[11]=-1,t[12]=0,t[13]=0,t[14]=2*e*a*o,t[15]=0,t},e.perspectiveFromFieldOfView=function(t,n,r,a){var e=Math.tan(n.upDegrees*Math.PI/180),u=Math.tan(n.downDegrees*Math.PI/180),o=Math.tan(n.leftDegrees*Math.PI/180),i=Math.tan(n.rightDegrees*Math.PI/180),c=2/(o+i),f=2/(e+u);return t[0]=c,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=f,t[6]=0,t[7]=0,t[8]=-((o-i)*c*.5),t[9]=(e-u)*f*.5,t[10]=a/(r-a),t[11]=-1,t[12]=0,t[13]=0,t[14]=a*r/(r-a),t[15]=0,t},e.ortho=function(t,n,r,a,e,u,o){var i=1/(n-r),c=1/(a-e),f=1/(u-o);return t[0]=-2*i,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=-2*c,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=2*f,t[11]=0,t[12]=(n+r)*i,t[13]=(e+a)*c,t[14]=(o+u)*f,t[15]=1,t},e.lookAt=function(t,n,r,u){var o,i,c,f,s,h,M,l,v,m,p=n[0],d=n[1],A=n[2],R=u[0],w=u[1],q=u[2],Y=r[0],g=r[1],y=r[2];return Math.abs(p-Y)<a.EPSILON&&Math.abs(d-g)<a.EPSILON&&Math.abs(A-y)<a.EPSILON?e.identity(t):(M=p-Y,l=d-g,v=A-y,m=1/Math.sqrt(M*M+l*l+v*v),M*=m,l*=m,v*=m,o=w*v-q*l,i=q*M-R*v,c=R*l-w*M,m=Math.sqrt(o*o+i*i+c*c),m?(m=1/m,o*=m,i*=m,c*=m):(o=0,i=0,c=0),f=l*c-v*i,s=v*o-M*c,h=M*i-l*o,m=Math.sqrt(f*f+s*s+h*h),m?(m=1/m,f*=m,s*=m,h*=m):(f=0,s=0,h=0),t[0]=o,t[1]=f,t[2]=M,t[3]=0,t[4]=i,t[5]=s,t[6]=l,t[7]=0,t[8]=c,t[9]=h,t[10]=v,t[11]=0,t[12]=-(o*p+i*d+c*A),t[13]=-(f*p+s*d+h*A),t[14]=-(M*p+l*d+v*A),t[15]=1,t)},e.str=function(t){return"mat4("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+", "+t[4]+", "+t[5]+", "+t[6]+", "+t[7]+", "+t[8]+", "+t[9]+", "+t[10]+", "+t[11]+", "+t[12]+", "+t[13]+", "+t[14]+", "+t[15]+")"},e.frob=function(t){return Math.sqrt(Math.pow(t[0],2)+Math.pow(t[1],2)+Math.pow(t[2],2)+Math.pow(t[3],2)+Math.pow(t[4],2)+Math.pow(t[5],2)+Math.pow(t[6],2)+Math.pow(t[7],2)+Math.pow(t[8],2)+Math.pow(t[9],2)+Math.pow(t[10],2)+Math.pow(t[11],2)+Math.pow(t[12],2)+Math.pow(t[13],2)+Math.pow(t[14],2)+Math.pow(t[15],2))},t.exports=e},function(t,n,r){var a=r(1),e=r(4),u=r(7),o=r(8),i={};i.create=function(){var t=new a.ARRAY_TYPE(4);return t[0]=0,t[1]=0,t[2]=0,t[3]=1,t},i.rotationTo=function(){var t=u.create(),n=u.fromValues(1,0,0),r=u.fromValues(0,1,0);return function(a,e,o){var c=u.dot(e,o);return-.999999>c?(u.cross(t,n,e),u.length(t)<1e-6&&u.cross(t,r,e),u.normalize(t,t),i.setAxisAngle(a,t,Math.PI),a):c>.999999?(a[0]=0,a[1]=0,a[2]=0,a[3]=1,a):(u.cross(t,e,o),a[0]=t[0],a[1]=t[1],a[2]=t[2],a[3]=1+c,i.normalize(a,a))}}(),i.setAxes=function(){var t=e.create();return function(n,r,a,e){return t[0]=a[0],t[3]=a[1],t[6]=a[2],t[1]=e[0],t[4]=e[1],t[7]=e[2],t[2]=-r[0],t[5]=-r[1],t[8]=-r[2],i.normalize(n,i.fromMat3(n,t))}}(),i.clone=o.clone,i.fromValues=o.fromValues,i.copy=o.copy,i.set=o.set,i.identity=function(t){return t[0]=0,t[1]=0,t[2]=0,t[3]=1,t},i.setAxisAngle=function(t,n,r){r=.5*r;var a=Math.sin(r);return t[0]=a*n[0],t[1]=a*n[1],t[2]=a*n[2],t[3]=Math.cos(r),t},i.add=o.add,i.multiply=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=r[0],c=r[1],f=r[2],s=r[3];return t[0]=a*s+o*i+e*f-u*c,t[1]=e*s+o*c+u*i-a*f,t[2]=u*s+o*f+a*c-e*i,t[3]=o*s-a*i-e*c-u*f,t},i.mul=i.multiply,i.scale=o.scale,i.rotateX=function(t,n,r){r*=.5;var a=n[0],e=n[1],u=n[2],o=n[3],i=Math.sin(r),c=Math.cos(r);return t[0]=a*c+o*i,t[1]=e*c+u*i,t[2]=u*c-e*i,t[3]=o*c-a*i,t},i.rotateY=function(t,n,r){r*=.5;var a=n[0],e=n[1],u=n[2],o=n[3],i=Math.sin(r),c=Math.cos(r);return t[0]=a*c-u*i,t[1]=e*c+o*i,t[2]=u*c+a*i,t[3]=o*c-e*i,t},i.rotateZ=function(t,n,r){r*=.5;var a=n[0],e=n[1],u=n[2],o=n[3],i=Math.sin(r),c=Math.cos(r);return t[0]=a*c+e*i,t[1]=e*c-a*i,t[2]=u*c+o*i,t[3]=o*c-u*i,t},i.calculateW=function(t,n){var r=n[0],a=n[1],e=n[2];return t[0]=r,t[1]=a,t[2]=e,t[3]=Math.sqrt(Math.abs(1-r*r-a*a-e*e)),t},i.dot=o.dot,i.lerp=o.lerp,i.slerp=function(t,n,r,a){var e,u,o,i,c,f=n[0],s=n[1],h=n[2],M=n[3],l=r[0],v=r[1],m=r[2],p=r[3];return u=f*l+s*v+h*m+M*p,0>u&&(u=-u,l=-l,v=-v,m=-m,p=-p),1-u>1e-6?(e=Math.acos(u),o=Math.sin(e),i=Math.sin((1-a)*e)/o,c=Math.sin(a*e)/o):(i=1-a,c=a),t[0]=i*f+c*l,t[1]=i*s+c*v,t[2]=i*h+c*m,t[3]=i*M+c*p,t},i.sqlerp=function(){var t=i.create(),n=i.create();return function(r,a,e,u,o,c){return i.slerp(t,a,o,c),i.slerp(n,e,u,c),i.slerp(r,t,n,2*c*(1-c)),r}}(),i.invert=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=r*r+a*a+e*e+u*u,i=o?1/o:0;return t[0]=-r*i,t[1]=-a*i,t[2]=-e*i,t[3]=u*i,t},i.conjugate=function(t,n){return t[0]=-n[0],t[1]=-n[1],t[2]=-n[2],t[3]=n[3],t},i.length=o.length,i.len=i.length,i.squaredLength=o.squaredLength,i.sqrLen=i.squaredLength,i.normalize=o.normalize,i.fromMat3=function(t,n){var r,a=n[0]+n[4]+n[8];if(a>0)r=Math.sqrt(a+1),t[3]=.5*r,r=.5/r,t[0]=(n[5]-n[7])*r,t[1]=(n[6]-n[2])*r,t[2]=(n[1]-n[3])*r;else{var e=0;n[4]>n[0]&&(e=1),n[8]>n[3*e+e]&&(e=2);var u=(e+1)%3,o=(e+2)%3;r=Math.sqrt(n[3*e+e]-n[3*u+u]-n[3*o+o]+1),t[e]=.5*r,r=.5/r,t[3]=(n[3*u+o]-n[3*o+u])*r,t[u]=(n[3*u+e]+n[3*e+u])*r,t[o]=(n[3*o+e]+n[3*e+o])*r}return t},i.str=function(t){return"quat("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+")"},t.exports=i},function(t,n,r){var a=r(1),e={};e.create=function(){var t=new a.ARRAY_TYPE(3);return t[0]=0,t[1]=0,t[2]=0,t},e.clone=function(t){var n=new a.ARRAY_TYPE(3);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n},e.fromValues=function(t,n,r){var e=new a.ARRAY_TYPE(3);return e[0]=t,e[1]=n,e[2]=r,e},e.copy=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t},e.set=function(t,n,r,a){return t[0]=n,t[1]=r,t[2]=a,t},e.add=function(t,n,r){return t[0]=n[0]+r[0],t[1]=n[1]+r[1],t[2]=n[2]+r[2],t},e.subtract=function(t,n,r){return t[0]=n[0]-r[0],t[1]=n[1]-r[1],t[2]=n[2]-r[2],t},e.sub=e.subtract,e.multiply=function(t,n,r){return t[0]=n[0]*r[0],t[1]=n[1]*r[1],t[2]=n[2]*r[2],t},e.mul=e.multiply,e.divide=function(t,n,r){return t[0]=n[0]/r[0],t[1]=n[1]/r[1],t[2]=n[2]/r[2],t},e.div=e.divide,e.min=function(t,n,r){return t[0]=Math.min(n[0],r[0]),t[1]=Math.min(n[1],r[1]),t[2]=Math.min(n[2],r[2]),t},e.max=function(t,n,r){return t[0]=Math.max(n[0],r[0]),t[1]=Math.max(n[1],r[1]),t[2]=Math.max(n[2],r[2]),t},e.scale=function(t,n,r){return t[0]=n[0]*r,t[1]=n[1]*r,t[2]=n[2]*r,t},e.scaleAndAdd=function(t,n,r,a){return t[0]=n[0]+r[0]*a,t[1]=n[1]+r[1]*a,t[2]=n[2]+r[2]*a,t},e.distance=function(t,n){var r=n[0]-t[0],a=n[1]-t[1],e=n[2]-t[2];return Math.sqrt(r*r+a*a+e*e)},e.dist=e.distance,e.squaredDistance=function(t,n){var r=n[0]-t[0],a=n[1]-t[1],e=n[2]-t[2];return r*r+a*a+e*e},e.sqrDist=e.squaredDistance,e.length=function(t){var n=t[0],r=t[1],a=t[2];return Math.sqrt(n*n+r*r+a*a)},e.len=e.length,e.squaredLength=function(t){var n=t[0],r=t[1],a=t[2];return n*n+r*r+a*a},e.sqrLen=e.squaredLength,e.negate=function(t,n){return t[0]=-n[0],t[1]=-n[1],t[2]=-n[2],t},e.inverse=function(t,n){return t[0]=1/n[0],t[1]=1/n[1],t[2]=1/n[2],t},e.normalize=function(t,n){var r=n[0],a=n[1],e=n[2],u=r*r+a*a+e*e;return u>0&&(u=1/Math.sqrt(u),t[0]=n[0]*u,t[1]=n[1]*u,t[2]=n[2]*u),t},e.dot=function(t,n){return t[0]*n[0]+t[1]*n[1]+t[2]*n[2]},e.cross=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=r[0],i=r[1],c=r[2];return t[0]=e*c-u*i,t[1]=u*o-a*c,t[2]=a*i-e*o,t},e.lerp=function(t,n,r,a){var e=n[0],u=n[1],o=n[2];return t[0]=e+a*(r[0]-e),t[1]=u+a*(r[1]-u),t[2]=o+a*(r[2]-o),t},e.hermite=function(t,n,r,a,e,u){var o=u*u,i=o*(2*u-3)+1,c=o*(u-2)+u,f=o*(u-1),s=o*(3-2*u);return t[0]=n[0]*i+r[0]*c+a[0]*f+e[0]*s,t[1]=n[1]*i+r[1]*c+a[1]*f+e[1]*s,t[2]=n[2]*i+r[2]*c+a[2]*f+e[2]*s,t},e.bezier=function(t,n,r,a,e,u){var o=1-u,i=o*o,c=u*u,f=i*o,s=3*u*i,h=3*c*o,M=c*u;return t[0]=n[0]*f+r[0]*s+a[0]*h+e[0]*M,t[1]=n[1]*f+r[1]*s+a[1]*h+e[1]*M,t[2]=n[2]*f+r[2]*s+a[2]*h+e[2]*M,t},e.random=function(t,n){n=n||1;var r=2*a.RANDOM()*Math.PI,e=2*a.RANDOM()-1,u=Math.sqrt(1-e*e)*n;return t[0]=Math.cos(r)*u,t[1]=Math.sin(r)*u,t[2]=e*n,t},e.transformMat4=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=r[3]*a+r[7]*e+r[11]*u+r[15];return o=o||1,t[0]=(r[0]*a+r[4]*e+r[8]*u+r[12])/o,t[1]=(r[1]*a+r[5]*e+r[9]*u+r[13])/o,t[2]=(r[2]*a+r[6]*e+r[10]*u+r[14])/o,t},e.transformMat3=function(t,n,r){var a=n[0],e=n[1],u=n[2];return t[0]=a*r[0]+e*r[3]+u*r[6],t[1]=a*r[1]+e*r[4]+u*r[7],t[2]=a*r[2]+e*r[5]+u*r[8],t},e.transformQuat=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=r[0],i=r[1],c=r[2],f=r[3],s=f*a+i*u-c*e,h=f*e+c*a-o*u,M=f*u+o*e-i*a,l=-o*a-i*e-c*u;return t[0]=s*f+l*-o+h*-c-M*-i,t[1]=h*f+l*-i+M*-o-s*-c,t[2]=M*f+l*-c+s*-i-h*-o,t},e.rotateX=function(t,n,r,a){var e=[],u=[];return e[0]=n[0]-r[0],e[1]=n[1]-r[1],e[2]=n[2]-r[2],u[0]=e[0],u[1]=e[1]*Math.cos(a)-e[2]*Math.sin(a),u[2]=e[1]*Math.sin(a)+e[2]*Math.cos(a),t[0]=u[0]+r[0],t[1]=u[1]+r[1],t[2]=u[2]+r[2],t},e.rotateY=function(t,n,r,a){var e=[],u=[];return e[0]=n[0]-r[0],e[1]=n[1]-r[1],e[2]=n[2]-r[2],u[0]=e[2]*Math.sin(a)+e[0]*Math.cos(a),u[1]=e[1],u[2]=e[2]*Math.cos(a)-e[0]*Math.sin(a),t[0]=u[0]+r[0],t[1]=u[1]+r[1],t[2]=u[2]+r[2],t},e.rotateZ=function(t,n,r,a){var e=[],u=[];return e[0]=n[0]-r[0],e[1]=n[1]-r[1],e[2]=n[2]-r[2],u[0]=e[0]*Math.cos(a)-e[1]*Math.sin(a),u[1]=e[0]*Math.sin(a)+e[1]*Math.cos(a),u[2]=e[2],t[0]=u[0]+r[0],t[1]=u[1]+r[1],t[2]=u[2]+r[2],t},e.forEach=function(){var t=e.create();return function(n,r,a,e,u,o){var i,c;for(r||(r=3),a||(a=0),c=e?Math.min(e*r+a,n.length):n.length,i=a;c>i;i+=r)t[0]=n[i],t[1]=n[i+1],t[2]=n[i+2],u(t,t,o),n[i]=t[0],n[i+1]=t[1],n[i+2]=t[2];return n}}(),e.angle=function(t,n){var r=e.fromValues(t[0],t[1],t[2]),a=e.fromValues(n[0],n[1],n[2]);e.normalize(r,r),e.normalize(a,a);var u=e.dot(r,a);return u>1?0:Math.acos(u)},e.str=function(t){return"vec3("+t[0]+", "+t[1]+", "+t[2]+")"},t.exports=e},function(t,n,r){var a=r(1),e={};e.create=function(){var t=new a.ARRAY_TYPE(4);return t[0]=0,t[1]=0,t[2]=0,t[3]=0,t},e.clone=function(t){var n=new a.ARRAY_TYPE(4);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=t[3],n},e.fromValues=function(t,n,r,e){var u=new a.ARRAY_TYPE(4);return u[0]=t,u[1]=n,u[2]=r,u[3]=e,u},e.copy=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t},e.set=function(t,n,r,a,e){return t[0]=n,t[1]=r,t[2]=a,t[3]=e,t},e.add=function(t,n,r){return t[0]=n[0]+r[0],t[1]=n[1]+r[1],t[2]=n[2]+r[2],t[3]=n[3]+r[3],t},e.subtract=function(t,n,r){return t[0]=n[0]-r[0],t[1]=n[1]-r[1],t[2]=n[2]-r[2],t[3]=n[3]-r[3],t},e.sub=e.subtract,e.multiply=function(t,n,r){return t[0]=n[0]*r[0],t[1]=n[1]*r[1],t[2]=n[2]*r[2],t[3]=n[3]*r[3],t},e.mul=e.multiply,e.divide=function(t,n,r){return t[0]=n[0]/r[0],t[1]=n[1]/r[1],t[2]=n[2]/r[2],t[3]=n[3]/r[3],t},e.div=e.divide,e.min=function(t,n,r){return t[0]=Math.min(n[0],r[0]),t[1]=Math.min(n[1],r[1]),t[2]=Math.min(n[2],r[2]),t[3]=Math.min(n[3],r[3]),t},e.max=function(t,n,r){return t[0]=Math.max(n[0],r[0]),t[1]=Math.max(n[1],r[1]),t[2]=Math.max(n[2],r[2]),t[3]=Math.max(n[3],r[3]),t},e.scale=function(t,n,r){return t[0]=n[0]*r,t[1]=n[1]*r,t[2]=n[2]*r,t[3]=n[3]*r,t},e.scaleAndAdd=function(t,n,r,a){return t[0]=n[0]+r[0]*a,t[1]=n[1]+r[1]*a,t[2]=n[2]+r[2]*a,t[3]=n[3]+r[3]*a,t},e.distance=function(t,n){var r=n[0]-t[0],a=n[1]-t[1],e=n[2]-t[2],u=n[3]-t[3];return Math.sqrt(r*r+a*a+e*e+u*u)},e.dist=e.distance,e.squaredDistance=function(t,n){var r=n[0]-t[0],a=n[1]-t[1],e=n[2]-t[2],u=n[3]-t[3];return r*r+a*a+e*e+u*u},e.sqrDist=e.squaredDistance,e.length=function(t){var n=t[0],r=t[1],a=t[2],e=t[3];return Math.sqrt(n*n+r*r+a*a+e*e)},e.len=e.length,e.squaredLength=function(t){var n=t[0],r=t[1],a=t[2],e=t[3];return n*n+r*r+a*a+e*e},e.sqrLen=e.squaredLength,e.negate=function(t,n){return t[0]=-n[0],t[1]=-n[1],t[2]=-n[2],t[3]=-n[3],t},e.inverse=function(t,n){return t[0]=1/n[0],t[1]=1/n[1],t[2]=1/n[2],t[3]=1/n[3],t},e.normalize=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=r*r+a*a+e*e+u*u;return o>0&&(o=1/Math.sqrt(o),t[0]=r*o,t[1]=a*o,t[2]=e*o,t[3]=u*o),t},e.dot=function(t,n){return t[0]*n[0]+t[1]*n[1]+t[2]*n[2]+t[3]*n[3]},e.lerp=function(t,n,r,a){var e=n[0],u=n[1],o=n[2],i=n[3];return t[0]=e+a*(r[0]-e),t[1]=u+a*(r[1]-u),t[2]=o+a*(r[2]-o),t[3]=i+a*(r[3]-i),t},e.random=function(t,n){return n=n||1,t[0]=a.RANDOM(),t[1]=a.RANDOM(),t[2]=a.RANDOM(),t[3]=a.RANDOM(),e.normalize(t,t),e.scale(t,t,n),t},e.transformMat4=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3];return t[0]=r[0]*a+r[4]*e+r[8]*u+r[12]*o,t[1]=r[1]*a+r[5]*e+r[9]*u+r[13]*o,t[2]=r[2]*a+r[6]*e+r[10]*u+r[14]*o,t[3]=r[3]*a+r[7]*e+r[11]*u+r[15]*o,t},e.transformQuat=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=r[0],i=r[1],c=r[2],f=r[3],s=f*a+i*u-c*e,h=f*e+c*a-o*u,M=f*u+o*e-i*a,l=-o*a-i*e-c*u;return t[0]=s*f+l*-o+h*-c-M*-i,t[1]=h*f+l*-i+M*-o-s*-c,t[2]=M*f+l*-c+s*-i-h*-o,t[3]=n[3],t},e.forEach=function(){var t=e.create();return function(n,r,a,e,u,o){var i,c;for(r||(r=4),a||(a=0),c=e?Math.min(e*r+a,n.length):n.length,i=a;c>i;i+=r)t[0]=n[i],t[1]=n[i+1],t[2]=n[i+2],t[3]=n[i+3],u(t,t,o),n[i]=t[0],n[i+1]=t[1],n[i+2]=t[2],n[i+3]=t[3];return n}}(),e.str=function(t){return"vec4("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+")"},t.exports=e},function(t,n,r){var a=r(1),e={};e.create=function(){var t=new a.ARRAY_TYPE(2);return t[0]=0,t[1]=0,t},e.clone=function(t){var n=new a.ARRAY_TYPE(2);return n[0]=t[0],n[1]=t[1],n},e.fromValues=function(t,n){var r=new a.ARRAY_TYPE(2);return r[0]=t,r[1]=n,r},e.copy=function(t,n){return t[0]=n[0],t[1]=n[1],t},e.set=function(t,n,r){return t[0]=n,t[1]=r,t},e.add=function(t,n,r){return t[0]=n[0]+r[0],t[1]=n[1]+r[1],t},e.subtract=function(t,n,r){return t[0]=n[0]-r[0],t[1]=n[1]-r[1],t},e.sub=e.subtract,e.multiply=function(t,n,r){return t[0]=n[0]*r[0],t[1]=n[1]*r[1],t},e.mul=e.multiply,e.divide=function(t,n,r){return t[0]=n[0]/r[0],t[1]=n[1]/r[1],t},e.div=e.divide,e.min=function(t,n,r){return t[0]=Math.min(n[0],r[0]),t[1]=Math.min(n[1],r[1]),t},e.max=function(t,n,r){return t[0]=Math.max(n[0],r[0]),t[1]=Math.max(n[1],r[1]),t},e.scale=function(t,n,r){return t[0]=n[0]*r,t[1]=n[1]*r,t},e.scaleAndAdd=function(t,n,r,a){return t[0]=n[0]+r[0]*a,t[1]=n[1]+r[1]*a,t},e.distance=function(t,n){var r=n[0]-t[0],a=n[1]-t[1];return Math.sqrt(r*r+a*a)},e.dist=e.distance,e.squaredDistance=function(t,n){var r=n[0]-t[0],a=n[1]-t[1];return r*r+a*a},e.sqrDist=e.squaredDistance,e.length=function(t){var n=t[0],r=t[1];return Math.sqrt(n*n+r*r)},e.len=e.length,e.squaredLength=function(t){var n=t[0],r=t[1];return n*n+r*r},e.sqrLen=e.squaredLength,e.negate=function(t,n){return t[0]=-n[0],t[1]=-n[1],t},e.inverse=function(t,n){return t[0]=1/n[0],t[1]=1/n[1],t},e.normalize=function(t,n){var r=n[0],a=n[1],e=r*r+a*a;return e>0&&(e=1/Math.sqrt(e),t[0]=n[0]*e,t[1]=n[1]*e),t},e.dot=function(t,n){return t[0]*n[0]+t[1]*n[1]},e.cross=function(t,n,r){var a=n[0]*r[1]-n[1]*r[0];return t[0]=t[1]=0,t[2]=a,t},e.lerp=function(t,n,r,a){var e=n[0],u=n[1];return t[0]=e+a*(r[0]-e),t[1]=u+a*(r[1]-u),t},e.random=function(t,n){n=n||1;var r=2*a.RANDOM()*Math.PI;return t[0]=Math.cos(r)*n,t[1]=Math.sin(r)*n,t},e.transformMat2=function(t,n,r){var a=n[0],e=n[1];return t[0]=r[0]*a+r[2]*e,t[1]=r[1]*a+r[3]*e,t},e.transformMat2d=function(t,n,r){var a=n[0],e=n[1];return t[0]=r[0]*a+r[2]*e+r[4],t[1]=r[1]*a+r[3]*e+r[5],t},e.transformMat3=function(t,n,r){var a=n[0],e=n[1];return t[0]=r[0]*a+r[3]*e+r[6],t[1]=r[1]*a+r[4]*e+r[7],t},e.transformMat4=function(t,n,r){var a=n[0],e=n[1];return t[0]=r[0]*a+r[4]*e+r[12],t[1]=r[1]*a+r[5]*e+r[13],t},e.forEach=function(){var t=e.create();return function(n,r,a,e,u,o){var i,c;for(r||(r=2),a||(a=0),c=e?Math.min(e*r+a,n.length):n.length,i=a;c>i;i+=r)t[0]=n[i],t[1]=n[i+1],u(t,t,o),n[i]=t[0],n[i+1]=t[1];return n}}(),e.str=function(t){return"vec2("+t[0]+", "+t[1]+")"},t.exports=e}])});'use strict';(function(global){if(tr.isNode){const glMatrixAbsPath=HTMLImportsLoader.hrefToAbsolutePath('/gl-matrix-min.js');const glMatrixModule=require(glMatrixAbsPath);for(const exportName in glMatrixModule){global[exportName]=glMatrixModule[exportName];}}})(this);'use strict';tr.exportTo('tr.b.math',function(){const PREFERRED_NUMBER_SERIES_MULTIPLIERS=[1,2,5,10];function approximately(x,y,delta){if(delta===undefined)delta=1e-9;return Math.abs(x-y)<delta;}
+function clamp(x,lo,hi){return Math.min(Math.max(x,lo),hi);}
+function lerp(percentage,lo,hi){const range=hi-lo;return lo+percentage*range;}
+function normalize(value,lo,hi){return(value-lo)/(hi-lo);}
+function deg2rad(deg){return(Math.PI*deg)/180.0;}
+function erf(x){const sign=(x>=0)?1:-1;x=Math.abs(x);const a1=0.254829592;const a2=-0.284496736;const a3=1.421413741;const a4=-1.453152027;const a5=1.061405429;const p=0.3275911;const t=1.0/(1.0+p*x);const y=1.0-(((((a5*t+a4)*t)+a3)*t+a2)*t+a1)*t*Math.exp(-x*x);return sign*y;}
+const tmpVec2=vec2.create();const tmpVec2b=vec2.create();const tmpVec4=vec4.create();const tmpMat2d=mat2d.create();vec2.createFromArray=function(arr){if(arr.length!==2)throw new Error('Should be length 2');const v=vec2.create();vec2.set(v,arr[0],arr[1]);return v;};vec2.createXY=function(x,y){const v=vec2.create();vec2.set(v,x,y);return v;};vec2.toString=function(a){return'['+a[0]+', '+a[1]+']';};vec2.addTwoScaledUnitVectors=function(out,u1,scale1,u2,scale2){vec2.scale(tmpVec2,u1,scale1);vec2.scale(tmpVec2b,u2,scale2);vec2.add(out,tmpVec2,tmpVec2b);};vec2.interpolatePiecewiseFunction=function(points,x){if(x<points[0][0])return points[0][1];for(let i=1;i<points.length;++i){if(x<points[i][0]){const percent=normalize(x,points[i-1][0],points[i][0]);return lerp(percent,points[i-1][1],points[i][1]);}}
+return points[points.length-1][1];};vec3.createXYZ=function(x,y,z){const v=vec3.create();vec3.set(v,x,y,z);return v;};vec3.toString=function(a){return'vec3('+a[0]+', '+a[1]+', '+a[2]+')';};mat2d.translateXY=function(out,x,y){vec2.set(tmpVec2,x,y);mat2d.translate(out,out,tmpVec2);};mat2d.scaleXY=function(out,x,y){vec2.set(tmpVec2,x,y);mat2d.scale(out,out,tmpVec2);};vec4.unitize=function(out,a){out[0]=a[0]/a[3];out[1]=a[1]/a[3];out[2]=a[2]/a[3];out[3]=1;return out;};vec2.copyFromVec4=function(out,a){vec4.unitize(tmpVec4,a);vec2.copy(out,tmpVec4);};function logOrLog10(x,base){if(base===10)return Math.log10(x);return Math.log(x)/Math.log(base);}
+function lesserPower(x,opt_base){const base=opt_base||10;return Math.pow(base,Math.floor(logOrLog10(x,base)));}
+function greaterPower(x,opt_base){const base=opt_base||10;return Math.pow(base,Math.ceil(logOrLog10(x,base)));}
+function lesserWholeNumber(x){if(x===0)return 0;const pow10=(x<0)?-lesserPower(-x):lesserPower(x);return pow10*Math.floor(x/pow10);}
+function greaterWholeNumber(x){if(x===0)return 0;const pow10=(x<0)?-lesserPower(-x):lesserPower(x);return pow10*Math.ceil(x/pow10);}
+function truncate(value,digits){const pow10=Math.pow(10,digits);return Math.round(value*pow10)/pow10;}
+function preferredNumberLargerThanMin(min){const absMin=Math.abs(min);const conservativeGuess=tr.b.math.lesserPower(absMin);let minPreferedNumber=undefined;for(const multiplier of PREFERRED_NUMBER_SERIES_MULTIPLIERS){const tightenedGuess=conservativeGuess*multiplier;if(tightenedGuess>=absMin){minPreferedNumber=tightenedGuess;break;}}
+if(minPreferedNumber===undefined){throw new Error('Could not compute preferred number for '+min);}
+if(min<0)minPreferedNumber*=-1;return minPreferedNumber;}
+return{approximately,clamp,lerp,normalize,deg2rad,erf,lesserPower,greaterPower,lesserWholeNumber,greaterWholeNumber,preferredNumberLargerThanMin,truncate,};});'use strict';tr.exportTo('tr.b.math',function(){function Range(){this.isEmpty_=true;this.min_=undefined;this.max_=undefined;}
+Range.prototype={__proto__:Object.prototype,clone(){if(this.isEmpty)return new Range();return Range.fromExplicitRange(this.min_,this.max_);},reset(){this.isEmpty_=true;this.min_=undefined;this.max_=undefined;},get isEmpty(){return this.isEmpty_;},addRange(range){if(range.isEmpty)return;this.addValue(range.min);this.addValue(range.max);},addValue(value){if(this.isEmpty_){this.max_=value;this.min_=value;this.isEmpty_=false;return;}
+this.max_=Math.max(this.max_,value);this.min_=Math.min(this.min_,value);},set min(min){this.isEmpty_=false;this.min_=min;},get min(){if(this.isEmpty_)return undefined;return this.min_;},get max(){if(this.isEmpty_)return undefined;return this.max_;},set max(max){this.isEmpty_=false;this.max_=max;},get range(){if(this.isEmpty_)return undefined;return this.max_-this.min_;},get center(){return(this.min_+this.max_)*0.5;},get duration(){if(this.isEmpty_)return 0;return this.max_-this.min_;},enclosingPowers(opt_base){if(this.isEmpty)return new Range();return Range.fromExplicitRange(tr.b.math.lesserPower(this.min_,opt_base),tr.b.math.greaterPower(this.max_,opt_base));},normalize(x){return tr.b.math.normalize(x,this.min,this.max);},lerp(x){return tr.b.math.lerp(x,this.min,this.max);},clamp(x){return tr.b.math.clamp(x,this.min,this.max);},equals(that){if(this.isEmpty&&that.isEmpty)return true;if(this.isEmpty!==that.isEmpty)return false;return(tr.b.math.approximately(this.min,that.min)&&tr.b.math.approximately(this.max,that.max));},containsExplicitRangeInclusive(min,max){if(this.isEmpty)return false;return this.min_<=min&&max<=this.max_;},containsExplicitRangeExclusive(min,max){if(this.isEmpty)return false;return this.min_<min&&max<this.max_;},intersectsExplicitRangeInclusive(min,max){if(this.isEmpty)return false;return this.min_<=max&&min<=this.max_;},intersectsExplicitRangeExclusive(min,max){if(this.isEmpty)return false;return this.min_<max&&min<this.max_;},containsRangeInclusive(range){if(range.isEmpty)return false;return this.containsExplicitRangeInclusive(range.min_,range.max_);},containsRangeExclusive(range){if(range.isEmpty)return false;return this.containsExplicitRangeExclusive(range.min_,range.max_);},intersectsRangeInclusive(range){if(range.isEmpty)return false;return this.intersectsExplicitRangeInclusive(range.min_,range.max_);},intersectsRangeExclusive(range){if(range.isEmpty)return false;return this.intersectsExplicitRangeExclusive(range.min_,range.max_);},findExplicitIntersectionDuration(min,max){min=Math.max(this.min,min);max=Math.min(this.max,max);if(max<min)return 0;return max-min;},findIntersection(range){if(this.isEmpty||range.isEmpty)return new Range();const min=Math.max(this.min,range.min);const max=Math.min(this.max,range.max);if(max<min)return new Range();return Range.fromExplicitRange(min,max);},toJSON(){if(this.isEmpty_)return{isEmpty:true};return{isEmpty:false,max:this.max,min:this.min};},filterArray(sortedArray,opt_keyFunc,opt_this){if(this.isEmpty_)return[];const keyFunc=opt_keyFunc||(x=>x);function getValue(obj){return keyFunc.call(opt_this,obj);}
+const first=tr.b.findFirstTrueIndexInSortedArray(sortedArray,obj=>this.min_===undefined||this.min_<=getValue(obj));const last=tr.b.findFirstTrueIndexInSortedArray(sortedArray,obj=>this.max_!==undefined&&this.max_<getValue(obj));return sortedArray.slice(first,last);}};Range.fromDict=function(d){if(d.isEmpty===true)return new Range();if(d.isEmpty===false){const range=new Range();range.min=d.min;range.max=d.max;return range;}
+throw new Error('Not a range');};Range.fromExplicitRange=function(min,max){const range=new Range();range.min=min;range.max=max;return range;};Range.compareByMinTimes=function(a,b){if(!a.isEmpty&&!b.isEmpty)return a.min_-b.min_;if(a.isEmpty&&!b.isEmpty)return-1;if(!a.isEmpty&&b.isEmpty)return 1;return 0;};Range.findDifference=function(rangeA,rangeB){if(!rangeA||rangeA.duration<0||!rangeB||rangeB.duration<0){throw new Error(`Couldn't subtract ranges`);}
+const resultRanges=[];if(rangeA.isEmpty)return resultRanges;if(rangeB.isEmpty)return[rangeA.clone()];const intersection=rangeA.findIntersection(rangeB);if(intersection.isEmpty){return[rangeA.clone()];}
+if(rangeA.duration===0&&rangeB.duration===0){if(intersection.empty)return[rangeA.clone()];else if(intersection.duration===0)return resultRanges;throw new Error(`Two points' intersection can only be a point or empty`);}
+const leftRange=tr.b.math.Range.fromExplicitRange(rangeA.min,intersection.min);if(leftRange.duration>0){resultRanges.push(leftRange);}
+const rightRange=tr.b.math.Range.fromExplicitRange(intersection.max,rangeA.max);if(rightRange.duration>0){resultRanges.push(rightRange);}
+return resultRanges;};Range.PERCENT_RANGE=Range.fromExplicitRange(0,1);Object.freeze(Range.PERCENT_RANGE);return{Range,};});'use strict';(function(exports){var rank={standard:function(array,key){array=array.sort(function(a,b){var x=a[key];var y=b[key];return((x<y)?-1:((x>y)?1:0));});for(var i=1;i<array.length+1;i++){array[i-1]['rank']=i;}
+return array;},fractional:function(array,key){array=this.standard(array,key);var pos=0;while(pos<array.length){var sum=0;var i=0;for(i=0;array[pos+i+1]&&(array[pos+i][key]===array[pos+i+1][key]);i++){sum+=array[pos+i]['rank'];}
+sum+=array[pos+i]['rank'];var endPos=pos+i+1;for(pos;pos<endPos;pos++){array[pos]['rank']=sum/(i+1);}
+pos=endPos;}
+return array;},rank:function(x,y){var nx=x.length,ny=y.length,combined=[],ranked;while(nx--){combined.push({set:'x',val:x[nx]});}
+while(ny--){combined.push({set:'y',val:y[ny]});}
+ranked=this.fractional(combined,'val');return ranked}};var erf=function erf(x){var cof=[-1.3026537197817094,6.4196979235649026e-1,1.9476473204185836e-2,-9.561514786808631e-3,-9.46595344482036e-4,3.66839497852761e-4,4.2523324806907e-5,-2.0278578112534e-5,-1.624290004647e-6,1.303655835580e-6,1.5626441722e-8,-8.5238095915e-8,6.529054439e-9,5.059343495e-9,-9.91364156e-10,-2.27365122e-10,9.6467911e-11,2.394038e-12,-6.886027e-12,8.94487e-13,3.13092e-13,-1.12708e-13,3.81e-16,7.106e-15,-1.523e-15,-9.4e-17,1.21e-16,-2.8e-17];var j=cof.length-1;var isneg=false;var d=0;var dd=0;var t,ty,tmp,res;if(x<0){x=-x;isneg=true;}
+t=2/(2+x);ty=4*t-2;for(;j>0;j--){tmp=d;d=ty*d-dd+cof[j];dd=tmp;}
+res=t*Math.exp(-x*x+0.5*(cof[0]+ty*d)-dd);return isneg?res-1:1-res;};var dnorm=function(x,mean,std){return 0.5*(1+erf((x-mean)/Math.sqrt(2*std*std)));}
+var statistic=function(x,y){var ranked=rank.rank(x,y),nr=ranked.length,nx=x.length,ny=y.length,ranksums={x:0,y:0},i=0,t=0,nt=1,tcf,ux,uy;while(i<nr){if(i>0){if(ranked[i].val==ranked[i-1].val){nt++;}else{if(nt>1){t+=Math.pow(nt,3)-nt
+nt=1;}}}
+ranksums[ranked[i].set]+=ranked[i].rank
+i++;}
+tcf=1-(t/(Math.pow(nr,3)-nr))
+ux=nx*ny+(nx*(nx+1)/2)-ranksums.x;uy=nx*ny-ux;return{tcf:tcf,ux:ux,uy:uy,big:Math.max(ux,uy),small:Math.min(ux,uy)}}
+exports.test=function(x,y,alt,corr){alt=typeof alt!=='undefined'?alt:'two-sided';corr=typeof corr!=='undefined'?corr:true;var nx=x.length,ny=y.length,f=1,u,mu,std,z,p;u=statistic(x,y);if(corr){mu=(nx*ny/2)+0.5;}else{mu=nx*ny/2;}
+std=Math.sqrt(u.tcf*nx*ny*(nx+ny+1)/12);if(alt=='less'){z=(u.ux-mu)/std;}else if(alt=='greater'){z=(u.uy-mu)/std;}else if(alt=='two-sided'){z=Math.abs((u.big-mu)/std);}else{console.log('Unknown alternative argument');}
+if(alt=='two-sided'){f=2;}
+p=dnorm(-z,0,1)*f;return{U:u.small,p:p};}})(typeof exports==='undefined'?this['mannwhitneyu']={}:exports);'use strict';(function(global){if(tr.isNode){const mwuAbsPath=HTMLImportsLoader.hrefToAbsolutePath('/mannwhitneyu.js');const mwuModule=require(mwuAbsPath);for(const exportName in mwuModule){global[exportName]=mwuModule[exportName];}}})(this);'use strict';tr.exportTo('tr.b.math',function(){const Statistics={};Statistics.divideIfPossibleOrZero=function(numerator,denominator){if(denominator===0)return 0;return numerator/denominator;};Statistics.sum=function(ary,opt_func,opt_this){const func=opt_func||(x=>x);let ret=0;let i=0;for(const elt of ary){ret+=func.call(opt_this,elt,i++);}
+return ret;};Statistics.mean=function(ary,opt_func,opt_this){const func=opt_func||(x=>x);let sum=0;let i=0;for(const elt of ary){sum+=func.call(opt_this,elt,i++);}
+if(i===0)return undefined;return sum/i;};Statistics.geometricMean=function(ary,opt_func,opt_this){const func=opt_func||(x=>x);let i=0;let logsum=0;for(const elt of ary){const x=func.call(opt_this,elt,i++);if(x<=0)return 0;logsum+=Math.log(Math.abs(x));}
+if(i===0)return 1;return Math.exp(logsum/i);};Statistics.weightedMean=function(ary,weightCallback,opt_valueCallback,opt_this){const valueCallback=opt_valueCallback||(x=>x);let numerator=0;let denominator=0;let i=-1;for(const elt of ary){i++;const value=valueCallback.call(opt_this,elt,i);if(value===undefined)continue;const weight=weightCallback.call(opt_this,elt,i,value);numerator+=weight*value;denominator+=weight;}
+if(denominator===0)return undefined;return numerator/denominator;};Statistics.variance=function(ary,opt_func,opt_this){if(ary.length===0)return undefined;if(ary.length===1)return 0;const func=opt_func||(x=>x);const mean=Statistics.mean(ary,func,opt_this);const sumOfSquaredDistances=Statistics.sum(ary,function(d,i){const v=func.call(this,d,i)-mean;return v*v;},opt_this);return sumOfSquaredDistances/(ary.length-1);};Statistics.stddev=function(ary,opt_func,opt_this){if(ary.length===0)return undefined;return Math.sqrt(Statistics.variance(ary,opt_func,opt_this));};Statistics.max=function(ary,opt_func,opt_this){const func=opt_func||(x=>x);let ret=-Infinity;let i=0;for(const elt of ary){ret=Math.max(ret,func.call(opt_this,elt,i++));}
+return ret;};Statistics.min=function(ary,opt_func,opt_this){const func=opt_func||(x=>x);let ret=Infinity;let i=0;for(const elt of ary){ret=Math.min(ret,func.call(opt_this,elt,i++));}
+return ret;};Statistics.range=function(ary,opt_func,opt_this){const func=opt_func||(x=>x);const ret=new tr.b.math.Range();let i=0;for(const elt of ary){ret.addValue(func.call(opt_this,elt,i++));}
+return ret;};Statistics.percentile=function(ary,percent,opt_func,opt_this){if(!(percent>=0&&percent<=1)){throw new Error('percent must be [0,1]');}
+const func=opt_func||(x=>x);const tmp=new Array(ary.length);let i=0;for(const elt of ary){tmp[i]=func.call(opt_this,elt,i++);}
+tmp.sort((a,b)=>a-b);const idx=Math.floor((ary.length-1)*percent);return tmp[idx];};Statistics.normalizeSamples=function(samples){if(samples.length===0){return{normalized_samples:samples,scale:1.0};}
+samples=samples.slice().sort(function(a,b){return a-b;});const low=Math.min.apply(null,samples);const high=Math.max.apply(null,samples);const newLow=0.5/samples.length;const newHigh=(samples.length-0.5)/samples.length;if(high-low===0.0){samples=Array.apply(null,new Array(samples.length)).map(function(){return 0.5;});return{normalized_samples:samples,scale:1.0};}
+const scale=(newHigh-newLow)/(high-low);for(let i=0;i<samples.length;i++){samples[i]=(samples[i]-low)*scale+newLow;}
+return{normalized_samples:samples,scale};};Statistics.discrepancy=function(samples,opt_locationCount){if(samples.length===0)return 0.0;let maxLocalDiscrepancy=0;const invSampleCount=1.0/samples.length;const locations=[];const countLess=[];const countLessEqual=[];if(opt_locationCount!==undefined){let sampleIndex=0;for(let i=0;i<opt_locationCount;i++){const location=i/(opt_locationCount-1);locations.push(location);while(sampleIndex<samples.length&&samples[sampleIndex]<location){sampleIndex+=1;}
+countLess.push(sampleIndex);while(sampleIndex<samples.length&&samples[sampleIndex]<=location){sampleIndex+=1;}
+countLessEqual.push(sampleIndex);}}else{if(samples[0]>0.0){locations.push(0.0);countLess.push(0);countLessEqual.push(0);}
+for(let i=0;i<samples.length;i++){locations.push(samples[i]);countLess.push(i);countLessEqual.push(i+1);}
+if(samples[-1]<1.0){locations.push(1.0);countLess.push(samples.length);countLessEqual.push(samples.length);}}
+let maxDiff=0;let minDiff=0;for(let i=1;i<locations.length;i++){const length=locations[i]-locations[i-1];const countClosed=countLessEqual[i]-countLess[i-1];const countOpen=countLess[i]-countLessEqual[i-1];const countClosedIncrement=countLessEqual[i]-countLessEqual[i-1];const countOpenIncrement=countLess[i]-countLess[i-1];maxDiff=Math.max(countClosedIncrement*invSampleCount-length+maxDiff,countClosed*invSampleCount-length);minDiff=Math.min(countOpenIncrement*invSampleCount-length+minDiff,countOpen*invSampleCount-length);maxLocalDiscrepancy=Math.max(maxDiff,-minDiff,maxLocalDiscrepancy);}
+return maxLocalDiscrepancy;};Statistics.timestampsDiscrepancy=function(timestamps,opt_absolute,opt_locationCount){if(timestamps.length===0)return 0.0;if(opt_absolute===undefined)opt_absolute=true;if(Array.isArray(timestamps[0])){const rangeDiscrepancies=timestamps.map(function(r){return Statistics.timestampsDiscrepancy(r);});return Math.max.apply(null,rangeDiscrepancies);}
+const s=Statistics.normalizeSamples(timestamps);const samples=s.normalized_samples;const sampleScale=s.scale;let discrepancy=Statistics.discrepancy(samples,opt_locationCount);const invSampleCount=1.0/samples.length;if(opt_absolute===true){discrepancy/=sampleScale;}else{discrepancy=tr.b.math.clamp((discrepancy-invSampleCount)/(1.0-invSampleCount),0.0,1.0);}
+return discrepancy;};Statistics.uniformlySampleArray=function(samples,count){if(samples.length<=count){return samples;}
+while(samples.length>count){const i=parseInt(Math.random()*samples.length);samples.splice(i,1);}
+return samples;};Statistics.uniformlySampleStream=function(samples,streamLength,newElement,numSamples){if(streamLength<=numSamples){if(samples.length>=streamLength){samples[streamLength-1]=newElement;}else{samples.push(newElement);}
+return;}
+const probToKeep=numSamples/streamLength;if(Math.random()>probToKeep)return;const index=Math.floor(Math.random()*numSamples);samples[index]=newElement;};Statistics.mergeSampledStreams=function(samplesA,streamLengthA,samplesB,streamLengthB,numSamples){if(streamLengthB<numSamples){const nbElements=Math.min(streamLengthB,samplesB.length);for(let i=0;i<nbElements;++i){Statistics.uniformlySampleStream(samplesA,streamLengthA+i+1,samplesB[i],numSamples);}
+return;}
+if(streamLengthA<numSamples){const nbElements=Math.min(streamLengthA,samplesA.length);const tempSamples=samplesB.slice();for(let i=0;i<nbElements;++i){Statistics.uniformlySampleStream(tempSamples,streamLengthB+i+1,samplesA[i],numSamples);}
+for(let i=0;i<tempSamples.length;++i){samplesA[i]=tempSamples[i];}
+return;}
+const nbElements=Math.min(numSamples,samplesB.length);const probOfSwapping=streamLengthB/(streamLengthA+streamLengthB);for(let i=0;i<nbElements;++i){if(Math.random()<probOfSwapping){samplesA[i]=samplesB[i];}}};function Distribution(){}
+Distribution.prototype={computeDensity(x){throw Error('Not implemented');},computePercentile(x){throw Error('Not implemented');},computeComplementaryPercentile(x){return 1-this.computePercentile(x);},get mean(){throw Error('Not implemented');},get mode(){throw Error('Not implemented');},get median(){throw Error('Not implemented');},get standardDeviation(){throw Error('Not implemented');},get variance(){throw Error('Not implemented');}};Statistics.UniformDistribution=function(opt_range){if(!opt_range)opt_range=tr.b.math.Range.fromExplicitRange(0,1);this.range=opt_range;};Statistics.UniformDistribution.prototype={__proto__:Distribution.prototype,computeDensity(x){return 1/this.range.range;},computePercentile(x){return tr.b.math.normalize(x,this.range.min,this.range.max);},get mean(){return this.range.center;},get mode(){return undefined;},get median(){return this.mean;},get standardDeviation(){return Math.sqrt(this.variance);},get variance(){return Math.pow(this.range.range,2)/12;}};Statistics.NormalDistribution=function(opt_mean,opt_variance){this.mean_=opt_mean||0;this.variance_=opt_variance||1;this.standardDeviation_=Math.sqrt(this.variance_);};Statistics.NormalDistribution.prototype={__proto__:Distribution.prototype,computeDensity(x){const scale=(1.0/(this.standardDeviation*Math.sqrt(2.0*Math.PI)));const exponent=-Math.pow(x-this.mean,2)/(2.0*this.variance);return scale*Math.exp(exponent);},computePercentile(x){const standardizedX=((x-this.mean)/Math.sqrt(2.0*this.variance));return(1.0+tr.b.math.erf(standardizedX))/2.0;},get mean(){return this.mean_;},get median(){return this.mean;},get mode(){return this.mean;},get standardDeviation(){return this.standardDeviation_;},get variance(){return this.variance_;}};Statistics.LogNormalDistribution=function(opt_location,opt_shape){this.normalDistribution_=new Statistics.NormalDistribution(opt_location,Math.pow(opt_shape||1,2));};Statistics.LogNormalDistribution.prototype={__proto__:Statistics.NormalDistribution.prototype,computeDensity(x){return this.normalDistribution_.computeDensity(Math.log(x))/x;},computePercentile(x){return this.normalDistribution_.computePercentile(Math.log(x));},get mean(){return Math.exp(this.normalDistribution_.mean+
+(this.normalDistribution_.variance/2));},get variance(){const nm=this.normalDistribution_.mean;const nv=this.normalDistribution_.variance;return(Math.exp(2*(nm+nv))-
+Math.exp(2*nm+nv));},get standardDeviation(){return Math.sqrt(this.variance);},get median(){return Math.exp(this.normalDistribution_.mean);},get mode(){return Math.exp(this.normalDistribution_.mean-
+this.normalDistribution_.variance);}};Statistics.LogNormalDistribution.fromMedianAndDiminishingReturns=function(median,diminishingReturns){diminishingReturns=Math.log(diminishingReturns/median);const shape=Math.sqrt(1-3*diminishingReturns-
+Math.sqrt(Math.pow(diminishingReturns-3,2)-8))/2;const location=Math.log(median);return new Statistics.LogNormalDistribution(location,shape);};Statistics.DEFAULT_ALPHA=0.01;Statistics.MAX_SUGGESTED_SAMPLE_SIZE=20;Statistics.Significance={SIGNIFICANT:'REJECT',INSIGNIFICANT:'FAIL_TO_REJECT',NEED_MORE_DATA:'NEED_MORE_DATA',DONT_CARE:'DONT_CARE',};class HypothesisTestResult{constructor(p,u,needMoreData,opt_alpha){this.p_=p;this.u_=u;this.needMoreData_=needMoreData;this.compare(opt_alpha);}
+get p(){return this.p_;}
+get U(){return this.u_;}
+get significance(){return this.significance_;}
+compare(opt_alpha){const alpha=opt_alpha||Statistics.DEFAULT_ALPHA;if(this.p<alpha){this.significance_=Statistics.Significance.SIGNIFICANT;}else if(this.needMoreData_){this.significance_=Statistics.Significance.NEED_MORE_DATA;}else{this.significance_=Statistics.Significance.INSIGNIFICANT;}
+return this.significance_;}
+asDict(){return{p:this.p,U:this.U,significance:this.significance,};}}
+Statistics.mwu=function(a,b,opt_alpha,opt_reqSampleSize){const result=mannwhitneyu.test(a,b);const needMoreData=opt_reqSampleSize&&Math.min(a.length,b.length)<opt_reqSampleSize;return new HypothesisTestResult(result.p,result.U,needMoreData,opt_alpha);};return{Statistics,};});'use strict';tr.exportTo('tr.b',function(){const GREEK_SMALL_LETTER_MU=String.fromCharCode(956);const SECONDS_IN_A_MINUTE=60;const SECONDS_IN_AN_HOUR=SECONDS_IN_A_MINUTE*60;const SECONDS_IN_A_DAY=SECONDS_IN_AN_HOUR*24;const SECONDS_IN_A_WEEK=SECONDS_IN_A_DAY*7;const SECONDS_IN_A_YEAR=SECONDS_IN_A_DAY*365.2422;const SECONDS_IN_A_MONTH=SECONDS_IN_A_YEAR/12;const UnitPrefixScale={};const UnitScale={};function defineUnitPrefixScale(name,prefixes){if(UnitPrefixScale[name]!==undefined){throw new Error('Unit prefix scale \''+name+'\' already exists');}
+if(prefixes.AUTO!==undefined){throw new Error('The \'AUTO\' unit prefix is not supported for unit'+'prefix scales and cannot be added to scale \''+name+'\'');}
+UnitPrefixScale[name]=prefixes;}
+UnitScale.defineUnitScale=function(name,unitScale){if(UnitScale[name]!==undefined){throw new Error('Unit scale \''+name+'\' already exists');}
+if(unitScale.AUTO!==undefined){throw new Error('\'AUTO\' unit scale will be added automatically '+'for unit scale \''+name+'\'');}
+unitScale.AUTO=Object.values(unitScale);unitScale.AUTO.sort((a,b)=>a.value-b.value);if(name)UnitScale[name]=unitScale;return unitScale;};function definePrefixScaleFromUnitScale(prefixName,unitScale){if(!unitScale){throw new Error('Cannot create PrefixScale without a unit scale.');}
+const prefixScale={};for(const[curPrefix,curScale]of Object.entries(unitScale)){if(curPrefix==='AUTO'){continue;}
+if(curScale.symbol===undefined||!curScale.value){throw new Error(`Cannot create PrefixScale from malformed unit ${curScale}.`);}
+prefixScale[curPrefix]={value:curScale.value,symbol:curScale.symbol};}
+return defineUnitPrefixScale(prefixName,prefixScale);}
+UnitScale.defineUnitScaleFromPrefixScale=function(baseSymbol,baseName,prefixScale,opt_scaleName){if(baseSymbol===undefined){throw new Error('Cannot create UnitScale with undefined baseSymbol.');}
+if(!baseName){throw new Error('Cannot create UnitScale without a baseName.');}
+if(!prefixScale){throw new Error('Cannot create UnitScale without a prefix scale.');}
+const unitScale={};for(const curPrefix of Object.keys(prefixScale)){const curScale=prefixScale[curPrefix];if(curScale.symbol===undefined||!curScale.value){throw new Error(`Cannot convert PrefixScale with malformed prefix ${curScale}.`);}
+const name=curPrefix==='NONE'?baseName:`${curPrefix}_${baseName}`;unitScale[name]={value:curScale.value,symbol:curScale.symbol+baseSymbol,baseSymbol};}
+return UnitScale.defineUnitScale(opt_scaleName,unitScale);};function convertUnit(value,fromScale,toScale){if(value===undefined)return undefined;const fromScaleBase=fromScale.baseSymbol;const toScaleBase=toScale.baseSymbol;if(fromScaleBase!==undefined&&toScaleBase!==undefined&&fromScaleBase!==toScaleBase){throw new Error('Cannot convert between units with different base symbols.');}
+return value*(fromScale.value/toScale.value);}
+defineUnitPrefixScale('BINARY',{NONE:{value:Math.pow(1024,0),symbol:''},KIBI:{value:Math.pow(1024,1),symbol:'Ki'},MEBI:{value:Math.pow(1024,2),symbol:'Mi'},GIBI:{value:Math.pow(1024,3),symbol:'Gi'},TEBI:{value:Math.pow(1024,4),symbol:'Ti'}});defineUnitPrefixScale('METRIC',{NANO:{value:1e-9,symbol:'n'},MICRO:{value:1e-6,symbol:GREEK_SMALL_LETTER_MU},MILLI:{value:1e-3,symbol:'m'},NONE:{value:1,symbol:''},KILO:{value:1e3,symbol:'k'},MEGA:{value:1e6,symbol:'M'},GIGA:{value:1e9,symbol:'G'}});UnitScale.defineUnitScale('TIME',{NANO_SEC:{value:1e-9,symbol:'ns',baseSymbol:'s'},MICRO_SEC:{value:1e-6,symbol:GREEK_SMALL_LETTER_MU+'s',baseSymbol:'s'},MILLI_SEC:{value:1e-3,symbol:'ms',baseSymbol:'s'},SEC:{value:1,symbol:'s',baseSymbol:'s'},MINUTE:{value:SECONDS_IN_A_MINUTE,symbol:'min',baseSymbol:'s'},HOUR:{value:SECONDS_IN_AN_HOUR,symbol:'hr',baseSymbol:'s'},DAY:{value:SECONDS_IN_A_DAY,symbol:'days',baseSymbol:'s'},WEEK:{value:SECONDS_IN_A_WEEK,symbol:'weeks',baseSymbol:'s'},MONTH:{value:SECONDS_IN_A_MONTH,symbol:'months',baseSymbol:'s'},YEAR:{value:SECONDS_IN_A_YEAR,symbol:'years',baseSymbol:'s'}});UnitScale.defineUnitScaleFromPrefixScale('B','BYTE',UnitPrefixScale.BINARY,'MEMORY');definePrefixScaleFromUnitScale('DATA_SIZE',UnitScale.MEMORY);UnitScale.defineUnitScaleFromPrefixScale('/s','SECONDS',UnitPrefixScale.DATA_SIZE,'BANDWIDTH_BYTES');return{UnitPrefixScale,UnitScale,convertUnit,GREEK_SMALL_LETTER_MU,};});'use strict';tr.exportTo('tr.b',function(){const msDisplayMode={scale:1e-3,suffix:'ms',roundedLess(a,b){return Math.round(a*1000)<Math.round(b*1000);},formatSpec:{unitScale:[tr.b.UnitScale.TIME.MILLI_SEC],minimumFractionDigits:3,}};const nsDisplayMode={scale:1e-9,suffix:'ns',roundedLess(a,b){return Math.round(a*1000000)<Math.round(b*1000000);},formatSpec:{unitScale:[tr.b.UnitScale.TIME.NANO_SEC],maximumFractionDigits:0}};const TimeDisplayModes={ns:nsDisplayMode,ms:msDisplayMode};return{TimeDisplayModes,};});'use strict';tr.exportTo('tr.ui.b',function(){function iterateElementDeeplyImpl(element,cb,thisArg,includeElement){if(includeElement&&cb.call(thisArg,element))return true;if(element.root&&element.root!==element&&iterateElementDeeplyImpl(element.root,cb,thisArg,false)){return true;}
+const children=Polymer.dom(element).children;for(let i=0;i<children.length;i++){if(iterateElementDeeplyImpl(children[i],cb,thisArg,true)){return true;}}
+return false;}
+function iterateElementDeeply(element,cb,thisArg){iterateElementDeeplyImpl(element,cb,thisArg,false);}
+function findDeepElementMatchingPredicate(element,predicate){let foundElement=undefined;function matches(element){const match=predicate(element);if(!match){return false;}
+foundElement=element;return true;}
+iterateElementDeeply(element,matches);return foundElement;}
+function findDeepElementsMatchingPredicate(element,predicate){const foundElements=[];function matches(element){const match=predicate(element);if(match){foundElements.push(element);}
+return false;}
+iterateElementDeeply(element,matches);return foundElements;}
+function findDeepElementMatching(element,selector){return findDeepElementMatchingPredicate(element,function(element){return element.matches(selector);});}
+function findDeepElementsMatching(element,selector){return findDeepElementsMatchingPredicate(element,function(element){return element.matches(selector);});}
+function findDeepElementWithTextContent(element,re){return findDeepElementMatchingPredicate(element,function(element){if(element.children.length!==0)return false;return re.test(Polymer.dom(element).textContent);});}
+return{findDeepElementMatching,findDeepElementsMatching,findDeepElementMatchingPredicate,findDeepElementsMatchingPredicate,findDeepElementWithTextContent,};});'use strict';tr.exportTo('tr.b',function(){const TimeDisplayModes=tr.b.TimeDisplayModes;const PLUS_MINUS_SIGN=String.fromCharCode(177);const CACHED_FORMATTERS={};function getNumberFormatter(minSpec,maxSpec,minCtx,maxCtx){const key=minSpec+'-'+maxSpec+'-'+minCtx+'-'+maxCtx;let formatter=CACHED_FORMATTERS[key];if(formatter===undefined){let minimumFractionDigits=minCtx!==undefined?minCtx:minSpec;let maximumFractionDigits=maxCtx!==undefined?maxCtx:maxSpec;if(minimumFractionDigits>maximumFractionDigits){if(minCtx!==undefined&&maxCtx===undefined){maximumFractionDigits=minimumFractionDigits;}else if(minCtx===undefined&&maxCtx!==undefined){minimumFractionDigits=maximumFractionDigits;}}
+formatter=new Intl.NumberFormat(undefined,{minimumFractionDigits,maximumFractionDigits,});CACHED_FORMATTERS[key]=formatter;}
+return formatter;}
+function max(a,b){if(a===undefined)return b;if(b===undefined)return a;return a.scale>b.scale?a:b;}
+const ImprovementDirection={DONT_CARE:0,BIGGER_IS_BETTER:1,SMALLER_IS_BETTER:2};function Unit(unitName,jsonName,scaleBaseUnit,isDelta,improvementDirection,formatSpec){this.unitName=unitName;this.jsonName=jsonName;this.scaleBaseUnit=scaleBaseUnit;this.isDelta=isDelta;this.improvementDirection=improvementDirection;this.formatSpec_=formatSpec;this.baseUnit=undefined;this.correspondingDeltaUnit=undefined;}
+Unit.prototype={asJSON(){return this.jsonName;},asJSON2(){return this.asJSON().replace('_smallerIsBetter','-').replace('_biggerIsBetter','+');},truncate(value){if(typeof value!=='number')return value;if(0===(value%1))return value;if(typeof this.formatSpec_!=='function'&&(!this.formatSpec_.unitScale||((this.formatSpec_.unitScale.length===1)&&(this.formatSpec_.unitScale[0].value===1)))){const digits=this.formatSpec_.maximumFractionDigits||this.formatSpec_.minimumFractionDigits;return tr.b.math.truncate(value,digits+1);}
+const formatted=this.format(value);let test=Math.round(value);if(formatted===this.format(test))return test;let lo=1;let hi=16;while(lo<hi-1){const digits=parseInt((lo+hi)/2);test=tr.b.math.truncate(value,digits);if(formatted===this.format(test)){hi=digits;}else{lo=digits;}}
+test=tr.b.math.truncate(value,lo);if(formatted===this.format(test))return test;return tr.b.math.truncate(value,hi);},getUnitScale_(opt_context){let formatSpec=this.formatSpec_;let formatSpecWasFunction=false;if(typeof formatSpec==='function'){formatSpecWasFunction=true;formatSpec=formatSpec();}
+const context=opt_context||{};let scale=undefined;if(context.unitScale){scale=context.unitScale;}else if(context.unitPrefix){const symbol=formatSpec.baseSymbol?formatSpec.baseSymbol:this.scaleBaseUnit.baseSymbol;scale=tr.b.UnitScale.defineUnitScaleFromPrefixScale(symbol,symbol,[context.unitPrefix]).AUTO;}else{scale=formatSpec.unitScale;if(!scale){scale=[{value:1,symbol:formatSpec.baseSymbol||'',baseSymbol:formatSpec.baseSymbol||''}];if(!formatSpecWasFunction)formatSpec.unitScale=scale;}}
+if(!(scale instanceof Array)){throw new Error('Unit has a malformed unit scale.');}
+return scale;},get unitString(){const scale=this.getUnitScale_();if(!scale){throw new Error('A UnitScale could not be found for Unit '+this.unitName);}
+return scale[0].symbol;},format(value,opt_context){let signString='';if(value<0){signString='-';value=-value;}else if(this.isDelta){signString=value===0?PLUS_MINUS_SIGN:'+';}
+const context=opt_context||{};const scale=this.getUnitScale_(context);let deltaValue=context.deltaValue===undefined?value:context.deltaValue;deltaValue=Math.abs(deltaValue)*this.scaleBaseUnit.value;if(deltaValue===0){deltaValue=1;}
+let i=0;while(i<scale.length-1&&deltaValue/scale[i+1].value>=1){i++;}
+const selectedSubUnit=scale[i];let formatSpec=this.formatSpec_;if(typeof formatSpec==='function')formatSpec=formatSpec();let unitString='';if(selectedSubUnit.symbol){if(!formatSpec.avoidSpacePrecedingUnit)unitString=' ';unitString+=selectedSubUnit.symbol;}
+value=tr.b.convertUnit(value,this.scaleBaseUnit,selectedSubUnit);const numberString=getNumberFormatter(formatSpec.minimumFractionDigits,formatSpec.maximumFractionDigits,context.minimumFractionDigits,context.maximumFractionDigits).format(value);return signString+numberString+unitString;}};Unit.reset=function(){Unit.currentTimeDisplayMode=TimeDisplayModes.ms;};Unit.timestampFromUs=function(us){return tr.b.convertUnit(us,tr.b.UnitPrefixScale.METRIC.MICRO,tr.b.UnitPrefixScale.METRIC.MILLI);};Object.defineProperty(Unit,'currentTimeDisplayMode',{get(){return Unit.currentTimeDisplayMode_;},set(value){if(Unit.currentTimeDisplayMode_===value)return;Unit.currentTimeDisplayMode_=value;Unit.dispatchEvent(new tr.b.Event('display-mode-changed'));}});Unit.didPreferredTimeDisplayUnitChange=function(){let largest=undefined;const els=tr.ui.b.findDeepElementsMatching(document.body,'tr-v-ui-preferred-display-unit');els.forEach(function(el){largest=max(largest,el.preferredTimeDisplayMode);});Unit.currentTimeDisplayMode=largest===undefined?TimeDisplayModes.ms:largest;};Unit.byName={};Unit.byJSONName={};Unit.fromJSON=function(object){if(typeof(object)==='string'){if(object.endsWith('+')){object=object.slice(0,object.length-1)+'_biggerIsBetter';}else if(object.endsWith('-')){object=object.slice(0,object.length-1)+'_smallerIsBetter';}
+const u=Unit.byJSONName[object];if(u)return u;}
+throw new Error(`Unrecognized unit "${object}"`);};Unit.define=function(params){const definedUnits=[];for(const improvementDirection of Object.values(ImprovementDirection)){const regularUnit=Unit.defineUnitVariant_(params,false,improvementDirection);const deltaUnit=Unit.defineUnitVariant_(params,true,improvementDirection);regularUnit.correspondingDeltaUnit=deltaUnit;deltaUnit.correspondingDeltaUnit=deltaUnit;definedUnits.push(regularUnit,deltaUnit);}
+const baseUnit=Unit.byName[params.baseUnitName];definedUnits.forEach(u=>u.baseUnit=baseUnit);};Unit.nameSuffixForImprovementDirection=function(improvementDirection){switch(improvementDirection){case ImprovementDirection.DONT_CARE:return'';case ImprovementDirection.BIGGER_IS_BETTER:return'_biggerIsBetter';case ImprovementDirection.SMALLER_IS_BETTER:return'_smallerIsBetter';default:throw new Error('Unknown improvement direction: '+improvementDirection);}};Unit.defineUnitVariant_=function(params,isDelta,improvementDirection){let nameSuffix=isDelta?'Delta':'';nameSuffix+=Unit.nameSuffixForImprovementDirection(improvementDirection);const unitName=params.baseUnitName+nameSuffix;const jsonName=params.baseJsonName+nameSuffix;if(Unit.byName[unitName]!==undefined){throw new Error('Unit \''+unitName+'\' already exists');}
+if(Unit.byJSONName[jsonName]!==undefined){throw new Error('JSON unit \''+jsonName+'\' alread exists');}
+let scaleBaseUnit=params.scaleBaseUnit;if(!scaleBaseUnit){let formatSpec=params.formatSpec;if(typeof formatSpec==='function')formatSpec=formatSpec();const baseSymbol=formatSpec.unitScale?formatSpec.unitScale[0].baseSymbol:(formatSpec.baseSymbol||'');scaleBaseUnit={value:1,symbol:baseSymbol,baseSymbol};}
+const unit=new Unit(unitName,jsonName,scaleBaseUnit,isDelta,improvementDirection,params.formatSpec);Unit.byName[unitName]=unit;Unit.byJSONName[jsonName]=unit;return unit;};tr.b.EventTarget.decorate(Unit);Unit.reset();Unit.define({baseUnitName:'timeInMsAutoFormat',baseJsonName:'msBestFitFormat',scaleBaseUnit:tr.b.UnitScale.TIME.MILLI_SEC,formatSpec:{unitScale:tr.b.UnitScale.TIME.AUTO,minimumFractionDigits:0,maximumFractionDigits:3}});Unit.define({baseUnitName:'timeDurationInMs',baseJsonName:'ms',scaleBaseUnit:tr.b.UnitScale.TIME.MILLI_SEC,formatSpec(){return Unit.currentTimeDisplayMode_.formatSpec;}});Unit.define({baseUnitName:'timeStampInMs',baseJsonName:'tsMs',scaleBaseUnit:tr.b.UnitScale.TIME.MILLI_SEC,formatSpec(){return Unit.currentTimeDisplayMode_.formatSpec;}});Unit.define({baseUnitName:'normalizedPercentage',baseJsonName:'n%',formatSpec:{unitScale:[{value:0.01,symbol:'%'}],avoidSpacePrecedingUnit:true,minimumFractionDigits:1,maximumFractionDigits:1}});Unit.define({baseUnitName:'sizeInBytes',baseJsonName:'sizeInBytes',formatSpec:{unitScale:tr.b.UnitScale.MEMORY.AUTO,minimumFractionDigits:1,maximumFractionDigits:1}});Unit.define({baseUnitName:'bandwidthInBytesPerSecond',baseJsonName:'bytesPerSecond',formatSpec:{unitScale:tr.b.UnitScale.BANDWIDTH_BYTES.AUTO,minimumFractionDigits:1,maximumFractionDigits:1}});Unit.define({baseUnitName:'energyInJoules',baseJsonName:'J',formatSpec:{unitScale:tr.b.UnitScale.defineUnitScaleFromPrefixScale('J','JOULE',tr.b.UnitPrefixScale.METRIC,'JOULE').AUTO,minimumFractionDigits:3}});Unit.define({baseUnitName:'powerInWatts',baseJsonName:'W',formatSpec:{unitScale:tr.b.UnitScale.defineUnitScaleFromPrefixScale('W','WATT',tr.b.UnitPrefixScale.METRIC,'WATT').AUTO,minimumFractionDigits:3}});Unit.define({baseUnitName:'electricCurrentInAmperes',baseJsonName:'A',formatSpec:{baseSymbol:'A',unitScale:tr.b.UnitScale.defineUnitScaleFromPrefixScale('A','AMPERE',tr.b.UnitPrefixScale.METRIC,'AMPERE').AUTO,minimumFractionDigits:3}});Unit.define({baseUnitName:'electricPotentialInVolts',baseJsonName:'V',formatSpec:{baseSymbol:'V',unitScale:tr.b.UnitScale.defineUnitScaleFromPrefixScale('V','VOLT',tr.b.UnitPrefixScale.METRIC,'VOLT').AUTO,minimumFractionDigits:3}});Unit.define({baseUnitName:'frequencyInHertz',baseJsonName:'Hz',formatSpec:{baseSymbol:'Hz',unitScale:tr.b.UnitScale.defineUnitScaleFromPrefixScale('Hz','HERTZ',tr.b.UnitPrefixScale.METRIC,'HERTZ').AUTO,minimumFractionDigits:3}});Unit.define({baseUnitName:'unitlessNumber',baseJsonName:'unitless',formatSpec:{minimumFractionDigits:3,maximumFractionDigits:3}});Unit.define({baseUnitName:'count',baseJsonName:'count',formatSpec:{minimumFractionDigits:0,maximumFractionDigits:0}});Unit.define({baseUnitName:'sigma',baseJsonName:'sigma',formatSpec:{baseSymbol:String.fromCharCode(963),minimumFractionDigits:1,maximumFractionDigits:1}});return{ImprovementDirection,Unit,};});'use strict';tr.exportTo('tr.b',function(){class Scalar{constructor(unit,value){if(!(unit instanceof tr.b.Unit)){throw new Error('Expected Unit');}
+if(!(typeof(value)==='number')){throw new Error('Expected value to be number');}
+this.unit=unit;this.value=value;}
+asDict(){return{unit:this.unit.asJSON(),value:tr.b.numberToJson(this.value),};}
+toString(){return this.unit.format(this.value);}
+static fromDict(d){return new Scalar(tr.b.Unit.fromJSON(d.unit),tr.b.numberFromJson(d.value));}}
+return{Scalar,};});'use strict';tr.exportTo('tr.c',function(){function Auditor(model){this.model_=model;}
+Auditor.prototype={__proto__:Object.prototype,get model(){return this.model_;},runAnnotate(){},installUserFriendlyCategoryDriverIfNeeded(){},runAudit(){}};const options=new tr.b.ExtensionRegistryOptions(tr.b.BASIC_REGISTRY_MODE);options.defaultMetadata={};options.mandatoryBaseClass=Auditor;tr.b.decorateExtensionRegistry(Auditor,options);return{Auditor,};});'use strict';tr.exportTo('tr.b',function(){function clamp01(value){return Math.max(0,Math.min(1,value));}
+function Color(opt_r,opt_g,opt_b,opt_a){this.r=Math.floor(opt_r)||0;this.g=Math.floor(opt_g)||0;this.b=Math.floor(opt_b)||0;this.a=opt_a;}
+Color.fromString=function(str){let tmp;let values;if(str.substr(0,4)==='rgb('){tmp=str.substr(4,str.length-5);values=tmp.split(',').map(function(v){return v.replace(/^\s+/,'','g');});if(values.length!==3){throw new Error('Malformatted rgb-expression');}
+return new Color(parseInt(values[0]),parseInt(values[1]),parseInt(values[2]));}
+if(str.substr(0,5)==='rgba('){tmp=str.substr(5,str.length-6);values=tmp.split(',').map(function(v){return v.replace(/^\s+/,'','g');});if(values.length!==4){throw new Error('Malformatted rgb-expression');}
+return new Color(parseInt(values[0]),parseInt(values[1]),parseInt(values[2]),parseFloat(values[3]));}
+if(str[0]==='#'&&str.length===7){return new Color(parseInt(str.substr(1,2),16),parseInt(str.substr(3,2),16),parseInt(str.substr(5,2),16));}
+throw new Error('Unrecognized string format.');};Color.lerp=function(a,b,percent){if(a.a!==undefined&&b.a!==undefined){return Color.lerpRGBA(a,b,percent);}
+return Color.lerpRGB(a,b,percent);};Color.lerpRGB=function(a,b,percent){return new Color(((b.r-a.r)*percent)+a.r,((b.g-a.g)*percent)+a.g,((b.b-a.b)*percent)+a.b);};Color.lerpRGBA=function(a,b,percent){return new Color(((b.r-a.r)*percent)+a.r,((b.g-a.g)*percent)+a.g,((b.b-a.b)*percent)+a.b,((b.a-a.a)*percent)+a.a);};Color.fromDict=function(dict){return new Color(dict.r,dict.g,dict.b,dict.a);};Color.fromHSLExplicit=function(h,s,l,a){let r;let g;let b;function hue2rgb(p,q,t){if(t<0)t+=1;if(t>1)t-=1;if(t<1/6)return p+(q-p)*6*t;if(t<1/2)return q;if(t<2/3)return p+(q-p)*(2/3-t)*6;return p;}
+if(s===0){r=g=b=l;}else{const q=l<0.5?l*(1+s):l+s-l*s;const p=2*l-q;r=hue2rgb(p,q,h+1/3);g=hue2rgb(p,q,h);b=hue2rgb(p,q,h-1/3);}
+return new Color(Math.floor(r*255),Math.floor(g*255),Math.floor(b*255),a);};Color.fromHSL=function(hsl){return Color.fromHSLExplicit(hsl.h,hsl.s,hsl.l,hsl.a);};Color.prototype={clone(){const c=new Color();c.r=this.r;c.g=this.g;c.b=this.b;c.a=this.a;return c;},blendOver(bgColor){const oneMinusThisAlpha=1-this.a;const outA=this.a+bgColor.a*oneMinusThisAlpha;const bgBlend=(bgColor.a*oneMinusThisAlpha)/bgColor.a;return new Color(this.r*this.a+bgColor.r*bgBlend,this.g*this.a+bgColor.g*bgBlend,this.b*this.a+bgColor.b*bgBlend,outA);},brighten(opt_k){const k=opt_k||0.45;return new Color(Math.min(255,this.r+Math.floor(this.r*k)),Math.min(255,this.g+Math.floor(this.g*k)),Math.min(255,this.b+Math.floor(this.b*k)),this.a);},lighten(k,opt_maxL){const maxL=opt_maxL!==undefined?opt_maxL:1.0;const hsl=this.toHSL();hsl.l=Math.min(hsl.l+k,maxL);return Color.fromHSL(hsl);},darken(opt_k){let k;if(opt_k!==undefined){k=opt_k;}else{k=0.45;}
+return new Color(Math.min(255,this.r-Math.floor(this.r*k)),Math.min(255,this.g-Math.floor(this.g*k)),Math.min(255,this.b-Math.floor(this.b*k)),this.a);},desaturate(opt_desaturateFactor){let desaturateFactor;if(opt_desaturateFactor!==undefined){desaturateFactor=opt_desaturateFactor;}else{desaturateFactor=1;}
+const hsl=this.toHSL();hsl.s=clamp01(hsl.s*(1-desaturateFactor));return Color.fromHSL(hsl);},withAlpha(a){return new Color(this.r,this.g,this.b,a);},toString(){if(this.a!==undefined){return'rgba('+
+this.r+','+this.g+','+
+this.b+','+this.a+')';}
+return'rgb('+this.r+','+this.g+','+this.b+')';},toHSL(){const r=this.r/255;const g=this.g/255;const b=this.b/255;const max=Math.max(r,g,b);const min=Math.min(r,g,b);let h;let s;const l=(max+min)/2;if(min===max){h=0;s=0;}else{const delta=max-min;if(l>0.5){s=delta/(2-max-min);}else{s=delta/(max+min);}
+if(r===max){h=(g-b)/delta;if(g<b)h+=6;}else if(g===max){h=2+((b-r)/delta);}else{h=4+((r-g)/delta);}
+h/=6;}
+return{h,s,l,a:this.a};},toStringWithAlphaOverride(alpha){return'rgba('+
+this.r+','+this.g+','+
+this.b+','+alpha+')';}};return{Color,};});'use strict';tr.exportTo('tr.b',function(){function SinebowColorGenerator(opt_a,opt_brightness){this.a_=(opt_a===undefined)?1:opt_a;this.brightness_=(opt_brightness===undefined)?1:opt_brightness;this.colorIndex_=0;this.keyToColor={};}
+SinebowColorGenerator.prototype={colorForKey(key){if(!this.keyToColor[key]){this.keyToColor[key]=this.nextColor();}
+return this.keyToColor[key];},nextColor(){const components=SinebowColorGenerator.nthColor(this.colorIndex_++);return tr.b.Color.fromString(SinebowColorGenerator.calculateColor(components[0],components[1],components[2],this.a_,this.brightness_));}};SinebowColorGenerator.PHI=(1+Math.sqrt(5))/2;SinebowColorGenerator.sinebow=function(h){h+=0.5;h=-h;let r=Math.sin(Math.PI*h);let g=Math.sin(Math.PI*(h+1/3));let b=Math.sin(Math.PI*(h+2/3));r*=r;g*=g;b*=b;const y=2*(0.2989*r+0.5870*g+0.1140*b);r/=y;g/=y;b/=y;return[256*r,256*g,256*b];};SinebowColorGenerator.nthColor=function(n){return SinebowColorGenerator.sinebow(n*this.PHI);};SinebowColorGenerator.calculateColor=function(r,g,b,a,brightness){if(brightness<=1){r*=brightness;g*=brightness;b*=brightness;}else{r=tr.b.math.lerp(tr.b.math.normalize(brightness,1,2),r,255);g=tr.b.math.lerp(tr.b.math.normalize(brightness,1,2),g,255);b=tr.b.math.lerp(tr.b.math.normalize(brightness,1,2),b,255);}
+r=Math.round(r);g=Math.round(g);b=Math.round(b);return'rgba('+r+','+g+','+b+', '+a+')';};return{SinebowColorGenerator,};});'use strict';tr.exportTo('tr.b',function(){const numGeneralPurposeColorIds=23;const generalPurposeColors=new Array(numGeneralPurposeColorIds);const sinebowAlpha=1.0;const sinebowBrightness=1.5;const sinebowColorGenerator=new tr.b.SinebowColorGenerator(sinebowAlpha,sinebowBrightness);for(let i=0;i<numGeneralPurposeColorIds;i++){generalPurposeColors[i]=sinebowColorGenerator.nextColor();}
+const reservedColorsByName={thread_state_uninterruptible:new tr.b.Color(182,125,143),thread_state_iowait:new tr.b.Color(255,140,0),thread_state_running:new tr.b.Color(126,200,148),thread_state_runnable:new tr.b.Color(133,160,210),thread_state_sleeping:new tr.b.Color(240,240,240),thread_state_unknown:new tr.b.Color(199,155,125),background_memory_dump:new tr.b.Color(0,180,180),light_memory_dump:new tr.b.Color(0,0,180),detailed_memory_dump:new tr.b.Color(180,0,180),vsync_highlight_color:new tr.b.Color(0,0,255),generic_work:new tr.b.Color(125,125,125),good:new tr.b.Color(0,125,0),bad:new tr.b.Color(180,125,0),terrible:new tr.b.Color(180,0,0),black:new tr.b.Color(0,0,0),grey:new tr.b.Color(221,221,221),white:new tr.b.Color(255,255,255),yellow:new tr.b.Color(255,255,0),olive:new tr.b.Color(100,100,0),rail_response:new tr.b.Color(67,135,253),rail_animation:new tr.b.Color(244,74,63),rail_idle:new tr.b.Color(238,142,0),rail_load:new tr.b.Color(13,168,97),startup:new tr.b.Color(230,230,0),heap_dump_stack_frame:new tr.b.Color(128,128,128),heap_dump_object_type:new tr.b.Color(0,0,255),heap_dump_child_node_arrow:new tr.b.Color(204,102,0),cq_build_running:new tr.b.Color(255,255,119),cq_build_passed:new tr.b.Color(153,238,102),cq_build_failed:new tr.b.Color(238,136,136),cq_build_abandoned:new tr.b.Color(187,187,187),cq_build_attempt_runnig:new tr.b.Color(222,222,75),cq_build_attempt_passed:new tr.b.Color(103,218,35),cq_build_attempt_failed:new tr.b.Color(197,81,81)};const numReservedColorIds=Object.keys(reservedColorsByName).length;const numColorsPerVariant=numGeneralPurposeColorIds+numReservedColorIds;function ColorScheme(){}
+const paletteBase=[];paletteBase.push.apply(paletteBase,generalPurposeColors);paletteBase.push.apply(paletteBase,Object.values(reservedColorsByName));ColorScheme.colors=[];ColorScheme.properties={};ColorScheme.properties={numColorsPerVariant,};function pushVariant(func){const variantColors=paletteBase.map(func);ColorScheme.colors.push.apply(ColorScheme.colors,variantColors);}
+pushVariant(function(c){return c;});ColorScheme.properties.brightenedOffsets=[];ColorScheme.properties.brightenedOffsets.push(ColorScheme.colors.length);pushVariant(function(c){return c.lighten(0.3,0.8);});ColorScheme.properties.brightenedOffsets.push(ColorScheme.colors.length);pushVariant(function(c){return c.lighten(0.48,0.85);});ColorScheme.properties.brightenedOffsets.push(ColorScheme.colors.length);pushVariant(function(c){return c.lighten(0.65,0.9);});ColorScheme.properties.dimmedOffsets=[];ColorScheme.properties.dimmedOffsets.push(ColorScheme.colors.length);pushVariant(function(c){return c.desaturate();});ColorScheme.properties.dimmedOffsets.push(ColorScheme.colors.length);pushVariant(function(c){return c.desaturate(0.5);});ColorScheme.properties.dimmedOffsets.push(ColorScheme.colors.length);pushVariant(function(c){return c.desaturate(0.3);});ColorScheme.colorsAsStrings=ColorScheme.colors.map(function(c){return c.toString();});const reservedColorNameToIdMap=(function(){const m=new Map();let i=generalPurposeColors.length;for(const key of Object.keys(reservedColorsByName)){m.set(key,i++);}
+return m;})();ColorScheme.getColorIdForReservedName=function(name){const id=reservedColorNameToIdMap.get(name);if(id===undefined){throw new Error('Unrecognized color '+name);}
+return id;};ColorScheme.getColorForReservedNameAsString=function(reservedName){const id=ColorScheme.getColorIdForReservedName(reservedName);return ColorScheme.colorsAsStrings[id];};ColorScheme.getStringHash=function(name){let hash=0;for(let i=0;i<name.length;++i){hash=(hash+37*hash+11*name.charCodeAt(i))%0xFFFFFFFF;}
+return hash;};const stringColorIdCache=new Map();ColorScheme.getColorIdForGeneralPurposeString=function(string){if(stringColorIdCache.get(string)===undefined){const hash=ColorScheme.getStringHash(string);stringColorIdCache.set(string,hash%numGeneralPurposeColorIds);}
+return stringColorIdCache.get(string);};ColorScheme.getAnotherColorId=function(colorId,n){return(colorId+n)%numColorsPerVariant;};ColorScheme.getVariantColorId=function(colorId,offset){return colorId+offset;};return{ColorScheme,};});'use strict';tr.exportTo('tr.model',function(){const ColorScheme=tr.b.ColorScheme;function EventInfo(title,description,docLinks){this.title=title;this.description=description;this.docLinks=docLinks;this.colorId=ColorScheme.getColorIdForGeneralPurposeString(title);}
+return{EventInfo,};});'use strict';tr.exportTo('tr.b',function(){let nextGUID=1;const UUID4_PATTERN='xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx';const GUID={allocateSimple(){return nextGUID++;},getLastSimpleGuid(){return nextGUID-1;},allocateUUID4(){return UUID4_PATTERN.replace(/[xy]/g,function(c){let r=parseInt(Math.random()*16);if(c==='y')r=(r&3)+8;return r.toString(16);});}};return{GUID,};});'use strict';tr.exportTo('tr.model',function(){function EventRegistry(){}
+const options=new tr.b.ExtensionRegistryOptions(tr.b.BASIC_REGISTRY_MODE);tr.b.decorateExtensionRegistry(EventRegistry,options);EventRegistry.addEventListener('will-register',function(e){const metadata=e.typeInfo.metadata;if(metadata.name===undefined){throw new Error('Registered events must provide name metadata');}
+if(metadata.pluralName===undefined){throw new Error('Registered events must provide pluralName metadata');}
+if(metadata.subTypes===undefined){metadata.subTypes={};const options=new tr.b.ExtensionRegistryOptions(tr.b.TYPE_BASED_REGISTRY_MODE);options.mandatoryBaseClass=e.typeInfo.constructor;options.defaultConstructor=e.typeInfo.constructor;tr.b.decorateExtensionRegistry(metadata.subTypes,options);}else{if(!metadata.subTypes.register){throw new Error('metadata.subTypes must be an extension registry.');}}
+e.typeInfo.constructor.subTypes=metadata.subTypes;});let eventsByTypeName=undefined;EventRegistry.getEventTypeInfoByTypeName=function(typeName){if(eventsByTypeName===undefined){eventsByTypeName={};EventRegistry.getAllRegisteredTypeInfos().forEach(function(typeInfo){eventsByTypeName[typeInfo.metadata.name]=typeInfo;});}
+return eventsByTypeName[typeName];};EventRegistry.addEventListener('registry-changed',function(){eventsByTypeName=undefined;});function convertCamelCaseToTitleCase(name){let result=name.replace(/[A-Z]/g,' $&');result=result.charAt(0).toUpperCase()+result.slice(1);return result;}
+EventRegistry.getUserFriendlySingularName=function(typeName){const typeInfo=EventRegistry.getEventTypeInfoByTypeName(typeName);const str=typeInfo.metadata.name;return convertCamelCaseToTitleCase(str);};EventRegistry.getUserFriendlyPluralName=function(typeName){const typeInfo=EventRegistry.getEventTypeInfoByTypeName(typeName);const str=typeInfo.metadata.pluralName;return convertCamelCaseToTitleCase(str);};return{EventRegistry,};});'use strict';tr.exportTo('tr.model',function(){const EventRegistry=tr.model.EventRegistry;const RequestSelectionChangeEvent=tr.b.Event.bind(undefined,'requestSelectionChange',true,false);function EventSet(opt_events){this.bounds_=new tr.b.math.Range();this.events_=new Set();this.guid_=tr.b.GUID.allocateSimple();if(opt_events){if(opt_events instanceof Array){for(const event of opt_events){this.push(event);}}else if(opt_events instanceof EventSet){this.addEventSet(opt_events);}else{this.push(opt_events);}}}
+EventSet.prototype={__proto__:Object.prototype,get bounds(){return this.bounds_;},get duration(){if(this.bounds_.isEmpty)return 0;return this.bounds_.max-this.bounds_.min;},get length(){return this.events_.size;},get guid(){return this.guid_;},*[Symbol.iterator](){for(const event of this.events_){yield event;}},clear(){this.bounds_=new tr.b.math.Range();this.events_.clear();},push(...events){let numPushed;for(const event of events){if(event.guid===undefined){throw new Error('Event must have a GUID');}
+if(!this.events_.has(event)){this.events_.add(event);if(event.addBoundsToRange){if(this.bounds_!==undefined){event.addBoundsToRange(this.bounds_);}}}
+numPushed++;}
+return numPushed;},contains(event){if(this.events_.has(event))return event;return undefined;},addEventSet(eventSet){for(const event of eventSet){this.push(event);}},intersectionIsEmpty(otherEventSet){return!this.some(event=>otherEventSet.contains(event));},equals(that){if(this.length!==that.length)return false;return this.every(event=>that.contains(event));},sortEvents(compare){const ary=this.toArray();ary.sort(compare);this.clear();for(const event of ary){this.push(event);}},getEventsOrganizedByBaseType(opt_pruneEmpty){const allTypeInfos=EventRegistry.getAllRegisteredTypeInfos();const events=this.getEventsOrganizedByCallback(function(event){let maxEventIndex=-1;let maxEventTypeInfo=undefined;allTypeInfos.forEach(function(eventTypeInfo,eventIndex){if(!(event instanceof eventTypeInfo.constructor))return;if(eventIndex>maxEventIndex){maxEventIndex=eventIndex;maxEventTypeInfo=eventTypeInfo;}});if(maxEventIndex===-1){throw new Error(`Unrecognized event type: ${event.constructor.name}`);}
+return maxEventTypeInfo.metadata.name;});if(!opt_pruneEmpty){allTypeInfos.forEach(function(eventTypeInfo){if(events[eventTypeInfo.metadata.name]===undefined){events[eventTypeInfo.metadata.name]=new EventSet();}});}
+return events;},getEventsOrganizedByTitle(){return this.getEventsOrganizedByCallback(function(event){if(event.title===undefined){throw new Error('An event didn\'t have a title!');}
+return event.title;});},getEventsOrganizedByCallback(cb,opt_this){const groupedEvents=tr.b.groupIntoMap(this,cb,opt_this||this);const groupedEventsDict={};for(const[k,events]of groupedEvents){groupedEventsDict[k]=new EventSet(events);}
+return groupedEventsDict;},enumEventsOfType(type,func){for(const event of this){if(event instanceof type){func(event);}}},get userFriendlyName(){if(this.length===0){throw new Error('Empty event set');}
+const eventsByBaseType=this.getEventsOrganizedByBaseType(true);const eventTypeName=Object.keys(eventsByBaseType)[0];if(this.length===1){const tmp=EventRegistry.getUserFriendlySingularName(eventTypeName);return tr.b.getOnlyElement(this.events_).userFriendlyName;}
+const numEventTypes=Object.keys(eventsByBaseType).length;if(numEventTypes!==1){return this.length+' events of various types';}
+const tmp=EventRegistry.getUserFriendlyPluralName(eventTypeName);return this.length+' '+tmp;},filter(fn,opt_this){const res=new EventSet();for(const event of this){if(fn.call(opt_this,event)){res.push(event);}}
+return res;},toArray(){const ary=[];for(const event of this){ary.push(event);}
+return ary;},forEach(fn,opt_this){for(const event of this){fn.call(opt_this,event);}},map(fn,opt_this){const res=[];for(const event of this){res.push(fn.call(opt_this,event));}
+return res;},every(fn,opt_this){for(const event of this){if(!fn.call(opt_this,event)){return false;}}
+return true;},some(fn,opt_this){for(const event of this){if(fn.call(opt_this,event)){return true;}}
+return false;},asDict(){const stableIds=[];for(const event of this){stableIds.push(event.stableId);}
+return{'events':stableIds};},asSet(){return this.events_;}};EventSet.IMMUTABLE_EMPTY_SET=(function(){const s=new EventSet();s.push=function(){throw new Error('Cannot push to an immutable event set');};s.addEventSet=function(){throw new Error('Cannot add to an immutable event set');};Object.freeze(s);return s;})();return{EventSet,RequestSelectionChangeEvent,};});'use strict';tr.exportTo('tr.model',function(){const ColorScheme=tr.b.ColorScheme;const SelectionState={NONE:0,SELECTED:ColorScheme.properties.brightenedOffsets[0],HIGHLIGHTED:ColorScheme.properties.brightenedOffsets[1],DIMMED:ColorScheme.properties.dimmedOffsets[0],BRIGHTENED0:ColorScheme.properties.brightenedOffsets[0],BRIGHTENED1:ColorScheme.properties.brightenedOffsets[1],BRIGHTENED2:ColorScheme.properties.brightenedOffsets[2],DIMMED0:ColorScheme.properties.dimmedOffsets[0],DIMMED1:ColorScheme.properties.dimmedOffsets[1],DIMMED2:ColorScheme.properties.dimmedOffsets[2]};const brighteningLevels=[SelectionState.NONE,SelectionState.BRIGHTENED0,SelectionState.BRIGHTENED1,SelectionState.BRIGHTENED2];SelectionState.getFromBrighteningLevel=function(level){return brighteningLevels[level];};const dimmingLevels=[SelectionState.DIMMED0,SelectionState.DIMMED1,SelectionState.DIMMED2];SelectionState.getFromDimmingLevel=function(level){return dimmingLevels[level];};return{SelectionState,};});'use strict';tr.exportTo('tr.model',function(){const SelectionState=tr.model.SelectionState;function SelectableItem(modelItem){this.modelItem_=modelItem;}
+SelectableItem.prototype={get modelItem(){return this.modelItem_;},get selected(){return this.selectionState===SelectionState.SELECTED;},addToSelection(selection){const modelItem=this.modelItem_;if(!modelItem)return;selection.push(modelItem);},addToTrackMap(eventToTrackMap,track){const modelItem=this.modelItem_;if(!modelItem)return;eventToTrackMap.addEvent(modelItem,track);}};return{SelectableItem,};});'use strict';tr.exportTo('tr.model',function(){const SelectableItem=tr.model.SelectableItem;const SelectionState=tr.model.SelectionState;const IMMUTABLE_EMPTY_SET=tr.model.EventSet.IMMUTABLE_EMPTY_SET;function Event(){SelectableItem.call(this,this);this.guid_=tr.b.GUID.allocateSimple();this.selectionState=SelectionState.NONE;this.info=undefined;}
+Event.prototype={__proto__:SelectableItem.prototype,get guid(){return this.guid_;},get stableId(){return undefined;},get range(){const range=new tr.b.math.Range();this.addBoundsToRange(range);return range;},associatedAlerts:IMMUTABLE_EMPTY_SET,addAssociatedAlert(alert){if(this.associatedAlerts===IMMUTABLE_EMPTY_SET){this.associatedAlerts=new tr.model.EventSet();}
+this.associatedAlerts.push(alert);},addBoundsToRange(range){}};return{Event,};});'use strict';tr.exportTo('tr.model',function(){function TimedEvent(start){tr.model.Event.call(this);this.start=start;this.duration=0;this.cpuStart=undefined;this.cpuDuration=undefined;this.contexts=Object.freeze([]);}
+TimedEvent.prototype={__proto__:tr.model.Event.prototype,get end(){return this.start+this.duration;},get boundsRange(){return tr.b.math.Range.fromExplicitRange(this.start,this.end);},addBoundsToRange(range){range.addValue(this.start);range.addValue(this.end);},bounds(that,opt_precisionUnit){if(opt_precisionUnit===undefined){opt_precisionUnit=tr.b.TimeDisplayModes.ms;}
+const startsBefore=opt_precisionUnit.roundedLess(that.start,this.start);const endsAfter=opt_precisionUnit.roundedLess(this.end,that.end);return!startsBefore&&!endsAfter;}};return{TimedEvent,};});'use strict';tr.exportTo('tr.model',function(){function Alert(info,start,opt_associatedEvents,opt_args){tr.model.TimedEvent.call(this,start);this.info=info;this.args=opt_args||{};this.associatedEvents=new tr.model.EventSet(opt_associatedEvents);this.associatedEvents.forEach(function(event){event.addAssociatedAlert(this);},this);}
+Alert.prototype={__proto__:tr.model.TimedEvent.prototype,get title(){return this.info.title;},get colorId(){return this.info.colorId;},get userFriendlyName(){return'Alert '+this.title+' at '+
+tr.b.Unit.byName.timeStampInMs.format(this.start);}};tr.model.EventRegistry.register(Alert,{name:'alert',pluralName:'alerts'});return{Alert,};});'use strict';tr.exportTo('tr.model',function(){const ColorScheme=tr.b.ColorScheme;const Statistics=tr.b.math.Statistics;const FRAME_PERF_CLASS={GOOD:'good',BAD:'bad',TERRIBLE:'terrible',NEUTRAL:'generic_work'};function Frame(associatedEvents,threadTimeRanges,opt_args){tr.model.Event.call(this);this.threadTimeRanges=threadTimeRanges;this.associatedEvents=new tr.model.EventSet(associatedEvents);this.args=opt_args||{};this.title='Frame';this.start=Statistics.min(threadTimeRanges,function(x){return x.start;});this.end=Statistics.max(threadTimeRanges,function(x){return x.end;});this.totalDuration=Statistics.sum(threadTimeRanges,function(x){return x.end-x.start;});this.perfClass=FRAME_PERF_CLASS.NEUTRAL;}
+Frame.prototype={__proto__:tr.model.Event.prototype,set perfClass(perfClass){this.colorId=ColorScheme.getColorIdForReservedName(perfClass);this.perfClass_=perfClass;},get perfClass(){return this.perfClass_;},shiftTimestampsForward(amount){this.start+=amount;this.end+=amount;for(let i=0;i<this.threadTimeRanges.length;i++){this.threadTimeRanges[i].start+=amount;this.threadTimeRanges[i].end+=amount;}},addBoundsToRange(range){range.addValue(this.start);range.addValue(this.end);}};tr.model.EventRegistry.register(Frame,{name:'frame',pluralName:'frames'});return{Frame,FRAME_PERF_CLASS,};});'use strict';tr.exportTo('tr.model.helpers',function(){const Frame=tr.model.Frame;const Statistics=tr.b.math.Statistics;const UI_DRAW_TYPE={NONE:'none',LEGACY:'legacy',MARSHMALLOW:'marshmallow'};const UI_THREAD_DRAW_NAMES={'performTraversals':UI_DRAW_TYPE.LEGACY,'Choreographer#doFrame':UI_DRAW_TYPE.MARSHMALLOW};const RENDER_THREAD_DRAW_NAME='DrawFrame';const RENDER_THREAD_INDEP_DRAW_NAME='doFrame';const RENDER_THREAD_QUEUE_NAME='queueBuffer';const RENDER_THREAD_SWAP_NAME='eglSwapBuffers';const THREAD_SYNC_NAME='syncFrameState';function getSlicesForThreadTimeRanges(threadTimeRanges){const ret=[];threadTimeRanges.forEach(function(threadTimeRange){const slices=[];threadTimeRange.thread.sliceGroup.iterSlicesInTimeRange(function(slice){slices.push(slice);},threadTimeRange.start,threadTimeRange.end);ret.push.apply(ret,slices);});return ret;}
+function makeFrame(threadTimeRanges,surfaceFlinger){const args={};if(surfaceFlinger&&surfaceFlinger.hasVsyncs){const start=Statistics.min(threadTimeRanges,function(threadTimeRanges){return threadTimeRanges.start;});args.deadline=surfaceFlinger.getFrameDeadline(start);args.frameKickoff=surfaceFlinger.getFrameKickoff(start);}
+const events=getSlicesForThreadTimeRanges(threadTimeRanges);return new Frame(events,threadTimeRanges,args);}
+function findOverlappingDrawFrame(renderThread,uiDrawSlice){if(!renderThread)return undefined;let overlappingDrawFrame;const slices=tr.b.iterateOverIntersectingIntervals(renderThread.sliceGroup.slices,function(range){return range.start;},function(range){return range.end;},uiDrawSlice.start,uiDrawSlice.end,function(rtDrawSlice){if(rtDrawSlice.title===RENDER_THREAD_DRAW_NAME){const rtSyncSlice=rtDrawSlice.findDescendentSlice(THREAD_SYNC_NAME);if(rtSyncSlice&&rtSyncSlice.start>=uiDrawSlice.start&&rtSyncSlice.end<=uiDrawSlice.end){overlappingDrawFrame=rtDrawSlice;}}});return overlappingDrawFrame;}
+function getPreTraversalWorkRanges(uiThread){if(!uiThread)return[];const preFrameEvents=[];uiThread.sliceGroup.slices.forEach(function(slice){if(slice.title==='obtainView'||slice.title==='setupListItem'||slice.title==='deliverInputEvent'||slice.title==='RV Scroll'){preFrameEvents.push(slice);}});uiThread.asyncSliceGroup.slices.forEach(function(slice){if(slice.title==='deliverInputEvent'){preFrameEvents.push(slice);}});return tr.b.math.mergeRanges(tr.b.math.convertEventsToRanges(preFrameEvents),3,function(events){return{start:events[0].min,end:events[events.length-1].max};});}
+function getFrameStartTime(traversalStart,preTraversalWorkRanges){const preTraversalWorkRange=tr.b.findClosestIntervalInSortedIntervals(preTraversalWorkRanges,function(range){return range.start;},function(range){return range.end;},traversalStart,3);if(preTraversalWorkRange){return preTraversalWorkRange.start;}
+return traversalStart;}
+function getRtFrameEndTime(rtDrawSlice){const rtQueueSlice=rtDrawSlice.findDescendentSlice(RENDER_THREAD_QUEUE_NAME);if(rtQueueSlice){return rtQueueSlice.end;}
+const rtSwapSlice=rtDrawSlice.findDescendentSlice(RENDER_THREAD_SWAP_NAME);if(rtSwapSlice){return rtSwapSlice.end;}
+return rtDrawSlice.end;}
+function getUiThreadDrivenFrames(app){if(!app.uiThread)return[];let preTraversalWorkRanges=[];if(app.uiDrawType===UI_DRAW_TYPE.LEGACY){preTraversalWorkRanges=getPreTraversalWorkRanges(app.uiThread);}
+const frames=[];app.uiThread.sliceGroup.slices.forEach(function(slice){if(!(slice.title in UI_THREAD_DRAW_NAMES)){return;}
+const threadTimeRanges=[];const uiThreadTimeRange={thread:app.uiThread,start:getFrameStartTime(slice.start,preTraversalWorkRanges),end:slice.end};threadTimeRanges.push(uiThreadTimeRange);const rtDrawSlice=findOverlappingDrawFrame(app.renderThread,slice);if(rtDrawSlice){const rtSyncSlice=rtDrawSlice.findDescendentSlice(THREAD_SYNC_NAME);if(rtSyncSlice){uiThreadTimeRange.end=Math.min(uiThreadTimeRange.end,rtSyncSlice.start);}
+threadTimeRanges.push({thread:app.renderThread,start:rtDrawSlice.start,end:getRtFrameEndTime(rtDrawSlice)});}
+frames.push(makeFrame(threadTimeRanges,app.surfaceFlinger));});return frames;}
+function getRenderThreadDrivenFrames(app){if(!app.renderThread)return[];const frames=[];app.renderThread.sliceGroup.getSlicesOfName(RENDER_THREAD_INDEP_DRAW_NAME).forEach(function(slice){const threadTimeRanges=[{thread:app.renderThread,start:slice.start,end:slice.end}];frames.push(makeFrame(threadTimeRanges,app.surfaceFlinger));});return frames;}
+function getUiDrawType(uiThread){if(!uiThread){return UI_DRAW_TYPE.NONE;}
+const slices=uiThread.sliceGroup.slices;for(let i=0;i<slices.length;i++){if(slices[i].title in UI_THREAD_DRAW_NAMES){return UI_THREAD_DRAW_NAMES[slices[i].title];}}
+return UI_DRAW_TYPE.NONE;}
+function getInputSamples(process){let samples=undefined;for(const counterName in process.counters){if(/^android\.aq\:pending/.test(counterName)&&process.counters[counterName].numSeries===1){samples=process.counters[counterName].series[0].samples;break;}}
+if(!samples)return[];const inputSamples=[];let lastValue=0;samples.forEach(function(sample){if(sample.value>lastValue){inputSamples.push(sample);}
+lastValue=sample.value;});return inputSamples;}
+function getAnimationAsyncSlices(uiThread){if(!uiThread)return[];const slices=[];for(const slice of uiThread.asyncSliceGroup.getDescendantEvents()){if(/^animator\:/.test(slice.title)){slices.push(slice);}}
+return slices;}
+function AndroidApp(process,uiThread,renderThread,surfaceFlinger,uiDrawType){this.process=process;this.uiThread=uiThread;this.renderThread=renderThread;this.surfaceFlinger=surfaceFlinger;this.uiDrawType=uiDrawType;this.frames_=undefined;this.inputs_=undefined;}
+AndroidApp.createForProcessIfPossible=function(process,surfaceFlinger){let uiThread=process.getThread(process.pid);const uiDrawType=getUiDrawType(uiThread);if(uiDrawType===UI_DRAW_TYPE.NONE){uiThread=undefined;}
+const renderThreads=process.findAllThreadsNamed('RenderThread');const renderThread=(renderThreads.length===1?renderThreads[0]:undefined);if(uiThread||renderThread){return new AndroidApp(process,uiThread,renderThread,surfaceFlinger,uiDrawType);}};AndroidApp.prototype={getFrames(){if(!this.frames_){const uiFrames=getUiThreadDrivenFrames(this);const rtFrames=getRenderThreadDrivenFrames(this);this.frames_=uiFrames.concat(rtFrames);this.frames_.sort(function(a,b){a.end-b.end;});}
+return this.frames_;},getInputSamples(){if(!this.inputs_){this.inputs_=getInputSamples(this.process);}
+return this.inputs_;},getAnimationAsyncSlices(){if(!this.animations_){this.animations_=getAnimationAsyncSlices(this.uiThread);}
+return this.animations_;}};return{AndroidApp,};});'use strict';tr.exportTo('tr.model.helpers',function(){const findLowIndexInSortedArray=tr.b.findLowIndexInSortedArray;const VSYNC_SF_NAME='android.VSYNC-sf';const VSYNC_APP_NAME='android.VSYNC-app';const VSYNC_FALLBACK_NAME='android.VSYNC';const TIMESTAMP_FUDGE_MS=0.01;function getVsyncTimestamps(process,counterName){let vsync=process.counters[counterName];if(!vsync){vsync=process.counters[VSYNC_FALLBACK_NAME];}
+if(vsync&&vsync.numSeries===1&&vsync.numSamples>1){return vsync.series[0].timestamps;}
+return undefined;}
+function AndroidSurfaceFlinger(process,thread){this.process=process;this.thread=thread;this.appVsync_=undefined;this.sfVsync_=undefined;this.appVsyncTimestamps_=getVsyncTimestamps(process,VSYNC_APP_NAME);this.sfVsyncTimestamps_=getVsyncTimestamps(process,VSYNC_SF_NAME);this.deadlineDelayMs_=this.appVsyncTimestamps_!==this.sfVsyncTimestamps_?5:TIMESTAMP_FUDGE_MS;}
+AndroidSurfaceFlinger.createForProcessIfPossible=function(process){const mainThread=process.getThread(process.pid);if(mainThread&&mainThread.name&&/surfaceflinger/.test(mainThread.name)){return new AndroidSurfaceFlinger(process,mainThread);}
+const primaryThreads=process.findAllThreadsNamed('SurfaceFlinger');if(primaryThreads.length===1){return new AndroidSurfaceFlinger(process,primaryThreads[0]);}
+return undefined;};AndroidSurfaceFlinger.prototype={get hasVsyncs(){return!!this.appVsyncTimestamps_&&!!this.sfVsyncTimestamps_;},getFrameKickoff(timestamp){if(!this.hasVsyncs){throw new Error('cannot query vsync info without vsyncs');}
+const firstGreaterIndex=findLowIndexInSortedArray(this.appVsyncTimestamps_,function(x){return x;},timestamp+TIMESTAMP_FUDGE_MS);if(firstGreaterIndex<1)return undefined;return this.appVsyncTimestamps_[firstGreaterIndex-1];},getFrameDeadline(timestamp){if(!this.hasVsyncs){throw new Error('cannot query vsync info without vsyncs');}
+const firstGreaterIndex=findLowIndexInSortedArray(this.sfVsyncTimestamps_,function(x){return x;},timestamp+this.deadlineDelayMs_);if(firstGreaterIndex>=this.sfVsyncTimestamps_.length){return undefined;}
+return this.sfVsyncTimestamps_[firstGreaterIndex];}};return{AndroidSurfaceFlinger,};});'use strict';tr.exportTo('tr.model.helpers',function(){const AndroidApp=tr.model.helpers.AndroidApp;const AndroidSurfaceFlinger=tr.model.helpers.AndroidSurfaceFlinger;const IMPORTANT_SURFACE_FLINGER_SLICES={'doComposition':true,'updateTexImage':true,'postFramebuffer':true};const IMPORTANT_UI_THREAD_SLICES={'Choreographer#doFrame':true,'performTraversals':true,'deliverInputEvent':true};const IMPORTANT_RENDER_THREAD_SLICES={'doFrame':true};function iterateImportantThreadSlices(thread,important,callback){if(!thread)return;thread.sliceGroup.slices.forEach(function(slice){if(slice.title in important){callback(slice);}});}
+function AndroidModelHelper(model){this.model=model;this.apps=[];this.surfaceFlinger=undefined;const processes=model.getAllProcesses();for(let i=0;i<processes.length&&!this.surfaceFlinger;i++){this.surfaceFlinger=AndroidSurfaceFlinger.createForProcessIfPossible(processes[i]);}
+model.getAllProcesses().forEach(function(process){const app=AndroidApp.createForProcessIfPossible(process,this.surfaceFlinger);if(app){this.apps.push(app);}},this);}
+AndroidModelHelper.guid=tr.b.GUID.allocateSimple();AndroidModelHelper.supportsModel=function(model){return true;};AndroidModelHelper.prototype={iterateImportantSlices(callback){if(this.surfaceFlinger){iterateImportantThreadSlices(this.surfaceFlinger.thread,IMPORTANT_SURFACE_FLINGER_SLICES,callback);}
+this.apps.forEach(function(app){iterateImportantThreadSlices(app.uiThread,IMPORTANT_UI_THREAD_SLICES,callback);iterateImportantThreadSlices(app.renderThread,IMPORTANT_RENDER_THREAD_SLICES,callback);});}};return{AndroidModelHelper,};});'use strict';tr.exportTo('tr.model',function(){function Slice(category,title,colorId,start,args,opt_duration,opt_cpuStart,opt_cpuDuration,opt_argsStripped,opt_bindId){if(new.target){throw new Error('Can\'t instantiate pure virtual class Slice');}
+tr.model.TimedEvent.call(this,start);this.category=category||'';this.title=title;this.colorId=colorId;this.args=args;this.startStackFrame=undefined;this.endStackFrame=undefined;this.didNotFinish=false;this.inFlowEvents=[];this.outFlowEvents=[];this.subSlices=[];this.selfTime=undefined;this.cpuSelfTime=undefined;this.important=false;this.parentContainer=undefined;this.argsStripped=false;this.bind_id_=opt_bindId;this.parentSlice=undefined;this.isTopLevel=false;if(opt_duration!==undefined){this.duration=opt_duration;}
+if(opt_cpuStart!==undefined){this.cpuStart=opt_cpuStart;}
+if(opt_cpuDuration!==undefined){this.cpuDuration=opt_cpuDuration;}
+if(opt_argsStripped!==undefined){this.argsStripped=true;}}
+Slice.prototype={__proto__:tr.model.TimedEvent.prototype,get analysisTypeName(){return this.title;},get userFriendlyName(){return'Slice '+this.title+' at '+
+tr.b.Unit.byName.timeStampInMs.format(this.start);},get stableId(){const parentSliceGroup=this.parentContainer.sliceGroup;return parentSliceGroup.stableId+'.'+
+parentSliceGroup.slices.indexOf(this);},get bindId(){return this.bind_id_;},findDescendentSlice(targetTitle){if(!this.subSlices){return undefined;}
+for(let i=0;i<this.subSlices.length;i++){if(this.subSlices[i].title===targetTitle){return this.subSlices[i];}
+const slice=this.subSlices[i].findDescendentSlice(targetTitle);if(slice)return slice;}
+return undefined;},get mostTopLevelSlice(){if(!this.parentSlice)return this;return this.parentSlice.mostTopLevelSlice;},getProcess(){const thread=this.parentContainer;if(thread&&thread.getProcess){return thread.getProcess();}
+return undefined;},get model(){const process=this.getProcess();if(process!==undefined){return this.getProcess().model;}
+return undefined;},*findTopmostSlicesRelativeToThisSlice(eventPredicate){if(eventPredicate(this)){yield this;return;}
+for(const s of this.subSlices){yield*s.findTopmostSlicesRelativeToThisSlice(eventPredicate);}},iterateAllSubsequentSlices(callback,opt_this){const parentStack=[];let started=false;const topmostSlice=this.mostTopLevelSlice;parentStack.push(topmostSlice);while(parentStack.length!==0){const curSlice=parentStack.pop();if(started){callback.call(opt_this,curSlice);}else{started=(curSlice.guid===this.guid);}
+for(let i=curSlice.subSlices.length-1;i>=0;i--){parentStack.push(curSlice.subSlices[i]);}}},get subsequentSlices(){const res=[];this.iterateAllSubsequentSlices(function(subseqSlice){res.push(subseqSlice);});return res;},*enumerateAllAncestors(){let curSlice=this.parentSlice;while(curSlice){yield curSlice;curSlice=curSlice.parentSlice;}},get ancestorSlices(){return Array.from(this.enumerateAllAncestors());},iterateEntireHierarchy(callback,opt_this){const mostTopLevelSlice=this.mostTopLevelSlice;callback.call(opt_this,mostTopLevelSlice);mostTopLevelSlice.iterateAllSubsequentSlices(callback,opt_this);},get entireHierarchy(){const res=[];this.iterateEntireHierarchy(function(slice){res.push(slice);});return res;},get ancestorAndSubsequentSlices(){const res=[];res.push(this);for(const aSlice of this.enumerateAllAncestors()){res.push(aSlice);}
+this.iterateAllSubsequentSlices(function(sSlice){res.push(sSlice);});return res;},*enumerateAllDescendents(){for(const slice of this.subSlices){yield slice;}
+for(const slice of this.subSlices){yield*slice.enumerateAllDescendents();}},get descendentSlices(){const res=[];for(const slice of this.enumerateAllDescendents()){res.push(slice);}
+return res;}};return{Slice,};});'use strict';tr.exportTo('tr.model',function(){const Slice=tr.model.Slice;const SCHEDULING_STATE={DEBUG:'Debug',EXIT_DEAD:'Exit Dead',RUNNABLE:'Runnable',RUNNING:'Running',SLEEPING:'Sleeping',STOPPED:'Stopped',TASK_DEAD:'Task Dead',UNINTR_SLEEP:'Uninterruptible Sleep',UNINTR_SLEEP_WAKE_KILL:'Uninterruptible Sleep | WakeKill',UNINTR_SLEEP_WAKING:'Uninterruptible Sleep | Waking',UNINTR_SLEEP_IO:'Uninterruptible Sleep - Block I/O',UNINTR_SLEEP_WAKE_KILL_IO:'Uninterruptible Sleep | WakeKill - Block I/O',UNINTR_SLEEP_WAKING_IO:'Uninterruptible Sleep | Waking - Block I/O',UNKNOWN:'UNKNOWN',WAKE_KILL:'Wakekill',WAKING:'Waking',ZOMBIE:'Zombie'};function ThreadTimeSlice(thread,schedulingState,cat,start,args,opt_duration){Slice.call(this,cat,schedulingState,this.getColorForState_(schedulingState),start,args,opt_duration);this.thread=thread;this.schedulingState=schedulingState;this.cpuOnWhichThreadWasRunning=undefined;}
+ThreadTimeSlice.prototype={__proto__:Slice.prototype,getColorForState_(state){const getColorIdForReservedName=tr.b.ColorScheme.getColorIdForReservedName;switch(state){case SCHEDULING_STATE.RUNNABLE:return getColorIdForReservedName('thread_state_runnable');case SCHEDULING_STATE.RUNNING:return getColorIdForReservedName('thread_state_running');case SCHEDULING_STATE.SLEEPING:return getColorIdForReservedName('thread_state_sleeping');case SCHEDULING_STATE.DEBUG:case SCHEDULING_STATE.EXIT_DEAD:case SCHEDULING_STATE.STOPPED:case SCHEDULING_STATE.TASK_DEAD:case SCHEDULING_STATE.UNINTR_SLEEP:case SCHEDULING_STATE.UNINTR_SLEEP_WAKE_KILL:case SCHEDULING_STATE.UNINTR_SLEEP_WAKING:case SCHEDULING_STATE.UNKNOWN:case SCHEDULING_STATE.WAKE_KILL:case SCHEDULING_STATE.WAKING:case SCHEDULING_STATE.ZOMBIE:return getColorIdForReservedName('thread_state_uninterruptible');case SCHEDULING_STATE.UNINTR_SLEEP_IO:case SCHEDULING_STATE.UNINTR_SLEEP_WAKE_KILL_IO:case SCHEDULING_STATE.UNINTR_SLEEP_WAKING_IO:return getColorIdForReservedName('thread_state_iowait');default:return getColorIdForReservedName('thread_state_unknown');}},get analysisTypeName(){return'tr.ui.analysis.ThreadTimeSlice';},getAssociatedCpuSlice(){if(!this.cpuOnWhichThreadWasRunning)return undefined;const cpuSlices=this.cpuOnWhichThreadWasRunning.slices;for(let i=0;i<cpuSlices.length;i++){const cpuSlice=cpuSlices[i];if(cpuSlice.start!==this.start)continue;if(cpuSlice.duration!==this.duration)continue;return cpuSlice;}
+return undefined;},getCpuSliceThatTookCpu(){if(this.cpuOnWhichThreadWasRunning)return undefined;let curIndex=this.thread.indexOfTimeSlice(this);let cpuSliceWhenLastRunning;while(curIndex>=0){const curSlice=this.thread.timeSlices[curIndex];if(!curSlice.cpuOnWhichThreadWasRunning){curIndex--;continue;}
+cpuSliceWhenLastRunning=curSlice.getAssociatedCpuSlice();break;}
+if(!cpuSliceWhenLastRunning)return undefined;const cpu=cpuSliceWhenLastRunning.cpu;const indexOfSliceOnCpuWhenLastRunning=cpu.indexOf(cpuSliceWhenLastRunning);const nextRunningSlice=cpu.slices[indexOfSliceOnCpuWhenLastRunning+1];if(!nextRunningSlice)return undefined;if(Math.abs(nextRunningSlice.start-cpuSliceWhenLastRunning.end)<0.00001){return nextRunningSlice;}
+return undefined;}};tr.model.EventRegistry.register(ThreadTimeSlice,{name:'threadTimeSlice',pluralName:'threadTimeSlices'});return{ThreadTimeSlice,SCHEDULING_STATE,};});'use strict';tr.exportTo('tr.model',function(){const CompoundEventSelectionState={NOT_SELECTED:0,EVENT_SELECTED:0x1,SOME_ASSOCIATED_EVENTS_SELECTED:0x2,ALL_ASSOCIATED_EVENTS_SELECTED:0x4,EVENT_AND_SOME_ASSOCIATED_SELECTED:0x1|0x2,EVENT_AND_ALL_ASSOCIATED_SELECTED:0x1|0x4};return{CompoundEventSelectionState,};});'use strict';tr.exportTo('tr.model.um',function(){const CompoundEventSelectionState=tr.model.CompoundEventSelectionState;function UserExpectation(parentModel,initiatorType,start,duration){tr.model.TimedEvent.call(this,start);this.associatedEvents=new tr.model.EventSet();this.duration=duration;this.initiatorType_=initiatorType;this.parentModel=parentModel;this.typeInfo_=undefined;this.sourceEvents=new tr.model.EventSet();}
+const INITIATOR_TYPE={KEYBOARD:'Keyboard',MOUSE:'Mouse',MOUSE_WHEEL:'MouseWheel',TAP:'Tap',PINCH:'Pinch',FLING:'Fling',TOUCH:'Touch',SCROLL:'Scroll',CSS:'CSS',WEBGL:'WebGL',VIDEO:'Video',VR:'VR',};UserExpectation.prototype={__proto__:tr.model.TimedEvent.prototype,computeCompoundEvenSelectionState(selection){let cess=CompoundEventSelectionState.NOT_SELECTED;if(selection.contains(this)){cess|=CompoundEventSelectionState.EVENT_SELECTED;}
+if(this.associatedEvents.intersectionIsEmpty(selection)){return cess;}
+const allContained=this.associatedEvents.every(function(event){return selection.contains(event);});if(allContained){cess|=CompoundEventSelectionState.ALL_ASSOCIATED_EVENTS_SELECTED;}else{cess|=CompoundEventSelectionState.SOME_ASSOCIATED_EVENTS_SELECTED;}
+return cess;},get associatedSamples(){const samples=new tr.model.EventSet();this.associatedEvents.forEach(function(event){if(event instanceof tr.model.ThreadSlice){samples.addEventSet(event.overlappingSamples);}});return samples;},get userFriendlyName(){return this.title+' User Expectation at '+
+tr.b.Unit.byName.timeStampInMs.format(this.start);},get stableId(){return('UserExpectation.'+this.guid);},get typeInfo(){if(!this.typeInfo_){this.typeInfo_=UserExpectation.subTypes.findTypeInfo(this.constructor);}
+if(!this.typeInfo_){throw new Error('Unregistered UserExpectation');}
+return this.typeInfo_;},get colorId(){return this.typeInfo.metadata.colorId;},get stageTitle(){return this.typeInfo.metadata.stageTitle;},get initiatorType(){return this.initiatorType_;},get title(){if(!this.initiatorType){return this.stageTitle;}
+return this.initiatorType+' '+this.stageTitle;},get totalCpuMs(){let cpuMs=0;this.associatedEvents.forEach(function(event){if(event.cpuSelfTime){cpuMs+=event.cpuSelfTime;}});return cpuMs;}};const subTypes={};const options=new tr.b.ExtensionRegistryOptions(tr.b.BASIC_REGISTRY_MODE);tr.b.decorateExtensionRegistry(subTypes,options);subTypes.addEventListener('will-register',function(e){const metadata=e.typeInfo.metadata;if(metadata.stageTitle===undefined){throw new Error('Registered UserExpectations must provide '+'stageTitle');}
+if(metadata.colorId===undefined){throw new Error('Registered UserExpectations must provide '+'colorId');}});tr.model.EventRegistry.register(UserExpectation,{name:'userExpectation',pluralName:'userExpectations',subTypes});return{UserExpectation,INITIATOR_TYPE,};});'use strict';tr.exportTo('tr.model.um',function(){function ResponseExpectation(parentModel,initiatorTitle,start,duration,opt_isAnimationBegin){tr.model.um.UserExpectation.call(this,parentModel,initiatorTitle,start,duration);this.isAnimationBegin=opt_isAnimationBegin||false;}
+ResponseExpectation.prototype={__proto__:tr.model.um.UserExpectation.prototype,constructor:ResponseExpectation};tr.model.um.UserExpectation.subTypes.register(ResponseExpectation,{stageTitle:'Response',colorId:tr.b.ColorScheme.getColorIdForReservedName('rail_response')});return{ResponseExpectation,};});'use strict';tr.exportTo('tr.e.audits',function(){const SCHEDULING_STATE=tr.model.SCHEDULING_STATE;const Auditor=tr.c.Auditor;const AndroidModelHelper=tr.model.helpers.AndroidModelHelper;const ColorScheme=tr.b.ColorScheme;const Statistics=tr.b.math.Statistics;const FRAME_PERF_CLASS=tr.model.FRAME_PERF_CLASS;const Alert=tr.model.Alert;const EventInfo=tr.model.EventInfo;const Scalar=tr.b.Scalar;const timeDurationInMs=tr.b.Unit.byName.timeDurationInMs;const EXPECTED_FRAME_TIME_MS=16.67;function getStart(e){return e.start;}
+function getDuration(e){return e.duration;}
+function getCpuDuration(e){return(e.cpuDuration!==undefined)?e.cpuDuration:e.duration;}
+function frameIsActivityStart(frame){return frame.associatedEvents.any(x=>x.title==='activityStart');}
+function frameMissedDeadline(frame){return frame.args.deadline&&frame.args.deadline<frame.end;}
+function DocLinkBuilder(){this.docLinks=[];}
+DocLinkBuilder.prototype={addAppVideo(name,videoId){this.docLinks.push({label:'Video Link',textContent:('Android Performance Patterns: '+name),href:'https://www.youtube.com/watch?list=PLWz5rJ2EKKc9CBxr3BVjPTPoDPLdPIFCE&v='+videoId});return this;},addDacRef(name,link){this.docLinks.push({label:'Doc Link',textContent:(name+' documentation'),href:'https://developer.android.com/reference/'+link});return this;},build(){return this.docLinks;}};function AndroidAuditor(model){Auditor.call(this,model);const helper=model.getOrCreateHelper(AndroidModelHelper);if(helper.apps.length||helper.surfaceFlinger){this.helper=helper;}}
+AndroidAuditor.viewAlphaAlertInfo_=new EventInfo('Inefficient View alpha usage','Setting an alpha between 0 and 1 has significant performance costs, if one of the fast alpha paths is not used.',new DocLinkBuilder().addAppVideo('Hidden Cost of Transparency','wIy8g8yNhNk').addDacRef('View#setAlpha()','android/view/View.html#setAlpha(float)').build());AndroidAuditor.saveLayerAlertInfo_=new EventInfo('Expensive rendering with Canvas#saveLayer()','Canvas#saveLayer() incurs extremely high rendering cost. They disrupt the rendering pipeline when drawn, forcing a flush of drawing content. Instead use View hardware layers, or static Bitmaps. This enables the offscreen buffers to be reused in between frames, and avoids the disruptive render target switch.',new DocLinkBuilder().addAppVideo('Hidden Cost of Transparency','wIy8g8yNhNk').addDacRef('Canvas#saveLayerAlpha()','android/graphics/Canvas.html#saveLayerAlpha(android.graphics.RectF, int, int)').build());AndroidAuditor.getSaveLayerAlerts_=function(frame){const badAlphaRegEx=/^(.+) alpha caused (unclipped )?saveLayer (\d+)x(\d+)$/;const saveLayerRegEx=/^(unclipped )?saveLayer (\d+)x(\d+)$/;const ret=[];const events=[];frame.associatedEvents.forEach(function(slice){const match=badAlphaRegEx.exec(slice.title);if(match){const args={'view name':match[1],'width':parseInt(match[3]),'height':parseInt(match[4])};ret.push(new Alert(AndroidAuditor.viewAlphaAlertInfo_,slice.start,[slice],args));}else if(saveLayerRegEx.test(slice.title)){events.push(slice);}},this);if(events.length>ret.length){const unclippedSeen=Statistics.sum(events,function(slice){return saveLayerRegEx.exec(slice.title)[1]?1:0;});const clippedSeen=events.length-unclippedSeen;const earliestStart=Statistics.min(events,function(slice){return slice.start;});const args={'Unclipped saveLayer count (especially bad!)':unclippedSeen,'Clipped saveLayer count':clippedSeen};events.push(frame);ret.push(new Alert(AndroidAuditor.saveLayerAlertInfo_,earliestStart,events,args));}
+return ret;};AndroidAuditor.pathAlertInfo_=new EventInfo('Path texture churn','Paths are drawn with a mask texture, so when a path is modified / newly drawn, that texture must be generated and uploaded to the GPU. Ensure that you cache paths between frames and do not unnecessarily call Path#reset(). You can cut down on this cost by sharing Path object instances between drawables/views.');AndroidAuditor.getPathAlert_=function(frame){const uploadRegEx=/^Generate Path Texture$/;const events=frame.associatedEvents.filter(function(event){return event.title==='Generate Path Texture';});const start=Statistics.min(events,getStart);const duration=Statistics.sum(events,getDuration);if(duration<3)return undefined;events.push(frame);return new Alert(AndroidAuditor.pathAlertInfo_,start,events,{'Time spent':new Scalar(timeDurationInMs,duration)});};AndroidAuditor.uploadAlertInfo_=new EventInfo('Expensive Bitmap uploads','Bitmaps that have been modified / newly drawn must be uploaded to the GPU. Since this is expensive if the total number of pixels uploaded is large, reduce the amount of Bitmap churn in this animation/context, per frame.');AndroidAuditor.getUploadAlert_=function(frame){const uploadRegEx=/^Upload (\d+)x(\d+) Texture$/;const events=[];let start=Number.POSITIVE_INFINITY;let duration=0;let pixelsUploaded=0;frame.associatedEvents.forEach(function(event){const match=uploadRegEx.exec(event.title);if(match){events.push(event);start=Math.min(start,event.start);duration+=event.duration;pixelsUploaded+=parseInt(match[1])*parseInt(match[2]);}});if(events.length===0||duration<3)return undefined;const mPixels=(pixelsUploaded/1000000).toFixed(2)+' million';const args={'Pixels uploaded':mPixels,'Time spent':new Scalar(timeDurationInMs,duration)};events.push(frame);return new Alert(AndroidAuditor.uploadAlertInfo_,start,events,args);};AndroidAuditor.ListViewInflateAlertInfo_=new EventInfo('Inflation during ListView recycling','ListView item recycling involved inflating views. Ensure your Adapter#getView() recycles the incoming View, instead of constructing a new one.');AndroidAuditor.ListViewBindAlertInfo_=new EventInfo('Inefficient ListView recycling/rebinding','ListView recycling taking too much time per frame. Ensure your Adapter#getView() binds data efficiently.');AndroidAuditor.getListViewAlert_=function(frame){const events=frame.associatedEvents.filter(function(event){return event.title==='obtainView'||event.title==='setupListItem';});const duration=Statistics.sum(events,getCpuDuration);if(events.length===0||duration<3)return undefined;let hasInflation=false;for(const event of events){if(event.findDescendentSlice('inflate')){hasInflation=true;}}
+const start=Statistics.min(events,getStart);const args={'Time spent':new Scalar(timeDurationInMs,duration)};args['ListView items '+(hasInflation?'inflated':'rebound')]=events.length/2;const eventInfo=hasInflation?AndroidAuditor.ListViewInflateAlertInfo_:AndroidAuditor.ListViewBindAlertInfo_;events.push(frame);return new Alert(eventInfo,start,events,args);};AndroidAuditor.measureLayoutAlertInfo_=new EventInfo('Expensive measure/layout pass','Measure/Layout took a significant time, contributing to jank. Avoid triggering layout during animations.',new DocLinkBuilder().addAppVideo('Invalidations, Layouts, and Performance','we6poP0kw6E').build());AndroidAuditor.getMeasureLayoutAlert_=function(frame){const events=frame.associatedEvents.filter(function(event){return event.title==='measure'||event.title==='layout';});const duration=Statistics.sum(events,getCpuDuration);if(events.length===0||duration<3)return undefined;const start=Statistics.min(events,getStart);events.push(frame);return new Alert(AndroidAuditor.measureLayoutAlertInfo_,start,events,{'Time spent':new Scalar(timeDurationInMs,duration)});};AndroidAuditor.viewDrawAlertInfo_=new EventInfo('Long View#draw()','Recording the drawing commands of invalidated Views took a long time. Avoid significant work in View or Drawable custom drawing, especially allocations or drawing to Bitmaps.',new DocLinkBuilder().addAppVideo('Invalidations, Layouts, and Performance','we6poP0kw6E').addAppVideo('Avoiding Allocations in onDraw()','HAK5acHQ53E').build());AndroidAuditor.getViewDrawAlert_=function(frame){let slice=undefined;for(const event of frame.associatedEvents){if(event.title==='getDisplayList'||event.title==='Record View#draw()'){slice=event;break;}}
+if(!slice||getCpuDuration(slice)<3)return undefined;return new Alert(AndroidAuditor.viewDrawAlertInfo_,slice.start,[slice,frame],{'Time spent':new Scalar(timeDurationInMs,getCpuDuration(slice))});};AndroidAuditor.blockingGcAlertInfo_=new EventInfo('Blocking Garbage Collection','Blocking GCs are caused by object churn, and made worse by having large numbers of objects in the heap. Avoid allocating objects during animations/scrolling, and recycle Bitmaps to avoid triggering garbage collection.',new DocLinkBuilder().addAppVideo('Garbage Collection in Android','pzfzz50W5Uo').addAppVideo('Avoiding Allocations in onDraw()','HAK5acHQ53E').build());AndroidAuditor.getBlockingGcAlert_=function(frame){const events=frame.associatedEvents.filter(function(event){return event.title==='DVM Suspend'||event.title==='GC: Wait For Concurrent';});const blockedDuration=Statistics.sum(events,getDuration);if(blockedDuration<3)return undefined;const start=Statistics.min(events,getStart);events.push(frame);return new Alert(AndroidAuditor.blockingGcAlertInfo_,start,events,{'Blocked duration':new Scalar(timeDurationInMs,blockedDuration)});};AndroidAuditor.lockContentionAlertInfo_=new EventInfo('Lock contention','UI thread lock contention is caused when another thread holds a lock that the UI thread is trying to use. UI thread progress is blocked until the lock is released. Inspect locking done within the UI thread, and ensure critical sections are short.');AndroidAuditor.getLockContentionAlert_=function(frame){const events=frame.associatedEvents.filter(function(event){return/^Lock Contention on /.test(event.title);});const blockedDuration=Statistics.sum(events,getDuration);if(blockedDuration<1)return undefined;const start=Statistics.min(events,getStart);events.push(frame);return new Alert(AndroidAuditor.lockContentionAlertInfo_,start,events,{'Blocked duration':new Scalar(timeDurationInMs,blockedDuration)});};AndroidAuditor.schedulingAlertInfo_=new EventInfo('Scheduling delay','Work to produce this frame was descheduled for several milliseconds, contributing to jank. Ensure that code on the UI thread doesn\'t block on work being done on other threads, and that background threads (doing e.g. network or bitmap loading) are running at android.os.Process#THREAD_PRIORITY_BACKGROUND or lower so they are less likely to interrupt the UI thread. These background threads should show up with a priority number of 130 or higher in the scheduling section under the Kernel process.');AndroidAuditor.getSchedulingAlert_=function(frame){let totalDuration=0;const totalStats={};for(const ttr of frame.threadTimeRanges){const stats=ttr.thread.getSchedulingStatsForRange(ttr.start,ttr.end);for(const[key,value]of Object.entries(stats)){if(!(key in totalStats)){totalStats[key]=0;}
+totalStats[key]+=value;totalDuration+=value;}}
+if(!(SCHEDULING_STATE.RUNNING in totalStats)||totalDuration===0||totalDuration-totalStats[SCHEDULING_STATE.RUNNING]<3){return;}
+const args={};for(const[key,value]of Object.entries(totalStats)){let newKey=key;if(key===SCHEDULING_STATE.RUNNABLE){newKey='Not scheduled, but runnable';}else if(key===SCHEDULING_STATE.UNINTR_SLEEP){newKey='Blocking I/O delay';}
+args[newKey]=new Scalar(timeDurationInMs,value);}
+return new Alert(AndroidAuditor.schedulingAlertInfo_,frame.start,[frame],args);};AndroidAuditor.prototype={__proto__:Auditor.prototype,renameAndSort_(){this.model.kernel.important=false;this.model.getAllProcesses().forEach(function(process){if(this.helper.surfaceFlinger&&process===this.helper.surfaceFlinger.process){if(!process.name){process.name='SurfaceFlinger';}
+process.sortIndex=Number.NEGATIVE_INFINITY;process.important=false;return;}
+const uiThread=process.getThread(process.pid);if(!process.name&&uiThread&&uiThread.name){if(/^ndroid\./.test(uiThread.name)){uiThread.name='a'+uiThread.name;}
+process.name=uiThread.name;uiThread.name='UI Thread';}
+process.sortIndex=0;for(const tid in process.threads){process.sortIndex-=process.threads[tid].sliceGroup.slices.length;}},this);this.model.getAllThreads().forEach(function(thread){if(thread.tid===thread.parent.pid){thread.sortIndex=-3;}
+if(thread.name==='RenderThread'){thread.sortIndex=-2;}
+if(/^hwuiTask/.test(thread.name)){thread.sortIndex=-1;}});},pushFramesAndJudgeJank_(){let badFramesObserved=0;let framesObserved=0;const surfaceFlinger=this.helper.surfaceFlinger;this.helper.apps.forEach(function(app){app.process.frames=app.getFrames();app.process.frames.forEach(function(frame){if(frame.totalDuration>EXPECTED_FRAME_TIME_MS*2){badFramesObserved+=2;frame.perfClass=FRAME_PERF_CLASS.TERRIBLE;}else if(frame.totalDuration>EXPECTED_FRAME_TIME_MS||frameMissedDeadline(frame)){badFramesObserved++;frame.perfClass=FRAME_PERF_CLASS.BAD;}else{frame.perfClass=FRAME_PERF_CLASS.GOOD;}});framesObserved+=app.process.frames.length;});if(framesObserved){const portionBad=badFramesObserved/framesObserved;if(portionBad>0.3){this.model.faviconHue='red';}else if(portionBad>0.05){this.model.faviconHue='yellow';}else{this.model.faviconHue='green';}}},pushEventInfo_(){const appAnnotator=new AppAnnotator();this.helper.apps.forEach(function(app){if(app.uiThread){appAnnotator.applyEventInfos(app.uiThread.sliceGroup);}
+if(app.renderThread){appAnnotator.applyEventInfos(app.renderThread.sliceGroup);}});},runAnnotate(){if(!this.helper)return;this.renameAndSort_();this.pushFramesAndJudgeJank_();this.pushEventInfo_();this.helper.iterateImportantSlices(function(slice){slice.important=true;});},runAudit(){if(!this.helper)return;const alerts=this.model.alerts;this.helper.apps.forEach(function(app){app.getFrames().forEach(function(frame){alerts.push.apply(alerts,AndroidAuditor.getSaveLayerAlerts_(frame));if(frame.perfClass===FRAME_PERF_CLASS.NEUTRAL||frame.perfClass===FRAME_PERF_CLASS.GOOD){return;}
+let alert=AndroidAuditor.getPathAlert_(frame);if(alert)alerts.push(alert);alert=AndroidAuditor.getUploadAlert_(frame);if(alert)alerts.push(alert);alert=AndroidAuditor.getListViewAlert_(frame);if(alert)alerts.push(alert);alert=AndroidAuditor.getMeasureLayoutAlert_(frame);if(alert)alerts.push(alert);alert=AndroidAuditor.getViewDrawAlert_(frame);if(alert)alerts.push(alert);alert=AndroidAuditor.getBlockingGcAlert_(frame);if(alert)alerts.push(alert);alert=AndroidAuditor.getLockContentionAlert_(frame);if(alert)alerts.push(alert);alert=AndroidAuditor.getSchedulingAlert_(frame);if(alert)alerts.push(alert);});},this);this.addRenderingInteractionRecords();this.addInputInteractionRecords();},addRenderingInteractionRecords(){const events=[];this.helper.apps.forEach(function(app){events.push.apply(events,app.getAnimationAsyncSlices());events.push.apply(events,app.getFrames());});const mergerFunction=function(events){const ir=new tr.model.um.ResponseExpectation(this.model,'Rendering',events[0].min,events[events.length-1].max-events[0].min);this.model.userModel.expectations.push(ir);}.bind(this);tr.b.math.mergeRanges(tr.b.math.convertEventsToRanges(events),30,mergerFunction);},addInputInteractionRecords(){const inputSamples=[];this.helper.apps.forEach(function(app){inputSamples.push.apply(inputSamples,app.getInputSamples());});const mergerFunction=function(events){const ir=new tr.model.um.ResponseExpectation(this.model,'Input',events[0].min,events[events.length-1].max-events[0].min);this.model.userModel.expectations.push(ir);}.bind(this);const inputRanges=inputSamples.map(function(sample){return tr.b.math.Range.fromExplicitRange(sample.timestamp,sample.timestamp);});tr.b.math.mergeRanges(inputRanges,30,mergerFunction);}};Auditor.register(AndroidAuditor);function AppAnnotator(){this.titleInfoLookup=new Map();this.titleParentLookup=new Map();this.build_();}
+AppAnnotator.prototype={build_(){const registerEventInfo=function(dict){this.titleInfoLookup.set(dict.title,new EventInfo(dict.title,dict.description,dict.docLinks));if(dict.parents){this.titleParentLookup.set(dict.title,dict.parents);}}.bind(this);registerEventInfo({title:'inflate',description:'Constructing a View hierarchy from pre-processed XML via LayoutInflater#layout. This includes constructing all of the View objects in the hierarchy, and applying styled attributes.'});registerEventInfo({title:'obtainView',description:'Adapter#getView() called to bind content to a recycled View that is being presented.'});registerEventInfo({title:'setupListItem',description:'Attached a newly-bound, recycled View to its parent ListView.'});registerEventInfo({title:'setupGridItem',description:'Attached a newly-bound, recycled View to its parent GridView.'});const choreographerLinks=new DocLinkBuilder().addDacRef('Choreographer','android/view/Choreographer.html').build();registerEventInfo({title:'Choreographer#doFrame',docLinks:choreographerLinks,description:'Choreographer executes frame callbacks for inputs, animations, and rendering traversals. When this work is done, a frame will be presented to the user.'});registerEventInfo({title:'input',parents:['Choreographer#doFrame'],docLinks:choreographerLinks,description:'Input callbacks are processed. This generally encompasses dispatching input to Views, as well as any work the Views do to process this input/gesture.'});registerEventInfo({title:'animation',parents:['Choreographer#doFrame'],docLinks:choreographerLinks,description:'Animation callbacks are processed. This is generally minimal work, as animations determine progress for the frame, and push new state to animated objects (such as setting View properties).'});registerEventInfo({title:'traversals',parents:['Choreographer#doFrame'],docLinks:choreographerLinks,description:'Primary draw traversals. This is the primary traversal of the View hierarchy, including layout and draw passes.'});const traversalParents=['Choreographer#doFrame','performTraversals'];const layoutLinks=new DocLinkBuilder().addDacRef('View#Layout','android/view/View.html#Layout').build();registerEventInfo({title:'performTraversals',description:'A drawing traversal of the View hierarchy, comprised of all layout and drawing needed to produce the frame.'});registerEventInfo({title:'measure',parents:traversalParents,docLinks:layoutLinks,description:'First of two phases in view hierarchy layout. Views are asked to size themselves according to constraints supplied by their parent. Some ViewGroups may measure a child more than once to help satisfy their own constraints. Nesting ViewGroups that measure children more than once can lead to excessive and repeated work.'});registerEventInfo({title:'layout',parents:traversalParents,docLinks:layoutLinks,description:'Second of two phases in view hierarchy layout, repositioning content and child Views into their new locations.'});const drawString='Draw pass over the View hierarchy. Every invalidated View will have its drawing commands recorded. On Android versions prior to Lollipop, this would also include the issuing of draw commands to the GPU. Starting with Lollipop, it only includes the recording of commands, and syncing that information to the RenderThread.';registerEventInfo({title:'draw',parents:traversalParents,description:drawString});const recordString='Every invalidated View\'s drawing commands are recorded. Each will have View#draw() called, and is passed a Canvas that will record and store its drawing commands until it is next invalidated/rerecorded.';registerEventInfo({title:'getDisplayList',parents:['draw'],description:recordString});registerEventInfo({title:'Record View#draw()',parents:['draw'],description:recordString});registerEventInfo({title:'drawDisplayList',parents:['draw'],description:'Execution of recorded draw commands to generate a frame. This represents the actual formation and issuing of drawing commands to the GPU. On Android L and higher devices, this work is done on a dedicated RenderThread, instead of on the UI Thread.'});registerEventInfo({title:'DrawFrame',description:'RenderThread portion of the standard UI/RenderThread split frame. This represents the actual formation and issuing of drawing commands to the GPU.'});registerEventInfo({title:'doFrame',description:'RenderThread animation frame. Represents drawing work done by the RenderThread on a frame where the UI thread did not produce new drawing content.'});registerEventInfo({title:'syncFrameState',description:'Sync stage between the UI thread and the RenderThread, where the UI thread hands off a frame (including information about modified Views). Time in this method primarily consists of uploading modified Bitmaps to the GPU. After this sync is completed, the UI thread is unblocked, and the RenderThread starts to render the frame.'});registerEventInfo({title:'flush drawing commands',description:'Issuing the now complete drawing commands to the GPU.'});registerEventInfo({title:'eglSwapBuffers',description:'Complete GPU rendering of the frame.'});registerEventInfo({title:'RV Scroll',description:'RecyclerView is calculating a scroll. If there are too many of these in Systrace, some Views inside RecyclerView might be causing it. Try to avoid using EditText, focusable views or handle them with care.'});registerEventInfo({title:'RV OnLayout',description:'OnLayout has been called by the View system. If this shows up too many times in Systrace, make sure the children of RecyclerView do not update themselves directly. This will cause a full re-layout but when it happens via the Adapter notifyItemChanged, RecyclerView can avoid full layout calculation.'});registerEventInfo({title:'RV FullInvalidate',description:'NotifyDataSetChanged or equal has been called. If this is taking a long time, try sending granular notify adapter changes instead of just calling notifyDataSetChanged or setAdapter / swapAdapter. Adding stable ids to your adapter might help.'});registerEventInfo({title:'RV PartialInvalidate',description:'RecyclerView is rebinding a View. If this is taking a lot of time, consider optimizing your layout or make sure you are not doing extra operations in onBindViewHolder call.'});registerEventInfo({title:'RV OnBindView',description:'RecyclerView is rebinding a View. If this is taking a lot of time, consider optimizing your layout or make sure you are not doing extra operations in onBindViewHolder call.'});registerEventInfo({title:'RV CreateView',description:'RecyclerView is creating a new View. If too many of these are present: 1) There might be a problem in Recycling (e.g. custom Animations that set transient state and prevent recycling or ItemAnimator not implementing the contract properly. See Adapter#onFailedToRecycleView(ViewHolder). 2) There may be too many item view types. Try merging them. 3) There might be too many itemChange animations and not enough space in RecyclerPool. Try increasing your pool size and item cache size.'});registerEventInfo({title:'eglSwapBuffers',description:'The CPU has finished producing drawing commands, and is flushing drawing work to the GPU, and posting that buffer to the consumer (which is often SurfaceFlinger window composition). Once this is completed, the GPU can produce the frame content without any involvement from the CPU.'});},applyEventInfosRecursive_(parentNames,slice){const checkExpectedParentNames=function(expectedParentNames){if(!expectedParentNames)return true;return expectedParentNames.some(function(name){return parentNames.has(name);});};if(this.titleInfoLookup.has(slice.title)){if(checkExpectedParentNames(this.titleParentLookup.get(slice.title))){slice.info=this.titleInfoLookup.get(slice.title);}}
+if(slice.subSlices.length>0){if(!parentNames.has(slice.title)){parentNames.set(slice.title,0);}
+parentNames.set(slice.title,parentNames.get(slice.title)+1);slice.subSlices.forEach(function(subSlice){this.applyEventInfosRecursive_(parentNames,subSlice);},this);parentNames.set(slice.title,parentNames.get(slice.title)-1);if(parentNames.get(slice.title)===0){delete parentNames[slice.title];}}},applyEventInfos(sliceGroup){sliceGroup.topLevelSlices.forEach(function(slice){this.applyEventInfosRecursive_(new Map(),slice);},this);}};return{AndroidAuditor,};});'use strict';tr.exportTo('tr.model',function(){function ObjectSnapshot(objectInstance,ts,args){tr.model.Event.call(this);this.objectInstance=objectInstance;this.ts=ts;this.args=args;}
+ObjectSnapshot.prototype={__proto__:tr.model.Event.prototype,preInitialize(){},initialize(){},referencedAt(item,object,field){},addBoundsToRange(range){range.addValue(this.ts);},get userFriendlyName(){return'Snapshot of '+this.objectInstance.userFriendlyName+' @ '+
+tr.b.Unit.byName.timeStampInMs.format(this.ts);}};tr.model.EventRegistry.register(ObjectSnapshot,{name:'objectSnapshot',pluralName:'objectSnapshots'});return{ObjectSnapshot,};});'use strict';tr.exportTo('tr.model',function(){const ObjectSnapshot=tr.model.ObjectSnapshot;function ObjectInstance(parent,scopedId,category,name,creationTs,opt_baseTypeName){tr.model.Event.call(this);this.parent=parent;this.scopedId=scopedId;this.category=category;this.baseTypeName=opt_baseTypeName?opt_baseTypeName:name;this.name=name;this.creationTs=creationTs;this.creationTsWasExplicit=false;this.deletionTs=Number.MAX_VALUE;this.deletionTsWasExplicit=false;this.colorId=0;this.bounds=new tr.b.math.Range();this.snapshots=[];this.hasImplicitSnapshots=false;}
+ObjectInstance.prototype={__proto__:tr.model.Event.prototype,get typeName(){return this.name;},addBoundsToRange(range){range.addRange(this.bounds);},addSnapshot(ts,args,opt_name,opt_baseTypeName){if(ts<this.creationTs){throw new Error('Snapshots must be >= instance.creationTs');}
+if(ts>=this.deletionTs){throw new Error('Snapshots cannot be added after '+'an objects deletion timestamp.');}
+let lastSnapshot;if(this.snapshots.length>0){lastSnapshot=this.snapshots[this.snapshots.length-1];if(lastSnapshot.ts===ts){throw new Error('Snapshots already exists at this time!');}
+if(ts<lastSnapshot.ts){throw new Error('Snapshots must be added in increasing timestamp order');}}
+if(opt_name&&(this.name!==opt_name)){if(!opt_baseTypeName){throw new Error('Must provide base type name for name update');}
+if(this.baseTypeName!==opt_baseTypeName){throw new Error('Cannot update type name: base types dont match');}
+this.name=opt_name;}
+const snapshotConstructor=tr.model.ObjectSnapshot.subTypes.getConstructor(this.category,this.name);const snapshot=new snapshotConstructor(this,ts,args);this.snapshots.push(snapshot);return snapshot;},wasDeleted(ts){let lastSnapshot;if(this.snapshots.length>0){lastSnapshot=this.snapshots[this.snapshots.length-1];if(lastSnapshot.ts>ts){throw new Error('Instance cannot be deleted at ts='+
+ts+'. A snapshot exists that is older.');}}
+this.deletionTs=ts;this.deletionTsWasExplicit=true;},preInitialize(){for(let i=0;i<this.snapshots.length;i++){this.snapshots[i].preInitialize();}},initialize(){for(let i=0;i<this.snapshots.length;i++){this.snapshots[i].initialize();}},isAliveAt(ts){if(ts<this.creationTs&&this.creationTsWasExplicit){return false;}
+if(ts>this.deletionTs){return false;}
+return true;},getSnapshotAt(ts){if(ts<this.creationTs){if(this.creationTsWasExplicit){throw new Error('ts must be within lifetime of this instance');}
+return this.snapshots[0];}
+if(ts>this.deletionTs){throw new Error('ts must be within lifetime of this instance');}
+const snapshots=this.snapshots;const i=tr.b.findIndexInSortedIntervals(snapshots,function(snapshot){return snapshot.ts;},function(snapshot,i){if(i===snapshots.length-1){return snapshots[i].objectInstance.deletionTs;}
+return snapshots[i+1].ts-snapshots[i].ts;},ts);if(i<0){return this.snapshots[0];}
+if(i>=this.snapshots.length){return this.snapshots[this.snapshots.length-1];}
+return this.snapshots[i];},updateBounds(){this.bounds.reset();this.bounds.addValue(this.creationTs);if(this.deletionTs!==Number.MAX_VALUE){this.bounds.addValue(this.deletionTs);}else if(this.snapshots.length>0){this.bounds.addValue(this.snapshots[this.snapshots.length-1].ts);}},shiftTimestampsForward(amount){this.creationTs+=amount;if(this.deletionTs!==Number.MAX_VALUE){this.deletionTs+=amount;}
+this.snapshots.forEach(function(snapshot){snapshot.ts+=amount;});},get userFriendlyName(){return this.typeName+' object '+this.scopedId;}};tr.model.EventRegistry.register(ObjectInstance,{name:'objectInstance',pluralName:'objectInstances'});return{ObjectInstance,};});'use strict';tr.exportTo('tr.e.chrome',function(){const ObjectSnapshot=tr.model.ObjectSnapshot;const ObjectInstance=tr.model.ObjectInstance;function BlameContextSnapshot(){ObjectSnapshot.apply(this,arguments);}
+BlameContextSnapshot.prototype={__proto__:ObjectSnapshot.prototype,get parentContext(){if(this.args.parent instanceof BlameContextSnapshot){return this.args.parent;}
+return undefined;},get userFriendlyName(){return'BlameContext';}};function BlameContextInstance(){ObjectInstance.apply(this,arguments);}
+BlameContextInstance.prototype={__proto__:ObjectInstance.prototype,get blameContextType(){throw new Error('Not implemented');}};return{BlameContextSnapshot,BlameContextInstance,};});'use strict';tr.exportTo('tr.e.chrome',function(){const BlameContextSnapshot=tr.e.chrome.BlameContextSnapshot;const BlameContextInstance=tr.e.chrome.BlameContextInstance;function FrameTreeNodeSnapshot(){BlameContextSnapshot.apply(this,arguments);}
+FrameTreeNodeSnapshot.prototype={__proto__:BlameContextSnapshot.prototype,get renderFrame(){if(this.args.renderFrame instanceof tr.e.chrome.RenderFrameSnapshot){return this.args.renderFrame;}
+return undefined;},get url(){return this.args.url;},get userFriendlyName(){return'FrameTreeNode';}};tr.model.ObjectSnapshot.subTypes.register(FrameTreeNodeSnapshot,{typeName:'FrameTreeNode'});function FrameTreeNodeInstance(){BlameContextInstance.apply(this,arguments);}
+FrameTreeNodeInstance.prototype={__proto__:BlameContextInstance.prototype,get blameContextType(){return'Frame';}};tr.model.ObjectInstance.subTypes.register(FrameTreeNodeInstance,{typeName:'FrameTreeNode'});return{FrameTreeNodeSnapshot,FrameTreeNodeInstance,};});'use strict';tr.exportTo('tr.e.chrome',function(){const BlameContextSnapshot=tr.e.chrome.BlameContextSnapshot;const BlameContextInstance=tr.e.chrome.BlameContextInstance;function RenderFrameSnapshot(){BlameContextSnapshot.apply(this,arguments);}
+RenderFrameSnapshot.prototype={__proto__:BlameContextSnapshot.prototype,referencedAt(item,object,field){if(item instanceof tr.e.chrome.FrameTreeNodeSnapshot&&object===item.args&&field==='renderFrame'){this.args.frameTreeNode=item;}},get frameTreeNode(){if(this.args.frameTreeNode instanceof
+tr.e.chrome.FrameTreeNodeSnapshot){return this.args.frameTreeNode;}
+return undefined;},get url(){if(this.frameTreeNode){return this.frameTreeNode.url;}
+return undefined;},get userFriendlyName(){return'RenderFrame';}};tr.model.ObjectSnapshot.subTypes.register(RenderFrameSnapshot,{typeName:'RenderFrame'});function RenderFrameInstance(){BlameContextInstance.apply(this,arguments);}
+RenderFrameInstance.prototype={__proto__:BlameContextInstance.prototype,get blameContextType(){return'Frame';}};tr.model.ObjectInstance.subTypes.register(RenderFrameInstance,{typeName:'RenderFrame'});return{RenderFrameSnapshot,RenderFrameInstance,};});'use strict';tr.exportTo('tr.e.chrome',function(){const BlameContextSnapshot=tr.e.chrome.BlameContextSnapshot;const BlameContextInstance=tr.e.chrome.BlameContextInstance;function TopLevelSnapshot(){BlameContextSnapshot.apply(this,arguments);}
+TopLevelSnapshot.prototype={__proto__:BlameContextSnapshot.prototype,get userFriendlyName(){return'TopLevel';}};tr.model.ObjectSnapshot.subTypes.register(TopLevelSnapshot,{typeName:'TopLevel'});function TopLevelInstance(){BlameContextInstance.apply(this,arguments);}
+TopLevelInstance.prototype={__proto__:BlameContextInstance.prototype,get blameContextType(){return'TopLevel';}};tr.model.ObjectInstance.subTypes.register(TopLevelInstance,{typeName:'TopLevel'});return{TopLevelSnapshot,TopLevelInstance,};});'use strict';tr.exportTo('tr.model',function(){function AsyncSlice(category,title,colorId,start,args,duration,opt_isTopLevel,opt_cpuStart,opt_cpuDuration,opt_argsStripped){tr.model.TimedEvent.call(this,start);this.category=category||'';this.originalTitle=title;this.title=title;this.colorId=colorId;this.args=args;this.startStackFrame=undefined;this.endStackFrame=undefined;this.didNotFinish=false;this.important=false;this.subSlices=[];this.parentContainer_=undefined;this.id=undefined;this.startThread=undefined;this.endThread=undefined;this.cpuStart=undefined;this.cpuDuration=undefined;this.argsStripped=false;this.startStackFrame=undefined;this.endStackFrame=undefined;this.duration=duration;this.isTopLevel=(opt_isTopLevel===true);if(opt_cpuStart!==undefined){this.cpuStart=opt_cpuStart;}
+if(opt_cpuDuration!==undefined){this.cpuDuration=opt_cpuDuration;}
+if(opt_argsStripped!==undefined){this.argsStripped=opt_argsStripped;}}
+AsyncSlice.prototype={__proto__:tr.model.TimedEvent.prototype,get analysisTypeName(){return this.title;},get parentContainer(){return this.parentContainer_;},set parentContainer(parentContainer){this.parentContainer_=parentContainer;for(let i=0;i<this.subSlices.length;i++){const subSlice=this.subSlices[i];if(subSlice.parentContainer===undefined){subSlice.parentContainer=parentContainer;}}},get viewSubGroupTitle(){return this.title;},get viewSubGroupGroupingKey(){return undefined;},get userFriendlyName(){return'Async slice '+this.title+' at '+
+tr.b.Unit.byName.timeStampInMs.format(this.start);},get stableId(){const parentAsyncSliceGroup=this.parentContainer.asyncSliceGroup;return parentAsyncSliceGroup.stableId+'.'+
+parentAsyncSliceGroup.slices.indexOf(this);},*findTopmostSlicesRelativeToThisSlice(eventPredicate,opt_this){if(eventPredicate(this)){yield this;return;}
+for(const s of this.subSlices){yield*s.findTopmostSlicesRelativeToThisSlice(eventPredicate);}},findDescendentSlice(targetTitle){if(!this.subSlices)return undefined;for(let i=0;i<this.subSlices.length;i++){if(this.subSlices[i].title===targetTitle){return this.subSlices[i];}
+const slice=this.subSlices[i].findDescendentSlice(targetTitle);if(slice)return slice;}
+return undefined;},*enumerateAllDescendents(){for(const slice of this.subSlices){yield slice;}
+for(const slice of this.subSlices){if(slice.enumerateAllDescendents!==undefined){yield*slice.enumerateAllDescendents();}}},compareTo(that){return this.title.localeCompare(that.title);}};tr.model.EventRegistry.register(AsyncSlice,{name:'asyncSlice',pluralName:'asyncSlices'});return{AsyncSlice,};});'use strict';tr.exportTo('tr.e.blink',function(){class BlinkSchedulerAsyncSlice extends tr.model.AsyncSlice{get viewSubGroupGroupingKey(){if(this.title.startsWith('FrameScheduler.')){return'Frame'+this.id;}
+if(this.title.startsWith('Scheduler.')){return'Renderer Scheduler';}
+return undefined;}
+get viewSubGroupTitle(){if(this.title.startsWith('FrameScheduler.')){return this.title.substring(15);}
+if(this.title.startsWith('Scheduler.')){return this.title.substring(10);}
+return this.title;}}
+tr.model.AsyncSlice.subTypes.register(BlinkSchedulerAsyncSlice,{categoryParts:['renderer.scheduler','disabled-by-default-renderer.scheduler','disabled-by-default-renderer.scheduler.debug',]});return{BlinkSchedulerAsyncSlice,};});'use strict';tr.exportTo('tr.model.helpers',function(){const MAIN_FRAMETIME_TYPE='main_frametime_type';const IMPL_FRAMETIME_TYPE='impl_frametime_type';const MAIN_RENDERING_STATS='BenchmarkInstrumentation::MainThreadRenderingStats';const IMPL_RENDERING_STATS='BenchmarkInstrumentation::ImplThreadRenderingStats';function getSlicesIntersectingRange(rangeOfInterest,slices){const slicesInFilterRange=[];for(let i=0;i<slices.length;i++){const slice=slices[i];if(rangeOfInterest.intersectsExplicitRangeInclusive(slice.start,slice.end)){slicesInFilterRange.push(slice);}}
+return slicesInFilterRange;}
+function ChromeProcessHelper(modelHelper,process){this.modelHelper=modelHelper;this.process=process;this.telemetryInternalRanges_=undefined;}
+ChromeProcessHelper.prototype={get pid(){return this.process.pid;},isTelemetryInternalEvent(slice){if(this.telemetryInternalRanges_===undefined){this.findTelemetryInternalRanges_();}
+for(const range of this.telemetryInternalRanges_){if(range.containsExplicitRangeInclusive(slice.start,slice.end)){return true;}}
+return false;},findTelemetryInternalRanges_(){this.telemetryInternalRanges_=[];let start=0;for(const thread of Object.values(this.process.threads)){for(const slice of thread.asyncSliceGroup.getDescendantEvents()){if(/^telemetry\.internal\..*\.start$/.test(slice.title)){start=slice.start;}else if(/^telemetry\.internal\..*\.end$/.test(slice.title)&&start!==undefined){this.telemetryInternalRanges_.push(tr.b.math.Range.fromExplicitRange(start,slice.end));start=undefined;}}}},getFrameEventsInRange(frametimeType,range){const titleToGet=(frametimeType===MAIN_FRAMETIME_TYPE?MAIN_RENDERING_STATS:IMPL_RENDERING_STATS);const frameEvents=[];for(const event of this.process.getDescendantEvents()){if(event.title===titleToGet){if(range.intersectsExplicitRangeInclusive(event.start,event.end)){frameEvents.push(event);}}}
+frameEvents.sort(function(a,b){return a.start-b.start;});return frameEvents;}};function getFrametimeDataFromEvents(frameEvents){const frametimeData=[];for(let i=1;i<frameEvents.length;i++){const diff=frameEvents[i].start-frameEvents[i-1].start;frametimeData.push({'x':frameEvents[i].start,'frametime':diff});}
+return frametimeData;}
+return{ChromeProcessHelper,MAIN_FRAMETIME_TYPE,IMPL_FRAMETIME_TYPE,MAIN_RENDERING_STATS,IMPL_RENDERING_STATS,getSlicesIntersectingRange,getFrametimeDataFromEvents,};});'use strict';tr.exportTo('tr.model.helpers',function(){function ChromeBrowserHelper(modelHelper,process){tr.model.helpers.ChromeProcessHelper.call(this,modelHelper,process);this.mainThread_=process.findAtMostOneThreadNamed('CrBrowserMain');if(!process.name){process.name=ChromeBrowserHelper.PROCESS_NAME;}}
+ChromeBrowserHelper.PROCESS_NAME='Browser';ChromeBrowserHelper.isBrowserProcess=function(process){return!!process.findAtMostOneThreadNamed('CrBrowserMain');};ChromeBrowserHelper.prototype={__proto__:tr.model.helpers.ChromeProcessHelper.prototype,get browserName(){const hasInProcessRendererThread=this.process.findAllThreadsNamed('Chrome_InProcRendererThread').length>0;return hasInProcessRendererThread?'webview':'chrome';},get mainThread(){return this.mainThread_;},get rendererHelpers(){return this.modelHelper.rendererHelpers;},getLoadingEventsInRange(rangeOfInterest){return this.getAllAsyncSlicesMatching(function(slice){return slice.title.indexOf('WebContentsImpl Loading')===0&&rangeOfInterest.intersectsExplicitRangeInclusive(slice.start,slice.end);});},getCommitProvisionalLoadEventsInRange(rangeOfInterest){return this.getAllAsyncSlicesMatching(function(slice){return slice.title==='RenderFrameImpl::didCommitProvisionalLoad'&&rangeOfInterest.intersectsExplicitRangeInclusive(slice.start,slice.end);});},get hasLatencyEvents(){let hasLatency=false;for(const thread of this.modelHelper.model.getAllThreads()){for(const event of thread.getDescendantEvents()){if(!event.isTopLevel)continue;if(!(event instanceof tr.e.cc.InputLatencyAsyncSlice)){continue;}
+hasLatency=true;}}
+return hasLatency;},getLatencyEventsInRange(rangeOfInterest){return this.getAllAsyncSlicesMatching(function(slice){return(slice.title.indexOf('InputLatency')===0)&&rangeOfInterest.intersectsExplicitRangeInclusive(slice.start,slice.end);});},getAllAsyncSlicesMatching(pred,opt_this){const events=[];this.iterAllThreads(function(thread){for(const slice of thread.getDescendantEvents()){if(pred.call(opt_this,slice)){events.push(slice);}}});return events;},iterAllThreads(func,opt_this){for(const thread of Object.values(this.process.threads)){func.call(opt_this,thread);}
+for(const rendererHelper of Object.values(this.rendererHelpers)){const rendererProcess=rendererHelper.process;for(const thread of Object.values(rendererProcess.threads)){func.call(opt_this,thread);}}}};return{ChromeBrowserHelper,};});'use strict';tr.exportTo('tr.model.helpers',function(){function ChromeGpuHelper(modelHelper,process){tr.model.helpers.ChromeProcessHelper.call(this,modelHelper,process);if(!process.name){process.name=ChromeGpuHelper.PROCESS_NAME;}}
+ChromeGpuHelper.PROCESS_NAME='GPU Process';ChromeGpuHelper.isGpuProcess=function(process){if(process.findAtMostOneThreadNamed('CrBrowserMain')||process.findAtMostOneThreadNamed('CrRendererMain')){return false;}
+return process.findAllThreadsNamed('CrGpuMain').length>0;};ChromeGpuHelper.prototype={__proto__:tr.model.helpers.ChromeProcessHelper.prototype};return{ChromeGpuHelper,};});'use strict';tr.exportTo('tr.model.helpers',function(){const NET_CATEGORIES=new Set(['net','netlog','disabled-by-default-netlog','disabled-by-default-network']);class ChromeThreadHelper{constructor(thread){this.thread=thread;}
+getNetworkEvents(){const networkEvents=[];for(const slice of this.thread.asyncSliceGroup.slices){const categories=tr.b.getCategoryParts(slice.category);const isNetEvent=category=>NET_CATEGORIES.has(category);if(categories.filter(isNetEvent).length===0)continue;networkEvents.push(slice);}
+return networkEvents;}}
+return{ChromeThreadHelper,};});'use strict';tr.exportTo('tr.model.helpers',function(){const ChromeThreadHelper=tr.model.helpers.ChromeThreadHelper;function ChromeRendererHelper(modelHelper,process){tr.model.helpers.ChromeProcessHelper.call(this,modelHelper,process);this.mainThread_=process.findAtMostOneThreadNamed('CrRendererMain')||process.findAtMostOneThreadNamed('Chrome_InProcRendererThread');this.compositorThread_=process.findAtMostOneThreadNamed('Compositor');this.rasterWorkerThreads_=process.findAllThreadsMatching(function(t){if(t.name===undefined)return false;if(t.name.startsWith('CompositorTileWorker'))return true;if(t.name.startsWith('CompositorRasterWorker'))return true;return false;});this.dedicatedWorkerThreads_=process.findAllThreadsMatching(function(t){return t.name&&t.name.startsWith('DedicatedWorker');});this.foregroundWorkerThreads_=process.findAllThreadsMatching(function(t){return t.name&&t.name.startsWith('ThreadPoolForegroundWorker');});if(!process.name){process.name=ChromeRendererHelper.PROCESS_NAME;}}
+ChromeRendererHelper.PROCESS_NAME='Renderer';ChromeRendererHelper.isRenderProcess=function(process){if(process.findAtMostOneThreadNamed('CrRendererMain'))return true;if(process.findAtMostOneThreadNamed('Compositor'))return true;return false;};ChromeRendererHelper.isTracingProcess=function(process){return process.labels!==undefined&&process.labels.length===1&&process.labels[0]==='chrome://tracing';};ChromeRendererHelper.prototype={__proto__:tr.model.helpers.ChromeProcessHelper.prototype,get mainThread(){return this.mainThread_;},get compositorThread(){return this.compositorThread_;},get rasterWorkerThreads(){return this.rasterWorkerThreads_;},get dedicatedWorkerThreads(){return this.dedicatedWorkerThreads_;},get foregroundWorkerThreads(){return this.foregroundWorkerThreads_;},get isChromeTracingUI(){return ChromeRendererHelper.isTracingProcess(this.process);},};return{ChromeRendererHelper,};});'use strict';tr.exportTo('tr.model.um',function(){class Segment extends tr.model.TimedEvent{constructor(start,duration){super(start);this.duration=duration;this.expectations_=[];}
+get expectations(){return this.expectations_;}
+clone(){const clone=new Segment(this.start,this.duration);clone.expectations.push(...this.expectations);return clone;}
+addSegment(other){this.duration+=other.duration;this.expectations.push(...other.expectations);}}
+return{Segment,};});'use strict';tr.exportTo('tr.model.helpers',function(){const GESTURE_EVENT='SyntheticGestureController::running';const IR_REG_EXP=/Interaction\.([^/]+)(\/[^/]*)?$/;const ChromeRendererHelper=tr.model.helpers.ChromeRendererHelper;class TelemetryHelper{constructor(modelHelper){this.modelHelper=modelHelper;this.renderersWithIR_=undefined;this.irSegments_=undefined;this.uiSegments_=undefined;this.animationSegments_=undefined;}
+get renderersWithIR(){this.findIRs_();return this.renderersWithIR_;}
+get irSegments(){this.findIRs_();return this.irSegments_;}
+get uiSegments(){this.findIRs_();return this.uiSegments_;}
+get animationSegments(){if(this.animationSegments_===undefined){const model=this.modelHelper.model;this.animationSegments_=model.userModel.segments.filter(segment=>segment.expectations.find(ue=>ue instanceof tr.model.um.AnimationExpectation));this.animationSegments_.sort((x,y)=>x.start-y.start);}
+return this.animationSegments_;}
+findIRs_(){if(this.irSegments_!==undefined)return;this.renderersWithIR_=[];const gestureEvents=[];const interactionRecords=[];const processes=Object.values(this.modelHelper.rendererHelpers).concat(this.modelHelper.browserHelpers).map(processHelper=>processHelper.process);for(const process of processes){let foundIR=false;for(const thread of Object.values(process.threads)){for(const slice of thread.asyncSliceGroup.slices){if(slice.title===GESTURE_EVENT){gestureEvents.push(slice);}else if(IR_REG_EXP.test(slice.title)){interactionRecords.push(slice);foundIR=true;}}}
+if(foundIR&&ChromeRendererHelper.isRenderProcess(process)&&!ChromeRendererHelper.isTracingProcess(process)){this.renderersWithIR_.push(new ChromeRendererHelper(this.modelHelper,process));}}
+this.irSegments_=[];this.uiSegments_=[];for(const ir of interactionRecords){const parts=IR_REG_EXP.exec(ir.title);let gestureEventFound=false;if(parts[1].startsWith('Gesture_')){for(const gestureEvent of gestureEvents){if(ir.boundsRange.intersectsRangeInclusive(gestureEvent.boundsRange)){this.irSegments_.push(new tr.model.um.Segment(gestureEvent.start,gestureEvent.duration));gestureEventFound=true;break;}}}else if(parts[1].startsWith('ui_')){this.uiSegments_.push(new tr.model.um.Segment(ir.start,ir.duration));}
+if(!gestureEventFound){this.irSegments_.push(new tr.model.um.Segment(ir.start,ir.duration));}}
+this.irSegments_.sort((x,y)=>x.start-y.start);this.uiSegments_.sort((x,y)=>x.start-y.start);}}
+return{TelemetryHelper,};});'use strict';tr.exportTo('tr.model.helpers',function(){function findChromeBrowserProcesses(model){return model.getAllProcesses(tr.model.helpers.ChromeBrowserHelper.isBrowserProcess);}
+function findChromeRenderProcesses(model){return model.getAllProcesses(tr.model.helpers.ChromeRendererHelper.isRenderProcess);}
+function findChromeGpuProcess(model){const gpuProcesses=model.getAllProcesses(tr.model.helpers.ChromeGpuHelper.isGpuProcess);if(gpuProcesses.length!==1)return undefined;return gpuProcesses[0];}
+function findTelemetrySurfaceFlingerProcess(model){const surfaceFlingerProcesses=model.getAllProcesses(process=>(process.name==='SurfaceFlinger'));if(surfaceFlingerProcesses.length!==1)return undefined;return surfaceFlingerProcesses[0];}
+function ChromeModelHelper(model){this.model_=model;const browserProcesses=findChromeBrowserProcesses(model);this.browserHelpers_=browserProcesses.map(p=>new tr.model.helpers.ChromeBrowserHelper(this,p));const gpuProcess=findChromeGpuProcess(model);if(gpuProcess){this.gpuHelper_=new tr.model.helpers.ChromeGpuHelper(this,gpuProcess);}else{this.gpuHelper_=undefined;}
+const rendererProcesses_=findChromeRenderProcesses(model);this.rendererHelpers_={};rendererProcesses_.forEach(function(renderProcess){const rendererHelper=new tr.model.helpers.ChromeRendererHelper(this,renderProcess);this.rendererHelpers_[rendererHelper.pid]=rendererHelper;},this);this.surfaceFlingerProcess_=findTelemetrySurfaceFlingerProcess(model);this.chromeBounds_=undefined;this.telemetryHelper_=new tr.model.helpers.TelemetryHelper(this);}
+ChromeModelHelper.guid=tr.b.GUID.allocateSimple();ChromeModelHelper.supportsModel=function(model){if(findChromeBrowserProcesses(model).length)return true;if(findChromeRenderProcesses(model).length)return true;return false;};ChromeModelHelper.prototype={get pid(){throw new Error('woah');},get process(){throw new Error('woah');},get model(){return this.model_;},get browserProcess(){if(this.browserHelper===undefined)return undefined;return this.browserHelper.process;},get browserHelper(){return this.browserHelpers_[0];},get browserHelpers(){return this.browserHelpers_;},get gpuHelper(){return this.gpuHelper_;},get rendererHelpers(){return this.rendererHelpers_;},get surfaceFlingerProcess(){return this.surfaceFlingerProcess_;},get chromeBounds(){if(!this.chromeBounds_){this.chromeBounds_=new tr.b.math.Range();for(const browserHelper of Object.values(this.browserHelpers)){this.chromeBounds_.addRange(browserHelper.process.bounds);}
+for(const rendererHelper of Object.values(this.rendererHelpers)){this.chromeBounds_.addRange(rendererHelper.process.bounds);}
+if(this.gpuHelper){this.chromeBounds_.addRange(this.gpuHelper.process.bounds);}}
+if(this.chromeBounds_.isEmpty){return undefined;}
+return this.chromeBounds_;},get telemetryHelper(){return this.telemetryHelper_;}};return{ChromeModelHelper,};});'use strict';tr.exportTo('tr.e.cc',function(){const AsyncSlice=tr.model.AsyncSlice;const EventSet=tr.model.EventSet;const UI_COMP_NAME='INPUT_EVENT_LATENCY_UI_COMPONENT';const ORIGINAL_COMP_NAME='INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT';const BEGIN_COMP_NAME='INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT';const END_COMP_NAME='INPUT_EVENT_GPU_SWAP_BUFFER_COMPONENT';const LEGACY_END_COMP_NAME='INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT';const MAIN_RENDERER_THREAD_NAME='CrRendererMain';const COMPOSITOR_THREAD_NAME='Compositor';const OLD_IPC_FLOW_EVENT='disabled-by-default-ipc.flow';const OLD_POSTTASK_FLOW_EVENT='disabled-by-default-toplevel.flow';const NEW_POSTTASK_FLOW_EVENT='toplevel.flow';const INPUT_EVENT_TYPE_NAMES={CHAR:'Char',CLICK:'GestureClick',CONTEXT_MENU:'ContextMenu',FLING_CANCEL:'GestureFlingCancel',FLING_START:'GestureFlingStart',KEY_DOWN:'KeyDown',KEY_DOWN_RAW:'RawKeyDown',KEY_UP:'KeyUp',LATENCY_SCROLL_UPDATE:'ScrollUpdate',MOUSE_DOWN:'MouseDown',MOUSE_ENTER:'MouseEnter',MOUSE_LEAVE:'MouseLeave',MOUSE_MOVE:'MouseMove',MOUSE_UP:'MouseUp',MOUSE_WHEEL:'MouseWheel',PINCH_BEGIN:'GesturePinchBegin',PINCH_END:'GesturePinchEnd',PINCH_UPDATE:'GesturePinchUpdate',SCROLL_BEGIN:'GestureScrollBegin',SCROLL_END:'GestureScrollEnd',SCROLL_UPDATE:'GestureScrollUpdate',SCROLL_UPDATE_RENDERER:'ScrollUpdate',SHOW_PRESS:'GestureShowPress',TAP:'GestureTap',TAP_CANCEL:'GestureTapCancel',TAP_DOWN:'GestureTapDown',TOUCH_CANCEL:'TouchCancel',TOUCH_END:'TouchEnd',TOUCH_MOVE:'TouchMove',TOUCH_START:'TouchStart',UNKNOWN:'UNKNOWN'};function InputLatencyAsyncSlice(){AsyncSlice.apply(this,arguments);this.associatedEvents_=new EventSet();this.typeName_=undefined;if(!this.isLegacyEvent){this.determineModernTypeName_();}}
+InputLatencyAsyncSlice.prototype={__proto__:AsyncSlice.prototype,get isLegacyEvent(){return this.title==='InputLatency';},get typeName(){if(!this.typeName_){this.determineLegacyTypeName_();}
+return this.typeName_;},checkTypeName_(){if(!this.typeName_){throw new Error('Unable to determine typeName');}
+let found=false;for(const typeName in INPUT_EVENT_TYPE_NAMES){if(this.typeName===INPUT_EVENT_TYPE_NAMES[typeName]){found=true;break;}}
+if(!found){this.typeName_=INPUT_EVENT_TYPE_NAMES.UNKNOWN;}},determineModernTypeName_(){const lastColonIndex=this.title.lastIndexOf(':');if(lastColonIndex<0)return;const characterAfterLastColonIndex=lastColonIndex+1;this.typeName_=this.title.slice(characterAfterLastColonIndex);this.checkTypeName_();},determineLegacyTypeName_(){for(const subSlice of this.enumerateAllDescendents()){const subSliceIsAInputLatencyAsyncSlice=(subSlice instanceof InputLatencyAsyncSlice);if(!subSliceIsAInputLatencyAsyncSlice)continue;if(!subSlice.typeName)continue;if(this.typeName_&&subSlice.typeName_){const subSliceHasDifferentTypeName=(this.typeName_!==subSlice.typeName_);if(subSliceHasDifferentTypeName){throw new Error('InputLatencyAsyncSlice.determineLegacyTypeName_() '+' found multiple typeNames');}}
+this.typeName_=subSlice.typeName_;}
+if(!this.typeName_){throw new Error('InputLatencyAsyncSlice.determineLegacyTypeName_() failed');}
+this.checkTypeName_();},getRendererHelper(sourceSlices){const traceModel=this.startThread.parent.model;const modelHelper=traceModel.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);if(!modelHelper)return undefined;let mainThread=undefined;let compositorThread=undefined;for(const i in sourceSlices){if(sourceSlices[i].parentContainer.name===MAIN_RENDERER_THREAD_NAME){mainThread=sourceSlices[i].parentContainer;}else if(sourceSlices[i].parentContainer.name===COMPOSITOR_THREAD_NAME){compositorThread=sourceSlices[i].parentContainer;}
+if(mainThread&&compositorThread)break;}
+const rendererHelpers=modelHelper.rendererHelpers;const pids=Object.keys(rendererHelpers);for(let i=0;i<pids.length;i++){const pid=pids[i];const rendererHelper=rendererHelpers[pid];if(rendererHelper.mainThread===mainThread||rendererHelper.compositorThread===compositorThread){return rendererHelper;}}
+return undefined;},addEntireSliceHierarchy(slice){this.associatedEvents_.push(slice);slice.iterateAllSubsequentSlices(function(subsequentSlice){this.associatedEvents_.push(subsequentSlice);},this);},addDirectlyAssociatedEvents(flowEvents){const slices=[];flowEvents.forEach(function(flowEvent){this.associatedEvents_.push(flowEvent);const newSource=flowEvent.startSlice.mostTopLevelSlice;if(slices.indexOf(newSource)===-1){slices.push(newSource);}},this);const lastFlowEvent=flowEvents[flowEvents.length-1];const lastSource=lastFlowEvent.endSlice.mostTopLevelSlice;if(slices.indexOf(lastSource)===-1){slices.push(lastSource);}
+return slices;},belongToOtherInputs(slice,flowEvents){let fromOtherInputs=false;slice.iterateEntireHierarchy(function(subsequentSlice){if(fromOtherInputs)return;subsequentSlice.inFlowEvents.forEach(function(inflow){if(fromOtherInputs)return;if(inflow.category.indexOf('input')>-1){if(flowEvents.indexOf(inflow)===-1){fromOtherInputs=true;}}},this);},this);return fromOtherInputs;},triggerOtherInputs(event,flowEvents){if(event.outFlowEvents===undefined||event.outFlowEvents.length===0){return false;}
+const flow=event.outFlowEvents[0];const isPostTask=flow.category===NEW_POSTTASK_FLOW_EVENT||flow.category===OLD_POSTTASK_FLOW_EVENT;if(!isPostTask||!flow.endSlice){return false;}
+const endSlice=flow.endSlice;if(this.belongToOtherInputs(endSlice.mostTopLevelSlice,flowEvents)){return true;}
+return false;},followSubsequentSlices(event,queue,visited,flowEvents){let stopFollowing=false;let inputAck=false;event.iterateAllSubsequentSlices(function(slice){if(stopFollowing)return;if(slice.title==='TaskQueueManager::RunTask')return;if(slice.title==='ThreadProxy::ScheduledActionSendBeginMainFrame'){return;}
+if(slice.title==='Scheduler::ScheduleBeginImplFrameDeadline'){if(this.triggerOtherInputs(slice,flowEvents))return;}
+if(slice.title==='CompositorImpl::PostComposite'){if(this.triggerOtherInputs(slice,flowEvents))return;}
+if(slice.title==='InputRouterImpl::ProcessInputEventAck'){inputAck=true;}
+if(inputAck&&slice.title==='InputRouterImpl::FilterAndSendWebInputEvent'){stopFollowing=true;}
+this.followCurrentSlice(slice,queue,visited);},this);},followCurrentSlice(event,queue,visited){event.outFlowEvents.forEach(function(outflow){if((outflow.category===NEW_POSTTASK_FLOW_EVENT||outflow.category===OLD_POSTTASK_FLOW_EVENT||outflow.category===OLD_IPC_FLOW_EVENT)&&outflow.endSlice){this.associatedEvents_.push(outflow);const nextEvent=outflow.endSlice.mostTopLevelSlice;if(!visited.contains(nextEvent)){visited.push(nextEvent);queue.push(nextEvent);}}},this);},backtraceFromDraw(beginImplFrame,visited){const pendingEventQueue=[];pendingEventQueue.push(beginImplFrame.mostTopLevelSlice);while(pendingEventQueue.length!==0){const event=pendingEventQueue.pop();this.addEntireSliceHierarchy(event);event.inFlowEvents.forEach(function(inflow){if(inflow.category===POSTTASK_FLOW_EVENT&&inflow.startSlice){const nextEvent=inflow.startSlice.mostTopLevelSlice;if(!visited.contains(nextEvent)){visited.push(nextEvent);pendingEventQueue.push(nextEvent);}}},this);}},sortRasterizerSlices(rasterWorkerThreads,sortedRasterizerSlices){rasterWorkerThreads.forEach(function(rasterizer){Array.prototype.push.apply(sortedRasterizerSlices,rasterizer.sliceGroup.slices);},this);sortedRasterizerSlices.sort(function(a,b){if(a.start!==b.start){return a.start-b.start;}
+return a.guid-b.guid;});},addRasterizationEvents(prepareTiles,rendererHelper,visited,flowEvents,sortedRasterizerSlices){if(!prepareTiles.args.prepare_tiles_id)return;if(!rendererHelper||!rendererHelper.rasterWorkerThreads){return;}
+const rasterWorkerThreads=rendererHelper.rasterWorkerThreads;const prepareTileId=prepareTiles.args.prepare_tiles_id;const pendingEventQueue=[];if(sortedRasterizerSlices.length===0){this.sortRasterizerSlices(rasterWorkerThreads,sortedRasterizerSlices);}
+let numFinishedTasks=0;const RASTER_TASK_TITLE='RasterizerTaskImpl::RunOnWorkerThread';const IMAGEDECODE_TASK_TITLE='ImageDecodeTaskImpl::RunOnWorkerThread';const FINISHED_TASK_TITLE='TaskSetFinishedTaskImpl::RunOnWorkerThread';for(let i=0;i<sortedRasterizerSlices.length;i++){const task=sortedRasterizerSlices[i];if(task.title===RASTER_TASK_TITLE||task.title===IMAGEDECODE_TASK_TITLE){if(task.args.source_prepare_tiles_id===prepareTileId){this.addEntireSliceHierarchy(task.mostTopLevelSlice);}}else if(task.title===FINISHED_TASK_TITLE){if(task.start>prepareTiles.start){pendingEventQueue.push(task.mostTopLevelSlice);if(++numFinishedTasks===3)break;}}}
+while(pendingEventQueue.length!==0){const event=pendingEventQueue.pop();this.addEntireSliceHierarchy(event);this.followSubsequentSlices(event,pendingEventQueue,visited,flowEvents);}},addOtherCausallyRelatedEvents(rendererHelper,sourceSlices,flowEvents,sortedRasterizerSlices){const pendingEventQueue=[];const visitedEvents=new EventSet();let beginImplFrame=undefined;let prepareTiles=undefined;sortedRasterizerSlices=[];sourceSlices.forEach(function(sourceSlice){if(!visitedEvents.contains(sourceSlice)){visitedEvents.push(sourceSlice);pendingEventQueue.push(sourceSlice);}},this);while(pendingEventQueue.length!==0){const event=pendingEventQueue.pop();this.addEntireSliceHierarchy(event);this.followCurrentSlice(event,pendingEventQueue,visitedEvents);this.followSubsequentSlices(event,pendingEventQueue,visitedEvents,flowEvents);const COMPOSITOR_PREPARE_TILES='TileManager::PrepareTiles';prepareTiles=event.findDescendentSlice(COMPOSITOR_PREPARE_TILES);if(prepareTiles){this.addRasterizationEvents(prepareTiles,rendererHelper,visitedEvents,flowEvents,sortedRasterizerSlices);}
+const COMPOSITOR_ON_BIFD='Scheduler::OnBeginImplFrameDeadline';beginImplFrame=event.findDescendentSlice(COMPOSITOR_ON_BIFD);if(beginImplFrame){this.backtraceFromDraw(beginImplFrame,visitedEvents);}}},get associatedEvents(){if(this.associatedEvents_.length!==0){return this.associatedEvents_;}
+const modelIndices=this.startThread.parent.model.modelIndices;const flowEvents=modelIndices.getFlowEventsWithId(this.id);if(flowEvents.length===0){return this.associatedEvents_;}
+const sourceSlices=this.addDirectlyAssociatedEvents(flowEvents);const rendererHelper=this.getRendererHelper(sourceSlices);this.addOtherCausallyRelatedEvents(rendererHelper,sourceSlices,flowEvents);return this.associatedEvents_;},get inputLatency(){if(!('data'in this.args))return undefined;const data=this.args.data;const endTimeComp=data[END_COMP_NAME]||data[LEGACY_END_COMP_NAME];if(endTimeComp===undefined)return undefined;let latency=0;const endTime=endTimeComp.time;if(ORIGINAL_COMP_NAME in data){latency=endTime-data[ORIGINAL_COMP_NAME].time;}else if(UI_COMP_NAME in data){latency=endTime-data[UI_COMP_NAME].time;}else if(BEGIN_COMP_NAME in data){latency=endTime-data[BEGIN_COMP_NAME].time;}else{throw new Error('No valid begin latency component');}
+return latency;}};const eventTypeNames=['Char','ContextMenu','GestureClick','GestureFlingCancel','GestureFlingStart','GestureScrollBegin','GestureScrollEnd','GestureScrollUpdate','GestureShowPress','GestureTap','GestureTapCancel','GestureTapDown','GesturePinchBegin','GesturePinchEnd','GesturePinchUpdate','KeyDown','KeyUp','MouseDown','MouseEnter','MouseLeave','MouseMove','MouseUp','MouseWheel','RawKeyDown','ScrollUpdate','TouchCancel','TouchEnd','TouchMove','TouchStart'];const allTypeNames=['InputLatency'];eventTypeNames.forEach(function(eventTypeName){allTypeNames.push('InputLatency:'+eventTypeName);allTypeNames.push('InputLatency::'+eventTypeName);});AsyncSlice.subTypes.register(InputLatencyAsyncSlice,{typeNames:allTypeNames,categoryParts:['latencyInfo']});return{InputLatencyAsyncSlice,INPUT_EVENT_TYPE_NAMES,};});'use strict';tr.exportTo('tr.e.chrome',function(){const SAME_AS_PARENT='same-as-parent';const TITLES_FOR_USER_FRIENDLY_CATEGORY={composite:['CompositingInputsUpdater::update','ThreadProxy::SetNeedsUpdateLayers','LayerTreeHost::DoUpdateLayers','LayerTreeHost::UpdateLayers::BuildPropertyTrees','LocalFrameView::pushPaintArtifactToCompositor','LocalFrameView::updateCompositedSelectionIfNeeded','LocalFrameView::RunCompositingLifecyclePhase','UpdateLayerTree',],gc:['minorGC','majorGC','MajorGC','MinorGC','V8.GCScavenger','V8.GCIncrementalMarking','V8.GCIdleNotification','V8.GCContext','V8.GCCompactor','V8GCController::traceDOMWrappers',],iframe_creation:['WebLocalFrameImpl::createChildframe',],imageDecode:['Decode Image','ImageFrameGenerator::decode','ImageFrameGenerator::decodeAndScale','ImageFrameGenerator::decodeToYUV','ImageResourceContent::updateImage',],input:['HitTest','ScrollableArea::scrollPositionChanged','EventHandler::handleMouseMoveEvent',],layout:['IntersectionObserverController::computeTrackedIntersectionObservations','LocalFrameView::invalidateTree','LocalFrameView::layout','LocalFrameView::performLayout','LocalFrameView::performPostLayoutTasks','LocalFrameView::performPreLayoutTasks','LocalFrameView::RunStyleAndLayoutCompositingPhases','Layout','PaintLayer::updateLayerPositionsAfterLayout','ResourceLoadPriorityOptimizer::updateAllImageResourcePriorities','WebViewImpl::updateAllLifecyclePhases','WebViewImpl::beginFrame',],parseHTML:['BackgroundHTMLParser::pumpTokenizer','BackgroundHTMLParser::sendTokensToMainThread','HTMLDocumentParser::didReceiveParsedChunkFromBackgroundParser','HTMLDocumentParser::documentElementAvailable','HTMLDocumentParser::notifyPendingTokenizedChunks','HTMLDocumentParser::processParsedChunkFromBackgroundParser','HTMLDocumentParser::processTokenizedChunkFromBackgroundParser','ParseHTML',],raster:['DisplayListRasterSource::PerformSolidColorAnalysis','Picture::Raster','RasterBufferImpl::Playback','RasterTask','RasterizerTaskImpl::RunOnWorkerThread','SkCanvas::drawImageRect()','SkCanvas::drawPicture()','SkCanvas::drawTextBlob()','TileTaskWorkerPool::PlaybackToMemory',],record:['Canvas2DLayerBridge::flushRecordingOnly','CompositingInputsUpdater::update','CompositingRequirementsUpdater::updateRecursive','ContentLayerDelegate::paintContents','DisplayItemList::Finalize','LocalFrameView::RunPaintLifecyclePhase','LocalFrameView::RunPrePaintLifecyclePhase','Paint','PaintController::commitNewDisplayItems','PaintLayerCompositor::updateIfNeededRecursive','Picture::Record','PictureLayer::Update',],style:['CSSParserImpl::parseStyleSheet.parse','CSSParserImpl::parseStyleSheet.tokenize','Document::rebuildLayoutTree','Document::recalcStyle','Document::updateActiveStyle','Document::updateStyle','Document::updateStyleInvalidationIfNeeded','LocalFrameView::updateStyleAndLayoutIfNeededRecursive','ParseAuthorStyleSheet','RuleSet::addRulesFromSheet','StyleElement::processStyleSheet','StyleEngine::createResolver','StyleEngine::updateActiveStyleSheets','StyleSheetContents::parseAuthorStyleSheet','UpdateLayoutTree',],script_parse_and_compile:['V8.CompileFullCode','V8.NewContext','V8.Parse','V8.ParseLazy','V8.RecompileSynchronous','V8.ScriptCompiler','v8.compile','v8.parseOnBackground',],script_execute:['EvaluateScript','FunctionCall','HTMLParserScriptRunner ExecuteScript','V8.Execute','V8.RunMicrotasks','V8.Task','WindowProxy::initialize','v8.callFunction','v8.run',],resource_loading:['RenderFrameImpl::didFinishDocumentLoad','RenderFrameImpl::didFinishLoad','Resource::appendData','ResourceDispatcher::OnReceivedData','ResourceDispatcher::OnReceivedResponse','ResourceDispatcher::OnRequestComplete','ResourceFetcher::requestResource','WebURLLoaderImpl::Context::Cancel','WebURLLoaderImpl::Context::OnCompletedRequest','WebURLLoaderImpl::Context::OnReceivedData','WebURLLoaderImpl::Context::OnReceivedRedirect','WebURLLoaderImpl::Context::OnReceivedResponse','WebURLLoaderImpl::Context::Start','WebURLLoaderImpl::loadAsynchronously','WebURLLoaderImpl::loadSynchronously','content::mojom::URLLoaderClient',],renderer_misc:['DecodeFont','ThreadState::completeSweep',],v8_runtime:[],[SAME_AS_PARENT]:['SyncChannel::Send',]};const COLOR_FOR_USER_FRIENDLY_CATEGORY=new tr.b.SinebowColorGenerator();const USER_FRIENDLY_CATEGORY_FOR_TITLE=new Map();for(const category in TITLES_FOR_USER_FRIENDLY_CATEGORY){TITLES_FOR_USER_FRIENDLY_CATEGORY[category].forEach(function(title){USER_FRIENDLY_CATEGORY_FOR_TITLE.set(title,category);});}
+const USER_FRIENDLY_CATEGORY_FOR_EVENT_CATEGORY={netlog:'net',overhead:'overhead',startup:'startup',gpu:'gpu',};function ChromeUserFriendlyCategoryDriver(){}
+ChromeUserFriendlyCategoryDriver.fromEvent=function(event){let userFriendlyCategory=USER_FRIENDLY_CATEGORY_FOR_TITLE.get(event.title);if(userFriendlyCategory){if(userFriendlyCategory===SAME_AS_PARENT){if(event.parentSlice){return ChromeUserFriendlyCategoryDriver.fromEvent(event.parentSlice);}}else{return userFriendlyCategory;}}
+const eventCategoryParts=tr.b.getCategoryParts(event.category);for(let i=0;i<eventCategoryParts.length;++i){const eventCategory=eventCategoryParts[i];userFriendlyCategory=USER_FRIENDLY_CATEGORY_FOR_EVENT_CATEGORY[eventCategory];if(userFriendlyCategory){return userFriendlyCategory;}}
+return'other';};ChromeUserFriendlyCategoryDriver.getColor=function(ufc){return COLOR_FOR_USER_FRIENDLY_CATEGORY.colorForKey(ufc);};ChromeUserFriendlyCategoryDriver.ALL_TITLES=['other'];for(const category in TITLES_FOR_USER_FRIENDLY_CATEGORY){if(category===SAME_AS_PARENT)continue;ChromeUserFriendlyCategoryDriver.ALL_TITLES.push(category);}
+for(const category of Object.values(USER_FRIENDLY_CATEGORY_FOR_EVENT_CATEGORY)){ChromeUserFriendlyCategoryDriver.ALL_TITLES.push(category);}
+ChromeUserFriendlyCategoryDriver.ALL_TITLES.sort();for(const category of ChromeUserFriendlyCategoryDriver.ALL_TITLES){ChromeUserFriendlyCategoryDriver.getColor(category);}
+return{ChromeUserFriendlyCategoryDriver,};});'use strict';tr.exportTo('tr.model',function(){return{BROWSER_PROCESS_PID_REF:-1,OBJECT_DEFAULT_SCOPE:'ptr',LOCAL_ID_PHASES:new Set(['N','D','O','(',')'])};});'use strict';tr.exportTo('tr.e.audits',function(){const Auditor=tr.c.Auditor;const Alert=tr.model.Alert;const EventInfo=tr.model.EventInfo;function ChromeAuditor(model){Auditor.call(this,model);const modelHelper=this.model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);if(modelHelper&&modelHelper.browserHelper){this.modelHelper=modelHelper;}else{this.modelHelper=undefined;}}
+function getMissedFrameAlerts(rendererHelpers){const alerts=[];for(const rendererHelper of rendererHelpers){if(!rendererHelper.compositorThread)continue;const thread=rendererHelper.compositorThread;const asyncSlices=Object.values(thread.asyncSliceGroup.slices);for(const slice of asyncSlices){if(slice.title!=='PipelineReporter'||!slice.args.termination_status||slice.args.termination_status!=='missed_frame')continue;const alertSlices=[slice].concat(slice.subSlices);alerts.push(new Alert(new EventInfo('Missed Frame','Frame was not submitted before deadline.'),slice.start,alertSlices));}}
+return alerts;}
+ChromeAuditor.prototype={__proto__:Auditor.prototype,runAnnotate(){if(!this.modelHelper)return;for(const pid in this.modelHelper.rendererHelpers){const rendererHelper=this.modelHelper.rendererHelpers[pid];if(rendererHelper.isChromeTracingUI){rendererHelper.process.important=false;}}},installUserFriendlyCategoryDriverIfNeeded(){this.model.addUserFriendlyCategoryDriver(tr.e.chrome.ChromeUserFriendlyCategoryDriver);},runAudit(){if(!this.modelHelper)return;this.model.replacePIDRefsInPatchups(tr.model.BROWSER_PROCESS_PID_REF,this.modelHelper.browserProcess.pid);this.model.applyObjectRefPatchups();const alerts=getMissedFrameAlerts(Object.values(this.modelHelper.rendererHelpers));this.model.alerts=this.model.alerts.concat(alerts);}};Auditor.register(ChromeAuditor);return{ChromeAuditor,};});'use strict';tr.exportTo('tr.e.chrome',function(){const KNOWN_PROPERTIES={absX:1,absY:1,address:1,anonymous:1,childNeeds:1,children:1,classNames:1,col:1,colSpan:1,float:1,height:1,htmlId:1,name:1,posChildNeeds:1,positioned:1,positionedMovement:1,relX:1,relY:1,relativePositioned:1,row:1,rowSpan:1,selfNeeds:1,stickyPositioned:1,tag:1,width:1};function LayoutObject(snapshot,args){this.snapshot_=snapshot;this.id_=args.address;this.name_=args.name;this.childLayoutObjects_=[];this.otherProperties_={};this.tag_=args.tag;this.relativeRect_=tr.b.math.Rect.fromXYWH(args.relX,args.relY,args.width,args.height);this.absoluteRect_=tr.b.math.Rect.fromXYWH(args.absX,args.absY,args.width,args.height);this.isFloat_=args.float;this.isStickyPositioned_=args.stickyPositioned;this.isPositioned_=args.positioned;this.isRelativePositioned_=args.relativePositioned;this.isAnonymous_=args.anonymous;this.htmlId_=args.htmlId;this.classNames_=args.classNames;this.needsLayoutReasons_=[];if(args.selfNeeds){this.needsLayoutReasons_.push('self');}
+if(args.childNeeds){this.needsLayoutReasons_.push('child');}
+if(args.posChildNeeds){this.needsLayoutReasons_.push('positionedChild');}
+if(args.positionedMovement){this.needsLayoutReasons_.push('positionedMovement');}
+this.tableRow_=args.row;this.tableCol_=args.col;this.tableRowSpan_=args.rowSpan;this.tableColSpan_=args.colSpan;if(args.children){args.children.forEach(function(child){this.childLayoutObjects_.push(new LayoutObject(snapshot,child));}.bind(this));}
+for(const property in args){if(!KNOWN_PROPERTIES[property]){this.otherProperties_[property]=args[property];}}}
+LayoutObject.prototype={get snapshot(){return this.snapshot_;},get id(){return this.id_;},get name(){return this.name_;},get tag(){return this.tag_;},get relativeRect(){return this.relativeRect_;},get absoluteRect(){return this.absoluteRect_;},get isPositioned(){return this.isPositioned_;},get isFloat(){return this.isFloat_;},get isStickyPositioned(){return this.isStickyPositioned_;},get isRelativePositioned(){return this.isRelativePositioned_;},get isAnonymous(){return this.isAnonymous_;},get tableRow(){return this.tableRow_;},get tableCol(){return this.tableCol_;},get tableRowSpan(){return this.tableRowSpan_;},get tableColSpan(){return this.tableColSpan_;},get htmlId(){return this.htmlId_;},get classNames(){return this.classNames_;},get needsLayoutReasons(){return this.needsLayoutReasons_;},get hasChildLayoutObjects(){return this.childLayoutObjects_.length>0;},get childLayoutObjects(){return this.childLayoutObjects_;},traverseTree(cb,opt_this){cb.call(opt_this,this);if(!this.hasChildLayoutObjects)return;this.childLayoutObjects.forEach(function(child){child.traverseTree(cb,opt_this);});},get otherPropertyNames(){const names=[];for(const name in this.otherProperties_){names.push(name);}
+return names;},getProperty(name){return this.otherProperties_[name];},get previousSnapshotLayoutObject(){if(!this.snapshot.previousSnapshot)return undefined;return this.snapshot.previousSnapshot.getLayoutObjectById(this.id);},get nextSnapshotLayoutObject(){if(!this.snapshot.nextSnapshot)return undefined;return this.snapshot.nextSnapshot.getLayoutObjectById(this.id);}};return{LayoutObject,};});'use strict';tr.exportTo('tr.e.chrome',function(){const ObjectSnapshot=tr.model.ObjectSnapshot;const ObjectInstance=tr.model.ObjectInstance;function LayoutTreeInstance(){ObjectInstance.apply(this,arguments);}
+LayoutTreeInstance.prototype={__proto__:ObjectInstance.prototype,};ObjectInstance.subTypes.register(LayoutTreeInstance,{typeName:'LayoutTree'});function LayoutTreeSnapshot(){ObjectSnapshot.apply(this,arguments);this.rootLayoutObject=new tr.e.chrome.LayoutObject(this,this.args);}
+LayoutTreeSnapshot.prototype={__proto__:ObjectSnapshot.prototype,};ObjectSnapshot.subTypes.register(LayoutTreeSnapshot,{typeName:'LayoutTree'});return{LayoutTreeInstance,LayoutTreeSnapshot,};});'use strict';tr.exportTo('tr.model',function(){function EventContainer(){this.guid_=tr.b.GUID.allocateSimple();this.important=true;this.bounds_=new tr.b.math.Range();}
+EventContainer.prototype={get guid(){return this.guid_;},get stableId(){throw new Error('Not implemented');},get bounds(){return this.bounds_;},updateBounds(){throw new Error('Not implemented');},shiftTimestampsForward(amount){throw new Error('Not implemented');},*childEvents(){},*getDescendantEvents(){yield*this.childEvents();for(const container of this.childEventContainers()){yield*container.getDescendantEvents();}},*childEventContainers(){},*getDescendantEventContainers(){yield this;for(const container of this.childEventContainers()){yield*container.getDescendantEventContainers();}},*getDescendantEventsInSortedRanges(ranges,opt_containerPredicate){if(opt_containerPredicate===undefined||opt_containerPredicate(this)){for(const event of this.childEvents()){const i=tr.b.findFirstTrueIndexInSortedArray(ranges,range=>event.start<=range.max);if(i<ranges.length&&event.end>=ranges[i].min)yield event;}}
+for(const container of this.childEventContainers()){yield*container.getDescendantEventsInSortedRanges(ranges,opt_containerPredicate);}},*findTopmostSlicesInThisContainer(eventPredicate,opt_this){},*findTopmostSlices(eventPredicate){for(const ec of this.getDescendantEventContainers()){yield*ec.findTopmostSlicesInThisContainer(eventPredicate);}},*findTopmostSlicesNamed(name){yield*this.findTopmostSlices(e=>e.title===name);}};return{EventContainer,};});'use strict';tr.exportTo('tr.model',function(){const Event=tr.model.Event;const EventRegistry=tr.model.EventRegistry;class ResourceUsageSample extends Event{constructor(series,start,usage){super();this.series_=series;this.start_=start;this.usage_=usage;}
+get series(){return this.series_;}
+get start(){return this.start_;}
+set start(value){this.start_=value;}
+get usage(){return this.usage_;}
+set usage(value){this.usage_=value;}
+addBoundsToRange(range){range.addValue(this.start);}}
+EventRegistry.register(ResourceUsageSample,{name:'resourceUsageSample',pluralName:'resourceUsageSamples'});return{ResourceUsageSample,};});'use strict';tr.exportTo('tr.model',function(){const ResourceUsageSample=tr.model.ResourceUsageSample;class ResourceUsageSeries extends tr.model.EventContainer{constructor(device){super();this.device_=device;this.samples_=[];}
+get device(){return this.device_;}
+get samples(){return this.samples_;}
+get stableId(){return this.device_.stableId+'.ResourceUsageSeries';}
+addUsageSample(ts,val){const sample=new ResourceUsageSample(this,ts,val);this.samples_.push(sample);return sample;}
+computeResourceTimeConsumedInMs(start,end){const measurementRange=tr.b.math.Range.fromExplicitRange(start,end);let resourceTimeInMs=0;let startIndex=tr.b.findLowIndexInSortedArray(this.samples,x=>x.start,start)-1;const endIndex=tr.b.findLowIndexInSortedArray(this.samples,x=>x.start,end);if(startIndex<0)startIndex=0;for(let i=startIndex;i<endIndex;i++){const sample=this.samples[i];const nextSample=this.samples[i+1];const sampleRange=new tr.b.math.Range();sampleRange.addValue(sample.start);sampleRange.addValue(nextSample?nextSample.start:sample.start);const intersectionRangeInMs=measurementRange.findIntersection(sampleRange);resourceTimeInMs+=intersectionRangeInMs.duration*sample.usage;}
+return resourceTimeInMs;}
+getSamplesWithinRange(start,end){const startIndex=tr.b.findLowIndexInSortedArray(this.samples,x=>x.start,start);const endIndex=tr.b.findLowIndexInSortedArray(this.samples,x=>x.start,end);return this.samples.slice(startIndex,endIndex);}
+shiftTimestampsForward(amount){for(let i=0;i<this.samples_.length;++i){this.samples_[i].start+=amount;}}
+updateBounds(){this.bounds.reset();if(this.samples_.length===0)return;this.bounds.addValue(this.samples_[0].start);this.bounds.addValue(this.samples_[this.samples_.length-1].start);}*childEvents(){yield*this.samples_;}}
+return{ResourceUsageSeries,};});'use strict';tr.exportTo('tr.e.audits',function(){class CpuUsageAuditor extends tr.c.Auditor{constructor(model){super();this.model_=model;}
+runAnnotate(){this.model_.device.cpuUsageSeries=this.computeCpuUsageSeries_(this.model_.bounds.min,this.model_.bounds.max,this.computeCpuUsage_());}
+computeBinSize_(start,end){const MIN_BIN_SIZE_MS=1.0;const MAX_NUM_BINS=100000;const interval=end-start;let binSize=MIN_BIN_SIZE_MS;while(binSize*MAX_NUM_BINS<interval)binSize*=2;return binSize;}
+computeCpuUsageSeries_(start,end,usageRecords){const series=new tr.model.ResourceUsageSeries();if(start===undefined||usageRecords.length===0)return series;const binSize=this.computeBinSize_(start,end);const numBins=Math.ceil((end-start)/binSize);const arr=new Array(numBins).fill(0);for(const record of usageRecords){const firstIndex=Math.ceil((record.start-start)/binSize);const lastIndex=Math.floor((record.end-start)/binSize);for(let i=firstIndex;i<=lastIndex;i++)arr[i]+=record.usage;}
+for(let i=0;i<numBins;i++){series.addUsageSample(start+(i*binSize),arr[i]);}
+return series;}
+computeCpuUsage_(){const model=this.model_;const result=[];for(const pid in model.processes){for(const e of model.processes[pid].getDescendantEvents()){if(!(e instanceof tr.model.ThreadSlice)||e.duration===0||e.cpuDuration===undefined){continue;}
+if(e.selfTime===0||e.selfTime===undefined||e.cpuSelfTime===undefined){continue;}
+const usage=tr.b.math.clamp(e.cpuSelfTime/e.selfTime,0,1);let lastTime=e.start;for(const subslice of e.subSlices){result.push({usage,start:lastTime,end:subslice.start});lastTime=subslice.end;}
+result.push({usage,start:lastTime,end:e.end});}}
+return result;}}
+tr.c.Auditor.register(CpuUsageAuditor);return{CpuUsageAuditor};});'use strict';tr.exportTo('tr.e.img',function(){const ObjectSnapshot=tr.model.ObjectSnapshot;function ImageSnapshot(){ObjectSnapshot.apply(this,arguments);}
+ImageSnapshot.prototype={__proto__:ObjectSnapshot.prototype,initialize(){this.data_=this.args.data;this.type_=this.args.params.type;},get data(){return this.data_;},get type(){return this.type_;},};ObjectSnapshot.subTypes.register(ImageSnapshot,{typeNames:['gfx::Image']});return{ImageSnapshot,};});'use strict';tr.exportTo('tr.b',function(){function Base64(){}
+function b64ToUint6(nChr){if(nChr>64&&nChr<91)return nChr-65;if(nChr>96&&nChr<123)return nChr-71;if(nChr>47&&nChr<58)return nChr+4;if(nChr===43)return 62;if(nChr===47)return 63;return 0;}
+Base64.getDecodedBufferLength=function(input){let pad=0;if(input.substr(-2)==='=='){pad=2;}else if(input.substr(-1)==='='){pad=1;}
+return((input.length*3+1)>>2)-pad;};Base64.EncodeArrayBufferToString=function(input){let binary='';const bytes=new Uint8Array(input);const len=bytes.byteLength;for(let i=0;i<len;i++){binary+=String.fromCharCode(bytes[i]);}
+return btoa(binary);};Base64.DecodeToTypedArray=function(input,output){const nInLen=input.length;const nOutLen=Base64.getDecodedBufferLength(input);let nMod3=0;let nMod4=0;let nUint24=0;let nOutIdx=0;if(nOutLen>output.byteLength){throw new Error('Output buffer too small to decode.');}
+for(let nInIdx=0;nInIdx<nInLen;nInIdx++){nMod4=nInIdx&3;nUint24|=b64ToUint6(input.charCodeAt(nInIdx))<<18-6*nMod4;if(nMod4===3||nInLen-nInIdx===1){for(nMod3=0;nMod3<3&&nOutIdx<nOutLen;nMod3++,nOutIdx++){output.setUint8(nOutIdx,nUint24>>>(16>>>nMod3&24)&255);}
+nUint24=0;}}
+return nOutLen;};Base64.btoa=function(input){return btoa(input);};Base64.atob=function(input){return atob(input);};return{Base64,};});'use strict';tr.exportTo('tr.e.importer.etw',function(){function Parser(importer){this.importer=importer;this.model=importer.model;}
+Parser.prototype={__proto__:Object.prototype};const options=new tr.b.ExtensionRegistryOptions(tr.b.BASIC_REGISTRY_MODE);options.mandatoryBaseClass=Parser;tr.b.decorateExtensionRegistry(Parser,options);return{Parser,};});'use strict';tr.exportTo('tr.e.importer.etw',function(){const Parser=tr.e.importer.etw.Parser;const guid='68FDD900-4A3E-11D1-84F4-0000F80464E3';const kEventTraceHeaderOpcode=0;function EventTraceParser(importer){Parser.call(this,importer);importer.registerEventHandler(guid,kEventTraceHeaderOpcode,EventTraceParser.prototype.decodeHeader.bind(this));}
+EventTraceParser.prototype={__proto__:Parser.prototype,decodeFields(header,decoder){if(header.version!==2){throw new Error('Incompatible EventTrace event version.');}
+const bufferSize=decoder.decodeUInt32();const version=decoder.decodeUInt32();const providerVersion=decoder.decodeUInt32();const numberOfProcessors=decoder.decodeUInt32();const endTime=decoder.decodeUInt64ToString();const timerResolution=decoder.decodeUInt32();const maxFileSize=decoder.decodeUInt32();const logFileMode=decoder.decodeUInt32();const buffersWritten=decoder.decodeUInt32();const startBuffers=decoder.decodeUInt32();const pointerSize=decoder.decodeUInt32();const eventsLost=decoder.decodeUInt32();const cpuSpeed=decoder.decodeUInt32();const loggerName=decoder.decodeUInteger(header.is64);const logFileName=decoder.decodeUInteger(header.is64);const timeZoneInformation=decoder.decodeTimeZoneInformation();const padding=decoder.decodeUInt32();const bootTime=decoder.decodeUInt64ToString();const perfFreq=decoder.decodeUInt64ToString();const startTime=decoder.decodeUInt64ToString();const reservedFlags=decoder.decodeUInt32();const buffersLost=decoder.decodeUInt32();const sessionNameString=decoder.decodeW16String();const logFileNameString=decoder.decodeW16String();return{bufferSize,version,providerVersion,numberOfProcessors,endTime,timerResolution,maxFileSize,logFileMode,buffersWritten,startBuffers,pointerSize,eventsLost,cpuSpeed,loggerName,logFileName,timeZoneInformation,bootTime,perfFreq,startTime,reservedFlags,buffersLost,sessionNameString,logFileNameString};},decodeHeader(header,decoder){const fields=this.decodeFields(header,decoder);return true;}};Parser.register(EventTraceParser);return{EventTraceParser,};});'use strict';tr.exportTo('tr.e.importer.etw',function(){const Parser=tr.e.importer.etw.Parser;const guid='3D6FA8D0-FE05-11D0-9DDA-00C04FD7BA7C';const kProcessStartOpcode=1;const kProcessEndOpcode=2;const kProcessDCStartOpcode=3;const kProcessDCEndOpcode=4;const kProcessDefunctOpcode=39;function ProcessParser(importer){Parser.call(this,importer);importer.registerEventHandler(guid,kProcessStartOpcode,ProcessParser.prototype.decodeStart.bind(this));importer.registerEventHandler(guid,kProcessEndOpcode,ProcessParser.prototype.decodeEnd.bind(this));importer.registerEventHandler(guid,kProcessDCStartOpcode,ProcessParser.prototype.decodeDCStart.bind(this));importer.registerEventHandler(guid,kProcessDCEndOpcode,ProcessParser.prototype.decodeDCEnd.bind(this));importer.registerEventHandler(guid,kProcessDefunctOpcode,ProcessParser.prototype.decodeDefunct.bind(this));}
+ProcessParser.prototype={__proto__:Parser.prototype,decodeFields(header,decoder){if(header.version>5){throw new Error('Incompatible Process event version.');}
+let pageDirectoryBase;if(header.version===1){pageDirectoryBase=decoder.decodeUInteger(header.is64);}
+let uniqueProcessKey;if(header.version>=2){uniqueProcessKey=decoder.decodeUInteger(header.is64);}
+const processId=decoder.decodeUInt32();const parentId=decoder.decodeUInt32();let sessionId;let exitStatus;if(header.version>=1){sessionId=decoder.decodeUInt32();exitStatus=decoder.decodeInt32();}
+let directoryTableBase;if(header.version>=3){directoryTableBase=decoder.decodeUInteger(header.is64);}
+let flags;if(header.version>=4){flags=decoder.decodeUInt32();}
+const userSID=decoder.decodeSID(header.is64);let imageFileName;if(header.version>=1){imageFileName=decoder.decodeString();}
+let commandLine;if(header.version>=2){commandLine=decoder.decodeW16String();}
+let packageFullName;let applicationId;if(header.version>=4){packageFullName=decoder.decodeW16String();applicationId=decoder.decodeW16String();}
+let exitTime;if(header.version===5&&header.opcode===kProcessDefunctOpcode){exitTime=decoder.decodeUInt64ToString();}
+return{pageDirectoryBase,uniqueProcessKey,processId,parentId,sessionId,exitStatus,directoryTableBase,flags,userSID,imageFileName,commandLine,packageFullName,applicationId,exitTime};},decodeStart(header,decoder){const fields=this.decodeFields(header,decoder);const process=this.model.getOrCreateProcess(fields.processId);if(process.hasOwnProperty('has_ended')){throw new Error('Process clash detected.');}
+process.name=fields.imageFileName;return true;},decodeEnd(header,decoder){const fields=this.decodeFields(header,decoder);const process=this.model.getOrCreateProcess(fields.processId);process.has_ended=true;return true;},decodeDCStart(header,decoder){const fields=this.decodeFields(header,decoder);const process=this.model.getOrCreateProcess(fields.processId);if(process.hasOwnProperty('has_ended')){throw new Error('Process clash detected.');}
+process.name=fields.imageFileName;return true;},decodeDCEnd(header,decoder){const fields=this.decodeFields(header,decoder);const process=this.model.getOrCreateProcess(fields.processId);process.has_ended=true;return true;},decodeDefunct(header,decoder){const fields=this.decodeFields(header,decoder);return true;}};Parser.register(ProcessParser);return{ProcessParser,};});'use strict';tr.exportTo('tr.e.importer.etw',function(){const Parser=tr.e.importer.etw.Parser;const guid='3D6FA8D1-FE05-11D0-9DDA-00C04FD7BA7C';const kThreadStartOpcode=1;const kThreadEndOpcode=2;const kThreadDCStartOpcode=3;const kThreadDCEndOpcode=4;const kThreadCSwitchOpcode=36;function ThreadParser(importer){Parser.call(this,importer);importer.registerEventHandler(guid,kThreadStartOpcode,ThreadParser.prototype.decodeStart.bind(this));importer.registerEventHandler(guid,kThreadEndOpcode,ThreadParser.prototype.decodeEnd.bind(this));importer.registerEventHandler(guid,kThreadDCStartOpcode,ThreadParser.prototype.decodeDCStart.bind(this));importer.registerEventHandler(guid,kThreadDCEndOpcode,ThreadParser.prototype.decodeDCEnd.bind(this));importer.registerEventHandler(guid,kThreadCSwitchOpcode,ThreadParser.prototype.decodeCSwitch.bind(this));}
+ThreadParser.prototype={__proto__:Parser.prototype,decodeFields(header,decoder){if(header.version>3){throw new Error('Incompatible Thread event version '+
+header.version+'.');}
+const processId=decoder.decodeUInt32();const threadId=decoder.decodeUInt32();let stackBase;let stackLimit;let userStackBase;let userStackLimit;let affinity;let startAddr;let win32StartAddr;let tebBase;let subProcessTag;let basePriority;let pagePriority;let ioPriority;let threadFlags;let waitMode;if(header.version===1){if(header.opcode===kThreadStartOpcode||header.opcode===kThreadDCStartOpcode){stackBase=decoder.decodeUInteger(header.is64);stackLimit=decoder.decodeUInteger(header.is64);userStackBase=decoder.decodeUInteger(header.is64);userStackLimit=decoder.decodeUInteger(header.is64);startAddr=decoder.decodeUInteger(header.is64);win32StartAddr=decoder.decodeUInteger(header.is64);waitMode=decoder.decodeInt8();decoder.skip(3);}}else{stackBase=decoder.decodeUInteger(header.is64);stackLimit=decoder.decodeUInteger(header.is64);userStackBase=decoder.decodeUInteger(header.is64);userStackLimit=decoder.decodeUInteger(header.is64);if(header.version===2){startAddr=decoder.decodeUInteger(header.is64);}else{affinity=decoder.decodeUInteger(header.is64);}
+win32StartAddr=decoder.decodeUInteger(header.is64);tebBase=decoder.decodeUInteger(header.is64);subProcessTag=decoder.decodeUInt32();if(header.version===3){basePriority=decoder.decodeUInt8();pagePriority=decoder.decodeUInt8();ioPriority=decoder.decodeUInt8();threadFlags=decoder.decodeUInt8();}}
+return{processId,threadId,stackBase,stackLimit,userStackBase,userStackLimit,affinity,startAddr,win32StartAddr,tebBase,subProcessTag,waitMode,basePriority,pagePriority,ioPriority,threadFlags};},decodeCSwitchFields(header,decoder){if(header.version<2||header.version>4){throw new Error('Incompatible cswitch event version '+
+header.version+'.');}
+const newThreadId=decoder.decodeUInt32();const oldThreadId=decoder.decodeUInt32();const newThreadPriority=decoder.decodeInt8();const oldThreadPriority=decoder.decodeInt8();const previousCState=decoder.decodeUInt8();const spareByte=decoder.decodeInt8();const oldThreadWaitReason=decoder.decodeInt8();const oldThreadWaitMode=decoder.decodeInt8();const oldThreadState=decoder.decodeInt8();const oldThreadWaitIdealProcessor=decoder.decodeInt8();const newThreadWaitTime=decoder.decodeUInt32();const reserved=decoder.decodeUInt32();return{newThreadId,oldThreadId,newThreadPriority,oldThreadPriority,previousCState,spareByte,oldThreadWaitReason,oldThreadWaitMode,oldThreadState,oldThreadWaitIdealProcessor,newThreadWaitTime,reserved};},decodeStart(header,decoder){const fields=this.decodeFields(header,decoder);this.importer.createThreadIfNeeded(fields.processId,fields.threadId);return true;},decodeEnd(header,decoder){const fields=this.decodeFields(header,decoder);this.importer.removeThreadIfPresent(fields.threadId);return true;},decodeDCStart(header,decoder){const fields=this.decodeFields(header,decoder);this.importer.createThreadIfNeeded(fields.processId,fields.threadId);return true;},decodeDCEnd(header,decoder){const fields=this.decodeFields(header,decoder);this.importer.removeThreadIfPresent(fields.threadId);return true;},decodeCSwitch(header,decoder){const fields=this.decodeCSwitchFields(header,decoder);const cpu=this.importer.getOrCreateCpu(header.cpu);const newThread=this.importer.getThreadFromWindowsTid(fields.newThreadId);let newThreadName;if(newThread&&newThread.userFriendlyName){newThreadName=newThread.userFriendlyName;}else{const newProcessId=this.importer.getPidFromWindowsTid(fields.newThreadId);const newProcess=this.model.getProcess(newProcessId);let newProcessName;if(newProcess){newProcessName=newProcess.name;}else{newProcessName='Unknown process';}
+newThreadName=newProcessName+' (tid '+fields.newThreadId+')';}
+cpu.switchActiveThread(header.timestamp,{},fields.newThreadId,newThreadName,fields);return true;}};Parser.register(ThreadParser);return{ThreadParser,};});'use strict';tr.exportTo('tr.b',function(){function max(a,b){if(a===undefined)return b;if(b===undefined)return a;return Math.max(a,b);}
+function IntervalTree(beginPositionCb,endPositionCb){this.beginPositionCb_=beginPositionCb;this.endPositionCb_=endPositionCb;this.root_=undefined;this.size_=0;}
+IntervalTree.prototype={insert(datum){const startPosition=this.beginPositionCb_(datum);const endPosition=this.endPositionCb_(datum);const node=new IntervalTreeNode(datum,startPosition,endPosition);this.size_++;this.root_=this.insertNode_(this.root_,node);this.root_.colour=Colour.BLACK;return datum;},insertNode_(root,node){if(root===undefined)return node;if(root.leftNode&&root.leftNode.isRed&&root.rightNode&&root.rightNode.isRed){this.flipNodeColour_(root);}
+if(node.key<root.key){root.leftNode=this.insertNode_(root.leftNode,node);}else if(node.key===root.key){root.merge(node);}else{root.rightNode=this.insertNode_(root.rightNode,node);}
+if(root.rightNode&&root.rightNode.isRed&&(root.leftNode===undefined||!root.leftNode.isRed)){root=this.rotateLeft_(root);}
+if(root.leftNode&&root.leftNode.isRed&&root.leftNode.leftNode&&root.leftNode.leftNode.isRed){root=this.rotateRight_(root);}
+return root;},rotateRight_(node){const sibling=node.leftNode;node.leftNode=sibling.rightNode;sibling.rightNode=node;sibling.colour=node.colour;node.colour=Colour.RED;return sibling;},rotateLeft_(node){const sibling=node.rightNode;node.rightNode=sibling.leftNode;sibling.leftNode=node;sibling.colour=node.colour;node.colour=Colour.RED;return sibling;},flipNodeColour_(node){node.colour=this.flipColour_(node.colour);node.leftNode.colour=this.flipColour_(node.leftNode.colour);node.rightNode.colour=this.flipColour_(node.rightNode.colour);},flipColour_(colour){return colour===Colour.RED?Colour.BLACK:Colour.RED;},updateHighValues(){this.updateHighValues_(this.root_);},updateHighValues_(node){if(node===undefined)return undefined;node.maxHighLeft=this.updateHighValues_(node.leftNode);node.maxHighRight=this.updateHighValues_(node.rightNode);return max(max(node.maxHighLeft,node.highValue),node.maxHighRight);},validateFindArguments_(queryLow,queryHigh){if(queryLow===undefined||queryHigh===undefined){throw new Error('queryLow and queryHigh must be defined');}
+if((typeof queryLow!=='number')||(typeof queryHigh!=='number')){throw new Error('queryLow and queryHigh must be numbers');}},findIntersection(queryLow,queryHigh){this.validateFindArguments_(queryLow,queryHigh);if(this.root_===undefined)return[];const ret=[];this.root_.appendIntersectionsInto_(ret,queryLow,queryHigh);return ret;},get size(){return this.size_;},get root(){return this.root_;},dump_(){if(this.root_===undefined)return[];return this.root_.dump();}};const Colour={RED:'red',BLACK:'black'};function IntervalTreeNode(datum,lowValue,highValue){this.lowValue_=lowValue;this.data_=[{datum,high:highValue,low:lowValue}];this.colour_=Colour.RED;this.parentNode_=undefined;this.leftNode_=undefined;this.rightNode_=undefined;this.maxHighLeft_=undefined;this.maxHighRight_=undefined;}
+IntervalTreeNode.prototype={appendIntersectionsInto_(ret,queryLow,queryHigh){if(this.lowValue_>=queryHigh){if(!this.leftNode_)return;return this.leftNode_.appendIntersectionsInto_(ret,queryLow,queryHigh);}
+if(this.maxHighLeft_>queryLow){this.leftNode_.appendIntersectionsInto_(ret,queryLow,queryHigh);}
+if(this.highValue>queryLow){for(let i=(this.data.length-1);i>=0;--i){if(this.data[i].high<queryLow)break;ret.push(this.data[i].datum);}}
+if(this.rightNode_){this.rightNode_.appendIntersectionsInto_(ret,queryLow,queryHigh);}},get colour(){return this.colour_;},set colour(colour){this.colour_=colour;},get key(){return this.lowValue_;},get lowValue(){return this.lowValue_;},get highValue(){return this.data_[this.data_.length-1].high;},set leftNode(left){this.leftNode_=left;},get leftNode(){return this.leftNode_;},get hasLeftNode(){return this.leftNode_!==undefined;},set rightNode(right){this.rightNode_=right;},get rightNode(){return this.rightNode_;},get hasRightNode(){return this.rightNode_!==undefined;},set parentNode(parent){this.parentNode_=parent;},get parentNode(){return this.parentNode_;},get isRootNode(){return this.parentNode_===undefined;},set maxHighLeft(high){this.maxHighLeft_=high;},get maxHighLeft(){return this.maxHighLeft_;},set maxHighRight(high){this.maxHighRight_=high;},get maxHighRight(){return this.maxHighRight_;},get data(){return this.data_;},get isRed(){return this.colour_===Colour.RED;},merge(node){for(let i=0;i<node.data.length;i++){this.data_.push(node.data[i]);}
+this.data_.sort(function(a,b){return a.high-b.high;});},dump(){const ret={};if(this.leftNode_){ret.left=this.leftNode_.dump();}
+ret.data=this.data_.map(function(d){return[d.low,d.high];});if(this.rightNode_){ret.right=this.rightNode_.dump();}
+return ret;}};return{IntervalTree,};});'use strict';tr.exportTo('tr.b.math',function(){const tmpVec2s=[];for(let i=0;i<8;i++){tmpVec2s[i]=vec2.create();}
+const tmpVec2a=vec4.create();const tmpVec4a=vec4.create();const tmpVec4b=vec4.create();const tmpMat4=mat4.create();const tmpMat4b=mat4.create();const p00=vec2.createXY(0,0);const p10=vec2.createXY(1,0);const p01=vec2.createXY(0,1);const p11=vec2.createXY(1,1);const lerpingVecA=vec2.create();const lerpingVecB=vec2.create();function lerpVec2(out,a,b,amt){vec2.scale(lerpingVecA,a,amt);vec2.scale(lerpingVecB,b,1-amt);vec2.add(out,lerpingVecA,lerpingVecB);vec2.normalize(out,out);return out;}
+function Quad(){this.p1=vec2.create();this.p2=vec2.create();this.p3=vec2.create();this.p4=vec2.create();}
+Quad.fromXYWH=function(x,y,w,h){const q=new Quad();vec2.set(q.p1,x,y);vec2.set(q.p2,x+w,y);vec2.set(q.p3,x+w,y+h);vec2.set(q.p4,x,y+h);return q;};Quad.fromRect=function(r){return new Quad.fromXYWH(r.x,r.y,r.width,r.height);};Quad.from4Vecs=function(p1,p2,p3,p4){const q=new Quad();vec2.set(q.p1,p1[0],p1[1]);vec2.set(q.p2,p2[0],p2[1]);vec2.set(q.p3,p3[0],p3[1]);vec2.set(q.p4,p4[0],p4[1]);return q;};Quad.from8Array=function(arr){if(arr.length!==8){throw new Error('Array must be 8 long');}
+const q=new Quad();q.p1[0]=arr[0];q.p1[1]=arr[1];q.p2[0]=arr[2];q.p2[1]=arr[3];q.p3[0]=arr[4];q.p3[1]=arr[5];q.p4[0]=arr[6];q.p4[1]=arr[7];return q;};Quad.prototype={pointInside(point){return pointInImplicitQuad(point,this.p1,this.p2,this.p3,this.p4);},boundingRect(){const x0=Math.min(this.p1[0],this.p2[0],this.p3[0],this.p4[0]);const y0=Math.min(this.p1[1],this.p2[1],this.p3[1],this.p4[1]);const x1=Math.max(this.p1[0],this.p2[0],this.p3[0],this.p4[0]);const y1=Math.max(this.p1[1],this.p2[1],this.p3[1],this.p4[1]);return new tr.b.math.Rect.fromXYWH(x0,y0,x1-x0,y1-y0);},clone(){const q=new Quad();vec2.copy(q.p1,this.p1);vec2.copy(q.p2,this.p2);vec2.copy(q.p3,this.p3);vec2.copy(q.p4,this.p4);return q;},scale(s){const q=new Quad();this.scaleFast(q,s);return q;},scaleFast(dstQuad,s){vec2.copy(dstQuad.p1,this.p1,s);vec2.copy(dstQuad.p2,this.p2,s);vec2.copy(dstQuad.p3,this.p3,s);vec2.copy(dstQuad.p3,this.p3,s);},isRectangle(){const bounds=this.boundingRect();return(bounds.x===this.p1[0]&&bounds.y===this.p1[1]&&bounds.width===this.p2[0]-this.p1[0]&&bounds.y===this.p2[1]&&bounds.width===this.p3[0]-this.p1[0]&&bounds.height===this.p3[1]-this.p2[1]&&bounds.x===this.p4[0]&&bounds.height===this.p4[1]-this.p2[1]);},projectUnitRect(rect){const q=new Quad();this.projectUnitRectFast(q,rect);return q;},projectUnitRectFast(dstQuad,rect){const v12=tmpVec2s[0];const v14=tmpVec2s[1];const v23=tmpVec2s[2];const v43=tmpVec2s[3];vec2.sub(v12,this.p2,this.p1);const l12=vec2.length(v12);vec2.scale(v12,v12,1/l12);vec2.sub(v14,this.p4,this.p1);const l14=vec2.length(v14);vec2.scale(v14,v14,1/l14);vec2.sub(v23,this.p3,this.p2);const l23=vec2.length(v23);vec2.scale(v23,v23,1/l23);vec2.sub(v43,this.p3,this.p4);const l43=vec2.length(v43);vec2.scale(v43,v43,1/l43);const b12=tmpVec2s[0];const b14=tmpVec2s[1];const b23=tmpVec2s[2];const b43=tmpVec2s[3];lerpVec2(b12,v12,v43,rect.y);lerpVec2(b43,v12,v43,1-rect.bottom);lerpVec2(b14,v14,v23,rect.x);lerpVec2(b23,v14,v23,1-rect.right);vec2.addTwoScaledUnitVectors(tmpVec2a,b12,l12*rect.x,b14,l14*rect.y);vec2.add(dstQuad.p1,this.p1,tmpVec2a);vec2.addTwoScaledUnitVectors(tmpVec2a,b12,l12*-(1.0-rect.right),b23,l23*rect.y);vec2.add(dstQuad.p2,this.p2,tmpVec2a);vec2.addTwoScaledUnitVectors(tmpVec2a,b43,l43*-(1.0-rect.right),b23,l23*-(1.0-rect.bottom));vec2.add(dstQuad.p3,this.p3,tmpVec2a);vec2.addTwoScaledUnitVectors(tmpVec2a,b43,l43*rect.left,b14,l14*-(1.0-rect.bottom));vec2.add(dstQuad.p4,this.p4,tmpVec2a);},toString(){return'Quad('+
+vec2.toString(this.p1)+', '+
+vec2.toString(this.p2)+', '+
+vec2.toString(this.p3)+', '+
+vec2.toString(this.p4)+')';}};function sign(p1,p2,p3){return(p1[0]-p3[0])*(p2[1]-p3[1])-
+(p2[0]-p3[0])*(p1[1]-p3[1]);}
+function pointInTriangle2(pt,p1,p2,p3){const b1=sign(pt,p1,p2)<0.0;const b2=sign(pt,p2,p3)<0.0;const b3=sign(pt,p3,p1)<0.0;return((b1===b2)&&(b2===b3));}
+function pointInImplicitQuad(point,p1,p2,p3,p4){return pointInTriangle2(point,p1,p2,p3)||pointInTriangle2(point,p1,p3,p4);}
+return{pointInTriangle2,pointInImplicitQuad,Quad,};});'use strict';tr.exportTo('tr.b',function(){const ESTIMATED_IDLE_PERIOD_LENGTH_MILLISECONDS=10;const REQUEST_IDLE_CALLBACK_TIMEOUT_MILLISECONDS=100;const recordRAFStacks=false;let pendingPreAFs=[];let pendingRAFs=[];const pendingIdleCallbacks=[];let currentRAFDispatchList=undefined;let rafScheduled=false;let idleWorkScheduled=false;function scheduleRAF(){if(rafScheduled)return;rafScheduled=true;if(tr.isHeadless){Promise.resolve().then(function(){processRequests(false,0);},function(e){throw e;});}else{if(window.requestAnimationFrame){window.requestAnimationFrame(processRequests.bind(this,false));}else{const delta=Date.now()-window.performance.now();window.webkitRequestAnimationFrame(function(domTimeStamp){processRequests(false,domTimeStamp-delta);});}}}
+function nativeRequestIdleCallbackSupported(){return!tr.isHeadless&&window.requestIdleCallback;}
+function scheduleIdleWork(){if(idleWorkScheduled)return;if(!nativeRequestIdleCallbackSupported()){scheduleRAF();return;}
+idleWorkScheduled=true;window.requestIdleCallback(function(deadline,didTimeout){processIdleWork(false,deadline);},{timeout:REQUEST_IDLE_CALLBACK_TIMEOUT_MILLISECONDS});}
+function onAnimationFrameError(e,opt_stack){console.log(e.stack);if(tr.isHeadless)throw e;if(opt_stack)console.log(opt_stack);if(e.message){console.error(e.message,e.stack);}else{console.error(e);}}
+function runTask(task,frameBeginTime){try{task.callback.call(task.context,frameBeginTime);}catch(e){tr.b.onAnimationFrameError(e,task.stack);}}
+function processRequests(forceAllTasksToRun,frameBeginTime){rafScheduled=false;const currentPreAFs=pendingPreAFs;currentRAFDispatchList=pendingRAFs;pendingPreAFs=[];pendingRAFs=[];const hasRAFTasks=currentPreAFs.length||currentRAFDispatchList.length;for(let i=0;i<currentPreAFs.length;i++){runTask(currentPreAFs[i],frameBeginTime);}
+while(currentRAFDispatchList.length>0){runTask(currentRAFDispatchList.shift(),frameBeginTime);}
+currentRAFDispatchList=undefined;if((!hasRAFTasks&&!nativeRequestIdleCallbackSupported())||forceAllTasksToRun){const rafCompletionDeadline=frameBeginTime+ESTIMATED_IDLE_PERIOD_LENGTH_MILLISECONDS;processIdleWork(forceAllTasksToRun,{timeRemaining(){return rafCompletionDeadline-window.performance.now();}});}
+if(pendingIdleCallbacks.length>0)scheduleIdleWork();}
+function processIdleWork(forceAllTasksToRun,deadline){idleWorkScheduled=false;while(pendingIdleCallbacks.length>0){runTask(pendingIdleCallbacks.shift());if(!forceAllTasksToRun&&(tr.isHeadless||deadline.timeRemaining()<=0)){break;}}
+if(pendingIdleCallbacks.length>0)scheduleIdleWork();}
+function getStack_(){if(!recordRAFStacks)return'';const stackLines=tr.b.stackTrace();stackLines.shift();return stackLines.join('\n');}
+function requestPreAnimationFrame(callback,opt_this){pendingPreAFs.push({callback,context:opt_this||global,stack:getStack_()});scheduleRAF();}
+function requestAnimationFrameInThisFrameIfPossible(callback,opt_this){if(!currentRAFDispatchList){requestAnimationFrame(callback,opt_this);return;}
+currentRAFDispatchList.push({callback,context:opt_this||global,stack:getStack_()});return;}
+function requestAnimationFrame(callback,opt_this){pendingRAFs.push({callback,context:opt_this||global,stack:getStack_()});scheduleRAF();}
+function animationFrame(){return new Promise(resolve=>requestAnimationFrame(resolve));}
+function requestIdleCallback(callback,opt_this){pendingIdleCallbacks.push({callback,context:opt_this||global,stack:getStack_()});scheduleIdleWork();}
+function forcePendingRAFTasksToRun(frameBeginTime){if(!rafScheduled)return;processRequests(false,frameBeginTime);}
+function forceAllPendingTasksToRunForTest(){if(!rafScheduled&&!idleWorkScheduled)return;processRequests(true,0);}
+function timeout(ms){return new Promise(resolve=>window.setTimeout(resolve,ms));}
+function idle(){return new Promise(resolve=>requestIdleCallback(resolve));}
+return{animationFrame,forceAllPendingTasksToRunForTest,forcePendingRAFTasksToRun,idle,onAnimationFrameError,requestAnimationFrame,requestAnimationFrameInThisFrameIfPossible,requestIdleCallback,requestPreAnimationFrame,timeout,};});'use strict';tr.exportTo('tr.b',function(){class Mark{constructor(groupName,functionName,opt_timestamp){if(tr.isHeadless)return;this.groupName_=groupName;this.functionName_=functionName;const guid=tr.b.GUID.allocateSimple();this.measureName_=`${groupName} ${functionName}`;if(opt_timestamp){this.startMark_={startTime:opt_timestamp};}else{this.startMarkName_=`${this.measureName} ${guid} start`;}
+this.endMark_=undefined;this.endMarkName_=`${this.measureName} ${guid} end`;window.performance.mark(this.startMarkName_);}
+get groupName(){return this.groupName_;}
+get functionName(){return this.functionName_;}
+get measureName(){return this.measureName_;}
+get startMark(){return this.startMark_||tr.b.getOnlyElement(window.performance.getEntriesByName(this.startMarkName_));}
+get endMark(){return this.endMark_||tr.b.getOnlyElement(window.performance.getEntriesByName(this.endMarkName_));}
+get durationMs(){return this.endMark.startTime-this.startMark.startTime;}
+end(opt_timestamp){if(tr.isHeadless)return;if(opt_timestamp){this.endMark_={startTime:opt_timestamp};}else{window.performance.mark(this.endMarkName_);}
+if(!this.startMark_&&!this.endMark_){window.performance.measure(this.measureName_,this.startMarkName_,this.endMarkName_);}else if(Timing.logVoidMarks&&!(window.ga instanceof Function)){console.log('void mark',this.groupName,this.functionName,this.durationMs);}
+if(!(window.ga instanceof Function))return;ga('send',{hitType:'event',eventCategory:this.groupName,eventAction:this.functionName,eventValue:this.durationMs,});}}
+class Timing{static mark(groupName,functionName,opt_timestamp){return new Mark(groupName,functionName,opt_timestamp);}
+static instant(groupName,functionName,opt_value){const valueString=opt_value===undefined?'':' '+opt_value;if(console&&console.timeStamp){console.timeStamp(`${groupName} ${functionName}${valueString}`);}
+if(window&&window.ga instanceof Function){ga('send',{hitType:'event',eventCategory:groupName,eventAction:functionName,eventValue:opt_value,});}}
+static getCurrentTimeMs(){try{return performance.now();}catch(error){}
+return 0;}}
+Timing.logVoidMarks=false;return{Timing,};});'use strict';tr.exportTo('tr.b',function(){const Timing=tr.b.Timing;function Task(runCb,thisArg){if(runCb!==undefined&&thisArg===undefined&&runCb.prototype!==undefined){throw new Error('Almost certainly you meant to pass a bound callback '+'or thisArg.');}
+this.runCb_=runCb;this.thisArg_=thisArg;this.afterTask_=undefined;this.subTasks_=[];this.updatesUi_=false;}
+Task.prototype={get name(){return this.runCb_.name;},set updatesUi(value){this.updatesUi_=value;},subTask(cb,thisArg){if(cb instanceof Task){this.subTasks_.push(cb);}else{this.subTasks_.push(new Task(cb,thisArg));}
+return this.subTasks_[this.subTasks_.length-1];},run(){if(this.runCb_!==undefined)this.runCb_.call(this.thisArg_,this);const subTasks=this.subTasks_;this.subTasks_=undefined;if(!subTasks.length)return this.afterTask_;for(let i=1;i<subTasks.length;i++){subTasks[i-1].afterTask_=subTasks[i];}
+subTasks[subTasks.length-1].afterTask_=this.afterTask_;return subTasks[0];},after(cb,thisArg){if(this.afterTask_){throw new Error('Has an after task already');}
+if(cb instanceof Task){this.afterTask_=cb;}else{this.afterTask_=new Task(cb,thisArg);}
+return this.afterTask_;},enqueue(cb,thisArg){if(!this.afterTask_)return this.after(cb,thisArg);return this.afterTask_.enqueue(cb,thisArg);}};Task.RunSynchronously=function(task){let curTask=task;while(curTask){curTask=curTask.run();}};Task.RunWhenIdle=function(task){return new Promise(function(resolve,reject){let curTask=task;function runAnother(){try{curTask=curTask.run();}catch(e){reject(e);return;}
+if(curTask){if(curTask.updatesUi_){tr.b.requestAnimationFrameInThisFrameIfPossible(runAnother);}else{tr.b.requestIdleCallback(runAnother);}
+return;}
+resolve();}
+tr.b.requestIdleCallback(runAnother);});};return{Task,};});'use strict';tr.exportTo('tr.c',function(){function makeCaseInsensitiveRegex(pattern){pattern=pattern.replace(/[.*+?^${}()|[\]\\]/g,'\\$&');return new RegExp(pattern,'i');}
+function Filter(){}
+Filter.prototype={__proto__:Object.prototype,matchCounter(counter){return true;},matchCpu(cpu){return true;},matchProcess(process){return true;},matchSlice(slice){return true;},matchThread(thread){return true;}};function TitleOrCategoryFilter(text){Filter.call(this);this.regex_=makeCaseInsensitiveRegex(text);if(!text.length){throw new Error('Filter text is empty.');}}
+TitleOrCategoryFilter.prototype={__proto__:Filter.prototype,matchSlice(slice){if(slice.title===undefined&&slice.category===undefined){return false;}
+return this.regex_.test(slice.title)||(!!slice.category&&this.regex_.test(slice.category));}};function ExactTitleFilter(text){Filter.call(this);this.text_=text;if(!text.length){throw new Error('Filter text is empty.');}}
+ExactTitleFilter.prototype={__proto__:Filter.prototype,matchSlice(slice){return slice.title===this.text_;}};function FullTextFilter(text){Filter.call(this);this.regex_=makeCaseInsensitiveRegex(text);this.titleOrCategoryFilter_=new TitleOrCategoryFilter(text);}
+FullTextFilter.prototype={__proto__:Filter.prototype,matchObject_(obj){for(const key in obj){if(!obj.hasOwnProperty(key))continue;if(this.regex_.test(key))return true;if(this.regex_.test(obj[key]))return true;}
+return false;},matchSlice(slice){if(this.titleOrCategoryFilter_.matchSlice(slice))return true;return this.matchObject_(slice.args);}};return{Filter,TitleOrCategoryFilter,ExactTitleFilter,FullTextFilter,};});'use strict';tr.exportTo('tr.model',function(){const ClockDomainId={BATTOR:'BATTOR',UNKNOWN_CHROME_LEGACY:'UNKNOWN_CHROME_LEGACY',LINUX_CLOCK_MONOTONIC:'LINUX_CLOCK_MONOTONIC',LINUX_FTRACE_GLOBAL:'LINUX_FTRACE_GLOBAL',MAC_MACH_ABSOLUTE_TIME:'MAC_MACH_ABSOLUTE_TIME',WIN_ROLLOVER_PROTECTED_TIME_GET_TIME:'WIN_ROLLOVER_PROTECTED_TIME_GET_TIME',WIN_QPC:'WIN_QPC',SYSTRACE:'SYSTRACE',TELEMETRY:'TELEMETRY'};const POSSIBLE_CHROME_CLOCK_DOMAINS=new Set([ClockDomainId.UNKNOWN_CHROME_LEGACY,ClockDomainId.LINUX_CLOCK_MONOTONIC,ClockDomainId.MAC_MACH_ABSOLUTE_TIME,ClockDomainId.WIN_ROLLOVER_PROTECTED_TIME_GET_TIME,ClockDomainId.WIN_QPC]);const BATTOR_FAST_SYNC_THRESHOLD_MS=3;function ClockSyncManager(){this.domainsSeen_=new Set();this.markersBySyncId_=new Map();this.transformerMapByDomainId_={};}
+ClockSyncManager.prototype={addClockSyncMarker(domainId,syncId,startTs,opt_endTs){this.onDomainSeen_(domainId);if(Object.values(ClockDomainId).indexOf(domainId)<0){throw new Error('"'+domainId+'" is not in the list of known '+'clock domain IDs.');}
+if(this.modelDomainId_){throw new Error('Cannot add new clock sync markers after getting '+'a model time transformer.');}
+const marker=new ClockSyncMarker(domainId,startTs,opt_endTs);if(!this.markersBySyncId_.has(syncId)){this.markersBySyncId_.set(syncId,[marker]);return;}
+const markers=this.markersBySyncId_.get(syncId);if(markers.length===2){throw new Error('Clock sync with ID "'+syncId+'" is already '+'complete - cannot add a third clock sync marker to it.');}
+if(markers[0].domainId===domainId){throw new Error('A clock domain cannot sync with itself.');}
+markers.push(marker);this.onSyncCompleted_(markers[0],marker);},get completeSyncIds(){const completeSyncIds=[];for(const[syncId,markers]of this.markersBySyncId){if(markers.length===2)completeSyncIds.push(syncId);}
+return completeSyncIds;},get markersBySyncId(){return this.markersBySyncId_;},get domainsSeen(){return this.domainsSeen_;},getModelTimeTransformer(domainId){this.onDomainSeen_(domainId);if(!this.modelDomainId_){this.selectModelDomainId_();}
+return this.getTimeTransformerRaw_(domainId,this.modelDomainId_).fn;},getTimeTransformerError(fromDomainId,toDomainId){this.onDomainSeen_(fromDomainId);this.onDomainSeen_(toDomainId);return this.getTimeTransformerRaw_(fromDomainId,toDomainId).error;},getTimeTransformerRaw_(fromDomainId,toDomainId){const transformer=this.getTransformerBetween_(fromDomainId,toDomainId);if(!transformer){throw new Error('No clock sync markers exist pairing clock domain "'+
+fromDomainId+'" '+'with target clock domain "'+
+toDomainId+'".');}
+return transformer;},getTransformerBetween_(fromDomainId,toDomainId){const visitedDomainIds=new Set();const queue=[{domainId:fromDomainId,transformer:Transformer.IDENTITY}];while(queue.length>0){queue.sort((domain1,domain2)=>domain1.transformer.error-domain2.transformer.error);const current=queue.shift();if(current.domainId===toDomainId){return current.transformer;}
+if(visitedDomainIds.has(current.domainId)){continue;}
+visitedDomainIds.add(current.domainId);const outgoingTransformers=this.transformerMapByDomainId_[current.domainId];if(!outgoingTransformers)continue;for(const outgoingDomainId in outgoingTransformers){const toNextDomainTransformer=outgoingTransformers[outgoingDomainId];const toCurrentDomainTransformer=current.transformer;queue.push({domainId:outgoingDomainId,transformer:Transformer.compose(toNextDomainTransformer,toCurrentDomainTransformer)});}}
+return undefined;},selectModelDomainId_(){this.ensureAllDomainsAreConnected_();for(const chromeDomainId of POSSIBLE_CHROME_CLOCK_DOMAINS){if(this.domainsSeen_.has(chromeDomainId)){this.modelDomainId_=chromeDomainId;return;}}
+const domainsSeenArray=Array.from(this.domainsSeen_);domainsSeenArray.sort();this.modelDomainId_=domainsSeenArray[0];},ensureAllDomainsAreConnected_(){let firstDomainId=undefined;for(const domainId of this.domainsSeen_){if(!firstDomainId){firstDomainId=domainId;continue;}
+if(!this.getTransformerBetween_(firstDomainId,domainId)){throw new Error('Unable to select a master clock domain because no '+'path can be found from "'+firstDomainId+'" to "'+domainId+'".');}}
+return true;},onDomainSeen_(domainId){if(domainId===ClockDomainId.UNKNOWN_CHROME_LEGACY&&!this.domainsSeen_.has(ClockDomainId.UNKNOWN_CHROME_LEGACY)){for(const chromeDomainId of POSSIBLE_CHROME_CLOCK_DOMAINS){if(chromeDomainId===ClockDomainId.UNKNOWN_CHROME_LEGACY){continue;}
+this.collapseDomains_(ClockDomainId.UNKNOWN_CHROME_LEGACY,chromeDomainId);}}
+this.domainsSeen_.add(domainId);},onSyncCompleted_(marker1,marker2){const forwardTransformer=Transformer.fromMarkers(marker1,marker2);const backwardTransformer=Transformer.fromMarkers(marker2,marker1);const existingTransformer=this.getOrCreateTransformerMap_(marker1.domainId)[marker2.domainId];if(!existingTransformer||forwardTransformer.error<existingTransformer.error){this.getOrCreateTransformerMap_(marker1.domainId)[marker2.domainId]=forwardTransformer;this.getOrCreateTransformerMap_(marker2.domainId)[marker1.domainId]=backwardTransformer;}},collapseDomains_(domain1Id,domain2Id){this.getOrCreateTransformerMap_(domain1Id)[domain2Id]=this.getOrCreateTransformerMap_(domain2Id)[domain1Id]=Transformer.IDENTITY;},getOrCreateTransformerMap_(domainId){if(!this.transformerMapByDomainId_[domainId]){this.transformerMapByDomainId_[domainId]={};}
+return this.transformerMapByDomainId_[domainId];},computeDotGraph(){let dotString='graph {\n';const domainsSeen=[...this.domainsSeen_].sort();for(const domainId of domainsSeen){dotString+=`  ${domainId}[shape=box]\n`;}
+const markersBySyncIdEntries=[...this.markersBySyncId_.entries()].sort(([syncId1,markers1],[syncId2,markers2])=>syncId1.localeCompare(syncId2));for(const[syncId,markers]of markersBySyncIdEntries){const sortedMarkers=markers.sort((a,b)=>a.domainId.localeCompare(b.domainId));for(const m of markers){dotString+=`  "${syncId}" -- ${m.domainId} `;dotString+=`[label="[${m.startTs}, ${m.endTs}]"]\n`;}}
+dotString+='}';return dotString;}};function ClockSyncMarker(domainId,startTs,opt_endTs){this.domainId=domainId;this.startTs=startTs;this.endTs=opt_endTs===undefined?startTs:opt_endTs;}
+ClockSyncMarker.prototype={get duration(){return this.endTs-this.startTs;},get ts(){return this.startTs+this.duration/2;}};function Transformer(fn,error){this.fn=fn;this.error=error;}
+Transformer.IDENTITY=new Transformer((x=>x),0);Transformer.compose=function(aToB,bToC){return new Transformer((ts)=>bToC.fn(aToB.fn(ts)),aToB.error+bToC.error);};Transformer.fromMarkers=function(fromMarker,toMarker){let fromTs=fromMarker.ts;let toTs=toMarker.ts;if(fromMarker.domainId===ClockDomainId.BATTOR&&toMarker.duration>BATTOR_FAST_SYNC_THRESHOLD_MS){toTs=toMarker.startTs;}else if(toMarker.domainId===ClockDomainId.BATTOR&&fromMarker.duration>BATTOR_FAST_SYNC_THRESHOLD_MS){fromTs=fromMarker.startTs;}
+const tsShift=toTs-fromTs;return new Transformer((ts)=>ts+tsShift,fromMarker.duration+toMarker.duration);};return{ClockDomainId,ClockSyncManager,};});'use strict';tr.exportTo('tr.model',function(){function CounterSample(series,timestamp,value){tr.model.Event.call(this);this.series_=series;this.timestamp_=timestamp;this.value_=value;}
+CounterSample.groupByTimestamp=function(samples){const samplesByTimestamp=tr.b.groupIntoMap(samples,s=>s.timestamp);const timestamps=Array.from(samplesByTimestamp.keys());timestamps.sort();const groups=[];for(const ts of timestamps){const group=samplesByTimestamp.get(ts);group.sort((x,y)=>x.series.seriesIndex-y.series.seriesIndex);groups.push(group);}
+return groups;};CounterSample.prototype={__proto__:tr.model.Event.prototype,get series(){return this.series_;},get timestamp(){return this.timestamp_;},get value(){return this.value_;},set timestamp(timestamp){this.timestamp_=timestamp;},addBoundsToRange(range){range.addValue(this.timestamp);},getSampleIndex(){return tr.b.findLowIndexInSortedArray(this.series.timestamps,function(x){return x;},this.timestamp_);},get userFriendlyName(){return'Counter sample from '+this.series_.title+' at '+
+tr.b.Unit.byName.timeStampInMs.format(this.timestamp);}};tr.model.EventRegistry.register(CounterSample,{name:'counterSample',pluralName:'counterSamples'});return{CounterSample,};});'use strict';tr.exportTo('tr.model',function(){const CounterSample=tr.model.CounterSample;function CounterSeries(name,color){tr.model.EventContainer.call(this);this.name_=name;this.color_=color;this.timestamps_=[];this.samples_=[];this.counter=undefined;this.seriesIndex=undefined;}
+CounterSeries.prototype={__proto__:tr.model.EventContainer.prototype,get length(){return this.timestamps_.length;},get name(){return this.name_;},get color(){return this.color_;},get samples(){return this.samples_;},get timestamps(){return this.timestamps_;},getSample(idx){return this.samples_[idx];},getTimestamp(idx){return this.timestamps_[idx];},addCounterSample(ts,val){const sample=new CounterSample(this,ts,val);this.addSample(sample);return sample;},addSample(sample){this.timestamps_.push(sample.timestamp);this.samples_.push(sample);},getStatistics(sampleIndices){let sum=0;let min=Number.MAX_VALUE;let max=-Number.MAX_VALUE;for(let i=0;i<sampleIndices.length;++i){const sample=this.getSample(sampleIndices[i]).value;sum+=sample;min=Math.min(sample,min);max=Math.max(sample,max);}
+return{min,max,avg:(sum/sampleIndices.length),start:this.getSample(sampleIndices[0]).value,end:this.getSample(sampleIndices.length-1).value};},shiftTimestampsForward(amount){for(let i=0;i<this.timestamps_.length;++i){this.timestamps_[i]+=amount;this.samples_[i].timestamp=this.timestamps_[i];}},*childEvents(){yield*this.samples_;},*childEventContainers(){}};return{CounterSeries,};});'use strict';tr.exportTo('tr.model',function(){function Counter(parent,id,category,name){tr.model.EventContainer.call(this);this.parent_=parent;this.id_=id;this.category_=category||'';this.name_=name;this.series_=[];this.totals=[];}
+Counter.prototype={__proto__:tr.model.EventContainer.prototype,get parent(){return this.parent_;},get id(){return this.id_;},get category(){return this.category_;},get name(){return this.name_;},*childEvents(){},*childEventContainers(){yield*this.series;},set timestamps(arg){throw new Error('Bad counter API. No cookie.');},set seriesNames(arg){throw new Error('Bad counter API. No cookie.');},set seriesColors(arg){throw new Error('Bad counter API. No cookie.');},set samples(arg){throw new Error('Bad counter API. No cookie.');},addSeries(series){series.counter=this;series.seriesIndex=this.series_.length;this.series_.push(series);return series;},getSeries(idx){return this.series_[idx];},get series(){return this.series_;},get numSeries(){return this.series_.length;},get numSamples(){if(this.series_.length===0)return 0;return this.series_[0].length;},get timestamps(){if(this.series_.length===0)return[];return this.series_[0].timestamps;},getSampleStatistics(sampleIndices){sampleIndices.sort();const ret=[];this.series_.forEach(function(series){ret.push(series.getStatistics(sampleIndices));});return ret;},shiftTimestampsForward(amount){for(let i=0;i<this.series_.length;++i){this.series_[i].shiftTimestampsForward(amount);}},updateBounds(){this.totals=[];this.maxTotal=0;this.bounds.reset();if(this.series_.length===0)return;const firstSeries=this.series_[0];const lastSeries=this.series_[this.series_.length-1];this.bounds.addValue(firstSeries.getTimestamp(0));this.bounds.addValue(lastSeries.getTimestamp(lastSeries.length-1));const numSeries=this.numSeries;this.maxTotal=-Infinity;for(let i=0;i<firstSeries.length;++i){let total=0;this.series_.forEach(function(series){total+=series.getSample(i).value;this.totals.push(total);}.bind(this));this.maxTotal=Math.max(total,this.maxTotal);}}};Counter.compare=function(x,y){let tmp=x.parent.compareTo(y.parent);if(tmp!==0)return tmp;tmp=x.name.localeCompare(y.name);if(tmp===0)return x.tid-y.tid;return tmp;};return{Counter,};});'use strict';tr.exportTo('tr.model',function(){const Slice=tr.model.Slice;function CpuSlice(cat,title,colorId,start,args,opt_duration){Slice.apply(this,arguments);this.threadThatWasRunning=undefined;this.cpu=undefined;}
+CpuSlice.prototype={__proto__:Slice.prototype,get analysisTypeName(){return'tr.ui.analysis.CpuSlice';},getAssociatedTimeslice(){if(!this.threadThatWasRunning){return undefined;}
+const timeSlices=this.threadThatWasRunning.timeSlices;for(let i=0;i<timeSlices.length;i++){const timeSlice=timeSlices[i];if(timeSlice.start!==this.start){continue;}
+if(timeSlice.duration!==this.duration){continue;}
+return timeSlice;}
+return undefined;}};tr.model.EventRegistry.register(CpuSlice,{name:'cpuSlice',pluralName:'cpuSlices'});return{CpuSlice,};});'use strict';tr.exportTo('tr.model',function(){function TimeToObjectInstanceMap(createObjectInstanceFunction,parent,scopedId){this.createObjectInstanceFunction_=createObjectInstanceFunction;this.parent=parent;this.scopedId=scopedId;this.instances=[];}
+TimeToObjectInstanceMap.prototype={idWasCreated(category,name,ts){if(this.instances.length===0){this.instances.push(this.createObjectInstanceFunction_(this.parent,this.scopedId,category,name,ts));this.instances[0].creationTsWasExplicit=true;return this.instances[0];}
+let lastInstance=this.instances[this.instances.length-1];if(ts<lastInstance.deletionTs){throw new Error('Mutation of the TimeToObjectInstanceMap must be '+'done in ascending timestamp order.');}
+lastInstance=this.createObjectInstanceFunction_(this.parent,this.scopedId,category,name,ts);lastInstance.creationTsWasExplicit=true;this.instances.push(lastInstance);return lastInstance;},addSnapshot(category,name,ts,args,opt_baseTypeName){if(this.instances.length===0){this.instances.push(this.createObjectInstanceFunction_(this.parent,this.scopedId,category,name,ts,opt_baseTypeName));}
+const i=tr.b.findIndexInSortedIntervals(this.instances,function(inst){return inst.creationTs;},function(inst){return inst.deletionTs-inst.creationTs;},ts);let instance;if(i<0){instance=this.instances[0];if(ts>instance.deletionTs||instance.creationTsWasExplicit){throw new Error('At the provided timestamp, no instance was still alive');}
+if(instance.snapshots.length!==0){throw new Error('Cannot shift creationTs forward, '+'snapshots have been added. First snap was at ts='+
+instance.snapshots[0].ts+' and creationTs was '+
+instance.creationTs);}
+instance.creationTs=ts;}else if(i>=this.instances.length){instance=this.instances[this.instances.length-1];if(ts>=instance.deletionTs){instance=this.createObjectInstanceFunction_(this.parent,this.scopedId,category,name,ts,opt_baseTypeName);this.instances.push(instance);}else{let lastValidIndex;for(let i=this.instances.length-1;i>=0;i--){const tmp=this.instances[i];if(ts>=tmp.deletionTs)break;if(tmp.creationTsWasExplicit===false&&tmp.snapshots.length===0){lastValidIndex=i;}}
+if(lastValidIndex===undefined){throw new Error('Cannot add snapshot. No instance was alive that was mutable.');}
+instance=this.instances[lastValidIndex];instance.creationTs=ts;}}else{instance=this.instances[i];}
+return instance.addSnapshot(ts,args,name,opt_baseTypeName);},get lastInstance(){if(this.instances.length===0)return undefined;return this.instances[this.instances.length-1];},idWasDeleted(category,name,ts){if(this.instances.length===0){this.instances.push(this.createObjectInstanceFunction_(this.parent,this.scopedId,category,name,ts));}
+let lastInstance=this.instances[this.instances.length-1];if(ts<lastInstance.creationTs){throw new Error('Cannot delete an id before it was created');}
+if(lastInstance.deletionTs===Number.MAX_VALUE){lastInstance.wasDeleted(ts);return lastInstance;}
+if(ts<lastInstance.deletionTs){throw new Error('id was already deleted earlier.');}
+lastInstance=this.createObjectInstanceFunction_(this.parent,this.scopedId,category,name,ts);this.instances.push(lastInstance);lastInstance.wasDeleted(ts);return lastInstance;},getInstanceAt(ts){const i=tr.b.findIndexInSortedIntervals(this.instances,function(inst){return inst.creationTs;},function(inst){return inst.deletionTs-inst.creationTs;},ts);if(i<0){if(this.instances[0].creationTsWasExplicit){return undefined;}
+return this.instances[0];}else if(i>=this.instances.length){return undefined;}
+return this.instances[i];}};return{TimeToObjectInstanceMap,};});'use strict';tr.exportTo('tr.model',function(){const ObjectInstance=tr.model.ObjectInstance;const ObjectSnapshot=tr.model.ObjectSnapshot;function ObjectCollection(parent){tr.model.EventContainer.call(this);this.parent=parent;this.instanceMapsByScopedId_={};this.instancesByTypeName_={};this.createObjectInstance_=this.createObjectInstance_.bind(this);}
+ObjectCollection.prototype={__proto__:tr.model.EventContainer.prototype,*childEvents(){for(const instance of this.getAllObjectInstances()){yield instance;yield*instance.snapshots;}},createObjectInstance_(parent,scopedId,category,name,creationTs,opt_baseTypeName){const constructor=tr.model.ObjectInstance.subTypes.getConstructor(category,name);const instance=new constructor(parent,scopedId,category,name,creationTs,opt_baseTypeName);const typeName=instance.typeName;let instancesOfTypeName=this.instancesByTypeName_[typeName];if(!instancesOfTypeName){instancesOfTypeName=[];this.instancesByTypeName_[typeName]=instancesOfTypeName;}
+instancesOfTypeName.push(instance);return instance;},getOrCreateInstanceMap_(scopedId){let dict;if(scopedId.scope in this.instanceMapsByScopedId_){dict=this.instanceMapsByScopedId_[scopedId.scope];}else{dict={};this.instanceMapsByScopedId_[scopedId.scope]=dict;}
+let instanceMap=dict[scopedId.id];if(instanceMap)return instanceMap;instanceMap=new tr.model.TimeToObjectInstanceMap(this.createObjectInstance_,this.parent,scopedId);dict[scopedId.id]=instanceMap;return instanceMap;},idWasCreated(scopedId,category,name,ts){const instanceMap=this.getOrCreateInstanceMap_(scopedId);return instanceMap.idWasCreated(category,name,ts);},addSnapshot(scopedId,category,name,ts,args,opt_baseTypeName){const instanceMap=this.getOrCreateInstanceMap_(scopedId);const snapshot=instanceMap.addSnapshot(category,name,ts,args,opt_baseTypeName);if(snapshot.objectInstance.category!==category){const msg='Added snapshot name='+name+' with cat='+category+' impossible. It instance was created/snapshotted with cat='+
+snapshot.objectInstance.category+' name='+
+snapshot.objectInstance.name;throw new Error(msg);}
+if(opt_baseTypeName&&snapshot.objectInstance.baseTypeName!==opt_baseTypeName){throw new Error('Could not add snapshot with baseTypeName='+
+opt_baseTypeName+'. It '+'was previously created with name='+
+snapshot.objectInstance.baseTypeName);}
+if(snapshot.objectInstance.name!==name){throw new Error('Could not add snapshot with name='+name+'. It '+'was previously created with name='+
+snapshot.objectInstance.name);}
+return snapshot;},idWasDeleted(scopedId,category,name,ts){const instanceMap=this.getOrCreateInstanceMap_(scopedId);const deletedInstance=instanceMap.idWasDeleted(category,name,ts);if(!deletedInstance)return;if(deletedInstance.category!==category){const msg='Deleting object '+deletedInstance.name+' with a different category '+'than when it was created. It previous had cat='+
+deletedInstance.category+' but the delete command '+'had cat='+category;throw new Error(msg);}
+if(deletedInstance.baseTypeName!==name){throw new Error('Deletion requested for name='+
+name+' could not proceed: '+'An existing object with baseTypeName='+
+deletedInstance.baseTypeName+' existed.');}},autoDeleteObjects(maxTimestamp){for(const imapById of Object.values(this.instanceMapsByScopedId_)){for(const i2imap of Object.values(imapById)){const lastInstance=i2imap.lastInstance;if(lastInstance.deletionTs!==Number.MAX_VALUE)continue;i2imap.idWasDeleted(lastInstance.category,lastInstance.name,maxTimestamp);lastInstance.deletionTsWasExplicit=false;}}},getObjectInstanceAt(scopedId,ts){let instanceMap;if(scopedId.scope in this.instanceMapsByScopedId_){instanceMap=this.instanceMapsByScopedId_[scopedId.scope][scopedId.id];}
+if(!instanceMap)return undefined;return instanceMap.getInstanceAt(ts);},getSnapshotAt(scopedId,ts){const instance=this.getObjectInstanceAt(scopedId,ts);if(!instance)return undefined;return instance.getSnapshotAt(ts);},iterObjectInstances(iter,opt_this){opt_this=opt_this||this;for(const imapById of Object.values(this.instanceMapsByScopedId_)){for(const i2imap of Object.values(imapById)){i2imap.instances.forEach(iter,opt_this);}}},getAllObjectInstances(){const instances=[];this.iterObjectInstances(function(i){instances.push(i);});return instances;},getAllInstancesNamed(name){return this.instancesByTypeName_[name];},getAllInstancesByTypeName(){return this.instancesByTypeName_;},preInitializeAllObjects(){this.iterObjectInstances(function(instance){instance.preInitialize();});},initializeAllObjects(){this.iterObjectInstances(function(instance){instance.initialize();});},initializeInstances(){this.iterObjectInstances(function(instance){instance.initialize();});},updateBounds(){this.bounds.reset();this.iterObjectInstances(function(instance){instance.updateBounds();this.bounds.addRange(instance.bounds);},this);},shiftTimestampsForward(amount){this.iterObjectInstances(function(instance){instance.shiftTimestampsForward(amount);});},addCategoriesToDict(categoriesDict){this.iterObjectInstances(function(instance){categoriesDict[instance.category]=true;});}};return{ObjectCollection,};});'use strict';tr.exportTo('tr.model',function(){class AsyncSliceGroup extends tr.model.EventContainer{constructor(parentContainer,opt_name){super();this.parentContainer_=parentContainer;this.name_=opt_name;this.slices=[];this.viewSubGroups_=undefined;this.nestedLevel_=0;this.hasNestedSubGroups_=true;this.title_=undefined;}
+get parentContainer(){return this.parentContainer_;}
+get model(){return this.parentContainer_.parent.model;}
+get stableId(){return this.parentContainer_.stableId+'.AsyncSliceGroup';}
+get title(){if(this.nested_level_===0){return'<root>';}
+return this.title_;}
+getSettingsKey(){if(this.name_===undefined){return undefined;}
+const parentKey=this.parentContainer_.getSettingsKey();if(parentKey===undefined){return undefined;}
+return parentKey+'.'+this.name_;}
+push(slice){if(this.viewSubGroups_!==undefined){throw new Error('No new slices are allowed when view sub-groups already formed.');}
+slice.parentContainer=this.parentContainer;this.slices.push(slice);return slice;}
+get length(){return this.slices.length;}
+shiftTimestampsForward(amount){for(const slice of this.childEvents()){slice.start+=amount;}}
+updateBounds(){this.bounds.reset();for(let i=0;i<this.slices.length;i++){this.bounds.addValue(this.slices[i].start);this.bounds.addValue(this.slices[i].end);}}
+autoCloseOpenSlices(){const maxTimestamp=this.parentContainer_.parent.model.bounds.max;for(const slice of this.childEvents()){if(slice.didNotFinish){slice.duration=maxTimestamp-slice.start;}}}
+get viewSubGroups(){if(!this.hasNestedSubGroups_||this.nestedLevel_===2){return[];}
+if(this.viewSubGroups_!==undefined){return this.viewSubGroups_;}
+const subGroupsByTitle=new Map();for(const slice of this.slices){let subGroupTitle=slice.viewSubGroupTitle;let hasNestedSubGroups=false;if(this.nestedLevel_===0&&slice.viewSubGroupGroupingKey!==undefined){subGroupTitle=slice.viewSubGroupGroupingKey;hasNestedSubGroups=true;}
+let subGroup=subGroupsByTitle.get(subGroupTitle);if(subGroup===undefined){let name;if(this.name_!==undefined){name=this.name_+'.'+subGroupTitle;}else{name=subGroupTitle;}
+subGroup=new AsyncSliceGroup(this.parentContainer_,name);subGroup.title_=subGroupTitle;subGroup.hasNestedSubGroups_=hasNestedSubGroups;subGroup.nestedLevel_=this.nestedLevel_+1;subGroupsByTitle.set(subGroupTitle,subGroup);}
+subGroup.push(slice);}
+this.viewSubGroups_=Array.from(subGroupsByTitle.values());this.viewSubGroups_.sort((a,b)=>a.title.localeCompare(b.title));return this.viewSubGroups_;}*findTopmostSlicesInThisContainer(eventPredicate,opt_this){for(const slice of this.slices){if(slice.isTopLevel){yield*slice.findTopmostSlicesRelativeToThisSlice(eventPredicate,opt_this);}}}*childEvents(){for(const slice of this.slices){yield slice;yield*slice.enumerateAllDescendents();}}*childEventContainers(){}}
+return{AsyncSliceGroup,};});'use strict';tr.exportTo('tr.model',function(){const Slice=tr.model.Slice;function ThreadSlice(cat,title,colorId,start,args,opt_duration,opt_cpuStart,opt_cpuDuration,opt_argsStripped,opt_bindId){Slice.call(this,cat,title,colorId,start,args,opt_duration,opt_cpuStart,opt_cpuDuration,opt_argsStripped,opt_bindId);this.subSlices=[];}
+ThreadSlice.prototype={__proto__:Slice.prototype,get overlappingSamples(){const samples=new tr.model.EventSet();if(!this.parentContainer||!this.parentContainer.samples){return samples;}
+this.parentContainer.samples.forEach(function(sample){if(this.start<=sample.start&&sample.start<=this.end){samples.push(sample);}},this);return samples;}};tr.model.EventRegistry.register(ThreadSlice,{name:'slice',pluralName:'slices'});return{ThreadSlice,};});'use strict';tr.exportTo('tr.model',function(){const ColorScheme=tr.b.ColorScheme;const ThreadSlice=tr.model.ThreadSlice;function getSliceLo(s){return s.start;}
+function getSliceHi(s){return s.end;}
+function SliceGroup(parentContainer,opt_sliceConstructor,opt_name){tr.model.EventContainer.call(this);this.parentContainer_=parentContainer;const sliceConstructor=opt_sliceConstructor||ThreadSlice;this.sliceConstructor=sliceConstructor;this.sliceConstructorSubTypes=this.sliceConstructor.subTypes;if(!this.sliceConstructorSubTypes){throw new Error('opt_sliceConstructor must have a subtype registry.');}
+this.openPartialSlices_=[];this.slices=[];this.topLevelSlices=[];this.haveTopLevelSlicesBeenBuilt=false;this.name_=opt_name;if(this.model===undefined){throw new Error('SliceGroup must have model defined.');}}
+SliceGroup.prototype={__proto__:tr.model.EventContainer.prototype,get parentContainer(){return this.parentContainer_;},get model(){return this.parentContainer_.model;},get stableId(){return this.parentContainer_.stableId+'.SliceGroup';},getSettingsKey(){if(!this.name_)return undefined;const parentKey=this.parentContainer_.getSettingsKey();if(!parentKey)return undefined;return parentKey+'.'+this.name;},get length(){return this.slices.length;},pushSlice(slice){this.haveTopLevelSlicesBeenBuilt=false;slice.parentContainer=this.parentContainer_;this.slices.push(slice);return slice;},pushSlices(slices){this.haveTopLevelSlicesBeenBuilt=false;slices.forEach(function(slice){slice.parentContainer=this.parentContainer_;this.slices.push(slice);},this);},beginSlice(category,title,ts,opt_args,opt_tts,opt_argsStripped,opt_colorId,opt_bindId){const colorId=opt_colorId||ColorScheme.getColorIdForGeneralPurposeString(title);const sliceConstructorSubTypes=this.sliceConstructorSubTypes;const sliceType=sliceConstructorSubTypes.getConstructor(category,title);const slice=new sliceType(category,title,colorId,ts,opt_args?opt_args:{},null,opt_tts,undefined,opt_argsStripped,opt_bindId);this.openPartialSlices_.push(slice);slice.didNotFinish=true;this.pushSlice(slice);return slice;},isTimestampValidForBeginOrEnd(ts){if(!this.openPartialSlices_.length)return true;const top=this.openPartialSlices_[this.openPartialSlices_.length-1];return ts>=top.start;},get openSliceCount(){return this.openPartialSlices_.length;},get mostRecentlyOpenedPartialSlice(){if(!this.openPartialSlices_.length)return undefined;return this.openPartialSlices_[this.openPartialSlices_.length-1];},endSlice(ts,opt_tts,opt_colorId){if(!this.openSliceCount){throw new Error('endSlice called without an open slice');}
+const slice=this.openPartialSlices_[this.openSliceCount-1];this.openPartialSlices_.splice(this.openSliceCount-1,1);if(ts<slice.start){throw new Error('Slice '+slice.title+' end time is before its start.');}
+slice.duration=ts-slice.start;slice.didNotFinish=false;slice.colorId=opt_colorId||slice.colorId;if(opt_tts&&slice.cpuStart!==undefined){slice.cpuDuration=opt_tts-slice.cpuStart;}
+return slice;},pushCompleteSlice(category,title,ts,duration,tts,cpuDuration,opt_args,opt_argsStripped,opt_colorId,opt_bindId){const colorId=opt_colorId||ColorScheme.getColorIdForGeneralPurposeString(title);const sliceConstructorSubTypes=this.sliceConstructorSubTypes;const sliceType=sliceConstructorSubTypes.getConstructor(category,title);const slice=new sliceType(category,title,colorId,ts,opt_args?opt_args:{},duration,tts,cpuDuration,opt_argsStripped,opt_bindId);if(duration===undefined){slice.didNotFinish=true;}
+this.pushSlice(slice);return slice;},autoCloseOpenSlices(){this.updateBounds();const maxTimestamp=this.bounds.max;for(let sI=0;sI<this.slices.length;sI++){const slice=this.slices[sI];if(slice.didNotFinish){slice.duration=maxTimestamp-slice.start;}}
+this.openPartialSlices_=[];},shiftTimestampsForward(amount){for(let sI=0;sI<this.slices.length;sI++){const slice=this.slices[sI];slice.start=(slice.start+amount);}},updateBounds(){this.bounds.reset();for(let i=0;i<this.slices.length;i++){this.bounds.addValue(this.slices[i].start);this.bounds.addValue(this.slices[i].end);}},copySlice(slice){const sliceConstructorSubTypes=this.sliceConstructorSubTypes;const sliceType=sliceConstructorSubTypes.getConstructor(slice.category,slice.title);const newSlice=new sliceType(slice.category,slice.title,slice.colorId,slice.start,slice.args,slice.duration,slice.cpuStart,slice.cpuDuration);newSlice.didNotFinish=slice.didNotFinish;return newSlice;},*findTopmostSlicesInThisContainer(eventPredicate,opt_this){if(!this.haveTopLevelSlicesBeenBuilt){throw new Error('Nope');}
+for(const s of this.topLevelSlices){yield*s.findTopmostSlicesRelativeToThisSlice(eventPredicate);}},*childEvents(){yield*this.slices;},*childEventContainers(){},*getDescendantEventsInSortedRanges(ranges,opt_containerPredicate){if(ranges.length===0||(opt_containerPredicate!==undefined&&!opt_containerPredicate(this))){return;}
+let rangeIndex=0;let range=ranges[rangeIndex];for(const event of this.childEvents()){while(event.start>range.max){rangeIndex++;if(rangeIndex>=ranges.length)return;range=ranges[rangeIndex];}
+if(event.end>=range.min)yield event;}},getSlicesOfName(title){const slices=[];for(let i=0;i<this.slices.length;i++){if(this.slices[i].title===title){slices.push(this.slices[i]);}}
+return slices;},iterSlicesInTimeRange(callback,start,end){const ret=[];tr.b.iterateOverIntersectingIntervals(this.topLevelSlices,function(s){return s.start;},function(s){return s.duration;},start,end,function(topLevelSlice){callback(topLevelSlice);for(const slice of topLevelSlice.enumerateAllDescendents()){callback(slice);}});return ret;},findFirstSlice(){if(!this.haveTopLevelSlicesBeenBuilt){throw new Error('Nope');}
+if(0===this.slices.length)return undefined;return this.slices[0];},findSliceAtTs(ts){if(!this.haveTopLevelSlicesBeenBuilt)throw new Error('Nope');let i=tr.b.findIndexInSortedClosedIntervals(this.topLevelSlices,getSliceLo,getSliceHi,ts);if(i===-1||i===this.topLevelSlices.length){return undefined;}
+let curSlice=this.topLevelSlices[i];while(true){i=tr.b.findIndexInSortedClosedIntervals(curSlice.subSlices,getSliceLo,getSliceHi,ts);if(i===-1||i===curSlice.subSlices.length){return curSlice;}
+curSlice=curSlice.subSlices[i];}},findNextSliceAfter(ts,refGuid){let i=tr.b.findLowIndexInSortedArray(this.slices,getSliceLo,ts);if(i===this.slices.length){return undefined;}
+for(;i<this.slices.length;i++){const slice=this.slices[i];if(slice.start>ts)return slice;if(slice.guid<=refGuid)continue;return slice;}
+return undefined;},hasCpuDuration_(){if(this.slices.some(function(slice){return slice.cpuDuration!==undefined;}))return true;return false;},createSubSlices(){this.haveTopLevelSlicesBeenBuilt=true;this.createSubSlicesImpl_();if(!this.hasCpuDuration_()&&this.parentContainer.timeSlices){this.addCpuTimeToSubslices_(this.parentContainer.timeSlices);}
+this.slices.forEach(function(slice){let selfTime=slice.duration;for(let i=0;i<slice.subSlices.length;i++){selfTime-=slice.subSlices[i].duration;}
+slice.selfTime=selfTime;if(slice.cpuDuration===undefined)return;let cpuSelfTime=slice.cpuDuration;for(let i=0;i<slice.subSlices.length;i++){if(slice.subSlices[i].cpuDuration!==undefined){cpuSelfTime-=slice.subSlices[i].cpuDuration;}}
+slice.cpuSelfTime=cpuSelfTime;});},createSubSlicesImpl_(){const precisionUnit=this.model.intrinsicTimeUnit;function addSliceIfBounds(parent,child){if(parent.bounds(child,precisionUnit)){child.parentSlice=parent;if(parent.subSlices===undefined){parent.subSlices=[];}
+parent.subSlices.push(child);return true;}
+return false;}
+if(!this.slices.length)return;const ops=[];for(let i=0;i<this.slices.length;i++){if(this.slices[i].subSlices){this.slices[i].subSlices.splice(0,this.slices[i].subSlices.length);}
+ops.push(i);}
+const originalSlices=this.slices;ops.sort(function(ix,iy){const x=originalSlices[ix];const y=originalSlices[iy];if(x.start!==y.start){return x.start-y.start;}
+return ix-iy;});const slices=new Array(this.slices.length);for(let i=0;i<ops.length;i++){slices[i]=originalSlices[ops[i]];}
+let rootSlice=slices[0];this.topLevelSlices=[];this.topLevelSlices.push(rootSlice);rootSlice.isTopLevel=true;for(let i=1;i<slices.length;i++){const slice=slices[i];while(rootSlice!==undefined&&(!addSliceIfBounds(rootSlice,slice))){rootSlice=rootSlice.parentSlice;}
+if(rootSlice===undefined){this.topLevelSlices.push(slice);slice.isTopLevel=true;}
+rootSlice=slice;}
+this.slices=slices;},addCpuTimeToSubslices_(timeSlices){const SCHEDULING_STATE=tr.model.SCHEDULING_STATE;let sliceIdx=0;timeSlices.forEach(function(timeSlice){if(timeSlice.schedulingState===SCHEDULING_STATE.RUNNING){while(sliceIdx<this.topLevelSlices.length){if(this.addCpuTimeToSubslice_(this.topLevelSlices[sliceIdx],timeSlice)){sliceIdx++;}else{break;}}}},this);},addCpuTimeToSubslice_(slice,timeSlice){if(slice.start>timeSlice.end||slice.end<timeSlice.start){return slice.end<=timeSlice.end;}
+let duration=timeSlice.duration;if(slice.start>timeSlice.start){duration-=slice.start-timeSlice.start;}
+if(timeSlice.end>slice.end){duration-=timeSlice.end-slice.end;}
+if(slice.cpuDuration){slice.cpuDuration+=duration;}else{slice.cpuDuration=duration;}
+for(let i=0;i<slice.subSlices.length;i++){this.addCpuTimeToSubslice_(slice.subSlices[i],timeSlice);}
+return slice.end<=timeSlice.end;}};SliceGroup.merge=function(groupA,groupB){if(groupA.openPartialSlices_.length>0){throw new Error('groupA has open partial slices');}
+if(groupB.openPartialSlices_.length>0){throw new Error('groupB has open partial slices');}
+if(groupA.parentContainer!==groupB.parentContainer){throw new Error('Different parent threads. Cannot merge');}
+if(groupA.sliceConstructor!==groupB.sliceConstructor){throw new Error('Different slice constructors. Cannot merge');}
+const result=new SliceGroup(groupA.parentContainer,groupA.sliceConstructor,groupA.name_);const slicesA=groupA.slices;const slicesB=groupB.slices;let idxA=0;let idxB=0;const openA=[];const openB=[];const splitOpenSlices=function(when){for(let i=0;i<openB.length;i++){const oldSlice=openB[i];const oldEnd=oldSlice.end;if(when<oldSlice.start||oldEnd<when){throw new Error('slice should not be split');}
+const newSlice=result.copySlice(oldSlice);newSlice.start=when;newSlice.duration=oldEnd-when;if(newSlice.title.indexOf(' (cont.)')===-1){newSlice.title+=' (cont.)';}
+oldSlice.duration=when-oldSlice.start;openB[i]=newSlice;result.pushSlice(newSlice);}};const closeOpenSlices=function(upTo){while(openA.length>0||openB.length>0){const nextA=openA[openA.length-1];const nextB=openB[openB.length-1];const endA=nextA&&nextA.end;const endB=nextB&&nextB.end;if((endA===undefined||endA>upTo)&&(endB===undefined||endB>upTo)){return;}
+if(endB===undefined||endA<endB){splitOpenSlices(endA);openA.pop();}else{openB.pop();}}};while(idxA<slicesA.length||idxB<slicesB.length){const sA=slicesA[idxA];const sB=slicesB[idxB];let nextSlice;let isFromB;if(sA===undefined||(sB!==undefined&&sA.start>sB.start)){nextSlice=result.copySlice(sB);isFromB=true;idxB++;}else{nextSlice=result.copySlice(sA);isFromB=false;idxA++;}
+closeOpenSlices(nextSlice.start);result.pushSlice(nextSlice);if(isFromB){openB.push(nextSlice);}else{splitOpenSlices(nextSlice.start);openA.push(nextSlice);}}
+closeOpenSlices();return result;};return{SliceGroup,};});'use strict';tr.exportTo('tr.model',function(){const AsyncSlice=tr.model.AsyncSlice;const AsyncSliceGroup=tr.model.AsyncSliceGroup;const SliceGroup=tr.model.SliceGroup;const ThreadSlice=tr.model.ThreadSlice;const ThreadTimeSlice=tr.model.ThreadTimeSlice;function Thread(parent,tid){if(!parent){throw new Error('Parent must be provided.');}
+tr.model.EventContainer.call(this);this.parent=parent;this.sortIndex=0;this.tid=tid;this.name=undefined;this.samples_=undefined;this.sliceGroup=new SliceGroup(this,ThreadSlice,'slices');this.timeSlices=undefined;this.kernelSliceGroup=new SliceGroup(this,ThreadSlice,'kernel-slices');this.asyncSliceGroup=new AsyncSliceGroup(this,'async-slices');}
+Thread.prototype={__proto__:tr.model.EventContainer.prototype,get model(){return this.parent.model;},get stableId(){return this.parent.stableId+'.'+this.tid;},compareTo(that){return Thread.compare(this,that);},*childEventContainers(){if(this.sliceGroup.length){yield this.sliceGroup;}
+if(this.kernelSliceGroup.length){yield this.kernelSliceGroup;}
+if(this.asyncSliceGroup.length){yield this.asyncSliceGroup;}},*childEvents(){if(this.timeSlices){yield*this.timeSlices;}},iterateAllPersistableObjects(cb){cb(this);if(this.sliceGroup.length){cb(this.sliceGroup);}
+this.asyncSliceGroup.viewSubGroups.forEach(cb);},shiftTimestampsForward(amount){this.sliceGroup.shiftTimestampsForward(amount);if(this.timeSlices){for(let i=0;i<this.timeSlices.length;i++){const slice=this.timeSlices[i];slice.start+=amount;}}
+this.kernelSliceGroup.shiftTimestampsForward(amount);this.asyncSliceGroup.shiftTimestampsForward(amount);},get isEmpty(){if(this.sliceGroup.length)return false;if(this.sliceGroup.openSliceCount)return false;if(this.timeSlices&&this.timeSlices.length)return false;if(this.kernelSliceGroup.length)return false;if(this.asyncSliceGroup.length)return false;if(this.samples_.length)return false;return true;},updateBounds(){this.bounds.reset();this.sliceGroup.updateBounds();this.bounds.addRange(this.sliceGroup.bounds);this.kernelSliceGroup.updateBounds();this.bounds.addRange(this.kernelSliceGroup.bounds);this.asyncSliceGroup.updateBounds();this.bounds.addRange(this.asyncSliceGroup.bounds);if(this.timeSlices&&this.timeSlices.length){this.bounds.addValue(this.timeSlices[0].start);this.bounds.addValue(this.timeSlices[this.timeSlices.length-1].end);}
+if(this.samples_&&this.samples_.length){this.bounds.addValue(this.samples_[0].start);this.bounds.addValue(this.samples_[this.samples_.length-1].end);}},addCategoriesToDict(categoriesDict){for(let i=0;i<this.sliceGroup.length;i++){categoriesDict[this.sliceGroup.slices[i].category]=true;}
+for(let i=0;i<this.kernelSliceGroup.length;i++){categoriesDict[this.kernelSliceGroup.slices[i].category]=true;}
+for(let i=0;i<this.asyncSliceGroup.length;i++){categoriesDict[this.asyncSliceGroup.slices[i].category]=true;}
+if(this.samples_){for(let i=0;i<this.samples_.length;i++){categoriesDict[this.samples_[i].category]=true;}}},autoCloseOpenSlices(){this.sliceGroup.autoCloseOpenSlices();this.asyncSliceGroup.autoCloseOpenSlices();this.kernelSliceGroup.autoCloseOpenSlices();},mergeKernelWithUserland(){if(this.kernelSliceGroup.length>0){const newSlices=SliceGroup.merge(this.sliceGroup,this.kernelSliceGroup);this.sliceGroup.slices=newSlices.slices;this.kernelSliceGroup=new SliceGroup(this);this.updateBounds();}},createSubSlices(){this.sliceGroup.createSubSlices();this.samples_=this.parent.model.samples.filter(sample=>sample.thread===this);},get userFriendlyName(){return this.name||this.tid;},get userFriendlyDetails(){return'tid: '+this.tid+
+(this.name?', name: '+this.name:'');},getSettingsKey(){if(!this.name)return undefined;const parentKey=this.parent.getSettingsKey();if(!parentKey)return undefined;return parentKey+'.'+this.name;},getProcess(){return this.parent;},indexOfTimeSlice(timeSlice){const i=tr.b.findLowIndexInSortedArray(this.timeSlices,function(slice){return slice.start;},timeSlice.start);if(this.timeSlices[i]!==timeSlice)return undefined;return i;},sumOverToplevelSlicesInRange(range,func){let sum=0;tr.b.iterateOverIntersectingIntervals(this.sliceGroup.topLevelSlices,slice=>slice.start,slice=>slice.end,range.min,range.max,slice=>{let fractionOfSliceInsideRangeOfInterest=1;if(slice.duration>0){const intersection=range.findIntersection(slice.range);fractionOfSliceInsideRangeOfInterest=intersection.duration/slice.duration;}
+sum+=func(slice)*fractionOfSliceInsideRangeOfInterest;});return sum;},getCpuTimeForRange(range){return this.sumOverToplevelSlicesInRange(range,slice=>slice.cpuDuration||0);},getNumToplevelSlicesForRange(range){return this.sumOverToplevelSlicesInRange(range,slice=>1);},getWallTimeForRange(range){return this.sumOverToplevelSlicesInRange(range,slice=>slice.duration||0);},getSchedulingStatsForRange(start,end){const stats={};if(!this.timeSlices)return stats;function addStatsForSlice(threadTimeSlice){const overlapStart=Math.max(threadTimeSlice.start,start);const overlapEnd=Math.min(threadTimeSlice.end,end);const schedulingState=threadTimeSlice.schedulingState;if(!(schedulingState in stats))stats[schedulingState]=0;stats[schedulingState]+=overlapEnd-overlapStart;}
+tr.b.iterateOverIntersectingIntervals(this.timeSlices,function(x){return x.start;},function(x){return x.end;},start,end,addStatsForSlice);return stats;},get samples(){return this.samples_;},get type(){const re=/^[^0-9|\/]+/;const matches=re.exec(this.name);if(matches&&matches[0])return matches[0];throw new Error('Could not determine thread type for thread name '+
+this.name);}};Thread.compare=function(x,y){let tmp=x.parent.compareTo(y.parent);if(tmp)return tmp;tmp=x.sortIndex-y.sortIndex;if(tmp)return tmp;if(x.name!==undefined){if(y.name!==undefined){tmp=x.name.localeCompare(y.name);}else{tmp=-1;}}else if(y.name!==undefined){tmp=1;}
+if(tmp)return tmp;return x.tid-y.tid;};return{Thread,};});'use strict';tr.exportTo('tr.model',function(){const Thread=tr.model.Thread;const Counter=tr.model.Counter;function ProcessBase(model){if(!model){throw new Error('Must provide a model');}
+tr.model.EventContainer.call(this);this.model=model;this.threads={};this.counters={};this.objects=new tr.model.ObjectCollection(this);this.sortIndex=0;}
+ProcessBase.compare=function(x,y){return x.sortIndex-y.sortIndex;};ProcessBase.prototype={__proto__:tr.model.EventContainer.prototype,get stableId(){throw new Error('Not implemented');},*childEventContainers(){yield*Object.values(this.threads);yield*Object.values(this.counters);yield this.objects;},iterateAllPersistableObjects(cb){cb(this);for(const tid in this.threads){this.threads[tid].iterateAllPersistableObjects(cb);}},get numThreads(){let n=0;for(const p in this.threads){n++;}
+return n;},shiftTimestampsForward(amount){for(const child of this.childEventContainers()){child.shiftTimestampsForward(amount);}},autoCloseOpenSlices(){for(const tid in this.threads){const thread=this.threads[tid];thread.autoCloseOpenSlices();}},autoDeleteObjects(maxTimestamp){this.objects.autoDeleteObjects(maxTimestamp);},preInitializeObjects(){this.objects.preInitializeAllObjects();},initializeObjects(){this.objects.initializeAllObjects();},mergeKernelWithUserland(){for(const tid in this.threads){const thread=this.threads[tid];thread.mergeKernelWithUserland();}},updateBounds(){this.bounds.reset();for(const tid in this.threads){this.threads[tid].updateBounds();this.bounds.addRange(this.threads[tid].bounds);}
+for(const id in this.counters){this.counters[id].updateBounds();this.bounds.addRange(this.counters[id].bounds);}
+this.objects.updateBounds();this.bounds.addRange(this.objects.bounds);},addCategoriesToDict(categoriesDict){for(const tid in this.threads){this.threads[tid].addCategoriesToDict(categoriesDict);}
+for(const id in this.counters){categoriesDict[this.counters[id].category]=true;}
+this.objects.addCategoriesToDict(categoriesDict);},findAllThreadsMatching(predicate,opt_this){const threads=[];for(const tid in this.threads){const thread=this.threads[tid];if(predicate.call(opt_this,thread)){threads.push(thread);}}
+return threads;},findAllThreadsNamed(name){const threads=this.findAllThreadsMatching(function(thread){if(!thread.name)return false;return thread.name===name;});return threads;},findAtMostOneThreadNamed(name){const threads=this.findAllThreadsNamed(name);if(threads.length===0)return undefined;if(threads.length>1){throw new Error('Expected no more than one '+name);}
+return threads[0];},pruneEmptyContainers(){const threadsToKeep={};for(const tid in this.threads){const thread=this.threads[tid];if(!thread.isEmpty){threadsToKeep[tid]=thread;}}
+this.threads=threadsToKeep;},getThread(tid){return this.threads[tid];},getOrCreateThread(tid){if(!this.threads[tid]){this.threads[tid]=new Thread(this,tid);}
+return this.threads[tid];},getOrCreateCounter(cat,name){const id=cat+'.'+name;if(!this.counters[id]){this.counters[id]=new Counter(this,id,cat,name);}
+return this.counters[id];},getSettingsKey(){throw new Error('Not implemented');},createSubSlices(){for(const tid in this.threads){this.threads[tid].createSubSlices();}}};return{ProcessBase,};});'use strict';tr.exportTo('tr.model',function(){const ColorScheme=tr.b.ColorScheme;const Counter=tr.model.Counter;const CpuSlice=tr.model.CpuSlice;function Cpu(kernel,number){if(kernel===undefined||number===undefined){throw new Error('Missing arguments');}
+this.kernel=kernel;this.cpuNumber=number;this.slices=[];this.counters={};this.bounds_=new tr.b.math.Range();this.samples_=undefined;this.lastActiveTimestamp_=undefined;this.lastActiveThread_=undefined;this.lastActiveName_=undefined;this.lastActiveArgs_=undefined;}
+Cpu.prototype={__proto__:tr.model.EventContainer.prototype,get samples(){return this.samples_;},get userFriendlyName(){return'CPU '+this.cpuNumber;},*findTopmostSlicesInThisContainer(eventPredicate,opt_this){for(const s of this.slices){yield*s.findTopmostSlicesRelativeToThisSlice(eventPredicate,opt_this);}},*childEvents(){yield*this.slices;if(this.samples_){yield*this.samples_;}},*childEventContainers(){yield*Object.values(this.counters);},getOrCreateCounter(cat,name){const id=cat+'.'+name;if(!this.counters[id]){this.counters[id]=new Counter(this,id,cat,name);}
+return this.counters[id];},getCounter(cat,name){const id=cat+'.'+name;if(!this.counters[id]){return undefined;}
+return this.counters[id];},shiftTimestampsForward(amount){for(let sI=0;sI<this.slices.length;sI++){this.slices[sI].start=(this.slices[sI].start+amount);}
+for(const id in this.counters){this.counters[id].shiftTimestampsForward(amount);}},updateBounds(){this.bounds_.reset();if(this.slices.length){this.bounds_.addValue(this.slices[0].start);this.bounds_.addValue(this.slices[this.slices.length-1].end);}
+for(const id in this.counters){this.counters[id].updateBounds();this.bounds_.addRange(this.counters[id].bounds);}
+if(this.samples_&&this.samples_.length){this.bounds_.addValue(this.samples_[0].start);this.bounds_.addValue(this.samples_[this.samples_.length-1].end);}},createSubSlices(){this.samples_=this.kernel.model.samples.filter(function(sample){return sample.cpu===this;},this);},addCategoriesToDict(categoriesDict){for(let i=0;i<this.slices.length;i++){categoriesDict[this.slices[i].category]=true;}
+for(const id in this.counters){categoriesDict[this.counters[id].category]=true;}
+for(let i=0;i<this.samples_.length;i++){categoriesDict[this.samples_[i].category]=true;}},indexOf(cpuSlice){const i=tr.b.findLowIndexInSortedArray(this.slices,function(slice){return slice.start;},cpuSlice.start);if(this.slices[i]!==cpuSlice)return undefined;return i;},closeActiveThread(endTimestamp,args){if(this.lastActiveThread_===undefined||this.lastActiveThread_===0){return;}
+if(endTimestamp<this.lastActiveTimestamp_){throw new Error('The end timestamp of a thread running on CPU '+
+this.cpuNumber+' is before its start timestamp.');}
+for(const key in args){this.lastActiveArgs_[key]=args[key];}
+const duration=endTimestamp-this.lastActiveTimestamp_;const slice=new tr.model.CpuSlice('',this.lastActiveName_,ColorScheme.getColorIdForGeneralPurposeString(this.lastActiveName_),this.lastActiveTimestamp_,this.lastActiveArgs_,duration);slice.cpu=this;this.slices.push(slice);this.lastActiveTimestamp_=undefined;this.lastActiveThread_=undefined;this.lastActiveName_=undefined;this.lastActiveArgs_=undefined;},switchActiveThread(timestamp,oldThreadArgs,newThreadId,newThreadName,newThreadArgs){this.closeActiveThread(timestamp,oldThreadArgs);this.lastActiveTimestamp_=timestamp;this.lastActiveThread_=newThreadId;this.lastActiveName_=newThreadName;this.lastActiveArgs_=newThreadArgs;},getFreqStatsForRange(range){const stats={};function addStatsForFreq(freqSample,index){const freqEnd=(index<freqSample.series_.length-1)?freqSample.series_.samples_[index+1].timestamp:range.max;const freqRange=tr.b.math.Range.fromExplicitRange(freqSample.timestamp,freqEnd);const intersection=freqRange.findIntersection(range);if(!(freqSample.value in stats)){stats[freqSample.value]=0;}
+stats[freqSample.value]+=intersection.duration;}
+const freqCounter=this.getCounter('','Clock Frequency');if(freqCounter!==undefined){const freqSeries=freqCounter.getSeries(0);if(!freqSeries)return;tr.b.iterateOverIntersectingIntervals(freqSeries.samples_,function(x){return x.timestamp;},function(x,index){if(index<freqSeries.length-1){return freqSeries.samples_[index+1].timestamp;}
+return range.max;},range.min,range.max,addStatsForFreq);}
+return stats;}};Cpu.compare=function(x,y){return x.cpuNumber-y.cpuNumber;};return{Cpu,};});'use strict';tr.exportTo('tr.model',function(){const Event=tr.model.Event;const EventRegistry=tr.model.EventRegistry;function PowerSample(series,start,powerInW){Event.call(this);this.series_=series;this.start_=parseFloat(start);this.powerInW_=parseFloat(powerInW);}
+PowerSample.prototype={__proto__:Event.prototype,get series(){return this.series_;},get start(){return this.start_;},set start(value){this.start_=value;},get powerInW(){return this.powerInW_;},set powerInW(value){this.powerInW_=value;},addBoundsToRange(range){range.addValue(this.start);}};EventRegistry.register(PowerSample,{name:'powerSample',pluralName:'powerSamples'});return{PowerSample,};});'use strict';tr.exportTo('tr.model',function(){const PowerSample=tr.model.PowerSample;function PowerSeries(device){tr.model.EventContainer.call(this);this.device_=device;this.samples_=[];}
+PowerSeries.prototype={__proto__:tr.model.EventContainer.prototype,get device(){return this.device_;},get samples(){return this.samples_;},get stableId(){return this.device_.stableId+'.PowerSeries';},addPowerSample(ts,val){const sample=new PowerSample(this,ts,val);this.samples_.push(sample);return sample;},getEnergyConsumedInJ(start,end){const measurementRange=tr.b.math.Range.fromExplicitRange(start,end);let energyConsumedInJ=0;let startIndex=tr.b.findLowIndexInSortedArray(this.samples,x=>x.start,start)-1;const endIndex=tr.b.findLowIndexInSortedArray(this.samples,x=>x.start,end);if(startIndex<0){startIndex=0;}
+for(let i=startIndex;i<endIndex;i++){const sample=this.samples[i];const nextSample=this.samples[i+1];const sampleRange=new tr.b.math.Range();sampleRange.addValue(sample.start);sampleRange.addValue(nextSample?nextSample.start:sample.start);const intersectionRangeInMs=measurementRange.findIntersection(sampleRange);const durationInS=tr.b.convertUnit(intersectionRangeInMs.duration,tr.b.UnitPrefixScale.METRIC.MILLI,tr.b.UnitPrefixScale.METRIC.NONE);energyConsumedInJ+=durationInS*sample.powerInW;}
+return energyConsumedInJ;},getSamplesWithinRange(start,end){const startIndex=tr.b.findLowIndexInSortedArray(this.samples,x=>x.start,start);const endIndex=tr.b.findLowIndexInSortedArray(this.samples,x=>x.start,end);return this.samples.slice(startIndex,endIndex);},shiftTimestampsForward(amount){for(let i=0;i<this.samples_.length;++i){this.samples_[i].start+=amount;}},updateBounds(){this.bounds.reset();if(this.samples_.length===0)return;this.bounds.addValue(this.samples_[0].start);this.bounds.addValue(this.samples_[this.samples_.length-1].start);},*childEvents(){yield*this.samples_;},};return{PowerSeries,};});'use strict';tr.exportTo('tr.model',function(){function Device(model){if(!model){throw new Error('Must provide a model.');}
+tr.model.EventContainer.call(this);this.powerSeries_=undefined;this.cpuUsageSeries_=undefined;this.vSyncTimestamps_=[];}
+Device.compare=function(x,y){return x.guid-y.guid;};Device.prototype={__proto__:tr.model.EventContainer.prototype,compareTo(that){return Device.compare(this,that);},get userFriendlyName(){return'Device';},get userFriendlyDetails(){return'Device';},get stableId(){return'Device';},getSettingsKey(){return'device';},get powerSeries(){return this.powerSeries_;},set powerSeries(powerSeries){this.powerSeries_=powerSeries;},get cpuUsageSeries(){return this.cpuUsageSeries_;},set cpuUsageSeries(cpuUsageSeries){this.cpuUsageSeries_=cpuUsageSeries;},get vSyncTimestamps(){return this.vSyncTimestamps_;},set vSyncTimestamps(value){this.vSyncTimestamps_=value;},updateBounds(){this.bounds.reset();for(const child of this.childEventContainers()){child.updateBounds();this.bounds.addRange(child.bounds);}},shiftTimestampsForward(amount){for(const child of this.childEventContainers()){child.shiftTimestampsForward(amount);}
+for(let i=0;i<this.vSyncTimestamps_.length;i++){this.vSyncTimestamps_[i]+=amount;}},addCategoriesToDict(categoriesDict){},*childEventContainers(){if(this.powerSeries_){yield this.powerSeries_;}
+if(this.cpuUsageSeries_){yield this.cpuUsageSeries_;}}};return{Device,};});'use strict';tr.exportTo('tr.model',function(){function FlowEvent(category,id,title,colorId,start,args,opt_duration){tr.model.TimedEvent.call(this,start);this.category=category||'';this.title=title;this.colorId=colorId;this.start=start;this.args=args;this.id=id;this.startSlice=undefined;this.endSlice=undefined;this.startStackFrame=undefined;this.endStackFrame=undefined;if(opt_duration!==undefined){this.duration=opt_duration;}}
+FlowEvent.prototype={__proto__:tr.model.TimedEvent.prototype,get userFriendlyName(){return'Flow event named '+this.title+' at '+
+tr.b.Unit.byName.timeStampInMs.format(this.timestamp);}};tr.model.EventRegistry.register(FlowEvent,{name:'flowEvent',pluralName:'flowEvents'});return{FlowEvent,};});'use strict';tr.exportTo('tr.model',function(){function ContainerMemoryDump(start){tr.model.TimedEvent.call(this,start);this.levelOfDetail=undefined;this.memoryAllocatorDumps_=undefined;this.memoryAllocatorDumpsByFullName_=undefined;}
+ContainerMemoryDump.LevelOfDetail={BACKGROUND:0,LIGHT:1,DETAILED:2};ContainerMemoryDump.prototype={__proto__:tr.model.TimedEvent.prototype,shiftTimestampsForward(amount){this.start+=amount;},get memoryAllocatorDumps(){return this.memoryAllocatorDumps_;},set memoryAllocatorDumps(memoryAllocatorDumps){this.memoryAllocatorDumps_=memoryAllocatorDumps;this.forceRebuildingMemoryAllocatorDumpByFullNameIndex();},getMemoryAllocatorDumpByFullName(fullName){if(this.memoryAllocatorDumps_===undefined)return undefined;if(this.memoryAllocatorDumpsByFullName_===undefined){const index={};function addDumpsToIndex(dumps){dumps.forEach(function(dump){index[dump.fullName]=dump;addDumpsToIndex(dump.children);});}
+addDumpsToIndex(this.memoryAllocatorDumps_);this.memoryAllocatorDumpsByFullName_=index;}
+return this.memoryAllocatorDumpsByFullName_[fullName];},forceRebuildingMemoryAllocatorDumpByFullNameIndex(){this.memoryAllocatorDumpsByFullName_=undefined;},iterateRootAllocatorDumps(fn,opt_this){if(this.memoryAllocatorDumps===undefined)return;this.memoryAllocatorDumps.forEach(fn,opt_this||this);}};return{ContainerMemoryDump,};});'use strict';tr.exportTo('tr.model',function(){function MemoryAllocatorDump(containerMemoryDump,fullName,opt_guid){this.fullName=fullName;this.parent=undefined;this.children=[];this.numerics={};this.diagnostics={};this.containerMemoryDump=containerMemoryDump;this.owns=undefined;this.ownedBy=[];this.ownedBySiblingSizes=new Map();this.retains=[];this.retainedBy=[];this.weak=false;this.infos=[];this.guid=opt_guid;}
+MemoryAllocatorDump.SIZE_NUMERIC_NAME='size';MemoryAllocatorDump.EFFECTIVE_SIZE_NUMERIC_NAME='effective_size';MemoryAllocatorDump.RESIDENT_SIZE_NUMERIC_NAME='resident_size';MemoryAllocatorDump.DISPLAYED_SIZE_NUMERIC_NAME=MemoryAllocatorDump.EFFECTIVE_SIZE_NUMERIC_NAME;MemoryAllocatorDump.prototype={get name(){return this.fullName.substring(this.fullName.lastIndexOf('/')+1);},get quantifiedName(){return'\''+this.fullName+'\' in '+
+this.containerMemoryDump.containerName;},getDescendantDumpByFullName(fullName){return this.containerMemoryDump.getMemoryAllocatorDumpByFullName(this.fullName+'/'+fullName);},isDescendantOf(otherDump){if(this===otherDump)return true;if(this.parent===undefined)return false;return this.parent.isDescendantOf(otherDump);},addNumeric(name,numeric){if(!(numeric instanceof tr.b.Scalar)){throw new Error('Numeric value must be an instance of Scalar.');}
+if(name in this.numerics){throw new Error('Duplicate numeric name: '+name+'.');}
+this.numerics[name]=numeric;},addDiagnostic(name,text){if(typeof text!=='string'){throw new Error('Diagnostic text must be a string.');}
+if(name in this.diagnostics){throw new Error('Duplicate diagnostic name: '+name+'.');}
+this.diagnostics[name]=text;},aggregateNumericsRecursively(opt_model){const numericNames=new Set();this.children.forEach(function(child){child.aggregateNumericsRecursively(opt_model);for(const[item,value]of Object.entries(child.numerics)){numericNames.add(item,value);}},this);numericNames.forEach(function(numericName){if(numericName===MemoryAllocatorDump.SIZE_NUMERIC_NAME||numericName===MemoryAllocatorDump.EFFECTIVE_SIZE_NUMERIC_NAME||this.numerics[numericName]!==undefined){return;}
+this.numerics[numericName]=MemoryAllocatorDump.aggregateNumerics(this.children.map(function(child){return child.numerics[numericName];}),opt_model);},this);}};MemoryAllocatorDump.aggregateNumerics=function(numerics,opt_model){let shouldLogWarning=!!opt_model;let aggregatedUnit=undefined;let aggregatedValue=0;numerics.forEach(function(numeric){if(numeric===undefined)return;const unit=numeric.unit;if(aggregatedUnit===undefined){aggregatedUnit=unit;}else if(aggregatedUnit!==unit){if(shouldLogWarning){opt_model.importWarning({type:'numeric_parse_error',message:'Multiple units provided for numeric: \''+
+aggregatedUnit.unitName+'\' and \''+unit.unitName+'\'.'});shouldLogWarning=false;}
+aggregatedUnit=tr.b.Unit.byName.unitlessNumber_smallerIsBetter;}
+aggregatedValue+=numeric.value;},this);if(aggregatedUnit===undefined)return undefined;return new tr.b.Scalar(aggregatedUnit,aggregatedValue);};function MemoryAllocatorDumpLink(source,target,opt_importance){this.source=source;this.target=target;this.importance=opt_importance;this.size=undefined;}
+const MemoryAllocatorDumpInfoType={PROVIDED_SIZE_LESS_THAN_AGGREGATED_CHILDREN:0,PROVIDED_SIZE_LESS_THAN_LARGEST_OWNER:1};return{MemoryAllocatorDump,MemoryAllocatorDumpLink,MemoryAllocatorDumpInfoType,};});'use strict';tr.exportTo('tr.model',function(){function GlobalMemoryDump(model,start){tr.model.ContainerMemoryDump.call(this,start);this.model=model;this.processMemoryDumps={};}
+const SIZE_NUMERIC_NAME=tr.model.MemoryAllocatorDump.SIZE_NUMERIC_NAME;const EFFECTIVE_SIZE_NUMERIC_NAME=tr.model.MemoryAllocatorDump.EFFECTIVE_SIZE_NUMERIC_NAME;const MemoryAllocatorDumpInfoType=tr.model.MemoryAllocatorDumpInfoType;const PROVIDED_SIZE_LESS_THAN_AGGREGATED_CHILDREN=MemoryAllocatorDumpInfoType.PROVIDED_SIZE_LESS_THAN_AGGREGATED_CHILDREN;const PROVIDED_SIZE_LESS_THAN_LARGEST_OWNER=MemoryAllocatorDumpInfoType.PROVIDED_SIZE_LESS_THAN_LARGEST_OWNER;function getSize(dump){const numeric=dump.numerics[SIZE_NUMERIC_NAME];if(numeric===undefined)return 0;return numeric.value;}
+function hasSize(dump){return dump.numerics[SIZE_NUMERIC_NAME]!==undefined;}
+function optional(value,defaultValue){if(value===undefined)return defaultValue;return value;}
+GlobalMemoryDump.prototype={__proto__:tr.model.ContainerMemoryDump.prototype,get stableId(){return'memory.'+this.model.globalMemoryDumps.indexOf(this);},get userFriendlyName(){return'Global memory dump at '+
+tr.b.Unit.byName.timeStampInMs.format(this.start);},get containerName(){return'global space';},finalizeGraph(){this.removeWeakDumps();this.setUpTracingOverheadOwnership();this.aggregateNumerics();this.calculateSizes();this.calculateEffectiveSizes();this.discountTracingOverheadFromVmRegions();this.forceRebuildingMemoryAllocatorDumpByFullNameIndices();},removeWeakDumps(){this.traverseAllocatorDumpsInDepthFirstPreOrder(function(dump){if(dump.weak)return;if((dump.owns!==undefined&&dump.owns.target.weak)||(dump.parent!==undefined&&dump.parent.weak)){dump.weak=true;}});function removeWeakDumpsFromListRecursively(dumps){tr.b.inPlaceFilter(dumps,function(dump){if(dump.weak){return false;}
+removeWeakDumpsFromListRecursively(dump.children);tr.b.inPlaceFilter(dump.ownedBy,function(ownershipLink){return!ownershipLink.source.weak;});return true;});}
+this.iterateContainerDumps(function(containerDump){const memoryAllocatorDumps=containerDump.memoryAllocatorDumps;if(memoryAllocatorDumps!==undefined){removeWeakDumpsFromListRecursively(memoryAllocatorDumps);}});},calculateSizes(){this.traverseAllocatorDumpsInDepthFirstPostOrder(this.calculateMemoryAllocatorDumpSize_.bind(this));},calculateMemoryAllocatorDumpSize_(dump){let shouldDefineSize=false;function getDependencySize(dependencyDump){const numeric=dependencyDump.numerics[SIZE_NUMERIC_NAME];if(numeric===undefined)return 0;shouldDefineSize=true;return numeric.value;}
+const sizeNumeric=dump.numerics[SIZE_NUMERIC_NAME];let size=0;let checkDependencySizeIsConsistent=function(){};if(sizeNumeric!==undefined){size=sizeNumeric.value;shouldDefineSize=true;if(sizeNumeric.unit!==tr.b.Unit.byName.sizeInBytes_smallerIsBetter){this.model.importWarning({type:'memory_dump_parse_error',message:'Invalid unit of \'size\' numeric of memory allocator '+'dump '+dump.quantifiedName+': '+
+sizeNumeric.unit.unitName+'.'});}
+checkDependencySizeIsConsistent=function(dependencySize,dependencyInfoType,dependencyName){if(size>=dependencySize)return;this.model.importWarning({type:'memory_dump_parse_error',message:'Size provided by memory allocator dump \''+
+dump.fullName+'\''+
+tr.b.Unit.byName.sizeInBytes.format(size)+') is less than '+dependencyName+' ('+
+tr.b.Unit.byName.sizeInBytes.format(dependencySize)+').'});dump.infos.push({type:dependencyInfoType,providedSize:size,dependencySize});}.bind(this);}
+let aggregatedChildrenSize=0;const allOverlaps={};dump.children.forEach(function(childDump){function aggregateDescendantDump(descendantDump){const ownedDumpLink=descendantDump.owns;if(ownedDumpLink!==undefined&&ownedDumpLink.target.isDescendantOf(dump)){let ownedChildDump=ownedDumpLink.target;while(ownedChildDump.parent!==dump){ownedChildDump=ownedChildDump.parent;}
+if(childDump!==ownedChildDump){const ownedBySiblingSize=getDependencySize(descendantDump);if(ownedBySiblingSize>0){const previousTotalOwnedBySiblingSize=ownedChildDump.ownedBySiblingSizes.get(childDump)||0;const updatedTotalOwnedBySiblingSize=previousTotalOwnedBySiblingSize+ownedBySiblingSize;ownedChildDump.ownedBySiblingSizes.set(childDump,updatedTotalOwnedBySiblingSize);}}
+return;}
+if(descendantDump.children.length===0){aggregatedChildrenSize+=getDependencySize(descendantDump);return;}
+descendantDump.children.forEach(aggregateDescendantDump);}
+aggregateDescendantDump(childDump);});checkDependencySizeIsConsistent(aggregatedChildrenSize,PROVIDED_SIZE_LESS_THAN_AGGREGATED_CHILDREN,'the aggregated size of its children');let largestOwnerSize=0;dump.ownedBy.forEach(function(ownershipLink){const owner=ownershipLink.source;const ownerSize=getDependencySize(owner);largestOwnerSize=Math.max(largestOwnerSize,ownerSize);});checkDependencySizeIsConsistent(largestOwnerSize,PROVIDED_SIZE_LESS_THAN_LARGEST_OWNER,'the size of its largest owner');if(!shouldDefineSize){delete dump.numerics[SIZE_NUMERIC_NAME];return;}
+size=Math.max(size,aggregatedChildrenSize,largestOwnerSize);dump.numerics[SIZE_NUMERIC_NAME]=new tr.b.Scalar(tr.b.Unit.byName.sizeInBytes_smallerIsBetter,size);if(aggregatedChildrenSize<size&&dump.children!==undefined&&dump.children.length>0){const virtualChild=new tr.model.MemoryAllocatorDump(dump.containerMemoryDump,dump.fullName+'/<unspecified>');virtualChild.parent=dump;dump.children.unshift(virtualChild);virtualChild.numerics[SIZE_NUMERIC_NAME]=new tr.b.Scalar(tr.b.Unit.byName.sizeInBytes_smallerIsBetter,size-aggregatedChildrenSize);}},calculateEffectiveSizes(){this.traverseAllocatorDumpsInDepthFirstPostOrder(this.calculateDumpSubSizes_.bind(this));this.traverseAllocatorDumpsInDepthFirstPostOrder(this.calculateDumpOwnershipCoefficient_.bind(this));this.traverseAllocatorDumpsInDepthFirstPreOrder(this.calculateDumpCumulativeOwnershipCoefficient_.bind(this));this.traverseAllocatorDumpsInDepthFirstPostOrder(this.calculateDumpEffectiveSize_.bind(this));},calculateDumpSubSizes_(dump){if(!hasSize(dump))return;if(dump.children===undefined||dump.children.length===0){const size=getSize(dump);dump.notOwningSubSize_=size;dump.notOwnedSubSize_=size;return;}
+let notOwningSubSize=0;dump.children.forEach(function(childDump){if(childDump.owns!==undefined)return;notOwningSubSize+=optional(childDump.notOwningSubSize_,0);});dump.notOwningSubSize_=notOwningSubSize;let notOwnedSubSize=0;dump.children.forEach(function(childDump){if(childDump.ownedBy.length===0){notOwnedSubSize+=optional(childDump.notOwnedSubSize_,0);return;}
+let largestChildOwnerSize=0;childDump.ownedBy.forEach(function(ownershipLink){largestChildOwnerSize=Math.max(largestChildOwnerSize,getSize(ownershipLink.source));});notOwnedSubSize+=getSize(childDump)-largestChildOwnerSize;});dump.notOwnedSubSize_=notOwnedSubSize;},calculateDumpOwnershipCoefficient_(dump){if(!hasSize(dump))return;if(dump.ownedBy.length===0)return;const owners=dump.ownedBy.map(function(ownershipLink){return{dump:ownershipLink.source,importance:optional(ownershipLink.importance,0),notOwningSubSize:optional(ownershipLink.source.notOwningSubSize_,0)};});owners.sort(function(a,b){if(a.importance===b.importance){return a.notOwningSubSize-b.notOwningSubSize;}
+return b.importance-a.importance;});let currentImportanceStartPos=0;let alreadyAttributedSubSize=0;while(currentImportanceStartPos<owners.length){const currentImportance=owners[currentImportanceStartPos].importance;let nextImportanceStartPos=currentImportanceStartPos+1;while(nextImportanceStartPos<owners.length&&owners[nextImportanceStartPos].importance===currentImportance){nextImportanceStartPos++;}
+let attributedNotOwningSubSize=0;for(let pos=currentImportanceStartPos;pos<nextImportanceStartPos;pos++){const owner=owners[pos];const notOwningSubSize=owner.notOwningSubSize;if(notOwningSubSize>alreadyAttributedSubSize){attributedNotOwningSubSize+=(notOwningSubSize-alreadyAttributedSubSize)/(nextImportanceStartPos-pos);alreadyAttributedSubSize=notOwningSubSize;}
+let owningCoefficient=0;if(notOwningSubSize!==0){owningCoefficient=attributedNotOwningSubSize/notOwningSubSize;}
+owner.dump.owningCoefficient_=owningCoefficient;}
+currentImportanceStartPos=nextImportanceStartPos;}
+const notOwnedSubSize=optional(dump.notOwnedSubSize_,0);const remainderSubSize=notOwnedSubSize-alreadyAttributedSubSize;let ownedCoefficient=0;if(notOwnedSubSize!==0){ownedCoefficient=remainderSubSize/notOwnedSubSize;}
+dump.ownedCoefficient_=ownedCoefficient;},calculateDumpCumulativeOwnershipCoefficient_(dump){if(!hasSize(dump))return;let cumulativeOwnedCoefficient=optional(dump.ownedCoefficient_,1);const parent=dump.parent;if(dump.parent!==undefined){cumulativeOwnedCoefficient*=dump.parent.cumulativeOwnedCoefficient_;}
+dump.cumulativeOwnedCoefficient_=cumulativeOwnedCoefficient;let cumulativeOwningCoefficient;if(dump.owns!==undefined){cumulativeOwningCoefficient=dump.owningCoefficient_*dump.owns.target.cumulativeOwningCoefficient_;}else if(dump.parent!==undefined){cumulativeOwningCoefficient=dump.parent.cumulativeOwningCoefficient_;}else{cumulativeOwningCoefficient=1;}
+dump.cumulativeOwningCoefficient_=cumulativeOwningCoefficient;},calculateDumpEffectiveSize_(dump){if(!hasSize(dump)){delete dump.numerics[EFFECTIVE_SIZE_NUMERIC_NAME];return;}
+let effectiveSize;if(dump.children===undefined||dump.children.length===0){effectiveSize=getSize(dump)*dump.cumulativeOwningCoefficient_*dump.cumulativeOwnedCoefficient_;}else{effectiveSize=0;dump.children.forEach(function(childDump){if(!hasSize(childDump))return;effectiveSize+=childDump.numerics[EFFECTIVE_SIZE_NUMERIC_NAME].value;});}
+dump.numerics[EFFECTIVE_SIZE_NUMERIC_NAME]=new tr.b.Scalar(tr.b.Unit.byName.sizeInBytes_smallerIsBetter,effectiveSize);},aggregateNumerics(){this.iterateRootAllocatorDumps(function(dump){dump.aggregateNumericsRecursively(this.model);});this.iterateRootAllocatorDumps(this.propagateNumericsAndDiagnosticsRecursively);for(const processMemoryDump of Object.values(this.processMemoryDumps)){processMemoryDump.iterateRootAllocatorDumps(function(dump){dump.aggregateNumericsRecursively(this.model);},this);}},propagateNumericsAndDiagnosticsRecursively(globalAllocatorDump){['numerics','diagnostics'].forEach(function(field){for(const[name,value]of
+Object.entries(globalAllocatorDump[field])){globalAllocatorDump.ownedBy.forEach(function(ownershipLink){const processAllocatorDump=ownershipLink.source;if(processAllocatorDump[field][name]!==undefined){return;}
+processAllocatorDump[field][name]=value;});}});globalAllocatorDump.children.forEach(this.propagateNumericsAndDiagnosticsRecursively,this);},setUpTracingOverheadOwnership(){for(const dump of Object.values(this.processMemoryDumps)){dump.setUpTracingOverheadOwnership(this.model);}},discountTracingOverheadFromVmRegions(){for(const dump of Object.values(this.processMemoryDumps)){dump.discountTracingOverheadFromVmRegions(this.model);}},forceRebuildingMemoryAllocatorDumpByFullNameIndices(){this.iterateContainerDumps(function(containerDump){containerDump.forceRebuildingMemoryAllocatorDumpByFullNameIndex();});},iterateContainerDumps(fn){fn.call(this,this);for(const processDump of Object.values(this.processMemoryDumps)){fn.call(this,processDump);}},iterateAllRootAllocatorDumps(fn){this.iterateContainerDumps(function(containerDump){containerDump.iterateRootAllocatorDumps(fn,this);});},traverseAllocatorDumpsInDepthFirstPostOrder(fn){const visitedDumps=new WeakSet();const openDumps=new WeakSet();function visit(dump){if(visitedDumps.has(dump))return;if(openDumps.has(dump)){throw new Error(dump.userFriendlyName+' contains a cycle');}
+openDumps.add(dump);dump.ownedBy.forEach(function(ownershipLink){visit.call(this,ownershipLink.source);},this);dump.children.forEach(visit,this);fn.call(this,dump);visitedDumps.add(dump);openDumps.delete(dump);}
+this.iterateAllRootAllocatorDumps(visit);},traverseAllocatorDumpsInDepthFirstPreOrder(fn){const visitedDumps=new WeakSet();function visit(dump){if(visitedDumps.has(dump))return;if(dump.owns!==undefined&&!visitedDumps.has(dump.owns.target)){return;}
+if(dump.parent!==undefined&&!visitedDumps.has(dump.parent)){return;}
+fn.call(this,dump);visitedDumps.add(dump);dump.ownedBy.forEach(function(ownershipLink){visit.call(this,ownershipLink.source);},this);dump.children.forEach(visit,this);}
+this.iterateAllRootAllocatorDumps(visit);}};tr.model.EventRegistry.register(GlobalMemoryDump,{name:'globalMemoryDump',pluralName:'globalMemoryDumps'});return{GlobalMemoryDump,};});'use strict';tr.exportTo('tr.model',function(){const InstantEventType={GLOBAL:1,PROCESS:2};function InstantEvent(category,title,colorId,start,args,parent){tr.model.TimedEvent.call(this,start);this.category=category||'';this.title=title;this.colorId=colorId;this.args=args;this.parent_=parent;this.type=undefined;}
+InstantEvent.prototype={__proto__:tr.model.TimedEvent.prototype,};function GlobalInstantEvent(category,title,colorId,start,args,parent){InstantEvent.apply(this,arguments);this.type=InstantEventType.GLOBAL;}
+GlobalInstantEvent.prototype={__proto__:InstantEvent.prototype,get userFriendlyName(){return'Global instant event '+this.title+' @ '+
+tr.b.Unit.byName.timeStampInMs.format(start);},get stableId(){return'instant.'+this.parent_.instantEvents.indexOf(this);},};function ProcessInstantEvent(category,title,colorId,start,args,parent){InstantEvent.apply(this,arguments);this.type=InstantEventType.PROCESS;}
+ProcessInstantEvent.prototype={__proto__:InstantEvent.prototype,get userFriendlyName(){return'Process-level instant event '+this.title+' @ '+
+tr.b.Unit.byName.timeStampInMs.format(start);},get stableId(){return this.parent_.stableId+'.instant.'+
+this.parent_.instantEvents.indexOf(this);},};tr.model.EventRegistry.register(InstantEvent,{name:'instantEvent',pluralName:'instantEvents'});return{GlobalInstantEvent,ProcessInstantEvent,InstantEventType,InstantEvent,};});'use strict';tr.exportTo('tr.model',function(){const Cpu=tr.model.Cpu;const ProcessBase=tr.model.ProcessBase;function Kernel(model){ProcessBase.call(this,model);this.cpus={};this.softwareMeasuredCpuCount_=undefined;}
+Kernel.compare=function(x,y){return 0;};Kernel.prototype={__proto__:ProcessBase.prototype,compareTo(that){return Kernel.compare(this,that);},get userFriendlyName(){return'Kernel';},get userFriendlyDetails(){return'Kernel';},get stableId(){return'Kernel';},getOrCreateCpu(cpuNumber){if(!this.cpus[cpuNumber]){this.cpus[cpuNumber]=new Cpu(this,cpuNumber);}
+return this.cpus[cpuNumber];},get softwareMeasuredCpuCount(){return this.softwareMeasuredCpuCount_;},set softwareMeasuredCpuCount(softwareMeasuredCpuCount){if(this.softwareMeasuredCpuCount_!==undefined&&this.softwareMeasuredCpuCount_!==softwareMeasuredCpuCount){throw new Error('Cannot change the softwareMeasuredCpuCount once it is set');}
+this.softwareMeasuredCpuCount_=softwareMeasuredCpuCount;},get bestGuessAtCpuCount(){const realCpuCount=Object.keys(this.cpus).length;if(realCpuCount!==0){return realCpuCount;}
+return this.softwareMeasuredCpuCount;},updateBounds(){ProcessBase.prototype.updateBounds.call(this);for(const cpuNumber in this.cpus){const cpu=this.cpus[cpuNumber];cpu.updateBounds();this.bounds.addRange(cpu.bounds);}},createSubSlices(){ProcessBase.prototype.createSubSlices.call(this);for(const cpuNumber in this.cpus){const cpu=this.cpus[cpuNumber];cpu.createSubSlices();}},addCategoriesToDict(categoriesDict){ProcessBase.prototype.addCategoriesToDict.call(this,categoriesDict);for(const cpuNumber in this.cpus){this.cpus[cpuNumber].addCategoriesToDict(categoriesDict);}},getSettingsKey(){return'kernel';},*childEventContainers(){yield*ProcessBase.prototype.childEventContainers.call(this);yield*Object.values(this.cpus);},};return{Kernel,};});'use strict';tr.exportTo('tr.model',function(){function ModelIndices(model){this.flowEventsById_={};model.flowEvents.forEach(function(fe){if(fe.id!==undefined){if(!this.flowEventsById_.hasOwnProperty(fe.id)){this.flowEventsById_[fe.id]=[];}
+this.flowEventsById_[fe.id].push(fe);}},this);}
+ModelIndices.prototype={addEventWithId(id,event){if(!this.flowEventsById_.hasOwnProperty(id)){this.flowEventsById_[id]=[];}
+this.flowEventsById_[id].push(event);},getFlowEventsWithId(id){if(!this.flowEventsById_.hasOwnProperty(id)){return[];}
+return this.flowEventsById_[id];}};return{ModelIndices,};});'use strict';tr.exportTo('tr.model',function(){function ModelStats(){this.traceEventCountsByKey_=new Map();this.allTraceEventStats_=[];this.traceEventStatsInTimeIntervals_=new Map();this.allTraceEventStatsInTimeIntervals_=[];this.hasEventSizesinBytes_=false;this.traceImportDurationMs_=undefined;}
+ModelStats.prototype={TIME_INTERVAL_SIZE_IN_MS:100,willProcessBasicTraceEvent(phase,category,title,ts,opt_eventSizeinBytes){const key=phase+'/'+category+'/'+title;let eventStats=this.traceEventCountsByKey_.get(key);if(eventStats===undefined){eventStats={phase,category,title,numEvents:0,totalEventSizeinBytes:0};this.traceEventCountsByKey_.set(key,eventStats);this.allTraceEventStats_.push(eventStats);}
+eventStats.numEvents++;const timeIntervalKey=Math.floor(tr.b.Unit.timestampFromUs(ts)/this.TIME_INTERVAL_SIZE_IN_MS);let eventStatsByTimeInverval=this.traceEventStatsInTimeIntervals_.get(timeIntervalKey);if(eventStatsByTimeInverval===undefined){eventStatsByTimeInverval={timeInterval:timeIntervalKey,numEvents:0,totalEventSizeinBytes:0};this.traceEventStatsInTimeIntervals_.set(timeIntervalKey,eventStatsByTimeInverval);this.allTraceEventStatsInTimeIntervals_.push(eventStatsByTimeInverval);}
+eventStatsByTimeInverval.numEvents++;if(opt_eventSizeinBytes!==undefined){this.hasEventSizesinBytes_=true;eventStats.totalEventSizeinBytes+=opt_eventSizeinBytes;eventStatsByTimeInverval.totalEventSizeinBytes+=opt_eventSizeinBytes;}},get allTraceEventStats(){return this.allTraceEventStats_;},get allTraceEventStatsInTimeIntervals(){return this.allTraceEventStatsInTimeIntervals_;},get hasEventSizesinBytes(){return this.hasEventSizesinBytes_;},get traceImportDurationMs(){return this.traceImportDurationMs_;},set traceImportDurationMs(traceImportDurationMs){this.traceImportDurationMs_=traceImportDurationMs;}};return{ModelStats,};});'use strict';tr.exportTo('tr.model',function(){function VMRegion(startAddress,sizeInBytes,protectionFlags,mappedFile,byteStats){this.startAddress=startAddress;this.sizeInBytes=sizeInBytes;this.protectionFlags=protectionFlags;this.mappedFile=mappedFile||'';this.byteStats=byteStats||{};}
+VMRegion.PROTECTION_FLAG_READ=4;VMRegion.PROTECTION_FLAG_WRITE=2;VMRegion.PROTECTION_FLAG_EXECUTE=1;VMRegion.PROTECTION_FLAG_MAYSHARE=128;VMRegion.prototype={get uniqueIdWithinProcess(){return this.mappedFile+'#'+this.startAddress;},get protectionFlagsToString(){if(this.protectionFlags===undefined)return undefined;return((this.protectionFlags&VMRegion.PROTECTION_FLAG_READ?'r':'-')+
+(this.protectionFlags&VMRegion.PROTECTION_FLAG_WRITE?'w':'-')+
+(this.protectionFlags&VMRegion.PROTECTION_FLAG_EXECUTE?'x':'-')+
+(this.protectionFlags&VMRegion.PROTECTION_FLAG_MAYSHARE?'s':'p'));}};VMRegion.fromDict=function(dict){return new VMRegion(dict.startAddress,dict.sizeInBytes,dict.protectionFlags,dict.mappedFile,dict.byteStats);};function VMRegionClassificationNode(opt_rule){this.rule_=opt_rule||VMRegionClassificationNode.CLASSIFICATION_RULES;this.hasRegions=false;this.sizeInBytes=undefined;this.byteStats={};this.children_=undefined;this.regions_=[];}
+VMRegionClassificationNode.CLASSIFICATION_RULES={name:'Total',children:[{name:'Android',file:/^\/dev\/ashmem(?!\/libc malloc)/,children:[{name:'Java runtime',file:/^\/dev\/ashmem\/dalvik-/,children:[{name:'Spaces',file:/\/dalvik-(alloc|main|large object|non moving|zygote) space/,children:[{name:'Normal',file:/\/dalvik-(alloc|main)/},{name:'Large',file:/\/dalvik-large object/},{name:'Zygote',file:/\/dalvik-zygote/},{name:'Non-moving',file:/\/dalvik-non moving/}]},{name:'Linear Alloc',file:/\/dalvik-LinearAlloc/},{name:'Indirect Reference Table',file:/\/dalvik-indirect.ref/},{name:'Cache',file:/\/dalvik-jit-code-cache/},{name:'Accounting'}]},{name:'Cursor',file:/\/CursorWindow/},{name:'Ashmem'}]},{name:'Native heap',file:/^((\[heap\])|(\[anon:)|(\/dev\/ashmem\/libc malloc)|(\[discounted tracing overhead\])|$)/},{name:'Stack',file:/^\[stack/},{name:'Files',file:/\.((((jar)|(apk)|(ttf)|(odex)|(oat)|(art))$)|(dex)|(so))/,children:[{name:'so',file:/\.so/},{name:'jar',file:/\.jar$/},{name:'apk',file:/\.apk$/},{name:'ttf',file:/\.ttf$/},{name:'dex',file:/\.((dex)|(odex$))/},{name:'oat',file:/\.oat$/},{name:'art',file:/\.art$/}]},{name:'Devices',file:/(^\/dev\/)|(anon_inode:dmabuf)/,children:[{name:'GPU',file:/\/((nv)|(mali)|(kgsl))/},{name:'DMA',file:/anon_inode:dmabuf/}]}]};VMRegionClassificationNode.OTHER_RULE={name:'Other'};VMRegionClassificationNode.fromRegions=function(regions,opt_rules){const tree=new VMRegionClassificationNode(opt_rules);tree.regions_=regions;for(let i=0;i<regions.length;i++){tree.addStatsFromRegion_(regions[i]);}
+return tree;};VMRegionClassificationNode.prototype={get title(){return this.rule_.name;},get children(){if(this.isLeafNode){return undefined;}
+if(this.children_===undefined){this.buildTree_();}
+return this.children_;},get regions(){if(!this.isLeafNode){return undefined;}
+return this.regions_;},get allRegionsForTesting(){if(this.regions_!==undefined){if(this.children_!==undefined){throw new Error('Internal error: a VM region classification node '+'cannot have both regions and children');}
+return this.regions_;}
+let regions=[];this.children_.forEach(function(childNode){regions=regions.concat(childNode.allRegionsForTesting);});return regions;},get isLeafNode(){const children=this.rule_.children;return children===undefined||children.length===0;},addRegion(region){this.addRegionRecursively_(region,true);},someRegion(fn,opt_this){if(this.regions_!==undefined){return this.regions_.some(fn,opt_this);}
+return this.children_.some(function(childNode){return childNode.someRegion(fn,opt_this);});},addRegionRecursively_(region,addStatsToThisNode){if(addStatsToThisNode){this.addStatsFromRegion_(region);}
+if(this.regions_!==undefined){if(this.children_!==undefined){throw new Error('Internal error: a VM region classification node '+'cannot have both regions and children');}
+this.regions_.push(region);return;}
+function regionRowMatchesChildNide(child){const fileRegExp=child.rule_.file;if(fileRegExp===undefined)return true;return fileRegExp.test(region.mappedFile);}
+let matchedChild=this.children_.find(regionRowMatchesChildNide);if(matchedChild===undefined){if(this.children_.length!==this.rule_.children.length){throw new Error('Internal error');}
+matchedChild=new VMRegionClassificationNode(VMRegionClassificationNode.OTHER_RULE);this.children_.push(matchedChild);}
+matchedChild.addRegionRecursively_(region,true);},buildTree_(){const cachedRegions=this.regions_;this.regions_=undefined;this.buildChildNodesRecursively_();for(let i=0;i<cachedRegions.length;i++){this.addRegionRecursively_(cachedRegions[i],false);}},buildChildNodesRecursively_(){if(this.children_!==undefined){throw new Error('Internal error: Classification node already has children');}
+if(this.regions_!==undefined&&this.regions_.length!==0){throw new Error('Internal error: Classification node should have no regions');}
+if(this.isLeafNode){return;}
+this.regions_=undefined;this.children_=this.rule_.children.map(function(childRule){const child=new VMRegionClassificationNode(childRule);child.buildChildNodesRecursively_();return child;});},addStatsFromRegion_(region){this.hasRegions=true;const regionSizeInBytes=region.sizeInBytes;if(regionSizeInBytes!==undefined){this.sizeInBytes=(this.sizeInBytes||0)+regionSizeInBytes;}
+const thisByteStats=this.byteStats;const regionByteStats=region.byteStats;for(const byteStatName in regionByteStats){const regionByteStatValue=regionByteStats[byteStatName];if(regionByteStatValue===undefined)continue;thisByteStats[byteStatName]=(thisByteStats[byteStatName]||0)+regionByteStatValue;}
+if(region.mappedFile.includes('/base.odex')||region.mappedFile.includes('/base.vdex')){if(region.byteStats.proportionalResident!==undefined){thisByteStats.javaBasePss=(thisByteStats.javaBasePss||0)+
+region.byteStats.proportionalResident;}
+if(region.byteStats.privateCleanResident!==undefined){thisByteStats.javaBaseCleanResident=(thisByteStats.javaBaseCleanResident||0)+
+region.byteStats.privateCleanResident;}
+if(region.byteStats.sharedCleanResident!==undefined){thisByteStats.javaBaseCleanResident=(thisByteStats.javaBaseCleanResident||0)+
+region.byteStats.sharedCleanResident;}}
+const textProtectionFlags=(VMRegion.PROTECTION_FLAG_READ|VMRegion.PROTECTION_FLAG_EXECUTE);if((region.protectionFlags===textProtectionFlags)&&(region.mappedFile.includes('/base.apk')||region.mappedFile.includes('/libchrome.so'))){if(regionSizeInBytes!==undefined){this.nativeLibrarySizeInBytes=(this.nativeLibrarySizeInBytes||0)+regionSizeInBytes;}
+if(region.byteStats.privateCleanResident!==undefined){thisByteStats.nativeLibraryPrivateCleanResident=(thisByteStats.nativeLibraryPrivateCleanResident||0)+
+region.byteStats.privateCleanResident;}
+if(region.byteStats.sharedCleanResident!==undefined){thisByteStats.nativeLibrarySharedCleanResident=(thisByteStats.nativeLibrarySharedCleanResident||0)+
+region.byteStats.sharedCleanResident;}
+if(region.byteStats.proportionalResident!==undefined){thisByteStats.nativeLibraryProportionalResident=(thisByteStats.nativeLibraryProportionalResident||0)+
+region.byteStats.proportionalResident;}}}};return{VMRegion,VMRegionClassificationNode,};});'use strict';tr.exportTo('tr.model',function(){const DISCOUNTED_ALLOCATOR_NAMES=['winheap','malloc'];const TRACING_OVERHEAD_PATH=['allocated_objects','tracing_overhead'];const SIZE_NUMERIC_NAME=tr.model.MemoryAllocatorDump.SIZE_NUMERIC_NAME;const RESIDENT_SIZE_NUMERIC_NAME=tr.model.MemoryAllocatorDump.RESIDENT_SIZE_NUMERIC_NAME;function getSizeNumericValue(dump,sizeNumericName){const sizeNumeric=dump.numerics[sizeNumericName];if(sizeNumeric===undefined)return 0;return sizeNumeric.value;}
+function ProcessMemoryDump(globalMemoryDump,process,start){tr.model.ContainerMemoryDump.call(this,start);this.process=process;this.globalMemoryDump=globalMemoryDump;this.totals=undefined;this.vmRegions=undefined;this.heapDumps=undefined;this.tracingOverheadOwnershipSetUp_=false;this.tracingOverheadDiscountedFromVmRegions_=false;}
+ProcessMemoryDump.prototype={__proto__:tr.model.ContainerMemoryDump.prototype,get stableId(){return this.process.stableId+'.memory.'+
+this.process.memoryDumps.indexOf(this);},get userFriendlyName(){return'Process memory dump at '+
+tr.b.Unit.byName.timeStampInMs.format(this.start);},get containerName(){return this.process.userFriendlyName;},get processMemoryDumps(){const dumps={};dumps[this.process.pid]=this;return dumps;},get hasOwnVmRegions(){return this.vmRegions!==undefined;},setUpTracingOverheadOwnership(opt_model){if(this.tracingOverheadOwnershipSetUp_)return;this.tracingOverheadOwnershipSetUp_=true;const tracingDump=this.getMemoryAllocatorDumpByFullName('tracing');if(tracingDump===undefined||tracingDump.owns!==undefined){return;}
+if(tracingDump.owns!==undefined)return;const hasDiscountedFromAllocatorDumps=DISCOUNTED_ALLOCATOR_NAMES.some(function(allocatorName){const allocatorDump=this.getMemoryAllocatorDumpByFullName(allocatorName);if(allocatorDump===undefined){return false;}
+let nextPathIndex=0;let currentDump=allocatorDump;let currentFullName=allocatorName;for(;nextPathIndex<TRACING_OVERHEAD_PATH.length;nextPathIndex++){const childFullName=currentFullName+'/'+
+TRACING_OVERHEAD_PATH[nextPathIndex];const childDump=this.getMemoryAllocatorDumpByFullName(childFullName);if(childDump===undefined)break;currentDump=childDump;currentFullName=childFullName;}
+for(;nextPathIndex<TRACING_OVERHEAD_PATH.length;nextPathIndex++){const childFullName=currentFullName+'/'+
+TRACING_OVERHEAD_PATH[nextPathIndex];const childDump=new tr.model.MemoryAllocatorDump(currentDump.containerMemoryDump,childFullName);childDump.parent=currentDump;currentDump.children.push(childDump);currentFullName=childFullName;currentDump=childDump;}
+const ownershipLink=new tr.model.MemoryAllocatorDumpLink(tracingDump,currentDump);tracingDump.owns=ownershipLink;currentDump.ownedBy.push(ownershipLink);return true;},this);if(hasDiscountedFromAllocatorDumps){this.forceRebuildingMemoryAllocatorDumpByFullNameIndex();}},discountTracingOverheadFromVmRegions(opt_model){if(this.tracingOverheadDiscountedFromVmRegions_)return;this.tracingOverheadDiscountedFromVmRegions_=true;const tracingDump=this.getMemoryAllocatorDumpByFullName('tracing');if(tracingDump===undefined)return;const discountedSize=getSizeNumericValue(tracingDump,SIZE_NUMERIC_NAME);const discountedResidentSize=getSizeNumericValue(tracingDump,RESIDENT_SIZE_NUMERIC_NAME);if(discountedSize<=0&&discountedResidentSize<=0)return;if(this.totals!==undefined){if(this.totals.residentBytes!==undefined){this.totals.residentBytes-=discountedResidentSize;}
+if(this.totals.peakResidentBytes!==undefined){this.totals.peakResidentBytes-=discountedResidentSize;}}
+if(this.vmRegions!==undefined){const hasSizeInBytes=this.vmRegions.sizeInBytes!==undefined;const hasPrivateDirtyResident=this.vmRegions.byteStats.privateDirtyResident!==undefined;const hasProportionalResident=this.vmRegions.byteStats.proportionalResident!==undefined;if((hasSizeInBytes&&discountedSize>0)||((hasPrivateDirtyResident||hasProportionalResident)&&discountedResidentSize>0)){const byteStats={};if(hasPrivateDirtyResident){byteStats.privateDirtyResident=-discountedResidentSize;}
+if(hasProportionalResident){byteStats.proportionalResident=-discountedResidentSize;}
+this.vmRegions.addRegion(tr.model.VMRegion.fromDict({mappedFile:'[discounted tracing overhead]',sizeInBytes:hasSizeInBytes?-discountedSize:undefined,byteStats}));}}}};ProcessMemoryDump.hookUpMostRecentVmRegionsLinks=function(processDumps){let mostRecentVmRegions=undefined;processDumps.forEach(function(processDump){if(processDump.vmRegions!==undefined){mostRecentVmRegions=processDump.vmRegions;}
+processDump.mostRecentVmRegions=mostRecentVmRegions;});};tr.model.EventRegistry.register(ProcessMemoryDump,{name:'processMemoryDump',pluralName:'processMemoryDumps'});return{ProcessMemoryDump,};});'use strict';tr.exportTo('tr.model',function(){const ProcessBase=tr.model.ProcessBase;const ProcessInstantEvent=tr.model.ProcessInstantEvent;const Frame=tr.model.Frame;const ProcessMemoryDump=tr.model.ProcessMemoryDump;function Process(model,pid){if(model===undefined){throw new Error('model must be provided');}
+if(pid===undefined){throw new Error('pid must be provided');}
+tr.model.ProcessBase.call(this,model);this.pid=pid;this.name=undefined;this.labels=[];this.uptime_seconds=0;this.instantEvents=[];this.memoryDumps=[];this.frames=[];this.activities=[];}
+Process.compare=function(x,y){let tmp=tr.model.ProcessBase.compare(x,y);if(tmp)return tmp;if(x.name!==undefined){if(y.name!==undefined){tmp=x.name.localeCompare(y.name);}else{tmp=-1;}}else if(y.name!==undefined){tmp=1;}
+if(tmp)return tmp;tmp=tr.b.compareArrays(x.labels,y.labels,function(x,y){return x.localeCompare(y);});if(tmp)return tmp;return x.pid-y.pid;};Process.prototype={__proto__:tr.model.ProcessBase.prototype,get stableId(){return this.pid;},compareTo(that){return Process.compare(this,that);},*childEvents(){yield*ProcessBase.prototype.childEvents.call(this);yield*this.instantEvents;yield*this.frames;yield*this.memoryDumps;},addLabelIfNeeded(labelName){for(let i=0;i<this.labels.length;i++){if(this.labels[i]===labelName)return;}
+this.labels.push(labelName);},get userFriendlyName(){let res;if(this.name){res=this.name+' (pid '+this.pid+')';}else{res='Process '+this.pid;}
+if(this.labels.length){res+=': '+this.labels.join(', ');}
+if(this.uptime_seconds){res+=', uptime:'+this.uptime_seconds+'s';}
+return res;},get userFriendlyDetails(){if(this.name){return this.name+' (pid '+this.pid+')';}
+return'pid: '+this.pid;},getSettingsKey(){if(!this.name)return undefined;if(!this.labels.length)return'processes.'+this.name;return'processes.'+this.name+'.'+this.labels.join('.');},shiftTimestampsForward(amount){for(let i=0;i<this.instantEvents.length;i++){this.instantEvents[i].start+=amount;}
+for(let i=0;i<this.frames.length;i++){this.frames[i].shiftTimestampsForward(amount);}
+for(let i=0;i<this.memoryDumps.length;i++){this.memoryDumps[i].shiftTimestampsForward(amount);}
+for(let i=0;i<this.activities.length;i++){this.activities[i].shiftTimestampsForward(amount);}
+tr.model.ProcessBase.prototype.shiftTimestampsForward.apply(this,arguments);},updateBounds(){tr.model.ProcessBase.prototype.updateBounds.apply(this);for(let i=0;i<this.frames.length;i++){this.frames[i].addBoundsToRange(this.bounds);}
+for(let i=0;i<this.memoryDumps.length;i++){this.memoryDumps[i].addBoundsToRange(this.bounds);}
+for(let i=0;i<this.activities.length;i++){this.activities[i].addBoundsToRange(this.bounds);}},sortMemoryDumps(){this.memoryDumps.sort(function(x,y){return x.start-y.start;});tr.model.ProcessMemoryDump.hookUpMostRecentVmRegionsLinks(this.memoryDumps);}};return{Process,};});'use strict';tr.exportTo('tr.model',function(){function Sample(start,title,leafNode,thread,opt_cpu,opt_weight,opt_args){tr.model.TimedEvent.call(this,start);this.start_=start;this.title_=title;this.leafNode_=leafNode;this.thread_=thread;this.colorId_=leafNode.colorId;this.cpu_=opt_cpu;this.weight_=opt_weight;this.args=opt_args||{};}
+Sample.prototype={__proto__:tr.model.TimedEvent.prototype,get title(){return this.title_;},get colorId(){return this.colorId_;},get thread(){return this.thread_;},get leafNode(){return this.leafNode_;},get userFriendlyName(){return'Sample at '+
+tr.b.Unit.byName.timeStampInMs.format(this.start);},get userFriendlyStack(){return this.leafNode_.userFriendlyStack;},getNodesAsArray(){const nodes=[];let node=this.leafNode_;while(node!==undefined){nodes.push(node);node=node.parentNode;}
+return nodes;},get cpu(){return this.cpu_;},get weight(){return this.weight_;},};tr.model.EventRegistry.register(Sample,{name:'Sample',pluralName:'Samples'});return{Sample,};});'use strict';tr.exportTo('tr.model',function(){function StackFrame(parentFrame,id,title,colorId,opt_sourceInfo){if(id===undefined){throw new Error('id must be given');}
+this.parentFrame_=parentFrame;this.id=id;this.title_=title;this.colorId=colorId;this.children=[];this.sourceInfo_=opt_sourceInfo;if(this.parentFrame_){this.parentFrame_.addChild(this);}}
+StackFrame.prototype={get parentFrame(){return this.parentFrame_;},get title(){if(this.sourceInfo_){const src=this.sourceInfo_.toString();return this.title_+(src===''?'':' '+src);}
+return this.title_;},get domain(){let result='unknown';if(this.sourceInfo_&&this.sourceInfo_.domain){result=this.sourceInfo_.domain;}
+if(result==='unknown'&&this.parentFrame){result=this.parentFrame.domain;}
+return result;},get sourceInfo(){return this.sourceInfo_;},set parentFrame(parentFrame){if(this.parentFrame_){Polymer.dom(this.parentFrame_).removeChild(this);}
+this.parentFrame_=parentFrame;if(this.parentFrame_){this.parentFrame_.addChild(this);}},addChild(child){this.children.push(child);},removeChild(child){const i=this.children.indexOf(child.id);if(i===-1){throw new Error('omg');}
+this.children.splice(i,1);},removeAllChildren(){for(let i=0;i<this.children.length;i++){this.children[i].parentFrame_=undefined;}
+this.children.splice(0,this.children.length);},get stackTrace(){const stack=[this];let cur=this.parentFrame;while(cur){stack.push(cur);cur=cur.parentFrame;}
+return stack;},getUserFriendlyStackTrace(){return this.stackTrace.map(function(x){return x.title;});}};return{StackFrame,};});'use strict';tr.exportTo('tr.model.um',function(){class UserModel extends tr.model.EventContainer{constructor(parentModel){super();this.parentModel_=parentModel;this.expectations_=new tr.model.EventSet();this.segments_=[];}
+get stableId(){return'UserModel';}
+get parentModel(){return this.parentModel_;}
+sortExpectations(){this.expectations_.sortEvents((x,y)=>(x.start-y.start));}
+get expectations(){return this.expectations_;}
+shiftTimestampsForward(amount){}
+addCategoriesToDict(categoriesDict){}
+get segments(){return this.segments_;}*childEvents(){yield*this.expectations;}*childEventContainers(){}
+updateBounds(){this.bounds.reset();for(const expectation of this.expectations){expectation.addBoundsToRange(this.bounds);}}
+resegment(getKeyForSegment){const newSegments=[];let prevKey=undefined;let prevSegment=undefined;for(let i=0;i<this.segments.length;++i){const segment=this.segments[i];const key=getKeyForSegment(segment,i);if(prevSegment!==undefined&&key===prevKey){prevSegment.addSegment(segment);}else{prevSegment=segment.clone();newSegments.push(prevSegment);}
+prevKey=key;}
+return newSegments;}}
+return{UserModel,};});'use strict';tr.exportTo('tr',function(){const Process=tr.model.Process;const Device=tr.model.Device;const Kernel=tr.model.Kernel;const GlobalMemoryDump=tr.model.GlobalMemoryDump;const GlobalInstantEvent=tr.model.GlobalInstantEvent;const FlowEvent=tr.model.FlowEvent;const Alert=tr.model.Alert;const Sample=tr.model.Sample;function Model(){tr.model.EventContainer.call(this);tr.b.EventTarget.decorate(this);this.timestampShiftToZeroAmount_=0;this.faviconHue='blue';this.device=new Device(this);this.kernel=new Kernel(this);this.processes={};this.metadata=[];this.categories=[];this.instantEvents=[];this.flowEvents=[];this.clockSyncManager=new tr.model.ClockSyncManager();this.intrinsicTimeUnit_=undefined;this.stackFrames={};this.samples=[];this.alerts=[];this.userModel=new tr.model.um.UserModel(this);this.flowIntervalTree=new tr.b.IntervalTree((f)=>f.start,(f)=>f.end);this.globalMemoryDumps=[];this.userFriendlyCategoryDrivers_=[];this.annotationsByGuid_={};this.modelIndices=undefined;this.stats=new tr.model.ModelStats();this.importWarnings_=[];this.reportedImportWarnings_={};this.isTimeHighResolution_=true;this.patchupsToApply_=[];this.doesHelperGUIDSupportThisModel_={};this.helpersByConstructorGUID_={};this.eventsByStableId_=undefined;}
+Model.prototype={__proto__:tr.model.EventContainer.prototype,getEventByStableId(stableId){if(this.eventsByStableId_===undefined){this.eventsByStableId_={};for(const event of this.getDescendantEvents()){this.eventsByStableId_[event.stableId]=event;}}
+return this.eventsByStableId_[stableId];},getOrCreateHelper(constructor){if(!constructor.guid){throw new Error('Helper constructors must have GUIDs');}
+if(this.helpersByConstructorGUID_[constructor.guid]===undefined){if(this.doesHelperGUIDSupportThisModel_[constructor.guid]===undefined){this.doesHelperGUIDSupportThisModel_[constructor.guid]=constructor.supportsModel(this);}
+if(!this.doesHelperGUIDSupportThisModel_[constructor.guid]){return undefined;}
+this.helpersByConstructorGUID_[constructor.guid]=new constructor(this);}
+return this.helpersByConstructorGUID_[constructor.guid];},*childEvents(){yield*this.globalMemoryDumps;yield*this.instantEvents;yield*this.flowEvents;yield*this.alerts;yield*this.samples;},*childEventContainers(){yield this.userModel;yield this.device;yield this.kernel;yield*Object.values(this.processes);},iterateAllPersistableObjects(callback){this.kernel.iterateAllPersistableObjects(callback);for(const pid in this.processes){this.processes[pid].iterateAllPersistableObjects(callback);}},updateBounds(){this.bounds.reset();const bounds=this.bounds;for(const ec of this.childEventContainers()){ec.updateBounds();bounds.addRange(ec.bounds);}
+for(const event of this.childEvents()){event.addBoundsToRange(bounds);}},shiftWorldToZero(){const shiftAmount=-this.bounds.min;this.timestampShiftToZeroAmount_=shiftAmount;for(const ec of this.childEventContainers()){ec.shiftTimestampsForward(shiftAmount);}
+for(const event of this.childEvents()){event.start+=shiftAmount;}
+this.updateBounds();},convertTimestampToModelTime(sourceClockDomainName,ts){if(sourceClockDomainName!=='traceEventClock'){throw new Error('Only traceEventClock is supported.');}
+return tr.b.Unit.timestampFromUs(ts)+
+this.timestampShiftToZeroAmount_;},get numProcesses(){let n=0;for(const p in this.processes){n++;}
+return n;},getProcess(pid){return this.processes[pid];},getOrCreateProcess(pid){if(!this.processes[pid]){this.processes[pid]=new Process(this,pid);}
+return this.processes[pid];},addStackFrame(stackFrame){if(this.stackFrames[stackFrame.id]){throw new Error('Stack frame already exists');}
+this.stackFrames[stackFrame.id]=stackFrame;return stackFrame;},updateCategories_(){const categoriesDict={};this.userModel.addCategoriesToDict(categoriesDict);this.device.addCategoriesToDict(categoriesDict);this.kernel.addCategoriesToDict(categoriesDict);for(const pid in this.processes){this.processes[pid].addCategoriesToDict(categoriesDict);}
+this.categories=[];for(const category in categoriesDict){if(category!==''){this.categories.push(category);}}},getAllThreads(){const threads=[];for(const tid in this.kernel.threads){threads.push(process.threads[tid]);}
+for(const pid in this.processes){const process=this.processes[pid];for(const tid in process.threads){threads.push(process.threads[tid]);}}
+return threads;},getAllProcesses(opt_predicate){const processes=[];for(const pid in this.processes){const process=this.processes[pid];if(opt_predicate===undefined||opt_predicate(process)){processes.push(process);}}
+return processes;},getAllCounters(){const counters=[];counters.push.apply(counters,Object.values(this.device.counters||{}));counters.push.apply(counters,Object.values(this.kernel.counters||{}));for(const pid in this.processes){const process=this.processes[pid];for(const tid in process.counters){counters.push(process.counters[tid]);}}
+return counters;},getAnnotationByGUID(guid){return this.annotationsByGuid_[guid];},addAnnotation(annotation){if(!annotation.guid){throw new Error('Annotation with undefined guid given');}
+this.annotationsByGuid_[annotation.guid]=annotation;tr.b.dispatchSimpleEvent(this,'annotationChange');},removeAnnotation(annotation){this.annotationsByGuid_[annotation.guid].onRemove();delete this.annotationsByGuid_[annotation.guid];tr.b.dispatchSimpleEvent(this,'annotationChange');},getAllAnnotations(){return Object.values(this.annotationsByGuid_);},addUserFriendlyCategoryDriver(ufcd){this.userFriendlyCategoryDrivers_.push(ufcd);},getUserFriendlyCategoryFromEvent(event){for(let i=0;i<this.userFriendlyCategoryDrivers_.length;i++){const ufc=this.userFriendlyCategoryDrivers_[i].fromEvent(event);if(ufc!==undefined)return ufc;}
+return undefined;},findAllThreadsNamed(name){const namedThreads=[];namedThreads.push.apply(namedThreads,this.kernel.findAllThreadsNamed(name));for(const pid in this.processes){namedThreads.push.apply(namedThreads,this.processes[pid].findAllThreadsNamed(name));}
+return namedThreads;},get importOptions(){return this.importOptions_;},set importOptions(options){this.importOptions_=options;},get intrinsicTimeUnit(){if(this.intrinsicTimeUnit_===undefined){return tr.b.TimeDisplayModes.ms;}
+return this.intrinsicTimeUnit_;},set intrinsicTimeUnit(value){if(this.intrinsicTimeUnit_===value)return;if(this.intrinsicTimeUnit_!==undefined){throw new Error('Intrinsic time unit already set');}
+this.intrinsicTimeUnit_=value;},get isTimeHighResolution(){return this.isTimeHighResolution_;},set isTimeHighResolution(value){this.isTimeHighResolution_=value;},get canonicalUrl(){return this.canonicalUrl_;},set canonicalUrl(value){if(this.canonicalUrl_===value)return;if(this.canonicalUrl_!==undefined){throw new Error('canonicalUrl already set');}
+this.canonicalUrl_=value;},importWarning(data){data.showToUser=!!data.showToUser;this.importWarnings_.push(data);if(this.reportedImportWarnings_[data.type]===true)return;this.reportedImportWarnings_[data.type]=true;},get hasImportWarnings(){return(this.importWarnings_.length>0);},get importWarnings(){return this.importWarnings_;},get importWarningsThatShouldBeShownToUser(){return this.importWarnings_.filter(function(warning){return warning.showToUser;});},autoCloseOpenSlices(){this.samples.sort(function(x,y){return x.start-y.start;});this.updateBounds();this.kernel.autoCloseOpenSlices();for(const pid in this.processes){this.processes[pid].autoCloseOpenSlices();}},createSubSlices(){this.kernel.createSubSlices();for(const pid in this.processes){this.processes[pid].createSubSlices();}},preInitializeObjects(){for(const pid in this.processes){this.processes[pid].preInitializeObjects();}},initializeObjects(){for(const pid in this.processes){this.processes[pid].initializeObjects();}},pruneEmptyContainers(){this.kernel.pruneEmptyContainers();for(const pid in this.processes){this.processes[pid].pruneEmptyContainers();}},mergeKernelWithUserland(){for(const pid in this.processes){this.processes[pid].mergeKernelWithUserland();}},computeWorldBounds(shiftWorldToZero){this.updateBounds();this.updateCategories_();if(shiftWorldToZero){this.shiftWorldToZero();}},buildFlowEventIntervalTree(){for(let i=0;i<this.flowEvents.length;++i){const flowEvent=this.flowEvents[i];this.flowIntervalTree.insert(flowEvent);}
+this.flowIntervalTree.updateHighValues();},cleanupUndeletedObjects(){for(const pid in this.processes){this.processes[pid].autoDeleteObjects(this.bounds.max);}},sortMemoryDumps(){this.globalMemoryDumps.sort(function(x,y){return x.start-y.start;});for(const pid in this.processes){this.processes[pid].sortMemoryDumps();}},finalizeMemoryGraphs(){this.globalMemoryDumps.forEach(function(dump){dump.finalizeGraph();});},buildEventIndices(){this.modelIndices=new tr.model.ModelIndices(this);},sortAlerts(){this.alerts.sort(function(x,y){return x.start-y.start;});},applyObjectRefPatchups(){const unresolved=[];this.patchupsToApply_.forEach(function(patchup){if(patchup.pidRef in this.processes){const snapshot=this.processes[patchup.pidRef].objects.getSnapshotAt(patchup.scopedId,patchup.ts);if(snapshot){patchup.object[patchup.field]=snapshot;snapshot.referencedAt(patchup.item,patchup.object,patchup.field);return;}}
+unresolved.push(patchup);},this);this.patchupsToApply_=unresolved;},replacePIDRefsInPatchups(oldPidRef,newPidRef){this.patchupsToApply_.forEach(function(patchup){if(patchup.pidRef===oldPidRef){patchup.pidRef=newPidRef;}});},joinRefs(){this.joinObjectRefs_();this.applyObjectRefPatchups();},joinObjectRefs_(){for(const[pid,process]of Object.entries(this.processes)){this.joinObjectRefsForProcess_(pid,process);}},joinObjectRefsForProcess_(pid,process){for(const thread of Object.values(process.threads)){thread.asyncSliceGroup.slices.forEach(function(item){this.searchItemForIDRefs_(pid,'start',item);},this);thread.sliceGroup.slices.forEach(function(item){this.searchItemForIDRefs_(pid,'start',item);},this);}
+process.objects.iterObjectInstances(function(instance){instance.snapshots.forEach(function(item){this.searchItemForIDRefs_(pid,'ts',item);},this);},this);},searchItemForIDRefs_(pid,itemTimestampField,item){if(!item.args&&!item.contexts)return;const patchupsToApply=this.patchupsToApply_;function handleField(object,fieldName,fieldValue){if(!fieldValue||(!fieldValue.id_ref&&!fieldValue.idRef)){return;}
+const scope=fieldValue.scope||tr.model.OBJECT_DEFAULT_SCOPE;const idRef=fieldValue.id_ref||fieldValue.idRef;const scopedId=new tr.model.ScopedId(scope,idRef);const pidRef=fieldValue.pid_ref||fieldValue.pidRef||pid;const ts=item[itemTimestampField];patchupsToApply.push({item,object,field:fieldName,pidRef,scopedId,ts});}
+function iterObjectFieldsRecursively(object){if(!(object instanceof Object))return;if((object instanceof tr.model.ObjectSnapshot)||(object instanceof Float32Array)||(object instanceof tr.b.math.Quad)){return;}
+if(object instanceof Array){for(let i=0;i<object.length;i++){handleField(object,i,object[i]);iterObjectFieldsRecursively(object[i]);}
+return;}
+for(const key in object){const value=object[key];handleField(object,key,value);iterObjectFieldsRecursively(value);}}
+iterObjectFieldsRecursively(item.args);iterObjectFieldsRecursively(item.contexts);}};return{Model,};});'use strict';tr.exportTo('tr.e.importer.etw',function(){const kThreadGuid='3D6FA8D1-FE05-11D0-9DDA-00C04FD7BA7C';const kThreadDCStartOpcode=3;function Decoder(){this.payload_=new DataView(new ArrayBuffer(256));}
+Decoder.prototype={__proto__:Object.prototype,reset(base64Payload){const decodedSize=tr.b.Base64.getDecodedBufferLength(base64Payload);if(decodedSize>this.payload_.byteLength){this.payload_=new DataView(new ArrayBuffer(decodedSize));}
+tr.b.Base64.DecodeToTypedArray(base64Payload,this.payload_);this.position_=0;},skip(length){this.position_+=length;},decodeUInt8(){const result=this.payload_.getUint8(this.position_,true);this.position_+=1;return result;},decodeUInt16(){const result=this.payload_.getUint16(this.position_,true);this.position_+=2;return result;},decodeUInt32(){const result=this.payload_.getUint32(this.position_,true);this.position_+=4;return result;},decodeUInt64ToString(){const low=this.decodeUInt32();const high=this.decodeUInt32();const lowStr=('0000000'+low.toString(16)).substr(-8);const highStr=('0000000'+high.toString(16)).substr(-8);const result=highStr+lowStr;return result;},decodeInt8(){const result=this.payload_.getInt8(this.position_,true);this.position_+=1;return result;},decodeInt16(){const result=this.payload_.getInt16(this.position_,true);this.position_+=2;return result;},decodeInt32(){const result=this.payload_.getInt32(this.position_,true);this.position_+=4;return result;},decodeInt64ToString(){return this.decodeUInt64ToString();},decodeUInteger(is64){if(is64){return this.decodeUInt64ToString();}
+return this.decodeUInt32();},decodeString(){let str='';while(true){const c=this.decodeUInt8();if(!c)return str;str=str+String.fromCharCode(c);}},decodeW16String(){let str='';while(true){const c=this.decodeUInt16();if(!c)return str;str=str+String.fromCharCode(c);}},decodeFixedW16String(length){const oldPosition=this.position_;let str='';for(let i=0;i<length;i++){const c=this.decodeUInt16();if(!c)break;str=str+String.fromCharCode(c);}
+this.position_=oldPosition+2*length;return str;},decodeBytes(length){const bytes=[];for(let i=0;i<length;++i){const c=this.decodeUInt8();bytes.push(c);}
+return bytes;},decodeSID(is64){const pSid=this.decodeUInteger(is64);const attributes=this.decodeUInt32();if(is64){this.decodeUInt32();}
+const revision=this.decodeUInt8();const subAuthorityCount=this.decodeUInt8();this.decodeUInt16();this.decodeUInt32();if(revision!==1){throw new Error('Invalid SID revision: could not decode the SID structure.');}
+const sid=this.decodeBytes(4*subAuthorityCount);return{pSid,attributes,sid};},decodeSystemTime(){const wYear=this.decodeInt16();const wMonth=this.decodeInt16();const wDayOfWeek=this.decodeInt16();const wDay=this.decodeInt16();const wHour=this.decodeInt16();const wMinute=this.decodeInt16();const wSecond=this.decodeInt16();const wMilliseconds=this.decodeInt16();return{wYear,wMonth,wDayOfWeek,wDay,wHour,wMinute,wSecond,wMilliseconds};},decodeTimeZoneInformation(){const bias=this.decodeUInt32();const standardName=this.decodeFixedW16String(32);const standardDate=this.decodeSystemTime();const standardBias=this.decodeUInt32();const daylightName=this.decodeFixedW16String(32);const daylightDate=this.decodeSystemTime();const daylightBias=this.decodeUInt32();return{bias,standardName,standardDate,standardBias,daylightName,daylightDate,daylightBias};}};function EtwImporter(model,events){this.importPriority=3;this.model_=model;this.events_=events;this.handlers_={};this.decoder_=new Decoder();this.walltime_=undefined;this.ticks_=undefined;this.is64bit_=undefined;this.tidsToPid_={};const allTypeInfos=tr.e.importer.etw.Parser.getAllRegisteredTypeInfos();this.parsers_=allTypeInfos.map(function(typeInfo){return new typeInfo.constructor(this);},this);}
+EtwImporter.canImport=function(events){if(!events.hasOwnProperty('name')||!events.hasOwnProperty('content')||events.name!=='ETW'){return false;}
+return true;};EtwImporter.prototype={__proto__:tr.importer.Importer.prototype,get importerName(){return'EtwImporter';},get model(){return this.model_;},createThreadIfNeeded(pid,tid){this.tidsToPid_[tid]=pid;},removeThreadIfPresent(tid){this.tidsToPid_[tid]=undefined;},getPidFromWindowsTid(tid){if(tid===0)return 0;const pid=this.tidsToPid_[tid];if(pid===undefined){return 0;}
+return pid;},getThreadFromWindowsTid(tid){const pid=this.getPidFromWindowsTid(tid);const process=this.model_.getProcess(pid);if(!process)return undefined;return process.getThread(tid);},getOrCreateCpu(cpuNumber){const cpu=this.model_.kernel.getOrCreateCpu(cpuNumber);return cpu;},importEvents(){this.events_.content.forEach(this.parseInfo.bind(this));if(this.walltime_===undefined||this.ticks_===undefined){throw Error('Cannot find clock sync information in the system trace.');}
+if(this.is64bit_===undefined){throw Error('Cannot determine pointer size of the system trace.'+'Consider deselecting "System tracing" or disabling the "Paging '+'Executive" feature of Windows');}
+this.events_.content.forEach(this.parseEvent.bind(this));},importTimestamp(timestamp){const ts=parseInt(timestamp,16);return(ts-this.walltime_+this.ticks_)/1000.;},parseInfo(event){if(event.hasOwnProperty('guid')&&event.hasOwnProperty('walltime')&&event.hasOwnProperty('tick')&&event.guid==='ClockSync'){this.walltime_=parseInt(event.walltime,16);this.ticks_=parseInt(event.tick,16);}
+if(this.is64bit_===undefined&&event.hasOwnProperty('guid')&&event.hasOwnProperty('op')&&event.hasOwnProperty('ver')&&event.hasOwnProperty('payload')&&event.guid===kThreadGuid&&event.op===kThreadDCStartOpcode){const decodedSize=tr.b.Base64.getDecodedBufferLength(event.payload);if(event.ver===1){if(decodedSize>=52){this.is64bit_=true;}else{this.is64bit_=false;}}else if(event.ver===2){if(decodedSize>=64){this.is64bit_=true;}else{this.is64bit_=false;}}else if(event.ver===3){if(decodedSize>=60){this.is64bit_=true;}else{this.is64bit_=false;}}}
+return true;},parseEvent(event){if(!event.hasOwnProperty('guid')||!event.hasOwnProperty('op')||!event.hasOwnProperty('ver')||!event.hasOwnProperty('cpu')||!event.hasOwnProperty('ts')||!event.hasOwnProperty('payload')){return false;}
+const timestamp=this.importTimestamp(event.ts);const header={guid:event.guid,opcode:event.op,version:event.ver,cpu:event.cpu,timestamp,is64:this.is64bit_};const decoder=this.decoder_;decoder.reset(event.payload);const handler=this.getEventHandler(header.guid,header.opcode);if(!handler)return false;if(!handler(header,decoder)){this.model_.importWarning({type:'parse_error',message:'Malformed '+header.guid+' event ('+event.payload+')'});return false;}
+return true;},registerEventHandler(guid,opcode,handler){if(this.handlers_[guid]===undefined){this.handlers_[guid]=[];}
+this.handlers_[guid][opcode]=handler;},getEventHandler(guid,opcode){if(this.handlers_[guid]===undefined){return undefined;}
+return this.handlers_[guid][opcode];}};tr.importer.Importer.register(EtwImporter);return{EtwImporter,};});'use strict';tr.exportTo('tr.b',function(){class TraceStream{static get HEADER_SIZE(){return Math.pow(2,10);}
+static get CHUNK_SIZE(){return Math.pow(2,20);}
+get isBinary(){throw new Error('Not implemented');}
+get hasData(){throw new Error('Not implemented');}
+get header(){throw new Error('Not implemented');}
+readUntilDelimiter(delim){throw new Error('Not implemented');}
+readNumBytes(opt_size){throw new Error('Not implemented');}
+rewind(){throw new Error('Not implemented');}
+substream(offset,opt_length,opt_headerSize){throw new Error('Not implemented');}}
+return{TraceStream,};});'use strict';tr.exportTo('tr.e.importer.fuchsia',function(){const IMPORT_PRIORITY=0;const IDLE_THREAD_THRESHOLD=6444000000;const ZX_THREAD_STATE_NEW=0;const ZX_THREAD_STATE_RUNNING=1;const ZX_THREAD_STATE_SUSPENDED=2;const ZX_THREAD_STATE_BLOCKED=3;const ZX_THREAD_STATE_DYING=4;const ZX_THREAD_STATE_DEAD=5;class FuchsiaImporter extends tr.importer.Importer{constructor(model,eventData){super(model,eventData);this.importPriority=IMPORT_PRIORITY;this.model_=model;this.events_=eventData.events;this.parsers_=[];this.threadInfo_=new Map();this.processNames_=new Map();this.threadStates_=new Map();}
+static canImport(eventData){if(eventData instanceof tr.b.TraceStream){if(eventData.isBinary)return false;eventData=eventData.header;}
+if(eventData instanceof Object&&eventData.type==='fuchsia'){return true;}
+return false;}
+get importerName(){return'FuchsiaImporter';}
+get model(){return this.model_;}
+importClockSyncMarkers(){}
+finalizeImport(){}
+isIdleThread(prio,tid){if(prio===undefined){return tid>IDLE_THREAD_THRESHOLD;}
+return prio===0;}
+recordThreadState_(tid,timestamp,state,prio){if(this.isIdleThread(prio,tid)){return;}
+const states=this.threadStates_.has(tid)?this.threadStates_.get(tid):[];states.push({'ts':timestamp,state});this.threadStates_.set(tid,states);}
+processContextSwitchEvent_(event){let tid=event.in.tid;let threadName=tid.toString();let procName='';const prio=event.in.prio;if(this.threadInfo_.has(tid)){const threadInfo=this.threadInfo_.get(tid);threadName=threadInfo.name;const pid=threadInfo.pid;if(this.processNames_.has(pid)){procName=this.processNames_.get(pid)+':';}}
+const name=procName+threadName;if(this.isIdleThread(prio,tid)){tid=undefined;}
+const cpu=this.model_.kernel.getOrCreateCpu(event.cpu);const timestamp=tr.b.Unit.timestampFromUs(event.ts);cpu.switchActiveThread(timestamp,{},tid,name,tid);const SCHEDULING_STATE=tr.model.SCHEDULING_STATE;this.recordThreadState_(tid,timestamp,SCHEDULING_STATE.RUNNING,prio);let outState=SCHEDULING_STATE.UNKNOWN;switch(event.out.state){case ZX_THREAD_STATE_NEW:outState=SCHEDULING_STATE.RUNNABLE;break;case ZX_THREAD_STATE_RUNNING:outState=SCHEDULING_STATE.RUNNABLE;break;case ZX_THREAD_STATE_BLOCKED:outState=SCHEDULING_STATE.SLEEPING;break;case ZX_THREAD_STATE_SUSPENDED:outState=SCHEDULING_STATE.STOPPED;break;case ZX_THREAD_STATE_DEAD:outState=SCHEDULING_STATE.TASK_DEAD;break;}
+this.recordThreadState_(event.out.tid,timestamp,outState,event.out.prio);}
+processProcessInfoEvent_(event){const process=this.model_.getOrCreateProcess(event.pid);process.name=event.name;this.processNames_.set(event.pid,event.name);if('sort_index'in event){process.sortIndex=event.sort_index;}}
+processThreadInfoEvent_(event){const thread=this.model_.getOrCreateProcess(event.pid).getOrCreateThread(event.tid);thread.name=event.name;this.threadInfo_.set(event.tid,{'name':event.name,'pid':event.pid});if('sort_index'in event){const thread=this.model_.getOrCreateProcess(event.pid).getOrCreateThread(event.tid);thread.sortIndex=event.sort_index;}}
+processEvent_(event){switch(event.ph){case'k':this.processContextSwitchEvent_(event);break;case'p':this.processProcessInfoEvent_(event);break;case't':this.processThreadInfoEvent_(event);break;}}
+postProcessStates_(){for(const[tid,states]of this.threadStates_){if(!this.threadInfo_.has(tid)){continue;}
+const pid=this.threadInfo_.get(tid).pid;const thread=this.model_.getOrCreateProcess(pid).getOrCreateThread(tid);const slices=[];for(let i=0;i<states.length-1;i++){slices.push(new tr.model.ThreadTimeSlice(thread,states[i].state,'',states[i].ts,{},states[i+1].ts-states[i].ts));}
+thread.timeSlices=slices;}}
+importEvents(){for(const event of this.events_){this.processEvent_(event);}
+this.postProcessStates_();}}
+tr.importer.Importer.register(FuchsiaImporter);return{FuchsiaImporter,IMPORT_PRIORITY,};});'use strict';tr.exportTo('tr.b',function(){const MAX_FUNCTION_ARGS_COUNT=Math.pow(2,15)-1;class InMemoryTraceStream extends tr.b.TraceStream{constructor(buffer,isBinary,opt_headerSize){super();if(!buffer instanceof Uint8Array){throw new Error('buffer should be a Uint8Array');}
+const headerSize=opt_headerSize||tr.b.TraceStream.HEADER_SIZE;this.data_=buffer;this.isBinary_=isBinary;this.header_=InMemoryTraceStream.uint8ArrayToString_(this.data_.subarray(0,headerSize));this.cursor_=0;}
+get isBinary(){return this.isBinary_;}
+get hasData(){return this.cursor_<this.data_.length;}
+get header(){return this.header_;}
+get data(){return this.data_;}
+toString(){this.rewind();return this.readNumBytes(Number.MAX_VALUE);}
+readUntilDelimiter(delim){if(delim.length!==1){throw new Error('delim must be exactly one character');}
+const offset=this.data_.indexOf(delim.charCodeAt(0),this.cursor_)+1;return this.readToOffset_(offset>0?Math.min(offset,this.data_.length):this.data_.length);}
+readNumBytes(opt_size){if(opt_size!==undefined&&opt_size<=0){throw new Error(`readNumBytes expects a positive size (${opt_size} given)`);}
+const size=opt_size||tr.b.TraceStream.CHUNK_SIZE;const offset=Math.min(this.cursor_+size,this.data_.length);return this.readToOffset_(offset);}
+rewind(){this.cursor_=0;}
+substream(startOffset,opt_endOffset,opt_headerSize){return new InMemoryTraceStream(this.data_.subarray(startOffset,opt_endOffset),this.isBinary_,opt_headerSize);}
+readToOffset_(offset){const out=InMemoryTraceStream.uint8ArrayToString_(this.data_.subarray(this.cursor_,offset));this.cursor_=offset;return out;}
+static uint8ArrayToString_(arr){if(typeof TextDecoder!=='undefined'){const decoder=new TextDecoder('utf-8');return decoder.decode(arr);}
+const c=[];for(let i=0;i<arr.length;i+=MAX_FUNCTION_ARGS_COUNT){c.push(String.fromCharCode(...arr.subarray(i,i+MAX_FUNCTION_ARGS_COUNT)));}
+return c.join('');}}
+return{InMemoryTraceStream,};});!function(t){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).pako=t()}}(function(){return function t(e,a,i){function n(s,o){if(!a[s]){if(!e[s]){var l="function"==typeof require&&require;if(!o&&l)return l(s,!0);if(r)return r(s,!0);var h=new Error("Cannot find module '"+s+"'");throw h.code="MODULE_NOT_FOUND",h}var d=a[s]={exports:{}};e[s][0].call(d.exports,function(t){var a=e[s][1][t];return n(a||t)},d,d.exports,t,e,a,i)}return a[s].exports}for(var r="function"==typeof require&&require,s=0;s<i.length;s++)n(i[s]);return n}({1:[function(t,e,a){"use strict";function i(t){if(!(this instanceof i))return new i(t);this.options=s.assign({level:_,method:c,chunkSize:16384,windowBits:15,memLevel:8,strategy:u,to:""},t||{});var e=this.options;e.raw&&e.windowBits>0?e.windowBits=-e.windowBits:e.gzip&&e.windowBits>0&&e.windowBits<16&&(e.windowBits+=16),this.err=0,this.msg="",this.ended=!1,this.chunks=[],this.strm=new h,this.strm.avail_out=0;var a=r.deflateInit2(this.strm,e.level,e.method,e.windowBits,e.memLevel,e.strategy);if(a!==f)throw new Error(l[a]);if(e.header&&r.deflateSetHeader(this.strm,e.header),e.dictionary){var n;if(n="string"==typeof e.dictionary?o.string2buf(e.dictionary):"[object ArrayBuffer]"===d.call(e.dictionary)?new Uint8Array(e.dictionary):e.dictionary,(a=r.deflateSetDictionary(this.strm,n))!==f)throw new Error(l[a]);this._dict_set=!0}}function n(t,e){var a=new i(e);if(a.push(t,!0),a.err)throw a.msg||l[a.err];return a.result}var r=t("./zlib/deflate"),s=t("./utils/common"),o=t("./utils/strings"),l=t("./zlib/messages"),h=t("./zlib/zstream"),d=Object.prototype.toString,f=0,_=-1,u=0,c=8;i.prototype.push=function(t,e){var a,i,n=this.strm,l=this.options.chunkSize;if(this.ended)return!1;i=e===~~e?e:!0===e?4:0,"string"==typeof t?n.input=o.string2buf(t):"[object ArrayBuffer]"===d.call(t)?n.input=new Uint8Array(t):n.input=t,n.next_in=0,n.avail_in=n.input.length;do{if(0===n.avail_out&&(n.output=new s.Buf8(l),n.next_out=0,n.avail_out=l),1!==(a=r.deflate(n,i))&&a!==f)return this.onEnd(a),this.ended=!0,!1;0!==n.avail_out&&(0!==n.avail_in||4!==i&&2!==i)||("string"===this.options.to?this.onData(o.buf2binstring(s.shrinkBuf(n.output,n.next_out))):this.onData(s.shrinkBuf(n.output,n.next_out)))}while((n.avail_in>0||0===n.avail_out)&&1!==a);return 4===i?(a=r.deflateEnd(this.strm),this.onEnd(a),this.ended=!0,a===f):2!==i||(this.onEnd(f),n.avail_out=0,!0)},i.prototype.onData=function(t){this.chunks.push(t)},i.prototype.onEnd=function(t){t===f&&("string"===this.options.to?this.result=this.chunks.join(""):this.result=s.flattenChunks(this.chunks)),this.chunks=[],this.err=t,this.msg=this.strm.msg},a.Deflate=i,a.deflate=n,a.deflateRaw=function(t,e){return e=e||{},e.raw=!0,n(t,e)},a.gzip=function(t,e){return e=e||{},e.gzip=!0,n(t,e)}},{"./utils/common":3,"./utils/strings":4,"./zlib/deflate":8,"./zlib/messages":13,"./zlib/zstream":15}],2:[function(t,e,a){"use strict";function i(t){if(!(this instanceof i))return new i(t);this.options=s.assign({chunkSize:16384,windowBits:0,to:""},t||{});var e=this.options;e.raw&&e.windowBits>=0&&e.windowBits<16&&(e.windowBits=-e.windowBits,0===e.windowBits&&(e.windowBits=-15)),!(e.windowBits>=0&&e.windowBits<16)||t&&t.windowBits||(e.windowBits+=32),e.windowBits>15&&e.windowBits<48&&0==(15&e.windowBits)&&(e.windowBits|=15),this.err=0,this.msg="",this.ended=!1,this.chunks=[],this.strm=new d,this.strm.avail_out=0;var a=r.inflateInit2(this.strm,e.windowBits);if(a!==l.Z_OK)throw new Error(h[a]);this.header=new f,r.inflateGetHeader(this.strm,this.header)}function n(t,e){var a=new i(e);if(a.push(t,!0),a.err)throw a.msg||h[a.err];return a.result}var r=t("./zlib/inflate"),s=t("./utils/common"),o=t("./utils/strings"),l=t("./zlib/constants"),h=t("./zlib/messages"),d=t("./zlib/zstream"),f=t("./zlib/gzheader"),_=Object.prototype.toString;i.prototype.push=function(t,e){var a,i,n,h,d,f,u=this.strm,c=this.options.chunkSize,b=this.options.dictionary,g=!1;if(this.ended)return!1;i=e===~~e?e:!0===e?l.Z_FINISH:l.Z_NO_FLUSH,"string"==typeof t?u.input=o.binstring2buf(t):"[object ArrayBuffer]"===_.call(t)?u.input=new Uint8Array(t):u.input=t,u.next_in=0,u.avail_in=u.input.length;do{if(0===u.avail_out&&(u.output=new s.Buf8(c),u.next_out=0,u.avail_out=c),(a=r.inflate(u,l.Z_NO_FLUSH))===l.Z_NEED_DICT&&b&&(f="string"==typeof b?o.string2buf(b):"[object ArrayBuffer]"===_.call(b)?new Uint8Array(b):b,a=r.inflateSetDictionary(this.strm,f)),a===l.Z_BUF_ERROR&&!0===g&&(a=l.Z_OK,g=!1),a!==l.Z_STREAM_END&&a!==l.Z_OK)return this.onEnd(a),this.ended=!0,!1;u.next_out&&(0!==u.avail_out&&a!==l.Z_STREAM_END&&(0!==u.avail_in||i!==l.Z_FINISH&&i!==l.Z_SYNC_FLUSH)||("string"===this.options.to?(n=o.utf8border(u.output,u.next_out),h=u.next_out-n,d=o.buf2string(u.output,n),u.next_out=h,u.avail_out=c-h,h&&s.arraySet(u.output,u.output,n,h,0),this.onData(d)):this.onData(s.shrinkBuf(u.output,u.next_out)))),0===u.avail_in&&0===u.avail_out&&(g=!0)}while((u.avail_in>0||0===u.avail_out)&&a!==l.Z_STREAM_END);return a===l.Z_STREAM_END&&(i=l.Z_FINISH),i===l.Z_FINISH?(a=r.inflateEnd(this.strm),this.onEnd(a),this.ended=!0,a===l.Z_OK):i!==l.Z_SYNC_FLUSH||(this.onEnd(l.Z_OK),u.avail_out=0,!0)},i.prototype.onData=function(t){this.chunks.push(t)},i.prototype.onEnd=function(t){t===l.Z_OK&&("string"===this.options.to?this.result=this.chunks.join(""):this.result=s.flattenChunks(this.chunks)),this.chunks=[],this.err=t,this.msg=this.strm.msg},a.Inflate=i,a.inflate=n,a.inflateRaw=function(t,e){return e=e||{},e.raw=!0,n(t,e)},a.ungzip=n},{"./utils/common":3,"./utils/strings":4,"./zlib/constants":6,"./zlib/gzheader":9,"./zlib/inflate":11,"./zlib/messages":13,"./zlib/zstream":15}],3:[function(t,e,a){"use strict";function i(t,e){return Object.prototype.hasOwnProperty.call(t,e)}var n="undefined"!=typeof Uint8Array&&"undefined"!=typeof Uint16Array&&"undefined"!=typeof Int32Array;a.assign=function(t){for(var e=Array.prototype.slice.call(arguments,1);e.length;){var a=e.shift();if(a){if("object"!=typeof a)throw new TypeError(a+"must be non-object");for(var n in a)i(a,n)&&(t[n]=a[n])}}return t},a.shrinkBuf=function(t,e){return t.length===e?t:t.subarray?t.subarray(0,e):(t.length=e,t)};var r={arraySet:function(t,e,a,i,n){if(e.subarray&&t.subarray)t.set(e.subarray(a,a+i),n);else for(var r=0;r<i;r++)t[n+r]=e[a+r]},flattenChunks:function(t){var e,a,i,n,r,s;for(i=0,e=0,a=t.length;e<a;e++)i+=t[e].length;for(s=new Uint8Array(i),n=0,e=0,a=t.length;e<a;e++)r=t[e],s.set(r,n),n+=r.length;return s}},s={arraySet:function(t,e,a,i,n){for(var r=0;r<i;r++)t[n+r]=e[a+r]},flattenChunks:function(t){return[].concat.apply([],t)}};a.setTyped=function(t){t?(a.Buf8=Uint8Array,a.Buf16=Uint16Array,a.Buf32=Int32Array,a.assign(a,r)):(a.Buf8=Array,a.Buf16=Array,a.Buf32=Array,a.assign(a,s))},a.setTyped(n)},{}],4:[function(t,e,a){"use strict";function i(t,e){if(e<65537&&(t.subarray&&s||!t.subarray&&r))return String.fromCharCode.apply(null,n.shrinkBuf(t,e));for(var a="",i=0;i<e;i++)a+=String.fromCharCode(t[i]);return a}var n=t("./common"),r=!0,s=!0;try{String.fromCharCode.apply(null,[0])}catch(t){r=!1}try{String.fromCharCode.apply(null,new Uint8Array(1))}catch(t){s=!1}for(var o=new n.Buf8(256),l=0;l<256;l++)o[l]=l>=252?6:l>=248?5:l>=240?4:l>=224?3:l>=192?2:1;o[254]=o[254]=1,a.string2buf=function(t){var e,a,i,r,s,o=t.length,l=0;for(r=0;r<o;r++)55296==(64512&(a=t.charCodeAt(r)))&&r+1<o&&56320==(64512&(i=t.charCodeAt(r+1)))&&(a=65536+(a-55296<<10)+(i-56320),r++),l+=a<128?1:a<2048?2:a<65536?3:4;for(e=new n.Buf8(l),s=0,r=0;s<l;r++)55296==(64512&(a=t.charCodeAt(r)))&&r+1<o&&56320==(64512&(i=t.charCodeAt(r+1)))&&(a=65536+(a-55296<<10)+(i-56320),r++),a<128?e[s++]=a:a<2048?(e[s++]=192|a>>>6,e[s++]=128|63&a):a<65536?(e[s++]=224|a>>>12,e[s++]=128|a>>>6&63,e[s++]=128|63&a):(e[s++]=240|a>>>18,e[s++]=128|a>>>12&63,e[s++]=128|a>>>6&63,e[s++]=128|63&a);return e},a.buf2binstring=function(t){return i(t,t.length)},a.binstring2buf=function(t){for(var e=new n.Buf8(t.length),a=0,i=e.length;a<i;a++)e[a]=t.charCodeAt(a);return e},a.buf2string=function(t,e){var a,n,r,s,l=e||t.length,h=new Array(2*l);for(n=0,a=0;a<l;)if((r=t[a++])<128)h[n++]=r;else if((s=o[r])>4)h[n++]=65533,a+=s-1;else{for(r&=2===s?31:3===s?15:7;s>1&&a<l;)r=r<<6|63&t[a++],s--;s>1?h[n++]=65533:r<65536?h[n++]=r:(r-=65536,h[n++]=55296|r>>10&1023,h[n++]=56320|1023&r)}return i(h,n)},a.utf8border=function(t,e){var a;for((e=e||t.length)>t.length&&(e=t.length),a=e-1;a>=0&&128==(192&t[a]);)a--;return a<0?e:0===a?e:a+o[t[a]]>e?a:e}},{"./common":3}],5:[function(t,e,a){"use strict";e.exports=function(t,e,a,i){for(var n=65535&t|0,r=t>>>16&65535|0,s=0;0!==a;){a-=s=a>2e3?2e3:a;do{r=r+(n=n+e[i++]|0)|0}while(--s);n%=65521,r%=65521}return n|r<<16|0}},{}],6:[function(t,e,a){"use strict";e.exports={Z_NO_FLUSH:0,Z_PARTIAL_FLUSH:1,Z_SYNC_FLUSH:2,Z_FULL_FLUSH:3,Z_FINISH:4,Z_BLOCK:5,Z_TREES:6,Z_OK:0,Z_STREAM_END:1,Z_NEED_DICT:2,Z_ERRNO:-1,Z_STREAM_ERROR:-2,Z_DATA_ERROR:-3,Z_BUF_ERROR:-5,Z_NO_COMPRESSION:0,Z_BEST_SPEED:1,Z_BEST_COMPRESSION:9,Z_DEFAULT_COMPRESSION:-1,Z_FILTERED:1,Z_HUFFMAN_ONLY:2,Z_RLE:3,Z_FIXED:4,Z_DEFAULT_STRATEGY:0,Z_BINARY:0,Z_TEXT:1,Z_UNKNOWN:2,Z_DEFLATED:8}},{}],7:[function(t,e,a){"use strict";var i=function(){for(var t,e=[],a=0;a<256;a++){t=a;for(var i=0;i<8;i++)t=1&t?3988292384^t>>>1:t>>>1;e[a]=t}return e}();e.exports=function(t,e,a,n){var r=i,s=n+a;t^=-1;for(var o=n;o<s;o++)t=t>>>8^r[255&(t^e[o])];return-1^t}},{}],8:[function(t,e,a){"use strict";function i(t,e){return t.msg=A[e],e}function n(t){return(t<<1)-(t>4?9:0)}function r(t){for(var e=t.length;--e>=0;)t[e]=0}function s(t){var e=t.state,a=e.pending;a>t.avail_out&&(a=t.avail_out),0!==a&&(z.arraySet(t.output,e.pending_buf,e.pending_out,a,t.next_out),t.next_out+=a,e.pending_out+=a,t.total_out+=a,t.avail_out-=a,e.pending-=a,0===e.pending&&(e.pending_out=0))}function o(t,e){B._tr_flush_block(t,t.block_start>=0?t.block_start:-1,t.strstart-t.block_start,e),t.block_start=t.strstart,s(t.strm)}function l(t,e){t.pending_buf[t.pending++]=e}function h(t,e){t.pending_buf[t.pending++]=e>>>8&255,t.pending_buf[t.pending++]=255&e}function d(t,e,a,i){var n=t.avail_in;return n>i&&(n=i),0===n?0:(t.avail_in-=n,z.arraySet(e,t.input,t.next_in,n,a),1===t.state.wrap?t.adler=S(t.adler,e,n,a):2===t.state.wrap&&(t.adler=E(t.adler,e,n,a)),t.next_in+=n,t.total_in+=n,n)}function f(t,e){var a,i,n=t.max_chain_length,r=t.strstart,s=t.prev_length,o=t.nice_match,l=t.strstart>t.w_size-it?t.strstart-(t.w_size-it):0,h=t.window,d=t.w_mask,f=t.prev,_=t.strstart+at,u=h[r+s-1],c=h[r+s];t.prev_length>=t.good_match&&(n>>=2),o>t.lookahead&&(o=t.lookahead);do{if(a=e,h[a+s]===c&&h[a+s-1]===u&&h[a]===h[r]&&h[++a]===h[r+1]){r+=2,a++;do{}while(h[++r]===h[++a]&&h[++r]===h[++a]&&h[++r]===h[++a]&&h[++r]===h[++a]&&h[++r]===h[++a]&&h[++r]===h[++a]&&h[++r]===h[++a]&&h[++r]===h[++a]&&r<_);if(i=at-(_-r),r=_-at,i>s){if(t.match_start=e,s=i,i>=o)break;u=h[r+s-1],c=h[r+s]}}}while((e=f[e&d])>l&&0!=--n);return s<=t.lookahead?s:t.lookahead}function _(t){var e,a,i,n,r,s=t.w_size;do{if(n=t.window_size-t.lookahead-t.strstart,t.strstart>=s+(s-it)){z.arraySet(t.window,t.window,s,s,0),t.match_start-=s,t.strstart-=s,t.block_start-=s,e=a=t.hash_size;do{i=t.head[--e],t.head[e]=i>=s?i-s:0}while(--a);e=a=s;do{i=t.prev[--e],t.prev[e]=i>=s?i-s:0}while(--a);n+=s}if(0===t.strm.avail_in)break;if(a=d(t.strm,t.window,t.strstart+t.lookahead,n),t.lookahead+=a,t.lookahead+t.insert>=et)for(r=t.strstart-t.insert,t.ins_h=t.window[r],t.ins_h=(t.ins_h<<t.hash_shift^t.window[r+1])&t.hash_mask;t.insert&&(t.ins_h=(t.ins_h<<t.hash_shift^t.window[r+et-1])&t.hash_mask,t.prev[r&t.w_mask]=t.head[t.ins_h],t.head[t.ins_h]=r,r++,t.insert--,!(t.lookahead+t.insert<et)););}while(t.lookahead<it&&0!==t.strm.avail_in)}function u(t,e){for(var a,i;;){if(t.lookahead<it){if(_(t),t.lookahead<it&&e===Z)return _t;if(0===t.lookahead)break}if(a=0,t.lookahead>=et&&(t.ins_h=(t.ins_h<<t.hash_shift^t.window[t.strstart+et-1])&t.hash_mask,a=t.prev[t.strstart&t.w_mask]=t.head[t.ins_h],t.head[t.ins_h]=t.strstart),0!==a&&t.strstart-a<=t.w_size-it&&(t.match_length=f(t,a)),t.match_length>=et)if(i=B._tr_tally(t,t.strstart-t.match_start,t.match_length-et),t.lookahead-=t.match_length,t.match_length<=t.max_lazy_match&&t.lookahead>=et){t.match_length--;do{t.strstart++,t.ins_h=(t.ins_h<<t.hash_shift^t.window[t.strstart+et-1])&t.hash_mask,a=t.prev[t.strstart&t.w_mask]=t.head[t.ins_h],t.head[t.ins_h]=t.strstart}while(0!=--t.match_length);t.strstart++}else t.strstart+=t.match_length,t.match_length=0,t.ins_h=t.window[t.strstart],t.ins_h=(t.ins_h<<t.hash_shift^t.window[t.strstart+1])&t.hash_mask;else i=B._tr_tally(t,0,t.window[t.strstart]),t.lookahead--,t.strstart++;if(i&&(o(t,!1),0===t.strm.avail_out))return _t}return t.insert=t.strstart<et-1?t.strstart:et-1,e===N?(o(t,!0),0===t.strm.avail_out?ct:bt):t.last_lit&&(o(t,!1),0===t.strm.avail_out)?_t:ut}function c(t,e){for(var a,i,n;;){if(t.lookahead<it){if(_(t),t.lookahead<it&&e===Z)return _t;if(0===t.lookahead)break}if(a=0,t.lookahead>=et&&(t.ins_h=(t.ins_h<<t.hash_shift^t.window[t.strstart+et-1])&t.hash_mask,a=t.prev[t.strstart&t.w_mask]=t.head[t.ins_h],t.head[t.ins_h]=t.strstart),t.prev_length=t.match_length,t.prev_match=t.match_start,t.match_length=et-1,0!==a&&t.prev_length<t.max_lazy_match&&t.strstart-a<=t.w_size-it&&(t.match_length=f(t,a),t.match_length<=5&&(t.strategy===H||t.match_length===et&&t.strstart-t.match_start>4096)&&(t.match_length=et-1)),t.prev_length>=et&&t.match_length<=t.prev_length){n=t.strstart+t.lookahead-et,i=B._tr_tally(t,t.strstart-1-t.prev_match,t.prev_length-et),t.lookahead-=t.prev_length-1,t.prev_length-=2;do{++t.strstart<=n&&(t.ins_h=(t.ins_h<<t.hash_shift^t.window[t.strstart+et-1])&t.hash_mask,a=t.prev[t.strstart&t.w_mask]=t.head[t.ins_h],t.head[t.ins_h]=t.strstart)}while(0!=--t.prev_length);if(t.match_available=0,t.match_length=et-1,t.strstart++,i&&(o(t,!1),0===t.strm.avail_out))return _t}else if(t.match_available){if((i=B._tr_tally(t,0,t.window[t.strstart-1]))&&o(t,!1),t.strstart++,t.lookahead--,0===t.strm.avail_out)return _t}else t.match_available=1,t.strstart++,t.lookahead--}return t.match_available&&(i=B._tr_tally(t,0,t.window[t.strstart-1]),t.match_available=0),t.insert=t.strstart<et-1?t.strstart:et-1,e===N?(o(t,!0),0===t.strm.avail_out?ct:bt):t.last_lit&&(o(t,!1),0===t.strm.avail_out)?_t:ut}function b(t,e){for(var a,i,n,r,s=t.window;;){if(t.lookahead<=at){if(_(t),t.lookahead<=at&&e===Z)return _t;if(0===t.lookahead)break}if(t.match_length=0,t.lookahead>=et&&t.strstart>0&&(n=t.strstart-1,(i=s[n])===s[++n]&&i===s[++n]&&i===s[++n])){r=t.strstart+at;do{}while(i===s[++n]&&i===s[++n]&&i===s[++n]&&i===s[++n]&&i===s[++n]&&i===s[++n]&&i===s[++n]&&i===s[++n]&&n<r);t.match_length=at-(r-n),t.match_length>t.lookahead&&(t.match_length=t.lookahead)}if(t.match_length>=et?(a=B._tr_tally(t,1,t.match_length-et),t.lookahead-=t.match_length,t.strstart+=t.match_length,t.match_length=0):(a=B._tr_tally(t,0,t.window[t.strstart]),t.lookahead--,t.strstart++),a&&(o(t,!1),0===t.strm.avail_out))return _t}return t.insert=0,e===N?(o(t,!0),0===t.strm.avail_out?ct:bt):t.last_lit&&(o(t,!1),0===t.strm.avail_out)?_t:ut}function g(t,e){for(var a;;){if(0===t.lookahead&&(_(t),0===t.lookahead)){if(e===Z)return _t;break}if(t.match_length=0,a=B._tr_tally(t,0,t.window[t.strstart]),t.lookahead--,t.strstart++,a&&(o(t,!1),0===t.strm.avail_out))return _t}return t.insert=0,e===N?(o(t,!0),0===t.strm.avail_out?ct:bt):t.last_lit&&(o(t,!1),0===t.strm.avail_out)?_t:ut}function m(t,e,a,i,n){this.good_length=t,this.max_lazy=e,this.nice_length=a,this.max_chain=i,this.func=n}function w(t){t.window_size=2*t.w_size,r(t.head),t.max_lazy_match=x[t.level].max_lazy,t.good_match=x[t.level].good_length,t.nice_match=x[t.level].nice_length,t.max_chain_length=x[t.level].max_chain,t.strstart=0,t.block_start=0,t.lookahead=0,t.insert=0,t.match_length=t.prev_length=et-1,t.match_available=0,t.ins_h=0}function p(){this.strm=null,this.status=0,this.pending_buf=null,this.pending_buf_size=0,this.pending_out=0,this.pending=0,this.wrap=0,this.gzhead=null,this.gzindex=0,this.method=q,this.last_flush=-1,this.w_size=0,this.w_bits=0,this.w_mask=0,this.window=null,this.window_size=0,this.prev=null,this.head=null,this.ins_h=0,this.hash_size=0,this.hash_bits=0,this.hash_mask=0,this.hash_shift=0,this.block_start=0,this.match_length=0,this.prev_match=0,this.match_available=0,this.strstart=0,this.match_start=0,this.lookahead=0,this.prev_length=0,this.max_chain_length=0,this.max_lazy_match=0,this.level=0,this.strategy=0,this.good_match=0,this.nice_match=0,this.dyn_ltree=new z.Buf16(2*$),this.dyn_dtree=new z.Buf16(2*(2*Q+1)),this.bl_tree=new z.Buf16(2*(2*V+1)),r(this.dyn_ltree),r(this.dyn_dtree),r(this.bl_tree),this.l_desc=null,this.d_desc=null,this.bl_desc=null,this.bl_count=new z.Buf16(tt+1),this.heap=new z.Buf16(2*J+1),r(this.heap),this.heap_len=0,this.heap_max=0,this.depth=new z.Buf16(2*J+1),r(this.depth),this.l_buf=0,this.lit_bufsize=0,this.last_lit=0,this.d_buf=0,this.opt_len=0,this.static_len=0,this.matches=0,this.insert=0,this.bi_buf=0,this.bi_valid=0}function v(t){var e;return t&&t.state?(t.total_in=t.total_out=0,t.data_type=Y,e=t.state,e.pending=0,e.pending_out=0,e.wrap<0&&(e.wrap=-e.wrap),e.status=e.wrap?rt:dt,t.adler=2===e.wrap?0:1,e.last_flush=Z,B._tr_init(e),D):i(t,U)}function k(t){var e=v(t);return e===D&&w(t.state),e}function y(t,e,a,n,r,s){if(!t)return U;var o=1;if(e===L&&(e=6),n<0?(o=0,n=-n):n>15&&(o=2,n-=16),r<1||r>G||a!==q||n<8||n>15||e<0||e>9||s<0||s>M)return i(t,U);8===n&&(n=9);var l=new p;return t.state=l,l.strm=t,l.wrap=o,l.gzhead=null,l.w_bits=n,l.w_size=1<<l.w_bits,l.w_mask=l.w_size-1,l.hash_bits=r+7,l.hash_size=1<<l.hash_bits,l.hash_mask=l.hash_size-1,l.hash_shift=~~((l.hash_bits+et-1)/et),l.window=new z.Buf8(2*l.w_size),l.head=new z.Buf16(l.hash_size),l.prev=new z.Buf16(l.w_size),l.lit_bufsize=1<<r+6,l.pending_buf_size=4*l.lit_bufsize,l.pending_buf=new z.Buf8(l.pending_buf_size),l.d_buf=1*l.lit_bufsize,l.l_buf=3*l.lit_bufsize,l.level=e,l.strategy=s,l.method=a,k(t)}var x,z=t("../utils/common"),B=t("./trees"),S=t("./adler32"),E=t("./crc32"),A=t("./messages"),Z=0,R=1,C=3,N=4,O=5,D=0,I=1,U=-2,T=-3,F=-5,L=-1,H=1,j=2,K=3,M=4,P=0,Y=2,q=8,G=9,X=15,W=8,J=286,Q=30,V=19,$=2*J+1,tt=15,et=3,at=258,it=at+et+1,nt=32,rt=42,st=69,ot=73,lt=91,ht=103,dt=113,ft=666,_t=1,ut=2,ct=3,bt=4,gt=3;x=[new m(0,0,0,0,function(t,e){var a=65535;for(a>t.pending_buf_size-5&&(a=t.pending_buf_size-5);;){if(t.lookahead<=1){if(_(t),0===t.lookahead&&e===Z)return _t;if(0===t.lookahead)break}t.strstart+=t.lookahead,t.lookahead=0;var i=t.block_start+a;if((0===t.strstart||t.strstart>=i)&&(t.lookahead=t.strstart-i,t.strstart=i,o(t,!1),0===t.strm.avail_out))return _t;if(t.strstart-t.block_start>=t.w_size-it&&(o(t,!1),0===t.strm.avail_out))return _t}return t.insert=0,e===N?(o(t,!0),0===t.strm.avail_out?ct:bt):(t.strstart>t.block_start&&(o(t,!1),t.strm.avail_out),_t)}),new m(4,4,8,4,u),new m(4,5,16,8,u),new m(4,6,32,32,u),new m(4,4,16,16,c),new m(8,16,32,32,c),new m(8,16,128,128,c),new m(8,32,128,256,c),new m(32,128,258,1024,c),new m(32,258,258,4096,c)],a.deflateInit=function(t,e){return y(t,e,q,X,W,P)},a.deflateInit2=y,a.deflateReset=k,a.deflateResetKeep=v,a.deflateSetHeader=function(t,e){return t&&t.state?2!==t.state.wrap?U:(t.state.gzhead=e,D):U},a.deflate=function(t,e){var a,o,d,f;if(!t||!t.state||e>O||e<0)return t?i(t,U):U;if(o=t.state,!t.output||!t.input&&0!==t.avail_in||o.status===ft&&e!==N)return i(t,0===t.avail_out?F:U);if(o.strm=t,a=o.last_flush,o.last_flush=e,o.status===rt)if(2===o.wrap)t.adler=0,l(o,31),l(o,139),l(o,8),o.gzhead?(l(o,(o.gzhead.text?1:0)+(o.gzhead.hcrc?2:0)+(o.gzhead.extra?4:0)+(o.gzhead.name?8:0)+(o.gzhead.comment?16:0)),l(o,255&o.gzhead.time),l(o,o.gzhead.time>>8&255),l(o,o.gzhead.time>>16&255),l(o,o.gzhead.time>>24&255),l(o,9===o.level?2:o.strategy>=j||o.level<2?4:0),l(o,255&o.gzhead.os),o.gzhead.extra&&o.gzhead.extra.length&&(l(o,255&o.gzhead.extra.length),l(o,o.gzhead.extra.length>>8&255)),o.gzhead.hcrc&&(t.adler=E(t.adler,o.pending_buf,o.pending,0)),o.gzindex=0,o.status=st):(l(o,0),l(o,0),l(o,0),l(o,0),l(o,0),l(o,9===o.level?2:o.strategy>=j||o.level<2?4:0),l(o,gt),o.status=dt);else{var _=q+(o.w_bits-8<<4)<<8;_|=(o.strategy>=j||o.level<2?0:o.level<6?1:6===o.level?2:3)<<6,0!==o.strstart&&(_|=nt),_+=31-_%31,o.status=dt,h(o,_),0!==o.strstart&&(h(o,t.adler>>>16),h(o,65535&t.adler)),t.adler=1}if(o.status===st)if(o.gzhead.extra){for(d=o.pending;o.gzindex<(65535&o.gzhead.extra.length)&&(o.pending!==o.pending_buf_size||(o.gzhead.hcrc&&o.pending>d&&(t.adler=E(t.adler,o.pending_buf,o.pending-d,d)),s(t),d=o.pending,o.pending!==o.pending_buf_size));)l(o,255&o.gzhead.extra[o.gzindex]),o.gzindex++;o.gzhead.hcrc&&o.pending>d&&(t.adler=E(t.adler,o.pending_buf,o.pending-d,d)),o.gzindex===o.gzhead.extra.length&&(o.gzindex=0,o.status=ot)}else o.status=ot;if(o.status===ot)if(o.gzhead.name){d=o.pending;do{if(o.pending===o.pending_buf_size&&(o.gzhead.hcrc&&o.pending>d&&(t.adler=E(t.adler,o.pending_buf,o.pending-d,d)),s(t),d=o.pending,o.pending===o.pending_buf_size)){f=1;break}f=o.gzindex<o.gzhead.name.length?255&o.gzhead.name.charCodeAt(o.gzindex++):0,l(o,f)}while(0!==f);o.gzhead.hcrc&&o.pending>d&&(t.adler=E(t.adler,o.pending_buf,o.pending-d,d)),0===f&&(o.gzindex=0,o.status=lt)}else o.status=lt;if(o.status===lt)if(o.gzhead.comment){d=o.pending;do{if(o.pending===o.pending_buf_size&&(o.gzhead.hcrc&&o.pending>d&&(t.adler=E(t.adler,o.pending_buf,o.pending-d,d)),s(t),d=o.pending,o.pending===o.pending_buf_size)){f=1;break}f=o.gzindex<o.gzhead.comment.length?255&o.gzhead.comment.charCodeAt(o.gzindex++):0,l(o,f)}while(0!==f);o.gzhead.hcrc&&o.pending>d&&(t.adler=E(t.adler,o.pending_buf,o.pending-d,d)),0===f&&(o.status=ht)}else o.status=ht;if(o.status===ht&&(o.gzhead.hcrc?(o.pending+2>o.pending_buf_size&&s(t),o.pending+2<=o.pending_buf_size&&(l(o,255&t.adler),l(o,t.adler>>8&255),t.adler=0,o.status=dt)):o.status=dt),0!==o.pending){if(s(t),0===t.avail_out)return o.last_flush=-1,D}else if(0===t.avail_in&&n(e)<=n(a)&&e!==N)return i(t,F);if(o.status===ft&&0!==t.avail_in)return i(t,F);if(0!==t.avail_in||0!==o.lookahead||e!==Z&&o.status!==ft){var u=o.strategy===j?g(o,e):o.strategy===K?b(o,e):x[o.level].func(o,e);if(u!==ct&&u!==bt||(o.status=ft),u===_t||u===ct)return 0===t.avail_out&&(o.last_flush=-1),D;if(u===ut&&(e===R?B._tr_align(o):e!==O&&(B._tr_stored_block(o,0,0,!1),e===C&&(r(o.head),0===o.lookahead&&(o.strstart=0,o.block_start=0,o.insert=0))),s(t),0===t.avail_out))return o.last_flush=-1,D}return e!==N?D:o.wrap<=0?I:(2===o.wrap?(l(o,255&t.adler),l(o,t.adler>>8&255),l(o,t.adler>>16&255),l(o,t.adler>>24&255),l(o,255&t.total_in),l(o,t.total_in>>8&255),l(o,t.total_in>>16&255),l(o,t.total_in>>24&255)):(h(o,t.adler>>>16),h(o,65535&t.adler)),s(t),o.wrap>0&&(o.wrap=-o.wrap),0!==o.pending?D:I)},a.deflateEnd=function(t){var e;return t&&t.state?(e=t.state.status)!==rt&&e!==st&&e!==ot&&e!==lt&&e!==ht&&e!==dt&&e!==ft?i(t,U):(t.state=null,e===dt?i(t,T):D):U},a.deflateSetDictionary=function(t,e){var a,i,n,s,o,l,h,d,f=e.length;if(!t||!t.state)return U;if(a=t.state,2===(s=a.wrap)||1===s&&a.status!==rt||a.lookahead)return U;for(1===s&&(t.adler=S(t.adler,e,f,0)),a.wrap=0,f>=a.w_size&&(0===s&&(r(a.head),a.strstart=0,a.block_start=0,a.insert=0),d=new z.Buf8(a.w_size),z.arraySet(d,e,f-a.w_size,a.w_size,0),e=d,f=a.w_size),o=t.avail_in,l=t.next_in,h=t.input,t.avail_in=f,t.next_in=0,t.input=e,_(a);a.lookahead>=et;){i=a.strstart,n=a.lookahead-(et-1);do{a.ins_h=(a.ins_h<<a.hash_shift^a.window[i+et-1])&a.hash_mask,a.prev[i&a.w_mask]=a.head[a.ins_h],a.head[a.ins_h]=i,i++}while(--n);a.strstart=i,a.lookahead=et-1,_(a)}return a.strstart+=a.lookahead,a.block_start=a.strstart,a.insert=a.lookahead,a.lookahead=0,a.match_length=a.prev_length=et-1,a.match_available=0,t.next_in=l,t.input=h,t.avail_in=o,a.wrap=s,D},a.deflateInfo="pako deflate (from Nodeca project)"},{"../utils/common":3,"./adler32":5,"./crc32":7,"./messages":13,"./trees":14}],9:[function(t,e,a){"use strict";e.exports=function(){this.text=0,this.time=0,this.xflags=0,this.os=0,this.extra=null,this.extra_len=0,this.name="",this.comment="",this.hcrc=0,this.done=!1}},{}],10:[function(t,e,a){"use strict";e.exports=function(t,e){var a,i,n,r,s,o,l,h,d,f,_,u,c,b,g,m,w,p,v,k,y,x,z,B,S;a=t.state,i=t.next_in,B=t.input,n=i+(t.avail_in-5),r=t.next_out,S=t.output,s=r-(e-t.avail_out),o=r+(t.avail_out-257),l=a.dmax,h=a.wsize,d=a.whave,f=a.wnext,_=a.window,u=a.hold,c=a.bits,b=a.lencode,g=a.distcode,m=(1<<a.lenbits)-1,w=(1<<a.distbits)-1;t:do{c<15&&(u+=B[i++]<<c,c+=8,u+=B[i++]<<c,c+=8),p=b[u&m];e:for(;;){if(v=p>>>24,u>>>=v,c-=v,0===(v=p>>>16&255))S[r++]=65535&p;else{if(!(16&v)){if(0==(64&v)){p=b[(65535&p)+(u&(1<<v)-1)];continue e}if(32&v){a.mode=12;break t}t.msg="invalid literal/length code",a.mode=30;break t}k=65535&p,(v&=15)&&(c<v&&(u+=B[i++]<<c,c+=8),k+=u&(1<<v)-1,u>>>=v,c-=v),c<15&&(u+=B[i++]<<c,c+=8,u+=B[i++]<<c,c+=8),p=g[u&w];a:for(;;){if(v=p>>>24,u>>>=v,c-=v,!(16&(v=p>>>16&255))){if(0==(64&v)){p=g[(65535&p)+(u&(1<<v)-1)];continue a}t.msg="invalid distance code",a.mode=30;break t}if(y=65535&p,v&=15,c<v&&(u+=B[i++]<<c,(c+=8)<v&&(u+=B[i++]<<c,c+=8)),(y+=u&(1<<v)-1)>l){t.msg="invalid distance too far back",a.mode=30;break t}if(u>>>=v,c-=v,v=r-s,y>v){if((v=y-v)>d&&a.sane){t.msg="invalid distance too far back",a.mode=30;break t}if(x=0,z=_,0===f){if(x+=h-v,v<k){k-=v;do{S[r++]=_[x++]}while(--v);x=r-y,z=S}}else if(f<v){if(x+=h+f-v,(v-=f)<k){k-=v;do{S[r++]=_[x++]}while(--v);if(x=0,f<k){k-=v=f;do{S[r++]=_[x++]}while(--v);x=r-y,z=S}}}else if(x+=f-v,v<k){k-=v;do{S[r++]=_[x++]}while(--v);x=r-y,z=S}for(;k>2;)S[r++]=z[x++],S[r++]=z[x++],S[r++]=z[x++],k-=3;k&&(S[r++]=z[x++],k>1&&(S[r++]=z[x++]))}else{x=r-y;do{S[r++]=S[x++],S[r++]=S[x++],S[r++]=S[x++],k-=3}while(k>2);k&&(S[r++]=S[x++],k>1&&(S[r++]=S[x++]))}break}}break}}while(i<n&&r<o);i-=k=c>>3,u&=(1<<(c-=k<<3))-1,t.next_in=i,t.next_out=r,t.avail_in=i<n?n-i+5:5-(i-n),t.avail_out=r<o?o-r+257:257-(r-o),a.hold=u,a.bits=c}},{}],11:[function(t,e,a){"use strict";function i(t){return(t>>>24&255)+(t>>>8&65280)+((65280&t)<<8)+((255&t)<<24)}function n(){this.mode=0,this.last=!1,this.wrap=0,this.havedict=!1,this.flags=0,this.dmax=0,this.check=0,this.total=0,this.head=null,this.wbits=0,this.wsize=0,this.whave=0,this.wnext=0,this.window=null,this.hold=0,this.bits=0,this.length=0,this.offset=0,this.extra=0,this.lencode=null,this.distcode=null,this.lenbits=0,this.distbits=0,this.ncode=0,this.nlen=0,this.ndist=0,this.have=0,this.next=null,this.lens=new u.Buf16(320),this.work=new u.Buf16(288),this.lendyn=null,this.distdyn=null,this.sane=0,this.back=0,this.was=0}function r(t){var e;return t&&t.state?(e=t.state,t.total_in=t.total_out=e.total=0,t.msg="",e.wrap&&(t.adler=1&e.wrap),e.mode=N,e.last=0,e.havedict=0,e.dmax=32768,e.head=null,e.hold=0,e.bits=0,e.lencode=e.lendyn=new u.Buf32(dt),e.distcode=e.distdyn=new u.Buf32(ft),e.sane=1,e.back=-1,z):E}function s(t){var e;return t&&t.state?(e=t.state,e.wsize=0,e.whave=0,e.wnext=0,r(t)):E}function o(t,e){var a,i;return t&&t.state?(i=t.state,e<0?(a=0,e=-e):(a=1+(e>>4),e<48&&(e&=15)),e&&(e<8||e>15)?E:(null!==i.window&&i.wbits!==e&&(i.window=null),i.wrap=a,i.wbits=e,s(t))):E}function l(t,e){var a,i;return t?(i=new n,t.state=i,i.window=null,(a=o(t,e))!==z&&(t.state=null),a):E}function h(t){if(ut){var e;for(f=new u.Buf32(512),_=new u.Buf32(32),e=0;e<144;)t.lens[e++]=8;for(;e<256;)t.lens[e++]=9;for(;e<280;)t.lens[e++]=7;for(;e<288;)t.lens[e++]=8;for(m(p,t.lens,0,288,f,0,t.work,{bits:9}),e=0;e<32;)t.lens[e++]=5;m(v,t.lens,0,32,_,0,t.work,{bits:5}),ut=!1}t.lencode=f,t.lenbits=9,t.distcode=_,t.distbits=5}function d(t,e,a,i){var n,r=t.state;return null===r.window&&(r.wsize=1<<r.wbits,r.wnext=0,r.whave=0,r.window=new u.Buf8(r.wsize)),i>=r.wsize?(u.arraySet(r.window,e,a-r.wsize,r.wsize,0),r.wnext=0,r.whave=r.wsize):((n=r.wsize-r.wnext)>i&&(n=i),u.arraySet(r.window,e,a-i,n,r.wnext),(i-=n)?(u.arraySet(r.window,e,a-i,i,0),r.wnext=i,r.whave=r.wsize):(r.wnext+=n,r.wnext===r.wsize&&(r.wnext=0),r.whave<r.wsize&&(r.whave+=n))),0}var f,_,u=t("../utils/common"),c=t("./adler32"),b=t("./crc32"),g=t("./inffast"),m=t("./inftrees"),w=0,p=1,v=2,k=4,y=5,x=6,z=0,B=1,S=2,E=-2,A=-3,Z=-4,R=-5,C=8,N=1,O=2,D=3,I=4,U=5,T=6,F=7,L=8,H=9,j=10,K=11,M=12,P=13,Y=14,q=15,G=16,X=17,W=18,J=19,Q=20,V=21,$=22,tt=23,et=24,at=25,it=26,nt=27,rt=28,st=29,ot=30,lt=31,ht=32,dt=852,ft=592,_t=15,ut=!0;a.inflateReset=s,a.inflateReset2=o,a.inflateResetKeep=r,a.inflateInit=function(t){return l(t,_t)},a.inflateInit2=l,a.inflate=function(t,e){var a,n,r,s,o,l,f,_,dt,ft,_t,ut,ct,bt,gt,mt,wt,pt,vt,kt,yt,xt,zt,Bt,St=0,Et=new u.Buf8(4),At=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15];if(!t||!t.state||!t.output||!t.input&&0!==t.avail_in)return E;(a=t.state).mode===M&&(a.mode=P),o=t.next_out,r=t.output,f=t.avail_out,s=t.next_in,n=t.input,l=t.avail_in,_=a.hold,dt=a.bits,ft=l,_t=f,xt=z;t:for(;;)switch(a.mode){case N:if(0===a.wrap){a.mode=P;break}for(;dt<16;){if(0===l)break t;l--,_+=n[s++]<<dt,dt+=8}if(2&a.wrap&&35615===_){a.check=0,Et[0]=255&_,Et[1]=_>>>8&255,a.check=b(a.check,Et,2,0),_=0,dt=0,a.mode=O;break}if(a.flags=0,a.head&&(a.head.done=!1),!(1&a.wrap)||(((255&_)<<8)+(_>>8))%31){t.msg="incorrect header check",a.mode=ot;break}if((15&_)!==C){t.msg="unknown compression method",a.mode=ot;break}if(_>>>=4,dt-=4,yt=8+(15&_),0===a.wbits)a.wbits=yt;else if(yt>a.wbits){t.msg="invalid window size",a.mode=ot;break}a.dmax=1<<yt,t.adler=a.check=1,a.mode=512&_?j:M,_=0,dt=0;break;case O:for(;dt<16;){if(0===l)break t;l--,_+=n[s++]<<dt,dt+=8}if(a.flags=_,(255&a.flags)!==C){t.msg="unknown compression method",a.mode=ot;break}if(57344&a.flags){t.msg="unknown header flags set",a.mode=ot;break}a.head&&(a.head.text=_>>8&1),512&a.flags&&(Et[0]=255&_,Et[1]=_>>>8&255,a.check=b(a.check,Et,2,0)),_=0,dt=0,a.mode=D;case D:for(;dt<32;){if(0===l)break t;l--,_+=n[s++]<<dt,dt+=8}a.head&&(a.head.time=_),512&a.flags&&(Et[0]=255&_,Et[1]=_>>>8&255,Et[2]=_>>>16&255,Et[3]=_>>>24&255,a.check=b(a.check,Et,4,0)),_=0,dt=0,a.mode=I;case I:for(;dt<16;){if(0===l)break t;l--,_+=n[s++]<<dt,dt+=8}a.head&&(a.head.xflags=255&_,a.head.os=_>>8),512&a.flags&&(Et[0]=255&_,Et[1]=_>>>8&255,a.check=b(a.check,Et,2,0)),_=0,dt=0,a.mode=U;case U:if(1024&a.flags){for(;dt<16;){if(0===l)break t;l--,_+=n[s++]<<dt,dt+=8}a.length=_,a.head&&(a.head.extra_len=_),512&a.flags&&(Et[0]=255&_,Et[1]=_>>>8&255,a.check=b(a.check,Et,2,0)),_=0,dt=0}else a.head&&(a.head.extra=null);a.mode=T;case T:if(1024&a.flags&&((ut=a.length)>l&&(ut=l),ut&&(a.head&&(yt=a.head.extra_len-a.length,a.head.extra||(a.head.extra=new Array(a.head.extra_len)),u.arraySet(a.head.extra,n,s,ut,yt)),512&a.flags&&(a.check=b(a.check,n,ut,s)),l-=ut,s+=ut,a.length-=ut),a.length))break t;a.length=0,a.mode=F;case F:if(2048&a.flags){if(0===l)break t;ut=0;do{yt=n[s+ut++],a.head&&yt&&a.length<65536&&(a.head.name+=String.fromCharCode(yt))}while(yt&&ut<l);if(512&a.flags&&(a.check=b(a.check,n,ut,s)),l-=ut,s+=ut,yt)break t}else a.head&&(a.head.name=null);a.length=0,a.mode=L;case L:if(4096&a.flags){if(0===l)break t;ut=0;do{yt=n[s+ut++],a.head&&yt&&a.length<65536&&(a.head.comment+=String.fromCharCode(yt))}while(yt&&ut<l);if(512&a.flags&&(a.check=b(a.check,n,ut,s)),l-=ut,s+=ut,yt)break t}else a.head&&(a.head.comment=null);a.mode=H;case H:if(512&a.flags){for(;dt<16;){if(0===l)break t;l--,_+=n[s++]<<dt,dt+=8}if(_!==(65535&a.check)){t.msg="header crc mismatch",a.mode=ot;break}_=0,dt=0}a.head&&(a.head.hcrc=a.flags>>9&1,a.head.done=!0),t.adler=a.check=0,a.mode=M;break;case j:for(;dt<32;){if(0===l)break t;l--,_+=n[s++]<<dt,dt+=8}t.adler=a.check=i(_),_=0,dt=0,a.mode=K;case K:if(0===a.havedict)return t.next_out=o,t.avail_out=f,t.next_in=s,t.avail_in=l,a.hold=_,a.bits=dt,S;t.adler=a.check=1,a.mode=M;case M:if(e===y||e===x)break t;case P:if(a.last){_>>>=7&dt,dt-=7&dt,a.mode=nt;break}for(;dt<3;){if(0===l)break t;l--,_+=n[s++]<<dt,dt+=8}switch(a.last=1&_,_>>>=1,dt-=1,3&_){case 0:a.mode=Y;break;case 1:if(h(a),a.mode=Q,e===x){_>>>=2,dt-=2;break t}break;case 2:a.mode=X;break;case 3:t.msg="invalid block type",a.mode=ot}_>>>=2,dt-=2;break;case Y:for(_>>>=7&dt,dt-=7&dt;dt<32;){if(0===l)break t;l--,_+=n[s++]<<dt,dt+=8}if((65535&_)!=(_>>>16^65535)){t.msg="invalid stored block lengths",a.mode=ot;break}if(a.length=65535&_,_=0,dt=0,a.mode=q,e===x)break t;case q:a.mode=G;case G:if(ut=a.length){if(ut>l&&(ut=l),ut>f&&(ut=f),0===ut)break t;u.arraySet(r,n,s,ut,o),l-=ut,s+=ut,f-=ut,o+=ut,a.length-=ut;break}a.mode=M;break;case X:for(;dt<14;){if(0===l)break t;l--,_+=n[s++]<<dt,dt+=8}if(a.nlen=257+(31&_),_>>>=5,dt-=5,a.ndist=1+(31&_),_>>>=5,dt-=5,a.ncode=4+(15&_),_>>>=4,dt-=4,a.nlen>286||a.ndist>30){t.msg="too many length or distance symbols",a.mode=ot;break}a.have=0,a.mode=W;case W:for(;a.have<a.ncode;){for(;dt<3;){if(0===l)break t;l--,_+=n[s++]<<dt,dt+=8}a.lens[At[a.have++]]=7&_,_>>>=3,dt-=3}for(;a.have<19;)a.lens[At[a.have++]]=0;if(a.lencode=a.lendyn,a.lenbits=7,zt={bits:a.lenbits},xt=m(w,a.lens,0,19,a.lencode,0,a.work,zt),a.lenbits=zt.bits,xt){t.msg="invalid code lengths set",a.mode=ot;break}a.have=0,a.mode=J;case J:for(;a.have<a.nlen+a.ndist;){for(;St=a.lencode[_&(1<<a.lenbits)-1],gt=St>>>24,mt=St>>>16&255,wt=65535&St,!(gt<=dt);){if(0===l)break t;l--,_+=n[s++]<<dt,dt+=8}if(wt<16)_>>>=gt,dt-=gt,a.lens[a.have++]=wt;else{if(16===wt){for(Bt=gt+2;dt<Bt;){if(0===l)break t;l--,_+=n[s++]<<dt,dt+=8}if(_>>>=gt,dt-=gt,0===a.have){t.msg="invalid bit length repeat",a.mode=ot;break}yt=a.lens[a.have-1],ut=3+(3&_),_>>>=2,dt-=2}else if(17===wt){for(Bt=gt+3;dt<Bt;){if(0===l)break t;l--,_+=n[s++]<<dt,dt+=8}dt-=gt,yt=0,ut=3+(7&(_>>>=gt)),_>>>=3,dt-=3}else{for(Bt=gt+7;dt<Bt;){if(0===l)break t;l--,_+=n[s++]<<dt,dt+=8}dt-=gt,yt=0,ut=11+(127&(_>>>=gt)),_>>>=7,dt-=7}if(a.have+ut>a.nlen+a.ndist){t.msg="invalid bit length repeat",a.mode=ot;break}for(;ut--;)a.lens[a.have++]=yt}}if(a.mode===ot)break;if(0===a.lens[256]){t.msg="invalid code -- missing end-of-block",a.mode=ot;break}if(a.lenbits=9,zt={bits:a.lenbits},xt=m(p,a.lens,0,a.nlen,a.lencode,0,a.work,zt),a.lenbits=zt.bits,xt){t.msg="invalid literal/lengths set",a.mode=ot;break}if(a.distbits=6,a.distcode=a.distdyn,zt={bits:a.distbits},xt=m(v,a.lens,a.nlen,a.ndist,a.distcode,0,a.work,zt),a.distbits=zt.bits,xt){t.msg="invalid distances set",a.mode=ot;break}if(a.mode=Q,e===x)break t;case Q:a.mode=V;case V:if(l>=6&&f>=258){t.next_out=o,t.avail_out=f,t.next_in=s,t.avail_in=l,a.hold=_,a.bits=dt,g(t,_t),o=t.next_out,r=t.output,f=t.avail_out,s=t.next_in,n=t.input,l=t.avail_in,_=a.hold,dt=a.bits,a.mode===M&&(a.back=-1);break}for(a.back=0;St=a.lencode[_&(1<<a.lenbits)-1],gt=St>>>24,mt=St>>>16&255,wt=65535&St,!(gt<=dt);){if(0===l)break t;l--,_+=n[s++]<<dt,dt+=8}if(mt&&0==(240&mt)){for(pt=gt,vt=mt,kt=wt;St=a.lencode[kt+((_&(1<<pt+vt)-1)>>pt)],gt=St>>>24,mt=St>>>16&255,wt=65535&St,!(pt+gt<=dt);){if(0===l)break t;l--,_+=n[s++]<<dt,dt+=8}_>>>=pt,dt-=pt,a.back+=pt}if(_>>>=gt,dt-=gt,a.back+=gt,a.length=wt,0===mt){a.mode=it;break}if(32&mt){a.back=-1,a.mode=M;break}if(64&mt){t.msg="invalid literal/length code",a.mode=ot;break}a.extra=15&mt,a.mode=$;case $:if(a.extra){for(Bt=a.extra;dt<Bt;){if(0===l)break t;l--,_+=n[s++]<<dt,dt+=8}a.length+=_&(1<<a.extra)-1,_>>>=a.extra,dt-=a.extra,a.back+=a.extra}a.was=a.length,a.mode=tt;case tt:for(;St=a.distcode[_&(1<<a.distbits)-1],gt=St>>>24,mt=St>>>16&255,wt=65535&St,!(gt<=dt);){if(0===l)break t;l--,_+=n[s++]<<dt,dt+=8}if(0==(240&mt)){for(pt=gt,vt=mt,kt=wt;St=a.distcode[kt+((_&(1<<pt+vt)-1)>>pt)],gt=St>>>24,mt=St>>>16&255,wt=65535&St,!(pt+gt<=dt);){if(0===l)break t;l--,_+=n[s++]<<dt,dt+=8}_>>>=pt,dt-=pt,a.back+=pt}if(_>>>=gt,dt-=gt,a.back+=gt,64&mt){t.msg="invalid distance code",a.mode=ot;break}a.offset=wt,a.extra=15&mt,a.mode=et;case et:if(a.extra){for(Bt=a.extra;dt<Bt;){if(0===l)break t;l--,_+=n[s++]<<dt,dt+=8}a.offset+=_&(1<<a.extra)-1,_>>>=a.extra,dt-=a.extra,a.back+=a.extra}if(a.offset>a.dmax){t.msg="invalid distance too far back",a.mode=ot;break}a.mode=at;case at:if(0===f)break t;if(ut=_t-f,a.offset>ut){if((ut=a.offset-ut)>a.whave&&a.sane){t.msg="invalid distance too far back",a.mode=ot;break}ut>a.wnext?(ut-=a.wnext,ct=a.wsize-ut):ct=a.wnext-ut,ut>a.length&&(ut=a.length),bt=a.window}else bt=r,ct=o-a.offset,ut=a.length;ut>f&&(ut=f),f-=ut,a.length-=ut;do{r[o++]=bt[ct++]}while(--ut);0===a.length&&(a.mode=V);break;case it:if(0===f)break t;r[o++]=a.length,f--,a.mode=V;break;case nt:if(a.wrap){for(;dt<32;){if(0===l)break t;l--,_|=n[s++]<<dt,dt+=8}if(_t-=f,t.total_out+=_t,a.total+=_t,_t&&(t.adler=a.check=a.flags?b(a.check,r,_t,o-_t):c(a.check,r,_t,o-_t)),_t=f,(a.flags?_:i(_))!==a.check){t.msg="incorrect data check",a.mode=ot;break}_=0,dt=0}a.mode=rt;case rt:if(a.wrap&&a.flags){for(;dt<32;){if(0===l)break t;l--,_+=n[s++]<<dt,dt+=8}if(_!==(4294967295&a.total)){t.msg="incorrect length check",a.mode=ot;break}_=0,dt=0}a.mode=st;case st:xt=B;break t;case ot:xt=A;break t;case lt:return Z;case ht:default:return E}return t.next_out=o,t.avail_out=f,t.next_in=s,t.avail_in=l,a.hold=_,a.bits=dt,(a.wsize||_t!==t.avail_out&&a.mode<ot&&(a.mode<nt||e!==k))&&d(t,t.output,t.next_out,_t-t.avail_out)?(a.mode=lt,Z):(ft-=t.avail_in,_t-=t.avail_out,t.total_in+=ft,t.total_out+=_t,a.total+=_t,a.wrap&&_t&&(t.adler=a.check=a.flags?b(a.check,r,_t,t.next_out-_t):c(a.check,r,_t,t.next_out-_t)),t.data_type=a.bits+(a.last?64:0)+(a.mode===M?128:0)+(a.mode===Q||a.mode===q?256:0),(0===ft&&0===_t||e===k)&&xt===z&&(xt=R),xt)},a.inflateEnd=function(t){if(!t||!t.state)return E;var e=t.state;return e.window&&(e.window=null),t.state=null,z},a.inflateGetHeader=function(t,e){var a;return t&&t.state?0==(2&(a=t.state).wrap)?E:(a.head=e,e.done=!1,z):E},a.inflateSetDictionary=function(t,e){var a,i,n=e.length;return t&&t.state?0!==(a=t.state).wrap&&a.mode!==K?E:a.mode===K&&(i=1,(i=c(i,e,n,0))!==a.check)?A:d(t,e,n,n)?(a.mode=lt,Z):(a.havedict=1,z):E},a.inflateInfo="pako inflate (from Nodeca project)"},{"../utils/common":3,"./adler32":5,"./crc32":7,"./inffast":10,"./inftrees":12}],12:[function(t,e,a){"use strict";var i=t("../utils/common"),n=[3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258,0,0],r=[16,16,16,16,16,16,16,16,17,17,17,17,18,18,18,18,19,19,19,19,20,20,20,20,21,21,21,21,16,72,78],s=[1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0],o=[16,16,16,16,17,17,18,18,19,19,20,20,21,21,22,22,23,23,24,24,25,25,26,26,27,27,28,28,29,29,64,64];e.exports=function(t,e,a,l,h,d,f,_){var u,c,b,g,m,w,p,v,k,y=_.bits,x=0,z=0,B=0,S=0,E=0,A=0,Z=0,R=0,C=0,N=0,O=null,D=0,I=new i.Buf16(16),U=new i.Buf16(16),T=null,F=0;for(x=0;x<=15;x++)I[x]=0;for(z=0;z<l;z++)I[e[a+z]]++;for(E=y,S=15;S>=1&&0===I[S];S--);if(E>S&&(E=S),0===S)return h[d++]=20971520,h[d++]=20971520,_.bits=1,0;for(B=1;B<S&&0===I[B];B++);for(E<B&&(E=B),R=1,x=1;x<=15;x++)if(R<<=1,(R-=I[x])<0)return-1;if(R>0&&(0===t||1!==S))return-1;for(U[1]=0,x=1;x<15;x++)U[x+1]=U[x]+I[x];for(z=0;z<l;z++)0!==e[a+z]&&(f[U[e[a+z]]++]=z);if(0===t?(O=T=f,w=19):1===t?(O=n,D-=257,T=r,F-=257,w=256):(O=s,T=o,w=-1),N=0,z=0,x=B,m=d,A=E,Z=0,b=-1,C=1<<E,g=C-1,1===t&&C>852||2===t&&C>592)return 1;for(;;){p=x-Z,f[z]<w?(v=0,k=f[z]):f[z]>w?(v=T[F+f[z]],k=O[D+f[z]]):(v=96,k=0),u=1<<x-Z,B=c=1<<A;do{h[m+(N>>Z)+(c-=u)]=p<<24|v<<16|k|0}while(0!==c);for(u=1<<x-1;N&u;)u>>=1;if(0!==u?(N&=u-1,N+=u):N=0,z++,0==--I[x]){if(x===S)break;x=e[a+f[z]]}if(x>E&&(N&g)!==b){for(0===Z&&(Z=E),m+=B,R=1<<(A=x-Z);A+Z<S&&!((R-=I[A+Z])<=0);)A++,R<<=1;if(C+=1<<A,1===t&&C>852||2===t&&C>592)return 1;h[b=N&g]=E<<24|A<<16|m-d|0}}return 0!==N&&(h[m+N]=x-Z<<24|64<<16|0),_.bits=E,0}},{"../utils/common":3}],13:[function(t,e,a){"use strict";e.exports={2:"need dictionary",1:"stream end",0:"","-1":"file error","-2":"stream error","-3":"data error","-4":"insufficient memory","-5":"buffer error","-6":"incompatible version"}},{}],14:[function(t,e,a){"use strict";function i(t){for(var e=t.length;--e>=0;)t[e]=0}function n(t,e,a,i,n){this.static_tree=t,this.extra_bits=e,this.extra_base=a,this.elems=i,this.max_length=n,this.has_stree=t&&t.length}function r(t,e){this.dyn_tree=t,this.max_code=0,this.stat_desc=e}function s(t){return t<256?et[t]:et[256+(t>>>7)]}function o(t,e){t.pending_buf[t.pending++]=255&e,t.pending_buf[t.pending++]=e>>>8&255}function l(t,e,a){t.bi_valid>M-a?(t.bi_buf|=e<<t.bi_valid&65535,o(t,t.bi_buf),t.bi_buf=e>>M-t.bi_valid,t.bi_valid+=a-M):(t.bi_buf|=e<<t.bi_valid&65535,t.bi_valid+=a)}function h(t,e,a){l(t,a[2*e],a[2*e+1])}function d(t,e){var a=0;do{a|=1&t,t>>>=1,a<<=1}while(--e>0);return a>>>1}function f(t){16===t.bi_valid?(o(t,t.bi_buf),t.bi_buf=0,t.bi_valid=0):t.bi_valid>=8&&(t.pending_buf[t.pending++]=255&t.bi_buf,t.bi_buf>>=8,t.bi_valid-=8)}function _(t,e){var a,i,n,r,s,o,l=e.dyn_tree,h=e.max_code,d=e.stat_desc.static_tree,f=e.stat_desc.has_stree,_=e.stat_desc.extra_bits,u=e.stat_desc.extra_base,c=e.stat_desc.max_length,b=0;for(r=0;r<=K;r++)t.bl_count[r]=0;for(l[2*t.heap[t.heap_max]+1]=0,a=t.heap_max+1;a<j;a++)(r=l[2*l[2*(i=t.heap[a])+1]+1]+1)>c&&(r=c,b++),l[2*i+1]=r,i>h||(t.bl_count[r]++,s=0,i>=u&&(s=_[i-u]),o=l[2*i],t.opt_len+=o*(r+s),f&&(t.static_len+=o*(d[2*i+1]+s)));if(0!==b){do{for(r=c-1;0===t.bl_count[r];)r--;t.bl_count[r]--,t.bl_count[r+1]+=2,t.bl_count[c]--,b-=2}while(b>0);for(r=c;0!==r;r--)for(i=t.bl_count[r];0!==i;)(n=t.heap[--a])>h||(l[2*n+1]!==r&&(t.opt_len+=(r-l[2*n+1])*l[2*n],l[2*n+1]=r),i--)}}function u(t,e,a){var i,n,r=new Array(K+1),s=0;for(i=1;i<=K;i++)r[i]=s=s+a[i-1]<<1;for(n=0;n<=e;n++){var o=t[2*n+1];0!==o&&(t[2*n]=d(r[o]++,o))}}function c(){var t,e,a,i,r,s=new Array(K+1);for(a=0,i=0;i<U-1;i++)for(it[i]=a,t=0;t<1<<W[i];t++)at[a++]=i;for(at[a-1]=i,r=0,i=0;i<16;i++)for(nt[i]=r,t=0;t<1<<J[i];t++)et[r++]=i;for(r>>=7;i<L;i++)for(nt[i]=r<<7,t=0;t<1<<J[i]-7;t++)et[256+r++]=i;for(e=0;e<=K;e++)s[e]=0;for(t=0;t<=143;)$[2*t+1]=8,t++,s[8]++;for(;t<=255;)$[2*t+1]=9,t++,s[9]++;for(;t<=279;)$[2*t+1]=7,t++,s[7]++;for(;t<=287;)$[2*t+1]=8,t++,s[8]++;for(u($,F+1,s),t=0;t<L;t++)tt[2*t+1]=5,tt[2*t]=d(t,5);rt=new n($,W,T+1,F,K),st=new n(tt,J,0,L,K),ot=new n(new Array(0),Q,0,H,P)}function b(t){var e;for(e=0;e<F;e++)t.dyn_ltree[2*e]=0;for(e=0;e<L;e++)t.dyn_dtree[2*e]=0;for(e=0;e<H;e++)t.bl_tree[2*e]=0;t.dyn_ltree[2*Y]=1,t.opt_len=t.static_len=0,t.last_lit=t.matches=0}function g(t){t.bi_valid>8?o(t,t.bi_buf):t.bi_valid>0&&(t.pending_buf[t.pending++]=t.bi_buf),t.bi_buf=0,t.bi_valid=0}function m(t,e,a,i){g(t),i&&(o(t,a),o(t,~a)),A.arraySet(t.pending_buf,t.window,e,a,t.pending),t.pending+=a}function w(t,e,a,i){var n=2*e,r=2*a;return t[n]<t[r]||t[n]===t[r]&&i[e]<=i[a]}function p(t,e,a){for(var i=t.heap[a],n=a<<1;n<=t.heap_len&&(n<t.heap_len&&w(e,t.heap[n+1],t.heap[n],t.depth)&&n++,!w(e,i,t.heap[n],t.depth));)t.heap[a]=t.heap[n],a=n,n<<=1;t.heap[a]=i}function v(t,e,a){var i,n,r,o,d=0;if(0!==t.last_lit)do{i=t.pending_buf[t.d_buf+2*d]<<8|t.pending_buf[t.d_buf+2*d+1],n=t.pending_buf[t.l_buf+d],d++,0===i?h(t,n,e):(h(t,(r=at[n])+T+1,e),0!==(o=W[r])&&l(t,n-=it[r],o),h(t,r=s(--i),a),0!==(o=J[r])&&l(t,i-=nt[r],o))}while(d<t.last_lit);h(t,Y,e)}function k(t,e){var a,i,n,r=e.dyn_tree,s=e.stat_desc.static_tree,o=e.stat_desc.has_stree,l=e.stat_desc.elems,h=-1;for(t.heap_len=0,t.heap_max=j,a=0;a<l;a++)0!==r[2*a]?(t.heap[++t.heap_len]=h=a,t.depth[a]=0):r[2*a+1]=0;for(;t.heap_len<2;)r[2*(n=t.heap[++t.heap_len]=h<2?++h:0)]=1,t.depth[n]=0,t.opt_len--,o&&(t.static_len-=s[2*n+1]);for(e.max_code=h,a=t.heap_len>>1;a>=1;a--)p(t,r,a);n=l;do{a=t.heap[1],t.heap[1]=t.heap[t.heap_len--],p(t,r,1),i=t.heap[1],t.heap[--t.heap_max]=a,t.heap[--t.heap_max]=i,r[2*n]=r[2*a]+r[2*i],t.depth[n]=(t.depth[a]>=t.depth[i]?t.depth[a]:t.depth[i])+1,r[2*a+1]=r[2*i+1]=n,t.heap[1]=n++,p(t,r,1)}while(t.heap_len>=2);t.heap[--t.heap_max]=t.heap[1],_(t,e),u(r,h,t.bl_count)}function y(t,e,a){var i,n,r=-1,s=e[1],o=0,l=7,h=4;for(0===s&&(l=138,h=3),e[2*(a+1)+1]=65535,i=0;i<=a;i++)n=s,s=e[2*(i+1)+1],++o<l&&n===s||(o<h?t.bl_tree[2*n]+=o:0!==n?(n!==r&&t.bl_tree[2*n]++,t.bl_tree[2*q]++):o<=10?t.bl_tree[2*G]++:t.bl_tree[2*X]++,o=0,r=n,0===s?(l=138,h=3):n===s?(l=6,h=3):(l=7,h=4))}function x(t,e,a){var i,n,r=-1,s=e[1],o=0,d=7,f=4;for(0===s&&(d=138,f=3),i=0;i<=a;i++)if(n=s,s=e[2*(i+1)+1],!(++o<d&&n===s)){if(o<f)do{h(t,n,t.bl_tree)}while(0!=--o);else 0!==n?(n!==r&&(h(t,n,t.bl_tree),o--),h(t,q,t.bl_tree),l(t,o-3,2)):o<=10?(h(t,G,t.bl_tree),l(t,o-3,3)):(h(t,X,t.bl_tree),l(t,o-11,7));o=0,r=n,0===s?(d=138,f=3):n===s?(d=6,f=3):(d=7,f=4)}}function z(t){var e;for(y(t,t.dyn_ltree,t.l_desc.max_code),y(t,t.dyn_dtree,t.d_desc.max_code),k(t,t.bl_desc),e=H-1;e>=3&&0===t.bl_tree[2*V[e]+1];e--);return t.opt_len+=3*(e+1)+5+5+4,e}function B(t,e,a,i){var n;for(l(t,e-257,5),l(t,a-1,5),l(t,i-4,4),n=0;n<i;n++)l(t,t.bl_tree[2*V[n]+1],3);x(t,t.dyn_ltree,e-1),x(t,t.dyn_dtree,a-1)}function S(t){var e,a=4093624447;for(e=0;e<=31;e++,a>>>=1)if(1&a&&0!==t.dyn_ltree[2*e])return R;if(0!==t.dyn_ltree[18]||0!==t.dyn_ltree[20]||0!==t.dyn_ltree[26])return C;for(e=32;e<T;e++)if(0!==t.dyn_ltree[2*e])return C;return R}function E(t,e,a,i){l(t,(O<<1)+(i?1:0),3),m(t,e,a,!0)}var A=t("../utils/common"),Z=4,R=0,C=1,N=2,O=0,D=1,I=2,U=29,T=256,F=T+1+U,L=30,H=19,j=2*F+1,K=15,M=16,P=7,Y=256,q=16,G=17,X=18,W=[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0],J=[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13],Q=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7],V=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],$=new Array(2*(F+2));i($);var tt=new Array(2*L);i(tt);var et=new Array(512);i(et);var at=new Array(256);i(at);var it=new Array(U);i(it);var nt=new Array(L);i(nt);var rt,st,ot,lt=!1;a._tr_init=function(t){lt||(c(),lt=!0),t.l_desc=new r(t.dyn_ltree,rt),t.d_desc=new r(t.dyn_dtree,st),t.bl_desc=new r(t.bl_tree,ot),t.bi_buf=0,t.bi_valid=0,b(t)},a._tr_stored_block=E,a._tr_flush_block=function(t,e,a,i){var n,r,s=0;t.level>0?(t.strm.data_type===N&&(t.strm.data_type=S(t)),k(t,t.l_desc),k(t,t.d_desc),s=z(t),n=t.opt_len+3+7>>>3,(r=t.static_len+3+7>>>3)<=n&&(n=r)):n=r=a+5,a+4<=n&&-1!==e?E(t,e,a,i):t.strategy===Z||r===n?(l(t,(D<<1)+(i?1:0),3),v(t,$,tt)):(l(t,(I<<1)+(i?1:0),3),B(t,t.l_desc.max_code+1,t.d_desc.max_code+1,s+1),v(t,t.dyn_ltree,t.dyn_dtree)),b(t),i&&g(t)},a._tr_tally=function(t,e,a){return t.pending_buf[t.d_buf+2*t.last_lit]=e>>>8&255,t.pending_buf[t.d_buf+2*t.last_lit+1]=255&e,t.pending_buf[t.l_buf+t.last_lit]=255&a,t.last_lit++,0===e?t.dyn_ltree[2*a]++:(t.matches++,e--,t.dyn_ltree[2*(at[a]+T+1)]++,t.dyn_dtree[2*s(e)]++),t.last_lit===t.lit_bufsize-1},a._tr_align=function(t){l(t,D<<1,3),h(t,Y,$),f(t)}},{"../utils/common":3}],15:[function(t,e,a){"use strict";e.exports=function(){this.input=null,this.next_in=0,this.avail_in=0,this.total_in=0,this.output=null,this.next_out=0,this.avail_out=0,this.total_out=0,this.msg="",this.state=null,this.data_type=2,this.adler=0}},{}],"/":[function(t,e,a){"use strict";var i={};(0,t("./lib/utils/common").assign)(i,t("./lib/deflate"),t("./lib/inflate"),t("./lib/zlib/constants")),e.exports=i},{"./lib/deflate":1,"./lib/inflate":2,"./lib/utils/common":3,"./lib/zlib/constants":6}]},{},[])("/")});'use strict';tr.exportTo('tr.e.importer',function(){const GZIP_MEMBER_HEADER_ID_SIZE=3;const GZIP_HEADER_ID1=0x1f;const GZIP_HEADER_ID2=0x8b;const GZIP_DEFLATE_COMPRESSION=8;function _stringToUInt8Array(str){const array=new Uint8Array(str.length);for(let i=0;i<str.length;++i){array[i]=str.charCodeAt(i);}
+return array;}
+function GzipImporter(model,eventData){this.inflateAsTraceStream=false;if(typeof(eventData)==='string'||eventData instanceof String){eventData=_stringToUInt8Array(eventData);}else if(eventData instanceof ArrayBuffer){eventData=new Uint8Array(eventData);}else if(eventData instanceof tr.b.InMemoryTraceStream){eventData=eventData.data;this.inflateAsTraceStream_=true;}else{throw new Error('Unknown gzip data format');}
+this.model_=model;this.gzipData_=eventData;}
+GzipImporter.canImport=function(eventData){if(eventData instanceof tr.b.InMemoryTraceStream){eventData=eventData.header;}
+let header;if(eventData instanceof ArrayBuffer){header=new Uint8Array(eventData.slice(0,GZIP_MEMBER_HEADER_ID_SIZE));}else if(typeof(eventData)==='string'||eventData instanceof String){header=eventData.substring(0,GZIP_MEMBER_HEADER_ID_SIZE);header=_stringToUInt8Array(header);}else{return false;}
+return header[0]===GZIP_HEADER_ID1&&header[1]===GZIP_HEADER_ID2&&header[2]===GZIP_DEFLATE_COMPRESSION;};GzipImporter.inflateGzipData_=function(data){let position=0;function getByte(){if(position>=data.length){throw new Error('Unexpected end of gzip data');}
+return data[position++];}
+function getWord(){const low=getByte();const high=getByte();return(high<<8)+low;}
+function skipBytes(amount){position+=amount;}
+function skipZeroTerminatedString(){while(getByte()!==0){}}
+const id1=getByte();const id2=getByte();if(id1!==GZIP_HEADER_ID1||id2!==GZIP_HEADER_ID2){throw new Error('Not gzip data');}
+const compressionMethod=getByte();if(compressionMethod!==GZIP_DEFLATE_COMPRESSION){throw new Error('Unsupported compression method: '+compressionMethod);}
+const flags=getByte();const haveHeaderCrc=flags&(1<<1);const haveExtraFields=flags&(1<<2);const haveFileName=flags&(1<<3);const haveComment=flags&(1<<4);skipBytes(4+1+1);if(haveExtraFields){const bytesToSkip=getWord();skipBytes(bytesToSkip);}
+if(haveFileName)skipZeroTerminatedString();if(haveComment)skipZeroTerminatedString();if(haveHeaderCrc)getWord();const inflatedData=pako.inflateRaw(data.subarray(position));if(this.inflateAsTraceStream_){return GzipImporter.transformToStream(inflatedData);}
+let string;try{string=GzipImporter.transformToString(inflatedData);}catch(err){return GzipImporter.transformToStream(inflatedData);}
+if(inflatedData.length>0&&string.length===0){return GzipImporter.transformToStream(inflatedData);}
+return string;};GzipImporter.transformToStream=function(data){if(data instanceof Uint8Array){return new tr.b.InMemoryTraceStream(data,false);}
+throw new Error(`Cannot transform ${type} to TraceStream.`);};GzipImporter.transformToString=function(data){if(typeof(data)==='string')return data;if(typeof TextDecoder==='undefined'){if(data instanceof ArrayBuffer){data=new Uint8Array(data);}
+const result=[];let chunk=65536;let k=0;const len=data.length;while(k<len&&chunk>1){try{const chunklen=Math.min(k+chunk,len);let dataslice;if(data instanceof Array){dataslice=data.slice(k,chunklen);}else{dataslice=data.subarray(k,chunklen);}
+result.push(String.fromCharCode.apply(null,dataslice));k+=chunk;}catch(e){chunk=Math.floor(chunk/2);}}
+return result.join('');}
+if(data instanceof Array){data=new Uint8Array(data);}
+return new TextDecoder('utf-8').decode(data);};GzipImporter.prototype={__proto__:tr.importer.Importer.prototype,get importerName(){return'GzipImporter';},isTraceDataContainer(){return true;},extractSubtraces(){const eventData=GzipImporter.inflateGzipData_(this.gzipData_);return eventData?[eventData]:[];}};tr.importer.Importer.register(GzipImporter);return{GzipImporter,};});'use strict';tr.exportTo('tr.importer',function(){class SimpleLineReader{constructor(text){this.data_=text instanceof tr.b.TraceStream?text:text.split(new RegExp('\r?\n'));this.curLine_=0;this.readLastLine_=false;this.savedLines_=undefined;}*[Symbol.iterator](){let lastLine=undefined;while(this.hasData_){if(this.readLastLine_){this.curLine_++;this.readLastLine_=false;}else if(this.data_ instanceof tr.b.TraceStream){this.curLine_++;const line=this.data_.readUntilDelimiter('\n');if(line.endsWith('\r\n')){lastLine=line.slice(0,-2);}else if(line.endsWith('\n')){lastLine=line.slice(0,-1);}else{lastLine=line;}}else{this.curLine_++;lastLine=this.data_[this.curLine_-1];}
+yield lastLine;}}
+get curLineNumber(){return this.curLine_;}
+get hasData_(){if(this.data_ instanceof tr.b.TraceStream)return this.data_.hasData;return this.curLine_<this.data_.length;}
+advanceToLineMatching(regex){for(const line of this){if(this.savedLines_!==undefined)this.savedLines_.push(line);if(regex.test(line)){this.goBack_();return true;}}
+return false;}
+goBack_(){if(this.readLastLine_){throw new Error('There should be at least one nextLine call between '+'any two goBack calls.');}
+if(this.curLine_===0){throw new Error('There should be at least one nextLine call before '+'the first goBack call.');}
+this.readLastLine_=true;this.curLine_--;}
+beginSavingLines(){this.savedLines_=[];}
+endSavingLinesAndGetResult(){const tmp=this.savedLines_;this.savedLines_=undefined;return tmp;}}
+return{SimpleLineReader,};});'use strict';tr.exportTo('tr.e.importer',function(){function Trace2HTMLImporter(model,events){this.importPriority=0;}
+Trace2HTMLImporter.subtraces_=[];function _extractEventsFromHTML(text){Trace2HTMLImporter.subtraces_=[];const r=new tr.importer.SimpleLineReader(text);while(true){if(!r.advanceToLineMatching(new RegExp('^<\s*script id="viewer-data" '+'type="(application\/json|text\/plain)">\r?$'))){break;}
+r.beginSavingLines();if(!r.advanceToLineMatching(/^<\/\s*script>\r?$/))return;let rawEvents=r.endSavingLinesAndGetResult();rawEvents=rawEvents.slice(1,rawEvents.length-1);const data64=rawEvents.join('\n');const buffer=new ArrayBuffer(tr.b.Base64.getDecodedBufferLength(data64));const len=tr.b.Base64.DecodeToTypedArray(data64,new DataView(buffer));Trace2HTMLImporter.subtraces_.push(buffer.slice(0,len));}}
+function _canImportFromHTML(text){if(!/^<!DOCTYPE html>/.test(text))return false;_extractEventsFromHTML(text);if(Trace2HTMLImporter.subtraces_.length===0)return false;return true;}
+Trace2HTMLImporter.canImport=function(events){if(events instanceof tr.b.TraceStream)return false;return _canImportFromHTML(events);};Trace2HTMLImporter.prototype={__proto__:tr.importer.Importer.prototype,get importerName(){return'Trace2HTMLImporter';},isTraceDataContainer(){return true;},extractSubtraces(){return Trace2HTMLImporter.subtraces_;},importEvents(){}};tr.importer.Importer.register(Trace2HTMLImporter);return{Trace2HTMLImporter,};});'use strict';tr.exportTo('tr.e.importer.v8',function(){function SplayTree(){}
+SplayTree.prototype.root_=null;SplayTree.prototype.isEmpty=function(){return!this.root_;};SplayTree.prototype.insert=function(key,value){if(this.isEmpty()){this.root_=new SplayTree.Node(key,value);return;}
+this.splay_(key);if(this.root_.key===key){return;}
+const node=new SplayTree.Node(key,value);if(key>this.root_.key){node.left=this.root_;node.right=this.root_.right;this.root_.right=null;}else{node.right=this.root_;node.left=this.root_.left;this.root_.left=null;}
+this.root_=node;};SplayTree.prototype.remove=function(key){if(this.isEmpty()){throw Error('Key not found: '+key);}
+this.splay_(key);if(this.root_.key!==key){throw Error('Key not found: '+key);}
+const removed=this.root_;if(!this.root_.left){this.root_=this.root_.right;}else{const right=this.root_.right;this.root_=this.root_.left;this.splay_(key);this.root_.right=right;}
+return removed;};SplayTree.prototype.find=function(key){if(this.isEmpty())return null;this.splay_(key);return this.root_.key===key?this.root_:null;};SplayTree.prototype.findMin=function(){if(this.isEmpty())return null;let current=this.root_;while(current.left){current=current.left;}
+return current;};SplayTree.prototype.findMax=function(opt_startNode){if(this.isEmpty())return null;let current=opt_startNode||this.root_;while(current.right){current=current.right;}
+return current;};SplayTree.prototype.findGreatestLessThan=function(key){if(this.isEmpty())return null;this.splay_(key);if(this.root_.key<=key){return this.root_;}
+if(this.root_.left){return this.findMax(this.root_.left);}
+return null;};SplayTree.prototype.exportKeysAndValues=function(){const result=[];this.traverse_(function(node){result.push([node.key,node.value]);});return result;};SplayTree.prototype.exportValues=function(){const result=[];this.traverse_(function(node){result.push(node.value);});return result;};SplayTree.prototype.splay_=function(key){if(this.isEmpty())return;const dummy=new SplayTree.Node(null,null);let left=dummy;let right=dummy;let current=this.root_;while(true){if(key<current.key){if(!current.left){break;}
+if(key<current.left.key){const tmp=current.left;current.left=tmp.right;tmp.right=current;current=tmp;if(!current.left){break;}}
+right.left=current;right=current;current=current.left;}else if(key>current.key){if(!current.right){break;}
+if(key>current.right.key){const tmp=current.right;current.right=tmp.left;tmp.left=current;current=tmp;if(!current.right){break;}}
+left.right=current;left=current;current=current.right;}else{break;}}
+left.right=current.left;right.left=current.right;current.left=dummy.right;current.right=dummy.left;this.root_=current;};SplayTree.prototype.traverse_=function(f){const nodesToVisit=[this.root_];while(nodesToVisit.length>0){const node=nodesToVisit.shift();if(node===null)continue;f(node);nodesToVisit.push(node.left);nodesToVisit.push(node.right);}};SplayTree.Node=function(key,value){this.key=key;this.value=value;};SplayTree.Node.prototype.left=null;SplayTree.Node.prototype.right=null;return{SplayTree,};});'use strict';tr.exportTo('tr.e.importer.v8',function(){function CodeMap(){this.dynamics_=new tr.e.importer.v8.SplayTree();this.dynamicsNameGen_=new tr.e.importer.v8.CodeMap.NameGenerator();this.statics_=new tr.e.importer.v8.SplayTree();this.libraries_=new tr.e.importer.v8.SplayTree();this.pages_=[];}
+CodeMap.PAGE_ALIGNMENT=12;CodeMap.PAGE_SIZE=1<<CodeMap.PAGE_ALIGNMENT;CodeMap.prototype.addCode=function(start,codeEntry){this.deleteAllCoveredNodes_(this.dynamics_,start,start+codeEntry.size);this.dynamics_.insert(start,codeEntry);};CodeMap.prototype.moveCode=function(from,to){const removedNode=this.dynamics_.remove(from);this.deleteAllCoveredNodes_(this.dynamics_,to,to+removedNode.value.size);this.dynamics_.insert(to,removedNode.value);};CodeMap.prototype.deleteCode=function(start){const removedNode=this.dynamics_.remove(start);};CodeMap.prototype.addLibrary=function(start,codeEntry){this.markPages_(start,start+codeEntry.size);this.libraries_.insert(start,codeEntry);};CodeMap.prototype.addStaticCode=function(start,codeEntry){this.statics_.insert(start,codeEntry);};CodeMap.prototype.markPages_=function(start,end){for(let addr=start;addr<=end;addr+=CodeMap.PAGE_SIZE){this.pages_[addr>>>CodeMap.PAGE_ALIGNMENT]=1;}};CodeMap.prototype.deleteAllCoveredNodes_=function(tree,start,end){const toDelete=[];let addr=end-1;while(addr>=start){const node=tree.findGreatestLessThan(addr);if(!node)break;const start2=node.key;const end2=start2+node.value.size;if(start2<end&&start<end2)toDelete.push(start2);addr=start2-1;}
+for(let i=0,l=toDelete.length;i<l;++i)tree.remove(toDelete[i]);};CodeMap.prototype.isAddressBelongsTo_=function(addr,node){return addr>=node.key&&addr<(node.key+node.value.size);};CodeMap.prototype.findInTree_=function(tree,addr){const node=tree.findGreatestLessThan(addr);return node&&this.isAddressBelongsTo_(addr,node)?node.value:null;};CodeMap.prototype.findEntryInLibraries=function(addr){const pageAddr=addr>>>CodeMap.PAGE_ALIGNMENT;if(pageAddr in this.pages_){return this.findInTree_(this.libraries_,addr);}
+return undefined;};CodeMap.prototype.findEntry=function(addr){const pageAddr=addr>>>CodeMap.PAGE_ALIGNMENT;if(pageAddr in this.pages_){return this.findInTree_(this.statics_,addr)||this.findInTree_(this.libraries_,addr);}
+const min=this.dynamics_.findMin();const max=this.dynamics_.findMax();if(max!==null&&addr<(max.key+max.value.size)&&addr>=min.key){const dynaEntry=this.findInTree_(this.dynamics_,addr);if(dynaEntry===null)return null;if(!dynaEntry.nameUpdated_){dynaEntry.name=this.dynamicsNameGen_.getName(dynaEntry.name);dynaEntry.nameUpdated_=true;}
+return dynaEntry;}
+return null;};CodeMap.prototype.findDynamicEntryByStartAddress=function(addr){const node=this.dynamics_.find(addr);return node?node.value:null;};CodeMap.prototype.getAllDynamicEntries=function(){return this.dynamics_.exportValues();};CodeMap.prototype.getAllDynamicEntriesWithAddresses=function(){return this.dynamics_.exportKeysAndValues();};CodeMap.prototype.getAllStaticEntries=function(){return this.statics_.exportValues();};CodeMap.prototype.getAllLibrariesEntries=function(){return this.libraries_.exportValues();};CodeMap.CodeState={COMPILED:0,OPTIMIZABLE:1,OPTIMIZED:2};CodeMap.CodeEntry=function(size,opt_name,opt_type){this.id=tr.b.GUID.allocateSimple();this.size=size;this.name_=opt_name||'';this.type=opt_type||'';this.nameUpdated_=false;};CodeMap.CodeEntry.prototype={__proto__:Object.prototype,get name(){return this.name_;},set name(value){this.name_=value;},toString(){this.name_+': '+this.size.toString(16);}};CodeMap.CodeEntry.TYPE={SHARED_LIB:'SHARED_LIB',CPP:'CPP'};CodeMap.DynamicFuncCodeEntry=function(size,type,func,state){CodeMap.CodeEntry.call(this,size,'',type);this.func=func;this.state=state;};CodeMap.DynamicFuncCodeEntry.STATE_PREFIX=['','~','*'];CodeMap.DynamicFuncCodeEntry.prototype={__proto__:CodeMap.CodeEntry.prototype,get name(){return CodeMap.DynamicFuncCodeEntry.STATE_PREFIX[this.state]+
+this.func.name;},set name(value){this.name_=value;},getRawName(){return this.func.getName();},isJSFunction(){return true;},toString(){return this.type+': '+this.name+': '+this.size.toString(16);}};CodeMap.FunctionEntry=function(name){CodeMap.CodeEntry.call(this,0,name);};CodeMap.FunctionEntry.prototype={__proto__:CodeMap.CodeEntry.prototype,get name(){let name=this.name_;if(name.length===0){name='<anonymous>';}else if(name.charAt(0)===' '){name='<anonymous>'+name;}
+return name;},set name(value){this.name_=value;}};CodeMap.NameGenerator=function(){this.knownNames_={};};CodeMap.NameGenerator.prototype.getName=function(name){if(!(name in this.knownNames_)){this.knownNames_[name]=0;return name;}
+const count=++this.knownNames_[name];return name+' {'+count+'}';};return{CodeMap,};});'use strict';tr.exportTo('tr.e.importer.v8',function(){function CsvParser(){}
+CsvParser.CSV_FIELD_RE_=/^"((?:[^"]|"")*)"|([^,]*)/;CsvParser.DOUBLE_QUOTE_RE_=/""/g;CsvParser.prototype.parseLine=function(line){const fieldRe=CsvParser.CSV_FIELD_RE_;const doubleQuoteRe=CsvParser.DOUBLE_QUOTE_RE_;let pos=0;const endPos=line.length;const fields=[];if(endPos>0){do{const fieldMatch=fieldRe.exec(line.substr(pos));if(typeof fieldMatch[1]==='string'){const field=fieldMatch[1];pos+=field.length+3;fields.push(field.replace(doubleQuoteRe,'"'));}else{const field=fieldMatch[2];pos+=field.length+1;fields.push(field);}}while(pos<=endPos);}
+return fields;};function LogReader(dispatchTable){this.dispatchTable_=dispatchTable;this.lineNum_=0;this.csvParser_=new CsvParser();}
+LogReader.prototype.printError=function(str){};LogReader.prototype.processLogChunk=function(chunk){this.processLog_(chunk.split('\n'));};LogReader.prototype.processLogLine=function(line){this.processLog_([line]);};LogReader.prototype.processStack=function(pc,func,stack){const fullStack=func?[pc,func]:[pc];let prevFrame=pc;for(let i=0,n=stack.length;i<n;++i){const frame=stack[i];const firstChar=frame.charAt(0);if(firstChar==='+'||firstChar==='-'){prevFrame+=parseInt(frame,16);fullStack.push(prevFrame);}else if(firstChar!=='o'){fullStack.push(parseInt(frame,16));}}
+return fullStack;};LogReader.prototype.skipDispatch=function(dispatch){return false;};LogReader.prototype.dispatchLogRow_=function(fields){const command=fields[0];if(!(command in this.dispatchTable_))return;const dispatch=this.dispatchTable_[command];if(dispatch===null||this.skipDispatch(dispatch)){return;}
+const parsedFields=[];for(let i=0;i<dispatch.parsers.length;++i){const parser=dispatch.parsers[i];if(parser===null){parsedFields.push(fields[1+i]);}else if(typeof parser==='function'){parsedFields.push(parser(fields[1+i]));}else{parsedFields.push(fields.slice(1+i));break;}}
+dispatch.processor.apply(this,parsedFields);};LogReader.prototype.processLog_=function(lines){for(let i=0,n=lines.length;i<n;++i,++this.lineNum_){const line=lines[i];if(!line){continue;}
+try{const fields=this.csvParser_.parseLine(line);this.dispatchLogRow_(fields);}catch(e){this.printError('line '+(this.lineNum_+1)+': '+
+(e.message||e));}}};return{LogReader,};});'use strict';tr.exportTo('tr.model',function(){function ProfileNode(id,title,parentNode){this.id_=id;this.title_=title;this.parentNode_=parentNode;this.colorId_=-1;this.userFriendlyStack_=[];}
+ProfileNode.prototype={__proto__:Object.prototype,get title(){return this.title_;},get parentNode(){return this.parentNode_;},set parentNode(value){this.parentNode_=value;},get id(){return this.id_;},get colorId(){return this.colorId_;},set colorId(value){this.colorId_=value;},get userFriendlyName(){return this.title_;},get userFriendlyStack(){if(this.userFriendlyStack_.length===0){this.userFriendlyStack_=[this.userFriendlyName];if(this.parentNode_!==undefined){this.userFriendlyStack_=this.userFriendlyStack_.concat(this.parentNode_.userFriendlyStack);}}
+return this.userFriendlyStack_;},get sampleTitle(){throw new Error('Not implemented.');}};tr.model.EventRegistry.register(ProfileNode,{name:'Node',pluralName:'Nodes'});return{ProfileNode,};});'use strict';tr.exportTo('tr.e.v8',function(){const ProfileNode=tr.model.ProfileNode;function V8CpuProfileNode(id,callFrame,parentNode){ProfileNode.call(this,id,callFrame.functionName,parentNode);this.callFrame_=tr.b.deepCopy(callFrame);this.deoptReason_='';this.colorId_=tr.b.ColorScheme.getColorIdForGeneralPurposeString(callFrame.functionName);}
+V8CpuProfileNode.prototype={__proto__:ProfileNode.prototype,get functionName(){return this.callFrame_.functionName;},get scriptId(){return this.callFrame_.scriptId;},get url(){if(!this.callFrame_.url){return'unknown';}
+let url=this.callFrame_.url;if(this.callFrame_.lineNumber===undefined){return url;}
+url=url+':'+this.callFrame_.lineNumber;if(this.callFrame_.columnNumber===undefined){return url;}
+url=url+':'+this.callFrame_.columnNumber;return url;},get deoptReason(){return this.deoptReason_;},set deoptReason(value){this.deoptReason_=value;},get userFriendlyName(){const name=this.functionName+' url: '+this.url;return!this.deoptReason_?name:name+' Deoptimized reason: '+this.deoptReason_;},get sampleTitle(){return'V8 Sample';}};V8CpuProfileNode.constructFromObject=function(profileTree,node){const nodeId=node.id;if(nodeId===1){return undefined;}
+const parentNode=profileTree.getNode(node.parent);const profileNode=new V8CpuProfileNode(nodeId,node.callFrame,parentNode);if(node.deoptReason!==undefined){profileNode.deoptReason=node.deoptReason;}
+return profileNode;};ProfileNode.subTypes.register(V8CpuProfileNode,{typeName:'cpuProfile',name:'v8 cpu profile node',pluralName:'v8 cpu profile nodes'});ProfileNode.subTypes.register(V8CpuProfileNode,{typeName:'legacySample',name:'v8 cpu profile node',pluralName:'v8 cpu profile nodes'});return{ProfileNode,};});'use strict';tr.exportTo('tr.model',function(){function ProfileTree(){this.startTime_=undefined;this.endTime_=undefined;this.tree_=new Map();this.pid_=-1;this.tid_=-1;}
+ProfileTree.prototype={__proto__:Object.prototype,get pid(){return this.pid_;},set pid(value){this.pid_=value;},get tid(){return this.tid_;},set tid(value){this.tid_=value;},get tree(){return this.tree_;},get startTime(){return this.startTime_;},set startTime(value){this.startTime_=value;this.endTime_=value;},get endTime(){return this.endTime_;},set endTime(value){this.endTime_=value;},add(node){if(this.tree_.has(node.id)){throw new Error('Conflict id in the profile tree.');}
+this.tree_.set(node.id,node);return node;},getNode(nodeId){return this.tree_.get(nodeId);}};return{ProfileTree,};});'use strict';tr.exportTo('tr.e.importer.v8',function(){const CodeEntry=tr.e.importer.v8.CodeMap.CodeEntry;const CodeMap=tr.e.importer.v8.CodeMap;const ColorScheme=tr.b.ColorScheme;const DynamicFuncCodeEntry=tr.e.importer.v8.CodeMap.DynamicFuncCodeEntry;const FunctionEntry=tr.e.importer.v8.CodeMap.FunctionEntry;const ProfileNodeType=tr.model.ProfileNode.subTypes.getConstructor(undefined,'legacySample');function V8LogImporter(model,eventData){this.importPriority=3;this.model_=model;this.logData_=eventData;this.code_map_=new CodeMap();this.v8_timer_thread_=undefined;this.v8_thread_=undefined;this.profileTree_=new tr.model.ProfileTree();this.profileTree_.add(new ProfileNodeType(-1,{url:'',functionName:'unknown'}));this.v8_stack_timeline_=[];}
+const kV8BinarySuffixes=['/d8','/libv8.so'];const TimerEventDefaultArgs={'V8.Execute':{pause:false,no_execution:false},'V8.External':{pause:false,no_execution:true},'V8.CompileFullCode':{pause:true,no_execution:true},'V8.RecompileSynchronous':{pause:true,no_execution:true},'V8.RecompileParallel':{pause:false,no_execution:false},'V8.CompileEval':{pause:true,no_execution:true},'V8.Parse':{pause:true,no_execution:true},'V8.PreParse':{pause:true,no_execution:true},'V8.ParseLazy':{pause:true,no_execution:true},'V8.GCScavenger':{pause:true,no_execution:true},'V8.GCCompactor':{pause:true,no_execution:true},'V8.GCContext':{pause:true,no_execution:true}};V8LogImporter.canImport=function(eventData){if(typeof(eventData)!=='string'&&!(eventData instanceof String)){return false;}
+return eventData.substring(0,11)==='v8-version,'||eventData.substring(0,12)==='timer-event,'||eventData.substring(0,5)==='tick,'||eventData.substring(0,15)==='shared-library,'||eventData.substring(0,9)==='profiler,'||eventData.substring(0,14)==='code-creation,';};V8LogImporter.prototype={__proto__:tr.importer.Importer.prototype,get importerName(){return'V8LogImporter';},processTimerEvent_(name,startInUs,lengthInUs){const args=TimerEventDefaultArgs[name];if(args===undefined)return;const startInMs=tr.b.convertUnit(startInUs,tr.b.UnitPrefixScale.METRIC.MICRO,tr.b.UnitPrefixScale.METRIC.MILLI);const lengthInMs=tr.b.convertUnit(lengthInUs,tr.b.UnitPrefixScale.METRIC.MICRO,tr.b.UnitPrefixScale.METRIC.MILLI);const colorId=ColorScheme.getColorIdForGeneralPurposeString(name);const slice=new tr.model.ThreadSlice('v8',name,colorId,startInMs,args,lengthInMs);this.v8_timer_thread_.sliceGroup.pushSlice(slice);},processTimerEventStart_(name,startInUs){const args=TimerEventDefaultArgs[name];if(args===undefined)return;const startInMs=tr.b.convertUnit(startInUs,tr.b.UnitPrefixScale.METRIC.MICRO,tr.b.UnitPrefixScale.METRIC.MILLI);this.v8_timer_thread_.sliceGroup.beginSlice('v8',name,startInMs,args);},processTimerEventEnd_(name,endInUs){const endInMs=tr.b.convertUnit(endInUs,tr.b.UnitPrefixScale.METRIC.MICRO,tr.b.UnitPrefixScale.METRIC.MILLI);this.v8_timer_thread_.sliceGroup.endSlice(endInMs);},processCodeCreateEvent_(type,kind,address,size,name,maybeFunc){function parseState(s){switch(s){case'':return CodeMap.CodeState.COMPILED;case'~':return CodeMap.CodeState.OPTIMIZABLE;case'*':return CodeMap.CodeState.OPTIMIZED;}
+throw new Error('unknown code state: '+s);}
+if(maybeFunc.length){const funcAddr=parseInt(maybeFunc[0]);const state=parseState(maybeFunc[1]);let func=this.code_map_.findDynamicEntryByStartAddress(funcAddr);if(!func){func=new FunctionEntry(name);func.kind=kind;this.code_map_.addCode(funcAddr,func);}else if(func.name!==name){func.name=name;}
+let entry=this.code_map_.findDynamicEntryByStartAddress(address);if(entry){if(entry.size===size&&entry.func===func){entry.state=state;}}else{entry=new DynamicFuncCodeEntry(size,type,func,state);entry.kind=kind;this.code_map_.addCode(address,entry);}}else{const codeEntry=new CodeEntry(size,name);codeEntry.kind=kind;this.code_map_.addCode(address,codeEntry);}},processCodeMoveEvent_(from,to){this.code_map_.moveCode(from,to);},processCodeDeleteEvent_(address){this.code_map_.deleteCode(address);},processSharedLibrary_(name,start,end){const codeEntry=new CodeEntry(end-start,name,CodeEntry.TYPE.SHARED_LIB);codeEntry.kind=-3;for(let i=0;i<kV8BinarySuffixes.length;i++){const suffix=kV8BinarySuffixes[i];if(name.indexOf(suffix,name.length-suffix.length)>=0){codeEntry.kind=-1;break;}}
+this.code_map_.addLibrary(start,codeEntry);},processCppSymbol_(address,size,name){const codeEntry=new CodeEntry(size,name,CodeEntry.TYPE.CPP);codeEntry.kind=-1;this.code_map_.addStaticCode(address,codeEntry);},processTickEvent_(pc,startInUs,isExternalCallback,tosOrExternalCallback,vmstate,stack){const startInMs=tr.b.convertUnit(startInUs,tr.b.UnitPrefixScale.METRIC.MICRO,tr.b.UnitPrefixScale.METRIC.MILLI);function findChildWithEntryID(stackFrame,entryID){for(let i=0;i<stackFrame.children.length;i++){if(stackFrame.children[i].entryID===entryID){return stackFrame.children[i];}}
+return undefined;}
+function processStack(pc,func,stack){const fullStack=func?[pc,func]:[pc];let prevFrame=pc;for(let i=0,n=stack.length;i<n;++i){const frame=stack[i];const firstChar=frame.charAt(0);if(firstChar==='+'||firstChar==='-'){prevFrame+=parseInt(frame,16);fullStack.push(prevFrame);}else if(firstChar!=='o'){fullStack.push(parseInt(frame,16));}}
+return fullStack;}
+if(isExternalCallback){pc=tosOrExternalCallback;tosOrExternalCallback=0;}else if(tosOrExternalCallback){const funcEntry=this.code_map_.findEntry(tosOrExternalCallback);if(!funcEntry||!funcEntry.isJSFunction||!funcEntry.isJSFunction()){tosOrExternalCallback=0;}}
+let processedStack=processStack(pc,tosOrExternalCallback,stack);let node=undefined;let lastNode=undefined;processedStack=processedStack.reverse();for(let i=0,n=processedStack.length;i<n;i++){const frame=processedStack[i];if(!frame)break;const entry=this.code_map_.findEntry(frame);if(!entry&&i!==0){continue;}
+let sourceInfo=undefined;if(entry&&entry.type===CodeEntry.TYPE.CPP){const libEntry=this.code_map_.findEntryInLibraries(frame);if(libEntry){sourceInfo={file:libEntry.name};}}
+const entryId=entry?entry.id:-1;node=this.profileTree_.getNode(entryId);if(node===undefined){node=this.profileTree_.add(new ProfileNodeType(entryId,{functionName:entry.name,url:sourceInfo?sourceInfo.file:'',lineNumber:sourceInfo?sourceInfo.line:undefined,columnNumber:sourceInfo?sourceInfo.column:undefined,scriptId:sourceInfo?sourceInfo.scriptId:undefined},lastNode));}
+lastNode=node;}
+this.model_.samples.push(new tr.model.Sample(startInMs,'V8 PC',node,this.v8_thread_,undefined,1));},processDistortion_(distortionInPicoseconds){},processPlotRange_(start,end){},processV8Version_(major,minor,build,patch,candidate){},importEvents(){const logreader=new tr.e.importer.v8.LogReader({'timer-event':{parsers:[null,parseInt,parseInt],processor:this.processTimerEvent_.bind(this)},'shared-library':{parsers:[null,parseInt,parseInt],processor:this.processSharedLibrary_.bind(this)},'timer-event-start':{parsers:[null,parseInt],processor:this.processTimerEventStart_.bind(this)},'timer-event-end':{parsers:[null,parseInt],processor:this.processTimerEventEnd_.bind(this)},'code-creation':{parsers:[null,parseInt,parseInt,parseInt,null,'var-args'],processor:this.processCodeCreateEvent_.bind(this)},'code-move':{parsers:[parseInt,parseInt],processor:this.processCodeMoveEvent_.bind(this)},'code-delete':{parsers:[parseInt],processor:this.processCodeDeleteEvent_.bind(this)},'cpp':{parsers:[parseInt,parseInt,null],processor:this.processCppSymbol_.bind(this)},'tick':{parsers:[parseInt,parseInt,parseInt,parseInt,parseInt,'var-args'],processor:this.processTickEvent_.bind(this)},'distortion':{parsers:[parseInt],processor:this.processDistortion_.bind(this)},'plot-range':{parsers:[parseInt,parseInt],processor:this.processPlotRange_.bind(this)},'v8-version':{parsers:[parseInt,parseInt,parseInt,parseInt,parseInt],processor:this.processV8Version_.bind(this)}});this.v8_timer_thread_=this.model_.getOrCreateProcess(-32).getOrCreateThread(1);this.v8_timer_thread_.name='V8 Timers';this.v8_thread_=this.model_.getOrCreateProcess(-32).getOrCreateThread(2);this.v8_thread_.name='V8';const lines=this.logData_.split('\n');for(let i=0;i<lines.length;i++){logreader.processLogLine(lines[i]);}
+function addSlices(slices,thread){for(let i=0;i<slices.length;i++){const duration=slices[i].end-slices[i].start;const slice=new tr.model.ThreadSlice('v8',slices[i].name,ColorScheme.getColorIdForGeneralPurposeString(slices[i].name),slices[i].start,{},duration);thread.sliceGroup.pushSlice(slice);addSlices(slices[i].children,thread);}}
+addSlices(this.v8_stack_timeline_,this.v8_thread_);}};tr.importer.Importer.register(V8LogImporter);return{V8LogImporter,};});'use strict';if(tr.isVinn){global.window={};}
+!function(a){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=a();else if("function"==typeof define&&define.amd)define([],a);else{var b;"undefined"!=typeof window?b=window:"undefined"!=typeof global?b=global:"undefined"!=typeof self&&(b=self),b.JSZip=a()}}(function(){return function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);throw new Error("Cannot find module '"+g+"'")}var j=c[g]={exports:{}};b[g][0].call(j.exports,function(a){var c=b[g][1][a];return e(c?c:a)},j,j.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;g<d.length;g++)e(d[g]);return e}({1:[function(a,b,c){"use strict";var d="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";c.encode=function(a){for(var b,c,e,f,g,h,i,j="",k=0;k<a.length;)b=a.charCodeAt(k++),c=a.charCodeAt(k++),e=a.charCodeAt(k++),f=b>>2,g=(3&b)<<4|c>>4,h=(15&c)<<2|e>>6,i=63&e,isNaN(c)?h=i=64:isNaN(e)&&(i=64),j=j+d.charAt(f)+d.charAt(g)+d.charAt(h)+d.charAt(i);return j},c.decode=function(a){var b,c,e,f,g,h,i,j="",k=0;for(a=a.replace(/[^A-Za-z0-9\+\/\=]/g,"");k<a.length;)f=d.indexOf(a.charAt(k++)),g=d.indexOf(a.charAt(k++)),h=d.indexOf(a.charAt(k++)),i=d.indexOf(a.charAt(k++)),b=f<<2|g>>4,c=(15&g)<<4|h>>2,e=(3&h)<<6|i,j+=String.fromCharCode(b),64!=h&&(j+=String.fromCharCode(c)),64!=i&&(j+=String.fromCharCode(e));return j}},{}],2:[function(a,b){"use strict";function c(){this.compressedSize=0,this.uncompressedSize=0,this.crc32=0,this.compressionMethod=null,this.compressedContent=null}c.prototype={getContent:function(){return null},getCompressedContent:function(){return null}},b.exports=c},{}],3:[function(a,b,c){"use strict";c.STORE={magic:"\x00\x00",compress:function(a){return a},uncompress:function(a){return a},compressInputType:null,uncompressInputType:null},c.DEFLATE=a("./flate")},{"./flate":8}],4:[function(a,b){"use strict";var c=a("./utils"),d=[0,1996959894,3993919788,2567524794,124634137,1886057615,3915621685,2657392035,249268274,2044508324,3772115230,2547177864,162941995,2125561021,3887607047,2428444049,498536548,1789927666,4089016648,2227061214,450548861,1843258603,4107580753,2211677639,325883990,1684777152,4251122042,2321926636,335633487,1661365465,4195302755,2366115317,997073096,1281953886,3579855332,2724688242,1006888145,1258607687,3524101629,2768942443,901097722,1119000684,3686517206,2898065728,853044451,1172266101,3705015759,2882616665,651767980,1373503546,3369554304,3218104598,565507253,1454621731,3485111705,3099436303,671266974,1594198024,3322730930,2970347812,795835527,1483230225,3244367275,3060149565,1994146192,31158534,2563907772,4023717930,1907459465,112637215,2680153253,3904427059,2013776290,251722036,2517215374,3775830040,2137656763,141376813,2439277719,3865271297,1802195444,476864866,2238001368,4066508878,1812370925,453092731,2181625025,4111451223,1706088902,314042704,2344532202,4240017532,1658658271,366619977,2362670323,4224994405,1303535960,984961486,2747007092,3569037538,1256170817,1037604311,2765210733,3554079995,1131014506,879679996,2909243462,3663771856,1141124467,855842277,2852801631,3708648649,1342533948,654459306,3188396048,3373015174,1466479909,544179635,3110523913,3462522015,1591671054,702138776,2966460450,3352799412,1504918807,783551873,3082640443,3233442989,3988292384,2596254646,62317068,1957810842,3939845945,2647816111,81470997,1943803523,3814918930,2489596804,225274430,2053790376,3826175755,2466906013,167816743,2097651377,4027552580,2265490386,503444072,1762050814,4150417245,2154129355,426522225,1852507879,4275313526,2312317920,282753626,1742555852,4189708143,2394877945,397917763,1622183637,3604390888,2714866558,953729732,1340076626,3518719985,2797360999,1068828381,1219638859,3624741850,2936675148,906185462,1090812512,3747672003,2825379669,829329135,1181335161,3412177804,3160834842,628085408,1382605366,3423369109,3138078467,570562233,1426400815,3317316542,2998733608,733239954,1555261956,3268935591,3050360625,752459403,1541320221,2607071920,3965973030,1969922972,40735498,2617837225,3943577151,1913087877,83908371,2512341634,3803740692,2075208622,213261112,2463272603,3855990285,2094854071,198958881,2262029012,4057260610,1759359992,534414190,2176718541,4139329115,1873836001,414664567,2282248934,4279200368,1711684554,285281116,2405801727,4167216745,1634467795,376229701,2685067896,3608007406,1308918612,956543938,2808555105,3495958263,1231636301,1047427035,2932959818,3654703836,1088359270,936918e3,2847714899,3736837829,1202900863,817233897,3183342108,3401237130,1404277552,615818150,3134207493,3453421203,1423857449,601450431,3009837614,3294710456,1567103746,711928724,3020668471,3272380065,1510334235,755167117];b.exports=function(a,b){if("undefined"==typeof a||!a.length)return 0;var e="string"!==c.getTypeOf(a);"undefined"==typeof b&&(b=0);var f=0,g=0,h=0;b=-1^b;for(var i=0,j=a.length;j>i;i++)h=e?a[i]:a.charCodeAt(i),g=255&(b^h),f=d[g],b=b>>>8^f;return-1^b}},{"./utils":21}],5:[function(a,b){"use strict";function c(){this.data=null,this.length=0,this.index=0}var d=a("./utils");c.prototype={checkOffset:function(a){this.checkIndex(this.index+a)},checkIndex:function(a){if(this.length<a||0>a)throw new Error("End of data reached (data length = "+this.length+", asked index = "+a+"). Corrupted zip ?")},setIndex:function(a){this.checkIndex(a),this.index=a},skip:function(a){this.setIndex(this.index+a)},byteAt:function(){},readInt:function(a){var b,c=0;for(this.checkOffset(a),b=this.index+a-1;b>=this.index;b--)c=(c<<8)+this.byteAt(b);return this.index+=a,c},readString:function(a){return d.transformTo("string",this.readData(a))},readData:function(){},lastIndexOfSignature:function(){},readDate:function(){var a=this.readInt(4);return new Date((a>>25&127)+1980,(a>>21&15)-1,a>>16&31,a>>11&31,a>>5&63,(31&a)<<1)}},b.exports=c},{"./utils":21}],6:[function(a,b,c){"use strict";c.base64=!1,c.binary=!1,c.dir=!1,c.createFolders=!1,c.date=null,c.compression=null,c.comment=null},{}],7:[function(a,b,c){"use strict";var d=a("./utils");c.string2binary=function(a){return d.string2binary(a)},c.string2Uint8Array=function(a){return d.transformTo("uint8array",a)},c.uint8Array2String=function(a){return d.transformTo("string",a)},c.string2Blob=function(a){var b=d.transformTo("arraybuffer",a);return d.arrayBuffer2Blob(b)},c.arrayBuffer2Blob=function(a){return d.arrayBuffer2Blob(a)},c.transformTo=function(a,b){return d.transformTo(a,b)},c.getTypeOf=function(a){return d.getTypeOf(a)},c.checkSupport=function(a){return d.checkSupport(a)},c.MAX_VALUE_16BITS=d.MAX_VALUE_16BITS,c.MAX_VALUE_32BITS=d.MAX_VALUE_32BITS,c.pretty=function(a){return d.pretty(a)},c.findCompression=function(a){return d.findCompression(a)},c.isRegExp=function(a){return d.isRegExp(a)}},{"./utils":21}],8:[function(a,b,c){"use strict";var d="undefined"!=typeof Uint8Array&&"undefined"!=typeof Uint16Array&&"undefined"!=typeof Uint32Array,e=a("pako");c.uncompressInputType=d?"uint8array":"array",c.compressInputType=d?"uint8array":"array",c.magic="\b\x00",c.compress=function(a){return e.deflateRaw(a)},c.uncompress=function(a){return e.inflateRaw(a)}},{pako:24}],9:[function(a,b){"use strict";function c(a,b){return this instanceof c?(this.files={},this.comment=null,this.root="",a&&this.load(a,b),void(this.clone=function(){var a=new c;for(var b in this)"function"!=typeof this[b]&&(a[b]=this[b]);return a})):new c(a,b)}var d=a("./base64");c.prototype=a("./object"),c.prototype.load=a("./load"),c.support=a("./support"),c.defaults=a("./defaults"),c.utils=a("./deprecatedPublicUtils"),c.base64={encode:function(a){return d.encode(a)},decode:function(a){return d.decode(a)}},c.compressions=a("./compressions"),b.exports=c},{"./base64":1,"./compressions":3,"./defaults":6,"./deprecatedPublicUtils":7,"./load":10,"./object":13,"./support":17}],10:[function(a,b){"use strict";var c=a("./base64"),d=a("./zipEntries");b.exports=function(a,b){var e,f,g,h;for(b=b||{},b.base64&&(a=c.decode(a)),f=new d(a,b),e=f.files,g=0;g<e.length;g++)h=e[g],this.file(h.fileName,h.decompressed,{binary:!0,optimizedBinaryString:!0,date:h.date,dir:h.dir,comment:h.fileComment.length?h.fileComment:null,createFolders:b.createFolders});return f.zipComment.length&&(this.comment=f.zipComment),this}},{"./base64":1,"./zipEntries":22}],11:[function(a,b){(function(a){"use strict";b.exports=function(b,c){return new a(b,c)},b.exports.test=function(b){return a.isBuffer(b)}}).call(this,"undefined"!=typeof Buffer?Buffer:void 0)},{}],12:[function(a,b){"use strict";function c(a){this.data=a,this.length=this.data.length,this.index=0}var d=a("./uint8ArrayReader");c.prototype=new d,c.prototype.readData=function(a){this.checkOffset(a);var b=this.data.slice(this.index,this.index+a);return this.index+=a,b},b.exports=c},{"./uint8ArrayReader":18}],13:[function(a,b){"use strict";var c=a("./support"),d=a("./utils"),e=a("./crc32"),f=a("./signature"),g=a("./defaults"),h=a("./base64"),i=a("./compressions"),j=a("./compressedObject"),k=a("./nodeBuffer"),l=a("./utf8"),m=a("./stringWriter"),n=a("./uint8ArrayWriter"),o=function(a){if(a._data instanceof j&&(a._data=a._data.getContent(),a.options.binary=!0,a.options.base64=!1,"uint8array"===d.getTypeOf(a._data))){var b=a._data;a._data=new Uint8Array(b.length),0!==b.length&&a._data.set(b,0)}return a._data},p=function(a){var b=o(a),e=d.getTypeOf(b);return"string"===e?!a.options.binary&&c.nodebuffer?k(b,"utf-8"):a.asBinary():b},q=function(a){var b=o(this);return null===b||"undefined"==typeof b?"":(this.options.base64&&(b=h.decode(b)),b=a&&this.options.binary?A.utf8decode(b):d.transformTo("string",b),a||this.options.binary||(b=d.transformTo("string",A.utf8encode(b))),b)},r=function(a,b,c){this.name=a,this.dir=c.dir,this.date=c.date,this.comment=c.comment,this._data=b,this.options=c,this._initialMetadata={dir:c.dir,date:c.date}};r.prototype={asText:function(){return q.call(this,!0)},asBinary:function(){return q.call(this,!1)},asNodeBuffer:function(){var a=p(this);return d.transformTo("nodebuffer",a)},asUint8Array:function(){var a=p(this);return d.transformTo("uint8array",a)},asArrayBuffer:function(){return this.asUint8Array().buffer}};var s=function(a,b){var c,d="";for(c=0;b>c;c++)d+=String.fromCharCode(255&a),a>>>=8;return d},t=function(){var a,b,c={};for(a=0;a<arguments.length;a++)for(b in arguments[a])arguments[a].hasOwnProperty(b)&&"undefined"==typeof c[b]&&(c[b]=arguments[a][b]);return c},u=function(a){return a=a||{},a.base64!==!0||null!==a.binary&&void 0!==a.binary||(a.binary=!0),a=t(a,g),a.date=a.date||new Date,null!==a.compression&&(a.compression=a.compression.toUpperCase()),a},v=function(a,b,c){var e,f=d.getTypeOf(b);if(c=u(c),c.createFolders&&(e=w(a))&&x.call(this,e,!0),c.dir||null===b||"undefined"==typeof b)c.base64=!1,c.binary=!1,b=null;else if("string"===f)c.binary&&!c.base64&&c.optimizedBinaryString!==!0&&(b=d.string2binary(b));else{if(c.base64=!1,c.binary=!0,!(f||b instanceof j))throw new Error("The data of '"+a+"' is in an unsupported format !");"arraybuffer"===f&&(b=d.transformTo("uint8array",b))}var g=new r(a,b,c);return this.files[a]=g,g},w=function(a){"/"==a.slice(-1)&&(a=a.substring(0,a.length-1));var b=a.lastIndexOf("/");return b>0?a.substring(0,b):""},x=function(a,b){return"/"!=a.slice(-1)&&(a+="/"),b="undefined"!=typeof b?b:!1,this.files[a]||v.call(this,a,null,{dir:!0,createFolders:b}),this.files[a]},y=function(a,b){var c,f=new j;return a._data instanceof j?(f.uncompressedSize=a._data.uncompressedSize,f.crc32=a._data.crc32,0===f.uncompressedSize||a.dir?(b=i.STORE,f.compressedContent="",f.crc32=0):a._data.compressionMethod===b.magic?f.compressedContent=a._data.getCompressedContent():(c=a._data.getContent(),f.compressedContent=b.compress(d.transformTo(b.compressInputType,c)))):(c=p(a),(!c||0===c.length||a.dir)&&(b=i.STORE,c=""),f.uncompressedSize=c.length,f.crc32=e(c),f.compressedContent=b.compress(d.transformTo(b.compressInputType,c))),f.compressedSize=f.compressedContent.length,f.compressionMethod=b.magic,f},z=function(a,b,c,g){var h,i,j,k,m=(c.compressedContent,d.transformTo("string",l.utf8encode(b.name))),n=b.comment||"",o=d.transformTo("string",l.utf8encode(n)),p=m.length!==b.name.length,q=o.length!==n.length,r=b.options,t="",u="",v="";j=b._initialMetadata.dir!==b.dir?b.dir:r.dir,k=b._initialMetadata.date!==b.date?b.date:r.date,h=k.getHours(),h<<=6,h|=k.getMinutes(),h<<=5,h|=k.getSeconds()/2,i=k.getFullYear()-1980,i<<=4,i|=k.getMonth()+1,i<<=5,i|=k.getDate(),p&&(u=s(1,1)+s(e(m),4)+m,t+="up"+s(u.length,2)+u),q&&(v=s(1,1)+s(this.crc32(o),4)+o,t+="uc"+s(v.length,2)+v);var w="";w+="\n\x00",w+=p||q?"\x00\b":"\x00\x00",w+=c.compressionMethod,w+=s(h,2),w+=s(i,2),w+=s(c.crc32,4),w+=s(c.compressedSize,4),w+=s(c.uncompressedSize,4),w+=s(m.length,2),w+=s(t.length,2);var x=f.LOCAL_FILE_HEADER+w+m+t,y=f.CENTRAL_FILE_HEADER+"\x00"+w+s(o.length,2)+"\x00\x00\x00\x00"+(j===!0?"\x00\x00\x00":"\x00\x00\x00\x00")+s(g,4)+m+t+o;return{fileRecord:x,dirRecord:y,compressedObject:c}},A={load:function(){throw new Error("Load method is not defined. Is the file jszip-load.js included ?")},filter:function(a){var b,c,d,e,f=[];for(b in this.files)this.files.hasOwnProperty(b)&&(d=this.files[b],e=new r(d.name,d._data,t(d.options)),c=b.slice(this.root.length,b.length),b.slice(0,this.root.length)===this.root&&a(c,e)&&f.push(e));return f},file:function(a,b,c){if(1===arguments.length){if(d.isRegExp(a)){var e=a;return this.filter(function(a,b){return!b.dir&&e.test(a)})}return this.filter(function(b,c){return!c.dir&&b===a})[0]||null}return a=this.root+a,v.call(this,a,b,c),this},folder:function(a){if(!a)return this;if(d.isRegExp(a))return this.filter(function(b,c){return c.dir&&a.test(b)});var b=this.root+a,c=x.call(this,b),e=this.clone();return e.root=c.name,e},remove:function(a){a=this.root+a;var b=this.files[a];if(b||("/"!=a.slice(-1)&&(a+="/"),b=this.files[a]),b&&!b.dir)delete this.files[a];else for(var c=this.filter(function(b,c){return c.name.slice(0,a.length)===a}),d=0;d<c.length;d++)delete this.files[c[d].name];return this},generate:function(a){a=t(a||{},{base64:!0,compression:"STORE",type:"base64",comment:null}),d.checkSupport(a.type);var b,c,e=[],g=0,j=0,k=d.transformTo("string",this.utf8encode(a.comment||this.comment||""));for(var l in this.files)if(this.files.hasOwnProperty(l)){var o=this.files[l],p=o.options.compression||a.compression.toUpperCase(),q=i[p];if(!q)throw new Error(p+" is not a valid compression method !");var r=y.call(this,o,q),u=z.call(this,l,o,r,g);g+=u.fileRecord.length+r.compressedSize,j+=u.dirRecord.length,e.push(u)}var v="";v=f.CENTRAL_DIRECTORY_END+"\x00\x00\x00\x00"+s(e.length,2)+s(e.length,2)+s(j,4)+s(g,4)+s(k.length,2)+k;var w=a.type.toLowerCase();for(b="uint8array"===w||"arraybuffer"===w||"blob"===w||"nodebuffer"===w?new n(g+j+v.length):new m(g+j+v.length),c=0;c<e.length;c++)b.append(e[c].fileRecord),b.append(e[c].compressedObject.compressedContent);for(c=0;c<e.length;c++)b.append(e[c].dirRecord);b.append(v);var x=b.finalize();switch(a.type.toLowerCase()){case"uint8array":case"arraybuffer":case"nodebuffer":return d.transformTo(a.type.toLowerCase(),x);case"blob":return d.arrayBuffer2Blob(d.transformTo("arraybuffer",x));case"base64":return a.base64?h.encode(x):x;default:return x}},crc32:function(a,b){return e(a,b)},utf8encode:function(a){return d.transformTo("string",l.utf8encode(a))},utf8decode:function(a){return l.utf8decode(a)}};b.exports=A},{"./base64":1,"./compressedObject":2,"./compressions":3,"./crc32":4,"./defaults":6,"./nodeBuffer":11,"./signature":14,"./stringWriter":16,"./support":17,"./uint8ArrayWriter":19,"./utf8":20,"./utils":21}],14:[function(a,b,c){"use strict";c.LOCAL_FILE_HEADER="PK",c.CENTRAL_FILE_HEADER="PK",c.CENTRAL_DIRECTORY_END="PK",c.ZIP64_CENTRAL_DIRECTORY_LOCATOR="PK",c.ZIP64_CENTRAL_DIRECTORY_END="PK",c.DATA_DESCRIPTOR="PK\b"},{}],15:[function(a,b){"use strict";function c(a,b){this.data=a,b||(this.data=e.string2binary(this.data)),this.length=this.data.length,this.index=0}var d=a("./dataReader"),e=a("./utils");c.prototype=new d,c.prototype.byteAt=function(a){return this.data.charCodeAt(a)},c.prototype.lastIndexOfSignature=function(a){return this.data.lastIndexOf(a)},c.prototype.readData=function(a){this.checkOffset(a);var b=this.data.slice(this.index,this.index+a);return this.index+=a,b},b.exports=c},{"./dataReader":5,"./utils":21}],16:[function(a,b){"use strict";var c=a("./utils"),d=function(){this.data=[]};d.prototype={append:function(a){a=c.transformTo("string",a),this.data.push(a)},finalize:function(){return this.data.join("")}},b.exports=d},{"./utils":21}],17:[function(a,b,c){(function(a){"use strict";if(c.base64=!0,c.array=!0,c.string=!0,c.arraybuffer="undefined"!=typeof ArrayBuffer&&"undefined"!=typeof Uint8Array,c.nodebuffer="undefined"!=typeof a,c.uint8array="undefined"!=typeof Uint8Array,"undefined"==typeof ArrayBuffer)c.blob=!1;else{var b=new ArrayBuffer(0);try{c.blob=0===new Blob([b],{type:"application/zip"}).size}catch(d){try{var e=window.BlobBuilder||window.WebKitBlobBuilder||window.MozBlobBuilder||window.MSBlobBuilder,f=new e;f.append(b),c.blob=0===f.getBlob("application/zip").size}catch(d){c.blob=!1}}}}).call(this,"undefined"!=typeof Buffer?Buffer:void 0)},{}],18:[function(a,b){"use strict";function c(a){a&&(this.data=a,this.length=this.data.length,this.index=0)}var d=a("./dataReader");c.prototype=new d,c.prototype.byteAt=function(a){return this.data[a]},c.prototype.lastIndexOfSignature=function(a){for(var b=a.charCodeAt(0),c=a.charCodeAt(1),d=a.charCodeAt(2),e=a.charCodeAt(3),f=this.length-4;f>=0;--f)if(this.data[f]===b&&this.data[f+1]===c&&this.data[f+2]===d&&this.data[f+3]===e)return f;return-1},c.prototype.readData=function(a){if(this.checkOffset(a),0===a)return new Uint8Array(0);var b=this.data.subarray(this.index,this.index+a);return this.index+=a,b},b.exports=c},{"./dataReader":5}],19:[function(a,b){"use strict";var c=a("./utils"),d=function(a){this.data=new Uint8Array(a),this.index=0};d.prototype={append:function(a){0!==a.length&&(a=c.transformTo("uint8array",a),this.data.set(a,this.index),this.index+=a.length)},finalize:function(){return this.data}},b.exports=d},{"./utils":21}],20:[function(a,b,c){"use strict";for(var d=a("./utils"),e=a("./support"),f=a("./nodeBuffer"),g=new Array(256),h=0;256>h;h++)g[h]=h>=252?6:h>=248?5:h>=240?4:h>=224?3:h>=192?2:1;g[254]=g[254]=1;var i=function(a){var b,c,d,f,g,h=a.length,i=0;for(f=0;h>f;f++)c=a.charCodeAt(f),55296===(64512&c)&&h>f+1&&(d=a.charCodeAt(f+1),56320===(64512&d)&&(c=65536+(c-55296<<10)+(d-56320),f++)),i+=128>c?1:2048>c?2:65536>c?3:4;for(b=e.uint8array?new Uint8Array(i):new Array(i),g=0,f=0;i>g;f++)c=a.charCodeAt(f),55296===(64512&c)&&h>f+1&&(d=a.charCodeAt(f+1),56320===(64512&d)&&(c=65536+(c-55296<<10)+(d-56320),f++)),128>c?b[g++]=c:2048>c?(b[g++]=192|c>>>6,b[g++]=128|63&c):65536>c?(b[g++]=224|c>>>12,b[g++]=128|c>>>6&63,b[g++]=128|63&c):(b[g++]=240|c>>>18,b[g++]=128|c>>>12&63,b[g++]=128|c>>>6&63,b[g++]=128|63&c);return b},j=function(a,b){var c;for(b=b||a.length,b>a.length&&(b=a.length),c=b-1;c>=0&&128===(192&a[c]);)c--;return 0>c?b:0===c?b:c+g[a[c]]>b?c:b},k=function(a){var b,c,e,f,h=a.length,i=new Array(2*h);for(c=0,b=0;h>b;)if(e=a[b++],128>e)i[c++]=e;else if(f=g[e],f>4)i[c++]=65533,b+=f-1;else{for(e&=2===f?31:3===f?15:7;f>1&&h>b;)e=e<<6|63&a[b++],f--;f>1?i[c++]=65533:65536>e?i[c++]=e:(e-=65536,i[c++]=55296|e>>10&1023,i[c++]=56320|1023&e)}return i.length!==c&&(i.subarray?i=i.subarray(0,c):i.length=c),d.applyFromCharCode(i)};c.utf8encode=function(a){return e.nodebuffer?f(a,"utf-8"):i(a)},c.utf8decode=function(a){if(e.nodebuffer)return d.transformTo("nodebuffer",a).toString("utf-8");a=d.transformTo(e.uint8array?"uint8array":"array",a);for(var b=[],c=0,f=a.length,g=65536;f>c;){var h=j(a,Math.min(c+g,f));b.push(e.uint8array?k(a.subarray(c,h)):k(a.slice(c,h))),c=h}return b.join("")}},{"./nodeBuffer":11,"./support":17,"./utils":21}],21:[function(a,b,c){"use strict";function d(a){return a}function e(a,b){for(var c=0;c<a.length;++c)b[c]=255&a.charCodeAt(c);return b}function f(a){var b=65536,d=[],e=a.length,f=c.getTypeOf(a),g=0,h=!0;try{switch(f){case"uint8array":String.fromCharCode.apply(null,new Uint8Array(0));break;case"nodebuffer":String.fromCharCode.apply(null,j(0))}}catch(i){h=!1}if(!h){for(var k="",l=0;l<a.length;l++)k+=String.fromCharCode(a[l]);return k}for(;e>g&&b>1;)try{d.push("array"===f||"nodebuffer"===f?String.fromCharCode.apply(null,a.slice(g,Math.min(g+b,e))):String.fromCharCode.apply(null,a.subarray(g,Math.min(g+b,e)))),g+=b}catch(i){b=Math.floor(b/2)}return d.join("")}function g(a,b){for(var c=0;c<a.length;c++)b[c]=a[c];return b}var h=a("./support"),i=a("./compressions"),j=a("./nodeBuffer");c.string2binary=function(a){for(var b="",c=0;c<a.length;c++)b+=String.fromCharCode(255&a.charCodeAt(c));return b},c.arrayBuffer2Blob=function(a){c.checkSupport("blob");try{return new Blob([a],{type:"application/zip"})}catch(b){try{var d=window.BlobBuilder||window.WebKitBlobBuilder||window.MozBlobBuilder||window.MSBlobBuilder,e=new d;return e.append(a),e.getBlob("application/zip")}catch(b){throw new Error("Bug : can't construct the Blob.")}}},c.applyFromCharCode=f;var k={};k.string={string:d,array:function(a){return e(a,new Array(a.length))},arraybuffer:function(a){return k.string.uint8array(a).buffer},uint8array:function(a){return e(a,new Uint8Array(a.length))},nodebuffer:function(a){return e(a,j(a.length))}},k.array={string:f,array:d,arraybuffer:function(a){return new Uint8Array(a).buffer},uint8array:function(a){return new Uint8Array(a)},nodebuffer:function(a){return j(a)}},k.arraybuffer={string:function(a){return f(new Uint8Array(a))},array:function(a){return g(new Uint8Array(a),new Array(a.byteLength))},arraybuffer:d,uint8array:function(a){return new Uint8Array(a)},nodebuffer:function(a){return j(new Uint8Array(a))}},k.uint8array={string:f,array:function(a){return g(a,new Array(a.length))},arraybuffer:function(a){return a.buffer},uint8array:d,nodebuffer:function(a){return j(a)}},k.nodebuffer={string:f,array:function(a){return g(a,new Array(a.length))},arraybuffer:function(a){return k.nodebuffer.uint8array(a).buffer},uint8array:function(a){return g(a,new Uint8Array(a.length))},nodebuffer:d},c.transformTo=function(a,b){if(b||(b=""),!a)return b;c.checkSupport(a);var d=c.getTypeOf(b),e=k[d][a](b);return e},c.getTypeOf=function(a){return"string"==typeof a?"string":"[object Array]"===Object.prototype.toString.call(a)?"array":h.nodebuffer&&j.test(a)?"nodebuffer":h.uint8array&&a instanceof Uint8Array?"uint8array":h.arraybuffer&&a instanceof ArrayBuffer?"arraybuffer":void 0},c.checkSupport=function(a){var b=h[a.toLowerCase()];if(!b)throw new Error(a+" is not supported by this browser")},c.MAX_VALUE_16BITS=65535,c.MAX_VALUE_32BITS=-1,c.pretty=function(a){var b,c,d="";for(c=0;c<(a||"").length;c++)b=a.charCodeAt(c),d+="\\x"+(16>b?"0":"")+b.toString(16).toUpperCase();return d},c.findCompression=function(a){for(var b in i)if(i.hasOwnProperty(b)&&i[b].magic===a)return i[b];return null},c.isRegExp=function(a){return"[object RegExp]"===Object.prototype.toString.call(a)}},{"./compressions":3,"./nodeBuffer":11,"./support":17}],22:[function(a,b){"use strict";function c(a,b){this.files=[],this.loadOptions=b,a&&this.load(a)}var d=a("./stringReader"),e=a("./nodeBufferReader"),f=a("./uint8ArrayReader"),g=a("./utils"),h=a("./signature"),i=a("./zipEntry"),j=a("./support"),k=a("./object");c.prototype={checkSignature:function(a){var b=this.reader.readString(4);if(b!==a)throw new Error("Corrupted zip or bug : unexpected signature ("+g.pretty(b)+", expected "+g.pretty(a)+")")},readBlockEndOfCentral:function(){this.diskNumber=this.reader.readInt(2),this.diskWithCentralDirStart=this.reader.readInt(2),this.centralDirRecordsOnThisDisk=this.reader.readInt(2),this.centralDirRecords=this.reader.readInt(2),this.centralDirSize=this.reader.readInt(4),this.centralDirOffset=this.reader.readInt(4),this.zipCommentLength=this.reader.readInt(2),this.zipComment=this.reader.readString(this.zipCommentLength),this.zipComment=k.utf8decode(this.zipComment)},readBlockZip64EndOfCentral:function(){this.zip64EndOfCentralSize=this.reader.readInt(8),this.versionMadeBy=this.reader.readString(2),this.versionNeeded=this.reader.readInt(2),this.diskNumber=this.reader.readInt(4),this.diskWithCentralDirStart=this.reader.readInt(4),this.centralDirRecordsOnThisDisk=this.reader.readInt(8),this.centralDirRecords=this.reader.readInt(8),this.centralDirSize=this.reader.readInt(8),this.centralDirOffset=this.reader.readInt(8),this.zip64ExtensibleData={};for(var a,b,c,d=this.zip64EndOfCentralSize-44,e=0;d>e;)a=this.reader.readInt(2),b=this.reader.readInt(4),c=this.reader.readString(b),this.zip64ExtensibleData[a]={id:a,length:b,value:c}},readBlockZip64EndOfCentralLocator:function(){if(this.diskWithZip64CentralDirStart=this.reader.readInt(4),this.relativeOffsetEndOfZip64CentralDir=this.reader.readInt(8),this.disksCount=this.reader.readInt(4),this.disksCount>1)throw new Error("Multi-volumes zip are not supported")},readLocalFiles:function(){var a,b;for(a=0;a<this.files.length;a++)b=this.files[a],this.reader.setIndex(b.localHeaderOffset),this.checkSignature(h.LOCAL_FILE_HEADER),b.readLocalPart(this.reader),b.handleUTF8()},readCentralDir:function(){var a;for(this.reader.setIndex(this.centralDirOffset);this.reader.readString(4)===h.CENTRAL_FILE_HEADER;)a=new i({zip64:this.zip64},this.loadOptions),a.readCentralPart(this.reader),this.files.push(a)},readEndOfCentral:function(){var a=this.reader.lastIndexOfSignature(h.CENTRAL_DIRECTORY_END);if(-1===a)throw new Error("Corrupted zip : can't find end of central directory");if(this.reader.setIndex(a),this.checkSignature(h.CENTRAL_DIRECTORY_END),this.readBlockEndOfCentral(),this.diskNumber===g.MAX_VALUE_16BITS||this.diskWithCentralDirStart===g.MAX_VALUE_16BITS||this.centralDirRecordsOnThisDisk===g.MAX_VALUE_16BITS||this.centralDirRecords===g.MAX_VALUE_16BITS||this.centralDirSize===g.MAX_VALUE_32BITS||this.centralDirOffset===g.MAX_VALUE_32BITS){if(this.zip64=!0,a=this.reader.lastIndexOfSignature(h.ZIP64_CENTRAL_DIRECTORY_LOCATOR),-1===a)throw new Error("Corrupted zip : can't find the ZIP64 end of central directory locator");this.reader.setIndex(a),this.checkSignature(h.ZIP64_CENTRAL_DIRECTORY_LOCATOR),this.readBlockZip64EndOfCentralLocator(),this.reader.setIndex(this.relativeOffsetEndOfZip64CentralDir),this.checkSignature(h.ZIP64_CENTRAL_DIRECTORY_END),this.readBlockZip64EndOfCentral()}},prepareReader:function(a){var b=g.getTypeOf(a);this.reader="string"!==b||j.uint8array?"nodebuffer"===b?new e(a):new f(g.transformTo("uint8array",a)):new d(a,this.loadOptions.optimizedBinaryString)},load:function(a){this.prepareReader(a),this.readEndOfCentral(),this.readCentralDir(),this.readLocalFiles()}},b.exports=c},{"./nodeBufferReader":12,"./object":13,"./signature":14,"./stringReader":15,"./support":17,"./uint8ArrayReader":18,"./utils":21,"./zipEntry":23}],23:[function(a,b){"use strict";function c(a,b){this.options=a,this.loadOptions=b}var d=a("./stringReader"),e=a("./utils"),f=a("./compressedObject"),g=a("./object");c.prototype={isEncrypted:function(){return 1===(1&this.bitFlag)},useUTF8:function(){return 2048===(2048&this.bitFlag)},prepareCompressedContent:function(a,b,c){return function(){var d=a.index;a.setIndex(b);var e=a.readData(c);return a.setIndex(d),e}},prepareContent:function(a,b,c,d,f){return function(){var a=e.transformTo(d.uncompressInputType,this.getCompressedContent()),b=d.uncompress(a);if(b.length!==f)throw new Error("Bug : uncompressed data size mismatch");return b}},readLocalPart:function(a){var b,c;if(a.skip(22),this.fileNameLength=a.readInt(2),c=a.readInt(2),this.fileName=a.readString(this.fileNameLength),a.skip(c),-1==this.compressedSize||-1==this.uncompressedSize)throw new Error("Bug or corrupted zip : didn't get enough informations from the central directory (compressedSize == -1 || uncompressedSize == -1)");if(b=e.findCompression(this.compressionMethod),null===b)throw new Error("Corrupted zip : compression "+e.pretty(this.compressionMethod)+" unknown (inner file : "+this.fileName+")");if(this.decompressed=new f,this.decompressed.compressedSize=this.compressedSize,this.decompressed.uncompressedSize=this.uncompressedSize,this.decompressed.crc32=this.crc32,this.decompressed.compressionMethod=this.compressionMethod,this.decompressed.getCompressedContent=this.prepareCompressedContent(a,a.index,this.compressedSize,b),this.decompressed.getContent=this.prepareContent(a,a.index,this.compressedSize,b,this.uncompressedSize),this.loadOptions.checkCRC32&&(this.decompressed=e.transformTo("string",this.decompressed.getContent()),g.crc32(this.decompressed)!==this.crc32))throw new Error("Corrupted zip : CRC32 mismatch")},readCentralPart:function(a){if(this.versionMadeBy=a.readString(2),this.versionNeeded=a.readInt(2),this.bitFlag=a.readInt(2),this.compressionMethod=a.readString(2),this.date=a.readDate(),this.crc32=a.readInt(4),this.compressedSize=a.readInt(4),this.uncompressedSize=a.readInt(4),this.fileNameLength=a.readInt(2),this.extraFieldsLength=a.readInt(2),this.fileCommentLength=a.readInt(2),this.diskNumberStart=a.readInt(2),this.internalFileAttributes=a.readInt(2),this.externalFileAttributes=a.readInt(4),this.localHeaderOffset=a.readInt(4),this.isEncrypted())throw new Error("Encrypted zip are not supported");this.fileName=a.readString(this.fileNameLength),this.readExtraFields(a),this.parseZIP64ExtraField(a),this.fileComment=a.readString(this.fileCommentLength),this.dir=16&this.externalFileAttributes?!0:!1},parseZIP64ExtraField:function(){if(this.extraFields[1]){var a=new d(this.extraFields[1].value);this.uncompressedSize===e.MAX_VALUE_32BITS&&(this.uncompressedSize=a.readInt(8)),this.compressedSize===e.MAX_VALUE_32BITS&&(this.compressedSize=a.readInt(8)),this.localHeaderOffset===e.MAX_VALUE_32BITS&&(this.localHeaderOffset=a.readInt(8)),this.diskNumberStart===e.MAX_VALUE_32BITS&&(this.diskNumberStart=a.readInt(4))}},readExtraFields:function(a){var b,c,d,e=a.index;for(this.extraFields=this.extraFields||{};a.index<e+this.extraFieldsLength;)b=a.readInt(2),c=a.readInt(2),d=a.readString(c),this.extraFields[b]={id:b,length:c,value:d}},handleUTF8:function(){if(this.useUTF8())this.fileName=g.utf8decode(this.fileName),this.fileComment=g.utf8decode(this.fileComment);else{var a=this.findExtraFieldUnicodePath();null!==a&&(this.fileName=a);var b=this.findExtraFieldUnicodeComment();null!==b&&(this.fileComment=b)}},findExtraFieldUnicodePath:function(){var a=this.extraFields[28789];if(a){var b=new d(a.value);return 1!==b.readInt(1)?null:g.crc32(this.fileName)!==b.readInt(4)?null:g.utf8decode(b.readString(a.length-5))}return null},findExtraFieldUnicodeComment:function(){var a=this.extraFields[25461];if(a){var b=new d(a.value);return 1!==b.readInt(1)?null:g.crc32(this.fileComment)!==b.readInt(4)?null:g.utf8decode(b.readString(a.length-5))}return null}},b.exports=c},{"./compressedObject":2,"./object":13,"./stringReader":15,"./utils":21}],24:[function(a,b){"use strict";var c=a("./lib/utils/common").assign,d=a("./lib/deflate"),e=a("./lib/inflate"),f=a("./lib/zlib/constants"),g={};c(g,d,e,f),b.exports=g},{"./lib/deflate":25,"./lib/inflate":26,"./lib/utils/common":27,"./lib/zlib/constants":30}],25:[function(a,b,c){"use strict";function d(a,b){var c=new s(b);if(c.push(a,!0),c.err)throw c.msg;return c.result}function e(a,b){return b=b||{},b.raw=!0,d(a,b)}function f(a,b){return b=b||{},b.gzip=!0,d(a,b)}var g=a("./zlib/deflate.js"),h=a("./utils/common"),i=a("./utils/strings"),j=a("./zlib/messages"),k=a("./zlib/zstream"),l=0,m=4,n=0,o=1,p=-1,q=0,r=8,s=function(a){this.options=h.assign({level:p,method:r,chunkSize:16384,windowBits:15,memLevel:8,strategy:q,to:""},a||{});var b=this.options;b.raw&&b.windowBits>0?b.windowBits=-b.windowBits:b.gzip&&b.windowBits>0&&b.windowBits<16&&(b.windowBits+=16),this.err=0,this.msg="",this.ended=!1,this.chunks=[],this.strm=new k,this.strm.avail_out=0;var c=g.deflateInit2(this.strm,b.level,b.method,b.windowBits,b.memLevel,b.strategy);if(c!==n)throw new Error(j[c]);b.header&&g.deflateSetHeader(this.strm,b.header)};s.prototype.push=function(a,b){var c,d,e=this.strm,f=this.options.chunkSize;if(this.ended)return!1;d=b===~~b?b:b===!0?m:l,e.input="string"==typeof a?i.string2buf(a):a,e.next_in=0,e.avail_in=e.input.length;do{if(0===e.avail_out&&(e.output=new h.Buf8(f),e.next_out=0,e.avail_out=f),c=g.deflate(e,d),c!==o&&c!==n)return this.onEnd(c),this.ended=!0,!1;(0===e.avail_out||0===e.avail_in&&d===m)&&this.onData("string"===this.options.to?i.buf2binstring(h.shrinkBuf(e.output,e.next_out)):h.shrinkBuf(e.output,e.next_out))}while((e.avail_in>0||0===e.avail_out)&&c!==o);return d===m?(c=g.deflateEnd(this.strm),this.onEnd(c),this.ended=!0,c===n):!0},s.prototype.onData=function(a){this.chunks.push(a)},s.prototype.onEnd=function(a){a===n&&(this.result="string"===this.options.to?this.chunks.join(""):h.flattenChunks(this.chunks)),this.chunks=[],this.err=a,this.msg=this.strm.msg},c.Deflate=s,c.deflate=d,c.deflateRaw=e,c.gzip=f},{"./utils/common":27,"./utils/strings":28,"./zlib/deflate.js":32,"./zlib/messages":37,"./zlib/zstream":39}],26:[function(a,b,c){"use strict";function d(a,b){var c=new m(b);if(c.push(a,!0),c.err)throw c.msg;return c.result}function e(a,b){return b=b||{},b.raw=!0,d(a,b)}var f=a("./zlib/inflate.js"),g=a("./utils/common"),h=a("./utils/strings"),i=a("./zlib/constants"),j=a("./zlib/messages"),k=a("./zlib/zstream"),l=a("./zlib/gzheader"),m=function(a){this.options=g.assign({chunkSize:16384,windowBits:0,to:""},a||{});var b=this.options;b.raw&&b.windowBits>=0&&b.windowBits<16&&(b.windowBits=-b.windowBits,0===b.windowBits&&(b.windowBits=-15)),!(b.windowBits>=0&&b.windowBits<16)||a&&a.windowBits||(b.windowBits+=32),b.windowBits>15&&b.windowBits<48&&0===(15&b.windowBits)&&(b.windowBits|=15),this.err=0,this.msg="",this.ended=!1,this.chunks=[],this.strm=new k,this.strm.avail_out=0;var c=f.inflateInit2(this.strm,b.windowBits);if(c!==i.Z_OK)throw new Error(j[c]);this.header=new l,f.inflateGetHeader(this.strm,this.header)};m.prototype.push=function(a,b){var c,d,e,j,k,l=this.strm,m=this.options.chunkSize;if(this.ended)return!1;d=b===~~b?b:b===!0?i.Z_FINISH:i.Z_NO_FLUSH,l.input="string"==typeof a?h.binstring2buf(a):a,l.next_in=0,l.avail_in=l.input.length;do{if(0===l.avail_out&&(l.output=new g.Buf8(m),l.next_out=0,l.avail_out=m),c=f.inflate(l,i.Z_NO_FLUSH),c!==i.Z_STREAM_END&&c!==i.Z_OK)return this.onEnd(c),this.ended=!0,!1;l.next_out&&(0===l.avail_out||c===i.Z_STREAM_END||0===l.avail_in&&d===i.Z_FINISH)&&("string"===this.options.to?(e=h.utf8border(l.output,l.next_out),j=l.next_out-e,k=h.buf2string(l.output,e),l.next_out=j,l.avail_out=m-j,j&&g.arraySet(l.output,l.output,e,j,0),this.onData(k)):this.onData(g.shrinkBuf(l.output,l.next_out)))}while(l.avail_in>0&&c!==i.Z_STREAM_END);return c===i.Z_STREAM_END&&(d=i.Z_FINISH),d===i.Z_FINISH?(c=f.inflateEnd(this.strm),this.onEnd(c),this.ended=!0,c===i.Z_OK):!0},m.prototype.onData=function(a){this.chunks.push(a)},m.prototype.onEnd=function(a){a===i.Z_OK&&(this.result="string"===this.options.to?this.chunks.join(""):g.flattenChunks(this.chunks)),this.chunks=[],this.err=a,this.msg=this.strm.msg},c.Inflate=m,c.inflate=d,c.inflateRaw=e,c.ungzip=d},{"./utils/common":27,"./utils/strings":28,"./zlib/constants":30,"./zlib/gzheader":33,"./zlib/inflate.js":35,"./zlib/messages":37,"./zlib/zstream":39}],27:[function(a,b,c){"use strict";var d="undefined"!=typeof Uint8Array&&"undefined"!=typeof Uint16Array&&"undefined"!=typeof Int32Array;c.assign=function(a){for(var b=Array.prototype.slice.call(arguments,1);b.length;){var c=b.shift();if(c){if("object"!=typeof c)throw new TypeError(c+"must be non-object");for(var d in c)c.hasOwnProperty(d)&&(a[d]=c[d])}}return a},c.shrinkBuf=function(a,b){return a.length===b?a:a.subarray?a.subarray(0,b):(a.length=b,a)};var e={arraySet:function(a,b,c,d,e){if(b.subarray&&a.subarray)return void a.set(b.subarray(c,c+d),e);for(var f=0;d>f;f++)a[e+f]=b[c+f]},flattenChunks:function(a){var b,c,d,e,f,g;for(d=0,b=0,c=a.length;c>b;b++)d+=a[b].length;for(g=new Uint8Array(d),e=0,b=0,c=a.length;c>b;b++)f=a[b],g.set(f,e),e+=f.length;return g}},f={arraySet:function(a,b,c,d,e){for(var f=0;d>f;f++)a[e+f]=b[c+f]},flattenChunks:function(a){return[].concat.apply([],a)}};c.setTyped=function(a){a?(c.Buf8=Uint8Array,c.Buf16=Uint16Array,c.Buf32=Int32Array,c.assign(c,e)):(c.Buf8=Array,c.Buf16=Array,c.Buf32=Array,c.assign(c,f))},c.setTyped(d)},{}],28:[function(a,b,c){"use strict";function d(a,b){if(65537>b&&(a.subarray&&g||!a.subarray&&f))return String.fromCharCode.apply(null,e.shrinkBuf(a,b));for(var c="",d=0;b>d;d++)c+=String.fromCharCode(a[d]);return c}var e=a("./common"),f=!0,g=!0;try{String.fromCharCode.apply(null,[0])}catch(h){f=!1}try{String.fromCharCode.apply(null,new Uint8Array(1))}catch(h){g=!1}for(var i=new e.Buf8(256),j=0;256>j;j++)i[j]=j>=252?6:j>=248?5:j>=240?4:j>=224?3:j>=192?2:1;i[254]=i[254]=1,c.string2buf=function(a){var b,c,d,f,g,h=a.length,i=0;for(f=0;h>f;f++)c=a.charCodeAt(f),55296===(64512&c)&&h>f+1&&(d=a.charCodeAt(f+1),56320===(64512&d)&&(c=65536+(c-55296<<10)+(d-56320),f++)),i+=128>c?1:2048>c?2:65536>c?3:4;for(b=new e.Buf8(i),g=0,f=0;i>g;f++)c=a.charCodeAt(f),55296===(64512&c)&&h>f+1&&(d=a.charCodeAt(f+1),56320===(64512&d)&&(c=65536+(c-55296<<10)+(d-56320),f++)),128>c?b[g++]=c:2048>c?(b[g++]=192|c>>>6,b[g++]=128|63&c):65536>c?(b[g++]=224|c>>>12,b[g++]=128|c>>>6&63,b[g++]=128|63&c):(b[g++]=240|c>>>18,b[g++]=128|c>>>12&63,b[g++]=128|c>>>6&63,b[g++]=128|63&c);return b},c.buf2binstring=function(a){return d(a,a.length)},c.binstring2buf=function(a){for(var b=new e.Buf8(a.length),c=0,d=b.length;d>c;c++)b[c]=a.charCodeAt(c);return b},c.buf2string=function(a,b){var c,e,f,g,h=b||a.length,j=new Array(2*h);for(e=0,c=0;h>c;)if(f=a[c++],128>f)j[e++]=f;else if(g=i[f],g>4)j[e++]=65533,c+=g-1;else{for(f&=2===g?31:3===g?15:7;g>1&&h>c;)f=f<<6|63&a[c++],g--;g>1?j[e++]=65533:65536>f?j[e++]=f:(f-=65536,j[e++]=55296|f>>10&1023,j[e++]=56320|1023&f)}return d(j,e)},c.utf8border=function(a,b){var c;for(b=b||a.length,b>a.length&&(b=a.length),c=b-1;c>=0&&128===(192&a[c]);)c--;return 0>c?b:0===c?b:c+i[a[c]]>b?c:b}},{"./common":27}],29:[function(a,b){"use strict";function c(a,b,c,d){for(var e=65535&a|0,f=a>>>16&65535|0,g=0;0!==c;){g=c>2e3?2e3:c,c-=g;do e=e+b[d++]|0,f=f+e|0;while(--g);e%=65521,f%=65521}return e|f<<16|0}b.exports=c},{}],30:[function(a,b){b.exports={Z_NO_FLUSH:0,Z_PARTIAL_FLUSH:1,Z_SYNC_FLUSH:2,Z_FULL_FLUSH:3,Z_FINISH:4,Z_BLOCK:5,Z_TREES:6,Z_OK:0,Z_STREAM_END:1,Z_NEED_DICT:2,Z_ERRNO:-1,Z_STREAM_ERROR:-2,Z_DATA_ERROR:-3,Z_BUF_ERROR:-5,Z_NO_COMPRESSION:0,Z_BEST_SPEED:1,Z_BEST_COMPRESSION:9,Z_DEFAULT_COMPRESSION:-1,Z_FILTERED:1,Z_HUFFMAN_ONLY:2,Z_RLE:3,Z_FIXED:4,Z_DEFAULT_STRATEGY:0,Z_BINARY:0,Z_TEXT:1,Z_UNKNOWN:2,Z_DEFLATED:8}},{}],31:[function(a,b){"use strict";function c(){for(var a,b=[],c=0;256>c;c++){a=c;for(var d=0;8>d;d++)a=1&a?3988292384^a>>>1:a>>>1;b[c]=a}return b}function d(a,b,c,d){var f=e,g=d+c;a=-1^a;for(var h=d;g>h;h++)a=a>>>8^f[255&(a^b[h])];return-1^a}var e=c();b.exports=d},{}],32:[function(a,b,c){"use strict";function d(a,b){return a.msg=G[b],b}function e(a){return(a<<1)-(a>4?9:0)}function f(a){for(var b=a.length;--b>=0;)a[b]=0}function g(a){var b=a.state,c=b.pending;c>a.avail_out&&(c=a.avail_out),0!==c&&(C.arraySet(a.output,b.pending_buf,b.pending_out,c,a.next_out),a.next_out+=c,b.pending_out+=c,a.total_out+=c,a.avail_out-=c,b.pending-=c,0===b.pending&&(b.pending_out=0))}function h(a,b){D._tr_flush_block(a,a.block_start>=0?a.block_start:-1,a.strstart-a.block_start,b),a.block_start=a.strstart,g(a.strm)}function i(a,b){a.pending_buf[a.pending++]=b}function j(a,b){a.pending_buf[a.pending++]=b>>>8&255,a.pending_buf[a.pending++]=255&b}function k(a,b,c,d){var e=a.avail_in;return e>d&&(e=d),0===e?0:(a.avail_in-=e,C.arraySet(b,a.input,a.next_in,e,c),1===a.state.wrap?a.adler=E(a.adler,b,e,c):2===a.state.wrap&&(a.adler=F(a.adler,b,e,c)),a.next_in+=e,a.total_in+=e,e)}function l(a,b){var c,d,e=a.max_chain_length,f=a.strstart,g=a.prev_length,h=a.nice_match,i=a.strstart>a.w_size-jb?a.strstart-(a.w_size-jb):0,j=a.window,k=a.w_mask,l=a.prev,m=a.strstart+ib,n=j[f+g-1],o=j[f+g];a.prev_length>=a.good_match&&(e>>=2),h>a.lookahead&&(h=a.lookahead);do if(c=b,j[c+g]===o&&j[c+g-1]===n&&j[c]===j[f]&&j[++c]===j[f+1]){f+=2,c++;do;while(j[++f]===j[++c]&&j[++f]===j[++c]&&j[++f]===j[++c]&&j[++f]===j[++c]&&j[++f]===j[++c]&&j[++f]===j[++c]&&j[++f]===j[++c]&&j[++f]===j[++c]&&m>f);if(d=ib-(m-f),f=m-ib,d>g){if(a.match_start=b,g=d,d>=h)break;n=j[f+g-1],o=j[f+g]}}while((b=l[b&k])>i&&0!==--e);return g<=a.lookahead?g:a.lookahead}function m(a){var b,c,d,e,f,g=a.w_size;do{if(e=a.window_size-a.lookahead-a.strstart,a.strstart>=g+(g-jb)){C.arraySet(a.window,a.window,g,g,0),a.match_start-=g,a.strstart-=g,a.block_start-=g,c=a.hash_size,b=c;do d=a.head[--b],a.head[b]=d>=g?d-g:0;while(--c);c=g,b=c;do d=a.prev[--b],a.prev[b]=d>=g?d-g:0;while(--c);e+=g}if(0===a.strm.avail_in)break;if(c=k(a.strm,a.window,a.strstart+a.lookahead,e),a.lookahead+=c,a.lookahead+a.insert>=hb)for(f=a.strstart-a.insert,a.ins_h=a.window[f],a.ins_h=(a.ins_h<<a.hash_shift^a.window[f+1])&a.hash_mask;a.insert&&(a.ins_h=(a.ins_h<<a.hash_shift^a.window[f+hb-1])&a.hash_mask,a.prev[f&a.w_mask]=a.head[a.ins_h],a.head[a.ins_h]=f,f++,a.insert--,!(a.lookahead+a.insert<hb)););}while(a.lookahead<jb&&0!==a.strm.avail_in)}function n(a,b){var c=65535;for(c>a.pending_buf_size-5&&(c=a.pending_buf_size-5);;){if(a.lookahead<=1){if(m(a),0===a.lookahead&&b===H)return sb;if(0===a.lookahead)break}a.strstart+=a.lookahead,a.lookahead=0;var d=a.block_start+c;if((0===a.strstart||a.strstart>=d)&&(a.lookahead=a.strstart-d,a.strstart=d,h(a,!1),0===a.strm.avail_out))return sb;if(a.strstart-a.block_start>=a.w_size-jb&&(h(a,!1),0===a.strm.avail_out))return sb}return a.insert=0,b===K?(h(a,!0),0===a.strm.avail_out?ub:vb):a.strstart>a.block_start&&(h(a,!1),0===a.strm.avail_out)?sb:sb}function o(a,b){for(var c,d;;){if(a.lookahead<jb){if(m(a),a.lookahead<jb&&b===H)return sb;if(0===a.lookahead)break}if(c=0,a.lookahead>=hb&&(a.ins_h=(a.ins_h<<a.hash_shift^a.window[a.strstart+hb-1])&a.hash_mask,c=a.prev[a.strstart&a.w_mask]=a.head[a.ins_h],a.head[a.ins_h]=a.strstart),0!==c&&a.strstart-c<=a.w_size-jb&&(a.match_length=l(a,c)),a.match_length>=hb)if(d=D._tr_tally(a,a.strstart-a.match_start,a.match_length-hb),a.lookahead-=a.match_length,a.match_length<=a.max_lazy_match&&a.lookahead>=hb){a.match_length--;do a.strstart++,a.ins_h=(a.ins_h<<a.hash_shift^a.window[a.strstart+hb-1])&a.hash_mask,c=a.prev[a.strstart&a.w_mask]=a.head[a.ins_h],a.head[a.ins_h]=a.strstart;while(0!==--a.match_length);a.strstart++}else a.strstart+=a.match_length,a.match_length=0,a.ins_h=a.window[a.strstart],a.ins_h=(a.ins_h<<a.hash_shift^a.window[a.strstart+1])&a.hash_mask;else d=D._tr_tally(a,0,a.window[a.strstart]),a.lookahead--,a.strstart++;if(d&&(h(a,!1),0===a.strm.avail_out))return sb}return a.insert=a.strstart<hb-1?a.strstart:hb-1,b===K?(h(a,!0),0===a.strm.avail_out?ub:vb):a.last_lit&&(h(a,!1),0===a.strm.avail_out)?sb:tb}function p(a,b){for(var c,d,e;;){if(a.lookahead<jb){if(m(a),a.lookahead<jb&&b===H)return sb;if(0===a.lookahead)break}if(c=0,a.lookahead>=hb&&(a.ins_h=(a.ins_h<<a.hash_shift^a.window[a.strstart+hb-1])&a.hash_mask,c=a.prev[a.strstart&a.w_mask]=a.head[a.ins_h],a.head[a.ins_h]=a.strstart),a.prev_length=a.match_length,a.prev_match=a.match_start,a.match_length=hb-1,0!==c&&a.prev_length<a.max_lazy_match&&a.strstart-c<=a.w_size-jb&&(a.match_length=l(a,c),a.match_length<=5&&(a.strategy===S||a.match_length===hb&&a.strstart-a.match_start>4096)&&(a.match_length=hb-1)),a.prev_length>=hb&&a.match_length<=a.prev_length){e=a.strstart+a.lookahead-hb,d=D._tr_tally(a,a.strstart-1-a.prev_match,a.prev_length-hb),a.lookahead-=a.prev_length-1,a.prev_length-=2;do++a.strstart<=e&&(a.ins_h=(a.ins_h<<a.hash_shift^a.window[a.strstart+hb-1])&a.hash_mask,c=a.prev[a.strstart&a.w_mask]=a.head[a.ins_h],a.head[a.ins_h]=a.strstart);while(0!==--a.prev_length);if(a.match_available=0,a.match_length=hb-1,a.strstart++,d&&(h(a,!1),0===a.strm.avail_out))return sb}else if(a.match_available){if(d=D._tr_tally(a,0,a.window[a.strstart-1]),d&&h(a,!1),a.strstart++,a.lookahead--,0===a.strm.avail_out)return sb}else a.match_available=1,a.strstart++,a.lookahead--}return a.match_available&&(d=D._tr_tally(a,0,a.window[a.strstart-1]),a.match_available=0),a.insert=a.strstart<hb-1?a.strstart:hb-1,b===K?(h(a,!0),0===a.strm.avail_out?ub:vb):a.last_lit&&(h(a,!1),0===a.strm.avail_out)?sb:tb}function q(a,b){for(var c,d,e,f,g=a.window;;){if(a.lookahead<=ib){if(m(a),a.lookahead<=ib&&b===H)return sb;if(0===a.lookahead)break}if(a.match_length=0,a.lookahead>=hb&&a.strstart>0&&(e=a.strstart-1,d=g[e],d===g[++e]&&d===g[++e]&&d===g[++e])){f=a.strstart+ib;do;while(d===g[++e]&&d===g[++e]&&d===g[++e]&&d===g[++e]&&d===g[++e]&&d===g[++e]&&d===g[++e]&&d===g[++e]&&f>e);a.match_length=ib-(f-e),a.match_length>a.lookahead&&(a.match_length=a.lookahead)}if(a.match_length>=hb?(c=D._tr_tally(a,1,a.match_length-hb),a.lookahead-=a.match_length,a.strstart+=a.match_length,a.match_length=0):(c=D._tr_tally(a,0,a.window[a.strstart]),a.lookahead--,a.strstart++),c&&(h(a,!1),0===a.strm.avail_out))return sb}return a.insert=0,b===K?(h(a,!0),0===a.strm.avail_out?ub:vb):a.last_lit&&(h(a,!1),0===a.strm.avail_out)?sb:tb}function r(a,b){for(var c;;){if(0===a.lookahead&&(m(a),0===a.lookahead)){if(b===H)return sb;break}if(a.match_length=0,c=D._tr_tally(a,0,a.window[a.strstart]),a.lookahead--,a.strstart++,c&&(h(a,!1),0===a.strm.avail_out))return sb}return a.insert=0,b===K?(h(a,!0),0===a.strm.avail_out?ub:vb):a.last_lit&&(h(a,!1),0===a.strm.avail_out)?sb:tb}function s(a){a.window_size=2*a.w_size,f(a.head),a.max_lazy_match=B[a.level].max_lazy,a.good_match=B[a.level].good_length,a.nice_match=B[a.level].nice_length,a.max_chain_length=B[a.level].max_chain,a.strstart=0,a.block_start=0,a.lookahead=0,a.insert=0,a.match_length=a.prev_length=hb-1,a.match_available=0,a.ins_h=0}function t(){this.strm=null,this.status=0,this.pending_buf=null,this.pending_buf_size=0,this.pending_out=0,this.pending=0,this.wrap=0,this.gzhead=null,this.gzindex=0,this.method=Y,this.last_flush=-1,this.w_size=0,this.w_bits=0,this.w_mask=0,this.window=null,this.window_size=0,this.prev=null,this.head=null,this.ins_h=0,this.hash_size=0,this.hash_bits=0,this.hash_mask=0,this.hash_shift=0,this.block_start=0,this.match_length=0,this.prev_match=0,this.match_available=0,this.strstart=0,this.match_start=0,this.lookahead=0,this.prev_length=0,this.max_chain_length=0,this.max_lazy_match=0,this.level=0,this.strategy=0,this.good_match=0,this.nice_match=0,this.dyn_ltree=new C.Buf16(2*fb),this.dyn_dtree=new C.Buf16(2*(2*db+1)),this.bl_tree=new C.Buf16(2*(2*eb+1)),f(this.dyn_ltree),f(this.dyn_dtree),f(this.bl_tree),this.l_desc=null,this.d_desc=null,this.bl_desc=null,this.bl_count=new C.Buf16(gb+1),this.heap=new C.Buf16(2*cb+1),f(this.heap),this.heap_len=0,this.heap_max=0,this.depth=new C.Buf16(2*cb+1),f(this.depth),this.l_buf=0,this.lit_bufsize=0,this.last_lit=0,this.d_buf=0,this.opt_len=0,this.static_len=0,this.matches=0,this.insert=0,this.bi_buf=0,this.bi_valid=0}function u(a){var b;return a&&a.state?(a.total_in=a.total_out=0,a.data_type=X,b=a.state,b.pending=0,b.pending_out=0,b.wrap<0&&(b.wrap=-b.wrap),b.status=b.wrap?lb:qb,a.adler=2===b.wrap?0:1,b.last_flush=H,D._tr_init(b),M):d(a,O)}function v(a){var b=u(a);return b===M&&s(a.state),b}function w(a,b){return a&&a.state?2!==a.state.wrap?O:(a.state.gzhead=b,M):O}function x(a,b,c,e,f,g){if(!a)return O;var h=1;if(b===R&&(b=6),0>e?(h=0,e=-e):e>15&&(h=2,e-=16),1>f||f>Z||c!==Y||8>e||e>15||0>b||b>9||0>g||g>V)return d(a,O);8===e&&(e=9);var i=new t;return a.state=i,i.strm=a,i.wrap=h,i.gzhead=null,i.w_bits=e,i.w_size=1<<i.w_bits,i.w_mask=i.w_size-1,i.hash_bits=f+7,i.hash_size=1<<i.hash_bits,i.hash_mask=i.hash_size-1,i.hash_shift=~~((i.hash_bits+hb-1)/hb),i.window=new C.Buf8(2*i.w_size),i.head=new C.Buf16(i.hash_size),i.prev=new C.Buf16(i.w_size),i.lit_bufsize=1<<f+6,i.pending_buf_size=4*i.lit_bufsize,i.pending_buf=new C.Buf8(i.pending_buf_size),i.d_buf=i.lit_bufsize>>1,i.l_buf=3*i.lit_bufsize,i.level=b,i.strategy=g,i.method=c,v(a)}function y(a,b){return x(a,b,Y,$,_,W)}function z(a,b){var c,h,k,l;if(!a||!a.state||b>L||0>b)return a?d(a,O):O;if(h=a.state,!a.output||!a.input&&0!==a.avail_in||h.status===rb&&b!==K)return d(a,0===a.avail_out?Q:O);if(h.strm=a,c=h.last_flush,h.last_flush=b,h.status===lb)if(2===h.wrap)a.adler=0,i(h,31),i(h,139),i(h,8),h.gzhead?(i(h,(h.gzhead.text?1:0)+(h.gzhead.hcrc?2:0)+(h.gzhead.extra?4:0)+(h.gzhead.name?8:0)+(h.gzhead.comment?16:0)),i(h,255&h.gzhead.time),i(h,h.gzhead.time>>8&255),i(h,h.gzhead.time>>16&255),i(h,h.gzhead.time>>24&255),i(h,9===h.level?2:h.strategy>=T||h.level<2?4:0),i(h,255&h.gzhead.os),h.gzhead.extra&&h.gzhead.extra.length&&(i(h,255&h.gzhead.extra.length),i(h,h.gzhead.extra.length>>8&255)),h.gzhead.hcrc&&(a.adler=F(a.adler,h.pending_buf,h.pending,0)),h.gzindex=0,h.status=mb):(i(h,0),i(h,0),i(h,0),i(h,0),i(h,0),i(h,9===h.level?2:h.strategy>=T||h.level<2?4:0),i(h,wb),h.status=qb);else{var m=Y+(h.w_bits-8<<4)<<8,n=-1;n=h.strategy>=T||h.level<2?0:h.level<6?1:6===h.level?2:3,m|=n<<6,0!==h.strstart&&(m|=kb),m+=31-m%31,h.status=qb,j(h,m),0!==h.strstart&&(j(h,a.adler>>>16),j(h,65535&a.adler)),a.adler=1}if(h.status===mb)if(h.gzhead.extra){for(k=h.pending;h.gzindex<(65535&h.gzhead.extra.length)&&(h.pending!==h.pending_buf_size||(h.gzhead.hcrc&&h.pending>k&&(a.adler=F(a.adler,h.pending_buf,h.pending-k,k)),g(a),k=h.pending,h.pending!==h.pending_buf_size));)i(h,255&h.gzhead.extra[h.gzindex]),h.gzindex++;h.gzhead.hcrc&&h.pending>k&&(a.adler=F(a.adler,h.pending_buf,h.pending-k,k)),h.gzindex===h.gzhead.extra.length&&(h.gzindex=0,h.status=nb)}else h.status=nb;if(h.status===nb)if(h.gzhead.name){k=h.pending;do{if(h.pending===h.pending_buf_size&&(h.gzhead.hcrc&&h.pending>k&&(a.adler=F(a.adler,h.pending_buf,h.pending-k,k)),g(a),k=h.pending,h.pending===h.pending_buf_size)){l=1;break}l=h.gzindex<h.gzhead.name.length?255&h.gzhead.name.charCodeAt(h.gzindex++):0,i(h,l)}while(0!==l);h.gzhead.hcrc&&h.pending>k&&(a.adler=F(a.adler,h.pending_buf,h.pending-k,k)),0===l&&(h.gzindex=0,h.status=ob)}else h.status=ob;if(h.status===ob)if(h.gzhead.comment){k=h.pending;do{if(h.pending===h.pending_buf_size&&(h.gzhead.hcrc&&h.pending>k&&(a.adler=F(a.adler,h.pending_buf,h.pending-k,k)),g(a),k=h.pending,h.pending===h.pending_buf_size)){l=1;break}l=h.gzindex<h.gzhead.comment.length?255&h.gzhead.comment.charCodeAt(h.gzindex++):0,i(h,l)}while(0!==l);h.gzhead.hcrc&&h.pending>k&&(a.adler=F(a.adler,h.pending_buf,h.pending-k,k)),0===l&&(h.status=pb)}else h.status=pb;if(h.status===pb&&(h.gzhead.hcrc?(h.pending+2>h.pending_buf_size&&g(a),h.pending+2<=h.pending_buf_size&&(i(h,255&a.adler),i(h,a.adler>>8&255),a.adler=0,h.status=qb)):h.status=qb),0!==h.pending){if(g(a),0===a.avail_out)return h.last_flush=-1,M}else if(0===a.avail_in&&e(b)<=e(c)&&b!==K)return d(a,Q);if(h.status===rb&&0!==a.avail_in)return d(a,Q);if(0!==a.avail_in||0!==h.lookahead||b!==H&&h.status!==rb){var o=h.strategy===T?r(h,b):h.strategy===U?q(h,b):B[h.level].func(h,b);if((o===ub||o===vb)&&(h.status=rb),o===sb||o===ub)return 0===a.avail_out&&(h.last_flush=-1),M;if(o===tb&&(b===I?D._tr_align(h):b!==L&&(D._tr_stored_block(h,0,0,!1),b===J&&(f(h.head),0===h.lookahead&&(h.strstart=0,h.block_start=0,h.insert=0))),g(a),0===a.avail_out))return h.last_flush=-1,M}return b!==K?M:h.wrap<=0?N:(2===h.wrap?(i(h,255&a.adler),i(h,a.adler>>8&255),i(h,a.adler>>16&255),i(h,a.adler>>24&255),i(h,255&a.total_in),i(h,a.total_in>>8&255),i(h,a.total_in>>16&255),i(h,a.total_in>>24&255)):(j(h,a.adler>>>16),j(h,65535&a.adler)),g(a),h.wrap>0&&(h.wrap=-h.wrap),0!==h.pending?M:N)}function A(a){var b;return a&&a.state?(b=a.state.status,b!==lb&&b!==mb&&b!==nb&&b!==ob&&b!==pb&&b!==qb&&b!==rb?d(a,O):(a.state=null,b===qb?d(a,P):M)):O}var B,C=a("../utils/common"),D=a("./trees"),E=a("./adler32"),F=a("./crc32"),G=a("./messages"),H=0,I=1,J=3,K=4,L=5,M=0,N=1,O=-2,P=-3,Q=-5,R=-1,S=1,T=2,U=3,V=4,W=0,X=2,Y=8,Z=9,$=15,_=8,ab=29,bb=256,cb=bb+1+ab,db=30,eb=19,fb=2*cb+1,gb=15,hb=3,ib=258,jb=ib+hb+1,kb=32,lb=42,mb=69,nb=73,ob=91,pb=103,qb=113,rb=666,sb=1,tb=2,ub=3,vb=4,wb=3,xb=function(a,b,c,d,e){this.good_length=a,this.max_lazy=b,this.nice_length=c,this.max_chain=d,this.func=e};B=[new xb(0,0,0,0,n),new xb(4,4,8,4,o),new xb(4,5,16,8,o),new xb(4,6,32,32,o),new xb(4,4,16,16,p),new xb(8,16,32,32,p),new xb(8,16,128,128,p),new xb(8,32,128,256,p),new xb(32,128,258,1024,p),new xb(32,258,258,4096,p)],c.deflateInit=y,c.deflateInit2=x,c.deflateReset=v,c.deflateResetKeep=u,c.deflateSetHeader=w,c.deflate=z,c.deflateEnd=A,c.deflateInfo="pako deflate (from Nodeca project)"},{"../utils/common":27,"./adler32":29,"./crc32":31,"./messages":37,"./trees":38}],33:[function(a,b){"use strict";function c(){this.text=0,this.time=0,this.xflags=0,this.os=0,this.extra=null,this.extra_len=0,this.name="",this.comment="",this.hcrc=0,this.done=!1}b.exports=c},{}],34:[function(a,b){"use strict";var c=30,d=12;b.exports=function(a,b){var e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C;e=a.state,f=a.next_in,B=a.input,g=f+(a.avail_in-5),h=a.next_out,C=a.output,i=h-(b-a.avail_out),j=h+(a.avail_out-257),k=e.dmax,l=e.wsize,m=e.whave,n=e.wnext,o=e.window,p=e.hold,q=e.bits,r=e.lencode,s=e.distcode,t=(1<<e.lenbits)-1,u=(1<<e.distbits)-1;a:do{15>q&&(p+=B[f++]<<q,q+=8,p+=B[f++]<<q,q+=8),v=r[p&t];b:for(;;){if(w=v>>>24,p>>>=w,q-=w,w=v>>>16&255,0===w)C[h++]=65535&v;else{if(!(16&w)){if(0===(64&w)){v=r[(65535&v)+(p&(1<<w)-1)];continue b}if(32&w){e.mode=d;break a}a.msg="invalid literal/length code",e.mode=c;break a}x=65535&v,w&=15,w&&(w>q&&(p+=B[f++]<<q,q+=8),x+=p&(1<<w)-1,p>>>=w,q-=w),15>q&&(p+=B[f++]<<q,q+=8,p+=B[f++]<<q,q+=8),v=s[p&u];c:for(;;){if(w=v>>>24,p>>>=w,q-=w,w=v>>>16&255,!(16&w)){if(0===(64&w)){v=s[(65535&v)+(p&(1<<w)-1)];continue c}a.msg="invalid distance code",e.mode=c;break a}if(y=65535&v,w&=15,w>q&&(p+=B[f++]<<q,q+=8,w>q&&(p+=B[f++]<<q,q+=8)),y+=p&(1<<w)-1,y>k){a.msg="invalid distance too far back",e.mode=c;break a}if(p>>>=w,q-=w,w=h-i,y>w){if(w=y-w,w>m&&e.sane){a.msg="invalid distance too far back",e.mode=c;break a}if(z=0,A=o,0===n){if(z+=l-w,x>w){x-=w;do C[h++]=o[z++];while(--w);z=h-y,A=C}}else if(w>n){if(z+=l+n-w,w-=n,x>w){x-=w;do C[h++]=o[z++];while(--w);if(z=0,x>n){w=n,x-=w;do C[h++]=o[z++];while(--w);z=h-y,A=C}}}else if(z+=n-w,x>w){x-=w;do C[h++]=o[z++];while(--w);z=h-y,A=C}for(;x>2;)C[h++]=A[z++],C[h++]=A[z++],C[h++]=A[z++],x-=3;x&&(C[h++]=A[z++],x>1&&(C[h++]=A[z++]))}else{z=h-y;do C[h++]=C[z++],C[h++]=C[z++],C[h++]=C[z++],x-=3;while(x>2);x&&(C[h++]=C[z++],x>1&&(C[h++]=C[z++]))}break}}break}}while(g>f&&j>h);x=q>>3,f-=x,q-=x<<3,p&=(1<<q)-1,a.next_in=f,a.next_out=h,a.avail_in=g>f?5+(g-f):5-(f-g),a.avail_out=j>h?257+(j-h):257-(h-j),e.hold=p,e.bits=q}},{}],35:[function(a,b,c){"use strict";function d(a){return(a>>>24&255)+(a>>>8&65280)+((65280&a)<<8)+((255&a)<<24)}function e(){this.mode=0,this.last=!1,this.wrap=0,this.havedict=!1,this.flags=0,this.dmax=0,this.check=0,this.total=0,this.head=null,this.wbits=0,this.wsize=0,this.whave=0,this.wnext=0,this.window=null,this.hold=0,this.bits=0,this.length=0,this.offset=0,this.extra=0,this.lencode=null,this.distcode=null,this.lenbits=0,this.distbits=0,this.ncode=0,this.nlen=0,this.ndist=0,this.have=0,this.next=null,this.lens=new r.Buf16(320),this.work=new r.Buf16(288),this.lendyn=null,this.distdyn=null,this.sane=0,this.back=0,this.was=0}function f(a){var b;return a&&a.state?(b=a.state,a.total_in=a.total_out=b.total=0,a.msg="",b.wrap&&(a.adler=1&b.wrap),b.mode=K,b.last=0,b.havedict=0,b.dmax=32768,b.head=null,b.hold=0,b.bits=0,b.lencode=b.lendyn=new r.Buf32(ob),b.distcode=b.distdyn=new r.Buf32(pb),b.sane=1,b.back=-1,C):F}function g(a){var b;return a&&a.state?(b=a.state,b.wsize=0,b.whave=0,b.wnext=0,f(a)):F}function h(a,b){var c,d;return a&&a.state?(d=a.state,0>b?(c=0,b=-b):(c=(b>>4)+1,48>b&&(b&=15)),b&&(8>b||b>15)?F:(null!==d.window&&d.wbits!==b&&(d.window=null),d.wrap=c,d.wbits=b,g(a))):F}function i(a,b){var c,d;return a?(d=new e,a.state=d,d.window=null,c=h(a,b),c!==C&&(a.state=null),c):F}function j(a){return i(a,rb)}function k(a){if(sb){var b;for(p=new r.Buf32(512),q=new r.Buf32(32),b=0;144>b;)a.lens[b++]=8;for(;256>b;)a.lens[b++]=9;for(;280>b;)a.lens[b++]=7;for(;288>b;)a.lens[b++]=8;for(v(x,a.lens,0,288,p,0,a.work,{bits:9}),b=0;32>b;)a.lens[b++]=5;v(y,a.lens,0,32,q,0,a.work,{bits:5}),sb=!1}a.lencode=p,a.lenbits=9,a.distcode=q,a.distbits=5}function l(a,b,c,d){var e,f=a.state;return null===f.window&&(f.wsize=1<<f.wbits,f.wnext=0,f.whave=0,f.window=new r.Buf8(f.wsize)),d>=f.wsize?(r.arraySet(f.window,b,c-f.wsize,f.wsize,0),f.wnext=0,f.whave=f.wsize):(e=f.wsize-f.wnext,e>d&&(e=d),r.arraySet(f.window,b,c-d,e,f.wnext),d-=e,d?(r.arraySet(f.window,b,c-d,d,0),f.wnext=d,f.whave=f.wsize):(f.wnext+=e,f.wnext===f.wsize&&(f.wnext=0),f.whave<f.wsize&&(f.whave+=e))),0}function m(a,b){var c,e,f,g,h,i,j,m,n,o,p,q,ob,pb,qb,rb,sb,tb,ub,vb,wb,xb,yb,zb,Ab=0,Bb=new r.Buf8(4),Cb=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15];if(!a||!a.state||!a.output||!a.input&&0!==a.avail_in)return F;c=a.state,c.mode===V&&(c.mode=W),h=a.next_out,f=a.output,j=a.avail_out,g=a.next_in,e=a.input,i=a.avail_in,m=c.hold,n=c.bits,o=i,p=j,xb=C;a:for(;;)switch(c.mode){case K:if(0===c.wrap){c.mode=W;break}for(;16>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}if(2&c.wrap&&35615===m){c.check=0,Bb[0]=255&m,Bb[1]=m>>>8&255,c.check=t(c.check,Bb,2,0),m=0,n=0,c.mode=L;break}if(c.flags=0,c.head&&(c.head.done=!1),!(1&c.wrap)||(((255&m)<<8)+(m>>8))%31){a.msg="incorrect header check",c.mode=lb;break}if((15&m)!==J){a.msg="unknown compression method",c.mode=lb;break}if(m>>>=4,n-=4,wb=(15&m)+8,0===c.wbits)c.wbits=wb;else if(wb>c.wbits){a.msg="invalid window size",c.mode=lb;break}c.dmax=1<<wb,a.adler=c.check=1,c.mode=512&m?T:V,m=0,n=0;break;case L:for(;16>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}if(c.flags=m,(255&c.flags)!==J){a.msg="unknown compression method",c.mode=lb;break}if(57344&c.flags){a.msg="unknown header flags set",c.mode=lb;break}c.head&&(c.head.text=m>>8&1),512&c.flags&&(Bb[0]=255&m,Bb[1]=m>>>8&255,c.check=t(c.check,Bb,2,0)),m=0,n=0,c.mode=M;case M:for(;32>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}c.head&&(c.head.time=m),512&c.flags&&(Bb[0]=255&m,Bb[1]=m>>>8&255,Bb[2]=m>>>16&255,Bb[3]=m>>>24&255,c.check=t(c.check,Bb,4,0)),m=0,n=0,c.mode=N;case N:for(;16>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}c.head&&(c.head.xflags=255&m,c.head.os=m>>8),512&c.flags&&(Bb[0]=255&m,Bb[1]=m>>>8&255,c.check=t(c.check,Bb,2,0)),m=0,n=0,c.mode=O;case O:if(1024&c.flags){for(;16>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}c.length=m,c.head&&(c.head.extra_len=m),512&c.flags&&(Bb[0]=255&m,Bb[1]=m>>>8&255,c.check=t(c.check,Bb,2,0)),m=0,n=0}else c.head&&(c.head.extra=null);c.mode=P;case P:if(1024&c.flags&&(q=c.length,q>i&&(q=i),q&&(c.head&&(wb=c.head.extra_len-c.length,c.head.extra||(c.head.extra=new Array(c.head.extra_len)),r.arraySet(c.head.extra,e,g,q,wb)),512&c.flags&&(c.check=t(c.check,e,q,g)),i-=q,g+=q,c.length-=q),c.length))break a;c.length=0,c.mode=Q;case Q:if(2048&c.flags){if(0===i)break a;q=0;do wb=e[g+q++],c.head&&wb&&c.length<65536&&(c.head.name+=String.fromCharCode(wb));while(wb&&i>q);if(512&c.flags&&(c.check=t(c.check,e,q,g)),i-=q,g+=q,wb)break a}else c.head&&(c.head.name=null);c.length=0,c.mode=R;case R:if(4096&c.flags){if(0===i)break a;q=0;do wb=e[g+q++],c.head&&wb&&c.length<65536&&(c.head.comment+=String.fromCharCode(wb));while(wb&&i>q);if(512&c.flags&&(c.check=t(c.check,e,q,g)),i-=q,g+=q,wb)break a}else c.head&&(c.head.comment=null);c.mode=S;case S:if(512&c.flags){for(;16>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}if(m!==(65535&c.check)){a.msg="header crc mismatch",c.mode=lb;break}m=0,n=0}c.head&&(c.head.hcrc=c.flags>>9&1,c.head.done=!0),a.adler=c.check=0,c.mode=V;break;case T:for(;32>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}a.adler=c.check=d(m),m=0,n=0,c.mode=U;case U:if(0===c.havedict)return a.next_out=h,a.avail_out=j,a.next_in=g,a.avail_in=i,c.hold=m,c.bits=n,E;a.adler=c.check=1,c.mode=V;case V:if(b===A||b===B)break a;case W:if(c.last){m>>>=7&n,n-=7&n,c.mode=ib;break}for(;3>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}switch(c.last=1&m,m>>>=1,n-=1,3&m){case 0:c.mode=X;break;case 1:if(k(c),c.mode=bb,b===B){m>>>=2,n-=2;break a}break;case 2:c.mode=$;break;case 3:a.msg="invalid block type",c.mode=lb}m>>>=2,n-=2;break;case X:for(m>>>=7&n,n-=7&n;32>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}if((65535&m)!==(m>>>16^65535)){a.msg="invalid stored block lengths",c.mode=lb;break}if(c.length=65535&m,m=0,n=0,c.mode=Y,b===B)break a;case Y:c.mode=Z;case Z:if(q=c.length){if(q>i&&(q=i),q>j&&(q=j),0===q)break a;r.arraySet(f,e,g,q,h),i-=q,g+=q,j-=q,h+=q,c.length-=q;break}c.mode=V;break;case $:for(;14>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}if(c.nlen=(31&m)+257,m>>>=5,n-=5,c.ndist=(31&m)+1,m>>>=5,n-=5,c.ncode=(15&m)+4,m>>>=4,n-=4,c.nlen>286||c.ndist>30){a.msg="too many length or distance symbols",c.mode=lb;break}c.have=0,c.mode=_;case _:for(;c.have<c.ncode;){for(;3>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}c.lens[Cb[c.have++]]=7&m,m>>>=3,n-=3}for(;c.have<19;)c.lens[Cb[c.have++]]=0;if(c.lencode=c.lendyn,c.lenbits=7,yb={bits:c.lenbits},xb=v(w,c.lens,0,19,c.lencode,0,c.work,yb),c.lenbits=yb.bits,xb){a.msg="invalid code lengths set",c.mode=lb;break}c.have=0,c.mode=ab;case ab:for(;c.have<c.nlen+c.ndist;){for(;Ab=c.lencode[m&(1<<c.lenbits)-1],qb=Ab>>>24,rb=Ab>>>16&255,sb=65535&Ab,!(n>=qb);){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}if(16>sb)m>>>=qb,n-=qb,c.lens[c.have++]=sb;else{if(16===sb){for(zb=qb+2;zb>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}if(m>>>=qb,n-=qb,0===c.have){a.msg="invalid bit length repeat",c.mode=lb;break}wb=c.lens[c.have-1],q=3+(3&m),m>>>=2,n-=2}else if(17===sb){for(zb=qb+3;zb>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}m>>>=qb,n-=qb,wb=0,q=3+(7&m),m>>>=3,n-=3}else{for(zb=qb+7;zb>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}m>>>=qb,n-=qb,wb=0,q=11+(127&m),m>>>=7,n-=7}if(c.have+q>c.nlen+c.ndist){a.msg="invalid bit length repeat",c.mode=lb;break}for(;q--;)c.lens[c.have++]=wb}}if(c.mode===lb)break;if(0===c.lens[256]){a.msg="invalid code -- missing end-of-block",c.mode=lb;break}if(c.lenbits=9,yb={bits:c.lenbits},xb=v(x,c.lens,0,c.nlen,c.lencode,0,c.work,yb),c.lenbits=yb.bits,xb){a.msg="invalid literal/lengths set",c.mode=lb;break}if(c.distbits=6,c.distcode=c.distdyn,yb={bits:c.distbits},xb=v(y,c.lens,c.nlen,c.ndist,c.distcode,0,c.work,yb),c.distbits=yb.bits,xb){a.msg="invalid distances set",c.mode=lb;break}if(c.mode=bb,b===B)break a;case bb:c.mode=cb;case cb:if(i>=6&&j>=258){a.next_out=h,a.avail_out=j,a.next_in=g,a.avail_in=i,c.hold=m,c.bits=n,u(a,p),h=a.next_out,f=a.output,j=a.avail_out,g=a.next_in,e=a.input,i=a.avail_in,m=c.hold,n=c.bits,c.mode===V&&(c.back=-1);break}for(c.back=0;Ab=c.lencode[m&(1<<c.lenbits)-1],qb=Ab>>>24,rb=Ab>>>16&255,sb=65535&Ab,!(n>=qb);){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}if(rb&&0===(240&rb)){for(tb=qb,ub=rb,vb=sb;Ab=c.lencode[vb+((m&(1<<tb+ub)-1)>>tb)],qb=Ab>>>24,rb=Ab>>>16&255,sb=65535&Ab,!(n>=tb+qb);){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}m>>>=tb,n-=tb,c.back+=tb}if(m>>>=qb,n-=qb,c.back+=qb,c.length=sb,0===rb){c.mode=hb;break}if(32&rb){c.back=-1,c.mode=V;break}if(64&rb){a.msg="invalid literal/length code",c.mode=lb;break}c.extra=15&rb,c.mode=db;case db:if(c.extra){for(zb=c.extra;zb>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}c.length+=m&(1<<c.extra)-1,m>>>=c.extra,n-=c.extra,c.back+=c.extra}c.was=c.length,c.mode=eb;case eb:for(;Ab=c.distcode[m&(1<<c.distbits)-1],qb=Ab>>>24,rb=Ab>>>16&255,sb=65535&Ab,!(n>=qb);){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}if(0===(240&rb)){for(tb=qb,ub=rb,vb=sb;Ab=c.distcode[vb+((m&(1<<tb+ub)-1)>>tb)],qb=Ab>>>24,rb=Ab>>>16&255,sb=65535&Ab,!(n>=tb+qb);){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}m>>>=tb,n-=tb,c.back+=tb}if(m>>>=qb,n-=qb,c.back+=qb,64&rb){a.msg="invalid distance code",c.mode=lb;break}c.offset=sb,c.extra=15&rb,c.mode=fb;case fb:if(c.extra){for(zb=c.extra;zb>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}c.offset+=m&(1<<c.extra)-1,m>>>=c.extra,n-=c.extra,c.back+=c.extra}if(c.offset>c.dmax){a.msg="invalid distance too far back",c.mode=lb;break}c.mode=gb;case gb:if(0===j)break a;if(q=p-j,c.offset>q){if(q=c.offset-q,q>c.whave&&c.sane){a.msg="invalid distance too far back",c.mode=lb;break}q>c.wnext?(q-=c.wnext,ob=c.wsize-q):ob=c.wnext-q,q>c.length&&(q=c.length),pb=c.window}else pb=f,ob=h-c.offset,q=c.length;q>j&&(q=j),j-=q,c.length-=q;do f[h++]=pb[ob++];while(--q);0===c.length&&(c.mode=cb);break;case hb:if(0===j)break a;f[h++]=c.length,j--,c.mode=cb;break;case ib:if(c.wrap){for(;32>n;){if(0===i)break a;i--,m|=e[g++]<<n,n+=8}if(p-=j,a.total_out+=p,c.total+=p,p&&(a.adler=c.check=c.flags?t(c.check,f,p,h-p):s(c.check,f,p,h-p)),p=j,(c.flags?m:d(m))!==c.check){a.msg="incorrect data check",c.mode=lb;break}m=0,n=0}c.mode=jb;case jb:if(c.wrap&&c.flags){for(;32>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}if(m!==(4294967295&c.total)){a.msg="incorrect length check",c.mode=lb;break}m=0,n=0}c.mode=kb;case kb:xb=D;break a;case lb:xb=G;break a;case mb:return H;case nb:default:return F}return a.next_out=h,a.avail_out=j,a.next_in=g,a.avail_in=i,c.hold=m,c.bits=n,(c.wsize||p!==a.avail_out&&c.mode<lb&&(c.mode<ib||b!==z))&&l(a,a.output,a.next_out,p-a.avail_out)?(c.mode=mb,H):(o-=a.avail_in,p-=a.avail_out,a.total_in+=o,a.total_out+=p,c.total+=p,c.wrap&&p&&(a.adler=c.check=c.flags?t(c.check,f,p,a.next_out-p):s(c.check,f,p,a.next_out-p)),a.data_type=c.bits+(c.last?64:0)+(c.mode===V?128:0)+(c.mode===bb||c.mode===Y?256:0),(0===o&&0===p||b===z)&&xb===C&&(xb=I),xb)}function n(a){if(!a||!a.state)return F;var b=a.state;return b.window&&(b.window=null),a.state=null,C}function o(a,b){var c;return a&&a.state?(c=a.state,0===(2&c.wrap)?F:(c.head=b,b.done=!1,C)):F}var p,q,r=a("../utils/common"),s=a("./adler32"),t=a("./crc32"),u=a("./inffast"),v=a("./inftrees"),w=0,x=1,y=2,z=4,A=5,B=6,C=0,D=1,E=2,F=-2,G=-3,H=-4,I=-5,J=8,K=1,L=2,M=3,N=4,O=5,P=6,Q=7,R=8,S=9,T=10,U=11,V=12,W=13,X=14,Y=15,Z=16,$=17,_=18,ab=19,bb=20,cb=21,db=22,eb=23,fb=24,gb=25,hb=26,ib=27,jb=28,kb=29,lb=30,mb=31,nb=32,ob=852,pb=592,qb=15,rb=qb,sb=!0;c.inflateReset=g,c.inflateReset2=h,c.inflateResetKeep=f,c.inflateInit=j,c.inflateInit2=i,c.inflate=m,c.inflateEnd=n,c.inflateGetHeader=o,c.inflateInfo="pako inflate (from Nodeca project)"},{"../utils/common":27,"./adler32":29,"./crc32":31,"./inffast":34,"./inftrees":36}],36:[function(a,b){"use strict";var c=a("../utils/common"),d=15,e=852,f=592,g=0,h=1,i=2,j=[3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258,0,0],k=[16,16,16,16,16,16,16,16,17,17,17,17,18,18,18,18,19,19,19,19,20,20,20,20,21,21,21,21,16,72,78],l=[1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0],m=[16,16,16,16,17,17,18,18,19,19,20,20,21,21,22,22,23,23,24,24,25,25,26,26,27,27,28,28,29,29,64,64];b.exports=function(a,b,n,o,p,q,r,s){var t,u,v,w,x,y,z,A,B,C=s.bits,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=null,O=0,P=new c.Buf16(d+1),Q=new c.Buf16(d+1),R=null,S=0;for(D=0;d>=D;D++)P[D]=0;for(E=0;o>E;E++)P[b[n+E]]++;for(H=C,G=d;G>=1&&0===P[G];G--);if(H>G&&(H=G),0===G)return p[q++]=20971520,p[q++]=20971520,s.bits=1,0;for(F=1;G>F&&0===P[F];F++);for(F>H&&(H=F),K=1,D=1;d>=D;D++)if(K<<=1,K-=P[D],0>K)return-1;if(K>0&&(a===g||1!==G))return-1;for(Q[1]=0,D=1;d>D;D++)Q[D+1]=Q[D]+P[D];for(E=0;o>E;E++)0!==b[n+E]&&(r[Q[b[n+E]]++]=E);if(a===g?(N=R=r,y=19):a===h?(N=j,O-=257,R=k,S-=257,y=256):(N=l,R=m,y=-1),M=0,E=0,D=F,x=q,I=H,J=0,v=-1,L=1<<H,w=L-1,a===h&&L>e||a===i&&L>f)return 1;for(var T=0;;){T++,z=D-J,r[E]<y?(A=0,B=r[E]):r[E]>y?(A=R[S+r[E]],B=N[O+r[E]]):(A=96,B=0),t=1<<D-J,u=1<<I,F=u;do u-=t,p[x+(M>>J)+u]=z<<24|A<<16|B|0;while(0!==u);for(t=1<<D-1;M&t;)t>>=1;if(0!==t?(M&=t-1,M+=t):M=0,E++,0===--P[D]){if(D===G)break;D=b[n+r[E]]}if(D>H&&(M&w)!==v){for(0===J&&(J=H),x+=F,I=D-J,K=1<<I;G>I+J&&(K-=P[I+J],!(0>=K));)I++,K<<=1;if(L+=1<<I,a===h&&L>e||a===i&&L>f)return 1;v=M&w,p[v]=H<<24|I<<16|x-q|0}}return 0!==M&&(p[x+M]=D-J<<24|64<<16|0),s.bits=H,0}},{"../utils/common":27}],37:[function(a,b){"use strict";b.exports={2:"need dictionary",1:"stream end",0:"","-1":"file error","-2":"stream error","-3":"data error","-4":"insufficient memory","-5":"buffer error","-6":"incompatible version"}},{}],38:[function(a,b,c){"use strict";function d(a){for(var b=a.length;--b>=0;)a[b]=0}function e(a){return 256>a?gb[a]:gb[256+(a>>>7)]}function f(a,b){a.pending_buf[a.pending++]=255&b,a.pending_buf[a.pending++]=b>>>8&255}function g(a,b,c){a.bi_valid>V-c?(a.bi_buf|=b<<a.bi_valid&65535,f(a,a.bi_buf),a.bi_buf=b>>V-a.bi_valid,a.bi_valid+=c-V):(a.bi_buf|=b<<a.bi_valid&65535,a.bi_valid+=c)}function h(a,b,c){g(a,c[2*b],c[2*b+1])}function i(a,b){var c=0;do c|=1&a,a>>>=1,c<<=1;while(--b>0);return c>>>1}function j(a){16===a.bi_valid?(f(a,a.bi_buf),a.bi_buf=0,a.bi_valid=0):a.bi_valid>=8&&(a.pending_buf[a.pending++]=255&a.bi_buf,a.bi_buf>>=8,a.bi_valid-=8)}function k(a,b){var c,d,e,f,g,h,i=b.dyn_tree,j=b.max_code,k=b.stat_desc.static_tree,l=b.stat_desc.has_stree,m=b.stat_desc.extra_bits,n=b.stat_desc.extra_base,o=b.stat_desc.max_length,p=0;for(f=0;U>=f;f++)a.bl_count[f]=0;for(i[2*a.heap[a.heap_max]+1]=0,c=a.heap_max+1;T>c;c++)d=a.heap[c],f=i[2*i[2*d+1]+1]+1,f>o&&(f=o,p++),i[2*d+1]=f,d>j||(a.bl_count[f]++,g=0,d>=n&&(g=m[d-n]),h=i[2*d],a.opt_len+=h*(f+g),l&&(a.static_len+=h*(k[2*d+1]+g)));if(0!==p){do{for(f=o-1;0===a.bl_count[f];)f--;a.bl_count[f]--,a.bl_count[f+1]+=2,a.bl_count[o]--,p-=2}while(p>0);for(f=o;0!==f;f--)for(d=a.bl_count[f];0!==d;)e=a.heap[--c],e>j||(i[2*e+1]!==f&&(a.opt_len+=(f-i[2*e+1])*i[2*e],i[2*e+1]=f),d--)}}function l(a,b,c){var d,e,f=new Array(U+1),g=0;for(d=1;U>=d;d++)f[d]=g=g+c[d-1]<<1;for(e=0;b>=e;e++){var h=a[2*e+1];0!==h&&(a[2*e]=i(f[h]++,h))}}function m(){var a,b,c,d,e,f=new Array(U+1);for(c=0,d=0;O-1>d;d++)for(ib[d]=c,a=0;a<1<<_[d];a++)hb[c++]=d;for(hb[c-1]=d,e=0,d=0;16>d;d++)for(jb[d]=e,a=0;a<1<<ab[d];a++)gb[e++]=d;for(e>>=7;R>d;d++)for(jb[d]=e<<7,a=0;a<1<<ab[d]-7;a++)gb[256+e++]=d;for(b=0;U>=b;b++)f[b]=0;for(a=0;143>=a;)eb[2*a+1]=8,a++,f[8]++;for(;255>=a;)eb[2*a+1]=9,a++,f[9]++;for(;279>=a;)eb[2*a+1]=7,a++,f[7]++;for(;287>=a;)eb[2*a+1]=8,a++,f[8]++;for(l(eb,Q+1,f),a=0;R>a;a++)fb[2*a+1]=5,fb[2*a]=i(a,5);kb=new nb(eb,_,P+1,Q,U),lb=new nb(fb,ab,0,R,U),mb=new nb(new Array(0),bb,0,S,W)}function n(a){var b;for(b=0;Q>b;b++)a.dyn_ltree[2*b]=0;for(b=0;R>b;b++)a.dyn_dtree[2*b]=0;for(b=0;S>b;b++)a.bl_tree[2*b]=0;a.dyn_ltree[2*X]=1,a.opt_len=a.static_len=0,a.last_lit=a.matches=0}function o(a){a.bi_valid>8?f(a,a.bi_buf):a.bi_valid>0&&(a.pending_buf[a.pending++]=a.bi_buf),a.bi_buf=0,a.bi_valid=0}function p(a,b,c,d){o(a),d&&(f(a,c),f(a,~c)),E.arraySet(a.pending_buf,a.window,b,c,a.pending),a.pending+=c}function q(a,b,c,d){var e=2*b,f=2*c;return a[e]<a[f]||a[e]===a[f]&&d[b]<=d[c]}function r(a,b,c){for(var d=a.heap[c],e=c<<1;e<=a.heap_len&&(e<a.heap_len&&q(b,a.heap[e+1],a.heap[e],a.depth)&&e++,!q(b,d,a.heap[e],a.depth));)a.heap[c]=a.heap[e],c=e,e<<=1;a.heap[c]=d}function s(a,b,c){var d,f,i,j,k=0;if(0!==a.last_lit)do d=a.pending_buf[a.d_buf+2*k]<<8|a.pending_buf[a.d_buf+2*k+1],f=a.pending_buf[a.l_buf+k],k++,0===d?h(a,f,b):(i=hb[f],h(a,i+P+1,b),j=_[i],0!==j&&(f-=ib[i],g(a,f,j)),d--,i=e(d),h(a,i,c),j=ab[i],0!==j&&(d-=jb[i],g(a,d,j)));while(k<a.last_lit);h(a,X,b)}function t(a,b){var c,d,e,f=b.dyn_tree,g=b.stat_desc.static_tree,h=b.stat_desc.has_stree,i=b.stat_desc.elems,j=-1;for(a.heap_len=0,a.heap_max=T,c=0;i>c;c++)0!==f[2*c]?(a.heap[++a.heap_len]=j=c,a.depth[c]=0):f[2*c+1]=0;for(;a.heap_len<2;)e=a.heap[++a.heap_len]=2>j?++j:0,f[2*e]=1,a.depth[e]=0,a.opt_len--,h&&(a.static_len-=g[2*e+1]);for(b.max_code=j,c=a.heap_len>>1;c>=1;c--)r(a,f,c);e=i;do c=a.heap[1],a.heap[1]=a.heap[a.heap_len--],r(a,f,1),d=a.heap[1],a.heap[--a.heap_max]=c,a.heap[--a.heap_max]=d,f[2*e]=f[2*c]+f[2*d],a.depth[e]=(a.depth[c]>=a.depth[d]?a.depth[c]:a.depth[d])+1,f[2*c+1]=f[2*d+1]=e,a.heap[1]=e++,r(a,f,1);while(a.heap_len>=2);a.heap[--a.heap_max]=a.heap[1],k(a,b),l(f,j,a.bl_count)}function u(a,b,c){var d,e,f=-1,g=b[1],h=0,i=7,j=4;for(0===g&&(i=138,j=3),b[2*(c+1)+1]=65535,d=0;c>=d;d++)e=g,g=b[2*(d+1)+1],++h<i&&e===g||(j>h?a.bl_tree[2*e]+=h:0!==e?(e!==f&&a.bl_tree[2*e]++,a.bl_tree[2*Y]++):10>=h?a.bl_tree[2*Z]++:a.bl_tree[2*$]++,h=0,f=e,0===g?(i=138,j=3):e===g?(i=6,j=3):(i=7,j=4))}function v(a,b,c){var d,e,f=-1,i=b[1],j=0,k=7,l=4;for(0===i&&(k=138,l=3),d=0;c>=d;d++)if(e=i,i=b[2*(d+1)+1],!(++j<k&&e===i)){if(l>j){do h(a,e,a.bl_tree);while(0!==--j)}else 0!==e?(e!==f&&(h(a,e,a.bl_tree),j--),h(a,Y,a.bl_tree),g(a,j-3,2)):10>=j?(h(a,Z,a.bl_tree),g(a,j-3,3)):(h(a,$,a.bl_tree),g(a,j-11,7));j=0,f=e,0===i?(k=138,l=3):e===i?(k=6,l=3):(k=7,l=4)}}function w(a){var b;for(u(a,a.dyn_ltree,a.l_desc.max_code),u(a,a.dyn_dtree,a.d_desc.max_code),t(a,a.bl_desc),b=S-1;b>=3&&0===a.bl_tree[2*cb[b]+1];b--);return a.opt_len+=3*(b+1)+5+5+4,b}function x(a,b,c,d){var e;for(g(a,b-257,5),g(a,c-1,5),g(a,d-4,4),e=0;d>e;e++)g(a,a.bl_tree[2*cb[e]+1],3);v(a,a.dyn_ltree,b-1),v(a,a.dyn_dtree,c-1)}function y(a){var b,c=4093624447;for(b=0;31>=b;b++,c>>>=1)if(1&c&&0!==a.dyn_ltree[2*b])return G;if(0!==a.dyn_ltree[18]||0!==a.dyn_ltree[20]||0!==a.dyn_ltree[26])return H;for(b=32;P>b;b++)if(0!==a.dyn_ltree[2*b])return H;return G}function z(a){pb||(m(),pb=!0),a.l_desc=new ob(a.dyn_ltree,kb),a.d_desc=new ob(a.dyn_dtree,lb),a.bl_desc=new ob(a.bl_tree,mb),a.bi_buf=0,a.bi_valid=0,n(a)}function A(a,b,c,d){g(a,(J<<1)+(d?1:0),3),p(a,b,c,!0)}function B(a){g(a,K<<1,3),h(a,X,eb),j(a)}function C(a,b,c,d){var e,f,h=0;a.level>0?(a.strm.data_type===I&&(a.strm.data_type=y(a)),t(a,a.l_desc),t(a,a.d_desc),h=w(a),e=a.opt_len+3+7>>>3,f=a.static_len+3+7>>>3,e>=f&&(e=f)):e=f=c+5,e>=c+4&&-1!==b?A(a,b,c,d):a.strategy===F||f===e?(g(a,(K<<1)+(d?1:0),3),s(a,eb,fb)):(g(a,(L<<1)+(d?1:0),3),x(a,a.l_desc.max_code+1,a.d_desc.max_code+1,h+1),s(a,a.dyn_ltree,a.dyn_dtree)),n(a),d&&o(a)}function D(a,b,c){return a.pending_buf[a.d_buf+2*a.last_lit]=b>>>8&255,a.pending_buf[a.d_buf+2*a.last_lit+1]=255&b,a.pending_buf[a.l_buf+a.last_lit]=255&c,a.last_lit++,0===b?a.dyn_ltree[2*c]++:(a.matches++,b--,a.dyn_ltree[2*(hb[c]+P+1)]++,a.dyn_dtree[2*e(b)]++),a.last_lit===a.lit_bufsize-1}var E=a("../utils/common"),F=4,G=0,H=1,I=2,J=0,K=1,L=2,M=3,N=258,O=29,P=256,Q=P+1+O,R=30,S=19,T=2*Q+1,U=15,V=16,W=7,X=256,Y=16,Z=17,$=18,_=[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0],ab=[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13],bb=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7],cb=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],db=512,eb=new Array(2*(Q+2));d(eb);var fb=new Array(2*R);d(fb);var gb=new Array(db);d(gb);var hb=new Array(N-M+1);d(hb);var ib=new Array(O);d(ib);var jb=new Array(R);d(jb);var kb,lb,mb,nb=function(a,b,c,d,e){this.static_tree=a,this.extra_bits=b,this.extra_base=c,this.elems=d,this.max_length=e,this.has_stree=a&&a.length},ob=function(a,b){this.dyn_tree=a,this.max_code=0,this.stat_desc=b},pb=!1;c._tr_init=z,c._tr_stored_block=A,c._tr_flush_block=C,c._tr_tally=D,c._tr_align=B},{"../utils/common":27}],39:[function(a,b){"use strict";function c(){this.input=null,this.next_in=0,this.avail_in=0,this.total_in=0,this.output=null,this.next_out=0,this.avail_out=0,this.total_out=0,this.msg="",this.state=null,this.data_type=2,this.adler=0}b.exports=c},{}]},{},[9])(9)});'use strict';if(tr.isVinn){global.JSZip=global.window.JSZip;global.window=undefined;}else if(tr.isNode){const jsZipAbsPath=HTMLImportsLoader.hrefToAbsolutePath('/jszip.min.js');const jsZipModule=require(jsZipAbsPath);global.JSZip=jsZipModule;}'use strict';tr.exportTo('tr.e.importer',function(){function ZipImporter(model,eventData){if(eventData instanceof ArrayBuffer){eventData=new Uint8Array(eventData);}
+this.model_=model;this.eventData_=eventData;}
+ZipImporter.canImport=function(eventData){let header;if(eventData instanceof ArrayBuffer){header=new Uint8Array(eventData.slice(0,2));}else if(typeof(eventData)==='string'||eventData instanceof String){header=[eventData.charCodeAt(0),eventData.charCodeAt(1)];}else{return false;}
+return header[0]==='P'.charCodeAt(0)&&header[1]==='K'.charCodeAt(0);};ZipImporter.prototype={__proto__:tr.importer.Importer.prototype,get importerName(){return'ZipImporter';},isTraceDataContainer(){return true;},extractSubtraces(){const zip=new JSZip(this.eventData_);const subtraces=[];for(const idx in zip.files){subtraces.push(zip.files[idx].asBinary());}
+return subtraces;}};tr.importer.Importer.register(ZipImporter);return{ZipImporter,};});'use strict';tr.exportTo('tr.model',function(){function HeapEntry(heapDump,leafStackFrame,objectTypeName,size,count,valuesAreTotals){this.heapDump=heapDump;this.leafStackFrame=leafStackFrame;this.objectTypeName=objectTypeName;this.size=size;this.count=count;this.valuesAreTotals=valuesAreTotals;}
+function HeapDump(processMemoryDump,allocatorName,isComplete){this.processMemoryDump=processMemoryDump;this.allocatorName=allocatorName;this.isComplete=isComplete;this.entries=[];}
+HeapDump.prototype={addEntry(leafStackFrame,objectTypeName,size,count,opt_valuesAreTotals){if(opt_valuesAreTotals===undefined)opt_valuesAreTotals=true;const valuesAreTotals=opt_valuesAreTotals;const entry=new HeapEntry(this,leafStackFrame,objectTypeName,size,count,valuesAreTotals);this.entries.push(entry);return entry;}};return{HeapEntry,HeapDump,};});'use strict';tr.exportTo('tr.e.importer',function(){function HeapDumpTraceEventImporter(heapProfileExpander,stackFrames,processMemoryDump,idPrefix,model){this.expander=heapProfileExpander;this.stackFrames=stackFrames;this.processMemoryDump=processMemoryDump;this.idPrefix=idPrefix;this.model=model;}
+HeapDumpTraceEventImporter.prototype={getLeafStackFrame(stackFrameId){if(stackFrameId==='')return undefined;const parentId=this.idPrefix+stackFrameId;const id=parentId+':self';if(!this.stackFrames[id]){const parentStackFrame=this.stackFrames[parentId];const stackFrame=new tr.model.StackFrame(parentStackFrame,id,'<self>',undefined);this.model.addStackFrame(stackFrame);}
+return this.stackFrames[id];},parseEntry(entry,heapDump){const size=entry.size;const count=entry.count;const leafStackFrame=this.getLeafStackFrame(entry.node.id);const objectTypeName=entry.type.name;const valuesAreTotals=false;if(objectTypeName===undefined){this.model_.importWarning({type:'memory_dump_parse_error',message:'Missing object type name (ID '+typeId+')',});}
+heapDump.addEntry(leafStackFrame,objectTypeName,size,count,valuesAreTotals);},parse(){const heapDumps={};const inflated=this.expander.inflated;for(const[allocatorName,entries]of Object.entries(inflated)){const heapDump=new tr.model.HeapDump(this.processMemoryDump,allocatorName);for(const entry of entries){this.parseEntry(entry,heapDump);}
+heapDump.isComplete=true;heapDumps[allocatorName]=heapDump;}
+return heapDumps;},};return{HeapDumpTraceEventImporter,};});'use strict';tr.exportTo('tr.e.importer',function(){function LegacyHeapDumpTraceEventImporter(model,processMemoryDump,processObjectTypeNameMap,idPrefix,dumpId,rawHeapDumps){this.model_=model;this.processObjectTypeNameMap_=processObjectTypeNameMap;this.idPrefix_=idPrefix;this.processMemoryDump_=processMemoryDump;this.pid_=this.processMemoryDump_.process.pid;this.dumpId_=dumpId;this.rawHeapDumps_=rawHeapDumps;}
+LegacyHeapDumpTraceEventImporter.prototype={parseRawHeapDump(rawHeapDump,allocatorName){const model=this.model_;const processMemoryDump=this.processMemoryDump_;const heapDump=new tr.model.HeapDump(processMemoryDump,allocatorName);const entries=rawHeapDump.entries;if(entries===undefined||entries.length===0){this.model_.importWarning({type:'memory_dump_parse_error',message:'No heap entries in a '+allocatorName+' heap dump for PID='+this.pid_+' and dump ID='+this.dumpId_+'.'});return undefined;}
+const isOldFormat=entries[0].bt===undefined;if(!isOldFormat&&this.processObjectTypeNameMap_===undefined){return undefined;}
+for(let i=0;i<entries.length;i++){const entry=entries[i];const size=parseInt(entry.size,16);const leafStackFrameIndex=entry.bt;let leafStackFrame;if(isOldFormat){if(leafStackFrameIndex===undefined){leafStackFrame=undefined;}else{let leafStackFrameId=this.idPrefix_+leafStackFrameIndex;if(leafStackFrameIndex===''){leafStackFrame=undefined;}else{leafStackFrame=model.stackFrames[leafStackFrameId];if(leafStackFrame===undefined){this.model_.importWarning({type:'memory_dump_parse_error',message:'Missing leaf stack frame (ID '+
+leafStackFrameId+') of heap entry '+i+' (size '+
+size+') in a '+allocatorName+' heap dump for PID='+this.pid_+'.'});continue;}}
+leafStackFrameId+=':self';if(model.stackFrames[leafStackFrameId]!==undefined){leafStackFrame=model.stackFrames[leafStackFrameId];}else{leafStackFrame=new tr.model.StackFrame(leafStackFrame,leafStackFrameId,'<self>',undefined);model.addStackFrame(leafStackFrame);}}}else{if(leafStackFrameIndex===undefined){this.model_.importWarning({type:'memory_dump_parse_error',message:'Missing stack frame ID of heap entry '+i+' (size '+size+') in a '+allocatorName+' heap dump for PID='+this.pid_+'.'});continue;}
+const leafStackFrameId=this.idPrefix_+leafStackFrameIndex;if(leafStackFrameIndex===''){leafStackFrame=undefined;}else{leafStackFrame=model.stackFrames[leafStackFrameId];if(leafStackFrame===undefined){this.model_.importWarning({type:'memory_dump_parse_error',message:'Missing leaf stack frame (ID '+leafStackFrameId+') of heap entry '+i+' (size '+size+') in a '+
+allocatorName+' heap dump for PID='+this.pid_+'.'});continue;}}}
+const objectTypeId=entry.type;let objectTypeName;if(objectTypeId===undefined){objectTypeName=undefined;}else if(this.processObjectTypeNameMap_===undefined){continue;}else{objectTypeName=this.processObjectTypeNameMap_[objectTypeId];if(objectTypeName===undefined){this.model_.importWarning({type:'memory_dump_parse_error',message:'Missing object type name (ID '+objectTypeId+') of heap entry '+i+' (size '+size+') in a '+
+allocatorName+' heap dump for PID='+this.pid_+'.'});continue;}}
+const count=entry.count===undefined?undefined:parseInt(entry.count,16);heapDump.addEntry(leafStackFrame,objectTypeName,size,count);}
+return heapDump;},parse(){const heapDumps={};for(const allocatorName in this.rawHeapDumps_){const rawHeapDump=this.rawHeapDumps_[allocatorName];const heapDump=this.parseRawHeapDump(rawHeapDump,allocatorName);if(heapDump!==undefined&&heapDump.entries.length>0){heapDumps[allocatorName]=heapDump;}}
+return heapDumps;},};return{LegacyHeapDumpTraceEventImporter,};});'use strict';if(tr.isHeadless){global.window={};}
+(function(window,Object,Array,Error,JSON,undefined){var partialComplete=varArgs(function(fn,args){var numBoundArgs=args.length;return varArgs(function(callArgs){for(var i=0;i<callArgs.length;i++){args[numBoundArgs+i]=callArgs[i];}
+args.length=numBoundArgs+callArgs.length;return fn.apply(this,args);});}),compose=varArgs(function(fns){var fnsList=arrayAsList(fns);function next(params,curFn){return[apply(params,curFn)];}
+return varArgs(function(startParams){return foldR(next,startParams,fnsList)[0];});});function compose2(f1,f2){return function(){return f1.call(this,f2.apply(this,arguments));}}
+function attr(key){return function(o){return o[key];};}
+var lazyUnion=varArgs(function(fns){return varArgs(function(params){var maybeValue;for(var i=0;i<len(fns);i++){maybeValue=apply(params,fns[i]);if(maybeValue){return maybeValue;}}});});function apply(args,fn){return fn.apply(undefined,args);}
+function varArgs(fn){var numberOfFixedArguments=fn.length-1,slice=Array.prototype.slice;if(numberOfFixedArguments==0){return function(){return fn.call(this,slice.call(arguments));}}else if(numberOfFixedArguments==1){return function(){return fn.call(this,arguments[0],slice.call(arguments,1));}}
+var argsHolder=Array(fn.length);return function(){for(var i=0;i<numberOfFixedArguments;i++){argsHolder[i]=arguments[i];}
+argsHolder[numberOfFixedArguments]=slice.call(arguments,numberOfFixedArguments);return fn.apply(this,argsHolder);}}
+function flip(fn){return function(a,b){return fn(b,a);}}
+function lazyIntersection(fn1,fn2){return function(param){return fn1(param)&&fn2(param);};}
+function noop(){}
+function always(){return true}
+function functor(val){return function(){return val;}}
+function isOfType(T,maybeSomething){return maybeSomething&&maybeSomething.constructor===T;}
+var len=attr('length'),isString=partialComplete(isOfType,String);function defined(value){return value!==undefined;}
+function hasAllProperties(fieldList,o){return(o instanceof Object)&&all(function(field){return(field in o);},fieldList);}
+function cons(x,xs){return[x,xs];}
+var emptyList=null,head=attr(0),tail=attr(1);function arrayAsList(inputArray){return reverseList(inputArray.reduce(flip(cons),emptyList));}
+var list=varArgs(arrayAsList);function listAsArray(list){return foldR(function(arraySoFar,listItem){arraySoFar.unshift(listItem);return arraySoFar;},[],list);}
+function map(fn,list){return list?cons(fn(head(list)),map(fn,tail(list))):emptyList;}
+function foldR(fn,startValue,list){return list?fn(foldR(fn,startValue,tail(list)),head(list)):startValue;}
+function foldR1(fn,list){return tail(list)?fn(foldR1(fn,tail(list)),head(list)):head(list);}
+function without(list,test,removedFn){return withoutInner(list,removedFn||noop);function withoutInner(subList,removedFn){return subList?(test(head(subList))?(removedFn(head(subList)),tail(subList)):cons(head(subList),withoutInner(tail(subList),removedFn))):emptyList;}}
+function all(fn,list){return!list||(fn(head(list))&&all(fn,tail(list)));}
+function applyEach(fnList,args){if(fnList){head(fnList).apply(null,args);applyEach(tail(fnList),args);}}
+function reverseList(list){function reverseInner(list,reversedAlready){if(!list){return reversedAlready;}
+return reverseInner(tail(list),cons(head(list),reversedAlready))}
+return reverseInner(list,emptyList);}
+function first(test,list){return list&&(test(head(list))?head(list):first(test,tail(list)));}
+function clarinet(eventBus){"use strict";var
+emitSaxKey=eventBus(SAX_KEY).emit,emitValueOpen=eventBus(SAX_VALUE_OPEN).emit,emitValueClose=eventBus(SAX_VALUE_CLOSE).emit,emitFail=eventBus(FAIL_EVENT).emit,MAX_BUFFER_LENGTH=64*1024,stringTokenPattern=/[\\"\n]/g,_n=0,BEGIN=_n++,VALUE=_n++,OPEN_OBJECT=_n++,CLOSE_OBJECT=_n++,OPEN_ARRAY=_n++,CLOSE_ARRAY=_n++,STRING=_n++,OPEN_KEY=_n++,CLOSE_KEY=_n++,TRUE=_n++,TRUE2=_n++,TRUE3=_n++,FALSE=_n++,FALSE2=_n++,FALSE3=_n++,FALSE4=_n++,NULL=_n++,NULL2=_n++,NULL3=_n++,NUMBER_DECIMAL_POINT=_n++,NUMBER_DIGIT=_n,bufferCheckPosition=MAX_BUFFER_LENGTH,latestError,c,p,textNode=undefined,numberNode="",slashed=false,closed=false,state=BEGIN,stack=[],unicodeS=null,unicodeI=0,depth=0,position=0,column=0,line=1;function checkBufferLength(){var maxActual=0;if(textNode!==undefined&&textNode.length>MAX_BUFFER_LENGTH){emitError("Max buffer length exceeded: textNode");maxActual=Math.max(maxActual,textNode.length);}
+if(numberNode.length>MAX_BUFFER_LENGTH){emitError("Max buffer length exceeded: numberNode");maxActual=Math.max(maxActual,numberNode.length);}
+bufferCheckPosition=(MAX_BUFFER_LENGTH-maxActual)
++position;}
+eventBus(STREAM_DATA).on(handleData);eventBus(STREAM_END).on(handleStreamEnd);function emitError(errorString){if(textNode!==undefined){emitValueOpen(textNode);emitValueClose();textNode=undefined;}
+latestError=Error(errorString+"\nLn: "+line+"\nCol: "+column+"\nChr: "+c);emitFail(errorReport(undefined,undefined,latestError));}
+function handleStreamEnd(){if(state==BEGIN){emitValueOpen({});emitValueClose();closed=true;return;}
+if(state!==VALUE||depth!==0)
+emitError("Unexpected end");if(textNode!==undefined){emitValueOpen(textNode);emitValueClose();textNode=undefined;}
+closed=true;}
+function whitespace(c){return c=='\r'||c=='\n'||c==' '||c=='\t';}
+function handleData(chunk){if(latestError)
+return;if(closed){return emitError("Cannot write after close");}
+var i=0;c=chunk[0];while(c){p=c;c=chunk[i++];if(!c)break;position++;if(c=="\n"){line++;column=0;}else column++;switch(state){case BEGIN:if(c==="{")state=OPEN_OBJECT;else if(c==="[")state=OPEN_ARRAY;else if(!whitespace(c))
+return emitError("Non-whitespace before {[.");continue;case OPEN_KEY:case OPEN_OBJECT:if(whitespace(c))continue;if(state===OPEN_KEY)stack.push(CLOSE_KEY);else{if(c==='}'){emitValueOpen({});emitValueClose();state=stack.pop()||VALUE;continue;}else stack.push(CLOSE_OBJECT);}
+if(c==='"')
+state=STRING;else
+return emitError("Malformed object key should start with \" ");continue;case CLOSE_KEY:case CLOSE_OBJECT:if(whitespace(c))continue;if(c===':'){if(state===CLOSE_OBJECT){stack.push(CLOSE_OBJECT);if(textNode!==undefined){emitValueOpen({});emitSaxKey(textNode);textNode=undefined;}
+depth++;}else{if(textNode!==undefined){emitSaxKey(textNode);textNode=undefined;}}
+state=VALUE;}else if(c==='}'){if(textNode!==undefined){emitValueOpen(textNode);emitValueClose();textNode=undefined;}
+emitValueClose();depth--;state=stack.pop()||VALUE;}else if(c===','){if(state===CLOSE_OBJECT)
+stack.push(CLOSE_OBJECT);if(textNode!==undefined){emitValueOpen(textNode);emitValueClose();textNode=undefined;}
+state=OPEN_KEY;}else
+return emitError('Bad object');continue;case OPEN_ARRAY:case VALUE:if(whitespace(c))continue;if(state===OPEN_ARRAY){emitValueOpen([]);depth++;state=VALUE;if(c===']'){emitValueClose();depth--;state=stack.pop()||VALUE;continue;}else{stack.push(CLOSE_ARRAY);}}
+if(c==='"')state=STRING;else if(c==='{')state=OPEN_OBJECT;else if(c==='[')state=OPEN_ARRAY;else if(c==='t')state=TRUE;else if(c==='f')state=FALSE;else if(c==='n')state=NULL;else if(c==='-'){numberNode+=c;}else if(c==='0'){numberNode+=c;state=NUMBER_DIGIT;}else if('123456789'.indexOf(c)!==-1){numberNode+=c;state=NUMBER_DIGIT;}else
+return emitError("Bad value");continue;case CLOSE_ARRAY:if(c===','){stack.push(CLOSE_ARRAY);if(textNode!==undefined){emitValueOpen(textNode);emitValueClose();textNode=undefined;}
+state=VALUE;}else if(c===']'){if(textNode!==undefined){emitValueOpen(textNode);emitValueClose();textNode=undefined;}
+emitValueClose();depth--;state=stack.pop()||VALUE;}else if(whitespace(c))
+continue;else
+return emitError('Bad array');continue;case STRING:if(textNode===undefined){textNode="";}
+var starti=i-1;STRING_BIGLOOP:while(true){while(unicodeI>0){unicodeS+=c;c=chunk.charAt(i++);if(unicodeI===4){textNode+=String.fromCharCode(parseInt(unicodeS,16));unicodeI=0;starti=i-1;}else{unicodeI++;}
+if(!c)break STRING_BIGLOOP;}
+if(c==='"'&&!slashed){state=stack.pop()||VALUE;textNode+=chunk.substring(starti,i-1);break;}
+if(c==='\\'&&!slashed){slashed=true;textNode+=chunk.substring(starti,i-1);c=chunk.charAt(i++);if(!c)break;}
+if(slashed){slashed=false;if(c==='n'){textNode+='\n';}
+else if(c==='r'){textNode+='\r';}
+else if(c==='t'){textNode+='\t';}
+else if(c==='f'){textNode+='\f';}
+else if(c==='b'){textNode+='\b';}
+else if(c==='u'){unicodeI=1;unicodeS='';}else{textNode+=c;}
+c=chunk.charAt(i++);starti=i-1;if(!c)break;else continue;}
+stringTokenPattern.lastIndex=i;var reResult=stringTokenPattern.exec(chunk);if(!reResult){i=chunk.length+1;textNode+=chunk.substring(starti,i-1);break;}
+i=reResult.index+1;c=chunk.charAt(reResult.index);if(!c){textNode+=chunk.substring(starti,i-1);break;}}
+continue;case TRUE:if(!c)continue;if(c==='r')state=TRUE2;else
+return emitError('Invalid true started with t'+c);continue;case TRUE2:if(!c)continue;if(c==='u')state=TRUE3;else
+return emitError('Invalid true started with tr'+c);continue;case TRUE3:if(!c)continue;if(c==='e'){emitValueOpen(true);emitValueClose();state=stack.pop()||VALUE;}else
+return emitError('Invalid true started with tru'+c);continue;case FALSE:if(!c)continue;if(c==='a')state=FALSE2;else
+return emitError('Invalid false started with f'+c);continue;case FALSE2:if(!c)continue;if(c==='l')state=FALSE3;else
+return emitError('Invalid false started with fa'+c);continue;case FALSE3:if(!c)continue;if(c==='s')state=FALSE4;else
+return emitError('Invalid false started with fal'+c);continue;case FALSE4:if(!c)continue;if(c==='e'){emitValueOpen(false);emitValueClose();state=stack.pop()||VALUE;}else
+return emitError('Invalid false started with fals'+c);continue;case NULL:if(!c)continue;if(c==='u')state=NULL2;else
+return emitError('Invalid null started with n'+c);continue;case NULL2:if(!c)continue;if(c==='l')state=NULL3;else
+return emitError('Invalid null started with nu'+c);continue;case NULL3:if(!c)continue;if(c==='l'){emitValueOpen(null);emitValueClose();state=stack.pop()||VALUE;}else
+return emitError('Invalid null started with nul'+c);continue;case NUMBER_DECIMAL_POINT:if(c==='.'){numberNode+=c;state=NUMBER_DIGIT;}else
+return emitError('Leading zero not followed by .');continue;case NUMBER_DIGIT:if('0123456789'.indexOf(c)!==-1)numberNode+=c;else if(c==='.'){if(numberNode.indexOf('.')!==-1)
+return emitError('Invalid number has two dots');numberNode+=c;}else if(c==='e'||c==='E'){if(numberNode.indexOf('e')!==-1||numberNode.indexOf('E')!==-1)
+return emitError('Invalid number has two exponential');numberNode+=c;}else if(c==="+"||c==="-"){if(!(p==='e'||p==='E'))
+return emitError('Invalid symbol in number');numberNode+=c;}else{if(numberNode){emitValueOpen(parseFloat(numberNode));emitValueClose();numberNode="";}
+i--;state=stack.pop()||VALUE;}
+continue;default:return emitError("Unknown state: "+state);}}
+if(position>=bufferCheckPosition)
+checkBufferLength();}}
+function ascentManager(oboeBus,handlers){"use strict";var listenerId={},ascent;function stateAfter(handler){return function(param){ascent=handler(ascent,param);}}
+for(var eventName in handlers){oboeBus(eventName).on(stateAfter(handlers[eventName]),listenerId);}
+oboeBus(NODE_SWAP).on(function(newNode){var oldHead=head(ascent),key=keyOf(oldHead),ancestors=tail(ascent),parentNode;if(ancestors){parentNode=nodeOf(head(ancestors));parentNode[key]=newNode;}});oboeBus(NODE_DROP).on(function(){var oldHead=head(ascent),key=keyOf(oldHead),ancestors=tail(ascent),parentNode;if(ancestors){parentNode=nodeOf(head(ancestors));delete parentNode[key];}});oboeBus(ABORTING).on(function(){for(var eventName in handlers){oboeBus(eventName).un(listenerId);}});}
+function parseResponseHeaders(headerStr){var headers={};headerStr&&headerStr.split('\u000d\u000a').forEach(function(headerPair){var index=headerPair.indexOf('\u003a\u0020');headers[headerPair.substring(0,index)]=headerPair.substring(index+2);});return headers;}
+function isCrossOrigin(pageLocation,ajaxHost){function defaultPort(protocol){return{'http:':80,'https:':443}[protocol];}
+function portOf(location){return location.port||defaultPort(location.protocol||pageLocation.protocol);}
+return!!((ajaxHost.protocol&&(ajaxHost.protocol!=pageLocation.protocol))||(ajaxHost.host&&(ajaxHost.host!=pageLocation.host))||(ajaxHost.host&&(portOf(ajaxHost)!=portOf(pageLocation))));}
+function parseUrlOrigin(url){var URL_HOST_PATTERN=/(\w+:)?(?:\/\/)([\w.-]+)?(?::(\d+))?\/?/,urlHostMatch=URL_HOST_PATTERN.exec(url)||[];return{protocol:urlHostMatch[1]||'',host:urlHostMatch[2]||'',port:urlHostMatch[3]||''};}
+function httpTransport(){return new XMLHttpRequest();}
+function streamingHttp(oboeBus,xhr,method,url,data,headers,withCredentials){"use strict";var emitStreamData=oboeBus(STREAM_DATA).emit,emitFail=oboeBus(FAIL_EVENT).emit,numberOfCharsAlreadyGivenToCallback=0,stillToSendStartEvent=true;oboeBus(ABORTING).on(function(){xhr.onreadystatechange=null;xhr.abort();});function handleProgress(){var textSoFar=xhr.responseText,newText=textSoFar.substr(numberOfCharsAlreadyGivenToCallback);if(newText){emitStreamData(newText);}
+numberOfCharsAlreadyGivenToCallback=len(textSoFar);}
+if('onprogress'in xhr){xhr.onprogress=handleProgress;}
+xhr.onreadystatechange=function(){function sendStartIfNotAlready(){try{stillToSendStartEvent&&oboeBus(HTTP_START).emit(xhr.status,parseResponseHeaders(xhr.getAllResponseHeaders()));stillToSendStartEvent=false;}catch(e){}}
+switch(xhr.readyState){case 2:case 3:return sendStartIfNotAlready();case 4:sendStartIfNotAlready();var successful=String(xhr.status)[0]==2;if(successful){handleProgress();oboeBus(STREAM_END).emit();}else{emitFail(errorReport(xhr.status,xhr.responseText));}}};try{xhr.open(method,url,true);for(var headerName in headers){xhr.setRequestHeader(headerName,headers[headerName]);}
+if(!isCrossOrigin(window.location,parseUrlOrigin(url))){xhr.setRequestHeader('X-Requested-With','XMLHttpRequest');}
+xhr.withCredentials=withCredentials;xhr.send(data);}catch(e){window.setTimeout(partialComplete(emitFail,errorReport(undefined,undefined,e)),0);}}
+var jsonPathSyntax=(function(){var
+regexDescriptor=function regexDescriptor(regex){return regex.exec.bind(regex);},jsonPathClause=varArgs(function(componentRegexes){componentRegexes.unshift(/^/);return regexDescriptor(RegExp(componentRegexes.map(attr('source')).join('')));}),possiblyCapturing=/(\$?)/,namedNode=/([\w-_]+|\*)/,namePlaceholder=/()/,nodeInArrayNotation=/\["([^"]+)"\]/,numberedNodeInArrayNotation=/\[(\d+|\*)\]/,fieldList=/{([\w ]*?)}/,optionalFieldList=/(?:{([\w ]*?)})?/
+,jsonPathNamedNodeInObjectNotation=jsonPathClause(possiblyCapturing,namedNode,optionalFieldList),jsonPathNamedNodeInArrayNotation=jsonPathClause(possiblyCapturing,nodeInArrayNotation,optionalFieldList),jsonPathNumberedNodeInArrayNotation=jsonPathClause(possiblyCapturing,numberedNodeInArrayNotation,optionalFieldList),jsonPathPureDuckTyping=jsonPathClause(possiblyCapturing,namePlaceholder,fieldList),jsonPathDoubleDot=jsonPathClause(/\.\./),jsonPathDot=jsonPathClause(/\./),jsonPathBang=jsonPathClause(possiblyCapturing,/!/),emptyString=jsonPathClause(/$/);return function(fn){return fn(lazyUnion(jsonPathNamedNodeInObjectNotation,jsonPathNamedNodeInArrayNotation,jsonPathNumberedNodeInArrayNotation,jsonPathPureDuckTyping),jsonPathDoubleDot,jsonPathDot,jsonPathBang,emptyString);};}());function namedNode(key,node){return{key:key,node:node};}
+var keyOf=attr('key');var nodeOf=attr('node');var ROOT_PATH={};function incrementalContentBuilder(oboeBus){var emitNodeOpened=oboeBus(NODE_OPENED).emit,emitNodeClosed=oboeBus(NODE_CLOSED).emit,emitRootOpened=oboeBus(ROOT_PATH_FOUND).emit,emitRootClosed=oboeBus(ROOT_NODE_FOUND).emit;function arrayIndicesAreKeys(possiblyInconsistentAscent,newDeepestNode){var parentNode=nodeOf(head(possiblyInconsistentAscent));return isOfType(Array,parentNode)?keyFound(possiblyInconsistentAscent,len(parentNode),newDeepestNode):possiblyInconsistentAscent;}
+function nodeOpened(ascent,newDeepestNode){if(!ascent){emitRootOpened(newDeepestNode);return keyFound(ascent,ROOT_PATH,newDeepestNode);}
+var arrayConsistentAscent=arrayIndicesAreKeys(ascent,newDeepestNode),ancestorBranches=tail(arrayConsistentAscent),previouslyUnmappedName=keyOf(head(arrayConsistentAscent));appendBuiltContent(ancestorBranches,previouslyUnmappedName,newDeepestNode);return cons(namedNode(previouslyUnmappedName,newDeepestNode),ancestorBranches);}
+function appendBuiltContent(ancestorBranches,key,node){nodeOf(head(ancestorBranches))[key]=node;}
+function keyFound(ascent,newDeepestName,maybeNewDeepestNode){if(ascent){appendBuiltContent(ascent,newDeepestName,maybeNewDeepestNode);}
+var ascentWithNewPath=cons(namedNode(newDeepestName,maybeNewDeepestNode),ascent);emitNodeOpened(ascentWithNewPath);return ascentWithNewPath;}
+function nodeClosed(ascent){emitNodeClosed(ascent);return tail(ascent)||emitRootClosed(nodeOf(head(ascent)));}
+var contentBuilderHandlers={};contentBuilderHandlers[SAX_VALUE_OPEN]=nodeOpened;contentBuilderHandlers[SAX_VALUE_CLOSE]=nodeClosed;contentBuilderHandlers[SAX_KEY]=keyFound;return contentBuilderHandlers;}
+var jsonPathCompiler=jsonPathSyntax(function(pathNodeSyntax,doubleDotSyntax,dotSyntax,bangSyntax,emptySyntax){var CAPTURING_INDEX=1;var NAME_INDEX=2;var FIELD_LIST_INDEX=3;var headKey=compose2(keyOf,head),headNode=compose2(nodeOf,head);function nameClause(previousExpr,detection){var name=detection[NAME_INDEX],matchesName=(!name||name=='*')?always:function(ascent){return headKey(ascent)==name};return lazyIntersection(matchesName,previousExpr);}
+function duckTypeClause(previousExpr,detection){var fieldListStr=detection[FIELD_LIST_INDEX];if(!fieldListStr)
+return previousExpr;var hasAllrequiredFields=partialComplete(hasAllProperties,arrayAsList(fieldListStr.split(/\W+/))),isMatch=compose2(hasAllrequiredFields,headNode);return lazyIntersection(isMatch,previousExpr);}
+function capture(previousExpr,detection){var capturing=!!detection[CAPTURING_INDEX];if(!capturing)
+return previousExpr;return lazyIntersection(previousExpr,head);}
+function skip1(previousExpr){if(previousExpr==always){return always;}
+function notAtRoot(ascent){return headKey(ascent)!=ROOT_PATH;}
+return lazyIntersection(notAtRoot,compose2(previousExpr,tail));}
+function skipMany(previousExpr){if(previousExpr==always){return always;}
+var
+terminalCaseWhenArrivingAtRoot=rootExpr(),terminalCaseWhenPreviousExpressionIsSatisfied=previousExpr,recursiveCase=skip1(function(ascent){return cases(ascent);}),cases=lazyUnion(terminalCaseWhenArrivingAtRoot,terminalCaseWhenPreviousExpressionIsSatisfied,recursiveCase);return cases;}
+function rootExpr(){return function(ascent){return headKey(ascent)==ROOT_PATH;};}
+function statementExpr(lastClause){return function(ascent){var exprMatch=lastClause(ascent);return exprMatch===true?head(ascent):exprMatch;};}
+function expressionsReader(exprs,parserGeneratedSoFar,detection){return foldR(function(parserGeneratedSoFar,expr){return expr(parserGeneratedSoFar,detection);},parserGeneratedSoFar,exprs);}
+function generateClauseReaderIfTokenFound(tokenDetector,clauseEvaluatorGenerators,jsonPath,parserGeneratedSoFar,onSuccess){var detected=tokenDetector(jsonPath);if(detected){var compiledParser=expressionsReader(clauseEvaluatorGenerators,parserGeneratedSoFar,detected),remainingUnparsedJsonPath=jsonPath.substr(len(detected[0]));return onSuccess(remainingUnparsedJsonPath,compiledParser);}}
+function clauseMatcher(tokenDetector,exprs){return partialComplete(generateClauseReaderIfTokenFound,tokenDetector,exprs);}
+var clauseForJsonPath=lazyUnion(clauseMatcher(pathNodeSyntax,list(capture,duckTypeClause,nameClause,skip1)),clauseMatcher(doubleDotSyntax,list(skipMany)),clauseMatcher(dotSyntax,list()),clauseMatcher(bangSyntax,list(capture,rootExpr)),clauseMatcher(emptySyntax,list(statementExpr)),function(jsonPath){throw Error('"'+jsonPath+'" could not be tokenised')});function returnFoundParser(_remainingJsonPath,compiledParser){return compiledParser}
+function compileJsonPathToFunction(uncompiledJsonPath,parserGeneratedSoFar){var onFind=uncompiledJsonPath?compileJsonPathToFunction:returnFoundParser;return clauseForJsonPath(uncompiledJsonPath,parserGeneratedSoFar,onFind);}
+return function(jsonPath){try{return compileJsonPathToFunction(jsonPath,always);}catch(e){throw Error('Could not compile "'+jsonPath+'" because '+e.message);}}});function singleEventPubSub(eventType,newListener,removeListener){var listenerTupleList,listenerList;function hasId(id){return function(tuple){return tuple.id==id;};}
+return{on:function(listener,listenerId){var tuple={listener:listener,id:listenerId||listener};if(newListener){newListener.emit(eventType,listener,tuple.id);}
+listenerTupleList=cons(tuple,listenerTupleList);listenerList=cons(listener,listenerList);return this;},emit:function(){applyEach(listenerList,arguments);},un:function(listenerId){var removed;listenerTupleList=without(listenerTupleList,hasId(listenerId),function(tuple){removed=tuple;});if(removed){listenerList=without(listenerList,function(listener){return listener==removed.listener;});if(removeListener){removeListener.emit(eventType,removed.listener,removed.id);}}},listeners:function(){return listenerList;},hasListener:function(listenerId){var test=listenerId?hasId(listenerId):always;return defined(first(test,listenerTupleList));}};}
+function pubSub(){var singles={},newListener=newSingle('newListener'),removeListener=newSingle('removeListener');function newSingle(eventName){return singles[eventName]=singleEventPubSub(eventName,newListener,removeListener);}
+function pubSubInstance(eventName){return singles[eventName]||newSingle(eventName);}
+['emit','on','un'].forEach(function(methodName){pubSubInstance[methodName]=varArgs(function(eventName,parameters){apply(parameters,pubSubInstance(eventName)[methodName]);});});return pubSubInstance;}
+var
+_S=1,NODE_OPENED=_S++,NODE_CLOSED=_S++,NODE_SWAP=_S++,NODE_DROP=_S++,FAIL_EVENT='fail',ROOT_NODE_FOUND=_S++,ROOT_PATH_FOUND=_S++,HTTP_START='start',STREAM_DATA='data',STREAM_END='end',ABORTING=_S++,SAX_KEY=_S++,SAX_VALUE_OPEN=_S++,SAX_VALUE_CLOSE=_S++;function errorReport(statusCode,body,error){try{var jsonBody=JSON.parse(body);}catch(e){}
+return{statusCode:statusCode,body:body,jsonBody:jsonBody,thrown:error};}
+function patternAdapter(oboeBus,jsonPathCompiler){var predicateEventMap={node:oboeBus(NODE_CLOSED),path:oboeBus(NODE_OPENED)};function emitMatchingNode(emitMatch,node,ascent){var descent=reverseList(ascent);emitMatch(node,listAsArray(tail(map(keyOf,descent))),listAsArray(map(nodeOf,descent)));}
+function addUnderlyingListener(fullEventName,predicateEvent,compiledJsonPath){var emitMatch=oboeBus(fullEventName).emit;predicateEvent.on(function(ascent){var maybeMatchingMapping=compiledJsonPath(ascent);if(maybeMatchingMapping!==false){emitMatchingNode(emitMatch,nodeOf(maybeMatchingMapping),ascent);}},fullEventName);oboeBus('removeListener').on(function(removedEventName){if(removedEventName==fullEventName){if(!oboeBus(removedEventName).listeners()){predicateEvent.un(fullEventName);}}});}
+oboeBus('newListener').on(function(fullEventName){var match=/(node|path):(.*)/.exec(fullEventName);if(match){var predicateEvent=predicateEventMap[match[1]];if(!predicateEvent.hasListener(fullEventName)){addUnderlyingListener(fullEventName,predicateEvent,jsonPathCompiler(match[2]));}}})}
+function instanceApi(oboeBus,contentSource){var oboeApi,fullyQualifiedNamePattern=/^(node|path):./,rootNodeFinishedEvent=oboeBus(ROOT_NODE_FOUND),emitNodeDrop=oboeBus(NODE_DROP).emit,emitNodeSwap=oboeBus(NODE_SWAP).emit,addListener=varArgs(function(eventId,parameters){if(oboeApi[eventId]){apply(parameters,oboeApi[eventId]);}else{var event=oboeBus(eventId),listener=parameters[0];if(fullyQualifiedNamePattern.test(eventId)){addForgettableCallback(event,listener);}else{event.on(listener);}}
+return oboeApi;}),removeListener=function(eventId,p2,p3){if(eventId=='done'){rootNodeFinishedEvent.un(p2);}else if(eventId=='node'||eventId=='path'){oboeBus.un(eventId+':'+p2,p3);}else{var listener=p2;oboeBus(eventId).un(listener);}
+return oboeApi;};function addProtectedCallback(eventName,callback){oboeBus(eventName).on(protectedCallback(callback),callback);return oboeApi;}
+function addForgettableCallback(event,callback,listenerId){listenerId=listenerId||callback;var safeCallback=protectedCallback(callback);event.on(function(){var discard=false;oboeApi.forget=function(){discard=true;};apply(arguments,safeCallback);delete oboeApi.forget;if(discard){event.un(listenerId);}},listenerId);return oboeApi;}
+function protectedCallback(callback){return function(){try{return callback.apply(oboeApi,arguments);}catch(e){setTimeout(function(){throw new Error(e.message);});}}}
+function fullyQualifiedPatternMatchEvent(type,pattern){return oboeBus(type+':'+pattern);}
+function wrapCallbackToSwapNodeIfSomethingReturned(callback){return function(){var returnValueFromCallback=callback.apply(this,arguments);if(defined(returnValueFromCallback)){if(returnValueFromCallback==oboe.drop){emitNodeDrop();}else{emitNodeSwap(returnValueFromCallback);}}}}
+function addSingleNodeOrPathListener(eventId,pattern,callback){var effectiveCallback;if(eventId=='node'){effectiveCallback=wrapCallbackToSwapNodeIfSomethingReturned(callback);}else{effectiveCallback=callback;}
+addForgettableCallback(fullyQualifiedPatternMatchEvent(eventId,pattern),effectiveCallback,callback);}
+function addMultipleNodeOrPathListeners(eventId,listenerMap){for(var pattern in listenerMap){addSingleNodeOrPathListener(eventId,pattern,listenerMap[pattern]);}}
+function addNodeOrPathListenerApi(eventId,jsonPathOrListenerMap,callback){if(isString(jsonPathOrListenerMap)){addSingleNodeOrPathListener(eventId,jsonPathOrListenerMap,callback);}else{addMultipleNodeOrPathListeners(eventId,jsonPathOrListenerMap);}
+return oboeApi;}
+oboeBus(ROOT_PATH_FOUND).on(function(rootNode){oboeApi.root=functor(rootNode);});oboeBus(HTTP_START).on(function(_statusCode,headers){oboeApi.header=function(name){return name?headers[name]:headers;}});return oboeApi={on:addListener,addListener:addListener,removeListener:removeListener,emit:oboeBus.emit,node:partialComplete(addNodeOrPathListenerApi,'node'),path:partialComplete(addNodeOrPathListenerApi,'path'),done:partialComplete(addForgettableCallback,rootNodeFinishedEvent),start:partialComplete(addProtectedCallback,HTTP_START),fail:oboeBus(FAIL_EVENT).on,abort:oboeBus(ABORTING).emit,write:oboeBus(STREAM_DATA).emit,finish:oboeBus(STREAM_END).emit,header:noop,root:noop,source:contentSource};}
+function wire(httpMethodName,contentSource,body,headers,withCredentials){var oboeBus=pubSub();if(contentSource){streamingHttp(oboeBus,httpTransport(),httpMethodName,contentSource,body,headers,withCredentials);}
+clarinet(oboeBus);ascentManager(oboeBus,incrementalContentBuilder(oboeBus));patternAdapter(oboeBus,jsonPathCompiler);return instanceApi(oboeBus,contentSource);}
+function applyDefaults(passthrough,url,httpMethodName,body,headers,withCredentials,cached){headers=headers?JSON.parse(JSON.stringify(headers)):{};if(body){if(!isString(body)){body=JSON.stringify(body);headers['Content-Type']=headers['Content-Type']||'application/json';}}else{body=null;}
+function modifiedUrl(baseUrl,cached){if(cached===false){if(baseUrl.indexOf('?')==-1){baseUrl+='?';}else{baseUrl+='&';}
+baseUrl+='_='+new Date().getTime();}
+return baseUrl;}
+return passthrough(httpMethodName||'GET',modifiedUrl(url,cached),body,headers,withCredentials||false);}
+function oboe(arg1){var nodeStreamMethodNames=list('resume','pause','pipe'),isStream=partialComplete(hasAllProperties,nodeStreamMethodNames);if(arg1){if(isStream(arg1)||isString(arg1)){return applyDefaults(wire,arg1);}else{return applyDefaults(wire,arg1.url,arg1.method,arg1.body,arg1.headers,arg1.withCredentials,arg1.cached);}}else{return wire();}}
+oboe.drop=function(){return oboe.drop;};if(typeof define==="function"&&define.amd){define("oboe",[],function(){return oboe;});}else if(typeof exports==='object'){module.exports=oboe;}else{window.oboe=oboe;}})((function(){try{return window;}catch(e){return self;}}()),Object,Array,Error,JSON);'use strict';if(tr.isVinn){global.oboe=global.window.oboe;global.window=undefined;}else if(tr.isNode){global.window=undefined;const path=HTMLImportsLoader.hrefToAbsolutePath('/oboe/dist/oboe-node.js');global.oboe=require(path);}'use strict';tr.exportTo('tr.e.importer',function(){const STRING_ID_SUFFIX='_sid';const PLURAL_STRING_ID_SUFFIX='_sids';function isStringReference(s){return s.endsWith(STRING_ID_SUFFIX)||s.endsWith(PLURAL_STRING_ID_SUFFIX);}
+function getStringReferenceName(name){if(name.endsWith(PLURAL_STRING_ID_SUFFIX)){return name.slice(0,-PLURAL_STRING_ID_SUFFIX.length);}
+return name.slice(0,-STRING_ID_SUFFIX.length);}
+function deferenceStrings(idToString,o){const clone=Object.assign({},o);for(const[key,value]of Object.entries(clone)){if(isStringReference(key)){const name=getStringReferenceName(key);clone[name]=idToString(value);}}
+return clone;}
+function singularize(word){if(word.endsWith('s')){return word.slice(0,-1);}
+return word;}
+function getMetadataPairs(dataJson){const isMetadata=v=>typeof v!=='object'||Array.isArray(v);const pairs=Object.entries(dataJson);const metadataPairs=pairs.filter(([_,v])=>isMetadata(v));return metadataPairs;}
+function getGroupPairs(dataJson){const pairs=Object.entries(dataJson);const nonMapPairs=pairs.filter(([k,_])=>k!=='maps');const groupPairs=nonMapPairs.filter(([_,v])=>typeof v==='object');return groupPairs;}
+function createMap(mapJson){const map=new Map();for(const entry of mapJson){if(entry.id===undefined){throw new Error('Missing required key "id" in streaming event.');}
+map.set(entry.id,entry);}
+return map;}
+function createMaps(mapsJson){const maps=new Map();for(const[name,mapJson]of Object.entries(mapsJson)){maps.set(name,createMap(mapJson));}
+return maps;}
+function createGroup(groupJson,opt_startTime){const entries=[];const n=Object.values(groupJson)[0].length;for(let i=0;i<n;i++){const entry={};for(const name in groupJson){entry[name]=groupJson[name][i];}
+entries.push(entry);}
+const timeDelta=groupJson.timeDelta;if(opt_startTime===undefined&&timeDelta!==undefined){throw new Error('Missing required key "startTime" in streaming event.');}
+if(opt_startTime){let delta=0;for(const entry of entries){delta+=entry.timeDelta?entry.timeDelta:0;entry.time=opt_startTime+delta;}}
+return entries;}
+function createGroups(groupsJson,opt_startTime){const groups=new Map();for(const[name,groupJson]of Object.entries(groupsJson)){groups.set(name,createGroup(groupJson,opt_startTime));}
+return groups;}
+function createMetadata(metadataPairs){const metadata=new Map();for(const[name,value]of metadataPairs){metadata.set(name,value);}
+if(metadata.get('version')===undefined){throw new Error('Missing required key "version" in streaming event.');}
+return metadata;}
+class ProfilingDictionaryReader{constructor(opt_metadata,opt_maps,opt_groups,opt_parent){this.metadata=opt_metadata||new Map();this.maps=opt_maps||new Map();this.groups=opt_groups||new Map();this.parent_=opt_parent||undefined;this.inflated_=undefined;this.raw_=undefined;this.boundGetString_=this.getString.bind(this);this.deferenceStrings_=o=>deferenceStrings(this.boundGetString_,o);}
+static empty(){return new ProfilingDictionaryReader();}
+get parent(){return this.parent_;}
+get raw(){if(this.raw_)return this.raw_;this.raw_={};for(const[name,group]of this.groups.entries()){this.raw_[name]=group;}
+return this.raw_;}
+get inflated(){if(this.inflated_)return this.inflated_;this.inflated_={};for(const[name,group]of this.groups.entries()){this.inflated_[name]=this.inflateGroup(group);}
+return this.inflated_;}
+getNewMap(name){return this.maps.get(name)||new Map();}
+getMapValue(mapName,id){let value=this.getNewMap(mapName).get(id);if(value===undefined&&this.parent){value=this.parent.getMapValue(mapName,id);}
+return value;}
+getString(id){const value=this.getMapValue('strings',id);if(value===undefined)return undefined;return value.string;}
+hasMap(name){if(this.maps.has(name))return true;if(this.parent===undefined)return false;return this.parent.hasMap(name);}
+inflateGroup(group){return group.map(this.inflateEntry.bind(this));}
+inflateEntry(entry){const inflatedEntry={};for(const[name,value]of Object.entries(entry)){let inflatedValue;if(this.hasMap(name)){const id=value;inflatedValue=this.deferenceStrings_(this.getMapValue(name,id));}else{inflatedValue=value;}
+inflatedEntry[singularize(name)]=inflatedValue;}
+return this.deferenceStrings_(inflatedEntry);}
+expandData(data){const mapsJson=data.maps||{};const groupsJson=data.allocators||{};const metadataPairs=getMetadataPairs(data);const metadata=createMetadata(metadataPairs);const opt_startTime=metadata.get('startTime');const maps=createMaps(mapsJson);const groups=createGroups(groupsJson,opt_startTime);return new ProfilingDictionaryReader(metadata,maps,groups,this);}
+expandEvent(event){return this.expandData(event.args.data);}}
+return{ProfilingDictionaryReader,singularize,deferenceStringsForTest:deferenceStrings,};});'use strict';tr.exportTo('tr.model.source_info',function(){function SourceInfo(file,opt_line,opt_column){this.file_=file;this.line_=opt_line||-1;this.column_=opt_column||-1;}
+SourceInfo.prototype={get file(){return this.file_;},get line(){return this.line_;},get column(){return this.column_;},get domain(){if(!this.file_)return undefined;const domain=this.file_.match(/(.*:\/\/[^:\/]*)/i);return domain?domain[1]:undefined;},toString(){let str='';if(this.file_){str+=this.file_;}
+if(this.line_>0){str+=':'+this.line_;}
+if(this.column_>0){str+=':'+this.column_;}
+return str;}};return{SourceInfo,};});'use strict';tr.exportTo('tr.model.source_info',function(){function JSSourceInfo(file,line,column,isNative,scriptId,state){tr.model.source_info.SourceInfo.call(this,file,line,column);this.isNative_=isNative;this.scriptId_=scriptId;this.state_=state;}
+JSSourceInfo.prototype={__proto__:tr.model.source_info.SourceInfo.prototype,get state(){return this.state_;},get isNative(){return this.isNative_;},get scriptId(){return this.scriptId_;},toString(){const str=this.isNative_?'[native v8] ':'';return str+
+tr.model.source_info.SourceInfo.prototype.toString.call(this);}};const JSSourceState={COMPILED:'compiled',OPTIMIZABLE:'optimizable',OPTIMIZED:'optimized',UNKNOWN:'unknown',};return{JSSourceInfo,JSSourceState,};});'use strict';tr.exportTo('tr.e.importer',function(){function TraceCodeEntry(address,size,name,scriptId){this.id_=tr.b.GUID.allocateSimple();this.address_=address;this.size_=size;const rePrefix=/^(\w*:)?([*~]?)(.*)$/m;const tokens=rePrefix.exec(name);const prefix=tokens[1];let state=tokens[2];const body=tokens[3];if(state==='*'){state=tr.model.source_info.JSSourceState.OPTIMIZED;}else if(state==='~'){state=tr.model.source_info.JSSourceState.OPTIMIZABLE;}else if(state===''){state=tr.model.source_info.JSSourceState.COMPILED;}else{state=tr.model.source_info.JSSourceState.UNKNOWN;}
+let rawName;let rawUrl;if(prefix==='Script:'){rawName='';rawUrl=body;}else{const spacePos=body.lastIndexOf(' ');rawName=spacePos!==-1?body.substr(0,spacePos):body;rawUrl=spacePos!==-1?body.substr(spacePos+1):'';}
+function splitLineAndColumn(url){const lineColumnRegEx=/(?::(\d+))?(?::(\d+))?$/;const lineColumnMatch=lineColumnRegEx.exec(url);let lineNumber;let columnNumber;if(typeof(lineColumnMatch[1])==='string'){lineNumber=parseInt(lineColumnMatch[1],10);lineNumber=isNaN(lineNumber)?undefined:lineNumber-1;}
+if(typeof(lineColumnMatch[2])==='string'){columnNumber=parseInt(lineColumnMatch[2],10);columnNumber=isNaN(columnNumber)?undefined:columnNumber-1;}
+return{url:url.substring(0,url.length-lineColumnMatch[0].length),lineNumber,columnNumber};}
+const nativeSuffix=' native';const isNative=rawName.endsWith(nativeSuffix);this.name_=isNative?rawName.slice(0,-nativeSuffix.length):rawName;const urlData=splitLineAndColumn(rawUrl);const url=urlData.url||'';const line=urlData.lineNumber||0;const column=urlData.columnNumber||0;this.sourceInfo_=new tr.model.source_info.JSSourceInfo(url,line,column,isNative,scriptId,state);}
+TraceCodeEntry.prototype={get id(){return this.id_;},get sourceInfo(){return this.sourceInfo_;},get name(){return this.name_;},set address(address){this.address_=address;},get address(){return this.address_;},set size(size){this.size_=size;},get size(){return this.size_;}};return{TraceCodeEntry,};});'use strict';tr.exportTo('tr.e.importer',function(){function TraceCodeMap(){this.banks_=new Map();}
+TraceCodeMap.prototype={addEntry(addressHex,size,name,scriptId){const entry=new tr.e.importer.TraceCodeEntry(this.getAddress_(addressHex),size,name,scriptId);this.addEntry_(addressHex,entry);},moveEntry(oldAddressHex,newAddressHex,size){const entry=this.getBank_(oldAddressHex).removeEntry(this.getAddress_(oldAddressHex));if(!entry)return;entry.address=this.getAddress_(newAddressHex);entry.size=size;this.addEntry_(newAddressHex,entry);},lookupEntry(addressHex){return this.getBank_(addressHex).lookupEntry(this.getAddress_(addressHex));},addEntry_(addressHex,entry){this.getBank_(addressHex).addEntry(entry);},getAddress_(addressHex){const bankSizeHexDigits=13;addressHex=addressHex.slice(2);return parseInt(addressHex.slice(-bankSizeHexDigits),16);},getBank_(addressHex){addressHex=addressHex.slice(2);const bankSizeHexDigits=13;const maxHexDigits=16;const bankName=addressHex.slice(-maxHexDigits,-bankSizeHexDigits);let bank=this.banks_.get(bankName);if(!bank){bank=new TraceCodeBank();this.banks_.set(bankName,bank);}
+return bank;}};function TraceCodeBank(){this.entries_=[];}
+TraceCodeBank.prototype={removeEntry(address){if(this.entries_.length===0)return undefined;const index=tr.b.findLowIndexInSortedArray(this.entries_,function(entry){return entry.address;},address);const entry=this.entries_[index];if(!entry||entry.address!==address)return undefined;this.entries_.splice(index,1);return entry;},lookupEntry(address){const index=tr.b.findFirstTrueIndexInSortedArray(this.entries_,e=>(address<e.address))-1;const entry=this.entries_[index];return entry&&address<entry.address+entry.size?entry:undefined;},addEntry(newEntry){if(this.entries_.length===0){this.entries_.push(newEntry);}
+const endAddress=newEntry.address+newEntry.size;const lastIndex=tr.b.findLowIndexInSortedArray(this.entries_,function(entry){return entry.address;},endAddress);let index;for(index=lastIndex-1;index>=0;--index){const entry=this.entries_[index];const entryEndAddress=entry.address+entry.size;if(entryEndAddress<=newEntry.address)break;}
+++index;this.entries_.splice(index,lastIndex-index,newEntry);}};return{TraceCodeMap,};});'use strict';tr.exportTo('tr.e.measure',function(){const AsyncSlice=tr.model.AsyncSlice;const MEASURE_NAME_REGEX=/([^\/:]+):(.*?)(?:\/([A-Za-z0-9+/]+=?=?))?$/;function MeasureAsyncSlice(){this.groupTitle_='Ungrouped Measure';const matched=MEASURE_NAME_REGEX.exec(arguments[1]);if(matched!==null){arguments[1]=matched[2];this.groupTitle_=matched[1];}
+AsyncSlice.apply(this,arguments);}
+MeasureAsyncSlice.prototype={__proto__:AsyncSlice.prototype,get viewSubGroupTitle(){return this.groupTitle_;},get title(){return this.title_;},set title(title){this.title_=title;}};AsyncSlice.subTypes.register(MeasureAsyncSlice,{categoryParts:['blink.user_timing']});return{MEASURE_NAME_REGEX,MeasureAsyncSlice,};});'use strict';tr.exportTo('tr.importer',function(){function ContextProcessor(model){this.model_=model;this.activeContexts_=[];this.stackPerType_={};this.contextCache_={};this.contextSetCache_={};this.cachedEntryForActiveContexts_=undefined;this.seenSnapshots_={};}
+ContextProcessor.prototype={enterContext(contextType,scopedId){const newActiveContexts=[this.getOrCreateContext_(contextType,scopedId),];for(const oldContext of this.activeContexts_){if(oldContext.type===contextType){this.pushContext_(oldContext);}else{newActiveContexts.push(oldContext);}}
+this.activeContexts_=newActiveContexts;this.cachedEntryForActiveContexts_=undefined;},leaveContext(contextType,scopedId){this.leaveContextImpl_(context=>context.type===contextType&&context.snapshot.scope===scopedId.scope&&context.snapshot.idRef===scopedId.id);},destroyContext(scopedId){for(const stack of Object.values(this.stackPerType_)){let newLength=0;for(let i=0;i<stack.length;++i){if(stack[i].snapshot.scope!==scopedId.scope||stack[i].snapshot.idRef!==scopedId.id){stack[newLength++]=stack[i];}}
+stack.length=newLength;}
+this.leaveContextImpl_(context=>context.snapshot.scope===scopedId.scope&&context.snapshot.idRef===scopedId.id);},leaveContextImpl_(predicate){const newActiveContexts=[];for(const oldContext of this.activeContexts_){if(predicate(oldContext)){const previousContext=this.popContext_(oldContext.type);if(previousContext){newActiveContexts.push(previousContext);}}else{newActiveContexts.push(oldContext);}}
+this.activeContexts_=newActiveContexts;this.cachedEntryForActiveContexts_=undefined;},getOrCreateContext_(contextType,scopedId){const context={type:contextType,snapshot:{scope:scopedId.scope,idRef:scopedId.id}};const key=this.getContextKey_(context);if(key in this.contextCache_){return this.contextCache_[key];}
+this.contextCache_[key]=context;const snapshotKey=this.getSnapshotKey_(scopedId);this.seenSnapshots_[snapshotKey]=true;return context;},pushContext_(context){if(!(context.type in this.stackPerType_)){this.stackPerType_[context.type]=[];}
+this.stackPerType_[context.type].push(context);},popContext_(contextType){if(!(contextType in this.stackPerType_)){return undefined;}
+return this.stackPerType_[contextType].pop();},getContextKey_(context){return[context.type,context.snapshot.scope,context.snapshot.idRef].join('\x00');},getSnapshotKey_(scopedId){return[scopedId.scope,scopedId.idRef].join('\x00');},get activeContexts(){if(this.cachedEntryForActiveContexts_===undefined){let key=[];for(const context of this.activeContexts_){key.push(this.getContextKey_(context));}
+key.sort();key=key.join('\x00');if(key in this.contextSetCache_){this.cachedEntryForActiveContexts_=this.contextSetCache_[key];}else{this.activeContexts_.sort(function(a,b){const keyA=this.getContextKey_(a);const keyB=this.getContextKey_(b);if(keyA<keyB){return-1;}
+if(keyA>keyB){return 1;}
+return 0;}.bind(this));this.contextSetCache_[key]=Object.freeze(this.activeContexts_);this.cachedEntryForActiveContexts_=this.contextSetCache_[key];}}
+return this.cachedEntryForActiveContexts_;},invalidateContextCacheForSnapshot(scopedId){const snapshotKey=this.getSnapshotKey_(scopedId);if(!(snapshotKey in this.seenSnapshots_))return;this.contextCache_={};this.contextSetCache_={};this.cachedEntryForActiveContexts_=undefined;this.activeContexts_=this.activeContexts_.map(function(context){if(context.snapshot.scope!==scopedId.scope||context.snapshot.idRef!==scopedId.id){return context;}
+return{type:context.type,snapshot:{scope:context.snapshot.scope,idRef:context.snapshot.idRef}};});this.seenSnapshots_={};},};return{ContextProcessor,};});'use strict';tr.exportTo('tr.model',function(){function Annotation(){this.guid_=tr.b.GUID.allocateSimple();this.view_=undefined;}
+Annotation.fromDictIfPossible=function(args){if(args.typeName===undefined){throw new Error('Missing typeName argument');}
+const typeInfo=Annotation.findTypeInfoMatching(function(typeInfo){return typeInfo.metadata.typeName===args.typeName;});if(typeInfo===undefined)return undefined;return typeInfo.constructor.fromDict(args);};Annotation.fromDict=function(){throw new Error('Not implemented');};Annotation.prototype={get guid(){return this.guid_;},onRemove(){},toDict(){throw new Error('Not implemented');},getOrCreateView(viewport){if(!this.view_){this.view_=this.createView_(viewport);}
+return this.view_;},createView_(){throw new Error('Not implemented');}};const options=new tr.b.ExtensionRegistryOptions(tr.b.BASIC_REGISTRY_MODE);tr.b.decorateExtensionRegistry(Annotation,options);Annotation.addEventListener('will-register',function(e){if(!e.typeInfo.constructor.hasOwnProperty('fromDict')){throw new Error('Must have fromDict method');}
+if(!e.typeInfo.metadata.typeName){throw new Error('Registered Annotations must provide typeName');}});return{Annotation,};});'use strict';tr.exportTo('tr.model',function(){function YComponent(stableId,yPercentOffset){this.stableId=stableId;this.yPercentOffset=yPercentOffset;}
+YComponent.prototype={toDict(){return{stableId:this.stableId,yPercentOffset:this.yPercentOffset};}};function Location(xWorld,yComponents){this.xWorld_=xWorld;this.yComponents_=yComponents;}
+Location.fromViewCoordinates=function(viewport,viewX,viewY){const dt=viewport.currentDisplayTransform;const xWorld=dt.xViewToWorld(viewX);const yComponents=[];let elem=document.elementFromPoint(viewX+viewport.modelTrackContainer.canvas.offsetLeft,viewY+viewport.modelTrackContainer.canvas.offsetTop);while(elem instanceof tr.ui.tracks.Track){if(elem.eventContainer){const boundRect=elem.getBoundingClientRect();const yPercentOffset=(viewY-boundRect.top)/boundRect.height;yComponents.push(new YComponent(elem.eventContainer.stableId,yPercentOffset));}
+elem=elem.parentElement;}
+if(yComponents.length===0)return;return new Location(xWorld,yComponents);};Location.fromStableIdAndTimestamp=function(viewport,stableId,ts){const xWorld=ts;const yComponents=[];const containerToTrack=viewport.containerToTrackMap;let elem=containerToTrack.getTrackByStableId(stableId);if(!elem)return;const firstY=elem.getBoundingClientRect().top;while(elem instanceof tr.ui.tracks.Track){if(elem.eventContainer){const boundRect=elem.getBoundingClientRect();const yPercentOffset=(firstY-boundRect.top)/boundRect.height;yComponents.push(new YComponent(elem.eventContainer.stableId,yPercentOffset));}
+elem=elem.parentElement;}
+if(yComponents.length===0)return;return new Location(xWorld,yComponents);};Location.prototype={get xWorld(){return this.xWorld_;},getContainingTrack(viewport){const containerToTrack=viewport.containerToTrackMap;for(const i in this.yComponents_){const yComponent=this.yComponents_[i];const track=containerToTrack.getTrackByStableId(yComponent.stableId);if(track!==undefined)return track;}},toViewCoordinates(viewport){const dt=viewport.currentDisplayTransform;const containerToTrack=viewport.containerToTrackMap;const viewX=dt.xWorldToView(this.xWorld_);let viewY=-1;for(const index in this.yComponents_){const yComponent=this.yComponents_[index];const track=containerToTrack.getTrackByStableId(yComponent.stableId);if(track!==undefined){const boundRect=track.getBoundingClientRect();viewY=yComponent.yPercentOffset*boundRect.height+boundRect.top;break;}}
+return{viewX,viewY};},toDict(){return{xWorld:this.xWorld_,yComponents:this.yComponents_};}};return{Location,};});'use strict';tr.exportTo('tr.ui.annotations',function(){function AnnotationView(viewport,annotation){}
+AnnotationView.prototype={draw(ctx){throw new Error('Not implemented');}};return{AnnotationView,};});'use strict';tr.exportTo('tr.ui.annotations',function(){function RectAnnotationView(viewport,annotation){this.viewport_=viewport;this.annotation_=annotation;}
+RectAnnotationView.prototype={__proto__:tr.ui.annotations.AnnotationView.prototype,draw(ctx){const dt=this.viewport_.currentDisplayTransform;const startCoords=this.annotation_.startLocation.toViewCoordinates(this.viewport_);const endCoords=this.annotation_.endLocation.toViewCoordinates(this.viewport_);let startY=startCoords.viewY-ctx.canvas.getBoundingClientRect().top;const sizeY=endCoords.viewY-startCoords.viewY;if(startY+sizeY<0){startY=sizeY;}else if(startY<0){startY=0;}
+ctx.fillStyle=this.annotation_.fillStyle;ctx.fillRect(startCoords.viewX,startY,endCoords.viewX-startCoords.viewX,sizeY);}};return{RectAnnotationView,};});'use strict';tr.exportTo('tr.model',function(){function RectAnnotation(start,end){tr.model.Annotation.apply(this,arguments);this.startLocation_=start;this.endLocation_=end;this.fillStyle='rgba(255, 180, 0, 0.3)';}
+RectAnnotation.fromDict=function(dict){const args=dict.args;const startLoc=new tr.model.Location(args.start.xWorld,args.start.yComponents);const endLoc=new tr.model.Location(args.end.xWorld,args.end.yComponents);return new tr.model.RectAnnotation(startLoc,endLoc);};RectAnnotation.prototype={__proto__:tr.model.Annotation.prototype,get startLocation(){return this.startLocation_;},get endLocation(){return this.endLocation_;},toDict(){return{typeName:'rect',args:{start:this.startLocation.toDict(),end:this.endLocation.toDict()}};},createView_(viewport){return new tr.ui.annotations.RectAnnotationView(viewport,this);}};tr.model.Annotation.register(RectAnnotation,{typeName:'rect'});return{RectAnnotation,};});'use strict';tr.exportTo('tr.ui.annotations',function(){function CommentBoxAnnotationView(viewport,annotation){this.viewport_=viewport;this.annotation_=annotation;this.textArea_=undefined;this.styleWidth=250;this.styleHeight=50;this.fontSize=10;this.rightOffset=50;this.topOffset=25;}
+CommentBoxAnnotationView.prototype={__proto__:tr.ui.annotations.AnnotationView.prototype,removeTextArea(){Polymer.dom(Polymer.dom(this.textArea_).parentNode).removeChild(this.textArea_);},draw(ctx){const coords=this.annotation_.location.toViewCoordinates(this.viewport_);if(coords.viewX<0){if(this.textArea_){this.textArea_.style.visibility='hidden';}
+return;}
+if(!this.textArea_){this.textArea_=document.createElement('textarea');this.textArea_.style.position='absolute';this.textArea_.readOnly=true;this.textArea_.value=this.annotation_.text;this.textArea_.style.zIndex=1;Polymer.dom(Polymer.dom(ctx.canvas).parentNode).appendChild(this.textArea_);}
+this.textArea_.style.width=this.styleWidth+'px';this.textArea_.style.height=this.styleHeight+'px';this.textArea_.style.fontSize=this.fontSize+'px';this.textArea_.style.visibility='visible';this.textArea_.style.left=coords.viewX+ctx.canvas.getBoundingClientRect().left+
+this.rightOffset+'px';this.textArea_.style.top=coords.viewY-ctx.canvas.getBoundingClientRect().top-
+this.topOffset+'px';ctx.strokeStyle='rgb(0, 0, 0)';ctx.lineWidth=2;ctx.beginPath();tr.ui.b.drawLine(ctx,coords.viewX,coords.viewY-ctx.canvas.getBoundingClientRect().top,coords.viewX+this.rightOffset,coords.viewY-this.topOffset-
+ctx.canvas.getBoundingClientRect().top);ctx.stroke();}};return{CommentBoxAnnotationView,};});'use strict';tr.exportTo('tr.model',function(){function CommentBoxAnnotation(location,text){tr.model.Annotation.apply(this,arguments);this.location=location;this.text=text;}
+CommentBoxAnnotation.fromDict=function(dict){const args=dict.args;const location=new tr.model.Location(args.location.xWorld,args.location.yComponents);return new tr.model.CommentBoxAnnotation(location,args.text);};CommentBoxAnnotation.prototype={__proto__:tr.model.Annotation.prototype,onRemove(){this.view_.removeTextArea();},toDict(){return{typeName:'comment_box',args:{text:this.text,location:this.location.toDict()}};},createView_(viewport){return new tr.ui.annotations.CommentBoxAnnotationView(viewport,this);}};tr.model.Annotation.register(CommentBoxAnnotation,{typeName:'comment_box'});return{CommentBoxAnnotation,};});'use strict';tr.exportTo('tr.model',function(){function ScopedId(scope,id,pid){if(scope===undefined){throw new Error('Scope should be defined. Use \''+
+tr.model.OBJECT_DEFAULT_SCOPE+'\' as the default scope.');}
+this.scope=scope;this.id=id;this.pid=pid;}
+ScopedId.prototype={toString(){const pidStr=this.pid===undefined?'':'pid: '+this.pid+', ';return'{'+pidStr+'scope: '+this.scope+', id: '+this.id+'}';},toStringWithDelimiter(delim){return(this.pid===undefined?'':this.pid)+delim+
+this.scope+delim+this.id;}};return{ScopedId,};});'use strict';tr.exportTo('tr.ui.annotations',function(){function XMarkerAnnotationView(viewport,annotation){this.viewport_=viewport;this.annotation_=annotation;}
+XMarkerAnnotationView.prototype={__proto__:tr.ui.annotations.AnnotationView.prototype,draw(ctx){const dt=this.viewport_.currentDisplayTransform;const viewX=dt.xWorldToView(this.annotation_.timestamp);ctx.beginPath();tr.ui.b.drawLine(ctx,viewX,0,viewX,ctx.canvas.height);ctx.strokeStyle=this.annotation_.strokeStyle;ctx.stroke();}};return{XMarkerAnnotationView,};});'use strict';tr.exportTo('tr.model',function(){function XMarkerAnnotation(timestamp){tr.model.Annotation.apply(this,arguments);this.timestamp=timestamp;this.strokeStyle='rgba(0, 0, 255, 0.5)';}
+XMarkerAnnotation.fromDict=function(dict){return new XMarkerAnnotation(dict.args.timestamp);};XMarkerAnnotation.prototype={__proto__:tr.model.Annotation.prototype,toDict(){return{typeName:'xmarker',args:{timestamp:this.timestamp}};},createView_(viewport){return new tr.ui.annotations.XMarkerAnnotationView(viewport,this);}};tr.model.Annotation.register(XMarkerAnnotation,{typeName:'xmarker'});return{XMarkerAnnotation,};});'use strict';tr.exportTo('tr.e.importer',function(){const Base64=tr.b.Base64;const deepCopy=tr.b.deepCopy;const ColorScheme=tr.b.ColorScheme;const HeapDumpTraceEventImporter=tr.e.importer.HeapDumpTraceEventImporter;const LegacyHeapDumpTraceEventImporter=tr.e.importer.LegacyHeapDumpTraceEventImporter;const StreamingEventExpander=tr.e.importer.StreamingEventExpander;const ProfilingDictionaryReader=tr.e.importer.ProfilingDictionaryReader;const MEASURE_NAME_REGEX=tr.e.measure.MEASURE_NAME_REGEX;function getEventColor(event,opt_customName){if(event.cname){return ColorScheme.getColorIdForReservedName(event.cname);}else if(opt_customName||event.name){return ColorScheme.getColorIdForGeneralPurposeString(opt_customName||event.name);}}
+function isLegacyChromeClockSyncEvent(event){return event.name!==undefined&&event.name.startsWith(LEGACY_CHROME_CLOCK_SYNC_EVENT_NAME_PREFIX)&&((event.ph==='S')||(event.ph==='F'));}
+const PRODUCER='producer';const CONSUMER='consumer';const STEP='step';const BACKGROUND=tr.model.ContainerMemoryDump.LevelOfDetail.BACKGROUND;const LIGHT=tr.model.ContainerMemoryDump.LevelOfDetail.LIGHT;const DETAILED=tr.model.ContainerMemoryDump.LevelOfDetail.DETAILED;const MEMORY_DUMP_LEVEL_OF_DETAIL_ORDER=[undefined,BACKGROUND,LIGHT,DETAILED];const GLOBAL_MEMORY_ALLOCATOR_DUMP_PREFIX='global/';const LEGACY_CHROME_CLOCK_SYNC_EVENT_NAME_PREFIX='ClockSyncEvent.';const BYTE_STAT_NAME_MAP={'pc':'privateCleanResident','pd':'privateDirtyResident','sc':'sharedCleanResident','sd':'sharedDirtyResident','pss':'proportionalResident','sw':'swapped'};const WEAK_MEMORY_ALLOCATOR_DUMP_FLAG=1<<0;const OBJECT_TYPE_NAME_PATTERNS=[{prefix:'const char *WTF::getStringWithTypeName() [T = ',suffix:']'},{prefix:'const char* WTF::getStringWithTypeName() [with T = ',suffix:']'},{prefix:'const char *__cdecl WTF::getStringWithTypeName<',suffix:'>(void)'}];const SUBTRACE_FIELDS=new Set(['powerTraceAsString','systemTraceEvents','androidProcessDump',]);const NON_METADATA_FIELDS=new Set(['displayTimeUnit','samples','stackFrames','traceAnnotations','traceEvents',...SUBTRACE_FIELDS]);function TraceEventImporter(model,eventData){this.hasEvents_=undefined;this.importPriority=1;this.model_=model;this.events_=undefined;this.sampleEvents_=undefined;this.stackFrameEvents_=undefined;this.stackFrameTree_=new tr.model.ProfileTree();this.subtraces_=[];this.eventsWereFromString_=false;this.softwareMeasuredCpuCount_=undefined;this.allAsyncEvents_=[];this.allFlowEvents_=[];this.allObjectEvents_=[];this.contextProcessorPerThread={};this.traceEventSampleStackFramesByName_={};this.v8ProcessCodeMaps_={};this.v8ProcessRootStackFrame_={};this.v8SamplingData_=[];this.profileTrees_=new Map();this.profileInfo_=new Map();this.legacyChromeClockSyncStartEvent_=undefined;this.legacyChromeClockSyncFinishEvent_=undefined;this.allMemoryDumpEvents_={};this.heapProfileExpander=new ProfilingDictionaryReader();this.objectTypeNameMap_={};this.clockDomainId_=tr.model.ClockDomainId.UNKNOWN_CHROME_LEGACY;this.toModelTime_=undefined;if(typeof(eventData)==='string'||eventData instanceof String){eventData=eventData.trim();if(eventData[0]==='['){eventData=eventData.replace(/\s*,\s*$/,'');if(eventData[eventData.length-1]!==']'){eventData=eventData+']';}}
+this.events_=JSON.parse(eventData);this.eventsWereFromString_=true;}else{this.events_=eventData;}
+if(this.events_.traceEvents){const container=this.events_;this.events_=this.events_.traceEvents;for(const subtraceField of SUBTRACE_FIELDS){if(container[subtraceField]){this.storeSubtrace_(container[subtraceField]);}}
+this.storeSamples_(container.samples);this.storeStackFrames_(container.stackFrames);this.storeDisplayTimeUnit_(container.displayTimeUnit);this.storeTraceAnnotations_(container.traceAnnotations);this.storeMetadata_(container);}else if(this.events_ instanceof tr.b.TraceStream){const parser=oboe().node('{cat ph}',function(e){return oboe.drop;}).node('!.powerTraceAsString',this.storeSubtrace_.bind(this)).node('!.systemTraceEvents',this.storeSubtrace_.bind(this)).node('!.samples',this.storeSamples_.bind(this)).node('!.stackFrames',this.storeStackFrames_.bind(this)).node('!.displayTimeUnit',this.storeDisplayTimeUnit_.bind(this)).node('!.traceAnnotations',this.storeTraceAnnotations_.bind(this)).done(this.storeMetadata_.bind(this));this.events_.rewind();while(this.events_.hasData){parser.write(this.events_.readNumBytes());}
+parser.finish();}}
+TraceEventImporter.canImport=function(eventData){if(eventData instanceof tr.b.TraceStream){if(eventData.isBinary)return false;eventData=eventData.header;}
+if(typeof(eventData)==='string'||eventData instanceof String){eventData=eventData.trim();return eventData[0]==='{'||eventData[0]==='[';}
+if(eventData instanceof Array&&eventData.length&&eventData[0].ph){return true;}
+if(eventData.traceEvents){if(eventData.traceEvents instanceof Array){if(eventData.traceEvents.length&&eventData.traceEvents[0].ph){return true;}
+if(eventData.samples&&eventData.samples.length&&eventData.stackFrames!==undefined){return true;}}}
+return false;};TraceEventImporter.scopedIdForEvent_=function(event){const scope=event.scope||tr.model.OBJECT_DEFAULT_SCOPE;let pid=undefined;if(event.id!==undefined){if(event.id2!==undefined){throw new Error('Event has both id and id2');}
+pid=tr.model.LOCAL_ID_PHASES.has(event.ph)?event.pid:undefined;return new tr.model.ScopedId(scope,event.id,pid);}else if(event.id2!==undefined){if(event.id2.global!==undefined){return new tr.model.ScopedId(scope,event.id2.global);}else if(event.id2.local!==undefined){return new tr.model.ScopedId(scope,event.id2.local,event.pid);}
+throw new Error('Event that uses id2 must have either a global or local ID');}
+return undefined;};TraceEventImporter.prototype={__proto__:tr.importer.Importer.prototype,get importerName(){return'TraceEventImporter';},extractSubtraces(){const subtraces=this.subtraces_;this.subtraces_=[];return subtraces;},deepCopyIfNeeded_(obj){if(obj===undefined)obj={};if(this.eventsWereFromString_)return obj;return deepCopy(obj);},deepCopyAlways_(obj){if(obj===undefined)obj={};return deepCopy(obj);},processAsyncEvent(event){const thread=this.model_.getOrCreateProcess(event.pid).getOrCreateThread(event.tid);this.allAsyncEvents_.push({sequenceNumber:this.allAsyncEvents_.length,event,thread});},processFlowEvent(event,opt_slice){const thread=this.model_.getOrCreateProcess(event.pid).getOrCreateThread(event.tid);this.allFlowEvents_.push({refGuid:tr.b.GUID.getLastSimpleGuid(),sequenceNumber:this.allFlowEvents_.length,event,slice:opt_slice,thread});},processCounterEvent(event){let ctrName;if(event.id!==undefined){ctrName=event.name+'['+event.id+']';}else{ctrName=event.name;}
+const ctr=this.model_.getOrCreateProcess(event.pid).getOrCreateCounter(event.cat,ctrName);const reservedColorId=event.cname?getEventColor(event):undefined;if(ctr.numSeries===0){for(const seriesName in event.args){const colorId=reservedColorId||getEventColor(event,ctr.name+'.'+seriesName);ctr.addSeries(new tr.model.CounterSeries(seriesName,colorId));}
+if(ctr.numSeries===0){this.model_.importWarning({type:'counter_parse_error',message:'Expected counter '+event.name+' to have at least one argument to use as a value.'});delete ctr.parent.counters[ctr.name];return;}}
+const ts=this.toModelTimeFromUs_(event.ts);ctr.series.forEach(function(series){const val=event.args[series.name]?event.args[series.name]:0;series.addCounterSample(ts,val);});},processObjectEvent(event){const thread=this.model_.getOrCreateProcess(event.pid).getOrCreateThread(event.tid);this.allObjectEvents_.push({sequenceNumber:this.allObjectEvents_.length,event,thread});if(thread.guid in this.contextProcessorPerThread){const processor=this.contextProcessorPerThread[thread.guid];const scopedId=TraceEventImporter.scopedIdForEvent_(event);if(event.ph==='D'){processor.destroyContext(scopedId);}
+processor.invalidateContextCacheForSnapshot(scopedId);}},processContextEvent(event){const thread=this.model_.getOrCreateProcess(event.pid).getOrCreateThread(event.tid);if(!(thread.guid in this.contextProcessorPerThread)){this.contextProcessorPerThread[thread.guid]=new tr.importer.ContextProcessor(this.model_);}
+const scopedId=TraceEventImporter.scopedIdForEvent_(event);const contextType=event.name;const processor=this.contextProcessorPerThread[thread.guid];if(event.ph==='('){processor.enterContext(contextType,scopedId);}else if(event.ph===')'){processor.leaveContext(contextType,scopedId);}else{this.model_.importWarning({type:'unknown_context_phase',message:'Unknown context event phase: '+event.ph+'.'});}},setContextsFromThread_(thread,slice){if(thread.guid in this.contextProcessorPerThread){slice.contexts=this.contextProcessorPerThread[thread.guid].activeContexts;}},processDurationEvent(event){const thread=this.model_.getOrCreateProcess(event.pid).getOrCreateThread(event.tid);const ts=this.toModelTimeFromUs_(event.ts);if(event.dur===0&&!thread.sliceGroup.isTimestampValidForBeginOrEnd(ts)){this.model_.importWarning({type:'duration_parse_error',message:'Timestamps are moving backward.'});return;}
+if(event.ph==='B'){const slice=thread.sliceGroup.beginSlice(event.cat,event.name,this.toModelTimeFromUs_(event.ts),this.deepCopyIfNeeded_(event.args),this.toModelTimeFromUs_(event.tts),event.argsStripped,getEventColor(event),event.bind_id);slice.startStackFrame=this.getStackFrameForEvent_(event);this.setContextsFromThread_(thread,slice);}else if(event.ph==='I'||event.ph==='i'||event.ph==='R'){if(event.s!==undefined&&event.s!=='t'){throw new Error('This should never happen');}
+thread.sliceGroup.beginSlice(event.cat,event.name,this.toModelTimeFromUs_(event.ts),this.deepCopyIfNeeded_(event.args),this.toModelTimeFromUs_(event.tts),event.argsStripped,getEventColor(event),event.bind_id);const slice=thread.sliceGroup.endSlice(this.toModelTimeFromUs_(event.ts),this.toModelTimeFromUs_(event.tts));slice.startStackFrame=this.getStackFrameForEvent_(event);slice.endStackFrame=undefined;}else{if(!thread.sliceGroup.openSliceCount){this.model_.importWarning({type:'duration_parse_error',message:'E phase event without a matching B phase event.'});return;}
+const slice=thread.sliceGroup.endSlice(this.toModelTimeFromUs_(event.ts),this.toModelTimeFromUs_(event.tts),getEventColor(event));if(event.name&&slice.title!==event.name){this.model_.importWarning({type:'title_match_error',message:'Titles do not match. Title is '+
+slice.title+' in openSlice, and is '+
+event.name+' in endSlice'});}
+slice.endStackFrame=this.getStackFrameForEvent_(event);this.mergeArgsInto_(slice.args,event.args,slice.title);}},mergeArgsInto_(dstArgs,srcArgs,eventName){for(const arg in srcArgs){if(dstArgs[arg]!==undefined){this.model_.importWarning({type:'arg_merge_error',message:'Different phases of '+eventName+' provided values for argument '+arg+'.'+' The last provided value will be used.'});}
+dstArgs[arg]=this.deepCopyIfNeeded_(srcArgs[arg]);}},processCompleteEvent(event){if(event.cat!==undefined&&event.cat.indexOf('trace_event_overhead')>-1){return undefined;}
+const thread=this.model_.getOrCreateProcess(event.pid).getOrCreateThread(event.tid);if(event.flow_out){if(event.flow_in){event.flowPhase=STEP;}else{event.flowPhase=PRODUCER;}}else if(event.flow_in){event.flowPhase=CONSUMER;}
+const slice=thread.sliceGroup.pushCompleteSlice(event.cat,event.name,this.toModelTimeFromUs_(event.ts),this.durationFromUs_(event.dur),this.maybeToModelTimeFromUs_(event.tts),this.durationFromUs_(event.tdur),this.deepCopyIfNeeded_(event.args),event.argsStripped,getEventColor(event),event.bind_id);slice.startStackFrame=this.getStackFrameForEvent_(event);slice.endStackFrame=this.getStackFrameForEvent_(event,true);this.setContextsFromThread_(thread,slice);return slice;},processJitCodeEvent(event){if(this.v8ProcessCodeMaps_[event.pid]===undefined){this.v8ProcessCodeMaps_[event.pid]=new tr.e.importer.TraceCodeMap();}
+const map=this.v8ProcessCodeMaps_[event.pid];const data=event.args.data;if(event.name==='JitCodeMoved'){map.moveEntry(data.code_start,data.new_code_start,data.code_len);}else{map.addEntry(data.code_start,data.code_len,data.name,data.script_id);}},processMetadataEvent(event){if(event.name==='JitCodeAdded'||event.name==='JitCodeMoved'){this.v8SamplingData_.push(event);return;}
+if(event.argsStripped)return;if(event.name==='process_name'){const process=this.model_.getOrCreateProcess(event.pid);process.name=event.args.name;}else if(event.name==='process_labels'){const process=this.model_.getOrCreateProcess(event.pid);const stackFrames=event.args.stackFrames;if(event.args.labels===undefined){this.model_.importWarning({type:'metadata_parse_error',message:'No labels found in a \''+event.name+'\' metadata event'});}else{const labels=event.args.labels.split(',');for(let i=0;i<labels.length;i++){process.addLabelIfNeeded(labels[i]);}}}else if(event.name==='process_uptime_seconds'){const process=this.model_.getOrCreateProcess(event.pid);process.uptime_seconds=event.args.uptime;}else if(event.name==='process_sort_index'){const process=this.model_.getOrCreateProcess(event.pid);process.sortIndex=event.args.sort_index;}else if(event.name==='thread_name'){const thread=this.model_.getOrCreateProcess(event.pid).getOrCreateThread(event.tid);thread.name=event.args.name;}else if(event.name==='thread_sort_index'){const thread=this.model_.getOrCreateProcess(event.pid).getOrCreateThread(event.tid);thread.sortIndex=event.args.sort_index;}else if(event.name==='num_cpus'){let n=event.args.number;if(this.softwareMeasuredCpuCount_!==undefined){n=Math.max(n,this.softwareMeasuredCpuCount_);}
+this.softwareMeasuredCpuCount_=n;}else if(event.name==='stackFrames'){const stackFrames=event.args.stackFrames;if(stackFrames===undefined){this.model_.importWarning({type:'metadata_parse_error',message:'No stack frames found in a \''+event.name+'\' metadata event'});}else{this.importStackFrames_(stackFrames,'p'+event.pid+':');}}else if(event.name==='typeNames'){const objectTypeNameMap=event.args.typeNames;if(objectTypeNameMap===undefined){this.model_.importWarning({type:'metadata_parse_error',message:'No mapping from object type IDs to names found in a \''+
+event.name+'\' metadata event'});}else{this.importObjectTypeNameMap_(objectTypeNameMap,event.pid);}}else if(event.name==='TraceConfig'){this.model_.metadata.push({name:'TraceConfig',value:event.args.value});}else{this.model_.importWarning({type:'metadata_parse_error',message:'Unrecognized metadata name: '+event.name});}},processInstantEvent(event){if(event.name==='JitCodeAdded'||event.name==='JitCodeMoved'){this.v8SamplingData_.push(event);return;}
+if(event.s==='t'||event.s===undefined){this.processDurationEvent(event);return;}
+let constructor;let parent;switch(event.s){case'g':constructor=tr.model.GlobalInstantEvent;parent=this.model_;break;case'p':constructor=tr.model.ProcessInstantEvent;parent=this.model_.getOrCreateProcess(event.pid);break;default:this.model_.importWarning({type:'instant_parse_error',message:'I phase event with unknown "s" field value.'});return;}
+const instantEvent=new constructor(event.cat,event.name,getEventColor(event),this.toModelTimeFromUs_(event.ts),this.deepCopyIfNeeded_(event.args),parent);parent.instantEvents.push(instantEvent);},getOrCreateProfileTree_(sampleType,id){if(!this.profileTrees_.has(sampleType)){this.profileTrees_.set(sampleType,new Map());}
+const profileTreeMap=this.profileTrees_.get(sampleType);if(profileTreeMap.has(id)){return profileTreeMap.get(id);}
+const profileTree=new tr.model.ProfileTree();profileTreeMap.set(id,profileTree);const info=this.profileInfo_.get(id);if(info!==undefined){profileTree.startTime=info.startTime;profileTree.pid=info.pid;profileTree.tid=info.tid;}
+return profileTree;},processSample(event){if(event.args===undefined||event.args.data===undefined){return;}
+if(event.id===undefined){throw new Error('No event ID in sample');}
+const data=event.args.data;if(data.startTime!==undefined){this.profileInfo_.set(event.id,{startTime:data.startTime,pid:event.pid,tid:event.tid});}
+const timeDeltas=data.timeDeltas;for(const sampleType in data){if(sampleType==='timeDeltas'||sampleType==='startTime'){continue;}
+if(data[sampleType].samples&&timeDeltas&&data[sampleType].samples.length!==timeDeltas.length){throw new Error('samples and timeDeltas array should have same length');}
+const profileTree=this.getOrCreateProfileTree_(sampleType,event.id);const nodes=data[sampleType].nodes;const samples=data[sampleType].samples;if(nodes!==undefined){for(const node of nodes){const ProfileNodeType=tr.model.ProfileNode.subTypes.getConstructor(undefined,sampleType);const profileNode=ProfileNodeType.constructFromObject(profileTree,node);if(profileNode===undefined){continue;}
+profileTree.add(profileNode);}}
+if(samples!==undefined){const thread=this.model_.getOrCreateProcess(profileTree.pid).getOrCreateThread(profileTree.tid);for(let i=0,len=samples.length;i<len;++i){const node=profileTree.getNode(samples[i]);profileTree.endTime+=timeDeltas[i];if(node===undefined)continue;const start=this.toModelTimeFromUs_(profileTree.endTime);this.model_.samples.push(new tr.model.Sample(start,node.sampleTitle,node,thread));}}}},processLegacyV8Sample(event){const data=event.args.data;const sampleType='legacySample';const ProfileNodeType=tr.model.ProfileNode.subTypes.getConstructor(undefined,sampleType);if(data.vm_state==='js'&&!data.stack.length)return;const profileTree=this.getOrCreateProfileTree_(sampleType,event.pid);if(profileTree.getNode(-1)===undefined){profileTree.add(new ProfileNodeType(-1,{url:'',scriptId:-1,functionName:'unknown'},undefined));}
+let node=undefined;if(data.stack.length>0&&this.v8ProcessCodeMaps_[event.pid]){const map=this.v8ProcessCodeMaps_[event.pid];data.stack.reverse();let parentNode=undefined;for(let i=0;i<data.stack.length;i++){const entry=map.lookupEntry(data.stack[i]);if(entry===undefined){node=profileTree.getNode(-1);}else{node=profileTree.getNode(entry.id);if(node===undefined){const sourceInfo=entry.sourceInfo;node=new ProfileNodeType(entry.id,{functionName:entry.name,url:entry.sourceInfo.file,lineNumber:sourceInfo.line!==-1?sourceInfo.line:undefined,columnNumber:sourceInfo.column!==-1?sourceInfo.column:undefined,scriptid:entry.sourceInfo.scriptId},parentNode);profileTree.add(node);}}
+parentNode=node;}}else{node=profileTree.getNode(data.vm_state);if(node===undefined){node=new ProfileNodeType(data.vm_state,{url:'',functionName:data.vm_state},undefined);profileTree.add(node);}}
+const thread=this.model_.getOrCreateProcess(event.pid).getOrCreateThread(event.tid);this.model_.samples.push(new tr.model.Sample(this.toModelTimeFromUs_(event.ts),node.sampleTitle,node,thread));},processTraceSampleEvent(event){if(event.name==='V8Sample'||event.name.startsWith('Profile')){this.v8SamplingData_.push(event);return;}
+let node=this.stackFrameTree_.getNode(event.name);if(node===undefined&&event.sf!==undefined){node=this.stackFrameTree_.getNode('g'+event.sf);}
+if(node===undefined){let id=event.name;if(event.sf){id='g'+event.sf;}
+const ProfileNodeType=tr.model.ProfileNode.subTypes.getConstructor(undefined,'legacySample');node=this.stackFrameTree_.add(new ProfileNodeType(id,{functionName:event.name},undefined));}
+const thread=this.model_.getOrCreateProcess(event.pid).getOrCreateThread(event.tid);const sample=new tr.model.Sample(this.toModelTimeFromUs_(event.ts),'Trace Event Sample',node,thread,undefined,1,this.deepCopyIfNeeded_(event.args));this.setContextsFromThread_(thread,sample);this.model_.samples.push(sample);},processMemoryDumpEvent(event){if(event.ph!=='v'){throw new Error('Invalid memory dump event phase "'+event.ph+'".');}
+const dumpId=event.id;if(dumpId===undefined){this.model_.importWarning({type:'memory_dump_parse_error',message:'Memory dump event (phase \''+event.ph+'\') without a dump ID.'});return;}
+const pid=event.pid;if(pid===undefined){this.model_.importWarning({type:'memory_dump_parse_error',message:'Memory dump event (phase\''+event.ph+'\', dump ID \''+
+dumpId+'\') without a PID.'});return;}
+const allEvents=this.allMemoryDumpEvents_;let dumpIdEvents=allEvents[dumpId];if(dumpIdEvents===undefined){allEvents[dumpId]=dumpIdEvents={};}
+let processEvents=dumpIdEvents[pid];if(processEvents===undefined){dumpIdEvents[pid]=processEvents=[];}
+processEvents.push(event);},processClockSyncEvent(event){if(event.ph!=='c'){throw new Error('Invalid clock sync event phase "'+event.ph+'".');}
+const syncId=event.args.sync_id;if(syncId===undefined){this.model_.importWarning({type:'clock_sync_parse_error',message:'Clock sync at time '+event.ts+' without an ID.'});return;}
+if(event.args&&event.args.issue_ts!==undefined){this.model_.clockSyncManager.addClockSyncMarker(this.clockDomainId_,syncId,tr.b.Unit.timestampFromUs(event.args.issue_ts),tr.b.Unit.timestampFromUs(event.ts));}else{this.model_.clockSyncManager.addClockSyncMarker(this.clockDomainId_,syncId,tr.b.Unit.timestampFromUs(event.ts));}},processLegacyChromeClockSyncEvent(event){if(event.ph==='S'){this.legacyChromeClockSyncStartEvent_=event;}else if(event.ph==='F'){this.legacyChromeClockSyncFinishEvent_=event;}
+if(this.legacyChromeClockSyncStartEvent_===undefined||this.legacyChromeClockSyncFinishEvent_===undefined){return;}
+const startSyncId=this.legacyChromeClockSyncStartEvent_.name.substring(LEGACY_CHROME_CLOCK_SYNC_EVENT_NAME_PREFIX.length);const finishSyncId=this.legacyChromeClockSyncFinishEvent_.name.substring(LEGACY_CHROME_CLOCK_SYNC_EVENT_NAME_PREFIX.length);if(startSyncId!==finishSyncId){throw new Error('Inconsistent clock sync ID of legacy Chrome clock sync events');}
+this.model_.clockSyncManager.addClockSyncMarker(this.clockDomainId_,startSyncId,tr.b.Unit.timestampFromUs(this.legacyChromeClockSyncStartEvent_.ts),tr.b.Unit.timestampFromUs(this.legacyChromeClockSyncFinishEvent_.ts));},processV8Events(){this.v8SamplingData_.sort(function(a,b){if(a.ts!==b.ts)return a.ts-b.ts;if(a.ph==='M'||a.ph==='I'){return-1;}else if(b.ph==='M'||b.ph==='I'){return 1;}
+return 0;});const length=this.v8SamplingData_.length;for(let i=0;i<length;++i){const event=this.v8SamplingData_[i];if(event.ph==='M'||event.ph==='I'){this.processJitCodeEvent(event);}else if(event.ph==='P'){if(event.name.startsWith('Profile')){this.processSample(event);}else{this.processLegacyV8Sample(event);}}}},importClockSyncMarkers(){if(this.events_ instanceof tr.b.TraceStream){const parser=oboe().node('{cat ph}',this.importClockSyncMarker_.bind(this));this.events_.rewind();while(this.events_.hasData){parser.write(this.events_.readNumBytes());}
+parser.finish();}else{for(let i=0;i<this.events_.length;i++){this.importClockSyncMarker_(this.events_[i]);}}},importClockSyncMarker_(event){const isLegacyChromeClockSync=isLegacyChromeClockSyncEvent(event);if(event.ph!=='c'&&!isLegacyChromeClockSync)return;const eventSizeInBytes=this.model_.importOptions.trackDetailedModelStats?JSON.stringify(event).length:undefined;this.model_.stats.willProcessBasicTraceEvent('clock_sync',event.cat,event.name,event.ts,eventSizeInBytes);if(isLegacyChromeClockSync){this.processLegacyChromeClockSyncEvent(event);}else{this.processClockSyncEvent(event);}},importEvents(){this.hasEvents_=false;if(this.stackFrameEvents_){this.importStackFrames_(this.stackFrameEvents_,'g');}
+if(this.traceAnnotations_)this.importAnnotations_();if(this.events_ instanceof tr.b.TraceStream){const parser=oboe().node('{cat ph}',this.processEvent_.bind(this));this.events_.rewind();while(this.events_.hasData){parser.write(this.events_.readNumBytes());}
+parser.finish();}else{for(let eI=0;eI<this.events_.length;eI++){this.processEvent_(this.events_[eI]);}}
+this.createAsyncSlices_();this.processV8Events();for(const frame of Object.values(this.v8ProcessRootStackFrame_)){frame.removeAllChildren();}},storeSubtrace_(subtrace){this.subtraces_.push(subtrace);return oboe.drop;},storeSamples_(samples){this.sampleEvents_=samples;return oboe.drop;},storeStackFrames_(stackFrames){this.stackFrameEvents_=stackFrames;return oboe.drop;},storeDisplayTimeUnit_(unitName){if(!unitName)return;const unit=tr.b.TimeDisplayModes[unitName];if(unit===undefined){throw new Error('Unit '+unitName+' is not supported.');}
+this.model_.intrinsicTimeUnit=unit;return oboe.drop;},storeTraceAnnotations_(traceAnnotations){this.traceAnnotations_=traceAnnotations;return oboe.drop;},storeMetadata_(container){for(const fieldName of Object.keys(container)){if(NON_METADATA_FIELDS.has(fieldName))continue;this.model_.metadata.push({name:fieldName,value:container[fieldName]});if(fieldName!=='metadata')continue;const metadata=container[fieldName];if(metadata['highres-ticks']){this.model_.isTimeHighResolution=metadata['highres-ticks'];}
+if(metadata['clock-domain']){this.clockDomainId_=metadata['clock-domain'];}}
+return oboe.drop;},processEvent_(event){this.hasEvents_=true;const importOptions=this.model_.importOptions;const trackDetailedModelStats=importOptions.trackDetailedModelStats;const modelStats=this.model_.stats;if(event.args==='__stripped__'){event.argsStripped=true;event.args=undefined;}
+let eventSizeInBytes=undefined;if(trackDetailedModelStats){eventSizeInBytes=JSON.stringify(event).length;}
+switch(event.ph){case'B':case'E':modelStats.willProcessBasicTraceEvent('begin_end (non-compact)',event.cat,event.name,event.ts,eventSizeInBytes);this.processDurationEvent(event);break;case'X':{modelStats.willProcessBasicTraceEvent('begin_end (compact)',event.cat,event.name,event.ts,eventSizeInBytes);const slice=this.processCompleteEvent(event);if(slice!==undefined&&event.bind_id!==undefined){this.processFlowEvent(event,slice);}
+break;}
+case'b':case'e':case'n':case'S':case'F':case'T':case'p':modelStats.willProcessBasicTraceEvent('async',event.cat,event.name,event.ts,eventSizeInBytes);this.processAsyncEvent(event);break;case'I':case'i':case'R':modelStats.willProcessBasicTraceEvent('instant',event.cat,event.name,event.ts,eventSizeInBytes);this.processInstantEvent(event);break;case'P':modelStats.willProcessBasicTraceEvent('samples',event.cat,event.name,event.ts,eventSizeInBytes);this.processTraceSampleEvent(event);break;case'C':modelStats.willProcessBasicTraceEvent('counters',event.cat,event.name,event.ts,eventSizeInBytes);this.processCounterEvent(event);break;case'M':modelStats.willProcessBasicTraceEvent('metadata',event.cat,event.name,event.ts,eventSizeInBytes);this.processMetadataEvent(event);break;case'N':case'D':case'O':modelStats.willProcessBasicTraceEvent('objects',event.cat,event.name,event.ts,eventSizeInBytes);this.processObjectEvent(event);break;case's':case't':case'f':modelStats.willProcessBasicTraceEvent('flows',event.cat,event.name,event.ts,eventSizeInBytes);this.processFlowEvent(event);break;case'v':modelStats.willProcessBasicTraceEvent('memory_dumps',event.cat,event.name,event.ts,eventSizeInBytes);this.processMemoryDumpEvent(event);break;case'(':case')':this.processContextEvent(event);break;case'c':break;default:modelStats.willProcessBasicTraceEvent('unknown',event.cat,event.name,event.ts,eventSizeInBytes);this.model_.importWarning({type:'parse_error',message:'Unrecognized event phase: '+
+event.ph+' ('+event.name+')'});}
+return oboe.drop;},importStackFrames_(rawStackFrames,idPrefix){const model=this.model_;for(const id in rawStackFrames){const rawStackFrame=rawStackFrames[id];const fullId=idPrefix+id;const textForColor=rawStackFrame.category?rawStackFrame.category:rawStackFrame.name;const stackFrame=new tr.model.StackFrame(undefined,fullId,rawStackFrame.name,ColorScheme.getColorIdForGeneralPurposeString(textForColor));model.addStackFrame(stackFrame);}
+for(const id in rawStackFrames){const fullId=idPrefix+id;const stackFrame=model.stackFrames[fullId];if(stackFrame===undefined){throw new Error('Internal error');}
+const rawStackFrame=rawStackFrames[id];const parentId=rawStackFrame.parent;let parentStackFrame;if(parentId===undefined){parentStackFrame=undefined;}else{const parentFullId=idPrefix+parentId;parentStackFrame=model.stackFrames[parentFullId];if(parentStackFrame===undefined){this.model_.importWarning({type:'metadata_parse_error',message:'Missing parent frame with ID '+parentFullId+' for stack frame \''+stackFrame.name+'\' (ID '+fullId+').'});}}
+stackFrame.parentFrame=parentStackFrame;}
+const ProfileNodeType=tr.model.ProfileNode.subTypes.getConstructor(undefined,'legacySample');if(idPrefix==='g'){for(const id in rawStackFrames){const rawStackFrame=rawStackFrames[id];const textForColor=rawStackFrame.category?rawStackFrame.category:rawStackFrame.name;const node=this.stackFrameTree_.add(new ProfileNodeType('g'+id,{functionName:rawStackFrame.name},undefined));node.colorId=ColorScheme.getColorIdForGeneralPurposeString(textForColor);node.parentId=rawStackFrame.parent;}
+for(const id in rawStackFrames){const node=this.stackFrameTree_.getNode('g'+id);const parentId=node.parentId;let parentNode=undefined;if(parentId!==undefined){parentNode=this.stackFrameTree_.getNode('g'+parentId);if(parentNode===undefined){this.model_.importWarning({type:'metadata_parse_error',message:'Missing parent frame with ID '+parentId+' for stack frame \''+node.name+'\' (ID '+node.id+').'});}
+node.parentNode=parentNode;}}}},importObjectTypeNameMap_(rawObjectTypeNameMap,pid){if(pid in this.objectTypeNameMap_){this.model_.importWarning({type:'metadata_parse_error',message:'Mapping from object type IDs to names provided for pid='+
+pid+' multiple times.'});return;}
+let objectTypeNamePrefix=undefined;let objectTypeNameSuffix=undefined;const objectTypeNameMap={};for(const objectTypeId in rawObjectTypeNameMap){const rawObjectTypeName=rawObjectTypeNameMap[objectTypeId];if(objectTypeNamePrefix===undefined){for(let i=0;i<OBJECT_TYPE_NAME_PATTERNS.length;i++){const pattern=OBJECT_TYPE_NAME_PATTERNS[i];if(rawObjectTypeName.startsWith(pattern.prefix)&&rawObjectTypeName.endsWith(pattern.suffix)){objectTypeNamePrefix=pattern.prefix;objectTypeNameSuffix=pattern.suffix;break;}}}
+if(objectTypeNamePrefix!==undefined&&rawObjectTypeName.startsWith(objectTypeNamePrefix)&&rawObjectTypeName.endsWith(objectTypeNameSuffix)){objectTypeNameMap[objectTypeId]=rawObjectTypeName.substring(objectTypeNamePrefix.length,rawObjectTypeName.length-objectTypeNameSuffix.length);}else{objectTypeNameMap[objectTypeId]=rawObjectTypeName;}}
+this.objectTypeNameMap_[pid]=objectTypeNameMap;},importAnnotations_(){for(const id in this.traceAnnotations_){const annotation=tr.model.Annotation.fromDictIfPossible(this.traceAnnotations_[id]);if(!annotation){this.model_.importWarning({type:'annotation_warning',message:'Unrecognized traceAnnotation typeName \"'+
+this.traceAnnotations_[id].typeName+'\"'});continue;}
+this.model_.addAnnotation(annotation);}},finalizeImport(){if(this.softwareMeasuredCpuCount_!==undefined){this.model_.kernel.softwareMeasuredCpuCount=this.softwareMeasuredCpuCount_;}
+this.createFlowSlices_();this.createExplicitObjects_();this.createImplicitObjects_();this.createMemoryDumps_();},getStackFrameForEvent_(event,opt_lookForEndEvent){let sf;let stack;if(opt_lookForEndEvent){sf=event.esf;stack=event.estack;}else{sf=event.sf;stack=event.stack;}
+if(stack!==undefined&&sf!==undefined){this.model_.importWarning({type:'stack_frame_and_stack_error',message:'Event at '+event.ts+' cannot have both a stack and a stackframe.'});return undefined;}
+if(stack!==undefined){return this.model_.resolveStackToStackFrame_(event.pid,stack);}
+if(sf===undefined)return undefined;const stackFrame=this.model_.stackFrames['g'+sf];if(stackFrame===undefined){this.model_.importWarning({type:'sample_import_error',message:'No frame for '+sf});return;}
+return stackFrame;},resolveStackToStackFrame_(pid,stack){return undefined;},importSampleData(){if(!this.sampleEvents_)return;const m=this.model_;const events=this.sampleEvents_;if(this.hasEvents_===undefined){throw new Error('importEvents is not run before importSampleData');}else if(!this.hasEvents_){for(let i=0;i<events.length;i++){const event=events[i];m.getOrCreateProcess(event.tid).getOrCreateThread(event.tid);}}
+const threadsByTid={};m.getAllThreads().forEach(function(t){threadsByTid[t.tid]=t;});for(let i=0;i<events.length;i++){const event=events[i];const thread=threadsByTid[event.tid];if(thread===undefined){m.importWarning({type:'sample_import_error',message:'Thread '+events.tid+'not found'});continue;}
+let cpu;if(event.cpu!==undefined){cpu=m.kernel.getOrCreateCpu(event.cpu);}
+const leafNode=this.stackFrameTree_.getNode('g'+event.sf);const sample=new tr.model.Sample(this.toModelTimeFromUs_(event.ts),event.name,leafNode,thread,cpu,event.weight);m.samples.push(sample);}},createAsyncSlices_(){if(this.allAsyncEvents_.length===0)return;this.allAsyncEvents_.sort(function(x,y){const d=x.event.ts-y.event.ts;if(d!==0)return d;return x.sequenceNumber-y.sequenceNumber;});const legacyEvents=[];const nestableAsyncEventsByKey={};const nestableMeasureAsyncEventsByKey={};for(let i=0;i<this.allAsyncEvents_.length;i++){const asyncEventState=this.allAsyncEvents_[i];const event=asyncEventState.event;if(event.ph==='S'||event.ph==='F'||event.ph==='T'||event.ph==='p'){legacyEvents.push(asyncEventState);continue;}
+if(event.cat===undefined){this.model_.importWarning({type:'async_slice_parse_error',message:'Nestable async events (ph: b, e, or n) require a '+'cat parameter.'});continue;}
+if(event.name===undefined){this.model_.importWarning({type:'async_slice_parse_error',message:'Nestable async events (ph: b, e, or n) require a '+'name parameter.'});continue;}
+const id=TraceEventImporter.scopedIdForEvent_(event);if(id===undefined){this.model_.importWarning({type:'async_slice_parse_error',message:'Nestable async events (ph: b, e, or n) require an '+'id parameter.'});continue;}
+if(event.cat==='blink.user_timing'){const matched=MEASURE_NAME_REGEX.exec(event.name);if(matched!==null){const key=matched[1]+':'+event.cat;try{event.args=JSON.parse(Base64.atob(matched[3])||'{}');}catch(e){}
+if(nestableMeasureAsyncEventsByKey[key]===undefined){nestableMeasureAsyncEventsByKey[key]=[];}
+nestableMeasureAsyncEventsByKey[key].push(asyncEventState);continue;}}
+const key=event.cat+':'+id.toStringWithDelimiter(':');if(nestableAsyncEventsByKey[key]===undefined){nestableAsyncEventsByKey[key]=[];}
+nestableAsyncEventsByKey[key].push(asyncEventState);}
+this.createLegacyAsyncSlices_(legacyEvents);this.createNestableAsyncSlices_(nestableMeasureAsyncEventsByKey);this.createNestableAsyncSlices_(nestableAsyncEventsByKey);},createLegacyAsyncSlice_(events){const asyncEventState=events[events.length-1];const event=asyncEventState.event;const name=event.name;const id=TraceEventImporter.scopedIdForEvent_(event);const key=id.toStringWithDelimiter(':');const asyncSliceConstructor=tr.model.AsyncSlice.subTypes.getConstructor(events[0].event.cat,name);let duration;if(event.ts!==undefined){duration=this.toModelTimeFromUs_(event.ts-events[0].event.ts);}
+const slice=new asyncSliceConstructor(events[0].event.cat,name,getEventColor(events[0].event),this.toModelTimeFromUs_(events[0].event.ts),Object.assign({},events[0].event.args,event.args),duration||0,true,undefined,undefined,events[0].event.argsStripped);if(duration===undefined){slice.didNotFinish=true;slice.error='Slice has no matching END. End time has been adjusted.';this.model_.importWarning({type:'async_slice_parse_error',message:'Legacy async BEGIN event at '+
+events[0].event.ts+' with name="'+
+name+'" and id='+key+' was unmatched.'});}
+slice.startThread=events[0].thread;slice.endThread=asyncEventState.thread;slice.id=key;const stepType=events[1].event.ph;let isValid=true;for(let j=1;j<events.length-1;++j){if(events[j].event.ph==='T'||events[j].event.ph==='p'){isValid=this.assertStepTypeMatches_(stepType,events[j]);if(!isValid)break;}
+if(events[j].event.ph==='S'){this.model_.importWarning({type:'async_slice_parse_error',message:'At '+events[j].event.ts+', a slice named "'+
+name+'" with id='+id+' had a step before the start event.'});continue;}
+if(events[j].event.ph==='F'){this.model_.importWarning({type:'async_slice_parse_error',message:'At '+events[j].event.ts+', a slice named '+
+name+' with id='+id+' had a step after the finish event.'});continue;}
+const startIndex=j+(stepType==='T'?0:-1);const endIndex=startIndex+1;let subName=name;if(!events[j].event.argsStripped&&(events[j].event.ph==='T'||events[j].event.ph==='p')){subName=events[j].event.args.step;}
+const asyncSliceConstructor=tr.model.AsyncSlice.subTypes.getConstructor(events[0].event.cat,subName);let duration;if(events[endIndex].event.ts!==undefined){duration=this.toModelTimeFromUs_(events[endIndex].event.ts-events[startIndex].event.ts);}
+const subSlice=new asyncSliceConstructor(events[0].event.cat,subName,getEventColor(events[0].event,subName+j),this.toModelTimeFromUs_(events[startIndex].event.ts),this.deepCopyIfNeeded_(events[j].event.args),duration||0,undefined,undefined,events[startIndex].event.argsStripped);if(duration===undefined){subSlice.didNotFinish=true;subSlice.error='Slice has no matching END. End time has been adjusted.';}
+subSlice.startThread=events[startIndex].thread;subSlice.endThread=events[endIndex].thread;subSlice.id=key;slice.subSlices.push(subSlice);}
+if(isValid){slice.startThread.asyncSliceGroup.push(slice);}},createLegacyAsyncSlices_(legacyEvents){if(legacyEvents.length===0)return;legacyEvents.sort(function(x,y){const d=x.event.ts-y.event.ts;if(d!==0)return d;return x.sequenceNumber-y.sequenceNumber;});const asyncEventStatesByNameThenID={};for(let i=0;i<legacyEvents.length;i++){const asyncEventState=legacyEvents[i];const event=asyncEventState.event;const name=event.name;if(name===undefined){this.model_.importWarning({type:'async_slice_parse_error',message:'Async events (ph: S, T, p, or F) require a name '+' parameter.'});continue;}
+const id=TraceEventImporter.scopedIdForEvent_(event);if(id===undefined){this.model_.importWarning({type:'async_slice_parse_error',message:'Async events (ph: S, T, p, or F) require an id parameter.'});continue;}
+const key=id.toStringWithDelimiter(':');if(event.ph==='S'){if(asyncEventStatesByNameThenID[name]===undefined){asyncEventStatesByNameThenID[name]={};}
+if(asyncEventStatesByNameThenID[name][key]){this.model_.importWarning({type:'async_slice_parse_error',message:'At '+event.ts+', a slice of the same id '+id+' was alrady open.'});continue;}
+asyncEventStatesByNameThenID[name][key]=[];asyncEventStatesByNameThenID[name][key].push(asyncEventState);}else{if(asyncEventStatesByNameThenID[name]===undefined){this.model_.importWarning({type:'async_slice_parse_error',message:`At ${event.ts}, no slice named "${name}" was open.`,});continue;}
+if(asyncEventStatesByNameThenID[name][key]===undefined){this.model_.importWarning({type:'async_slice_parse_error',message:`At ${event.ts}, no slice named "${name}" with id=${id} was `+'open.',});continue;}
+const events=asyncEventStatesByNameThenID[name][key];events.push(asyncEventState);if(event.ph==='F'){this.createLegacyAsyncSlice_(events);delete asyncEventStatesByNameThenID[name][key];}}}
+for(const[name,statesByID]of
+Object.entries(asyncEventStatesByNameThenID)){for(const[id,states]of Object.entries(statesByID)){const startEvent=states[0].event;states.push({sequenceNumber:1+states[states.length-1].sequenceNumber,event:{ph:'F',name,id:startEvent.id,id2:startEvent.id2,scope:startEvent.scope,pid:startEvent.pid,tid:startEvent.tid,cat:startEvent.cat,args:{},},thread:this.model_.getOrCreateProcess(startEvent.pid).getOrCreateThread(startEvent.tid),});this.createLegacyAsyncSlice_(states);}}},createNestableAsyncSlices_(nestableEventsByKey){for(const key in nestableEventsByKey){const eventStateEntries=nestableEventsByKey[key];const parentStack=[];for(let i=0;i<eventStateEntries.length;++i){const eventStateEntry=eventStateEntries[i];if(eventStateEntry.event.ph==='e'){let parentIndex=-1;for(let k=parentStack.length-1;k>=0;--k){if(parentStack[k].event.name===eventStateEntry.event.name){parentIndex=k;break;}}
+if(parentIndex===-1){eventStateEntry.finished=false;}else{parentStack[parentIndex].end=eventStateEntry;while(parentIndex<parentStack.length){parentStack.pop();}}}
+if(parentStack.length>0){eventStateEntry.parentEntry=parentStack[parentStack.length-1];}
+if(eventStateEntry.event.ph==='b'){parentStack.push(eventStateEntry);}}
+const topLevelSlices=[];for(let i=0;i<eventStateEntries.length;++i){const eventStateEntry=eventStateEntries[i];if(eventStateEntry.event.ph==='e'&&eventStateEntry.finished===undefined){continue;}
+let startState=undefined;let endState=undefined;let sliceArgs=eventStateEntry.event.args||{};let sliceError=undefined;const id=TraceEventImporter.scopedIdForEvent_(eventStateEntry.event);if(eventStateEntry.event.ph==='n'){startState=eventStateEntry;endState=eventStateEntry;}else if(eventStateEntry.event.ph==='b'){if(eventStateEntry.end===undefined){eventStateEntry.end=eventStateEntries[eventStateEntries.length-1];sliceError='Slice has no matching END. End time has been adjusted.';this.model_.importWarning({type:'async_slice_parse_error',message:'Nestable async BEGIN event at '+
+eventStateEntry.event.ts+' with name="'+
+eventStateEntry.event.name+'" and id='+id+' was unmatched.'});}else{function concatenateArguments(args1,args2){if(args1.params===undefined||args2.params===undefined){return Object.assign({},args1,args2);}
+const args3={};args3.params=Object.assign({},args1.params,args2.params);return Object.assign({},args1,args2,args3);}
+const endArgs=eventStateEntry.end.event.args||{};sliceArgs=concatenateArguments(sliceArgs,endArgs);}
+startState=eventStateEntry;endState=eventStateEntry.end;}else{sliceError='Slice has no matching BEGIN. Start time has been adjusted.';this.model_.importWarning({type:'async_slice_parse_error',message:'Nestable async END event at '+
+eventStateEntry.event.ts+' with name='+
+eventStateEntry.event.name+' and id='+id+' was unmatched.'});startState=eventStateEntries[0];endState=eventStateEntry;}
+const isTopLevel=(eventStateEntry.parentEntry===undefined);const asyncSliceConstructor=tr.model.AsyncSlice.subTypes.getConstructor(eventStateEntry.event.cat,eventStateEntry.event.name);let threadStart=undefined;let threadDuration=undefined;if(startState.event.tts&&startState.event.use_async_tts){threadStart=this.toModelTimeFromUs_(startState.event.tts);if(endState.event.tts){const threadEnd=this.toModelTimeFromUs_(endState.event.tts);threadDuration=threadEnd-threadStart;}}
+const slice=new asyncSliceConstructor(eventStateEntry.event.cat,eventStateEntry.event.name,getEventColor(endState.event),this.toModelTimeFromUs_(startState.event.ts),sliceArgs,this.toModelTimeFromUs_(endState.event.ts-startState.event.ts),isTopLevel,threadStart,threadDuration,startState.event.argsStripped);slice.startThread=startState.thread;slice.endThread=endState.thread;slice.startStackFrame=this.getStackFrameForEvent_(startState.event);slice.endStackFrame=this.getStackFrameForEvent_(endState.event);slice.id=key;if(sliceError!==undefined){slice.error=sliceError;}
+eventStateEntry.slice=slice;if(isTopLevel){topLevelSlices.push(slice);}else if(eventStateEntry.parentEntry.slice!==undefined){eventStateEntry.parentEntry.slice.subSlices.push(slice);}}
+for(let si=0;si<topLevelSlices.length;si++){topLevelSlices[si].startThread.asyncSliceGroup.push(topLevelSlices[si]);}}},assertStepTypeMatches_(stepType,event){if(stepType!==event.event.ph){this.model_.importWarning({type:'async_slice_parse_error',message:'At '+event.event.ts+', a slice named '+
+event.event.name+' with id='+
+TraceEventImporter.scopedIdForEvent_(event.event)+' had both begin and end steps, which is not allowed.'});return false;}
+return true;},validateFlowEvent_(event){if(event.name===undefined){this.model_.importWarning({type:'flow_slice_parse_error',message:'Flow events (ph: s, t or f) require a name parameter.'});return false;}
+if(event.ph==='s'||event.ph==='f'||event.ph==='t'){if(event.id===undefined){this.model_.importWarning({type:'flow_slice_parse_error',message:'Flow events (ph: s, t or f) require an id parameter.'});return false;}
+return true;}
+if(event.bind_id){if(event.flow_in===undefined&&event.flow_out===undefined){this.model_.importWarning({type:'flow_slice_parse_error',message:'Flow producer or consumer require flow_in or flow_out.'});return false;}
+return true;}
+return false;},createFlowSlices_(){if(this.allFlowEvents_.length===0)return;const createFlowEvent=function(thread,event,opt_slice){let startSlice;let flowId;let flowStartTs;if(event.bind_id){startSlice=opt_slice;flowId=event.bind_id;flowStartTs=this.toModelTimeFromUs_(event.ts+event.dur);}else{const ts=this.toModelTimeFromUs_(event.ts);startSlice=thread.sliceGroup.findSliceAtTs(ts);if(startSlice===undefined)return undefined;flowId=event.id;flowStartTs=ts;}
+const flowEvent=new tr.model.FlowEvent(event.cat,flowId,event.name,getEventColor(event),flowStartTs,this.deepCopyAlways_(event.args));flowEvent.startSlice=startSlice;flowEvent.startStackFrame=this.getStackFrameForEvent_(event);flowEvent.endStackFrame=undefined;startSlice.outFlowEvents.push(flowEvent);return flowEvent;}.bind(this);const finishFlowEventWith=function(flowEvent,thread,event,refGuid,bindToParent,opt_slice){let endSlice;if(event.bind_id){endSlice=opt_slice;}else{const ts=this.toModelTimeFromUs_(event.ts);if(bindToParent){endSlice=thread.sliceGroup.findSliceAtTs(ts);}else{endSlice=thread.sliceGroup.findNextSliceAfter(ts,refGuid);}
+if(endSlice===undefined)return false;}
+endSlice.inFlowEvents.push(flowEvent);flowEvent.endSlice=endSlice;flowEvent.duration=this.toModelTimeFromUs_(event.ts)-flowEvent.start;flowEvent.endStackFrame=this.getStackFrameForEvent_(event);this.mergeArgsInto_(flowEvent.args,event.args,flowEvent.title);return true;}.bind(this);const processFlowConsumer=function(flowIdToEvent,sliceGuidToEvent,event,slice){let flowEvent=flowIdToEvent[event.bind_id];if(flowEvent===undefined){this.model_.importWarning({type:'flow_slice_ordering_error',message:'Flow consumer '+event.bind_id+' does not have '+'a flow producer'});return false;}else if(flowEvent.endSlice){const flowProducer=flowEvent.startSlice;flowEvent=createFlowEvent(undefined,sliceGuidToEvent[flowProducer.guid],flowProducer);}
+const refGuid=undefined;const ok=finishFlowEventWith(flowEvent,undefined,event,refGuid,undefined,slice);if(ok){this.model_.flowEvents.push(flowEvent);}else{this.model_.importWarning({type:'flow_slice_end_error',message:'Flow consumer '+event.bind_id+' does not end '+'at an actual slice, so cannot be created.'});return false;}
+return true;}.bind(this);const processFlowProducer=function(flowIdToEvent,flowStatus,event,slice){if(flowIdToEvent[event.bind_id]&&flowStatus[event.bind_id]){this.model_.importWarning({type:'flow_slice_start_error',message:'Flow producer '+event.bind_id+' already seen'});return false;}
+const flowEvent=createFlowEvent(undefined,event,slice);if(!flowEvent){this.model_.importWarning({type:'flow_slice_start_error',message:'Flow producer '+event.bind_id+' does not start'+'a flow'});return false;}
+flowIdToEvent[event.bind_id]=flowEvent;}.bind(this);this.allFlowEvents_.sort(function(x,y){const d=x.event.ts-y.event.ts;if(d!==0)return d;return x.sequenceNumber-y.sequenceNumber;});const flowIdToEvent={};const sliceGuidToEvent={};const flowStatus={};for(let i=0;i<this.allFlowEvents_.length;++i){const data=this.allFlowEvents_[i];const refGuid=data.refGuid;const event=data.event;const thread=data.thread;if(!this.validateFlowEvent_(event))continue;if(event.bind_id){const slice=data.slice;sliceGuidToEvent[slice.guid]=event;if(event.flowPhase===PRODUCER){if(!processFlowProducer(flowIdToEvent,flowStatus,event,slice)){continue;}
+flowStatus[event.bind_id]=true;}else{if(!processFlowConsumer(flowIdToEvent,sliceGuidToEvent,event,slice)){continue;}
+flowStatus[event.bind_id]=false;if(event.flowPhase===STEP){if(!processFlowProducer(flowIdToEvent,flowStatus,event,slice)){continue;}
+flowStatus[event.bind_id]=true;}}
+continue;}
+const fullFlowId=JSON.stringify({id:event.id,cat:event.cat,name:event.name});let flowEvent;if(event.ph==='s'){if(flowIdToEvent[fullFlowId]){this.model_.importWarning({type:'flow_slice_start_error',message:'event id '+event.id+' already seen when '+'encountering start of flow event.'});continue;}
+flowEvent=createFlowEvent(thread,event);if(!flowEvent){this.model_.importWarning({type:'flow_slice_start_error',message:'event id '+event.id+' does not start '+'at an actual slice, so cannot be created.'});continue;}
+flowIdToEvent[fullFlowId]=flowEvent;}else if(event.ph==='t'||event.ph==='f'){flowEvent=flowIdToEvent[fullFlowId];if(flowEvent===undefined){this.model_.importWarning({type:'flow_slice_ordering_error',message:'Found flow phase '+event.ph+' for id: '+event.id+' but no flow start found.'});continue;}
+let bindToParent=event.ph==='t';if(event.ph==='f'){if(event.bp===undefined){if(event.cat.indexOf('input')>-1){bindToParent=true;}else if(event.cat.indexOf('ipc.flow')>-1){bindToParent=true;}}else{if(event.bp!=='e'){this.model_.importWarning({type:'flow_slice_bind_point_error',message:'Flow event with invalid binding point (event.bp).'});continue;}
+bindToParent=true;}}
+const ok=finishFlowEventWith(flowEvent,thread,event,refGuid,bindToParent);if(ok){this.model_.flowEvents.push(flowEvent);}else{this.model_.importWarning({type:'flow_slice_end_error',message:'event id '+event.id+' does not end '+'at an actual slice, so cannot be created.'});}
+flowIdToEvent[fullFlowId]=undefined;if(ok&&event.ph==='t'){flowEvent=createFlowEvent(thread,event);flowIdToEvent[fullFlowId]=flowEvent;}}}},createExplicitObjects_(){if(this.allObjectEvents_.length===0)return;const processEvent=function(objectEventState){const event=objectEventState.event;const scopedId=TraceEventImporter.scopedIdForEvent_(event);const thread=objectEventState.thread;if(event.name===undefined){this.model_.importWarning({type:'object_parse_error',message:'While processing '+JSON.stringify(event)+': '+'Object events require an name parameter.'});}
+if(scopedId===undefined||scopedId.id===undefined){this.model_.importWarning({type:'object_parse_error',message:'While processing '+JSON.stringify(event)+': '+'Object events require an id parameter.'});}
+const process=thread.parent;const ts=this.toModelTimeFromUs_(event.ts);let instance;if(event.ph==='N'){try{instance=process.objects.idWasCreated(scopedId,event.cat,event.name,ts);}catch(e){this.model_.importWarning({type:'object_parse_error',message:'While processing create of '+
+scopedId+' at ts='+ts+': '+e});return;}}else if(event.ph==='O'){if(event.args.snapshot===undefined){this.model_.importWarning({type:'object_parse_error',message:'While processing '+scopedId+' at ts='+ts+': '+'Snapshots must have args: {snapshot: ...}'});return;}
+let snapshot;try{const args=this.deepCopyIfNeeded_(event.args.snapshot);let cat;if(args.cat){cat=args.cat;delete args.cat;}else{cat=event.cat;}
+let baseTypename;if(args.base_type){baseTypename=args.base_type;delete args.base_type;}else{baseTypename=undefined;}
+snapshot=process.objects.addSnapshot(scopedId,cat,event.name,ts,args,baseTypename);snapshot.snapshottedOnThread=thread;}catch(e){this.model_.importWarning({type:'object_parse_error',message:'While processing snapshot of '+
+scopedId+' at ts='+ts+': '+e});return;}
+instance=snapshot.objectInstance;}else if(event.ph==='D'){try{process.objects.idWasDeleted(scopedId,event.cat,event.name,ts);const instanceMap=process.objects.getOrCreateInstanceMap_(scopedId);instance=instanceMap.lastInstance;}catch(e){this.model_.importWarning({type:'object_parse_error',message:'While processing delete of '+
+scopedId+' at ts='+ts+': '+e});return;}}
+if(instance){instance.colorId=getEventColor(event,instance.typeName);}}.bind(this);this.allObjectEvents_.sort(function(x,y){const d=x.event.ts-y.event.ts;if(d!==0)return d;return x.sequenceNumber-y.sequenceNumber;});const allObjectEvents=this.allObjectEvents_;for(let i=0;i<allObjectEvents.length;i++){const objectEventState=allObjectEvents[i];try{processEvent.call(this,objectEventState);}catch(e){this.model_.importWarning({type:'object_parse_error',message:e.message});}}},createImplicitObjects_(){for(const proc of Object.values(this.model_.processes)){this.createImplicitObjectsForProcess_(proc);}},createImplicitObjectsForProcess_(process){function processField(referencingObject,referencingObjectFieldName,referencingObjectFieldValue,containingSnapshot){if(!referencingObjectFieldValue)return;if(referencingObjectFieldValue instanceof
+tr.model.ObjectSnapshot){return null;}
+if(referencingObjectFieldValue.id===undefined)return;const implicitSnapshot=referencingObjectFieldValue;const rawId=implicitSnapshot.id;const m=/(.+)\/(.+)/.exec(rawId);if(!m){throw new Error('Implicit snapshots must have names.');}
+delete implicitSnapshot.id;const name=m[1];const id=m[2];let res;let cat;if(implicitSnapshot.cat!==undefined){cat=implicitSnapshot.cat;}else{cat=containingSnapshot.objectInstance.category;}
+let baseTypename;if(implicitSnapshot.base_type){baseTypename=implicitSnapshot.base_type;}else{baseTypename=undefined;}
+const scope=containingSnapshot.objectInstance.scopedId.scope;try{res=process.objects.addSnapshot(new tr.model.ScopedId(scope,id),cat,name,containingSnapshot.ts,implicitSnapshot,baseTypename);}catch(e){this.model_.importWarning({type:'object_snapshot_parse_error',message:'While processing implicit snapshot of '+
+rawId+' at ts='+containingSnapshot.ts+': '+e});return;}
+res.objectInstance.hasImplicitSnapshots=true;res.containingSnapshot=containingSnapshot;res.snapshottedOnThread=containingSnapshot.snapshottedOnThread;referencingObject[referencingObjectFieldName]=res;if(!(res instanceof tr.model.ObjectSnapshot)){throw new Error('Created object must be instanceof snapshot');}
+return res.args;}
+function iterObject(object,func,containingSnapshot,thisArg){if(!(object instanceof Object))return;if(object instanceof Array){for(let i=0;i<object.length;i++){const res=func.call(thisArg,object,i,object[i],containingSnapshot);if(res===null)continue;if(res){iterObject(res,func,containingSnapshot,thisArg);}else{iterObject(object[i],func,containingSnapshot,thisArg);}}
+return;}
+for(const key in object){const res=func.call(thisArg,object,key,object[key],containingSnapshot);if(res===null)continue;if(res){iterObject(res,func,containingSnapshot,thisArg);}else{iterObject(object[key],func,containingSnapshot,thisArg);}}}
+process.objects.iterObjectInstances(function(instance){instance.snapshots.forEach(function(snapshot){if(snapshot.args.id!==undefined){throw new Error('args cannot have an id field inside it');}
+iterObject(snapshot.args,processField,snapshot,this);},this);},this);},minimalTimestampInPidToEvents_(pidToEvents){let smallestTs=Infinity;for(const events of Object.values(pidToEvents)){for(const event of events){if(event.ts<smallestTs){smallestTs=event.ts;}}}
+return smallestTs;},createMemoryDumps_(){const pairs=Object.entries(this.allMemoryDumpEvents_);const key=x=>this.minimalTimestampInPidToEvents_(x);pairs.sort((x,y)=>key(x[1])-key(y[1]));for(const[dumpId,pidToEvents]of pairs){this.createGlobalMemoryDump_(pidToEvents,dumpId);}},createGlobalMemoryDump_(dumpIdEvents,dumpId){const globalRange=new tr.b.math.Range();for(const pid in dumpIdEvents){const processEvents=dumpIdEvents[pid];for(let i=0;i<processEvents.length;i++){globalRange.addValue(this.toModelTimeFromUs_(processEvents[i].ts));}}
+if(globalRange.isEmpty){throw new Error('Internal error: Global memory dump without events');}
+const globalMemoryDump=new tr.model.GlobalMemoryDump(this.model_,globalRange.min);globalMemoryDump.duration=globalRange.range;this.model_.globalMemoryDumps.push(globalMemoryDump);const globalMemoryAllocatorDumpsByFullName={};const levelsOfDetail={};const allMemoryAllocatorDumpsByGuid={};for(const pid in dumpIdEvents){this.createProcessMemoryDump_(globalMemoryDump,globalMemoryAllocatorDumpsByFullName,levelsOfDetail,allMemoryAllocatorDumpsByGuid,dumpIdEvents[pid],pid,dumpId);}
+globalMemoryDump.levelOfDetail=levelsOfDetail.global;globalMemoryDump.memoryAllocatorDumps=this.inferMemoryAllocatorDumpTree_(globalMemoryAllocatorDumpsByFullName);this.parseMemoryDumpAllocatorEdges_(allMemoryAllocatorDumpsByGuid,dumpIdEvents,dumpId);},createProcessMemoryDump_(globalMemoryDump,globalMemoryAllocatorDumpsByFullName,levelsOfDetail,allMemoryAllocatorDumpsByGuid,processEvents,pid,dumpId){const processRange=new tr.b.math.Range();for(let i=0;i<processEvents.length;i++){processRange.addValue(this.toModelTimeFromUs_(processEvents[i].ts));}
+if(processRange.isEmpty){throw new Error('Internal error: Process memory dump without events');}
+const process=this.model_.getOrCreateProcess(pid);const processMemoryDump=new tr.model.ProcessMemoryDump(globalMemoryDump,process,processRange.min);processMemoryDump.duration=processRange.range;process.memoryDumps.push(processMemoryDump);globalMemoryDump.processMemoryDumps[pid]=processMemoryDump;const processMemoryAllocatorDumpsByFullName={};for(let i=0;i<processEvents.length;i++){const processEvent=processEvents[i];const dumps=processEvent.args.dumps;if(dumps===undefined){this.model_.importWarning({type:'memory_dump_parse_error',message:'\'dumps\' field not found in a process memory dump'+' event for PID='+pid+' and dump ID='+dumpId+'.'});continue;}
+this.parseMemoryDumpTotals_(processMemoryDump,dumps,pid,dumpId);this.parseMemoryDumpVmRegions_(processMemoryDump,dumps,pid,dumpId);this.parseMemoryDumpHeapDumps_(processMemoryDump,dumps,pid,dumpId);this.parseMemoryDumpLevelOfDetail_(levelsOfDetail,dumps,pid,dumpId);this.parseMemoryDumpAllocatorDumps_(processMemoryDump,globalMemoryDump,processMemoryAllocatorDumpsByFullName,globalMemoryAllocatorDumpsByFullName,allMemoryAllocatorDumpsByGuid,dumps,pid,dumpId);}
+if(levelsOfDetail.process===undefined){levelsOfDetail.process=processMemoryDump.vmRegions?DETAILED:LIGHT;}
+if(!this.updateMemoryDumpLevelOfDetail_(levelsOfDetail,'global',levelsOfDetail.process)){this.model_.importWarning({type:'memory_dump_parse_error',message:'diffent levels of detail provided for global memory'+' dump (dump ID='+dumpId+').'});}
+processMemoryDump.levelOfDetail=levelsOfDetail.process;delete levelsOfDetail.process;processMemoryDump.memoryAllocatorDumps=this.inferMemoryAllocatorDumpTree_(processMemoryAllocatorDumpsByFullName);},parseMemoryDumpTotals_(processMemoryDump,dumps,pid,dumpId){const rawTotals=dumps.process_totals;if(rawTotals===undefined)return;if(processMemoryDump.totals!==undefined){this.model_.importWarning({type:'memory_dump_parse_error',message:'Process totals provided multiple times for'+' process memory dump for PID='+pid+' and dump ID='+dumpId+'.'});return;}
+const totals={};let platformSpecificTotals=undefined;for(const rawTotalName in rawTotals){const rawTotalValue=rawTotals[rawTotalName];if(rawTotalValue===undefined)continue;if(rawTotalName==='resident_set_bytes'){totals.residentBytes=parseInt(rawTotalValue,16);continue;}
+if(rawTotalName==='peak_resident_set_bytes'){totals.peakResidentBytes=parseInt(rawTotalValue,16);continue;}
+if(rawTotalName==='is_peak_rss_resetable'){totals.arePeakResidentBytesResettable=!!rawTotalValue;continue;}
+if(rawTotalName==='private_footprint_bytes'){totals.privateFootprintBytes=parseInt(rawTotalValue,16);continue;}
+if(platformSpecificTotals===undefined){platformSpecificTotals={};totals.platformSpecific=platformSpecificTotals;}
+platformSpecificTotals[rawTotalName]=parseInt(rawTotalValue,16);}
+if(totals.peakResidentBytes===undefined&&totals.arePeakResidentBytesResettable!==undefined){this.model_.importWarning({type:'memory_dump_parse_error',message:'Optional field peak_resident_set_bytes found'+' but is_peak_rss_resetable not found in'+' process memory dump for PID='+pid+' and dump ID='+dumpId+'.'});}
+if(totals.arePeakResidentBytesResettable!==undefined&&totals.peakResidentBytes===undefined){this.model_.importWarning({type:'memory_dump_parse_error',message:'Optional field is_peak_rss_resetable found'+' but peak_resident_set_bytes not found in'+' process memory dump for PID='+pid+' and dump ID='+dumpId+'.'});}
+processMemoryDump.totals=totals;},parseMemoryDumpVmRegions_(processMemoryDump,dumps,pid,dumpId){const rawProcessMmaps=dumps.process_mmaps;if(rawProcessMmaps===undefined)return;const rawVmRegions=rawProcessMmaps.vm_regions;if(rawVmRegions===undefined)return;if(processMemoryDump.vmRegions!==undefined){this.model_.importWarning({type:'memory_dump_parse_error',message:'VM regions provided multiple times for'+' process memory dump for PID='+pid+' and dump ID='+dumpId+'.'});return;}
+const vmRegions=new Array(rawVmRegions.length);for(let i=0;i<rawVmRegions.length;i++){const rawVmRegion=rawVmRegions[i];const byteStats={};const rawByteStats=rawVmRegion.bs;for(const rawByteStatName in rawByteStats){const rawByteStatValue=rawByteStats[rawByteStatName];if(rawByteStatValue===undefined){this.model_.importWarning({type:'memory_dump_parse_error',message:'Byte stat \''+rawByteStatName+'\' of VM region '+
+i+' ('+rawVmRegion.mf+') in process memory dump for '+'PID='+pid+' and dump ID='+dumpId+' does not have a value.'});continue;}
+const byteStatName=BYTE_STAT_NAME_MAP[rawByteStatName];if(byteStatName===undefined){this.model_.importWarning({type:'memory_dump_parse_error',message:'Unknown byte stat name \''+rawByteStatName+'\' ('+
+rawByteStatValue+') of VM region '+i+' ('+
+rawVmRegion.mf+') in process memory dump for PID='+pid+' and dump ID='+dumpId+'.'});continue;}
+byteStats[byteStatName]=parseInt(rawByteStatValue,16);if(byteStatName==='proportionalResident'&&byteStats[byteStatName]===0){byteStats[byteStatName]=undefined;}}
+vmRegions[i]=new tr.model.VMRegion(parseInt(rawVmRegion.sa,16),parseInt(rawVmRegion.sz,16),rawVmRegion.pf,rawVmRegion.mf,byteStats);}
+processMemoryDump.vmRegions=tr.model.VMRegionClassificationNode.fromRegions(vmRegions);},parseMemoryDumpHeapDumps_(processMemoryDump,dumps,pid,dumpId){const idPrefix='p'+pid+':';let importer;if(dumps.heaps){const processTypeMap=this.objectTypeNameMap_[pid];if(processTypeMap===undefined){this.model_.importWarning({type:'memory_dump_parse_error',message:'Missing mapping from object type IDs to names.'});}
+importer=new LegacyHeapDumpTraceEventImporter(this.model_,processMemoryDump,processTypeMap,idPrefix,dumpId,dumps.heaps);}else if(dumps.heaps_v2){const data=dumps.heaps_v2;this.heapProfileExpander=this.heapProfileExpander.expandData(data);this.addNewStackFramesFromExpander_(this.heapProfileExpander,idPrefix);importer=new HeapDumpTraceEventImporter(this.heapProfileExpander,this.model_.stackFrames,processMemoryDump,idPrefix,this.model_);}
+if(!importer)return;const heapDumps=importer.parse();if(!heapDumps)return;if(processMemoryDump.heapDumps!==undefined){this.model_.importWarning({type:'memory_dump_parse_error',message:'Heap dumps provided multiple times for'+' process memory dump for PID='+pid+' and dump ID='+dumpId+'.'});return;}
+if(Object.keys(heapDumps).length>0){processMemoryDump.heapDumps=heapDumps;}},addNewStackFramesFromExpander_(expander,idPrefix){const nodeMap=expander.getNewMap('nodes');const newStackFrames={};for(const[id,stackFrame]of nodeMap.entries()){if(!this.model_.stackFrames[idPrefix+id]){newStackFrames[id]={id,name:expander.getString(stackFrame.name_sid),};if(stackFrame.parent)newStackFrames[id].parent=stackFrame.parent;}}
+this.importStackFrames_(newStackFrames,idPrefix);},parseMemoryDumpLevelOfDetail_(levelsOfDetail,dumps,pid,dumpId){const rawLevelOfDetail=dumps.level_of_detail;let level;switch(rawLevelOfDetail){case'background':level=BACKGROUND;break;case'light':level=LIGHT;break;case'detailed':level=DETAILED;break;case undefined:level=undefined;break;default:this.model_.importWarning({type:'memory_dump_parse_error',message:'unknown raw level of detail \''+rawLevelOfDetail+'\' of process memory dump for PID='+pid+' and dump ID='+dumpId+'.'});return;}
+if(!this.updateMemoryDumpLevelOfDetail_(levelsOfDetail,'process',level)){this.model_.importWarning({type:'memory_dump_parse_error',message:'diffent levels of detail provided for process memory'+' dump for PID='+pid+' (dump ID='+dumpId+').'});}},updateMemoryDumpLevelOfDetail_(levelsOfDetail,scope,level){if(!(scope in levelsOfDetail)||level===levelsOfDetail[scope]){levelsOfDetail[scope]=level;return true;}
+if(MEMORY_DUMP_LEVEL_OF_DETAIL_ORDER.indexOf(level)>MEMORY_DUMP_LEVEL_OF_DETAIL_ORDER.indexOf(levelsOfDetail[scope])){levelsOfDetail[scope]=level;}
+return false;},parseMemoryDumpAllocatorDumps_(processMemoryDump,globalMemoryDump,processMemoryAllocatorDumpsByFullName,globalMemoryAllocatorDumpsByFullName,allMemoryAllocatorDumpsByGuid,dumps,pid,dumpId){const rawAllocatorDumps=dumps.allocators;if(rawAllocatorDumps===undefined)return;for(let fullName in rawAllocatorDumps){const rawAllocatorDump=rawAllocatorDumps[fullName];const guid=rawAllocatorDump.guid;if(guid===undefined){this.model_.importWarning({type:'memory_dump_parse_error',message:'Memory allocator dump '+fullName+' for PID='+pid+' and dump ID='+dumpId+' does not have a GUID.'});}
+const flags=rawAllocatorDump.flags||0;const isWeakDump=!!(flags&WEAK_MEMORY_ALLOCATOR_DUMP_FLAG);let containerMemoryDump;let dstIndex;if(fullName.startsWith(GLOBAL_MEMORY_ALLOCATOR_DUMP_PREFIX)){fullName=fullName.substring(GLOBAL_MEMORY_ALLOCATOR_DUMP_PREFIX.length);containerMemoryDump=globalMemoryDump;dstIndex=globalMemoryAllocatorDumpsByFullName;}else{containerMemoryDump=processMemoryDump;dstIndex=processMemoryAllocatorDumpsByFullName;}
+let allocatorDump=allMemoryAllocatorDumpsByGuid[guid];if(allocatorDump===undefined){if(fullName in dstIndex){this.model_.importWarning({type:'memory_dump_parse_error',message:'Multiple GUIDs provided for'+' memory allocator dump '+fullName+': '+
+dstIndex[fullName].guid+', '+guid+' (ignored) for'+' PID='+pid+' and dump ID='+dumpId+'.'});continue;}
+allocatorDump=new tr.model.MemoryAllocatorDump(containerMemoryDump,fullName,guid);allocatorDump.weak=isWeakDump;dstIndex[fullName]=allocatorDump;if(guid!==undefined){allMemoryAllocatorDumpsByGuid[guid]=allocatorDump;}}else{if(allocatorDump.containerMemoryDump!==containerMemoryDump){this.model_.importWarning({type:'memory_dump_parse_error',message:'Memory allocator dump '+fullName+' (GUID='+guid+') for PID='+pid+' and dump ID='+
+dumpId+' dumped in different contexts.'});continue;}
+if(allocatorDump.fullName!==fullName){this.model_.importWarning({type:'memory_dump_parse_error',message:'Memory allocator dump with GUID='+guid+' for PID='+
+pid+' and dump ID='+dumpId+' has multiple names: '+
+allocatorDump.fullName+', '+fullName+' (ignored).'});continue;}
+if(!isWeakDump){allocatorDump.weak=false;}}
+let attributes=rawAllocatorDump.attrs;if(attributes===undefined){this.model_.importWarning({type:'memory_dump_parse_error',message:'Memory allocator dump '+fullName+' (GUID='+guid+') for PID='+pid+' and dump ID='+dumpId+' does not have attributes.'});attributes={};}
+for(const attrName in attributes){const attrArgs=attributes[attrName];const attrType=attrArgs.type;const attrValue=attrArgs.value;switch(attrType){case'scalar':{if(attrName in allocatorDump.numerics){this.model_.importWarning({type:'memory_dump_parse_error',message:'Multiple values provided for scalar attribute '+
+attrName+' of memory allocator dump '+fullName+' (GUID='+guid+') for PID='+pid+' and dump ID='+
+dumpId+'.'});break;}
+const unit=attrArgs.units==='bytes'?tr.b.Unit.byName.sizeInBytes_smallerIsBetter:tr.b.Unit.byName.unitlessNumber_smallerIsBetter;const value=parseInt(attrValue,16);allocatorDump.addNumeric(attrName,new tr.b.Scalar(unit,value));break;}
+case'string':if(attrName in allocatorDump.diagnostics){this.model_.importWarning({type:'memory_dump_parse_error',message:'Multiple values provided for string attribute '+
+attrName+' of memory allocator dump '+fullName+' (GUID='+guid+') for PID='+pid+' and dump ID='+
+dumpId+'.'});break;}
+allocatorDump.addDiagnostic(attrName,attrValue);break;default:this.model_.importWarning({type:'memory_dump_parse_error',message:'Unknown type provided for attribute '+attrName+' of memory allocator dump '+fullName+' (GUID='+guid+') for PID='+pid+' and dump ID='+dumpId+': '+
+attrType});break;}}}},inferMemoryAllocatorDumpTree_(memoryAllocatorDumpsByFullName){const rootAllocatorDumps=[];const fullNames=Object.keys(memoryAllocatorDumpsByFullName);fullNames.sort();for(let i=0;i<fullNames.length;i++){let fullName=fullNames[i];let allocatorDump=memoryAllocatorDumpsByFullName[fullName];while(true){const lastSlashIndex=fullName.lastIndexOf('/');if(lastSlashIndex===-1){rootAllocatorDumps.push(allocatorDump);break;}
+const parentFullName=fullName.substring(0,lastSlashIndex);let parentAllocatorDump=memoryAllocatorDumpsByFullName[parentFullName];let parentAlreadyExisted=true;if(parentAllocatorDump===undefined){parentAlreadyExisted=false;parentAllocatorDump=new tr.model.MemoryAllocatorDump(allocatorDump.containerMemoryDump,parentFullName);if(allocatorDump.weak!==false){parentAllocatorDump.weak=undefined;}
+memoryAllocatorDumpsByFullName[parentFullName]=parentAllocatorDump;}
+allocatorDump.parent=parentAllocatorDump;parentAllocatorDump.children.push(allocatorDump);if(parentAlreadyExisted){if(!allocatorDump.weak){while(parentAllocatorDump!==undefined&&parentAllocatorDump.weak===undefined){parentAllocatorDump.weak=false;parentAllocatorDump=parentAllocatorDump.parent;}}
+break;}
+fullName=parentFullName;allocatorDump=parentAllocatorDump;}}
+for(const fullName in memoryAllocatorDumpsByFullName){const allocatorDump=memoryAllocatorDumpsByFullName[fullName];if(allocatorDump.weak===undefined){allocatorDump.weak=true;}}
+return rootAllocatorDumps;},parseMemoryDumpAllocatorEdges_(allMemoryAllocatorDumpsByGuid,dumpIdEvents,dumpId){for(const pid in dumpIdEvents){const processEvents=dumpIdEvents[pid];for(let i=0;i<processEvents.length;i++){const processEvent=processEvents[i];const dumps=processEvent.args.dumps;if(dumps===undefined)continue;const rawEdges=dumps.allocators_graph;if(rawEdges===undefined)continue;for(let j=0;j<rawEdges.length;j++){const rawEdge=rawEdges[j];const sourceGuid=rawEdge.source;const sourceDump=allMemoryAllocatorDumpsByGuid[sourceGuid];if(sourceDump===undefined){this.model_.importWarning({type:'memory_dump_parse_error',message:'Edge for PID='+pid+' and dump ID='+dumpId+' is missing source memory allocator dump (GUID='+
+sourceGuid+').'});continue;}
+const targetGuid=rawEdge.target;const targetDump=allMemoryAllocatorDumpsByGuid[targetGuid];if(targetDump===undefined){this.model_.importWarning({type:'memory_dump_parse_error',message:'Edge for PID='+pid+' and dump ID='+dumpId+' is missing target memory allocator dump (GUID='+
+targetGuid+').'});continue;}
+const importance=rawEdge.importance;const edge=new tr.model.MemoryAllocatorDumpLink(sourceDump,targetDump,importance);switch(rawEdge.type){case'ownership':if(sourceDump.owns!==undefined){this.model_.importWarning({type:'memory_dump_parse_error',message:'Memory allocator dump '+sourceDump.fullName+' (GUID='+sourceGuid+') already owns a memory'+' allocator dump ('+
+sourceDump.owns.target.fullName+').'});}else{sourceDump.owns=edge;targetDump.ownedBy.push(edge);}
+break;case'retention':sourceDump.retains.push(edge);targetDump.retainedBy.push(edge);break;default:this.model_.importWarning({type:'memory_dump_parse_error',message:'Invalid edge type: '+rawEdge.type+' (PID='+pid+', dump ID='+dumpId+', source='+sourceGuid+', target='+targetGuid+', importance='+importance+').'});}}}}},toModelTimeFromUs_(ts){if(!this.toModelTime_){this.toModelTime_=this.model_.clockSyncManager.getModelTimeTransformer(this.clockDomainId_);}
+return this.toModelTime_(tr.b.Unit.timestampFromUs(ts));},maybeToModelTimeFromUs_(ts){if(ts===undefined){return undefined;}
+return this.toModelTimeFromUs_(ts);},durationFromUs_(dur){if(dur===undefined){return undefined;}
+return tr.b.Unit.timestampFromUs(dur);}};tr.importer.Importer.register(TraceEventImporter);return{TraceEventImporter,};});'use strict';tr.exportTo('tr.e.net',function(){const AsyncSlice=tr.model.AsyncSlice;function NetAsyncSlice(){AsyncSlice.apply(this,arguments);this.url_=undefined;this.byteCount_=undefined;this.isTitleComputed_=false;this.isUrlComputed_=false;}
+NetAsyncSlice.prototype={__proto__:AsyncSlice.prototype,get viewSubGroupTitle(){return'NetLog';},get title(){if(this.isTitleComputed_||!this.isTopLevel){return this.title_;}
+if(this.url!==undefined&&this.url.length>0){this.title_=this.url;}else if(this.args!==undefined&&this.args.source_type!==undefined){this.title_=this.args.source_type;}
+this.isTitleComputed_=true;return this.title_;},set title(title){this.title_=title;},get url(){if(this.isUrlComputed_){return this.url_;}
+if(this.args!==undefined&&this.args.params!==undefined&&this.args.params.url!==undefined){this.url_=this.args.params.url;}else if(this.subSlices!==undefined&&this.subSlices.length>0){for(let i=0;i<this.subSlices.length&&!this.url_;i++){if(this.subSlices[i].url!==undefined){this.url_=this.subSlices[i].url;}}}
+this.isUrlComputed_=true;return this.url_;},get byteCount(){if(this.byteCount_!==undefined){return this.byteCount_;}
+this.byteCount_=0;if((this.originalTitle==='URL_REQUEST_JOB_FILTERED_BYTES_READ'||this.originalTitle==='URL_REQUEST_JOB_BYTES_READ')&&this.args!==undefined&&this.args.params!==undefined&&this.args.params.byte_count!==undefined){this.byteCount_=this.args.params.byte_count;}
+for(let i=0;i<this.subSlices.length;i++){this.byteCount_+=this.subSlices[i].byteCount;}
+return this.byteCount_;}};AsyncSlice.subTypes.register(NetAsyncSlice,{categoryParts:['netlog','disabled-by-default-netlog']});return{NetAsyncSlice,};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){function Parser(importer){this.importer=importer;this.model=importer.model;}
+Parser.prototype={__proto__:Object.prototype};const options=new tr.b.ExtensionRegistryOptions(tr.b.BASIC_REGISTRY_MODE);options.mandatoryBaseClass=Parser;tr.b.decorateExtensionRegistry(Parser,options);return{Parser,};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){const ColorScheme=tr.b.ColorScheme;const Parser=tr.e.importer.linux_perf.Parser;function AndroidParser(importer){Parser.call(this,importer);importer.registerEventHandler('tracing_mark_write:android',AndroidParser.prototype.traceMarkWriteAndroidEvent.bind(this));importer.registerEventHandler('0:android',AndroidParser.prototype.traceMarkWriteAndroidEvent.bind(this));this.model_=importer.model_;this.ppids_={};}
+function parseArgs(argsString){const args={};if(argsString){const argsArray=argsString.split(';');for(let i=0;i<argsArray.length;++i){const parts=argsArray[i].split('=');if(parts[0]){args[parts.shift()]=parts.join('=');}}}
+return args;}
+AndroidParser.prototype={__proto__:Parser.prototype,openAsyncSlice(thread,category,name,cookie,ts,args){const asyncSliceConstructor=tr.model.AsyncSlice.subTypes.getConstructor(category,name);const slice=new asyncSliceConstructor(category,name,ColorScheme.getColorIdForGeneralPurposeString(name),ts,args);const key=category+':'+name+':'+cookie;slice.id=cookie;slice.startThread=thread;if(!this.openAsyncSlices){this.openAsyncSlices={};}
+this.openAsyncSlices[key]=slice;},closeAsyncSlice(thread,category,name,cookie,ts,args){if(!this.openAsyncSlices){return;}
+const key=category+':'+name+':'+cookie;const slice=this.openAsyncSlices[key];if(!slice){return;}
+for(const arg in args){if(slice.args[arg]!==undefined){this.model_.importWarning({type:'parse_error',message:'Both the S and F events of '+slice.title+' provided values for argument '+arg+'.'+' The value of the F event will be used.'});}
+slice.args[arg]=args[arg];}
+slice.endThread=thread;slice.duration=ts-slice.start;slice.startThread.asyncSliceGroup.push(slice);delete this.openAsyncSlices[key];},traceMarkWriteAndroidEvent(eventName,cpuNumber,pid,ts,eventBase){const eventData=eventBase.details.split('|');switch(eventData[0]){case'B':{const ppid=parseInt(eventData[1]);const title=eventData[2];const args=parseArgs(eventData[3]);let category=eventData[4];if(category===undefined)category='android';const thread=this.model_.getOrCreateProcess(ppid).getOrCreateThread(pid);thread.name=eventBase.threadName;if(!thread.sliceGroup.isTimestampValidForBeginOrEnd(ts)){this.model_.importWarning({type:'parse_error',message:'Timestamps are moving backward.'});return false;}
+this.ppids_[pid]=ppid;thread.sliceGroup.beginSlice(category,title,ts,args);break;}
+case'E':{const ppid=this.ppids_[pid];if(ppid===undefined){break;}
+const thread=this.model_.getOrCreateProcess(ppid).getOrCreateThread(pid);if(!thread.sliceGroup.openSliceCount){break;}
+const slice=thread.sliceGroup.endSlice(ts);const args=parseArgs(eventData[3]);for(const arg in args){if(slice.args[arg]!==undefined){this.model_.importWarning({type:'parse_error',message:'Both the B and E events of '+slice.title+' provided values for argument '+arg+'.'+' The value of the E event will be used.'});}
+slice.args[arg]=args[arg];}
+break;}
+case'C':{const ppid=parseInt(eventData[1]);const name=eventData[2];const value=parseInt(eventData[3]);let category=eventData[4];if(category===undefined)category='android';const ctr=this.model_.getOrCreateProcess(ppid).getOrCreateCounter(category,name);if(ctr.numSeries===0){ctr.addSeries(new tr.model.CounterSeries(value,ColorScheme.getColorIdForGeneralPurposeString(ctr.name+'.'+'value')));}
+ctr.series.forEach(function(series){series.addCounterSample(ts,value);});break;}
+case'S':{const ppid=parseInt(eventData[1]);const name=eventData[2];const cookie=parseInt(eventData[3]);const args=parseArgs(eventData[4]);let category=eventData[5];if(category===undefined)category='android';const thread=this.model_.getOrCreateProcess(ppid).getOrCreateThread(pid);thread.name=eventBase.threadName;this.ppids_[pid]=ppid;this.openAsyncSlice(thread,category,name,cookie,ts,args);break;}
+case'F':{const ppid=parseInt(eventData[1]);const name=eventData[2];const cookie=parseInt(eventData[3]);const args=parseArgs(eventData[4]);let category=eventData[5];if(category===undefined)category='android';const thread=this.model_.getOrCreateProcess(ppid).getOrCreateThread(pid);thread.name=eventBase.threadName;this.ppids_[pid]=ppid;this.closeAsyncSlice(thread,category,name,cookie,ts,args);break;}
+default:return false;}
+return true;}};Parser.register(AndroidParser);return{AndroidParser,};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){const ColorScheme=tr.b.ColorScheme;const Parser=tr.e.importer.linux_perf.Parser;const binderTransRE=new RegExp('transaction=(\\d+) dest_node=(\\d+) '+'dest_proc=(\\d+) dest_thread=(\\d+) '+'reply=(\\d+) flags=(0x[0-9a-fA-F]+) '+'code=(0x[0-9a-fA-F]+)');const binderAllocRE=new RegExp('transaction=(\\d+) data_size=(\\d+) '+'offsets_size=(\\d+)');const binderTransReceivedRE=/transaction=(\d+)/;function isBinderThread(name){return(name.indexOf('Binder')>-1);}
+const TF_ONE_WAY=0x01;const TF_ROOT_OBJECT=0x04;const TF_STATUS_CODE=0x08;const TF_ACCEPT_FDS=0x10;const NO_FLAGS=0;function binderFlagsToHuman(num){const flag=parseInt(num,16);let str='';if(flag&TF_ONE_WAY){str+='this is a one-way call: async, no return; ';}
+if(flag&TF_ROOT_OBJECT){str+='contents are the components root object; ';}
+if(flag&TF_STATUS_CODE){str+='contents are a 32-bit status code; ';}
+if(flag&TF_ACCEPT_FDS){str+='allow replies with file descriptors; ';}
+if(flag===NO_FLAGS){str+='No Flags Set';}
+return str;}
+function isReplyToOrigin(calling,called){return(called.dest_proc===calling.calling_pid||called.dest_thread===calling.calling_pid);}
+function binderCodeToHuman(code){return'Java Layer Dependent';}
+function doInternalSlice(trans,slice,ts){if(slice.subSlices.length!==0){slice.subSlices[0].start=ts;return slice.subSlices[0];}
+const kthread=trans.calling_kthread.thread;const internalSlice=kthread.sliceGroup.pushCompleteSlice('binder',slice.title,ts,.001,0,0,slice.args);internalSlice.title=slice.title;internalSlice.id=slice.id;internalSlice.colorId=slice.colorId;slice.subSlices.push(internalSlice);return internalSlice;}
+function generateBinderArgsForSlice(trans,cThreadName){return{'Transaction Id':trans.transaction_key,'Destination Node':trans.dest_node,'Destination Process':trans.dest_proc,'Destination Thread':trans.dest_thread,'Destination Name':cThreadName,'Reply transaction?':trans.is_reply_transaction,'Flags':trans.flags+' '+
+binderFlagsToHuman(trans.flags),'Code':trans.code+' '+
+binderCodeToHuman(trans.code),'Calling PID':trans.calling_pid,'Calling tgid':trans.calling_kthread.thread.parent.pid};}
+function BinderTransaction(events,callingPid,callingTs,callingKthread){this.transaction_key=parseInt(events[1]);this.dest_node=parseInt(events[2]);this.dest_proc=parseInt(events[3]);this.dest_thread=parseInt(events[4]);this.is_reply_transaction=parseInt(events[5])===1?true:false;this.expect_reply=((this.is_reply_transaction===false)&&(parseInt(events[6],16)&TF_ONE_WAY)===0);this.flags=events[6];this.code=events[7];this.calling_pid=callingPid;this.calling_ts=callingTs;this.calling_kthread=callingKthread;}
+function BinderParser(importer){Parser.call(this,importer);importer.registerEventHandler('binder_locked',BinderParser.prototype.binderLocked.bind(this));importer.registerEventHandler('binder_unlock',BinderParser.prototype.binderUnlock.bind(this));importer.registerEventHandler('binder_lock',BinderParser.prototype.binderLock.bind(this));importer.registerEventHandler('binder_transaction',BinderParser.prototype.binderTransaction.bind(this));importer.registerEventHandler('binder_transaction_received',BinderParser.prototype.binderTransactionReceived.bind(this));importer.registerEventHandler('binder_transaction_alloc_buf',BinderParser.prototype.binderTransactionAllocBuf.bind(this));this.model_=importer.model;this.kthreadlookup={};this.importer_=importer;this.transWaitingRecv={};this.syncTransWaitingCompletion={};this.recursiveSyncTransWaitingCompletion_ByPID={};this.receivedTransWaitingConversion={};}
+BinderParser.prototype={__proto__:Parser.prototype,binderLock(eventName,cpuNumber,pid,ts,eventBase){const tgid=parseInt(eventBase.tgid);if(isNaN(tgid))return false;this.doNameMappings(pid,tgid,eventName.threadName);const kthread=this.importer_.getOrCreateBinderKernelThread(eventBase.threadName,tgid,pid);kthread.binderAttemptLockTS=ts;kthread.binderOpenTsA=ts;return true;},binderLocked(eventName,cpuNumber,pid,ts,eventBase){const tgid=parseInt(eventBase.tgid);if(isNaN(tgid))return false;const binderThread=isBinderThread(eventBase.threadName);const name=eventBase.threadName;const kthread=this.importer_.getOrCreateBinderKernelThread(eventBase.threadName,tgid,pid);this.doNameMappings(pid,tgid,name);const rthread=kthread.thread;kthread.binderLockAquiredTS=ts;if(kthread.binderAttemptLockTS===undefined)return false;const args=this.generateArgsForSlice(tgid,pid,name,kthread);rthread.sliceGroup.pushCompleteSlice('binder','binder lock waiting',kthread.binderAttemptLockTS,ts-kthread.binderAttemptLockTS,0,0,args);kthread.binderAttemptLockTS=undefined;return true;},binderUnlock(eventName,cpuNumber,pid,ts,eventBase){const tgid=parseInt(eventBase.tgid);if(isNaN(tgid))return false;const kthread=this.importer_.getOrCreateBinderKernelThread(eventBase.threadName,tgid,pid);if(kthread.binderLockAquiredTS===undefined)return false;const args=this.generateArgsForSlice(tgid,pid,eventBase.threadName,kthread);kthread.thread.sliceGroup.pushCompleteSlice('binder','binder lock held',kthread.binderLockAquiredTS,ts-kthread.binderLockAquiredTS,0,0,args);kthread.binderLockAquiredTS=undefined;return true;},binderTransaction(eventName,cpuNumber,pid,ts,eventBase){const event=binderTransRE.exec(eventBase.details);if(event===undefined)return false;const tgid=parseInt(eventBase.tgid);if(isNaN(tgid))return false;this.doNameMappings(pid,tgid,eventBase.threadName);const kthread=this.importer_.getOrCreateBinderKernelThread(eventBase.threadName,tgid,pid);const trans=new BinderTransaction(event,pid,ts,kthread);const args=generateBinderArgsForSlice(trans,eventBase.threadName);const priorReceive=this.getPriorReceiveOnPID(pid);if(priorReceive!==false){return this.modelPriorReceive(priorReceive,ts,pid,tgid,kthread,trans,args,event);}
+const recursiveTrans=this.getRecursiveTransactionNeedingCompletion(pid);if(recursiveTrans!==false){return this.modelRecursiveTransactions(recursiveTrans,ts,pid,kthread,trans,args);}
+const slice=kthread.thread.sliceGroup.pushCompleteSlice('binder','',ts,.03,0,0,args);slice.colorId=ColorScheme.getColorIdForGeneralPurposeString(ts.toString());trans.slice=slice;if(trans.expect_reply){slice.title='binder transaction';}else{slice.title='binder transaction async';}
+this.addTransactionWaitingForRecv(trans.transaction_key,trans);return true;},binderTransactionReceived(eventName,cpuNumber,pid,ts,eventBase){const event=binderTransReceivedRE.exec(eventBase.details);if(event===undefined)return false;const tgid=parseInt(eventBase.tgid);if(isNaN(tgid))return false;const transactionkey=parseInt(event[1]);const kthread=this.importer_.getOrCreateBinderKernelThread(eventBase.threadName,tgid,pid);const syncComplete=this.getSyncTransNeedsCompletion(transactionkey);if(syncComplete!==false){const syncTrans=syncComplete[0];const syncSlice=syncTrans.slice;const responseTrans=syncComplete[1];const responseSlice=responseTrans.slice;syncSlice.duration=ts-syncSlice.start;const syncInternal=doInternalSlice(syncTrans,syncSlice,ts);const responseTs=responseSlice.start+responseSlice.duration;const responseInternal=doInternalSlice(responseTrans,responseSlice,responseTs);if(responseSlice.outFlowEvents.length===0||syncSlice.inFlowEvents.length===0){const flow=this.generateFlow(responseInternal,syncInternal,responseTrans,syncTrans);syncSlice.inFlowEvents.push(flow);responseSlice.outFlowEvents.push(flow);this.model_.flowEvents.push(flow);}
+for(let i=1;i<syncSlice.inFlowEvents.length;i++){syncSlice.inFlowEvents[i].duration=ts-syncSlice.inFlowEvents[i].start;}
+return true;}
+const trForRecv=this.getTransactionWaitingForRecv(transactionkey);if(trForRecv!==false){if(!trForRecv.expect_reply){const args=generateBinderArgsForSlice(trForRecv,eventBase.threadName);const slice=kthread.thread.sliceGroup.pushCompleteSlice('binder','binder Async recv',ts,.03,0,0,args);const fakeEvent=[0,0,0,0,0,0,0];const fakeTrans=new BinderTransaction(fakeEvent,pid,ts,kthread);const flow=this.generateFlow(trForRecv.slice,slice,trForRecv,fakeTrans);this.model_.flowEvents.push(flow);trForRecv.slice.title='binder transaction async';trForRecv.slice.duration=.03;return true;}
+trForRecv.slice.title='binder transaction';this.setCurrentReceiveOnPID(pid,[ts,trForRecv]);return true;}
+return false;},binderTransactionAllocBuf(eventName,cpuNumber,pid,ts,eventBase){const event=binderAllocRE.exec(eventBase.details);if(event===null)return false;const tgid=parseInt(eventBase.tgid);if(isNaN(tgid))return false;const transactionkey=parseInt(event[1]);const kthread=this.importer_.getOrCreateBinderKernelThread(eventBase.threadName,tgid,pid);const trans=this.peekTransactionWaitingForRecv(transactionkey);if(trans&&trans.slice){trans.slice.args['Data Size']=parseInt(event[2]);trans.slice.args['Offsets Size']=parseInt(event[3]);return true;}
+return false;},modelRecursiveTransactions(recursiveTrans,ts,pid,kthread,trans,args){const recursiveSlice=recursiveTrans[1].slice;const origSlice=recursiveTrans[0].slice;recursiveSlice.duration=ts-recursiveSlice.start;recursiveSlice.args=args;trans.slice=recursiveSlice;if(trans.is_reply_transaction){origSlice.duration=ts-origSlice.start;this.addSyncTransNeedingCompletion(trans.transaction_key,recursiveTrans);if(isReplyToOrigin(recursiveTrans[0],trans)){this.removeRecursiveTransaction(pid);}}else{const slice=kthread.thread.sliceGroup.pushCompleteSlice('binder','',ts,.03,0,0,args);trans.slice=slice;this.addTransactionWaitingForRecv(trans.transaction_key,trans);}
+return true;},modelPriorReceive(priorReceive,ts,pid,tgid,kthread,trans,args,event){const calleeSlice=priorReceive[1].slice;const calleeTrans=priorReceive[1];const recvTs=priorReceive[0];let slice=kthread.thread.sliceGroup.pushCompleteSlice('binder','',recvTs,ts-recvTs,0,0);const flow=this.generateFlow(calleeSlice,slice,calleeTrans,trans);this.model_.flowEvents.push(flow);trans.slice=slice;if(trans.is_reply_transaction){slice.title='binder reply';slice.args=args;this.addSyncTransNeedingCompletion(trans.transaction_key,[calleeTrans,trans]);}else{slice.title='binder reply';const trans1=new BinderTransaction(event,pid,ts,kthread);slice=kthread.thread.sliceGroup.pushCompleteSlice('binder','binder transaction',recvTs,(ts-recvTs),0,0,args);if(!trans.expect_reply){slice.title='binder transaction async';slice.duration=.03;}else{}
+trans1.slice=slice;this.addRecursiveSyncTransNeedingCompletion(pid,[calleeTrans,trans]);this.addTransactionWaitingForRecv(trans.transaction_key,trans1);}
+return true;},getRecursiveTransactionNeedingCompletion(pid){if(this.recursiveSyncTransWaitingCompletion_ByPID[pid]===undefined){return false;}
+const len=this.recursiveSyncTransWaitingCompletion_ByPID[pid].length;if(len===0)return false;return this.recursiveSyncTransWaitingCompletion_ByPID[pid][len-1];},addRecursiveSyncTransNeedingCompletion(pid,tuple){if(this.recursiveSyncTransWaitingCompletion_ByPID[pid]===undefined){this.recursiveSyncTransWaitingCompletion_ByPID[pid]=[];}
+this.recursiveSyncTransWaitingCompletion_ByPID[pid].push(tuple);},removeRecursiveTransaction(pid){const len=this.recursiveSyncTransWaitingCompletion_ByPID[pid].length;if(len===0){delete this.recursiveSyncTransWaitingCompletion_ByPID[pid];return;}
+this.recursiveSyncTransWaitingCompletion_ByPID[pid].splice(len-1,1);},setCurrentReceiveOnPID(pid,tuple){if(this.receivedTransWaitingConversion[pid]===undefined){this.receivedTransWaitingConversion[pid]=[];}
+this.receivedTransWaitingConversion[pid].push(tuple);},getPriorReceiveOnPID(pid){if(this.receivedTransWaitingConversion[pid]===undefined){return false;}
+const len=this.receivedTransWaitingConversion[pid].length;if(len===0)return false;return this.receivedTransWaitingConversion[pid].splice(len-1,1)[0];},addSyncTransNeedingCompletion(transactionkey,tuple){const dict=this.syncTransWaitingCompletion;dict[transactionkey]=tuple;},getSyncTransNeedsCompletion(transactionkey){const ret=this.syncTransWaitingCompletion[transactionkey];if(ret===undefined)return false;delete this.syncTransWaitingCompletion[transactionkey];return ret;},getTransactionWaitingForRecv(transactionkey){const ret=this.transWaitingRecv[transactionkey];if(ret===undefined)return false;delete this.transWaitingRecv[transactionkey];return ret;},peekTransactionWaitingForRecv(transactionkey){const ret=this.transWaitingRecv[transactionkey];if(ret===undefined)return false;return ret;},addTransactionWaitingForRecv(transactionkey,transaction){this.transWaitingRecv[transactionkey]=transaction;},generateFlow(from,to,fromTrans,toTrans){const title='Transaction from : '+
+this.pid2name(fromTrans.calling_pid)+' From PID: '+fromTrans.calling_pid+' to pid: '+
+toTrans.calling_pid+' Thread Name: '+this.pid2name(toTrans.calling_pid);const ts=from.start;const flow=new tr.model.FlowEvent('binder','binder',title,1,ts,[]);flow.startSlice=from;flow.endSlice=to;flow.start=from.start;flow.duration=to.start-ts;from.outFlowEvents.push(flow);to.inFlowEvents.push(flow);return flow;},generateArgsForSlice(tgid,pid,name,kthread){return{'Thread Name':name,pid,'gid':tgid};},pid2name(pid){return this.kthreadlookup[pid];},doNameMappings(pid,tgid,name){this.registerPidName(pid,name);this.registerPidName(tgid,name);},registerPidName(pid,name){if(this.pid2name(pid)===undefined){this.kthreadlookup[pid]=name;}}};Parser.register(BinderParser);return{BinderParser,};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){const ColorScheme=tr.b.ColorScheme;const Parser=tr.e.importer.linux_perf.Parser;function BusParser(importer){Parser.call(this,importer);importer.registerEventHandler('memory_bus_usage',BusParser.prototype.traceMarkWriteBusEvent.bind(this));this.model_=importer.model_;this.ppids_={};}
+BusParser.prototype={__proto__:Parser.prototype,traceMarkWriteBusEvent(eventName,cpuNumber,pid,ts,eventBase,threadName){const re=new RegExp('bus=(\\S+) rw_bytes=(\\d+) r_bytes=(\\d+) '+'w_bytes=(\\d+) cycles=(\\d+) ns=(\\d+)');const event=re.exec(eventBase.details);const name=event[1];const rwBytes=parseInt(event[2]);const rBytes=parseInt(event[3]);const wBytes=parseInt(event[4]);const cycles=parseInt(event[5]);const ns=parseInt(event[6]);const sec=tr.b.convertUnit(ns,tr.b.UnitPrefixScale.METRIC.NANO,tr.b.UnitPrefixScale.METRIC.NONE);const readBandwidthInBps=rBytes/sec;const readBandwidthInMiBps=tr.b.convertUnit(readBandwidthInBps,tr.b.UnitPrefixScale.BINARY.NONE,tr.b.UnitPrefixScale.BINARY.MEBI);const writeBandwidthInBps=wBytes/sec;const writeBandwidthInMiBps=tr.b.convertUnit(writeBandwidthInBps,tr.b.UnitPrefixScale.BINARY.NONE,tr.b.UnitPrefixScale.BINARY.MEBI);let ctr=this.model_.kernel.getOrCreateCounter(null,'bus '+name+' read');if(ctr.numSeries===0){ctr.addSeries(new tr.model.CounterSeries('value',ColorScheme.getColorIdForGeneralPurposeString(ctr.name+'.'+'value')));}
+ctr.series.forEach(function(series){series.addCounterSample(ts,readBandwidthInMiBps);});ctr=this.model_.kernel.getOrCreateCounter(null,'bus '+name+' write');if(ctr.numSeries===0){ctr.addSeries(new tr.model.CounterSeries('value',ColorScheme.getColorIdForGeneralPurposeString(ctr.name+'.'+'value')));}
+ctr.series.forEach(function(series){series.addCounterSample(ts,writeBandwidthInMiBps);});return true;}};Parser.register(BusParser);return{BusParser,};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){const ColorScheme=tr.b.ColorScheme;const Parser=tr.e.importer.linux_perf.Parser;function ClockParser(importer){Parser.call(this,importer);importer.registerEventHandler('clock_set_rate',ClockParser.prototype.traceMarkWriteClockEvent.bind(this));importer.registerEventHandler('clk_set_rate',ClockParser.prototype.traceMarkWriteClkEvent.bind(this));importer.registerEventHandler('clock_enable',ClockParser.prototype.traceMarkWriteClockOnOffEvent.bind(this));importer.registerEventHandler('clock_disable',ClockParser.prototype.traceMarkWriteClockOnOffEvent.bind(this));importer.registerEventHandler('clk_enable',ClockParser.prototype.traceMarkWriteClkOnEvent.bind(this));importer.registerEventHandler('clk_disable',ClockParser.prototype.traceMarkWriteClkOffEvent.bind(this));this.model_=importer.model_;this.ppids_={};}
+ClockParser.prototype={__proto__:Parser.prototype,clockMark(name,subName,value,ts){const ctr=this.model_.kernel.getOrCreateCounter(null,name+' '+subName);if(ctr.numSeries===0){ctr.addSeries(new tr.model.CounterSeries('value',ColorScheme.getColorIdForGeneralPurposeString(ctr.name+'.'+'value')));}
+ctr.series.forEach(function(series){series.addCounterSample(ts,value);});},traceMarkWriteClockEvent(eventName,cpuNumber,pid,ts,eventBase,threadName){const event=/(\S+) state=(\d+)/.exec(eventBase.details);const name=event[1];const rate=parseInt(event[2]);this.clockMark(name,'Frequency',rate,ts);return true;},traceMarkWriteClkEvent(eventName,cpuNumber,pid,ts,eventBase,threadName){const event=/(\S+) (\d+)/.exec(eventBase.details);const name=event[1];const rate=parseInt(event[2]);this.clockMark(name,'Frequency',rate,ts);return true;},traceMarkWriteClockOnOffEvent(eventName,cpuNumber,pid,ts,eventBase,threadName){const event=/(\S+) state=(\d+)/.exec(eventBase.details);const name=event[1];const state=parseInt(event[2]);this.clockMark(name,'State',state,ts);return true;},traceMarkWriteClkOnEvent(eventName,cpuNumber,pid,ts,eventBase,threadName){const event=/\S+/.exec(eventBase.details);const name=event[0];this.clockMark(name,'State',1,ts);return true;},traceMarkWriteClkOffEvent(eventName,cpuNumber,pid,ts,eventBase,threadName){const event=/\S+/.exec(eventBase.details);const name=event[0];this.clockMark(name,'State',0,ts);return true;}};Parser.register(ClockParser);return{ClockParser,};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){const ColorScheme=tr.b.ColorScheme;const Parser=tr.e.importer.linux_perf.Parser;function CpufreqParser(importer){Parser.call(this,importer);importer.registerEventHandler('cpufreq_interactive_up',CpufreqParser.prototype.cpufreqUpDownEvent.bind(this));importer.registerEventHandler('cpufreq_interactive_down',CpufreqParser.prototype.cpufreqUpDownEvent.bind(this));importer.registerEventHandler('cpufreq_interactive_already',CpufreqParser.prototype.cpufreqTargetEvent.bind(this));importer.registerEventHandler('cpufreq_interactive_notyet',CpufreqParser.prototype.cpufreqTargetEvent.bind(this));importer.registerEventHandler('cpufreq_interactive_setspeed',CpufreqParser.prototype.cpufreqTargetEvent.bind(this));importer.registerEventHandler('cpufreq_interactive_target',CpufreqParser.prototype.cpufreqTargetEvent.bind(this));importer.registerEventHandler('cpufreq_interactive_boost',CpufreqParser.prototype.cpufreqBoostUnboostEvent.bind(this));importer.registerEventHandler('cpufreq_interactive_unboost',CpufreqParser.prototype.cpufreqBoostUnboostEvent.bind(this));}
+function splitData(input){const data={};const args=input.split(/\s+/);const len=args.length;for(let i=0;i<len;i++){const item=args[i].split('=');data[item[0]]=parseInt(item[1]);}
+return data;}
+CpufreqParser.prototype={__proto__:Parser.prototype,cpufreqSlice(ts,eventName,cpu,args){const kthread=this.importer.getOrCreatePseudoThread('cpufreq');kthread.openSlice=eventName;const slice=new tr.model.ThreadSlice('',kthread.openSlice,ColorScheme.getColorIdForGeneralPurposeString(kthread.openSlice),ts,args,0);kthread.thread.sliceGroup.pushSlice(slice);},cpufreqBoostSlice(ts,eventName,args){const kthread=this.importer.getOrCreatePseudoThread('cpufreq_boost');kthread.openSlice=eventName;const slice=new tr.model.ThreadSlice('',kthread.openSlice,ColorScheme.getColorIdForGeneralPurposeString(kthread.openSlice),ts,args,0);kthread.thread.sliceGroup.pushSlice(slice);},cpufreqUpDownEvent(eventName,cpuNumber,pid,ts,eventBase){const data=splitData(eventBase.details);this.cpufreqSlice(ts,eventName,data.cpu,data);return true;},cpufreqTargetEvent(eventName,cpuNumber,pid,ts,eventBase){const data=splitData(eventBase.details);this.cpufreqSlice(ts,eventName,data.cpu,data);return true;},cpufreqBoostUnboostEvent(eventName,cpuNumber,pid,ts,eventBase){this.cpufreqBoostSlice(ts,eventName,{type:eventBase.details});return true;}};Parser.register(CpufreqParser);return{CpufreqParser,};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){const ColorScheme=tr.b.ColorScheme;const Parser=tr.e.importer.linux_perf.Parser;function DiskParser(importer){Parser.call(this,importer);importer.registerEventHandler('f2fs_write_begin',DiskParser.prototype.f2fsWriteBeginEvent.bind(this));importer.registerEventHandler('f2fs_write_end',DiskParser.prototype.f2fsWriteEndEvent.bind(this));importer.registerEventHandler('f2fs_sync_file_enter',DiskParser.prototype.f2fsSyncFileEnterEvent.bind(this));importer.registerEventHandler('f2fs_sync_file_exit',DiskParser.prototype.f2fsSyncFileExitEvent.bind(this));importer.registerEventHandler('ext4_sync_file_enter',DiskParser.prototype.ext4SyncFileEnterEvent.bind(this));importer.registerEventHandler('ext4_sync_file_exit',DiskParser.prototype.ext4SyncFileExitEvent.bind(this));importer.registerEventHandler('ext4_da_write_begin',DiskParser.prototype.ext4WriteBeginEvent.bind(this));importer.registerEventHandler('ext4_da_write_end',DiskParser.prototype.ext4WriteEndEvent.bind(this));importer.registerEventHandler('block_rq_issue',DiskParser.prototype.blockRqIssueEvent.bind(this));importer.registerEventHandler('block_rq_complete',DiskParser.prototype.blockRqCompleteEvent.bind(this));}
+DiskParser.prototype={__proto__:Parser.prototype,openAsyncSlice(ts,category,threadName,pid,key,name){const kthread=this.importer.getOrCreateKernelThread(category+':'+threadName,pid);const asyncSliceConstructor=tr.model.AsyncSlice.subTypes.getConstructor(category,name);const slice=new asyncSliceConstructor(category,name,ColorScheme.getColorIdForGeneralPurposeString(name),ts);slice.startThread=kthread.thread;if(!kthread.openAsyncSlices){kthread.openAsyncSlices={};}
+kthread.openAsyncSlices[key]=slice;},closeAsyncSlice(ts,category,threadName,pid,key,args){const kthread=this.importer.getOrCreateKernelThread(category+':'+threadName,pid);if(kthread.openAsyncSlices){const slice=kthread.openAsyncSlices[key];if(slice){slice.duration=ts-slice.start;slice.args=args;slice.endThread=kthread.thread;slice.subSlices=[new tr.model.AsyncSlice(category,slice.title,slice.colorId,slice.start,slice.args,slice.duration)];kthread.thread.asyncSliceGroup.push(slice);delete kthread.openAsyncSlices[key];}}},f2fsWriteBeginEvent(eventName,cpuNumber,pid,ts,eventBase){const event=/dev = \((\d+,\d+)\), ino = (\d+), pos = (\d+), len = (\d+), flags = (\d+)/.exec(eventBase.details);if(!event)return false;const device=event[1];const inode=parseInt(event[2]);const pos=parseInt(event[3]);const len=parseInt(event[4]);const key=device+'-'+inode+'-'+pos+'-'+len;this.openAsyncSlice(ts,'f2fs',eventBase.threadName,eventBase.pid,key,'f2fs_write');return true;},f2fsWriteEndEvent(eventName,cpuNumber,pid,ts,eventBase){const event=/dev = \((\d+,\d+)\), ino = (\d+), pos = (\d+), len = (\d+), copied = (\d+)/.exec(eventBase.details);if(!event)return false;const device=event[1];const inode=parseInt(event[2]);const pos=parseInt(event[3]);const len=parseInt(event[4]);const error=parseInt(event[5])!==len;const key=device+'-'+inode+'-'+pos+'-'+len;this.closeAsyncSlice(ts,'f2fs',eventBase.threadName,eventBase.pid,key,{device,inode,error});return true;},ext4WriteBeginEvent(eventName,cpuNumber,pid,ts,eventBase){const event=/dev (\d+,\d+) ino (\d+) pos (\d+) len (\d+) flags (\d+)/.exec(eventBase.details);if(!event)return false;const device=event[1];const inode=parseInt(event[2]);const pos=parseInt(event[3]);const len=parseInt(event[4]);const key=device+'-'+inode+'-'+pos+'-'+len;this.openAsyncSlice(ts,'ext4',eventBase.threadName,eventBase.pid,key,'ext4_write');return true;},ext4WriteEndEvent(eventName,cpuNumber,pid,ts,eventBase){const event=/dev (\d+,\d+) ino (\d+) pos (\d+) len (\d+) copied (\d+)/.exec(eventBase.details);if(!event)return false;const device=event[1];const inode=parseInt(event[2]);const pos=parseInt(event[3]);const len=parseInt(event[4]);const error=parseInt(event[5])!==len;const key=device+'-'+inode+'-'+pos+'-'+len;this.closeAsyncSlice(ts,'ext4',eventBase.threadName,eventBase.pid,key,{device,inode,error});return true;},f2fsSyncFileEnterEvent(eventName,cpuNumber,pid,ts,eventBase){const event=new RegExp('dev = \\((\\d+,\\d+)\\), ino = (\\d+), pino = (\\d+), i_mode = (\\S+), '+'i_size = (\\d+), i_nlink = (\\d+), i_blocks = (\\d+), i_advise = (\\d+)').exec(eventBase.details);if(!event)return false;const device=event[1];const inode=parseInt(event[2]);const key=device+'-'+inode;this.openAsyncSlice(ts,'f2fs',eventBase.threadName,eventBase.pid,key,'fsync');return true;},f2fsSyncFileExitEvent(eventName,cpuNumber,pid,ts,eventBase){const event=new RegExp('dev = \\((\\d+,\\d+)\\), ino = (\\d+), checkpoint is (\\S+), '+'datasync = (\\d+), ret = (\\d+)').exec(eventBase.details.replace('not needed','not_needed'));if(!event)return false;const device=event[1];const inode=parseInt(event[2]);const error=parseInt(event[5]);const key=device+'-'+inode;this.closeAsyncSlice(ts,'f2fs',eventBase.threadName,eventBase.pid,key,{device,inode,error});return true;},ext4SyncFileEnterEvent(eventName,cpuNumber,pid,ts,eventBase){const event=/dev (\d+,\d+) ino (\d+) parent (\d+) datasync (\d+)/.exec(eventBase.details);if(!event)return false;const device=event[1];const inode=parseInt(event[2]);const datasync=(event[4]==='1')||(event[4]===1);const key=device+'-'+inode;const action=datasync?'fdatasync':'fsync';this.openAsyncSlice(ts,'ext4',eventBase.threadName,eventBase.pid,key,action);return true;},ext4SyncFileExitEvent(eventName,cpuNumber,pid,ts,eventBase){const event=/dev (\d+,\d+) ino (\d+) ret (\d+)/.exec(eventBase.details);if(!event)return false;const device=event[1];const inode=parseInt(event[2]);const error=parseInt(event[3]);const key=device+'-'+inode;this.closeAsyncSlice(ts,'ext4',eventBase.threadName,eventBase.pid,key,{device,inode,error});return true;},blockRqIssueEvent(eventName,cpuNumber,pid,ts,eventBase){const event=new RegExp('(\\d+,\\d+) (F)?([DWRN])(F)?(A)?(S)?(M)? '+'\\d+ \\(.*\\) (\\d+) \\+ (\\d+) \\[.*\\]').exec(eventBase.details);if(!event)return false;let action;switch(event[3]){case'D':action='discard';break;case'W':action='write';break;case'R':action='read';break;case'N':action='none';break;default:action='unknown';break;}
+if(event[2]){action+=' flush';}
+if(event[4]==='F'){action+=' fua';}
+if(event[5]==='A'){action+=' ahead';}
+if(event[6]==='S'){action+=' sync';}
+if(event[7]==='M'){action+=' meta';}
+const device=event[1];const sector=parseInt(event[8]);const numSectors=parseInt(event[9]);const key=device+'-'+sector+'-'+numSectors;this.openAsyncSlice(ts,'block',eventBase.threadName,eventBase.pid,key,action);return true;},blockRqCompleteEvent(eventName,cpuNumber,pid,ts,eventBase){const event=new RegExp('(\\d+,\\d+) (F)?([DWRN])(F)?(A)?(S)?(M)? '+'\\(.*\\) (\\d+) \\+ (\\d+) \\[(.*)\\]').exec(eventBase.details);if(!event)return false;const device=event[1];const sector=parseInt(event[8]);const numSectors=parseInt(event[9]);const error=parseInt(event[10]);const key=device+'-'+sector+'-'+numSectors;this.closeAsyncSlice(ts,'block',eventBase.threadName,eventBase.pid,key,{device,sector,numSectors,error});return true;}};Parser.register(DiskParser);return{DiskParser,};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){const ColorScheme=tr.b.ColorScheme;const Parser=tr.e.importer.linux_perf.Parser;function DmaFenceParser(importer){Parser.call(this,importer);this.model_=importer.model_;importer.registerEventHandler('dma_fence_init',DmaFenceParser.prototype.initEvent.bind(this));importer.registerEventHandler('dma_fence_emit',DmaFenceParser.prototype.initEvent.bind(this));importer.registerEventHandler('dma_fence_destroy',DmaFenceParser.prototype.fenceDestroyEvent.bind(this));importer.registerEventHandler('dma_fence_enable_signal',DmaFenceParser.prototype.fenceEnableSignalEvent.bind(this));importer.registerEventHandler('dma_fence_signaled',DmaFenceParser.prototype.fenceSignaledEvent.bind(this));importer.registerEventHandler('dma_fence_wait_start',DmaFenceParser.prototype.fenceWaitEvent.bind(this));importer.registerEventHandler('dma_fence_wait_end',DmaFenceParser.prototype.fenceWaitEvent.bind(this));importer.registerEventHandler('fence_init',DmaFenceParser.prototype.initEvent.bind(this));importer.registerEventHandler('fence_emit',DmaFenceParser.prototype.initEvent.bind(this));importer.registerEventHandler('fence_destroy',DmaFenceParser.prototype.fenceDestroyEvent.bind(this));importer.registerEventHandler('fence_enable_signal',DmaFenceParser.prototype.fenceEnableSignalEvent.bind(this));importer.registerEventHandler('fence_signaled',DmaFenceParser.prototype.fenceSignaledEvent.bind(this));importer.registerEventHandler('fence_wait_start',DmaFenceParser.prototype.fenceWaitEvent.bind(this));importer.registerEventHandler('fence_wait_end',DmaFenceParser.prototype.fenceWaitEvent.bind(this));this.model_=importer.model_;}
+const fenceRE=/driver=(\S+) timeline=(\S+) context=(\d+) seqno=(\d+)/;DmaFenceParser.prototype={__proto__:Parser.prototype,initEvent(eventName,cpuNumber,pid,ts,eventBase){const event=fenceRE.exec(eventBase.details);if(!event)return false;if(eventBase.tgid===undefined){return false;}
+const thread=this.importer.getOrCreatePseudoThread(event[2]);thread.lastActiveTs=ts;return true;},fenceDestroyEvent(eventName,cpuNumber,pid,ts,eventBase){const event=fenceRE.exec(eventBase.details);if(!event)return false;if(eventBase.tgid===undefined){return false;}
+const thread=this.importer.getOrCreatePseudoThread(event[2]);const name='fence_destroy('+event[4]+')';const colorName='fence('+event[4]+')';if(thread.lastActiveTs!==undefined){const duration=ts-thread.lastActiveTs;const slice=new tr.model.ThreadSlice('',name,ColorScheme.getColorIdForGeneralPurposeString(colorName),thread.lastActiveTs,{driver:event[1],context:event[3]},duration);thread.thread.sliceGroup.pushSlice(slice);}
+if(thread.thread.sliceGroup.openSliceCount>0){thread.thread.sliceGroup.endSlice(ts);}
+thread.lastActiveTs=ts;},fenceEnableSignalEvent(eventName,cpuNumber,pid,ts,eventBase){const event=fenceRE.exec(eventBase.details);if(!event)return false;if(eventBase.tgid===undefined){return false;}
+const thread=this.importer.getOrCreatePseudoThread(event[2]);const name='fence_enable('+event[4]+')';const colorName='fence('+event[4]+')';if(thread.lastActiveTs!==undefined){const duration=ts-thread.lastActiveTs;const slice=new tr.model.ThreadSlice('',name,ColorScheme.getColorIdForGeneralPurposeString(colorName),thread.lastActiveTs,{driver:event[1],context:event[3]},duration);thread.thread.sliceGroup.pushSlice(slice);}
+if(thread.thread.sliceGroup.openSliceCount>0){thread.thread.sliceGroup.endSlice(ts);}
+thread.lastActiveTs=ts;},fenceSignaledEvent(eventName,cpuNumber,pid,ts,eventBase){const event=fenceRE.exec(eventBase.details);if(!event)return false;if(eventBase.tgid===undefined){return false;}
+const thread=this.importer.getOrCreatePseudoThread(event[2]);const name='fence_signal('+event[4]+')';const colorName='fence('+event[4]+')';if(thread.lastActiveTs!==undefined){const duration=ts-thread.lastActiveTs;const slice=new tr.model.ThreadSlice('',name,ColorScheme.getColorIdForGeneralPurposeString(colorName),thread.lastActiveTs,{driver:event[1],context:event[3]},duration);thread.thread.sliceGroup.pushSlice(slice);}
+if(thread.thread.sliceGroup.openSliceCount>0){thread.thread.sliceGroup.endSlice(ts);}
+thread.lastActiveTs=ts;return true;},fenceWaitEvent(eventName,cpuNumber,pid,ts,eventBase){if(eventBase.tgid===undefined)return false;const event=fenceRE.exec(eventBase.details);if(!event)return false;const tgid=parseInt(eventBase.tgid);const thread=this.model_.getOrCreateProcess(tgid).getOrCreateThread(pid);thread.name=eventBase.threadName;const slices=thread.kernelSliceGroup;if(!slices.isTimestampValidForBeginOrEnd(ts)){this.model_.importWarning({type:'parse_error',message:'Timestamps are moving backward.'});return false;}
+const name='dma_fence_wait("'+event[2]+'")';if(eventName.endsWith('start')){const slice=slices.beginSlice(null,name,ts,{driver:event[1],context:event[3],seqno:event[4],});}else{if(slices.openSliceCount>0){slices.endSlice(ts);}}
+return true;},};Parser.register(DmaFenceParser);return{DmaFenceParser,};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){const ColorScheme=tr.b.ColorScheme;const Parser=tr.e.importer.linux_perf.Parser;function DrmParser(importer){Parser.call(this,importer);importer.registerEventHandler('drm_vblank_event',DrmParser.prototype.vblankEvent.bind(this));}
+DrmParser.prototype={__proto__:Parser.prototype,drmVblankSlice(ts,eventName,args){const kthread=this.importer.getOrCreatePseudoThread('drm_vblank');kthread.openSlice=eventName;const slice=new tr.model.ThreadSlice('',kthread.openSlice,ColorScheme.getColorIdForGeneralPurposeString(kthread.openSlice),ts,args,0);kthread.thread.sliceGroup.pushSlice(slice);},vblankEvent(eventName,cpuNumber,pid,ts,eventBase){const event=/crtc=(\d+), seq=(\d+)/.exec(eventBase.details);if(!event)return false;const crtc=parseInt(event[1]);const seq=parseInt(event[2]);this.drmVblankSlice(ts,'vblank:'+crtc,{crtc,seq});return true;}};Parser.register(DrmParser);return{DrmParser,};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){const ColorScheme=tr.b.ColorScheme;const Parser=tr.e.importer.linux_perf.Parser;function ExynosParser(importer){Parser.call(this,importer);importer.registerEventHandler('exynos_busfreq_target_int',ExynosParser.prototype.busfreqTargetIntEvent.bind(this));importer.registerEventHandler('exynos_busfreq_target_mif',ExynosParser.prototype.busfreqTargetMifEvent.bind(this));importer.registerEventHandler('exynos_page_flip_state',ExynosParser.prototype.pageFlipStateEvent.bind(this));}
+ExynosParser.prototype={__proto__:Parser.prototype,exynosBusfreqSample(name,ts,frequency){const targetCpu=this.importer.getOrCreateCpu(0);const counter=targetCpu.getOrCreateCounter('',name);if(counter.numSeries===0){counter.addSeries(new tr.model.CounterSeries('frequency',ColorScheme.getColorIdForGeneralPurposeString(counter.name+'.'+'frequency')));}
+counter.series.forEach(function(series){series.addCounterSample(ts,frequency);});},busfreqTargetIntEvent(eventName,cpuNumber,pid,ts,eventBase){const event=/frequency=(\d+)/.exec(eventBase.details);if(!event)return false;this.exynosBusfreqSample('INT Frequency',ts,parseInt(event[1]));return true;},busfreqTargetMifEvent(eventName,cpuNumber,pid,ts,eventBase){const event=/frequency=(\d+)/.exec(eventBase.details);if(!event)return false;this.exynosBusfreqSample('MIF Frequency',ts,parseInt(event[1]));return true;},exynosPageFlipStateOpenSlice(ts,pipe,fb,state){const kthread=this.importer.getOrCreatePseudoThread('exynos_flip_state (pipe:'+pipe+', fb:'+fb+')');kthread.openSliceTS=ts;kthread.openSlice=state;},exynosPageFlipStateCloseSlice(ts,pipe,fb,args){const kthread=this.importer.getOrCreatePseudoThread('exynos_flip_state (pipe:'+pipe+', fb:'+fb+')');if(kthread.openSlice){const slice=new tr.model.ThreadSlice('',kthread.openSlice,ColorScheme.getColorIdForGeneralPurposeString(kthread.openSlice),kthread.openSliceTS,args,ts-kthread.openSliceTS);kthread.thread.sliceGroup.pushSlice(slice);}
+kthread.openSlice=undefined;},pageFlipStateEvent(eventName,cpuNumber,pid,ts,eventBase){const event=/pipe=(\d+), fb=(\d+), state=(.*)/.exec(eventBase.details);if(!event)return false;const pipe=parseInt(event[1]);const fb=parseInt(event[2]);const state=event[3];this.exynosPageFlipStateCloseSlice(ts,pipe,fb,{pipe,fb});if(state!=='flipped'){this.exynosPageFlipStateOpenSlice(ts,pipe,fb,state);}
+return true;}};Parser.register(ExynosParser);return{ExynosParser,};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){const Parser=tr.e.importer.linux_perf.Parser;function GestureParser(importer){Parser.call(this,importer);importer.registerEventHandler('tracing_mark_write:log',GestureParser.prototype.logEvent.bind(this));importer.registerEventHandler('tracing_mark_write:SyncInterpret',GestureParser.prototype.syncEvent.bind(this));importer.registerEventHandler('tracing_mark_write:HandleTimer',GestureParser.prototype.timerEvent.bind(this));}
+GestureParser.prototype={__proto__:Parser.prototype,gestureOpenSlice(title,ts,opt_args){const thread=this.importer.getOrCreatePseudoThread('gesture').thread;thread.sliceGroup.beginSlice('touchpad_gesture',title,ts,opt_args);},gestureCloseSlice(title,ts){const thread=this.importer.getOrCreatePseudoThread('gesture').thread;if(thread.sliceGroup.openSliceCount){const slice=thread.sliceGroup.mostRecentlyOpenedPartialSlice;if(slice.title!==title){this.importer.model.importWarning({type:'title_match_error',message:'Titles do not match. Title is '+
+slice.title+' in openSlice, and is '+
+title+' in endSlice'});}else{thread.sliceGroup.endSlice(ts);}}},logEvent(eventName,cpuNumber,pid,ts,eventBase){const innerEvent=/^\s*(\w+):\s*(\w+)$/.exec(eventBase.details);switch(innerEvent[1]){case'start':this.gestureOpenSlice('GestureLog',ts,{name:innerEvent[2]});break;case'end':this.gestureCloseSlice('GestureLog',ts);}
+return true;},syncEvent(eventName,cpuNumber,pid,ts,eventBase){const innerEvent=/^\s*(\w+):\s*(\w+)$/.exec(eventBase.details);switch(innerEvent[1]){case'start':this.gestureOpenSlice('SyncInterpret',ts,{interpreter:innerEvent[2]});break;case'end':this.gestureCloseSlice('SyncInterpret',ts);}
+return true;},timerEvent(eventName,cpuNumber,pid,ts,eventBase){const innerEvent=/^\s*(\w+):\s*(\w+)$/.exec(eventBase.details);switch(innerEvent[1]){case'start':this.gestureOpenSlice('HandleTimer',ts,{interpreter:innerEvent[2]});break;case'end':this.gestureCloseSlice('HandleTimer',ts);}
+return true;}};Parser.register(GestureParser);return{GestureParser,};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){const ColorScheme=tr.b.ColorScheme;const Parser=tr.e.importer.linux_perf.Parser;function I2cParser(importer){Parser.call(this,importer);importer.registerEventHandler('i2c_write',I2cParser.prototype.i2cWriteEvent.bind(this));importer.registerEventHandler('i2c_read',I2cParser.prototype.i2cReadEvent.bind(this));importer.registerEventHandler('i2c_reply',I2cParser.prototype.i2cReplyEvent.bind(this));importer.registerEventHandler('i2c_result',I2cParser.prototype.i2cResultEvent.bind(this));}
+const i2cWriteReplyRE=new RegExp('i2c-(\\d+) #(\\d+) a=([\\da-fA-F]+) f=([\\da-fA-F]+) l=(\\d+) '+'(\\[[\\da-fA-F\\-]+\\])');const i2cReadRE=/i2c-(\d+) #(\d+) a=([\da-fA-F]+) f=([\da-fA-F]+) l=(\d+)/;const i2cResultRE=/i2c-(\d+) n=(\d+) ret=(\d+)/;I2cParser.prototype={__proto__:Parser.prototype,i2cWriteEvent(eventName,cpuNumber,pid,ts,eventBase){const event=i2cWriteReplyRE.exec(eventBase.details);if(!event)return false;const adapterNumber=parseInt(event[1]);const messageNumber=event[2];const address=event[3];const flags=event[4];const dataLength=event[5];const data=event[6];const thread=this.importer.getOrCreatePseudoThread('i2c adapter '+adapterNumber);pushLastSliceIfNeeded(thread,event[1],ts);thread.lastEntryTitle='i2c write';thread.lastEntryTs=ts;thread.lastEntryArgs={'Message number':messageNumber,'Address':address,'Flags':flags,'Data Length':dataLength,'Data':data};return true;},i2cReadEvent(eventName,cpuNumber,pid,ts,eventBase){const event=i2cReadRE.exec(eventBase.details);if(!event)return false;const adapterNumber=parseInt(event[1]);const messageNumber=event[2];const address=event[3];const flags=event[4];const dataLength=event[5];const thread=this.importer.getOrCreatePseudoThread('i2c adapter '+adapterNumber);pushLastSliceIfNeeded(thread,event[1],ts);thread.lastEntryTitle='i2c read';thread.lastEntryTs=ts;thread.lastEntryArgs={'Message number':messageNumber,'Address':address,'Flags':flags,'Data Length':dataLength};return true;},i2cReplyEvent(eventName,cpuNumber,pid,ts,eventBase){const event=i2cWriteReplyRE.exec(eventBase.details);if(!event)return false;const adapterNumber=parseInt(event[1]);const messageNumber=event[2];const address=event[3];const flags=event[4];const dataLength=event[5];const data=event[6];const thread=this.importer.getOrCreatePseudoThread('i2c adapter '+adapterNumber);pushLastSliceIfNeeded(thread,event[1],ts);thread.lastEntryTitle='i2c reply';thread.lastEntryTs=ts;thread.lastEntryArgs={'Message number':messageNumber,'Address':address,'Flags':flags,'Data Length':dataLength,'Data':data};return true;},i2cResultEvent(eventName,cpuNumber,pid,ts,eventBase){const event=i2cResultRE.exec(eventBase.details);if(!event)return false;const adapterNumber=parseInt(event[1]);const numMessages=event[2];const ret=event[3];const thread=this.importer.getOrCreatePseudoThread('i2c adapter '+adapterNumber);const args=thread.lastEntryArgs;if(args!==undefined){args['Number of messages']=numMessages;args.Return=ret;}
+pushLastSliceIfNeeded(thread,event[1],ts);thread.lastEntryTitle=undefined;thread.lastEntryTs=undefined;thread.lastEntryArgs=undefined;return true;},};function pushLastSliceIfNeeded(thread,id,currentTs){if(thread.lastEntryTs!==undefined){const duration=currentTs-thread.lastEntryTs;const slice=new tr.model.ThreadSlice('',thread.lastEntryTitle,ColorScheme.getColorIdForGeneralPurposeString(id),thread.lastEntryTs,thread.lastEntryArgs,duration);thread.thread.sliceGroup.pushSlice(slice);}}
+Parser.register(I2cParser);return{I2cParser,};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){const ColorScheme=tr.b.ColorScheme;const Parser=tr.e.importer.linux_perf.Parser;function I915Parser(importer){Parser.call(this,importer);importer.registerEventHandler('i915_gem_object_create',I915Parser.prototype.gemObjectCreateEvent.bind(this));importer.registerEventHandler('i915_gem_object_bind',I915Parser.prototype.gemObjectBindEvent.bind(this));importer.registerEventHandler('i915_gem_object_unbind',I915Parser.prototype.gemObjectBindEvent.bind(this));importer.registerEventHandler('i915_gem_object_change_domain',I915Parser.prototype.gemObjectChangeDomainEvent.bind(this));importer.registerEventHandler('i915_gem_object_pread',I915Parser.prototype.gemObjectPreadWriteEvent.bind(this));importer.registerEventHandler('i915_gem_object_pwrite',I915Parser.prototype.gemObjectPreadWriteEvent.bind(this));importer.registerEventHandler('i915_gem_object_fault',I915Parser.prototype.gemObjectFaultEvent.bind(this));importer.registerEventHandler('i915_gem_object_clflush',I915Parser.prototype.gemObjectDestroyEvent.bind(this));importer.registerEventHandler('i915_gem_object_destroy',I915Parser.prototype.gemObjectDestroyEvent.bind(this));importer.registerEventHandler('i915_gem_ring_dispatch',I915Parser.prototype.gemRingDispatchEvent.bind(this));importer.registerEventHandler('i915_gem_ring_flush',I915Parser.prototype.gemRingFlushEvent.bind(this));importer.registerEventHandler('i915_gem_request',I915Parser.prototype.gemRequestEvent.bind(this));importer.registerEventHandler('i915_gem_request_add',I915Parser.prototype.gemRequestEvent.bind(this));importer.registerEventHandler('i915_gem_request_complete',I915Parser.prototype.gemRequestEvent.bind(this));importer.registerEventHandler('i915_gem_request_retire',I915Parser.prototype.gemRequestEvent.bind(this));importer.registerEventHandler('i915_gem_request_wait_begin',I915Parser.prototype.gemRequestEvent.bind(this));importer.registerEventHandler('i915_gem_request_wait_end',I915Parser.prototype.gemRequestEvent.bind(this));importer.registerEventHandler('i915_gem_ring_wait_begin',I915Parser.prototype.gemRingWaitEvent.bind(this));importer.registerEventHandler('i915_gem_ring_wait_end',I915Parser.prototype.gemRingWaitEvent.bind(this));importer.registerEventHandler('i915_reg_rw',I915Parser.prototype.regRWEvent.bind(this));importer.registerEventHandler('i915_flip_request',I915Parser.prototype.flipEvent.bind(this));importer.registerEventHandler('i915_flip_complete',I915Parser.prototype.flipEvent.bind(this));importer.registerEventHandler('intel_gpu_freq_change',I915Parser.prototype.gpuFrequency.bind(this));}
+I915Parser.prototype={__proto__:Parser.prototype,i915FlipOpenSlice(ts,obj,plane){const kthread=this.importer.getOrCreatePseudoThread('i915_flip');kthread.openSliceTS=ts;kthread.openSlice='flip:'+obj+'/'+plane;},i915FlipCloseSlice(ts,args){const kthread=this.importer.getOrCreatePseudoThread('i915_flip');if(kthread.openSlice){const slice=new tr.model.ThreadSlice('',kthread.openSlice,ColorScheme.getColorIdForGeneralPurposeString(kthread.openSlice),kthread.openSliceTS,args,ts-kthread.openSliceTS);kthread.thread.sliceGroup.pushSlice(slice);}
+kthread.openSlice=undefined;},i915GemObjectSlice(ts,eventName,obj,args){const kthread=this.importer.getOrCreatePseudoThread('i915_gem');kthread.openSlice=eventName+':'+obj;const slice=new tr.model.ThreadSlice('',kthread.openSlice,ColorScheme.getColorIdForGeneralPurposeString(kthread.openSlice),ts,args,0);kthread.thread.sliceGroup.pushSlice(slice);},i915GemRingSlice(ts,eventName,dev,ring,args){const kthread=this.importer.getOrCreatePseudoThread('i915_gem_ring');kthread.openSlice=eventName+':'+dev+'.'+ring;const slice=new tr.model.ThreadSlice('',kthread.openSlice,ColorScheme.getColorIdForGeneralPurposeString(kthread.openSlice),ts,args,0);kthread.thread.sliceGroup.pushSlice(slice);},i915RegSlice(ts,eventName,reg,args){const kthread=this.importer.getOrCreatePseudoThread('i915_reg');kthread.openSlice=eventName+':'+reg;const slice=new tr.model.ThreadSlice('',kthread.openSlice,ColorScheme.getColorIdForGeneralPurposeString(kthread.openSlice),ts,args,0);kthread.thread.sliceGroup.pushSlice(slice);},i915FreqChangeSlice(ts,eventName,args){const kthread=this.importer.getOrCreatePseudoThread('i915_gpu_freq');kthread.openSlice=eventName;const slice=new tr.model.ThreadSlice('',kthread.openSlice,ColorScheme.getColorIdForGeneralPurposeString(kthread.openSlice),ts,args,0);kthread.thread.sliceGroup.pushSlice(slice);},gemObjectCreateEvent(eventName,cpuNumber,pid,ts,eventBase){const event=/obj=(\w+), size=(\d+)/.exec(eventBase.details);if(!event)return false;const obj=event[1];const size=parseInt(event[2]);this.i915GemObjectSlice(ts,eventName,obj,{obj,size});return true;},gemObjectBindEvent(eventName,cpuNumber,pid,ts,eventBase){const event=/obj=(\w+), offset=(\w+), size=(\d+)/.exec(eventBase.details);if(!event)return false;const obj=event[1];const offset=event[2];const size=parseInt(event[3]);this.i915ObjectGemSlice(ts,eventName+':'+obj,{obj,offset,size});return true;},gemObjectChangeDomainEvent(eventName,cpuNumber,pid,ts,eventBase){const event=/obj=(\w+), read=(\w+=>\w+), write=(\w+=>\w+)/.exec(eventBase.details);if(!event)return false;const obj=event[1];const read=event[2];const write=event[3];this.i915GemObjectSlice(ts,eventName,obj,{obj,read,write});return true;},gemObjectPreadWriteEvent(eventName,cpuNumber,pid,ts,eventBase){const event=/obj=(\w+), offset=(\d+), len=(\d+)/.exec(eventBase.details);if(!event)return false;const obj=event[1];const offset=parseInt(event[2]);const len=parseInt(event[3]);this.i915GemObjectSlice(ts,eventName,obj,{obj,offset,len});return true;},gemObjectFaultEvent(eventName,cpuNumber,pid,ts,eventBase){const event=/obj=(\w+), (\w+) index=(\d+)/.exec(eventBase.details);if(!event)return false;const obj=event[1];const type=event[2];const index=parseInt(event[3]);this.i915GemObjectSlice(ts,eventName,obj,{obj,type,index});return true;},gemObjectDestroyEvent(eventName,cpuNumber,pid,ts,eventBase){const event=/obj=(\w+)/.exec(eventBase.details);if(!event)return false;const obj=event[1];this.i915GemObjectSlice(ts,eventName,obj,{obj});return true;},gemRingDispatchEvent(eventName,cpuNumber,pid,ts,eventBase){const event=/dev=(\d+), ring=(\d+), seqno=(\d+)/.exec(eventBase.details);if(!event)return false;const dev=parseInt(event[1]);const ring=parseInt(event[2]);const seqno=parseInt(event[3]);this.i915GemRingSlice(ts,eventName,dev,ring,{dev,ring,seqno});return true;},gemRingFlushEvent(eventName,cpuNumber,pid,ts,eventBase){const event=/dev=(\d+), ring=(\w+), invalidate=(\w+), flush=(\w+)/.exec(eventBase.details);if(!event)return false;const dev=parseInt(event[1]);const ring=parseInt(event[2]);const invalidate=event[3];const flush=event[4];this.i915GemRingSlice(ts,eventName,dev,ring,{dev,ring,invalidate,flush});return true;},gemRequestEvent(eventName,cpuNumber,pid,ts,eventBase){const event=/dev=(\d+), ring=(\d+), seqno=(\d+)/.exec(eventBase.details);if(!event)return false;const dev=parseInt(event[1]);const ring=parseInt(event[2]);const seqno=parseInt(event[3]);this.i915GemRingSlice(ts,eventName,dev,ring,{dev,ring,seqno});return true;},gemRingWaitEvent(eventName,cpuNumber,pid,ts,eventBase){const event=/dev=(\d+), ring=(\d+)/.exec(eventBase.details);if(!event)return false;const dev=parseInt(event[1]);const ring=parseInt(event[2]);this.i915GemRingSlice(ts,eventName,dev,ring,{dev,ring});return true;},regRWEvent(eventName,cpuNumber,pid,ts,eventBase){const event=/(\w+) reg=(\w+), len=(\d+), val=(\(\w+, \w+\))/.exec(eventBase.details);if(!event)return false;const rw=event[1];const reg=event[2];const len=event[3];const data=event[3];this.i915RegSlice(ts,rw,reg,{rw,reg,len,data});return true;},flipEvent(eventName,cpuNumber,pid,ts,eventBase){const event=/plane=(\d+), obj=(\w+)/.exec(eventBase.details);if(!event)return false;const plane=parseInt(event[1]);const obj=event[2];if(eventName==='i915_flip_request'){this.i915FlipOpenSlice(ts,obj,plane);}else{this.i915FlipCloseSlice(ts,{obj,plane});}
+return true;},gpuFrequency(eventName,cpuNumver,pid,ts,eventBase){const event=/new_freq=(\d+)/.exec(eventBase.details);if(!event)return false;const freq=parseInt(event[1]);this.i915FreqChangeSlice(ts,eventName,{freq});return true;}};Parser.register(I915Parser);return{I915Parser,};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){const ColorScheme=tr.b.ColorScheme;const Parser=tr.e.importer.linux_perf.Parser;function IonHeapParser(importer){Parser.call(this,importer);importer.registerEventHandler('ion_heap_shrink',IonHeapParser.prototype.traceIonHeapShrink.bind(this));importer.registerEventHandler('ion_heap_grow',IonHeapParser.prototype.traceIonHeapGrow.bind(this));this.model_=importer.model_;}
+const TestExports={};const ionHeapRE=new RegExp('heap_name=(\\S+), len=(\\d+), total_allocated=(\\d+)');TestExports.ionHeapRE=ionHeapRE;IonHeapParser.prototype={__proto__:Parser.prototype,traceIonHeapShrink(eventName,cpuNumber,pid,ts,eventBase,threadName){const event=ionHeapRE.exec(eventBase.details);if(!event)return false;const name=event[1];const len=parseInt(event[2]);const totalAllocated=parseInt(event[3]);const ionHeap=totalAllocated+len;const ctr=this.model_.kernel.getOrCreateCounter(null,name+' ion heap');if(ctr.numSeries===0){ctr.addSeries(new tr.model.CounterSeries('value',ColorScheme.getColorIdForGeneralPurposeString(ctr.name+'.'+'value')));}
+ctr.series.forEach(function(series){series.addCounterSample(ts,ionHeap);});return true;},traceIonHeapGrow(eventName,cpuNumber,pid,ts,eventBase,threadName){const event=ionHeapRE.exec(eventBase.details);if(!event)return false;const name=event[1];const len=parseInt(event[2]);const totalAllocated=parseInt(event[3]);const ionHeap=totalAllocated+len;const ctr=this.model_.kernel.getOrCreateCounter(null,name+' ion heap');if(ctr.numSeries===0){ctr.addSeries(new tr.model.CounterSeries('value',ColorScheme.getColorIdForGeneralPurposeString(ctr.name+'.'+'value')));}
+ctr.series.forEach(function(series){series.addCounterSample(ts,ionHeap);});return true;}};Parser.register(IonHeapParser);return{IonHeapParser,_IonHeapParserTestExports:TestExports};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){const ColorScheme=tr.b.ColorScheme;const Parser=tr.e.importer.linux_perf.Parser;function IrqParser(importer){Parser.call(this,importer);importer.registerEventHandler('irq_handler_entry',IrqParser.prototype.irqHandlerEntryEvent.bind(this));importer.registerEventHandler('irq_handler_exit',IrqParser.prototype.irqHandlerExitEvent.bind(this));importer.registerEventHandler('softirq_raise',IrqParser.prototype.softirqRaiseEvent.bind(this));importer.registerEventHandler('softirq_entry',IrqParser.prototype.softirqEntryEvent.bind(this));importer.registerEventHandler('softirq_exit',IrqParser.prototype.softirqExitEvent.bind(this));importer.registerEventHandler('ipi_entry',IrqParser.prototype.ipiEntryEvent.bind(this));importer.registerEventHandler('ipi_exit',IrqParser.prototype.ipiExitEvent.bind(this));importer.registerEventHandler('preempt_disable',IrqParser.prototype.preemptStartEvent.bind(this));importer.registerEventHandler('preempt_enable',IrqParser.prototype.preemptEndEvent.bind(this));importer.registerEventHandler('irq_disable',IrqParser.prototype.irqoffStartEvent.bind(this));importer.registerEventHandler('irq_enable',IrqParser.prototype.irqoffEndEvent.bind(this));}
+const irqHandlerEntryRE=/irq=(\d+) name=(.+)/;const irqHandlerExitRE=/irq=(\d+) ret=(.+)/;const softirqRE=/vec=(\d+) \[action=(.+)\]/;const ipiHandlerExitRE=/\((.+)\)/;const preemptirqRE=/caller=(.+) parent=(.+)/;IrqParser.prototype={__proto__:Parser.prototype,irqHandlerEntryEvent(eventName,cpuNumber,pid,ts,eventBase){const event=irqHandlerEntryRE.exec(eventBase.details);if(!event)return false;const irq=parseInt(event[1]);const name=event[2];const thread=this.importer.getOrCreatePseudoThread('irqs cpu '+cpuNumber);thread.lastEntryTs=ts;thread.irqName=name;return true;},irqHandlerExitEvent(eventName,cpuNumber,pid,ts,eventBase){const event=irqHandlerExitRE.exec(eventBase.details);if(!event)return false;const irq=parseInt(event[1]);const ret=event[2];const thread=this.importer.getOrCreatePseudoThread('irqs cpu '+cpuNumber);if(thread.lastEntryTs!==undefined){const duration=ts-thread.lastEntryTs;const slice=new tr.model.ThreadSlice('','IRQ ('+thread.irqName+')',ColorScheme.getColorIdForGeneralPurposeString(event[1]),thread.lastEntryTs,{ret},duration);thread.thread.sliceGroup.pushSlice(slice);}
+thread.lastEntryTs=undefined;thread.irqName=undefined;return true;},softirqRaiseEvent(eventName,cpuNumber,pid,ts,eventBase){return true;},softirqEntryEvent(eventName,cpuNumber,pid,ts,eventBase){const event=softirqRE.exec(eventBase.details);if(!event)return false;const action=event[2];const thread=this.importer.getOrCreatePseudoThread('softirq cpu '+cpuNumber);thread.lastEntryTs=ts;return true;},softirqExitEvent(eventName,cpuNumber,pid,ts,eventBase){const event=softirqRE.exec(eventBase.details);if(!event)return false;const vec=parseInt(event[1]);const action=event[2];const thread=this.importer.getOrCreatePseudoThread('softirq cpu '+cpuNumber);if(thread.lastEntryTs!==undefined){const duration=ts-thread.lastEntryTs;const slice=new tr.model.ThreadSlice('',action,ColorScheme.getColorIdForGeneralPurposeString(event[1]),thread.lastEntryTs,{vec},duration);thread.thread.sliceGroup.pushSlice(slice);}
+thread.lastEntryTs=undefined;return true;},ipiEntryEvent(eventName,cpuNumber,pid,ts,eventBase){const thread=this.importer.getOrCreatePseudoThread('irqs cpu '+cpuNumber);thread.lastEntryTs=ts;return true;},ipiExitEvent(eventName,cpuNumber,pid,ts,eventBase){const event=ipiHandlerExitRE.exec(eventBase.details);if(!event)return false;const ipiName=event[1];const thread=this.importer.getOrCreatePseudoThread('irqs cpu '+cpuNumber);if(thread.lastEntryTs!==undefined){const duration=ts-thread.lastEntryTs;const slice=new tr.model.ThreadSlice('','IPI ('+ipiName+')',ColorScheme.getColorIdForGeneralPurposeString(ipiName),thread.lastEntryTs,{},duration);thread.thread.sliceGroup.pushSlice(slice);}
+thread.lastEntryTs=undefined;return true;},preemptStartEvent(eventName,cpuNumber,pid,ts,eventBase){const event=preemptirqRE.exec(eventBase.details);if(!event)return false;const thread=this.importer.getOrCreatePseudoThread('preempt cpu '+cpuNumber);thread.lastEntryTs=ts;thread.preemptStartCaller=event[1];thread.preemptStartParent=event[2];return true;},preemptEndEvent(eventName,cpuNumber,pid,ts,eventBase){const event=preemptirqRE.exec(eventBase.details);if(!event)return false;const thread=this.importer.getOrCreatePseudoThread('preempt cpu '+cpuNumber);thread.preemptEndCaller=event[1];thread.preemptEndParent=event[2];if(thread.lastEntryTs!==undefined){const duration=ts-thread.lastEntryTs;const slice=new tr.model.ThreadSlice('',thread.preemptStartParent+': '+thread.preemptStartCaller,ColorScheme.getColorIdForGeneralPurposeString(thread.preemptEndCaller),thread.lastEntryTs,{},duration);thread.thread.sliceGroup.pushSlice(slice);}
+thread.lastEntryTs=undefined;return true;},irqoffStartEvent(eventName,cpuNumber,pid,ts,eventBase){const event=preemptirqRE.exec(eventBase.details);if(!event)return false;const thread=this.importer.getOrCreatePseudoThread('irqoff cpu '+cpuNumber);thread.lastEntryTs=ts;thread.irqoffStartCaller=event[1];thread.irqoffStartParent=event[2];return true;},irqoffEndEvent(eventName,cpuNumber,pid,ts,eventBase){const event=preemptirqRE.exec(eventBase.details);if(!event)return false;const thread=this.importer.getOrCreatePseudoThread('irqoff cpu '+cpuNumber);thread.irqoffEndCaller=event[1];thread.irqoffEndParent=event[2];if(thread.lastEntryTs!==undefined){const duration=ts-thread.lastEntryTs;const slice=new tr.model.ThreadSlice('',thread.irqoffStartParent+': '+thread.irqoffStartCaller,ColorScheme.getColorIdForGeneralPurposeString(thread.irqoffEndCaller),thread.lastEntryTs,{},duration);thread.thread.sliceGroup.pushSlice(slice);}
+thread.lastEntryTs=undefined;return true;}};Parser.register(IrqParser);return{IrqParser,};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){const LinuxPerfParser=tr.e.importer.linux_perf.Parser;function KernelFuncParser(importer){LinuxPerfParser.call(this,importer);importer.registerEventHandler('graph_ent',KernelFuncParser.prototype.traceKernelFuncEnterEvent.bind(this));importer.registerEventHandler('graph_ret',KernelFuncParser.prototype.traceKernelFuncReturnEvent.bind(this));this.model_=importer.model_;this.ppids_={};}
+const TestExports={};const funcEnterRE=new RegExp('func=(.+)');TestExports.funcEnterRE=funcEnterRE;KernelFuncParser.prototype={__proto__:LinuxPerfParser.prototype,traceKernelFuncEnterEvent(eventName,cpuNumber,pid,ts,eventBase){const eventData=funcEnterRE.exec(eventBase.details);if(!eventData)return false;if(eventBase.tgid===undefined){return false;}
+const tgid=parseInt(eventBase.tgid);const name=eventData[1];const thread=this.model_.getOrCreateProcess(tgid).getOrCreateThread(pid);thread.name=eventBase.threadName;const slices=thread.kernelSliceGroup;if(!slices.isTimestampValidForBeginOrEnd(ts)){this.model_.importWarning({type:'parse_error',message:'Timestamps are moving backward.'});return false;}
+const slice=slices.beginSlice(null,name,ts,{});return true;},traceKernelFuncReturnEvent(eventName,cpuNumber,pid,ts,eventBase){if(eventBase.tgid===undefined){return false;}
+const tgid=parseInt(eventBase.tgid);const thread=this.model_.getOrCreateProcess(tgid).getOrCreateThread(pid);thread.name=eventBase.threadName;const slices=thread.kernelSliceGroup;if(!slices.isTimestampValidForBeginOrEnd(ts)){this.model_.importWarning({type:'parse_error',message:'Timestamps are moving backward.'});return false;}
+if(slices.openSliceCount>0){slices.endSlice(ts);}
+return true;}};LinuxPerfParser.register(KernelFuncParser);return{KernelFuncParser,};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){const ColorScheme=tr.b.ColorScheme;const Parser=tr.e.importer.linux_perf.Parser;function MaliParser(importer){Parser.call(this,importer);importer.registerEventHandler('mali_dvfs_event',MaliParser.prototype.dvfsEventEvent.bind(this));importer.registerEventHandler('mali_dvfs_set_clock',MaliParser.prototype.dvfsSetClockEvent.bind(this));importer.registerEventHandler('mali_dvfs_set_voltage',MaliParser.prototype.dvfsSetVoltageEvent.bind(this));this.addJMCounter('mali_hwc_MESSAGES_SENT','Messages Sent');this.addJMCounter('mali_hwc_MESSAGES_RECEIVED','Messages Received');this.addJMCycles('mali_hwc_GPU_ACTIVE','GPU Active');this.addJMCycles('mali_hwc_IRQ_ACTIVE','IRQ Active');for(let i=0;i<7;i++){const jobStr='JS'+i;const jobHWCStr='mali_hwc_'+jobStr;this.addJMCounter(jobHWCStr+'_JOBS',jobStr+' Jobs');this.addJMCounter(jobHWCStr+'_TASKS',jobStr+' Tasks');this.addJMCycles(jobHWCStr+'_ACTIVE',jobStr+' Active');this.addJMCycles(jobHWCStr+'_WAIT_READ',jobStr+' Wait Read');this.addJMCycles(jobHWCStr+'_WAIT_ISSUE',jobStr+' Wait Issue');this.addJMCycles(jobHWCStr+'_WAIT_DEPEND',jobStr+' Wait Depend');this.addJMCycles(jobHWCStr+'_WAIT_FINISH',jobStr+' Wait Finish');}
+this.addTilerCounter('mali_hwc_TRIANGLES','Triangles');this.addTilerCounter('mali_hwc_QUADS','Quads');this.addTilerCounter('mali_hwc_POLYGONS','Polygons');this.addTilerCounter('mali_hwc_POINTS','Points');this.addTilerCounter('mali_hwc_LINES','Lines');this.addTilerCounter('mali_hwc_VCACHE_HIT','VCache Hit');this.addTilerCounter('mali_hwc_VCACHE_MISS','VCache Miss');this.addTilerCounter('mali_hwc_FRONT_FACING','Front Facing');this.addTilerCounter('mali_hwc_BACK_FACING','Back Facing');this.addTilerCounter('mali_hwc_PRIM_VISIBLE','Prim Visible');this.addTilerCounter('mali_hwc_PRIM_CULLED','Prim Culled');this.addTilerCounter('mali_hwc_PRIM_CLIPPED','Prim Clipped');this.addTilerCounter('mali_hwc_WRBUF_HIT','Wrbuf Hit');this.addTilerCounter('mali_hwc_WRBUF_MISS','Wrbuf Miss');this.addTilerCounter('mali_hwc_WRBUF_LINE','Wrbuf Line');this.addTilerCounter('mali_hwc_WRBUF_PARTIAL','Wrbuf Partial');this.addTilerCounter('mali_hwc_WRBUF_STALL','Wrbuf Stall');this.addTilerCycles('mali_hwc_ACTIVE','Tiler Active');this.addTilerCycles('mali_hwc_INDEX_WAIT','Index Wait');this.addTilerCycles('mali_hwc_INDEX_RANGE_WAIT','Index Range Wait');this.addTilerCycles('mali_hwc_VERTEX_WAIT','Vertex Wait');this.addTilerCycles('mali_hwc_PCACHE_WAIT','Pcache Wait');this.addTilerCycles('mali_hwc_WRBUF_WAIT','Wrbuf Wait');this.addTilerCycles('mali_hwc_BUS_READ','Bus Read');this.addTilerCycles('mali_hwc_BUS_WRITE','Bus Write');this.addTilerCycles('mali_hwc_TILER_UTLB_STALL','Tiler UTLB Stall');this.addTilerCycles('mali_hwc_TILER_UTLB_HIT','Tiler UTLB Hit');this.addFragCycles('mali_hwc_FRAG_ACTIVE','Active');this.addFragCounter('mali_hwc_FRAG_PRIMATIVES','Primitives');this.addFragCounter('mali_hwc_FRAG_PRIMATIVES_DROPPED','Primitives Dropped');this.addFragCycles('mali_hwc_FRAG_CYCLE_DESC','Descriptor Processing');this.addFragCycles('mali_hwc_FRAG_CYCLES_PLR','PLR Processing??');this.addFragCycles('mali_hwc_FRAG_CYCLES_VERT','Vertex Processing');this.addFragCycles('mali_hwc_FRAG_CYCLES_TRISETUP','Triangle Setup');this.addFragCycles('mali_hwc_FRAG_CYCLES_RAST','Rasterization???');this.addFragCounter('mali_hwc_FRAG_THREADS','Threads');this.addFragCounter('mali_hwc_FRAG_DUMMY_THREADS','Dummy Threads');this.addFragCounter('mali_hwc_FRAG_QUADS_RAST','Quads Rast');this.addFragCounter('mali_hwc_FRAG_QUADS_EZS_TEST','Quads EZS Test');this.addFragCounter('mali_hwc_FRAG_QUADS_EZS_KILLED','Quads EZS Killed');this.addFragCounter('mali_hwc_FRAG_QUADS_LZS_TEST','Quads LZS Test');this.addFragCounter('mali_hwc_FRAG_QUADS_LZS_KILLED','Quads LZS Killed');this.addFragCycles('mali_hwc_FRAG_CYCLE_NO_TILE','No Tiles');this.addFragCounter('mali_hwc_FRAG_NUM_TILES','Tiles');this.addFragCounter('mali_hwc_FRAG_TRANS_ELIM','Transactions Eliminated');this.addComputeCycles('mali_hwc_COMPUTE_ACTIVE','Active');this.addComputeCounter('mali_hwc_COMPUTE_TASKS','Tasks');this.addComputeCounter('mali_hwc_COMPUTE_THREADS','Threads Started');this.addComputeCycles('mali_hwc_COMPUTE_CYCLES_DESC','Waiting for Descriptors');this.addTripipeCycles('mali_hwc_TRIPIPE_ACTIVE','Active');this.addArithCounter('mali_hwc_ARITH_WORDS','Instructions (/Pipes)');this.addArithCycles('mali_hwc_ARITH_CYCLES_REG','Reg scheduling stalls (/Pipes)');this.addArithCycles('mali_hwc_ARITH_CYCLES_L0','L0 cache miss stalls (/Pipes)');this.addArithCounter('mali_hwc_ARITH_FRAG_DEPEND','Frag dep check failures (/Pipes)');this.addLSCounter('mali_hwc_LS_WORDS','Instruction Words Completed');this.addLSCounter('mali_hwc_LS_ISSUES','Full Pipeline Issues');this.addLSCounter('mali_hwc_LS_RESTARTS','Restarts (unpairable insts)');this.addLSCounter('mali_hwc_LS_REISSUES_MISS','Pipeline reissue (cache miss/uTLB)');this.addLSCounter('mali_hwc_LS_REISSUES_VD','Pipeline reissue (varying data)');this.addLSCounter('mali_hwc_LS_REISSUE_ATTRIB_MISS','Pipeline reissue (attribute cache miss)');this.addLSCounter('mali_hwc_LS_REISSUE_NO_WB','Writeback not used');this.addTexCounter('mali_hwc_TEX_WORDS','Words');this.addTexCounter('mali_hwc_TEX_BUBBLES','Bubbles');this.addTexCounter('mali_hwc_TEX_WORDS_L0','Words L0');this.addTexCounter('mali_hwc_TEX_WORDS_DESC','Words Desc');this.addTexCounter('mali_hwc_TEX_THREADS','Threads');this.addTexCounter('mali_hwc_TEX_RECIRC_FMISS','Recirc due to Full Miss');this.addTexCounter('mali_hwc_TEX_RECIRC_DESC','Recirc due to Desc Miss');this.addTexCounter('mali_hwc_TEX_RECIRC_MULTI','Recirc due to Multipass');this.addTexCounter('mali_hwc_TEX_RECIRC_PMISS','Recirc due to Partial Cache Miss');this.addTexCounter('mali_hwc_TEX_RECIRC_CONF','Recirc due to Cache Conflict');this.addLSCCounter('mali_hwc_LSC_READ_HITS','Read Hits');this.addLSCCounter('mali_hwc_LSC_READ_MISSES','Read Misses');this.addLSCCounter('mali_hwc_LSC_WRITE_HITS','Write Hits');this.addLSCCounter('mali_hwc_LSC_WRITE_MISSES','Write Misses');this.addLSCCounter('mali_hwc_LSC_ATOMIC_HITS','Atomic Hits');this.addLSCCounter('mali_hwc_LSC_ATOMIC_MISSES','Atomic Misses');this.addLSCCounter('mali_hwc_LSC_LINE_FETCHES','Line Fetches');this.addLSCCounter('mali_hwc_LSC_DIRTY_LINE','Dirty Lines');this.addLSCCounter('mali_hwc_LSC_SNOOPS','Snoops');this.addAXICounter('mali_hwc_AXI_TLB_STALL','Address channel stall');this.addAXICounter('mali_hwc_AXI_TLB_MISS','Cache Miss');this.addAXICounter('mali_hwc_AXI_TLB_TRANSACTION','Transactions');this.addAXICounter('mali_hwc_LS_TLB_MISS','LS Cache Miss');this.addAXICounter('mali_hwc_LS_TLB_HIT','LS Cache Hit');this.addAXICounter('mali_hwc_AXI_BEATS_READ','Read Beats');this.addAXICounter('mali_hwc_AXI_BEATS_WRITE','Write Beats');this.addMMUCounter('mali_hwc_MMU_TABLE_WALK','Page Table Walks');this.addMMUCounter('mali_hwc_MMU_REPLAY_MISS','Cache Miss from Replay Buffer');this.addMMUCounter('mali_hwc_MMU_REPLAY_FULL','Replay Buffer Full');this.addMMUCounter('mali_hwc_MMU_NEW_MISS','Cache Miss on New Request');this.addMMUCounter('mali_hwc_MMU_HIT','Cache Hit');this.addMMUCycles('mali_hwc_UTLB_STALL','UTLB Stalled');this.addMMUCycles('mali_hwc_UTLB_REPLAY_MISS','UTLB Replay Miss');this.addMMUCycles('mali_hwc_UTLB_REPLAY_FULL','UTLB Replay Full');this.addMMUCycles('mali_hwc_UTLB_NEW_MISS','UTLB New Miss');this.addMMUCycles('mali_hwc_UTLB_HIT','UTLB Hit');this.addL2Counter('mali_hwc_L2_READ_BEATS','Read Beats');this.addL2Counter('mali_hwc_L2_WRITE_BEATS','Write Beats');this.addL2Counter('mali_hwc_L2_ANY_LOOKUP','Any Lookup');this.addL2Counter('mali_hwc_L2_READ_LOOKUP','Read Lookup');this.addL2Counter('mali_hwc_L2_SREAD_LOOKUP','Shareable Read Lookup');this.addL2Counter('mali_hwc_L2_READ_REPLAY','Read Replayed');this.addL2Counter('mali_hwc_L2_READ_SNOOP','Read Snoop');this.addL2Counter('mali_hwc_L2_READ_HIT','Read Cache Hit');this.addL2Counter('mali_hwc_L2_CLEAN_MISS','CleanUnique Miss');this.addL2Counter('mali_hwc_L2_WRITE_LOOKUP','Write Lookup');this.addL2Counter('mali_hwc_L2_SWRITE_LOOKUP','Shareable Write Lookup');this.addL2Counter('mali_hwc_L2_WRITE_REPLAY','Write Replayed');this.addL2Counter('mali_hwc_L2_WRITE_SNOOP','Write Snoop');this.addL2Counter('mali_hwc_L2_WRITE_HIT','Write Cache Hit');this.addL2Counter('mali_hwc_L2_EXT_READ_FULL','ExtRD with BIU Full');this.addL2Counter('mali_hwc_L2_EXT_READ_HALF','ExtRD with BIU >1/2 Full');this.addL2Counter('mali_hwc_L2_EXT_WRITE_FULL','ExtWR with BIU Full');this.addL2Counter('mali_hwc_L2_EXT_WRITE_HALF','ExtWR with BIU >1/2 Full');this.addL2Counter('mali_hwc_L2_EXT_READ','External Read (ExtRD)');this.addL2Counter('mali_hwc_L2_EXT_READ_LINE','ExtRD (linefill)');this.addL2Counter('mali_hwc_L2_EXT_WRITE','External Write (ExtWR)');this.addL2Counter('mali_hwc_L2_EXT_WRITE_LINE','ExtWR (linefill)');this.addL2Counter('mali_hwc_L2_EXT_WRITE_SMALL','ExtWR (burst size <64B)');this.addL2Counter('mali_hwc_L2_EXT_BARRIER','External Barrier');this.addL2Counter('mali_hwc_L2_EXT_AR_STALL','Address Read stalls');this.addL2Counter('mali_hwc_L2_EXT_R_BUF_FULL','Response Buffer full stalls');this.addL2Counter('mali_hwc_L2_EXT_RD_BUF_FULL','Read Data Buffer full stalls');this.addL2Counter('mali_hwc_L2_EXT_R_RAW','RAW hazard stalls');this.addL2Counter('mali_hwc_L2_EXT_W_STALL','Write Data stalls');this.addL2Counter('mali_hwc_L2_EXT_W_BUF_FULL','Write Data Buffer full');this.addL2Counter('mali_hwc_L2_EXT_R_W_HAZARD','WAW or WAR hazard stalls');this.addL2Counter('mali_hwc_L2_TAG_HAZARD','Tag hazard replays');this.addL2Cycles('mali_hwc_L2_SNOOP_FULL','Snoop buffer full');this.addL2Cycles('mali_hwc_L2_REPLAY_FULL','Replay buffer full');importer.registerEventHandler('tracing_mark_write:mali_driver',MaliParser.prototype.maliDDKEvent.bind(this));importer.registerEventHandler('mali_job_systrace_event_start',MaliParser.prototype.maliJobEvent.bind(this));importer.registerEventHandler('mali_job_systrace_event_stop',MaliParser.prototype.maliJobEvent.bind(this));this.model_=importer.model_;this.deferredJobs_={};}
+MaliParser.prototype={__proto__:Parser.prototype,maliDDKOpenSlice(pid,tid,ts,func,blockinfo){const thread=this.importer.model_.getOrCreateProcess(pid).getOrCreateThread(tid);const funcArgs=/^([\w\d_]*)(?:\(\))?:?\s*(.*)$/.exec(func);thread.sliceGroup.beginSlice('gpu-driver',funcArgs[1],ts,{'args':funcArgs[2],blockinfo});},maliDDKCloseSlice(pid,tid,ts,args,blockinfo){const thread=this.importer.model_.getOrCreateProcess(pid).getOrCreateThread(tid);if(!thread.sliceGroup.openSliceCount){return;}
+thread.sliceGroup.endSlice(ts);},autoDetectLineRE(line){const lineREWithThread=/^\s*\(([\w\-]*)\)\s*(\w+):\s*([\w\\\/\.\-]*@\d*):?\s*(.*)$/;if(lineREWithThread.test(line)){return lineREWithThread;}
+const lineRENoThread=/^s*()(\w+):\s*([\w\\\/.\-]*):?\s*(.*)$/;if(lineRENoThread.test(line)){return lineRENoThread;}
+return null;},lineRE:null,maliDDKEvent(eventName,cpuNumber,pid,ts,eventBase){if(this.lineRE===null){this.lineRE=this.autoDetectLineRE(eventBase.details);if(this.lineRE===null)return false;}
+const maliEvent=this.lineRE.exec(eventBase.details);const tid=(maliEvent[1]===''?'mali':maliEvent[1]);switch(maliEvent[2]){case'cros_trace_print_enter':this.maliDDKOpenSlice(pid,tid,ts,maliEvent[4],maliEvent[3]);break;case'cros_trace_print_exit':this.maliDDKCloseSlice(pid,tid,ts,[],maliEvent[3]);}
+return true;},maliJobEvent(eventName,cpuNumber,pid,ts,eventBase){const jobEventRE=/^.*tracing_mark_write: (S|F)\|(\d+)\|(\w+)-job\|(\d+)\|(\d+)\|(\d+)\|(\d+)\|(\d+)\|([a-z0-9]+)\|(\d+)$/;const jobEvent=jobEventRE.exec(eventBase.details);if(!jobEvent){this.model_.importWarning({type:'parse_error',args:'unexpected mali_job_systrace_event_* event syntax'});return;}
+const jobType=jobEvent[3];const jobId=jobEvent[4];const thread=this.importer.model_.getOrCreateProcess(0).getOrCreateThread('mali:'+jobType);switch(jobEvent[1]){case'S':{const args={ctx:jobEvent[9],pid:parseInt(jobEvent[2],10),dep0:parseInt(jobEvent[5],10),dep1:parseInt(jobEvent[7],10)};if(thread.sliceGroup.openSliceCount){if(!(jobType in this.deferredJobs_)){this.deferredJobs_[jobType]=[];}
+this.deferredJobs_[jobType].push({id:jobId,args});}else{thread.sliceGroup.beginSlice(null,jobId,ts,args);}}break;case'F':{if(!thread.sliceGroup.openSliceCount){return;}
+if(thread.sliceGroup.mostRecentlyOpenedPartialSlice.title!==jobId){this.model_.importWarning({type:'invalid event nesting',message:'non-sequential jobs in same mali job slot'});}
+thread.sliceGroup.endSlice(ts);const deferredJobs=this.deferredJobs_[jobType];if(deferredJobs&&deferredJobs.length){const job=deferredJobs.shift();thread.sliceGroup.beginSlice(null,job.id,ts,job.args);}}break;}
+return true;},dvfsSample(counterName,seriesName,ts,s){const value=parseInt(s);const counter=this.model_.kernel.getOrCreateCounter('DVFS',counterName);if(counter.numSeries===0){counter.addSeries(new tr.model.CounterSeries(seriesName,ColorScheme.getColorIdForGeneralPurposeString(counter.name)));}
+counter.series.forEach(function(series){series.addCounterSample(ts,value);});},dvfsEventEvent(eventName,cpuNumber,pid,ts,eventBase){const event=/utilization=(\d+)/.exec(eventBase.details);if(!event)return false;this.dvfsSample('DVFS Utilization','utilization',ts,event[1]);return true;},dvfsSetClockEvent(eventName,cpuNumber,pid,ts,eventBase){const event=/frequency=(\d+)/.exec(eventBase.details);if(!event)return false;this.dvfsSample('DVFS Frequency','frequency',ts,event[1]);return true;},dvfsSetVoltageEvent(eventName,cpuNumber,pid,ts,eventBase){const event=/voltage=(\d+)/.exec(eventBase.details);if(!event)return false;this.dvfsSample('DVFS Voltage','voltage',ts,event[1]);return true;},hwcSample(cat,counterName,seriesName,ts,eventBase){const event=/val=(\d+)/.exec(eventBase.details);if(!event)return false;const value=parseInt(event[1]);const counter=this.model_.kernel.getOrCreateCounter(cat,counterName);if(counter.numSeries===0){counter.addSeries(new tr.model.CounterSeries(seriesName,ColorScheme.getColorIdForGeneralPurposeString(counter.name)));}
+counter.series.forEach(function(series){series.addCounterSample(ts,value);});return true;},jmSample(ctrName,seriesName,ts,eventBase){return this.hwcSample('mali:jm','JM: '+ctrName,seriesName,ts,eventBase);},addJMCounter(hwcEventName,hwcTitle){function handler(eventName,cpuNumber,pid,ts,eventBase){return this.jmSample(hwcTitle,'count',ts,eventBase);}
+this.importer.registerEventHandler(hwcEventName,handler.bind(this));},addJMCycles(hwcEventName,hwcTitle){function handler(eventName,cpuNumber,pid,ts,eventBase){return this.jmSample(hwcTitle,'cycles',ts,eventBase);}
+this.importer.registerEventHandler(hwcEventName,handler.bind(this));},tilerSample(ctrName,seriesName,ts,eventBase){return this.hwcSample('mali:tiler','Tiler: '+ctrName,seriesName,ts,eventBase);},addTilerCounter(hwcEventName,hwcTitle){function handler(eventName,cpuNumber,pid,ts,eventBase){return this.tilerSample(hwcTitle,'count',ts,eventBase);}
+this.importer.registerEventHandler(hwcEventName,handler.bind(this));},addTilerCycles(hwcEventName,hwcTitle){function handler(eventName,cpuNumber,pid,ts,eventBase){return this.tilerSample(hwcTitle,'cycles',ts,eventBase);}
+this.importer.registerEventHandler(hwcEventName,handler.bind(this));},fragSample(ctrName,seriesName,ts,eventBase){return this.hwcSample('mali:fragment','Fragment: '+ctrName,seriesName,ts,eventBase);},addFragCounter(hwcEventName,hwcTitle){function handler(eventName,cpuNumber,pid,ts,eventBase){return this.fragSample(hwcTitle,'count',ts,eventBase);}
+this.importer.registerEventHandler(hwcEventName,handler.bind(this));},addFragCycles(hwcEventName,hwcTitle){function handler(eventName,cpuNumber,pid,ts,eventBase){return this.fragSample(hwcTitle,'cycles',ts,eventBase);}
+this.importer.registerEventHandler(hwcEventName,handler.bind(this));},computeSample(ctrName,seriesName,ts,eventBase){return this.hwcSample('mali:compute','Compute: '+ctrName,seriesName,ts,eventBase);},addComputeCounter(hwcEventName,hwcTitle){function handler(eventName,cpuNumber,pid,ts,eventBase){return this.computeSample(hwcTitle,'count',ts,eventBase);}
+this.importer.registerEventHandler(hwcEventName,handler.bind(this));},addComputeCycles(hwcEventName,hwcTitle){function handler(eventName,cpuNumber,pid,ts,eventBase){return this.computeSample(hwcTitle,'cycles',ts,eventBase);}
+this.importer.registerEventHandler(hwcEventName,handler.bind(this));},addTripipeCycles(hwcEventName,hwcTitle){function handler(eventName,cpuNumber,pid,ts,eventBase){return this.hwcSample('mali:shader','Tripipe: '+hwcTitle,'cycles',ts,eventBase);}
+this.importer.registerEventHandler(hwcEventName,handler.bind(this));},arithSample(ctrName,seriesName,ts,eventBase){return this.hwcSample('mali:arith','Arith: '+ctrName,seriesName,ts,eventBase);},addArithCounter(hwcEventName,hwcTitle){function handler(eventName,cpuNumber,pid,ts,eventBase){return this.arithSample(hwcTitle,'count',ts,eventBase);}
+this.importer.registerEventHandler(hwcEventName,handler.bind(this));},addArithCycles(hwcEventName,hwcTitle){function handler(eventName,cpuNumber,pid,ts,eventBase){return this.arithSample(hwcTitle,'cycles',ts,eventBase);}
+this.importer.registerEventHandler(hwcEventName,handler.bind(this));},addLSCounter(hwcEventName,hwcTitle){function handler(eventName,cpuNumber,pid,ts,eventBase){return this.hwcSample('mali:ls','LS: '+hwcTitle,'count',ts,eventBase);}
+this.importer.registerEventHandler(hwcEventName,handler.bind(this));},textureSample(ctrName,seriesName,ts,eventBase){return this.hwcSample('mali:texture','Texture: '+ctrName,seriesName,ts,eventBase);},addTexCounter(hwcEventName,hwcTitle){function handler(eventName,cpuNumber,pid,ts,eventBase){return this.textureSample(hwcTitle,'count',ts,eventBase);}
+this.importer.registerEventHandler(hwcEventName,handler.bind(this));},addLSCCounter(hwcEventName,hwcTitle){function handler(eventName,cpuNumber,pid,ts,eventBase){return this.hwcSample('mali:lsc','LSC: '+hwcTitle,'count',ts,eventBase);}
+this.importer.registerEventHandler(hwcEventName,handler.bind(this));},addAXICounter(hwcEventName,hwcTitle){function handler(eventName,cpuNumber,pid,ts,eventBase){return this.hwcSample('mali:axi','AXI: '+hwcTitle,'count',ts,eventBase);}
+this.importer.registerEventHandler(hwcEventName,handler.bind(this));},mmuSample(ctrName,seriesName,ts,eventBase){return this.hwcSample('mali:mmu','MMU: '+ctrName,seriesName,ts,eventBase);},addMMUCounter(hwcEventName,hwcTitle){function handler(eventName,cpuNumber,pid,ts,eventBase){return this.mmuSample(hwcTitle,'count',ts,eventBase);}
+this.importer.registerEventHandler(hwcEventName,handler.bind(this));},addMMUCycles(hwcEventName,hwcTitle){function handler(eventName,cpuNumber,pid,ts,eventBase){return this.mmuSample(hwcTitle,'cycles',ts,eventBase);}
+this.importer.registerEventHandler(hwcEventName,handler.bind(this));},l2Sample(ctrName,seriesName,ts,eventBase){return this.hwcSample('mali:l2','L2: '+ctrName,seriesName,ts,eventBase);},addL2Counter(hwcEventName,hwcTitle){function handler(eventName,cpuNumber,pid,ts,eventBase){return this.l2Sample(hwcTitle,'count',ts,eventBase);}
+this.importer.registerEventHandler(hwcEventName,handler.bind(this));},addL2Cycles(hwcEventName,hwcTitle){function handler(eventName,cpuNumber,pid,ts,eventBase){return this.l2Sample(hwcTitle,'cycles',ts,eventBase);}
+this.importer.registerEventHandler(hwcEventName,handler.bind(this));}};Parser.register(MaliParser);return{MaliParser,};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){const Parser=tr.e.importer.linux_perf.Parser;function MemReclaimParser(importer){Parser.call(this,importer);importer.registerEventHandler('mm_vmscan_kswapd_wake',MemReclaimParser.prototype.kswapdWake.bind(this));importer.registerEventHandler('mm_vmscan_kswapd_sleep',MemReclaimParser.prototype.kswapdSleep.bind(this));importer.registerEventHandler('mm_vmscan_direct_reclaim_begin',MemReclaimParser.prototype.reclaimBegin.bind(this));importer.registerEventHandler('mm_vmscan_direct_reclaim_end',MemReclaimParser.prototype.reclaimEnd.bind(this));importer.registerEventHandler('lowmemory_kill',MemReclaimParser.prototype.lowmemoryKill.bind(this));}
+const kswapdWakeRE=/nid=(\d+) order=(\d+)/;const kswapdSleepRE=/nid=(\d+)/;const reclaimBeginRE=/order=(\d+) may_writepage=\d+ gfp_flags=(.+)/;const reclaimEndRE=/nr_reclaimed=(\d+)/;const lowmemoryRE=/([^ ]+) \((\d+)\), page cache (\d+)kB \(limit (\d+)kB\), free (-?\d+)Kb/;MemReclaimParser.prototype={__proto__:Parser.prototype,kswapdWake(eventName,cpuNumber,pid,ts,eventBase){const event=kswapdWakeRE.exec(eventBase.details);if(!event)return false;const tgid=parseInt(eventBase.tgid);const nid=parseInt(event[1]);const order=parseInt(event[2]);const kthread=this.importer.getOrCreateKernelThread(eventBase.threadName,tgid,pid);if(kthread.openSliceTS){if(order>kthread.order){kthread.order=order;}}else{kthread.openSliceTS=ts;kthread.order=order;}
+return true;},kswapdSleep(eventName,cpuNumber,pid,ts,eventBase){const tgid=parseInt(eventBase.tgid);const kthread=this.importer.getOrCreateKernelThread(eventBase.threadName,tgid,pid);if(kthread.openSliceTS){kthread.thread.sliceGroup.pushCompleteSlice('memreclaim',eventBase.threadName,kthread.openSliceTS,ts-kthread.openSliceTS,0,0,{order:kthread.order});}
+kthread.openSliceTS=undefined;kthread.order=undefined;return true;},reclaimBegin(eventName,cpuNumber,pid,ts,eventBase){const event=reclaimBeginRE.exec(eventBase.details);if(!event)return false;const order=parseInt(event[1]);const gfp=event[2];const tgid=parseInt(eventBase.tgid);const kthread=this.importer.getOrCreateKernelThread(eventBase.threadName,tgid,pid);kthread.openSliceTS=ts;kthread.order=order;kthread.gfp=gfp;return true;},reclaimEnd(eventName,cpuNumber,pid,ts,eventBase){const event=reclaimEndRE.exec(eventBase.details);if(!event)return false;const nrReclaimed=parseInt(event[1]);const tgid=parseInt(eventBase.tgid);const kthread=this.importer.getOrCreateKernelThread(eventBase.threadName,tgid,pid);if(kthread.openSliceTS!==undefined){kthread.thread.sliceGroup.pushCompleteSlice('memreclaim','direct reclaim',kthread.openSliceTS,ts-kthread.openSliceTS,0,0,{order:kthread.order,gfp:kthread.gfp,nr_reclaimed:nrReclaimed});}
+kthread.openSliceTS=undefined;kthread.order=undefined;kthread.gfp=undefined;return true;},lowmemoryKill(eventName,cpuNumber,pid,ts,eventBase){const event=lowmemoryRE.exec(eventBase.details);if(!event)return false;const tgid=parseInt(eventBase.tgid);const killedName=event[1];const killedPid=parseInt(event[2]);const cache=parseInt(event[3]);const free=parseInt(event[5]);const kthread=this.importer.getOrCreateKernelThread(eventBase.threadName,tgid,pid);kthread.thread.sliceGroup.pushCompleteSlice('lowmemory','low memory kill',ts,0,0,0,{killed_name:killedName,killed_pid:killedPid,cache,free});return true;}};Parser.register(MemReclaimParser);return{MemReclaimParser,};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){const ColorScheme=tr.b.ColorScheme;const Parser=tr.e.importer.linux_perf.Parser;function PowerParser(importer){Parser.call(this,importer);importer.registerEventHandler('power_start',PowerParser.prototype.powerStartEvent.bind(this));importer.registerEventHandler('power_frequency',PowerParser.prototype.powerFrequencyEvent.bind(this));importer.registerEventHandler('cpu_frequency',PowerParser.prototype.cpuFrequencyEvent.bind(this));importer.registerEventHandler('cpu_frequency_limits',PowerParser.prototype.cpuFrequencyLimitsEvent.bind(this));importer.registerEventHandler('cpu_idle',PowerParser.prototype.cpuIdleEvent.bind(this));}
+PowerParser.prototype={__proto__:Parser.prototype,cpuStateSlice(ts,targetCpuNumber,eventType,cpuState){const targetCpu=this.importer.getOrCreateCpu(targetCpuNumber);if(eventType!=='1'){this.importer.model.importWarning({type:'parse_error',message:'Don\'t understand power_start events of '+'type '+eventType});return;}
+const powerCounter=targetCpu.getOrCreateCounter('','C-State');if(powerCounter.numSeries===0){powerCounter.addSeries(new tr.model.CounterSeries('state',ColorScheme.getColorIdForGeneralPurposeString(powerCounter.name+'.'+'state')));}
+powerCounter.series.forEach(function(series){series.addCounterSample(ts,cpuState);});},cpuIdleSlice(ts,targetCpuNumber,cpuState){const targetCpu=this.importer.getOrCreateCpu(targetCpuNumber);const powerCounter=targetCpu.getOrCreateCounter('','C-State');if(powerCounter.numSeries===0){powerCounter.addSeries(new tr.model.CounterSeries('state',ColorScheme.getColorIdForGeneralPurposeString(powerCounter.name)));}
+const val=(cpuState!==4294967295?cpuState+1:0);powerCounter.series.forEach(function(series){series.addCounterSample(ts,val);});},cpuFrequencySlice(ts,targetCpuNumber,powerState){const targetCpu=this.importer.getOrCreateCpu(targetCpuNumber);const powerCounter=targetCpu.getOrCreateCounter('','Clock Frequency');if(powerCounter.numSeries===0){powerCounter.addSeries(new tr.model.CounterSeries('state',ColorScheme.getColorIdForGeneralPurposeString(powerCounter.name+'.'+'state')));}
+powerCounter.series.forEach(function(series){series.addCounterSample(ts,powerState);});},cpuFrequencyLimitsSlice(ts,targetCpuNumber,minFreq,maxFreq){const targetCpu=this.importer.getOrCreateCpu(targetCpuNumber);const powerCounter=targetCpu.getOrCreateCounter('','Clock Frequency Limits');if(powerCounter.numSeries===0){powerCounter.addSeries(new tr.model.CounterSeries('Min Frequency',ColorScheme.getColorIdForGeneralPurposeString(powerCounter.name+'.'+'Min Frequency')));powerCounter.addSeries(new tr.model.CounterSeries('Max Frequency',ColorScheme.getColorIdForGeneralPurposeString(powerCounter.name+'.'+'Max Frequency')));}
+powerCounter.series.forEach(function(series){if(series.name==='Min Frequency'){series.addCounterSample(ts,minFreq);}
+if(series.name==='Max Frequency'){series.addCounterSample(ts,maxFreq);}});},powerStartEvent(eventName,cpuNumber,pid,ts,eventBase){const event=/type=(\d+) state=(\d) cpu_id=(\d+)/.exec(eventBase.details);if(!event)return false;const targetCpuNumber=parseInt(event[3]);const cpuState=parseInt(event[2]);this.cpuStateSlice(ts,targetCpuNumber,event[1],cpuState);return true;},powerFrequencyEvent(eventName,cpuNumber,pid,ts,eventBase){const event=/type=(\d+) state=(\d+) cpu_id=(\d+)/.exec(eventBase.details);if(!event)return false;const targetCpuNumber=parseInt(event[3]);const powerState=parseInt(event[2]);this.cpuFrequencySlice(ts,targetCpuNumber,powerState);return true;},cpuFrequencyEvent(eventName,cpuNumber,pid,ts,eventBase){const event=/state=(\d+) cpu_id=(\d+)/.exec(eventBase.details);if(!event)return false;const targetCpuNumber=parseInt(event[2]);const powerState=parseInt(event[1]);this.cpuFrequencySlice(ts,targetCpuNumber,powerState);return true;},cpuFrequencyLimitsEvent(eventName,cpu,pid,ts,eventBase){const event=/min=(\d+) max=(\d+) cpu_id=(\d+)/.exec(eventBase.details);if(!event)return false;const targetCpuNumber=parseInt(event[3]);const minFreq=parseInt(event[1]);const maxFreq=parseInt(event[2]);this.cpuFrequencyLimitsSlice(ts,targetCpuNumber,minFreq,maxFreq);return true;},cpuIdleEvent(eventName,cpuNumber,pid,ts,eventBase){const event=/state=(\d+) cpu_id=(\d+)/.exec(eventBase.details);if(!event)return false;const targetCpuNumber=parseInt(event[2]);const cpuState=parseInt(event[1]);this.cpuIdleSlice(ts,targetCpuNumber,cpuState);return true;}};Parser.register(PowerParser);return{PowerParser,};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){const ColorScheme=tr.b.ColorScheme;const Parser=tr.e.importer.linux_perf.Parser;function RegulatorParser(importer){Parser.call(this,importer);importer.registerEventHandler('regulator_enable',RegulatorParser.prototype.regulatorEnableEvent.bind(this));importer.registerEventHandler('regulator_enable_delay',RegulatorParser.prototype.regulatorEnableDelayEvent.bind(this));importer.registerEventHandler('regulator_enable_complete',RegulatorParser.prototype.regulatorEnableCompleteEvent.bind(this));importer.registerEventHandler('regulator_disable',RegulatorParser.prototype.regulatorDisableEvent.bind(this));importer.registerEventHandler('regulator_disable_complete',RegulatorParser.prototype.regulatorDisableCompleteEvent.bind(this));importer.registerEventHandler('regulator_set_voltage',RegulatorParser.prototype.regulatorSetVoltageEvent.bind(this));importer.registerEventHandler('regulator_set_voltage_complete',RegulatorParser.prototype.regulatorSetVoltageCompleteEvent.bind(this));this.model_=importer.model_;}
+const regulatorEnableRE=/name=(.+)/;const regulatorDisableRE=/name=(.+)/;const regulatorSetVoltageCompleteRE=/name=(\S+), val=(\d+)/;RegulatorParser.prototype={__proto__:Parser.prototype,getCtr_(ctrName,valueName){const ctr=this.model_.kernel.getOrCreateCounter(null,'vreg '+ctrName+' '+valueName);if(ctr.series[0]===undefined){ctr.addSeries(new tr.model.CounterSeries(valueName,ColorScheme.getColorIdForGeneralPurposeString(ctrName+'.'+valueName)));}
+return ctr;},regulatorEnableEvent(eventName,cpuNum,pid,ts,eventBase){const event=regulatorEnableRE.exec(eventBase.details);if(!event)return false;const name=event[1];const ctr=this.getCtr_(name,'enabled');ctr.series[0].addCounterSample(ts,1);return true;},regulatorEnableDelayEvent(eventName,cpuNum,pid,ts,eventBase){return true;},regulatorEnableCompleteEvent(eventName,cpuNum,pid,ts,eventBase){return true;},regulatorDisableEvent(eventName,cpuNum,pid,ts,eventBase){const event=regulatorDisableRE.exec(eventBase.details);if(!event)return false;const name=event[1];const ctr=this.getCtr_(name,'enabled');ctr.series[0].addCounterSample(ts,0);return true;},regulatorDisableCompleteEvent(eventName,cpuNum,pid,ts,eventBase){return true;},regulatorSetVoltageEvent(eventName,cpuNum,pid,ts,eventBase){return true;},regulatorSetVoltageCompleteEvent(eventName,cpuNum,pid,ts,eventBase){const event=regulatorSetVoltageCompleteRE.exec(eventBase.details);if(!event)return false;const name=event[1];const voltage=parseInt(event[2]);const ctr=this.getCtr_(name,'voltage');ctr.series[0].addCounterSample(ts,voltage);return true;}};Parser.register(RegulatorParser);return{RegulatorParser,};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){const Parser=tr.e.importer.linux_perf.Parser;function RssParser(importer){Parser.call(this,importer);importer.registerEventHandler('rss_stat',RssParser.prototype.rssStat.bind(this));}
+const TestExports={};const rssStatRE=new RegExp('member=(\\d+) size=(\\d+)');TestExports.rssStatRE=rssStatRE;const unknownThreadName='<...>';RssParser.prototype={__proto__:Parser.prototype,rssStat(eventName,cpuNumber,pid,ts,eventBase){const event=rssStatRE.exec(eventBase.details);if(!event)return false;const member=parseInt(event[1]);const size=parseInt(event[2]);if(eventBase.tgid===undefined){return false;}
+const tgid=parseInt(eventBase.tgid);const process=this.importer.model_.getOrCreateProcess(tgid);let subTitle='';if(member===0){subTitle=' (file pages)';}else if(member===1){subTitle=' (anon)';}
+const rssCounter=process.getOrCreateCounter('RSS','RSS '+member+subTitle);if(rssCounter.numSeries===0){rssCounter.addSeries(new tr.model.CounterSeries('RSS',tr.b.ColorScheme.getColorIdForGeneralPurposeString(rssCounter.name)));}
+rssCounter.series.forEach(function(series){series.addCounterSample(ts,size);});return true;},};Parser.register(RssParser);return{RssParser,_RssParserTestExports:TestExports};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){const Parser=tr.e.importer.linux_perf.Parser;function SchedParser(importer){Parser.call(this,importer);importer.registerEventHandler('sched_switch',SchedParser.prototype.schedSwitchEvent.bind(this));importer.registerEventHandler('sched_wakeup',SchedParser.prototype.schedWakeupEvent.bind(this));importer.registerEventHandler('sched_blocked_reason',SchedParser.prototype.schedBlockedEvent.bind(this));importer.registerEventHandler('sched_cpu_hotplug',SchedParser.prototype.schedCpuHotplugEvent.bind(this));}
+const TestExports={};const schedSwitchRE=new RegExp('prev_comm=(.+) prev_pid=(\\d+) prev_prio=(\\d+) '+'prev_state=(\\S\\+?|\\S\\|\\S) ==> '+'next_comm=(.+) next_pid=(\\d+) next_prio=(\\d+)');const schedBlockedRE=new RegExp('pid=(\\d+) iowait=(\\d) caller=(.+)');TestExports.schedSwitchRE=schedSwitchRE;const schedWakeupRE=/comm=(.+) pid=(\d+) prio=(\d+)(?: success=\d+)? target_cpu=(\d+)/;TestExports.schedWakeupRE=schedWakeupRE;const unknownThreadName='<...>';SchedParser.prototype={__proto__:Parser.prototype,schedSwitchEvent(eventName,cpuNumber,pid,ts,eventBase){const event=schedSwitchRE.exec(eventBase.details);if(!event)return false;const prevState=event[4];const nextComm=event[5];const nextPid=parseInt(event[6]);const nextPrio=parseInt(event[7]);if(eventBase.tgid!==undefined){const tgid=parseInt(eventBase.tgid);const process=this.importer.model_.getOrCreateProcess(tgid);const storedThread=process.getThread(pid);if(!storedThread){const thread=process.getOrCreateThread(pid);thread.name=eventBase.threadName;}else if(storedThread.name===unknownThreadName){storedThread.name=eventBase.threadName;}}
+const nextThread=this.importer.threadsByLinuxPid[nextPid];let nextName;if(nextThread){nextName=nextThread.userFriendlyName;}else{nextName=nextComm;}
+const cpu=this.importer.getOrCreateCpu(cpuNumber);cpu.switchActiveThread(ts,{stateWhenDescheduled:prevState},nextPid,nextName,{comm:nextComm,tid:nextPid,prio:nextPrio});return true;},schedWakeupEvent(eventName,cpuNumber,pid,ts,eventBase){const event=schedWakeupRE.exec(eventBase.details);if(!event)return false;const fromPid=pid;const comm=event[1];pid=parseInt(event[2]);const prio=parseInt(event[3]);this.importer.markPidRunnable(ts,pid,comm,prio,fromPid);return true;},schedCpuHotplugEvent(eventName,cpuNumber,pid,ts,eventBase){const event=/cpu (\d+) (.+) error=(\d+)/.exec(eventBase.details);if(!event)return false;cpuNumber=event[1];const state=event[2];const targetCpu=this.importer.getOrCreateCpu(cpuNumber);const powerCounter=targetCpu.getOrCreateCounter('','Cpu Hotplug');if(powerCounter.numSeries===0){powerCounter.addSeries(new tr.model.CounterSeries('State',tr.b.ColorScheme.getColorIdForGeneralPurposeString(powerCounter.name+'.'+'State')));}
+powerCounter.series.forEach(function(series){if(series.name==='State'){series.addCounterSample(ts,state.localeCompare('offline')?0:1);}});return true;},schedBlockedEvent(eventName,cpuNumber,pid,ts,eventBase){const event=schedBlockedRE.exec(eventBase.details);if(!event)return false;pid=parseInt(event[1]);const iowait=parseInt(event[2]);const caller=event[3];this.importer.addPidBlockedReason(ts,pid,iowait,caller);return true;}};Parser.register(SchedParser);return{SchedParser,_SchedParserTestExports:TestExports};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){const ColorScheme=tr.b.ColorScheme;const Parser=tr.e.importer.linux_perf.Parser;function SyncParser(importer){Parser.call(this,importer);importer.registerEventHandler('sync_timeline',SyncParser.prototype.timelineEvent.bind(this));importer.registerEventHandler('sync_wait',SyncParser.prototype.syncWaitEvent.bind(this));importer.registerEventHandler('sync_pt',SyncParser.prototype.syncPtEvent.bind(this));this.model_=importer.model_;}
+const syncTimelineRE=/name=(\S+) value=(\S*)/;const syncWaitRE=/(\S+) name=(\S+) state=(\d+)/;const syncPtRE=/name=(\S+) value=(\S*)/;SyncParser.prototype={__proto__:Parser.prototype,timelineEvent(eventName,cpuNumber,pid,ts,eventBase){const event=syncTimelineRE.exec(eventBase.details);if(!event)return false;const thread=this.importer.getOrCreatePseudoThread(event[1]);if(thread.lastActiveTs!==undefined){const duration=ts-thread.lastActiveTs;let value=thread.lastActiveValue;if(value===undefined)value=' ';const slice=new tr.model.ThreadSlice('',value,ColorScheme.getColorIdForGeneralPurposeString(value),thread.lastActiveTs,{},duration);thread.thread.sliceGroup.pushSlice(slice);}
+thread.lastActiveTs=ts;thread.lastActiveValue=event[2];return true;},syncWaitEvent(eventName,cpuNumber,pid,ts,eventBase){const event=syncWaitRE.exec(eventBase.details);if(!event)return false;if(eventBase.tgid===undefined){return false;}
+const tgid=parseInt(eventBase.tgid);const thread=this.model_.getOrCreateProcess(tgid).getOrCreateThread(pid);thread.name=eventBase.threadName;const slices=thread.kernelSliceGroup;if(!slices.isTimestampValidForBeginOrEnd(ts)){this.model_.importWarning({type:'parse_error',message:'Timestamps are moving backward.'});return false;}
+const name='fence_wait("'+event[2]+'")';if(event[1]==='begin'){const slice=slices.beginSlice(null,name,ts,{'Start state':event[3]});}else if(event[1]==='end'){if(slices.openSliceCount>0){slices.endSlice(ts);}}else{return false;}
+return true;},syncPtEvent(eventName,cpuNumber,pid,ts,eventBase){return!!syncPtRE.exec(eventBase.details);}};Parser.register(SyncParser);return{SyncParser,};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){const ColorScheme=tr.b.ColorScheme;const Parser=tr.e.importer.linux_perf.Parser;function WorkqueueParser(importer){Parser.call(this,importer);importer.registerEventHandler('workqueue_execute_start',WorkqueueParser.prototype.executeStartEvent.bind(this));importer.registerEventHandler('workqueue_execute_end',WorkqueueParser.prototype.executeEndEvent.bind(this));importer.registerEventHandler('workqueue_queue_work',WorkqueueParser.prototype.executeQueueWork.bind(this));importer.registerEventHandler('workqueue_activate_work',WorkqueueParser.prototype.executeActivateWork.bind(this));}
+const workqueueExecuteStartRE=/work struct (.+): function (\S+)/;const workqueueExecuteEndRE=/work struct (.+)/;WorkqueueParser.prototype={__proto__:Parser.prototype,executeStartEvent(eventName,cpuNumber,pid,ts,eventBase){const event=workqueueExecuteStartRE.exec(eventBase.details);if(!event)return false;const kthread=this.importer.getOrCreateKernelThread(eventBase.threadName,pid,pid);kthread.openSliceTS=ts;kthread.openSlice=event[2];return true;},executeEndEvent(eventName,cpuNumber,pid,ts,eventBase){const event=workqueueExecuteEndRE.exec(eventBase.details);if(!event)return false;const kthread=this.importer.getOrCreateKernelThread(eventBase.threadName,pid,pid);if(kthread.openSlice){const slice=new tr.model.ThreadSlice('',kthread.openSlice,ColorScheme.getColorIdForGeneralPurposeString(kthread.openSlice),kthread.openSliceTS,{},ts-kthread.openSliceTS);kthread.thread.sliceGroup.pushSlice(slice);}
+kthread.openSlice=undefined;return true;},executeQueueWork(eventName,cpuNumber,pid,ts,eventBase){return true;},executeActivateWork(eventName,cpuNumber,pid,ts,eventBase){return true;}};Parser.register(WorkqueueParser);return{WorkqueueParser,};});'use strict';tr.exportTo('tr.e.importer.linux_perf',function(){const MONOTONIC_TO_FTRACE_GLOBAL_SYNC_ID='linux_clock_monotonic_to_ftrace_global';const IMPORT_PRIORITY=2;function FTraceImporter(model,events){this.importPriority=IMPORT_PRIORITY;this.model_=model;this.events_=events;this.wakeups_=[];this.blockedReasons_=[];this.kernelThreadStates_={};this.buildMapFromLinuxPidsToThreads_();this.lines_=[];this.pseudoThreadCounter=1;this.parsers_=[];this.eventHandlers_={};this.haveClockSyncedMonotonicToGlobal_=false;this.clockDomainId_=tr.model.ClockDomainId.LINUX_FTRACE_GLOBAL;}
+const TestExports={};const lineREWithTGID=new RegExp('^\\s*(.+)-(\\d+)\\s+\\(\\s*(\\d+|-+)\\)\\s\\[(\\d+)\\]'+'\\s+[dX.][Nnp.][Hhs.][0-9a-f.]'+'\\s+(\\d+\\.\\d+):\\s+(\\S+):\\s(.*)$');const lineParserWithTGID=function(line){const groups=lineREWithTGID.exec(line);if(!groups)return groups;let tgid=groups[3];if(tgid[0]==='-')tgid=undefined;return{threadName:groups[1],pid:groups[2],tgid,cpuNumber:groups[4],timestamp:groups[5],eventName:groups[6],details:groups[7]};};TestExports.lineParserWithTGID=lineParserWithTGID;const lineREWithIRQInfo=new RegExp('^\\s*(.+)-(\\d+)\\s+\\[(\\d+)\\]'+'\\s+[dX.][Nnp.][Hhs.][0-9a-f.]'+'\\s+(\\d+\\.\\d+):\\s+(\\S+):\\s(.*)$');const lineParserWithIRQInfo=function(line){const groups=lineREWithIRQInfo.exec(line);if(!groups)return groups;return{threadName:groups[1],pid:groups[2],cpuNumber:groups[3],timestamp:groups[4],eventName:groups[5],details:groups[6]};};TestExports.lineParserWithIRQInfo=lineParserWithIRQInfo;const lineREWithLegacyFmt=/^\s*(.+)-(\d+)\s+\[(\d+)\]\s*(\d+\.\d+):\s+(\S+):\s(.*)$/;const lineParserWithLegacyFmt=function(line){const groups=lineREWithLegacyFmt.exec(line);if(!groups){return groups;}
+return{threadName:groups[1],pid:groups[2],cpuNumber:groups[3],timestamp:groups[4],eventName:groups[5],details:groups[6]};};TestExports.lineParserWithLegacyFmt=lineParserWithLegacyFmt;const traceEventClockSyncRE=/trace_event_clock_sync: parent_ts=(\d+\.?\d*)/;TestExports.traceEventClockSyncRE=traceEventClockSyncRE;const realTimeClockSyncRE=/trace_event_clock_sync: realtime_ts=(\d+)/;const genericClockSyncRE=/trace_event_clock_sync: name=([\w\-]+)/;const pseudoKernelPID=0;function autoDetectLineParser(line){if(line[0]==='{')return false;if(lineREWithTGID.test(line))return lineParserWithTGID;if(lineREWithIRQInfo.test(line))return lineParserWithIRQInfo;if(lineREWithLegacyFmt.test(line))return lineParserWithLegacyFmt;return undefined;}
+TestExports.autoDetectLineParser=autoDetectLineParser;FTraceImporter.canImport=function(events){if(events instanceof tr.b.TraceStream)events=events.header;if(!(typeof(events)==='string'||events instanceof String)){return false;}
+if(FTraceImporter._extractEventsFromSystraceHTML(events,false).ok){return true;}
+if(FTraceImporter._extractEventsFromSystraceMultiHTML(events,false).ok){return true;}
+if(/^# tracer:/.test(events))return true;const lineBreakIndex=events.indexOf('\n');if(lineBreakIndex>-1)events=events.substring(0,lineBreakIndex);if(autoDetectLineParser(events))return true;return false;};FTraceImporter._extractEventsFromSystraceHTML=function(incomingEvents,produceResult){const failure={ok:false};if(produceResult===undefined)produceResult=true;const header=incomingEvents instanceof tr.b.TraceStream?incomingEvents.header:incomingEvents;if(!/^<!DOCTYPE html>/.test(header))return failure;const r=new tr.importer.SimpleLineReader(incomingEvents);if(!r.advanceToLineMatching(/^  <script>$/))return failure;if(!r.advanceToLineMatching(/^  var linuxPerfData = "\\$/))return failure;const eventsBeginAtLine=r.curLineNumber+1;r.beginSavingLines();if(!r.advanceToLineMatching(/^  <\/script>$/))return failure;let rawEvents=r.endSavingLinesAndGetResult();rawEvents=rawEvents.slice(1,rawEvents.length-1);if(!r.advanceToLineMatching(/^<\/body>$/))return failure;if(!r.advanceToLineMatching(/^<\/html>$/))return failure;function endsWith(str,suffix){return str.indexOf(suffix,str.length-suffix.length)!==-1;}
+function stripSuffix(str,suffix){if(!endsWith(str,suffix))return str;return str.substring(str,str.length-suffix.length);}
+let events=[];if(produceResult){for(let i=0;i<rawEvents.length;i++){let event=rawEvents[i];event=stripSuffix(event,'\\n\\');events.push(event);}}else{events=[rawEvents[rawEvents.length-1]];}
+const oldLastEvent=events[events.length-1];const newLastEvent=stripSuffix(oldLastEvent,'\\n";');if(newLastEvent===oldLastEvent)return failure;events[events.length-1]=newLastEvent;return{ok:true,lines:produceResult?events:undefined,eventsBeginAtLine};};FTraceImporter._extractEventsFromSystraceMultiHTML=function(incomingEvents,produceResult){const failure={ok:false};if(produceResult===undefined)produceResult=true;const header=incomingEvents instanceof tr.b.TraceStream?incomingEvents.header:incomingEvents;if(!(new RegExp('^<!DOCTYPE HTML>','i').test(header)))return failure;const r=new tr.importer.SimpleLineReader(incomingEvents);let events=[];let eventsBeginAtLine;while(!/^# tracer:/.test(events)){if(!r.advanceToLineMatching(/^  <script class="trace-data" type="application\/text">$/)){return failure;}
+eventsBeginAtLine=r.curLineNumber+1;r.beginSavingLines();if(!r.advanceToLineMatching(/^  <\/script>$/))return failure;events=r.endSavingLinesAndGetResult();events=events.slice(1,events.length-1);}
+if(!r.advanceToLineMatching(/^<\/body>$/))return failure;if(!r.advanceToLineMatching(/^<\/html>$/))return failure;return{ok:true,lines:produceResult?events:undefined,eventsBeginAtLine,};};FTraceImporter.prototype={__proto__:tr.importer.Importer.prototype,get importerName(){return'FTraceImporter';},get model(){return this.model_;},importClockSyncMarkers(){this.lazyInit_();this.forEachLine_(function(text,eventBase,cpuNumber,pid,ts){const eventName=eventBase.eventName;if(eventName!=='tracing_mark_write'&&eventName!=='0')return;if(traceEventClockSyncRE.exec(eventBase.details)||genericClockSyncRE.exec(eventBase.details)){this.traceClockSyncEvent_(eventName,cpuNumber,pid,ts,eventBase);}else if(realTimeClockSyncRE.exec(eventBase.details)){const match=realTimeClockSyncRE.exec(eventBase.details);this.model_.realtime_to_monotonic_offset_ms=ts-match[1];}}.bind(this));},importEvents(){if(this.lines_.length===0)return;const modelTimeTransformer=this.model_.clockSyncManager.getModelTimeTransformer(this.clockDomainId_);this.importCpuData_(modelTimeTransformer);this.buildMapFromLinuxPidsToThreads_();this.buildPerThreadCpuSlicesFromCpuState_();},registerEventHandler(eventName,handler){this.eventHandlers_[eventName]=handler;},getOrCreateCpu(cpuNumber){return this.model_.kernel.getOrCreateCpu(cpuNumber);},getOrCreateKernelThread(kernelThreadName,pid,tid){if(!this.kernelThreadStates_[kernelThreadName]){const thread=this.model_.getOrCreateProcess(pid).getOrCreateThread(tid);thread.name=kernelThreadName;this.kernelThreadStates_[kernelThreadName]={pid,thread,openSlice:undefined,openSliceTS:undefined};this.threadsByLinuxPid[tid]=thread;}
+return this.kernelThreadStates_[kernelThreadName];},getOrCreateBinderKernelThread(kernelThreadName,pid,tid){const key=kernelThreadName+pid+tid;if(!this.kernelThreadStates_[key]){const thread=this.model_.getOrCreateProcess(pid).getOrCreateThread(tid);thread.name=kernelThreadName;this.kernelThreadStates_[key]={pid,thread,openSlice:undefined,openSliceTS:undefined};this.threadsByLinuxPid[tid]=thread;}
+return this.kernelThreadStates_[key];},getOrCreatePseudoThread(threadName){let thread=this.kernelThreadStates_[threadName];if(!thread){thread=this.getOrCreateKernelThread(threadName,pseudoKernelPID,this.pseudoThreadCounter);this.pseudoThreadCounter++;}
+return thread;},markPidRunnable(ts,pid,comm,prio,fromPid){this.wakeups_.push({ts,tid:pid,fromTid:fromPid});},addPidBlockedReason(ts,pid,iowait,caller){this.blockedReasons_.push({ts,tid:pid,iowait,caller});},buildMapFromLinuxPidsToThreads_(){this.threadsByLinuxPid={};this.model_.getAllThreads().forEach(function(thread){this.threadsByLinuxPid[thread.tid]=thread;}.bind(this));},buildPerThreadCpuSlicesFromCpuState_(){const SCHEDULING_STATE=tr.model.SCHEDULING_STATE;for(const cpuNumber in this.model_.kernel.cpus){const cpu=this.model_.kernel.cpus[cpuNumber];for(let i=0;i<cpu.slices.length;i++){const cpuSlice=cpu.slices[i];const thread=this.threadsByLinuxPid[cpuSlice.args.tid];if(!thread)continue;cpuSlice.threadThatWasRunning=thread;if(!thread.tempCpuSlices){thread.tempCpuSlices=[];}
+thread.tempCpuSlices.push(cpuSlice);}}
+for(const i in this.wakeups_){const wakeup=this.wakeups_[i];const thread=this.threadsByLinuxPid[wakeup.tid];if(!thread)continue;thread.tempWakeups=thread.tempWakeups||[];thread.tempWakeups.push(wakeup);}
+for(const i in this.blockedReasons_){const reason=this.blockedReasons_[i];const thread=this.threadsByLinuxPid[reason.tid];if(!thread)continue;thread.tempBlockedReasons=thread.tempBlockedReasons||[];thread.tempBlockedReasons.push(reason);}
+this.model_.getAllThreads().forEach(function(thread){if(thread.tempCpuSlices===undefined)return;const origSlices=thread.tempCpuSlices;delete thread.tempCpuSlices;origSlices.sort(function(x,y){return x.start-y.start;});const wakeups=thread.tempWakeups||[];delete thread.tempWakeups;wakeups.sort(function(x,y){return x.ts-y.ts;});const reasons=thread.tempBlockedReasons||[];delete thread.tempBlockedReasons;reasons.sort(function(x,y){return x.ts-y.ts;});const slices=[];if(origSlices.length){const slice=origSlices[0];if(wakeups.length&&wakeups[0].ts<slice.start){const wakeup=wakeups.shift();const wakeupDuration=slice.start-wakeup.ts;const args={'wakeup from tid':wakeup.fromTid};slices.push(new tr.model.ThreadTimeSlice(thread,SCHEDULING_STATE.RUNNABLE,'',wakeup.ts,args,wakeupDuration));}
+const runningSlice=new tr.model.ThreadTimeSlice(thread,SCHEDULING_STATE.RUNNING,'',slice.start,{},slice.duration);runningSlice.cpuOnWhichThreadWasRunning=slice.cpu;slices.push(runningSlice);}
+for(let i=1;i<origSlices.length;i++){let wakeup=undefined;const prevSlice=origSlices[i-1];const nextSlice=origSlices[i];let midDuration=nextSlice.start-prevSlice.end;while(wakeups.length&&wakeups[0].ts<nextSlice.start){const w=wakeups.shift();if(wakeup===undefined&&w.ts>prevSlice.end){wakeup=w;}}
+let blockedReason=undefined;while(reasons.length&&reasons[0].ts<prevSlice.end){const r=reasons.shift();}
+if(wakeup!==undefined&&reasons.length&&reasons[0].ts<wakeup.ts){blockedReason=reasons.shift();}
+const pushSleep=function(state){if(wakeup!==undefined){midDuration=wakeup.ts-prevSlice.end;}
+if(blockedReason!==undefined){const args={'kernel callsite when blocked:':blockedReason.caller};if(blockedReason.iowait){switch(state){case SCHEDULING_STATE.UNINTR_SLEEP:state=SCHEDULING_STATE.UNINTR_SLEEP_IO;break;case SCHEDULING_STATE.UNINTR_SLEEP_WAKE_KILL:state=SCHEDULING_STATE.UNINTR_SLEEP_WAKE_KILL_IO;break;case SCHEDULING_STATE.UNINTR_SLEEP_WAKING:state=SCHEDULING_STATE.UNINTR_SLEEP_WAKE_KILL_IO;break;default:}}
+slices.push(new tr.model.ThreadTimeSlice(thread,state,'',prevSlice.end,args,midDuration));}else{slices.push(new tr.model.ThreadTimeSlice(thread,state,'',prevSlice.end,{},midDuration));}
+if(wakeup!==undefined){const wakeupDuration=nextSlice.start-wakeup.ts;const args={'wakeup from tid':wakeup.fromTid};slices.push(new tr.model.ThreadTimeSlice(thread,SCHEDULING_STATE.RUNNABLE,'',wakeup.ts,args,wakeupDuration));wakeup=undefined;}};if(prevSlice.args.stateWhenDescheduled==='S'){pushSleep(SCHEDULING_STATE.SLEEPING);}else if(prevSlice.args.stateWhenDescheduled==='R'||prevSlice.args.stateWhenDescheduled==='R+'){slices.push(new tr.model.ThreadTimeSlice(thread,SCHEDULING_STATE.RUNNABLE,'',prevSlice.end,{},midDuration));}else if(prevSlice.args.stateWhenDescheduled==='D'){pushSleep(SCHEDULING_STATE.UNINTR_SLEEP);}else if(prevSlice.args.stateWhenDescheduled==='T'){slices.push(new tr.model.ThreadTimeSlice(thread,SCHEDULING_STATE.STOPPED,'',prevSlice.end,{},midDuration));}else if(prevSlice.args.stateWhenDescheduled==='t'){slices.push(new tr.model.ThreadTimeSlice(thread,SCHEDULING_STATE.DEBUG,'',prevSlice.end,{},midDuration));}else if(prevSlice.args.stateWhenDescheduled==='Z'){slices.push(new tr.model.ThreadTimeSlice(thread,SCHEDULING_STATE.ZOMBIE,'',prevSlice.end,{},midDuration));}else if(prevSlice.args.stateWhenDescheduled==='X'){slices.push(new tr.model.ThreadTimeSlice(thread,SCHEDULING_STATE.EXIT_DEAD,'',prevSlice.end,{},midDuration));}else if(prevSlice.args.stateWhenDescheduled==='x'){slices.push(new tr.model.ThreadTimeSlice(thread,SCHEDULING_STATE.TASK_DEAD,'',prevSlice.end,{},midDuration));}else if(prevSlice.args.stateWhenDescheduled==='K'){slices.push(new tr.model.ThreadTimeSlice(thread,SCHEDULING_STATE.WAKE_KILL,'',prevSlice.end,{},midDuration));}else if(prevSlice.args.stateWhenDescheduled==='W'){slices.push(new tr.model.ThreadTimeSlice(thread,SCHEDULING_STATE.WAKING,'',prevSlice.end,{},midDuration));}else if(prevSlice.args.stateWhenDescheduled==='D|K'){pushSleep(SCHEDULING_STATE.UNINTR_SLEEP_WAKE_KILL);}else if(prevSlice.args.stateWhenDescheduled==='D|W'){pushSleep(SCHEDULING_STATE.UNINTR_SLEEP_WAKING);}else{slices.push(new tr.model.ThreadTimeSlice(thread,SCHEDULING_STATE.UNKNOWN,'',prevSlice.end,{},midDuration));this.model_.importWarning({type:'parse_error',message:'Unrecognized sleep state: '+
+prevSlice.args.stateWhenDescheduled});}
+const runningSlice=new tr.model.ThreadTimeSlice(thread,SCHEDULING_STATE.RUNNING,'',nextSlice.start,{},nextSlice.duration);runningSlice.cpuOnWhichThreadWasRunning=prevSlice.cpu;slices.push(runningSlice);}
+thread.timeSlices=slices;},this);},createParsers_(){const allTypeInfos=tr.e.importer.linux_perf.Parser.getAllRegisteredTypeInfos();const parsers=allTypeInfos.map(function(typeInfo){return new typeInfo.constructor(this);},this);return parsers;},registerDefaultHandlers_(){this.registerEventHandler('tracing_mark_write',FTraceImporter.prototype.traceMarkingWriteEvent_.bind(this));this.registerEventHandler('0',FTraceImporter.prototype.traceMarkingWriteEvent_.bind(this));this.registerEventHandler('tracing_mark_write:trace_event_clock_sync',function(){return true;});this.registerEventHandler('0:trace_event_clock_sync',function(){return true;});},traceClockSyncEvent_(eventName,cpuNumber,pid,ts,eventBase){let event=/name=(\w+?)\s(.+)/.exec(eventBase.details);if(event){const name=event[1];const pieces=event[2].split(' ');const args={perfTs:ts};for(let i=0;i<pieces.length;i++){const parts=pieces[i].split('=');if(parts.length!==2){throw new Error('omgbbq');}
+args[parts[0]]=parts[1];}
+this.model_.clockSyncManager.addClockSyncMarker(this.clockDomainId_,name,ts);return true;}
+event=/name=([\w\-]+)/.exec(eventBase.details);if(event){this.model_.clockSyncManager.addClockSyncMarker(this.clockDomainId_,event[1],ts);return true;}
+event=/parent_ts=(\d+\.?\d*)/.exec(eventBase.details);if(!event)return false;let monotonicTs=event[1]*1000;if(monotonicTs===0)monotonicTs=ts;if(this.haveClockSyncedMonotonicToGlobal_){return true;}
+this.model_.clockSyncManager.addClockSyncMarker(this.clockDomainId_,MONOTONIC_TO_FTRACE_GLOBAL_SYNC_ID,ts);this.model_.clockSyncManager.addClockSyncMarker(tr.model.ClockDomainId.LINUX_CLOCK_MONOTONIC,MONOTONIC_TO_FTRACE_GLOBAL_SYNC_ID,monotonicTs);this.haveClockSyncedMonotonicToGlobal_=true;return true;},traceMarkingWriteEvent_(eventName,cpuNumber,pid,ts,eventBase,threadName){eventBase.details=eventBase.details.replace(/\\n.*$/,'');const event=/^\s*(\w+):\s*(.*)$/.exec(eventBase.details);if(!event){const tag=eventBase.details.substring(0,2);if(tag==='B|'||tag==='E'||tag==='E|'||tag==='X|'||tag==='C|'||tag==='S|'||tag==='F|'){eventBase.subEventName='android';}else{return false;}}else{eventBase.subEventName=event[1];eventBase.details=event[2];}
+const writeEventName=eventName+':'+eventBase.subEventName;const handler=this.eventHandlers_[writeEventName];if(!handler){this.model_.importWarning({type:'parse_error',message:'Unknown trace_marking_write event '+writeEventName});return true;}
+return handler(writeEventName,cpuNumber,pid,ts,eventBase,threadName);},importCpuData_(modelTimeTransformer){this.forEachLine_(function(text,eventBase,cpuNumber,pid,ts){const eventName=eventBase.eventName;const handler=this.eventHandlers_[eventName];if(!handler){this.model_.importWarning({type:'parse_error',message:'Unknown event '+eventName+' ('+text+')'});return;}
+ts=modelTimeTransformer(ts);if(!handler(eventName,cpuNumber,pid,ts,eventBase)){this.model_.importWarning({type:'parse_error',message:'Malformed '+eventName+' event ('+text+')'});}}.bind(this));},parseLines_(){let extractResult=FTraceImporter._extractEventsFromSystraceHTML(this.events_,true);if(!extractResult.ok){extractResult=FTraceImporter._extractEventsFromSystraceMultiHTML(this.events_,true);}
+let lineParser=undefined;if(extractResult.ok){for(const line of extractResult.lines){lineParser=this.parseLine_(line,lineParser);}}else{const r=new tr.importer.SimpleLineReader(this.events_);for(const line of r){lineParser=this.parseLine_(line,lineParser);}}},parseLine_(line,lineParser){line=line.trim();if(line.length===0)return lineParser;if(/^#/.test(line)){const clockType=/^# clock_type=([A-Z_]+)$/.exec(line);if(clockType){this.clockDomainId_=clockType[1];}
+return lineParser;}
+if(!lineParser){lineParser=autoDetectLineParser(line);if(!lineParser){this.model_.importWarning({type:'parse_error',message:'Cannot parse line: '+line});return lineParser;}}
+const eventBase=lineParser(line);if(!eventBase){this.model_.importWarning({type:'parse_error',message:'Unrecognized line: '+line});return lineParser;}
+this.lines_.push([line,eventBase,parseInt(eventBase.cpuNumber),parseInt(eventBase.pid),parseFloat(eventBase.timestamp)*1000]);return lineParser;},forEachLine_(handler){for(let i=0;i<this.lines_.length;++i){const line=this.lines_[i];handler.apply(this,line);}},lazyInit_(){this.parsers_=this.createParsers_();this.registerDefaultHandlers_();this.parseLines_();}};tr.importer.Importer.register(FTraceImporter);return{FTraceImporter,_FTraceImporterTestExports:TestExports,IMPORT_PRIORITY,};});'use strict';tr.exportTo('tr.e.importer.android.atrace_process_dump',function(){const IMPORT_PRIORITY=tr.e.importer.linux_perf.IMPORT_PRIORITY+1;const HEADER='ATRACE_PROCESS_DUMP';const PROTECTION_FLAG_LETTERS={'-':0,'r':tr.model.VMRegion.PROTECTION_FLAG_READ,'w':tr.model.VMRegion.PROTECTION_FLAG_WRITE,'x':tr.model.VMRegion.PROTECTION_FLAG_EXECUTE,'s':tr.model.VMRegion.PROTECTION_FLAG_MAYSHARE,};class AtraceProcessDumpImporter extends tr.importer.Importer{constructor(model,data){super(model,data);this.importPriority=IMPORT_PRIORITY;this.model_=model;this.raw_data_=data;this.clock_sync_markers_={};this.snapshots_=[];this.processes_={};}
+static canImport(events){if(!(typeof(events)==='string'||events instanceof String)){return false;}
+return events.startsWith(HEADER);}
+get importerName(){return'AtraceProcessDumpImporter';}
+get model(){return this.model_;}
+lazyParseData(){if(this.raw_data_===undefined){return;}
+const dump=JSON.parse(this.raw_data_.slice(HEADER.length+1));this.clock_sync_markers_=dump.clock_sync_markers;this.snapshots_=dump.dump.snapshots;this.processes_=dump.dump.processes;this.raw_data_=undefined;}
+importClockSyncMarkers(){this.lazyParseData();for(const syncId in this.clock_sync_markers_){const ts=parseInt(this.clock_sync_markers_[syncId]);this.model_.clockSyncManager.addClockSyncMarker(tr.model.ClockDomainId.LINUX_CLOCK_MONOTONIC,syncId,ts);}}
+setProcessMemoryDumpTotals_(pmd,processInfo){pmd.totals={'residentBytes':processInfo.rss*1024,'platformSpecific':{'vm':processInfo.vm*1024}};const totals=pmd.totals.platformSpecific;function importGpuMetric(name){if(processInfo[name]!==undefined&&processInfo[name]>0){totals[name]=processInfo[name]*1024;totals[name+'_pss']=processInfo[name+'_pss']*1024;}}
+importGpuMetric('gpu_egl');importGpuMetric('gpu_gl');importGpuMetric('gpu_etc');if(processInfo.pss!==undefined){totals.pss=processInfo.pss*1024;totals.swp=processInfo.swp*1024;totals.pc=processInfo.pc*1024;totals.pd=processInfo.pd*1024;totals.sc=processInfo.sc*1024;totals.sd=processInfo.sd*1024;}}
+setProcessMemoryDumpVmRegions_(pmd,processInfo){if(processInfo.mmaps===undefined){return;}
+const vmRegions=[];for(const memoryMap of processInfo.mmaps){const addr=memoryMap.vm.split('-').map(x=>parseInt(x,16));let flags=0;for(const letter of memoryMap.flags){flags|=PROTECTION_FLAG_LETTERS[letter];}
+const totals={'proportionalResident':memoryMap.pss*1024,'privateCleanResident':memoryMap.pc*1024,'privateDirtyResident':memoryMap.pd*1024,'sharedCleanResident':memoryMap.sc*1024,'sharedDirtyResident':memoryMap.sd*1024,'swapped':memoryMap.swp*1024,};vmRegions.push(new tr.model.VMRegion(addr[0],addr[1]-addr[0],flags,memoryMap.file,totals));}
+pmd.vmRegions=tr.model.VMRegionClassificationNode.fromRegions(vmRegions);}
+importEvents(){this.lazyParseData();for(const[pid,process]of Object.entries(this.processes_)){const modelProcess=this.model_.getProcess(pid);if(modelProcess===undefined){continue;}
+modelProcess.name=process.name;const threads=process.threads;if(threads===undefined){continue;}
+for(const[tid,thread]of Object.entries(threads)){const modelThread=modelProcess.threads[tid];if(modelThread===undefined){continue;}
+modelThread.name=thread.name;}}
+const memCounter=this.model_.kernel.getOrCreateCounter('global','SystemMemory');const memUsedSeries=new tr.model.CounterSeries('Used (KB)',0);const memSwappedSeries=new tr.model.CounterSeries('Swapped (KB)',0);memCounter.addSeries(memUsedSeries);memCounter.addSeries(memSwappedSeries);for(const snapshot of this.snapshots_){const ts=parseInt(snapshot.ts);const memoryDump=snapshot.memdump;if(memoryDump===undefined){const memInfo=snapshot.meminfo;if(memInfo===undefined){continue;}
+const memCaches=memInfo.Buffers+memInfo.Cached-memInfo.Mapped;const memUsed=memInfo.MemTotal-memInfo.MemFree-memCaches;const memSwapped=memInfo.SwapTotal-memInfo.SwapFree;memUsedSeries.addCounterSample(ts,memUsed);memSwappedSeries.addCounterSample(ts,memSwapped);continue;}
+const gmd=new tr.model.GlobalMemoryDump(this.model_,ts);this.model_.globalMemoryDumps.push(gmd);for(const[pid,processInfo]of Object.entries(memoryDump)){if(processInfo.rss===undefined){continue;}
+const modelProcess=this.model_.getProcess(pid);if(modelProcess===undefined){continue;}
+const pmd=new tr.model.ProcessMemoryDump(gmd,modelProcess,ts);gmd.processMemoryDumps[pid]=pmd;modelProcess.memoryDumps.push(pmd);this.setProcessMemoryDumpTotals_(pmd,processInfo);this.setProcessMemoryDumpVmRegions_(pmd,processInfo);}}}}
+tr.importer.Importer.register(AtraceProcessDumpImporter);return{AtraceProcessDumpImporter,};});'use strict';tr.exportTo('tr.model',function(){const ColorScheme=tr.b.ColorScheme;function Activity(name,category,range,args){tr.model.TimedEvent.call(this,range.min);this.title=name;this.category=category;this.colorId=ColorScheme.getColorIdForGeneralPurposeString(name);this.duration=range.duration;this.args=args;this.name=name;}
+Activity.prototype={__proto__:tr.model.TimedEvent.prototype,shiftTimestampsForward(amount){this.start+=amount;},addBoundsToRange(range){range.addValue(this.start);range.addValue(this.end);}};return{Activity,};});'use strict';tr.exportTo('tr.e.importer.android',function(){const Importer=tr.importer.Importer;const ACTIVITY_STATE={NONE:'none',CREATED:'created',STARTED:'started',RESUMED:'resumed',PAUSED:'paused',STOPPED:'stopped',DESTROYED:'destroyed'};const activityMap={};function EventLogImporter(model,events){this.model_=model;this.events_=events;this.importPriority=3;}
+const eventLogActivityRE=new RegExp('(\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}.\\d+)'+'\\s+(\\d+)\\s+(\\d+)\\s+([A-Z])\\s*'+'(am_\\w+)\\s*:(.*)');const amCreateRE=new RegExp('\s*\\[.*,.*,.*,(.*),.*,.*,.*,.*\\]');const amFocusedRE=new RegExp('\s*\\[\\d+,(.*)\\]');const amProcStartRE=new RegExp('\s*\\[\\d+,\\d+,\\d+,.*,activity,(.*)\\]');const amOnResumeRE=new RegExp('\s*\\[\\d+,(.*)\\]');const amOnPauseRE=new RegExp('\s*\\[\\d+,(.*)\\]');const amLaunchTimeRE=new RegExp('\s*\\[\\d+,\\d+,(.*),(\\d+),(\\d+)');const amDestroyRE=new RegExp('\s*\\[\\d+,\\d+,\\d+,(.*)\\]');EventLogImporter.canImport=function(events){if(!(typeof(events)==='string'||events instanceof String)){return false;}
+if(/^<!DOCTYPE html>/.test(events))return false;return eventLogActivityRE.test(events);};EventLogImporter.prototype={__proto__:Importer.prototype,get importerName(){return'EventLogImporter';},get model(){return this.model_;},getFullActivityName(component){const componentSplit=component.split('/');if(componentSplit[1].startsWith('.')){return componentSplit[0]+componentSplit[1];}
+return componentSplit[1];},getProcName(component){const componentSplit=component.split('/');return componentSplit[0];},findOrCreateActivity(activityName){if(activityName in activityMap){return activityMap[activityName];}
+const activity={state:ACTIVITY_STATE.NONE,name:activityName};activityMap[activityName]=activity;return activity;},deleteActivity(activityName){delete activityMap[activityName];},handleCreateActivity(ts,activityName){const activity=this.findOrCreateActivity(activityName);activity.state=ACTIVITY_STATE.CREATED;activity.createdTs=ts;},handleFocusActivity(ts,procName,activityName){const activity=this.findOrCreateActivity(activityName);activity.lastFocusedTs=ts;},handleProcStartForActivity(ts,activityName){const activity=this.findOrCreateActivity(activityName);activity.procStartTs=ts;},handleOnResumeCalled(ts,pid,activityName){const activity=this.findOrCreateActivity(activityName);activity.state=ACTIVITY_STATE.RESUMED;activity.lastResumeTs=ts;activity.pid=pid;},handleOnPauseCalled(ts,activityName){const activity=this.findOrCreateActivity(activityName);activity.state=ACTIVITY_STATE.PAUSED;activity.lastPauseTs=ts;if(ts>this.model_.bounds.min&&ts<this.model_.bounds.max){this.addActivityToProcess(activity);}},handleLaunchTime(ts,activityName,launchTime){const activity=this.findOrCreateActivity(activityName);activity.launchTime=launchTime;},handleDestroyActivity(ts,activityName){this.deleteActivity(activityName);},addActivityToProcess(activity){if(activity.pid===undefined)return;const process=this.model_.getOrCreateProcess(activity.pid);const range=tr.b.math.Range.fromExplicitRange(Math.max(this.model_.bounds.min,activity.lastResumeTs),activity.lastPauseTs);const newActivity=new tr.model.Activity(activity.name,'Android Activity',range,{created:activity.createdTs,procstart:activity.procStartTs,lastfocus:activity.lastFocusedTs});process.activities.push(newActivity);},parseAmLine_(line){let match=eventLogActivityRE.exec(line);if(!match)return;const firstRealtimeTs=this.model_.bounds.min-
+this.model_.realtime_to_monotonic_offset_ms;const year=new Date(firstRealtimeTs).getFullYear();const ts=match[1].substring(0,5)+'-'+year+' '+
+match[1].substring(5,match[1].length);const monotonicTs=Date.parse(ts)+
+this.model_.realtime_to_monotonic_offset_ms;const pid=match[2];const action=match[5];const data=match[6];if(action==='am_create_activity'){match=amCreateRE.exec(data);if(match&&match.length>=2){this.handleCreateActivity(monotonicTs,this.getFullActivityName(match[1]));}}else if(action==='am_focused_activity'){match=amFocusedRE.exec(data);if(match&&match.length>=2){this.handleFocusActivity(monotonicTs,this.getProcName(match[1]),this.getFullActivityName(match[1]));}}else if(action==='am_proc_start'){match=amProcStartRE.exec(data);if(match&&match.length>=2){this.handleProcStartForActivity(monotonicTs,this.getFullActivityName(match[1]));}}else if(action==='am_on_resume_called'){match=amOnResumeRE.exec(data);if(match&&match.length>=2){this.handleOnResumeCalled(monotonicTs,pid,match[1]);}}else if(action==='am_on_paused_called'){match=amOnPauseRE.exec(data);if(match&&match.length>=2){this.handleOnPauseCalled(monotonicTs,match[1]);}}else if(action==='am_activity_launch_time'){match=amLaunchTimeRE.exec(data);this.handleLaunchTime(monotonicTs,this.getFullActivityName(match[1]),match[2]);}else if(action==='am_destroy_activity'){match=amDestroyRE.exec(data);if(match&&match.length===2){this.handleDestroyActivity(monotonicTs,this.getFullActivityName(match[1]));}}},importEvents(){if(isNaN(this.model_.realtime_to_monotonic_offset_ms)){this.model_.importWarning({type:'eveng_log_clock_sync',message:'Need a trace_event_clock_sync to map realtime to import.'});return;}
+this.model_.updateBounds();const lines=this.events_.split('\n');lines.forEach(this.parseAmLine_,this);for(const activityName in activityMap){const activity=activityMap[activityName];if(activity.state===ACTIVITY_STATE.RESUMED){activity.lastPauseTs=this.model_.bounds.max;this.addActivityToProcess(activity);}}}};Importer.register(EventLogImporter);return{EventLogImporter,};});'use strict';tr.exportTo('tr.e.importer.android.process_data',function(){const Importer=tr.importer.Importer;const PROCESS_DUMP_HEADER='PROCESS DUMP';function ProcessDataImporter(model,processData){this.model_=model;this.processDataLines=processData.split('\n');this.importPriority=3;}
+ProcessDataImporter.canImport=function(events){if(!(typeof(events)==='string'||events instanceof String)){return false;}
+if(events.split('\n')[0]===PROCESS_DUMP_HEADER){return true;}
+return false;};ProcessDataImporter.prototype={__proto__:Importer.prototype,get importerName(){return'ProcessDataImporter';},get model(){return this.model_;},parseEventData(data){const allDumpedProcesses={};let parseProcesses=false;let parseThreads=false;let legacy=false;for(let i=1;i<data.length;i++){const cols=data[i].split(/\s+/);if(cols[0].startsWith('USER')){if(parseProcesses){parseProcesses=false;parseThreads=true;}else{parseThreads=false;parseProcesses=true;}
+const colCount=cols.length;if(parseProcesses&&colCount===9){legacy=false;}else if(parseProcesses&&colCount===8){legacy=true;}
+continue;}
+if(parseProcesses){const pid=Number(cols[1]);if(allDumpedProcesses[pid]===undefined){allDumpedProcesses[pid]={};}
+allDumpedProcesses[pid]={'name':cols[8],pid,'comm':cols[9]};continue;}
+if(parseThreads){let pid;let tid;let name;if(legacy){pid=Number(cols[1]);if(allDumpedProcesses[pid]!==undefined){tid=pid;}else{tid=pid;pid=Number(cols[2]);}
+name=cols.slice(8).join(' ');}else{pid=Number(cols[1]);tid=Number(cols[2]);name=cols.slice(3).join(' ');}
+if(allDumpedProcesses[pid]===undefined)continue;if(allDumpedProcesses[pid].threads===undefined){allDumpedProcesses[pid].threads={};}
+allDumpedProcesses[pid].threads[tid]={tid,name};continue;}}
+return allDumpedProcesses;},importEvents(){const allDumpedProcesses=this.parseEventData(this.processDataLines);const modelProcesses=this.model_.getAllProcesses();for(let i=0;i<modelProcesses.length;i++){const modelProcess=modelProcesses[i];const pid=modelProcess.pid;const dumpedProcess=allDumpedProcesses[pid];if(dumpedProcess===undefined){continue;}
+modelProcess.name=dumpedProcess.name;const processDumpThreads=dumpedProcess.threads;if(processDumpThreads!==undefined){for(const tid in modelProcess.threads){const modelThread=modelProcess.threads[tid];if(Number(pid)===Number(tid)){modelThread.name='UI thread';}else if(modelThread.name==='<...>'){if(processDumpThreads[tid]!==undefined){modelThread.name=processDumpThreads[tid].name;}}}}}}};Importer.register(ProcessDataImporter);return{ProcessDataImporter,};});'use strict';tr.exportTo('tr.e.importer.battor',function(){function BattorImporter(model,events){this.importPriority=3;this.model_=model;this.samples_=[];this.syncTimestampsById_=new Map();this.parseTrace_(events);}
+const battorDataLineRE=new RegExp('^(-?\\d+\\.\\d+)\\s+(-?\\d+\\.\\d+)\\s+(-?\\d+\\.\\d+)'+'(?:\\s+<(\\S+)>)?$');const battorHeaderLineRE=/^# BattOr/;BattorImporter.canImport=function(events){if(!(typeof(events)==='string'||events instanceof String)){return false;}
+return battorHeaderLineRE.test(events);};BattorImporter.prototype={__proto__:tr.importer.Importer.prototype,get importerName(){return'BattorImporter';},get model(){return this.model_;},importClockSyncMarkers(){for(const[syncId,ts]of this.syncTimestampsById_){this.model_.clockSyncManager.addClockSyncMarker(tr.model.ClockDomainId.BATTOR,syncId,ts);}},importEvents(){if(this.model_.device.powerSeries){this.model_.importWarning({type:'import_error',message:'Power counter exists, can not import BattOr power trace.'});return;}
+const modelTimeTransformer=this.model_.clockSyncManager.getModelTimeTransformer(tr.model.ClockDomainId.BATTOR);const powerSeries=this.model_.device.powerSeries=new tr.model.PowerSeries(this.model_.device);for(let i=0;i<this.samples_.length;i++){const sample=this.samples_[i];powerSeries.addPowerSample(modelTimeTransformer(sample.ts),sample.powerInW);}},parseTrace_(trace){const lines=trace.split('\n');for(let line of lines){line=line.trim();if(line.length===0)continue;if(line.startsWith('#'))continue;const groups=battorDataLineRE.exec(line);if(!groups){this.model_.importWarning({type:'parse_error',message:'Unrecognized line in BattOr trace: '+line});continue;}
+const ts=parseFloat(groups[1]);const voltageInV=tr.b.convertUnit(parseFloat(groups[2]),tr.b.UnitPrefixScale.METRIC.MILLI,tr.b.UnitPrefixScale.METRIC.NONE);const currentInA=tr.b.convertUnit(parseFloat(groups[3]),tr.b.UnitPrefixScale.METRIC.MILLI,tr.b.UnitPrefixScale.METRIC.NONE);const syncId=groups[4];if(syncId){this.syncTimestampsById_.set(syncId,ts);}
+if(voltageInV<0||currentInA<0){this.model_.importWarning({type:'parse_error',message:'The following line in the BattOr trace has a negative '+'voltage or current, neither of which are allowed: '+line+'. A common cause of this is that the device is charging '+'while the trace is being recorded.'});continue;}
+this.samples_.push(new Sample(ts,voltageInV,currentInA));}}};function Sample(ts,voltageInV,currentInA){this.ts=ts;this.voltageInV=voltageInV;this.currentInA=currentInA;}
+Sample.prototype={get powerInW(){return this.voltageInV*this.currentInA;}};tr.importer.Importer.register(BattorImporter);return{BattorImporter,};});'use strict';tr.exportTo('tr.e.importer.ddms',function(){const kPid=0;const kCategory='java';const kMethodLutEndMarker='\n*end\n';const kThreadsStart='\n*threads\n';const kMethodsStart='\n*methods\n';const kTraceMethodEnter=0x00;const kTraceMethodExit=0x01;const kTraceUnroll=0x02;const kTraceMethodActionMask=0x03;const kTraceHeaderLength=32;const kTraceMagicValue=0x574f4c53;const kTraceVersionSingleClock=2;const kTraceVersionDualClock=3;const kTraceRecordSizeSingleClock=10;const kTraceRecordSizeDualClock=14;function Reader(stringPayload){this.position_=0;this.data_=new Uint8Array(stringPayload.length);for(let i=0;i<stringPayload.length;++i){this.data_[i]=stringPayload.charCodeAt(i);}}
+Reader.prototype={__proto__:Object.prototype,uint8(){const result=this.data_[this.position_];this.position_+=1;return result;},uint16(){let result=0;result+=this.uint8();result+=this.uint8()<<8;return result;},uint32(){let result=0;result+=this.uint8();result+=this.uint8()<<8;result+=this.uint8()<<16;result+=this.uint8()<<24;return result;},uint64(){const low=this.uint32();const high=this.uint32();const lowStr=('0000000'+low.toString(16)).substr(-8);const highStr=('0000000'+high.toString(16)).substr(-8);const result=highStr+lowStr;return result;},seekTo(position){this.position_=position;},hasMore(){return this.position_<this.data_.length;}};function DdmsImporter(model,data){this.importPriority=3;this.model_=model;this.data_=data;}
+DdmsImporter.canImport=function(data){if(typeof(data)==='string'||data instanceof String){const header=data.slice(0,1000);return header.startsWith('*version\n')&&header.indexOf('\nvm=')>=0&&header.indexOf(kThreadsStart)>=0;}
+return false;};DdmsImporter.prototype={__proto__:tr.importer.Importer.prototype,get importerName(){return'DdmsImporter';},get model(){return this.model_;},importEvents(){const divider=this.data_.indexOf(kMethodLutEndMarker)+
+kMethodLutEndMarker.length;this.metadata_=this.data_.slice(0,divider);this.methods_={};this.parseThreads();this.parseMethods();const traceReader=new Reader(this.data_.slice(divider));const magic=traceReader.uint32();if(magic!==kTraceMagicValue){throw Error('Failed to match magic value');}
+this.version_=traceReader.uint16();if(this.version_!==kTraceVersionDualClock){throw Error('Unknown version');}
+const dataOffest=traceReader.uint16();const startDateTime=traceReader.uint64();const recordSize=traceReader.uint16();traceReader.seekTo(dataOffest);while(traceReader.hasMore()){this.parseTraceEntry(traceReader);}},parseTraceEntry(reader){const tid=reader.uint16();const methodPacked=reader.uint32();const cpuSinceStart=reader.uint32();const wallClockSinceStart=reader.uint32();let method=methodPacked&~kTraceMethodActionMask;const action=methodPacked&kTraceMethodActionMask;const thread=this.getTid(tid);method=this.getMethodName(method);if(action===kTraceMethodEnter){thread.sliceGroup.beginSlice(kCategory,method,wallClockSinceStart,undefined,cpuSinceStart);}else if(thread.sliceGroup.openSliceCount){thread.sliceGroup.endSlice(wallClockSinceStart,cpuSinceStart);}},parseThreads(){let threads=this.metadata_.slice(this.metadata_.indexOf(kThreadsStart)+
+kThreadsStart.length);threads=threads.slice(0,threads.indexOf('\n*'));threads=threads.split('\n');threads.forEach(this.parseThread.bind(this));},parseThread(threadLine){const tid=threadLine.slice(0,threadLine.indexOf('\t'));const thread=this.getTid(parseInt(tid));thread.name=threadLine.slice(threadLine.indexOf('\t')+1);},getTid(tid){return this.model_.getOrCreateProcess(kPid).getOrCreateThread(tid);},parseMethods(){let methods=this.metadata_.slice(this.metadata_.indexOf(kMethodsStart)+
+kMethodsStart.length);methods=methods.slice(0,methods.indexOf('\n*'));methods=methods.split('\n');methods.forEach(this.parseMethod.bind(this));},parseMethod(methodLine){const data=methodLine.split('\t');const methodId=parseInt(data[0]);const methodName=data[1]+'.'+data[2]+data[3];this.addMethod(methodId,methodName);},addMethod(methodId,methodName){this.methods_[methodId]=methodName;},getMethodName(methodId){return this.methods_[methodId];}};tr.importer.Importer.register(DdmsImporter);return{DdmsImporter,};});'use strict';tr.exportTo('tr.e.audits',function(){class LowMemoryAuditor extends tr.c.Auditor{constructor(model){super();this.model_=model;}
+runAnnotate(){this.model_.device.lowMemoryEvents=this.getLowMemoryEvents_();}
+getLowMemoryEvents_(){const model=this.model_;const result=[];for(const process of model.getAllProcesses()){for(const e of process.getDescendantEvents()){if(!(e instanceof tr.model.ThreadSlice)||e.duration!==0){continue;}
+if(e.category!=='lowmemory'){continue;}
+result.push(e);}}
+return result;}}
+tr.c.Auditor.register(LowMemoryAuditor);return{LowMemoryAuditor};});'use strict';function filterDuplicateTimestamps(timestamps){const dedupedTimestamps=[];let lastTs=0;for(const ts of timestamps){if(ts-lastTs>=1){dedupedTimestamps.push(ts);lastTs=ts;}}
+return dedupedTimestamps;}
+tr.exportTo('tr.e.audits',function(){const VSYNC_COUNTER_PRECISIONS={'android.VSYNC-app':15,'android.VSYNC':15};const VSYNC_SLICE_PRECISIONS={'RenderWidgetHostViewAndroid::OnVSync':5,'VSYNC':10,'vblank':10,'DisplayLinkMac::GetVSyncParameters':5};const BEGIN_FRAME_SLICE_PRECISION={'DisplayScheduler::BeginFrame':10};function VSyncAuditor(model){tr.c.Auditor.call(this,model);}
+VSyncAuditor.prototype={__proto__:tr.c.Auditor.prototype,runAnnotate(){this.model.device.vSyncTimestamps=this.findVSyncTimestamps(this.model);},findVSyncTimestamps(model){let times=[];let maxPrecision=Number.NEGATIVE_INFINITY;let maxTitle=undefined;function useInstead(title,precisions){const precision=precisions[title];if(precision===undefined)return false;if(title===maxTitle)return true;if(precision<=maxPrecision){if(precision===maxPrecision){model.importWarning({type:'VSyncAuditor',message:'Encountered two different VSync events ('+
+maxTitle+', '+title+') with the same precision, '+'ignoring the newer one ('+title+')',showToUser:false,});}
+return false;}
+maxPrecision=precision;maxTitle=title;times=[];return true;}
+for(const pid in model.processes){const process=model.processes[pid];for(const cid in process.counters){if(useInstead(cid,VSYNC_COUNTER_PRECISIONS)){const counter=process.counters[cid];for(let i=0;i<counter.series.length;i++){const series=counter.series[i];Array.prototype.push.apply(times,series.timestamps);}}}
+for(const tid in process.threads){const thread=process.threads[tid];for(let i=0;i<thread.sliceGroup.slices.length;i++){const slice=thread.sliceGroup.slices[i];if(useInstead(slice.title,VSYNC_SLICE_PRECISIONS)){times.push(slice.start);}else if(useInstead(slice.title,BEGIN_FRAME_SLICE_PRECISION)&&slice.args.args&&slice.args.args.frame_time_us){times.push(slice.args.args.frame_time_us/1000.0);}}}}
+times.sort(function(x,y){return x-y;});return filterDuplicateTimestamps(times);}};tr.c.Auditor.register(VSyncAuditor);return{VSyncAuditor,};});'use strict';tr.exportTo('tr.importer',function(){function EmptyImporter(events){this.importPriority=0;}
+EmptyImporter.canImport=function(eventData){if(eventData instanceof Array&&eventData.length===0){return true;}
+if(typeof(eventData)==='string'||eventData instanceof String){return eventData.length===0;}
+return false;};EmptyImporter.prototype={__proto__:tr.importer.Importer.prototype,get importerName(){return'EmptyImporter';}};tr.importer.Importer.register(EmptyImporter);return{EmptyImporter,};});'use strict';tr.exportTo('tr.model.um',function(){function AnimationExpectation(parentModel,initiatorTitle,start,duration){tr.model.um.UserExpectation.call(this,parentModel,initiatorTitle,start,duration);this.frameEvents_=undefined;}
+AnimationExpectation.prototype={__proto__:tr.model.um.UserExpectation.prototype,constructor:AnimationExpectation,get frameEvents(){if(this.frameEvents_){return this.frameEvents_;}
+this.frameEvents_=new tr.model.EventSet();this.associatedEvents.forEach(function(event){if(event.title===tr.model.helpers.IMPL_RENDERING_STATS){this.frameEvents_.push(event);}},this);return this.frameEvents_;}};tr.model.um.UserExpectation.subTypes.register(AnimationExpectation,{stageTitle:'Animation',colorId:tr.b.ColorScheme.getColorIdForReservedName('rail_animation')});return{AnimationExpectation,};});'use strict';tr.exportTo('tr.importer',function(){function ProtoExpectation(type,initiatorType){this.type=type;this.initiatorType=initiatorType;this.start=Infinity;this.end=-Infinity;this.associatedEvents=new tr.model.EventSet();this.isAnimationBegin=false;}
+ProtoExpectation.RESPONSE_TYPE='r';ProtoExpectation.ANIMATION_TYPE='a';ProtoExpectation.IGNORED_TYPE='ignored';const INITIATOR_HIERARCHY=[tr.model.um.INITIATOR_TYPE.PINCH,tr.model.um.INITIATOR_TYPE.FLING,tr.model.um.INITIATOR_TYPE.MOUSE_WHEEL,tr.model.um.INITIATOR_TYPE.SCROLL,tr.model.um.INITIATOR_TYPE.VR,tr.model.um.INITIATOR_TYPE.VIDEO,tr.model.um.INITIATOR_TYPE.WEBGL,tr.model.um.INITIATOR_TYPE.CSS,tr.model.um.INITIATOR_TYPE.MOUSE,tr.model.um.INITIATOR_TYPE.KEYBOARD,tr.model.um.INITIATOR_TYPE.TAP,tr.model.um.INITIATOR_TYPE.TOUCH];function combineInitiatorTypes(title1,title2){for(const item of INITIATOR_HIERARCHY){if(title1===item||title2===item)return item;}
+throw new Error('Invalid titles in combineInitiatorTypes');}
+ProtoExpectation.prototype={get isValid(){return this.end>this.start;},containsTypeNames(typeNames){return this.associatedEvents.some(x=>typeNames.indexOf(x.typeName)>=0);},containsSliceTitle(title){return this.associatedEvents.some(x=>title===x.title);},createInteractionRecord(model){if(this.type!==ProtoExpectation.IGNORED_TYPE&&!this.isValid){model.importWarning({type:'ProtoExpectation',message:'Please file a bug with this trace. '+this.debug(),showToUser:true});return undefined;}
+const duration=this.end-this.start;let ir=undefined;switch(this.type){case ProtoExpectation.RESPONSE_TYPE:ir=new tr.model.um.ResponseExpectation(model,this.initiatorType,this.start,duration,this.isAnimationBegin);break;case ProtoExpectation.ANIMATION_TYPE:ir=new tr.model.um.AnimationExpectation(model,this.initiatorType,this.start,duration);break;}
+if(!ir)return undefined;ir.sourceEvents.addEventSet(this.associatedEvents);function pushAssociatedEvents(event){ir.associatedEvents.push(event);if(event.associatedEvents){ir.associatedEvents.addEventSet(event.associatedEvents);}}
+this.associatedEvents.forEach(function(event){pushAssociatedEvents(event);if(event.subSlices){event.subSlices.forEach(pushAssociatedEvents);}});return ir;},merge(other){this.initiatorType=combineInitiatorTypes(this.initiatorType,other.initiatorType);this.associatedEvents.addEventSet(other.associatedEvents);this.start=Math.min(this.start,other.start);this.end=Math.max(this.end,other.end);if(other.isAnimationBegin){this.isAnimationBegin=true;}},pushEvent(event){this.start=Math.min(this.start,event.start);this.end=Math.max(this.end,event.end);this.associatedEvents.push(event);},pushSample(sample){this.start=Math.min(this.start,sample.timestamp);this.end=Math.max(this.end,sample.timestamp);this.associatedEvents.push(sample);},containsTimestampInclusive(timestamp){return(this.start<=timestamp)&&(timestamp<=this.end);},intersects(other){return(other.start<this.end)&&(other.end>this.start);},isNear(event,threshold){return(this.end+threshold)>event.start;},debug(){let debugString=this.type+'(';debugString+=parseInt(this.start)+' ';debugString+=parseInt(this.end);this.associatedEvents.forEach(function(event){debugString+=' '+event.typeName;});return debugString+')';}};return{ProtoExpectation,};});'use strict';tr.exportTo('tr.importer',function(){const ProtoExpectation=tr.importer.ProtoExpectation;const INITIATOR_TYPE=tr.model.um.INITIATOR_TYPE;const INPUT_TYPE=tr.e.cc.INPUT_EVENT_TYPE_NAMES;const KEYBOARD_TYPE_NAMES=[INPUT_TYPE.CHAR,INPUT_TYPE.KEY_DOWN_RAW,INPUT_TYPE.KEY_DOWN,INPUT_TYPE.KEY_UP];const MOUSE_RESPONSE_TYPE_NAMES=[INPUT_TYPE.CLICK,INPUT_TYPE.CONTEXT_MENU];const MOUSE_WHEEL_TYPE_NAMES=[INPUT_TYPE.MOUSE_WHEEL];const MOUSE_DRAG_TYPE_NAMES=[INPUT_TYPE.MOUSE_DOWN,INPUT_TYPE.MOUSE_MOVE,INPUT_TYPE.MOUSE_UP];const TAP_TYPE_NAMES=[INPUT_TYPE.TAP,INPUT_TYPE.TAP_CANCEL,INPUT_TYPE.TAP_DOWN];const PINCH_TYPE_NAMES=[INPUT_TYPE.PINCH_BEGIN,INPUT_TYPE.PINCH_END,INPUT_TYPE.PINCH_UPDATE];const FLING_TYPE_NAMES=[INPUT_TYPE.FLING_CANCEL,INPUT_TYPE.FLING_START];const TOUCH_TYPE_NAMES=[INPUT_TYPE.TOUCH_END,INPUT_TYPE.TOUCH_MOVE,INPUT_TYPE.TOUCH_START];const SCROLL_TYPE_NAMES=[INPUT_TYPE.SCROLL_BEGIN,INPUT_TYPE.SCROLL_END,INPUT_TYPE.SCROLL_UPDATE];const ALL_HANDLED_TYPE_NAMES=[].concat(KEYBOARD_TYPE_NAMES,MOUSE_RESPONSE_TYPE_NAMES,MOUSE_WHEEL_TYPE_NAMES,MOUSE_DRAG_TYPE_NAMES,PINCH_TYPE_NAMES,TAP_TYPE_NAMES,FLING_TYPE_NAMES,TOUCH_TYPE_NAMES,SCROLL_TYPE_NAMES);const RENDERER_FLING_TITLE='InputHandlerProxy::HandleGestureFling::started';const PLAYBACK_EVENT_TITLE='VideoPlayback';const CSS_ANIMATION_TITLE='Animation';const VR_COUNTER_NAMES=['gpu.WebVR FPS','gpu.WebVR frame time (ms)','gpu.WebVR pose prediction (ms)','gpu.WebXR FPS',];const VR_EXPECTATION_EVENTS={'Vr.AcquireGvrFrame':{'histogramName':'acquire_frame','description':'Duration acquire a frame from GVR','hasCpuTime':true,},'Vr.DrawFrame':{'histogramName':'draw_frame','description':'Duration to render one frame','hasCpuTime':true,},'Vr.PostSubmitDrawOnGpu':{'histogramName':'post_submit_draw_on_gpu','description':'Duration to draw a frame on GPU post submit to '+'GVR. Note this duration may include time spent on '+'reprojection','hasCpuTime':false,},'Vr.ProcessControllerInput':{'histogramName':'update_controller','description':'Duration to query input from the controller','hasCpuTime':true,},'Vr.ProcessControllerInputForWebXr':{'histogramName':'update_controller_webxr','description':'Duration to query input from the controller for WebXR','hasCpuTime':true,},'Vr.SubmitFrameNow':{'histogramName':'submit_frame','description':'Duration to submit a frame to GVR','hasCpuTime':true,}};const WEBXR_INSTANT_EVENTS={'WebXR frame time (ms)':{'javascript':{'histogramName':'webxr_frame_time_javascript','description':'WebXR frame time spent on JavaScript',},'rendering':{'histogramName':'webxr_frame_time_rendering','description':'WebXR frame time spent on rendering'}},'WebXR pose prediction':{'milliseconds':{'histogramName':'webxr_pose_prediction','description':'WebXR pose prediction in ms',},},};const XR_DEVICE_SERVICE_PROCESS='Service: xr_device_service';function isXrDeviceServiceProcess(process){if(process.name===XR_DEVICE_SERVICE_PROCESS)return true;return false;}
+const VR_RESPONSE_MS=1000;const INPUT_MERGE_THRESHOLD_MS=200;const ANIMATION_MERGE_THRESHOLD_MS=32;const MOUSE_WHEEL_THRESHOLD_MS=40;const MOUSE_MOVE_THRESHOLD_MS=40;function compareEvents(x,y){if(x.start!==y.start){return x.start-y.start;}
+if(x.end!==y.end){return x.end-y.end;}
+if(x.guid&&y.guid){return x.guid-y.guid;}
+return 0;}
+function forEventTypesIn(events,typeNames,cb,opt_this){events.forEach(function(event){if(typeNames.indexOf(event.typeName)>=0){cb.call(opt_this,event);}});}
+function causedFrame(event){return event.associatedEvents.some(isImplFrameEvent);}
+function getSortedFrameEventsByProcess(modelHelper){const frameEventsByPid={};for(const[pid,rendererHelper]of
+Object.entries(modelHelper.rendererHelpers)){frameEventsByPid[pid]=rendererHelper.getFrameEventsInRange(tr.model.helpers.IMPL_FRAMETIME_TYPE,modelHelper.model.bounds);}
+return frameEventsByPid;}
+function getSortedInputEvents(modelHelper){const inputEvents=[];const browserProcess=modelHelper.browserHelper.process;const mainThread=browserProcess.findAtMostOneThreadNamed('CrBrowserMain');for(const slice of mainThread.asyncSliceGroup.getDescendantEvents()){if(!slice.isTopLevel)continue;if(!(slice instanceof tr.e.cc.InputLatencyAsyncSlice))continue;if(isNaN(slice.start)||isNaN(slice.duration)||isNaN(slice.end)){continue;}
+inputEvents.push(slice);}
+return inputEvents.sort(compareEvents);}
+function findProtoExpectations(modelHelper,sortedInputEvents,warn){const protoExpectations=[];const handlers=[handleKeyboardEvents,handleMouseResponseEvents,handleMouseWheelEvents,handleMouseDragEvents,handleTapResponseEvents,handlePinchEvents,handleFlingEvents,handleTouchEvents,handleScrollEvents,handleCSSAnimations,handleWebGLAnimations,handleVideoAnimations,handleVrAnimations,];handlers.forEach(function(handler){protoExpectations.push.apply(protoExpectations,handler(modelHelper,sortedInputEvents,warn));});protoExpectations.sort(compareEvents);return protoExpectations;}
+function handleKeyboardEvents(modelHelper,sortedInputEvents,warn){const protoExpectations=[];forEventTypesIn(sortedInputEvents,KEYBOARD_TYPE_NAMES,function(event){const pe=new ProtoExpectation(ProtoExpectation.RESPONSE_TYPE,INITIATOR_TYPE.KEYBOARD);pe.pushEvent(event);protoExpectations.push(pe);});return protoExpectations;}
+function handleMouseResponseEvents(modelHelper,sortedInputEvents,warn){const protoExpectations=[];forEventTypesIn(sortedInputEvents,MOUSE_RESPONSE_TYPE_NAMES,function(event){const pe=new ProtoExpectation(ProtoExpectation.RESPONSE_TYPE,INITIATOR_TYPE.MOUSE);pe.pushEvent(event);protoExpectations.push(pe);});return protoExpectations;}
+function handleMouseWheelEvents(modelHelper,sortedInputEvents,warn){const protoExpectations=[];let currentPE=undefined;let prevEvent_=undefined;forEventTypesIn(sortedInputEvents,MOUSE_WHEEL_TYPE_NAMES,function(event){const prevEvent=prevEvent_;prevEvent_=event;if(currentPE&&(prevEvent.start+MOUSE_WHEEL_THRESHOLD_MS)>=event.start){if(currentPE.type===ProtoExpectation.ANIMATION_TYPE){currentPE.pushEvent(event);}else{currentPE=new ProtoExpectation(ProtoExpectation.ANIMATION_TYPE,INITIATOR_TYPE.MOUSE_WHEEL);currentPE.pushEvent(event);protoExpectations.push(currentPE);}
+return;}
+currentPE=new ProtoExpectation(ProtoExpectation.RESPONSE_TYPE,INITIATOR_TYPE.MOUSE_WHEEL);currentPE.pushEvent(event);protoExpectations.push(currentPE);});return protoExpectations;}
+function handleMouseDragEvents(modelHelper,sortedInputEvents,warn){const protoExpectations=[];let currentPE=undefined;let mouseDownEvent=undefined;forEventTypesIn(sortedInputEvents,MOUSE_DRAG_TYPE_NAMES,function(event){switch(event.typeName){case INPUT_TYPE.MOUSE_DOWN:if(causedFrame(event)){const pe=new ProtoExpectation(ProtoExpectation.RESPONSE_TYPE,INITIATOR_TYPE.MOUSE);pe.pushEvent(event);protoExpectations.push(pe);}else{mouseDownEvent=event;}
+break;case INPUT_TYPE.MOUSE_MOVE:if(!causedFrame(event)){const pe=new ProtoExpectation(ProtoExpectation.IGNORED_TYPE);pe.pushEvent(event);protoExpectations.push(pe);}else if(!currentPE||!currentPE.isNear(event,MOUSE_MOVE_THRESHOLD_MS)){currentPE=new ProtoExpectation(ProtoExpectation.RESPONSE_TYPE,INITIATOR_TYPE.MOUSE);currentPE.pushEvent(event);if(mouseDownEvent){currentPE.associatedEvents.push(mouseDownEvent);mouseDownEvent=undefined;}
+protoExpectations.push(currentPE);}else{if(currentPE.type===ProtoExpectation.ANIMATION_TYPE){currentPE.pushEvent(event);}else{currentPE=new ProtoExpectation(ProtoExpectation.ANIMATION_TYPE,INITIATOR_TYPE.MOUSE);currentPE.pushEvent(event);protoExpectations.push(currentPE);}}
+break;case INPUT_TYPE.MOUSE_UP:if(!mouseDownEvent){const pe=new ProtoExpectation(causedFrame(event)?ProtoExpectation.RESPONSE_TYPE:ProtoExpectation.IGNORED_TYPE,INITIATOR_TYPE.MOUSE);pe.pushEvent(event);protoExpectations.push(pe);break;}
+if(currentPE){currentPE.pushEvent(event);}else{currentPE=new ProtoExpectation(ProtoExpectation.RESPONSE_TYPE,INITIATOR_TYPE.MOUSE);if(mouseDownEvent){currentPE.associatedEvents.push(mouseDownEvent);}
+currentPE.pushEvent(event);protoExpectations.push(currentPE);}
+mouseDownEvent=undefined;currentPE=undefined;break;}});if(mouseDownEvent){currentPE=new ProtoExpectation(ProtoExpectation.IGNORED_TYPE);currentPE.pushEvent(mouseDownEvent);protoExpectations.push(currentPE);}
+return protoExpectations;}
+function handleTapResponseEvents(modelHelper,sortedInputEvents,warn){const protoExpectations=[];let currentPE=undefined;forEventTypesIn(sortedInputEvents,TAP_TYPE_NAMES,function(event){switch(event.typeName){case INPUT_TYPE.TAP_DOWN:currentPE=new ProtoExpectation(ProtoExpectation.RESPONSE_TYPE,INITIATOR_TYPE.TAP);currentPE.pushEvent(event);protoExpectations.push(currentPE);break;case INPUT_TYPE.TAP:if(currentPE){currentPE.pushEvent(event);}else{currentPE=new ProtoExpectation(ProtoExpectation.RESPONSE_TYPE,INITIATOR_TYPE.TAP);currentPE.pushEvent(event);protoExpectations.push(currentPE);}
+currentPE=undefined;break;case INPUT_TYPE.TAP_CANCEL:if(!currentPE){const pe=new ProtoExpectation(ProtoExpectation.IGNORED_TYPE);pe.pushEvent(event);protoExpectations.push(pe);break;}
+if(currentPE.isNear(event,INPUT_MERGE_THRESHOLD_MS)){currentPE.pushEvent(event);}else{currentPE=new ProtoExpectation(ProtoExpectation.RESPONSE_TYPE,INITIATOR_TYPE.TAP);currentPE.pushEvent(event);protoExpectations.push(currentPE);}
+currentPE=undefined;break;}});return protoExpectations;}
+function handlePinchEvents(modelHelper,sortedInputEvents,warn){const protoExpectations=[];let currentPE=undefined;let sawFirstUpdate=false;const modelBounds=modelHelper.model.bounds;forEventTypesIn(sortedInputEvents,PINCH_TYPE_NAMES,function(event){switch(event.typeName){case INPUT_TYPE.PINCH_BEGIN:if(currentPE&&currentPE.isNear(event,INPUT_MERGE_THRESHOLD_MS)){currentPE.pushEvent(event);break;}
+currentPE=new ProtoExpectation(ProtoExpectation.RESPONSE_TYPE,INITIATOR_TYPE.PINCH);currentPE.pushEvent(event);currentPE.isAnimationBegin=true;protoExpectations.push(currentPE);sawFirstUpdate=false;break;case INPUT_TYPE.PINCH_UPDATE:if(!currentPE||((currentPE.type===ProtoExpectation.RESPONSE_TYPE)&&sawFirstUpdate)||!currentPE.isNear(event,INPUT_MERGE_THRESHOLD_MS)){currentPE=new ProtoExpectation(ProtoExpectation.ANIMATION_TYPE,INITIATOR_TYPE.PINCH);currentPE.pushEvent(event);protoExpectations.push(currentPE);}else{currentPE.pushEvent(event);sawFirstUpdate=true;}
+break;case INPUT_TYPE.PINCH_END:if(currentPE){currentPE.pushEvent(event);}else{const pe=new ProtoExpectation(ProtoExpectation.IGNORED_TYPE);pe.pushEvent(event);protoExpectations.push(pe);}
+currentPE=undefined;break;}});return protoExpectations;}
+function handleFlingEvents(modelHelper,sortedInputEvents,warn){const protoExpectations=[];let currentPE=undefined;function isRendererFling(event){return event.title===RENDERER_FLING_TITLE;}
+const browserHelper=modelHelper.browserHelper;const flingEvents=browserHelper.getAllAsyncSlicesMatching(isRendererFling);forEventTypesIn(sortedInputEvents,FLING_TYPE_NAMES,function(event){flingEvents.push(event);});flingEvents.sort(compareEvents);flingEvents.forEach(function(event){if(event.title===RENDERER_FLING_TITLE){if(currentPE){currentPE.pushEvent(event);}else{currentPE=new ProtoExpectation(ProtoExpectation.ANIMATION_TYPE,INITIATOR_TYPE.FLING);currentPE.pushEvent(event);protoExpectations.push(currentPE);}
+return;}
+switch(event.typeName){case INPUT_TYPE.FLING_START:if(currentPE){warn({type:'UserModelBuilder',message:'Unexpected FlingStart',showToUser:false,});currentPE.pushEvent(event);}else{currentPE=new ProtoExpectation(ProtoExpectation.ANIMATION_TYPE,INITIATOR_TYPE.FLING);currentPE.pushEvent(event);currentPE.end=0;protoExpectations.push(currentPE);}
+break;case INPUT_TYPE.FLING_CANCEL:if(currentPE){currentPE.pushEvent(event);currentPE.end=event.start;currentPE=undefined;}else{const pe=new ProtoExpectation(ProtoExpectation.IGNORED_TYPE);pe.pushEvent(event);protoExpectations.push(pe);}
+break;}});if(currentPE&&!currentPE.end){currentPE.end=modelHelper.model.bounds.max;}
+return protoExpectations;}
+function handleTouchEvents(modelHelper,sortedInputEvents,warn){const protoExpectations=[];let currentPE=undefined;let sawFirstMove=false;forEventTypesIn(sortedInputEvents,TOUCH_TYPE_NAMES,function(event){switch(event.typeName){case INPUT_TYPE.TOUCH_START:if(currentPE){currentPE.pushEvent(event);}else{currentPE=new ProtoExpectation(ProtoExpectation.RESPONSE_TYPE,INITIATOR_TYPE.TOUCH);currentPE.pushEvent(event);currentPE.isAnimationBegin=true;protoExpectations.push(currentPE);sawFirstMove=false;}
+break;case INPUT_TYPE.TOUCH_MOVE:if(!currentPE){currentPE=new ProtoExpectation(ProtoExpectation.ANIMATION_TYPE,INITIATOR_TYPE.TOUCH);currentPE.pushEvent(event);protoExpectations.push(currentPE);break;}
+if((sawFirstMove&&(currentPE.type===ProtoExpectation.RESPONSE_TYPE))||!currentPE.isNear(event,INPUT_MERGE_THRESHOLD_MS)){const prevEnd=currentPE.end;currentPE=new ProtoExpectation(ProtoExpectation.ANIMATION_TYPE,INITIATOR_TYPE.TOUCH);currentPE.pushEvent(event);currentPE.start=prevEnd;protoExpectations.push(currentPE);}else{currentPE.pushEvent(event);sawFirstMove=true;}
+break;case INPUT_TYPE.TOUCH_END:if(!currentPE){const pe=new ProtoExpectation(ProtoExpectation.IGNORED_TYPE);pe.pushEvent(event);protoExpectations.push(pe);break;}
+if(currentPE.isNear(event,INPUT_MERGE_THRESHOLD_MS)){currentPE.pushEvent(event);}else{const pe=new ProtoExpectation(ProtoExpectation.IGNORED_TYPE);pe.pushEvent(event);protoExpectations.push(pe);}
+currentPE=undefined;break;}});return protoExpectations;}
+function handleScrollEvents(modelHelper,sortedInputEvents,warn){const protoExpectations=[];let currentPE=undefined;let sawFirstUpdate=false;forEventTypesIn(sortedInputEvents,SCROLL_TYPE_NAMES,function(event){switch(event.typeName){case INPUT_TYPE.SCROLL_BEGIN:currentPE=new ProtoExpectation(ProtoExpectation.RESPONSE_TYPE,INITIATOR_TYPE.SCROLL);currentPE.pushEvent(event);currentPE.isAnimationBegin=true;protoExpectations.push(currentPE);sawFirstUpdate=false;break;case INPUT_TYPE.SCROLL_UPDATE:if(currentPE){if(currentPE.isNear(event,INPUT_MERGE_THRESHOLD_MS)&&((currentPE.type===ProtoExpectation.ANIMATION_TYPE)||!sawFirstUpdate)){currentPE.pushEvent(event);sawFirstUpdate=true;}else{currentPE=new ProtoExpectation(ProtoExpectation.ANIMATION_TYPE,INITIATOR_TYPE.SCROLL);currentPE.pushEvent(event);protoExpectations.push(currentPE);}}else{currentPE=new ProtoExpectation(ProtoExpectation.ANIMATION_TYPE,INITIATOR_TYPE.SCROLL);currentPE.pushEvent(event);protoExpectations.push(currentPE);}
+break;case INPUT_TYPE.SCROLL_END:if(!currentPE){warn({type:'UserModelBuilder',message:'Unexpected ScrollEnd',showToUser:false,});const pe=new ProtoExpectation(ProtoExpectation.IGNORED_TYPE);pe.pushEvent(event);protoExpectations.push(pe);break;}
+currentPE.pushEvent(event);break;}});return protoExpectations;}
+function handleVideoAnimations(modelHelper,sortedInputEvents,warn){const events=[];for(const pid in modelHelper.rendererHelpers){for(const tid in modelHelper.rendererHelpers[pid].process.threads){for(const asyncSlice of
+modelHelper.rendererHelpers[pid].process.threads[tid].asyncSliceGroup.slices){if(asyncSlice.title===PLAYBACK_EVENT_TITLE){events.push(asyncSlice);}}}}
+events.sort(tr.importer.compareEvents);const protoExpectations=[];for(const event of events){const currentPE=new ProtoExpectation(ProtoExpectation.ANIMATION_TYPE,INITIATOR_TYPE.VIDEO);currentPE.start=event.start;currentPE.end=event.end;currentPE.pushEvent(event);protoExpectations.push(currentPE);}
+return protoExpectations;}
+function handleVrAnimations(modelHelper,sortedInputEvents,warn){const events=[];const processes=[];if(typeof modelHelper.gpuHelper!=='undefined'){processes.push(modelHelper.gpuHelper.process);}
+for(const helper of Object.values(modelHelper.rendererHelpers)){processes.push(helper.process);}
+for(const helper of Object.values(modelHelper.browserHelpers)){processes.push(helper.process);}
+for(const service of modelHelper.model.getAllProcesses(isXrDeviceServiceProcess)){processes.push(service);}
+let vrCounterStart=Number.MAX_SAFE_INTEGER;let vrEventStart=Number.MAX_SAFE_INTEGER;for(const proc of processes){for(const[counterName,counterSeries]of
+Object.entries(proc.counters)){if(VR_COUNTER_NAMES.includes(counterName)){for(const series of counterSeries.series){for(const sample of series.samples){events.push(sample);vrCounterStart=Math.min(vrCounterStart,sample.timestamp);}}}}
+for(const thread of Object.values(proc.threads)){for(const container of thread.childEventContainers()){for(const slice of container.slices){if(slice.title in VR_EXPECTATION_EVENTS||slice.title in WEBXR_INSTANT_EVENTS){events.push(slice);vrEventStart=Math.min(vrEventStart,slice.start);}}}}}
+if(events.length===0){return[];}
+events.sort(function(x,y){if(x.range.min!==y.range.min){return x.range.min-y.range.min;}
+return x.guid-y.guid;});vrCounterStart=(vrCounterStart===Number.MAX_SAFE_INTEGER)?0:vrCounterStart;vrEventStart=(vrEventStart===Number.MAX_SAFE_INTEGER)?0:vrEventStart;const vrAnimationStart=Math.max(vrCounterStart,vrEventStart)+
+VR_RESPONSE_MS;const responsePE=new ProtoExpectation(ProtoExpectation.RESPONSE_TYPE,INITIATOR_TYPE.VR);const animationPE=new ProtoExpectation(ProtoExpectation.ANIMATION_TYPE,INITIATOR_TYPE.VR);let lastResponseEvent;for(const event of events){if(event.range.min<vrAnimationStart){if(event instanceof tr.model.CounterSample){responsePE.pushSample(event);}else{responsePE.pushEvent(event);}
+lastResponseEvent=event;}else{if(event instanceof tr.model.CounterSample){animationPE.pushSample(event);}else{animationPE.pushEvent(event);}}}
+if(lastResponseEvent instanceof tr.model.CounterSample){animationPE.pushSample(lastResponseEvent);}else{animationPE.pushEvent(lastResponseEvent);}
+return[responsePE,animationPE];}
+function handleCSSAnimations(modelHelper,sortedInputEvents,warn){const animationEvents=modelHelper.browserHelper.getAllAsyncSlicesMatching(function(event){return((event.title===CSS_ANIMATION_TITLE)&&event.isTopLevel&&(event.duration>0));});const animationRanges=[];function pushAnimationRange(start,end,animation){const range=tr.b.math.Range.fromExplicitRange(start,end);range.animation=animation;animationRanges.push(range);}
+animationEvents.forEach(function(animation){if(animation.subSlices.length===0){pushAnimationRange(animation.start,animation.end,animation);}else{let start=undefined;animation.subSlices.forEach(function(sub){if((sub.args.data.state==='running')&&(start===undefined)){start=sub.start;}else if((sub.args.data.state==='paused')||(sub.args.data.state==='idle')||(sub.args.data.state==='finished')){if(start===undefined){start=modelHelper.model.bounds.min;}
+pushAnimationRange(start,sub.start,animation);start=undefined;}});if(start!==undefined){pushAnimationRange(start,animation.end,animation);}}});return animationRanges.map(function(range){const protoExpectation=new ProtoExpectation(ProtoExpectation.ANIMATION_TYPE,INITIATOR_TYPE.CSS);protoExpectation.start=range.min;protoExpectation.end=range.max;protoExpectation.associatedEvents.push(range.animation);return protoExpectation;});}
+function findWebGLEvents(modelHelper,mailboxEvents,animationEvents){for(const event of modelHelper.model.getDescendantEvents()){if(event.title==='DrawingBuffer::prepareMailbox'){mailboxEvents.push(event);}else if(event.title==='PageAnimator::serviceScriptedAnimations'){animationEvents.push(event);}}}
+function findMailboxEventsNearAnimationEvents(mailboxEvents,animationEvents){if(animationEvents.length===0)return[];mailboxEvents.sort(compareEvents);animationEvents.sort(compareEvents);const animationIterator=animationEvents[Symbol.iterator]();let animationEvent=animationIterator.next().value;const filteredEvents=[];for(const event of mailboxEvents){while(animationEvent&&(animationEvent.start<(event.start-ANIMATION_MERGE_THRESHOLD_MS))){animationEvent=animationIterator.next().value;}
+if(!animationEvent)break;if(animationEvent.start<(event.start+ANIMATION_MERGE_THRESHOLD_MS)){filteredEvents.push(event);}}
+return filteredEvents;}
+function createProtoExpectationsFromMailboxEvents(mailboxEvents){const protoExpectations=[];let currentPE=undefined;for(const event of mailboxEvents){if(currentPE===undefined||!currentPE.isNear(event,ANIMATION_MERGE_THRESHOLD_MS)){currentPE=new ProtoExpectation(ProtoExpectation.ANIMATION_TYPE,INITIATOR_TYPE.WEBGL);currentPE.pushEvent(event);protoExpectations.push(currentPE);}else{currentPE.pushEvent(event);}}
+return protoExpectations;}
+function handleWebGLAnimations(modelHelper,sortedInputEvents,warn){const prepareMailboxEvents=[];const scriptedAnimationEvents=[];findWebGLEvents(modelHelper,prepareMailboxEvents,scriptedAnimationEvents);const webGLMailboxEvents=findMailboxEventsNearAnimationEvents(prepareMailboxEvents,scriptedAnimationEvents);return createProtoExpectationsFromMailboxEvents(webGLMailboxEvents);}
+function postProcessProtoExpectations(modelHelper,protoExpectations){protoExpectations=findFrameEventsForAnimations(modelHelper,protoExpectations);protoExpectations=mergeIntersectingResponses(protoExpectations);protoExpectations=mergeIntersectingAnimations(protoExpectations);protoExpectations=fixResponseAnimationStarts(protoExpectations);protoExpectations=fixTapResponseTouchAnimations(protoExpectations);return protoExpectations;}
+function mergeIntersectingResponses(protoExpectations){const newPEs=[];while(protoExpectations.length){const pe=protoExpectations.shift();newPEs.push(pe);if(pe.type!==ProtoExpectation.RESPONSE_TYPE)continue;for(let i=0;i<protoExpectations.length;++i){const otherPE=protoExpectations[i];if(otherPE.type!==pe.type)continue;if(!otherPE.intersects(pe))continue;const typeNames=pe.associatedEvents.map(function(event){return event.typeName;});if(otherPE.containsTypeNames(typeNames))continue;pe.merge(otherPE);protoExpectations.splice(i,1);--i;}}
+return newPEs;}
+function mergeIntersectingAnimations(protoExpectations){const newPEs=[];while(protoExpectations.length){const pe=protoExpectations.shift();newPEs.push(pe);if(pe.type!==ProtoExpectation.ANIMATION_TYPE)continue;const isCSS=pe.initiatorType===INITIATOR_TYPE.CSS;const isFling=pe.containsTypeNames([INPUT_TYPE.FLING_START]);const isVideo=pe.initiatorType===INITIATOR_TYPE.VIDEO;for(let i=0;i<protoExpectations.length;++i){const otherPE=protoExpectations[i];if(otherPE.type!==pe.type)continue;if((isCSS&&otherPE.initiatorType!==INITIATOR_TYPE.CSS)||isFling!==otherPE.containsTypeNames([INPUT_TYPE.FLING_START])||isVideo&&otherPE.initiatorType!==INITIATOR_TYPE.VIDEO||otherPE.initiatorType===INITIATOR_TYPE.VR){continue;}
+if(isCSS){if(!pe.isNear(otherPE,ANIMATION_MERGE_THRESHOLD_MS)){continue;}}else if(!otherPE.intersects(pe)){continue;}
+pe.merge(otherPE);protoExpectations.splice(i,1);--i;}}
+return newPEs;}
+function fixResponseAnimationStarts(protoExpectations){protoExpectations.forEach(function(ape){if(ape.type!==ProtoExpectation.ANIMATION_TYPE){return;}
+protoExpectations.forEach(function(rpe){if(rpe.type!==ProtoExpectation.RESPONSE_TYPE){return;}
+if(!ape.containsTimestampInclusive(rpe.end)){return;}
+if(ape.containsTimestampInclusive(rpe.start)){return;}
+ape.start=rpe.end;if(ape.associatedEvents!==undefined){ape.associatedEvents=ape.associatedEvents.filter(e=>(!isImplFrameEvent(e)||e.start>=ape.start));}});});return protoExpectations;}
+function isImplFrameEvent(event){return event.title===tr.model.helpers.IMPL_RENDERING_STATS;}
+function fixTapResponseTouchAnimations(protoExpectations){function isTapResponse(pe){return(pe.type===ProtoExpectation.RESPONSE_TYPE)&&pe.containsTypeNames([INPUT_TYPE.TAP]);}
+function isTouchAnimation(pe){return(pe.type===ProtoExpectation.ANIMATION_TYPE)&&pe.containsTypeNames([INPUT_TYPE.TOUCH_MOVE])&&!pe.containsTypeNames([INPUT_TYPE.SCROLL_UPDATE,INPUT_TYPE.PINCH_UPDATE]);}
+const newPEs=[];while(protoExpectations.length){const pe=protoExpectations.shift();newPEs.push(pe);const peIsTapResponse=isTapResponse(pe);const peIsTouchAnimation=isTouchAnimation(pe);if(!peIsTapResponse&&!peIsTouchAnimation){continue;}
+for(let i=0;i<protoExpectations.length;++i){const otherPE=protoExpectations[i];if(!otherPE.intersects(pe))continue;if(peIsTapResponse&&!isTouchAnimation(otherPE))continue;if(peIsTouchAnimation&&!isTapResponse(otherPE))continue;pe.type=ProtoExpectation.RESPONSE_TYPE;pe.merge(otherPE);protoExpectations.splice(i,1);--i;}}
+return newPEs;}
+function findFrameEventsForAnimations(modelHelper,protoExpectations){const newPEs=[];const frameEventsByPid=getSortedFrameEventsByProcess(modelHelper);for(const pe of protoExpectations){if(pe.type!==ProtoExpectation.ANIMATION_TYPE){newPEs.push(pe);continue;}
+const frameEvents=[];for(const pid of Object.keys(modelHelper.rendererHelpers)){const range=tr.b.math.Range.fromExplicitRange(pe.start,pe.end);frameEvents.push.apply(frameEvents,range.filterArray(frameEventsByPid[pid],e=>e.start));}
+if(frameEvents.length===0&&!(pe.initiatorType===INITIATOR_TYPE.WEBGL||pe.initiatorType===INITIATOR_TYPE.VR)){pe.type=ProtoExpectation.IGNORED_TYPE;newPEs.push(pe);continue;}
+pe.associatedEvents.addEventSet(frameEvents);newPEs.push(pe);}
+return newPEs;}
+function checkAllInputEventsHandled(modelHelper,sortedInputEvents,protoExpectations,warn){const handledEvents=[];protoExpectations.forEach(function(protoExpectation){protoExpectation.associatedEvents.forEach(function(event){if((event.title===CSS_ANIMATION_TITLE)&&(event.subSlices.length>0)){return;}
+if((handledEvents.indexOf(event)>=0)&&(!isImplFrameEvent(event))){warn({type:'UserModelBuilder',message:`double-handled event: ${event.typeName} @ ${event.start}`,showToUser:false,});return;}
+handledEvents.push(event);});});sortedInputEvents.forEach(function(event){if(handledEvents.indexOf(event)<0){warn({type:'UserModelBuilder',message:`double-handled event: ${event.typeName} @ ${event.start}`,showToUser:false,});}});}
+function findInputExpectations(modelHelper){let warning;function warn(w){if(warning)return;warning=w;}
+const sortedInputEvents=getSortedInputEvents(modelHelper);let protoExpectations=findProtoExpectations(modelHelper,sortedInputEvents,warn);protoExpectations=postProcessProtoExpectations(modelHelper,protoExpectations);checkAllInputEventsHandled(modelHelper,sortedInputEvents,protoExpectations,warn);if(warning)modelHelper.model.importWarning(warning);const expectations=[];protoExpectations.forEach(function(protoExpectation){const ir=protoExpectation.createInteractionRecord(modelHelper.model);if(ir){expectations.push(ir);}});return expectations;}
+return{findInputExpectations,compareEvents,CSS_ANIMATION_TITLE,VR_EXPECTATION_EVENTS,WEBXR_INSTANT_EVENTS,};});'use strict';tr.exportTo('tr.b',function(){class FixedColorScheme{constructor(namesToColors){this.namesToColors_=namesToColors;}
+static fromNames(names){const namesToColors=new Map();const generator=new tr.b.SinebowColorGenerator();for(const name of names){namesToColors.set(name,generator.colorForKey(name));}
+return new FixedColorScheme(namesToColors);}
+getColor(name){const color=this.namesToColors_.get(name);if(color===undefined)throw new Error('Unknown color: '+name);return color;}}
+const MemoryColumnColorScheme=new FixedColorScheme(new Map([['used_memory_column',new tr.b.Color(0,0,255)],['older_used_memory_column',new tr.b.Color(153,204,255)],['tracing_memory_column',new tr.b.Color(153,153,153)]]));function FixedColorSchemeRegistry(){}
+FixedColorSchemeRegistry.lookUp=function(name){const info=this.findTypeInfoMatching(info=>info.metadata.name===name);if(!info)return undefined;return info.constructor();};const options=new tr.b.ExtensionRegistryOptions(tr.b.BASIC_REGISTRY_MODE);tr.b.decorateExtensionRegistry(FixedColorSchemeRegistry,options);return{MemoryColumnColorScheme,FixedColorScheme,FixedColorSchemeRegistry,};});'use strict';tr.exportTo('tr.e.chrome.chrome_processes',function(){const CHROME_PROCESS_NAMES={BROWSER:'browser_process',RENDERER:'renderer_processes',ALL:'all_processes',GPU:'gpu_process',PPAPI:'ppapi_process',UNKNOWN:'unknown_processes',};const PROCESS_COLOR_SCHEME_NAME='ChromeProcessNames';const PROCESS_COLOR_SCHEME=tr.b.FixedColorScheme.fromNames(Object.values(CHROME_PROCESS_NAMES));tr.b.FixedColorSchemeRegistry.register(()=>PROCESS_COLOR_SCHEME,{name:PROCESS_COLOR_SCHEME_NAME,});function canonicalizeName(name){return name.toLowerCase().replace(' ','_');}
+function canonicalizeProcessName(rawProcessName){if(!rawProcessName)return CHROME_PROCESS_NAMES.UNKNOWN;const baseCanonicalName=canonicalizeName(rawProcessName);switch(baseCanonicalName){case'renderer':return CHROME_PROCESS_NAMES.RENDERER;case'browser':return CHROME_PROCESS_NAMES.BROWSER;}
+if(Object.values(CHROME_PROCESS_NAMES).includes(baseCanonicalName)){return baseCanonicalName;}
+return CHROME_PROCESS_NAMES.UNKNOWN;}
+return{CHROME_PROCESS_NAMES,PROCESS_COLOR_SCHEME,PROCESS_COLOR_SCHEME_NAME,canonicalizeName,canonicalizeProcessName,};});'use strict';tr.exportTo('tr.metrics.sh',function(){function perceptualBlend(ir,index,score){return Math.exp(1-score);}
+function filterExpectationsByRange(irs,opt_range){const filteredExpectations=[];irs.forEach(function(ir){if(!(ir instanceof tr.model.um.UserExpectation))return;if(!opt_range||opt_range.intersectsExplicitRangeInclusive(ir.start,ir.end)){filteredExpectations.push(ir);}});return filteredExpectations;}
+function splitGlobalDumpsByBrowserName(model,opt_rangeOfInterest){const chromeModelHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);const browserNameToGlobalDumps=new Map();const globalDumpToBrowserHelper=new WeakMap();if(chromeModelHelper){chromeModelHelper.browserHelpers.forEach(function(helper){const globalDumps=skipDumpsThatDoNotIntersectRange(helper.process.memoryDumps.map(d=>d.globalMemoryDump),opt_rangeOfInterest);globalDumps.forEach(function(globalDump){const existingHelper=globalDumpToBrowserHelper.get(globalDump);if(existingHelper!==undefined){throw new Error('Memory dump ID clash across multiple browsers '+'with PIDs: '+existingHelper.pid+' and '+helper.pid);}
+globalDumpToBrowserHelper.set(globalDump,helper);});makeKeyUniqueAndSet(browserNameToGlobalDumps,tr.e.chrome.chrome_processes.canonicalizeName(helper.browserName),globalDumps);});}
+const unclassifiedGlobalDumps=skipDumpsThatDoNotIntersectRange(model.globalMemoryDumps.filter(g=>!globalDumpToBrowserHelper.has(g)),opt_rangeOfInterest);if(unclassifiedGlobalDumps.length>0){makeKeyUniqueAndSet(browserNameToGlobalDumps,'unknown_browser',unclassifiedGlobalDumps);}
+return browserNameToGlobalDumps;}
+function makeKeyUniqueAndSet(map,key,value){let uniqueKey=key;let nextIndex=2;while(map.has(uniqueKey)){uniqueKey=key+nextIndex;nextIndex++;}
+map.set(uniqueKey,value);}
+function skipDumpsThatDoNotIntersectRange(dumps,opt_range){if(!opt_range)return dumps;return dumps.filter(d=>opt_range.intersectsExplicitRangeInclusive(d.start,d.end));}
+function hasCategoryAndName(event,category,title){return event.title===title&&event.category&&tr.b.getCategoryParts(event.category).includes(category);}
+return{hasCategoryAndName,filterExpectationsByRange,perceptualBlend,splitGlobalDumpsByBrowserName};});'use strict';tr.exportTo('tr.e.chrome',function(){const CHROME_INTERNAL_URLS=['','about:blank','data:text/html,pluginplaceholderdata','chrome-error://chromewebdata/'];const SCHEDULER_TOP_LEVEL_TASK_TITLE='ThreadControllerImpl::RunTask';const SCHEDULER_TOP_LEVEL_TASKS=new Set([SCHEDULER_TOP_LEVEL_TASK_TITLE,'ThreadControllerImpl::DoWork','TaskQueueManager::ProcessTaskFromWorkQueue']);class EventFinderUtils{static hasCategoryAndName(event,category,title){return event.title===title&&event.category&&tr.b.getCategoryParts(event.category).includes(category);}
+static*getMainThreadEvents(rendererHelper,eventTitle,eventCategory){if(!rendererHelper.mainThread)return;for(const ev of rendererHelper.mainThread.sliceGroup.childEvents()){if(rendererHelper.isTelemetryInternalEvent(ev))continue;if(!this.hasCategoryAndName(ev,eventCategory,eventTitle)){continue;}
+yield ev;}}
+static getNetworkEventsInRange(process,range){const networkEvents=[];for(const thread of Object.values(process.threads)){const threadHelper=new tr.model.helpers.ChromeThreadHelper(thread);const events=threadHelper.getNetworkEvents();for(const event of events){if(range.intersectsExplicitRangeInclusive(event.start,event.end)){networkEvents.push(event);}}}
+return networkEvents;}
+static getSortedMainThreadEventsByFrame(rendererHelper,eventTitle,eventCategory){const eventsByFrame=new Map();const events=this.getMainThreadEvents(rendererHelper,eventTitle,eventCategory);for(const ev of events){const frameIdRef=ev.args.frame;if(frameIdRef===undefined)continue;if(!eventsByFrame.has(frameIdRef)){eventsByFrame.set(frameIdRef,[]);}
+eventsByFrame.get(frameIdRef).push(ev);}
+return eventsByFrame;}
+static getSortedMainThreadEventsByNavId(rendererHelper,eventTitle,eventCategory){const eventsByNavId=new Map();const events=this.getMainThreadEvents(rendererHelper,eventTitle,eventCategory);for(const ev of events){if(ev.args.data===undefined)continue;const navIdRef=ev.args.data.navigationId;if(navIdRef===undefined)continue;eventsByNavId.set(navIdRef,ev);}
+return eventsByNavId;}
+static findLastEventStartingOnOrBeforeTimestamp(sortedEvents,timestamp){const firstIndexAfterTimestamp=tr.b.findFirstTrueIndexInSortedArray(sortedEvents,e=>e.start>timestamp);if(firstIndexAfterTimestamp===0)return undefined;return sortedEvents[firstIndexAfterTimestamp-1];}
+static findLastEventStartingBeforeTimestamp(sortedEvents,timestamp){const firstIndexAfterTimestamp=tr.b.findFirstTrueIndexInSortedArray(sortedEvents,e=>e.start>=timestamp);if(firstIndexAfterTimestamp===0)return undefined;return sortedEvents[firstIndexAfterTimestamp-1];}
+static findNextEventStartingOnOrAfterTimestamp(sortedEvents,timestamp){const firstIndexOnOrAfterTimestamp=tr.b.findFirstTrueIndexInSortedArray(sortedEvents,e=>e.start>=timestamp);if(firstIndexOnOrAfterTimestamp===sortedEvents.length){return undefined;}
+return sortedEvents[firstIndexOnOrAfterTimestamp];}
+static findNextEventStartingAfterTimestamp(sortedEvents,timestamp){const firstIndexOnOrAfterTimestamp=tr.b.findFirstTrueIndexInSortedArray(sortedEvents,e=>e.start>timestamp);if(firstIndexOnOrAfterTimestamp===sortedEvents.length){return undefined;}
+return sortedEvents[firstIndexOnOrAfterTimestamp];}
+static findToplevelSchedulerTasks(mainThread){const tasks=[];for(const task of mainThread.findTopmostSlices(slice=>slice.category==='toplevel'&&SCHEDULER_TOP_LEVEL_TASKS.has(slice.title))){tasks.push(task);}
+return tasks;}}
+return{EventFinderUtils,CHROME_INTERNAL_URLS,SCHEDULER_TOP_LEVEL_TASK_TITLE,};});'use strict';tr.exportTo('tr.e.chrome',function(){const TIME_TO_INTERACTIVE_WINDOW_SIZE_MS=5000;const ACTIVE_REQUEST_TOLERANCE=2;const FCI_MIN_CLUSTER_SEPARATION_MS=1000;const TASK_CLUSTER_HEAVINESS_THRESHOLD_MS=250;const ENDPOINT_TYPES={LONG_TASK_START:'LONG_TASK_START',LONG_TASK_END:'LONG_TASK_END',REQUEST_START:'REQUEST_START',REQUEST_END:'REQUEST_END'};function getEndpoints_(events,startType,endType){const endpoints=[];for(const event of events){endpoints.push({time:event.start,type:startType});endpoints.push({time:event.end,type:endType});}
+return endpoints;}
+function reachedTTIQuiscence_(timestamp,networkQuietWindowStart,mainThreadQuietWindowStart){if(networkQuietWindowStart===undefined||mainThreadQuietWindowStart===undefined){return false;}
+const mainThreadQuietForLongEnough=timestamp-mainThreadQuietWindowStart>=TIME_TO_INTERACTIVE_WINDOW_SIZE_MS;const networkQuietForLongEnough=timestamp-networkQuietWindowStart>=TIME_TO_INTERACTIVE_WINDOW_SIZE_MS;return mainThreadQuietForLongEnough&&networkQuietForLongEnough;}
+function findInteractiveTime(searchBegin,searchEnd,domContentLoadedEnd,longTasksInWindow,networkRequests){const longTaskEndpoints=getEndpoints_(longTasksInWindow,ENDPOINT_TYPES.LONG_TASK_START,ENDPOINT_TYPES.LONG_TASK_END);const networkRequestEndpoints=getEndpoints_(networkRequests,ENDPOINT_TYPES.REQUEST_START,ENDPOINT_TYPES.REQUEST_END);const endpoints=longTaskEndpoints.concat(networkRequestEndpoints);endpoints.sort((a,b)=>a.time-b.time);let networkQuietWindowStart=searchBegin;let mainThreadQuietWindowStart=searchBegin;let interactiveCandidate=undefined;let activeRequests=0;for(const endpoint of endpoints){if(reachedTTIQuiscence_(endpoint.time,networkQuietWindowStart,mainThreadQuietWindowStart)){interactiveCandidate=mainThreadQuietWindowStart;break;}
+switch(endpoint.type){case ENDPOINT_TYPES.LONG_TASK_START:mainThreadQuietWindowStart=undefined;break;case ENDPOINT_TYPES.LONG_TASK_END:mainThreadQuietWindowStart=endpoint.time;break;case ENDPOINT_TYPES.REQUEST_START:activeRequests++;if(activeRequests>ACTIVE_REQUEST_TOLERANCE){networkQuietWindowStart=undefined;}
+break;case ENDPOINT_TYPES.REQUEST_END:activeRequests--;if(activeRequests===ACTIVE_REQUEST_TOLERANCE){networkQuietWindowStart=endpoint.time;}
+break;default:throw new Error('Internal Error: Unhandled endpoint type.');}}
+if(interactiveCandidate===undefined&&reachedTTIQuiscence_(searchEnd,networkQuietWindowStart,mainThreadQuietWindowStart)){interactiveCandidate=mainThreadQuietWindowStart;}
+if(interactiveCandidate===undefined)return undefined;return Math.max(interactiveCandidate,domContentLoadedEnd);}
+function requiredFCIWindowSizeMs(timeSinceSearchBeginMs){const timeCoefficient=1/15*Math.log(2);const timeSinceSearchBeginSeconds=tr.b.convertUnit(timeSinceSearchBeginMs,tr.b.UnitPrefixScale.METRIC.MILLI,tr.b.UnitPrefixScale.METRIC.NONE);const windowSizeSeconds=4*Math.exp(-timeCoefficient*timeSinceSearchBeginSeconds)+1;return tr.b.convertUnit(windowSizeSeconds,tr.b.UnitPrefixScale.METRIC.NONE,tr.b.UnitPrefixScale.METRIC.MILLI);}
+class TaskCluster{constructor(tasksInClusterSorted){if(tasksInClusterSorted.length===0){throw new Error('Internal Error: TaskCluster must have non zero tasks');}
+for(let i=0;i<tasksInClusterSorted.length-1;i++){const durationBetweenTasks=tasksInClusterSorted[i+1].start-
+tasksInClusterSorted[i].end;if(durationBetweenTasks>=FCI_MIN_CLUSTER_SEPARATION_MS){throw new Error('Internal Error: Tasks in a TaskCluster cannot be '+'more than '+FCI_MIN_CLUSTER_SEPARATION_MS+' miliseconds apart');}
+if(durationBetweenTasks<-1e7){throw new Error('Internal Error: List of tasks used to construct '+'TaskCluster must be sorted.');}}
+this._clusterTasks=tasksInClusterSorted;}
+get start(){return this._clusterTasks[0].start;}
+get end(){return this._clusterTasks[this._clusterTasks.length-1].end;}
+isHeavy(){return this.end-this.start>TASK_CLUSTER_HEAVINESS_THRESHOLD_MS;}}
+function findFCITaskClusters(sortedLongTasks){const clusters=[];if(sortedLongTasks.length===0)return clusters;const firstTask=sortedLongTasks[0];const restOfTasks=sortedLongTasks.slice(1);let currentClusterTasks=[firstTask];for(const currTask of restOfTasks){const prevTask=currentClusterTasks[currentClusterTasks.length-1];if(currTask.start-prevTask.end<FCI_MIN_CLUSTER_SEPARATION_MS){currentClusterTasks.push(currTask);}else{clusters.push(new TaskCluster(currentClusterTasks));currentClusterTasks=[currTask];}}
+clusters.push(new TaskCluster(currentClusterTasks));return clusters;}
+function reachedFCIQuiescence_(timestamp,mainThreadQuietWindowStart,searchBegin){const quietWindowSize=timestamp-mainThreadQuietWindowStart;const timeSinceSearchBegin=mainThreadQuietWindowStart-searchBegin;const requiredWindowSize=requiredFCIWindowSizeMs(timeSinceSearchBegin);return quietWindowSize>requiredWindowSize;}
+function findFirstCpuIdleTime(searchBegin,searchEnd,domContentLoadedEnd,longTasksInWindow){const sortedLongTasks=longTasksInWindow.sort((a,b)=>a.start-b.start);const taskClusters=findFCITaskClusters(sortedLongTasks);const heavyTaskClusters=taskClusters.filter(cluster=>cluster.isHeavy());let quietWindowBegin=searchBegin;let fiCandidate=undefined;for(const cluster of heavyTaskClusters){if(reachedFCIQuiescence_(cluster.start,quietWindowBegin,searchBegin)){fiCandidate=quietWindowBegin;break;}
+quietWindowBegin=cluster.end;}
+if(fiCandidate===undefined){if(reachedFCIQuiescence_(searchEnd,quietWindowBegin,searchBegin)){fiCandidate=quietWindowBegin;}else{return undefined;}}
+return Math.max(fiCandidate,domContentLoadedEnd);}
+return{findInteractiveTime,findFirstCpuIdleTime,requiredFCIWindowSizeMs,findFCITaskClusters,};});'use strict';tr.exportTo('tr.model.um',function(){const LOAD_SUBTYPE_NAMES={SUCCESSFUL:'Successful',FAILED:'Failed',};const DOES_LOAD_SUBTYPE_NAME_EXIST={};for(const key in LOAD_SUBTYPE_NAMES){DOES_LOAD_SUBTYPE_NAME_EXIST[LOAD_SUBTYPE_NAMES[key]]=true;}
+function LoadExpectation(parentModel,initiatorTitle,start,duration,renderer,navigationStart,fmpEvent,dclEndEvent,cpuIdleTime,timeToInteractive,url,frameId){if(!DOES_LOAD_SUBTYPE_NAME_EXIST[initiatorTitle]){throw new Error(initiatorTitle+' is not in LOAD_SUBTYPE_NAMES');}
+tr.model.um.UserExpectation.call(this,parentModel,initiatorTitle,start,duration);this.renderProcess=renderer;this.renderMainThread=undefined;this.routingId=undefined;this.parentRoutingId=undefined;this.loadFinishedEvent=undefined;this.navigationStart=navigationStart;this.fmpEvent=fmpEvent;this.domContentLoadedEndEvent=dclEndEvent;this.firstCpuIdleTime=cpuIdleTime;this.timeToInteractive=timeToInteractive;this.url=url;this.frameId=frameId;}
+LoadExpectation.prototype={__proto__:tr.model.um.UserExpectation.prototype,constructor:LoadExpectation};tr.model.um.UserExpectation.subTypes.register(LoadExpectation,{stageTitle:'Load',colorId:tr.b.ColorScheme.getColorIdForReservedName('rail_load')});return{LOAD_SUBTYPE_NAMES,LoadExpectation,};});'use strict';tr.exportTo('tr.importer',function(){const LONG_TASK_THRESHOLD_MS=50;const IGNORE_URLS=['','about:blank',];function findFrameLoaderSnapshotAt(rendererHelper,frameIdRef,ts){const objects=rendererHelper.process.objects;const frameLoaderInstances=objects.instancesByTypeName_.FrameLoader;if(frameLoaderInstances===undefined)return undefined;let snapshot;for(const instance of frameLoaderInstances){if(!instance.isAliveAt(ts))continue;const maybeSnapshot=instance.getSnapshotAt(ts);if(frameIdRef!==maybeSnapshot.args.frame.id_ref)continue;snapshot=maybeSnapshot;}
+return snapshot;}
+function findFirstMeaningfulPaintCandidates(rendererHelper){const candidatesForFrameId={};for(const ev of rendererHelper.process.getDescendantEvents()){if(!tr.e.chrome.EventFinderUtils.hasCategoryAndName(ev,'loading','firstMeaningfulPaintCandidate')){continue;}
+if(rendererHelper.isTelemetryInternalEvent(ev))continue;const frameIdRef=ev.args.frame;if(frameIdRef===undefined)continue;let list=candidatesForFrameId[frameIdRef];if(list===undefined){candidatesForFrameId[frameIdRef]=list=[];}
+list.push(ev);}
+return candidatesForFrameId;}
+function computeInteractivityMetricSample_(rendererHelper,navigationStart,fmpEvent,domContentLoadedEndEvent,searchWindowEnd){if(domContentLoadedEndEvent===undefined||fmpEvent===undefined){return{interactiveTime:undefined,firstCpuIdleTime:undefined};}
+const firstMeaningfulPaintTime=fmpEvent.start;const mainThreadTasks=tr.e.chrome.EventFinderUtils.findToplevelSchedulerTasks(rendererHelper.mainThread);const longTasks=mainThreadTasks.filter(task=>task.duration>=LONG_TASK_THRESHOLD_MS);const longTasksInWindow=longTasks.filter(task=>task.range.intersectsExplicitRangeInclusive(firstMeaningfulPaintTime,searchWindowEnd));const resourceLoadEvents=tr.e.chrome.EventFinderUtils.getNetworkEventsInRange(rendererHelper.process,tr.b.math.Range.fromExplicitRange(navigationStart.start,searchWindowEnd));const firstCpuIdleTime=tr.e.chrome.findFirstCpuIdleTime(firstMeaningfulPaintTime,searchWindowEnd,domContentLoadedEndEvent.start,longTasksInWindow);const interactiveTime=resourceLoadEvents.length>0?tr.e.chrome.findInteractiveTime(firstMeaningfulPaintTime,searchWindowEnd,domContentLoadedEndEvent.start,longTasksInWindow,resourceLoadEvents):undefined;return{interactiveTime,firstCpuIdleTime};}
+function constructLoadingExpectation_(rendererHelper,frameToDomContentLoadedEndEvents,navigationStart,fmpEvent,searchWindowEnd,url,frameId){const dclTimesForFrame=frameToDomContentLoadedEndEvents.get(frameId)||[];const dclSearchRange=tr.b.math.Range.fromExplicitRange(navigationStart.start,searchWindowEnd);const dclTimesInWindow=dclSearchRange.filterArray(dclTimesForFrame,event=>event.start);let domContentLoadedEndEvent=undefined;if(dclTimesInWindow.length!==0){domContentLoadedEndEvent=dclTimesInWindow[dclTimesInWindow.length-1];}
+const{interactiveTime,firstCpuIdleTime}=computeInteractivityMetricSample_(rendererHelper,navigationStart,fmpEvent,domContentLoadedEndEvent,searchWindowEnd);const duration=(interactiveTime===undefined)?searchWindowEnd-navigationStart.start:interactiveTime-navigationStart.start;return new tr.model.um.LoadExpectation(rendererHelper.modelHelper.model,tr.model.um.LOAD_SUBTYPE_NAMES.SUCCESSFUL,navigationStart.start,duration,rendererHelper.process,navigationStart,fmpEvent,domContentLoadedEndEvent,firstCpuIdleTime,interactiveTime,url,frameId);}
+function collectLoadExpectationsForRenderer(rendererHelper){const samples=[];const frameToNavStartEvents=tr.e.chrome.EventFinderUtils.getSortedMainThreadEventsByFrame(rendererHelper,'navigationStart','blink.user_timing');const frameToDomContentLoadedEndEvents=tr.e.chrome.EventFinderUtils.getSortedMainThreadEventsByFrame(rendererHelper,'domContentLoadedEventEnd','blink.user_timing');function addSamples(frameIdRef,navigationStart,fmpCandidateEvents,searchWindowEnd,url){let fmpMarkerEvent=tr.e.chrome.EventFinderUtils.findLastEventStartingOnOrBeforeTimestamp(fmpCandidateEvents,searchWindowEnd);if(fmpMarkerEvent!==undefined&&navigationStart.start>fmpMarkerEvent.start){fmpMarkerEvent=undefined;}
+samples.push(constructLoadingExpectation_(rendererHelper,frameToDomContentLoadedEndEvents,navigationStart,fmpMarkerEvent,searchWindowEnd,url,frameIdRef));}
+const candidatesForFrameId=findFirstMeaningfulPaintCandidates(rendererHelper);for(const[frameIdRef,navStartEvents]of frameToNavStartEvents){const fmpCandidateEvents=candidatesForFrameId[frameIdRef]||[];let prevNavigation={navigationEvent:undefined,url:undefined};for(let index=0;index<navStartEvents.length;index++){const currNavigation=navStartEvents[index];let url;let isLoadingMainFrame=false;if(currNavigation.args.data){url=currNavigation.args.data.documentLoaderURL;isLoadingMainFrame=currNavigation.args.data.isLoadingMainFrame;}else{const snapshot=findFrameLoaderSnapshotAt(rendererHelper,frameIdRef,currNavigation.start);if(snapshot){url=snapshot.args.documentLoaderURL;isLoadingMainFrame=snapshot.args.isLoadingMainFrame;}}
+if(!isLoadingMainFrame)continue;if(url===undefined||IGNORE_URLS.includes(url))continue;if(prevNavigation.navigationEvent!==undefined){addSamples(frameIdRef,prevNavigation.navigationEvent,fmpCandidateEvents,currNavigation.start,prevNavigation.url);}
+prevNavigation={navigationEvent:currNavigation,url};}
+if(prevNavigation.navigationEvent!==undefined){addSamples(frameIdRef,prevNavigation.navigationEvent,fmpCandidateEvents,rendererHelper.modelHelper.chromeBounds.max,prevNavigation.url);}}
+return samples;}
+function findLoadExpectations(modelHelper){const loads=[];const chromeHelper=modelHelper.model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);for(const pid in chromeHelper.rendererHelpers){const rendererHelper=chromeHelper.rendererHelpers[pid];if(rendererHelper.isChromeTracingUI)continue;loads.push.apply(loads,collectLoadExpectationsForRenderer(rendererHelper));}
+return loads;}
+return{findLoadExpectations,};});'use strict';tr.exportTo('tr.model.um',function(){function StartupExpectation(parentModel,start,duration){tr.model.um.UserExpectation.call(this,parentModel,'',start,duration);}
+StartupExpectation.prototype={__proto__:tr.model.um.UserExpectation.prototype,constructor:StartupExpectation};tr.model.um.UserExpectation.subTypes.register(StartupExpectation,{stageTitle:'Startup',colorId:tr.b.ColorScheme.getColorIdForReservedName('startup')});return{StartupExpectation,};});'use strict';tr.exportTo('tr.importer',function(){function getAllFrameEvents(modelHelper){const frameEvents=[];frameEvents.push.apply(frameEvents,modelHelper.browserHelper.getFrameEventsInRange(tr.model.helpers.IMPL_FRAMETIME_TYPE,modelHelper.model.bounds));for(const renderer of Object.values(modelHelper.rendererHelpers)){frameEvents.push.apply(frameEvents,renderer.getFrameEventsInRange(tr.model.helpers.IMPL_FRAMETIME_TYPE,modelHelper.model.bounds));}
+return frameEvents.sort(tr.importer.compareEvents);}
+function getStartupEvents(modelHelper){function isStartupSlice(slice){return slice.title==='BrowserMainLoop::CreateThreads';}
+const events=modelHelper.browserHelper.getAllAsyncSlicesMatching(isStartupSlice);const deduper=new tr.model.EventSet();events.forEach(function(event){const sliceGroup=event.parentContainer.sliceGroup;const slice=sliceGroup&&sliceGroup.findFirstSlice();if(slice){deduper.push(slice);}});return deduper.toArray();}
+function findStartupExpectations(modelHelper){const openingEvents=getStartupEvents(modelHelper);const closingEvents=getAllFrameEvents(modelHelper);const startups=[];openingEvents.forEach(function(openingEvent){closingEvents.forEach(function(closingEvent){if(openingEvent.closingEvent)return;if(closingEvent.openingEvent)return;if(closingEvent.start<=openingEvent.start)return;if(openingEvent.parentContainer.parent.pid!==closingEvent.parentContainer.parent.pid){return;}
+openingEvent.closingEvent=closingEvent;closingEvent.openingEvent=openingEvent;const se=new tr.model.um.StartupExpectation(modelHelper.model,openingEvent.start,closingEvent.end-openingEvent.start);se.associatedEvents.push(openingEvent);se.associatedEvents.push(closingEvent);startups.push(se);});});return startups;}
+return{findStartupExpectations,};});'use strict';tr.exportTo('tr.model',function(){function getAssociatedEvents(irs){const allAssociatedEvents=new tr.model.EventSet();irs.forEach(function(ir){ir.associatedEvents.forEach(function(event){if(event instanceof tr.model.FlowEvent)return;allAssociatedEvents.push(event);});});return allAssociatedEvents;}
+function getUnassociatedEvents(model,associatedEvents){const unassociatedEvents=new tr.model.EventSet();for(const proc of model.getAllProcesses()){for(const thread of Object.values(proc.threads)){for(const event of thread.sliceGroup.getDescendantEvents()){if(!associatedEvents.contains(event)){unassociatedEvents.push(event);}}}}
+return unassociatedEvents;}
+function getTotalCpuDuration(events){let cpuMs=0;events.forEach(function(event){if(event.cpuSelfTime){cpuMs+=event.cpuSelfTime;}});return cpuMs;}
+function getIRCoverageFromModel(model){const associatedEvents=getAssociatedEvents(model.userModel.expectations);if(!associatedEvents.length)return undefined;const unassociatedEvents=getUnassociatedEvents(model,associatedEvents);const associatedCpuMs=getTotalCpuDuration(associatedEvents);const unassociatedCpuMs=getTotalCpuDuration(unassociatedEvents);const totalEventCount=associatedEvents.length+unassociatedEvents.length;const totalCpuMs=associatedCpuMs+unassociatedCpuMs;let coveredEventsCpuTimeRatio=undefined;if(totalCpuMs!==0){coveredEventsCpuTimeRatio=associatedCpuMs/totalCpuMs;}
+return{associatedEventsCount:associatedEvents.length,unassociatedEventsCount:unassociatedEvents.length,associatedEventsCpuTimeMs:associatedCpuMs,unassociatedEventsCpuTimeMs:unassociatedCpuMs,coveredEventsCountRatio:associatedEvents.length/totalEventCount,coveredEventsCpuTimeRatio};}
+return{getIRCoverageFromModel,getAssociatedEvents,getUnassociatedEvents,};});'use strict';tr.exportTo('tr.model.um',function(){function IdleExpectation(parentModel,start,duration){const initiatorTitle='';tr.model.um.UserExpectation.call(this,parentModel,initiatorTitle,start,duration);}
+IdleExpectation.prototype={__proto__:tr.model.um.UserExpectation.prototype,constructor:IdleExpectation};tr.model.um.UserExpectation.subTypes.register(IdleExpectation,{stageTitle:'Idle',colorId:tr.b.ColorScheme.getColorIdForReservedName('rail_idle')});return{IdleExpectation,};});'use strict';tr.exportTo('tr.importer',function(){const INSIGNIFICANT_MS=1;class UserModelBuilder{constructor(model){this.model=model;this.modelHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);}
+static supportsModelHelper(modelHelper){return modelHelper.browserHelper!==undefined;}
+buildUserModel(){if(!this.modelHelper||!this.modelHelper.browserHelper)return;try{for(const ue of this.findUserExpectations()){this.model.userModel.expectations.push(ue);}
+this.model.userModel.segments.push(...this.findSegments());}catch(error){this.model.importWarning({type:'UserModelBuilder',message:error,showToUser:true});}}
+findSegments(){let timestamps=new Set();for(const expectation of this.model.userModel.expectations){timestamps.add(expectation.start);timestamps.add(expectation.end);}
+timestamps=[...timestamps];timestamps.sort((x,y)=>x-y);const segments=[];for(let i=0;i<timestamps.length-1;++i){const segment=new tr.model.um.Segment(timestamps[i],timestamps[i+1]-timestamps[i]);segments.push(segment);const segmentRange=tr.b.math.Range.fromExplicitRange(segment.start,segment.end);for(const expectation of this.model.userModel.expectations){const expectationRange=tr.b.math.Range.fromExplicitRange(expectation.start,expectation.end);if(segmentRange.intersectsRangeExclusive(expectationRange)){segment.expectations.push(expectation);}}}
+return segments;}
+findUserExpectations(){const expectations=[];expectations.push.apply(expectations,tr.importer.findStartupExpectations(this.modelHelper));expectations.push.apply(expectations,tr.importer.findLoadExpectations(this.modelHelper));expectations.push.apply(expectations,tr.importer.findInputExpectations(this.modelHelper));expectations.push.apply(expectations,this.findIdleExpectations(expectations));this.collectUnassociatedEvents_(expectations);return expectations;}
+collectUnassociatedEvents_(expectations){const vacuumUEs=[];for(const expectation of expectations){if(expectation instanceof tr.model.um.IdleExpectation||expectation instanceof tr.model.um.LoadExpectation||expectation instanceof tr.model.um.StartupExpectation){vacuumUEs.push(expectation);}}
+if(vacuumUEs.length===0)return;const allAssociatedEvents=tr.model.getAssociatedEvents(expectations);const unassociatedEvents=tr.model.getUnassociatedEvents(this.model,allAssociatedEvents);for(const event of unassociatedEvents){if(!(event instanceof tr.model.ThreadSlice))continue;if(!event.isTopLevel)continue;for(let index=0;index<vacuumUEs.length;++index){const expectation=vacuumUEs[index];if((event.start>=expectation.start)&&(event.start<expectation.end)){expectation.associatedEvents.addEventSet(event.entireHierarchy);break;}}}}
+findIdleExpectations(otherUEs){if(this.model.bounds.isEmpty)return;const emptyRanges=tr.b.math.findEmptyRangesBetweenRanges(tr.b.math.convertEventsToRanges(otherUEs),this.model.bounds);const expectations=[];const model=this.model;for(const range of emptyRanges){if(range.max<(range.min+INSIGNIFICANT_MS))continue;expectations.push(new tr.model.um.IdleExpectation(model,range.min,range.max-range.min));}
+return expectations;}}
+function createCustomizeModelLinesFromModel(model){const modelLines=[];modelLines.push('      audits.addEvent(model.browserMain,');modelLines.push('          {title: \'model start\', start: 0, end: 1});');const typeNames={};for(const typeName in tr.e.cc.INPUT_EVENT_TYPE_NAMES){typeNames[tr.e.cc.INPUT_EVENT_TYPE_NAMES[typeName]]=typeName;}
+let modelEvents=new tr.model.EventSet();for(const ue of model.userModel.expectations){modelEvents.addEventSet(ue.sourceEvents);}
+modelEvents=modelEvents.toArray();modelEvents.sort(tr.importer.compareEvents);for(const event of modelEvents){const startAndEnd='start: '+parseInt(event.start)+', '+'end: '+parseInt(event.end)+'});';if(event instanceof tr.e.cc.InputLatencyAsyncSlice){modelLines.push('      audits.addInputEvent(model, INPUT_TYPE.'+
+typeNames[event.typeName]+',');}else if(event.title==='RenderFrameImpl::didCommitProvisionalLoad'){modelLines.push('      audits.addCommitLoadEvent(model,');}else if(event.title==='InputHandlerProxy::HandleGestureFling::started'){modelLines.push('      audits.addFlingAnimationEvent(model,');}else if(event.title===tr.model.helpers.IMPL_RENDERING_STATS){modelLines.push('      audits.addFrameEvent(model,');}else if(event.title===tr.importer.CSS_ANIMATION_TITLE){modelLines.push('      audits.addEvent(model.rendererMain, {');modelLines.push('        title: \'Animation\', '+startAndEnd);return;}else{throw new Error('You must extend createCustomizeModelLinesFromModel()'+'to support this event:\n'+event.title+'\n');}
+modelLines.push('          {'+startAndEnd);}
+modelLines.push('      audits.addEvent(model.browserMain,');modelLines.push('          {'+'title: \'model end\', '+'start: '+(parseInt(model.bounds.max)-1)+', '+'end: '+parseInt(model.bounds.max)+'});');return modelLines;}
+function createExpectedUELinesFromModel(model){const expectedLines=[];const ueCount=model.userModel.expectations.length;for(let index=0;index<ueCount;++index){const expectation=model.userModel.expectations[index];let ueString='      {';ueString+='title: \''+expectation.title+'\', ';ueString+='start: '+parseInt(expectation.start)+', ';ueString+='end: '+parseInt(expectation.end)+', ';ueString+='eventCount: '+expectation.sourceEvents.length;ueString+='}';if(index<(ueCount-1))ueString+=',';expectedLines.push(ueString);}
+return expectedLines;}
+function createUEFinderTestCaseStringFromModel(model){const filename=window.location.hash.substr(1);let testName=filename.substr(filename.lastIndexOf('/')+1);testName=testName.substr(0,testName.indexOf('.'));try{const testLines=[];testLines.push('  /*');testLines.push('    This test was generated from');testLines.push('    '+filename+'');testLines.push('   */');testLines.push('  test(\''+testName+'\', function() {');testLines.push('    const verifier = new UserExpectationVerifier();');testLines.push('    verifier.customizeModelCallback = function(model) {');testLines.push.apply(testLines,createCustomizeModelLinesFromModel(model));testLines.push('    };');testLines.push('    verifier.expectedUEs = [');testLines.push.apply(testLines,createExpectedUELinesFromModel(model));testLines.push('    ];');testLines.push('    verifier.verify();');testLines.push('  });');return testLines.join('\n');}catch(error){return error;}}
+return{UserModelBuilder,createUEFinderTestCaseStringFromModel,};});'use strict';tr.exportTo('tr.ui.b',function(){function decorate(source,constr){let elements;if(typeof source==='string'){elements=Polymer.dom(tr.doc).querySelectorAll(source);}else{elements=[source];}
+for(let i=0,el;el=elements[i];i++){if(!(el instanceof constr)){constr.decorate(el);}}}
+function define(className,opt_parentConstructor,opt_tagNS){if(typeof className==='function'){throw new Error('Passing functions as className is deprecated. Please '+'use (className, opt_parentConstructor) to subclass');}
+className=className.toLowerCase();if(opt_parentConstructor&&!opt_parentConstructor.tagName){throw new Error('opt_parentConstructor was not '+'created by tr.ui.b.define');}
+let tagName=className;let tagNS=undefined;if(opt_parentConstructor){if(opt_tagNS){throw new Error('Must not specify tagNS if parentConstructor is given');}
+let parent=opt_parentConstructor;while(parent&&parent.tagName){tagName=parent.tagName;tagNS=parent.tagNS;parent=parent.parentConstructor;}}else{tagNS=opt_tagNS;}
+function f(){if(opt_parentConstructor&&f.prototype.__proto__!==opt_parentConstructor.prototype){throw new Error(className+' prototye\'s __proto__ field is messed up. '+'It MUST be the prototype of '+opt_parentConstructor.tagName);}
+let el;if(tagNS===undefined){el=tr.doc.createElement(tagName);}else{el=tr.doc.createElementNS(tagNS,tagName);}
+f.decorate.call(this,el,arguments);return el;}
+f.decorate=function(el){el.__proto__=f.prototype;el.decorate.apply(el,arguments[1]);el.constructor=f;};f.className=className;f.tagName=tagName;f.tagNS=tagNS;f.parentConstructor=(opt_parentConstructor?opt_parentConstructor:undefined);f.toString=function(){if(!f.parentConstructor){return f.tagName;}
+return f.parentConstructor.toString()+'::'+f.className;};return f;}
+function elementIsChildOf(el,potentialParent){if(el===potentialParent)return false;let cur=el;while(Polymer.dom(cur).parentNode){if(cur===potentialParent)return true;cur=Polymer.dom(cur).parentNode;}
+return false;}
+return{decorate,define,elementIsChildOf,};});'use strict';tr.exportTo('tr.b.math',function(){function Rect(){this.x=0;this.y=0;this.width=0;this.height=0;}
+Rect.fromXYWH=function(x,y,w,h){const rect=new Rect();rect.x=x;rect.y=y;rect.width=w;rect.height=h;return rect;};Rect.fromArray=function(ary){if(ary.length!==4){throw new Error('ary.length must be 4');}
+const rect=new Rect();rect.x=ary[0];rect.y=ary[1];rect.width=ary[2];rect.height=ary[3];return rect;};Rect.prototype={__proto__:Object.prototype,get left(){return this.x;},get top(){return this.y;},get right(){return this.x+this.width;},get bottom(){return this.y+this.height;},toString(){return'Rect('+this.x+', '+this.y+', '+
+this.width+', '+this.height+')';},toArray(){return[this.x,this.y,this.width,this.height];},clone(){const rect=new Rect();rect.x=this.x;rect.y=this.y;rect.width=this.width;rect.height=this.height;return rect;},enlarge(pad){const rect=new Rect();this.enlargeFast(rect,pad);return rect;},enlargeFast(out,pad){out.x=this.x-pad;out.y=this.y-pad;out.width=this.width+2*pad;out.height=this.height+2*pad;return out;},size(){return{width:this.width,height:this.height};},scale(s){const rect=new Rect();this.scaleFast(rect,s);return rect;},scaleSize(s){return Rect.fromXYWH(this.x,this.y,this.width*s,this.height*s);},scaleFast(out,s){out.x=this.x*s;out.y=this.y*s;out.width=this.width*s;out.height=this.height*s;return out;},translate(v){const rect=new Rect();this.translateFast(rect,v);return rect;},translateFast(out,v){out.x=this.x+v[0];out.y=this.x+v[1];out.width=this.width;out.height=this.height;return out;},asUVRectInside(containingRect){const rect=new Rect();rect.x=(this.x-containingRect.x)/containingRect.width;rect.y=(this.y-containingRect.y)/containingRect.height;rect.width=this.width/containingRect.width;rect.height=this.height/containingRect.height;return rect;},intersects(that){let ok=true;ok&=this.x<that.right;ok&=this.right>that.x;ok&=this.y<that.bottom;ok&=this.bottom>that.y;return ok;},equalTo(rect){return rect&&(this.x===rect.x)&&(this.y===rect.y)&&(this.width===rect.width)&&(this.height===rect.height);}};return{Rect,};});'use strict';tr.exportTo('tr.ui.b',function(){function instantiateTemplate(selector,doc){doc=doc||document;const el=Polymer.dom(doc).querySelector(selector);if(!el){throw new Error('Element not found: '+selector);}
+return doc.importNode(el.content,true);}
+function windowRectForElement(element){const position=[element.offsetLeft,element.offsetTop];const size=[element.offsetWidth,element.offsetHeight];let node=element.offsetParent;while(node){position[0]+=node.offsetLeft;position[1]+=node.offsetTop;node=node.offsetParent;}
+return tr.b.math.Rect.fromXYWH(position[0],position[1],size[0],size[1]);}
+function scrollIntoViewIfNeeded(el){const pr=el.parentElement.getBoundingClientRect();const cr=el.getBoundingClientRect();if(cr.top<pr.top){el.scrollIntoView(true);}else if(cr.bottom>pr.bottom){el.scrollIntoView(false);}}
+function extractUrlString(url){let extracted=url.replace(/url\((.*)\)/,'$1');extracted=extracted.replace(/\"(.*)\"/,'$1');return extracted;}
+function toThreeDigitLocaleString(value){return value.toLocaleString(undefined,{minimumFractionDigits:3,maximumFractionDigits:3});}
+function isUnknownElementName(name){return document.createElement(name)instanceof HTMLUnknownElement;}
+return{isUnknownElementName,toThreeDigitLocaleString,instantiateTemplate,windowRectForElement,scrollIntoViewIfNeeded,extractUrlString,};});'use strict';tr.exportTo('tr.ui.b',function(){if(tr.isHeadless)return{};const THIS_DOC=document._currentScript.ownerDocument;const Overlay=tr.ui.b.define('overlay');Overlay.prototype={__proto__:HTMLDivElement.prototype,decorate(){Polymer.dom(this).classList.add('overlay');this.parentEl_=this.ownerDocument.body;this.visible_=false;this.userCanClose_=true;this.onKeyDown_=this.onKeyDown_.bind(this);this.onClick_=this.onClick_.bind(this);this.onFocusIn_=this.onFocusIn_.bind(this);this.onDocumentClick_=this.onDocumentClick_.bind(this);this.onClose_=this.onClose_.bind(this);this.addEventListener('visible-change',tr.ui.b.Overlay.prototype.onVisibleChange_.bind(this),true);const createShadowRoot=this.createShadowRoot||this.webkitCreateShadowRoot;this.shadow_=createShadowRoot.call(this);Polymer.dom(this.shadow_).appendChild(tr.ui.b.instantiateTemplate('#overlay-template',THIS_DOC));this.closeBtn_=Polymer.dom(this.shadow_).querySelector('close-button');this.closeBtn_.addEventListener('click',this.onClose_);Polymer.dom(this.shadow_).querySelector('overlay-frame').addEventListener('click',this.onClick_);this.observer_=new MutationObserver(this.didButtonBarMutate_.bind(this));this.observer_.observe(Polymer.dom(this.shadow_).querySelector('button-bar'),{childList:true});Object.defineProperty(this,'title',{get(){return Polymer.dom(Polymer.dom(this.shadow_).querySelector('title')).textContent;},set(title){Polymer.dom(Polymer.dom(this.shadow_).querySelector('title')).textContent=title;}});},set userCanClose(userCanClose){this.userCanClose_=userCanClose;this.closeBtn_.style.display=userCanClose?'block':'none';},get buttons(){return Polymer.dom(this.shadow_).querySelector('button-bar');},get visible(){return this.visible_;},set visible(newValue){if(this.visible_===newValue)return;this.visible_=newValue;const e=new tr.b.Event('visible-change');this.dispatchEvent(e);},onVisibleChange_(){this.visible_?this.show_():this.hide_();},show_(){Polymer.dom(this.parentEl_).appendChild(this);if(this.userCanClose_){this.addEventListener('keydown',this.onKeyDown_.bind(this));this.addEventListener('click',this.onDocumentClick_.bind(this));this.closeBtn_.addEventListener('click',this.onClose_);}
+this.parentEl_.addEventListener('focusin',this.onFocusIn_);this.tabIndex=0;const elList=Polymer.dom(this).querySelectorAll('button, input, list, select, a');if(elList.length>0){if(elList[0]===this.closeBtn_){if(elList.length>1)return elList[1].focus();}else{return elList[0].focus();}}
+this.focus();},hide_(){Polymer.dom(this.parentEl_).removeChild(this);this.parentEl_.removeEventListener('focusin',this.onFocusIn_);if(this.closeBtn_){this.closeBtn_.removeEventListener('click',this.onClose_);}
+document.removeEventListener('keydown',this.onKeyDown_);document.removeEventListener('click',this.onDocumentClick_);},onClose_(e){this.visible=false;if((e.type!=='keydown')||(e.type==='keydown'&&e.keyCode===27)){e.stopPropagation();}
+e.preventDefault();tr.b.dispatchSimpleEvent(this,'closeclick');},onFocusIn_(e){let node=e.target;while(node){if(node===this){return;}
+node=node.parentNode;}
+tr.b.timeout(0).then(()=>this.focus());e.preventDefault();e.stopPropagation();},didButtonBarMutate_(e){const hasButtons=this.buttons.children.length>0;if(hasButtons){Polymer.dom(this.shadow_).querySelector('button-bar').style.display=undefined;}else{Polymer.dom(this.shadow_).querySelector('button-bar').style.display='none';}},onKeyDown_(e){if(e.keyCode===9&&e.shiftKey&&e.target===this){e.preventDefault();return;}
+if(e.keyCode!==27)return;this.onClose_(e);},onClick_(e){e.stopPropagation();},onDocumentClick_(e){if(!this.userCanClose_)return;this.onClose_(e);}};Overlay.showError=function(msg,opt_err){const o=new Overlay();o.title='Error';Polymer.dom(o).textContent=msg;if(opt_err){const e=tr.b.normalizeException(opt_err);const stackDiv=document.createElement('pre');Polymer.dom(stackDiv).textContent=e.stack;stackDiv.style.paddingLeft='8px';stackDiv.style.margin=0;Polymer.dom(o).appendChild(stackDiv);}
+const b=document.createElement('button');Polymer.dom(b).textContent='OK';b.addEventListener('click',function(){o.visible=false;});Polymer.dom(o.buttons).appendChild(b);o.visible=true;return o;};return{Overlay,};});'use strict';tr.exportTo('tr.importer',function(){const Timing=tr.b.Timing;function ImportOptions(){this.shiftWorldToZero=true;this.pruneEmptyContainers=true;this.showImportWarnings=true;this.trackDetailedModelStats=false;this.customizeModelCallback=undefined;const auditorTypes=tr.c.Auditor.getAllRegisteredTypeInfos();this.auditorConstructors=auditorTypes.map(function(typeInfo){return typeInfo.constructor;});}
+function Import(model,opt_options){if(model===undefined){throw new Error('Must provide model to import into.');}
+this.importing_=false;this.importOptions_=opt_options||new ImportOptions();this.model_=model;this.model_.importOptions=this.importOptions_;}
+Import.prototype={__proto__:Object.prototype,importTraces(traces){const progressMeter={update(msg){}};tr.b.Task.RunSynchronously(this.createImportTracesTask(progressMeter,traces));},importTracesWithProgressDialog(traces){if(tr.isHeadless){throw new Error('Cannot use this method in headless mode.');}
+const overlay=tr.ui.b.Overlay();overlay.title='Importing...';overlay.userCanClose=false;overlay.msgEl=document.createElement('div');Polymer.dom(overlay).appendChild(overlay.msgEl);overlay.msgEl.style.margin='20px';overlay.update=function(msg){Polymer.dom(this.msgEl).textContent=msg;};overlay.visible=true;const promise=tr.b.Task.RunWhenIdle(this.createImportTracesTask(overlay,traces));promise.then(function(){overlay.visible=false;},function(err){overlay.visible=false;});return promise;},createImportTracesTask(progressMeter,traces){const importStartTimeMs=tr.b.Timing.getCurrentTimeMs();if(this.importing_){throw new Error('Already importing.');}
+this.importing_=true;const importTask=new tr.b.Task(function prepareImport(){progressMeter.update('I will now import your traces for you...');},this);let lastTask=importTask;const importers=[];function addImportStage(title,callback){lastTask=lastTask.after(()=>progressMeter.update(title));lastTask.updatesUi=true;lastTask=lastTask.after(callback);}
+function addStageForEachImporter(title,callback){lastTask=lastTask.after((task)=>{importers.forEach((importer,index)=>{const uiSubTask=task.subTask(()=>{progressMeter.update(`${title} ${index + 1} of ${importers.length}`);});uiSubTask.updatesUi=true;task.subTask(()=>callback(importer));});});}
+addImportStage('Creating importers...',()=>{traces=traces.slice(0);progressMeter.update('Creating importers...');for(let i=0;i<traces.length;++i){importers.push(this.createImporter_(traces[i]));}
+for(let i=0;i<importers.length;i++){const subtraces=importers[i].extractSubtraces();for(let j=0;j<subtraces.length;j++){try{traces.push(subtraces[j]);importers.push(this.createImporter_(subtraces[j]));}catch(error){this.model_.importWarning({type:error.name,message:error.message,showToUser:true,});continue;}}}
+if(traces.length&&!this.hasEventDataDecoder_(importers)){throw new Error('Could not find an importer for the provided eventData.');}
+importers.sort(function(x,y){return x.importPriority-y.importPriority;});});addStageForEachImporter('Importing clock sync markers',importer=>importer.importClockSyncMarkers());addStageForEachImporter('Importing',importer=>importer.importEvents());if(this.importOptions_.customizeModelCallback){addImportStage('Customizing',()=>{this.importOptions_.customizeModelCallback(this.model_);});}
+addStageForEachImporter('Importing sample data',importer=>importer.importSampleData());addImportStage('Autoclosing open slices...',()=>{this.model_.autoCloseOpenSlices();this.model_.createSubSlices();});addStageForEachImporter('Finalizing import',importer=>importer.finalizeImport());addImportStage('Initializing objects (step 1/2)...',()=>this.model_.preInitializeObjects());if(this.importOptions_.pruneEmptyContainers){addImportStage('Pruning empty containers...',()=>this.model_.pruneEmptyContainers());}
+addImportStage('Merging kernel with userland...',()=>this.model_.mergeKernelWithUserland());let auditors=[];addImportStage('Adding arbitrary data to model...',()=>{auditors=this.importOptions_.auditorConstructors.map(auditorConstructor=>new auditorConstructor(this.model_));auditors.forEach((auditor)=>{auditor.runAnnotate();auditor.installUserFriendlyCategoryDriverIfNeeded();});});addImportStage('Computing final world bounds...',()=>{this.model_.computeWorldBounds(this.importOptions_.shiftWorldToZero);});addImportStage('Building flow event map...',()=>this.model_.buildFlowEventIntervalTree());addImportStage('Joining object refs...',()=>this.model_.joinRefs());addImportStage('Cleaning up undeleted objects...',()=>this.model_.cleanupUndeletedObjects());addImportStage('Sorting memory dumps...',()=>this.model_.sortMemoryDumps());addImportStage('Finalizing memory dump graphs...',()=>this.model_.finalizeMemoryGraphs());addImportStage('Initializing objects (step 2/2)...',()=>this.model_.initializeObjects());addImportStage('Building event indices...',()=>this.model_.buildEventIndices());addImportStage('Building UserModel...',()=>{const userModelBuilder=new tr.importer.UserModelBuilder(this.model_);userModelBuilder.buildUserModel();});addImportStage('Sorting user expectations...',()=>this.model_.userModel.sortExpectations());addImportStage('Running auditors...',()=>{auditors.forEach(auditor=>auditor.runAudit());});addImportStage('Updating alerts...',()=>this.model_.sortAlerts());addImportStage('Update bounds...',()=>this.model_.updateBounds());addImportStage('Looking for warnings...',()=>{if(!this.model_.isTimeHighResolution){this.model_.importWarning({type:'low_resolution_timer',message:'Trace time is low resolution, trace may be unusable.',showToUser:true});}});lastTask.after(()=>{this.importing_=false;this.model_.stats.traceImportDurationMs=tr.b.Timing.getCurrentTimeMs()-importStartTimeMs;});return importTask;},createImporter_(eventData){const importerConstructor=tr.importer.Importer.findImporterFor(eventData);if(!importerConstructor){throw new Error('Couldn\'t create an importer for the provided '+'eventData.');}
+return new importerConstructor(this.model_,eventData);},hasEventDataDecoder_(importers){for(let i=0;i<importers.length;++i){if(!importers[i].isTraceDataContainer())return true;}
+return false;}};return{ImportOptions,Import,};});'use strict';tr.exportTo('tr.e.v8',function(){const ThreadSlice=tr.model.ThreadSlice;function V8GCStatsThreadSlice(){ThreadSlice.apply(this,arguments);this.liveObjects_=JSON.parse(this.args.live);delete this.args.live;this.deadObjects_=JSON.parse(this.args.dead);delete this.args.dead;}
+V8GCStatsThreadSlice.prototype={__proto__:ThreadSlice.prototype,get liveObjects(){return this.liveObjects_;},get deadObjects(){return this.deadObjects_;}};ThreadSlice.subTypes.register(V8GCStatsThreadSlice,{categoryParts:['disabled-by-default-v8.gc_stats'],name:'v8 gc stats slice',pluralName:'v8 gc stats slices'});return{V8GCStatsThreadSlice,};});'use strict';tr.exportTo('tr.e.v8',function(){const ThreadSlice=tr.model.ThreadSlice;function V8ICStatsThreadSlice(){ThreadSlice.apply(this,arguments);this.icStats_=undefined;if(this.args['ic-stats']){this.icStats_=this.args['ic-stats'].data;delete this.args['ic-stats'];}}
+V8ICStatsThreadSlice.prototype={__proto__:ThreadSlice.prototype,get icStats(){return this.icStats_;}};ThreadSlice.subTypes.register(V8ICStatsThreadSlice,{categoryParts:['disabled-by-default-v8.ic_stats'],name:'v8 ic stats slice',pluralName:'v8 ic stats slices'});return{V8ICStatsThreadSlice,};});'use strict';tr.exportTo('tr.e.v8',function(){const ThreadSlice=tr.model.ThreadSlice;function V8ThreadSlice(){ThreadSlice.apply(this,arguments);this.runtimeCallStats_=undefined;}
+V8ThreadSlice.prototype={__proto__:ThreadSlice.prototype,get runtimeCallStats(){if('runtime-call-stats'in this.args){this.runtimeCallStats_=this.args['runtime-call-stats'];delete this.args['runtime-call-stats'];}
+return this.runtimeCallStats_;}};ThreadSlice.subTypes.register(V8ThreadSlice,{categoryParts:['v8','disabled-by-default-v8.runtime_stats'],name:'v8 slice',pluralName:'v8 slices'});return{V8ThreadSlice,};});'use strict';tr.exportTo('tr.e.cc',function(){function PictureAsImageData(picture,errorOrImageData){this.picture_=picture;if(errorOrImageData instanceof ImageData){this.error_=undefined;this.imageData_=errorOrImageData;}else{this.error_=errorOrImageData;this.imageData_=undefined;}}
+PictureAsImageData.Pending=function(picture){return new PictureAsImageData(picture,undefined);};PictureAsImageData.prototype={get picture(){return this.picture_;},get error(){return this.error_;},get imageData(){return this.imageData_;},isPending(){return this.error_===undefined&&this.imageData_===undefined;},asCanvas(){if(!this.imageData_)return;const canvas=document.createElement('canvas');const ctx=canvas.getContext('2d');canvas.width=this.imageData_.width;canvas.height=this.imageData_.height;ctx.putImageData(this.imageData_,0,0);return canvas;}};return{PictureAsImageData,};});'use strict';tr.exportTo('tr.e.cc',function(){const convertedNameCache={};function convertNameToJSConvention(name){if(name in convertedNameCache){return convertedNameCache[name];}
+if(name[0]==='_'||name[name.length-1]==='_'){convertedNameCache[name]=name;return name;}
+const words=name.split('_');if(words.length===1){convertedNameCache[name]=words[0];return words[0];}
+for(let i=1;i<words.length;i++){words[i]=words[i][0].toUpperCase()+words[i].substring(1);}
+convertedNameCache[name]=words.join('');return convertedNameCache[name];}
+function moveRequiredFieldsFromArgsToToplevel(object,fields){for(let i=0;i<fields.length;i++){const key=fields[i];if(object.args[key]===undefined){throw Error('Expected field '+key+' not found in args');}
+if(object[key]!==undefined){throw Error('Field '+key+' already in object');}
+object[key]=object.args[key];delete object.args[key];}}
+function moveOptionalFieldsFromArgsToToplevel(object,fields){for(let i=0;i<fields.length;i++){const key=fields[i];if(object.args[key]===undefined)continue;if(object[key]!==undefined){throw Error('Field '+key+' already in object');}
+object[key]=object.args[key];delete object.args[key];}}
+function preInitializeObject(object){preInitializeObjectInner(object.args,false);}
+function preInitializeObjectInner(object,hasRecursed){if(!(object instanceof Object))return;if(object instanceof Array){for(let i=0;i<object.length;i++){preInitializeObjectInner(object[i],true);}
+return;}
+if(hasRecursed&&(object instanceof tr.model.ObjectSnapshot||object instanceof tr.model.ObjectInstance)){return;}
+for(let key in object){const newKey=convertNameToJSConvention(key);if(newKey!==key){const value=object[key];delete object[key];object[newKey]=value;key=newKey;}
+if(/Quad$/.test(key)&&!(object[key]instanceof tr.b.math.Quad)){let q;try{q=tr.b.math.Quad.from8Array(object[key]);}catch(e){}
+object[key]=q;continue;}
+if(/Rect$/.test(key)&&!(object[key]instanceof tr.b.math.Rect)){let r;try{r=tr.b.math.Rect.fromArray(object[key]);}catch(e){}
+object[key]=r;}
+preInitializeObjectInner(object[key],true);}}
+return{preInitializeObject,convertNameToJSConvention,moveRequiredFieldsFromArgsToToplevel,moveOptionalFieldsFromArgsToToplevel,};});'use strict';tr.exportTo('tr.e.cc',function(){const ObjectSnapshot=tr.model.ObjectSnapshot;const PictureCount=0;const OPS_TIMING_ITERATIONS=3;function Picture(skp64,layerRect){this.skp64_=skp64;this.layerRect_=layerRect;this.guid_=tr.b.GUID.allocateSimple();}
+Picture.prototype={get canSave(){return true;},get layerRect(){return this.layerRect_;},get guid(){return this.guid_;},getBase64SkpData(){return this.skp64_;},getOps(){if(!PictureSnapshot.CanGetOps()){console.error(PictureSnapshot.HowToEnablePictureDebugging());return undefined;}
+const ops=window.chrome.skiaBenchmarking.getOps({skp64:this.skp64_,params:{layer_rect:this.layerRect_.toArray()}});if(!ops){console.error('Failed to get picture ops.');}
+return ops;},getOpTimings(){if(!PictureSnapshot.CanGetOpTimings()){console.error(PictureSnapshot.HowToEnablePictureDebugging());return undefined;}
+const opTimings=window.chrome.skiaBenchmarking.getOpTimings({skp64:this.skp64_,params:{layer_rect:this.layerRect_.toArray()}});if(!opTimings){console.error('Failed to get picture op timings.');}
+return opTimings;},tagOpsWithTimings(ops){const opTimings=[];for(let iteration=0;iteration<OPS_TIMING_ITERATIONS;iteration++){opTimings[iteration]=this.getOpTimings();if(!opTimings[iteration]||!opTimings[iteration].cmd_times){return ops;}
+if(opTimings[iteration].cmd_times.length!==ops.length){return ops;}}
+for(let opIndex=0;opIndex<ops.length;opIndex++){let min=Number.MAX_VALUE;for(let i=0;i<OPS_TIMING_ITERATIONS;i++){min=Math.min(min,opTimings[i].cmd_times[opIndex]);}
+ops[opIndex].cmd_time=min;}
+return ops;},rasterize(params,rasterCompleteCallback){if(!PictureSnapshot.CanRasterize()||!PictureSnapshot.CanGetOps()){rasterCompleteCallback(new tr.e.cc.PictureAsImageData(this,tr.e.cc.PictureSnapshot.HowToEnablePictureDebugging()));return;}
+if(!this.layerRect_.width||!this.layerRect_.height){rasterCompleteCallback(new tr.e.cc.PictureAsImageData(this,null));return;}
+const raster=window.chrome.skiaBenchmarking.rasterize({skp64:this.skp64_,params:{layer_rect:this.layerRect_.toArray()}},{stop:params.stopIndex===undefined?-1:params.stopIndex,overdraw:!!params.showOverdraw,params:{}});if(raster){const canvas=document.createElement('canvas');const ctx=canvas.getContext('2d');canvas.width=raster.width;canvas.height=raster.height;const imageData=ctx.createImageData(raster.width,raster.height);imageData.data.set(new Uint8ClampedArray(raster.data));rasterCompleteCallback(new tr.e.cc.PictureAsImageData(this,imageData));}else{const error='Failed to rasterize picture. '+'Your recording may be from an old Chrome version. '+'The SkPicture format is not backward compatible.';rasterCompleteCallback(new tr.e.cc.PictureAsImageData(this,error));}}};function LayeredPicture(pictures){this.guid_=tr.b.GUID.allocateSimple();this.pictures_=pictures;this.layerRect_=undefined;}
+LayeredPicture.prototype={__proto__:Picture.prototype,get canSave(){return false;},get typeName(){return'cc::LayeredPicture';},get layerRect(){if(this.layerRect_!==undefined){return this.layerRect_;}
+this.layerRect_={x:0,y:0,width:0,height:0};for(let i=0;i<this.pictures_.length;++i){const rect=this.pictures_[i].layerRect;this.layerRect_.x=Math.min(this.layerRect_.x,rect.x);this.layerRect_.y=Math.min(this.layerRect_.y,rect.y);this.layerRect_.width=Math.max(this.layerRect_.width,rect.x+rect.width);this.layerRect_.height=Math.max(this.layerRect_.height,rect.y+rect.height);}
+return this.layerRect_;},get guid(){return this.guid_;},getBase64SkpData(){throw new Error('Not available with a LayeredPicture.');},getOps(){let ops=[];for(let i=0;i<this.pictures_.length;++i){ops=ops.concat(this.pictures_[i].getOps());}
+return ops;},getOpTimings(){const opTimings=this.pictures_[0].getOpTimings();for(let i=1;i<this.pictures_.length;++i){const timings=this.pictures_[i].getOpTimings();opTimings.cmd_times=opTimings.cmd_times.concat(timings.cmd_times);opTimings.total_time+=timings.total_time;}
+return opTimings;},tagOpsWithTimings(ops){const opTimings=[];for(let iteration=0;iteration<OPS_TIMING_ITERATIONS;iteration++){opTimings[iteration]=this.getOpTimings();if(!opTimings[iteration]||!opTimings[iteration].cmd_times){return ops;}}
+for(let opIndex=0;opIndex<ops.length;opIndex++){let min=Number.MAX_VALUE;for(let i=0;i<OPS_TIMING_ITERATIONS;i++){min=Math.min(min,opTimings[i].cmd_times[opIndex]);}
+ops[opIndex].cmd_time=min;}
+return ops;},rasterize(params,rasterCompleteCallback){this.picturesAsImageData_=[];const rasterCallback=function(pictureAsImageData){this.picturesAsImageData_.push(pictureAsImageData);if(this.picturesAsImageData_.length!==this.pictures_.length){return;}
+const canvas=document.createElement('canvas');const ctx=canvas.getContext('2d');canvas.width=this.layerRect.width;canvas.height=this.layerRect.height;for(let i=0;i<this.picturesAsImageData_.length;++i){ctx.putImageData(this.picturesAsImageData_[i].imageData,this.pictures_[i].layerRect.x,this.pictures_[i].layerRect.y);}
+this.picturesAsImageData_=[];rasterCompleteCallback(new tr.e.cc.PictureAsImageData(this,ctx.getImageData(this.layerRect.x,this.layerRect.y,this.layerRect.width,this.layerRect.height)));}.bind(this);for(let i=0;i<this.pictures_.length;++i){this.pictures_[i].rasterize(params,rasterCallback);}}};function PictureSnapshot(){ObjectSnapshot.apply(this,arguments);}
+PictureSnapshot.HasSkiaBenchmarking=function(){return tr.isExported('chrome.skiaBenchmarking');};PictureSnapshot.CanRasterize=function(){if(!PictureSnapshot.HasSkiaBenchmarking()){return false;}
+if(!window.chrome.skiaBenchmarking.rasterize){return false;}
+return true;};PictureSnapshot.CanGetOps=function(){if(!PictureSnapshot.HasSkiaBenchmarking()){return false;}
+if(!window.chrome.skiaBenchmarking.getOps){return false;}
+return true;};PictureSnapshot.CanGetOpTimings=function(){if(!PictureSnapshot.HasSkiaBenchmarking()){return false;}
+if(!window.chrome.skiaBenchmarking.getOpTimings){return false;}
+return true;};PictureSnapshot.CanGetInfo=function(){if(!PictureSnapshot.HasSkiaBenchmarking()){return false;}
+if(!window.chrome.skiaBenchmarking.getInfo){return false;}
+return true;};PictureSnapshot.HowToEnablePictureDebugging=function(){if(tr.isHeadless){return'Pictures only work in chrome';}
+const usualReason=['For pictures to show up, the Chrome browser displaying the trace ','needs to be running with --enable-skia-benchmarking. Please restart ','chrome with this flag and try loading the trace again.'].join('');if(!PictureSnapshot.HasSkiaBenchmarking()){return usualReason;}
+if(!PictureSnapshot.CanRasterize()){return'Your chrome is old: chrome.skipBenchmarking.rasterize not found';}
+if(!PictureSnapshot.CanGetOps()){return'Your chrome is old: chrome.skiaBenchmarking.getOps not found';}
+if(!PictureSnapshot.CanGetOpTimings()){return'Your chrome is old: '+'chrome.skiaBenchmarking.getOpTimings not found';}
+if(!PictureSnapshot.CanGetInfo()){return'Your chrome is old: chrome.skiaBenchmarking.getInfo not found';}
+return undefined;};PictureSnapshot.CanDebugPicture=function(){return PictureSnapshot.HowToEnablePictureDebugging()===undefined;};PictureSnapshot.prototype={__proto__:ObjectSnapshot.prototype,preInitialize(){tr.e.cc.preInitializeObject(this);this.rasterResult_=undefined;},initialize(){if(this.args.alias){this.args=this.args.alias.args;}
+if(!this.args.params.layerRect){throw new Error('Missing layer rect');}
+this.layerRect_=this.args.params.layerRect;this.picture_=new Picture(this.args.skp64,this.args.params.layerRect);},set picture(picture){this.picture_=picture;},get canSave(){return this.picture_.canSave;},get layerRect(){return this.layerRect_?this.layerRect_:this.picture_.layerRect;},get guid(){return this.picture_.guid;},getBase64SkpData(){return this.picture_.getBase64SkpData();},getOps(){return this.picture_.getOps();},getOpTimings(){return this.picture_.getOpTimings();},tagOpsWithTimings(ops){return this.picture_.tagOpsWithTimings(ops);},rasterize(params,rasterCompleteCallback){this.picture_.rasterize(params,rasterCompleteCallback);}};ObjectSnapshot.subTypes.register(PictureSnapshot,{typeNames:['cc::Picture']});return{PictureSnapshot,Picture,LayeredPicture,};});'use strict';tr.exportTo('tr.e.cc',function(){const ObjectSnapshot=tr.model.ObjectSnapshot;function DisplayItemList(skp64,layerRect){tr.e.cc.Picture.apply(this,arguments);}
+DisplayItemList.prototype={__proto__:tr.e.cc.Picture.prototype};function DisplayItemListSnapshot(){tr.e.cc.PictureSnapshot.apply(this,arguments);}
+DisplayItemListSnapshot.prototype={__proto__:tr.e.cc.PictureSnapshot.prototype,initialize(){tr.e.cc.PictureSnapshot.prototype.initialize.call(this);this.displayItems_=this.args.params.items;},get items(){return this.displayItems_;}};ObjectSnapshot.subTypes.register(DisplayItemListSnapshot,{typeNames:['cc::DisplayItemList']});return{DisplayItemListSnapshot,DisplayItemList,};});'use strict';tr.exportTo('tr.b.math',function(){function BBox2(){this.isEmpty_=true;this.min_=undefined;this.max_=undefined;}
+BBox2.prototype={__proto__:Object.prototype,reset(){this.isEmpty_=true;this.min_=undefined;this.max_=undefined;},get isEmpty(){return this.isEmpty_;},addBBox2(bbox2){if(bbox2.isEmpty)return;this.addVec2(bbox2.min_);this.addVec2(bbox2.max_);},clone(){const bbox=new BBox2();bbox.addBBox2(this);return bbox;},addXY(x,y){if(this.isEmpty_){this.max_=vec2.create();this.min_=vec2.create();vec2.set(this.max_,x,y);vec2.set(this.min_,x,y);this.isEmpty_=false;return;}
+this.max_[0]=Math.max(this.max_[0],x);this.max_[1]=Math.max(this.max_[1],y);this.min_[0]=Math.min(this.min_[0],x);this.min_[1]=Math.min(this.min_[1],y);},addVec2(value){if(this.isEmpty_){this.max_=vec2.create();this.min_=vec2.create();vec2.set(this.max_,value[0],value[1]);vec2.set(this.min_,value[0],value[1]);this.isEmpty_=false;return;}
+this.max_[0]=Math.max(this.max_[0],value[0]);this.max_[1]=Math.max(this.max_[1],value[1]);this.min_[0]=Math.min(this.min_[0],value[0]);this.min_[1]=Math.min(this.min_[1],value[1]);},addQuad(quad){this.addVec2(quad.p1);this.addVec2(quad.p2);this.addVec2(quad.p3);this.addVec2(quad.p4);},get minVec2(){if(this.isEmpty_)return undefined;return this.min_;},get maxVec2(){if(this.isEmpty_)return undefined;return this.max_;},get sizeAsVec2(){if(this.isEmpty_){throw new Error('Empty BBox2 has no size');}
+const size=vec2.create();vec2.subtract(size,this.max_,this.min_);return size;},get size(){if(this.isEmpty_){throw new Error('Empty BBox2 has no size');}
+return{width:this.max_[0]-this.min_[0],height:this.max_[1]-this.min_[1]};},get width(){if(this.isEmpty_){throw new Error('Empty BBox2 has no width');}
+return this.max_[0]-this.min_[0];},get height(){if(this.isEmpty_){throw new Error('Empty BBox2 has no width');}
+return this.max_[1]-this.min_[1];},toString(){if(this.isEmpty_)return'empty';return'min=('+this.min_[0]+','+this.min_[1]+') '+'max=('+this.max_[0]+','+this.max_[1]+')';},asRect(){return tr.b.math.Rect.fromXYWH(this.min_[0],this.min_[1],this.max_[0]-this.min_[0],this.max_[1]-this.min_[1]);}};return{BBox2,};});'use strict';tr.exportTo('tr.e.cc',function(){const constants={};constants.ACTIVE_TREE=0;constants.PENDING_TREE=1;constants.HIGH_PRIORITY_BIN=0;constants.LOW_PRIORITY_BIN=1;constants.SEND_BEGIN_FRAME_EVENT='ThreadProxy::ScheduledActionSendBeginMainFrame';constants.BEGIN_MAIN_FRAME_EVENT='ThreadProxy::BeginMainFrame';return{constants};});'use strict';tr.exportTo('tr.e.cc',function(){function Region(){this.rects=[];}
+Region.fromArray=function(array){if(array.length%4!==0){throw new Error('Array must consist be a multiple of 4 in length');}
+const r=new Region();for(let i=0;i<array.length;i+=4){r.rects.push(tr.b.math.Rect.fromXYWH(array[i],array[i+1],array[i+2],array[i+3]));}
+return r;};Region.fromArrayOrUndefined=function(array){if(array===undefined)return new Region();return Region.fromArray(array);};Region.prototype={__proto__:Region.prototype,rectIntersects(r){for(let i=0;i<this.rects.length;i++){if(this.rects[i].intersects(r))return true;}
+return false;},addRect(r){this.rects.push(r);}};return{Region,};});'use strict';tr.exportTo('tr.e.cc',function(){function TileCoverageRect(rect,tile){this.geometryRect=rect;this.tile=tile;}
+return{TileCoverageRect,};});'use strict';tr.exportTo('tr.e.cc',function(){const constants=tr.e.cc.constants;const ObjectSnapshot=tr.model.ObjectSnapshot;function LayerImplSnapshot(){ObjectSnapshot.apply(this,arguments);}
+LayerImplSnapshot.prototype={__proto__:ObjectSnapshot.prototype,preInitialize(){tr.e.cc.preInitializeObject(this);this.layerTreeImpl_=undefined;this.parentLayer=undefined;},initialize(){this.invalidation=new tr.e.cc.Region();this.unrecordedRegion=new tr.e.cc.Region();this.pictures=[];tr.e.cc.moveRequiredFieldsFromArgsToToplevel(this,['layerId','layerQuad']);tr.e.cc.moveOptionalFieldsFromArgsToToplevel(this,['children','maskLayer','replicaLayer','idealContentsScale','geometryContentsScale','layoutRects','usingGpuRasterization']);this.gpuMemoryUsageInBytes=this.args.gpuMemoryUsage;this.bounds=tr.b.math.Rect.fromXYWH(0,0,this.args.bounds.width,this.args.bounds.height);if(this.args.animationBounds){this.animationBoundsRect=tr.b.math.Rect.fromXYWH(this.args.animationBounds[0],this.args.animationBounds[1],this.args.animationBounds[3],this.args.animationBounds[4]);}
+if(this.children){for(let i=0;i<this.children.length;i++){this.children[i].parentLayer=this;}}
+if(this.maskLayer){this.maskLayer.parentLayer=this;}
+if(this.replicaLayer){this.replicaLayer.parentLayer=this;}
+if(!this.geometryContentsScale){this.geometryContentsScale=1.0;}
+if(!this.idealContentsScale){this.idealContentsScale=1.0;}
+this.touchEventHandlerRegion=tr.e.cc.Region.fromArrayOrUndefined(this.args.touchEventHandlerRegion);this.wheelEventHandlerRegion=tr.e.cc.Region.fromArrayOrUndefined(this.args.wheelEventHandlerRegion);this.nonFastScrollableRegion=tr.e.cc.Region.fromArrayOrUndefined(this.args.nonFastScrollableRegion);},get layerTreeImpl(){if(this.layerTreeImpl_){return this.layerTreeImpl_;}
+if(this.parentLayer){return this.parentLayer.layerTreeImpl;}
+return undefined;},set layerTreeImpl(layerTreeImpl){this.layerTreeImpl_=layerTreeImpl;},get activeLayer(){if(this.layerTreeImpl.whichTree===constants.ACTIVE_TREE){return this;}
+const activeTree=this.layerTreeImpl.layerTreeHostImpl.activeTree;return activeTree.findLayerWithId(this.layerId);},get pendingLayer(){if(this.layerTreeImpl.whichTree===constants.PENDING_TREE){return this;}
+const pendingTree=this.layerTreeImpl.layerTreeHostImpl.pendingTree;return pendingTree.findLayerWithId(this.layerId);}};function PictureLayerImplSnapshot(){LayerImplSnapshot.apply(this,arguments);}
+PictureLayerImplSnapshot.prototype={__proto__:LayerImplSnapshot.prototype,initialize(){LayerImplSnapshot.prototype.initialize.call(this);if(this.args.debugInfo){for(const i in this.args.debugInfo){this.args[i]=this.args.debugInfo[i];}
+delete this.args.debugInfo;}
+if(this.args.annotatedInvalidationRects){this.invalidation=new tr.e.cc.Region();for(const annotatedRect of this.args.annotatedInvalidationRects){const rect=annotatedRect.geometryRect;rect.reason=annotatedRect.reason;rect.client=annotatedRect.client;this.invalidation.addRect(rect);}
+delete this.args.annotatedInvalidationRects;}else if(this.args.invalidation){this.invalidation=tr.e.cc.Region.fromArray(this.args.invalidation);}
+delete this.args.invalidation;if(this.args.unrecordedRegion){this.unrecordedRegion=tr.e.cc.Region.fromArray(this.args.unrecordedRegion);delete this.args.unrecordedRegion;}
+if(this.args.pictures){this.pictures=this.args.pictures;this.pictures.sort(function(a,b){return a.ts-b.ts;});}
+this.tileCoverageRects=[];if(this.args.coverageTiles){for(let i=0;i<this.args.coverageTiles.length;++i){const rect=this.args.coverageTiles[i].geometryRect.scale(this.idealContentsScale);const tile=this.args.coverageTiles[i].tile;this.tileCoverageRects.push(new tr.e.cc.TileCoverageRect(rect,tile));}
+delete this.args.coverageTiles;}}};ObjectSnapshot.subTypes.register(PictureLayerImplSnapshot,{typeName:'cc::PictureLayerImpl'});ObjectSnapshot.subTypes.register(LayerImplSnapshot,{typeNames:['cc::LayerImpl','cc::DelegatedRendererLayerImpl','cc::HeadsUpDisplayLayerImpl','cc::IOSurfaceLayerImpl','cc::NinePatchLayerImpl','cc::PictureImageLayerImpl','cc::ScrollbarLayerImpl','cc::SolidColorLayerImpl','cc::SolidColorScrollbarLayerImpl','cc::SurfaceLayerImpl','cc::TextureLayerImpl','cc::TiledLayerImpl','cc::VideoLayerImpl','cc::PaintedScrollbarLayerImpl','ClankPatchLayer','TabBorderLayer','CounterLayer']});return{LayerImplSnapshot,PictureLayerImplSnapshot,};});'use strict';tr.exportTo('tr.e.cc',function(){const constants=tr.e.cc.constants;const ObjectSnapshot=tr.model.ObjectSnapshot;function LayerTreeImplSnapshot(){ObjectSnapshot.apply(this,arguments);}
+LayerTreeImplSnapshot.prototype={__proto__:ObjectSnapshot.prototype,preInitialize(){tr.e.cc.preInitializeObject(this);this.layerTreeHostImpl=undefined;this.whichTree=undefined;this.sourceFrameNumber=undefined;},initialize(){tr.e.cc.moveRequiredFieldsFromArgsToToplevel(this,['renderSurfaceLayerList']);tr.e.cc.moveOptionalFieldsFromArgsToToplevel(this,['rootLayer','layers']);if(this.args.sourceFrameNumber){this.sourceFrameNumber=this.args.sourceFrameNumber;}
+if(this.rootLayer){this.rootLayer.layerTreeImpl=this;}else{for(let i=0;i<this.layers.length;i++){this.layers[i].layerTreeImpl=this;}}
+if(this.args.swapPromiseTraceIds&&this.args.swapPromiseTraceIds.length){this.tracedInputLatencies=[];const ownProcess=this.objectInstance.parent;const modelHelper=ownProcess.model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);if(modelHelper){this._initializeTracedInputLatencies(modelHelper);}}},_initializeTracedInputLatencies(modelHelper){const latencyEvents=modelHelper.browserHelper.getLatencyEventsInRange(modelHelper.model.bounds);latencyEvents.forEach(function(event){for(let i=0;i<this.args.swapPromiseTraceIds.length;i++){if(!event.args.data||!event.args.data.trace_id){continue;}
+if(parseInt(event.args.data.trace_id)===this.args.swapPromiseTraceIds[i]){this.tracedInputLatencies.push(event);}}},this);},get hasSourceFrameBeenDrawnBefore(){if(this.whichTree===tr.e.cc.constants.PENDING_TREE){return false;}
+if(this.sourceFrameNumber===undefined)return;const thisLTHI=this.layerTreeHostImpl;const thisLTHIIndex=thisLTHI.objectInstance.snapshots.indexOf(thisLTHI);const prevLTHIIndex=thisLTHIIndex-1;if(prevLTHIIndex<0||prevLTHIIndex>=thisLTHI.objectInstance.snapshots.length){return false;}
+const prevLTHI=thisLTHI.objectInstance.snapshots[prevLTHIIndex];if(!prevLTHI.activeTree)return false;if(prevLTHI.activeTree.sourceFrameNumber===undefined)return;return prevLTHI.activeTree.sourceFrameNumber===this.sourceFrameNumber;},get otherTree(){const other=this.whichTree===constants.ACTIVE_TREE?constants.PENDING_TREE:constants.ACTIVE_TREE;return this.layerTreeHostImpl.getTree(other);},get gpuMemoryUsageInBytes(){let totalBytes=0;this.iterLayers(function(layer){if(layer.gpuMemoryUsageInBytes!==undefined){totalBytes+=layer.gpuMemoryUsageInBytes;}});return totalBytes;},iterLayers(func,thisArg){const visitedLayers={};function visitLayer(layer,depth,isMask,isReplica){if(visitedLayers[layer.layerId])return;visitedLayers[layer.layerId]=true;func.call(thisArg,layer,depth,isMask,isReplica);if(layer.children){for(let i=0;i<layer.children.length;i++){visitLayer(layer.children[i],depth+1);}}
+if(layer.maskLayer){visitLayer(layer.maskLayer,depth+1,true,false);}
+if(layer.replicaLayer){visitLayer(layer.replicaLayer,depth+1,false,true);}}
+if(this.rootLayer){visitLayer(this.rootLayer,0,false,false);}else{for(let i=0;i<this.layers.length;i++){visitLayer(this.layers[i],0,false,false);}}},findLayerWithId(id){let foundLayer=undefined;function visitLayer(layer){if(layer.layerId===id){foundLayer=layer;}}
+this.iterLayers(visitLayer);return foundLayer;}};ObjectSnapshot.subTypes.register(LayerTreeImplSnapshot,{typeName:'cc::LayerTreeImpl'});return{LayerTreeImplSnapshot,};});'use strict';tr.exportTo('tr.e.cc',function(){const constants=tr.e.cc.constants;const ObjectSnapshot=tr.model.ObjectSnapshot;const ObjectInstance=tr.model.ObjectInstance;function LayerTreeHostImplSnapshot(){ObjectSnapshot.apply(this,arguments);}
+LayerTreeHostImplSnapshot.prototype={__proto__:ObjectSnapshot.prototype,preInitialize(){tr.e.cc.preInitializeObject(this);},initialize(){tr.e.cc.moveRequiredFieldsFromArgsToToplevel(this,['deviceViewportSize','activeTree']);tr.e.cc.moveOptionalFieldsFromArgsToToplevel(this,['pendingTree']);if(this.args.activeTiles!==undefined){this.activeTiles=this.args.activeTiles;delete this.args.activeTiles;}else if(this.args.tiles!==undefined){this.activeTiles=this.args.tiles;delete this.args.tiles;}
+if(!this.activeTiles){this.activeTiles=[];}
+this.activeTree.layerTreeHostImpl=this;this.activeTree.whichTree=constants.ACTIVE_TREE;if(this.pendingTree){this.pendingTree.layerTreeHostImpl=this;this.pendingTree.whichTree=constants.PENDING_TREE;}},getContentsScaleNames(){const scales={};for(let i=0;i<this.activeTiles.length;++i){const tile=this.activeTiles[i];scales[tile.contentsScale]=tile.resolution;}
+return scales;},getTree(whichTree){if(whichTree===constants.ACTIVE_TREE){return this.activeTree;}
+if(whichTree===constants.PENDING_TREE){return this.pendingTree;}
+throw new Exception('Unknown tree type + '+whichTree);},get tilesHaveGpuMemoryUsageInfo(){if(this.tilesHaveGpuMemoryUsageInfo_!==undefined){return this.tilesHaveGpuMemoryUsageInfo_;}
+for(let i=0;i<this.activeTiles.length;i++){if(this.activeTiles[i].gpuMemoryUsageInBytes===undefined){continue;}
+this.tilesHaveGpuMemoryUsageInfo_=true;return true;}
+this.tilesHaveGpuMemoryUsageInfo_=false;return false;},get gpuMemoryUsageInBytes(){if(!this.tilesHaveGpuMemoryUsageInfo)return;let usage=0;for(let i=0;i<this.activeTiles.length;i++){const u=this.activeTiles[i].gpuMemoryUsageInBytes;if(u!==undefined)usage+=u;}
+return usage;},get userFriendlyName(){let frameNumber;if(!this.activeTree){frameNumber=this.objectInstance.snapshots.indexOf(this);}else{if(this.activeTree.sourceFrameNumber===undefined){frameNumber=this.objectInstance.snapshots.indexOf(this);}else{frameNumber=this.activeTree.sourceFrameNumber;}}
+return'cc::LayerTreeHostImpl frame '+frameNumber;}};ObjectSnapshot.subTypes.register(LayerTreeHostImplSnapshot,{typeName:'cc::LayerTreeHostImpl'});function LayerTreeHostImplInstance(){ObjectInstance.apply(this,arguments);this.allLayersBBox_=undefined;}
+LayerTreeHostImplInstance.prototype={__proto__:ObjectInstance.prototype,get allContentsScales(){if(this.allContentsScales_){return this.allContentsScales_;}
+const scales={};for(const tileID in this.allTileHistories_){const tileHistory=this.allTileHistories_[tileID];scales[tileHistory.contentsScale]=true;}
+this.allContentsScales_=Object.keys(scales);return this.allContentsScales_;},get allLayersBBox(){if(this.allLayersBBox_){return this.allLayersBBox_;}
+const bbox=new tr.b.math.BBox2();function handleTree(tree){tree.renderSurfaceLayerList.forEach(function(layer){bbox.addQuad(layer.layerQuad);});}
+this.snapshots.forEach(function(lthi){handleTree(lthi.activeTree);if(lthi.pendingTree){handleTree(lthi.pendingTree);}});this.allLayersBBox_=bbox;return this.allLayersBBox_;}};ObjectInstance.subTypes.register(LayerTreeHostImplInstance,{typeName:'cc::LayerTreeHostImpl'});return{LayerTreeHostImplSnapshot,LayerTreeHostImplInstance,};});'use strict';tr.exportTo('tr.e.cc',function(){const tileTypes={highRes:'highRes',lowRes:'lowRes',extraHighRes:'extraHighRes',extraLowRes:'extraLowRes',missing:'missing',culled:'culled',solidColor:'solidColor',picture:'picture',directPicture:'directPicture',unknown:'unknown'};const tileBorder={highRes:{color:'rgba(80, 200, 200, 0.7)',width:1},lowRes:{color:'rgba(212, 83, 192, 0.7)',width:2},extraHighRes:{color:'rgba(239, 231, 20, 0.7)',width:2},extraLowRes:{color:'rgba(93, 186, 18, 0.7)',width:2},missing:{color:'rgba(255, 0, 0, 0.7)',width:1},culled:{color:'rgba(160, 100, 0, 0.8)',width:1},solidColor:{color:'rgba(128, 128, 128, 0.7)',width:1},picture:{color:'rgba(64, 64, 64, 0.7)',width:1},directPicture:{color:'rgba(127, 255, 0, 1.0)',width:1},unknown:{color:'rgba(0, 0, 0, 1.0)',width:2}};return{tileTypes,tileBorder};});'use strict';tr.exportTo('tr.e.cc',function(){const ObjectSnapshot=tr.model.ObjectSnapshot;function TileSnapshot(){ObjectSnapshot.apply(this,arguments);}
+TileSnapshot.prototype={__proto__:ObjectSnapshot.prototype,preInitialize(){tr.e.cc.preInitializeObject(this);},initialize(){tr.e.cc.moveOptionalFieldsFromArgsToToplevel(this,['layerId','contentsScale','contentRect']);if(this.args.managedState){this.resolution=this.args.managedState.resolution;this.isSolidColor=this.args.managedState.isSolidColor;this.isUsingGpuMemory=this.args.managedState.isUsingGpuMemory;this.hasResource=this.args.managedState.hasResource;this.scheduledPriority=this.args.scheduledPriority;this.gpuMemoryUsageInBytes=this.args.gpuMemoryUsage;}else{this.resolution=this.args.resolution;this.isSolidColor=this.args.drawInfo.isSolidColor;this.isUsingGpuMemory=this.args.isUsingGpuMemory;this.hasResource=this.args.hasResource;this.scheduledPriority=this.args.scheduledPriority;this.gpuMemoryUsageInBytes=this.args.gpuMemoryUsage;}
+if(this.contentRect){this.layerRect=this.contentRect.scale(1.0/this.contentsScale);}
+if(this.isSolidColor){this.type_=tr.e.cc.tileTypes.solidColor;}else if(!this.hasResource){this.type_=tr.e.cc.tileTypes.missing;}else if(this.resolution==='HIGH_RESOLUTION'){this.type_=tr.e.cc.tileTypes.highRes;}else if(this.resolution==='LOW_RESOLUTION'){this.type_=tr.e.cc.tileTypes.lowRes;}else{this.type_=tr.e.cc.tileTypes.unknown;}},getTypeForLayer(layer){let type=this.type_;if(type===tr.e.cc.tileTypes.unknown){if(this.contentsScale<layer.idealContentsScale){type=tr.e.cc.tileTypes.extraLowRes;}else if(this.contentsScale>layer.idealContentsScale){type=tr.e.cc.tileTypes.extraHighRes;}}
+return type;}};ObjectSnapshot.subTypes.register(TileSnapshot,{typeName:'cc::Tile'});return{TileSnapshot,};});'use strict';tr.exportTo('tr.ui.b',function(){const Location=tr.model.Location;function UIState(location,scaleX){this.location_=location;this.scaleX_=scaleX;}
+UIState.fromUserFriendlyString=function(model,viewport,stateString){const navByFinderPattern=/^(-?\d+(\.\d+)?)@(.+)x(\d+(\.\d+)?)$/g;const match=navByFinderPattern.exec(stateString);if(!match)return;const timestamp=parseFloat(match[1]);const stableId=match[3];const scaleX=parseFloat(match[4]);if(scaleX<=0){throw new Error('Invalid ScaleX value in UI State string.');}
+if(!viewport.containerToTrackMap.getTrackByStableId(stableId)){throw new Error('Invalid StableID given in UI State String.');}
+const loc=tr.model.Location.fromStableIdAndTimestamp(viewport,stableId,timestamp);return new UIState(loc,scaleX);};UIState.prototype={get location(){return this.location_;},get scaleX(){return this.scaleX_;},toUserFriendlyString(viewport){const timestamp=this.location_.xWorld;const stableId=this.location_.getContainingTrack(viewport).eventContainer.stableId;const scaleX=this.scaleX_;return timestamp.toFixed(5)+'@'+stableId+'x'+scaleX.toFixed(5);},toDict(){return{location:this.location_.toDict(),scaleX:this.scaleX_};}};return{UIState,};});'use strict';tr.exportTo('tr.ui.b',function(){const EventSet=tr.model.EventSet;const SelectionState=tr.model.SelectionState;function BrushingState(){this.guid_=tr.b.GUID.allocateSimple();this.selection_=new EventSet();this.findMatches_=new EventSet();this.analysisViewRelatedEvents_=new EventSet();this.analysisLinkHoveredEvents_=new EventSet();this.appliedToModel_=undefined;this.viewSpecificBrushingStates_={};}
+BrushingState.prototype={get guid(){return this.guid_;},clone(){const that=new BrushingState();that.selection_=this.selection_;that.findMatches_=this.findMatches_;that.analysisViewRelatedEvents_=this.analysisViewRelatedEvents_;that.analysisLinkHoveredEvents_=this.analysisLinkHoveredEvents_;that.viewSpecificBrushingStates_=this.viewSpecificBrushingStates_;return that;},equals(that){if(!this.selection_.equals(that.selection_)){return false;}
+if(!this.findMatches_.equals(that.findMatches_)){return false;}
+if(!this.analysisViewRelatedEvents_.equals(that.analysisViewRelatedEvents_)){return false;}
+if(!this.analysisLinkHoveredEvents_.equals(that.analysisLinkHoveredEvents_)){return false;}
+return true;},get selectionOfInterest(){if(this.selection_.length){return this.selection_;}
+if(this.highlight_.length){return this.highlight_;}
+if(this.analysisViewRelatedEvents_.length){return this.analysisViewRelatedEvents_;}
+if(this.analysisLinkHoveredEvents_.length){return this.analysisLinkHoveredEvents_;}
+return this.selection_;},get selection(){return this.selection_;},set selection(selection){if(this.appliedToModel_){throw new Error('Cannot mutate this state right now');}
+if(selection===undefined){selection=new EventSet();}
+this.selection_=selection;},get findMatches(){return this.findMatches_;},set findMatches(findMatches){if(this.appliedToModel_){throw new Error('Cannot mutate this state right now');}
+if(findMatches===undefined){findMatches=new EventSet();}
+this.findMatches_=findMatches;},get analysisViewRelatedEvents(){return this.analysisViewRelatedEvents_;},set analysisViewRelatedEvents(analysisViewRelatedEvents){if(this.appliedToModel_){throw new Error('Cannot mutate this state right now');}
+if(!(analysisViewRelatedEvents instanceof EventSet)){analysisViewRelatedEvents=new EventSet();}
+this.analysisViewRelatedEvents_=analysisViewRelatedEvents;},get analysisLinkHoveredEvents(){return this.analysisLinkHoveredEvents_;},set analysisLinkHoveredEvents(analysisLinkHoveredEvents){if(this.appliedToModel_){throw new Error('Cannot mutate this state right now');}
+if(!(analysisLinkHoveredEvents instanceof EventSet)){analysisLinkHoveredEvents=new EventSet();}
+this.analysisLinkHoveredEvents_=analysisLinkHoveredEvents;},get isAppliedToModel(){return this.appliedToModel_!==undefined;},get viewSpecificBrushingStates(){return this.viewSpecificBrushingStates_;},set viewSpecificBrushingStates(viewSpecificBrushingStates){this.viewSpecificBrushingStates_=viewSpecificBrushingStates;},get defaultState_(){const standoutEventExists=(this.analysisLinkHoveredEvents_.length>0||this.analysisViewRelatedEvents_.length>0||this.findMatches_.length>0);return(standoutEventExists?SelectionState.DIMMED0:SelectionState.NONE);},get brightenedEvents_(){const brightenedEvents=new EventSet();brightenedEvents.addEventSet(this.findMatches);brightenedEvents.addEventSet(this.analysisViewRelatedEvents_);brightenedEvents.addEventSet(this.selection_);brightenedEvents.addEventSet(this.analysisLinkHoveredEvents_);return brightenedEvents;},applyToEventSelectionStates(model){this.appliedToModel_=model;if(model){const newDefaultState=this.defaultState_;const currentDefaultState=tr.b.getFirstElement(model.getDescendantEvents()).selectionState;if(currentDefaultState!==newDefaultState){for(const e of model.getDescendantEvents()){e.selectionState=newDefaultState;}}}
+let level;for(const e of this.brightenedEvents_){level=0;if(this.analysisViewRelatedEvents_.contains(e)||this.findMatches_.contains(e)){level++;}
+if(this.analysisLinkHoveredEvents_.contains(e)){level++;}
+if(this.selection_.contains(e)){level++;}
+e.selectionState=SelectionState.getFromBrighteningLevel(level);}},transferModelOwnershipToClone(that){if(!this.appliedToModel_){throw new Error('Not applied');}
+that.appliedToModel_=this.appliedToModel_;this.appliedToModel_=undefined;},unapplyFromEventSelectionStates(){if(!this.appliedToModel_){throw new Error('Not applied');}
+const model=this.appliedToModel_;this.appliedToModel_=undefined;const defaultState=this.defaultState_;for(const e of this.brightenedEvents_){e.selectionState=defaultState;}
+return defaultState;}};return{BrushingState,};});'use strict';tr.exportTo('tr.ui.b',function(){function Animation(){}
+Animation.prototype={canTakeOverFor(existingAnimation){throw new Error('Not implemented');},takeOverFor(existingAnimation,newStartTimestamp,target){throw new Error('Not implemented');},start(timestamp,target){throw new Error('Not implemented');},didStopEarly(timestamp,target,willBeTakenOverByAnotherAnimation){},tick(timestamp,target){throw new Error('Not implemented');}};return{Animation,};});'use strict';tr.exportTo('tr.ui.b',function(){function AnimationController(){tr.b.EventTarget.call(this);this.target_=undefined;this.activeAnimation_=undefined;this.tickScheduled_=false;}
+AnimationController.prototype={__proto__:tr.b.EventTarget.prototype,get target(){return this.target_;},set target(target){if(this.activeAnimation_){throw new Error('Cannot change target while animation is running.');}
+if(target.cloneAnimationState===undefined||typeof target.cloneAnimationState!=='function'){throw new Error('target must have a cloneAnimationState function');}
+this.target_=target;},get activeAnimation(){return this.activeAnimation_;},get hasActiveAnimation(){return!!this.activeAnimation_;},queueAnimation(animation,opt_now){if(this.target_===undefined){throw new Error('Cannot queue animations without a target');}
+let now;if(opt_now!==undefined){now=opt_now;}else{now=window.performance.now();}
+if(this.activeAnimation_){const done=this.activeAnimation_.tick(now,this.target_);if(done){this.activeAnimation_=undefined;}}
+if(this.activeAnimation_){if(animation.canTakeOverFor(this.activeAnimation_)){this.activeAnimation_.didStopEarly(now,this.target_,true);animation.takeOverFor(this.activeAnimation_,now,this.target_);}else{this.activeAnimation_.didStopEarly(now,this.target_,false);}}
+this.activeAnimation_=animation;this.activeAnimation_.start(now,this.target_);if(this.tickScheduled_)return;this.tickScheduled_=true;tr.b.requestAnimationFrame(this.tickActiveAnimation_,this);},cancelActiveAnimation(opt_now){if(!this.activeAnimation_)return;let now;if(opt_now!==undefined){now=opt_now;}else{now=window.performance.now();}
+this.activeAnimation_.didStopEarly(now,this.target_,false);this.activeAnimation_=undefined;},tickActiveAnimation_(frameBeginTime){this.tickScheduled_=false;if(!this.activeAnimation_)return;if(this.target_===undefined){this.activeAnimation_.didStopEarly(frameBeginTime,this.target_,false);return;}
+const oldTargetState=this.target_.cloneAnimationState();const done=this.activeAnimation_.tick(frameBeginTime,this.target_);if(done){this.activeAnimation_=undefined;}
+if(this.activeAnimation_){this.tickScheduled_=true;tr.b.requestAnimationFrame(this.tickActiveAnimation_,this);}
+if(oldTargetState){const e=new tr.b.Event('didtick');e.oldTargetState=oldTargetState;this.dispatchEvent(e,false,false);}}};return{AnimationController,};});'use strict';tr.exportTo('tr.b',function(){function Settings(){return Settings;}
+if(tr.b.unittest&&tr.b.unittest.TestRunner){tr.b.unittest.TestRunner.addEventListener('tr-unittest-will-run',function(){if(tr.isHeadless){Settings.setAlternativeStorageInstance(new HeadlessStorage());}else{Settings.setAlternativeStorageInstance(global.sessionStorage);global.sessionStorage.clear();}});}
+function SessionSettings(){return SessionSettings;}
+function AddStaticStorageFunctionsToClass_(inputClass,storage){inputClass.storage_=storage;inputClass.get=function(key,opt_default,opt_namespace){key=inputClass.namespace_(key,opt_namespace);const rawVal=inputClass.storage_.getItem(key);if(rawVal===null||rawVal===undefined){return opt_default;}
+try{return JSON.parse(rawVal).value;}catch(e){inputClass.storage_.removeItem(key);return opt_default;}};inputClass.set=function(key,value,opt_namespace){if(value===undefined){throw new Error('Settings.set: value must not be undefined');}
+const v=JSON.stringify({value});inputClass.storage_.setItem(inputClass.namespace_(key,opt_namespace),v);};inputClass.keys=function(opt_namespace){const result=[];opt_namespace=opt_namespace||'';for(let i=0;i<inputClass.storage_.length;i++){const key=inputClass.storage_.key(i);if(inputClass.isnamespaced_(key,opt_namespace)){result.push(inputClass.unnamespace_(key,opt_namespace));}}
+return result;};inputClass.isnamespaced_=function(key,opt_namespace){return key.indexOf(inputClass.normalize_(opt_namespace))===0;};inputClass.namespace_=function(key,opt_namespace){return inputClass.normalize_(opt_namespace)+key;};inputClass.unnamespace_=function(key,opt_namespace){return key.replace(inputClass.normalize_(opt_namespace),'');};inputClass.normalize_=function(opt_namespace){return inputClass.NAMESPACE+(opt_namespace?opt_namespace+'.':'');};inputClass.setAlternativeStorageInstance=function(instance){inputClass.storage_=instance;};inputClass.getAlternativeStorageInstance=function(){if(!tr.isHeadless&&inputClass.storage_===localStorage){return undefined;}
+return inputClass.storage_;};inputClass.NAMESPACE='trace-viewer';}
+function HeadlessStorage(){this.length=0;this.hasItem_={};this.items_={};this.itemsAsArray_=undefined;}
+HeadlessStorage.prototype={key(index){return this.itemsAsArray[index];},get itemsAsArray(){if(this.itemsAsArray_!==undefined){return this.itemsAsArray_;}
+const itemsAsArray=[];for(const k in this.items_){itemsAsArray.push(k);}
+this.itemsAsArray_=itemsAsArray;return this.itemsAsArray_;},getItem(key){if(!this.hasItem_[key]){return null;}
+return this.items_[key];},removeItem(key){if(!this.hasItem_[key]){return;}
+const value=this.items_[key];delete this.hasItem_[key];delete this.items_[key];this.length--;this.itemsAsArray_=undefined;return value;},setItem(key,value){if(this.hasItem_[key]){this.items_[key]=value;return;}
+this.items_[key]=value;this.hasItem_[key]=true;this.length++;this.itemsAsArray_=undefined;return value;}};if(tr.isHeadless){AddStaticStorageFunctionsToClass_(Settings,new HeadlessStorage());AddStaticStorageFunctionsToClass_(SessionSettings,new HeadlessStorage());}else{AddStaticStorageFunctionsToClass_(Settings,localStorage);AddStaticStorageFunctionsToClass_(SessionSettings,sessionStorage);}
+return{Settings,SessionSettings,};});'use strict';tr.exportTo('tr.ui.b',function(){function createSpan(opt_dictionary){let ownerDocument=document;if(opt_dictionary&&opt_dictionary.ownerDocument){ownerDocument=opt_dictionary.ownerDocument;}
+const spanEl=ownerDocument.createElement('span');if(opt_dictionary){if(opt_dictionary.className){spanEl.className=opt_dictionary.className;}
+if(opt_dictionary.textContent){Polymer.dom(spanEl).textContent=opt_dictionary.textContent;}
+if(opt_dictionary.tooltip){spanEl.title=opt_dictionary.tooltip;}
+if(opt_dictionary.parent){Polymer.dom(opt_dictionary.parent).appendChild(spanEl);}
+if(opt_dictionary.bold){spanEl.style.fontWeight='bold';}
+if(opt_dictionary.italic){spanEl.style.fontStyle='italic';}
+if(opt_dictionary.marginLeft){spanEl.style.marginLeft=opt_dictionary.marginLeft;}
+if(opt_dictionary.marginRight){spanEl.style.marginRight=opt_dictionary.marginRight;}
+if(opt_dictionary.backgroundColor){spanEl.style.backgroundColor=opt_dictionary.backgroundColor;}
+if(opt_dictionary.color){spanEl.style.color=opt_dictionary.color;}}
+return spanEl;}
+function createLink(opt_args){let ownerDocument=document;if(opt_args&&opt_args.ownerDocument){ownerDocument=opt_args.ownerDocument;}
+const linkEl=ownerDocument.createElement('a');if(opt_args){if(opt_args.href)linkEl.href=opt_args.href;if(opt_args.tooltip)linkEl.title=opt_args.tooltip;if(opt_args.color)linkEl.style.color=opt_args.color;if(opt_args.bold)linkEl.style.fontWeight='bold';if(opt_args.italic)linkEl.style.fontStyle='italic';if(opt_args.className)linkEl.className=opt_args.className;if(opt_args.parent)Polymer.dom(opt_args.parent).appendChild(linkEl);if(opt_args.marginLeft)linkEl.style.marginLeft=opt_args.marginLeft;if(opt_args.marginRight)linkEl.style.marginRight=opt_args.marginRight;if(opt_args.backgroundColor){linkEl.style.backgroundColor=opt_args.backgroundColor;}
+if(opt_args.textContent){Polymer.dom(linkEl).textContent=opt_args.textContent;}}
+return linkEl;}
+function createDiv(opt_dictionary){const divEl=document.createElement('div');if(opt_dictionary){if(opt_dictionary.className){divEl.className=opt_dictionary.className;}
+if(opt_dictionary.parent){Polymer.dom(opt_dictionary.parent).appendChild(divEl);}
+if(opt_dictionary.textContent){Polymer.dom(divEl).textContent=opt_dictionary.textContent;}
+if(opt_dictionary.maxWidth){divEl.style.maxWidth=opt_dictionary.maxWidth;}}
+return divEl;}
+function createScopedStyle(styleContent){const styleEl=document.createElement('style');styleEl.scoped=true;Polymer.dom(styleEl).innerHTML=styleContent;return styleEl;}
+function valuesEqual(a,b){if(a instanceof Array&&b instanceof Array){return a.length===b.length&&JSON.stringify(a)===JSON.stringify(b);}
+return a===b;}
+function createSelector(targetEl,targetElProperty,settingsKey,defaultValue,items,opt_namespace){let defaultValueIndex;for(let i=0;i<items.length;i++){const item=items[i];if(valuesEqual(item.value,defaultValue)){defaultValueIndex=i;break;}}
+if(defaultValueIndex===undefined){throw new Error('defaultValue must be in the items list');}
+const selectorEl=document.createElement('select');selectorEl.addEventListener('change',onChange);for(let i=0;i<items.length;i++){const item=items[i];const optionEl=document.createElement('option');Polymer.dom(optionEl).textContent=item.label;optionEl.targetPropertyValue=item.value;optionEl.item=item;Polymer.dom(selectorEl).appendChild(optionEl);}
+function onChange(e){const value=selectorEl.selectedValue;tr.b.Settings.set(settingsKey,value,opt_namespace);targetEl[targetElProperty]=value;}
+const oldSetter=targetEl.__lookupSetter__('selectedIndex');selectorEl.__defineGetter__('selectedValue',function(v){return selectorEl.children[selectorEl.selectedIndex].targetPropertyValue;});selectorEl.__defineGetter__('selectedItem',function(v){return selectorEl.children[selectorEl.selectedIndex].item;});selectorEl.__defineSetter__('selectedValue',function(v){for(let i=0;i<selectorEl.children.length;i++){const value=selectorEl.children[i].targetPropertyValue;if(valuesEqual(value,v)){const changed=selectorEl.selectedIndex!==i;if(changed){selectorEl.selectedIndex=i;onChange();}
+return;}}
+throw new Error('Not a valid value');});const initialValue=tr.b.Settings.get(settingsKey,defaultValue,opt_namespace);let didSet=false;for(let i=0;i<selectorEl.children.length;i++){if(valuesEqual(selectorEl.children[i].targetPropertyValue,initialValue)){didSet=true;targetEl[targetElProperty]=initialValue;selectorEl.selectedIndex=i;break;}}
+if(!didSet){selectorEl.selectedIndex=defaultValueIndex;targetEl[targetElProperty]=defaultValue;}
+return selectorEl;}
+function createEditCategorySpan(optionGroupEl,targetEl){const spanEl=createSpan({className:'edit-categories'});Polymer.dom(spanEl).textContent='Edit categories';Polymer.dom(spanEl).classList.add('labeled-option');spanEl.addEventListener('click',function(){targetEl.onClickEditCategories();});return spanEl;}
+function createOptionGroup(targetEl,targetElProperty,settingsKey,defaultValue,items){function onChange(){let value=[];if(this.value.length){value=this.value.split(',');}
+tr.b.Settings.set(settingsKey,value);targetEl[targetElProperty]=value;}
+const optionGroupEl=createSpan({className:'labeled-option-group'});const initialValue=tr.b.Settings.get(settingsKey,defaultValue);for(let i=0;i<items.length;++i){const item=items[i];const id='category-preset-'+item.label.replace(/ /g,'-');const radioEl=document.createElement('input');radioEl.type='radio';Polymer.dom(radioEl).setAttribute('id',id);Polymer.dom(radioEl).setAttribute('name','category-presets-group');Polymer.dom(radioEl).setAttribute('value',item.value);radioEl.addEventListener('change',onChange.bind(radioEl,targetEl,targetElProperty,settingsKey));if(valuesEqual(initialValue,item.value)){radioEl.checked=true;}
+const labelEl=document.createElement('label');Polymer.dom(labelEl).textContent=item.label;Polymer.dom(labelEl).setAttribute('for',id);const spanEl=createSpan({className:'labeled-option'});Polymer.dom(spanEl).appendChild(radioEl);Polymer.dom(spanEl).appendChild(labelEl);spanEl.__defineSetter__('checked',function(opt_bool){const changed=radioEl.checked!==(!!opt_bool);if(!changed)return;radioEl.checked=!!opt_bool;onChange();});spanEl.__defineGetter__('checked',function(){return radioEl.checked;});Polymer.dom(optionGroupEl).appendChild(spanEl);}
+Polymer.dom(optionGroupEl).appendChild(createEditCategorySpan(optionGroupEl,targetEl));if(!initialValue.length){Polymer.dom(optionGroupEl).classList.add('categories-expanded');}
+targetEl[targetElProperty]=initialValue;return optionGroupEl;}
+let nextCheckboxId=1;function createCheckBox(targetEl,targetElProperty,settingsKey,defaultValue,label,opt_changeCb){const buttonEl=document.createElement('input');buttonEl.type='checkbox';let initialValue=defaultValue;if(settingsKey!==undefined){initialValue=tr.b.Settings.get(settingsKey,defaultValue);buttonEl.checked=!!initialValue;}
+if(targetEl){targetEl[targetElProperty]=initialValue;}
+function onChange(){if(settingsKey!==undefined){tr.b.Settings.set(settingsKey,buttonEl.checked);}
+if(targetEl){targetEl[targetElProperty]=buttonEl.checked;}
+if(opt_changeCb){opt_changeCb.call();}}
+buttonEl.addEventListener('change',onChange);const id='#checkbox-'+nextCheckboxId++;const spanEl=createSpan();spanEl.style.display='flex';spanEl.style.whiteSpace='nowrap';Polymer.dom(buttonEl).setAttribute('id',id);const labelEl=document.createElement('label');Polymer.dom(labelEl).textContent=label;Polymer.dom(labelEl).setAttribute('for',id);Polymer.dom(spanEl).appendChild(buttonEl);Polymer.dom(spanEl).appendChild(labelEl);spanEl.__defineSetter__('checked',function(opt_bool){const changed=buttonEl.checked!==(!!opt_bool);if(!changed)return;buttonEl.checked=!!opt_bool;onChange();});spanEl.__defineGetter__('checked',function(){return buttonEl.checked;});return spanEl;}
+function createButton(label,opt_callback,opt_this){const buttonEl=document.createElement('input');buttonEl.type='button';buttonEl.value=label;function onClick(){opt_callback.call(opt_this||buttonEl);}
+if(opt_callback){buttonEl.addEventListener('click',onClick);}
+return buttonEl;}
+function createTextInput(targetEl,targetElProperty,settingsKey,defaultValue){const initialValue=tr.b.Settings.get(settingsKey,defaultValue);const el=document.createElement('input');el.type='text';function onChange(e){tr.b.Settings.set(settingsKey,el.value);targetEl[targetElProperty]=el.value;}
+el.addEventListener('input',onChange);el.value=initialValue;targetEl[targetElProperty]=initialValue;return el;}
+function isElementAttachedToDocument(el){let cur=el;while(Polymer.dom(cur).parentNode){cur=Polymer.dom(cur).parentNode;}
+return(cur===el.ownerDocument||cur.nodeName==='#document-fragment');}
+function asHTMLOrTextNode(value,opt_ownerDocument){if(value instanceof Node){return value;}
+const ownerDocument=opt_ownerDocument||document;return ownerDocument.createTextNode(value);}
+return{createSpan,createLink,createDiv,createScopedStyle,createSelector,createOptionGroup,createCheckBox,createButton,createTextInput,isElementAttachedToDocument,asHTMLOrTextNode,};});'use strict';tr.exportTo('tr.ui.b',function(){const elidedTitleCacheDict=new Map();const elidedTitleCache=new ElidedTitleCache();function ElidedTitleCache(){this.textWidthMap=new Map();}
+ElidedTitleCache.prototype={get(ctx,pixWidth,title,width,sliceDuration){let elidedDict=elidedTitleCacheDict.get(title);if(!elidedDict){elidedDict=new Map();elidedTitleCacheDict.set(title,elidedDict);}
+let elidedDictForPixWidth=elidedDict.get(pixWidth);if(!elidedDictForPixWidth){elidedDict.set(pixWidth,new Map());elidedDictForPixWidth=elidedDict.get(pixWidth);}
+let stringWidthPair=elidedDictForPixWidth.get(sliceDuration);if(stringWidthPair===undefined){let newtitle=title;let elided=false;while(this.labelWidthWorld(ctx,newtitle,pixWidth)>sliceDuration){if(newtitle.length*0.75<1)break;newtitle=newtitle.substring(0,newtitle.length*0.75);elided=true;}
+if(elided&&newtitle.length>3){newtitle=newtitle.substring(0,newtitle.length-3)+'...';}
+stringWidthPair=new ElidedStringWidthPair(newtitle,this.labelWidth(ctx,newtitle));elidedDictForPixWidth.set(sliceDuration,stringWidthPair);}
+return stringWidthPair;},quickMeasureText_(ctx,text){let w=this.textWidthMap.get(text);if(!w){w=ctx.measureText(text).width;this.textWidthMap.set(text,w);}
+return w;},labelWidth(ctx,title){return this.quickMeasureText_(ctx,title)+2;},labelWidthWorld(ctx,title,pixWidth){return this.labelWidth(ctx,title)*pixWidth;}};function ElidedStringWidthPair(string,width){this.string=string;this.width=width;}
+return{ElidedTitleCache,};});'use strict';tr.exportTo('tr.ui.b',function(){const ColorScheme=tr.b.ColorScheme;const colors=ColorScheme.colors;const colorsAsStrings=ColorScheme.colorsAsStrings;const SelectionState=tr.model.SelectionState;const EventPresenter={getSelectableItemColorAsString(item){const offset=this.getColorIdOffset_(item);const colorId=ColorScheme.getVariantColorId(item.colorId,offset);return colorsAsStrings[colorId];},getColorIdOffset_(event){return event.selectionState;},getTextColor(event){if(event.selectionState===SelectionState.DIMMED){return'rgb(60,60,60)';}
+return'rgb(0,0,0)';},getSliceColorId(slice){const offset=this.getColorIdOffset_(slice);return ColorScheme.getVariantColorId(slice.colorId,offset);},getSliceAlpha(slice,async){let alpha=1;if(async){alpha*=0.3;}
+return alpha;},getInstantSliceColor(instant){const offset=this.getColorIdOffset_(instant);const colorId=ColorScheme.getVariantColorId(instant.colorId,offset);return colors[colorId].toStringWithAlphaOverride(1.0);},getObjectInstanceColor(instance){const offset=this.getColorIdOffset_(instance);const colorId=ColorScheme.getVariantColorId(instance.colorId,offset);return colors[colorId].toStringWithAlphaOverride(0.25);},getObjectSnapshotColor(snapshot){const offset=this.getColorIdOffset_(snapshot);let colorId=snapshot.objectInstance.colorId;colorId=ColorScheme.getVariantColorId(colorId,offset);return colors[colorId];},getCounterSeriesColor(colorId,selectionState,opt_alphaMultiplier){const event={selectionState};const offset=this.getColorIdOffset_(event);const c=colors[ColorScheme.getVariantColorId(colorId,offset)];return c.toStringWithAlphaOverride(opt_alphaMultiplier!==undefined?opt_alphaMultiplier:1.0);},getBarSnapshotColor(snapshot,offset){const snapshotOffset=this.getColorIdOffset_(snapshot);let colorId=snapshot.objectInstance.colorId;colorId=ColorScheme.getAnotherColorId(colorId,offset);colorId=ColorScheme.getVariantColorId(colorId,snapshotOffset);return colors[colorId].toStringWithAlphaOverride(1.0);}};return{EventPresenter,};});'use strict';tr.exportTo('tr.ui.b',function(){const elidedTitleCache=new tr.ui.b.ElidedTitleCache();const ColorScheme=tr.b.ColorScheme;const colorsAsStrings=ColorScheme.colorsAsStrings;const EventPresenter=tr.ui.b.EventPresenter;const blackColorId=ColorScheme.getColorIdForReservedName('black');const THIN_SLICE_HEIGHT=4;const SLICE_WAITING_WIDTH_DRAW_THRESHOLD=3;const SLICE_ACTIVE_WIDTH_DRAW_THRESHOLD=1;const SHOULD_ELIDE_TEXT=true;function drawLine(ctx,x1,y1,x2,y2){ctx.moveTo(x1,y1);ctx.lineTo(x2,y2);}
+function drawTriangle(ctx,x1,y1,x2,y2,x3,y3){ctx.beginPath();ctx.moveTo(x1,y1);ctx.lineTo(x2,y2);ctx.lineTo(x3,y3);ctx.closePath();}
+function drawArrow(ctx,x1,y1,x2,y2,arrowLength,arrowWidth){const dx=x2-x1;const dy=y2-y1;const len=Math.sqrt(dx*dx+dy*dy);const perc=(len-arrowLength)/len;const bx=x1+perc*dx;const by=y1+perc*dy;const ux=dx/len;const uy=dy/len;const ax=uy*arrowWidth;const ay=-ux*arrowWidth;ctx.beginPath();drawLine(ctx,x1,y1,x2,y2);ctx.stroke();drawTriangle(ctx,bx+ax,by+ay,x2,y2,bx-ax,by-ay);ctx.fill();}
+function drawSlices(ctx,dt,viewLWorld,viewRWorld,viewHeight,slices,async){const pixelRatio=window.devicePixelRatio||1;const height=viewHeight*pixelRatio;const viewL=dt.xWorldToView(viewLWorld);const viewR=dt.xWorldToView(viewRWorld);let darkRectHeight=THIN_SLICE_HEIGHT*pixelRatio;if(height<darkRectHeight){darkRectHeight=0;}
+const lightRectHeight=height-darkRectHeight;ctx.save();const rect=new tr.ui.b.FastRectRenderer(ctx,viewL,viewR,2,2,colorsAsStrings);rect.setYandH(0,height);const lowSlice=tr.b.findLowIndexInSortedArray(slices,function(slice){return slice.start+slice.duration;},viewLWorld);let hadTopLevel=false;for(let i=lowSlice;i<slices.length;++i){const slice=slices[i];const x=slice.start;if(x>viewRWorld)break;const xView=dt.xWorldToView(x);let wView=1;if(slice.duration>0){const w=Math.max(slice.duration,0.000001);wView=Math.max(dt.xWorldVectorToView(w),1);}
+const colorId=EventPresenter.getSliceColorId(slice);const alpha=EventPresenter.getSliceAlpha(slice,async);const lightAlpha=alpha*0.70;if(async&&slice.isTopLevel){rect.setYandH(3,height-3);hadTopLevel=true;}else{rect.setYandH(0,height);}
+if(!slice.cpuDuration){rect.fillRect(xView,wView,colorId,alpha);continue;}
+let activeWidth=wView*(slice.cpuDuration/slice.duration);let waitingWidth=wView-activeWidth;if(activeWidth<SLICE_ACTIVE_WIDTH_DRAW_THRESHOLD){activeWidth=0;waitingWidth=wView;}
+if(waitingWidth<SLICE_WAITING_WIDTH_DRAW_THRESHOLD){activeWidth=wView;waitingWidth=0;}
+if(activeWidth>0){rect.fillRect(xView,activeWidth,colorId,alpha);}
+if(waitingWidth>0){rect.setYandH(0,lightRectHeight);rect.fillRect(xView+activeWidth-1,waitingWidth+1,colorId,lightAlpha);rect.setYandH(lightRectHeight,darkRectHeight);rect.fillRect(xView+activeWidth-1,waitingWidth+1,colorId,alpha);rect.setYandH(0,height);}}
+rect.flush();if(async&&hadTopLevel){rect.setYandH(2,1);for(let i=lowSlice;i<slices.length;++i){const slice=slices[i];const x=slice.start;if(x>viewRWorld)break;if(!slice.isTopLevel)continue;const xView=dt.xWorldToView(x);let wView=1;if(slice.duration>0){const w=Math.max(slice.duration,0.000001);wView=Math.max(dt.xWorldVectorToView(w),1);}
+rect.fillRect(xView,wView,blackColorId,0.7);}
+rect.flush();}
+ctx.restore();}
+function drawInstantSlicesAsLines(ctx,dt,viewLWorld,viewRWorld,viewHeight,slices,lineWidthInPixels){const pixelRatio=window.devicePixelRatio||1;const height=viewHeight*pixelRatio;ctx.save();ctx.lineWidth=lineWidthInPixels*pixelRatio;const lowSlice=tr.b.findLowIndexInSortedArray(slices,function(slice){return slice.start;},viewLWorld);for(let i=lowSlice;i<slices.length;++i){const slice=slices[i];const x=slice.start;if(x>viewRWorld)break;ctx.strokeStyle=EventPresenter.getInstantSliceColor(slice);const xView=dt.xWorldToView(x);ctx.beginPath();ctx.moveTo(xView,0);ctx.lineTo(xView,height);ctx.stroke();}
+ctx.restore();}
+function drawLabels(ctx,dt,viewLWorld,viewRWorld,slices,async,fontSize,yOffset){const pixelRatio=window.devicePixelRatio||1;const pixWidth=dt.xViewVectorToWorld(1);ctx.save();ctx.textAlign='center';ctx.textBaseline='top';ctx.font=(fontSize*pixelRatio)+'px sans-serif';if(async){ctx.font='italic '+ctx.font;}
+const cY=yOffset*pixelRatio;const lowSlice=tr.b.findLowIndexInSortedArray(slices,function(slice){return slice.start+slice.duration;},viewLWorld);const quickDiscardThreshold=pixWidth*20;for(let i=lowSlice;i<slices.length;++i){const slice=slices[i];if(slice.start>viewRWorld)break;if(slice.duration<=quickDiscardThreshold)continue;const xLeftClipped=Math.max(slice.start,viewLWorld);const xRightClipped=Math.min(slice.start+slice.duration,viewRWorld);const visibleWidth=xRightClipped-xLeftClipped;const title=slice.title+
+(slice.didNotFinish?' (Did Not Finish)':'');let drawnTitle=title;let drawnWidth=elidedTitleCache.labelWidth(ctx,drawnTitle);const fullLabelWidth=elidedTitleCache.labelWidthWorld(ctx,drawnTitle,pixWidth);if(SHOULD_ELIDE_TEXT&&fullLabelWidth>visibleWidth){const elidedValues=elidedTitleCache.get(ctx,pixWidth,drawnTitle,drawnWidth,visibleWidth);drawnTitle=elidedValues.string;drawnWidth=elidedValues.width;}
+if(drawnWidth*pixWidth<visibleWidth){ctx.fillStyle=EventPresenter.getTextColor(slice);const cX=dt.xWorldToView((xLeftClipped+xRightClipped)/2);ctx.fillText(drawnTitle,cX,cY,drawnWidth);}}
+ctx.restore();}
+return{drawSlices,drawInstantSlicesAsLines,drawLabels,drawLine,drawTriangle,drawArrow,elidedTitleCache_:elidedTitleCache,THIN_SLICE_HEIGHT,};});'use strict';tr.exportTo('tr.ui',function(){function TimelineDisplayTransform(opt_that){if(opt_that){this.set(opt_that);return;}
+this.scaleX=1;this.panX=0;this.panY=0;}
+TimelineDisplayTransform.prototype={set(that){this.scaleX=that.scaleX;this.panX=that.panX;this.panY=that.panY;},clone(){return new TimelineDisplayTransform(this);},equals(that){let eq=true;if(that===undefined||that===null){return false;}
+eq&=this.panX===that.panX;eq&=this.panY===that.panY;eq&=this.scaleX===that.scaleX;return!!eq;},almostEquals(that){let eq=true;if(that===undefined||that===null){return false;}
+eq&=Math.abs(this.panX-that.panX)<0.001;eq&=Math.abs(this.panY-that.panY)<0.001;eq&=Math.abs(this.scaleX-that.scaleX)<0.001;return!!eq;},incrementPanXInViewUnits(xDeltaView){this.panX+=this.xViewVectorToWorld(xDeltaView);},xPanWorldPosToViewPos(worldX,viewX,viewWidth){if(typeof viewX==='string'){if(viewX==='left'){viewX=0;}else if(viewX==='center'){viewX=viewWidth/2;}else if(viewX==='right'){viewX=viewWidth-1;}else{throw new Error('viewX must be left|center|right or number.');}}
+this.panX=(viewX/this.scaleX)-worldX;},xPanWorldBoundsIntoView(worldMin,worldMax,viewWidth){if(this.xWorldToView(worldMin)<0){this.xPanWorldPosToViewPos(worldMin,'left',viewWidth);}else if(this.xWorldToView(worldMax)>viewWidth){this.xPanWorldPosToViewPos(worldMax,'right',viewWidth);}},xSetWorldBounds(worldMin,worldMax,viewWidth){const worldWidth=worldMax-worldMin;const scaleX=viewWidth/worldWidth;const panX=-worldMin;this.setPanAndScale(panX,scaleX);},setPanAndScale(p,s){this.scaleX=s;this.panX=p;},xWorldToView(x){return(x+this.panX)*this.scaleX;},xWorldVectorToView(x){return x*this.scaleX;},xViewToWorld(x){return(x/this.scaleX)-this.panX;},xViewVectorToWorld(x){return x/this.scaleX;}};return{TimelineDisplayTransform,};});'use strict';tr.exportTo('tr.ui',function(){function SnapIndicator(y,height){this.y=y;this.height=height;}
+function TimelineInterestRange(vp){this.viewport_=vp;this.range_=new tr.b.math.Range();this.leftSelected_=false;this.rightSelected_=false;this.leftSnapIndicator_=undefined;this.rightSnapIndicator_=undefined;}
+TimelineInterestRange.prototype={get isEmpty(){return this.range_.isEmpty;},reset(){this.range_.reset();this.leftSelected_=false;this.rightSelected_=false;this.leftSnapIndicator_=undefined;this.rightSnapIndicator_=undefined;this.viewport_.dispatchChangeEvent();},get min(){return this.range_.min;},set min(min){this.range_.min=min;this.viewport_.dispatchChangeEvent();},get max(){return this.range_.max;},set max(max){this.range_.max=max;this.viewport_.dispatchChangeEvent();},set(range){this.range_.reset();this.range_.addRange(range);this.viewport_.dispatchChangeEvent();},setMinAndMax(min,max){this.range_.min=min;this.range_.max=max;this.viewport_.dispatchChangeEvent();},get range(){return this.range_.range;},asRangeObject(){const range=new tr.b.math.Range();range.addRange(this.range_);return range;},get leftSelected(){return this.leftSelected_;},set leftSelected(leftSelected){if(this.leftSelected_===leftSelected)return;this.leftSelected_=leftSelected;this.viewport_.dispatchChangeEvent();},get rightSelected(){return this.rightSelected_;},set rightSelected(rightSelected){if(this.rightSelected_===rightSelected)return;this.rightSelected_=rightSelected;this.viewport_.dispatchChangeEvent();},get leftSnapIndicator(){return this.leftSnapIndicator_;},set leftSnapIndicator(leftSnapIndicator){this.leftSnapIndicator_=leftSnapIndicator;this.viewport_.dispatchChangeEvent();},get rightSnapIndicator(){return this.rightSnapIndicator_;},set rightSnapIndicator(rightSnapIndicator){this.rightSnapIndicator_=rightSnapIndicator;this.viewport_.dispatchChangeEvent();},draw(ctx,viewLWorld,viewRWorld,viewHeight){if(this.range_.isEmpty)return;const dt=this.viewport_.currentDisplayTransform;const markerLWorld=this.min;const markerRWorld=this.max;const markerLView=Math.round(dt.xWorldToView(markerLWorld));const markerRView=Math.round(dt.xWorldToView(markerRWorld));ctx.fillStyle='rgba(0, 0, 0, 0.2)';if(markerLWorld>viewLWorld){ctx.fillRect(dt.xWorldToView(viewLWorld),0,markerLView,viewHeight);}
+if(markerRWorld<viewRWorld){ctx.fillRect(markerRView,0,dt.xWorldToView(viewRWorld),viewHeight);}
+const pixelRatio=window.devicePixelRatio||1;ctx.lineWidth=Math.round(pixelRatio);if(this.range_.range>0){this.drawLine_(ctx,viewLWorld,viewRWorld,viewHeight,this.min,this.leftSelected_);this.drawLine_(ctx,viewLWorld,viewRWorld,viewHeight,this.max,this.rightSelected_);}else{this.drawLine_(ctx,viewLWorld,viewRWorld,viewHeight,this.min,this.leftSelected_||this.rightSelected_);}
+ctx.lineWidth=1;},drawLine_(ctx,viewLWorld,viewRWorld,height,ts,selected){if(ts<viewLWorld||ts>=viewRWorld)return;const dt=this.viewport_.currentDisplayTransform;const viewX=Math.round(dt.xWorldToView(ts));ctx.save();ctx.translate((Math.round(ctx.lineWidth)%2)/2,0);ctx.beginPath();tr.ui.b.drawLine(ctx,viewX,0,viewX,height);if(selected){ctx.strokeStyle='rgb(255, 0, 0)';}else{ctx.strokeStyle='rgb(0, 0, 0)';}
+ctx.stroke();ctx.restore();},drawIndicators(ctx,viewLWorld,viewRWorld){if(this.leftSnapIndicator_){this.drawIndicator_(ctx,viewLWorld,viewRWorld,this.range_.min,this.leftSnapIndicator_,this.leftSelected_);}
+if(this.rightSnapIndicator_){this.drawIndicator_(ctx,viewLWorld,viewRWorld,this.range_.max,this.rightSnapIndicator_,this.rightSelected_);}},drawIndicator_(ctx,viewLWorld,viewRWorld,xWorld,si,selected){const dt=this.viewport_.currentDisplayTransform;const viewX=Math.round(dt.xWorldToView(xWorld));ctx.save();ctx.translate((Math.round(ctx.lineWidth)%2)/2,0);const pixelRatio=window.devicePixelRatio||1;const viewY=si.y*devicePixelRatio;const viewHeight=si.height*devicePixelRatio;const arrowSize=4*pixelRatio;if(selected){ctx.fillStyle='rgb(255, 0, 0)';}else{ctx.fillStyle='rgb(0, 0, 0)';}
+tr.ui.b.drawTriangle(ctx,viewX-arrowSize*0.75,viewY,viewX+arrowSize*0.75,viewY,viewX,viewY+arrowSize);ctx.fill();tr.ui.b.drawTriangle(ctx,viewX-arrowSize*0.75,viewY+viewHeight,viewX+arrowSize*0.75,viewY+viewHeight,viewX,viewY+viewHeight-arrowSize);ctx.fill();ctx.restore();}};return{SnapIndicator,TimelineInterestRange,};});'use strict';tr.exportTo('tr.ui.tracks',function(){function ContainerToTrackMap(){this.stableIdToTrackMap_={};}
+ContainerToTrackMap.prototype={addContainer(container,track){if(!track){throw new Error('Must provide a track.');}
+this.stableIdToTrackMap_[container.stableId]=track;},clear(){this.stableIdToTrackMap_={};},getTrackByStableId(stableId){return this.stableIdToTrackMap_[stableId];}};return{ContainerToTrackMap,};});'use strict';tr.exportTo('tr.ui.tracks',function(){function EventToTrackMap(){}
+EventToTrackMap.prototype={addEvent(event,track){if(!track){throw new Error('Must provide a track.');}
+this[event.guid]=track;}};return{EventToTrackMap,};});'use strict';tr.exportTo('tr.ui',function(){const TimelineDisplayTransform=tr.ui.TimelineDisplayTransform;const TimelineInterestRange=tr.ui.TimelineInterestRange;const IDEAL_MAJOR_MARK_DISTANCE_PX=150;const MAJOR_MARK_ROUNDING_FACTOR=100000;class AnimationControllerProxy{constructor(target){this.target_=target;}
+get panX(){return this.target_.currentDisplayTransform_.panX;}
+set panX(panX){this.target_.currentDisplayTransform_.panX=panX;}
+get panY(){return this.target_.currentDisplayTransform_.panY;}
+set panY(panY){this.target_.currentDisplayTransform_.panY=panY;}
+get scaleX(){return this.target_.currentDisplayTransform_.scaleX;}
+set scaleX(scaleX){this.target_.currentDisplayTransform_.scaleX=scaleX;}
+cloneAnimationState(){return this.target_.currentDisplayTransform_.clone();}
+xPanWorldPosToViewPos(xWorld,xView){this.target_.currentDisplayTransform_.xPanWorldPosToViewPos(xWorld,xView,this.target_.modelTrackContainer_.canvas.clientWidth);}}
+function TimelineViewport(parentEl){this.parentEl_=parentEl;this.modelTrackContainer_=undefined;this.currentDisplayTransform_=new TimelineDisplayTransform();this.initAnimationController_();this.selectedFlowEvents_=new Set();this.highlightVSync_=false;this.highDetails_=false;this.gridTimebase_=0;this.gridStep_=1000/60;this.gridEnabled_=false;this.hasCalledSetupFunction_=false;this.onResize_=this.onResize_.bind(this);this.onModelTrackControllerScroll_=this.onModelTrackControllerScroll_.bind(this);this.timeMode_=TimelineViewport.TimeMode.TIME_IN_MS;this.majorMarkWorldPositions_=[];this.majorMarkUnit_=undefined;this.interestRange_=new TimelineInterestRange(this);this.eventToTrackMap_=new tr.ui.tracks.EventToTrackMap();this.containerToTrackMap=new tr.ui.tracks.ContainerToTrackMap();this.dispatchChangeEvent=this.dispatchChangeEvent.bind(this);}
+TimelineViewport.TimeMode={TIME_IN_MS:0,REVISIONS:1};TimelineViewport.prototype={__proto__:tr.b.EventTarget.prototype,get isAttachedToDocumentOrInTestMode(){if(this.parentEl_===undefined)return;return tr.ui.b.isElementAttachedToDocument(this.parentEl_);},onResize_(){this.dispatchChangeEvent();},dispatchChangeEvent(){tr.b.dispatchSimpleEvent(this,'change');},detach(){window.removeEventListener('resize',this.dispatchChangeEvent);},initAnimationController_(){this.dtAnimationController_=new tr.ui.b.AnimationController();this.dtAnimationController_.addEventListener('didtick',function(e){this.onCurentDisplayTransformChange_(e.oldTargetState);}.bind(this));this.dtAnimationController_.target=new AnimationControllerProxy(this);},get currentDisplayTransform(){return this.currentDisplayTransform_;},setDisplayTransformImmediately(displayTransform){this.dtAnimationController_.cancelActiveAnimation();const oldDisplayTransform=this.dtAnimationController_.target.cloneAnimationState();this.currentDisplayTransform_.set(displayTransform);this.onCurentDisplayTransformChange_(oldDisplayTransform);},queueDisplayTransformAnimation(animation){if(!(animation instanceof tr.ui.b.Animation)){throw new Error('animation must be instanceof tr.ui.b.Animation');}
+this.dtAnimationController_.queueAnimation(animation);},onCurentDisplayTransformChange_(oldDisplayTransform){if(this.modelTrackContainer_){this.currentDisplayTransform.panY=tr.b.math.clamp(this.currentDisplayTransform.panY,0,this.modelTrackContainer_.scrollHeight-
+this.modelTrackContainer_.clientHeight);}
+const changed=!this.currentDisplayTransform.equals(oldDisplayTransform);const yChanged=this.currentDisplayTransform.panY!==oldDisplayTransform.panY;if(yChanged){this.modelTrackContainer_.scrollTop=this.currentDisplayTransform.panY;}
+if(changed){this.dispatchChangeEvent();}},onModelTrackControllerScroll_(e){if(this.dtAnimationController_.activeAnimation&&this.dtAnimationController_.activeAnimation.affectsPanY){this.dtAnimationController_.cancelActiveAnimation();}
+const panY=this.modelTrackContainer_.scrollTop;this.currentDisplayTransform_.panY=panY;},get modelTrackContainer(){return this.modelTrackContainer_;},set modelTrackContainer(m){if(this.modelTrackContainer_){this.modelTrackContainer_.removeEventListener('scroll',this.onModelTrackControllerScroll_);}
+this.modelTrackContainer_=m;this.modelTrackContainer_.addEventListener('scroll',this.onModelTrackControllerScroll_);},get selectedFlowEvents(){return this.selectedFlowEvents_;},set selectedFlowEvents(selectedFlowEvents){this.selectedFlowEvents_=selectedFlowEvents;this.dispatchChangeEvent();},get highlightVSync(){return this.highlightVSync_;},set highlightVSync(highlightVSync){this.highlightVSync_=highlightVSync;this.dispatchChangeEvent();},get highDetails(){return this.highDetails_;},set highDetails(highDetails){this.highDetails_=highDetails;this.dispatchChangeEvent();},get gridEnabled(){return this.gridEnabled_;},set gridEnabled(enabled){if(this.gridEnabled_===enabled)return;this.gridEnabled_=enabled&&true;this.dispatchChangeEvent();},get gridTimebase(){return this.gridTimebase_;},set gridTimebase(timebase){if(this.gridTimebase_===timebase)return;this.gridTimebase_=timebase;this.dispatchChangeEvent();},get gridStep(){return this.gridStep_;},get interestRange(){return this.interestRange_;},get majorMarkWorldPositions(){return this.majorMarkWorldPositions_;},get majorMarkUnit(){switch(this.timeMode_){case TimelineViewport.TimeMode.TIME_IN_MS:return tr.b.Unit.byName.timeInMsAutoFormat;case TimelineViewport.TimeMode.REVISIONS:return tr.b.Unit.byName.count;default:throw new Error('Cannot get Unit for unsupported time mode '+this.timeMode_);}},get timeMode(){return this.timeMode_;},set timeMode(mode){this.timeMode_=mode;this.dispatchChangeEvent();},updateMajorMarkData(viewLWorld,viewRWorld){const pixelRatio=window.devicePixelRatio||1;const dt=this.currentDisplayTransform;const idealMajorMarkDistancePix=IDEAL_MAJOR_MARK_DISTANCE_PX*pixelRatio;const idealMajorMarkDistanceWorld=dt.xViewVectorToWorld(idealMajorMarkDistancePix);const majorMarkDistanceWorld=tr.b.math.preferredNumberLargerThanMin(idealMajorMarkDistanceWorld);const firstMajorMark=Math.floor(viewLWorld/majorMarkDistanceWorld)*majorMarkDistanceWorld;this.majorMarkWorldPositions_=[];if(firstMajorMark/majorMarkDistanceWorld>1e15)return;for(let curX=firstMajorMark;curX<viewRWorld;curX+=majorMarkDistanceWorld){this.majorMarkWorldPositions_.push(Math.floor(MAJOR_MARK_ROUNDING_FACTOR*curX)/MAJOR_MARK_ROUNDING_FACTOR);}},drawMajorMarkLines(ctx,viewHeight){ctx.save();ctx.translate((Math.round(ctx.lineWidth)%2)/2,0);ctx.beginPath();for(const majorMark of this.majorMarkWorldPositions_){const x=this.currentDisplayTransform.xWorldToView(majorMark);tr.ui.b.drawLine(ctx,x,0,x,viewHeight);}
+ctx.strokeStyle='#ddd';ctx.stroke();ctx.restore();},drawGridLines(ctx,viewLWorld,viewRWorld,viewHeight){if(!this.gridEnabled)return;const dt=this.currentDisplayTransform;let x=this.gridTimebase;ctx.save();ctx.translate((Math.round(ctx.lineWidth)%2)/2,0);ctx.beginPath();while(x<viewRWorld){if(x>=viewLWorld){const vx=Math.floor(dt.xWorldToView(x));tr.ui.b.drawLine(ctx,vx,0,vx,viewHeight);}
+x+=this.gridStep;}
+ctx.strokeStyle='rgba(255, 0, 0, 0.25)';ctx.stroke();ctx.restore();},getShiftedSelection(selection,offset){const newSelection=new tr.model.EventSet();for(const event of selection){if(event instanceof tr.model.FlowEvent){if(offset>0){newSelection.push(event.endSlice);}else if(offset<0){newSelection.push(event.startSlice);}else{}
+continue;}
+const track=this.trackForEvent(event);track.addEventNearToProvidedEventToSelection(event,offset,newSelection);}
+if(newSelection.length===0)return undefined;return newSelection;},rebuildEventToTrackMap(){this.eventToTrackMap_=new tr.ui.tracks.EventToTrackMap();this.modelTrackContainer_.addEventsToTrackMap(this.eventToTrackMap_);},rebuildContainerToTrackMap(){this.containerToTrackMap.clear();this.modelTrackContainer_.addContainersToTrackMap(this.containerToTrackMap);},trackForEvent(event){return this.eventToTrackMap_[event.guid];}};return{TimelineViewport,};});'use strict';tr.exportTo('tr.c',function(){const BrushingState=tr.ui.b.BrushingState;const EventSet=tr.model.EventSet;const SelectionState=tr.model.SelectionState;const Viewport=tr.ui.TimelineViewport;function BrushingStateController(timelineView){tr.b.EventTarget.call(this);this.timelineView_=timelineView;this.currentBrushingState_=new BrushingState();this.onPopState_=this.onPopState_.bind(this);this.historyEnabled_=false;this.selections_={};}
+BrushingStateController.prototype={__proto__:tr.b.EventTarget.prototype,dispatchChangeEvent_(){const e=new tr.b.Event('change',false,false);this.dispatchEvent(e);},get model(){if(!this.timelineView_){return undefined;}
+return this.timelineView_.model;},get trackView(){if(!this.timelineView_){return undefined;}
+return this.timelineView_.trackView;},get viewport(){if(!this.timelineView_){return undefined;}
+if(!this.timelineView_.trackView){return undefined;}
+return this.timelineView_.trackView.viewport;},get historyEnabled(){return this.historyEnabled_;},set historyEnabled(historyEnabled){this.historyEnabled_=!!historyEnabled;if(historyEnabled){window.addEventListener('popstate',this.onPopState_);}else{window.removeEventListener('popstate',this.onPopState_);}},modelWillChange(){if(this.currentBrushingState_.isAppliedToModel){this.currentBrushingState_.unapplyFromEventSelectionStates();}},modelDidChange(){this.selections_={};this.currentBrushingState_=new BrushingState();this.currentBrushingState_.applyToEventSelectionStates(this.model);const e=new tr.b.Event('model-changed',false,false);this.dispatchEvent(e);this.dispatchChangeEvent_();},onUserInitiatedSelectionChange_(){const selection=this.selection;if(this.historyEnabled){this.selections_[selection.guid]=selection;const state={selection_guid:selection.guid};window.history.pushState(state,document.title);}},onPopState_(e){if(e.state===null)return;const selection=this.selections_[e.state.selection_guid];if(selection){const newState=this.currentBrushingState_.clone();newState.selection=selection;this.currentBrushingState=newState;}
+e.stopPropagation();},get selection(){return this.currentBrushingState_.selection;},get findMatches(){return this.currentBrushingState_.findMatches;},get selectionOfInterest(){return this.currentBrushingState_.selectionOfInterest;},get currentBrushingState(){return this.currentBrushingState_;},set currentBrushingState(newBrushingState){if(newBrushingState.isAppliedToModel){throw new Error('Cannot apply this state, it is applied');}
+const hasValueChanged=!this.currentBrushingState_.equals(newBrushingState);if(newBrushingState!==this.currentBrushingState_&&!hasValueChanged){if(this.currentBrushingState_.isAppliedToModel){this.currentBrushingState_.transferModelOwnershipToClone(newBrushingState);}
+this.currentBrushingState_=newBrushingState;return;}
+if(this.currentBrushingState_.isAppliedToModel){this.currentBrushingState_.unapplyFromEventSelectionStates();}
+this.currentBrushingState_=newBrushingState;this.currentBrushingState_.applyToEventSelectionStates(this.model);this.dispatchChangeEvent_();},addAllEventsMatchingFilterToSelectionAsTask(filter,selection){const timelineView=this.timelineView_.trackView;if(!timelineView){return new tr.b.Task();}
+return timelineView.addAllEventsMatchingFilterToSelectionAsTask(filter,selection);},findTextChangedTo(allPossibleMatches){const newBrushingState=this.currentBrushingState_.clone();newBrushingState.findMatches=allPossibleMatches;this.currentBrushingState=newBrushingState;},findFocusChangedTo(currentFocus){const newBrushingState=this.currentBrushingState_.clone();newBrushingState.selection=currentFocus;this.currentBrushingState=newBrushingState;this.onUserInitiatedSelectionChange_();},findTextCleared(){if(this.xNavStringMarker_!==undefined){this.model.removeAnnotation(this.xNavStringMarker_);this.xNavStringMarker_=undefined;}
+if(this.guideLineAnnotation_!==undefined){this.model.removeAnnotation(this.guideLineAnnotation_);this.guideLineAnnotation_=undefined;}
+const newBrushingState=this.currentBrushingState_.clone();newBrushingState.selection=new EventSet();newBrushingState.findMatches=new EventSet();this.currentBrushingState=newBrushingState;this.onUserInitiatedSelectionChange_();},uiStateFromString(string){return tr.ui.b.UIState.fromUserFriendlyString(this.model,this.viewport,string);},navToPosition(uiState,showNavLine){this.trackView.navToPosition(uiState,showNavLine);},changeSelectionFromTimeline(selection){const newBrushingState=this.currentBrushingState_.clone();newBrushingState.selection=selection;newBrushingState.findMatches=new EventSet();this.currentBrushingState=newBrushingState;this.onUserInitiatedSelectionChange_();},showScriptControlSelection(selection){const newBrushingState=this.currentBrushingState_.clone();newBrushingState.selection=selection;newBrushingState.findMatches=new EventSet();this.currentBrushingState=newBrushingState;},changeSelectionFromRequestSelectionChangeEvent(selection){const newBrushingState=this.currentBrushingState_.clone();newBrushingState.selection=selection;newBrushingState.findMatches=new EventSet();this.currentBrushingState=newBrushingState;this.onUserInitiatedSelectionChange_();},changeAnalysisViewRelatedEvents(eventSet){const newBrushingState=this.currentBrushingState_.clone();newBrushingState.analysisViewRelatedEvents=eventSet;this.currentBrushingState=newBrushingState;},changeAnalysisLinkHoveredEvents(eventSet){const newBrushingState=this.currentBrushingState_.clone();newBrushingState.analysisLinkHoveredEvents=eventSet;this.currentBrushingState=newBrushingState;},getViewSpecificBrushingState(viewId){return this.currentBrushingState.viewSpecificBrushingStates[viewId];},changeViewSpecificBrushingState(viewId,newState){const oldStates=this.currentBrushingState_.viewSpecificBrushingStates;const newStates={};for(const id in oldStates){newStates[id]=oldStates[id];}
+if(newState===undefined){delete newStates[viewId];}else{newStates[viewId]=newState;}
+const newBrushingState=this.currentBrushingState_.clone();newBrushingState.viewSpecificBrushingStates=newStates;this.currentBrushingState=newBrushingState;}};BrushingStateController.getControllerForElement=function(element){if(tr.isHeadless){throw new Error('Unsupported');}
+let currentElement=element;while(currentElement){if(currentElement.brushingStateController){return currentElement.brushingStateController;}
+if(currentElement.parentElement){currentElement=currentElement.parentElement;continue;}
+let currentNode=currentElement;while(Polymer.dom(currentNode).parentNode){currentNode=Polymer.dom(currentNode).parentNode;}
+currentElement=currentNode.host;}
+return undefined;};return{BrushingStateController,};});'use strict';Polymer({is:'tr-ui-a-analysis-link',properties:{href:{type:String}},listeners:{'click':'onClicked_','mouseenter':'onMouseEnter_','mouseleave':'onMouseLeave_'},ready(){this.selection_=undefined;},attached(){this.controller_=tr.c.BrushingStateController.getControllerForElement(this);},detached(){this.clearHighlight_();this.controller_=undefined;},set color(c){this.style.color=c;},get selection(){return this.selection_;},set selection(selection){this.selection_=selection;Polymer.dom(this).textContent=selection.userFriendlyName;},setSelectionAndContent(selection,opt_textContent){this.selection_=selection;if(opt_textContent){Polymer.dom(this).textContent=opt_textContent;}},getCurrentSelection_(){if(typeof this.selection_==='function'){return this.selection_();}
+return this.selection_;},setHighlight_(opt_eventSet){if(this.controller_){this.controller_.changeAnalysisLinkHoveredEvents(opt_eventSet);}},clearHighlight_(opt_eventSet){this.setHighlight_();},onClicked_(clickEvent){if(!this.selection_)return;clickEvent.stopPropagation();const event=new tr.model.RequestSelectionChangeEvent();event.selection=this.getCurrentSelection_();this.dispatchEvent(event);},onMouseEnter_(){this.setHighlight_(this.getCurrentSelection_());},onMouseLeave_(){this.clearHighlight_();}});'use strict';tr.exportTo('tr.ui.b',function(){const TableFormat={};TableFormat.SelectionMode={NONE:0,ROW:1,CELL:2};TableFormat.HighlightStyle={DEFAULT:0,NONE:1,LIGHT:2,DARK:3};TableFormat.ColumnAlignment={LEFT:0,RIGHT:1};return{TableFormat,};});'use strict';(function(){const RIGHT_ARROW=String.fromCharCode(0x25b6);const UNSORTED_ARROW=String.fromCharCode(0x25BF);const ASCENDING_ARROW=String.fromCharCode(0x25B4);const DESCENDING_ARROW=String.fromCharCode(0x25BE);const SelectionMode=tr.ui.b.TableFormat.SelectionMode;const SelectionModeValues=new Set(Object.values(SelectionMode));const HighlightStyle=tr.ui.b.TableFormat.HighlightStyle;const HighlightStyleValues=new Set(Object.values(HighlightStyle));const ColumnAlignment=tr.ui.b.TableFormat.ColumnAlignment;const ColumnAlignmentValues=new Set(Object.values(ColumnAlignment));Polymer({is:'tr-ui-b-table',created(){this.selectionMode_=SelectionMode.NONE;this.rowHighlightStyle_=HighlightStyle.DEFAULT;this.cellHighlightStyle_=HighlightStyle.DEFAULT;this.selectedTableRowInfo_=undefined;this.selectedColumnIndex_=undefined;this.tableColumns_=[];this.tableRows_=[];this.tableRowsInfo_=new WeakMap();this.tableFooterRows_=[];this.tableFooterRowsInfo_=new WeakMap();this.sortColumnIndex_=undefined;this.sortDescending_=false;this.columnsWithExpandButtons_=[];this.headerCells_=[];this.showHeader_=true;this.emptyValue_=undefined;this.subRowsPropertyName_='subRows';this.customizeTableRowCallback_=undefined;this.defaultExpansionStateCallback_=undefined;this.userCanModifySortOrder_=true;this.computedFontSizePx_=undefined;},ready(){this.$.body.addEventListener('keydown',this.onKeyDown_.bind(this),true);this.$.body.addEventListener('focus',this.onFocus_.bind(this),true);},clear(){this.selectionMode_=SelectionMode.NONE;this.rowHighlightStyle_=HighlightStyle.DEFAULT;this.cellHighlightStyle_=HighlightStyle.DEFAULT;this.selectedTableRowInfo_=undefined;this.selectedColumnIndex_=undefined;Polymer.dom(this).textContent='';this.tableColumns_=[];this.tableRows_=[];this.tableRowsInfo_=new WeakMap();this.tableFooterRows_=[];this.tableFooterRowsInfo_=new WeakMap();this.sortColumnIndex_=undefined;this.sortDescending_=false;this.columnsWithExpandButtons_=[];this.headerCells_=[];this.showHeader_=true;this.emptyValue_=undefined;this.subRowsPropertyName_='subRows';this.defaultExpansionStateCallback_=undefined;this.userCanModifySortOrder_=true;},set zebra(zebra){if(zebra){this.setAttribute('zebra',true);}else{this.removeAttribute('zebra');}},get zebra(){return this.getAttribute('zebra');},get showHeader(){return this.showHeader_;},set showHeader(showHeader){this.showHeader_=showHeader;this.scheduleRebuildHeaders_();},set subRowsPropertyName(name){this.subRowsPropertyName_=name;},set defaultExpansionStateCallback(cb){this.defaultExpansionStateCallback_=cb;this.scheduleRebuildBody_();},set customizeTableRowCallback(cb){this.customizeTableRowCallback_=cb;this.scheduleRebuildBody_();},get emptyValue(){return this.emptyValue_;},set emptyValue(emptyValue){const previousEmptyValue=this.emptyValue_;this.emptyValue_=emptyValue;if(this.tableRows_.length===0&&emptyValue!==previousEmptyValue){this.scheduleRebuildBody_();}},set tableColumns(columns){let columnsWithExpandButtons=[];for(let i=0;i<columns.length;i++){if(columns[i].showExpandButtons){columnsWithExpandButtons.push(i);}}
+if(columnsWithExpandButtons.length===0){columnsWithExpandButtons=[0];}
+for(let i=0;i<columns.length;i++){const colInfo=columns[i];if(colInfo.width===undefined)continue;const hasExpandButton=columnsWithExpandButtons.includes(i);const w=colInfo.width;if(w){if(/\d+px/.test(w)){continue;}else if(/\d+%/.test(w)){if(hasExpandButton){throw new Error('Columns cannot be %-sized and host '+' an expand button');}}else{throw new Error('Unrecognized width string');}}}
+let sortIndex=undefined;const currentSortColumn=this.tableColumns[this.sortColumnIndex_];if(currentSortColumn){for(const[i,column]of columns.entries()){if(currentSortColumn.title===column.title){sortIndex=i;break;}}}
+this.tableColumns_=columns;this.headerCells_=[];this.columnsWithExpandButtons_=columnsWithExpandButtons;this.scheduleRebuildHeaders_();this.sortColumnIndex=sortIndex;this.tableRows=this.tableRows_;},get tableColumns(){return this.tableColumns_;},set tableRows(rows){this.selectedTableRowInfo_=undefined;this.selectedColumnIndex_=undefined;this.tableRows_=rows;this.tableRowsInfo_=new WeakMap();this.scheduleRebuildBody_();},get tableRows(){return this.tableRows_;},set footerRows(rows){this.tableFooterRows_=rows;this.tableFooterRowsInfo_=new WeakMap();this.scheduleRebuildFooter_();},get footerRows(){return this.tableFooterRows_;},get userCanModifySortOrder(){return this.userCanModifySortOrder_;},set userCanModifySortOrder(userCanModifySortOrder){const newUserCanModifySortOrder=!!userCanModifySortOrder;if(newUserCanModifySortOrder===this.userCanModifySortOrder_){return;}
+this.userCanModifySortOrder_=newUserCanModifySortOrder;this.scheduleRebuildHeaders_();},set sortColumnIndex(number){if(number===this.sortColumnIndex_)return;if(number!==undefined){if(this.tableColumns_.length<=number){throw new Error('Column number '+number+' is out of bounds.');}
+if(!this.tableColumns_[number].cmp){throw new Error('Column '+number+' does not have a comparator.');}}
+this.sortColumnIndex_=number;this.updateHeaderArrows_();this.scheduleRebuildBody_();this.dispatchSortingChangedEvent_();},get sortColumnIndex(){return this.sortColumnIndex_;},set sortDescending(value){const newValue=!!value;if(newValue!==this.sortDescending_){this.sortDescending_=newValue;this.updateHeaderArrows_();this.scheduleRebuildBody_();this.dispatchSortingChangedEvent_();}},get sortDescending(){return this.sortDescending_;},updateHeaderArrows_(){for(let i=0;i<this.headerCells_.length;i++){const headerCell=this.headerCells_[i];const isColumnCurrentlySorted=i===this.sortColumnIndex_;if(!this.tableColumns_[i].cmp||(!this.userCanModifySortOrder_&&!isColumnCurrentlySorted)){headerCell.sideContent='';continue;}
+if(!isColumnCurrentlySorted){headerCell.sideContent=UNSORTED_ARROW;headerCell.sideContentDisabled=false;continue;}
+headerCell.sideContent=this.sortDescending_?DESCENDING_ARROW:ASCENDING_ARROW;headerCell.sideContentDisabled=!this.userCanModifySortOrder_;}},generateHeaderColumns_(){const selectedTableColumnIndex=this.selectedTableColumnIndex;Polymer.dom(this.$.cols).textContent='';for(let i=0;i<this.tableColumns_.length;++i){const colElement=document.createElement('col');if(i===selectedTableColumnIndex){colElement.setAttribute('selected',true);}
+Polymer.dom(this.$.cols).appendChild(colElement);}
+this.headerCells_=[];Polymer.dom(this.$.head).textContent='';if(!this.showHeader_)return;const tr=this.appendNewElement_(this.$.head,'tr');for(let i=0;i<this.tableColumns_.length;i++){const td=this.appendNewElement_(tr,'td');const headerCell=document.createElement('tr-ui-b-table-header-cell');headerCell.column=this.tableColumns_[i];if(this.tableColumns_[i].cmp){const isColumnCurrentlySorted=i===this.sortColumnIndex_;if(isColumnCurrentlySorted){headerCell.sideContent=this.sortDescending_?DESCENDING_ARROW:ASCENDING_ARROW;if(!this.userCanModifySortOrder_){headerCell.sideContentDisabled=true;}}
+if(this.userCanModifySortOrder_){Polymer.dom(td).classList.add('sensitive');if(!isColumnCurrentlySorted){headerCell.sideContent=UNSORTED_ARROW;}
+headerCell.tapCallback=this.createSortCallback_(i);}}
+Polymer.dom(td).appendChild(headerCell);this.headerCells_.push(headerCell);}},applySizes_(){if(this.tableRows_.length===0&&!this.showHeader)return;let rowToRemoveSizing;let rowToSize;if(this.showHeader){rowToSize=Polymer.dom(this.$.head).children[0];rowToRemoveSizing=Polymer.dom(this.$.body).children[0];}else{rowToSize=Polymer.dom(this.$.body).children[0];rowToRemoveSizing=Polymer.dom(this.$.head).children[0];}
+for(let i=0;i<this.tableColumns_.length;i++){if(rowToRemoveSizing&&Polymer.dom(rowToRemoveSizing).children[i]){const tdToRemoveSizing=Polymer.dom(rowToRemoveSizing).children[i];tdToRemoveSizing.style.minWidth='';tdToRemoveSizing.style.width='';}
+const td=Polymer.dom(rowToSize).children[i];let delta;if(this.columnsWithExpandButtons_.includes(i)){td.style.paddingLeft=this.basicIndentation_+'px';delta=this.basicIndentation_+'px';}else{delta=undefined;}
+function calc(base,delta){if(delta){return'calc('+base+' - '+delta+')';}
+return base;}
+const w=this.tableColumns_[i].width;if(w){if(/\d+px/.test(w)){td.style.minWidth=calc(w,delta);}else if(/\d+%/.test(w)){td.style.width=w;}else{throw new Error('Unrecognized width string: '+w);}}}},createSortCallback_(columnNumber){return function(){if(!this.userCanModifySortOrder_)return;const previousIndex=this.sortColumnIndex;this.sortColumnIndex=columnNumber;if(previousIndex!==columnNumber){this.sortDescending=false;}else{this.sortDescending=!this.sortDescending;}}.bind(this);},generateTableRowNodes_(tableSection,userRows,rowInfoMap,indentation,lastAddedRow,parentRowInfo){if(this.sortColumnIndex_!==undefined&&tableSection===this.$.body){userRows=userRows.slice();userRows.sort(function(rowA,rowB){let c=this.tableColumns_[this.sortColumnIndex_].cmp(rowA,rowB);if(this.sortDescending_){c=-c;}
+return c;}.bind(this));}
+for(let i=0;i<userRows.length;i++){const userRow=userRows[i];const rowInfo=this.getOrCreateRowInfoFor_(rowInfoMap,userRow,parentRowInfo);const htmlNode=this.getHTMLNodeForRowInfo_(tableSection,rowInfo,rowInfoMap,indentation);if(lastAddedRow===undefined){Polymer.dom(tableSection).insertBefore(htmlNode,Polymer.dom(tableSection).firstChild);}else{const nextSiblingOfLastAdded=Polymer.dom(lastAddedRow).nextSibling;Polymer.dom(tableSection).insertBefore(htmlNode,nextSiblingOfLastAdded);}
+lastAddedRow=htmlNode;if(!rowInfo.isExpanded)continue;lastAddedRow=this.generateTableRowNodes_(tableSection,userRow[this.subRowsPropertyName_],rowInfoMap,indentation+1,lastAddedRow,rowInfo);}
+return lastAddedRow;},getOrCreateRowInfoFor_(rowInfoMap,userRow,parentRowInfo){let rowInfo=undefined;if(rowInfoMap.has(userRow)){rowInfo=rowInfoMap.get(userRow);}else{rowInfo={userRow,htmlNode:undefined,parentRowInfo};rowInfoMap.set(userRow,rowInfo);}
+rowInfo.isExpanded=this.getExpandedForUserRow_(userRow);return rowInfo;},customizeTableRow_(userRow,trElement){if(!this.customizeTableRowCallback_)return;this.customizeTableRowCallback_(userRow,trElement);},get basicIndentation_(){if(this.computedFontSizePx_===undefined){this.computedFontSizePx_=parseInt(getComputedStyle(this).fontSize)||16;}
+return this.computedFontSizePx_-2;},getHTMLNodeForRowInfo_(tableSection,rowInfo,rowInfoMap,indentation){if(rowInfo.htmlNode){this.customizeTableRow_(rowInfo.userRow,rowInfo.htmlNode);return rowInfo.htmlNode;}
+const INDENT_SPACE=indentation*16;const INDENT_SPACE_NO_BUTTON=indentation*16+this.basicIndentation_;const trElement=this.ownerDocument.createElement('tr');rowInfo.htmlNode=trElement;rowInfo.indentation=indentation;trElement.rowInfo=rowInfo;this.customizeTableRow_(rowInfo.userRow,trElement);const isBodyRow=tableSection===this.$.body;const isExpandableRow=rowInfo.userRow[this.subRowsPropertyName_]&&rowInfo.userRow[this.subRowsPropertyName_].length;for(let i=0;i<this.tableColumns_.length;){const td=this.appendNewElement_(trElement,'td');td.columnIndex=i;const column=this.tableColumns_[i];const value=column.value(rowInfo.userRow);const colSpan=column.colSpan?column.colSpan:1;td.style.colSpan=colSpan;switch(column.align){case undefined:case ColumnAlignment.LEFT:break;case ColumnAlignment.RIGHT:td.style.textAlign='right';break;default:throw new Error('Invalid alignment of column at index='+i+': '+column.align);}
+if(this.doesColumnIndexSupportSelection(i)){Polymer.dom(td).classList.add('supports-selection');}
+if(this.columnsWithExpandButtons_.includes(i)){if(rowInfo.userRow[this.subRowsPropertyName_]&&rowInfo.userRow[this.subRowsPropertyName_].length>0){td.style.paddingLeft=INDENT_SPACE+'px';td.style.display='flex';const expandButton=this.appendNewElement_(td,'expand-button');Polymer.dom(expandButton).textContent=RIGHT_ARROW;if(rowInfo.isExpanded){Polymer.dom(expandButton).classList.add('button-expanded');}}else{td.style.paddingLeft=INDENT_SPACE_NO_BUTTON+'px';}}
+if(value!==undefined){Polymer.dom(td).appendChild(tr.ui.b.asHTMLOrTextNode(value,this.ownerDocument));}
+td.addEventListener('click',function(i,clickEvent){clickEvent.preventDefault();if(!isBodyRow&&!isExpandableRow)return;clickEvent.stopPropagation();if(clickEvent.target.tagName==='EXPAND-BUTTON'){this.setExpandedForUserRow_(tableSection,rowInfoMap,rowInfo.userRow,!rowInfo.isExpanded);return;}
+if(isBodyRow&&this.selectionMode_!==SelectionMode.NONE){let shouldSelect=false;let shouldFocus=false;switch(this.selectionMode_){case SelectionMode.ROW:shouldSelect=this.selectedTableRowInfo_!==rowInfo;shouldFocus=true;break;case SelectionMode.CELL:if(this.doesColumnIndexSupportSelection(i)){shouldSelect=this.selectedTableRowInfo_!==rowInfo||this.selectedColumnIndex_!==i;shouldFocus=true;}
+break;default:throw new Error('Invalid selection mode '+
+this.selectionMode_);}
+if(shouldFocus){this.focus();}
+if(shouldSelect){this.didTableRowInfoGetClicked_(rowInfo,i);return;}}
+if(isExpandableRow){this.setExpandedForUserRow_(tableSection,rowInfoMap,rowInfo.userRow,!rowInfo.isExpanded);}}.bind(this,i));if(isBodyRow){td.addEventListener('dblclick',function(i,e){e.stopPropagation();this.dispatchStepIntoEvent_(rowInfo,i);}.bind(this,i));}
+i+=colSpan;}
+return rowInfo.htmlNode;},removeSubNodes_(tableSection,rowInfo,rowInfoMap){if(rowInfo.userRow[this.subRowsPropertyName_]===undefined)return;for(let i=0;i<rowInfo.userRow[this.subRowsPropertyName_].length;i++){const subRow=rowInfo.userRow[this.subRowsPropertyName_][i];const subRowInfo=rowInfoMap.get(subRow);if(!subRowInfo)continue;const subNode=subRowInfo.htmlNode;if(subNode&&Polymer.dom(subNode).parentNode===tableSection){Polymer.dom(tableSection).removeChild(subNode);this.removeSubNodes_(tableSection,subRowInfo,rowInfoMap);}}},scheduleRebuildHeaders_(){this.headerDirty_=true;this.scheduleRebuild_();},scheduleRebuildBody_(){this.bodyDirty_=true;this.scheduleRebuild_();},scheduleRebuildFooter_(){this.footerDirty_=true;this.scheduleRebuild_();},scheduleRebuild_(){if(this.rebuildPending_)return;this.rebuildPending_=true;setTimeout(function(){this.rebuildPending_=false;this.rebuild();}.bind(this),0);},rebuildIfNeeded_(){this.rebuild();},rebuild(){const wasBodyOrHeaderDirty=this.headerDirty_||this.bodyDirty_;if(this.headerDirty_){this.generateHeaderColumns_();this.headerDirty_=false;}
+if(this.bodyDirty_){Polymer.dom(this.$.body).textContent='';this.generateTableRowNodes_(this.$.body,this.tableRows_,this.tableRowsInfo_,0,undefined,undefined);if(this.tableRows_.length===0&&this.emptyValue_!==undefined){const trElement=this.ownerDocument.createElement('tr');Polymer.dom(this.$.body).appendChild(trElement);Polymer.dom(trElement).classList.add('empty-row');const td=this.ownerDocument.createElement('td');Polymer.dom(trElement).appendChild(td);td.colSpan=this.tableColumns_.length;const emptyValue=this.emptyValue_;Polymer.dom(td).appendChild(tr.ui.b.asHTMLOrTextNode(emptyValue,this.ownerDocument));}
+this.bodyDirty_=false;}
+if(wasBodyOrHeaderDirty)this.applySizes_();if(this.footerDirty_){Polymer.dom(this.$.foot).textContent='';this.generateTableRowNodes_(this.$.foot,this.tableFooterRows_,this.tableFooterRowsInfo_,0,undefined,undefined);if(this.tableFooterRowsInfo_.length){Polymer.dom(this.$.body).classList.add('has-footer');}else{Polymer.dom(this.$.body).classList.remove('has-footer');}
+this.footerDirty_=false;}},appendNewElement_(parent,tagName){const element=parent.ownerDocument.createElement(tagName);Polymer.dom(parent).appendChild(element);return element;},getExpandedForTableRow(userRow){this.rebuildIfNeeded_();const rowInfo=this.tableRowsInfo_.get(userRow);if(rowInfo===undefined){throw new Error('Row has not been seen, must expand its parents');}
+return rowInfo.isExpanded;},getExpandedForUserRow_(userRow){if(userRow[this.subRowsPropertyName_]===undefined){return false;}
+if(userRow[this.subRowsPropertyName_].length===0){return false;}
+if(userRow.isExpanded){return true;}
+if((userRow.isExpanded!==undefined)&&(userRow.isExpanded===false)){return false;}
+const rowInfo=this.tableRowsInfo_.get(userRow);if(rowInfo&&rowInfo.isExpanded){return true;}
+if(this.defaultExpansionStateCallback_===undefined){return false;}
+let parentUserRow=undefined;if(rowInfo&&rowInfo.parentRowInfo){parentUserRow=rowInfo.parentRowInfo.userRow;}
+return this.defaultExpansionStateCallback_(userRow,parentUserRow);},setExpandedForTableRow(userRow,expanded){this.rebuildIfNeeded_();const rowInfo=this.tableRowsInfo_.get(userRow);if(rowInfo===undefined){throw new Error('Row has not been seen, must expand its parents');}
+return this.setExpandedForUserRow_(this.$.body,this.tableRowsInfo_,userRow,expanded);},setExpandedForUserRow_(tableSection,rowInfoMap,userRow,expanded){this.rebuildIfNeeded_();const rowInfo=rowInfoMap.get(userRow);if(rowInfo===undefined){throw new Error('Row has not been seen, must expand its parents');}
+const wasExpanded=rowInfo.isExpanded;rowInfo.isExpanded=!!expanded;if(rowInfo.htmlNode===undefined)return;if(rowInfo.htmlNode.parentElement!==tableSection){return;}
+const expandButton=Polymer.dom(rowInfo.htmlNode).querySelector('expand-button');if(rowInfo.isExpanded){Polymer.dom(expandButton).classList.add('button-expanded');const lastAddedRow=rowInfo.htmlNode;if(rowInfo.userRow[this.subRowsPropertyName_]){this.generateTableRowNodes_(tableSection,rowInfo.userRow[this.subRowsPropertyName_],rowInfoMap,rowInfo.indentation+1,lastAddedRow,rowInfo);}}else{Polymer.dom(expandButton).classList.remove('button-expanded');this.removeSubNodes_(tableSection,rowInfo,rowInfoMap);}
+if(wasExpanded!==rowInfo.isExpanded){const e=new tr.b.Event('row-expanded-changed');e.row=rowInfo.userRow;this.dispatchEvent(e);}
+this.maybeUpdateSelectedRow_();},get selectionMode(){return this.selectionMode_;},set selectionMode(selectionMode){if(!SelectionModeValues.has(selectionMode)){throw new Error('Invalid selection mode '+selectionMode);}
+this.rebuildIfNeeded_();this.selectionMode_=selectionMode;this.didSelectionStateChange_();},get rowHighlightStyle(){return this.rowHighlightStyle_;},set rowHighlightStyle(rowHighlightStyle){if(!HighlightStyleValues.has(rowHighlightStyle)){throw new Error('Invalid row highlight style '+rowHighlightStyle);}
+this.rebuildIfNeeded_();this.rowHighlightStyle_=rowHighlightStyle;this.didSelectionStateChange_();},get resolvedRowHighlightStyle(){if(this.rowHighlightStyle_!==HighlightStyle.DEFAULT){return this.rowHighlightStyle_;}
+switch(this.selectionMode_){case SelectionMode.NONE:return HighlightStyle.NONE;case SelectionMode.ROW:return HighlightStyle.DARK;case SelectionMode.CELL:return HighlightStyle.LIGHT;default:throw new Error('Invalid selection mode '+selectionMode);}},get cellHighlightStyle(){return this.cellHighlightStyle_;},set cellHighlightStyle(cellHighlightStyle){if(!HighlightStyleValues.has(cellHighlightStyle)){throw new Error('Invalid cell highlight style '+cellHighlightStyle);}
+this.rebuildIfNeeded_();this.cellHighlightStyle_=cellHighlightStyle;this.didSelectionStateChange_();},get resolvedCellHighlightStyle(){if(this.cellHighlightStyle_!==HighlightStyle.DEFAULT){return this.cellHighlightStyle_;}
+switch(this.selectionMode_){case SelectionMode.NONE:case SelectionMode.ROW:return HighlightStyle.NONE;case SelectionMode.CELL:return HighlightStyle.DARK;default:throw new Error('Invalid selection mode '+selectionMode);}},setHighlightStyle_(highlightAttribute,resolvedHighlightStyle){switch(resolvedHighlightStyle){case HighlightStyle.NONE:Polymer.dom(this.$.body).removeAttribute(highlightAttribute);break;case HighlightStyle.LIGHT:Polymer.dom(this.$.body).setAttribute(highlightAttribute,'light');break;case HighlightStyle.DARK:Polymer.dom(this.$.body).setAttribute(highlightAttribute,'dark');break;default:throw new Error('Invalid resolved highlight style '+
+resolvedHighlightStyle);}},didSelectionStateChange_(){this.setHighlightStyle_('row-highlight-style',this.resolvedRowHighlightStyle);this.setHighlightStyle_('cell-highlight-style',this.resolvedCellHighlightStyle);this.removeSelectedState_();switch(this.selectionMode_){case SelectionMode.ROW:Polymer.dom(this.$.body).setAttribute('selection-mode','row');Polymer.dom(this.$.body).setAttribute('tabindex',0);this.selectedColumnIndex_=undefined;break;case SelectionMode.CELL:Polymer.dom(this.$.body).setAttribute('selection-mode','cell');Polymer.dom(this.$.body).setAttribute('tabindex',0);if(this.selectedTableRowInfo_&&this.selectedColumnIndex_===undefined){const i=this.getFirstSelectableColumnIndex_();if(i===-1){this.selectedTableRowInfo_=undefined;}else{this.selectedColumnIndex_=i;}}
+break;case SelectionMode.NONE:Polymer.dom(this.$.body).removeAttribute('selection-mode');Polymer.dom(this.$.body).removeAttribute('tabindex');this.$.body.blur();this.selectedTableRowInfo_=undefined;this.selectedColumnIndex_=undefined;break;default:throw new Error('Invalid selection mode '+this.selectionMode_);}
+this.maybeUpdateSelectedRow_();},maybeUpdateSelectedRow_(){if(this.selectedTableRowInfo_===undefined)return;function isVisible(rowInfo){if(!rowInfo.htmlNode)return false;return!!rowInfo.htmlNode.parentElement;}
+if(isVisible(this.selectedTableRowInfo_)){this.updateSelectedState_();return;}
+this.removeSelectedState_();let curRowInfo=this.selectedTableRowInfo_;while(curRowInfo&&!isVisible(curRowInfo)){curRowInfo=curRowInfo.parentRowInfo;}
+this.selectedTableRowInfo_=curRowInfo;if(this.selectedTableRowInfo_){this.updateSelectedState_();}else{this.selectedColumnIndex_=undefined;}},didTableRowInfoGetClicked_(rowInfo,columnIndex){switch(this.selectionMode_){case SelectionMode.NONE:return;case SelectionMode.CELL:if(!this.doesColumnIndexSupportSelection(columnIndex)){return;}
+if(this.selectedColumnIndex!==columnIndex){this.selectedColumnIndex=columnIndex;}
+case SelectionMode.ROW:if(this.selectedTableRowInfo_!==rowInfo){this.selectedTableRow=rowInfo.userRow;}}},dispatchStepIntoEvent_(rowInfo,columnIndex){const e=new tr.b.Event('step-into');e.tableRow=rowInfo.userRow;e.tableColumn=this.tableColumns_[columnIndex];e.columnIndex=columnIndex;this.dispatchEvent(e);},get selectedCell(){const row=this.selectedTableRow;const columnIndex=this.selectedColumnIndex;if(row===undefined||columnIndex===undefined||this.tableColumns_.length<=columnIndex){return undefined;}
+const column=this.tableColumns_[columnIndex];return{row,column,value:column.value(row)};},get selectedTableColumnIndex(){const cols=Polymer.dom(this.$.cols).children;for(let i=0;i<cols.length;++i){if(cols[i].getAttribute('selected')){return i;}}
+return undefined;},set selectedTableColumnIndex(selectedIndex){const cols=Polymer.dom(this.$.cols).children;for(let i=0;i<cols.length;++i){if(i===selectedIndex){cols[i].setAttribute('selected',true);}else{cols[i].removeAttribute('selected');}}},get selectedTableRow(){if(!this.selectedTableRowInfo_)return undefined;return this.selectedTableRowInfo_.userRow;},set selectedTableRow(userRow){this.rebuildIfNeeded_();if(this.selectionMode_===SelectionMode.NONE){throw new Error('Selection is off.');}
+let rowInfo;if(userRow===undefined){rowInfo=undefined;}else{rowInfo=this.tableRowsInfo_.get(userRow);if(!rowInfo){throw new Error('Row has not been seen, must expand its parents.');}}
+const e=this.prepareToChangeSelection_();if(!rowInfo){this.selectedColumnIndex_=undefined;}else{switch(this.selectionMode_){case SelectionMode.ROW:this.selectedColumnIndex_=undefined;break;case SelectionMode.CELL:if(this.selectedColumnIndex_===undefined){const i=this.getFirstSelectableColumnIndex_();if(i===-1){throw new Error('Cannot find a selectable column.');}
+this.selectedColumnIndex_=i;}
+break;default:throw new Error('Invalid selection mode '+this.selectionMode_);}}
+this.selectedTableRowInfo_=rowInfo;this.updateSelectedState_();this.dispatchEvent(e);},prepareToChangeSelection_(){const e=new tr.b.Event('selection-changed');const previousSelectedRowInfo=this.selectedTableRowInfo_;if(previousSelectedRowInfo){e.previousSelectedTableRow=previousSelectedRowInfo.userRow;}else{e.previousSelectedTableRow=undefined;}
+this.removeSelectedState_();return e;},removeSelectedState_(){this.setSelectedState_(false);},updateSelectedState_(){this.setSelectedState_(true);},setSelectedState_(select){if(this.selectedTableRowInfo_===undefined)return;const rowNode=this.selectedTableRowInfo_.htmlNode;if(select){Polymer.dom(rowNode).setAttribute('selected',true);}else{Polymer.dom(rowNode).removeAttribute('selected');}
+const cellNode=Polymer.dom(rowNode).children[this.selectedColumnIndex_];if(!cellNode)return;if(select){Polymer.dom(cellNode).setAttribute('selected',true);}else{Polymer.dom(cellNode).removeAttribute('selected');}},doesColumnIndexSupportSelection(columnIndex){const columnInfo=this.tableColumns_[columnIndex];const scs=columnInfo.supportsCellSelection;if(scs===false)return false;return true;},getFirstSelectableColumnIndex_(){for(let i=0;i<this.tableColumns_.length;i++){if(this.doesColumnIndexSupportSelection(i)){return i;}}
+return-1;},getSelectableNodeGivenTableRowNode_(htmlNode){switch(this.selectionMode_){case SelectionMode.ROW:return htmlNode;case SelectionMode.CELL:return Polymer.dom(htmlNode).children[this.selectedColumnIndex_];default:throw new Error('Invalid selection mode '+this.selectionMode_);}},get selectedColumnIndex(){if(this.selectionMode_!==SelectionMode.CELL){return undefined;}
+return this.selectedColumnIndex_;},set selectedColumnIndex(selectedColumnIndex){this.rebuildIfNeeded_();if(this.selectionMode_===SelectionMode.NONE){throw new Error('Selection is off.');}
+if(selectedColumnIndex<0||selectedColumnIndex>=this.tableColumns_.length){throw new Error('Invalid index');}
+if(!this.doesColumnIndexSupportSelection(selectedColumnIndex)){throw new Error('Selection is not supported on this column');}
+const e=this.prepareToChangeSelection_();if(this.selectedColumnIndex_===undefined){this.selectedTableRowInfo_=undefined;}else if(!this.selectedTableRowInfo_){if(this.tableRows_.length===0){throw new Error('No available row to be selected');}
+this.selectedTableRowInfo_=this.tableRowsInfo_.get(this.tableRows_[0]);}
+this.selectedColumnIndex_=selectedColumnIndex;this.updateSelectedState_();this.dispatchEvent(e);},onKeyDown_(e){if(this.selectionMode_===SelectionMode.NONE)return;const CODE_TO_COMMAND_NAMES={13:'ENTER',32:'SPACE',37:'ARROW_LEFT',38:'ARROW_UP',39:'ARROW_RIGHT',40:'ARROW_DOWN'};const cmdName=CODE_TO_COMMAND_NAMES[e.keyCode];if(cmdName===undefined)return;e.stopPropagation();e.preventDefault();this.performKeyCommand_(cmdName);},onFocus_(e){if(this.selectionMode_===SelectionMode.NONE||this.selectedTableRow||this.tableRows_.length===0){return;}
+if(this.selectionMode_===SelectionMode.CELL&&this.getFirstSelectableColumnIndex_()===-1){return;}
+this.selectedTableRow=this.tableRows_[0];},focus(){this.$.body.focus();this.onFocus_();},blur(){this.$.body.blur();},get isFocused(){return this.root.activeElement===this.$.body;},performKeyCommand_(cmdName){this.rebuildIfNeeded_();switch(cmdName){case'ARROW_UP':this.selectPreviousOrFirstRowIfPossible_();return;case'ARROW_DOWN':this.selectNextOrFirstRowIfPossible_();return;case'ARROW_RIGHT':switch(this.selectionMode_){case SelectionMode.NONE:return;case SelectionMode.ROW:this.expandRowAndSelectChildRowIfPossible_();return;case SelectionMode.CELL:this.selectNextSelectableCellToTheRightIfPossible_();return;default:throw new Error('Invalid selection mode '+this.selectionMode_);}
+case'ARROW_LEFT':switch(this.selectionMode_){case SelectionMode.NONE:return;case SelectionMode.ROW:this.collapseRowOrSelectParentRowIfPossible_();return;case SelectionMode.CELL:this.selectNextSelectableCellToTheLeftIfPossible_();return;default:throw new Error('Invalid selection mode '+this.selectionMode_);}
+case'SPACE':this.toggleRowExpansionStateIfPossible_();return;case'ENTER':this.stepIntoSelectionIfPossible_();return;default:throw new Error('Unrecognized command '+cmdName);}},selectPreviousOrFirstRowIfPossible_(){const prev=this.selectedTableRowInfo_?this.selectedTableRowInfo_.htmlNode.previousElementSibling:this.$.body.firstChild;if(!prev)return;if(this.selectionMode_===SelectionMode.CELL&&this.getFirstSelectableColumnIndex_()===-1){return;}
+tr.ui.b.scrollIntoViewIfNeeded(prev);this.selectedTableRow=prev.rowInfo.userRow;},selectNextOrFirstRowIfPossible_(){this.getFirstSelectableColumnIndex_;const next=this.selectedTableRowInfo_?this.selectedTableRowInfo_.htmlNode.nextElementSibling:this.$.body.firstChild;if(!next)return;if(this.selectionMode_===SelectionMode.CELL&&this.getFirstSelectableColumnIndex_()===-1){return;}
+tr.ui.b.scrollIntoViewIfNeeded(next);this.selectedTableRow=next.rowInfo.userRow;},expandRowAndSelectChildRowIfPossible_(){const selectedRowInfo=this.selectedTableRowInfo_;if(!selectedRowInfo||selectedRowInfo.userRow[this.subRowsPropertyName_]===undefined||selectedRowInfo.userRow[this.subRowsPropertyName_].length===0){return;}
+if(!selectedRowInfo.isExpanded){this.setExpandedForTableRow(selectedRowInfo.userRow,true);}
+this.selectedTableRow=selectedRowInfo.htmlNode.nextElementSibling.rowInfo.userRow;},collapseRowOrSelectParentRowIfPossible_(){const selectedRowInfo=this.selectedTableRowInfo_;if(!selectedRowInfo)return;if(selectedRowInfo.isExpanded){this.setExpandedForTableRow(selectedRowInfo.userRow,false);}else{const parentRowInfo=selectedRowInfo.parentRowInfo;if(parentRowInfo){this.selectedTableRow=parentRowInfo.userRow;}}},selectNextSelectableCellToTheRightIfPossible_(){if(!this.selectedTableRowInfo_||this.selectedColumnIndex_===undefined){return;}
+for(let i=this.selectedColumnIndex_+1;i<this.tableColumns_.length;i++){if(this.doesColumnIndexSupportSelection(i)){this.selectedColumnIndex=i;return;}}},selectNextSelectableCellToTheLeftIfPossible_(){if(!this.selectedTableRowInfo_||this.selectedColumnIndex_===undefined){return;}
+for(let i=this.selectedColumnIndex_-1;i>=0;i--){if(this.doesColumnIndexSupportSelection(i)){this.selectedColumnIndex=i;return;}}},toggleRowExpansionStateIfPossible_(){const selectedRowInfo=this.selectedTableRowInfo_;if(!selectedRowInfo||selectedRowInfo.userRow[this.subRowsPropertyName_]===undefined||selectedRowInfo.userRow[this.subRowsPropertyName_].length===0){return;}
+this.setExpandedForTableRow(selectedRowInfo.userRow,!selectedRowInfo.isExpanded);},stepIntoSelectionIfPossible_(){if(!this.selectedTableRowInfo_)return;this.dispatchStepIntoEvent_(this.selectedTableRowInfo_,this.selectedColumnIndex_);},dispatchSortingChangedEvent_(){const e=new tr.b.Event('sort-column-changed');e.sortColumnIndex=this.sortColumnIndex_;e.sortDescending=this.sortDescending_;this.dispatchEvent(e);}});})();'use strict';const ColumnAlignment=tr.ui.b.TableFormat.ColumnAlignment;Polymer({is:'tr-ui-b-table-header-cell',created(){this.tapCallback_=undefined;this.cellTitle_='';this.align_=undefined;this.selectable_=false;this.column_=undefined;},ready(){this.addEventListener('click',this.onTap_.bind(this));},set column(column){this.column_=column;this.align=column.align;this.cellTitle=column.title;},get column(){return this.column_;},set cellTitle(value){this.cellTitle_=value;const titleNode=tr.ui.b.asHTMLOrTextNode(this.cellTitle_,this.ownerDocument);this.$.title.innerText='';Polymer.dom(this.$.title).appendChild(titleNode);},get cellTitle(){return this.cellTitle_;},set align(align){switch(align){case undefined:case ColumnAlignment.LEFT:this.style.justifyContent='';break;case ColumnAlignment.RIGHT:this.style.justifyContent='flex-end';break;default:throw new Error('Invalid alignment of column (title=\''+
+this.cellTitle_+'\'): '+align);}
+this.align_=align;},get align(){return this.align_;},clearSideContent(){Polymer.dom(this.$.side).textContent='';},set sideContent(content){Polymer.dom(this.$.side).textContent=content;this.$.side.style.display=content?'inline':'none';},get sideContent(){return Polymer.dom(this.$.side).textContent;},set sideContentDisabled(sideContentDisabled){this.$.side.classList.toggle('disabled',sideContentDisabled);},get sideContentDisabled(){return this.$.side.classList.contains('disabled');},set tapCallback(callback){this.style.cursor='pointer';this.tapCallback_=callback;},get tapCallback(){return this.tapCallback_;},onTap_(){if(this.tapCallback_){this.tapCallback_();}}});'use strict';tr.exportTo('tr.b.math',function(){class RunningStatistics{constructor(){this.mean_=0;this.count_=0;this.max_=-Infinity;this.min_=Infinity;this.sum_=0;this.variance_=0;this.meanlogs_=0;}
+get count(){return this.count_;}
+get geometricMean(){if(this.meanlogs_===undefined)return 0;return Math.exp(this.meanlogs_);}
+get mean(){if(this.count_===0)return undefined;return this.mean_;}
+get max(){return this.max_;}
+get min(){return this.min_;}
+get sum(){return this.sum_;}
+get variance(){if(this.count_===0)return undefined;if(this.count_===1)return 0;return this.variance_/(this.count_-1);}
+get stddev(){if(this.count_===0)return undefined;return Math.sqrt(this.variance);}
+add(x){this.count_++;this.max_=Math.max(this.max_,x);this.min_=Math.min(this.min_,x);this.sum_+=x;if(x<=0){this.meanlogs_=undefined;}else if(this.meanlogs_!==undefined){this.meanlogs_+=(Math.log(Math.abs(x))-this.meanlogs_)/this.count;}
+if(this.count_===1){this.mean_=x;this.variance_=0;}else{const oldMean=this.mean_;const oldVariance=this.variance_;if(oldMean===Infinity||oldMean===-Infinity){this.mean_=this.sum_/this.count_;}else{this.mean_=oldMean+(x-oldMean)/this.count_;}
+this.variance_=oldVariance+(x-oldMean)*(x-this.mean_);}}
+merge(other){const result=new RunningStatistics();result.count_=this.count_+other.count_;result.sum_=this.sum_+other.sum_;result.min_=Math.min(this.min_,other.min_);result.max_=Math.max(this.max_,other.max_);if(result.count===0){result.mean_=0;result.variance_=0;result.meanlogs_=0;}else{result.mean_=result.sum/result.count;const deltaMean=(this.mean||0)-(other.mean||0);result.variance_=this.variance_+other.variance_+
+(this.count*other.count*deltaMean*deltaMean/result.count);if(this.meanlogs_===undefined||other.meanlogs_===undefined){result.meanlogs_=undefined;}else{result.meanlogs_=(this.count*this.meanlogs_+
+other.count*other.meanlogs_)/result.count;}}
+return result;}
+truncate(unit){this.max_=unit.truncate(this.max_);if(this.meanlogs_!==undefined){const formatted=unit.format(this.geometricMean);let lo=1;let hi=16;while(lo<hi-1){const digits=parseInt((lo+hi)/2);const test=tr.b.math.truncate(this.meanlogs_,digits);if(formatted===unit.format(Math.exp(test))){hi=digits;}else{lo=digits;}}
+const test=tr.b.math.truncate(this.meanlogs_,lo);if(formatted===unit.format(Math.exp(test))){this.meanlogs_=test;}else{this.meanlogs_=tr.b.math.truncate(this.meanlogs_,hi);}}
+this.mean_=unit.truncate(this.mean_);this.min_=unit.truncate(this.min_);this.sum_=unit.truncate(this.sum_);this.variance_=unit.truncate(this.variance_);}
+asDict(){if(!this.count){return[];}
+return[this.count_,this.max_,this.meanlogs_,this.mean_,this.min_,this.sum_,this.variance_,];}
+static fromDict(dict){const result=new RunningStatistics();if(dict.length!==7){return result;}
+[result.count_,result.max_,result.meanlogs_,result.mean_,result.min_,result.sum_,result.variance_,]=dict;return result;}}
+return{RunningStatistics,};});'use strict';tr.exportTo('tr.v.d',function(){class Diagnostic{constructor(){this.guid_=undefined;}
+clone(){return new this.constructor();}
+canAddDiagnostic(otherDiagnostic){return false;}
+addDiagnostic(otherDiagnostic){throw new Error('Abstract virtual method: subclasses must override '+'this method if they override canAddDiagnostic');}
+get guid(){if(this.guid_===undefined){this.guid_=tr.b.GUID.allocateUUID4();}
+return this.guid_;}
+set guid(guid){if(this.guid_!==undefined){throw new Error('Cannot reset guid');}
+this.guid_=guid;}
+get hasGuid(){return this.guid_!==undefined;}
+asDictOrReference(){if(this.guid_!==undefined){return this.guid_;}
+return this.asDict();}
+asDict(){const result={type:this.constructor.name};if(this.guid_!==undefined){result.guid=this.guid_;}
+this.asDictInto_(result);return result;}
+asDictInto_(d){throw new Error('Abstract virtual method: subclasses must override '+'this method if they override canAddDiagnostic');}
+static fromDict(d){const typeInfo=Diagnostic.findTypeInfoWithName(d.type);if(!typeInfo){throw new Error('Unrecognized diagnostic type: '+d.type);}
+const diagnostic=typeInfo.constructor.fromDict(d);if(d.guid!==undefined)diagnostic.guid=d.guid;return diagnostic;}
+static deserialize(type,d,deserializer){const typeInfo=Diagnostic.findTypeInfoWithName(type);if(!typeInfo){throw new Error('Unrecognized diagnostic type: '+type);}
+return typeInfo.constructor.deserialize(d,deserializer);}}
+const options=new tr.b.ExtensionRegistryOptions(tr.b.BASIC_REGISTRY_MODE);options.defaultMetadata={};options.mandatoryBaseClass=Diagnostic;tr.b.decorateExtensionRegistry(Diagnostic,options);Diagnostic.addEventListener('will-register',function(e){const constructor=e.typeInfo.constructor;if(!(constructor.deserialize instanceof Function)||(constructor.deserialize===Diagnostic.deserialize)||(constructor.deserialize.length!==2)){throw new Error(`Please define ${constructor.name}.deserialize(data, deserializer)`);}
+if(!(constructor.fromDict instanceof Function)||(constructor.fromDict===Diagnostic.fromDict)||(constructor.fromDict.length!==1)){throw new Error(`Please define ${constructor.name}.fromDict(d)`);}
+if(!(constructor.prototype.serialize instanceof Function)||(constructor.prototype.serialize===Diagnostic.prototype.serialize)||(constructor.prototype.serialize.length!==1)){throw new Error(`Please define ${constructor.name}.serialize(serializer)`);}});return{Diagnostic,};});'use strict';tr.exportTo('tr.v.d',function(){class Breakdown extends tr.v.d.Diagnostic{constructor(){super();this.values_=new Map();this.colorScheme='';}
+truncate(unit){for(const[name,value]of this){this.values_.set(name,unit.truncate(value));}}
+clone(){const clone=new Breakdown();clone.colorScheme=this.colorScheme;clone.addDiagnostic(this);return clone;}
+equals(other){if(this.colorScheme!==other.colorScheme)return false;if(this.values_.size!==other.values_.size)return false;for(const[k,v]of this){if(v!==other.get(k))return false;}
+return true;}
+canAddDiagnostic(otherDiagnostic){return((otherDiagnostic instanceof Breakdown)&&(otherDiagnostic.colorScheme===this.colorScheme));}
+addDiagnostic(otherDiagnostic){for(const[name,value]of otherDiagnostic){this.set(name,this.get(name)+value);}
+return this;}
+set(name,value){if(typeof name!=='string'||typeof value!=='number'){throw new Error('Breakdown maps from strings to numbers');}
+this.values_.set(name,value);}
+get(name){return this.values_.get(name)||0;}*[Symbol.iterator](){for(const pair of this.values_){yield pair;}}
+get size(){return this.values_.size;}
+serialize(serializer){const keys=[...this.values_.keys()];keys.sort();return[serializer.getOrAllocateId(this.colorScheme),serializer.getOrAllocateId(keys.map(k=>serializer.getOrAllocateId(k))),...keys.map(k=>this.get(k)),];}
+asDictInto_(d){d.values={};for(const[name,value]of this){d.values[name]=tr.b.numberToJson(value);}
+if(this.colorScheme){d.colorScheme=this.colorScheme;}}
+static fromEntries(entries){const breakdown=new Breakdown();for(const[name,value]of entries){breakdown.set(name,value);}
+return breakdown;}
+static deserialize(data,deserializer){const breakdown=new Breakdown();breakdown.colorScheme=deserializer.getObject(data[0]);const keys=deserializer.getObject(data[1]);for(let i=0;i<keys.length;++i){breakdown.set(deserializer.getObject(keys[i]),tr.b.numberFromJson(data[i+2]));}
+return breakdown;}
+static fromDict(d){const breakdown=new Breakdown();for(const[name,value]of Object.entries(d.values)){breakdown.set(name,tr.b.numberFromJson(value));}
+if(d.colorScheme){breakdown.colorScheme=d.colorScheme;}
+return breakdown;}}
+tr.v.d.Diagnostic.register(Breakdown,{elementName:'tr-v-ui-breakdown-span'});return{Breakdown,};});'use strict';tr.exportTo('tr.v.d',function(){class CollectedRelatedEventSet extends tr.v.d.Diagnostic{constructor(){super();this.eventSetsByCanonicalUrl_=new Map();}
+asDictInto_(d){d.events={};for(const[canonicalUrl,eventSet]of this){d.events[canonicalUrl]=[];for(const event of eventSet){d.events[canonicalUrl].push({stableId:event.stableId,title:event.title,start:event.start,duration:event.duration});}}}
+static deserialize(events,deserializer){return CollectedRelatedEventSet.fromDict({events});}
+serialize(serializer){const d={};this.asDictInto(d);return d.events;}
+static fromDict(d){const result=new CollectedRelatedEventSet();for(const[canonicalUrl,events]of Object.entries(d.events)){result.eventSetsByCanonicalUrl_.set(canonicalUrl,events.map(e=>new tr.v.d.EventRef(e)));}
+return result;}
+get size(){return this.eventSetsByCanonicalUrl_.size;}
+get(canonicalUrl){return this.eventSetsByCanonicalUrl_.get(canonicalUrl);}*[Symbol.iterator](){for(const[canonicalUrl,eventSet]of this.eventSetsByCanonicalUrl_){yield[canonicalUrl,eventSet];}}
+canAddDiagnostic(otherDiagnostic){return otherDiagnostic instanceof tr.v.d.RelatedEventSet||otherDiagnostic instanceof tr.v.d.CollectedRelatedEventSet;}
+addEventSetForCanonicalUrl(canonicalUrl,events){let myEventSet=this.eventSetsByCanonicalUrl_.get(canonicalUrl);if(myEventSet===undefined){myEventSet=new Set();this.eventSetsByCanonicalUrl_.set(canonicalUrl,myEventSet);}
+for(const event of events){myEventSet.add(event);}}
+addDiagnostic(otherDiagnostic){if(otherDiagnostic instanceof tr.v.d.CollectedRelatedEventSet){for(const[canonicalUrl,otherEventSet]of otherDiagnostic){this.addEventSetForCanonicalUrl(canonicalUrl,otherEventSet);}
+return;}
+if(!otherDiagnostic.canonicalUrl)return;this.addEventSetForCanonicalUrl(otherDiagnostic.canonicalUrl,otherDiagnostic);}}
+tr.v.d.Diagnostic.register(CollectedRelatedEventSet,{elementName:'tr-v-ui-collected-related-event-set-span'});return{CollectedRelatedEventSet,};});'use strict';tr.exportTo('tr.v.d',function(){class DateRange extends tr.v.d.Diagnostic{constructor(ms){super();this.range_=new tr.b.math.Range();this.range_.addValue(ms);}
+get minTimestamp(){return this.range_.min;}
+get maxTimestamp(){return this.range_.max;}
+get minDate(){return new Date(this.range_.min);}
+get maxDate(){return new Date(this.range_.max);}
+get durationMs(){return this.range_.duration;}
+clone(){const clone=new tr.v.d.DateRange(this.range_.min);clone.addDiagnostic(this);return clone;}
+equals(other){if(!(other instanceof DateRange))return false;return this.range_.equals(other.range_);}
+canAddDiagnostic(otherDiagnostic){return otherDiagnostic instanceof DateRange;}
+addDiagnostic(other){this.range_.addRange(other.range_);}
+toString(){const minDate=tr.b.formatDate(this.minDate);if(this.durationMs===0)return minDate;const maxDate=tr.b.formatDate(this.maxDate);return`${minDate} - ${maxDate}`;}
+serialize(serializer){if(this.durationMs===0)return this.range_.min;return[this.range_.min,this.range_.max];}
+asDictInto_(d){d.min=this.range_.min;if(this.durationMs===0)return;d.max=this.range_.max;}
+static deserialize(data,deserializer){if(data instanceof Array){const dr=new DateRange(data[0]);dr.range_.addValue(data[1]);return dr;}
+return new DateRange(data);}
+static fromDict(d){const dateRange=new DateRange(d.min);if(d.max!==undefined)dateRange.range_.addValue(d.max);return dateRange;}}
+tr.v.d.Diagnostic.register(DateRange,{elementName:'tr-v-ui-date-range-span'});return{DateRange,};});'use strict';tr.exportTo('tr.v.d',function(){class DiagnosticRef{constructor(guid){this.guid=guid;}
+asDict(){return this.guid;}
+asDictOrReference(){return this.asDict();}}
+return{DiagnosticRef,};});'use strict';tr.exportTo('tr.v.d',function(){function stableStringify(obj){let replacer;if(!(obj instanceof Array)&&obj!==null){replacer=Object.keys(obj).sort();}
+return JSON.stringify(obj,replacer);}
+class GenericSet extends tr.v.d.Diagnostic{constructor(values){super();if(typeof values[Symbol.iterator]!=='function'){throw new Error('GenericSet must be constructed from an interable.');}
+this.values_=new Set(values);this.has_objects_=false;for(const value of values){if(typeof value==='object'){this.has_objects_=true;}}}
+get size(){return this.values_.size;}
+get length(){return this.values_.size;}*[Symbol.iterator](){for(const value of this.values_){yield value;}}
+has(value){if(typeof value!=='object')return this.values_.has(value);const json=JSON.stringify(value);for(const x of this){if(typeof x!=='object')continue;if(json===JSON.stringify(x))return true;}
+return false;}
+equals(other){if(!(other instanceof GenericSet))return false;if(this.size!==other.size)return false;for(const value of this){if(!other.has(value))return false;}
+return true;}
+get hashKey(){if(this.has_objects_)return undefined;if(this.hash_key_!==undefined){return this.hash_key_;}
+let key='';for(const value of Array.from(this.values_.values()).sort()){key+=value;}
+this.hash_key_=key;return key;}
+serialize(serializer){const i=[...this].map(x=>serializer.getOrAllocateId(x));return(i.length===1)?i[0]:i;}
+asDictInto_(d){d.values=Array.from(this);}
+static deserialize(data,deserializer){if(!(data instanceof Array)){data=[data];}
+return new GenericSet(data.map(datum=>deserializer.getObject(datum)));}
+static fromDict(d){return new GenericSet(d.values);}
+clone(){return new GenericSet(this.values_);}
+canAddDiagnostic(otherDiagnostic){return otherDiagnostic instanceof GenericSet;}
+addDiagnostic(otherDiagnostic){const jsons=new Set();for(const value of this){if(typeof value!=='object')continue;jsons.add(stableStringify(value));}
+for(const value of otherDiagnostic){if(typeof value==='object'){if(jsons.has(stableStringify(value))){continue;}
+this.has_objects_=true;}
+this.values_.add(value);}}}
+tr.v.d.Diagnostic.register(GenericSet,{elementName:'tr-v-ui-generic-set-span'});return{GenericSet,};});'use strict';tr.exportTo('tr.v.d',function(){class EventRef{constructor(event){this.stableId=event.stableId;this.title=event.title;this.start=event.start;this.duration=event.duration;this.end=this.start+this.duration;this.guid=tr.b.GUID.allocateSimple();}}
+return{EventRef,};});'use strict';tr.exportTo('tr.v.d',function(){class RelatedEventSet extends tr.v.d.Diagnostic{constructor(opt_events){super();this.eventsByStableId_=new Map();this.canonicalUrl_=undefined;if(opt_events){if(opt_events instanceof tr.model.EventSet||opt_events instanceof Array){for(const event of opt_events){this.add(event);}}else{this.add(opt_events);}}}
+clone(){const clone=new tr.v.d.CollectedRelatedEventSet();clone.addDiagnostic(this);return clone;}
+equals(other){if(this.length!==other.length)return false;for(const event of this){if(!other.has(event))return false;}
+return true;}
+add(event){this.eventsByStableId_.set(event.stableId,event);}
+has(event){return this.eventsByStableId_.has(event.stableId);}
+get length(){return this.eventsByStableId_.size;}*[Symbol.iterator](){for(const event of this.eventsByStableId_.values()){yield event;}}
+get canonicalUrl(){return this.canonicalUrl_;}
+resolve(model,opt_required){for(const[stableId,value]of this.eventsByStableId_){if(!(value instanceof tr.v.d.EventRef))continue;const event=model.getEventByStableId(stableId);if(event instanceof tr.model.Event){this.eventsByStableId_.set(stableId,event);}else if(opt_required){throw new Error('Unable to find Event '+stableId);}}}
+serialize(serializer){return[...this].map(event=>[event.stableId,serializer.getOrAllocateId(event.title),event.start,event.duration,]);}
+asDictInto_(d){d.events=[];for(const event of this){d.events.push({stableId:event.stableId,title:event.title,start:tr.b.Unit.byName.timeStampInMs.truncate(event.start),duration:tr.b.Unit.byName.timeDurationInMs.truncate(event.duration),});}}
+static deserialize(data,deserializer){return new RelatedEventSet(data.map(event=>new tr.v.d.EventRef({stableId:event[0],title:deserializer.getObject(event[1]),start:event[2],duration:event[3],})));}
+static fromDict(d){return new RelatedEventSet(d.events.map(event=>new tr.v.d.EventRef(event)));}}
+tr.v.d.Diagnostic.register(RelatedEventSet,{elementName:'tr-v-ui-related-event-set-span'});return{RelatedEventSet,};});'use strict';tr.exportTo('tr.v.d',function(){class RelatedNameMap extends tr.v.d.Diagnostic{constructor(opt_info){super();this.map_=new Map();if(opt_info){for(const[key,name]of Object.entries(opt_info)){this.set(key,name);}}}
+clone(){const clone=new RelatedNameMap();clone.addDiagnostic(this);return clone;}
+equals(other){if(!(other instanceof RelatedNameMap))return false;const keys1=new Set(this.map_.keys());const keys2=new Set(other.map_.keys());if(!tr.b.setsEqual(keys1,keys2))return false;for(const[key,name]of this){if(name!==other.get(key))return false;}
+return true;}
+canAddDiagnostic(otherDiagnostic){return otherDiagnostic instanceof RelatedNameMap;}
+addDiagnostic(otherDiagnostic){for(const[key,name]of otherDiagnostic){const existing=this.get(key);if(existing===undefined){this.set(key,name);}else if(existing!==name){throw new Error('Histogram names differ: '+`"${existing}" != "${name}"`);}}}
+serialize(serializer){const keys=[...this.map_.keys()];keys.sort();const names=keys.map(k=>serializer.getOrAllocateId(this.get(k)));const keysId=serializer.getOrAllocateId(keys.map(k=>serializer.getOrAllocateId(k)));return[keysId,...names];}
+asDictInto_(d){d.names={};for(const[key,name]of this)d.names[key]=name;}
+set(key,name){this.map_.set(key,name);}
+get(key){return this.map_.get(key);}*[Symbol.iterator](){for(const pair of this.map_)yield pair;}*values(){for(const value of this.map_.values())yield value;}
+static fromEntries(entries){const names=new RelatedNameMap();for(const[key,name]of entries){names.set(key,name);}
+return names;}
+static deserialize(data,deserializer){const names=new RelatedNameMap();const keys=deserializer.getObject(data[0]);for(let i=0;i<keys.length;++i){names.set(deserializer.getObject(keys[i]),deserializer.getObject(data[i+1]));}
+return names;}
+static fromDict(d){return RelatedNameMap.fromEntries(Object.entries(d.names||{}));}}
+tr.v.d.Diagnostic.register(RelatedNameMap,{elementName:'tr-v-ui-related-name-map-span',});return{RelatedNameMap,};});'use strict';tr.exportTo('tr.v.d',function(){class Scalar extends tr.v.d.Diagnostic{constructor(value){super();if(!(value instanceof tr.b.Scalar)){throw new Error('expected Scalar');}
+this.value=value;}
+clone(){return new Scalar(this.value);}
+serialize(serializer){return this.value.asDict();}
+asDictInto_(d){d.value=this.value.asDict();}
+static deserialize(value,deserializer){return Scalar.fromDict({value});}
+static fromDict(d){return new Scalar(tr.b.Scalar.fromDict(d.value));}}
+tr.v.d.Diagnostic.register(Scalar,{elementName:'tr-v-ui-scalar-diagnostic-span'});return{Scalar,};});'use strict';tr.exportTo('tr.v.d',function(){class UnmergeableDiagnosticSet extends tr.v.d.Diagnostic{constructor(diagnostics){super();this._diagnostics=diagnostics;}
+clone(){const clone=new tr.v.d.UnmergeableDiagnosticSet();clone.addDiagnostic(this);return clone;}
+canAddDiagnostic(otherDiagnostic){return true;}
+addDiagnostic(otherDiagnostic){if(otherDiagnostic instanceof UnmergeableDiagnosticSet){for(const subOtherDiagnostic of otherDiagnostic){const clone=subOtherDiagnostic.clone();this.addDiagnostic(clone);}
+return;}
+for(let i=0;i<this._diagnostics.length;++i){if(this._diagnostics[i].canAddDiagnostic(otherDiagnostic)){this._diagnostics[i].addDiagnostic(otherDiagnostic);return;}}
+const clone=otherDiagnostic.clone();this._diagnostics.push(clone);}
+get length(){return this._diagnostics.length;}*[Symbol.iterator](){for(const diagnostic of this._diagnostics)yield diagnostic;}
+asDictInto_(d){d.diagnostics=this._diagnostics.map(d=>d.asDictOrReference());}
+static deserialize(data,deserializer){return new UnmergeableDiagnosticSet(d.map(i=>deserializer.getDiagnostic(i).diagnostic));}
+serialize(serializer){return this._diagnostics.map(d=>serializer.getOrAllocateDiagnosticId('',d));}
+static fromDict(d){return new UnmergeableDiagnosticSet(d.diagnostics.map(d=>((typeof d==='string')?new tr.v.d.DiagnosticRef(d):tr.v.d.Diagnostic.fromDict(d))));}}
+tr.v.d.Diagnostic.register(UnmergeableDiagnosticSet,{elementName:'tr-v-ui-unmergeable-diagnostic-set-span'});return{UnmergeableDiagnosticSet,};});'use strict';tr.exportTo('tr.v.d',function(){const RESERVED_INFOS={ANGLE_REVISIONS:{name:'angleRevisions',type:tr.v.d.GenericSet},ARCHITECTURES:{name:'architectures',type:tr.v.d.GenericSet},BENCHMARKS:{name:'benchmarks',type:tr.v.d.GenericSet},BENCHMARK_START:{name:'benchmarkStart',type:tr.v.d.DateRange},BENCHMARK_DESCRIPTIONS:{name:'benchmarkDescriptions',type:tr.v.d.GenericSet},BOTS:{name:'bots',type:tr.v.d.GenericSet},BUG_COMPONENTS:{name:'bugComponents',type:tr.v.d.GenericSet},BUILDS:{name:'builds',type:tr.v.d.GenericSet},CATAPULT_REVISIONS:{name:'catapultRevisions',type:tr.v.d.GenericSet},CHROMIUM_COMMIT_POSITIONS:{name:'chromiumCommitPositions',type:tr.v.d.GenericSet},CHROMIUM_REVISIONS:{name:'chromiumRevisions',type:tr.v.d.GenericSet},DESCRIPTION:{name:'description',type:tr.v.d.GenericSet},DEVICE_IDS:{name:'deviceIds',type:tr.v.d.GenericSet},DOCUMENTATION_URLS:{name:'documentationUrls',type:tr.v.d.GenericSet},FUCHSIA_GARNET_REVISIONS:{name:'fuchsiaGarnetRevisions',type:tr.v.d.GenericSet},FUCHSIA_PERIDOT_REVISIONS:{name:'fuchsiaPeridotRevisions',type:tr.v.d.GenericSet},FUCHSIA_TOPAZ_REVISIONS:{name:'fuchsiaTopazRevisions',type:tr.v.d.GenericSet},FUCHSIA_ZIRCON_REVISIONS:{name:'fuchsiaZirconRevisions',type:tr.v.d.GenericSet},GPUS:{name:'gpus',type:tr.v.d.GenericSet},IS_REFERENCE_BUILD:{name:'isReferenceBuild',type:tr.v.d.GenericSet},LABELS:{name:'labels',type:tr.v.d.GenericSet},LOG_URLS:{name:'logUrls',type:tr.v.d.GenericSet},MASTERS:{name:'masters',type:tr.v.d.GenericSet},MEMORY_AMOUNTS:{name:'memoryAmounts',type:tr.v.d.GenericSet},OS_NAMES:{name:'osNames',type:tr.v.d.GenericSet},OS_VERSIONS:{name:'osVersions',type:tr.v.d.GenericSet},OWNERS:{name:'owners',type:tr.v.d.GenericSet},POINT_ID:{name:'pointId',type:tr.v.d.GenericSet},PRODUCT_VERSIONS:{name:'productVersions',type:tr.v.d.GenericSet},REVISION_TIMESTAMPS:{name:'revisionTimestamps',type:tr.v.d.DateRange},SKIA_REVISIONS:{name:'skiaRevisions',type:tr.v.d.GenericSet},STATISTICS_NAMES:{name:'statisticsNames',type:tr.v.d.GenericSet},STORIES:{name:'stories',type:tr.v.d.GenericSet},STORYSET_REPEATS:{name:'storysetRepeats',type:tr.v.d.GenericSet},STORY_TAGS:{name:'storyTags',type:tr.v.d.GenericSet},SUMMARY_KEYS:{name:'summaryKeys',type:tr.v.d.GenericSet},TEST_PATH:{name:'testPath',type:tr.v.d.GenericSet},TRACE_START:{name:'traceStart',type:tr.v.d.DateRange},TRACE_URLS:{name:'traceUrls',type:tr.v.d.GenericSet},V8_COMMIT_POSITIONS:{name:'v8CommitPositions',type:tr.v.d.DateRange},V8_REVISIONS:{name:'v8Revisions',type:tr.v.d.GenericSet},WEBRTC_REVISIONS:{name:'webrtcRevisions',type:tr.v.d.GenericSet},WEBRTC_INTERNAL_REVISIONS:{name:'webrtcInternalRevisions',type:tr.v.d.GenericSet},};const RESERVED_NAMES={};const RESERVED_NAMES_TO_TYPES=new Map();for(const[codename,info]of Object.entries(RESERVED_INFOS)){RESERVED_NAMES[codename]=info.name;if(RESERVED_NAMES_TO_TYPES.has(info.name)){throw new Error(`Duplicate reserved name "${info.name}"`);}
+RESERVED_NAMES_TO_TYPES.set(info.name,info.type);}
+const RESERVED_NAMES_SET=new Set(Object.values(RESERVED_NAMES));return{RESERVED_INFOS,RESERVED_NAMES,RESERVED_NAMES_SET,RESERVED_NAMES_TO_TYPES,};});'use strict';tr.exportTo('tr.v.d',function(){class DiagnosticMap extends Map{constructor(opt_allowReservedNames){super();if(opt_allowReservedNames===undefined){opt_allowReservedNames=true;}
+this.allowReservedNames_=opt_allowReservedNames;}
+set(name,diagnostic){if(typeof(name)!=='string'){throw new Error(`name must be string, not ${name}`);}
+if(!(diagnostic instanceof tr.v.d.Diagnostic)&&!(diagnostic instanceof tr.v.d.DiagnosticRef)){throw new Error(`Must be instanceof Diagnostic: ${diagnostic}`);}
+if(!this.allowReservedNames_&&tr.v.d.RESERVED_NAMES_SET.has(name)&&!(diagnostic instanceof tr.v.d.UnmergeableDiagnosticSet)&&!(diagnostic instanceof tr.v.d.DiagnosticRef)){const type=tr.v.d.RESERVED_NAMES_TO_TYPES.get(name);if(type&&!(diagnostic instanceof type)){throw new Error(`Diagnostics named "${name}" must be ${type.name}, `+`not ${diagnostic.constructor.name}`);}}
+Map.prototype.set.call(this,name,diagnostic);}
+delete(name){if(name===undefined)throw new Error('missing name');Map.prototype.delete.call(this,name);}
+deserializeAdd(data,deserializer){for(const id of data){const{name,diagnostic}=deserializer.getDiagnostic(id);this.set(name,diagnostic);}}
+addDicts(dict){for(const[name,diagnosticDict]of Object.entries(dict)){if(name==='tagmap')continue;if(typeof diagnosticDict==='string'){this.set(name,new tr.v.d.DiagnosticRef(diagnosticDict));}else if(diagnosticDict.type!=='RelatedHistogramMap'&&diagnosticDict.type!=='RelatedHistogramBreakdown'&&diagnosticDict.type!=='TagMap'){this.set(name,tr.v.d.Diagnostic.fromDict(diagnosticDict));}}}
+resolveSharedDiagnostics(histograms,opt_required){for(const[name,value]of this){if(!(value instanceof tr.v.d.DiagnosticRef)){continue;}
+const guid=value.guid;const diagnostic=histograms.lookupDiagnostic(guid);if(diagnostic instanceof tr.v.d.Diagnostic){this.set(name,diagnostic);}else if(opt_required){throw new Error('Unable to find shared Diagnostic '+guid);}}}
+serialize(serializer){const data=[];for(const[name,diagnostic]of this){data.push(serializer.getOrAllocateDiagnosticId(name,diagnostic));}
+return data;}
+asDict(){const dict={};for(const[name,diagnostic]of this){dict[name]=diagnostic.asDictOrReference();}
+return dict;}
+static deserialize(data,deserializer){const diagnostics=new DiagnosticMap();diagnostics.deserializeAdd(data,deserializer);return diagnostics;}
+static fromDict(d){const diagnostics=new DiagnosticMap();diagnostics.addDicts(d);return diagnostics;}
+static fromObject(obj){const diagnostics=new DiagnosticMap();if(!(obj instanceof Map))obj=Object.entries(obj);for(const[name,diagnostic]of obj){if(!diagnostic)continue;diagnostics.set(name,diagnostic);}
+return diagnostics;}
+addDiagnostics(other){for(const[name,otherDiagnostic]of other){const myDiagnostic=this.get(name);if(myDiagnostic!==undefined&&myDiagnostic.canAddDiagnostic(otherDiagnostic)){myDiagnostic.addDiagnostic(otherDiagnostic);continue;}
+const clone=otherDiagnostic.clone();if(myDiagnostic===undefined){this.set(name,clone);continue;}
+this.set(name,new tr.v.d.UnmergeableDiagnosticSet([myDiagnostic,clone]));}}}
+return{DiagnosticMap};});'use strict';tr.exportTo('tr.v',function(){const MAX_DIAGNOSTIC_MAPS=16;const DEFAULT_SAMPLE_VALUES_PER_BIN=10;const DEFAULT_REBINNED_COUNT=40;const DEFAULT_BOUNDARIES_FOR_UNIT=new Map();const DEFAULT_ITERATION_FOR_BOOTSTRAP_RESAMPLING=500;const DELTA=String.fromCharCode(916);const Z_SCORE_NAME='z-score';const P_VALUE_NAME='p-value';const U_STATISTIC_NAME='U';function percentToString(percent,opt_force3){if(percent<0||percent>1){throw new Error('percent must be in [0,1]');}
+if(percent===0)return'000';if(percent===1)return'100';let str=percent.toString();if(str[1]!=='.'){throw new Error('Unexpected percent');}
+str=str+'0'.repeat(Math.max(4-str.length,0));if(str.length>4){if(opt_force3){str=str.slice(0,4);}else{str=str.slice(0,4)+'_'+str.slice(4);}}
+return'0'+str.slice(2);}
+function percentFromString(s){return parseFloat(s[0]+'.'+s.substr(1).replace(/_/g,''));}
+class HistogramBin{constructor(range){this.range=range;this.count=0;this.diagnosticMaps=[];}
+addSample(value){this.count+=1;}
+addDiagnosticMap(diagnostics){tr.b.math.Statistics.uniformlySampleStream(this.diagnosticMaps,this.count,diagnostics,MAX_DIAGNOSTIC_MAPS);}
+addBin(other){if(!this.range.equals(other.range)){throw new Error('Merging incompatible Histogram bins.');}
+tr.b.math.Statistics.mergeSampledStreams(this.diagnosticMaps,this.count,other.diagnosticMaps,other.count,MAX_DIAGNOSTIC_MAPS);this.count+=other.count;}
+deserialize(data,deserializer){if(!(data instanceof Array)){this.count=data;return;}
+this.count=data[0];for(const sample of data.slice(1)){if(!(sample instanceof Array))continue;this.diagnosticMaps.push(tr.v.d.DiagnosticMap.deserialize(sample.slice(1),deserializer));}}
+fromDict(dict){this.count=dict[0];if(dict.length>1){for(const map of dict[1]){this.diagnosticMaps.push(tr.v.d.DiagnosticMap.fromDict(map));}}}
+serialize(serializer){if(!this.diagnosticMaps.length){return this.count;}
+return[this.count,...this.diagnosticMaps.map(d=>[undefined,...d.serialize(serializer)])];}
+asDict(){if(!this.diagnosticMaps.length){return[this.count];}
+return[this.count,this.diagnosticMaps.map(d=>d.asDict())];}}
+const DEFAULT_SUMMARY_OPTIONS=new Map([['avg',true],['count',true],['geometricMean',false],['max',true],['min',true],['nans',false],['std',true],['sum',true],]);class Histogram{constructor(name,unit,opt_binBoundaries){if(!(unit instanceof tr.b.Unit)){throw new Error('unit must be a Unit: '+unit);}
+let binBoundaries=opt_binBoundaries;if(!binBoundaries){const baseUnit=unit.baseUnit?unit.baseUnit:unit;binBoundaries=DEFAULT_BOUNDARIES_FOR_UNIT.get(baseUnit.unitName);}
+this.binBoundariesDict_=binBoundaries.asDict();this.allBins=binBoundaries.bins.slice();this.description='';const allowReservedNames=false;this.diagnostics_=new tr.v.d.DiagnosticMap(allowReservedNames);this.maxNumSampleValues_=this.defaultMaxNumSampleValues_;this.name_=name;this.nanDiagnosticMaps=[];this.numNans=0;this.running_=undefined;this.sampleValues_=[];this.sampleMeans_=[];this.summaryOptions=new Map(DEFAULT_SUMMARY_OPTIONS);this.summaryOptions.set('percentile',[]);this.summaryOptions.set('iprs',[]);this.summaryOptions.set('ci',[]);this.unit=unit;}
+static create(name,unit,samples,opt_options){const options=opt_options||{};const hist=new Histogram(name,unit,options.binBoundaries);if(options.description)hist.description=options.description;if(options.summaryOptions){let summaryOptions=options.summaryOptions;if(!(summaryOptions instanceof Map)){summaryOptions=Object.entries(summaryOptions);}
+for(const[name,value]of summaryOptions){hist.summaryOptions.set(name,value);}}
+if(options.diagnostics!==undefined){let diagnostics=options.diagnostics;if(!(diagnostics instanceof Map)){diagnostics=Object.entries(diagnostics);}
+for(const[name,diagnostic]of diagnostics){if(!diagnostic)continue;hist.diagnostics.set(name,diagnostic);}}
+if(!(samples instanceof Array))samples=[samples];for(const sample of samples){if(typeof sample==='object'){hist.addSample(sample.value,sample.diagnostics);}else{hist.addSample(sample);}}
+return hist;}
+get diagnostics(){return this.diagnostics_;}
+get running(){return this.running_;}
+get maxNumSampleValues(){return this.maxNumSampleValues_;}
+set maxNumSampleValues(n){this.maxNumSampleValues_=n;tr.b.math.Statistics.uniformlySampleArray(this.sampleValues_,this.maxNumSampleValues_);}
+get name(){return this.name_;}
+deserializeStatistics_(){const statisticsNames=this.diagnostics.get(tr.v.d.RESERVED_NAMES.STATISTICS_NAMES);if(!statisticsNames)return;for(const statName of statisticsNames){if(statName.startsWith('pct_')){const percent=percentFromString(statName.substr(4));this.summaryOptions.get('percentile').push(percent);}else if(statName.startsWith('ipr_')){const lower=percentFromString(statName.substr(4,3));const upper=percentFromString(statName.substr(8));this.summaryOptions.get('iprs').push(tr.b.math.Range.fromExplicitRange(lower,upper));}else if(statName.startsWith('ci_')){const percent=percentFromString(statName.replace('_lower','').replace('_upper','').substr(3));if(!this.summaryOptions.get('ci').includes(percent)){this.summaryOptions.get('ci').push(percent);}}}
+for(const statName of this.summaryOptions.keys()){if(statName==='percentile'||statName==='iprs'||statName==='ci'){continue;}
+this.summaryOptions.set(statName,statisticsNames.has(statName));}}
+deserializeBin_(i,bin,deserializer){this.allBins[i]=new HistogramBin(this.allBins[i].range);this.allBins[i].deserialize(bin,deserializer);if(!(bin instanceof Array))return;for(let sample of bin.slice(1)){if(sample instanceof Array){sample=sample[0];}
+this.sampleValues_.push(sample);}}
+deserializeBins_(bins,deserializer){if(bins instanceof Array){for(let i=0;i<bins.length;++i){this.deserializeBin_(i,bins[i],deserializer);}}else{for(const[i,binData]of Object.entries(bins)){this.deserializeBin_(i,binData,deserializer);}}}
+static deserialize(data,deserializer){const[name,unit,boundaries,diagnostics,running,bins,nanBin]=data;const hist=new Histogram(deserializer.getObject(name),tr.b.Unit.fromJSON(unit),HistogramBinBoundaries.fromDict(deserializer.getObject(boundaries)));hist.diagnostics.deserializeAdd(diagnostics,deserializer);const description=hist.diagnostics.get(tr.v.d.RESERVED_NAMES.DESCRIPTION);if(description&&description.length){hist.description=[...description][0];}
+hist.deserializeStatistics_();if(running){hist.running_=tr.b.math.RunningStatistics.fromDict(running);}
+if(bins){hist.deserializeBins_(bins,deserializer);}
+if(nanBin){if(!(nanBin instanceof Array)){hist.numNans=nanBin;}else{hist.numNans=nanBin[0];for(const sample of nanBin.slice(1)){if(!(sample instanceof Array))continue;hist.nanDiagnosticMaps.push(tr.v.d.DiagnosticMap.deserialize(sample.slice(1),deserializer));}}}
+return hist;}
+static fromDict(dict){const hist=new Histogram(dict.name,tr.b.Unit.fromJSON(dict.unit),HistogramBinBoundaries.fromDict(dict.binBoundaries));if(dict.description){hist.description=dict.description;}
+if(dict.diagnostics){hist.diagnostics.addDicts(dict.diagnostics);}
+if(dict.allBins){if(dict.allBins.length!==undefined){for(let i=0;i<dict.allBins.length;++i){hist.allBins[i]=new HistogramBin(hist.allBins[i].range);hist.allBins[i].fromDict(dict.allBins[i]);}}else{for(const[i,binDict]of Object.entries(dict.allBins)){if(i>=hist.allBins.length||i<0){throw new Error('Invalid index "'+i+'" out of bounds of [0..'+hist.allBins.length+')');}
+hist.allBins[i]=new HistogramBin(hist.allBins[i].range);hist.allBins[i].fromDict(binDict);}}}
+if(dict.running){hist.running_=tr.b.math.RunningStatistics.fromDict(dict.running);}
+if(dict.summaryOptions){if(dict.summaryOptions.iprs){dict.summaryOptions.iprs=dict.summaryOptions.iprs.map(r=>tr.b.math.Range.fromExplicitRange(r[0],r[1]));}
+hist.customizeSummaryOptions(dict.summaryOptions);}
+if(dict.maxNumSampleValues!==undefined){hist.maxNumSampleValues=dict.maxNumSampleValues;}
+if(dict.sampleValues){hist.sampleValues_=dict.sampleValues;}
+if(dict.numNans){hist.numNans=dict.numNans;}
+if(dict.nanDiagnostics){for(const map of dict.nanDiagnostics){hist.nanDiagnosticMaps.push(tr.v.d.DiagnosticMap.fromDict(map));}}
+return hist;}
+get numValues(){return this.running_?this.running_.count:0;}
+get average(){return this.running_?this.running_.mean:undefined;}
+get standardDeviation(){return this.running_?this.running_.stddev:undefined;}
+get geometricMean(){return this.running_?this.running_.geometricMean:0;}
+get sum(){return this.running_?this.running_.sum:0;}
+get min(){return this.running_?this.running_.min:Infinity;}
+get max(){return this.running_?this.running_.max:-Infinity;}
+getDifferenceSignificance(other,opt_alpha){if(this.unit!==other.unit){throw new Error('Cannot compare Histograms with different units');}
+if(this.unit.improvementDirection===tr.b.ImprovementDirection.DONT_CARE){return tr.b.math.Statistics.Significance.DONT_CARE;}
+if(!(other instanceof Histogram)){throw new Error('Unable to compute a p-value');}
+const testResult=tr.b.math.Statistics.mwu(this.sampleValues,other.sampleValues,opt_alpha);return testResult.significance;}
+getApproximatePercentile(percent){if(percent<0||percent>1){throw new Error('percent must be in [0,1]');}
+if(this.numValues===0)return undefined;if(this.allBins.length===1){const sortedSampleValues=this.sampleValues.slice().sort((x,y)=>x-y);return sortedSampleValues[Math.floor((sortedSampleValues.length-1)*percent)];}
+let valuesToSkip=Math.floor((this.numValues-1)*percent);for(const bin of this.allBins){valuesToSkip-=bin.count;if(valuesToSkip>=0)continue;if(bin.range.min===-Number.MAX_VALUE){return bin.range.max;}
+if(bin.range.max===Number.MAX_VALUE){return bin.range.min;}
+return bin.range.center;}
+return this.allBins[this.allBins.length-1].range.min;}
+getBinIndexForValue(value){const i=tr.b.findFirstTrueIndexInSortedArray(this.allBins,b=>value<b.range.max);if(0<=i&&i<this.allBins.length)return i;return this.allBins.length-1;}
+getBinForValue(value){return this.allBins[this.getBinIndexForValue(value)];}
+addSample(value,opt_diagnostics){if(opt_diagnostics){if(!(opt_diagnostics instanceof tr.v.d.DiagnosticMap)){opt_diagnostics=tr.v.d.DiagnosticMap.fromObject(opt_diagnostics);}
+for(const[name,diag]of opt_diagnostics){if(diag instanceof tr.v.d.Breakdown){diag.truncate(this.unit);}}}
+if(typeof(value)!=='number'||isNaN(value)){this.numNans++;if(opt_diagnostics){tr.b.math.Statistics.uniformlySampleStream(this.nanDiagnosticMaps,this.numNans,opt_diagnostics,MAX_DIAGNOSTIC_MAPS);}}else{if(this.running_===undefined){this.running_=new tr.b.math.RunningStatistics();}
+this.sampleMeans_=[];this.running_.add(value);value=this.unit.truncate(value);const binIndex=this.getBinIndexForValue(value);let bin=this.allBins[binIndex];if(bin.count===0){bin=new HistogramBin(bin.range);this.allBins[binIndex]=bin;}
+bin.addSample(value);if(opt_diagnostics){bin.addDiagnosticMap(opt_diagnostics);}}
+tr.b.math.Statistics.uniformlySampleStream(this.sampleValues_,this.numValues+this.numNans,value,this.maxNumSampleValues);}
+resampleMean_(percent){const filteredSamples=this.sampleValues_.filter(value=>typeof(value)==='number'&&!isNaN(value));const sampleCount=filteredSamples.length;if(sampleCount===0||percent<=0.0||percent>=1.0){return[undefined,undefined];}else if(sampleCount===1){return[filteredSamples[0],filteredSamples[0]];}
+const iterations=DEFAULT_ITERATION_FOR_BOOTSTRAP_RESAMPLING;if(this.sampleMeans_.length!==iterations){this.sampleMeans_=[];for(let i=0;i<iterations;i++){let tempSum=0.0;for(let j=0;j<sampleCount;j++){tempSum+=filteredSamples[Math.floor(Math.random()*sampleCount)];}
+this.sampleMeans_.push(tempSum/sampleCount);}
+this.sampleMeans_.sort((a,b)=>a-b);}
+return[this.sampleMeans_[Math.floor((iterations-1)*(0.5-percent/2))],this.sampleMeans_[Math.ceil((iterations-1)*(0.5+percent/2))],];}
+sampleValuesInto(samples){for(const sampleValue of this.sampleValues){samples.push(sampleValue);}}
+canAddHistogram(other){if(this.unit!==other.unit){return false;}
+if(this.binBoundariesDict_===other.binBoundariesDict_){return true;}
+if(!this.binBoundariesDict_||!other.binBoundariesDict_){return true;}
+if(this.binBoundariesDict_.length!==other.binBoundariesDict_.length){return false;}
+for(let i=0;i<this.binBoundariesDict_.length;++i){const slice=this.binBoundariesDict_[i];const otherSlice=other.binBoundariesDict_[i];if(slice instanceof Array){if(!(otherSlice instanceof Array)){return false;}
+if(slice[0]!==otherSlice[0]||!tr.b.math.approximately(slice[1],otherSlice[1])||slice[2]!==otherSlice[2]){return false;}}else{if(otherSlice instanceof Array){return false;}
+if(!tr.b.math.approximately(slice,otherSlice)){return false;}}}
+return true;}
+addHistogram(other){if(!this.canAddHistogram(other)){throw new Error('Merging incompatible Histograms');}
+if(!!this.binBoundariesDict_===!!other.binBoundariesDict_){for(let i=0;i<this.allBins.length;++i){let bin=this.allBins[i];if(bin.count===0){bin=new HistogramBin(bin.range);this.allBins[i]=bin;}
+bin.addBin(other.allBins[i]);}}else{const[multiBin,singleBin]=this.binBoundariesDict_?[this,other]:[other,this];for(const value of singleBin.sampleValues){if(typeof(value)!=='number'||isNaN(value)){continue;}
+const binIndex=multiBin.getBinIndexForValue(value);let bin=multiBin.allBins[binIndex];if(bin.count===0){bin=new HistogramBin(bin.range);multiBin.allBins[binIndex]=bin;}
+bin.addSample(value);}}
+tr.b.math.Statistics.mergeSampledStreams(this.nanDiagnosticMaps,this.numNans,other.nanDiagnosticMaps,other.numNans,MAX_DIAGNOSTIC_MAPS);tr.b.math.Statistics.mergeSampledStreams(this.sampleValues,this.numValues+this.numNans,other.sampleValues,other.numValues+other.numNans,(this.maxNumSampleValues+other.maxNumSampleValues)/2);this.numNans+=other.numNans;if(other.running_!==undefined){if(this.running_===undefined){this.running_=new tr.b.math.RunningStatistics();}
+this.running_=this.running_.merge(other.running_);}
+this.sampleMeans_=[];this.diagnostics.addDiagnostics(other.diagnostics);for(const[stat,option]of other.summaryOptions){if(stat==='percentile'){const percentiles=this.summaryOptions.get(stat);for(const percent of option){if(!percentiles.includes(percent))percentiles.push(percent);}}else if(stat==='iprs'){const thisIprs=this.summaryOptions.get(stat);for(const ipr of option){let found=false;for(const thisIpr of thisIprs){found=ipr.equals(thisIpr);if(found)break;}
+if(!found)thisIprs.push(ipr);}}else if(stat==='ci'){const CIs=this.summaryOptions.get(stat);for(const CI of option){if(!CIs.includes(CI))CIs.push(CI);}}else if(option&&!this.summaryOptions.get(stat)){this.summaryOptions.set(stat,true);}}}
+customizeSummaryOptions(summaryOptions){for(const[key,value]of Object.entries(summaryOptions)){this.summaryOptions.set(key,value);}}
+getStatisticScalar(statName,opt_referenceHistogram,opt_mwu){if(statName==='avg'){if(typeof(this.average)!=='number')return undefined;return new tr.b.Scalar(this.unit,this.average);}
+if(statName==='std'){if(typeof(this.standardDeviation)!=='number')return undefined;return new tr.b.Scalar(this.unit,this.standardDeviation);}
+if(statName==='geometricMean'){if(typeof(this.geometricMean)!=='number')return undefined;return new tr.b.Scalar(this.unit,this.geometricMean);}
+if(statName==='min'||statName==='max'||statName==='sum'){if(this.running_===undefined){this.running_=new tr.b.math.RunningStatistics();}
+if(typeof(this.running_[statName])!=='number')return undefined;return new tr.b.Scalar(this.unit,this.running_[statName]);}
+if(statName==='nans'){return new tr.b.Scalar(tr.b.Unit.byName.count_smallerIsBetter,this.numNans);}
+if(statName==='count'){return new tr.b.Scalar(tr.b.Unit.byName.count_smallerIsBetter,this.numValues);}
+if(statName.substr(0,4)==='pct_'){if(this.numValues===0)return undefined;const percent=percentFromString(statName.substr(4));const percentile=this.getApproximatePercentile(percent);if(typeof(percentile)!=='number')return undefined;return new tr.b.Scalar(this.unit,percentile);}
+if(statName.substr(0,3)==='ci_'){const percent=percentFromString(statName.substr(3,3));const[lowCI,highCI]=this.resampleMean_(percent);if(statName.substr(7)==='lower'){if(typeof(lowCI)!=='number')return undefined;return new tr.b.Scalar(this.unit,lowCI);}else if(statName.substr(7)==='upper'){if(typeof(highCI)!=='number')return undefined;return new tr.b.Scalar(this.unit,highCI);}
+if(typeof(highCI)!=='number'||typeof(lowCI)!=='number'){return undefined;}
+return new tr.b.Scalar(this.unit,highCI-lowCI);}
+if(statName.substr(0,4)==='ipr_'){let lower=percentFromString(statName.substr(4,3));let upper=percentFromString(statName.substr(8));if(lower>=upper){throw new Error('Invalid inter-percentile range: '+statName);}
+lower=this.getApproximatePercentile(lower);upper=this.getApproximatePercentile(upper);const ipr=upper-lower;if(typeof(ipr)!=='number')return undefined;return new tr.b.Scalar(this.unit,ipr);}
+if(!this.canCompare(opt_referenceHistogram)){throw new Error('Cannot compute '+statName+' when histograms are not comparable');}
+const suffix=tr.b.Unit.nameSuffixForImprovementDirection(this.unit.improvementDirection);const deltaIndex=statName.indexOf(DELTA);if(deltaIndex>=0){const baseStatName=statName.substr(deltaIndex+1);const thisStat=this.getStatisticScalar(baseStatName);const otherStat=opt_referenceHistogram.getStatisticScalar(baseStatName);const deltaValue=thisStat.value-otherStat.value;if(statName[0]==='%'){return new tr.b.Scalar(tr.b.Unit.byName['normalizedPercentageDelta'+suffix],deltaValue/otherStat.value);}
+return new tr.b.Scalar(thisStat.unit.correspondingDeltaUnit,deltaValue);}
+if(statName===Z_SCORE_NAME){return new tr.b.Scalar(tr.b.Unit.byName['sigmaDelta'+suffix],(this.average-opt_referenceHistogram.average)/opt_referenceHistogram.standardDeviation);}
+const mwu=opt_mwu||tr.b.math.Statistics.mwu(this.sampleValues,opt_referenceHistogram.sampleValues);if(statName===P_VALUE_NAME){return new tr.b.Scalar(tr.b.Unit.byName.unitlessNumber,mwu.p);}
+if(statName===U_STATISTIC_NAME){return new tr.b.Scalar(tr.b.Unit.byName.unitlessNumber,mwu.U);}
+throw new Error('Unrecognized statistic name: '+statName);}
+get statisticsNames(){const statisticsNames=new Set();for(const[statName,option]of this.summaryOptions){if(statName==='percentile'){for(const pctile of option){statisticsNames.add('pct_'+tr.v.percentToString(pctile));}}else if(statName==='iprs'){for(const range of option){statisticsNames.add('ipr_'+tr.v.percentToString(range.min,true)+'_'+tr.v.percentToString(range.max,true));}}else if(statName==='ci'){for(const CIpctile of option){const CIpctStr=tr.v.percentToString(CIpctile);statisticsNames.add('ci_'+CIpctStr+'_lower');statisticsNames.add('ci_'+CIpctStr+'_upper');statisticsNames.add('ci_'+CIpctStr);}}else if(option){statisticsNames.add(statName);}}
+return statisticsNames;}
+canCompare(other){return other instanceof Histogram&&this.unit===other.unit&&this.numValues>0&&other.numValues>0;}
+getAvailableStatisticName(statName,opt_referenceHist){if(this.canCompare(opt_referenceHist))return statName;if(statName===Z_SCORE_NAME||statName===P_VALUE_NAME||statName===U_STATISTIC_NAME){return'avg';}
+const deltaIndex=statName.indexOf(DELTA);if(deltaIndex<0)return statName;return statName.substr(deltaIndex+1);}
+static getDeltaStatisticsNames(statNames){const deltaNames=[];for(const statName of statNames){deltaNames.push(`${DELTA}${statName}`);deltaNames.push(`%${DELTA}${statName}`);}
+return deltaNames.concat([Z_SCORE_NAME,P_VALUE_NAME,U_STATISTIC_NAME]);}
+get statisticsScalars(){const results=new Map();for(const statName of this.statisticsNames){const scalar=this.getStatisticScalar(statName);if(scalar===undefined)continue;results.set(statName,scalar);}
+return results;}
+get sampleValues(){return this.sampleValues_;}
+clone(){const binBoundaries=HistogramBinBoundaries.fromDict(this.binBoundariesDict_);const hist=new Histogram(this.name,this.unit,binBoundaries);for(const[stat,option]of this.summaryOptions){if(stat==='percentile'||stat==='iprs'||stat==='ci'){hist.summaryOptions.set(stat,Array.from(option));}else{hist.summaryOptions.set(stat,option);}}
+hist.addHistogram(this);return hist;}
+rebin(newBoundaries){const rebinned=new tr.v.Histogram(this.name,this.unit,newBoundaries);rebinned.description=this.description;for(const sample of this.sampleValues){rebinned.addSample(sample);}
+rebinned.running_=this.running_;for(const[name,diagnostic]of this.diagnostics){rebinned.diagnostics.set(name,diagnostic);}
+for(const[stat,option]of this.summaryOptions){if(stat==='percentile'||stat==='ci'){rebinned.summaryOptions.set(stat,Array.from(option));}else{rebinned.summaryOptions.set(stat,option);}}
+return rebinned;}
+serialize(serializer){let nanBin=this.numNans;if(this.nanDiagnosticMaps.length){nanBin=[nanBin,...this.nanDiagnosticMaps.map(dm=>[undefined,...dm.serialize(serializer)])];}
+this.diagnostics.set(tr.v.d.RESERVED_NAMES.STATISTICS_NAMES,new tr.v.d.GenericSet([...this.statisticsNames].sort()));this.diagnostics.set(tr.v.d.RESERVED_NAMES.DESCRIPTION,new tr.v.d.GenericSet([this.description].sort()));return[serializer.getOrAllocateId(this.name),this.unit.asJSON2(),serializer.getOrAllocateId(this.binBoundariesDict_),this.diagnostics.serialize(serializer),this.running_?this.running_.asDict():0,this.serializeBins_(serializer),nanBin,];}
+asDict(){const dict={};dict.name=this.name;dict.unit=this.unit.asJSON();if(this.binBoundariesDict_!==undefined){dict.binBoundaries=this.binBoundariesDict_;}
+if(this.description){dict.description=this.description;}
+if(this.diagnostics.size){dict.diagnostics=this.diagnostics.asDict();}
+if(this.maxNumSampleValues!==this.defaultMaxNumSampleValues_){dict.maxNumSampleValues=this.maxNumSampleValues;}
+if(this.numNans){dict.numNans=this.numNans;}
+if(this.nanDiagnosticMaps.length){dict.nanDiagnostics=this.nanDiagnosticMaps.map(dm=>dm.asDict());}
+if(this.numValues){dict.sampleValues=this.sampleValues.slice();this.running.truncate(this.unit);dict.running=this.running_.asDict();dict.allBins=this.allBinsAsDict_();}
+const summaryOptions={};let anyOverriddenSummaryOptions=false;for(const[name,value]of this.summaryOptions){let option;if(name==='percentile'){if(value.length===0)continue;option=Array.from(value);}else if(name==='iprs'){if(value.length===0)continue;option=value.map(r=>[r.min,r.max]);}else if(name==='ci'){if(value.length===0)continue;option=Array.from(value);}else if(value===DEFAULT_SUMMARY_OPTIONS.get(name)){continue;}else{option=value;}
+summaryOptions[name]=option;anyOverriddenSummaryOptions=true;}
+if(anyOverriddenSummaryOptions){dict.summaryOptions=summaryOptions;}
+return dict;}
+serializeBins_(serializer){const numBins=this.allBins.length;let emptyBins=0;for(let i=0;i<numBins;++i){if(this.allBins[i].count===0){++emptyBins;}}
+if(emptyBins===numBins){return 0;}
+if(emptyBins>(numBins/2)){const allBinsDict={};for(let i=0;i<numBins;++i){const bin=this.allBins[i];if(bin.count>0){allBinsDict[i]=bin.serialize(serializer);}}
+return allBinsDict;}
+const allBinsArray=[];for(let i=0;i<numBins;++i){allBinsArray.push(this.allBins[i].serialize(serializer));}
+return allBinsArray;}
+allBinsAsDict_(){const numBins=this.allBins.length;let emptyBins=0;for(let i=0;i<numBins;++i){if(this.allBins[i].count===0){++emptyBins;}}
+if(emptyBins===numBins){return undefined;}
+if(emptyBins>(numBins/2)){const allBinsDict={};for(let i=0;i<numBins;++i){const bin=this.allBins[i];if(bin.count>0){allBinsDict[i]=bin.asDict();}}
+return allBinsDict;}
+const allBinsArray=[];for(let i=0;i<numBins;++i){allBinsArray.push(this.allBins[i].asDict());}
+return allBinsArray;}
+get defaultMaxNumSampleValues_(){return DEFAULT_SAMPLE_VALUES_PER_BIN*Math.max(this.allBins.length,DEFAULT_REBINNED_COUNT);}}
+Histogram.AVERAGE_ONLY_SUMMARY_OPTIONS={count:false,max:false,min:false,std:false,sum:false,};const HISTOGRAM_BIN_BOUNDARIES_CACHE=new Map();class HistogramBinBoundaries{static createLinear(min,max,numBins){return new HistogramBinBoundaries(min).addLinearBins(max,numBins);}
+static createExponential(min,max,numBins){return new HistogramBinBoundaries(min).addExponentialBins(max,numBins);}
+static createWithBoundaries(binBoundaries){const builder=new HistogramBinBoundaries(binBoundaries[0]);for(const boundary of binBoundaries.slice(1)){builder.addBinBoundary(boundary);}
+return builder;}
+constructor(minBinBoundary){this.builder_=[minBinBoundary];this.range_=new tr.b.math.Range();this.range_.addValue(minBinBoundary);this.binRanges_=undefined;this.bins_=undefined;}
+get range(){return this.range_;}
+asDict(){if(this.builder_.length===1&&this.builder_[0]===Number.MAX_VALUE){return undefined;}
+return this.builder_;}
+pushBuilderSlice_(slice){this.builder_.push(slice);this.builder_=this.builder_.slice();}
+static fromDict(dict){if(dict===undefined){return HistogramBinBoundaries.SINGULAR;}
+const cacheKey=JSON.stringify(dict);if(HISTOGRAM_BIN_BOUNDARIES_CACHE.has(cacheKey)){return HISTOGRAM_BIN_BOUNDARIES_CACHE.get(cacheKey);}
+const binBoundaries=new HistogramBinBoundaries(dict[0]);for(const slice of dict.slice(1)){if(!(slice instanceof Array)){binBoundaries.addBinBoundary(slice);continue;}
+switch(slice[0]){case HistogramBinBoundaries.SLICE_TYPE.LINEAR:binBoundaries.addLinearBins(slice[1],slice[2]);break;case HistogramBinBoundaries.SLICE_TYPE.EXPONENTIAL:binBoundaries.addExponentialBins(slice[1],slice[2]);break;default:throw new Error('Unrecognized HistogramBinBoundaries slice type');}}
+HISTOGRAM_BIN_BOUNDARIES_CACHE.set(cacheKey,binBoundaries);return binBoundaries;}
+get bins(){if(this.bins_===undefined){this.buildBins_();}
+return this.bins_;}
+buildBins_(){this.bins_=this.binRanges.map(r=>new HistogramBin(r));}
+get binRanges(){if(this.binRanges_===undefined){this.buildBinRanges_();}
+return this.binRanges_;}
+buildBinRanges_(){if(typeof this.builder_[0]!=='number'){throw new Error('Invalid start of builder_');}
+this.binRanges_=[];let prevBoundary=this.builder_[0];if(prevBoundary>-Number.MAX_VALUE){this.binRanges_.push(tr.b.math.Range.fromExplicitRange(-Number.MAX_VALUE,prevBoundary));}
+for(const slice of this.builder_.slice(1)){if(!(slice instanceof Array)){this.binRanges_.push(tr.b.math.Range.fromExplicitRange(prevBoundary,slice));prevBoundary=slice;continue;}
+const nextMaxBinBoundary=slice[1];const binCount=slice[2];const sliceMinBinBoundary=prevBoundary;switch(slice[0]){case HistogramBinBoundaries.SLICE_TYPE.LINEAR:{const binWidth=(nextMaxBinBoundary-prevBoundary)/binCount;for(let i=1;i<binCount;i++){const boundary=sliceMinBinBoundary+i*binWidth;this.binRanges_.push(tr.b.math.Range.fromExplicitRange(prevBoundary,boundary));prevBoundary=boundary;}
+break;}
+case HistogramBinBoundaries.SLICE_TYPE.EXPONENTIAL:{const binExponentWidth=Math.log(nextMaxBinBoundary/prevBoundary)/binCount;for(let i=1;i<binCount;i++){const boundary=sliceMinBinBoundary*Math.exp(i*binExponentWidth);this.binRanges_.push(tr.b.math.Range.fromExplicitRange(prevBoundary,boundary));prevBoundary=boundary;}
+break;}
+default:throw new Error('Unrecognized HistogramBinBoundaries slice type');}
+this.binRanges_.push(tr.b.math.Range.fromExplicitRange(prevBoundary,nextMaxBinBoundary));prevBoundary=nextMaxBinBoundary;}
+if(prevBoundary<Number.MAX_VALUE){this.binRanges_.push(tr.b.math.Range.fromExplicitRange(prevBoundary,Number.MAX_VALUE));}}
+addBinBoundary(nextMaxBinBoundary){if(nextMaxBinBoundary<=this.range.max){throw new Error('The added max bin boundary must be larger than '+'the current max boundary');}
+this.binRanges_=undefined;this.bins_=undefined;this.pushBuilderSlice_(nextMaxBinBoundary);this.range.addValue(nextMaxBinBoundary);return this;}
+addLinearBins(nextMaxBinBoundary,binCount){if(binCount<=0){throw new Error('Bin count must be positive');}
+if(nextMaxBinBoundary<=this.range.max){throw new Error('The new max bin boundary must be greater than '+'the previous max bin boundary');}
+this.binRanges_=undefined;this.bins_=undefined;this.pushBuilderSlice_([HistogramBinBoundaries.SLICE_TYPE.LINEAR,nextMaxBinBoundary,binCount]);this.range.addValue(nextMaxBinBoundary);return this;}
+addExponentialBins(nextMaxBinBoundary,binCount){if(binCount<=0){throw new Error('Bin count must be positive');}
+if(this.range.max<=0){throw new Error('Current max bin boundary must be positive');}
+if(this.range.max>=nextMaxBinBoundary){throw new Error('The last added max boundary must be greater than '+'the current max boundary boundary');}
+this.binRanges_=undefined;this.bins_=undefined;this.pushBuilderSlice_([HistogramBinBoundaries.SLICE_TYPE.EXPONENTIAL,nextMaxBinBoundary,binCount]);this.range.addValue(nextMaxBinBoundary);return this;}}
+HistogramBinBoundaries.SLICE_TYPE={LINEAR:0,EXPONENTIAL:1,};HistogramBinBoundaries.SINGULAR=new HistogramBinBoundaries(Number.MAX_VALUE);DEFAULT_BOUNDARIES_FOR_UNIT.set(tr.b.Unit.byName.timeDurationInMs.unitName,HistogramBinBoundaries.createExponential(1e-3,1e6,1e2));DEFAULT_BOUNDARIES_FOR_UNIT.set(tr.b.Unit.byName.timeInMsAutoFormat.unitName,new HistogramBinBoundaries(0).addBinBoundary(1).addExponentialBins(1e3,3).addBinBoundary(tr.b.convertUnit(2,tr.b.UnitScale.TIME.SEC,tr.b.UnitScale.TIME.MILLI_SEC)).addBinBoundary(tr.b.convertUnit(5,tr.b.UnitScale.TIME.SEC,tr.b.UnitScale.TIME.MILLI_SEC)).addBinBoundary(tr.b.convertUnit(10,tr.b.UnitScale.TIME.SEC,tr.b.UnitScale.TIME.MILLI_SEC)).addBinBoundary(tr.b.convertUnit(30,tr.b.UnitScale.TIME.SEC,tr.b.UnitScale.TIME.MILLI_SEC)).addBinBoundary(tr.b.convertUnit(tr.b.UnitScale.TIME.MINUTE.value,tr.b.UnitScale.TIME.SEC,tr.b.UnitScale.TIME.MILLI_SEC)).addBinBoundary(2*tr.b.convertUnit(tr.b.UnitScale.TIME.MINUTE.value,tr.b.UnitScale.TIME.SEC,tr.b.UnitScale.TIME.MILLI_SEC)).addBinBoundary(5*tr.b.convertUnit(tr.b.UnitScale.TIME.MINUTE.value,tr.b.UnitScale.TIME.SEC,tr.b.UnitScale.TIME.MILLI_SEC)).addBinBoundary(10*tr.b.convertUnit(tr.b.UnitScale.TIME.MINUTE.value,tr.b.UnitScale.TIME.SEC,tr.b.UnitScale.TIME.MILLI_SEC)).addBinBoundary(30*tr.b.convertUnit(tr.b.UnitScale.TIME.MINUTE.value,tr.b.UnitScale.TIME.SEC,tr.b.UnitScale.TIME.MILLI_SEC)).addBinBoundary(tr.b.convertUnit(tr.b.UnitScale.TIME.HOUR.value,tr.b.UnitScale.TIME.SEC,tr.b.UnitScale.TIME.MILLI_SEC)).addBinBoundary(2*tr.b.convertUnit(tr.b.UnitScale.TIME.HOUR.value,tr.b.UnitScale.TIME.SEC,tr.b.UnitScale.TIME.MILLI_SEC)).addBinBoundary(6*tr.b.convertUnit(tr.b.UnitScale.TIME.HOUR.value,tr.b.UnitScale.TIME.SEC,tr.b.UnitScale.TIME.MILLI_SEC)).addBinBoundary(12*tr.b.convertUnit(tr.b.UnitScale.TIME.HOUR.value,tr.b.UnitScale.TIME.SEC,tr.b.UnitScale.TIME.MILLI_SEC)).addBinBoundary(tr.b.convertUnit(tr.b.UnitScale.TIME.DAY.value,tr.b.UnitScale.TIME.SEC,tr.b.UnitScale.TIME.MILLI_SEC)).addBinBoundary(tr.b.convertUnit(tr.b.UnitScale.TIME.WEEK.value,tr.b.UnitScale.TIME.SEC,tr.b.UnitScale.TIME.MILLI_SEC)).addBinBoundary(tr.b.convertUnit(tr.b.UnitScale.TIME.MONTH.value,tr.b.UnitScale.TIME.SEC,tr.b.UnitScale.TIME.MILLI_SEC)).addBinBoundary(tr.b.convertUnit(tr.b.UnitScale.TIME.YEAR.value,tr.b.UnitScale.TIME.SEC,tr.b.UnitScale.TIME.MILLI_SEC)));DEFAULT_BOUNDARIES_FOR_UNIT.set(tr.b.Unit.byName.timeStampInMs.unitName,HistogramBinBoundaries.createLinear(0,1e10,1e3));DEFAULT_BOUNDARIES_FOR_UNIT.set(tr.b.Unit.byName.normalizedPercentage.unitName,HistogramBinBoundaries.createLinear(0,1.0,20));DEFAULT_BOUNDARIES_FOR_UNIT.set(tr.b.Unit.byName.sizeInBytes.unitName,HistogramBinBoundaries.createExponential(1,1e12,1e2));DEFAULT_BOUNDARIES_FOR_UNIT.set(tr.b.Unit.byName.energyInJoules.unitName,HistogramBinBoundaries.createExponential(1e-3,1e3,50));DEFAULT_BOUNDARIES_FOR_UNIT.set(tr.b.Unit.byName.powerInWatts.unitName,HistogramBinBoundaries.createExponential(1e-3,1,50));DEFAULT_BOUNDARIES_FOR_UNIT.set(tr.b.Unit.byName.unitlessNumber.unitName,HistogramBinBoundaries.createExponential(1e-3,1e3,50));DEFAULT_BOUNDARIES_FOR_UNIT.set(tr.b.Unit.byName.count.unitName,HistogramBinBoundaries.createExponential(1,1e3,20));DEFAULT_BOUNDARIES_FOR_UNIT.set(tr.b.Unit.byName.sigma.unitName,HistogramBinBoundaries.createLinear(-5,5,50));return{DEFAULT_REBINNED_COUNT,DELTA,Histogram,HistogramBinBoundaries,P_VALUE_NAME,U_STATISTIC_NAME,Z_SCORE_NAME,percentFromString,percentToString,};});'use strict';tr.exportTo('tr.v.ui',function(){Polymer({is:'tr-v-ui-scalar-context-controller',created(){this.host_=undefined;this.groupToContext_=new Map();this.dirtyGroups_=new Set();},attached(){if(this.host_){throw new Error('Scalar context controller is already attached to a host');}
+const host=findParentOrHost(this);if(host.__scalarContextController){throw new Error('Multiple scalar context controllers attached to this host');}
+host.__scalarContextController=this;this.host_=host;},detached(){if(!this.host_){throw new Error('Scalar context controller is not attached to a host');}
+if(this.host_.__scalarContextController!==this){throw new Error('Scalar context controller is not attached to its host');}
+delete this.host_.__scalarContextController;this.host_=undefined;},getContext(group){return this.groupToContext_.get(group);},onScalarSpanAdded(group,span){let context=this.groupToContext_.get(group);if(context===undefined){context={spans:new Set(),range:new tr.b.math.Range()};this.groupToContext_.set(group,context);}
+if(context.spans.has(span)){throw new Error('Scalar span already registered with group: '+group);}
+context.spans.add(span);this.markGroupDirtyAndScheduleUpdate_(group);},onScalarSpanRemoved(group,span){const context=this.groupToContext_.get(group);if(!context.spans.has(span)){throw new Error('Scalar span not registered with group: '+group);}
+context.spans.delete(span);this.markGroupDirtyAndScheduleUpdate_(group);},onScalarSpanUpdated(group,span){const context=this.groupToContext_.get(group);if(!context.spans.has(span)){throw new Error('Scalar span not registered with group: '+group);}
+this.markGroupDirtyAndScheduleUpdate_(group);},markGroupDirtyAndScheduleUpdate_(group){const alreadyDirty=this.dirtyGroups_.size>0;this.dirtyGroups_.add(group);if(!alreadyDirty){tr.b.requestAnimationFrameInThisFrameIfPossible(this.updateContext,this);}},updateContext(){const groups=this.dirtyGroups_;if(groups.size===0)return;this.dirtyGroups_=new Set();for(const group of groups){this.updateGroup_(group);}
+const event=new tr.b.Event('context-updated');event.groups=groups;this.dispatchEvent(event);},updateGroup_(group){const context=this.groupToContext_.get(group);if(context.spans.size===0){this.groupToContext_.delete(group);return;}
+context.range.reset();for(const span of context.spans){context.range.addValue(span.value);}}});function getScalarContextControllerForElement(element){while(element){if(element.__scalarContextController){return element.__scalarContextController;}
+element=findParentOrHost(element);}
+return undefined;}
+function findParentOrHost(node){if(node.parentElement){return node.parentElement;}
+while(Polymer.dom(node).parentNode){node=Polymer.dom(node).parentNode;}
+return node.host;}
+return{getScalarContextControllerForElement,};});'use strict';tr.exportTo('tr.v.ui',function(){function createScalarSpan(value,opt_config){if(value===undefined)return'';const config=opt_config||{};const ownerDocument=config.ownerDocument||document;const span=unwrap(ownerDocument).createElement('tr-v-ui-scalar-span');let numericValue;if(value instanceof tr.b.Scalar){span.value=value;numericValue=value.value;}else if(value instanceof tr.v.Histogram){numericValue=value.average;if(numericValue===undefined)return'';span.setValueAndUnit(numericValue,value.unit);}else{const unit=config.unit;if(unit===undefined){throw new Error('Unit must be provided in config when value is a number');}
+span.setValueAndUnit(value,unit);numericValue=value;}
+if(config.context){span.context=config.context;}
+if(config.customContextRange){span.customContextRange=config.customContextRange;}
+if(config.leftAlign){span.leftAlign=true;}
+if(config.inline){span.inline=true;}
+if(config.significance!==undefined){span.significance=config.significance;}
+if(config.contextGroup!==undefined){span.contextGroup=config.contextGroup;}
+return span;}
+return{createScalarSpan,};});'use strict';Polymer({is:'tr-v-ui-scalar-span',properties:{contextGroup:{type:String,reflectToAttribute:true,observer:'contextGroupChanged_'}},created(){this.value_=undefined;this.unit_=undefined;this.context_=undefined;this.warning_=undefined;this.significance_=tr.b.math.Statistics.Significance.DONT_CARE;this.shouldSearchForContextController_=false;this.lazyContextController_=undefined;this.onContextUpdated_=this.onContextUpdated_.bind(this);this.updateContents_=this.updateContents_.bind(this);this.customContextRange_=undefined;},get significance(){return this.significance_;},set significance(s){this.significance_=s;this.updateContents_();},set contentTextDecoration(deco){this.$.content.style.textDecoration=deco;},get value(){return this.value_;},set value(value){if(value instanceof tr.b.Scalar){this.value_=value.value;this.unit_=value.unit;}else{this.value_=value;}
+this.updateContents_();if(this.hasContext_(this.contextGroup)){this.contextController_.onScalarSpanUpdated(this.contextGroup,this);}else{this.updateSparkline_();}},get contextController_(){if(this.shouldSearchForContextController_){this.lazyContextController_=tr.v.ui.getScalarContextControllerForElement(this);this.shouldSearchForContextController_=false;}
+return this.lazyContextController_;},hasContext_(contextGroup){return!!(contextGroup&&this.contextController_);},contextGroupChanged_(newContextGroup,oldContextGroup){this.detachFromContextControllerIfPossible_(oldContextGroup);if(!this.attachToContextControllerIfPossible_(newContextGroup)){this.onContextUpdated_();}},attachToContextControllerIfPossible_(contextGroup){if(!this.hasContext_(contextGroup))return false;this.contextController_.addEventListener('context-updated',this.onContextUpdated_);this.contextController_.onScalarSpanAdded(contextGroup,this);return true;},detachFromContextControllerIfPossible_(contextGroup){if(!this.hasContext_(contextGroup))return;this.contextController_.removeEventListener('context-updated',this.onContextUpdated_);this.contextController_.onScalarSpanRemoved(contextGroup,this);},attached(){tr.b.Unit.addEventListener('display-mode-changed',this.updateContents_);this.shouldSearchForContextController_=true;this.attachToContextControllerIfPossible_(this.contextGroup);},detached(){tr.b.Unit.removeEventListener('display-mode-changed',this.updateContents_);this.detachFromContextControllerIfPossible_(this.contextGroup);this.shouldSearchForContextController_=false;this.lazyContextController_=undefined;},onContextUpdated_(){this.updateSparkline_();},get context(){return this.context_;},set context(context){this.context_=context;this.updateContents_();},get unit(){return this.unit_;},set unit(unit){this.unit_=unit;this.updateContents_();this.updateSparkline_();},setValueAndUnit(value,unit){this.value_=value;this.unit_=unit;this.updateContents_();},get customContextRange(){return this.customContextRange_;},set customContextRange(customContextRange){this.customContextRange_=customContextRange;this.updateSparkline_();},get inline(){return Polymer.dom(this).classList.contains('inline');},set inline(inline){if(inline){Polymer.dom(this).classList.add('inline');}else{Polymer.dom(this).classList.remove('inline');}},get leftAlign(){return Polymer.dom(this).classList.contains('left-align');},set leftAlign(leftAlign){if(leftAlign){Polymer.dom(this).classList.add('left-align');}else{Polymer.dom(this).classList.remove('left-align');}},updateSparkline_(){Polymer.dom(this.$.sparkline).classList.remove('positive');Polymer.dom(this.$.sparkline).classList.remove('better');Polymer.dom(this.$.sparkline).classList.remove('worse');Polymer.dom(this.$.sparkline).classList.remove('same');this.$.sparkline.style.display='none';this.$.sparkline.style.left='0';this.$.sparkline.style.width='0';let range=this.customContextRange_;if(!range&&this.hasContext_(this.contextGroup)){const context=this.contextController_.getContext(this.contextGroup);if(context){range=context.range;}}
+if(!range||range.isEmpty)return;const leftPoint=Math.min(range.min,0);const rightPoint=Math.max(range.max,0);const pointDistance=rightPoint-leftPoint;if(pointDistance===0){return;}
+this.$.sparkline.style.display='block';let left;let width;if(this.value>0){width=Math.min(this.value,rightPoint);left=-leftPoint;Polymer.dom(this.$.sparkline).classList.add('positive');}else if(this.value<=0){width=-Math.max(this.value,leftPoint);left=(-leftPoint)-width;}
+this.$.sparkline.style.left=this.buildSparklineStyle_(left/pointDistance,false);this.$.sparkline.style.width=this.buildSparklineStyle_(width/pointDistance,true);const changeClass=this.changeClassName_;if(changeClass){Polymer.dom(this.$.sparkline).classList.add(changeClass);}},buildSparklineStyle_(ratio,isWidth){let position='calc('+ratio+' * (100% - 1px)';if(isWidth){position+=' + 1px';}
+position+=')';return position;},updateContents_(){Polymer.dom(this.$.content).textContent='';Polymer.dom(this.$.content).classList.remove('better');Polymer.dom(this.$.content).classList.remove('worse');Polymer.dom(this.$.content).classList.remove('same');this.$.insignificant.style.display='';this.$.significantly_better.style.display='';this.$.significantly_worse.style.display='';if(this.unit_===undefined)return;this.$.content.title='';Polymer.dom(this.$.content).textContent=this.unit_.format(this.value,this.context);this.updateDelta_();},updateDelta_(){let changeClass=this.changeClassName_;if(!changeClass){this.$.significance.style.display='none';return;}
+this.$.significance.style.display='inline';let title;switch(changeClass){case'better':title='improvement';break;case'worse':title='regression';break;case'same':title='no change';break;default:throw new Error('Unknown change class: '+changeClass);}
+Polymer.dom(this.$.content).classList.add(changeClass);switch(this.significance){case tr.b.math.Statistics.Significance.DONT_CARE:break;case tr.b.math.Statistics.Significance.INSIGNIFICANT:if(changeClass!=='same')title='insignificant '+title;this.$.insignificant.style.display='inline';changeClass='same';break;case tr.b.math.Statistics.Significance.SIGNIFICANT:if(changeClass==='same'){throw new Error('How can no change be significant?');}
+this.$['significantly_'+changeClass].style.display='inline';title='significant '+title;break;default:throw new Error('Unknown significance '+this.significance);}
+this.$.significance.title=title;this.$.content.title=title;},get changeClassName_(){if(!this.unit_||!this.unit_.isDelta)return undefined;switch(this.unit_.improvementDirection){case tr.b.ImprovementDirection.DONT_CARE:return undefined;case tr.b.ImprovementDirection.BIGGER_IS_BETTER:if(this.value===0)return'same';return this.value>0?'better':'worse';case tr.b.ImprovementDirection.SMALLER_IS_BETTER:if(this.value===0)return'same';return this.value<0?'better':'worse';default:throw new Error('Unknown improvement direction: '+
+this.unit_.improvementDirection);}},get warning(){return this.warning_;},set warning(warning){this.warning_=warning;const warningEl=this.$.warning;if(this.warning_){warningEl.title=warning;warningEl.style.display='inline';}else{warningEl.title='';warningEl.style.display='';}},get timestamp(){return this.value;},set timestamp(timestamp){if(timestamp instanceof tr.b.u.TimeStamp){this.value=timestamp;return;}
+this.setValueAndUnit(timestamp,tr.b.u.Units.timeStampInMs);},get duration(){return this.value;},set duration(duration){if(duration instanceof tr.b.u.TimeDuration){this.value=duration;return;}
+this.setValueAndUnit(duration,tr.b.u.Units.timeDurationInMs);}});'use strict';function isTable(object){if(!(object instanceof Array)||(object.length<2))return false;for(const colName in object[0]){if(typeof colName!=='string')return false;}
+for(let i=0;i<object.length;++i){if(!(object[i]instanceof Object))return false;for(const colName in object[i]){if(i&&(object[0][colName]===undefined))return false;const cellType=typeof object[i][colName];if(cellType!=='string'&&cellType!=='number')return false;}
+if(i){for(const colName in object[0]){if(object[i][colName]===undefined)return false;}}}
+return true;}
+Polymer({is:'tr-ui-a-generic-object-view',ready(){this.object_=undefined;},get object(){return this.object_;},set object(object){this.object_=object;this.updateContents_();},updateContents_(){Polymer.dom(this.$.content).textContent='';this.appendElementsForType_('',this.object_,0,0,5,'');},appendElementsForType_(label,object,indent,depth,maxDepth,suffix){if(depth>maxDepth){this.appendSimpleText_(label,indent,'<recursion limit reached>',suffix);return;}
+if(object===undefined){this.appendSimpleText_(label,indent,'undefined',suffix);return;}
+if(object===null){this.appendSimpleText_(label,indent,'null',suffix);return;}
+if(!(object instanceof Object)){const type=typeof object;if(type!=='string'){return this.appendSimpleText_(label,indent,object,suffix);}
+let objectReplaced=false;if((object[0]==='{'&&object[object.length-1]==='}')||(object[0]==='['&&object[object.length-1]===']')){try{object=JSON.parse(object);objectReplaced=true;}catch(e){}}
+if(!objectReplaced){if(object.includes('\n')){const lines=object.split('\n');lines.forEach(function(line,i){let text;let ioff;let ll;let ss;if(i===0){text='"'+line;ioff=0;ll=label;ss='';}else if(i<lines.length-1){text=line;ioff=1;ll='';ss='';}else{text=line+'"';ioff=1;ll='';ss=suffix;}
+const el=this.appendSimpleText_(ll,indent+ioff*label.length+ioff,text,ss);el.style.whiteSpace='pre';return el;},this);return;}
+if(tr.b.isUrl(object)){const link=document.createElement('a');link.href=object;link.textContent=object;this.appendElementWithLabel_(label,indent,link,suffix);return;}
+this.appendSimpleText_(label,indent,'"'+object+'"',suffix);return;}}
+if(object instanceof tr.model.ObjectSnapshot){const link=document.createElement('tr-ui-a-analysis-link');link.selection=new tr.model.EventSet(object);this.appendElementWithLabel_(label,indent,link,suffix);return;}
+if(object instanceof tr.model.ObjectInstance){const link=document.createElement('tr-ui-a-analysis-link');link.selection=new tr.model.EventSet(object);this.appendElementWithLabel_(label,indent,link,suffix);return;}
+if(object instanceof tr.b.math.Rect){this.appendSimpleText_(label,indent,object.toString(),suffix);return;}
+if(object instanceof tr.b.Scalar){const el=this.ownerDocument.createElement('tr-v-ui-scalar-span');el.value=object;el.inline=true;this.appendElementWithLabel_(label,indent,el,suffix);return;}
+if(object instanceof Array){this.appendElementsForArray_(label,object,indent,depth,maxDepth,suffix);return;}
+this.appendElementsForObject_(label,object,indent,depth,maxDepth,suffix);},appendElementsForArray_(label,object,indent,depth,maxDepth,suffix){if(object.length===0){this.appendSimpleText_(label,indent,'[]',suffix);return;}
+if(isTable(object)){const table=document.createElement('tr-ui-b-table');const columns=[];for(const colName of Object.keys(object[0])){let allStrings=true;let allNumbers=true;for(let i=0;i<object.length;++i){if(typeof(object[i][colName])!=='string'){allStrings=false;}
+if(typeof(object[i][colName])!=='number'){allNumbers=false;}
+if(!allStrings&&!allNumbers)break;}
+const column={title:colName};column.value=function(row){return row[colName];};if(allStrings){column.cmp=function(x,y){return x[colName].localeCompare(y[colName]);};}else if(allNumbers){column.cmp=function(x,y){return x[colName]-y[colName];};}
+columns.push(column);}
+table.tableColumns=columns;table.tableRows=object;this.appendElementWithLabel_(label,indent,table,suffix);table.rebuild();return;}
+this.appendElementsForType_(label+'[',object[0],indent,depth+1,maxDepth,object.length>1?',':']'+suffix);for(let i=1;i<object.length;i++){this.appendElementsForType_('',object[i],indent+label.length+1,depth+1,maxDepth,i<object.length-1?',':']'+suffix);}
+return;},appendElementsForObject_(label,object,indent,depth,maxDepth,suffix){const keys=Object.keys(object);if(keys.length===0){this.appendSimpleText_(label,indent,'{}',suffix);return;}
+this.appendElementsForType_(label+'{'+keys[0]+': ',object[keys[0]],indent,depth,maxDepth,keys.length>1?',':'}'+suffix);for(let i=1;i<keys.length;i++){this.appendElementsForType_(keys[i]+': ',object[keys[i]],indent+label.length+1,depth+1,maxDepth,i<keys.length-1?',':'}'+suffix);}},appendElementWithLabel_(label,indent,dataElement,suffix){const row=document.createElement('div');const indentSpan=document.createElement('span');indentSpan.style.whiteSpace='pre';for(let i=0;i<indent;i++){Polymer.dom(indentSpan).textContent+=' ';}
+Polymer.dom(row).appendChild(indentSpan);const labelSpan=document.createElement('span');Polymer.dom(labelSpan).textContent=label;Polymer.dom(row).appendChild(labelSpan);Polymer.dom(row).appendChild(dataElement);const suffixSpan=document.createElement('span');Polymer.dom(suffixSpan).textContent=suffix;Polymer.dom(row).appendChild(suffixSpan);row.dataElement=dataElement;Polymer.dom(this.$.content).appendChild(row);},appendSimpleText_(label,indent,text,suffix){const el=this.ownerDocument.createElement('span');Polymer.dom(el).textContent=text;this.appendElementWithLabel_(label,indent,el,suffix);return el;}});'use strict';Polymer({is:'tr-ui-a-generic-object-view-with-label',ready(){this.labelEl_=document.createElement('div');this.genericObjectView_=document.createElement('tr-ui-a-generic-object-view');Polymer.dom(this.root).appendChild(this.labelEl_);Polymer.dom(this.root).appendChild(this.genericObjectView_);},get label(){return Polymer.dom(this.labelEl_).textContent;},set label(label){Polymer.dom(this.labelEl_).textContent=label;},get object(){return this.genericObjectView_.object;},set object(object){this.genericObjectView_.object=object;}});'use strict';tr.exportTo('tr.ui.analysis',function(){const ObjectSnapshotView=tr.ui.b.define('object-snapshot-view');ObjectSnapshotView.prototype={__proto__:HTMLDivElement.prototype,decorate(){this.objectSnapshot_=undefined;},get requiresTallView(){return true;},set modelEvent(obj){this.objectSnapshot=obj;},get modelEvent(){return this.objectSnapshot;},get objectSnapshot(){return this.objectSnapshot_;},set objectSnapshot(i){this.objectSnapshot_=i;this.updateContents();},updateContents(){throw new Error('Not implemented');}};const options=new tr.b.ExtensionRegistryOptions(tr.b.TYPE_BASED_REGISTRY_MODE);options.mandatoryBaseClass=ObjectSnapshotView;options.defaultMetadata={showInstances:true,showInTrackView:true};tr.b.decorateExtensionRegistry(ObjectSnapshotView,options);return{ObjectSnapshotView,};});'use strict';Polymer({is:'tr-ui-b-drag-handle',created(){this.lastMousePos_=0;this.onMouseMove_=this.onMouseMove_.bind(this);this.onMouseUp_=this.onMouseUp_.bind(this);this.addEventListener('mousedown',this.onMouseDown_);this.target_=undefined;this.horizontal=true;this.observer_=new MutationObserver(this.didTargetMutate_.bind(this));this.targetSizesByModeKey_={};this.currentDraggingSize_=undefined;},get modeKey_(){return this.target_.className===''?'.':this.target_.className;},get target(){return this.target_;},set target(target){this.observer_.disconnect();this.target_=target;if(!this.target_)return;this.observer_.observe(this.target_,{attributes:true,attributeFilter:['class']});},get horizontal(){return this.horizontal_;},set horizontal(h){this.horizontal_=h;if(this.horizontal_){this.className='horizontal-drag-handle';}else{this.className='vertical-drag-handle';}},get vertical(){return!this.horizontal_;},set vertical(v){this.horizontal=!v;},forceMutationObserverFlush_(){const records=this.observer_.takeRecords();if(records.length){this.didTargetMutate_(records);}},didTargetMutate_(e){const modeSize=this.targetSizesByModeKey_[this.modeKey_];if(modeSize!==undefined){this.setTargetSize_(modeSize);return;}
+this.target_.style[this.targetStyleKey_]='';},get targetStyleKey_(){return this.horizontal_?'height':'width';},getTargetSize_(){const size=parseInt(window.getComputedStyle(this.target_)[this.targetStyleKey_]);this.targetSizesByModeKey_[this.modeKey_]=size;return size;},setTargetSize_(s){this.target_.style[this.targetStyleKey_]=s+'px';this.targetSizesByModeKey_[this.modeKey_]=this.getTargetSize_();tr.b.dispatchSimpleEvent(this,'drag-handle-resize',true,false);},applyDelta_(delta){if(this.target_===this.nextElementSibling){this.currentDraggingSize_+=delta;}else{this.currentDraggingSize_-=delta;}
+this.setTargetSize_(this.currentDraggingSize_);},onMouseMove_(e){const curMousePos=this.horizontal_?e.clientY:e.clientX;const delta=this.lastMousePos_-curMousePos;this.applyDelta_(delta);this.lastMousePos_=curMousePos;e.preventDefault();return true;},onMouseDown_(e){if(!this.target_)return;this.forceMutationObserverFlush_();this.currentDraggingSize_=this.getTargetSize_();this.lastMousePos_=this.horizontal_?e.clientY:e.clientX;document.addEventListener('mousemove',this.onMouseMove_);document.addEventListener('mouseup',this.onMouseUp_);e.preventDefault();return true;},onMouseUp_(e){document.removeEventListener('mousemove',this.onMouseMove_);document.removeEventListener('mouseup',this.onMouseUp_);e.preventDefault();this.currentDraggingSize_=undefined;}});'use strict';tr.exportTo('tr.ui.b',function(){function HotKey(dict){if(dict.eventType===undefined){throw new Error('eventType must be given');}
+if(dict.keyCode===undefined&&dict.keyCodes===undefined){throw new Error('keyCode or keyCodes must be given');}
+if(dict.keyCode!==undefined&&dict.keyCodes!==undefined){throw new Error('Only keyCode or keyCodes can be given');}
+if(dict.callback===undefined){throw new Error('callback must be given');}
+this.eventType_=dict.eventType;this.keyCodes_=[];if(dict.keyCode){this.pushKeyCode_(dict.keyCode);}else if(dict.keyCodes){dict.keyCodes.forEach(this.pushKeyCode_,this);}
+this.useCapture_=!!dict.useCapture;this.callback_=dict.callback;this.thisArg_=dict.thisArg!==undefined?dict.thisArg:undefined;this.helpText_=dict.helpText!==undefined?dict.helpText:undefined;}
+HotKey.prototype={get eventType(){return this.eventType_;},get keyCodes(){return this.keyCodes_;},get helpText(){return this.helpText_;},call(e){this.callback_.call(this.thisArg_,e);},pushKeyCode_(keyCode){this.keyCodes_.push(keyCode);}};return{HotKey,};});'use strict';Polymer({is:'tv-ui-b-hotkey-controller',created(){this.isAttached_=false;this.globalMode_=false;this.slavedToParentController_=undefined;this.curHost_=undefined;this.childControllers_=[];this.bubblingKeyDownHotKeys_={};this.capturingKeyDownHotKeys_={};this.bubblingKeyPressHotKeys_={};this.capturingKeyPressHotKeys_={};this.onBubblingKeyDown_=this.onKey_.bind(this,false);this.onCapturingKeyDown_=this.onKey_.bind(this,true);this.onBubblingKeyPress_=this.onKey_.bind(this,false);this.onCapturingKeyPress_=this.onKey_.bind(this,true);},attached(){this.isAttached_=true;const host=this.findHost_();if(host.__hotkeyController){throw new Error('Multiple hotkey controllers attached to this host');}
+host.__hotkeyController=this;this.curHost_=host;let parentElement;if(host.parentElement){parentElement=host.parentElement;}else{parentElement=Polymer.dom(host).parentNode.host;}
+const parentController=tr.b.getHotkeyControllerForElement(parentElement);if(parentController){this.slavedToParentController_=parentController;parentController.addChildController_(this);return;}
+host.addEventListener('keydown',this.onBubblingKeyDown_,false);host.addEventListener('keydown',this.onCapturingKeyDown_,true);host.addEventListener('keypress',this.onBubblingKeyPress_,false);host.addEventListener('keypress',this.onCapturingKeyPress_,true);},detached(){this.isAttached_=false;const host=this.curHost_;if(!host)return;delete host.__hotkeyController;this.curHost_=undefined;if(this.slavedToParentController_){this.slavedToParentController_.removeChildController_(this);this.slavedToParentController_=undefined;return;}
+host.removeEventListener('keydown',this.onBubblingKeyDown_,false);host.removeEventListener('keydown',this.onCapturingKeyDown_,true);host.removeEventListener('keypress',this.onBubblingKeyPress_,false);host.removeEventListener('keypress',this.onCapturingKeyPress_,true);},addChildController_(controller){const i=this.childControllers_.indexOf(controller);if(i!==-1){throw new Error('Controller already registered');}
+this.childControllers_.push(controller);},removeChildController_(controller){const i=this.childControllers_.indexOf(controller);if(i===-1){throw new Error('Controller not registered');}
+this.childControllers_.splice(i,1);return controller;},getKeyMapForEventType_(eventType,useCapture){if(eventType==='keydown'){if(!useCapture){return this.bubblingKeyDownHotKeys_;}
+return this.capturingKeyDownHotKeys_;}
+if(eventType==='keypress'){if(!useCapture){return this.bubblingKeyPressHotKeys_;}
+return this.capturingKeyPressHotKeys_;}
+throw new Error('Unsupported key event');},addHotKey(hotKey){if(!(hotKey instanceof tr.ui.b.HotKey)){throw new Error('hotKey must be a tr.ui.b.HotKey');}
+const keyMap=this.getKeyMapForEventType_(hotKey.eventType,hotKey.useCapture);for(let i=0;i<hotKey.keyCodes.length;i++){const keyCode=hotKey.keyCodes[i];if(keyMap[keyCode]){throw new Error('Key is already bound for keyCode='+keyCode);}}
+for(let i=0;i<hotKey.keyCodes.length;i++){const keyCode=hotKey.keyCodes[i];keyMap[keyCode]=hotKey;}
+return hotKey;},removeHotKey(hotKey){if(!(hotKey instanceof tr.ui.b.HotKey)){throw new Error('hotKey must be a tr.ui.b.HotKey');}
+const keyMap=this.getKeyMapForEventType_(hotKey.eventType,hotKey.useCapture);for(let i=0;i<hotKey.keyCodes.length;i++){const keyCode=hotKey.keyCodes[i];if(!keyMap[keyCode]){throw new Error('Key is not bound for keyCode='+keyCode);}
+keyMap[keyCode]=hotKey;}
+for(let i=0;i<hotKey.keyCodes.length;i++){const keyCode=hotKey.keyCodes[i];delete keyMap[keyCode];}
+return hotKey;},get globalMode(){return this.globalMode_;},set globalMode(globalMode){const wasAttached=this.isAttached_;if(wasAttached){this.detached();}
+this.globalMode_=!!globalMode;if(wasAttached){this.attached();}},get topmostConroller_(){if(this.slavedToParentController_){return this.slavedToParentController_.topmostConroller_;}
+return this;},childRequestsGeneralFocus(child){const topmost=this.topmostConroller_;if(topmost.curHost_){if(topmost.curHost_.hasAttribute('tabIndex')){topmost.curHost_.focus();}else{if(document.activeElement){document.activeElement.blur();}}}else{if(document.activeElement){document.activeElement.blur();}}},childRequestsBlur(child){child.blur();const topmost=this.topmostConroller_;if(topmost.curHost_){topmost.curHost_.focus();}},findHost_(){if(this.globalMode_)return wrap(document.body);if(this.parentElement)return this.parentElement;if(!Polymer.dom(this).parentNode)return this.host;let node=this.parentNode;while(Polymer.dom(node).parentNode)node=Polymer.dom(node).parentNode;return node.host;},appendMatchingHotKeysTo_(matchedHotKeys,useCapture,e){const localKeyMap=this.getKeyMapForEventType_(e.type,useCapture);const localHotKey=localKeyMap[e.keyCode];if(localHotKey){matchedHotKeys.push(localHotKey);}
+for(let i=0;i<this.childControllers_.length;i++){const controller=this.childControllers_[i];controller.appendMatchingHotKeysTo_(matchedHotKeys,useCapture,e);}},onKey_(useCapture,e){if(!useCapture&&e.path[0].tagName==='INPUT')return;let sortedControllers;const matchedHotKeys=[];this.appendMatchingHotKeysTo_(matchedHotKeys,useCapture,e);if(matchedHotKeys.length===0)return false;if(matchedHotKeys.length>1){throw new Error('More than one hotKey is currently unsupported');}
+const hotKey=matchedHotKeys[0];let prevented=0;prevented|=hotKey.call(e);return!prevented&&e.defaultPrevented;}});'use strict';tr.exportTo('tr.b',function(){function getHotkeyControllerForElement(refElement){let curElement=refElement;while(curElement){if(curElement.tagName==='tv-ui-b-hotkey-controller'){return curElement;}
+if(curElement.__hotkeyController){return curElement.__hotkeyController;}
+if(curElement.parentElement){curElement=curElement.parentElement;continue;}
+curElement=findHost(curElement);}
+return undefined;}
+function findHost(initialNode){let node=initialNode;while(Polymer.dom(node).parentNode){node=Polymer.dom(node).parentNode;}
+return node.host;}
+return{getHotkeyControllerForElement,};});'use strict';Polymer({is:'tr-ui-b-info-bar',ready(){this.messageEl_=this.$.message;this.buttonsEl_=this.$.buttons;this.message='';},get message(){return Polymer.dom(this.messageEl_).textContent;},set message(message){Polymer.dom(this.messageEl_).textContent=message;},get visible(){return!this.hidden;},set visible(visible){this.hidden=!visible;},removeAllButtons(){Polymer.dom(this.buttonsEl_).textContent='';},addButton(text,clickCallback){const button=document.createElement('button');Polymer.dom(button).textContent=text;button.addEventListener('click',event=>clickCallback(event,this));Polymer.dom(this.buttonsEl_).appendChild(button);return button;}});'use strict';tr.exportTo('tr.ui.b',function(){const ContainerThatDecoratesItsChildren=tr.ui.b.define('div');ContainerThatDecoratesItsChildren.prototype={__proto__:HTMLDivElement.prototype,decorate(){this.observer_=new MutationObserver(this.didMutate_.bind(this));this.observer_.observe(this,{childList:true});Object.defineProperty(this,'textContent',{get:undefined,set:this.onSetTextContent_});},appendChild(x){HTMLDivElement.prototype.appendChild.call(this,x);this.didMutate_(this.observer_.takeRecords());},insertBefore(x,y){HTMLDivElement.prototype.insertBefore.call(this,x,y);this.didMutate_(this.observer_.takeRecords());},removeChild(x){HTMLDivElement.prototype.removeChild.call(this,x);this.didMutate_(this.observer_.takeRecords());},replaceChild(x,y){HTMLDivElement.prototype.replaceChild.call(this,x,y);this.didMutate_(this.observer_.takeRecords());},onSetTextContent_(textContent){if(textContent!==''){throw new Error('textContent can only be set to \'\'.');}
+this.clear();},clear(){while(Polymer.dom(this).lastChild){HTMLDivElement.prototype.removeChild.call(this,Polymer.dom(this).lastChild);}
+this.didMutate_(this.observer_.takeRecords());},didMutate_(records){this.beginDecorating_();for(let i=0;i<records.length;i++){const addedNodes=records[i].addedNodes;if(addedNodes){for(let j=0;j<addedNodes.length;j++){this.decorateChild_(addedNodes[j]);}}
+const removedNodes=records[i].removedNodes;if(removedNodes){for(let j=0;j<removedNodes.length;j++){this.undecorateChild_(removedNodes[j]);}}}
+this.doneDecoratingForNow_();},decorateChild_(child){throw new Error('Not implemented');},undecorateChild_(child){throw new Error('Not implemented');},beginDecorating_(){},doneDecoratingForNow_(){}};return{ContainerThatDecoratesItsChildren,};});'use strict';tr.exportTo('tr.ui.b',function(){const ListView=tr.ui.b.define('x-list-view',tr.ui.b.ContainerThatDecoratesItsChildren);ListView.prototype={__proto__:tr.ui.b.ContainerThatDecoratesItsChildren.prototype,decorate(){tr.ui.b.ContainerThatDecoratesItsChildren.prototype.decorate.call(this);Polymer.dom(this).classList.add('x-list-view');this.style.display='block';this.style.userSelect='none';this.style.outline='none';this.onItemClicked_=this.onItemClicked_.bind(this);this.onKeyDown_=this.onKeyDown_.bind(this);this.tabIndex=0;this.addEventListener('keydown',this.onKeyDown_);this.selectionChanged_=false;},decorateChild_(item){Polymer.dom(item).classList.add('list-item');item.style.paddingTop='2px';item.style.paddingRight='4px';item.style.paddingBottom='2px';item.style.paddingLeft='4px';item.addEventListener('click',this.onItemClicked_,true);Object.defineProperty(item,'selected',{configurable:true,get:()=>item.hasAttribute('selected'),set:value=>{const oldSelection=this.selectedElement;if(oldSelection&&oldSelection!==item&&value){Polymer.dom(this.selectedElement).removeAttribute('selected');}
+if(value){Polymer.dom(item).setAttribute('selected','selected');item.style.backgroundColor='rgb(171, 217, 202)';item.style.outline='1px dotted rgba(0,0,0,0.1)';item.style.outlineOffset=0;}else{Polymer.dom(item).removeAttribute('selected');item.style.backgroundColor='';}
+const newSelection=this.selectedElement;if(newSelection!==oldSelection){tr.b.dispatchSimpleEvent(this,'selection-changed',false);}},});},undecorateChild_(item){this.selectionChanged_|=item.selected;Polymer.dom(item).classList.remove('list-item');item.removeEventListener('click',this.onItemClicked_);delete item.selected;},beginDecorating_(){this.selectionChanged_=false;},doneDecoratingForNow_(){if(this.selectionChanged_){tr.b.dispatchSimpleEvent(this,'selection-changed',false);}},get selectedElement(){const el=Polymer.dom(this).querySelector('.list-item[selected]');if(!el)return undefined;return el;},set selectedElement(el){if(!el){if(this.selectedElement){this.selectedElement.selected=false;}
+return;}
+if(el.parentElement!==this){throw new Error('Can only select elements that are children of this list view');}
+el.selected=true;},getElementByIndex(index){return Polymer.dom(this).querySelector('.list-item:nth-child('+index+')');},clear(){const changed=this.selectedElement!==undefined;tr.ui.b.ContainerThatDecoratesItsChildren.prototype.clear.call(this);if(changed){tr.b.dispatchSimpleEvent(this,'selection-changed',false);}},onItemClicked_(e){const currentSelectedElement=this.selectedElement;if(currentSelectedElement){Polymer.dom(currentSelectedElement).removeAttribute('selected');}
+let element=e.target;while(element.parentElement!==this){element=element.parentElement;}
+if(element!==currentSelectedElement){Polymer.dom(element).setAttribute('selected','selected');}
+tr.b.dispatchSimpleEvent(this,'selection-changed',false);},onKeyDown_(e){if(this.selectedElement===undefined)return;if(e.keyCode===38){const prev=Polymer.dom(this.selectedElement).previousSibling;if(prev){prev.selected=true;tr.ui.b.scrollIntoViewIfNeeded(prev);e.preventDefault();return true;}}else if(e.keyCode===40){const next=Polymer.dom(this.selectedElement).nextSibling;if(next){next.selected=true;tr.ui.b.scrollIntoViewIfNeeded(next);e.preventDefault();return true;}}},addItem(textContent){const item=document.createElement('div');Polymer.dom(item).textContent=textContent;Polymer.dom(this).appendChild(item);item.style.userSelect='none';return item;}};return{ListView,};});'use strict';tr.exportTo('tr.ui.b',function(){const MOUSE_SELECTOR_MODE={};MOUSE_SELECTOR_MODE.SELECTION=0x1;MOUSE_SELECTOR_MODE.PANSCAN=0x2;MOUSE_SELECTOR_MODE.ZOOM=0x4;MOUSE_SELECTOR_MODE.TIMING=0x8;MOUSE_SELECTOR_MODE.ROTATE=0x10;MOUSE_SELECTOR_MODE.ALL_MODES=0x1F;const MOUSE_SELECTOR_MODE_INFOS={};MOUSE_SELECTOR_MODE_INFOS[MOUSE_SELECTOR_MODE.PANSCAN]={name:'PANSCAN',mode:MOUSE_SELECTOR_MODE.PANSCAN,title:'pan',eventNames:{enter:'enterpan',begin:'beginpan',update:'updatepan',end:'endpan',exit:'exitpan'},activeBackgroundPosition:'-30px -10px',defaultBackgroundPosition:'0 -10px'};MOUSE_SELECTOR_MODE_INFOS[MOUSE_SELECTOR_MODE.SELECTION]={name:'SELECTION',mode:MOUSE_SELECTOR_MODE.SELECTION,title:'selection',eventNames:{enter:'enterselection',begin:'beginselection',update:'updateselection',end:'endselection',exit:'exitselection'},activeBackgroundPosition:'-30px -40px',defaultBackgroundPosition:'0 -40px'};MOUSE_SELECTOR_MODE_INFOS[MOUSE_SELECTOR_MODE.ZOOM]={name:'ZOOM',mode:MOUSE_SELECTOR_MODE.ZOOM,title:'zoom',eventNames:{enter:'enterzoom',begin:'beginzoom',update:'updatezoom',end:'endzoom',exit:'exitzoom'},activeBackgroundPosition:'-30px -70px',defaultBackgroundPosition:'0 -70px'};MOUSE_SELECTOR_MODE_INFOS[MOUSE_SELECTOR_MODE.TIMING]={name:'TIMING',mode:MOUSE_SELECTOR_MODE.TIMING,title:'timing',eventNames:{enter:'entertiming',begin:'begintiming',update:'updatetiming',end:'endtiming',exit:'exittiming'},activeBackgroundPosition:'-30px -100px',defaultBackgroundPosition:'0 -100px'};MOUSE_SELECTOR_MODE_INFOS[MOUSE_SELECTOR_MODE.ROTATE]={name:'ROTATE',mode:MOUSE_SELECTOR_MODE.ROTATE,title:'rotate',eventNames:{enter:'enterrotate',begin:'beginrotate',update:'updaterotate',end:'endrotate',exit:'exitrotate'},activeBackgroundPosition:'-30px -130px',defaultBackgroundPosition:'0 -130px'};return{MOUSE_SELECTOR_MODE_INFOS,MOUSE_SELECTOR_MODE,};});'use strict';Polymer({is:'tr-ui-b-mouse-mode-icon',properties:{modeName:{type:String,reflectToAttribute:true,observer:'modeNameChanged'},},created(){this.active_=false;this.acceleratorKey_=undefined;},ready(){this.updateContents_();},get mode(){return tr.ui.b.MOUSE_SELECTOR_MODE[this.modeName];},set mode(mode){const modeInfo=tr.ui.b.MOUSE_SELECTOR_MODE_INFOS[mode];if(modeInfo===undefined){throw new Error('Unknown mode');}
+this.modeName=modeInfo.name;},modeNameChanged(){this.updateContents_();},get active(){return this.active_;},set active(active){this.active_=!!active;if(this.active_){Polymer.dom(this).classList.add('active');}else{Polymer.dom(this).classList.remove('active');}
+this.updateContents_();},get acceleratorKey(){return this.acceleratorKey_;},set acceleratorKey(acceleratorKey){this.acceleratorKey_=acceleratorKey;this.updateContents_();},updateContents_(){if(this.modeName===undefined)return;const mode=this.mode;if(mode===undefined){throw new Error('Invalid mode');}
+const modeInfo=tr.ui.b.MOUSE_SELECTOR_MODE_INFOS[mode];if(!modeInfo){throw new Error('Invalid mode');}
+let title=modeInfo.title;if(this.acceleratorKey_){title=title+' ('+this.acceleratorKey_+')';}
+this.title=title;let bp;if(this.active_){bp=modeInfo.activeBackgroundPosition;}else{bp=modeInfo.defaultBackgroundPosition;}
+this.style.backgroundPosition=bp;}});'use strict';tr.exportTo('tr.ui.b',function(){function MouseTracker(opt_targetElement){this.onMouseDown_=this.onMouseDown_.bind(this);this.onMouseMove_=this.onMouseMove_.bind(this);this.onMouseUp_=this.onMouseUp_.bind(this);this.targetElement=opt_targetElement;}
+MouseTracker.prototype={get targetElement(){return this.targetElement_;},set targetElement(targetElement){if(this.targetElement_){this.targetElement_.removeEventListener('mousedown',this.onMouseDown_);}
+this.targetElement_=targetElement;if(this.targetElement_){this.targetElement_.addEventListener('mousedown',this.onMouseDown_);}},onMouseDown_(e){if(e.button!==0)return true;e=this.remakeEvent_(e,'mouse-tracker-start');this.targetElement_.dispatchEvent(e);document.addEventListener('mousemove',this.onMouseMove_);document.addEventListener('mouseup',this.onMouseUp_);this.targetElement_.addEventListener('blur',this.onMouseUp_);this.savePreviousUserSelect_=document.body.style['-webkit-user-select'];document.body.style['-webkit-user-select']='none';e.preventDefault();return true;},onMouseMove_(e){e=this.remakeEvent_(e,'mouse-tracker-move');this.targetElement_.dispatchEvent(e);},onMouseUp_(e){document.removeEventListener('mousemove',this.onMouseMove_);document.removeEventListener('mouseup',this.onMouseUp_);this.targetElement_.removeEventListener('blur',this.onMouseUp_);document.body.style['-webkit-user-select']=this.savePreviousUserSelect_;e=this.remakeEvent_(e,'mouse-tracker-end');this.targetElement_.dispatchEvent(e);},remakeEvent_(e,newType){const remade=new tr.b.Event(newType,true,true);remade.x=e.x;remade.y=e.y;remade.offsetX=e.offsetX;remade.offsetY=e.offsetY;remade.clientX=e.clientX;remade.clientY=e.clientY;return remade;}};function trackMouseMovesUntilMouseUp(mouseMoveHandler,opt_mouseUpHandler,opt_keyUpHandler){function cleanupAndDispatchToMouseUp(e){document.removeEventListener('mousemove',mouseMoveHandler);if(opt_keyUpHandler){document.removeEventListener('keyup',opt_keyUpHandler);}
+document.removeEventListener('mouseup',cleanupAndDispatchToMouseUp);if(opt_mouseUpHandler){opt_mouseUpHandler(e);}}
+document.addEventListener('mousemove',mouseMoveHandler);if(opt_keyUpHandler){document.addEventListener('keyup',opt_keyUpHandler);}
+document.addEventListener('mouseup',cleanupAndDispatchToMouseUp);}
+return{MouseTracker,trackMouseMovesUntilMouseUp,};});'use strict';tr.exportTo('tr.ui.b',function(){const MOUSE_SELECTOR_MODE=tr.ui.b.MOUSE_SELECTOR_MODE;const MOUSE_SELECTOR_MODE_INFOS=tr.ui.b.MOUSE_SELECTOR_MODE_INFOS;const MIN_MOUSE_SELECTION_DISTANCE=4;const MODIFIER={SHIFT:0x1,SPACE:0x2,CMD_OR_CTRL:0x4};function isCmdOrCtrlPressed(event){if(tr.isMac)return event.metaKey;return event.ctrlKey;}
+Polymer({is:'tr-ui-b-mouse-mode-selector',created(){this.supportedModeMask_=MOUSE_SELECTOR_MODE.ALL_MODES;this.initialRelativeMouseDownPos_={x:0,y:0};this.defaultMode_=MOUSE_SELECTOR_MODE.PANSCAN;this.settingsKey_=undefined;this.mousePos_={x:0,y:0};this.mouseDownPos_={x:0,y:0};this.onMouseDown_=this.onMouseDown_.bind(this);this.onMouseMove_=this.onMouseMove_.bind(this);this.onMouseUp_=this.onMouseUp_.bind(this);this.onKeyDown_=this.onKeyDown_.bind(this);this.onKeyUp_=this.onKeyUp_.bind(this);this.mode_=undefined;this.modeToKeyCodeMap_={};this.modifierToModeMap_={};this.targetElement_=undefined;this.modeBeforeAlternativeModeActivated_=null;this.isInteracting_=false;this.isClick_=false;},ready(){this.buttonsEl_=Polymer.dom(this.root).querySelector('.buttons');this.dragHandleEl_=Polymer.dom(this.root).querySelector('.drag-handle');this.supportedModeMask=MOUSE_SELECTOR_MODE.ALL_MODES;this.dragHandleEl_.addEventListener('mousedown',this.onDragHandleMouseDown_.bind(this));this.buttonsEl_.addEventListener('mouseup',this.onButtonMouseUp_);this.buttonsEl_.addEventListener('mousedown',this.onButtonMouseDown_);this.buttonsEl_.addEventListener('click',this.onButtonPress_.bind(this));},attached(){document.addEventListener('keydown',this.onKeyDown_);document.addEventListener('keyup',this.onKeyUp_);},detached(){document.removeEventListener('keydown',this.onKeyDown_);document.removeEventListener('keyup',this.onKeyUp_);},get targetElement(){return this.targetElement_;},set targetElement(target){if(this.targetElement_){this.targetElement_.removeEventListener('mousedown',this.onMouseDown_);}
+this.targetElement_=target;if(this.targetElement_){this.targetElement_.addEventListener('mousedown',this.onMouseDown_);}},get defaultMode(){return this.defaultMode_;},set defaultMode(defaultMode){this.defaultMode_=defaultMode;},get settingsKey(){return this.settingsKey_;},set settingsKey(settingsKey){this.settingsKey_=settingsKey;if(!this.settingsKey_)return;let mode=tr.b.Settings.get(this.settingsKey_+'.mode',undefined);if(MOUSE_SELECTOR_MODE_INFOS[mode]===undefined){mode=undefined;}
+if((mode&this.supportedModeMask_)===0){mode=undefined;}
+if(!mode)mode=this.defaultMode_;this.mode=mode;const pos=tr.b.Settings.get(this.settingsKey_+'.pos',undefined);if(pos)this.pos=pos;},get supportedModeMask(){return this.supportedModeMask_;},set supportedModeMask(supportedModeMask){if(this.mode&&(supportedModeMask&this.mode)===0){throw new Error('supportedModeMask must include current mode.');}
+function createButtonForMode(mode){return button;}
+this.supportedModeMask_=supportedModeMask;Polymer.dom(this.buttonsEl_).textContent='';for(const modeName in MOUSE_SELECTOR_MODE){if(modeName==='ALL_MODES')continue;const mode=MOUSE_SELECTOR_MODE[modeName];if((this.supportedModeMask_&mode)===0)continue;const button=document.createElement('tr-ui-b-mouse-mode-icon');button.mode=mode;Polymer.dom(button).classList.add('tool-button');Polymer.dom(this.buttonsEl_).appendChild(button);}},getButtonForMode_(mode){for(let i=0;i<this.buttonsEl_.children.length;i++){const buttonEl=this.buttonsEl_.children[i];if(buttonEl.mode===mode){return buttonEl;}}
+return undefined;},get mode(){return this.currentMode_;},set mode(newMode){if(newMode!==undefined){if(typeof newMode!=='number'){throw new Error('Mode must be a number');}
+if((newMode&this.supportedModeMask_)===0){throw new Error('Cannot switch to this mode, it is not supported');}
+if(MOUSE_SELECTOR_MODE_INFOS[newMode]===undefined){throw new Error('Unrecognized mode');}}
+let modeInfo;if(this.currentMode_===newMode)return;if(this.currentMode_){const buttonEl=this.getButtonForMode_(this.currentMode_);if(buttonEl)buttonEl.active=false;if(this.isInteracting_){const mouseEvent=this.createEvent_(MOUSE_SELECTOR_MODE_INFOS[this.mode].eventNames.end);this.dispatchEvent(mouseEvent);}
+modeInfo=MOUSE_SELECTOR_MODE_INFOS[this.currentMode_];tr.b.dispatchSimpleEvent(this,modeInfo.eventNames.exit,true);}
+this.currentMode_=newMode;if(this.currentMode_){const buttonEl=this.getButtonForMode_(this.currentMode_);if(buttonEl)buttonEl.active=true;this.mouseDownPos_.x=this.mousePos_.x;this.mouseDownPos_.y=this.mousePos_.y;modeInfo=MOUSE_SELECTOR_MODE_INFOS[this.currentMode_];if(!this.isInAlternativeMode_){tr.b.dispatchSimpleEvent(this,modeInfo.eventNames.enter,true);}
+if(this.isInteracting_){const mouseEvent=this.createEvent_(MOUSE_SELECTOR_MODE_INFOS[this.mode].eventNames.begin);this.dispatchEvent(mouseEvent);}}
+if(this.settingsKey_&&!this.isInAlternativeMode_){tr.b.Settings.set(this.settingsKey_+'.mode',this.mode);}},setKeyCodeForMode(mode,keyCode){if((mode&this.supportedModeMask_)===0){throw new Error('Mode not supported');}
+this.modeToKeyCodeMap_[mode]=keyCode;if(!this.buttonsEl_)return;const buttonEl=this.getButtonForMode_(mode);if(buttonEl){buttonEl.acceleratorKey=String.fromCharCode(keyCode);}},setCurrentMousePosFromEvent_(e){this.mousePos_.x=e.clientX;this.mousePos_.y=e.clientY;},createEvent_(eventName,sourceEvent){const event=new tr.b.Event(eventName,true);event.clientX=this.mousePos_.x;event.clientY=this.mousePos_.y;event.deltaX=this.mousePos_.x-this.mouseDownPos_.x;event.deltaY=this.mousePos_.y-this.mouseDownPos_.y;event.mouseDownX=this.mouseDownPos_.x;event.mouseDownY=this.mouseDownPos_.y;event.didPreventDefault=false;event.preventDefault=function(){event.didPreventDefault=true;if(sourceEvent){sourceEvent.preventDefault();}};event.stopPropagation=function(){sourceEvent.stopPropagation();};event.stopImmediatePropagation=function(){throw new Error('Not implemented');};return event;},onMouseDown_(e){if(e.button!==0)return;this.setCurrentMousePosFromEvent_(e);const mouseEvent=this.createEvent_(MOUSE_SELECTOR_MODE_INFOS[this.mode].eventNames.begin,e);if(this.mode===MOUSE_SELECTOR_MODE.SELECTION){mouseEvent.appendSelection=isCmdOrCtrlPressed(e);}
+this.dispatchEvent(mouseEvent);this.isInteracting_=true;this.isClick_=true;tr.ui.b.trackMouseMovesUntilMouseUp(this.onMouseMove_,this.onMouseUp_);},onMouseMove_(e){this.setCurrentMousePosFromEvent_(e);const mouseEvent=this.createEvent_(MOUSE_SELECTOR_MODE_INFOS[this.mode].eventNames.update,e);this.dispatchEvent(mouseEvent);if(this.isInteracting_){this.checkIsClick_(e);}},onMouseUp_(e){if(e.button!==0)return;const mouseEvent=this.createEvent_(MOUSE_SELECTOR_MODE_INFOS[this.mode].eventNames.end,e);mouseEvent.isClick=this.isClick_;this.dispatchEvent(mouseEvent);if(this.isClick_&&!mouseEvent.didPreventDefault){this.dispatchClickEvents_(e);}
+this.isInteracting_=false;this.updateAlternativeModeState_(e);},onButtonMouseDown_(e){e.preventDefault();e.stopImmediatePropagation();},onButtonMouseUp_(e){e.preventDefault();e.stopImmediatePropagation();},onButtonPress_(e){this.modeBeforeAlternativeModeActivated_=undefined;this.mode=e.target.mode;e.preventDefault();},onKeyDown_(e){if(e.path[0].tagName==='INPUT')return;if(e.keyCode===' '.charCodeAt(0)){this.spacePressed_=true;}
+this.updateAlternativeModeState_(e);},onKeyUp_(e){if(e.path[0].tagName==='INPUT')return;if(e.keyCode===' '.charCodeAt(0)){this.spacePressed_=false;}
+let didHandleKey=false;for(const[modeStr,keyCode]of Object.entries(this.modeToKeyCodeMap_)){if(e.keyCode===keyCode){this.modeBeforeAlternativeModeActivated_=undefined;const mode=parseInt(modeStr);this.mode=mode;didHandleKey=true;}}
+if(didHandleKey){e.preventDefault();e.stopPropagation();return;}
+this.updateAlternativeModeState_(e);},updateAlternativeModeState_(e){const shiftPressed=e.shiftKey;const spacePressed=this.spacePressed_;const cmdOrCtrlPressed=isCmdOrCtrlPressed(e);const smm=this.supportedModeMask_;let newMode;let isNewModeAnAlternativeMode=false;if(shiftPressed&&(this.modifierToModeMap_[MODIFIER.SHIFT]&smm)!==0){newMode=this.modifierToModeMap_[MODIFIER.SHIFT];isNewModeAnAlternativeMode=true;}else if(spacePressed&&(this.modifierToModeMap_[MODIFIER.SPACE]&smm)!==0){newMode=this.modifierToModeMap_[MODIFIER.SPACE];isNewModeAnAlternativeMode=true;}else if(cmdOrCtrlPressed&&(this.modifierToModeMap_[MODIFIER.CMD_OR_CTRL]&smm)!==0){newMode=this.modifierToModeMap_[MODIFIER.CMD_OR_CTRL];isNewModeAnAlternativeMode=true;}else{if(this.isInAlternativeMode_){newMode=this.modeBeforeAlternativeModeActivated_;isNewModeAnAlternativeMode=false;}else{newMode=undefined;}}
+if(this.mode===newMode||newMode===undefined)return;if(isNewModeAnAlternativeMode){this.modeBeforeAlternativeModeActivated_=this.mode;}
+this.mode=newMode;},get isInAlternativeMode_(){return!!this.modeBeforeAlternativeModeActivated_;},setModifierForAlternateMode(mode,modifier){this.modifierToModeMap_[modifier]=mode;},get pos(){return{x:parseInt(this.style.left),y:parseInt(this.style.top)};},set pos(pos){pos=this.constrainPositionToBounds_(pos);this.style.left=pos.x+'px';this.style.top=pos.y+'px';if(this.settingsKey_){tr.b.Settings.set(this.settingsKey_+'.pos',this.pos);}},constrainPositionToBounds_(pos){const parent=this.offsetParent||document.body;const parentRect=tr.ui.b.windowRectForElement(parent);const top=0;const bottom=parentRect.height-this.offsetHeight;const left=0;const right=parentRect.width-this.offsetWidth;const res={};res.x=Math.max(pos.x,left);res.x=Math.min(res.x,right);res.y=Math.max(pos.y,top);res.y=Math.min(res.y,bottom);return res;},onDragHandleMouseDown_(e){e.preventDefault();e.stopImmediatePropagation();const mouseDownPos={x:e.clientX-this.offsetLeft,y:e.clientY-this.offsetTop};tr.ui.b.trackMouseMovesUntilMouseUp(function(e){const pos={};pos.x=e.clientX-mouseDownPos.x;pos.y=e.clientY-mouseDownPos.y;this.pos=pos;}.bind(this));},checkIsClick_(e){if(!this.isInteracting_||!this.isClick_)return;const deltaX=this.mousePos_.x-this.mouseDownPos_.x;const deltaY=this.mousePos_.y-this.mouseDownPos_.y;const minDist=MIN_MOUSE_SELECTION_DISTANCE;if(deltaX*deltaX+deltaY*deltaY>minDist*minDist){this.isClick_=false;}},dispatchClickEvents_(e){if(!this.isClick_)return;const modeInfo=MOUSE_SELECTOR_MODE_INFOS[MOUSE_SELECTOR_MODE.SELECTION];const eventNames=modeInfo.eventNames;let mouseEvent=this.createEvent_(eventNames.begin);mouseEvent.appendSelection=isCmdOrCtrlPressed(e);this.dispatchEvent(mouseEvent);mouseEvent=this.createEvent_(eventNames.end);this.dispatchEvent(mouseEvent);}});return{MIN_MOUSE_SELECTION_DISTANCE,MODIFIER,};});'use strict';(function(){const DETAILS_SPLIT_REGEX=/^(\S*)\s*([\S\s]*)$/;Polymer({is:'tr-ui-e-chrome-cc-display-item-list-item',created(){Polymer.dom(this).setAttribute('name','');Polymer.dom(this).setAttribute('rawDetails','');Polymer.dom(this).setAttribute('richDetails',undefined);Polymer.dom(this).setAttribute('data_',undefined);},get data(){return this.data_;},set data(data){this.data_=data;if(!data){this.name='DATA MISSING';this.rawDetails='';this.richDetails=undefined;}else if(typeof data==='string'){const match=data.match(DETAILS_SPLIT_REGEX);this.name=match[1];this.rawDetails=match[2];this.richDetails=undefined;}else{this.name=data.name;this.rawDetails='';this.richDetails=data;}},stopPropagation(e){e.stopPropagation();},_computeIfSKP(richDetails){return richDetails&&richDetails.skp64;},_computeHref(richDetails){return'data:application/octet-stream;base64,'+richDetails.skp64;}});})();'use strict';tr.exportTo('tr.ui.e.chrome.cc',function(){function Selection(){this.selectionToSetIfClicked=undefined;}
+Selection.prototype={get specicifity(){throw new Error('Not implemented');},get associatedLayerId(){throw new Error('Not implemented');},get associatedRenderPassId(){throw new Error('Not implemented');},get highlightsByLayerId(){return{};},createAnalysis(){throw new Error('Not implemented');},findEquivalent(lthi){throw new Error('Not implemented');}};function RenderPassSelection(renderPass,renderPassId){if(!renderPass||(renderPassId===undefined)){throw new Error('Render pass (with id) is required');}
+this.renderPass_=renderPass;this.renderPassId_=renderPassId;}
+RenderPassSelection.prototype={__proto__:Selection.prototype,get specicifity(){return 1;},get associatedLayerId(){return undefined;},get associatedRenderPassId(){return this.renderPassId_;},get renderPass(){return this.renderPass_;},createAnalysis(){const dataView=document.createElement('tr-ui-a-generic-object-view-with-label');dataView.label='RenderPass '+this.renderPassId_;dataView.object=this.renderPass_.args;return dataView;},get title(){return this.renderPass_.objectInstance.typeName;}};function LayerSelection(layer){if(!layer){throw new Error('Layer is required');}
+this.layer_=layer;}
+LayerSelection.prototype={__proto__:Selection.prototype,get specicifity(){return 1;},get associatedLayerId(){return this.layer_.layerId;},get associatedRenderPassId(){return undefined;},get layer(){return this.layer_;},createAnalysis(){const dataView=document.createElement('tr-ui-a-generic-object-view-with-label');dataView.label='Layer '+this.layer_.layerId;if(this.layer_.usingGpuRasterization){dataView.label+=' (GPU-rasterized)';}
+dataView.object=this.layer_.args;return dataView;},get title(){return this.layer_.objectInstance.typeName;},findEquivalent(lthi){const layer=lthi.activeTree.findLayerWithId(this.layer_.layerId)||lthi.pendingTree.findLayerWithId(this.layer_.layerId);if(!layer)return undefined;return new LayerSelection(layer);}};function TileSelection(tile,opt_data){this.tile_=tile;this.data_=opt_data||{};}
+TileSelection.prototype={__proto__:Selection.prototype,get specicifity(){return 2;},get associatedLayerId(){return this.tile_.layerId;},get highlightsByLayerId(){const highlights={};highlights[this.tile_.layerId]=[{colorKey:this.tile_.objectInstance.typeName,rect:this.tile_.layerRect}];return highlights;},createAnalysis(){const analysis=document.createElement('tr-ui-a-generic-object-view-with-label');analysis.label='Tile '+this.tile_.objectInstance.id+' on layer '+
+this.tile_.layerId;if(this.data_){analysis.object={moreInfo:this.data_,tileArgs:this.tile_.args};}else{analysis.object=this.tile_.args;}
+return analysis;},findEquivalent(lthi){const tileInstance=this.tile_.tileInstance;if(lthi.ts<tileInstance.creationTs||lthi.ts>=tileInstance.deletionTs){return undefined;}
+const tileSnapshot=tileInstance.getSnapshotAt(lthi.ts);if(!tileSnapshot)return undefined;return new TileSelection(tileSnapshot);}};function LayerRectSelection(layer,rectType,rect,opt_data){this.layer_=layer;this.rectType_=rectType;this.rect_=rect;this.data_=opt_data!==undefined?opt_data:rect;}
+LayerRectSelection.prototype={__proto__:Selection.prototype,get specicifity(){return 2;},get associatedLayerId(){return this.layer_.layerId;},get highlightsByLayerId(){const highlights={};highlights[this.layer_.layerId]=[{colorKey:this.rectType_,rect:this.rect_}];return highlights;},createAnalysis(){const analysis=document.createElement('tr-ui-a-generic-object-view-with-label');analysis.label=this.rectType_+' on layer '+this.layer_.layerId;analysis.object=this.data_;return analysis;},findEquivalent(lthi){return undefined;}};function AnimationRectSelection(layer,rect){this.layer_=layer;this.rect_=rect;}
+AnimationRectSelection.prototype={__proto__:Selection.prototype,get specicifity(){return 0;},get associatedLayerId(){return this.layer_.layerId;},createAnalysis(){const analysis=document.createElement('tr-ui-a-generic-object-view-with-label');analysis.label='Animation Bounds of layer '+this.layer_.layerId;analysis.object=this.rect_;return analysis;}};return{Selection,RenderPassSelection,LayerSelection,TileSelection,LayerRectSelection,AnimationRectSelection,};});'use strict';tr.exportTo('tr.ui.e.chrome.cc',function(){const OPS_TIMING_ITERATIONS=3;const ANNOTATION='Comment';const BEGIN_ANNOTATION='BeginCommentGroup';const END_ANNOTATION='EndCommentGroup';const ANNOTATION_ID='ID: ';const ANNOTATION_CLASS='CLASS: ';const ANNOTATION_TAG='TAG: ';const constants=tr.e.cc.constants;const PictureOpsListView=tr.ui.b.define('tr-ui-e-chrome-cc-picture-ops-list-view');PictureOpsListView.prototype={__proto__:HTMLDivElement.prototype,decorate(){this.style.borderTop='1px solid grey';this.style.overflow='auto';this.opsList_=new tr.ui.b.ListView();Polymer.dom(this).appendChild(this.opsList_);this.selectedOp_=undefined;this.selectedOpIndex_=undefined;this.opsList_.addEventListener('selection-changed',this.onSelectionChanged_.bind(this));this.picture_=undefined;},get picture(){return this.picture_;},set picture(picture){this.picture_=picture;this.updateContents_();},updateContents_(){this.opsList_.clear();if(!this.picture_)return;let ops=this.picture_.getOps();if(!ops)return;ops=this.picture_.tagOpsWithTimings(ops);ops=this.opsTaggedWithAnnotations_(ops);for(let i=0;i<ops.length;i++){const op=ops[i];const item=document.createElement('div');item.opIndex=op.opIndex;Polymer.dom(item).textContent=i+') '+op.cmd_string;if(op.elementInfo.tag||op.elementInfo.id||op.elementInfo.class){const elementInfo=document.createElement('span');Polymer.dom(elementInfo).classList.add('elementInfo');elementInfo.style.color='purple';elementInfo.style.fontSize='small';elementInfo.style.fontWeight='bold';elementInfo.style.color='#777';const tag=op.elementInfo.tag?op.elementInfo.tag:'unknown';const id=op.elementInfo.id?'id='+op.elementInfo.id:undefined;const className=op.elementInfo.class?'class='+
+op.elementInfo.class:undefined;Polymer.dom(elementInfo).textContent='<'+tag+(id?' ':'')+
+(id?id:'')+(className?' ':'')+
+(className?className:'')+'>';Polymer.dom(item).appendChild(elementInfo);}
+if(op.info.length>0){const infoItem=document.createElement('div');Polymer.dom(infoItem).textContent=JSON.stringify(op.info);infoItem.style.fontSize='x-small';infoItem.style.color='#777';Polymer.dom(item).appendChild(infoItem);}
+if(op.cmd_time&&op.cmd_time>=0.0001){const time=document.createElement('span');Polymer.dom(time).classList.add('time');const rounded=op.cmd_time.toFixed(4);Polymer.dom(time).textContent='('+rounded+'ms)';time.style.fontSize='x-small';time.style.color='rgb(136, 0, 0)';Polymer.dom(item).appendChild(time);}
+item.style.borderBottom='1px solid #555';item.style.fontSize='small';item.style.fontWeight='bold';item.style.paddingBottom='5px';item.style.paddingLeft='5px';item.style.cursor='pointer';for(const child of item.children){child.style.fontWeight='normal';child.style.marginLeft='1em';child.style.maxWidth='300px';}
+Polymer.dom(this.opsList_).appendChild(item);}},onSelectionChanged_(e){let beforeSelectedOp=true;if(this.opsList_.selectedElement===this.selectedOp_){this.opsList_.selectedElement=undefined;beforeSelectedOp=false;this.selectedOpIndex_=undefined;}
+this.selectedOp_=this.opsList_.selectedElement;const ops=this.opsList_.children;for(let i=0;i<ops.length;i++){const op=ops[i];if(op===this.selectedOp_){beforeSelectedOp=false;this.selectedOpIndex_=op.opIndex;}else if(beforeSelectedOp){Polymer.dom(op).setAttribute('beforeSelection','beforeSelection');op.style.backgroundColor='rgb(103, 199, 165)';}else{Polymer.dom(op).removeAttribute('beforeSelection');op.style.backgroundColor='';}}
+tr.b.dispatchSimpleEvent(this,'selection-changed',false);},get numOps(){return this.opsList_.children.length;},get selectedOpIndex(){return this.selectedOpIndex_;},set selectedOpIndex(s){this.selectedOpIndex_=s;if(s===undefined){this.opsList_.selectedElement=this.selectedOp_;this.onSelectionChanged_();}else{if(s<0)throw new Error('Invalid index');if(s>=this.numOps)throw new Error('Invalid index');this.opsList_.selectedElement=this.opsList_.getElementByIndex(s+1);tr.ui.b.scrollIntoViewIfNeeded(this.opsList_.selectedElement);}},opsTaggedWithAnnotations_(ops){const annotationGroups=[];const opsWithoutAnnotations=[];for(let opIndex=0;opIndex<ops.length;opIndex++){const op=ops[opIndex];op.opIndex=opIndex;switch(op.cmd_string){case BEGIN_ANNOTATION:annotationGroups.push([]);break;case END_ANNOTATION:annotationGroups.pop();break;case ANNOTATION:annotationGroups[annotationGroups.length-1].push(op);break;default:{const annotations=[];let elementInfo={};annotationGroups.forEach(function(annotationGroup){elementInfo={};annotationGroup.forEach(function(annotation){annotation.info.forEach(function(info){if(info.includes(ANNOTATION_TAG)){elementInfo.tag=info.substring(info.indexOf(ANNOTATION_TAG)+
+ANNOTATION_TAG.length).toLowerCase();}else if(info.includes(ANNOTATION_ID)){elementInfo.id=info.substring(info.indexOf(ANNOTATION_ID)+
+ANNOTATION_ID.length);}else if(info.includes(ANNOTATION_CLASS)){elementInfo.class=info.substring(info.indexOf(ANNOTATION_CLASS)+
+ANNOTATION_CLASS.length);}
+annotations.push(info);});});});op.annotations=annotations;op.elementInfo=elementInfo;opsWithoutAnnotations.push(op);}}}
+return opsWithoutAnnotations;}};return{PictureOpsListView,};});'use strict';tr.exportTo('tr.ui.e.chrome.cc',function(){const THIS_DOC=document.currentScript.ownerDocument;const DisplayItemDebugger=tr.ui.b.define('tr-ui-e-chrome-cc-display-item-debugger');DisplayItemDebugger.prototype={__proto__:HTMLDivElement.prototype,decorate(){const node=tr.ui.b.instantiateTemplate('#tr-ui-e-chrome-cc-display-item-debugger-template',THIS_DOC);Polymer.dom(this).appendChild(node);this.style.flexGrow=1;this.style.flexShrink=1;this.style.flexBasis='auto';this.style.display='flex';this.style.minWidth=0;this.pictureAsImageData_=undefined;this.zoomScaleValue_=1;this.sizeInfo_=Polymer.dom(this).querySelector('.size');this.rasterArea_=Polymer.dom(this).querySelector('raster-area');this.rasterArea_.style.flexGrow=1;this.rasterArea_.style.flexShrink=1;this.rasterArea_.style.flexBasis='auto';this.rasterArea_.style.backgroundColor='#ddd';this.rasterArea_.style.minHeight='200px';this.rasterArea_.style.minWidth='200px';this.rasterArea_.style.paddingLeft='5px';this.rasterArea_.style.display='flex';this.rasterArea_.style.flexDirection='column';this.rasterCanvas_=Polymer.dom(this.rasterArea_).querySelector('canvas');this.rasterCtx_=this.rasterCanvas_.getContext('2d');const canvasScroller=Polymer.dom(this).querySelector('canvas-scroller');canvasScroller.style.flexGrow=1;canvasScroller.style.flexShrink=1;canvasScroller.style.flexBasis='auto';canvasScroller.style.minWidth=0;canvasScroller.style.minHeight=0;canvasScroller.style.overflow='auto';this.trackMouse_();this.displayItemInfo_=Polymer.dom(this).querySelector('display-item-info');this.displayItemInfo_.addEventListener('click',this.onDisplayItemInfoClick_.bind(this),false);this.displayItemListView_=new tr.ui.b.ListView();this.displayItemListView_.addEventListener('selection-changed',this.onDisplayItemListSelection_.bind(this));Polymer.dom(this.displayItemInfo_).appendChild(this.displayItemListView_);this.displayListFilename_=Polymer.dom(this).querySelector('.dlfilename');this.displayListExportButton_=Polymer.dom(this).querySelector('.dlexport');this.displayListExportButton_.addEventListener('click',this.onExportDisplayListClicked_.bind(this));this.skpFilename_=Polymer.dom(this).querySelector('.skpfilename');this.skpExportButton_=Polymer.dom(this).querySelector('.skpexport');this.skpExportButton_.addEventListener('click',this.onExportSkPictureClicked_.bind(this));const leftPanel=Polymer.dom(this).querySelector('left-panel');leftPanel.style.flexGrow=0;leftPanel.style.flexShrink=0;leftPanel.style.flexBasis='auto';leftPanel.style.minWidth='200px';leftPanel.style.overflow='auto';leftPanel.children[0].paddingTop='2px';leftPanel.children[0].children[0].style.borderBottom='1px solid #555';const leftPanelTitle=leftPanel.querySelector('.title');leftPanelTitle.style.fontWeight='bold';leftPanelTitle.style.marginLeft='5px';leftPanelTitle.style.marginright='5px';for(const div of leftPanel.querySelectorAll('.export')){div.style.margin='5px';}
+const middleDragHandle=document.createElement('tr-ui-b-drag-handle');middleDragHandle.style.flexGrow=0;middleDragHandle.style.flexShrink=0;middleDragHandle.style.flexBasis='auto';middleDragHandle.horizontal=false;middleDragHandle.target=leftPanel;const rightPanel=Polymer.dom(this).querySelector('right-panel');rightPanel.style.display='flex';rightPanel.style.flexGrow=1;rightPanel.style.flexShrink=1;rightPanel.style.flexBasis='auto';rightPanel.style.minWidth=0;this.infoBar_=document.createElement('tr-ui-b-info-bar');Polymer.dom(this.rasterArea_).insertBefore(this.infoBar_,canvasScroller);Polymer.dom(this).insertBefore(middleDragHandle,rightPanel);this.picture_=undefined;this.pictureOpsListView_=new tr.ui.e.chrome.cc.PictureOpsListView();this.pictureOpsListView_.style.flexGrow=0;this.pictureOpsListView_.style.flexShrink=0;this.pictureOpsListView_.style.flexBasis='auto';this.pictureOpsListView_.style.overflow='auto';this.pictureOpsListView_.style.minWidth='100px';Polymer.dom(rightPanel).insertBefore(this.pictureOpsListView_,this.rasterArea_);this.pictureOpsListDragHandle_=document.createElement('tr-ui-b-drag-handle');this.pictureOpsListDragHandle_.horizontal=false;this.pictureOpsListDragHandle_.target=this.pictureOpsListView_;Polymer.dom(rightPanel).insertBefore(this.pictureOpsListDragHandle_,this.rasterArea_);},get picture(){return this.picture_;},set displayItemList(displayItemList){this.displayItemList_=displayItemList;this.picture=this.displayItemList_;this.displayItemListView_.clear();this.displayItemList_.items.forEach(function(item){const listItem=document.createElement('tr-ui-e-chrome-cc-display-item-list-item');listItem.data=item;Polymer.dom(this.displayItemListView_).appendChild(listItem);}.bind(this));},set picture(picture){this.picture_=picture;const showOpsList=picture&&picture!==this.displayItemList_;this.updateDrawOpsList_(showOpsList);if(picture){const size=this.getRasterCanvasSize_();this.rasterCanvas_.width=size.width;this.rasterCanvas_.height=size.height;}
+const bounds=this.rasterArea_.getBoundingClientRect();const selectorBounds=this.mouseModeSelector_.getBoundingClientRect();this.mouseModeSelector_.pos={x:(bounds.right-selectorBounds.width-10),y:bounds.top};this.rasterize_();this.scheduleUpdateContents_();},getRasterCanvasSize_(){const style=window.getComputedStyle(this.rasterArea_);let width=parseInt(style.width);let height=parseInt(style.height);if(this.picture_){width=Math.max(width,this.picture_.layerRect.width);height=Math.max(height,this.picture_.layerRect.height);}
+return{width,height};},scheduleUpdateContents_(){if(this.updateContentsPending_)return;this.updateContentsPending_=true;tr.b.requestAnimationFrameInThisFrameIfPossible(this.updateContents_.bind(this));},updateContents_(){this.updateContentsPending_=false;if(this.picture_){Polymer.dom(this.sizeInfo_).textContent='('+
+this.picture_.layerRect.width+' x '+
+this.picture_.layerRect.height+')';}
+if(!this.pictureAsImageData_)return;this.infoBar_.visible=false;this.infoBar_.removeAllButtons();if(this.pictureAsImageData_.error){this.infoBar_.message='Cannot rasterize...';this.infoBar_.addButton('More info...',function(e){const overlay=new tr.ui.b.Overlay();Polymer.dom(overlay).textContent=this.pictureAsImageData_.error;overlay.visible=true;e.stopPropagation();return false;}.bind(this));this.infoBar_.visible=true;}
+this.drawPicture_();},drawPicture_(){const size=this.getRasterCanvasSize_();if(size.width!==this.rasterCanvas_.width){this.rasterCanvas_.width=size.width;}
+if(size.height!==this.rasterCanvas_.height){this.rasterCanvas_.height=size.height;}
+this.rasterCtx_.clearRect(0,0,size.width,size.height);if(!this.picture_||!this.pictureAsImageData_.imageData)return;const imgCanvas=this.pictureAsImageData_.asCanvas();const w=imgCanvas.width;const h=imgCanvas.height;this.rasterCtx_.drawImage(imgCanvas,0,0,w,h,0,0,w*this.zoomScaleValue_,h*this.zoomScaleValue_);},rasterize_(){if(this.picture_){this.picture_.rasterize({showOverdraw:false},this.onRasterComplete_.bind(this));}},onRasterComplete_(pictureAsImageData){this.pictureAsImageData_=pictureAsImageData;this.scheduleUpdateContents_();},onDisplayItemListSelection_(e){const selected=this.displayItemListView_.selectedElement;if(!selected){this.picture=this.displayItemList_;return;}
+const index=Array.prototype.indexOf.call(this.displayItemListView_.children,selected);const displayItem=this.displayItemList_.items[index];if(displayItem&&displayItem.skp64){this.picture=new tr.e.cc.Picture(displayItem.skp64,this.displayItemList_.layerRect);}else{this.picture=undefined;}},onDisplayItemInfoClick_(e){if(e&&e.target===this.displayItemInfo_){this.displayItemListView_.selectedElement=undefined;}},updateDrawOpsList_(showOpsList){if(showOpsList){this.pictureOpsListView_.picture=this.picture_;if(this.pictureOpsListView_.numOps>0){this.pictureOpsListView_.style.display='block';this.pictureOpsListDragHandle_.style.display='block';}}else{this.pictureOpsListView_.style.display='none';this.pictureOpsListDragHandle_.style.display='none';}},trackMouse_(){this.mouseModeSelector_=document.createElement('tr-ui-b-mouse-mode-selector');this.mouseModeSelector_.targetElement=this.rasterArea_;Polymer.dom(this.rasterArea_).appendChild(this.mouseModeSelector_);this.mouseModeSelector_.supportedModeMask=tr.ui.b.MOUSE_SELECTOR_MODE.ZOOM;this.mouseModeSelector_.mode=tr.ui.b.MOUSE_SELECTOR_MODE.ZOOM;this.mouseModeSelector_.defaultMode=tr.ui.b.MOUSE_SELECTOR_MODE.ZOOM;this.mouseModeSelector_.settingsKey='pictureDebugger.mouseModeSelector';this.mouseModeSelector_.addEventListener('beginzoom',this.onBeginZoom_.bind(this));this.mouseModeSelector_.addEventListener('updatezoom',this.onUpdateZoom_.bind(this));this.mouseModeSelector_.addEventListener('endzoom',this.onEndZoom_.bind(this));},onBeginZoom_(e){this.isZooming_=true;this.lastMouseViewPos_=this.extractRelativeMousePosition_(e);e.preventDefault();},onUpdateZoom_(e){if(!this.isZooming_)return;const currentMouseViewPos=this.extractRelativeMousePosition_(e);this.zoomScaleValue_+=((this.lastMouseViewPos_.y-currentMouseViewPos.y)*0.001);this.zoomScaleValue_=Math.max(this.zoomScaleValue_,0.1);this.drawPicture_();this.lastMouseViewPos_=currentMouseViewPos;},onEndZoom_(e){this.lastMouseViewPos_=undefined;this.isZooming_=false;e.preventDefault();},extractRelativeMousePosition_(e){return{x:e.clientX-this.rasterArea_.offsetLeft,y:e.clientY-this.rasterArea_.offsetTop};},saveFile_(filename,rawData){if(!rawData)return;const length=rawData.length;const arrayBuffer=new ArrayBuffer(length);const uint8Array=new Uint8Array(arrayBuffer);for(let c=0;c<length;c++){uint8Array[c]=rawData.charCodeAt(c);}
+const blob=new Blob([uint8Array],{type:'application/octet-binary'});const blobUrl=window.URL.createObjectURL(blob);const link=document.createElementNS('http://www.w3.org/1999/xhtml','a');link.href=blobUrl;link.download=filename;const event=document.createEvent('MouseEvents');event.initMouseEvent('click',true,false,window,0,0,0,0,0,false,false,false,false,0,null);link.dispatchEvent(event);},onExportDisplayListClicked_(){const rawData=JSON.stringify(this.displayItemList_.items);this.saveFile_(this.displayListFilename_.value,rawData);},onExportSkPictureClicked_(){const rawData=tr.b.Base64.atob(this.picture_.getBase64SkpData());this.saveFile_(this.skpFilename_.value,rawData);}};return{DisplayItemDebugger,};});'use strict';tr.exportTo('tr.ui.e.chrome.cc',function(){const DisplayItemSnapshotView=tr.ui.b.define('tr-ui-e-chrome-cc-display-item-list-view',tr.ui.analysis.ObjectSnapshotView);DisplayItemSnapshotView.prototype={__proto__:tr.ui.analysis.ObjectSnapshotView.prototype,decorate(){this.style.display='flex';this.style.flexGrow=1;this.style.flexShrink=1;this.style.flexBasis='auto';this.style.minWidth=0;this.displayItemDebugger_=new tr.ui.e.chrome.cc.DisplayItemDebugger();this.displayItemDebugger_.style.flexGrow=1;this.displayItemDebugger_.style.flexShrink=1;this.displayItemDebugger_.style.flexBasis='auto';this.displayItemDebugger_.style.minWidth=0;Polymer.dom(this).appendChild(this.displayItemDebugger_);},updateContents(){if(this.objectSnapshot_&&this.displayItemDebugger_){this.displayItemDebugger_.displayItemList=this.objectSnapshot_;}}};tr.ui.analysis.ObjectSnapshotView.register(DisplayItemSnapshotView,{typeNames:['cc::DisplayItemList'],showInstances:false});return{DisplayItemSnapshotView,};});'use strict';tr.exportTo('tr.ui.e.chrome.cc',function(){const constants=tr.e.cc.constants;const RENDER_PASS_QUADS=Math.max(constants.ACTIVE_TREE,constants.PENDING_TREE)+1;const LayerPicker=tr.ui.b.define('tr-ui-e-chrome-cc-layer-picker');LayerPicker.prototype={__proto__:HTMLUnknownElement.prototype,decorate(){this.lthi_=undefined;this.controls_=document.createElement('top-controls');this.renderPassQuads_=false;this.style.display='flex';this.style.flexDirection='column';this.controls_.style.flexGrow=0;this.controls_.style.flexShrink=0;this.controls_.style.flexBasis='auto';this.controls_.style.backgroundImage='-webkit-gradient(linear, 0 0, 100% 0, from(#E5E5E5), to(#D1D1D1))';this.controls_.style.borderBottom='1px solid #8e8e8e';this.controls_.style.borderTop='1px solid white';this.controls_.style.display='inline';this.controls_.style.fontSize='14px';this.controls_.style.paddingLeft='2px';this.itemList_=new tr.ui.b.ListView();this.itemList_.style.flexGrow=1;this.itemList_.style.flexShrink=1;this.itemList_.style.flexBasis='auto';this.itemList_.style.fontFamily='monospace';this.itemList_.style.overflow='auto';Polymer.dom(this).appendChild(this.controls_);Polymer.dom(this).appendChild(this.itemList_);this.itemList_.addEventListener('selection-changed',this.onItemSelectionChanged_.bind(this));Polymer.dom(this.controls_).appendChild(tr.ui.b.createSelector(this,'whichTree','layerPicker.whichTree',constants.ACTIVE_TREE,[{label:'Active tree',value:constants.ACTIVE_TREE},{label:'Pending tree',value:constants.PENDING_TREE},{label:'Render pass quads',value:RENDER_PASS_QUADS}]));this.showPureTransformLayers_=false;const showPureTransformLayers=tr.ui.b.createCheckBox(this,'showPureTransformLayers','layerPicker.showPureTransformLayers',false,'Transform layers');Polymer.dom(showPureTransformLayers).classList.add('show-transform-layers');showPureTransformLayers.title='When checked, pure transform layers are shown';Polymer.dom(this.controls_).appendChild(showPureTransformLayers);},get lthiSnapshot(){return this.lthiSnapshot_;},set lthiSnapshot(lthiSnapshot){this.lthiSnapshot_=lthiSnapshot;this.updateContents_();},get whichTree(){return this.renderPassQuads_?constants.ACTIVE_TREE:this.whichTree_;},set whichTree(whichTree){this.whichTree_=whichTree;this.renderPassQuads_=(whichTree===RENDER_PASS_QUADS);this.updateContents_();tr.b.dispatchSimpleEvent(this,'selection-change',false);},get layerTreeImpl(){if(this.lthiSnapshot===undefined)return undefined;return this.lthiSnapshot.getTree(this.whichTree);},get isRenderPassQuads(){return this.renderPassQuads_;},get showPureTransformLayers(){return this.showPureTransformLayers_;},set showPureTransformLayers(show){if(this.showPureTransformLayers_===show)return;this.showPureTransformLayers_=show;this.updateContents_();},getRenderPassInfos_(){if(!this.lthiSnapshot_)return[];const renderPassInfo=[];if(!this.lthiSnapshot_.args.frame||!this.lthiSnapshot_.args.frame.renderPasses){return renderPassInfo;}
+const renderPasses=this.lthiSnapshot_.args.frame.renderPasses;for(let i=0;i<renderPasses.length;++i){const info={renderPass:renderPasses[i],depth:0,id:i,name:'cc::RenderPass'};renderPassInfo.push(info);}
+return renderPassInfo;},getLayerInfos_(){if(!this.lthiSnapshot_)return[];const tree=this.lthiSnapshot_.getTree(this.whichTree_);if(!tree)return[];const layerInfos=[];const showPureTransformLayers=this.showPureTransformLayers_;const visitedLayers={};function visitLayer(layer,depth,isMask,isReplica){if(visitedLayers[layer.layerId])return;visitedLayers[layer.layerId]=true;const info={layer,depth};if(layer.args.drawsContent){info.name=layer.objectInstance.name;}else{info.name='cc::LayerImpl';}
+if(layer.usingGpuRasterization){info.name+=' (G)';}
+info.isMaskLayer=isMask;info.replicaLayer=isReplica;if(showPureTransformLayers||layer.args.drawsContent){layerInfos.push(info);}}
+tree.iterLayers(visitLayer);return layerInfos;},updateContents_(){if(this.renderPassQuads_){this.updateRenderPassContents_();}else{this.updateLayerContents_();}},updateRenderPassContents_(){this.itemList_.clear();let selectedRenderPassId;if(this.selection_&&this.selection_.associatedRenderPassId){selectedRenderPassId=this.selection_.associatedRenderPassId;}
+const renderPassInfos=this.getRenderPassInfos_();renderPassInfos.forEach(function(renderPassInfo){const renderPass=renderPassInfo.renderPass;const id=renderPassInfo.id;const item=this.createElementWithDepth_(renderPassInfo.depth);const labelEl=Polymer.dom(item).appendChild(tr.ui.b.createSpan());Polymer.dom(labelEl).textContent=renderPassInfo.name+' '+id;item.renderPass=renderPass;item.renderPassId=id;Polymer.dom(this.itemList_).appendChild(item);if(id===selectedRenderPassId){renderPass.selectionState=tr.model.SelectionState.SELECTED;}},this);},updateLayerContents_(){this.changingItemSelection_=true;try{this.itemList_.clear();let selectedLayerId;if(this.selection_&&this.selection_.associatedLayerId){selectedLayerId=this.selection_.associatedLayerId;}
+const layerInfos=this.getLayerInfos_();layerInfos.forEach(function(layerInfo){const layer=layerInfo.layer;const id=layer.layerId;const item=this.createElementWithDepth_(layerInfo.depth);const labelEl=Polymer.dom(item).appendChild(tr.ui.b.createSpan());Polymer.dom(labelEl).textContent=layerInfo.name+' '+id;const notesEl=Polymer.dom(item).appendChild(tr.ui.b.createSpan());if(layerInfo.isMaskLayer){Polymer.dom(notesEl).textContent+='(mask)';}
+if(layerInfo.isReplicaLayer){Polymer.dom(notesEl).textContent+='(replica)';}
+if((layer.gpuMemoryUsageInBytes!==undefined)&&(layer.gpuMemoryUsageInBytes>0)){const gpuUsageStr=tr.b.Unit.byName.sizeInBytes.format(layer.gpuMemoryUsageInBytes);Polymer.dom(notesEl).textContent+=' ('+gpuUsageStr+' MiB)';}
+item.layer=layer;Polymer.dom(this.itemList_).appendChild(item);if(layer.layerId===selectedLayerId){layer.selectionState=tr.model.SelectionState.SELECTED;item.selected=true;}},this);}finally{this.changingItemSelection_=false;}},createElementWithDepth_(depth){const item=document.createElement('div');const indentEl=Polymer.dom(item).appendChild(tr.ui.b.createSpan());indentEl.style.whiteSpace='pre';for(let i=0;i<depth;i++){Polymer.dom(indentEl).textContent=Polymer.dom(indentEl).textContent+' ';}
+return item;},onItemSelectionChanged_(e){if(this.changingItemSelection_)return;if(this.renderPassQuads_){this.onRenderPassSelected_(e);}else{this.onLayerSelected_(e);}
+tr.b.dispatchSimpleEvent(this,'selection-change',false);},onRenderPassSelected_(e){let selectedRenderPass;let selectedRenderPassId;if(this.itemList_.selectedElement){selectedRenderPass=this.itemList_.selectedElement.renderPass;selectedRenderPassId=this.itemList_.selectedElement.renderPassId;}
+if(selectedRenderPass){this.selection_=new tr.ui.e.chrome.cc.RenderPassSelection(selectedRenderPass,selectedRenderPassId);}else{this.selection_=undefined;}},onLayerSelected_(e){let selectedLayer;if(this.itemList_.selectedElement){selectedLayer=this.itemList_.selectedElement.layer;}
+if(selectedLayer){this.selection_=new tr.ui.e.chrome.cc.LayerSelection(selectedLayer);}else{this.selection_=undefined;}},get selection(){return this.selection_;},set selection(selection){if(this.selection_===selection)return;this.selection_=selection;this.updateContents_();}};return{LayerPicker,};});'use strict';tr.exportTo('tr.e.cc',function(){const ObjectSnapshot=tr.model.ObjectSnapshot;function RenderPassSnapshot(){ObjectSnapshot.apply(this,arguments);}
+RenderPassSnapshot.prototype={__proto__:ObjectSnapshot.prototype,preInitialize(){tr.e.cc.preInitializeObject(this);},initialize(){tr.e.cc.moveRequiredFieldsFromArgsToToplevel(this,['quadList']);}};ObjectSnapshot.subTypes.register(RenderPassSnapshot,{typeName:'cc::RenderPass'});return{RenderPassSnapshot,};});'use strict';tr.exportTo('tr.ui.b',function(){const deg2rad=tr.b.math.deg2rad;const constants={DEFAULT_SCALE:0.5,DEFAULT_EYE_DISTANCE:10000,MINIMUM_DISTANCE:1000,MAXIMUM_DISTANCE:100000,FOV:15,RESCALE_TIMEOUT_MS:200,MAXIMUM_TILT:80,SETTINGS_NAMESPACE:'tr.ui_camera'};const Camera=tr.ui.b.define('camera');Camera.prototype={__proto__:HTMLUnknownElement.prototype,decorate(eventSource){this.eventSource_=eventSource;this.eventSource_.addEventListener('beginpan',this.onPanBegin_.bind(this));this.eventSource_.addEventListener('updatepan',this.onPanUpdate_.bind(this));this.eventSource_.addEventListener('endpan',this.onPanEnd_.bind(this));this.eventSource_.addEventListener('beginzoom',this.onZoomBegin_.bind(this));this.eventSource_.addEventListener('updatezoom',this.onZoomUpdate_.bind(this));this.eventSource_.addEventListener('endzoom',this.onZoomEnd_.bind(this));this.eventSource_.addEventListener('beginrotate',this.onRotateBegin_.bind(this));this.eventSource_.addEventListener('updaterotate',this.onRotateUpdate_.bind(this));this.eventSource_.addEventListener('endrotate',this.onRotateEnd_.bind(this));this.eye_=[0,0,constants.DEFAULT_EYE_DISTANCE];this.gazeTarget_=[0,0,0];this.rotation_=[0,0];this.pixelRatio_=window.devicePixelRatio||1;},get modelViewMatrix(){const mvMatrix=mat4.create();mat4.lookAt(mvMatrix,this.eye_,this.gazeTarget_,[0,1,0]);return mvMatrix;},get projectionMatrix(){const rect=tr.ui.b.windowRectForElement(this.canvas_).scaleSize(this.pixelRatio_);const aspectRatio=rect.width/rect.height;const matrix=mat4.create();mat4.perspective(matrix,deg2rad(constants.FOV),aspectRatio,1,100000);return matrix;},set canvas(c){this.canvas_=c;},set deviceRect(rect){this.deviceRect_=rect;},get stackingDistanceDampening(){const gazeVector=[this.gazeTarget_[0]-this.eye_[0],this.gazeTarget_[1]-this.eye_[1],this.gazeTarget_[2]-this.eye_[2]];vec3.normalize(gazeVector,gazeVector);return 1+gazeVector[2];},loadCameraFromSettings(settings){this.eye_=settings.get('eye',this.eye_,constants.SETTINGS_NAMESPACE);this.gazeTarget_=settings.get('gaze_target',this.gazeTarget_,constants.SETTINGS_NAMESPACE);this.rotation_=settings.get('rotation',this.rotation_,constants.SETTINGS_NAMESPACE);this.dispatchRenderEvent_();},saveCameraToSettings(settings){settings.set('eye',this.eye_,constants.SETTINGS_NAMESPACE);settings.set('gaze_target',this.gazeTarget_,constants.SETTINGS_NAMESPACE);settings.set('rotation',this.rotation_,constants.SETTINGS_NAMESPACE);},resetCamera(){this.eye_=[0,0,constants.DEFAULT_EYE_DISTANCE];this.gazeTarget_=[0,0,0];this.rotation_=[0,0];const settings=tr.b.SessionSettings();const keys=settings.keys(constants.SETTINGS_NAMESPACE);if(keys.length!==0){this.loadCameraFromSettings(settings);return;}
+if(this.deviceRect_){const rect=tr.ui.b.windowRectForElement(this.canvas_).scaleSize(this.pixelRatio_);this.eye_[0]=this.deviceRect_.width/2;this.eye_[1]=this.deviceRect_.height/2;this.gazeTarget_[0]=this.deviceRect_.width/2;this.gazeTarget_[1]=this.deviceRect_.height/2;}
+this.saveCameraToSettings(settings);this.dispatchRenderEvent_();},updatePanByDelta(delta){const rect=tr.ui.b.windowRectForElement(this.canvas_).scaleSize(this.pixelRatio_);const eyeVector=[this.eye_[0]-this.gazeTarget_[0],this.eye_[1]-this.gazeTarget_[1],this.eye_[2]-this.gazeTarget_[2]];const length=vec3.length(eyeVector);vec3.normalize(eyeVector,eyeVector);const halfFov=constants.FOV/2;const multiplier=2.0*length*Math.tan(deg2rad(halfFov))/rect.height;const up=[0,1,0];const rotMatrix=mat4.create();mat4.rotate(rotMatrix,rotMatrix,deg2rad(this.rotation_[1]),[0,1,0]);mat4.rotate(rotMatrix,rotMatrix,deg2rad(this.rotation_[0]),[1,0,0]);vec3.transformMat4(up,up,rotMatrix);const right=[0,0,0];vec3.cross(right,eyeVector,up);vec3.normalize(right,right);for(let i=0;i<3;++i){this.gazeTarget_[i]+=delta[0]*multiplier*right[i]-delta[1]*multiplier*up[i];this.eye_[i]=this.gazeTarget_[i]+length*eyeVector[i];}
+if(Math.abs(this.gazeTarget_[2])>1e-6){const gazeVector=[-eyeVector[0],-eyeVector[1],-eyeVector[2]];const newLength=tr.b.math.clamp(-this.eye_[2]/gazeVector[2],constants.MINIMUM_DISTANCE,constants.MAXIMUM_DISTANCE);for(let i=0;i<3;++i){this.gazeTarget_[i]=this.eye_[i]+newLength*gazeVector[i];}}
+this.saveCameraToSettings(tr.b.SessionSettings());this.dispatchRenderEvent_();},updateZoomByDelta(delta){let deltaY=delta[1];deltaY=tr.b.math.clamp(deltaY,-50,50);let scale=1.0-deltaY/100.0;const eyeVector=[0,0,0];vec3.subtract(eyeVector,this.eye_,this.gazeTarget_);const length=vec3.length(eyeVector);if(length*scale<constants.MINIMUM_DISTANCE){scale=constants.MINIMUM_DISTANCE/length;}else if(length*scale>constants.MAXIMUM_DISTANCE){scale=constants.MAXIMUM_DISTANCE/length;}
+vec3.scale(eyeVector,eyeVector,scale);vec3.add(this.eye_,this.gazeTarget_,eyeVector);this.saveCameraToSettings(tr.b.SessionSettings());this.dispatchRenderEvent_();},updateRotateByDelta(delta){delta[0]*=0.5;delta[1]*=0.5;if(Math.abs(this.rotation_[0]+delta[1])>constants.MAXIMUM_TILT){return;}
+if(Math.abs(this.rotation_[1]-delta[0])>constants.MAXIMUM_TILT){return;}
+const eyeVector=[0,0,0,0];vec3.subtract(eyeVector,this.eye_,this.gazeTarget_);const rotMatrix=mat4.create();mat4.rotate(rotMatrix,rotMatrix,-deg2rad(this.rotation_[0]),[1,0,0]);mat4.rotate(rotMatrix,rotMatrix,-deg2rad(this.rotation_[1]),[0,1,0]);vec4.transformMat4(eyeVector,eyeVector,rotMatrix);this.rotation_[0]+=delta[1];this.rotation_[1]-=delta[0];mat4.identity(rotMatrix);mat4.rotate(rotMatrix,rotMatrix,deg2rad(this.rotation_[1]),[0,1,0]);mat4.rotate(rotMatrix,rotMatrix,deg2rad(this.rotation_[0]),[1,0,0]);vec4.transformMat4(eyeVector,eyeVector,rotMatrix);vec3.add(this.eye_,this.gazeTarget_,eyeVector);this.saveCameraToSettings(tr.b.SessionSettings());this.dispatchRenderEvent_();},onPanBegin_(e){this.panning_=true;this.lastMousePosition_=this.getMousePosition_(e);},onPanUpdate_(e){if(!this.panning_)return;const delta=this.getMouseDelta_(e,this.lastMousePosition_);this.lastMousePosition_=this.getMousePosition_(e);this.updatePanByDelta(delta);},onPanEnd_(e){this.panning_=false;},onZoomBegin_(e){this.zooming_=true;const p=this.getMousePosition_(e);this.lastMousePosition_=p;this.zoomPoint_=p;},onZoomUpdate_(e){if(!this.zooming_)return;const delta=this.getMouseDelta_(e,this.lastMousePosition_);this.lastMousePosition_=this.getMousePosition_(e);this.updateZoomByDelta(delta);},onZoomEnd_(e){this.zooming_=false;this.zoomPoint_=undefined;},onRotateBegin_(e){this.rotating_=true;this.lastMousePosition_=this.getMousePosition_(e);},onRotateUpdate_(e){if(!this.rotating_)return;const delta=this.getMouseDelta_(e,this.lastMousePosition_);this.lastMousePosition_=this.getMousePosition_(e);this.updateRotateByDelta(delta);},onRotateEnd_(e){this.rotating_=false;},getMousePosition_(e){const rect=tr.ui.b.windowRectForElement(this.canvas_);return[(e.clientX-rect.x)*this.pixelRatio_,(e.clientY-rect.y)*this.pixelRatio_];},getMouseDelta_(e,p){const newP=this.getMousePosition_(e);return[newP[0]-p[0],newP[1]-p[1]];},dispatchRenderEvent_(){tr.b.dispatchSimpleEvent(this,'renderrequired',false,false);}};return{Camera,};});'use strict';tr.exportTo('tr.ui.b',function(){const THIS_DOC=document.currentScript.ownerDocument;const constants={};constants.IMAGE_LOAD_RETRY_TIME_MS=500;constants.SUBDIVISION_MINIMUM=1;constants.SUBDIVISION_RECURSION_DEPTH=3;constants.SUBDIVISION_DEPTH_THRESHOLD=100;constants.FAR_PLANE_DISTANCE=10000;function drawTexturedTriangle(ctx,img,p0,p1,p2,t0,t1,t2){const tmpP0=[p0[0],p0[1]];const tmpP1=[p1[0],p1[1]];const tmpP2=[p2[0],p2[1]];const tmpT0=[t0[0],t0[1]];const tmpT1=[t1[0],t1[1]];const tmpT2=[t2[0],t2[1]];ctx.beginPath();ctx.moveTo(tmpP0[0],tmpP0[1]);ctx.lineTo(tmpP1[0],tmpP1[1]);ctx.lineTo(tmpP2[0],tmpP2[1]);ctx.closePath();tmpP1[0]-=tmpP0[0];tmpP1[1]-=tmpP0[1];tmpP2[0]-=tmpP0[0];tmpP2[1]-=tmpP0[1];tmpT1[0]-=tmpT0[0];tmpT1[1]-=tmpT0[1];tmpT2[0]-=tmpT0[0];tmpT2[1]-=tmpT0[1];const det=1/(tmpT1[0]*tmpT2[1]-tmpT2[0]*tmpT1[1]);const a=(tmpT2[1]*tmpP1[0]-tmpT1[1]*tmpP2[0])*det;const b=(tmpT2[1]*tmpP1[1]-tmpT1[1]*tmpP2[1])*det;const c=(tmpT1[0]*tmpP2[0]-tmpT2[0]*tmpP1[0])*det;const d=(tmpT1[0]*tmpP2[1]-tmpT2[0]*tmpP1[1])*det;const e=tmpP0[0]-a*tmpT0[0]-c*tmpT0[1];const f=tmpP0[1]-b*tmpT0[0]-d*tmpT0[1];ctx.save();ctx.transform(a,b,c,d,e,f);ctx.clip();ctx.drawImage(img,0,0);ctx.restore();}
+function drawTriangleSub(ctx,img,p0,p1,p2,t0,t1,t2,opt_recursionDepth){const depth=opt_recursionDepth||0;let subdivisionIndex=0;if(depth<constants.SUBDIVISION_MINIMUM){subdivisionIndex=7;}else if(depth<constants.SUBDIVISION_RECURSION_DEPTH){if(Math.abs(p0[2]-p1[2])>constants.SUBDIVISION_DEPTH_THRESHOLD){subdivisionIndex+=1;}
+if(Math.abs(p0[2]-p2[2])>constants.SUBDIVISION_DEPTH_THRESHOLD){subdivisionIndex+=2;}
+if(Math.abs(p1[2]-p2[2])>constants.SUBDIVISION_DEPTH_THRESHOLD){subdivisionIndex+=4;}}
+const p01=vec4.create();const p02=vec4.create();const p12=vec4.create();const t01=vec2.create();const t02=vec2.create();const t12=vec2.create();for(let i=0;i<2;++i){p0[i]*=p0[2];p1[i]*=p1[2];p2[i]*=p2[2];}
+for(let i=0;i<4;++i){p01[i]=(p0[i]+p1[i])/2;p02[i]=(p0[i]+p2[i])/2;p12[i]=(p1[i]+p2[i])/2;}
+for(let i=0;i<2;++i){p0[i]/=p0[2];p1[i]/=p1[2];p2[i]/=p2[2];p01[i]/=p01[2];p02[i]/=p02[2];p12[i]/=p12[2];}
+for(let i=0;i<2;++i){t01[i]=(t0[i]+t1[i])/2;t02[i]=(t0[i]+t2[i])/2;t12[i]=(t1[i]+t2[i])/2;}
+switch(subdivisionIndex){case 1:drawTriangleSub(ctx,img,p0,p01,p2,t0,t01,t2,depth+1);drawTriangleSub(ctx,img,p01,p1,p2,t01,t1,t2,depth+1);break;case 2:drawTriangleSub(ctx,img,p0,p1,p02,t0,t1,t02,depth+1);drawTriangleSub(ctx,img,p1,p02,p2,t1,t02,t2,depth+1);break;case 3:drawTriangleSub(ctx,img,p0,p01,p02,t0,t01,t02,depth+1);drawTriangleSub(ctx,img,p02,p01,p2,t02,t01,t2,depth+1);drawTriangleSub(ctx,img,p01,p1,p2,t01,t1,t2,depth+1);break;case 4:drawTriangleSub(ctx,img,p0,p12,p2,t0,t12,t2,depth+1);drawTriangleSub(ctx,img,p0,p1,p12,t0,t1,t12,depth+1);break;case 5:drawTriangleSub(ctx,img,p0,p01,p2,t0,t01,t2,depth+1);drawTriangleSub(ctx,img,p2,p01,p12,t2,t01,t12,depth+1);drawTriangleSub(ctx,img,p01,p1,p12,t01,t1,t12,depth+1);break;case 6:drawTriangleSub(ctx,img,p0,p12,p02,t0,t12,t02,depth+1);drawTriangleSub(ctx,img,p0,p1,p12,t0,t1,t12,depth+1);drawTriangleSub(ctx,img,p02,p12,p2,t02,t12,t2,depth+1);break;case 7:drawTriangleSub(ctx,img,p0,p01,p02,t0,t01,t02,depth+1);drawTriangleSub(ctx,img,p01,p12,p02,t01,t12,t02,depth+1);drawTriangleSub(ctx,img,p01,p1,p12,t01,t1,t12,depth+1);drawTriangleSub(ctx,img,p02,p12,p2,t02,t12,t2,depth+1);break;default:drawTexturedTriangle(ctx,img,p0,p1,p2,t0,t1,t2);break;}}
+const tmpVec4=vec4.create();function transform(transformed,point,matrix,viewport){vec4.set(tmpVec4,point[0],point[1],0,1);vec4.transformMat4(tmpVec4,tmpVec4,matrix);let w=tmpVec4[3];if(w<1e-6)w=1e-6;transformed[0]=((tmpVec4[0]/w)+1)*viewport.width/2;transformed[1]=((tmpVec4[1]/w)+1)*viewport.height/2;transformed[2]=w;}
+function drawProjectedQuadBackgroundToContext(quad,p1,p2,p3,p4,ctx,quadCanvas){if(quad.imageData){quadCanvas.width=quad.imageData.width;quadCanvas.height=quad.imageData.height;quadCanvas.getContext('2d').putImageData(quad.imageData,0,0);const quadBBox=new tr.b.math.BBox2();quadBBox.addQuad(quad);const iw=quadCanvas.width;const ih=quadCanvas.height;drawTriangleSub(ctx,quadCanvas,p1,p2,p4,[0,0],[iw,0],[0,ih]);drawTriangleSub(ctx,quadCanvas,p2,p3,p4,[iw,0],[iw,ih],[0,ih]);}
+if(quad.backgroundColor){ctx.fillStyle=quad.backgroundColor;ctx.beginPath();ctx.moveTo(p1[0],p1[1]);ctx.lineTo(p2[0],p2[1]);ctx.lineTo(p3[0],p3[1]);ctx.lineTo(p4[0],p4[1]);ctx.closePath();ctx.fill();}}
+function drawProjectedQuadOutlineToContext(quad,p1,p2,p3,p4,ctx,quadCanvas){ctx.beginPath();ctx.moveTo(p1[0],p1[1]);ctx.lineTo(p2[0],p2[1]);ctx.lineTo(p3[0],p3[1]);ctx.lineTo(p4[0],p4[1]);ctx.closePath();ctx.save();if(quad.borderColor){ctx.strokeStyle=quad.borderColor;}else{ctx.strokeStyle='rgb(128,128,128)';}
+if(quad.shadowOffset){ctx.shadowColor='rgb(0, 0, 0)';ctx.shadowOffsetX=quad.shadowOffset[0];ctx.shadowOffsetY=quad.shadowOffset[1];if(quad.shadowBlur){ctx.shadowBlur=quad.shadowBlur;}}
+if(quad.borderWidth){ctx.lineWidth=quad.borderWidth;}else{ctx.lineWidth=1;}
+ctx.stroke();ctx.restore();}
+function drawProjectedQuadSelectionOutlineToContext(quad,p1,p2,p3,p4,ctx,quadCanvas){if(!quad.upperBorderColor)return;ctx.lineWidth=8;ctx.strokeStyle=quad.upperBorderColor;ctx.beginPath();ctx.moveTo(p1[0],p1[1]);ctx.lineTo(p2[0],p2[1]);ctx.lineTo(p3[0],p3[1]);ctx.lineTo(p4[0],p4[1]);ctx.closePath();ctx.stroke();}
+function drawProjectedQuadToContext(passNumber,quad,p1,p2,p3,p4,ctx,quadCanvas){if(passNumber===0){drawProjectedQuadBackgroundToContext(quad,p1,p2,p3,p4,ctx,quadCanvas);}else if(passNumber===1){drawProjectedQuadOutlineToContext(quad,p1,p2,p3,p4,ctx,quadCanvas);}else if(passNumber===2){drawProjectedQuadSelectionOutlineToContext(quad,p1,p2,p3,p4,ctx,quadCanvas);}else{throw new Error('Invalid pass number');}}
+const tmpP1=vec3.create();const tmpP2=vec3.create();const tmpP3=vec3.create();const tmpP4=vec3.create();function transformAndProcessQuads(matrix,viewport,quads,numPasses,handleQuadFunc,opt_arg1,opt_arg2){for(let passNumber=0;passNumber<numPasses;passNumber++){for(let i=0;i<quads.length;i++){const quad=quads[i];transform(tmpP1,quad.p1,matrix,viewport);transform(tmpP2,quad.p2,matrix,viewport);transform(tmpP3,quad.p3,matrix,viewport);transform(tmpP4,quad.p4,matrix,viewport);handleQuadFunc(passNumber,quad,tmpP1,tmpP2,tmpP3,tmpP4,opt_arg1,opt_arg2);}}}
+const QuadStackView=tr.ui.b.define('quad-stack-view');QuadStackView.prototype={__proto__:HTMLDivElement.prototype,decorate(){this.className='quad-stack-view';this.style.display='flex';this.style.position='relative';const node=tr.ui.b.instantiateTemplate('#quad-stack-view-template',THIS_DOC);Polymer.dom(this).appendChild(node);this.updateHeaderVisibility_();const header=Polymer.dom(this).querySelector('#header');header.style.position='absolute';header.style.fontSize='70%';header.style.top='10px';header.style.left='10px';header.style.right='150px';const scroller=Polymer.dom(this).querySelector('#canvas-scroller');scroller.style.flexGrow=1;scroller.style.flexShrink=1;scroller.style.flexBasis='auto';scroller.style.minWidth=0;scroller.style.minHeight=0;scroller.style.overflow='auto';this.canvas_=Polymer.dom(this).querySelector('#canvas');this.chromeImages_={left:Polymer.dom(this).querySelector('#chrome-left'),mid:Polymer.dom(this).querySelector('#chrome-mid'),right:Polymer.dom(this).querySelector('#chrome-right')};const stackingDistanceSlider=Polymer.dom(this).querySelector('#stacking-distance-slider');stackingDistanceSlider.style.position='absolute';stackingDistanceSlider.style.fontSize='70%';stackingDistanceSlider.style.top='10px';stackingDistanceSlider.style.right='10px';stackingDistanceSlider.value=tr.b.Settings.get('quadStackView.stackingDistance',45);stackingDistanceSlider.addEventListener('change',this.onStackingDistanceChange_.bind(this));stackingDistanceSlider.addEventListener('input',this.onStackingDistanceChange_.bind(this));this.trackMouse_();this.camera_=new tr.ui.b.Camera(this.mouseModeSelector_);this.camera_.addEventListener('renderrequired',this.onRenderRequired_.bind(this));this.cameraWasReset_=false;this.camera_.canvas=this.canvas_;this.viewportRect_=tr.b.math.Rect.fromXYWH(0,0,0,0);this.pixelRatio_=window.devicePixelRatio||1;},updateHeaderVisibility_(){if(this.headerText){Polymer.dom(this).querySelector('#header').style.display='';}else{Polymer.dom(this).querySelector('#header').style.display='none';}},get headerText(){return Polymer.dom(this).querySelector('#header').textContent;},set headerText(headerText){Polymer.dom(this).querySelector('#header').textContent=headerText;this.updateHeaderVisibility_();},onStackingDistanceChange_(e){tr.b.Settings.set('quadStackView.stackingDistance',this.stackingDistance);this.scheduleRender();e.stopPropagation();},get stackingDistance(){return Polymer.dom(this).querySelector('#stacking-distance-slider').value;},get mouseModeSelector(){return this.mouseModeSelector_;},get camera(){return this.camera_;},set quads(q){this.quads_=q;this.scheduleRender();},set deviceRect(rect){if(!rect||rect.equalTo(this.deviceRect_))return;this.deviceRect_=rect;this.camera_.deviceRect=rect;this.chromeQuad_=undefined;},resize(){if(!this.offsetParent)return true;const width=parseInt(window.getComputedStyle(this.offsetParent).width);const height=parseInt(window.getComputedStyle(this.offsetParent).height);const rect=tr.b.math.Rect.fromXYWH(0,0,width,height);if(rect.equalTo(this.viewportRect_))return false;this.viewportRect_=rect;this.canvas_.style.width=width+'px';this.canvas_.style.height=height+'px';this.canvas_.width=this.pixelRatio_*width;this.canvas_.height=this.pixelRatio_*height;if(!this.cameraWasReset_){this.camera_.resetCamera();this.cameraWasReset_=true;}
+return true;},readyToDraw(){if(!this.chromeImages_.left.src){let leftContent=window.getComputedStyle(this.chromeImages_.left).backgroundImage;leftContent=tr.ui.b.extractUrlString(leftContent);let midContent=window.getComputedStyle(this.chromeImages_.mid).backgroundImage;midContent=tr.ui.b.extractUrlString(midContent);let rightContent=window.getComputedStyle(this.chromeImages_.right).backgroundImage;rightContent=tr.ui.b.extractUrlString(rightContent);this.chromeImages_.left.src=leftContent;this.chromeImages_.mid.src=midContent;this.chromeImages_.right.src=rightContent;}
+return(this.chromeImages_.left.height>0)&&(this.chromeImages_.mid.height>0)&&(this.chromeImages_.right.height>0);},get chromeQuad(){if(this.chromeQuad_)return this.chromeQuad_;const chromeCanvas=document.createElement('canvas');const offsetY=this.chromeImages_.left.height;chromeCanvas.width=this.deviceRect_.width;chromeCanvas.height=this.deviceRect_.height+offsetY;const leftWidth=this.chromeImages_.left.width;const midWidth=this.chromeImages_.mid.width;const rightWidth=this.chromeImages_.right.width;const chromeCtx=chromeCanvas.getContext('2d');chromeCtx.drawImage(this.chromeImages_.left,0,0);chromeCtx.save();chromeCtx.translate(leftWidth,0);const s=(this.deviceRect_.width-leftWidth-rightWidth)/midWidth;chromeCtx.scale(s,1);chromeCtx.drawImage(this.chromeImages_.mid,0,0);chromeCtx.restore();chromeCtx.drawImage(this.chromeImages_.right,leftWidth+s*midWidth,0);const chromeRect=tr.b.math.Rect.fromXYWH(this.deviceRect_.x,this.deviceRect_.y-offsetY,this.deviceRect_.width,this.deviceRect_.height+offsetY);const chromeQuad=tr.b.math.Quad.fromRect(chromeRect);chromeQuad.stackingGroupId=this.maxStackingGroupId_+1;chromeQuad.imageData=chromeCtx.getImageData(0,0,chromeCanvas.width,chromeCanvas.height);chromeQuad.shadowOffset=[0,0];chromeQuad.shadowBlur=5;chromeQuad.borderWidth=3;this.chromeQuad_=chromeQuad;return this.chromeQuad_;},scheduleRender(){if(this.redrawScheduled_)return false;this.redrawScheduled_=true;tr.b.requestAnimationFrame(this.render,this);},onRenderRequired_(e){this.scheduleRender();},stackTransformAndProcessQuads_(numPasses,handleQuadFunc,includeChromeQuad,opt_arg1,opt_arg2){const mv=this.camera_.modelViewMatrix;const p=this.camera_.projectionMatrix;const viewport=tr.b.math.Rect.fromXYWH(0,0,this.canvas_.width,this.canvas_.height);const quadStacks=[];for(let i=0;i<this.quads_.length;++i){const quad=this.quads_[i];const stackingId=quad.stackingGroupId||0;while(stackingId>=quadStacks.length){quadStacks.push([]);}
+quadStacks[stackingId].push(quad);}
+const mvp=mat4.create();this.maxStackingGroupId_=quadStacks.length;const effectiveStackingDistance=this.stackingDistance*this.camera_.stackingDistanceDampening;mat4.multiply(mvp,p,mv);for(let i=0;i<quadStacks.length;++i){transformAndProcessQuads(mvp,viewport,quadStacks[i],numPasses,handleQuadFunc,opt_arg1,opt_arg2);mat4.translate(mv,mv,[0,0,effectiveStackingDistance]);mat4.multiply(mvp,p,mv);}
+if(includeChromeQuad&&this.deviceRect_){transformAndProcessQuads(mvp,viewport,[this.chromeQuad],numPasses,drawProjectedQuadToContext,opt_arg1,opt_arg2);}},render(){this.redrawScheduled_=false;if(!this.readyToDraw()){setTimeout(this.scheduleRender.bind(this),constants.IMAGE_LOAD_RETRY_TIME_MS);return;}
+if(!this.quads_)return;const canvasCtx=this.canvas_.getContext('2d');if(!this.resize()){canvasCtx.clearRect(0,0,this.canvas_.width,this.canvas_.height);}
+const quadCanvas=document.createElement('canvas');this.stackTransformAndProcessQuads_(3,drawProjectedQuadToContext,true,canvasCtx,quadCanvas);quadCanvas.width=0;},trackMouse_(){this.mouseModeSelector_=document.createElement('tr-ui-b-mouse-mode-selector');this.mouseModeSelector_.targetElement=this.canvas_;this.mouseModeSelector_.supportedModeMask=tr.ui.b.MOUSE_SELECTOR_MODE.SELECTION|tr.ui.b.MOUSE_SELECTOR_MODE.PANSCAN|tr.ui.b.MOUSE_SELECTOR_MODE.ZOOM|tr.ui.b.MOUSE_SELECTOR_MODE.ROTATE;this.mouseModeSelector_.mode=tr.ui.b.MOUSE_SELECTOR_MODE.PANSCAN;this.mouseModeSelector_.pos={x:0,y:100};Polymer.dom(this).appendChild(this.mouseModeSelector_);this.mouseModeSelector_.settingsKey='quadStackView.mouseModeSelector';this.mouseModeSelector_.setModifierForAlternateMode(tr.ui.b.MOUSE_SELECTOR_MODE.ROTATE,tr.ui.b.MODIFIER.SHIFT);this.mouseModeSelector_.setModifierForAlternateMode(tr.ui.b.MOUSE_SELECTOR_MODE.PANSCAN,tr.ui.b.MODIFIER.SPACE);this.mouseModeSelector_.setModifierForAlternateMode(tr.ui.b.MOUSE_SELECTOR_MODE.ZOOM,tr.ui.b.MODIFIER.CMD_OR_CTRL);this.mouseModeSelector_.addEventListener('updateselection',this.onSelectionUpdate_.bind(this));this.mouseModeSelector_.addEventListener('endselection',this.onSelectionUpdate_.bind(this));},extractRelativeMousePosition_(e){const br=this.canvas_.getBoundingClientRect();return[this.pixelRatio_*(e.clientX-this.canvas_.offsetLeft-br.left),this.pixelRatio_*(e.clientY-this.canvas_.offsetTop-br.top)];},onSelectionUpdate_(e){const mousePos=this.extractRelativeMousePosition_(e);const res=[];function handleQuad(passNumber,quad,p1,p2,p3,p4){if(tr.b.math.pointInImplicitQuad(mousePos,p1,p2,p3,p4)){res.push(quad);}}
+this.stackTransformAndProcessQuads_(1,handleQuad,false);e=new tr.b.Event('selectionchange');e.quads=res;this.dispatchEvent(e);}};return{QuadStackView,};});'use strict';tr.exportTo('tr.ui.e.chrome.cc',function(){const ColorScheme=tr.b.ColorScheme;const THIS_DOC=document.currentScript.ownerDocument;const TILE_HEATMAP_TYPE={};TILE_HEATMAP_TYPE.NONE='none';TILE_HEATMAP_TYPE.SCHEDULED_PRIORITY='scheduledPriority';TILE_HEATMAP_TYPE.USING_GPU_MEMORY='usingGpuMemory';const cc=tr.ui.e.chrome.cc;function createTileRectsSelectorBaseOptions(){return[{label:'None',value:'none'},{label:'Coverage Rects',value:'coverage'}];}
+const LayerTreeQuadStackView=tr.ui.b.define('tr-ui-e-chrome-cc-layer-tree-quad-stack-view');LayerTreeQuadStackView.prototype={__proto__:HTMLDivElement.prototype,decorate(){this.style.flexGrow=1;this.style.flexShrink=1;this.style.flexBasis='auto';this.style.flexDirection='column';this.style.minHeight=0;this.style.display='flex';this.isRenderPassQuads_=false;this.pictureAsImageData_={};this.messages_=[];this.controls_=document.createElement('top-controls');this.controls_.style.flexGrow=0;this.controls_.style.flexShrink=0;this.controls_.style.flexBasis='auto';this.controls_.style.backgroundImage='-webkit-gradient(linear, 0 0, 100% 0, from(#E5E5E5), to(#D1D1D1))';this.controls_.style.borderBottom='1px solid #8e8e8e';this.controls_.style.borderTop='1px solid white';this.controls_.style.display='flex';this.controls_.style.flexDirection='row';this.controls_.style.flexWrap='wrap';this.controls_.style.fontSize='14px';this.controls_.style.paddingLeft='2px';this.controls_.style.overflow='hidden';this.infoBar_=document.createElement('tr-ui-b-info-bar');this.quadStackView_=new tr.ui.b.QuadStackView();this.quadStackView_.addEventListener('selectionchange',this.onQuadStackViewSelectionChange_.bind(this));this.quadStackView_.style.flexGrow=1;this.quadStackView_.style.flexShrink=1;this.quadStackView_.style.flexBasis='auto';this.quadStackView_.style.minWidth='200px';this.extraHighlightsByLayerId_=undefined;this.inputEventImageData_=undefined;const m=tr.ui.b.MOUSE_SELECTOR_MODE;const mms=this.quadStackView_.mouseModeSelector;mms.settingsKey='tr.e.cc.layerTreeQuadStackView.mouseModeSelector';mms.setKeyCodeForMode(m.SELECTION,'Z'.charCodeAt(0));mms.setKeyCodeForMode(m.PANSCAN,'X'.charCodeAt(0));mms.setKeyCodeForMode(m.ZOOM,'C'.charCodeAt(0));mms.setKeyCodeForMode(m.ROTATE,'V'.charCodeAt(0));const node=tr.ui.b.instantiateTemplate('#tr-ui-e-chrome-cc-layer-tree-quad-stack-view-template',THIS_DOC);Polymer.dom(this).appendChild(node);Polymer.dom(this).appendChild(this.controls_);Polymer.dom(this).appendChild(this.infoBar_);Polymer.dom(this).appendChild(this.quadStackView_);this.tileRectsSelector_=tr.ui.b.createSelector(this,'howToShowTiles','layerView.howToShowTiles','none',createTileRectsSelectorBaseOptions());Polymer.dom(this.controls_).appendChild(this.tileRectsSelector_);const tileHeatmapText=tr.ui.b.createSpan({textContent:'Tile heatmap:'});Polymer.dom(this.controls_).appendChild(tileHeatmapText);const tileHeatmapSelector=tr.ui.b.createSelector(this,'tileHeatmapType','layerView.tileHeatmapType',TILE_HEATMAP_TYPE.NONE,[{label:'None',value:TILE_HEATMAP_TYPE.NONE},{label:'Scheduled Priority',value:TILE_HEATMAP_TYPE.SCHEDULED_PRIORITY},{label:'Is using GPU memory',value:TILE_HEATMAP_TYPE.USING_GPU_MEMORY}]);Polymer.dom(this.controls_).appendChild(tileHeatmapSelector);const showOtherLayersCheckbox=tr.ui.b.createCheckBox(this,'showOtherLayers','layerView.showOtherLayers',true,'Other layers/passes');showOtherLayersCheckbox.title='When checked, show all layers, selected or not.';Polymer.dom(this.controls_).appendChild(showOtherLayersCheckbox);const showInvalidationsCheckbox=tr.ui.b.createCheckBox(this,'showInvalidations','layerView.showInvalidations',true,'Invalidations');showInvalidationsCheckbox.title='When checked, compositing invalidations are highlighted in red';Polymer.dom(this.controls_).appendChild(showInvalidationsCheckbox);const showUnrecordedRegionCheckbox=tr.ui.b.createCheckBox(this,'showUnrecordedRegion','layerView.showUnrecordedRegion',true,'Unrecorded area');showUnrecordedRegionCheckbox.title='When checked, unrecorded areas are highlighted in yellow';Polymer.dom(this.controls_).appendChild(showUnrecordedRegionCheckbox);const showBottlenecksCheckbox=tr.ui.b.createCheckBox(this,'showBottlenecks','layerView.showBottlenecks',true,'Bottlenecks');showBottlenecksCheckbox.title='When checked, scroll bottlenecks are highlighted';Polymer.dom(this.controls_).appendChild(showBottlenecksCheckbox);const showLayoutRectsCheckbox=tr.ui.b.createCheckBox(this,'showLayoutRects','layerView.showLayoutRects',false,'Layout rects');showLayoutRectsCheckbox.title='When checked, shows rects for regions where layout happened';Polymer.dom(this.controls_).appendChild(showLayoutRectsCheckbox);const showContentsCheckbox=tr.ui.b.createCheckBox(this,'showContents','layerView.showContents',true,'Contents');showContentsCheckbox.title='When checked, show the rendered contents inside the layer outlines';Polymer.dom(this.controls_).appendChild(showContentsCheckbox);const showAnimationBoundsCheckbox=tr.ui.b.createCheckBox(this,'showAnimationBounds','layerView.showAnimationBounds',false,'Animation Bounds');showAnimationBoundsCheckbox.title='When checked, show a border around'+' a layer showing the extent of its animation.';Polymer.dom(this.controls_).appendChild(showAnimationBoundsCheckbox);const showInputEventsCheckbox=tr.ui.b.createCheckBox(this,'showInputEvents','layerView.showInputEvents',true,'Input events');showInputEventsCheckbox.title='When checked, input events are '+'displayed as circles.';Polymer.dom(this.controls_).appendChild(showInputEventsCheckbox);this.whatRasterizedLink_=document.createElement('tr-ui-a-analysis-link');this.whatRasterizedLink_.style.position='absolute';this.whatRasterizedLink_.style.bottom='15px';this.whatRasterizedLink_.style.left='10px';this.whatRasterizedLink_.selection=this.getWhatRasterizedEventSet_.bind(this);Polymer.dom(this.quadStackView_).appendChild(this.whatRasterizedLink_);},get layerTreeImpl(){return this.layerTreeImpl_;},set isRenderPassQuads(newValue){this.isRenderPassQuads_=newValue;},set layerTreeImpl(layerTreeImpl){if(this.layerTreeImpl_===layerTreeImpl)return;this.layerTreeImpl_=layerTreeImpl;this.selection=undefined;},get extraHighlightsByLayerId(){return this.extraHighlightsByLayerId_;},set extraHighlightsByLayerId(extraHighlightsByLayerId){this.extraHighlightsByLayerId_=extraHighlightsByLayerId;this.scheduleUpdateContents_();},get showOtherLayers(){return this.showOtherLayers_;},set showOtherLayers(show){this.showOtherLayers_=show;this.updateContents_();},get showAnimationBounds(){return this.showAnimationBounds_;},set showAnimationBounds(show){this.showAnimationBounds_=show;this.updateContents_();},get showInputEvents(){return this.showInputEvents_;},set showInputEvents(show){this.showInputEvents_=show;this.updateContents_();},get showContents(){return this.showContents_;},set showContents(show){this.showContents_=show;this.updateContents_();},get showInvalidations(){return this.showInvalidations_;},set showInvalidations(show){this.showInvalidations_=show;this.updateContents_();},get showUnrecordedRegion(){return this.showUnrecordedRegion_;},set showUnrecordedRegion(show){this.showUnrecordedRegion_=show;this.updateContents_();},get showBottlenecks(){return this.showBottlenecks_;},set showBottlenecks(show){this.showBottlenecks_=show;this.updateContents_();},get showLayoutRects(){return this.showLayoutRects_;},set showLayoutRects(show){this.showLayoutRects_=show;this.updateContents_();},get howToShowTiles(){return this.howToShowTiles_;},set howToShowTiles(val){if(val!=='none'&&val!=='coverage'&&isNaN(parseFloat(val))){throw new Error('howToShowTiles requires "none" or "coverage" or a number');}
+this.howToShowTiles_=val;this.updateContents_();},get tileHeatmapType(){return this.tileHeatmapType_;},set tileHeatmapType(val){this.tileHeatmapType_=val;this.updateContents_();},get selection(){return this.selection_;},set selection(selection){if(this.selection===selection)return;this.selection_=selection;tr.b.dispatchSimpleEvent(this,'selection-change');this.updateContents_();},regenerateContent(){this.updateTilesSelector_();this.updateContents_();},loadDataForImageElement_(image,callback){const imageContent=window.getComputedStyle(image).backgroundImage;if(!imageContent){this.scheduleUpdateContents_();return;}
+image.src=tr.ui.b.extractUrlString(imageContent);image.onload=function(){const canvas=document.createElement('canvas');const ctx=canvas.getContext('2d');canvas.width=image.width;canvas.height=image.height;ctx.drawImage(image,0,0);const imageData=ctx.getImageData(0,0,canvas.width,canvas.height);callback(imageData);};},onQuadStackViewSelectionChange_(e){const selectableQuads=e.quads.filter(function(q){return q.selectionToSetIfClicked!==undefined;});if(selectableQuads.length===0){this.selection=undefined;return;}
+selectableQuads.sort(function(x,y){const z=x.stackingGroupId-y.stackingGroupId;if(z!==0)return z;return x.selectionToSetIfClicked.specicifity-
+y.selectionToSetIfClicked.specicifity;});const quadToSelect=selectableQuads[selectableQuads.length-1];this.selection=quadToSelect.selectionToSetIfClicked;},scheduleUpdateContents_(){if(this.updateContentsPending_)return;this.updateContentsPending_=true;tr.b.requestAnimationFrameInThisFrameIfPossible(this.updateContents_,this);},updateContents_(){if(!this.layerTreeImpl_){this.quadStackView_.headerText='No tree';this.quadStackView_.quads=[];return;}
+const status=this.computePictureLoadingStatus_();if(!status.picturesComplete)return;const lthi=this.layerTreeImpl_.layerTreeHostImpl;const lthiInstance=lthi.objectInstance;const worldViewportRect=tr.b.math.Rect.fromXYWH(0,0,lthi.deviceViewportSize.width,lthi.deviceViewportSize.height);this.quadStackView_.deviceRect=worldViewportRect;if(this.isRenderPassQuads_){this.quadStackView_.quads=this.generateRenderPassQuads();}else{this.quadStackView_.quads=this.generateLayerQuads();}
+this.updateWhatRasterizedLinkState_();let message='';if(lthi.tilesHaveGpuMemoryUsageInfo){const thisTreeUsageInBytes=this.layerTreeImpl_.gpuMemoryUsageInBytes;const otherTreeUsageInBytes=lthi.gpuMemoryUsageInBytes-
+thisTreeUsageInBytes;message+=tr.b.convertUnit(thisTreeUsageInBytes,tr.b.UnitPrefixScale.BINARY.NONE,tr.b.UnitPrefixScale.BINARY.MEBI).toFixed(1)+' MiB on this tree';if(otherTreeUsageInBytes){message+=', '+
+tr.b.convertUnit(otherTreeUsageInBytes,tr.b.UnitPrefixScale.BINARY.NONE,tr.b.UnitPrefixScale.BINARY.MEBI).toFixed(1)+' MiB on the other tree';}}else{if(this.layerTreeImpl_){const thisTreeUsageInBytes=this.layerTreeImpl_.gpuMemoryUsageInBytes;message+=tr.b.convertUnit(thisTreeUsageInBytes,tr.b.UnitPrefixScale.BINARY.NONE,tr.b.UnitPrefixScale.BINARY.MEBI).toFixed(1)+' MiB on this tree';if(this.layerTreeImpl_.otherTree){message+=', ??? MiB on other tree. ';}}}
+if(lthi.args.tileManagerBasicState){const tmgs=lthi.args.tileManagerBasicState.globalState;message+=' (softMax='+
+tr.b.convertUnit(tmgs.softMemoryLimitInBytes,tr.b.UnitPrefixScale.BINARY.NONE,tr.b.UnitPrefixScale.BINARY.MEBI).toFixed(1)+' MiB, hardMax='+
+tr.b.convertUnit(tmgs.hardMemoryLimitInBytes,tr.b.UnitPrefixScale.BINARY.NONE,tr.b.UnitPrefixScale.BINARY.MEBI).toFixed(1)+' MiB, '+
+tmgs.memoryLimitPolicy+')';}else{const thread=lthi.snapshottedOnThread;const didManageTilesSlices=thread.sliceGroup.slices.filter(s=>{if(s.category!=='tr.e.cc')return false;if(s.title!=='DidManage')return false;if(s.end>lthi.ts)return false;return true;});didManageTilesSlices.sort(function(x,y){return x.end-y.end;});if(didManageTilesSlices.length>0){const newest=didManageTilesSlices[didManageTilesSlices.length-1];const tmgs=newest.args.state.global_state;message+=' (softMax='+
+tr.b.convertUnit(tmgs.softMemoryLimitInBytes,tr.b.UnitPrefixScale.BINARY.NONE,tr.b.UnitPrefixScale.BINARY.MEBI).toFixed(1)+' MiB, hardMax='+
+tr.b.convertUnit(tmgs.hardMemoryLimitInBytes,tr.b.UnitPrefixScale.BINARY.NONE,tr.b.UnitPrefixScale.BINARY.MEBI).toFixed(1)+' MiB, '+
+tmgs.memoryLimitPolicy+')';}}
+if(this.layerTreeImpl_.otherTree){message+=' (Another tree exists)';}
+if(message.length){this.quadStackView_.headerText=message;}else{this.quadStackView_.headerText=undefined;}
+this.updateInfoBar_(status.messages);},updateTilesSelector_(){const data=createTileRectsSelectorBaseOptions();if(this.layerTreeImpl_){const lthi=this.layerTreeImpl_.layerTreeHostImpl;const scaleNames=lthi.getContentsScaleNames();for(const scale in scaleNames){data.push({label:'Scale '+scale+' ('+scaleNames[scale]+')',value:scale});}}
+const newSelector=tr.ui.b.createSelector(this,'howToShowTiles','layerView.howToShowTiles','none',data);this.controls_.replaceChild(newSelector,this.tileRectsSelector_);this.tileRectsSelector_=newSelector;},computePictureLoadingStatus_(){const layers=this.layers;const status={messages:[],picturesComplete:true};if(this.showContents){let hasPendingRasterizeImage=false;let firstPictureError=undefined;let hasMissingLayerRect=false;let hasUnresolvedPictureRef=false;for(let i=0;i<layers.length;i++){const layer=layers[i];for(let ir=0;ir<layer.pictures.length;++ir){const picture=layer.pictures[ir];if(picture.idRef){hasUnresolvedPictureRef=true;continue;}
+if(!picture.layerRect){hasMissingLayerRect=true;continue;}
+const pictureAsImageData=this.pictureAsImageData_[picture.guid];if(!pictureAsImageData){hasPendingRasterizeImage=true;this.pictureAsImageData_[picture.guid]=tr.e.cc.PictureAsImageData.Pending(this);picture.rasterize({stopIndex:undefined},function(pictureImageData){const picture_=pictureImageData.picture;this.pictureAsImageData_[picture_.guid]=pictureImageData;this.scheduleUpdateContents_();}.bind(this));continue;}
+if(pictureAsImageData.isPending()){hasPendingRasterizeImage=true;continue;}
+if(pictureAsImageData.error){if(!firstPictureError){firstPictureError=pictureAsImageData.error;}
+break;}}}
+if(hasPendingRasterizeImage){status.picturesComplete=false;}else{if(hasUnresolvedPictureRef){status.messages.push({header:'Missing picture',details:'Your trace didn\'t have pictures for every layer. '+'Old chrome versions had this problem'});}
+if(hasMissingLayerRect){status.messages.push({header:'Missing layer rect',details:'Your trace may be corrupt or from a very old '+'Chrome revision.'});}
+if(firstPictureError){status.messages.push({header:'Cannot rasterize',details:firstPictureError});}}}
+if(this.showInputEvents&&this.layerTreeImpl.tracedInputLatencies&&this.inputEventImageData_===undefined){const image=Polymer.dom(this).querySelector('#input-event');if(!image.src){this.loadDataForImageElement_(image,function(imageData){this.inputEventImageData_=imageData;this.updateContentsPending_=false;this.scheduleUpdateContents_();}.bind(this));}
+status.picturesComplete=false;}
+return status;},get selectedRenderPass(){if(this.selection){return this.selection.renderPass_;}},get selectedLayer(){if(this.selection){const selectedLayerId=this.selection.associatedLayerId;return this.layerTreeImpl_.findLayerWithId(selectedLayerId);}},get renderPasses(){let renderPasses=this.layerTreeImpl.layerTreeHostImpl.args.frame.renderPasses;if(!this.showOtherLayers){const selectedRenderPass=this.selectedRenderPass;if(selectedRenderPass){renderPasses=[selectedRenderPass];}}
+return renderPasses;},get layers(){let layers=this.layerTreeImpl.renderSurfaceLayerList;if(!this.showOtherLayers){const selectedLayer=this.selectedLayer;if(selectedLayer){layers=[selectedLayer];}}
+return layers;},appendImageQuads_(quads,layer,layerQuad){for(let ir=0;ir<layer.pictures.length;++ir){const picture=layer.pictures[ir];if(!picture.layerRect)continue;const unitRect=picture.layerRect.asUVRectInside(layer.bounds);const iq=layerQuad.projectUnitRect(unitRect);const pictureData=this.pictureAsImageData_[picture.guid];if(this.showContents&&pictureData&&pictureData.imageData){iq.imageData=pictureData.imageData;iq.borderColor='rgba(0,0,0,0)';}else{iq.imageData=undefined;}
+iq.stackingGroupId=layerQuad.stackingGroupId;quads.push(iq);}},appendAnimationQuads_(quads,layer,layerQuad){if(!layer.animationBoundsRect)return;const rect=layer.animationBoundsRect;const abq=tr.b.math.Quad.fromRect(rect);abq.backgroundColor='rgba(164,191,48,0.5)';abq.borderColor='rgba(205,255,0,0.75)';abq.borderWidth=3.0;abq.stackingGroupId=layerQuad.stackingGroupId;abq.selectionToSetIfClicked=new cc.AnimationRectSelection(layer,rect);quads.push(abq);},appendInvalidationQuads_(quads,layer,layerQuad){if(layer.layerTreeImpl.hasSourceFrameBeenDrawnBefore)return;for(const rect of layer.invalidation.rects){const unitRect=rect.asUVRectInside(layer.bounds);const iq=layerQuad.projectUnitRect(unitRect);iq.backgroundColor='rgba(0, 255, 0, 0.1)';if(rect.reason==='appeared'){iq.backgroundColor='rgba(0, 255, 128, 0.1)';}
+iq.borderColor='rgba(0, 255, 0, 1)';iq.stackingGroupId=layerQuad.stackingGroupId;let message='Invalidation rect';if(rect.reason){message+=' ('+rect.reason+')';}
+if(rect.client){message+=' for '+rect.client;}
+iq.selectionToSetIfClicked=new cc.LayerRectSelection(layer,message,rect,rect);quads.push(iq);}},appendUnrecordedRegionQuads_(quads,layer,layerQuad){for(let ir=0;ir<layer.unrecordedRegion.rects.length;ir++){const rect=layer.unrecordedRegion.rects[ir];const unitRect=rect.asUVRectInside(layer.bounds);const iq=layerQuad.projectUnitRect(unitRect);iq.backgroundColor='rgba(240, 230, 140, 0.3)';iq.borderColor='rgba(240, 230, 140, 1)';iq.stackingGroupId=layerQuad.stackingGroupId;iq.selectionToSetIfClicked=new cc.LayerRectSelection(layer,'Unrecorded area',rect,rect);quads.push(iq);}},appendBottleneckQuads_(quads,layer,layerQuad,stackingGroupId){function processRegion(region,label,borderColor){const backgroundColor=borderColor.clone();backgroundColor.a=0.4*(borderColor.a||1.0);if(!region||!region.rects)return;for(let ir=0;ir<region.rects.length;ir++){const rect=region.rects[ir];const unitRect=rect.asUVRectInside(layer.bounds);const iq=layerQuad.projectUnitRect(unitRect);iq.backgroundColor=backgroundColor.toString();iq.borderColor=borderColor.toString();iq.borderWidth=4.0;iq.stackingGroupId=stackingGroupId;iq.selectionToSetIfClicked=new cc.LayerRectSelection(layer,label,rect,rect);quads.push(iq);}}
+processRegion(layer.touchEventHandlerRegion,'Touch listener',tr.b.Color.fromString('rgb(228, 226, 27)'));processRegion(layer.wheelEventHandlerRegion,'Wheel listener',tr.b.Color.fromString('rgb(176, 205, 29)'));processRegion(layer.nonFastScrollableRegion,'Repaints on scroll',tr.b.Color.fromString('rgb(213, 134, 32)'));},appendTileCoverageRectQuads_(quads,layer,layerQuad,heatmapType){if(!layer.tileCoverageRects)return;const tiles=[];for(let ct=0;ct<layer.tileCoverageRects.length;++ct){const tile=layer.tileCoverageRects[ct].tile;if(tile!==undefined)tiles.push(tile);}
+const lthi=this.layerTreeImpl_.layerTreeHostImpl;const minMax=this.getMinMaxForHeatmap_(lthi.activeTiles,heatmapType);const heatmapResult=this.computeHeatmapColors_(tiles,minMax,heatmapType);let heatIndex=0;for(let ct=0;ct<layer.tileCoverageRects.length;++ct){let rect=layer.tileCoverageRects[ct].geometryRect;rect=rect.scale(1.0/layer.geometryContentsScale);const tile=layer.tileCoverageRects[ct].tile;const unitRect=rect.asUVRectInside(layer.bounds);const quad=layerQuad.projectUnitRect(unitRect);quad.backgroundColor='rgba(0, 0, 0, 0)';quad.stackingGroupId=layerQuad.stackingGroupId;let type=tr.e.cc.tileTypes.missing;if(tile){type=tile.getTypeForLayer(layer);quad.backgroundColor=heatmapResult[heatIndex].color;++heatIndex;}
+quad.borderColor=tr.e.cc.tileBorder[type].color;quad.borderWidth=tr.e.cc.tileBorder[type].width;let label;if(tile){label='coverageRect';}else{label='checkerboard coverageRect';}
+quad.selectionToSetIfClicked=new cc.LayerRectSelection(layer,label,rect,layer.tileCoverageRects[ct]);quads.push(quad);}},appendLayoutRectQuads_(quads,layer,layerQuad){if(!layer.layoutRects){return;}
+for(let ct=0;ct<layer.layoutRects.length;++ct){let rect=layer.layoutRects[ct].geometryRect;rect=rect.scale(1.0/layer.geometryContentsScale);const unitRect=rect.asUVRectInside(layer.bounds);const quad=layerQuad.projectUnitRect(unitRect);quad.backgroundColor='rgba(0, 0, 0, 0)';quad.stackingGroupId=layerQuad.stackingGroupId;quad.borderColor='rgba(0, 0, 200, 0.7)';quad.borderWidth=2;const label='Layout rect';quad.selectionToSetIfClicked=new cc.LayerRectSelection(layer,label,rect);quads.push(quad);}},getValueForHeatmap_(tile,heatmapType){if(heatmapType===TILE_HEATMAP_TYPE.SCHEDULED_PRIORITY){return tile.scheduledPriority===0?undefined:tile.scheduledPriority;}else if(heatmapType===TILE_HEATMAP_TYPE.USING_GPU_MEMORY){if(tile.isSolidColor)return 0.5;return tile.isUsingGpuMemory?0:1;}},getMinMaxForHeatmap_(tiles,heatmapType){const range=new tr.b.math.Range();if(heatmapType===TILE_HEATMAP_TYPE.USING_GPU_MEMORY){range.addValue(0);range.addValue(1);return range;}
+for(let i=0;i<tiles.length;++i){const value=this.getValueForHeatmap_(tiles[i],heatmapType);if(value===undefined)continue;range.addValue(value);}
+if(range.range===0){range.addValue(1);}
+return range;},computeHeatmapColors_(tiles,minMax,heatmapType){const min=minMax.min;const max=minMax.max;const color=function(value){let hue=120*(1-(value-min)/(max-min));if(hue<0)hue=0;return'hsla('+hue+', 100%, 50%, 0.5)';};const values=[];for(let i=0;i<tiles.length;++i){const tile=tiles[i];const value=this.getValueForHeatmap_(tile,heatmapType);const res={value,color:value!==undefined?color(value):undefined};values.push(res);}
+return values;},appendTilesWithScaleQuads_(quads,layer,layerQuad,scale,heatmapType){const lthi=this.layerTreeImpl_.layerTreeHostImpl;const tiles=[];for(let i=0;i<lthi.activeTiles.length;++i){const tile=lthi.activeTiles[i];if(Math.abs(tile.contentsScale-scale)>1e-6){continue;}
+if(layer.layerId!==tile.layerId)continue;tiles.push(tile);}
+const minMax=this.getMinMaxForHeatmap_(lthi.activeTiles,heatmapType);const heatmapResult=this.computeHeatmapColors_(tiles,minMax,heatmapType);for(let i=0;i<tiles.length;++i){const tile=tiles[i];const rect=tile.layerRect;if(!tile.layerRect)continue;const unitRect=rect.asUVRectInside(layer.bounds);const quad=layerQuad.projectUnitRect(unitRect);quad.backgroundColor='rgba(0, 0, 0, 0)';quad.stackingGroupId=layerQuad.stackingGroupId;const type=tile.getTypeForLayer(layer);quad.borderColor=tr.e.cc.tileBorder[type].color;quad.borderWidth=tr.e.cc.tileBorder[type].width;quad.backgroundColor=heatmapResult[i].color;const data={tileType:type};if(heatmapType!==TILE_HEATMAP_TYPE.NONE){data[heatmapType]=heatmapResult[i].value;}
+quad.selectionToSetIfClicked=new cc.TileSelection(tile,data);quads.push(quad);}},appendHighlightQuadsForLayer_(quads,layer,layerQuad,highlights){highlights.forEach(function(highlight){const rect=highlight.rect;const unitRect=rect.asUVRectInside(layer.bounds);const quad=layerQuad.projectUnitRect(unitRect);let colorId=ColorScheme.getColorIdForGeneralPurposeString(highlight.colorKey);const offset=ColorScheme.properties.brightenedOffsets[0];colorId=ColorScheme.getVariantColorId(colorId,offset);const color=ColorScheme.colors[colorId];const quadForDrawing=quad.clone();quadForDrawing.backgroundColor=color.withAlpha(0.5).toString();quadForDrawing.borderColor=color.withAlpha(1.0).darken().toString();quadForDrawing.stackingGroupId=layerQuad.stackingGroupId;quads.push(quadForDrawing);},this);},generateRenderPassQuads(){if(!this.layerTreeImpl.layerTreeHostImpl.args.frame)return[];const renderPasses=this.renderPasses;if(!renderPasses)return[];const quads=[];for(let i=0;i<renderPasses.length;++i){const quadList=renderPasses[i].quadList;for(let j=0;j<quadList.length;++j){const drawQuad=quadList[j];const quad=drawQuad.rectAsTargetSpaceQuad.clone();quad.borderColor='rgb(170, 204, 238)';quad.borderWidth=2;quad.stackingGroupId=i;quads.push(quad);}}
+return quads;},generateLayerQuads(){this.updateContentsPending_=false;const layers=this.layers;const quads=[];let nextStackingGroupId=0;const alreadyVisitedLayerIds={};let selectionHighlightsByLayerId;if(this.selection){selectionHighlightsByLayerId=this.selection.highlightsByLayerId;}else{selectionHighlightsByLayerId={};}
+const extraHighlightsByLayerId=this.extraHighlightsByLayerId||{};for(let i=1;i<=layers.length;i++){const layer=layers[layers.length-i];alreadyVisitedLayerIds[layer.layerId]=true;if(layer.objectInstance.name==='cc::NinePatchLayerImpl'){continue;}
+const layerQuad=layer.layerQuad.clone();if(layer.usingGpuRasterization){const pixelRatio=window.devicePixelRatio||1;layerQuad.borderWidth=2.0*pixelRatio;layerQuad.borderColor='rgba(154,205,50,0.75)';}else{layerQuad.borderColor='rgba(0,0,0,0.75)';}
+layerQuad.stackingGroupId=nextStackingGroupId++;layerQuad.selectionToSetIfClicked=new cc.LayerSelection(layer);layerQuad.layer=layer;if(this.showOtherLayers&&this.selectedLayer===layer){layerQuad.upperBorderColor='rgb(156,189,45)';}
+if(this.showAnimationBounds){this.appendAnimationQuads_(quads,layer,layerQuad);}
+this.appendImageQuads_(quads,layer,layerQuad);quads.push(layerQuad);if(this.showInvalidations){this.appendInvalidationQuads_(quads,layer,layerQuad);}
+if(this.showUnrecordedRegion){this.appendUnrecordedRegionQuads_(quads,layer,layerQuad);}
+if(this.showBottlenecks){this.appendBottleneckQuads_(quads,layer,layerQuad,layerQuad.stackingGroupId);}
+if(this.showLayoutRects){this.appendLayoutRectQuads_(quads,layer,layerQuad);}
+if(this.howToShowTiles==='coverage'){this.appendTileCoverageRectQuads_(quads,layer,layerQuad,this.tileHeatmapType);}else if(this.howToShowTiles!=='none'){this.appendTilesWithScaleQuads_(quads,layer,layerQuad,this.howToShowTiles,this.tileHeatmapType);}
+let highlights;highlights=extraHighlightsByLayerId[layer.layerId];if(highlights){this.appendHighlightQuadsForLayer_(quads,layer,layerQuad,highlights);}
+highlights=selectionHighlightsByLayerId[layer.layerId];if(highlights){this.appendHighlightQuadsForLayer_(quads,layer,layerQuad,highlights);}}
+this.layerTreeImpl.iterLayers(function(layer,depth,isMask,isReplica){if(!this.showOtherLayers&&this.selectedLayer!==layer)return;if(alreadyVisitedLayerIds[layer.layerId])return;const layerQuad=layer.layerQuad;const stackingGroupId=nextStackingGroupId++;if(this.showBottlenecks){this.appendBottleneckQuads_(quads,layer,layerQuad,stackingGroupId);}},this);const tracedInputLatencies=this.layerTreeImpl.tracedInputLatencies;if(this.showInputEvents&&tracedInputLatencies){for(let i=0;i<tracedInputLatencies.length;i++){const coordinatesArray=tracedInputLatencies[i].args.data.coordinates;for(let j=0;j<coordinatesArray.length;j++){const inputQuad=tr.b.math.Quad.fromXYWH(coordinatesArray[j].x-25,coordinatesArray[j].y-25,50,50);inputQuad.borderColor='rgba(0, 0, 0, 0)';inputQuad.imageData=this.inputEventImageData_;quads.push(inputQuad);}}}
+return quads;},updateInfoBar_(infoBarMessages){if(infoBarMessages.length){this.infoBar_.removeAllButtons();this.infoBar_.message='Some problems were encountered...';this.infoBar_.addButton('More info...',function(e){const overlay=new tr.ui.b.Overlay();Polymer.dom(overlay).textContent='';infoBarMessages.forEach(function(message){const title=document.createElement('h3');Polymer.dom(title).textContent=message.header;const details=document.createElement('div');Polymer.dom(details).textContent=message.details;Polymer.dom(overlay).appendChild(title);Polymer.dom(overlay).appendChild(details);});overlay.visible=true;e.stopPropagation();return false;});this.infoBar_.visible=true;}else{this.infoBar_.removeAllButtons();this.infoBar_.message='';this.infoBar_.visible=false;}},getWhatRasterized_(){const lthi=this.layerTreeImpl_.layerTreeHostImpl;const renderProcess=lthi.objectInstance.parent;const tasks=[];for(const event of renderProcess.getDescendantEvents()){if(!(event instanceof tr.model.Slice))continue;const tile=tr.e.cc.getTileFromRasterTaskSlice(event);if(tile===undefined)continue;if(tile.containingSnapshot===lthi){tasks.push(event);}}
+return tasks;},updateWhatRasterizedLinkState_(){const tasks=this.getWhatRasterized_();if(tasks.length){Polymer.dom(this.whatRasterizedLink_).textContent=tasks.length+' raster tasks';this.whatRasterizedLink_.style.display='';}else{Polymer.dom(this.whatRasterizedLink_).textContent='';this.whatRasterizedLink_.style.display='none';}},getWhatRasterizedEventSet_(){return new tr.model.EventSet(this.getWhatRasterized_());}};return{LayerTreeQuadStackView,};});'use strict';tr.exportTo('tr.ui.e.chrome.cc',function(){const constants=tr.e.cc.constants;const LayerView=tr.ui.b.define('tr-ui-e-chrome-cc-layer-view');LayerView.prototype={__proto__:HTMLDivElement.prototype,decorate(){this.style.flexDirection='column';this.style.display='flex';this.layerTreeQuadStackView_=new tr.ui.e.chrome.cc.LayerTreeQuadStackView();this.dragBar_=document.createElement('tr-ui-b-drag-handle');this.analysisEl_=document.createElement('tr-ui-e-chrome-cc-layer-view-analysis');this.analysisEl_.style.flexGrow=0;this.analysisEl_.style.flexShrink=0;this.analysisEl_.style.flexBasis='auto';this.analysisEl_.style.height='150px';this.analysisEl_.style.overflow='auto';this.analysisEl_.addEventListener('requestSelectionChange',this.onRequestSelectionChangeFromAnalysisEl_.bind(this));this.dragBar_.target=this.analysisEl_;Polymer.dom(this).appendChild(this.layerTreeQuadStackView_);Polymer.dom(this).appendChild(this.dragBar_);Polymer.dom(this).appendChild(this.analysisEl_);this.layerTreeQuadStackView_.addEventListener('selection-change',function(){this.layerTreeQuadStackViewSelectionChanged_();}.bind(this));this.layerTreeQuadStackViewSelectionChanged_();},get layerTreeImpl(){return this.layerTreeQuadStackView_.layerTreeImpl;},set layerTreeImpl(newValue){return this.layerTreeQuadStackView_.layerTreeImpl=newValue;},set isRenderPassQuads(newValue){return this.layerTreeQuadStackView_.isRenderPassQuads=newValue;},get selection(){return this.layerTreeQuadStackView_.selection;},set selection(newValue){this.layerTreeQuadStackView_.selection=newValue;},regenerateContent(){this.layerTreeQuadStackView_.regenerateContent();},layerTreeQuadStackViewSelectionChanged_(){const selection=this.layerTreeQuadStackView_.selection;if(selection){this.dragBar_.style.display='';this.analysisEl_.style.display='';Polymer.dom(this.analysisEl_).textContent='';const layer=selection.layer;if(tr.e.cc.PictureSnapshot.CanDebugPicture()&&layer&&layer.args&&layer.args.pictures&&layer.args.pictures.length){Polymer.dom(this.analysisEl_).appendChild(this.createPictureBtn_(layer.args.pictures));}
+const analysis=selection.createAnalysis();Polymer.dom(this.analysisEl_).appendChild(analysis);for(const child of this.analysisEl_.children){child.style.userSelect='text';}}else{this.dragBar_.style.display='none';this.analysisEl_.style.display='none';const analysis=Polymer.dom(this.analysisEl_).firstChild;if(analysis){Polymer.dom(this.analysisEl_).removeChild(analysis);}
+this.layerTreeQuadStackView_.style.height=window.getComputedStyle(this).height;}
+tr.b.dispatchSimpleEvent(this,'selection-change');},createPictureBtn_(pictures){if(!(pictures instanceof Array)){pictures=[pictures];}
+const link=document.createElement('tr-ui-a-analysis-link');link.selection=function(){const layeredPicture=new tr.e.cc.LayeredPicture(pictures);const snapshot=new tr.e.cc.PictureSnapshot(layeredPicture);snapshot.picture=layeredPicture;const selection=new tr.model.EventSet();selection.push(snapshot);return selection;};Polymer.dom(link).textContent='View in Picture Debugger';return link;},onRequestSelectionChangeFromAnalysisEl_(e){if(!(e.selection instanceof tr.ui.e.chrome.cc.Selection)){return;}
+e.stopPropagation();this.selection=e.selection;},get extraHighlightsByLayerId(){return this.layerTreeQuadStackView_.extraHighlightsByLayerId;},set extraHighlightsByLayerId(extraHighlightsByLayerId){this.layerTreeQuadStackView_.extraHighlightsByLayerId=extraHighlightsByLayerId;}};return{LayerView,};});'use strict';tr.exportTo('tr.ui.e.chrome.cc',function(){const LayerTreeHostImplSnapshotView=tr.ui.b.define('tr-ui-e-chrome-cc-layer-tree-host-impl-snapshot-view',tr.ui.analysis.ObjectSnapshotView);LayerTreeHostImplSnapshotView.prototype={__proto__:tr.ui.analysis.ObjectSnapshotView.prototype,decorate(){Polymer.dom(this).classList.add('tr-ui-e-chrome-cc-lthi-s-view');this.style.display='flex';this.style.flexDirection='row';this.style.flexGrow=1;this.style.flexShrink=1;this.style.flexBasis='auto';this.style.minWidth=0;this.selection_=undefined;this.layerPicker_=new tr.ui.e.chrome.cc.LayerPicker();this.layerPicker_.style.flexGrow=0;this.layerPicker_.style.flexShrink=0;this.layerPicker_.style.flexBasis='auto';this.layerPicker_.style.minWidth='200px';this.layerPicker_.addEventListener('selection-change',this.onLayerPickerSelectionChanged_.bind(this));this.layerView_=new tr.ui.e.chrome.cc.LayerView();this.layerView_.addEventListener('selection-change',this.onLayerViewSelectionChanged_.bind(this));this.layerView_.style.flexGrow=1;this.layerView_.style.flexShrink=1;this.layerView_.style.flexBasis='auto';this.layerView_.style.minWidth=0;this.dragHandle_=document.createElement('tr-ui-b-drag-handle');this.dragHandle_.style.flexGrow=0;this.dragHandle_.style.flexShrink=0;this.dragHandle_.style.flexBasis='auto';this.dragHandle_.horizontal=false;this.dragHandle_.target=this.layerPicker_;Polymer.dom(this).appendChild(this.layerPicker_);Polymer.dom(this).appendChild(this.dragHandle_);Polymer.dom(this).appendChild(this.layerView_);this.onLayerViewSelectionChanged_();this.onLayerPickerSelectionChanged_();},get objectSnapshot(){return this.objectSnapshot_;},set objectSnapshot(objectSnapshot){this.objectSnapshot_=objectSnapshot;const lthi=this.objectSnapshot;let layerTreeImpl;if(lthi){layerTreeImpl=lthi.getTree(this.layerPicker_.whichTree);}
+this.layerPicker_.lthiSnapshot=lthi;this.layerView_.layerTreeImpl=layerTreeImpl;this.layerView_.regenerateContent();if(!this.selection_)return;this.selection=this.selection_.findEquivalent(lthi);},get selection(){return this.selection_;},set selection(selection){if(this.selection_===selection)return;this.selection_=selection;this.layerPicker_.selection=selection;this.layerView_.selection=selection;tr.b.dispatchSimpleEvent(this,'cc-selection-change');},onLayerPickerSelectionChanged_(){this.selection_=this.layerPicker_.selection;this.layerView_.selection=this.selection;this.layerView_.layerTreeImpl=this.layerPicker_.layerTreeImpl;this.layerView_.isRenderPassQuads=this.layerPicker_.isRenderPassQuads;this.layerView_.regenerateContent();tr.b.dispatchSimpleEvent(this,'cc-selection-change');},onLayerViewSelectionChanged_(){this.selection_=this.layerView_.selection;this.layerPicker_.selection=this.selection;tr.b.dispatchSimpleEvent(this,'cc-selection-change');},get extraHighlightsByLayerId(){return this.layerView_.extraHighlightsByLayerId;},set extraHighlightsByLayerId(extraHighlightsByLayerId){this.layerView_.extraHighlightsByLayerId=extraHighlightsByLayerId;}};tr.ui.analysis.ObjectSnapshotView.register(LayerTreeHostImplSnapshotView,{typeName:'cc::LayerTreeHostImpl'});return{LayerTreeHostImplSnapshotView,};});'use strict';tr.exportTo('tr.ui.e.chrome.cc',function(){const OPS_TIMING_ITERATIONS=3;const CHART_PADDING_LEFT=65;const CHART_PADDING_RIGHT=40;const AXIS_PADDING_LEFT=60;const AXIS_PADDING_RIGHT=35;const AXIS_PADDING_TOP=25;const AXIS_PADDING_BOTTOM=45;const AXIS_LABEL_PADDING=5;const AXIS_TICK_SIZE=10;const LABEL_PADDING=5;const LABEL_INTERLEAVE_OFFSET=15;const BAR_PADDING=5;const VERTICAL_TICKS=5;const HUE_CHAR_CODE_ADJUSTMENT=5.7;const PictureOpsChartSummaryView=tr.ui.b.define('tr-ui-e-chrome-cc-picture-ops-chart-summary-view');PictureOpsChartSummaryView.prototype={__proto__:HTMLDivElement.prototype,decorate(){this.style.flexGrow=0;this.style.flexShrink=0;this.style.flexBasis='auto';this.style.fontSize=0;this.style.margin=0;this.style.minHeight='200px';this.style.minWidth='200px';this.style.overflow='hidden';this.style.padding=0;this.picture_=undefined;this.pictureDataProcessed_=false;this.chartScale_=window.devicePixelRatio;this.chart_=document.createElement('canvas');this.chartCtx_=this.chart_.getContext('2d');Polymer.dom(this).appendChild(this.chart_);this.opsTimingData_=[];this.chartWidth_=0;this.chartHeight_=0;this.requiresRedraw_=true;this.currentBarMouseOverTarget_=null;this.chart_.addEventListener('mousemove',this.onMouseMove_.bind(this));try{new ResizeObserver(this.onResize_.bind(this)).observe(this);}catch(e){}},get requiresRedraw(){return this.requiresRedraw_;},set requiresRedraw(requiresRedraw){this.requiresRedraw_=requiresRedraw;},get picture(){return this.picture_;},set picture(picture){this.picture_=picture;this.pictureDataProcessed_=false;if(Polymer.dom(this).classList.contains('hidden'))return;this.processPictureData_();this.requiresRedraw=true;this.updateChartContents();},hide(){Polymer.dom(this).classList.add('hidden');this.style.display='none';},show(){Polymer.dom(this).classList.remove('hidden');this.style.display='';if(!this.pictureDataProcessed_){this.processPictureData_();}
+this.requiresRedraw=true;this.updateChartContents();},onMouseMove_(e){const lastBarMouseOverTarget=this.currentBarMouseOverTarget_;this.currentBarMouseOverTarget_=null;const x=e.offsetX;const y=e.offsetY;const chartLeft=CHART_PADDING_LEFT;const chartRight=this.chartWidth_-CHART_PADDING_RIGHT;const chartTop=AXIS_PADDING_TOP;const chartBottom=this.chartHeight_-AXIS_PADDING_BOTTOM;const chartInnerWidth=chartRight-chartLeft;if(x>chartLeft&&x<chartRight&&y>chartTop&&y<chartBottom){this.currentBarMouseOverTarget_=Math.floor((x-chartLeft)/chartInnerWidth*this.opsTimingData_.length);this.currentBarMouseOverTarget_=tr.b.math.clamp(this.currentBarMouseOverTarget_,0,this.opsTimingData_.length-1);}
+if(this.currentBarMouseOverTarget_===lastBarMouseOverTarget)return;this.drawChartContents_();},onResize_(){this.requiresRedraw=true;this.updateChartContents();},updateChartContents(){if(this.requiresRedraw){this.updateChartDimensions_();}
+this.drawChartContents_();},updateChartDimensions_(){this.chartWidth_=this.offsetWidth;this.chartHeight_=this.offsetHeight;this.chart_.width=this.chartWidth_*this.chartScale_;this.chart_.height=this.chartHeight_*this.chartScale_;this.chart_.style.width=this.chartWidth_+'px';this.chart_.style.height=this.chartHeight_+'px';this.chartCtx_.scale(this.chartScale_,this.chartScale_);},processPictureData_(){this.resetOpsTimingData_();this.pictureDataProcessed_=true;if(!this.picture_)return;let ops=this.picture_.getOps();if(!ops)return;ops=this.picture_.tagOpsWithTimings(ops);if(ops[0].cmd_time===undefined)return;this.collapseOpsToTimingBuckets_(ops);},drawChartContents_(){this.clearChartContents_();if(this.opsTimingData_.length===0){this.showNoTimingDataMessage_();return;}
+this.drawChartAxes_();this.drawBars_();this.drawLineAtBottomOfChart_();if(this.currentBarMouseOverTarget_===null)return;this.drawTooltip_();},drawLineAtBottomOfChart_(){this.chartCtx_.strokeStyle='#AAA';this.chartCtx_.moveTo(0,this.chartHeight_-0.5);this.chartCtx_.lineTo(this.chartWidth_,this.chartHeight_-0.5);this.chartCtx_.stroke();},drawTooltip_(){const tooltipData=this.opsTimingData_[this.currentBarMouseOverTarget_];const tooltipTitle=tooltipData.cmd_string;const tooltipTime=tooltipData.cmd_time.toFixed(4);const tooltipWidth=110;const tooltipHeight=40;const chartInnerWidth=this.chartWidth_-CHART_PADDING_RIGHT-
+CHART_PADDING_LEFT;const barWidth=chartInnerWidth/this.opsTimingData_.length;const tooltipOffset=Math.round((tooltipWidth-barWidth)*0.5);const left=CHART_PADDING_LEFT+this.currentBarMouseOverTarget_*barWidth-tooltipOffset;const top=Math.round((this.chartHeight_-tooltipHeight)*0.5);this.chartCtx_.save();this.chartCtx_.shadowOffsetX=0;this.chartCtx_.shadowOffsetY=5;this.chartCtx_.shadowBlur=4;this.chartCtx_.shadowColor='rgba(0,0,0,0.4)';this.chartCtx_.strokeStyle='#888';this.chartCtx_.fillStyle='#EEE';this.chartCtx_.fillRect(left,top,tooltipWidth,tooltipHeight);this.chartCtx_.shadowColor='transparent';this.chartCtx_.translate(0.5,0.5);this.chartCtx_.strokeRect(left,top,tooltipWidth,tooltipHeight);this.chartCtx_.restore();this.chartCtx_.fillStyle='#222';this.chartCtx_.textBaseline='top';this.chartCtx_.font='800 12px Arial';this.chartCtx_.fillText(tooltipTitle,left+8,top+8);this.chartCtx_.fillStyle='#555';this.chartCtx_.textBaseline='top';this.chartCtx_.font='400 italic 10px Arial';this.chartCtx_.fillText('Total: '+tooltipTime+'ms',left+8,top+22);},drawBars_(){const len=this.opsTimingData_.length;const max=this.opsTimingData_[0].cmd_time;const min=this.opsTimingData_[len-1].cmd_time;const width=this.chartWidth_-CHART_PADDING_LEFT-CHART_PADDING_RIGHT;const height=this.chartHeight_-AXIS_PADDING_TOP-AXIS_PADDING_BOTTOM;const barWidth=Math.floor(width/len);let opData;let opTiming;let opHeight;let opLabel;let barLeft;for(let b=0;b<len;b++){opData=this.opsTimingData_[b];opTiming=opData.cmd_time/max;opHeight=Math.round(Math.max(1,opTiming*height));opLabel=opData.cmd_string;barLeft=CHART_PADDING_LEFT+b*barWidth;this.chartCtx_.fillStyle=this.getOpColor_(opLabel);this.chartCtx_.fillRect(barLeft+BAR_PADDING,AXIS_PADDING_TOP+
+height-opHeight,barWidth-2*BAR_PADDING,opHeight);}},getOpColor_(opName){const characters=opName.split('');const hue=characters.reduce(this.reduceNameToHue,0)%360;return'hsl('+hue+', 30%, 50%)';},reduceNameToHue(previousValue,currentValue,index,array){return Math.round(previousValue+currentValue.charCodeAt(0)*HUE_CHAR_CODE_ADJUSTMENT);},drawChartAxes_(){const len=this.opsTimingData_.length;const max=this.opsTimingData_[0].cmd_time;const min=this.opsTimingData_[len-1].cmd_time;const width=this.chartWidth_-AXIS_PADDING_LEFT-AXIS_PADDING_RIGHT;const height=this.chartHeight_-AXIS_PADDING_TOP-AXIS_PADDING_BOTTOM;const totalBarWidth=this.chartWidth_-CHART_PADDING_LEFT-
+CHART_PADDING_RIGHT;const barWidth=Math.floor(totalBarWidth/len);const tickYInterval=height/(VERTICAL_TICKS-1);let tickYPosition=0;const tickValInterval=(max-min)/(VERTICAL_TICKS-1);let tickVal=0;this.chartCtx_.fillStyle='#333';this.chartCtx_.strokeStyle='#777';this.chartCtx_.save();this.chartCtx_.translate(0.5,0.5);this.chartCtx_.save();this.chartCtx_.translate(AXIS_PADDING_LEFT,AXIS_PADDING_TOP);this.chartCtx_.moveTo(0,0);this.chartCtx_.lineTo(0,height);this.chartCtx_.lineTo(width,height);this.chartCtx_.font='10px Arial';this.chartCtx_.textAlign='right';this.chartCtx_.textBaseline='middle';for(let t=0;t<VERTICAL_TICKS;t++){tickYPosition=Math.round(t*tickYInterval);tickVal=(max-t*tickValInterval).toFixed(4);this.chartCtx_.moveTo(0,tickYPosition);this.chartCtx_.lineTo(-AXIS_TICK_SIZE,tickYPosition);this.chartCtx_.fillText(tickVal,-AXIS_TICK_SIZE-AXIS_LABEL_PADDING,tickYPosition);}
+this.chartCtx_.stroke();this.chartCtx_.restore();this.chartCtx_.save();this.chartCtx_.translate(CHART_PADDING_LEFT+Math.round(barWidth*0.5),AXIS_PADDING_TOP+height+LABEL_PADDING);this.chartCtx_.font='10px Arial';this.chartCtx_.textAlign='center';this.chartCtx_.textBaseline='top';let labelTickLeft;let labelTickBottom;for(let l=0;l<len;l++){labelTickLeft=Math.round(l*barWidth);labelTickBottom=l%2*LABEL_INTERLEAVE_OFFSET;this.chartCtx_.save();this.chartCtx_.moveTo(labelTickLeft,-LABEL_PADDING);this.chartCtx_.lineTo(labelTickLeft,labelTickBottom);this.chartCtx_.stroke();this.chartCtx_.restore();this.chartCtx_.fillText(this.opsTimingData_[l].cmd_string,labelTickLeft,labelTickBottom);}
+this.chartCtx_.restore();this.chartCtx_.restore();},clearChartContents_(){this.chartCtx_.clearRect(0,0,this.chartWidth_,this.chartHeight_);},showNoTimingDataMessage_(){this.chartCtx_.font='800 italic 14px Arial';this.chartCtx_.fillStyle='#333';this.chartCtx_.textAlign='center';this.chartCtx_.textBaseline='middle';this.chartCtx_.fillText('No timing data available.',this.chartWidth_*0.5,this.chartHeight_*0.5);},collapseOpsToTimingBuckets_(ops){const opsTimingDataIndexHash_={};const timingData=this.opsTimingData_;let op;let opIndex;for(let i=0;i<ops.length;i++){op=ops[i];if(op.cmd_time===undefined)continue;opIndex=opsTimingDataIndexHash_[op.cmd_string]||null;if(opIndex===null){timingData.push({cmd_time:0,cmd_string:op.cmd_string});opIndex=timingData.length-1;opsTimingDataIndexHash_[op.cmd_string]=opIndex;}
+timingData[opIndex].cmd_time+=op.cmd_time;}
+timingData.sort(this.sortTimingBucketsByOpTimeDescending_);this.collapseTimingBucketsToOther_(4);},collapseTimingBucketsToOther_(count){const timingData=this.opsTimingData_;const otherSource=timingData.splice(count,timingData.length-count);let otherDestination=null;if(!otherSource.length)return;timingData.push({cmd_time:0,cmd_string:'Other'});otherDestination=timingData[timingData.length-1];for(let i=0;i<otherSource.length;i++){otherDestination.cmd_time+=otherSource[i].cmd_time;}},sortTimingBucketsByOpTimeDescending_(a,b){return b.cmd_time-a.cmd_time;},resetOpsTimingData_(){this.opsTimingData_.length=0;}};return{PictureOpsChartSummaryView,};});'use strict';tr.exportTo('tr.ui.e.chrome.cc',function(){const BAR_PADDING=1;const BAR_WIDTH=5;const CHART_PADDING_LEFT=65;const CHART_PADDING_RIGHT=30;const CHART_PADDING_BOTTOM=35;const CHART_PADDING_TOP=20;const AXIS_PADDING_LEFT=55;const AXIS_PADDING_RIGHT=30;const AXIS_PADDING_BOTTOM=35;const AXIS_PADDING_TOP=20;const AXIS_TICK_SIZE=5;const AXIS_LABEL_PADDING=5;const VERTICAL_TICKS=5;const HUE_CHAR_CODE_ADJUSTMENT=5.7;const PictureOpsChartView=tr.ui.b.define('tr-ui-e-chrome-cc-picture-ops-chart-view');PictureOpsChartView.prototype={__proto__:HTMLDivElement.prototype,decorate(){this.style.display='block';this.style.height='180px';this.style.margin=0;this.style.padding=0;this.style.position='relative';this.picture_=undefined;this.pictureOps_=undefined;this.opCosts_=undefined;this.chartScale_=window.devicePixelRatio;this.chart_=document.createElement('canvas');this.chartCtx_=this.chart_.getContext('2d');Polymer.dom(this).appendChild(this.chart_);this.selectedOpIndex_=undefined;this.chartWidth_=0;this.chartHeight_=0;this.dimensionsHaveChanged_=true;this.currentBarMouseOverTarget_=undefined;this.ninetyFifthPercentileCost_=0;this.totalOpCost_=0;this.chart_.addEventListener('click',this.onClick_.bind(this));this.chart_.addEventListener('mousemove',this.onMouseMove_.bind(this));try{new ResizeObserver(this.onResize_.bind(this)).observe(this);}catch(e){}
+this.usePercentileScale_=false;this.usePercentileScaleCheckbox_=tr.ui.b.createCheckBox(this,'usePercentileScale','PictureOpsChartView.usePercentileScale',false,'Limit to 95%-ile');Polymer.dom(this.usePercentileScaleCheckbox_).classList.add('use-percentile-scale');this.usePercentileScaleCheckbox_.style.position='absolute';this.usePercentileScaleCheckbox_.style.left=0;this.usePercentileScaleCheckbox_.style.top=0;Polymer.dom(this).appendChild(this.usePercentileScaleCheckbox_);},get dimensionsHaveChanged(){return this.dimensionsHaveChanged_;},set dimensionsHaveChanged(dimensionsHaveChanged){this.dimensionsHaveChanged_=dimensionsHaveChanged;},get usePercentileScale(){return this.usePercentileScale_;},set usePercentileScale(usePercentileScale){this.usePercentileScale_=usePercentileScale;this.drawChartContents_();},get numOps(){return this.opCosts_.length;},get selectedOpIndex(){return this.selectedOpIndex_;},set selectedOpIndex(selectedOpIndex){if(selectedOpIndex<0)throw new Error('Invalid index');if(selectedOpIndex>=this.numOps)throw new Error('Invalid index');this.selectedOpIndex_=selectedOpIndex;},get picture(){return this.picture_;},set picture(picture){this.picture_=picture;this.pictureOps_=picture.tagOpsWithTimings(picture.getOps());this.currentBarMouseOverTarget_=undefined;this.processPictureData_();this.dimensionsHaveChanged=true;},processPictureData_(){if(this.pictureOps_===undefined)return;let totalOpCost=0;this.opCosts_=this.pictureOps_.map(function(op){totalOpCost+=op.cmd_time;return op.cmd_time;});this.opCosts_.sort();const ninetyFifthPercentileCostIndex=Math.floor(this.opCosts_.length*0.95);this.ninetyFifthPercentileCost_=this.opCosts_[ninetyFifthPercentileCostIndex];this.maxCost_=this.opCosts_[this.opCosts_.length-1];this.totalOpCost_=totalOpCost;},extractBarIndex_(e){let index=undefined;if(this.pictureOps_===undefined||this.pictureOps_.length===0){return index;}
+const x=e.offsetX;const y=e.offsetY;const totalBarWidth=(BAR_WIDTH+BAR_PADDING)*this.pictureOps_.length;const chartLeft=CHART_PADDING_LEFT;const chartTop=0;const chartBottom=this.chartHeight_-CHART_PADDING_BOTTOM;const chartRight=chartLeft+totalBarWidth;if(x<chartLeft||x>chartRight||y<chartTop||y>chartBottom){return index;}
+index=Math.floor((x-chartLeft)/totalBarWidth*this.pictureOps_.length);index=tr.b.math.clamp(index,0,this.pictureOps_.length-1);return index;},onClick_(e){const barClicked=this.extractBarIndex_(e);if(barClicked===undefined)return;if(barClicked===this.selectedOpIndex){this.selectedOpIndex=undefined;}else{this.selectedOpIndex=barClicked;}
+e.preventDefault();tr.b.dispatchSimpleEvent(this,'selection-changed',false);},onMouseMove_(e){const lastBarMouseOverTarget=this.currentBarMouseOverTarget_;this.currentBarMouseOverTarget_=this.extractBarIndex_(e);if(this.currentBarMouseOverTarget_===lastBarMouseOverTarget){return;}
+this.drawChartContents_();},onResize_(){this.dimensionsHaveChanged=true;this.updateChartContents();},scrollSelectedItemIntoViewIfNecessary(){if(this.selectedOpIndex===undefined){return;}
+const width=this.offsetWidth;const left=this.scrollLeft;const right=left+width;const targetLeft=CHART_PADDING_LEFT+
+(BAR_WIDTH+BAR_PADDING)*this.selectedOpIndex;if(targetLeft>left&&targetLeft<right){return;}
+this.scrollLeft=(targetLeft-width*0.5);},updateChartContents(){if(this.dimensionsHaveChanged){this.updateChartDimensions_();}
+this.drawChartContents_();},updateChartDimensions_(){if(!this.pictureOps_)return;let width=CHART_PADDING_LEFT+CHART_PADDING_RIGHT+
+((BAR_WIDTH+BAR_PADDING)*this.pictureOps_.length);if(width<this.offsetWidth){width=this.offsetWidth;}
+this.chartWidth_=width;this.chartHeight_=this.getBoundingClientRect().height;this.chart_.width=this.chartWidth_*this.chartScale_;this.chart_.height=this.chartHeight_*this.chartScale_;this.chart_.style.width=this.chartWidth_+'px';this.chart_.style.height=this.chartHeight_+'px';this.chartCtx_.scale(this.chartScale_,this.chartScale_);this.dimensionsHaveChanged=false;},drawChartContents_(){this.clearChartContents_();if(this.pictureOps_===undefined||this.pictureOps_.length===0||this.pictureOps_[0].cmd_time===undefined){this.showNoTimingDataMessage_();return;}
+this.drawSelection_();this.drawBars_();this.drawChartAxes_();this.drawLinesAtTickMarks_();this.drawLineAtBottomOfChart_();if(this.currentBarMouseOverTarget_===undefined){return;}
+this.drawTooltip_();},drawSelection_(){if(this.selectedOpIndex===undefined){return;}
+const width=(BAR_WIDTH+BAR_PADDING)*this.selectedOpIndex;this.chartCtx_.fillStyle='rgb(223, 235, 230)';this.chartCtx_.fillRect(CHART_PADDING_LEFT,CHART_PADDING_TOP,width,this.chartHeight_-CHART_PADDING_TOP-CHART_PADDING_BOTTOM);},drawChartAxes_(){const min=this.opCosts_[0];const max=this.opCosts_[this.opCosts_.length-1];const height=this.chartHeight_-AXIS_PADDING_TOP-AXIS_PADDING_BOTTOM;const tickYInterval=height/(VERTICAL_TICKS-1);let tickYPosition=0;const tickValInterval=(max-min)/(VERTICAL_TICKS-1);let tickVal=0;this.chartCtx_.fillStyle='#333';this.chartCtx_.strokeStyle='#777';this.chartCtx_.save();this.chartCtx_.translate(0.5,0.5);this.chartCtx_.beginPath();this.chartCtx_.moveTo(AXIS_PADDING_LEFT,AXIS_PADDING_TOP);this.chartCtx_.lineTo(AXIS_PADDING_LEFT,this.chartHeight_-
+AXIS_PADDING_BOTTOM);this.chartCtx_.lineTo(this.chartWidth_-AXIS_PADDING_RIGHT,this.chartHeight_-AXIS_PADDING_BOTTOM);this.chartCtx_.stroke();this.chartCtx_.closePath();this.chartCtx_.translate(AXIS_PADDING_LEFT,AXIS_PADDING_TOP);this.chartCtx_.font='10px Arial';this.chartCtx_.textAlign='right';this.chartCtx_.textBaseline='middle';this.chartCtx_.beginPath();for(let t=0;t<VERTICAL_TICKS;t++){tickYPosition=Math.round(t*tickYInterval);tickVal=(max-t*tickValInterval).toFixed(4);this.chartCtx_.moveTo(0,tickYPosition);this.chartCtx_.lineTo(-AXIS_TICK_SIZE,tickYPosition);this.chartCtx_.fillText(tickVal,-AXIS_TICK_SIZE-AXIS_LABEL_PADDING,tickYPosition);}
+this.chartCtx_.stroke();this.chartCtx_.closePath();this.chartCtx_.restore();},drawLinesAtTickMarks_(){const height=this.chartHeight_-AXIS_PADDING_TOP-AXIS_PADDING_BOTTOM;const width=this.chartWidth_-AXIS_PADDING_LEFT-AXIS_PADDING_RIGHT;const tickYInterval=height/(VERTICAL_TICKS-1);let tickYPosition=0;this.chartCtx_.save();this.chartCtx_.translate(AXIS_PADDING_LEFT+0.5,AXIS_PADDING_TOP+0.5);this.chartCtx_.beginPath();this.chartCtx_.strokeStyle='rgba(0,0,0,0.05)';for(let t=0;t<VERTICAL_TICKS;t++){tickYPosition=Math.round(t*tickYInterval);this.chartCtx_.moveTo(0,tickYPosition);this.chartCtx_.lineTo(width,tickYPosition);this.chartCtx_.stroke();}
+this.chartCtx_.restore();this.chartCtx_.closePath();},drawLineAtBottomOfChart_(){this.chartCtx_.strokeStyle='#AAA';this.chartCtx_.beginPath();this.chartCtx_.moveTo(0,this.chartHeight_-0.5);this.chartCtx_.lineTo(this.chartWidth_,this.chartHeight_-0.5);this.chartCtx_.stroke();this.chartCtx_.closePath();},drawTooltip_(){const tooltipData=this.pictureOps_[this.currentBarMouseOverTarget_];const tooltipTitle=tooltipData.cmd_string;const tooltipTime=tooltipData.cmd_time.toFixed(4);const toolTipTimePercentage=((tooltipData.cmd_time/this.totalOpCost_)*100).toFixed(2);const tooltipWidth=120;const tooltipHeight=40;const chartInnerWidth=this.chartWidth_-CHART_PADDING_RIGHT-
+CHART_PADDING_LEFT;const barWidth=BAR_WIDTH+BAR_PADDING;const tooltipOffset=Math.round((tooltipWidth-barWidth)*0.5);const left=CHART_PADDING_LEFT+this.currentBarMouseOverTarget_*barWidth-tooltipOffset;const top=Math.round((this.chartHeight_-tooltipHeight)*0.5);this.chartCtx_.save();this.chartCtx_.shadowOffsetX=0;this.chartCtx_.shadowOffsetY=5;this.chartCtx_.shadowBlur=4;this.chartCtx_.shadowColor='rgba(0,0,0,0.4)';this.chartCtx_.strokeStyle='#888';this.chartCtx_.fillStyle='#EEE';this.chartCtx_.fillRect(left,top,tooltipWidth,tooltipHeight);this.chartCtx_.shadowColor='transparent';this.chartCtx_.translate(0.5,0.5);this.chartCtx_.strokeRect(left,top,tooltipWidth,tooltipHeight);this.chartCtx_.restore();this.chartCtx_.fillStyle='#222';this.chartCtx_.textAlign='left';this.chartCtx_.textBaseline='top';this.chartCtx_.font='800 12px Arial';this.chartCtx_.fillText(tooltipTitle,left+8,top+8);this.chartCtx_.fillStyle='#555';this.chartCtx_.font='400 italic 10px Arial';this.chartCtx_.fillText(tooltipTime+'ms ('+
+toolTipTimePercentage+'%)',left+8,top+22);},drawBars_(){let op;let opColor=0;let opHeight=0;const opWidth=BAR_WIDTH+BAR_PADDING;let opHover=false;const bottom=this.chartHeight_-CHART_PADDING_BOTTOM;const maxHeight=this.chartHeight_-CHART_PADDING_BOTTOM-
+CHART_PADDING_TOP;let maxValue;if(this.usePercentileScale){maxValue=this.ninetyFifthPercentileCost_;}else{maxValue=this.maxCost_;}
+for(let b=0;b<this.pictureOps_.length;b++){op=this.pictureOps_[b];opHeight=Math.round((op.cmd_time/maxValue)*maxHeight);opHeight=Math.max(opHeight,1);opHover=(b===this.currentBarMouseOverTarget_);opColor=this.getOpColor_(op.cmd_string,opHover);if(b===this.selectedOpIndex){this.chartCtx_.fillStyle='#FFFF00';}else{this.chartCtx_.fillStyle=opColor;}
+this.chartCtx_.fillRect(CHART_PADDING_LEFT+b*opWidth,bottom-opHeight,BAR_WIDTH,opHeight);}},getOpColor_(opName,hover){const characters=opName.split('');const hue=characters.reduce(this.reduceNameToHue,0)%360;const saturation=30;const lightness=hover?'75%':'50%';return'hsl('+hue+', '+saturation+'%, '+lightness+'%)';},reduceNameToHue(previousValue,currentValue,index,array){return Math.round(previousValue+currentValue.charCodeAt(0)*HUE_CHAR_CODE_ADJUSTMENT);},clearChartContents_(){this.chartCtx_.clearRect(0,0,this.chartWidth_,this.chartHeight_);},showNoTimingDataMessage_(){this.chartCtx_.font='800 italic 14px Arial';this.chartCtx_.fillStyle='#333';this.chartCtx_.textAlign='center';this.chartCtx_.textBaseline='middle';this.chartCtx_.fillText('No timing data available.',this.chartWidth_*0.5,this.chartHeight_*0.5);}};return{PictureOpsChartView,};});'use strict';tr.exportTo('tr.ui.e.chrome.cc',function(){const THIS_DOC=document._currentScript.ownerDocument;const PictureDebugger=tr.ui.b.define('tr-ui-e-chrome-cc-picture-debugger');PictureDebugger.prototype={__proto__:HTMLDivElement.prototype,decorate(){const node=tr.ui.b.instantiateTemplate('#tr-ui-e-chrome-cc-picture-debugger-template',THIS_DOC);Polymer.dom(this).appendChild(node);this.style.display='flex';this.style.flexDirection='row';const title=this.querySelector('.title');title.style.fontWeight='bold';title.style.marginLeft='5px';title.style.marginRight='5px';this.pictureAsImageData_=undefined;this.showOverdraw_=false;this.zoomScaleValue_=1;this.sizeInfo_=Polymer.dom(this).querySelector('.size');this.rasterArea_=Polymer.dom(this).querySelector('raster-area');this.rasterArea_.style.backgroundColor='#ddd';this.rasterArea_.style.minHeight='100px';this.rasterArea_.style.minWidth='200px';this.rasterArea_.style.overflow='auto';this.rasterArea_.style.paddingLeft='5px';this.rasterCanvas_=Polymer.dom(this.rasterArea_).querySelector('canvas');this.rasterCtx_=this.rasterCanvas_.getContext('2d');this.filename_=Polymer.dom(this).querySelector('.filename');this.filename_.style.userSelect='text';this.filename_.style.marginLeft='5px';this.drawOpsChartSummaryView_=new tr.ui.e.chrome.cc.PictureOpsChartSummaryView();this.drawOpsChartView_=new tr.ui.e.chrome.cc.PictureOpsChartView();this.drawOpsChartView_.addEventListener('selection-changed',this.onChartBarClicked_.bind(this));this.exportButton_=Polymer.dom(this).querySelector('.export');this.exportButton_.addEventListener('click',this.onSaveAsSkPictureClicked_.bind(this));this.trackMouse_();const overdrawCheckbox=tr.ui.b.createCheckBox(this,'showOverdraw','pictureView.showOverdraw',false,'Show overdraw');const chartCheckbox=tr.ui.b.createCheckBox(this,'showSummaryChart','pictureView.showSummaryChart',false,'Show timing summary');const pictureInfo=Polymer.dom(this).querySelector('picture-info');pictureInfo.style.flexGrow=0;pictureInfo.style.flexShrink=0;pictureInfo.style.flexBasis='auto';pictureInfo.style.paddingTop='2px';Polymer.dom(pictureInfo).appendChild(overdrawCheckbox);Polymer.dom(pictureInfo).appendChild(chartCheckbox);this.drawOpsView_=new tr.ui.e.chrome.cc.PictureOpsListView();this.drawOpsView_.flexGrow=1;this.drawOpsView_.flexShrink=1;this.drawOpsView_.flexBasis='auto';this.drawOpsView_.addEventListener('selection-changed',this.onChangeDrawOps_.bind(this));const leftPanel=Polymer.dom(this).querySelector('left-panel');leftPanel.style.flexDirection='column';leftPanel.style.display='flex';leftPanel.style.flexGrow=0;leftPanel.style.flexShrink=0;leftPanel.style.flexBasis='auto';leftPanel.style.minWidth='200px';leftPanel.style.overflow='auto';Polymer.dom(leftPanel).appendChild(this.drawOpsChartSummaryView_);Polymer.dom(leftPanel).appendChild(this.drawOpsView_);const middleDragHandle=document.createElement('tr-ui-b-drag-handle');middleDragHandle.style.flexGrow=0;middleDragHandle.style.flexShrink=0;middleDragHandle.style.flexBasis='auto';middleDragHandle.horizontal=false;middleDragHandle.target=leftPanel;const rightPanel=Polymer.dom(this).querySelector('right-panel');rightPanel.style.flexGrow=1;rightPanel.style.flexShrink=1;rightPanel.style.flexBasis='auto';rightPanel.style.minWidth=0;rightPanel.style.flexDirection='column';rightPanel.style.display='flex';const chartView=Polymer.dom(rightPanel).querySelector('tr-ui-e-chrome-cc-picture-ops-chart-view');this.drawOpsChartView_.style.flexGrow=0;this.drawOpsChartView_.style.flexShrink=0;this.drawOpsChartView_.style.flexBasis='auto';this.drawOpsChartView_.style.minWidth=0;this.drawOpsChartView_.style.overflowX='auto';this.drawOpsChartView_.style.overflowY='hidden';rightPanel.replaceChild(this.drawOpsChartView_,chartView);this.infoBar_=document.createElement('tr-ui-b-info-bar');Polymer.dom(this.rasterArea_).appendChild(this.infoBar_);Polymer.dom(this).insertBefore(middleDragHandle,rightPanel);this.picture_=undefined;const hkc=document.createElement('tv-ui-b-hotkey-controller');hkc.addHotKey(new tr.ui.b.HotKey({eventType:'keypress',thisArg:this,keyCode:'h'.charCodeAt(0),callback(e){this.moveSelectedOpBy(-1);e.stopPropagation();}}));hkc.addHotKey(new tr.ui.b.HotKey({eventType:'keypress',thisArg:this,keyCode:'l'.charCodeAt(0),callback(e){this.moveSelectedOpBy(1);e.stopPropagation();}}));Polymer.dom(this).appendChild(hkc);},onSaveAsSkPictureClicked_(){const rawData=tr.b.Base64.atob(this.picture_.getBase64SkpData());const length=rawData.length;const arrayBuffer=new ArrayBuffer(length);const uint8Array=new Uint8Array(arrayBuffer);for(let c=0;c<length;c++){uint8Array[c]=rawData.charCodeAt(c);}
+const blob=new Blob([uint8Array],{type:'application/octet-binary'});const blobUrl=window.webkitURL.createObjectURL(blob);const link=document.createElementNS('http://www.w3.org/1999/xhtml','a');link.href=blobUrl;link.download=this.filename_.value;const event=document.createEvent('MouseEvents');event.initMouseEvent('click',true,false,window,0,0,0,0,0,false,false,false,false,0,null);link.dispatchEvent(event);},get picture(){return this.picture_;},set picture(picture){this.drawOpsView_.picture=picture;this.drawOpsChartView_.picture=picture;this.drawOpsChartSummaryView_.picture=picture;this.picture_=picture;this.exportButton_.disabled=!this.picture_.canSave;if(picture){const size=this.getRasterCanvasSize_();this.rasterCanvas_.width=size.width;this.rasterCanvas_.height=size.height;}
+const bounds=this.rasterArea_.getBoundingClientRect();const selectorBounds=this.mouseModeSelector_.getBoundingClientRect();this.mouseModeSelector_.pos={x:(bounds.right-selectorBounds.width-10),y:bounds.top};this.rasterize_();this.scheduleUpdateContents_();},getRasterCanvasSize_(){const style=window.getComputedStyle(this.rasterArea_);const width=Math.max(parseInt(style.width),this.picture_.layerRect.width);const height=Math.max(parseInt(style.height),this.picture_.layerRect.height);return{width,height};},scheduleUpdateContents_(){if(this.updateContentsPending_)return;this.updateContentsPending_=true;tr.b.requestAnimationFrameInThisFrameIfPossible(this.updateContents_.bind(this));},updateContents_(){this.updateContentsPending_=false;if(this.picture_){Polymer.dom(this.sizeInfo_).textContent='('+
+this.picture_.layerRect.width+' x '+
+this.picture_.layerRect.height+')';}
+this.drawOpsChartView_.updateChartContents();this.drawOpsChartView_.scrollSelectedItemIntoViewIfNecessary();if(!this.pictureAsImageData_)return;this.infoBar_.visible=false;this.infoBar_.removeAllButtons();if(this.pictureAsImageData_.error){this.infoBar_.message='Cannot rasterize...';this.infoBar_.addButton('More info...',function(e){const overlay=new tr.ui.b.Overlay();Polymer.dom(overlay).textContent=this.pictureAsImageData_.error;overlay.visible=true;e.stopPropagation();return false;}.bind(this));this.infoBar_.visible=true;}
+this.drawPicture_();},drawPicture_(){const size=this.getRasterCanvasSize_();if(size.width!==this.rasterCanvas_.width){this.rasterCanvas_.width=size.width;}
+if(size.height!==this.rasterCanvas_.height){this.rasterCanvas_.height=size.height;}
+this.rasterCtx_.clearRect(0,0,size.width,size.height);if(!this.pictureAsImageData_.imageData)return;const imgCanvas=this.pictureAsImageData_.asCanvas();const w=imgCanvas.width;const h=imgCanvas.height;this.rasterCtx_.drawImage(imgCanvas,0,0,w,h,0,0,w*this.zoomScaleValue_,h*this.zoomScaleValue_);},rasterize_(){if(this.picture_){this.picture_.rasterize({stopIndex:this.drawOpsView_.selectedOpIndex,showOverdraw:this.showOverdraw_},this.onRasterComplete_.bind(this));}},onRasterComplete_(pictureAsImageData){this.pictureAsImageData_=pictureAsImageData;this.scheduleUpdateContents_();},moveSelectedOpBy(increment){if(this.selectedOpIndex===undefined){this.selectedOpIndex=0;return;}
+this.selectedOpIndex=tr.b.math.clamp(this.selectedOpIndex+increment,0,this.numOps);},get numOps(){return this.drawOpsView_.numOps;},get selectedOpIndex(){return this.drawOpsView_.selectedOpIndex;},set selectedOpIndex(index){this.drawOpsView_.selectedOpIndex=index;this.drawOpsChartView_.selectedOpIndex=index;},onChartBarClicked_(e){this.drawOpsView_.selectedOpIndex=this.drawOpsChartView_.selectedOpIndex;},onChangeDrawOps_(e){this.rasterize_();this.scheduleUpdateContents_();this.drawOpsChartView_.selectedOpIndex=this.drawOpsView_.selectedOpIndex;},set showOverdraw(v){this.showOverdraw_=v;this.rasterize_();},set showSummaryChart(chartShouldBeVisible){if(chartShouldBeVisible){this.drawOpsChartSummaryView_.show();}else{this.drawOpsChartSummaryView_.hide();}},trackMouse_(){this.mouseModeSelector_=document.createElement('tr-ui-b-mouse-mode-selector');this.mouseModeSelector_.targetElement=this.rasterArea_;Polymer.dom(this.rasterArea_).appendChild(this.mouseModeSelector_);this.mouseModeSelector_.supportedModeMask=tr.ui.b.MOUSE_SELECTOR_MODE.ZOOM;this.mouseModeSelector_.mode=tr.ui.b.MOUSE_SELECTOR_MODE.ZOOM;this.mouseModeSelector_.defaultMode=tr.ui.b.MOUSE_SELECTOR_MODE.ZOOM;this.mouseModeSelector_.settingsKey='pictureDebugger.mouseModeSelector';this.mouseModeSelector_.addEventListener('beginzoom',this.onBeginZoom_.bind(this));this.mouseModeSelector_.addEventListener('updatezoom',this.onUpdateZoom_.bind(this));this.mouseModeSelector_.addEventListener('endzoom',this.onEndZoom_.bind(this));},onBeginZoom_(e){this.isZooming_=true;this.lastMouseViewPos_=this.extractRelativeMousePosition_(e);e.preventDefault();},onUpdateZoom_(e){if(!this.isZooming_)return;const currentMouseViewPos=this.extractRelativeMousePosition_(e);this.zoomScaleValue_+=((this.lastMouseViewPos_.y-currentMouseViewPos.y)*0.001);this.zoomScaleValue_=Math.max(this.zoomScaleValue_,0.1);this.drawPicture_();this.lastMouseViewPos_=currentMouseViewPos;},onEndZoom_(e){this.lastMouseViewPos_=undefined;this.isZooming_=false;e.preventDefault();},extractRelativeMousePosition_(e){return{x:e.clientX-this.rasterArea_.offsetLeft,y:e.clientY-this.rasterArea_.offsetTop};}};return{PictureDebugger,};});'use strict';tr.exportTo('tr.ui.e.chrome.cc',function(){const PictureSnapshotView=tr.ui.b.define('tr-ui-e-chrome-cc-picture-snapshot-view',tr.ui.analysis.ObjectSnapshotView);PictureSnapshotView.prototype={__proto__:tr.ui.analysis.ObjectSnapshotView.prototype,decorate(){Polymer.dom(this).classList.add('tr-ui-e-chrome-cc-picture-snapshot-view');this.style.display='flex';this.style.flexGrow=1;this.style.flexShrink=1;this.style.flexBasis='auto';this.style.minWidth=0;this.pictureDebugger_=new tr.ui.e.chrome.cc.PictureDebugger();this.pictureDebugger_.style.flexGrow=1;this.pictureDebugger_.style.flexShrink=1;this.pictureDebugger_.style.flexBasis='auto';this.pictureDebugger_.style.minWidth=0;Polymer.dom(this).appendChild(this.pictureDebugger_);},updateContents(){if(this.objectSnapshot_&&this.pictureDebugger_){this.pictureDebugger_.picture=this.objectSnapshot_;}}};tr.ui.analysis.ObjectSnapshotView.register(PictureSnapshotView,{typeNames:['cc::Picture','cc::LayeredPicture'],showInstances:false});return{PictureSnapshotView,};});'use strict';tr.exportTo('tr.e.cc',function(){const knownRasterTaskNames=['TileManager::RunRasterTask','RasterWorkerPoolTaskImpl::RunRasterOnThread','RasterWorkerPoolTaskImpl::Raster','RasterTaskImpl::Raster','cc::RasterTask','RasterTask'];const knownAnalysisTaskNames=['TileManager::RunAnalyzeTask','RasterWorkerPoolTaskImpl::RunAnalysisOnThread','RasterWorkerPoolTaskImpl::Analyze','RasterTaskImpl::Analyze','cc::AnalyzeTask','AnalyzeTask'];function getTileFromRasterTaskSlice(slice){if(!(isSliceDoingRasterization(slice)||isSliceDoingAnalysis(slice))){return undefined;}
+let tileData;if(slice.args.data){tileData=slice.args.data;}else{tileData=slice.args.tileData;}
+if(tileData===undefined)return undefined;if(tileData.tile_id)return tileData.tile_id;const tile=tileData.tileId;if(!(tile instanceof tr.e.cc.TileSnapshot)){return undefined;}
+return tileData.tileId;}
+function isSliceDoingRasterization(slice){return knownRasterTaskNames.includes(slice.title);}
+function isSliceDoingAnalysis(slice){return knownAnalysisTaskNames.includes(slice.title);}
+return{getTileFromRasterTaskSlice,isSliceDoingRasterization,isSliceDoingAnalysis};});'use strict';tr.exportTo('tr.ui.analysis',function(){const AnalysisSubView={set tabLabel(label){Polymer.dom(this).setAttribute('tab-label',label);},get tabLabel(){return this.getAttribute('tab-label');},get requiresTallView(){return false;},get relatedEventsToHighlight(){return undefined;},set selection(selection){throw new Error('Not implemented!');},get selection(){throw new Error('Not implemented!');}};const allTypeInfosByEventProto=new Map();let onlyRootTypeInfosByEventProto=undefined;let eventProtoToRootTypeInfoMap=undefined;function AnalysisSubViewTypeInfo(eventConstructor,options){if(options.multi===undefined){throw new Error('missing field: multi');}
+if(options.title===undefined){throw new Error('missing field: title');}
+this.eventConstructor=eventConstructor;this.singleTagName=undefined;this.singleTitle=undefined;this.multiTagName=undefined;this.multiTitle=undefined;this.childrenTypeInfos_=undefined;}
+AnalysisSubViewTypeInfo.prototype={get childrenTypeInfos(){return this.childrenTypeInfos_;},resetchildrenTypeInfos(){this.childrenTypeInfos_=[];}};AnalysisSubView.register=function(tagName,eventConstructor,options){let typeInfo=allTypeInfosByEventProto.get(eventConstructor.prototype);if(typeInfo===undefined){typeInfo=new AnalysisSubViewTypeInfo(eventConstructor,options);allTypeInfosByEventProto.set(typeInfo.eventConstructor.prototype,typeInfo);onlyRootTypeInfosByEventProto=undefined;}
+if(!options.multi){if(typeInfo.singleTagName!==undefined){throw new Error('SingleTagName already set');}
+typeInfo.singleTagName=tagName;typeInfo.singleTitle=options.title;}else{if(typeInfo.multiTagName!==undefined){throw new Error('MultiTagName already set');}
+typeInfo.multiTagName=tagName;typeInfo.multiTitle=options.title;}
+return typeInfo;};function rebuildRootSubViewTypeInfos(){onlyRootTypeInfosByEventProto=new Map();allTypeInfosByEventProto.forEach(function(typeInfo){typeInfo.resetchildrenTypeInfos();});allTypeInfosByEventProto.forEach(function(typeInfo,eventProto){const eventPrototype=typeInfo.eventConstructor.prototype;let lastEventProto=eventPrototype;let curEventProto=eventPrototype.__proto__;while(true){if(!allTypeInfosByEventProto.has(curEventProto)){const rootTypeInfo=allTypeInfosByEventProto.get(lastEventProto);const rootEventProto=lastEventProto;const isNew=onlyRootTypeInfosByEventProto.has(rootEventProto);onlyRootTypeInfosByEventProto.set(rootEventProto,rootTypeInfo);break;}
+lastEventProto=curEventProto;curEventProto=curEventProto.__proto__;}});allTypeInfosByEventProto.forEach(function(typeInfo,eventProto){const eventPrototype=typeInfo.eventConstructor.prototype;const parentEventProto=eventPrototype.__proto__;const parentTypeInfo=allTypeInfosByEventProto.get(parentEventProto);if(!parentTypeInfo)return;parentTypeInfo.childrenTypeInfos.push(typeInfo);});eventProtoToRootTypeInfoMap=new Map();allTypeInfosByEventProto.forEach(function(typeInfo,eventProto){const eventPrototype=typeInfo.eventConstructor.prototype;let curEventProto=eventPrototype;while(true){if(onlyRootTypeInfosByEventProto.has(curEventProto)){const rootTypeInfo=onlyRootTypeInfosByEventProto.get(curEventProto);eventProtoToRootTypeInfoMap.set(eventPrototype,rootTypeInfo);break;}
+curEventProto=curEventProto.__proto__;}});}
+function findLowestTypeInfoForEvents(thisTypeInfo,events){if(events.length===0)return thisTypeInfo;const event0=tr.b.getFirstElement(events);let candidateSubTypeInfo;for(let i=0;i<thisTypeInfo.childrenTypeInfos.length;i++){const childTypeInfo=thisTypeInfo.childrenTypeInfos[i];if(event0 instanceof childTypeInfo.eventConstructor){candidateSubTypeInfo=childTypeInfo;break;}}
+if(!candidateSubTypeInfo)return thisTypeInfo;let allMatch=true;for(const event of events){if(event instanceof candidateSubTypeInfo.eventConstructor)continue;allMatch=false;break;}
+if(!allMatch){return thisTypeInfo;}
+return findLowestTypeInfoForEvents(candidateSubTypeInfo,events);}
+const primaryEventProtoToTypeInfoMap=new Map();function getRootTypeInfoForEvent(event){const curProto=event.__proto__;const typeInfo=primaryEventProtoToTypeInfoMap.get(curProto);if(typeInfo)return typeInfo;return getRootTypeInfoForEventSlow(event);}
+function getRootTypeInfoForEventSlow(event){let typeInfo;let curProto=event.__proto__;while(true){if(curProto===Object.prototype){throw new Error('No view registered for '+event.toString());}
+typeInfo=onlyRootTypeInfosByEventProto.get(curProto);if(typeInfo){primaryEventProtoToTypeInfoMap.set(event.__proto__,typeInfo);return typeInfo;}
+curProto=curProto.__proto__;}}
+AnalysisSubView.getEventsOrganizedByTypeInfo=function(selection){if(onlyRootTypeInfosByEventProto===undefined){rebuildRootSubViewTypeInfos();}
+const eventsByRootTypeInfo=tr.b.groupIntoMap(selection,function(event){return getRootTypeInfoForEvent(event);},this,tr.model.EventSet);const eventsByLowestTypeInfo=new Map();eventsByRootTypeInfo.forEach(function(events,typeInfo){const lowestTypeInfo=findLowestTypeInfoForEvents(typeInfo,events);eventsByLowestTypeInfo.set(lowestTypeInfo,events);});return eventsByLowestTypeInfo;};return{AnalysisSubView,AnalysisSubViewTypeInfo,};});Polymer({is:'tr-ui-a-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView]});'use strict';Polymer({is:'tr-ui-a-stack-frame',ready(){this.stackFrame_=undefined;this.$.table.tableColumns=[];this.$.table.showHeader=true;},get stackFrame(){return this.stackFrame_;},set stackFrame(stackFrame){const table=this.$.table;this.stackFrame_=stackFrame;if(stackFrame===undefined){table.tableColumns=[];table.tableRows=[];table.rebuild();return;}
+let hasName=false;let hasTitle=false;table.tableRows=stackFrame.stackTrace;table.tableRows.forEach(function(row){hasName|=row.name!==undefined;hasTitle|=row.title!==undefined;});const cols=[];if(hasName){cols.push({title:'Name',value(row){return row.name;}});}
+if(hasTitle){cols.push({title:'Title',value(row){return row.title;}});}
+table.tableColumns=cols;table.rebuild();},tableForTesting(){return this.$.table;}});'use strict';Polymer({is:'tr-ui-a-single-event-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],properties:{isFlow:{type:Boolean,value:false}},ready(){this.currentSelection_=undefined;this.$.table.tableColumns=[{title:'Label',value(row){return row.name;},width:'150px'},{title:'Value',width:'100%',value(row){return row.value;}}];this.$.table.showHeader=false;},get selection(){return this.currentSelection_;},set selection(selection){if(selection.length!==1){throw new Error('Only supports single slices');}
+this.setSelectionWithoutErrorChecks(selection);},setSelectionWithoutErrorChecks(selection){this.currentSelection_=selection;this.updateContents_();},getFlowEventRows_(event){const rows=this.getEventRowsHelper_(event);rows.splice(0,0,{name:'ID',value:event.id});function createLinkTo(slice){const linkEl=document.createElement('tr-ui-a-analysis-link');linkEl.setSelectionAndContent(function(){return new tr.model.EventSet(slice);});Polymer.dom(linkEl).textContent=slice.userFriendlyName;return linkEl;}
+rows.push({name:'From',value:createLinkTo(event.startSlice)});rows.push({name:'To',value:createLinkTo(event.endSlice)});return rows;},getEventRowsHelper_(event){const rows=[];if(event.error){rows.push({name:'Error',value:event.error});}
+if(event.title){let title=event.title;if(tr.isExported('tr-ui-e-chrome-codesearch')){const container=document.createElement('div');container.appendChild(document.createTextNode(title));const link=document.createElement('tr-ui-e-chrome-codesearch');link.searchPhrase=title;container.appendChild(link);title=container;}
+rows.push({name:'Title',value:title});}
+if(event.category){rows.push({name:'Category',value:event.category});}
+if(event.model!==undefined){const ufc=event.model.getUserFriendlyCategoryFromEvent(event);if(ufc!==undefined){rows.push({name:'User Friendly Category',value:ufc});}}
+if(event.name){rows.push({name:'Name',value:event.name});}
+rows.push({name:'Start',value:tr.v.ui.createScalarSpan(event.start,{unit:tr.b.Unit.byName.timeStampInMs})});if(event.duration){rows.push({name:'Wall Duration',value:tr.v.ui.createScalarSpan(event.duration,{unit:tr.b.Unit.byName.timeDurationInMs})});}
+if(event.cpuDuration){rows.push({name:'CPU Duration',value:tr.v.ui.createScalarSpan(event.cpuDuration,{unit:tr.b.Unit.byName.timeDurationInMs})});}
+if(event.subSlices!==undefined&&event.subSlices.length!==0){if(event.selfTime){rows.push({name:'Self Time',value:tr.v.ui.createScalarSpan(event.selfTime,{unit:tr.b.Unit.byName.timeDurationInMs})});}
+if(event.cpuSelfTime){const cpuSelfTimeEl=tr.v.ui.createScalarSpan(event.cpuSelfTime,{unit:tr.b.Unit.byName.timeDurationInMs});if(event.cpuSelfTime>event.selfTime){cpuSelfTimeEl.warning=' Note that CPU Self Time is larger than Self Time. '+'This is a known limitation of this system, which occurs '+'due to several subslices, rounding issues, and imprecise '+'time at which we get cpu- and real-time.';}
+rows.push({name:'CPU Self Time',value:cpuSelfTimeEl});}}
+if(event.durationInUserTime){rows.push({name:'Duration (U)',value:tr.v.ui.createScalarSpan(event.durationInUserTime,{unit:tr.b.Unit.byName.timeDurationInMs})});}
+function createStackFrameEl(sf){const sfEl=document.createElement('tr-ui-a-stack-frame');sfEl.stackFrame=sf;return sfEl;}
+if(event.startStackFrame&&event.endStackFrame){if(event.startStackFrame===event.endStackFrame){rows.push({name:'Start+End Stack Trace',value:createStackFrameEl(event.startStackFrame)});}else{rows.push({name:'Start Stack Trace',value:createStackFrameEl(event.startStackFrame)});rows.push({name:'End Stack Trace',value:createStackFrameEl(event.endStackFrame)});}}else if(event.startStackFrame){rows.push({name:'Start Stack Trace',value:createStackFrameEl(event.startStackFrame)});}else if(event.endStackFrame){rows.push({name:'End Stack Trace',value:createStackFrameEl(event.endStackFrame)});}
+if(event.info){const descriptionEl=tr.ui.b.createDiv({textContent:event.info.description,maxWidth:'300px'});rows.push({name:'Description',value:descriptionEl});if(event.info.docLinks){event.info.docLinks.forEach(function(linkObject){const linkEl=document.createElement('a');linkEl.target='_blank';linkEl.href=linkObject.href;Polymer.dom(linkEl).textContent=Polymer.dom(linkObject).textContent;rows.push({name:linkObject.label,value:linkEl});});}}
+if(event.associatedAlerts.length){const alertSubRows=[];event.associatedAlerts.forEach(function(alert){const linkEl=document.createElement('tr-ui-a-analysis-link');linkEl.setSelectionAndContent(function(){return new tr.model.EventSet(alert);},alert.info.description);alertSubRows.push({name:alert.title,value:linkEl});});rows.push({name:'Alerts',value:'',isExpanded:true,subRows:alertSubRows});}
+return rows;},getEventRows_(event){if(this.isFlow){return this.getFlowEventRows_(event);}
+return this.getEventRowsHelper_(event);},addArgsToRows_(rows,args){let n=0;for(const argName in args){n+=1;}
+if(n>0){const subRows=[];for(const argName in args){n+=1;}
+if(n>0){const subRows=[];for(const argName in args){const argView=document.createElement('tr-ui-a-generic-object-view');argView.object=args[argName];subRows.push({name:argName,value:argView});}
+rows.push({name:'Args',value:'',isExpanded:true,subRows});}}},addContextsToRows_(rows,contexts){if(contexts.length){const subRows=contexts.map(function(context){const contextView=document.createElement('tr-ui-a-generic-object-view');contextView.object=context;return{name:'Context',value:contextView};});rows.push({name:'Contexts',value:'',isExpanded:true,subRows});}},updateContents_(){if(this.currentSelection_===undefined){this.$.table.rows=[];this.$.table.rebuild();return;}
+const event=tr.b.getOnlyElement(this.currentSelection_);const rows=this.getEventRows_(event);if(event.argsStripped){rows.push({name:'Args',value:'Stripped'});}else{this.addArgsToRows_(rows,event.args);}
+this.addContextsToRows_(rows,event.contexts);const customizeRowsEvent=new tr.b.Event('customize-rows');customizeRowsEvent.rows=rows;this.dispatchEvent(customizeRowsEvent);this.$.table.tableRows=rows;this.$.table.rebuild();}});'use strict';Polymer({is:'tr-ui-e-chrome-cc-raster-task-view',created(){this.selection_=undefined;},set selection(selection){this.selection_=selection;this.updateContents_();},updateColumns_(hadCpuDurations){const timeSpanConfig={unit:tr.b.Unit.byName.timeDurationInMs,ownerDocument:this.ownerDocument};const columns=[{title:'Layer',value(row){if(row.isTotals)return'Totals';if(row.layer){const linkEl=document.createElement('tr-ui-a-analysis-link');linkEl.setSelectionAndContent(function(){return new tr.ui.e.chrome.cc.LayerSelection(row.layer);},'Layer '+row.layerId);return linkEl;}
+return'Layer '+row.layerId;},width:'250px'},{title:'Num Tiles',value(row){return row.numTiles;},cmp(a,b){return a.numTiles-b.numTiles;}},{title:'Num Analysis Tasks',value(row){return row.numAnalysisTasks;},cmp(a,b){return a.numAnalysisTasks-b.numAnalysisTasks;}},{title:'Num Raster Tasks',value(row){return row.numRasterTasks;},cmp(a,b){return a.numRasterTasks-b.numRasterTasks;}},{title:'Wall Duration (ms)',value(row){return tr.v.ui.createScalarSpan(row.duration,timeSpanConfig);},cmp(a,b){return a.duration-b.duration;}}];if(hadCpuDurations){columns.push({title:'CPU Duration (ms)',value(row){return tr.v.ui.createScalarSpan(row.cpuDuration,timeSpanConfig);},cmp(a,b){return a.cpuDuration-b.cpuDuration;}});}
+let colWidthPercentage;if(columns.length===1){colWidthPercentage='100%';}else{colWidthPercentage=(100/(columns.length-1)).toFixed(3)+'%';}
+for(let i=1;i<columns.length;i++){columns[i].width=colWidthPercentage;}
+this.$.content.tableColumns=columns;this.$.content.sortColumnIndex=columns.length-1;},updateContents_(){const table=this.$.content;if(this.selection_.length===0){this.$.link.setSelectionAndContent(undefined,'');table.tableRows=[];table.footerRows=[];table.rebuild();return;}
+const lthi=tr.e.cc.getTileFromRasterTaskSlice(tr.b.getFirstElement(this.selection_)).containingSnapshot;this.$.link.setSelectionAndContent(function(){return new tr.model.EventSet(lthi);},lthi.userFriendlyName);const costsByLayerId={};function getCurrentCostsForLayerId(tile){const layerId=tile.layerId;const lthi=tile.containingSnapshot;let layer;if(lthi.activeTree){layer=lthi.activeTree.findLayerWithId(layerId);}
+if(layer===undefined&&lthi.pendingTree){layer=lthi.pendingTree.findLayerWithId(layerId);}
+if(costsByLayerId[layerId]===undefined){costsByLayerId[layerId]={layerId,layer,numTiles:0,numAnalysisTasks:0,numRasterTasks:0,duration:0,cpuDuration:0};}
+return costsByLayerId[layerId];}
+let totalDuration=0;let totalCpuDuration=0;let totalNumAnalyzeTasks=0;let totalNumRasterizeTasks=0;let hadCpuDurations=false;const tilesThatWeHaveSeen={};this.selection_.forEach(function(slice){const tile=tr.e.cc.getTileFromRasterTaskSlice(slice);const curCosts=getCurrentCostsForLayerId(tile);if(!tilesThatWeHaveSeen[tile.objectInstance.id]){tilesThatWeHaveSeen[tile.objectInstance.id]=true;curCosts.numTiles+=1;}
+if(tr.e.cc.isSliceDoingAnalysis(slice)){curCosts.numAnalysisTasks+=1;totalNumAnalyzeTasks+=1;}else{curCosts.numRasterTasks+=1;totalNumRasterizeTasks+=1;}
+curCosts.duration+=slice.duration;totalDuration+=slice.duration;if(slice.cpuDuration!==undefined){curCosts.cpuDuration+=slice.cpuDuration;totalCpuDuration+=slice.cpuDuration;hadCpuDurations=true;}});this.updateColumns_(hadCpuDurations);table.tableRows=Object.values(costsByLayerId);table.rebuild();table.footerRows=[{isTotals:true,numTiles:Object.keys(tilesThatWeHaveSeen).length,numAnalysisTasks:totalNumAnalyzeTasks,numRasterTasks:totalNumRasterizeTasks,duration:totalDuration,cpuDuration:totalCpuDuration}];}});'use strict';tr.exportTo('tr.ui.e.chrome.cc',function(){function RasterTaskSelection(selection){tr.ui.e.chrome.cc.Selection.call(this);const whySupported=RasterTaskSelection.whySuported(selection);if(!whySupported.ok){throw new Error('Fail: '+whySupported.why);}
+this.slices_=Array.from(selection);this.tiles_=this.slices_.map(function(slice){const tile=tr.e.cc.getTileFromRasterTaskSlice(slice);if(tile===undefined){throw new Error('This should never happen due to .supports check.');}
+return tile;});}
+RasterTaskSelection.whySuported=function(selection){if(!(selection instanceof tr.model.EventSet)){return{ok:false,why:'Must be selection'};}
+if(selection.length===0){return{ok:false,why:'Selection must be non empty'};}
+let referenceSnapshot=undefined;for(const event of selection){if(!(event instanceof tr.model.Slice)){return{ok:false,why:'Not a slice'};}
+const tile=tr.e.cc.getTileFromRasterTaskSlice(event);if(tile===undefined){return{ok:false,why:'No tile found'};}
+if(!referenceSnapshot){referenceSnapshot=tile.containingSnapshot;}else{if(tile.containingSnapshot!==referenceSnapshot){return{ok:false,why:'Raster tasks are from different compositor instances'};}}}
+return{ok:true};};RasterTaskSelection.supports=function(selection){return RasterTaskSelection.whySuported(selection).ok;};RasterTaskSelection.prototype={__proto__:tr.ui.e.chrome.cc.Selection.prototype,get specicifity(){return 3;},get associatedLayerId(){const tile0=this.tiles_[0];const allSameLayer=this.tiles_.every(function(tile){tile.layerId===tile0.layerId;});if(allSameLayer){return tile0.layerId;}
+return undefined;},get extraHighlightsByLayerId(){const highlights={};this.tiles_.forEach(function(tile,i){if(highlights[tile.layerId]===undefined){highlights[tile.layerId]=[];}
+const slice=this.slices_[i];highlights[tile.layerId].push({colorKey:slice.title,rect:tile.layerRect});},this);return highlights;},createAnalysis(){const sel=new tr.model.EventSet();this.slices_.forEach(function(slice){sel.push(slice);});let analysis;if(sel.length===1){analysis=document.createElement('tr-ui-a-single-event-sub-view');}else{analysis=document.createElement('tr-ui-e-chrome-cc-raster-task-view');}
+analysis.selection=sel;return analysis;},findEquivalent(lthi){return undefined;},get containingSnapshot(){return this.tiles_[0].containingSnapshot;}};return{RasterTaskSelection,};});'use strict';tr.exportTo('tr.ui.e.chrome.cc',function(){const TileSnapshotView=tr.ui.b.define('tr-ui-e-chrome-cc-tile-snapshot-view',tr.ui.analysis.ObjectSnapshotView);TileSnapshotView.prototype={__proto__:tr.ui.analysis.ObjectSnapshotView.prototype,decorate(){Polymer.dom(this).classList.add('tr-ui-e-chrome-cc-tile-snapshot-view');this.layerTreeView_=new tr.ui.e.chrome.cc.LayerTreeHostImplSnapshotView();Polymer.dom(this).appendChild(this.layerTreeView_);},updateContents(){const tile=this.objectSnapshot_;const layerTreeHostImpl=tile.containingSnapshot;if(!layerTreeHostImpl)return;this.layerTreeView_.objectSnapshot=layerTreeHostImpl;this.layerTreeView_.selection=new tr.ui.e.chrome.cc.TileSelection(tile);}};tr.ui.analysis.ObjectSnapshotView.register(TileSnapshotView,{typeName:'cc::Tile',showInTrackView:false});return{TileSnapshotView,};});'use strict';tr.exportTo('tr.ui.e.chrome',function(){Polymer({is:'tr-ui-e-chrome-codesearch',set searchPhrase(phrase){const link=Polymer.dom(this.$.codesearchLink);const codeSearchURL='https://cs.chromium.org/search/?sq=package:chromium&type=cs&q=';link.setAttribute('href',codeSearchURL+encodeURIComponent(phrase));},onClick(clickEvent){clickEvent.stopPropagation();}});return{};});'use strict';tr.exportTo('tr.e.gpu',function(){const AsyncSlice=tr.model.AsyncSlice;function GpuAsyncSlice(){AsyncSlice.apply(this,arguments);}
+GpuAsyncSlice.prototype={__proto__:AsyncSlice.prototype,get viewSubGroupTitle(){if(this.args.channel){if(this.category==='disabled-by-default-gpu.device'){return'Device.'+this.args.channel;}
+return'Service.'+this.args.channel;}
+return this.title;}};AsyncSlice.subTypes.register(GpuAsyncSlice,{categoryParts:['disabled-by-default-gpu.device','disabled-by-default-gpu.service']});return{GpuAsyncSlice,};});'use strict';tr.exportTo('tr.e.gpu',function(){const ObjectSnapshot=tr.model.ObjectSnapshot;function StateSnapshot(){ObjectSnapshot.apply(this,arguments);}
+StateSnapshot.prototype={__proto__:ObjectSnapshot.prototype,preInitialize(){this.screenshot_=undefined;},initialize(){if(this.args.screenshot){this.screenshot_=this.args.screenshot;}},get screenshot(){return this.screenshot_;}};ObjectSnapshot.subTypes.register(StateSnapshot,{typeName:'gpu::State'});return{StateSnapshot,};});'use strict';tr.exportTo('tr.ui.e.chrome.gpu',function(){const StateSnapshotView=tr.ui.b.define('tr-ui-e-chrome-gpu-state-snapshot-view',tr.ui.analysis.ObjectSnapshotView);StateSnapshotView.prototype={__proto__:tr.ui.analysis.ObjectSnapshotView.prototype,decorate(){Polymer.dom(this).classList.add('tr-ui-e-chrome-gpu-state-snapshot-view');this.screenshotImage_=document.createElement('img');Polymer.dom(this).appendChild(this.screenshotImage_);},updateContents(){if(this.objectSnapshot_&&this.objectSnapshot_.screenshot){this.screenshotImage_.src='data:image/png;base64,'+
+this.objectSnapshot_.screenshot;}}};tr.ui.analysis.ObjectSnapshotView.register(StateSnapshotView,{typeName:'gpu::State'});return{StateSnapshotView,};});'use strict';tr.exportTo('tr.ui.analysis',function(){Polymer({is:'tr-ui-a-layout-tree-sub-view',behaviors:['tr-ui-a-sub-view'],set selection(selection){this.currentSelection_=selection;this.updateContents_();},get selection(){return this.currentSelection_;},updateContents_(){this.set('$.content.textContent','');if(!this.currentSelection_)return;const columns=[{title:'Tag/Name',value(layoutObject){return layoutObject.tag||':'+layoutObject.name;}},{title:'htmlId',value(layoutObject){return layoutObject.htmlId||'';}},{title:'classNames',value(layoutObject){return layoutObject.classNames||'';}},{title:'reasons',value(layoutObject){return layoutObject.needsLayoutReasons.join(', ');}},{title:'width',value(layoutObject){return layoutObject.absoluteRect.width;}},{title:'height',value(layoutObject){return layoutObject.absoluteRect.height;}},{title:'absX',value(layoutObject){return layoutObject.absoluteRect.left;}},{title:'absY',value(layoutObject){return layoutObject.absoluteRect.top;}},{title:'relX',value(layoutObject){return layoutObject.relativeRect.left;}},{title:'relY',value(layoutObject){return layoutObject.relativeRect.top;}},{title:'float',value(layoutObject){return layoutObject.isFloat?'float':'';}},{title:'positioned',value(layoutObject){return layoutObject.isPositioned?'positioned':'';}},{title:'relative',value(layoutObject){return layoutObject.isRelativePositioned?'relative':'';}},{title:'sticky',value(layoutObject){return layoutObject.isStickyPositioned?'sticky':'';}},{title:'anonymous',value(layoutObject){return layoutObject.isAnonymous?'anonymous':'';}},{title:'row',value(layoutObject){if(layoutObject.tableRow===undefined){return'';}
+return layoutObject.tableRow;}},{title:'col',value(layoutObject){if(layoutObject.tableCol===undefined){return'';}
+return layoutObject.tableCol;}},{title:'rowSpan',value(layoutObject){if(layoutObject.tableRowSpan===undefined){return'';}
+return layoutObject.tableRowSpan;}},{title:'colSpan',value(layoutObject){if(layoutObject.tableColSpan===undefined){return'';}
+return layoutObject.tableColSpan;}},{title:'address',value(layoutObject){return layoutObject.id.toString(16);}}];const table=this.ownerDocument.createElement('tr-ui-b-table');table.defaultExpansionStateCallback=function(layoutObject,parentLayoutObject){return true;};table.subRowsPropertyName='childLayoutObjects';table.tableColumns=columns;table.tableRows=this.currentSelection_.map(function(snapshot){return snapshot.rootLayoutObject;});table.rebuild();Polymer.dom(this.$.content).appendChild(table);},});return{};});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-layout-tree-sub-view',tr.e.chrome.LayoutTreeSnapshot,{multi:false,title:'Layout Tree',});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-layout-tree-sub-view',tr.e.chrome.LayoutTreeSnapshot,{multi:true,title:'Layout Trees',});'use strict';tr.exportTo('tr.ui.e.img',function(){const THIS_DOC=document.currentScript.ownerDocument;const ImageSnapshotView=tr.ui.b.define('tr-ui-e-img-image-snapshot-view',tr.ui.analysis.ObjectSnapshotView);ImageSnapshotView.prototype={__proto__:tr.ui.analysis.ObjectSnapshotView.prototype,decorate(){const node=tr.ui.b.instantiateTemplate('#tr-ui-e-img-image-snapshot-view-template',THIS_DOC);Polymer.dom(this).appendChild(node);const info=Polymer.dom(this).querySelector('.image-info');this.sizeInfo_=Polymer.dom(info).querySelector('.size');this.imageContainer_=Polymer.dom(this).querySelector('.image-container');this.image_=Polymer.dom(this.imageContainer_).querySelector('img');this.zoomScaleValue_=1;this.trackMouse_();},updateContents(){if(this.objectSnapshot_&&this.objectSnapshot_.data&&this.objectSnapshot_.type){this.image_.onload=this.drawPicture_.bind(this);this.image_.src=`data:image/${this.objectSnapshot_.type};`+`base64,${this.objectSnapshot_.data}`;}
+this.drawPicture_();},drawPicture_(){if(!this.image_.complete)return;const naturalWidth=this.image_.naturalWidth;const naturalHeight=this.image_.naturalHeight;this.sizeInfo_.textContent=`(${naturalWidth} x ${naturalHeight})`;this.image_.width=naturalWidth*this.zoomScaleValue_;this.image_.height=naturalHeight*this.zoomScaleValue_;},trackMouse_(){this.mouseModeSelector_=document.createElement('tr-ui-b-mouse-mode-selector');this.mouseModeSelector_.targetElement=this.imageContainer_;Polymer.dom(this.imageContainer_).appendChild(this.mouseModeSelector_);this.mouseModeSelector_.supportedModeMask=tr.ui.b.MOUSE_SELECTOR_MODE.ZOOM;this.mouseModeSelector_.mode=tr.ui.b.MOUSE_SELECTOR_MODE.ZOOM;this.mouseModeSelector_.defaultMode=tr.ui.b.MOUSE_SELECTOR_MODE.ZOOM;this.mouseModeSelector_.settingsKey='pictureDebugger.mouseModeSelector';this.mouseModeSelector_.addEventListener('beginzoom',this.onBeginZoom_.bind(this));this.mouseModeSelector_.addEventListener('updatezoom',this.onUpdateZoom_.bind(this));this.mouseModeSelector_.addEventListener('endzoom',this.onEndZoom_.bind(this));},onBeginZoom_(e){this.isZooming_=true;this.lastMouseViewPos_=this.extractRelativeMousePosition_(e);e.preventDefault();},onUpdateZoom_(e){if(!this.isZooming_)return;const currentMouseViewPos=this.extractRelativeMousePosition_(e);this.zoomScaleValue_+=((this.lastMouseViewPos_.y-currentMouseViewPos.y)*0.001);this.zoomScaleValue_=Math.max(this.zoomScaleValue_,0.1);this.drawPicture_();this.lastMouseViewPos_=currentMouseViewPos;},onEndZoom_(e){this.lastMouseViewPos_=undefined;this.isZooming_=false;e.preventDefault();},extractRelativeMousePosition_(e){return{x:e.clientX-this.imageContainer_.offsetLeft,y:e.clientY-this.imageContainer_.offsetTop};},};tr.ui.analysis.ObjectSnapshotView.register(ImageSnapshotView,{typeName:'gfx::Image'});return{ImageSnapshotView,};});'use strict';tr.exportTo('tr.ui.behaviors',function(){const SidePanel={get rangeOfInterest(){throw new Error('Not implemented');},set rangeOfInterest(rangeOfInterest){throw new Error('Not implemented');},get selection(){throw new Error('Not implemented');},set selection(selection){throw new Error('Not implemented');},get model(){throw new Error('Not implemented');},set model(model){throw new Error('Not implemented');},supportsModel(m){throw new Error('Not implemented');}};return{SidePanel,};});'use strict';tr.exportTo('tr.ui.side_panel',function(){function SidePanelRegistry(){}
+const options=new tr.b.ExtensionRegistryOptions(tr.b.BASIC_REGISTRY_MODE);tr.b.decorateExtensionRegistry(SidePanelRegistry,options);return{SidePanelRegistry,};});'use strict';tr.exportTo('tr.ui.e.s',function(){const BlameContextSnapshot=tr.e.chrome.BlameContextSnapshot;const FrameTreeNodeSnapshot=tr.e.chrome.FrameTreeNodeSnapshot;const RenderFrameSnapshot=tr.e.chrome.RenderFrameSnapshot;const TopLevelSnapshot=tr.e.chrome.TopLevelSnapshot;const BlameContextInstance=tr.e.chrome.BlameContextInstance;const FrameTreeNodeInstance=tr.e.chrome.FrameTreeNodeInstance;const RenderFrameInstance=tr.e.chrome.RenderFrameInstance;const TopLevelInstance=tr.e.chrome.TopLevelInstance;function Row(context){this.subRows=undefined;this.contexts=[];this.type=undefined;this.renderer='N/A';this.url=undefined;this.time=0;this.eventsOfInterest=new tr.model.EventSet();if(context===undefined)return;this.type=context.objectInstance.blameContextType;this.contexts.push(context);if(context instanceof FrameTreeNodeSnapshot){if(context.renderFrame){this.contexts.push(context.renderFrame);this.renderer=context.renderFrame.objectInstance.parent.pid;}}else if(context instanceof RenderFrameSnapshot){if(context.frameTreeNode){this.contexts.push(context.frameTreeNode);}
+this.renderer=context.objectInstance.parent.pid;}else if(context instanceof TopLevelSnapshot){this.renderer=context.objectInstance.parent.pid;}else{throw new Error('Unknown context type');}
+this.eventsOfInterest.addEventSet(this.contexts);this.url=context.url;}
+const groupFunctions={none:rows=>rows,tree(rows,rowMap){const getParentRow=function(row){let pivot;row.contexts.forEach(function(context){if(context instanceof tr.e.chrome.FrameTreeNodeSnapshot){pivot=context;}});if(pivot&&pivot.parentContext){return rowMap[pivot.parentContext.guid];}
+return undefined;};const rootRows=[];rows.forEach(function(row){const parentRow=getParentRow(row);if(parentRow===undefined){rootRows.push(row);return;}
+if(parentRow.subRows===undefined){parentRow.subRows=[];}
+parentRow.subRows.push(row);});const aggregateAllDescendants=function(row){if(!row.subRows){if(getParentRow(row)){row.type='Subframe';}
+return row;}
+const result=new Row();result.type='Frame Tree';result.renderer=row.renderer;result.url=row.url;result.subRows=[row];row.subRows.forEach(subRow=>result.subRows.push(aggregateAllDescendants(subRow)));result.subRows.forEach(function(subRow){result.time+=subRow.time;result.eventsOfInterest.addEventSet(subRow.eventsOfInterest);});row.subRows=undefined;return result;};return rootRows.map(rootRow=>aggregateAllDescendants(rootRow));}};Polymer({is:'tr-ui-e-s-frame-data-side-panel',behaviors:[tr.ui.behaviors.SidePanel],ready(){this.model_=undefined;this.rangeOfInterest_=new tr.b.math.Range();this.$.table.showHeader=true;this.$.table.selectionMode=tr.ui.b.TableFormat.SelectionMode.ROW;this.$.table.tableColumns=this.createFrameDataTableColumns_();this.$.table.addEventListener('selection-changed',function(e){this.selectEventSet_(this.$.table.selectedTableRow.eventsOfInterest);}.bind(this));this.$.select.addEventListener('change',function(e){this.updateContents_();}.bind(this));},selectEventSet_(eventSet){const event=new tr.model.RequestSelectionChangeEvent();event.selection=eventSet;this.dispatchEvent(event);},createFrameDataTableColumns_(){return[{title:'Renderer',value:row=>row.renderer,cmp:(a,b)=>a.renderer-b.renderer},{title:'Type',value:row=>row.type},{title:'Time',value:row=>tr.v.ui.createScalarSpan(row.time,{unit:tr.b.Unit.byName.timeStampInMs,ownerDocument:this.ownerDocument}),cmp:(a,b)=>a.time-b.time},{title:'URL',value:row=>row.url,cmp:(a,b)=>(a.url||'').localeCompare(b.url||'')}];},createFrameDataTableRows_(){if(!this.model_)return[];const rows=[];const rowMap={};for(const proc of Object.values(this.model_.processes)){proc.objects.iterObjectInstances(function(objectInstance){if(!(objectInstance instanceof BlameContextInstance)){return;}
+objectInstance.snapshots.forEach(function(snapshot){if(rowMap[snapshot.guid])return;const row=new Row(snapshot);row.contexts.forEach(context=>rowMap[context.guid]=row);rows.push(row);},this);},this);}
+for(const proc of Object.values(this.model_.processes)){for(const thread of Object.values(proc.threads)){thread.sliceGroup.iterSlicesInTimeRange(function(topLevelSlice){topLevelSlice.contexts.forEach(function(context){if(!context.snapshot.guid||!rowMap[context.snapshot.guid]){return;}
+const row=rowMap[context.snapshot.guid];row.eventsOfInterest.push(topLevelSlice);row.time+=topLevelSlice.selfTime||0;});},this.currentRangeOfInterest.min,this.currentRangeOfInterest.max);}}
+const select=this.$.select;const groupOption=select.options[select.selectedIndex].value;const groupFunction=groupFunctions[groupOption];return groupFunction(rows,rowMap);},updateContents_(){this.$.table.tableRows=this.createFrameDataTableRows_();this.$.table.rebuild();},supportsModel(m){if(!m){return{supported:false,reason:'No model available.'};}
+const ans={supported:false};for(const proc of Object.values(m.processes)){proc.objects.iterObjectInstances(function(instance){if(instance instanceof BlameContextInstance){ans.supported=true;}});}
+if(!ans.supported){ans.reason='No frame data available';}
+return ans;},get currentRangeOfInterest(){if(this.rangeOfInterest_.isEmpty){return this.model_.bounds;}
+return this.rangeOfInterest_;},get rangeOfInterest(){return this.rangeOfInterest_;},set rangeOfInterest(rangeOfInterest){this.rangeOfInterest_=rangeOfInterest;this.updateContents_();},get selection(){},set selection(_){},get textLabel(){return'Frame Data';},get model(){return this.model_;},set model(model){this.model_=model;this.updateContents_();}});tr.ui.side_panel.SidePanelRegistry.register(function(){return document.createElement('tr-ui-e-s-frame-data-side-panel');});});'use strict';Polymer({is:'tr-ui-b-chart-legend-key',ready(){this.$.checkbox.addEventListener('change',this.onCheckboxChange_.bind(this));},onCheckboxChange_(){tr.b.dispatchSimpleEvent(this,tr.ui.b.DataSeriesEnableChangeEventType,true,false,{key:Polymer.dom(this).textContent,enabled:this.enabled});},set textContent(t){Polymer.dom(this.$.label).textContent=t;Polymer.dom(this.$.link).textContent=t;this.updateContents_();},set width(w){w-=20;this.$.link.style.width=w+'px';this.$.label.style.width=w+'px';},get textContent(){return Polymer.dom(this.$.label).textContent;},set optional(optional){this.$.checkbox.style.visibility=optional?'visible':'hidden';},get optional(){return this.$.checkbox.style.visibility==='visible';},set enabled(enabled){this.$.checkbox.checked=enabled?'checked':'';},get enabled(){return this.$.checkbox.checked;},set color(c){this.$.label.style.color=c;this.$.link.color=c;},set target(target){this.$.link.setSelectionAndContent(target,Polymer.dom(this.$.label).textContent);this.updateContents_();},get target(){return this.$.link.selection;},set title(title){this.$.link.title=title;},updateContents_(){this.$.link.style.display=this.target?'':'none';this.$.label.style.display=this.target?'none':'';this.$.label.htmlFor=this.optional?'checkbox':'';}});'use strict';(function(window){window.define=function(x){window.d3=x;};window.define.amd=true;})(this);!function(){function n(n){return null!=n&&!isNaN(n)}function t(n){return n.length}function e(n){for(var t=1;n*t%1;)t*=10;return t}function r(n,t){try{for(var e in t)Object.defineProperty(n.prototype,e,{value:t[e],enumerable:!1})}catch(r){n.prototype=t}}function u(){}function i(n){return aa+n in this}function o(n){return n=aa+n,n in this&&delete this[n]}function a(){var n=[];return this.forEach(function(t){n.push(t)}),n}function c(){var n=0;for(var t in this)t.charCodeAt(0)===ca&&++n;return n}function s(){for(var n in this)if(n.charCodeAt(0)===ca)return!1;return!0}function l(){}function f(n,t,e){return function(){var r=e.apply(t,arguments);return r===t?n:r}}function h(n,t){if(t in n)return t;t=t.charAt(0).toUpperCase()+t.substring(1);for(var e=0,r=sa.length;r>e;++e){var u=sa[e]+t;if(u in n)return u}}function g(){}function p(){}function v(n){function t(){for(var t,r=e,u=-1,i=r.length;++u<i;)(t=r[u].on)&&t.apply(this,arguments);return n}var e=[],r=new u;return t.on=function(t,u){var i,o=r.get(t);return arguments.length<2?o&&o.on:(o&&(o.on=null,e=e.slice(0,i=e.indexOf(o)).concat(e.slice(i+1)),r.remove(t)),u&&e.push(r.set(t,{on:u})),n)},t}function d(){Xo.event.preventDefault()}function m(){for(var n,t=Xo.event;n=t.sourceEvent;)t=n;return t}function y(n){for(var t=new p,e=0,r=arguments.length;++e<r;)t[arguments[e]]=v(t);return t.of=function(e,r){return function(u){try{var i=u.sourceEvent=Xo.event;u.target=n,Xo.event=u,t[u.type].apply(e,r)}finally{Xo.event=i}}},t}function x(n){return fa(n,da),n}function M(n){return"function"==typeof n?n:function(){return ha(n,this)}}function _(n){return"function"==typeof n?n:function(){return ga(n,this)}}function b(n,t){function e(){this.removeAttribute(n)}function r(){this.removeAttributeNS(n.space,n.local)}function u(){this.setAttribute(n,t)}function i(){this.setAttributeNS(n.space,n.local,t)}function o(){var e=t.apply(this,arguments);null==e?this.removeAttribute(n):this.setAttribute(n,e)}function a(){var e=t.apply(this,arguments);null==e?this.removeAttributeNS(n.space,n.local):this.setAttributeNS(n.space,n.local,e)}return n=Xo.ns.qualify(n),null==t?n.local?r:e:"function"==typeof t?n.local?a:o:n.local?i:u}function w(n){return n.trim().replace(/\s+/g," ")}function S(n){return new RegExp("(?:^|\\s+)"+Xo.requote(n)+"(?:\\s+|$)","g")}function k(n){return n.trim().split(/^|\s+/)}function E(n,t){function e(){for(var e=-1;++e<u;)n[e](this,t)}function r(){for(var e=-1,r=t.apply(this,arguments);++e<u;)n[e](this,r)}n=k(n).map(A);var u=n.length;return"function"==typeof t?r:e}function A(n){var t=S(n);return function(e,r){if(u=e.classList)return r?u.add(n):u.remove(n);var u=e.getAttribute("class")||"";r?(t.lastIndex=0,t.test(u)||e.setAttribute("class",w(u+" "+n))):e.setAttribute("class",w(u.replace(t," ")))}}function C(n,t,e){function r(){this.style.removeProperty(n)}function u(){this.style.setProperty(n,t,e)}function i(){var r=t.apply(this,arguments);null==r?this.style.removeProperty(n):this.style.setProperty(n,r,e)}return null==t?r:"function"==typeof t?i:u}function N(n,t){function e(){delete this[n]}function r(){this[n]=t}function u(){var e=t.apply(this,arguments);null==e?delete this[n]:this[n]=e}return null==t?e:"function"==typeof t?u:r}function L(n){return"function"==typeof n?n:(n=Xo.ns.qualify(n)).local?function(){return this.ownerDocument.createElementNS(n.space,n.local)}:function(){return this.ownerDocument.createElementNS(this.namespaceURI,n)}}function T(n){return{__data__:n}}function q(n){return function(){return va(this,n)}}function z(n){return arguments.length||(n=Xo.ascending),function(t,e){return t&&e?n(t.__data__,e.__data__):!t-!e}}function R(n,t){for(var e=0,r=n.length;r>e;e++)for(var u,i=n[e],o=0,a=i.length;a>o;o++)(u=i[o])&&t(u,o,e);return n}function D(n){return fa(n,ya),n}function P(n){var t,e;return function(r,u,i){var o,a=n[i].update,c=a.length;for(i!=e&&(e=i,t=0),u>=t&&(t=u+1);!(o=a[t])&&++t<c;);return o}}function U(){var n=this.__transition__;n&&++n.active}function j(n,t,e){function r(){var t=this[o];t&&(this.removeEventListener(n,t,t.$),delete this[o])}function u(){var u=c(t,Bo(arguments));r.call(this),this.addEventListener(n,this[o]=u,u.$=e),u._=t}function i(){var t,e=new RegExp("^__on([^.]+)"+Xo.requote(n)+"$");for(var r in this)if(t=r.match(e)){var u=this[r];this.removeEventListener(t[1],u,u.$),delete this[r]}}var o="__on"+n,a=n.indexOf("."),c=H;a>0&&(n=n.substring(0,a));var s=Ma.get(n);return s&&(n=s,c=F),a?t?u:r:t?g:i}function H(n,t){return function(e){var r=Xo.event;Xo.event=e,t[0]=this.__data__;try{n.apply(this,t)}finally{Xo.event=r}}}function F(n,t){var e=H(n,t);return function(n){var t=this,r=n.relatedTarget;r&&(r===t||8&r.compareDocumentPosition(t))||e.call(t,n)}}function O(){var n=".dragsuppress-"+ ++ba,t="click"+n,e=Xo.select(Go).on("touchmove"+n,d).on("dragstart"+n,d).on("selectstart"+n,d);if(_a){var r=Jo.style,u=r[_a];r[_a]="none"}return function(i){function o(){e.on(t,null)}e.on(n,null),_a&&(r[_a]=u),i&&(e.on(t,function(){d(),o()},!0),setTimeout(o,0))}}function Y(n,t){t.changedTouches&&(t=t.changedTouches[0]);var e=n.ownerSVGElement||n;if(e.createSVGPoint){var r=e.createSVGPoint();if(0>wa&&(Go.scrollX||Go.scrollY)){e=Xo.select("body").append("svg").style({position:"absolute",top:0,left:0,margin:0,padding:0,border:"none"},"important");var u=e[0][0].getScreenCTM();wa=!(u.f||u.e),e.remove()}return wa?(r.x=t.pageX,r.y=t.pageY):(r.x=t.clientX,r.y=t.clientY),r=r.matrixTransform(n.getScreenCTM().inverse()),[r.x,r.y]}var i=n.getBoundingClientRect();return[t.clientX-i.left-n.clientLeft,t.clientY-i.top-n.clientTop]}function I(n){return n>0?1:0>n?-1:0}function Z(n,t,e){return(t[0]-n[0])*(e[1]-n[1])-(t[1]-n[1])*(e[0]-n[0])}function V(n){return n>1?0:-1>n?Sa:Math.acos(n)}function X(n){return n>1?Ea:-1>n?-Ea:Math.asin(n)}function $(n){return((n=Math.exp(n))-1/n)/2}function B(n){return((n=Math.exp(n))+1/n)/2}function W(n){return((n=Math.exp(2*n))-1)/(n+1)}function J(n){return(n=Math.sin(n/2))*n}function G(){}function K(n,t,e){return new Q(n,t,e)}function Q(n,t,e){this.h=n,this.s=t,this.l=e}function nt(n,t,e){function r(n){return n>360?n-=360:0>n&&(n+=360),60>n?i+(o-i)*n/60:180>n?o:240>n?i+(o-i)*(240-n)/60:i}function u(n){return Math.round(255*r(n))}var i,o;return n=isNaN(n)?0:(n%=360)<0?n+360:n,t=isNaN(t)?0:0>t?0:t>1?1:t,e=0>e?0:e>1?1:e,o=.5>=e?e*(1+t):e+t-e*t,i=2*e-o,gt(u(n+120),u(n),u(n-120))}function tt(n,t,e){return new et(n,t,e)}function et(n,t,e){this.h=n,this.c=t,this.l=e}function rt(n,t,e){return isNaN(n)&&(n=0),isNaN(t)&&(t=0),ut(e,Math.cos(n*=Na)*t,Math.sin(n)*t)}function ut(n,t,e){return new it(n,t,e)}function it(n,t,e){this.l=n,this.a=t,this.b=e}function ot(n,t,e){var r=(n+16)/116,u=r+t/500,i=r-e/200;return u=ct(u)*Fa,r=ct(r)*Oa,i=ct(i)*Ya,gt(lt(3.2404542*u-1.5371385*r-.4985314*i),lt(-.969266*u+1.8760108*r+.041556*i),lt(.0556434*u-.2040259*r+1.0572252*i))}function at(n,t,e){return n>0?tt(Math.atan2(e,t)*La,Math.sqrt(t*t+e*e),n):tt(0/0,0/0,n)}function ct(n){return n>.206893034?n*n*n:(n-4/29)/7.787037}function st(n){return n>.008856?Math.pow(n,1/3):7.787037*n+4/29}function lt(n){return Math.round(255*(.00304>=n?12.92*n:1.055*Math.pow(n,1/2.4)-.055))}function ft(n){return gt(n>>16,255&n>>8,255&n)}function ht(n){return ft(n)+""}function gt(n,t,e){return new pt(n,t,e)}function pt(n,t,e){this.r=n,this.g=t,this.b=e}function vt(n){return 16>n?"0"+Math.max(0,n).toString(16):Math.min(255,n).toString(16)}function dt(n,t,e){var r,u,i,o,a=0,c=0,s=0;if(u=/([a-z]+)\((.*)\)/i.exec(n))switch(i=u[2].split(","),u[1]){case"hsl":return e(parseFloat(i[0]),parseFloat(i[1])/100,parseFloat(i[2])/100);case"rgb":return t(Mt(i[0]),Mt(i[1]),Mt(i[2]))}return(o=Va.get(n))?t(o.r,o.g,o.b):(null!=n&&"#"===n.charAt(0)&&(r=parseInt(n.substring(1),16),isNaN(r)||(4===n.length?(a=(3840&r)>>4,a=a>>4|a,c=240&r,c=c>>4|c,s=15&r,s=s<<4|s):7===n.length&&(a=(16711680&r)>>16,c=(65280&r)>>8,s=255&r))),t(a,c,s))}function mt(n,t,e){var r,u,i=Math.min(n/=255,t/=255,e/=255),o=Math.max(n,t,e),a=o-i,c=(o+i)/2;return a?(u=.5>c?a/(o+i):a/(2-o-i),r=n==o?(t-e)/a+(e>t?6:0):t==o?(e-n)/a+2:(n-t)/a+4,r*=60):(r=0/0,u=c>0&&1>c?0:r),K(r,u,c)}function yt(n,t,e){n=xt(n),t=xt(t),e=xt(e);var r=st((.4124564*n+.3575761*t+.1804375*e)/Fa),u=st((.2126729*n+.7151522*t+.072175*e)/Oa),i=st((.0193339*n+.119192*t+.9503041*e)/Ya);return ut(116*u-16,500*(r-u),200*(u-i))}function xt(n){return(n/=255)<=.04045?n/12.92:Math.pow((n+.055)/1.055,2.4)}function Mt(n){var t=parseFloat(n);return"%"===n.charAt(n.length-1)?Math.round(2.55*t):t}function _t(n){return"function"==typeof n?n:function(){return n}}function bt(n){return n}function wt(n){return function(t,e,r){return 2===arguments.length&&"function"==typeof e&&(r=e,e=null),St(t,e,n,r)}}function St(n,t,e,r){function u(){var n,t=c.status;if(!t&&c.responseText||t>=200&&300>t||304===t){try{n=e.call(i,c)}catch(r){return o.error.call(i,r),void 0}o.load.call(i,n)}else o.error.call(i,c)}var i={},o=Xo.dispatch("beforesend","progress","load","error"),a={},c=new XMLHttpRequest,s=null;return!Go.XDomainRequest||"withCredentials"in c||!/^(http(s)?:)?\/\//.test(n)||(c=new XDomainRequest),"onload"in c?c.onload=c.onerror=u:c.onreadystatechange=function(){c.readyState>3&&u()},c.onprogress=function(n){var t=Xo.event;Xo.event=n;try{o.progress.call(i,c)}finally{Xo.event=t}},i.header=function(n,t){return n=(n+"").toLowerCase(),arguments.length<2?a[n]:(null==t?delete a[n]:a[n]=t+"",i)},i.mimeType=function(n){return arguments.length?(t=null==n?null:n+"",i):t},i.responseType=function(n){return arguments.length?(s=n,i):s},i.response=function(n){return e=n,i},["get","post"].forEach(function(n){i[n]=function(){return i.send.apply(i,[n].concat(Bo(arguments)))}}),i.send=function(e,r,u){if(2===arguments.length&&"function"==typeof r&&(u=r,r=null),c.open(e,n,!0),null==t||"accept"in a||(a.accept=t+",*/*"),c.setRequestHeader)for(var l in a)c.setRequestHeader(l,a[l]);return null!=t&&c.overrideMimeType&&c.overrideMimeType(t),null!=s&&(c.responseType=s),null!=u&&i.on("error",u).on("load",function(n){u(null,n)}),o.beforesend.call(i,c),c.send(null==r?null:r),i},i.abort=function(){return c.abort(),i},Xo.rebind(i,o,"on"),null==r?i:i.get(kt(r))}function kt(n){return 1===n.length?function(t,e){n(null==t?e:null)}:n}function Et(){var n=At(),t=Ct()-n;t>24?(isFinite(t)&&(clearTimeout(Wa),Wa=setTimeout(Et,t)),Ba=0):(Ba=1,Ga(Et))}function At(){var n=Date.now();for(Ja=Xa;Ja;)n>=Ja.t&&(Ja.f=Ja.c(n-Ja.t)),Ja=Ja.n;return n}function Ct(){for(var n,t=Xa,e=1/0;t;)t.f?t=n?n.n=t.n:Xa=t.n:(t.t<e&&(e=t.t),t=(n=t).n);return $a=n,e}function Nt(n,t){return t-(n?Math.ceil(Math.log(n)/Math.LN10):1)}function Lt(n,t){var e=Math.pow(10,3*oa(8-t));return{scale:t>8?function(n){return n/e}:function(n){return n*e},symbol:n}}function Tt(n){var t=n.decimal,e=n.thousands,r=n.grouping,u=n.currency,i=r?function(n){for(var t=n.length,u=[],i=0,o=r[0];t>0&&o>0;)u.push(n.substring(t-=o,t+o)),o=r[i=(i+1)%r.length];return u.reverse().join(e)}:bt;return function(n){var e=Qa.exec(n),r=e[1]||" ",o=e[2]||">",a=e[3]||"",c=e[4]||"",s=e[5],l=+e[6],f=e[7],h=e[8],g=e[9],p=1,v="",d="",m=!1;switch(h&&(h=+h.substring(1)),(s||"0"===r&&"="===o)&&(s=r="0",o="=",f&&(l-=Math.floor((l-1)/4))),g){case"n":f=!0,g="g";break;case"%":p=100,d="%",g="f";break;case"p":p=100,d="%",g="r";break;case"b":case"o":case"x":case"X":"#"===c&&(v="0"+g.toLowerCase());case"c":case"d":m=!0,h=0;break;case"s":p=-1,g="r"}"$"===c&&(v=u[0],d=u[1]),"r"!=g||h||(g="g"),null!=h&&("g"==g?h=Math.max(1,Math.min(21,h)):("e"==g||"f"==g)&&(h=Math.max(0,Math.min(20,h)))),g=nc.get(g)||qt;var y=s&&f;return function(n){var e=d;if(m&&n%1)return"";var u=0>n||0===n&&0>1/n?(n=-n,"-"):a;if(0>p){var c=Xo.formatPrefix(n,h);n=c.scale(n),e=c.symbol+d}else n*=p;n=g(n,h);var x=n.lastIndexOf("."),M=0>x?n:n.substring(0,x),_=0>x?"":t+n.substring(x+1);!s&&f&&(M=i(M));var b=v.length+M.length+_.length+(y?0:u.length),w=l>b?new Array(b=l-b+1).join(r):"";return y&&(M=i(w+M)),u+=v,n=M+_,("<"===o?u+n+w:">"===o?w+u+n:"^"===o?w.substring(0,b>>=1)+u+n+w.substring(b):u+(y?n:w+n))+e}}}function qt(n){return n+""}function zt(){this._=new Date(arguments.length>1?Date.UTC.apply(this,arguments):arguments[0])}function Rt(n,t,e){function r(t){var e=n(t),r=i(e,1);return r-t>t-e?e:r}function u(e){return t(e=n(new ec(e-1)),1),e}function i(n,e){return t(n=new ec(+n),e),n}function o(n,r,i){var o=u(n),a=[];if(i>1)for(;r>o;)e(o)%i||a.push(new Date(+o)),t(o,1);else for(;r>o;)a.push(new Date(+o)),t(o,1);return a}function a(n,t,e){try{ec=zt;var r=new zt;return r._=n,o(r,t,e)}finally{ec=Date}}n.floor=n,n.round=r,n.ceil=u,n.offset=i,n.range=o;var c=n.utc=Dt(n);return c.floor=c,c.round=Dt(r),c.ceil=Dt(u),c.offset=Dt(i),c.range=a,n}function Dt(n){return function(t,e){try{ec=zt;var r=new zt;return r._=t,n(r,e)._}finally{ec=Date}}}function Pt(n){function t(n){function t(t){for(var e,u,i,o=[],a=-1,c=0;++a<r;)37===n.charCodeAt(a)&&(o.push(n.substring(c,a)),null!=(u=uc[e=n.charAt(++a)])&&(e=n.charAt(++a)),(i=C[e])&&(e=i(t,null==u?"e"===e?" ":"0":u)),o.push(e),c=a+1);return o.push(n.substring(c,a)),o.join("")}var r=n.length;return t.parse=function(t){var r={y:1900,m:0,d:1,H:0,M:0,S:0,L:0,Z:null},u=e(r,n,t,0);if(u!=t.length)return null;"p"in r&&(r.H=r.H%12+12*r.p);var i=null!=r.Z&&ec!==zt,o=new(i?zt:ec);return"j"in r?o.setFullYear(r.y,0,r.j):"w"in r&&("W"in r||"U"in r)?(o.setFullYear(r.y,0,1),o.setFullYear(r.y,0,"W"in r?(r.w+6)%7+7*r.W-(o.getDay()+5)%7:r.w+7*r.U-(o.getDay()+6)%7)):o.setFullYear(r.y,r.m,r.d),o.setHours(r.H+Math.floor(r.Z/100),r.M+r.Z%100,r.S,r.L),i?o._:o},t.toString=function(){return n},t}function e(n,t,e,r){for(var u,i,o,a=0,c=t.length,s=e.length;c>a;){if(r>=s)return-1;if(u=t.charCodeAt(a++),37===u){if(o=t.charAt(a++),i=N[o in uc?t.charAt(a++):o],!i||(r=i(n,e,r))<0)return-1}else if(u!=e.charCodeAt(r++))return-1}return r}function r(n,t,e){b.lastIndex=0;var r=b.exec(t.substring(e));return r?(n.w=w.get(r[0].toLowerCase()),e+r[0].length):-1}function u(n,t,e){M.lastIndex=0;var r=M.exec(t.substring(e));return r?(n.w=_.get(r[0].toLowerCase()),e+r[0].length):-1}function i(n,t,e){E.lastIndex=0;var r=E.exec(t.substring(e));return r?(n.m=A.get(r[0].toLowerCase()),e+r[0].length):-1}function o(n,t,e){S.lastIndex=0;var r=S.exec(t.substring(e));return r?(n.m=k.get(r[0].toLowerCase()),e+r[0].length):-1}function a(n,t,r){return e(n,C.c.toString(),t,r)}function c(n,t,r){return e(n,C.x.toString(),t,r)}function s(n,t,r){return e(n,C.X.toString(),t,r)}function l(n,t,e){var r=x.get(t.substring(e,e+=2).toLowerCase());return null==r?-1:(n.p=r,e)}var f=n.dateTime,h=n.date,g=n.time,p=n.periods,v=n.days,d=n.shortDays,m=n.months,y=n.shortMonths;t.utc=function(n){function e(n){try{ec=zt;var t=new ec;return t._=n,r(t)}finally{ec=Date}}var r=t(n);return e.parse=function(n){try{ec=zt;var t=r.parse(n);return t&&t._}finally{ec=Date}},e.toString=r.toString,e},t.multi=t.utc.multi=ee;var x=Xo.map(),M=jt(v),_=Ht(v),b=jt(d),w=Ht(d),S=jt(m),k=Ht(m),E=jt(y),A=Ht(y);p.forEach(function(n,t){x.set(n.toLowerCase(),t)});var C={a:function(n){return d[n.getDay()]},A:function(n){return v[n.getDay()]},b:function(n){return y[n.getMonth()]},B:function(n){return m[n.getMonth()]},c:t(f),d:function(n,t){return Ut(n.getDate(),t,2)},e:function(n,t){return Ut(n.getDate(),t,2)},H:function(n,t){return Ut(n.getHours(),t,2)},I:function(n,t){return Ut(n.getHours()%12||12,t,2)},j:function(n,t){return Ut(1+tc.dayOfYear(n),t,3)},L:function(n,t){return Ut(n.getMilliseconds(),t,3)},m:function(n,t){return Ut(n.getMonth()+1,t,2)},M:function(n,t){return Ut(n.getMinutes(),t,2)},p:function(n){return p[+(n.getHours()>=12)]},S:function(n,t){return Ut(n.getSeconds(),t,2)},U:function(n,t){return Ut(tc.sundayOfYear(n),t,2)},w:function(n){return n.getDay()},W:function(n,t){return Ut(tc.mondayOfYear(n),t,2)},x:t(h),X:t(g),y:function(n,t){return Ut(n.getFullYear()%100,t,2)},Y:function(n,t){return Ut(n.getFullYear()%1e4,t,4)},Z:ne,"%":function(){return"%"}},N={a:r,A:u,b:i,B:o,c:a,d:Bt,e:Bt,H:Jt,I:Jt,j:Wt,L:Qt,m:$t,M:Gt,p:l,S:Kt,U:Ot,w:Ft,W:Yt,x:c,X:s,y:Zt,Y:It,Z:Vt,"%":te};return t}function Ut(n,t,e){var r=0>n?"-":"",u=(r?-n:n)+"",i=u.length;return r+(e>i?new Array(e-i+1).join(t)+u:u)}function jt(n){return new RegExp("^(?:"+n.map(Xo.requote).join("|")+")","i")}function Ht(n){for(var t=new u,e=-1,r=n.length;++e<r;)t.set(n[e].toLowerCase(),e);return t}function Ft(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e,e+1));return r?(n.w=+r[0],e+r[0].length):-1}function Ot(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e));return r?(n.U=+r[0],e+r[0].length):-1}function Yt(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e));return r?(n.W=+r[0],e+r[0].length):-1}function It(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e,e+4));return r?(n.y=+r[0],e+r[0].length):-1}function Zt(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e,e+2));return r?(n.y=Xt(+r[0]),e+r[0].length):-1}function Vt(n,t,e){return/^[+-]\d{4}$/.test(t=t.substring(e,e+5))?(n.Z=+t,e+5):-1}function Xt(n){return n+(n>68?1900:2e3)}function $t(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e,e+2));return r?(n.m=r[0]-1,e+r[0].length):-1}function Bt(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e,e+2));return r?(n.d=+r[0],e+r[0].length):-1}function Wt(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e,e+3));return r?(n.j=+r[0],e+r[0].length):-1}function Jt(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e,e+2));return r?(n.H=+r[0],e+r[0].length):-1}function Gt(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e,e+2));return r?(n.M=+r[0],e+r[0].length):-1}function Kt(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e,e+2));return r?(n.S=+r[0],e+r[0].length):-1}function Qt(n,t,e){ic.lastIndex=0;var r=ic.exec(t.substring(e,e+3));return r?(n.L=+r[0],e+r[0].length):-1}function ne(n){var t=n.getTimezoneOffset(),e=t>0?"-":"+",r=~~(oa(t)/60),u=oa(t)%60;return e+Ut(r,"0",2)+Ut(u,"0",2)}function te(n,t,e){oc.lastIndex=0;var r=oc.exec(t.substring(e,e+1));return r?e+r[0].length:-1}function ee(n){for(var t=n.length,e=-1;++e<t;)n[e][0]=this(n[e][0]);return function(t){for(var e=0,r=n[e];!r[1](t);)r=n[++e];return r[0](t)}}function re(){}function ue(n,t,e){var r=e.s=n+t,u=r-n,i=r-u;e.t=n-i+(t-u)}function ie(n,t){n&&lc.hasOwnProperty(n.type)&&lc[n.type](n,t)}function oe(n,t,e){var r,u=-1,i=n.length-e;for(t.lineStart();++u<i;)r=n[u],t.point(r[0],r[1],r[2]);t.lineEnd()}function ae(n,t){var e=-1,r=n.length;for(t.polygonStart();++e<r;)oe(n[e],t,1);t.polygonEnd()}function ce(){function n(n,t){n*=Na,t=t*Na/2+Sa/4;var e=n-r,o=e>=0?1:-1,a=o*e,c=Math.cos(t),s=Math.sin(t),l=i*s,f=u*c+l*Math.cos(a),h=l*o*Math.sin(a);hc.add(Math.atan2(h,f)),r=n,u=c,i=s}var t,e,r,u,i;gc.point=function(o,a){gc.point=n,r=(t=o)*Na,u=Math.cos(a=(e=a)*Na/2+Sa/4),i=Math.sin(a)},gc.lineEnd=function(){n(t,e)}}function se(n){var t=n[0],e=n[1],r=Math.cos(e);return[r*Math.cos(t),r*Math.sin(t),Math.sin(e)]}function le(n,t){return n[0]*t[0]+n[1]*t[1]+n[2]*t[2]}function fe(n,t){return[n[1]*t[2]-n[2]*t[1],n[2]*t[0]-n[0]*t[2],n[0]*t[1]-n[1]*t[0]]}function he(n,t){n[0]+=t[0],n[1]+=t[1],n[2]+=t[2]}function ge(n,t){return[n[0]*t,n[1]*t,n[2]*t]}function pe(n){var t=Math.sqrt(n[0]*n[0]+n[1]*n[1]+n[2]*n[2]);n[0]/=t,n[1]/=t,n[2]/=t}function ve(n){return[Math.atan2(n[1],n[0]),X(n[2])]}function de(n,t){return oa(n[0]-t[0])<Aa&&oa(n[1]-t[1])<Aa}function me(n,t){n*=Na;var e=Math.cos(t*=Na);ye(e*Math.cos(n),e*Math.sin(n),Math.sin(t))}function ye(n,t,e){++pc,dc+=(n-dc)/pc,mc+=(t-mc)/pc,yc+=(e-yc)/pc}function xe(){function n(n,u){n*=Na;var i=Math.cos(u*=Na),o=i*Math.cos(n),a=i*Math.sin(n),c=Math.sin(u),s=Math.atan2(Math.sqrt((s=e*c-r*a)*s+(s=r*o-t*c)*s+(s=t*a-e*o)*s),t*o+e*a+r*c);vc+=s,xc+=s*(t+(t=o)),Mc+=s*(e+(e=a)),_c+=s*(r+(r=c)),ye(t,e,r)}var t,e,r;kc.point=function(u,i){u*=Na;var o=Math.cos(i*=Na);t=o*Math.cos(u),e=o*Math.sin(u),r=Math.sin(i),kc.point=n,ye(t,e,r)}}function Me(){kc.point=me}function _e(){function n(n,t){n*=Na;var e=Math.cos(t*=Na),o=e*Math.cos(n),a=e*Math.sin(n),c=Math.sin(t),s=u*c-i*a,l=i*o-r*c,f=r*a-u*o,h=Math.sqrt(s*s+l*l+f*f),g=r*o+u*a+i*c,p=h&&-V(g)/h,v=Math.atan2(h,g);bc+=p*s,wc+=p*l,Sc+=p*f,vc+=v,xc+=v*(r+(r=o)),Mc+=v*(u+(u=a)),_c+=v*(i+(i=c)),ye(r,u,i)}var t,e,r,u,i;kc.point=function(o,a){t=o,e=a,kc.point=n,o*=Na;var c=Math.cos(a*=Na);r=c*Math.cos(o),u=c*Math.sin(o),i=Math.sin(a),ye(r,u,i)},kc.lineEnd=function(){n(t,e),kc.lineEnd=Me,kc.point=me}}function be(){return!0}function we(n,t,e,r,u){var i=[],o=[];if(n.forEach(function(n){if(!((t=n.length-1)<=0)){var t,e=n[0],r=n[t];if(de(e,r)){u.lineStart();for(var a=0;t>a;++a)u.point((e=n[a])[0],e[1]);return u.lineEnd(),void 0}var c=new ke(e,n,null,!0),s=new ke(e,null,c,!1);c.o=s,i.push(c),o.push(s),c=new ke(r,n,null,!1),s=new ke(r,null,c,!0),c.o=s,i.push(c),o.push(s)}}),o.sort(t),Se(i),Se(o),i.length){for(var a=0,c=e,s=o.length;s>a;++a)o[a].e=c=!c;for(var l,f,h=i[0];;){for(var g=h,p=!0;g.v;)if((g=g.n)===h)return;l=g.z,u.lineStart();do{if(g.v=g.o.v=!0,g.e){if(p)for(var a=0,s=l.length;s>a;++a)u.point((f=l[a])[0],f[1]);else r(g.x,g.n.x,1,u);g=g.n}else{if(p){l=g.p.z;for(var a=l.length-1;a>=0;--a)u.point((f=l[a])[0],f[1])}else r(g.x,g.p.x,-1,u);g=g.p}g=g.o,l=g.z,p=!p}while(!g.v);u.lineEnd()}}}function Se(n){if(t=n.length){for(var t,e,r=0,u=n[0];++r<t;)u.n=e=n[r],e.p=u,u=e;u.n=e=n[0],e.p=u}}function ke(n,t,e,r){this.x=n,this.z=t,this.o=e,this.e=r,this.v=!1,this.n=this.p=null}function Ee(n,t,e,r){return function(u,i){function o(t,e){var r=u(t,e);n(t=r[0],e=r[1])&&i.point(t,e)}function a(n,t){var e=u(n,t);d.point(e[0],e[1])}function c(){y.point=a,d.lineStart()}function s(){y.point=o,d.lineEnd()}function l(n,t){v.push([n,t]);var e=u(n,t);M.point(e[0],e[1])}function f(){M.lineStart(),v=[]}function h(){l(v[0][0],v[0][1]),M.lineEnd();var n,t=M.clean(),e=x.buffer(),r=e.length;if(v.pop(),p.push(v),v=null,r){if(1&t){n=e[0];var u,r=n.length-1,o=-1;for(i.lineStart();++o<r;)i.point((u=n[o])[0],u[1]);return i.lineEnd(),void 0}r>1&&2&t&&e.push(e.pop().concat(e.shift())),g.push(e.filter(Ae))}}var g,p,v,d=t(i),m=u.invert(r[0],r[1]),y={point:o,lineStart:c,lineEnd:s,polygonStart:function(){y.point=l,y.lineStart=f,y.lineEnd=h,g=[],p=[],i.polygonStart()},polygonEnd:function(){y.point=o,y.lineStart=c,y.lineEnd=s,g=Xo.merge(g);var n=Le(m,p);g.length?we(g,Ne,n,e,i):n&&(i.lineStart(),e(null,null,1,i),i.lineEnd()),i.polygonEnd(),g=p=null},sphere:function(){i.polygonStart(),i.lineStart(),e(null,null,1,i),i.lineEnd(),i.polygonEnd()}},x=Ce(),M=t(x);return y}}function Ae(n){return n.length>1}function Ce(){var n,t=[];return{lineStart:function(){t.push(n=[])},point:function(t,e){n.push([t,e])},lineEnd:g,buffer:function(){var e=t;return t=[],n=null,e},rejoin:function(){t.length>1&&t.push(t.pop().concat(t.shift()))}}}function Ne(n,t){return((n=n.x)[0]<0?n[1]-Ea-Aa:Ea-n[1])-((t=t.x)[0]<0?t[1]-Ea-Aa:Ea-t[1])}function Le(n,t){var e=n[0],r=n[1],u=[Math.sin(e),-Math.cos(e),0],i=0,o=0;hc.reset();for(var a=0,c=t.length;c>a;++a){var s=t[a],l=s.length;if(l)for(var f=s[0],h=f[0],g=f[1]/2+Sa/4,p=Math.sin(g),v=Math.cos(g),d=1;;){d===l&&(d=0),n=s[d];var m=n[0],y=n[1]/2+Sa/4,x=Math.sin(y),M=Math.cos(y),_=m-h,b=_>=0?1:-1,w=b*_,S=w>Sa,k=p*x;if(hc.add(Math.atan2(k*b*Math.sin(w),v*M+k*Math.cos(w))),i+=S?_+b*ka:_,S^h>=e^m>=e){var E=fe(se(f),se(n));pe(E);var A=fe(u,E);pe(A);var C=(S^_>=0?-1:1)*X(A[2]);(r>C||r===C&&(E[0]||E[1]))&&(o+=S^_>=0?1:-1)}if(!d++)break;h=m,p=x,v=M,f=n}}return(-Aa>i||Aa>i&&0>hc)^1&o}function Te(n){var t,e=0/0,r=0/0,u=0/0;return{lineStart:function(){n.lineStart(),t=1},point:function(i,o){var a=i>0?Sa:-Sa,c=oa(i-e);oa(c-Sa)<Aa?(n.point(e,r=(r+o)/2>0?Ea:-Ea),n.point(u,r),n.lineEnd(),n.lineStart(),n.point(a,r),n.point(i,r),t=0):u!==a&&c>=Sa&&(oa(e-u)<Aa&&(e-=u*Aa),oa(i-a)<Aa&&(i-=a*Aa),r=qe(e,r,i,o),n.point(u,r),n.lineEnd(),n.lineStart(),n.point(a,r),t=0),n.point(e=i,r=o),u=a},lineEnd:function(){n.lineEnd(),e=r=0/0},clean:function(){return 2-t}}}function qe(n,t,e,r){var u,i,o=Math.sin(n-e);return oa(o)>Aa?Math.atan((Math.sin(t)*(i=Math.cos(r))*Math.sin(e)-Math.sin(r)*(u=Math.cos(t))*Math.sin(n))/(u*i*o)):(t+r)/2}function ze(n,t,e,r){var u;if(null==n)u=e*Ea,r.point(-Sa,u),r.point(0,u),r.point(Sa,u),r.point(Sa,0),r.point(Sa,-u),r.point(0,-u),r.point(-Sa,-u),r.point(-Sa,0),r.point(-Sa,u);else if(oa(n[0]-t[0])>Aa){var i=n[0]<t[0]?Sa:-Sa;u=e*i/2,r.point(-i,u),r.point(0,u),r.point(i,u)}else r.point(t[0],t[1])}function Re(n){function t(n,t){return Math.cos(n)*Math.cos(t)>i}function e(n){var e,i,c,s,l;return{lineStart:function(){s=c=!1,l=1},point:function(f,h){var g,p=[f,h],v=t(f,h),d=o?v?0:u(f,h):v?u(f+(0>f?Sa:-Sa),h):0;if(!e&&(s=c=v)&&n.lineStart(),v!==c&&(g=r(e,p),(de(e,g)||de(p,g))&&(p[0]+=Aa,p[1]+=Aa,v=t(p[0],p[1]))),v!==c)l=0,v?(n.lineStart(),g=r(p,e),n.point(g[0],g[1])):(g=r(e,p),n.point(g[0],g[1]),n.lineEnd()),e=g;else if(a&&e&&o^v){var m;d&i||!(m=r(p,e,!0))||(l=0,o?(n.lineStart(),n.point(m[0][0],m[0][1]),n.point(m[1][0],m[1][1]),n.lineEnd()):(n.point(m[1][0],m[1][1]),n.lineEnd(),n.lineStart(),n.point(m[0][0],m[0][1])))}!v||e&&de(e,p)||n.point(p[0],p[1]),e=p,c=v,i=d},lineEnd:function(){c&&n.lineEnd(),e=null},clean:function(){return l|(s&&c)<<1}}}function r(n,t,e){var r=se(n),u=se(t),o=[1,0,0],a=fe(r,u),c=le(a,a),s=a[0],l=c-s*s;if(!l)return!e&&n;var f=i*c/l,h=-i*s/l,g=fe(o,a),p=ge(o,f),v=ge(a,h);he(p,v);var d=g,m=le(p,d),y=le(d,d),x=m*m-y*(le(p,p)-1);if(!(0>x)){var M=Math.sqrt(x),_=ge(d,(-m-M)/y);if(he(_,p),_=ve(_),!e)return _;var b,w=n[0],S=t[0],k=n[1],E=t[1];w>S&&(b=w,w=S,S=b);var A=S-w,C=oa(A-Sa)<Aa,N=C||Aa>A;if(!C&&k>E&&(b=k,k=E,E=b),N?C?k+E>0^_[1]<(oa(_[0]-w)<Aa?k:E):k<=_[1]&&_[1]<=E:A>Sa^(w<=_[0]&&_[0]<=S)){var L=ge(d,(-m+M)/y);return he(L,p),[_,ve(L)]}}}function u(t,e){var r=o?n:Sa-n,u=0;return-r>t?u|=1:t>r&&(u|=2),-r>e?u|=4:e>r&&(u|=8),u}var i=Math.cos(n),o=i>0,a=oa(i)>Aa,c=cr(n,6*Na);return Ee(t,e,c,o?[0,-n]:[-Sa,n-Sa])}function De(n,t,e,r){return function(u){var i,o=u.a,a=u.b,c=o.x,s=o.y,l=a.x,f=a.y,h=0,g=1,p=l-c,v=f-s;if(i=n-c,p||!(i>0)){if(i/=p,0>p){if(h>i)return;g>i&&(g=i)}else if(p>0){if(i>g)return;i>h&&(h=i)}if(i=e-c,p||!(0>i)){if(i/=p,0>p){if(i>g)return;i>h&&(h=i)}else if(p>0){if(h>i)return;g>i&&(g=i)}if(i=t-s,v||!(i>0)){if(i/=v,0>v){if(h>i)return;g>i&&(g=i)}else if(v>0){if(i>g)return;i>h&&(h=i)}if(i=r-s,v||!(0>i)){if(i/=v,0>v){if(i>g)return;i>h&&(h=i)}else if(v>0){if(h>i)return;g>i&&(g=i)}return h>0&&(u.a={x:c+h*p,y:s+h*v}),1>g&&(u.b={x:c+g*p,y:s+g*v}),u}}}}}}function Pe(n,t,e,r){function u(r,u){return oa(r[0]-n)<Aa?u>0?0:3:oa(r[0]-e)<Aa?u>0?2:1:oa(r[1]-t)<Aa?u>0?1:0:u>0?3:2}function i(n,t){return o(n.x,t.x)}function o(n,t){var e=u(n,1),r=u(t,1);return e!==r?e-r:0===e?t[1]-n[1]:1===e?n[0]-t[0]:2===e?n[1]-t[1]:t[0]-n[0]}return function(a){function c(n){for(var t=0,e=d.length,r=n[1],u=0;e>u;++u)for(var i,o=1,a=d[u],c=a.length,s=a[0];c>o;++o)i=a[o],s[1]<=r?i[1]>r&&Z(s,i,n)>0&&++t:i[1]<=r&&Z(s,i,n)<0&&--t,s=i;return 0!==t}function s(i,a,c,s){var l=0,f=0;if(null==i||(l=u(i,c))!==(f=u(a,c))||o(i,a)<0^c>0){do s.point(0===l||3===l?n:e,l>1?r:t);while((l=(l+c+4)%4)!==f)}else s.point(a[0],a[1])}function l(u,i){return u>=n&&e>=u&&i>=t&&r>=i}function f(n,t){l(n,t)&&a.point(n,t)}function h(){N.point=p,d&&d.push(m=[]),S=!0,w=!1,_=b=0/0}function g(){v&&(p(y,x),M&&w&&A.rejoin(),v.push(A.buffer())),N.point=f,w&&a.lineEnd()}function p(n,t){n=Math.max(-Ac,Math.min(Ac,n)),t=Math.max(-Ac,Math.min(Ac,t));var e=l(n,t);if(d&&m.push([n,t]),S)y=n,x=t,M=e,S=!1,e&&(a.lineStart(),a.point(n,t));else if(e&&w)a.point(n,t);else{var r={a:{x:_,y:b},b:{x:n,y:t}};C(r)?(w||(a.lineStart(),a.point(r.a.x,r.a.y)),a.point(r.b.x,r.b.y),e||a.lineEnd(),k=!1):e&&(a.lineStart(),a.point(n,t),k=!1)}_=n,b=t,w=e}var v,d,m,y,x,M,_,b,w,S,k,E=a,A=Ce(),C=De(n,t,e,r),N={point:f,lineStart:h,lineEnd:g,polygonStart:function(){a=A,v=[],d=[],k=!0},polygonEnd:function(){a=E,v=Xo.merge(v);var t=c([n,r]),e=k&&t,u=v.length;(e||u)&&(a.polygonStart(),e&&(a.lineStart(),s(null,null,1,a),a.lineEnd()),u&&we(v,i,t,s,a),a.polygonEnd()),v=d=m=null}};return N}}function Ue(n,t){function e(e,r){return e=n(e,r),t(e[0],e[1])}return n.invert&&t.invert&&(e.invert=function(e,r){return e=t.invert(e,r),e&&n.invert(e[0],e[1])}),e}function je(n){var t=0,e=Sa/3,r=nr(n),u=r(t,e);return u.parallels=function(n){return arguments.length?r(t=n[0]*Sa/180,e=n[1]*Sa/180):[180*(t/Sa),180*(e/Sa)]},u}function He(n,t){function e(n,t){var e=Math.sqrt(i-2*u*Math.sin(t))/u;return[e*Math.sin(n*=u),o-e*Math.cos(n)]}var r=Math.sin(n),u=(r+Math.sin(t))/2,i=1+r*(2*u-r),o=Math.sqrt(i)/u;return e.invert=function(n,t){var e=o-t;return[Math.atan2(n,e)/u,X((i-(n*n+e*e)*u*u)/(2*u))]},e}function Fe(){function n(n,t){Nc+=u*n-r*t,r=n,u=t}var t,e,r,u;Rc.point=function(i,o){Rc.point=n,t=r=i,e=u=o},Rc.lineEnd=function(){n(t,e)}}function Oe(n,t){Lc>n&&(Lc=n),n>qc&&(qc=n),Tc>t&&(Tc=t),t>zc&&(zc=t)}function Ye(){function n(n,t){o.push("M",n,",",t,i)}function t(n,t){o.push("M",n,",",t),a.point=e}function e(n,t){o.push("L",n,",",t)}function r(){a.point=n}function u(){o.push("Z")}var i=Ie(4.5),o=[],a={point:n,lineStart:function(){a.point=t},lineEnd:r,polygonStart:function(){a.lineEnd=u},polygonEnd:function(){a.lineEnd=r,a.point=n},pointRadius:function(n){return i=Ie(n),a},result:function(){if(o.length){var n=o.join("");return o=[],n}}};return a}function Ie(n){return"m0,"+n+"a"+n+","+n+" 0 1,1 0,"+-2*n+"a"+n+","+n+" 0 1,1 0,"+2*n+"z"}function Ze(n,t){dc+=n,mc+=t,++yc}function Ve(){function n(n,r){var u=n-t,i=r-e,o=Math.sqrt(u*u+i*i);xc+=o*(t+n)/2,Mc+=o*(e+r)/2,_c+=o,Ze(t=n,e=r)}var t,e;Pc.point=function(r,u){Pc.point=n,Ze(t=r,e=u)}}function Xe(){Pc.point=Ze}function $e(){function n(n,t){var e=n-r,i=t-u,o=Math.sqrt(e*e+i*i);xc+=o*(r+n)/2,Mc+=o*(u+t)/2,_c+=o,o=u*n-r*t,bc+=o*(r+n),wc+=o*(u+t),Sc+=3*o,Ze(r=n,u=t)}var t,e,r,u;Pc.point=function(i,o){Pc.point=n,Ze(t=r=i,e=u=o)},Pc.lineEnd=function(){n(t,e)}}function Be(n){function t(t,e){n.moveTo(t,e),n.arc(t,e,o,0,ka)}function e(t,e){n.moveTo(t,e),a.point=r}function r(t,e){n.lineTo(t,e)}function u(){a.point=t}function i(){n.closePath()}var o=4.5,a={point:t,lineStart:function(){a.point=e},lineEnd:u,polygonStart:function(){a.lineEnd=i},polygonEnd:function(){a.lineEnd=u,a.point=t},pointRadius:function(n){return o=n,a},result:g};return a}function We(n){function t(n){return(a?r:e)(n)}function e(t){return Ke(t,function(e,r){e=n(e,r),t.point(e[0],e[1])})}function r(t){function e(e,r){e=n(e,r),t.point(e[0],e[1])}function r(){x=0/0,S.point=i,t.lineStart()}function i(e,r){var i=se([e,r]),o=n(e,r);u(x,M,y,_,b,w,x=o[0],M=o[1],y=e,_=i[0],b=i[1],w=i[2],a,t),t.point(x,M)}function o(){S.point=e,t.lineEnd()}function c(){r(),S.point=s,S.lineEnd=l}function s(n,t){i(f=n,h=t),g=x,p=M,v=_,d=b,m=w,S.point=i}function l(){u(x,M,y,_,b,w,g,p,f,v,d,m,a,t),S.lineEnd=o,o()}var f,h,g,p,v,d,m,y,x,M,_,b,w,S={point:e,lineStart:r,lineEnd:o,polygonStart:function(){t.polygonStart(),S.lineStart=c},polygonEnd:function(){t.polygonEnd(),S.lineStart=r}};return S}function u(t,e,r,a,c,s,l,f,h,g,p,v,d,m){var y=l-t,x=f-e,M=y*y+x*x;if(M>4*i&&d--){var _=a+g,b=c+p,w=s+v,S=Math.sqrt(_*_+b*b+w*w),k=Math.asin(w/=S),E=oa(oa(w)-1)<Aa||oa(r-h)<Aa?(r+h)/2:Math.atan2(b,_),A=n(E,k),C=A[0],N=A[1],L=C-t,T=N-e,q=x*L-y*T;(q*q/M>i||oa((y*L+x*T)/M-.5)>.3||o>a*g+c*p+s*v)&&(u(t,e,r,a,c,s,C,N,E,_/=S,b/=S,w,d,m),m.point(C,N),u(C,N,E,_,b,w,l,f,h,g,p,v,d,m))}}var i=.5,o=Math.cos(30*Na),a=16;return t.precision=function(n){return arguments.length?(a=(i=n*n)>0&&16,t):Math.sqrt(i)},t}function Je(n){var t=We(function(t,e){return n([t*La,e*La])});return function(n){return tr(t(n))}}function Ge(n){this.stream=n}function Ke(n,t){return{point:t,sphere:function(){n.sphere()},lineStart:function(){n.lineStart()},lineEnd:function(){n.lineEnd()},polygonStart:function(){n.polygonStart()},polygonEnd:function(){n.polygonEnd()}}}function Qe(n){return nr(function(){return n})()}function nr(n){function t(n){return n=a(n[0]*Na,n[1]*Na),[n[0]*h+c,s-n[1]*h]}function e(n){return n=a.invert((n[0]-c)/h,(s-n[1])/h),n&&[n[0]*La,n[1]*La]}function r(){a=Ue(o=ur(m,y,x),i);var n=i(v,d);return c=g-n[0]*h,s=p+n[1]*h,u()}function u(){return l&&(l.valid=!1,l=null),t}var i,o,a,c,s,l,f=We(function(n,t){return n=i(n,t),[n[0]*h+c,s-n[1]*h]}),h=150,g=480,p=250,v=0,d=0,m=0,y=0,x=0,M=Ec,_=bt,b=null,w=null;return t.stream=function(n){return l&&(l.valid=!1),l=tr(M(o,f(_(n)))),l.valid=!0,l},t.clipAngle=function(n){return arguments.length?(M=null==n?(b=n,Ec):Re((b=+n)*Na),u()):b},t.clipExtent=function(n){return arguments.length?(w=n,_=n?Pe(n[0][0],n[0][1],n[1][0],n[1][1]):bt,u()):w},t.scale=function(n){return arguments.length?(h=+n,r()):h},t.translate=function(n){return arguments.length?(g=+n[0],p=+n[1],r()):[g,p]},t.center=function(n){return arguments.length?(v=n[0]%360*Na,d=n[1]%360*Na,r()):[v*La,d*La]},t.rotate=function(n){return arguments.length?(m=n[0]%360*Na,y=n[1]%360*Na,x=n.length>2?n[2]%360*Na:0,r()):[m*La,y*La,x*La]},Xo.rebind(t,f,"precision"),function(){return i=n.apply(this,arguments),t.invert=i.invert&&e,r()}}function tr(n){return Ke(n,function(t,e){n.point(t*Na,e*Na)})}function er(n,t){return[n,t]}function rr(n,t){return[n>Sa?n-ka:-Sa>n?n+ka:n,t]}function ur(n,t,e){return n?t||e?Ue(or(n),ar(t,e)):or(n):t||e?ar(t,e):rr}function ir(n){return function(t,e){return t+=n,[t>Sa?t-ka:-Sa>t?t+ka:t,e]}}function or(n){var t=ir(n);return t.invert=ir(-n),t}function ar(n,t){function e(n,t){var e=Math.cos(t),a=Math.cos(n)*e,c=Math.sin(n)*e,s=Math.sin(t),l=s*r+a*u;return[Math.atan2(c*i-l*o,a*r-s*u),X(l*i+c*o)]}var r=Math.cos(n),u=Math.sin(n),i=Math.cos(t),o=Math.sin(t);return e.invert=function(n,t){var e=Math.cos(t),a=Math.cos(n)*e,c=Math.sin(n)*e,s=Math.sin(t),l=s*i-c*o;return[Math.atan2(c*i+s*o,a*r+l*u),X(l*r-a*u)]},e}function cr(n,t){var e=Math.cos(n),r=Math.sin(n);return function(u,i,o,a){var c=o*t;null!=u?(u=sr(e,u),i=sr(e,i),(o>0?i>u:u>i)&&(u+=o*ka)):(u=n+o*ka,i=n-.5*c);for(var s,l=u;o>0?l>i:i>l;l-=c)a.point((s=ve([e,-r*Math.cos(l),-r*Math.sin(l)]))[0],s[1])}}function sr(n,t){var e=se(t);e[0]-=n,pe(e);var r=V(-e[1]);return((-e[2]<0?-r:r)+2*Math.PI-Aa)%(2*Math.PI)}function lr(n,t,e){var r=Xo.range(n,t-Aa,e).concat(t);return function(n){return r.map(function(t){return[n,t]})}}function fr(n,t,e){var r=Xo.range(n,t-Aa,e).concat(t);return function(n){return r.map(function(t){return[t,n]})}}function hr(n){return n.source}function gr(n){return n.target}function pr(n,t,e,r){var u=Math.cos(t),i=Math.sin(t),o=Math.cos(r),a=Math.sin(r),c=u*Math.cos(n),s=u*Math.sin(n),l=o*Math.cos(e),f=o*Math.sin(e),h=2*Math.asin(Math.sqrt(J(r-t)+u*o*J(e-n))),g=1/Math.sin(h),p=h?function(n){var t=Math.sin(n*=h)*g,e=Math.sin(h-n)*g,r=e*c+t*l,u=e*s+t*f,o=e*i+t*a;return[Math.atan2(u,r)*La,Math.atan2(o,Math.sqrt(r*r+u*u))*La]}:function(){return[n*La,t*La]};return p.distance=h,p}function vr(){function n(n,u){var i=Math.sin(u*=Na),o=Math.cos(u),a=oa((n*=Na)-t),c=Math.cos(a);Uc+=Math.atan2(Math.sqrt((a=o*Math.sin(a))*a+(a=r*i-e*o*c)*a),e*i+r*o*c),t=n,e=i,r=o}var t,e,r;jc.point=function(u,i){t=u*Na,e=Math.sin(i*=Na),r=Math.cos(i),jc.point=n},jc.lineEnd=function(){jc.point=jc.lineEnd=g}}function dr(n,t){function e(t,e){var r=Math.cos(t),u=Math.cos(e),i=n(r*u);return[i*u*Math.sin(t),i*Math.sin(e)]}return e.invert=function(n,e){var r=Math.sqrt(n*n+e*e),u=t(r),i=Math.sin(u),o=Math.cos(u);return[Math.atan2(n*i,r*o),Math.asin(r&&e*i/r)]},e}function mr(n,t){function e(n,t){var e=oa(oa(t)-Ea)<Aa?0:o/Math.pow(u(t),i);return[e*Math.sin(i*n),o-e*Math.cos(i*n)]}var r=Math.cos(n),u=function(n){return Math.tan(Sa/4+n/2)},i=n===t?Math.sin(n):Math.log(r/Math.cos(t))/Math.log(u(t)/u(n)),o=r*Math.pow(u(n),i)/i;return i?(e.invert=function(n,t){var e=o-t,r=I(i)*Math.sqrt(n*n+e*e);return[Math.atan2(n,e)/i,2*Math.atan(Math.pow(o/r,1/i))-Ea]},e):xr}function yr(n,t){function e(n,t){var e=i-t;return[e*Math.sin(u*n),i-e*Math.cos(u*n)]}var r=Math.cos(n),u=n===t?Math.sin(n):(r-Math.cos(t))/(t-n),i=r/u+n;return oa(u)<Aa?er:(e.invert=function(n,t){var e=i-t;return[Math.atan2(n,e)/u,i-I(u)*Math.sqrt(n*n+e*e)]},e)}function xr(n,t){return[n,Math.log(Math.tan(Sa/4+t/2))]}function Mr(n){var t,e=Qe(n),r=e.scale,u=e.translate,i=e.clipExtent;return e.scale=function(){var n=r.apply(e,arguments);return n===e?t?e.clipExtent(null):e:n},e.translate=function(){var n=u.apply(e,arguments);return n===e?t?e.clipExtent(null):e:n},e.clipExtent=function(n){var o=i.apply(e,arguments);if(o===e){if(t=null==n){var a=Sa*r(),c=u();i([[c[0]-a,c[1]-a],[c[0]+a,c[1]+a]])}}else t&&(o=null);return o},e.clipExtent(null)}function _r(n,t){return[Math.log(Math.tan(Sa/4+t/2)),-n]}function br(n){return n[0]}function wr(n){return n[1]}function Sr(n){for(var t=n.length,e=[0,1],r=2,u=2;t>u;u++){for(;r>1&&Z(n[e[r-2]],n[e[r-1]],n[u])<=0;)--r;e[r++]=u}return e.slice(0,r)}function kr(n,t){return n[0]-t[0]||n[1]-t[1]}function Er(n,t,e){return(e[0]-t[0])*(n[1]-t[1])<(e[1]-t[1])*(n[0]-t[0])}function Ar(n,t,e,r){var u=n[0],i=e[0],o=t[0]-u,a=r[0]-i,c=n[1],s=e[1],l=t[1]-c,f=r[1]-s,h=(a*(c-s)-f*(u-i))/(f*o-a*l);return[u+h*o,c+h*l]}function Cr(n){var t=n[0],e=n[n.length-1];return!(t[0]-e[0]||t[1]-e[1])}function Nr(){Jr(this),this.edge=this.site=this.circle=null}function Lr(n){var t=Jc.pop()||new Nr;return t.site=n,t}function Tr(n){Or(n),$c.remove(n),Jc.push(n),Jr(n)}function qr(n){var t=n.circle,e=t.x,r=t.cy,u={x:e,y:r},i=n.P,o=n.N,a=[n];Tr(n);for(var c=i;c.circle&&oa(e-c.circle.x)<Aa&&oa(r-c.circle.cy)<Aa;)i=c.P,a.unshift(c),Tr(c),c=i;a.unshift(c),Or(c);for(var s=o;s.circle&&oa(e-s.circle.x)<Aa&&oa(r-s.circle.cy)<Aa;)o=s.N,a.push(s),Tr(s),s=o;a.push(s),Or(s);var l,f=a.length;for(l=1;f>l;++l)s=a[l],c=a[l-1],$r(s.edge,c.site,s.site,u);c=a[0],s=a[f-1],s.edge=Vr(c.site,s.site,null,u),Fr(c),Fr(s)}function zr(n){for(var t,e,r,u,i=n.x,o=n.y,a=$c._;a;)if(r=Rr(a,o)-i,r>Aa)a=a.L;else{if(u=i-Dr(a,o),!(u>Aa)){r>-Aa?(t=a.P,e=a):u>-Aa?(t=a,e=a.N):t=e=a;break}if(!a.R){t=a;break}a=a.R}var c=Lr(n);if($c.insert(t,c),t||e){if(t===e)return Or(t),e=Lr(t.site),$c.insert(c,e),c.edge=e.edge=Vr(t.site,c.site),Fr(t),Fr(e),void 0;if(!e)return c.edge=Vr(t.site,c.site),void 0;Or(t),Or(e);var s=t.site,l=s.x,f=s.y,h=n.x-l,g=n.y-f,p=e.site,v=p.x-l,d=p.y-f,m=2*(h*d-g*v),y=h*h+g*g,x=v*v+d*d,M={x:(d*y-g*x)/m+l,y:(h*x-v*y)/m+f};$r(e.edge,s,p,M),c.edge=Vr(s,n,null,M),e.edge=Vr(n,p,null,M),Fr(t),Fr(e)}}function Rr(n,t){var e=n.site,r=e.x,u=e.y,i=u-t;if(!i)return r;var o=n.P;if(!o)return-1/0;e=o.site;var a=e.x,c=e.y,s=c-t;if(!s)return a;var l=a-r,f=1/i-1/s,h=l/s;return f?(-h+Math.sqrt(h*h-2*f*(l*l/(-2*s)-c+s/2+u-i/2)))/f+r:(r+a)/2}function Dr(n,t){var e=n.N;if(e)return Rr(e,t);var r=n.site;return r.y===t?r.x:1/0}function Pr(n){this.site=n,this.edges=[]}function Ur(n){for(var t,e,r,u,i,o,a,c,s,l,f=n[0][0],h=n[1][0],g=n[0][1],p=n[1][1],v=Xc,d=v.length;d--;)if(i=v[d],i&&i.prepare())for(a=i.edges,c=a.length,o=0;c>o;)l=a[o].end(),r=l.x,u=l.y,s=a[++o%c].start(),t=s.x,e=s.y,(oa(r-t)>Aa||oa(u-e)>Aa)&&(a.splice(o,0,new Br(Xr(i.site,l,oa(r-f)<Aa&&p-u>Aa?{x:f,y:oa(t-f)<Aa?e:p}:oa(u-p)<Aa&&h-r>Aa?{x:oa(e-p)<Aa?t:h,y:p}:oa(r-h)<Aa&&u-g>Aa?{x:h,y:oa(t-h)<Aa?e:g}:oa(u-g)<Aa&&r-f>Aa?{x:oa(e-g)<Aa?t:f,y:g}:null),i.site,null)),++c)}function jr(n,t){return t.angle-n.angle}function Hr(){Jr(this),this.x=this.y=this.arc=this.site=this.cy=null}function Fr(n){var t=n.P,e=n.N;if(t&&e){var r=t.site,u=n.site,i=e.site;if(r!==i){var o=u.x,a=u.y,c=r.x-o,s=r.y-a,l=i.x-o,f=i.y-a,h=2*(c*f-s*l);if(!(h>=-Ca)){var g=c*c+s*s,p=l*l+f*f,v=(f*g-s*p)/h,d=(c*p-l*g)/h,f=d+a,m=Gc.pop()||new Hr;m.arc=n,m.site=u,m.x=v+o,m.y=f+Math.sqrt(v*v+d*d),m.cy=f,n.circle=m;for(var y=null,x=Wc._;x;)if(m.y<x.y||m.y===x.y&&m.x<=x.x){if(!x.L){y=x.P;break}x=x.L}else{if(!x.R){y=x;break}x=x.R}Wc.insert(y,m),y||(Bc=m)}}}}function Or(n){var t=n.circle;t&&(t.P||(Bc=t.N),Wc.remove(t),Gc.push(t),Jr(t),n.circle=null)}function Yr(n){for(var t,e=Vc,r=De(n[0][0],n[0][1],n[1][0],n[1][1]),u=e.length;u--;)t=e[u],(!Ir(t,n)||!r(t)||oa(t.a.x-t.b.x)<Aa&&oa(t.a.y-t.b.y)<Aa)&&(t.a=t.b=null,e.splice(u,1))}function Ir(n,t){var e=n.b;if(e)return!0;var r,u,i=n.a,o=t[0][0],a=t[1][0],c=t[0][1],s=t[1][1],l=n.l,f=n.r,h=l.x,g=l.y,p=f.x,v=f.y,d=(h+p)/2,m=(g+v)/2;if(v===g){if(o>d||d>=a)return;if(h>p){if(i){if(i.y>=s)return}else i={x:d,y:c};e={x:d,y:s}}else{if(i){if(i.y<c)return}else i={x:d,y:s};e={x:d,y:c}}}else if(r=(h-p)/(v-g),u=m-r*d,-1>r||r>1)if(h>p){if(i){if(i.y>=s)return}else i={x:(c-u)/r,y:c};e={x:(s-u)/r,y:s}}else{if(i){if(i.y<c)return}else i={x:(s-u)/r,y:s};e={x:(c-u)/r,y:c}}else if(v>g){if(i){if(i.x>=a)return}else i={x:o,y:r*o+u};e={x:a,y:r*a+u}}else{if(i){if(i.x<o)return}else i={x:a,y:r*a+u};e={x:o,y:r*o+u}}return n.a=i,n.b=e,!0}function Zr(n,t){this.l=n,this.r=t,this.a=this.b=null}function Vr(n,t,e,r){var u=new Zr(n,t);return Vc.push(u),e&&$r(u,n,t,e),r&&$r(u,t,n,r),Xc[n.i].edges.push(new Br(u,n,t)),Xc[t.i].edges.push(new Br(u,t,n)),u}function Xr(n,t,e){var r=new Zr(n,null);return r.a=t,r.b=e,Vc.push(r),r}function $r(n,t,e,r){n.a||n.b?n.l===e?n.b=r:n.a=r:(n.a=r,n.l=t,n.r=e)}function Br(n,t,e){var r=n.a,u=n.b;this.edge=n,this.site=t,this.angle=e?Math.atan2(e.y-t.y,e.x-t.x):n.l===t?Math.atan2(u.x-r.x,r.y-u.y):Math.atan2(r.x-u.x,u.y-r.y)}function Wr(){this._=null}function Jr(n){n.U=n.C=n.L=n.R=n.P=n.N=null}function Gr(n,t){var e=t,r=t.R,u=e.U;u?u.L===e?u.L=r:u.R=r:n._=r,r.U=u,e.U=r,e.R=r.L,e.R&&(e.R.U=e),r.L=e}function Kr(n,t){var e=t,r=t.L,u=e.U;u?u.L===e?u.L=r:u.R=r:n._=r,r.U=u,e.U=r,e.L=r.R,e.L&&(e.L.U=e),r.R=e}function Qr(n){for(;n.L;)n=n.L;return n}function nu(n,t){var e,r,u,i=n.sort(tu).pop();for(Vc=[],Xc=new Array(n.length),$c=new Wr,Wc=new Wr;;)if(u=Bc,i&&(!u||i.y<u.y||i.y===u.y&&i.x<u.x))(i.x!==e||i.y!==r)&&(Xc[i.i]=new Pr(i),zr(i),e=i.x,r=i.y),i=n.pop();else{if(!u)break;qr(u.arc)}t&&(Yr(t),Ur(t));var o={cells:Xc,edges:Vc};return $c=Wc=Vc=Xc=null,o}function tu(n,t){return t.y-n.y||t.x-n.x}function eu(n,t,e){return(n.x-e.x)*(t.y-n.y)-(n.x-t.x)*(e.y-n.y)}function ru(n){return n.x}function uu(n){return n.y}function iu(){return{leaf:!0,nodes:[],point:null,x:null,y:null}}function ou(n,t,e,r,u,i){if(!n(t,e,r,u,i)){var o=.5*(e+u),a=.5*(r+i),c=t.nodes;c[0]&&ou(n,c[0],e,r,o,a),c[1]&&ou(n,c[1],o,r,u,a),c[2]&&ou(n,c[2],e,a,o,i),c[3]&&ou(n,c[3],o,a,u,i)}}function au(n,t){n=Xo.rgb(n),t=Xo.rgb(t);var e=n.r,r=n.g,u=n.b,i=t.r-e,o=t.g-r,a=t.b-u;return function(n){return"#"+vt(Math.round(e+i*n))+vt(Math.round(r+o*n))+vt(Math.round(u+a*n))}}function cu(n,t){var e,r={},u={};for(e in n)e in t?r[e]=fu(n[e],t[e]):u[e]=n[e];for(e in t)e in n||(u[e]=t[e]);return function(n){for(e in r)u[e]=r[e](n);return u}}function su(n,t){return t-=n=+n,function(e){return n+t*e}}function lu(n,t){var e,r,u,i,o,a=0,c=0,s=[],l=[];for(n+="",t+="",Qc.lastIndex=0,r=0;e=Qc.exec(t);++r)e.index&&s.push(t.substring(a,c=e.index)),l.push({i:s.length,x:e[0]}),s.push(null),a=Qc.lastIndex;for(a<t.length&&s.push(t.substring(a)),r=0,i=l.length;(e=Qc.exec(n))&&i>r;++r)if(o=l[r],o.x==e[0]){if(o.i)if(null==s[o.i+1])for(s[o.i-1]+=o.x,s.splice(o.i,1),u=r+1;i>u;++u)l[u].i--;else for(s[o.i-1]+=o.x+s[o.i+1],s.splice(o.i,2),u=r+1;i>u;++u)l[u].i-=2;else if(null==s[o.i+1])s[o.i]=o.x;else for(s[o.i]=o.x+s[o.i+1],s.splice(o.i+1,1),u=r+1;i>u;++u)l[u].i--;l.splice(r,1),i--,r--}else o.x=su(parseFloat(e[0]),parseFloat(o.x));for(;i>r;)o=l.pop(),null==s[o.i+1]?s[o.i]=o.x:(s[o.i]=o.x+s[o.i+1],s.splice(o.i+1,1)),i--;return 1===s.length?null==s[0]?(o=l[0].x,function(n){return o(n)+""}):function(){return t}:function(n){for(r=0;i>r;++r)s[(o=l[r]).i]=o.x(n);return s.join("")}}function fu(n,t){for(var e,r=Xo.interpolators.length;--r>=0&&!(e=Xo.interpolators[r](n,t)););return e}function hu(n,t){var e,r=[],u=[],i=n.length,o=t.length,a=Math.min(n.length,t.length);for(e=0;a>e;++e)r.push(fu(n[e],t[e]));for(;i>e;++e)u[e]=n[e];for(;o>e;++e)u[e]=t[e];return function(n){for(e=0;a>e;++e)u[e]=r[e](n);return u}}function gu(n){return function(t){return 0>=t?0:t>=1?1:n(t)}}function pu(n){return function(t){return 1-n(1-t)}}function vu(n){return function(t){return.5*(.5>t?n(2*t):2-n(2-2*t))}}function du(n){return n*n}function mu(n){return n*n*n}function yu(n){if(0>=n)return 0;if(n>=1)return 1;var t=n*n,e=t*n;return 4*(.5>n?e:3*(n-t)+e-.75)}function xu(n){return function(t){return Math.pow(t,n)}}function Mu(n){return 1-Math.cos(n*Ea)}function _u(n){return Math.pow(2,10*(n-1))}function bu(n){return 1-Math.sqrt(1-n*n)}function wu(n,t){var e;return arguments.length<2&&(t=.45),arguments.length?e=t/ka*Math.asin(1/n):(n=1,e=t/4),function(r){return 1+n*Math.pow(2,-10*r)*Math.sin((r-e)*ka/t)}}function Su(n){return n||(n=1.70158),function(t){return t*t*((n+1)*t-n)}}function ku(n){return 1/2.75>n?7.5625*n*n:2/2.75>n?7.5625*(n-=1.5/2.75)*n+.75:2.5/2.75>n?7.5625*(n-=2.25/2.75)*n+.9375:7.5625*(n-=2.625/2.75)*n+.984375}function Eu(n,t){n=Xo.hcl(n),t=Xo.hcl(t);var e=n.h,r=n.c,u=n.l,i=t.h-e,o=t.c-r,a=t.l-u;return isNaN(o)&&(o=0,r=isNaN(r)?t.c:r),isNaN(i)?(i=0,e=isNaN(e)?t.h:e):i>180?i-=360:-180>i&&(i+=360),function(n){return rt(e+i*n,r+o*n,u+a*n)+""}}function Au(n,t){n=Xo.hsl(n),t=Xo.hsl(t);var e=n.h,r=n.s,u=n.l,i=t.h-e,o=t.s-r,a=t.l-u;return isNaN(o)&&(o=0,r=isNaN(r)?t.s:r),isNaN(i)?(i=0,e=isNaN(e)?t.h:e):i>180?i-=360:-180>i&&(i+=360),function(n){return nt(e+i*n,r+o*n,u+a*n)+""}}function Cu(n,t){n=Xo.lab(n),t=Xo.lab(t);var e=n.l,r=n.a,u=n.b,i=t.l-e,o=t.a-r,a=t.b-u;return function(n){return ot(e+i*n,r+o*n,u+a*n)+""}}function Nu(n,t){return t-=n,function(e){return Math.round(n+t*e)}}function Lu(n){var t=[n.a,n.b],e=[n.c,n.d],r=qu(t),u=Tu(t,e),i=qu(zu(e,t,-u))||0;t[0]*e[1]<e[0]*t[1]&&(t[0]*=-1,t[1]*=-1,r*=-1,u*=-1),this.rotate=(r?Math.atan2(t[1],t[0]):Math.atan2(-e[0],e[1]))*La,this.translate=[n.e,n.f],this.scale=[r,i],this.skew=i?Math.atan2(u,i)*La:0}function Tu(n,t){return n[0]*t[0]+n[1]*t[1]}function qu(n){var t=Math.sqrt(Tu(n,n));return t&&(n[0]/=t,n[1]/=t),t}function zu(n,t,e){return n[0]+=e*t[0],n[1]+=e*t[1],n}function Ru(n,t){var e,r=[],u=[],i=Xo.transform(n),o=Xo.transform(t),a=i.translate,c=o.translate,s=i.rotate,l=o.rotate,f=i.skew,h=o.skew,g=i.scale,p=o.scale;return a[0]!=c[0]||a[1]!=c[1]?(r.push("translate(",null,",",null,")"),u.push({i:1,x:su(a[0],c[0])},{i:3,x:su(a[1],c[1])})):c[0]||c[1]?r.push("translate("+c+")"):r.push(""),s!=l?(s-l>180?l+=360:l-s>180&&(s+=360),u.push({i:r.push(r.pop()+"rotate(",null,")")-2,x:su(s,l)})):l&&r.push(r.pop()+"rotate("+l+")"),f!=h?u.push({i:r.push(r.pop()+"skewX(",null,")")-2,x:su(f,h)}):h&&r.push(r.pop()+"skewX("+h+")"),g[0]!=p[0]||g[1]!=p[1]?(e=r.push(r.pop()+"scale(",null,",",null,")"),u.push({i:e-4,x:su(g[0],p[0])},{i:e-2,x:su(g[1],p[1])})):(1!=p[0]||1!=p[1])&&r.push(r.pop()+"scale("+p+")"),e=u.length,function(n){for(var t,i=-1;++i<e;)r[(t=u[i]).i]=t.x(n);return r.join("")}}function Du(n,t){return t=t-(n=+n)?1/(t-n):0,function(e){return(e-n)*t}}function Pu(n,t){return t=t-(n=+n)?1/(t-n):0,function(e){return Math.max(0,Math.min(1,(e-n)*t))}}function Uu(n){for(var t=n.source,e=n.target,r=Hu(t,e),u=[t];t!==r;)t=t.parent,u.push(t);for(var i=u.length;e!==r;)u.splice(i,0,e),e=e.parent;return u}function ju(n){for(var t=[],e=n.parent;null!=e;)t.push(n),n=e,e=e.parent;return t.push(n),t}function Hu(n,t){if(n===t)return n;for(var e=ju(n),r=ju(t),u=e.pop(),i=r.pop(),o=null;u===i;)o=u,u=e.pop(),i=r.pop();return o}function Fu(n){n.fixed|=2}function Ou(n){n.fixed&=-7}function Yu(n){n.fixed|=4,n.px=n.x,n.py=n.y}function Iu(n){n.fixed&=-5}function Zu(n,t,e){var r=0,u=0;if(n.charge=0,!n.leaf)for(var i,o=n.nodes,a=o.length,c=-1;++c<a;)i=o[c],null!=i&&(Zu(i,t,e),n.charge+=i.charge,r+=i.charge*i.cx,u+=i.charge*i.cy);if(n.point){n.leaf||(n.point.x+=Math.random()-.5,n.point.y+=Math.random()-.5);var s=t*e[n.point.index];n.charge+=n.pointCharge=s,r+=s*n.point.x,u+=s*n.point.y}n.cx=r/n.charge,n.cy=u/n.charge}function Vu(n,t){return Xo.rebind(n,t,"sort","children","value"),n.nodes=n,n.links=Wu,n}function Xu(n){return n.children}function $u(n){return n.value}function Bu(n,t){return t.value-n.value}function Wu(n){return Xo.merge(n.map(function(n){return(n.children||[]).map(function(t){return{source:n,target:t}})}))}function Ju(n){return n.x}function Gu(n){return n.y}function Ku(n,t,e){n.y0=t,n.y=e}function Qu(n){return Xo.range(n.length)}function ni(n){for(var t=-1,e=n[0].length,r=[];++t<e;)r[t]=0;return r}function ti(n){for(var t,e=1,r=0,u=n[0][1],i=n.length;i>e;++e)(t=n[e][1])>u&&(r=e,u=t);return r}function ei(n){return n.reduce(ri,0)}function ri(n,t){return n+t[1]}function ui(n,t){return ii(n,Math.ceil(Math.log(t.length)/Math.LN2+1))}function ii(n,t){for(var e=-1,r=+n[0],u=(n[1]-r)/t,i=[];++e<=t;)i[e]=u*e+r;return i}function oi(n){return[Xo.min(n),Xo.max(n)]}function ai(n,t){return n.parent==t.parent?1:2}function ci(n){var t=n.children;return t&&t.length?t[0]:n._tree.thread}function si(n){var t,e=n.children;return e&&(t=e.length)?e[t-1]:n._tree.thread}function li(n,t){var e=n.children;if(e&&(u=e.length))for(var r,u,i=-1;++i<u;)t(r=li(e[i],t),n)>0&&(n=r);return n}function fi(n,t){return n.x-t.x}function hi(n,t){return t.x-n.x}function gi(n,t){return n.depth-t.depth}function pi(n,t){function e(n,r){var u=n.children;if(u&&(o=u.length))for(var i,o,a=null,c=-1;++c<o;)i=u[c],e(i,a),a=i;t(n,r)}e(n,null)}function vi(n){for(var t,e=0,r=0,u=n.children,i=u.length;--i>=0;)t=u[i]._tree,t.prelim+=e,t.mod+=e,e+=t.shift+(r+=t.change)}function di(n,t,e){n=n._tree,t=t._tree;var r=e/(t.number-n.number);n.change+=r,t.change-=r,t.shift+=e,t.prelim+=e,t.mod+=e}function mi(n,t,e){return n._tree.ancestor.parent==t.parent?n._tree.ancestor:e}function yi(n,t){return n.value-t.value}function xi(n,t){var e=n._pack_next;n._pack_next=t,t._pack_prev=n,t._pack_next=e,e._pack_prev=t}function Mi(n,t){n._pack_next=t,t._pack_prev=n}function _i(n,t){var e=t.x-n.x,r=t.y-n.y,u=n.r+t.r;return.999*u*u>e*e+r*r}function bi(n){function t(n){l=Math.min(n.x-n.r,l),f=Math.max(n.x+n.r,f),h=Math.min(n.y-n.r,h),g=Math.max(n.y+n.r,g)}if((e=n.children)&&(s=e.length)){var e,r,u,i,o,a,c,s,l=1/0,f=-1/0,h=1/0,g=-1/0;if(e.forEach(wi),r=e[0],r.x=-r.r,r.y=0,t(r),s>1&&(u=e[1],u.x=u.r,u.y=0,t(u),s>2))for(i=e[2],Ei(r,u,i),t(i),xi(r,i),r._pack_prev=i,xi(i,u),u=r._pack_next,o=3;s>o;o++){Ei(r,u,i=e[o]);var p=0,v=1,d=1;for(a=u._pack_next;a!==u;a=a._pack_next,v++)if(_i(a,i)){p=1;break}if(1==p)for(c=r._pack_prev;c!==a._pack_prev&&!_i(c,i);c=c._pack_prev,d++);p?(d>v||v==d&&u.r<r.r?Mi(r,u=a):Mi(r=c,u),o--):(xi(r,i),u=i,t(i))}var m=(l+f)/2,y=(h+g)/2,x=0;for(o=0;s>o;o++)i=e[o],i.x-=m,i.y-=y,x=Math.max(x,i.r+Math.sqrt(i.x*i.x+i.y*i.y));n.r=x,e.forEach(Si)}}function wi(n){n._pack_next=n._pack_prev=n}function Si(n){delete n._pack_next,delete n._pack_prev}function ki(n,t,e,r){var u=n.children;if(n.x=t+=r*n.x,n.y=e+=r*n.y,n.r*=r,u)for(var i=-1,o=u.length;++i<o;)ki(u[i],t,e,r)}function Ei(n,t,e){var r=n.r+e.r,u=t.x-n.x,i=t.y-n.y;if(r&&(u||i)){var o=t.r+e.r,a=u*u+i*i;o*=o,r*=r;var c=.5+(r-o)/(2*a),s=Math.sqrt(Math.max(0,2*o*(r+a)-(r-=a)*r-o*o))/(2*a);e.x=n.x+c*u+s*i,e.y=n.y+c*i-s*u}else e.x=n.x+r,e.y=n.y}function Ai(n){return 1+Xo.max(n,function(n){return n.y})}function Ci(n){return n.reduce(function(n,t){return n+t.x},0)/n.length}function Ni(n){var t=n.children;return t&&t.length?Ni(t[0]):n}function Li(n){var t,e=n.children;return e&&(t=e.length)?Li(e[t-1]):n}function Ti(n){return{x:n.x,y:n.y,dx:n.dx,dy:n.dy}}function qi(n,t){var e=n.x+t[3],r=n.y+t[0],u=n.dx-t[1]-t[3],i=n.dy-t[0]-t[2];return 0>u&&(e+=u/2,u=0),0>i&&(r+=i/2,i=0),{x:e,y:r,dx:u,dy:i}}function zi(n){var t=n[0],e=n[n.length-1];return e>t?[t,e]:[e,t]}function Ri(n){return n.rangeExtent?n.rangeExtent():zi(n.range())}function Di(n,t,e,r){var u=e(n[0],n[1]),i=r(t[0],t[1]);return function(n){return i(u(n))}}function Pi(n,t){var e,r=0,u=n.length-1,i=n[r],o=n[u];return i>o&&(e=r,r=u,u=e,e=i,i=o,o=e),n[r]=t.floor(i),n[u]=t.ceil(o),n}function Ui(n){return n?{floor:function(t){return Math.floor(t/n)*n},ceil:function(t){return Math.ceil(t/n)*n}}:ls}function ji(n,t,e,r){var u=[],i=[],o=0,a=Math.min(n.length,t.length)-1;for(n[a]<n[0]&&(n=n.slice().reverse(),t=t.slice().reverse());++o<=a;)u.push(e(n[o-1],n[o])),i.push(r(t[o-1],t[o]));return function(t){var e=Xo.bisect(n,t,1,a)-1;return i[e](u[e](t))}}function Hi(n,t,e,r){function u(){var u=Math.min(n.length,t.length)>2?ji:Di,c=r?Pu:Du;return o=u(n,t,c,e),a=u(t,n,c,fu),i}function i(n){return o(n)}var o,a;return i.invert=function(n){return a(n)},i.domain=function(t){return arguments.length?(n=t.map(Number),u()):n},i.range=function(n){return arguments.length?(t=n,u()):t},i.rangeRound=function(n){return i.range(n).interpolate(Nu)},i.clamp=function(n){return arguments.length?(r=n,u()):r},i.interpolate=function(n){return arguments.length?(e=n,u()):e},i.ticks=function(t){return Ii(n,t)},i.tickFormat=function(t,e){return Zi(n,t,e)},i.nice=function(t){return Oi(n,t),u()},i.copy=function(){return Hi(n,t,e,r)},u()}function Fi(n,t){return Xo.rebind(n,t,"range","rangeRound","interpolate","clamp")}function Oi(n,t){return Pi(n,Ui(Yi(n,t)[2]))}function Yi(n,t){null==t&&(t=10);var e=zi(n),r=e[1]-e[0],u=Math.pow(10,Math.floor(Math.log(r/t)/Math.LN10)),i=t/r*u;return.15>=i?u*=10:.35>=i?u*=5:.75>=i&&(u*=2),e[0]=Math.ceil(e[0]/u)*u,e[1]=Math.floor(e[1]/u)*u+.5*u,e[2]=u,e}function Ii(n,t){return Xo.range.apply(Xo,Yi(n,t))}function Zi(n,t,e){var r=Yi(n,t);return Xo.format(e?e.replace(Qa,function(n,t,e,u,i,o,a,c,s,l){return[t,e,u,i,o,a,c,s||"."+Xi(l,r),l].join("")}):",."+Vi(r[2])+"f")}function Vi(n){return-Math.floor(Math.log(n)/Math.LN10+.01)}function Xi(n,t){var e=Vi(t[2]);return n in fs?Math.abs(e-Vi(Math.max(Math.abs(t[0]),Math.abs(t[1]))))+ +("e"!==n):e-2*("%"===n)}function $i(n,t,e,r){function u(n){return(e?Math.log(0>n?0:n):-Math.log(n>0?0:-n))/Math.log(t)}function i(n){return e?Math.pow(t,n):-Math.pow(t,-n)}function o(t){return n(u(t))}return o.invert=function(t){return i(n.invert(t))},o.domain=function(t){return arguments.length?(e=t[0]>=0,n.domain((r=t.map(Number)).map(u)),o):r},o.base=function(e){return arguments.length?(t=+e,n.domain(r.map(u)),o):t},o.nice=function(){var t=Pi(r.map(u),e?Math:gs);return n.domain(t),r=t.map(i),o},o.ticks=function(){var n=zi(r),o=[],a=n[0],c=n[1],s=Math.floor(u(a)),l=Math.ceil(u(c)),f=t%1?2:t;if(isFinite(l-s)){if(e){for(;l>s;s++)for(var h=1;f>h;h++)o.push(i(s)*h);o.push(i(s))}else for(o.push(i(s));s++<l;)for(var h=f-1;h>0;h--)o.push(i(s)*h);for(s=0;o[s]<a;s++);for(l=o.length;o[l-1]>c;l--);o=o.slice(s,l)}return o},o.tickFormat=function(n,t){if(!arguments.length)return hs;arguments.length<2?t=hs:"function"!=typeof t&&(t=Xo.format(t));var r,a=Math.max(.1,n/o.ticks().length),c=e?(r=1e-12,Math.ceil):(r=-1e-12,Math.floor);return function(n){return n/i(c(u(n)+r))<=a?t(n):""}},o.copy=function(){return $i(n.copy(),t,e,r)},Fi(o,n)}function Bi(n,t,e){function r(t){return n(u(t))}var u=Wi(t),i=Wi(1/t);return r.invert=function(t){return i(n.invert(t))},r.domain=function(t){return arguments.length?(n.domain((e=t.map(Number)).map(u)),r):e},r.ticks=function(n){return Ii(e,n)},r.tickFormat=function(n,t){return Zi(e,n,t)},r.nice=function(n){return r.domain(Oi(e,n))},r.exponent=function(o){return arguments.length?(u=Wi(t=o),i=Wi(1/t),n.domain(e.map(u)),r):t},r.copy=function(){return Bi(n.copy(),t,e)},Fi(r,n)}function Wi(n){return function(t){return 0>t?-Math.pow(-t,n):Math.pow(t,n)}}function Ji(n,t){function e(e){return o[((i.get(e)||"range"===t.t&&i.set(e,n.push(e)))-1)%o.length]}function r(t,e){return Xo.range(n.length).map(function(n){return t+e*n})}var i,o,a;return e.domain=function(r){if(!arguments.length)return n;n=[],i=new u;for(var o,a=-1,c=r.length;++a<c;)i.has(o=r[a])||i.set(o,n.push(o));return e[t.t].apply(e,t.a)},e.range=function(n){return arguments.length?(o=n,a=0,t={t:"range",a:arguments},e):o},e.rangePoints=function(u,i){arguments.length<2&&(i=0);var c=u[0],s=u[1],l=(s-c)/(Math.max(1,n.length-1)+i);return o=r(n.length<2?(c+s)/2:c+l*i/2,l),a=0,t={t:"rangePoints",a:arguments},e},e.rangeBands=function(u,i,c){arguments.length<2&&(i=0),arguments.length<3&&(c=i);var s=u[1]<u[0],l=u[s-0],f=u[1-s],h=(f-l)/(n.length-i+2*c);return o=r(l+h*c,h),s&&o.reverse(),a=h*(1-i),t={t:"rangeBands",a:arguments},e},e.rangeRoundBands=function(u,i,c){arguments.length<2&&(i=0),arguments.length<3&&(c=i);var s=u[1]<u[0],l=u[s-0],f=u[1-s],h=Math.floor((f-l)/(n.length-i+2*c)),g=f-l-(n.length-i)*h;return o=r(l+Math.round(g/2),h),s&&o.reverse(),a=Math.round(h*(1-i)),t={t:"rangeRoundBands",a:arguments},e},e.rangeBand=function(){return a},e.rangeExtent=function(){return zi(t.a[0])},e.copy=function(){return Ji(n,t)},e.domain(n)}function Gi(n,t){function e(){var e=0,i=t.length;for(u=[];++e<i;)u[e-1]=Xo.quantile(n,e/i);return r}function r(n){return isNaN(n=+n)?void 0:t[Xo.bisect(u,n)]}var u;return r.domain=function(t){return arguments.length?(n=t.filter(function(n){return!isNaN(n)}).sort(Xo.ascending),e()):n},r.range=function(n){return arguments.length?(t=n,e()):t},r.quantiles=function(){return u},r.invertExtent=function(e){return e=t.indexOf(e),0>e?[0/0,0/0]:[e>0?u[e-1]:n[0],e<u.length?u[e]:n[n.length-1]]},r.copy=function(){return Gi(n,t)},e()}function Ki(n,t,e){function r(t){return e[Math.max(0,Math.min(o,Math.floor(i*(t-n))))]}function u(){return i=e.length/(t-n),o=e.length-1,r}var i,o;return r.domain=function(e){return arguments.length?(n=+e[0],t=+e[e.length-1],u()):[n,t]},r.range=function(n){return arguments.length?(e=n,u()):e},r.invertExtent=function(t){return t=e.indexOf(t),t=0>t?0/0:t/i+n,[t,t+1/i]},r.copy=function(){return Ki(n,t,e)},u()}function Qi(n,t){function e(e){return e>=e?t[Xo.bisect(n,e)]:void 0}return e.domain=function(t){return arguments.length?(n=t,e):n},e.range=function(n){return arguments.length?(t=n,e):t},e.invertExtent=function(e){return e=t.indexOf(e),[n[e-1],n[e]]},e.copy=function(){return Qi(n,t)},e}function no(n){function t(n){return+n}return t.invert=t,t.domain=t.range=function(e){return arguments.length?(n=e.map(t),t):n},t.ticks=function(t){return Ii(n,t)},t.tickFormat=function(t,e){return Zi(n,t,e)},t.copy=function(){return no(n)},t}function to(n){return n.innerRadius}function eo(n){return n.outerRadius}function ro(n){return n.startAngle}function uo(n){return n.endAngle}function io(n){function t(t){function o(){s.push("M",i(n(l),a))}for(var c,s=[],l=[],f=-1,h=t.length,g=_t(e),p=_t(r);++f<h;)u.call(this,c=t[f],f)?l.push([+g.call(this,c,f),+p.call(this,c,f)]):l.length&&(o(),l=[]);return l.length&&o(),s.length?s.join(""):null}var e=br,r=wr,u=be,i=oo,o=i.key,a=.7;return t.x=function(n){return arguments.length?(e=n,t):e},t.y=function(n){return arguments.length?(r=n,t):r},t.defined=function(n){return arguments.length?(u=n,t):u},t.interpolate=function(n){return arguments.length?(o="function"==typeof n?i=n:(i=Ms.get(n)||oo).key,t):o},t.tension=function(n){return arguments.length?(a=n,t):a},t}function oo(n){return n.join("L")}function ao(n){return oo(n)+"Z"}function co(n){for(var t=0,e=n.length,r=n[0],u=[r[0],",",r[1]];++t<e;)u.push("H",(r[0]+(r=n[t])[0])/2,"V",r[1]);return e>1&&u.push("H",r[0]),u.join("")}function so(n){for(var t=0,e=n.length,r=n[0],u=[r[0],",",r[1]];++t<e;)u.push("V",(r=n[t])[1],"H",r[0]);return u.join("")}function lo(n){for(var t=0,e=n.length,r=n[0],u=[r[0],",",r[1]];++t<e;)u.push("H",(r=n[t])[0],"V",r[1]);return u.join("")}function fo(n,t){return n.length<4?oo(n):n[1]+po(n.slice(1,n.length-1),vo(n,t))}function ho(n,t){return n.length<3?oo(n):n[0]+po((n.push(n[0]),n),vo([n[n.length-2]].concat(n,[n[1]]),t))}function go(n,t){return n.length<3?oo(n):n[0]+po(n,vo(n,t))}function po(n,t){if(t.length<1||n.length!=t.length&&n.length!=t.length+2)return oo(n);var e=n.length!=t.length,r="",u=n[0],i=n[1],o=t[0],a=o,c=1;if(e&&(r+="Q"+(i[0]-2*o[0]/3)+","+(i[1]-2*o[1]/3)+","+i[0]+","+i[1],u=n[1],c=2),t.length>1){a=t[1],i=n[c],c++,r+="C"+(u[0]+o[0])+","+(u[1]+o[1])+","+(i[0]-a[0])+","+(i[1]-a[1])+","+i[0]+","+i[1];for(var s=2;s<t.length;s++,c++)i=n[c],a=t[s],r+="S"+(i[0]-a[0])+","+(i[1]-a[1])+","+i[0]+","+i[1]}if(e){var l=n[c];r+="Q"+(i[0]+2*a[0]/3)+","+(i[1]+2*a[1]/3)+","+l[0]+","+l[1]}return r}function vo(n,t){for(var e,r=[],u=(1-t)/2,i=n[0],o=n[1],a=1,c=n.length;++a<c;)e=i,i=o,o=n[a],r.push([u*(o[0]-e[0]),u*(o[1]-e[1])]);return r}function mo(n){if(n.length<3)return oo(n);var t=1,e=n.length,r=n[0],u=r[0],i=r[1],o=[u,u,u,(r=n[1])[0]],a=[i,i,i,r[1]],c=[u,",",i,"L",_o(ws,o),",",_o(ws,a)];for(n.push(n[e-1]);++t<=e;)r=n[t],o.shift(),o.push(r[0]),a.shift(),a.push(r[1]),bo(c,o,a);return n.pop(),c.push("L",r),c.join("")}function yo(n){if(n.length<4)return oo(n);for(var t,e=[],r=-1,u=n.length,i=[0],o=[0];++r<3;)t=n[r],i.push(t[0]),o.push(t[1]);for(e.push(_o(ws,i)+","+_o(ws,o)),--r;++r<u;)t=n[r],i.shift(),i.push(t[0]),o.shift(),o.push(t[1]),bo(e,i,o);return e.join("")}function xo(n){for(var t,e,r=-1,u=n.length,i=u+4,o=[],a=[];++r<4;)e=n[r%u],o.push(e[0]),a.push(e[1]);for(t=[_o(ws,o),",",_o(ws,a)],--r;++r<i;)e=n[r%u],o.shift(),o.push(e[0]),a.shift(),a.push(e[1]),bo(t,o,a);return t.join("")}function Mo(n,t){var e=n.length-1;if(e)for(var r,u,i=n[0][0],o=n[0][1],a=n[e][0]-i,c=n[e][1]-o,s=-1;++s<=e;)r=n[s],u=s/e,r[0]=t*r[0]+(1-t)*(i+u*a),r[1]=t*r[1]+(1-t)*(o+u*c);return mo(n)}function _o(n,t){return n[0]*t[0]+n[1]*t[1]+n[2]*t[2]+n[3]*t[3]}function bo(n,t,e){n.push("C",_o(_s,t),",",_o(_s,e),",",_o(bs,t),",",_o(bs,e),",",_o(ws,t),",",_o(ws,e))}function wo(n,t){return(t[1]-n[1])/(t[0]-n[0])}function So(n){for(var t=0,e=n.length-1,r=[],u=n[0],i=n[1],o=r[0]=wo(u,i);++t<e;)r[t]=(o+(o=wo(u=i,i=n[t+1])))/2;return r[t]=o,r}function ko(n){for(var t,e,r,u,i=[],o=So(n),a=-1,c=n.length-1;++a<c;)t=wo(n[a],n[a+1]),oa(t)<Aa?o[a]=o[a+1]=0:(e=o[a]/t,r=o[a+1]/t,u=e*e+r*r,u>9&&(u=3*t/Math.sqrt(u),o[a]=u*e,o[a+1]=u*r));for(a=-1;++a<=c;)u=(n[Math.min(c,a+1)][0]-n[Math.max(0,a-1)][0])/(6*(1+o[a]*o[a])),i.push([u||0,o[a]*u||0]);return i}function Eo(n){return n.length<3?oo(n):n[0]+po(n,ko(n))}function Ao(n){for(var t,e,r,u=-1,i=n.length;++u<i;)t=n[u],e=t[0],r=t[1]+ys,t[0]=e*Math.cos(r),t[1]=e*Math.sin(r);return n}function Co(n){function t(t){function c(){v.push("M",a(n(m),f),l,s(n(d.reverse()),f),"Z")}for(var h,g,p,v=[],d=[],m=[],y=-1,x=t.length,M=_t(e),_=_t(u),b=e===r?function(){return g}:_t(r),w=u===i?function(){return p}:_t(i);++y<x;)o.call(this,h=t[y],y)?(d.push([g=+M.call(this,h,y),p=+_.call(this,h,y)]),m.push([+b.call(this,h,y),+w.call(this,h,y)])):d.length&&(c(),d=[],m=[]);return d.length&&c(),v.length?v.join(""):null}var e=br,r=br,u=0,i=wr,o=be,a=oo,c=a.key,s=a,l="L",f=.7;return t.x=function(n){return arguments.length?(e=r=n,t):r},t.x0=function(n){return arguments.length?(e=n,t):e},t.x1=function(n){return arguments.length?(r=n,t):r},t.y=function(n){return arguments.length?(u=i=n,t):i},t.y0=function(n){return arguments.length?(u=n,t):u},t.y1=function(n){return arguments.length?(i=n,t):i},t.defined=function(n){return arguments.length?(o=n,t):o},t.interpolate=function(n){return arguments.length?(c="function"==typeof n?a=n:(a=Ms.get(n)||oo).key,s=a.reverse||a,l=a.closed?"M":"L",t):c},t.tension=function(n){return arguments.length?(f=n,t):f},t}function No(n){return n.radius}function Lo(n){return[n.x,n.y]}function To(n){return function(){var t=n.apply(this,arguments),e=t[0],r=t[1]+ys;return[e*Math.cos(r),e*Math.sin(r)]}}function qo(){return 64}function zo(){return"circle"}function Ro(n){var t=Math.sqrt(n/Sa);return"M0,"+t+"A"+t+","+t+" 0 1,1 0,"+-t+"A"+t+","+t+" 0 1,1 0,"+t+"Z"}function Do(n,t){return fa(n,Ns),n.id=t,n}function Po(n,t,e,r){var u=n.id;return R(n,"function"==typeof e?function(n,i,o){n.__transition__[u].tween.set(t,r(e.call(n,n.__data__,i,o)))}:(e=r(e),function(n){n.__transition__[u].tween.set(t,e)}))}function Uo(n){return null==n&&(n=""),function(){this.textContent=n}}function jo(n,t,e,r){var i=n.__transition__||(n.__transition__={active:0,count:0}),o=i[e];if(!o){var a=r.time;o=i[e]={tween:new u,time:a,ease:r.ease,delay:r.delay,duration:r.duration},++i.count,Xo.timer(function(r){function u(r){return i.active>e?s():(i.active=e,o.event&&o.event.start.call(n,l,t),o.tween.forEach(function(e,r){(r=r.call(n,l,t))&&v.push(r)}),Xo.timer(function(){return p.c=c(r||1)?be:c,1},0,a),void 0)}function c(r){if(i.active!==e)return s();for(var u=r/g,a=f(u),c=v.length;c>0;)v[--c].call(n,a);return u>=1?(o.event&&o.event.end.call(n,l,t),s()):void 0}function s(){return--i.count?delete i[e]:delete n.__transition__,1}var l=n.__data__,f=o.ease,h=o.delay,g=o.duration,p=Ja,v=[];return p.t=h+a,r>=h?u(r-h):(p.c=u,void 0)},0,a)}}function Ho(n,t){n.attr("transform",function(n){return"translate("+t(n)+",0)"})}function Fo(n,t){n.attr("transform",function(n){return"translate(0,"+t(n)+")"})}function Oo(n){return n.toISOString()}function Yo(n,t,e){function r(t){return n(t)}function u(n,e){var r=n[1]-n[0],u=r/e,i=Xo.bisect(js,u);return i==js.length?[t.year,Yi(n.map(function(n){return n/31536e6}),e)[2]]:i?t[u/js[i-1]<js[i]/u?i-1:i]:[Os,Yi(n,e)[2]]}return r.invert=function(t){return Io(n.invert(t))},r.domain=function(t){return arguments.length?(n.domain(t),r):n.domain().map(Io)},r.nice=function(n,t){function e(e){return!isNaN(e)&&!n.range(e,Io(+e+1),t).length}var i=r.domain(),o=zi(i),a=null==n?u(o,10):"number"==typeof n&&u(o,n);return a&&(n=a[0],t=a[1]),r.domain(Pi(i,t>1?{floor:function(t){for(;e(t=n.floor(t));)t=Io(t-1);return t},ceil:function(t){for(;e(t=n.ceil(t));)t=Io(+t+1);return t}}:n))},r.ticks=function(n,t){var e=zi(r.domain()),i=null==n?u(e,10):"number"==typeof n?u(e,n):!n.range&&[{range:n},t];return i&&(n=i[0],t=i[1]),n.range(e[0],Io(+e[1]+1),1>t?1:t)},r.tickFormat=function(){return e},r.copy=function(){return Yo(n.copy(),t,e)},Fi(r,n)}function Io(n){return new Date(n)}function Zo(n){return JSON.parse(n.responseText)}function Vo(n){var t=Wo.createRange();return t.selectNode(Wo.body),t.createContextualFragment(n.responseText)}var Xo={version:"3.4.3"};Date.now||(Date.now=function(){return+new Date});var $o=[].slice,Bo=function(n){return $o.call(n)},Wo=document,Jo=Wo.documentElement,Go=window;try{Bo(Jo.childNodes)[0].nodeType}catch(Ko){Bo=function(n){for(var t=n.length,e=new Array(t);t--;)e[t]=n[t];return e}}try{Wo.createElement("div").style.setProperty("opacity",0,"")}catch(Qo){var na=Go.Element.prototype,ta=na.setAttribute,ea=na.setAttributeNS,ra=Go.CSSStyleDeclaration.prototype,ua=ra.setProperty;na.setAttribute=function(n,t){ta.call(this,n,t+"")},na.setAttributeNS=function(n,t,e){ea.call(this,n,t,e+"")},ra.setProperty=function(n,t,e){ua.call(this,n,t+"",e)}}Xo.ascending=function(n,t){return t>n?-1:n>t?1:n>=t?0:0/0},Xo.descending=function(n,t){return n>t?-1:t>n?1:t>=n?0:0/0},Xo.min=function(n,t){var e,r,u=-1,i=n.length;if(1===arguments.length){for(;++u<i&&!(null!=(e=n[u])&&e>=e);)e=void 0;for(;++u<i;)null!=(r=n[u])&&e>r&&(e=r)}else{for(;++u<i&&!(null!=(e=t.call(n,n[u],u))&&e>=e);)e=void 0;for(;++u<i;)null!=(r=t.call(n,n[u],u))&&e>r&&(e=r)}return e},Xo.max=function(n,t){var e,r,u=-1,i=n.length;if(1===arguments.length){for(;++u<i&&!(null!=(e=n[u])&&e>=e);)e=void 0;for(;++u<i;)null!=(r=n[u])&&r>e&&(e=r)}else{for(;++u<i&&!(null!=(e=t.call(n,n[u],u))&&e>=e);)e=void 0;for(;++u<i;)null!=(r=t.call(n,n[u],u))&&r>e&&(e=r)}return e},Xo.extent=function(n,t){var e,r,u,i=-1,o=n.length;if(1===arguments.length){for(;++i<o&&!(null!=(e=u=n[i])&&e>=e);)e=u=void 0;for(;++i<o;)null!=(r=n[i])&&(e>r&&(e=r),r>u&&(u=r))}else{for(;++i<o&&!(null!=(e=u=t.call(n,n[i],i))&&e>=e);)e=void 0;for(;++i<o;)null!=(r=t.call(n,n[i],i))&&(e>r&&(e=r),r>u&&(u=r))}return[e,u]},Xo.sum=function(n,t){var e,r=0,u=n.length,i=-1;if(1===arguments.length)for(;++i<u;)isNaN(e=+n[i])||(r+=e);else for(;++i<u;)isNaN(e=+t.call(n,n[i],i))||(r+=e);return r},Xo.mean=function(t,e){var r,u=t.length,i=0,o=-1,a=0;if(1===arguments.length)for(;++o<u;)n(r=t[o])&&(i+=(r-i)/++a);else for(;++o<u;)n(r=e.call(t,t[o],o))&&(i+=(r-i)/++a);return a?i:void 0},Xo.quantile=function(n,t){var e=(n.length-1)*t+1,r=Math.floor(e),u=+n[r-1],i=e-r;return i?u+i*(n[r]-u):u},Xo.median=function(t,e){return arguments.length>1&&(t=t.map(e)),t=t.filter(n),t.length?Xo.quantile(t.sort(Xo.ascending),.5):void 0},Xo.bisector=function(n){return{left:function(t,e,r,u){for(arguments.length<3&&(r=0),arguments.length<4&&(u=t.length);u>r;){var i=r+u>>>1;n.call(t,t[i],i)<e?r=i+1:u=i}return r},right:function(t,e,r,u){for(arguments.length<3&&(r=0),arguments.length<4&&(u=t.length);u>r;){var i=r+u>>>1;e<n.call(t,t[i],i)?u=i:r=i+1}return r}}};var ia=Xo.bisector(function(n){return n});Xo.bisectLeft=ia.left,Xo.bisect=Xo.bisectRight=ia.right,Xo.shuffle=function(n){for(var t,e,r=n.length;r;)e=0|Math.random()*r--,t=n[r],n[r]=n[e],n[e]=t;return n},Xo.permute=function(n,t){for(var e=t.length,r=new Array(e);e--;)r[e]=n[t[e]];return r},Xo.pairs=function(n){for(var t,e=0,r=n.length-1,u=n[0],i=new Array(0>r?0:r);r>e;)i[e]=[t=u,u=n[++e]];return i},Xo.zip=function(){if(!(u=arguments.length))return[];for(var n=-1,e=Xo.min(arguments,t),r=new Array(e);++n<e;)for(var u,i=-1,o=r[n]=new Array(u);++i<u;)o[i]=arguments[i][n];return r},Xo.transpose=function(n){return Xo.zip.apply(Xo,n)},Xo.keys=function(n){var t=[];for(var e in n)t.push(e);return t},Xo.values=function(n){var t=[];for(var e in n)t.push(n[e]);return t},Xo.entries=function(n){var t=[];for(var e in n)t.push({key:e,value:n[e]});return t},Xo.merge=function(n){for(var t,e,r,u=n.length,i=-1,o=0;++i<u;)o+=n[i].length;for(e=new Array(o);--u>=0;)for(r=n[u],t=r.length;--t>=0;)e[--o]=r[t];return e};var oa=Math.abs;Xo.range=function(n,t,r){if(arguments.length<3&&(r=1,arguments.length<2&&(t=n,n=0)),1/0===(t-n)/r)throw new Error("infinite range");var u,i=[],o=e(oa(r)),a=-1;if(n*=o,t*=o,r*=o,0>r)for(;(u=n+r*++a)>t;)i.push(u/o);else for(;(u=n+r*++a)<t;)i.push(u/o);return i},Xo.map=function(n){var t=new u;if(n instanceof u)n.forEach(function(n,e){t.set(n,e)});else for(var e in n)t.set(e,n[e]);return t},r(u,{has:i,get:function(n){return this[aa+n]},set:function(n,t){return this[aa+n]=t},remove:o,keys:a,values:function(){var n=[];return this.forEach(function(t,e){n.push(e)}),n},entries:function(){var n=[];return this.forEach(function(t,e){n.push({key:t,value:e})}),n},size:c,empty:s,forEach:function(n){for(var t in this)t.charCodeAt(0)===ca&&n.call(this,t.substring(1),this[t])}});var aa="\x00",ca=aa.charCodeAt(0);Xo.nest=function(){function n(t,a,c){if(c>=o.length)return r?r.call(i,a):e?a.sort(e):a;for(var s,l,f,h,g=-1,p=a.length,v=o[c++],d=new u;++g<p;)(h=d.get(s=v(l=a[g])))?h.push(l):d.set(s,[l]);return t?(l=t(),f=function(e,r){l.set(e,n(t,r,c))}):(l={},f=function(e,r){l[e]=n(t,r,c)}),d.forEach(f),l}function t(n,e){if(e>=o.length)return n;var r=[],u=a[e++];return n.forEach(function(n,u){r.push({key:n,values:t(u,e)})}),u?r.sort(function(n,t){return u(n.key,t.key)}):r}var e,r,i={},o=[],a=[];return i.map=function(t,e){return n(e,t,0)},i.entries=function(e){return t(n(Xo.map,e,0),0)},i.key=function(n){return o.push(n),i},i.sortKeys=function(n){return a[o.length-1]=n,i},i.sortValues=function(n){return e=n,i},i.rollup=function(n){return r=n,i},i},Xo.set=function(n){var t=new l;if(n)for(var e=0,r=n.length;r>e;++e)t.add(n[e]);return t},r(l,{has:i,add:function(n){return this[aa+n]=!0,n},remove:function(n){return n=aa+n,n in this&&delete this[n]},values:a,size:c,empty:s,forEach:function(n){for(var t in this)t.charCodeAt(0)===ca&&n.call(this,t.substring(1))}}),Xo.behavior={},Xo.rebind=function(n,t){for(var e,r=1,u=arguments.length;++r<u;)n[e=arguments[r]]=f(n,t,t[e]);return n};var sa=["webkit","ms","moz","Moz","o","O"];Xo.dispatch=function(){for(var n=new p,t=-1,e=arguments.length;++t<e;)n[arguments[t]]=v(n);return n},p.prototype.on=function(n,t){var e=n.indexOf("."),r="";if(e>=0&&(r=n.substring(e+1),n=n.substring(0,e)),n)return arguments.length<2?this[n].on(r):this[n].on(r,t);if(2===arguments.length){if(null==t)for(n in this)this.hasOwnProperty(n)&&this[n].on(r,null);return this}},Xo.event=null,Xo.requote=function(n){return n.replace(la,"\\$&")};var la=/[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g,fa={}.__proto__?function(n,t){n.__proto__=t}:function(n,t){for(var e in t)n[e]=t[e]},ha=function(n,t){return t.querySelector(n)},ga=function(n,t){return t.querySelectorAll(n)},pa=Jo[h(Jo,"matchesSelector")],va=function(n,t){return pa.call(n,t)};"function"==typeof Sizzle&&(ha=function(n,t){return Sizzle(n,t)[0]||null},ga=Sizzle,va=Sizzle.matchesSelector),Xo.selection=function(){return xa};var da=Xo.selection.prototype=[];da.select=function(n){var t,e,r,u,i=[];n=M(n);for(var o=-1,a=this.length;++o<a;){i.push(t=[]),t.parentNode=(r=this[o]).parentNode;for(var c=-1,s=r.length;++c<s;)(u=r[c])?(t.push(e=n.call(u,u.__data__,c,o)),e&&"__data__"in u&&(e.__data__=u.__data__)):t.push(null)}return x(i)},da.selectAll=function(n){var t,e,r=[];n=_(n);for(var u=-1,i=this.length;++u<i;)for(var o=this[u],a=-1,c=o.length;++a<c;)(e=o[a])&&(r.push(t=Bo(n.call(e,e.__data__,a,u))),t.parentNode=e);return x(r)};var ma={svg:"http://www.w3.org/2000/svg",xhtml:"http://www.w3.org/1999/xhtml",xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"};Xo.ns={prefix:ma,qualify:function(n){var t=n.indexOf(":"),e=n;return t>=0&&(e=n.substring(0,t),n=n.substring(t+1)),ma.hasOwnProperty(e)?{space:ma[e],local:n}:n}},da.attr=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node();return n=Xo.ns.qualify(n),n.local?e.getAttributeNS(n.space,n.local):e.getAttribute(n)}for(t in n)this.each(b(t,n[t]));return this}return this.each(b(n,t))},da.classed=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node(),r=(n=k(n)).length,u=-1;if(t=e.classList){for(;++u<r;)if(!t.contains(n[u]))return!1}else for(t=e.getAttribute("class");++u<r;)if(!S(n[u]).test(t))return!1;return!0}for(t in n)this.each(E(t,n[t]));return this}return this.each(E(n,t))},da.style=function(n,t,e){var r=arguments.length;if(3>r){if("string"!=typeof n){2>r&&(t="");for(e in n)this.each(C(e,n[e],t));return this}if(2>r)return Go.getComputedStyle(this.node(),null).getPropertyValue(n);e=""}return this.each(C(n,t,e))},da.property=function(n,t){if(arguments.length<2){if("string"==typeof n)return this.node()[n];for(t in n)this.each(N(t,n[t]));return this}return this.each(N(n,t))},da.text=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.textContent=null==t?"":t}:null==n?function(){this.textContent=""}:function(){this.textContent=n}):this.node().textContent},da.html=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.innerHTML=null==t?"":t}:null==n?function(){this.innerHTML=""}:function(){this.innerHTML=n}):this.node().innerHTML},da.append=function(n){return n=L(n),this.select(function(){return this.appendChild(n.apply(this,arguments))})},da.insert=function(n,t){return n=L(n),t=M(t),this.select(function(){return this.insertBefore(n.apply(this,arguments),t.apply(this,arguments)||null)})},da.remove=function(){return this.each(function(){var n=this.parentNode;n&&n.removeChild(this)})},da.data=function(n,t){function e(n,e){var r,i,o,a=n.length,f=e.length,h=Math.min(a,f),g=new Array(f),p=new Array(f),v=new Array(a);if(t){var d,m=new u,y=new u,x=[];for(r=-1;++r<a;)d=t.call(i=n[r],i.__data__,r),m.has(d)?v[r]=i:m.set(d,i),x.push(d);for(r=-1;++r<f;)d=t.call(e,o=e[r],r),(i=m.get(d))?(g[r]=i,i.__data__=o):y.has(d)||(p[r]=T(o)),y.set(d,o),m.remove(d);for(r=-1;++r<a;)m.has(x[r])&&(v[r]=n[r])}else{for(r=-1;++r<h;)i=n[r],o=e[r],i?(i.__data__=o,g[r]=i):p[r]=T(o);for(;f>r;++r)p[r]=T(e[r]);for(;a>r;++r)v[r]=n[r]}p.update=g,p.parentNode=g.parentNode=v.parentNode=n.parentNode,c.push(p),s.push(g),l.push(v)}var r,i,o=-1,a=this.length;if(!arguments.length){for(n=new Array(a=(r=this[0]).length);++o<a;)(i=r[o])&&(n[o]=i.__data__);return n}var c=D([]),s=x([]),l=x([]);if("function"==typeof n)for(;++o<a;)e(r=this[o],n.call(r,r.parentNode.__data__,o));else for(;++o<a;)e(r=this[o],n);return s.enter=function(){return c},s.exit=function(){return l},s},da.datum=function(n){return arguments.length?this.property("__data__",n):this.property("__data__")},da.filter=function(n){var t,e,r,u=[];"function"!=typeof n&&(n=q(n));for(var i=0,o=this.length;o>i;i++){u.push(t=[]),t.parentNode=(e=this[i]).parentNode;for(var a=0,c=e.length;c>a;a++)(r=e[a])&&n.call(r,r.__data__,a,i)&&t.push(r)}return x(u)},da.order=function(){for(var n=-1,t=this.length;++n<t;)for(var e,r=this[n],u=r.length-1,i=r[u];--u>=0;)(e=r[u])&&(i&&i!==e.nextSibling&&i.parentNode.insertBefore(e,i),i=e);return this},da.sort=function(n){n=z.apply(this,arguments);for(var t=-1,e=this.length;++t<e;)this[t].sort(n);return this.order()},da.each=function(n){return R(this,function(t,e,r){n.call(t,t.__data__,e,r)})},da.call=function(n){var t=Bo(arguments);return n.apply(t[0]=this,t),this},da.empty=function(){return!this.node()},da.node=function(){for(var n=0,t=this.length;t>n;n++)for(var e=this[n],r=0,u=e.length;u>r;r++){var i=e[r];if(i)return i}return null},da.size=function(){var n=0;return this.each(function(){++n}),n};var ya=[];Xo.selection.enter=D,Xo.selection.enter.prototype=ya,ya.append=da.append,ya.empty=da.empty,ya.node=da.node,ya.call=da.call,ya.size=da.size,ya.select=function(n){for(var t,e,r,u,i,o=[],a=-1,c=this.length;++a<c;){r=(u=this[a]).update,o.push(t=[]),t.parentNode=u.parentNode;for(var s=-1,l=u.length;++s<l;)(i=u[s])?(t.push(r[s]=e=n.call(u.parentNode,i.__data__,s,a)),e.__data__=i.__data__):t.push(null)}return x(o)},ya.insert=function(n,t){return arguments.length<2&&(t=P(this)),da.insert.call(this,n,t)},da.transition=function(){for(var n,t,e=ks||++Ls,r=[],u=Es||{time:Date.now(),ease:yu,delay:0,duration:250},i=-1,o=this.length;++i<o;){r.push(n=[]);for(var a=this[i],c=-1,s=a.length;++c<s;)(t=a[c])&&jo(t,c,e,u),n.push(t)}return Do(r,e)},da.interrupt=function(){return this.each(U)},Xo.select=function(n){var t=["string"==typeof n?ha(n,Wo):n];return t.parentNode=Jo,x([t])},Xo.selectAll=function(n){var t=Bo("string"==typeof n?ga(n,Wo):n);return t.parentNode=Jo,x([t])};var xa=Xo.select(Jo);da.on=function(n,t,e){var r=arguments.length;if(3>r){if("string"!=typeof n){2>r&&(t=!1);for(e in n)this.each(j(e,n[e],t));return this}if(2>r)return(r=this.node()["__on"+n])&&r._;e=!1}return this.each(j(n,t,e))};var Ma=Xo.map({mouseenter:"mouseover",mouseleave:"mouseout"});Ma.forEach(function(n){"on"+n in Wo&&Ma.remove(n)});var _a="onselectstart"in Wo?null:h(Jo.style,"userSelect"),ba=0;Xo.mouse=function(n){return Y(n,m())};var wa=/WebKit/.test(Go.navigator.userAgent)?-1:0;Xo.touches=function(n,t){return arguments.length<2&&(t=m().touches),t?Bo(t).map(function(t){var e=Y(n,t);return e.identifier=t.identifier,e}):[]},Xo.behavior.drag=function(){function n(){this.on("mousedown.drag",o).on("touchstart.drag",a)}function t(){return Xo.event.changedTouches[0].identifier}function e(n,t){return Xo.touches(n).filter(function(n){return n.identifier===t})[0]}function r(n,t,e,r){return function(){function o(){var n=t(l,g),e=n[0]-v[0],r=n[1]-v[1];d|=e|r,v=n,f({type:"drag",x:n[0]+c[0],y:n[1]+c[1],dx:e,dy:r})}function a(){m.on(e+"."+p,null).on(r+"."+p,null),y(d&&Xo.event.target===h),f({type:"dragend"})}var c,s=this,l=s.parentNode,f=u.of(s,arguments),h=Xo.event.target,g=n(),p=null==g?"drag":"drag-"+g,v=t(l,g),d=0,m=Xo.select(Go).on(e+"."+p,o).on(r+"."+p,a),y=O();i?(c=i.apply(s,arguments),c=[c.x-v[0],c.y-v[1]]):c=[0,0],f({type:"dragstart"})}}var u=y(n,"drag","dragstart","dragend"),i=null,o=r(g,Xo.mouse,"mousemove","mouseup"),a=r(t,e,"touchmove","touchend");return n.origin=function(t){return arguments.length?(i=t,n):i},Xo.rebind(n,u,"on")};var Sa=Math.PI,ka=2*Sa,Ea=Sa/2,Aa=1e-6,Ca=Aa*Aa,Na=Sa/180,La=180/Sa,Ta=Math.SQRT2,qa=2,za=4;Xo.interpolateZoom=function(n,t){function e(n){var t=n*y;if(m){var e=B(v),o=i/(qa*h)*(e*W(Ta*t+v)-$(v));return[r+o*s,u+o*l,i*e/B(Ta*t+v)]}return[r+n*s,u+n*l,i*Math.exp(Ta*t)]}var r=n[0],u=n[1],i=n[2],o=t[0],a=t[1],c=t[2],s=o-r,l=a-u,f=s*s+l*l,h=Math.sqrt(f),g=(c*c-i*i+za*f)/(2*i*qa*h),p=(c*c-i*i-za*f)/(2*c*qa*h),v=Math.log(Math.sqrt(g*g+1)-g),d=Math.log(Math.sqrt(p*p+1)-p),m=d-v,y=(m||Math.log(c/i))/Ta;return e.duration=1e3*y,e},Xo.behavior.zoom=function(){function n(n){n.on(A,s).on(Pa+".zoom",f).on(C,h).on("dblclick.zoom",g).on(L,l)}function t(n){return[(n[0]-S.x)/S.k,(n[1]-S.y)/S.k]}function e(n){return[n[0]*S.k+S.x,n[1]*S.k+S.y]}function r(n){S.k=Math.max(E[0],Math.min(E[1],n))}function u(n,t){t=e(t),S.x+=n[0]-t[0],S.y+=n[1]-t[1]}function i(){_&&_.domain(M.range().map(function(n){return(n-S.x)/S.k}).map(M.invert)),w&&w.domain(b.range().map(function(n){return(n-S.y)/S.k}).map(b.invert))}function o(n){n({type:"zoomstart"})}function a(n){i(),n({type:"zoom",scale:S.k,translate:[S.x,S.y]})}function c(n){n({type:"zoomend"})}function s(){function n(){l=1,u(Xo.mouse(r),g),a(i)}function e(){f.on(C,Go===r?h:null).on(N,null),p(l&&Xo.event.target===s),c(i)}var r=this,i=T.of(r,arguments),s=Xo.event.target,l=0,f=Xo.select(Go).on(C,n).on(N,e),g=t(Xo.mouse(r)),p=O();U.call(r),o(i)}function l(){function n(){var n=Xo.touches(g);return h=S.k,n.forEach(function(n){n.identifier in v&&(v[n.identifier]=t(n))}),n}function e(){for(var t=Xo.event.changedTouches,e=0,i=t.length;i>e;++e)v[t[e].identifier]=null;var o=n(),c=Date.now();if(1===o.length){if(500>c-x){var s=o[0],l=v[s.identifier];r(2*S.k),u(s,l),d(),a(p)}x=c}else if(o.length>1){var s=o[0],f=o[1],h=s[0]-f[0],g=s[1]-f[1];m=h*h+g*g}}function i(){for(var n,t,e,i,o=Xo.touches(g),c=0,s=o.length;s>c;++c,i=null)if(e=o[c],i=v[e.identifier]){if(t)break;n=e,t=i}if(i){var l=(l=e[0]-n[0])*l+(l=e[1]-n[1])*l,f=m&&Math.sqrt(l/m);n=[(n[0]+e[0])/2,(n[1]+e[1])/2],t=[(t[0]+i[0])/2,(t[1]+i[1])/2],r(f*h)}x=null,u(n,t),a(p)}function f(){if(Xo.event.touches.length){for(var t=Xo.event.changedTouches,e=0,r=t.length;r>e;++e)delete v[t[e].identifier];for(var u in v)return void n()}b.on(M,null).on(_,null),w.on(A,s).on(L,l),k(),c(p)}var h,g=this,p=T.of(g,arguments),v={},m=0,y=Xo.event.changedTouches[0].identifier,M="touchmove.zoom-"+y,_="touchend.zoom-"+y,b=Xo.select(Go).on(M,i).on(_,f),w=Xo.select(g).on(A,null).on(L,e),k=O();U.call(g),e(),o(p)}function f(){var n=T.of(this,arguments);m?clearTimeout(m):(U.call(this),o(n)),m=setTimeout(function(){m=null,c(n)},50),d();var e=v||Xo.mouse(this);p||(p=t(e)),r(Math.pow(2,.002*Ra())*S.k),u(e,p),a(n)}function h(){p=null}function g(){var n=T.of(this,arguments),e=Xo.mouse(this),i=t(e),s=Math.log(S.k)/Math.LN2;o(n),r(Math.pow(2,Xo.event.shiftKey?Math.ceil(s)-1:Math.floor(s)+1)),u(e,i),a(n),c(n)}var p,v,m,x,M,_,b,w,S={x:0,y:0,k:1},k=[960,500],E=Da,A="mousedown.zoom",C="mousemove.zoom",N="mouseup.zoom",L="touchstart.zoom",T=y(n,"zoomstart","zoom","zoomend");return n.event=function(n){n.each(function(){var n=T.of(this,arguments),t=S;ks?Xo.select(this).transition().each("start.zoom",function(){S=this.__chart__||{x:0,y:0,k:1},o(n)}).tween("zoom:zoom",function(){var e=k[0],r=k[1],u=e/2,i=r/2,o=Xo.interpolateZoom([(u-S.x)/S.k,(i-S.y)/S.k,e/S.k],[(u-t.x)/t.k,(i-t.y)/t.k,e/t.k]);return function(t){var r=o(t),c=e/r[2];this.__chart__=S={x:u-r[0]*c,y:i-r[1]*c,k:c},a(n)}}).each("end.zoom",function(){c(n)}):(this.__chart__=S,o(n),a(n),c(n))})},n.translate=function(t){return arguments.length?(S={x:+t[0],y:+t[1],k:S.k},i(),n):[S.x,S.y]},n.scale=function(t){return arguments.length?(S={x:S.x,y:S.y,k:+t},i(),n):S.k},n.scaleExtent=function(t){return arguments.length?(E=null==t?Da:[+t[0],+t[1]],n):E},n.center=function(t){return arguments.length?(v=t&&[+t[0],+t[1]],n):v},n.size=function(t){return arguments.length?(k=t&&[+t[0],+t[1]],n):k},n.x=function(t){return arguments.length?(_=t,M=t.copy(),S={x:0,y:0,k:1},n):_},n.y=function(t){return arguments.length?(w=t,b=t.copy(),S={x:0,y:0,k:1},n):w},Xo.rebind(n,T,"on")};var Ra,Da=[0,1/0],Pa="onwheel"in Wo?(Ra=function(){return-Xo.event.deltaY*(Xo.event.deltaMode?120:1)},"wheel"):"onmousewheel"in Wo?(Ra=function(){return Xo.event.wheelDelta},"mousewheel"):(Ra=function(){return-Xo.event.detail},"MozMousePixelScroll");G.prototype.toString=function(){return this.rgb()+""},Xo.hsl=function(n,t,e){return 1===arguments.length?n instanceof Q?K(n.h,n.s,n.l):dt(""+n,mt,K):K(+n,+t,+e)};var Ua=Q.prototype=new G;Ua.brighter=function(n){return n=Math.pow(.7,arguments.length?n:1),K(this.h,this.s,this.l/n)},Ua.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),K(this.h,this.s,n*this.l)},Ua.rgb=function(){return nt(this.h,this.s,this.l)},Xo.hcl=function(n,t,e){return 1===arguments.length?n instanceof et?tt(n.h,n.c,n.l):n instanceof it?at(n.l,n.a,n.b):at((n=yt((n=Xo.rgb(n)).r,n.g,n.b)).l,n.a,n.b):tt(+n,+t,+e)};var ja=et.prototype=new G;ja.brighter=function(n){return tt(this.h,this.c,Math.min(100,this.l+Ha*(arguments.length?n:1)))},ja.darker=function(n){return tt(this.h,this.c,Math.max(0,this.l-Ha*(arguments.length?n:1)))},ja.rgb=function(){return rt(this.h,this.c,this.l).rgb()},Xo.lab=function(n,t,e){return 1===arguments.length?n instanceof it?ut(n.l,n.a,n.b):n instanceof et?rt(n.l,n.c,n.h):yt((n=Xo.rgb(n)).r,n.g,n.b):ut(+n,+t,+e)};var Ha=18,Fa=.95047,Oa=1,Ya=1.08883,Ia=it.prototype=new G;Ia.brighter=function(n){return ut(Math.min(100,this.l+Ha*(arguments.length?n:1)),this.a,this.b)},Ia.darker=function(n){return ut(Math.max(0,this.l-Ha*(arguments.length?n:1)),this.a,this.b)},Ia.rgb=function(){return ot(this.l,this.a,this.b)},Xo.rgb=function(n,t,e){return 1===arguments.length?n instanceof pt?gt(n.r,n.g,n.b):dt(""+n,gt,nt):gt(~~n,~~t,~~e)};var Za=pt.prototype=new G;Za.brighter=function(n){n=Math.pow(.7,arguments.length?n:1);var t=this.r,e=this.g,r=this.b,u=30;return t||e||r?(t&&u>t&&(t=u),e&&u>e&&(e=u),r&&u>r&&(r=u),gt(Math.min(255,~~(t/n)),Math.min(255,~~(e/n)),Math.min(255,~~(r/n)))):gt(u,u,u)},Za.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),gt(~~(n*this.r),~~(n*this.g),~~(n*this.b))},Za.hsl=function(){return mt(this.r,this.g,this.b)},Za.toString=function(){return"#"+vt(this.r)+vt(this.g)+vt(this.b)};var Va=Xo.map({aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074});Va.forEach(function(n,t){Va.set(n,ft(t))}),Xo.functor=_t,Xo.xhr=wt(bt),Xo.dsv=function(n,t){function e(n,e,i){arguments.length<3&&(i=e,e=null);var o=St(n,t,null==e?r:u(e),i);return o.row=function(n){return arguments.length?o.response(null==(e=n)?r:u(n)):e},o}function r(n){return e.parse(n.responseText)}function u(n){return function(t){return e.parse(t.responseText,n)}}function i(t){return t.map(o).join(n)}function o(n){return a.test(n)?'"'+n.replace(/\"/g,'""')+'"':n}var a=new RegExp('["'+n+"\n]"),c=n.charCodeAt(0);return e.parse=function(n,t){var r;return e.parseRows(n,function(n,e){if(r)return r(n,e-1);var u=new Function("d","return {"+n.map(function(n,t){return JSON.stringify(n)+": d["+t+"]"}).join(",")+"}");r=t?function(n,e){return t(u(n),e)}:u})},e.parseRows=function(n,t){function e(){if(l>=s)return o;if(u)return u=!1,i;var t=l;if(34===n.charCodeAt(t)){for(var e=t;e++<s;)if(34===n.charCodeAt(e)){if(34!==n.charCodeAt(e+1))break;++e}l=e+2;var r=n.charCodeAt(e+1);return 13===r?(u=!0,10===n.charCodeAt(e+2)&&++l):10===r&&(u=!0),n.substring(t+1,e).replace(/""/g,'"')}for(;s>l;){var r=n.charCodeAt(l++),a=1;if(10===r)u=!0;else if(13===r)u=!0,10===n.charCodeAt(l)&&(++l,++a);else if(r!==c)continue;return n.substring(t,l-a)}return n.substring(t)}for(var r,u,i={},o={},a=[],s=n.length,l=0,f=0;(r=e())!==o;){for(var h=[];r!==i&&r!==o;)h.push(r),r=e();(!t||(h=t(h,f++)))&&a.push(h)}return a},e.format=function(t){if(Array.isArray(t[0]))return e.formatRows(t);var r=new l,u=[];return t.forEach(function(n){for(var t in n)r.has(t)||u.push(r.add(t))}),[u.map(o).join(n)].concat(t.map(function(t){return u.map(function(n){return o(t[n])}).join(n)})).join("\n")},e.formatRows=function(n){return n.map(i).join("\n")},e},Xo.csv=Xo.dsv(",","text/csv"),Xo.tsv=Xo.dsv("	","text/tab-separated-values");var Xa,$a,Ba,Wa,Ja,Ga=Go[h(Go,"requestAnimationFrame")]||function(n){setTimeout(n,17)};Xo.timer=function(n,t,e){var r=arguments.length;2>r&&(t=0),3>r&&(e=Date.now());var u=e+t,i={c:n,t:u,f:!1,n:null};$a?$a.n=i:Xa=i,$a=i,Ba||(Wa=clearTimeout(Wa),Ba=1,Ga(Et))},Xo.timer.flush=function(){At(),Ct()},Xo.round=function(n,t){return t?Math.round(n*(t=Math.pow(10,t)))/t:Math.round(n)};var Ka=["y","z","a","f","p","n","\xb5","m","","k","M","G","T","P","E","Z","Y"].map(Lt);Xo.formatPrefix=function(n,t){var e=0;return n&&(0>n&&(n*=-1),t&&(n=Xo.round(n,Nt(n,t))),e=1+Math.floor(1e-12+Math.log(n)/Math.LN10),e=Math.max(-24,Math.min(24,3*Math.floor((0>=e?e+1:e-1)/3)))),Ka[8+e/3]};var Qa=/(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i,nc=Xo.map({b:function(n){return n.toString(2)},c:function(n){return String.fromCharCode(n)},o:function(n){return n.toString(8)},x:function(n){return n.toString(16)},X:function(n){return n.toString(16).toUpperCase()},g:function(n,t){return n.toPrecision(t)},e:function(n,t){return n.toExponential(t)},f:function(n,t){return n.toFixed(t)},r:function(n,t){return(n=Xo.round(n,Nt(n,t))).toFixed(Math.max(0,Math.min(20,Nt(n*(1+1e-15),t))))}}),tc=Xo.time={},ec=Date;zt.prototype={getDate:function(){return this._.getUTCDate()},getDay:function(){return this._.getUTCDay()},getFullYear:function(){return this._.getUTCFullYear()},getHours:function(){return this._.getUTCHours()},getMilliseconds:function(){return this._.getUTCMilliseconds()},getMinutes:function(){return this._.getUTCMinutes()},getMonth:function(){return this._.getUTCMonth()},getSeconds:function(){return this._.getUTCSeconds()},getTime:function(){return this._.getTime()},getTimezoneOffset:function(){return 0},valueOf:function(){return this._.valueOf()},setDate:function(){rc.setUTCDate.apply(this._,arguments)},setDay:function(){rc.setUTCDay.apply(this._,arguments)},setFullYear:function(){rc.setUTCFullYear.apply(this._,arguments)},setHours:function(){rc.setUTCHours.apply(this._,arguments)},setMilliseconds:function(){rc.setUTCMilliseconds.apply(this._,arguments)},setMinutes:function(){rc.setUTCMinutes.apply(this._,arguments)},setMonth:function(){rc.setUTCMonth.apply(this._,arguments)},setSeconds:function(){rc.setUTCSeconds.apply(this._,arguments)},setTime:function(){rc.setTime.apply(this._,arguments)}};var rc=Date.prototype;tc.year=Rt(function(n){return n=tc.day(n),n.setMonth(0,1),n},function(n,t){n.setFullYear(n.getFullYear()+t)},function(n){return n.getFullYear()}),tc.years=tc.year.range,tc.years.utc=tc.year.utc.range,tc.day=Rt(function(n){var t=new ec(2e3,0);return t.setFullYear(n.getFullYear(),n.getMonth(),n.getDate()),t},function(n,t){n.setDate(n.getDate()+t)},function(n){return n.getDate()-1}),tc.days=tc.day.range,tc.days.utc=tc.day.utc.range,tc.dayOfYear=function(n){var t=tc.year(n);return Math.floor((n-t-6e4*(n.getTimezoneOffset()-t.getTimezoneOffset()))/864e5)},["sunday","monday","tuesday","wednesday","thursday","friday","saturday"].forEach(function(n,t){t=7-t;var e=tc[n]=Rt(function(n){return(n=tc.day(n)).setDate(n.getDate()-(n.getDay()+t)%7),n},function(n,t){n.setDate(n.getDate()+7*Math.floor(t))},function(n){var e=tc.year(n).getDay();return Math.floor((tc.dayOfYear(n)+(e+t)%7)/7)-(e!==t)});tc[n+"s"]=e.range,tc[n+"s"].utc=e.utc.range,tc[n+"OfYear"]=function(n){var e=tc.year(n).getDay();return Math.floor((tc.dayOfYear(n)+(e+t)%7)/7)}}),tc.week=tc.sunday,tc.weeks=tc.sunday.range,tc.weeks.utc=tc.sunday.utc.range,tc.weekOfYear=tc.sundayOfYear;var uc={"-":"",_:" ",0:"0"},ic=/^\s*\d+/,oc=/^%/;Xo.locale=function(n){return{numberFormat:Tt(n),timeFormat:Pt(n)}};var ac=Xo.locale({decimal:".",thousands:",",grouping:[3],currency:["$",""],dateTime:"%a %b %e %X %Y",date:"%m/%d/%Y",time:"%H:%M:%S",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});Xo.format=ac.numberFormat,Xo.geo={},re.prototype={s:0,t:0,add:function(n){ue(n,this.t,cc),ue(cc.s,this.s,this),this.s?this.t+=cc.t:this.s=cc.t},reset:function(){this.s=this.t=0},valueOf:function(){return this.s}};var cc=new re;Xo.geo.stream=function(n,t){n&&sc.hasOwnProperty(n.type)?sc[n.type](n,t):ie(n,t)};var sc={Feature:function(n,t){ie(n.geometry,t)},FeatureCollection:function(n,t){for(var e=n.features,r=-1,u=e.length;++r<u;)ie(e[r].geometry,t)}},lc={Sphere:function(n,t){t.sphere()},Point:function(n,t){n=n.coordinates,t.point(n[0],n[1],n[2])},MultiPoint:function(n,t){for(var e=n.coordinates,r=-1,u=e.length;++r<u;)n=e[r],t.point(n[0],n[1],n[2])},LineString:function(n,t){oe(n.coordinates,t,0)},MultiLineString:function(n,t){for(var e=n.coordinates,r=-1,u=e.length;++r<u;)oe(e[r],t,0)},Polygon:function(n,t){ae(n.coordinates,t)},MultiPolygon:function(n,t){for(var e=n.coordinates,r=-1,u=e.length;++r<u;)ae(e[r],t)},GeometryCollection:function(n,t){for(var e=n.geometries,r=-1,u=e.length;++r<u;)ie(e[r],t)}};Xo.geo.area=function(n){return fc=0,Xo.geo.stream(n,gc),fc};var fc,hc=new re,gc={sphere:function(){fc+=4*Sa},point:g,lineStart:g,lineEnd:g,polygonStart:function(){hc.reset(),gc.lineStart=ce},polygonEnd:function(){var n=2*hc;fc+=0>n?4*Sa+n:n,gc.lineStart=gc.lineEnd=gc.point=g}};Xo.geo.bounds=function(){function n(n,t){x.push(M=[l=n,h=n]),f>t&&(f=t),t>g&&(g=t)}function t(t,e){var r=se([t*Na,e*Na]);if(m){var u=fe(m,r),i=[u[1],-u[0],0],o=fe(i,u);pe(o),o=ve(o);var c=t-p,s=c>0?1:-1,v=o[0]*La*s,d=oa(c)>180;if(d^(v>s*p&&s*t>v)){var y=o[1]*La;y>g&&(g=y)}else if(v=(v+360)%360-180,d^(v>s*p&&s*t>v)){var y=-o[1]*La;f>y&&(f=y)}else f>e&&(f=e),e>g&&(g=e);d?p>t?a(l,t)>a(l,h)&&(h=t):a(t,h)>a(l,h)&&(l=t):h>=l?(l>t&&(l=t),t>h&&(h=t)):t>p?a(l,t)>a(l,h)&&(h=t):a(t,h)>a(l,h)&&(l=t)}else n(t,e);m=r,p=t}function e(){_.point=t}function r(){M[0]=l,M[1]=h,_.point=n,m=null}function u(n,e){if(m){var r=n-p;y+=oa(r)>180?r+(r>0?360:-360):r}else v=n,d=e;gc.point(n,e),t(n,e)}function i(){gc.lineStart()}function o(){u(v,d),gc.lineEnd(),oa(y)>Aa&&(l=-(h=180)),M[0]=l,M[1]=h,m=null}function a(n,t){return(t-=n)<0?t+360:t}function c(n,t){return n[0]-t[0]}function s(n,t){return t[0]<=t[1]?t[0]<=n&&n<=t[1]:n<t[0]||t[1]<n}var l,f,h,g,p,v,d,m,y,x,M,_={point:n,lineStart:e,lineEnd:r,polygonStart:function(){_.point=u,_.lineStart=i,_.lineEnd=o,y=0,gc.polygonStart()},polygonEnd:function(){gc.polygonEnd(),_.point=n,_.lineStart=e,_.lineEnd=r,0>hc?(l=-(h=180),f=-(g=90)):y>Aa?g=90:-Aa>y&&(f=-90),M[0]=l,M[1]=h}};return function(n){g=h=-(l=f=1/0),x=[],Xo.geo.stream(n,_);var t=x.length;if(t){x.sort(c);for(var e,r=1,u=x[0],i=[u];t>r;++r)e=x[r],s(e[0],u)||s(e[1],u)?(a(u[0],e[1])>a(u[0],u[1])&&(u[1]=e[1]),a(e[0],u[1])>a(u[0],u[1])&&(u[0]=e[0])):i.push(u=e);for(var o,e,p=-1/0,t=i.length-1,r=0,u=i[t];t>=r;u=e,++r)e=i[r],(o=a(u[1],e[0]))>p&&(p=o,l=e[0],h=u[1])}return x=M=null,1/0===l||1/0===f?[[0/0,0/0],[0/0,0/0]]:[[l,f],[h,g]]}}(),Xo.geo.centroid=function(n){pc=vc=dc=mc=yc=xc=Mc=_c=bc=wc=Sc=0,Xo.geo.stream(n,kc);var t=bc,e=wc,r=Sc,u=t*t+e*e+r*r;return Ca>u&&(t=xc,e=Mc,r=_c,Aa>vc&&(t=dc,e=mc,r=yc),u=t*t+e*e+r*r,Ca>u)?[0/0,0/0]:[Math.atan2(e,t)*La,X(r/Math.sqrt(u))*La]};var pc,vc,dc,mc,yc,xc,Mc,_c,bc,wc,Sc,kc={sphere:g,point:me,lineStart:xe,lineEnd:Me,polygonStart:function(){kc.lineStart=_e},polygonEnd:function(){kc.lineStart=xe}},Ec=Ee(be,Te,ze,[-Sa,-Sa/2]),Ac=1e9;Xo.geo.clipExtent=function(){var n,t,e,r,u,i,o={stream:function(n){return u&&(u.valid=!1),u=i(n),u.valid=!0,u},extent:function(a){return arguments.length?(i=Pe(n=+a[0][0],t=+a[0][1],e=+a[1][0],r=+a[1][1]),u&&(u.valid=!1,u=null),o):[[n,t],[e,r]]}};return o.extent([[0,0],[960,500]])},(Xo.geo.conicEqualArea=function(){return je(He)}).raw=He,Xo.geo.albers=function(){return Xo.geo.conicEqualArea().rotate([96,0]).center([-.6,38.7]).parallels([29.5,45.5]).scale(1070)},Xo.geo.albersUsa=function(){function n(n){var i=n[0],o=n[1];return t=null,e(i,o),t||(r(i,o),t)||u(i,o),t}var t,e,r,u,i=Xo.geo.albers(),o=Xo.geo.conicEqualArea().rotate([154,0]).center([-2,58.5]).parallels([55,65]),a=Xo.geo.conicEqualArea().rotate([157,0]).center([-3,19.9]).parallels([8,18]),c={point:function(n,e){t=[n,e]}};return n.invert=function(n){var t=i.scale(),e=i.translate(),r=(n[0]-e[0])/t,u=(n[1]-e[1])/t;return(u>=.12&&.234>u&&r>=-.425&&-.214>r?o:u>=.166&&.234>u&&r>=-.214&&-.115>r?a:i).invert(n)},n.stream=function(n){var t=i.stream(n),e=o.stream(n),r=a.stream(n);return{point:function(n,u){t.point(n,u),e.point(n,u),r.point(n,u)},sphere:function(){t.sphere(),e.sphere(),r.sphere()},lineStart:function(){t.lineStart(),e.lineStart(),r.lineStart()},lineEnd:function(){t.lineEnd(),e.lineEnd(),r.lineEnd()},polygonStart:function(){t.polygonStart(),e.polygonStart(),r.polygonStart()},polygonEnd:function(){t.polygonEnd(),e.polygonEnd(),r.polygonEnd()}}},n.precision=function(t){return arguments.length?(i.precision(t),o.precision(t),a.precision(t),n):i.precision()},n.scale=function(t){return arguments.length?(i.scale(t),o.scale(.35*t),a.scale(t),n.translate(i.translate())):i.scale()},n.translate=function(t){if(!arguments.length)return i.translate();var s=i.scale(),l=+t[0],f=+t[1];return e=i.translate(t).clipExtent([[l-.455*s,f-.238*s],[l+.455*s,f+.238*s]]).stream(c).point,r=o.translate([l-.307*s,f+.201*s]).clipExtent([[l-.425*s+Aa,f+.12*s+Aa],[l-.214*s-Aa,f+.234*s-Aa]]).stream(c).point,u=a.translate([l-.205*s,f+.212*s]).clipExtent([[l-.214*s+Aa,f+.166*s+Aa],[l-.115*s-Aa,f+.234*s-Aa]]).stream(c).point,n},n.scale(1070)};var Cc,Nc,Lc,Tc,qc,zc,Rc={point:g,lineStart:g,lineEnd:g,polygonStart:function(){Nc=0,Rc.lineStart=Fe},polygonEnd:function(){Rc.lineStart=Rc.lineEnd=Rc.point=g,Cc+=oa(Nc/2)}},Dc={point:Oe,lineStart:g,lineEnd:g,polygonStart:g,polygonEnd:g},Pc={point:Ze,lineStart:Ve,lineEnd:Xe,polygonStart:function(){Pc.lineStart=$e},polygonEnd:function(){Pc.point=Ze,Pc.lineStart=Ve,Pc.lineEnd=Xe}};Xo.geo.path=function(){function n(n){return n&&("function"==typeof a&&i.pointRadius(+a.apply(this,arguments)),o&&o.valid||(o=u(i)),Xo.geo.stream(n,o)),i.result()}function t(){return o=null,n}var e,r,u,i,o,a=4.5;return n.area=function(n){return Cc=0,Xo.geo.stream(n,u(Rc)),Cc},n.centroid=function(n){return dc=mc=yc=xc=Mc=_c=bc=wc=Sc=0,Xo.geo.stream(n,u(Pc)),Sc?[bc/Sc,wc/Sc]:_c?[xc/_c,Mc/_c]:yc?[dc/yc,mc/yc]:[0/0,0/0]},n.bounds=function(n){return qc=zc=-(Lc=Tc=1/0),Xo.geo.stream(n,u(Dc)),[[Lc,Tc],[qc,zc]]},n.projection=function(n){return arguments.length?(u=(e=n)?n.stream||Je(n):bt,t()):e},n.context=function(n){return arguments.length?(i=null==(r=n)?new Ye:new Be(n),"function"!=typeof a&&i.pointRadius(a),t()):r},n.pointRadius=function(t){return arguments.length?(a="function"==typeof t?t:(i.pointRadius(+t),+t),n):a},n.projection(Xo.geo.albersUsa()).context(null)},Xo.geo.transform=function(n){return{stream:function(t){var e=new Ge(t);for(var r in n)e[r]=n[r];return e}}},Ge.prototype={point:function(n,t){this.stream.point(n,t)},sphere:function(){this.stream.sphere()},lineStart:function(){this.stream.lineStart()},lineEnd:function(){this.stream.lineEnd()},polygonStart:function(){this.stream.polygonStart()},polygonEnd:function(){this.stream.polygonEnd()}},Xo.geo.projection=Qe,Xo.geo.projectionMutator=nr,(Xo.geo.equirectangular=function(){return Qe(er)}).raw=er.invert=er,Xo.geo.rotation=function(n){function t(t){return t=n(t[0]*Na,t[1]*Na),t[0]*=La,t[1]*=La,t}return n=ur(n[0]%360*Na,n[1]*Na,n.length>2?n[2]*Na:0),t.invert=function(t){return t=n.invert(t[0]*Na,t[1]*Na),t[0]*=La,t[1]*=La,t},t},rr.invert=er,Xo.geo.circle=function(){function n(){var n="function"==typeof r?r.apply(this,arguments):r,t=ur(-n[0]*Na,-n[1]*Na,0).invert,u=[];return e(null,null,1,{point:function(n,e){u.push(n=t(n,e)),n[0]*=La,n[1]*=La}}),{type:"Polygon",coordinates:[u]}}var t,e,r=[0,0],u=6;return n.origin=function(t){return arguments.length?(r=t,n):r},n.angle=function(r){return arguments.length?(e=cr((t=+r)*Na,u*Na),n):t},n.precision=function(r){return arguments.length?(e=cr(t*Na,(u=+r)*Na),n):u},n.angle(90)},Xo.geo.distance=function(n,t){var e,r=(t[0]-n[0])*Na,u=n[1]*Na,i=t[1]*Na,o=Math.sin(r),a=Math.cos(r),c=Math.sin(u),s=Math.cos(u),l=Math.sin(i),f=Math.cos(i);return Math.atan2(Math.sqrt((e=f*o)*e+(e=s*l-c*f*a)*e),c*l+s*f*a)},Xo.geo.graticule=function(){function n(){return{type:"MultiLineString",coordinates:t()}}function t(){return Xo.range(Math.ceil(i/d)*d,u,d).map(h).concat(Xo.range(Math.ceil(s/m)*m,c,m).map(g)).concat(Xo.range(Math.ceil(r/p)*p,e,p).filter(function(n){return oa(n%d)>Aa}).map(l)).concat(Xo.range(Math.ceil(a/v)*v,o,v).filter(function(n){return oa(n%m)>Aa}).map(f))}var e,r,u,i,o,a,c,s,l,f,h,g,p=10,v=p,d=90,m=360,y=2.5;return n.lines=function(){return t().map(function(n){return{type:"LineString",coordinates:n}})},n.outline=function(){return{type:"Polygon",coordinates:[h(i).concat(g(c).slice(1),h(u).reverse().slice(1),g(s).reverse().slice(1))]}},n.extent=function(t){return arguments.length?n.majorExtent(t).minorExtent(t):n.minorExtent()},n.majorExtent=function(t){return arguments.length?(i=+t[0][0],u=+t[1][0],s=+t[0][1],c=+t[1][1],i>u&&(t=i,i=u,u=t),s>c&&(t=s,s=c,c=t),n.precision(y)):[[i,s],[u,c]]},n.minorExtent=function(t){return arguments.length?(r=+t[0][0],e=+t[1][0],a=+t[0][1],o=+t[1][1],r>e&&(t=r,r=e,e=t),a>o&&(t=a,a=o,o=t),n.precision(y)):[[r,a],[e,o]]},n.step=function(t){return arguments.length?n.majorStep(t).minorStep(t):n.minorStep()},n.majorStep=function(t){return arguments.length?(d=+t[0],m=+t[1],n):[d,m]},n.minorStep=function(t){return arguments.length?(p=+t[0],v=+t[1],n):[p,v]},n.precision=function(t){return arguments.length?(y=+t,l=lr(a,o,90),f=fr(r,e,y),h=lr(s,c,90),g=fr(i,u,y),n):y},n.majorExtent([[-180,-90+Aa],[180,90-Aa]]).minorExtent([[-180,-80-Aa],[180,80+Aa]])},Xo.geo.greatArc=function(){function n(){return{type:"LineString",coordinates:[t||r.apply(this,arguments),e||u.apply(this,arguments)]}}var t,e,r=hr,u=gr;return n.distance=function(){return Xo.geo.distance(t||r.apply(this,arguments),e||u.apply(this,arguments))},n.source=function(e){return arguments.length?(r=e,t="function"==typeof e?null:e,n):r},n.target=function(t){return arguments.length?(u=t,e="function"==typeof t?null:t,n):u},n.precision=function(){return arguments.length?n:0},n},Xo.geo.interpolate=function(n,t){return pr(n[0]*Na,n[1]*Na,t[0]*Na,t[1]*Na)},Xo.geo.length=function(n){return Uc=0,Xo.geo.stream(n,jc),Uc};var Uc,jc={sphere:g,point:g,lineStart:vr,lineEnd:g,polygonStart:g,polygonEnd:g},Hc=dr(function(n){return Math.sqrt(2/(1+n))},function(n){return 2*Math.asin(n/2)});(Xo.geo.azimuthalEqualArea=function(){return Qe(Hc)}).raw=Hc;var Fc=dr(function(n){var t=Math.acos(n);return t&&t/Math.sin(t)},bt);(Xo.geo.azimuthalEquidistant=function(){return Qe(Fc)}).raw=Fc,(Xo.geo.conicConformal=function(){return je(mr)}).raw=mr,(Xo.geo.conicEquidistant=function(){return je(yr)}).raw=yr;var Oc=dr(function(n){return 1/n},Math.atan);(Xo.geo.gnomonic=function(){return Qe(Oc)}).raw=Oc,xr.invert=function(n,t){return[n,2*Math.atan(Math.exp(t))-Ea]},(Xo.geo.mercator=function(){return Mr(xr)}).raw=xr;var Yc=dr(function(){return 1},Math.asin);(Xo.geo.orthographic=function(){return Qe(Yc)}).raw=Yc;var Ic=dr(function(n){return 1/(1+n)},function(n){return 2*Math.atan(n)});(Xo.geo.stereographic=function(){return Qe(Ic)}).raw=Ic,_r.invert=function(n,t){return[-t,2*Math.atan(Math.exp(n))-Ea]},(Xo.geo.transverseMercator=function(){var n=Mr(_r),t=n.center,e=n.rotate;return n.center=function(n){return n?t([-n[1],n[0]]):(n=t(),[-n[1],n[0]])},n.rotate=function(n){return n?e([n[0],n[1],n.length>2?n[2]+90:90]):(n=e(),[n[0],n[1],n[2]-90])},n.rotate([0,0])}).raw=_r,Xo.geom={},Xo.geom.hull=function(n){function t(n){if(n.length<3)return[];var t,u=_t(e),i=_t(r),o=n.length,a=[],c=[];for(t=0;o>t;t++)a.push([+u.call(this,n[t],t),+i.call(this,n[t],t),t]);for(a.sort(kr),t=0;o>t;t++)c.push([a[t][0],-a[t][1]]);var s=Sr(a),l=Sr(c),f=l[0]===s[0],h=l[l.length-1]===s[s.length-1],g=[];for(t=s.length-1;t>=0;--t)g.push(n[a[s[t]][2]]);for(t=+f;t<l.length-h;++t)g.push(n[a[l[t]][2]]);return g}var e=br,r=wr;return arguments.length?t(n):(t.x=function(n){return arguments.length?(e=n,t):e},t.y=function(n){return arguments.length?(r=n,t):r},t)},Xo.geom.polygon=function(n){return fa(n,Zc),n};var Zc=Xo.geom.polygon.prototype=[];Zc.area=function(){for(var n,t=-1,e=this.length,r=this[e-1],u=0;++t<e;)n=r,r=this[t],u+=n[1]*r[0]-n[0]*r[1];return.5*u},Zc.centroid=function(n){var t,e,r=-1,u=this.length,i=0,o=0,a=this[u-1];for(arguments.length||(n=-1/(6*this.area()));++r<u;)t=a,a=this[r],e=t[0]*a[1]-a[0]*t[1],i+=(t[0]+a[0])*e,o+=(t[1]+a[1])*e;return[i*n,o*n]},Zc.clip=function(n){for(var t,e,r,u,i,o,a=Cr(n),c=-1,s=this.length-Cr(this),l=this[s-1];++c<s;){for(t=n.slice(),n.length=0,u=this[c],i=t[(r=t.length-a)-1],e=-1;++e<r;)o=t[e],Er(o,l,u)?(Er(i,l,u)||n.push(Ar(i,o,l,u)),n.push(o)):Er(i,l,u)&&n.push(Ar(i,o,l,u)),i=o;a&&n.push(n[0]),l=u}return n};var Vc,Xc,$c,Bc,Wc,Jc=[],Gc=[];Pr.prototype.prepare=function(){for(var n,t=this.edges,e=t.length;e--;)n=t[e].edge,n.b&&n.a||t.splice(e,1);return t.sort(jr),t.length},Br.prototype={start:function(){return this.edge.l===this.site?this.edge.a:this.edge.b},end:function(){return this.edge.l===this.site?this.edge.b:this.edge.a}},Wr.prototype={insert:function(n,t){var e,r,u;if(n){if(t.P=n,t.N=n.N,n.N&&(n.N.P=t),n.N=t,n.R){for(n=n.R;n.L;)n=n.L;n.L=t}else n.R=t;e=n}else this._?(n=Qr(this._),t.P=null,t.N=n,n.P=n.L=t,e=n):(t.P=t.N=null,this._=t,e=null);for(t.L=t.R=null,t.U=e,t.C=!0,n=t;e&&e.C;)r=e.U,e===r.L?(u=r.R,u&&u.C?(e.C=u.C=!1,r.C=!0,n=r):(n===e.R&&(Gr(this,e),n=e,e=n.U),e.C=!1,r.C=!0,Kr(this,r))):(u=r.L,u&&u.C?(e.C=u.C=!1,r.C=!0,n=r):(n===e.L&&(Kr(this,e),n=e,e=n.U),e.C=!1,r.C=!0,Gr(this,r))),e=n.U;this._.C=!1},remove:function(n){n.N&&(n.N.P=n.P),n.P&&(n.P.N=n.N),n.N=n.P=null;var t,e,r,u=n.U,i=n.L,o=n.R;if(e=i?o?Qr(o):i:o,u?u.L===n?u.L=e:u.R=e:this._=e,i&&o?(r=e.C,e.C=n.C,e.L=i,i.U=e,e!==o?(u=e.U,e.U=n.U,n=e.R,u.L=n,e.R=o,o.U=e):(e.U=u,u=e,n=e.R)):(r=n.C,n=e),n&&(n.U=u),!r){if(n&&n.C)return n.C=!1,void 0;do{if(n===this._)break;if(n===u.L){if(t=u.R,t.C&&(t.C=!1,u.C=!0,Gr(this,u),t=u.R),t.L&&t.L.C||t.R&&t.R.C){t.R&&t.R.C||(t.L.C=!1,t.C=!0,Kr(this,t),t=u.R),t.C=u.C,u.C=t.R.C=!1,Gr(this,u),n=this._;break}}else if(t=u.L,t.C&&(t.C=!1,u.C=!0,Kr(this,u),t=u.L),t.L&&t.L.C||t.R&&t.R.C){t.L&&t.L.C||(t.R.C=!1,t.C=!0,Gr(this,t),t=u.L),t.C=u.C,u.C=t.L.C=!1,Kr(this,u),n=this._;break}t.C=!0,n=u,u=u.U}while(!n.C);n&&(n.C=!1)}}},Xo.geom.voronoi=function(n){function t(n){var t=new Array(n.length),r=a[0][0],u=a[0][1],i=a[1][0],o=a[1][1];return nu(e(n),a).cells.forEach(function(e,a){var c=e.edges,s=e.site,l=t[a]=c.length?c.map(function(n){var t=n.start();return[t.x,t.y]}):s.x>=r&&s.x<=i&&s.y>=u&&s.y<=o?[[r,o],[i,o],[i,u],[r,u]]:[];l.point=n[a]}),t}function e(n){return n.map(function(n,t){return{x:Math.round(i(n,t)/Aa)*Aa,y:Math.round(o(n,t)/Aa)*Aa,i:t}})}var r=br,u=wr,i=r,o=u,a=Kc;return n?t(n):(t.links=function(n){return nu(e(n)).edges.filter(function(n){return n.l&&n.r}).map(function(t){return{source:n[t.l.i],target:n[t.r.i]}})},t.triangles=function(n){var t=[];return nu(e(n)).cells.forEach(function(e,r){for(var u,i,o=e.site,a=e.edges.sort(jr),c=-1,s=a.length,l=a[s-1].edge,f=l.l===o?l.r:l.l;++c<s;)u=l,i=f,l=a[c].edge,f=l.l===o?l.r:l.l,r<i.i&&r<f.i&&eu(o,i,f)<0&&t.push([n[r],n[i.i],n[f.i]])}),t},t.x=function(n){return arguments.length?(i=_t(r=n),t):r},t.y=function(n){return arguments.length?(o=_t(u=n),t):u},t.clipExtent=function(n){return arguments.length?(a=null==n?Kc:n,t):a===Kc?null:a},t.size=function(n){return arguments.length?t.clipExtent(n&&[[0,0],n]):a===Kc?null:a&&a[1]},t)};var Kc=[[-1e6,-1e6],[1e6,1e6]];Xo.geom.delaunay=function(n){return Xo.geom.voronoi().triangles(n)},Xo.geom.quadtree=function(n,t,e,r,u){function i(n){function i(n,t,e,r,u,i,o,a){if(!isNaN(e)&&!isNaN(r))if(n.leaf){var c=n.x,l=n.y;if(null!=c)if(oa(c-e)+oa(l-r)<.01)s(n,t,e,r,u,i,o,a);else{var f=n.point;n.x=n.y=n.point=null,s(n,f,c,l,u,i,o,a),s(n,t,e,r,u,i,o,a)}else n.x=e,n.y=r,n.point=t}else s(n,t,e,r,u,i,o,a)}function s(n,t,e,r,u,o,a,c){var s=.5*(u+a),l=.5*(o+c),f=e>=s,h=r>=l,g=(h<<1)+f;n.leaf=!1,n=n.nodes[g]||(n.nodes[g]=iu()),f?u=s:a=s,h?o=l:c=l,i(n,t,e,r,u,o,a,c)}var l,f,h,g,p,v,d,m,y,x=_t(a),M=_t(c);if(null!=t)v=t,d=e,m=r,y=u;else if(m=y=-(v=d=1/0),f=[],h=[],p=n.length,o)for(g=0;p>g;++g)l=n[g],l.x<v&&(v=l.x),l.y<d&&(d=l.y),l.x>m&&(m=l.x),l.y>y&&(y=l.y),f.push(l.x),h.push(l.y);else for(g=0;p>g;++g){var _=+x(l=n[g],g),b=+M(l,g);v>_&&(v=_),d>b&&(d=b),_>m&&(m=_),b>y&&(y=b),f.push(_),h.push(b)}var w=m-v,S=y-d;w>S?y=d+w:m=v+S;var k=iu();if(k.add=function(n){i(k,n,+x(n,++g),+M(n,g),v,d,m,y)},k.visit=function(n){ou(n,k,v,d,m,y)},g=-1,null==t){for(;++g<p;)i(k,n[g],f[g],h[g],v,d,m,y);--g}else n.forEach(k.add);return f=h=n=l=null,k}var o,a=br,c=wr;return(o=arguments.length)?(a=ru,c=uu,3===o&&(u=e,r=t,e=t=0),i(n)):(i.x=function(n){return arguments.length?(a=n,i):a},i.y=function(n){return arguments.length?(c=n,i):c},i.extent=function(n){return arguments.length?(null==n?t=e=r=u=null:(t=+n[0][0],e=+n[0][1],r=+n[1][0],u=+n[1][1]),i):null==t?null:[[t,e],[r,u]]},i.size=function(n){return arguments.length?(null==n?t=e=r=u=null:(t=e=0,r=+n[0],u=+n[1]),i):null==t?null:[r-t,u-e]},i)},Xo.interpolateRgb=au,Xo.interpolateObject=cu,Xo.interpolateNumber=su,Xo.interpolateString=lu;var Qc=/[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g;Xo.interpolate=fu,Xo.interpolators=[function(n,t){var e=typeof t;return("string"===e?Va.has(t)||/^(#|rgb\(|hsl\()/.test(t)?au:lu:t instanceof G?au:"object"===e?Array.isArray(t)?hu:cu:su)(n,t)}],Xo.interpolateArray=hu;var ns=function(){return bt},ts=Xo.map({linear:ns,poly:xu,quad:function(){return du},cubic:function(){return mu},sin:function(){return Mu},exp:function(){return _u},circle:function(){return bu},elastic:wu,back:Su,bounce:function(){return ku}}),es=Xo.map({"in":bt,out:pu,"in-out":vu,"out-in":function(n){return vu(pu(n))}});Xo.ease=function(n){var t=n.indexOf("-"),e=t>=0?n.substring(0,t):n,r=t>=0?n.substring(t+1):"in";return e=ts.get(e)||ns,r=es.get(r)||bt,gu(r(e.apply(null,$o.call(arguments,1))))},Xo.interpolateHcl=Eu,Xo.interpolateHsl=Au,Xo.interpolateLab=Cu,Xo.interpolateRound=Nu,Xo.transform=function(n){var t=Wo.createElementNS(Xo.ns.prefix.svg,"g");return(Xo.transform=function(n){if(null!=n){t.setAttribute("transform",n);var e=t.transform.baseVal.consolidate()}return new Lu(e?e.matrix:rs)})(n)},Lu.prototype.toString=function(){return"translate("+this.translate+")rotate("+this.rotate+")skewX("+this.skew+")scale("+this.scale+")"};var rs={a:1,b:0,c:0,d:1,e:0,f:0};Xo.interpolateTransform=Ru,Xo.layout={},Xo.layout.bundle=function(){return function(n){for(var t=[],e=-1,r=n.length;++e<r;)t.push(Uu(n[e]));return t}},Xo.layout.chord=function(){function n(){var n,s,f,h,g,p={},v=[],d=Xo.range(i),m=[];for(e=[],r=[],n=0,h=-1;++h<i;){for(s=0,g=-1;++g<i;)s+=u[h][g];v.push(s),m.push(Xo.range(i)),n+=s}for(o&&d.sort(function(n,t){return o(v[n],v[t])}),a&&m.forEach(function(n,t){n.sort(function(n,e){return a(u[t][n],u[t][e])})}),n=(ka-l*i)/n,s=0,h=-1;++h<i;){for(f=s,g=-1;++g<i;){var y=d[h],x=m[y][g],M=u[y][x],_=s,b=s+=M*n;p[y+"-"+x]={index:y,subindex:x,startAngle:_,endAngle:b,value:M}}r[y]={index:y,startAngle:f,endAngle:s,value:(s-f)/n},s+=l}for(h=-1;++h<i;)for(g=h-1;++g<i;){var w=p[h+"-"+g],S=p[g+"-"+h];(w.value||S.value)&&e.push(w.value<S.value?{source:S,target:w}:{source:w,target:S})}c&&t()}function t(){e.sort(function(n,t){return c((n.source.value+n.target.value)/2,(t.source.value+t.target.value)/2)})}var e,r,u,i,o,a,c,s={},l=0;return s.matrix=function(n){return arguments.length?(i=(u=n)&&u.length,e=r=null,s):u},s.padding=function(n){return arguments.length?(l=n,e=r=null,s):l},s.sortGroups=function(n){return arguments.length?(o=n,e=r=null,s):o},s.sortSubgroups=function(n){return arguments.length?(a=n,e=null,s):a},s.sortChords=function(n){return arguments.length?(c=n,e&&t(),s):c},s.chords=function(){return e||n(),e},s.groups=function(){return r||n(),r},s},Xo.layout.force=function(){function n(n){return function(t,e,r,u){if(t.point!==n){var i=t.cx-n.x,o=t.cy-n.y,a=u-e,c=i*i+o*o;if(c>a*a/d){if(p>c){var s=t.charge/c;n.px-=i*s,n.py-=o*s}return!0}if(t.point&&c&&p>c){var s=t.pointCharge/c;n.px-=i*s,n.py-=o*s}}return!t.charge}}function t(n){n.px=Xo.event.x,n.py=Xo.event.y,a.resume()}var e,r,u,i,o,a={},c=Xo.dispatch("start","tick","end"),s=[1,1],l=.9,f=us,h=is,g=-30,p=os,v=.1,d=.64,m=[],y=[];return a.tick=function(){if((r*=.99)<.005)return c.end({type:"end",alpha:r=0}),!0;var t,e,a,f,h,p,d,x,M,_=m.length,b=y.length;for(e=0;b>e;++e)a=y[e],f=a.source,h=a.target,x=h.x-f.x,M=h.y-f.y,(p=x*x+M*M)&&(p=r*i[e]*((p=Math.sqrt(p))-u[e])/p,x*=p,M*=p,h.x-=x*(d=f.weight/(h.weight+f.weight)),h.y-=M*d,f.x+=x*(d=1-d),f.y+=M*d);if((d=r*v)&&(x=s[0]/2,M=s[1]/2,e=-1,d))for(;++e<_;)a=m[e],a.x+=(x-a.x)*d,a.y+=(M-a.y)*d;if(g)for(Zu(t=Xo.geom.quadtree(m),r,o),e=-1;++e<_;)(a=m[e]).fixed||t.visit(n(a));for(e=-1;++e<_;)a=m[e],a.fixed?(a.x=a.px,a.y=a.py):(a.x-=(a.px-(a.px=a.x))*l,a.y-=(a.py-(a.py=a.y))*l);c.tick({type:"tick",alpha:r})},a.nodes=function(n){return arguments.length?(m=n,a):m},a.links=function(n){return arguments.length?(y=n,a):y},a.size=function(n){return arguments.length?(s=n,a):s},a.linkDistance=function(n){return arguments.length?(f="function"==typeof n?n:+n,a):f},a.distance=a.linkDistance,a.linkStrength=function(n){return arguments.length?(h="function"==typeof n?n:+n,a):h},a.friction=function(n){return arguments.length?(l=+n,a):l},a.charge=function(n){return arguments.length?(g="function"==typeof n?n:+n,a):g},a.chargeDistance=function(n){return arguments.length?(p=n*n,a):Math.sqrt(p)},a.gravity=function(n){return arguments.length?(v=+n,a):v},a.theta=function(n){return arguments.length?(d=n*n,a):Math.sqrt(d)},a.alpha=function(n){return arguments.length?(n=+n,r?r=n>0?n:0:n>0&&(c.start({type:"start",alpha:r=n}),Xo.timer(a.tick)),a):r},a.start=function(){function n(n,r){if(!e){for(e=new Array(c),a=0;c>a;++a)e[a]=[];for(a=0;s>a;++a){var u=y[a];e[u.source.index].push(u.target),e[u.target.index].push(u.source)}}for(var i,o=e[t],a=-1,s=o.length;++a<s;)if(!isNaN(i=o[a][n]))return i;return Math.random()*r}var t,e,r,c=m.length,l=y.length,p=s[0],v=s[1];for(t=0;c>t;++t)(r=m[t]).index=t,r.weight=0;for(t=0;l>t;++t)r=y[t],"number"==typeof r.source&&(r.source=m[r.source]),"number"==typeof r.target&&(r.target=m[r.target]),++r.source.weight,++r.target.weight;for(t=0;c>t;++t)r=m[t],isNaN(r.x)&&(r.x=n("x",p)),isNaN(r.y)&&(r.y=n("y",v)),isNaN(r.px)&&(r.px=r.x),isNaN(r.py)&&(r.py=r.y);if(u=[],"function"==typeof f)for(t=0;l>t;++t)u[t]=+f.call(this,y[t],t);else for(t=0;l>t;++t)u[t]=f;if(i=[],"function"==typeof h)for(t=0;l>t;++t)i[t]=+h.call(this,y[t],t);else for(t=0;l>t;++t)i[t]=h;if(o=[],"function"==typeof g)for(t=0;c>t;++t)o[t]=+g.call(this,m[t],t);else for(t=0;c>t;++t)o[t]=g;return a.resume()},a.resume=function(){return a.alpha(.1)},a.stop=function(){return a.alpha(0)},a.drag=function(){return e||(e=Xo.behavior.drag().origin(bt).on("dragstart.force",Fu).on("drag.force",t).on("dragend.force",Ou)),arguments.length?(this.on("mouseover.force",Yu).on("mouseout.force",Iu).call(e),void 0):e},Xo.rebind(a,c,"on")};var us=20,is=1,os=1/0;Xo.layout.hierarchy=function(){function n(t,o,a){var c=u.call(e,t,o);if(t.depth=o,a.push(t),c&&(s=c.length)){for(var s,l,f=-1,h=t.children=new Array(s),g=0,p=o+1;++f<s;)l=h[f]=n(c[f],p,a),l.parent=t,g+=l.value;r&&h.sort(r),i&&(t.value=g)}else delete t.children,i&&(t.value=+i.call(e,t,o)||0);return t}function t(n,r){var u=n.children,o=0;if(u&&(a=u.length))for(var a,c=-1,s=r+1;++c<a;)o+=t(u[c],s);else i&&(o=+i.call(e,n,r)||0);return i&&(n.value=o),o}function e(t){var e=[];return n(t,0,e),e}var r=Bu,u=Xu,i=$u;return e.sort=function(n){return arguments.length?(r=n,e):r},e.children=function(n){return arguments.length?(u=n,e):u},e.value=function(n){return arguments.length?(i=n,e):i},e.revalue=function(n){return t(n,0),n},e},Xo.layout.partition=function(){function n(t,e,r,u){var i=t.children;if(t.x=e,t.y=t.depth*u,t.dx=r,t.dy=u,i&&(o=i.length)){var o,a,c,s=-1;for(r=t.value?r/t.value:0;++s<o;)n(a=i[s],e,c=a.value*r,u),e+=c}}function t(n){var e=n.children,r=0;if(e&&(u=e.length))for(var u,i=-1;++i<u;)r=Math.max(r,t(e[i]));return 1+r}function e(e,i){var o=r.call(this,e,i);return n(o[0],0,u[0],u[1]/t(o[0])),o}var r=Xo.layout.hierarchy(),u=[1,1];return e.size=function(n){return arguments.length?(u=n,e):u},Vu(e,r)},Xo.layout.pie=function(){function n(i){var o=i.map(function(e,r){return+t.call(n,e,r)}),a=+("function"==typeof r?r.apply(this,arguments):r),c=(("function"==typeof u?u.apply(this,arguments):u)-a)/Xo.sum(o),s=Xo.range(i.length);null!=e&&s.sort(e===as?function(n,t){return o[t]-o[n]}:function(n,t){return e(i[n],i[t])});var l=[];return s.forEach(function(n){var t;l[n]={data:i[n],value:t=o[n],startAngle:a,endAngle:a+=t*c}}),l}var t=Number,e=as,r=0,u=ka;return n.value=function(e){return arguments.length?(t=e,n):t},n.sort=function(t){return arguments.length?(e=t,n):e},n.startAngle=function(t){return arguments.length?(r=t,n):r},n.endAngle=function(t){return arguments.length?(u=t,n):u},n};var as={};Xo.layout.stack=function(){function n(a,c){var s=a.map(function(e,r){return t.call(n,e,r)}),l=s.map(function(t){return t.map(function(t,e){return[i.call(n,t,e),o.call(n,t,e)]})}),f=e.call(n,l,c);s=Xo.permute(s,f),l=Xo.permute(l,f);var h,g,p,v=r.call(n,l,c),d=s.length,m=s[0].length;for(g=0;m>g;++g)for(u.call(n,s[0][g],p=v[g],l[0][g][1]),h=1;d>h;++h)u.call(n,s[h][g],p+=l[h-1][g][1],l[h][g][1]);return a}var t=bt,e=Qu,r=ni,u=Ku,i=Ju,o=Gu;return n.values=function(e){return arguments.length?(t=e,n):t},n.order=function(t){return arguments.length?(e="function"==typeof t?t:cs.get(t)||Qu,n):e},n.offset=function(t){return arguments.length?(r="function"==typeof t?t:ss.get(t)||ni,n):r},n.x=function(t){return arguments.length?(i=t,n):i},n.y=function(t){return arguments.length?(o=t,n):o},n.out=function(t){return arguments.length?(u=t,n):u},n};var cs=Xo.map({"inside-out":function(n){var t,e,r=n.length,u=n.map(ti),i=n.map(ei),o=Xo.range(r).sort(function(n,t){return u[n]-u[t]}),a=0,c=0,s=[],l=[];for(t=0;r>t;++t)e=o[t],c>a?(a+=i[e],s.push(e)):(c+=i[e],l.push(e));return l.reverse().concat(s)},reverse:function(n){return Xo.range(n.length).reverse()},"default":Qu}),ss=Xo.map({silhouette:function(n){var t,e,r,u=n.length,i=n[0].length,o=[],a=0,c=[];for(e=0;i>e;++e){for(t=0,r=0;u>t;t++)r+=n[t][e][1];r>a&&(a=r),o.push(r)}for(e=0;i>e;++e)c[e]=(a-o[e])/2;return c},wiggle:function(n){var t,e,r,u,i,o,a,c,s,l=n.length,f=n[0],h=f.length,g=[];for(g[0]=c=s=0,e=1;h>e;++e){for(t=0,u=0;l>t;++t)u+=n[t][e][1];for(t=0,i=0,a=f[e][0]-f[e-1][0];l>t;++t){for(r=0,o=(n[t][e][1]-n[t][e-1][1])/(2*a);t>r;++r)o+=(n[r][e][1]-n[r][e-1][1])/a;i+=o*n[t][e][1]}g[e]=c-=u?i/u*a:0,s>c&&(s=c)}for(e=0;h>e;++e)g[e]-=s;return g},expand:function(n){var t,e,r,u=n.length,i=n[0].length,o=1/u,a=[];for(e=0;i>e;++e){for(t=0,r=0;u>t;t++)r+=n[t][e][1];if(r)for(t=0;u>t;t++)n[t][e][1]/=r;else for(t=0;u>t;t++)n[t][e][1]=o}for(e=0;i>e;++e)a[e]=0;return a},zero:ni});Xo.layout.histogram=function(){function n(n,i){for(var o,a,c=[],s=n.map(e,this),l=r.call(this,s,i),f=u.call(this,l,s,i),i=-1,h=s.length,g=f.length-1,p=t?1:1/h;++i<g;)o=c[i]=[],o.dx=f[i+1]-(o.x=f[i]),o.y=0;if(g>0)for(i=-1;++i<h;)a=s[i],a>=l[0]&&a<=l[1]&&(o=c[Xo.bisect(f,a,1,g)-1],o.y+=p,o.push(n[i]));return c}var t=!0,e=Number,r=oi,u=ui;return n.value=function(t){return arguments.length?(e=t,n):e},n.range=function(t){return arguments.length?(r=_t(t),n):r},n.bins=function(t){return arguments.length?(u="number"==typeof t?function(n){return ii(n,t)}:_t(t),n):u},n.frequency=function(e){return arguments.length?(t=!!e,n):t},n},Xo.layout.tree=function(){function n(n,i){function o(n,t){var r=n.children,u=n._tree;if(r&&(i=r.length)){for(var i,a,s,l=r[0],f=l,h=-1;++h<i;)s=r[h],o(s,a),f=c(s,a,f),a=s;vi(n);var g=.5*(l._tree.prelim+s._tree.prelim);t?(u.prelim=t._tree.prelim+e(n,t),u.mod=u.prelim-g):u.prelim=g}else t&&(u.prelim=t._tree.prelim+e(n,t))}function a(n,t){n.x=n._tree.prelim+t;var e=n.children;if(e&&(r=e.length)){var r,u=-1;for(t+=n._tree.mod;++u<r;)a(e[u],t)}}function c(n,t,r){if(t){for(var u,i=n,o=n,a=t,c=n.parent.children[0],s=i._tree.mod,l=o._tree.mod,f=a._tree.mod,h=c._tree.mod;a=si(a),i=ci(i),a&&i;)c=ci(c),o=si(o),o._tree.ancestor=n,u=a._tree.prelim+f-i._tree.prelim-s+e(a,i),u>0&&(di(mi(a,n,r),n,u),s+=u,l+=u),f+=a._tree.mod,s+=i._tree.mod,h+=c._tree.mod,l+=o._tree.mod;a&&!si(o)&&(o._tree.thread=a,o._tree.mod+=f-l),i&&!ci(c)&&(c._tree.thread=i,c._tree.mod+=s-h,r=n)}return r}var s=t.call(this,n,i),l=s[0];pi(l,function(n,t){n._tree={ancestor:n,prelim:0,mod:0,change:0,shift:0,number:t?t._tree.number+1:0}}),o(l),a(l,-l._tree.prelim);var f=li(l,hi),h=li(l,fi),g=li(l,gi),p=f.x-e(f,h)/2,v=h.x+e(h,f)/2,d=g.depth||1;return pi(l,u?function(n){n.x*=r[0],n.y=n.depth*r[1],delete n._tree}:function(n){n.x=(n.x-p)/(v-p)*r[0],n.y=n.depth/d*r[1],delete n._tree}),s}var t=Xo.layout.hierarchy().sort(null).value(null),e=ai,r=[1,1],u=!1;return n.separation=function(t){return arguments.length?(e=t,n):e},n.size=function(t){return arguments.length?(u=null==(r=t),n):u?null:r},n.nodeSize=function(t){return arguments.length?(u=null!=(r=t),n):u?r:null},Vu(n,t)},Xo.layout.pack=function(){function n(n,i){var o=e.call(this,n,i),a=o[0],c=u[0],s=u[1],l=null==t?Math.sqrt:"function"==typeof t?t:function(){return t};if(a.x=a.y=0,pi(a,function(n){n.r=+l(n.value)}),pi(a,bi),r){var f=r*(t?1:Math.max(2*a.r/c,2*a.r/s))/2;pi(a,function(n){n.r+=f}),pi(a,bi),pi(a,function(n){n.r-=f})}return ki(a,c/2,s/2,t?1:1/Math.max(2*a.r/c,2*a.r/s)),o}var t,e=Xo.layout.hierarchy().sort(yi),r=0,u=[1,1];return n.size=function(t){return arguments.length?(u=t,n):u},n.radius=function(e){return arguments.length?(t=null==e||"function"==typeof e?e:+e,n):t},n.padding=function(t){return arguments.length?(r=+t,n):r},Vu(n,e)},Xo.layout.cluster=function(){function n(n,i){var o,a=t.call(this,n,i),c=a[0],s=0;pi(c,function(n){var t=n.children;t&&t.length?(n.x=Ci(t),n.y=Ai(t)):(n.x=o?s+=e(n,o):0,n.y=0,o=n)});var l=Ni(c),f=Li(c),h=l.x-e(l,f)/2,g=f.x+e(f,l)/2;return pi(c,u?function(n){n.x=(n.x-c.x)*r[0],n.y=(c.y-n.y)*r[1]}:function(n){n.x=(n.x-h)/(g-h)*r[0],n.y=(1-(c.y?n.y/c.y:1))*r[1]}),a}var t=Xo.layout.hierarchy().sort(null).value(null),e=ai,r=[1,1],u=!1;return n.separation=function(t){return arguments.length?(e=t,n):e},n.size=function(t){return arguments.length?(u=null==(r=t),n):u?null:r},n.nodeSize=function(t){return arguments.length?(u=null!=(r=t),n):u?r:null},Vu(n,t)},Xo.layout.treemap=function(){function n(n,t){for(var e,r,u=-1,i=n.length;++u<i;)r=(e=n[u]).value*(0>t?0:t),e.area=isNaN(r)||0>=r?0:r}function t(e){var i=e.children;if(i&&i.length){var o,a,c,s=f(e),l=[],h=i.slice(),p=1/0,v="slice"===g?s.dx:"dice"===g?s.dy:"slice-dice"===g?1&e.depth?s.dy:s.dx:Math.min(s.dx,s.dy);for(n(h,s.dx*s.dy/e.value),l.area=0;(c=h.length)>0;)l.push(o=h[c-1]),l.area+=o.area,"squarify"!==g||(a=r(l,v))<=p?(h.pop(),p=a):(l.area-=l.pop().area,u(l,v,s,!1),v=Math.min(s.dx,s.dy),l.length=l.area=0,p=1/0);l.length&&(u(l,v,s,!0),l.length=l.area=0),i.forEach(t)}}function e(t){var r=t.children;if(r&&r.length){var i,o=f(t),a=r.slice(),c=[];for(n(a,o.dx*o.dy/t.value),c.area=0;i=a.pop();)c.push(i),c.area+=i.area,null!=i.z&&(u(c,i.z?o.dx:o.dy,o,!a.length),c.length=c.area=0);r.forEach(e)}}function r(n,t){for(var e,r=n.area,u=0,i=1/0,o=-1,a=n.length;++o<a;)(e=n[o].area)&&(i>e&&(i=e),e>u&&(u=e));return r*=r,t*=t,r?Math.max(t*u*p/r,r/(t*i*p)):1/0}function u(n,t,e,r){var u,i=-1,o=n.length,a=e.x,s=e.y,l=t?c(n.area/t):0;if(t==e.dx){for((r||l>e.dy)&&(l=e.dy);++i<o;)u=n[i],u.x=a,u.y=s,u.dy=l,a+=u.dx=Math.min(e.x+e.dx-a,l?c(u.area/l):0);u.z=!0,u.dx+=e.x+e.dx-a,e.y+=l,e.dy-=l}else{for((r||l>e.dx)&&(l=e.dx);++i<o;)u=n[i],u.x=a,u.y=s,u.dx=l,s+=u.dy=Math.min(e.y+e.dy-s,l?c(u.area/l):0);u.z=!1,u.dy+=e.y+e.dy-s,e.x+=l,e.dx-=l}}function i(r){var u=o||a(r),i=u[0];return i.x=0,i.y=0,i.dx=s[0],i.dy=s[1],o&&a.revalue(i),n([i],i.dx*i.dy/i.value),(o?e:t)(i),h&&(o=u),u}var o,a=Xo.layout.hierarchy(),c=Math.round,s=[1,1],l=null,f=Ti,h=!1,g="squarify",p=.5*(1+Math.sqrt(5));return i.size=function(n){return arguments.length?(s=n,i):s},i.padding=function(n){function t(t){var e=n.call(i,t,t.depth);return null==e?Ti(t):qi(t,"number"==typeof e?[e,e,e,e]:e)}function e(t){return qi(t,n)}if(!arguments.length)return l;var r;return f=null==(l=n)?Ti:"function"==(r=typeof n)?t:"number"===r?(n=[n,n,n,n],e):e,i},i.round=function(n){return arguments.length?(c=n?Math.round:Number,i):c!=Number},i.sticky=function(n){return arguments.length?(h=n,o=null,i):h},i.ratio=function(n){return arguments.length?(p=n,i):p},i.mode=function(n){return arguments.length?(g=n+"",i):g},Vu(i,a)},Xo.random={normal:function(n,t){var e=arguments.length;return 2>e&&(t=1),1>e&&(n=0),function(){var e,r,u;do e=2*Math.random()-1,r=2*Math.random()-1,u=e*e+r*r;while(!u||u>1);return n+t*e*Math.sqrt(-2*Math.log(u)/u)}},logNormal:function(){var n=Xo.random.normal.apply(Xo,arguments);return function(){return Math.exp(n())}},bates:function(n){var t=Xo.random.irwinHall(n);return function(){return t()/n}},irwinHall:function(n){return function(){for(var t=0,e=0;n>e;e++)t+=Math.random();return t}}},Xo.scale={};var ls={floor:bt,ceil:bt};Xo.scale.linear=function(){return Hi([0,1],[0,1],fu,!1)};var fs={s:1,g:1,p:1,r:1,e:1};Xo.scale.log=function(){return $i(Xo.scale.linear().domain([0,1]),10,!0,[1,10])};var hs=Xo.format(".0e"),gs={floor:function(n){return-Math.ceil(-n)},ceil:function(n){return-Math.floor(-n)}};Xo.scale.pow=function(){return Bi(Xo.scale.linear(),1,[0,1])},Xo.scale.sqrt=function(){return Xo.scale.pow().exponent(.5)},Xo.scale.ordinal=function(){return Ji([],{t:"range",a:[[]]})},Xo.scale.category10=function(){return Xo.scale.ordinal().range(ps)},Xo.scale.category20=function(){return Xo.scale.ordinal().range(vs)},Xo.scale.category20b=function(){return Xo.scale.ordinal().range(ds)},Xo.scale.category20c=function(){return Xo.scale.ordinal().range(ms)};var ps=[2062260,16744206,2924588,14034728,9725885,9197131,14907330,8355711,12369186,1556175].map(ht),vs=[2062260,11454440,16744206,16759672,2924588,10018698,14034728,16750742,9725885,12955861,9197131,12885140,14907330,16234194,8355711,13092807,12369186,14408589,1556175,10410725].map(ht),ds=[3750777,5395619,7040719,10264286,6519097,9216594,11915115,13556636,9202993,12426809,15186514,15190932,8666169,11356490,14049643,15177372,8077683,10834324,13528509,14589654].map(ht),ms=[3244733,7057110,10406625,13032431,15095053,16616764,16625259,16634018,3253076,7652470,10607003,13101504,7695281,10394312,12369372,14342891,6513507,9868950,12434877,14277081].map(ht);Xo.scale.quantile=function(){return Gi([],[])},Xo.scale.quantize=function(){return Ki(0,1,[0,1])},Xo.scale.threshold=function(){return Qi([.5],[0,1])},Xo.scale.identity=function(){return no([0,1])},Xo.svg={},Xo.svg.arc=function(){function n(){var n=t.apply(this,arguments),i=e.apply(this,arguments),o=r.apply(this,arguments)+ys,a=u.apply(this,arguments)+ys,c=(o>a&&(c=o,o=a,a=c),a-o),s=Sa>c?"0":"1",l=Math.cos(o),f=Math.sin(o),h=Math.cos(a),g=Math.sin(a);return c>=xs?n?"M0,"+i+"A"+i+","+i+" 0 1,1 0,"+-i+"A"+i+","+i+" 0 1,1 0,"+i+"M0,"+n+"A"+n+","+n+" 0 1,0 0,"+-n+"A"+n+","+n+" 0 1,0 0,"+n+"Z":"M0,"+i+"A"+i+","+i+" 0 1,1 0,"+-i+"A"+i+","+i+" 0 1,1 0,"+i+"Z":n?"M"+i*l+","+i*f+"A"+i+","+i+" 0 "+s+",1 "+i*h+","+i*g+"L"+n*h+","+n*g+"A"+n+","+n+" 0 "+s+",0 "+n*l+","+n*f+"Z":"M"+i*l+","+i*f+"A"+i+","+i+" 0 "+s+",1 "+i*h+","+i*g+"L0,0"+"Z"}var t=to,e=eo,r=ro,u=uo;return n.innerRadius=function(e){return arguments.length?(t=_t(e),n):t},n.outerRadius=function(t){return arguments.length?(e=_t(t),n):e},n.startAngle=function(t){return arguments.length?(r=_t(t),n):r},n.endAngle=function(t){return arguments.length?(u=_t(t),n):u},n.centroid=function(){var n=(t.apply(this,arguments)+e.apply(this,arguments))/2,i=(r.apply(this,arguments)+u.apply(this,arguments))/2+ys;return[Math.cos(i)*n,Math.sin(i)*n]},n};var ys=-Ea,xs=ka-Aa;Xo.svg.line=function(){return io(bt)};var Ms=Xo.map({linear:oo,"linear-closed":ao,step:co,"step-before":so,"step-after":lo,basis:mo,"basis-open":yo,"basis-closed":xo,bundle:Mo,cardinal:go,"cardinal-open":fo,"cardinal-closed":ho,monotone:Eo});Ms.forEach(function(n,t){t.key=n,t.closed=/-closed$/.test(n)});var _s=[0,2/3,1/3,0],bs=[0,1/3,2/3,0],ws=[0,1/6,2/3,1/6];Xo.svg.line.radial=function(){var n=io(Ao);return n.radius=n.x,delete n.x,n.angle=n.y,delete n.y,n},so.reverse=lo,lo.reverse=so,Xo.svg.area=function(){return Co(bt)},Xo.svg.area.radial=function(){var n=Co(Ao);return n.radius=n.x,delete n.x,n.innerRadius=n.x0,delete n.x0,n.outerRadius=n.x1,delete n.x1,n.angle=n.y,delete n.y,n.startAngle=n.y0,delete n.y0,n.endAngle=n.y1,delete n.y1,n},Xo.svg.chord=function(){function n(n,a){var c=t(this,i,n,a),s=t(this,o,n,a);return"M"+c.p0+r(c.r,c.p1,c.a1-c.a0)+(e(c,s)?u(c.r,c.p1,c.r,c.p0):u(c.r,c.p1,s.r,s.p0)+r(s.r,s.p1,s.a1-s.a0)+u(s.r,s.p1,c.r,c.p0))+"Z"}function t(n,t,e,r){var u=t.call(n,e,r),i=a.call(n,u,r),o=c.call(n,u,r)+ys,l=s.call(n,u,r)+ys;return{r:i,a0:o,a1:l,p0:[i*Math.cos(o),i*Math.sin(o)],p1:[i*Math.cos(l),i*Math.sin(l)]}}function e(n,t){return n.a0==t.a0&&n.a1==t.a1}function r(n,t,e){return"A"+n+","+n+" 0 "+ +(e>Sa)+",1 "+t}function u(n,t,e,r){return"Q 0,0 "+r}var i=hr,o=gr,a=No,c=ro,s=uo;return n.radius=function(t){return arguments.length?(a=_t(t),n):a},n.source=function(t){return arguments.length?(i=_t(t),n):i},n.target=function(t){return arguments.length?(o=_t(t),n):o},n.startAngle=function(t){return arguments.length?(c=_t(t),n):c},n.endAngle=function(t){return arguments.length?(s=_t(t),n):s},n},Xo.svg.diagonal=function(){function n(n,u){var i=t.call(this,n,u),o=e.call(this,n,u),a=(i.y+o.y)/2,c=[i,{x:i.x,y:a},{x:o.x,y:a},o];return c=c.map(r),"M"+c[0]+"C"+c[1]+" "+c[2]+" "+c[3]}var t=hr,e=gr,r=Lo;return n.source=function(e){return arguments.length?(t=_t(e),n):t},n.target=function(t){return arguments.length?(e=_t(t),n):e},n.projection=function(t){return arguments.length?(r=t,n):r},n},Xo.svg.diagonal.radial=function(){var n=Xo.svg.diagonal(),t=Lo,e=n.projection;return n.projection=function(n){return arguments.length?e(To(t=n)):t},n},Xo.svg.symbol=function(){function n(n,r){return(Ss.get(t.call(this,n,r))||Ro)(e.call(this,n,r))}var t=zo,e=qo;return n.type=function(e){return arguments.length?(t=_t(e),n):t},n.size=function(t){return arguments.length?(e=_t(t),n):e},n};var Ss=Xo.map({circle:Ro,cross:function(n){var t=Math.sqrt(n/5)/2;return"M"+-3*t+","+-t+"H"+-t+"V"+-3*t+"H"+t+"V"+-t+"H"+3*t+"V"+t+"H"+t+"V"+3*t+"H"+-t+"V"+t+"H"+-3*t+"Z"},diamond:function(n){var t=Math.sqrt(n/(2*Cs)),e=t*Cs;return"M0,"+-t+"L"+e+",0"+" 0,"+t+" "+-e+",0"+"Z"},square:function(n){var t=Math.sqrt(n)/2;return"M"+-t+","+-t+"L"+t+","+-t+" "+t+","+t+" "+-t+","+t+"Z"},"triangle-down":function(n){var t=Math.sqrt(n/As),e=t*As/2;return"M0,"+e+"L"+t+","+-e+" "+-t+","+-e+"Z"},"triangle-up":function(n){var t=Math.sqrt(n/As),e=t*As/2;return"M0,"+-e+"L"+t+","+e+" "+-t+","+e+"Z"}});Xo.svg.symbolTypes=Ss.keys();var ks,Es,As=Math.sqrt(3),Cs=Math.tan(30*Na),Ns=[],Ls=0;Ns.call=da.call,Ns.empty=da.empty,Ns.node=da.node,Ns.size=da.size,Xo.transition=function(n){return arguments.length?ks?n.transition():n:xa.transition()},Xo.transition.prototype=Ns,Ns.select=function(n){var t,e,r,u=this.id,i=[];n=M(n);for(var o=-1,a=this.length;++o<a;){i.push(t=[]);for(var c=this[o],s=-1,l=c.length;++s<l;)(r=c[s])&&(e=n.call(r,r.__data__,s,o))?("__data__"in r&&(e.__data__=r.__data__),jo(e,s,u,r.__transition__[u]),t.push(e)):t.push(null)}return Do(i,u)},Ns.selectAll=function(n){var t,e,r,u,i,o=this.id,a=[];n=_(n);for(var c=-1,s=this.length;++c<s;)for(var l=this[c],f=-1,h=l.length;++f<h;)if(r=l[f]){i=r.__transition__[o],e=n.call(r,r.__data__,f,c),a.push(t=[]);for(var g=-1,p=e.length;++g<p;)(u=e[g])&&jo(u,g,o,i),t.push(u)}return Do(a,o)},Ns.filter=function(n){var t,e,r,u=[];"function"!=typeof n&&(n=q(n));for(var i=0,o=this.length;o>i;i++){u.push(t=[]);for(var e=this[i],a=0,c=e.length;c>a;a++)(r=e[a])&&n.call(r,r.__data__,a,i)&&t.push(r)}return Do(u,this.id)},Ns.tween=function(n,t){var e=this.id;return arguments.length<2?this.node().__transition__[e].tween.get(n):R(this,null==t?function(t){t.__transition__[e].tween.remove(n)}:function(r){r.__transition__[e].tween.set(n,t)})},Ns.attr=function(n,t){function e(){this.removeAttribute(a)}function r(){this.removeAttributeNS(a.space,a.local)}function u(n){return null==n?e:(n+="",function(){var t,e=this.getAttribute(a);return e!==n&&(t=o(e,n),function(n){this.setAttribute(a,t(n))})})}function i(n){return null==n?r:(n+="",function(){var t,e=this.getAttributeNS(a.space,a.local);return e!==n&&(t=o(e,n),function(n){this.setAttributeNS(a.space,a.local,t(n))})})}if(arguments.length<2){for(t in n)this.attr(t,n[t]);return this}var o="transform"==n?Ru:fu,a=Xo.ns.qualify(n);return Po(this,"attr."+n,t,a.local?i:u)},Ns.attrTween=function(n,t){function e(n,e){var r=t.call(this,n,e,this.getAttribute(u));return r&&function(n){this.setAttribute(u,r(n))}}function r(n,e){var r=t.call(this,n,e,this.getAttributeNS(u.space,u.local));return r&&function(n){this.setAttributeNS(u.space,u.local,r(n))}}var u=Xo.ns.qualify(n);return this.tween("attr."+n,u.local?r:e)},Ns.style=function(n,t,e){function r(){this.style.removeProperty(n)}function u(t){return null==t?r:(t+="",function(){var r,u=Go.getComputedStyle(this,null).getPropertyValue(n);return u!==t&&(r=fu(u,t),function(t){this.style.setProperty(n,r(t),e)})})}var i=arguments.length;if(3>i){if("string"!=typeof n){2>i&&(t="");for(e in n)this.style(e,n[e],t);return this}e=""}return Po(this,"style."+n,t,u)},Ns.styleTween=function(n,t,e){function r(r,u){var i=t.call(this,r,u,Go.getComputedStyle(this,null).getPropertyValue(n));return i&&function(t){this.style.setProperty(n,i(t),e)}}return arguments.length<3&&(e=""),this.tween("style."+n,r)},Ns.text=function(n){return Po(this,"text",n,Uo)},Ns.remove=function(){return this.each("end.transition",function(){var n;this.__transition__.count<2&&(n=this.parentNode)&&n.removeChild(this)})},Ns.ease=function(n){var t=this.id;return arguments.length<1?this.node().__transition__[t].ease:("function"!=typeof n&&(n=Xo.ease.apply(Xo,arguments)),R(this,function(e){e.__transition__[t].ease=n}))},Ns.delay=function(n){var t=this.id;return R(this,"function"==typeof n?function(e,r,u){e.__transition__[t].delay=+n.call(e,e.__data__,r,u)}:(n=+n,function(e){e.__transition__[t].delay=n}))},Ns.duration=function(n){var t=this.id;return R(this,"function"==typeof n?function(e,r,u){e.__transition__[t].duration=Math.max(1,n.call(e,e.__data__,r,u))}:(n=Math.max(1,n),function(e){e.__transition__[t].duration=n}))},Ns.each=function(n,t){var e=this.id;if(arguments.length<2){var r=Es,u=ks;ks=e,R(this,function(t,r,u){Es=t.__transition__[e],n.call(t,t.__data__,r,u)}),Es=r,ks=u}else R(this,function(r){var u=r.__transition__[e];(u.event||(u.event=Xo.dispatch("start","end"))).on(n,t)});return this},Ns.transition=function(){for(var n,t,e,r,u=this.id,i=++Ls,o=[],a=0,c=this.length;c>a;a++){o.push(n=[]);for(var t=this[a],s=0,l=t.length;l>s;s++)(e=t[s])&&(r=Object.create(e.__transition__[u]),r.delay+=r.duration,jo(e,s,i,r)),n.push(e)}return Do(o,i)},Xo.svg.axis=function(){function n(n){n.each(function(){var n,s=Xo.select(this),l=this.__chart__||e,f=this.__chart__=e.copy(),h=null==c?f.ticks?f.ticks.apply(f,a):f.domain():c,g=null==t?f.tickFormat?f.tickFormat.apply(f,a):bt:t,p=s.selectAll(".tick").data(h,f),v=p.enter().insert("g",".domain").attr("class","tick").style("opacity",Aa),d=Xo.transition(p.exit()).style("opacity",Aa).remove(),m=Xo.transition(p).style("opacity",1),y=Ri(f),x=s.selectAll(".domain").data([0]),M=(x.enter().append("path").attr("class","domain"),Xo.transition(x));v.append("line"),v.append("text");var _=v.select("line"),b=m.select("line"),w=p.select("text").text(g),S=v.select("text"),k=m.select("text");switch(r){case"bottom":n=Ho,_.attr("y2",u),S.attr("y",Math.max(u,0)+o),b.attr("x2",0).attr("y2",u),k.attr("x",0).attr("y",Math.max(u,0)+o),w.attr("dy",".71em").style("text-anchor","middle"),M.attr("d","M"+y[0]+","+i+"V0H"+y[1]+"V"+i);break;case"top":n=Ho,_.attr("y2",-u),S.attr("y",-(Math.max(u,0)+o)),b.attr("x2",0).attr("y2",-u),k.attr("x",0).attr("y",-(Math.max(u,0)+o)),w.attr("dy","0em").style("text-anchor","middle"),M.attr("d","M"+y[0]+","+-i+"V0H"+y[1]+"V"+-i);break;case"left":n=Fo,_.attr("x2",-u),S.attr("x",-(Math.max(u,0)+o)),b.attr("x2",-u).attr("y2",0),k.attr("x",-(Math.max(u,0)+o)).attr("y",0),w.attr("dy",".32em").style("text-anchor","end"),M.attr("d","M"+-i+","+y[0]+"H0V"+y[1]+"H"+-i);break;case"right":n=Fo,_.attr("x2",u),S.attr("x",Math.max(u,0)+o),b.attr("x2",u).attr("y2",0),k.attr("x",Math.max(u,0)+o).attr("y",0),w.attr("dy",".32em").style("text-anchor","start"),M.attr("d","M"+i+","+y[0]+"H0V"+y[1]+"H"+i)}if(f.rangeBand){var E=f,A=E.rangeBand()/2;l=f=function(n){return E(n)+A}}else l.rangeBand?l=f:d.call(n,f);v.call(n,l),m.call(n,f)})}var t,e=Xo.scale.linear(),r=Ts,u=6,i=6,o=3,a=[10],c=null;return n.scale=function(t){return arguments.length?(e=t,n):e},n.orient=function(t){return arguments.length?(r=t in qs?t+"":Ts,n):r},n.ticks=function(){return arguments.length?(a=arguments,n):a},n.tickValues=function(t){return arguments.length?(c=t,n):c},n.tickFormat=function(e){return arguments.length?(t=e,n):t},n.tickSize=function(t){var e=arguments.length;return e?(u=+t,i=+arguments[e-1],n):u},n.innerTickSize=function(t){return arguments.length?(u=+t,n):u},n.outerTickSize=function(t){return arguments.length?(i=+t,n):i},n.tickPadding=function(t){return arguments.length?(o=+t,n):o},n.tickSubdivide=function(){return arguments.length&&n},n};var Ts="bottom",qs={top:1,right:1,bottom:1,left:1};Xo.svg.brush=function(){function n(i){i.each(function(){var i=Xo.select(this).style("pointer-events","all").style("-webkit-tap-highlight-color","rgba(0,0,0,0)").on("mousedown.brush",u).on("touchstart.brush",u),o=i.selectAll(".background").data([0]);o.enter().append("rect").attr("class","background").style("visibility","hidden").style("cursor","crosshair"),i.selectAll(".extent").data([0]).enter().append("rect").attr("class","extent").style("cursor","move");var a=i.selectAll(".resize").data(p,bt);a.exit().remove(),a.enter().append("g").attr("class",function(n){return"resize "+n}).style("cursor",function(n){return zs[n]}).append("rect").attr("x",function(n){return/[ew]$/.test(n)?-3:null}).attr("y",function(n){return/^[ns]/.test(n)?-3:null}).attr("width",6).attr("height",6).style("visibility","hidden"),a.style("display",n.empty()?"none":null);var l,f=Xo.transition(i),h=Xo.transition(o);c&&(l=Ri(c),h.attr("x",l[0]).attr("width",l[1]-l[0]),e(f)),s&&(l=Ri(s),h.attr("y",l[0]).attr("height",l[1]-l[0]),r(f)),t(f)})}function t(n){n.selectAll(".resize").attr("transform",function(n){return"translate("+l[+/e$/.test(n)]+","+f[+/^s/.test(n)]+")"})}function e(n){n.select(".extent").attr("x",l[0]),n.selectAll(".extent,.n>rect,.s>rect").attr("width",l[1]-l[0])}function r(n){n.select(".extent").attr("y",f[0]),n.selectAll(".extent,.e>rect,.w>rect").attr("height",f[1]-f[0])}function u(){function u(){32==Xo.event.keyCode&&(C||(x=null,L[0]-=l[1],L[1]-=f[1],C=2),d())}function p(){32==Xo.event.keyCode&&2==C&&(L[0]+=l[1],L[1]+=f[1],C=0,d())}function v(){var n=Xo.mouse(_),u=!1;M&&(n[0]+=M[0],n[1]+=M[1]),C||(Xo.event.altKey?(x||(x=[(l[0]+l[1])/2,(f[0]+f[1])/2]),L[0]=l[+(n[0]<x[0])],L[1]=f[+(n[1]<x[1])]):x=null),E&&m(n,c,0)&&(e(S),u=!0),A&&m(n,s,1)&&(r(S),u=!0),u&&(t(S),w({type:"brush",mode:C?"move":"resize"}))}function m(n,t,e){var r,u,a=Ri(t),c=a[0],s=a[1],p=L[e],v=e?f:l,d=v[1]-v[0];return C&&(c-=p,s-=d+p),r=(e?g:h)?Math.max(c,Math.min(s,n[e])):n[e],C?u=(r+=p)+d:(x&&(p=Math.max(c,Math.min(s,2*x[e]-r))),r>p?(u=r,r=p):u=p),v[0]!=r||v[1]!=u?(e?o=null:i=null,v[0]=r,v[1]=u,!0):void 0}function y(){v(),S.style("pointer-events","all").selectAll(".resize").style("display",n.empty()?"none":null),Xo.select("body").style("cursor",null),T.on("mousemove.brush",null).on("mouseup.brush",null).on("touchmove.brush",null).on("touchend.brush",null).on("keydown.brush",null).on("keyup.brush",null),N(),w({type:"brushend"})}var x,M,_=this,b=Xo.select(Xo.event.target),w=a.of(_,arguments),S=Xo.select(_),k=b.datum(),E=!/^(n|s)$/.test(k)&&c,A=!/^(e|w)$/.test(k)&&s,C=b.classed("extent"),N=O(),L=Xo.mouse(_),T=Xo.select(Go).on("keydown.brush",u).on("keyup.brush",p);if(Xo.event.changedTouches?T.on("touchmove.brush",v).on("touchend.brush",y):T.on("mousemove.brush",v).on("mouseup.brush",y),S.interrupt().selectAll("*").interrupt(),C)L[0]=l[0]-L[0],L[1]=f[0]-L[1];else if(k){var q=+/w$/.test(k),z=+/^n/.test(k);M=[l[1-q]-L[0],f[1-z]-L[1]],L[0]=l[q],L[1]=f[z]}else Xo.event.altKey&&(x=L.slice());S.style("pointer-events","none").selectAll(".resize").style("display",null),Xo.select("body").style("cursor",b.style("cursor")),w({type:"brushstart"}),v()}var i,o,a=y(n,"brushstart","brush","brushend"),c=null,s=null,l=[0,0],f=[0,0],h=!0,g=!0,p=Rs[0];return n.event=function(n){n.each(function(){var n=a.of(this,arguments),t={x:l,y:f,i:i,j:o},e=this.__chart__||t;this.__chart__=t,ks?Xo.select(this).transition().each("start.brush",function(){i=e.i,o=e.j,l=e.x,f=e.y,n({type:"brushstart"})}).tween("brush:brush",function(){var e=hu(l,t.x),r=hu(f,t.y);return i=o=null,function(u){l=t.x=e(u),f=t.y=r(u),n({type:"brush",mode:"resize"})}}).each("end.brush",function(){i=t.i,o=t.j,n({type:"brush",mode:"resize"}),n({type:"brushend"})}):(n({type:"brushstart"}),n({type:"brush",mode:"resize"}),n({type:"brushend"}))})},n.x=function(t){return arguments.length?(c=t,p=Rs[!c<<1|!s],n):c},n.y=function(t){return arguments.length?(s=t,p=Rs[!c<<1|!s],n):s},n.clamp=function(t){return arguments.length?(c&&s?(h=!!t[0],g=!!t[1]):c?h=!!t:s&&(g=!!t),n):c&&s?[h,g]:c?h:s?g:null},n.extent=function(t){var e,r,u,a,h;return arguments.length?(c&&(e=t[0],r=t[1],s&&(e=e[0],r=r[0]),i=[e,r],c.invert&&(e=c(e),r=c(r)),e>r&&(h=e,e=r,r=h),(e!=l[0]||r!=l[1])&&(l=[e,r])),s&&(u=t[0],a=t[1],c&&(u=u[1],a=a[1]),o=[u,a],s.invert&&(u=s(u),a=s(a)),u>a&&(h=u,u=a,a=h),(u!=f[0]||a!=f[1])&&(f=[u,a])),n):(c&&(i?(e=i[0],r=i[1]):(e=l[0],r=l[1],c.invert&&(e=c.invert(e),r=c.invert(r)),e>r&&(h=e,e=r,r=h))),s&&(o?(u=o[0],a=o[1]):(u=f[0],a=f[1],s.invert&&(u=s.invert(u),a=s.invert(a)),u>a&&(h=u,u=a,a=h))),c&&s?[[e,u],[r,a]]:c?[e,r]:s&&[u,a])},n.clear=function(){return n.empty()||(l=[0,0],f=[0,0],i=o=null),n},n.empty=function(){return!!c&&l[0]==l[1]||!!s&&f[0]==f[1]},Xo.rebind(n,a,"on")};var zs={n:"ns-resize",e:"ew-resize",s:"ns-resize",w:"ew-resize",nw:"nwse-resize",ne:"nesw-resize",se:"nwse-resize",sw:"nesw-resize"},Rs=[["n","e","s","w","nw","ne","se","sw"],["e","w"],["n","s"],[]],Ds=tc.format=ac.timeFormat,Ps=Ds.utc,Us=Ps("%Y-%m-%dT%H:%M:%S.%LZ");Ds.iso=Date.prototype.toISOString&&+new Date("2000-01-01T00:00:00.000Z")?Oo:Us,Oo.parse=function(n){var t=new Date(n);return isNaN(t)?null:t},Oo.toString=Us.toString,tc.second=Rt(function(n){return new ec(1e3*Math.floor(n/1e3))},function(n,t){n.setTime(n.getTime()+1e3*Math.floor(t))},function(n){return n.getSeconds()}),tc.seconds=tc.second.range,tc.seconds.utc=tc.second.utc.range,tc.minute=Rt(function(n){return new ec(6e4*Math.floor(n/6e4))},function(n,t){n.setTime(n.getTime()+6e4*Math.floor(t))},function(n){return n.getMinutes()}),tc.minutes=tc.minute.range,tc.minutes.utc=tc.minute.utc.range,tc.hour=Rt(function(n){var t=n.getTimezoneOffset()/60;return new ec(36e5*(Math.floor(n/36e5-t)+t))},function(n,t){n.setTime(n.getTime()+36e5*Math.floor(t))},function(n){return n.getHours()}),tc.hours=tc.hour.range,tc.hours.utc=tc.hour.utc.range,tc.month=Rt(function(n){return n=tc.day(n),n.setDate(1),n},function(n,t){n.setMonth(n.getMonth()+t)},function(n){return n.getMonth()}),tc.months=tc.month.range,tc.months.utc=tc.month.utc.range;var js=[1e3,5e3,15e3,3e4,6e4,3e5,9e5,18e5,36e5,108e5,216e5,432e5,864e5,1728e5,6048e5,2592e6,7776e6,31536e6],Hs=[[tc.second,1],[tc.second,5],[tc.second,15],[tc.second,30],[tc.minute,1],[tc.minute,5],[tc.minute,15],[tc.minute,30],[tc.hour,1],[tc.hour,3],[tc.hour,6],[tc.hour,12],[tc.day,1],[tc.day,2],[tc.week,1],[tc.month,1],[tc.month,3],[tc.year,1]],Fs=Ds.multi([[".%L",function(n){return n.getMilliseconds()}],[":%S",function(n){return n.getSeconds()}],["%I:%M",function(n){return n.getMinutes()}],["%I %p",function(n){return n.getHours()}],["%a %d",function(n){return n.getDay()&&1!=n.getDate()}],["%b %d",function(n){return 1!=n.getDate()}],["%B",function(n){return n.getMonth()}],["%Y",be]]),Os={range:function(n,t,e){return Xo.range(Math.ceil(n/e)*e,+t,e).map(Io)},floor:bt,ceil:bt};Hs.year=tc.year,tc.scale=function(){return Yo(Xo.scale.linear(),Hs,Fs)};var Ys=Hs.map(function(n){return[n[0].utc,n[1]]}),Is=Ps.multi([[".%L",function(n){return n.getUTCMilliseconds()}],[":%S",function(n){return n.getUTCSeconds()}],["%I:%M",function(n){return n.getUTCMinutes()}],["%I %p",function(n){return n.getUTCHours()}],["%a %d",function(n){return n.getUTCDay()&&1!=n.getUTCDate()}],["%b %d",function(n){return 1!=n.getUTCDate()}],["%B",function(n){return n.getUTCMonth()}],["%Y",be]]);Ys.year=tc.year.utc,tc.scale.utc=function(){return Yo(Xo.scale.linear(),Ys,Is)},Xo.text=wt(function(n){return n.responseText}),Xo.json=function(n,t){return St(n,"application/json",Zo,t)},Xo.html=function(n,t){return St(n,"text/html",Vo,t)},Xo.xml=wt(function(n){return n.responseXML}),"function"==typeof define&&define.amd?define(Xo):"object"==typeof module&&module.exports?module.exports=Xo:this.d3=Xo}();'use strict';(function(window){window.define=undefined;}).call(this,this);'use strict';tr.exportTo('tr.ui.b',function(){const DataSeriesEnableChangeEventType='data-series-enabled-change';const THIS_DOC=document._currentScript.ownerDocument;const svgNS='http://www.w3.org/2000/svg';const ColorScheme=tr.b.ColorScheme;function getColorOfKey(key,selected){let id=ColorScheme.getColorIdForGeneralPurposeString(key);if(selected){id+=ColorScheme.properties.brightenedOffsets[0];}
+return ColorScheme.colorsAsStrings[id];}
+function getSVGTextSize(parentNode,text,opt_callback,opt_this){const textNode=document.createElementNS('http://www.w3.org/2000/svg','text');textNode.setAttributeNS(null,'x',0);textNode.setAttributeNS(null,'y',0);textNode.setAttributeNS(null,'fill','black');textNode.appendChild(document.createTextNode(text));parentNode.appendChild(textNode);if(opt_callback){opt_callback.call(opt_this||parentNode,textNode);}
+const width=textNode.getComputedTextLength();const height=textNode.getBBox().height;parentNode.removeChild(textNode);return{width,height};}
+function DataSeries(key){this.key_=key;this.target_=undefined;this.title_='';this.optional_=false;this.enabled_=true;this.color_=getColorOfKey(key,false);this.highlightedColor_=getColorOfKey(key,true);}
+DataSeries.prototype={get key(){return this.key_;},get title(){return this.title_;},set title(t){this.title_=t;},get color(){return this.color_;},set color(c){this.color_=c;},get highlightedColor(){return this.highlightedColor_;},set highlightedColor(c){this.highlightedColor_=c;},get optional(){return this.optional_;},set optional(optional){this.optional_=optional;},get enabled(){return this.enabled_;},set enabled(enabled){if(!this.optional&&!enabled){this.optional=true;}
+this.enabled_=enabled;},get target(){return this.target_;},set target(t){this.target_=t;}};const ChartBase=tr.ui.b.define('svg',undefined,svgNS);ChartBase.prototype={__proto__:HTMLUnknownElement.prototype,getDataSeries(key){if(!this.seriesByKey_.has(key)){this.seriesByKey_.set(key,new DataSeries(key));}
+return this.seriesByKey_.get(key);},decorate(){Polymer.dom(this).classList.add('chart-base');this.setAttribute('style','cursor: default; user-select: none;');this.chartTitle_=undefined;this.seriesByKey_=new Map();this.graphWidth_=undefined;this.graphHeight_=undefined;this.margin={top:0,right:0,bottom:0,left:0,};this.hideLegend_=false;this.showTitleInLegend_=false;this.titleHeight_='16pt';const template=Polymer.dom(THIS_DOC).querySelector('#chart-base-template');const svgEl=Polymer.dom(template.content).querySelector('svg');for(let i=0;i<Polymer.dom(svgEl).children.length;i++){Polymer.dom(this).appendChild(Polymer.dom(svgEl.children[i]).cloneNode(true));}
+this.addEventListener(DataSeriesEnableChangeEventType,this.onDataSeriesEnableChange_.bind(this));},get hideLegend(){return this.hideLegend_;},set hideLegend(h){this.hideLegend_=h;this.updateContents_();},get showTitleInLegend(){return this.showTitleInLegend_;},set showTitleInLegend(s){this.showTitleInLegend_=s;this.updateContents_();},isSeriesEnabled(key){return this.getDataSeries(key).enabled;},onDataSeriesEnableChange_(event){this.getDataSeries(event.key).enabled=event.enabled;this.updateContents_();},get chartTitle(){return this.chartTitle_;},set chartTitle(chartTitle){this.chartTitle_=chartTitle;this.updateContents_();},get chartAreaElement(){return Polymer.dom(this).querySelector('#chart-area');},get graphWidth(){if(this.graphWidth_===undefined)return this.defaultGraphWidth;return this.graphWidth_;},set graphWidth(width){this.graphWidth_=width;this.updateContents_();},get defaultGraphWidth(){return 0;},get graphHeight(){if(this.graphHeight_===undefined)return this.defaultGraphHeight;return this.graphHeight_;},set graphHeight(height){this.graphHeight_=height;this.updateContents_();},get titleHeight(){return this.titleHeight_;},set titleHeight(height){this.titleHeight_=height;this.updateContents_();},get defaultGraphHeight(){return 0;},get totalWidth(){return this.margin.left+this.graphWidth+this.margin.right;},get totalHeight(){return this.margin.top+this.graphHeight+this.margin.bottom;},updateMargins_(){const legendSize=this.computeLegendSize_();this.margin.right=Math.max(this.margin.right,legendSize.width);this.margin.bottom=Math.max(this.margin.bottom,legendSize.height-this.graphHeight);if(this.chartTitle_){const titleSize=getSVGTextSize(this,this.chartTitle_,textNode=>{textNode.style.fontSize='16pt';});this.margin.top=Math.max(this.margin.top,titleSize.height+15);const horizontalOverhangPx=(titleSize.width-this.graphWidth)/2;this.margin.left=Math.max(this.margin.left,horizontalOverhangPx);this.margin.right=Math.max(this.margin.right,horizontalOverhangPx);}},computeLegendSize_(){let width=0;let height=0;if(this.hideLegend)return{width,height};let series=[...this.seriesByKey_.values()];if(this.showTitleInLegend){series=series.filter(series=>series.title!=='');}
+for(const seriesEntry of series){const legendText=this.showTitleInLegend?seriesEntry.title:seriesEntry.key;const textSize=getSVGTextSize(this,legendText);width=Math.max(width,textSize.width+30);height+=textSize.height;}
+return{width,height};},updateDimensions_(){const thisSel=d3.select(this);thisSel.attr('width',this.totalWidth);thisSel.attr('height',this.totalHeight);d3.select(this.chartAreaElement).attr('transform','translate('+this.margin.left+', '+this.margin.top+')');},updateContents_(){this.updateMargins_();this.updateDimensions_();this.updateTitle_();this.updateLegend_();},updateTitle_(){const titleSel=d3.select(this.chartAreaElement).select('#title');if(!this.chartTitle_){titleSel.style('display','none');return;}
+titleSel.attr('transform','translate('+this.graphWidth*0.5+',-15)').style('display',undefined).style('text-anchor','middle').style('font-size',this.titleHeight).attr('class','title').attr('width',this.graphWidth).text(this.chartTitle_);},updateLegend_(){const chartAreaSel=d3.select(this.chartAreaElement);chartAreaSel.selectAll('.legend').remove();if(this.hideLegend)return;let series;let seriesText;if(this.showTitleInLegend){series=[...this.seriesByKey_.values()].filter(series=>series.title!=='').filter(series=>series.color!=='transparent').reverse();seriesText=series=>series.title;}else{series=[...this.seriesByKey_.values()].filter(series=>series.color!=='transparent').reverse();seriesText=series=>series.key;}
+const legendEntriesSel=chartAreaSel.selectAll('.legend').data(series);legendEntriesSel.enter().append('foreignObject').attr('class','legend').attr('x',this.graphWidth+2).attr('width',this.margin.right).attr('height',18).attr('transform',(series,i)=>'translate(0,'+i*18+')').append('xhtml:body').style('margin',0).append('tr-ui-b-chart-legend-key').property('color',series=>((this.currentHighlightedLegendKey===series.key)?series.highlightedColor:series.color)).property('width',this.margin.right).property('target',series=>series.target).property('title',series=>series.title).property('optional',series=>series.optional).property('enabled',series=>series.enabled).text(seriesText);legendEntriesSel.exit().remove();},get highlightedLegendKey(){return this.highlightedLegendKey_;},set highlightedLegendKey(highlightedLegendKey){this.highlightedLegendKey_=highlightedLegendKey;this.updateHighlight_();},get currentHighlightedLegendKey(){if(this.tempHighlightedLegendKey_){return this.tempHighlightedLegendKey_;}
+return this.highlightedLegendKey_;},pushTempHighlightedLegendKey(key){if(this.tempHighlightedLegendKey_){throw new Error('push cannot nest');}
+this.tempHighlightedLegendKey_=key;this.updateHighlight_();},popTempHighlightedLegendKey(key){if(this.tempHighlightedLegendKey_!==key){throw new Error('pop cannot happen');}
+this.tempHighlightedLegendKey_=undefined;this.updateHighlight_();},updateHighlight_(){const chartAreaSel=d3.select(this.chartAreaElement);const legendEntriesSel=chartAreaSel.selectAll('.legend');const getDataSeries=chart.getDataSeries.bind(chart);const currentHighlightedLegendKey=chart.currentHighlightedLegendKey;legendEntriesSel.each(function(key){const dataSeries=getDataSeries(key);if(key===currentHighlightedLegendKey){this.style.fill=dataSeries.highlightedColor;this.style.fontWeight='bold';}else{this.style.fill=dataSeries.color;this.style.fontWeight='';}});}};return{ChartBase,DataSeriesEnableChangeEventType,getColorOfKey,getSVGTextSize,};});'use strict';tr.exportTo('tr.ui.b',function(){const D3_Y_AXIS_WIDTH_PX=9;const D3_X_AXIS_HEIGHT_PX=23;function sanitizePower(x,defaultValue){if(!isNaN(x)&&isFinite(x)&&(x!==0))return x;return defaultValue;}
+const ChartBase2D=tr.ui.b.define('chart-base-2d',tr.ui.b.ChartBase);ChartBase2D.prototype={__proto__:tr.ui.b.ChartBase.prototype,decorate(){super.decorate();Polymer.dom(this).classList.add('chart-base-2d');this.xScale_=d3.scale.linear();this.yScale_=d3.scale.linear();this.isYLogScale_=false;this.yLogScaleBase_=10;this.yLogScaleMin_=undefined;this.autoDataRange_=new tr.b.math.Range();this.overrideDataRange_=undefined;this.hideXAxis_=false;this.hideYAxis_=false;this.data_=[];this.xAxisLabel_='';this.yAxisLabel_='';this.textHeightPx_=0;this.unit_=undefined;d3.select(this.chartAreaElement).append('g').attr('id','brushes');d3.select(this.chartAreaElement).append('g').attr('id','series');this.addEventListener('mousedown',this.onMouseDown_.bind(this));},get yLogScaleBase(){return this.yLogScaleBase_;},set yLogScaleBase(b){this.yLogScaleBase_=b;},get unit(){return this.unit_;},set unit(unit){this.unit_=unit;this.updateContents_();},get xAxisLabel(){return this.xAxisLabel_;},set xAxisLabel(label){this.xAxisLabel_=label;},get yAxisLabel(){return this.yAxisLabel_;},set yAxisLabel(label){this.yAxisLabel_=label;},get hideXAxis(){return this.hideXAxis_;},set hideXAxis(h){this.hideXAxis_=h;this.updateContents_();},get hideYAxis(){return this.hideYAxis_;},set hideYAxis(h){this.hideYAxis_=h;this.updateContents_();},get data(){return this.data_;},set data(data){if(data===undefined){throw new Error('data must be an Array');}
+this.data_=data;this.updateSeriesKeys_();this.updateDataRange_();this.updateContents_();},set isYLogScale(logScale){if(logScale){this.yScale_=d3.scale.log().base(this.yLogScaleBase);}else{this.yScale_=d3.scale.linear();}
+this.isYLogScale_=logScale;},getYScaleMin_(){return this.isYLogScale_?this.yLogScaleMin_:0;},getYScaleDomain_(minValue,maxValue){if(this.overrideDataRange_!==undefined){return[this.dataRange.min,this.dataRange.max];}
+if(this.isYLogScale_){return[this.getYScaleMin_(),maxValue];}
+return[Math.min(minValue,this.getYScaleMin_()),maxValue];},getSampleWidth_(data,index,leftSide){let leftIndex;let rightIndex;if(leftSide){leftIndex=Math.max(index-1,0);rightIndex=index;}else{leftIndex=index;rightIndex=Math.min(index+1,data.length-1);}
+const leftWidth=this.getXForDatum_(data[index],index)-
+this.getXForDatum_(data[leftIndex],leftIndex);const rightWidth=this.getXForDatum_(data[rightIndex],rightIndex)-
+this.getXForDatum_(data[index],index);return tr.b.math.Statistics.mean([leftWidth,rightWidth]);},updateSeriesKeys_(){this.data_.forEach(function(datum){Object.keys(datum).forEach(function(key){if(this.isDatumFieldSeries_(key)){this.getDataSeries(key);}},this);},this);},isDatumFieldSeries_(fieldName){return fieldName!=='x';},getXForDatum_(datum,index){return datum.x;},updateMargins_(){this.margin.left=this.hideYAxis?0:this.yAxisWidth;this.margin.bottom=this.hideXAxis?0:this.xAxisHeight;if(this.hideXAxis&&!this.hideYAxis){this.margin.bottom=10;}
+if(this.hideYAxis&&!this.hideXAxis){this.margin.left=10;}
+this.margin.top=this.hideYAxis?0:10;if(this.yAxisLabel){this.margin.top+=this.textHeightPx_;}
+if(this.xAxisLabel){this.margin.right=Math.max(this.margin.right,16+tr.ui.b.getSVGTextSize(this,this.xAxisLabel).width);}
+super.updateMargins_();},get xAxisHeight(){return D3_X_AXIS_HEIGHT_PX;},computeScaleTickWidth_(scale){if(this.data.length===0)return 0;let tickValues=scale.ticks();let tickFormat=scale.tickFormat();if(this.isYLogScale_){const enclosingPowers=this.dataRange.enclosingPowers();tickValues=[];const maxPower=sanitizePower(enclosingPowers.max,this.yLogScaleBase);for(let power=sanitizePower(enclosingPowers.min,1);power<=maxPower;power*=this.yLogScaleBase){tickValues.push(power);}
+tickFormat=v=>v.toString();}
+if(this.unit){tickFormat=v=>this.unit.format(v);}
+let maxTickWidth=0;for(const tickValue of tickValues){maxTickWidth=Math.max(maxTickWidth,tr.ui.b.getSVGTextSize(this,tickFormat(tickValue)).width);}
+return D3_Y_AXIS_WIDTH_PX+maxTickWidth;},get yAxisWidth(){return this.computeScaleTickWidth_(this.yScale_);},updateScales_(){if(this.data_.length===0)return;this.xScale_.range([0,this.graphWidth]);this.xScale_.domain(d3.extent(this.data_,this.getXForDatum_.bind(this)));this.yScale_.range([this.graphHeight,0]);this.yScale_.domain([this.dataRange.min,this.dataRange.max]);},updateBrushContents_(brushSel){brushSel.selectAll('*').remove();},updateXAxis_(xAxis){xAxis.selectAll('*').remove();xAxis[0][0].style.opacity=0;if(this.hideXAxis)return;this.drawXAxis_(xAxis);const label=xAxis.append('text').attr('class','label');this.drawXAxisTicks_(xAxis);this.drawXAxisLabel_(label);xAxis[0][0].style.opacity=1;},drawXAxis_(xAxis){xAxis.attr('transform','translate(0,'+this.graphHeight+')').call(d3.svg.axis().scale(this.xScale_).orient('bottom'));},drawXAxisLabel_(label){label.attr('x',this.graphWidth+16).attr('y',8).text(this.xAxisLabel);},drawXAxisTicks_(xAxis){let previousRight=undefined;xAxis.selectAll('.tick')[0].forEach(function(tick){const currentLeft=tick.transform.baseVal[0].matrix.e;if((previousRight===undefined)||(currentLeft>(previousRight+3))){const currentWidth=tick.getBBox().width;previousRight=currentLeft+currentWidth;}else{tick.style.opacity=0;}});},set overrideDataRange(range){this.overrideDataRange_=range;},get dataRange(){if(this.overrideDataRange_!==undefined){return this.overrideDataRange_;}
+return this.autoDataRange_;},updateDataRange_(){if(this.overrideDataRange_!==undefined)return;const dataBySeriesKey=this.getDataBySeriesKey_();this.autoDataRange_.reset();for(const[series,values]of Object.entries(dataBySeriesKey)){for(let i=0;i<values.length;i++){this.autoDataRange_.addValue(values[i][series]);}}
+this.yLogScaleMin_=undefined;if(this.autoDataRange_.min!==undefined){let minValue=this.autoDataRange_.min;if(minValue===0){minValue=1;}
+const onePowerLess=tr.b.math.lesserPower(minValue/this.yLogScaleBase);this.yLogScaleMin_=onePowerLess;}},updateYAxis_(yAxis){yAxis.selectAll('*').remove();yAxis[0][0].style.opacity=0;if(this.hideYAxis)return;this.drawYAxis_(yAxis);this.drawYAxisTicks_(yAxis);const label=yAxis.append('text').attr('class','label');this.drawYAxisLabel_(label);},drawYAxis_(yAxis){let axisModifier=d3.svg.axis().scale(this.yScale_).orient('left');let tickFormat;if(this.isYLogScale_){if(this.yLogScaleMin_===undefined)return;const tickValues=[];const enclosingPowers=this.dataRange.enclosingPowers();const maxPower=sanitizePower(enclosingPowers.max,this.yLogScaleBase);for(let power=sanitizePower(enclosingPowers.min,1);power<=maxPower;power*=this.yLogScaleBase){tickValues.push(power);}
+axisModifier=axisModifier.tickValues(tickValues);tickFormat=v=>v.toString();}
+if(this.unit){tickFormat=v=>this.unit.format(v);}
+if(tickFormat){axisModifier=axisModifier.tickFormat(tickFormat);}
+yAxis.call(axisModifier);},drawYAxisLabel_(label){const labelWidthPx=Math.ceil(tr.ui.b.getSVGTextSize(this.chartAreaElement,this.yAxisLabel).width);label.attr('x',-labelWidthPx).attr('y',-8).text(this.yAxisLabel);},drawYAxisTicks_(yAxis){let previousTop=undefined;yAxis.selectAll('.tick')[0].forEach(function(tick){const bbox=tick.getBBox();const currentTop=tick.transform.baseVal[0].matrix.f;const currentBottom=currentTop+bbox.height;if((previousTop===undefined)||(previousTop>(currentBottom+3))){previousTop=currentTop;}else{tick.style.opacity=0;}});yAxis[0][0].style.opacity=1;},updateContents_(){if(this.textHeightPx_===0){this.textHeightPx_=tr.ui.b.getSVGTextSize(this,'Ay').height;}
+this.updateScales_();super.updateContents_();const chartAreaSel=d3.select(this.chartAreaElement);this.updateXAxis_(chartAreaSel.select('.x.axis'));this.updateYAxis_(chartAreaSel.select('.y.axis'));for(const child of Array.from(this.querySelectorAll('.axis path, .axis line'))){child.style.fill='none';child.style.shapeRendering='crispEdges';child.style.stroke='black';}
+this.updateBrushContents_(chartAreaSel.select('#brushes'));this.updateDataContents_(chartAreaSel.select('#series'));},updateDataContents_(seriesSel){throw new Error('Not implemented');},getDataBySeriesKey_(){const dataBySeriesKey={};for(const[key,series]of this.seriesByKey_){dataBySeriesKey[key]=[];}
+this.data_.forEach(function(multiSeriesDatum,index){const x=this.getXForDatum_(multiSeriesDatum,index);d3.keys(multiSeriesDatum).forEach(function(seriesKey){if(seriesKey==='x')return;if(multiSeriesDatum[seriesKey]===undefined)return;if(!this.isDatumFieldSeries_(seriesKey))return;const singleSeriesDatum={x};singleSeriesDatum[seriesKey]=multiSeriesDatum[seriesKey];dataBySeriesKey[seriesKey].push(singleSeriesDatum);},this);},this);return dataBySeriesKey;},getChartPointAtClientPoint_(clientPoint){const rect=this.getBoundingClientRect();return{x:clientPoint.x-rect.left-this.margin.left,y:clientPoint.y-rect.top-this.margin.top};},getDataPointAtChartPoint_(chartPoint){return{x:tr.b.math.clamp(this.xScale_.invert(chartPoint.x),this.xScale_.domain()[0],this.xScale_.domain()[1]),y:tr.b.math.clamp(this.yScale_.invert(chartPoint.y),this.yScale_.domain()[0],this.yScale_.domain()[1])};},getDataPointAtClientPoint_(clientX,clientY){const chartPoint=this.getChartPointAtClientPoint_({x:clientX,y:clientY});return this.getDataPointAtChartPoint_(chartPoint);},prepareDataEvent_(mouseEvent,dataEvent){const dataPoint=this.getDataPointAtClientPoint_(mouseEvent.clientX,mouseEvent.clientY);dataEvent.x=dataPoint.x;dataEvent.y=dataPoint.y;},onMouseDown_(mouseEvent){tr.ui.b.trackMouseMovesUntilMouseUp(this.onMouseMove_.bind(this,mouseEvent.button),this.onMouseUp_.bind(this,mouseEvent.button));mouseEvent.preventDefault();mouseEvent.stopPropagation();const dataEvent=new tr.b.Event('item-mousedown');dataEvent.button=mouseEvent.button;this.prepareDataEvent_(mouseEvent,dataEvent);this.dispatchEvent(dataEvent);for(const child of Array.from(this.querySelector('#brushes').children)){child.setAttribute('fill','rgb(103, 199, 165)');}},onMouseMove_(button,mouseEvent){if(mouseEvent.buttons!==undefined){mouseEvent.preventDefault();mouseEvent.stopPropagation();}
+const dataEvent=new tr.b.Event('item-mousemove');dataEvent.button=button;this.prepareDataEvent_(mouseEvent,dataEvent);this.dispatchEvent(dataEvent);for(const child of Array.from(this.querySelector('#brushes').children)){child.setAttribute('fill','rgb(103, 199, 165)');}},onMouseUp_(button,mouseEvent){mouseEvent.preventDefault();mouseEvent.stopPropagation();const dataEvent=new tr.b.Event('item-mouseup');dataEvent.button=button;this.prepareDataEvent_(mouseEvent,dataEvent);this.dispatchEvent(dataEvent);for(const child of Array.from(this.querySelector('#brushes').children)){child.setAttribute('fill','rgb(213, 236, 229)');}}};return{ChartBase2D,};});'use strict';tr.exportTo('tr.ui.b',function(){const ChartBase2D=tr.ui.b.ChartBase2D;const ChartBase2DBrushX=tr.ui.b.define('chart-base-2d-brush-1d',ChartBase2D);ChartBase2DBrushX.prototype={__proto__:ChartBase2D.prototype,decorate(){super.decorate();this.brushedRange_=new tr.b.math.Range();},set brushedRange(range){this.brushedRange_.reset();this.brushedRange_.addRange(range);this.updateContents_();},get brushedRange(){return tr.b.math.Range.fromDict(this.brushedRange_.toJSON());},computeBrushRangeFromIndices(indexA,indexB){indexA=tr.b.math.clamp(indexA,0,this.data_.length-1);indexB=tr.b.math.clamp(indexB,0,this.data_.length-1);const leftIndex=Math.min(indexA,indexB);const rightIndex=Math.max(indexA,indexB);const brushRange=new tr.b.math.Range();brushRange.addValue(this.getXForDatum_(this.data_[leftIndex],leftIndex)-
+this.getSampleWidth_(this.data_,leftIndex,true));brushRange.addValue(this.getXForDatum_(this.data_[rightIndex],rightIndex)+
+this.getSampleWidth_(this.data_,rightIndex,false));return brushRange;},getDataIndex_(dataX){if(this.data.length===0)return undefined;const bisect=d3.bisector(this.getXForDatum_.bind(this)).right;return bisect(this.data_,dataX)-1;},prepareDataEvent_(mouseEvent,dataEvent){ChartBase2D.prototype.prepareDataEvent_.call(this,mouseEvent,dataEvent);dataEvent.index=this.getDataIndex_(dataEvent.x);if(dataEvent.index!==undefined){dataEvent.data=this.data_[dataEvent.index];}},updateBrushContents_(brushSel){brushSel.selectAll('*').remove();const brushes=this.brushedRange_.isEmpty?[]:[this.brushedRange_];const brushRectsSel=brushSel.selectAll('rect').data(brushes);brushRectsSel.enter().append('rect');brushRectsSel.exit().remove();this.drawBrush_(brushRectsSel);},drawBrush_(brushRectsSel){brushRectsSel.attr('x',d=>this.xScale_(d.min)).attr('y',0).attr('width',d=>this.xScale_(d.max)-this.xScale_(d.min)).attr('height',this.graphHeight).attr('fill','rgb(213, 236, 229)');}};return{ChartBase2DBrushX,};});'use strict';tr.exportTo('tr.ui.b',function(){const ColumnChart=tr.ui.b.define('column-chart',tr.ui.b.ChartBase2DBrushX);ColumnChart.prototype={__proto__:tr.ui.b.ChartBase2DBrushX.prototype,decorate(){super.decorate();this.xCushion_=1;this.isStacked_=false;this.isGrouped_=false;this.enableHoverBox=true;this.displayXInHover=false;this.enableToolTip=false;this.toolTipCallBack_=()=>{};},set toolTipCallBack(callback){this.toolTipCallBack_=callback;},get toolTipCallBack(){return this.toolTipCallBack_;},set isGrouped(grouped){this.isGrouped_=grouped;if(grouped){this.getDataSeries('group').color='transparent';}
+this.updateContents_();},get isGrouped(){return this.isGrouped_;},set isStacked(stacked){this.isStacked_=true;this.updateContents_();},get isStacked(){return this.isStacked_;},get defaultGraphHeight(){return 100;},get defaultGraphWidth(){return 10*this.data_.length;},updateScales_(){if(this.data_.length===0)return;let xDifferences=0;let currentX=undefined;let previousX=undefined;this.data_.forEach(function(datum,index){previousX=currentX;currentX=this.getXForDatum_(datum,index);if(previousX!==undefined){xDifferences+=currentX-previousX;}},this);this.xScale_.range([0,this.graphWidth]);const domain=d3.extent(this.data_,this.getXForDatum_.bind(this));if(this.data_.length>1){this.xCushion_=xDifferences/(this.data_.length-1);}
+this.xScale_.domain([domain[0],domain[1]+this.xCushion_]);this.yScale_.range([this.graphHeight,0]);this.yScale_.domain(this.getYScaleDomain_(this.dataRange.min,this.dataRange.max));},updateDataRange_(){if(!this.isStacked){super.updateDataRange_();return;}
+this.autoDataRange_.reset();this.autoDataRange_.addValue(0);for(const datum of this.data_){let sum=0;for(const[key,series]of this.seriesByKey_){if(datum[key]===undefined){continue;}else if(this.isGrouped&&key==='group'){continue;}
+sum+=datum[key];}
+this.autoDataRange_.addValue(sum);}},getStackedRectsForDatum_(datum,index){const stacks=[];let bottom=this.yScale_.range()[0];let sum=0;for(const[key,series]of this.seriesByKey_){if(datum[key]===undefined||!this.isSeriesEnabled(key)){continue;}else if(this.isGrouped&&key==='group'){continue;}
+sum+=this.dataRange.clamp(datum[key]);const heightPx=bottom-this.yScale_(sum);bottom-=heightPx;stacks.push({key,value:datum[key],color:this.getDataSeries(key).color,heightPx,topPx:bottom,underflow:sum<this.dataRange.min,overflow:sum>this.dataRange.max,});}
+return stacks;},getRectsForDatum_(datum,index){if(this.isStacked){return this.getStackedRectsForDatum_(datum,index);}
+const stacks=[];for(const[key,series]of this.seriesByKey_){if(datum[key]===undefined||!this.isSeriesEnabled(key)){continue;}
+const clampedValue=this.dataRange.clamp(datum[key]);const topPx=this.yScale_(Math.max(clampedValue,this.getYScaleMin_()));stacks.push({key,value:datum[key],topPx,heightPx:this.yScale_.range()[0]-topPx,color:this.getDataSeries(key).color,underflow:datum[key]<this.dataRange.min,overflow:datum[key]>this.dataRange.max,});}
+stacks.sort(function(a,b){return b.topPx-a.topPx;});return stacks;},drawToolTip_(rect){if(!this.enableToolTip)return;const chartAreaSel=d3.select(this.chartAreaElement);chartAreaSel.selectAll('.tooltip').remove();const labelText='View Breakdown';const labelWidth=tr.ui.b.getSVGTextSize(this.chartAreaElement,labelText).width+5;const labelHeight=this.textHeightPx_;const toolTipLeftPx=rect.leftPx+(rect.widthPx/2);const toolTipTopPx=rect.topPx;chartAreaSel.append('rect').attr('class','tooltip').attr('fill','white').attr('opacity',0.8).attr('stroke','black').attr('x',toolTipLeftPx).attr('y',toolTipTopPx).attr('width',labelWidth+5).attr('height',labelHeight+10);chartAreaSel.append('text').style('cursor','pointer').attr('class','tooltip').on('mousedown',()=>this.toolTipCallBack_(rect)).attr('fill','blue').attr('x',toolTipLeftPx+4).attr('y',toolTipTopPx+labelHeight).attr('text-decoration','underline').text(labelText);},drawHoverValueBox_(rect){const rectHoverEvent=new tr.b.Event('rect-mouseenter');rectHoverEvent.rect=rect;this.dispatchEvent(rectHoverEvent);if(!this.enableHoverBox)return;const seriesKeys=[...this.seriesByKey_.keys()];const chartAreaSel=d3.select(this.chartAreaElement);chartAreaSel.selectAll('.hover').remove();let keyWidthPx=0;let keyHeightPx=0;if(seriesKeys.length>1&&!this.isGrouped){keyWidthPx=tr.ui.b.getSVGTextSize(this.chartAreaElement,rect.key).width+5;keyHeightPx=this.textHeightPx_;}
+let xLabelWidthPx=0;let xLabelHeightPx=0;if(this.displayXInHover){xLabelWidthPx=tr.ui.b.getSVGTextSize(this.chartAreaElement,rect.datum.x).width+5;xLabelHeightPx=this.textHeightPx_;}
+let groupWidthPx=0;let groupHeightPx=0;if(this.isGrouped&&rect.datum.group!==undefined){groupWidthPx=tr.ui.b.getSVGTextSize(this.chartAreaElement,rect.datum.group).width+5;groupHeightPx=this.textHeightPx_;}
+let value=rect.value;if(this.unit)value=this.unit.format(value);const valueWidthPx=tr.ui.b.getSVGTextSize(this.chartAreaElement,value).width+5;const valueHeightPx=this.textHeightPx_;const hoverWidthPx=Math.max(keyWidthPx,valueWidthPx,xLabelWidthPx,groupWidthPx);let hoverLeftPx=rect.leftPx+(rect.widthPx/2);hoverLeftPx=Math.max(hoverLeftPx-hoverWidthPx,-this.margin.left);const hoverHeightPx=keyHeightPx+valueHeightPx+
+xLabelHeightPx+groupHeightPx+2;const topOffSetPx=this.isGrouped?36:12;let hoverTopPx=rect.topPx;hoverTopPx=Math.min(hoverTopPx,this.getBoundingClientRect().height-
+hoverHeightPx-topOffSetPx);chartAreaSel.append('rect').attr('class','hover').on('mouseleave',()=>this.clearHoverValueBox_(rect)).on('mousedown',this.drawToolTip_.bind(this,rect)).attr('fill','white').attr('stroke','black').attr('x',hoverLeftPx).attr('y',hoverTopPx).attr('width',hoverWidthPx).attr('height',hoverHeightPx);if(seriesKeys.length>1&&!this.isGrouped){chartAreaSel.append('text').attr('class','hover').on('mouseleave',()=>this.clearHoverValueBox_(rect)).on('mousedown',this.drawToolTip_.bind(this,rect)).attr('fill',rect.color).attr('x',hoverLeftPx+2).attr('y',hoverTopPx+keyHeightPx-2).text(rect.key);}
+if(this.displayXInHover){chartAreaSel.append('text').attr('class','hover').on('mouseleave',()=>this.clearHoverValueBox_(rect)).on('mousedown',this.drawToolTip_.bind(this,rect)).attr('fill',rect.color).attr('x',hoverLeftPx+2).attr('y',hoverTopPx+keyHeightPx+xLabelHeightPx-2).text(rect.datum.x);}
+if(this.isGrouped&&rect.datum.group!==undefined){chartAreaSel.append('text').attr('class','hover').on('mouseleave',()=>this.clearHoverValueBox_(rect)).on('mousedown',this.drawToolTip_.bind(this,rect)).attr('fill',rect.color).attr('x',hoverLeftPx+2).attr('y',hoverTopPx+keyHeightPx+
+xLabelHeightPx+groupHeightPx-2).text(rect.datum.group);}
+chartAreaSel.append('text').attr('class','hover').on('mouseleave',()=>this.clearHoverValueBox_(rect)).on('mousedown',this.drawToolTip_.bind(this,rect)).attr('fill',rect.color).attr('x',hoverLeftPx+2).attr('y',hoverTopPx+hoverHeightPx-2).text(value);},clearHoverValueBox_(rect){const event=window.event;if(event.relatedTarget&&Array.from(event.relatedTarget.classList).includes('hover')){return;}
+const rectHoverEvent=new tr.b.Event('rect-mouseleave');rectHoverEvent.rect=rect;this.dispatchEvent(rectHoverEvent);d3.select(this.chartAreaElement).selectAll('.hover').remove();},drawRect_(rect,sel){sel=sel.data([rect]);sel.enter().append('rect').attr('fill',rect.color).attr('x',rect.leftPx).attr('y',rect.topPx).attr('width',rect.widthPx).attr('height',rect.heightPx).on('mousedown',this.drawToolTip_.bind(this,rect)).on('mouseenter',this.drawHoverValueBox_.bind(this,rect)).on('mouseleave',this.clearHoverValueBox_.bind(this,rect));sel.exit().remove();},drawUnderflow_(rect,sel){sel=sel.data([rect]);sel.enter().append('text').text('*').attr('fill',rect.color).attr('x',rect.leftPx+(rect.widthPx/2)).attr('y',this.graphHeight).on('mousedown',this.drawToolTip_.bind(this,rect)).on('mouseenter',this.drawHoverValueBox_.bind(this,rect)).on('mouseleave',this.clearHoverValueBox_.bind(this,rect));sel.exit().remove();},drawOverflow_(rect,sel){sel=sel.data([rect]);sel.enter().append('text').text('*').attr('fill',rect.color).attr('x',rect.leftPx+(rect.widthPx/2)).attr('y',0);sel.exit().remove();},updateDataContents_(dataSel){dataSel.selectAll('*').remove();const chartAreaSel=d3.select(this.chartAreaElement);const seriesKeys=[...this.seriesByKey_.keys()];const rectsSel=dataSel.selectAll('path');this.data_.forEach(function(datum,index){const currentX=this.getXForDatum_(datum,index);let width=undefined;if(index<(this.data_.length-1)){const nextX=this.getXForDatum_(this.data_[index+1],index+1);width=nextX-currentX;}else{width=this.xCushion_;}
+for(const rect of this.getRectsForDatum_(datum,index)){rect.datum=datum;rect.index=index;rect.leftPx=this.xScale_(currentX);rect.rightPx=this.xScale_(currentX+width);rect.widthPx=rect.rightPx-rect.leftPx;this.drawRect_(rect,rectsSel);if(rect.underflow){this.drawUnderflow_(rect,rectsSel);}
+if(rect.overflow){this.drawOverflow_(rect,rectsSel);}}},this);}};return{ColumnChart,};});'use strict';tr.exportTo('tr.ui.b',function(){const LineChart=tr.ui.b.define('line-chart',tr.ui.b.ChartBase2DBrushX);LineChart.prototype={__proto__:tr.ui.b.ChartBase2DBrushX.prototype,decorate(){super.decorate();this.enableHoverBox=true;this.displayXInHover=false;},get defaultGraphWidth(){return 20*this.data_.length;},get defaultGraphHeight(){return 100;},drawHoverValueBox_(circle){tr.ui.b.ColumnChart.prototype.drawHoverValueBox_.call(this,circle);},clearHoverValueBox_(circle){tr.ui.b.ColumnChart.prototype.clearHoverValueBox_.call(this,circle);},updateDataContents_(dataSel){dataSel.selectAll('*').remove();const dataBySeriesKey=this.getDataBySeriesKey_();const seriesKeys=[...this.seriesByKey_.keys()];const pathsSel=dataSel.selectAll('path').data(seriesKeys);pathsSel.enter().append('path').style('fill','none').style('stroke-width','1.5px').style('stroke',key=>this.getDataSeries(key).color).attr('d',key=>{const line=d3.svg.line().x(d=>this.xScale_(d.x)).y(d=>this.yScale_(this.dataRange.clamp(d[key])));return line(dataBySeriesKey[key]);});pathsSel.exit().remove();if(this.enableHoverBox){for(let index=0;index<this.data_.length;++index){const datum=this.data_[index];const x=this.getXForDatum_(datum,index);for(const[key,value]of Object.entries(datum)){if(key==='x')continue;if(value===undefined)continue;const color=this.getDataSeries(key).color;const circle=document.createElementNS('http://www.w3.org/2000/svg','circle');circle.setAttribute('cx',this.xScale_(x));circle.setAttribute('cy',this.yScale_(this.dataRange.clamp(value)));circle.setAttribute('r',5);circle.style.fill=color;circle.datum=datum;circle.key=key;circle.value=datum[key];circle.leftPx=this.xScale_(x);circle.widthPx=0;circle.color=color;circle.topPx=this.yScale_(this.dataRange.clamp(value));circle.heightPx=0;circle.addEventListener('mouseenter',()=>this.drawHoverValueBox_(circle));circle.addEventListener('mouseleave',()=>this.clearHoverValueBox_(circle));dataSel[0][0].appendChild(circle);}}}}};return{LineChart,};});'use strict';Polymer({is:'tr-ui-e-s-input-latency-side-panel',behaviors:[tr.ui.behaviors.SidePanel],ready(){this.rangeOfInterest_=new tr.b.math.Range();this.frametimeType_=tr.model.helpers.IMPL_FRAMETIME_TYPE;this.latencyChart_=undefined;this.frametimeChart_=undefined;this.selectedProcessId_=undefined;this.mouseDownIndex_=undefined;this.curMouseIndex_=undefined;},get model(){return this.model_;},set model(model){this.model_=model;if(this.model_){this.modelHelper_=this.model_.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);}else{this.modelHelper_=undefined;}
+this.updateToolbar_();this.updateContents_();},get frametimeType(){return this.frametimeType_;},set frametimeType(type){if(this.frametimeType_===type)return;this.frametimeType_=type;this.updateContents_();},get selectedProcessId(){return this.selectedProcessId_;},set selectedProcessId(process){if(this.selectedProcessId_===process)return;this.selectedProcessId_=process;this.updateContents_();},set selection(selection){if(this.latencyChart_===undefined)return;this.latencyChart_.brushedRange=selection.bounds;},setBrushedIndices(mouseDownIndex,curIndex){this.mouseDownIndex_=mouseDownIndex;this.curMouseIndex_=curIndex;this.updateBrushedRange_();},updateBrushedRange_(){if(this.latencyChart_===undefined)return;let r=new tr.b.math.Range();if(this.mouseDownIndex_===undefined){this.latencyChart_.brushedRange=r;return;}
+r=this.latencyChart_.computeBrushRangeFromIndices(this.mouseDownIndex_,this.curMouseIndex_);this.latencyChart_.brushedRange=r;let latencySlices=[];for(const thread of this.model_.getAllThreads()){for(const event of thread.getDescendantEvents()){if(event.title.indexOf('InputLatency:')===0){latencySlices.push(event);}}}
+latencySlices=tr.model.helpers.getSlicesIntersectingRange(r,latencySlices);const event=new tr.model.RequestSelectionChangeEvent();event.selection=new tr.model.EventSet(latencySlices);this.latencyChart_.dispatchEvent(event);},registerMouseEventForLatencyChart_(){this.latencyChart_.addEventListener('item-mousedown',function(e){this.mouseDownIndex_=e.index;this.curMouseIndex_=e.index;this.updateBrushedRange_();}.bind(this));this.latencyChart_.addEventListener('item-mousemove',function(e){if(e.button===undefined)return;this.curMouseIndex_=e.index;this.updateBrushedRange_();}.bind(this));this.latencyChart_.addEventListener('item-mouseup',function(e){this.curMouseIndex=e.index;this.updateBrushedRange_();}.bind(this));},updateToolbar_(){const browserProcess=this.modelHelper_.browserProcess;const labels=[];if(browserProcess!==undefined){const labelStr='Browser: '+browserProcess.pid;labels.push({label:labelStr,value:browserProcess.pid});}
+for(const rendererHelper of
+Object.values(this.modelHelper_.rendererHelpers)){const rendererProcess=rendererHelper.process;const labelStr='Renderer: '+rendererProcess.userFriendlyName;labels.push({label:labelStr,value:rendererProcess.userFriendlyName});}
+if(labels.length===0)return;this.selectedProcessId_=labels[0].value;const toolbarEl=this.$.toolbar;Polymer.dom(toolbarEl).appendChild(tr.ui.b.createSelector(this,'frametimeType','inputLatencySidePanel.frametimeType',this.frametimeType_,[{label:'Main Thread Frame Times',value:tr.model.helpers.MAIN_FRAMETIME_TYPE},{label:'Impl Thread Frame Times',value:tr.model.helpers.IMPL_FRAMETIME_TYPE}]));Polymer.dom(toolbarEl).appendChild(tr.ui.b.createSelector(this,'selectedProcessId','inputLatencySidePanel.selectedProcessId',this.selectedProcessId_,labels));},get currentRangeOfInterest(){if(this.rangeOfInterest_.isEmpty){return this.model_.bounds;}
+return this.rangeOfInterest_;},createLatencyLineChart(data,title,parentNode){const chart=new tr.ui.b.LineChart();Polymer.dom(parentNode).appendChild(chart);let width=600;if(document.body.clientWidth!==undefined){width=document.body.clientWidth*0.5;}
+chart.graphWidth=width;chart.chartTitle=title;chart.data=data;return chart;},updateContents_(){const resultArea=this.$.result_area;this.latencyChart_=undefined;this.frametimeChart_=undefined;Polymer.dom(resultArea).textContent='';if(this.modelHelper_===undefined)return;const rangeOfInterest=this.currentRangeOfInterest;let chromeProcess;if(this.modelHelper_.rendererHelpers[this.selectedProcessId_]){chromeProcess=this.modelHelper_.rendererHelpers[this.selectedProcessId_];}else{chromeProcess=this.modelHelper_.browserHelper;}
+const frameEvents=chromeProcess.getFrameEventsInRange(this.frametimeType,rangeOfInterest);const frametimeData=tr.model.helpers.getFrametimeDataFromEvents(frameEvents);const averageFrametime=tr.b.math.Statistics.mean(frametimeData,d=>d.frametime);const latencyEvents=this.modelHelper_.browserHelper.getLatencyEventsInRange(rangeOfInterest);const latencyData=[];latencyEvents.forEach(function(event){if(event.inputLatency===undefined)return;latencyData.push({x:event.start,latency:event.inputLatency/1000});});const averageLatency=tr.b.math.Statistics.mean(latencyData,function(d){return d.latency;});const latencySummaryText=document.createElement('div');Polymer.dom(latencySummaryText).appendChild(tr.ui.b.createSpan({textContent:'Average Latency '+averageLatency+' ms',bold:true}));Polymer.dom(resultArea).appendChild(latencySummaryText);const frametimeSummaryText=document.createElement('div');Polymer.dom(frametimeSummaryText).appendChild(tr.ui.b.createSpan({textContent:'Average Frame Time '+averageFrametime+' ms',bold:true}));Polymer.dom(resultArea).appendChild(frametimeSummaryText);if(latencyData.length!==0){this.latencyChart_=this.createLatencyLineChart(latencyData,'Latency Over Time',resultArea);this.registerMouseEventForLatencyChart_();}
+if(frametimeData.length!==0){this.frametimeChart_=this.createLatencyLineChart(frametimeData,'Frame Times',resultArea);}},get rangeOfInterest(){return this.rangeOfInterest_;},set rangeOfInterest(rangeOfInterest){this.rangeOfInterest_=rangeOfInterest;this.updateContents_();},supportsModel(m){if(m===undefined){return{supported:false,reason:'Unknown tracing model'};}
+if(!tr.model.helpers.ChromeModelHelper.supportsModel(m)){return{supported:false,reason:'No Chrome browser or renderer process found'};}
+const modelHelper=m.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);if(modelHelper.browserHelper&&modelHelper.browserHelper.hasLatencyEvents){return{supported:true};}
+return{supported:false,reason:'No InputLatency events trace. Consider enabling '+'benchmark" and "input" category when recording the trace'};},get textLabel(){return'Input Latency';}});tr.ui.side_panel.SidePanelRegistry.register(function(){return document.createElement('tr-ui-e-s-input-latency-side-panel');});'use strict';tr.exportTo('tr.e.system_stats',function(){const ObjectSnapshot=tr.model.ObjectSnapshot;function SystemStatsSnapshot(objectInstance,ts,args){ObjectSnapshot.apply(this,arguments);this.objectInstance=objectInstance;this.ts=ts;this.args=args;this.stats_=args;}
+SystemStatsSnapshot.prototype={__proto__:ObjectSnapshot.prototype,initialize(){if(this.args.length===0){throw new Error('No system stats snapshot data.');}
+this.stats_=this.args;},getStats(){return this.stats_;},setStats(stats){this.stats_=stats;}};ObjectSnapshot.subTypes.register(SystemStatsSnapshot,{typeName:'base::TraceEventSystemStatsMonitor::SystemStats'});return{SystemStatsSnapshot,};});'use strict';tr.exportTo('tr.ui.b',function(){const constants={HEADING_WIDTH:250};return{constants,};});'use strict';Polymer({is:'tr-ui-b-heading',DOWN_ARROW:String.fromCharCode(0x25BE),RIGHT_ARROW:String.fromCharCode(0x25B8),ready(viewport){this.style.width=(tr.ui.b.constants.HEADING_WIDTH-6)+'px';this.heading_='';this.expanded_=true;this.arrowVisible_=false;this.selectionGenerator_=undefined;this.updateContents_();},get heading(){return this.heading_;},set heading(text){if(this.heading_===text)return;this.heading_=text;this.updateContents_();},set arrowVisible(val){if(this.arrowVisible_===val)return;this.arrowVisible_=!!val;this.updateContents_();},set tooltip(text){this.$.heading.title=text;},set selectionGenerator(generator){if(this.selectionGenerator_===generator)return;this.selectionGenerator_=generator;this.updateContents_();},get expanded(){return this.expanded_;},set expanded(expanded){if(this.expanded_===expanded)return;this.expanded_=!!expanded;this.updateContents_();},onHeadingDivClicked_(){this.dispatchEvent(new tr.b.Event('heading-clicked',true));},updateContents_(){if(this.arrowVisible_){this.$.arrow.style.display='';}else{this.$.arrow.style.display='none';this.$.heading.style.display=this.expanded_?'':'none';}
+if(this.arrowVisible_){Polymer.dom(this.$.arrow).textContent=this.expanded_?this.DOWN_ARROW:this.RIGHT_ARROW;}
+this.$.link.style.display='none';this.$.heading_content.style.display='none';if(this.selectionGenerator_){this.$.link.style.display='inline-block';this.$.link.selection=this.selectionGenerator_;Polymer.dom(this.$.link).textContent=this.heading_;}else{this.$.heading_content.style.display='inline-block';Polymer.dom(this.$.heading_content).textContent=this.heading_;}}});'use strict';tr.exportTo('tr.ui.tracks',function(){const Track=tr.ui.b.define('track',tr.ui.b.ContainerThatDecoratesItsChildren);Track.prototype={__proto__:tr.ui.b.ContainerThatDecoratesItsChildren.prototype,decorate(viewport){tr.ui.b.ContainerThatDecoratesItsChildren.prototype.decorate.call(this);if(viewport===undefined){throw new Error('viewport is required when creating a Track.');}
+this.viewport_=viewport;Polymer.dom(this).classList.add('track');},get viewport(){return this.viewport_;},get drawingContainer(){if(this instanceof tr.ui.tracks.DrawingContainer)return this;let cur=this.parentElement;while(cur){if(cur instanceof tr.ui.tracks.DrawingContainer)return cur;cur=cur.parentElement;}
+return undefined;},get eventContainer(){},invalidateDrawingContainer(){const dc=this.drawingContainer;if(dc)dc.invalidate();},context(){if(!Polymer.dom(this).parentNode)return undefined;if(!Polymer.dom(this).parentNode.context){throw new Error('Parent container does not support context() method.');}
+return Polymer.dom(this).parentNode.context();},decorateChild_(childTrack){},undecorateChild_(childTrack){if(childTrack.detach){childTrack.detach();}},updateContents_(){},drawTrack(type){const ctx=this.context();const pixelRatio=window.devicePixelRatio||1;const bounds=this.getBoundingClientRect();const canvasBounds=ctx.canvas.getBoundingClientRect();ctx.save();ctx.translate(0,pixelRatio*(bounds.top-canvasBounds.top));const dt=this.viewport.currentDisplayTransform;const viewLWorld=dt.xViewToWorld(0);const viewRWorld=dt.xViewToWorld(canvasBounds.width*pixelRatio);const viewHeight=bounds.height*pixelRatio;this.draw(type,viewLWorld,viewRWorld,viewHeight);ctx.restore();},draw(type,viewLWorld,viewRWorld,viewHeight){},addEventsToTrackMap(eventToTrackMap){},addContainersToTrackMap(containerToTrackMap){},addIntersectingEventsInRangeToSelection(loVX,hiVX,loVY,hiVY,selection){const pixelRatio=window.devicePixelRatio||1;const dt=this.viewport.currentDisplayTransform;const viewPixWidthWorld=dt.xViewVectorToWorld(1);const loWX=dt.xViewToWorld(loVX*pixelRatio);const hiWX=dt.xViewToWorld(hiVX*pixelRatio);const clientRect=this.getBoundingClientRect();const a=Math.max(loVY,clientRect.top);const b=Math.min(hiVY,clientRect.bottom);if(a>b)return;this.addIntersectingEventsInRangeToSelectionInWorldSpace(loWX,hiWX,viewPixWidthWorld,selection);},addIntersectingEventsInRangeToSelectionInWorldSpace(loWX,hiWX,viewPixWidthWorld,selection){},addClosestEventToSelection(worldX,worldMaxDist,loY,hiY,selection){},addClosestInstantEventToSelection(instantEvents,worldX,worldMaxDist,selection){const instantEvent=tr.b.findClosestElementInSortedArray(instantEvents,function(x){return x.start;},worldX,worldMaxDist);if(!instantEvent)return;selection.push(instantEvent);}};return{Track,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const SelectionState=tr.model.SelectionState;const EventPresenter=tr.ui.b.EventPresenter;const ObjectInstanceTrack=tr.ui.b.define('object-instance-track',tr.ui.tracks.Track);ObjectInstanceTrack.prototype={__proto__:tr.ui.tracks.Track.prototype,decorate(viewport){tr.ui.tracks.Track.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('object-instance-track');this.objectInstances_=[];this.objectSnapshots_=[];this.heading_=document.createElement('tr-ui-b-heading');Polymer.dom(this).appendChild(this.heading_);},set heading(heading){this.heading_.heading=heading;},get heading(){return this.heading_.heading;},set tooltip(tooltip){this.heading_.tooltip=tooltip;},get objectInstances(){return this.objectInstances_;},set objectInstances(objectInstances){if(!objectInstances||objectInstances.length===0){this.heading='';this.objectInstances_=[];this.objectSnapshots_=[];return;}
+this.heading=objectInstances[0].baseTypeName;this.objectInstances_=objectInstances;this.objectSnapshots_=[];this.objectInstances_.forEach(function(instance){this.objectSnapshots_.push.apply(this.objectSnapshots_,instance.snapshots);},this);this.objectSnapshots_.sort(function(a,b){return a.ts-b.ts;});},get height(){return window.getComputedStyle(this).height;},set height(height){this.style.height=height;},get snapshotRadiusView(){return 7*(window.devicePixelRatio||1);},draw(type,viewLWorld,viewRWorld,viewHeight){switch(type){case tr.ui.tracks.DrawType.GENERAL_EVENT:this.drawObjectInstances_(viewLWorld,viewRWorld);break;}},drawObjectInstances_(viewLWorld,viewRWorld){const ctx=this.context();const pixelRatio=window.devicePixelRatio||1;const bounds=this.getBoundingClientRect();const height=bounds.height*pixelRatio;const halfHeight=height*0.5;const twoPi=Math.PI*2;const dt=this.viewport.currentDisplayTransform;const snapshotRadiusView=this.snapshotRadiusView;const snapshotRadiusWorld=dt.xViewVectorToWorld(height);const objectInstances=this.objectInstances_;let loI=tr.b.findLowIndexInSortedArray(objectInstances,function(instance){return instance.deletionTs;},viewLWorld);ctx.save();ctx.strokeStyle='rgb(0,0,0)';for(let i=loI;i<objectInstances.length;++i){const instance=objectInstances[i];const x=instance.creationTs;if(x>viewRWorld)break;const right=instance.deletionTs===Number.MAX_VALUE?viewRWorld:instance.deletionTs;const xView=dt.xWorldToView(x);const widthView=dt.xWorldVectorToView(right-x);ctx.fillStyle=EventPresenter.getObjectInstanceColor(instance);ctx.fillRect(xView,pixelRatio,widthView,height-2*pixelRatio);}
+ctx.restore();const objectSnapshots=this.objectSnapshots_;loI=tr.b.findLowIndexInSortedArray(objectSnapshots,function(snapshot){return snapshot.ts+snapshotRadiusWorld;},viewLWorld);for(let i=loI;i<objectSnapshots.length;++i){const snapshot=objectSnapshots[i];const x=snapshot.ts;if(x-snapshotRadiusWorld>viewRWorld)break;const xView=dt.xWorldToView(x);ctx.fillStyle=EventPresenter.getObjectSnapshotColor(snapshot);ctx.beginPath();ctx.arc(xView,halfHeight,snapshotRadiusView,0,twoPi);ctx.fill();if(snapshot.selected){ctx.lineWidth=5;ctx.strokeStyle='rgb(100,100,0)';ctx.stroke();ctx.beginPath();ctx.arc(xView,halfHeight,snapshotRadiusView-1,0,twoPi);ctx.lineWidth=2;ctx.strokeStyle='rgb(255,255,0)';ctx.stroke();}else{ctx.lineWidth=1;ctx.strokeStyle='rgb(0,0,0)';ctx.stroke();}}
+ctx.lineWidth=1;let selectionState=SelectionState.NONE;if(objectInstances.length&&objectInstances[0].selectionState===SelectionState.DIMMED){selectionState=SelectionState.DIMMED;}
+if(selectionState===SelectionState.DIMMED){const width=bounds.width*pixelRatio;ctx.fillStyle='rgba(255,255,255,0.5)';ctx.fillRect(0,0,width,height);ctx.restore();}},addEventsToTrackMap(eventToTrackMap){if(this.objectInstance_!==undefined){this.objectInstance_.forEach(function(obj){eventToTrackMap.addEvent(obj,this);},this);}
+if(this.objectSnapshots_!==undefined){this.objectSnapshots_.forEach(function(obj){eventToTrackMap.addEvent(obj,this);},this);}},addIntersectingEventsInRangeToSelectionInWorldSpace(loWX,hiWX,viewPixWidthWorld,selection){let foundSnapshot=false;function onSnapshot(snapshot){selection.push(snapshot);foundSnapshot=true;}
+const snapshotRadiusView=this.snapshotRadiusView;const snapshotRadiusWorld=viewPixWidthWorld*snapshotRadiusView;tr.b.iterateOverIntersectingIntervals(this.objectSnapshots_,function(x){return x.ts-snapshotRadiusWorld;},function(x){return 2*snapshotRadiusWorld;},loWX,hiWX,onSnapshot);if(foundSnapshot)return;tr.b.iterateOverIntersectingIntervals(this.objectInstances_,function(x){return x.creationTs;},function(x){return x.deletionTs-x.creationTs;},loWX,hiWX,(value)=>{selection.push(value);});},addEventNearToProvidedEventToSelection(event,offset,selection){let events;if(event instanceof tr.model.ObjectSnapshot){events=this.objectSnapshots_;}else if(event instanceof tr.model.ObjectInstance){events=this.objectInstances_;}else{throw new Error('Unrecognized event');}
+const index=events.indexOf(event);const newIndex=index+offset;if(newIndex>=0&&newIndex<events.length){selection.push(events[newIndex]);return true;}
+return false;},addAllEventsMatchingFilterToSelection(filter,selection){},addClosestEventToSelection(worldX,worldMaxDist,loY,hiY,selection){const snapshot=tr.b.findClosestElementInSortedArray(this.objectSnapshots_,function(x){return x.ts;},worldX,worldMaxDist);if(!snapshot)return;selection.push(snapshot);}};const options=new tr.b.ExtensionRegistryOptions(tr.b.TYPE_BASED_REGISTRY_MODE);tr.b.decorateExtensionRegistry(ObjectInstanceTrack,options);return{ObjectInstanceTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const StackedBarsTrack=tr.ui.b.define('stacked-bars-track',tr.ui.tracks.Track);StackedBarsTrack.prototype={__proto__:tr.ui.tracks.Track.prototype,decorate(viewport){tr.ui.tracks.Track.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('stacked-bars-track');this.objectInstance_=null;this.heading_=document.createElement('tr-ui-b-heading');Polymer.dom(this).appendChild(this.heading_);},set heading(heading){this.heading_.heading=heading;},get heading(){return this.heading_.heading;},set tooltip(tooltip){this.heading_.tooltip=tooltip;},addEventsToTrackMap(eventToTrackMap){const objectSnapshots=this.objectInstance_.snapshots;objectSnapshots.forEach(function(obj){eventToTrackMap.addEvent(obj,this);},this);},addIntersectingEventsInRangeToSelectionInWorldSpace(loWX,hiWX,viewPixWidthWorld,selection){function onSnapshot(snapshot){selection.push(snapshot);}
+const snapshots=this.objectInstance_.snapshots;const maxBounds=this.objectInstance_.parent.model.bounds.max;tr.b.iterateOverIntersectingIntervals(snapshots,function(x){return x.ts;},function(x,i){if(i===snapshots.length-1){if(snapshots.length===1){return maxBounds;}
+return snapshots[i].ts-snapshots[i-1].ts;}
+return snapshots[i+1].ts-snapshots[i].ts;},loWX,hiWX,onSnapshot);},addEventNearToProvidedEventToSelection(event,offset,selection){if(!(event instanceof tr.model.ObjectSnapshot)){throw new Error('Unrecognized event');}
+const objectSnapshots=this.objectInstance_.snapshots;const index=objectSnapshots.indexOf(event);const newIndex=index+offset;if(newIndex>=0&&newIndex<objectSnapshots.length){selection.push(objectSnapshots[newIndex]);return true;}
+return false;},addAllEventsMatchingFilterToSelection(filter,selection){},addClosestEventToSelection(worldX,worldMaxDist,loY,hiY,selection){const snapshot=tr.b.findClosestElementInSortedArray(this.objectInstance_.snapshots,function(x){return x.ts;},worldX,worldMaxDist);if(!snapshot)return;selection.push(snapshot);}};return{StackedBarsTrack,};});'use strict';tr.exportTo('tr.ui.e.system_stats',function(){const EventPresenter=tr.ui.b.EventPresenter;let statCount;const excludedStats={'meminfo':{'pswpin':0,'pswpout':0,'pgmajfault':0},'diskinfo':{'io':0,'io_time':0,'read_time':0,'reads':0,'reads_merged':0,'sectors_read':0,'sectors_written':0,'weighted_io_time':0,'write_time':0,'writes':0,'writes_merged':0},'swapinfo':{},'perfinfo':{'idle_time':0,'read_transfer_count':0,'write_transfer_count':0,'other_transfer_count':0,'read_operation_count':0,'write_operation_count':0,'other_operation_count':0,'pagefile_pages_written':0,'pagefile_pages_write_ios':0,'available_pages':0,'pages_read':0,'page_read_ios':0}};const SystemStatsInstanceTrack=tr.ui.b.define('tr-ui-e-system-stats-instance-track',tr.ui.tracks.StackedBarsTrack);const kPageSizeWindows=4096;SystemStatsInstanceTrack.prototype={__proto__:tr.ui.tracks.StackedBarsTrack.prototype,decorate(viewport){tr.ui.tracks.StackedBarsTrack.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('tr-ui-e-system-stats-instance-track');this.objectInstance_=null;},set objectInstances(objectInstances){if(!objectInstances){this.objectInstance_=[];return;}
+if(objectInstances.length!==1){throw new Error('Bad object instance count.');}
+this.objectInstance_=objectInstances[0];if(this.objectInstance_!==null){this.computeRates_(this.objectInstance_.snapshots);this.maxStats_=this.computeMaxStats_(this.objectInstance_.snapshots);}},computeRates_(snapshots){for(let i=0;i<snapshots.length;i++){const snapshot=snapshots[i];const stats=snapshot.getStats();let prevSnapshot;if(i===0){prevSnapshot=snapshots[0];}else{prevSnapshot=snapshots[i-1];}
+const prevStats=prevSnapshot.getStats();let timeIntervalSeconds=(snapshot.ts-prevSnapshot.ts)/1000;if(timeIntervalSeconds===0){timeIntervalSeconds=1;}
+this.computeRatesRecursive_(prevStats,stats,timeIntervalSeconds);}},computeRatesRecursive_(prevStats,stats,timeIntervalSeconds){for(const statName in stats){if(stats[statName]instanceof Object){this.computeRatesRecursive_(prevStats[statName],stats[statName],timeIntervalSeconds);}else{if(statName==='sectors_read'){stats.bytes_read_per_sec=(stats.sectors_read-
+prevStats.sectors_read)*512/timeIntervalSeconds;}
+if(statName==='sectors_written'){stats.bytes_written_per_sec=(stats.sectors_written-
+prevStats.sectors_written)*512/timeIntervalSeconds;}
+if(statName==='pgmajfault'){stats.pgmajfault_per_sec=(stats.pgmajfault-
+prevStats.pgmajfault)/timeIntervalSeconds;}
+if(statName==='pswpin'){stats.bytes_swpin_per_sec=(stats.pswpin-
+prevStats.pswpin)*1000/timeIntervalSeconds;}
+if(statName==='pswpout'){stats.bytes_swpout_per_sec=(stats.pswpout-
+prevStats.pswpout)*1000/timeIntervalSeconds;}
+if(statName==='idle_time'){const units=tr.b.convertUnit(100.,tr.b.UnitScale.TIME.NANO_SEC,tr.b.UnitScale.TIME.SEC);const idleTile=(stats.idle_time-prevStats.idle_time)*units;stats.idle_time_per_sec=idleTile/timeIntervalSeconds;}
+if(statName==='read_transfer_count'){const bytesRead=stats.read_transfer_count-
+prevStats.read_transfer_count;stats.bytes_read_per_sec=bytesRead/timeIntervalSeconds;}
+if(statName==='write_transfer_count'){const bytesWritten=stats.write_transfer_count-
+prevStats.write_transfer_count;stats.bytes_written_per_sec=bytesWritten/timeIntervalSeconds;}
+if(statName==='other_transfer_count'){const bytesTransfer=stats.other_transfer_count-
+prevStats.other_transfer_count;stats.bytes_other_per_sec=bytesTransfer/timeIntervalSeconds;}
+if(statName==='read_operation_count'){const readOperation=stats.read_operation_count-
+prevStats.read_operation_count;stats.read_operation_per_sec=readOperation/timeIntervalSeconds;}
+if(statName==='write_operation_count'){const writeOperation=stats.write_operation_count-
+prevStats.write_operation_count;stats.write_operation_per_sec=writeOperation/timeIntervalSeconds;}
+if(statName==='other_operation_count'){const otherOperation=stats.other_operation_count-
+prevStats.other_operation_count;stats.other_operation_per_sec=otherOperation/timeIntervalSeconds;}
+if(statName==='pagefile_pages_written'){const pageFileBytesWritten=(stats.pagefile_pages_written-
+prevStats.pagefile_pages_written)*kPageSizeWindows;stats.pagefile_bytes_written_per_sec=pageFileBytesWritten/timeIntervalSeconds;}
+if(statName==='pagefile_pages_write_ios'){const pagefileWriteOperation=stats.pagefile_pages_write_ios-
+prevStats.pagefile_pages_write_ios;stats.pagefile_write_operation_per_sec=pagefileWriteOperation/timeIntervalSeconds;}
+if(statName==='available_pages'){stats.available_pages_in_bytes=stats.available_pages*kPageSizeWindows;}
+if(statName==='pages_read'){const pagesBytesRead=(stats.pages_read-prevStats.pages_read)*kPageSizeWindows;stats.bytes_read_per_sec=pagesBytesRead/timeIntervalSeconds;}
+if(statName==='page_read_ios'){const pagesBytesReadOperations=stats.page_read_ios-prevStats.page_read_ios;stats.pagefile_write_operation_per_sec=pagesBytesReadOperations/timeIntervalSeconds;}}}},computeMaxStats_(snapshots){const maxStats={};statCount=0;for(let i=0;i<snapshots.length;i++){const snapshot=snapshots[i];const stats=snapshot.getStats();this.computeMaxStatsRecursive_(stats,maxStats,excludedStats);}
+return maxStats;},computeMaxStatsRecursive_(stats,maxStats,excludedStats){for(const statName in stats){if(stats[statName]instanceof Object){if(!(statName in maxStats)){maxStats[statName]={};}
+let excludedNested;if(excludedStats&&statName in excludedStats){excludedNested=excludedStats[statName];}else{excludedNested=null;}
+this.computeMaxStatsRecursive_(stats[statName],maxStats[statName],excludedNested);}else{if(excludedStats&&statName in excludedStats){continue;}
+if(!(statName in maxStats)){maxStats[statName]=0;statCount++;}
+if(stats[statName]>maxStats[statName]){maxStats[statName]=stats[statName];}}}},get height(){return window.getComputedStyle(this).height;},set height(height){this.style.height=height;},draw(type,viewLWorld,viewRWorld,viewHeight){switch(type){case tr.ui.tracks.DrawType.GENERAL_EVENT:this.drawStatBars_(viewLWorld,viewRWorld);break;}},drawStatBars_(viewLWorld,viewRWorld){const ctx=this.context();const pixelRatio=window.devicePixelRatio||1;const bounds=this.getBoundingClientRect();const width=bounds.width*pixelRatio;const height=(bounds.height*pixelRatio)/statCount;const vp=this.viewport.currentDisplayTransform;const maxStats=this.maxStats_;const objectSnapshots=this.objectInstance_.snapshots;let lowIndex=tr.b.findLowIndexInSortedArray(objectSnapshots,function(snapshot){return snapshot.ts;},viewLWorld);if(lowIndex>0)lowIndex-=1;for(let i=lowIndex;i<objectSnapshots.length;++i){const snapshot=objectSnapshots[i];const trace=snapshot.getStats();const currentY=height;const left=snapshot.ts;if(left>viewRWorld)break;let leftView=vp.xWorldToView(left);if(leftView<0)leftView=0;let right;if(i!==objectSnapshots.length-1){right=objectSnapshots[i+1].ts;}else{if(objectSnapshots.length>1){right=objectSnapshots[i].ts+(objectSnapshots[i].ts-
+objectSnapshots[i-1].ts);}else{right=this.objectInstance_.parent.model.bounds.max;}}
+let rightView=vp.xWorldToView(right);if(rightView>width){rightView=width;}
+leftView=Math.floor(leftView);rightView=Math.floor(rightView);this.drawStatBarsRecursive_(snapshot,leftView,rightView,height,trace,maxStats,currentY);if(i===lowIndex){this.drawStatNames_(leftView,height,currentY,'',maxStats);}}
+ctx.lineWidth=1;},drawStatBarsRecursive_(snapshot,leftView,rightView,height,stats,maxStats,currentY){const ctx=this.context();for(const statName in maxStats){if(stats[statName]instanceof Object){currentY=this.drawStatBarsRecursive_(snapshot,leftView,rightView,height,stats[statName],maxStats[statName],currentY);}else{const maxStat=maxStats[statName];ctx.fillStyle=EventPresenter.getBarSnapshotColor(snapshot,Math.round(currentY/height));let barHeight;if(maxStat>0){barHeight=height*Math.max(stats[statName],0)/maxStat;}else{barHeight=0;}
+ctx.fillRect(leftView,currentY-barHeight,Math.max(rightView-leftView,1),barHeight);currentY+=height;}}
+return currentY;},drawStatNames_(leftView,height,currentY,prefix,maxStats){const ctx=this.context();ctx.textAlign='end';ctx.font='12px Arial';ctx.fillStyle='#000000';for(const statName in maxStats){if(maxStats[statName]instanceof Object){currentY=this.drawStatNames_(leftView,height,currentY,statName,maxStats[statName]);}else{let fullname=statName;if(prefix!==''){fullname=prefix+' :: '+statName;}
+ctx.fillText(fullname,leftView-10,currentY-height/4);currentY+=height;}}
+return currentY;}};tr.ui.tracks.ObjectInstanceTrack.register(SystemStatsInstanceTrack,{typeName:'base::TraceEventSystemStatsMonitor::SystemStats'});return{SystemStatsInstanceTrack,};});'use strict';tr.exportTo('tr.ui.e.system_stats',function(){const SystemStatsSnapshotView=tr.ui.b.define('tr-ui-e-system-stats-snapshot-view',tr.ui.analysis.ObjectSnapshotView);SystemStatsSnapshotView.prototype={__proto__:tr.ui.analysis.ObjectSnapshotView.prototype,decorate(){Polymer.dom(this).classList.add('tr-ui-e-system-stats-snapshot-view');},updateContents(){const snapshot=this.objectSnapshot_;if(!snapshot||!snapshot.getStats()){Polymer.dom(this).textContent='No system stats snapshot found.';return;}
+Polymer.dom(this).textContent='';const stats=snapshot.getStats();Polymer.dom(this).appendChild(this.buildList_(stats));},isFloat(n){return typeof n==='number'&&n%1!==0;},buildList_(stats){const statList=document.createElement('ul');for(const statName in stats){const statText=document.createElement('li');Polymer.dom(statText).textContent=''+statName+': ';Polymer.dom(statList).appendChild(statText);if(stats[statName]instanceof Object){Polymer.dom(statList).appendChild(this.buildList_(stats[statName]));}else{if(this.isFloat(stats[statName])){Polymer.dom(statText).textContent+=stats[statName].toFixed(2);}else{Polymer.dom(statText).textContent+=stats[statName];}}}
+return statList;}};tr.ui.analysis.ObjectSnapshotView.register(SystemStatsSnapshotView,{typeName:'base::TraceEventSystemStatsMonitor::SystemStats'});return{SystemStatsSnapshotView,};});'use strict';tr.exportTo('tr.ui.e.v8',function(){const IGNORED_ENTRIES={match:full=>full.startsWith('*CODE_AGE_')};const INSTANCE_TYPE_GROUPS={FIXED_ARRAY_TYPE:{match:full=>full.startsWith('*FIXED_ARRAY_'),realEntry:'FIXED_ARRAY_TYPE',keyToName:key=>key.slice('*FIXED_ARRAY_'.length).slice(0,-('_SUB_TYPE'.length)),nameToKey:name=>'*FIXED_ARRAY_'+name+'_SUB_TYPE'},CODE_TYPE:{match:full=>full.startsWith('*CODE_'),realEntry:'CODE_TYPE',keyToName:key=>key.slice('*CODE_'.length),nameToKey:name=>'*CODE_'+name},JS_OBJECTS:{match:full=>full.startsWith('JS_'),keyToName:key=>key,nameToKey:name=>name},Strings:{match:full=>full.endsWith('STRING_TYPE'),keyToName:key=>key,nameToKey:name=>name},Maps:{match:full=>full.startsWith('MAP_')&&full.endsWith('_TYPE'),keyToName:key=>key,nameToKey:name=>name},DescriptorArrays:{match:full=>full.endsWith('DESCRIPTOR_ARRAY_TYPE'),keyToName:key=>key,nameToKey:name=>name}};const DIFF_COLOR={GREEN:'#64DD17',RED:'#D50000'};function computePercentage(valueA,valueB){if(valueA===0)return 0;return valueA/valueB*100;}
+class DiffEntry{constructor(originalEntry,diffEntry){this.originalEntry_=originalEntry;this.diffEntry_=diffEntry;}
+get title(){return this.diffEntry_.title;}
+get overall(){return this.diffEntry_.overall;}
+get overAllocated(){return this.diffEntry_.overAllocated;}
+get count(){return this.diffEntry_.count;}
+get overallPercent(){return this.diffEntry_.overallPercent;}
+get overAllocatedPercent(){return this.diffEntry_.overAllocatedPercent;}
+get origin(){return this.originalEntry_;}
+get diff(){return this.diffEntry_;}
+get subRows(){return this.diffEntry_.subRows;}}
+class Entry{constructor(title,count,overall,overAllocated,histogram,overAllocatedHistogram){this.title_=title;this.overall_=overall;this.count_=count;this.overAllocated_=overAllocated;this.histogram_=histogram;this.overAllocatedHistogram_=overAllocatedHistogram;this.bucketSize_=this.histogram_.length;this.overallPercent_=100;this.overAllocatedPercent_=100;}
+get title(){return this.title_;}
+get overall(){return this.overall_;}
+get count(){return this.count_;}
+get overAllocated(){return this.overAllocated_;}
+get histogram(){return this.histogram_;}
+get overAllocatedHistogram(){return this.overAllocatedHistogram_;}
+get bucketSize(){return this.bucketSize_;}
+get overallPercent(){return this.overallPercent_;}
+set overallPercent(value){this.overallPercent_=value;}
+get overAllocatedPercent(){return this.overAllocatedPercent_;}
+set overAllocatedPercent(value){this.overAllocatedPercent_=value;}
+setFromObject(obj){this.count_=obj.count;this.overall_=obj.overall/1024;this.overAllocated_=obj.over_allocated/1024;this.histogram_=obj.histogram;this.overAllocatedHistogram_=obj.over_allocated_histogram;}
+diff(other){const entry=new Entry(this.title_,other.count_-this.count,other.overall_-this.overall,other.overAllocated_-this.overAllocated,[],[]);entry.overallPercent=computePercentage(entry.overall,this.overall);entry.overAllocatedPercent=computePercentage(entry.overAllocated,this.overAllocated);return new DiffEntry(this,entry);}}
+class GroupedEntry extends Entry{constructor(title,count,overall,overAllocated,histogram,overAllocatedHistogram){super(title,count,overall,overAllocated,histogram,overAllocatedHistogram);this.histogram_.fill(0);this.overAllocatedHistogram_.fill(0);this.entries_=new Map();}
+get title(){return this.title_;}
+set title(value){this.title_=value;}
+get subRows(){return Array.from(this.entries_.values());}
+getEntryFromTitle(title){return this.entries_.get(title);}
+add(entry){this.count_+=entry.count;this.overall_+=entry.overall;this.overAllocated_+=entry.overAllocated;if(this.bucketSize_===entry.bucketSize){for(let i=0;i<this.bucketSize_;++i){this.histogram_[i]+=entry.histogram[i];this.overAllocatedHistogram_[i]+=entry.overAllocatedHistogram[i];}}
+this.entries_.set(entry.title,entry);}
+accumulateUnknown(title){let unknownCount=this.count_;let unknownOverall=this.overall_;let unknownOverAllocated=this.overAllocated_;const unknownHistogram=tr.b.deepCopy(this.histogram_);const unknownOverAllocatedHistogram=tr.b.deepCopy(this.overAllocatedHistogram_);for(const entry of this.entries_.values()){unknownCount-=entry.count;unknownOverall-=entry.overall;unknownOverAllocated-=entry.overAllocated;for(let i=0;i<this.bucketSize_;++i){unknownHistogram[i]-=entry.histogram[i];unknownOverAllocatedHistogram[i]-=entry.overAllocatedHistogram[i];}}
+unknownOverAllocated=unknownOverAllocated<0?0:unknownOverAllocated;this.entries_.set(title,new Entry(title,unknownCount,unknownOverall,unknownOverAllocated,unknownHistogram,unknownOverAllocatedHistogram));}
+calculatePercentage(){for(const entry of this.entries_.values()){entry.overallPercent=computePercentage(entry.overall,this.overall_);entry.overAllocatedPercent=computePercentage(entry.overAllocated,this.overAllocated_);if(entry instanceof GroupedEntry)entry.calculatePercentage();}}
+diff(other){let newTitle='';if(this.title_.startsWith('Isolate')){newTitle='Total';}else{newTitle=this.title_;}
+const result=new GroupedEntry(newTitle,0,0,0,[],[]);for(const entry of this.entries_){const otherEntry=other.getEntryFromTitle(entry[0]);if(otherEntry===undefined)continue;result.add(entry[1].diff(otherEntry));}
+result.overallPercent=computePercentage(result.overall,this.overall);result.overAllocatedPercent=computePercentage(result.overAllocated,this.overAllocated);return new DiffEntry(this,result);}}
+function createSelector(targetEl,defaultValue,items,callback){const selectorEl=document.createElement('select');selectorEl.addEventListener('change',callback.bind(targetEl));const defaultOptionEl=document.createElement('option');for(let i=0;i<items.length;i++){const item=items[i];const optionEl=document.createElement('option');Polymer.dom(optionEl).textContent=item.label;optionEl.targetPropertyValue=item.value;optionEl.item=item;Polymer.dom(selectorEl).appendChild(optionEl);}
+selectorEl.__defineGetter__('selectedValue',function(v){if(selectorEl.children[selectorEl.selectedIndex]===undefined){return undefined;}
+return selectorEl.children[selectorEl.selectedIndex].targetPropertyValue;});selectorEl.__defineGetter__('selectedItem',function(v){if(selectorEl.children[selectorEl.selectedIndex]===undefined){return undefined;}
+return selectorEl.children[selectorEl.selectedIndex].item;});selectorEl.__defineSetter__('selectedValue',function(v){for(let i=0;i<selectorEl.children.length;i++){const value=selectorEl.children[i].targetPropertyValue;if(value===v){const changed=selectorEl.selectedIndex!==i;if(changed){selectorEl.selectedIndex=i;callback();}
+return;}}
+throw new Error('Not a valid value');});selectorEl.selectedIndex=-1;return selectorEl;}
+function plusMinus(value,toFixed=3){return(value>0?'+':'')+value.toFixed(toFixed);}
+function addArrow(value){if(value===0)return value;if(value===Number.NEGATIVE_INFINITY)return'\u2193\u221E';if(value===Number.POSITIVE_INFINITY)return'\u2191\u221E';return(value>0?'\u2191':'\u2193')+Math.abs(value.toFixed(3));}
+Polymer({is:'tr-ui-e-v8-gc-objects-stats-table',ready(){this.$.diffOption.style.display='none';this.isolateEntries_=[];this.selector1_=undefined;this.selector2_=undefined;},constructDiffTable_(table){this.$.diffTable.selectionMode=tr.ui.b.TableFormat.SelectionMode.ROW;this.$.diffTable.tableColumns=[{title:'Component',value(row){const typeEl=document.createElement('span');typeEl.innerText=row.title;return typeEl;},showExpandButtons:true},{title:'Overall Memory(KB)',value(row){const spanEl=tr.ui.b.createSpan();spanEl.innerText=row.origin.overall.toFixed(3);return spanEl;},cmp(a,b){return a.origin.overall-b.origin.overall;}},{title:'diff(KB)',value(row){const spanEl=tr.ui.b.createSpan();spanEl.innerText=plusMinus(row.overall);if(row.overall>0){spanEl.style.color=DIFF_COLOR.RED;}else if(row.overall<0){spanEl.style.color=DIFF_COLOR.GREEN;}
+return spanEl;},cmp(a,b){return a.overall-b.overall;}},{title:'diff(%)',value(row){const spanEl=tr.ui.b.createSpan();spanEl.innerText=addArrow(row.overallPercent);if(row.overall>0){spanEl.style.color=DIFF_COLOR.RED;}else if(row.overall<0){spanEl.style.color=DIFF_COLOR.GREEN;}
+return spanEl;},cmp(a,b){return a.overall-b.overall;}},{title:'Over Allocated Memory(KB)',value(row){const spanEl=tr.ui.b.createSpan();spanEl.innerText=row.origin.overAllocated.toFixed(3);return spanEl;},cmp(a,b){return a.origin.overAllocated-b.origin.overAllocated;}},{title:'diff(KB)',value(row){const spanEl=tr.ui.b.createSpan();spanEl.innerText=plusMinus(row.overAllocated);if(row.overAllocated>0){spanEl.style.color=DIFF_COLOR.RED;}else if(row.overAllocated<0){spanEl.style.color=DIFF_COLOR.GREEN;}
+return spanEl;},cmp(a,b){return a.overAllocated-b.overAllocated;}},{title:'diff(%)',value(row){const spanEl=tr.ui.b.createSpan();spanEl.innerText=addArrow(row.overAllocatedPercent);if(row.overAllocated>0){spanEl.style.color=DIFF_COLOR.RED;}else if(row.overAllocated<0){spanEl.style.color=DIFF_COLOR.GREEN;}
+return spanEl;},cmp(a,b){return a.overAllocated-b.overAllocated;}},{title:'Count',value(row){const spanEl=tr.ui.b.createSpan();spanEl.innerText=row.origin.count;return spanEl;},cmp(a,b){return a.origin.count-b.origin.count;}},{title:'diff',value(row){const spanEl=tr.ui.b.createSpan();spanEl.innerText=plusMinus(row.count,0);if(row.count>0){spanEl.style.color=DIFF_COLOR.RED;}else if(row.count<0){spanEl.style.color=DIFF_COLOR.GREEN;}
+return spanEl;},cmp(a,b){return a.count-b.count;}},];},buildOptions_(){const items=[];for(const isolateEntry of this.isolateEntries_){items.push({label:isolateEntry.title,value:isolateEntry});}
+this.$.diffOption.style.display='inline-block';this.selector1_=createSelector(this,'',items,this.diffOptionChanged_);Polymer.dom(this.$.diffOption).appendChild(this.selector1_);const spanEl=tr.ui.b.createSpan();spanEl.innerText=' VS ';Polymer.dom(this.$.diffOption).appendChild(spanEl);this.selector2_=createSelector(this,'',items,this.diffOptionChanged_);Polymer.dom(this.$.diffOption).appendChild(this.selector2_);},diffOptionChanged_(){const isolateEntry1=this.selector1_.selectedValue;const isolateEntry2=this.selector2_.selectedValue;if(isolateEntry1===undefined||isolateEntry2===undefined){return;}
+if(isolateEntry1===isolateEntry2){this.$.diffTable.tableRows=[];this.$.diffTable.rebuild();return;}
+this.$.diffTable.tableRows=[isolateEntry1.diff(isolateEntry2)];this.$.diffTable.rebuild();},constructTable_(){this.$.table.selectionMode=tr.ui.b.TableFormat.SelectionMode.ROW;this.$.table.tableColumns=[{title:'Component',value(row){const typeEl=document.createElement('span');typeEl.innerText=row.title;return typeEl;},showExpandButtons:true},{title:'Overall Memory (KB)',value(row){const typeEl=document.createElement('span');typeEl.innerText=row.overall.toFixed(3);return typeEl;},cmp(a,b){return a.overall-b.overall;}},{title:'Over Allocated Memory (KB)',value(row){const typeEl=document.createElement('span');typeEl.innerText=row.overAllocated.toFixed(3);return typeEl;},cmp(a,b){return a.overAllocated-b.overAllocated;}},{title:'Overall Count',value(row){const typeEl=document.createElement('span');typeEl.innerText=row.count;return typeEl;},cmp(a,b){return a.count-b.count;}},{title:'Overall Memory Percent',value(row){const typeEl=document.createElement('span');typeEl.innerText=row.overallPercent.toFixed(3)+'%';return typeEl;},cmp(a,b){return a.overall-b.overall;}},{title:'Overall Allocated Memory Percent',value(row){const typeEl=document.createElement('span');typeEl.innerText=row.overAllocatedPercent.toFixed(3)+'%';return typeEl;},cmp(a,b){return a.overAllocated-b.overAllocated;}}];this.$.table.sortColumnIndex=1;this.$.table.sortDescending=true;},buildSubEntry_(objects,groupEntry,keyToName){const typeGroup=INSTANCE_TYPE_GROUPS[groupEntry.title];for(const instanceType of typeGroup){const e=objects[instanceType];if(e===undefined)continue;delete objects[instanceType];let title=instanceType;if(keyToName!==undefined)title=keyToName(title);groupEntry.add(new Entry(title,e.count,e.overall/1024,e.over_allocated/1024,e.histogram,e.over_allocated_histogram));}},buildUnGroupedEntries_(objects,objectEntry,bucketSize){for(const title of Object.getOwnPropertyNames(objects)){const obj=objects[title];const groupedEntry=new GroupedEntry(title,0,0,0,new Array(bucketSize),new Array(bucketSize));groupedEntry.setFromObject(obj);objectEntry.add(groupedEntry);}},createGroupEntries_(groupEntries,objects,bucketSize){for(const groupName of Object.getOwnPropertyNames(INSTANCE_TYPE_GROUPS)){const groupEntry=new GroupedEntry(groupName,0,0,0,new Array(bucketSize),new Array(bucketSize));if(INSTANCE_TYPE_GROUPS[groupName].realEntry!==undefined){groupEntry.savedRealEntry=objects[INSTANCE_TYPE_GROUPS[groupName].realEntry];delete objects[INSTANCE_TYPE_GROUPS[groupName].realEntry];}
+groupEntries[groupName]=groupEntry;}},buildGroupEntries_(groupEntries,objectEntry){for(const groupName of Object.getOwnPropertyNames(groupEntries)){const groupEntry=groupEntries[groupName];if(groupEntry.savedRealEntry!==undefined){groupEntry.setFromObject(groupEntry.savedRealEntry);groupEntry.accumulateUnknown('UNKNOWN');delete groupEntry.savedRealEntry;}
+objectEntry.add(groupEntry);}},buildSubEntriesForGroups_(groupEntries,objects){for(const instanceType of Object.getOwnPropertyNames(objects)){if(IGNORED_ENTRIES.match(instanceType)){delete objects[instanceType];continue;}
+const e=objects[instanceType];for(const name of Object.getOwnPropertyNames(INSTANCE_TYPE_GROUPS)){const group=INSTANCE_TYPE_GROUPS[name];if(group.match(instanceType)){groupEntries[name].add(new Entry(group.keyToName(instanceType),e.count,e.overall/1024,e.over_allocated/1024,e.histogram,e.over_allocated_histogram));delete objects[instanceType];}}}},build_(objects,objectEntry,bucketSize){delete objects.END;const groupEntries={};this.createGroupEntries_(groupEntries,objects,bucketSize);this.buildSubEntriesForGroups_(groupEntries,objects);this.buildGroupEntries_(groupEntries,objectEntry);this.buildUnGroupedEntries_(objects,objectEntry,bucketSize);},set selection(slices){slices.sortEvents(function(a,b){return b.start-a.start;});const previous=undefined;for(const slice of slices){if(!slice instanceof tr.e.v8.V8GCStatsThreadSlice)continue;const liveObjects=slice.liveObjects;const deadObjects=slice.deadObjects;const isolate=liveObjects.isolate;const isolateEntry=new GroupedEntry('Isolate_'+isolate+' at '+slice.start.toFixed(3)+' ms',0,0,0,[],[]);const liveEntry=new GroupedEntry('live objects',0,0,0,[],[]);const deadEntry=new GroupedEntry('dead objects',0,0,0,[],[]);const liveBucketSize=liveObjects.bucket_sizes.length;const deadBucketSize=deadObjects.bucket_sizes.length;this.build_(tr.b.deepCopy(liveObjects.type_data),liveEntry,liveBucketSize);isolateEntry.add(liveEntry);this.build_(tr.b.deepCopy(deadObjects.type_data),deadEntry,deadBucketSize);isolateEntry.add(deadEntry);isolateEntry.calculatePercentage();this.isolateEntries_.push(isolateEntry);}
+this.updateTable_();if(slices.length>1){this.buildOptions_();this.constructDiffTable_();}},updateTable_(){this.constructTable_();this.$.table.tableRows=this.isolateEntries_;this.$.table.rebuild();},});return{};});'use strict';Polymer({is:'tr-ui-e-multi-v8-gc-stats-thread-slice-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],get selection(){return this.$.content.selection;},set selection(selection){this.$.gcObjectsStats.selection=selection;}});tr.ui.analysis.AnalysisSubView.register('tr-ui-e-multi-v8-gc-stats-thread-slice-sub-view',tr.e.v8.V8GCStatsThreadSlice,{multi:true,title:'V8 GC Stats slices'});'use strict';tr.exportTo('tr.e.v8',function(){const IC_STATS_PROPERTIES=['type','category','scriptName','filePosition','state','isNative','map','propertiesMode','numberOfOwnProperties','instanceType'];class ICStatsEntry{constructor(obj){this.type_=obj.type;if(this.type_.includes('Store')){this.category_='Store';}else if(this.type_.includes('Load')){this.category_='Load';}
+this.state_=obj.state;if(obj.functionName){this.functionName_=obj.optimized?'*':'~';this.functionName_+=obj.functionName.length===0?'(anonymous function)':obj.functionName;}
+this.offset_=obj.offset;this.scriptName_=obj.scriptName?obj.scriptName:'unknown';this.isNative_=obj.scriptName&&obj.scriptName.includes('native');this.lineNum_=obj.lineNum?obj.lineNum:'unknown';this.filePosition_=this.scriptName_+':'+this.lineNum_;if(this.functionName_){this.filePosition_+=' '+this.functionName_+'+'+this.offset_;}
+this.constructor_=obj.constructor?false:true;this.map_=obj.map;if(this.map_){this.propertiesMode_=obj.dict===1?'slow':'fast';}else{this.propertiesMode_='unknown';}
+this.numberOfOwnProperties_=obj.own;this.instanceType_=obj.instanceType;this.key_=obj.key;}
+get type(){return this.type_;}
+get category(){return this.category_;}
+get state(){return this.state_;}
+get functionName(){return this.functionName_;}
+get offset(){return this.offset_;}
+get scriptName(){return this.scriptName_;}
+get isNative(){return this.isNative_;}
+get lineNumber(){return this.lineNum_;}
+get isConstructor(){return this.constructor_;}
+get map(){return this.map_;}
+get propertiesMode(){return this.propertiesMode_;}
+get numberOfOwnProperties(){return this.numberOfOwnProperties_;}
+get instanceType(){return this.instanceType_;}
+get filePosition(){return this.filePosition_;}}
+class ICStatsEntryGroup{constructor(property,key){this.property_=property;this.key_=key;this.percentage_=0;this.entries_=[];this.subGroup_=undefined;}
+static groupBy(groups,entries,property){for(const entry of entries){const key=entry[property];let group=groups.get(key);if(!group){group=new ICStatsEntryGroup(property,key);groups.set(key,group);}
+group.add(entry);}
+for(const group of groups.values()){group.percentage=group.length/entries.length;}}
+add(entry){this.entries_.push(entry);}
+createSubGroup(){if(this.subGroup_)return this.subGroup_;this.subGroup_=new Map();for(const property of IC_STATS_PROPERTIES){if(property===this.property_)continue;const groups=new Map();this.subGroup_.set(property,groups);ICStatsEntryGroup.groupBy(groups,this.entries_,property);}
+return this.subGroup_;}
+get entries(){return this.entries_;}
+get key(){return this.key_;}
+get length(){return this.entries_.length;}
+get percentage(){return this.percentage_;}
+set percentage(value){this.percentage_=value;}}
+class ICStatsCollection{constructor(){this.entries_=[];this.groupedEntries_=new Map();}
+add(entry){this.entries_.push(entry);}
+groupBy(property){if(this.groupedEntries_.has(property)){return Array.from(this.groupedEntries_.get(property).values());}
+const groups=new Map();this.groupedEntries_.set(property,groups);ICStatsEntryGroup.groupBy(groups,this.entries_,property);return Array.from(groups.values());}
+get entries(){return this.entries_;}
+get length(){return this.entries_.length;}}
+return{IC_STATS_PROPERTIES,ICStatsEntry,ICStatsEntryGroup,ICStatsCollection,};});'use strict';tr.exportTo('tr.ui.e.v8',function(){const PROPERTIES=tr.e.v8.IC_STATS_PROPERTIES.map(x=>{return{label:x,value:x};});const ICStatsEntry=tr.e.v8.ICStatsEntry;const ICStatsEntryGroup=tr.e.v8.ICStatsEntryGroup;const ICStatsCollection=tr.e.v8.ICStatsCollection;Polymer({is:'tr-ui-e-v8-ic-stats-table',ready(){this.icStatsCollection_=new ICStatsCollection();this.groupKey_=PROPERTIES[0].value;this.selector_=tr.ui.b.createSelector(this,'groupKey','v8ICStatsGroupKey',this.groupKey_,PROPERTIES);Polymer.dom(this.$.groupOption).appendChild(this.selector_);},get groupKey(){return this.groupKey_;},set groupKey(key){this.groupKey_=key;if(this.icStatsCollection_.length===0)return;this.updateTable_(this.groupKey_);},constructTable_(table,groupKey){table.tableColumns=[{title:'',value:row=>{let expanded=false;const buttonEl=tr.ui.b.createButton('details',function(){const previousSibling=Polymer.dom(this).parentNode.parentNode;const parentNode=previousSibling.parentNode;if(expanded){const trEls=parentNode.getElementsByClassName('subTable');Array.from(trEls).map(x=>x.parentNode.removeChild(x));expanded=false;return;}
+expanded=true;const subGroups=row.createSubGroup();const tr=document.createElement('tr');tr.classList.add('subTable');tr.appendChild(document.createElement('td'));const td=document.createElement('td');td.colSpan=3;for(const subGroup of subGroups){const property=subGroup[0];const all=Array.from(subGroup[1].values());const group=all.slice(0,20);const divEl=document.createElement('div');const spanEl=document.createElement('span');const subTableEl=document.createElement('tr-ui-b-table');spanEl.innerText=`Top 20 out of ${all.length}`;spanEl.style.fontWeight='bold';spanEl.style.fontSize='14px';divEl.appendChild(spanEl);this.constructTable_(subTableEl,property);subTableEl.tableRows=group;subTableEl.rebuild();divEl.appendChild(subTableEl);td.appendChild(divEl);}
+tr.appendChild(td);parentNode.insertBefore(tr,previousSibling.nextSibling);});return buttonEl;}},{title:'Percentage',value(row){const spanEl=document.createElement('span');spanEl.innerText=(row.percentage*100).toFixed(3)+'%';return spanEl;},cmp:(a,b)=>a.percentage-b.percentage},{title:'Count',value(row){const spanEl=document.createElement('span');spanEl.innerText=row.length;return spanEl;},cmp:(a,b)=>a.length-b.length},{title:groupKey,value(row){const spanEl=document.createElement('span');spanEl.innerText=row.key?row.key:'';return spanEl;}}];table.sortColumnIndex=1;table.sortDescending=true;},updateTable_(groupKey){this.constructTable_(this.$.table,groupKey);this.$.table.tableRows=this.icStatsCollection_.groupBy(groupKey);this.$.table.rebuild();},set selection(slices){for(const slice of slices){for(const icStatsObj of slice.icStats){const entry=new ICStatsEntry(icStatsObj);this.icStatsCollection_.add(entry);}}
+this.$.total.innerText='Total items: '+this.icStatsCollection_.length;this.updateTable_(this.selector_.selectedValue);}});return{};});'use strict';Polymer({is:'tr-ui-e-multi-v8-ic-stats-thread-slice-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],get selection(){return this.$.content.selection;},set selection(selection){this.$.table.selection=selection;}});tr.ui.analysis.AnalysisSubView.register('tr-ui-e-multi-v8-ic-stats-thread-slice-sub-view',tr.e.v8.V8ICStatsThreadSlice,{multi:true,title:'V8 IC stats slices'});'use strict';tr.exportTo('tr.e.v8',function(){class RuntimeStatsEntry{constructor(name,count,time){this.name_=name;this.count_=count;this.time_=time;}
+get name(){return this.name_;}
+get count(){return this.count_;}
+get time(){return this.time_;}
+addSample(count,time){this.count_+=count;this.time_+=time;}}
+class RuntimeStatsGroup extends RuntimeStatsEntry{constructor(name,matchRegex){super(name,0,0);this.regex_=matchRegex;this.entries_=new Map();}
+match(name){return this.regex_&&name.match(this.regex_);}
+add(entry){const value=this.entries_.get(entry.name);if(value!==undefined){value.addSample(entry.count,entry.time);}else{this.entries_.set(entry.name,entry);}
+this.count_+=entry.count;this.time_+=entry.time;}
+get values(){return Array.from(this.entries_.values());}}
+class RuntimeStatsGroupCollection{constructor(){this.blink_cpp_group_=new RuntimeStatsGroup('Blink C++',/.*Callback.*/);this.api_group_=new RuntimeStatsGroup('API',/.*API.*/);this.groups_=[new RuntimeStatsGroup('Total'),new RuntimeStatsGroup('IC',/.*IC_.*/),new RuntimeStatsGroup('Optimize-Background',/(.*OptimizeBackground.*)|RecompileConcurrent.*/),new RuntimeStatsGroup('Optimize',/StackGuard|.*Optimize.*|.*Deoptimize.*|Recompile.*/),new RuntimeStatsGroup('Compile-Background',/(.*CompileBackground.*)/),new RuntimeStatsGroup('Compile',/(^Compile.*)|(.*_Compile.*)/),new RuntimeStatsGroup('Parse-Background',/.*ParseBackground.*/),new RuntimeStatsGroup('Parse',/.*Parse.*/),this.blink_cpp_group_,this.api_group_,new RuntimeStatsGroup('GC-Background-Marking',/.*GC.MC.BACKGROUND.*MARKING.*/),new RuntimeStatsGroup('GC-Background-Sweeping',/.*GC.MC.BACKGROUND.*SWEEPING.*/),new RuntimeStatsGroup('GC-Background-Scavenger',/.*GC.SCAVENGER.BACKGROUND.*/),new RuntimeStatsGroup('GC-Background-MinorMC',/.*GC.MINOR_MC.BACKGROUND.*/),new RuntimeStatsGroup('GC-Background-MajorMC',/.*GC.MC.BACKGROUND.*/),new RuntimeStatsGroup('GC-Background-Other',/.*GC.*BACKGROUND.*/),new RuntimeStatsGroup('GC',/GC|AllocateInTargetSpace/),new RuntimeStatsGroup('JavaScript',/JS_Execution/),new RuntimeStatsGroup('V8 C++',/.*/)];this.blink_group_collection_=null;}
+addSlices(slices){const blinkEntries=[];for(const slice of slices){if(!(slice instanceof tr.e.v8.V8ThreadSlice))return;let runtimeCallStats;try{runtimeCallStats=JSON.parse(slice.runtimeCallStats);}catch(e){runtimeCallStats=slice.runtimeCallStats;}
+if(runtimeCallStats===undefined)continue;for(const[name,stat]of Object.entries(runtimeCallStats)){if(name.match(/Blink_.*/)){if(name==='Blink_V8')continue;const entry=new RuntimeStatsEntry(name,stat[0],stat[1]);blinkEntries.push(entry);continue;}
+for(let i=1;i<this.groups_.length;++i){if(this.groups_[i].match(name)){if(stat.length!==2)break;const entry=new RuntimeStatsEntry(name,stat[0],stat[1]);this.groups_[0].addSample(stat[0],stat[1]);this.groups_[i].add(entry);break;}}}}
+this.blink_group_collection_=new BlinkRuntimeStatsGroupCollection(blinkEntries);}
+get totalTime(){return this.groups_[0].time;}
+get totalCount(){return this.groups_[0].count;}
+get runtimeGroups(){return this.groups_;}
+get blinkRCSGroupCollection(){return this.blink_group_collection_;}
+get blinkCppTotalTime(){return this.blink_cpp_group_.time+this.api_group_.time;}}
+class BlinkRuntimeStatsGroupCollection{constructor(entries){this.groups_=[new RuntimeStatsGroup('Blink_Bindings',/^Blink_Bindings_(.*)/),new RuntimeStatsGroup('Blink_GC',/^Blink_GC_(.*)/),new RuntimeStatsGroup('Blink_Layout',/^Blink_Layout_(.*)/),new RuntimeStatsGroup('Blink_Parsing',/^Blink_Parsing_(.*)/),new RuntimeStatsGroup('Blink_Style',/^Blink_Style_(.*)/),new RuntimeStatsGroup('Blink_Callbacks',/^Blink_(.*)/)];this.total_group_=new RuntimeStatsGroup('Blink_Total',/.*/);for(const entry of entries){for(const group of this.groups_){if(group.match(entry.name)){const newEntry=new RuntimeStatsEntry('Blink_'+group.match(entry.name)[1],entry.count,entry.time);group.add(newEntry);this.total_group_.addSample(entry.count,entry.time);break;}}}}
+get runtimeGroups(){return this.groups_.concat(this.total_group_);}
+get values(){return this.groups_.reduce((values,group)=>values.concat(group.values),[]);}
+get totalTime(){return this.total_group_.time;}
+get totalCount(){return this.total_group_.count;}}
+return{BlinkRuntimeStatsGroupCollection,RuntimeStatsEntry,RuntimeStatsGroup,RuntimeStatsGroupCollection,};});'use strict';tr.exportTo('tr.ui.e.v8',function(){const codeSearchURL_='https://cs.chromium.org/search/?sq=package:chromium&type=cs&q=';function removeBlinkPrefix_(name){if(name.startsWith('Blink_'))name=name.substring(6);return name;}
+function handleCodeSearchForV8_(event){if(event.target.parentNode===undefined)return;let name=event.target.parentNode.entryName;if(name.startsWith('API_'))name=name.substring(4);const url=codeSearchURL_+encodeURIComponent(name)+'+file:src/v8/src';window.open(url,'_blank');}
+function handleCodeSearchForBlink_(event){if(event.target.parentNode===undefined)return;const name=event.target.parentNode.entryName;const url=codeSearchURL_+
+encodeURIComponent('RuntimeCallStats::CounterId::k'+name)+'+file:src/third_party/WebKit/|src/out/Debug/';window.open(url,'_blank');}
+function createCodeSearchEl_(handleCodeSearch){const codeSearchEl=document.createElement('span');codeSearchEl.innerText='?';codeSearchEl.style.float='right';codeSearchEl.style.borderRadius='5px';codeSearchEl.style.backgroundColor='#EEE';codeSearchEl.addEventListener('click',handleCodeSearch.bind(this));return codeSearchEl;}
+const timeColumn_={title:'Time',value(row){const typeEl=document.createElement('span');typeEl.innerText=(row.time/1000.0).toFixed(3)+' ms';return typeEl;},width:'100px',cmp(a,b){return a.time-b.time;}};const countColumn_={title:'Count',value(row){const typeEl=document.createElement('span');typeEl.innerText=row.count;return typeEl;},width:'100px',cmp(a,b){return a.count-b.count;}};function percentColumn_(title,totalTime){return{title,value(row){const typeEl=document.createElement('span');typeEl.innerText=(row.time/totalTime*100).toFixed(3)+'%';return typeEl;},width:'100px',cmp(a,b){return a.time-b.time;}};}
+function nameColumn_(handleCodeSearch,modifyName){return{title:'Name',value(row){const typeEl=document.createElement('span');let name=row.name;if(modifyName)name=modifyName(name);typeEl.innerText=name;if(!(row instanceof tr.e.v8.RuntimeStatsGroup)){typeEl.title='click ? for code search';typeEl.entryName=name;const codeSearchEl=createCodeSearchEl_(handleCodeSearch);typeEl.appendChild(codeSearchEl);}
+return typeEl;},width:'200px',showExpandButtons:true};}
+function initializeCommonOptions_(table){table.selectionMode=tr.ui.b.TableFormat.SelectionMode.ROW;table.sortColumnIndex=1;table.sortDescending=true;table.subRowsPropertyName='values';}
+Polymer({is:'tr-ui-e-v8-runtime-call-stats-table',ready(){this.table_=this.$.table;this.blink_rcs_table_=this.$.blink_rcs_table;this.totalTime_=0;},constructV8RCSTable_(totalTime){this.table_.tableColumns=[nameColumn_(handleCodeSearchForV8_),timeColumn_,countColumn_,percentColumn_('Percent',totalTime)];initializeCommonOptions_(this.table_);},constructBlinkRCSTable_(blinkCppTotalTime){this.blink_rcs_table_.tableColumns=[nameColumn_(handleCodeSearchForBlink_,removeBlinkPrefix_),timeColumn_,countColumn_,percentColumn_('Percent (of \'Blink C++\' + \'API\')',blinkCppTotalTime)];initializeCommonOptions_(this.blink_rcs_table_);},set slices(slices){const runtimeGroupCollection=new tr.e.v8.RuntimeStatsGroupCollection();runtimeGroupCollection.addSlices(slices);if(runtimeGroupCollection.totalTime>0){this.$.v8_rcs_heading.textContent='V8 Runtime Call Stats';this.constructV8RCSTable_(runtimeGroupCollection.totalTime);this.table_.tableRows=runtimeGroupCollection.runtimeGroups;this.table_.rebuild();}
+const blinkRCSGroupCollection=runtimeGroupCollection.blinkRCSGroupCollection;if(runtimeGroupCollection.blinkCppTotalTime>0&&blinkRCSGroupCollection.totalTime>0){this.$.blink_rcs_heading.textContent='Blink Runtime Call Stats';this.constructBlinkRCSTable_(runtimeGroupCollection.blinkCppTotalTime);this.blink_rcs_table_.tableRows=blinkRCSGroupCollection.runtimeGroups;this.blink_rcs_table_.rebuild();}}});return{};});'use strict';Polymer({is:'tr-ui-e-multi-v8-thread-slice-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],get selection(){return this.$.content.selection;},set selection(selection){this.$.runtimeCallStats.slices=selection;this.$.content.selection=selection;}});tr.ui.analysis.AnalysisSubView.register('tr-ui-e-multi-v8-thread-slice-sub-view',tr.e.v8.V8ThreadSlice,{multi:true,title:'V8 slices'});'use strict';Polymer({is:'tr-ui-e-single-v8-gc-stats-thread-slice-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],get selection(){return this.$.content.selection;},set selection(selection){this.$.content.selection=selection;this.$.gcObjectsStats.selection=selection;}});tr.ui.analysis.AnalysisSubView.register('tr-ui-e-single-v8-gc-stats-thread-slice-sub-view',tr.e.v8.V8GCStatsThreadSlice,{multi:false,title:'V8 GC stats slice'});'use strict';Polymer({is:'tr-ui-e-single-v8-ic-stats-thread-slice-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],get selection(){return this.$.content.selection;},set selection(selection){this.$.table.selection=selection;}});tr.ui.analysis.AnalysisSubView.register('tr-ui-e-single-v8-ic-stats-thread-slice-sub-view',tr.e.v8.V8ICStatsThreadSlice,{multi:false,title:'V8 IC stats slice'});'use strict';Polymer({is:'tr-ui-e-single-v8-thread-slice-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],get selection(){return this.$.content.selection;},set selection(selection){this.$.runtimeCallStats.slices=selection;this.$.content.selection=selection;}});tr.ui.analysis.AnalysisSubView.register('tr-ui-e-single-v8-thread-slice-sub-view',tr.e.v8.V8ThreadSlice,{multi:false,title:'V8 slice'});'use strict';tr.exportTo('tr.c',function(){function ScriptingObject(){}
+ScriptingObject.prototype={onModelChanged(model){}};return{ScriptingObject,};});'use strict';tr.exportTo('tr.c',function(){function ScriptingController(brushingStateController){this.brushingStateController_=brushingStateController;this.scriptObjectNames_=[];this.scriptObjectValues_=[];this.brushingStateController.addEventListener('model-changed',this.onModelChanged_.bind(this));const typeInfos=ScriptingObjectRegistry.getAllRegisteredTypeInfos();typeInfos.forEach(function(typeInfo){this.addScriptObject(typeInfo.metadata.name,typeInfo.constructor);global[typeInfo.metadata.name]=typeInfo.constructor;},this);}
+function ScriptingObjectRegistry(){}
+const options=new tr.b.ExtensionRegistryOptions(tr.b.BASIC_REGISTRY_MODE);tr.b.decorateExtensionRegistry(ScriptingObjectRegistry,options);ScriptingController.prototype={get brushingStateController(){return this.brushingStateController_;},onModelChanged_(){this.scriptObjectValues_.forEach(function(v){if(v.onModelChanged){v.onModelChanged(this.brushingStateController.model);}},this);},addScriptObject(name,value){this.scriptObjectNames_.push(name);this.scriptObjectValues_.push(value);},executeCommand(command){const f=new Function(this.scriptObjectNames_,'return eval('+command+')');return f.apply(null,this.scriptObjectValues_);}};return{ScriptingController,ScriptingObjectRegistry,};});'use strict';tr.exportTo('tr.metrics',function(){function MetricRegistry(){}
+const options=new tr.b.ExtensionRegistryOptions(tr.b.BASIC_REGISTRY_MODE);options.defaultMetadata={};tr.b.decorateExtensionRegistry(MetricRegistry,options);function camelCaseToHackerString(camelCase){let hackerString='';for(const c of camelCase){const lowered=c.toLocaleLowerCase();if(lowered===c){hackerString+=c;}else{hackerString+='_'+lowered;}}
+return hackerString;}
+function getCallStack(){try{throw new Error();}catch(error){return error.stack;}}
+function getPathsFromStack(stack){return stack.split('\n').map(line=>{line=line.replace(/^ */,'').split(':');if(line.length<4)return'';return line[line.length-3].split('/');}).filter(x=>x);}
+MetricRegistry.checkFilename=function(metricName,opt_metricPathForTest){if(metricName==='runtimeStatsTotalMetric'||metricName==='v8AndMemoryMetrics'){return;}
+const expectedFilename=camelCaseToHackerString(metricName)+'.html';const stack=getCallStack();let metricPath=opt_metricPathForTest;if(metricPath===undefined){const paths=getPathsFromStack(stack);const METRIC_STACK_INDEX=5;if(paths.length<=METRIC_STACK_INDEX||paths[METRIC_STACK_INDEX].join('/')===paths[0].join('/')){return;}
+metricPath=paths[METRIC_STACK_INDEX].slice(paths[METRIC_STACK_INDEX].length-2);}
+if(!metricPath[1].endsWith('_test.html')&&!metricPath[1].endsWith('_test.html.js')&&metricPath[1]!==expectedFilename&&metricPath[1]!==expectedFilename+'.js'&&metricPath.join('_')!==expectedFilename&&metricPath.join('_')!==expectedFilename+'.js'){throw new Error('Expected '+metricName+' to be in a file named '+
+expectedFilename+'; actual: '+metricPath.join('/')+'; stack: '+stack.replace(/\n/g,'\n  '));}};MetricRegistry.addEventListener('will-register',function(e){const metric=e.typeInfo.constructor;if(!(metric instanceof Function)){throw new Error('Metrics must be functions.');}
+if(!metric.name.endsWith('Metric')&&!metric.name.endsWith('Metrics')){throw new Error('Metric names must end with "Metric" or "Metrics".');}
+if(metric.length<2){throw new Error('Metrics take a HistogramSet and a Model and '+'optionally an options dictionary.');}
+MetricRegistry.checkFilename(metric.name);});return{MetricRegistry,};});'use strict';tr.exportTo('tr.metrics',function(){function accessibilityMetric(histograms,model){const browserAccessibilityEventsHist=new tr.v.Histogram('browser_accessibility_events',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter);browserAccessibilityEventsHist.description='Browser accessibility events time';const renderAccessibilityEventsHist=new tr.v.Histogram('render_accessibility_events',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter);renderAccessibilityEventsHist.description='Render accessibility events time';const renderAccessibilityLocationsHist=new tr.v.Histogram('render_accessibility_locations',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter);renderAccessibilityLocationsHist.description='Render accessibility locations time';const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);if(chromeHelper===undefined)return;for(const rendererHelper of Object.values(chromeHelper.rendererHelpers)){const mainThread=rendererHelper.mainThread;if(mainThread===undefined)continue;for(const slice of mainThread.getDescendantEvents()){if(!(slice instanceof tr.model.ThreadSlice))continue;if(slice.title==='RenderAccessibilityImpl::SendPendingAccessibilityEvents'){renderAccessibilityEventsHist.addSample(slice.duration,{event:new tr.v.d.RelatedEventSet(slice)});}
+if(slice.title==='RenderAccessibilityImpl::SendLocationChanges'){renderAccessibilityLocationsHist.addSample(slice.duration,{event:new tr.v.d.RelatedEventSet(slice)});}}}
+for(const browserHelper of Object.values(chromeHelper.browserHelpers)){const mainThread=browserHelper.mainThread;if(mainThread===undefined)continue;for(const slice of mainThread.getDescendantEvents()){if(slice.title==='BrowserAccessibilityManager::OnAccessibilityEvents'){browserAccessibilityEventsHist.addSample(slice.duration,{event:new tr.v.d.RelatedEventSet(slice)});}}}
+histograms.addHistogram(browserAccessibilityEventsHist);histograms.addHistogram(renderAccessibilityEventsHist);histograms.addHistogram(renderAccessibilityLocationsHist);}
+tr.metrics.MetricRegistry.register(accessibilityMetric);return{accessibilityMetric,};});'use strict';tr.exportTo('tr.metrics.sh',function(){const MESSAGE_LOOP_EVENT_NAME='Startup.BrowserMessageLoopStartTime';const CONTENT_START_EVENT_NAME='content::Start';const NAVIGATION_EVENT_NAME='Navigation StartToCommit';const FIRST_CONTENTFUL_PAINT_EVENT_NAME='firstContentfulPaint';function androidStartupMetric(histograms,model){let messageLoopStartEvents=[];let navigationEvents=[];const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);if(!chromeHelper)return;for(const helper of chromeHelper.browserHelpers){for(const ev of helper.mainThread.asyncSliceGroup.childEvents()){if(ev.title===MESSAGE_LOOP_EVENT_NAME){messageLoopStartEvents.push(ev);}else if(ev.title===NAVIGATION_EVENT_NAME){navigationEvents.push(ev);}}}
+let contentStartEvents=[];let firstContentfulPaintEvents=[];const rendererHelpers=chromeHelper.rendererHelpers;const pids=Object.keys(rendererHelpers);for(const rendererHelper of Object.values(chromeHelper.rendererHelpers)){if(!rendererHelper.mainThread)continue;for(const ev of rendererHelper.mainThread.sliceGroup.childEvents()){if(ev.title===FIRST_CONTENTFUL_PAINT_EVENT_NAME){firstContentfulPaintEvents.push(ev);break;}else if(ev.title===CONTENT_START_EVENT_NAME){contentStartEvents.push(ev);}}}
+let totalBrowserStarts=messageLoopStartEvents.length;let totalContentStartEvents=contentStartEvents.length;let totalFcpEvents=firstContentfulPaintEvents.length;let totalNavigations=navigationEvents.length;if(totalFcpEvents!==totalBrowserStarts||totalNavigations!==totalBrowserStarts||totalContentStartEvents!==totalBrowserStarts||totalBrowserStarts===0){messageLoopStartEvents=[];contentStartEvents=[];navigationEvents=[];firstContentfulPaintEvents=[];for(const proc of Object.values(model.processes)){for(const ev of proc.getDescendantEvents()){if(ev.title===MESSAGE_LOOP_EVENT_NAME){messageLoopStartEvents.push(ev);}else if(ev.title===NAVIGATION_EVENT_NAME){navigationEvents.push(ev);}else if(ev.title===CONTENT_START_EVENT_NAME){contentStartEvents.push(ev);}}
+for(const ev of proc.getDescendantEvents()){if(ev.title===FIRST_CONTENTFUL_PAINT_EVENT_NAME){firstContentfulPaintEvents.push(ev);break;}}}
+totalBrowserStarts=messageLoopStartEvents.length;totalContentStartEvents=contentStartEvents.length;totalNavigations=navigationEvents.length;totalFcpEvents=firstContentfulPaintEvents.length;}
+function orderEvents(event1,event2){return event1.start-event2.start;}
+messageLoopStartEvents.sort(orderEvents);contentStartEvents.sort(orderEvents);navigationEvents.sort(orderEvents);firstContentfulPaintEvents.sort(orderEvents);if(totalFcpEvents<totalBrowserStarts){throw new Error('Found fewer FCP events ('+totalFcpEvents+') than browser starts ('+totalBrowserStarts+')');}
+const messageLoopStartHistogram=histograms.createHistogram('messageloop_start_time',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,[]);const contentStartHistogram=histograms.createHistogram('experimental_content_start_time',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,[]);const navigationStartHistogram=histograms.createHistogram('experimental_navigation_start_time',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,[]);const navigationCommitHistogram=histograms.createHistogram('navigation_commit_time',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,[]);const firstContentfulPaintHistogram=histograms.createHistogram('first_contentful_paint_time',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,[]);let contentIndex=0;let navIndex=0;let fcpIndex=0;for(let loopStartIndex=0;loopStartIndex<totalBrowserStarts;){const startEvent=messageLoopStartEvents[loopStartIndex];if(fcpIndex===totalFcpEvents){break;}
+const contentStartEvent=contentIndex<contentStartEvents.length?contentStartEvents[contentIndex]:null;if(contentStartEvent&&contentStartEvent.start<startEvent.start){contentIndex++;continue;}
+const navEvent=navIndex<navigationEvents.length?navigationEvents[navIndex]:null;if(navEvent&&navEvent.start<startEvent.start){navIndex++;continue;}
+const fcpEvent=firstContentfulPaintEvents[fcpIndex];if(fcpEvent.start<startEvent.start){fcpIndex++;continue;}
+loopStartIndex++;if(fcpIndex<2){continue;}
+messageLoopStartHistogram.addSample(startEvent.duration,{events:new tr.v.d.RelatedEventSet([startEvent])});if(contentStartEvent){contentStartHistogram.addSample(contentStartEvent.start-startEvent.start,{events:new tr.v.d.RelatedEventSet([startEvent,contentStartEvent])});}
+if(navEvent){navigationStartHistogram.addSample(navEvent.start-startEvent.start,{events:new tr.v.d.RelatedEventSet([startEvent,navEvent])});navigationCommitHistogram.addSample(navEvent.end-startEvent.start,{events:new tr.v.d.RelatedEventSet([startEvent,navEvent])});}
+firstContentfulPaintHistogram.addSample(fcpEvent.end-startEvent.start,{events:new tr.v.d.RelatedEventSet([startEvent,fcpEvent])});}}
+tr.metrics.MetricRegistry.register(androidStartupMetric);return{androidStartupMetric,};});'use strict';tr.exportTo('tr.metrics.sh',function(){const MAX_INPUT_EVENT_TO_STARTUP_DELAY_IN_MS=2000;const MIN_DRAW_DELAY_IN_MS=80;const MAX_DRAW_DELAY_IN_MS=2000;function findProcess(processName,model){for(const pid in model.processes){const process=model.processes[pid];if(process.name===processName){return process;}}
+return undefined;}
+function findThreads(process,threadPrefix){if(process===undefined)return undefined;const threads=[];for(const tid in process.threads){const thread=process.threads[tid];if(thread.name.startsWith(threadPrefix)){threads.push(thread);}}
+return threads;}
+function findUIThread(process){if(process===undefined)return undefined;const threads=findThreads(process,'UI Thread');if(threads!==undefined&&threads.length===1){return threads[0];}
+return process.threads[process.pid];}
+function findLaunchSlices(model){const launches=[];const binders=findThreads(findProcess('system_server',model),'Binder');for(const binderId in binders){const binder=binders[binderId];for(const sliceId in binder.asyncSliceGroup.slices){const slice=binder.asyncSliceGroup.slices[sliceId];if(slice.title.startsWith('launching:')){launches.push(slice);}}}
+return launches;}
+function findDrawSlice(appName,startNotBefore,model){let drawSlice=undefined;const thread=findUIThread(findProcess(appName,model));if(thread===undefined)return undefined;for(const sliceId in thread.sliceGroup.slices){const slice=thread.sliceGroup.slices[sliceId];if(slice.start<startNotBefore+MIN_DRAW_DELAY_IN_MS||slice.start>startNotBefore+MAX_DRAW_DELAY_IN_MS)continue;if(slice.title!=='draw')continue;if(drawSlice===undefined||slice.start<drawSlice.start){drawSlice=slice;}}
+return drawSlice;}
+function findInputEventSlice(endNotAfter,model){const endNotBefore=endNotAfter-MAX_INPUT_EVENT_TO_STARTUP_DELAY_IN_MS;let inputSlice=undefined;const systemUi=findUIThread(findProcess('com.android.systemui',model));if(systemUi===undefined)return undefined;for(const sliceId in systemUi.asyncSliceGroup.slices){const slice=systemUi.asyncSliceGroup.slices[sliceId];if(slice.end>endNotAfter||slice.end<endNotBefore)continue;if(slice.title!=='deliverInputEvent')continue;if(inputSlice===undefined||slice.end>inputSlice.end){inputSlice=slice;}}
+return inputSlice;}
+function computeStartupTimeInMs(appName,launchSlice,model){let startupStart=launchSlice.start;let startupEnd=launchSlice.end;const drawSlice=findDrawSlice(appName,launchSlice.end,model);if(drawSlice!==undefined){startupEnd=drawSlice.end;}
+const inputSlice=findInputEventSlice(launchSlice.start,model);if(inputSlice!==undefined){startupStart=inputSlice.start;}
+return startupEnd-startupStart;}
+function measureStartup(histograms,model){const launches=findLaunchSlices(model);for(const sliceId in launches){const launchSlice=launches[sliceId];const appName=launchSlice.title.split(': ')[1];const startupMs=computeStartupTimeInMs(appName,launchSlice,model);histograms.createHistogram(`android:systrace:startup:${appName}`,tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,startupMs);}}
+function measureThreadStates(histograms,model,rangeOfInterest){for(const pid in model.processes){const process=model.processes[pid];if(process.name===undefined)continue;let hasSlices=false;let timeRunning=0;let timeRunnable=0;let timeSleeping=0;let timeUninterruptible=0;let timeBlockIO=0;let timeUnknown=0;for(const tid in process.threads){const thread=process.threads[tid];if(thread.timeSlices===undefined)continue;for(const sliceId in thread.timeSlices){const slice=thread.timeSlices[sliceId];const sliceRange=tr.b.math.Range.fromExplicitRange(slice.start,slice.end);const intersection=rangeOfInterest.findIntersection(sliceRange);const duration=intersection.duration;if(duration===0)continue;hasSlices=true;if(slice.title==='Running'){timeRunning+=duration;}else if(slice.title==='Runnable'){timeRunnable+=duration;}else if(slice.title==='Sleeping'){timeSleeping+=duration;}else if(slice.title.startsWith('Uninterruptible')){timeUninterruptible+=duration;if(slice.title.includes('Block I/O'))timeBlockIO+=duration;}else{timeUnknown+=duration;}}}
+if(hasSlices){const wall=rangeOfInterest.max-rangeOfInterest.min;histograms.createHistogram(`android:systrace:threadtime:${process.name}:running`,tr.b.Unit.byName.normalizedPercentage,timeRunning/wall);histograms.createHistogram(`android:systrace:threadtime:${process.name}:runnable`,tr.b.Unit.byName.normalizedPercentage,timeRunnable/wall);histograms.createHistogram(`android:systrace:threadtime:${process.name}:sleeping`,tr.b.Unit.byName.normalizedPercentage,timeSleeping/wall);histograms.createHistogram(`android:systrace:threadtime:${process.name}:blockio`,tr.b.Unit.byName.normalizedPercentage,timeBlockIO/wall);histograms.createHistogram(`android:systrace:threadtime:${process.name}:uninterruptible`,tr.b.Unit.byName.normalizedPercentage,timeUninterruptible/wall);if(timeUnknown>0){histograms.createHistogram(`android:systrace:threadtime:${process.name}:unknown`,tr.b.Unit.byName.normalizedPercentage,timeUnknown/wall);}}}}
+function androidSystraceMetric(histograms,model,options){let rangeOfInterest=model.bounds;if(options!==undefined&&options.rangeOfInterest!==undefined){rangeOfInterest=options.rangeOfInterest;}
+measureStartup(histograms,model);measureThreadStates(histograms,model,rangeOfInterest);}
+tr.metrics.MetricRegistry.register(androidSystraceMetric,{supportsRangeOfInterest:true});return{androidSystraceMetric,};});'use strict';tr.exportTo('tr.b.math',function(){const PERCENTILE_PRECISION=1e-7;function PiecewiseLinearFunction(){this.pieces=[];}
+PiecewiseLinearFunction.prototype={push(x1,y1,x2,y2){if(x1>=x2){throw new Error('Invalid segment');}
+if(this.pieces.length>0&&this.pieces[this.pieces.length-1].x2>x1){throw new Error('Potentially overlapping segments');}
+if(x1<x2){this.pieces.push(new Piece(x1,y1,x2,y2));}},partBelow(y){return this.pieces.reduce((acc,p)=>(acc+p.partBelow(y)),0);},get min(){return this.pieces.reduce((acc,p)=>Math.min(acc,p.min),Infinity);},get max(){return this.pieces.reduce((acc,p)=>Math.max(acc,p.max),-Infinity);},get average(){let weightedSum=0;let totalWeight=0;this.pieces.forEach(function(piece){weightedSum+=piece.width*piece.average;totalWeight+=piece.width;});if(totalWeight===0)return 0;return weightedSum/totalWeight;},percentile(percent){if(!(percent>=0&&percent<=1)){throw new Error('percent must be [0,1]');}
+let lower=this.min;let upper=this.max;const total=this.partBelow(upper);if(total===0)return 0;while(upper-lower>PERCENTILE_PRECISION){const middle=(lower+upper)/2;const below=this.partBelow(middle);if(below/total<percent){lower=middle;}else{upper=middle;}}
+return(lower+upper)/2;}};function Piece(x1,y1,x2,y2){this.x1=x1;this.y1=y1;this.x2=x2;this.y2=y2;}
+Piece.prototype={partBelow(y){const width=this.width;if(width===0)return 0;const minY=this.min;const maxY=this.max;if(y>=maxY)return width;if(y<minY)return 0;return(y-minY)/(maxY-minY)*width;},get min(){return Math.min(this.y1,this.y2);},get max(){return Math.max(this.y1,this.y2);},get average(){return(this.y1+this.y2)/2;},get width(){return this.x2-this.x1;}};return{PiecewiseLinearFunction,};});'use strict';tr.exportTo('tr.metrics.v8.utils',function(){const IDLE_TASK_EVENT='SingleThreadIdleTaskRunner::RunTask';const V8_EXECUTE='V8.Execute';const GC_EVENT_PREFIX='V8.GC';const FULL_GC_EVENT='V8.GCCompactor';const LOW_MEMORY_EVENT='V8.GCLowMemoryNotification';const MAJOR_GC_EVENT='MajorGC';const MINOR_GC_EVENT='MinorGC';const TOP_GC_EVENTS={'V8.GCCompactor':'v8-gc-full-mark-compactor','V8.GCFinalizeMC':'v8-gc-latency-mark-compactor','V8.GCFinalizeMCReduceMemory':'v8-gc-memory-mark-compactor','V8.GCIncrementalMarking':'v8-gc-incremental-step','V8.GCIncrementalMarkingFinalize':'v8-gc-incremental-finalize','V8.GCIncrementalMarkingStart':'v8-gc-incremental-start','V8.GCPhantomHandleProcessingCallback':'v8-gc-phantom-handle-callback','V8.GCScavenger':'v8-gc-scavenger'};const MARK_COMPACTOR_EVENTS=new Set(['V8.GCCompactor','V8.GCFinalizeMC','V8.GCFinalizeMCReduceMemory','V8.GCIncrementalMarking','V8.GCIncrementalMarkingFinalize','V8.GCIncrementalMarkingStart','V8.GCPhantomHandleProcessingCallback']);const LOW_MEMORY_MARK_COMPACTOR='v8-gc-low-memory-mark-compactor';function findParent(event,predicate){let parent=event.parentSlice;while(parent){if(predicate(parent)){return parent;}
+parent=parent.parentSlice;}
+return null;}
+function isIdleTask(event){return event.title===IDLE_TASK_EVENT;}
+function isLowMemoryEvent(event){return event.title===LOW_MEMORY_EVENT;}
+function isV8Event(event){return event.title.startsWith('V8.');}
+function isV8ExecuteEvent(event){return event.title===V8_EXECUTE;}
+function isTopV8ExecuteEvent(event){return isV8ExecuteEvent(event)&&findParent(isV8ExecuteEvent)===null;}
+function isGarbageCollectionEvent(event){return event.title&&event.title.startsWith(GC_EVENT_PREFIX)&&event.title!==LOW_MEMORY_EVENT;}
+function isTopGarbageCollectionEvent(event){return event.title in TOP_GC_EVENTS;}
+function isForcedGarbageCollectionEvent(event){return findParent(event,isLowMemoryEvent)!==null;}
+function isSubGarbageCollectionEvent(event){return isGarbageCollectionEvent(event)&&event.parentSlice&&(isTopGarbageCollectionEvent(event.parentSlice)||event.parentSlice.title===MAJOR_GC_EVENT||event.parentSlice.title===MINOR_GC_EVENT);}
+function isNotForcedTopGarbageCollectionEvent(event){return tr.metrics.v8.utils.isTopGarbageCollectionEvent(event)&&!tr.metrics.v8.utils.isForcedGarbageCollectionEvent(event);}
+function isNotForcedSubGarbageCollectionEvent(event){return tr.metrics.v8.utils.isSubGarbageCollectionEvent(event)&&!tr.metrics.v8.utils.isForcedGarbageCollectionEvent(event);}
+function isFullMarkCompactorEvent(event){return event.title==='V8.GCCompactor';}
+function isMarkCompactorSummaryEvent(event){return event.title==='V8.GCMarkCompactorSummary';}
+function isMarkCompactorMarkingSummaryEvent(event){return event.title==='V8.GCMarkCompactorMarkingSummary';}
+function isIncrementalMarkingEvent(event){return event.title.startsWith('V8.GCIncrementalMarking');}
+function isLatencyMarkCompactorEvent(event){return event.title==='V8.GCFinalizeMC';}
+function isMemoryMarkCompactorEvent(event){return event.title==='V8.GCFinalizeMCReduceMemory';}
+function isScavengerEvent(event){return event.title==='V8.GCScavenger';}
+function isCompileOptimizeRCSCategory(name){return name==='Optimize';}
+function isCompileUnoptimizeRCSCategory(name){return name==='Compile';}
+function isCompileParseRCSCategory(name){return name==='Parse';}
+function isCompileRCSCategory(name){return name==='Compile'||name==='Optimize'||name==='Parse';}
+function isV8RCSEvent(event){return event instanceof tr.e.v8.V8ThreadSlice;}
+function isMarkCompactorEvent(event){return MARK_COMPACTOR_EVENTS.has(event.title);}
+function isNotForcedMarkCompactorEvent(event){return!isForcedGarbageCollectionEvent(event)&&isMarkCompactorEvent(event);}
+function forcedGCEventName(){return LOW_MEMORY_EVENT;}
+function topGarbageCollectionEventName(event){if(event.title===FULL_GC_EVENT){if(findParent(event,isLowMemoryEvent)){return LOW_MEMORY_MARK_COMPACTOR;}}
+return TOP_GC_EVENTS[event.title];}
+function topGarbageCollectionEventNames(){return Object.values(TOP_GC_EVENTS);}
+function subGarbageCollectionEventName(event){const topEvent=findParent(event,isTopGarbageCollectionEvent);const prefix=topEvent?topGarbageCollectionEventName(topEvent):'unknown';const name=event.title.replace('V8.GC_MC_','').replace('V8.GC_SCAVENGER_','').replace('V8.GC_','').replace(/_/g,'-').toLowerCase();return prefix+'-'+name;}
+function jsExecutionThreads(model){const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);let threads=[];for(const rendererHelper of Object.values(chromeHelper.rendererHelpers)){if(rendererHelper.isChromeTracingUI)continue;threads.push(rendererHelper.mainThread);threads=threads.concat(rendererHelper.dedicatedWorkerThreads);threads=threads.concat(rendererHelper.foregroundWorkerThreads);}
+return threads;}
+function groupAndProcessEvents(model,filterCallback,groupCallback,processCallback,groups){const groupToEvents={};if(groups){for(const group of groups){groupToEvents[group]=[];}}
+const threads=jsExecutionThreads(model);for(const thread of threads){for(const event of thread.sliceGroup.childEvents()){if(!filterCallback(event))continue;const group=groupCallback(event);if(groups&&!(group in groupToEvents)){continue;}
+groupToEvents[group]=groupToEvents[group]||[];groupToEvents[group].push(event);}}
+for(const[group,events]of Object.entries(groupToEvents)){processCallback(group,events);}}
+function filterEvents(model,filterCallback){const threads=jsExecutionThreads(model);const events=[];for(const thread of threads){for(const event of thread.sliceGroup.childEvents()){if(!filterCallback(event))continue;events.push(event);}}
+return events;}
+function unionOfIntervals(intervals){if(intervals.length===0)return[];return tr.b.math.mergeRanges(intervals.map(x=>{return{min:x.start,max:x.end};}),1e-6,function(ranges){return{start:ranges.reduce((acc,x)=>Math.min(acc,x.min),ranges[0].min),end:ranges.reduce((acc,x)=>Math.max(acc,x.max),ranges[0].max)};});}
+function hasV8Stats(globalMemoryDump){let v8stats=undefined;globalMemoryDump.iterateContainerDumps(function(dump){v8stats=v8stats||dump.getMemoryAllocatorDumpByFullName('v8');});return!!v8stats;}
+function rangeForMemoryDumps(model){const startOfFirstDumpWithV8=model.globalMemoryDumps.filter(hasV8Stats).reduce((start,dump)=>Math.min(start,dump.start),Infinity);if(startOfFirstDumpWithV8===Infinity)return new tr.b.math.Range();return tr.b.math.Range.fromExplicitRange(startOfFirstDumpWithV8,Infinity);}
+class WindowEndpoint{constructor(start,points){this.points=points;this.lastIndex=-1;this.position=start;this.distanceUntilNextPoint=points[0].position-start;this.cummulativePause=0;this.stackDepth=0;}
+advance(delta){if(delta<this.distanceUntilNextPoint){this.position+=delta;this.cummulativePause+=this.stackDepth>0?delta:0;this.distanceUntilNextPoint=this.points[this.lastIndex+1].position-this.position;}else{this.position+=this.distanceUntilNextPoint;this.cummulativePause+=this.stackDepth>0?this.distanceUntilNextPoint:0;this.distanceUntilNextPoint=0;this.lastIndex++;if(this.lastIndex<this.points.length){this.stackDepth+=this.points[this.lastIndex].delta;if(this.lastIndex+1<this.points.length){this.distanceUntilNextPoint=this.points[this.lastIndex+1].position-this.position;}}}}}
+function mutatorUtilization(start,end,timeWindow,intervals){const mu=new tr.b.math.PiecewiseLinearFunction();if(end-start<=timeWindow){return mu;}
+if(intervals.length===0){mu.push(start,1.0,end-timeWindow,1.0);return mu;}
+intervals=unionOfIntervals(intervals);const points=[];for(const interval of intervals){points.push({position:interval.start,delta:1});points.push({position:interval.end,delta:-1});}
+points.sort((a,b)=>a.position-b.position);points.push({position:end,delta:0});const left=new WindowEndpoint(start,points);const right=new WindowEndpoint(start,points);const EPSILON=1e-6;while(right.position-left.position<timeWindow-EPSILON){right.advance(timeWindow-(right.position-left.position));}
+while(right.lastIndex<points.length){const distanceUntilNextPoint=Math.min(left.distanceUntilNextPoint,right.distanceUntilNextPoint);const position1=left.position;const value1=right.cummulativePause-left.cummulativePause;left.advance(distanceUntilNextPoint);right.advance(distanceUntilNextPoint);if(distanceUntilNextPoint>0){const position2=left.position;const value2=right.cummulativePause-left.cummulativePause;mu.push(position1,1.0-value1/timeWindow,position2,1.0-value2/timeWindow);}}
+return mu;}
+function addMutatorUtilization(metricName,eventFilter,timeWindows,rendererHelpers,histograms){const histogramMap=new Map();for(const timeWindow of timeWindows){const summaryOptions={avg:false,count:false,max:false,min:true,std:false,sum:false};const description=`The minimum mutator utilization in ${timeWindow}ms time window`;const histogram=histograms.createHistogram(`${metricName}-${timeWindow}ms_window`,tr.b.Unit.byName.normalizedPercentage_biggerIsBetter,[],{summaryOptions,description});histogramMap.set(timeWindow,histogram);}
+for(const rendererHelper of rendererHelpers){if(rendererHelper.isChromeTracingUI)continue;if(rendererHelper.mainThread===undefined)continue;const pauses=[];for(const event of rendererHelper.mainThread.sliceGroup.childEvents()){if(eventFilter(event)&&event.end>event.start){pauses.push({start:event.start,end:event.end});}}
+pauses.sort((a,b)=>a.start-b.start);const start=rendererHelper.mainThread.bounds.min;const end=rendererHelper.mainThread.bounds.max;for(const timeWindow of timeWindows){const mu=mutatorUtilization(start,end,timeWindow,pauses);histogramMap.get(timeWindow).addSample(mu.min);}}}
+return{addMutatorUtilization,findParent,forcedGCEventName,filterEvents,groupAndProcessEvents,isForcedGarbageCollectionEvent,isFullMarkCompactorEvent,isGarbageCollectionEvent,isIdleTask,isIncrementalMarkingEvent,isLatencyMarkCompactorEvent,isLowMemoryEvent,isMarkCompactorSummaryEvent,isMarkCompactorMarkingSummaryEvent,isMemoryMarkCompactorEvent,isNotForcedMarkCompactorEvent,isNotForcedTopGarbageCollectionEvent,isNotForcedSubGarbageCollectionEvent,isScavengerEvent,isSubGarbageCollectionEvent,isTopGarbageCollectionEvent,isTopV8ExecuteEvent,isV8Event,isV8ExecuteEvent,isV8RCSEvent,isCompileRCSCategory,isCompileOptimizeRCSCategory,isCompileUnoptimizeRCSCategory,isCompileParseRCSCategory,mutatorUtilization,rangeForMemoryDumps,subGarbageCollectionEventName,topGarbageCollectionEventName,topGarbageCollectionEventNames,unionOfIntervals,};});'use strict';tr.exportTo('tr.metrics.blink',function(){const BLINK_NON_AGGREGATED_GC_EVENTS_NAMES_MAP={'BlinkGC.AtomicPauseMarkEpilogue':'blink-gc-atomic-pause-mark-epilogue','BlinkGC.AtomicPauseMarkPrologue':'blink-gc-atomic-pause-mark-prologue','BlinkGC.AtomicPauseMarkRoots':'blink-gc-atomic-pause-mark-roots','BlinkGC.IncrementalMarkingStartMarking':'blink-gc-incremental-start','BlinkGC.IncrementalMarkingStep':'blink-gc-incremental-step','BlinkGC.UnifiedMarkingStep':'blink-gc-unified-marking-by-v8','BlinkGC.CompleteSweep':'blink-gc-complete-sweep','BlinkGC.LazySweepInIdle':'blink-gc-sweep-task-foreground','BlinkGC.LazySweepOnAllocation':'blink-gc-sweep-allocation','BlinkGC.AtomicPauseSweepAndCompact':'blink-gc-atomic-pause-sweep-and-compact'};const BLINK_TOP_GC_ROOTS_MARKING_EVENTS=['BlinkGC.VisitRoots'];const BLINK_GC_ATOMIC_PAUSE_TRANSITIVE_CLOSURE_EVENTS=['BlinkGC.AtomicPauseMarkTransitiveClosure'];const BLINK_GC_FOREGROUND_MARKING_TRANSITIVE_CLOSURE_EVENTS=['BlinkGC.AtomicPauseMarkTransitiveClosure','BlinkGC.IncrementalMarkingStep','BlinkGC.UnifiedMarkingStep'];const BLINK_TOP_GC_FOREGROUND_MARKING_EVENTS=['BlinkGC.AtomicPauseMarkEpilogue','BlinkGC.AtomicPauseMarkPrologue','BlinkGC.AtomicPauseMarkRoots','BlinkGC.IncrementalMarkingStartMarking',].concat(BLINK_GC_FOREGROUND_MARKING_TRANSITIVE_CLOSURE_EVENTS);const BLINK_TOP_GC_BACKGROUND_MARKING_EVENTS=['BlinkGC.ConcurrentMarkingStep'];const BLINK_TOP_GC_FOREGROUND_SWEEPING_EVENTS=['BlinkGC.CompleteSweep','BlinkGC.LazySweepInIdle','BlinkGC.LazySweepOnAllocation'];const BLINK_TOP_GC_BACKGROUND_SWEEPING_EVENTS=['BlinkGC.ConcurrentSweepingStep'];const BLINK_TOP_GC_EVENTS=Object.keys(BLINK_NON_AGGREGATED_GC_EVENTS_NAMES_MAP).concat(BLINK_GC_ATOMIC_PAUSE_TRANSITIVE_CLOSURE_EVENTS);const ATOMIC_PAUSE_EVENTS=['BlinkGC.AtomicPauseMarkEpilogue','BlinkGC.AtomicPauseMarkPrologue','BlinkGC.AtomicPauseMarkRoots','BlinkGC.AtomicPauseMarkTransitiveClosure','BlinkGC.AtomicPauseSweepAndCompact'];function blinkGarbageCollectionEventName(event){return BLINK_NON_AGGREGATED_GC_EVENTS_NAMES_MAP[event.title];}
+function blinkGarbageCollectionEventNames(){return Object.values(BLINK_NON_AGGREGATED_GC_EVENTS_NAMES_MAP);}
+function isNonForcedEvent(event){return(!event.args||!event.args.forced)&&!tr.metrics.v8.utils.isForcedGarbageCollectionEvent(event);}
+function isNonForcedBlinkGarbageCollectionEvent(event){return BLINK_TOP_GC_EVENTS.includes(event.title)&&isNonForcedEvent(event);}
+function isNonForcedNonAggregatedBlinkGarbageCollectionEvent(event){return event.title in BLINK_NON_AGGREGATED_GC_EVENTS_NAMES_MAP&&isNonForcedEvent(event);}
+function isNonForcedBlinkGarbageCollectionAtomicPauseEvent(event){return ATOMIC_PAUSE_EVENTS.includes(event.title)&&isNonForcedEvent(event);}
+function isNonForcedBlinkGarbageCollectionRootsMarkingEvent(event){return BLINK_TOP_GC_ROOTS_MARKING_EVENTS.includes(event.title)&&isNonForcedEvent(event);}
+function
+isNonForcedBlinkGarbageCollectionMarkingTransitiveColsureEvent(event){return BLINK_GC_FOREGROUND_MARKING_TRANSITIVE_CLOSURE_EVENTS.includes(event.title)&&isNonForcedEvent(event);}
+function
+isNonForcedBlinkGarbageCollectionAtomicPauseTransitiveColsureEvent(event){return BLINK_GC_ATOMIC_PAUSE_TRANSITIVE_CLOSURE_EVENTS.includes(event.title)&&isNonForcedEvent(event);}
+function isNonForcedBlinkGarbageCollectionForegroundMarkingEvent(event){return BLINK_TOP_GC_FOREGROUND_MARKING_EVENTS.includes(event.title)&&isNonForcedEvent(event);}
+function isNonForcedBlinkGarbageCollectionBackgroundMarkingEvent(event){return BLINK_TOP_GC_BACKGROUND_MARKING_EVENTS.includes(event.title)&&isNonForcedEvent(event);}
+function isNonForcedBlinkGarbageCollectionForegroundSweepingEvent(event){return BLINK_TOP_GC_FOREGROUND_SWEEPING_EVENTS.includes(event.title)&&isNonForcedEvent(event);}
+function isNonForcedBlinkGarbageCollectionBackgroundSweepingEvent(event){return BLINK_TOP_GC_BACKGROUND_SWEEPING_EVENTS.includes(event.title)&&isNonForcedEvent(event);}
+function isNonNestedNonForcedBlinkGarbageCollectionEvent(event){return isNonForcedBlinkGarbageCollectionEvent(event)&&!tr.metrics.v8.utils.findParent(event,tr.metrics.v8.utils.isGarbageCollectionEvent);}
+function blinkGcMetric(histograms,model){addDurationOfTopEvents(histograms,model);addDurationOfAtomicPause(histograms,model);addDurationOfAtomicPauseTransitiveClosure(histograms,model);addTotalDurationOfTopEvents(histograms,model);addTotalDurationOfBlinkAndV8TopEvents(histograms,model);addTotalDurationOfRootsMarking(histograms,model);addTotalDurationOfMarkingTransitiveClosure(histograms,model);addTotalDurationOfForegroundMarking(histograms,model);addTotalDurationOfBackgroundMarking(histograms,model);addTotalDurationOfForegroundSweeping(histograms,model);addTotalDurationOfBackgroundSweeping(histograms,model);}
+tr.metrics.MetricRegistry.register(blinkGcMetric);const timeDurationInMs_smallerIsBetter=tr.b.Unit.byName.timeDurationInMs_smallerIsBetter;const CUSTOM_BOUNDARIES=tr.v.HistogramBinBoundaries.createLinear(0,20,200).addExponentialBins(200,100);function createNumericForTopEventTime(name){const n=new tr.v.Histogram(name,timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);n.customizeSummaryOptions({avg:true,count:true,max:true,min:false,std:true,sum:true,percentile:[0.90]});return n;}
+function createNumericForTotalEventTime(name){const n=new tr.v.Histogram(name,timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);n.customizeSummaryOptions({avg:false,count:true,max:false,min:false,std:false,sum:true,percentile:[0.90]});return n;}
+function createNumericForUnifiedEventTime(name){const n=new tr.v.Histogram(name,timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);n.customizeSummaryOptions({avg:false,count:true,max:true,min:false,std:false,sum:true,percentile:[0.90]});return n;}
+function addDurationOfTopEvents(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isNonForcedNonAggregatedBlinkGarbageCollectionEvent,blinkGarbageCollectionEventName,function(name,events){const cpuDuration=createNumericForTopEventTime(name);for(const event of events){cpuDuration.addSample(event.cpuDuration);}
+histograms.addHistogram(cpuDuration);},blinkGarbageCollectionEventNames());}
+function addDurationOfAtomicPause(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isNonForcedBlinkGarbageCollectionAtomicPauseEvent,event=>event.args.epoch,function(group,events){const cpuDuration=createNumericForTopEventTime('blink-gc-atomic-pause');cpuDuration.addSample(events.reduce((acc,current)=>acc+current.cpuDuration,0));histograms.addHistogram(cpuDuration);});}
+function addDurationOfAtomicPauseTransitiveClosure(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isNonForcedBlinkGarbageCollectionAtomicPauseTransitiveColsureEvent,event=>event.args.epoch,function(group,events){const cpuDuration=createNumericForTopEventTime('blink-gc-atomic-pause-mark-transitive-closure');cpuDuration.addSample(events.reduce((acc,current)=>acc+current.cpuDuration,0));histograms.addHistogram(cpuDuration);});}
+function addTotalDurationOfTopEvents(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isNonForcedBlinkGarbageCollectionEvent,event=>'blink-gc-total',function(name,events){const cpuDuration=createNumericForTotalEventTime(name);for(const event of events){cpuDuration.addSample(event.cpuDuration);}
+histograms.addHistogram(cpuDuration);},['blink-gc-total']);}
+function addTotalDurationOfRootsMarking(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isNonForcedBlinkGarbageCollectionRootsMarkingEvent,event=>'blink-gc-mark-roots',function(name,events){const cpuDuration=createNumericForTotalEventTime(name);for(const event of events){cpuDuration.addSample(event.cpuDuration);}
+histograms.addHistogram(cpuDuration);},['blink-gc-mark-roots']);}
+function addTotalDurationOfMarkingTransitiveClosure(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isNonForcedBlinkGarbageCollectionMarkingTransitiveColsureEvent,event=>'blink-gc-mark-transitive-closure',function(name,events){const cpuDuration=createNumericForTotalEventTime(name);for(const event of events){cpuDuration.addSample(event.cpuDuration);}
+histograms.addHistogram(cpuDuration);},['blink-gc-mark-transitive-closure']);}
+function addTotalDurationOfForegroundMarking(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isNonForcedBlinkGarbageCollectionForegroundMarkingEvent,event=>'blink-gc-mark-foreground',function(name,events){const cpuDuration=createNumericForTotalEventTime(name);for(const event of events){cpuDuration.addSample(event.cpuDuration);}
+histograms.addHistogram(cpuDuration);},['blink-gc-mark-foreground']);}
+function addTotalDurationOfBackgroundMarking(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isNonForcedBlinkGarbageCollectionBackgroundMarkingEvent,event=>'blink-gc-mark-background',function(name,events){const cpuDuration=createNumericForTotalEventTime(name);for(const event of events){cpuDuration.addSample(event.cpuDuration);}
+histograms.addHistogram(cpuDuration);},['blink-gc-mark-background']);}
+function addTotalDurationOfForegroundSweeping(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isNonForcedBlinkGarbageCollectionForegroundSweepingEvent,event=>'blink-gc-sweep-foreground',function(name,events){const cpuDuration=createNumericForTotalEventTime(name);for(const event of events){cpuDuration.addSample(event.cpuDuration);}
+histograms.addHistogram(cpuDuration);},['blink-gc-sweep-foreground']);}
+function addTotalDurationOfBackgroundSweeping(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isNonForcedBlinkGarbageCollectionBackgroundSweepingEvent,event=>'blink-gc-sweep-background',function(name,events){const cpuDuration=createNumericForTotalEventTime(name);for(const event of events){cpuDuration.addSample(event.cpuDuration);}
+histograms.addHistogram(cpuDuration);},['blink-gc-sweep-background']);}
+function isV8OrBlinkTopLevelGarbageCollectionEvent(event){return tr.metrics.v8.utils.isNotForcedTopGarbageCollectionEvent(event)||isNonNestedNonForcedBlinkGarbageCollectionEvent(event);}
+function addTotalDurationOfBlinkAndV8TopEvents(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isV8OrBlinkTopLevelGarbageCollectionEvent,event=>'unified-gc-total',function(name,events){const cpuDuration=createNumericForUnifiedEventTime(name);for(const event of events){cpuDuration.addSample(event.cpuDuration);}
+histograms.addHistogram(cpuDuration);},['unified-gc-total']);}
+return{blinkGcMetric,};});'use strict';tr.exportTo('tr.metrics.blink',function(){function leakDetectionMetric(histograms,model){const modelHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);if(modelHelper===undefined){throw new Error('Chrome is not present.');}
+const rendererHelpers=modelHelper.rendererHelpers;if(Object.keys(rendererHelpers).length===0){throw new Error('Renderer process is not present.');}
+const pids=Object.keys(rendererHelpers);const chromeDumps=tr.metrics.sh.splitGlobalDumpsByBrowserName(model,undefined).get('chrome');const sumCounter=new Map();for(const pid of pids){for(const[key,count]of countLeakedBlinkObjects(chromeDumps,pid)){sumCounter.set(key,(sumCounter.get(key)||0)+count);}}
+for(const[key,count]of sumCounter){histograms.createHistogram('Leaked '+key,tr.b.Unit.byName.count_smallerIsBetter,count);}
+for(const[key,count]of sumCounter){if(count>0){throw new Error('Memory leak is found.');}}}
+tr.metrics.MetricRegistry.register(leakDetectionMetric);function countLeakedBlinkObjects(dumps,pid){if(dumps===undefined||dumps.length<2){throw new Error('Expected at least two memory dumps.');}
+const firstCounter=countBlinkObjects(dumps[0],pid);const lastCounter=countBlinkObjects(dumps[dumps.length-1],pid);const diffCounter=new Map();for(const[key,lastCount]of lastCounter){diffCounter.set(key,lastCount-firstCounter.get(key));}
+return diffCounter;}
+function countBlinkObjects(dump,pid){const counter=new Map();const processesMemoryDumps=dump.processMemoryDumps;if(processesMemoryDumps[pid]===undefined)return counter;const blinkObjectsDump=processesMemoryDumps[pid].memoryAllocatorDumps.find(dump=>dump.fullName==='blink_objects');for(const v of blinkObjectsDump.children){counter.set(v.name,v.numerics.object_count.value);}
+return counter;}
+return{leakDetectionMetric,};});'use strict';tr.exportTo('tr.metrics.console',function(){const COUNT_BOUNDARIES=tr.v.HistogramBinBoundaries.createExponential(1,1e4,30);const SUMMARY_OPTIONS=tr.v.Histogram.AVERAGE_ONLY_SUMMARY_OPTIONS;const SOURCES=['all','js','network'];function consoleErrorMetric(histograms,model){const counts={};for(const source of SOURCES){counts[source]=0;}
+for(const slice of model.getDescendantEvents()){if(slice.category==='blink.console'&&slice.title==='ConsoleMessage::Error'){const source=slice.args.source.toLowerCase();counts.all++;if(source in counts){counts[source]++;}}
+if(slice.category==='v8.console'&&(slice.title==='V8ConsoleMessage::Exception'||slice.title==='V8ConsoleMessage::Error'||slice.title==='V8ConsoleMessage::Assert')){counts.all++;counts.js++;}}
+for(const source of SOURCES){histograms.createHistogram(`console:error:${source}`,tr.b.Unit.byName.count_smallerIsBetter,counts[source],{description:`Number of ${source} console error messages`,summaryOptions:SUMMARY_OPTIONS});}}
+tr.metrics.MetricRegistry.register(consoleErrorMetric);return{consoleErrorMetric,};});'use strict';tr.exportTo('tr.metrics.sh',function(){function getCpuSnapshotsFromModel(model){const snapshots=[];for(const pid in model.processes){const snapshotInstances=model.processes[pid].objects.getAllInstancesNamed('CPUSnapshots');if(!snapshotInstances)continue;for(const object of snapshotInstances[0].snapshots){snapshots.push(object.args.processes);}}
+return snapshots;}
+function getProcessSumsFromSnapshot(snapshot){const processSums=new Map();for(const processData of snapshot){const processName=processData.name;if(!(processSums.has(processName))){processSums.set(processName,{sum:0.0,paths:new Set()});}
+processSums.get(processName).sum+=parseFloat(processData.pCpu);if(processData.path){processSums.get(processName).paths.add(processData.path);}}
+return processSums;}
+function buildNumericsFromSnapshots(snapshots){const processNumerics=new Map();for(const snapshot of snapshots){const processSums=getProcessSumsFromSnapshot(snapshot);for(const[processName,processData]of processSums.entries()){if(!(processNumerics.has(processName))){processNumerics.set(processName,{numeric:new tr.v.Histogram('cpu:percent:'+processName,tr.b.Unit.byName.normalizedPercentage_smallerIsBetter),paths:new Set()});}
+processNumerics.get(processName).numeric.addSample(processData.sum/100.0);for(const path of processData.paths){processNumerics.get(processName).paths.add(path);}}}
+return processNumerics;}
+function cpuProcessMetric(histograms,model){const snapshots=getCpuSnapshotsFromModel(model);const processNumerics=buildNumericsFromSnapshots(snapshots);for(const[processName,processData]of processNumerics){const numeric=processData.numeric;const missingSnapshotCount=snapshots.length-numeric.numValues;for(let i=0;i<missingSnapshotCount;i++){numeric.addSample(0);}
+numeric.diagnostics.set('paths',new
+tr.v.d.GenericSet([...processData.paths]));histograms.addHistogram(numeric);}}
+tr.metrics.MetricRegistry.register(cpuProcessMetric);return{cpuProcessMetric,};});'use strict';tr.exportTo('tr.metrics',function(){function mediaMetric(histograms,model){const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);if(chromeHelper===undefined)return;for(const rendererHelper of Object.values(chromeHelper.rendererHelpers)){const mainThread=rendererHelper.mainThread;if(mainThread===undefined)continue;const videoThreads=rendererHelper.process.findAllThreadsMatching(thread=>(thread.name?thread.name.startsWith('ThreadPoolSingleThreadSharedForegroundBlocking'):false));const compositorThread=rendererHelper.compositorThread;if(compositorThread!==undefined){videoThreads.push(compositorThread);}
+const audioThreads=rendererHelper.process.findAllThreadsNamed('AudioOutputDevice');if(audioThreads.length===0&&videoThreads.length===0)continue;const processData=new PerProcessData();processData.recordPlayStarts(mainThread);if(!processData.hasPlaybacks)continue;if(videoThreads.length!==0){processData.calculateTimeToVideoPlays(videoThreads);processData.calculateDroppedFrameCounts(videoThreads);}
+if(audioThreads.length!==0){processData.calculateTimeToAudioPlays(audioThreads);}
+processData.calculateSeekTimes(mainThread);processData.calculateBufferingTimes(mainThread);processData.addMetricToHistograms(histograms);}}
+class PerProcessData{constructor(){this.playbackIdToDataMap_=new Map();}
+recordPlayStarts(mainThread){for(const event of mainThread.sliceGroup.getDescendantEvents()){if(event.title==='WebMediaPlayerImpl::DoLoad'){const id=event.args.id;if(this.playbackIdToDataMap_.has(id)){throw new Error('Unexpected multiple initialization of a media playback');}
+this.playbackIdToDataMap_.set(id,new PerPlaybackData(event.start));}}}
+get hasPlaybacks(){return this.playbackIdToDataMap_.size>0;}
+calculateTimeToVideoPlays(videoThreads){for(const thread of videoThreads){for(const event of thread.sliceGroup.getDescendantEvents()){if(event.title==='VideoRendererImpl::Render'){this.getPerPlaybackObject_(event.args.id).processVideoRenderTime(event.start);}}}}
+calculateTimeToAudioPlays(audioThreads){for(const audioThread of audioThreads){for(const event of audioThread.sliceGroup.getDescendantEvents()){if(event.title==='AudioRendererImpl::Render'){this.getPerPlaybackObject_(event.args.id).processAudioRenderTime(event.start);}}}}
+calculateSeekTimes(mainThread){for(const event of mainThread.sliceGroup.getDescendantEvents()){if(event.title==='WebMediaPlayerImpl::DoSeek'){this.getPerPlaybackObject_(event.args.id).processDoSeek(event.args.target,event.start);}else if(event.title==='WebMediaPlayerImpl::OnPipelineSeeked'){this.getPerPlaybackObject_(event.args.id).processOnPipelineSeeked(event.args.target,event.start);}else if(event.title==='WebMediaPlayerImpl::BufferingHaveEnough'){this.getPerPlaybackObject_(event.args.id).processBufferingHaveEnough(event.start);}}}
+calculateBufferingTimes(mainThread){for(const event of mainThread.sliceGroup.getDescendantEvents()){if(event.title==='WebMediaPlayerImpl::OnEnded'){this.getPerPlaybackObject_(event.args.id).processOnEnded(event.start,event.args.duration);}}}
+calculateDroppedFrameCounts(videoThreads){for(const thread of videoThreads){for(const event of thread.sliceGroup.getDescendantEvents()){if(event.title==='VideoFramesDropped'){this.getPerPlaybackObject_(event.args.id).processVideoFramesDropped(event.args.count);}}}}
+addMetricToHistograms(histograms){for(const[id,playbackData]of this.playbackIdToDataMap_){playbackData.addMetricToHistograms(histograms);}}
+getPerPlaybackObject_(playbackId){let perPlaybackObject=this.playbackIdToDataMap_.get(playbackId);if(perPlaybackObject===undefined){perPlaybackObject=new PerPlaybackData(undefined);this.playbackIdToDataMap_.set(playbackId,perPlaybackObject);}
+return perPlaybackObject;}}
+class PerPlaybackData{constructor(playStartTime){this.playStart_=playStartTime;this.timeToVideoPlay_=undefined;this.timeToAudioPlay_=undefined;this.bufferingTime_=undefined;this.droppedFrameCount_=0;this.seekError_=false;this.seekTimes_=new Map();this.currentSeek_=undefined;}
+get timeToVideoPlay(){return this.timeToVideoPlay_;}
+get timeToAudioPlay(){return this.timeToAudioPlay_;}
+get bufferingTime(){return this.bufferingTime_;}
+get droppedFrameCount(){return(this.timeToVideoPlay_!==undefined)?this.droppedFrameCount_:undefined;}
+get seekTimes(){if(this.seekError_||this.currentSeek_!==undefined)return new Map();return this.seekTimes_;}
+processVideoRenderTime(videoRenderTime){if(this.playStart_!==undefined&&this.timeToVideoPlay_===undefined){this.timeToVideoPlay_=videoRenderTime-this.playStart_;}}
+processAudioRenderTime(audioRenderTime){if(this.playStart_!==undefined&&this.timeToAudioPlay_===undefined){this.timeToAudioPlay_=audioRenderTime-this.playStart_;}}
+processVideoFramesDropped(count){this.droppedFrameCount_+=count;}
+processDoSeek(target,startTime){if(this.currentSeek_!==undefined){this.seekError_=true;return;}
+this.currentSeek_={target,startTime};this.seekTimes_.set(target,this.currentSeek_);}
+processOnPipelineSeeked(target,time){if(this.seekError_)return;const currentSeek=this.currentSeek_;if(currentSeek===undefined){return;}
+if(currentSeek.target!==target){this.seekError_=true;return;}
+if(currentSeek.pipelineSeekTime!==undefined){this.seekError_=true;return;}
+currentSeek.pipelineSeekTime=time-currentSeek.startTime;}
+processBufferingHaveEnough(time){if(this.seekError_)return;const currentSeek=this.currentSeek_;if(currentSeek===undefined){return;}
+if(currentSeek.pipelineSeekTime===undefined){return;}
+currentSeek.seekTime=time-currentSeek.startTime;this.currentSeek_=undefined;}
+processOnEnded(playEndTime,duration){if(this.playStart_===undefined)return;if(this.seekTimes_.size!==0||this.seekError_)return;if(this.bufferingTime_!==undefined)return;duration=tr.b.convertUnit(duration,tr.b.UnitPrefixScale.METRIC.NONE,tr.b.UnitPrefixScale.METRIC.MILLI);const playTime=playEndTime-this.playStart_;if(this.timeToVideoPlay_!==undefined){this.bufferingTime_=playTime-duration-this.timeToVideoPlay_;}else if(this.timeToAudioPlay!==undefined){this.bufferingTime_=playTime-duration-this.timeToAudioPlay_;}}
+addMetricToHistograms(histograms){this.addSample_(histograms,'time_to_video_play',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,this.timeToVideoPlay);this.addSample_(histograms,'time_to_audio_play',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,this.timeToAudioPlay);this.addSample_(histograms,'dropped_frame_count',tr.b.Unit.byName.count_smallerIsBetter,this.droppedFrameCount);for(const[key,value]of this.seekTimes.entries()){const keyString=key.toString().replace('.','_');this.addSample_(histograms,'pipeline_seek_time_'+keyString,tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,value.pipelineSeekTime);this.addSample_(histograms,'seek_time_'+keyString,tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,value.seekTime);}
+this.addSample_(histograms,'buffering_time',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,this.bufferingTime);}
+addSample_(histograms,name,unit,sample){if(sample===undefined)return;const histogram=histograms.getHistogramNamed(name);if(histogram===undefined){histograms.createHistogram(name,unit,sample);}else{histogram.addSample(sample);}}}
+tr.metrics.MetricRegistry.register(mediaMetric);return{mediaMetric,};});'use strict';tr.exportTo('tr.metrics.rendering',function(){const UNKNOWN_THREAD_NAME='Unknown';const CATEGORY_THREAD_MAP=new Map();CATEGORY_THREAD_MAP.set('total_all',[/.*/]);CATEGORY_THREAD_MAP.set('browser',[/^Browser Compositor$/,/^CrBrowserMain$/]);CATEGORY_THREAD_MAP.set('display_compositor',[/^VizCompositorThread$/]);CATEGORY_THREAD_MAP.set('GPU',[/^Chrome_InProcGpuThread$/,/^CrGpuMain$/]);CATEGORY_THREAD_MAP.set('IO',[/IOThread/]);CATEGORY_THREAD_MAP.set('raster',[/CompositorTileWorker/]);CATEGORY_THREAD_MAP.set('renderer_compositor',[/^Compositor$/]);CATEGORY_THREAD_MAP.set('renderer_main',[/^CrRendererMain$/]);CATEGORY_THREAD_MAP.set('total_rendering',[/^Browser Compositor$/,/^Chrome_InProcGpuThread$/,/^Compositor$/,/CompositorTileWorker/,/^CrBrowserMain$/,/^CrGpuMain$/,/^CrRendererMain$/,/IOThread/,/^VizCompositorThread$/]);const ALL_CATEGORIES=[...CATEGORY_THREAD_MAP.keys(),'other'];function addValueToMap_(map,key,value){const oldValue=map.get(key)||0;map.set(key,oldValue+value);}
+function categoryShouldHaveBreakdown(category){return category==='total_all'||category==='total_rendering';}
+function*getCategories_(threadName){let isOther=true;for(const[category,regexps]of CATEGORY_THREAD_MAP){for(const regexp of regexps){if(regexp.test(threadName)){if(category!=='total_all')isOther=false;yield category;break;}}}
+if(isOther)yield'other';}
+function isSubset_(regexps1,regexps2){for(const r1 of regexps1){if(regexps2.find(r2=>r2.toString()===r1.toString())===undefined){return false;}}
+return true;}
+function addCpuUtilizationHistograms(histograms,model,segments,segmentCostFunc,histogramNameFunc,description,unit){if(!unit)unit=tr.b.Unit.byName.unitlessNumber;const histogramMap=new Map();for(const category of ALL_CATEGORIES){const histogram=histograms.createHistogram(histogramNameFunc(category),unit,[],{binBoundaries:tr.v.HistogramBinBoundaries.createExponential(1,50,20),description,summaryOptions:tr.metrics.rendering.SUMMARY_OPTIONS,});histogramMap.set(category,histogram);}
+for(const[category,regexps]of CATEGORY_THREAD_MAP){const relatedCategories=new tr.v.d.RelatedNameMap();const histogram=histogramMap.get(category);for(const[otherCategory,otherRegexps]of CATEGORY_THREAD_MAP){if(otherCategory===category)continue;if(category!=='all'&&!isSubset_(otherRegexps,regexps))continue;const otherHistogram=histogramMap.get(otherCategory);relatedCategories.set(otherCategory,otherHistogram.name);}
+if([...relatedCategories.values()].length>0){histogram.diagnostics.set('breakdown',relatedCategories);}}
+for(const segment of segments){const threadValues=new Map();for(const thread of model.getAllThreads()){addValueToMap_(threadValues,thread.name||UNKNOWN_THREAD_NAME,segmentCostFunc(thread,segment));}
+const categoryValues=new Map();const breakdowns=new Map();for(const[threadName,coresPerSec]of threadValues){for(const category of getCategories_(threadName)){addValueToMap_(categoryValues,category,coresPerSec);if(!categoryShouldHaveBreakdown(category))continue;if(!breakdowns.has(category)){breakdowns.set(category,new tr.v.d.Breakdown());}
+breakdowns.get(category).set(threadName,coresPerSec);}}
+for(const category of ALL_CATEGORIES){const value=categoryValues.get(category)||0;const diagnostics=new tr.v.d.DiagnosticMap();const breakdown=breakdowns.get(category);if(breakdown)diagnostics.set('breakdown',breakdown);const histogram=histogramMap.get(category);histogram.addSample(value,diagnostics);}}}
+const SUMMARY_OPTIONS={percentile:[0.90,0.95],ci:[0.95],};return{addCpuUtilizationHistograms,SUMMARY_OPTIONS,};});'use strict';tr.exportTo('tr.metrics.rendering',function(){const PRESENT_EVENT='Display::FrameDisplayed';const DISPLAY_EVENT='BenchmarkInstrumentation::DisplayRenderingStats';const DRM_EVENT='DrmEventFlipComplete';const SURFACE_FLINGER_EVENT='vsync_before';const COMPOSITOR_FRAME_PRESENTED_EVENT='FramePresented';const MIN_FRAME_LENGTH=0.5;const MIN_FRAME_COUNT=10;const PAUSE_THRESHOLD=20;const ASH_ENVIRONMENT='ash';const BROWSER_ENVIRONMENT='browser';class FrameEvent{constructor(event){this.event_=event;}
+get eventStart(){return this.event_.start;}
+get frameStart(){if(this.event_.title!==DRM_EVENT)return this.event_.start;const data=this.event_.args.data;const TIME=tr.b.UnitScale.TIME;return tr.b.convertUnit(data['vblank.tv_sec'],TIME.SEC,TIME.MILLI_SEC)+
+tr.b.convertUnit(data['vblank.tv_usec'],TIME.MICRO_SEC,TIME.MILLI_SEC);}
+get event(){return this.event_;}}
+class FrameSegment{constructor(frameEvent,duration){this.frameEvent_=frameEvent;this.duration_=duration;this.segment_=new tr.model.um.Segment(frameEvent.eventStart,duration);this.length_=undefined;}
+updateLength(refreshPeriod){this.length_=this.duration_/refreshPeriod;}
+get segment(){return this.segment_;}
+get boundsRange(){return this.segment_.boundsRange;}
+get length(){return this.length_;}
+get duration(){return this.duration_;}
+get event(){return this.frameEvent_.event;}}
+function getDisplayCompositorPresentationEventsExp_(modelHelper){if(!modelHelper)return[];function findEventsFromProcess(process){const events=[];for(const event of process.findTopmostSlicesNamed(PRESENT_EVENT)){events.push(event);}
+return events;}
+if(modelHelper.gpuHelper){const events=findEventsFromProcess(modelHelper.gpuHelper.process);if(events.length>0)return events;}
+if(!modelHelper.browserProcess)return[];return findEventsFromProcess(modelHelper.browserProcess);}
+function getDisplayCompositorPresentationEvents_(modelHelper){if(!modelHelper||!modelHelper.browserProcess)return[];let events=[];if(modelHelper.surfaceFlingerProcess){events=[...modelHelper.surfaceFlingerProcess.findTopmostSlicesNamed(SURFACE_FLINGER_EVENT)];if(events.length>0)return events;}
+if(modelHelper.gpuHelper){const gpuProcess=modelHelper.gpuHelper.process;events=[...gpuProcess.findTopmostSlicesNamed(DRM_EVENT)];if(events.length>0)return events;events=[...gpuProcess.findTopmostSlicesNamed(DISPLAY_EVENT)];if(events.length>0)return events;}
+return[...modelHelper.browserProcess.findTopmostSlicesNamed(DISPLAY_EVENT)];}
+function getUIPresentationEvents_(modelHelper){if(!modelHelper||!modelHelper.browserProcess)return[];const legacyEvents=[];const eventsByEnvironment={};eventsByEnvironment[ASH_ENVIRONMENT]=[];eventsByEnvironment[BROWSER_ENVIRONMENT]=[];for(const event of modelHelper.browserProcess.findTopmostSlicesNamed(COMPOSITOR_FRAME_PRESENTED_EVENT)){if(!('environment'in event.args)){legacyEvents.push(event);}else{eventsByEnvironment[event.args.environment].push(event);}}
+if(eventsByEnvironment[ASH_ENVIRONMENT].length>0){return eventsByEnvironment[ASH_ENVIRONMENT];}
+if(eventsByEnvironment[BROWSER_ENVIRONMENT].length>0){return eventsByEnvironment[BROWSER_ENVIRONMENT];}
+return legacyEvents;}
+function computeFrameSegments_(events,segments,opt_minFrameCount){const minFrameCount=opt_minFrameCount||MIN_FRAME_COUNT;const frameEvents=events.map(e=>new FrameEvent(e));const frameSegments=[];for(const segment of segments){const filtered=segment.boundsRange.filterArray(frameEvents,x=>x.eventStart);if(filtered.length<minFrameCount)continue;for(let i=1;i<filtered.length;i++){const duration=filtered[i].frameStart-filtered[i-1].frameStart;frameSegments.push(new FrameSegment(filtered[i-1],duration));}}
+return frameSegments;}
+function addBasicFrameTimeHistograms_(histograms,frameSegments,prefix){const frameTimes=(frameSegments.length===0)?[0]:frameSegments.map(x=>x.duration);histograms.createHistogram(`${prefix}frame_times`,tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,frameTimes,{binBoundaries:tr.v.HistogramBinBoundaries.createLinear(0,50,20),description:'Raw frame times.',summaryOptions:tr.metrics.rendering.SUMMARY_OPTIONS,});histograms.createHistogram(`${prefix}percentage_smooth`,tr.b.Unit.byName.unitlessNumber_biggerIsBetter,100*tr.b.math.Statistics.sum(frameTimes,(x=>(x<17?1:0)))/frameTimes.length,{description:'Percentage of frames that were hitting 60 FPS.',summaryOptions:{},});}
+function addFrameTimeHistograms(histograms,model,segments,opt_minFrameCount){const minFrameCount=opt_minFrameCount||MIN_FRAME_COUNT;const modelHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);const events=getDisplayCompositorPresentationEvents_(modelHelper);if(!events)return;addFrameTimeHistogramsHelper(histograms,model,segments,events,'',true,minFrameCount);const eventsExp=getDisplayCompositorPresentationEventsExp_(modelHelper);if(eventsExp&&eventsExp.length>0){addFrameTimeHistogramsHelper(histograms,model,segments,eventsExp,'exp_',false,minFrameCount);}}
+function addFrameTimeHistogramsHelper(histograms,model,segments,events,prefix,addCpuMetrics,minFrameCount){const frameSegments=computeFrameSegments_(events,segments,minFrameCount);addBasicFrameTimeHistograms_(histograms,frameSegments,prefix+'');if(addCpuMetrics){tr.metrics.rendering.addCpuUtilizationHistograms(histograms,model,frameSegments,(thread,segment)=>thread.getCpuTimeForRange(segment.boundsRange),category=>`thread_${category}_cpu_time_per_frame`,'CPU cores of a thread group per frame',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter);tr.metrics.rendering.addCpuUtilizationHistograms(histograms,model,frameSegments,(thread,segment)=>thread.getNumToplevelSlicesForRange(segment.boundsRange),category=>`tasks_per_frame_${category}`,'Number of tasks of a thread group per frame',tr.b.Unit.byName.unitlessNumber_smallerIsBetter);let totalWallTime=0;let totalCpuTime=0;for(const segment of frameSegments){for(const thread of model.getAllThreads()){totalCpuTime+=thread.getCpuTimeForRange(segment.boundsRange);totalWallTime+=thread.getWallTimeForRange(segment.boundsRange);}}
+histograms.createHistogram('cpu_wall_time_ratio',tr.b.Unit.byName.unitlessNumber_biggerIsBetter,totalCpuTime/totalWallTime,{description:'Ratio of total cpu-time vs. wall-time.',summaryOptions:{},});}
+const refreshPeriod=getRefreshPeriod(model,frameSegments.map(fs=>fs.boundsRange));frameSegments.forEach(fs=>fs.updateLength(refreshPeriod));const validFrames=frameSegments.filter(fs=>fs.length>=MIN_FRAME_LENGTH);const totalFrameDuration=tr.b.math.Statistics.sum(frameSegments,fs=>fs.duration);addJankCountHistograms(histograms,validFrames,prefix);const frameLengths=validFrames.map(frame=>frame.length);histograms.createHistogram(prefix+'frame_lengths',tr.b.Unit.byName.unitlessNumber_smallerIsBetter,frameLengths,{binBoundaries:tr.v.HistogramBinBoundaries.createLinear(0,5,20),summaryOptions:tr.metrics.rendering.SUMMARY_OPTIONS,description:'Frame times in vsyncs.'});histograms.createHistogram(prefix+'avg_surface_fps',tr.b.Unit.byName.unitlessNumber_biggerIsBetter,frameLengths.length/tr.b.convertUnit(totalFrameDuration,tr.b.UnitScale.TIME.MILLI_SEC,tr.b.UnitScale.TIME.SEC),{description:'Average frames per second.',summaryOptions:{},});}
+function addUIFrameTimeHistograms(histograms,model,segments,opt_minFrameCount){const minFrameCount=opt_minFrameCount||MIN_FRAME_COUNT;const events=getUIPresentationEvents_(model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper));if(events.length===0)return;const frameSegments=computeFrameSegments_(events,segments,minFrameCount);addBasicFrameTimeHistograms_(histograms,frameSegments,'ui_');}
+function addJankCountHistograms(histograms,validFrames,prefix){const jankEvents=[];for(let i=1;i<validFrames.length;i++){const change=Math.round((validFrames[i].length-validFrames[i-1].length));if(change>0&&change<PAUSE_THRESHOLD){jankEvents.push(validFrames[i].event);}}
+const jankCount=jankEvents.length;const diagnostics=new tr.v.d.DiagnosticMap();diagnostics.set('events',new tr.v.d.RelatedEventSet(jankEvents));diagnostics.set('timestamps',new tr.v.d.GenericSet(jankEvents.map(e=>e.start)));const histogram=histograms.createHistogram(prefix+'jank_count',tr.b.Unit.byName.count_smallerIsBetter,{value:jankCount,diagnostics},{description:'Number of changes in frame rate.',summaryOptions:{},});}
+function getRefreshPeriod(model,ranges){for(const metadata of model.metadata){if(metadata.value&&metadata.value.surface_flinger){return metadata.value.surface_flinger.refresh_period;}}
+const FRAME_LENGTH=1000.0/60;const BEGIN_FRAME_ARGS='Scheduler::BeginFrame';const FRAME_INTERVAL='interval_us';const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);for(const rendererHelper of Object.values(chromeHelper.rendererHelpers)){if(rendererHelper.compositorThread===undefined)continue;const slices=rendererHelper.compositorThread.sliceGroup;for(const slice of slices.getDescendantEventsInSortedRanges(ranges)){if(slice.title!==BEGIN_FRAME_ARGS)continue;const data=slice.args.args;if(!(FRAME_INTERVAL in data)){throw new Error(`${FRAME_INTERVAL} is missing`);}
+return tr.b.convertUnit(data[FRAME_INTERVAL],tr.b.UnitScale.TIME.MICRO_SEC,tr.b.UnitScale.TIME.MILLI_SEC);}}
+return FRAME_LENGTH;}
+return{addFrameTimeHistograms,addUIFrameTimeHistograms,};});'use strict';tr.exportTo('tr.metrics.rendering',function(){const RGB_DECODE_EVENT='ImageFrameGenerator::decode';const YUV_DECODE_EVENT='ImageFrameGenerator::decodeToYUV';const BLINK_GPU_RASTER_DECODE_EVENT='GpuImageDecodeCache::DecodeImage';const BLINK_SOFTWARE_RASTER_DECODE_EVENT='SoftwareImageDecodeCache::'+'DecodeImageInTask';function getImageDecodingEvents_(modelHelper,ranges){if(!modelHelper||!modelHelper.rendererHelpers)return[];const events=[];for(const renderer of Object.values(modelHelper.rendererHelpers)){for(const thread of renderer.rasterWorkerThreads){const slices=thread.sliceGroup;for(const slice of slices.getDescendantEventsInSortedRanges(ranges)){if(slice.title===RGB_DECODE_EVENT||slice.title===YUV_DECODE_EVENT||slice.title===BLINK_GPU_RASTER_DECODE_EVENT||slice.title===BLINK_SOFTWARE_RASTER_DECODE_EVENT){events.push(slice);}}}}
+return events;}
+function addImageDecodeTimeHistograms(histograms,model,segments){const modelHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);const decodeEvents=getImageDecodingEvents_(modelHelper,segments.map(s=>s.boundsRange));if(!decodeEvents)return;histograms.createHistogram('rgb_decode_time',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,decodeEvents.filter(slice=>slice.title===RGB_DECODE_EVENT).map(slice=>slice.cpuDuration),{description:'Duration of the Blink RGB decoding path for a chunk '+'of image data (possibly the whole image).',summaryOptions:tr.metrics.rendering.SUMMARY_OPTIONS,});histograms.createHistogram('yuv_decode_time',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,decodeEvents.filter(slice=>slice.title===YUV_DECODE_EVENT).map(slice=>slice.cpuDuration),{description:'Duration of the Blink YUV decoding path for a '+'chunk of image data (possibly the whole image).',summaryOptions:tr.metrics.rendering.SUMMARY_OPTIONS,});histograms.createHistogram('blink_decode_time_gpu_rasterization',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,decodeEvents.filter(slice=>slice.title===BLINK_GPU_RASTER_DECODE_EVENT).map(slice=>slice.cpuDuration),{description:'Duration of decoding and scaling within the '+'GpuImageDecodeCache for a chunk of image data '+'(possibly the whole image)',summaryOptions:tr.metrics.rendering.SUMMARY_OPTIONS,});histograms.createHistogram('blink_decode_time_software_rasterization',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,decodeEvents.filter(slice=>slice.title===BLINK_SOFTWARE_RASTER_DECODE_EVENT).map(slice=>slice.cpuDuration),{description:'Duration of decoding and scaling within the '+'SoftwareImageDecodeCache for a chunk of image data '+'(possibly the whole image)',summaryOptions:tr.metrics.rendering.SUMMARY_OPTIONS,});}
+return{addImageDecodeTimeHistograms};});'use strict';tr.exportTo('tr.metrics.rendering',function(){function eventIsValidGraphicsEvent_(event,eventMap){if(event.title!=='Graphics.Pipeline'||!event.bindId||!event.args||!event.args.step){return false;}
+const bindId=event.bindId;if(eventMap.has(bindId)&&event.args.step in eventMap.get(bindId)){if(event.args.step==='IssueBeginFrame'||event.args.step==='ReceiveBeginFrame'){throw new Error('Unexpected duplicate step: '+event.args.step);}
+return false;}
+return true;}
+function generateBreakdownForCompositorPipelineInClient_(flow){const breakdown=new tr.v.d.Breakdown();breakdown.set('time before GenerateRenderPass',flow.GenerateRenderPass.start-flow.ReceiveBeginFrame.start);breakdown.set('GenerateRenderPass duration',flow.GenerateRenderPass.duration);breakdown.set('GenerateCompositorFrame duration',flow.GenerateCompositorFrame.duration);breakdown.set('SubmitCompositorFrame duration',flow.SubmitCompositorFrame.duration);return breakdown;}
+function generateBreakdownForCompositorPipelineInService_(flow){const breakdown=new tr.v.d.Breakdown();breakdown.set('Processing CompositorFrame on reception',flow.ReceiveCompositorFrame.duration);breakdown.set('Delay before SurfaceAggregation',flow.SurfaceAggregation.start-flow.ReceiveCompositorFrame.end);breakdown.set('SurfaceAggregation duration',flow.SurfaceAggregation.duration);return breakdown;}
+function generateBreakdownForDraw_(drawEvent){const breakdown=new tr.v.d.Breakdown();for(const slice of drawEvent.subSlices){breakdown.set(slice.title,slice.duration);}
+return breakdown;}
+function getDisplayCompositorThread_(model){const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);const gpuHelper=chromeHelper.gpuHelper;if(gpuHelper){const thread=gpuHelper.process.findAtMostOneThreadNamed('VizCompositorThread');if(thread){return thread;}}
+if(!chromeHelper.browserProcess)return null;return chromeHelper.browserProcess.findAtMostOneThreadNamed('CrBrowserMain');}
+function getRasterTaskTimes(sourceFrameNumber,model){const modelHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);const renderers=modelHelper.telemetryHelper.renderersWithIR;if(renderers.length===0)return;const rasterThreads=renderers[0].rasterWorkerThreads;let earliestStart=undefined;let lastEnd=undefined;for(const rasterThread of rasterThreads){for(const slice of[...rasterThread.findTopmostSlicesNamed('TaskGraphRunner::RunTask')]){if(slice.args&&slice.args.source_frame_number_&&slice.args.source_frame_number_===sourceFrameNumber){if(earliestStart===undefined||slice.start<earliestStart){earliestStart=slice.start;}
+if(lastEnd===undefined||slice.end>lastEnd){lastEnd=slice.end;}}}}
+return{start:earliestStart,end:lastEnd};}
+function addPipelineHistograms(histograms,model,segments){const ranges=segments.map(s=>s.boundsRange);const bindEvents=new Map();for(const thread of model.getAllThreads()){for(const event of thread.sliceGroup.childEvents()){if(!eventIsValidGraphicsEvent_(event,bindEvents))continue;for(const range of ranges){if(range.containsExplicitRangeInclusive(event.start,event.end)){if(!bindEvents.has(event.bindId))bindEvents.set(event.bindId,{});break;}}
+if(bindEvents.has(event.bindId)){bindEvents.get(event.bindId)[event.args.step]=event;}}}
+const dcThread=getDisplayCompositorThread_(model);const drawEvents={};if(dcThread){const events=[...dcThread.findTopmostSlicesNamed('Graphics.Pipeline.DrawAndSwap')];for(const segment of segments){const filteredEvents=segment.boundsRange.filterArray(events,evt=>evt.start);for(const event of filteredEvents){if((event.args&&event.args.status==='canceled')||!event.id.startsWith(':ptr:')){continue;}
+const id=parseInt(event.id.substring(5),16);if(id in drawEvents){throw new Error('Duplicate draw events: '+id);}
+drawEvents[id]=event;}}}
+const issueToReceipt=histograms.createHistogram('pipeline:begin_frame_transport',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,[],{description:'Latency of begin-frame message from the display '+'compositor to the client, including the IPC latency and task-'+'queue time in the client.',summaryOptions:tr.metrics.rendering.SUMMARY_OPTIONS,});const issueToRasterStart=histograms.createHistogram('pipeline:begin_frame_to_raster_start',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,[],{description:'Latency between begin-frame message and '+'the beginning of the first CompositorTask run in the compositor.',summaryOptions:tr.metrics.rendering.SUMMARY_OPTIONS,});const issueToRasterEnd=histograms.createHistogram('pipeline:begin_frame_to_raster_end',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,[],{description:'Latency between begin-frame message and '+'the end of the last CompositorTask run in the compositor.',summaryOptions:tr.metrics.rendering.SUMMARY_OPTIONS,});const receiptToSubmit=histograms.createHistogram('pipeline:begin_frame_to_frame_submission',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,[],{description:'Latency between begin-frame reception and '+'CompositorFrame submission in the renderer.',summaryOptions:tr.metrics.rendering.SUMMARY_OPTIONS,});const submitToAggregate=histograms.createHistogram('pipeline:frame_submission_to_display',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,[],{description:'Latency between CompositorFrame submission in the '+'renderer to display in the display-compositor, including IPC '+'latency, task-queue time in the display-compositor, and '+'additional processing (e.g. surface-sync etc.)',summaryOptions:tr.metrics.rendering.SUMMARY_OPTIONS,});const aggregateToDraw=histograms.createHistogram('pipeline:draw',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,[],{description:'How long it takes for the gpu-swap step.',summaryOptions:tr.metrics.rendering.SUMMARY_OPTIONS,});for(const flow of bindEvents.values()){if(!flow.IssueBeginFrame||!flow.ReceiveBeginFrame||!flow.SubmitCompositorFrame||!flow.SurfaceAggregation){continue;}
+issueToReceipt.addSample(flow.ReceiveBeginFrame.start-
+flow.IssueBeginFrame.start);receiptToSubmit.addSample(flow.SubmitCompositorFrame.end-flow.ReceiveBeginFrame.start,{breakdown:generateBreakdownForCompositorPipelineInClient_(flow)});submitToAggregate.addSample(flow.SurfaceAggregation.end-flow.SubmitCompositorFrame.end,{breakdown:generateBreakdownForCompositorPipelineInService_(flow)});if(flow.SubmitCompositorFrame.parentSlice){const sourceFrameNumber=flow.SubmitCompositorFrame.parentSlice.args.source_frame_number_;const rasterDuration=getRasterTaskTimes(sourceFrameNumber,model);if(rasterDuration&&rasterDuration.start&&rasterDuration.end){const receiveToStart=rasterDuration.start-
+flow.ReceiveBeginFrame.start;const receiveToEnd=rasterDuration.end-flow.ReceiveBeginFrame.end;if(receiveToEnd>0){issueToRasterStart.addSample(receiveToStart>0?receiveToStart:0);issueToRasterEnd.addSample(receiveToEnd);}}}
+if(flow.SurfaceAggregation.args&&flow.SurfaceAggregation.args.display_trace){const displayTrace=flow.SurfaceAggregation.args.display_trace;if(!(displayTrace in drawEvents))continue;const drawEvent=drawEvents[displayTrace];aggregateToDraw.addSample(drawEvent.duration,{breakdown:generateBreakdownForDraw_(drawEvent)});}}}
+return{addPipelineHistograms,};});'use strict';tr.exportTo('tr.metrics.rendering',function(){const IMPL_THREAD_RENDERING_STATS_EVENT='BenchmarkInstrumentation::ImplThreadRenderingStats';const VISIBLE_CONTENT_DATA='visible_content_area';const APPROXIMATED_VISIBLE_CONTENT_DATA='approximated_visible_content_area';const CHECKERBOARDED_VISIBLE_CONTENT_DATA='checkerboarded_visible_content_area';function addPixelsHistograms(histograms,model,segments){const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);if(!chromeHelper)return;const approximatedPixelPercentages=[];const checkerboardedPixelPercentages=[];const ranges=segments.map(s=>s.boundsRange);for(const rendererHelper of Object.values(chromeHelper.rendererHelpers)){if(rendererHelper.compositorThread===undefined)continue;const slices=rendererHelper.compositorThread.sliceGroup;for(const slice of slices.getDescendantEventsInSortedRanges(ranges)){if(slice.title!==IMPL_THREAD_RENDERING_STATS_EVENT)continue;const data=slice.args.data;if(!(VISIBLE_CONTENT_DATA in data)){throw new Error(`${VISIBLE_CONTENT_DATA} is missing`);}
+const visibleContentArea=data[VISIBLE_CONTENT_DATA];if(visibleContentArea===0){continue;}
+if(APPROXIMATED_VISIBLE_CONTENT_DATA in data){approximatedPixelPercentages.push(data[APPROXIMATED_VISIBLE_CONTENT_DATA]/visibleContentArea);}
+if(CHECKERBOARDED_VISIBLE_CONTENT_DATA in data){checkerboardedPixelPercentages.push(data[CHECKERBOARDED_VISIBLE_CONTENT_DATA]/visibleContentArea);}}}
+histograms.createHistogram('mean_pixels_approximated',tr.b.Unit.byName.normalizedPercentage_smallerIsBetter,100*tr.b.math.Statistics.mean(approximatedPixelPercentages),{description:'Percentage of pixels that were approximated '+'(checkerboarding, low-resolution tiles, etc.).',summaryOptions:{},});histograms.createHistogram('mean_pixels_checkerboarded',tr.b.Unit.byName.normalizedPercentage_smallerIsBetter,100*tr.b.math.Statistics.mean(checkerboardedPixelPercentages),{description:'Percentage of pixels that were checkerboarded.',summaryOptions:{},});}
+return{addPixelsHistograms,};});'use strict';tr.exportTo('tr.metrics.rendering',function(){const BEGIN_MAIN_FRAME_EVENT='ThreadProxy::BeginMainFrame';const SEND_BEGIN_FRAME_EVENT='ThreadProxy::ScheduledActionSendBeginMainFrame';function getEventTimesByBeginFrameId_(thread,title,ranges){const out=new Map();const slices=thread.sliceGroup;for(const slice of slices.getDescendantEventsInSortedRanges(ranges)){if(slice.title!==title)continue;const id=slice.args.begin_frame_id;if(id===undefined)throw new Error('Event is missing begin_frame_id');if(out.has(id))throw new Error(`There must be exactly one ${title}`);out.set(id,slice.start);}
+return out;}
+function addQueueingDurationHistograms(histograms,model,segments){const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);if(!chromeHelper)return;let targetRenderers=chromeHelper.telemetryHelper.renderersWithIR;if(targetRenderers.length===0){targetRenderers=Object.values(chromeHelper.rendererHelpers);}
+const queueingDurations=[];const ranges=segments.map(s=>s.boundsRange);for(const rendererHelper of targetRenderers){const mainThread=rendererHelper.mainThread;const compositorThread=rendererHelper.compositorThread;if(mainThread===undefined||compositorThread===undefined)continue;const beginMainFrameTimes=getEventTimesByBeginFrameId_(mainThread,BEGIN_MAIN_FRAME_EVENT,ranges);const sendBeginFrameTimes=getEventTimesByBeginFrameId_(compositorThread,SEND_BEGIN_FRAME_EVENT,ranges);for(const[id,time]of sendBeginFrameTimes){queueingDurations.push(beginMainFrameTimes.get(id)-time);}}
+histograms.createHistogram('queueing_durations',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,queueingDurations,{binBoundaries:tr.v.HistogramBinBoundaries.createExponential(0.01,2,20),summaryOptions:tr.metrics.rendering.SUMMARY_OPTIONS,description:'Time between ScheduledActionSendBeginMainFrame in '+'the compositor thread and the corresponding '+'BeginMainFrame in the main thread.'});}
+return{addQueueingDurationHistograms,};});'use strict';tr.exportTo('tr.metrics.rendering',function(){const GESTURE_EVENT='SyntheticGestureController::running';function renderingMetric(histograms,model){const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);if(!chromeHelper)return;let segments=chromeHelper.telemetryHelper.irSegments;if(segments.length===0){segments=chromeHelper.telemetryHelper.animationSegments;}
+if(segments.length>0){tr.metrics.rendering.addFrameTimeHistograms(histograms,model,segments);tr.metrics.rendering.addImageDecodeTimeHistograms(histograms,model,segments);tr.metrics.rendering.addPipelineHistograms(histograms,model,segments);tr.metrics.rendering.addPixelsHistograms(histograms,model,segments);tr.metrics.rendering.addQueueingDurationHistograms(histograms,model,segments);}
+const uiSegments=chromeHelper.telemetryHelper.uiSegments;if(uiSegments.length>0){tr.metrics.rendering.addUIFrameTimeHistograms(histograms,model,chromeHelper.telemetryHelper.uiSegments);}}
+tr.metrics.MetricRegistry.register(renderingMetric,{requiredCategories:['benchmark','toplevel'],});return{renderingMetric,};});'use strict';tr.exportTo('tr.metrics',function(){const timeDurationInMs_smallerIsBetter=tr.b.Unit.byName.timeDurationInMs_smallerIsBetter;const unitlessNumber_smallerIsBetter=tr.b.Unit.byName.unitlessNumber_smallerIsBetter;const EventFinderUtils=tr.e.chrome.EventFinderUtils;const METRIC_BOUNDARIES=tr.v.HistogramBinBoundaries.createLinear(0,1e3,20).addLinearBins(3e3,20).addExponentialBins(80e3,30);const SUMMARY_OPTIONS={avg:true,count:false,max:true,min:true,std:true,sum:false,};function reportedByPageMetric(histograms,model){const timeToViewable=histograms.createHistogram('reported_by_page:time_to_viewable',timeDurationInMs_smallerIsBetter,[],{binBoundaries:METRIC_BOUNDARIES,description:'Time from navigation start'+'to telemetry:reported_by_page:viewable',summaryOptions:SUMMARY_OPTIONS,});const timeToInteractive=histograms.createHistogram('reported_by_page:time_to_interactive',timeDurationInMs_smallerIsBetter,[],{binBoundaries:METRIC_BOUNDARIES,description:'Time from navigation start '+'to telemetry:reported_by_page:interactive',summaryOptions:SUMMARY_OPTIONS,});const benchmarkTime=histograms.createHistogram('reported_by_page:benchmark_time',timeDurationInMs_smallerIsBetter,[],{binBoundaries:METRIC_BOUNDARIES,description:'Time from telemetry:reported_by_page:benchmark_begin '+'to telemetry:reported_by_page:benchmark_end',summaryOptions:SUMMARY_OPTIONS,});const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);for(const pid in chromeHelper.rendererHelpers){const rendererHelper=chromeHelper.rendererHelpers[pid];if(rendererHelper.isChromeTracingUI)continue;if(rendererHelper.mainThread===undefined)continue;measureUserTime(rendererHelper,'navigationStart','telemetry:reported_by_page:viewable',timeToViewable);measureUserTime(rendererHelper,'navigationStart','telemetry:reported_by_page:interactive',timeToInteractive);measureUserTime(rendererHelper,'telemetry:reported_by_page:benchmark_begin','telemetry:reported_by_page:benchmark_end',benchmarkTime);}}
+function measureUserTime(rendererHelper,startName,endName,histogram){const startEventByNavId=new Map();for(const event of rendererHelper.mainThread.sliceGroup.childEvents()){const navId=getNavigationId(event);if(!navId)continue;if(EventFinderUtils.hasCategoryAndName(event,'blink.user_timing',startName)){startEventByNavId.set(navId,event);}
+if(EventFinderUtils.hasCategoryAndName(event,'blink.user_timing',endName)){if(!startEventByNavId.has(navId)){throw Error(`Missing ${startName} for ${endName} at {event.start}`);}
+const range=tr.b.math.Range.fromExplicitRange(startEventByNavId.get(navId).start,event.start);histogram.addSample(range.duration);startEventByNavId.delete(navId);}}}
+function getNavigationId(event){return event.args.data&&event.args.data.navigationId;}
+tr.metrics.MetricRegistry.register(reportedByPageMetric);return{reportedByPageMetric};});'use strict';tr.exportTo('tr.metrics',function(){function sampleExceptionMetric(histograms,model){const hist=new tr.v.Histogram('foo',tr.b.Unit.byName.sizeInBytes_smallerIsBetter);hist.addSample(9);hist.addSample(91,{bar:new tr.v.d.GenericSet([{hello:42}])});for(const expectation of model.userModel.expectations){if(expectation instanceof tr.model.um.ResponseExpectation){}else if(expectation instanceof tr.model.um.AnimationExpectation){}else if(expectation instanceof tr.model.um.IdleExpectation){}else if(expectation instanceof tr.model.um.LoadExpectation){}}
+const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);for(const[pid,process]of Object.entries(model.processes)){}
+histograms.addHistogram(hist);throw new Error('There was an error');}
+tr.metrics.MetricRegistry.register(sampleExceptionMetric);return{sampleExceptionMetric,};});'use strict';tr.exportTo('tr.metrics',function(){function sampleMetric(histograms,model){const hist=new tr.v.Histogram('foo',tr.b.Unit.byName.sizeInBytes_smallerIsBetter);hist.addSample(9);hist.addSample(91,{bar:new tr.v.d.GenericSet([{hello:42}])});for(const expectation of model.userModel.expectations){if(expectation instanceof tr.model.um.ResponseExpectation){}else if(expectation instanceof tr.model.um.AnimationExpectation){}else if(expectation instanceof tr.model.um.IdleExpectation){}else if(expectation instanceof tr.model.um.LoadExpectation){}}
+const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);for(const[pid,process]of Object.entries(model.processes)){}
+histograms.addHistogram(hist);}
+tr.metrics.MetricRegistry.register(sampleMetric);return{sampleMetric,};});'use strict';tr.exportTo('tr.metrics',function(){const HANDLE_INPUT_EVENT_TITLE='WebViewImpl::handleInputEvent';function findPrecedingEvents_(eventsA,eventsB){const events=new Map();let eventsBIndex=0;for(const eventA of eventsA){for(;eventsBIndex<eventsB.length;eventsBIndex++){if(eventsB[eventsBIndex].start>eventA.start)break;}
+if(eventsBIndex>0){events.set(eventA,eventsB[eventsBIndex-1]);}}
+return events;}
+function findFollowingEvents_(eventsA,eventsB){const events=new Map();let eventsBIndex=0;for(const eventA of eventsA){for(;eventsBIndex<eventsB.length;eventsBIndex++){if(eventsB[eventsBIndex].start>=eventA.start)break;}
+if(eventsBIndex>=0&&eventsBIndex<eventsB.length){events.set(eventA,eventsB[eventsBIndex]);}}
+return events;}
+function getSpaNavigationStartCandidates_(rendererHelper,browserHelper){const isNavStartEvent=e=>{if(e.title===HANDLE_INPUT_EVENT_TITLE&&e.args.type==='MouseUp'){return true;}
+return e.title==='NavigationControllerImpl::GoToIndex';};return[...rendererHelper.mainThread.sliceGroup.getDescendantEvents(),...browserHelper.mainThread.sliceGroup.getDescendantEvents()].filter(isNavStartEvent);}
+function getSpaNavigationEvents_(rendererHelper){const isNavEvent=e=>e.category==='blink'&&e.title==='FrameLoader::updateForSameDocumentNavigation';return[...rendererHelper.mainThread.sliceGroup.getDescendantEvents()].filter(isNavEvent);}
+function getInputLatencyEvents_(browserHelper){const isInputLatencyEvent=e=>e.title==='InputLatency::MouseUp';return browserHelper.getAllAsyncSlicesMatching(isInputLatencyEvent);}
+function getInputLatencyEventByBindIdMap_(browserHelper){const inputLatencyEventByBindIdMap=new Map();for(const event of getInputLatencyEvents_(browserHelper)){inputLatencyEventByBindIdMap.set(event.args.data.trace_id,event);}
+return inputLatencyEventByBindIdMap;}
+function getSpaNavigationEventToNavigationStartMap_(rendererHelper,browserHelper){const mainThread=rendererHelper.mainThread;const spaNavEvents=getSpaNavigationEvents_(rendererHelper);const navStartCandidates=getSpaNavigationStartCandidates_(rendererHelper,browserHelper).sort(tr.importer.compareEvents);const spaNavEventToNavStartCandidateMap=findPrecedingEvents_(spaNavEvents,navStartCandidates);const inputLatencyEventByBindIdMap=getInputLatencyEventByBindIdMap_(browserHelper);const spaNavEventToNavStartEventMap=new Map();for(const[spaNavEvent,navStartCandidate]of
+spaNavEventToNavStartCandidateMap){if(navStartCandidate.title===HANDLE_INPUT_EVENT_TITLE){const inputLatencySlice=inputLatencyEventByBindIdMap.get(Number(navStartCandidate.parentSlice.bindId));if(inputLatencySlice){spaNavEventToNavStartEventMap.set(spaNavEvent,inputLatencySlice);}}else{spaNavEventToNavStartEventMap.set(spaNavEvent,navStartCandidate);}}
+return spaNavEventToNavStartEventMap;}
+function getFirstPaintEvents_(rendererHelper){const isFirstPaintEvent=e=>e.category==='blink'&&e.title==='PaintLayerCompositor::updateIfNeededRecursive';return[...rendererHelper.mainThread.sliceGroup.getDescendantEvents()].filter(isFirstPaintEvent);}
+function getSpaNavigationEventToFirstPaintEventMap_(rendererHelper){const spaNavEvents=getSpaNavigationEvents_(rendererHelper).sort(tr.importer.compareEvents);const firstPaintEvents=getFirstPaintEvents_(rendererHelper).sort(tr.importer.compareEvents);return findFollowingEvents_(spaNavEvents,firstPaintEvents);}
+function findSpaNavigationsOnRenderer(rendererHelper,browserHelper){const spaNavEventToNavStartMap=getSpaNavigationEventToNavigationStartMap_(rendererHelper,browserHelper);const spaNavEventToFirstPaintEventMap=getSpaNavigationEventToFirstPaintEventMap_(rendererHelper);const spaNavigations=[];for(const[spaNavEvent,navStartEvent]of
+spaNavEventToNavStartMap){if(spaNavEventToFirstPaintEventMap.has(spaNavEvent)){const firstPaintEvent=spaNavEventToFirstPaintEventMap.get(spaNavEvent);const isNavStartAsyncSlice=navStartEvent instanceof tr.model.AsyncSlice;spaNavigations.push({navStartCandidates:{inputLatencyAsyncSlice:isNavStartAsyncSlice?navStartEvent:undefined,goToIndexSlice:isNavStartAsyncSlice?undefined:navStartEvent},firstPaintEvent,url:spaNavEvent.args.url});}}
+return spaNavigations;}
+return{findSpaNavigationsOnRenderer,};});'use strict';tr.exportTo('tr.metrics.sh',function(){function getWallClockSelfTime_(event,rangeOfInterest){if(event.duration===0)return 0;const selfTimeRanges=[rangeOfInterest.findIntersection(event.range)];for(const subSlice of event.subSlices){if(selfTimeRanges.length===0)return 0;const lastRange=selfTimeRanges.pop();selfTimeRanges.push(...tr.b.math.Range.findDifference(lastRange,subSlice.range));}
+return tr.b.math.Statistics.sum(selfTimeRanges,r=>r.duration);}
+function getCPUSelfTime_(event,rangeOfInterest){if(event.duration===0||event.selfTime===0)return 0;if(event.cpuSelfTime===undefined)return 0;const cpuTimeDensity=event.cpuSelfTime/event.selfTime;return getWallClockSelfTime_(event,rangeOfInterest)*cpuTimeDensity;}
+function generateTimeBreakdownTree(mainThread,rangeOfInterest,getEventSelfTime){if(mainThread===null)return;const breakdownTree={};for(const title of
+tr.e.chrome.ChromeUserFriendlyCategoryDriver.ALL_TITLES){breakdownTree[title]={total:0,events:{}};}
+for(const event of mainThread.sliceGroup.childEvents()){if(!rangeOfInterest.intersectsRangeExclusive(event.range))continue;const eventSelfTime=getEventSelfTime(event,rangeOfInterest);const title=tr.e.chrome.ChromeUserFriendlyCategoryDriver.fromEvent(event);breakdownTree[title].total+=eventSelfTime;if(breakdownTree[title].events[event.title]===undefined){breakdownTree[title].events[event.title]=0;}
+breakdownTree[title].events[event.title]+=eventSelfTime;let timeIntersectionRatio=0;if(event.duration>0){timeIntersectionRatio=rangeOfInterest.findExplicitIntersectionDuration(event.start,event.end)/event.duration;}
+const v8Runtime=event.args['runtime-call-stat'];if(v8Runtime!==undefined){const v8RuntimeObject=JSON.parse(v8Runtime);for(const runtimeCall in v8RuntimeObject){if(v8RuntimeObject[runtimeCall].length===2){if(breakdownTree.v8_runtime.events[runtimeCall]===undefined){breakdownTree.v8_runtime.events[runtimeCall]=0;}
+const runtimeTime=tr.b.Unit.timestampFromUs(v8RuntimeObject[runtimeCall][1]*timeIntersectionRatio);breakdownTree.v8_runtime.total+=runtimeTime;breakdownTree.v8_runtime.events[runtimeCall]+=runtimeTime;}}}}
+return breakdownTree;}
+function addIdleAndBlockByNetworkBreakdown_(breakdownTree,mainThreadEvents,networkEvents,rangeOfInterest){const mainThreadEventRanges=tr.b.math.convertEventsToRanges(mainThreadEvents);const networkEventRanges=tr.b.math.convertEventsToRanges(networkEvents);const eventRanges=mainThreadEventRanges.concat(networkEventRanges);const idleRanges=tr.b.math.findEmptyRangesBetweenRanges(eventRanges,rangeOfInterest);const totalFreeDuration=tr.b.math.Statistics.sum(idleRanges,range=>range.duration);breakdownTree.idle={total:totalFreeDuration,events:{}};let totalBlockedDuration=rangeOfInterest.duration;for(const[title,component]of Object.entries(breakdownTree)){if(title==='v8_runtime')continue;totalBlockedDuration-=component.total;}
+breakdownTree.blocked_on_network={total:Math.max(totalBlockedDuration,0),events:{}};}
+function generateWallClockTimeBreakdownTree(mainThread,networkEvents,rangeOfInterest){const breakdownTree=generateTimeBreakdownTree(mainThread,rangeOfInterest,getWallClockSelfTime_);const mainThreadEventsInRange=tr.model.helpers.getSlicesIntersectingRange(rangeOfInterest,mainThread.sliceGroup.topLevelSlices);addIdleAndBlockByNetworkBreakdown_(breakdownTree,mainThreadEventsInRange,networkEvents,rangeOfInterest);return breakdownTree;}
+function generateCpuTimeBreakdownTree(mainThread,rangeOfInterest){return generateTimeBreakdownTree(mainThread,rangeOfInterest,getCPUSelfTime_);}
+return{generateTimeBreakdownTree,generateWallClockTimeBreakdownTree,generateCpuTimeBreakdownTree,};});'use strict';tr.exportTo('tr.e.chrome',function(){const LCP_CANDIDATE_EVENT_TITLE='NavStartToLargestContentfulPaint::Candidate::AllFrames::UKM';const LCP_INVALIDATE_EVENT_TITLE='NavStartToLargestContentfulPaint::Invalidate::AllFrames::UKM';class LcpEvent{constructor(event){if(!LcpInvalidateEvent.isLcpInvalidateEvent(event)&&!LcpCandidateEvent.isLcpCandidateEvent(event)){throw new Error('The LCP event should be either a candidate event or'+'an invalidate event.');}
+if(event.start===undefined||event.args.main_frame_tree_node_id===undefined){throw new Error('The LCP event is in unexpected format.');}
+this.start=event.start;this.mainFrameTreeNodeId=event.args.main_frame_tree_node_id;}}
+class LcpCandidateEvent extends LcpEvent{constructor(event){super(event);const{durationInMilliseconds,size,type,inMainFrame}=event.args.data;if(durationInMilliseconds===undefined||size===undefined||type===undefined||inMainFrame===undefined||event.args.main_frame_tree_node_id===undefined||!LcpCandidateEvent.isLcpCandidateEvent(event)){throw new Error('The LCP candidate event is in unexpected format.');}
+this.durationInMilliseconds=durationInMilliseconds;this.size=size;this.type=type;this.inMainFrame=inMainFrame;}
+static isLcpCandidateEvent(event){return event.title===LCP_CANDIDATE_EVENT_TITLE;}}
+class LcpInvalidateEvent extends LcpEvent{constructor(event){super(event);if(!LcpInvalidateEvent.isLcpInvalidateEvent(event)){throw new Error('The LCP invalidate event is in unexpected format.');}}
+static isLcpInvalidateEvent(event){return event.title===LCP_INVALIDATE_EVENT_TITLE;}}
+class LargestContentfulPaint{constructor(allBrowserEvents){this.allBrowserEvents=allBrowserEvents;}
+findCandidates(){const finalLcpEvents=this.findFinalLcpEventOfEachNavigation(this.allBrowserEvents);const finalCandidates=finalLcpEvents.filter(finalLcpEvent=>!LcpInvalidateEvent.isLcpInvalidateEvent(finalLcpEvent));return finalCandidates;}
+findFinalLcpEventOfEachNavigation(allBrowserEvents){const lcpEvents=[];for(const lcpEvent of allBrowserEvents){if(LcpCandidateEvent.isLcpCandidateEvent(lcpEvent)){lcpEvents.push(new LcpCandidateEvent(lcpEvent));}else if(LcpInvalidateEvent.isLcpInvalidateEvent(lcpEvent)){lcpEvents.push(new LcpInvalidateEvent(lcpEvent));}}
+const lcpEventsGroupedByNavigation=new Map();for(const e of lcpEvents){const key=e.mainFrameTreeNodeId;if(!lcpEventsGroupedByNavigation.has(key)){lcpEventsGroupedByNavigation.set(key,[]);}
+lcpEventsGroupedByNavigation.get(key).push(e);}
+const finalLcpEventOfEachNavigation=[];for(const lcpEventList of lcpEventsGroupedByNavigation.values()){lcpEventList.sort((a,b)=>a.start-b.start);finalLcpEventOfEachNavigation.push(lcpEventList[lcpEventList.length-1]);}
+return finalLcpEventOfEachNavigation;}}
+return{LCP_CANDIDATE_EVENT_TITLE,LCP_INVALIDATE_EVENT_TITLE,LargestContentfulPaint,};});'use strict';tr.exportTo('tr.b.math',function(){function earthMoversDistance(firstHistogram,secondHistogram){const buckets=firstHistogram.length;if(secondHistogram.length!==buckets){throw new Error('Histograms have a different number of bins.');}
+const arrSum=arr=>arr.reduce((a,b)=>a+b,0);if(arrSum(firstHistogram)!==arrSum(secondHistogram)){throw new Error('The histograms\' sizes don\'t match.');}
+let total=0;let remainder=0;for(let bucket=0;bucket<buckets;bucket++){remainder+=secondHistogram[bucket]-
+firstHistogram[bucket];total+=Math.abs(remainder);}
+return total;}
+return{earthMoversDistance,};});'use strict';tr.exportTo('tr.e.chrome',function(){const earthMoversDistance=tr.b.math.earthMoversDistance;class SpeedIndex{static getSnapshotsProgress_(timestampedColorHistograms){const numberOfScreenshots=timestampedColorHistograms.length;const firstHistogram=timestampedColorHistograms[0].colorHistogram;const lastHistogram=timestampedColorHistograms[numberOfScreenshots-1].colorHistogram;const totalDistance=earthMoversDistance(firstHistogram[0],lastHistogram[0])+
+earthMoversDistance(firstHistogram[1],lastHistogram[1])+
+earthMoversDistance(firstHistogram[2],lastHistogram[2]);if(totalDistance===0){return[{value:1,ts:timestampedColorHistograms[0].ts}];}
+const snapshotsProgress=new Array(numberOfScreenshots);for(let i=0;i<numberOfScreenshots;i++){const histogram=timestampedColorHistograms[i].colorHistogram;const distance=earthMoversDistance(histogram[0],lastHistogram[0])+
+earthMoversDistance(histogram[1],lastHistogram[1])+
+earthMoversDistance(histogram[2],lastHistogram[2]);const moved=Math.max(totalDistance-distance,0);snapshotsProgress[i]={value:(moved/totalDistance),ts:timestampedColorHistograms[i].ts};}
+return snapshotsProgress;}
+static speedIndexFromSnapshotsProgress_(snapshotsProgress){if(snapshotsProgress.length===0){throw new Error('No snapshots were provided.');}
+let prevSnapshotTimeTaken=0;let prevSnapshotProgress=0;let speedIndex=0;const numberOfScreenshots=snapshotsProgress.length;for(let i=0;i<numberOfScreenshots;i++){const elapsed=snapshotsProgress[i].ts-prevSnapshotTimeTaken;speedIndex+=elapsed*(1.0-prevSnapshotProgress);prevSnapshotTimeTaken=snapshotsProgress[i].ts;prevSnapshotProgress=snapshotsProgress[i].value;}
+return Math.round(speedIndex);}
+static createColorHistogram(imagePixelValues){const n=imagePixelValues.length;const histogram=new Array(3);for(let j=0;j<3;j++){histogram[j]=new Array(256).fill(0);}
+for(let i=0;i<n;i+=4){const r=imagePixelValues[i];const g=imagePixelValues[i+1];const b=imagePixelValues[i+2];histogram[0][r]++;histogram[1][g]++;histogram[2][b]++;}
+return histogram;}
+static calculateSpeedIndex(timestampedColorHistograms){const snapshotsProgress=SpeedIndex.getSnapshotsProgress_(timestampedColorHistograms);return SpeedIndex.speedIndexFromSnapshotsProgress_(snapshotsProgress);}
+static lineSweep(lineSweepRects,viewport){const verticalSweepEdges=[];const horizontalSweepEdges=[];for(let i=0;i<lineSweepRects.length;i++){const rect=lineSweepRects[i];let left=rect.left;let right=rect.right;let top=rect.top;let bottom=rect.bottom;if(left>viewport.x+viewport.width)continue;if(right<viewport.x)continue;if(top>viewport.y+viewport.height)continue;if(bottom<viewport.y)continue;left=Math.max(left,viewport.y);right=Math.min(right,viewport.y+viewport.width);top=Math.max(top,viewport.y);bottom=Math.min(bottom,viewport.y+viewport.height);verticalSweepEdges.push({id:i,value:left,type:'left'},{id:i,value:right,type:'right'});horizontalSweepEdges.push({id:i,value:top,type:'top'},{id:i,value:bottom,type:'bottom'});}
+if(verticalSweepEdges.length===0||horizontalSweepEdges.length===0){return 0;}
+verticalSweepEdges.sort((a,b)=>a.value-b.value);horizontalSweepEdges.sort((a,b)=>a.value-b.value);const active=new Array(lineSweepRects.length).fill(false);let area=0;active[verticalSweepEdges[0].id]=true;for(let i=1;i<verticalSweepEdges.length;i++){const currentLine=verticalSweepEdges[i];const previousLine=verticalSweepEdges[i-1];const deltaX=currentLine.value-previousLine.value;if(deltaX===0)continue;let count=0;let firstRect;for(let j=0;j<horizontalSweepEdges.length;j++){if(active[horizontalSweepEdges[j].id]===true){if(horizontalSweepEdges[j].type==='top'){if(count===0){firstRect=j;}
+count++;}else{if(count===1){const deltaY=horizontalSweepEdges[j].value-
+horizontalSweepEdges[firstRect].value;area+=deltaX*deltaY;}
+count--;}}}
+active[currentLine.id]=(currentLine.type==='left');}
+return area;}
+static quadToRect(quad){const left=Math.min(quad[0],quad[2],quad[4]);const right=Math.max(quad[0],quad[2],quad[4]);const top=Math.min(quad[1],quad[3],quad[5]);const bottom=Math.max(quad[1],quad[3],quad[5]);return{left,right,top,bottom};}
+static calculateRectsBasedSpeedIndex(timestampedPaintRects,viewport){const numberOfRects=timestampedPaintRects.length;if(numberOfRects===0){throw new Error('Can\'t calculate speed index without any paint '+'rectangles.');}
+const areaAddedAtTimestamp=new Array(numberOfRects);const rects=[];let previousAreaOfUnion=0;let totalAreaOfUnion=0;for(let i=numberOfRects-1;i>=0;i--){rects.push(timestampedPaintRects[i].rect);const currentAreaOfUnion=SpeedIndex.lineSweep(rects,viewport);areaAddedAtTimestamp[i]={value:currentAreaOfUnion-previousAreaOfUnion,ts:timestampedPaintRects[i].ts};totalAreaOfUnion+=areaAddedAtTimestamp[i].value;previousAreaOfUnion=currentAreaOfUnion;}
+const paintProgressAtTimestamp=new Array(numberOfRects);let lastProgressRecorded=0;for(let i=0;i<numberOfRects;i++){paintProgressAtTimestamp[i]={value:areaAddedAtTimestamp[i].value/totalAreaOfUnion+
+lastProgressRecorded,ts:areaAddedAtTimestamp[i].ts};lastProgressRecorded=paintProgressAtTimestamp[i].value;}
+return SpeedIndex.speedIndexFromSnapshotsProgress_(paintProgressAtTimestamp);}}
+return{SpeedIndex,};});'use strict';tr.exportTo('tr.metrics.sh',function(){const timeDurationInMs_smallerIsBetter=tr.b.Unit.byName.timeDurationInMs_smallerIsBetter;const SpeedIndex=tr.e.chrome.SpeedIndex;const EventFinderUtils=tr.e.chrome.EventFinderUtils;const BIN_BOUNDARIES=tr.v.HistogramBinBoundaries.createLinear(0,1e3,20).addLinearBins(3e3,20).addExponentialBins(20e3,20);const SUMMARY_OPTIONS={avg:true,count:false,max:true,min:true,std:true,sum:false,};function addRectsBasedSpeedIndexSample(samples,rendererHelper,navigationStart,loadDuration,frameID){let viewport;for(const event of EventFinderUtils.getMainThreadEvents(rendererHelper,'viewport','loading')){if(event.args.data.frameID===frameID&&event.start<(navigationStart+loadDuration)){viewport=event.args.data;}}
+if(!viewport)return;const timestampedPaintRects=[];for(const event of EventFinderUtils.getMainThreadEvents(rendererHelper,'PaintTimingVisualizer::LayoutObjectPainted','loading')){if(event.start>=navigationStart&&event.start<navigationStart+loadDuration){const paintRect=event.args.data.rect;if(!paintRect)continue;timestampedPaintRects.push({rect:SpeedIndex.quadToRect(paintRect),ts:event.start});}}
+const numberOfRects=timestampedPaintRects.length;if(numberOfRects===0)return;samples.push({value:SpeedIndex.calculateRectsBasedSpeedIndex(timestampedPaintRects,viewport)-navigationStart});}
+function collectRectsBasedSpeedIndexSamplesFromLoadExpectations(model,chromeHelper){const rectsBasedSpeedIndexSamples=[];for(const expectation of model.userModel.expectations){if(!(expectation instanceof tr.model.um.LoadExpectation))continue;if(tr.e.chrome.CHROME_INTERNAL_URLS.includes(expectation.url)){continue;}
+const rendererHelper=chromeHelper.rendererHelpers[expectation.renderProcess.pid];addRectsBasedSpeedIndexSample(rectsBasedSpeedIndexSamples,rendererHelper,expectation.navigationStart.start,expectation.duration,expectation.navigationStart.args.frame);}
+return rectsBasedSpeedIndexSamples;}
+function rectsBasedSpeedIndexMetric(histograms,model){const rectsBasedSpeedIndexHistogram=histograms.createHistogram('rectsBasedSpeedIndex',timeDurationInMs_smallerIsBetter,[],{binBoundaries:BIN_BOUNDARIES,description:' the average time at which visible parts of the'+' page are displayed (in ms).',summaryOptions:SUMMARY_OPTIONS,});const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);const samples=collectRectsBasedSpeedIndexSamplesFromLoadExpectations(model,chromeHelper);for(const sample of samples){rectsBasedSpeedIndexHistogram.addSample(sample.value);}}
+tr.metrics.MetricRegistry.register(rectsBasedSpeedIndexMetric);return{rectsBasedSpeedIndexMetric};});'use strict';tr.exportTo('tr.metrics.sh',function(){const LONG_TASK_THRESHOLD_MS=50;const timeDurationInMs_smallerIsBetter=tr.b.Unit.byName.timeDurationInMs_smallerIsBetter;const unitlessNumber_smallerIsBetter=tr.b.Unit.byName.unitlessNumber_smallerIsBetter;const RelatedEventSet=tr.v.d.RelatedEventSet;const hasCategoryAndName=tr.metrics.sh.hasCategoryAndName;const EventFinderUtils=tr.e.chrome.EventFinderUtils;function createBreakdownDiagnostic(breakdownTree){const breakdownDiagnostic=new tr.v.d.Breakdown();breakdownDiagnostic.colorScheme=tr.v.d.COLOR_SCHEME_CHROME_USER_FRIENDLY_CATEGORY_DRIVER;for(const label in breakdownTree){breakdownDiagnostic.set(label,breakdownTree[label].total);}
+return breakdownDiagnostic;}
+const LOADING_METRIC_BOUNDARIES=tr.v.HistogramBinBoundaries.createLinear(0,1e3,20).addLinearBins(3e3,20).addExponentialBins(20e3,20);const TIME_TO_INTERACTIVE_BOUNDARIES=tr.v.HistogramBinBoundaries.createExponential(1,40e3,35).addExponentialBins(80e3,15);const LAYOUT_SHIFT_SCORE_BOUNDARIES=tr.v.HistogramBinBoundaries.createLinear(0,50,25);const SUMMARY_OPTIONS={avg:true,count:false,max:true,min:true,std:true,sum:false,};function findFrameLoaderSnapshotAt(rendererHelper,frameIdRef,ts){const objects=rendererHelper.process.objects;const frameLoaderInstances=objects.instancesByTypeName_.FrameLoader;if(frameLoaderInstances===undefined)return undefined;let snapshot;for(const instance of frameLoaderInstances){if(!instance.isAliveAt(ts))continue;const maybeSnapshot=instance.getSnapshotAt(ts);if(frameIdRef!==maybeSnapshot.args.frame.id_ref)continue;snapshot=maybeSnapshot;}
+return snapshot;}
+function findAllEvents(rendererHelper,category,title){const targetEvents=[];for(const ev of rendererHelper.process.getDescendantEvents()){if(!hasCategoryAndName(ev,category,title))continue;targetEvents.push(ev);}
+return targetEvents;}
+function getMostRecentValidEvent(rendererHelper,category,title){const targetEvents=findAllEvents(rendererHelper,category,title);let validEvent;for(const targetEvent of targetEvents){if(rendererHelper.isTelemetryInternalEvent(targetEvent))continue;if(validEvent===undefined){validEvent=targetEvent;}else{if(validEvent.start<targetEvent.start){validEvent=targetEvent;}}}
+return validEvent;}
+function getFirstViewportReadySamples(rendererHelper,navIdToNavStartEvents){const samples=[];const pcEvent=getMostRecentValidEvent(rendererHelper,'blink.user_timing','pc');if(pcEvent===undefined)return samples;if(rendererHelper.isTelemetryInternalEvent(pcEvent))return samples;const navigationStartEvent=navIdToNavStartEvents.get(pcEvent.args.data.navigationId);if(navigationStartEvent===undefined)return samples;const navStartToEventRange=tr.b.math.Range.fromExplicitRange(navigationStartEvent.start,pcEvent.start);const networkEvents=EventFinderUtils.getNetworkEventsInRange(rendererHelper.process,navStartToEventRange);if(rendererHelper.mainThread===undefined)return samples;const breakdownTree=tr.metrics.sh.generateWallClockTimeBreakdownTree(rendererHelper.mainThread,networkEvents,navStartToEventRange);samples.push({value:navStartToEventRange.duration,breakdownTree,diagnostics:{breakdown:createBreakdownDiagnostic(breakdownTree),Start:new RelatedEventSet(navigationStartEvent),End:new RelatedEventSet(pcEvent)}});return samples;}
+function getAboveTheFoldLoadedToVisibleSamples(rendererHelper){const samples=[];const pcEvent=getMostRecentValidEvent(rendererHelper,'blink.user_timing','pc');const visibleEvent=getMostRecentValidEvent(rendererHelper,'blink.user_timing','visible');if(pcEvent!==undefined&&visibleEvent!==undefined){samples.push({value:Math.max(0.0,pcEvent.start-visibleEvent.start),diagnostics:{Start:new RelatedEventSet(visibleEvent),End:new RelatedEventSet(pcEvent)}});}
+return samples;}
+function findTimeToXEntries(category,eventName,rendererHelper,frameToNavStartEvents,navIdToNavStartEvents){const targetEvents=findAllEvents(rendererHelper,category,eventName);const entries=[];for(const targetEvent of targetEvents){if(rendererHelper.isTelemetryInternalEvent(targetEvent))continue;const frameIdRef=targetEvent.args.frame;const snapshot=findFrameLoaderSnapshotAt(rendererHelper,frameIdRef,targetEvent.start);if(snapshot===undefined||!snapshot.args.isLoadingMainFrame)continue;const url=snapshot.args.documentLoaderURL;if(tr.e.chrome.CHROME_INTERNAL_URLS.includes(url))continue;let navigationStartEvent;if(targetEvent.args.data===undefined||targetEvent.args.data.navigationId===undefined){navigationStartEvent=EventFinderUtils.findLastEventStartingOnOrBeforeTimestamp(frameToNavStartEvents.get(frameIdRef)||[],targetEvent.start);}else{navigationStartEvent=navIdToNavStartEvents.get(targetEvent.args.data.navigationId);}
+if(navigationStartEvent===undefined)continue;entries.push({navigationStartEvent,targetEvent,url,});}
+return entries;}
+function collectTimeToEvent(rendererHelper,timeToXEntries){const samples=[];for(const{targetEvent,navigationStartEvent,url}of timeToXEntries){const navStartToEventRange=tr.b.math.Range.fromExplicitRange(navigationStartEvent.start,targetEvent.start);const networkEvents=EventFinderUtils.getNetworkEventsInRange(rendererHelper.process,navStartToEventRange);const breakdownTree=tr.metrics.sh.generateWallClockTimeBreakdownTree(rendererHelper.mainThread,networkEvents,navStartToEventRange);samples.push({value:navStartToEventRange.duration,breakdownTree,diagnostics:{breakdown:createBreakdownDiagnostic(breakdownTree),url:new tr.v.d.GenericSet([url]),Start:new RelatedEventSet(navigationStartEvent),End:new RelatedEventSet(targetEvent)}});}
+return samples;}
+function collectTimeToEventInCpuTime(rendererHelper,timeToXEntries){const samples=[];for(const{targetEvent,navigationStartEvent,url}of timeToXEntries){const navStartToEventRange=tr.b.math.Range.fromExplicitRange(navigationStartEvent.start,targetEvent.start);const mainThreadCpuTime=rendererHelper.mainThread.getCpuTimeForRange(navStartToEventRange);const breakdownTree=tr.metrics.sh.generateCpuTimeBreakdownTree(rendererHelper.mainThread,navStartToEventRange);samples.push({value:mainThreadCpuTime,breakdownTree,diagnostics:{breakdown:createBreakdownDiagnostic(breakdownTree),start:new RelatedEventSet(navigationStartEvent),end:new RelatedEventSet(targetEvent),infos:new tr.v.d.GenericSet([{pid:rendererHelper.pid,start:navigationStartEvent.start,event:targetEvent.start,}]),}});}
+return samples;}
+function findLayoutShiftSamples(rendererHelper){let sample;EventFinderUtils.getSortedMainThreadEventsByFrame(rendererHelper,'LayoutShift','loading').forEach((events)=>{const evData=events.pop().args.data;if(evData.is_main_frame){sample={value:evData.cumulative_score};}});return sample?[sample]:[];}
+function addFirstMeaningfulPaintSample(samples,rendererHelper,navigationStart,fmpMarkerEvent,url){const navStartToFMPRange=tr.b.math.Range.fromExplicitRange(navigationStart.start,fmpMarkerEvent.start);const networkEvents=EventFinderUtils.getNetworkEventsInRange(rendererHelper.process,navStartToFMPRange);const timeToFirstMeaningfulPaint=navStartToFMPRange.duration;const breakdownTree=tr.metrics.sh.generateWallClockTimeBreakdownTree(rendererHelper.mainThread,networkEvents,navStartToFMPRange);samples.push({value:timeToFirstMeaningfulPaint,breakdownTree,diagnostics:{breakdown:createBreakdownDiagnostic(breakdownTree),start:new RelatedEventSet(navigationStart),end:new RelatedEventSet(fmpMarkerEvent),infos:new tr.v.d.GenericSet([{url,pid:rendererHelper.pid,start:navigationStart.start,fmp:fmpMarkerEvent.start,}]),}});}
+function addFirstMeaningfulPaintCpuTimeSample(samples,rendererHelper,navigationStart,fmpMarkerEvent,url){const navStartToFMPRange=tr.b.math.Range.fromExplicitRange(navigationStart.start,fmpMarkerEvent.start);const mainThreadCpuTime=rendererHelper.mainThread.getCpuTimeForRange(navStartToFMPRange);const breakdownTree=tr.metrics.sh.generateCpuTimeBreakdownTree(rendererHelper.mainThread,navStartToFMPRange);samples.push({value:mainThreadCpuTime,breakdownTree,diagnostics:{breakdown:createBreakdownDiagnostic(breakdownTree),start:new RelatedEventSet(navigationStart),end:new RelatedEventSet(fmpMarkerEvent),infos:new tr.v.d.GenericSet([{url,pid:rendererHelper.pid,start:navigationStart.start,fmp:fmpMarkerEvent.start,}]),}});}
+function decorateInteractivitySampleWithDiagnostics_(rendererHelper,eventTimestamp,navigationStartEvent,firstMeaningfulPaintTime,domContentLoadedEndTime,url){if(eventTimestamp===undefined)return undefined;const navigationStartTime=navigationStartEvent.start;const navStartToEventTimeRange=tr.b.math.Range.fromExplicitRange(navigationStartTime,eventTimestamp);const networkEvents=EventFinderUtils.getNetworkEventsInRange(rendererHelper.process,navStartToEventTimeRange);const breakdownTree=tr.metrics.sh.generateWallClockTimeBreakdownTree(rendererHelper.mainThread,networkEvents,navStartToEventTimeRange);const breakdownDiagnostic=createBreakdownDiagnostic(breakdownTree);return{value:navStartToEventTimeRange.duration,diagnostics:tr.v.d.DiagnosticMap.fromObject({'Start':new RelatedEventSet(navigationStartEvent),'Navigation infos':new tr.v.d.GenericSet([{url,pid:rendererHelper.pid,navigationStartTime,firstMeaningfulPaintTime,domContentLoadedEndTime,eventTimestamp,}]),'Breakdown of [navStart, eventTimestamp]':breakdownDiagnostic,}),};}
+function getCandidateIndex(entry){return entry.targetEvent.args.data.candidateIndex;}
+function findLastCandidateForEachNavigation(timeToXEntries){const entryMap=new Map();for(const e of timeToXEntries){const navStartEvent=e.navigationStartEvent;if(!entryMap.has(navStartEvent)){entryMap.set(navStartEvent,[]);}
+entryMap.get(navStartEvent).push(e);}
+const lastCandidates=[];for(const timeToXEntriesByNavigation of entryMap.values()){let lastCandidate=timeToXEntriesByNavigation.shift();for(const entry of timeToXEntriesByNavigation){if(getCandidateIndex(entry)>getCandidateIndex(lastCandidate)){lastCandidate=entry;}}
+lastCandidates.push(lastCandidate);}
+return lastCandidates;}
+function findLargestTextPaintSamples(rendererHelper,frameToNavStartEvents,navIdToNavStartEvents){const timeToPaintEntries=findTimeToXEntries('loading','LargestTextPaint::Candidate',rendererHelper,frameToNavStartEvents,navIdToNavStartEvents);const timeToPaintBlockingEntries=findTimeToXEntries('loading','LargestTextPaint::NoCandidate',rendererHelper,frameToNavStartEvents,navIdToNavStartEvents);const lastCandidateEvents=findLastCandidateForEachNavigation(timeToPaintEntries.concat(timeToPaintBlockingEntries)).filter(event=>event.targetEvent.title!=='LargestTextPaint::NoCandidate');return collectTimeToEvent(rendererHelper,lastCandidateEvents);}
+function findLargestImagePaintSamples(rendererHelper,frameToNavStartEvents,navIdToNavStartEvents){const timeToPaintEntries=findTimeToXEntries('loading','LargestImagePaint::Candidate',rendererHelper,frameToNavStartEvents,navIdToNavStartEvents);const timeToPaintBlockingEntries=findTimeToXEntries('loading','LargestImagePaint::NoCandidate',rendererHelper,frameToNavStartEvents,navIdToNavStartEvents);const lastCandidateEvents=findLastCandidateForEachNavigation(timeToPaintEntries.concat(timeToPaintBlockingEntries)).filter(event=>event.targetEvent.title!=='LargestImagePaint::NoCandidate');return collectTimeToEvent(rendererHelper,lastCandidateEvents);}
+function findLargestContentfulPaintHistogramSamples(allBrowserEvents){const lcp=new tr.e.chrome.LargestContentfulPaint(allBrowserEvents);const lcpSamples=lcp.findCandidates().map(candidate=>{const{durationInMilliseconds,size,type,inMainFrame,mainFrameTreeNodeId}=candidate;return{value:durationInMilliseconds,diagnostics:{size:new tr.v.d.GenericSet([size]),type:new tr.v.d.GenericSet([type]),inMainFrame:new tr.v.d.GenericSet([inMainFrame]),mainFrameTreeNodeId:new tr.v.d.GenericSet([mainFrameTreeNodeId]),},};});return lcpSamples;}
+function collectLoadingMetricsForRenderer(rendererHelper){const frameToNavStartEvents=EventFinderUtils.getSortedMainThreadEventsByFrame(rendererHelper,'navigationStart','blink.user_timing');const navIdToNavStartEvents=EventFinderUtils.getSortedMainThreadEventsByNavId(rendererHelper,'navigationStart','blink.user_timing');const firstPaintSamples=collectTimeToEvent(rendererHelper,findTimeToXEntries('loading','firstPaint',rendererHelper,frameToNavStartEvents,navIdToNavStartEvents));const timeToFCPEntries=findTimeToXEntries('loading','firstContentfulPaint',rendererHelper,frameToNavStartEvents,navIdToNavStartEvents);const firstContentfulPaintSamples=collectTimeToEvent(rendererHelper,timeToFCPEntries);const firstContentfulPaintCpuTimeSamples=collectTimeToEventInCpuTime(rendererHelper,timeToFCPEntries);const onLoadSamples=collectTimeToEvent(rendererHelper,findTimeToXEntries('blink.user_timing','loadEventStart',rendererHelper,frameToNavStartEvents,navIdToNavStartEvents));const aboveTheFoldLoadedToVisibleSamples=getAboveTheFoldLoadedToVisibleSamples(rendererHelper);const firstViewportReadySamples=getFirstViewportReadySamples(rendererHelper,navIdToNavStartEvents);const largestImagePaintSamples=findLargestImagePaintSamples(rendererHelper,frameToNavStartEvents,navIdToNavStartEvents);const largestTextPaintSamples=findLargestTextPaintSamples(rendererHelper,frameToNavStartEvents,navIdToNavStartEvents);const layoutShiftSamples=findLayoutShiftSamples(rendererHelper);const navigationStartSamples=timeToFCPEntries.map(entry=>{return{value:entry.navigationStartEvent.start};});return{frameToNavStartEvents,firstPaintSamples,firstContentfulPaintSamples,firstContentfulPaintCpuTimeSamples,onLoadSamples,aboveTheFoldLoadedToVisibleSamples,firstViewportReadySamples,largestImagePaintSamples,largestTextPaintSamples,layoutShiftSamples,navigationStartSamples,};}
+function collectMetricsFromLoadExpectations(model,chromeHelper){const interactiveSamples=[];const firstCpuIdleSamples=[];const firstMeaningfulPaintSamples=[];const firstMeaningfulPaintCpuTimeSamples=[];for(const expectation of model.userModel.expectations){if(!(expectation instanceof tr.model.um.LoadExpectation))continue;if(tr.e.chrome.CHROME_INTERNAL_URLS.includes(expectation.url)){continue;}
+const rendererHelper=chromeHelper.rendererHelpers[expectation.renderProcess.pid];if(expectation.fmpEvent!==undefined){addFirstMeaningfulPaintSample(firstMeaningfulPaintSamples,rendererHelper,expectation.navigationStart,expectation.fmpEvent,expectation.url);addFirstMeaningfulPaintCpuTimeSample(firstMeaningfulPaintCpuTimeSamples,rendererHelper,expectation.navigationStart,expectation.fmpEvent,expectation.url);}
+if(expectation.firstCpuIdleTime!==undefined){firstCpuIdleSamples.push(decorateInteractivitySampleWithDiagnostics_(rendererHelper,expectation.firstCpuIdleTime,expectation.navigationStart,expectation.fmpEvent.start,expectation.domContentLoadedEndEvent.start,expectation.url));}
+if(expectation.timeToInteractive!==undefined){interactiveSamples.push(decorateInteractivitySampleWithDiagnostics_(rendererHelper,expectation.timeToInteractive,expectation.navigationStart,expectation.fmpEvent.start,expectation.domContentLoadedEndEvent.start,expectation.url));}}
+return{firstMeaningfulPaintSamples,firstMeaningfulPaintCpuTimeSamples,firstCpuIdleSamples,interactiveSamples,};}
+function addSamplesToHistogram(samples,histogram,histograms){for(const sample of samples){histogram.addSample(sample.value,sample.diagnostics);if(histogram.name!=='timeToFirstContentfulPaint')continue;if(!sample.breakdownTree)continue;for(const[category,breakdown]of Object.entries(sample.breakdownTree)){const relatedName=`${histogram.name}:${category}`;let relatedHist=histograms.getHistogramsNamed(relatedName)[0];if(!relatedHist){relatedHist=histograms.createHistogram(relatedName,histogram.unit,[],{binBoundaries:LOADING_METRIC_BOUNDARIES,summaryOptions:{count:false,max:false,min:false,sum:false,},});let relatedNames=histogram.diagnostics.get('breakdown');if(!relatedNames){relatedNames=new tr.v.d.RelatedNameMap();histogram.diagnostics.set('breakdown',relatedNames);}
+relatedNames.set(category,relatedName);}
+relatedHist.addSample(breakdown.total,{breakdown:tr.v.d.Breakdown.fromEntries(Object.entries(breakdown.events)),});}}}
+function loadingMetric(histograms,model){const firstPaintHistogram=histograms.createHistogram('timeToFirstPaint',timeDurationInMs_smallerIsBetter,[],{binBoundaries:LOADING_METRIC_BOUNDARIES,description:'time to first paint',summaryOptions:SUMMARY_OPTIONS,});const firstContentfulPaintHistogram=histograms.createHistogram('timeToFirstContentfulPaint',timeDurationInMs_smallerIsBetter,[],{binBoundaries:LOADING_METRIC_BOUNDARIES,description:'time to first contentful paint',summaryOptions:SUMMARY_OPTIONS,});const firstContentfulPaintCpuTimeHistogram=histograms.createHistogram('cpuTimeToFirstContentfulPaint',timeDurationInMs_smallerIsBetter,[],{binBoundaries:LOADING_METRIC_BOUNDARIES,description:'CPU time to first contentful paint',summaryOptions:SUMMARY_OPTIONS,});const onLoadHistogram=histograms.createHistogram('timeToOnload',timeDurationInMs_smallerIsBetter,[],{binBoundaries:LOADING_METRIC_BOUNDARIES,description:'time to onload. '+'This is temporary metric used for PCv1/v2 sanity checking',summaryOptions:SUMMARY_OPTIONS,});const firstMeaningfulPaintHistogram=histograms.createHistogram('timeToFirstMeaningfulPaint',timeDurationInMs_smallerIsBetter,[],{binBoundaries:LOADING_METRIC_BOUNDARIES,description:'time to first meaningful paint',summaryOptions:SUMMARY_OPTIONS,});const firstMeaningfulPaintCpuTimeHistogram=histograms.createHistogram('cpuTimeToFirstMeaningfulPaint',timeDurationInMs_smallerIsBetter,[],{binBoundaries:LOADING_METRIC_BOUNDARIES,description:'CPU time to first meaningful paint',summaryOptions:SUMMARY_OPTIONS,});const timeToInteractiveHistogram=histograms.createHistogram('timeToInteractive',timeDurationInMs_smallerIsBetter,[],{binBoundaries:TIME_TO_INTERACTIVE_BOUNDARIES,description:'Time to Interactive',summaryOptions:SUMMARY_OPTIONS,});const timeToFirstCpuIdleHistogram=histograms.createHistogram('timeToFirstCpuIdle',timeDurationInMs_smallerIsBetter,[],{binBoundaries:TIME_TO_INTERACTIVE_BOUNDARIES,description:'Time to First CPU Idle',summaryOptions:SUMMARY_OPTIONS,});const aboveTheFoldLoadedToVisibleHistogram=histograms.createHistogram('aboveTheFoldLoadedToVisible',timeDurationInMs_smallerIsBetter,[],{binBoundaries:TIME_TO_INTERACTIVE_BOUNDARIES,description:'Time from first visible to load for AMP pages only.',summaryOptions:SUMMARY_OPTIONS,});const firstViewportReadyHistogram=histograms.createHistogram('timeToFirstViewportReady',timeDurationInMs_smallerIsBetter,[],{binBoundaries:TIME_TO_INTERACTIVE_BOUNDARIES,description:'Time from navigation to load for AMP pages only. ',summaryOptions:SUMMARY_OPTIONS,});const largestImagePaintHistogram=histograms.createHistogram('largestImagePaint',timeDurationInMs_smallerIsBetter,[],{binBoundaries:LOADING_METRIC_BOUNDARIES,description:'Time to Largest Image Paint',summaryOptions:SUMMARY_OPTIONS,});const largestTextPaintHistogram=histograms.createHistogram('largestTextPaint',timeDurationInMs_smallerIsBetter,[],{binBoundaries:LOADING_METRIC_BOUNDARIES,description:'Time to Largest Text Paint',summaryOptions:SUMMARY_OPTIONS,});const largestContentfulPaintHistogram=histograms.createHistogram('largestContentfulPaint',timeDurationInMs_smallerIsBetter,[],{binBoundaries:LOADING_METRIC_BOUNDARIES,description:'Time to Largest Contentful Paint',summaryOptions:SUMMARY_OPTIONS,});const layoutShiftHistogram=histograms.createHistogram('mainFrameCumulativeLayoutShift',unitlessNumber_smallerIsBetter,[],{binBoundaries:LAYOUT_SHIFT_SCORE_BOUNDARIES,description:'Main Frame Document Cumulative Layout Shift Score',summaryOptions:SUMMARY_OPTIONS,});const navigationStartHistogram=histograms.createHistogram('navigationStart',timeDurationInMs_smallerIsBetter,[],{binBoundaries:LOADING_METRIC_BOUNDARIES,description:'navigationStart',summaryOptions:SUMMARY_OPTIONS,});tr.metrics.sh.rectsBasedSpeedIndexMetric(histograms,model);const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);for(const pid in chromeHelper.rendererHelpers){const rendererHelper=chromeHelper.rendererHelpers[pid];if(rendererHelper.isChromeTracingUI)continue;const samplesSet=collectLoadingMetricsForRenderer(rendererHelper);const lcpSamples=findLargestContentfulPaintHistogramSamples(chromeHelper.browserHelper.mainThread.sliceGroup.slices);addSamplesToHistogram(lcpSamples,largestContentfulPaintHistogram,histograms);addSamplesToHistogram(samplesSet.firstPaintSamples,firstPaintHistogram,histograms);addSamplesToHistogram(samplesSet.firstContentfulPaintSamples,firstContentfulPaintHistogram,histograms);addSamplesToHistogram(samplesSet.firstContentfulPaintCpuTimeSamples,firstContentfulPaintCpuTimeHistogram,histograms);addSamplesToHistogram(samplesSet.onLoadSamples,onLoadHistogram,histograms);addSamplesToHistogram(samplesSet.aboveTheFoldLoadedToVisibleSamples,aboveTheFoldLoadedToVisibleHistogram,histograms);addSamplesToHistogram(samplesSet.firstViewportReadySamples,firstViewportReadyHistogram,histograms);addSamplesToHistogram(samplesSet.largestImagePaintSamples,largestImagePaintHistogram,histograms);addSamplesToHistogram(samplesSet.largestTextPaintSamples,largestTextPaintHistogram,histograms);addSamplesToHistogram(samplesSet.layoutShiftSamples,layoutShiftHistogram,histograms);addSamplesToHistogram(samplesSet.navigationStartSamples,navigationStartHistogram,histograms);}
+const samplesSet=collectMetricsFromLoadExpectations(model,chromeHelper);addSamplesToHistogram(samplesSet.firstMeaningfulPaintSamples,firstMeaningfulPaintHistogram,histograms);addSamplesToHistogram(samplesSet.firstMeaningfulPaintCpuTimeSamples,firstMeaningfulPaintCpuTimeHistogram,histograms);addSamplesToHistogram(samplesSet.interactiveSamples,timeToInteractiveHistogram,histograms);addSamplesToHistogram(samplesSet.firstCpuIdleSamples,timeToFirstCpuIdleHistogram,histograms);}
+tr.metrics.MetricRegistry.register(loadingMetric);return{loadingMetric,createBreakdownDiagnostic};});'use strict';tr.exportTo('tr.metrics',function(){const SPA_NAVIGATION_START_TO_FIRST_PAINT_DURATION_BIN_BOUNDARY=tr.v.HistogramBinBoundaries.createExponential(1,1000,50);function spaNavigationMetric(histograms,model){const histogram=new tr.v.Histogram('spaNavigationStartToFpDuration',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,SPA_NAVIGATION_START_TO_FIRST_PAINT_DURATION_BIN_BOUNDARY);histogram.description='Latency between the input event causing'+' a SPA navigation and the first paint event after it';histogram.customizeSummaryOptions({count:false,sum:false,});const modelHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);if(!modelHelper){return;}
+const rendererHelpers=modelHelper.rendererHelpers;if(!rendererHelpers){return;}
+const browserHelper=modelHelper.browserHelper;for(const rendererHelper of Object.values(rendererHelpers)){const spaNavigations=tr.metrics.findSpaNavigationsOnRenderer(rendererHelper,browserHelper);for(const spaNav of spaNavigations){let beginTs=0;if(spaNav.navStartCandidates.inputLatencyAsyncSlice){const beginData=spaNav.navStartCandidates.inputLatencyAsyncSlice.args.data;beginTs=model.convertTimestampToModelTime('traceEventClock',beginData.INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT.time);}else{beginTs=spaNav.navStartCandidates.goToIndexSlice.start;}
+const rangeOfInterest=tr.b.math.Range.fromExplicitRange(beginTs,spaNav.firstPaintEvent.start);const networkEvents=tr.e.chrome.EventFinderUtils.getNetworkEventsInRange(rendererHelper.process,rangeOfInterest);const breakdownDict=tr.metrics.sh.generateWallClockTimeBreakdownTree(rendererHelper.mainThread,networkEvents,rangeOfInterest);const breakdownDiagnostic=new tr.v.d.Breakdown();breakdownDiagnostic.colorScheme=tr.v.d.COLOR_SCHEME_CHROME_USER_FRIENDLY_CATEGORY_DRIVER;for(const label in breakdownDict){breakdownDiagnostic.set(label,parseInt(breakdownDict[label].total*1e3)/1e3);}
+histogram.addSample(rangeOfInterest.duration,{'Breakdown of [navStart, firstPaint]':breakdownDiagnostic,'Start':new tr.v.d.RelatedEventSet(spaNav.navigationStart),'End':new tr.v.d.RelatedEventSet(spaNav.firstPaintEvent),'Navigation infos':new tr.v.d.GenericSet([{url:spaNav.url,pid:rendererHelper.pid,navStart:beginTs,firstPaint:spaNav.firstPaintEvent.start}]),});}}
+histograms.addHistogram(histogram);}
+tr.metrics.MetricRegistry.register(spaNavigationMetric);return{spaNavigationMetric,};});'use strict';tr.exportTo('tr.metrics.sh',function(){const LATENCY_BOUNDS=tr.v.HistogramBinBoundaries.createLinear(0,20,100);function clockSyncLatencyMetric(values,model){const domains=Array.from(model.clockSyncManager.domainsSeen).sort();for(let i=0;i<domains.length;i++){for(let j=i+1;j<domains.length;j++){const latency=model.clockSyncManager.getTimeTransformerError(domains[i],domains[j]);const hist=new tr.v.Histogram('clock_sync_latency_'+
+domains[i].toLowerCase()+'_to_'+domains[j].toLowerCase(),tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,LATENCY_BOUNDS);hist.customizeSummaryOptions({avg:true,count:false,max:false,min:false,std:false,sum:false,});hist.description='Clock sync latency for domain '+domains[i]+' to domain '+domains[j];hist.addSample(latency);values.addHistogram(hist);}}}
+tr.metrics.MetricRegistry.register(clockSyncLatencyMetric);return{clockSyncLatencyMetric,};});'use strict';tr.exportTo('tr.metrics.sh',function(){const CPU_TIME_PERCENTAGE_BOUNDARIES=tr.v.HistogramBinBoundaries.createExponential(0.01,50,200);function cpuTimeMetric(histograms,model,opt_options){let rangeOfInterest=model.bounds;if(opt_options&&opt_options.rangeOfInterest){rangeOfInterest=opt_options.rangeOfInterest;}else{const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);if(chromeHelper){const chromeBounds=chromeHelper.chromeBounds;if(chromeBounds){rangeOfInterest=chromeBounds;}}}
+let allProcessCpuTime=0;for(const pid in model.processes){const process=model.processes[pid];if(tr.model.helpers.ChromeRendererHelper.isTracingProcess(process)){continue;}
+let processCpuTime=0;for(const tid in process.threads){const thread=process.threads[tid];processCpuTime+=thread.getCpuTimeForRange(rangeOfInterest);}
+allProcessCpuTime+=processCpuTime;}
+let normalizedAllProcessCpuTime=0;if(rangeOfInterest.duration>0){normalizedAllProcessCpuTime=allProcessCpuTime/rangeOfInterest.duration;}
+const unit=tr.b.Unit.byName.normalizedPercentage_smallerIsBetter;const cpuTimeHist=new tr.v.Histogram('cpu_time_percentage',unit,CPU_TIME_PERCENTAGE_BOUNDARIES);cpuTimeHist.description='Percent CPU utilization, normalized against a single core. Can be '+'greater than 100% if machine has multiple cores.';cpuTimeHist.customizeSummaryOptions({avg:true,count:false,max:false,min:false,std:false,sum:false});cpuTimeHist.addSample(normalizedAllProcessCpuTime);histograms.addHistogram(cpuTimeHist);}
+tr.metrics.MetricRegistry.register(cpuTimeMetric,{supportsRangeOfInterest:true});return{cpuTimeMetric,};});'use strict';tr.exportTo('tr.v',function(){class HistogramDeserializer{static deserialize(data){const deserializer=new HistogramDeserializer(data[0],data[1]);return data.slice(2).map(datum=>tr.v.Histogram.deserialize(datum,deserializer));}
+constructor(objects,diagnostics){this.objects_=objects;this.diagnostics_=[];for(const[type,diagnosticsByName]of Object.entries(diagnostics||{})){for(const[name,diagnosticsById]of Object.entries(diagnosticsByName)){for(const[id,data]of Object.entries(diagnosticsById)){const diagnostic=tr.v.d.Diagnostic.deserialize(type,data,this);this.diagnostics_[parseInt(id)]={name,diagnostic};}}}}
+getObject(id){return this.objects_[id];}
+getDiagnostic(id){return this.diagnostics_[parseInt(id)];}}
+return{HistogramDeserializer};});'use strict';tr.exportTo('tr.v',function(){class HistogramGrouping{constructor(key,callback){this.key_=key;this.callback_=callback;HistogramGrouping.BY_KEY.set(key,this);}
+get key(){return this.key_;}
+get callback(){return this.callback_;}
+get label(){return this.key;}
+static buildFromTags(tags,diagnosticName){const booleanTags=new Set();const keyValueTags=new Set();for(const tag of tags){if(tag.includes(':')){const key=tag.split(':')[0];if(booleanTags.has(key)){throw new Error(`Tag "${key}" cannot be both boolean and key-value`);}
+keyValueTags.add(key);}else{if(keyValueTags.has(tag)){throw new Error(`Tag "${tag}" cannot be both boolean and key-value`);}
+booleanTags.add(tag);}}
+const groupings=[];for(const tag of booleanTags){groupings.push(HistogramGrouping.buildBooleanTagGrouping_(tag,diagnosticName));}
+for(const tag of keyValueTags){groupings.push(HistogramGrouping.buildKeyValueTagGrouping_(tag,diagnosticName));}
+return groupings;}
+static buildBooleanTagGrouping_(tag,diagnosticName){return new HistogramGrouping(`${tag}Tag`,h=>{const tags=h.diagnostics.get(diagnosticName);if(tags===undefined||!tags.has(tag))return`~${tag}`;return tag;});}
+static buildKeyValueTagGrouping_(tag,diagnosticName){return new HistogramGrouping(`${tag}Tag`,h=>{const tags=h.diagnostics.get(diagnosticName);if(tags===undefined)return`~${tag}`;const values=new Set();for(const value of tags){const kvp=value.split(':');if(kvp.length<2||kvp[0]!==tag)continue;values.add(kvp[1]);}
+if(values.size===0)return`~${tag}`;const sortedValues=Array.from(values);sortedValues.sort();return sortedValues.join(',');},`${tag} tag`);}}
+HistogramGrouping.BY_KEY=new Map();HistogramGrouping.HISTOGRAM_NAME=new HistogramGrouping('name',h=>h.name);HistogramGrouping.DISPLAY_LABEL=new HistogramGrouping('displayLabel',hist=>{const labels=hist.diagnostics.get(tr.v.d.RESERVED_NAMES.LABELS);if(labels!==undefined&&labels.size>0){return Array.from(labels).join(',');}
+const benchmarks=hist.diagnostics.get(tr.v.d.RESERVED_NAMES.BENCHMARKS);const start=hist.diagnostics.get(tr.v.d.RESERVED_NAMES.BENCHMARK_START);if(benchmarks===undefined){if(start===undefined)return'Value';return start.toString();}
+const benchmarksStr=Array.from(benchmarks).join('\n');if(start===undefined)return benchmarksStr;return benchmarksStr+'\n'+start.toString();});class GenericSetGrouping extends HistogramGrouping{constructor(name){super(name,undefined);this.callback_=this.compute_.bind(this);}
+compute_(hist){const diag=hist.diagnostics.get(this.key);if(diag===undefined)return'';const parts=Array.from(diag);parts.sort();return parts.join(',');}}
+GenericSetGrouping.NAMES=[tr.v.d.RESERVED_NAMES.ARCHITECTURES,tr.v.d.RESERVED_NAMES.BENCHMARKS,tr.v.d.RESERVED_NAMES.BOTS,tr.v.d.RESERVED_NAMES.BUILDS,tr.v.d.RESERVED_NAMES.DEVICE_IDS,tr.v.d.RESERVED_NAMES.MASTERS,tr.v.d.RESERVED_NAMES.MEMORY_AMOUNTS,tr.v.d.RESERVED_NAMES.OS_NAMES,tr.v.d.RESERVED_NAMES.OS_VERSIONS,tr.v.d.RESERVED_NAMES.PRODUCT_VERSIONS,tr.v.d.RESERVED_NAMES.STORIES,tr.v.d.RESERVED_NAMES.STORYSET_REPEATS,tr.v.d.RESERVED_NAMES.STORY_TAGS,tr.v.d.RESERVED_NAMES.TEST_PATH,];for(const name of GenericSetGrouping.NAMES){new GenericSetGrouping(name);}
+class DateRangeGrouping extends HistogramGrouping{constructor(name){super(name,undefined);this.callback_=this.compute_.bind(this);}
+compute_(hist){const diag=hist.diagnostics.get(this.key);if(diag===undefined)return'';return diag.toString();}}
+DateRangeGrouping.NAMES=[tr.v.d.RESERVED_NAMES.BENCHMARK_START,tr.v.d.RESERVED_NAMES.TRACE_START,];for(const name of DateRangeGrouping.NAMES){new DateRangeGrouping(name);}
+return{HistogramGrouping,GenericSetGrouping,DateRangeGrouping,};});'use strict';tr.exportTo('tr.v',function(){class HistogramSet{constructor(opt_histograms){this.histograms_=new Set();this.sharedDiagnosticsByGuid_=new Map();if(opt_histograms!==undefined){for(const hist of opt_histograms){this.addHistogram(hist);}}}
+has(hist){return this.histograms_.has(hist);}
+createHistogram(name,unit,samples,opt_options){const hist=tr.v.Histogram.create(name,unit,samples,opt_options);this.addHistogram(hist);return hist;}
+addHistogram(hist,opt_diagnostics){if(this.has(hist)){throw new Error('Cannot add same Histogram twice');}
+if(opt_diagnostics!==undefined){if(!(opt_diagnostics instanceof Map)){opt_diagnostics=Object.entries(opt_diagnostics);}
+for(const[name,diagnostic]of opt_diagnostics){hist.diagnostics.set(name,diagnostic);}}
+this.histograms_.add(hist);}
+addSharedDiagnosticToAllHistograms(name,diagnostic){this.addSharedDiagnostic(diagnostic);for(const hist of this){hist.diagnostics.set(name,diagnostic);}}
+addSharedDiagnostic(diagnostic){this.sharedDiagnosticsByGuid_.set(diagnostic.guid,diagnostic);}
+get length(){return this.histograms_.size;}*[Symbol.iterator](){for(const hist of this.histograms_){yield hist;}}
+getHistogramsNamed(name){return[...this].filter(h=>h.name===name);}
+getHistogramNamed(name){const histograms=this.getHistogramsNamed(name);if(histograms.length===0)return undefined;if(histograms.length>1){throw new Error(`Unexpectedly found multiple histograms named "${name}"`);}
+return histograms[0];}
+lookupDiagnostic(guid){return this.sharedDiagnosticsByGuid_.get(guid);}
+deserialize(data){for(const hist of tr.v.HistogramDeserializer.deserialize(data)){this.addHistogram(hist);}}
+importDicts(dicts){if((dicts instanceof Array)&&(dicts.length>2)&&(dicts[0]instanceof Array)){this.deserialize(dicts);return;}
+for(const dict of dicts){this.importLegacyDict(dict);}}
+importLegacyDict(dict){if(dict.type!==undefined){if(dict.type==='TagMap')return;if(!tr.v.d.Diagnostic.findTypeInfoWithName(dict.type)){throw new Error('Unrecognized shared diagnostic type '+dict.type);}
+this.sharedDiagnosticsByGuid_.set(dict.guid,tr.v.d.Diagnostic.fromDict(dict));}else{const hist=tr.v.Histogram.fromDict(dict);this.addHistogram(hist);hist.diagnostics.resolveSharedDiagnostics(this,true);}}
+asDicts(){const dicts=[];for(const diagnostic of this.sharedDiagnosticsByGuid_.values()){dicts.push(diagnostic.asDict());}
+for(const hist of this){dicts.push(hist.asDict());}
+return dicts;}
+get sourceHistograms(){const diagnosticNames=new Set();for(const hist of this){for(const diagnostic of hist.diagnostics.values()){if(!(diagnostic instanceof tr.v.d.RelatedNameMap))continue;for(const name of diagnostic.values()){diagnosticNames.add(name);}}}
+const sourceHistograms=new HistogramSet;for(const hist of this){if(!diagnosticNames.has(hist.name)){sourceHistograms.addHistogram(hist);}}
+return sourceHistograms;}
+groupHistogramsRecursively(groupings,opt_skipGroupingCallback){function recurse(histograms,level){if(level===groupings.length){return histograms;}
+const grouping=groupings[level];const groupedHistograms=tr.b.groupIntoMap(histograms,grouping.callback);if(opt_skipGroupingCallback&&opt_skipGroupingCallback(grouping,groupedHistograms)){return recurse(histograms,level+1);}
+for(const[key,group]of groupedHistograms){groupedHistograms.set(key,recurse(group,level+1));}
+return groupedHistograms;}
+return recurse([...this],0);}
+deduplicateDiagnostics(){const namesToCandidates=new Map();const diagnosticsToHistograms=new Map();const keysToDiagnostics=new Map();for(const hist of this){for(const[name,candidate]of hist.diagnostics){if(candidate.equals===undefined){this.sharedDiagnosticsByGuid_.set(candidate.guid,candidate);continue;}
+const hashKey=candidate.hashKey;if(candidate.hashKey!==undefined){if(keysToDiagnostics.has(hashKey)){hist.diagnostics.set(name,keysToDiagnostics.get(hashKey));}else{keysToDiagnostics.set(hashKey,candidate);this.sharedDiagnosticsByGuid_.set(candidate.guid,candidate);}
+continue;}
+if(diagnosticsToHistograms.get(candidate)===undefined){diagnosticsToHistograms.set(candidate,[hist]);}else{diagnosticsToHistograms.get(candidate).push(hist);}
+if(!namesToCandidates.has(name)){namesToCandidates.set(name,new Set());}
+namesToCandidates.get(name).add(candidate);}}
+for(const[name,candidates]of namesToCandidates){const deduplicatedDiagnostics=new Set();for(const candidate of candidates){let found=false;for(const test of deduplicatedDiagnostics){if(candidate.equals(test)){const hists=diagnosticsToHistograms.get(candidate);for(const hist of hists){hist.diagnostics.set(name,test);}
+found=true;break;}}
+if(!found){deduplicatedDiagnostics.add(candidate);}
+for(const diagnostic of deduplicatedDiagnostics){this.sharedDiagnosticsByGuid_.set(diagnostic.guid,diagnostic);}}}}
+buildGroupingsFromTags(names){const tags=new Map();for(const hist of this){for(const name of names){if(!hist.diagnostics.has(name))continue;if(!tags.has(name))tags.set(name,new Set());for(const tag of hist.diagnostics.get(name)){tags.get(name).add(tag);}}}
+const groupings=[];for(const[name,values]of tags){const built=tr.v.HistogramGrouping.buildFromTags(values,name);for(const grouping of built){groupings.push(grouping);}}
+return groupings;}}
+return{HistogramSet};});'use strict';tr.exportTo('tr.e.chrome',function(){function hasTitleAndCategory(event,title,category){return event.title===title&&event.category&&tr.b.getCategoryParts(event.category).includes(category);}
+function getNavStartTimestamps(rendererHelper){const navStartTimestamps=[];for(const e of rendererHelper.mainThread.sliceGroup.childEvents()){if(hasTitleAndCategory(e,'navigationStart','blink.user_timing')){navStartTimestamps.push(e.start);}}
+return navStartTimestamps;}
+function getInteractiveTimestamps(model){const interactiveTimestampsMap=new Map();const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);for(const rendererHelper of Object.values(chromeHelper.rendererHelpers)){const timestamps=[];interactiveTimestampsMap.set(rendererHelper.pid,timestamps);}
+for(const expectation of model.userModel.expectations){if(!(expectation instanceof tr.model.um.LoadExpectation))continue;if(tr.e.chrome.CHROME_INTERNAL_URLS.includes(expectation.url)){continue;}
+if(expectation.timeToInteractive===undefined)continue;if(interactiveTimestampsMap.get(expectation.renderProcess.pid)===undefined){interactiveTimestampsMap.set(expectation.renderProcess.pid,[]);}
+interactiveTimestampsMap.get(expectation.renderProcess.pid).push(expectation.timeToInteractive);}
+return interactiveTimestampsMap;}
+function getPostInteractiveTaskWindows(interactiveTimestamps,navStartTimestamps,traceEndTimestamp){let navStartTsIndex=0;let lastTaskWindowEndTs=undefined;const taskWindows=[];for(const currTTI of interactiveTimestamps){while(navStartTsIndex<navStartTimestamps.length&&navStartTimestamps[navStartTsIndex]<currTTI){navStartTsIndex++;}
+const taskWindowEndTs=navStartTsIndex<navStartTimestamps.length?navStartTimestamps[navStartTsIndex]:traceEndTimestamp;if(taskWindowEndTs===lastTaskWindowEndTs){throw Error('Encountered two consecutive interactive timestamps '+'with no navigationStart between them. '+'PostInteractiveTaskWindow is not well defined in this case.');}
+taskWindows.push(tr.b.math.Range.fromExplicitRange(currTTI,taskWindowEndTs));lastTaskWindowEndTs=taskWindowEndTs;}
+return taskWindows;}
+function contributionToEQT(window,task){const startInWindow=Math.max(window.min,task.start);const endInWindow=Math.min(window.max,task.end);const durationInWindow=endInWindow-startInWindow;if(durationInWindow<=0)return 0;const probabilityOfTask=durationInWindow/(window.max-window.min);const minQueueingTime=task.end-endInWindow;const maxQueueingTime=task.end-startInWindow;const expectedQueueingTimeDueToTask=(maxQueueingTime+minQueueingTime)/2;return probabilityOfTask*expectedQueueingTimeDueToTask;}
+function weightedExpectedQueueingTime(window,weightedTasks){let result=0;for(const task of weightedTasks){result+=contributionToEQT(window,task)*task.weight;}
+return result;}
+function expectedQueueingTime(window,tasks){return weightedExpectedQueueingTime(window,tasks.map(function(task){return{start:task.start,end:task.end,weight:1};}));}
+class SlidingWindow{constructor(startTime,windowSize,sortedTasks){this.windowSize_=windowSize;this.sortedTasks_=sortedTasks;this.range_=tr.b.math.Range.fromExplicitRange(startTime,startTime+windowSize);this.firstTaskIndex_=sortedTasks.findIndex(task=>startTime<task.end);if(this.firstTaskIndex_===-1){this.firstTaskIndex_=sortedTasks.length;}
+this.lastTaskIndex_=-1;while(this.lastTaskIndex_+1<sortedTasks.length&&sortedTasks[this.lastTaskIndex_+1].start<startTime+windowSize){this.lastTaskIndex_++;}
+this.innerEQT_=0;for(let i=this.firstTaskIndex_+1;i<this.lastTaskIndex_;i++){this.innerEQT_+=contributionToEQT(this.range_,sortedTasks[i]);}}
+get getEQT(){let firstTaskEQT=0;if(this.firstTaskIndex_<this.sortedTasks_.length){firstTaskEQT=contributionToEQT(this.range_,this.sortedTasks_[this.firstTaskIndex_]);}
+let lastTaskEQT=0;if(this.firstTaskIndex_<this.lastTaskIndex_){lastTaskEQT=contributionToEQT(this.range_,this.sortedTasks_[this.lastTaskIndex_]);}
+return firstTaskEQT+this.innerEQT_+lastTaskEQT;}
+slide(t){this.range_=tr.b.math.Range.fromExplicitRange(t,t+this.windowSize_);if(this.firstTaskIndex_<this.sortedTasks_.length&&this.sortedTasks_[this.firstTaskIndex_].end<=t){this.firstTaskIndex_++;if(this.firstTaskIndex_<this.lastTaskIndex_){this.innerEQT_-=contributionToEQT(this.range_,this.sortedTasks_[this.firstTaskIndex_]);}}
+if(this.lastTaskIndex_+1<this.sortedTasks_.length&&this.sortedTasks_[this.lastTaskIndex_+1].start<t+this.windowSize_){if(this.firstTaskIndex_<this.lastTaskIndex_){this.innerEQT_+=contributionToEQT(this.range_,this.sortedTasks_[this.lastTaskIndex_]);}
+this.lastTaskIndex_++;}}}
+function maxExpectedQueueingTimeInSlidingWindow(startTime,endTime,windowSize,tasks){if(windowSize<=0){throw Error('The window size must be positive number');}
+if(startTime+windowSize>endTime){throw Error('The sliding window must fit in the specified time range');}
+const sortedTasks=tasks.slice().sort((a,b)=>a.start-b.start);for(let i=1;i<sortedTasks.length;i++){if(sortedTasks[i-1].end>sortedTasks[i].start){const midpoint=(sortedTasks[i-1].end+sortedTasks[i].start)/2;sortedTasks[i-1].end=midpoint;sortedTasks[i].start=midpoint;}}
+let endpoints=[];endpoints.push(startTime);endpoints.push(endTime-windowSize);for(const task of tasks){endpoints.push(task.start-windowSize);endpoints.push(task.start);endpoints.push(task.end-windowSize);endpoints.push(task.end);}
+endpoints=endpoints.filter(x=>(startTime<=x&&x+windowSize<=endTime));endpoints.sort((a,b)=>a-b);const slidingWindow=new SlidingWindow(endpoints[0],windowSize,sortedTasks);let maxEQT=0;for(const t of endpoints){slidingWindow.slide(t);maxEQT=Math.max(maxEQT,slidingWindow.getEQT);}
+return maxEQT;}
+return{getPostInteractiveTaskWindows,getNavStartTimestamps,getInteractiveTimestamps,expectedQueueingTime,maxExpectedQueueingTimeInSlidingWindow,weightedExpectedQueueingTime};});'use strict';tr.exportTo('tr.metrics.sh',function(){const WINDOW_SIZE_MS=500;const EQT_BOUNDARIES=tr.v.HistogramBinBoundaries.createExponential(0.01,WINDOW_SIZE_MS,50);function containsForcedGC_(slice){return slice.findTopmostSlicesRelativeToThisSlice(tr.metrics.v8.utils.isForcedGarbageCollectionEvent).length>0;}
+function getOrCreateHistogram_(histograms,name,description){return histograms.getHistogramNamed(name)||histograms.createHistogram(name,tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,[],{binBoundaries:EQT_BOUNDARIES,description,summaryOptions:{avg:false,count:false,max:true,min:false,std:false,sum:false,},});}
+function expectedQueueingTimeMetric(histograms,model){const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);const rendererHelpers=Object.values(chromeHelper.rendererHelpers);addExpectedQueueingTimeMetric_('renderer_eqt',event=>{return{start:event.start,duration:event.duration};},false,rendererHelpers,histograms,model);}
+function addExpectedQueueingTimeMetric_(eqtName,getEventTimes,isCpuTime,rendererHelpers,histograms,model){function getTasks(rendererHelper){const tasks=[];for(const slice of
+tr.e.chrome.EventFinderUtils.findToplevelSchedulerTasks(rendererHelper.mainThread)){const times=getEventTimes(slice);if(times.duration>0&&!containsForcedGC_(slice)){tasks.push({start:times.start,end:times.start+times.duration});}}
+return tasks;}
+const totalHistogram=getOrCreateHistogram_(histograms,`total:${WINDOW_SIZE_MS}ms_window:${eqtName}`,`The maximum EQT in a ${WINDOW_SIZE_MS}ms sliding window`+' for a given renderer');for(const rendererHelper of rendererHelpers){if(rendererHelper.isChromeTracingUI)continue;if(rendererHelper.mainThread===undefined)continue;if(rendererHelper.mainThread.bounds.duration<WINDOW_SIZE_MS)continue;const tasks=getTasks(rendererHelper);const totalBreakdown=getV8Contribution_(eqtName,getEventTimes,isCpuTime,totalHistogram,histograms,rendererHelper,model);totalHistogram.addSample(tr.e.chrome.maxExpectedQueueingTimeInSlidingWindow(rendererHelper.mainThread.bounds.min,rendererHelper.mainThread.bounds.max,WINDOW_SIZE_MS,tasks),{v8:totalBreakdown});}}
+function getV8Contribution_(eqtName,getEventTimes,isCpuTime,totalEqtHistogram,histograms,rendererHelper,model){if(!model.categories.includes('v8'))return null;const totalBreakdown=new tr.v.d.Breakdown();const eventNamesWithTaskExtractors=getV8EventNamesWithTaskExtractors_(getEventTimes);if(!isCpuTime){const taskExtractorsUsingRCS=getV8EventNamesWithTaskExtractorsUsingRCS_(getEventTimes);for(const[eventName,getTasks]of taskExtractorsUsingRCS){eventNamesWithTaskExtractors.set(eventName,getTasks);}}
+let totalNames=totalEqtHistogram.diagnostics.get('v8');if(!totalNames){totalNames=new tr.v.d.RelatedNameMap();totalEqtHistogram.diagnostics.set('v8',totalNames);}
+for(const[eventName,getTasks]of eventNamesWithTaskExtractors){const totalHistogram=getOrCreateHistogram_(histograms,`total:${WINDOW_SIZE_MS}ms_window:${eqtName}:${eventName}`,`Contribution to the expected queueing time by ${eventName}`+' for a given renderer. It is computed as the maximum EQT in'+` a ${WINDOW_SIZE_MS}ms sliding window after shrinking top-level`+` tasks to contain only ${eventName} subevents`);const tasks=getTasks(rendererHelper);const totalSample=tr.e.chrome.maxExpectedQueueingTimeInSlidingWindow(rendererHelper.mainThread.bounds.min,rendererHelper.mainThread.bounds.max,WINDOW_SIZE_MS,tasks);totalHistogram.addSample(totalSample);totalBreakdown.set(eventName,totalSample);totalNames.set(eventName,totalHistogram.name);}
+return totalBreakdown;}
+function getV8EventNamesWithTaskExtractors_(getEventTimes,cpuMetrics){function durationOfTopmostSubSlices(slice,predicate,excludePredicate){let duration=0;for(const sub of slice.findTopmostSlicesRelativeToThisSlice(predicate)){duration+=getEventTimes(sub).duration;if(excludePredicate!==null&&excludePredicate!==undefined){duration-=durationOfTopmostSubSlices(sub,excludePredicate);}}
+return duration;}
+function taskExtractor(predicate,excludePredicate){return function(rendererHelper){const slices=tr.e.chrome.EventFinderUtils.findToplevelSchedulerTasks(rendererHelper.mainThread);const result=[];for(const slice of slices){const times=getEventTimes(slice);if(times.duration>0&&!containsForcedGC_(slice)){const duration=durationOfTopmostSubSlices(slice,predicate,excludePredicate);result.push({start:times.start,end:times.start+duration});}}
+return result;};}
+return new Map([['v8',taskExtractor(tr.metrics.v8.utils.isV8Event)],['v8:execute',taskExtractor(tr.metrics.v8.utils.isV8ExecuteEvent)],['v8:gc',taskExtractor(tr.metrics.v8.utils.isGarbageCollectionEvent)]]);}
+function extractTaskRCS(getEventTimes,predicate,rendererHelper){const result=[];for(const topSlice of
+rendererHelper.mainThread.sliceGroup.topLevelSlices){const times=getEventTimes(topSlice);if(times.duration<=0||containsForcedGC_(topSlice)){continue;}
+const v8ThreadSlices=[];for(const slice of topSlice.descendentSlices){if(tr.metrics.v8.utils.isV8RCSEvent(slice)){v8ThreadSlices.push(slice);}}
+const runtimeGroupCollection=new tr.e.v8.RuntimeStatsGroupCollection();runtimeGroupCollection.addSlices(v8ThreadSlices);let duration=0;for(const runtimeGroup of runtimeGroupCollection.runtimeGroups){if(predicate(runtimeGroup.name)){duration+=runtimeGroup.time;}}
+duration=tr.b.convertUnit(duration,tr.b.UnitPrefixScale.METRIC.MICRO,tr.b.UnitPrefixScale.METRIC.MILLI);result.push({start:times.start,end:times.start+duration});}
+return result;}
+function getV8EventNamesWithTaskExtractorsUsingRCS_(getEventTimes){const extractors=new Map();extractors.set('v8:compile_rcs',rendererHelper=>extractTaskRCS(getEventTimes,tr.metrics.v8.utils.isCompileRCSCategory,rendererHelper));extractors.set('v8:compile:optimize_rcs',rendererHelper=>extractTaskRCS(getEventTimes,tr.metrics.v8.utils.isCompileOptimizeRCSCategory,rendererHelper));return extractors;}
+tr.metrics.MetricRegistry.register(expectedQueueingTimeMetric);return{expectedQueueingTimeMetric,};});'use strict';tr.exportTo('tr.b',function(){function MultiDimensionalViewNode(title,valueCount){this.title=title;const dimensions=title.length;this.children=new Array(dimensions);for(let i=0;i<dimensions;i++){this.children[i]=new Map();}
+this.values=new Array(valueCount);for(let v=0;v<valueCount;v++){this.values[v]={self:0,total:0,totalState:NOT_PROVIDED};}}
+MultiDimensionalViewNode.TotalState={NOT_PROVIDED:0,LOWER_BOUND:1,EXACT:2};const NOT_PROVIDED=MultiDimensionalViewNode.TotalState.NOT_PROVIDED;const LOWER_BOUND=MultiDimensionalViewNode.TotalState.LOWER_BOUND;const EXACT=MultiDimensionalViewNode.TotalState.EXACT;MultiDimensionalViewNode.prototype={get subRows(){return Array.from(this.children[0].values());}};function MultiDimensionalViewBuilder(dimensions,valueCount){if(typeof(dimensions)!=='number'||dimensions<0){throw new Error('Dimensions must be a non-negative number');}
+this.dimensions_=dimensions;if(typeof(valueCount)!=='number'||valueCount<0){throw new Error('Number of values must be a non-negative number');}
+this.valueCount_=valueCount;this.buildRoot_=this.createRootNode_();this.topDownTreeViewRoot_=undefined;this.topDownHeavyViewRoot_=undefined;this.bottomUpHeavyViewNode_=undefined;this.complete_=false;this.maxDimensionDepths_=new Array(dimensions);for(let d=0;d<dimensions;d++){this.maxDimensionDepths_[d]=0;}}
+MultiDimensionalViewBuilder.ValueKind={SELF:0,TOTAL:1};MultiDimensionalViewBuilder.ViewType={TOP_DOWN_TREE_VIEW:0,TOP_DOWN_HEAVY_VIEW:1,BOTTOM_UP_HEAVY_VIEW:2};MultiDimensionalViewBuilder.prototype={addPath(path,values,valueKind){if(this.buildRoot_===undefined){throw new Error('Paths cannot be added after either view has been built');}
+if(path.length!==this.dimensions_){throw new Error('Path must be '+this.dimensions_+'-dimensional');}
+if(values.length!==this.valueCount_){throw new Error('Must provide '+this.valueCount_+' values');}
+let isTotal;switch(valueKind){case MultiDimensionalViewBuilder.ValueKind.SELF:isTotal=false;break;case MultiDimensionalViewBuilder.ValueKind.TOTAL:isTotal=true;break;default:throw new Error('Invalid value kind: '+valueKind);}
+let node=this.buildRoot_;for(let d=0;d<path.length;d++){const singleDimensionPath=path[d];const singleDimensionPathLength=singleDimensionPath.length;this.maxDimensionDepths_[d]=Math.max(this.maxDimensionDepths_[d],singleDimensionPathLength);for(let i=0;i<singleDimensionPathLength;i++){node=this.getOrCreateChildNode_(node,d,singleDimensionPath[i]);}}
+for(let v=0;v<this.valueCount_;v++){const addedValue=values[v];if(addedValue===undefined)continue;const nodeValue=node.values[v];if(isTotal){nodeValue.total+=addedValue;nodeValue.totalState=EXACT;}else{nodeValue.self+=addedValue;nodeValue.totalState=Math.max(nodeValue.totalState,LOWER_BOUND);}}},get complete(){return this.complete_;},set complete(isComplete){if(this.buildRoot_===undefined){throw new Error('Can\'t set complete after any view has been built.');}
+this.complete_=isComplete;},buildView(viewType){switch(viewType){case MultiDimensionalViewBuilder.ViewType.TOP_DOWN_TREE_VIEW:return this.buildTopDownTreeView();case MultiDimensionalViewBuilder.ViewType.TOP_DOWN_HEAVY_VIEW:return this.buildTopDownHeavyView();case MultiDimensionalViewBuilder.ViewType.BOTTOM_UP_HEAVY_VIEW:return this.buildBottomUpHeavyView();default:throw new Error('Unknown multi-dimensional view type: '+viewType);}},buildTopDownTreeView(){if(this.topDownTreeViewRoot_===undefined){const treeViewRoot=this.buildRoot_;this.buildRoot_=undefined;this.setUpMissingChildRelationships_(treeViewRoot,0);this.finalizeTotalValues_(treeViewRoot,0,new WeakMap());this.topDownTreeViewRoot_=treeViewRoot;}
+return this.topDownTreeViewRoot_;},buildTopDownHeavyView(){if(this.topDownHeavyViewRoot_===undefined){this.topDownHeavyViewRoot_=this.buildGenericHeavyView_(this.addDimensionToTopDownHeavyViewNode_.bind(this));}
+return this.topDownHeavyViewRoot_;},buildBottomUpHeavyView(){if(this.bottomUpHeavyViewNode_===undefined){this.bottomUpHeavyViewNode_=this.buildGenericHeavyView_(this.addDimensionToBottomUpHeavyViewNode_.bind(this));}
+return this.bottomUpHeavyViewNode_;},createRootNode_(){return new MultiDimensionalViewNode(new Array(this.dimensions_),this.valueCount_);},getOrCreateChildNode_(parentNode,dimension,childDimensionTitle){if(dimension<0||dimension>=this.dimensions_){throw new Error('Invalid dimension');}
+const dimensionChildren=parentNode.children[dimension];let childNode=dimensionChildren.get(childDimensionTitle);if(childNode!==undefined){return childNode;}
+const childTitle=parentNode.title.slice();childTitle[dimension]=childDimensionTitle;childNode=new MultiDimensionalViewNode(childTitle,this.valueCount_);dimensionChildren.set(childDimensionTitle,childNode);return childNode;},setUpMissingChildRelationships_(node,firstDimensionToSetUp){for(let d=firstDimensionToSetUp;d<this.dimensions_;d++){const currentDimensionChildTitles=new Set(node.children[d].keys());for(let i=0;i<d;i++){for(const previousDimensionChildNode of node.children[i].values()){for(const previousDimensionGrandChildTitle of
+previousDimensionChildNode.children[d].keys()){currentDimensionChildTitles.add(previousDimensionGrandChildTitle);}}}
+for(const currentDimensionChildTitle of currentDimensionChildTitles){const currentDimensionChildNode=this.getOrCreateChildNode_(node,d,currentDimensionChildTitle);for(let i=0;i<d;i++){for(const previousDimensionChildNode of
+node.children[i].values()){const previousDimensionGrandChildNode=previousDimensionChildNode.children[d].get(currentDimensionChildTitle);if(previousDimensionGrandChildNode!==undefined){currentDimensionChildNode.children[i].set(previousDimensionChildNode.title[i],previousDimensionGrandChildNode);}}}
+this.setUpMissingChildRelationships_(currentDimensionChildNode,d);}}},finalizeTotalValues_(node,firstDimensionToFinalize,dimensionalSelfSumsMap){const dimensionalSelfSums=new Array(this.dimensions_);const minResidual=new Array(this.valueCount_);for(let v=0;v<this.valueCount_;v++)minResidual[v]=0;const nodeValues=node.values;const nodeSelfSums=new Array(this.valueCount_);for(let v=0;v<this.valueCount_;v++){nodeSelfSums[v]=nodeValues[v].self;}
+for(let d=0;d<this.dimensions_;d++){const childResidualSums=new Array(this.valueCount_);for(let v=0;v<this.valueCount_;v++){childResidualSums[v]=0;}
+for(const childNode of node.children[d].values()){if(d>=firstDimensionToFinalize){this.finalizeTotalValues_(childNode,d,dimensionalSelfSumsMap);}
+const childNodeSelfSums=dimensionalSelfSumsMap.get(childNode);const childNodeValues=childNode.values;for(let v=0;v<this.valueCount_;v++){nodeSelfSums[v]+=childNodeSelfSums[d][v];const residual=childNodeValues[v].total-
+childNodeSelfSums[this.dimensions_-1][v];childResidualSums[v]+=residual;if(this.complete){nodeValues[v].totalState=EXACT;}else if(childNodeValues[v].totalState>NOT_PROVIDED){nodeValues[v].totalState=Math.max(nodeValues[v].totalState,LOWER_BOUND);}}}
+dimensionalSelfSums[d]=nodeSelfSums.slice();for(let v=0;v<this.valueCount_;v++){minResidual[v]=Math.max(minResidual[v],childResidualSums[v]);}}
+for(let v=0;v<this.valueCount_;v++){nodeValues[v].total=Math.max(nodeValues[v].total,nodeSelfSums[v]+minResidual[v]);}
+if(dimensionalSelfSumsMap.has(node)){throw new Error('Internal error: Node finalized more than once');}
+dimensionalSelfSumsMap.set(node,dimensionalSelfSums);},buildGenericHeavyView_(treeViewNodeHandler){const treeViewRoot=this.buildTopDownTreeView();const heavyViewRoot=this.createRootNode_();heavyViewRoot.values=treeViewRoot.values;const recursionDepthTrackers=new Array(this.dimensions_);for(let d=0;d<this.dimensions_;d++){recursionDepthTrackers[d]=new RecursionDepthTracker(this.maxDimensionDepths_[d],d);}
+this.addDimensionsToGenericHeavyViewNode_(treeViewRoot,heavyViewRoot,0,recursionDepthTrackers,false,treeViewNodeHandler);this.setUpMissingChildRelationships_(heavyViewRoot,0);return heavyViewRoot;},addDimensionsToGenericHeavyViewNode_(treeViewParentNode,heavyViewParentNode,startDimension,recursionDepthTrackers,previousDimensionsRecursive,treeViewNodeHandler){for(let d=startDimension;d<this.dimensions_;d++){this.addDimensionDescendantsToGenericHeavyViewNode_(treeViewParentNode,heavyViewParentNode,d,recursionDepthTrackers,previousDimensionsRecursive,treeViewNodeHandler);}},addDimensionDescendantsToGenericHeavyViewNode_(treeViewParentNode,heavyViewParentNode,currentDimension,recursionDepthTrackers,previousDimensionsRecursive,treeViewNodeHandler){const treeViewChildren=treeViewParentNode.children[currentDimension];const recursionDepthTracker=recursionDepthTrackers[currentDimension];for(const treeViewChildNode of treeViewChildren.values()){recursionDepthTracker.push(treeViewChildNode);treeViewNodeHandler(treeViewChildNode,heavyViewParentNode,currentDimension,recursionDepthTrackers,previousDimensionsRecursive);this.addDimensionDescendantsToGenericHeavyViewNode_(treeViewChildNode,heavyViewParentNode,currentDimension,recursionDepthTrackers,previousDimensionsRecursive,treeViewNodeHandler);recursionDepthTracker.pop();}},addDimensionToTopDownHeavyViewNode_(treeViewChildNode,heavyViewParentNode,currentDimension,recursionDepthTrackers,previousDimensionsRecursive){this.addDimensionToTopDownHeavyViewNodeRecursively_(treeViewChildNode,heavyViewParentNode,currentDimension,recursionDepthTrackers,previousDimensionsRecursive,1);},addDimensionToTopDownHeavyViewNodeRecursively_(treeViewChildNode,heavyViewParentNode,currentDimension,recursionDepthTrackers,previousDimensionsRecursive,subTreeDepth){const recursionDepthTracker=recursionDepthTrackers[currentDimension];const currentDimensionRecursive=subTreeDepth<=recursionDepthTracker.recursionDepth;const currentOrPreviousDimensionsRecursive=currentDimensionRecursive||previousDimensionsRecursive;const dimensionTitle=treeViewChildNode.title[currentDimension];const heavyViewChildNode=this.getOrCreateChildNode_(heavyViewParentNode,currentDimension,dimensionTitle);this.addNodeValues_(treeViewChildNode,heavyViewChildNode,!currentOrPreviousDimensionsRecursive);this.addDimensionsToGenericHeavyViewNode_(treeViewChildNode,heavyViewChildNode,currentDimension+1,recursionDepthTrackers,currentOrPreviousDimensionsRecursive,this.addDimensionToTopDownHeavyViewNode_.bind(this));for(const treeViewGrandChildNode of
+treeViewChildNode.children[currentDimension].values()){recursionDepthTracker.push(treeViewGrandChildNode);this.addDimensionToTopDownHeavyViewNodeRecursively_(treeViewGrandChildNode,heavyViewChildNode,currentDimension,recursionDepthTrackers,previousDimensionsRecursive,subTreeDepth+1);recursionDepthTracker.pop();}},addDimensionToBottomUpHeavyViewNode_(treeViewChildNode,heavyViewParentNode,currentDimension,recursionDepthTrackers,previousDimensionsRecursive){const recursionDepthTracker=recursionDepthTrackers[currentDimension];const bottomIndex=recursionDepthTracker.bottomIndex;const topIndex=recursionDepthTracker.topIndex;const firstNonRecursiveIndex=bottomIndex+recursionDepthTracker.recursionDepth;const viewNodePath=recursionDepthTracker.viewNodePath;const trackerAncestorNode=recursionDepthTracker.trackerAncestorNode;let heavyViewDescendantNode=heavyViewParentNode;for(let i=bottomIndex;i<topIndex;i++){const treeViewAncestorNode=viewNodePath[i];const dimensionTitle=treeViewAncestorNode.title[currentDimension];heavyViewDescendantNode=this.getOrCreateChildNode_(heavyViewDescendantNode,currentDimension,dimensionTitle);const currentDimensionRecursive=i<firstNonRecursiveIndex;const currentOrPreviousDimensionsRecursive=currentDimensionRecursive||previousDimensionsRecursive;this.addNodeValues_(treeViewChildNode,heavyViewDescendantNode,!currentOrPreviousDimensionsRecursive);this.addDimensionsToGenericHeavyViewNode_(treeViewChildNode,heavyViewDescendantNode,currentDimension+1,recursionDepthTrackers,currentOrPreviousDimensionsRecursive,this.addDimensionToBottomUpHeavyViewNode_.bind(this));}},addNodeValues_(sourceNode,targetNode,addTotal){const targetNodeValues=targetNode.values;const sourceNodeValues=sourceNode.values;for(let v=0;v<this.valueCount_;v++){const targetNodeValue=targetNodeValues[v];const sourceNodeValue=sourceNodeValues[v];targetNodeValue.self+=sourceNodeValue.self;if(addTotal){targetNodeValue.total+=sourceNodeValue.total;if(this.complete){targetNodeValue.totalState=EXACT;}else if(sourceNodeValue.totalState>NOT_PROVIDED){targetNodeValue.totalState=Math.max(targetNodeValue.totalState,LOWER_BOUND);}}}}};function RecursionDepthTracker(maxDepth,dimension){this.titlePath=new Array(maxDepth);this.viewNodePath=new Array(maxDepth);this.bottomIndex=this.topIndex=maxDepth;this.dimension_=dimension;this.currentTrackerNode_=this.createNode_(0,undefined);}
+RecursionDepthTracker.prototype={push(viewNode){if(this.bottomIndex===0){throw new Error('Cannot push to a full tracker');}
+const title=viewNode.title[this.dimension_];this.bottomIndex--;this.titlePath[this.bottomIndex]=title;this.viewNodePath[this.bottomIndex]=viewNode;let childTrackerNode=this.currentTrackerNode_.children.get(title);if(childTrackerNode!==undefined){this.currentTrackerNode_=childTrackerNode;return;}
+const maxLengths=zFunction(this.titlePath,this.bottomIndex);let recursionDepth=0;for(let i=0;i<maxLengths.length;i++){recursionDepth=Math.max(recursionDepth,maxLengths[i]);}
+childTrackerNode=this.createNode_(recursionDepth,this.currentTrackerNode_);this.currentTrackerNode_.children.set(title,childTrackerNode);this.currentTrackerNode_=childTrackerNode;},pop(){if(this.bottomIndex===this.topIndex){throw new Error('Cannot pop from an empty tracker');}
+this.titlePath[this.bottomIndex]=undefined;this.viewNodePath[this.bottomIndex]=undefined;this.bottomIndex++;this.currentTrackerNode_=this.currentTrackerNode_.parent;},get recursionDepth(){return this.currentTrackerNode_.recursionDepth;},createNode_(recursionDepth,parent){return{recursionDepth,parent,children:new Map()};}};function zFunction(list,startIndex){const n=list.length-startIndex;if(n===0)return[];const z=new Array(n);z[0]=0;for(let i=1,left=0,right=0;i<n;++i){let maxLength;if(i<=right){maxLength=Math.min(right-i+1,z[i-left]);}else{maxLength=0;}
+while(i+maxLength<n&&list[startIndex+maxLength]===list[startIndex+i+maxLength]){++maxLength;}
+if(i+maxLength-1>right){left=i;right=i+maxLength-1;}
+z[i]=maxLength;}
+return z;}
+return{MultiDimensionalViewBuilder,MultiDimensionalViewNode,RecursionDepthTracker,zFunction,};});'use strict';tr.exportTo('tr.e.chrome',function(){class CpuTime{static getStageToInitiatorToSegmentBounds(segments,rangeOfInterest){const stageToInitiatorToRanges=new Map();stageToInitiatorToRanges.set('all_stages',new Map([['all_initiators',new Set()]]));const allRanges=stageToInitiatorToRanges.get('all_stages').get('all_initiators');for(const segment of segments){if(!rangeOfInterest.intersectsRangeInclusive(segment.range))continue;const intersectingRange=rangeOfInterest.findIntersection(segment.range);allRanges.add(intersectingRange);for(const expectation of segment.expectations){const stageTitle=expectation.stageTitle;if(!stageToInitiatorToRanges.has(stageTitle)){stageToInitiatorToRanges.set(stageTitle,new Map([['all_initiators',new Set()]]));}
+const initiatorToRanges=stageToInitiatorToRanges.get(stageTitle);initiatorToRanges.get('all_initiators').add(intersectingRange);const initiatorType=expectation.initiatorType;if(initiatorType){if(!initiatorToRanges.has(initiatorType)){initiatorToRanges.set(initiatorType,new Set());}
+initiatorToRanges.get(initiatorType).add(intersectingRange);}}}
+return stageToInitiatorToRanges;}
+static constructMultiDimensionalView(model,rangeOfInterest){const mdvBuilder=new tr.b.MultiDimensionalViewBuilder(3,2);const stageToInitiatorToRanges=CpuTime.getStageToInitiatorToSegmentBounds(model.userModel.segments,rangeOfInterest);const allSegmentBoundsInRange=stageToInitiatorToRanges.get('all_stages').get('all_initiators');for(const[pid,process]of Object.entries(model.processes)){const processType=tr.e.chrome.chrome_processes.canonicalizeProcessName(process.name);for(const[tid,thread]of Object.entries(process.threads)){const rangeToCpuTime=new Map();for(const range of allSegmentBoundsInRange){rangeToCpuTime.set(range,thread.getCpuTimeForRange(range));}
+for(const[stage,initiatorToRanges]of stageToInitiatorToRanges){for(const[initiator,ranges]of initiatorToRanges){const cpuTime=tr.b.math.Statistics.sum(ranges,range=>rangeToCpuTime.get(range));const duration=tr.b.math.Statistics.sum(ranges,range=>range.duration);const cpuTimePerSecond=cpuTime/duration;mdvBuilder.addPath([[processType],[thread.type],[stage,initiator]],[cpuTimePerSecond,cpuTime],tr.b.MultiDimensionalViewBuilder.ValueKind.TOTAL);}}}}
+return mdvBuilder.buildTopDownTreeView();}}
+return{CpuTime,};});'use strict';tr.exportTo('tr.metrics.sh',function(){const CPU_PERCENTAGE_UNIT=tr.b.Unit.byName.normalizedPercentage_smallerIsBetter;const CPU_TIME_UNIT=tr.b.Unit.byName.timeDurationInMs_smallerIsBetter;function clonePath_(previousPath){return previousPath.map(subPath=>subPath.map(x=>x));}
+function decodePath_(path){return{processType:path[0][0],threadType:path[1][0],railStage:path[2][0],initiatorType:path[2][1]};}
+function stringifyPathName_(path){const decodedPath=decodePath_(path);return[decodedPath.processType,decodedPath.threadType,decodedPath.railStage,decodedPath.initiatorType].join(':');}
+class CpuTimeTreeDataReporter{constructor(){this.visitedSet_=new Set();}
+reportValuesFromNode_(node,path){const decodedPath=decodePath_(path);const processType=decodedPath.processType||'all_processes';const threadType=decodedPath.threadType||'all_threads';if(!decodedPath.railStage||!decodedPath.initiatorType)return;const{railStage,initiatorType}=decodedPath;const serializedPathName=[processType,threadType,railStage,initiatorType].join(':');const cpuPercentageValue=node.values[0].total;const cpuTimeValue=node.values[1].total;this.histogramSet_.createHistogram(`cpuPercentage:${serializedPathName}`,CPU_PERCENTAGE_UNIT,cpuPercentageValue);this.histogramSet_.createHistogram(`cpuTime:${serializedPathName}`,CPU_TIME_UNIT,cpuTimeValue);}
+reportDataFromTree_(root,rootPath){const rootPathString=stringifyPathName_(rootPath);if(this.visitedSet_.has(rootPathString))return;this.visitedSet_.add(rootPathString);this.reportValuesFromNode_(root,rootPath);for(let dimension=0;dimension<root.children.length;dimension++){const children=root.children[dimension];for(const[name,node]of children){const childPath=clonePath_(rootPath);childPath[dimension].push(name);this.reportDataFromTree_(node,childPath);}}}
+addTreeValuesToHistogramSet(rootNode,histogramSet){const rootPath=[[],[],[]];this.rootNode_=rootNode;this.histogramSet_=histogramSet;this.reportDataFromTree_(this.rootNode_,rootPath);}
+static reportToHistogramSet(rootNode,histogramSet){const reporter=new CpuTimeTreeDataReporter();reporter.addTreeValuesToHistogramSet(rootNode,histogramSet);}}
+return{CpuTimeTreeDataReporter,};});'use strict';tr.exportTo('tr.metrics.sh',function(){function newCpuTimeMetric(histograms,model,opt_options){const rangeOfInterest=opt_options&&opt_options.rangeOfInterest?opt_options.rangeOfInterest:model.bounds;const rootNode=tr.e.chrome.CpuTime.constructMultiDimensionalView(model,rangeOfInterest);tr.metrics.sh.CpuTimeTreeDataReporter.reportToHistogramSet(rootNode,histograms);}
+tr.metrics.MetricRegistry.register(newCpuTimeMetric,{supportsRangeOfInterest:true});return{newCpuTimeMetric,};});'use strict';tr.exportTo('tr.metrics.sh',function(){const includeHistogramNames=['cpuTime:all_processes:all_threads:all_stages:all_initiators','cpuPercentage:all_processes:all_threads:all_stages:all_initiators','cpuTime:browser_process:all_threads:all_stages:all_initiators','cpuPercentage:browser_process:all_threads:all_stages:all_initiators','cpuTime:renderer_processes:all_threads:all_stages:all_initiators','cpuPercentage:renderer_processes:all_threads:all_stages:all_initiators','cpuTime:gpu_process:all_threads:all_stages:all_initiators','cpuPercentage:gpu_process:all_threads:all_stages:all_initiators','cpuTime:renderer_processes:CrRendererMain:all_stages:all_initiators','cpuPercentage:renderer_processes:CrRendererMain:all_stages:all_initiators','cpuTime:browser_process:CrBrowserMain:all_stages:all_initiators','cpuPercentage:browser_process:CrBrowserMain:all_stages:all_initiators','cpuTime:all_processes:all_threads:Load:Successful','cpuPercentage:all_processes:all_threads:Load:Successful',];function limitedCpuTimeMetric(histograms,model,opt_options){const allCpuHistograms=new tr.v.HistogramSet();tr.metrics.sh.newCpuTimeMetric(allCpuHistograms,model,opt_options);for(const histogramName of includeHistogramNames){const histogram=allCpuHistograms.getHistogramNamed(histogramName);if(histogram)histograms.addHistogram(histogram);}}
+tr.metrics.MetricRegistry.register(limitedCpuTimeMetric,{supportsRangeOfInterest:true});return{limitedCpuTimeMetric,};});'use strict';tr.exportTo('tr.metrics.sh',function(){const LONG_TASK_MS=50;const LONGEST_TASK_MS=1000;function iterateLongTopLevelTasksOnThreadInRange(thread,opt_range,cb,opt_this){thread.sliceGroup.topLevelSlices.forEach(function(slice){if(opt_range&&!opt_range.intersectsExplicitRangeInclusive(slice.start,slice.end)){return;}
+if(slice.duration<LONG_TASK_MS)return;cb.call(opt_this,slice);});}
+function iterateRendererMainThreads(model,cb,opt_this){const modelHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);if(modelHelper!==undefined){Object.values(modelHelper.rendererHelpers).forEach(function(rendererHelper){if(!rendererHelper.mainThread)return;cb.call(opt_this,rendererHelper.mainThread);});}}
+const BIN_BOUNDARIES=tr.v.HistogramBinBoundaries.createLinear(LONG_TASK_MS,LONGEST_TASK_MS,40);function longTasksMetric(histograms,model,opt_options){const rangeOfInterest=opt_options?opt_options.rangeOfInterest:undefined;const longTaskHist=histograms.createHistogram('longTasks',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,[],{binBoundaries:BIN_BOUNDARIES,description:'durations of long tasks',});const relatedNames=new tr.v.d.RelatedNameMap();longTaskHist.diagnostics.set('categories',relatedNames);iterateRendererMainThreads(model,function(thread){iterateLongTopLevelTasksOnThreadInRange(thread,rangeOfInterest,function(task){const breakdown=new tr.v.d.Breakdown();breakdown.colorScheme=tr.v.d.COLOR_SCHEME_CHROME_USER_FRIENDLY_CATEGORY_DRIVER;for(const slice of task.descendentSlices){const sample=slice.cpuSelfTime;if(sample===undefined)continue;const category=model.getUserFriendlyCategoryFromEvent(slice);const histName='longTasks:'+category;let hist=histograms.getHistogramNamed(histName);if(hist===undefined){hist=histograms.createHistogram(histName,tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,[],{binBoundaries:BIN_BOUNDARIES,});relatedNames.set(category,hist.name);}
+hist.addSample(sample,{events:new tr.v.d.RelatedEventSet([slice]),});breakdown.set(category,sample+breakdown.get(category));}
+longTaskHist.addSample(task.duration,{events:new tr.v.d.RelatedEventSet([task]),categories:breakdown,});});});}
+tr.metrics.MetricRegistry.register(longTasksMetric,{supportsRangeOfInterest:true,requiredCategories:['toplevel'],});return{longTasksMetric,iterateLongTopLevelTasksOnThreadInRange,iterateRendererMainThreads,LONG_TASK_MS,LONGEST_TASK_MS,};});'use strict';tr.exportTo('tr.metrics.sh',function(){const BACKGROUND=tr.model.ContainerMemoryDump.LevelOfDetail.BACKGROUND;const LIGHT=tr.model.ContainerMemoryDump.LevelOfDetail.LIGHT;const DETAILED=tr.model.ContainerMemoryDump.LevelOfDetail.DETAILED;const sizeInBytes_smallerIsBetter=tr.b.Unit.byName.sizeInBytes_smallerIsBetter;const count_smallerIsBetter=tr.b.Unit.byName.count_smallerIsBetter;const DISPLAYED_SIZE_NUMERIC_NAME=tr.model.MemoryAllocatorDump.DISPLAYED_SIZE_NUMERIC_NAME;const LEVEL_OF_DETAIL_NAMES=new Map();LEVEL_OF_DETAIL_NAMES.set(BACKGROUND,'background');LEVEL_OF_DETAIL_NAMES.set(LIGHT,'light');LEVEL_OF_DETAIL_NAMES.set(DETAILED,'detailed');const HEAP_PROFILER_DETAIL_NAME='heap_profiler';const BOUNDARIES_FOR_UNIT_MAP=new WeakMap();BOUNDARIES_FOR_UNIT_MAP.set(count_smallerIsBetter,tr.v.HistogramBinBoundaries.createLinear(0,20,20));BOUNDARIES_FOR_UNIT_MAP.set(sizeInBytes_smallerIsBetter,new tr.v.HistogramBinBoundaries(0).addBinBoundary(1024).addExponentialBins(16*1024*1024*1024,4*24));const CHROME_PROCESS_NAMES=tr.e.chrome.chrome_processes.CHROME_PROCESS_NAMES;function memoryMetric(values,model,opt_options){const rangeOfInterest=opt_options?opt_options.rangeOfInterest:undefined;const browserNameToGlobalDumps=tr.metrics.sh.splitGlobalDumpsByBrowserName(model,rangeOfInterest);addGeneralMemoryDumpValues(browserNameToGlobalDumps,values);addDetailedMemoryDumpValues(browserNameToGlobalDumps,values);addMemoryDumpCountValues(browserNameToGlobalDumps,values);}
+const USER_FRIENDLY_BROWSER_NAMES={'chrome':'Chrome','webview':'WebView','unknown_browser':'an unknown browser'};function convertBrowserNameToUserFriendlyName(browserName){for(const baseName in USER_FRIENDLY_BROWSER_NAMES){if(!browserName.startsWith(baseName))continue;const userFriendlyBaseName=USER_FRIENDLY_BROWSER_NAMES[baseName];const suffix=browserName.substring(baseName.length);if(suffix.length===0){return userFriendlyBaseName;}else if(/^\d+$/.test(suffix)){return userFriendlyBaseName+'('+suffix+')';}}
+return'\''+browserName+'\' browser';}
+function convertProcessNameToUserFriendlyName(processName,opt_requirePlural){switch(processName){case CHROME_PROCESS_NAMES.BROWSER:return opt_requirePlural?'browser processes':'the browser process';case CHROME_PROCESS_NAMES.RENDERER:return'renderer processes';case CHROME_PROCESS_NAMES.GPU:return opt_requirePlural?'GPU processes':'the GPU process';case CHROME_PROCESS_NAMES.PPAPI:return opt_requirePlural?'PPAPI processes':'the PPAPI process';case CHROME_PROCESS_NAMES.ALL:return'all processes';case CHROME_PROCESS_NAMES.UNKNOWN:return'unknown processes';default:return'\''+processName+'\' processes';}}
+function addGeneralMemoryDumpValues(browserNameToGlobalDumps,values){addMemoryDumpValues(browserNameToGlobalDumps,gmd=>true,function(processDump,addProcessScalar){addProcessScalar({source:'process_count',property:PROCESS_COUNT,value:1});if(processDump.totals!==undefined){addProcessScalar({source:'reported_by_os',property:RESIDENT_SIZE,component:['system_memory'],value:processDump.totals.residentBytes});addProcessScalar({source:'reported_by_os',property:PEAK_RESIDENT_SIZE,component:['system_memory'],value:processDump.totals.peakResidentBytes});addProcessScalar({source:'reported_by_os',property:PRIVATE_FOOTPRINT_SIZE,component:['system_memory'],value:processDump.totals.privateFootprintBytes,});}
+if(processDump.memoryAllocatorDumps===undefined)return;processDump.memoryAllocatorDumps.forEach(function(rootAllocatorDump){CHROME_VALUE_PROPERTIES.forEach(function(property){addProcessScalar({source:'reported_by_chrome',component:[rootAllocatorDump.name],property,value:rootAllocatorDump.numerics[property.name]});});if(rootAllocatorDump.numerics.allocated_objects_size===undefined){const allocatedObjectsDump=rootAllocatorDump.getDescendantDumpByFullName('allocated_objects');if(allocatedObjectsDump!==undefined){addProcessScalar({source:'reported_by_chrome',component:[rootAllocatorDump.name],property:ALLOCATED_OBJECTS_SIZE,value:allocatedObjectsDump.numerics.size});}}});addTopHeapDumpCategoryValue(processDump,addProcessScalar);addV8MemoryDumpValues(processDump,addProcessScalar);},function(componentTree){const tracingNode=componentTree.children[1].get('tracing');if(tracingNode===undefined)return;for(let i=0;i<componentTree.values.length;i++){componentTree.values[i].total-=tracingNode.values[i].total;}},values);}
+function addTopHeapDumpCategoryValue(processDump,addProcessScalar){if(!processDump.heapDumps){return;}
+for(const allocatorName in processDump.heapDumps){const heapDump=processDump.heapDumps[allocatorName];if(heapDump.entries===undefined||heapDump.entries.length===0){return;}
+const typeToSize={};for(let i=0;i<heapDump.entries.length;i+=1){const entry=heapDump.entries[i];if(!entry.objectTypeName||entry.leafStackFrame){continue;}
+if(!typeToSize[entry.objectTypeName]){typeToSize[entry.objectTypeName]=0;}
+typeToSize[entry.objectTypeName]+=entry.size;}
+let largestValue=0;let largestType='';for(const key in typeToSize){if(largestValue<typeToSize[key]){largestValue=typeToSize[key];largestType=key;}}
+addProcessScalar({source:'reported_by_chrome',component:[allocatorName,largestType],property:HEAP_CATEGORY_SIZE,value:largestValue});}}
+function addV8MemoryDumpValues(processDump,addProcessScalar){const v8Dump=processDump.getMemoryAllocatorDumpByFullName('v8');if(v8Dump===undefined)return;const sharedDump=v8Dump.getDescendantDumpByFullName('shared');if(sharedDump!==undefined){addV8ComponentValues(sharedDump,['v8','shared'],addProcessScalar);sharedDump.children.forEach(function(subDump){addV8ComponentValues(subDump,['v8','shared',subDump.name],addProcessScalar);});}
+v8Dump.children.forEach(function(isolateDump){const mallocDump=isolateDump.getDescendantDumpByFullName('malloc');if(mallocDump!==undefined){addV8ComponentValues(mallocDump,['v8','allocated_by_malloc'],addProcessScalar);}
+let heapDump=isolateDump.getDescendantDumpByFullName('heap');if(heapDump===undefined){heapDump=isolateDump.getDescendantDumpByFullName('heap_spaces');}
+if(heapDump!==undefined){addV8ComponentValues(heapDump,['v8','heap'],addProcessScalar);heapDump.children.forEach(function(spaceDump){if(spaceDump.name==='other_spaces')return;addV8ComponentValues(spaceDump,['v8','heap',spaceDump.name],addProcessScalar);});}});addProcessScalar({source:'reported_by_chrome',component:['v8'],property:CODE_AND_METADATA_SIZE,value:v8Dump.numerics.code_and_metadata_size});addProcessScalar({source:'reported_by_chrome',component:['v8'],property:CODE_AND_METADATA_SIZE,value:v8Dump.numerics.bytecode_and_metadata_size});}
+function addV8ComponentValues(componentDump,componentPath,addProcessScalar){CHROME_VALUE_PROPERTIES.forEach(function(property){addProcessScalar({source:'reported_by_chrome',component:componentPath,property,value:componentDump.numerics[property.name]});});}
+const PROCESS_COUNT={unit:count_smallerIsBetter,buildDescriptionPrefix(componentPath,processName){if(componentPath.length>0){throw new Error('Unexpected process count non-empty component path: '+
+componentPath.join(':'));}
+return'total number of '+convertProcessNameToUserFriendlyName(processName,true);}};const EFFECTIVE_SIZE={name:'effective_size',unit:sizeInBytes_smallerIsBetter,buildDescriptionPrefix(componentPath,processName){return buildChromeValueDescriptionPrefix(componentPath,processName,{userFriendlyPropertyName:'effective size',componentPreposition:'of'});}};const ALLOCATED_OBJECTS_SIZE={name:'allocated_objects_size',unit:sizeInBytes_smallerIsBetter,buildDescriptionPrefix(componentPath,processName){return buildChromeValueDescriptionPrefix(componentPath,processName,{userFriendlyPropertyName:'size of all objects allocated',totalUserFriendlyPropertyName:'size of all allocated objects',componentPreposition:'by'});}};const SHIM_ALLOCATED_OBJECTS_SIZE={name:'shim_allocated_objects_size',unit:sizeInBytes_smallerIsBetter,buildDescriptionPrefix(componentPath,processName){return buildChromeValueDescriptionPrefix(componentPath,processName,{userFriendlyPropertyName:'size of all objects allocated through shim',totalUserFriendlyPropertyName:'size of all allocated objects through shim',componentPreposition:'by'});}};const LOCKED_SIZE={name:'locked_size',unit:sizeInBytes_smallerIsBetter,buildDescriptionPrefix(componentPath,processName){return buildChromeValueDescriptionPrefix(componentPath,processName,{userFriendlyPropertyName:'locked (pinned) size',componentPreposition:'of'});}};const PEAK_SIZE={name:'peak_size',unit:sizeInBytes_smallerIsBetter,buildDescriptionPrefix(componentPath,processName){return buildChromeValueDescriptionPrefix(componentPath,processName,{userFriendlyPropertyName:'peak size',componentPreposition:'of'});}};const HEAP_CATEGORY_SIZE={name:'heap_category_size',unit:sizeInBytes_smallerIsBetter,buildDescriptionPrefix(componentPath,processName){return buildChromeValueDescriptionPrefix(componentPath,processName,{userFriendlyPropertyName:'heap profiler category size',componentPreposition:'for'});}};const CODE_AND_METADATA_SIZE={name:'code_and_metadata_size',unit:sizeInBytes_smallerIsBetter,buildDescriptionPrefix(componentPath,processName){return buildChromeValueDescriptionPrefix(componentPath,processName,{userFriendlyPropertyNamePrefix:'size of',userFriendlyPropertyName:'code and metadata'});}};const CHROME_VALUE_PROPERTIES=[EFFECTIVE_SIZE,ALLOCATED_OBJECTS_SIZE,SHIM_ALLOCATED_OBJECTS_SIZE,LOCKED_SIZE,PEAK_SIZE];function buildChromeValueDescriptionPrefix(componentPath,processName,formatSpec){const nameParts=[];if(componentPath.length===0){nameParts.push('total');if(formatSpec.totalUserFriendlyPropertyName){nameParts.push(formatSpec.totalUserFriendlyPropertyName);}else{if(formatSpec.userFriendlyPropertyNamePrefix){nameParts.push(formatSpec.userFriendlyPropertyNamePrefix);}
+nameParts.push(formatSpec.userFriendlyPropertyName);}
+nameParts.push('reported by Chrome for');}else{if(formatSpec.componentPreposition===undefined){if(formatSpec.userFriendlyPropertyNamePrefix){nameParts.push(formatSpec.userFriendlyPropertyNamePrefix);}
+nameParts.push(componentPath.join(':'));nameParts.push(formatSpec.userFriendlyPropertyName);}else{if(formatSpec.userFriendlyPropertyNamePrefix){nameParts.push(formatSpec.userFriendlyPropertyNamePrefix);}
+nameParts.push(formatSpec.userFriendlyPropertyName);nameParts.push(formatSpec.componentPreposition);if(componentPath[componentPath.length-1]==='allocated_by_malloc'){nameParts.push('objects allocated by malloc for');nameParts.push(componentPath.slice(0,componentPath.length-1).join(':'));}else{nameParts.push(componentPath.join(':'));}}
+nameParts.push('in');}
+nameParts.push(convertProcessNameToUserFriendlyName(processName));return nameParts.join(' ');}
+const RESIDENT_SIZE={name:'resident_size',unit:sizeInBytes_smallerIsBetter,buildDescriptionPrefix(componentPath,processName){return buildOsValueDescriptionPrefix(componentPath,processName,'resident set size (RSS)');}};const PEAK_RESIDENT_SIZE={name:'peak_resident_size',unit:sizeInBytes_smallerIsBetter,buildDescriptionPrefix(componentPath,processName){return buildOsValueDescriptionPrefix(componentPath,processName,'peak resident set size');}};const PROPORTIONAL_RESIDENT_SIZE={name:'proportional_resident_size',unit:sizeInBytes_smallerIsBetter,buildDescriptionPrefix(componentPath,processName){return buildOsValueDescriptionPrefix(componentPath,processName,'proportional resident size (PSS)');}};const PRIVATE_DIRTY_SIZE={name:'private_dirty_size',unit:sizeInBytes_smallerIsBetter,buildDescriptionPrefix(componentPath,processName){return buildOsValueDescriptionPrefix(componentPath,processName,'private dirty size');}};const PRIVATE_FOOTPRINT_SIZE={name:'private_footprint_size',unit:sizeInBytes_smallerIsBetter,buildDescriptionPrefix(componentPath,processName){return buildOsValueDescriptionPrefix(componentPath,processName,'private footprint size');}};const JAVA_BASE_CLEAN_RESIDENT={name:'java_base_clean_resident',unit:sizeInBytes_smallerIsBetter,buildDescriptionPrefix(componentPath,processName){return buildOsValueDescriptionPrefix(componentPath,processName,'java base odex and vdex total clean resident size');}};const JAVA_BASE_PSS={name:'java_base_pss',unit:sizeInBytes_smallerIsBetter,buildDescriptionPrefix(componentPath,processName){return buildOsValueDescriptionPrefix(componentPath,processName,'java base odex and vdex proportional resident size');}};const NATIVE_LIBRARY_PRIVATE_CLEAN_RESIDENT={name:'native_library_private_clean_resident',unit:sizeInBytes_smallerIsBetter,buildDescriptionPrefix(componentPath,processName){return buildOsValueDescriptionPrefix(componentPath,processName,'native library private clean resident size');}};const NATIVE_LIBRARY_SHARED_CLEAN_RESIDENT={name:'native_library_shared_clean_resident',unit:sizeInBytes_smallerIsBetter,buildDescriptionPrefix(componentPath,processName){return buildOsValueDescriptionPrefix(componentPath,processName,'native library shared clean resident size');}};const NATIVE_LIBRARY_PROPORTIONAL_RESIDENT={name:'native_library_proportional_resident',unit:sizeInBytes_smallerIsBetter,buildDescriptionPrefix(componentPath,processName){return buildOsValueDescriptionPrefix(componentPath,processName,'native library proportional resident size');}};function buildOsValueDescriptionPrefix(componentPath,processName,userFriendlyPropertyName){if(componentPath.length>2){throw new Error('OS value component path for \''+
+userFriendlyPropertyName+'\' too long: '+componentPath.join(':'));}
+const nameParts=[];if(componentPath.length<2){nameParts.push('total');}
+nameParts.push(userFriendlyPropertyName);if(componentPath.length>0){switch(componentPath[0]){case'system_memory':if(componentPath.length>1){const userFriendlyComponentName=SYSTEM_VALUE_COMPONENTS[componentPath[1]].userFriendlyName;if(userFriendlyComponentName===undefined){throw new Error('System value sub-component for \''+
+userFriendlyPropertyName+'\' unknown: '+
+componentPath.join(':'));}
+nameParts.push('of',userFriendlyComponentName,'in');}else{nameParts.push('of system memory (RAM) used by');}
+break;case'gpu_memory':if(componentPath.length>1){nameParts.push('of the',componentPath[1]);nameParts.push('Android memtrack component in');}else{nameParts.push('of GPU memory (Android memtrack) used by');}
+break;default:throw new Error('OS value component for \''+
+userFriendlyPropertyName+'\' unknown: '+
+componentPath.join(':'));}}else{nameParts.push('reported by the OS for');}
+nameParts.push(convertProcessNameToUserFriendlyName(processName));return nameParts.join(' ');}
+function addDetailedMemoryDumpValues(browserNameToGlobalDumps,values){addMemoryDumpValues(browserNameToGlobalDumps,g=>g.levelOfDetail===DETAILED,function(processDump,addProcessScalar){for(const[componentName,componentSpec]of
+Object.entries(SYSTEM_VALUE_COMPONENTS)){const node=getDescendantVmRegionClassificationNode(processDump.vmRegions,componentSpec.classificationPath);const componentPath=['system_memory'];if(componentName)componentPath.push(componentName);addProcessScalar({source:'reported_by_os',component:componentPath,property:PROPORTIONAL_RESIDENT_SIZE,value:node===undefined?0:(node.byteStats.proportionalResident||0)});addProcessScalar({source:'reported_by_os',component:componentPath,property:PRIVATE_DIRTY_SIZE,value:node===undefined?0:(node.byteStats.privateDirtyResident||0)});if(node){if(node.byteStats.javaBasePss){addProcessScalar({source:'reported_by_os',component:componentPath,property:JAVA_BASE_PSS,value:node.byteStats.javaBasePss});}
+if(node.byteStats.javaBaseCleanResident){addProcessScalar({source:'reported_by_os',component:componentPath,property:JAVA_BASE_CLEAN_RESIDENT,value:node.byteStats.javaBaseCleanResident});}}
+if(node){if(node.byteStats.nativeLibraryPrivateCleanResident){addProcessScalar({source:'reported_by_os',component:componentPath,property:NATIVE_LIBRARY_PRIVATE_CLEAN_RESIDENT,value:node.byteStats.nativeLibraryPrivateCleanResident});}
+if(node.byteStats.nativeLibrarySharedCleanResident){addProcessScalar({source:'reported_by_os',component:componentPath,property:NATIVE_LIBRARY_SHARED_CLEAN_RESIDENT,value:node.byteStats.nativeLibrarySharedCleanResident});}
+if(node.byteStats.nativeLibraryProportionalResident){addProcessScalar({source:'reported_by_os',component:componentPath,property:NATIVE_LIBRARY_PROPORTIONAL_RESIDENT,value:node.byteStats.nativeLibraryProportionalResident});}}}
+const memtrackDump=processDump.getMemoryAllocatorDumpByFullName('gpu/android_memtrack');if(memtrackDump!==undefined){memtrackDump.children.forEach(function(memtrackChildDump){addProcessScalar({source:'reported_by_os',component:['gpu_memory',memtrackChildDump.name],property:PROPORTIONAL_RESIDENT_SIZE,value:memtrackChildDump.numerics.memtrack_pss});});}},function(componentTree){},values);}
+const SYSTEM_VALUE_COMPONENTS={'':{classificationPath:[],},'java_heap':{classificationPath:['Android','Java runtime','Spaces'],userFriendlyName:'the Java heap'},'ashmem':{classificationPath:['Android','Ashmem'],userFriendlyName:'ashmem'},'native_heap':{classificationPath:['Native heap'],userFriendlyName:'the native heap'},'stack':{classificationPath:['Stack'],userFriendlyName:'the thread stacks'}};function getDescendantVmRegionClassificationNode(node,path){for(let i=0;i<path.length;i++){if(node===undefined)break;node=node.children.find(c=>c.title===path[i]);}
+return node;}
+function addMemoryDumpCountValues(browserNameToGlobalDumps,values){browserNameToGlobalDumps.forEach(function(globalDumps,browserName){let totalDumpCount=0;const levelOfDetailNameToDumpCount={};LEVEL_OF_DETAIL_NAMES.forEach(function(levelOfDetailName){levelOfDetailNameToDumpCount[levelOfDetailName]=0;});levelOfDetailNameToDumpCount[HEAP_PROFILER_DETAIL_NAME]=0;globalDumps.forEach(function(globalDump){totalDumpCount++;const levelOfDetailName=LEVEL_OF_DETAIL_NAMES.get(globalDump.levelOfDetail);if(levelOfDetailName===undefined){return;}
+levelOfDetailNameToDumpCount[levelOfDetailName]++;if(globalDump.levelOfDetail===DETAILED){if(detectHeapProfilerInMemoryDump(globalDump)){levelOfDetailNameToDumpCount[HEAP_PROFILER_DETAIL_NAME]++;}}});reportMemoryDumpCountAsValue(browserName,undefined,totalDumpCount,values);for(const[levelOfDetailName,levelOfDetailDumpCount]of
+Object.entries(levelOfDetailNameToDumpCount)){reportMemoryDumpCountAsValue(browserName,levelOfDetailName,levelOfDetailDumpCount,values);}});}
+function detectHeapProfilerInMemoryDump(globalDump){for(const processDump of Object.values(globalDump.processMemoryDumps)){if(processDump.heapDumps&&processDump.heapDumps.malloc){const mallocDump=processDump.heapDumps.malloc;if(mallocDump.entries&&mallocDump.entries.length>0){return true;}}}
+return false;}
+function reportMemoryDumpCountAsValue(browserName,levelOfDetailName,levelOfDetailDumpCount,values){const nameParts=['memory',browserName,'all_processes','dump_count'];if(levelOfDetailName!==undefined){nameParts.push(levelOfDetailName);}
+const name=nameParts.join(':');const histogram=new tr.v.Histogram(name,count_smallerIsBetter,BOUNDARIES_FOR_UNIT_MAP.get(count_smallerIsBetter));histogram.addSample(levelOfDetailDumpCount);const userFriendlyLevelOfDetail=(levelOfDetailName||'all').replace('_',' ');histogram.description=['total number of',userFriendlyLevelOfDetail,'memory dumps added by',convertBrowserNameToUserFriendlyName(browserName),'to the trace'].join(' ');values.addHistogram(histogram);}
+function addMemoryDumpValues(browserNameToGlobalDumps,customGlobalDumpFilter,customProcessDumpValueExtractor,customComponentTreeModifier,values){browserNameToGlobalDumps.forEach(function(globalDumps,browserName){const filteredGlobalDumps=globalDumps.filter(customGlobalDumpFilter);const sourceToPropertyToBuilder=extractDataFromGlobalDumps(filteredGlobalDumps,customProcessDumpValueExtractor);reportDataAsValues(sourceToPropertyToBuilder,browserName,customComponentTreeModifier,values);});}
+function extractDataFromGlobalDumps(globalDumps,customProcessDumpValueExtractor){const sourceToPropertyToBuilder=new Map();const dumpCount=globalDumps.length;globalDumps.forEach(function(globalDump,dumpIndex){for(const processDump of Object.values(globalDump.processMemoryDumps)){extractDataFromProcessDump(processDump,sourceToPropertyToBuilder,dumpIndex,dumpCount,customProcessDumpValueExtractor);}});return sourceToPropertyToBuilder;}
+function extractDataFromProcessDump(processDump,sourceToPropertyToBuilder,dumpIndex,dumpCount,customProcessDumpValueExtractor){const rawProcessName=processDump.process.name;const processNamePath=[tr.e.chrome.chrome_processes.canonicalizeProcessName(rawProcessName)];customProcessDumpValueExtractor(processDump,function addProcessScalar(spec){if(spec.value===undefined)return;const component=spec.component||[];function createDetailsForErrorMessage(){return['source=',spec.source,', property=',spec.property.name||'(undefined)',', component=',component.length===0?'(empty)':component.join(':'),' in ',processDump.process.userFriendlyName].join('');}
+let value;if(spec.value instanceof tr.b.Scalar){value=spec.value.value;if(spec.value.unit!==spec.property.unit){throw new Error('Scalar unit for '+
+createDetailsForErrorMessage()+' ('+
+spec.value.unit.unitName+') doesn\'t match the unit of the property ('+
+spec.property.unit.unitName+')');}}else{value=spec.value;}
+let propertyToBuilder=sourceToPropertyToBuilder.get(spec.source);if(propertyToBuilder===undefined){propertyToBuilder=new Map();sourceToPropertyToBuilder.set(spec.source,propertyToBuilder);}
+let builder=propertyToBuilder.get(spec.property);if(builder===undefined){builder=new tr.b.MultiDimensionalViewBuilder(2,dumpCount),propertyToBuilder.set(spec.property,builder);}
+const values=new Array(dumpCount);values[dumpIndex]=value;builder.addPath([processNamePath,component],values,tr.b.MultiDimensionalViewBuilder.ValueKind.TOTAL);});}
+function reportDataAsValues(sourceToPropertyToBuilder,browserName,customComponentTreeModifier,values){sourceToPropertyToBuilder.forEach(function(propertyToBuilder,sourceName){propertyToBuilder.forEach(function(builders,property){const tree=builders.buildTopDownTreeView();reportComponentDataAsValues(browserName,sourceName,property,[],[],tree,values,customComponentTreeModifier);});});}
+function reportComponentDataAsValues(browserName,sourceName,property,processPath,componentPath,tree,values,customComponentTreeModifier,opt_cachedHistograms){const cachedHistograms=opt_cachedHistograms||new Map();function recurse(processPath,componentPath,node){return reportComponentDataAsValues(browserName,sourceName,property,processPath,componentPath,node,values,customComponentTreeModifier,cachedHistograms);}
+function buildHistogram(processPath,componentPath,node){return buildNamedMemoryNumericFromNode(browserName,sourceName,property,processPath.length===0?'all_processes':processPath[0],componentPath,node);}
+customComponentTreeModifier(tree);const histogram=buildHistogram(processPath,componentPath,tree);if(cachedHistograms.has(histogram.name)){return cachedHistograms.get(histogram.name);}
+cachedHistograms.set(histogram.name,histogram);const processNames=new tr.v.d.RelatedNameMap();for(const[childProcessName,childProcessNode]of tree.children[0]){processPath.push(childProcessName);const childProcessHistogram=recurse(processPath,componentPath,childProcessNode);processNames.set(childProcessName,childProcessHistogram.name);processPath.pop();}
+const componentNames=new tr.v.d.RelatedNameMap();for(const[childComponentName,childComponentNode]of tree.children[1]){componentPath.push(childComponentName);const childComponentHistogram=recurse(processPath,componentPath,childComponentNode);componentNames.set(childComponentName,childComponentHistogram.name);componentPath.pop();}
+values.addHistogram(histogram);if(tree.children[0].size>0){histogram.diagnostics.set('processes',processNames);}
+if(tree.children[1].size>0){histogram.diagnostics.set('components',componentNames);}
+return histogram;}
+function getNumericName(browserName,sourceName,propertyName,processName,componentPath){const nameParts=['memory',browserName,processName,sourceName].concat(componentPath);if(propertyName!==undefined)nameParts.push(propertyName);return nameParts.join(':');}
+function getNumericDescription(property,browserName,processName,componentPath){return[property.buildDescriptionPrefix(componentPath,processName),'in',convertBrowserNameToUserFriendlyName(browserName)].join(' ');}
+function buildNamedMemoryNumericFromNode(browserName,sourceName,property,processName,componentPath,node){const name=getNumericName(browserName,sourceName,property.name,processName,componentPath);const description=getNumericDescription(property,browserName,processName,componentPath);const numeric=buildMemoryNumericFromNode(name,node,property.unit);numeric.description=description;return numeric;}
+function buildSampleDiagnostics(value,node){if(node.children.length<2)return undefined;const diagnostics=new Map();const i=node.values.indexOf(value);const processBreakdown=new tr.v.d.Breakdown();processBreakdown.colorScheme=tr.e.chrome.chrome_processes.PROCESS_COLOR_SCHEME_NAME;for(const[name,subNode]of node.children[0]){processBreakdown.set(name,subNode.values[i].total);}
+if(processBreakdown.size>0){diagnostics.set('processes',processBreakdown);}
+const componentBreakdown=new tr.v.d.Breakdown();for(const[name,subNode]of node.children[1]){componentBreakdown.set(name,subNode.values[i].total);}
+if(componentBreakdown.size>0){diagnostics.set('components',componentBreakdown);}
+if(diagnostics.size===0)return undefined;return diagnostics;}
+function buildMemoryNumericFromNode(name,node,unit){const histogram=new tr.v.Histogram(name,unit,BOUNDARIES_FOR_UNIT_MAP.get(unit));node.values.forEach(v=>histogram.addSample(v.total,buildSampleDiagnostics(v,node)));return histogram;}
+tr.metrics.MetricRegistry.register(memoryMetric,{supportsRangeOfInterest:true});return{memoryMetric,};});'use strict';tr.exportTo('tr.metrics.sh',function(){const BYTE_BOUNDARIES=tr.v.HistogramBinBoundaries.createExponential(1,1e9,1e2);function nativeCodeResidentMemoryMetric(histograms,model){const histogram=new tr.v.Histogram('NativeCodeResidentMemory',tr.b.Unit.byName.sizeInBytes_smallerIsBetter,BYTE_BOUNDARIES);for(const slice of model.getDescendantEvents()){if(slice.category==='disabled-by-default-memory-infra'&&slice.title==='ReportGlobalNativeCodeResidentMemoryKb'&&slice.args.NativeCodeResidentMemory){histogram.addSample(slice.args.NativeCodeResidentMemory);}}
+histograms.addHistogram(histogram);}
+tr.metrics.MetricRegistry.register(nativeCodeResidentMemoryMetric);return{nativeCodeResidentMemoryMetric,};});'use strict';tr.exportTo('tr.metrics.sh',function(){const timeDurationInMs_smallerIsBetter=tr.b.Unit.byName.timeDurationInMs_smallerIsBetter;const EventFinderUtils=tr.e.chrome.EventFinderUtils;const LOADING_METRIC_BOUNDARIES=tr.v.HistogramBinBoundaries.createLinear(0,1e3,20).addLinearBins(3e3,20).addExponentialBins(20e3,20);const SUMMARY_OPTIONS={avg:true,count:false,max:false,min:false,std:false,sum:false,};function addSamplesToHistogram(pairInfo,breakdownTree,histogram,histograms,diagnostics){histogram.addSample(pairInfo.end-pairInfo.start,diagnostics);if(!breakdownTree){return;}
+for(const[category,breakdown]of Object.entries(breakdownTree)){const relatedName=`${histogram.name}:${category}`;if(!histograms.getHistogramNamed(relatedName)){const relatedHist=histograms.createHistogram(relatedName,histogram.unit,[],{binBoundaries:LOADING_METRIC_BOUNDARIES,summaryOptions:{count:false,max:false,min:false,sum:false,},});}
+const relatedHist=histograms.getHistogramNamed(relatedName);let relatedNames=histogram.diagnostics.get('breakdown');if(!relatedNames){relatedNames=new tr.v.d.RelatedNameMap();histogram.diagnostics.set('breakdown',relatedNames);}
+relatedNames.set(category,relatedName);relatedHist.addSample(breakdown.total,{breakdown:tr.v.d.Breakdown.fromEntries(Object.entries(breakdown.events)),});}}
+function splitOneRangeIntoPerSecondRanges(startTime,endTime){const results=[];for(let i=0;startTime+(i+1)*1000<=endTime;i+=1){const start=i*1000;const end=(i+1)*1000;results.push({start,end,});}
+return results;}
+function getNavigationInfos(model){const navigationInfos=[];const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);for(const expectation of model.userModel.expectations){if(!(expectation instanceof tr.model.um.LoadExpectation))continue;if(tr.e.chrome.CHROME_INTERNAL_URLS.includes(expectation.url)){continue;}
+const rendererHelper=chromeHelper.rendererHelpers[expectation.renderProcess.pid];if(rendererHelper.mainThread===undefined)continue;navigationInfos.push({navigationStart:expectation.navigationStart,rendererHelper,url:expectation.url});}
+navigationInfos.forEach((navInfo,i)=>{if(i===navigationInfos.length-1){navInfo.navigationEndTime=model.bounds.max;}else{navInfo.navigationEndTime=navigationInfos[i+1].navigationStart.start;}});return navigationInfos;}
+function getWallTimeBreakdownTree(rendererHelper,start,end){const startEndRange=tr.b.math.Range.fromExplicitRange(start,end);const networkEvents=EventFinderUtils.getNetworkEventsInRange(rendererHelper.process,startEndRange);const breakdownTree=tr.metrics.sh.generateWallClockTimeBreakdownTree(rendererHelper.mainThread,networkEvents,startEndRange);return breakdownTree;}
+function getCpuTimeBreakdownTree(rendererHelper,start,end){const startEndRange=tr.b.math.Range.fromExplicitRange(start,end);const breakdownTree=tr.metrics.sh.generateCpuTimeBreakdownTree(rendererHelper.mainThread,startEndRange);return breakdownTree;}
+function persecondMetric(histograms,model){const navigationInfos=getNavigationInfos(model);if(navigationInfos.length===0){return;}
+navigationInfos.forEach(navInfo=>{const navigationStart=navInfo.navigationStart.start;const navigationEnd=navInfo.navigationEndTime;const startEndPairs=splitOneRangeIntoPerSecondRanges(navigationStart,navigationEnd);const breakdownList=startEndPairs.map(p=>{const wallHistogramName=`wall_${p.start}_to_${p.end}`;const wallHistogramDescription=`Wall-clock time ${p.start} to ${p.end} breakdown`;const cpuHistogramName=`cpu_${p.start}_to_${p.end}`;const cpuHistogramDescription=`CPU time ${p.start} to ${p.end} breakdown`;const pid=navInfo.rendererHelper.pid;const breakdownTree=getWallTimeBreakdownTree(navInfo.rendererHelper,navigationStart+p.start,navigationStart+p.end);const cpuBreakdownTree=getCpuTimeBreakdownTree(navInfo.rendererHelper,navigationStart+p.start,navigationStart+p.end);const diagnostics={'Navigation infos':new tr.v.d.GenericSet([{url:navInfo.url,pid:navInfo.rendererHelper.pid,navStart:navigationStart,frameIdRef:navInfo.navigationStart.args.frame}]),'breakdown':tr.metrics.sh.createBreakdownDiagnostic(breakdownTree),};return Object.assign(p,{breakdownTree,cpuBreakdownTree,wallHistogramName,wallHistogramDescription,cpuHistogramName,cpuHistogramDescription,diagnostics,});});breakdownList.forEach(p=>{if(!histograms.getHistogramNamed(p.wallHistogramName)){histograms.createHistogram(p.wallHistogramName,timeDurationInMs_smallerIsBetter,[],{binBoundaries:LOADING_METRIC_BOUNDARIES,description:p.wallHistogramDescription,summaryOptions:SUMMARY_OPTIONS,});}
+const wallHistogram=histograms.getHistogramNamed(p.wallHistogramName);addSamplesToHistogram(p,p.breakdownTree,wallHistogram,histograms,p.diagnostics);if(!histograms.getHistogramNamed(p.cpuHistogramName)){histograms.createHistogram(p.cpuHistogramName,timeDurationInMs_smallerIsBetter,[],{binBoundaries:LOADING_METRIC_BOUNDARIES,description:p.cpuHistogramDescription,summaryOptions:SUMMARY_OPTIONS,});}
+const cpuHistogram=histograms.getHistogramNamed(p.cpuHistogramName);addSamplesToHistogram(p,p.cpuBreakdownTree,cpuHistogram,histograms,p.diagnostics);});});}
+tr.metrics.MetricRegistry.register(persecondMetric);return{persecondMetric,splitOneRangeIntoPerSecondRanges};});'use strict';tr.exportTo('tr.metrics.sh',function(){const CHROME_POWER_GRACE_PERIOD_MS=1;function createEmptyHistogram_(interval,histograms){if(interval.perSecond){return{perSecond:true,energy:histograms.createHistogram(`${interval.name}:power`,tr.b.Unit.byName.powerInWatts_smallerIsBetter,[],{description:`Energy consumption rate for ${interval.description}`,summaryOptions:{avg:true,count:false,max:true,min:true,std:false,sum:false,},}),};}
+return{perSecond:false,energy:histograms.createHistogram(`${interval.name}:energy`,tr.b.Unit.byName.energyInJoules_smallerIsBetter,[],{description:`Energy consumed in ${interval.description}`,summaryOptions:{avg:false,count:false,max:true,min:true,std:false,sum:true,},}),};}
+function createHistograms_(data,interval,histograms){if(data.histograms[interval.name]===undefined){data.histograms[interval.name]=createEmptyHistogram_(interval,histograms);}
+if(data.histograms[interval.name].perSecond){for(const sample of data.model.device.powerSeries.getSamplesWithinRange(interval.bounds.min,interval.bounds.max)){data.histograms[interval.name].energy.addSample(sample.powerInW);}}else{const energyInJ=data.model.device.powerSeries.getEnergyConsumedInJ(interval.bounds.min,interval.bounds.max);data.histograms[interval.name].energy.addSample(energyInJ);}}
+function getNavigationTTIIntervals_(model){const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);const intervals=[];for(const expectation of model.userModel.expectations){if(!(expectation instanceof tr.model.um.LoadExpectation))continue;if(tr.e.chrome.CHROME_INTERNAL_URLS.includes(expectation.url)){continue;}
+if(expectation.timeToInteractive!==undefined){intervals.push(tr.b.math.Range.fromExplicitRange(expectation.navigationStart.start,expectation.timeToInteractive));}}
+return intervals.sort((x,y)=>x.min-y.min);}
+function*computeTimeIntervals_(model){const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);const powerSeries=model.device.powerSeries;if(powerSeries===undefined||powerSeries.samples.length===0){return;}
+yield{bounds:model.bounds,name:'story',description:'user story',perSecond:true};const chromeBounds=computeChromeBounds_(model);if(chromeBounds.isEmpty)return;const powerSeriesBoundsWithGracePeriod=tr.b.math.Range.fromExplicitRange(powerSeries.bounds.min-CHROME_POWER_GRACE_PERIOD_MS,powerSeries.bounds.max+CHROME_POWER_GRACE_PERIOD_MS);if(!powerSeriesBoundsWithGracePeriod.containsRangeExclusive(chromeBounds)){return;}
+for(const interval of getRailStageIntervals_(model)){yield{bounds:interval.bounds.findIntersection(chromeBounds),name:interval.name,description:interval.description,perSecond:interval.perSecond};}
+for(const interval of getLoadingIntervals_(model,chromeBounds)){yield{bounds:interval.bounds.findIntersection(chromeBounds),name:interval.name,description:interval.description,perSecond:interval.perSecond};}}
+function*getRailStageIntervals_(model){for(const exp of model.userModel.expectations){const histogramName=exp.title.toLowerCase().replace(' ','_');const energyHist=undefined;if(histogramName.includes('response')){yield{bounds:tr.b.math.Range.fromExplicitRange(exp.start,exp.end),name:histogramName,description:'RAIL stage '+histogramName,perSecond:false};}else if(histogramName.includes('animation')||histogramName.includes('idle')){yield{bounds:tr.b.math.Range.fromExplicitRange(exp.start,exp.end),name:histogramName,description:'RAIL stage '+histogramName,perSecond:true};}}}
+function*getLoadingIntervals_(model,chromeBounds){const ttiIntervals=getNavigationTTIIntervals_(model);for(const ttiInterval of ttiIntervals){yield{bounds:ttiInterval,name:'load',description:'page loads',perSecond:false};}}
+function computeChromeBounds_(model){const chromeBounds=new tr.b.math.Range();const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);if(chromeHelper===undefined)return chromeBounds;for(const helper of chromeHelper.browserHelpers){if(helper.mainThread){chromeBounds.addRange(helper.mainThread.bounds);}}
+for(const pid in chromeHelper.rendererHelpers){if(chromeHelper.rendererHelpers[pid].mainThread){chromeBounds.addRange(chromeHelper.rendererHelpers[pid].mainThread.bounds);}}
+return chromeBounds;}
+function powerMetric(histograms,model){const data={model,histograms:{}};for(const interval of computeTimeIntervals_(model)){createHistograms_(data,interval,histograms);}}
+tr.metrics.MetricRegistry.register(powerMetric);return{powerMetric};});'use strict';tr.exportTo('tr.metrics.sh',function(){function computeAnimationThroughput(animationExpectation){if(animationExpectation.frameEvents===undefined||animationExpectation.frameEvents.length===0){throw new Error('Animation missing frameEvents '+
+animationExpectation.stableId);}
+const durationInS=tr.b.convertUnit(animationExpectation.duration,tr.b.UnitPrefixScale.METRIC.MILLI,tr.b.UnitPrefixScale.METRIC.NONE);return animationExpectation.frameEvents.length/durationInS;}
+function computeAnimationframeTimeDiscrepancy(animationExpectation){if(animationExpectation.frameEvents===undefined||animationExpectation.frameEvents.length===0){throw new Error('Animation missing frameEvents '+
+animationExpectation.stableId);}
+let frameTimestamps=animationExpectation.frameEvents;frameTimestamps=frameTimestamps.toArray().map(function(event){return event.start;});const absolute=true;return tr.b.math.Statistics.timestampsDiscrepancy(frameTimestamps,absolute);}
+function responsivenessMetric(histograms,model,opt_options){const responseNumeric=new tr.v.Histogram('response latency',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,tr.v.HistogramBinBoundaries.createLinear(100,1e3,50));const throughputNumeric=new tr.v.Histogram('animation throughput',tr.b.Unit.byName.unitlessNumber_biggerIsBetter,tr.v.HistogramBinBoundaries.createLinear(10,60,10));const frameTimeDiscrepancyNumeric=new tr.v.Histogram('animation frameTimeDiscrepancy',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,tr.v.HistogramBinBoundaries.createLinear(0,1e3,50).addExponentialBins(1e4,10));const latencyNumeric=new tr.v.Histogram('animation latency',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,tr.v.HistogramBinBoundaries.createLinear(0,300,60));model.userModel.expectations.forEach(function(ue){if(opt_options&&opt_options.rangeOfInterest&&!opt_options.rangeOfInterest.intersectsExplicitRangeInclusive(ue.start,ue.end)){return;}
+const sampleDiagnosticMap=tr.v.d.DiagnosticMap.fromObject({relatedEvents:new tr.v.d.RelatedEventSet([ue])});if(ue instanceof tr.model.um.IdleExpectation){return;}else if(ue instanceof tr.model.um.StartupExpectation){return;}else if(ue instanceof tr.model.um.LoadExpectation){}else if(ue instanceof tr.model.um.ResponseExpectation){responseNumeric.addSample(ue.duration,sampleDiagnosticMap);}else if(ue instanceof tr.model.um.AnimationExpectation){if(ue.frameEvents===undefined||ue.frameEvents.length===0){return;}
+const throughput=computeAnimationThroughput(ue);if(throughput===undefined){throw new Error('Missing throughput for '+
+ue.stableId);}
+throughputNumeric.addSample(throughput,sampleDiagnosticMap);const frameTimeDiscrepancy=computeAnimationframeTimeDiscrepancy(ue);if(frameTimeDiscrepancy===undefined){throw new Error('Missing frameTimeDiscrepancy for '+
+ue.stableId);}
+frameTimeDiscrepancyNumeric.addSample(frameTimeDiscrepancy,sampleDiagnosticMap);ue.associatedEvents.forEach(function(event){if(!(event instanceof tr.e.cc.InputLatencyAsyncSlice)){return;}
+latencyNumeric.addSample(event.duration,sampleDiagnosticMap);});}else{throw new Error('Unrecognized stage for '+ue.stableId);}});[responseNumeric,throughputNumeric,frameTimeDiscrepancyNumeric,latencyNumeric].forEach(function(numeric){numeric.customizeSummaryOptions({avg:true,max:true,min:true,std:true});});histograms.addHistogram(responseNumeric);histograms.addHistogram(throughputNumeric);histograms.addHistogram(frameTimeDiscrepancyNumeric);histograms.addHistogram(latencyNumeric);}
+tr.metrics.MetricRegistry.register(responsivenessMetric,{supportsRangeOfInterest:true,requiredCategories:['rail'],});return{responsivenessMetric,};});var JpegImage=(function jpegImage(){"use strict";var dctZigZag=new Int32Array([0,1,8,16,9,2,3,10,17,24,32,25,18,11,4,5,12,19,26,33,40,48,41,34,27,20,13,6,7,14,21,28,35,42,49,56,57,50,43,36,29,22,15,23,30,37,44,51,58,59,52,45,38,31,39,46,53,60,61,54,47,55,62,63]);var dctCos1=4017
+var dctSin1=799
+var dctCos3=3406
+var dctSin3=2276
+var dctCos6=1567
+var dctSin6=3784
+var dctSqrt2=5793
+var dctSqrt1d2=2896
+function constructor(){}
+function buildHuffmanTable(codeLengths,values){var k=0,code=[],i,j,length=16;while(length>0&&!codeLengths[length-1])
+length--;code.push({children:[],index:0});var p=code[0],q;for(i=0;i<length;i++){for(j=0;j<codeLengths[i];j++){p=code.pop();p.children[p.index]=values[k];while(p.index>0){p=code.pop();}
+p.index++;code.push(p);while(code.length<=i){code.push(q={children:[],index:0});p.children[p.index]=q.children;p=q;}
+k++;}
+if(i+1<length){code.push(q={children:[],index:0});p.children[p.index]=q.children;p=q;}}
+return code[0].children;}
+function decodeScan(data,offset,frame,components,resetInterval,spectralStart,spectralEnd,successivePrev,successive){var precision=frame.precision;var samplesPerLine=frame.samplesPerLine;var scanLines=frame.scanLines;var mcusPerLine=frame.mcusPerLine;var progressive=frame.progressive;var maxH=frame.maxH,maxV=frame.maxV;var startOffset=offset,bitsData=0,bitsCount=0;function readBit(){if(bitsCount>0){bitsCount--;return(bitsData>>bitsCount)&1;}
+bitsData=data[offset++];if(bitsData==0xFF){var nextByte=data[offset++];if(nextByte){throw new Error("unexpected marker: "+((bitsData<<8)|nextByte).toString(16));}}
+bitsCount=7;return bitsData>>>7;}
+function decodeHuffman(tree){var node=tree,bit;while((bit=readBit())!==null){node=node[bit];if(typeof node==='number')
+return node;if(typeof node!=='object')
+throw new Error("invalid huffman sequence");}
+return null;}
+function receive(length){var n=0;while(length>0){var bit=readBit();if(bit===null)return;n=(n<<1)|bit;length--;}
+return n;}
+function receiveAndExtend(length){var n=receive(length);if(n>=1<<(length-1))
+return n;return n+(-1<<length)+1;}
+function decodeBaseline(component,zz){var t=decodeHuffman(component.huffmanTableDC);var diff=t===0?0:receiveAndExtend(t);zz[0]=(component.pred+=diff);var k=1;while(k<64){var rs=decodeHuffman(component.huffmanTableAC);var s=rs&15,r=rs>>4;if(s===0){if(r<15)
+break;k+=16;continue;}
+k+=r;var z=dctZigZag[k];zz[z]=receiveAndExtend(s);k++;}}
+function decodeDCFirst(component,zz){var t=decodeHuffman(component.huffmanTableDC);var diff=t===0?0:(receiveAndExtend(t)<<successive);zz[0]=(component.pred+=diff);}
+function decodeDCSuccessive(component,zz){zz[0]|=readBit()<<successive;}
+var eobrun=0;function decodeACFirst(component,zz){if(eobrun>0){eobrun--;return;}
+var k=spectralStart,e=spectralEnd;while(k<=e){var rs=decodeHuffman(component.huffmanTableAC);var s=rs&15,r=rs>>4;if(s===0){if(r<15){eobrun=receive(r)+(1<<r)-1;break;}
+k+=16;continue;}
+k+=r;var z=dctZigZag[k];zz[z]=receiveAndExtend(s)*(1<<successive);k++;}}
+var successiveACState=0,successiveACNextValue;function decodeACSuccessive(component,zz){var k=spectralStart,e=spectralEnd,r=0;while(k<=e){var z=dctZigZag[k];var direction=zz[z]<0?-1:1;switch(successiveACState){case 0:var rs=decodeHuffman(component.huffmanTableAC);var s=rs&15,r=rs>>4;if(s===0){if(r<15){eobrun=receive(r)+(1<<r);successiveACState=4;}else{r=16;successiveACState=1;}}else{if(s!==1)
+throw new Error("invalid ACn encoding");successiveACNextValue=receiveAndExtend(s);successiveACState=r?2:3;}
+continue;case 1:case 2:if(zz[z])
+zz[z]+=(readBit()<<successive)*direction;else{r--;if(r===0)
+successiveACState=successiveACState==2?3:0;}
+break;case 3:if(zz[z])
+zz[z]+=(readBit()<<successive)*direction;else{zz[z]=successiveACNextValue<<successive;successiveACState=0;}
+break;case 4:if(zz[z])
+zz[z]+=(readBit()<<successive)*direction;break;}
+k++;}
+if(successiveACState===4){eobrun--;if(eobrun===0)
+successiveACState=0;}}
+function decodeMcu(component,decode,mcu,row,col){var mcuRow=(mcu/mcusPerLine)|0;var mcuCol=mcu%mcusPerLine;var blockRow=mcuRow*component.v+row;var blockCol=mcuCol*component.h+col;decode(component,component.blocks[blockRow][blockCol]);}
+function decodeBlock(component,decode,mcu){var blockRow=(mcu/component.blocksPerLine)|0;var blockCol=mcu%component.blocksPerLine;decode(component,component.blocks[blockRow][blockCol]);}
+var componentsLength=components.length;var component,i,j,k,n;var decodeFn;if(progressive){if(spectralStart===0)
+decodeFn=successivePrev===0?decodeDCFirst:decodeDCSuccessive;else
+decodeFn=successivePrev===0?decodeACFirst:decodeACSuccessive;}else{decodeFn=decodeBaseline;}
+var mcu=0,marker;var mcuExpected;if(componentsLength==1){mcuExpected=components[0].blocksPerLine*components[0].blocksPerColumn;}else{mcuExpected=mcusPerLine*frame.mcusPerColumn;}
+if(!resetInterval)resetInterval=mcuExpected;var h,v;while(mcu<mcuExpected){for(i=0;i<componentsLength;i++)
+components[i].pred=0;eobrun=0;if(componentsLength==1){component=components[0];for(n=0;n<resetInterval;n++){decodeBlock(component,decodeFn,mcu);mcu++;}}else{for(n=0;n<resetInterval;n++){for(i=0;i<componentsLength;i++){component=components[i];h=component.h;v=component.v;for(j=0;j<v;j++){for(k=0;k<h;k++){decodeMcu(component,decodeFn,mcu,j,k);}}}
+mcu++;if(mcu===mcuExpected)break;}}
+bitsCount=0;marker=(data[offset]<<8)|data[offset+1];if(marker<0xFF00){throw new Error("marker was not found");}
+if(marker>=0xFFD0&&marker<=0xFFD7){offset+=2;}
+else
+break;}
+return offset-startOffset;}
+function buildComponentData(frame,component){var lines=[];var blocksPerLine=component.blocksPerLine;var blocksPerColumn=component.blocksPerColumn;var samplesPerLine=blocksPerLine<<3;var R=new Int32Array(64),r=new Uint8Array(64);function quantizeAndInverse(zz,dataOut,dataIn){var qt=component.quantizationTable;var v0,v1,v2,v3,v4,v5,v6,v7,t;var p=dataIn;var i;for(i=0;i<64;i++)
+p[i]=zz[i]*qt[i];for(i=0;i<8;++i){var row=8*i;if(p[1+row]==0&&p[2+row]==0&&p[3+row]==0&&p[4+row]==0&&p[5+row]==0&&p[6+row]==0&&p[7+row]==0){t=(dctSqrt2*p[0+row]+512)>>10;p[0+row]=t;p[1+row]=t;p[2+row]=t;p[3+row]=t;p[4+row]=t;p[5+row]=t;p[6+row]=t;p[7+row]=t;continue;}
+v0=(dctSqrt2*p[0+row]+128)>>8;v1=(dctSqrt2*p[4+row]+128)>>8;v2=p[2+row];v3=p[6+row];v4=(dctSqrt1d2*(p[1+row]-p[7+row])+128)>>8;v7=(dctSqrt1d2*(p[1+row]+p[7+row])+128)>>8;v5=p[3+row]<<4;v6=p[5+row]<<4;t=(v0-v1+1)>>1;v0=(v0+v1+1)>>1;v1=t;t=(v2*dctSin6+v3*dctCos6+128)>>8;v2=(v2*dctCos6-v3*dctSin6+128)>>8;v3=t;t=(v4-v6+1)>>1;v4=(v4+v6+1)>>1;v6=t;t=(v7+v5+1)>>1;v5=(v7-v5+1)>>1;v7=t;t=(v0-v3+1)>>1;v0=(v0+v3+1)>>1;v3=t;t=(v1-v2+1)>>1;v1=(v1+v2+1)>>1;v2=t;t=(v4*dctSin3+v7*dctCos3+2048)>>12;v4=(v4*dctCos3-v7*dctSin3+2048)>>12;v7=t;t=(v5*dctSin1+v6*dctCos1+2048)>>12;v5=(v5*dctCos1-v6*dctSin1+2048)>>12;v6=t;p[0+row]=v0+v7;p[7+row]=v0-v7;p[1+row]=v1+v6;p[6+row]=v1-v6;p[2+row]=v2+v5;p[5+row]=v2-v5;p[3+row]=v3+v4;p[4+row]=v3-v4;}
+for(i=0;i<8;++i){var col=i;if(p[1*8+col]==0&&p[2*8+col]==0&&p[3*8+col]==0&&p[4*8+col]==0&&p[5*8+col]==0&&p[6*8+col]==0&&p[7*8+col]==0){t=(dctSqrt2*dataIn[i+0]+8192)>>14;p[0*8+col]=t;p[1*8+col]=t;p[2*8+col]=t;p[3*8+col]=t;p[4*8+col]=t;p[5*8+col]=t;p[6*8+col]=t;p[7*8+col]=t;continue;}
+v0=(dctSqrt2*p[0*8+col]+2048)>>12;v1=(dctSqrt2*p[4*8+col]+2048)>>12;v2=p[2*8+col];v3=p[6*8+col];v4=(dctSqrt1d2*(p[1*8+col]-p[7*8+col])+2048)>>12;v7=(dctSqrt1d2*(p[1*8+col]+p[7*8+col])+2048)>>12;v5=p[3*8+col];v6=p[5*8+col];t=(v0-v1+1)>>1;v0=(v0+v1+1)>>1;v1=t;t=(v2*dctSin6+v3*dctCos6+2048)>>12;v2=(v2*dctCos6-v3*dctSin6+2048)>>12;v3=t;t=(v4-v6+1)>>1;v4=(v4+v6+1)>>1;v6=t;t=(v7+v5+1)>>1;v5=(v7-v5+1)>>1;v7=t;t=(v0-v3+1)>>1;v0=(v0+v3+1)>>1;v3=t;t=(v1-v2+1)>>1;v1=(v1+v2+1)>>1;v2=t;t=(v4*dctSin3+v7*dctCos3+2048)>>12;v4=(v4*dctCos3-v7*dctSin3+2048)>>12;v7=t;t=(v5*dctSin1+v6*dctCos1+2048)>>12;v5=(v5*dctCos1-v6*dctSin1+2048)>>12;v6=t;p[0*8+col]=v0+v7;p[7*8+col]=v0-v7;p[1*8+col]=v1+v6;p[6*8+col]=v1-v6;p[2*8+col]=v2+v5;p[5*8+col]=v2-v5;p[3*8+col]=v3+v4;p[4*8+col]=v3-v4;}
+for(i=0;i<64;++i){var sample=128+((p[i]+8)>>4);dataOut[i]=sample<0?0:sample>0xFF?0xFF:sample;}}
+var i,j;for(var blockRow=0;blockRow<blocksPerColumn;blockRow++){var scanLine=blockRow<<3;for(i=0;i<8;i++)
+lines.push(new Uint8Array(samplesPerLine));for(var blockCol=0;blockCol<blocksPerLine;blockCol++){quantizeAndInverse(component.blocks[blockRow][blockCol],r,R);var offset=0,sample=blockCol<<3;for(j=0;j<8;j++){var line=lines[scanLine+j];for(i=0;i<8;i++)
+line[sample+i]=r[offset++];}}}
+return lines;}
+function clampTo8bit(a){return a<0?0:a>255?255:a;}
+constructor.prototype={load:function load(path){var xhr=new XMLHttpRequest();xhr.open("GET",path,true);xhr.responseType="arraybuffer";xhr.onload=(function(){var data=new Uint8Array(xhr.response||xhr.mozResponseArrayBuffer);this.parse(data);if(this.onload)
+this.onload();}).bind(this);xhr.send(null);},parse:function parse(data){var offset=0,length=data.length;function readUint16(){var value=(data[offset]<<8)|data[offset+1];offset+=2;return value;}
+function readDataBlock(){var length=readUint16();var array=data.subarray(offset,offset+length-2);offset+=array.length;return array;}
+function prepareComponents(frame){var maxH=0,maxV=0;var component,componentId;for(componentId in frame.components){if(frame.components.hasOwnProperty(componentId)){component=frame.components[componentId];if(maxH<component.h)maxH=component.h;if(maxV<component.v)maxV=component.v;}}
+var mcusPerLine=Math.ceil(frame.samplesPerLine/8/maxH);var mcusPerColumn=Math.ceil(frame.scanLines/8/maxV);for(componentId in frame.components){if(frame.components.hasOwnProperty(componentId)){component=frame.components[componentId];var blocksPerLine=Math.ceil(Math.ceil(frame.samplesPerLine/8)*component.h/maxH);var blocksPerColumn=Math.ceil(Math.ceil(frame.scanLines/8)*component.v/maxV);var blocksPerLineForMcu=mcusPerLine*component.h;var blocksPerColumnForMcu=mcusPerColumn*component.v;var blocks=[];for(var i=0;i<blocksPerColumnForMcu;i++){var row=[];for(var j=0;j<blocksPerLineForMcu;j++)
+row.push(new Int32Array(64));blocks.push(row);}
+component.blocksPerLine=blocksPerLine;component.blocksPerColumn=blocksPerColumn;component.blocks=blocks;}}
+frame.maxH=maxH;frame.maxV=maxV;frame.mcusPerLine=mcusPerLine;frame.mcusPerColumn=mcusPerColumn;}
+var jfif=null;var adobe=null;var pixels=null;var frame,resetInterval;var quantizationTables=[],frames=[];var huffmanTablesAC=[],huffmanTablesDC=[];var fileMarker=readUint16();if(fileMarker!=0xFFD8){throw new Error("SOI not found");}
+fileMarker=readUint16();while(fileMarker!=0xFFD9){var i,j,l;switch(fileMarker){case 0xFF00:break;case 0xFFE0:case 0xFFE1:case 0xFFE2:case 0xFFE3:case 0xFFE4:case 0xFFE5:case 0xFFE6:case 0xFFE7:case 0xFFE8:case 0xFFE9:case 0xFFEA:case 0xFFEB:case 0xFFEC:case 0xFFED:case 0xFFEE:case 0xFFEF:case 0xFFFE:var appData=readDataBlock();if(fileMarker===0xFFE0){if(appData[0]===0x4A&&appData[1]===0x46&&appData[2]===0x49&&appData[3]===0x46&&appData[4]===0){jfif={version:{major:appData[5],minor:appData[6]},densityUnits:appData[7],xDensity:(appData[8]<<8)|appData[9],yDensity:(appData[10]<<8)|appData[11],thumbWidth:appData[12],thumbHeight:appData[13],thumbData:appData.subarray(14,14+3*appData[12]*appData[13])};}}
+if(fileMarker===0xFFEE){if(appData[0]===0x41&&appData[1]===0x64&&appData[2]===0x6F&&appData[3]===0x62&&appData[4]===0x65&&appData[5]===0){adobe={version:appData[6],flags0:(appData[7]<<8)|appData[8],flags1:(appData[9]<<8)|appData[10],transformCode:appData[11]};}}
+break;case 0xFFDB:var quantizationTablesLength=readUint16();var quantizationTablesEnd=quantizationTablesLength+offset-2;while(offset<quantizationTablesEnd){var quantizationTableSpec=data[offset++];var tableData=new Int32Array(64);if((quantizationTableSpec>>4)===0){for(j=0;j<64;j++){var z=dctZigZag[j];tableData[z]=data[offset++];}}else if((quantizationTableSpec>>4)===1){for(j=0;j<64;j++){var z=dctZigZag[j];tableData[z]=readUint16();}}else
+throw new Error("DQT: invalid table spec");quantizationTables[quantizationTableSpec&15]=tableData;}
+break;case 0xFFC0:case 0xFFC1:case 0xFFC2:readUint16();frame={};frame.extended=(fileMarker===0xFFC1);frame.progressive=(fileMarker===0xFFC2);frame.precision=data[offset++];frame.scanLines=readUint16();frame.samplesPerLine=readUint16();frame.components={};frame.componentsOrder=[];var componentsCount=data[offset++],componentId;var maxH=0,maxV=0;for(i=0;i<componentsCount;i++){componentId=data[offset];var h=data[offset+1]>>4;var v=data[offset+1]&15;var qId=data[offset+2];frame.componentsOrder.push(componentId);frame.components[componentId]={h:h,v:v,quantizationIdx:qId};offset+=3;}
+prepareComponents(frame);frames.push(frame);break;case 0xFFC4:var huffmanLength=readUint16();for(i=2;i<huffmanLength;){var huffmanTableSpec=data[offset++];var codeLengths=new Uint8Array(16);var codeLengthSum=0;for(j=0;j<16;j++,offset++)
+codeLengthSum+=(codeLengths[j]=data[offset]);var huffmanValues=new Uint8Array(codeLengthSum);for(j=0;j<codeLengthSum;j++,offset++)
+huffmanValues[j]=data[offset];i+=17+codeLengthSum;((huffmanTableSpec>>4)===0?huffmanTablesDC:huffmanTablesAC)[huffmanTableSpec&15]=buildHuffmanTable(codeLengths,huffmanValues);}
+break;case 0xFFDD:readUint16();resetInterval=readUint16();break;case 0xFFDA:var scanLength=readUint16();var selectorsCount=data[offset++];var components=[],component;for(i=0;i<selectorsCount;i++){component=frame.components[data[offset++]];var tableSpec=data[offset++];component.huffmanTableDC=huffmanTablesDC[tableSpec>>4];component.huffmanTableAC=huffmanTablesAC[tableSpec&15];components.push(component);}
+var spectralStart=data[offset++];var spectralEnd=data[offset++];var successiveApproximation=data[offset++];var processed=decodeScan(data,offset,frame,components,resetInterval,spectralStart,spectralEnd,successiveApproximation>>4,successiveApproximation&15);offset+=processed;break;case 0xFFFF:if(data[offset]!==0xFF){offset--;}
+break;default:if(data[offset-3]==0xFF&&data[offset-2]>=0xC0&&data[offset-2]<=0xFE){offset-=3;break;}
+throw new Error("unknown JPEG marker "+fileMarker.toString(16));}
+fileMarker=readUint16();}
+if(frames.length!=1)
+throw new Error("only single frame JPEGs supported");for(var i=0;i<frames.length;i++){var cp=frames[i].components;for(var j in cp){cp[j].quantizationTable=quantizationTables[cp[j].quantizationIdx];delete cp[j].quantizationIdx;}}
+this.width=frame.samplesPerLine;this.height=frame.scanLines;this.jfif=jfif;this.adobe=adobe;this.components=[];for(var i=0;i<frame.componentsOrder.length;i++){var component=frame.components[frame.componentsOrder[i]];this.components.push({lines:buildComponentData(frame,component),scaleX:component.h/frame.maxH,scaleY:component.v/frame.maxV});}},getData:function getData(width,height){var scaleX=this.width/width,scaleY=this.height/height;var component1,component2,component3,component4;var component1Line,component2Line,component3Line,component4Line;var x,y;var offset=0;var Y,Cb,Cr,K,C,M,Ye,R,G,B;var colorTransform;var dataLength=width*height*this.components.length;var data=new Uint8Array(dataLength);switch(this.components.length){case 1:component1=this.components[0];for(y=0;y<height;y++){component1Line=component1.lines[0|(y*component1.scaleY*scaleY)];for(x=0;x<width;x++){Y=component1Line[0|(x*component1.scaleX*scaleX)];data[offset++]=Y;}}
+break;case 2:component1=this.components[0];component2=this.components[1];for(y=0;y<height;y++){component1Line=component1.lines[0|(y*component1.scaleY*scaleY)];component2Line=component2.lines[0|(y*component2.scaleY*scaleY)];for(x=0;x<width;x++){Y=component1Line[0|(x*component1.scaleX*scaleX)];data[offset++]=Y;Y=component2Line[0|(x*component2.scaleX*scaleX)];data[offset++]=Y;}}
+break;case 3:colorTransform=true;if(this.adobe&&this.adobe.transformCode)
+colorTransform=true;else if(typeof this.colorTransform!=='undefined')
+colorTransform=!!this.colorTransform;component1=this.components[0];component2=this.components[1];component3=this.components[2];for(y=0;y<height;y++){component1Line=component1.lines[0|(y*component1.scaleY*scaleY)];component2Line=component2.lines[0|(y*component2.scaleY*scaleY)];component3Line=component3.lines[0|(y*component3.scaleY*scaleY)];for(x=0;x<width;x++){if(!colorTransform){R=component1Line[0|(x*component1.scaleX*scaleX)];G=component2Line[0|(x*component2.scaleX*scaleX)];B=component3Line[0|(x*component3.scaleX*scaleX)];}else{Y=component1Line[0|(x*component1.scaleX*scaleX)];Cb=component2Line[0|(x*component2.scaleX*scaleX)];Cr=component3Line[0|(x*component3.scaleX*scaleX)];R=clampTo8bit(Y+1.402*(Cr-128));G=clampTo8bit(Y-0.3441363*(Cb-128)-0.71413636*(Cr-128));B=clampTo8bit(Y+1.772*(Cb-128));}
+data[offset++]=R;data[offset++]=G;data[offset++]=B;}}
+break;case 4:if(!this.adobe)
+throw new Error('Unsupported color mode (4 components)');colorTransform=false;if(this.adobe&&this.adobe.transformCode)
+colorTransform=true;else if(typeof this.colorTransform!=='undefined')
+colorTransform=!!this.colorTransform;component1=this.components[0];component2=this.components[1];component3=this.components[2];component4=this.components[3];for(y=0;y<height;y++){component1Line=component1.lines[0|(y*component1.scaleY*scaleY)];component2Line=component2.lines[0|(y*component2.scaleY*scaleY)];component3Line=component3.lines[0|(y*component3.scaleY*scaleY)];component4Line=component4.lines[0|(y*component4.scaleY*scaleY)];for(x=0;x<width;x++){if(!colorTransform){C=component1Line[0|(x*component1.scaleX*scaleX)];M=component2Line[0|(x*component2.scaleX*scaleX)];Ye=component3Line[0|(x*component3.scaleX*scaleX)];K=component4Line[0|(x*component4.scaleX*scaleX)];}else{Y=component1Line[0|(x*component1.scaleX*scaleX)];Cb=component2Line[0|(x*component2.scaleX*scaleX)];Cr=component3Line[0|(x*component3.scaleX*scaleX)];K=component4Line[0|(x*component4.scaleX*scaleX)];C=255-clampTo8bit(Y+1.402*(Cr-128));M=255-clampTo8bit(Y-0.3441363*(Cb-128)-0.71413636*(Cr-128));Ye=255-clampTo8bit(Y+1.772*(Cb-128));}
+data[offset++]=255-C;data[offset++]=255-M;data[offset++]=255-Ye;data[offset++]=255-K;}}
+break;default:throw new Error('Unsupported color mode');}
+return data;},copyToImageData:function copyToImageData(imageData){var width=imageData.width,height=imageData.height;var imageDataArray=imageData.data;var data=this.getData(width,height);var i=0,j=0,x,y;var Y,K,C,M,R,G,B;switch(this.components.length){case 1:for(y=0;y<height;y++){for(x=0;x<width;x++){Y=data[i++];imageDataArray[j++]=Y;imageDataArray[j++]=Y;imageDataArray[j++]=Y;imageDataArray[j++]=255;}}
+break;case 3:for(y=0;y<height;y++){for(x=0;x<width;x++){R=data[i++];G=data[i++];B=data[i++];imageDataArray[j++]=R;imageDataArray[j++]=G;imageDataArray[j++]=B;imageDataArray[j++]=255;}}
+break;case 4:for(y=0;y<height;y++){for(x=0;x<width;x++){C=data[i++];M=data[i++];Y=data[i++];K=data[i++];R=255-clampTo8bit(C*(1-K/255)+K);G=255-clampTo8bit(M*(1-K/255)+K);B=255-clampTo8bit(Y*(1-K/255)+K);imageDataArray[j++]=R;imageDataArray[j++]=G;imageDataArray[j++]=B;imageDataArray[j++]=255;}}
+break;default:throw new Error('Unsupported color mode');}}};return constructor;})();global.jpegDecode=decode;function decode(jpegData,opts){var defaultOpts={useTArray:false,colorTransform:true};if(opts){if(typeof opts==='object'){opts={useTArray:(typeof opts.useTArray==='undefined'?defaultOpts.useTArray:opts.useTArray),colorTransform:(typeof opts.colorTransform==='undefined'?defaultOpts.colorTransform:opts.colorTransform)};}else{opts=defaultOpts;opts.useTArray=true;}}else{opts=defaultOpts;}
+var arr=new Uint8Array(jpegData);var decoder=new JpegImage();decoder.parse(arr);decoder.colorTransform=opts.colorTransform;var image={width:decoder.width,height:decoder.height,data:opts.useTArray?new Uint8Array(decoder.width*decoder.height*4):new Buffer(decoder.width*decoder.height*4)};decoder.copyToImageData(image);return image;}'use strict';tr.exportTo('tr.metrics.sh',function(){const timeDurationInMs_smallerIsBetter=tr.b.Unit.byName.timeDurationInMs_smallerIsBetter;const SpeedIndex=tr.e.chrome.SpeedIndex;const LOADING_METRIC_BOUNDARIES=tr.v.HistogramBinBoundaries.createLinear(0,1e3,20).addLinearBins(3e3,20).addExponentialBins(20e3,20);const SUMMARY_OPTIONS={avg:true,count:false,max:true,min:true,std:true,sum:false,};function addSpeedIndexScreenshotsBasedSample(samples,navigationStart,loadDuration,browserHelper){const screenshotObjects=browserHelper.process.objects.getAllInstancesNamed('Screenshot');if(!screenshotObjects)return;for(let i=0;i<screenshotObjects.length;i++){const snapshots=screenshotObjects[i].snapshots;const timestampedColorHistograms=[];snapshots.map(snapshot=>{if(snapshot.ts>=navigationStart.start&&snapshot.ts<navigationStart.start+loadDuration){timestampedColorHistograms.push({colorHistogram:SpeedIndex.createColorHistogram(getPixelData(snapshot.args)),ts:snapshot.ts});}});samples.push({value:SpeedIndex.calculateSpeedIndex(timestampedColorHistograms)-
+navigationStart.start});}}
+function getPixelData(base64JpegImage){const binaryString=atob(base64JpegImage);const bytes=new DataView(new ArrayBuffer(base64JpegImage.length));tr.b.Base64.DecodeToTypedArray(base64JpegImage,bytes);const rawImageData=jpegDecode(bytes.buffer,{useTArray:true});return rawImageData.data;}
+function collectSpeedIndexSamplesFromLoadExpectations(model,chromeHelper){const speedIndexScreenshotsBasedSamples=[];for(const expectation of model.userModel.expectations){if(!(expectation instanceof tr.model.um.LoadExpectation))continue;if(tr.e.chrome.CHROME_INTERNAL_URLS.includes(expectation.url)){continue;}
+const rendererHelper=chromeHelper.rendererHelpers[expectation.renderProcess.pid];addSpeedIndexScreenshotsBasedSample(speedIndexScreenshotsBasedSamples,expectation.navigationStart,expectation.duration,chromeHelper.browserHelper);}
+return speedIndexScreenshotsBasedSamples;}
+function screenshotsBasedSpeedIndexMetric(histograms,model){const speedIndexScreenshotsBasedHistogram=histograms.createHistogram('speedIndexScreenshotsBased',timeDurationInMs_smallerIsBetter,[],{binBoundaries:LOADING_METRIC_BOUNDARIES,description:'The average time at which visible parts of the'+' page are displayed.',summaryOptions:SUMMARY_OPTIONS,});const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);const samples=collectSpeedIndexSamplesFromLoadExpectations(model,chromeHelper);for(const sample of samples){speedIndexScreenshotsBasedHistogram.addSample(sample.value);}}
+tr.metrics.MetricRegistry.register(screenshotsBasedSpeedIndexMetric);return{screenshotsBasedSpeedIndexMetric};});'use strict';tr.exportTo('tr.metrics.sh',function(){function webviewStartupMetric(histograms,model){const startupWallHist=new tr.v.Histogram('webview_startup_wall_time',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter);startupWallHist.description='WebView startup wall time';const startupCPUHist=new tr.v.Histogram('webview_startup_cpu_time',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter);startupCPUHist.description='WebView startup CPU time';const loadWallHist=new tr.v.Histogram('webview_url_load_wall_time',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter);loadWallHist.description='WebView blank URL load wall time';const loadCPUHist=new tr.v.Histogram('webview_url_load_cpu_time',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter);loadCPUHist.description='WebView blank URL load CPU time';for(const slice of model.getDescendantEvents()){if(!(slice instanceof tr.model.ThreadSlice))continue;if(slice.title==='WebViewStartupInterval'){startupWallHist.addSample(slice.duration);startupCPUHist.addSample(slice.cpuDuration);}
+if(slice.title==='WebViewBlankUrlLoadInterval'){loadWallHist.addSample(slice.duration);loadCPUHist.addSample(slice.cpuDuration);}}
+histograms.addHistogram(startupWallHist);histograms.addHistogram(startupCPUHist);histograms.addHistogram(loadWallHist);histograms.addHistogram(loadCPUHist);}
+tr.metrics.MetricRegistry.register(webviewStartupMetric);return{webviewStartupMetric,};});'use strict';tr.exportTo('tr.metrics.tabs',function(){function tabsMetric(histograms,model,opt_options){const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);if(!chromeHelper){return;}
+const tabSwitchRequestDelays=[];const TAB_SWITCHING_REQUEST_TITLE='TabSwitchVisibilityRequest';let startTabSwitchVisibilityRequest=Number.MAX_SAFE_INTEGER;for(const helper of chromeHelper.browserHelpers){if(!helper.mainThread)continue;for(const slice of helper.mainThread.asyncSliceGroup.slices){if(slice.title===TAB_SWITCHING_REQUEST_TITLE&&!slice.error){tabSwitchRequestDelays.push(slice.duration);if(slice.start<startTabSwitchVisibilityRequest){startTabSwitchVisibilityRequest=slice.start;}}}}
+histograms.createHistogram('tab_switching_request_delay',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,tabSwitchRequestDelays,{description:'Delay before tab-request is made',summaryOptions:{sum:false}});const tabSwitchLatencies=[];const TAB_SWITCHING_SLICE_TITLE='TabSwitching::Latency';function extractLatencyFromHelpers(helpers,legacy){for(const helper of helpers){if(!helper.mainThread){continue;}
+const thread=helper.mainThread;for(const slice of thread.asyncSliceGroup.slices){if(slice.title===TAB_SWITCHING_SLICE_TITLE&&(legacy||slice.args.latency)&&slice.start>startTabSwitchVisibilityRequest){tabSwitchLatencies.push(legacy?slice.duration:slice.args.latency);}}}}
+extractLatencyFromHelpers(chromeHelper.browserHelpers);extractLatencyFromHelpers(Object.values(chromeHelper.rendererHelpers));if(tabSwitchLatencies.length===0){extractLatencyFromHelpers(chromeHelper.browserHelpers,true);}
+histograms.createHistogram('tab_switching_latency',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,tabSwitchLatencies,{description:'Tab switching time in ms',summaryOptions:{sum:false}});}
+tr.metrics.MetricRegistry.register(tabsMetric,{supportsRangeOfInterest:false,});return{tabsMetric,};});'use strict';tr.exportTo('tr.metrics',function(){const MEMORY_INFRA_TRACING_CATEGORY='disabled-by-default-memory-infra';const TIME_BOUNDARIES=tr.v.HistogramBinBoundaries.createExponential(1e-3,1e5,30);const BYTE_BOUNDARIES=tr.v.HistogramBinBoundaries.createExponential(1,1e9,30);const COUNT_BOUNDARIES=tr.v.HistogramBinBoundaries.createExponential(1,1e5,30);const SUMMARY_OPTIONS=tr.v.Histogram.AVERAGE_ONLY_SUMMARY_OPTIONS;function addMemoryInfraHistograms(histograms,model,categoryNamesToTotalEventSizes){const memoryDumpCount=model.globalMemoryDumps.length;if(memoryDumpCount===0)return;let totalOverhead=0;let nonMemoryInfraThreadOverhead=0;const overheadByProvider={};for(const process of Object.values(model.processes)){for(const thread of Object.values(process.threads)){for(const slice of Object.values(thread.sliceGroup.slices)){if(slice.category!==MEMORY_INFRA_TRACING_CATEGORY)continue;totalOverhead+=slice.duration;if(thread.name!=='MemoryInfra'){nonMemoryInfraThreadOverhead+=slice.duration;}
+if(slice.args&&slice.args['dump_provider.name']){const providerName=slice.args['dump_provider.name'];let durationAndCount=overheadByProvider[providerName];if(durationAndCount===undefined){overheadByProvider[providerName]=durationAndCount={duration:0,count:0};}
+durationAndCount.duration+=slice.duration;durationAndCount.count++;}}}}
+histograms.createHistogram('memory_dump_cpu_overhead',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,totalOverhead/memoryDumpCount,{binBoundaries:TIME_BOUNDARIES,description:'Average CPU overhead on all threads per memory-infra dump',summaryOptions:SUMMARY_OPTIONS,});histograms.createHistogram('nonmemory_thread_memory_dump_cpu_overhead',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,nonMemoryInfraThreadOverhead/memoryDumpCount,{binBoundaries:TIME_BOUNDARIES,description:'Average CPU overhead on non-memory-infra threads '+'per memory-infra dump',summaryOptions:SUMMARY_OPTIONS,});for(const[providerName,overhead]of Object.entries(overheadByProvider)){histograms.createHistogram(`${providerName}_memory_dump_cpu_overhead`,tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,overhead.duration/overhead.count,{binBoundaries:TIME_BOUNDARIES,description:`Average CPU overhead of ${providerName} per OnMemoryDump call`,summaryOptions:SUMMARY_OPTIONS,});}
+const memoryInfraEventsSize=categoryNamesToTotalEventSizes.get(MEMORY_INFRA_TRACING_CATEGORY);const memoryInfraTraceBytesValue=new tr.v.Histogram('total_memory_dump_size',tr.b.Unit.byName.sizeInBytes_smallerIsBetter,BYTE_BOUNDARIES);memoryInfraTraceBytesValue.description='Total trace size of memory-infra dumps in bytes';memoryInfraTraceBytesValue.customizeSummaryOptions(SUMMARY_OPTIONS);memoryInfraTraceBytesValue.addSample(memoryInfraEventsSize);histograms.addHistogram(memoryInfraTraceBytesValue);const traceBytesPerDumpValue=new tr.v.Histogram('memory_dump_size',tr.b.Unit.byName.sizeInBytes_smallerIsBetter,BYTE_BOUNDARIES);traceBytesPerDumpValue.description='Average trace size of memory-infra dumps in bytes';traceBytesPerDumpValue.customizeSummaryOptions(SUMMARY_OPTIONS);traceBytesPerDumpValue.addSample(memoryInfraEventsSize/memoryDumpCount);histograms.addHistogram(traceBytesPerDumpValue);}
+function tracingMetric(histograms,model){if(!model.stats.hasEventSizesinBytes)return;const eventStats=model.stats.allTraceEventStatsInTimeIntervals;eventStats.sort((a,b)=>a.timeInterval-b.timeInterval);const totalTraceBytes=eventStats.reduce((a,b)=>a+b.totalEventSizeinBytes,0);let maxEventCountPerSec=0;let maxEventBytesPerSec=0;const INTERVALS_PER_SEC=Math.floor(1000/model.stats.TIME_INTERVAL_SIZE_IN_MS);let runningEventNumPerSec=0;let runningEventBytesPerSec=0;let start=0;let end=0;while(end<eventStats.length){runningEventNumPerSec+=eventStats[end].numEvents;runningEventBytesPerSec+=eventStats[end].totalEventSizeinBytes;end++;while((eventStats[end-1].timeInterval-
+eventStats[start].timeInterval)>=INTERVALS_PER_SEC){runningEventNumPerSec-=eventStats[start].numEvents;runningEventBytesPerSec-=eventStats[start].totalEventSizeinBytes;start++;}
+maxEventCountPerSec=Math.max(maxEventCountPerSec,runningEventNumPerSec);maxEventBytesPerSec=Math.max(maxEventBytesPerSec,runningEventBytesPerSec);}
+const stats=model.stats.allTraceEventStats;const categoryNamesToTotalEventSizes=(stats.reduce((map,stat)=>(map.set(stat.category,((map.get(stat.category)||0)+
+stat.totalEventSizeinBytes))),new Map()));const maxCatNameAndBytes=Array.from(categoryNamesToTotalEventSizes.entries()).reduce((a,b)=>((b[1]>=a[1])?b:a));const maxEventBytesPerCategory=maxCatNameAndBytes[1];const categoryWithMaxEventBytes=maxCatNameAndBytes[0];const maxEventCountPerSecValue=new tr.v.Histogram('peak_event_rate',tr.b.Unit.byName.count_smallerIsBetter,COUNT_BOUNDARIES);maxEventCountPerSecValue.description='Max number of events per second';maxEventCountPerSecValue.customizeSummaryOptions(SUMMARY_OPTIONS);maxEventCountPerSecValue.addSample(maxEventCountPerSec);const maxEventBytesPerSecValue=new tr.v.Histogram('peak_event_size_rate',tr.b.Unit.byName.sizeInBytes_smallerIsBetter,BYTE_BOUNDARIES);maxEventBytesPerSecValue.description='Max event size in bytes per second';maxEventBytesPerSecValue.customizeSummaryOptions(SUMMARY_OPTIONS);maxEventBytesPerSecValue.addSample(maxEventBytesPerSec);const totalTraceBytesValue=new tr.v.Histogram('trace_size',tr.b.Unit.byName.sizeInBytes_smallerIsBetter,BYTE_BOUNDARIES);totalTraceBytesValue.customizeSummaryOptions(SUMMARY_OPTIONS);totalTraceBytesValue.addSample(totalTraceBytes);const biggestCategory={name:categoryWithMaxEventBytes,size_in_bytes:maxEventBytesPerCategory};totalTraceBytesValue.diagnostics.set('category_with_max_event_size',new tr.v.d.GenericSet([biggestCategory]));histograms.addHistogram(totalTraceBytesValue);maxEventCountPerSecValue.diagnostics.set('category_with_max_event_size',new tr.v.d.GenericSet([biggestCategory]));histograms.addHistogram(maxEventCountPerSecValue);maxEventBytesPerSecValue.diagnostics.set('category_with_max_event_size',new tr.v.d.GenericSet([biggestCategory]));histograms.addHistogram(maxEventBytesPerSecValue);addMemoryInfraHistograms(histograms,model,categoryNamesToTotalEventSizes);}
+tr.metrics.MetricRegistry.register(tracingMetric);return{tracingMetric,MEMORY_INFRA_TRACING_CATEGORY,};});'use strict';tr.exportTo('tr.metrics',function(){function parseBuckets_(event,processName){const len=tr.b.Base64.getDecodedBufferLength(event.args.buckets);const buffer=new ArrayBuffer(len);const dataView=new DataView(buffer);tr.b.Base64.DecodeToTypedArray(event.args.buckets,dataView);const decoded=new Uint32Array(buffer);const sum=decoded[1]+decoded[2]*0x100000000;const bins=[];let position=4;while(position<=decoded.length-4){const min=decoded[position++];const max=decoded[position++]+decoded[position++]*0x100000000;const count=decoded[position++];const processes=new tr.v.d.Breakdown();processes.set(processName,count);const events=new tr.v.d.RelatedEventSet([event]);bins.push({min,max,count,processes,events});}
+return{sum,bins};}
+function mergeBins_(x,y){x.sum+=y.sum;const allBins=[...x.bins,...y.bins];allBins.sort((a,b)=>a.min-b.min);x.bins=[];let last=undefined;for(const bin of allBins){if(last!==undefined&&bin.min===last.min){if(last.max!==bin.max)throw new Error('Incompatible bins');if(bin.count===0)continue;last.count+=bin.count;for(const event of bin.events){last.events.add(event);}
+last.processes.addDiagnostic(bin.processes);}else{if(last!==undefined&&bin.min<last.max){throw new Error('Incompatible bins');}
+x.bins.push(bin);last=bin;}}}
+function subtractBins_(x,y){x.sum-=y.sum;let p1=0;let p2=0;while(p2<y.bins.length){while(p1<x.bins.length&&x.bins[p1].min!==y.bins[p2].min){p1++;}
+if(p1===x.bins.length)throw new Error('Cannot subtract');if(x.bins[p1].max!==y.bins[p2].max){throw new Error('Incompatible bins');}
+if(x.bins[p1].count<y.bins[p2].count){throw new Error('Cannot subtract');}
+x.bins[p1].count-=y.bins[p2].count;for(const event of y.bins[p2].events){x.bins[p1].events.add(event);}
+const processName=tr.b.getOnlyElement(x.bins[p1].processes)[0];x.bins[p1].processes.set(processName,x.bins[p1].count);p2++;}}
+function getHistogramUnit_(name){return tr.b.Unit.byName.unitlessNumber_smallerIsBetter;}
+function getHistogramBoundaries_(name){if(name.startsWith('Event.Latency.Scroll')){return tr.v.HistogramBinBoundaries.createExponential(1e3,1e5,50);}
+if(name.startsWith('Graphics.Smoothness.Throughput')){return tr.v.HistogramBinBoundaries.createLinear(0,100,101);}
+if(name.startsWith('Memory.Memory.GPU.PeakMemoryUsage')){return tr.v.HistogramBinBoundaries.createLinear(0,1e6,100);}
+return tr.v.HistogramBinBoundaries.createExponential(1e-3,1e3,50);}
+function umaMetric(histograms,model){const histogramValues=new Map();const nameCounts=new Map();for(const process of model.getAllProcesses()){const histogramEvents=new Map();for(const event of process.instantEvents){if(event.title!=='UMAHistogramSamples')continue;const name=event.args.name;const events=histogramEvents.get(name)||[];if(!histogramEvents.has(name))histogramEvents.set(name,events);events.push(event);}
+let processName=tr.e.chrome.chrome_processes.canonicalizeProcessName(process.name);nameCounts.set(processName,(nameCounts.get(processName)||0)+1);processName=`${processName}_${nameCounts.get(processName)}`;for(const[name,events]of histogramEvents){const values=histogramValues.get(name)||{sum:0,bins:[]};if(!histogramValues.has(name))histogramValues.set(name,values);const endValues=parseBuckets_(events[events.length-1],processName);if(events.length===1){mergeBins_(values,endValues);}else if(events.length===2){subtractBins_(endValues,parseBuckets_(events[0],processName));mergeBins_(values,endValues);}else{throw new Error('There should be at most two snapshots of an UMA '+'histogram in each process');}}}
+for(const[name,values]of histogramValues){const histogram=new tr.v.Histogram(name,getHistogramUnit_(name),getHistogramBoundaries_(name));let sumOfMiddles=0;let sumOfBinLengths=0;for(const bin of values.bins){sumOfMiddles+=bin.count*(bin.min+bin.max)/2;sumOfBinLengths+=bin.count*(bin.max-bin.min);}
+const shift=(values.sum-sumOfMiddles)/sumOfBinLengths;if(Math.abs(shift)>0.5)throw new Error('Samples sum is wrong');for(const bin of values.bins){if(bin.count===0)continue;const shiftedValue=(bin.min+bin.max)/2+shift*(bin.max-bin.min);for(const[processName,count]of bin.processes){bin.processes.set(processName,shiftedValue*count/bin.count);}
+for(let i=0;i<bin.count;i++){histogram.addSample(shiftedValue,{processes:bin.processes,events:bin.events});}}
+histograms.addHistogram(histogram);}}
+tr.metrics.MetricRegistry.register(umaMetric,{requiredCategories:['benchmark'],});return{umaMetric,};});'use strict';tr.exportTo('tr.metrics.v8',function(){const CUSTOM_BOUNDARIES=tr.v.HistogramBinBoundaries.createLinear(4,200,100);function computeExecuteMetrics(histograms,model){const cpuTotalExecution=new tr.v.Histogram('v8_execution_cpu_total',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);cpuTotalExecution.description='cpu total time spent in script execution';const wallTotalExecution=new tr.v.Histogram('v8_execution_wall_total',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);wallTotalExecution.description='wall total time spent in script execution';const cpuSelfExecution=new tr.v.Histogram('v8_execution_cpu_self',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);cpuSelfExecution.description='cpu self time spent in script execution';const wallSelfExecution=new tr.v.Histogram('v8_execution_wall_self',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);wallSelfExecution.description='wall self time spent in script execution';for(const e of model.findTopmostSlicesNamed('V8.Execute')){cpuTotalExecution.addSample(e.cpuDuration);wallTotalExecution.addSample(e.duration);cpuSelfExecution.addSample(e.cpuSelfTime);wallSelfExecution.addSample(e.selfTime);}
+histograms.addHistogram(cpuTotalExecution);histograms.addHistogram(wallTotalExecution);histograms.addHistogram(cpuSelfExecution);histograms.addHistogram(wallSelfExecution);}
+function computeParseLazyMetrics(histograms,model){const cpuSelfParseLazy=new tr.v.Histogram('v8_parse_lazy_cpu_self',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);cpuSelfParseLazy.description='cpu self time spent performing lazy parsing';const wallSelfParseLazy=new tr.v.Histogram('v8_parse_lazy_wall_self',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);wallSelfParseLazy.description='wall self time spent performing lazy parsing';for(const e of model.findTopmostSlicesNamed('V8.ParseLazyMicroSeconds')){cpuSelfParseLazy.addSample(e.cpuSelfTime);wallSelfParseLazy.addSample(e.selfTime);}
+for(const e of model.findTopmostSlicesNamed('V8.ParseLazy')){cpuSelfParseLazy.addSample(e.cpuSelfTime);wallSelfParseLazy.addSample(e.selfTime);}
+histograms.addHistogram(cpuSelfParseLazy);histograms.addHistogram(wallSelfParseLazy);}
+function computeCompileFullCodeMetrics(histograms,model){const cpuSelfCompileFullCode=new tr.v.Histogram('v8_compile_full_code_cpu_self',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);cpuSelfCompileFullCode.description='cpu self time spent performing compiling full code';const wallSelfCompileFullCode=new tr.v.Histogram('v8_compile_full_code_wall_self',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);wallSelfCompileFullCode.description='wall self time spent performing compiling full code';for(const e of model.findTopmostSlicesNamed('V8.CompileFullCode')){cpuSelfCompileFullCode.addSample(e.cpuSelfTime);wallSelfCompileFullCode.addSample(e.selfTime);}
+histograms.addHistogram(cpuSelfCompileFullCode);histograms.addHistogram(wallSelfCompileFullCode);}
+function computeCompileIgnitionMetrics(histograms,model){const cpuSelfCompileIgnition=new tr.v.Histogram('v8_compile_ignition_cpu_self',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);cpuSelfCompileIgnition.description='cpu self time spent in compile ignition';const wallSelfCompileIgnition=new tr.v.Histogram('v8_compile_ignition_wall_self',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);wallSelfCompileIgnition.description='wall self time spent in compile ignition';for(const e of model.findTopmostSlicesNamed('V8.CompileIgnition')){cpuSelfCompileIgnition.addSample(e.cpuSelfTime);wallSelfCompileIgnition.addSample(e.selfTime);}
+histograms.addHistogram(cpuSelfCompileIgnition);histograms.addHistogram(wallSelfCompileIgnition);}
+function computeRecompileMetrics(histograms,model){const cpuTotalRecompileSynchronous=new tr.v.Histogram('v8_recompile_synchronous_cpu_total',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);cpuTotalRecompileSynchronous.description='cpu total time spent in synchronous recompilation';const wallTotalRecompileSynchronous=new tr.v.Histogram('v8_recompile_synchronous_wall_total',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);wallTotalRecompileSynchronous.description='wall total time spent in synchronous recompilation';const cpuTotalRecompileConcurrent=new tr.v.Histogram('v8_recompile_concurrent_cpu_total',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);cpuTotalRecompileConcurrent.description='cpu total time spent in concurrent recompilation';const wallTotalRecompileConcurrent=new tr.v.Histogram('v8_recompile_concurrent_wall_total',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);wallTotalRecompileConcurrent.description='wall total time spent in concurrent recompilation';const cpuTotalRecompileOverall=new tr.v.Histogram('v8_recompile_overall_cpu_total',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);cpuTotalRecompileOverall.description='cpu total time spent in synchronous or concurrent recompilation';const wallTotalRecompileOverall=new tr.v.Histogram('v8_recompile_overall_wall_total',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);wallTotalRecompileOverall.description='wall total time spent in synchronous or concurrent recompilation';for(const e of model.findTopmostSlicesNamed('V8.RecompileSynchronous')){cpuTotalRecompileSynchronous.addSample(e.cpuDuration);wallTotalRecompileSynchronous.addSample(e.duration);cpuTotalRecompileOverall.addSample(e.cpuDuration);wallTotalRecompileOverall.addSample(e.duration);}
+histograms.addHistogram(cpuTotalRecompileSynchronous);histograms.addHistogram(wallTotalRecompileSynchronous);for(const e of model.findTopmostSlicesNamed('V8.RecompileConcurrent')){cpuTotalRecompileConcurrent.addSample(e.cpuDuration);wallTotalRecompileConcurrent.addSample(e.duration);cpuTotalRecompileOverall.addSample(e.cpuDuration);wallTotalRecompileOverall.addSample(e.duration);}
+histograms.addHistogram(cpuTotalRecompileConcurrent);histograms.addHistogram(wallTotalRecompileConcurrent);histograms.addHistogram(cpuTotalRecompileOverall);histograms.addHistogram(wallTotalRecompileOverall);}
+function computeOptimizeCodeMetrics(histograms,model){const cpuTotalOptimizeCode=new tr.v.Histogram('v8_optimize_code_cpu_total',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);cpuTotalOptimizeCode.description='cpu total time spent in code optimization';const wallTotalOptimizeCode=new tr.v.Histogram('v8_optimize_code_wall_total',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);wallTotalOptimizeCode.description='wall total time spent in code optimization';for(const e of model.findTopmostSlicesNamed('V8.OptimizeCode')){cpuTotalOptimizeCode.addSample(e.cpuDuration);wallTotalOptimizeCode.addSample(e.duration);}
+histograms.addHistogram(cpuTotalOptimizeCode);histograms.addHistogram(wallTotalOptimizeCode);}
+function computeDeoptimizeCodeMetrics(histograms,model){const cpuTotalDeoptimizeCode=new tr.v.Histogram('v8_deoptimize_code_cpu_total',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);cpuTotalDeoptimizeCode.description='cpu total time spent in code deoptimization';const wallTotalDeoptimizeCode=new tr.v.Histogram('v8_deoptimize_code_wall_total',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);wallTotalDeoptimizeCode.description='wall total time spent in code deoptimization';for(const e of model.findTopmostSlicesNamed('V8.DeoptimizeCode')){cpuTotalDeoptimizeCode.addSample(e.cpuDuration);wallTotalDeoptimizeCode.addSample(e.duration);}
+histograms.addHistogram(cpuTotalDeoptimizeCode);histograms.addHistogram(wallTotalDeoptimizeCode);}
+function executionMetric(histograms,model){computeExecuteMetrics(histograms,model);computeParseLazyMetrics(histograms,model);computeCompileIgnitionMetrics(histograms,model);computeCompileFullCodeMetrics(histograms,model);computeRecompileMetrics(histograms,model);computeOptimizeCodeMetrics(histograms,model);computeDeoptimizeCodeMetrics(histograms,model);}
+tr.metrics.MetricRegistry.register(executionMetric);return{executionMetric,};});'use strict';tr.exportTo('tr.metrics.v8',function(){const TARGET_FPS=60;const MS_PER_SECOND=1000;const WINDOW_SIZE_MS=MS_PER_SECOND/TARGET_FPS;function gcMetric(histograms,model,options){options=options||{};addDurationOfTopEvents(histograms,model);addTotalDurationOfTopEvents(histograms,model);if(options.include_sub_events){addDurationOfSubEvents(histograms,model);}
+addPercentageInV8ExecuteOfTopEvents(histograms,model);addTotalPercentageInV8Execute(histograms,model);addMarkCompactorMutatorUtilization(histograms,model);addTotalMarkCompactorTime(histograms,model);addTotalMarkCompactorMarkingTime(histograms,model);}
+tr.metrics.MetricRegistry.register(gcMetric);const timeDurationInMs_smallerIsBetter=tr.b.Unit.byName.timeDurationInMs_smallerIsBetter;const percentage_biggerIsBetter=tr.b.Unit.byName.normalizedPercentage_biggerIsBetter;const percentage_smallerIsBetter=tr.b.Unit.byName.normalizedPercentage_smallerIsBetter;const CUSTOM_BOUNDARIES=tr.v.HistogramBinBoundaries.createLinear(0,20,200).addExponentialBins(200,100);function createNumericForTopEventTime(name){const n=new tr.v.Histogram(name,timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);n.customizeSummaryOptions({avg:true,count:true,max:true,min:false,std:true,sum:true,percentile:[0.90]});return n;}
+function createNumericForSubEventTime(name){const n=new tr.v.Histogram(name,timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);n.customizeSummaryOptions({avg:true,count:false,max:true,min:false,std:false,sum:false,percentile:[0.90]});return n;}
+function createNumericForIdleTime(name){const n=new tr.v.Histogram(name,timeDurationInMs_smallerIsBetter,CUSTOM_BOUNDARIES);n.customizeSummaryOptions({avg:true,count:false,max:true,min:false,std:false,sum:true,percentile:[]});return n;}
+function createPercentage(name,numerator,denominator,unit){const hist=new tr.v.Histogram(name,unit);if(denominator===0){hist.addSample(0);}else{hist.addSample(numerator/denominator);}
+hist.customizeSummaryOptions({avg:true,count:false,max:false,min:false,std:false,sum:false,percentile:[]});return hist;}
+function addDurationOfTopEvents(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,tr.metrics.v8.utils.isNotForcedTopGarbageCollectionEvent,tr.metrics.v8.utils.topGarbageCollectionEventName,function(name,events){const cpuDuration=createNumericForTopEventTime(name);events.forEach(function(event){cpuDuration.addSample(event.cpuDuration);});histograms.addHistogram(cpuDuration);},tr.metrics.v8.utils.topGarbageCollectionEventNames());}
+function addTotalDurationOfTopEvents(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,tr.metrics.v8.utils.isNotForcedTopGarbageCollectionEvent,event=>'v8-gc-total',function(name,events){const cpuDuration=createNumericForTopEventTime(name);events.forEach(function(event){cpuDuration.addSample(event.cpuDuration);});histograms.addHistogram(cpuDuration);},['v8-gc-total']);}
+function isV8MarkCompactorSummary(event){return!tr.metrics.v8.utils.isForcedGarbageCollectionEvent(event)&&tr.metrics.v8.utils.isMarkCompactorSummaryEvent(event);}
+function isV8MarkCompactorMarkingSummary(event){return!tr.metrics.v8.utils.isForcedGarbageCollectionEvent(event)&&tr.metrics.v8.utils.isMarkCompactorMarkingSummaryEvent(event);}
+function createHistogramFromSummary(histograms,name,events){const foregroundDuration=createNumericForTopEventTime(name+'-foreground');const backgroundDuration=createNumericForTopEventTime(name+'-background');const totalDuration=createNumericForTopEventTime(name+'-total');const relatedNames=new tr.v.d.RelatedNameMap();relatedNames.set('foreground',foregroundDuration.name);relatedNames.set('background',backgroundDuration.name);for(const event of events){foregroundDuration.addSample(event.args.duration);backgroundDuration.addSample(event.args.background_duration);const breakdownForTotal=new tr.v.d.Breakdown();breakdownForTotal.set('foreground',event.args.duration);breakdownForTotal.set('background',event.args.background_duration);totalDuration.addSample(event.args.duration+event.args.background_duration,{breakdown:breakdownForTotal});}
+histograms.addHistogram(foregroundDuration);histograms.addHistogram(backgroundDuration);histograms.addHistogram(totalDuration,{breakdown:relatedNames});}
+function addTotalMarkCompactorTime(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isV8MarkCompactorSummary,event=>'v8-gc-mark-compactor',(name,events)=>createHistogramFromSummary(histograms,name,events),['v8-gc-mark-compactor']);}
+function addTotalMarkCompactorMarkingTime(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,isV8MarkCompactorMarkingSummary,event=>'v8-gc-mark-compactor-marking',(name,events)=>createHistogramFromSummary(histograms,name,events),['v8-gc-mark-compactor-marking']);}
+function addDurationOfSubEvents(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,tr.metrics.v8.utils.isNotForcedSubGarbageCollectionEvent,tr.metrics.v8.utils.subGarbageCollectionEventName,function(name,events){const cpuDuration=createNumericForSubEventTime(name);events.forEach(function(event){cpuDuration.addSample(event.cpuDuration);});histograms.addHistogram(cpuDuration);});}
+function addPercentageInV8ExecuteOfTopEvents(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,tr.metrics.v8.utils.isNotForcedTopGarbageCollectionEvent,tr.metrics.v8.utils.topGarbageCollectionEventName,function(name,events){addPercentageInV8Execute(histograms,model,name,events);},tr.metrics.v8.utils.topGarbageCollectionEventNames());}
+function addTotalPercentageInV8Execute(histograms,model){tr.metrics.v8.utils.groupAndProcessEvents(model,tr.metrics.v8.utils.isNotForcedTopGarbageCollectionEvent,event=>'v8-gc-total',function(name,events){addPercentageInV8Execute(histograms,model,name,events);},['v8-gc-total']);}
+function addPercentageInV8Execute(histograms,model,name,events){let cpuDurationInV8Execute=0;let cpuDurationTotal=0;events.forEach(function(event){const v8Execute=tr.metrics.v8.utils.findParent(event,tr.metrics.v8.utils.isV8ExecuteEvent);if(v8Execute){cpuDurationInV8Execute+=event.cpuDuration;}
+cpuDurationTotal+=event.cpuDuration;});const percentage=createPercentage(name+'_percentage_in_v8_execute',cpuDurationInV8Execute,cpuDurationTotal,percentage_smallerIsBetter);histograms.addHistogram(percentage);}
+function addMarkCompactorMutatorUtilization(histograms,model){const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);const rendererHelpers=Object.values(chromeHelper.rendererHelpers);tr.metrics.v8.utils.addMutatorUtilization('v8-gc-mark-compactor-mmu',tr.metrics.v8.utils.isNotForcedMarkCompactorEvent,[100],rendererHelpers,histograms);}
+return{gcMetric,WINDOW_SIZE_MS,};});'use strict';tr.exportTo('tr.metrics.v8',function(){const COUNT_CUSTOM_BOUNDARIES=tr.v.HistogramBinBoundaries.createExponential(1,1000000,50);const DURATION_CUSTOM_BOUNDARIES=tr.v.HistogramBinBoundaries.createExponential(0.1,10000,50);const SUMMARY_OPTIONS={std:false,count:false,sum:false,min:false,max:false,};function convertMicroToMilli_(time){return tr.b.convertUnit(time,tr.b.UnitPrefixScale.METRIC.MICRO,tr.b.UnitPrefixScale.METRIC.MILLI);}
+function addDurationHistogram(histogramName,time,histograms){const value=convertMicroToMilli_(time);histograms.createHistogram(`${histogramName}:duration`,tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,{value},{binBoundaries:DURATION_CUSTOM_BOUNDARIES,summaryOptions:SUMMARY_OPTIONS,});}
+function addCountHistogram(histogramName,value,histograms){histograms.createHistogram(`${histogramName}:count`,tr.b.Unit.byName.count_smallerIsBetter,{value},{binBoundaries:COUNT_CUSTOM_BOUNDARIES,summaryOptions:SUMMARY_OPTIONS});}
+function runtimeStatsTotalMetric(histograms,model){const v8Slices=tr.metrics.v8.utils.filterEvents(model,ev=>ev instanceof tr.e.v8.V8ThreadSlice);const runtimeGroupCollection=new tr.e.v8.RuntimeStatsGroupCollection();runtimeGroupCollection.addSlices(v8Slices);let overallV8Time=runtimeGroupCollection.totalTime;let overallV8Count=runtimeGroupCollection.totalCount;let mainThreadTime=runtimeGroupCollection.totalTime;let mainThreadCount=runtimeGroupCollection.totalCount;let mainThreadV8Time=runtimeGroupCollection.totalTime;let mainThreadV8Count=runtimeGroupCollection.totalCount;for(const runtimeGroup of runtimeGroupCollection.runtimeGroups){addDurationHistogram(runtimeGroup.name,runtimeGroup.time,histograms);if(runtimeGroup.name==='Blink C++'){overallV8Time-=runtimeGroup.time;mainThreadV8Time-=runtimeGroup.time;}else if(runtimeGroup.name.includes('Background')){mainThreadTime-=runtimeGroup.time;mainThreadV8Time-=runtimeGroup.time;}
+addCountHistogram(runtimeGroup.name,runtimeGroup.count,histograms);if(runtimeGroup.name==='Blink C++'){overallV8Count-=runtimeGroup.count;mainThreadV8Count-=runtimeGroup.count;}else if(runtimeGroup.name.includes('Background')){mainThreadCount-=runtimeGroup.count;mainThreadV8Count-=runtimeGroup.count;}}
+if(runtimeGroupCollection.blinkRCSGroupCollection.totalTime>0){const blinkRCSGroupCollection=runtimeGroupCollection.blinkRCSGroupCollection;for(const group of blinkRCSGroupCollection.runtimeGroups){addDurationHistogram(group.name,group.time,histograms);addCountHistogram(group.name,group.count,histograms);}}
+addDurationHistogram('V8-Only',overallV8Time,histograms);addCountHistogram('V8-Only',overallV8Count,histograms);addDurationHistogram('Total-Main-Thread',mainThreadTime,histograms);addCountHistogram('Total-Main-Thread',mainThreadCount,histograms);addDurationHistogram('V8-Only-Main-Thread',mainThreadV8Time,histograms);addCountHistogram('V8-Only-Main-Thread',mainThreadV8Count,histograms);}
+tr.metrics.MetricRegistry.register(runtimeStatsTotalMetric);return{runtimeStatsTotalMetric,};});'use strict';tr.exportTo('tr.metrics.v8',function(){function v8AndMemoryMetrics(histograms,model){tr.metrics.v8.executionMetric(histograms,model);tr.metrics.v8.gcMetric(histograms,model);tr.metrics.sh.memoryMetric(histograms,model,{rangeOfInterest:tr.metrics.v8.utils.rangeForMemoryDumps(model)});}
+tr.metrics.MetricRegistry.register(v8AndMemoryMetrics);return{v8AndMemoryMetrics,};});'use strict';tr.exportTo('tr.metrics.vr',function(){const VR_GL_THREAD_NAME='VrShellGL';function createHistograms(histograms,name,options,hasCpuTime){const createdHistograms={wall:histograms.createHistogram(name+'_wall',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,[],options)};if(hasCpuTime){createdHistograms.cpu=histograms.createHistogram(name+'_cpu',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,[],options);}
+return createdHistograms;}
+function frameCycleDurationMetric(histograms,model,opt_options){const histogramsByEventTitle=new Map();const expectationEvents=tr.importer.VR_EXPECTATION_EVENTS;for(const eventName in expectationEvents){const extraInfo=expectationEvents[eventName];histogramsByEventTitle.set(eventName,createHistograms(histograms,extraInfo.histogramName,{description:extraInfo.description},extraInfo.hasCpuTime));}
+histogramsByEventTitle.set('UiScene::OnBeginFrame.UpdateAnimationsAndOpacity',createHistograms(histograms,'update_animations_and_opacity',{description:'Duration to apply animation and opacity changes'},true));histogramsByEventTitle.set('UiScene::OnBeginFrame.UpdateBindings',createHistograms(histograms,'update_bindings',{description:'Duration to push binding values'},true));histogramsByEventTitle.set('UiScene::OnBeginFrame.UpdateLayout',createHistograms(histograms,'update_layout',{description:'Duration to compute element sizes, layout and textures'},true));histogramsByEventTitle.set('UiScene::OnBeginFrame.UpdateWorldSpaceTransform',createHistograms(histograms,'update_world_space_transforms',{description:'Duration to calculate element transforms in world space'},true));histogramsByEventTitle.set('UiRenderer::DrawUiView',createHistograms(histograms,'draw_ui',{description:'Duration to draw the UI'},true));histogramsByEventTitle.set('UiElementRenderer::DrawTexturedQuad',createHistograms(histograms,'draw_textured_quad',{description:'Duration to draw a textured element'},true));histogramsByEventTitle.set('UiElementRenderer::DrawGradientQuad',createHistograms(histograms,'draw_gradient_quad',{description:'Duration to draw a gradient element'},true));histogramsByEventTitle.set('UiElementRenderer::DrawGradientGridQuad',createHistograms(histograms,'draw_gradient_grid_quad',{description:'Duration to draw a gradient grid element'},true));histogramsByEventTitle.set('UiElementRenderer::DrawController',createHistograms(histograms,'draw_controller',{description:'Duration to draw the controller'},true));histogramsByEventTitle.set('UiElementRenderer::DrawLaser',createHistograms(histograms,'draw_laser',{description:'Duration to draw the laser'},true));histogramsByEventTitle.set('UiElementRenderer::DrawReticle',createHistograms(histograms,'draw_reticle',{description:'Duration to draw the reticle'},true));histogramsByEventTitle.set('UiElementRenderer::DrawShadow',createHistograms(histograms,'draw_shadow',{description:'Duration to draw a shadow element'},true));histogramsByEventTitle.set('UiElementRenderer::DrawStars',createHistograms(histograms,'draw_stars',{description:'Duration to draw the stars'},true));histogramsByEventTitle.set('UiElementRenderer::DrawBackground',createHistograms(histograms,'draw_background',{description:'Duration to draw the textured background'},true));histogramsByEventTitle.set('UiElementRenderer::DrawKeyboard',createHistograms(histograms,'draw_keyboard',{description:'Duration to draw the keyboard'},true));const drawUiSubSlicesMap=new Map();const chromeHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);let rangeOfInterest=model.bounds;const userExpectationsOfInterest=[tr.model.um.AnimationExpectation];if(opt_options&&opt_options.rangeOfInterest){rangeOfInterest=opt_options.rangeOfInterest;userExpectationsOfInterest.push(tr.model.um.ResponseExpectation);}
+for(const ue of model.userModel.expectations){if(ue.initiatorType!==tr.model.um.INITIATOR_TYPE.VR){continue;}
+if(!userExpectationsOfInterest.some(function(ueOfInterest){return ue instanceof ueOfInterest;})){continue;}
+if(!rangeOfInterest.intersectsExplicitRangeInclusive(ue.start,ue.end)){continue;}
+for(const helper of chromeHelper.browserHelpers){const glThreads=helper.process.findAllThreadsNamed(VR_GL_THREAD_NAME);for(const glThread of glThreads){for(const event of glThread.getDescendantEvents()){if(!(histogramsByEventTitle.has(event.title))){continue;}
+if(event.start<ue.start||event.end>ue.end){continue;}
+if(event.start<rangeOfInterest.min||event.end>rangeOfInterest.max){continue;}
+if(event.parentSlice&&event.parentSlice.title==='UiRenderer::DrawUiView'){const guid=event.parentSlice.guid;if(!drawUiSubSlicesMap.has(guid)){drawUiSubSlicesMap.set(guid,[]);}
+drawUiSubSlicesMap.get(guid).push(event);continue;}
+const{wall:wallHist,cpu:cpuHist}=histogramsByEventTitle.get(event.title);wallHist.addSample(event.duration);if(cpuHist!==undefined){cpuHist.addSample(event.cpuDuration);}}}}}
+for(const subSlices of drawUiSubSlicesMap.values()){const eventMap=new Map();for(const event of subSlices){if(!eventMap.has(event.title)){eventMap.set(event.title,{wall:0,cpu:0});}
+eventMap.get(event.title).wall+=event.duration;eventMap.get(event.title).cpu+=event.cpuDuration;}
+for(const[title,values]of eventMap.entries()){const{wall:wallHist,cpu:cpuHist}=histogramsByEventTitle.get(title);wallHist.addSample(values.wall);if(cpuHist!==undefined){cpuHist.addSample(values.cpu);}}}}
+tr.metrics.MetricRegistry.register(frameCycleDurationMetric,{supportsRangeOfInterest:true,});return{frameCycleDurationMetric,};});'use strict';tr.exportTo('tr.metrics.vr',function(){function webvrMetric(histograms,model,opt_options){const WEBVR_COUNTERS=new Map([['gpu.WebVR FPS',{name:'webvr_fps',unit:tr.b.Unit.byName.count_biggerIsBetter,samples:{},options:{description:'WebVR frame per second',binBoundaries:tr.v.HistogramBinBoundaries.createLinear(20,120,25),},}],['gpu.WebVR frame time (ms)',{name:'webvr_frame_time',unit:tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,samples:{},options:{description:'WebVR frame time in ms',binBoundaries:tr.v.HistogramBinBoundaries.createLinear(20,120,25),},}],['gpu.WebVR pose prediction (ms)',{name:'webvr_pose_prediction',unit:tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,samples:{},options:{description:'WebVR pose prediction in ms',binBoundaries:tr.v.HistogramBinBoundaries.createLinear(20,120,25),},}],]);for(const ue of model.userModel.expectations){const rangeOfInterestEnabled=opt_options&&opt_options.rangeOfInterest;if(rangeOfInterestEnabled&&!opt_options.rangeOfInterest.intersectsExplicitRangeInclusive(ue.start,ue.end)){continue;}
+if(ue.initiatorType!==tr.model.um.INITIATOR_TYPE.VR)continue;if(!rangeOfInterestEnabled){if(!(ue instanceof tr.model.um.AnimationExpectation))continue;}else{if(!(ue instanceof tr.model.um.AnimationExpectation||ue instanceof tr.model.um.ResponseExpectation))continue;}
+for(const counter of model.getAllCounters()){if(!(WEBVR_COUNTERS.has(counter.id)))continue;for(const series of counter.series){if(!(series.name in WEBVR_COUNTERS.get(counter.id).samples)){WEBVR_COUNTERS.get(counter.id).samples[series.name]=[];}
+for(const sample of series.samples){if(sample.timestamp<ue.start||sample.timestamp>=ue.end){continue;}
+if(rangeOfInterestEnabled&&!opt_options.rangeOfInterest.intersectsExplicitRangeInclusive(sample.timestamp,sample.timestamp)){continue;}
+WEBVR_COUNTERS.get(counter.id).samples[series.name].push(sample.value);}}}}
+if(!('value'in WEBVR_COUNTERS.get('gpu.WebVR FPS').samples)){WEBVR_COUNTERS.get('gpu.WebVR FPS').samples.value=[0];}
+for(const[key,value]of WEBVR_COUNTERS){for(const[seriesName,samples]of Object.entries(value.samples)){let histogramName=value.name;if(seriesName!=='value'){histogramName=`${histogramName}_${seriesName}`;}
+histograms.createHistogram(histogramName,value.unit,samples,value.options);}}}
+tr.metrics.MetricRegistry.register(webvrMetric,{supportsRangeOfInterest:true,});return{webvrMetric,};});'use strict';tr.exportTo('tr.metrics.vr',function(){function webxrMetric(histograms,model,opt_options){const DEFAULT_BIN_BOUNDARIES=tr.v.HistogramBinBoundaries.createLinear(20,120,25);const counterHistogramsByTitle=new Map();counterHistogramsByTitle.set('gpu.WebXR FPS',histograms.createHistogram('webxr_fps',tr.b.Unit.byName.count_biggerIsBetter,[],{description:'WebXR frames per second',binBoundaries:DEFAULT_BIN_BOUNDARIES,}));const instantHistogramsByTitle=new Map();const expectationEvents=tr.importer.WEBXR_INSTANT_EVENTS;for(const[eventName,eventData]of Object.entries(expectationEvents)){const argsToHistograms={};for(const[argName,argData]of Object.entries(eventData)){argsToHistograms[argName]=histograms.createHistogram(argData.histogramName,tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,[],{description:argData.description,binBoundaries:DEFAULT_BIN_BOUNDARIES,});}
+instantHistogramsByTitle.set(eventName,argsToHistograms);}
+const rangeOfInterestEnabled=opt_options&&opt_options.rangeOfInterest;const rangeOfInterest=(rangeOfInterestEnabled?opt_options.rangeOfInterest:tr.b.math.Range.fromExplicitRange(-Infinity,Infinity));for(const ue of model.userModel.expectations){if(!rangeOfInterest.intersectsExplicitRangeInclusive(ue.start,ue.end)){continue;}
+if(ue.initiatorType!==tr.model.um.INITIATOR_TYPE.VR)continue;if(!rangeOfInterestEnabled){if(!(ue instanceof tr.model.um.AnimationExpectation))continue;}else{if(!(ue instanceof tr.model.um.AnimationExpectation||ue instanceof tr.model.um.ResponseExpectation))continue;}
+for(const counter of model.getAllCounters()){if(!(counterHistogramsByTitle.has(counter.id)))continue;for(const series of counter.series){for(const sample of series.samples){if(sample.timestamp<ue.start||sample.timestamp>=ue.end){continue;}
+if(!rangeOfInterest.intersectsExplicitRangeInclusive(sample.timestamp,sample.timestamp)){continue;}
+counterHistogramsByTitle.get(counter.id).addSample(sample.value);}}}
+for(const event of ue.associatedEvents.asSet()){if(!(instantHistogramsByTitle.has(event.title))){continue;}
+if(!rangeOfInterest.intersectsExplicitRangeInclusive(event.start,event.start)){continue;}
+const eventHistograms=instantHistogramsByTitle.get(event.title);for(const[key,value]of Object.entries(event.args)){if(key in eventHistograms){eventHistograms[key].addSample(value,{event:new tr.v.d.RelatedEventSet(event)});}}}}
+if(counterHistogramsByTitle.get('gpu.WebXR FPS').numValues===0){counterHistogramsByTitle.get('gpu.WebXR FPS').addSample(0);}}
+tr.metrics.MetricRegistry.register(webxrMetric,{supportsRangeOfInterest:true,});return{webxrMetric,};});'use strict';tr.exportTo('tr.metrics.webrtc',function(){const DISPLAY_HERTZ=60.0;const VSYNC_DURATION_US=1e6/DISPLAY_HERTZ;const SEVERITY=3;const FROZEN_FRAME_VSYNC_COUNT_THRESHOLD=6;const WEB_MEDIA_PLAYER_UPDATE_TITLE='UpdateCurrentFrame';const IDEAL_RENDER_INSTANT_NAME='Ideal Render Instant';const ACTUAL_RENDER_BEGIN_NAME='Actual Render Begin';const ACTUAL_RENDER_END_NAME='Actual Render End';const STREAM_ID_NAME='Serial';const REQUIRED_EVENT_ARGS_NAMES=[IDEAL_RENDER_INSTANT_NAME,ACTUAL_RENDER_BEGIN_NAME,ACTUAL_RENDER_END_NAME,STREAM_ID_NAME];const SUMMARY_OPTIONS=tr.v.Histogram.AVERAGE_ONLY_SUMMARY_OPTIONS;const count_smallerIsBetter=tr.b.Unit.byName.count_smallerIsBetter;const percentage_biggerIsBetter=tr.b.Unit.byName.normalizedPercentage_biggerIsBetter;const percentage_smallerIsBetter=tr.b.Unit.byName.normalizedPercentage_smallerIsBetter;const timeDurationInMs_smallerIsBetter=tr.b.Unit.byName.timeDurationInMs_smallerIsBetter;const unitlessNumber_biggerIsBetter=tr.b.Unit.byName.unitlessNumber_biggerIsBetter;function isValidEvent(event){if(event.title!==WEB_MEDIA_PLAYER_UPDATE_TITLE||!event.args){return false;}
+for(const parameter of REQUIRED_EVENT_ARGS_NAMES){if(!(parameter in event.args)){return false;}}
+return true;}
+function webrtcRenderingMetric(histograms,model){const modelHelper=model.getOrCreateHelper(tr.model.helpers.ChromeModelHelper);let webMediaPlayerMSEvents=[];for(const rendererPid in modelHelper.rendererHelpers){const rendererHelper=modelHelper.rendererHelpers[rendererPid];const compositorThread=rendererHelper.compositorThread;if(compositorThread!==undefined){webMediaPlayerMSEvents=webMediaPlayerMSEvents.concat(compositorThread.sliceGroup.slices.filter(isValidEvent));}}
+const eventsByStreamName=tr.b.groupIntoMap(webMediaPlayerMSEvents,event=>event.args[STREAM_ID_NAME]);for(const[streamName,events]of eventsByStreamName){getTimeStats(histograms,streamName,events);}}
+tr.metrics.MetricRegistry.register(webrtcRenderingMetric);function getTimeStats(histograms,streamName,events){const frameHist=getFrameDistribution(histograms,events);addFpsFromFrameDistribution(histograms,frameHist);addFreezingScore(histograms,frameHist);const driftTimeStats=getDriftStats(events);histograms.createHistogram('WebRTCRendering_drift_time',timeDurationInMs_smallerIsBetter,driftTimeStats.driftTime,{summaryOptions:{count:false,min:false,percentile:[0.75,0.9],},});histograms.createHistogram('WebRTCRendering_rendering_length_error',percentage_smallerIsBetter,driftTimeStats.renderingLengthError,{summaryOptions:SUMMARY_OPTIONS,});const smoothnessStats=getSmoothnessStats(driftTimeStats.driftTime);histograms.createHistogram('WebRTCRendering_percent_badly_out_of_sync',percentage_smallerIsBetter,smoothnessStats.percentBadlyOutOfSync,{summaryOptions:SUMMARY_OPTIONS,});histograms.createHistogram('WebRTCRendering_percent_out_of_sync',percentage_smallerIsBetter,smoothnessStats.percentOutOfSync,{summaryOptions:SUMMARY_OPTIONS,});histograms.createHistogram('WebRTCRendering_smoothness_score',percentage_biggerIsBetter,smoothnessStats.smoothnessScore,{summaryOptions:SUMMARY_OPTIONS,});histograms.createHistogram('WebRTCRendering_frames_out_of_sync',count_smallerIsBetter,smoothnessStats.framesOutOfSync,{summaryOptions:SUMMARY_OPTIONS,});histograms.createHistogram('WebRTCRendering_frames_badly_out_of_sync',count_smallerIsBetter,smoothnessStats.framesSeverelyOutOfSync,{summaryOptions:SUMMARY_OPTIONS,});}
+const FRAME_DISTRIBUTION_BIN_BOUNDARIES=tr.v.HistogramBinBoundaries.createLinear(1,50,49);function getFrameDistribution(histograms,events){const cadence=tr.b.runLengthEncoding(events.map(e=>e.args[IDEAL_RENDER_INSTANT_NAME]));return histograms.createHistogram('WebRTCRendering_frame_distribution',count_smallerIsBetter,cadence.map(ticks=>ticks.count),{binBoundaries:FRAME_DISTRIBUTION_BIN_BOUNDARIES,summaryOptions:{percentile:[0.75,0.9],},});}
+function addFpsFromFrameDistribution(histograms,frameHist){let numberFrames=0;let numberVsyncs=0;for(let ticks=1;ticks<frameHist.allBins.length;++ticks){const count=frameHist.allBins[ticks].count;numberFrames+=count;numberVsyncs+=ticks*count;}
+const meanRatio=numberVsyncs/numberFrames;histograms.createHistogram('WebRTCRendering_fps',unitlessNumber_biggerIsBetter,DISPLAY_HERTZ/meanRatio,{summaryOptions:SUMMARY_OPTIONS,});}
+function frozenPenaltyWeight(numberFrozenFrames){const penalty={5:1,6:5,7:15,8:25};return penalty[numberFrozenFrames]||(8*(numberFrozenFrames-4));}
+function addFreezingScore(histograms,frameHist){let numberVsyncs=0;let freezingScore=0;let frozenFramesCount=0;for(let ticks=1;ticks<frameHist.allBins.length;++ticks){const count=frameHist.allBins[ticks].count;numberVsyncs+=ticks*count;if(ticks>=FROZEN_FRAME_VSYNC_COUNT_THRESHOLD){frozenFramesCount+=count*(ticks-1);freezingScore+=count*frozenPenaltyWeight(ticks-1);}}
+freezingScore=1-freezingScore/numberVsyncs;if(freezingScore<0){freezingScore=0;}
+histograms.createHistogram('WebRTCRendering_frozen_frames_count',count_smallerIsBetter,frozenFramesCount,{summaryOptions:SUMMARY_OPTIONS,});histograms.createHistogram('WebRTCRendering_freezing_score',percentage_biggerIsBetter,freezingScore,{summaryOptions:SUMMARY_OPTIONS,});}
+function getDriftStats(events){const driftTime=[];const discrepancy=[];let oldIdealRender=0;let expectedIdealRender=0;for(const event of events){const currentIdealRender=event.args[IDEAL_RENDER_INSTANT_NAME];expectedIdealRender+=VSYNC_DURATION_US;if(currentIdealRender===oldIdealRender){continue;}
+const actualRenderBegin=event.args[ACTUAL_RENDER_BEGIN_NAME];driftTime.push(actualRenderBegin-currentIdealRender);discrepancy.push(Math.abs(currentIdealRender-expectedIdealRender));expectedIdealRender=currentIdealRender;oldIdealRender=currentIdealRender;}
+const discrepancySum=tr.b.math.Statistics.sum(discrepancy)-
+discrepancy[0];const lastIdealRender=events[events.length-1].args[IDEAL_RENDER_INSTANT_NAME];const firstIdealRender=events[0].args[IDEAL_RENDER_INSTANT_NAME];const idealRenderSpan=lastIdealRender-firstIdealRender;const renderingLengthError=discrepancySum/idealRenderSpan;return{driftTime,renderingLengthError};}
+function getSmoothnessStats(driftTimes){const meanDriftTime=tr.b.math.Statistics.mean(driftTimes);const normDriftTimes=driftTimes.map(driftTime=>Math.abs(driftTime-meanDriftTime));const framesSeverelyOutOfSync=normDriftTimes.filter(driftTime=>driftTime>2*VSYNC_DURATION_US).length;const framesOutOfSync=normDriftTimes.filter(driftTime=>driftTime>VSYNC_DURATION_US).length;const percentBadlyOutOfSync=framesSeverelyOutOfSync/driftTimes.length;const percentOutOfSync=framesOutOfSync/driftTimes.length;const framesOutOfSyncOnlyOnce=framesOutOfSync-framesSeverelyOutOfSync;let smoothnessScore=1-(framesOutOfSyncOnlyOnce+
+SEVERITY*framesSeverelyOutOfSync)/driftTimes.length;if(smoothnessScore<0){smoothnessScore=0;}
+return{framesOutOfSync,framesSeverelyOutOfSync,percentBadlyOutOfSync,percentOutOfSync,smoothnessScore};}
+return{webrtcRenderingMetric,};});'use strict';Polymer({is:'tr-ui-a-alert-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],ready(){this.currentSelection_=undefined;this.$.table.tableColumns=[{title:'Label',value(row){return row.name;},width:'150px'},{title:'Value',width:'100%',value(row){return row.value;}}];this.$.table.showHeader=false;},get selection(){return this.currentSelection_;},set selection(selection){this.currentSelection_=selection;this.updateContents_();},getRowsForSingleAlert_(alert){const rows=[];for(const argName in alert.args){const argView=document.createElement('tr-ui-a-generic-object-view');argView.object=alert.args[argName];rows.push({name:argName,value:argView});}
+if(alert.associatedEvents.length){alert.associatedEvents.forEach(function(event,i){const linkEl=document.createElement('tr-ui-a-analysis-link');linkEl.setSelectionAndContent(new tr.model.EventSet(event),event.title);let valueString='';if(event instanceof tr.model.TimedEvent){valueString='took '+event.duration.toFixed(2)+'ms';}
+rows.push({name:linkEl,value:valueString});});}
+const descriptionEl=tr.ui.b.createDiv({textContent:alert.info.description,maxWidth:'300px'});rows.push({name:'Description',value:descriptionEl});if(alert.info.docLinks){alert.info.docLinks.forEach(function(linkObject){const linkEl=document.createElement('a');linkEl.target='_blank';linkEl.href=linkObject.href;Polymer.dom(linkEl).textContent=Polymer.dom(linkObject).textContent;rows.push({name:linkObject.label,value:linkEl});});}
+return rows;},getRowsForAlerts_(alerts){if(alerts.length===1){const rows=[{name:'Alert',value:tr.b.getOnlyElement(alerts).title}];const detailRows=this.getRowsForSingleAlert_(tr.b.getOnlyElement(alerts));rows.push.apply(rows,detailRows);return rows;}
+return alerts.map(function(alert){return{name:'Alert',value:alert.title,isExpanded:alerts.size<10,subRows:this.getRowsForSingleAlert_(alert)};},this);},updateContents_(){if(this.currentSelection_===undefined){this.$.table.rows=[];this.$.table.rebuild();return;}
+const alerts=this.currentSelection_;this.$.table.tableRows=this.getRowsForAlerts_(alerts);this.$.table.rebuild();},get relatedEventsToHighlight(){if(!this.currentSelection_)return undefined;const result=new tr.model.EventSet();for(const event of this.currentSelection_){result.addEventSet(event.associatedEvents);}
+return result;}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-alert-sub-view',tr.model.Alert,{multi:false,title:'Alert',});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-alert-sub-view',tr.model.Alert,{multi:true,title:'Alerts',});'use strict';tr.exportTo('tr.ui.analysis',function(){const NO_BREAK_SPACE=String.fromCharCode(160);const RIGHTWARDS_ARROW=String.fromCharCode(8594);const COLLATOR=new Intl.Collator(undefined,{numeric:true});function TitleColumn(title){this.title=title;}
+TitleColumn.prototype={supportsCellSelection:false,value(row){const formattedTitle=this.formatTitle(row);const contexts=row.contexts;if(contexts===undefined||contexts.length===0){return formattedTitle;}
+const firstContext=contexts[0];const lastContext=contexts[contexts.length-1];let changeDefinedContextCount=0;for(let i=1;i<contexts.length;i++){if((contexts[i]===undefined)!==(contexts[i-1]===undefined)){changeDefinedContextCount++;}}
+let color=undefined;let prefix=undefined;if(!firstContext&&lastContext){color='red';prefix='+++';}else if(firstContext&&!lastContext){color='green';prefix='---';}
+if(changeDefinedContextCount>1){color='purple';}
+if(color===undefined&&prefix===undefined){return formattedTitle;}
+const titleEl=document.createElement('span');if(prefix!==undefined){const prefixEl=tr.ui.b.createSpan({textContent:prefix});prefixEl.style.fontFamily='monospace';Polymer.dom(titleEl).appendChild(prefixEl);Polymer.dom(titleEl).appendChild(tr.ui.b.asHTMLOrTextNode(NO_BREAK_SPACE));}
+if(color!==undefined){titleEl.style.color=color;}
+Polymer.dom(titleEl).appendChild(tr.ui.b.asHTMLOrTextNode(formattedTitle));return titleEl;},formatTitle(row){return row.title;},cmp(rowA,rowB){return COLLATOR.compare(rowA.title,rowB.title);}};function MemoryColumn(name,cellPath,aggregationMode){this.name=name;this.cellPath=cellPath;this.shouldSetContextGroup=false;this.aggregationMode=aggregationMode;}
+MemoryColumn.fromRows=function(rows,config){const cellNames=new Set();function gatherCellNames(rows){rows.forEach(function(row){if(row===undefined)return;const fieldCells=row[config.cellKey];if(fieldCells!==undefined){for(const[fieldName,fieldCell]of Object.entries(fieldCells)){if(fieldCell===undefined||fieldCell.fields===undefined){continue;}
+cellNames.add(fieldName);}}
+const subRows=row.subRows;if(subRows!==undefined){gatherCellNames(subRows);}});}
+gatherCellNames(rows);const positions=[];cellNames.forEach(function(cellName){const cellPath=[config.cellKey,cellName];const matchingRule=MemoryColumn.findMatchingRule(cellName,config.rules);const constructor=matchingRule.columnConstructor;const column=new constructor(cellName,cellPath,config.aggregationMode);column.shouldSetContextGroup=!!config.shouldSetContextGroup;positions.push({importance:matchingRule.importance,column});});positions.sort(function(a,b){if(a.importance===b.importance){return COLLATOR.compare(a.column.name,b.column.name);}
+return b.importance-a.importance;});return positions.map(function(position){return position.column;});};MemoryColumn.spaceEqually=function(columns){const columnWidth=(100/columns.length).toFixed(3)+'%';columns.forEach(function(column){column.width=columnWidth;});};MemoryColumn.findMatchingRule=function(name,rules){for(let i=0;i<rules.length;i++){const rule=rules[i];if(MemoryColumn.nameMatchesCondition(name,rule.condition)){return rule;}}
+return undefined;};MemoryColumn.nameMatchesCondition=function(name,condition){if(condition===undefined)return true;if(typeof(condition)==='string')return name===condition;return condition.test(name);};MemoryColumn.AggregationMode={DIFF:0,MAX:1};MemoryColumn.SOME_TIMESTAMPS_INFO_QUANTIFIER='at some selected timestamps';MemoryColumn.prototype={get title(){return this.name;},cell(row){let cell=row;const cellPath=this.cellPath;for(let i=0;i<cellPath.length;i++){if(cell===undefined)return undefined;cell=cell[cellPath[i]];}
+return cell;},aggregateCells(row,subRows){},fields(row){const cell=this.cell(row);if(cell===undefined)return undefined;return cell.fields;},value(row){const fields=this.fields(row);if(this.hasAllRelevantFieldsUndefined(fields))return'';const contexts=row.contexts;const color=this.color(fields,contexts);const infos=[];this.addInfos(fields,contexts,infos);const formattedFields=this.formatFields(fields);if((color===undefined||formattedFields==='')&&infos.length===0){return formattedFields;}
+const fieldEl=document.createElement('span');fieldEl.style.display='flex';fieldEl.style.alignItems='center';fieldEl.style.justifyContent='flex-end';Polymer.dom(fieldEl).appendChild(tr.ui.b.asHTMLOrTextNode(formattedFields));infos.forEach(function(info){const infoEl=document.createElement('span');infoEl.style.paddingLeft='4px';infoEl.style.cursor='help';infoEl.style.fontWeight='bold';Polymer.dom(infoEl).textContent=info.icon;if(info.color!==undefined){infoEl.style.color=info.color;}
+infoEl.title=info.message;Polymer.dom(fieldEl).appendChild(infoEl);},this);if(color!==undefined){fieldEl.style.color=color;}
+return fieldEl;},hasAllRelevantFieldsUndefined(fields){if(fields===undefined)return true;switch(this.aggregationMode){case MemoryColumn.AggregationMode.DIFF:return fields[0]===undefined&&fields[fields.length-1]===undefined;case MemoryColumn.AggregationMode.MAX:default:return fields.every(function(field){return field===undefined;});}},color(fields,contexts){return undefined;},formatFields(fields){if(fields.length===1){return this.formatSingleField(fields[0]);}
+return this.formatMultipleFields(fields);},formatSingleField(field){throw new Error('Not implemented');},formatMultipleFields(fields){switch(this.aggregationMode){case MemoryColumn.AggregationMode.DIFF:return this.formatMultipleFieldsDiff(fields[0],fields[fields.length-1]);case MemoryColumn.AggregationMode.MAX:return this.formatMultipleFieldsMax(fields);default:return tr.ui.b.createSpan({textContent:'(unsupported aggregation mode)',italic:true});}},formatMultipleFieldsDiff(firstField,lastField){throw new Error('Not implemented');},formatMultipleFieldsMax(fields){return this.formatSingleField(this.getMaxField(fields));},cmp(rowA,rowB){const fieldsA=this.fields(rowA);const fieldsB=this.fields(rowB);if(fieldsA!==undefined&&fieldsB!==undefined&&fieldsA.length!==fieldsB.length){throw new Error('Different number of fields');}
+const undefinedA=this.hasAllRelevantFieldsUndefined(fieldsA);const undefinedB=this.hasAllRelevantFieldsUndefined(fieldsB);if(undefinedA&&undefinedB)return 0;if(undefinedA)return-1;if(undefinedB)return 1;return this.compareFields(fieldsA,fieldsB);},compareFields(fieldsA,fieldsB){if(fieldsA.length===1){return this.compareSingleFields(fieldsA[0],fieldsB[0]);}
+return this.compareMultipleFields(fieldsA,fieldsB);},compareSingleFields(fieldA,fieldB){throw new Error('Not implemented');},compareMultipleFields(fieldsA,fieldsB){switch(this.aggregationMode){case MemoryColumn.AggregationMode.DIFF:return this.compareMultipleFieldsDiff(fieldsA[0],fieldsA[fieldsA.length-1],fieldsB[0],fieldsB[fieldsB.length-1]);case MemoryColumn.AggregationMode.MAX:return this.compareMultipleFieldsMax(fieldsA,fieldsB);default:return 0;}},compareMultipleFieldsDiff(firstFieldA,lastFieldA,firstFieldB,lastFieldB){throw new Error('Not implemented');},compareMultipleFieldsMax(fieldsA,fieldsB){return this.compareSingleFields(this.getMaxField(fieldsA),this.getMaxField(fieldsB));},getMaxField(fields){return fields.reduce(function(accumulator,field){if(field===undefined){return accumulator;}
+if(accumulator===undefined||this.compareSingleFields(field,accumulator)>0){return field;}
+return accumulator;}.bind(this),undefined);},addInfos(fields,contexts,infos){},getImportance(importanceRules){if(importanceRules.length===0)return 0;const matchingRule=MemoryColumn.findMatchingRule(this.name,importanceRules);if(matchingRule!==undefined){return matchingRule.importance;}
+let minImportance=importanceRules[0].importance;for(let i=1;i<importanceRules.length;i++){minImportance=Math.min(minImportance,importanceRules[i].importance);}
+return minImportance-1;}};function StringMemoryColumn(name,cellPath,aggregationMode){MemoryColumn.call(this,name,cellPath,aggregationMode);}
+StringMemoryColumn.prototype={__proto__:MemoryColumn.prototype,formatSingleField(string){return string;},formatMultipleFieldsDiff(firstString,lastString){if(firstString===undefined){const spanEl=tr.ui.b.createSpan({color:'red'});Polymer.dom(spanEl).appendChild(tr.ui.b.asHTMLOrTextNode('+'));Polymer.dom(spanEl).appendChild(tr.ui.b.asHTMLOrTextNode(this.formatSingleField(lastString)));return spanEl;}else if(lastString===undefined){const spanEl=tr.ui.b.createSpan({color:'green'});Polymer.dom(spanEl).appendChild(tr.ui.b.asHTMLOrTextNode('-'));Polymer.dom(spanEl).appendChild(tr.ui.b.asHTMLOrTextNode(this.formatSingleField(firstString)));return spanEl;}else if(firstString===lastString){return this.formatSingleField(firstString);}
+const spanEl=tr.ui.b.createSpan({color:'DarkOrange'});Polymer.dom(spanEl).appendChild(tr.ui.b.asHTMLOrTextNode(this.formatSingleField(firstString)));Polymer.dom(spanEl).appendChild(tr.ui.b.asHTMLOrTextNode(' '+RIGHTWARDS_ARROW+' '));Polymer.dom(spanEl).appendChild(tr.ui.b.asHTMLOrTextNode(this.formatSingleField(lastString)));return spanEl;},compareSingleFields(stringA,stringB){return COLLATOR.compare(stringA,stringB);},compareMultipleFieldsDiff(firstStringA,lastStringA,firstStringB,lastStringB){if(firstStringA===undefined&&firstStringB!==undefined){return 1;}
+if(firstStringA!==undefined&&firstStringB===undefined){return-1;}
+if(firstStringA===undefined&&firstStringB===undefined){return this.compareSingleFields(lastStringA,lastStringB);}
+if(lastStringA===undefined&&lastStringB!==undefined){return-1;}
+if(lastStringA!==undefined&&lastStringB===undefined){return 1;}
+if(lastStringA===undefined&&lastStringB===undefined){return this.compareSingleFields(firstStringB,firstStringA);}
+const areStringsAEqual=firstStringA===lastStringA;const areStringsBEqual=firstStringB===lastStringB;if(areStringsAEqual&&areStringsBEqual)return 0;if(areStringsAEqual)return-1;if(areStringsBEqual)return 1;return 0;}};function NumericMemoryColumn(name,cellPath,aggregationMode){MemoryColumn.call(this,name,cellPath,aggregationMode);}
+NumericMemoryColumn.DIFF_EPSILON=0.0001;NumericMemoryColumn.prototype={__proto__:MemoryColumn.prototype,align:tr.ui.b.TableFormat.ColumnAlignment.RIGHT,aggregateCells(row,subRows){const subRowCells=subRows.map(this.cell,this);let hasDefinedSubRowNumeric=false;let timestampCount=undefined;subRowCells.forEach(function(subRowCell){if(subRowCell===undefined)return;const subRowNumerics=subRowCell.fields;if(subRowNumerics===undefined)return;if(timestampCount===undefined){timestampCount=subRowNumerics.length;}else if(timestampCount!==subRowNumerics.length){throw new Error('Sub-rows have different numbers of timestamps');}
+if(hasDefinedSubRowNumeric){return;}
+hasDefinedSubRowNumeric=subRowNumerics.some(function(numeric){return numeric!==undefined;});});if(!hasDefinedSubRowNumeric){return;}
+const cellPath=this.cellPath;let rowCell=row;for(let i=0;i<cellPath.length;i++){const nextStepName=cellPath[i];let nextStep=rowCell[nextStepName];if(nextStep===undefined){if(i<cellPath.length-1){nextStep={};}else{nextStep=new MemoryCell(undefined);}
+rowCell[nextStepName]=nextStep;}
+rowCell=nextStep;}
+if(rowCell.fields===undefined){rowCell.fields=new Array(timestampCount);}else if(rowCell.fields.length!==timestampCount){throw new Error('Row has a different number of timestamps than sub-rows');}
+for(let i=0;i<timestampCount;i++){if(rowCell.fields[i]!==undefined)continue;rowCell.fields[i]=tr.model.MemoryAllocatorDump.aggregateNumerics(subRowCells.map(function(subRowCell){if(subRowCell===undefined||subRowCell.fields===undefined){return undefined;}
+return subRowCell.fields[i];}));}},formatSingleField(numeric){return tr.v.ui.createScalarSpan(numeric,{context:this.getFormattingContext(numeric.unit),contextGroup:this.shouldSetContextGroup?this.name:undefined,inline:true,});},getFormattingContext(unit){return undefined;},formatMultipleFieldsDiff(firstNumeric,lastNumeric){return this.formatSingleField(this.getDiffField_(firstNumeric,lastNumeric));},compareSingleFields(numericA,numericB){return numericA.value-numericB.value;},compareMultipleFieldsDiff(firstNumericA,lastNumericA,firstNumericB,lastNumericB){return this.getDiffFieldValue_(firstNumericA,lastNumericA)-
+this.getDiffFieldValue_(firstNumericB,lastNumericB);},getDiffField_(firstNumeric,lastNumeric){const definedNumeric=firstNumeric||lastNumeric;return new tr.b.Scalar(definedNumeric.unit.correspondingDeltaUnit,this.getDiffFieldValue_(firstNumeric,lastNumeric));},getDiffFieldValue_(firstNumeric,lastNumeric){const firstValue=firstNumeric===undefined?0:firstNumeric.value;const lastValue=lastNumeric===undefined?0:lastNumeric.value;const diff=lastValue-firstValue;return Math.abs(diff)<NumericMemoryColumn.DIFF_EPSILON?0:diff;}};function MemoryCell(fields){this.fields=fields;}
+MemoryCell.extractFields=function(cell){if(cell===undefined)return undefined;return cell.fields;};const RECURSIVE_EXPANSION_MAX_VISIBLE_ROW_COUNT=10;function expandTableRowsRecursively(table){let currentLevelRows=table.tableRows;let totalVisibleRowCount=currentLevelRows.length;while(currentLevelRows.length>0){let nextLevelRowCount=0;currentLevelRows.forEach(function(currentLevelRow){const subRows=currentLevelRow.subRows;if(subRows===undefined||subRows.length===0)return;nextLevelRowCount+=subRows.length;});if(totalVisibleRowCount+nextLevelRowCount>RECURSIVE_EXPANSION_MAX_VISIBLE_ROW_COUNT){break;}
+const nextLevelRows=new Array(nextLevelRowCount);let nextLevelRowIndex=0;currentLevelRows.forEach(function(currentLevelRow){const subRows=currentLevelRow.subRows;if(subRows===undefined||subRows.length===0)return;table.setExpandedForTableRow(currentLevelRow,true);subRows.forEach(function(subRow){nextLevelRows[nextLevelRowIndex++]=subRow;});});totalVisibleRowCount+=nextLevelRowCount;currentLevelRows=nextLevelRows;}}
+function aggregateTableRowCellsRecursively(row,columns,opt_predicate){const subRows=row.subRows;if(subRows===undefined||subRows.length===0)return;subRows.forEach(function(subRow){aggregateTableRowCellsRecursively(subRow,columns,opt_predicate);});if(opt_predicate===undefined||opt_predicate(row.contexts)){aggregateTableRowCells(row,subRows,columns);}}
+function aggregateTableRowCells(row,subRows,columns){columns.forEach(function(column){if(!(column instanceof MemoryColumn))return;column.aggregateCells(row,subRows);});}
+function createCells(timeToValues,valueFieldsGetter,opt_this){opt_this=opt_this||this;const fieldNameToFields=tr.b.invertArrayOfDicts(timeToValues,valueFieldsGetter,opt_this);const result={};for(const[fieldName,fields]of Object.entries(fieldNameToFields)){result[fieldName]=new tr.ui.analysis.MemoryCell(fields);}
+return result;}
+function createWarningInfo(message){return{message,icon:String.fromCharCode(9888),color:'red'};}
+function DetailsNumericMemoryColumn(name,cellPath,aggregationMode){NumericMemoryColumn.call(this,name,cellPath,aggregationMode);}
+DetailsNumericMemoryColumn.prototype={__proto__:NumericMemoryColumn.prototype,getFormattingContext(unit){if(unit.baseUnit===tr.b.Unit.byName.sizeInBytes){return{unitPrefix:tr.b.UnitPrefixScale.BINARY.KIBI};}
+return undefined;}};return{TitleColumn,MemoryColumn,StringMemoryColumn,NumericMemoryColumn,MemoryCell,expandTableRowsRecursively,aggregateTableRowCellsRecursively,aggregateTableRowCells,createCells,createWarningInfo,DetailsNumericMemoryColumn,};});'use strict';tr.exportTo('tr.ui.analysis',function(){const LATIN_SMALL_LETTER_F_WITH_HOOK=String.fromCharCode(0x0192);const CIRCLED_LATIN_CAPITAL_LETTER_T=String.fromCharCode(0x24C9);const HeapDetailsRowDimension={ROOT:{},STACK_FRAME:{label:'Stack frame',symbol:LATIN_SMALL_LETTER_F_WITH_HOOK,color:'heap_dump_stack_frame'},OBJECT_TYPE:{label:'Object type',symbol:CIRCLED_LATIN_CAPITAL_LETTER_T,color:'heap_dump_object_type'}};function HeapDetailsTitleColumn(title){tr.ui.analysis.TitleColumn.call(this,title);}
+HeapDetailsTitleColumn.prototype={__proto__:tr.ui.analysis.TitleColumn.prototype,formatTitle(row){if(row.dimension===HeapDetailsRowDimension.ROOT){return row.title;}
+const symbolEl=document.createElement('span');Polymer.dom(symbolEl).textContent=row.dimension.symbol;symbolEl.title=row.dimension.label;symbolEl.style.color=tr.b.ColorScheme.getColorForReservedNameAsString(row.dimension.color);symbolEl.style.paddingRight='4px';symbolEl.style.cursor='help';symbolEl.style.fontWeight='bold';const titleEl=document.createElement('span');Polymer.dom(titleEl).appendChild(symbolEl);Polymer.dom(titleEl).appendChild(document.createTextNode(row.title));return titleEl;}};function AllocationCountColumn(name,cellPath,aggregationMode){tr.ui.analysis.DetailsNumericMemoryColumn.call(this,name,cellPath,aggregationMode);}
+AllocationCountColumn.prototype={__proto__:tr.ui.analysis.DetailsNumericMemoryColumn.prototype,getFormattingContext(unit){return{minimumFractionDigits:0};}};const HEAP_DETAILS_COLUMN_RULES=[{condition:'Size',importance:2,columnConstructor:tr.ui.analysis.DetailsNumericMemoryColumn},{condition:'Count',importance:1,columnConstructor:AllocationCountColumn},{importance:0,columnConstructor:tr.ui.analysis.DetailsNumericMemoryColumn}];return{HeapDetailsRowDimension,HeapDetailsTitleColumn,AllocationCountColumn,HEAP_DETAILS_COLUMN_RULES,};});'use strict';tr.exportTo('tr.ui.analysis',function(){const RebuildableBehavior={rebuild(){if(!this.paneDirty_){return;}
+this.paneDirty_=false;this.onRebuild_();},scheduleRebuild_(){if(this.paneDirty_)return;this.paneDirty_=true;tr.b.requestAnimationFrame(this.rebuild.bind(this));},onRebuild_(){}};return{RebuildableBehavior,};});'use strict';Polymer({is:'tr-ui-b-tab-view',properties:{label_:{type:String,value:()=>''},selectedSubView_:Object,subViews_:{type:Array,value:()=>[]},tabsHidden:{type:Boolean,value:false,observer:'tabsHiddenChanged_'}},ready(){this.$.tabs.addEventListener('keydown',this.onKeyDown_.bind(this),true);this.updateFocusability_();},set label(newLabel){this.set('label_',newLabel);},get tabs(){return this.get('subViews_');},get selectedSubView(){return this.selectedSubView_;},set selectedSubView(subView){if(subView===this.selectedSubView_)return;if(this.selectedSubView_){Polymer.dom(this.$.subView).removeChild(this.selectedSubView_);const oldInput=this.root.getElementById(this.computeRadioId_(this.selectedSubView_));if(oldInput){oldInput.checked=false;}}
+this.set('selectedSubView_',subView);if(subView){Polymer.dom(this.$.subView).appendChild(subView);const newInput=this.root.getElementById(this.computeRadioId_(subView));if(newInput){newInput.checked=true;}}
+this.fire('selected-tab-change');},clearSubViews(){this.splice('subViews_',0,this.subViews_.length);this.selectedSubView=undefined;this.updateFocusability_();},addSubView(subView){this.push('subViews_',subView);if(!this.selectedSubView_)this.selectedSubView=subView;this.updateFocusability_();},get subViews(){return this.subViews_;},resetSubViews(subViews){this.splice('subViews_',0,this.subViews_.length);if(subViews.length){for(const subView of subViews){this.push('subViews_',subView);}
+this.selectedSubView=subViews[0];}else{this.selectedSubView=undefined;}
+this.updateFocusability_();},onTabChanged_(event){this.selectedSubView=event.model.item;},isChecked_(subView){return this.selectedSubView_===subView;},tabsHiddenChanged_(){this.updateFocusability_();},onKeyDown_(e){if(this.tabsHidden)return;let keyHandled=false;switch(e.keyCode){case 37:keyHandled=this.selectPreviousTabIfPossible();break;case 39:keyHandled=this.selectNextTabIfPossible();break;}
+if(!keyHandled)return;e.stopPropagation();e.preventDefault();},selectNextTabIfPossible(){return this.selectTabByOffsetIfPossible_(1);},selectPreviousTabIfPossible(){return this.selectTabByOffsetIfPossible_(-1);},selectTabByOffsetIfPossible_(offset){if(!this.selectedSubView_)return false;const currentIndex=this.subViews_.indexOf(this.selectedSubView_);const newSubView=this.tabs[currentIndex+offset];if(!newSubView)return false;this.selectedSubView=newSubView;return true;},shouldBeFocusable_(){return!this.tabsHidden&&this.subViews_.length>0;},updateFocusability_(){if(this.shouldBeFocusable_()){Polymer.dom(this.$.tabs).setAttribute('tabindex',0);}else{Polymer.dom(this.$.tabs).removeAttribute('tabindex');}},computeRadioId_(subView){return subView.tagName+'-'+subView.tabLabel.replace(/ /g,'-');}});'use strict';tr.exportTo('tr.ui.analysis',function(){const RESONABLE_NUMBER_OF_ROWS=200;const TabUiState={NO_LONG_TAIL:0,HIDING_LONG_TAIL:1,SHOWING_LONG_TAIL:2,};function EmptyFillerColumn(){}
+EmptyFillerColumn.prototype={title:'',value(){return'';},};Polymer({is:'tr-ui-a-memory-dump-heap-details-breakdown-view',behaviors:[tr.ui.analysis.RebuildableBehavior],created(){this.displayedNode_=undefined;this.dimensionToTab_=new Map();},ready(){this.scheduleRebuild_();this.root.addEventListener('keydown',this.onKeyDown_.bind(this),true);},get displayedNode(){return this.displayedNode_;},set displayedNode(node){this.displayedNode_=node;this.scheduleRebuild_();},get aggregationMode(){return this.aggregationMode_;},set aggregationMode(aggregationMode){this.aggregationMode_=aggregationMode;for(const tab of this.$.tabs.tabs){tab.aggregationMode=aggregationMode;}},onRebuild_(){const previouslySelectedTab=this.$.tabs.selectedSubView;let previouslySelectedTabFocused=false;let previouslySelectedDimension=undefined;if(previouslySelectedTab){previouslySelectedTabFocused=previouslySelectedTab.isFocused;previouslySelectedDimension=previouslySelectedTab.dimension;}
+for(const tab of this.$.tabs.tabs){tab.nodes=undefined;}
+this.$.tabs.clearSubViews();if(this.displayedNode_===undefined){this.$.tabs.label='No heap node provided.';return;}
+for(const[dimension,children]of this.displayedNode_.childNodes){if(!this.dimensionToTab_.has(dimension)){this.dimensionToTab_.set(dimension,document.createElement('tr-ui-a-memory-dump-heap-details-breakdown-view-tab'));}
+const tab=this.dimensionToTab_.get(dimension);tab.aggregationMode=this.aggregationMode_;tab.dimension=dimension;tab.nodes=children;this.$.tabs.addSubView(tab);tab.rebuild();if(dimension===previouslySelectedDimension){this.$.tabs.selectedSubView=tab;if(previouslySelectedTabFocused){tab.focus();}}}
+if(this.$.tabs.tabs.length>0){this.$.tabs.label='Break selected node further by:';}else{this.$.tabs.label='Selected node cannot be broken down any further.';}},onKeyDown_(keyEvent){if(!this.displayedNode_)return;let keyHandled=false;switch(keyEvent.keyCode){case 8:{if(!this.displayedNode_.parentNode)break;const viewEvent=new tr.b.Event('enter-node');viewEvent.node=this.displayedNode_.parentNode;this.dispatchEvent(viewEvent);keyHandled=true;break;}
+case 37:case 39:{const wasFocused=this.$.tabs.selectedSubView.isFocused;keyHandled=keyEvent.keyCode===37?this.$.tabs.selectPreviousTabIfPossible():this.$.tabs.selectNextTabIfPossible();if(wasFocused&&keyHandled){this.$.tabs.selectedSubView.focus();}}}
+if(!keyHandled)return;keyEvent.stopPropagation();keyEvent.preventDefault();}});Polymer({is:'tr-ui-a-memory-dump-heap-details-breakdown-view-tab',behaviors:[tr.ui.analysis.RebuildableBehavior],created(){this.dimension_=undefined;this.nodes_=undefined;this.aggregationMode_=undefined;this.displayLongTail_=false;},ready(){this.$.table.addEventListener('step-into',function(tableEvent){const viewEvent=new tr.b.Event('enter-node');viewEvent.node=tableEvent.tableRow;this.dispatchEvent(viewEvent);}.bind(this));},get displayLongTail(){return this.displayLongTail_;},set displayLongTail(newValue){if(this.displayLongTail===newValue)return;this.displayLongTail_=newValue;this.scheduleRebuild_();},get dimension(){return this.dimension_;},set dimension(dimension){this.dimension_=dimension;this.scheduleRebuild_();},get nodes(){return this.nodes_;},set nodes(nodes){this.nodes_=nodes;this.scheduleRebuild_();},get nodes(){return this.nodes_||[];},get dimensionLabel_(){if(this.dimension_===undefined)return'(undefined)';return this.dimension_.label;},get tabLabel(){let nodeCount=0;if(this.nodes_){nodeCount=this.nodes_.length;}
+return this.dimensionLabel_+' ('+nodeCount+')';},get tabIcon(){if(this.dimension_===undefined||this.dimension_===tr.ui.analysis.HeapDetailsRowDimension.ROOT){return undefined;}
+return{text:this.dimension_.symbol,style:'color: '+tr.b.ColorScheme.getColorForReservedNameAsString(this.dimension_.color)+';'};},get aggregationMode(){return this.aggregationMode_;},set aggregationMode(aggregationMode){this.aggregationMode_=aggregationMode;this.scheduleRebuild_();},focus(){this.$.table.focus();},blur(){this.$.table.blur();},get isFocused(){return this.$.table.isFocused;},onRebuild_(){this.$.table.selectionMode=tr.ui.b.TableFormat.SelectionMode.ROW;this.$.table.emptyValue='Cannot break down by '+
+this.dimensionLabel_.toLowerCase()+' any further.';const[state,rows]=this.getRows_();const total=this.nodes.length;const displayed=rows.length;const hidden=total-displayed;this.updateInfoBar_(state,[total,displayed,hidden]);this.$.table.tableRows=rows;this.$.table.tableColumns=this.createColumns_(rows);if(this.$.table.sortColumnIndex===undefined){this.$.table.sortColumnIndex=0;this.$.table.sortDescending=false;}
+this.$.table.rebuild();},createColumns_(rows){const titleColumn=new tr.ui.analysis.HeapDetailsTitleColumn(this.dimensionLabel_);titleColumn.width='400px';const numericColumns=tr.ui.analysis.MemoryColumn.fromRows(rows,{cellKey:'cells',aggregationMode:this.aggregationMode_,rules:tr.ui.analysis.HEAP_DETAILS_COLUMN_RULES,shouldSetContextGroup:true});if(numericColumns.length===0){numericColumns.push(new EmptyFillerColumn());}
+tr.ui.analysis.MemoryColumn.spaceEqually(numericColumns);const columns=[titleColumn].concat(numericColumns);return columns;},getRows_(){let rows=this.nodes;if(rows.length<=RESONABLE_NUMBER_OF_ROWS){return[TabUiState.NO_LONG_TAIL,rows];}else if(this.displayLongTail){return[TabUiState.SHOWING_LONG_TAIL,rows];}
+const absSize=row=>Math.max(row.cells.Size.fields[0].value);rows.sort((a,b)=>absSize(b)-absSize(a));rows=rows.slice(0,RESONABLE_NUMBER_OF_ROWS);return[TabUiState.HIDING_LONG_TAIL,rows];},updateInfoBar_(state,rowStats){if(state===TabUiState.SHOWING_LONG_TAIL){this.longTailVisibleInfoBar_(rowStats);}else if(state===TabUiState.HIDING_LONG_TAIL){this.longTailHiddenInfoBar_(rowStats);}else{this.hideInfoBar_();}},longTailVisibleInfoBar_(rowStats){const[total,visible,hidden]=rowStats;const couldHide=total-RESONABLE_NUMBER_OF_ROWS;this.$.info.message='Showing '+total+' rows. This may be slow.';this.$.info.removeAllButtons();const buttonText='Hide '+couldHide+' rows.';this.$.info.addButton(buttonText,()=>this.displayLongTail=false);this.$.info.visible=true;},longTailHiddenInfoBar_(rowStats){const[total,visible,hidden]=rowStats;this.$.info.message='Hiding the smallest '+hidden+' rows.';this.$.info.removeAllButtons();this.$.info.addButton('Show all.',()=>this.displayLongTail=true);this.$.info.visible=true;},hideInfoBar_(){this.$.info.visible=false;},});return{};});'use strict';tr.exportTo('tr.ui.analysis',function(){const DOWNWARDS_ARROW_WITH_TIP_RIGHTWARDS=String.fromCharCode(0x21B3);function HeapDetailsPathColumn(title){tr.ui.analysis.HeapDetailsTitleColumn.call(this,title);}
+HeapDetailsPathColumn.prototype={__proto__:tr.ui.analysis.HeapDetailsTitleColumn.prototype,formatTitle(row){const title=tr.ui.analysis.HeapDetailsTitleColumn.prototype.formatTitle.call(this,row);if(row.dimension===tr.ui.analysis.HeapDetailsRowDimension.ROOT){return title;}
+const arrowEl=document.createElement('span');Polymer.dom(arrowEl).textContent=DOWNWARDS_ARROW_WITH_TIP_RIGHTWARDS;arrowEl.style.paddingRight='2px';arrowEl.style.fontWeight='bold';arrowEl.style.color=tr.b.ColorScheme.getColorForReservedNameAsString('heap_dump_child_node_arrow');const rowEl=document.createElement('span');Polymer.dom(rowEl).appendChild(arrowEl);Polymer.dom(rowEl).appendChild(tr.ui.b.asHTMLOrTextNode(title));return rowEl;}};Polymer({is:'tr-ui-a-memory-dump-heap-details-path-view',behaviors:[tr.ui.analysis.RebuildableBehavior],created(){this.selectedNode_=undefined;this.aggregationMode_=undefined;},ready(){this.$.table.addEventListener('selection-changed',function(event){this.selectedNode_=this.$.table.selectedTableRow;this.didSelectedNodeChange_();}.bind(this));},didSelectedNodeChange_(){this.dispatchEvent(new tr.b.Event('selected-node-changed'));},get selectedNode(){return this.selectedNode_;},set selectedNode(node){this.selectedNode_=node;this.didSelectedNodeChange_();this.scheduleRebuild_();},get aggregationMode(){return this.aggregationMode_;},set aggregationMode(aggregationMode){this.aggregationMode_=aggregationMode;this.scheduleRebuild_();},onRebuild_(){if(this.selectedNode_===undefined){this.$.table.clear();return;}
+if(this.$.table.tableRows.includes(this.selectedNode_)){this.$.table.selectedTableRow=this.selectedNode_;return;}
+this.$.table.selectionMode=tr.ui.b.TableFormat.SelectionMode.ROW;this.$.table.userCanModifySortOrder=false;const rows=this.createRows_(this.selectedNode_);this.$.table.tableRows=rows;this.$.table.tableColumns=this.createColumns_(rows);this.$.table.selectedTableRow=rows[rows.length-1];},createRows_(node){const rows=[];while(node){rows.push(node);node=node.parentNode;}
+rows.reverse();return rows;},createColumns_(rows){const titleColumn=new HeapDetailsPathColumn('Current path');titleColumn.width='200px';const numericColumns=tr.ui.analysis.MemoryColumn.fromRows(rows,{cellKey:'cells',aggregationMode:this.aggregationMode_,rules:tr.ui.analysis.HEAP_DETAILS_COLUMN_RULES,shouldSetContextGroup:true});tr.ui.analysis.MemoryColumn.spaceEqually(numericColumns);return[titleColumn].concat(numericColumns);}});return{};});'use strict';tr.exportTo('tr.ui.analysis',function(){const StackedPaneImpl={set childPaneBuilder(childPaneBuilder){this.childPaneBuilder_=childPaneBuilder;this.dispatchEvent(new tr.b.Event('request-child-pane-change'));},get childPaneBuilder(){return this.childPaneBuilder_;},appended(){this.rebuild();}};const StackedPane=[tr.ui.analysis.RebuildableBehavior,StackedPaneImpl];return{StackedPane,};});Polymer({is:'tr-ui-a-stacked-pane',behaviors:[tr.ui.analysis.StackedPane]});'use strict';tr.exportTo('tr.ui.analysis',function(){const Scalar=tr.b.Scalar;const sizeInBytes_smallerIsBetter=tr.b.Unit.byName.sizeInBytes_smallerIsBetter;const count_smallerIsBetter=tr.b.Unit.byName.count_smallerIsBetter;const MultiDimensionalViewBuilder=tr.b.MultiDimensionalViewBuilder;const TotalState=tr.b.MultiDimensionalViewNode.TotalState;function HeapDumpTreeNode(stackFrameNodes,dimension,title,heavyView,parentNode){this.dimension=dimension;this.title=title;this.parentNode=parentNode;this.heavyView_=heavyView;this.stackFrameNodes_=stackFrameNodes;this.lazyCells_=undefined;this.lazyChildNodes_=undefined;}
+HeapDumpTreeNode.prototype={get minDisplayedTotalState_(){if(this.heavyView_){return TotalState.LOWER_BOUND;}
+return TotalState.EXACT;},get childNodes(){if(!this.lazyChildNodes_){this.lazyChildNodes_=new Map();this.addDimensionChildNodes_(tr.ui.analysis.HeapDetailsRowDimension.STACK_FRAME,0);this.addDimensionChildNodes_(tr.ui.analysis.HeapDetailsRowDimension.OBJECT_TYPE,1);this.releaseStackFrameNodesIfPossible_();}
+return this.lazyChildNodes_;},get cells(){if(!this.lazyCells_){this.addCells_();this.releaseStackFrameNodesIfPossible_();}
+return this.lazyCells_;},releaseStackFrameNodesIfPossible_(){if(this.lazyCells_&&this.lazyChildNodes_){this.stackFrameNodes_=undefined;}},addDimensionChildNodes_(dimension,dimensionIndex){const dimensionChildTitleToStackFrameNodes=tr.b.invertArrayOfDicts(this.stackFrameNodes_,node=>this.convertStackFrameNodeDimensionToChildDict_(node,dimensionIndex));const dimensionChildNodes=[];for(const[childTitle,childStackFrameNodes]of
+Object.entries(dimensionChildTitleToStackFrameNodes)){dimensionChildNodes.push(new HeapDumpTreeNode(childStackFrameNodes,dimension,childTitle,this.heavyView_,this));}
+this.lazyChildNodes_.set(dimension,dimensionChildNodes);},convertStackFrameNodeDimensionToChildDict_(stackFrameNode,dimensionIndex){const childDict={};let displayedChildrenTotalSize=0;let displayedChildrenTotalCount=0;let hasDisplayedChildren=false;let allDisplayedChildrenHaveDisplayedCounts=true;for(const child of stackFrameNode.children[dimensionIndex].values()){if(child.values[0].totalState<this.minDisplayedTotalState_){continue;}
+if(child.values[1].totalState<this.minDisplayedTotalState_){allDisplayedChildrenHaveDisplayedCounts=false;}
+childDict[child.title[dimensionIndex]]=child;displayedChildrenTotalSize+=child.values[0].total;displayedChildrenTotalCount+=child.values[1].total;hasDisplayedChildren=true;}
+const nodeTotalSize=stackFrameNode.values[0].total;const nodeTotalCount=stackFrameNode.values[1].total;const hasUnclassifiedSizeOrCount=displayedChildrenTotalSize<nodeTotalSize||displayedChildrenTotalCount<nodeTotalCount;if(!this.heavyView_&&hasUnclassifiedSizeOrCount&&hasDisplayedChildren){const otherTitle=stackFrameNode.title.slice();otherTitle[dimensionIndex]='<other>';const otherNode=new tr.b.MultiDimensionalViewNode(otherTitle,2);childDict[otherTitle[dimensionIndex]]=otherNode;otherNode.values[0].total=nodeTotalSize-displayedChildrenTotalSize;otherNode.values[0].totalState=this.minDisplayedTotalState_;otherNode.values[1].total=nodeTotalCount-displayedChildrenTotalCount;otherNode.values[1].totalState=allDisplayedChildrenHaveDisplayedCounts?this.minDisplayedTotalState_:TotalState.NOT_PROVIDED;}
+return childDict;},addCells_(){this.lazyCells_=tr.ui.analysis.createCells(this.stackFrameNodes_,function(stackFrameNode){const size=stackFrameNode.values[0].total;const numerics={'Size':new Scalar(sizeInBytes_smallerIsBetter,size)};const countValue=stackFrameNode.values[1];if(countValue.totalState>=this.minDisplayedTotalState_){const count=countValue.total;numerics.Count=new Scalar(count_smallerIsBetter,count);}
+return numerics;},this);}};Polymer({is:'tr-ui-a-memory-dump-heap-details-pane',behaviors:[tr.ui.analysis.StackedPane],created(){this.heapDumps_=undefined;this.viewMode_=undefined;this.aggregationMode_=undefined;this.cachedBuilders_=new Map();},ready(){this.$.info_bar.message='Note: Values displayed in the heavy view '+'are lower bounds (except for the root).';Polymer.dom(this.$.view_mode_container).appendChild(tr.ui.b.createSelector(this,'viewMode','memoryDumpHeapDetailsPane.viewMode',MultiDimensionalViewBuilder.ViewType.TOP_DOWN_TREE_VIEW,[{label:'Top-down (Tree)',value:MultiDimensionalViewBuilder.ViewType.TOP_DOWN_TREE_VIEW},{label:'Top-down (Heavy)',value:MultiDimensionalViewBuilder.ViewType.TOP_DOWN_HEAVY_VIEW},{label:'Bottom-up (Heavy)',value:MultiDimensionalViewBuilder.ViewType.BOTTOM_UP_HEAVY_VIEW}]));this.$.drag_handle.target=this.$.path_view;this.$.drag_handle.horizontal=false;this.$.path_view.addEventListener('selected-node-changed',(function(e){this.$.breakdown_view.displayedNode=this.$.path_view.selectedNode;}).bind(this));this.$.breakdown_view.addEventListener('enter-node',(function(e){this.$.path_view.selectedNode=e.node;}).bind(this));},set heapDumps(heapDumps){this.heapDumps_=heapDumps;this.scheduleRebuild_();},get heapDumps(){return this.heapDumps_;},set aggregationMode(aggregationMode){this.aggregationMode_=aggregationMode;this.$.path_view.aggregationMode=aggregationMode;this.$.breakdown_view.aggregationMode=aggregationMode;},get aggregationMode(){return this.aggregationMode_;},set viewMode(viewMode){this.viewMode_=viewMode;this.scheduleRebuild_();},get viewMode(){return this.viewMode_;},get heavyView(){switch(this.viewMode){case MultiDimensionalViewBuilder.ViewType.TOP_DOWN_HEAVY_VIEW:case MultiDimensionalViewBuilder.ViewType.BOTTOM_UP_HEAVY_VIEW:return true;default:return false;}},onRebuild_(){if(this.heapDumps_===undefined||this.heapDumps_.length===0){this.$.info_text.style.display='block';this.$.split_view.style.display='none';this.$.view_mode_container.style.display='none';this.$.info_bar.hidden=true;this.$.path_view.selectedNode=undefined;return;}
+this.$.info_text.style.display='none';this.$.split_view.style.display='flex';this.$.view_mode_container.style.display='block';this.$.info_bar.hidden=!this.heavyView;this.$.path_view.selectedNode=this.createHeapTree_();this.$.path_view.rebuild();this.$.breakdown_view.rebuild();},createHeapTree_(){const definedHeapDump=this.heapDumps_.find(x=>x);if(definedHeapDump===undefined)return undefined;const rootRowTitle=definedHeapDump.allocatorName;const stackFrameTrees=this.createStackFrameTrees_(this.heapDumps_);return new HeapDumpTreeNode(stackFrameTrees,tr.ui.analysis.HeapDetailsRowDimension.ROOT,rootRowTitle,this.heavyView);},createStackFrameTrees_(heapDumps){const builders=heapDumps.map(heapDump=>this.createBuilder_(heapDump));const views=builders.map(builder=>{if(builder===undefined)return undefined;return builder.buildView(this.viewMode);});return views;},createBuilder_(heapDump){if(heapDump===undefined)return undefined;if(this.cachedBuilders_.has(heapDump)){return this.cachedBuilders_.get(heapDump);}
+const dimensions=2;const valueCount=2;const builder=new MultiDimensionalViewBuilder(dimensions,valueCount);for(const entry of heapDump.entries){const leafStackFrame=entry.leafStackFrame;const stackTracePath=leafStackFrame===undefined?[]:leafStackFrame.getUserFriendlyStackTrace().reverse();const objectTypeName=entry.objectTypeName;const objectTypeNamePath=objectTypeName===undefined?[]:[objectTypeName];const valueKind=entry.valuesAreTotals?MultiDimensionalViewBuilder.ValueKind.TOTAL:MultiDimensionalViewBuilder.ValueKind.SELF;builder.addPath([stackTracePath,objectTypeNamePath],[entry.size,entry.count],valueKind);}
+builder.complete=heapDump.isComplete;this.cachedBuilders_.set(heapDump,builder);return builder;},});return{};});'use strict';tr.exportTo('tr.ui.analysis',function(){const URL_TO_SIZE_VS_EFFECTIVE_SIZE='https://chromium.googlesource.com/chromium/src/+/master/docs/memory-infra/README.md#effective_size-vs_size';const SUBALLOCATION_CONTEXT=true;const MemoryAllocatorDumpInfoType=tr.model.MemoryAllocatorDumpInfoType;const PROVIDED_SIZE_LESS_THAN_AGGREGATED_CHILDREN=MemoryAllocatorDumpInfoType.PROVIDED_SIZE_LESS_THAN_AGGREGATED_CHILDREN;const PROVIDED_SIZE_LESS_THAN_LARGEST_OWNER=MemoryAllocatorDumpInfoType.PROVIDED_SIZE_LESS_THAN_LARGEST_OWNER;const LEFTWARDS_OPEN_HEADED_ARROW=String.fromCharCode(0x21FD);const RIGHTWARDS_OPEN_HEADED_ARROW=String.fromCharCode(0x21FE);const EN_DASH=String.fromCharCode(0x2013);const CIRCLED_LATIN_SMALL_LETTER_I=String.fromCharCode(0x24D8);function AllocatorDumpNameColumn(){tr.ui.analysis.TitleColumn.call(this,'Component');}
+AllocatorDumpNameColumn.prototype={__proto__:tr.ui.analysis.TitleColumn.prototype,formatTitle(row){if(!row.suballocation){return row.title;}
+return tr.ui.b.createSpan({textContent:row.title,italic:true,tooltip:row.fullNames===undefined?undefined:row.fullNames.join(', ')});}};function getAndUpdateEntry(map,name,createdCallback){let entry=map.get(name);if(entry===undefined){entry={count:0};createdCallback(entry);map.set(name,entry);}
+entry.count++;return entry;}
+function SizeInfoMessageBuilder(){this.parts_=[];this.indent_=0;}
+SizeInfoMessageBuilder.prototype={append(){this.parts_.push.apply(this.parts_,Array.prototype.slice.apply(arguments));},appendMap(map,hasPluralSuffix,emptyText,itemCallback,opt_this){opt_this=opt_this||this;if(map.size===0){if(emptyText){this.append(emptyText);}}else if(map.size===1){this.parts_.push(' ');const key=map.keys().next().value;itemCallback.call(opt_this,key,map.get(key));}else{if(hasPluralSuffix){this.parts_.push('s');}
+this.parts_.push(':');this.indent_++;for(const key of map.keys()){this.parts_.push('\n',' '.repeat(3*(this.indent_-1)),' - ');itemCallback.call(opt_this,key,map.get(key));}
+this.indent_--;}},appendImportanceRange(range){this.append(' (importance: ');if(range.min===range.max){this.append(range.min);}else{this.append(range.min,EN_DASH,range.max);}
+this.append(')');},appendSizeIfDefined(size){if(size!==undefined){this.append(' (',tr.b.Unit.byName.sizeInBytes.format(size),')');}},appendSomeTimestampsQuantifier(){this.append(' ',tr.ui.analysis.MemoryColumn.SOME_TIMESTAMPS_INFO_QUANTIFIER);},build(){return this.parts_.join('');}};function EffectiveSizeColumn(name,cellPath,aggregationMode){tr.ui.analysis.DetailsNumericMemoryColumn.call(this,name,cellPath,aggregationMode);}
+EffectiveSizeColumn.prototype={__proto__:tr.ui.analysis.DetailsNumericMemoryColumn.prototype,get title(){return tr.ui.b.createLink({textContent:this.name,tooltip:'Memory used by this component',href:URL_TO_SIZE_VS_EFFECTIVE_SIZE});},addInfos(numerics,memoryAllocatorDumps,infos){if(memoryAllocatorDumps===undefined)return;const ownerNameToEntry=new Map();const ownedNameToEntry=new Map();for(let i=0;i<numerics.length;i++){if(numerics[i]===undefined)continue;const dump=memoryAllocatorDumps[i];if(dump===SUBALLOCATION_CONTEXT){return;}
+dump.ownedBy.forEach(function(ownerLink){const ownerDump=ownerLink.source;this.getAndUpdateOwnershipEntry_(ownerNameToEntry,ownerDump,ownerLink);},this);const ownedLink=dump.owns;if(ownedLink!==undefined){const ownedDump=ownedLink.target;const ownedEntry=this.getAndUpdateOwnershipEntry_(ownedNameToEntry,ownedDump,ownedLink,true);const sharerNameToEntry=ownedEntry.sharerNameToEntry;ownedDump.ownedBy.forEach(function(sharerLink){const sharerDump=sharerLink.source;if(sharerDump===dump)return;this.getAndUpdateOwnershipEntry_(sharerNameToEntry,sharerDump,sharerLink);},this);}}
+if(ownerNameToEntry.size>0){const messageBuilder=new SizeInfoMessageBuilder();messageBuilder.append('shared by');messageBuilder.appendMap(ownerNameToEntry,false,undefined,function(ownerName,ownerEntry){messageBuilder.append(ownerName);if(ownerEntry.count<numerics.length){messageBuilder.appendSomeTimestampsQuantifier();}
+messageBuilder.appendImportanceRange(ownerEntry.importanceRange);},this);infos.push({message:messageBuilder.build(),icon:LEFTWARDS_OPEN_HEADED_ARROW,color:'green'});}
+if(ownedNameToEntry.size>0){const messageBuilder=new SizeInfoMessageBuilder();messageBuilder.append('shares');messageBuilder.appendMap(ownedNameToEntry,false,undefined,function(ownedName,ownedEntry){messageBuilder.append(ownedName);const ownedCount=ownedEntry.count;if(ownedCount<numerics.length){messageBuilder.appendSomeTimestampsQuantifier();}
+messageBuilder.appendImportanceRange(ownedEntry.importanceRange);messageBuilder.append(' with');messageBuilder.appendMap(ownedEntry.sharerNameToEntry,false,' no other dumps',function(sharerName,sharerEntry){messageBuilder.append(sharerName);if(sharerEntry.count<ownedCount){messageBuilder.appendSomeTimestampsQuantifier();}
+messageBuilder.appendImportanceRange(sharerEntry.importanceRange);},this);},this);infos.push({message:messageBuilder.build(),icon:RIGHTWARDS_OPEN_HEADED_ARROW,color:'green'});}},getAndUpdateOwnershipEntry_(map,dump,link,opt_withSharerNameToEntry){const entry=getAndUpdateEntry(map,dump.quantifiedName,function(newEntry){newEntry.importanceRange=new tr.b.math.Range();if(opt_withSharerNameToEntry){newEntry.sharerNameToEntry=new Map();}});entry.importanceRange.addValue(link.importance||0);return entry;}};function SizeColumn(name,cellPath,aggregationMode){tr.ui.analysis.DetailsNumericMemoryColumn.call(this,name,cellPath,aggregationMode);}
+SizeColumn.prototype={__proto__:tr.ui.analysis.DetailsNumericMemoryColumn.prototype,get title(){return tr.ui.b.createLink({textContent:this.name,tooltip:'Memory requested by this component',href:URL_TO_SIZE_VS_EFFECTIVE_SIZE});},addInfos(numerics,memoryAllocatorDumps,infos){if(memoryAllocatorDumps===undefined)return;this.addOverlapInfo_(numerics,memoryAllocatorDumps,infos);this.addProvidedSizeWarningInfos_(numerics,memoryAllocatorDumps,infos);},addOverlapInfo_(numerics,memoryAllocatorDumps,infos){const siblingNameToEntry=new Map();for(let i=0;i<numerics.length;i++){if(numerics[i]===undefined)continue;const dump=memoryAllocatorDumps[i];if(dump===SUBALLOCATION_CONTEXT){return;}
+const ownedBySiblingSizes=dump.ownedBySiblingSizes;for(const siblingDump of ownedBySiblingSizes.keys()){const siblingName=siblingDump.name;getAndUpdateEntry(siblingNameToEntry,siblingName,function(newEntry){if(numerics.length===1){newEntry.size=ownedBySiblingSizes.get(siblingDump);}});}}
+if(siblingNameToEntry.size>0){const messageBuilder=new SizeInfoMessageBuilder();messageBuilder.append('overlaps with its sibling');messageBuilder.appendMap(siblingNameToEntry,true,undefined,function(siblingName,siblingEntry){messageBuilder.append('\'',siblingName,'\'');messageBuilder.appendSizeIfDefined(siblingEntry.size);if(siblingEntry.count<numerics.length){messageBuilder.appendSomeTimestampsQuantifier();}},this);infos.push({message:messageBuilder.build(),icon:CIRCLED_LATIN_SMALL_LETTER_I,color:'blue'});}},addProvidedSizeWarningInfos_(numerics,memoryAllocatorDumps,infos){const infoTypeToEntry=new Map();for(let i=0;i<numerics.length;i++){if(numerics[i]===undefined)continue;const dump=memoryAllocatorDumps[i];if(dump===SUBALLOCATION_CONTEXT){return;}
+dump.infos.forEach(function(dumpInfo){getAndUpdateEntry(infoTypeToEntry,dumpInfo.type,function(newEntry){if(numerics.length===1){newEntry.providedSize=dumpInfo.providedSize;newEntry.dependencySize=dumpInfo.dependencySize;}});});}
+for(const infoType of infoTypeToEntry.keys()){const entry=infoTypeToEntry.get(infoType);const messageBuilder=new SizeInfoMessageBuilder();messageBuilder.append('provided size');messageBuilder.appendSizeIfDefined(entry.providedSize);let dependencyName;switch(infoType){case PROVIDED_SIZE_LESS_THAN_AGGREGATED_CHILDREN:dependencyName='the aggregated size of the children';break;case PROVIDED_SIZE_LESS_THAN_LARGEST_OWNER:dependencyName='the size of the largest owner';break;default:dependencyName='an unknown dependency';break;}
+messageBuilder.append(' was less than ',dependencyName);messageBuilder.appendSizeIfDefined(entry.dependencySize);if(entry.count<numerics.length){messageBuilder.appendSomeTimestampsQuantifier();}
+infos.push(tr.ui.analysis.createWarningInfo(messageBuilder.build()));}}};const NUMERIC_COLUMN_RULES=[{condition:tr.model.MemoryAllocatorDump.EFFECTIVE_SIZE_NUMERIC_NAME,importance:10,columnConstructor:EffectiveSizeColumn},{condition:tr.model.MemoryAllocatorDump.SIZE_NUMERIC_NAME,importance:9,columnConstructor:SizeColumn},{condition:'page_size',importance:0,columnConstructor:tr.ui.analysis.DetailsNumericMemoryColumn},{condition:/size/,importance:5,columnConstructor:tr.ui.analysis.DetailsNumericMemoryColumn},{importance:0,columnConstructor:tr.ui.analysis.DetailsNumericMemoryColumn}];const DIAGNOSTIC_COLUMN_RULES=[{importance:0,columnConstructor:tr.ui.analysis.StringMemoryColumn}];Polymer({is:'tr-ui-a-memory-dump-allocator-details-pane',behaviors:[tr.ui.analysis.StackedPane],created(){this.memoryAllocatorDumps_=undefined;this.heapDumps_=undefined;this.aggregationMode_=undefined;},ready(){this.$.table.selectionMode=tr.ui.b.TableFormat.SelectionMode.ROW;},set memoryAllocatorDumps(memoryAllocatorDumps){this.memoryAllocatorDumps_=memoryAllocatorDumps;this.scheduleRebuild_();},get memoryAllocatorDumps(){return this.memoryAllocatorDumps_;},set heapDumps(heapDumps){this.heapDumps_=heapDumps;this.scheduleRebuild_();},set aggregationMode(aggregationMode){this.aggregationMode_=aggregationMode;this.scheduleRebuild_();},get aggregationMode(){return this.aggregationMode_;},onRebuild_(){if(this.memoryAllocatorDumps_===undefined||this.memoryAllocatorDumps_.length===0){this.$.info_text.style.display='block';this.$.table.style.display='none';this.$.table.clear();this.$.table.rebuild();this.childPaneBuilder=undefined;return;}
+this.$.info_text.style.display='none';this.$.table.style.display='block';const rows=this.createRows_();const columns=this.createColumns_(rows);rows.forEach(function(rootRow){tr.ui.analysis.aggregateTableRowCellsRecursively(rootRow,columns,function(contexts){return contexts!==undefined&&contexts.some(function(context){return context===SUBALLOCATION_CONTEXT;});});});this.$.table.tableRows=rows;this.$.table.tableColumns=columns;this.$.table.rebuild();tr.ui.analysis.expandTableRowsRecursively(this.$.table);if(this.heapDumps_===undefined){this.childPaneBuilder=undefined;}else{this.childPaneBuilder=function(){const pane=document.createElement('tr-ui-a-memory-dump-heap-details-pane');pane.heapDumps=this.heapDumps_;pane.aggregationMode=this.aggregationMode_;return pane;}.bind(this);}},createRows_(){return[this.createAllocatorRowRecursively_(this.memoryAllocatorDumps_)];},createAllocatorRowRecursively_(dumps){const definedDump=dumps.find(x=>x);const title=definedDump.name;const fullName=definedDump.fullName;const numericCells=tr.ui.analysis.createCells(dumps,function(dump){return dump.numerics;});const diagnosticCells=tr.ui.analysis.createCells(dumps,function(dump){return dump.diagnostics;});let suballocatedBy=undefined;if(title.startsWith('__')){for(let i=0;i<dumps.length;i++){const dump=dumps[i];if(dump===undefined||dump.ownedBy.length===0){continue;}
+const ownerDump=dump.ownedBy[0].source;if(dump.ownedBy.length>1||dump.children.length>0||ownerDump.containerMemoryDump!==dump.containerMemoryDump){suballocatedBy=undefined;break;}
+if(suballocatedBy===undefined){suballocatedBy=ownerDump.fullName;}else if(suballocatedBy!==ownerDump.fullName){suballocatedBy=undefined;break;}}}
+const row={title,fullNames:[fullName],contexts:dumps,numericCells,diagnosticCells,suballocatedBy};const childDumpNameToDumps=tr.b.invertArrayOfDicts(dumps,function(dump){const results={};for(const child of dump.children){results[child.name]=child;}
+return results;});const subRows=[];let suballocationClassificationRootNode=undefined;for(const childDumps of Object.values(childDumpNameToDumps)){const childRow=this.createAllocatorRowRecursively_(childDumps);if(childRow.suballocatedBy===undefined){subRows.push(childRow);}else{suballocationClassificationRootNode=this.classifySuballocationRow_(childRow,suballocationClassificationRootNode);}}
+if(suballocationClassificationRootNode!==undefined){const suballocationRow=this.createSuballocationRowRecursively_('suballocations',suballocationClassificationRootNode);subRows.push(suballocationRow);}
+if(subRows.length>0){row.subRows=subRows;}
+return row;},classifySuballocationRow_(suballocationRow,rootNode){if(rootNode===undefined){rootNode={children:{},row:undefined};}
+const suballocationLevels=suballocationRow.suballocatedBy.split('/');let currentNode=rootNode;for(let i=0;i<suballocationLevels.length;i++){const suballocationLevel=suballocationLevels[i];let nextNode=currentNode.children[suballocationLevel];if(nextNode===undefined){currentNode.children[suballocationLevel]=nextNode={children:{},row:undefined};}
+currentNode=nextNode;}
+const existingRow=currentNode.row;if(existingRow!==undefined){for(let i=0;i<suballocationRow.contexts.length;i++){const newContext=suballocationRow.contexts[i];if(newContext===undefined)continue;if(existingRow.contexts[i]!==undefined){throw new Error('Multiple suballocations with the same owner name');}
+existingRow.contexts[i]=newContext;['numericCells','diagnosticCells'].forEach(function(cellKey){const suballocationCells=suballocationRow[cellKey];if(suballocationCells===undefined)return;for(const[cellName,cell]of Object.entries(suballocationCells)){if(cell===undefined)continue;const fields=cell.fields;if(fields===undefined)continue;const field=fields[i];if(field===undefined)continue;let existingCells=existingRow[cellKey];if(existingCells===undefined){existingCells={};existingRow[cellKey]=existingCells;}
+let existingCell=existingCells[cellName];if(existingCell===undefined){existingCell=new tr.ui.analysis.MemoryCell(new Array(fields.length));existingCells[cellName]=existingCell;}
+existingCell.fields[i]=field;}});}
+existingRow.fullNames.push.apply(existingRow.fullNames,suballocationRow.fullNames);}else{currentNode.row=suballocationRow;}
+return rootNode;},createSuballocationRowRecursively_(name,node){const childCount=Object.keys(node.children).length;if(childCount===0){if(node.row===undefined){throw new Error('Suballocation node must have a row or children');}
+const row=node.row;row.title=name;row.suballocation=true;return row;}
+const subRows=[];for(const[subName,subNode]of Object.entries(node.children)){subRows.push(this.createSuballocationRowRecursively_(subName,subNode));}
+if(node.row!==undefined){const row=node.row;row.title='<unspecified>';row.suballocation=true;subRows.unshift(row);}
+const contexts=new Array(subRows[0].contexts.length);for(let i=0;i<subRows.length;i++){subRows[i].contexts.forEach(function(subContext,index){if(subContext!==undefined){contexts[index]=SUBALLOCATION_CONTEXT;}});}
+return{title:name,suballocation:true,contexts,subRows};},createColumns_(rows){const titleColumn=new AllocatorDumpNameColumn();titleColumn.width='200px';const numericColumns=tr.ui.analysis.MemoryColumn.fromRows(rows,{cellKey:'numericCells',aggregationMode:this.aggregationMode_,rules:NUMERIC_COLUMN_RULES});const diagnosticColumns=tr.ui.analysis.MemoryColumn.fromRows(rows,{cellKey:'diagnosticCells',aggregationMode:this.aggregationMode_,rules:DIAGNOSTIC_COLUMN_RULES});const fieldColumns=numericColumns.concat(diagnosticColumns);tr.ui.analysis.MemoryColumn.spaceEqually(fieldColumns);const columns=[titleColumn].concat(fieldColumns);return columns;}});return{SUBALLOCATION_CONTEXT,AllocatorDumpNameColumn,EffectiveSizeColumn,SizeColumn,};});'use strict';tr.exportTo('tr.ui.analysis',function(){const Scalar=tr.b.Scalar;const sizeInBytes_smallerIsBetter=tr.b.Unit.byName.sizeInBytes_smallerIsBetter;const CONSTANT_COLUMN_RULES=[{condition:'Start address',importance:0,columnConstructor:tr.ui.analysis.StringMemoryColumn}];const VARIABLE_COLUMN_RULES=[{condition:'Virtual size',importance:7,columnConstructor:tr.ui.analysis.DetailsNumericMemoryColumn},{condition:'Protection flags',importance:6,columnConstructor:tr.ui.analysis.StringMemoryColumn},{condition:'PSS',importance:5,columnConstructor:tr.ui.analysis.DetailsNumericMemoryColumn},{condition:'Private dirty',importance:4,columnConstructor:tr.ui.analysis.DetailsNumericMemoryColumn},{condition:'Private clean',importance:3,columnConstructor:tr.ui.analysis.DetailsNumericMemoryColumn},{condition:'Shared dirty',importance:2,columnConstructor:tr.ui.analysis.DetailsNumericMemoryColumn},{condition:'Shared clean',importance:1,columnConstructor:tr.ui.analysis.DetailsNumericMemoryColumn},{condition:'Swapped',importance:0,columnConstructor:tr.ui.analysis.DetailsNumericMemoryColumn}];const BYTE_STAT_COLUMN_MAP={'proportionalResident':'PSS','privateDirtyResident':'Private dirty','privateCleanResident':'Private clean','sharedDirtyResident':'Shared dirty','sharedCleanResident':'Shared clean','swapped':'Swapped'};function hexString(address,is64BitAddress){if(address===undefined)return undefined;const hexPadding=is64BitAddress?'0000000000000000':'00000000';return(hexPadding+address.toString(16)).substr(-hexPadding.length);}
+function pruneEmptyRuleRows(row){if(row.subRows===undefined||row.subRows.length===0)return;if(row.subRows[0].rule===undefined){return;}
+row.subRows.forEach(pruneEmptyRuleRows);row.subRows=row.subRows.filter(function(subRow){return subRow.subRows.length>0;});}
+Polymer({is:'tr-ui-a-memory-dump-vm-regions-details-pane',behaviors:[tr.ui.analysis.StackedPane],created(){this.vmRegions_=undefined;this.aggregationMode_=undefined;},ready(){this.$.table.selectionMode=tr.ui.b.TableFormat.SelectionMode.ROW;},set vmRegions(vmRegions){this.vmRegions_=vmRegions;this.scheduleRebuild_();},get vmRegions(){return this.vmRegions_;},set aggregationMode(aggregationMode){this.aggregationMode_=aggregationMode;this.scheduleRebuild_();},get aggregationMode(){return this.aggregationMode_;},onRebuild_(){if(this.vmRegions_===undefined||this.vmRegions_.length===0){this.$.info_text.style.display='block';this.$.table.style.display='none';this.$.table.clear();this.$.table.rebuild();return;}
+this.$.info_text.style.display='none';this.$.table.style.display='block';const rows=this.createRows_(this.vmRegions_);const columns=this.createColumns_(rows);this.$.table.tableRows=rows;this.$.table.tableColumns=columns;this.$.table.rebuild();tr.ui.analysis.expandTableRowsRecursively(this.$.table);},createRows_(timeToVmRegionTree){const is64BitAddress=timeToVmRegionTree.some(function(vmRegionTree){if(vmRegionTree===undefined)return false;return vmRegionTree.someRegion(function(region){if(region.startAddress===undefined)return false;return region.startAddress>=4294967296;});});return[this.createClassificationNodeRow(timeToVmRegionTree,is64BitAddress)];},createClassificationNodeRow(timeToNode,is64BitAddress){const definedNode=timeToNode.find(x=>x);const childNodeIdToTimeToNode=Object.values(tr.b.invertArrayOfDicts(timeToNode,function(node){const children=node.children;if(children===undefined)return undefined;const childMap={};children.forEach(function(childNode){if(!childNode.hasRegions)return;childMap[childNode.title]=childNode;});return childMap;}));const childNodeSubRows=childNodeIdToTimeToNode.map(function(timeToChildNode){return this.createClassificationNodeRow(timeToChildNode,is64BitAddress);},this);const regionIdToTimeToRegion=Object.values(tr.b.invertArrayOfDicts(timeToNode,function(node){const regions=node.regions;if(regions===undefined)return undefined;const results={};for(const region of regions){results[region.uniqueIdWithinProcess]=region;}
+return results;}));const regionSubRows=regionIdToTimeToRegion.map(function(timeToRegion){return this.createRegionRow_(timeToRegion,is64BitAddress);},this);const subRows=childNodeSubRows.concat(regionSubRows);return{title:definedNode.title,contexts:timeToNode,variableCells:this.createVariableCells_(timeToNode),subRows};},createRegionRow_(timeToRegion,is64BitAddress){const definedRegion=timeToRegion.find(x=>x);return{title:definedRegion.mappedFile,contexts:timeToRegion,constantCells:this.createConstantCells_(definedRegion,is64BitAddress),variableCells:this.createVariableCells_(timeToRegion)};},createConstantCells_(definedRegion,is64BitAddress){return tr.ui.analysis.createCells([definedRegion],function(region){const startAddress=region.startAddress;if(startAddress===undefined)return undefined;return{'Start address':hexString(startAddress,is64BitAddress)};});},createVariableCells_(timeToRegion){return tr.ui.analysis.createCells(timeToRegion,function(region){const fields={};const sizeInBytes=region.sizeInBytes;if(sizeInBytes!==undefined){fields['Virtual size']=new Scalar(sizeInBytes_smallerIsBetter,sizeInBytes);}
+const protectionFlags=region.protectionFlagsToString;if(protectionFlags!==undefined){fields['Protection flags']=protectionFlags;}
+for(const[byteStatName,columnName]of
+Object.entries(BYTE_STAT_COLUMN_MAP)){const byteStat=region.byteStats[byteStatName];if(byteStat===undefined)continue;fields[columnName]=new Scalar(sizeInBytes_smallerIsBetter,byteStat);}
+return fields;});},createColumns_(rows){const titleColumn=new tr.ui.analysis.TitleColumn('Mapped file');titleColumn.width='200px';const constantColumns=tr.ui.analysis.MemoryColumn.fromRows(rows,{cellKey:'constantCells',aggregationMode:undefined,rules:CONSTANT_COLUMN_RULES});const variableColumns=tr.ui.analysis.MemoryColumn.fromRows(rows,{cellKey:'variableCells',aggregationMode:this.aggregationMode_,rules:VARIABLE_COLUMN_RULES});const fieldColumns=constantColumns.concat(variableColumns);tr.ui.analysis.MemoryColumn.spaceEqually(fieldColumns);const columns=[titleColumn].concat(fieldColumns);return columns;}});return{};});'use strict';Polymer({is:'tr-ui-b-color-legend',ready(){const blackSquareCharCode=9632;this.$.square.innerText=String.fromCharCode(blackSquareCharCode);this.label_=undefined;this.compoundEventSelectionState_=tr.model.CompoundEventSelectionState.NOT_SELECTED;},set compoundEventSelectionState(compoundEventSelectionState){this.compoundEventSelectionState_=compoundEventSelectionState;},get label(){return this.label_;},set label(label){if(label===undefined){this.setLabelAndColorId(undefined,undefined);return;}
+const colorId=tr.b.ColorScheme.getColorIdForGeneralPurposeString(label);this.setLabelAndColorId(label,colorId);},setLabelAndColorId(label,colorId){this.label_=label;Polymer.dom(this.$.label).textContent='';Polymer.dom(this.$.label).appendChild(tr.ui.b.asHTMLOrTextNode(label));if(colorId===undefined){this.$.square.style.color='initial';}else{this.$.square.style.color=tr.b.ColorScheme.colorsAsStrings[colorId];}}});'use strict';Polymer({is:'tr-ui-b-view-specific-brushing-state',get viewId(){return this.getAttribute('view-id');},set viewId(viewId){Polymer.dom(this).setAttribute('view-id',viewId);},get(){const viewId=this.viewId;if(!viewId){throw new Error('Element must have a view-id attribute!');}
+const brushingStateController=tr.c.BrushingStateController.getControllerForElement(this);if(!brushingStateController)return undefined;return brushingStateController.getViewSpecificBrushingState(viewId);},set(state){const viewId=this.viewId;if(!viewId){throw new Error('Element must have a view-id attribute!');}
+const brushingStateController=tr.c.BrushingStateController.getControllerForElement(this);if(!brushingStateController)return;brushingStateController.changeViewSpecificBrushingState(viewId,state);}});'use strict';tr.exportTo('tr.ui.analysis',function(){const MemoryColumnColorScheme=tr.b.MemoryColumnColorScheme;const Scalar=tr.b.Scalar;const sizeInBytes_smallerIsBetter=tr.b.Unit.byName.sizeInBytes_smallerIsBetter;const PLATFORM_SPECIFIC_TOTAL_NAME_SUFFIX='_bytes';const DISPLAYED_SIZE_NUMERIC_NAME=tr.model.MemoryAllocatorDump.DISPLAYED_SIZE_NUMERIC_NAME;const SOME_TIMESTAMPS_INFO_QUANTIFIER=tr.ui.analysis.MemoryColumn.SOME_TIMESTAMPS_INFO_QUANTIFIER;const RIGHTWARDS_ARROW_WITH_HOOK=String.fromCharCode(0x21AA);const RIGHTWARDS_ARROW_FROM_BAR=String.fromCharCode(0x21A6);const GREATER_THAN_OR_EQUAL_TO=String.fromCharCode(0x2265);const UNMARRIED_PARTNERSHIP_SYMBOL=String.fromCharCode(0x26AF);const TRIGRAM_FOR_HEAVEN=String.fromCharCode(0x2630);function lazyMap(list,fn,opt_this){opt_this=opt_this||this;let result=undefined;list.forEach(function(item,index){const value=fn.call(opt_this,item,index);if(value===undefined)return;if(result===undefined){result=new Array(list.length);}
+result[index]=value;});return result;}
+function ProcessNameColumn(){tr.ui.analysis.TitleColumn.call(this,'Process');}
+ProcessNameColumn.prototype={__proto__:tr.ui.analysis.TitleColumn.prototype,formatTitle(row){if(row.contexts===undefined){return row.title;}
+const titleEl=document.createElement('tr-ui-b-color-legend');titleEl.label=row.title;return titleEl;}};function UsedMemoryColumn(name,cellPath,aggregationMode){tr.ui.analysis.NumericMemoryColumn.call(this,name,cellPath,aggregationMode);}
+UsedMemoryColumn.COLOR=MemoryColumnColorScheme.getColor('used_memory_column').toString();UsedMemoryColumn.OLDER_COLOR=MemoryColumnColorScheme.getColor('older_used_memory_column').toString();UsedMemoryColumn.prototype={__proto__:tr.ui.analysis.NumericMemoryColumn.prototype,get title(){return tr.ui.b.createSpan({textContent:this.name,color:UsedMemoryColumn.COLOR});},getFormattingContext(unit){return{unitPrefix:tr.b.UnitPrefixScale.BINARY.MEBI};},color(numerics,processMemoryDumps){return UsedMemoryColumn.COLOR;},getChildPaneBuilder(processMemoryDumps){if(processMemoryDumps===undefined)return undefined;const vmRegions=lazyMap(processMemoryDumps,function(pmd){if(pmd===undefined)return undefined;return pmd.mostRecentVmRegions;});if(vmRegions===undefined)return undefined;return function(){const pane=document.createElement('tr-ui-a-memory-dump-vm-regions-details-pane');pane.vmRegions=vmRegions;pane.aggregationMode=this.aggregationMode;return pane;}.bind(this);}};function PeakMemoryColumn(name,cellPath,aggregationMode){UsedMemoryColumn.call(this,name,cellPath,aggregationMode);}
+PeakMemoryColumn.prototype={__proto__:UsedMemoryColumn.prototype,addInfos(numerics,processMemoryDumps,infos){if(processMemoryDumps===undefined)return;let resettableValueCount=0;let nonResettableValueCount=0;for(let i=0;i<numerics.length;i++){if(numerics[i]===undefined)continue;if(processMemoryDumps[i].arePeakResidentBytesResettable){resettableValueCount++;}else{nonResettableValueCount++;}}
+if(resettableValueCount>0&&nonResettableValueCount>0){infos.push(tr.ui.analysis.createWarningInfo('Both resettable and '+'non-resettable peak RSS values were provided by the process'));}else if(resettableValueCount>0){infos.push({icon:RIGHTWARDS_ARROW_WITH_HOOK,message:'Peak RSS since previous memory dump.'});}else{infos.push({icon:RIGHTWARDS_ARROW_FROM_BAR,message:'Peak RSS since process startup. Finer grained '+'peaks require a Linux kernel version '+
+GREATER_THAN_OR_EQUAL_TO+' 4.0.'});}}};function ByteStatColumn(name,cellPath,aggregationMode){UsedMemoryColumn.call(this,name,cellPath,aggregationMode);}
+ByteStatColumn.prototype={__proto__:UsedMemoryColumn.prototype,color(numerics,processMemoryDumps){if(processMemoryDumps===undefined){return UsedMemoryColumn.COLOR;}
+const allOlderValues=processMemoryDumps.every(function(processMemoryDump){if(processMemoryDump===undefined)return true;return!processMemoryDump.hasOwnVmRegions;});if(allOlderValues){return UsedMemoryColumn.OLDER_COLOR;}
+return UsedMemoryColumn.COLOR;},addInfos(numerics,processMemoryDumps,infos){if(processMemoryDumps===undefined)return;let olderValueCount=0;for(let i=0;i<numerics.length;i++){const processMemoryDump=processMemoryDumps[i];if(processMemoryDump!==undefined&&!processMemoryDump.hasOwnVmRegions){olderValueCount++;}}
+if(olderValueCount===0){return;}
+const infoQuantifier=olderValueCount<numerics.length?' '+SOME_TIMESTAMPS_INFO_QUANTIFIER:'';infos.push({message:'Older value'+infoQuantifier+' (only heavy (purple) memory dumps contain memory maps).',icon:UNMARRIED_PARTNERSHIP_SYMBOL});}};UsedMemoryColumn.RULES=[{condition:'Total resident',importance:10,columnConstructor:UsedMemoryColumn},{condition:'Peak total resident',importance:9,columnConstructor:PeakMemoryColumn},{condition:'PSS',importance:8,columnConstructor:ByteStatColumn},{condition:'Private dirty',importance:7,columnConstructor:ByteStatColumn},{condition:'Swapped',importance:6,columnConstructor:ByteStatColumn},{importance:0,columnConstructor:UsedMemoryColumn}];UsedMemoryColumn.TOTALS_MAP={'residentBytes':'Total resident','peakResidentBytes':'Peak total resident','privateFootprintBytes':'Private footprint',};UsedMemoryColumn.PLATFORM_SPECIFIC_TOTALS_MAP={'vm':'Total virtual','swp':'Swapped','pc':'Private clean','pd':'Private dirty','sc':'Shared clean','sd':'Shared dirty','gpu_egl':'GPU EGL','gpu_egl_pss':'GPU EGL PSS','gpu_gl':'GPU GL','gpu_gl_pss':'GPU GL PSS','gpu_etc':'GPU Other','gpu_etc_pss':'GPU Other PSS',};UsedMemoryColumn.BYTE_STAT_MAP={'proportionalResident':'PSS','privateDirtyResident':'Private dirty','swapped':'Swapped'};function AllocatorColumn(name,cellPath,aggregationMode){tr.ui.analysis.NumericMemoryColumn.call(this,name,cellPath,aggregationMode);}
+AllocatorColumn.prototype={__proto__:tr.ui.analysis.NumericMemoryColumn.prototype,get title(){const titleEl=document.createElement('tr-ui-b-color-legend');titleEl.label=this.name;return titleEl;},getFormattingContext(unit){return{unitPrefix:tr.b.UnitPrefixScale.BINARY.MEBI};},addInfos(numerics,processMemoryDumps,infos){if(processMemoryDumps===undefined)return;let heapDumpCount=0;let missingSizeCount=0;for(let i=0;i<processMemoryDumps.length;i++){const processMemoryDump=processMemoryDumps[i];if(processMemoryDump===undefined)continue;const heapDumps=processMemoryDump.heapDumps;if(heapDumps!==undefined&&heapDumps[this.name]!==undefined){heapDumpCount++;}
+const allocatorDump=processMemoryDump.getMemoryAllocatorDumpByFullName(this.name);if(allocatorDump!==undefined&&allocatorDump.numerics[DISPLAYED_SIZE_NUMERIC_NAME]===undefined){missingSizeCount++;}}
+if(heapDumpCount>0){const infoQuantifier=heapDumpCount<numerics.length?' '+SOME_TIMESTAMPS_INFO_QUANTIFIER:'';infos.push({message:'Heap dump provided'+infoQuantifier+'.',icon:TRIGRAM_FOR_HEAVEN});}
+if(missingSizeCount>0){const infoQuantifier=missingSizeCount<numerics.length?' '+SOME_TIMESTAMPS_INFO_QUANTIFIER:'';infos.push(tr.ui.analysis.createWarningInfo('Size was not provided'+infoQuantifier+'.'));}},getChildPaneBuilder(processMemoryDumps){if(processMemoryDumps===undefined)return undefined;const memoryAllocatorDumps=lazyMap(processMemoryDumps,function(pmd){if(pmd===undefined)return undefined;return pmd.getMemoryAllocatorDumpByFullName(this.name);},this);if(memoryAllocatorDumps===undefined)return undefined;const heapDumps=lazyMap(processMemoryDumps,function(pmd){if(pmd===undefined||pmd.heapDumps===undefined)return undefined;return pmd.heapDumps[this.name];},this);return function(){const pane=document.createElement('tr-ui-a-memory-dump-allocator-details-pane');pane.memoryAllocatorDumps=memoryAllocatorDumps;pane.heapDumps=heapDumps;pane.aggregationMode=this.aggregationMode;return pane;}.bind(this);}};function TracingColumn(name,cellPath,aggregationMode){AllocatorColumn.call(this,name,cellPath,aggregationMode);}
+TracingColumn.COLOR=MemoryColumnColorScheme.getColor('tracing_memory_column').toString();TracingColumn.prototype={__proto__:AllocatorColumn.prototype,get title(){return tr.ui.b.createSpan({textContent:this.name,color:TracingColumn.COLOR});},color(numerics,processMemoryDumps){return TracingColumn.COLOR;}};AllocatorColumn.RULES=[{condition:'tracing',importance:0,columnConstructor:TracingColumn},{importance:1,columnConstructor:AllocatorColumn}];Polymer({is:'tr-ui-a-memory-dump-overview-pane',behaviors:[tr.ui.analysis.StackedPane],created(){this.processMemoryDumps_=undefined;this.aggregationMode_=undefined;},ready(){this.$.table.selectionMode=tr.ui.b.TableFormat.SelectionMode.CELL;this.$.table.addEventListener('selection-changed',function(tableEvent){tableEvent.stopPropagation();this.changeChildPane_();}.bind(this));},set processMemoryDumps(processMemoryDumps){this.processMemoryDumps_=processMemoryDumps;this.scheduleRebuild_();},get processMemoryDumps(){return this.processMemoryDumps_;},set aggregationMode(aggregationMode){this.aggregationMode_=aggregationMode;this.scheduleRebuild_();},get aggregationMode(){return this.aggregationMode_;},get selectedMemoryCell(){if(this.processMemoryDumps_===undefined||this.processMemoryDumps_.length===0){return undefined;}
+const selectedTableRow=this.$.table.selectedTableRow;if(!selectedTableRow)return undefined;const selectedColumnIndex=this.$.table.selectedColumnIndex;if(selectedColumnIndex===undefined)return undefined;const selectedColumn=this.$.table.tableColumns[selectedColumnIndex];const selectedMemoryCell=selectedColumn.cell(selectedTableRow);return selectedMemoryCell;},changeChildPane_(){this.storeSelection_();this.childPaneBuilder=this.determineChildPaneBuilderFromSelection_();},determineChildPaneBuilderFromSelection_(){if(this.processMemoryDumps_===undefined||this.processMemoryDumps_.length===0){return undefined;}
+const selectedTableRow=this.$.table.selectedTableRow;if(!selectedTableRow)return undefined;const selectedColumnIndex=this.$.table.selectedColumnIndex;if(selectedColumnIndex===undefined)return undefined;const selectedColumn=this.$.table.tableColumns[selectedColumnIndex];return selectedColumn.getChildPaneBuilder(selectedTableRow.contexts);},onRebuild_(){if(this.processMemoryDumps_===undefined||this.processMemoryDumps_.length===0){this.$.info_text.style.display='block';this.$.table.style.display='none';this.$.table.clear();this.$.table.rebuild();return;}
+this.$.info_text.style.display='none';this.$.table.style.display='block';const rows=this.createRows_();const columns=this.createColumns_(rows);const footerRows=this.createFooterRows_(rows,columns);this.$.table.tableRows=rows;this.$.table.footerRows=footerRows;this.$.table.tableColumns=columns;this.$.table.rebuild();this.restoreSelection_();},createRows_(){const timeToPidToProcessMemoryDump=this.processMemoryDumps_;const pidToTimeToProcessMemoryDump=tr.b.invertArrayOfDicts(timeToPidToProcessMemoryDump);const rows=[];for(const[pid,timeToDump]of
+Object.entries(pidToTimeToProcessMemoryDump)){const process=timeToDump.find(x=>x).process;const usedMemoryCells=tr.ui.analysis.createCells(timeToDump,function(dump){const sizes={};const totals=dump.totals;if(totals!==undefined){for(const[totalName,cellName]of
+Object.entries(UsedMemoryColumn.TOTALS_MAP)){const total=totals[totalName];if(total===undefined)continue;sizes[cellName]=new Scalar(sizeInBytes_smallerIsBetter,total);}
+const platformSpecific=totals.platformSpecific;if(platformSpecific!==undefined){for(const[name,size]of Object.entries(platformSpecific)){let newName=name;if(UsedMemoryColumn.PLATFORM_SPECIFIC_TOTALS_MAP[name]===undefined){if(name.endsWith(PLATFORM_SPECIFIC_TOTAL_NAME_SUFFIX)){newName=name.substring(0,name.length-
+PLATFORM_SPECIFIC_TOTAL_NAME_SUFFIX.length);}
+newName=newName.replace('_',' ').trim();newName=newName.charAt(0).toUpperCase()+newName.slice(1);}else{newName=UsedMemoryColumn.PLATFORM_SPECIFIC_TOTALS_MAP[name];}
+sizes[newName]=new Scalar(sizeInBytes_smallerIsBetter,size);}}}
+const vmRegions=dump.mostRecentVmRegions;if(vmRegions!==undefined){for(const[byteStatName,cellName]of
+Object.entries(UsedMemoryColumn.BYTE_STAT_MAP)){const byteStat=vmRegions.byteStats[byteStatName];if(byteStat===undefined)continue;sizes[cellName]=new Scalar(sizeInBytes_smallerIsBetter,byteStat);}}
+return sizes;});const allocatorCells=tr.ui.analysis.createCells(timeToDump,function(dump){const memoryAllocatorDumps=dump.memoryAllocatorDumps;if(memoryAllocatorDumps===undefined)return undefined;const sizes={};memoryAllocatorDumps.forEach(function(allocatorDump){let rootDisplayedSizeNumeric=allocatorDump.numerics[DISPLAYED_SIZE_NUMERIC_NAME];if(rootDisplayedSizeNumeric===undefined){rootDisplayedSizeNumeric=new Scalar(sizeInBytes_smallerIsBetter,0);}
+sizes[allocatorDump.fullName]=rootDisplayedSizeNumeric;});return sizes;});rows.push({title:process.userFriendlyName,contexts:timeToDump,usedMemoryCells,allocatorCells});}
+return rows;},createFooterRows_(rows,columns){if(rows.length<=1)return[];const totalRow={title:'Total'};tr.ui.analysis.aggregateTableRowCells(totalRow,rows,columns);return[totalRow];},createColumns_(rows){const titleColumn=new ProcessNameColumn();titleColumn.width='200px';const usedMemorySizeColumns=tr.ui.analysis.MemoryColumn.fromRows(rows,{cellKey:'usedMemoryCells',aggregationMode:this.aggregationMode_,rules:UsedMemoryColumn.RULES});const allocatorSizeColumns=tr.ui.analysis.MemoryColumn.fromRows(rows,{cellKey:'allocatorCells',aggregationMode:this.aggregationMode_,rules:AllocatorColumn.RULES});const sizeColumns=usedMemorySizeColumns.concat(allocatorSizeColumns);tr.ui.analysis.MemoryColumn.spaceEqually(sizeColumns);const columns=[titleColumn].concat(sizeColumns);return columns;},storeSelection_(){let selectedRowTitle;const selectedRow=this.$.table.selectedTableRow;if(selectedRow!==undefined){selectedRowTitle=selectedRow.title;}
+let selectedColumnName;const selectedColumnIndex=this.$.table.selectedColumnIndex;if(selectedColumnIndex!==undefined){const selectedColumn=this.$.table.tableColumns[selectedColumnIndex];selectedColumnName=selectedColumn.name;}
+this.$.state.set({rowTitle:selectedRowTitle,columnName:selectedColumnName});},restoreSelection_(){const settings=this.$.state.get();if(settings===undefined||settings.rowTitle===undefined||settings.columnName===undefined){return;}
+const selectedColumnIndex=this.$.table.tableColumns.findIndex(col=>col.name===settings.columnName);if(selectedColumnIndex===-1)return;const selectedRowTitle=settings.rowTitle;const selectedRow=this.$.table.tableRows.find(row=>row.title===selectedRowTitle);if(selectedRow===undefined)return;this.$.table.selectedTableRow=selectedRow;this.$.table.selectedColumnIndex=selectedColumnIndex;}});return{ProcessNameColumn,UsedMemoryColumn,PeakMemoryColumn,ByteStatColumn,AllocatorColumn,TracingColumn,};});'use strict';tr.exportTo('tr.ui.analysis',function(){Polymer({is:'tr-ui-a-memory-dump-header-pane',behaviors:[tr.ui.analysis.StackedPane],created(){this.containerMemoryDumps_=undefined;},ready(){Polymer.dom(this.$.aggregation_mode_container).appendChild(tr.ui.b.createSelector(this,'aggregationMode','memoryDumpHeaderPane.aggregationMode',tr.ui.analysis.MemoryColumn.AggregationMode.DIFF,[{label:'Diff',value:tr.ui.analysis.MemoryColumn.AggregationMode.DIFF},{label:'Max',value:tr.ui.analysis.MemoryColumn.AggregationMode.MAX}]));},set containerMemoryDumps(containerMemoryDumps){this.containerMemoryDumps_=containerMemoryDumps;this.scheduleRebuild_();},get containerMemoryDumps(){return this.containerMemoryDumps_;},set aggregationMode(aggregationMode){this.aggregationMode_=aggregationMode;this.scheduleRebuild_();},get aggregationMode(){return this.aggregationMode_;},onRebuild_(){this.updateLabel_();this.updateAggregationModeSelector_();this.changeChildPane_();},updateLabel_(){Polymer.dom(this.$.label).textContent='';if(this.containerMemoryDumps_===undefined||this.containerMemoryDumps_.length<=0){Polymer.dom(this.$.label).textContent='No memory dumps selected';return;}
+const containerDumpCount=this.containerMemoryDumps_.length;const isMultiSelection=containerDumpCount>1;Polymer.dom(this.$.label).appendChild(document.createTextNode('Selected '+containerDumpCount+' memory dump'+
+(isMultiSelection?'s':'')+' in '+this.containerMemoryDumps_[0].containerName+' at '));Polymer.dom(this.$.label).appendChild(document.createTextNode(tr.b.Unit.byName.timeStampInMs.format(this.containerMemoryDumps_[0].start)));if(isMultiSelection){const ELLIPSIS=String.fromCharCode(8230);Polymer.dom(this.$.label).appendChild(document.createTextNode(ELLIPSIS));Polymer.dom(this.$.label).appendChild(document.createTextNode(tr.b.Unit.byName.timeStampInMs.format(this.containerMemoryDumps_[containerDumpCount-1].start)));}},updateAggregationModeSelector_(){let displayStyle;if(this.containerMemoryDumps_===undefined||this.containerMemoryDumps_.length<=1){displayStyle='none';}else{displayStyle='initial';}
+this.$.aggregation_mode_container.style.display=displayStyle;},changeChildPane_(){this.childPaneBuilder=function(){if(this.containerMemoryDumps_===undefined||this.containerMemoryDumps_.length<=0){return undefined;}
+const overviewPane=document.createElement('tr-ui-a-memory-dump-overview-pane');overviewPane.processMemoryDumps=this.containerMemoryDumps_.map(function(containerDump){return containerDump.processMemoryDumps;});overviewPane.aggregationMode=this.aggregationMode;return overviewPane;}.bind(this);}});return{};});'use strict';Polymer({is:'tr-ui-a-stacked-pane-view',setPaneBuilder(paneBuilder,opt_parentPane){const paneContainer=this.$.pane_container;if(opt_parentPane){if(!(opt_parentPane instanceof HTMLElement)){throw new Error('Parent pane must be an HTML element');}
+if(opt_parentPane.parentElement!==paneContainer){throw new Error('Parent pane must be a child of the pane container');}}
+while(Polymer.dom(paneContainer).lastElementChild!==null&&Polymer.dom(paneContainer).lastElementChild!==opt_parentPane){const removedPane=Polymer.dom(this.$.pane_container).lastElementChild;const listener=this.listeners_.get(removedPane);if(listener===undefined){throw new Error('No listener associated with pane');}
+this.listeners_.delete(removedPane);removedPane.removeEventListener('request-child-pane-change',listener);Polymer.dom(paneContainer).removeChild(removedPane);}
+if(opt_parentPane&&opt_parentPane.parentElement!==paneContainer){throw new Error('Parent pane was removed from the pane container');}
+if(!paneBuilder)return;const pane=paneBuilder();if(!pane)return;if(!(pane instanceof HTMLElement)){throw new Error('Pane must be an HTML element');}
+const listener=function(event){this.setPaneBuilder(pane.childPaneBuilder,pane);}.bind(this);if(!this.listeners_){this.listeners_=new WeakMap();}
+this.listeners_.set(pane,listener);pane.addEventListener('request-child-pane-change',listener);Polymer.dom(paneContainer).appendChild(pane);pane.appended();},rebuild(){let currentPane=Polymer.dom(this.$.pane_container).firstElementChild;while(currentPane){currentPane.rebuild();currentPane=currentPane.nextElementSibling;}},get panesForTesting(){const panes=[];let currentChild=Polymer.dom(this.$.pane_container).firstElementChild;while(currentChild){panes.push(currentChild);currentChild=currentChild.nextElementSibling;}
+return panes;}});'use strict';tr.exportTo('tr.ui.analysis',function(){Polymer({is:'tr-ui-a-container-memory-dump-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],set selection(selection){if(selection===undefined){this.currentSelection_=undefined;this.dumpsByContainerName_=undefined;this.updateContents_();return;}
+selection.forEach(function(event){if(!(event instanceof tr.model.ContainerMemoryDump)){throw new Error('Memory dump sub-view only supports container memory dumps');}});this.currentSelection_=selection;this.dumpsByContainerName_=tr.b.groupIntoMap(this.currentSelection_.toArray(),dump=>dump.containerName);for(const dumps of this.dumpsByContainerName_.values()){dumps.sort((a,b)=>a.start-b.start);}
+this.updateContents_();},get selection(){return this.currentSelection_;},get requiresTallView(){return true;},updateContents_(){Polymer.dom(this.$.content).textContent='';if(this.dumpsByContainerName_===undefined)return;const containerNames=Array.from(this.dumpsByContainerName_.keys());if(containerNames.length===0)return;if(containerNames.length>1){this.buildViewForMultipleContainerNames_();}else{this.buildViewForSingleContainerName_();}},buildViewForSingleContainerName_(){const containerMemoryDumps=tr.b.getFirstElement(this.dumpsByContainerName_.values());const dumpView=unwrap(this.ownerDocument).createElement('tr-ui-a-stacked-pane-view');Polymer.dom(this.$.content).appendChild(dumpView);dumpView.setPaneBuilder(function(){const headerPane=document.createElement('tr-ui-a-memory-dump-header-pane');headerPane.containerMemoryDumps=containerMemoryDumps;return headerPane;});},buildViewForMultipleContainerNames_(){const ownerDocument=this.ownerDocument;const rows=[];for(const[containerName,dumps]of this.dumpsByContainerName_){rows.push({containerName,subRows:dumps,isExpanded:true,});}
+rows.sort(function(a,b){return a.containerName.localeCompare(b.containerName);});const columns=[{title:'Dump',value(row){if(row.subRows===undefined){return this.singleDumpValue_(row);}
+return this.groupedDumpValue_(row);},singleDumpValue_(row){const linkEl=unwrap(ownerDocument).createElement('tr-ui-a-analysis-link');linkEl.setSelectionAndContent(new tr.model.EventSet([row]));Polymer.dom(linkEl).appendChild(tr.v.ui.createScalarSpan(row.start,{unit:tr.b.Unit.byName.timeStampInMs,ownerDocument}));return linkEl;},groupedDumpValue_(row){const linkEl=unwrap(ownerDocument).createElement('tr-ui-a-analysis-link');linkEl.setSelectionAndContent(new tr.model.EventSet(row.subRows));Polymer.dom(linkEl).appendChild(tr.ui.b.createSpan({ownerDocument,textContent:row.subRows.length+' memory dump'+
+(row.subRows.length===1?'':'s')+' in '}));Polymer.dom(linkEl).appendChild(tr.ui.b.createSpan({ownerDocument,textContent:row.containerName,bold:true}));return linkEl;}}];const table=unwrap(this.ownerDocument).createElement('tr-ui-b-table');table.tableColumns=columns;table.tableRows=rows;table.showHeader=false;table.rebuild();Polymer.dom(this.$.content).appendChild(table);}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-container-memory-dump-sub-view',tr.model.GlobalMemoryDump,{multi:false,title:'Global Memory Dump',});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-container-memory-dump-sub-view',tr.model.GlobalMemoryDump,{multi:true,title:'Global Memory Dumps',});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-container-memory-dump-sub-view',tr.model.ProcessMemoryDump,{multi:false,title:'Process Memory Dump',});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-container-memory-dump-sub-view',tr.model.ProcessMemoryDump,{multi:true,title:'Process Memory Dumps',});return{};});'use strict';(function(){const COUNTER_SAMPLE_TABLE_COLUMNS=[{title:'Counter',width:'150px',value(row){return row.counter;}},{title:'Series',width:'150px',value(row){return row.series;}},{title:'Time',width:'150px',value(row){return row.start;}},{title:'Value',width:'100%',value(row){return row.value;}}];Polymer({is:'tr-ui-a-counter-sample-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],ready(){this.currentSelection_=undefined;this.$.table.tableColumns=COUNTER_SAMPLE_TABLE_COLUMNS;},get selection(){return this.currentSelection_;},set selection(selection){this.currentSelection_=selection;this.updateContents_();},updateContents_(){this.$.table.tableRows=this.selection?this.getRows_(this.selection.toArray()):[];this.$.table.rebuild();},getRows_(samples){const samplesByCounter=tr.b.groupIntoMap(samples,sample=>sample.series.counter.guid);const rows=[];for(const counterSamples of samplesByCounter.values()){const samplesBySeries=tr.b.groupIntoMap(counterSamples,sample=>sample.series.guid);for(const seriesSamples of samplesBySeries.values()){const seriesRows=this.getRowsForSamples_(seriesSamples);seriesRows[0].counter=seriesSamples[0].series.counter.name;seriesRows[0].series=seriesSamples[0].series.name;if(seriesRows.length>1){seriesRows[0].subRows=seriesRows.slice(1);seriesRows[0].isExpanded=true;}
+rows.push(seriesRows[0]);}}
+return rows;},getRowsForSamples_(samples){return samples.map(function(sample){return{start:sample.timestamp,value:sample.value};});}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-counter-sample-sub-view',tr.model.CounterSample,{multi:false,title:'Counter Sample',});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-counter-sample-sub-view',tr.model.CounterSample,{multi:true,title:'Counter Samples',});})();'use strict';tr.exportTo('tr.ui.analysis',function(){function MultiEventSummary(title,events){this.title=title;this.duration_=undefined;this.selfTime_=undefined;this.events_=events;this.cpuTimesComputed_=false;this.cpuSelfTime_=undefined;this.cpuDuration_=undefined;this.maxDuration_=undefined;this.maxCpuDuration_=undefined;this.maxSelfTime_=undefined;this.maxCpuSelfTime_=undefined;this.untotallableArgs_=[];this.totalledArgs_=undefined;}
+MultiEventSummary.prototype={set title(title){if(title==='Totals'){this.totalsRow=true;}
+this.title_=title;},get title(){return this.title_;},get duration(){if(this.duration_===undefined){this.duration_=tr.b.math.Statistics.sum(this.events_,function(event){return event.duration;});}
+return this.duration_;},get cpuSelfTime(){this.computeCpuTimesIfNeeded_();return this.cpuSelfTime_;},get cpuDuration(){this.computeCpuTimesIfNeeded_();return this.cpuDuration_;},computeCpuTimesIfNeeded_(){if(this.cpuTimesComputed_)return;this.cpuTimesComputed_=true;let cpuSelfTime=0;let cpuDuration=0;let hasCpuData=false;for(const event of this.events_){if(event.cpuDuration!==undefined){cpuDuration+=event.cpuDuration;hasCpuData=true;}
+if(event.cpuSelfTime!==undefined){cpuSelfTime+=event.cpuSelfTime;hasCpuData=true;}}
+if(hasCpuData){this.cpuDuration_=cpuDuration;this.cpuSelfTime_=cpuSelfTime;}},get selfTime(){if(this.selfTime_===undefined){this.selfTime_=0;for(const event of this.events_){if(event.selfTime!==undefined){this.selfTime_+=event.selfTime;}}}
+return this.selfTime_;},get events(){return this.events_;},get numEvents(){return this.events_.length;},get numAlerts(){if(this.numAlerts_===undefined){this.numAlerts_=tr.b.math.Statistics.sum(this.events_,event=>event.associatedAlerts.length);}
+return this.numAlerts_;},get untotallableArgs(){this.updateArgsIfNeeded_();return this.untotallableArgs_;},get totalledArgs(){this.updateArgsIfNeeded_();return this.totalledArgs_;},get maxDuration(){if(this.maxDuration_===undefined){this.maxDuration_=tr.b.math.Statistics.max(this.events_,function(event){return event.duration;});}
+return this.maxDuration_;},get maxCpuDuration(){if(this.maxCpuDuration_===undefined){this.maxCpuDuration_=tr.b.math.Statistics.max(this.events_,function(event){return event.cpuDuration;});}
+return this.maxCpuDuration_;},get maxSelfTime(){if(this.maxSelfTime_===undefined){this.maxSelfTime_=tr.b.math.Statistics.max(this.events_,function(event){return event.selfTime;});}
+return this.maxSelfTime_;},get maxCpuSelfTime(){if(this.maxCpuSelfTime_===undefined){this.maxCpuSelfTime_=tr.b.math.Statistics.max(this.events_,function(event){return event.cpuSelfTime;});}
+return this.maxCpuSelfTime_;},updateArgsIfNeeded_(){if(this.totalledArgs_!==undefined)return;const untotallableArgs={};const totalledArgs={};for(const event of this.events_){for(const argName in event.args){const argVal=event.args[argName];const type=typeof argVal;if(type!=='number'){untotallableArgs[argName]=true;delete totalledArgs[argName];continue;}
+if(untotallableArgs[argName]){continue;}
+if(totalledArgs[argName]===undefined){totalledArgs[argName]=0;}
+totalledArgs[argName]+=argVal;}}
+this.untotallableArgs_=Object.keys(untotallableArgs);this.totalledArgs_=totalledArgs;}};return{MultiEventSummary,};});'use strict';Polymer({is:'tr-ui-a-multi-event-summary-table',ready(){this.showTotals_=false;this.eventsHaveDuration_=true;this.eventsHaveSubRows_=true;this.eventsByTitle_=undefined;},updateTableColumns_(rows,maxValues){let hasCpuData=false;let hasAlerts=false;rows.forEach(function(row){if(row.cpuDuration!==undefined){hasCpuData=true;}
+if(row.cpuSelfTime!==undefined){hasCpuData=true;}
+if(row.numAlerts){hasAlerts=true;}});const ownerDocument=this.ownerDocument;const columns=[];columns.push({title:'Name',value(row){if(row.title==='Totals')return'Totals';const container=document.createElement('div');const linkEl=document.createElement('tr-ui-a-analysis-link');linkEl.setSelectionAndContent(function(){return new tr.model.EventSet(row.events);},row.title);container.appendChild(linkEl);if(tr.isExported('tr-ui-e-chrome-codesearch')){const link=document.createElement('tr-ui-e-chrome-codesearch');link.searchPhrase=row.title;container.appendChild(link);}
+return container;},width:'350px',cmp(rowA,rowB){return rowA.title.localeCompare(rowB.title);}});if(this.eventsHaveDuration_){columns.push({title:'Wall Duration',value(row){return tr.v.ui.createScalarSpan(row.duration,{unit:tr.b.Unit.byName.timeDurationInMs,customContextRange:row.totalsRow?undefined:tr.b.math.Range.fromExplicitRange(0,maxValues.duration),ownerDocument,});},width:'<upated further down>',cmp(rowA,rowB){return rowA.duration-rowB.duration;}});}
+if(this.eventsHaveDuration_&&hasCpuData){columns.push({title:'CPU Duration',value(row){return tr.v.ui.createScalarSpan(row.cpuDuration,{unit:tr.b.Unit.byName.timeDurationInMs,customContextRange:row.totalsRow?undefined:tr.b.math.Range.fromExplicitRange(0,maxValues.cpuDuration),ownerDocument,});},width:'<upated further down>',cmp(rowA,rowB){return rowA.cpuDuration-rowB.cpuDuration;}});}
+if(this.eventsHaveSubRows_&&this.eventsHaveDuration_){columns.push({title:'Self time',value(row){return tr.v.ui.createScalarSpan(row.selfTime,{unit:tr.b.Unit.byName.timeDurationInMs,customContextRange:row.totalsRow?undefined:tr.b.math.Range.fromExplicitRange(0,maxValues.selfTime),ownerDocument,});},width:'<upated further down>',cmp(rowA,rowB){return rowA.selfTime-rowB.selfTime;}});}
+if(this.eventsHaveSubRows_&&this.eventsHaveDuration_&&hasCpuData){columns.push({title:'CPU Self Time',value(row){return tr.v.ui.createScalarSpan(row.cpuSelfTime,{unit:tr.b.Unit.byName.timeDurationInMs,customContextRange:row.totalsRow?undefined:tr.b.math.Range.fromExplicitRange(0,maxValues.cpuSelfTime),ownerDocument,});},width:'<upated further down>',cmp(rowA,rowB){return rowA.cpuSelfTime-rowB.cpuSelfTime;}});}
+if(this.eventsHaveDuration_){columns.push({title:'Average '+(hasCpuData?'CPU':'Wall')+' Duration',value(row){const totalDuration=hasCpuData?row.cpuDuration:row.duration;return tr.v.ui.createScalarSpan(totalDuration/row.numEvents,{unit:tr.b.Unit.byName.timeDurationInMs,customContextRange:row.totalsRow?undefined:tr.b.math.Range.fromExplicitRange(0,maxValues.duration),ownerDocument,});},width:'<upated further down>',cmp(rowA,rowB){if(hasCpuData){return rowA.cpuDuration/rowA.numEvents-
+rowB.cpuDuration/rowB.numEvents;}
+return rowA.duration/rowA.numEvents-
+rowB.duration/rowB.numEvents;}});}
+columns.push({title:'Occurrences',value(row){return row.numEvents;},width:'<upated further down>',cmp(rowA,rowB){return rowA.numEvents-rowB.numEvents;}});let alertsColumnIndex;if(hasAlerts){columns.push({title:'Num Alerts',value(row){return row.numAlerts;},width:'<upated further down>',cmp(rowA,rowB){return rowA.numAlerts-rowB.numAlerts;}});alertsColumnIndex=columns.length-1;}
+let colWidthPercentage;if(columns.length===1){colWidthPercentage='100%';}else{colWidthPercentage=(100/(columns.length-1)).toFixed(3)+'%';}
+for(let i=1;i<columns.length;i++){columns[i].width=colWidthPercentage;}
+this.$.table.tableColumns=columns;if(hasAlerts){this.$.table.sortColumnIndex=alertsColumnIndex;this.$.table.sortDescending=true;}},configure(config){if(config.eventsByTitle===undefined){throw new Error('Required: eventsByTitle');}
+if(config.showTotals!==undefined){this.showTotals_=config.showTotals;}else{this.showTotals_=true;}
+if(config.eventsHaveDuration!==undefined){this.eventsHaveDuration_=config.eventsHaveDuration;}else{this.eventsHaveDuration_=true;}
+if(config.eventsHaveSubRows!==undefined){this.eventsHaveSubRows_=config.eventsHaveSubRows;}else{this.eventsHaveSubRows_=true;}
+this.eventsByTitle_=config.eventsByTitle;this.updateContents_();},get showTotals(){return this.showTotals_;},set showTotals(showTotals){this.showTotals_=showTotals;this.updateContents_();},get eventsHaveDuration(){return this.eventsHaveDuration_;},set eventsHaveDuration(eventsHaveDuration){this.eventsHaveDuration_=eventsHaveDuration;this.updateContents_();},get eventsHaveSubRows(){return this.eventsHaveSubRows_;},set eventsHaveSubRows(eventsHaveSubRows){this.eventsHaveSubRows_=eventsHaveSubRows;this.updateContents_();},get eventsByTitle(){return this.eventsByTitle_;},set eventsByTitle(eventsByTitle){this.eventsByTitle_=eventsByTitle;this.updateContents_();},get selectionBounds(){return this.selectionBounds_;},set selectionBounds(selectionBounds){this.selectionBounds_=selectionBounds;this.updateContents_();},updateContents_(){let eventsByTitle;if(this.eventsByTitle_!==undefined){eventsByTitle=this.eventsByTitle_;}else{eventsByTitle=[];}
+const allEvents=new tr.model.EventSet();const rows=[];for(const[title,eventsOfSingleTitle]of Object.entries(eventsByTitle)){for(const event of eventsOfSingleTitle)allEvents.push(event);const row=new tr.ui.analysis.MultiEventSummary(title,eventsOfSingleTitle);rows.push(row);}
+this.updateTableColumns_(rows);this.$.table.tableRows=rows;const maxValues={duration:undefined,selfTime:undefined,cpuSelfTime:undefined,cpuDuration:undefined};if(this.eventsHaveDuration){for(const column in maxValues){maxValues[column]=tr.b.math.Statistics.max(rows,function(event){return event[column];});}}
+const footerRows=[];if(this.showTotals_){const multiEventSummary=new tr.ui.analysis.MultiEventSummary('Totals',allEvents);footerRows.push(multiEventSummary);}
+this.updateTableColumns_(rows,maxValues);this.$.table.tableRows=rows;this.$.table.footerRows=footerRows;this.$.table.rebuild();}});'use strict';Polymer({is:'tr-ui-a-selection-summary-table',created(){this.selection_=new tr.b.math.Range();},ready(){this.$.table.showHeader=false;this.$.table.tableColumns=[{title:'Name',value(row){return row.title;},width:'350px'},{title:'Value',width:'100%',value(row){return row.value;}}];},get selection(){return this.selection_;},set selection(selection){this.selection_=selection;this.updateContents_();},updateContents_(){const selection=this.selection_;const rows=[];let hasRange;if(this.selection_&&(!selection.bounds.isEmpty)){hasRange=true;}else{hasRange=false;}
+rows.push({title:'Selection start',value:hasRange?tr.v.ui.createScalarSpan(selection.bounds.min,{unit:tr.b.Unit.byName.timeStampInMs,ownerDocument:this.ownerDocument}):'<empty>'});rows.push({title:'Selection extent',value:hasRange?tr.v.ui.createScalarSpan(selection.bounds.range,{unit:tr.b.Unit.byName.timeDurationInMs,ownerDocument:this.ownerDocument}):'<empty>'});this.$.table.tableRows=rows;this.$.table.rebuild();}});'use strict';Polymer({is:'tr-ui-b-radio-picker',created(){this.needsInit_=true;this.settingsKey_=undefined;this.isReady_=false;this.radioButtons_=undefined;this.selectedKey_=undefined;},ready(){this.isReady_=true;this.maybeInit_();this.maybeRenderRadioButtons_();},get vertical(){return this.getAttribute('vertical');},set vertical(vertical){if(vertical){this.setAttribute('vertical',true);}else{this.removeAttribute('vertical');}},get settingsKey(){return this.settingsKey_;},set settingsKey(settingsKey){if(!this.needsInit_){throw new Error('Already initialized.');}
+this.settingsKey_=settingsKey;this.maybeInit_();},maybeInit_(){if(!this.needsInit_)return;if(this.settingsKey_===undefined)return;this.needsInit_=false;this.select(tr.b.Settings.get(this.settingsKey_));},set items(items){this.radioButtons_={};items.forEach(function(e){if(e.key in this.radioButtons_){throw new Error(e.key+' already exists');}
+const radioButton=document.createElement('div');const input=document.createElement('input');const label=document.createElement('label');input.type='radio';input.id=e.label;input.addEventListener('click',function(){this.select(e.key);}.bind(this));Polymer.dom(label).innerHTML=e.label;label.htmlFor=e.label;label.style.display='inline';Polymer.dom(radioButton).appendChild(input);Polymer.dom(radioButton).appendChild(label);this.radioButtons_[e.key]=input;}.bind(this));this.maybeInit_();this.maybeRenderRadioButtons_();},maybeRenderRadioButtons_(){if(!this.isReady_)return;if(this.radioButtons_===undefined)return;for(const key in this.radioButtons_){Polymer.dom(this.$.container).appendChild(this.radioButtons_[key].parentElement);}
+if(this.selectedKey_!==undefined){this.select(this.selectedKey_);}},select(key){if(key===undefined||key===this.selectedKey_){return;}
+if(this.radioButtons_===undefined){this.selectedKey_=key;return;}
+if(!(key in this.radioButtons_)){throw new Error(key+' does not exists');}
+if(this.selectedKey_!==undefined){this.radioButtons_[this.selectedKey_].checked=false;}
+this.selectedKey_=key;tr.b.Settings.set(this.settingsKey_,this.selectedKey_);if(this.selectedKey_!==undefined){this.radioButtons_[this.selectedKey_].checked=true;}
+this.dispatchEvent(new tr.b.Event('change',false));},get selectedKey(){return this.selectedKey_;},});'use strict';tr.exportTo('tr.ui.b',function(){const MIN_GUIDELINE_HEIGHT_PX=3;const CHECKBOX_WIDTH_PX=18;const NameColumnChart=tr.ui.b.define('name-column-chart',tr.ui.b.ColumnChart);NameColumnChart.prototype={__proto__:tr.ui.b.ColumnChart.prototype,get xAxisHeight(){return 5+(this.textHeightPx_*this.data_.length);},updateMargins_(){super.updateMargins_();let xAxisTickOverhangPx=0;for(let i=0;i<this.data_.length;++i){const datum=this.data_[i];xAxisTickOverhangPx=Math.max(xAxisTickOverhangPx,this.xScale_(i)+tr.ui.b.getSVGTextSize(this,datum.x).width-
+this.graphWidth);}
+this.margin.right=Math.max(this.margin.right,xAxisTickOverhangPx);},getXForDatum_(datum,index){return index;},get xAxisTickOffset(){return 0.5;},updateXAxis_(xAxis){xAxis.selectAll('*').remove();if(this.hideXAxis)return;const nameTexts=xAxis.selectAll('text').data(this.data_);nameTexts.enter().append('text').attr('transform',(d,index)=>'translate(0, '+
+this.textHeightPx_*(this.data_.length-index)+')').attr('x',(d,index)=>this.xScale_(index)).attr('y',d=>this.graphHeight).text(d=>d.x);nameTexts.exit().remove();const guideLines=xAxis.selectAll('line.guide').data(this.data_);guideLines.enter().append('line').attr('x1',(d,index)=>this.xScale_(index+this.xAxisTickOffset)).attr('x2',(d,index)=>this.xScale_(index+this.xAxisTickOffset)).attr('y1',()=>this.graphHeight).attr('y2',(d,index)=>this.graphHeight+Math.max(MIN_GUIDELINE_HEIGHT_PX,(this.textHeightPx_*(this.data_.length-index-1))));}};return{NameColumnChart,};});'use strict';tr.exportTo('tr.ui.b',function(){const LineChart=tr.ui.b.LineChart;const NameLineChart=tr.ui.b.define('name-line-chart',LineChart);NameLineChart.prototype={__proto__:LineChart.prototype,getXForDatum_(datum,index){return index;},get xAxisHeight(){return 5+(this.textHeightPx_*this.data_.length);},get xAxisTickOffset(){return 0;},updateMargins_(){tr.ui.b.NameColumnChart.prototype.updateMargins_.call(this);},updateXAxis_(xAxis){xAxis.selectAll('*').remove();if(this.hideXAxis)return;tr.ui.b.NameColumnChart.prototype.updateXAxis_.call(this,xAxis);const baseline=xAxis.selectAll('path').data([this]);baseline.enter().append('line').attr('stroke','black').attr('x1',this.xScale_(0)).attr('x2',this.xScale_(this.data_.length-1)).attr('y1',this.graphHeight).attr('y2',this.graphHeight);baseline.exit().remove();}};return{NameLineChart,};});'use strict';tr.exportTo('tr.ui.b',function(){const BoxChart=tr.ui.b.define('box-chart',tr.ui.b.NameLineChart);BoxChart.prototype={__proto__:tr.ui.b.NameLineChart.prototype,get hideLegend(){return true;},updateDataRange_(){if(this.overrideDataRange_!==undefined){return;}
+this.autoDataRange_.reset();for(const datum of this.data_){this.autoDataRange_.addValue(datum.percentile_0);this.autoDataRange_.addValue(datum.percentile_100);}},updateScales_(){super.updateScales_();this.xScale_.domain([0,this.data_.length]);},get xAxisTickOffset(){return 0.5;},updateDataRange_(){if(this.overrideDataRange_!==undefined)return;this.autoDataRange_.reset();for(const datum of this.data_){this.autoDataRange_.addValue(datum.percentile_0);this.autoDataRange_.addValue(datum.percentile_100);}},updateXAxis_(xAxis){xAxis.selectAll('*').remove();if(this.hideXAxis)return;tr.ui.b.NameColumnChart.prototype.updateXAxis_.call(this,xAxis);const baseline=xAxis.selectAll('path').data([this]);baseline.enter().append('line').attr('stroke','black').attr('x1',this.xScale_(0)).attr('x2',this.xScale_(this.data_.length)).attr('y1',this.graphHeight).attr('y2',this.graphHeight);baseline.exit().remove();},updateDataContents_(dataSel){dataSel.selectAll('*').remove();const boxesSel=dataSel.selectAll('path');for(let index=0;index<this.data_.length;++index){const datum=this.data_[index];const color=datum.color||'black';let sel=boxesSel.data([datum]);sel.enter().append('rect').attr('fill',color).attr('x',this.xScale_(index+0.2)).attr('width',this.xScale_(index+0.8)-this.xScale_(index+0.2)).attr('y',this.yScale_(datum.percentile_75)).attr('height',this.yScale_(datum.percentile_25)-
+this.yScale_(datum.percentile_75));sel.exit().remove();sel=boxesSel.data([datum]);sel.enter().append('line').attr('stroke',color).attr('x1',this.xScale_(index)).attr('x2',this.xScale_(index+1)).attr('y1',this.yScale_(datum.percentile_50)).attr('y2',this.yScale_(datum.percentile_50));sel.exit().remove();sel=boxesSel.data([datum]);sel.enter().append('line').attr('stroke',color).attr('x1',this.xScale_(index+0.4)).attr('x2',this.xScale_(index+0.6)).attr('y1',this.yScale_(datum.percentile_0)).attr('y2',this.yScale_(datum.percentile_0));sel.exit().remove();sel=boxesSel.data([datum]);sel.enter().append('line').attr('stroke',color).attr('x1',this.xScale_(index+0.4)).attr('x2',this.xScale_(index+0.6)).attr('y1',this.yScale_(datum.percentile_100)).attr('y2',this.yScale_(datum.percentile_100));sel.exit().remove();sel=boxesSel.data([datum]);sel.enter().append('line').attr('stroke',color).attr('x1',this.xScale_(index+0.5)).attr('x2',this.xScale_(index+0.5)).attr('y1',this.yScale_(datum.percentile_100)).attr('y2',this.yScale_(datum.percentile_0));sel.exit().remove();}}};return{BoxChart,};});'use strict';tr.exportTo('tr.ui.b',function(){const BarChart=tr.ui.b.define('bar-chart',tr.ui.b.ColumnChart);BarChart.prototype={__proto__:tr.ui.b.ColumnChart.prototype,decorate(){super.decorate();this.verticalScale_=undefined;this.horizontalScale_=undefined;this.isWaterfall_=false;},updateScales_(){super.updateScales_();this.yScale_.range([this.graphWidth,0]);this.xScale_.range([0,this.graphHeight]);this.verticalScale_=this.isYLogScale_?d3.scale.log(10):d3.scale.linear();this.verticalScale_.domain(this.xScale_.domain());this.verticalScale_.range([this.graphHeight,0]);this.horizontalScale_=d3.scale.linear();this.horizontalScale_.domain(this.yScale_.domain());this.horizontalScale_.range([0,this.graphWidth]);},set isWaterfall(waterfall){this.isWaterfall_=waterfall;if(waterfall){this.getDataSeries('hide').color='transparent';}
+this.updateContents_();},get isWaterfall(){return this.isWaterfall_;},get defaultGraphHeight(){return Math.max(20,10*this.data_.length);},get defaultGraphWidth(){return 100;},get barHeight(){return this.graphHeight/this.data.length;},drawBrush_(brushRectsSel){brushRectsSel.attr('x',0).attr('width',this.graphWidth).attr('y',d=>this.verticalScale_(d.max)).attr('height',d=>this.verticalScale_(d.min)-this.verticalScale_(d.max)).attr('fill','rgb(213, 236, 229)');},getDataPointAtChartPoint_(chartPoint){const flippedPoint={x:this.graphHeight-chartPoint.y,y:this.graphWidth-chartPoint.x};return super.getDataPointAtChartPoint_(flippedPoint);},drawXAxis_(xAxis){xAxis.attr('transform','translate(0,'+this.graphHeight+')').call(d3.svg.axis().scale(this.horizontalScale_).orient('bottom'));},get yAxisWidth(){return this.computeScaleTickWidth_(this.verticalScale_);},drawYAxis_(yAxis){const axisModifier=d3.svg.axis().scale(this.verticalScale_).orient('left');yAxis.call(axisModifier);},drawHoverValueBox_(rect){const rectHoverEvent=new tr.b.Event('rect-mouseenter');rectHoverEvent.rect=rect;this.dispatchEvent(rectHoverEvent);if(!this.enableHoverBox||(this.isWaterfall_&&rect.key==='hide')){return;}
+const seriesKeys=[...this.seriesByKey_.keys()];const chartAreaSel=d3.select(this.chartAreaElement);chartAreaSel.selectAll('.hover').remove();let keyWidthPx=0;let keyHeightPx=0;let xWidthPx=0;let xHeightPx=0;let groupWidthPx=0;let groupHeightPx=0;if(seriesKeys.length>1&&!this.isGrouped&&!this.isWaterfall_){keyWidthPx=tr.ui.b.getSVGTextSize(this.chartAreaElement,rect.key).width;keyHeightPx=this.textHeightPx_;}
+if(this.data.length>1&&!this.isWaterfall_){xWidthPx=tr.ui.b.getSVGTextSize(this.chartAreaElement,''+rect.datum.x).width;xHeightPx=this.textHeightPx_;}
+if(this.isGrouped&&rect.datum.group!==undefined){groupWidthPx=tr.ui.b.getSVGTextSize(this.chartAreaElement,rect.datum.group).width;groupHeightPx=this.textHeightPx_;}
+const valueWidthPx=tr.ui.b.getSVGTextSize(this.chartAreaElement,rect.value).width;const valueHeightPx=this.textHeightPx_;const maxWidthPx=Math.max(keyWidthPx,xWidthPx,groupWidthPx,valueWidthPx)+5;const hoverWidthPx=this.isGrouped?maxWidthPx:Math.min(maxWidthPx,Math.max(50,rect.widthPx));let hoverTopPx=rect.topPx;hoverTopPx=Math.min(hoverTopPx,this.getBoundingClientRect().height-
+valueHeightPx);let hoverLeftPx=rect.leftPx+(rect.widthPx/2);hoverLeftPx=Math.max(hoverLeftPx-hoverWidthPx,-this.margin.left);chartAreaSel.append('rect').attr('class','hover').attr('fill','white').attr('x',hoverLeftPx).attr('y',hoverTopPx).attr('width',hoverWidthPx).attr('height',keyHeightPx+xHeightPx+
+valueHeightPx+groupHeightPx);if(seriesKeys.length>1&&!this.isGrouped&&!this.isWaterfall_){chartAreaSel.append('text').attr('class','hover').attr('fill',rect.color==='transparent'?'#000000':rect.color).attr('x',hoverLeftPx+2).attr('y',hoverTopPx+keyHeightPx-3).text(rect.key);}
+if(this.data.length>1&&!this.isWaterfall_){chartAreaSel.append('text').attr('class','hover').attr('fill',rect.color==='transparent'?'#000000':rect.color).attr('x',hoverLeftPx+2).attr('y',hoverTopPx+keyHeightPx+valueHeightPx-3).text(''+rect.datum.x);}
+if(this.isGrouped&&rect.datum.group!==undefined){chartAreaSel.append('text').on('mouseleave',()=>this.clearHoverValueBox_(rect)).attr('class','hover').attr('fill',rect.color==='transparent'?'#000000':rect.color).attr('x',hoverLeftPx+2).attr('y',hoverTopPx+keyHeightPx+xHeightPx+groupHeightPx-3).text(rect.datum.group);}
+chartAreaSel.append('text').attr('class','hover').attr('fill',rect.color==='transparent'?'#000000':rect.color).attr('x',hoverLeftPx+2).attr('y',hoverTopPx+xHeightPx+keyHeightPx+
+groupHeightPx+valueHeightPx-3).text(rect.value);},flipRect_(rect){return{datum:rect.datum,index:rect.index,key:rect.key,value:rect.value,color:rect.color,topPx:this.graphHeight-rect.leftPx-rect.widthPx,leftPx:this.graphWidth-rect.topPx-rect.heightPx,widthPx:rect.heightPx,heightPx:rect.widthPx,underflow:rect.underflow,overflow:rect.overflow,};},drawRect_(rect,sel){super.drawRect_(this.flipRect_(rect),sel);},drawUnderflow_(rect,rectsSel){let sel=rectsSel.data([rect]);sel.enter().append('text').text('*').attr('fill',rect.color).attr('x',0).attr('y',this.graphHeight-rect.leftPx+
+3+(rect.widthPx/2));sel.exit().remove();sel=rectsSel.data([rect]);sel.enter().append('rect').attr('fill','rgba(0, 0, 0, 0)').attr('x',0).attr('y',this.graphHeight-rect.leftPx-rect.widthPx).attr('width',10).attr('height',rect.widthPx).on('mouseenter',()=>this.drawHoverValueBox_(this.flipRect_(rect))).on('mouseleave',()=>this.clearHoverValueBox_(rect));sel.exit().remove();},drawOverflow_(rect,sel){sel=sel.data([rect]);sel.enter().append('text').text('*').attr('fill',rect.color).attr('x',this.graphWidth).attr('y',this.graphHeight-rect.leftPx+
+3+(rect.widthPx/2));sel.exit().remove();}};return{BarChart,};});'use strict';tr.exportTo('tr.ui.b',function(){const NameBarChart=tr.ui.b.define('name-bar-chart',tr.ui.b.BarChart);const Y_AXIS_PADDING=2;NameBarChart.prototype={__proto__:tr.ui.b.BarChart.prototype,getDataPointAtChartPoint_(chartPoint){return{x:tr.ui.b.BarChart.prototype.getDataPointAtChartPoint_.call(this,chartPoint).x,y:parseInt(Math.floor((this.graphHeight-chartPoint.y)/this.barHeight))};},getXForDatum_(datum,index){return index;},get yAxisWidth(){if(this.data.length===0)return 0;return Y_AXIS_PADDING+tr.b.math.Statistics.max(this.data_,d=>tr.ui.b.getSVGTextSize(this,d.x).width);},get defaultGraphHeight(){return(3+this.textHeightPx_)*this.data.length;},updateYAxis_(yAxis){if(tr.ui.b.getSVGTextSize(this,'test').width===0){tr.b.requestAnimationFrame(()=>this.updateYAxis_(yAxis));return;}
+yAxis.selectAll('*').remove();if(this.hideYAxis)return;const nameTexts=yAxis.selectAll('text').data(this.data_);nameTexts.enter().append('text').attr('x',d=>-(tr.ui.b.getSVGTextSize(this,d.x).width+Y_AXIS_PADDING)).attr('y',(d,index)=>this.verticalScale_(index)).text(d=>d.x);nameTexts.exit().remove();let previousTop=undefined;for(const text of nameTexts[0]){const bbox=text.getBBox();if((previousTop===undefined)||(previousTop>(bbox.y+bbox.height))){previousTop=bbox.y;}else{text.style.opacity=0;}}}};return{NameBarChart,};});'use strict';tr.exportTo('tr.v.ui',function(){const DIAGNOSTIC_SPAN_BEHAVIOR={created(){this.diagnostic_=undefined;this.name_=undefined;this.histogram_=undefined;},attached(){if(this.diagnostic_)this.updateContents_();},get diagnostic(){return this.diagnostic_;},build(diagnostic,name,histogram){this.diagnostic_=diagnostic;this.name_=name;this.histogram_=histogram;if(this.isAttached)this.updateContents_();},updateContents_(){throw new Error('dom-modules must override updateContents_()');}};return{DIAGNOSTIC_SPAN_BEHAVIOR,};});'use strict';tr.exportTo('tr.v.ui',function(){const DEFAULT_COLOR_SCHEME=new tr.b.SinebowColorGenerator();function getHistogramName(histogram,diagnosticName,key){if(histogram===undefined)return undefined;const nameMap=histogram.diagnostics.get(diagnosticName);if(nameMap===undefined)return undefined;return nameMap.get(key);}
+class BreakdownTableSummaryRow{constructor(displayElement,histogramNames){this.displayElement_=displayElement;this.histogramNames_=histogramNames;this.keySpan_=undefined;}
+get numberValue(){return undefined;}
+get keySpan(){if(this.keySpan_===undefined){if(this.histogramNames_.length){this.keySpan_=document.createElement('tr-ui-a-analysis-link');this.keySpan_.setSelectionAndContent(this.histogramNames_,'Select All');}else{this.keySpan_='Sum';}}
+return this.keySpan_;}
+get name(){return'Sum';}
+get displayElement(){return this.displayElement_;}
+get stringPercent(){return'100%';}}
+class BreakdownTableRow{constructor(name,value,histogramName,unit,color){this.name_=name;this.value_=value;this.histogramName_=histogramName;this.unit_=unit;if(typeof value!=='number'){throw new Error('unsupported value '+value);}
+this.tableSum_=undefined;this.keySpan_=undefined;this.color_=color;const hsl=this.color.toHSL();hsl.l*=0.85;this.highlightedColor_=tr.b.Color.fromHSL(hsl);if(this.unit_){this.displayElement_=tr.v.ui.createScalarSpan(this.numberValue,{unit:this.unit_,});}else{this.displayElement_=tr.ui.b.createSpan({textContent:this.stringValue,});}}
+get name(){return this.name_;}
+get color(){return this.color_;}
+get highlightedColor(){return this.highlightedColor_;}
+get keySpan(){if(this.keySpan_===undefined){if(this.histogramName_){this.keySpan_=document.createElement('tr-ui-a-analysis-link');this.keySpan_.setSelectionAndContent([this.histogramName_],this.name);this.keySpan_.color=this.color;this.keySpan_.title=this.histogramName_;}else{this.keySpan_=document.createElement('span');this.keySpan_.innerText=this.name;this.keySpan_.style.color=this.color;}}
+return this.keySpan_;}
+get numberValue(){if(!isNaN(this.value_)&&(this.value_!==Infinity)&&(this.value_!==-Infinity)&&(this.value_>0))return this.value_;return undefined;}
+get stringValue(){if((this.unit_!==undefined)&&!isNaN(this.value_)&&(this.value_!==Infinity)&&(this.value_!==-Infinity)){return this.unit_.format(this.value_);}
+return this.value_.toString();}
+set tableSum(s){this.tableSum_=s;}
+get stringPercent(){if(this.tableSum_===undefined)return'';const num=this.numberValue;if(num===undefined)return'';return Math.floor(num*100.0/this.tableSum_)+'%';}
+get displayElement(){return this.displayElement_;}
+compare(other){if(this.numberValue===undefined){if(other.numberValue===undefined){return this.name.localeCompare(other.name);}
+return 1;}
+if(other.numberValue===undefined){return-1;}
+if(this.numberValue===other.numberValue){return this.name.localeCompare(other.name);}
+return other.numberValue-this.numberValue;}}
+Polymer({is:'tr-v-ui-breakdown-span',behaviors:[tr.v.ui.DIAGNOSTIC_SPAN_BEHAVIOR],created(){this.chart_=new tr.ui.b.ColumnChart();this.chart_.graphHeight=130;this.chart_.isStacked=true;this.chart_.hideXAxis=true;this.chart_.hideLegend=true;this.chart_.enableHoverBox=false;this.chart_.addEventListener('rect-mouseenter',event=>this.onRectMouseEnter_(event));this.chart_.addEventListener('rect-mouseleave',event=>this.onRectMouseLeave_(event));},onRectMouseEnter_(event){for(const row of this.$.table.tableRows){if(row.name===event.rect.key){row.displayElement.style.background=event.rect.color;row.keySpan.scrollIntoViewIfNeeded();}else{row.displayElement.style.background='';}}},onRectMouseLeave_(event){for(const row of this.$.table.tableRows){row.displayElement.style.background='';}},ready(){Polymer.dom(this.$.container).appendChild(this.chart_);this.$.table.zebra=true;this.$.table.showHeader=false;this.$.table.tableColumns=[{value:row=>row.keySpan,},{value:row=>row.displayElement,align:tr.ui.b.TableFormat.ColumnAlignment.RIGHT,},{value:row=>row.stringPercent,align:tr.ui.b.TableFormat.ColumnAlignment.RIGHT,},];},updateContents_(){this.$.container.style.display='none';this.$.table.style.display='none';this.$.empty.style.display='block';if(!this.diagnostic_){this.chart_.data=[];return;}
+if(this.histogram_)this.chart_.unit=this.histogram_.unit;let colorScheme=undefined;if(this.diagnostic.colorScheme===tr.v.d.COLOR_SCHEME_CHROME_USER_FRIENDLY_CATEGORY_DRIVER){colorScheme=(name)=>{let cat=name.split(' ');cat=cat[cat.length-1];return tr.e.chrome.ChromeUserFriendlyCategoryDriver.getColor(cat);};}else if(this.diagnostic.colorScheme){colorScheme=(name)=>tr.b.FixedColorSchemeRegistry.lookUp(this.diagnostic.colorScheme).getColor(name);}else{colorScheme=(name)=>DEFAULT_COLOR_SCHEME.colorForKey(name);}
+const tableRows=[];let tableSum=0;const histogramNames=[];for(const[key,value]of this.diagnostic){const histogramName=getHistogramName(this.histogram_,this.name_,key);const row=new BreakdownTableRow(key,value,histogramName,this.chart_.unit,colorScheme(key));tableRows.push(row);if(row.numberValue!==undefined)tableSum+=row.numberValue;if(histogramName){histogramNames.push(histogramName);}}
+tableRows.sort((x,y)=>x.compare(y));if(tableSum>0){let summaryDisplayElement=tableSum;if(this.chart_.unit!==undefined){summaryDisplayElement=this.chart_.unit.format(tableSum);}
+summaryDisplayElement=tr.ui.b.createSpan({textContent:summaryDisplayElement,});tableRows.unshift(new BreakdownTableSummaryRow(summaryDisplayElement,histogramNames));}
+const chartData={x:0};for(const row of tableRows){if(row.numberValue===undefined)continue;row.tableSum=tableSum;chartData[row.name]=row.numberValue;const dataSeries=this.chart_.getDataSeries(row.name);dataSeries.color=row.color;dataSeries.highlightedColor=row.highlightedColor;}
+if(tableRows.length>0){this.$.table.style.display='block';this.$.empty.style.display='none';this.$.table.tableRows=tableRows;this.$.table.rebuild();}
+if(Object.keys(chartData).length>1){this.$.container.style.display='block';this.$.empty.style.display='none';this.chart_.data=[chartData];}}});return{};});'use strict';tr.exportTo('tr.v.ui',function(){Polymer({is:'tr-v-ui-collected-related-event-set-span',behaviors:[tr.v.ui.DIAGNOSTIC_SPAN_BEHAVIOR],updateContents_(){Polymer.dom(this).textContent='';for(const[canonicalUrl,events]of this.diagnostic){const link=document.createElement('a');if(events.length===1){const event=tr.b.getOnlyElement(events);link.textContent=event.title+' '+
+tr.b.Unit.byName.timeDurationInMs.format(event.duration);}else{link.textContent=events.length+' events';}
+link.href=canonicalUrl;Polymer.dom(this).appendChild(link);Polymer.dom(this).appendChild(document.createElement('br'));}}});return{};});'use strict';tr.exportTo('tr.v.ui',function(){Polymer({is:'tr-v-ui-date-range-span',behaviors:[tr.v.ui.DIAGNOSTIC_SPAN_BEHAVIOR],updateContents_(){if(this.diagnostic===undefined){Polymer.dom(this).textContent='';return;}
+Polymer.dom(this).textContent=this.diagnostic.toString();}});return{};});'use strict';tr.exportTo('tr.v.ui',function(){function isLinkTuple(value){return((value instanceof Array)&&(value.length===2)&&(typeof value[0]==='string')&&tr.b.isUrl(value[1]));}
+Polymer({is:'tr-v-ui-generic-set-span',behaviors:[tr.v.ui.DIAGNOSTIC_SPAN_BEHAVIOR],updateContents_(){this.$.generic.style.display='none';this.$.links.textContent='';if(this.diagnostic===undefined)return;const values=Array.from(this.diagnostic);let areAllStrings=true;let areAllNumbers=true;for(const value of values){if(typeof value!=='number'){areAllNumbers=false;if(typeof value!=='string'&&!isLinkTuple(value)){areAllStrings=false;break;}}}
+if(!areAllStrings){this.$.generic.style.display='';this.$.generic.object=values;return;}
+if(areAllNumbers){values.sort((x,y)=>x-y);}else{values.sort();}
+for(const value of values){const link={textContent:''+value};if(isLinkTuple(value)){link.textContent=value[0];link.href=value[1];}else if(tr.b.isUrl(value)){link.href=value;}
+if(this.name_===tr.v.d.RESERVED_NAMES.TRACE_URLS){link.textContent=value.substr(1+value.lastIndexOf('/'));}
+const linkEl=tr.ui.b.createLink(link);if(link.href){linkEl.target='_blank';linkEl.addEventListener('click',e=>e.stopPropagation());}
+this.$.links.appendChild(linkEl);}}});return{};});'use strict';tr.exportTo('tr.v.ui',function(){Polymer({is:'tr-v-ui-related-event-set-span',behaviors:[tr.v.ui.DIAGNOSTIC_SPAN_BEHAVIOR],updateContents_(){Polymer.dom(this).textContent='';const events=new tr.model.EventSet([...this.diagnostic]);const link=document.createElement('tr-ui-a-analysis-link');let label=events.length+' events';if(events.length===1){const event=tr.b.getOnlyElement(events);label=event.title+' ';label+=tr.b.Unit.byName.timeDurationInMs.format(event.duration);}
+link.setSelectionAndContent(events,label);Polymer.dom(this).appendChild(link);}});return{};});'use strict';tr.exportTo('tr.v.ui',function(){Polymer({is:'tr-v-ui-scalar-diagnostic-span',behaviors:[tr.v.ui.DIAGNOSTIC_SPAN_BEHAVIOR],updateContents_(){this.$.scalar.setValueAndUnit(this.diagnostic.value.value,this.diagnostic.value.unit);}});return{};});'use strict';tr.exportTo('tr.v.ui',function(){Polymer({is:'tr-v-ui-unmergeable-diagnostic-set-span',behaviors:[tr.v.ui.DIAGNOSTIC_SPAN_BEHAVIOR],updateContents_(){Polymer.dom(this).textContent='';for(const diagnostic of this.diagnostic){if(diagnostic instanceof tr.v.d.RelatedNameMap)continue;const div=document.createElement('div');div.appendChild(tr.v.ui.createDiagnosticSpan(diagnostic,this.name_,this.histogram_));Polymer.dom(this).appendChild(div);}}});return{};});'use strict';tr.exportTo('tr.v.ui',function(){function findElementNameForDiagnostic(diagnostic){let typeInfo=undefined;let curProto=diagnostic.constructor.prototype;while(curProto){typeInfo=tr.v.d.Diagnostic.findTypeInfo(curProto.constructor);if(typeInfo&&typeInfo.metadata.elementName)break;typeInfo=undefined;curProto=curProto.__proto__;}
+if(typeInfo===undefined){throw new Error(diagnostic.constructor.name+' or a base class must have a registered elementName');}
+const tagName=typeInfo.metadata.elementName;if(tr.ui.b.isUnknownElementName(tagName)){throw new Error('Element not registered: '+tagName);}
+return tagName;}
+function createDiagnosticSpan(diagnostic,name,histogram){const tagName=findElementNameForDiagnostic(diagnostic);const span=document.createElement(tagName);if(span.build===undefined)throw new Error(tagName);span.build(diagnostic,name,histogram);return span;}
+return{createDiagnosticSpan,};});'use strict';tr.exportTo('tr.v.ui',function(){function makeColumn(title,histogram){return{title,value(map){const diagnostic=map.get(title);if(!diagnostic)return'';return tr.v.ui.createDiagnosticSpan(diagnostic,title,histogram);}};}
+Polymer({is:'tr-v-ui-diagnostic-map-table',created(){this.diagnosticMaps_=undefined;this.histogram_=undefined;this.isMetadata_=false;},set histogram(h){this.histogram_=h;},set isMetadata(m){this.isMetadata_=m;this.$.table.showHeader=!this.isMetadata_;},set diagnosticMaps(maps){this.diagnosticMaps_=maps;this.updateContents_();},get diagnosticMaps(){return this.diagnosticMaps_;},updateContents_(){if(this.isMetadata_&&this.diagnosticMaps_.length!==1){throw new Error('Metadata diagnostic-map-tables require exactly 1 DiagnosticMap');}
+if(this.diagnosticMaps_===undefined||this.diagnosticMaps_.length===0){this.$.table.tableRows=[];this.$.table.tableColumns=[];return;}
+let names=new Set();for(const map of this.diagnosticMaps_){for(const[name,diagnostic]of map){if(diagnostic instanceof tr.v.d.UnmergeableDiagnosticSet)continue;if(diagnostic instanceof tr.v.d.CollectedRelatedEventSet)continue;names.add(name);}}
+names=Array.from(names).sort();const histogram=this.histogram_;if(this.isMetadata_){const diagnosticMap=this.diagnosticMaps_[0];this.$.table.tableColumns=[{value(name){return name.name;}},{value(name){const diagnostic=diagnosticMap.get(name.name);if(!diagnostic)return'';return tr.v.ui.createDiagnosticSpan(diagnostic,name.name,histogram);}},];this.$.table.tableRows=names.map(name=>{return{name};});}else{this.$.table.tableColumns=names.map(name=>makeColumn(name,histogram));this.$.table.tableRows=this.diagnosticMaps_;}
+this.$.table.rebuild();}});return{};});'use strict';tr.exportTo('tr.b',function(){class Serializable{constructor(){Object.defineProperty(this,'properties_',{configurable:false,enumerable:false,value:new Map(),});}
+define(name,initialValue){if(this[name]!==undefined){throw new Error(`"${name}" is already defined.`);}
+if(name[name.length-1]==='_'){throw new Error(`"${name}" cannot end with an underscore.`);}
+this.properties_.set(name,initialValue);Object.defineProperty(this,name,{configurable:false,enumerable:true,get:()=>this.properties_.get(name),set:value=>this.setProperty_(name,value),});}
+setProperty_(name,value){this.properties_.set(name,value);}
+clone(){return Serializable.fromDict(this.asDict());}
+asDict(){function visit(obj){if(obj instanceof Serializable)return obj.asDict();if(obj instanceof Set)return Array.from(obj);if(obj instanceof Array)return obj.map(visit);if(!(obj instanceof Map))return obj;const result={};for(const[name,value]of obj){result[name]=visit(value);}
+return result;}
+const dict={type:this.constructor.name};for(const[name,value]of this.properties_){dict[name.replace(/_$/,'')]=visit(value);}
+return dict;}
+static fromDict(dict){function visit(d){if(d instanceof Array)return d.map(visit);if(!(d instanceof Object))return d;if(typeof d.type==='string')return Serializable.fromDict(d);const result=new Map();for(const[name,value]of Object.entries(d)){result.set(name,visit(value));}
+return result;}
+const typeInfo=Serializable.findTypeInfoWithName(dict.type);const result=new typeInfo.constructor();for(const[name,value]of Object.entries(dict)){result[name]=visit(value);}
+return result;}}
+const options=new tr.b.ExtensionRegistryOptions(tr.b.BASIC_REGISTRY_MODE);options.defaultMetadata={};options.mandatoryBaseClass=Serializable;tr.b.decorateExtensionRegistry(Serializable,options);return{Serializable,};});'use strict';tr.exportTo('tr.b',function(){class ViewState extends tr.b.Serializable{constructor(){super();tr.b.EventTarget.decorate(this);}
+setProperty_(name,value){this.update(new Map([[name,value]]));}
+async updateFromViewState(other){await this.update(other.properties_);}
+async update(delta){if(!(delta instanceof Map))delta=new Map(Object.entries(delta));const actualDelta={};for(const[name,current]of delta){const previous=this[name];if(previous===current)continue;actualDelta[name]={previous,current};tr.b.Serializable.prototype.setProperty_.call(this,name,current);}
+if(Object.keys(actualDelta).length===0)return;await tr.b.dispatchSimpleEventAsync(this,this.updateEventName_,{delta:actualDelta});}
+get updateEventName_(){return this.constructor.name+'.update';}
+addUpdateListener(listener){this.addEventListener(this.updateEventName_,listener);}
+removeUpdateListener(listener){this.removeEventListener(this.updateEventName_,listener);}}
+return{ViewState,};});'use strict';tr.exportTo('tr.v.ui',function(){class HistogramSetViewState extends tr.b.ViewState{constructor(){super();this.define('searchQuery','');this.define('referenceDisplayLabel','');this.define('displayStatisticName','');this.define('showAll',true);this.define('groupings',[]);this.define('sortColumnIndex',0);this.define('sortDescending',false);this.define('constrainNameColumn',true);this.define('tableRowStates',new Map());this.define('alpha',0.01);}}
+tr.b.ViewState.register(HistogramSetViewState);class HistogramSetTableRowState extends tr.b.ViewState{constructor(){super();this.define('isExpanded',false);this.define('isOverviewed',false);this.define('cells',new Map());this.define('subRows',new Map());this.define('diagnosticsTab','');}
+asCompactDict(){const result={};if(this.isExpanded)result.e='1';if(this.isOverviewed)result.o='1';if(this.diagnosticsTab)result.d=this.diagnosticsTab;const cells={};for(const[name,cell]of this.cells){const cellDict=cell.asCompactDict();if(cellDict===undefined)continue;cells[name]=cellDict;}
+if(Object.keys(cells).length>0)result.c=cells;const subRows={};for(const[name,row]of this.subRows){const rowDict=row.asCompactDict();if(rowDict===undefined)continue;subRows[name]=rowDict;}
+if(Object.keys(subRows).length>0)result.r=subRows;if(Object.keys(result).length===0)return undefined;return result;}
+async updateFromCompactDict(dict){await this.update({isExpanded:dict.e==='1',isOverviewed:dict.o==='1',diagnosticsTab:dict.d||'',});for(const[name,cellDict]of Object.entries(dict.c||{})){const cell=this.cells.get(name);if(cell===undefined)continue;await cell.updateFromCompactDict(cellDict);}
+for(const[name,subRowDict]of Object.entries(dict.r||{})){const subRow=this.subRows.get(name);if(subRow===undefined)continue;await subRow.updateFromCompactDict(subRowDict);}}*walk(){yield this;for(const row of this.subRows.values())yield*row.walk();}
+static*walkAll(rootRows){for(const rootRow of rootRows)yield*rootRow.walk();}}
+tr.b.ViewState.register(HistogramSetTableRowState);class HistogramSetTableCellState extends tr.b.ViewState{constructor(){super();this.define('isOpen',false);this.define('brushedBinRange',new tr.b.math.Range());this.define('mergeSampleDiagnostics',true);}
+asCompactDict(){const result={};if(this.isOpen)result.o='1';if(!this.mergeSampleDiagnostics)result.m='0';if(!this.brushedBinRange.isEmpty){result.b=this.brushedBinRange.min+'_'+this.brushedBinRange.max;}
+if(Object.keys(result).length===0)return undefined;return result;}
+async updateFromCompactDict(dict){let binRange=this.brushedBinRange;if(dict.b){let[bMin,bMax]=dict.b.split('_');bMin=parseInt(bMin);bMax=parseInt(bMax);if(bMin!==binRange.min||bMax!==binRange.max){binRange=tr.b.math.Range.fromExplicitRange(bMin,bMax);}}
+await this.update({isOpen:dict.o==='1',brushedBinRange:binRange,mergeSampleDiagnostics:dict.m!=='0',});}}
+tr.b.ViewState.register(HistogramSetTableCellState);return{HistogramSetTableCellState,HistogramSetTableRowState,HistogramSetViewState,};});'use strict';Polymer({is:'tr-v-ui-scalar-map-table',created(){this.scalarMap_=new Map();this.significance_=new Map();},ready(){this.$.table.showHeader=false;this.$.table.tableColumns=[{value(row){return row.name;}},{value(row){const span=tr.v.ui.createScalarSpan(row.value);if(row.significance!==undefined){span.significance=row.significance;}else if(row.anyRowsHaveSignificance){span.style.marginRight='18px';}
+span.style.whiteSpace='nowrap';return span;}}];},get scalarMap(){return this.scalarMap_;},set scalarMap(map){this.scalarMap_=map;this.updateContents_();},setSignificanceForKey(key,significance){this.significance_.set(key,significance);this.updateContents_();},updateContents_(){const rows=[];for(const[key,scalar]of this.scalarMap){rows.push({name:key,value:scalar,significance:this.significance_.get(key),anyRowsHaveSignificance:(this.significance_.size>0)});}
+this.$.table.tableRows=rows;this.$.table.rebuild();}});'use strict';tr.exportTo('tr.v.ui',function(){const DEFAULT_BAR_HEIGHT_PX=5;const TRUNCATE_BIN_MARGIN=0.15;const IGNORE_DELTA_STATISTICS_NAMES=[`${tr.v.DELTA}min`,`%${tr.v.DELTA}min`,`${tr.v.DELTA}max`,`%${tr.v.DELTA}max`,`${tr.v.DELTA}sum`,`%${tr.v.DELTA}sum`,`${tr.v.DELTA}count`,`%${tr.v.DELTA}count`,];Polymer({is:'tr-v-ui-histogram-span',created(){this.viewStateListener_=this.onViewStateUpdate_.bind(this);this.viewState=new tr.v.ui.HistogramSetTableCellState();this.rowStateListener_=this.onRowStateUpdate_.bind(this);this.rowState=new tr.v.ui.HistogramSetTableRowState();this.rootStateListener_=this.onRootStateUpdate_.bind(this);this.rootState=new tr.v.ui.HistogramSetViewState();this.histogram_=undefined;this.referenceHistogram_=undefined;this.graphWidth_=undefined;this.graphHeight_=undefined;this.mouseDownBin_=undefined;this.prevBrushedBinRange_=new tr.b.math.Range();this.anySampleDiagnostics_=false;this.canMergeSampleDiagnostics_=true;this.mwuResult_=undefined;},get rowState(){return this.rowState_;},set rowState(rs){if(this.rowState){this.rowState.removeUpdateListener(this.rowStateListener_);}
+this.rowState_=rs;this.rowState.addUpdateListener(this.rowStateListener_);if(this.isAttached)this.updateContents_();},get viewState(){return this.viewState_;},set viewState(vs){if(this.viewState){this.viewState.removeUpdateListener(this.viewStateListener_);}
+this.viewState_=vs;this.viewState.addUpdateListener(this.viewStateListener_);if(this.isAttached)this.updateContents_();},get rootState(){return this.rootState_;},set rootState(vs){if(this.rootState){this.rootState.removeUpdateListener(this.rootStateListener_);}
+this.rootState_=vs;this.rootState.addUpdateListener(this.rootStateListener_);if(this.isAttached)this.updateContents_();},build(histogram,opt_referenceHistogram){this.histogram_=histogram;this.$.metric_diagnostics.histogram=histogram;this.$.sample_diagnostics.histogram=histogram;this.referenceHistogram_=opt_referenceHistogram;if(this.histogram.canCompare(this.referenceHistogram)){this.mwuResult_=tr.b.math.Statistics.mwu(this.histogram.sampleValues,this.referenceHistogram.sampleValues,this.rootState.alpha);}
+this.anySampleDiagnostics_=false;for(const bin of this.histogram.allBins){if(bin.diagnosticMaps.length>0){this.anySampleDiagnostics_=true;break;}}
+if(this.isAttached)this.updateContents_();},onViewStateUpdate_(event){if(event.delta.brushedBinRange){if(this.chart_!==undefined){this.chart_.brushedRange=this.viewState.brushedBinRange;}
+this.updateDiagnostics_();}
+if(event.delta.mergeSampleDiagnostics&&(this.viewState.mergeSampleDiagnostics!==this.$.merge_sample_diagnostics.checked)){this.$.merge_sample_diagnostics.checked=this.canMergeSampleDiagnostics&&this.viewState.mergeSampleDiagnostics;this.updateDiagnostics_();}},updateSignificance_(){if(!this.mwuResult_)return;this.$.stats.setSignificanceForKey(`${tr.v.DELTA}avg`,this.mwuResult_.significance);},onRootStateUpdate_(event){if(event.delta.alpha&&this.mwuResult_){this.mwuResult_.compare(this.rootState.alpha);this.updateSignificance_();}},onRowStateUpdate_(event){if(event.delta.diagnosticsTab){if(this.rowState.diagnosticsTab===this.$.sample_diagnostics_container.tabLabel){this.updateDiagnostics_();}else{for(const tab of this.$.diagnostics.subViews){if(this.rowState.diagnosticsTab===tab.tabLabel){this.$.diagnostics.selectedSubView=tab;break;}}}}},ready(){this.$.metric_diagnostics.tabLabel='histogram diagnostics';this.$.sample_diagnostics_container.tabLabel='sample diagnostics';this.$.metadata_diagnostics.tabLabel='metadata';this.$.metadata_diagnostics.isMetadata=true;this.$.diagnostics.addEventListener('selected-tab-change',this.onSelectedDiagnosticsChanged_.bind(this));this.$.drag_handle.target=this.$.container;this.$.drag_handle.addEventListener('drag-handle-resize',this.onResize_.bind(this));},attached(){if(this.histogram_!==undefined)this.updateContents_();},get canMergeSampleDiagnostics(){return this.canMergeSampleDiagnostics_;},set canMergeSampleDiagnostics(merge){this.canMergeSampleDiagnostics_=merge;if(!merge)this.viewState.mergeSampleDiagnostics=false;this.$.merge_sample_diagnostics_container.style.display=(merge?'':'none');},onResize_(event){event.stopPropagation();let heightPx=parseInt(this.$.container.style.height);if(heightPx<this.defaultGraphHeight){heightPx=this.defaultGraphHeight;this.$.container.style.height=this.defaultGraphHeight+'px';}
+this.chart_.graphHeight=heightPx-(this.chart_.margin.top+
+this.chart_.margin.bottom);this.$.stats_container.style.maxHeight=this.chart_.getBoundingClientRect().height+'px';},get graphWidth(){return this.graphWidth_||this.defaultGraphWidth;},set graphWidth(width){this.graphWidth_=width;},get graphHeight(){return this.graphHeight_||this.defaultGraphHeight;},set graphHeight(height){this.graphHeight_=height;},get barHeight(){return this.chart_.barHeight;},set barHeight(px){this.graphHeight=this.computeChartHeight_(px);},computeChartHeight_(barHeightPx){return(this.chart_.margin.top+
+this.chart_.margin.bottom+
+(barHeightPx*this.histogram.allBins.length));},get defaultGraphHeight(){if(this.histogram&&this.histogram.allBins.length===1){return 150;}
+return this.computeChartHeight_(DEFAULT_BAR_HEIGHT_PX);},get defaultGraphWidth(){if(this.histogram.allBins.length===1){return 100;}
+return 300;},get brushedBins(){const bins=[];if(this.histogram&&!this.viewState.brushedBinRange.isEmpty){for(let i=this.viewState.brushedBinRange.min;i<this.viewState.brushedBinRange.max;++i){bins.push(this.histogram.allBins[i]);}}
+return bins;},async updateBrushedRange_(binIndex){const brushedBinRange=new tr.b.math.Range();brushedBinRange.addValue(tr.b.math.clamp(this.mouseDownBinIndex_,0,this.histogram.allBins.length-1));brushedBinRange.addValue(tr.b.math.clamp(binIndex,0,this.histogram.allBins.length-1));brushedBinRange.max+=1;await this.viewState.update({brushedBinRange});},onMouseDown_(chartEvent){chartEvent.stopPropagation();if(!this.histogram)return;this.prevBrushedBinRange_=this.viewState.brushedBinRange;this.mouseDownBinIndex_=chartEvent.y;this.updateBrushedRange_(chartEvent.y);},onMouseMove_(chartEvent){chartEvent.stopPropagation();if(!this.histogram)return;this.updateBrushedRange_(chartEvent.y);},onMouseUp_(chartEvent){chartEvent.stopPropagation();if(!this.histogram)return;this.updateBrushedRange_(chartEvent.y);if(this.prevBrushedBinRange_.range===1&&this.viewState.brushedBinRange.range===1&&(this.prevBrushedBinRange_.min===this.viewState.brushedBinRange.min)){tr.b.Timing.instant('histogram-span','clearBrushedBins');this.viewState.update({brushedBinRange:new tr.b.math.Range()});}else{tr.b.Timing.instant('histogram-span','brushBins');}
+this.mouseDownBinIndex_=undefined;},async onSelectedDiagnosticsChanged_(){await this.rowState.update({diagnosticsTab:this.$.diagnostics.selectedSubView.tabLabel,});if((this.$.diagnostics.selectedSubView===this.$.sample_diagnostics_container)&&this.histogram&&this.viewState.brushedBinRange.isEmpty){const brushedBinRange=tr.b.math.Range.fromExplicitRange(0,this.histogram.allBins.length);await this.viewState.update({brushedBinRange});this.updateDiagnostics_();}},updateDiagnostics_(){let maps=[];for(const bin of this.brushedBins){for(const map of bin.diagnosticMaps){maps.push(map);}}
+if(this.$.merge_sample_diagnostics.checked!==this.viewState.mergeSampleDiagnostics){this.viewState.update({mergeSampleDiagnostics:this.$.merge_sample_diagnostics.checked});}
+if(this.viewState.mergeSampleDiagnostics){const merged=new tr.v.d.DiagnosticMap();for(const map of maps){merged.addDiagnostics(map);}
+maps=[merged];}
+const mark=tr.b.Timing.mark('histogram-span',(this.viewState.mergeSampleDiagnostics?'merge':'split')+'SampleDiagnostics');this.$.sample_diagnostics.diagnosticMaps=maps;mark.end();if(this.anySampleDiagnostics_){this.$.diagnostics.selectedSubView=this.$.sample_diagnostics_container;}},get histogram(){return this.histogram_;},get referenceHistogram(){return this.referenceHistogram_;},getDeltaScalars_(statNames,scalarMap){if(!this.histogram.canCompare(this.referenceHistogram))return;for(const deltaStatName of tr.v.Histogram.getDeltaStatisticsNames(statNames)){if(IGNORE_DELTA_STATISTICS_NAMES.includes(deltaStatName))continue;const scalar=this.histogram.getStatisticScalar(deltaStatName,this.referenceHistogram,this.mwuResult_);if(scalar===undefined)continue;scalarMap.set(deltaStatName,scalar);}},set isYLogScale(logScale){this.chart_.isYLogScale=logScale;},async updateContents_(){this.$.chart.style.display='none';this.$.drag_handle.style.display='none';this.$.container.style.justifyContent='';while(Polymer.dom(this.$.chart).lastChild){Polymer.dom(this.$.chart).removeChild(Polymer.dom(this.$.chart).lastChild);}
+if(!this.histogram)return;this.$.container.style.display='';const scalarMap=new Map();this.getDeltaScalars_(this.histogram.statisticsNames,scalarMap);for(const[name,scalar]of this.histogram.statisticsScalars){scalarMap.set(name,scalar);}
+this.$.stats.scalarMap=scalarMap;this.updateSignificance_();const metricDiagnosticMap=new tr.v.d.DiagnosticMap();const metadataDiagnosticMap=new tr.v.d.DiagnosticMap();for(const[key,diagnostic]of this.histogram.diagnostics){if(diagnostic instanceof tr.v.d.RelatedNameMap)continue;if(tr.v.d.RESERVED_NAMES_SET.has(key)){metadataDiagnosticMap.set(key,diagnostic);}else{metricDiagnosticMap.set(key,diagnostic);}}
+const diagnosticTabs=[];if(metricDiagnosticMap.size){this.$.metric_diagnostics.diagnosticMaps=[metricDiagnosticMap];diagnosticTabs.push(this.$.metric_diagnostics);}
+if(this.anySampleDiagnostics_){diagnosticTabs.push(this.$.sample_diagnostics_container);}
+if(metadataDiagnosticMap.size){this.$.metadata_diagnostics.diagnosticMaps=[metadataDiagnosticMap];diagnosticTabs.push(this.$.metadata_diagnostics);}
+this.$.diagnostics.resetSubViews(diagnosticTabs);this.$.diagnostics.set('tabsHidden',diagnosticTabs.length<2);if(this.histogram.numValues<=1){await this.viewState.update({brushedBinRange:tr.b.math.Range.fromExplicitRange(0,this.histogram.allBins.length)});this.$.container.style.justifyContent='flex-end';return;}
+this.$.chart.style.display='block';this.$.drag_handle.style.display='block';if(this.histogram.allBins.length===1){if(this.histogram.min!==this.histogram.max){this.chart_=new tr.ui.b.BoxChart();Polymer.dom(this.$.chart).appendChild(this.chart_);this.chart_.graphWidth=this.graphWidth;this.chart_.graphHeight=this.graphHeight;this.chart_.hideXAxis=true;this.chart_.data=[{x:'',color:'blue',percentile_0:this.histogram.running.min,percentile_25:this.histogram.getApproximatePercentile(0.25),percentile_50:this.histogram.getApproximatePercentile(0.5),percentile_75:this.histogram.getApproximatePercentile(0.75),percentile_100:this.histogram.running.max,}];}
+this.$.stats_container.style.maxHeight=this.chart_.getBoundingClientRect().height+'px';await this.viewState.update({brushedBinRange:tr.b.math.Range.fromExplicitRange(0,this.histogram.allBins.length)});return;}
+this.chart_=new tr.ui.b.NameBarChart();Polymer.dom(this.$.chart).appendChild(this.chart_);this.chart_.graphWidth=this.graphWidth;this.chart_.graphHeight=this.graphHeight;this.chart_.addEventListener('item-mousedown',this.onMouseDown_.bind(this));this.chart_.addEventListener('item-mousemove',this.onMouseMove_.bind(this));this.chart_.addEventListener('item-mouseup',this.onMouseUp_.bind(this));this.chart_.hideLegend=true;this.chart_.getDataSeries('y').color='blue';this.chart_.xAxisLabel='#';this.chart_.brushedRange=this.viewState.brushedBinRange;if(!this.viewState.brushedBinRange.isEmpty){this.updateDiagnostics_();}
+const chartData=[];const binCounts=[];for(const bin of this.histogram.allBins){let x=bin.range.min;if(x===-Number.MAX_VALUE){x='<'+new tr.b.Scalar(this.histogram.unit,bin.range.max).toString();}else{x=new tr.b.Scalar(this.histogram.unit,x).toString();}
+chartData.push({x,y:bin.count});binCounts.push(bin.count);}
+binCounts.sort((x,y)=>y-x);const dataRange=tr.b.math.Range.fromExplicitRange(0,binCounts[0]);if(binCounts[1]>0&&binCounts[0]>(binCounts[1]*2)){dataRange.max=binCounts[1]*(1+TRUNCATE_BIN_MARGIN);}
+if(binCounts[2]>0&&binCounts[1]>(binCounts[2]*2)){dataRange.max=binCounts[2]*(1+TRUNCATE_BIN_MARGIN);}
+this.chart_.overrideDataRange=dataRange;this.chart_.data=chartData;this.$.stats_container.style.maxHeight=this.chart_.getBoundingClientRect().height+'px';}});});'use strict';tr.exportTo('tr.ui.analysis',function(){const EVENT_FIELD=[{key:'start',label:'Start'},{key:'cpuDuration',label:'CPU Duration'},{key:'duration',label:'Duration'},{key:'cpuSelfTime',label:'CPU Self Time'},{key:'selfTime',label:'Self Time'}];function buildDiagnostics_(slice){const diagnostics={};for(const item of EVENT_FIELD){const fieldName=item.key;if(slice[fieldName]===undefined)continue;diagnostics[fieldName]=new tr.v.d.Scalar(new tr.b.Scalar(tr.b.Unit.byName.timeDurationInMs,slice[fieldName]));}
+diagnostics.args=new tr.v.d.GenericSet([slice.args]);diagnostics.event=new tr.v.d.RelatedEventSet(slice);return diagnostics;}
+Polymer({is:'tr-ui-a-multi-event-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],created(){this.currentSelection_=undefined;this.eventsHaveDuration_=true;this.eventsHaveSubRows_=true;},ready(){this.$.radioPicker.style.display='none';this.$.radioPicker.items=EVENT_FIELD;this.$.radioPicker.select('cpuSelfTime');this.$.radioPicker.addEventListener('change',()=>{if(this.isAttached)this.updateContents_();});this.$.histogramSpan.graphWidth=400;this.$.histogramSpan.canMergeSampleDiagnostics=false;this.$.histogramContainer.style.display='none';},attached(){if(this.currentSelection_!==undefined)this.updateContents_();},set selection(selection){if(selection.length<=1){throw new Error('Only supports multiple items');}
+this.setSelectionWithoutErrorChecks(selection);},get selection(){return this.currentSelection_;},setSelectionWithoutErrorChecks(selection){this.currentSelection_=selection;if(this.isAttached)this.updateContents_();},get eventsHaveDuration(){return this.eventsHaveDuration_;},set eventsHaveDuration(eventsHaveDuration){this.eventsHaveDuration_=eventsHaveDuration;if(this.isAttached)this.updateContents_();},get eventsHaveSubRows(){return this.eventsHaveSubRows_;},set eventsHaveSubRows(eventsHaveSubRows){this.eventsHaveSubRows_=eventsHaveSubRows;if(this.isAttached)this.updateContents_();},buildHistogram_(selectedKey){let leftBoundary=Number.MAX_VALUE;let rightBoundary=tr.b.math.Statistics.percentile(this.currentSelection_,0.95,function(value){leftBoundary=Math.min(leftBoundary,value[selectedKey]);return value[selectedKey];});if(leftBoundary===rightBoundary)rightBoundary+=1;const histogram=new tr.v.Histogram('',tr.b.Unit.byName.timeDurationInMs,tr.v.HistogramBinBoundaries.createLinear(leftBoundary,rightBoundary,Math.ceil(Math.sqrt(this.currentSelection_.length))));histogram.customizeSummaryOptions({sum:false,percentile:[0.5,0.9],});for(const slice of this.currentSelection_){histogram.addSample(slice[selectedKey],buildDiagnostics_(slice));}
+return histogram;},updateContents_(){const selection=this.currentSelection_;if(!selection)return;const eventsByTitle=selection.getEventsOrganizedByTitle();const numTitles=Object.keys(eventsByTitle).length;this.$.eventSummaryTable.configure({showTotals:numTitles>1,eventsByTitle,eventsHaveDuration:this.eventsHaveDuration_,eventsHaveSubRows:this.eventsHaveSubRows_});this.$.selectionSummaryTable.selection=this.currentSelection_;if(numTitles===1){this.$.radioPicker.style.display='block';this.$.histogramContainer.style.display='flex';this.$.histogramSpan.build(this.buildHistogram_(this.$.radioPicker.selectedKey));if(this.$.histogramSpan.histogram.numValues===0){this.$.histogramContainer.style.display='none';}}else{this.$.radioPicker.style.display='none';this.$.histogramContainer.style.display='none';}}});return{};});'use strict';tr.exportTo('tr.ui.analysis',function(){const FLOW_IN=0x1;const FLOW_OUT=0x2;const FLOW_IN_OUT=FLOW_IN|FLOW_OUT;function FlowClassifier(){this.numEvents_=0;this.eventsByGUID_={};}
+FlowClassifier.prototype={getFS_(event){let fs=this.eventsByGUID_[event.guid];if(fs===undefined){this.numEvents_++;fs={state:0,event};this.eventsByGUID_[event.guid]=fs;}
+return fs;},addInFlow(event){const fs=this.getFS_(event);fs.state|=FLOW_IN;return event;},addOutFlow(event){const fs=this.getFS_(event);fs.state|=FLOW_OUT;return event;},hasEvents(){return this.numEvents_>0;},get inFlowEvents(){const selection=new tr.model.EventSet();for(const guid in this.eventsByGUID_){const fs=this.eventsByGUID_[guid];if(fs.state===FLOW_IN){selection.push(fs.event);}}
+return selection;},get outFlowEvents(){const selection=new tr.model.EventSet();for(const guid in this.eventsByGUID_){const fs=this.eventsByGUID_[guid];if(fs.state===FLOW_OUT){selection.push(fs.event);}}
+return selection;},get internalFlowEvents(){const selection=new tr.model.EventSet();for(const guid in this.eventsByGUID_){const fs=this.eventsByGUID_[guid];if(fs.state===FLOW_IN_OUT){selection.push(fs.event);}}
+return selection;}};return{FlowClassifier,};});'use strict';function*getEventInFlowEvents(event){if(!event.inFlowEvents)return;yield*event.inFlowEvents;}
+function*getEventOutFlowEvents(event){if(!event.outFlowEvents)return;yield*event.outFlowEvents;}
+function*getEventAncestors(event){if(!event.enumerateAllAncestors)return;yield*event.enumerateAllAncestors();}
+function*getEventDescendents(event){if(!event.enumerateAllDescendents)return;yield*event.enumerateAllDescendents();}
+Polymer({is:'tr-ui-a-related-events',ready(){this.eventGroups_=[];this.cancelFunctions_=[];this.$.table.tableColumns=[{title:'Event(s)',value(row){const typeEl=document.createElement('span');typeEl.innerText=row.type;if(row.tooltip){typeEl.title=row.tooltip;}
+return typeEl;},width:'150px'},{title:'Link',width:'100%',value(row){const linkEl=document.createElement('tr-ui-a-analysis-link');if(row.name){linkEl.setSelectionAndContent(row.selection,row.name);}else{linkEl.selection=row.selection;}
+return linkEl;}}];},hasRelatedEvents(){return(this.eventGroups_&&this.eventGroups_.length>0);},setRelatedEvents(eventSet){this.cancelAllTasks_();this.eventGroups_=[];this.addRuntimeCallStats_(eventSet);this.addOverlappingV8ICStats_(eventSet);this.addV8GCObjectStats_(eventSet);this.addV8Slices_(eventSet);this.addConnectedFlows_(eventSet);this.addConnectedEvents_(eventSet);this.addOverlappingSamples_(eventSet);this.updateContents_();},addConnectedFlows_(eventSet){const classifier=new tr.ui.analysis.FlowClassifier();eventSet.forEach(function(slice){if(slice.inFlowEvents){slice.inFlowEvents.forEach(function(flow){classifier.addInFlow(flow);});}
+if(slice.outFlowEvents){slice.outFlowEvents.forEach(function(flow){classifier.addOutFlow(flow);});}});if(!classifier.hasEvents())return;const addToEventGroups=function(type,flowEvent){this.eventGroups_.push({type,selection:new tr.model.EventSet(flowEvent),name:flowEvent.title});};classifier.inFlowEvents.forEach(addToEventGroups.bind(this,'Incoming flow'));classifier.outFlowEvents.forEach(addToEventGroups.bind(this,'Outgoing flow'));classifier.internalFlowEvents.forEach(addToEventGroups.bind(this,'Internal flow'));},cancelAllTasks_(){this.cancelFunctions_.forEach(function(cancelFunction){cancelFunction();});this.cancelFunctions_=[];},addConnectedEvents_(eventSet){this.cancelFunctions_.push(this.createEventsLinkIfNeeded_('Preceding events','Add all events that have led to the selected one(s), connected by '+'flow arrows or by call stack.',eventSet,function*(event){yield*getEventInFlowEvents(event);yield*getEventAncestors(event);if(event.startSlice){yield event.startSlice;}}.bind(this)));this.cancelFunctions_.push(this.createEventsLinkIfNeeded_('Following events','Add all events that have been caused by the selected one(s), '+'connected by flow arrows or by call stack.',eventSet,function*(event){yield*getEventOutFlowEvents(event);yield*getEventDescendents(event);if(event.endSlice){yield event.endSlice;}}.bind(this)));this.cancelFunctions_.push(this.createEventsLinkIfNeeded_('All connected events','Add all events connected to the selected one(s) by flow arrows or '+'by call stack.',eventSet,function*(event){yield*getEventInFlowEvents(event);yield*getEventOutFlowEvents(event);yield*getEventAncestors(event);yield*getEventDescendents(event);if(event.startSlice){yield event.startSlice;}
+if(event.endSlice){yield event.endSlice;}}.bind(this)));},createEventsLinkIfNeeded_(title,tooltip,events,connectedFn){events=new tr.model.EventSet(events);const eventsToProcess=new Set(events);let wasChanged=false;let task;let isCanceled=false;function addEventsUntilTimeout(){if(isCanceled)return;const timeout=window.performance.now()+8;while(eventsToProcess.size>0&&window.performance.now()<=timeout){const nextEvent=tr.b.getFirstElement(eventsToProcess);eventsToProcess.delete(nextEvent);for(const eventToAdd of connectedFn(nextEvent)){if(!events.contains(eventToAdd)){events.push(eventToAdd);eventsToProcess.add(eventToAdd);wasChanged=true;}}}
+if(eventsToProcess.size>0){const newTask=new tr.b.Task(addEventsUntilTimeout.bind(this),this);task.after(newTask);task=newTask;return;}
+if(!wasChanged)return;this.eventGroups_.push({type:title,tooltip,selection:events});this.updateContents_();}
+function cancelTask(){isCanceled=true;}
+task=new tr.b.Task(addEventsUntilTimeout.bind(this),this);tr.b.Task.RunWhenIdle(task);return cancelTask;},addOverlappingSamples_(eventSet){const samples=new tr.model.EventSet();for(const slice of eventSet){if(!slice.parentContainer||!slice.parentContainer.samples){continue;}
+const candidates=slice.parentContainer.samples;const range=tr.b.math.Range.fromExplicitRange(slice.start,slice.start+slice.duration);const filteredSamples=range.filterArray(candidates,function(value){return value.start;});for(const sample of filteredSamples){samples.push(sample);}}
+if(samples.length>0){this.eventGroups_.push({type:'Overlapping samples',tooltip:'All samples overlapping the selected slice(s).',selection:samples});}},addV8Slices_(eventSet){const v8Slices=new tr.model.EventSet();for(const slice of eventSet){if(slice.category==='v8'){v8Slices.push(slice);}}
+if(v8Slices.length>0){this.eventGroups_.push({type:'V8 Slices',tooltip:'All V8 slices in the selected slice(s).',selection:v8Slices});}},addRuntimeCallStats_(eventSet){const slices=eventSet.filter(function(slice){return(slice.category==='v8'||slice.category==='disabled-by-default-v8.runtime_stats')&&slice.runtimeCallStats;});if(slices.length>0){this.eventGroups_.push({type:'Runtime call stats table',tooltip:'All V8 slices containing runtime call stats table in the selected slice(s).',selection:slices});}},addV8GCObjectStats_(eventSet){const slices=new tr.model.EventSet();for(const slice of eventSet){if(slice.title==='V8.GC_Objects_Stats'){slices.push(slice);}}
+if(slices.length>0){this.eventGroups_.push({type:'V8 GC stats table',tooltip:'All V8 GC statistics slices in the selected set.',selection:slices});}},addOverlappingV8ICStats_(eventSet){const slices=new tr.model.EventSet();for(const slice of eventSet){if(!slice.parentContainer||!slice.parentContainer.sliceGroup){continue;}
+const sliceGroup=slice.parentContainer.sliceGroup.slices;const range=tr.b.math.Range.fromExplicitRange(slice.start,slice.start+slice.duration);const filteredSlices=range.filterArray(sliceGroup,value=>value.start);const icSlices=filteredSlices.filter(x=>x.title==='V8.ICStats');for(const icSlice of icSlices){slices.push(icSlice);}}
+if(slices.length>0){this.eventGroups_.push({type:'Overlapping V8 IC stats',tooltip:'All V8 IC statistics overlapping the selected set.',selection:slices});}},updateContents_(){const table=this.$.table;if(this.eventGroups_===undefined){table.tableRows=[];}else{table.tableRows=this.eventGroups_.slice();}
+table.rebuild();}});'use strict';Polymer({is:'tr-ui-a-multi-async-slice-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],get selection(){return this.$.content.selection;},set selection(selection){this.$.content.selection=selection;this.$.relatedEvents.setRelatedEvents(selection);if(this.$.relatedEvents.hasRelatedEvents()){this.$.relatedEvents.style.display='';}else{this.$.relatedEvents.style.display='none';}},get relatedEventsToHighlight(){if(!this.$.content.selection)return undefined;const selection=new tr.model.EventSet();this.$.content.selection.forEach(function(asyncEvent){if(!asyncEvent.associatedEvents)return;asyncEvent.associatedEvents.forEach(function(event){selection.push(event);});});if(selection.length)return selection;return undefined;}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-multi-async-slice-sub-view',tr.model.AsyncSlice,{multi:true,title:'Async Slices',});'use strict';Polymer({is:'tr-ui-a-multi-cpu-slice-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],ready(){this.$.content.eventsHaveSubRows=false;},get selection(){return this.$.content.selection;},set selection(selection){this.$.content.setSelectionWithoutErrorChecks(selection);}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-multi-cpu-slice-sub-view',tr.model.CpuSlice,{multi:true,title:'CPU Slices',});'use strict';Polymer({is:'tr-ui-a-multi-flow-event-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],ready(){this.$.content.eventsHaveDuration=false;this.$.content.eventsHaveSubRows=false;},set selection(selection){this.$.content.selection=selection;},get selection(){return this.$.content.selection;}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-multi-flow-event-sub-view',tr.model.FlowEvent,{multi:true,title:'Flow Events',});'use strict';Polymer({is:'tr-ui-a-multi-frame-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],created(){this.currentSelection_=undefined;},set selection(selection){Polymer.dom(this).textContent='';const realView=document.createElement('tr-ui-a-multi-event-sub-view');realView.eventsHaveDuration=false;realView.eventsHaveSubRows=false;Polymer.dom(this).appendChild(realView);realView.setSelectionWithoutErrorChecks(selection);this.currentSelection_=selection;},get selection(){return this.currentSelection_;},get relatedEventsToHighlight(){if(!this.currentSelection_)return undefined;const selection=new tr.model.EventSet();this.currentSelection_.forEach(function(frameEvent){frameEvent.associatedEvents.forEach(function(event){selection.push(event);});});return selection;}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-multi-frame-sub-view',tr.model.Frame,{multi:true,title:'Frames',});'use strict';Polymer({is:'tr-ui-a-multi-instant-event-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],created(){this.currentSelection_=undefined;},set selection(selection){Polymer.dom(this.$.content).textContent='';const realView=document.createElement('tr-ui-a-multi-event-sub-view');realView.eventsHaveDuration=false;realView.eventsHaveSubRows=false;Polymer.dom(this.$.content).appendChild(realView);realView.setSelectionWithoutErrorChecks(selection);this.currentSelection_=selection;},get selection(){return this.currentSelection_;}});'use strict';Polymer({is:'tr-ui-a-multi-object-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],created(){this.currentSelection_=undefined;},ready(){this.$.content.showHeader=false;},get selection(){return this.currentSelection_;},set selection(selection){this.currentSelection_=selection;const objectEvents=Array.from(selection).sort(tr.b.math.Range.compareByMinTimes);const timeSpanConfig={unit:tr.b.Unit.byName.timeStampInMs,ownerDocument:this.ownerDocument};const table=this.$.content;table.tableColumns=[{title:'First',value(event){if(event instanceof tr.model.ObjectSnapshot){return tr.v.ui.createScalarSpan(event.ts,timeSpanConfig);}
+const spanEl=document.createElement('span');Polymer.dom(spanEl).appendChild(tr.v.ui.createScalarSpan(event.creationTs,timeSpanConfig));Polymer.dom(spanEl).appendChild(tr.ui.b.createSpan({textContent:'-',marginLeft:'4px',marginRight:'4px'}));if(event.deletionTs!==Number.MAX_VALUE){Polymer.dom(spanEl).appendChild(tr.v.ui.createScalarSpan(event.deletionTs,timeSpanConfig));}
+return spanEl;},width:'200px'},{title:'Second',value(event){const linkEl=document.createElement('tr-ui-a-analysis-link');linkEl.setSelectionAndContent(function(){return new tr.model.EventSet(event);},event.userFriendlyName);return linkEl;},width:'100%'}];table.tableRows=objectEvents;table.rebuild();}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-multi-object-sub-view',tr.model.ObjectInstance,{multi:true,title:'Object Instances',});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-multi-object-sub-view',tr.model.ObjectSnapshot,{multi:true,title:'Object Snapshots',});'use strict';const EventSet=tr.model.EventSet;const CHART_TITLE='Power (W) by ms since vertical sync';Polymer({is:'tr-ui-a-frame-power-usage-chart',ready(){this.chart_=undefined;this.samples_=new EventSet();this.vSyncTimestamps_=[];},attached(){if(this.samples_)this.updateContents_();},get chart(){return this.chart_;},get samples(){return this.samples_;},get vSyncTimestamps(){return this.vSyncTimestamps_;},setData(samples,vSyncTimestamps){this.samples_=(samples===undefined)?new EventSet():samples;this.vSyncTimestamps_=(vSyncTimestamps===undefined)?[]:vSyncTimestamps;if(this.isAttached)this.updateContents_();},updateContents_(){this.clearChart_();const data=this.getDataForLineChart_();if(data.length===0)return;this.chart_=new tr.ui.b.LineChart();Polymer.dom(this.$.content).appendChild(this.chart_);this.chart_.chartTitle=CHART_TITLE;this.chart_.data=data;},clearChart_(){const content=this.$.content;while(Polymer.dom(content).firstChild){Polymer.dom(content).removeChild(Polymer.dom(content).firstChild);}
+this.chart_=undefined;},getDataForLineChart_(){const sortedSamples=this.sortSamplesByTimestampAscending_(this.samples);const vSyncTimestamps=this.vSyncTimestamps.slice();let lastVSyncTimestamp=undefined;const points=[];let frameNumber=0;sortedSamples.forEach(function(sample){while(vSyncTimestamps.length>0&&vSyncTimestamps[0]<=sample.start){lastVSyncTimestamp=vSyncTimestamps.shift();frameNumber++;}
+if(lastVSyncTimestamp===undefined)return;const point={x:sample.start-lastVSyncTimestamp};point['f'+frameNumber]=sample.powerInW;points.push(point);});return points;},sortSamplesByTimestampAscending_(samples){return samples.toArray().sort(function(smpl1,smpl2){return smpl1.start-smpl2.start;});}});'use strict';Polymer({is:'tr-ui-a-power-sample-summary-table',ready(){this.$.table.tableColumns=[{title:'Min power',width:'100px',value(row){return tr.b.Unit.byName.powerInWatts.format(row.min);}},{title:'Max power',width:'100px',value(row){return tr.b.Unit.byName.powerInWatts.format(row.max);}},{title:'Time-weighted average',width:'100px',value(row){return tr.b.Unit.byName.powerInWatts.format(row.timeWeightedAverageInW);}},{title:'Energy consumed',width:'100px',value(row){return tr.b.Unit.byName.energyInJoules.format(row.energyConsumedInJ);}},{title:'Sample count',width:'100%',value(row){return row.sampleCount;}}];this.samples=new tr.model.EventSet();},get samples(){return this.samples_;},set samples(samples){if(samples===this.samples)return;this.samples_=(samples===undefined)?new tr.model.EventSet():samples;this.updateContents_();},updateContents_(){if(this.samples.length===0){this.$.table.tableRows=[];}else{this.$.table.tableRows=[{min:this.getMin(),max:this.getMax(),timeWeightedAverageInW:this.getTimeWeightedAverageInW(),energyConsumedInJ:this.getEnergyConsumedInJ(),sampleCount:this.samples.length}];}
+this.$.table.rebuild();},getMin(){return Math.min.apply(null,this.samples.map(function(sample){return sample.powerInW;}));},getMax(){return Math.max.apply(null,this.samples.map(function(sample){return sample.powerInW;}));},getTimeWeightedAverageInW(){const energyConsumedInJ=this.getEnergyConsumedInJ();if(energyConsumedInJ==='N/A')return'N/A';const durationInS=tr.b.convertUnit(this.samples.bounds.duration,tr.b.UnitPrefixScale.METRIC.MILLI,tr.b.UnitPrefixScale.METRIC.NONE);return energyConsumedInJ/durationInS;},getEnergyConsumedInJ(){if(this.samples.length<2)return'N/A';const bounds=this.samples.bounds;const series=tr.b.getFirstElement(this.samples).series;return series.getEnergyConsumedInJ(bounds.min,bounds.max);}});'use strict';Polymer({is:'tr-ui-a-multi-power-sample-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],ready(){this.currentSelection_=undefined;},get selection(){return this.currentSelection_;},set selection(selection){this.currentSelection_=selection;this.updateContents_();},updateContents_(){const samples=this.selection;const vSyncTimestamps=(!samples?[]:tr.b.getFirstElement(samples).series.device.vSyncTimestamps);this.$.summaryTable.samples=samples;this.$.chart.setData(this.selection,vSyncTimestamps);}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-multi-power-sample-sub-view',tr.model.PowerSample,{multi:true,title:'Power Samples',});'use strict';(function(){const MultiDimensionalViewBuilder=tr.b.MultiDimensionalViewBuilder;Polymer({is:'tr-ui-a-multi-sample-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],created(){this.viewOption_=undefined;this.selection_=undefined;},ready(){const viewSelector=tr.ui.b.createSelector(this,'viewOption','tracing.ui.analysis.multi_sample_sub_view',MultiDimensionalViewBuilder.ViewType.TOP_DOWN_TREE_VIEW,[{label:'Top-down (Tree)',value:MultiDimensionalViewBuilder.ViewType.TOP_DOWN_TREE_VIEW},{label:'Top-down (Heavy)',value:MultiDimensionalViewBuilder.ViewType.TOP_DOWN_HEAVY_VIEW},{label:'Bottom-up (Heavy)',value:MultiDimensionalViewBuilder.ViewType.BOTTOM_UP_HEAVY_VIEW}]);Polymer.dom(this.$.control).appendChild(viewSelector);this.$.table.selectionMode=tr.ui.b.TableFormat.SelectionMode.ROW;},get selection(){return this.selection_;},set selection(selection){this.selection_=selection;this.updateContents_();},get viewOption(){return this.viewOption_;},set viewOption(viewOption){this.viewOption_=viewOption;this.updateContents_();},createSamplingSummary_(selection,viewOption){const builder=new MultiDimensionalViewBuilder(1,1);const samples=selection.filter(event=>event instanceof tr.model.Sample);samples.forEach(function(sample){builder.addPath([sample.userFriendlyStack.reverse()],[1],MultiDimensionalViewBuilder.ValueKind.SELF);});return builder.buildView(viewOption);},processSampleRows_(rows){for(const row of rows){let title=row.title[0];let results=/(.*) (Deoptimized reason: .*)/.exec(title);if(results!==null){row.deoptReason=results[2];title=results[1];}
+results=/(.*) url: (.*)/.exec(title);if(results!==null){row.functionName=results[1];row.url=results[2];if(row.functionName===''){row.functionName='(anonymous function)';}
+if(row.url===''){row.url='unknown';}}else{row.functionName=title;row.url='unknown';}
+this.processSampleRows_(row.subRows);}},updateContents_(){if(this.selection===undefined){this.$.table.tableColumns=[];this.$.table.tableRows=[];this.$.table.rebuild();return;}
+const samplingData=this.createSamplingSummary_(this.selection,this.viewOption);const total=samplingData.values[0].total;const columns=[this.createPercentColumn_('Total',total),this.createSamplesColumn_('Total'),this.createPercentColumn_('Self',total),this.createSamplesColumn_('Self'),{title:'Function Name',value(row){if(row.deoptReason!==undefined){const spanEl=tr.ui.b.createSpan({italic:true,color:'#F44336',tooltip:row.deoptReason});spanEl.innerText=row.functionName;return spanEl;}
+return row.functionName;},width:'150px',cmp:(a,b)=>a.functionName.localeCompare(b.functionName),showExpandButtons:true},{title:'Location',value(row){return row.url;},width:'250px',cmp:(a,b)=>a.url.localeCompare(b.url),}];this.processSampleRows_(samplingData.subRows);this.$.table.tableColumns=columns;this.$.table.sortColumnIndex=1;this.$.table.sortDescending=true;this.$.table.tableRows=samplingData.subRows;this.$.table.rebuild();},createPercentColumn_(title,samplingDataTotal){const field=title.toLowerCase();return{title:title+' percent',value(row){return tr.v.ui.createScalarSpan(row.values[0][field]/samplingDataTotal,{customContextRange:tr.b.math.Range.PERCENT_RANGE,unit:tr.b.Unit.byName.normalizedPercentage,context:{minimumFractionDigits:2,maximumFractionDigits:2},});},width:'60px',cmp:(a,b)=>a.values[0][field]-b.values[0][field]};},createSamplesColumn_(title){const field=title.toLowerCase();return{title:title+' samples',value(row){return tr.v.ui.createScalarSpan(row.values[0][field],{unit:tr.b.Unit.byName.unitlessNumber,context:{maximumFractionDigits:0},});},width:'60px',cmp:(a,b)=>a.values[0][field]-b.values[0][field]};}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-multi-sample-sub-view',tr.model.Sample,{multi:true,title:'Samples',});})();'use strict';Polymer({is:'tr-ui-a-multi-thread-slice-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],created(){this.selection_=undefined;},get selection(){return this.selection_;},set selection(selection){this.selection_=selection;if(tr.isExported('tr.ui.e.chrome.cc.RasterTaskSelection')){if(tr.ui.e.chrome.cc.RasterTaskSelection.supports(selection)){const ltvSelection=new tr.ui.e.chrome.cc.RasterTaskSelection(selection);const ltv=new tr.ui.e.chrome.cc.LayerTreeHostImplSnapshotView();ltv.objectSnapshot=ltvSelection.containingSnapshot;ltv.selection=ltvSelection;ltv.extraHighlightsByLayerId=ltvSelection.extraHighlightsByLayerId;Polymer.dom(this.$.content).textContent='';Polymer.dom(this.$.content).appendChild(ltv);this.requiresTallView_=true;return;}}
+Polymer.dom(this.$.content).textContent='';const mesv=document.createElement('tr-ui-a-multi-event-sub-view');mesv.selection=selection;Polymer.dom(this.$.content).appendChild(mesv);const relatedEvents=document.createElement('tr-ui-a-related-events');relatedEvents.setRelatedEvents(selection);if(relatedEvents.hasRelatedEvents()){Polymer.dom(this.$.content).appendChild(relatedEvents);}},get requiresTallView(){if(this.$.content.children.length===0)return false;const childTagName=this.$.content.children[0].tagName;if(childTagName==='TR-UI-A-MULTI-EVENT-SUB-VIEW'){return false;}
+return true;}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-multi-thread-slice-sub-view',tr.model.ThreadSlice,{multi:true,title:'Slices',});'use strict';Polymer({is:'tr-ui-a-multi-thread-time-slice-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],ready(){this.$.content.eventsHaveSubRows=false;},get selection(){return this.$.content.selection;},set selection(selection){this.$.content.setSelectionWithoutErrorChecks(selection);}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-multi-thread-time-slice-sub-view',tr.model.ThreadTimeSlice,{multi:true,title:'Thread Timeslices',});'use strict';Polymer({is:'tr-ui-a-user-expectation-related-samples-table',ready(){this.samples_=[];this.$.table.tableColumns=[{title:'Event(s)',value(row){const typeEl=document.createElement('span');typeEl.innerText=row.type;if(row.tooltip){typeEl.title=row.tooltip;}
+return typeEl;},width:'150px'},{title:'Link',width:'100%',value(row){const linkEl=document.createElement('tr-ui-a-analysis-link');if(row.name){linkEl.setSelectionAndContent(row.selection,row.name);}else{linkEl.selection=row.selection;}
+return linkEl;}}];},hasRelatedSamples(){return(this.samples_&&this.samples_.length>0);},set selection(eventSet){this.samples_=[];const samples=new tr.model.EventSet;eventSet.forEach(function(ue){samples.addEventSet(ue.associatedSamples);}.bind(this));if(samples.length>0){this.samples_.push({type:'Overlapping samples',tooltip:'All samples overlapping the selected user expectation(s).',selection:samples});}
+this.updateContents_();},updateContents_(){const table=this.$.table;if(this.samples_&&this.samples_.length>0){table.tableRows=this.samples_.slice();}else{table.tableRows=[];}
+table.rebuild();}});'use strict';Polymer({is:'tr-ui-a-multi-interaction-record-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],created(){this.currentSelection_=undefined;},set selection(selection){this.currentSelection_=selection;this.$.realView.setSelectionWithoutErrorChecks(selection);this.currentSelection_=selection;this.$.relatedSamples.selection=selection;if(this.$.relatedSamples.hasRelatedSamples()){this.$.events.style.display='';}else{this.$.events.style.display='none';}},get selection(){return this.currentSelection_;},get relatedEventsToHighlight(){if(!this.currentSelection_)return undefined;const selection=new tr.model.EventSet();this.currentSelection_.forEach(function(ir){ir.associatedEvents.forEach(function(event){selection.push(event);});});return selection;}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-single-user-expectation-sub-view',tr.model.um.UserExpectation,{multi:true,title:'User Expectations',});'use strict';Polymer({is:'tr-ui-a-single-async-slice-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],get selection(){return this.$.content.selection;},set selection(selection){if(selection.length!==1){throw new Error('Only supports single slices');}
+this.$.content.setSelectionWithoutErrorChecks(selection);this.$.relatedEvents.setRelatedEvents(selection);if(this.$.relatedEvents.hasRelatedEvents()){this.$.relatedEvents.style.display='';}else{this.$.relatedEvents.style.display='none';}},getEventRows_(event){const rows=this.__proto__.__proto__.getEventRows_(event);rows.splice(0,0,{name:'ID',value:event.id});return rows;},get relatedEventsToHighlight(){if(!this.currentSelection_)return undefined;return tr.b.getOnlyElement(this.currentSelection_).associatedEvents;}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-single-async-slice-sub-view',tr.model.AsyncSlice,{multi:false,title:'Async Slice',});'use strict';Polymer({is:'tr-ui-a-single-cpu-slice-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],created(){this.currentSelection_=undefined;},get selection(){return this.currentSelection_;},set selection(selection){const cpuSlice=tr.b.getOnlyElement(selection);if(!(cpuSlice instanceof tr.model.CpuSlice)){throw new Error('Only supports thread time slices');}
+this.currentSelection_=selection;const thread=cpuSlice.threadThatWasRunning;const root=Polymer.dom(this.root);if(thread){Polymer.dom(root.querySelector('#process-name')).textContent=thread.parent.userFriendlyName;Polymer.dom(root.querySelector('#thread-name')).textContent=thread.userFriendlyName;}else{root.querySelector('#process-name').parentElement.style.display='none';Polymer.dom(root.querySelector('#thread-name')).textContent=cpuSlice.title;}
+root.querySelector('#start').setValueAndUnit(cpuSlice.start,tr.b.Unit.byName.timeStampInMs);root.querySelector('#duration').setValueAndUnit(cpuSlice.duration,tr.b.Unit.byName.timeDurationInMs);const runningThreadEl=root.querySelector('#running-thread');const timeSlice=cpuSlice.getAssociatedTimeslice();if(!timeSlice){runningThreadEl.parentElement.style.display='none';}else{const threadLink=document.createElement('tr-ui-a-analysis-link');threadLink.selection=new tr.model.EventSet(timeSlice);Polymer.dom(threadLink).textContent='Click to select';runningThreadEl.parentElement.style.display='';Polymer.dom(runningThreadEl).textContent='';Polymer.dom(runningThreadEl).appendChild(threadLink);}
+root.querySelector('#args').object=cpuSlice.args;}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-single-cpu-slice-sub-view',tr.model.CpuSlice,{multi:false,title:'CPU Slice',});'use strict';function createAnalysisLinkTo(event){const linkEl=document.createElement('tr-ui-a-analysis-link');linkEl.setSelectionAndContent(new tr.model.EventSet(event),event.userFriendlyName);return linkEl;}
+Polymer({is:'tr-ui-a-single-flow-event-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],listeners:{'singleEventSubView.customize-rows':'onCustomizeRows_'},set selection(selection){this.currentSelection_=selection;this.$.singleEventSubView.setSelectionWithoutErrorChecks(selection);},get selection(){return this.currentSelection_;},onCustomizeRows_(e){const event=tr.b.getOnlyElement(this.currentSelection_);const rows=e.rows;rows.unshift({name:'ID',value:event.id});rows.push({name:'From',value:createAnalysisLinkTo(event.startSlice)});rows.push({name:'To',value:createAnalysisLinkTo(event.endSlice)});}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-single-flow-event-sub-view',tr.model.FlowEvent,{multi:false,title:'Flow Event',});'use strict';Polymer({is:'tr-ui-a-single-frame-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],ready(){this.currentSelection_=undefined;},get selection(){return this.currentSelection_;},set selection(selection){this.currentSelection_=selection;this.$.asv.selection=tr.b.getOnlyElement(selection).associatedAlerts;},get relatedEventsToHighlight(){if(!this.currentSelection_)return undefined;return tr.b.getOnlyElement(this.currentSelection_).associatedEvents;}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-single-frame-sub-view',tr.model.Frame,{multi:false,title:'Frame',});'use strict';Polymer({is:'tr-ui-a-single-instant-event-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],created(){this.currentSelection_=undefined;},set selection(selection){Polymer.dom(this.$.content).textContent='';const realView=document.createElement('tr-ui-a-single-event-sub-view');realView.setSelectionWithoutErrorChecks(selection);Polymer.dom(this.$.content).appendChild(realView);this.currentSelection_=selection;},get selection(){return this.currentSelection_;}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-single-instant-event-sub-view',tr.model.InstantEvent,{multi:false,title:'Instant Event',});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-multi-instant-event-sub-view',tr.model.InstantEvent,{multi:true,title:'Instant Events',});'use strict';tr.exportTo('tr.ui.analysis',function(){const ObjectInstanceView=tr.ui.b.define('object-instance-view');ObjectInstanceView.prototype={__proto__:HTMLDivElement.prototype,decorate(){this.objectInstance_=undefined;},get requiresTallView(){return true;},set modelEvent(obj){this.objectInstance=obj;},get modelEvent(){return this.objectInstance;},get objectInstance(){return this.objectInstance_;},set objectInstance(i){this.objectInstance_=i;this.updateContents();},updateContents(){throw new Error('Not implemented');}};const options=new tr.b.ExtensionRegistryOptions(tr.b.TYPE_BASED_REGISTRY_MODE);options.mandatoryBaseClass=ObjectInstanceView;options.defaultMetadata={showInTrackView:true};tr.b.decorateExtensionRegistry(ObjectInstanceView,options);return{ObjectInstanceView,};});'use strict';Polymer({is:'tr-ui-a-single-object-instance-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],created(){this.currentSelection_=undefined;},get requiresTallView(){if(this.$.content.children.length===0){return false;}
+if(this.$.content.children[0]instanceof
+tr.ui.analysis.ObjectInstanceView){return this.$.content.children[0].requiresTallView;}},get selection(){return this.currentSelection_;},set selection(selection){const instance=tr.b.getOnlyElement(selection);if(!(instance instanceof tr.model.ObjectInstance)){throw new Error('Only supports object instances');}
+Polymer.dom(this.$.content).textContent='';this.currentSelection_=selection;const typeInfo=tr.ui.analysis.ObjectInstanceView.getTypeInfo(instance.category,instance.typeName);if(typeInfo){const customView=new typeInfo.constructor();Polymer.dom(this.$.content).appendChild(customView);customView.modelEvent=instance;}else{this.appendGenericAnalysis_(instance);}},appendGenericAnalysis_(instance){let html='';html+='<div class="title">'+
+instance.typeName+' '+
+instance.id+'</div>\n';html+='<table>';html+='<tr>';html+='<tr><td>creationTs:</td><td>'+
+instance.creationTs+'</td></tr>\n';if(instance.deletionTs!==Number.MAX_VALUE){html+='<tr><td>deletionTs:</td><td>'+
+instance.deletionTs+'</td></tr>\n';}else{html+='<tr><td>deletionTs:</td><td>not deleted</td></tr>\n';}
+html+='<tr><td>snapshots:</td><td id="snapshots"></td></tr>\n';html+='</table>';Polymer.dom(this.$.content).innerHTML=html;const snapshotsEl=Polymer.dom(this.$.content).querySelector('#snapshots');instance.snapshots.forEach(function(snapshot){const snapshotLink=document.createElement('tr-ui-a-analysis-link');snapshotLink.selection=new tr.model.EventSet(snapshot);Polymer.dom(snapshotsEl).appendChild(snapshotLink);});}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-single-object-instance-sub-view',tr.model.ObjectInstance,{multi:false,title:'Object Instance',});'use strict';Polymer({is:'tr-ui-a-single-object-snapshot-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],created(){this.currentSelection_=undefined;},get requiresTallView(){if(this.children.length===0){return false;}
+if(this.children[0]instanceof tr.ui.analysis.ObjectSnapshotView){return this.children[0].requiresTallView;}},get selection(){return this.currentSelection_;},set selection(selection){const snapshot=tr.b.getOnlyElement(selection);if(!(snapshot instanceof tr.model.ObjectSnapshot)){throw new Error('Only supports object instances');}
+Polymer.dom(this).textContent='';this.currentSelection_=selection;const typeInfo=tr.ui.analysis.ObjectSnapshotView.getTypeInfo(snapshot.objectInstance.category,snapshot.objectInstance.typeName);if(typeInfo){const customView=new typeInfo.constructor();Polymer.dom(this).appendChild(customView);customView.modelEvent=snapshot;}else{this.appendGenericAnalysis_(snapshot);}},appendGenericAnalysis_(snapshot){const instance=snapshot.objectInstance;Polymer.dom(this).textContent='';const titleEl=document.createElement('div');Polymer.dom(titleEl).classList.add('title');Polymer.dom(titleEl).appendChild(document.createTextNode('Snapshot of '));Polymer.dom(this).appendChild(titleEl);const instanceLinkEl=document.createElement('tr-ui-a-analysis-link');instanceLinkEl.selection=new tr.model.EventSet(instance);Polymer.dom(titleEl).appendChild(instanceLinkEl);Polymer.dom(titleEl).appendChild(document.createTextNode(' @ '));Polymer.dom(titleEl).appendChild(tr.v.ui.createScalarSpan(snapshot.ts,{unit:tr.b.Unit.byName.timeStampInMs,ownerDocument:this.ownerDocument,inline:true,}));const tableEl=document.createElement('table');Polymer.dom(this).appendChild(tableEl);const rowEl=document.createElement('tr');Polymer.dom(tableEl).appendChild(rowEl);const labelEl=document.createElement('td');Polymer.dom(labelEl).textContent='args:';Polymer.dom(rowEl).appendChild(labelEl);const argsEl=document.createElement('td');argsEl.id='args';Polymer.dom(rowEl).appendChild(argsEl);const objectViewEl=document.createElement('tr-ui-a-generic-object-view');objectViewEl.object=snapshot.args;Polymer.dom(argsEl).appendChild(objectViewEl);}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-single-object-snapshot-sub-view',tr.model.ObjectSnapshot,{multi:false,title:'Object Snapshot',});'use strict';Polymer({is:'tr-ui-a-power-sample-table',ready(){this.$.table.tableColumns=[{title:'Time',width:'100px',value(row){return tr.v.ui.createScalarSpan(row.start,{unit:tr.b.Unit.byName.timeStampInMs});}},{title:'Power',width:'100%',value(row){return tr.v.ui.createScalarSpan(row.powerInW,{unit:tr.b.Unit.byName.powerInWatts});}}];this.sample=undefined;},get sample(){return this.sample_;},set sample(sample){this.sample_=sample;this.updateContents_();},updateContents_(){if(this.sample===undefined){this.$.table.tableRows=[];}else{this.$.table.tableRows=[this.sample];}
+this.$.table.rebuild();}});'use strict';Polymer({is:'tr-ui-a-single-power-sample-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],ready(){this.currentSelection_=undefined;},get selection(){return this.currentSelection_;},set selection(selection){this.currentSelection_=selection;this.updateContents_();},updateContents_(){if(this.selection.length!==1){throw new Error('Cannot pass multiple samples to sample table.');}
+this.$.samplesTable.sample=tr.b.getOnlyElement(this.selection);}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-single-power-sample-sub-view',tr.model.PowerSample,{multi:false,title:'Power Sample',});'use strict';Polymer({is:'tr-ui-a-single-sample-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],created(){this.currentSelection_=undefined;},ready(){this.$.content.tableColumns=[{title:'',value:row=>row.title,width:'100px'},{title:'',value:row=>row.value,width:'100%'}];this.$.content.showHeader=false;},get selection(){return this.currentSelection_;},set selection(selection){this.currentSelection_=selection;if(this.currentSelection_===undefined){this.$.content.tableRows=[];return;}
+const sample=tr.b.getOnlyElement(this.currentSelection_);const table=this.$.content;const rows=[];rows.push({title:'Title',value:sample.title});rows.push({title:'Sample time',value:tr.v.ui.createScalarSpan(sample.start,{unit:tr.b.Unit.byName.timeStampInMs,ownerDocument:this.ownerDocument})});const callStackTableEl=document.createElement('tr-ui-b-table');callStackTableEl.tableRows=sample.getNodesAsArray().reverse();callStackTableEl.tableColumns=[{title:'function name',value:row=>row.functionName||'(anonymous function)'},{title:'location',value:row=>row.url}];callStackTableEl.rebuild();rows.push({title:'Call stack',value:callStackTableEl});table.tableRows=rows;table.rebuild();}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-single-sample-sub-view',tr.model.Sample,{multi:false,title:'Sample',});'use strict';Polymer({is:'tr-ui-a-single-thread-slice-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],get selection(){return this.$.content.selection;},set selection(selection){this.$.content.selection=selection;this.$.relatedEvents.setRelatedEvents(selection);if(this.$.relatedEvents.hasRelatedEvents()){this.$.relatedEvents.style.display='';}else{this.$.relatedEvents.style.display='none';}}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-single-thread-slice-sub-view',tr.model.ThreadSlice,{multi:false,title:'Slice',});'use strict';Polymer({is:'tr-ui-a-single-thread-time-slice-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],created(){this.currentSelection_=undefined;},get selection(){return this.currentSelection_;},set selection(selection){const timeSlice=tr.b.getOnlyElement(selection);if(!(timeSlice instanceof tr.model.ThreadTimeSlice)){throw new Error('Only supports thread time slices');}
+this.currentSelection_=selection;const thread=timeSlice.thread;const root=Polymer.dom(this.root);Polymer.dom(root.querySelector('#state')).textContent=timeSlice.title;const stateColor=tr.b.ColorScheme.colorsAsStrings[timeSlice.colorId];root.querySelector('#state').style.backgroundColor=stateColor;Polymer.dom(root.querySelector('#process-name')).textContent=thread.parent.userFriendlyName;Polymer.dom(root.querySelector('#thread-name')).textContent=thread.userFriendlyName;root.querySelector('#start').setValueAndUnit(timeSlice.start,tr.b.Unit.byName.timeStampInMs);root.querySelector('#duration').setValueAndUnit(timeSlice.duration,tr.b.Unit.byName.timeDurationInMs);const onCpuEl=root.querySelector('#on-cpu');Polymer.dom(onCpuEl).textContent='';const runningInsteadEl=root.querySelector('#running-instead');if(timeSlice.cpuOnWhichThreadWasRunning){Polymer.dom(runningInsteadEl.parentElement).removeChild(runningInsteadEl);const cpuLink=document.createElement('tr-ui-a-analysis-link');cpuLink.selection=new tr.model.EventSet(timeSlice.getAssociatedCpuSlice());Polymer.dom(cpuLink).textContent=timeSlice.cpuOnWhichThreadWasRunning.userFriendlyName;Polymer.dom(onCpuEl).appendChild(cpuLink);}else{Polymer.dom(onCpuEl.parentElement).removeChild(onCpuEl);const cpuSliceThatTookCpu=timeSlice.getCpuSliceThatTookCpu();if(cpuSliceThatTookCpu){const cpuLink=document.createElement('tr-ui-a-analysis-link');cpuLink.selection=new tr.model.EventSet(cpuSliceThatTookCpu);if(cpuSliceThatTookCpu.thread){Polymer.dom(cpuLink).textContent=cpuSliceThatTookCpu.thread.userFriendlyName;}else{Polymer.dom(cpuLink).textContent=cpuSliceThatTookCpu.title;}
+Polymer.dom(runningInsteadEl).appendChild(cpuLink);}else{Polymer.dom(runningInsteadEl.parentElement).removeChild(runningInsteadEl);}}
+const argsEl=root.querySelector('#args');if(Object.keys(timeSlice.args).length>0){const argsView=document.createElement('tr-ui-a-generic-object-view');argsView.object=timeSlice.args;argsEl.parentElement.style.display='';Polymer.dom(argsEl).textContent='';Polymer.dom(argsEl).appendChild(argsView);}else{argsEl.parentElement.style.display='none';}}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-single-thread-time-slice-sub-view',tr.model.ThreadTimeSlice,{multi:false,title:'Thread Timeslice',});'use strict';Polymer({is:'tr-ui-a-single-user-expectation-sub-view',behaviors:[tr.ui.analysis.AnalysisSubView],created(){this.currentSelection_=undefined;},get selection(){return this.currentSelection_;},set selection(selection){this.$.realView.addEventListener('customize-rows',this.onCustomizeRows_.bind(this));this.currentSelection_=selection;this.$.realView.setSelectionWithoutErrorChecks(selection);this.$.relatedSamples.selection=selection;if(this.$.relatedSamples.hasRelatedSamples()){this.$.events.style.display='';}else{this.$.events.style.display='none';}},get relatedEventsToHighlight(){if(!this.currentSelection_)return undefined;return tr.b.getOnlyElement(this.currentSelection_).associatedEvents;},onCustomizeRows_(event){const ue=tr.b.getOnlyElement(this.selection);if(ue.rawCpuMs){event.rows.push({name:'Total CPU',value:tr.v.ui.createScalarSpan(ue.totalCpuMs,{unit:tr.b.Unit.byName.timeDurationInMs})});}}});tr.ui.analysis.AnalysisSubView.register('tr-ui-a-single-user-expectation-sub-view',tr.model.um.UserExpectation,{multi:false,title:'User Expectation',});'use strict';(function(){const EventRegistry=tr.model.EventRegistry;function getTabStripLabel(numEvents){if(numEvents===0){return'Nothing selected. Tap stuff.';}else if(numEvents===1){return'1 item selected.';}
+return numEvents+' items selected.';}
+function createSubView(subViewTypeInfo,selection){let tagName;if(selection.length===1){tagName=subViewTypeInfo.singleTagName;}else{tagName=subViewTypeInfo.multiTagName;}
+if(tagName===undefined){throw new Error('No view registered for '+
+subViewTypeInfo.eventConstructor.name);}
+const subView=document.createElement(tagName);let title;if(selection.length===1){title=subViewTypeInfo.singleTitle;}else{title=subViewTypeInfo.multiTitle;}
+title+=' ('+selection.length+')';subView.tabLabel=title;subView.selection=selection;return subView;}
+Polymer({is:'tr-ui-a-analysis-view',ready(){this.brushingStateController_=undefined;this.lastSelection_=undefined;this.tabView_=document.createElement('tr-ui-b-tab-view');this.tabView_.addEventListener('selected-tab-change',this.onSelectedSubViewChanged_.bind(this));Polymer.dom(this).appendChild(this.tabView_);},set tallMode(value){Polymer.dom(this).classList.toggle('tall-mode',value);},get tallMode(){return Polymer.dom(this).classList.contains('tall-mode');},get tabView(){return this.tabView_;},get brushingStateController(){return this.brushingStateController_;},set brushingStateController(brushingStateController){if(this.brushingStateController_){this.brushingStateController_.removeEventListener('change',this.onSelectionChanged_.bind(this));}
+this.brushingStateController_=brushingStateController;if(this.brushingStateController){this.brushingStateController_.addEventListener('change',this.onSelectionChanged_.bind(this));}
+this.onSelectionChanged_();},get selection(){return this.brushingStateController_.selection;},onSelectionChanged_(e){if(this.lastSelection_&&this.selection.equals(this.lastSelection_)){return;}
+this.lastSelection_=this.selection;this.tallMode=false;this.tabView_.label=getTabStripLabel(this.selection.length);const eventsByBaseTypeName=this.selection.getEventsOrganizedByBaseType(true);const ASV=tr.ui.analysis.AnalysisSubView;const eventsByTagName=ASV.getEventsOrganizedByTypeInfo(this.selection);const newSubViews=[];eventsByTagName.forEach(function(events,typeInfo){newSubViews.push(createSubView(typeInfo,events));});this.tabView_.resetSubViews(newSubViews);},onSelectedSubViewChanged_(){const selectedSubView=this.tabView_.selectedSubView;if(!selectedSubView){this.tallMode=false;this.maybeChangeRelatedEvents_(undefined);return;}
+this.tallMode=selectedSubView.requiresTallView;this.maybeChangeRelatedEvents_(selectedSubView.relatedEventsToHighlight);},maybeChangeRelatedEvents_(events){if(this.brushingStateController){this.brushingStateController.changeAnalysisViewRelatedEvents(events);}}});})();'use strict';tr.exportTo('tr.ui.b',function(){Polymer({is:'tr-ui-b-dropdown',properties:{label:{type:String,value:'',},},open(){if(this.isOpen)return;Polymer.dom(this.$.button).classList.add('open');const buttonRect=this.$.button.getBoundingClientRect();this.$.dialog.style.top=buttonRect.bottom-1+'px';this.$.dialog.style.left=buttonRect.left+'px';this.$.dialog.showModal();const dialogRect=this.$.dialog.getBoundingClientRect();if(dialogRect.right>window.innerWidth){this.$.dialog.style.left=Math.max(0,buttonRect.right-
+dialogRect.width)+'px';}},onDialogTap_(event){if(event.detail.sourceEvent.srcElement!==unwrap(this.$.dialog))return;const dialogRect=this.$.dialog.getBoundingClientRect();let inside=true;inside&=event.detail.x>=dialogRect.left;inside&=event.detail.x<dialogRect.right;inside&=event.detail.y>=dialogRect.top;inside&=event.detail.y<dialogRect.bottom;if(inside)return;event.preventDefault();this.close();},close(){if(!this.isOpen)return;this.$.dialog.close();Polymer.dom(this.$.button).classList.remove('open');this.$.button.focus();},get isOpen(){return this.$.button.classList.contains('open');}});return{};});'use strict';tr.exportTo('tr.ui.b',function(){const FaviconsByHue={blue:'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALgAAAC4CAYAAABQMybHAAAlrklEQVR4Ae2dCXwdVb3H5265yc3SpEk3ukEXCqVUBLT4Wm19oFKtaN0fKijy9CMguPBarIJsIiA8qsjTh7SllAoFeVBaEARkLV1ooXtL0yRdkqZp9u3uy/v/5uY/OZm75y659+acdnLOnP385zv/+58zZ2YMinTplIAhzsoDceaT2RKUQLwHIMFqh0V2ll0kn4XA6byv9/Vw834kX19e7keRQCzhRyk6bJJYRvD1YTXuhRdeqDj77LPPtNls400mU7HRaCzFFggEVJ/iSqhsicFgKIXUKL6bvB6fz9fj9/u7Kb4bPjaK67Xb7Q0HDhw49IUvfKEd2XUb7WpxHIYvXRgJ8AELkzRso1gmKrwkBfjG7373u5Zly5ZNKS8vn2G1Ws80m83YphPI0wnQUemQFp0IzQR9tdfrxXbI5XId6ujo+PCuu+6qXbNmjYfa9NMmngDoBmt+hIe944M53AUhwqwCvXTp0qJrr732opKSkk8XFhZ+imC+gIAryAZB0QnlJuB3OJ3Ot3p6el5/6KGHttxzzz0O6pse+GEP+3AGnKE2EhgG0tAFt99++4WkoT9tsVgW0DaH4guzAeg4+uD0eDxbaXuDNPzrt9xyy3bS8G4qB8BF6OOoKr+yDDfAB0B91VVXFf72t7+9lLT05QUFBZfQoYWtnA+ux+12v0ra/W+/+tWvXlq5cqWTBjUsYR8OgDPU8KGtjR9++OHHx4wZ8+2ioqKv0X4lbfnsWh0Ox9+bmprWzpgxYxsNFBpd1Op5bcbkM+AMtgr11q1bTz/zzDP/gy4Qv02zGtPzmehIY6MZmmq6UF176NChJ+bMmXOkD3QR9khFczY+HwEXwTbV1NTMI229FCYIXSTm43gTho8uUgMwYUir3zN16tR3qAIfbXkJej4dcIxF1dbkm44ePfqZqqqqpTT7MZf2pYsgAZqN2dTS0nLP5MmTX6EsDDrDHqFU7kTnA+Aa2BMmTDBv2bLliyNHjlxCZsgFuXMYhr6nZL7saGtru/eiiy7aUF9f76UeAfKcBz2XAUffVbgJbAuB/Y3KysoldONl5tDjkrs9oBtL+1tbWwH6UwS6/mZSzg0sVwHXTJG9e/deOGXKlOWksS/MOelncYdJo2+vra396axZs7ZTN0XTJYt7Hdq1XANc1dg0DNOqVatGLl68+DZa/3E1XTwCeOn6JLCly6ncU9+mNLnBZRLOYPAHHI5H2l5/8TdHbl3SRjUx6DkztZgrgKOfDLf5xIkT36moqLiLzJG0rAFJAomsKDp1W51S74IZnSIX8DcrXV3LlK/Oe5xqZPsckGc96LkAOPpowrZ79+5ZNK31BzkzQtKI4qxvV0dJTSLJ592kHKu7QfnPxXupFmhzbFkNeTb/tGsae/bs2Va6wr/lrLPO2izhTgLQZIuaaMp1yvTNyvNbb1HomFB1ZtrAUNYqymztGMNt2rhx44T58+evohs1n0r2+AyX8mnT4KIAvZ63lA82f1/55TX1FJ21tnk2As4zJObq6urP0BTgCmlri2TFDmcEcHQDtnlz4w+Uyz+Hm0Rsm2PuPGtcNpkomtZesGBBYXNz8210d+05CXfWsBLaEQNd5I+e8JyyYettCh0zyoBrpawyWbJFg2twv/jiixPnzZu3mhZFzQ2VqIyJRwIZ0+BiZzyeTcqebVcqS350nKKzxmTJBsDRB3WWZN++fXPpps060tpVouxkODEJDAng6GIg0KI0Hv+mcsXnN9FeVsyyDLWJwnCbadXfomnTpm2UcCcGc1blNhiqlNMmblT+9soi6hdmWKC4hlSJDiXgaBsCsNDKvysnTpz4JIWLaJMupyVgKFLGjHtSefrNK2kYFtpwjIeMs6FqWIOb7kr+Yty4cX+m2+0446XLBwkESHuPrPqz8uymX9BwhhTyoQBchZseQiigdcj30grAO+SDCPlAtW4MeLikdMQdyvqt9yp0rCl1SDR5pgFX4V64cGERvdhmRWlp6XU6scjdfJNAcfF1ysqNK5Q5C2F+ZhzyTF4AqHCPGjXKSjdwHqUHfr+ab8cyW8YzZLMo0QTgcj2jfO/S7ynNzS7KxtOI0UqkJC1TGlyFm3pccPDgwfsk3Ck5drlVidX6VWXFxvvAAG0Z0+SZAJzhtjQ2Ni6ld5D8KLeOjOxtyiRgK/6R8uy7S6m+jF14phtwmEBow3L8+PGr6FnJm1MmLFlRbkqgtOxm5am3rgITtIGNtJrJ6QQcHcdPkYUuKL9MsybLKSydlICijKxcrjz+0pdJFKzJ0wZ5ugBnuM27du2aT7ffV9JUIGCXTkqAJEAsjJ2wQlm1fj7tpPWOZzoAB9yo1/zSSy/NoLdJraMwFsdLJyUgSqBQGX/GOuX+FTMoEpCDmZRr8nQBbqIHgovnzp27mtaWlImjkmEpAU0CYGPmR1crF19cTHH4hU854KmuECcMOmo9derUAyNGjLiawtJlWAJZOQ8eTQb27keUyz7xM8qS8jnyVGpwNk0s+/fv/4qEO9oRlWkDJGArvVpZ89JXKC7lMyupApzhNm/YsGH6GWec8eCAAcgdKYFYEhhz2oPK3X+ZTtlSao+nEnDzxWRL0eNmj0q7O9bRlOkhEoA9ft6cR5WPq/Y4IE+J+ZyKSjS7m56jvK+srEzeqQw5epmNyDkbXBRPT8//Kl++6EaKSok9nqwG10yTHTt2fJpWB0q4xYMlw4lLoJhu5z/y3KepYEpMlWQBV7U3mSXFNN99H71YPfEByRJSAqIEwND4yfcpFyzgqcOkGE2mMGtvy2OPPXY9vZjnTLGfMiwlMGgJWCxnKktv/QmVT3pWZbCAM9zmxx9//IzRo0fj0STppARSJ4HykTcqN//3GVRhUqZKMoCrC6no6Zy7yTSxpW5ksiYpAZKA0WhTPj73dxRKakHWYABn7W3Zs2cPvjH5eXlApATSIoGi4i8oK56/tA9ysAr2EnKDARxlzJdddlkJ3dC5N6HWZGYpgUQlMH7SvbRWpYSKsamSUA2JAs7a2/ynP/3pOvrc9eSEWpOZpQQSlYDZPFn54a/xcDoDnpAWTxRw5DfRJ7DL6HUPP060rzK/lMCgJFA+8sfKZd/CqlRc9yXEbCKZWXtbli1b9gN6EX3loDorC0kJJCoBk6lS+ebVP6BiCU8bJgI48ppxU2fs2LHXJNpHmV9KICkJVFZdo3zsY7j5w6ZKXNXFCzhrb/PDDz/8HbK9x8ZVu8wkJZAqCZjNY5Wf3vkdqo4Bj8sWjxdw5DPRt3KKTjvtNNxhkk5KIPMSqBz1E2Xq7ITekBUP4Ky9LevWrfsGae9JmR+ZbFFKgCRgLpik3HL3NygUty0eD+Cq9h4/fnwBbTdIQUsJDKkERo+9QSkr47ubMfmNlQHaG5v56aef/ndaUDVtSAcnG5cSMFumKXc/fDGYpI35jCiXeADH3KOZ7lp+Sy6HjShHmZApCWA57dgJ3wKTtIFNQB7RxQIc6abLL7+cniEesTBiLTJBSiCTEiguWah8/isjqEkAHpXhaIk4M5BuXrp06ZfoOUtcvUonJTD0EjCaipSvff9L1JGYU4bRAEeaCjh9P+fr0jwZ+uMqe9AnAZgpo0Z/nfYY8IgcR0qA9sZmeuCBBybZbLZ/66taelIC2SEBKzF5zTJMWbMdDl5DXDTAVe29aNGib5D2jpQvpEIZISWQEQkYicm5C0QtnjDg6uwJPY72tYx0WDYiJZCoBMorGXDW4iE1hNPMOBMQb1qzZs0MmvueHlJKRmS1BCZYYZoOA2exTFd+dT/eTsuzKSFaPJwkNMDPO++8+fLiMvdA+Z8JJcqPN+9RGnocoZ0PBELjFF2cbjdYIEykvq4wWehd4APb05dBari4gaWCe/p8AT+uFOdT4j7aoJTB7oAGowFurqqqmicBV5QPmgLKX3b7lVbHANmRLLPVVSjnGT6hzFRa44dHHEqIHhQThXC8+YQiqQ66K9rnvakoD1O9DPiAJvSAo8vYjMXFxWZ6U9VFA3IP052fv+5VGntzBW4+SCYl4KtQ/L3tpCBJ0+WpC/hKLgKrvb29DDj41Q4WIvUOcaZHH310lslkGqlPHI77uQd38CgZTBbSVBVKXk+CGYwjS758/ywwS1sIz/oI1uCmmTNnflKaJ7l/OmuQG3migQ9xnvg0W2gaN/2TfYDzoLQDFw5wVYOT/T1XAq7JKacDKuS2csVg1B/unB6W2nkwaiiumEs7rMEBueZEG5zpN9Gt+QKyv+douWQg5yXAkPvtHYO78MxiCZisJXNsVRML7C3HndRN5li1w/WnNPaNDz744Ll0ZpRm8Zhk1wYhAYacjPJBlM7eIgHFUFryxZvPpR6q/Io9DavBJ0yYcJY0T0Qx5U84CDnNrtjb82dQZHqZysefRQPaRltEDc4JRlr7PS1/Ri9HopeAwWRWjLYKQiF/NLnBWgpmocGZY3XYoomCBOybaPXgNKnBVfnk7R8V8qLyvIAcrBoLiqaCXdoYcvXYMeB8KmPfSIBPUVPln7yWQD/kjEEOD7fABsBVfvtGoTIdYoOPHDnSXFhYODmHhyq7noAEgpCPUPyOTiql3QBMoIbsyGo0F04uInYdbW3RTZRbb711AnXZmh3dlr3IhAQYcpooz0RzaWmDTk1r0YLrwS4GwRaJuoMGmXrjOeecI5fHQiLDzKmQF9ILXFXIGYfc8q2jZ4JdBlyFnE9ZHolx1KhR8gJzmMHNw9Ugz8U7nrijWToyZCZFtMEBu7GoqGgiD1j6w08CKuTWUsXv6s65O56GApVdlWM+cnoNbqB3D+JzEdINYwkw5DlnkxvNYJetEdVEETU4Ioy0RLZEzoEPY7r7hh6EvIQ0eQ/FZP/sCpilPgNwKG0VbgyFdzTqCXC8ZFw6KQEAoxgLS3NoPbkR7GosIyxqcBxSgwQcYpCOJWDAOnIrKUbS5AH9M5GcKUt8OiEZcK1HbIMjQiVfAq7JRgb6JADIDQR5tpuuAaMGuGaisAbXIiTgkutwEujX5L2UnJ02uSEIOHdfZVpqcBaH9GNKIKjJQ6yAmOUylYHsa+6cprBZg3MfpA3OkpB+WAkENXmxEnDbs2+e3KABrvU9RINTih56LbMMSAlAAqomL7BRQFOU2SGYgMouOqV1jGHWIrxer50+8iofV8uOQ5a1vVA1OUEecOPtWdlhkxsUH/2saE5lmufBtVifz4erCOmkBGJKIKjJ8V0ETT/GLJPODAG/X8+uOg+O0087BaHB09kJWXd+SSCoyYuUgIceaB/qeXL/AA2uci3a4JB8QGrw/AIwE6NRNbmlcMht8oBftT40ZY2xsw2OsJogAYcopEtUAqomt5Am9w6dJg8ENPNagzysBs/2W7KJCl/mz4wE8OYsg3loNLnKbNAG1+DGqFmDI1LdpA2eGRjytRX19XAEecDr6kMqcyM1BNTrR41ltCxqcAYc6yOlkxIYtASCmhyP9WZ2doVmUXhtL1hWHWtw3lccDkcb1H22L6zROiwDWSmBoCa39mnyDHSRmPV7nG36lliDs1r3t7e31+kzyX0pgcFIQNPkGbrj6be3gV287Z95Vk0U7MCpkdXV1bXyIjMoEPk3eQmokJsKglOIAD1tm6J4Wo7UMsd9PQ+wBse+CvgzzzwjAe+TjvRSIwGGnB4qS02F4WohE8W58zk94CGzKP6XX3652+VyNdN6lFHh6pFxUgKDkQAgDygWxeDzDKZ47DJeV3PvvtfpVQChJgoKs80C+8Xf09NzRJopEIt0qZQAIFfou0GpXoUIVv0uxxHqq8ov+cxzyDShmsFut9elcmCyLikBloAKuZEm71Jsi/vdKrMi4GqTbIMz8cjgw0yK1OB8SKSfagkMgDwVlZMGDzg6oJR9tIFh5lmzwdEMR/pPnjxZiwjppATSJQHVJg/QRaffm3wT9Gvg624GswPgRsXhNLh//fr1u2nRFYCXTkogbRJQbybCXEl2diXgCzh2bthNFQHwAZAz4BgEgEaijz4C29zZ2VkjzRSIRbp0SiAIOT7MgCnExDeyThS/s7uma+vaZqpANFHUbusBZ8i9ra2tWyXgqozknzRLQIMcF56JOiLc19O6lYrB1hmgvVGVCDj2VQ1Ovq+mpmaLBBwikS4TElAhx7vJE55dIWhb6rZQH6G9WYNrXRYBh/ZmDe5buXLlVj85LacMSAmkWQIa5Im0Q4x2bXkUGpzhZo7VWsIBrp4JGzZsaCc7/KDU4olIW+ZNVgL9kMe2x4P2d+dB+86X8NFP1uARAUffWIPDnvHSdOE2CTjEIl0mJRCEPA57nAj3dzXj468qr+SzDa51V9TgiGTAcTZ4yQ7fLAHXZCUDGZSABnlUm5wgba3dDFZpE00Uraf6Bx5YveNM8C5fvnzbJZdc4iwuLqYH7Yavq+ytURq70rRIKIvEGlmZAYswDjZCRBchLUJ0ULeGqYzaQL8AfEj/PA5nz8u/Zw3O2ntAC+EAR0bVnnn33Xe7Gxsb35gyZcqlxhR9mGj/oU7liWfrlPZOd5jRZGfUbK9bmUnPGIYIeEB3B8i1PyUKBHTo+vPFEYrcfpR6orYfR6NZmiUQ8Cs9XU1vbDiyEysI2f5myLVe6wFHAqSlanDyPTt37nz+9NNPTxngv/3DHqW5lV4tkGPO67ErPi+9pgw/mYAGfjyO8zJo+vL6dH2dmc6vb1/fP31/9Pn1+7HK69P15fXt9eUP+LxKR/OB5yk7flrFOfABNehtcCSKgHuvu+66t2n5bGtk7TGgvpg7uQg3BmW22BSTGa8pIwehx+s4L3wxzOXFOM4j+sjHecSwmEcMi3nEsJhHDIt5ENY75IXjMhxWI+P4E6u8Pp3bYV/fHsWDRb/f1Vq3b9XblBzxAhNFowEOte+hlYWO+vr6f6QKcDSaq06F3FQYdeUEow9fDGfLmMU+ieFI/RPziOFU5Y9UT/T4gOJ2tP/D7e7Bmz+hwcNeYKKOcIAjHiaKZqa8+uqr6+l9KYgf9g6QG/sgxwHXbxAQgyCG9fmGal/skxiO1B8xjxhOVf5I9USLV8j+7mjd/Rz1RzRPwGuIiwQ4zBScFaDas3Tp0r0dHR2HpRYPyo8hD+7Jv5mUABj0eeyHjx58Yh+1y4CDVTAb4qIBzpCjEjfNiW+Qd+775dcPeTRdI9NCf+OSlQl98M3RvAFM0sbmCVhNCHAcSah8TYuvXr16PT2MjAql65OAapPjXXzRnP4iCnk5Llw5ToMvhsPlzYU4cQxiOFLfxTxiuC+/3+/xNB9/cz3tito7rHmCIpE0ONJwRrAd7l61alXjkSNHXpBaHKLpd5hZMfELJ3FA9Buy8oESw/p8vC/mEcOcnmu+OAYxHGkcYh4xTPlx38DtaHnhZP3rjZQEDR5xehBF4eIFXDVT1q5d+whp8YhnS7DK4fdXhdyEd/FJl04J+ANef3PDpkeoDTZPkgIcfR2gxe+7776aY8eO/VNq8dDD2A95sjamLE8/eSTggRsuLj2Otn821D5fQ4lxaW8cpWgaHOnQ1pqZQmHXU0899VePxxPWoEeB4ewYchwadhzmw4V4jhPDnJ6oL9YhhuOtRywjhuMtr88n1iGGOZ8YJ4Y5PZKv+H2BthOb/0pl8F5mEfCoFkUswNEHVICLTdVMufPOOw+QFn9TanGIJtTBHjeSucIHCjkQZsfhSOmcL14/2fqSLa/vZ6z6YqXr68M+1p24nK1vHq3++wHaZfMETEaFG2XjARzaWgOcwq4XX3zxYdLiKC9dGAkw5Pqf2czso0OMkRhGXG5u9N5vpb3p/YdpAKy9AR+YjGlJxAs4a3GcPa4lS5bsOnHixGapxUkaEVwQcnqrasYdw80wowMcl/HOJN0gtLfb1bH5yMHHd1FlDDhr75QAjk6yFsdVKyB3bty48UE5owLRRHYa5JgSY8dhniZDPMeJYU5P1BfrEMOR6hHzIBzLcV8j1aePR31cRgxzPjFODPel+xWvv6N5x4OUhCWoYA8MxqW9KV9cJgryAXBocQbcdeONN+6kd4k/J9eoQDyRnQq5se+Fk3yg2UcxDvcdULUmjotcbeQULhtvffr8XC6Sj5a5TORe9KdwXq5PXz5KOn0WUHH2nnyudt/qnVSMtXfMqcH+xuMHHGVYi6sXm2jwpptuWk4PJrfLNSqiSEPDGuShSTImggTUNSdee/uxA2uXUxaGO27bm6uNxwbnvKzF8fOABp2vvfZa89atW/8oLzhZRJF9zVyJnEWmCBKgb14qPZ01f2xv3o03VsE8AXNx295cVSKAo4yoxVXIFy9e/Aw91rZLXnCySCP7Jpo+NNLnPMQvHXAYfjz/UDuXEcNcVowTw5yeal9sQwxHakfMI4bF/HhiyuPq2LV/293PUB6GO2HtjfoHA7g4o4LGnWvWrLnL6XT6pKkCkUZ3gNxAL4HnA4rcCMfrOG+k8rHS420n3nyJthcrPxgK+D2+5oa37qI+qHyRj4vLhLU3xpAo4CjDgOOMUrX4HXfcse/AgQPr6I20SJcuhgQYcvVijS++pN938RpQHD0n1h378Cms99Zrb7CXkBsM4GiAIVenDGnfccMNNzzU0tLSKE2V+OSvmiuYXZFOkwDmvD2e7saa/X99iCLxOBoAF7W3ljfewGABZ1ucpw2d7733XusTTzxxE33+xCNNlfjED3vcqELON2WGr0+WCS03cXtaTmy6qbutppUkyHAnNO+tl/xgAUc9DLmmxWnacAeB/hDdANK3I/cjSCAIebi3d0QokKfRZHcrvZ01D9XtW72DhqjX3mBtUC5ZwGGqaFqcws5LL7109dGjR9+WN4DiPx7DHXLc0HE5Wt7es/m21WCob4PiTOimTjiJJwM46gPg2PiCE2ee/Wc/+9lvyB5vkvY4SSNO12+uxFkgT7LB7vZ6uptq9678DQ3JThsYggkAppgvCg7OJQs4WkUnMH2CMw6dc9ANoJNPPvnkL8ke90p7nCQSpzPS9CFscryHbzhsEEvA7/a2NLzzy46WXSdpV+WH/KQuLFEvu1QAzrY4mypqJ+lVE9u3bdv2Z9jjEnIWd2wfkBsM+W+T9813093K6j/X7l+9nSQjwp3UhaUo5VQAjvoY8gGmysKFC1fSgqxX3G6ckNLFKwEVcu3rY/k5swK729Hb9Mqed29fSXLRmyawCAZ9YSnKOVWAo06GHDTjQgGdti9atOjXdNH5noScpJGAU00VI74+ln+OXv2gOJ0t7x3cduevaXQqJ+TztGDK4IbkUg24aI+rkNNXIrquuOKKG+kBiYNyURZEHr/LR8j99OFXt6v94KH377/R4WjtImkAcBFuMJQS7Q1Jp0NFoHNiBw0Eube2tnbT/PnzFzz3UtMIA76mJV1cEjAYcIhInLgTkuMOZonH3XW8dvdff9zZur+JhtNLGwMO8zal2hviSgfgqBduAOhki7u6u7u3NHWO+yxNidkk5EEhxfM3CHmfSHN0zQq98Fjxunta6w+v+9GphneO0Wj0cKdUc7Nc0wW4qG608AcffNBrMlvfLx0x5XMGo7lAQs6HIbbfLytNnLELZUkOrO2mF2b2nDz64rX1hzccpG7p4YbmBuApd+kCHB3lI8G+2vnOlr0dBYVV+4tKxl1MswWW/gOX8rHlXYUsq+C8ChaeZv8/vOqYvo5hb2l48+d1+9fiNrwId8rmuyMd7HQCLrYJyDXQ20/tOGUxF+6wlU1aYDQWFPGBEwvIcHgJ9MtKE2f4jFkQq9rcnu72xrp//OTIgccx181wY8477XBDBJkGXAO9o2VPm+JzbioZMXWewVRQ2n/g0C3poklgoKyyc57cTxeUXnfHCVrXfU1D7fr9NJ4e2gA4w530OpNoMuK0TAGO9ljlaJB3tVd3u1yNb5ZVzPy40Wyt7L+Y4u5JP5IE+iFnsUbKmfl4zHN7nG3VdXtWXNvU8GYd9QBgZxxujDyTgKM9OAZc9e1dDY6ejoOvl1fNnm0yFY1TaApR/QhoMK/8G0UCGuQGEmUWKHK83jhA89z0gvoPDu1cfn1b864T1H29WZIRzc1iyzTgA+CmTqj7Lkeru6156xsVoy+cQk+fn44DJyHnQxTd1yBXRRk9bzpTsSrQ7/MoLvvJN/a/d9uSno5jLdQew40bOVghmFG4Md5MA4424UJA97rtvub6f71VPupcq9lSNttgNBLj8oZQUFzR/w6UU+ZVOeD2eV2B3u7ax/a9e/PvXI7OTuqxCDcuKDMON6Q2VICjbYacJ/jpHYte/8mjr35gtVUdLCwaPYfmyunDlFKbQ1ixXBByiDRzTl0RGPBiPXd7S8Pbyw68d+/TdAz5YlK8QzkkcEMSQwk42mfI4Wugt53c3uB0nHyttHz6THo4dywOnjRZIK7ojiHPxOw4lg4EYJI4mnfW7V95ff3h9bupd9DarLlhkohTgZk9+/pElS2AA27eVOjt3fW9p4699kr5qFkmc0HZR6TJ0nfEYngDzZUYmQeZrN6ZhEnSeXj1nk2/vr2nsw5vn4LGZrj1i6cG2VLyxYYacIyAz2zW4hro9HPnO3nstZ2FhZX7Cm1j5tCDAEWkyqU2j3HctV+7FJvjWE+CWRKvt6utpeGtX+7f/vv/6zNJGG7McfPFZNpuv8cY/oDkbAAcHRIhF0FXw21N2084HfWv2UonjaHPhEwJaikJ+oAjqdvRINfFD2ZXfSILF5I+Fz2kUP/akT0rlhyv2bCX6mKNDcD1N3CgqIbc4RzPJof+YOoEJx7eioNPl+FDlHSxqdgQnj77h5+oGPeJXxQUlE3Cg7qZ+EmmdnPWYYYjGRec/nMrbnfnsbaT2+6v2f3wZqoPJghDzVOAvNwVDbLCSqbplJTNNsAxKP5hBeR4OBGfSQDkDHpRYWFFyYzzf/Gd4oqpV5JGt+IZxlRqLGorr1zwmdjEmOMZEp/X4erpqFld/f4Djzud7ZghgabGBrDZ1sYsCa/lTqwhKphOly0min6MLCT42KAV2Kbzeb1Ob9Pxf+32utteLSqZOJ4++jRJmi16Efbv95/8rDsi++pzFX3mCM1kvXPkw7X/Vbd31eskc3H6D9pbhBvHJ7mfiv7upjSUjRpcHCD6xyYLa3PW6DBbVM0+4/yffKq88iPXmq0jJuOdf/J2vyjC/nBQk/fviyHRzva6u462N+96qHrng29RHtbUrLUx9cc3bljpsEISq8yKcLYDzkIC5Aw6bHNAzva5CrnZbC6c/pHrLykbefYVZmv5NAk6iy66PwBsV8fhrrYDj1Xv+uOr9GYyBpt9ntcWbe2s1NriiHMFcPSZtTlAhzZn0AE4ww7fOuP86z45ovLcKyzWkecEL0RN0kYnwYguaGP78MJLetl8277O1j2Pffj+n96mPAAZG8BmHxobYPMdSYCdtVqb+qa5XAKcO40+49qBQYc2Z42uAk77qj919tUfqxh1wZXWosrz6cEKslxQbPhOLwZNFKz4I7D9broL2fp+e/OO1TW7H3mPBMNgi75ojgBqvpCkYG64XAQckkW/sYlmCzQ6Ty2KoBeccc53Z5eP/uiXrIWjFpjNRTaD+no0FM1/2DWo6cIRb3D1eh12l7P5jY5TH6yv27cGt9cBsQg1wtDWvIl2dk5obeq75nIVcB4AQ86gs+nCoLNmV7V8YcnY4ikzvr3ANuKMz1mLqi4k0E3q+7nVu6OoIn+cOv9NUyJ4+ACfBKG3t263d9a9XPvh2jecPSdxg4a1M4BmyBlqnvaD1s4ZcyTc0ct1wHlMetBhi7CNziYM+6qmrzrtwtHjJi/6rK1k/OfoiblpAJ1hz0XNzpoai6AANTafu/uwvafh5cajG//ZcmL7KZIJA8xwiz7SoK1ZY+c02DQO1eUL4OJ4grZH0E6HRmetDsAZetE3T5q6eHr5mPPmWQurzjcXls8i0K20VFcx4iWYeA9JFpoyA4CmJatYI0JQu7zOjr0uZ8v7HU073zlW82w1dR7aGPAC5nA+0llj8z2HnDNFaAxhXb4BzoMMUtlvo0Ojs1bXA69qdEqHby4sLLeOm7p4Vln5tAsshRXnFxSMOJseirbgAhXPjAZvmrDYgn7/jRRuPjV+EGLUxbzRBSKWqdJ7RnChGKBPftAt9AMeZ/v7XR2HdzTWPLvX6eyAycFQA2jeGHBOY23NGhuNcEMUzA/HRyo/RhM6ChF0aHbRVhe1O0POceybiovH28ZNW/SR4pJJ55oLiieZzLZJJottPFY2BoHHWnWAT1Wr0owkUn18JJYoHv9xUQiQNd/roJfnNPi89mNed++x3p5jexoPb9zV29uAu4qAlDUx+ww2fI6Dz0CL9nWkzlD23HZ6qef2aKL3HmNl84VBZ83OQEfyOR98lDWOnjB3dFnFOZOttjGTLIWlk81m20RaMlBpUEw2Ay2QoRPARg1SffQXF7F9vtpFaOEgxbSrhuhDAV57gBZ+BBSf3e9ztXq99uMeZ/dRl73pWFf7vqOn6jfBhmYoRe0rwhsuLOZlu5p9tTv5/Gc4Ac7HEWMWN4ZW9AE6Q83Q8z6fHKKvQq+r10DmjrmoZEKx1Ta6yGItK7aYy7AiUvF4u+weV1evy37K4eip7yWzAmBCi4obwwyfta7oI8xAM8TYF/NwWbHevNXWNPYQNxwBF4Uggo4wg8q+CL0IuAg350Ec18H1oi0xjH3RMXiI4zBrVwZcDyxDy1DzPudnn+tjX2x32IQhfOmCEmBZMJDwGXQxLMYBbqSxz5AjDg4+b7wPH9DBMXz6fUCKOEAs+gwv+0gTw9jHBsd+cG+Y/uUDMUyHH3XYLBsGNJIvQq3PgwbEesQGGUDRR1i/Mez6eHEf9WJfOp0EWPi6aLkbQQIsLwYZ2aLFiekRqhwAJkPK8KJMtLhIdcr4PgnwwZECSU4Cejnq91G7Po7BFVvWx+n3xbwyHIcE/h9VLWRYHWXC/QAAAABJRU5ErkJggg==',green:'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALgAAAC4CAYAAABQMybHAAAltklEQVR4Ae2dCXQcxZnHR3NoNDp8SD7kU7bxFXCchBhMYoLNmhCcOBBykGw2gYTkPV6AhGXD2sTZJQcJG3jsgw3hscuCsTEsOAQW1sbY+MAHxpYtHzI+5EOy5UMStnWPZkZzab9/j75WTWt6NKO5Z6r82lVdXV1d9e/ffPq6uro7zyBDIhXIi7DyngjLyWJRKhDpCYiy2pwoztrpxSwCb+d1bayFm9f1Yu3+cj2MAgOJH2bXnNnEGiHWppW8d999d/inPvWp6YWFheNMJlOR0WgswdLT06PElFdM+xbn5eWVQDXK76TI7vP57H6/v5PyOxFjobwuh8Nx4dixYye+9rWvtaK4ZqFVNY/TiGUIoQCfsBCbcjaLNVHgJRUQG3/4wx9ali1bNmXYsGEzrFbrdLPZjGUagTyNAB2ZCLXoh3CJoD/p9XqxnOju7j7R1tZ2/LHHHqtbtWqVh47pp0X8AaAZbPmRzvnAJzPXhRBhVoBeunSp7b777ruuuLj4xoKCghsI5s8TcPnpIBT9oNwE/D6Xy7Xdbrd/8Oyzz+5+/PHHndQ2LfA5D3suA85QGwmMPLLQ+b///e/nkIW+0WKxLKBlLuUXpAPQEbTB5fF4KmnZShb+g0ceeaSKLLyb9gPgIvQRVJVdRXIN8CCo77777oI//vGPt5CV/n5+fv5NdGrhK2dDsLvd7k1k3f/n17/+9frly5e7qFM5CXsuAM5QI4a1Nh4/fvza0aNH/4PNZvs2rZfRks2h2el0/u2TTz55dcaMGXuoo7DoolXPajcmmwFnsBWoKysrJ02fPv3v6QLxH2hUY1o2E63XNxqhOUkXqq+eOHHitblz557pBV2EXW/XjM3PRsBFsE21tbXXk7VeCheELhKzsb9Rw0cXqT1wYciqP37FFVd8SBX4aMlK0LPphKMvirWm2FRfX//lESNGLKXRj3m0LoOOAjQas/Py5cuPV1RUbKQiDDrDrrNX5mRnA+Aq2OPHjzfv3r3766WlpUvIDfl85pyG1LeU3Jd9LS0tT1x33XVrzp8/76UWAfKMBz2TAUfbFbgJbAuBfUdZWdkSuvFyZepxydwW0I2lo83NzQD9rwS69mZSxnUsUwFXXZHDhw/PmTJlytNksedknPpp3GCy6FV1dXX/OGvWrCpqpui6pHGr+zct0wBXLDZ1w/TSSy+V3n777b+j+R8/pYtHAC9DrwIO9xHD5c5XDF5fS0ya0MWo3+nwvrBx47nfLLlvKypj0DNmaDFTAEc7GW5zQ0PDD4YPH/4YuSMJmQMSExVpsPPxhjsMHt/FuLWkp8dwqb3dt2zhnD2vUKXsnwPytAc9EwBHG01YDh06NIuGtf5DjoyQGmHC4XMLwmwd/Caft2fnmXr3A3d8Zf9hqgXWHEtaQ57Of9pViz179mwrXeE/MnPmzF0S7sEDGuueJnPevCuusO76sPq6R2bPHm2l+sy0gKG0NZTp2jCG27R27drx8+fPf4lu1NwQ6wnKlf0TZcFF/bwe//Z9uxw/vvfuj89Tftr65ukIOI+QmE+ePPllGgJ8UfraIloDp5MBOFoB37zxQvdPvr5gP24SsW+OsfO0CenkoqhWe8GCBQWXLl36Hd1de1vCnTas9GtIXp5h5LgJ1re3H7z2dwsWjMTUYlwrpZXLki4WXIV73bp1E66//vqVNClK3mLvh1RkGcmy4GJr3B7/zkOVXXfd86PD5yg/bVyWdAAcbVBGSY4cOTKPbtqsJqs9QhRPpqNTIBWAo4U0l+1yw1nXd29duH8nrabFKEuqXRSG20yz/hZPnTp1rYQ7OpjTqTRNUhwxtsK69t3tcxZTuzDCAsOVUiOaSsBxbAhgoZl/d02YMOF1SttokSGDFSCabeVj819/v3LOXdQNCy04xynjLFUHVuGmu5K/HDNmzHN0ux2/eBmyQoEe84gRluc2V13zS+pOSiFPBeAK3PQQQj7NQ36CZgA+Kh9EyAqqgzpBQ4h5w4aZH6URlidwrmljSix5sgFX4F60aJGNXmzzYklJyf1BqsiVrFOgqNh0/5ubJr24aFEp3M+kQ57MCwAF7pEjR1rpBs4KeuD3W1l3NtOkQ6kaRQnXfZfL/+Y3bqz7Ed3f6KZyPIwYbpe4bEuWBVfgphbn19TUPCnhjsu5y6hKCgqM33pr4+QnwQAtSbPkyQCc4bY0NjYupXeQ3JNRZ0Y2Nm4KFBab7tlSdc1SqjBpF56JBhwuEI5hOXfu3N30rOS/xk0tWVFGKjB0mPlfN1bOuRtM0AI2EuomJxJwNBx/iix0QfkNupJ+mtIySAUMpSPyn16z5fPfICnYkicM8kQBznCbq6ur59Pt9+U0FAjYZZAK4J6+aczE/BffWn/1fJIjoXc8EwE44Ea95vXr18+gt0mtpjQmx8sgFVAVIEgKJkzJX/2fq66aQZmAHMzE3ZInCnATPRBcNG/evJU0t2SI2iuZkAoIChiNeUM+O6d45cLbxxVRNv7Cxx3weFeIHwwaar148eJTQ4cO/SmlZUiyAuk4Dh5Ogs5O3wsLPrfnQSoT9zHyeFpw/FhQn+Xo0aPflHCHO6Vym6hASYnpp29v+dw3wQ4tYChuhjdegDPc5jVr1kybPHnyM2IHZFoqMJAC48Zbn/nzi1dNo3Jx9cfjCbh54cKFRfS42Qrpdw90OuV2rQLwx6/9QvGKhQsVfxyQx8WKx6MS/EgUv5vmGTw5ZMgQeadSe/aSvJ5pPrgoj73D91/zr97zEOXFxR+P1YKrrsm+fftupNmBEm7xbMl01AoUlRjvWb1u9o20Y1xclVgBV6w3uSVFNN79JL3LLuoOyR2kAqICYKhisu3JBQvG8tBhTIzGsjNbb8vLL7/8C3oxz3SxoTItFRisAhaLcfqyP435Oe0f86jKYAFnuM2vvPLK5FGjRuHRJBmkAnFToLTM8tCfnpk5mSqMyVWJBXBcWFro6Zw/0Z+Vwrj1TFYkFSAFwNQX5w/5N0rGNCFrMICz9bZ8/PHH+MbkV+UZkQokQoGiQtPX/rb+M7f0Qg5WwV5UYTCAYx/zrbfeWkw3dJ6I6miysFQgSgXGV9ieWHjrqGLajV2VqGqIFnC23ua//OUv99PnriuiOposLBWIUgGLJa9iya8q8HA6Ax6VFY8WcJQ30Sewh9DrHn4WZVtlcanAoBQYXmr62fe+NwGzUnHdFxWz0RRm621ZtmzZT+hF9GWDaq3cSSoQpQImU17ZnfeO+gntFvWwYTSAo6wZN3XKy8vvjbKNsrhUICYFykZa7r1mwUjc/GFXJaL6IgWcrbf5+eef/wH53uUR1S4LSQXipIDZklf+m99N/AFVx4BH5ItHCjjKmehbObaxY8fiDpMMUoGkK0BW/OezZxdH9YasSABn621ZvXr1HWS9Jya9Z/KAUgFSID8/b+KjT02/g5IR++KRAK5Y73HjxuXT8oBUWiqQSgVGlVseoCnZfHdzQH4HKgDrjcX8xhtv/B1NqJqays7JY0sFLPl5U59bVbEQTNLCfOoKEwngGHs0013L78npsLo6yg1JUgAMjhlb8D0wSQvYBOS6YSDAsd30/e9/n54hHrpItxa5QSqQRAWKh5gWffWbY4bSIQF4WIbDbcQvA9vNS5cuvY2es8TVqwxSgZQrYDQabHffU34bNWTAIcNwgGObAjh9P+c70j1J+XmVDehVACyOLs//Dq0y4Loc621g59301FNPTSwsLPyiVFcqkE4K2ArzvvjPv52GIWv2w0P64uEAV6z34sWL76BfjF65dOqzbEsOKQAm5/9diWjFowZcGT2hx9G+nUO6ya5mkAL0WBsDzla8X+tDWWa+uDStWrVqBo19T+u3l8xIawUsplFp3b54NY7mik/703/MxNtpeTSlnxWHk64NKuCf/exn58uLS6086b8+3Pqg4WDNHw0O5yf9Gkuf9+sX6N3twXmaVWwMkUWv+Q7eLVShHk1mv310Kg9Vrt/h/PStQoN/PlVxhBYYa7AbVCwc4PQxzxHXS8ANhkZ7jaGq8W8Gh6ed9MuM4C2ebrD7Jhp6CIJsDr481/UGw4nnqY8MeFB3tYDjF6BY8KKiIjO9qeq6oNI5urL+1L8bOt2XM673PrPf4OjwZDXk/p6e68BqV1cXAx5kxUP54MgzrVixYpbJZCrNuLOagAZnItyQwWQ2GgppXlKeEec8OwON75V+/YErZlHv2A8P6qieBTddeeWVX5LuSZBWGbnCkDs7PQa/PyO7EL7RZI5HTCj+EhXaTwt7IKpfprXgintCBU3kf8+TgIfXNlO2AnJbicVAt7izLoBR2xDLPOoYW/CgP1eiBWf6TXRrPp/877lZp0YOd4ghhyUPNUKRydJYbaa5IyYU5l8+53BRP5hjxYprf9NYNz7zzDOfpl9GSSZ3Wra9vwIMORm9rArUn5Kbfzzt09QphV+xcyEt+Pjx42dK90SUKXvSDDksedYEwnrYyIKZ1J89tOhacN5gpLnfU7Om87Ij/RRgyLPJiFlsZjALC84cK/1GBgdswLqJZg9OzabOcwdl3KcAIC8oNuMtrn2ZGZpCHyxW0xXU/H4Xmgw49xLrRgJ8Sob2VTY7CgVUyLNgnLwXcIXfXgkUpvv54KWlpeaCgoKKKHSSRTNYAQXyIrPB1eXVzOLIrE5ZrcaK0lKbuaXFCbDZYCsuCfcEmcbf/va34ym2cqaMs18BhjyTZ/3TmKB17ncngV1Y8X6AM/XGq65SPsaZ/WdV9jBIAUBuLSSfnPFgIjIoHj2pCFO7xR6oFpy7YRw5cqS8wAw69bmz0gc5cMiwQE0uKrH0G0kRfXDFQbfZbBMyrGuyuXFUQIGc3p/Q7fSRT65O6YjjERJXVX6hCewqHPNRsILAFjyP3j2Iz0XIkMMKBCA3ZdwQosloBLsqyziFogXHBiNNkS3OhrFRdE6GwSsAyPPJkrvJkmeCHVeYNeUBcPbBlc7ziko9AY6XjMsgFVDmkysXnqAjAwIN54NdlWWkRQuOLuRJwCGDDKyA0ZRnsNrM5JOn/zi5yZzHgHPz1VEUZCjkS8BVbWSiVwGGXCEkjVUxGlXA1b85bMHVDAl4Gp/BFDaNIXe7vGk7uEL+iOheK0zzKAqkkxY8hQBlwqEBeX4BJmilZ2uNRuX6UeGYW8gWnNelD85KyDikAgy5uzv9xslNRvUiU217PwtOW7TQq4VlQioABRTIrTQzNc1MeU9eD9gNacHVPzper9dBH3mVj6tJlsMqwJB7yJKnyzg5vTXAITRaYZrHwdV8n8/Xpa7IhFQgjAKAnOZhp83gSo/foGVXGQfHD1D9EcKCh+mT3CQVCFKAIfe6yZKrFAUVSdqK39cjsqtwLfrgaEiPtOBJOx9ZcyBAbs7H3JUUd8mnWPCgn5l4QalskICn+CRl6OEVyMld8brp9VkpMuU9fj+7KCrkIS14v9fpZqjostnJVYDuJJIlJ6RSYMrBrK9HAVyFG71nC45MZZE+eHKhyLajMeQ+jz/phtzvy4MPrrIMbUULzoDbs0102Z/kKgDITRZj0g253+8Huwy40mm24KoCTqezBeZezglXJZGJQSgAyA0EOSx5MgLcfp+7p0V7LLbgTL2/tbX1tLaQXJcKDEYBtuSD2Xcw+zg6u8EuflHMs+KiYAVByTx58mSdvMgMCCL/j12BpEFO9Laed9Yxx70t72ELjnUF8DfffFMC3quOjOKjAEOeyMEVfOyqevtFLeD9RlH8GzZs6Ozu7r5E81FGxqd7shapAI1mwCen5zz93sT45H5Pz6UTey52ktb9XBTor1jv3o1+u91+RropkEWGeCoAyI0EebyHV8Bqt8t7htoKuEMCjn4AcqWAw+E4jQwZpALxVkCBnG7tK5DDZ4nT4nb5wawIuNJ09sFFC+7DSIq04PE+tbI+VoAhj5dPjiHCbrsXgNNTGMEWXBwHVyFvamqq48bIWCqQCAUAeQ8ZcJoBGHP1+KF0NHvALCw4c6zUG8qC+995551DNOkq9iPH3HRZQTYrgJuJmKQVa6CvOffUfNhwiOoRXRSFXwYcx0AGCvjoI7CX2tvba6WbAllkSKQCsUKuXGB2eWsr37twidopuihKs7WAM+Te5ubmSgl4Ik+trJsVYMgHMz0E/ndXm6eS6qI3E+m7KHwsxYLTiq+2tna3BJxlkXGiFQDceDe5EiMd6UIPzLU0OneD2d4FDKtBz4L7li9fXkmzs4IKq3vJhFQgAQow5NFUTYT696w5DwsuuieK/416QgGu/BLWrFnTSn54jbTi0cgty8aqgAo5rj0HWHB7vtvhqTnyUVMrlWYLDrhDAo62YQOsNvwZLw0X7pGAkxIyJFWBgHsy8CHhf9tb3Pj4q8IrxWBXhRs1iBYc6ww4fg1e8sN3ScAhiwzJVoAhJ1dc/2YnNaq5oWsXRQBcdFHU5oo3epAJwBly79NPP73npptuchUVFRWoe+RgwnXRZmh3YBQqu4OuMQuyiX0a6GQHCuhs1D1GX7VBKVhp7APgtfvSS4dcm1bUsQVn6x105FCAo6Diz3z00UedjY2NW6dMmXKL0ag19kHtiHil9nyj4b2dVYaOLvEVFhHvnpKCXs9XDUa3m44dpF1QW7TiB23UWdHdR+cw8DlDBlCgE/S30A5h9tOpLubsaG/r6JWnJ+gNrtbmrRdO7sYMQva/GXK1nVrAsQGaoCDMvufgwYP/N2nSpLgB/sJb6w0tHWhTZgV3t4teidBNjYbkkEhPem2/uCyjpt1fu127f7LLa4+vbZ+2Pdry2vWB9tdu1+6vPV6gvN/vMzTUHv8/Ku2hBaz2gxs1hTLLqIEB995///07aPpss661QS1RhEyEG93LtxbQKxH4+7gQPdLAZRGLad5fzOMyYoxyXEZMi2XEtFhGTItlxLRYBmltQFkE3ofTSmYE/w20v3Y7H4dj7fECrorP42mu2rZhB23VvcDEnuEAh9n30MxC5/nz59+LF+A4aKaGAOT5wbxpO6M9X9jOedqyqVjntujxo21Tostrjxfheldnx3tuu91JxWHBQ15goqpQgCMfFpytuGfTpk3v0PtSkJ/zAZBbLL2QMyRiDIUYCjEtlkllWmyTmNZrk1hGTMervF49YfL99JbNpvrat6k5onsCXvsFPcDhpuBXofjhS5cuPdzW1nZKWvGAfhaGvJ+cMiPRCoBBj8t16tCOTUfoWAw4WAWz/UKoi0wUQmGGHJW4aUx8TVlZ2YP0DR9sz/kAyBG8HsgjQ7IUAOD2jvY1dDwMa0F8hjsk4HoWHO2FyVet+MqVK9+hh5Hl2YQyvQGQm/PJXQkXcKcCge9YcFrJDPFftOVDVJFWWdH2Z4Dy9PpjT92R/e9QH8EiPAwwGtI9oXxdHxzb8ItgP9z90ksvNZ45c+ZdOf8K0vQFC42sKJAzwNoYRfmkiWltOV4Xy4hp3p5psdgHMa3XD7GMmKbyALKrs/3dMx8faKQkLDgAB6MhrTflRww4fi3uV1999QWy4rq/FlSYi0GB3GLJxa4ntc9+r9d/5tjHL9BB2T2JCXA0PsiKP/nkk7Vnz559X1rx/ueVIQ9z8a+OJMsygYGmaHTAXVdnZ+f7x/bsqO0FfEC4cZbC+eDYDmutuimU7v7rX//63x6PR/dPAnbK1QDITcoQYq/fDSHwp5hjMR3I7b9d70+3Xj7XPdj6Yt1f266B6htou7a+3nW6c9lTf/Lwf9PuuJ0suidhPYqBAEdzUAEcecVN+cMf/nCMrPg2acUhTf9goYtOk5ncFT5RKII0B07rbedykcax1hfr/tp2DlTfQNu19dE6Rk4c9o5th3d+cIxW2T0Je3HJ1UQCOKy1Cjilu9etW/c8WXGuQ8YaBVTINflydXAK+H007+TUyedpb7begA9MDuhJRAo4W3H8erqXLFlS3dDQsEtacVJDJ0jIdYSJMhvW29nVuevAtvXVtCsDztY7LoCjSWzF4dgDctfatWufkSMqkEY/AHIzja5gLjMHTgcm9AfyOQ9lOM3bo43FOsS0Xj1iGaQHCtG2D/XxPmKa2yPmiWne3uP3+Zvqjj9D21y0gD0wGJH1pnIDXmSiDAIAD7rYfOihhw7Su8TflnNUFH10/zPTRafJbFZOMp9ojrETp/mEinm6lYbZEG192vLcDr042vZp69fuH247psR2tDS/XbVl/UHaj613RKMnLFEkLgqXZSuuXGzigA8//PDT9GByK/6MyKCvAEOuX0Ju0SoAprzd3a3VO9Y/TdsY7oh9b64vWsDZF8cBXZs3b75UWVn5Z3nByXLqxwHI5c0gfYWCt8B6Nzde+HPj6dN4VhDuCZiL2Pfm2qIBHPuIVlyB/Pbbb3+THmurlhecLKl+DH9cHULkYuyfI45kwX68j5jmfcU8Mc3b4x2LxxDTescRy4hpoTwezXN1dVVvfeuVN6kIwx219Ub1gwGcrbhysYkGrFq16jGXy+WTrgokDR8UyE00iZNPKIojHWngsnr7D7Q90uNEWi7a4w1QHgz5vF5f3ZEDj1ETADdfXEZtvdGFaAHHPgw4flGKFX/00UePHDt2bDW9kRbbZRhAAYYcWMslWAMDPcxgb768mm7qYL631nqDvajCYADHARhytuLOBx544NnLly83SlclMv0BuZFGV2ToU6CH4HY7nI37Nr/3LOXicTSt9e4rHGFqsICzL66Oi+/du7f5tddee5g+f+KRrkpk6pvplr4CObsbORwDKBpy9pyuqX74YkN9M60y3FGNe2uVHyzgqIchV604DRvuI9CfpRtA2uPIdR0FFMjlU1L0pQcvjZo0PHvggw37SCqt9QZrgwqxAg5XRbXilHbdcsstK+vr63fIG0CRnw+GPFf9cbpbaejqaNuxZfXylWCod+G7lmAsJYDjDOLgWPiCE788x4MPPvgb8sc/kf44qRFhCECeez45/O5up/OTqo3v/oakwuvOwBBcADDFfFFycCEWC85HRCMwfIJfHBrnpBtATa+//vqvyB/3Sn+cFIkw4Ja+URxCzHKfHGaZ/tJ76SmdX9FrIJpoVeGHYrDEw4KUHHyIB+BoJxrDrorSSHrVRNWePXuegz8uIY/8BCmQG7P/zQVgAn735aYLz+3fsq6KFBLhjunCUlQ7HoCjPoY8yFVZtGjRcpqQtdGtvLhSPKxMh1MgYMkBefZ65TRJ0NDZ1rpxy2vLl1NHta4JDCaYijnEC3A0hCHnURU02rF48eJ/oYvOvRLy6M6ViVwVoymepye64yeytI8sd1dH+94tb6z4FzDSu/CwYNzgRh/iqSAAF/1xNNhBX4nouPPOOx+iByRq5KQsSB55YMizyRXHiEm3vbNm99o3HnJ2dHSQGgBchBsMxcV6Q+lEOHtonNjAPILcW1dXt3P+/PkLPth/eGgePqclQ0QK4L3synvBs2BKMmYIuhz2c/s2rf1ZY33tJyRAFy0MONzbuFpvCJwIwFEvQhDo5It3d3Z27naYCm6mGXWFPNE9UFT+H04B/vhAgPHM9Mv9fvpglNPZfGjnpntOHzl0lvqrhTuulpv1TBTgogVX0wcOHOiix7j2Dx899is0HJYvIefTMHCc1/uFjUwckcL9EHphpv34gY/uq9nzUU0IuGG5AXjcQ6IAR0MZbI6VxjfV17UVlQw5OqR0xEKah2GRkEd+TlXIIW2GGHK86tjtcjnqjx74pwNb38dteNFyx228W0/FRAIuHhOQq6BfqD1+0WIp2Dds1KgFNCRmkz65KFX4tAp5Bvjk8LndDkfriQN7fn5g6waMdTPcGPNOONxQMtmAq6DTnasWn8e1s7R8wvVkyEv4xKFRMoRXQDUIiiGnz16n4b8eGud2d9kbqnd+cC+9bu0o9chOCwBnuHEzJyF+N9WrhmQBjgOyBVchv9xwobOro3XbqPGTrjVZLGV8MaW2TiZ0FQhATlKyqrolk78B49z0HsGT+zatua/uyMHT1AKAnXS40fNkAo7jITDgStx++aKz+cLZD8onTZ1NryEeA59c+uUBoQb6X4UcBdPAJ8dwJt5CRTMDD+xY88YvGs+caqCWad2SpFhu1i7ZgAfBTY1Q1umdz+7zp45uHXfFjCn0AstJeUYJOZ+ggWLVXUmxKcesQHqWkm6/t2zd+saKJW0Xmy5T2xlu3MjBDMGkwg3tkg04jonQD3S60vbVVh/cPmbyFGu+rXA2+eRkyGGWZBhIAdYpYMST75H30Bg3fcqlp62p4eWNry7/N3rVWju1WYQbF5RJhxu6pQpwHJsh5wsN+nit13+quupA4ZChNSVDh881mkw0wiKtOcQaKEAnCJrMoMwIpJESj6Orlaa8Ltv2v6++QeeQLybFO5QpgRtapBJwHJ8hR6yCfuFUzQX6U7d5RPn4K8kvL5cuC6QaOKiQJ8EfJ2/bgItJR3vbwb1b1v3iaOX2Q9RCWG223HBJxKHAZP/+FMHSBXDAzYsCPV18dp06eGBjecVkk7Ww6DPSZVHO14D/sbsyYMEYCuDOpNfj7mlpOL9yw6oXf996sQFvn4LFZri1k6diOFpsu6YacLSef9lsxVXQ6c+d79ShqoN05/NI0TByWYxwWWjAQPrmYc96nz7xNeWBhxRofNvpbDl7rPpX2/73f97qdUkYboxx88Vkwm6/h+28ZmM6AI4miZCLoCvp86eON9ibWzYPHVk+mlyWKXBZMC7WdyI1vZKrvdqwrLEJArAxSoJvgna0XNpctXntkqOVHx6mWtliA3DtDRwYqpQHkJJOAe3BXFr88PCmSist+OKqjZZCpK+55bYvVEy78pcFRcUT8eRL3zAZbZWhnwIAM5bAw3/dXfaz9SeO/vve99fsovrggjDUPATI011xwPj8smJpeO++6QY4mhUwzwHI8Zg5vrQKyBl0W0FJSfENt/39D0pHj73LYrVayXWR1pwE0guBGYjRMaeOkNBDtc1NDSs/XLP6FVdnJ0ZIYKmxAGz2tTFKwnO5ozsQ7ZjIkC4uiraPLBJiLLAK7NP5vG63t/bQvkMOR8emoWWjx9Fr0CZKt0UrYd96nyvHtkM/xhwudkfsra0fHtz6/j/v2/zuB6S5OPwH6y3CjfMT25+KvubGNZWOFlzsINrHLgtbc7bocFsUyz7vq9+6oXzK9PsKCgsraE6L4rb0nVSxutxOByx5aA3Yz/aRn+1yOOobT598dte6N7dTabbUbLUx9Mc3btjosEEKXXkKc9MdcJYGkDPo8M0BOfvnCuRms7lg7uJv31Q+ruJOa1HxVLzcEv65BJ0lDB0z2LiAJD/7VNOF+pcr1/5tE72uhMHmmMe1RV87La222NNMARxtZmsO0GHNGXQAzrAjtn5x0Te/VD556p0FxSVX4Y1RmIorQSdlhKCAjfFsL1lse+eRptOnXv7ovbd2UBGAjAVgcwyLDbD5jiTATlurTW1TQyYBzo1Gm3HtwKDDmrNFVwCndSW+5uavXzNu8oy7CocMuRpfVgi8hiF3hxcDLgpm/GFilMfg6OjYf+H08ZU0MrKXNGOwxVh0RwA1X0hSMjNCJgIOZdFuLKLbAovOQ4si6PlXz7959tipM28rKhm2wGzNL8TrGHLlopShxoQo3Fr3drsdXZ1tWxtO1byzf9v7uL0OiEWokYa15kX0szPCalPb1ZCpgHMHGHIGnV0XBp0tu2Lli4eNKPrc/C8vKC0v/0phybA5NI5uogldivuSbePpGAkB3JifjU+CODrbqlqamjYc2LZxq73tMm7QsHUG0Aw5Q83DfrDaGeOOMBRinOmAc1+0oPONInZfxFix9BOmXjVq+py5Nw8rG/kVmp47lV+XFvDVM8+NUS11H9R0S91xqq350oYTVZXvnzt15CKJxQAz3GKMbbDWbLEzGmwRDE5nQ8ygIwbksOhs1QE54NbG5qu+cMO0cZOmXW8bMvTqgsLiWQS7FW95hc+ersAHA+1XXmRJlrqbXqxz2NnRvv/CmZMfHtm1/ST1F9YY8ALmUDG2s8WGC5IVYFM/lJAtFpz7wzH6xbADdF4AuBZ4xaJTvrKtoLjYOuvaL80qGzPx8wVDSq622Yo/ZTSbLLhbqjwzqsxPp9JKCMiXqBGaAMQ4UMD1xU0Y8jsMmM2HJ9ZpLprH6bQfc3V07m9uPLvv8J4dh112O1wOhhpA88KA8za21myxldqpfFaFbAWcT5IIOvx00VcXrTtDznkcm4aWlRXOuGbeZ4aXjfm0xVYwMT/fOtFsLRhnwsMYyvCjUQG/76KVD62NtVIHoNWWUiAmoHFRCJAVX5pi+oKdk+zzBbe7+6zH6Trb2tz48fG9O6vbm5txVxGQsiXmmMFGzHmIGWjRvw7dGCqc6UGreqb3J1z70VcAzjFbddGVYbC1sVhW+aFUzPzMqNETJ1YUDyubaLUVVeRbrRNMFnOZyWguzAvAj9fToZ6AmwPLjxUKCk1EMltoir30OJOjhyD2+b0On8fb7O7uPtft7Kq3tzWf/eTs2fr6mmr40AylaH1FeEOlxbLsfnCstCeb/2PNs7mP2r6hz+ICeNmycwwwGWqGnde5jBgjjUWsN4/cHfPQ0lFF9PidzVpUWFRgK8KMSIPL2eXo7qLRuvZWZ3vLxS5yKwAmuwgcM8yI2eqKMdIMNEOMdbEM78t1ckzFciPkIuDimQ0CkjYwqByL8IuAY7u4jcujPqS5XkoGpbEuBhE4TrN1ZcC1wDK0DDWvc3mOuT6OxePmTDrXARdPNGvBcCLWgsvrDDEgRzmOOT9UXTgW5wM6BIZPuw5IkQeIxZjh5RjbxDTWsSBwHFjL0f9Z8BztfthuszaIwy0i1NpyOIBYj3hABlCMkdYuDLs2X1xHvViXQaMAi6/Jlqs6CrBeDDKKhcsTt+tUGQQmQ8rwYp9weXp1yvxeBfjkSEFiU0Cro3YdtWvzGFzxyNo87bpYVqYjUOD/AZrbm7Ts1rpFAAAAAElFTkSuQmCC',red:'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALgAAAC4CAYAAABQMybHAAAk/0lEQVR4Ae2dCZxUxZ3Hq8/pnhkGmOEQuQS5VCTxWHEDBlyNkciakMMkxujGuOvHO24IKCae0UQlKwmyroocoqtozGpA4oFiVAQU5IaRcchwDsPczNF39/5/b+bfVL/p7ume6bur+DyqXt31r2//5//q1XvPIJRLpgQMMVYeiDGfyhanBGKdgDirzYvsLLtIPguB0/lc7+vh5vNIvr68Oo8ige6EH6Vo3iSxjODrw1rcm2++2f+MM84YV1hYONRkMhUZjcY+OAKBgOZTXDGVLTYYDH0gNYpvIa/V5/O1+v3+FopvgY+D4tra29uP7N27d98VV1zRiOy6g06DcRyGr1wYCfCEhUnK2yiWiQYvSQG+8ac//all3rx5o/v16ze+oKBgnNlsxjGWQB5LgA5MhrToh1BL0Fd4vV4c+1wu176mpqYvHnnkkf0rVqzwUJt+OuQfALrBmh/hvHc8mfkuCBlmDei5c+fab7nllguLi4svttlsXyeYzyPgrJkgKPpBuQn4LU6n88PW1tZ1ixYt2vjoo486qG964PMe9nwGnKE2EhgG0tDWBx988HzS0BdbLJbpdEymeFsmAB1DH5wej2cTHR+Qhl937733biYN76ZyAFyGPoaqcitLvgEeAvX1119ve/jhhy8nLX211Wq9lKYWtnIuuFa3272WtPv/3nPPPW8tWbLESYPKS9jzAXCGGj60tfGLL764YPDgwT+x2+3fp/MyOnLZ1Tscjj/X1NS8OH78+E9poNDoslbPaTMmlwFnsDWoN23adNq4ceN+TBeIP6FVjbG5THSksdEKTQVdqL64b9++lyZPnlzVCboMe6SiWRufi4DLYJsqKyunkraeCxOELhJzcbxxw0cXqQGYMKTVHz399NM/pgp8dOQk6Lk04RiLpq3JNx04cOAbAwYMmEurH1PoXLkIEqDVmPV1dXWPjhw58l3KwqAz7BFKZU90LgAeBHvYsGHmjRs3/mtpaekcMkPOy55pSH9PyXzZ0tDQ8NiFF1646vDhw17qESDPetCzGXD0XYObwLYQ2FeVlZXNoRsvZ6Yfl+ztAd1Y2lNfXw/QXyHQ9TeTsm5g2Qp40BTZtWvX+aNHj15AGvv8rJN+BneYNPrm/fv3/2LixImbqZuy6ZLBve7atWwDXNPYNAzT0qVLS2fNmvUA7f+4gS4eAbxynRIwHN8ozDseFQZHTW9l4m/3BBa/8nnDfT97vKqBKmPQs2ZpMVsARz8ZbvPRo0ev6d+//yNkjiRlD0hvqUh3ecsrpwtD2+GEdcMfELUNbWLewNniBaqU7XNAnvGgZwPg6KMJx44dOybSstYf1coISSOKsy4tiJLa8ySPX6wvrxN3TLpX7KJaoM1xZDTkmfynPaixJ02aVEBX+PdOmDBhg4K754D2tqTFKKacPVhsOPEnce+kSQK/IjMdYChjFWWmdozhNq1evXrYtGnTltKNmq/3doLypXyyNLgsP49XfPhOpfjZzCcEbKGMtc0zEXBeITFXVFR8g5YAn1O2toxW9+FUAI5ewDY/UC9+PvrXAjeJ2DbH2nnGuEwyUYJae/r06bba2toH6O7a6wrujGGlS0eMBjFw1ADxetMT4oHpZ2lbi3GtlFEmS6Zo8CDca9asGT516tTltClK3WLvglRsEanS4HJvXF6x/v0vxHXfWigOUXzGmCyZADj6oK2S7N69ewrdtFlJWnuALDwVjk8C6QAcPSSTpa6iTvxwwm/EejrNiFWWdJsoDLeZdv3NHDNmzGoFd3wwZ1JuMlkGjBsoVlf9TsykfmGFBYorrUo0nYCjbQjAQjv/rhs+fPjLFLbToVwWS4Boto/sL14++ri4joZhoQNznDbO0tVwEG66K/nLIUOGPEW32/GLVy43JGAeUiKeqvsv8UsaTlohTwfgGtz0EIKV9iE/RjsAH1IPIuQG1SGjCAhDWaF4qHmBeGzwYIG3EaRFk6cacA3uGTNm2OnFNs/16dPn1hChqJOck0CJTdxaeY94bsZkzfxMOeSpvADQ4B44cGAB3cBZRg/8fi/nZjNDBpSuVZRow3d4xGsjHxT/VlsrXJSPlxGjFUlIWqo0uAY39dhaXl4+X8GdkLnLqkrsFvE90uTzwQAdKdPkqQCc4bZUV1fPpXeQ3JhVM6M6mzAJ9LGJG+v/IOZShSm78Ew24DCB0Ibl0KFD19Ozkr9JmLRURVkpgdIi8Zvqx8X1YIIOsJFUMzmZgKPj+FNkoQvK79CqyQIKK6ckIE4pEQsqHxbfIVGwJk8a5MkCnOE2b9++fRrdfl9CS4GAXTklATwiYRpVJp7bfb+YRuJI6h3PZAAOuFGv+a233hpPb5NaSeHkPGJCFSuXnRIgSGwTBomVb/2nGE8jAORgJuGaPFmAm+iB4KIpU6Ysp70lJdk5BarXyZaA0ShKLh4tls+6QBRRW/gLn3DAE10hfjDoaMHx48ef6Nu37w0UVi7FEsjEdfBoImh2iMX97hR3Up6Er5EnUoPjx4L6LHv27PmugjvalKo0WQJ97eKGLx8U3wU7dIChhCneRAHOcJtXrVo1dtSoUQvlAaiwkkB3EqAngxauuk2MpXwJtccTCbj5kksuKaLHzZYpu7u76VTpegnAHr9svFh2yQTNHgfkCdHiiagEPxLN7qbnKOeXlJSoO5X62UvxebbZ4LJ4yB5/muzx2RSXEHu8txo8aJps2bLlYtodqOCWZ0uF45YA7T68cfu94mIqmBBTpbeAa9qbzJIiWu+eTy9Wj3tAqoCSgCwBIETr4/OnjwsuHfaK0d4UZu1tef7552+nF/OMkzuqwkoCPZWA1SzGvXS9uI3K93pVpaeAM9zmF154YdSgQYPwaJJySgIJk8DgvmL2C/8hRlGFvTJVegM4Liwt9HTO78k0KUzYyFRFSgIkATJVCq88S/yOgr3akNUTwFl7W3bu3IlvTH5LzYiSQDIk0KdAXEEbsi7vhBysgr24XE8ARxnzlVdeWUw3dB6LqzWVWUkgTgmMHSgeu3Ky9oFeNlXiqiFewFl7m5988slb6XPXI+NqTWVWEohTAhaTGPnMLIGH0xnwuLR4vIAjv4k+gV1Cr3u4Kc6+quxKAj2SwIA+4qbrpwjsSsV1X1zMxpOZtbdl3rx5P6cX0Zf1qLeqkJJAnBIwmUTZ/TPFz6lY3MuG8QCOvGbc1DnllFNujrOPKruSQK8kQG/Kuple0Yx942yqxFRfrICz9jY/88wz15DtfUpMtatMSgIJkoDZJE5Z9mNxDVXHgMdki8cKOPKZ6Fs59lNPPRV3mJRTEki5BIb0FbdNOj2+N2TFAjhrb8vKlSuvIu09IuUjUw0qCZAErBYx4i/XiasoGLMtHgvgmvYeOnSolY47lKSVBNIpgWH9xR0lJcG7m93y210GaG8c5ldfffVfaEPVmHQOTrWtJEAbsca8f7O4BEzSwXxGFEwsgGPt0Ux3LX+ktsNGlKNKSJEEsJ121CDxIzBJB9gE5BFdd4Aj3XT11VfTM8R9Z0SsRSUoCaRQAn0LxIyrvyb6UpMAPCrD0RLxy0C6ee7cud+m5yzV50VIGMqlXwL0/Kb9nsvEt6kn3S4ZRgMcaRrg9P2cHyjzJP0Tq3rQIQGYKSP6iR/QGQMekeNICdDeOExPPPHEiMLCwq91VK3+VxLIDAkUWcXXnrhaYMma7XDw2sVFA1zT3jNnzryKtHekfF0qVBFKAqmQABFpnDUxRIvHDbi2ekKPo30/FR1WbSgJxCuBwcVBwFmLd6kinGbGLwHxphUrVoynte+xXUqpiIyWQKBoWEb3L1GdozXxsS/9u/Z2Wl5N6aLFYaTrXRDwr371q9PUxaVePJl/3nzef4uaN28S7hNHunQ2EOgSRa/r1rkuEXild1enr6unecJVHktd9OlwaOJp1LPddEApg92QotEANw8YMGCqApwktmen8K9cIURTI8kv810BdXGI72JR73LR9+ND5jvzOx9nD80u11QhVj1DxRjwkBr0gOMXoGnwoqIiM72p6sKQ3Hl64nv0fhGoPZ5Vo8ff7P5+v2jw+Eil5S7kfQKBC8FqW1sbAx6ixRGpd4gzLVu2bKLJZCrVJ+bjebbBzXNkoTsipfRQo0HTWRybWz7BWvqHkYMn0qjYDg8ZoB5w1uCmM8888yJlnoTIKitPGHIj3R3hyc0lHwCPLbRdRB4A56EF5yoc4Igzkf09RQEelFNWBwB5f3okJhfnE2MqNZumgFk6wC4gDzoZcKbfRLfmrWR/Tw7mUoGsl0Ao5DzVueEXmUyThxcW8heUeVDanMmAIwLnxoULF55Nv4w+Wg71X85IgCE3AoEccjScPr8ZderZNCSNX3lo8ioKk28aNmzYhFz8cyYPPF/DHZAbRKPXmzNrK6B6qM0ygbxP6WCOtaUjWYNzgpH2fo/JVwDyYdxmUuH9zWZN3eXKePuYjGAWPDPH2tD0GhwZTLR7cIzS4Jp8cvY/QN6PIG/KAU0OVouMxtPBLh0MuTZ3rMFBPRzOjQT4aO1M/ZfTEmDIc8Emt5s0wDV+OydNY5oBR5ym2ktLS802m21kTs+sGlxQAoC8r4nMFZp9DQAGIct8m9EwstRuh0XCw9DGqAfceP/992MrGrYzKJcnEjgJOdjIUhcQBbcPHQx2wXRwIGyDM/XGs846S22PzdI57k23AXkJmbAnfNm5dwUAn1mkbe3+ohNwRAVYgwcBHzhwoLrA7A0pWVxWg5xe5Wo8qQCzZjQAuNRs7rKSwhocAwHsRrvdPhwnyuWnBAB5H9LkLZomzy4ZFJmNYFfjmHuu1+AGevdgMScqPz8loEGuafLsGr/ZYAC7bI3A1x6751EgwkhbZIvVGjiLJH99QF5Mmrw1SzQ5mKVFcAAOpa3BjdnjkyD1BDheMq6ckoDQNDntQsQSYjY4ghzsBllGWLbBMQaDAhxiUI4lYCLNWEzmiqbJM/zBIKvByIBz9zUNzica+QpwFofyWQIMObGe0c4kAgx4sKeswYMRCvCMnsO0dY4hb/P5M/YZT7NJ0+AsI41pXkVBJCKUicLiUX4XCQDyIhNWyYP6sEuedEZE0+DcLwU4S0L5YSXAkLdrmjxslrRFGmOxwal3bLakraOq4cyWACAv1DR5ZvWTVlHArmaJcM/YRAn+zfF6ve2cqHwlgUgSYMi7rDNTASYs1b7PH5DZ1Zjm/gXH4fP52oInKqAkEEUCgJz2YWeMRU6Xv3p2NZWO1c3gCqfS4FFmVCV1kQBD7qS3aKX7LXE+v1/W4BrXbKJwxwNKg7MolB+rBAC5jd69Ql5anS8goMGDyhqdkS8otQQFeFrnKGsb1zQ5Qa5p8jSNwm8ImihByMNq8EC6/9akSUCq2d5JAK+H0zR576rpUWkwSyuXETW4Zq9QzQFlg/dIvqpQpwQYche9vDvVb7X1BgRs8CDL6JKswbUEAry1s6/KUxLokQQAeQFtQUz1HU96FzrYZcC1vss2uBbhcDgaoO7VnvAeza0q1CmBDsiFcPlTIxJQ7aTXoetbYw3O1PsbGxv/oc+kzpUEeiKBk5q8J6XjL9Pk9YBd/KSYZ81EwQmcFllRUbFfXWR2CET933sJAHKrZq4k9w4nelrldOwnLwg3wqzBka4lvPbaawpwSEO5hEkgCHkS18kB72v1zXrAg+vgTL3/7bffbnG5XLVms3lgwkaoKsp7CQByC0nBo+nRxIvD7ffXrjve1EI1dzFR0FoQcGRobW2tUmYKxKJcIiWgQU6gJ1qRg9U2X6CK+gq4wwKOcQByLUN7e/s/EKGckkCiJQDI6fUOCd9x2O7zgVkZcK3rbIPLGtyHlRSlwRM9tao+loAMOcf1xge8TT4vAPfREaLB5XXwIOTHjh3b35sGVVklge4kAMhhqngTsC0E9dR6fGA2BG70IZwG97/xxhs7aNMVgFdOSSBpEsDNxA5zpXdWuY/MjVW1zTuoowA8BHIGHIMA0Ej00Udga5ubmyuVmQKxKJdMCQByE/ENfd6Tf6C2xR+ofPFITS31UzZRtG7rAWfIvfX19ZsU4MmcWlU3SyAIeQ8UOYCt93g3keelI0R7o34ZcJxrGpx8X2Vl5UYFOESiXCokAMgBI3lxHTDkqxyujVQU2ps1eLDLMuD4MbAG9y1ZsmSTn1wwpwooCSRZAgx5PM3Qg3L+JTX10OAMN3OsVRMOcO2XsGrVqkayw8uVFo9H3CpvbyXAkMNa6e7AQ6DNXl/5W8fqGyk7a/CIgKNvrMFhz3hpufBTBTjEolwqJQDIAXd3DrDWuj34+KvGK/lsgweLyhockQw4fg1essM3KMCDslKBFEqAIY+mxdGdynbPBvIAuGyiIElz8o0eRLB6xy/Bu2DBgk8vvfRSZ1FRkU3Lnaf/VRaVCM/xmpwffSRlBijCuUjxyBsxLUJCpMfbkB39AvD6/jn8fufjh46wBmftHdJCOMCRUbNnPvnkk5bq6uoPRo8efbmRnphOhGvbWiGO/c9fhaeuORHVpaQOt+8rwlmCb7uHyC6k7UgpUctEKBStTEijnSf6iZfzRGhCyxJvO3K96Q7T42mi2nnig21N5dhByPY3Qx7snh5wJEAmmgYn37Nt27a/nnbaaQkDfP+dTwp3dT3aySrn9HtEu9+r2YYQUCw2IgbIeRk0lOO4cOmIk12q88ttI8x9jdR/fX79eXfl9en68pHG7w34xW5nzV8pv4cOeQ08pIpwahltMuDeW2+99SPaPlsfTUuE1NjNSTbCjSEVGS2i0NihD2KFG+U4L3w5jDQ4OY7zyL6cRw7LeeSwnEcOy3nksJwHYb1DXjguw2EtMob/uiuvT+d22Ne3h3iw6Az46he37PyITiNeYKJsNMCh9j20s9Bx+PDhvyUKcDSarQ6Q2wnyaNf4nMa3nTFWjsuEcXNfYu1fsvP3RCbQwLU+598a3W4HBaHBw15gou5wgCMeGpy1uGft2rVv0OskEJ/3DpDbjCYNWoZE9iEghkIOy3nSGZb7JIcj9UnOI4cTlT9SPdHiAeZ2Z93r5MnmCaK7uEiA40eCXwWo9sydO3dXU1PTl0qLd8iPIe84U/+nUgJgsC3g+XJJ8+7d1C4DDlbBbBcX7iITmZCZIUclbloTX1VWVnYnfcMH6XnvADmcKwDZKpcqCUBN13jbVpHnpoPNE+a1SzciaXBkRF1BLb58+fI36GFkVKhcpwQ0Td7lS4xKPMmUgFv4PG+3HXmD2pC1d1jzBP2IBjh+FSgIM8W9dOnS6qqqqjfV/iuShuSwsmJTkEsSSV4Qa9+1Pseb77ZWVVMr0OBgE4yC1bAuVsA1M+XFF19cTFo84q8lbAt5EKkgT80kuwMB/7q2I4upNTZPegU4eh2ixefPn1958ODBd5QW7zqhDHm0q3+V1nMJkPIW9f72d149UVHZCXi3cGOWomlwpENbB80UCrteeeWVZz0eT8Q/CSiUr64DciwhnnQcjnbjArk5PV6fy3KL+va6q6+35fX1d1dfd+n6+vjcL/yBjx3Vz1J5Fx2yeRLVougOcPQHFeBiUzNTfvvb3+4lLf53pcUhmq4ON4IKDB2QY3Lg2JfDPHFyHMLxOq67p/X1try+v93V1126vj6cd9jezr+vaCrfS6dsnoDJqHCjbCyAQ1sHAaewa82aNc+QFkd55cJIQA85w5cKH91hiORwKtpOVhs+4nij89gzNB7W3oAPTHZrScQKOGtx/Hpcc+bM2X706NENSouTNCI4QG4lTZ5qx3AzbGif41Ldl0S0B+1d73dtWNy4ezvVx4Cz9k4I4Ogna3EY9oDcuXr16oVqRQWiiexOavKTiOEyC44vtzisRXbGcxznicfnsrHWp8/P5SL5+v531zd9/fry3aV7aOVkk+P4QsrnpAPsgcGYtDfli8lEQT4ADi3OgLtmz569jd4l/rraowLxRHY2TZPjY6kd/5CTJ1kOR0qPXHP4FK471vr0+blcJF/uc/gehMbq69eXj5buoy2xR31trz/duGMblWPtHdPqCfciFhOF87IW1y420eBdd921gB5MblR7VFhE4X2GPHyqig0ngY49J97GxU27FlA6wx2z7c11xgs42+Jo0Pnee+/Vbtq06U/qgpPFGdkH5FhdUS42CeD5qb2exj997qzFG6tgnoC5mG1vbiUewFFG1uIa5LNmzXqNHmvbri44WaSR/QLaZstLiJyLrXP4sRwox2XkMJeV4+Qwpyfal9uQw5HakfPIYTk/tHej37X9vuMbX6M8DHfc2hv19wRw1uLaxSY6sGLFikecTifegYg6lYsiAUCO1RWeUGRFOFbHeSOV7y491nZizRdve93lB0Nu+qD8O22HH6E+AG6+uIxbe2MM8QKOMgw4flGaFn/ooYd27927dyW9kRbpynUjgSDkeP+HOkJkEKBfwCF/68oXmvdgv7dee4O9uFxPAEcDDDlrcccdd9yxqK6urlqZKrHJH5BbeqRfYqs/G3NhzftEwF39ZNPORdR/PI6m195xD6ungLMtzsuGzs8++6z+pZdeuos+f0JLl8pUiWUmGHL82c73A69hcwm/5/3WQ3eVOxrw2gWGO651b73cewo46mHIg1qclg23EOiL6AaQvh11HkECgNysNDltdPKLfe6GRU837d5CotJr7x5rzN4CDlMlqMUp7Lz88suXHzhw4CN1AygC0WGi8x1y3NCp8To++lXN+uVgqPPgu5ZgLC2AY6rQOA6+4MQvr/3OO++8j+zxGmWPkzRidJq5YuiNvomxoQzLBru72e+pWdS46z7qWjsdYAgmAJhivijYM5cIiaITWD7BLw6dc9ANoGMvv/zy3WSP0zeGevzjo6ryy2H50EKQR7pNnmvx0MvugN/7vuPw3Vucx47RbGv8kA+WeFmwVxAkAnAQjM6wqaJ1kl41sfnTTz99Cva4gjz2OQLk+DBTrjswAbt7r6fhqacbdm6m8cpw9+rCUpZdIgBHfQx5iKkyY8aMJbQh6123Gz9I5WKVwElNnrurK16C+4i39d05NeuXkFz0pgkUZkL+9CcKcMwdQw6acaGATrfPnDnz13TR+ZmCnKQRh4OpYs5Rm9yjXVS2f3ZX3YZfk0g0TsjnZcGEwQ1xJxpw2R7XIKevRJy49tprZ9MDEuVqUxZEHrtjyHNpjRwrJvU+R/nDjZtn13scJ0gaAFyGGwwlRHtD0snY3obOyR00EOTe/fv3r582bdr0pmXv9MVXbpWLTQImklWHQGWRxlY203IB7kaf69CC5p037XDU4osCbXQw4DBvE6q9Mf5kAI564UJAJ1vc1dLSsnFUZctltKOuUEHeIaRY/gfkcBBotq6k+KnzJwKe+mUnym9c13roIA1FD3dCNTfkBZcswGV1Ewxv3bq1rcBk+Xycpd836c+vVUHeMQmx/M+yCgozlkIZkoe2mYrWgKf19ROVt/y55cty6pYebmhuAJ5wlyzA0VGeC/a1zm9z1jaVme17hluKL6HVAgtPXMJHloMVsqxCBJrh4+yA292+tv3Ifz7btAu34WW4E7beHUkMyQRcbhNzEpyXTY5jx+kJly2jrSXTSZPbeeLkAiocXgIsq6Aww2fLiFjY3Cf8nsbX2/bf9mzjLqx1M9xY80463BBCqgEPgr7VWdvQbvCuH28tnUo2eR+eOHRKuegSCMqKTHOY55l44F0mDQHn0eXNX9z8yomKPTSiVjoAOMONmzlJsbup3qBLFeBokJVOEPJyV2PLUW/738+2DbjAZjCV8cVUsHcqEFECgDwoyIi50pOAde46n6NiYePuW9a2HfgH9QJgpxxujD6VgKM9OJ4XzT/gOeHY7W5Yd65t0CS70TRE24nRuWrQkV39H0kCDHmmrK1gZnH7/ZjXsfWRhs23b3HUHKW+682SlGhullmqAQ+Bmzqhndd6He5PHDUfTC48ZXShwXyagpynp3ufzRUIMp0OuwLpWUq6/d72wd21G+fsdzfVUX8YbtzIwQ7BlMINeaQacLQJ1wX0Fr/b9zfnwQ/PKxhUUGKyTjIJo4Enr6OI+j+SBGQ5YcU81Qfgdga8gQpP0/O/qP/4d41eB77yK8ONC8qUww15pQtwtM2Q84VGwEsbyN9srdo60FRYPsRin2wxmOzYS4AHc5WLLoGT5kr0fIlMxY5AvL+k2e9ufK/98Lz7aje9SnPIF5PyHcq0wI2xphNwtM+Qww+CvsFRfaTa2/beuILSM+0G0ynKZIGounephJxNkhpf+7aFjTtvp5WSHdRDaG3W3DBJ5KVAzHHKXaYADrj50KCv8rS0rXFUvXtOwSBTX5P1K8pkiY0NNleSSRNu3sAkKfc0L7+j9sMH97ua8fYpaGyGW795KrbOJyFXugHHkHgu4DPkmjanP3e+Na1V2waa7buHmAsn0/ZRu7YXQ5ksUVE4adIlducKcU0mCW7euBvWOo7c/UDtxr90miQMN9a4+WIyabffow5el5gJgKNLMuQy6Fp4g+PY0cNksoyylgymz4SM7nioS9nmurkMOT0JeUh0j05ga/toiuj78OKQr/W9RY3b57x64stdVBlrbACuv4EDJZV2l2lXb+gPrivxw8OXVgvosNFhp6MQ4TvKzvnni+yn/rLUaB2BJ1/4TzKlKRdGArCVe+PY1m70uw9+7Kz+wx/rt26g+mCCMNS8BMjbXbW/vr1pM5FlMw1wjA19wgHI8SVmKx2AnEG39zfbiu8vu+CasdZ+19HHWAvM2ESqzBYSUXgHDRwv5rxC0ub3uCrI1n6w/tMXGr1OrJBAU+MA2GxrY5WE93LH2xQVTZ7LFBNFP0IWEnwc0Aps0/mcfq/3rbYDO+r9zrUjLMVDaePWCGW26EV48px//Kw5ovl4wxSbI/Ty+Y+fa97zq0WNO9aRzOXlP2hvGW7MT0aYJCdH3RHCWDPZoX9ssrA2Z40Os0XT7HMGnP/1C2yDbulrtI7E64nx7lae1EweXKr7Bq0cybGd7SI7m9a1D3zmqln0WN3nH1J+1tSstbH0xzduWOlErjhSgymKz3TAWQyAnEGHbQ7I2T7XIDebzba7+p1z6STbgGv7GwvGKNBZdNF9GWx6J/eXO5x1z/++aetaejMZg80+r2vLtnZGam15xNkCOPrM2hygQ5sz6ACcYYdf8Kuy8y86zz7g2jKj7SwFOkkkjJPBJlNv9xZH3fOP12/+iLICZBwAm31obIDNdyQBdsZqbepb0GUT4Nxp9BnXDgw6tDlrdA1wOtf828rO+afJtkHXDTLZz7XiNQxUBIXz1XwB1KASa9n0Rilx3Of4fJPz+PKF9Vs/o2gGW/ZlcwRQ84UkBbPDZSPgkKzGKfmy2QKNzkuLMujWG0rPmnRhwZBvDzbbp9sN5kLAni8XpQy1n9AG1I6At51edPnBRlf1G4sbduP2OiCWoUYY2poP2c7OCq1NfQ+6bAWcB4D+A3IGnU0XBp01u6blh5qLi27od8b0Mdb+3xxosp9PoJvwch3Anmvr6Vi/BtRegprA9tX6HJu/dDe+vbhp7wf0RincoGHtDKAZcoaal/2gtbPGHKG+dnHZDjgPSA86TBi20dmEYV/T9FMKTx00q3j0ZSOsfb5ZYrCO0UyYLNbssqbuhBpfS/jyoLvl7f9r3f/O+vajx0kmDDDDLftIg7ZmjZ3VYNM4NJcrgMvjgTbHuAA5NDprdQDO0Mu++Yf9xo2dXDB4Kmn1c/uZCibShWkBPi+CR+gy1ZSRgcbmJzxJQ0t8riafaxdp6883uWo+Xtm0r4LGDG0MeAFzOB/prLFhguQE2DQOzeUa4PK4WKsDdD4Ath54TaNTvJbWz2wr+FHfsRMnWErPG2iyndvfVHAGwW7BBSqA7/jX0QwLL1kXrYAYjg1f+LhMBNC4UCSoPfSmqL21Pufn5Z6GLS83V+xq8jphcjDUAJoPBpzTWFuzxu6ongrkkuM5yqUxyWPB+Bh0va0ua3eGnOPYNw21FRX+oHDcV06zlpxdQvtfCg2mEYVGy1CrMNpZw7Mvwy93AmG9oBlafT6GGPYzQGbfLfyOdr/nSHvAd5B28x2scp/Y+Wr7vu1HnG24qwhIWROzz2DD5zj4DLRsX0fqDmXPbqeXe3aPJnrvGXT2WavLpgyDrfflvNoP5eLiYYMmWctGDjEVj+hrtowsMliG01cayugppEK6k2qnbWCFlNGMxhh81vRsXkAbgywizUuvWWinW+QOT8DX7vL76tsCnkPNXs+Bal/rwR3u+gPrWg/DhmYoZe0rwxsuLOdl84P96BLLgdR8Apynq4O5DqWKMOAFtLIPwBlqhp3P9Xk14DvrCKmbzB3zSGtx0RBjob2fuaCoj8GKHZGiJeBub/K62qr97Y4D7tY2MisAZofyPukzzPBZ68o+wgw0Q4xzOQ+X1ddN2fLD5SPg8syGAEkJMqx6kGXA9WlcDvUhzPWiLTmMc9kxeIjjMGtXBlwPLEPLUPM552ef62NfbjdvwhC+ch0SYFkwkPD14PI5QwzokY99jg9XF1rheEAHx/DpzwEp4gCx7DO87CNNDuMcBxz7HWd5+j8LPE+HH3XYLBv40Q4Zan0+NCDXIzfIAMo+wvqDYdfHy+eoF+fK6STAwtdFq9MIEmB5McjIFi1OTo9QZQiYDCnDizLR4iLVqeI7JcCTowTSOwno5ag/R+36OAZXblkfpz+X86pwDBL4fwN/IZwMBwH5AAAAAElFTkSuQmCC',yellow:'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALcAAAC4CAYAAAChOH1KAAAlaElEQVR4Ae2dCZhUxbXHTy+zL8ywDDsSVhEVJQoCkoSIIr4kvohLxO2ZfC8an0mQrCQm+uJ7qHkv5hE/xSQaNokBogkxigaUuLDIpsiOMA4MOwyz7zPd7/yLOZfqnu7p7umeXut83+2qW7du3apTv3v63Lr31rWRkUhowOZViPc6Nutpbq/8WPVO8173sYtJ6kgDusI7yme2nYdTdIZQj0NH1vrgwYNtc+bMyb344otzCgsL87KysnLT0tKym5ub6+rr62vKy8urd+7cWfv444/XlJSUAGSB2VfoKw3HM9KBBqQzOsiSspsEXgmhCMTtDGT2TTfdNDwvL28kQzvC6XSOcDgcQ2w2Wzfenme323M5nuN2uwPql/O5OF+dy+Wq4X2rOV7Z2tpa3NLSsp9Pgv3V1dX7XnnllU/4RKnj7S5edNARl4WjRnQNBFS+njnJ4wKxHtp37NgxpG/fvlPY6l7CAI/kZTgv/YMBN1L64hPAzcAf5eUTXvax9d9x/PjxtZdcckkxH0OAF8gljNThE7acVIdbQLZzDyJuX7du3YChQ4dOycnJ+QJb5M+zFR4Qr73L1v4IW/h3amtr/3nw4MG1kyZNOsJ1FdglBOwpKakItwfQTz31VN4dd9wxnd2LL7J1/hzDPCxRSWDYD7BVf5fdmbeXLl26avbs2dXcFsCdkqCnCtwCNEL7gAEDnBs2bPh8QUHBnenp6TdyWi4vySY1TU1NKysqKl6cMGHCO0eOHGnhBuqQJ71FT3a4FczcqQgdu3btGtWvX787MzMzv8YWun+y0eyvPWzRjzY0NPzp2LFjL44ePXoP52vlRbfo/nZN6PRkhdvyoX/7299245GNe9iHvoMvBC9P6N6KQOX5gvRD9tGX8gjMovvuu6+SixRrjjCpJJngRluwAGz78uXLu0+dOvXbDPW3eL2QFyOeGihnyOevWbPm6VtvvfUsbwLcAnpSuCzJALcH1KtXr+4zduzYWbm5uf/OnZWMvrQnouGv1bD8ftu2bf937bXXnmgDPCkgT3S4lZXmDrGvX79+0EUXXTSbRz3u5fXM8Ps85Upo4FGWBbt3735q4sSJh7n1YskRJqQkKtyoN8B2vPzyyz2uueaaX2RnZ9/NN1bSE7IX4qjSfMOoqa6ubvFbb7318xkzZpRx1XDxKZY8jmoauCqJBjfqi8WB4bzNmzf/W/fu3R/j9R6Bm5o6OWyuErK1fMiKwuhfJ8VNZWUVtT/77MTHFrYNI8oIS8L444kEt7ggju3bt182bNiweXwHcXwnuy5pdwPYzrofMdiR8SaaW9wffFLc+N3RE/7xEStNrHhkCu/iXkgEuFFHBfb8+fMLb7vttkf4YvGb7II4u1g3CVm8vXEpORtfiGzdbbaWmpqW3724ou4/v/WDj8u5cMAd965KvMMt1tp56NCh24qKip7gmy99IttzyVWao3ERYekKcbnpxKmy5h/3HbVpGZcvdzzj1ooDnngUnHQOLI8++mhBZWXlC3369FlowI5tV9lt1KdPz7SFdaUTXnj00REF0kccxqWRjMdKWWBv2rTpUn7YfwnfWRwZ225NnKN3peXWtdDion0799TedfkXPvqY0+GLywWnni2m8Xiz3KgPLLbz6NGj3xgzZsw7BuyY8uH34E47jRxzUc47J/eN/wb6ixf0W1zxFC+VEWvtnDt3biE/ybagZ8+ez7CysngxEqcasNkoq6i785m6w1ctmDt3GB5xEMjjwiOIh0qgDjjJMG49hp9ae5Gt9fA47c+4r1a03BJvRbS43J/s3FN3R5ubIhebMR0Tj7XlFoudtm/fvmsuvfTSNQZsb2wSY91ptw0fMzrnrYObr7iGa5zGC9yUmBrPWMItYGOY7xZ+W/wvrIw8XowkqAa4Q/M+MzjjL0d2jL+FmxBzFyVWcOO4OLPT+AH6b/ELuAs5bp4LYSUkujDg6f37Ohee3ncVHjUWCx4TzmJxUAE7/eTJk4/06NHjKb7bGIt6JDpH8Vt/N9l7dnc8dfaTcY9wJWG0YjKSEm2oFNh80ZhRVlb2NL/D+KP47SFTs3A1UFiQ9qOakglPjx7dKyMWgEcTbgX2+PHjs3j6hCX8fMjXw1We2T/+NZCTY//65jeGLRk/vjuGdaNqwaN1NavA5salnz179jl+9evO+O+WxKxhrIYCA2mrtq71xdwLNt7P+Zp4kacLA+0W1vZoWG4BO4197McM2GH1V8LunJPtuLP84FWPcQOidpHZ1XCjfCxppaWl32Ef+6GE7R1T8bA1UJDveOjUnvHfAQ+8CBthl+uvgK6EGy6PAru4uPj23r17z/VXCZOeOhro1cs5t3T7uNu5xQJ4l7nGXQU3KqzGsfmF0+v79+8/P5oTR6YOKgnYUjfZ+vdLm7/vg7HXtwEOTroE8K6AWyy2kx9ZHTdkyJAlbY1IwJ4wVe4KDTAgacMHZy3Z9vbl47h83MkEhxEHvCvgRpnOefPmFfF49lLMU83rRowGPDRgs1POpRdlLZ33xJAi3iCAe+QJdyXSZ4sCmyuVwY+truA5RKaFW0Gzf2gaiNehQH+tqKt3vZkzaAOeRWnkRZ4m9Jc9pPRIWm6cKMrPPnz48HcN2CH1Q8pmzs6yTzux+8rvsgJkiDBiBjdScAvY8LOv4hd58UyBEaOBoDRQ1DPtEfa/r+LMEX2SMFJwoxzHE0880Yv97AXsZ6OSRowGgtIAeLl0VNaCJx4d0ot3wL9/RLiMxF8AKgKY4WcvY3dkOseNxEgDieZz62qqq29dlTNo422cFhH/O9wzBCeHgptfOHjAgK13lYmHqoHsLMf0ozvHPcD7yehJWMY3HLgtsBcvXnwB34F8ONTGmPxGA94a6FuU9vDiZy6+gNPDBjxcuNXoyA033PA4+01mLmzvnjLrIWuA36jP/dcb8h7nHcMePeks3JbV3rp167X8sVF8NMmI0UBENJCXa7/xo3fGXsuFhWW9Ows39nNed911uRdeeOEvI9IiU4jRgKaBi0Zk/vK663rDGxDAta3BRTsDt2W1n3/++Vk8jfDQ4A5lchkNBK+BNKdt6OJfD5rFewjcIV9chgq3BTZ/UGkY36wxz2cH318mZ4ga4Js7Dy1fMHpYZwHvDNzqIpI/1fEkX0Sab8+E2GEme/AasNltmdO/kP8k79Gpi8tQ4Las9rvvvjuBXxe7LvhqmpxGA53TQE6O7bp1r4+RW/PgNWj3JFS4ldXmW+zfY6vdudqavYwGQtAAOONb89/nXUK23sHCbVnt119//TKelmFqCPUzWY0GwtJAbq596j9eueQyLiSki8tQ4IbVdl555ZWzOTRmO6zuMjuHpgGbbfxlOeAOcIPDoPgLBm7LavM3H0fl5+d/KbSKmdxGA+FrID/P/qWXXxw1iksK2noHC7ey2pMnT8bQXzD7hN8aU4LRgIcGbPYpV3UDf0Fb70CgitV2LFq0aAhb7RkexzMrRgNR1ADPezJj0fyLhvAhYWzBbofuSbBwO6dOnfogX7miUCNGAzHRAA+cOP5lSt6DfPCgXJNg4HawO5LDs0XdFJMWmYMaDWgaKChw3DR5ck/MqBDwwrIjuMUlwTQN0/lzHvjuoBGjgZhqwG6ngmfnDsTbXgGtd0dwY5u6kBwwYMCt5qZNTPvUHLxNA+BwYP/0W3lVLiz9MuxvA6w2FsecOXN68fPaX2wr2wRGAzHXQF6O44tzZlsvEwur7erVEdzY5rz77rtv5s9S49anEaOBuNCA3W5L+/rt3W/mynTomnQEt3JJ+JvrmA3IiNFAXGmgX1FawC+m+YJbzLxjyZIlI/mNdtzTN2I0EFcayMqyXfbS7y4cyZWSURNw6yH+4MYOjokTJ95iLiQ99BX3K271Tx331Qy7guBy0vg8WG/FKoft4IbP4i3IpPztwsLCz3tvNOvxrYEW23iqKPs9VxKfnUlc4fncPSrvtUpYb2lygU+/frc33JZLcs899xSwS3KpxxFSdMVWv4dsle+Qzd0c9xqAGevm/AJVVBSTy+ViCDwhQQN8JLVLc1P7/bz39VXOuTye+/rK5zvNcz+U1ZG4XO5Lb5teWbBs1QHMUCXsWoV4w42yYLUd99133yQ2/dBVSoutbhc5997MmkscS4hOK2hxU1mlb7h9daj3f7r3uq99Yp3GJ67jnqktk5atopVcF3Dr0UnecKNNCu5+/fpNNv42m4PyN8jWdDTW/Rjy8TF22yPLTWeriFyWLQu5mLjeAbD26eaezMHfeQG3SLJaiwRdsK7g5icAJ+kbUjVuc+OziYkpPD0Cdc/nDk0EM9xJFedn2ybyrvizEnatknS4oQIsjlmzZvXMzs6+0MplIgmrAQHcwT0tHZxMYXaGe9QDX03vCW55kaap/vIFt33mzJlXt2VUmcxPYmsAgBfmsWkD4Nz9SbbYvnq1G7yC5Q7hRgYH35W82vjbiQ20d+11wL23JfI6OO1TSIBbXBMArsTbciu4eU4Sc1dSNJREoQKcZ9+DBY+U4F8AIv8GEleJUfrJyiTw2g5uGS0R2hXcPL79mSjVyxwmyhpwwkXJdVNFTeRGUQRwNEXiEkajedkZBF4FblUN/nHr5zDi9p/85Cd92NSzh2YkWTUAwAtgwcWkJXhD+UTKm3VLWh9uhmJYmiNwo5lY7Pw8yXDZaMLk1YAArkZRuOdhaRN5mXSxDdyCZ2FZrUgPKrj55s1QczEpKknuEIB347cRYcGFiEQMUf++3V1DubcEbtVx7Sw3v3UzLLm71LRO14AADqudyJKbaQO3ArdqjQ434naeB3CIsdyJ3M2h110Aj+QoSui16Pwe4DUnm4ZwCYphDhXcGC2Rcxahg0dKkMlIimkAgOdnu6mqzvdTg/Gujqx0G7jFiInFM0iHIME+atSo9MzMzAEqxfyknAbOAc4gMBWJdnGZke4eMOozmengmBcFuA63bdq0aYV4jDDletU02NIAAM/LOge3lZgYEceUMa2FXFWAbcEtKzaen4RHP42kugYE8M6Mg8uFqVh+6FLSfOlVtnU2v+yHcFCRG/xaPOt3KG29evUyN2989UAKpgHwXH4evKZee0A6SD0IsMgucQl9FaFvk7iEgfLr27vnucGvwK38E9lu42FAY7lFGyYkZcGz2YkFLgkg+TkOsdyqtjJaomjnZ7gN3AnQidGsosOhWXDrHZdo1iC4YwHgzEzfbglKsBm4g1NkquUSwGsb4neYEG5MTjp5WG6P0RIeBswxN3BSDd3g2gvAc/irox35wsGV1DW5UK/0DDemNlZeCI5ijQkikT91jY1GjAZ8asAX4AI7Qj3us4BOJOpl6nFfRaU77AI3Ntv00RK+gDBw+1KaSTuvgXOAu6mOZwqRuUcEOuSSuITn9+x8TC9L4hLqpTqdynIjCdbbc+6t1tbWFiQaMRroSAMAnF/MpXoA3lHGKG9rddk8+BWfG9WwNTU11fqaoSjKdTSHSwANAPCsjDYTGQf1xb9IYzPVclWU1UaVdLipoaEBG40YDQSlAR1wuYrzDlGQRZuPUmWb937+1r3L0/fnuQM9+NXhdhu4fWjfJHWoAQtwocwrtyQHC6vX7u1WvctDBkmrb7YBbstTErhVQl1dHb82asRoIDQNAHA8j+frIi+0ksLLzRe5wq/iGaMlQrq7oqLCw6yHdyizdypp4JwFd1MDzz4noyjRbD9OrMpaD8vtlqFAAO4uLy8X8qNZL3OsJNEAf6uGLfg5wKPdJMBdXuMCv4plHF/cElWX06dPV5vREqUK89NJDZwDPPouCv4tTpVTtV5tgVvRvnv37hoDt64eE++MBgB4Bs+hDGvqvaA8pIlIXPIhXdIkjx7KNskvIa4q9xyyA24Py40ViHvlypXVPNbNMzobMRoITwMW4F7FeMOJzZKmxwVa71DPg7hIczNVvba+SdwSJFszTgntbh4xKTHWW1RmwnA0AMDTYcG5kK5ccAXLIyUlfBiLY9Rb3BLEscFVW1tbghUjRgOR0IAFuOaKRKJc7zJqG2wlnObiRTwRBbfQjg0uHg781Fhu1oSRiGkAgKfxuJy3ixGpdVS0qtb9KQeKYQ4V02K5BXA3j5gUI7MRo4FIakAAj2SZelmnKuggr1scYxvg1hNaecTkoLHcUI2RSGtAAI+UxZZyUM89h1wwyviamcWzWG5sVyZ94cKFn/L3CxE3YjQQcQ0AcCfPjAMwIyVMq2vhasenXJ5iWMoVuIV2165duxp4xOSYZDCh0UCkNaADLtY3nLC+yXZs14EmfsPTuqAEz9ZoiQU3p7XW1NQY1wTaMdJlGsC7urDg4Qpc6JoGN/xtuCSw3MKyB9xi0l0nTpzYbPzucNVu9g+kAQE8lDFwlOnh0TDKJ8tsmznZ4pfjHpYb+yABGVq3bNmywbjdUImRrtYAAHeE4IML2HJC4OvIW/e3buB66pZbVbudz41MP/3pT3fziwvmNnxX96wpX2kAgHd2ZtnGFqr68QuO3eCWF59uCQ5iWe7q6uqms2fPbjWuCdRiJBoaEMBDORb4LKugLYwrvmGuw62KEcuNFQtujrccO3bsAwO30pH5iZIGBPBgR05QrWNltk0c4K33gHADcGRq2bBhw3rjd7MmjERVAwAccAcj8LfX7Wxdz3kFbvCLRYleDOIYnOEX9tWca93OnDmznmd+7aFypuiP48jjhMVIdDUQjNdQVecuK7iheSLXrJIXPO7KM6ko46wAl9fMOE0Rj0Q1YsJhC8O9mT+Vfb09Ub8EhFaFKc2taVReYRmDMEszu4eigY4Ad7HZPnyKMAQoVtvjYhLH0eHGusCNHVr27du3euDAgRGF21axm2zH1pLN3Yzjxb3YG89Qel02PzIM3bUXf9jjtSdf4i8def3s4veFW39l+StHHaODjaGW5zd/R8fw08pQy2ppddOuva2rGVPFKrdN4EYzlehuCRKwDuB5Pk/KGzlyZM/169e/z5/vi8gXFwC28x9fZrDh1ieOVNW7cBcscSqcAjWtbXRXf/l/K6/ed6rpDDcXr5fh9jtAtzrKl+XGRtDXzJa77siRI6tHjBhxUyRcE9vhV8lWe4SLTizJR3XZLtRiwMlIzDWAx/qOn6HV+04Rf1iQ4AKAV3Brgc1x6/Y74iIw71hwFjTxqMlKniBTtoUV2lyJS0c+f+GLJzc3EgcaYI+EthyilVwVAAVOhVmP2unj3LJBLLfyZe6///5NVVVVRzty7mXHZA8BeC6PJcF3M0tsdADbzF94OPq9P5OMb4NTsdweCPqDWwCHyW8uKSl5zcB9Tm95fDWSg8FSIzHRAC48SyvoNT64YpNDARvMeogvuJEBZh474axo5ikf/trM784bOacBATzYO2kmH1t5/quLxNLCCK/aSX8Fl7yI1Qav7cQf3DgLLL/7ySefLC4rK9turPd5/QHwbOODn1dIFGKw2uW1tP3/3qZiPpzub7ez2qhOMHDj7GjasWPHSy1qSBG7GYEGlAVnwI3/HR0dtLK53XWCXmLVC9hgE0Y4JLg5v9oBO8L8N82cOfM1nvah1FhvqOa85BoLfl4ZXRiD1a6sp9L7lil/G3CDS79goyr+LDe24Wyw/G5+9axh+/btf4jUsCAOkCwigEfCpzRl+PbN8ZDUzhP0h5oadbNG97d9Wm2wFQhuAVxZ729+85t/raysPGmsd/vTEoBn8dRhRiKvAWW1G+jk7OXqQlKstt9REqlBR3AjD8w+CgHcjUePHq3duXPnImO9WRs+xFhw31Y33H8jWO29J2jR0Qr1QSc8+QcewSX49CuB4IblRgHqopLDxm9/+9sr+E2dMmO9fesUY+DGgvvWTWdSYbVrmqjsxytpBfjjRS4mO/S3caxAcCOPWG8FOD9vUrVnz54XjfWGanyLAG5GUcIfRcFzJPtP0ov7jhPe6RWwA1pt9EwwcIv1Vn4379M4Z86cl9h6VxrrDRX6FgW4GQf3rZwgU2G1qxup8ud/V8N/YrXBYUCrjUMEAzfyifVWvvfGjRvLN2/e/LS5awnV+Bfc5MnCOHiE7s6lWjktTN22Unp6awmVs5aD9rWlR4KF29t6N8yYMWMFT96z07xnKar0HQLwTDOK4ls5HaTCHTlVTTvvWqR8bTyrLaMkQVltFB0s3MjrYb358yL1y5Ytm8vzm7iMewL1+BcB3PjgwfnguMPC85G4Xt5Gc3nShnrWbMhWG70RCtztrPfDDz/88f79+/9sLi79gy1bBHBZN6F/DeA2+4Ez9OdfvE4fc65OWW2UHgrcyC/WG38ROJsavv/97/+Gb8ufNdYb6ulY4H/DRUk13zmU9kKDlY109sd/pd9wFGDLhWRQIyTYXyRUuGG9cRAMC+Kg9e+9914ZX2D+mt0UXjUSSAMAPMP75b5AO6XQ9iama0sJ/XrjQSrjZotLAt7AHfgLWkKFGwUL4GrkhNfr+eJyJd+93Gbck+D0LoCHYtFSIS/uRJ6oom23v6BeIROwwVnIYKMnOgs33BPrriXHG+bNm/cIv45WY0ZPoNbAIoAHznk+By5IIXJhKnGV2JYuaXpe2R4o1PfR4/720/PocX/5O0rH6EhlA9U88096hPPp7gg4A28hWW0cqzNwYz/xvS3r/dxzzx1cvXr1L3j0hOfZCLkeKDPlBP43XBSAEcwCBQlEelz21dP0uGwPFOr76HF/++l59Li//P7S20ZHaO0++sXv31cfbvK22uAtZOks3DgQDijWG2da/V133fXm3r17l5ubO1BPcALA01PcB29mp4OnaVj+jcX0JmsNYMsIiVjt4JTplSscuGGeAbhYb1So7pZbbvnV8ePH9xr/20vTHaxaFpxNWyr41nob4WefrKG9dy6kX4EfXsQlAVfgq9NuQAS+SsKHPy829rt5gquWDydMmPCVjIyMdMzaKWI/8S5hMdJeA+r7MNyN6GwR0Zy/v/NIp+O4ckw9LsfR0/S4bA81xAx1VQ1U+8s19K1/7qVjXGYtL7Dc8oCUpg0cMTQJx3LjSDi4t3tSN3/+fPjfjxn/O7TOyGAXJQ2f0ODdsEAkPLfWtb9yLH/HD7Q9lNrhsqyBnY639tJjv39X+dlitQXssKw26hIu3ChDABf3BGdeHfvfq/jR2BVm/BsqCl4E8OD3SMycGM/ec4JW3LuYVnELADa4wb2TsN0RLkNJJOBGQTjLMBbpAfj06dOfLC4uft8ADhUFLwAcF5m6b5pMcVxAlpyl9298jp5krXiDDY7AU9gSKbhREVhwffSkjt+3rLn55pt/WFpa+rEZQQmtrwA3XJRkE4B9pJw+/trz9MPKOjVhvLc7Ao4iIpGGW/xv/L2o0ZMDBw5U8HyDD/HjscUteEDXSNAaEMCTxWrjgaiT1VT84Ev00IFTVMGKELDBiwz7RQzurrYNqqKHDh1q4tvzGyZfmHVNbsUHufz5byNBasDRZn7kvpiuOokj9LXgEJJHj/vKK/kk7Ex+7KOLlIUQdyBP19CJOa/Q/W/soSOchJERwB1RP5vLs6Qr4PZ55vHFZb2r1b1lXNHRa/muXKYB3OqDgBFfgOvg+CtAz6PHuyo/jqEvOA7WYbF5GrSKX71NDyzaQAc4SYb88O+O67SI+dlcliVdATcKB+CyyMFsH+w6WsnTAO+8pD9dx4CnGcBFNYFDAVwfBw+8V+xzAGyeKar+D+vpu798k7ZzjWCtxR3Rh/0iXtmugluvqECO0MbPD5zpmUs7RvamKQx4Rgp/S0rXUVBxAVwpkk1ivPviAPtsPVUv2Uizfvaq+jgToBarDbBhsdGcLpFowo0GqIas3kOnud0fjBlAk/nWc450Wpe0MMkKFV3BB9ddgHiLtzC27GOfmvcWPTj3DfqIuwFQY4ErolvshIab22KdnWiIasyGYqrkZwreu/ICmsivYBVIpyGzkY41IP92cpHZce7ob23icY/jVXToZ3+jB55fR59wDfCNSN1iR3xkxFcro2G55bhyhlqAf3yEanccp7WfG05j+XszRQAcf7VGAmsgHgHHyQawedqzXQ8up/949WNrVEQHO2J3IANpKZpwS10EbhV+eoYaV++lt68dRaPyM2mAAVzUFDgUwJEz1v436oBb6p+W0cZbX6CHNn1KpzkJUMPP1h+GYo/U+ifnaNdJtOHWwZZGus/UUMufPqS114+mfvkZNNxpLHjQPS6Ax9JFwRh2Hdtjnqzy9WnP0k9Ky9QNmpiCDQVGG27pNB1yxF31jeT63Xu07ooLqLx3N7oy3UFO6TjZyYS+NaAPqUbbgquhvkZq5FGwX13/ND3L/YgPnsLH9jXch76OmsQKbjRQQd0WWrAv30r7bXbaOLIPXZHppG7GTQmOBQ/AeZeuHj1B7/HEOXSikg4/vZZmzV5Ba/mwsNbiX8uoiNygQR9HVWIJtzQUjYaLYrkp6w5QOfvhb04aSn3YDx9m3BRRVcehAN7VFCk3hAfz9p6kN29fQD9Y+REd4poJ2GKx9TuPXV0ln4qJNdxotPeiQD9dTc3sprw39gI60zufxsFNkb9cny0xiUoDMtrUFTTBr1e30huo8e399D/TfkPPcj9V8oF1/1qeFRGLHbOeiTXcaLj0g1hvPXSv2EqfuG20bngRXc43fAod/H8rHRgzrcX5gS39tOlKjEI4IcDGmzPHKql43ts0i7/g+y6rQe44yoiIgC19GFNNwTWLF0FdcLJh4cf1Cd/p5Q9SqyW7ex7lLL2X7vjsILq3WyZ/vIBzWZ3ImYy01wDch3AFUOMZbJ5TpH7rYVrAs64uPVvtYan1N2hgrbGIwQr38GHtHw+WW28AlCKLnP0IW+ubyLX0A9pxqIzWXNib+vLk7oPlYtNArqvwfDwcvYgLUsu2eP9pemfOSvrBwyvpHe4HuCAyGgKwvZ/siwuwoYV4styojwieYsaCGT14dj1lxfl7YZYlz3j2drr6Xy6m2T3zqD+PqpAZNmTt+BGAiiVYgcWHC8L3H46+toOeenAZvc/7wuUAzAI01vVnRCLwP8ElRlDiFW40EXUTwAVyAG4tA3tR3oI76J4xA+mOvHTKgKtiIIfq2kswcANquCDVTdTIj0YsvXcpLSo9rcatYZ31RaDmU8Aa5Wp/0BinxDPcUA3qhwXuEwCHLw5LbgGO+MyraNCDk+nuEb1pOrsr6TyyYiBnxXiLP8ABNW6dswvStP8UrXrmPVr8x410mPfXgUYcUGOID1CLbx3CfwLvFUWJd7hFFagnrLhALq4KLjoF9IyvXkb9Zk+lmSOK6Cv8XfZMfl7cQC4a9BECatyIqW6gBob6b0+toT/+5SM1OQ5cDgEbcd0FAdRwQeIWaq6bkkSBG5VFXQVyWHFxVQC4QK7iU4ZTr4e/RLeN7kdfzcugXAU57xnOBRYfIykE1htv8yioG6lm1zH6y3/9nZat/UQ96CQgA2yJ+3JB4h5sdFYiwS1wCeDe/jjAFosOa57+2c9Q4X9/mW4Z3Zdm8J3OQszJJ3c7Uwl0AI0Fkw80sFPBU5iV7zpOL//0VVqx9VP1pTAALEAjrltq8asTwlpz3S1JRLil8gI5XBUs8MdlfFwgV8AX5lHW4zfSxCsG0vUDu9NEfnY8Xax5Ml+Awu0QK13bRE2lZ2n9llJ6g4f11pdXW4+h6hYacfjUcus8YVwQrnM7SWS40RjUXyAXn1wgB+ACucTTxw2mbj+cRlNH9aZp/QroYobcpmZ3QkFcUiJbdLHQ8Bnw0gC7Hu5jFbRzDz8Dwi/nrtlUom6VwzLLIhYa6zrUsNJiqRPCBeH6tpNEh1sa5AtyfXQFcAN6gVydAHdNoAF3j6PpQ3rQlIIcGoxRFoCubg5x5niHXYcZz3wAaIx6VNRSSXEZrV28iVYt2UBHuCkCrkCNdT0O10OsdMJDzW1Rkixw6+0R0MVd0V0WuQgV0MXKO798CRV9bRxdMbQHje3TjS7n0Za+cF0wdq7DjgPFwroDZIgCmkPAjDHpttGO4/zo6YcHy2jbnzbRlld30CnOAmB1qAVoPR1Ay4IjyMLRxJdkg1vvEbQNroq4KwI7ABeovUNsQz7nzHHU78YxdMWQnjSWn0q8LDuNemIObVyQAnbrwpQzQ3Tg9fi5rYF/BV7klLgijX9wIQiYEeKtcn7r5czJKvqo+AxtW7mdtvxxkxq+E+urwytwSyh5BGgu0XI/AlcywXIkM9zSFWLJBXSEFsQcB+BYF+glriBvS7dPGU6FUy+iwcOKaFBRPg3qlkUD89NpIFv4fmzdnQAez1OLK6POLK91bBPLq0IGFxd8AjHSsN5mlVt4/PlYVROV8qQ2paeq6DDPr3d4zW4q4WE7fAsdYAJWAVbiANk7DpiRJjAjVIflMGkFfZAqgrbqC+AF6Ahl8QU20mS7vo86WXIzyfm1K6jfZQNpQEEm5fP0w1lZaZTNw47Z7L9ns4XPZl8+i61+NsOdwQA3svWtY9+4ni1xHfvJdTw8V1fPS2Mz1Vc0UNVHpXTkT1voWE2DB5AAFFCK1RVgBWR9Xc8j+wjMEnJRyS2pBLfekzrkiCtQOdThFaB9wS0nhYRShne5so5jIy4CwCACmncolhWQCpwIJS7wAmyJ+8rrXS5nTx3RFZ46rfZsqQCohwK7Hgr4HaXpZUgcR9PjWBfo9LikIRSQBWZ93V8a0vUyJI5jpKRA6UY8NSAgeoeAGmmBQu/9UDrSvAXwQQRCPRRQA4X6PhI/V6r59al0oxZPDQisSPUVlzQ9lLx6iLi3AEiIHgqk3qHk886rCjA/7TWADjESugZ0vUncO5RSJV3W9VBAlTRZ9w6xXdIkrwkDaKAjxQfY1WwOoIFQdGvADaDMzmz+f6SMYEX4z7hMAAAAAElFTkSuQmCC'};return{FaviconsByHue,};});'use strict';Polymer({is:'tr-ui-b-info-bar-group',ready(){this.messages_=[];},get messageCount(){return this.messages_.length;},clearMessages(){this.messages_=[];this.updateContents_();},addMessage(text,opt_buttons){opt_buttons=opt_buttons||[];for(let i=0;i<opt_buttons.length;i++){if(opt_buttons[i].buttonText===undefined){throw new Error('buttonText must be provided');}
+if(opt_buttons[i].onClick===undefined){throw new Error('onClick must be provided');}}
+this.messages_.push({text,buttons:opt_buttons||[]});this.updateContents_();},updateContents_(){Polymer.dom(this.$.messages).textContent='';this.messages_.forEach(function(message){const bar=document.createElement('tr-ui-b-info-bar');bar.message=message.text;bar.visible=true;message.buttons.forEach(function(button){bar.addButton(button.buttonText,button.onClick);},this);Polymer.dom(this.$.messages).appendChild(bar);},this);}});'use strict';Polymer({is:'tr-ui-b-toolbar-button'});'use strict';tr.exportTo('tr.ui',function(){const Task=tr.b.Task;function FindController(brushingStateController){this.brushingStateController_=brushingStateController;this.filterHits_=[];this.currentHitIndex_=-1;this.activePromise_=Promise.resolve();this.activeTask_=undefined;}
+FindController.prototype={__proto__:Object.prototype,get model(){return this.brushingStateController_.model;},get brushingStateController(){return this.brushingStateController_;},enqueueOperation_(operation){let task;if(operation instanceof tr.b.Task){task=operation;}else{task=new tr.b.Task(operation,this);}
+if(this.activeTask_){this.activeTask_=this.activeTask_.enqueue(task);}else{this.activeTask_=task;this.activePromise_=Task.RunWhenIdle(this.activeTask_);this.activePromise_.then(function(){this.activePromise_=undefined;this.activeTask_=undefined;}.bind(this));}},startFiltering(filterText){const sc=this.brushingStateController_;if(!sc)return;this.enqueueOperation_(function(){this.filterHits_=[];this.currentHitIndex_=-1;}.bind(this));let stateFromString;try{stateFromString=sc.uiStateFromString(filterText);}catch(e){this.enqueueOperation_(function(){const overlay=new tr.ui.b.Overlay();Polymer.dom(overlay).textContent=e.message;overlay.title='UI State Navigation Error';overlay.visible=true;});return this.activePromise_;}
+if(stateFromString!==undefined){this.enqueueOperation_(sc.navToPosition.bind(this,stateFromString,true));}else{if(filterText.length===0){this.enqueueOperation_(sc.findTextCleared.bind(sc));}else{const filter=new tr.c.FullTextFilter(filterText);const filterHitSet=new tr.model.EventSet();this.enqueueOperation_(sc.addAllEventsMatchingFilterToSelectionAsTask(filter,filterHitSet));this.enqueueOperation_(function(){this.filterHits_=filterHitSet.toArray();sc.findTextChangedTo(filterHitSet);}.bind(this));}}
+return this.activePromise_;},get filterHits(){return this.filterHits_;},get currentHitIndex(){return this.currentHitIndex_;},find_(dir){const firstHit=this.currentHitIndex_===-1;if(firstHit&&dir<0){this.currentHitIndex_=0;}
+const N=this.filterHits.length;this.currentHitIndex_=(this.currentHitIndex_+dir+N)%N;if(!this.brushingStateController_)return;this.brushingStateController_.findFocusChangedTo(new tr.model.EventSet(this.filterHits[this.currentHitIndex]));},findNext(){this.find_(1);},findPrevious(){this.find_(-1);}};return{FindController,};});'use strict';tr.exportTo('tr.ui.b',function(){function TimingTool(viewport,targetElement){this.viewport_=viewport;this.onMouseMove_=this.onMouseMove_.bind(this);this.onDblClick_=this.onDblClick_.bind(this);this.targetElement_=targetElement;this.isMovingLeftEdge_=false;}
+TimingTool.prototype={onEnterTiming(e){this.targetElement_.addEventListener('mousemove',this.onMouseMove_);this.targetElement_.addEventListener('dblclick',this.onDblClick_);},onBeginTiming(e){if(!this.isTouchPointInsideTrackBounds_(e.clientX,e.clientY)){return;}
+const pt=this.getSnappedToEventPosition_(e);this.mouseDownAt_(pt.x,pt.y);this.updateSnapIndicators_(pt);},updateSnapIndicators_(pt){if(!pt.snapped)return;const ir=this.viewport_.interestRange;if(ir.min===pt.x){ir.leftSnapIndicator=new tr.ui.SnapIndicator(pt.y,pt.height);}
+if(ir.max===pt.x){ir.rightSnapIndicator=new tr.ui.SnapIndicator(pt.y,pt.height);}},onUpdateTiming(e){const pt=this.getSnappedToEventPosition_(e);this.mouseMoveAt_(pt.x,pt.y,true);this.updateSnapIndicators_(pt);},onEndTiming(e){this.mouseUp_();},onExitTiming(e){this.targetElement_.removeEventListener('mousemove',this.onMouseMove_);this.targetElement_.removeEventListener('dblclick',this.onDblClick_);},onMouseMove_(e){if(e.button)return;const worldX=this.getWorldXFromEvent_(e);this.mouseMoveAt_(worldX,e.clientY,false);},onDblClick_(e){},isTouchPointInsideTrackBounds_(clientX,clientY){if(!this.viewport_||!this.viewport_.modelTrackContainer||!this.viewport_.modelTrackContainer.canvas){return false;}
+const canvas=this.viewport_.modelTrackContainer.canvas;const canvasRect=canvas.getBoundingClientRect();if(clientX>=canvasRect.left&&clientX<=canvasRect.right&&clientY>=canvasRect.top&&clientY<=canvasRect.bottom){return true;}
+return false;},mouseDownAt_(worldX,y){const ir=this.viewport_.interestRange;const dt=this.viewport_.currentDisplayTransform;const pixelRatio=window.devicePixelRatio||1;const nearnessThresholdWorld=dt.xViewVectorToWorld(6*pixelRatio);if(ir.isEmpty){ir.setMinAndMax(worldX,worldX);ir.rightSelected=true;this.isMovingLeftEdge_=false;return;}
+if(Math.abs(worldX-ir.min)<nearnessThresholdWorld){ir.leftSelected=true;ir.min=worldX;this.isMovingLeftEdge_=true;return;}
+if(Math.abs(worldX-ir.max)<nearnessThresholdWorld){ir.rightSelected=true;ir.max=worldX;this.isMovingLeftEdge_=false;return;}
+ir.setMinAndMax(worldX,worldX);ir.rightSelected=true;this.isMovingLeftEdge_=false;},mouseMoveAt_(worldX,y,mouseDown){if(mouseDown){this.updateMovingEdge_(worldX);return;}
+const ir=this.viewport_.interestRange;const dt=this.viewport_.currentDisplayTransform;const pixelRatio=window.devicePixelRatio||1;const nearnessThresholdWorld=dt.xViewVectorToWorld(6*pixelRatio);if(Math.abs(worldX-ir.min)<nearnessThresholdWorld){ir.leftSelected=true;ir.rightSelected=false;return;}
+if(Math.abs(worldX-ir.max)<nearnessThresholdWorld){ir.leftSelected=false;ir.rightSelected=true;return;}
+ir.leftSelected=false;ir.rightSelected=false;return;},updateMovingEdge_(newWorldX){const ir=this.viewport_.interestRange;let a=ir.min;let b=ir.max;if(this.isMovingLeftEdge_){a=newWorldX;}else{b=newWorldX;}
+if(a<=b){ir.setMinAndMax(a,b);}else{ir.setMinAndMax(b,a);}
+if(ir.min===newWorldX){this.isMovingLeftEdge_=true;ir.leftSelected=true;ir.rightSelected=false;}else{this.isMovingLeftEdge_=false;ir.leftSelected=false;ir.rightSelected=true;}},mouseUp_(){const dt=this.viewport_.currentDisplayTransform;const ir=this.viewport_.interestRange;ir.leftSelected=false;ir.rightSelected=false;const pixelRatio=window.devicePixelRatio||1;const minWidthValue=dt.xViewVectorToWorld(2*pixelRatio);if(ir.range<minWidthValue){ir.reset();}},getWorldXFromEvent_(e){const pixelRatio=window.devicePixelRatio||1;const canvas=this.viewport_.modelTrackContainer.canvas;const worldOffset=canvas.getBoundingClientRect().left;const viewX=(e.clientX-worldOffset)*pixelRatio;return this.viewport_.currentDisplayTransform.xViewToWorld(viewX);},getSnappedToEventPosition_(e){const pixelRatio=window.devicePixelRatio||1;const EVENT_SNAP_RANGE=16*pixelRatio;const modelTrackContainer=this.viewport_.modelTrackContainer;const modelTrackContainerRect=modelTrackContainer.getBoundingClientRect();const viewport=this.viewport_;const dt=viewport.currentDisplayTransform;const worldMaxDist=dt.xViewVectorToWorld(EVENT_SNAP_RANGE);const worldX=this.getWorldXFromEvent_(e);const mouseY=e.clientY;const selection=new tr.model.EventSet();modelTrackContainer.addClosestEventToSelection(worldX,worldMaxDist,mouseY,mouseY,selection);if(!selection.length){modelTrackContainer.addClosestEventToSelection(worldX,worldMaxDist,modelTrackContainerRect.top,modelTrackContainerRect.bottom,selection);}
+let minDistX=worldMaxDist;let minDistY=Infinity;const pixWidth=dt.xViewVectorToWorld(1);const result={x:worldX,y:mouseY-modelTrackContainerRect.top,height:0,snapped:false};const eventBounds=new tr.b.math.Range();for(const event of selection){const track=viewport.trackForEvent(event);const trackRect=track.getBoundingClientRect();eventBounds.reset();event.addBoundsToRange(eventBounds);let eventX;if(Math.abs(eventBounds.min-worldX)<Math.abs(eventBounds.max-worldX)){eventX=eventBounds.min;}else{eventX=eventBounds.max;}
+const distX=eventX-worldX;const eventY=trackRect.top;const eventHeight=trackRect.height;const distY=Math.abs(eventY+eventHeight/2-mouseY);if((distX<=minDistX||Math.abs(distX-minDistX)<pixWidth)&&distY<minDistY){minDistX=distX;minDistY=distY;result.x=eventX;result.y=eventY+
+modelTrackContainer.scrollTop-modelTrackContainerRect.top;result.height=eventHeight;result.snapped=true;}}
+return result;}};return{TimingTool,};});'use strict';tr.exportTo('tr.ui',function(){const kDefaultPanAnimationDurationMs=100.0;const lerp=tr.b.math.lerp;function TimelineDisplayTransformPanAnimation(deltaX,deltaY,opt_durationMs){this.deltaX=deltaX;this.deltaY=deltaY;if(opt_durationMs===undefined){this.durationMs=kDefaultPanAnimationDurationMs;}else{this.durationMs=opt_durationMs;}
+this.startPanX=undefined;this.startPanY=undefined;this.startTimeMs=undefined;}
+TimelineDisplayTransformPanAnimation.prototype={__proto__:tr.ui.b.Animation.prototype,get affectsPanY(){return this.deltaY!==0;},canTakeOverFor(existingAnimation){return existingAnimation instanceof TimelineDisplayTransformPanAnimation;},takeOverFor(existing,timestamp,target){const remainingDeltaXOnExisting=existing.goalPanX-target.panX;const remainingDeltaYOnExisting=existing.goalPanY-target.panY;let remainingTimeOnExisting=timestamp-(existing.startTimeMs+existing.durationMs);remainingTimeOnExisting=Math.max(remainingTimeOnExisting,0);this.deltaX+=remainingDeltaXOnExisting;this.deltaY+=remainingDeltaYOnExisting;this.durationMs+=remainingTimeOnExisting;},start(timestamp,target){this.startTimeMs=timestamp;this.startPanX=target.panX;this.startPanY=target.panY;},tick(timestamp,target){let percentDone=(timestamp-this.startTimeMs)/this.durationMs;percentDone=tr.b.math.clamp(percentDone,0,1);target.panX=lerp(percentDone,this.startPanX,this.goalPanX);if(this.affectsPanY){target.panY=lerp(percentDone,this.startPanY,this.goalPanY);}
+return timestamp>=this.startTimeMs+this.durationMs;},get goalPanX(){return this.startPanX+this.deltaX;},get goalPanY(){return this.startPanY+this.deltaY;}};function TimelineDisplayTransformZoomToAnimation(goalFocalPointXWorld,goalFocalPointXView,goalFocalPointY,zoomInRatioX,opt_durationMs){this.goalFocalPointXWorld=goalFocalPointXWorld;this.goalFocalPointXView=goalFocalPointXView;this.goalFocalPointY=goalFocalPointY;this.zoomInRatioX=zoomInRatioX;if(opt_durationMs===undefined){this.durationMs=kDefaultPanAnimationDurationMs;}else{this.durationMs=opt_durationMs;}
+this.startTimeMs=undefined;this.startScaleX=undefined;this.goalScaleX=undefined;this.startPanY=undefined;}
+TimelineDisplayTransformZoomToAnimation.prototype={__proto__:tr.ui.b.Animation.prototype,get affectsPanY(){return this.startPanY!==this.goalFocalPointY;},canTakeOverFor(existingAnimation){return false;},takeOverFor(existingAnimation,timestamp,target){this.goalScaleX=target.scaleX*this.zoomInRatioX;},start(timestamp,target){this.startTimeMs=timestamp;this.startScaleX=target.scaleX;this.goalScaleX=this.zoomInRatioX*target.scaleX;this.startPanY=target.panY;},tick(timestamp,target){let percentDone=(timestamp-this.startTimeMs)/this.durationMs;percentDone=tr.b.math.clamp(percentDone,0,1);target.scaleX=lerp(percentDone,this.startScaleX,this.goalScaleX);if(this.affectsPanY){target.panY=lerp(percentDone,this.startPanY,this.goalFocalPointY);}
+target.xPanWorldPosToViewPos(this.goalFocalPointXWorld,this.goalFocalPointXView);return timestamp>=this.startTimeMs+this.durationMs;}};return{TimelineDisplayTransformPanAnimation,TimelineDisplayTransformZoomToAnimation,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const DrawType={GENERAL_EVENT:1,INSTANT_EVENT:2,BACKGROUND:3,GRID:4,FLOW_ARROWS:5,MARKERS:6,HIGHLIGHTS:7,ANNOTATIONS:8};const MAX_OVERSIZE_MULTIPLE=3.0;const REDRAW_SLOP=(MAX_OVERSIZE_MULTIPLE-1)/2;const DrawingContainer=tr.ui.b.define('drawing-container',tr.ui.tracks.Track);DrawingContainer.prototype={__proto__:tr.ui.tracks.Track.prototype,decorate(viewport){tr.ui.tracks.Track.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('drawing-container');this.canvas_=document.createElement('canvas');this.canvas_.className='drawing-container-canvas';this.canvas_.style.left=tr.ui.b.constants.HEADING_WIDTH+'px';Polymer.dom(this).appendChild(this.canvas_);this.ctx_=this.canvas_.getContext('2d');this.offsetY_=0;this.viewportChange_=this.viewportChange_.bind(this);this.viewport.addEventListener('change',this.viewportChange_);window.addEventListener('resize',this.windowResized_.bind(this));this.addEventListener('scroll',this.scrollChanged_.bind(this));},get canvas(){return this.canvas_;},context(){return this.ctx_;},viewportChange_(){this.invalidate();},windowResized_(){this.invalidate();},scrollChanged_(){if(this.updateOffsetY_()){this.invalidate();}},invalidate(){if(this.rafPending_)return;this.rafPending_=true;tr.b.requestPreAnimationFrame(this.preDraw_,this);},preDraw_(){this.rafPending_=false;this.updateCanvasSizeIfNeeded_();tr.b.requestAnimationFrameInThisFrameIfPossible(this.draw_,this);},draw_(){this.ctx_.clearRect(0,0,this.canvas_.width,this.canvas_.height);const typesToDraw=[DrawType.BACKGROUND,DrawType.HIGHLIGHTS,DrawType.GRID,DrawType.INSTANT_EVENT,DrawType.GENERAL_EVENT,DrawType.MARKERS,DrawType.ANNOTATIONS,DrawType.FLOW_ARROWS];const children=this.children;for(const idx in typesToDraw){for(let i=0;i<children.length;++i){if(!(children[i]instanceof tr.ui.tracks.Track)){continue;}
+children[i].drawTrack(typesToDraw[idx]);}}
+const pixelRatio=window.devicePixelRatio||1;const bounds=this.canvas_.getBoundingClientRect();const dt=this.viewport.currentDisplayTransform;const viewLWorld=dt.xViewToWorld(0);const viewRWorld=dt.xViewToWorld(bounds.width*pixelRatio);const viewHeight=bounds.height*pixelRatio;this.viewport.drawGridLines(this.ctx_,viewLWorld,viewRWorld,viewHeight);},updateOffsetY_(){const maxYDelta=window.innerHeight*REDRAW_SLOP;let newOffset=this.scrollTop-maxYDelta;if(Math.abs(newOffset-this.offsetY_)<=maxYDelta)return false;const maxOffset=this.scrollHeight-
+this.canvas_.getBoundingClientRect().height;newOffset=Math.max(0,Math.min(newOffset,maxOffset));if(newOffset!==this.offsetY_){this.offsetY_=newOffset;return true;}
+return false;},updateCanvasSizeIfNeeded_(){const visibleChildTracks=Array.from(this.children).filter(this.visibleFilter_);if(visibleChildTracks.length===0){return;}
+const thisBounds=this.getBoundingClientRect();const firstChildTrackBounds=visibleChildTracks[0].getBoundingClientRect();const lastChildTrackBounds=visibleChildTracks[visibleChildTracks.length-1].getBoundingClientRect();const innerWidth=firstChildTrackBounds.width-
+tr.ui.b.constants.HEADING_WIDTH;const innerHeight=Math.min(lastChildTrackBounds.bottom-firstChildTrackBounds.top,Math.floor(window.innerHeight*MAX_OVERSIZE_MULTIPLE));const pixelRatio=window.devicePixelRatio||1;if(this.canvas_.width!==innerWidth*pixelRatio){this.canvas_.width=innerWidth*pixelRatio;this.canvas_.style.width=innerWidth+'px';}
+if(this.canvas_.height!==innerHeight*pixelRatio){this.canvas_.height=innerHeight*pixelRatio;this.canvas_.style.height=innerHeight+'px';}
+if(this.canvas_.top!==this.offsetY_){this.canvas_.top=this.offsetY_;this.canvas_.style.top=this.offsetY_+'px';}},visibleFilter_(element){if(!(element instanceof tr.ui.tracks.Track))return false;return window.getComputedStyle(element).display!=='none';},addClosestEventToSelection(worldX,worldMaxDist,loY,hiY,selection){const children=this.children;for(let i=0;i<children.length;++i){if(!(children[i]instanceof tr.ui.tracks.Track)){continue;}
+const trackClientRect=children[i].getBoundingClientRect();const a=Math.max(loY,trackClientRect.top);const b=Math.min(hiY,trackClientRect.bottom);if(a<=b){children[i].addClosestEventToSelection(worldX,worldMaxDist,loY,hiY,selection);}}
+tr.ui.tracks.Track.prototype.addClosestEventToSelection.apply(this,arguments);},addEventsToTrackMap(eventToTrackMap){const children=this.children;for(let i=0;i<children.length;++i){if(!(children[i]instanceof tr.ui.tracks.Track)){continue;}
+children[i].addEventsToTrackMap(eventToTrackMap);}}};return{DrawingContainer,DrawType,};});'use strict';tr.exportTo('tr.model',function(){const SelectableItem=tr.model.SelectableItem;const SelectionState=tr.model.SelectionState;function ProxySelectableItem(modelItem){SelectableItem.call(this,modelItem);}
+ProxySelectableItem.prototype={__proto__:SelectableItem.prototype,get selectionState(){const modelItem=this.modelItem_;if(modelItem===undefined){return SelectionState.NONE;}
+return modelItem.selectionState;}};return{ProxySelectableItem,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const EventPresenter=tr.ui.b.EventPresenter;const SelectionState=tr.model.SelectionState;const LetterDotTrack=tr.ui.b.define('letter-dot-track',tr.ui.tracks.Track);LetterDotTrack.prototype={__proto__:tr.ui.tracks.Track.prototype,decorate(viewport){tr.ui.tracks.Track.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('letter-dot-track');this.items_=undefined;this.heading_=document.createElement('tr-ui-b-heading');Polymer.dom(this).appendChild(this.heading_);},set heading(heading){this.heading_.heading=heading;},get heading(){return this.heading_.heading;},set tooltip(tooltip){this.heading_.tooltip=tooltip;},get items(){return this.items_;},set items(items){this.items_=items;this.invalidateDrawingContainer();},get height(){return window.getComputedStyle(this).height;},set height(height){this.style.height=height;},get dumpRadiusView(){return 7*(window.devicePixelRatio||1);},draw(type,viewLWorld,viewRWorld,viewHeight){if(this.items_===undefined)return;switch(type){case tr.ui.tracks.DrawType.GENERAL_EVENT:this.drawLetterDots_(viewLWorld,viewRWorld);break;}},drawLetterDots_(viewLWorld,viewRWorld){const ctx=this.context();const pixelRatio=window.devicePixelRatio||1;const bounds=this.getBoundingClientRect();const height=bounds.height*pixelRatio;const halfHeight=height*0.5;const twoPi=Math.PI*2;const dt=this.viewport.currentDisplayTransform;const dumpRadiusView=this.dumpRadiusView;const itemRadiusWorld=dt.xViewVectorToWorld(height);const items=this.items_;const loI=tr.b.findLowIndexInSortedArray(items,function(item){return item.start;},viewLWorld);const oldFont=ctx.font;ctx.font='400 '+Math.floor(9*pixelRatio)+'px Arial';ctx.strokeStyle='rgb(0,0,0)';ctx.textBaseline='middle';ctx.textAlign='center';const drawItems=function(selected){for(let i=loI;i<items.length;++i){const item=items[i];const x=item.start;if(x-itemRadiusWorld>viewRWorld)break;if(item.selected!==selected)continue;const xView=dt.xWorldToView(x);ctx.fillStyle=EventPresenter.getSelectableItemColorAsString(item);ctx.beginPath();ctx.arc(xView,halfHeight,dumpRadiusView+0.5,0,twoPi);ctx.fill();if(item.selected){ctx.lineWidth=3;ctx.strokeStyle='rgb(100,100,0)';ctx.stroke();ctx.beginPath();ctx.arc(xView,halfHeight,dumpRadiusView,0,twoPi);ctx.lineWidth=1.5;ctx.strokeStyle='rgb(255,255,0)';ctx.stroke();}else{ctx.lineWidth=1;ctx.strokeStyle='rgb(0,0,0)';ctx.stroke();}
+ctx.fillStyle='rgb(255, 255, 255)';ctx.fillText(item.dotLetter,xView,halfHeight);}};drawItems(false);drawItems(true);ctx.lineWidth=1;ctx.font=oldFont;},addEventsToTrackMap(eventToTrackMap){if(this.items_===undefined)return;this.items_.forEach(function(item){item.addToTrackMap(eventToTrackMap,this);},this);},addIntersectingEventsInRangeToSelectionInWorldSpace(loWX,hiWX,viewPixWidthWorld,selection){if(this.items_===undefined)return;const itemRadiusWorld=viewPixWidthWorld*this.dumpRadiusView;tr.b.iterateOverIntersectingIntervals(this.items_,function(x){return x.start-itemRadiusWorld;},function(x){return 2*itemRadiusWorld;},loWX,hiWX,function(item){item.addToSelection(selection);}.bind(this));},addEventNearToProvidedEventToSelection(event,offset,selection){if(this.items_===undefined)return;const index=this.items_.findIndex(item=>item.modelItem===event);if(index===-1)return false;const newIndex=index+offset;if(newIndex>=0&&newIndex<this.items_.length){this.items_[newIndex].addToSelection(selection);return true;}
+return false;},addAllEventsMatchingFilterToSelection(filter,selection){},addClosestEventToSelection(worldX,worldMaxDist,loY,hiY,selection){if(this.items_===undefined)return;const item=tr.b.findClosestElementInSortedArray(this.items_,function(x){return x.start;},worldX,worldMaxDist);if(!item)return;item.addToSelection(selection);}};function LetterDot(modelItem,dotLetter,colorId,start){tr.model.ProxySelectableItem.call(this,modelItem);this.dotLetter=dotLetter;this.colorId=colorId;this.start=start;}
+LetterDot.prototype={__proto__:tr.model.ProxySelectableItem.prototype};return{LetterDotTrack,LetterDot,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const AlertTrack=tr.ui.b.define('alert-track',tr.ui.tracks.LetterDotTrack);AlertTrack.prototype={__proto__:tr.ui.tracks.LetterDotTrack.prototype,decorate(viewport){tr.ui.tracks.LetterDotTrack.prototype.decorate.call(this,viewport);this.heading='Alerts';this.alerts_=undefined;},get alerts(){return this.alerts_;},set alerts(alerts){this.alerts_=alerts;if(alerts===undefined){this.items=undefined;return;}
+this.items=this.alerts_.map(function(alert){return new tr.ui.tracks.LetterDot(alert,String.fromCharCode(9888),alert.colorId,alert.start);});}};return{AlertTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const Task=tr.b.Task;const ContainerTrack=tr.ui.b.define('container-track',tr.ui.tracks.Track);ContainerTrack.prototype={__proto__:tr.ui.tracks.Track.prototype,decorate(viewport){tr.ui.tracks.Track.prototype.decorate.call(this,viewport);},detach(){Polymer.dom(this).textContent='';},get tracks_(){const tracks=[];const children=this.children;for(let i=0;i<children.length;i++){if(children[i]instanceof tr.ui.tracks.Track){tracks.push(children[i]);}}
+return tracks;},drawTrack(type){this.tracks_.forEach(function(track){track.drawTrack(type);});},addIntersectingEventsInRangeToSelection(loVX,hiVX,loY,hiY,selection){const tracks=this.tracks_;for(let i=0;i<tracks.length;i++){const trackClientRect=tracks[i].getBoundingClientRect();const a=Math.max(loY,trackClientRect.top);const b=Math.min(hiY,trackClientRect.bottom);if(a<=b){tracks[i].addIntersectingEventsInRangeToSelection(loVX,hiVX,loY,hiY,selection);}}
+tr.ui.tracks.Track.prototype.addIntersectingEventsInRangeToSelection.apply(this,arguments);},addEventsToTrackMap(eventToTrackMap){for(const track of this.tracks_){track.addEventsToTrackMap(eventToTrackMap);}},addAllEventsMatchingFilterToSelection(filter,selection){const tracks=this.tracks_;for(let i=0;i<tracks.length;i++){tracks[i].addAllEventsMatchingFilterToSelection(filter,selection);}},addAllEventsMatchingFilterToSelectionAsTask(filter,selection){const task=new Task();const tracks=this.tracks_;for(let i=0;i<tracks.length;i++){task.subTask(function(i){return function(){tracks[i].addAllEventsMatchingFilterToSelection(filter,selection);};}(i),this);}
+return task;},addClosestEventToSelection(worldX,worldMaxDist,loY,hiY,selection){const tracks=this.tracks_;for(let i=0;i<tracks.length;i++){const trackClientRect=tracks[i].getBoundingClientRect();const a=Math.max(loY,trackClientRect.top);const b=Math.min(hiY,trackClientRect.bottom);if(a<=b){tracks[i].addClosestEventToSelection(worldX,worldMaxDist,loY,hiY,selection);}}
+tr.ui.tracks.Track.prototype.addClosestEventToSelection.apply(this,arguments);},addContainersToTrackMap(containerToTrackMap){this.tracks_.forEach(function(track){track.addContainersToTrackMap(containerToTrackMap);});},clearTracks_(){this.tracks_.forEach(function(track){Polymer.dom(this).removeChild(track);},this);}};return{ContainerTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){function ChartPoint(modelItem,x,y,opt_yBase){tr.model.ProxySelectableItem.call(this,modelItem);this.x=x;this.y=y;this.dotLetter=undefined;this.yBase=opt_yBase;}
+ChartPoint.prototype={__proto__:tr.model.ProxySelectableItem.prototype,};return{ChartPoint,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const ColorScheme=tr.b.ColorScheme;const EventPresenter=tr.ui.b.EventPresenter;const SelectionState=tr.model.SelectionState;const ChartSeriesType={LINE:0,AREA:1};const DEFAULT_RENDERING_CONFIG={chartType:ChartSeriesType.LINE,selectedPointSize:4,unselectedPointSize:3,solidSelectedDots:false,colorId:0,lineWidth:1,skipDistance:1,unselectedPointDensityTransparent:0.10,unselectedPointDensityOpaque:0.05,backgroundOpacity:0.5,stepGraph:true};const LAST_POINT_WIDTH=16;const DOT_LETTER_RADIUS_PX=7;const DOT_LETTER_RADIUS_PADDING_PX=0.5;const DOT_LETTER_SELECTED_OUTLINE_WIDTH_PX=3;const DOT_LETTER_SELECTED_OUTLINE_DETAIL_WIDTH_PX=1.5;const DOT_LETTER_UNSELECTED_OUTLINE_WIDTH_PX=1;const DOT_LETTER_FONT_WEIGHT=400;const DOT_LETTER_FONT_SIZE_PX=9;const DOT_LETTER_FONT='Arial';const ChartSeriesComponent={BACKGROUND:0,LINE:1,DOTS:2};function ChartSeries(points,seriesYAxis,opt_renderingConfig){this.points=points;this.seriesYAxis=seriesYAxis;this.useRenderingConfig_(opt_renderingConfig);}
+ChartSeries.prototype={useRenderingConfig_(opt_renderingConfig){const config=opt_renderingConfig||{};for(const[key,defaultValue]of
+Object.entries(DEFAULT_RENDERING_CONFIG)){let value=config[key];if(value===undefined){value=defaultValue;}
+this[key+'_']=value;}
+this.topPadding=this.bottomPadding=Math.max(this.selectedPointSize_,this.unselectedPointSize_)/2;},get range(){const range=new tr.b.math.Range();this.points.forEach(function(point){range.addValue(point.y);},this);return range;},draw(ctx,transform,highDetails){if(this.points===undefined||this.points.length===0){return;}
+if(this.chartType_===ChartSeriesType.AREA){this.drawComponent_(ctx,transform,ChartSeriesComponent.BACKGROUND,highDetails);}
+if(this.chartType_===ChartSeriesType.LINE||highDetails){this.drawComponent_(ctx,transform,ChartSeriesComponent.LINE,highDetails);}
+this.drawComponent_(ctx,transform,ChartSeriesComponent.DOTS,highDetails);},drawComponent_(ctx,transform,component,highDetails){let extraPixels=0;if(component===ChartSeriesComponent.DOTS){extraPixels=Math.max(this.selectedPointSize_,this.unselectedPointSize_);}
+const pixelRatio=transform.pixelRatio;const leftViewX=transform.leftViewX-extraPixels*pixelRatio;const rightViewX=transform.rightViewX+extraPixels*pixelRatio;const leftTimestamp=transform.leftTimestamp-extraPixels;const rightTimestamp=transform.rightTimestamp+extraPixels;const firstVisibleIndex=tr.b.findLowIndexInSortedArray(this.points,function(point){return point.x;},leftTimestamp);let lastVisibleIndex=tr.b.findLowIndexInSortedArray(this.points,function(point){return point.x;},rightTimestamp);if(lastVisibleIndex>=this.points.length||this.points[lastVisibleIndex].x>rightTimestamp){lastVisibleIndex--;}
+const viewSkipDistance=this.skipDistance_*pixelRatio;let selectedCircleRadius;let letterDotRadius;let squareSize;let squareHalfSize;let squareOpacity;let unselectedSeriesColor;let currentStateSeriesColor;ctx.save();ctx.font=DOT_LETTER_FONT_WEIGHT+' '+
+Math.floor(DOT_LETTER_FONT_SIZE_PX*pixelRatio)+'px '+
+DOT_LETTER_FONT;ctx.textBaseline='middle';ctx.textAlign='center';switch(component){case ChartSeriesComponent.DOTS:{selectedCircleRadius=(this.selectedPointSize_/2)*pixelRatio;letterDotRadius=Math.max(selectedCircleRadius,DOT_LETTER_RADIUS_PX*pixelRatio);squareSize=this.unselectedPointSize_*pixelRatio;squareHalfSize=squareSize/2;unselectedSeriesColor=EventPresenter.getCounterSeriesColor(this.colorId_,SelectionState.NONE);if(!highDetails){squareOpacity=0;break;}
+const visibleIndexRange=lastVisibleIndex-firstVisibleIndex;if(visibleIndexRange<=0){squareOpacity=1;break;}
+const visibleViewXRange=transform.worldXToViewX(this.points[lastVisibleIndex].x)-
+transform.worldXToViewX(this.points[firstVisibleIndex].x);if(visibleViewXRange===0){squareOpacity=1;break;}
+const density=visibleIndexRange/visibleViewXRange;const clampedDensity=tr.b.math.clamp(density,this.unselectedPointDensityOpaque_,this.unselectedPointDensityTransparent_);const densityRange=this.unselectedPointDensityTransparent_-
+this.unselectedPointDensityOpaque_;squareOpacity=(this.unselectedPointDensityTransparent_-clampedDensity)/densityRange;break;}
+case ChartSeriesComponent.LINE:ctx.strokeStyle=EventPresenter.getCounterSeriesColor(this.colorId_,SelectionState.NONE);ctx.lineWidth=this.lineWidth_*pixelRatio;break;case ChartSeriesComponent.BACKGROUND:break;default:throw new Error('Invalid component: '+component);}
+let previousViewX=undefined;let previousViewY=undefined;let previousViewYBase=undefined;let lastSelectionState=undefined;let baseSteps=undefined;const startIndex=Math.max(firstVisibleIndex-1,0);let currentViewX;for(let i=startIndex;i<this.points.length;i++){const currentPoint=this.points[i];currentViewX=transform.worldXToViewX(currentPoint.x);if(currentViewX>rightViewX){if(previousViewX!==undefined){previousViewX=currentViewX=rightViewX;if(component===ChartSeriesComponent.BACKGROUND||component===ChartSeriesComponent.LINE){ctx.lineTo(currentViewX,previousViewY);}}
+break;}
+if(i+1<this.points.length){const nextPoint=this.points[i+1];const nextViewX=transform.worldXToViewX(nextPoint.x);if(previousViewX!==undefined&&nextViewX-previousViewX<=viewSkipDistance&&nextViewX<rightViewX){continue;}
+if(currentViewX<leftViewX){currentViewX=leftViewX;}}
+if(previousViewX!==undefined&&currentViewX-previousViewX<viewSkipDistance){currentViewX=previousViewX+viewSkipDistance;}
+const currentViewY=Math.round(transform.worldYToViewY(currentPoint.y));let currentViewYBase;if(currentPoint.yBase===undefined){currentViewYBase=transform.outerBottomViewY;}else{currentViewYBase=Math.round(transform.worldYToViewY(currentPoint.yBase));}
+const currentSelectionState=currentPoint.selectionState;if(currentSelectionState!==lastSelectionState){const opacity=currentSelectionState===SelectionState.SELECTED?1:squareOpacity;currentStateSeriesColor=EventPresenter.getCounterSeriesColor(this.colorId_,currentSelectionState,opacity);}
+switch(component){case ChartSeriesComponent.DOTS:if(currentPoint.dotLetter){ctx.fillStyle=unselectedSeriesColor;ctx.strokeStyle=ColorScheme.getColorForReservedNameAsString('black');ctx.beginPath();ctx.arc(currentViewX,currentViewY,letterDotRadius+DOT_LETTER_RADIUS_PADDING_PX,0,2*Math.PI);ctx.fill();if(currentSelectionState===SelectionState.SELECTED){ctx.lineWidth=DOT_LETTER_SELECTED_OUTLINE_WIDTH_PX;ctx.strokeStyle=ColorScheme.getColorForReservedNameAsString('olive');ctx.stroke();ctx.beginPath();ctx.arc(currentViewX,currentViewY,letterDotRadius,0,2*Math.PI);ctx.lineWidth=DOT_LETTER_SELECTED_OUTLINE_DETAIL_WIDTH_PX;ctx.strokeStyle=ColorScheme.getColorForReservedNameAsString('yellow');ctx.stroke();}else{ctx.lineWidth=DOT_LETTER_UNSELECTED_OUTLINE_WIDTH_PX;ctx.strokeStyle=ColorScheme.getColorForReservedNameAsString('black');ctx.stroke();}
+ctx.fillStyle=ColorScheme.getColorForReservedNameAsString('white');ctx.fillText(currentPoint.dotLetter,currentViewX,currentViewY);}else{ctx.strokeStyle=unselectedSeriesColor;ctx.lineWidth=pixelRatio;if(currentSelectionState===SelectionState.SELECTED){if(this.solidSelectedDots_){ctx.fillStyle=ctx.strokeStyle;}else{ctx.fillStyle=currentStateSeriesColor;}
+ctx.beginPath();ctx.arc(currentViewX,currentViewY,selectedCircleRadius,0,2*Math.PI);ctx.fill();ctx.stroke();}else if(squareOpacity>0){ctx.fillStyle=currentStateSeriesColor;ctx.fillRect(currentViewX-squareHalfSize,currentViewY-squareHalfSize,squareSize,squareSize);}}
+break;case ChartSeriesComponent.LINE:if(previousViewX===undefined){ctx.beginPath();ctx.moveTo(currentViewX,currentViewY);}else if(this.stepGraph_){ctx.lineTo(currentViewX,previousViewY);}
+ctx.lineTo(currentViewX,currentViewY);break;case ChartSeriesComponent.BACKGROUND:if(previousViewX!==undefined&&this.stepGraph_){ctx.lineTo(currentViewX,previousViewY);}else{ctx.lineTo(currentViewX,currentViewY);}
+if(currentSelectionState!==lastSelectionState){if(previousViewX!==undefined){let previousBaseStepViewX=currentViewX;for(let j=baseSteps.length-1;j>=0;j--){const baseStep=baseSteps[j];const baseStepViewX=baseStep.viewX;const baseStepViewY=baseStep.viewY;ctx.lineTo(previousBaseStepViewX,baseStepViewY);ctx.lineTo(baseStepViewX,baseStepViewY);previousBaseStepViewX=baseStepViewX;}
+ctx.closePath();ctx.fill();}
+ctx.beginPath();ctx.fillStyle=EventPresenter.getCounterSeriesColor(this.colorId_,currentSelectionState,this.backgroundOpacity_);ctx.moveTo(currentViewX,currentViewYBase);baseSteps=[];}
+if(currentViewYBase!==previousViewYBase||currentSelectionState!==lastSelectionState){baseSteps.push({viewX:currentViewX,viewY:currentViewYBase});}
+ctx.lineTo(currentViewX,currentViewY);break;default:throw new Error('Not reachable');}
+previousViewX=currentViewX;previousViewY=currentViewY;previousViewYBase=currentViewYBase;lastSelectionState=currentSelectionState;}
+if(previousViewX!==undefined){switch(component){case ChartSeriesComponent.DOTS:break;case ChartSeriesComponent.LINE:ctx.stroke();break;case ChartSeriesComponent.BACKGROUND:{let previousBaseStepViewX=currentViewX;for(let j=baseSteps.length-1;j>=0;j--){const baseStep=baseSteps[j];const baseStepViewX=baseStep.viewX;const baseStepViewY=baseStep.viewY;ctx.lineTo(previousBaseStepViewX,baseStepViewY);ctx.lineTo(baseStepViewX,baseStepViewY);previousBaseStepViewX=baseStepViewX;}
+ctx.closePath();ctx.fill();break;}
+default:throw new Error('Not reachable');}}
+ctx.restore();},addIntersectingEventsInRangeToSelectionInWorldSpace(loWX,hiWX,viewPixWidthWorld,selection){const points=this.points;function getPointWidth(point,i){if(i===points.length-1){return LAST_POINT_WIDTH*viewPixWidthWorld;}
+const nextPoint=points[i+1];return nextPoint.x-point.x;}
+function selectPoint(point){point.addToSelection(selection);}
+tr.b.iterateOverIntersectingIntervals(this.points,function(point){return point.x;},getPointWidth,loWX,hiWX,selectPoint);},addEventNearToProvidedEventToSelection(event,offset,selection){if(this.points===undefined)return false;const index=this.points.findIndex(point=>point.modelItem===event);if(index===-1)return false;const newIndex=index+offset;if(newIndex<0||newIndex>=this.points.length)return false;this.points[newIndex].addToSelection(selection);return true;},addClosestEventToSelection(worldX,worldMaxDist,loY,hiY,selection){if(this.points===undefined)return;const item=tr.b.findClosestElementInSortedArray(this.points,function(point){return point.x;},worldX,worldMaxDist);if(!item)return;item.addToSelection(selection);}};return{ChartSeries,ChartSeriesType,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const ColorScheme=tr.b.ColorScheme;const IDEAL_MAJOR_MARK_HEIGHT_PX=30;const AXIS_LABLE_MARGIN_PX=10;const AXIS_LABLE_FONT_SIZE_PX=9;const AXIS_LABLE_FONT='Arial';function ChartSeriesYAxis(opt_min,opt_max){this.guid_=tr.b.GUID.allocateSimple();this.bounds=new tr.b.math.Range();if(opt_min!==undefined)this.bounds.addValue(opt_min);if(opt_max!==undefined)this.bounds.addValue(opt_max);}
+ChartSeriesYAxis.prototype={get guid(){return this.guid_;},valueToUnitRange(value){if(this.bounds.isEmpty){throw new Error('Chart series y-axis bounds are empty');}
+const bounds=this.bounds;if(bounds.range===0)return 0;return(value-bounds.min)/bounds.range;},unitRangeToValue(unitRange){if(this.bounds.isEmpty){throw new Error('Chart series y-axis bounds are empty');}
+return unitRange*this.bounds.range+this.bounds.min;},autoSetFromSeries(series,opt_config){const range=new tr.b.math.Range();series.forEach(function(s){range.addRange(s.range);},this);this.autoSetFromRange(range,opt_config);},autoSetFromRange(range,opt_config){if(range.isEmpty)return;const bounds=this.bounds;if(bounds.isEmpty){bounds.addRange(range);return;}
+if(!opt_config)return;const useRangeMin=(opt_config.expandMin&&range.min<bounds.min||opt_config.shrinkMin&&range.min>bounds.min);const useRangeMax=(opt_config.expandMax&&range.max>bounds.max||opt_config.shrinkMax&&range.max<bounds.max);if(!useRangeMin&&!useRangeMax)return;if(useRangeMin&&useRangeMax){bounds.min=range.min;bounds.max=range.max;return;}
+if(useRangeMin){bounds.min=Math.min(range.min,bounds.max);}else{bounds.max=Math.max(range.max,bounds.min);}},majorMarkHeightWorld_(transform,pixelRatio){const idealMajorMarkHeightPx=IDEAL_MAJOR_MARK_HEIGHT_PX*pixelRatio;const idealMajorMarkHeightWorld=transform.vectorToWorldDistance(idealMajorMarkHeightPx);return tr.b.math.preferredNumberLargerThanMin(idealMajorMarkHeightWorld);},draw(ctx,transform,showYAxisLabels,showYGridLines){if(!showYAxisLabels&&!showYGridLines)return;const pixelRatio=transform.pixelRatio;const viewTop=transform.outerTopViewY;const worldTop=transform.viewYToWorldY(viewTop);const viewBottom=transform.outerBottomViewY;const viewHeight=viewBottom-viewTop;const viewLeft=transform.leftViewX;const viewRight=transform.rightViewX;const labelLeft=transform.leftYLabel;ctx.save();ctx.lineWidth=pixelRatio;ctx.fillStyle=ColorScheme.getColorForReservedNameAsString('black');ctx.textAlign='left';ctx.textBaseline='center';ctx.font=(AXIS_LABLE_FONT_SIZE_PX*pixelRatio)+'px '+AXIS_LABLE_FONT;ctx.beginPath();ctx.strokeStyle=ColorScheme.getColorForReservedNameAsString('black');tr.ui.b.drawLine(ctx,viewLeft,viewTop,viewLeft,viewBottom,viewLeft);ctx.stroke();ctx.closePath();ctx.beginPath();ctx.strokeStyle=ColorScheme.getColorForReservedNameAsString('grey');const majorMarkHeight=this.majorMarkHeightWorld_(transform,pixelRatio);const maxMajorMark=Math.max(transform.viewYToWorldY(viewTop),Math.abs(transform.viewYToWorldY(viewBottom)));for(let curWorldY=0;curWorldY<=maxMajorMark;curWorldY+=majorMarkHeight){const roundedUnitValue=Math.floor(curWorldY*1000000)/1000000;const curViewYPositive=transform.worldYToViewY(curWorldY);if(curViewYPositive>=viewTop){if(showYAxisLabels){ctx.fillText(roundedUnitValue,viewLeft+AXIS_LABLE_MARGIN_PX,curViewYPositive-AXIS_LABLE_MARGIN_PX);}
+if(showYGridLines){tr.ui.b.drawLine(ctx,viewLeft,curViewYPositive,viewRight,curViewYPositive);}}
+const curViewYNegative=transform.worldYToViewY(-1*curWorldY);if(curViewYNegative<=viewBottom){if(showYAxisLabels){ctx.fillText(roundedUnitValue,viewLeft+AXIS_LABLE_MARGIN_PX,curViewYNegative-AXIS_LABLE_MARGIN_PX);}
+if(showYGridLines){tr.ui.b.drawLine(ctx,viewLeft,curViewYNegative,viewRight,curViewYNegative);}}}
+ctx.stroke();ctx.restore();}};return{ChartSeriesYAxis,};});'use strict';tr.exportTo('tr.ui.tracks',function(){function ChartTransform(displayTransform,axis,trackWidth,trackHeight,topPadding,bottomPadding,pixelRatio){this.pixelRatio=pixelRatio;this.leftViewX=0;this.rightViewX=trackWidth;this.leftTimestamp=displayTransform.xViewToWorld(this.leftViewX);this.rightTimestamp=displayTransform.xViewToWorld(this.rightViewX);this.displayTransform_=displayTransform;this.outerTopViewY=0;this.innerTopViewY=topPadding;this.innerBottomViewY=trackHeight-bottomPadding;this.outerBottomViewY=trackHeight;this.axis_=axis;this.innerHeight_=this.innerBottomViewY-this.innerTopViewY;}
+ChartTransform.prototype={worldXToViewX(worldX){return this.displayTransform_.xWorldToView(worldX);},viewXToWorldX(viewX){return this.displayTransform_.xViewToWorld(viewX);},vectorToWorldDistance(viewY){return this.axis_.bounds.range*Math.abs(viewY/this.innerHeight_);},viewYToWorldY(viewY){return this.axis_.unitRangeToValue(1-(viewY-this.innerTopViewY)/this.innerHeight_);},worldYToViewY(worldY){const innerHeightCoefficient=1-this.axis_.valueToUnitRange(worldY);return innerHeightCoefficient*this.innerHeight_+this.innerTopViewY;}};return{ChartTransform,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const ChartTrack=tr.ui.b.define('chart-track',tr.ui.tracks.Track);ChartTrack.prototype={__proto__:tr.ui.tracks.Track.prototype,decorate(viewport){tr.ui.tracks.Track.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('chart-track');this.series_=undefined;this.axes_=undefined;this.axisGuidToAxisData_=undefined;this.topPadding_=undefined;this.bottomPadding_=undefined;this.showYAxisLabels_=undefined;this.showGridLines_=undefined;this.heading_=document.createElement('tr-ui-b-heading');Polymer.dom(this).appendChild(this.heading_);},set heading(heading){this.heading_.heading=heading;},get heading(){return this.heading_.heading;},set tooltip(tooltip){this.heading_.tooltip=tooltip;},get series(){return this.series_;},set series(series){this.series_=series;this.calculateAxisDataAndPadding_();this.invalidateDrawingContainer();},get height(){return window.getComputedStyle(this).height;},set height(height){this.style.height=height;this.invalidateDrawingContainer();},get showYAxisLabels(){return this.showYAxisLabels_;},set showYAxisLabels(showYAxisLabels){this.showYAxisLabels_=showYAxisLabels;this.invalidateDrawingContainer();},get showGridLines(){return this.showGridLines_;},set showGridLines(showGridLines){this.showGridLines_=showGridLines;this.invalidateDrawingContainer();},get hasVisibleContent(){return!!this.series&&this.series.length>0;},calculateAxisDataAndPadding_(){if(!this.series_){this.axes_=undefined;this.axisGuidToAxisData_=undefined;this.topPadding_=undefined;this.bottomPadding_=undefined;return;}
+const axisGuidToAxisData={};let topPadding=0;let bottomPadding=0;this.series_.forEach(function(series){const seriesYAxis=series.seriesYAxis;const axisGuid=seriesYAxis.guid;if(!(axisGuid in axisGuidToAxisData)){axisGuidToAxisData[axisGuid]={axis:seriesYAxis,series:[]};if(!this.axes_)this.axes_=[];this.axes_.push(seriesYAxis);}
+axisGuidToAxisData[axisGuid].series.push(series);topPadding=Math.max(topPadding,series.topPadding);bottomPadding=Math.max(bottomPadding,series.bottomPadding);},this);this.axisGuidToAxisData_=axisGuidToAxisData;this.topPadding_=topPadding;this.bottomPadding_=bottomPadding;},draw(type,viewLWorld,viewRWorld,viewHeight){switch(type){case tr.ui.tracks.DrawType.GENERAL_EVENT:this.drawChart_(viewLWorld,viewRWorld);break;}},drawChart_(viewLWorld,viewRWorld){if(!this.series_)return;const ctx=this.context();const displayTransform=this.viewport.currentDisplayTransform;const pixelRatio=window.devicePixelRatio||1;const bounds=this.getBoundingClientRect();const highDetails=this.viewport.highDetails;const width=bounds.width*pixelRatio;const height=bounds.height*pixelRatio;const topPadding=this.topPadding_*pixelRatio;const bottomPadding=this.bottomPadding_*pixelRatio;ctx.save();ctx.beginPath();ctx.rect(0,0,width,height);ctx.clip();if(this.axes_){if((this.showGridLines_||this.showYAxisLabels_)&&this.axes_.length>1){throw new Error('Only one axis allowed when showing grid lines.');}
+for(const yAxis of this.axes_){const chartTransform=new tr.ui.tracks.ChartTransform(displayTransform,yAxis,width,height,topPadding,bottomPadding,pixelRatio);yAxis.draw(ctx,chartTransform,this.showYAxisLabels_,this.showGridLines_);}}
+for(const series of this.series){const chartTransform=new tr.ui.tracks.ChartTransform(displayTransform,series.seriesYAxis,width,height,topPadding,bottomPadding,pixelRatio);series.draw(ctx,chartTransform,highDetails);}
+ctx.restore();},addEventsToTrackMap(eventToTrackMap){this.series_.forEach(function(series){series.points.forEach(function(point){point.addToTrackMap(eventToTrackMap,this);},this);},this);},addIntersectingEventsInRangeToSelectionInWorldSpace(loWX,hiWX,viewPixWidthWorld,selection){this.series_.forEach(function(series){series.addIntersectingEventsInRangeToSelectionInWorldSpace(loWX,hiWX,viewPixWidthWorld,selection);},this);},addEventNearToProvidedEventToSelection(event,offset,selection){let foundItem=false;this.series_.forEach(function(series){foundItem=foundItem||series.addEventNearToProvidedEventToSelection(event,offset,selection);},this);return foundItem;},addAllEventsMatchingFilterToSelection(filter,selection){},addClosestEventToSelection(worldX,worldMaxDist,loY,hiY,selection){this.series_.forEach(function(series){series.addClosestEventToSelection(worldX,worldMaxDist,loY,hiY,selection);},this);},autoSetAllAxes(opt_config){for(const axisData of Object.values(this.axisGuidToAxisData_)){const seriesYAxis=axisData.axis;const series=axisData.series;seriesYAxis.autoSetFromSeries(series,opt_config);}},autoSetAxis(seriesYAxis,opt_config){const series=this.axisGuidToAxisData_[seriesYAxis.guid].series;seriesYAxis.autoSetFromSeries(series,opt_config);}};return{ChartTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const ColorScheme=tr.b.ColorScheme;const ChartTrack=tr.ui.tracks.ChartTrack;const CpuUsageTrack=tr.ui.b.define('cpu-usage-track',ChartTrack);CpuUsageTrack.prototype={__proto__:ChartTrack.prototype,decorate(viewport){ChartTrack.prototype.decorate.call(this,viewport);this.classList.add('cpu-usage-track');this.heading='CPU usage';this.cpuUsageSeries_=undefined;},initialize(model){if(model!==undefined){this.cpuUsageSeries_=model.device.cpuUsageSeries;}else{this.cpuUsageSeries_=undefined;}
+this.series=this.buildChartSeries_();this.autoSetAllAxes({expandMax:true});},get hasVisibleContent(){return!!this.cpuUsageSeries_&&this.cpuUsageSeries_.samples.length>0;},addContainersToTrackMap(containerToTrackMap){containerToTrackMap.addContainer(this.series_,this);},buildChartSeries_(yAxis,color){if(!this.hasVisibleContent)return[];yAxis=new tr.ui.tracks.ChartSeriesYAxis(0,undefined);const usageSamples=this.cpuUsageSeries_.samples;const pts=new Array(usageSamples.length+1);for(let i=0;i<usageSamples.length;i++){pts[i]=new tr.ui.tracks.ChartPoint(undefined,usageSamples[i].start,usageSamples[i].usage);}
+pts[usageSamples.length]=new tr.ui.tracks.ChartPoint(undefined,usageSamples[usageSamples.length-1].start,0);const renderingConfig={chartType:tr.ui.tracks.ChartSeriesType.AREA,colorId:color};return[new tr.ui.tracks.ChartSeries(pts,yAxis,renderingConfig)];},};return{CpuUsageTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const ColorScheme=tr.b.ColorScheme;const ChartTrack=tr.ui.tracks.ChartTrack;const PowerSeriesTrack=tr.ui.b.define('power-series-track',ChartTrack);PowerSeriesTrack.prototype={__proto__:ChartTrack.prototype,decorate(viewport){ChartTrack.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('power-series-track');this.heading='Power';this.powerSeries_=undefined;},set powerSeries(powerSeries){this.powerSeries_=powerSeries;this.series=this.buildChartSeries_();this.autoSetAllAxes({expandMax:true});},get hasVisibleContent(){return(this.powerSeries_&&this.powerSeries_.samples.length>0);},addContainersToTrackMap(containerToTrackMap){containerToTrackMap.addContainer(this.powerSeries_,this);},buildChartSeries_(){if(!this.hasVisibleContent)return[];const seriesYAxis=new tr.ui.tracks.ChartSeriesYAxis(0,undefined);const pts=this.powerSeries_.samples.map(function(smpl){return new tr.ui.tracks.ChartPoint(smpl,smpl.start,smpl.powerInW);});const renderingConfig={chartType:tr.ui.tracks.ChartSeriesType.AREA,colorId:ColorScheme.getColorIdForGeneralPurposeString(this.heading)};return[new tr.ui.tracks.ChartSeries(pts,seriesYAxis,renderingConfig)];}};return{PowerSeriesTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const SpacingTrack=tr.ui.b.define('spacing-track',tr.ui.tracks.Track);SpacingTrack.prototype={__proto__:tr.ui.tracks.Track.prototype,decorate(viewport){tr.ui.tracks.Track.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('spacing-track');this.heading_=document.createElement('tr-ui-b-heading');Polymer.dom(this).appendChild(this.heading_);},addAllEventsMatchingFilterToSelection(filter,selection){}};return{SpacingTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const ContainerTrack=tr.ui.tracks.ContainerTrack;const DeviceTrack=tr.ui.b.define('device-track',ContainerTrack);DeviceTrack.prototype={__proto__:ContainerTrack.prototype,decorate(viewport){ContainerTrack.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('device-track');this.device_=undefined;this.powerSeriesTrack_=undefined;},get device(){return this.device_;},set device(device){this.device_=device;this.updateContents_();},get powerSeriesTrack(){return this.powerSeriesTrack_;},get hasVisibleContent(){return(this.powerSeriesTrack_&&this.powerSeriesTrack_.hasVisibleContent);},addContainersToTrackMap(containerToTrackMap){tr.ui.tracks.ContainerTrack.prototype.addContainersToTrackMap.call(this,containerToTrackMap);containerToTrackMap.addContainer(this.device,this);},addEventsToTrackMap(eventToTrackMap){this.tracks_.forEach(function(track){track.addEventsToTrackMap(eventToTrackMap);});},appendPowerSeriesTrack_(){this.powerSeriesTrack_=new tr.ui.tracks.PowerSeriesTrack(this.viewport);this.powerSeriesTrack_.powerSeries=this.device.powerSeries;if(this.powerSeriesTrack_.hasVisibleContent){Polymer.dom(this).appendChild(this.powerSeriesTrack_);Polymer.dom(this).appendChild(new tr.ui.tracks.SpacingTrack(this.viewport));}},updateContents_(){this.clearTracks_();this.appendPowerSeriesTrack_();}};return{DeviceTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const ColorScheme=tr.b.ColorScheme;const DISPLAYED_SIZE_NUMERIC_NAME=tr.model.MemoryAllocatorDump.DISPLAYED_SIZE_NUMERIC_NAME;const BACKGROUND=tr.model.ContainerMemoryDump.LevelOfDetail.BACKGROUND;const LIGHT=tr.model.ContainerMemoryDump.LevelOfDetail.LIGHT;const DETAILED=tr.model.ContainerMemoryDump.LevelOfDetail.DETAILED;const SYSTEM_MEMORY_CHART_RENDERING_CONFIG={chartType:tr.ui.tracks.ChartSeriesType.AREA,colorId:ColorScheme.getColorIdForGeneralPurposeString('systemMemory'),backgroundOpacity:0.8};const SYSTEM_MEMORY_SERIES_NAMES=['Used (KB)','Swapped (KB)'];function extractGlobalMemoryDumpUsedSizes(globalMemoryDump,addSize){for(const[pid,pmd]of
+Object.entries(globalMemoryDump.processMemoryDumps)){const mostRecentVmRegions=pmd.mostRecentVmRegions;if(mostRecentVmRegions===undefined)continue;addSize(pid,mostRecentVmRegions.byteStats.proportionalResident||0,pmd.process.userFriendlyName);}}
+function extractProcessMemoryDumpAllocatorSizes(processMemoryDump,addSize){const allocatorDumps=processMemoryDump.memoryAllocatorDumps;if(allocatorDumps===undefined)return;allocatorDumps.forEach(function(allocatorDump){if(allocatorDump.fullName==='tracing')return;const allocatorSize=allocatorDump.numerics[DISPLAYED_SIZE_NUMERIC_NAME];if(allocatorSize===undefined)return;const allocatorSizeValue=allocatorSize.value;if(allocatorSizeValue===undefined)return;addSize(allocatorDump.fullName,allocatorSizeValue);});}
+function extractGlobalMemoryDumpAllocatorSizes(globalMemoryDump,addSize){for(const pmd of Object.values(globalMemoryDump.processMemoryDumps)){extractProcessMemoryDumpAllocatorSizes(pmd,addSize);}}
+function buildMemoryChartSeries(memoryDumps,dumpSizeExtractor){const dumpCount=memoryDumps.length;const idToTimestampToPoint={};const idToName={};memoryDumps.forEach(function(dump,index){dumpSizeExtractor(dump,function addSize(id,size,opt_name){let timestampToPoint=idToTimestampToPoint[id];if(timestampToPoint===undefined){idToTimestampToPoint[id]=timestampToPoint=new Array(dumpCount);for(let i=0;i<dumpCount;i++){const modelItem=memoryDumps[i];timestampToPoint[i]=new tr.ui.tracks.ChartPoint(modelItem,modelItem.start,0);}}
+timestampToPoint[index].y+=size;if(opt_name!==undefined)idToName[id]=opt_name;});});const ids=Object.keys(idToTimestampToPoint);if(ids.length===0)return undefined;ids.sort();for(let i=0;i<dumpCount;i++){let baseSize=0;for(let j=ids.length-1;j>=0;j--){const point=idToTimestampToPoint[ids[j]][i];point.yBase=baseSize;point.y+=baseSize;baseSize=point.y;}}
+const seriesYAxis=new tr.ui.tracks.ChartSeriesYAxis(0);const series=ids.map(function(id){const colorId=ColorScheme.getColorIdForGeneralPurposeString(idToName[id]||id);const renderingConfig={chartType:tr.ui.tracks.ChartSeriesType.AREA,colorId,backgroundOpacity:0.8};return new tr.ui.tracks.ChartSeries(idToTimestampToPoint[id],seriesYAxis,renderingConfig);});series.reverse();return series;}
+function buildMemoryLetterDots(memoryDumps){const backgroundMemoryColorId=ColorScheme.getColorIdForReservedName('background_memory_dump');const lightMemoryColorId=ColorScheme.getColorIdForReservedName('light_memory_dump');const detailedMemoryColorId=ColorScheme.getColorIdForReservedName('detailed_memory_dump');return memoryDumps.map(function(memoryDump){let memoryColorId;switch(memoryDump.levelOfDetail){case BACKGROUND:memoryColorId=backgroundMemoryColorId;break;case DETAILED:memoryColorId=detailedMemoryColorId;break;case LIGHT:default:memoryColorId=lightMemoryColorId;}
+return new tr.ui.tracks.LetterDot(memoryDump,'M',memoryColorId,memoryDump.start);});}
+function buildGlobalUsedMemoryChartSeries(globalMemoryDumps){return buildMemoryChartSeries(globalMemoryDumps,extractGlobalMemoryDumpUsedSizes);}
+function buildProcessAllocatedMemoryChartSeries(processMemoryDumps){return buildMemoryChartSeries(processMemoryDumps,extractProcessMemoryDumpAllocatorSizes);}
+function buildGlobalAllocatedMemoryChartSeries(globalMemoryDumps){return buildMemoryChartSeries(globalMemoryDumps,extractGlobalMemoryDumpAllocatorSizes);}
+function buildSystemMemoryChartSeries(model){if(model.kernel.counters===undefined)return;const memoryCounter=model.kernel.counters['global.SystemMemory'];if(memoryCounter===undefined)return;const tracks=[];for(const name of SYSTEM_MEMORY_SERIES_NAMES){const series=memoryCounter.series.find(series=>series.name===name);if(series===undefined||series.samples.length===0)return;const chartPoints=[];const valueRange=new tr.b.math.Range();for(const sample of series.samples){chartPoints.push(new tr.ui.tracks.ChartPoint(sample,sample.timestamp,sample.value,0));valueRange.addValue(sample.value);}
+const baseLine=Math.max(0,valueRange.min-valueRange.range);const axisY=new tr.ui.tracks.ChartSeriesYAxis(baseLine,valueRange.max);const chartSeries=[new tr.ui.tracks.ChartSeries(chartPoints,axisY,SYSTEM_MEMORY_CHART_RENDERING_CONFIG)];tracks.push({name:'System Memory '+name,series:chartSeries});}
+return tracks;}
+return{buildMemoryLetterDots,buildGlobalUsedMemoryChartSeries,buildProcessAllocatedMemoryChartSeries,buildGlobalAllocatedMemoryChartSeries,buildSystemMemoryChartSeries,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const USED_MEMORY_TRACK_HEIGHT=50;const ALLOCATED_MEMORY_TRACK_HEIGHT=50;const GlobalMemoryDumpTrack=tr.ui.b.define('global-memory-dump-track',tr.ui.tracks.ContainerTrack);GlobalMemoryDumpTrack.prototype={__proto__:tr.ui.tracks.ContainerTrack.prototype,decorate(viewport){tr.ui.tracks.ContainerTrack.prototype.decorate.call(this,viewport);this.memoryDumps_=undefined;},get memoryDumps(){return this.memoryDumps_;},set memoryDumps(memoryDumps){this.memoryDumps_=memoryDumps;this.updateContents_();},updateContents_(){this.clearTracks_();if(!this.memoryDumps_||!this.memoryDumps_.length)return;this.appendDumpDotsTrack_();this.appendUsedMemoryTrack_();this.appendAllocatedMemoryTrack_();},appendDumpDotsTrack_(){const items=tr.ui.tracks.buildMemoryLetterDots(this.memoryDumps_);if(!items)return;const track=new tr.ui.tracks.LetterDotTrack(this.viewport);track.heading='Memory Dumps';track.items=items;Polymer.dom(this).appendChild(track);},appendUsedMemoryTrack_(){const tracks=[];const perProcessSeries=tr.ui.tracks.buildGlobalUsedMemoryChartSeries(this.memoryDumps_);if(perProcessSeries!==undefined){tracks.push({name:'Memory per process',series:perProcessSeries});}else{tracks.push.apply(tracks,tr.ui.tracks.buildSystemMemoryChartSeries(this.memoryDumps_[0].model));}
+for(const{name,series}of tracks){const track=new tr.ui.tracks.ChartTrack(this.viewport);track.heading=name;track.height=USED_MEMORY_TRACK_HEIGHT+'px';track.series=series;track.autoSetAllAxes({expandMax:true});Polymer.dom(this).appendChild(track);}},appendAllocatedMemoryTrack_(){const series=tr.ui.tracks.buildGlobalAllocatedMemoryChartSeries(this.memoryDumps_);if(!series)return;const track=new tr.ui.tracks.ChartTrack(this.viewport);track.heading='Memory per component';track.height=ALLOCATED_MEMORY_TRACK_HEIGHT+'px';track.series=series;track.autoSetAllAxes({expandMax:true});Polymer.dom(this).appendChild(track);}};return{GlobalMemoryDumpTrack,};});'use strict';tr.exportTo('tr.ui.b',function(){function FastRectRenderer(ctx,xMin,xMax,minRectSize,maxMergeDist,palette){this.ctx_=ctx;this.xMin_=xMin;this.xMax_=xMax;this.minRectSize_=minRectSize;this.maxMergeDist_=maxMergeDist;this.palette_=palette;}
+FastRectRenderer.prototype={y_:0,h_:0,merging_:false,mergeStartX_:0,mergeCurRight_:0,mergedColorId_:0,mergedAlpha_:0,setYandH(y,h){if(this.y_===y&&this.h_===h){return;}
+this.flush();this.y_=y;this.h_=h;},fillRect(x,w,colorId,alpha){const r=x+w;if(w<this.minRectSize_){if(r-this.mergeStartX_>this.maxMergeDist_){this.flush();}
+if(!this.merging_){this.merging_=true;this.mergeStartX_=x;this.mergeCurRight_=r;this.mergedColorId_=colorId;this.mergedAlpha_=alpha;}else{this.mergeCurRight_=r;if(this.mergedAlpha_<alpha||(this.mergedAlpha_===alpha&&this.mergedColorId_<colorId)){this.mergedAlpha_=alpha;this.mergedColorId_=colorId;}}}else{if(this.merging_){this.flush();}
+this.ctx_.fillStyle=this.palette_[colorId];this.ctx_.globalAlpha=alpha;const xLeft=Math.max(x,this.xMin_);const xRight=Math.min(r,this.xMax_);if(xLeft<xRight){this.ctx_.fillRect(xLeft,this.y_,xRight-xLeft,this.h_);}}},flush(){if(this.merging_){this.ctx_.fillStyle=this.palette_[this.mergedColorId_];this.ctx_.globalAlpha=this.mergedAlpha_;const xLeft=Math.max(this.mergeStartX_,this.xMin_);const xRight=Math.min(this.mergeCurRight_,this.xMax_);if(xLeft<xRight){this.ctx_.fillRect(xLeft,this.y_,xRight-xLeft,this.h_);}
+this.merging_=false;}}};return{FastRectRenderer,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const RectTrack=tr.ui.b.define('rect-track',tr.ui.tracks.Track);RectTrack.prototype={__proto__:tr.ui.tracks.Track.prototype,decorate(viewport){tr.ui.tracks.Track.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('rect-track');this.asyncStyle_=false;this.rects_=null;this.heading_=document.createElement('tr-ui-b-heading');Polymer.dom(this).appendChild(this.heading_);},set heading(heading){this.heading_.heading=heading;},get heading(){return this.heading_.heading;},set tooltip(tooltip){this.heading_.tooltip=tooltip;},set selectionGenerator(generator){this.heading_.selectionGenerator=generator;},set expanded(expanded){this.heading_.expanded=!!expanded;},set arrowVisible(arrowVisible){this.heading_.arrowVisible=!!arrowVisible;},get expanded(){return this.heading_.expanded;},get asyncStyle(){return this.asyncStyle_;},set asyncStyle(v){this.asyncStyle_=!!v;},get rects(){return this.rects_;},set rects(rects){this.rects_=rects||[];this.invalidateDrawingContainer();},get height(){return window.getComputedStyle(this).height;},set height(height){this.style.height=height;this.invalidateDrawingContainer();},get hasVisibleContent(){return this.rects_.length>0;},draw(type,viewLWorld,viewRWorld,viewHeight){switch(type){case tr.ui.tracks.DrawType.GENERAL_EVENT:this.drawRects_(viewLWorld,viewRWorld);break;}},drawRects_(viewLWorld,viewRWorld){const ctx=this.context();ctx.save();const bounds=this.getBoundingClientRect();tr.ui.b.drawSlices(ctx,this.viewport.currentDisplayTransform,viewLWorld,viewRWorld,bounds.height,this.rects_,this.asyncStyle_);ctx.restore();if(bounds.height<=6)return;let fontSize;let yOffset;if(bounds.height<15){fontSize=6;yOffset=1.0;}else{fontSize=10;yOffset=2.5;}
+tr.ui.b.drawLabels(ctx,this.viewport.currentDisplayTransform,viewLWorld,viewRWorld,this.rects_,this.asyncStyle_,fontSize,yOffset);},addEventsToTrackMap(eventToTrackMap){if(this.rects_===undefined||this.rects_===null){return;}
+this.rects_.forEach(function(rect){rect.addToTrackMap(eventToTrackMap,this);},this);},addIntersectingEventsInRangeToSelectionInWorldSpace(loWX,hiWX,viewPixWidthWorld,selection){function onRect(rect){rect.addToSelection(selection);}
+onRect=onRect.bind(this);const instantEventWidth=2*viewPixWidthWorld;tr.b.iterateOverIntersectingIntervals(this.rects_,function(x){return x.start;},function(x){return x.duration===0?x.duration+instantEventWidth:x.duration;},loWX,hiWX,onRect);},addEventNearToProvidedEventToSelection(event,offset,selection){const index=this.rects_.findIndex(rect=>rect.modelItem===event);if(index===-1)return false;const newIndex=index+offset;if(newIndex<0||newIndex>=this.rects_.length)return false;this.rects_[newIndex].addToSelection(selection);return true;},addAllEventsMatchingFilterToSelection(filter,selection){for(let i=0;i<this.rects_.length;++i){const modelItem=this.rects_[i].modelItem;if(!modelItem)continue;if(filter.matchSlice(modelItem)){selection.push(modelItem);}}},addClosestEventToSelection(worldX,worldMaxDist,loY,hiY,selection){const rect=tr.b.findClosestIntervalInSortedIntervals(this.rects_,function(x){return x.start;},function(x){return x.end;},worldX,worldMaxDist);if(!rect)return;rect.addToSelection(selection);}};function Rect(modelItem,title,colorId,start,duration){tr.model.ProxySelectableItem.call(this,modelItem);this.title=title;this.colorId=colorId;this.start=start;this.duration=duration;this.end=start+duration;}
+Rect.prototype={__proto__:tr.model.ProxySelectableItem.prototype};return{RectTrack,Rect,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const SliceTrack=tr.ui.b.define('slice-track',tr.ui.tracks.RectTrack);SliceTrack.prototype={__proto__:tr.ui.tracks.RectTrack.prototype,decorate(viewport){tr.ui.tracks.RectTrack.prototype.decorate.call(this,viewport);},get slices(){return this.rects;},set slices(slices){this.rects=slices;}};return{SliceTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const CpuTrack=tr.ui.b.define('cpu-track',tr.ui.tracks.ContainerTrack);CpuTrack.prototype={__proto__:tr.ui.tracks.ContainerTrack.prototype,decorate(viewport){tr.ui.tracks.ContainerTrack.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('cpu-track');this.detailedMode_=true;},get cpu(){return this.cpu_;},set cpu(cpu){this.cpu_=cpu;this.updateContents_();},get detailedMode(){return this.detailedMode_;},set detailedMode(detailedMode){this.detailedMode_=detailedMode;this.updateContents_();},get tooltip(){return this.tooltip_;},set tooltip(value){this.tooltip_=value;this.updateContents_();},get hasVisibleContent(){if(this.cpu_===undefined)return false;const cpu=this.cpu_;if(cpu.slices.length)return true;if(cpu.samples&&cpu.samples.length)return true;if(Object.keys(cpu.counters).length>0)return true;return false;},updateContents_(){this.detach();if(!this.cpu_)return;const slices=this.cpu_.slices;if(slices.length){const track=new tr.ui.tracks.SliceTrack(this.viewport);track.slices=slices;track.heading=this.cpu_.userFriendlyName+':';Polymer.dom(this).appendChild(track);}
+if(this.detailedMode_){this.appendSamplesTracks_();for(const counterName in this.cpu_.counters){const counter=this.cpu_.counters[counterName];const track=new tr.ui.tracks.CounterTrack(this.viewport);track.heading=this.cpu_.userFriendlyName+' '+
+counter.name+':';track.counter=counter;Polymer.dom(this).appendChild(track);}}},appendSamplesTracks_(){const samples=this.cpu_.samples;if(samples===undefined||samples.length===0){return;}
+const samplesByTitle={};samples.forEach(function(sample){if(samplesByTitle[sample.title]===undefined){samplesByTitle[sample.title]=[];}
+samplesByTitle[sample.title].push(sample);});const sampleTitles=Object.keys(samplesByTitle);sampleTitles.sort();sampleTitles.forEach(function(sampleTitle){const samples=samplesByTitle[sampleTitle];const samplesTrack=new tr.ui.tracks.SliceTrack(this.viewport);samplesTrack.group=this.cpu_;samplesTrack.slices=samples;samplesTrack.heading=this.cpu_.userFriendlyName+': '+
+sampleTitle;samplesTrack.tooltip=this.cpu_.userFriendlyDetails;samplesTrack.selectionGenerator=function(){const selection=new tr.model.EventSet();for(let i=0;i<samplesTrack.slices.length;i++){selection.push(samplesTrack.slices[i]);}
+return selection;};Polymer.dom(this).appendChild(samplesTrack);},this);}};return{CpuTrack,};});'use strict';tr.exportTo('tr.model',function(){const Settings=tr.b.Settings;function ModelSettings(model){this.model=model;this.objectsByKey_=[];this.nonuniqueKeys_=[];this.buildObjectsByKeyMap_();this.removeNonuniqueKeysFromSettings_();this.ephemeralSettingsByGUID_={};}
+ModelSettings.prototype={buildObjectsByKeyMap_(){const objects=[];this.model.iterateAllPersistableObjects(function(o){objects.push(o);});const objectsByKey={};const NONUNIQUE_KEY='nonuniqueKey';for(let i=0;i<objects.length;i++){const object=objects[i];const objectKey=object.getSettingsKey();if(!objectKey)continue;if(objectsByKey[objectKey]===undefined){objectsByKey[objectKey]=object;continue;}
+objectsByKey[objectKey]=NONUNIQUE_KEY;}
+const nonuniqueKeys={};Object.keys(objectsByKey).forEach(function(objectKey){if(objectsByKey[objectKey]!==NONUNIQUE_KEY){return;}
+delete objectsByKey[objectKey];nonuniqueKeys[objectKey]=true;});this.nonuniqueKeys=nonuniqueKeys;this.objectsByKey_=objectsByKey;},removeNonuniqueKeysFromSettings_(){const settings=Settings.get('trace_model_settings',{});let settingsChanged=false;Object.keys(settings).forEach(function(objectKey){if(!this.nonuniqueKeys[objectKey]){return;}
+settingsChanged=true;delete settings[objectKey];},this);if(settingsChanged){Settings.set('trace_model_settings',settings);}},hasUniqueSettingKey(object){const objectKey=object.getSettingsKey();if(!objectKey)return false;return this.objectsByKey_[objectKey]!==undefined;},getSettingFor(object,objectLevelKey,defaultValue){const objectKey=object.getSettingsKey();if(!objectKey||!this.objectsByKey_[objectKey]){const settings=this.getEphemeralSettingsFor_(object);const ephemeralValue=settings[objectLevelKey];if(ephemeralValue!==undefined){return ephemeralValue;}
+return defaultValue;}
+const settings=Settings.get('trace_model_settings',{});if(!settings[objectKey]){settings[objectKey]={};}
+const value=settings[objectKey][objectLevelKey];if(value!==undefined){return value;}
+return defaultValue;},setSettingFor(object,objectLevelKey,value){const objectKey=object.getSettingsKey();if(!objectKey||!this.objectsByKey_[objectKey]){this.getEphemeralSettingsFor_(object)[objectLevelKey]=value;return;}
+const settings=Settings.get('trace_model_settings',{});if(!settings[objectKey]){settings[objectKey]={};}
+if(settings[objectKey][objectLevelKey]===value){return;}
+settings[objectKey][objectLevelKey]=value;Settings.set('trace_model_settings',settings);},getEphemeralSettingsFor_(object){if(object.guid===undefined){throw new Error('Only objects with GUIDs can be persisted');}
+if(this.ephemeralSettingsByGUID_[object.guid]===undefined){this.ephemeralSettingsByGUID_[object.guid]={};}
+return this.ephemeralSettingsByGUID_[object.guid];}};return{ModelSettings,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const CounterTrack=tr.ui.b.define('counter-track',tr.ui.tracks.ChartTrack);CounterTrack.prototype={__proto__:tr.ui.tracks.ChartTrack.prototype,decorate(viewport){tr.ui.tracks.ChartTrack.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('counter-track');},get counter(){return this.chart;},set counter(counter){this.heading=counter.name+': ';this.series=CounterTrack.buildChartSeriesFromCounter(counter);this.autoSetAllAxes({expandMax:true});},getModelEventFromItem(chartValue){return chartValue;}};CounterTrack.buildChartSeriesFromCounter=function(counter){const numSeries=counter.series.length;const totals=counter.totals;const seriesYAxis=new tr.ui.tracks.ChartSeriesYAxis(0,undefined);const chartSeries=counter.series.map(function(series,seriesIndex){const chartPoints=series.samples.map(function(sample,sampleIndex){const total=totals[sampleIndex*numSeries+seriesIndex];return new tr.ui.tracks.ChartPoint(sample,sample.timestamp,total);});const renderingConfig={chartType:tr.ui.tracks.ChartSeriesType.AREA,colorId:series.color};return new tr.ui.tracks.ChartSeries(chartPoints,seriesYAxis,renderingConfig);});chartSeries.reverse();return chartSeries;};return{CounterTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const startCompare=function(x,y){return x.start-y.start;};const FrameTrack=tr.ui.b.define('frame-track',tr.ui.tracks.LetterDotTrack);FrameTrack.prototype={__proto__:tr.ui.tracks.LetterDotTrack.prototype,decorate(viewport){tr.ui.tracks.LetterDotTrack.prototype.decorate.call(this,viewport);this.heading='Frames';this.frames_=undefined;this.items=undefined;},get frames(){return this.frames_;},set frames(frames){this.frames_=frames;if(frames===undefined)return;this.frames_=this.frames_.slice();this.frames_.sort(startCompare);this.items=this.frames_.map(function(frame){return new FrameDot(frame);});}};function FrameDot(frame){tr.ui.tracks.LetterDot.call(this,frame,'F',frame.colorId,frame.start);}
+FrameDot.prototype={__proto__:tr.ui.tracks.LetterDot.prototype};return{FrameTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const MultiRowTrack=tr.ui.b.define('multi-row-track',tr.ui.tracks.ContainerTrack);MultiRowTrack.prototype={__proto__:tr.ui.tracks.ContainerTrack.prototype,decorate(viewport){tr.ui.tracks.ContainerTrack.prototype.decorate.call(this,viewport);this.tooltip_='';this.heading_='';this.groupingSource_=undefined;this.itemsToGroup_=undefined;this.defaultToCollapsedWhenSubRowCountMoreThan=1;this.currentSubRowsWithHeadings_=undefined;this.expanded_=true;},get itemsToGroup(){return this.itemsToGroup_;},setItemsToGroup(itemsToGroup,opt_groupingSource){this.itemsToGroup_=itemsToGroup;this.groupingSource_=opt_groupingSource;this.currentSubRowsWithHeadings_=undefined;this.updateContents_();this.updateExpandedStateFromGroupingSource_();},setPrebuiltSubRows(groupingSource,subRowsWithHeadings){this.itemsToGroup_=undefined;this.groupingSource_=groupingSource;this.currentSubRowsWithHeadings_=subRowsWithHeadings;this.updateContents_();this.updateExpandedStateFromGroupingSource_();},get heading(){return this.heading_;},set heading(h){this.heading_=h;this.updateHeadingAndTooltip_();},get tooltip(){return this.tooltip_;},set tooltip(t){this.tooltip_=t;this.updateHeadingAndTooltip_();},get subRows(){return this.currentSubRowsWithHeadings_.map(elem=>elem.row);},get hasVisibleContent(){return this.children.length>0;},get expanded(){return this.expanded_;},set expanded(expanded){if(this.expanded_===expanded)return;this.expanded_=expanded;this.expandedStateChanged_();},onHeadingClicked_(e){if(this.subRows.length<=1)return;this.expanded=!this.expanded;if(this.groupingSource_){const modelSettings=new tr.model.ModelSettings(this.groupingSource_.model);modelSettings.setSettingFor(this.groupingSource_,'expanded',this.expanded);}
+e.stopPropagation();},updateExpandedStateFromGroupingSource_(){if(this.groupingSource_){const numSubRows=this.subRows.length;const modelSettings=new tr.model.ModelSettings(this.groupingSource_.model);if(numSubRows>1){let defaultExpanded;if(numSubRows>this.defaultToCollapsedWhenSubRowCountMoreThan){defaultExpanded=false;}else{defaultExpanded=true;}
+this.expanded=modelSettings.getSettingFor(this.groupingSource_,'expanded',defaultExpanded);}else{this.expanded=undefined;}}},expandedStateChanged_(){const children=this.children;const minH=Math.max(2,Math.ceil(18/children.length));const h=(this.expanded_?18:minH)+'px';for(let i=0;i<children.length;i++){children[i].height=h;if(i===0){children[i].arrowVisible=true;}
+children[i].expanded=this.expanded;}
+if(children.length===1){children[0].expanded=true;children[0].arrowVisible=false;}},updateContents_(){tr.ui.tracks.ContainerTrack.prototype.updateContents_.call(this);this.detach();if(this.currentSubRowsWithHeadings_===undefined){if(this.itemsToGroup_===undefined){return;}
+const subRows=this.buildSubRows_(this.itemsToGroup_);this.currentSubRowsWithHeadings_=subRows.map(row=>{return{row,heading:undefined};});}
+if(this.currentSubRowsWithHeadings_===undefined||this.currentSubRowsWithHeadings_.length===0){return;}
+const addSubTrackEx=(items,opt_heading)=>{const track=this.addSubTrack_(items);if(opt_heading!==undefined){track.heading=opt_heading;}
+track.addEventListener('heading-clicked',this.onHeadingClicked_.bind(this));};if(this.currentSubRowsWithHeadings_[0].heading!==undefined&&this.currentSubRowsWithHeadings_[0].heading!==this.heading_){addSubTrackEx([]);}
+for(const subRowWithHeading of this.currentSubRowsWithHeadings_){const subRow=subRowWithHeading.row;if(subRow.length===0){continue;}
+addSubTrackEx(subRow,subRowWithHeading.heading);}
+this.updateHeadingAndTooltip_();this.expandedStateChanged_();},updateHeadingAndTooltip_(){if(!Polymer.dom(this).firstChild)return;Polymer.dom(this).firstChild.heading=this.heading_;Polymer.dom(this).firstChild.tooltip=this.tooltip_;},buildSubRows_(itemsToGroup){throw new Error('Not implemented');},addSubTrack_(subRowItems){throw new Error('Not implemented');},areArrayContentsSame_(a,b){if(!a||!b)return false;if(!a.length||!b.length)return false;if(a.length!==b.length)return false;for(let i=0;i<a.length;++i){if(a[i]!==b[i])return false;}
+return true;}};return{MultiRowTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const ObjectInstanceGroupTrack=tr.ui.b.define('object-instance-group-track',tr.ui.tracks.MultiRowTrack);ObjectInstanceGroupTrack.prototype={__proto__:tr.ui.tracks.MultiRowTrack.prototype,decorate(viewport){tr.ui.tracks.MultiRowTrack.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('object-instance-group-track');this.objectInstances_=undefined;},get objectInstances(){return this.itemsToGroup;},set objectInstances(objectInstances){this.setItemsToGroup(objectInstances);},addSubTrack_(objectInstances){const hasMultipleRows=this.subRows.length>1;const track=new tr.ui.tracks.ObjectInstanceTrack(this.viewport);track.objectInstances=objectInstances;Polymer.dom(this).appendChild(track);return track;},buildSubRows_(objectInstances){objectInstances.sort(function(x,y){return x.creationTs-y.creationTs;});const subRows=[];for(let i=0;i<objectInstances.length;i++){const objectInstance=objectInstances[i];let found=false;for(let j=0;j<subRows.length;j++){const subRow=subRows[j];const lastItemInSubRow=subRow[subRow.length-1];if(objectInstance.creationTs>=lastItemInSubRow.deletionTs){found=true;subRow.push(objectInstance);break;}}
+if(!found){subRows.push([objectInstance]);}}
+return subRows;},updateHeadingAndTooltip_(){}};return{ObjectInstanceGroupTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const AsyncSliceGroupTrack=tr.ui.b.define('async-slice-group-track',tr.ui.tracks.MultiRowTrack);AsyncSliceGroupTrack.prototype={__proto__:tr.ui.tracks.MultiRowTrack.prototype,decorate(viewport){tr.ui.tracks.MultiRowTrack.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('async-slice-group-track');this.group_=undefined;},addSubTrack_(slices){const track=new tr.ui.tracks.SliceTrack(this.viewport);track.slices=slices;Polymer.dom(this).appendChild(track);track.asyncStyle=true;return track;},get group(){return this.group_;},set group(group){this.group_=group;this.buildAndSetSubRows_();},get eventContainer(){return this.group;},addContainersToTrackMap(containerToTrackMap){tr.ui.tracks.MultiRowTrack.prototype.addContainersToTrackMap.apply(this,arguments);containerToTrackMap.addContainer(this.group,this);},buildAndSetSubRows_(){if(this.group_.viewSubGroups.length<=1){const rows=groupAsyncSlicesIntoSubRows(this.group_.slices);const rowsWithHeadings=rows.map(row=>{return{row,heading:undefined};});this.setPrebuiltSubRows(this.group_,rowsWithHeadings);return;}
+const rowsWithHeadings=[];for(const subGroup of this.group_.viewSubGroups){const subGroupRows=groupAsyncSlicesIntoSubRows(subGroup.slices);if(subGroupRows.length===0){continue;}
+for(let i=0;i<subGroupRows.length;i++){rowsWithHeadings.push({row:subGroupRows[i],heading:(i===0?subGroup.title:'')});}}
+this.setPrebuiltSubRows(this.group_,rowsWithHeadings);}};function stripSlice_(slice){if(slice.subSlices!==undefined&&slice.subSlices.length===1){const subSlice=slice.subSlices[0];if(tr.b.math.approximately(subSlice.start,slice.start,1)&&tr.b.math.approximately(subSlice.duration,slice.duration,1)){return subSlice;}}
+return slice;}
+function makeLevelSubRows_(slices){const rows=[];const putSlice=(slice,level)=>{while(rows.length<=level){rows.push([]);}
+rows[level].push(slice);};const putSliceRecursively=(slice,level)=>{putSlice(slice,level);if(slice.subSlices!==undefined){for(const subSlice of slice.subSlices){putSliceRecursively(subSlice,level+1);}}};for(const slice of slices){putSliceRecursively(stripSlice_(slice),0);}
+return rows;}
+function groupAsyncSlicesIntoSubRows(slices,opt_skipSort){if(!opt_skipSort){slices.sort((x,y)=>x.start-y.start);}
+const rows=[];let slicesLeft=slices;while(slicesLeft.length!==0){const fit=[];const unfit=[];let levelEndTime=-1;for(const slice of slicesLeft){if(slice.start>=levelEndTime){levelEndTime=slice.end;fit.push(slice);}else{unfit.push(slice);}}
+rows.push(...makeLevelSubRows_(fit));slicesLeft=unfit;}
+return rows;}
+return{AsyncSliceGroupTrack,groupAsyncSlicesIntoSubRows,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const SampleTrack=tr.ui.b.define('sample-track',tr.ui.tracks.RectTrack);SampleTrack.prototype={__proto__:tr.ui.tracks.RectTrack.prototype,decorate(viewport){tr.ui.tracks.RectTrack.prototype.decorate.call(this,viewport);},get samples(){return this.rects;},set samples(samples){this.rects=samples;}};return{SampleTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const SliceGroupTrack=tr.ui.b.define('slice-group-track',tr.ui.tracks.MultiRowTrack);SliceGroupTrack.prototype={__proto__:tr.ui.tracks.MultiRowTrack.prototype,decorate(viewport){tr.ui.tracks.MultiRowTrack.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('slice-group-track');this.group_=undefined;this.defaultToCollapsedWhenSubRowCountMoreThan=100;},addSubTrack_(slices){const track=new tr.ui.tracks.SliceTrack(this.viewport);track.slices=slices;Polymer.dom(this).appendChild(track);return track;},get group(){return this.group_;},set group(group){this.group_=group;this.setItemsToGroup(this.group_.slices,this.group_);},get eventContainer(){return this.group;},addContainersToTrackMap(containerToTrackMap){tr.ui.tracks.MultiRowTrack.prototype.addContainersToTrackMap.apply(this,arguments);containerToTrackMap.addContainer(this.group,this);},buildSubRows_(slices){const precisionUnit=this.group.model.intrinsicTimeUnit;if(!slices.length)return[];const ops=[];for(let i=0;i<slices.length;i++){if(slices[i].subSlices){slices[i].subSlices.splice(0,slices[i].subSlices.length);}
+ops.push(i);}
+ops.sort(function(ix,iy){const x=slices[ix];const y=slices[iy];if(x.start!==y.start)return x.start-y.start;return ix-iy;});const subRows=[[]];this.badSlices_=[];for(let i=0;i<ops.length;i++){const op=ops[i];const slice=slices[op];let inserted=false;for(let j=subRows.length-1;j>=0;j--){if(subRows[j].length===0)continue;const insertedSlice=subRows[j][subRows[j].length-1];if(slice.start<insertedSlice.start){this.badSlices_.push(slice);inserted=true;}
+if(insertedSlice.bounds(slice,precisionUnit)){while(subRows.length<=j+1){subRows.push([]);}
+subRows[j+1].push(slice);if(insertedSlice.subSlices){insertedSlice.subSlices.push(slice);}
+inserted=true;break;}}
+if(inserted)continue;subRows[0].push(slice);}
+return subRows;}};return{SliceGroupTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const ThreadTrack=tr.ui.b.define('thread-track',tr.ui.tracks.ContainerTrack);ThreadTrack.prototype={__proto__:tr.ui.tracks.ContainerTrack.prototype,decorate(viewport){tr.ui.tracks.ContainerTrack.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('thread-track');this.heading_=document.createElement('tr-ui-b-heading');},get thread(){return this.thread_;},set thread(thread){this.thread_=thread;this.updateContents_();},get hasVisibleContent(){return this.tracks_.length>0;},get hasSlices(){return this.thread_.asyncSliceGroup.length>0||this.thread_.sliceGroup.length>0;},get hasTimeSlices(){return this.thread_.timeSlices;},get eventContainer(){return this.thread;},addContainersToTrackMap(containerToTrackMap){tr.ui.tracks.ContainerTrack.prototype.addContainersToTrackMap.apply(this,arguments);containerToTrackMap.addContainer(this.thread,this);},updateContents_(){this.detach();if(!this.thread_)return;this.heading_.heading=this.thread_.userFriendlyName;this.heading_.tooltip=this.thread_.userFriendlyDetails;if(this.thread_.asyncSliceGroup.length){this.appendAsyncSliceTracks_();}
+this.appendThreadSamplesTracks_();let needsHeading=false;if(this.thread_.timeSlices){const timeSlicesTrack=new tr.ui.tracks.SliceTrack(this.viewport);timeSlicesTrack.heading='';timeSlicesTrack.height=tr.ui.b.THIN_SLICE_HEIGHT+'px';timeSlicesTrack.slices=this.thread_.timeSlices;if(timeSlicesTrack.hasVisibleContent){needsHeading=true;Polymer.dom(this).appendChild(timeSlicesTrack);}}
+if(this.thread_.sliceGroup.length){const track=new tr.ui.tracks.SliceGroupTrack(this.viewport);track.heading=this.thread_.userFriendlyName;track.tooltip=this.thread_.userFriendlyDetails;track.group=this.thread_.sliceGroup;if(track.hasVisibleContent){needsHeading=false;Polymer.dom(this).appendChild(track);}}
+if(needsHeading){Polymer.dom(this).appendChild(this.heading_);}},appendAsyncSliceTracks_(){const subGroups=this.thread_.asyncSliceGroup.viewSubGroups;subGroups.forEach(function(subGroup){const asyncTrack=new tr.ui.tracks.AsyncSliceGroupTrack(this.viewport);asyncTrack.group=subGroup;asyncTrack.heading=subGroup.title;if(asyncTrack.hasVisibleContent){Polymer.dom(this).appendChild(asyncTrack);}},this);},appendThreadSamplesTracks_(){const threadSamples=this.thread_.samples;if(threadSamples===undefined||threadSamples.length===0){return;}
+const samplesByTitle={};threadSamples.forEach(function(sample){if(samplesByTitle[sample.title]===undefined){samplesByTitle[sample.title]=[];}
+samplesByTitle[sample.title].push(sample);});const sampleTitles=Object.keys(samplesByTitle);sampleTitles.sort();sampleTitles.forEach(function(sampleTitle){const samples=samplesByTitle[sampleTitle];const samplesTrack=new tr.ui.tracks.SampleTrack(this.viewport);samplesTrack.group=this.thread_;samplesTrack.samples=samples;samplesTrack.heading=this.thread_.userFriendlyName+': '+
+sampleTitle;samplesTrack.tooltip=this.thread_.userFriendlyDetails;samplesTrack.selectionGenerator=function(){const selection=new tr.model.EventSet();for(let i=0;i<samplesTrack.samples.length;i++){selection.push(samplesTrack.samples[i]);}
+return selection;};Polymer.dom(this).appendChild(samplesTrack);},this);},collapsedDidChange(collapsed){if(collapsed){let h=parseInt(this.tracks[0].height);for(let i=0;i<this.tracks.length;++i){if(h>2){this.tracks[i].height=Math.floor(h)+'px';}else{this.tracks[i].style.display='none';}
+h=h*0.5;}}else{for(let i=0;i<this.tracks.length;++i){this.tracks[i].height=this.tracks[0].height;this.tracks[i].style.display='';}}}};return{ThreadTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const OtherThreadsTrack=tr.ui.b.define('other-threads-track',tr.ui.tracks.OtherThreadsTrack);const SpacingTrack=tr.ui.tracks.SpacingTrack;OtherThreadsTrack.prototype={__proto__:tr.ui.tracks.ContainerTrack.prototype,decorate(viewport){tr.ui.tracks.ContainerTrack.prototype.decorate.call(this,viewport);this.header_=document.createElement('tr-ui-b-heading');this.header_.addEventListener('click',this.onHeaderClick_.bind(this));this.header_.heading='Other Threads';this.header_.tooltip='Threads with only scheduling information';this.header_.arrowVisible=true;this.threads_=[];this.expanded=false;this.collapsible_=true;},set threads(threads){this.threads_=threads;this.updateContents_();},set collapsible(collapsible){this.collapsible_=collapsible;this.updateContents_();},onHeaderClick_(e){e.stopPropagation();e.preventDefault();this.expanded=!this.expanded;},get expanded(){return this.header_.expanded;},set expanded(expanded){expanded=!!expanded;if(this.expanded===expanded)return;this.header_.expanded=expanded;this.viewport_.dispatchChangeEvent();this.updateContents_();},updateContents_(){this.detach();if(this.collapsible_){Polymer.dom(this).appendChild(this.header_);}
+if(this.expanded||!this.collapsible_){for(const thread of this.threads_){const track=new tr.ui.tracks.ThreadTrack(this.viewport);track.thread=thread;if(!track.hasVisibleContent)return;Polymer.dom(this).appendChild(track);Polymer.dom(this).appendChild(new SpacingTrack(this.viewport));}}}};return{OtherThreadsTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const ColorScheme=tr.b.ColorScheme;const ProcessSummaryTrack=tr.ui.b.define('process-summary-track',tr.ui.tracks.RectTrack);ProcessSummaryTrack.buildRectsFromProcess=function(process){if(!process)return[];const ops=[];const pushOp=function(isStart,time,slice){ops.push({isStart,time,slice});};for(const tid in process.threads){const sliceGroup=process.threads[tid].sliceGroup;sliceGroup.topLevelSlices.forEach(function(slice){pushOp(true,slice.start,undefined);pushOp(false,slice.end,undefined);});sliceGroup.slices.forEach(function(slice){if(slice.important){pushOp(true,slice.start,slice);pushOp(false,slice.end,slice);}});}
+ops.sort(function(a,b){return a.time-b.time;});const rects=[];const genericColorId=ColorScheme.getColorIdForReservedName('generic_work');const pushRect=function(start,end,slice){rects.push(new tr.ui.tracks.Rect(slice,slice?slice.title:'',slice?slice.colorId:genericColorId,start,end-start));};let depth=0;let currentSlice=undefined;let lastStart=undefined;ops.forEach(function(op){depth+=op.isStart?1:-1;if(currentSlice){if(!op.isStart&&op.slice===currentSlice){pushRect(lastStart,op.time,currentSlice);lastStart=depth>=1?op.time:undefined;currentSlice=undefined;}}else{if(op.isStart){if(depth===1){lastStart=op.time;currentSlice=op.slice;}else if(op.slice){if(op.time!==lastStart){pushRect(lastStart,op.time,undefined);lastStart=op.time;}
+currentSlice=op.slice;}}else{if(depth===0){pushRect(lastStart,op.time,undefined);lastStart=undefined;}}}});return rects;};ProcessSummaryTrack.prototype={__proto__:tr.ui.tracks.RectTrack.prototype,decorate(viewport){tr.ui.tracks.RectTrack.prototype.decorate.call(this,viewport);},get process(){return this.process_;},set process(process){this.process_=process;this.rects=ProcessSummaryTrack.buildRectsFromProcess(process);}};return{ProcessSummaryTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const ObjectSnapshotView=tr.ui.analysis.ObjectSnapshotView;const ObjectInstanceView=tr.ui.analysis.ObjectInstanceView;const SpacingTrack=tr.ui.tracks.SpacingTrack;const ProcessTrackBase=tr.ui.b.define('process-track-base',tr.ui.tracks.ContainerTrack);ProcessTrackBase.prototype={__proto__:tr.ui.tracks.ContainerTrack.prototype,decorate(viewport){tr.ui.tracks.ContainerTrack.prototype.decorate.call(this,viewport);this.processBase_=undefined;Polymer.dom(this).classList.add('process-track-base');Polymer.dom(this).classList.add('expanded');this.processNameEl_=tr.ui.b.createSpan();Polymer.dom(this.processNameEl_).classList.add('process-track-name');this.closeEl_=tr.ui.b.createSpan();Polymer.dom(this.closeEl_).classList.add('process-track-close');this.closeEl_.textContent='X';this.headerEl_=tr.ui.b.createDiv({className:'process-track-header'});Polymer.dom(this.headerEl_).appendChild(this.processNameEl_);Polymer.dom(this.headerEl_).appendChild(this.closeEl_);this.headerEl_.addEventListener('click',this.onHeaderClick_.bind(this));Polymer.dom(this).appendChild(this.headerEl_);},get processBase(){return this.processBase_;},set processBase(processBase){this.processBase_=processBase;if(this.processBase_){const modelSettings=new tr.model.ModelSettings(this.processBase_.model);const defaultValue=this.processBase_.important;this.expanded=modelSettings.getSettingFor(this.processBase_,'expanded',defaultValue);}
+this.updateContents_();},get expanded(){return Polymer.dom(this).classList.contains('expanded');},set expanded(expanded){expanded=!!expanded;if(this.expanded===expanded)return;Polymer.dom(this).classList.toggle('expanded');this.viewport_.dispatchChangeEvent();if(!this.processBase_)return;const modelSettings=new tr.model.ModelSettings(this.processBase_.model);modelSettings.setSettingFor(this.processBase_,'expanded',expanded);this.updateContents_();this.viewport.rebuildEventToTrackMap();this.viewport.rebuildContainerToTrackMap();},set visible(visible){if(visible===this.visible)return;this.hidden=!visible;tr.b.dispatchSimpleEvent(this,'visibility');this.viewport_.dispatchChangeEvent();if(!this.processBase_)return;this.updateContents_();this.viewport.rebuildEventToTrackMap();this.viewport.rebuildContainerToTrackMap();},get visible(){return!this.hidden;},get hasVisibleContent(){if(this.expanded){return this.children.length>1;}
+return true;},onHeaderClick_(e){e.stopPropagation();e.preventDefault();if(e.target===this.closeEl_){this.visible=false;}else{this.expanded=!this.expanded;}},updateContents_(){this.clearTracks_();if(!this.processBase_)return;if(!this.visible)return;Polymer.dom(this.processNameEl_).textContent=this.processBase_.userFriendlyName;this.headerEl_.title=this.processBase_.userFriendlyDetails;this.willAppendTracks_();if(this.expanded){this.appendMemoryDumpTrack_();this.appendObjectInstanceTracks_();this.appendCounterTracks_();this.appendFrameTrack_();this.appendThreadTracks_();}else{this.appendSummaryTrack_();}
+this.didAppendTracks_();},willAppendTracks_(){},didAppendTracks_(){},appendMemoryDumpTrack_(){},appendSummaryTrack_(){const track=new tr.ui.tracks.ProcessSummaryTrack(this.viewport);track.process=this.process;if(!track.hasVisibleContent)return;Polymer.dom(this).appendChild(track);},appendFrameTrack_(){const frames=this.process?this.process.frames:undefined;if(!frames||!frames.length)return;const track=new tr.ui.tracks.FrameTrack(this.viewport);track.frames=frames;Polymer.dom(this).appendChild(track);},appendObjectInstanceTracks_(){const instancesByTypeName=this.processBase_.objects.getAllInstancesByTypeName();const instanceTypeNames=Object.keys(instancesByTypeName);instanceTypeNames.sort();let didAppendAtLeastOneTrack=false;instanceTypeNames.forEach(function(typeName){const allInstances=instancesByTypeName[typeName];let instanceViewInfo=ObjectInstanceView.getTypeInfo(undefined,typeName);let snapshotViewInfo=ObjectSnapshotView.getTypeInfo(undefined,typeName);if(instanceViewInfo&&!instanceViewInfo.metadata.showInTrackView){instanceViewInfo=undefined;}
+if(snapshotViewInfo&&!snapshotViewInfo.metadata.showInTrackView){snapshotViewInfo=undefined;}
+const hasViewInfo=instanceViewInfo||snapshotViewInfo;const visibleInstances=[];for(let i=0;i<allInstances.length;i++){const instance=allInstances[i];if(instance.snapshots.length===0)continue;if(instance.hasImplicitSnapshots&&!hasViewInfo)continue;visibleInstances.push(instance);}
+if(visibleInstances.length===0)return;let trackConstructor=tr.ui.tracks.ObjectInstanceTrack.getConstructor(undefined,typeName);if(!trackConstructor){snapshotViewInfo=ObjectSnapshotView.getTypeInfo(undefined,typeName);if(snapshotViewInfo&&snapshotViewInfo.metadata.showInstances){trackConstructor=tr.ui.tracks.ObjectInstanceGroupTrack;}else{trackConstructor=tr.ui.tracks.ObjectInstanceTrack;}}
+const track=new trackConstructor(this.viewport);track.objectInstances=visibleInstances;Polymer.dom(this).appendChild(track);didAppendAtLeastOneTrack=true;},this);if(didAppendAtLeastOneTrack){Polymer.dom(this).appendChild(new SpacingTrack(this.viewport));}},appendCounterTracks_(){const counters=Object.values(this.processBase.counters);counters.sort(tr.model.Counter.compare);counters.forEach(function(counter){const track=new tr.ui.tracks.CounterTrack(this.viewport);track.counter=counter;Polymer.dom(this).appendChild(track);Polymer.dom(this).appendChild(new SpacingTrack(this.viewport));}.bind(this));},appendThreadTracks_(){const threads=Object.values(this.processBase.threads);threads.sort(tr.model.Thread.compare);const otherThreads=[];let hasVisibleThreads=false;threads.forEach(function(thread){const track=new tr.ui.tracks.ThreadTrack(this.viewport);track.thread=thread;if(!track.hasVisibleContent)return;if(track.hasSlices){hasVisibleThreads=true;Polymer.dom(this).appendChild(track);Polymer.dom(this).appendChild(new SpacingTrack(this.viewport));}else if(track.hasTimeSlices){otherThreads.push(thread);}}.bind(this));if(otherThreads.length>0){const track=new tr.ui.tracks.OtherThreadsTrack(this.viewport);track.threads=otherThreads;track.collapsible=otherThreads.length>1&&hasVisibleThreads;Polymer.dom(this).appendChild(track);}}};return{ProcessTrackBase,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const Cpu=tr.model.Cpu;const CpuTrack=tr.ui.tracks.cpu_track;const ProcessTrackBase=tr.ui.tracks.ProcessTrackBase;const SpacingTrack=tr.ui.tracks.SpacingTrack;const KernelTrack=tr.ui.b.define('kernel-track',ProcessTrackBase);KernelTrack.prototype={__proto__:ProcessTrackBase.prototype,decorate(viewport){ProcessTrackBase.prototype.decorate.call(this,viewport);},set kernel(kernel){this.processBase=kernel;},get kernel(){return this.processBase;},get eventContainer(){return this.kernel;},get hasVisibleContent(){return this.children.length>1;},addContainersToTrackMap(containerToTrackMap){tr.ui.tracks.ProcessTrackBase.prototype.addContainersToTrackMap.call(this,containerToTrackMap);containerToTrackMap.addContainer(this.kernel,this);},willAppendTracks_(){const cpus=Object.values(this.kernel.cpus);cpus.sort(tr.model.Cpu.compare);let didAppendAtLeastOneTrack=false;for(let i=0;i<cpus.length;++i){const cpu=cpus[i];const track=new tr.ui.tracks.CpuTrack(this.viewport);track.detailedMode=this.expanded;track.cpu=cpu;if(!track.hasVisibleContent)continue;Polymer.dom(this).appendChild(track);didAppendAtLeastOneTrack=true;}
+if(didAppendAtLeastOneTrack){Polymer.dom(this).appendChild(new SpacingTrack(this.viewport));}}};return{KernelTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const InteractionTrack=tr.ui.b.define('interaction-track',tr.ui.tracks.MultiRowTrack);InteractionTrack.prototype={__proto__:tr.ui.tracks.MultiRowTrack.prototype,decorate(viewport){tr.ui.tracks.MultiRowTrack.prototype.decorate.call(this,viewport);this.heading='Interactions';this.subRows_=[];},set model(model){this.setItemsToGroup(model.userModel.expectations,{guid:tr.b.GUID.allocateSimple(),model,getSettingsKey(){return undefined;}});},buildSubRows_(slices){if(this.subRows_.length){return this.subRows_;}
+this.subRows_.push(...tr.ui.tracks.groupAsyncSlicesIntoSubRows(slices,true));return this.subRows_;},addSubTrack_(slices){const track=new tr.ui.tracks.SliceTrack(this.viewport);track.slices=slices;Polymer.dom(this).appendChild(track);return track;}};return{InteractionTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const ColorScheme=tr.b.ColorScheme;const LetterDotTrack=tr.ui.tracks.LetterDotTrack;const MemoryTrack=tr.ui.b.define('memory-track',LetterDotTrack);MemoryTrack.prototype={__proto__:LetterDotTrack.prototype,decorate(viewport){LetterDotTrack.prototype.decorate.call(this,viewport);this.classList.add('memory-track');this.heading='Memory Events';this.lowMemoryEvents_=undefined;},initialize(model){if(model!==undefined){this.lowMemoryEvents_=model.device.lowMemoryEvents;}else{this.lowMemoryEvents_=undefined;}
+if(this.hasVisibleContent){this.items=this.buildMemoryLetterDots_(this.lowMemoryEvents_);}},get hasVisibleContent(){return!!this.lowMemoryEvents_&&this.lowMemoryEvents_.length!==0;},buildMemoryLetterDots_(memoryEvents){return memoryEvents.map(memoryEvent=>new tr.ui.tracks.LetterDot(memoryEvent,'K',ColorScheme.getColorIdForReservedName('background_memory_dump'),memoryEvent.start));},};return{MemoryTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const ALLOCATED_MEMORY_TRACK_HEIGHT=50;const ProcessMemoryDumpTrack=tr.ui.b.define('process-memory-dump-track',tr.ui.tracks.ContainerTrack);ProcessMemoryDumpTrack.prototype={__proto__:tr.ui.tracks.ContainerTrack.prototype,decorate(viewport){tr.ui.tracks.ContainerTrack.prototype.decorate.call(this,viewport);this.memoryDumps_=undefined;},get memoryDumps(){return this.memoryDumps_;},set memoryDumps(memoryDumps){this.memoryDumps_=memoryDumps;this.updateContents_();},updateContents_(){this.clearTracks_();if(!this.memoryDumps_||!this.memoryDumps_.length)return;this.appendAllocatedMemoryTrack_();},appendAllocatedMemoryTrack_(){const series=tr.ui.tracks.buildProcessAllocatedMemoryChartSeries(this.memoryDumps_);if(!series)return;const track=new tr.ui.tracks.ChartTrack(this.viewport);track.heading='Memory per component';track.height=ALLOCATED_MEMORY_TRACK_HEIGHT+'px';track.series=series;track.autoSetAllAxes({expandMax:true});Polymer.dom(this).appendChild(track);}};return{ProcessMemoryDumpTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const ProcessTrackBase=tr.ui.tracks.ProcessTrackBase;const ProcessTrack=tr.ui.b.define('process-track',ProcessTrackBase);ProcessTrack.prototype={__proto__:ProcessTrackBase.prototype,decorate(viewport){tr.ui.tracks.ProcessTrackBase.prototype.decorate.call(this,viewport);},drawTrack(type){switch(type){case tr.ui.tracks.DrawType.INSTANT_EVENT:{if(!this.processBase.instantEvents||this.processBase.instantEvents.length===0){break;}
+const ctx=this.context();const pixelRatio=window.devicePixelRatio||1;const bounds=this.getBoundingClientRect();const canvasBounds=ctx.canvas.getBoundingClientRect();ctx.save();ctx.translate(0,pixelRatio*(bounds.top-canvasBounds.top));const dt=this.viewport.currentDisplayTransform;const viewLWorld=dt.xViewToWorld(0);const viewRWorld=dt.xViewToWorld(canvasBounds.width*pixelRatio);tr.ui.b.drawInstantSlicesAsLines(ctx,this.viewport.currentDisplayTransform,viewLWorld,viewRWorld,bounds.height,this.processBase.instantEvents,2);ctx.restore();break;}
+case tr.ui.tracks.DrawType.BACKGROUND:this.drawBackground_();return;}
+tr.ui.tracks.ContainerTrack.prototype.drawTrack.call(this,type);},drawBackground_(){const ctx=this.context();const canvasBounds=ctx.canvas.getBoundingClientRect();const pixelRatio=window.devicePixelRatio||1;const children=this.children;let draw=false;ctx.fillStyle='#eee';for(let i=0;i<children.length;++i){if(!(children[i]instanceof tr.ui.tracks.Track)||(children[i]instanceof tr.ui.tracks.SpacingTrack)){continue;}
+draw=!draw;if(!draw)continue;const bounds=children[i].getBoundingClientRect();ctx.fillRect(0,pixelRatio*(bounds.top-canvasBounds.top),ctx.canvas.width,pixelRatio*bounds.height);}},set process(process){this.processBase=process;},get process(){return this.processBase;},get eventContainer(){return this.process;},addContainersToTrackMap(containerToTrackMap){tr.ui.tracks.ProcessTrackBase.prototype.addContainersToTrackMap.apply(this,arguments);containerToTrackMap.addContainer(this.process,this);},appendMemoryDumpTrack_(){const processMemoryDumps=this.process.memoryDumps;if(processMemoryDumps.length){const pmdt=new tr.ui.tracks.ProcessMemoryDumpTrack(this.viewport_);pmdt.memoryDumps=processMemoryDumps;Polymer.dom(this).appendChild(pmdt);}},addIntersectingEventsInRangeToSelectionInWorldSpace(loWX,hiWX,viewPixWidthWorld,selection){function onPickHit(instantEvent){selection.push(instantEvent);}
+const instantEventWidth=2*viewPixWidthWorld;tr.b.iterateOverIntersectingIntervals(this.processBase.instantEvents,function(x){return x.start;},function(x){return x.duration+instantEventWidth;},loWX,hiWX,onPickHit.bind(this));tr.ui.tracks.ContainerTrack.prototype.addIntersectingEventsInRangeToSelectionInWorldSpace.apply(this,arguments);},addClosestEventToSelection(worldX,worldMaxDist,loY,hiY,selection){this.addClosestInstantEventToSelection(this.processBase.instantEvents,worldX,worldMaxDist,selection);tr.ui.tracks.ContainerTrack.prototype.addClosestEventToSelection.apply(this,arguments);}};return{ProcessTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const SelectionState=tr.model.SelectionState;const ColorScheme=tr.b.ColorScheme;const EventPresenter=tr.ui.b.EventPresenter;const ModelTrack=tr.ui.b.define('model-track',tr.ui.tracks.ContainerTrack);ModelTrack.VSYNC_HIGHLIGHT_ALPHA=0.1;ModelTrack.VSYNC_DENSITY_TRANSPARENT=0.20;ModelTrack.VSYNC_DENSITY_OPAQUE=0.10;ModelTrack.VSYNC_DENSITY_RANGE=ModelTrack.VSYNC_DENSITY_TRANSPARENT-ModelTrack.VSYNC_DENSITY_OPAQUE;ModelTrack.generateStripes_=function(times,minTime,maxTime){if(times.length===0)return[];const lowIndex=tr.b.findLowIndexInSortedArray(times,(x=>x),minTime);let highIndex=lowIndex-1;while(times[highIndex+1]<=maxTime){highIndex++;}
+const stripes=[];for(let i=lowIndex-(lowIndex%2);i<=highIndex;i+=2){const left=i<lowIndex?minTime:times[i];const right=i+1>highIndex?maxTime:times[i+1];stripes.push(tr.b.math.Range.fromExplicitRange(left,right));}
+return stripes;};ModelTrack.prototype={__proto__:tr.ui.tracks.ContainerTrack.prototype,decorate(viewport){tr.ui.tracks.ContainerTrack.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('model-track');this.upperMode_=false;this.annotationViews_=[];this.vSyncTimes_=[];},get processViews(){return Polymer.dom(this).querySelectorAll('.process-track-base');},get upperMode(){return this.upperMode_;},set upperMode(upperMode){this.upperMode_=upperMode;this.updateContents_();},detach(){tr.ui.tracks.ContainerTrack.prototype.detach.call(this);},get model(){return this.model_;},set model(model){this.model_=model;this.updateContents_();this.model_.addEventListener('annotationChange',this.updateAnnotations_.bind(this));},get hasVisibleContent(){return this.children.length>0;},updateContents_(){Polymer.dom(this).textContent='';if(!this.model_)return;if(this.upperMode_){this.updateContentsForUpperMode_();}else{this.updateContentsForLowerMode_();}},updateContentsForUpperMode_(){},updateContentsForLowerMode_(){if(this.model_.userModel.expectations.length>1){const mrt=new tr.ui.tracks.InteractionTrack(this.viewport_);mrt.model=this.model_;Polymer.dom(this).appendChild(mrt);}
+if(this.model_.alerts.length){const at=new tr.ui.tracks.AlertTrack(this.viewport_);at.alerts=this.model_.alerts;Polymer.dom(this).appendChild(at);}
+if(this.model_.globalMemoryDumps.length){const gmdt=new tr.ui.tracks.GlobalMemoryDumpTrack(this.viewport_);gmdt.memoryDumps=this.model_.globalMemoryDumps;Polymer.dom(this).appendChild(gmdt);}
+this.appendDeviceTrack_();this.appendCpuUsageTrack_();this.appendMemoryTrack_();this.appendKernelTrack_();const processes=this.model_.getAllProcesses();processes.sort(tr.model.Process.compare);for(let i=0;i<processes.length;++i){const process=processes[i];const track=new tr.ui.tracks.ProcessTrack(this.viewport);track.process=process;if(!track.hasVisibleContent)continue;Polymer.dom(this).appendChild(track);}
+this.viewport_.rebuildEventToTrackMap();this.viewport_.rebuildContainerToTrackMap();this.vSyncTimes_=this.model_.device.vSyncTimestamps;this.updateAnnotations_();},getContentBounds(){return this.model.bounds;},addAnnotation(annotation){this.model.addAnnotation(annotation);},removeAnnotation(annotation){this.model.removeAnnotation(annotation);},updateAnnotations_(){this.annotationViews_=[];const annotations=this.model_.getAllAnnotations();for(let i=0;i<annotations.length;i++){this.annotationViews_.push(annotations[i].getOrCreateView(this.viewport_));}
+this.invalidateDrawingContainer();},addEventsToTrackMap(eventToTrackMap){if(!this.model_)return;const tracks=this.children;for(let i=0;i<tracks.length;++i){tracks[i].addEventsToTrackMap(eventToTrackMap);}
+if(this.instantEvents===undefined)return;const vp=this.viewport_;this.instantEvents.forEach(function(ev){eventToTrackMap.addEvent(ev,this);}.bind(this));},appendDeviceTrack_(){const device=this.model.device;const track=new tr.ui.tracks.DeviceTrack(this.viewport);track.device=this.model.device;if(!track.hasVisibleContent)return;Polymer.dom(this).appendChild(track);},appendKernelTrack_(){const kernel=this.model.kernel;const track=new tr.ui.tracks.KernelTrack(this.viewport);track.kernel=this.model.kernel;if(!track.hasVisibleContent)return;Polymer.dom(this).appendChild(track);},appendCpuUsageTrack_(){const track=new tr.ui.tracks.CpuUsageTrack(this.viewport);track.initialize(this.model);if(!track.hasVisibleContent)return;this.appendChild(track);},appendMemoryTrack_(){const track=new tr.ui.tracks.MemoryTrack(this.viewport);track.initialize(this.model);if(!track.hasVisibleContent)return;Polymer.dom(this).appendChild(track);},drawTrack(type){const ctx=this.context();if(!this.model_)return;const pixelRatio=window.devicePixelRatio||1;const bounds=this.getBoundingClientRect();const canvasBounds=ctx.canvas.getBoundingClientRect();ctx.save();ctx.translate(0,pixelRatio*(bounds.top-canvasBounds.top));const dt=this.viewport.currentDisplayTransform;const viewLWorld=dt.xViewToWorld(0);const viewRWorld=dt.xViewToWorld(canvasBounds.width*pixelRatio);const viewHeight=bounds.height*pixelRatio;switch(type){case tr.ui.tracks.DrawType.GRID:this.viewport.drawMajorMarkLines(ctx,viewHeight);ctx.restore();return;case tr.ui.tracks.DrawType.FLOW_ARROWS:if(this.model_.flowIntervalTree.size===0){ctx.restore();return;}
+this.drawFlowArrows_(viewLWorld,viewRWorld);ctx.restore();return;case tr.ui.tracks.DrawType.INSTANT_EVENT:if(!this.model_.instantEvents||this.model_.instantEvents.length===0){break;}
+tr.ui.b.drawInstantSlicesAsLines(ctx,this.viewport.currentDisplayTransform,viewLWorld,viewRWorld,bounds.height,this.model_.instantEvents,4);break;case tr.ui.tracks.DrawType.MARKERS:if(!this.viewport.interestRange.isEmpty){this.viewport.interestRange.draw(ctx,viewLWorld,viewRWorld,viewHeight);this.viewport.interestRange.drawIndicators(ctx,viewLWorld,viewRWorld);}
+ctx.restore();return;case tr.ui.tracks.DrawType.HIGHLIGHTS:this.drawVSyncHighlight(ctx,dt,viewLWorld,viewRWorld,viewHeight);ctx.restore();return;case tr.ui.tracks.DrawType.ANNOTATIONS:for(let i=0;i<this.annotationViews_.length;i++){this.annotationViews_[i].draw(ctx);}
+ctx.restore();return;}
+ctx.restore();tr.ui.tracks.ContainerTrack.prototype.drawTrack.call(this,type);},drawFlowArrows_(viewLWorld,viewRWorld){const ctx=this.context();ctx.strokeStyle='rgba(0, 0, 0, 0.4)';ctx.fillStyle='rgba(0, 0, 0, 0.4)';ctx.lineWidth=1;const events=this.model_.flowIntervalTree.findIntersection(viewLWorld,viewRWorld);const canvasBounds=ctx.canvas.getBoundingClientRect();for(let i=0;i<events.length;++i){const onlyHighlighted=!tr.b.getCategoryParts(events[i].category).some((x)=>this.viewport.selectedFlowEvents.has(x));if(onlyHighlighted&&events[i].selectionState!==SelectionState.SELECTED&&events[i].selectionState!==SelectionState.HIGHLIGHTED){continue;}
+this.drawFlowArrow_(ctx,events[i],canvasBounds);}},drawFlowArrow_(ctx,flowEvent,canvasBounds){const dt=this.viewport.currentDisplayTransform;const pixelRatio=window.devicePixelRatio||1;const startTrack=this.viewport.trackForEvent(flowEvent.startSlice);const endTrack=this.viewport.trackForEvent(flowEvent.endSlice);if(startTrack===undefined||endTrack===undefined)return;const startBounds=startTrack.getBoundingClientRect();const endBounds=endTrack.getBoundingClientRect();if(flowEvent.selectionState===SelectionState.SELECTED){ctx.shadowBlur=1;ctx.shadowColor='red';ctx.shadowOffsety=2;ctx.strokeStyle=tr.b.ColorScheme.colorsAsStrings[tr.b.ColorScheme.getVariantColorId(flowEvent.colorId,tr.b.ColorScheme.properties.brightenedOffsets[0])];}else if(flowEvent.selectionState===SelectionState.HIGHLIGHTED){ctx.shadowBlur=1;ctx.shadowColor='red';ctx.shadowOffsety=2;ctx.strokeStyle=tr.b.ColorScheme.colorsAsStrings[tr.b.ColorScheme.getVariantColorId(flowEvent.colorId,tr.b.ColorScheme.properties.brightenedOffsets[0])];}else if(flowEvent.selectionState===SelectionState.DIMMED){ctx.shadowBlur=0;ctx.shadowOffsetX=0;ctx.strokeStyle=tr.b.ColorScheme.colorsAsStrings[flowEvent.colorId];}else{let hasBoost=false;const startSlice=flowEvent.startSlice;hasBoost|=startSlice.selectionState===SelectionState.SELECTED;hasBoost|=startSlice.selectionState===SelectionState.HIGHLIGHTED;const endSlice=flowEvent.endSlice;hasBoost|=endSlice.selectionState===SelectionState.SELECTED;hasBoost|=endSlice.selectionState===SelectionState.HIGHLIGHTED;if(hasBoost){ctx.shadowBlur=1;ctx.shadowColor='rgba(255, 0, 0, 0.4)';ctx.shadowOffsety=2;ctx.strokeStyle=tr.b.ColorScheme.colorsAsStrings[tr.b.ColorScheme.getVariantColorId(flowEvent.colorId,tr.b.ColorScheme.properties.brightenedOffsets[0])];}else{ctx.shadowBlur=0;ctx.shadowOffsetX=0;ctx.strokeStyle=tr.b.ColorScheme.colorsAsStrings[flowEvent.colorId];}}
+const startSize=startBounds.left+startBounds.top+
+startBounds.bottom+startBounds.right;const endSize=endBounds.left+endBounds.top+
+endBounds.bottom+endBounds.right;if(startSize===0&&endSize===0)return;const startY=this.calculateTrackY_(startTrack,canvasBounds);const endY=this.calculateTrackY_(endTrack,canvasBounds);const worldOffset=this.getBoundingClientRect().top-canvasBounds.top;const pixelStartY=pixelRatio*(startY-worldOffset);const pixelEndY=pixelRatio*(endY-worldOffset);const startXView=dt.xWorldToView(flowEvent.start);const endXView=dt.xWorldToView(flowEvent.end);const midXView=(startXView+endXView)/2;ctx.beginPath();ctx.moveTo(startXView,pixelStartY);ctx.bezierCurveTo(midXView,pixelStartY,midXView,pixelEndY,endXView,pixelEndY);ctx.stroke();const arrowWidth=5*pixelRatio;const distance=endXView-startXView;if(distance<=(2*arrowWidth))return;const tipX=endXView;const tipY=pixelEndY;const arrowHeight=(endBounds.height/4)*pixelRatio;tr.ui.b.drawTriangle(ctx,tipX,tipY,tipX-arrowWidth,tipY-arrowHeight,tipX-arrowWidth,tipY+arrowHeight);ctx.fill();},drawVSyncHighlight(ctx,dt,viewLWorld,viewRWorld,viewHeight){if(!this.viewport_.highlightVSync){return;}
+const stripes=ModelTrack.generateStripes_(this.vSyncTimes_,viewLWorld,viewRWorld);if(stripes.length===0){return;}
+const vSyncHighlightColor=new tr.b.Color(ColorScheme.getColorForReservedNameAsString('vsync_highlight_color'));const stripeRange=stripes[stripes.length-1].max-stripes[0].min;const stripeDensity=stripeRange?stripes.length/(dt.scaleX*stripeRange):0;const clampedStripeDensity=tr.b.math.clamp(stripeDensity,ModelTrack.VSYNC_DENSITY_OPAQUE,ModelTrack.VSYNC_DENSITY_TRANSPARENT);const opacity=(ModelTrack.VSYNC_DENSITY_TRANSPARENT-clampedStripeDensity)/ModelTrack.VSYNC_DENSITY_RANGE;if(opacity===0){return;}
+ctx.fillStyle=vSyncHighlightColor.toStringWithAlphaOverride(ModelTrack.VSYNC_HIGHLIGHT_ALPHA*opacity);for(let i=0;i<stripes.length;i++){const xLeftView=dt.xWorldToView(stripes[i].min);const xRightView=dt.xWorldToView(stripes[i].max);ctx.fillRect(xLeftView,0,xRightView-xLeftView,viewHeight);}},calculateTrackY_(track,canvasBounds){const bounds=track.getBoundingClientRect();const size=bounds.left+bounds.top+bounds.bottom+bounds.right;if(size===0){return this.calculateTrackY_(Polymer.dom(track).parentNode,canvasBounds);}
+return bounds.top-canvasBounds.top+(bounds.height/2);},addIntersectingEventsInRangeToSelectionInWorldSpace(loWX,hiWX,viewPixWidthWorld,selection){function onPickHit(instantEvent){selection.push(instantEvent);}
+const instantEventWidth=3*viewPixWidthWorld;tr.b.iterateOverIntersectingIntervals(this.model_.instantEvents,function(x){return x.start;},function(x){return x.duration+instantEventWidth;},loWX,hiWX,onPickHit.bind(this));tr.ui.tracks.ContainerTrack.prototype.addIntersectingEventsInRangeToSelectionInWorldSpace.apply(this,arguments);},addClosestEventToSelection(worldX,worldMaxDist,loY,hiY,selection){this.addClosestInstantEventToSelection(this.model_.instantEvents,worldX,worldMaxDist,selection);tr.ui.tracks.ContainerTrack.prototype.addClosestEventToSelection.apply(this,arguments);}};return{ModelTrack,};});'use strict';tr.exportTo('tr.ui.tracks',function(){const XAxisTrack=tr.ui.b.define('x-axis-track',tr.ui.tracks.Track);XAxisTrack.prototype={__proto__:tr.ui.tracks.Track.prototype,decorate(viewport){tr.ui.tracks.Track.prototype.decorate.call(this,viewport);Polymer.dom(this).classList.add('x-axis-track');this.strings_secs_=[];this.strings_msecs_=[];this.strings_usecs_=[];this.strings_nsecs_=[];this.viewportChange_=this.viewportChange_.bind(this);viewport.addEventListener('change',this.viewportChange_);const heading=document.createElement('tr-ui-b-heading');heading.arrowVisible=false;Polymer.dom(this).appendChild(heading);},detach(){tr.ui.tracks.Track.prototype.detach.call(this);this.viewport.removeEventListener('change',this.viewportChange_);},viewportChange_(){if(this.viewport.interestRange.isEmpty){Polymer.dom(this).classList.remove('tall-mode');}else{Polymer.dom(this).classList.add('tall-mode');}},draw(type,viewLWorld,viewRWorld,viewHeight){switch(type){case tr.ui.tracks.DrawType.GRID:this.drawGrid_(viewLWorld,viewRWorld);break;case tr.ui.tracks.DrawType.MARKERS:this.drawMarkers_(viewLWorld,viewRWorld);break;}},drawGrid_(viewLWorld,viewRWorld){const ctx=this.context();const pixelRatio=window.devicePixelRatio||1;const canvasBounds=ctx.canvas.getBoundingClientRect();const trackBounds=this.getBoundingClientRect();const width=canvasBounds.width*pixelRatio;const height=trackBounds.height*pixelRatio;const hasInterestRange=!this.viewport.interestRange.isEmpty;const xAxisHeightPx=hasInterestRange?(height*2)/5:height;const vp=this.viewport;const dt=vp.currentDisplayTransform;vp.updateMajorMarkData(viewLWorld,viewRWorld);const majorMarkDistanceWorld=vp.majorMarkWorldPositions.length>1?vp.majorMarkWorldPositions[1]-vp.majorMarkWorldPositions[0]:0;const numTicksPerMajor=5;const minorMarkDistanceWorld=majorMarkDistanceWorld/numTicksPerMajor;const minorMarkDistancePx=dt.xWorldVectorToView(minorMarkDistanceWorld);const minorTickHeight=Math.floor(xAxisHeightPx*0.25);ctx.save();ctx.lineWidth=Math.round(pixelRatio);const crispLineCorrection=(ctx.lineWidth%2)/2;ctx.translate(crispLineCorrection,-crispLineCorrection);ctx.fillStyle='rgb(0, 0, 0)';ctx.strokeStyle='rgb(0, 0, 0)';ctx.textAlign='left';ctx.textBaseline='top';ctx.font=(9*pixelRatio)+'px sans-serif';const tickLabels=[];ctx.beginPath();for(let i=0;i<vp.majorMarkWorldPositions.length;i++){const curXWorld=vp.majorMarkWorldPositions[i];const curXView=dt.xWorldToView(curXWorld);const displayText=vp.majorMarkUnit.format(curXWorld,{deltaValue:majorMarkDistanceWorld});ctx.fillText(displayText,curXView+(2*pixelRatio),0);tr.ui.b.drawLine(ctx,curXView,0,curXView,xAxisHeightPx);if(minorMarkDistancePx){for(let j=1;j<numTicksPerMajor;++j){const xView=Math.floor(curXView+minorMarkDistancePx*j);tr.ui.b.drawLine(ctx,xView,xAxisHeightPx-minorTickHeight,xView,xAxisHeightPx);}}}
+ctx.strokeStyle='rgb(0, 0, 0)';tr.ui.b.drawLine(ctx,0,height,width,height);ctx.stroke();if(!hasInterestRange)return;tr.ui.b.drawLine(ctx,0,xAxisHeightPx,width,xAxisHeightPx);ctx.stroke();let displayDistance;const displayTextColor='rgb(0,0,0)';const arrowSpacing=10*pixelRatio;const arrowColor='rgb(128,121,121)';const arrowPosY=xAxisHeightPx*1.75;const arrowWidthView=3*pixelRatio;const arrowLengthView=10*pixelRatio;const spaceForArrowsView=2*(arrowWidthView+arrowSpacing);ctx.textBaseline='middle';ctx.font=(14*pixelRatio)+'px sans-serif';const textPosY=arrowPosY;const interestRange=vp.interestRange;if(interestRange.range===0){const markerWorld=interestRange.min;const markerView=dt.xWorldToView(markerWorld);const textToDraw=vp.majorMarkUnit.format(markerWorld);let textLeftView=markerView+4*pixelRatio;const textWidthView=ctx.measureText(textToDraw).width;if(textLeftView+textWidthView>width){textLeftView=markerView-4*pixelRatio-textWidthView;}
+ctx.fillStyle=displayTextColor;ctx.fillText(textToDraw,textLeftView,textPosY);return;}
+const leftMarker=interestRange.min;const rightMarker=interestRange.max;const leftMarkerView=dt.xWorldToView(leftMarker);const rightMarkerView=dt.xWorldToView(rightMarker);const distanceBetweenMarkers=interestRange.range;const distanceBetweenMarkersView=dt.xWorldVectorToView(distanceBetweenMarkers);const positionInMiddleOfMarkersView=leftMarkerView+(distanceBetweenMarkersView/2);const textToDraw=vp.majorMarkUnit.format(distanceBetweenMarkers);const textWidthView=ctx.measureText(textToDraw).width;const spaceForArrowsAndTextView=textWidthView+spaceForArrowsView+arrowSpacing;let textLeftView=positionInMiddleOfMarkersView-textWidthView/2;const textRightView=textLeftView+textWidthView;if(spaceForArrowsAndTextView>distanceBetweenMarkersView){textLeftView=rightMarkerView+2*arrowSpacing;if(textLeftView+textWidthView>width){textLeftView=leftMarkerView-2*arrowSpacing-textWidthView;}
+ctx.fillStyle=displayTextColor;ctx.fillText(textToDraw,textLeftView,textPosY);ctx.strokeStyle=arrowColor;ctx.beginPath();tr.ui.b.drawLine(ctx,leftMarkerView,arrowPosY,rightMarkerView,arrowPosY);ctx.stroke();ctx.fillStyle=arrowColor;tr.ui.b.drawArrow(ctx,leftMarkerView-1.5*arrowSpacing,arrowPosY,leftMarkerView,arrowPosY,arrowLengthView,arrowWidthView);tr.ui.b.drawArrow(ctx,rightMarkerView+1.5*arrowSpacing,arrowPosY,rightMarkerView,arrowPosY,arrowLengthView,arrowWidthView);}else if(spaceForArrowsView<=distanceBetweenMarkersView){let leftArrowStart;let rightArrowStart;if(spaceForArrowsAndTextView<=distanceBetweenMarkersView){ctx.fillStyle=displayTextColor;ctx.fillText(textToDraw,textLeftView,textPosY);leftArrowStart=textLeftView-arrowSpacing;rightArrowStart=textRightView+arrowSpacing;}else{leftArrowStart=positionInMiddleOfMarkersView;rightArrowStart=positionInMiddleOfMarkersView;}
+ctx.strokeStyle=arrowColor;ctx.fillStyle=arrowColor;tr.ui.b.drawArrow(ctx,leftArrowStart,arrowPosY,leftMarkerView,arrowPosY,arrowLengthView,arrowWidthView);tr.ui.b.drawArrow(ctx,rightArrowStart,arrowPosY,rightMarkerView,arrowPosY,arrowLengthView,arrowWidthView);}
+ctx.restore();},drawMarkers_(viewLWorld,viewRWorld){const pixelRatio=window.devicePixelRatio||1;const trackBounds=this.getBoundingClientRect();const viewHeight=trackBounds.height*pixelRatio;if(!this.viewport.interestRange.isEmpty){this.viewport.interestRange.draw(this.context(),viewLWorld,viewRWorld,viewHeight);}},addIntersectingEventsInRangeToSelection(loVX,hiVX,loY,hiY,selection){},addAllEventsMatchingFilterToSelection(filter,selection){}};return{XAxisTrack,};});'use strict';Polymer({is:'tr-ui-timeline-track-view',ready(){this.displayTransform_=new tr.ui.TimelineDisplayTransform();this.model_=undefined;this.timelineView_=undefined;this.pollIfViewportAttachedInterval_=undefined;this.viewport_=new tr.ui.TimelineViewport(this);this.viewportDisplayTransformAtMouseDown_=undefined;this.brushingStateController_=undefined;this.rulerTrackContainer_=new tr.ui.tracks.DrawingContainer(this.viewport_);Polymer.dom(this).appendChild(this.rulerTrackContainer_);this.rulerTrackContainer_.invalidate();this.rulerTrackContainer_.style.overflowY='hidden';this.rulerTrackContainer_.style.flexShrink='0';this.rulerTrack_=new tr.ui.tracks.XAxisTrack(this.viewport_);Polymer.dom(this.rulerTrackContainer_).appendChild(this.rulerTrack_);this.upperModelTrack_=new tr.ui.tracks.ModelTrack(this.viewport_);this.upperModelTrack_.upperMode=true;Polymer.dom(this.rulerTrackContainer_).appendChild(this.upperModelTrack_);this.modelTrackContainer_=new tr.ui.tracks.DrawingContainer(this.viewport_);Polymer.dom(this).appendChild(this.modelTrackContainer_);this.modelTrackContainer_.style.display='block';this.modelTrackContainer_.style.flexGrow='1';this.modelTrackContainer_.invalidate();this.viewport_.modelTrackContainer=this.modelTrackContainer_;this.modelTrack_=new tr.ui.tracks.ModelTrack(this.viewport_);Polymer.dom(this.modelTrackContainer_).appendChild(this.modelTrack_);this.timingTool_=new tr.ui.b.TimingTool(this.viewport_,this);this.initMouseModeSelector();this.hideDragBox_();this.initHintText_();this.onSelectionChanged_=this.onSelectionChanged_.bind(this);this.onDblClick_=this.onDblClick_.bind(this);this.addEventListener('dblclick',this.onDblClick_);this.onMouseWheel_=this.onMouseWheel_.bind(this);this.addEventListener('mousewheel',this.onMouseWheel_);this.onMouseDown_=this.onMouseDown_.bind(this);this.addEventListener('mousedown',this.onMouseDown_);this.onMouseMove_=this.onMouseMove_.bind(this);this.addEventListener('mousemove',this.onMouseMove_);this.onTouchStart_=this.onTouchStart_.bind(this);this.addEventListener('touchstart',this.onTouchStart_);this.onTouchMove_=this.onTouchMove_.bind(this);this.addEventListener('touchmove',this.onTouchMove_);this.onTouchEnd_=this.onTouchEnd_.bind(this);this.addEventListener('touchend',this.onTouchEnd_);this.addHotKeys_();this.mouseViewPosAtMouseDown_={x:0,y:0};this.lastMouseViewPos_={x:0,y:0};this.lastTouchViewPositions_=[];this.alert_=undefined;this.isPanningAndScanning_=false;this.isZooming_=false;},initMouseModeSelector(){this.mouseModeSelector_=document.createElement('tr-ui-b-mouse-mode-selector');this.mouseModeSelector_.targetElement=this;Polymer.dom(this).appendChild(this.mouseModeSelector_);this.mouseModeSelector_.addEventListener('beginpan',this.onBeginPanScan_.bind(this));this.mouseModeSelector_.addEventListener('updatepan',this.onUpdatePanScan_.bind(this));this.mouseModeSelector_.addEventListener('endpan',this.onEndPanScan_.bind(this));this.mouseModeSelector_.addEventListener('beginselection',this.onBeginSelection_.bind(this));this.mouseModeSelector_.addEventListener('updateselection',this.onUpdateSelection_.bind(this));this.mouseModeSelector_.addEventListener('endselection',this.onEndSelection_.bind(this));this.mouseModeSelector_.addEventListener('beginzoom',this.onBeginZoom_.bind(this));this.mouseModeSelector_.addEventListener('updatezoom',this.onUpdateZoom_.bind(this));this.mouseModeSelector_.addEventListener('endzoom',this.onEndZoom_.bind(this));this.mouseModeSelector_.addEventListener('entertiming',this.timingTool_.onEnterTiming.bind(this.timingTool_));this.mouseModeSelector_.addEventListener('begintiming',this.timingTool_.onBeginTiming.bind(this.timingTool_));this.mouseModeSelector_.addEventListener('updatetiming',this.timingTool_.onUpdateTiming.bind(this.timingTool_));this.mouseModeSelector_.addEventListener('endtiming',this.timingTool_.onEndTiming.bind(this.timingTool_));this.mouseModeSelector_.addEventListener('exittiming',this.timingTool_.onExitTiming.bind(this.timingTool_));const m=tr.ui.b.MOUSE_SELECTOR_MODE;this.mouseModeSelector_.supportedModeMask=m.SELECTION|m.PANSCAN|m.ZOOM|m.TIMING;this.mouseModeSelector_.settingsKey='timelineTrackView.mouseModeSelector';this.mouseModeSelector_.setKeyCodeForMode(m.PANSCAN,'2'.charCodeAt(0));this.mouseModeSelector_.setKeyCodeForMode(m.SELECTION,'1'.charCodeAt(0));this.mouseModeSelector_.setKeyCodeForMode(m.ZOOM,'3'.charCodeAt(0));this.mouseModeSelector_.setKeyCodeForMode(m.TIMING,'4'.charCodeAt(0));this.mouseModeSelector_.setModifierForAlternateMode(m.SELECTION,tr.ui.b.MODIFIER.SHIFT);this.mouseModeSelector_.setModifierForAlternateMode(m.PANSCAN,tr.ui.b.MODIFIER.SPACE);},get brushingStateController(){return this.brushingStateController_;},set brushingStateController(brushingStateController){if(this.brushingStateController_){this.brushingStateController_.removeEventListener('change',this.onSelectionChanged_);}
+this.brushingStateController_=brushingStateController;if(this.brushingStateController_){this.brushingStateController_.addEventListener('change',this.onSelectionChanged_);}},set timelineView(view){this.timelineView_=view;},get processViews(){return this.modelTrack_.processViews;},onSelectionChanged_(){this.showHintText_('Press \'m\' to mark current selection');this.viewport_.dispatchChangeEvent();},set selection(selection){throw new Error('DO NOT CALL THIS');},set highlight(highlight){throw new Error('DO NOT CALL THIS');},detach(){this.modelTrack_.detach();this.upperModelTrack_.detach();if(this.pollIfViewportAttachedInterval_){window.clearInterval(this.pollIfViewportAttachedInterval_);this.pollIfViewportAttachedInterval_=undefined;}
+this.viewport_.detach();},get viewport(){return this.viewport_;},get model(){return this.model_;},set model(model){if(!model){throw new Error('Model cannot be undefined');}
+const modelInstanceChanged=this.model_!==model;this.model_=model;this.modelTrack_.model=model;this.upperModelTrack_.model=model;if(modelInstanceChanged){this.pollIfViewportAttachedInterval_=window.setInterval(this.pollIfViewportAttached_.bind(this),250);}},get hasVisibleContent(){return this.modelTrack_.hasVisibleContent||this.upperModelTrack_.hasVisibleContent;},pollIfViewportAttached_(){if(!this.viewport_.isAttachedToDocumentOrInTestMode||this.viewport_.clientWidth===0){return;}
+window.addEventListener('resize',this.viewport_.dispatchChangeEvent);window.clearInterval(this.pollIfViewportAttachedInterval_);this.pollIfViewportAttachedInterval_=undefined;this.setInitialViewport_();},setInitialViewport_(){this.modelTrackContainer_.updateCanvasSizeIfNeeded_();const w=this.modelTrackContainer_.canvas.width;let min;let range;if(this.model_.bounds.isEmpty){min=0;range=1000;}else if(this.model_.bounds.range===0){min=this.model_.bounds.min;range=1000;}else{min=this.model_.bounds.min;range=this.model_.bounds.range;}
+const boost=range*0.15;this.displayTransform_.set(this.viewport_.currentDisplayTransform);this.displayTransform_.xSetWorldBounds(min-boost,min+range+boost,w);this.viewport_.setDisplayTransformImmediately(this.displayTransform_);},addAllEventsMatchingFilterToSelectionAsTask(filter,selection){const modelTrack=this.modelTrack_;const firstT=modelTrack.addAllEventsMatchingFilterToSelectionAsTask(filter,selection);const lastT=firstT.after(function(){this.upperModelTrack_.addAllEventsMatchingFilterToSelection(filter,selection);},this);return firstT;},onMouseMove_(e){if(this.isZooming_)return;this.storeLastMousePos_(e);},onTouchStart_(e){this.storeLastTouchPositions_(e);this.focusElements_();},onTouchMove_(e){e.preventDefault();this.onUpdateTransformForTouch_(e);},onTouchEnd_(e){this.storeLastTouchPositions_(e);this.focusElements_();},addHotKeys_(){this.addKeyDownHotKeys_();this.addKeyPressHotKeys_();},addKeyPressHotKey(dict){dict.eventType='keypress';dict.useCapture=false;dict.thisArg=this;const binding=new tr.ui.b.HotKey(dict);this.$.hotkey_controller.addHotKey(binding);},addKeyPressHotKeys_(){this.addKeyPressHotKey({keyCodes:['w'.charCodeAt(0),','.charCodeAt(0)],callback(e){this.zoomBy_(1.5,true);e.stopPropagation();}});this.addKeyPressHotKey({keyCodes:['s'.charCodeAt(0),'o'.charCodeAt(0)],callback(e){this.zoomBy_(1/1.5,true);e.stopPropagation();}});this.addKeyPressHotKey({keyCode:'g'.charCodeAt(0),callback(e){this.onGridToggle_(true);e.stopPropagation();}});this.addKeyPressHotKey({keyCode:'G'.charCodeAt(0),callback(e){this.onGridToggle_(false);e.stopPropagation();}});this.addKeyPressHotKey({keyCodes:['W'.charCodeAt(0),'<'.charCodeAt(0)],callback(e){this.zoomBy_(10,true);e.stopPropagation();}});this.addKeyPressHotKey({keyCodes:['S'.charCodeAt(0),'O'.charCodeAt(0)],callback(e){this.zoomBy_(1/10,true);e.stopPropagation();}});this.addKeyPressHotKey({keyCode:'a'.charCodeAt(0),callback(e){this.queueSmoothPan_(this.viewWidth_*0.3,0);e.stopPropagation();}});this.addKeyPressHotKey({keyCodes:['d'.charCodeAt(0),'e'.charCodeAt(0)],callback(e){this.queueSmoothPan_(this.viewWidth_*-0.3,0);e.stopPropagation();}});this.addKeyPressHotKey({keyCode:'A'.charCodeAt(0),callback(e){this.queueSmoothPan_(viewWidth*0.5,0);e.stopPropagation();}});this.addKeyPressHotKey({keyCode:'D'.charCodeAt(0),callback(e){this.queueSmoothPan_(viewWidth*-0.5,0);e.stopPropagation();}});this.addKeyPressHotKey({keyCode:'0'.charCodeAt(0),callback(e){this.setInitialViewport_();e.stopPropagation();}});this.addKeyPressHotKey({keyCode:'f'.charCodeAt(0),callback(e){this.zoomToSelection();e.stopPropagation();}});this.addKeyPressHotKey({keyCode:'m'.charCodeAt(0),callback(e){this.setCurrentSelectionAsInterestRange_();e.stopPropagation();}});this.addKeyPressHotKey({keyCode:'p'.charCodeAt(0),callback(e){this.selectPowerSamplesInCurrentTimeRange_();e.stopPropagation();}});this.addKeyPressHotKey({keyCode:'h'.charCodeAt(0),callback(e){this.toggleHighDetails_();e.stopPropagation();}});},get viewWidth_(){return this.modelTrackContainer_.canvas.clientWidth;},addKeyDownHotKeys_(){const addBinding=function(dict){dict.eventType='keydown';dict.useCapture=false;dict.thisArg=this;const binding=new tr.ui.b.HotKey(dict);this.$.hotkey_controller.addHotKey(binding);}.bind(this);addBinding({keyCode:37,callback(e){const curSel=this.brushingStateController_.selection;const sel=this.viewport.getShiftedSelection(curSel,-1);if(sel){this.brushingStateController.changeSelectionFromTimeline(sel);this.panToSelection();}else{this.queueSmoothPan_(this.viewWidth_*0.3,0);}
+e.preventDefault();e.stopPropagation();}});addBinding({keyCode:39,callback(e){const curSel=this.brushingStateController_.selection;const sel=this.viewport.getShiftedSelection(curSel,1);if(sel){this.brushingStateController.changeSelectionFromTimeline(sel);this.panToSelection();}else{this.queueSmoothPan_(-this.viewWidth_*0.3,0);}
+e.preventDefault();e.stopPropagation();}});},onDblClick_(e){if(this.mouseModeSelector_.mode!==tr.ui.b.MOUSE_SELECTOR_MODE.SELECTION){return;}
+const curSelection=this.brushingStateController_.selection;if(!curSelection.length||!tr.b.getOnlyElement(curSelection).title){return;}
+const selection=new tr.model.EventSet();const filter=new tr.c.ExactTitleFilter(tr.b.getOnlyElement(curSelection).title);this.modelTrack_.addAllEventsMatchingFilterToSelection(filter,selection);this.brushingStateController.changeSelectionFromTimeline(selection);},onMouseWheel_(e){if(!e.altKey)return;const delta=e.wheelDelta/120;const zoomScale=Math.pow(1.5,delta);this.zoomBy_(zoomScale);e.preventDefault();},onMouseDown_(e){if(this.mouseModeSelector_.mode!==tr.ui.b.MOUSE_SELECTOR_MODE.SELECTION){return;}
+if(e.target!==this.rulerTrack_)return;this.dragBeginEvent_=undefined;if(this.xNavStringMarker_){this.model.removeAnnotation(this.xNavStringMarker_);this.xNavStringMarker_=undefined;}
+const dt=this.viewport_.currentDisplayTransform;tr.ui.b.trackMouseMovesUntilMouseUp(function(e){if(e.target===this.rulerTrack_)return;const relativePosition=this.extractRelativeMousePosition_(e);const loc=tr.model.Location.fromViewCoordinates(this.viewport_,relativePosition.x,relativePosition.y);if(!loc)return;if(this.guideLineAnnotation_===undefined){this.guideLineAnnotation_=new tr.model.XMarkerAnnotation(loc.xWorld);this.model.addAnnotation(this.guideLineAnnotation_);}else{this.guideLineAnnotation_.timestamp=loc.xWorld;this.modelTrackContainer_.invalidate();}
+const state=new tr.ui.b.UIState(loc,this.viewport_.currentDisplayTransform.scaleX);this.timelineView_.setFindCtlText(state.toUserFriendlyString(this.viewport_));}.bind(this),undefined,function onKeyUpDuringDrag(){if(this.dragBeginEvent_){this.setDragBoxPosition_(this.dragBoxXStart_,this.dragBoxYStart_,this.dragBoxXEnd_,this.dragBoxYEnd_);}}.bind(this));},queueSmoothPan_(viewDeltaX,deltaY){const deltaX=this.viewport_.currentDisplayTransform.xViewVectorToWorld(viewDeltaX);const animation=new tr.ui.TimelineDisplayTransformPanAnimation(deltaX,deltaY);this.viewport_.queueDisplayTransformAnimation(animation);},zoomBy_(scale,smooth){if(scale<=0){return;}
+smooth=!!smooth;const vp=this.viewport_;const pixelRatio=window.devicePixelRatio||1;const goalFocalPointXView=this.lastMouseViewPos_.x*pixelRatio;const goalFocalPointXWorld=vp.currentDisplayTransform.xViewToWorld(goalFocalPointXView);if(smooth){const animation=new tr.ui.TimelineDisplayTransformZoomToAnimation(goalFocalPointXWorld,goalFocalPointXView,vp.currentDisplayTransform.panY,scale);vp.queueDisplayTransformAnimation(animation);}else{this.displayTransform_.set(vp.currentDisplayTransform);this.displayTransform_.scaleX*=scale;this.displayTransform_.xPanWorldPosToViewPos(goalFocalPointXWorld,goalFocalPointXView,this.viewWidth_);vp.setDisplayTransformImmediately(this.displayTransform_);}},zoomToSelection(){if(!this.brushingStateController.selectionOfInterest.length)return;const bounds=this.brushingStateController.selectionOfInterest.bounds;if(!bounds.range)return;const worldCenter=bounds.center;const viewCenter=this.modelTrackContainer_.canvas.width/2;const adjustedWorldRange=bounds.range*1.25;const newScale=this.modelTrackContainer_.canvas.width/adjustedWorldRange;const zoomInRatio=newScale/this.viewport_.currentDisplayTransform.scaleX;const animation=new tr.ui.TimelineDisplayTransformZoomToAnimation(worldCenter,viewCenter,this.viewport_.currentDisplayTransform.panY,zoomInRatio);this.viewport_.queueDisplayTransformAnimation(animation);},panToSelection(){if(!this.brushingStateController.selectionOfInterest.length)return;const bounds=this.brushingStateController.selectionOfInterest.bounds;const worldCenter=bounds.center;const viewWidth=this.viewWidth_;const dt=this.viewport_.currentDisplayTransform;if(false&&!bounds.range){if(dt.xWorldToView(bounds.center)<0||dt.xWorldToView(bounds.center)>viewWidth){this.displayTransform_.set(dt);this.displayTransform_.xPanWorldPosToViewPos(worldCenter,'center',viewWidth);const deltaX=this.displayTransform_.panX-dt.panX;const animation=new tr.ui.TimelineDisplayTransformPanAnimation(deltaX,0);this.viewport_.queueDisplayTransformAnimation(animation);}
+return;}
+this.displayTransform_.set(dt);this.displayTransform_.xPanWorldBoundsIntoView(bounds.min,bounds.max,viewWidth);const deltaX=this.displayTransform_.panX-dt.panX;const animation=new tr.ui.TimelineDisplayTransformPanAnimation(deltaX,0);this.viewport_.queueDisplayTransformAnimation(animation);},navToPosition(uiState,showNavLine){const location=uiState.location;const scaleX=uiState.scaleX;const track=location.getContainingTrack(this.viewport_);const worldCenter=location.xWorld;const viewCenter=this.modelTrackContainer_.canvas.width/5;const zoomInRatio=scaleX/this.viewport_.currentDisplayTransform.scaleX;track.scrollIntoViewIfNeeded();const animation=new tr.ui.TimelineDisplayTransformZoomToAnimation(worldCenter,viewCenter,this.viewport_.currentDisplayTransform.panY,zoomInRatio);this.viewport_.queueDisplayTransformAnimation(animation);if(!showNavLine)return;if(this.xNavStringMarker_){this.model.removeAnnotation(this.xNavStringMarker_);}
+this.xNavStringMarker_=new tr.model.XMarkerAnnotation(worldCenter);this.model.addAnnotation(this.xNavStringMarker_);},selectPowerSamplesInCurrentTimeRange_(){const selectionBounds=this.brushingStateController_.selection.bounds;if(this.model.device.powerSeries&&!selectionBounds.empty){const events=this.model.device.powerSeries.getSamplesWithinRange(selectionBounds.min,selectionBounds.max);const selection=new tr.model.EventSet(events);this.brushingStateController_.changeSelectionFromTimeline(selection);}},setCurrentSelectionAsInterestRange_(){const selectionBounds=this.brushingStateController_.selection.bounds;if(selectionBounds.empty){this.viewport_.interestRange.reset();return;}
+if(this.viewport_.interestRange.min===selectionBounds.min&&this.viewport_.interestRange.max===selectionBounds.max){this.viewport_.interestRange.reset();}else{this.viewport_.interestRange.set(selectionBounds);}},toggleHighDetails_(){this.viewport_.highDetails=!this.viewport_.highDetails;},hideDragBox_(){this.$.drag_box.style.left='-1000px';this.$.drag_box.style.top='-1000px';this.$.drag_box.style.width=0;this.$.drag_box.style.height=0;},setDragBoxPosition_(xStart,yStart,xEnd,yEnd){const loY=Math.min(yStart,yEnd);const hiY=Math.max(yStart,yEnd);const loX=Math.min(xStart,xEnd);const hiX=Math.max(xStart,xEnd);const modelTrackRect=this.modelTrack_.getBoundingClientRect();const dragRect={left:loX,top:loY,width:hiX-loX,height:hiY-loY};dragRect.right=dragRect.left+dragRect.width;dragRect.bottom=dragRect.top+dragRect.height;const modelTrackContainerRect=this.modelTrackContainer_.getBoundingClientRect();const clipRect={left:modelTrackContainerRect.left,top:modelTrackContainerRect.top,right:modelTrackContainerRect.right,bottom:modelTrackContainerRect.bottom};const headingWidth=window.getComputedStyle(Polymer.dom(this).querySelector('tr-ui-b-heading')).width;const trackTitleWidth=parseInt(headingWidth);clipRect.left=clipRect.left+trackTitleWidth;const intersectRect_=function(r1,r2){if(r2.left>r1.right||r2.right<r1.left||r2.top>r1.bottom||r2.bottom<r1.top){return false;}
+const results={};results.left=Math.max(r1.left,r2.left);results.top=Math.max(r1.top,r2.top);results.right=Math.min(r1.right,r2.right);results.bottom=Math.min(r1.bottom,r2.bottom);results.width=results.right-results.left;results.height=results.bottom-results.top;return results;};const finalDragBox=intersectRect_(clipRect,dragRect);this.$.drag_box.style.left=finalDragBox.left+'px';this.$.drag_box.style.width=finalDragBox.width+'px';this.$.drag_box.style.top=finalDragBox.top+'px';this.$.drag_box.style.height=finalDragBox.height+'px';this.$.drag_box.style.whiteSpace='nowrap';const pixelRatio=window.devicePixelRatio||1;const canv=this.modelTrackContainer_.canvas;const dt=this.viewport_.currentDisplayTransform;const loWX=dt.xViewToWorld((loX-canv.offsetLeft)*pixelRatio);const hiWX=dt.xViewToWorld((hiX-canv.offsetLeft)*pixelRatio);Polymer.dom(this.$.drag_box).textContent=tr.b.Unit.byName.timeDurationInMs.format(hiWX-loWX);const e=new tr.b.Event('selectionChanging');e.loWX=loWX;e.hiWX=hiWX;this.dispatchEvent(e);},onGridToggle_(left){const selection=this.brushingStateController_.selection;const tb=left?selection.bounds.min:selection.bounds.max;if(this.viewport_.gridEnabled&&this.viewport_.gridSide===left&&this.viewport_.gridInitialTimebase===tb){this.viewport_.gridside=undefined;this.viewport_.gridEnabled=false;this.viewport_.gridInitialTimebase=undefined;return;}
+const numIntervalsSinceStart=Math.ceil((tb-this.model_.bounds.min)/this.viewport_.gridStep_);this.viewport_.gridEnabled=true;this.viewport_.gridSide=left;this.viewport_.gridInitialTimebase=tb;this.viewport_.gridTimebase=tb-
+(numIntervalsSinceStart+1)*this.viewport_.gridStep_;},storeLastMousePos_(e){this.lastMouseViewPos_=this.extractRelativeMousePosition_(e);},storeLastTouchPositions_(e){this.lastTouchViewPositions_=this.extractRelativeTouchPositions_(e);},extractRelativeMousePosition_(e){const canv=this.modelTrackContainer_.canvas;return{x:e.clientX-canv.offsetLeft,y:e.clientY-canv.offsetTop};},extractRelativeTouchPositions_(e){const canv=this.modelTrackContainer_.canvas;const touches=[];for(let i=0;i<e.touches.length;++i){touches.push({x:e.touches[i].clientX-canv.offsetLeft,y:e.touches[i].clientY-canv.offsetTop});}
+return touches;},storeInitialMouseDownPos_(e){const position=this.extractRelativeMousePosition_(e);this.mouseViewPosAtMouseDown_.x=position.x;this.mouseViewPosAtMouseDown_.y=position.y;},focusElements_(){this.$.hotkey_controller.childRequestsGeneralFocus(this);},storeInitialInteractionPositionsAndFocus_(e){this.storeInitialMouseDownPos_(e);this.storeLastMousePos_(e);this.focusElements_();},onBeginPanScan_(e){const vp=this.viewport_;this.viewportDisplayTransformAtMouseDown_=vp.currentDisplayTransform.clone();this.isPanningAndScanning_=true;this.storeInitialInteractionPositionsAndFocus_(e);e.preventDefault();},onUpdatePanScan_(e){if(!this.isPanningAndScanning_)return;const viewWidth=this.viewWidth_;const pixelRatio=window.devicePixelRatio||1;const xDeltaView=pixelRatio*(this.lastMouseViewPos_.x-
+this.mouseViewPosAtMouseDown_.x);const yDelta=this.lastMouseViewPos_.y-
+this.mouseViewPosAtMouseDown_.y;this.displayTransform_.set(this.viewportDisplayTransformAtMouseDown_);this.displayTransform_.incrementPanXInViewUnits(xDeltaView);this.displayTransform_.panY-=yDelta;this.viewport_.setDisplayTransformImmediately(this.displayTransform_);e.preventDefault();e.stopPropagation();this.storeLastMousePos_(e);},onEndPanScan_(e){this.isPanningAndScanning_=false;this.storeLastMousePos_(e);if(!e.isClick){e.preventDefault();}},onBeginSelection_(e){const canv=this.modelTrackContainer_.canvas;const rect=this.modelTrack_.getBoundingClientRect();const canvRect=canv.getBoundingClientRect();const inside=rect&&e.clientX>=rect.left&&e.clientX<rect.right&&e.clientY>=rect.top&&e.clientY<rect.bottom&&e.clientX>=canvRect.left&&e.clientX<canvRect.right;if(!inside)return;this.dragBeginEvent_=e;this.storeInitialInteractionPositionsAndFocus_(e);e.preventDefault();},onUpdateSelection_(e){if(!this.dragBeginEvent_)return;this.dragBoxXStart_=this.dragBeginEvent_.clientX;this.dragBoxXEnd_=e.clientX;this.dragBoxYStart_=this.dragBeginEvent_.clientY;this.dragBoxYEnd_=e.clientY;this.setDragBoxPosition_(this.dragBoxXStart_,this.dragBoxYStart_,this.dragBoxXEnd_,this.dragBoxYEnd_);},onEndSelection_(e){e.preventDefault();if(!this.dragBeginEvent_)return;this.hideDragBox_();const eDown=this.dragBeginEvent_;this.dragBeginEvent_=undefined;const loY=Math.min(eDown.clientY,e.clientY);const hiY=Math.max(eDown.clientY,e.clientY);const loX=Math.min(eDown.clientX,e.clientX);const hiX=Math.max(eDown.clientX,e.clientX);const canv=this.modelTrackContainer_.canvas;const worldOffset=canv.getBoundingClientRect().left;const loVX=loX-worldOffset;const hiVX=hiX-worldOffset;const selection=new tr.model.EventSet();if(eDown.appendSelection){const previousSelection=this.brushingStateController_.selection;if(previousSelection!==undefined){selection.addEventSet(previousSelection);}}
+this.modelTrack_.addIntersectingEventsInRangeToSelection(loVX,hiVX,loY,hiY,selection);this.brushingStateController_.changeSelectionFromTimeline(selection);},onBeginZoom_(e){this.isZooming_=true;this.storeInitialInteractionPositionsAndFocus_(e);e.preventDefault();},onUpdateZoom_(e){if(!this.isZooming_)return;const newPosition=this.extractRelativeMousePosition_(e);const zoomScaleValue=1+(this.lastMouseViewPos_.y-
+newPosition.y)*0.01;this.zoomBy_(zoomScaleValue,false);this.storeLastMousePos_(e);},onEndZoom_(e){this.isZooming_=false;if(!e.isClick){e.preventDefault();}},computeTouchCenter_(positions){let xSum=0;let ySum=0;for(let i=0;i<positions.length;++i){xSum+=positions[i].x;ySum+=positions[i].y;}
+return{x:xSum/positions.length,y:ySum/positions.length};},computeTouchSpan_(positions){let xMin=Number.MAX_VALUE;let yMin=Number.MAX_VALUE;let xMax=Number.MIN_VALUE;let yMax=Number.MIN_VALUE;for(let i=0;i<positions.length;++i){xMin=Math.min(xMin,positions[i].x);yMin=Math.min(yMin,positions[i].y);xMax=Math.max(xMax,positions[i].x);yMax=Math.max(yMax,positions[i].y);}
+return Math.sqrt((xMin-xMax)*(xMin-xMax)+
+(yMin-yMax)*(yMin-yMax));},onUpdateTransformForTouch_(e){const newPositions=this.extractRelativeTouchPositions_(e);const currentPositions=this.lastTouchViewPositions_;const newCenter=this.computeTouchCenter_(newPositions);const currentCenter=this.computeTouchCenter_(currentPositions);const newSpan=this.computeTouchSpan_(newPositions);const currentSpan=this.computeTouchSpan_(currentPositions);const vp=this.viewport_;const viewWidth=this.viewWidth_;const pixelRatio=window.devicePixelRatio||1;const xDelta=pixelRatio*(newCenter.x-currentCenter.x);const yDelta=newCenter.y-currentCenter.y;const zoomScaleValue=currentSpan>10?newSpan/currentSpan:1;const viewFocus=pixelRatio*newCenter.x;const worldFocus=vp.currentDisplayTransform.xViewToWorld(viewFocus);this.displayTransform_.set(vp.currentDisplayTransform);this.displayTransform_.scaleX*=zoomScaleValue;this.displayTransform_.xPanWorldPosToViewPos(worldFocus,viewFocus,viewWidth);this.displayTransform_.incrementPanXInViewUnits(xDelta);this.displayTransform_.panY-=yDelta;vp.setDisplayTransformImmediately(this.displayTransform_);this.storeLastTouchPositions_(e);},initHintText_(){this.$.hint_text.style.display='none';this.pendingHintTextClearTimeout_=undefined;},showHintText_(text){if(this.pendingHintTextClearTimeout_){window.clearTimeout(this.pendingHintTextClearTimeout_);this.pendingHintTextClearTimeout_=undefined;}
+this.pendingHintTextClearTimeout_=setTimeout(this.hideHintText_.bind(this),1000);Polymer.dom(this.$.hint_text).textContent=text;this.$.hint_text.style.display='';},hideHintText_(){this.pendingHintTextClearTimeout_=undefined;this.$.hint_text.style.display='none';}});'use strict';Polymer({is:'tr-ui-find-control',filterKeyDown(e){if(e.keyCode===27){const hkc=tr.b.getHotkeyControllerForElement(this);if(hkc){hkc.childRequestsBlur(this);}else{this.blur();}
+e.preventDefault();e.stopPropagation();return;}else if(e.keyCode===13){if(e.shiftKey){this.findPrevious();}else{this.findNext();}}},filterBlur(e){this.updateHitCountEl();},filterFocus(e){this.$.filter.select();},filterMouseUp(e){e.preventDefault();},get controller(){return this.controller_;},set controller(c){this.controller_=c;this.updateHitCountEl();},focus(){this.$.filter.focus();},get hasFocus(){return this===document.activeElement;},filterTextChanged(){Polymer.dom(this.$.hitCount).textContent='';this.$.spinner.style.visibility='visible';this.$.spinner.style.animation='spin 1s linear infinite';this.controller.startFiltering(this.$.filter.value).then(function(){this.$.spinner.style.visibility='hidden';this.$.spinner.style.animation='';this.updateHitCountEl();}.bind(this));},findNext(){if(this.controller){this.controller.findNext();}
+this.updateHitCountEl();},findPrevious(){if(this.controller){this.controller.findPrevious();}
+this.updateHitCountEl();},updateHitCountEl(){if(!this.controller||this.$.filter.value.length===0){Polymer.dom(this.$.hitCount).textContent='';return;}
+const n=this.controller.filterHits.length;const i=n===0?-1:this.controller.currentHitIndex;Polymer.dom(this.$.hitCount).textContent=(i+1)+' of '+n;},setText(string){this.$.filter.value=string;}});'use strict';tr.exportTo('tr.e.tquery',function(){function Context(){this.event=undefined;this.ancestors=[];}
+Context.prototype={push(event){const ctx=new Context();ctx.ancestors=this.ancestors.slice();ctx.ancestors.push(event);return ctx;},pop(event){const ctx=new Context();ctx.event=this.ancestors[this.ancestors.length-1];ctx.ancestors=this.ancestors.slice(0,this.ancestors.length-1);return ctx;}};return{Context,};});'use strict';tr.exportTo('tr.e.tquery',function(){function Filter(){tr.c.ScriptingObject.call(this);}
+Filter.normalizeFilterExpression=function(filterExpression){if(filterExpression instanceof String||typeof(filterExpression)==='string'||filterExpression instanceof RegExp){const filter=new tr.e.tquery.FilterHasTitle(filterExpression);return filter;}
+return filterExpression;};Filter.prototype={__proto__:tr.c.ScriptingObject.prototype,evaluate(context){throw new Error('Not implemented');},matchValue_(value,expected){if(expected instanceof RegExp){return expected.test(value);}else if(expected instanceof Function){return expected(value);}
+return value===expected;}};return{Filter,};});'use strict';tr.exportTo('tr.e.tquery',function(){function FilterAllOf(opt_subExpressions){tr.e.tquery.Filter.call(this);this.subExpressions=opt_subExpressions||[];}
+FilterAllOf.prototype={__proto__:tr.e.tquery.Filter.prototype,set subExpressions(exprs){this.subExpressions_=[];for(let i=0;i<exprs.length;i++){this.subExpressions_.push(tr.e.tquery.Filter.normalizeFilterExpression(exprs[i]));}},get subExpressions(){return this.subExpressions_;},evaluate(context){if(!this.subExpressions.length)return true;for(let i=0;i<this.subExpressions.length;i++){if(!this.subExpressions[i].evaluate(context)){return false;}}
+return true;}};tr.c.ScriptingObjectRegistry.register(function(){const exprs=[];for(let i=0;i<arguments.length;i++){exprs.push(arguments[i]);}
+return new FilterAllOf(exprs);},{name:'allOf'});return{FilterAllOf,};});'use strict';tr.exportTo('tr.e.tquery',function(){function FilterNot(subExpression){tr.e.tquery.Filter.call(this);this.subExpression=subExpression;}
+FilterNot.prototype={__proto__:tr.e.tquery.Filter.prototype,set subExpression(expr){this.subExpression_=tr.e.tquery.Filter.normalizeFilterExpression(expr);},get subExpression(){return this.subExpression_;},evaluate(context){return!this.subExpression.evaluate(context);}};tr.c.ScriptingObjectRegistry.register(function(){const exprs=Array.prototype.slice.call(arguments);if(exprs.length!==1){throw new Error('not() must have exactly one subexpression');}
+return new FilterNot(exprs[0]);},{name:'not'});return{FilterNot,};});'use strict';tr.exportTo('tr.e.tquery',function(){function FilterAnyOf(opt_subExpressions){tr.e.tquery.Filter.call(this);this.subExpressions=opt_subExpressions||[];}
+FilterAnyOf.prototype={__proto__:tr.e.tquery.Filter.prototype,set subExpressions(exprs){this.subExpressions_=[];for(let i=0;i<exprs.length;i++){this.subExpressions_.push(tr.e.tquery.Filter.normalizeFilterExpression(exprs[i]));}},get subExpressions(){return this.subExpressions_;},evaluate(context){if(!this.subExpressions.length)return true;for(let i=0;i<this.subExpressions.length;i++){if(this.subExpressions[i].evaluate(context))return true;}
+return false;}};tr.c.ScriptingObjectRegistry.register(function(){const exprs=Array.prototype.slice.call(arguments);return new FilterAnyOf(exprs);},{name:'anyOf'});tr.c.ScriptingObjectRegistry.register(function(){const exprs=Array.prototype.slice.call(arguments);return new tr.e.tquery.FilterNot(new FilterAnyOf(exprs));},{name:'noneOf'});return{FilterAnyOf,};});'use strict';tr.exportTo('tr.e.tquery',function(){function FilterHasAncestor(opt_subExpression){this.subExpression=opt_subExpression;}
+FilterHasAncestor.prototype={__proto__:tr.e.tquery.Filter.prototype,set subExpression(expr){this.subExpression_=tr.e.tquery.Filter.normalizeFilterExpression(expr);},get subExpression(){return this.subExpression_;},evaluate(context){if(!this.subExpression){return context.ancestors.length>0;}
+while(context.ancestors.length){context=context.pop();if(this.subExpression.evaluate(context))return true;}
+return false;}};tr.c.ScriptingObjectRegistry.register(function(subExpression){return new FilterHasAncestor(subExpression);},{name:'hasAncestor'});return{FilterHasAncestor,};});'use strict';tr.exportTo('tr.e.tquery',function(){function FilterHasDuration(minValueOrExpected,opt_maxValue){if(minValueOrExpected!==undefined&&opt_maxValue!==undefined){this.minValue=minValueOrExpected;this.maxValue=opt_maxValue;}else{this.expected=minValueOrExpected;}}
+FilterHasDuration.prototype={__proto__:tr.e.tquery.Filter.prototype,evaluate(context){if(context.event.duration===undefined)return false;if(this.minValue!==undefined&&this.maxValue!==undefined){return context.event.duration>=this.minValue&&context.event.duration<=this.maxValue;}
+return this.matchValue_(context.event.duration,this.expected);}};tr.c.ScriptingObjectRegistry.register(function(minValueOrExpected,opt_maxValue){return new FilterHasDuration(minValueOrExpected,opt_maxValue);},{name:'hasDuration'});return{FilterHasDuration,};});'use strict';tr.exportTo('tr.e.tquery',function(){function FilterHasTitle(expected){tr.e.tquery.Filter.call(this);this.expected=expected;}
+FilterHasTitle.prototype={__proto__:tr.e.tquery.Filter.prototype,evaluate(context){return this.matchValue_(context.event.title,this.expected);}};tr.c.ScriptingObjectRegistry.register(function(expected){const filter=new tr.e.tquery.FilterHasTitle(expected);return filter;},{name:'hasTitle'});return{FilterHasTitle,};});'use strict';tr.exportTo('tr.e.tquery',function(){function FilterIsTopLevel(opt_subExpression){this.subExpression=opt_subExpression;}
+FilterIsTopLevel.prototype={__proto__:tr.e.tquery.Filter.prototype,set subExpression(expr){this.subExpression_=tr.e.tquery.Filter.normalizeFilterExpression(expr);},get subExpression(){return this.subExpression_;},evaluate(context){if(context.ancestors.length>0)return false;if(!this.subExpression)return true;return this.subExpression.evaluate(context);}};tr.c.ScriptingObjectRegistry.register(function(subExpression){return new FilterIsTopLevel(subExpression);},{name:'isTopLevel'});return{FilterIsTopLevel,};});'use strict';tr.exportTo('tr.e.tquery',function(){function addEventTreeToSelection(selection,event){selection.push(event);if(!event.subSlices)return;event.subSlices.forEach(addEventTreeToSelection.bind(undefined,selection));}
+function TQuery(model){tr.c.ScriptingObject.call(this);this.model_=model;this.parent_=undefined;this.filterExpression_=undefined;this.selection_=undefined;}
+TQuery.prototype={__proto__:tr.c.ScriptingObject.prototype,onModelChanged(model){this.model_=model;this.selection_=undefined;},get brushingStateController(){return this.brushingStateController_;},filter(filterExpression){const result=new TQuery(this.model_);result.parent_=this;result.filterExpression_=tr.e.tquery.Filter.normalizeFilterExpression(filterExpression);return result;},createFilterTaskGraph_(){const nodes=[this];while(nodes[nodes.length-1].parent_){nodes.push(nodes[nodes.length-1].parent_);}
+const rootTask=new tr.b.Task();let lastTask=rootTask;let node;for(let i=nodes.length-1;i>=0;i--){node=nodes[i];if(node.selection_!==undefined)continue;node.selection_=new tr.model.EventSet();if(node.parent_===undefined){lastTask=lastTask.after(this.selectEverythingAsTask_(node.selection_));}else{const prevNode=nodes[i+1];lastTask=this.createFilterTaskForNode_(lastTask,node,prevNode);}}
+return{rootTask,lastTask,lastNode:node};},createFilterTaskForNode_(lastTask,node,prevNode){return lastTask.after(function(){node.evaluateFilterExpression_(prevNode.selection_,node.selection_);},this);},evaluateFilterExpression_(inputSelection,outputSelection){const seenEvents={};inputSelection.forEach(function(event){const context=new tr.e.tquery.Context();context.event=event;this.evaluateFilterExpressionForEvent_(context,inputSelection,outputSelection,seenEvents);}.bind(this));},evaluateFilterExpressionForEvent_(context,inputSelection,outputSelection,seenEvents){const event=context.event;if(inputSelection.contains(event)&&!seenEvents[event.guid]){seenEvents[event.guid]=true;if(!this.filterExpression_||this.filterExpression_.evaluate(context)){outputSelection.push(event);}}
+if(!event.subSlices)return;context=context.push(event);for(let i=0;i<event.subSlices.length;i++){context.event=event.subSlices[i];this.evaluateFilterExpressionForEvent_(context,inputSelection,outputSelection,seenEvents);}},selectEverythingAsTask_(selection){const filterTask=new tr.b.Task();for(const container of this.model_.getDescendantEventContainers()){filterTask.subTask(()=>{for(const event of container.childEvents()){addEventTreeToSelection(selection,event);}},this);}
+return filterTask;},ready(){return new Promise(function(resolve,reject){const graph=this.createFilterTaskGraph_();graph.lastTask=graph.lastTask.after(function(){resolve(this.selection_);},this);tr.b.Task.RunWhenIdle(graph.rootTask);}.bind(this));},get selection(){if(this.selection_===undefined){const graph=this.createFilterTaskGraph_();tr.b.Task.RunSynchronously(graph.rootTask);}
+return this.selection_;}};tr.c.ScriptingObjectRegistry.register(new TQuery(),{name:'$t'});return{TQuery,};});'use strict';Polymer({is:'tr-ui-scripting-control',isEnterKey_(event){return event.keyCode!==229&&(event.key==='Enter'||event.keyIdentifier==='Enter');},setFocus_(focused){const promptEl=this.$.prompt;if(focused){promptEl.focus();Polymer.dom(this.$.root).classList.add('focused');if(promptEl.value.length>0){const sel=window.getSelection();sel.collapse(Polymer.dom(promptEl).firstChild,promptEl.value.length);}}else{promptEl.blur();Polymer.dom(this.$.root).classList.remove('focused');const parent=promptEl.parentElement;const nextEl=Polymer.dom(promptEl).nextSibling;promptEl.remove();Polymer.dom(parent).insertBefore(promptEl,nextEl);}},onConsoleFocus(e){e.stopPropagation();this.setFocus_(true);},onConsoleBlur(e){e.stopPropagation();this.setFocus_(false);},promptKeyDown(e){e.stopPropagation();if(!this.isEnterKey_(e))return;e.preventDefault();const promptEl=this.$.prompt;const command=promptEl.value;if(command.length===0)return;promptEl.value='';this.addLine_(String.fromCharCode(187)+' '+command);let result;try{result=this.controller_.executeCommand(command);}catch(e){result=e.stack||e.stackTrace;}
+if(result instanceof tr.e.tquery.TQuery){result.ready().then(function(selection){this.addLine_(selection.length+' matches');this.controller_.brushingStateController.showScriptControlSelection(selection);}.bind(this));}else{this.addLine_(result);}
+promptEl.scrollIntoView();},addLine_(line){const historyEl=this.$.history;if(historyEl.innerText.length!==0){historyEl.innerText+='\n';}
+historyEl.innerText+=line;},promptKeyPress(e){e.stopPropagation();},toggleVisibility(){const root=this.$.root;if(!this.visible){Polymer.dom(root).classList.remove('hidden');this.setFocus_(true);}else{Polymer.dom(root).classList.add('hidden');this.setFocus_(false);}},get hasFocus(){return this===document.activeElement;},get visible(){const root=this.$.root;return!Polymer.dom(root).classList.contains('hidden');},get controller(){return this.controller_;},set controller(c){this.controller_=c;}});'use strict';Polymer({is:'tr-ui-side-panel-container',ready(){this.activePanelContainer_=this.$.active_panel_container;this.tabStrip_=this.$.tab_strip;this.dragHandle_=this.$.side_panel_drag_handle;this.dragHandle_.horizontal=false;this.dragHandle_.target=this.activePanelContainer_;this.rangeOfInterest_=new tr.b.math.Range();this.brushingStateController_=undefined;this.onSelectionChanged_=this.onSelectionChanged_.bind(this);this.onModelChanged_=this.onModelChanged_.bind(this);},get brushingStateController(){return this.brushingStateController_;},set brushingStateController(brushingStateController){if(this.brushingStateController){this.brushingStateController_.removeEventListener('change',this.onSelectionChanged_);this.brushingStateController_.removeEventListener('model-changed',this.onModelChanged_);}
+this.brushingStateController_=brushingStateController;if(this.brushingStateController){this.brushingStateController_.addEventListener('change',this.onSelectionChanged_);this.brushingStateController_.addEventListener('model-changed',this.onModelChanged_);if(this.model){this.onModelChanged_();}}},onSelectionChanged_(){if(this.activePanel){this.activePanel.selection=this.selection;}},get model(){return this.brushingStateController_.model;},onModelChanged_(){this.activePanelType_=undefined;this.updateContents_();},get expanded(){this.hasAttribute('expanded');},get activePanel(){return this.activePanelContainer_.children[0];},get activePanelType(){return this.activePanelType_;},set activePanelType(panelType){if(this.model===undefined){throw new Error('Cannot activate panel without a model');}
+let panel=undefined;if(panelType){panel=document.createElement(panelType);}
+if(panel!==undefined&&!panel.supportsModel(this.model)){throw new Error('Cannot activate panel: does not support this model');}
+if(this.activePanelType){Polymer.dom(this.getLabelElementForPanelType_(this.activePanelType)).removeAttribute('selected');}
+if(this.activePanelType){this.getLabelElementForPanelType_(this.activePanelType).removeAttribute('selected');}
+if(this.activePanel){this.activePanelContainer_.removeChild(this.activePanel);}
+if(panelType===undefined){Polymer.dom(this).removeAttribute('expanded');this.activePanelType_=undefined;return;}
+Polymer.dom(this.getLabelElementForPanelType_(panelType)).setAttribute('selected',true);Polymer.dom(this).setAttribute('expanded',true);Polymer.dom(this.activePanelContainer_).appendChild(panel);panel.rangeOfInterest=this.rangeOfInterest_;panel.selection=this.selection_;panel.model=this.model;this.activePanelType_=panelType;},getPanelTypeForConstructor_(constructor){for(let i=0;i<this.tabStrip_.children.length;i++){if(this.tabStrip_.children[i].panelType.constructor===constructor){return this.tabStrip_.children[i].panelType;}}},getLabelElementForPanelType_(panelType){for(let i=0;i<this.tabStrip_.children.length;i++){if(this.tabStrip_.children[i].panelType===panelType){return this.tabStrip_.children[i];}}
+return undefined;},updateContents_(){const previouslyActivePanelType=this.activePanelType;Polymer.dom(this.tabStrip_).textContent='';const supportedPanelTypes=[];const panelTypeInfos=tr.ui.side_panel.SidePanelRegistry.getAllRegisteredTypeInfos();const unsupportedLabelEls=[];for(const panelTypeInfo of panelTypeInfos){const labelEl=document.createElement('tab-strip-label');const panel=panelTypeInfo.constructor();const panelType=panel.tagName;Polymer.dom(labelEl).textContent=panel.textLabel;labelEl.panelType=panelType;const supported=panel.supportsModel(this.model);if(this.model&&supported.supported){supportedPanelTypes.push(panelType);Polymer.dom(labelEl).setAttribute('enabled',true);labelEl.addEventListener('click',function(panelType){this.activePanelType=this.activePanelType===panelType?undefined:panelType;}.bind(this,panelType));Polymer.dom(this.tabStrip_).appendChild(labelEl);}else{if(this.activePanel){this.activePanelContainer_.removeChild(this.activePanel);}
+this.removeAttribute('expanded');unsupportedLabelEls.push(labelEl);}}
+for(const labelEl of unsupportedLabelEls){Polymer.dom(this.tabStrip_).appendChild(labelEl);}
+if(previouslyActivePanelType&&supportedPanelTypes.includes(previouslyActivePanelType)){this.activePanelType=previouslyActivePanelType;Polymer.dom(this).setAttribute('expanded',true);}else{if(this.activePanel){Polymer.dom(this.activePanelContainer_).removeChild(this.activePanel);}
+Polymer.dom(this).removeAttribute('expanded');}},get rangeOfInterest(){return this.rangeOfInterest_;},set rangeOfInterest(range){if(range===undefined){throw new Error('Must not be undefined');}
+this.rangeOfInterest_=range;if(this.activePanel){this.activePanel.rangeOfInterest=range;}}});'use strict';Polymer({is:'tr-ui-timeline-view-help-overlay',ready(){const mod=tr.isMac?'cmd ':'ctrl';const spans=Polymer.dom(this.root).querySelectorAll('span.mod');for(let i=0;i<spans.length;i++){Polymer.dom(spans[i]).textContent=mod;}}});'use strict';Polymer({is:'tr-ui-timeline-view-metadata-overlay',created(){this.metadata_=undefined;},ready(){this.$.table.tableColumns=[{title:'name',value:d=>d.name,},{title:'value',value:d=>{const gov=document.createElement('tr-ui-a-generic-object-view');gov.object=d.value;return gov;},}];},get metadata(){return this.metadata_;},set metadata(metadata){this.metadata_=metadata;this.$.table.tableRows=this.metadata_;this.$.table.rebuild();}});'use strict';Polymer({is:'tr-v-ui-preferred-display-unit',ready(){this.preferredTimeDisplayMode_=undefined;},attached(){tr.b.Unit.didPreferredTimeDisplayUnitChange();},detached(){tr.b.Unit.didPreferredTimeDisplayUnitChange();},get preferredTimeDisplayMode(){return this.preferredTimeDisplayMode_;},set preferredTimeDisplayMode(v){if(this.preferredTimeDisplayMode_===v)return;this.preferredTimeDisplayMode_=v;tr.b.Unit.didPreferredTimeDisplayUnitChange();}});'use strict';const POLYFILL_WARNING_MESSAGE='Trace Viewer is running with WebComponentsV0 polyfill, and some '+'features may be broken. As a workaround, you may try running chrome '+'with "--enable-blink-features=ShadowDOMV0,CustomElementsV0,HTMLImports" '+'flag. See crbug.com/1036492.';Polymer({is:'tr-ui-timeline-view',created(){this.trackViewContainer_=undefined;this.queuedModel_=undefined;this.builtPromise_=undefined;this.doneBuilding_=undefined;},attached(){this.async(function(){this.trackViewContainer_=Polymer.dom(this).querySelector('#track_view_container');if(!this.trackViewContainer_){throw new Error('missing trackviewContainer');}
+if(this.queuedModel_)this.updateContents_();});},ready(){this.tabIndex=0;this.polyfillWarnedOnce_=false;this.titleEl_=this.$.title;this.leftControlsEl_=this.$.left_controls;this.rightControlsEl_=this.$.right_controls;this.collapsingControlsEl_=this.$.collapsing_controls;this.sidePanelContainer_=this.$.side_panel_container;this.brushingStateController_=new tr.c.BrushingStateController(this);this.findCtl_=this.$.view_find_control;this.findCtl_.controller=new tr.ui.FindController(this.brushingStateController_);this.scriptingCtl_=document.createElement('tr-ui-scripting-control');this.scriptingCtl_.controller=new tr.c.ScriptingController(this.brushingStateController_);this.sidePanelContainer_.brushingStateController=this.brushingStateController_;if(window.tr.metrics&&window.tr.metrics.sh&&window.tr.metrics.sh.SystemHealthMetric){this.railScoreSpan_=document.createElement('tr-metrics-ui-sh-system-health-span');Polymer.dom(this.rightControls).appendChild(this.railScoreSpan_);}else{this.railScoreSpan_=undefined;}
+this.flowEventFilter_=this.$.flow_event_filter_dropdown;this.processFilter_=this.$.process_filter_dropdown;this.optionsDropdown_=this.$.view_options_dropdown;this.selectedFlowEvents_=new Set();this.highlightVSync_=false;this.highlightVSyncCheckbox_=tr.ui.b.createCheckBox(this,'highlightVSync','tr.ui.TimelineView.highlightVSync',false,'Highlight VSync');Polymer.dom(this.optionsDropdown_).appendChild(this.highlightVSyncCheckbox_);this.initMetadataButton_();this.initConsoleButton_();this.initHelpButton_();Polymer.dom(this.collapsingControls).appendChild(this.scriptingCtl_);this.dragEl_=this.$.drag_handle;this.analysisEl_=this.$.analysis;this.analysisEl_.brushingStateController=this.brushingStateController_;this.addEventListener('requestSelectionChange',function(e){const sc=this.brushingStateController_;sc.changeSelectionFromRequestSelectionChangeEvent(e.selection);}.bind(this));this.onViewportChanged_=this.onViewportChanged_.bind(this);this.bindKeyListeners_();this.dragEl_.target=this.analysisEl_;},get globalMode(){return this.hotkeyController.globalMode;},set globalMode(globalMode){globalMode=!!globalMode;this.brushingStateController_.historyEnabled=globalMode;this.hotkeyController.globalMode=globalMode;},get hotkeyController(){return this.$.hkc;},warnPolyfill(){if(this.polyfillWarnedOnce_)return;console.warn(POLYFILL_WARNING_MESSAGE);this.polyfillWarnedOnce_=true;if(!window.__hideTraceViewerPolyfillWarning){const polyfillWarningsEl=Polymer.dom(this.root).querySelector('#polyfill-warning');polyfillWarningsEl.addMessage(POLYFILL_WARNING_MESSAGE,[{buttonText:'Hide',onClick:()=>polyfillWarningsEl.clearMessages()}]);}},updateDocumentFavicon(){let hue;if(!this.model){hue='blue';}else{hue=this.model.faviconHue;}
+let faviconData=tr.ui.b.FaviconsByHue[hue];if(faviconData===undefined){faviconData=tr.ui.b.FaviconsByHue.blue;}
+let link=Polymer.dom(document.head).querySelector('link[rel="shortcut icon"]');if(!link){link=document.createElement('link');link.rel='shortcut icon';Polymer.dom(document.head).appendChild(link);}
+link.href=faviconData;},get selectedFlowEvents(){return this.selectedFlowEvents_;},set selectedFlowEvents(selectedFlowEvents){this.selectedFlowEvents_=selectedFlowEvents;},get highlightVSync(){return this.highlightVSync_;},set highlightVSync(highlightVSync){this.highlightVSync_=highlightVSync;if(!this.trackView_)return;this.trackView_.viewport.highlightVSync=highlightVSync;},initHelpButton_(){const helpButtonEl=this.$.view_help_button;const dlg=new tr.ui.b.Overlay();dlg.title='Chrome Tracing Help';dlg.visible=false;dlg.appendChild(document.createElement('tr-ui-timeline-view-help-overlay'));function onClick(e){dlg.visible=!dlg.visible;e.stopPropagation();}
+helpButtonEl.addEventListener('click',onClick.bind(this));},initConsoleButton_(){const toggleEl=this.$.view_console_button;function onClick(e){this.scriptingCtl_.toggleVisibility();e.stopPropagation();return false;}
+toggleEl.addEventListener('click',onClick.bind(this));},initMetadataButton_(){const showEl=this.$.view_metadata_button;function onClick(e){const dlg=new tr.ui.b.Overlay();dlg.title='Metadata for trace';const metadataOverlay=document.createElement('tr-ui-timeline-view-metadata-overlay');metadataOverlay.metadata=this.model.metadata;Polymer.dom(dlg).appendChild(metadataOverlay);dlg.visible=true;e.stopPropagation();return false;}
+showEl.addEventListener('click',onClick.bind(this));this.updateMetadataButtonVisibility_();},updateMetadataButtonVisibility_(){const showEl=this.$.view_metadata_button;showEl.style.display=(this.model&&this.model.metadata.length)?'':'none';},updateFlowEventList_(){const dropdown=Polymer.dom(this.flowEventFilter_);while(dropdown.firstChild){dropdown.removeChild(dropdown.firstChild);}
+if(!this.model)return;const cboxes=[];const updateAll=(checked)=>{for(const cbox of cboxes){cbox.checked=checked;}};dropdown.appendChild(tr.ui.b.createButton('All',()=>updateAll(true)));dropdown.appendChild(tr.ui.b.createButton('None',()=>updateAll(false)));const categories=new Set();for(const event of this.model.flowEvents){for(const category of tr.b.getCategoryParts(event.category)){categories.add(category);}}
+const sortedCategories=[...categories].sort((a,b)=>a.localeCompare(b,'en',{sensitivity:'base'}));for(const category of sortedCategories){const cbox=tr.ui.b.createCheckBox(undefined,undefined,'tr.ui.TimelineView.selectedFlowEvents.'+category,false,category,()=>{if(cbox.checked){this.selectedFlowEvents.add(category);}else{this.selectedFlowEvents.delete(category);}
+if(this.trackView_){this.trackView_.viewport.dispatchChangeEvent();}});if(cbox.checked){this.selectedFlowEvents.add(category);}
+cboxes.push(cbox);dropdown.appendChild(cbox);}},updateProcessList_(){const dropdown=Polymer.dom(this.processFilter_);while(dropdown.firstChild){dropdown.removeChild(dropdown.firstChild);}
+if(!this.model)return;const trackView=this.trackViewContainer_.querySelector('tr-ui-timeline-track-view');const processViews=trackView.processViews;const cboxes=[];const updateAll=(checked)=>{for(const cbox of cboxes){cbox.checked=checked;}};dropdown.appendChild(tr.ui.b.createButton('All',()=>updateAll(true)));dropdown.appendChild(tr.ui.b.createButton('None',()=>updateAll(false)));for(const view of processViews){const cbox=tr.ui.b.createCheckBox(undefined,undefined,undefined,true,view.processBase.userFriendlyName,()=>view.visible=cbox.checked);cbox.checked=view.visible;cboxes.push(cbox);view.addEventListener('visibility',()=>cbox.checked=view.visible);dropdown.appendChild(cbox);}},get leftControls(){return this.leftControlsEl_;},get rightControls(){return this.rightControlsEl_;},get collapsingControls(){return this.collapsingControlsEl_;},get viewTitle(){return Polymer.dom(this.titleEl_).textContent.substring(Polymer.dom(this.titleEl_).textContent.length-2);},set viewTitle(text){if(text===undefined){Polymer.dom(this.titleEl_).textContent='';this.titleEl_.hidden=true;return;}
+this.titleEl_.hidden=false;Polymer.dom(this.titleEl_).textContent=text;},get model(){if(this.trackView_){return this.trackView_.model;}
+return undefined;},set model(model){this.build(model);},async build(model){this.queuedModel_=model;this.builtPromise_=new Promise((resolve,reject)=>{this.doneBuilding_=resolve;});if(this.trackViewContainer_)await this.updateContents_();},get builtPromise(){return this.builtPromise_;},async updateContents_(){if(this.trackViewContainer_===undefined){throw new Error('timeline-view.updateContents_ requires trackViewContainer_');}
+const model=this.queuedModel_;this.queuedModel_=undefined;const modelInstanceChanged=model!==this.model;const modelValid=model&&!model.bounds.isEmpty;const importWarningsEl=Polymer.dom(this.root).querySelector('#import-warnings');Polymer.dom(importWarningsEl).textContent='';if(modelInstanceChanged){if(this.railScoreSpan_){this.railScoreSpan_.model=undefined;}
+Polymer.dom(this.trackViewContainer_).textContent='';if(this.trackView_){this.trackView_.viewport.removeEventListener('change',this.onViewportChanged_);this.trackView_.brushingStateController=undefined;this.trackView_.detach();this.trackView_=undefined;}
+this.brushingStateController_.modelWillChange();}
+if(modelValid&&!this.trackView_){this.trackView_=document.createElement('tr-ui-timeline-track-view');this.trackView_.timelineView=this;this.trackView.brushingStateController=this.brushingStateController_;Polymer.dom(this.trackViewContainer_).appendChild(this.trackView_);this.trackView_.viewport.addEventListener('change',this.onViewportChanged_);}
+if(modelValid){this.trackView_.model=model;this.trackView_.viewport.selectedFlowEvents=this.selectedFlowEvents;this.trackView_.viewport.highlightVSync=this.highlightVSync;if(this.railScoreSpan_){this.railScoreSpan_.model=model;}
+this.$.display_unit.preferredTimeDisplayMode=model.intrinsicTimeUnit;}
+if(window.CustomElements&&!window.CustomElements.hasNative){this.warnPolyfill();}
+if(model){for(const warning of model.importWarningsThatShouldBeShownToUser){importWarningsEl.addMessage(`Import Warning: ${warning.type}: ${warning.message}`,[{buttonText:'Dismiss',onClick(event,infobar){infobar.visible=false;}}]);}}
+if(modelInstanceChanged){this.updateFlowEventList_();this.updateProcessList_();this.updateMetadataButtonVisibility_();this.brushingStateController_.modelDidChange();this.onViewportChanged_();}
+this.doneBuilding_();},get brushingStateController(){return this.brushingStateController_;},get trackView(){return this.trackView_;},get settings(){if(!this.settings_){this.settings_=new tr.b.Settings();}
+return this.settings_;},set focusElement(value){throw new Error('This is deprecated. Please set globalMode to true.');},bindKeyListeners_(){const hkc=this.hotkeyController;hkc.addHotKey(new tr.ui.b.HotKey({eventType:'keypress',keyCode:'`'.charCodeAt(0),useCapture:true,thisArg:this,callback(e){this.scriptingCtl_.toggleVisibility();if(!this.scriptingCtl_.hasFocus){this.focus();}
+e.stopPropagation();}}));hkc.addHotKey(new tr.ui.b.HotKey({eventType:'keypress',keyCode:'/'.charCodeAt(0),useCapture:true,thisArg:this,callback(e){if(this.scriptingCtl_.hasFocus)return;if(this.findCtl_.hasFocus){this.focus();}else{this.findCtl_.focus();}
+e.preventDefault();e.stopPropagation();}}));hkc.addHotKey(new tr.ui.b.HotKey({eventType:'keypress',keyCode:'?'.charCodeAt(0),useCapture:false,thisArg:this,callback(e){this.$.view_help_button.click();e.stopPropagation();}}));hkc.addHotKey(new tr.ui.b.HotKey({eventType:'keypress',keyCode:'v'.charCodeAt(0),useCapture:false,thisArg:this,callback(e){this.toggleHighlightVSync_();e.stopPropagation();}}));},onViewportChanged_(e){const spc=this.sidePanelContainer_;if(!this.trackView_){spc.rangeOfInterest.reset();return;}
+const vr=this.trackView_.viewport.interestRange.asRangeObject();if(!spc.rangeOfInterest.equals(vr)){spc.rangeOfInterest=vr;}
+if(this.railScoreSpan_&&this.model){this.railScoreSpan_.model=this.model;}},toggleHighlightVSync_(){this.highlightVSyncCheckbox_.checked=!this.highlightVSyncCheckbox_.checked;},setFindCtlText(string){this.findCtl_.setText(string);}});'use strict';tr.exportTo('tr.ui.b',function(){function Row(title,data,groupingKeyFuncs,rowStatsConstructor){this.title=title;this.data_=data;if(groupingKeyFuncs===undefined){groupingKeyFuncs=[];}
+this.groupingKeyFuncs_=groupingKeyFuncs;this.rowStatsConstructor_=rowStatsConstructor;this.subRowsBuilt_=false;this.subRows_=undefined;this.rowStats_=undefined;}
+Row.prototype={getCurrentGroupingKeyFunc_(){if(this.groupingKeyFuncs_.length===0)return undefined;return this.groupingKeyFuncs_[0];},get data(){return this.data_;},get rowStats(){if(this.rowStats_===undefined){this.rowStats_=new this.rowStatsConstructor_(this);}
+return this.rowStats_;},rebuildSubRowsIfNeeded_(){if(this.subRowsBuilt_)return;this.subRowsBuilt_=true;const groupingKeyFunc=this.getCurrentGroupingKeyFunc_();if(groupingKeyFunc===undefined){this.subRows_=undefined;return;}
+const dataByKey={};let hasValues=false;this.data_.forEach(function(datum){const key=groupingKeyFunc(datum);hasValues=hasValues||(key!==undefined);if(dataByKey[key]===undefined){dataByKey[key]=[];}
+dataByKey[key].push(datum);});if(!hasValues){this.subRows_=undefined;return;}
+this.subRows_=[];for(const key in dataByKey){const row=new Row(key,dataByKey[key],this.groupingKeyFuncs_.slice(1),this.rowStatsConstructor_);this.subRows_.push(row);}},get isExpanded(){return(this.subRows&&(this.subRows.length>0)&&(this.subRows.length<5));},get subRows(){this.rebuildSubRowsIfNeeded_();return this.subRows_;}};Polymer({is:'tr-ui-b-grouping-table',created(){this.dataToGroup_=undefined;this.groupBy_=undefined;this.rowStatsConstructor_=undefined;},get tableColumns(){return this.$.table.tableColumns;},set tableColumns(tableColumns){this.$.table.tableColumns=tableColumns;},get tableRows(){return this.$.table.tableRows;},get sortColumnIndex(){return this.$.table.sortColumnIndex;},set sortColumnIndex(sortColumnIndex){this.$.table.sortColumnIndex=sortColumnIndex;},get sortDescending(){return this.$.table.sortDescending;},set sortDescending(sortDescending){this.$.table.sortDescending=sortDescending;},get selectionMode(){return this.$.table.selectionMode;},set selectionMode(selectionMode){this.$.table.selectionMode=selectionMode;},get rowHighlightStyle(){return this.$.table.rowHighlightStyle;},set rowHighlightStyle(rowHighlightStyle){this.$.table.rowHighlightStyle=rowHighlightStyle;},get cellHighlightStyle(){return this.$.table.cellHighlightStyle;},set cellHighlightStyle(cellHighlightStyle){this.$.table.cellHighlightStyle=cellHighlightStyle;},get selectedColumnIndex(){return this.$.table.selectedColumnIndex;},set selectedColumnIndex(selectedColumnIndex){this.$.table.selectedColumnIndex=selectedColumnIndex;},get selectedTableRow(){return this.$.table.selectedTableRow;},set selectedTableRow(selectedTableRow){this.$.table.selectedTableRow=selectedTableRow;},get groupBy(){return this.groupBy_;},set groupBy(groupBy){this.groupBy_=groupBy;this.updateContents_();},get dataToGroup(){return this.dataToGroup_;},set dataToGroup(dataToGroup){this.dataToGroup_=dataToGroup;this.updateContents_();},get rowStatsConstructor(){return this.rowStatsConstructor_;},set rowStatsConstructor(rowStatsConstructor){this.rowStatsConstructor_=rowStatsConstructor;this.updateContents_();},rebuild(){this.$.table.rebuild();},updateContents_(){const groupBy=this.groupBy_||[];const dataToGroup=this.dataToGroup_||[];const rowStatsConstructor=this.rowStatsConstructor_||function(){};const superRow=new Row('',dataToGroup,groupBy,rowStatsConstructor);this.$.table.tableRows=superRow.subRows||[];}});return{};});'use strict';tr.exportTo('tr.ui.b',function(){const THIS_DOC=document.currentScript.ownerDocument;Polymer({is:'tr-ui-b-grouping-table-groupby-picker-group',created(){this.picker_=undefined;this.group_=undefined;},get picker(){return this.picker_;},set picker(picker){this.picker_=picker;},get group(){return this.group_;},set group(g){this.group_=g;this.$.label.textContent=g.label;},get enabled(){return this.$.enabled.checked;},set enabled(enabled){this.$.enabled.checked=enabled;if(!this.enabled){this.$.left.style.display='none';this.$.right.style.display='none';}},set isFirst(isFirst){this.$.left.style.display=(!this.enabled||isFirst)?'none':'inline';},set isLast(isLast){this.$.right.style.display=(!this.enabled||isLast)?'none':'inline';},moveLeft_(){this.picker.moveLeft_(this);},moveRight_(){this.picker.moveRight_(this);},onEnableChanged_(){if(!this.enabled){this.$.left.style.display='none';this.$.right.style.display='none';}
+this.picker.onEnableChanged_(this);}});Polymer({is:'tr-ui-b-grouping-table-groupby-picker',created(){this.settingsKey_=undefined;},get settingsKey(){return this.settingsKey_;},set settingsKey(settingsKey){this.settingsKey_=settingsKey;if(this.$.container.children.length){this.restoreSetting_();}},restoreSetting_(){if(this.settingsKey_===undefined)return;this.currentGroupKeys=tr.b.Settings.get(this.settingsKey_,this.currentGroupKeys);},get possibleGroups(){return Array.from(this.$.container.children).map(groupEl=>groupEl.group);},set possibleGroups(possibleGroups){Polymer.dom(this.$.container).textContent='';for(let i=0;i<possibleGroups.length;++i){const groupEl=document.createElement('tr-ui-b-grouping-table-groupby-picker-group');groupEl.picker=this;groupEl.group=possibleGroups[i];Polymer.dom(this.$.container).appendChild(groupEl);}
+this.restoreSetting_();this.updateFirstLast_();},updateFirstLast_(){const groupEls=Array.from(this.$.container.children);const enabledGroupEls=groupEls.filter(el=>el.enabled);for(let i=0;i<enabledGroupEls.length;++i){enabledGroupEls[i].isFirst=i===0;enabledGroupEls[i].isLast=i===enabledGroupEls.length-1;}},get currentGroupKeys(){return this.currentGroups.map(group=>group.key);},get currentGroups(){const groups=[];for(const groupEl of Array.from(this.$.container.children)){if(groupEl.enabled){groups.push(groupEl.group);}}
+return groups;},set currentGroupKeys(newKeys){if(!tr.b.compareArrays(this.currentGroupKeys,newKeys,(x,y)=>x.localeCompare(y))){return;}
+const possibleGroups=new Map();for(const group of this.possibleGroups){possibleGroups.set(group.key,group);}
+const groupEls=this.$.container.children;let i=0;for(i=0;i<newKeys.length;++i){const group=possibleGroups.get(newKeys[i]);if(group===undefined){newKeys.splice(i,1);--i;continue;}
+groupEls[i].group=group;groupEls[i].enabled=true;possibleGroups.delete(newKeys[i]);}
+for(const group of possibleGroups.values()){groupEls[i].group=group;groupEls[i].enabled=false;++i;}
+this.updateFirstLast_();this.onCurrentGroupsChanged_();},moveLeft_(groupEl){const reference=groupEl.previousSibling;Polymer.dom(this.$.container).removeChild(groupEl);Polymer.dom(this.$.container).insertBefore(groupEl,reference);this.updateFirstLast_();if(groupEl.enabled){this.onCurrentGroupsChanged_();}},moveRight_(groupEl){const reference=groupEl.nextSibling.nextSibling;Polymer.dom(this.$.container).removeChild(groupEl);if(reference){Polymer.dom(this.$.container).insertBefore(groupEl,reference);}else{Polymer.dom(this.$.container).appendChild(groupEl);}
+this.updateFirstLast_();if(groupEl.enabled){this.onCurrentGroupsChanged_();}},onCurrentGroupsChanged_(){this.dispatchEvent(new tr.b.Event('current-groups-changed'));tr.b.Settings.set(this.settingsKey_,this.currentGroupKeys);},onEnableChanged_(groupEl){this.updateFirstLast_();this.onCurrentGroupsChanged_();}});return{};});'use strict';(function(){Polymer({is:'tr-ui-sp-file-size-stats-side-panel',behaviors:[tr.ui.behaviors.SidePanel],ready(){this.model_=undefined;this.selection_=new tr.model.EventSet();this.$.picker.settingsKey='tr-ui-sp-file-size-stats-side-panel-picker';this.$.picker.possibleGroups=[{key:'phase',label:'Event Type',dataFn(eventStat){return eventStat.phase;}},{key:'category',label:'Category',dataFn(eventStat){return eventStat.category;}},{key:'title',label:'Title',dataFn(eventStat){return eventStat.title;}}];if(this.$.picker.currentGroupKeys.length===0){this.$.picker.currentGroupKeys=['phase','title'];}
+this.$.picker.addEventListener('current-groups-changed',this.updateContents_.bind(this));},get textLabel(){return'File Size Stats';},supportsModel(m){if(!m){return{supported:false,reason:'No stats were collected for this file.'};}
+if(m.stats.allTraceEventStats.length===0){return{supported:false,reason:'No stats were collected for this file.'};}
+return{supported:true};},get model(){return this.model_;},set model(model){this.model_=model;this.updateContents_();},get rangeOfInterest(){return this.rangeOfInterest_;},set rangeOfInterest(rangeOfInterest){this.rangeOfInterest_=rangeOfInterest;},get selection(){return this.selection_;},set selection(selection){this.selection_=selection;},createColumns_(stats){const columns=[{title:'Title',value(row){const titleEl=document.createElement('span');Polymer.dom(titleEl).textContent=row.title;titleEl.style.textOverflow='ellipsis';return titleEl;},cmp(a,b){return a.title.localeCompare(b.title);},width:'400px'},{title:'Num Events',align:tr.ui.b.TableFormat.ColumnAlignment.RIGHT,value(row){return row.rowStats.numEvents;},cmp(a,b){return a.rowStats.numEvents-b.rowStats.numEvents;},width:'80px'}];if(stats&&stats.hasEventSizesinBytes){columns.push({title:'Bytes',value(row){const value=new tr.b.Scalar(tr.b.Unit.byName.sizeInBytes,row.rowStats.totalEventSizeinBytes);const spanEl=tr.v.ui.createScalarSpan(value);return spanEl;},cmp(a,b){return a.rowStats.totalEventSizeinBytes-
+b.rowStats.totalEventSizeinBytes;},width:'80px'});}
+return columns;},updateContents_(){const table=this.$.table;const columns=this.createColumns_(this.model.stats);table.rowStatsConstructor=function ModelStatsRowStats(row){const sum=tr.b.math.Statistics.sum(row.data,function(x){return x.numEvents;});const totalEventSizeinBytes=tr.b.math.Statistics.sum(row.data,x=>x.totalEventSizeinBytes);return{numEvents:sum,totalEventSizeinBytes};};table.tableColumns=columns;table.sortColumnIndex=1;table.sortDescending=true;table.selectionMode=tr.ui.b.TableFormat.SelectionMode.ROW;table.groupBy=this.$.picker.currentGroups.map(function(group){return group.dataFn;});if(!this.model){table.dataToGroup=[];}else{table.dataToGroup=this.model.stats.allTraceEventStats;}
+this.$.table.rebuild();}});tr.ui.side_panel.SidePanelRegistry.register(function(){return document.createElement('tr-ui-sp-file-size-stats-side-panel');});})();'use strict';tr.exportTo('tr.mre',function(){function Failure(job,functionHandleString,traceCanonicalUrl,failureTypeName,description,stack){this.job=job;this.functionHandleString=functionHandleString;this.traceCanonicalUrl=traceCanonicalUrl;this.failureTypeName=failureTypeName;this.description=description;this.stack=stack;}
+Failure.prototype={asDict(){return{function_handle_string:this.functionHandleString,trace_canonical_url:this.traceCanonicalUrl,type:this.failureTypeName,description:this.description,stack:this.stack};}};Failure.fromDict=function(failureDict){return new Failure(undefined,failureDict.function_handle_string,failureDict.trace_canonical_url,failureDict.type,failureDict.description,failureDict.stack);};return{Failure,};});'use strict';tr.exportTo('tr.mre',function(){const FunctionRegistry={allFunctions_:[],allFunctionsByName_:{},get allFunctions(){return this.allFunctions_;},get allFunctionsByName(){return this.allFunctionsByName_;}};FunctionRegistry.getFunction=function(name){return this.allFunctionsByName_[name];};FunctionRegistry.register=function(func){if(func.name===''){throw new Error('Registered functions must not be anonymous');}
+if(this.allFunctionsByName[func.name]!==undefined){throw new Error('Function named '+func.name+'is already registered.');}
+this.allFunctionsByName[func.name]=func;this.allFunctions.push(func);};function ModuleToLoad(href,filename){if((href!==undefined)?(filename!==undefined):(filename===undefined)){throw new Error('ModuleToLoad must specify exactly one of href or '+'filename');}
+this.href=href;this.filename=filename;}
+ModuleToLoad.prototype={asDict(){if(this.href!==undefined){return{'href':this.href};}
+return{'filename':this.filename};},toString(){if(this.href!==undefined){return'ModuleToLoad(href="'+this.href+'")';}
+return'ModuleToLoad(filename="'+this.filename+'")';}};ModuleToLoad.fromDict=function(moduleDict){return new ModuleToLoad(moduleDict.href,moduleDict.filename);};function FunctionHandle(modulesToLoad,functionName,opt_options){if(!(modulesToLoad instanceof Array)){throw new Error('modulesToLoad in FunctionHandle must be an array');}
+if(typeof(functionName)!=='string'){throw new Error('functionName in FunctionHandle must be a string');}
+this.modulesToLoad=modulesToLoad;this.functionName=functionName;this.options_=opt_options;}
+FunctionHandle.prototype={get options(){return this.options_;},asDict(){return{'modules_to_load':this.modulesToLoad.map(function(m){return m.asDict();}),'function_name':this.functionName,'options':this.options_};},asUserFriendlyString(){const parts=this.modulesToLoad.map(mtl=>mtl.filename);parts.push(this.functionName);parts.push(JSON.stringify(this.options_));return parts.join(',');},hasHrefs(){for(const module in this.modulesToLoad){if(this.modulesToLoad[module].href!==undefined){return true;}}
+return false;},load(){if(this.hasHrefs()){const err=new Error('FunctionHandle named '+this.functionName+' specifies hrefs, which cannot be loaded.');err.name='FunctionLoadingError';throw err;}
+for(const module in this.modulesToLoad){const filename=this.modulesToLoad[module].filename;try{HTMLImportsLoader.loadHTMLFile(filename);}catch(err){err.name='FunctionLoadingError';throw err;}}
+const func=FunctionRegistry.getFunction(this.functionName);if(func===undefined){const err=new Error('No registered function named '+this.functionName);err.name='FunctionNotDefinedError';throw err;}
+return func;},toString(){const modulesToLoadStr=this.modulesToLoad.map(function(module){return module.toString();});return'FunctionHandle(modulesToLoad=['+modulesToLoadStr+'], '+'functionName="'+this.functionName+'", options="'+
+JSON.stringify(this.options_)+'")';}};FunctionHandle.loadFromFilename_=function(filename){try{const numFunctionsBefore=FunctionRegistry.allFunctions.length;HTMLImportsLoader.loadHTMLFile(filename);}catch(err){err.name='FunctionLoadingError';throw err;}
+const numFunctionsNow=FunctionRegistry.allFunctions.length;if(numFunctionsNow!==(numFunctionsBefore+1)){const err=new Error(filename+' didn\'t call FunctionRegistry.register');err.name='FunctionNotDefinedError';throw err;}
+return FunctionRegistry.allFunctions[numFunctionsNow-1];};FunctionHandle.fromDict=function(handleDict){const options=handleDict.options;let modulesToLoad;if(handleDict.modules_to_load!==undefined){modulesToLoad=handleDict.modules_to_load.map(function(module){return ModuleToLoad.fromDict(module);});}
+return new FunctionHandle(modulesToLoad,handleDict.function_name,options);};return{FunctionHandle,ModuleToLoad,FunctionRegistry,};});'use strict';tr.exportTo('tr.metrics',function(){function runMetrics(model,options,addFailureCb){if(options===undefined){throw new Error('Options are required.');}
+const metricNames=options.metrics;if(!metricNames){throw new Error('Metric names should be specified.');}
+const allMetricsStart=new Date();const durationBreakdown=new tr.v.d.Breakdown();const categories=getTraceCategories(model);const histograms=new tr.v.HistogramSet();histograms.createHistogram('trace_import_duration',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,model.stats.traceImportDurationMs,{binBoundaries:tr.v.HistogramBinBoundaries.createExponential(1e-3,1e5,30),description:'Duration that trace viewer required to import the trace',summaryOptions:tr.v.Histogram.AVERAGE_ONLY_SUMMARY_OPTIONS,});for(const metricName of metricNames){const metricStart=new Date();const metric=tr.metrics.MetricRegistry.findTypeInfoWithName(metricName);if(metric===undefined){throw new Error(`"${metricName}" is not a registered metric.`);}
+validateTraceCategories(metric.metadata.requiredCategories,categories);try{metric.constructor(histograms,model,options);}catch(e){const err=tr.b.normalizeException(e);addFailureCb(new tr.mre.Failure(undefined,'metricMapFunction',model.canonicalUrl,err.typeName,err.message,err.stack));}
+const metricMs=new Date()-metricStart;histograms.createHistogram(metricName+'_duration',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,[metricMs]);durationBreakdown.set(metricName,metricMs);}
+validateDiagnosticNames(histograms);const allMetricsMs=new Date()-allMetricsStart+
+model.stats.traceImportDurationMs;durationBreakdown.set('traceImport',model.stats.traceImportDurationMs);durationBreakdown.set('other',allMetricsMs-tr.b.math.Statistics.sum(durationBreakdown,([metricName,metricMs])=>metricMs));const breakdownNames=tr.v.d.RelatedNameMap.fromEntries(new Map(metricNames.map(metricName=>[metricName,metricName+'_duration'])));breakdownNames.set('traceImport','trace_import_duration');histograms.createHistogram('metrics_duration',tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,[{value:allMetricsMs,diagnostics:{breakdown:durationBreakdown},},],{diagnostics:{breakdown:breakdownNames},});return histograms;}
+function getTraceCategories(model){for(const metadata of model.metadata){let config;if(metadata.name==='TraceConfig'&&metadata.value){config=metadata.value;}
+if(metadata.name==='metadata'&&metadata.value&&metadata.value['trace-config']&&metadata.value['trace-config']!=='__stripped__'){config=JSON.parse(metadata.value['trace-config']);}
+if(config){return{excluded:config.excluded_categories||[],included:config.included_categories||[],};}}}
+function validateTraceCategories(requiredCategories,categories){if(!requiredCategories)return;if(!categories)throw new Error('Missing trace config metadata');for(const cat of requiredCategories){const isDisabledByDefault=(cat.indexOf('disabled-by-default')===0);let missing=false;if(isDisabledByDefault){if(!categories.included.includes(cat)){missing=true;}}else if(categories.excluded.includes(cat)){missing=true;}
+if(missing){throw new Error(`Trace is missing required category "${cat}"`);}}}
+function validateDiagnosticNames(histograms){for(const hist of histograms){for(const name of hist.diagnostics.keys()){if(tr.v.d.RESERVED_NAMES_SET.has(name)){throw new Error(`Illegal diagnostic name "${name}" on Histogram "${hist.name}"`);}}}}
+function addTelemetryInfo(histograms,model){for(const metadata of model.metadata){if(!metadata.value||!metadata.value.telemetry)continue;for(const[name,value]of Object.entries(metadata.value.telemetry)){const type=tr.v.d.RESERVED_NAMES_TO_TYPES.get(name);if(type===undefined){throw new Error(`Unexpected telemetry.${name}`);}
+histograms.addSharedDiagnosticToAllHistograms(name,new type(value));}}}
+function metricMapFunction(result,model,options){const histograms=runMetrics(model,options,result.addFailure.bind(result));addTelemetryInfo(histograms,model);if(model.canonicalUrl!==undefined){const info=tr.v.d.RESERVED_INFOS.TRACE_URLS;histograms.addSharedDiagnosticToAllHistograms(info.name,new info.type([model.canonicalUrl]));}
+result.addPair('histograms',histograms.asDicts());const scalarDicts=[];for(const value of histograms){for(const[statName,scalar]of value.statisticsScalars){scalarDicts.push({name:value.name+'_'+statName,numeric:scalar.asDict(),description:value.description,});}}
+result.addPair('scalars',scalarDicts);}
+tr.mre.FunctionRegistry.register(metricMapFunction);return{metricMapFunction,runMetrics,};});'use strict';tr.exportTo('tr.mre',function(){class MreResult{constructor(failures,pairs){if(failures===undefined){failures=[];}
+if(pairs===undefined){pairs={};}
+this.failures=failures;this.pairs=pairs;}
+addFailure(failure){this.failures.push(failure);}
+addPair(key,value){if(key in this.pairs){throw new Error('Key '+key+' already exists in result.');}
+this.pairs[key]=value;}
+asDict(){const d={pairs:this.pairs};if(this.failures){d.failures=this.failures.map(function(f){return f.asDict();});}
+return d;}
+hadFailures(){return this.failures.length>0;}
+static fromDict(resultDict){const failures=(resultDict.failures!==undefined)?resultDict.failures.map(tr.mre.Failure.fromDict):undefined;const pairs=resultDict.pairs;return new MreResult(failures,pairs);}}
+return{MreResult,};});'use strict';tr.exportTo('tr.ui',function(){class NullBrushingStateController extends tr.c.BrushingStateController{constructor(){super(undefined);this.parentController=undefined;}
+dispatchChangeEvent_(){if(this.parentController)this.parentController.dispatchChangeEvent_();}
+get model(){if(!this.parentController)return undefined;return this.parentController.model;}
+get trackView(){if(!this.parentController)return undefined;return this.parentController.trackView;}
+get viewport(){if(!this.parentController)return undefined;return this.parentController.viewport;}
+get historyEnabled(){if(!this.parentController)return undefined;return this.parentController.historyEnabled;}
+set historyEnabled(historyEnabled){if(this.parentController){this.parentController.historyEnabled=historyEnabled;}}
+modelWillChange(){if(this.parentController)this.parentController.modelWillChange();}
+modelDidChange(){if(this.parentController)this.parentController.modelDidChange();}
+onUserInitiatedSelectionChange_(){if(this.parentController){this.parentController.onUserInitiatedSelectionChange_();}}
+onPopState_(e){if(this.parentController)this.parentController.onPopState_(e);}
+get selection(){if(!this.parentController)return undefined;return this.parentController.selection;}
+get findMatches(){if(!this.parentController)return undefined;return this.parentController.findMatches;}
+get selectionOfInterest(){if(!this.parentController)return undefined;return this.parentController.selectionOfInterest;}
+get currentBrushingState(){if(!this.parentController)return undefined;return this.parentController.currentBrushingState;}
+set currentBrushingState(newBrushingState){if(this.parentController){this.parentController.currentBrushingState=newBrushingState;}}
+addAllEventsMatchingFilterToSelectionAsTask(filter,selection){if(this.parentController){this.parentController.addAllEventsMatchingFilterToSelectionAsTask(filter,selection);}}
+findTextChangedTo(allPossibleMatches){if(this.parentController){this.parentController.findTextChangedTo(allPossibleMatches);}}
+findFocusChangedTo(currentFocus){if(this.parentController){this.parentController.findFocusChangedTo(currentFocus);}}
+findTextCleared(){if(this.parentController){this.parentController.findTextCleared();}}
+uiStateFromString(string){if(this.parentController){this.parentController.uiStateFromString(string);}}
+navToPosition(uiState,showNavLine){if(this.parentController){this.parentController.navToPosition(uiState,showNavLine);}}
+changeSelectionFromTimeline(selection){if(this.parentController){this.parentController.changeSelectionFromTimeline(selection);}}
+showScriptControlSelection(selection){if(this.parentController){this.parentController.showScriptControlSelection(selection);}}
+changeSelectionFromRequestSelectionChangeEvent(selection){if(this.parentController){this.parentController.changeSelectionFromRequestSelectionChangeEvent(selection);}}
+changeAnalysisViewRelatedEvents(eventSet){if(this.parentController&&(eventSet instanceof tr.model.EventSet)){this.parentController.changeAnalysisViewRelatedEvents(eventSet);}}
+changeAnalysisLinkHoveredEvents(eventSet){if(this.parentController&&(eventSet instanceof tr.model.EventSet)){this.parentController.changeAnalysisLinkHoveredEvents(eventSet);}}
+getViewSpecificBrushingState(viewId){if(this.parentController){this.parentController.getViewSpecificBrushingState(viewId);}}
+changeViewSpecificBrushingState(viewId,newState){if(this.parentController){this.parentController.changeViewSpecificBrushingState(viewId,newState);}}}
+return{NullBrushingStateController,};});'use strict';tr.exportTo('tr.v',function(){const IGNORE_GROUPING_KEYS=['name','storyTags','testPath',];class CSVBuilder{constructor(histograms){this.histograms_=histograms;this.table_=[];this.statisticsNames_=new Set();this.groupings_=[];}
+build(){this.prepare_();this.buildHeader_();this.buildTable_();}
+prepare_(){for(const[key,grouping]of tr.v.HistogramGrouping.BY_KEY){if(IGNORE_GROUPING_KEYS.includes(key))continue;this.groupings_.push(grouping);}
+this.groupings_.push(new tr.v.GenericSetGrouping(tr.v.d.RESERVED_NAMES.TRACE_URLS));this.groupings_.sort((a,b)=>a.key.localeCompare(b.key));for(const hist of this.histograms_){for(const name of hist.statisticsNames){this.statisticsNames_.add(name);}}
+this.statisticsNames_=Array.from(this.statisticsNames_);this.statisticsNames_.sort();}
+buildHeader_(){const header=['name','unit'];for(const name of this.statisticsNames_){header.push(name);}
+for(const grouping of this.groupings_){header.push(grouping.key);}
+this.table_.push(header);}
+buildTable_(){for(const hist of this.histograms_){const row=[hist.name,hist.unit.unitString];this.table_.push(row);for(const name of this.statisticsNames_){const stat=hist.getStatisticScalar(name);if(stat){row.push(stat.value);}else{row.push('');}}
+for(const grouping of this.groupings_){row.push(grouping.callback(hist));}}}
+toString(){let str='';for(const row of this.table_){for(let i=0;i<row.length;++i){if(i>0){str+=',';}
+let cell=''+row[i];cell=cell.replace(/\n/g,' ');if(cell.indexOf(',')>=0||cell.indexOf('"')>=0){cell='"'+cell.replace(/"/g,'""')+'"';}
+str+=cell;}
+str+='\n';}
+return str;}}
+return{CSVBuilder,};});'use strict';tr.exportTo('tr.v',function(){const getDisplayLabel=tr.v.HistogramGrouping.DISPLAY_LABEL.callback;const DEFAULT_POSSIBLE_GROUPS=[];const EXCLUDED_GROUPING_KEYS=[tr.v.HistogramGrouping.DISPLAY_LABEL.key,];for(const group of tr.v.HistogramGrouping.BY_KEY.values()){if(EXCLUDED_GROUPING_KEYS.includes(group.key))continue;DEFAULT_POSSIBLE_GROUPS.push(group);}
+class HistogramParameterCollector{constructor(){this.statisticNames_=new Set(['avg']);this.labelsToStartTimes_=new Map();this.keysToGroupings_=new Map(DEFAULT_POSSIBLE_GROUPS.map(g=>[g.key,g]));this.keysToValues_=new Map(DEFAULT_POSSIBLE_GROUPS.map(g=>[g.key,new Set()]));this.keysToValues_.delete(tr.v.HistogramGrouping.HISTOGRAM_NAME.key);}
+process(histograms){const allStoryTags=new Set();let maxSampleCount=0;for(const hist of histograms){maxSampleCount=Math.max(maxSampleCount,hist.numValues);for(const statName of hist.statisticsNames){this.statisticNames_.add(statName);}
+let startTime=hist.diagnostics.get(tr.v.d.RESERVED_NAMES.BENCHMARK_START);if(startTime!==undefined)startTime=startTime.minDate.getTime();const displayLabel=getDisplayLabel(hist);if(this.labelsToStartTimes_.has(displayLabel)){startTime=Math.min(startTime,this.labelsToStartTimes_.get(displayLabel));}
+this.labelsToStartTimes_.set(displayLabel,startTime);for(const[groupingKey,values]of this.keysToValues_){const grouping=this.keysToGroupings_.get(groupingKey);const value=grouping.callback(hist);if(!value)continue;values.add(value);if(values.size>1){this.keysToValues_.delete(groupingKey);}}
+const storyTags=hist.diagnostics.get(tr.v.d.RESERVED_NAMES.STORY_TAGS);for(const tag of(storyTags||[])){allStoryTags.add(tag);}}
+tr.b.Timing.instant('HistogramParameterCollector','maxSampleCount',maxSampleCount);for(const tagGrouping of tr.v.HistogramGrouping.buildFromTags(allStoryTags,tr.v.d.RESERVED_NAMES.STORY_TAGS)){const values=new Set();for(const hist of histograms){values.add(tagGrouping.callback(hist));}
+if(values.size>1){this.keysToGroupings_.set(tagGrouping.key,tagGrouping);this.keysToValues_.set(tagGrouping.key,values);}}
+this.statisticNames_.add('pct_090');}
+get statisticNames(){return Array.from(this.statisticNames_);}
+get labels(){const displayLabels=Array.from(this.labelsToStartTimes_.keys());displayLabels.sort((x,y)=>this.labelsToStartTimes_.get(x)-this.labelsToStartTimes_.get(y));return displayLabels;}
+get possibleGroupings(){for(const[key,values]of this.keysToValues_){if(values.size>=2)continue;this.keysToGroupings_.delete(key);}
+return Array.from(this.keysToGroupings_.values());}}
+return{HistogramParameterCollector,};});'use strict';tr.exportTo('tr.v.ui',function(){Polymer({is:'tr-v-ui-histogram-set-controls-export',exportRawCsv_(){this.export_(false,'csv');},exportRawJson_(){this.export_(false,'json');},exportMergedCsv_(){this.export_(true,'csv');},exportMergedJson_(){this.export_(true,'json');},export_(merged,format){tr.b.dispatchSimpleEvent(this,'export',true,true,{merged,format});},});return{};});'use strict';tr.exportTo('tr.v.ui',function(){const ALPHA_OPTIONS=[];for(let i=1;i<10;++i)ALPHA_OPTIONS.push(i*1e-3);for(let i=1;i<10;++i)ALPHA_OPTIONS.push(i*1e-2);ALPHA_OPTIONS.push(0.1);Polymer({is:'tr-v-ui-histogram-set-controls',properties:{searchQuery:{type:String,value:'',observer:'onSearchQueryChange_',},showAll:{type:Boolean,value:true,observer:'onUserChange_',},referenceDisplayLabel:{type:String,value:'',observer:'onUserChange_',},displayStatisticName:{type:String,value:'',observer:'onUserChange_',},alphaString:{type:String,computed:'getAlphaString_(alphaIndex)',},alphaIndex:{type:Number,value:9,observer:'onUserChange_',},},created(){this.viewState_=undefined;this.rowListener_=this.onRowViewStateUpdate_.bind(this);this.baseStatisticNames_=[];this.isInOnViewStateUpdate_=false;this.searchQueryDebounceMs=200;},ready(){this.$.picker.addEventListener('current-groups-changed',this.onGroupsChanged_.bind(this));},get viewState(){return this.viewState_;},set viewState(vs){if(this.viewState_){throw new Error('viewState must be set exactly once.');}
+this.viewState_=vs;this.viewState.addUpdateListener(this.onViewStateUpdate_.bind(this));},async onSearchQueryChange_(){if(this.searchQueryDebounceMs===0)return this.onUserChange_();this.debounce('onSearchQueryDebounce',this.onUserChange_,this.searchQueryDebounceMs);},async onUserChange_(){if(!this.viewState)return;if(this.isInOnViewStateUpdate_)return;const marks=[];if(this.searchQuery!==this.viewState.searchQuery){marks.push(tr.b.Timing.mark('histogram-set-controls','search'));}
+if(this.showAll!==this.viewState.showAll){marks.push(tr.b.Timing.mark('histogram-set-controls','showAll'));}
+if(this.referenceDisplayLabel!==this.viewState.referenceDisplayLabel){marks.push(tr.b.Timing.mark('histogram-set-controls','referenceColumn'));}
+if(this.displayStatisticName!==this.viewState.displayStatisticName){marks.push(tr.b.Timing.mark('histogram-set-controls','statistic'));}
+if(parseInt(this.alphaIndex)!==this.getAlphaIndexFromViewState_()){marks.push(tr.b.Timing.mark('histogram-set-controls','alpha'));}
+this.$.clear_search.style.visibility=this.searchQuery?'visible':'hidden';let displayStatisticName=this.displayStatisticName;if(this.viewState.referenceDisplayLabel===''&&this.referenceDisplayLabel!==''&&this.baseStatisticNames.length){displayStatisticName=`%${tr.v.DELTA}${this.displayStatisticName}`;}
+if(this.referenceDisplayLabel===''&&this.viewState.referenceDisplayLabel!==''&&this.baseStatisticNames.length){const deltaIndex=displayStatisticName.indexOf(tr.v.DELTA);if(deltaIndex>=0){displayStatisticName=displayStatisticName.slice(deltaIndex+1);}else if(!this.baseStatisticNames.includes(displayStatisticName)){displayStatisticName='avg';}}
+await this.viewState.update({searchQuery:this.searchQuery,showAll:this.showAll,referenceDisplayLabel:this.referenceDisplayLabel,displayStatisticName,alpha:ALPHA_OPTIONS[this.alphaIndex],});if(this.referenceDisplayLabel&&this.statisticNames.length===this.baseStatisticNames.length){this.statisticNames=this.baseStatisticNames.concat(tr.v.Histogram.getDeltaStatisticsNames(this.baseStatisticNames));}else if(!this.referenceDisplayLabel&&this.statisticNames.length>this.baseStatisticNames.length){this.statisticNames=this.baseStatisticNames;}
+for(const mark of marks)mark.end();},onViewStateUpdate_(event){this.isInOnViewStateUpdate_=true;if(event.delta.searchQuery){this.searchQuery=this.viewState.searchQuery;}
+if(event.delta.showAll)this.showAll=this.viewState.showAll;if(event.delta.displayStatisticName){this.displayStatisticName=this.viewState.displayStatisticName;}
+if(event.delta.referenceDisplayLabel){this.referenceDisplayLabel=this.viewState.referenceDisplayLabel;this.$.alpha.style.display=this.referenceDisplayLabel?'inline':'';}
+if(event.delta.groupings){this.$.picker.currentGroupKeys=this.viewState.groupings.map(g=>g.key);}
+if(event.delta.tableRowStates){for(const row of tr.v.ui.HistogramSetTableRowState.walkAll(this.viewState.tableRowStates.values())){row.addUpdateListener(this.rowListener_);}
+const anyShowing=this.anyOverviewCharts_;this.$.hide_overview.style.display=anyShowing?'inline':'none';this.$.show_overview.style.display=anyShowing?'none':'inline';}
+if(event.delta.alpha){this.alphaIndex=this.getAlphaIndexFromViewState_();}
+this.isInOnViewStateUpdate_=false;this.onUserChange_();},onRowViewStateUpdate_(event){if(event.delta.isOverviewed){const anyShowing=event.delta.isOverviewed.current||this.anyOverviewCharts_;this.$.hide_overview.style.display=anyShowing?'inline':'none';this.$.show_overview.style.display=anyShowing?'none':'inline';}
+if(event.delta.subRows){for(const subRow of event.delta.subRows.previous){subRow.removeUpdateListener(this.rowListener_);}
+for(const subRow of event.delta.subRows.current){subRow.addUpdateListener(this.rowListener_);}}},onGroupsChanged_(){if(this.$.picker.currentGroups.length===0&&this.$.picker.possibleGroups.length>0){this.$.picker.currentGroupKeys=[this.$.picker.possibleGroups[0].key];}
+this.viewState.groupings=this.$.picker.currentGroups;},set showAllEnabled(enable){if(!enable)this.$.show_all.checked=true;this.$.show_all.disabled=!enable;},set possibleGroupings(groupings){this.$.picker.possibleGroups=groupings;this.$.picker.style.display=(groupings.length<2)?'none':'block';this.onGroupsChanged_();},set displayLabels(labels){this.$.reference_display_label.style.display=(labels.length<2)?'none':'inline';while(this.$.reference_display_label.children.length>1){this.$.reference_display_label.removeChild(this.$.reference_display_label.lastChild);}
+for(const displayLabel of labels){const option=document.createElement('option');option.textContent=displayLabel;option.value=displayLabel;this.$.reference_display_label.appendChild(option);}
+if(labels.includes(this.viewState.referenceDisplayLabel)){this.referenceDisplayLabel=this.viewState.referenceDisplayLabel;}else{this.viewState.referenceDisplayLabel='';}},get baseStatisticNames(){return this.baseStatisticNames_;},set baseStatisticNames(names){this.baseStatisticNames_=names;this.statisticNames=names;},get statisticNames(){return Array.from(this.$.statistic.options).map(o=>o.value);},set statisticNames(names){this.$.statistic.style.display=(names.length<2)?'none':'inline';while(this.$.statistic.children.length){this.$.statistic.removeChild(this.$.statistic.lastChild);}
+for(const name of names){const option=document.createElement('option');option.textContent=name;this.$.statistic.appendChild(option);}
+if(names.includes(this.viewState.displayStatisticName)){this.displayStatisticName=this.viewState.displayStatisticName;this.$.statistic.value=this.displayStatisticName;}else{this.viewState.displayStatisticName=names[0]||'';}},get anyOverviewCharts_(){for(const row of tr.v.ui.HistogramSetTableRowState.walkAll(this.viewState.tableRowStates.values())){if(row.isOverviewed)return true;}
+return false;},async toggleOverviewLineCharts_(){const showOverviews=!this.anyOverviewCharts_;const mark=tr.b.Timing.mark('histogram-set-controls',(showOverviews?'show':'hide')+'OverviewCharts');for(const row of tr.v.ui.HistogramSetTableRowState.walkAll(this.viewState.tableRowStates.values())){await row.update({isOverviewed:showOverviews});}
+this.$.hide_overview.style.display=showOverviews?'inline':'none';this.$.show_overview.style.display=showOverviews?'none':'inline';await tr.b.animationFrame();mark.end();},set helpHref(href){this.$.help.href=href;this.$.help.style.display='inline';},set feedbackHref(href){this.$.feedback.href=href;this.$.feedback.style.display='inline';},clearSearch_(){this.set('searchQuery','');this.$.search.focus();},getAlphaString_(alphaIndex){return(''+ALPHA_OPTIONS[alphaIndex]).substr(0,5);},openAlphaSlider_(){const alphaButtonRect=this.$.alpha.getBoundingClientRect();this.$.alpha_slider_container.style.display='flex';this.$.alpha_slider_container.style.top=alphaButtonRect.bottom+'px';this.$.alpha_slider_container.style.left=alphaButtonRect.left+'px';this.$.alpha_slider.focus();},closeAlphaSlider_(){this.$.alpha_slider_container.style.display='';},updateAlpha_(){this.alphaIndex=this.$.alpha_slider.value;},getAlphaIndexFromViewState_(){for(let i=0;i<ALPHA_OPTIONS.length;++i){if(ALPHA_OPTIONS[i]>=this.viewState.alpha)return i;}
+return ALPHA_OPTIONS.length-1;},set enableVisualization(enable){this.$.show_visualization.style.display=enable?'inline':'none';},loadVisualization_(){tr.b.dispatchSimpleEvent(this,'loadVisualization',true,true,{});},});return{};});'use strict';tr.exportTo('tr.v',function(){class HistogramSetHierarchy{constructor(name){this.name=name;this.description='';this.depth=0;this.subRows=[];this.columns=new Map();}*walk(){yield this;for(const row of this.subRows)yield*row.walk();}
+static*walkAll(rootRows){for(const rootRow of rootRows)yield*rootRow.walk();}
+static build(histogramArrayMap){const rootRows=[];HistogramSetHierarchy.buildInternal_(histogramArrayMap,[],rootRows);const histograms=new tr.v.HistogramSet();for(const row of HistogramSetHierarchy.walkAll(rootRows)){for(const hist of row.columns.values()){if(!(hist instanceof tr.v.Histogram))continue;histograms.addHistogram(hist);}}
+histograms.deduplicateDiagnostics();for(const row of HistogramSetHierarchy.walkAll(rootRows)){row.maybeRebin_();}
+return rootRows;}
+maybeRebin_(){const dataRange=new tr.b.math.Range();for(const hist of this.columns.values()){if(!(hist instanceof tr.v.Histogram))continue;if(hist.allBins.length>1)return;if(hist.numValues===0)continue;dataRange.addValue(hist.min);dataRange.addValue(hist.max);}
+dataRange.addValue(tr.b.math.lesserWholeNumber(dataRange.min));dataRange.addValue(tr.b.math.greaterWholeNumber(dataRange.max));if(dataRange.min===dataRange.max)return;const boundaries=tr.v.HistogramBinBoundaries.createLinear(dataRange.min,dataRange.max,tr.v.DEFAULT_REBINNED_COUNT);for(const[name,hist]of this.columns){if(!(hist instanceof tr.v.Histogram))continue;this.columns.set(name,hist.rebin(boundaries));}}
+static mergeHistogramDownHierarchy_(histogram,hierarchy,columnName){for(const row of hierarchy){if(!row.description){row.description=histogram.description;}
+const existing=row.columns.get(columnName);if(existing===undefined){row.columns.set(columnName,histogram.clone());continue;}
+if(existing instanceof tr.v.HistogramSet){existing.addHistogram(histogram);continue;}
+if(!existing.canAddHistogram(histogram)){const unmergeableHistograms=new tr.v.HistogramSet([histogram]);row.columns.set(columnName,unmergeableHistograms);continue;}
+existing.addHistogram(histogram);}}
+static buildInternal_(histogramArrayMap,hierarchy,rootRows){for(const[name,histograms]of histogramArrayMap){if(histograms instanceof Array){for(const histogram of histograms){HistogramSetHierarchy.mergeHistogramDownHierarchy_(histogram,hierarchy,name);}}else if(histograms instanceof Map){const row=new HistogramSetHierarchy(name);row.depth=hierarchy.length;hierarchy.push(row);HistogramSetHierarchy.buildInternal_(histograms,hierarchy,rootRows);hierarchy.pop();if(hierarchy.length===0){rootRows.push(row);}else{const parentRow=hierarchy[hierarchy.length-1];parentRow.subRows.push(row);}}}}}
+return{HistogramSetHierarchy};});'use strict';tr.exportTo('tr.v.ui',function(){Polymer({is:'tr-v-ui-histogram-set-table-cell',created(){this.viewState_=undefined;this.rootListener_=this.onRootStateUpdate_.bind(this);this.row_=undefined;this.displayLabel_='';this.histogram_=undefined;this.histogramSpan_=undefined;this.overviewChart_=undefined;this.mwuResult_=undefined;},ready(){this.addEventListener('click',this.onClick_.bind(this));},attached(){if(this.row){this.row.rootViewState.addUpdateListener(this.rootListener_);}},detached(){this.row.rootViewState.removeUpdateListener(this.rootListener_);},updateMwu_(){const referenceHistogram=this.referenceHistogram;this.mwuResult_=undefined;if(!(this.histogram instanceof tr.v.Histogram))return;if(!this.histogram.canCompare(referenceHistogram))return;this.mwuResult_=tr.b.math.Statistics.mwu(this.histogram.sampleValues,referenceHistogram.sampleValues,this.row.rootViewState.alpha);},build(row,displayLabel,viewState){this.row_=row;this.displayLabel_=displayLabel;this.viewState_=viewState;this.histogram_=this.row.columns.get(displayLabel);if(this.viewState){this.viewState.addUpdateListener(this.onViewStateUpdate_.bind(this));}
+this.row.viewState.addUpdateListener(this.onRowStateUpdate_.bind(this));if(this.isAttached){this.row.rootViewState.addUpdateListener(this.rootListener_);}
+this.updateMwu_();this.updateContents_();},updateSignificance_(){if(!this.mwuResult_)return;this.$.scalar.significance=this.mwuResult_.significance;},get viewState(){return this.viewState_;},get row(){return this.row_;},get histogram(){return this.histogram_;},get referenceHistogram(){const referenceDisplayLabel=this.row.rootViewState.referenceDisplayLabel;if(!referenceDisplayLabel)return undefined;if(referenceDisplayLabel===this.displayLabel_)return undefined;return this.row.columns.get(referenceDisplayLabel);},get isHistogramOpen(){return(this.histogramSpan_!==undefined)&&(this.$.histogram.style.display==='block');},set isHistogramOpen(open){if(!(this.histogram instanceof tr.v.Histogram)||(this.histogram.numValues===0)){return;}
+this.$.scalar.style.display=open?'none':'flex';this.$.open_histogram.style.display=open?'none':'block';this.$.close_histogram.style.display=open?'block':'none';this.$.histogram.style.display=open?'block':'none';if(open&&this.histogramSpan_===undefined){this.histogramSpan_=document.createElement('tr-v-ui-histogram-span');this.histogramSpan_.viewState=this.viewState;this.histogramSpan_.rowState=this.row.viewState;this.histogramSpan_.rootState=this.row.rootViewState;this.histogramSpan_.build(this.histogram,this.referenceHistogram);this.$.histogram.appendChild(this.histogramSpan_);}
+this.viewState.isOpen=open;},onViewStateUpdate_(event){if(event.delta.isOpen){this.isHistogramOpen=this.viewState.isOpen;}},onRowStateUpdate_(event){if(event.delta.isOverviewed===undefined)return;if(this.row.viewState.isOverviewed){this.showOverview();}else{this.hideOverview();}},onRootStateUpdate_(event){if(event.delta.referenceDisplayLabel&&this.histogramSpan_){this.histogramSpan_.build(this.histogram,this.referenceHistogram);}
+if(event.delta.displayStatisticName||event.delta.referenceDisplayLabel){this.updateMwu_();this.updateContents_();}else if(event.delta.alpha&&this.mwuResult_){this.mwuResult_.compare(this.row.rootViewState.alpha);this.updateSignificance_();}
+if(this.row.viewState.isOverviewed&&(event.delta.sortColumnIndex||event.delta.sortDescending||event.delta.displayStatisticName||event.delta.referenceDisplayLabel)){if(this.overviewChart_!==undefined){this.$.overview_container.removeChild(this.overviewChart_);this.overviewChart_=undefined;}
+this.showOverview();}},onClick_(event){event.stopPropagation();},openHistogram_(){this.isHistogramOpen=true;tr.b.Timing.instant('histogram-set-table-cell','open');},closeHistogram_(){this.isHistogramOpen=false;tr.b.Timing.instant('histogram-set-table-cell','close');},updateContents_(){const isOpen=this.isHistogramOpen;this.$.empty.style.display='none';this.$.unmergeable.style.display='none';this.$.scalar.style.display='none';this.$.histogram.style.display='none';this.$.close_histogram.style.display='none';this.$.open_histogram.style.visibility='hidden';if(!this.histogram){this.$.missing.style.display='block';return;}
+this.$.missing.style.display='none';if(this.histogram instanceof tr.v.HistogramSet){this.$.unmergeable.style.display='block';return;}
+if(!(this.histogram instanceof tr.v.Histogram)){throw new Error('Invalid Histogram: '+this.histogram);}
+if(this.histogram.numValues===0){this.$.empty.style.display='block';return;}
+this.$.open_histogram.style.display='block';this.$.open_histogram.style.visibility='visible';this.$.scalar.style.display='flex';this.updateSignificance_();const referenceHistogram=this.referenceHistogram;const statName=this.histogram.getAvailableStatisticName(this.row.rootViewState.displayStatisticName,referenceHistogram);const statisticScalar=this.histogram.getStatisticScalar(statName,referenceHistogram);this.$.scalar.setValueAndUnit(statisticScalar.value,statisticScalar.unit);this.isHistogramOpen=isOpen;},showOverview(){this.$.overview_container.style.display='block';if(this.overviewChart_!==undefined)return;this.row.sortSubRows();let referenceDisplayLabel=this.row.rootViewState.referenceDisplayLabel;if(referenceDisplayLabel===this.displayLabel_){referenceDisplayLabel=undefined;}
+const displayStatisticName=this.row.rootViewState.displayStatisticName;const data=[];let unit;for(const subRow of this.row.subRows){const subHist=subRow.columns.get(this.displayLabel_);if(!(subHist instanceof tr.v.Histogram))continue;if(unit===undefined){unit=subHist.unit;}else if(unit!==subHist.unit){data.splice(0);break;}
+const refHist=subRow.columns.get(referenceDisplayLabel);const statName=subHist.getAvailableStatisticName(displayStatisticName,refHist);const statScalar=subHist.getStatisticScalar(statName,refHist);if(statScalar!==undefined){data.push({x:subRow.name,y:statScalar.value,});}}
+if(data.length<2)return;this.overviewChart_=new tr.ui.b.NameLineChart();this.$.overview_container.appendChild(this.overviewChart_);this.overviewChart_.displayXInHover=true;this.overviewChart_.hideLegend=true;this.overviewChart_.unit=unit;this.overviewChart_.overrideDataRange=this.row.overviewDataRange;this.overviewChart_.data=data;},hideOverview(){this.$.overview_container.style.display='none';}});return{};});'use strict';tr.exportTo('tr.v.ui',function(){const NAME_COLUMN_WIDTH_PX=300;Polymer({is:'tr-v-ui-histogram-set-table-name-cell',created(){this.row_=undefined;this.overviewChart_=undefined;this.cellListener_=this.onCellStateUpdate_.bind(this);this.rootListener_=this.onRootStateUpdate_.bind(this);},attached(){if(this.row){this.row.rootViewState.addUpdateListener(this.rootListener_);}},detached(){this.row.rootViewState.removeUpdateListener(this.rootListener_);},get row(){return this.row_;},build(row){if(this.row_!==undefined){throw new Error('row must be set exactly once.');}
+this.row_=row;this.row.viewState.addUpdateListener(this.onRowStateUpdate_.bind(this));this.constrainWidth=this.row.rootViewState.constrainNameColumn;if(this.isAttached){this.row.rootViewState.addUpdateListener(this.rootListener_);}
+for(const cellState of this.row.viewState.cells.values()){cellState.addUpdateListener(this.cellListener_);}
+Polymer.dom(this.$.name).textContent=this.row.name;this.title=this.row.name;if(this.row.description){this.title+='\n'+this.row.description;}
+if(this.row.overviewDataRange.isEmpty||this.row.overviewDataRange.min===this.row.overviewDataRange.max){this.$.show_overview.style.display='none';}
+let histogramCount=0;for(const cell of this.row.columns.values()){if(cell instanceof tr.v.Histogram&&cell.numValues>0){++histogramCount;}}
+if(histogramCount<=1){this.$.open_histograms.style.display='none';}},set constrainWidth(constrain){this.$.name.style.maxWidth=constrain?(this.nameWidthPx+'px'):'none';},get nameWidthPx(){return NAME_COLUMN_WIDTH_PX-(16*this.row.depth);},get isOverflowing(){return this.$.name.style.maxWidth!=='none'&&this.$.name.getBoundingClientRect().width===this.nameWidthPx;},get isOverviewed(){return this.$.overview_container.style.display==='block';},set isOverviewed(isOverviewed){if(isOverviewed===this.isOverviewed)return;if(isOverviewed){this.showOverview_();}else{this.hideOverview_();}},hideOverview_(opt_event){this.$.overview_container.style.display='none';this.$.hide_overview.style.display='none';this.$.show_overview.style.display='block';if(opt_event!==undefined){opt_event.stopPropagation();tr.b.Timing.instant('histogram-set-table-name-cell','hideOverview');this.row.viewState.isOverviewed=this.isOverviewed;}},showOverview_(opt_event){if(opt_event!==undefined){opt_event.stopPropagation();tr.b.Timing.instant('histogram-set-table-name-cell','showOverview');this.row.viewState.isOverviewed=true;}
+this.$.overview_container.style.display='block';this.$.hide_overview.style.display='block';this.$.show_overview.style.display='none';if(this.overviewChart_===undefined){const displayStatisticName=this.row.rootViewState.displayStatisticName;const data=[];let unit;for(const[displayLabel,hist]of this.row.sortedColumns()){if(!(hist instanceof tr.v.Histogram))continue;if(unit===undefined){unit=hist.unit;}else if(unit!==hist.unit){data.splice(0);break;}
+const statName=hist.getAvailableStatisticName(displayStatisticName);const statScalar=hist.getStatisticScalar(statName);if(statScalar!==undefined){data.push({x:displayLabel,y:statScalar.value,});}}
+if(data.length<2){return;}
+this.overviewChart_=new tr.ui.b.NameLineChart();this.$.overview_container.appendChild(this.overviewChart_);this.overviewChart_.displayXInHover=true;this.overviewChart_.hideLegend=true;this.overviewChart_.unit=unit;this.overviewChart_.overrideDataRange=this.row.overviewDataRange;this.overviewChart_.data=data;}},openHistograms_(event){event.stopPropagation();tr.b.Timing.instant('histogram-set-table-name-cell','openHistograms');for(const cell of this.row.cells.values()){cell.isHistogramOpen=true;}
+this.$.close_histograms.style.display='block';this.$.open_histograms.style.display='none';},closeHistograms_(event){event.stopPropagation();tr.b.Timing.instant('histogram-set-table-name-cell','closeHistograms');for(const cell of this.row.cells.values()){cell.isHistogramOpen=false;}
+this.$.open_histograms.style.display='block';this.$.close_histograms.style.display='none';},onRootStateUpdate_(event){if(event.delta.constrainNameColumn){this.constrainWidth=this.row.rootViewState.constrainNameColumn;}
+if(this.row.viewState.isOverviewed&&event.delta.displayStatisticName){this.row.resetOverviewDataRange();if(this.overviewChart_!==undefined){this.$.overview_container.removeChild(this.overviewChart_);this.overviewChart_=undefined;}
+this.showOverview_();}},onRowStateUpdate_(event){if(event.delta.isOverviewed){this.isOverviewed=this.row.viewState.isOverviewed;}},onCellStateUpdate_(event){if(!event.delta.isOpen)return;let cellCount=0;let openCellCount=0;for(const cell of this.row.cells.values()){if(!(cell.histogram instanceof tr.v.Histogram)||(cell.histogram.numValues===0)){continue;}
+++cellCount;if(cell.isHistogramOpen)++openCellCount;}
+if(cellCount<=1)return;const mostlyOpen=openCellCount>(cellCount/2);this.$.open_histograms.style.display=mostlyOpen?'none':'block';this.$.close_histograms.style.display=mostlyOpen?'block':'none';}});return{NAME_COLUMN_WIDTH_PX,};});'use strict';tr.exportTo('tr.v.ui',function(){class HistogramSetTableRow{constructor(hierarchy,baseTable,rootViewState){this.hierarchy_=hierarchy;this.baseTable_=baseTable;this.rootViewState_=rootViewState;this.viewState_=new tr.v.ui.HistogramSetTableRowState();this.viewState_.addUpdateListener(this.onViewStateUpdate_.bind(this));this.overviewDataRange_=undefined;this.nameCell_=undefined;this.cells_=new Map();this.subRows_=[];for(const subHierarchy of hierarchy.subRows){const subRow=new HistogramSetTableRow(subHierarchy,baseTable,rootViewState);this.subRows_.push(subRow);this.viewState.subRows.set(subRow.name,subRow.viewState);}
+for(const columnName of this.columns.keys()){this.viewState.cells.set(columnName,new tr.v.ui.HistogramSetTableCellState());}}
+get name(){return this.hierarchy_.name;}
+get depth(){return this.hierarchy_.depth;}
+get description(){return this.hierarchy_.description;}
+get columns(){return this.hierarchy_.columns;}*sortedColumns(){for(const col of this.baseTable_.tableColumns){yield[col.displayLabel,this.hierarchy_.columns.get(col.displayLabel),];}}
+get overviewDataRange(){if(this.overviewDataRange_===undefined){this.overviewDataRange_=new tr.b.math.Range();const displayStatisticName=this.rootViewState.displayStatisticName;const referenceDisplayLabel=this.rootViewState.referenceDisplayLabel;for(const[displayLabel,hist]of this.columns){if(hist instanceof tr.v.Histogram){const statName=hist.getAvailableStatisticName(displayStatisticName);const statScalar=hist.getStatisticScalar(statName);if(statScalar!==undefined){this.overviewDataRange_.addValue(statScalar.value);}}
+for(const subRow of this.subRows){const subHist=subRow.columns.get(displayLabel);if(!(subHist instanceof tr.v.Histogram))continue;const refHist=subRow.columns.get(referenceDisplayLabel);const statName=subHist.getAvailableStatisticName(displayStatisticName,refHist);const statScalar=subHist.getStatisticScalar(statName,refHist);if(statScalar!==undefined){this.overviewDataRange_.addValue(statScalar.value);}}}}
+return this.overviewDataRange_;}
+resetOverviewDataRange(){this.overviewDataRange_=undefined;}
+get rootViewState(){return this.rootViewState_;}
+get cells(){return this.cells_;}
+get subRows(){return this.subRows_;}
+get viewState(){return this.viewState_;}*walk(){yield this;for(const row of this.subRows)yield*row.walk();}
+static*walkAll(rootRows){for(const rootRow of rootRows)yield*rootRow.walk();}
+get nameCell(){if(this.nameCell_===undefined){this.nameCell_=document.createElement('tr-v-ui-histogram-set-table-name-cell');this.nameCell_.build(this);}
+return this.nameCell_;}
+getCell(columnName){if(this.cells.has(columnName))return this.cells.get(columnName);const cell=document.createElement('tr-v-ui-histogram-set-table-cell');cell.build(this,columnName,this.viewState.cells.get(columnName));this.cells.set(columnName,cell);return cell;}
+compareNames(other){return this.name.localeCompare(other.name);}
+compareCells(other,displayLabel){const referenceDisplayLabel=this.rootViewState.referenceDisplayLabel;let referenceCellA;let referenceCellB;if(referenceDisplayLabel&&referenceDisplayLabel!==displayLabel){referenceCellA=this.columns.get(referenceDisplayLabel);referenceCellB=other.columns.get(referenceDisplayLabel);}
+const cellA=this.columns.get(displayLabel);let valueA=0;if(cellA instanceof tr.v.Histogram){const statisticA=cellA.getAvailableStatisticName(this.rootViewState.displayStatisticName,referenceCellA);const scalarA=cellA.getStatisticScalar(statisticA,referenceCellA);if(scalarA){valueA=scalarA.value;}}
+const cellB=other.columns.get(displayLabel);let valueB=0;if(cellB instanceof tr.v.Histogram){const statisticB=cellB.getAvailableStatisticName(this.rootViewState.displayStatisticName,referenceCellB);const scalarB=cellB.getStatisticScalar(statisticB,referenceCellB);if(scalarB){valueB=scalarB.value;}}
+return valueA-valueB;}
+onViewStateUpdate_(event){if(event.delta.isExpanded){this.baseTable_.setExpandedForTableRow(this,this.viewState.isExpanded);}
+if(event.delta.subRows){throw new Error('HistogramSetTableRow.subRows must not be reassigned.');}
+if(event.delta.cells){for(const[displayLabel,cell]of this.cells){if(cell.viewState!==this.viewState.cells.get(displayLabel)){throw new Error('Only HistogramSetTableRow may update cells');}}}}
+async restoreState(vs){await this.viewState.update({isExpanded:vs.isExpanded,isOverviewed:vs.isOverviewed,});for(const[displayLabel,cell]of this.cells){const previousState=vs.cells.get(displayLabel);if(!previousState)continue;await cell.viewState.updateFromViewState(previousState);}
+for(const row of this.subRows){const previousState=vs.subRows.get(row.name);if(!previousState)continue;await row.restoreState(previousState);}}
+sortSubRows(){const sortColumn=this.baseTable_.tableColumns[this.rootViewState_.sortColumnIndex];if(sortColumn===undefined)return;this.subRows_.sort(sortColumn.cmp);if(this.rootViewState_.sortDescending){this.subRows_.reverse();}}}
+return{HistogramSetTableRow,};});'use strict';tr.exportTo('tr.v.ui',function(){const MIDLINE_HORIZONTAL_ELLIPSIS=String.fromCharCode(0x22ef);function escapeRegExp(str){return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,'\\$&');}
+Polymer({is:'tr-v-ui-histogram-set-table',created(){this.viewState_=undefined;this.progress_=()=>Promise.resolve();this.nameColumnTitle_=undefined;this.displayLabels_=[];this.histograms_=undefined;this.sourceHistograms_=undefined;this.filteredHistograms_=undefined;this.groupedHistograms_=undefined;this.hierarchies_=undefined;this.tableRows_=undefined;this.sortColumnChangedListener_=e=>this.onSortColumnChanged_(e);},ready(){this.$.table.zebra=true;this.addEventListener('sort-column-changed',this.sortColumnChangedListener_);this.addEventListener('requestSelectionChange',this.onRequestSelectionChange_.bind(this));this.addEventListener('row-expanded-changed',this.onRowExpandedChanged_.bind(this));},get viewState(){return this.viewState_;},set viewState(vs){if(this.viewState_){throw new Error('viewState must be set exactly once.');}
+this.viewState_=vs;this.viewState.addUpdateListener(this.onViewStateUpdate_.bind(this));},get histograms(){return this.histograms_;},async build(histograms,sourceHistograms,displayLabels,opt_progress){this.histograms_=histograms;this.sourceHistograms_=sourceHistograms;this.filteredHistograms_=undefined;this.groupedHistograms_=undefined;this.displayLabels_=displayLabels;if(opt_progress!==undefined)this.progress_=opt_progress;if(histograms.length===0){throw new Error('histogram-set-table requires non-empty HistogramSet.');}
+await this.progress_('Building columns...');this.$.table.tableColumns=[{title:this.buildNameColumnTitle_(),value:row=>row.nameCell,cmp:(a,b)=>a.compareNames(b),}].concat(displayLabels.map(l=>this.buildColumn_(l)));tr.b.Timing.instant('histogram-set-table','columnCount',this.$.table.tableColumns.length);await this.updateContents_();this.fire('display-ready');this.progress_=()=>Promise.resolve();this.checkNameColumnOverflow_(tr.v.ui.HistogramSetTableRow.walkAll(this.$.table.tableRows));},buildNameColumnTitle_(){this.nameColumnTitle_=document.createElement('span');this.nameColumnTitle_.style.display='inline-flex';const nameEl=document.createElement('span');nameEl.textContent='Name';this.nameColumnTitle_.appendChild(nameEl);const toggleWidthEl=document.createElement('span');toggleWidthEl.style.fontWeight='bold';toggleWidthEl.style.background='#bbb';toggleWidthEl.style.color='#333';toggleWidthEl.style.padding='0px 3px';toggleWidthEl.style.marginRight='8px';toggleWidthEl.style.display='none';toggleWidthEl.textContent=MIDLINE_HORIZONTAL_ELLIPSIS;toggleWidthEl.addEventListener('click',this.toggleNameColumnWidth_.bind(this));this.nameColumnTitle_.appendChild(toggleWidthEl);return this.nameColumnTitle_;},toggleNameColumnWidth_(opt_event){this.viewState.update({constrainNameColumn:!this.viewState.constrainNameColumn,});if(opt_event!==undefined){opt_event.stopPropagation();opt_event.preventDefault();tr.b.Timing.instant('histogram-set-table','nameColumn'+
+(this.viewState.constrainNameColumn?'Constrained':'Unconstrained'));}},buildColumn_(displayLabel){const title=document.createElement('span');title.textContent=displayLabel;title.style.whiteSpace='pre';return{displayLabel,title,value:row=>row.getCell(displayLabel),cmp:(rowA,rowB)=>rowA.compareCells(rowB,displayLabel),};},async updateContents_(){const previousRowStates=this.viewState.tableRowStates;if(!this.filteredHistograms_){await this.progress_('Filtering rows...');this.filteredHistograms_=this.viewState.showAll?this.histograms:this.sourceHistograms_;if(this.viewState.searchQuery){let query;try{query=new RegExp(this.viewState.searchQuery);}catch(e){}
+if(query!==undefined){this.filteredHistograms_=new tr.v.HistogramSet([...this.filteredHistograms_].filter(hist=>hist.name.match(query)));if(this.filteredHistograms_.length===0&&!this.viewState.showAll){await this.viewState.update({showAll:true});return;}}}
+this.groupedHistograms_=undefined;}
+if(!this.groupedHistograms_){await this.progress_('Grouping Histograms...');this.groupHistograms_();}
+if(!this.hierarchies_){await this.progress_('Merging Histograms...');this.hierarchies_=tr.v.HistogramSetHierarchy.build(this.groupedHistograms_);this.tableRows_=undefined;}
+const tableRowsDirty=this.tableRows_===undefined;if(tableRowsDirty){this.tableRows_=this.hierarchies_.map(hierarchy=>new tr.v.ui.HistogramSetTableRow(hierarchy,this.$.table,this.viewState));tr.b.Timing.instant('histogram-set-table','rootRowCount',this.tableRows_.length);const namesToRowStates=new Map();for(const row of this.tableRows_){namesToRowStates.set(row.name,row.viewState);}
+await this.viewState.update({tableRowStates:namesToRowStates});}
+await this.progress_('Configuring table...');this.nameColumnTitle_.children[1].style.filter=this.viewState.constrainNameColumn?'invert(100%)':'';const referenceDisplayLabelIndex=this.displayLabels_.indexOf(this.viewState.referenceDisplayLabel);this.$.table.selectedTableColumnIndex=(referenceDisplayLabelIndex<0)?undefined:(1+referenceDisplayLabelIndex);this.removeEventListener('sort-column-changed',this.sortColumnChangedListener_);this.$.table.sortColumnIndex=this.viewState.sortColumnIndex;this.$.table.sortDescending=this.viewState.sortDescending;this.addEventListener('sort-column-changed',this.sortColumnChangedListener_);if(tableRowsDirty){await this.progress_('Building DOM...');this.$.table.tableRows=this.tableRows_;for(const row of this.tableRows_){const previousState=previousRowStates.get(row.name);if(!previousState)continue;await row.restoreState(previousState);}}
+this.$.table.rebuild();},async onRowExpandedChanged_(event){event.row.viewState.isExpanded=this.$.table.getExpandedForTableRow(event.row);tr.b.Timing.instant('histogram-set-table','row'+(event.row.viewState.isExpanded?'Expanded':'Collapsed'));if(this.nameColumnTitle_.children[1].style.display==='block')return;await tr.b.animationFrame();this.checkNameColumnOverflow_(event.row.subRows);},checkNameColumnOverflow_(rows){for(const row of rows){if(!row.nameCell.isOverflowing)continue;const[nameSpan,dots]=Array.from(this.nameColumnTitle_.children);dots.style.display='block';const labelWidthPx=tr.v.ui.NAME_COLUMN_WIDTH_PX-
+dots.getBoundingClientRect().width;nameSpan.style.width=labelWidthPx+'px';return;}},groupHistograms_(){const groupings=this.viewState.groupings.slice();groupings.push(tr.v.HistogramGrouping.DISPLAY_LABEL);function canSkipGrouping(grouping,groupedHistograms){if(groupedHistograms.size>1)return false;if(grouping.key===groupings[0].key)return false;if(grouping.key===tr.v.HistogramGrouping.DISPLAY_LABEL.key){return false;}
+return true;}
+this.groupedHistograms_=this.filteredHistograms_.groupHistogramsRecursively(groupings,canSkipGrouping);this.hierarchies_=undefined;},async onViewStateUpdate_(event){if(this.histograms_===undefined)return;if(event.delta.searchQuery!==undefined||event.delta.showAll!==undefined){this.filteredHistograms_=undefined;}
+if(event.delta.groupings!==undefined){this.groupedHistograms_=undefined;}
+if(event.delta.displayStatistic!==undefined&&this.$.table.sortColumnIndex>0){this.$.table.sortColumnIndex=undefined;}
+if(event.delta.referenceDisplayLabel!==undefined||event.delta.displayStatisticName!==undefined){this.$.table.tableRows=this.$.table.tableRows;}
+if(event.delta.tableRowStates){if(this.tableRows_.length!==this.viewState.tableRowStates.size){throw new Error('Only histogram-set-table may update tableRowStates');}
+for(const row of this.tableRows_){if(this.viewState.tableRowStates.get(row.name)!==row.viewState){throw new Error('Only histogram-set-table may update tableRowStates');}}
+return;}
+await this.updateContents_();},onSortColumnChanged_(event){tr.b.Timing.instant('histogram-set-table','sortColumn');this.viewState.update({sortColumnIndex:event.sortColumnIndex,sortDescending:event.sortDescending,});},onRequestSelectionChange_(event){if(event.selection instanceof tr.model.EventSet)return;event.stopPropagation();tr.b.Timing.instant('histogram-set-table','selectHistogramNames');let histogramNames=event.selection;histogramNames.sort();histogramNames=histogramNames.map(escapeRegExp).join('|');this.viewState.update({showAll:true,searchQuery:`^(${histogramNames})$`,});},get leafHistograms(){const histograms=new tr.v.HistogramSet();for(const row of
+tr.v.ui.HistogramSetTableRow.walkAll(this.$.table.tableRows)){if(row.subRows.length)continue;for(const hist of row.columns.values()){if(!(hist instanceof tr.v.Histogram))continue;histograms.addHistogram(hist);}}
+return histograms;}});return{MIDLINE_HORIZONTAL_ELLIPSIS,};});'use strict';tr.exportTo('tr.v.ui',function(){const PAGE_BREAKDOWN_KEY='pageBreakdown';Polymer({is:'tr-v-ui-metrics-visualization',created(){this.charts_=new Map();},ready(){this.$.start.addEventListener('keydown',(e)=>{if(e.key==='Enter')this.filterByPercentile_();});this.$.end.addEventListener('keydown',(e)=>{if(e.key==='Enter')this.filterByPercentile_();});this.$.search_page.addEventListener('keydown',(e)=>{if(e.key==='Enter')this.searchByPage_();});},build(chartData){this.title_=chartData.title;this.aggregateData_=chartData.aggregate;this.data_=chartData.page;this.submetricsData_=chartData.submetrics;this.benchmarkCount_=chartData.aggregate.length;const aggregateChart=this.initializeColumnChart(this.title_);Polymer.dom(this.$.aggregateContainer).appendChild(aggregateChart);this.charts_.set(tr.v.ui.AGGREGATE_KEY,aggregateChart);this.setChartColors_(tr.v.ui.AGGREGATE_KEY);aggregateChart.data=chartData.aggregate;this.setChartSize_(tr.v.ui.AGGREGATE_KEY);const newChart=this.initializeColumnChart(this.title_+' Breakdown');newChart.enableToolTip=true;newChart.toolTipCallBack=(rect)=>this.openChildChart_(rect);Polymer.dom(this.$.pageByPageContainer).appendChild(newChart);this.charts_.set(PAGE_BREAKDOWN_KEY,newChart);this.setChartColors_(PAGE_BREAKDOWN_KEY);newChart.data=this.data_;this.setChartSize_(PAGE_BREAKDOWN_KEY);},setChartSize_(page){const chart=this.charts_.get(page);const pageCount=chart.data.length;chart.graphHeight=tr.b.math.clamp(pageCount*20,400,600);chart.graphWidth=tr.b.math.clamp(pageCount*30,200,1000);},setChartColors_(page){const chart=this.charts_.get(page);const metrics=tr.v.ui.METRICS.get(this.title_);for(let i=0;i<this.benchmarkCount_;++i){for(let j=0;j<metrics.length;++j){const mainColorIndex=j%tr.v.ui.COLORS.length;const subColorIndex=i%tr.v.ui.COLORS[mainColorIndex].length;const color=tr.v.ui.COLORS[mainColorIndex][subColorIndex];const series=metrics[j]+'-'+this.aggregateData_[i].x;chart.getDataSeries(series).color=color;if(i===0){chart.getDataSeries(series).title=metrics[j];}else{chart.getDataSeries(series).title='';}}}},initializeColumnChart(title){const newChart=new tr.ui.b.NameColumnChart();newChart.hideLegend=false;newChart.isStacked=true;newChart.yAxisLabel='ms';newChart.hideXAxis=true;newChart.displayXInHover=true;newChart.isGrouped=true;newChart.showTitleInLegend=true;newChart.chartTitle=title;newChart.titleHeight='14pt';return newChart;},initializeChildChart_(title,height,width){const div=document.createElement('div');div.classList.add('container');Polymer.dom(this.$.submetricsContainer).insertBefore(div,this.$.submetricsContainer.firstChild);const childChart=new tr.ui.b.NameBarChart();childChart.xAxisLabel='ms';childChart.chartTitle=title;childChart.graphHeight=height;childChart.graphWidth=width;childChart.titleHeight='14pt';childChart.isStacked=true;childChart.hideLegend=true;childChart.isGrouped=true;childChart.isWaterfall=true;div.appendChild(childChart);const button=this.initializeCloseButton_(div,this.$.submetricsContainer);div.appendChild(button);return childChart;},initializeCloseButton_(div,parent){const button=this.$.close.cloneNode(true);button.style.display='inline-block';button.addEventListener('click',()=>{Polymer.dom(parent).removeChild(div);});return button;},openChildChart_(rect){const metrics=tr.v.ui.METRICS.get(this.title_);let metric;let metricIndex;for(let i=0;i<metrics.length;++i){if(rect.key.startsWith(metrics[i])){metric=metrics[i];metricIndex=i;break;}}
+const page=rect.datum.group;const title=this.title_+' '+metric+': '+page;const submetrics=this.submetricsData_.get(page).get(metric);const width=tr.b.math.clamp(submetrics.size*150,300,700);const height=tr.b.math.clamp(submetrics.size*this.benchmarkCount_*50,300,700);const childChart=this.initializeChildChart_(title,height,width);childChart.data=this.processSubmetrics_(childChart,submetrics,0,metricIndex).data;},processSubmetrics_(chart,submetrics,hideValue,metricIndex){const finalData=[];let submetricIndex=0;for(const submetric of submetrics.values()){let benchmarkIndex=0;for(const benchmark of submetric.values()){benchmark.hide=!hideValue?0:hideValue;const series=benchmark.x+'-'+benchmark.group;const mainColorIndex=metricIndex%tr.v.ui.COLORS.length;const subColorIndex=benchmarkIndex%tr.v.ui.COLORS[mainColorIndex].length;chart.getDataSeries(series).color=tr.v.ui.COLORS[mainColorIndex][subColorIndex];if(benchmarkIndex===(this.benchmarkCount_-1)){hideValue+=benchmark[series];}
+finalData.push(benchmark);benchmarkIndex++;}
+submetricIndex++;}
+return{data:finalData,hide:hideValue};},filterByPercentile_(){const startPercentile=this.$.start.value;const endPercentile=this.$.end.value;if(startPercentile===''||endPercentile==='')return;const length=this.data_.length/(this.benchmarkCount_+1);const startIndex=this.getPercentileIndex_(startPercentile,length);const endIndex=this.getPercentileIndex_(endPercentile,length);this.charts_.get(PAGE_BREAKDOWN_KEY).data=this.data_.slice(startIndex,endIndex);},getPercentileIndex_(percentile,arrayLength){const index=Math.ceil(arrayLength*(percentile/100.0));if(index===-1)return 0;if(index>=arrayLength)return arrayLength;return index*this.benchmarkCount_;},searchByPage_(){const criteria=this.$.search_page.value;if(criteria==='')return;const query=new RegExp(criteria);const filteredData=[...this.data_].filter(group=>{if(group.group)return group.group.match(query);return false;});if(filteredData.length<1){this.$.search_error.style.display='block';return;}
+const page=filteredData[0].group;const title=this.title_+' Breakdown: '+page;const metricToSubmetricMap=this.submetricsData_.get(page);let totalSubmetrics=0;for(const submetrics of metricToSubmetricMap.values()){for(const benchmark of submetrics.values()){totalSubmetrics+=benchmark.length;}}
+const width=tr.b.math.clamp(totalSubmetrics*150,300,700);const height=tr.b.math.clamp(totalSubmetrics*this.benchmarkCount_*30,300,700);const childChart=this.initializeChildChart_(title,height,width);const childData=[];let hide=0;let metricIndex=0;for(const submetrics of metricToSubmetricMap.values()){const submetricsData=this.processSubmetrics_(childChart,submetrics,hide,metricIndex);childData.push(...submetricsData.data);hide=submetricsData.hide;metricIndex++;}
+childChart.data=childData;},});});'use strict';Polymer({is:'tr-v-ui-raster-visualization',ready(){this.$.pageSelector.addEventListener('click',()=>{this.selectPage_();});this.$.search_page.addEventListener('keydown',(e)=>{if(e.key==='Enter')this.searchByPage_();});this.$.search_button.addEventListener('click',()=>{this.searchByPage_();});},build(chartData){this.data_=chartData;const aggregateChart=this.createChart_('Aggregate Data by Run');Polymer.dom(this.$.aggregateContainer).appendChild(aggregateChart);aggregateChart.enableToolTip=true;aggregateChart.toolTipCallBack=(rect)=>this.openBenchmarkChart_(rect);this.setChartColors_(aggregateChart,this.data_.get(tr.v.ui.AGGREGATE_KEY));aggregateChart.data=this.data_.get(tr.v.ui.AGGREGATE_KEY);this.setChartSize_(aggregateChart,this.data_.get(tr.v.ui.AGGREGATE_KEY).length);for(const page of this.data_.keys()){if(page===tr.v.ui.AGGREGATE_KEY)continue;const option=document.createElement('option');option.textContent=page;option.value=page;this.$.pageSelector.appendChild(option);}},setChartSize_(chart,pageCount,dataLength){chart.graphHeight=tr.b.math.clamp(pageCount*25,175,1000);chart.graphWidth=tr.b.math.clamp(pageCount*25,500,1000);},setChartColors_(chart,data){const metrics=new Map();let count=0;for(const thread of tr.v.ui.FRAME.values()){for(const metric of thread.keys()){metrics.set(metric,count);count++;}}
+for(let i=0;i<Math.floor(data.length/tr.v.ui.FRAME.length);++i){let j=0;for(const[threadName,thread]of tr.v.ui.FRAME.entries()){for(const metric of thread.keys()){let color='transparent';if(thread.get(metric)){const mainColorIndex=metrics.get(metric)%tr.v.ui.COLORS.length;const subColorIndex=i%tr.v.ui.COLORS[mainColorIndex].length;color=tr.v.ui.COLORS[mainColorIndex][subColorIndex];}
+const series=metric+'-'+data[i*2+j].x+'-'+threadName;chart.getDataSeries(series).color=color;chart.getDataSeries(series).title=!i?metric:'';}
+j++;}}},createChart_(title){const newChart=new tr.ui.b.NameBarChart();newChart.chartTitle=title;newChart.xAxisLabel='ms';newChart.hideLegend=false;newChart.showTitleInLegend=true;newChart.hideYAxis=true;newChart.isStacked=true;newChart.displayXInHover=true;newChart.isGrouped=true;return newChart;},openBenchmarkChart_(rect){const benchmarkIndex=Math.floor(rect.index/tr.v.ui.FRAME.length);const title=rect.datum.x;const div=document.createElement('div');Polymer.dom(this.$.pageContainer).insertBefore(div,this.$.pageContainer.firstChild);const chart=this.createChart_(title);div.appendChild(chart);const button=this.initializeCloseButton_(div,this.$.pageContainer);div.appendChild(button);const newDataSet=[];for(const page of this.data_.keys()){if(page===tr.v.ui.AGGREGATE_KEY)continue;for(let i=0;i<tr.v.ui.FRAME.length;i++){newDataSet.push(this.data_.get(page)[benchmarkIndex*tr.v.ui.FRAME.length+i]);}}
+this.setChartColors_(chart,newDataSet);chart.data=newDataSet;this.setChartSize_(chart,newDataSet.length);},selectPage_(){const div=document.createElement('div');const page=this.$.pageSelector.value;if(page==='')return;Polymer.dom(this.$.pageContainer).insertBefore(div,this.$.pageContainer.firstChild);const pageChart=this.createChart_(page);div.appendChild(pageChart);const button=this.initializeCloseButton_(div,this.$.pageContainer);div.appendChild(button);const pageData=this.data_.get(page);this.setChartColors_(pageChart,pageData);pageChart.data=pageData;this.setChartSize_(pageChart,pageData.length);},searchByPage_(){const criteria=this.$.search_page.value;if(criteria==='')return;const query=new RegExp(criteria);const filteredData=[...this.data_.keys()].filter(page=>page.match(query));if(filteredData.length<1){this.$.search_error.style.display='block';return;}
+const page=filteredData[0];const div=document.createElement('div');Polymer.dom(this.$.pageContainer).insertBefore(div,this.$.pageContainer.firstChild);const pageChart=this.createChart_(page);div.appendChild(pageChart);const button=this.initializeCloseButton_(div,this.$.pageContainer);div.appendChild(button);const pageData=this.data_.get(page);this.setChartColors_(pageChart,pageData);pageChart.data=pageData;this.setChartSize_(pageChart,pageData.length);},initializeCloseButton_(div,parent){const button=this.$.close.cloneNode(true);button.style.display='inline-block';button.addEventListener('click',()=>{Polymer.dom(parent).removeChild(div);});return button;},});'use strict';tr.exportTo('tr.v.ui',function(){const STATISTICS_KEY='statistics';const SUBMETRICS_KEY='submetrics';const AGGREGATE_KEY='aggregate';const RASTER_START_METRIC_KEY='pipeline:begin_frame_to_raster_start';const COLORS=[['#FFD740','#FFC400','#FFAB00','#E29800'],['#FF6E40','#FF3D00','#DD2C00','#A32000'],['#40C4FF','#00B0FF','#0091EA','#006DAF'],['#89C641','#54B503','#4AA510','#377A0D'],['#B388FF','#7C4DFF','#651FFF','#6200EA'],['#FF80AB','#FF4081','#F50057','#C51162'],['#FFAB40','#FF9100','#FF6D00','#D65C02'],['#8C9EFF','#536DFE','#3D5AFE','#304FFE']];const FRAME=[new Map([['pipeline:begin_frame_to_raster_start',false],['pipeline:begin_frame_to_raster_end',true]]),new Map([['pipeline:begin_frame_transport',true],['pipeline:begin_frame_to_frame_submission',true],['pipeline:frame_submission_to_display',true],['pipeline:draw',true]])];const METRICS=new Map([['Pipeline',['pipeline:begin_frame_transport','pipeline:begin_frame_to_frame_submission','pipeline:frame_submission_to_display','pipeline:draw']],['Thread',['thread_browser_cpu_time_per_frame','thread_display_compositor_cpu_time_per_frame','thread_GPU_cpu_time_per_frame','thread_IO_cpu_time_per_frame','thread_other_cpu_time_per_frame','thread_raster_cpu_time_per_frame','thread_renderer_compositor_cpu_time_per_frame','thread_renderer_main_cpu_time_per_frame']]]);function getValueFromMap(key,map){let retrievedValue=map.get(key);if(!retrievedValue){retrievedValue=new Map();map.set(key,retrievedValue);}
+return retrievedValue;}
+Polymer({is:'tr-v-ui-visualizations-data-container',created(){this.orderedBenchmarks_=[];this.groupedData_=new Map();},build(leafHistograms,histograms){if(!leafHistograms||leafHistograms.length<1||!histograms||histograms.length<1){this.$.data_error.style.display='block';return;}
+this.processHistograms_(this.groupHistograms_(histograms),this.groupHistograms_(leafHistograms));this.buildCharts_();},processHistograms_(histograms,leafHistograms){const benchmarkStartGrouping=tr.v.HistogramGrouping.BY_KEY.get(tr.v.d.RESERVED_NAMES.BENCHMARK_START);const benchmarkToStartTime=new Map();for(const[metric,benchmarks]of histograms.entries()){for(const[benchmark,pages]of leafHistograms.get(metric).entries()){for(const[page,histograms]of pages.entries()){for(const histogram of histograms){const aggregateToBenchmarkMap=getValueFromMap(AGGREGATE_KEY,this.groupedData_);const benchmarkToMetricMap=getValueFromMap(benchmark,aggregateToBenchmarkMap);benchmarkToMetricMap.set(metric,new Map([[STATISTICS_KEY,histogram.running]]));}}}
+for(const[benchmark,pages]of benchmarks.entries()){for(const[page,histograms]of pages.entries()){for(const histogram of histograms){if(!benchmarkToStartTime.get(benchmark)){benchmarkToStartTime.set(benchmark,benchmarkStartGrouping.callback(histogram));}
+const pageToBenchmarkMap=getValueFromMap(page,this.groupedData_);const benchmarkToMetricMap=getValueFromMap(benchmark,pageToBenchmarkMap);const mergedSubmetrics=new tr.v.d.DiagnosticMap();for(const bin of histogram.allBins){for(const map of bin.diagnosticMaps){mergedSubmetrics.addDiagnostics(map);}}
+if(benchmarkToMetricMap.get(metric))continue;benchmarkToMetricMap.set(metric,new Map([[STATISTICS_KEY,histogram.running],[SUBMETRICS_KEY,mergedSubmetrics.get('breakdown')]]));}}}}
+this.orderedBenchmarks_=this.sortBenchmarks_(benchmarkToStartTime);},groupHistograms_(histograms){const groupings=[tr.v.HistogramGrouping.HISTOGRAM_NAME,tr.v.HistogramGrouping.DISPLAY_LABEL,tr.v.HistogramGrouping.BY_KEY.get(tr.v.d.RESERVED_NAMES.STORIES)];return histograms.groupHistogramsRecursively(groupings);},sortBenchmarks_(benchmarks){return Array.from(benchmarks.keys()).sort((a,b)=>{Date.parse(benchmarks.get(a))-Date.parse(benchmarks.get(b));});},getSeriesKey_(metric,benchmark){return metric+'-'+benchmark;},buildCharts_(){const rasterDataToBePassed=this.buildRasterChart_();this.$.rasterVisualization.build(rasterDataToBePassed);for(const chartName of METRICS.keys()){const metricsDataToBePassed=this.buildMetricsData_(chartName);const newChart=this.$.metricsVisualization.cloneNode(true);newChart.style.display='block';Polymer.dom(this.$.metrics_container).appendChild(newChart);newChart.build(metricsDataToBePassed);}},buildRasterChart_(){const orderedPages=[...this.groupedData_.keys()].filter((page)=>this.filterPagesWithoutRasterMetric_(page)).sort((a,b)=>this.sortByRasterStart_(a,b));const allChartData=new Map();for(const page of orderedPages){const pageMap=this.groupedData_.get(page);let chartData=[];for(const benchmark of this.orderedBenchmarks_){if(!pageMap.has(benchmark))continue;const benchmarkMap=pageMap.get(benchmark);const benchmarkData=[];if(benchmarkMap.get(RASTER_START_METRIC_KEY)===undefined){continue;}
+for(const[threadName,thread]of FRAME.entries()){const data={x:benchmark,hide:0};if(page!==AGGREGATE_KEY)data.group=page;let rasterBegin=0;for(const metric of thread.keys()){const metricMap=benchmarkMap.get(metric);const key=this.getSeriesKey_(metric,data.x+'-'+threadName);const stats=metricMap.get(STATISTICS_KEY);const mean=stats?stats.mean:0;let roundedMean=Math.round(mean*100)/100;if(metric===RASTER_START_METRIC_KEY){rasterBegin=roundedMean;}else if(metric==='pipeline:begin_frame_to_raster_end'){roundedMean-=rasterBegin;}
+data[key]=roundedMean;}
+benchmarkData.push(data);}
+chartData=chartData.concat(benchmarkData);}
+allChartData.set(page,chartData);}
+return allChartData;},buildMetricsData_(chartName){const orderedPages=[...this.groupedData_.keys()].sort((a,b)=>this.sortByTotal_(a,b,chartName));const chartData=[];const aggregateChart=[];for(const page of orderedPages){const pageMap=this.groupedData_.get(page);for(const benchmark of this.orderedBenchmarks_){if(!pageMap.has(benchmark))continue;const data={x:benchmark,group:page};const benchmarkMap=pageMap.get(benchmark);for(const metric of METRICS.get(chartName)){const metricMap=benchmarkMap.get(metric);const key=this.getSeriesKey_(metric,benchmark);const stats=metricMap.get(STATISTICS_KEY);const mean=stats?stats.mean:0;data[key]=Math.round(mean*100)/100;}
+if(page===AGGREGATE_KEY){aggregateChart.push(data);}else{chartData.push(data);}}
+chartData.push({});}
+chartData.shift();return{title:chartName,aggregate:aggregateChart,page:chartData,submetrics:this.processSubmetricsData_(chartName)};},submetricsHelper_(submetric,value,benchmark,metricToSubmetricMap){let submetricToBenchmarkMap=metricToSubmetricMap.get(submetric);if(!submetricToBenchmarkMap){submetricToBenchmarkMap=[];metricToSubmetricMap.set(submetric,submetricToBenchmarkMap);}
+const data={x:submetric,hide:0,group:benchmark};const mean=value;const roundedMean=Math.round(mean*100)/100;if(!roundedMean)return;data[this.getSeriesKey_(submetric,benchmark)]=roundedMean;submetricToBenchmarkMap.push(data);},processSubmetricsData_(chartName){const submetrics=new Map();for(const[page,pageMap]of this.groupedData_.entries()){if(page===AGGREGATE_KEY)continue;const pageToMetricMap=getValueFromMap(page,submetrics);for(const benchmark of this.orderedBenchmarks_){const benchmarkMap=pageMap.get(benchmark);if(!benchmarkMap)continue;for(const metric of METRICS.get(chartName)){const metricMap=benchmarkMap.get(metric);const metricToSubmetricMap=getValueFromMap(metric,pageToMetricMap);const submetrics=metricMap.get(SUBMETRICS_KEY);if(!submetrics){this.submetricsHelper_(metric,metricMap.get(STATISTICS_KEY),benchmark,metricToSubmetricMap);continue;}
+for(const[submetric,value]of[...submetrics]){this.submetricsHelper_(submetric,value,benchmark,metricToSubmetricMap);}}}}
+return submetrics;},sortByTotal_(a,b,chartName){if(a===AGGREGATE_KEY)return-1;if(b===AGGREGATE_KEY)return 1;let aValue=0;const aMap=this.groupedData_.get(a);if(aMap.get(this.orderedBenchmarks_[0])!==undefined){for(const metric of METRICS.get(chartName)){const aMetricMap=aMap.get(this.orderedBenchmarks_[0]).get(metric);const aStats=aMetricMap.get(STATISTICS_KEY);aValue+=aStats?aStats.mean:0;}}
+let bValue=0;const bMap=this.groupedData_.get(b);if(bMap.get(this.orderedBenchmarks_[0])!==undefined){for(const metric of METRICS.get(chartName)){const bMetricMap=bMap.get(this.orderedBenchmarks_[0]).get(metric);const bStats=bMetricMap.get(STATISTICS_KEY);bValue+=bStats?bStats.mean:0;}}
+return aValue-bValue;},filterPagesWithoutRasterMetric_(page){const pageMap=this.groupedData_.get(page);for(const benchmark of this.orderedBenchmarks_){const pageMetricMap=pageMap.get(benchmark);if(!pageMetricMap)continue;const wantedMetric=pageMetricMap.get(RASTER_START_METRIC_KEY);if(wantedMetric!==undefined)return true;}
+return false;},sortByRasterStart_(a,b){if(a===AGGREGATE_KEY)return 1;if(b===AGGREGATE_KEY)return-1;let aValue=0;const aMap=this.groupedData_.get(a);if(aMap.get(this.orderedBenchmarks_[0])!==undefined){const aMetricMap=aMap.get(this.orderedBenchmarks_[0]).get(RASTER_START_METRIC_KEY);const aStats=aMetricMap.get(STATISTICS_KEY);aValue=aStats?aStats.mean:0;}
+let bValue=0;const bMap=this.groupedData_.get(b);if(bMap.get(this.orderedBenchmarks_[0])!==undefined){const bMetricMap=bMap.get(this.orderedBenchmarks_[0]).get(RASTER_START_METRIC_KEY);const bStats=bMetricMap.get(STATISTICS_KEY);bValue=bStats?bStats.mean:0;}
+return bValue-aValue;},});return{STATISTICS_KEY,SUBMETRICS_KEY,AGGREGATE_KEY,COLORS,FRAME,METRICS,getValueFromMap,};});'use strict';tr.exportTo('tr.v.ui',function(){Polymer({is:'tr-v-ui-histogram-set-view',listeners:{export:'onExport_',loadVisualization:'onLoadVisualization_'},created(){this.brushingStateController_=new tr.ui.NullBrushingStateController();this.viewState_=new tr.v.ui.HistogramSetViewState();this.visualizationLoaded_=false;},ready(){this.$.table.viewState=this.viewState;this.$.controls.viewState=this.viewState;},attached(){this.brushingStateController.parentController=tr.c.BrushingStateController.getControllerForElement(this.parentNode);},get brushingStateController(){return this.brushingStateController_;},get viewState(){return this.viewState_;},get histograms(){return this.$.table.histograms;},async build(histograms,opt_options){const options=opt_options||{};const progress=options.progress||(()=>Promise.resolve());if(options.helpHref)this.$.controls.helpHref=options.helpHref;if(options.feedbackHref){this.$.controls.feedbackHref=options.feedbackHref;}
+if(histograms===undefined||histograms.length===0){this.$.container.style.display='none';this.$.zero.style.display='block';this.style.display='block';return;}
+this.$.zero.style.display='none';this.$.container.style.display='block';this.$.container.style.maxHeight=(window.innerHeight-16)+'px';const buildMark=tr.b.Timing.mark('histogram-set-view','build');await progress('Finding important Histograms...');const sourceHistogramsMark=tr.b.Timing.mark('histogram-set-view','sourceHistograms');const sourceHistograms=histograms.sourceHistograms;sourceHistogramsMark.end();this.$.controls.showAllEnabled=(sourceHistograms.length!==histograms.length);await progress('Collecting parameters...');const collectParametersMark=tr.b.Timing.mark('histogram-set-view','collectParameters');const parameterCollector=new tr.v.HistogramParameterCollector();parameterCollector.process(histograms);this.$.controls.baseStatisticNames=parameterCollector.statisticNames;this.$.controls.possibleGroupings=parameterCollector.possibleGroupings;const displayLabels=parameterCollector.labels;this.$.controls.displayLabels=displayLabels;collectParametersMark.end();const hist=[...histograms][0];const benchmarks=hist.diagnostics.get(tr.v.d.RESERVED_NAMES.BENCHMARKS);let enable=false;if(benchmarks!==undefined&&benchmarks.length>0){for(const benchmark of benchmarks){if(benchmark.includes('rendering')){enable=true;break;}}}
+this.$.controls.enableVisualization=enable;await this.$.table.build(histograms,sourceHistograms,displayLabels,progress);buildMark.end();},onExport_(event){const mark=tr.b.Timing.mark('histogram-set-view','export'+
+(event.merged?'Merged':'Raw')+event.format.toUpperCase());const histograms=event.merged?this.$.table.leafHistograms:this.histograms;let blob;if(event.format==='csv'){const csv=new tr.v.CSVBuilder(histograms);csv.build();blob=new window.Blob([csv.toString()],{type:'text/csv'});}else if(event.format==='json'){blob=new window.Blob([JSON.stringify(histograms.asDicts())],{type:'text/json'});}else{throw new Error(`Unable to export format "${event.format}"`);}
+const path=window.location.pathname.split('/');const basename=path[path.length-1].split('.')[0]||'histograms';const anchor=document.createElement('a');anchor.download=`${basename}.${event.format}`;anchor.href=window.URL.createObjectURL(blob);anchor.click();mark.end();},onLoadVisualization_(event){if(!this.visualizationLoaded_){this.$.visualizations.style.display='block';this.$.visualizations.build(this.$.table.leafHistograms,this.histograms);this.visualizationLoaded_=true;}else if(this.$.visualizations.style.display==='none'){this.$.visualizations.style.display='block';}else{this.$.visualizations.style.display='none';}},});return{};});'use strict';tr.exportTo('tr.ui',function(){Polymer({is:'tr-ui-sp-metrics-side-panel',behaviors:[tr.ui.behaviors.SidePanel],ready(){this.model_=undefined;this.rangeOfInterest_=undefined;this.metricLatenciesMs_=[];this.metrics_=[];tr.metrics.MetricRegistry.getAllRegisteredTypeInfos().forEach(function(m){if(m.constructor.name==='sampleMetric')return;this.metrics_.push({label:m.constructor.name,value:m.constructor.name});},this);this.metrics_.sort((x,y)=>x.label.localeCompare(y.label));this.settingsKey_='metrics-side-panel-metric-name';this.currentMetricName_='responsivenessMetric';const metricSelector=tr.ui.b.createSelector(this,'currentMetricName_',this.settingsKey_,this.currentMetricName_,this.metrics_);Polymer.dom(this.$.top_left_controls).appendChild(metricSelector);metricSelector.addEventListener('change',this.onMetricChange_.bind(this));this.currentMetricTypeInfo_=tr.metrics.MetricRegistry.findTypeInfoWithName(this.currentMetricName_);this.recomputeButton_=tr.ui.b.createButton('Recompute',this.onRecompute_,this);Polymer.dom(this.$.top_left_controls).appendChild(this.recomputeButton_);this.$.results.addEventListener('display-ready',()=>{this.$.results.style.display='';});},async build(model){this.model_=model;await this.updateContents_();},get metricLatencyMs(){return tr.b.math.Statistics.mean(this.metricLatenciesMs_);},onMetricChange_(){this.currentMetricTypeInfo_=tr.metrics.MetricRegistry.findTypeInfoWithName(this.currentMetricName_);this.metricLatenciesMs_=[];this.updateContents_();},onRecompute_(){this.updateContents_();},get textLabel(){return'Metrics';},supportsModel(m){if(!m){return{supported:false,reason:'No model available'};}
+return{supported:true};},get model(){return this.model_;},set model(model){this.build(model);},get selection(){},set selection(_){},get rangeOfInterest(){return this.rangeOfInterest_;},set rangeOfInterest(range){this.rangeOfInterest_=range;if(this.currentMetricTypeInfo_&&this.currentMetricTypeInfo_.metadata.supportsRangeOfInterest){if((this.metricLatencyMs===undefined)||(this.metricLatencyMs<100)){this.updateContents_();}else{this.recomputeButton_.style.background='red';}}},async updateContents_(){Polymer.dom(this.$.error).textContent='';this.$.results.style.display='none';if(!this.model_){Polymer.dom(this.$.error).textContent='Missing model';return;}
+const options={metrics:[this.currentMetricName_]};if(this.currentMetricTypeInfo_&&this.currentMetricTypeInfo_.metadata.supportsRangeOfInterest&&this.rangeOfInterest&&!this.rangeOfInterest.isEmpty){options.rangeOfInterest=this.rangeOfInterest;}
+const startDate=new Date();const addFailureCb=failure=>{Polymer.dom(this.$.error).textContent=failure.description;};const histograms=tr.metrics.runMetrics(this.model_,options,addFailureCb);this.metricLatenciesMs_.push(new Date()-startDate);while(this.metricLatenciesMs_.length>20){this.metricLatenciesMs_.shift();}
+this.recomputeButton_.style.background='';await this.$.results.build(histograms);}});tr.ui.side_panel.SidePanelRegistry.register(function(){return document.createElement('tr-ui-sp-metrics-side-panel');});return{};});'use strict';Polymer({is:'tr-ui-e-s-alerts-side-panel',behaviors:[tr.ui.behaviors.SidePanel],ready(){this.rangeOfInterest_=new tr.b.math.Range();this.selection_=undefined;},get model(){return this.model_;},set model(model){this.model_=model;this.updateContents_();},set selection(selection){},set rangeOfInterest(rangeOfInterest){},selectAlertsOfType(alertTypeString){const alertsOfType=this.model_.alerts.filter(function(alert){return alert.title===alertTypeString;});const event=new tr.model.RequestSelectionChangeEvent();event.selection=new tr.model.EventSet(alertsOfType);this.dispatchEvent(event);},alertsByType_(alerts){const alertsByType={};alerts.forEach(function(alert){if(!alertsByType[alert.title]){alertsByType[alert.title]=[];}
+alertsByType[alert.title].push(alert);});return alertsByType;},alertsTableRows_(alertsByType){return Object.keys(alertsByType).map(function(key){return{alertType:key,count:alertsByType[key].length};});},alertsTableColumns_(){return[{title:'Alert type',value(row){return row.alertType;},width:'180px'},{title:'Count',width:'100%',value(row){return row.count;}}];},createAlertsTable_(alerts){const alertsByType=this.alertsByType_(alerts);const table=document.createElement('tr-ui-b-table');table.tableColumns=this.alertsTableColumns_();table.tableRows=this.alertsTableRows_(alertsByType);table.selectionMode=tr.ui.b.TableFormat.SelectionMode.ROW;table.addEventListener('selection-changed',function(e){const row=table.selectedTableRow;if(row){this.selectAlertsOfType(row.alertType);}}.bind(this));return table;},updateContents_(){Polymer.dom(this.$.result_area).textContent='';if(this.model_===undefined)return;const panel=this.createAlertsTable_(this.model_.alerts);Polymer.dom(this.$.result_area).appendChild(panel);},supportsModel(m){if(m===undefined){return{supported:false,reason:'Unknown tracing model'};}else if(m.alerts.length===0){return{supported:false,reason:'No alerts in tracing model'};}
+return{supported:true};},get textLabel(){return'Alerts';}});tr.ui.side_panel.SidePanelRegistry.register(function(){return document.createElement('tr-ui-e-s-alerts-side-panel');});
+</script>
+</head>
+  <body>
+  </body>
+</html>
diff --git a/runtime/observatory_2/web/third_party/webcomponents.min.js b/runtime/observatory_2/web/third_party/webcomponents.min.js
new file mode 100644
index 0000000..ad8196b
--- /dev/null
+++ b/runtime/observatory_2/web/third_party/webcomponents.min.js
@@ -0,0 +1,14 @@
+/**
+ * @license
+ * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+ * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
+ * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
+ * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
+ * Code distributed by Google as part of the polymer project is also
+ * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
+ */
+// @version 0.7.24
+!function(){window.WebComponents=window.WebComponents||{flags:{}};var e="webcomponents.js",t=document.querySelector('script[src*="'+e+'"]'),n={};if(!n.noOpts){if(location.search.slice(1).split("&").forEach(function(e){var t,r=e.split("=");r[0]&&(t=r[0].match(/wc-(.+)/))&&(n[t[1]]=r[1]||!0)}),t)for(var r,o=0;r=t.attributes[o];o++)"src"!==r.name&&(n[r.name]=r.value||!0);if(n.log&&n.log.split){var i=n.log.split(",");n.log={},i.forEach(function(e){n.log[e]=!0})}else n.log={}}n.shadow=n.shadow||n.shadowdom||n.polyfill,"native"===n.shadow?n.shadow=!1:n.shadow=n.shadow||!HTMLElement.prototype.createShadowRoot,n.register&&(window.CustomElements=window.CustomElements||{flags:{}},window.CustomElements.flags.register=n.register),WebComponents.flags=n}(),WebComponents.flags.shadow&&("undefined"==typeof WeakMap&&!function(){var e=Object.defineProperty,t=Date.now()%1e9,n=function(){this.name="__st"+(1e9*Math.random()>>>0)+(t++ +"__")};n.prototype={set:function(t,n){var r=t[this.name];return r&&r[0]===t?r[1]=n:e(t,this.name,{value:[t,n],writable:!0}),this},get:function(e){var t;return(t=e[this.name])&&t[0]===e?t[1]:void 0},"delete":function(e){var t=e[this.name];return!(!t||t[0]!==e)&&(t[0]=t[1]=void 0,!0)},has:function(e){var t=e[this.name];return!!t&&t[0]===e}},window.WeakMap=n}(),window.ShadowDOMPolyfill={},function(e){"use strict";function t(){if("undefined"!=typeof chrome&&chrome.app&&chrome.app.runtime)return!1;if(navigator.getDeviceStorage)return!1;try{var e=new Function("return true;");return e()}catch(t){return!1}}function n(e){if(!e)throw new Error("Assertion failed")}function r(e,t){for(var n=W(t),r=0;r<n.length;r++){var o=n[r];A(e,o,F(t,o))}return e}function o(e,t){for(var n=W(t),r=0;r<n.length;r++){var o=n[r];switch(o){case"arguments":case"caller":case"length":case"name":case"prototype":case"toString":continue}A(e,o,F(t,o))}return e}function i(e,t){for(var n=0;n<t.length;n++)if(t[n]in e)return t[n]}function a(e,t,n){U.value=n,A(e,t,U)}function s(e,t){var n=e.__proto__||Object.getPrototypeOf(e);if(q)try{W(n)}catch(r){n=n.__proto__}var o=R.get(n);if(o)return o;var i=s(n),a=E(i);return g(n,a,t),a}function c(e,t){w(e,t,!0)}function l(e,t){w(t,e,!1)}function u(e){return/^on[a-z]+$/.test(e)}function d(e){return/^[a-zA-Z_$][a-zA-Z_$0-9]*$/.test(e)}function p(e){return k&&d(e)?new Function("return this.__impl4cf1e782hg__."+e):function(){return this.__impl4cf1e782hg__[e]}}function h(e){return k&&d(e)?new Function("v","this.__impl4cf1e782hg__."+e+" = v"):function(t){this.__impl4cf1e782hg__[e]=t}}function f(e){return k&&d(e)?new Function("return this.__impl4cf1e782hg__."+e+".apply(this.__impl4cf1e782hg__, arguments)"):function(){return this.__impl4cf1e782hg__[e].apply(this.__impl4cf1e782hg__,arguments)}}function m(e,t){try{return e===window&&"showModalDialog"===t?B:Object.getOwnPropertyDescriptor(e,t)}catch(n){return B}}function w(t,n,r,o){for(var i=W(t),a=0;a<i.length;a++){var s=i[a];if("polymerBlackList_"!==s&&!(s in n||t.polymerBlackList_&&t.polymerBlackList_[s])){q&&t.__lookupGetter__(s);var c,l,d=m(t,s);if("function"!=typeof d.value){var w=u(s);c=w?e.getEventHandlerGetter(s):p(s),(d.writable||d.set||V)&&(l=w?e.getEventHandlerSetter(s):h(s));var v=V||d.configurable;A(n,s,{get:c,set:l,configurable:v,enumerable:d.enumerable})}else r&&(n[s]=f(s))}}}function v(e,t,n){if(null!=e){var r=e.prototype;g(r,t,n),o(t,e)}}function g(e,t,r){var o=t.prototype;n(void 0===R.get(e)),R.set(e,t),I.set(o,e),c(e,o),r&&l(o,r),a(o,"constructor",t),t.prototype=o}function b(e,t){return R.get(t.prototype)===e}function y(e){var t=Object.getPrototypeOf(e),n=s(t),r=E(n);return g(t,r,e),r}function E(e){function t(t){e.call(this,t)}var n=Object.create(e.prototype);return n.constructor=t,t.prototype=n,t}function _(e){return e&&e.__impl4cf1e782hg__}function S(e){return!_(e)}function T(e){if(null===e)return null;n(S(e));var t=e.__wrapper8e3dd93a60__;return null!=t?t:e.__wrapper8e3dd93a60__=new(s(e,e))(e)}function M(e){return null===e?null:(n(_(e)),e.__impl4cf1e782hg__)}function O(e){return e.__impl4cf1e782hg__}function L(e,t){t.__impl4cf1e782hg__=e,e.__wrapper8e3dd93a60__=t}function N(e){return e&&_(e)?M(e):e}function C(e){return e&&!_(e)?T(e):e}function j(e,t){null!==t&&(n(S(e)),n(void 0===t||_(t)),e.__wrapper8e3dd93a60__=t)}function D(e,t,n){G.get=n,A(e.prototype,t,G)}function H(e,t){D(e,t,function(){return T(this.__impl4cf1e782hg__[t])})}function x(e,t){e.forEach(function(e){t.forEach(function(t){e.prototype[t]=function(){var e=C(this);return e[t].apply(e,arguments)}})})}var R=new WeakMap,I=new WeakMap,P=Object.create(null),k=t(),A=Object.defineProperty,W=Object.getOwnPropertyNames,F=Object.getOwnPropertyDescriptor,U={value:void 0,configurable:!0,enumerable:!1,writable:!0};W(window);var q=/Firefox/.test(navigator.userAgent),B={get:function(){},set:function(e){},configurable:!0,enumerable:!0},V=function(){var e=Object.getOwnPropertyDescriptor(Node.prototype,"nodeType");return e&&!e.get&&!e.set}(),G={get:void 0,configurable:!0,enumerable:!0};e.addForwardingProperties=c,e.assert=n,e.constructorTable=R,e.defineGetter=D,e.defineWrapGetter=H,e.forwardMethodsToWrapper=x,e.isIdentifierName=d,e.isWrapper=_,e.isWrapperFor=b,e.mixin=r,e.nativePrototypeTable=I,e.oneOf=i,e.registerObject=y,e.registerWrapper=v,e.rewrap=j,e.setWrapper=L,e.unsafeUnwrap=O,e.unwrap=M,e.unwrapIfNeeded=N,e.wrap=T,e.wrapIfNeeded=C,e.wrappers=P}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e,t,n){return{index:e,removed:t,addedCount:n}}function n(){}var r=0,o=1,i=2,a=3;n.prototype={calcEditDistances:function(e,t,n,r,o,i){for(var a=i-o+1,s=n-t+1,c=new Array(a),l=0;l<a;l++)c[l]=new Array(s),c[l][0]=l;for(var u=0;u<s;u++)c[0][u]=u;for(var l=1;l<a;l++)for(var u=1;u<s;u++)if(this.equals(e[t+u-1],r[o+l-1]))c[l][u]=c[l-1][u-1];else{var d=c[l-1][u]+1,p=c[l][u-1]+1;c[l][u]=d<p?d:p}return c},spliceOperationsFromEditDistances:function(e){for(var t=e.length-1,n=e[0].length-1,s=e[t][n],c=[];t>0||n>0;)if(0!=t)if(0!=n){var l,u=e[t-1][n-1],d=e[t-1][n],p=e[t][n-1];l=d<p?d<u?d:u:p<u?p:u,l==u?(u==s?c.push(r):(c.push(o),s=u),t--,n--):l==d?(c.push(a),t--,s=d):(c.push(i),n--,s=p)}else c.push(a),t--;else c.push(i),n--;return c.reverse(),c},calcSplices:function(e,n,s,c,l,u){var d=0,p=0,h=Math.min(s-n,u-l);if(0==n&&0==l&&(d=this.sharedPrefix(e,c,h)),s==e.length&&u==c.length&&(p=this.sharedSuffix(e,c,h-d)),n+=d,l+=d,s-=p,u-=p,s-n==0&&u-l==0)return[];if(n==s){for(var f=t(n,[],0);l<u;)f.removed.push(c[l++]);return[f]}if(l==u)return[t(n,[],s-n)];for(var m=this.spliceOperationsFromEditDistances(this.calcEditDistances(e,n,s,c,l,u)),f=void 0,w=[],v=n,g=l,b=0;b<m.length;b++)switch(m[b]){case r:f&&(w.push(f),f=void 0),v++,g++;break;case o:f||(f=t(v,[],0)),f.addedCount++,v++,f.removed.push(c[g]),g++;break;case i:f||(f=t(v,[],0)),f.addedCount++,v++;break;case a:f||(f=t(v,[],0)),f.removed.push(c[g]),g++}return f&&w.push(f),w},sharedPrefix:function(e,t,n){for(var r=0;r<n;r++)if(!this.equals(e[r],t[r]))return r;return n},sharedSuffix:function(e,t,n){for(var r=e.length,o=t.length,i=0;i<n&&this.equals(e[--r],t[--o]);)i++;return i},calculateSplices:function(e,t){return this.calcSplices(e,0,e.length,t,0,t.length)},equals:function(e,t){return e===t}},e.ArraySplice=n}(window.ShadowDOMPolyfill),function(e){"use strict";function t(){a=!1;var e=i.slice(0);i=[];for(var t=0;t<e.length;t++)(0,e[t])()}function n(e){i.push(e),a||(a=!0,r(t,0))}var r,o=window.MutationObserver,i=[],a=!1;if(o){var s=1,c=new o(t),l=document.createTextNode(s);c.observe(l,{characterData:!0}),r=function(){s=(s+1)%2,l.data=s}}else r=window.setTimeout;e.setEndOfMicrotask=n}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){e.scheduled_||(e.scheduled_=!0,f.push(e),m||(u(n),m=!0))}function n(){for(m=!1;f.length;){var e=f;f=[],e.sort(function(e,t){return e.uid_-t.uid_});for(var t=0;t<e.length;t++){var n=e[t];n.scheduled_=!1;var r=n.takeRecords();i(n),r.length&&n.callback_(r,n)}}}function r(e,t){this.type=e,this.target=t,this.addedNodes=new p.NodeList,this.removedNodes=new p.NodeList,this.previousSibling=null,this.nextSibling=null,this.attributeName=null,this.attributeNamespace=null,this.oldValue=null}function o(e,t){for(;e;e=e.parentNode){var n=h.get(e);if(n)for(var r=0;r<n.length;r++){var o=n[r];o.options.subtree&&o.addTransientObserver(t)}}}function i(e){for(var t=0;t<e.nodes_.length;t++){var n=e.nodes_[t],r=h.get(n);if(!r)return;for(var o=0;o<r.length;o++){var i=r[o];i.observer===e&&i.removeTransientObservers()}}}function a(e,n,o){for(var i=Object.create(null),a=Object.create(null),s=e;s;s=s.parentNode){var c=h.get(s);if(c)for(var l=0;l<c.length;l++){var u=c[l],d=u.options;if((s===e||d.subtree)&&("attributes"!==n||d.attributes)&&("attributes"!==n||!d.attributeFilter||null===o.namespace&&d.attributeFilter.indexOf(o.name)!==-1)&&("characterData"!==n||d.characterData)&&("childList"!==n||d.childList)){var p=u.observer;i[p.uid_]=p,("attributes"===n&&d.attributeOldValue||"characterData"===n&&d.characterDataOldValue)&&(a[p.uid_]=o.oldValue)}}}for(var f in i){var p=i[f],m=new r(n,e);"name"in o&&"namespace"in o&&(m.attributeName=o.name,m.attributeNamespace=o.namespace),o.addedNodes&&(m.addedNodes=o.addedNodes),o.removedNodes&&(m.removedNodes=o.removedNodes),o.previousSibling&&(m.previousSibling=o.previousSibling),o.nextSibling&&(m.nextSibling=o.nextSibling),void 0!==a[f]&&(m.oldValue=a[f]),t(p),p.records_.push(m)}}function s(e){if(this.childList=!!e.childList,this.subtree=!!e.subtree,"attributes"in e||!("attributeOldValue"in e||"attributeFilter"in e)?this.attributes=!!e.attributes:this.attributes=!0,"characterDataOldValue"in e&&!("characterData"in e)?this.characterData=!0:this.characterData=!!e.characterData,!this.attributes&&(e.attributeOldValue||"attributeFilter"in e)||!this.characterData&&e.characterDataOldValue)throw new TypeError;if(this.characterData=!!e.characterData,this.attributeOldValue=!!e.attributeOldValue,this.characterDataOldValue=!!e.characterDataOldValue,"attributeFilter"in e){if(null==e.attributeFilter||"object"!=typeof e.attributeFilter)throw new TypeError;this.attributeFilter=w.call(e.attributeFilter)}else this.attributeFilter=null}function c(e){this.callback_=e,this.nodes_=[],this.records_=[],this.uid_=++v,this.scheduled_=!1}function l(e,t,n){this.observer=e,this.target=t,this.options=n,this.transientObservedNodes=[]}var u=e.setEndOfMicrotask,d=e.wrapIfNeeded,p=e.wrappers,h=new WeakMap,f=[],m=!1,w=Array.prototype.slice,v=0;c.prototype={constructor:c,observe:function(e,t){e=d(e);var n,r=new s(t),o=h.get(e);o||h.set(e,o=[]);for(var i=0;i<o.length;i++)o[i].observer===this&&(n=o[i],n.removeTransientObservers(),n.options=r);n||(n=new l(this,e,r),o.push(n),this.nodes_.push(e))},disconnect:function(){this.nodes_.forEach(function(e){for(var t=h.get(e),n=0;n<t.length;n++){var r=t[n];if(r.observer===this){t.splice(n,1);break}}},this),this.records_=[]},takeRecords:function(){var e=this.records_;return this.records_=[],e}},l.prototype={addTransientObserver:function(e){if(e!==this.target){t(this.observer),this.transientObservedNodes.push(e);var n=h.get(e);n||h.set(e,n=[]),n.push(this)}},removeTransientObservers:function(){var e=this.transientObservedNodes;this.transientObservedNodes=[];for(var t=0;t<e.length;t++)for(var n=e[t],r=h.get(n),o=0;o<r.length;o++)if(r[o]===this){r.splice(o,1);break}}},e.enqueueMutation=a,e.registerTransientObservers=o,e.wrappers.MutationObserver=c,e.wrappers.MutationRecord=r}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e,t){this.root=e,this.parent=t}function n(e,t){if(e.treeScope_!==t){e.treeScope_=t;for(var r=e.shadowRoot;r;r=r.olderShadowRoot)r.treeScope_.parent=t;for(var o=e.firstChild;o;o=o.nextSibling)n(o,t)}}function r(n){if(n instanceof e.wrappers.Window,n.treeScope_)return n.treeScope_;var o,i=n.parentNode;return o=i?r(i):new t(n,null),n.treeScope_=o}t.prototype={get renderer(){return this.root instanceof e.wrappers.ShadowRoot?e.getRendererForHost(this.root.host):null},contains:function(e){for(;e;e=e.parent)if(e===this)return!0;return!1}},e.TreeScope=t,e.getTreeScope=r,e.setTreeScope=n}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){return e instanceof G.ShadowRoot}function n(e){return A(e).root}function r(e,r){var s=[],c=e;for(s.push(c);c;){var l=a(c);if(l&&l.length>0){for(var u=0;u<l.length;u++){var p=l[u];if(i(p)){var h=n(p),f=h.olderShadowRoot;f&&s.push(f)}s.push(p)}c=l[l.length-1]}else if(t(c)){if(d(e,c)&&o(r))break;c=c.host,s.push(c)}else c=c.parentNode,c&&s.push(c)}return s}function o(e){if(!e)return!1;switch(e.type){case"abort":case"error":case"select":case"change":case"load":case"reset":case"resize":case"scroll":case"selectstart":return!0}return!1}function i(e){return e instanceof HTMLShadowElement}function a(t){return e.getDestinationInsertionPoints(t)}function s(e,t){if(0===e.length)return t;t instanceof G.Window&&(t=t.document);for(var n=A(t),r=e[0],o=A(r),i=l(n,o),a=0;a<e.length;a++){var s=e[a];if(A(s)===i)return s}return e[e.length-1]}function c(e){for(var t=[];e;e=e.parent)t.push(e);return t}function l(e,t){for(var n=c(e),r=c(t),o=null;n.length>0&&r.length>0;){var i=n.pop(),a=r.pop();if(i!==a)break;o=i}return o}function u(e,t,n){t instanceof G.Window&&(t=t.document);var o,i=A(t),a=A(n),s=r(n,e),o=l(i,a);o||(o=a.root);for(var c=o;c;c=c.parent)for(var u=0;u<s.length;u++){var d=s[u];if(A(d)===c)return d}return null}function d(e,t){return A(e)===A(t)}function p(e){if(!K.get(e)&&(K.set(e,!0),f(V(e),V(e.target)),P)){var t=P;throw P=null,t}}function h(e){switch(e.type){case"load":case"beforeunload":case"unload":return!0}return!1}function f(t,n){if($.get(t))throw new Error("InvalidStateError");$.set(t,!0),e.renderAllPending();var o,i,a;if(h(t)&&!t.bubbles){var s=n;s instanceof G.Document&&(a=s.defaultView)&&(i=s,o=[])}if(!o)if(n instanceof G.Window)a=n,o=[];else if(o=r(n,t),!h(t)){var s=o[o.length-1];s instanceof G.Document&&(a=s.defaultView)}return ne.set(t,o),m(t,o,a,i)&&w(t,o,a,i)&&v(t,o,a,i),J.set(t,re),Y["delete"](t,null),$["delete"](t),t.defaultPrevented}function m(e,t,n,r){var o=oe;if(n&&!g(n,e,o,t,r))return!1;for(var i=t.length-1;i>0;i--)if(!g(t[i],e,o,t,r))return!1;return!0}function w(e,t,n,r){var o=ie,i=t[0]||n;return g(i,e,o,t,r)}function v(e,t,n,r){for(var o=ae,i=1;i<t.length;i++)if(!g(t[i],e,o,t,r))return;n&&t.length>0&&g(n,e,o,t,r)}function g(e,t,n,r,o){var i=z.get(e);if(!i)return!0;var a=o||s(r,e);if(a===e){if(n===oe)return!0;n===ae&&(n=ie)}else if(n===ae&&!t.bubbles)return!0;if("relatedTarget"in t){var c=B(t),l=c.relatedTarget;if(l){if(l instanceof Object&&l.addEventListener){var d=V(l),p=u(t,e,d);if(p===a)return!0}else p=null;Z.set(t,p)}}J.set(t,n);var h=t.type,f=!1;X.set(t,a),Y.set(t,e),i.depth++;for(var m=0,w=i.length;m<w;m++){var v=i[m];if(v.removed)f=!0;else if(!(v.type!==h||!v.capture&&n===oe||v.capture&&n===ae))try{if("function"==typeof v.handler?v.handler.call(e,t):v.handler.handleEvent(t),ee.get(t))return!1}catch(g){P||(P=g)}}if(i.depth--,f&&0===i.depth){var b=i.slice();i.length=0;for(var m=0;m<b.length;m++)b[m].removed||i.push(b[m])}return!Q.get(t)}function b(e,t,n){this.type=e,this.handler=t,this.capture=Boolean(n)}function y(e,t){if(!(e instanceof se))return V(T(se,"Event",e,t));var n=e;return be||"beforeunload"!==n.type||this instanceof M?void U(n,this):new M(n)}function E(e){return e&&e.relatedTarget?Object.create(e,{relatedTarget:{value:B(e.relatedTarget)}}):e}function _(e,t,n){var r=window[e],o=function(t,n){return t instanceof r?void U(t,this):V(T(r,e,t,n))};if(o.prototype=Object.create(t.prototype),n&&W(o.prototype,n),r)try{F(r,o,new r("temp"))}catch(i){F(r,o,document.createEvent(e))}return o}function S(e,t){return function(){arguments[t]=B(arguments[t]);var n=B(this);n[e].apply(n,arguments)}}function T(e,t,n,r){if(ve)return new e(n,E(r));var o=B(document.createEvent(t)),i=we[t],a=[n];return Object.keys(i).forEach(function(e){var t=null!=r&&e in r?r[e]:i[e];"relatedTarget"===e&&(t=B(t)),a.push(t)}),o["init"+t].apply(o,a),o}function M(e){y.call(this,e)}function O(e){return"function"==typeof e||e&&e.handleEvent}function L(e){switch(e){case"DOMAttrModified":case"DOMAttributeNameChanged":case"DOMCharacterDataModified":case"DOMElementNameChanged":case"DOMNodeInserted":case"DOMNodeInsertedIntoDocument":case"DOMNodeRemoved":case"DOMNodeRemovedFromDocument":case"DOMSubtreeModified":return!0}return!1}function N(e){U(e,this)}function C(e){return e instanceof G.ShadowRoot&&(e=e.host),B(e)}function j(e,t){var n=z.get(e);if(n)for(var r=0;r<n.length;r++)if(!n[r].removed&&n[r].type===t)return!0;return!1}function D(e,t){for(var n=B(e);n;n=n.parentNode)if(j(V(n),t))return!0;return!1}function H(e){k(e,Ee)}function x(t,n,o,i){e.renderAllPending();var a=V(_e.call(q(n),o,i));if(!a)return null;var c=r(a,null),l=c.lastIndexOf(t);return l==-1?null:(c=c.slice(0,l),s(c,t))}function R(e){return function(){var t=te.get(this);return t&&t[e]&&t[e].value||null}}function I(e){var t=e.slice(2);return function(n){var r=te.get(this);r||(r=Object.create(null),te.set(this,r));var o=r[e];if(o&&this.removeEventListener(t,o.wrapped,!1),"function"==typeof n){var i=function(t){var r=n.call(this,t);r===!1?t.preventDefault():"onbeforeunload"===e&&"string"==typeof r&&(t.returnValue=r)};this.addEventListener(t,i,!1),r[e]={value:n,wrapped:i}}}}var P,k=e.forwardMethodsToWrapper,A=e.getTreeScope,W=e.mixin,F=e.registerWrapper,U=e.setWrapper,q=e.unsafeUnwrap,B=e.unwrap,V=e.wrap,G=e.wrappers,z=(new WeakMap,new WeakMap),K=new WeakMap,$=new WeakMap,X=new WeakMap,Y=new WeakMap,Z=new WeakMap,J=new WeakMap,Q=new WeakMap,ee=new WeakMap,te=new WeakMap,ne=new WeakMap,re=0,oe=1,ie=2,ae=3;b.prototype={equals:function(e){return this.handler===e.handler&&this.type===e.type&&this.capture===e.capture},get removed(){return null===this.handler},remove:function(){this.handler=null}};var se=window.Event;se.prototype.polymerBlackList_={returnValue:!0,keyLocation:!0},y.prototype={get target(){return X.get(this)},get currentTarget(){return Y.get(this)},get eventPhase(){return J.get(this)},get path(){var e=ne.get(this);return e?e.slice():[]},stopPropagation:function(){Q.set(this,!0)},stopImmediatePropagation:function(){Q.set(this,!0),ee.set(this,!0)}};var ce=function(){var e=document.createEvent("Event");return e.initEvent("test",!0,!0),e.preventDefault(),e.defaultPrevented}();ce||(y.prototype.preventDefault=function(){this.cancelable&&(q(this).preventDefault(),Object.defineProperty(this,"defaultPrevented",{get:function(){return!0},configurable:!0}))}),F(se,y,document.createEvent("Event"));var le=_("UIEvent",y),ue=_("CustomEvent",y),de={get relatedTarget(){var e=Z.get(this);return void 0!==e?e:V(B(this).relatedTarget)}},pe=W({initMouseEvent:S("initMouseEvent",14)},de),he=W({initFocusEvent:S("initFocusEvent",5)},de),fe=_("MouseEvent",le,pe),me=_("FocusEvent",le,he),we=Object.create(null),ve=function(){try{new window.FocusEvent("focus")}catch(e){return!1}return!0}();if(!ve){var ge=function(e,t,n){if(n){var r=we[n];t=W(W({},r),t)}we[e]=t};ge("Event",{bubbles:!1,cancelable:!1}),ge("CustomEvent",{detail:null},"Event"),ge("UIEvent",{view:null,detail:0},"Event"),ge("MouseEvent",{screenX:0,screenY:0,clientX:0,clientY:0,ctrlKey:!1,altKey:!1,shiftKey:!1,metaKey:!1,button:0,relatedTarget:null},"UIEvent"),ge("FocusEvent",{relatedTarget:null},"UIEvent")}var be=window.BeforeUnloadEvent;M.prototype=Object.create(y.prototype),W(M.prototype,{get returnValue(){return q(this).returnValue},set returnValue(e){q(this).returnValue=e}}),be&&F(be,M);var ye=window.EventTarget,Ee=["addEventListener","removeEventListener","dispatchEvent"];[Node,Window].forEach(function(e){var t=e.prototype;Ee.forEach(function(e){Object.defineProperty(t,e+"_",{value:t[e]})})}),N.prototype={addEventListener:function(e,t,n){if(O(t)&&!L(e)){var r=new b(e,t,n),o=z.get(this);if(o){for(var i=0;i<o.length;i++)if(r.equals(o[i]))return}else o=[],o.depth=0,z.set(this,o);o.push(r);var a=C(this);a.addEventListener_(e,p,!0)}},removeEventListener:function(e,t,n){n=Boolean(n);var r=z.get(this);if(r){for(var o=0,i=!1,a=0;a<r.length;a++)r[a].type===e&&r[a].capture===n&&(o++,r[a].handler===t&&(i=!0,r[a].remove()));if(i&&1===o){var s=C(this);s.removeEventListener_(e,p,!0)}}},dispatchEvent:function(t){var n=B(t),r=n.type;K.set(n,!1),e.renderAllPending();var o;D(this,r)||(o=function(){},this.addEventListener(r,o,!0));try{return B(this).dispatchEvent_(n)}finally{o&&this.removeEventListener(r,o,!0)}}},ye&&F(ye,N);var _e=document.elementFromPoint;e.elementFromPoint=x,e.getEventHandlerGetter=R,e.getEventHandlerSetter=I,e.wrapEventTargetMethods=H,e.wrappers.BeforeUnloadEvent=M,e.wrappers.CustomEvent=ue,e.wrappers.Event=y,e.wrappers.EventTarget=N,e.wrappers.FocusEvent=me,e.wrappers.MouseEvent=fe,e.wrappers.UIEvent=le}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e,t){Object.defineProperty(e,t,m)}function n(e){l(e,this)}function r(){this.length=0,t(this,"length")}function o(e){for(var t=new r,o=0;o<e.length;o++)t[o]=new n(e[o]);return t.length=o,t}function i(e){a.call(this,e)}var a=e.wrappers.UIEvent,s=e.mixin,c=e.registerWrapper,l=e.setWrapper,u=e.unsafeUnwrap,d=e.wrap,p=window.TouchEvent;if(p){var h;try{h=document.createEvent("TouchEvent")}catch(f){return}var m={enumerable:!1};n.prototype={get target(){return d(u(this).target)}};var w={configurable:!0,enumerable:!0,get:null};["clientX","clientY","screenX","screenY","pageX","pageY","identifier","webkitRadiusX","webkitRadiusY","webkitRotationAngle","webkitForce"].forEach(function(e){w.get=function(){return u(this)[e]},Object.defineProperty(n.prototype,e,w)}),r.prototype={item:function(e){return this[e]}},i.prototype=Object.create(a.prototype),s(i.prototype,{get touches(){return o(u(this).touches)},get targetTouches(){return o(u(this).targetTouches)},get changedTouches(){return o(u(this).changedTouches)},initTouchEvent:function(){throw new Error("Not implemented")}}),c(p,i,h),e.wrappers.Touch=n,e.wrappers.TouchEvent=i,e.wrappers.TouchList=r}}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e,t){Object.defineProperty(e,t,s)}function n(){this.length=0,t(this,"length")}function r(e){if(null==e)return e;for(var t=new n,r=0,o=e.length;r<o;r++)t[r]=a(e[r]);return t.length=o,t}function o(e,t){e.prototype[t]=function(){return r(i(this)[t].apply(i(this),arguments))}}var i=e.unsafeUnwrap,a=e.wrap,s={enumerable:!1};n.prototype={item:function(e){return this[e]}},t(n.prototype,"item"),e.wrappers.NodeList=n,e.addWrapNodeListMethod=o,e.wrapNodeList=r}(window.ShadowDOMPolyfill),function(e){"use strict";e.wrapHTMLCollection=e.wrapNodeList,e.wrappers.HTMLCollection=e.wrappers.NodeList}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){O(e instanceof _)}function n(e){var t=new T;return t[0]=e,t.length=1,t}function r(e,t,n){N(t,"childList",{removedNodes:n,previousSibling:e.previousSibling,nextSibling:e.nextSibling})}function o(e,t){N(e,"childList",{removedNodes:t})}function i(e,t,r,o){if(e instanceof DocumentFragment){var i=s(e);U=!0;for(var a=i.length-1;a>=0;a--)e.removeChild(i[a]),i[a].parentNode_=t;U=!1;for(var a=0;a<i.length;a++)i[a].previousSibling_=i[a-1]||r,i[a].nextSibling_=i[a+1]||o;return r&&(r.nextSibling_=i[0]),o&&(o.previousSibling_=i[i.length-1]),i}var i=n(e),c=e.parentNode;return c&&c.removeChild(e),e.parentNode_=t,e.previousSibling_=r,e.nextSibling_=o,r&&(r.nextSibling_=e),o&&(o.previousSibling_=e),i}function a(e){if(e instanceof DocumentFragment)return s(e);var t=n(e),o=e.parentNode;return o&&r(e,o,t),t}function s(e){for(var t=new T,n=0,r=e.firstChild;r;r=r.nextSibling)t[n++]=r;return t.length=n,o(e,t),t}function c(e){return e}function l(e,t){R(e,t),e.nodeIsInserted_()}function u(e,t){for(var n=C(t),r=0;r<e.length;r++)l(e[r],n)}function d(e){R(e,new M(e,null))}function p(e){for(var t=0;t<e.length;t++)d(e[t])}function h(e,t){var n=e.nodeType===_.DOCUMENT_NODE?e:e.ownerDocument;n!==t.ownerDocument&&n.adoptNode(t)}function f(t,n){if(n.length){var r=t.ownerDocument;if(r!==n[0].ownerDocument)for(var o=0;o<n.length;o++)e.adoptNodeNoRemove(n[o],r)}}function m(e,t){f(e,t);var n=t.length;if(1===n)return P(t[0]);for(var r=P(e.ownerDocument.createDocumentFragment()),o=0;o<n;o++)r.appendChild(P(t[o]));return r}function w(e){if(void 0!==e.firstChild_)for(var t=e.firstChild_;t;){var n=t;t=t.nextSibling_,n.parentNode_=n.previousSibling_=n.nextSibling_=void 0}e.firstChild_=e.lastChild_=void 0}function v(e){if(e.invalidateShadowRenderer()){for(var t=e.firstChild;t;){O(t.parentNode===e);var n=t.nextSibling,r=P(t),o=r.parentNode;o&&X.call(o,r),t.previousSibling_=t.nextSibling_=t.parentNode_=null,t=n}e.firstChild_=e.lastChild_=null}else for(var n,i=P(e),a=i.firstChild;a;)n=a.nextSibling,X.call(i,a),a=n}function g(e){var t=e.parentNode;return t&&t.invalidateShadowRenderer()}function b(e){for(var t,n=0;n<e.length;n++)t=e[n],t.parentNode.removeChild(t)}function y(e,t,n){var r;if(r=A(n?q.call(n,I(e),!1):B.call(I(e),!1)),t){for(var o=e.firstChild;o;o=o.nextSibling)r.appendChild(y(o,!0,n));if(e instanceof F.HTMLTemplateElement)for(var i=r.content,o=e.content.firstChild;o;o=o.nextSibling)i.appendChild(y(o,!0,n))}return r}function E(e,t){if(!t||C(e)!==C(t))return!1;for(var n=t;n;n=n.parentNode)if(n===e)return!0;return!1}function _(e){O(e instanceof V),S.call(this,e),this.parentNode_=void 0,this.firstChild_=void 0,this.lastChild_=void 0,this.nextSibling_=void 0,this.previousSibling_=void 0,this.treeScope_=void 0}var S=e.wrappers.EventTarget,T=e.wrappers.NodeList,M=e.TreeScope,O=e.assert,L=e.defineWrapGetter,N=e.enqueueMutation,C=e.getTreeScope,j=e.isWrapper,D=e.mixin,H=e.registerTransientObservers,x=e.registerWrapper,R=e.setTreeScope,I=e.unsafeUnwrap,P=e.unwrap,k=e.unwrapIfNeeded,A=e.wrap,W=e.wrapIfNeeded,F=e.wrappers,U=!1,q=document.importNode,B=window.Node.prototype.cloneNode,V=window.Node,G=window.DocumentFragment,z=(V.prototype.appendChild,V.prototype.compareDocumentPosition),K=V.prototype.isEqualNode,$=V.prototype.insertBefore,X=V.prototype.removeChild,Y=V.prototype.replaceChild,Z=/Trident|Edge/.test(navigator.userAgent),J=Z?function(e,t){try{X.call(e,t)}catch(n){if(!(e instanceof G))throw n}}:function(e,t){X.call(e,t)};_.prototype=Object.create(S.prototype),D(_.prototype,{appendChild:function(e){return this.insertBefore(e,null)},insertBefore:function(e,n){t(e);var r;n?j(n)?r=P(n):(r=n,n=A(r)):(n=null,r=null),n&&O(n.parentNode===this);var o,s=n?n.previousSibling:this.lastChild,c=!this.invalidateShadowRenderer()&&!g(e);if(o=c?a(e):i(e,this,s,n),c)h(this,e),w(this),$.call(I(this),P(e),r);else{s||(this.firstChild_=o[0]),n||(this.lastChild_=o[o.length-1],void 0===this.firstChild_&&(this.firstChild_=this.firstChild));var l=r?r.parentNode:I(this);l?$.call(l,m(this,o),r):f(this,o)}return N(this,"childList",{addedNodes:o,nextSibling:n,previousSibling:s}),u(o,this),e},removeChild:function(e){if(t(e),e.parentNode!==this){for(var r=!1,o=(this.childNodes,this.firstChild);o;o=o.nextSibling)if(o===e){r=!0;break}if(!r)throw new Error("NotFoundError")}var i=P(e),a=e.nextSibling,s=e.previousSibling;if(this.invalidateShadowRenderer()){var c=this.firstChild,l=this.lastChild,u=i.parentNode;u&&J(u,i),c===e&&(this.firstChild_=a),l===e&&(this.lastChild_=s),s&&(s.nextSibling_=a),a&&(a.previousSibling_=s),e.previousSibling_=e.nextSibling_=e.parentNode_=void 0}else w(this),J(I(this),i);return U||N(this,"childList",{removedNodes:n(e),nextSibling:a,previousSibling:s}),H(this,e),e},replaceChild:function(e,r){t(e);var o;if(j(r)?o=P(r):(o=r,r=A(o)),r.parentNode!==this)throw new Error("NotFoundError");var s,c=r.nextSibling,l=r.previousSibling,p=!this.invalidateShadowRenderer()&&!g(e);return p?s=a(e):(c===e&&(c=e.nextSibling),s=i(e,this,l,c)),p?(h(this,e),w(this),Y.call(I(this),P(e),o)):(this.firstChild===r&&(this.firstChild_=s[0]),this.lastChild===r&&(this.lastChild_=s[s.length-1]),r.previousSibling_=r.nextSibling_=r.parentNode_=void 0,o.parentNode&&Y.call(o.parentNode,m(this,s),o)),N(this,"childList",{addedNodes:s,removedNodes:n(r),nextSibling:c,previousSibling:l}),d(r),u(s,this),r},nodeIsInserted_:function(){for(var e=this.firstChild;e;e=e.nextSibling)e.nodeIsInserted_()},hasChildNodes:function(){return null!==this.firstChild},get parentNode(){return void 0!==this.parentNode_?this.parentNode_:A(I(this).parentNode)},get firstChild(){return void 0!==this.firstChild_?this.firstChild_:A(I(this).firstChild)},get lastChild(){return void 0!==this.lastChild_?this.lastChild_:A(I(this).lastChild)},get nextSibling(){return void 0!==this.nextSibling_?this.nextSibling_:A(I(this).nextSibling)},get previousSibling(){return void 0!==this.previousSibling_?this.previousSibling_:A(I(this).previousSibling)},get parentElement(){for(var e=this.parentNode;e&&e.nodeType!==_.ELEMENT_NODE;)e=e.parentNode;return e},get textContent(){for(var e="",t=this.firstChild;t;t=t.nextSibling)t.nodeType!=_.COMMENT_NODE&&(e+=t.textContent);return e},set textContent(e){null==e&&(e="");var t=c(this.childNodes);if(this.invalidateShadowRenderer()){if(v(this),""!==e){var n=I(this).ownerDocument.createTextNode(e);this.appendChild(n)}}else w(this),I(this).textContent=e;var r=c(this.childNodes);N(this,"childList",{addedNodes:r,removedNodes:t}),p(t),u(r,this)},get childNodes(){for(var e=new T,t=0,n=this.firstChild;n;n=n.nextSibling)e[t++]=n;return e.length=t,e},cloneNode:function(e){return y(this,e)},contains:function(e){return E(this,W(e))},compareDocumentPosition:function(e){return z.call(I(this),k(e))},isEqualNode:function(e){return K.call(I(this),k(e))},normalize:function(){for(var e,t,n=c(this.childNodes),r=[],o="",i=0;i<n.length;i++)t=n[i],t.nodeType===_.TEXT_NODE?e||t.data.length?e?(o+=t.data,r.push(t)):e=t:this.removeChild(t):(e&&r.length&&(e.data+=o,b(r)),r=[],o="",e=null,t.childNodes.length&&t.normalize());e&&r.length&&(e.data+=o,b(r))}}),L(_,"ownerDocument"),x(V,_,document.createDocumentFragment()),delete _.prototype.querySelector,delete _.prototype.querySelectorAll,_.prototype=D(Object.create(S.prototype),_.prototype),e.cloneNode=y,e.nodeWasAdded=l,e.nodeWasRemoved=d,e.nodesWereAdded=u,e.nodesWereRemoved=p,e.originalInsertBefore=$,e.originalRemoveChild=X,e.snapshotNodeList=c,e.wrappers.Node=_}(window.ShadowDOMPolyfill),function(e){"use strict";function t(t,n,r,o){for(var i=null,a=null,s=0,c=t.length;s<c;s++)i=b(t[s]),!o&&(a=v(i).root)&&a instanceof e.wrappers.ShadowRoot||(r[n++]=i);return n}function n(e){return String(e).replace(/\/deep\/|::shadow|>>>/g," ")}function r(e){return String(e).replace(/:host\(([^\s]+)\)/g,"$1").replace(/([^\s]):host/g,"$1").replace(":host","*").replace(/\^|\/shadow\/|\/shadow-deep\/|::shadow|\/deep\/|::content|>>>/g," ")}function o(e,t){for(var n,r=e.firstElementChild;r;){if(r.matches(t))return r;if(n=o(r,t))return n;r=r.nextElementSibling}return null}function i(e,t){return e.matches(t)}function a(e,t,n){var r=e.localName;return r===t||r===n&&e.namespaceURI===j}function s(){return!0}function c(e,t,n){return e.localName===n}function l(e,t){return e.namespaceURI===t}function u(e,t,n){return e.namespaceURI===t&&e.localName===n}function d(e,t,n,r,o,i){for(var a=e.firstElementChild;a;)r(a,o,i)&&(n[t++]=a),t=d(a,t,n,r,o,i),a=a.nextElementSibling;return t}function p(n,r,o,i,a){var s,c=g(this),l=v(this).root;if(l instanceof e.wrappers.ShadowRoot)return d(this,r,o,n,i,null);if(c instanceof N)s=S.call(c,i);else{if(!(c instanceof C))return d(this,r,o,n,i,null);s=_.call(c,i)}return t(s,r,o,a)}function h(n,r,o,i,a){var s,c=g(this),l=v(this).root;if(l instanceof e.wrappers.ShadowRoot)return d(this,r,o,n,i,a);if(c instanceof N)s=M.call(c,i,a);else{if(!(c instanceof C))return d(this,r,o,n,i,a);s=T.call(c,i,a)}return t(s,r,o,!1)}function f(n,r,o,i,a){var s,c=g(this),l=v(this).root;if(l instanceof e.wrappers.ShadowRoot)return d(this,r,o,n,i,a);if(c instanceof N)s=L.call(c,i,a);else{if(!(c instanceof C))return d(this,r,o,n,i,a);s=O.call(c,i,a)}return t(s,r,o,!1)}var m=e.wrappers.HTMLCollection,w=e.wrappers.NodeList,v=e.getTreeScope,g=e.unsafeUnwrap,b=e.wrap,y=document.querySelector,E=document.documentElement.querySelector,_=document.querySelectorAll,S=document.documentElement.querySelectorAll,T=document.getElementsByTagName,M=document.documentElement.getElementsByTagName,O=document.getElementsByTagNameNS,L=document.documentElement.getElementsByTagNameNS,N=window.Element,C=window.HTMLDocument||window.Document,j="http://www.w3.org/1999/xhtml",D={
+querySelector:function(t){var r=n(t),i=r!==t;t=r;var a,s=g(this),c=v(this).root;if(c instanceof e.wrappers.ShadowRoot)return o(this,t);if(s instanceof N)a=b(E.call(s,t));else{if(!(s instanceof C))return o(this,t);a=b(y.call(s,t))}return a&&!i&&(c=v(a).root)&&c instanceof e.wrappers.ShadowRoot?o(this,t):a},querySelectorAll:function(e){var t=n(e),r=t!==e;e=t;var o=new w;return o.length=p.call(this,i,0,o,e,r),o}},H={matches:function(t){return t=r(t),e.originalMatches.call(g(this),t)}},x={getElementsByTagName:function(e){var t=new m,n="*"===e?s:a;return t.length=h.call(this,n,0,t,e,e.toLowerCase()),t},getElementsByClassName:function(e){return this.querySelectorAll("."+e)},getElementsByTagNameNS:function(e,t){var n=new m,r=null;return r="*"===e?"*"===t?s:c:"*"===t?l:u,n.length=f.call(this,r,0,n,e||null,t),n}};e.GetElementsByInterface=x,e.SelectorsInterface=D,e.MatchesInterface=H}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){for(;e&&e.nodeType!==Node.ELEMENT_NODE;)e=e.nextSibling;return e}function n(e){for(;e&&e.nodeType!==Node.ELEMENT_NODE;)e=e.previousSibling;return e}var r=e.wrappers.NodeList,o={get firstElementChild(){return t(this.firstChild)},get lastElementChild(){return n(this.lastChild)},get childElementCount(){for(var e=0,t=this.firstElementChild;t;t=t.nextElementSibling)e++;return e},get children(){for(var e=new r,t=0,n=this.firstElementChild;n;n=n.nextElementSibling)e[t++]=n;return e.length=t,e},remove:function(){var e=this.parentNode;e&&e.removeChild(this)}},i={get nextElementSibling(){return t(this.nextSibling)},get previousElementSibling(){return n(this.previousSibling)}},a={getElementById:function(e){return/[ \t\n\r\f]/.test(e)?null:this.querySelector('[id="'+e+'"]')}};e.ChildNodeInterface=i,e.NonElementParentNodeInterface=a,e.ParentNodeInterface=o}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){r.call(this,e)}var n=e.ChildNodeInterface,r=e.wrappers.Node,o=e.enqueueMutation,i=e.mixin,a=e.registerWrapper,s=e.unsafeUnwrap,c=window.CharacterData;t.prototype=Object.create(r.prototype),i(t.prototype,{get nodeValue(){return this.data},set nodeValue(e){this.data=e},get textContent(){return this.data},set textContent(e){this.data=e},get data(){return s(this).data},set data(e){var t=s(this).data;o(this,"characterData",{oldValue:t}),s(this).data=e}}),i(t.prototype,n),a(c,t,document.createTextNode("")),e.wrappers.CharacterData=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){return e>>>0}function n(e){r.call(this,e)}var r=e.wrappers.CharacterData,o=(e.enqueueMutation,e.mixin),i=e.registerWrapper,a=window.Text;n.prototype=Object.create(r.prototype),o(n.prototype,{splitText:function(e){e=t(e);var n=this.data;if(e>n.length)throw new Error("IndexSizeError");var r=n.slice(0,e),o=n.slice(e);this.data=r;var i=this.ownerDocument.createTextNode(o);return this.parentNode&&this.parentNode.insertBefore(i,this.nextSibling),i}}),i(a,n,document.createTextNode("")),e.wrappers.Text=n}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){return i(e).getAttribute("class")}function n(e,t){a(e,"attributes",{name:"class",namespace:null,oldValue:t})}function r(t){e.invalidateRendererBasedOnAttribute(t,"class")}function o(e,o,i){var a=e.ownerElement_;if(null==a)return o.apply(e,i);var s=t(a),c=o.apply(e,i);return t(a)!==s&&(n(a,s),r(a)),c}if(!window.DOMTokenList)return void console.warn("Missing DOMTokenList prototype, please include a compatible classList polyfill such as http://goo.gl/uTcepH.");var i=e.unsafeUnwrap,a=e.enqueueMutation,s=DOMTokenList.prototype.add;DOMTokenList.prototype.add=function(){o(this,s,arguments)};var c=DOMTokenList.prototype.remove;DOMTokenList.prototype.remove=function(){o(this,c,arguments)};var l=DOMTokenList.prototype.toggle;DOMTokenList.prototype.toggle=function(){return o(this,l,arguments)}}(window.ShadowDOMPolyfill),function(e){"use strict";function t(t,n){var r=t.parentNode;if(r&&r.shadowRoot){var o=e.getRendererForHost(r);o.dependsOnAttribute(n)&&o.invalidate()}}function n(e,t,n){u(e,"attributes",{name:t,namespace:null,oldValue:n})}function r(e){a.call(this,e)}var o=e.ChildNodeInterface,i=e.GetElementsByInterface,a=e.wrappers.Node,s=e.ParentNodeInterface,c=e.SelectorsInterface,l=e.MatchesInterface,u=(e.addWrapNodeListMethod,e.enqueueMutation),d=e.mixin,p=(e.oneOf,e.registerWrapper),h=e.unsafeUnwrap,f=e.wrappers,m=window.Element,w=["matches","mozMatchesSelector","msMatchesSelector","webkitMatchesSelector"].filter(function(e){return m.prototype[e]}),v=w[0],g=m.prototype[v],b=new WeakMap;r.prototype=Object.create(a.prototype),d(r.prototype,{createShadowRoot:function(){var t=new f.ShadowRoot(this);h(this).polymerShadowRoot_=t;var n=e.getRendererForHost(this);return n.invalidate(),t},get shadowRoot(){return h(this).polymerShadowRoot_||null},setAttribute:function(e,r){var o=h(this).getAttribute(e);h(this).setAttribute(e,r),n(this,e,o),t(this,e)},removeAttribute:function(e){var r=h(this).getAttribute(e);h(this).removeAttribute(e),n(this,e,r),t(this,e)},get classList(){var e=b.get(this);if(!e){if(e=h(this).classList,!e)return;e.ownerElement_=this,b.set(this,e)}return e},get className(){return h(this).className},set className(e){this.setAttribute("class",e)},get id(){return h(this).id},set id(e){this.setAttribute("id",e)}}),w.forEach(function(e){"matches"!==e&&(r.prototype[e]=function(e){return this.matches(e)})}),m.prototype.webkitCreateShadowRoot&&(r.prototype.webkitCreateShadowRoot=r.prototype.createShadowRoot),d(r.prototype,o),d(r.prototype,i),d(r.prototype,s),d(r.prototype,c),d(r.prototype,l),p(m,r,document.createElementNS(null,"x")),e.invalidateRendererBasedOnAttribute=t,e.matchesNames=w,e.originalMatches=g,e.wrappers.Element=r}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){switch(e){case"&":return"&amp;";case"<":return"&lt;";case">":return"&gt;";case'"':return"&quot;";case" ":return"&nbsp;"}}function n(e){return e.replace(L,t)}function r(e){return e.replace(N,t)}function o(e){for(var t={},n=0;n<e.length;n++)t[e[n]]=!0;return t}function i(e){if(e.namespaceURI!==D)return!0;var t=e.ownerDocument.doctype;return t&&t.publicId&&t.systemId}function a(e,t){switch(e.nodeType){case Node.ELEMENT_NODE:for(var o,a=e.tagName.toLowerCase(),c="<"+a,l=e.attributes,u=0;o=l[u];u++)c+=" "+o.name+'="'+n(o.value)+'"';return C[a]?(i(e)&&(c+="/"),c+">"):c+">"+s(e)+"</"+a+">";case Node.TEXT_NODE:var d=e.data;return t&&j[t.localName]?d:r(d);case Node.COMMENT_NODE:return"<!--"+e.data+"-->";default:throw console.error(e),new Error("not implemented")}}function s(e){e instanceof O.HTMLTemplateElement&&(e=e.content);for(var t="",n=e.firstChild;n;n=n.nextSibling)t+=a(n,e);return t}function c(e,t,n){var r=n||"div";e.textContent="";var o=T(e.ownerDocument.createElement(r));o.innerHTML=t;for(var i;i=o.firstChild;)e.appendChild(M(i))}function l(e){m.call(this,e)}function u(e,t){var n=T(e.cloneNode(!1));n.innerHTML=t;for(var r,o=T(document.createDocumentFragment());r=n.firstChild;)o.appendChild(r);return M(o)}function d(t){return function(){return e.renderAllPending(),S(this)[t]}}function p(e){w(l,e,d(e))}function h(t){Object.defineProperty(l.prototype,t,{get:d(t),set:function(n){e.renderAllPending(),S(this)[t]=n},configurable:!0,enumerable:!0})}function f(t){Object.defineProperty(l.prototype,t,{value:function(){return e.renderAllPending(),S(this)[t].apply(S(this),arguments)},configurable:!0,enumerable:!0})}var m=e.wrappers.Element,w=e.defineGetter,v=e.enqueueMutation,g=e.mixin,b=e.nodesWereAdded,y=e.nodesWereRemoved,E=e.registerWrapper,_=e.snapshotNodeList,S=e.unsafeUnwrap,T=e.unwrap,M=e.wrap,O=e.wrappers,L=/[&\u00A0"]/g,N=/[&\u00A0<>]/g,C=o(["area","base","br","col","command","embed","hr","img","input","keygen","link","meta","param","source","track","wbr"]),j=o(["style","script","xmp","iframe","noembed","noframes","plaintext","noscript"]),D="http://www.w3.org/1999/xhtml",H=/MSIE/.test(navigator.userAgent),x=window.HTMLElement,R=window.HTMLTemplateElement;l.prototype=Object.create(m.prototype),g(l.prototype,{get innerHTML(){return s(this)},set innerHTML(e){if(H&&j[this.localName])return void(this.textContent=e);var t=_(this.childNodes);this.invalidateShadowRenderer()?this instanceof O.HTMLTemplateElement?c(this.content,e):c(this,e,this.tagName):!R&&this instanceof O.HTMLTemplateElement?c(this.content,e):S(this).innerHTML=e;var n=_(this.childNodes);v(this,"childList",{addedNodes:n,removedNodes:t}),y(t),b(n,this)},get outerHTML(){return a(this,this.parentNode)},set outerHTML(e){var t=this.parentNode;if(t){t.invalidateShadowRenderer();var n=u(t,e);t.replaceChild(n,this)}},insertAdjacentHTML:function(e,t){var n,r;switch(String(e).toLowerCase()){case"beforebegin":n=this.parentNode,r=this;break;case"afterend":n=this.parentNode,r=this.nextSibling;break;case"afterbegin":n=this,r=this.firstChild;break;case"beforeend":n=this,r=null;break;default:return}var o=u(n,t);n.insertBefore(o,r)},get hidden(){return this.hasAttribute("hidden")},set hidden(e){e?this.setAttribute("hidden",""):this.removeAttribute("hidden")}}),["clientHeight","clientLeft","clientTop","clientWidth","offsetHeight","offsetLeft","offsetTop","offsetWidth","scrollHeight","scrollWidth"].forEach(p),["scrollLeft","scrollTop"].forEach(h),["focus","getBoundingClientRect","getClientRects","scrollIntoView"].forEach(f),E(x,l,document.createElement("b")),e.wrappers.HTMLElement=l,e.getInnerHTML=s,e.setInnerHTML=c}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){n.call(this,e)}var n=e.wrappers.HTMLElement,r=e.mixin,o=e.registerWrapper,i=e.unsafeUnwrap,a=e.wrap,s=window.HTMLCanvasElement;t.prototype=Object.create(n.prototype),r(t.prototype,{getContext:function(){var e=i(this).getContext.apply(i(this),arguments);return e&&a(e)}}),o(s,t,document.createElement("canvas")),e.wrappers.HTMLCanvasElement=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){n.call(this,e)}var n=e.wrappers.HTMLElement,r=e.mixin,o=e.registerWrapper,i=window.HTMLContentElement;t.prototype=Object.create(n.prototype),r(t.prototype,{constructor:t,get select(){return this.getAttribute("select")},set select(e){this.setAttribute("select",e)},setAttribute:function(e,t){n.prototype.setAttribute.call(this,e,t),"select"===String(e).toLowerCase()&&this.invalidateShadowRenderer(!0)}}),i&&o(i,t),e.wrappers.HTMLContentElement=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){n.call(this,e)}var n=e.wrappers.HTMLElement,r=e.mixin,o=e.registerWrapper,i=e.wrapHTMLCollection,a=e.unwrap,s=window.HTMLFormElement;t.prototype=Object.create(n.prototype),r(t.prototype,{get elements(){return i(a(this).elements)}}),o(s,t,document.createElement("form")),e.wrappers.HTMLFormElement=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){r.call(this,e)}function n(e,t){if(!(this instanceof n))throw new TypeError("DOM object constructor cannot be called as a function.");var o=i(document.createElement("img"));r.call(this,o),a(o,this),void 0!==e&&(o.width=e),void 0!==t&&(o.height=t)}var r=e.wrappers.HTMLElement,o=e.registerWrapper,i=e.unwrap,a=e.rewrap,s=window.HTMLImageElement;t.prototype=Object.create(r.prototype),o(s,t,document.createElement("img")),n.prototype=t.prototype,e.wrappers.HTMLImageElement=t,e.wrappers.Image=n}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){n.call(this,e)}var n=e.wrappers.HTMLElement,r=(e.mixin,e.wrappers.NodeList,e.registerWrapper),o=window.HTMLShadowElement;t.prototype=Object.create(n.prototype),t.prototype.constructor=t,o&&r(o,t),e.wrappers.HTMLShadowElement=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){if(!e.defaultView)return e;var t=d.get(e);if(!t){for(t=e.implementation.createHTMLDocument("");t.lastChild;)t.removeChild(t.lastChild);d.set(e,t)}return t}function n(e){for(var n,r=t(e.ownerDocument),o=c(r.createDocumentFragment());n=e.firstChild;)o.appendChild(n);return o}function r(e){if(o.call(this,e),!p){var t=n(e);u.set(this,l(t))}}var o=e.wrappers.HTMLElement,i=e.mixin,a=e.registerWrapper,s=e.unsafeUnwrap,c=e.unwrap,l=e.wrap,u=new WeakMap,d=new WeakMap,p=window.HTMLTemplateElement;r.prototype=Object.create(o.prototype),i(r.prototype,{constructor:r,get content(){return p?l(s(this).content):u.get(this)}}),p&&a(p,r),e.wrappers.HTMLTemplateElement=r}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){n.call(this,e)}var n=e.wrappers.HTMLElement,r=e.registerWrapper,o=window.HTMLMediaElement;o&&(t.prototype=Object.create(n.prototype),r(o,t,document.createElement("audio")),e.wrappers.HTMLMediaElement=t)}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){r.call(this,e)}function n(e){if(!(this instanceof n))throw new TypeError("DOM object constructor cannot be called as a function.");var t=i(document.createElement("audio"));r.call(this,t),a(t,this),t.setAttribute("preload","auto"),void 0!==e&&t.setAttribute("src",e)}var r=e.wrappers.HTMLMediaElement,o=e.registerWrapper,i=e.unwrap,a=e.rewrap,s=window.HTMLAudioElement;s&&(t.prototype=Object.create(r.prototype),o(s,t,document.createElement("audio")),n.prototype=t.prototype,e.wrappers.HTMLAudioElement=t,e.wrappers.Audio=n)}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){return e.replace(/\s+/g," ").trim()}function n(e){o.call(this,e)}function r(e,t,n,i){if(!(this instanceof r))throw new TypeError("DOM object constructor cannot be called as a function.");var a=c(document.createElement("option"));o.call(this,a),s(a,this),void 0!==e&&(a.text=e),void 0!==t&&a.setAttribute("value",t),n===!0&&a.setAttribute("selected",""),a.selected=i===!0}var o=e.wrappers.HTMLElement,i=e.mixin,a=e.registerWrapper,s=e.rewrap,c=e.unwrap,l=e.wrap,u=window.HTMLOptionElement;n.prototype=Object.create(o.prototype),i(n.prototype,{get text(){return t(this.textContent)},set text(e){this.textContent=t(String(e))},get form(){return l(c(this).form)}}),a(u,n,document.createElement("option")),r.prototype=n.prototype,e.wrappers.HTMLOptionElement=n,e.wrappers.Option=r}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){n.call(this,e)}var n=e.wrappers.HTMLElement,r=e.mixin,o=e.registerWrapper,i=e.unwrap,a=e.wrap,s=window.HTMLSelectElement;t.prototype=Object.create(n.prototype),r(t.prototype,{add:function(e,t){"object"==typeof t&&(t=i(t)),i(this).add(i(e),t)},remove:function(e){return void 0===e?void n.prototype.remove.call(this):("object"==typeof e&&(e=i(e)),void i(this).remove(e))},get form(){return a(i(this).form)}}),o(s,t,document.createElement("select")),e.wrappers.HTMLSelectElement=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){n.call(this,e)}var n=e.wrappers.HTMLElement,r=e.mixin,o=e.registerWrapper,i=e.unwrap,a=e.wrap,s=e.wrapHTMLCollection,c=window.HTMLTableElement;t.prototype=Object.create(n.prototype),r(t.prototype,{get caption(){return a(i(this).caption)},createCaption:function(){return a(i(this).createCaption())},get tHead(){return a(i(this).tHead)},createTHead:function(){return a(i(this).createTHead())},createTFoot:function(){return a(i(this).createTFoot())},get tFoot(){return a(i(this).tFoot)},get tBodies(){return s(i(this).tBodies)},createTBody:function(){return a(i(this).createTBody())},get rows(){return s(i(this).rows)},insertRow:function(e){return a(i(this).insertRow(e))}}),o(c,t,document.createElement("table")),e.wrappers.HTMLTableElement=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){n.call(this,e)}var n=e.wrappers.HTMLElement,r=e.mixin,o=e.registerWrapper,i=e.wrapHTMLCollection,a=e.unwrap,s=e.wrap,c=window.HTMLTableSectionElement;t.prototype=Object.create(n.prototype),r(t.prototype,{constructor:t,get rows(){return i(a(this).rows)},insertRow:function(e){return s(a(this).insertRow(e))}}),o(c,t,document.createElement("thead")),e.wrappers.HTMLTableSectionElement=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){n.call(this,e)}var n=e.wrappers.HTMLElement,r=e.mixin,o=e.registerWrapper,i=e.wrapHTMLCollection,a=e.unwrap,s=e.wrap,c=window.HTMLTableRowElement;t.prototype=Object.create(n.prototype),r(t.prototype,{get cells(){return i(a(this).cells)},insertCell:function(e){return s(a(this).insertCell(e))}}),o(c,t,document.createElement("tr")),e.wrappers.HTMLTableRowElement=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){switch(e.localName){case"content":return new n(e);case"shadow":return new o(e);case"template":return new i(e)}r.call(this,e)}var n=e.wrappers.HTMLContentElement,r=e.wrappers.HTMLElement,o=e.wrappers.HTMLShadowElement,i=e.wrappers.HTMLTemplateElement,a=(e.mixin,e.registerWrapper),s=window.HTMLUnknownElement;t.prototype=Object.create(r.prototype),a(s,t),e.wrappers.HTMLUnknownElement=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){n.call(this,e)}var n=e.wrappers.Element,r=e.wrappers.HTMLElement,o=e.registerWrapper,i=(e.defineWrapGetter,e.unsafeUnwrap),a=e.wrap,s=e.mixin,c="http://www.w3.org/2000/svg",l=window.SVGElement,u=document.createElementNS(c,"title");if(!("classList"in u)){var d=Object.getOwnPropertyDescriptor(n.prototype,"classList");Object.defineProperty(r.prototype,"classList",d),delete n.prototype.classList}t.prototype=Object.create(n.prototype),s(t.prototype,{get ownerSVGElement(){return a(i(this).ownerSVGElement)}}),o(l,t,document.createElementNS(c,"title")),e.wrappers.SVGElement=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){p.call(this,e)}var n=e.mixin,r=e.registerWrapper,o=e.unwrap,i=e.wrap,a=window.SVGUseElement,s="http://www.w3.org/2000/svg",c=i(document.createElementNS(s,"g")),l=document.createElementNS(s,"use"),u=c.constructor,d=Object.getPrototypeOf(u.prototype),p=d.constructor;t.prototype=Object.create(d),"instanceRoot"in l&&n(t.prototype,{get instanceRoot(){return i(o(this).instanceRoot)},get animatedInstanceRoot(){return i(o(this).animatedInstanceRoot)}}),r(a,t,l),e.wrappers.SVGUseElement=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){n.call(this,e)}var n=e.wrappers.EventTarget,r=e.mixin,o=e.registerWrapper,i=e.unsafeUnwrap,a=e.wrap,s=window.SVGElementInstance;s&&(t.prototype=Object.create(n.prototype),r(t.prototype,{get correspondingElement(){return a(i(this).correspondingElement)},get correspondingUseElement(){return a(i(this).correspondingUseElement)},get parentNode(){return a(i(this).parentNode)},get childNodes(){throw new Error("Not implemented")},get firstChild(){return a(i(this).firstChild)},get lastChild(){return a(i(this).lastChild)},get previousSibling(){return a(i(this).previousSibling)},get nextSibling(){return a(i(this).nextSibling)}}),o(s,t),e.wrappers.SVGElementInstance=t)}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){o(e,this)}var n=e.mixin,r=e.registerWrapper,o=e.setWrapper,i=e.unsafeUnwrap,a=e.unwrap,s=e.unwrapIfNeeded,c=e.wrap,l=window.CanvasRenderingContext2D;n(t.prototype,{get canvas(){return c(i(this).canvas)},drawImage:function(){arguments[0]=s(arguments[0]),i(this).drawImage.apply(i(this),arguments)},createPattern:function(){return arguments[0]=a(arguments[0]),i(this).createPattern.apply(i(this),arguments)}}),r(l,t,document.createElement("canvas").getContext("2d")),e.wrappers.CanvasRenderingContext2D=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){i(e,this)}var n=e.addForwardingProperties,r=e.mixin,o=e.registerWrapper,i=e.setWrapper,a=e.unsafeUnwrap,s=e.unwrapIfNeeded,c=e.wrap,l=window.WebGLRenderingContext;if(l){r(t.prototype,{get canvas(){return c(a(this).canvas)},texImage2D:function(){arguments[5]=s(arguments[5]),a(this).texImage2D.apply(a(this),arguments)},texSubImage2D:function(){arguments[6]=s(arguments[6]),a(this).texSubImage2D.apply(a(this),arguments)}});var u=Object.getPrototypeOf(l.prototype);u!==Object.prototype&&n(u,t.prototype);var d=/WebKit/.test(navigator.userAgent)?{drawingBufferHeight:null,drawingBufferWidth:null}:{};o(l,t,d),e.wrappers.WebGLRenderingContext=t}}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){n.call(this,e)}var n=e.wrappers.Node,r=e.GetElementsByInterface,o=e.NonElementParentNodeInterface,i=e.ParentNodeInterface,a=e.SelectorsInterface,s=e.mixin,c=e.registerObject,l=e.registerWrapper,u=window.DocumentFragment;t.prototype=Object.create(n.prototype),s(t.prototype,i),s(t.prototype,a),s(t.prototype,r),s(t.prototype,o),l(u,t,document.createDocumentFragment()),e.wrappers.DocumentFragment=t;var d=c(document.createComment(""));e.wrappers.Comment=d}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){var t=d(u(e).ownerDocument.createDocumentFragment());n.call(this,t),c(t,this);var o=e.shadowRoot;f.set(this,o),this.treeScope_=new r(this,a(o||e)),h.set(this,e)}var n=e.wrappers.DocumentFragment,r=e.TreeScope,o=e.elementFromPoint,i=e.getInnerHTML,a=e.getTreeScope,s=e.mixin,c=e.rewrap,l=e.setInnerHTML,u=e.unsafeUnwrap,d=e.unwrap,p=e.wrap,h=new WeakMap,f=new WeakMap;t.prototype=Object.create(n.prototype),s(t.prototype,{constructor:t,get innerHTML(){return i(this)},set innerHTML(e){l(this,e),this.invalidateShadowRenderer()},get olderShadowRoot(){return f.get(this)||null},get host(){return h.get(this)||null},invalidateShadowRenderer:function(){return h.get(this).invalidateShadowRenderer()},elementFromPoint:function(e,t){return o(this,this.ownerDocument,e,t)},getSelection:function(){return document.getSelection()},get activeElement(){var e=d(this).ownerDocument.activeElement;if(!e||!e.nodeType)return null;for(var t=p(e);!this.contains(t);){for(;t.parentNode;)t=t.parentNode;if(!t.host)return null;t=t.host}return t}}),e.wrappers.ShadowRoot=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){var t=d(e).root;return t instanceof h?t.host:null}function n(t,n){if(t.shadowRoot){n=Math.min(t.childNodes.length-1,n);var r=t.childNodes[n];if(r){var o=e.getDestinationInsertionPoints(r);if(o.length>0){var i=o[0].parentNode;i.nodeType==Node.ELEMENT_NODE&&(t=i)}}}return t}function r(e){return e=u(e),t(e)||e}function o(e){a(e,this)}var i=e.registerWrapper,a=e.setWrapper,s=e.unsafeUnwrap,c=e.unwrap,l=e.unwrapIfNeeded,u=e.wrap,d=e.getTreeScope,p=window.Range,h=e.wrappers.ShadowRoot;o.prototype={get startContainer(){return r(s(this).startContainer)},get endContainer(){return r(s(this).endContainer)},get commonAncestorContainer(){return r(s(this).commonAncestorContainer)},setStart:function(e,t){e=n(e,t),s(this).setStart(l(e),t)},setEnd:function(e,t){e=n(e,t),s(this).setEnd(l(e),t)},setStartBefore:function(e){s(this).setStartBefore(l(e))},setStartAfter:function(e){s(this).setStartAfter(l(e))},setEndBefore:function(e){s(this).setEndBefore(l(e))},setEndAfter:function(e){s(this).setEndAfter(l(e))},selectNode:function(e){s(this).selectNode(l(e))},selectNodeContents:function(e){s(this).selectNodeContents(l(e))},compareBoundaryPoints:function(e,t){return s(this).compareBoundaryPoints(e,c(t))},extractContents:function(){return u(s(this).extractContents())},cloneContents:function(){return u(s(this).cloneContents())},insertNode:function(e){s(this).insertNode(l(e))},surroundContents:function(e){s(this).surroundContents(l(e))},cloneRange:function(){return u(s(this).cloneRange())},isPointInRange:function(e,t){return s(this).isPointInRange(l(e),t)},comparePoint:function(e,t){return s(this).comparePoint(l(e),t)},intersectsNode:function(e){return s(this).intersectsNode(l(e))},toString:function(){return s(this).toString()}},p.prototype.createContextualFragment&&(o.prototype.createContextualFragment=function(e){return u(s(this).createContextualFragment(e))}),i(window.Range,o,document.createRange()),e.wrappers.Range=o}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){e.previousSibling_=e.previousSibling,e.nextSibling_=e.nextSibling,e.parentNode_=e.parentNode}function n(n,o,i){var a=x(n),s=x(o),c=i?x(i):null;if(r(o),t(o),i)n.firstChild===i&&(n.firstChild_=i),i.previousSibling_=i.previousSibling;else{n.lastChild_=n.lastChild,n.lastChild===n.firstChild&&(n.firstChild_=n.firstChild);var l=R(a.lastChild);l&&(l.nextSibling_=l.nextSibling)}e.originalInsertBefore.call(a,s,c)}function r(n){var r=x(n),o=r.parentNode;if(o){var i=R(o);t(n),n.previousSibling&&(n.previousSibling.nextSibling_=n),n.nextSibling&&(n.nextSibling.previousSibling_=n),i.lastChild===n&&(i.lastChild_=n),i.firstChild===n&&(i.firstChild_=n),e.originalRemoveChild.call(o,r)}}function o(e){P.set(e,[])}function i(e){var t=P.get(e);return t||P.set(e,t=[]),t}function a(e){for(var t=[],n=0,r=e.firstChild;r;r=r.nextSibling)t[n++]=r;return t}function s(){for(var e=0;e<F.length;e++){var t=F[e],n=t.parentRenderer;n&&n.dirty||t.render()}F=[]}function c(){T=null,s()}function l(e){var t=A.get(e);return t||(t=new h(e),A.set(e,t)),t}function u(e){var t=j(e).root;return t instanceof C?t:null}function d(e){return l(e.host)}function p(e){this.skip=!1,this.node=e,this.childNodes=[]}function h(e){this.host=e,this.dirty=!1,this.invalidateAttributes(),this.associateNode(e)}function f(e){for(var t=[],n=e.firstChild;n;n=n.nextSibling)E(n)?t.push.apply(t,i(n)):t.push(n);return t}function m(e){if(e instanceof L)return e;if(e instanceof O)return null;for(var t=e.firstChild;t;t=t.nextSibling){var n=m(t);if(n)return n}return null}function w(e,t){i(t).push(e);var n=k.get(e);n?n.push(t):k.set(e,[t])}function v(e){return k.get(e)}function g(e){k.set(e,void 0)}function b(e,t){var n=t.getAttribute("select");if(!n)return!0;if(n=n.trim(),!n)return!0;if(!(e instanceof M))return!1;if(!q.test(n))return!1;try{return e.matches(n)}catch(r){return!1}}function y(e,t){var n=v(t);return n&&n[n.length-1]===e}function E(e){return e instanceof O||e instanceof L}function _(e){return e.shadowRoot}function S(e){for(var t=[],n=e.shadowRoot;n;n=n.olderShadowRoot)t.push(n);return t}var T,M=e.wrappers.Element,O=e.wrappers.HTMLContentElement,L=e.wrappers.HTMLShadowElement,N=e.wrappers.Node,C=e.wrappers.ShadowRoot,j=(e.assert,e.getTreeScope),D=(e.mixin,e.oneOf),H=e.unsafeUnwrap,x=e.unwrap,R=e.wrap,I=e.ArraySplice,P=new WeakMap,k=new WeakMap,A=new WeakMap,W=D(window,["requestAnimationFrame","mozRequestAnimationFrame","webkitRequestAnimationFrame","setTimeout"]),F=[],U=new I;U.equals=function(e,t){return x(e.node)===t},p.prototype={append:function(e){var t=new p(e);return this.childNodes.push(t),t},sync:function(e){if(!this.skip){for(var t=this.node,o=this.childNodes,i=a(x(t)),s=e||new WeakMap,c=U.calculateSplices(o,i),l=0,u=0,d=0,p=0;p<c.length;p++){for(var h=c[p];d<h.index;d++)u++,o[l++].sync(s);for(var f=h.removed.length,m=0;m<f;m++){var w=R(i[u++]);s.get(w)||r(w)}for(var v=h.addedCount,g=i[u]&&R(i[u]),m=0;m<v;m++){var b=o[l++],y=b.node;n(t,y,g),s.set(y,!0),b.sync(s)}d+=v}for(var p=d;p<o.length;p++)o[p].sync(s)}}},h.prototype={render:function(e){if(this.dirty){this.invalidateAttributes();var t=this.host;this.distribution(t);var n=e||new p(t);this.buildRenderTree(n,t);var r=!e;r&&n.sync(),this.dirty=!1}},get parentRenderer(){return j(this.host).renderer},invalidate:function(){if(!this.dirty){this.dirty=!0;var e=this.parentRenderer;if(e&&e.invalidate(),F.push(this),T)return;T=window[W](c,0)}},distribution:function(e){this.resetAllSubtrees(e),this.distributionResolution(e)},resetAll:function(e){E(e)?o(e):g(e),this.resetAllSubtrees(e)},resetAllSubtrees:function(e){for(var t=e.firstChild;t;t=t.nextSibling)this.resetAll(t);e.shadowRoot&&this.resetAll(e.shadowRoot),e.olderShadowRoot&&this.resetAll(e.olderShadowRoot)},distributionResolution:function(e){if(_(e)){for(var t=e,n=f(t),r=S(t),o=0;o<r.length;o++)this.poolDistribution(r[o],n);for(var o=r.length-1;o>=0;o--){var i=r[o],a=m(i);if(a){var s=i.olderShadowRoot;s&&(n=f(s));for(var c=0;c<n.length;c++)w(n[c],a)}this.distributionResolution(i)}}for(var l=e.firstChild;l;l=l.nextSibling)this.distributionResolution(l)},poolDistribution:function(e,t){if(!(e instanceof L))if(e instanceof O){var n=e;this.updateDependentAttributes(n.getAttribute("select"));for(var r=!1,o=0;o<t.length;o++){var e=t[o];e&&b(e,n)&&(w(e,n),t[o]=void 0,r=!0)}if(!r)for(var i=n.firstChild;i;i=i.nextSibling)w(i,n)}else for(var i=e.firstChild;i;i=i.nextSibling)this.poolDistribution(i,t)},buildRenderTree:function(e,t){for(var n=this.compose(t),r=0;r<n.length;r++){var o=n[r],i=e.append(o);this.buildRenderTree(i,o)}if(_(t)){var a=l(t);a.dirty=!1}},compose:function(e){for(var t=[],n=e.shadowRoot||e,r=n.firstChild;r;r=r.nextSibling)if(E(r)){this.associateNode(n);for(var o=i(r),a=0;a<o.length;a++){var s=o[a];y(r,s)&&t.push(s)}}else t.push(r);return t},invalidateAttributes:function(){this.attributes=Object.create(null)},updateDependentAttributes:function(e){if(e){var t=this.attributes;/\.\w+/.test(e)&&(t["class"]=!0),/#\w+/.test(e)&&(t.id=!0),e.replace(/\[\s*([^\s=\|~\]]+)/g,function(e,n){t[n]=!0})}},dependsOnAttribute:function(e){return this.attributes[e]},associateNode:function(e){H(e).polymerShadowRenderer_=this}};var q=/^(:not\()?[*.#[a-zA-Z_|]/;N.prototype.invalidateShadowRenderer=function(e){var t=H(this).polymerShadowRenderer_;return!!t&&(t.invalidate(),!0)},O.prototype.getDistributedNodes=L.prototype.getDistributedNodes=function(){return s(),i(this)},M.prototype.getDestinationInsertionPoints=function(){return s(),v(this)||[]},O.prototype.nodeIsInserted_=L.prototype.nodeIsInserted_=function(){this.invalidateShadowRenderer();var e,t=u(this);t&&(e=d(t)),H(this).polymerShadowRenderer_=e,e&&e.invalidate()},e.getRendererForHost=l,e.getShadowTrees=S,e.renderAllPending=s,e.getDestinationInsertionPoints=v,e.visual={insertBefore:n,remove:r}}(window.ShadowDOMPolyfill),function(e){"use strict";function t(t){if(window[t]){r(!e.wrappers[t]);var c=function(e){n.call(this,e)};c.prototype=Object.create(n.prototype),o(c.prototype,{get form(){return s(a(this).form)}}),i(window[t],c,document.createElement(t.slice(4,-7))),e.wrappers[t]=c}}var n=e.wrappers.HTMLElement,r=e.assert,o=e.mixin,i=e.registerWrapper,a=e.unwrap,s=e.wrap,c=["HTMLButtonElement","HTMLFieldSetElement","HTMLInputElement","HTMLKeygenElement","HTMLLabelElement","HTMLLegendElement","HTMLObjectElement","HTMLOutputElement","HTMLTextAreaElement"];c.forEach(t)}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){r(e,this)}var n=e.registerWrapper,r=e.setWrapper,o=e.unsafeUnwrap,i=e.unwrap,a=e.unwrapIfNeeded,s=e.wrap,c=window.Selection;t.prototype={get anchorNode(){return s(o(this).anchorNode)},get focusNode(){return s(o(this).focusNode)},addRange:function(e){o(this).addRange(a(e))},collapse:function(e,t){o(this).collapse(a(e),t)},containsNode:function(e,t){return o(this).containsNode(a(e),t)},getRangeAt:function(e){return s(o(this).getRangeAt(e))},removeRange:function(e){o(this).removeRange(i(e))},selectAllChildren:function(e){o(this).selectAllChildren(e instanceof ShadowRoot?o(e.host):a(e))},toString:function(){return o(this).toString()}},c.prototype.extend&&(t.prototype.extend=function(e,t){o(this).extend(a(e),t)}),n(window.Selection,t,window.getSelection()),e.wrappers.Selection=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){r(e,this)}var n=e.registerWrapper,r=e.setWrapper,o=e.unsafeUnwrap,i=e.unwrapIfNeeded,a=e.wrap,s=window.TreeWalker;t.prototype={get root(){return a(o(this).root)},get currentNode(){return a(o(this).currentNode)},set currentNode(e){o(this).currentNode=i(e)},get filter(){return o(this).filter},parentNode:function(){return a(o(this).parentNode())},firstChild:function(){return a(o(this).firstChild())},lastChild:function(){return a(o(this).lastChild())},previousSibling:function(){return a(o(this).previousSibling())},previousNode:function(){return a(o(this).previousNode())},nextNode:function(){return a(o(this).nextNode())}},n(s,t),e.wrappers.TreeWalker=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){u.call(this,e),this.treeScope_=new w(this,null)}function n(e){var n=document[e];t.prototype[e]=function(){return j(n.apply(N(this),arguments))}}function r(e,t){x.call(N(t),C(e)),o(e,t)}function o(e,t){e.shadowRoot&&t.adoptNode(e.shadowRoot),e instanceof m&&i(e,t);for(var n=e.firstChild;n;n=n.nextSibling)o(n,t)}function i(e,t){var n=e.olderShadowRoot;n&&t.adoptNode(n)}function a(e){L(e,this)}function s(e,t){var n=document.implementation[t];e.prototype[t]=function(){
+return j(n.apply(N(this),arguments))}}function c(e,t){var n=document.implementation[t];e.prototype[t]=function(){return n.apply(N(this),arguments)}}var l=e.GetElementsByInterface,u=e.wrappers.Node,d=e.ParentNodeInterface,p=e.NonElementParentNodeInterface,h=e.wrappers.Selection,f=e.SelectorsInterface,m=e.wrappers.ShadowRoot,w=e.TreeScope,v=e.cloneNode,g=e.defineGetter,b=e.defineWrapGetter,y=e.elementFromPoint,E=e.forwardMethodsToWrapper,_=e.matchesNames,S=e.mixin,T=e.registerWrapper,M=e.renderAllPending,O=e.rewrap,L=e.setWrapper,N=e.unsafeUnwrap,C=e.unwrap,j=e.wrap,D=e.wrapEventTargetMethods,H=(e.wrapNodeList,new WeakMap);t.prototype=Object.create(u.prototype),b(t,"documentElement"),b(t,"body"),b(t,"head"),g(t,"activeElement",function(){var e=C(this).activeElement;if(!e||!e.nodeType)return null;for(var t=j(e);!this.contains(t);){for(;t.parentNode;)t=t.parentNode;if(!t.host)return null;t=t.host}return t}),["createComment","createDocumentFragment","createElement","createElementNS","createEvent","createEventNS","createRange","createTextNode"].forEach(n);var x=document.adoptNode,R=document.getSelection;S(t.prototype,{adoptNode:function(e){return e.parentNode&&e.parentNode.removeChild(e),r(e,this),e},elementFromPoint:function(e,t){return y(this,this,e,t)},importNode:function(e,t){return v(e,t,N(this))},getSelection:function(){return M(),new h(R.call(C(this)))},getElementsByName:function(e){return f.querySelectorAll.call(this,"[name="+JSON.stringify(String(e))+"]")}});var I=document.createTreeWalker,P=e.wrappers.TreeWalker;if(t.prototype.createTreeWalker=function(e,t,n,r){var o=null;return n&&(n.acceptNode&&"function"==typeof n.acceptNode?o={acceptNode:function(e){return n.acceptNode(j(e))}}:"function"==typeof n&&(o=function(e){return n(j(e))})),new P(I.call(C(this),C(e),t,o,r))},document.registerElement){var k=document.registerElement;t.prototype.registerElement=function(t,n){function r(e){return e?void L(e,this):i?document.createElement(i,t):document.createElement(t)}var o,i;if(void 0!==n&&(o=n.prototype,i=n["extends"]),o||(o=Object.create(HTMLElement.prototype)),e.nativePrototypeTable.get(o))throw new Error("NotSupportedError");for(var a,s=Object.getPrototypeOf(o),c=[];s&&!(a=e.nativePrototypeTable.get(s));)c.push(s),s=Object.getPrototypeOf(s);if(!a)throw new Error("NotSupportedError");for(var l=Object.create(a),u=c.length-1;u>=0;u--)l=Object.create(l);["createdCallback","attachedCallback","detachedCallback","attributeChangedCallback"].forEach(function(e){var t=o[e];t&&(l[e]=function(){j(this)instanceof r||O(this),t.apply(j(this),arguments)})});var d={prototype:l};i&&(d["extends"]=i),r.prototype=o,r.prototype.constructor=r,e.constructorTable.set(l,r),e.nativePrototypeTable.set(o,l);k.call(C(this),t,d);return r},E([window.HTMLDocument||window.Document],["registerElement"])}E([window.HTMLBodyElement,window.HTMLDocument||window.Document,window.HTMLHeadElement,window.HTMLHtmlElement],["appendChild","compareDocumentPosition","contains","getElementsByClassName","getElementsByTagName","getElementsByTagNameNS","insertBefore","querySelector","querySelectorAll","removeChild","replaceChild"]),E([window.HTMLBodyElement,window.HTMLHeadElement,window.HTMLHtmlElement],_),E([window.HTMLDocument||window.Document],["adoptNode","importNode","contains","createComment","createDocumentFragment","createElement","createElementNS","createEvent","createEventNS","createRange","createTextNode","createTreeWalker","elementFromPoint","getElementById","getElementsByName","getSelection"]),S(t.prototype,l),S(t.prototype,d),S(t.prototype,f),S(t.prototype,p),S(t.prototype,{get implementation(){var e=H.get(this);return e?e:(e=new a(C(this).implementation),H.set(this,e),e)},get defaultView(){return j(C(this).defaultView)}}),T(window.Document,t,document.implementation.createHTMLDocument("")),window.HTMLDocument&&T(window.HTMLDocument,t),D([window.HTMLBodyElement,window.HTMLDocument||window.Document,window.HTMLHeadElement]);var A=document.implementation.createDocument;a.prototype.createDocument=function(){return arguments[2]=C(arguments[2]),j(A.apply(N(this),arguments))},s(a,"createDocumentType"),s(a,"createHTMLDocument"),c(a,"hasFeature"),T(window.DOMImplementation,a),E([window.DOMImplementation],["createDocument","createDocumentType","createHTMLDocument","hasFeature"]),e.adoptNodeNoRemove=r,e.wrappers.DOMImplementation=a,e.wrappers.Document=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){n.call(this,e)}var n=e.wrappers.EventTarget,r=e.wrappers.Selection,o=e.mixin,i=e.registerWrapper,a=e.renderAllPending,s=e.unwrap,c=e.unwrapIfNeeded,l=e.wrap,u=window.Window,d=window.getComputedStyle,p=window.getDefaultComputedStyle,h=window.getSelection;t.prototype=Object.create(n.prototype),u.prototype.getComputedStyle=function(e,t){return l(this||window).getComputedStyle(c(e),t)},p&&(u.prototype.getDefaultComputedStyle=function(e,t){return l(this||window).getDefaultComputedStyle(c(e),t)}),u.prototype.getSelection=function(){return l(this||window).getSelection()},delete window.getComputedStyle,delete window.getDefaultComputedStyle,delete window.getSelection,["addEventListener","removeEventListener","dispatchEvent"].forEach(function(e){u.prototype[e]=function(){var t=l(this||window);return t[e].apply(t,arguments)},delete window[e]}),o(t.prototype,{getComputedStyle:function(e,t){return a(),d.call(s(this),c(e),t)},getSelection:function(){return a(),new r(h.call(s(this)))},get document(){return l(s(this).document)}}),p&&(t.prototype.getDefaultComputedStyle=function(e,t){return a(),p.call(s(this),c(e),t)}),i(u,t,window),e.wrappers.Window=t}(window.ShadowDOMPolyfill),function(e){"use strict";var t=e.unwrap,n=window.DataTransfer||window.Clipboard,r=n.prototype.setDragImage;r&&(n.prototype.setDragImage=function(e,n,o){r.call(this,t(e),n,o)})}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){var t;t=e instanceof i?e:new i(e&&o(e)),r(t,this)}var n=e.registerWrapper,r=e.setWrapper,o=e.unwrap,i=window.FormData;i&&(n(i,t,new i),e.wrappers.FormData=t)}(window.ShadowDOMPolyfill),function(e){"use strict";var t=e.unwrapIfNeeded,n=XMLHttpRequest.prototype.send;XMLHttpRequest.prototype.send=function(e){return n.call(this,t(e))}}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){var t=n[e],r=window[t];if(r){var o=document.createElement(e),i=o.constructor;window[t]=i}}var n=(e.isWrapperFor,{a:"HTMLAnchorElement",area:"HTMLAreaElement",audio:"HTMLAudioElement",base:"HTMLBaseElement",body:"HTMLBodyElement",br:"HTMLBRElement",button:"HTMLButtonElement",canvas:"HTMLCanvasElement",caption:"HTMLTableCaptionElement",col:"HTMLTableColElement",content:"HTMLContentElement",data:"HTMLDataElement",datalist:"HTMLDataListElement",del:"HTMLModElement",dir:"HTMLDirectoryElement",div:"HTMLDivElement",dl:"HTMLDListElement",embed:"HTMLEmbedElement",fieldset:"HTMLFieldSetElement",font:"HTMLFontElement",form:"HTMLFormElement",frame:"HTMLFrameElement",frameset:"HTMLFrameSetElement",h1:"HTMLHeadingElement",head:"HTMLHeadElement",hr:"HTMLHRElement",html:"HTMLHtmlElement",iframe:"HTMLIFrameElement",img:"HTMLImageElement",input:"HTMLInputElement",keygen:"HTMLKeygenElement",label:"HTMLLabelElement",legend:"HTMLLegendElement",li:"HTMLLIElement",link:"HTMLLinkElement",map:"HTMLMapElement",marquee:"HTMLMarqueeElement",menu:"HTMLMenuElement",menuitem:"HTMLMenuItemElement",meta:"HTMLMetaElement",meter:"HTMLMeterElement",object:"HTMLObjectElement",ol:"HTMLOListElement",optgroup:"HTMLOptGroupElement",option:"HTMLOptionElement",output:"HTMLOutputElement",p:"HTMLParagraphElement",param:"HTMLParamElement",pre:"HTMLPreElement",progress:"HTMLProgressElement",q:"HTMLQuoteElement",script:"HTMLScriptElement",select:"HTMLSelectElement",shadow:"HTMLShadowElement",source:"HTMLSourceElement",span:"HTMLSpanElement",style:"HTMLStyleElement",table:"HTMLTableElement",tbody:"HTMLTableSectionElement",template:"HTMLTemplateElement",textarea:"HTMLTextAreaElement",thead:"HTMLTableSectionElement",time:"HTMLTimeElement",title:"HTMLTitleElement",tr:"HTMLTableRowElement",track:"HTMLTrackElement",ul:"HTMLUListElement",video:"HTMLVideoElement"});Object.keys(n).forEach(t),Object.getOwnPropertyNames(e.wrappers).forEach(function(t){window[t]=e.wrappers[t]})}(window.ShadowDOMPolyfill),function(e){function t(e,t){var n="";return Array.prototype.forEach.call(e,function(e){n+=e.textContent+"\n\n"}),t||(n=n.replace(d,"")),n}function n(e){var t=document.createElement("style");return t.textContent=e,t}function r(e){var t=n(e);document.head.appendChild(t);var r=[];if(t.sheet)try{r=t.sheet.cssRules}catch(o){}else console.warn("sheet not found",t);return t.parentNode.removeChild(t),r}function o(){C.initialized=!0,document.body.appendChild(C);var e=C.contentDocument,t=e.createElement("base");t.href=document.baseURI,e.head.appendChild(t)}function i(e){C.initialized||o(),document.body.appendChild(C),e(C.contentDocument),document.body.removeChild(C)}function a(e,t){if(t){var o;if(e.match("@import")&&D){var a=n(e);i(function(e){e.head.appendChild(a.impl),o=Array.prototype.slice.call(a.sheet.cssRules,0),t(o)})}else o=r(e),t(o)}}function s(e){e&&l().appendChild(document.createTextNode(e))}function c(e,t){var r=n(e);r.setAttribute(t,""),r.setAttribute(x,""),document.head.appendChild(r)}function l(){return j||(j=document.createElement("style"),j.setAttribute(x,""),j[x]=!0),j}var u={strictStyling:!1,registry:{},shimStyling:function(e,n,r){var o=this.prepareRoot(e,n,r),i=this.isTypeExtension(r),a=this.makeScopeSelector(n,i),s=t(o,!0);s=this.scopeCssText(s,a),e&&(e.shimmedStyle=s),this.addCssToDocument(s,n)},shimStyle:function(e,t){return this.shimCssText(e.textContent,t)},shimCssText:function(e,t){return e=this.insertDirectives(e),this.scopeCssText(e,t)},makeScopeSelector:function(e,t){return e?t?"[is="+e+"]":e:""},isTypeExtension:function(e){return e&&e.indexOf("-")<0},prepareRoot:function(e,t,n){var r=this.registerRoot(e,t,n);return this.replaceTextInStyles(r.rootStyles,this.insertDirectives),this.removeStyles(e,r.rootStyles),this.strictStyling&&this.applyScopeToContent(e,t),r.scopeStyles},removeStyles:function(e,t){for(var n,r=0,o=t.length;r<o&&(n=t[r]);r++)n.parentNode.removeChild(n)},registerRoot:function(e,t,n){var r=this.registry[t]={root:e,name:t,extendsName:n},o=this.findStyles(e);r.rootStyles=o,r.scopeStyles=r.rootStyles;var i=this.registry[r.extendsName];return i&&(r.scopeStyles=i.scopeStyles.concat(r.scopeStyles)),r},findStyles:function(e){if(!e)return[];var t=e.querySelectorAll("style");return Array.prototype.filter.call(t,function(e){return!e.hasAttribute(R)})},applyScopeToContent:function(e,t){e&&(Array.prototype.forEach.call(e.querySelectorAll("*"),function(e){e.setAttribute(t,"")}),Array.prototype.forEach.call(e.querySelectorAll("template"),function(e){this.applyScopeToContent(e.content,t)},this))},insertDirectives:function(e){return e=this.insertPolyfillDirectivesInCssText(e),this.insertPolyfillRulesInCssText(e)},insertPolyfillDirectivesInCssText:function(e){return e=e.replace(p,function(e,t){return t.slice(0,-2)+"{"}),e.replace(h,function(e,t){return t+" {"})},insertPolyfillRulesInCssText:function(e){return e=e.replace(f,function(e,t){return t.slice(0,-1)}),e.replace(m,function(e,t,n,r){var o=e.replace(t,"").replace(n,"");return r+o})},scopeCssText:function(e,t){var n=this.extractUnscopedRulesFromCssText(e);if(e=this.insertPolyfillHostInCssText(e),e=this.convertColonHost(e),e=this.convertColonHostContext(e),e=this.convertShadowDOMSelectors(e),t){var e,r=this;a(e,function(n){e=r.scopeRules(n,t)})}return e=e+"\n"+n,e.trim()},extractUnscopedRulesFromCssText:function(e){for(var t,n="";t=w.exec(e);)n+=t[1].slice(0,-1)+"\n\n";for(;t=v.exec(e);)n+=t[0].replace(t[2],"").replace(t[1],t[3])+"\n\n";return n},convertColonHost:function(e){return this.convertColonRule(e,E,this.colonHostPartReplacer)},convertColonHostContext:function(e){return this.convertColonRule(e,_,this.colonHostContextPartReplacer)},convertColonRule:function(e,t,n){return e.replace(t,function(e,t,r,o){if(t=O,r){for(var i,a=r.split(","),s=[],c=0,l=a.length;c<l&&(i=a[c]);c++)i=i.trim(),s.push(n(t,i,o));return s.join(",")}return t+o})},colonHostContextPartReplacer:function(e,t,n){return t.match(g)?this.colonHostPartReplacer(e,t,n):e+t+n+", "+t+" "+e+n},colonHostPartReplacer:function(e,t,n){return e+t.replace(g,"")+n},convertShadowDOMSelectors:function(e){for(var t=0;t<N.length;t++)e=e.replace(N[t]," ");return e},scopeRules:function(e,t){var n="";return e&&Array.prototype.forEach.call(e,function(e){if(e.selectorText&&e.style&&void 0!==e.style.cssText)n+=this.scopeSelector(e.selectorText,t,this.strictStyling)+" {\n\t",n+=this.propertiesFromRule(e)+"\n}\n\n";else if(e.type===CSSRule.MEDIA_RULE)n+="@media "+e.media.mediaText+" {\n",n+=this.scopeRules(e.cssRules,t),n+="\n}\n\n";else try{e.cssText&&(n+=e.cssText+"\n\n")}catch(r){e.type===CSSRule.KEYFRAMES_RULE&&e.cssRules&&(n+=this.ieSafeCssTextFromKeyFrameRule(e))}},this),n},ieSafeCssTextFromKeyFrameRule:function(e){var t="@keyframes "+e.name+" {";return Array.prototype.forEach.call(e.cssRules,function(e){t+=" "+e.keyText+" {"+e.style.cssText+"}"}),t+=" }"},scopeSelector:function(e,t,n){var r=[],o=e.split(",");return o.forEach(function(e){e=e.trim(),this.selectorNeedsScoping(e,t)&&(e=n&&!e.match(O)?this.applyStrictSelectorScope(e,t):this.applySelectorScope(e,t)),r.push(e)},this),r.join(", ")},selectorNeedsScoping:function(e,t){if(Array.isArray(t))return!0;var n=this.makeScopeMatcher(t);return!e.match(n)},makeScopeMatcher:function(e){return e=e.replace(/\[/g,"\\[").replace(/\]/g,"\\]"),new RegExp("^("+e+")"+S,"m")},applySelectorScope:function(e,t){return Array.isArray(t)?this.applySelectorScopeList(e,t):this.applySimpleSelectorScope(e,t)},applySelectorScopeList:function(e,t){for(var n,r=[],o=0;n=t[o];o++)r.push(this.applySimpleSelectorScope(e,n));return r.join(", ")},applySimpleSelectorScope:function(e,t){return e.match(L)?(e=e.replace(O,t),e.replace(L,t+" ")):t+" "+e},applyStrictSelectorScope:function(e,t){t=t.replace(/\[is=([^\]]*)\]/g,"$1");var n=[" ",">","+","~"],r=e,o="["+t+"]";return n.forEach(function(e){var t=r.split(e);r=t.map(function(e){var t=e.trim().replace(L,"");return t&&n.indexOf(t)<0&&t.indexOf(o)<0&&(e=t.replace(/([^:]*)(:*)(.*)/,"$1"+o+"$2$3")),e}).join(e)}),r},insertPolyfillHostInCssText:function(e){return e.replace(M,b).replace(T,g)},propertiesFromRule:function(e){var t=e.style.cssText;e.style.content&&!e.style.content.match(/['"]+|attr/)&&(t=t.replace(/content:[^;]*;/g,"content: '"+e.style.content+"';"));var n=e.style;for(var r in n)"initial"===n[r]&&(t+=r+": initial; ");return t},replaceTextInStyles:function(e,t){e&&t&&(e instanceof Array||(e=[e]),Array.prototype.forEach.call(e,function(e){e.textContent=t.call(this,e.textContent)},this))},addCssToDocument:function(e,t){e.match("@import")?c(e,t):s(e)}},d=/\/\*[^*]*\*+([^\/*][^*]*\*+)*\//gim,p=/\/\*\s*@polyfill ([^*]*\*+([^\/*][^*]*\*+)*\/)([^{]*?){/gim,h=/polyfill-next-selector[^}]*content\:[\s]*?['"](.*?)['"][;\s]*}([^{]*?){/gim,f=/\/\*\s@polyfill-rule([^*]*\*+([^\/*][^*]*\*+)*)\//gim,m=/(polyfill-rule)[^}]*(content\:[\s]*['"](.*?)['"])[;\s]*[^}]*}/gim,w=/\/\*\s@polyfill-unscoped-rule([^*]*\*+([^\/*][^*]*\*+)*)\//gim,v=/(polyfill-unscoped-rule)[^}]*(content\:[\s]*['"](.*?)['"])[;\s]*[^}]*}/gim,g="-shadowcsshost",b="-shadowcsscontext",y=")(?:\\(((?:\\([^)(]*\\)|[^)(]*)+?)\\))?([^,{]*)",E=new RegExp("("+g+y,"gim"),_=new RegExp("("+b+y,"gim"),S="([>\\s~+[.,{:][\\s\\S]*)?$",T=/\:host/gim,M=/\:host-context/gim,O=g+"-no-combinator",L=new RegExp(g,"gim"),N=(new RegExp(b,"gim"),[/>>>/g,/::shadow/g,/::content/g,/\/deep\//g,/\/shadow\//g,/\/shadow-deep\//g,/\^\^/g,/\^(?!=)/g]),C=document.createElement("iframe");C.style.display="none";var j,D=navigator.userAgent.match("Chrome"),H="shim-shadowdom",x="shim-shadowdom-css",R="no-shim";if(window.ShadowDOMPolyfill){s("style { display: none !important; }\n");var I=ShadowDOMPolyfill.wrap(document),P=I.querySelector("head");P.insertBefore(l(),P.childNodes[0]),document.addEventListener("DOMContentLoaded",function(){e.urlResolver;if(window.HTMLImports&&!HTMLImports.useNative){var t="link[rel=stylesheet]["+H+"]",n="style["+H+"]";HTMLImports.importer.documentPreloadSelectors+=","+t,HTMLImports.importer.importsPreloadSelectors+=","+t,HTMLImports.parser.documentSelectors=[HTMLImports.parser.documentSelectors,t,n].join(",");var r=HTMLImports.parser.parseGeneric;HTMLImports.parser.parseGeneric=function(e){if(!e[x]){var t=e.__importElement||e;if(!t.hasAttribute(H))return void r.call(this,e);e.__resource&&(t=e.ownerDocument.createElement("style"),t.textContent=e.__resource),HTMLImports.path.resolveUrlsInStyle(t,e.href),t.textContent=u.shimStyle(t),t.removeAttribute(H,""),t.setAttribute(x,""),t[x]=!0,t.parentNode!==P&&(e.parentNode===P?P.replaceChild(t,e):this.addElementToDocument(t)),t.__importParsed=!0,this.markParsingComplete(e),this.parseNext()}};var o=HTMLImports.parser.hasResource;HTMLImports.parser.hasResource=function(e){return"link"===e.localName&&"stylesheet"===e.rel&&e.hasAttribute(H)?e.__resource:o.call(this,e)}}})}e.ShadowCSS=u}(window.WebComponents)),function(e){window.ShadowDOMPolyfill?(window.wrap=ShadowDOMPolyfill.wrapIfNeeded,window.unwrap=ShadowDOMPolyfill.unwrapIfNeeded):window.wrap=window.unwrap=function(e){return e}}(window.WebComponents),function(e){"use strict";function t(e){return void 0!==p[e]}function n(){s.call(this),this._isInvalid=!0}function r(e){return""==e&&n.call(this),e.toLowerCase()}function o(e){var t=e.charCodeAt(0);return t>32&&t<127&&[34,35,60,62,63,96].indexOf(t)==-1?e:encodeURIComponent(e)}function i(e){var t=e.charCodeAt(0);return t>32&&t<127&&[34,35,60,62,96].indexOf(t)==-1?e:encodeURIComponent(e)}function a(e,a,s){function c(e){b.push(e)}var l=a||"scheme start",u=0,d="",v=!1,g=!1,b=[];e:for(;(e[u-1]!=f||0==u)&&!this._isInvalid;){var y=e[u];switch(l){case"scheme start":if(!y||!m.test(y)){if(a){c("Invalid scheme.");break e}d="",l="no scheme";continue}d+=y.toLowerCase(),l="scheme";break;case"scheme":if(y&&w.test(y))d+=y.toLowerCase();else{if(":"!=y){if(a){if(f==y)break e;c("Code point not allowed in scheme: "+y);break e}d="",u=0,l="no scheme";continue}if(this._scheme=d,d="",a)break e;t(this._scheme)&&(this._isRelative=!0),l="file"==this._scheme?"relative":this._isRelative&&s&&s._scheme==this._scheme?"relative or authority":this._isRelative?"authority first slash":"scheme data"}break;case"scheme data":"?"==y?(this._query="?",l="query"):"#"==y?(this._fragment="#",l="fragment"):f!=y&&"\t"!=y&&"\n"!=y&&"\r"!=y&&(this._schemeData+=o(y));break;case"no scheme":if(s&&t(s._scheme)){l="relative";continue}c("Missing scheme."),n.call(this);break;case"relative or authority":if("/"!=y||"/"!=e[u+1]){c("Expected /, got: "+y),l="relative";continue}l="authority ignore slashes";break;case"relative":if(this._isRelative=!0,"file"!=this._scheme&&(this._scheme=s._scheme),f==y){this._host=s._host,this._port=s._port,this._path=s._path.slice(),this._query=s._query,this._username=s._username,this._password=s._password;break e}if("/"==y||"\\"==y)"\\"==y&&c("\\ is an invalid code point."),l="relative slash";else if("?"==y)this._host=s._host,this._port=s._port,this._path=s._path.slice(),this._query="?",this._username=s._username,this._password=s._password,l="query";else{if("#"!=y){var E=e[u+1],_=e[u+2];("file"!=this._scheme||!m.test(y)||":"!=E&&"|"!=E||f!=_&&"/"!=_&&"\\"!=_&&"?"!=_&&"#"!=_)&&(this._host=s._host,this._port=s._port,this._username=s._username,this._password=s._password,this._path=s._path.slice(),this._path.pop()),l="relative path";continue}this._host=s._host,this._port=s._port,this._path=s._path.slice(),this._query=s._query,this._fragment="#",this._username=s._username,this._password=s._password,l="fragment"}break;case"relative slash":if("/"!=y&&"\\"!=y){"file"!=this._scheme&&(this._host=s._host,this._port=s._port,this._username=s._username,this._password=s._password),l="relative path";continue}"\\"==y&&c("\\ is an invalid code point."),l="file"==this._scheme?"file host":"authority ignore slashes";break;case"authority first slash":if("/"!=y){c("Expected '/', got: "+y),l="authority ignore slashes";continue}l="authority second slash";break;case"authority second slash":if(l="authority ignore slashes","/"!=y){c("Expected '/', got: "+y);continue}break;case"authority ignore slashes":if("/"!=y&&"\\"!=y){l="authority";continue}c("Expected authority, got: "+y);break;case"authority":if("@"==y){v&&(c("@ already seen."),d+="%40"),v=!0;for(var S=0;S<d.length;S++){var T=d[S];if("\t"!=T&&"\n"!=T&&"\r"!=T)if(":"!=T||null!==this._password){var M=o(T);null!==this._password?this._password+=M:this._username+=M}else this._password="";else c("Invalid whitespace in authority.")}d=""}else{if(f==y||"/"==y||"\\"==y||"?"==y||"#"==y){u-=d.length,d="",l="host";continue}d+=y}break;case"file host":if(f==y||"/"==y||"\\"==y||"?"==y||"#"==y){2!=d.length||!m.test(d[0])||":"!=d[1]&&"|"!=d[1]?0==d.length?l="relative path start":(this._host=r.call(this,d),d="",l="relative path start"):l="relative path";continue}"\t"==y||"\n"==y||"\r"==y?c("Invalid whitespace in file host."):d+=y;break;case"host":case"hostname":if(":"!=y||g){if(f==y||"/"==y||"\\"==y||"?"==y||"#"==y){if(this._host=r.call(this,d),d="",l="relative path start",a)break e;continue}"\t"!=y&&"\n"!=y&&"\r"!=y?("["==y?g=!0:"]"==y&&(g=!1),d+=y):c("Invalid code point in host/hostname: "+y)}else if(this._host=r.call(this,d),d="",l="port","hostname"==a)break e;break;case"port":if(/[0-9]/.test(y))d+=y;else{if(f==y||"/"==y||"\\"==y||"?"==y||"#"==y||a){if(""!=d){var O=parseInt(d,10);O!=p[this._scheme]&&(this._port=O+""),d=""}if(a)break e;l="relative path start";continue}"\t"==y||"\n"==y||"\r"==y?c("Invalid code point in port: "+y):n.call(this)}break;case"relative path start":if("\\"==y&&c("'\\' not allowed in path."),l="relative path","/"!=y&&"\\"!=y)continue;break;case"relative path":if(f!=y&&"/"!=y&&"\\"!=y&&(a||"?"!=y&&"#"!=y))"\t"!=y&&"\n"!=y&&"\r"!=y&&(d+=o(y));else{"\\"==y&&c("\\ not allowed in relative path.");var L;(L=h[d.toLowerCase()])&&(d=L),".."==d?(this._path.pop(),"/"!=y&&"\\"!=y&&this._path.push("")):"."==d&&"/"!=y&&"\\"!=y?this._path.push(""):"."!=d&&("file"==this._scheme&&0==this._path.length&&2==d.length&&m.test(d[0])&&"|"==d[1]&&(d=d[0]+":"),this._path.push(d)),d="","?"==y?(this._query="?",l="query"):"#"==y&&(this._fragment="#",l="fragment")}break;case"query":a||"#"!=y?f!=y&&"\t"!=y&&"\n"!=y&&"\r"!=y&&(this._query+=i(y)):(this._fragment="#",l="fragment");break;case"fragment":f!=y&&"\t"!=y&&"\n"!=y&&"\r"!=y&&(this._fragment+=y)}u++}}function s(){this._scheme="",this._schemeData="",this._username="",this._password=null,this._host="",this._port="",this._path=[],this._query="",this._fragment="",this._isInvalid=!1,this._isRelative=!1}function c(e,t){void 0===t||t instanceof c||(t=new c(String(t))),this._url=e,s.call(this);var n=e.replace(/^[ \t\r\n\f]+|[ \t\r\n\f]+$/g,"");a.call(this,n,null,t)}var l=!1;if(!e.forceJURL)try{var u=new URL("b","http://a");u.pathname="c%20d",l="http://a/c%20d"===u.href}catch(d){}if(!l){var p=Object.create(null);p.ftp=21,p.file=0,p.gopher=70,p.http=80,p.https=443,p.ws=80,p.wss=443;var h=Object.create(null);h["%2e"]=".",h[".%2e"]="..",h["%2e."]="..",h["%2e%2e"]="..";var f=void 0,m=/[a-zA-Z]/,w=/[a-zA-Z0-9\+\-\.]/;c.prototype={toString:function(){return this.href},get href(){if(this._isInvalid)return this._url;var e="";return""==this._username&&null==this._password||(e=this._username+(null!=this._password?":"+this._password:"")+"@"),this.protocol+(this._isRelative?"//"+e+this.host:"")+this.pathname+this._query+this._fragment},set href(e){s.call(this),a.call(this,e)},get protocol(){return this._scheme+":"},set protocol(e){this._isInvalid||a.call(this,e+":","scheme start")},get host(){return this._isInvalid?"":this._port?this._host+":"+this._port:this._host},set host(e){!this._isInvalid&&this._isRelative&&a.call(this,e,"host")},get hostname(){return this._host},set hostname(e){!this._isInvalid&&this._isRelative&&a.call(this,e,"hostname")},get port(){return this._port},set port(e){!this._isInvalid&&this._isRelative&&a.call(this,e,"port")},get pathname(){return this._isInvalid?"":this._isRelative?"/"+this._path.join("/"):this._schemeData},set pathname(e){!this._isInvalid&&this._isRelative&&(this._path=[],a.call(this,e,"relative path start"))},get search(){return this._isInvalid||!this._query||"?"==this._query?"":this._query},set search(e){!this._isInvalid&&this._isRelative&&(this._query="?","?"==e[0]&&(e=e.slice(1)),a.call(this,e,"query"))},get hash(){return this._isInvalid||!this._fragment||"#"==this._fragment?"":this._fragment},set hash(e){this._isInvalid||(this._fragment="#","#"==e[0]&&(e=e.slice(1)),a.call(this,e,"fragment"))},get origin(){var e;if(this._isInvalid||!this._scheme)return"";switch(this._scheme){case"data":case"file":case"javascript":case"mailto":return"null"}return e=this.host,e?this._scheme+"://"+e:""}};var v=e.URL;v&&(c.createObjectURL=function(e){return v.createObjectURL.apply(v,arguments)},c.revokeObjectURL=function(e){v.revokeObjectURL(e)}),e.URL=c}}(self),function(e){function t(e){y.push(e),b||(b=!0,m(r))}function n(e){return window.ShadowDOMPolyfill&&window.ShadowDOMPolyfill.wrapIfNeeded(e)||e}function r(){b=!1;var e=y;y=[],e.sort(function(e,t){return e.uid_-t.uid_});var t=!1;e.forEach(function(e){var n=e.takeRecords();o(e),n.length&&(e.callback_(n,e),t=!0)}),t&&r()}function o(e){e.nodes_.forEach(function(t){var n=w.get(t);n&&n.forEach(function(t){t.observer===e&&t.removeTransientObservers()})})}function i(e,t){for(var n=e;n;n=n.parentNode){var r=w.get(n);if(r)for(var o=0;o<r.length;o++){var i=r[o],a=i.options;if(n===e||a.subtree){var s=t(a);s&&i.enqueue(s)}}}}function a(e){this.callback_=e,this.nodes_=[],this.records_=[],this.uid_=++E}function s(e,t){this.type=e,this.target=t,this.addedNodes=[],this.removedNodes=[],this.previousSibling=null,this.nextSibling=null,this.attributeName=null,this.attributeNamespace=null,this.oldValue=null}function c(e){var t=new s(e.type,e.target);return t.addedNodes=e.addedNodes.slice(),t.removedNodes=e.removedNodes.slice(),t.previousSibling=e.previousSibling,t.nextSibling=e.nextSibling,t.attributeName=e.attributeName,t.attributeNamespace=e.attributeNamespace,t.oldValue=e.oldValue,t}function l(e,t){return _=new s(e,t)}function u(e){return S?S:(S=c(_),S.oldValue=e,S)}function d(){_=S=void 0}function p(e){return e===S||e===_}function h(e,t){return e===t?e:S&&p(e)?S:null}function f(e,t,n){this.observer=e,this.target=t,this.options=n,this.transientObservedNodes=[]}if(!e.JsMutationObserver){var m,w=new WeakMap;if(/Trident|Edge/.test(navigator.userAgent))m=setTimeout;else if(window.setImmediate)m=window.setImmediate;else{var v=[],g=String(Math.random());window.addEventListener("message",function(e){if(e.data===g){var t=v;v=[],t.forEach(function(e){e()})}}),m=function(e){v.push(e),window.postMessage(g,"*")}}var b=!1,y=[],E=0;a.prototype={observe:function(e,t){if(e=n(e),!t.childList&&!t.attributes&&!t.characterData||t.attributeOldValue&&!t.attributes||t.attributeFilter&&t.attributeFilter.length&&!t.attributes||t.characterDataOldValue&&!t.characterData)throw new SyntaxError;var r=w.get(e);r||w.set(e,r=[]);for(var o,i=0;i<r.length;i++)if(r[i].observer===this){o=r[i],o.removeListeners(),o.options=t;break}o||(o=new f(this,e,t),r.push(o),this.nodes_.push(e)),o.addListeners()},disconnect:function(){this.nodes_.forEach(function(e){for(var t=w.get(e),n=0;n<t.length;n++){var r=t[n];if(r.observer===this){r.removeListeners(),t.splice(n,1);break}}},this),this.records_=[]},takeRecords:function(){var e=this.records_;return this.records_=[],e}};var _,S;f.prototype={enqueue:function(e){var n=this.observer.records_,r=n.length;if(n.length>0){var o=n[r-1],i=h(o,e);if(i)return void(n[r-1]=i)}else t(this.observer);n[r]=e},addListeners:function(){this.addListeners_(this.target)},addListeners_:function(e){var t=this.options;t.attributes&&e.addEventListener("DOMAttrModified",this,!0),t.characterData&&e.addEventListener("DOMCharacterDataModified",this,!0),t.childList&&e.addEventListener("DOMNodeInserted",this,!0),(t.childList||t.subtree)&&e.addEventListener("DOMNodeRemoved",this,!0)},removeListeners:function(){this.removeListeners_(this.target)},removeListeners_:function(e){var t=this.options;t.attributes&&e.removeEventListener("DOMAttrModified",this,!0),t.characterData&&e.removeEventListener("DOMCharacterDataModified",this,!0),t.childList&&e.removeEventListener("DOMNodeInserted",this,!0),(t.childList||t.subtree)&&e.removeEventListener("DOMNodeRemoved",this,!0)},addTransientObserver:function(e){if(e!==this.target){this.addListeners_(e),this.transientObservedNodes.push(e);var t=w.get(e);t||w.set(e,t=[]),t.push(this)}},removeTransientObservers:function(){var e=this.transientObservedNodes;this.transientObservedNodes=[],e.forEach(function(e){this.removeListeners_(e);for(var t=w.get(e),n=0;n<t.length;n++)if(t[n]===this){t.splice(n,1);break}},this)},handleEvent:function(e){switch(e.stopImmediatePropagation(),e.type){case"DOMAttrModified":var t=e.attrName,n=e.relatedNode.namespaceURI,r=e.target,o=new l("attributes",r);o.attributeName=t,o.attributeNamespace=n;var a=e.attrChange===MutationEvent.ADDITION?null:e.prevValue;i(r,function(e){if(e.attributes&&(!e.attributeFilter||!e.attributeFilter.length||e.attributeFilter.indexOf(t)!==-1||e.attributeFilter.indexOf(n)!==-1))return e.attributeOldValue?u(a):o});break;case"DOMCharacterDataModified":var r=e.target,o=l("characterData",r),a=e.prevValue;i(r,function(e){if(e.characterData)return e.characterDataOldValue?u(a):o});break;case"DOMNodeRemoved":this.addTransientObserver(e.target);case"DOMNodeInserted":var s,c,p=e.target;"DOMNodeInserted"===e.type?(s=[p],c=[]):(s=[],c=[p]);var h=p.previousSibling,f=p.nextSibling,o=l("childList",e.target.parentNode);o.addedNodes=s,o.removedNodes=c,o.previousSibling=h,o.nextSibling=f,i(e.relatedNode,function(e){if(e.childList)return o})}d()}},e.JsMutationObserver=a,e.MutationObserver||(e.MutationObserver=a,a._isPolyfilled=!0)}}(self),function(e){"use strict";if(!window.performance||!window.performance.now){var t=Date.now();window.performance={now:function(){return Date.now()-t}}}window.requestAnimationFrame||(window.requestAnimationFrame=function(){var e=window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame;return e?function(t){return e(function(){t(performance.now())})}:function(e){return window.setTimeout(e,1e3/60)}}()),window.cancelAnimationFrame||(window.cancelAnimationFrame=function(){return window.webkitCancelAnimationFrame||window.mozCancelAnimationFrame||function(e){clearTimeout(e)}}());var n=function(){var e=document.createEvent("Event");return e.initEvent("foo",!0,!0),e.preventDefault(),e.defaultPrevented}();if(!n){var r=Event.prototype.preventDefault;Event.prototype.preventDefault=function(){this.cancelable&&(r.call(this),Object.defineProperty(this,"defaultPrevented",{get:function(){return!0},configurable:!0}))}}var o=/Trident/.test(navigator.userAgent);if((!window.CustomEvent||o&&"function"!=typeof window.CustomEvent)&&(window.CustomEvent=function(e,t){t=t||{};var n=document.createEvent("CustomEvent");return n.initCustomEvent(e,Boolean(t.bubbles),Boolean(t.cancelable),t.detail),n},window.CustomEvent.prototype=window.Event.prototype),!window.Event||o&&"function"!=typeof window.Event){var i=window.Event;window.Event=function(e,t){t=t||{};var n=document.createEvent("Event");return n.initEvent(e,Boolean(t.bubbles),Boolean(t.cancelable)),n},window.Event.prototype=i.prototype}}(window.WebComponents),window.HTMLImports=window.HTMLImports||{flags:{}},function(e){function t(e,t){t=t||f,r(function(){i(e,t)},t)}function n(e){return"complete"===e.readyState||e.readyState===v}function r(e,t){if(n(t))e&&e();else{var o=function(){"complete"!==t.readyState&&t.readyState!==v||(t.removeEventListener(g,o),r(e,t))};t.addEventListener(g,o)}}function o(e){e.target.__loaded=!0}function i(e,t){function n(){c==l&&e&&e({allImports:s,loadedImports:u,errorImports:d})}function r(e){o(e),u.push(this),c++,n()}function i(e){
+d.push(this),c++,n()}var s=t.querySelectorAll("link[rel=import]"),c=0,l=s.length,u=[],d=[];if(l)for(var p,h=0;h<l&&(p=s[h]);h++)a(p)?(u.push(this),c++,n()):(p.addEventListener("load",r),p.addEventListener("error",i));else n()}function a(e){return d?e.__loaded||e["import"]&&"loading"!==e["import"].readyState:e.__importParsed}function s(e){for(var t,n=0,r=e.length;n<r&&(t=e[n]);n++)c(t)&&l(t)}function c(e){return"link"===e.localName&&"import"===e.rel}function l(e){var t=e["import"];t?o({target:e}):(e.addEventListener("load",o),e.addEventListener("error",o))}var u="import",d=Boolean(u in document.createElement("link")),p=Boolean(window.ShadowDOMPolyfill),h=function(e){return p?window.ShadowDOMPolyfill.wrapIfNeeded(e):e},f=h(document),m={get:function(){var e=window.HTMLImports.currentScript||document.currentScript||("complete"!==document.readyState?document.scripts[document.scripts.length-1]:null);return h(e)},configurable:!0};Object.defineProperty(document,"_currentScript",m),Object.defineProperty(f,"_currentScript",m);var w=/Trident/.test(navigator.userAgent),v=w?"complete":"interactive",g="readystatechange";d&&(new MutationObserver(function(e){for(var t,n=0,r=e.length;n<r&&(t=e[n]);n++)t.addedNodes&&s(t.addedNodes)}).observe(document.head,{childList:!0}),function(){if("loading"===document.readyState)for(var e,t=document.querySelectorAll("link[rel=import]"),n=0,r=t.length;n<r&&(e=t[n]);n++)l(e)}()),t(function(e){window.HTMLImports.ready=!0,window.HTMLImports.readyTime=(new Date).getTime();var t=f.createEvent("CustomEvent");t.initCustomEvent("HTMLImportsLoaded",!0,!0,e),f.dispatchEvent(t)}),e.IMPORT_LINK_TYPE=u,e.useNative=d,e.rootDocument=f,e.whenReady=t,e.isIE=w}(window.HTMLImports),function(e){var t=[],n=function(e){t.push(e)},r=function(){t.forEach(function(t){t(e)})};e.addModule=n,e.initializeModules=r}(window.HTMLImports),window.HTMLImports.addModule(function(e){var t=/(url\()([^)]*)(\))/g,n=/(@import[\s]+(?!url\())([^;]*)(;)/g,r={resolveUrlsInStyle:function(e,t){var n=e.ownerDocument,r=n.createElement("a");return e.textContent=this.resolveUrlsInCssText(e.textContent,t,r),e},resolveUrlsInCssText:function(e,r,o){var i=this.replaceUrls(e,o,r,t);return i=this.replaceUrls(i,o,r,n)},replaceUrls:function(e,t,n,r){return e.replace(r,function(e,r,o,i){var a=o.replace(/["']/g,"");return n&&(a=new URL(a,n).href),t.href=a,a=t.href,r+"'"+a+"'"+i})}};e.path=r}),window.HTMLImports.addModule(function(e){var t={async:!0,ok:function(e){return e.status>=200&&e.status<300||304===e.status||0===e.status},load:function(n,r,o){var i=new XMLHttpRequest;return(e.flags.debug||e.flags.bust)&&(n+="?"+Math.random()),i.open("GET",n,t.async),i.addEventListener("readystatechange",function(e){if(4===i.readyState){var n=null;try{var a=i.getResponseHeader("Location");a&&(n="/"===a.substr(0,1)?location.origin+a:a)}catch(e){console.error(e.message)}r.call(o,!t.ok(i)&&i,i.response||i.responseText,n)}}),i.send(),i},loadDocument:function(e,t,n){this.load(e,t,n).responseType="document"}};e.xhr=t}),window.HTMLImports.addModule(function(e){var t=e.xhr,n=e.flags,r=function(e,t){this.cache={},this.onload=e,this.oncomplete=t,this.inflight=0,this.pending={}};r.prototype={addNodes:function(e){this.inflight+=e.length;for(var t,n=0,r=e.length;n<r&&(t=e[n]);n++)this.require(t);this.checkDone()},addNode:function(e){this.inflight++,this.require(e),this.checkDone()},require:function(e){var t=e.src||e.href;e.__nodeUrl=t,this.dedupe(t,e)||this.fetch(t,e)},dedupe:function(e,t){if(this.pending[e])return this.pending[e].push(t),!0;return this.cache[e]?(this.onload(e,t,this.cache[e]),this.tail(),!0):(this.pending[e]=[t],!1)},fetch:function(e,r){if(n.load&&console.log("fetch",e,r),e)if(e.match(/^data:/)){var o=e.split(","),i=o[0],a=o[1];a=i.indexOf(";base64")>-1?atob(a):decodeURIComponent(a),setTimeout(function(){this.receive(e,r,null,a)}.bind(this),0)}else{var s=function(t,n,o){this.receive(e,r,t,n,o)}.bind(this);t.load(e,s)}else setTimeout(function(){this.receive(e,r,{error:"href must be specified"},null)}.bind(this),0)},receive:function(e,t,n,r,o){this.cache[e]=r;for(var i,a=this.pending[e],s=0,c=a.length;s<c&&(i=a[s]);s++)this.onload(e,i,r,n,o),this.tail();this.pending[e]=null},tail:function(){--this.inflight,this.checkDone()},checkDone:function(){this.inflight||this.oncomplete()}},e.Loader=r}),window.HTMLImports.addModule(function(e){var t=function(e){this.addCallback=e,this.mo=new MutationObserver(this.handler.bind(this))};t.prototype={handler:function(e){for(var t,n=0,r=e.length;n<r&&(t=e[n]);n++)"childList"===t.type&&t.addedNodes.length&&this.addedNodes(t.addedNodes)},addedNodes:function(e){this.addCallback&&this.addCallback(e);for(var t,n=0,r=e.length;n<r&&(t=e[n]);n++)t.children&&t.children.length&&this.addedNodes(t.children)},observe:function(e){this.mo.observe(e,{childList:!0,subtree:!0})}},e.Observer=t}),window.HTMLImports.addModule(function(e){function t(e){return"link"===e.localName&&e.rel===u}function n(e){var t=r(e);return"data:text/javascript;charset=utf-8,"+encodeURIComponent(t)}function r(e){return e.textContent+o(e)}function o(e){var t=e.ownerDocument;t.__importedScripts=t.__importedScripts||0;var n=e.ownerDocument.baseURI,r=t.__importedScripts?"-"+t.__importedScripts:"";return t.__importedScripts++,"\n//# sourceURL="+n+r+".js\n"}function i(e){var t=e.ownerDocument.createElement("style");return t.textContent=e.textContent,a.resolveUrlsInStyle(t),t}var a=e.path,s=e.rootDocument,c=e.flags,l=e.isIE,u=e.IMPORT_LINK_TYPE,d="link[rel="+u+"]",p={documentSelectors:d,importsSelectors:[d,"link[rel=stylesheet]:not([type])","style:not([type])","script:not([type])",'script[type="application/javascript"]','script[type="text/javascript"]'].join(","),map:{link:"parseLink",script:"parseScript",style:"parseStyle"},dynamicElements:[],parseNext:function(){var e=this.nextToParse();e&&this.parse(e)},parse:function(e){if(this.isParsed(e))return void(c.parse&&console.log("[%s] is already parsed",e.localName));var t=this[this.map[e.localName]];t&&(this.markParsing(e),t.call(this,e))},parseDynamic:function(e,t){this.dynamicElements.push(e),t||this.parseNext()},markParsing:function(e){c.parse&&console.log("parsing",e),this.parsingElement=e},markParsingComplete:function(e){e.__importParsed=!0,this.markDynamicParsingComplete(e),e.__importElement&&(e.__importElement.__importParsed=!0,this.markDynamicParsingComplete(e.__importElement)),this.parsingElement=null,c.parse&&console.log("completed",e)},markDynamicParsingComplete:function(e){var t=this.dynamicElements.indexOf(e);t>=0&&this.dynamicElements.splice(t,1)},parseImport:function(e){if(e["import"]=e.__doc,window.HTMLImports.__importsParsingHook&&window.HTMLImports.__importsParsingHook(e),e["import"]&&(e["import"].__importParsed=!0),this.markParsingComplete(e),e.__resource&&!e.__error?e.dispatchEvent(new CustomEvent("load",{bubbles:!1})):e.dispatchEvent(new CustomEvent("error",{bubbles:!1})),e.__pending)for(var t;e.__pending.length;)t=e.__pending.shift(),t&&t({target:e});this.parseNext()},parseLink:function(e){t(e)?this.parseImport(e):(e.href=e.href,this.parseGeneric(e))},parseStyle:function(e){var t=e;e=i(e),t.__appliedElement=e,e.__importElement=t,this.parseGeneric(e)},parseGeneric:function(e){this.trackElement(e),this.addElementToDocument(e)},rootImportForElement:function(e){for(var t=e;t.ownerDocument.__importLink;)t=t.ownerDocument.__importLink;return t},addElementToDocument:function(e){var t=this.rootImportForElement(e.__importElement||e);t.parentNode.insertBefore(e,t)},trackElement:function(e,t){var n=this,r=function(o){e.removeEventListener("load",r),e.removeEventListener("error",r),t&&t(o),n.markParsingComplete(e),n.parseNext()};if(e.addEventListener("load",r),e.addEventListener("error",r),l&&"style"===e.localName){var o=!1;if(e.textContent.indexOf("@import")==-1)o=!0;else if(e.sheet){o=!0;for(var i,a=e.sheet.cssRules,s=a?a.length:0,c=0;c<s&&(i=a[c]);c++)i.type===CSSRule.IMPORT_RULE&&(o=o&&Boolean(i.styleSheet))}o&&setTimeout(function(){e.dispatchEvent(new CustomEvent("load",{bubbles:!1}))})}},parseScript:function(t){var r=document.createElement("script");r.__importElement=t,r.src=t.src?t.src:n(t),e.currentScript=t,this.trackElement(r,function(t){r.parentNode&&r.parentNode.removeChild(r),e.currentScript=null}),this.addElementToDocument(r)},nextToParse:function(){return this._mayParse=[],!this.parsingElement&&(this.nextToParseInDoc(s)||this.nextToParseDynamic())},nextToParseInDoc:function(e,n){if(e&&this._mayParse.indexOf(e)<0){this._mayParse.push(e);for(var r,o=e.querySelectorAll(this.parseSelectorsForNode(e)),i=0,a=o.length;i<a&&(r=o[i]);i++)if(!this.isParsed(r))return this.hasResource(r)?t(r)?this.nextToParseInDoc(r.__doc,r):r:void 0}return n},nextToParseDynamic:function(){return this.dynamicElements[0]},parseSelectorsForNode:function(e){var t=e.ownerDocument||e;return t===s?this.documentSelectors:this.importsSelectors},isParsed:function(e){return e.__importParsed},needsDynamicParsing:function(e){return this.dynamicElements.indexOf(e)>=0},hasResource:function(e){return!t(e)||void 0!==e.__doc}};e.parser=p,e.IMPORT_SELECTOR=d}),window.HTMLImports.addModule(function(e){function t(e){return n(e,a)}function n(e,t){return"link"===e.localName&&e.getAttribute("rel")===t}function r(e){return!!Object.getOwnPropertyDescriptor(e,"baseURI")}function o(e,t){var n=document.implementation.createHTMLDocument(a);n._URL=t;var o=n.createElement("base");o.setAttribute("href",t),n.baseURI||r(n)||Object.defineProperty(n,"baseURI",{value:t});var i=n.createElement("meta");return i.setAttribute("charset","utf-8"),n.head.appendChild(i),n.head.appendChild(o),n.body.innerHTML=e,window.HTMLTemplateElement&&HTMLTemplateElement.bootstrap&&HTMLTemplateElement.bootstrap(n),n}var i=e.flags,a=e.IMPORT_LINK_TYPE,s=e.IMPORT_SELECTOR,c=e.rootDocument,l=e.Loader,u=e.Observer,d=e.parser,p={documents:{},documentPreloadSelectors:s,importsPreloadSelectors:[s].join(","),loadNode:function(e){h.addNode(e)},loadSubtree:function(e){var t=this.marshalNodes(e);h.addNodes(t)},marshalNodes:function(e){return e.querySelectorAll(this.loadSelectorsForNode(e))},loadSelectorsForNode:function(e){var t=e.ownerDocument||e;return t===c?this.documentPreloadSelectors:this.importsPreloadSelectors},loaded:function(e,n,r,a,s){if(i.load&&console.log("loaded",e,n),n.__resource=r,n.__error=a,t(n)){var c=this.documents[e];void 0===c&&(c=a?null:o(r,s||e),c&&(c.__importLink=n,this.bootDocument(c)),this.documents[e]=c),n.__doc=c}d.parseNext()},bootDocument:function(e){this.loadSubtree(e),this.observer.observe(e),d.parseNext()},loadedAll:function(){d.parseNext()}},h=new l(p.loaded.bind(p),p.loadedAll.bind(p));if(p.observer=new u,!document.baseURI){var f={get:function(){var e=document.querySelector("base");return e?e.href:window.location.href},configurable:!0};Object.defineProperty(document,"baseURI",f),Object.defineProperty(c,"baseURI",f)}e.importer=p,e.importLoader=h}),window.HTMLImports.addModule(function(e){var t=e.parser,n=e.importer,r={added:function(e){for(var r,o,i,a,s=0,c=e.length;s<c&&(a=e[s]);s++)r||(r=a.ownerDocument,o=t.isParsed(r)),i=this.shouldLoadNode(a),i&&n.loadNode(a),this.shouldParseNode(a)&&o&&t.parseDynamic(a,i)},shouldLoadNode:function(e){return 1===e.nodeType&&o.call(e,n.loadSelectorsForNode(e))},shouldParseNode:function(e){return 1===e.nodeType&&o.call(e,t.parseSelectorsForNode(e))}};n.observer.addCallback=r.added.bind(r);var o=HTMLElement.prototype.matches||HTMLElement.prototype.matchesSelector||HTMLElement.prototype.webkitMatchesSelector||HTMLElement.prototype.mozMatchesSelector||HTMLElement.prototype.msMatchesSelector}),function(e){function t(){window.HTMLImports.importer.bootDocument(r)}var n=e.initializeModules;e.isIE;if(!e.useNative){n();var r=e.rootDocument;"complete"===document.readyState||"interactive"===document.readyState&&!window.attachEvent?t():document.addEventListener("DOMContentLoaded",t)}}(window.HTMLImports),window.CustomElements=window.CustomElements||{flags:{}},function(e){var t=e.flags,n=[],r=function(e){n.push(e)},o=function(){n.forEach(function(t){t(e)})};e.addModule=r,e.initializeModules=o,e.hasNative=Boolean(document.registerElement),e.isIE=/Trident/.test(navigator.userAgent),e.useNative=!t.register&&e.hasNative&&!window.ShadowDOMPolyfill&&(!window.HTMLImports||window.HTMLImports.useNative)}(window.CustomElements),window.CustomElements.addModule(function(e){function t(e,t){n(e,function(e){return!!t(e)||void r(e,t)}),r(e,t)}function n(e,t,r){var o=e.firstElementChild;if(!o)for(o=e.firstChild;o&&o.nodeType!==Node.ELEMENT_NODE;)o=o.nextSibling;for(;o;)t(o,r)!==!0&&n(o,t,r),o=o.nextElementSibling;return null}function r(e,n){for(var r=e.shadowRoot;r;)t(r,n),r=r.olderShadowRoot}function o(e,t){i(e,t,[])}function i(e,t,n){if(e=window.wrap(e),!(n.indexOf(e)>=0)){n.push(e);for(var r,o=e.querySelectorAll("link[rel="+a+"]"),s=0,c=o.length;s<c&&(r=o[s]);s++)r["import"]&&i(r["import"],t,n);t(e)}}var a=window.HTMLImports?window.HTMLImports.IMPORT_LINK_TYPE:"none";e.forDocumentTree=o,e.forSubtree=t}),window.CustomElements.addModule(function(e){function t(e,t){return n(e,t)||r(e,t)}function n(t,n){return!!e.upgrade(t,n)||void(n&&a(t))}function r(e,t){b(e,function(e){if(n(e,t))return!0})}function o(e){S.push(e),_||(_=!0,setTimeout(i))}function i(){_=!1;for(var e,t=S,n=0,r=t.length;n<r&&(e=t[n]);n++)e();S=[]}function a(e){E?o(function(){s(e)}):s(e)}function s(e){e.__upgraded__&&!e.__attached&&(e.__attached=!0,e.attachedCallback&&e.attachedCallback())}function c(e){l(e),b(e,function(e){l(e)})}function l(e){E?o(function(){u(e)}):u(e)}function u(e){e.__upgraded__&&e.__attached&&(e.__attached=!1,e.detachedCallback&&e.detachedCallback())}function d(e){for(var t=e,n=window.wrap(document);t;){if(t==n)return!0;t=t.parentNode||t.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&t.host}}function p(e){if(e.shadowRoot&&!e.shadowRoot.__watched){g.dom&&console.log("watching shadow-root for: ",e.localName);for(var t=e.shadowRoot;t;)m(t),t=t.olderShadowRoot}}function h(e,n){if(g.dom){var r=n[0];if(r&&"childList"===r.type&&r.addedNodes&&r.addedNodes){for(var o=r.addedNodes[0];o&&o!==document&&!o.host;)o=o.parentNode;var i=o&&(o.URL||o._URL||o.host&&o.host.localName)||"";i=i.split("/?").shift().split("/").pop()}console.group("mutations (%d) [%s]",n.length,i||"")}var a=d(e);n.forEach(function(e){"childList"===e.type&&(T(e.addedNodes,function(e){e.localName&&t(e,a)}),T(e.removedNodes,function(e){e.localName&&c(e)}))}),g.dom&&console.groupEnd()}function f(e){for(e=window.wrap(e),e||(e=window.wrap(document));e.parentNode;)e=e.parentNode;var t=e.__observer;t&&(h(e,t.takeRecords()),i())}function m(e){if(!e.__observer){var t=new MutationObserver(h.bind(this,e));t.observe(e,{childList:!0,subtree:!0}),e.__observer=t}}function w(e){e=window.wrap(e),g.dom&&console.group("upgradeDocument: ",e.baseURI.split("/").pop());var n=e===window.wrap(document);t(e,n),m(e),g.dom&&console.groupEnd()}function v(e){y(e,w)}var g=e.flags,b=e.forSubtree,y=e.forDocumentTree,E=window.MutationObserver._isPolyfilled&&g["throttle-attached"];e.hasPolyfillMutations=E,e.hasThrottledAttached=E;var _=!1,S=[],T=Array.prototype.forEach.call.bind(Array.prototype.forEach),M=Element.prototype.createShadowRoot;M&&(Element.prototype.createShadowRoot=function(){var e=M.call(this);return window.CustomElements.watchShadow(this),e}),e.watchShadow=p,e.upgradeDocumentTree=v,e.upgradeDocument=w,e.upgradeSubtree=r,e.upgradeAll=t,e.attached=a,e.takeRecords=f}),window.CustomElements.addModule(function(e){function t(t,r){if("template"===t.localName&&window.HTMLTemplateElement&&HTMLTemplateElement.decorate&&HTMLTemplateElement.decorate(t),!t.__upgraded__&&t.nodeType===Node.ELEMENT_NODE){var o=t.getAttribute("is"),i=e.getRegisteredDefinition(t.localName)||e.getRegisteredDefinition(o);if(i&&(o&&i.tag==t.localName||!o&&!i["extends"]))return n(t,i,r)}}function n(t,n,o){return a.upgrade&&console.group("upgrade:",t.localName),n.is&&t.setAttribute("is",n.is),r(t,n),t.__upgraded__=!0,i(t),o&&e.attached(t),e.upgradeSubtree(t,o),a.upgrade&&console.groupEnd(),t}function r(e,t){Object.__proto__?e.__proto__=t.prototype:(o(e,t.prototype,t["native"]),e.__proto__=t.prototype)}function o(e,t,n){for(var r={},o=t;o!==n&&o!==HTMLElement.prototype;){for(var i,a=Object.getOwnPropertyNames(o),s=0;i=a[s];s++)r[i]||(Object.defineProperty(e,i,Object.getOwnPropertyDescriptor(o,i)),r[i]=1);o=Object.getPrototypeOf(o)}}function i(e){e.createdCallback&&e.createdCallback()}var a=e.flags;e.upgrade=t,e.upgradeWithDefinition=n,e.implementPrototype=r}),window.CustomElements.addModule(function(e){function t(t,r){var c=r||{};if(!t)throw new Error("document.registerElement: first argument `name` must not be empty");if(t.indexOf("-")<0)throw new Error("document.registerElement: first argument ('name') must contain a dash ('-'). Argument provided was '"+String(t)+"'.");if(o(t))throw new Error("Failed to execute 'registerElement' on 'Document': Registration failed for type '"+String(t)+"'. The type name is invalid.");if(l(t))throw new Error("DuplicateDefinitionError: a type with name '"+String(t)+"' is already registered");return c.prototype||(c.prototype=Object.create(HTMLElement.prototype)),c.__name=t.toLowerCase(),c["extends"]&&(c["extends"]=c["extends"].toLowerCase()),c.lifecycle=c.lifecycle||{},c.ancestry=i(c["extends"]),a(c),s(c),n(c.prototype),u(c.__name,c),c.ctor=d(c),c.ctor.prototype=c.prototype,c.prototype.constructor=c.ctor,e.ready&&w(document),c.ctor}function n(e){if(!e.setAttribute._polyfilled){var t=e.setAttribute;e.setAttribute=function(e,n){r.call(this,e,n,t)};var n=e.removeAttribute;e.removeAttribute=function(e){r.call(this,e,null,n)},e.setAttribute._polyfilled=!0}}function r(e,t,n){e=e.toLowerCase();var r=this.getAttribute(e);n.apply(this,arguments);var o=this.getAttribute(e);this.attributeChangedCallback&&o!==r&&this.attributeChangedCallback(e,r,o)}function o(e){for(var t=0;t<E.length;t++)if(e===E[t])return!0}function i(e){var t=l(e);return t?i(t["extends"]).concat([t]):[]}function a(e){for(var t,n=e["extends"],r=0;t=e.ancestry[r];r++)n=t.is&&t.tag;e.tag=n||e.__name,n&&(e.is=e.__name)}function s(e){if(!Object.__proto__){var t=HTMLElement.prototype;if(e.is){var n=document.createElement(e.tag);t=Object.getPrototypeOf(n)}for(var r,o=e.prototype,i=!1;o;)o==t&&(i=!0),r=Object.getPrototypeOf(o),r&&(o.__proto__=r),o=r;i||console.warn(e.tag+" prototype not found in prototype chain for "+e.is),e["native"]=t}}function c(e){return g(T(e.tag),e)}function l(e){if(e)return _[e.toLowerCase()]}function u(e,t){_[e]=t}function d(e){return function(){return c(e)}}function p(e,t,n){return e===S?h(t,n):M(e,t)}function h(e,t){e&&(e=e.toLowerCase()),t&&(t=t.toLowerCase());var n=l(t||e);if(n){if(e==n.tag&&t==n.is)return new n.ctor;if(!t&&!n.is)return new n.ctor}var r;return t?(r=h(e),r.setAttribute("is",t),r):(r=T(e),e.indexOf("-")>=0&&b(r,HTMLElement),r)}function f(e,t){var n=e[t];e[t]=function(){var e=n.apply(this,arguments);return v(e),e}}var m,w=(e.isIE,e.upgradeDocumentTree),v=e.upgradeAll,g=e.upgradeWithDefinition,b=e.implementPrototype,y=e.useNative,E=["annotation-xml","color-profile","font-face","font-face-src","font-face-uri","font-face-format","font-face-name","missing-glyph"],_={},S="http://www.w3.org/1999/xhtml",T=document.createElement.bind(document),M=document.createElementNS.bind(document);m=Object.__proto__||y?function(e,t){return e instanceof t}:function(e,t){if(e instanceof t)return!0;for(var n=e;n;){if(n===t.prototype)return!0;n=n.__proto__}return!1},f(Node.prototype,"cloneNode"),f(document,"importNode"),document.registerElement=t,document.createElement=h,document.createElementNS=p,e.registry=_,e["instanceof"]=m,e.reservedTagList=E,e.getRegisteredDefinition=l,document.register=document.registerElement}),function(e){function t(){i(window.wrap(document)),window.CustomElements.ready=!0;var e=window.requestAnimationFrame||function(e){setTimeout(e,16)};e(function(){setTimeout(function(){window.CustomElements.readyTime=Date.now(),window.HTMLImports&&(window.CustomElements.elapsed=window.CustomElements.readyTime-window.HTMLImports.readyTime),document.dispatchEvent(new CustomEvent("WebComponentsReady",{bubbles:!0}))})})}var n=e.useNative,r=e.initializeModules;e.isIE;if(n){var o=function(){};e.watchShadow=o,e.upgrade=o,e.upgradeAll=o,e.upgradeDocumentTree=o,e.upgradeSubtree=o,e.takeRecords=o,e["instanceof"]=function(e,t){return e instanceof t}}else r();var i=e.upgradeDocumentTree,a=e.upgradeDocument;if(window.wrap||(window.ShadowDOMPolyfill?(window.wrap=window.ShadowDOMPolyfill.wrapIfNeeded,window.unwrap=window.ShadowDOMPolyfill.unwrapIfNeeded):window.wrap=window.unwrap=function(e){return e}),window.HTMLImports&&(window.HTMLImports.__importsParsingHook=function(e){e["import"]&&a(wrap(e["import"]))}),"complete"===document.readyState||e.flags.eager)t();else if("interactive"!==document.readyState||window.attachEvent||window.HTMLImports&&!window.HTMLImports.ready){var s=window.HTMLImports&&!window.HTMLImports.ready?"HTMLImportsLoaded":"DOMContentLoaded";window.addEventListener(s,t)}else t()}(window.CustomElements),function(e){Function.prototype.bind||(Function.prototype.bind=function(e){var t=this,n=Array.prototype.slice.call(arguments,1);return function(){var r=n.slice();return r.push.apply(r,arguments),t.apply(e,r)}})}(window.WebComponents),function(e){var t=document.createElement("style");t.textContent="body {transition: opacity ease-in 0.2s; } \nbody[unresolved] {opacity: 0; display: block; overflow: hidden; position: relative; } \n";var n=document.querySelector("head");n.insertBefore(t,n.firstChild)}(window.WebComponents),function(e){window.Platform=e}(window.WebComponents);
\ No newline at end of file
diff --git a/runtime/observatory_2/web/timeline.html b/runtime/observatory_2/web/timeline.html
new file mode 100644
index 0000000..4bbc396
--- /dev/null
+++ b/runtime/observatory_2/web/timeline.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html style="height: 100%; width: 100%">
+<head>
+  <meta charset="utf-8">
+  <title>Dart VM Observatory Timeline</title>
+  <script src="timeline_message_handler.js"></script>
+  <script src="third_party/webcomponents.min.js"></script>
+  <link rel="import" href="third_party/trace_viewer_full.html">
+  <script src="timeline.js"></script>
+  <style>
+html, body {
+  box-sizing: border-box;
+  overflow: hidden;
+  margin: 0px;
+  padding: 0;
+  width: 100%;
+  height: 100%;
+}
+#trace-viewer {
+  width: 100%;
+  height: 100%;
+}
+#trace-viewer:focus {
+  outline: none;
+}
+  </style>
+</head>
+<body>
+</body>
+</html>
diff --git a/runtime/observatory_2/web/timeline.js b/runtime/observatory_2/web/timeline.js
new file mode 100644
index 0000000..5b431f6
--- /dev/null
+++ b/runtime/observatory_2/web/timeline.js
@@ -0,0 +1,667 @@
+// Copyright (c) 2015, 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.
+
+// See also timeline_message_handler.js.
+
+var pendingRequests;
+var loadingOverlay;
+
+function onModelLoaded(model) {
+  viewer.globalMode = true;
+  viewer.model = model;
+}
+
+function clearTimeline() {
+  viewer.model = undefined;
+}
+
+function onImportFail(err) {
+  var overlay = new tr.ui.b.Overlay();
+  overlay.textContent = tr.b.normalizeException(err).message;
+  overlay.title = 'Import error';
+  overlay.visible = true;
+  console.log('import failed');
+}
+
+function compareTimestamp(a, b) { return a.ts - b.ts; }
+function compareBeginTimestamp(a, b) { return a.begin.ts - b.begin.ts; }
+function compareEndTimestamp(a, b) { return a.end.ts - b.end.ts; }
+
+var basicModelEventsWaterfall = [
+  // Sort events and remove orphan async ends.
+  function filterUnwantedEvents(events) {
+    events = events.slice();
+    events.sort(compareTimestamp);
+    var threads = {};
+    return events.filter(function (event) {
+      if (event.ph === 'E') {
+        return threads[event.tid] && threads[event.tid].pop();
+      }
+      var result = event.args && event.args.mode === 'basic';
+      if (event.ph === 'B') {
+        threads[event.tid] = threads[event.tid] || [];
+        threads[event.tid].push(result);
+      }
+      return result;
+    });
+  }
+];
+
+var frameModelEventsWaterfall = [
+  // Sort events and remove orphan async ends.
+  function filterUnwantedEvents(events) {
+    events = events.slice();
+    events.sort(compareTimestamp);
+    var threads = {};
+    return events.filter(function (event) {
+      if (event.ph === 'E') {
+        if (!threads[event.tid]) {
+          return false
+        }
+        threads[event.tid] -= 1;
+      } else if (event.ph === 'B') {
+        threads[event.tid] = (threads[event.tid] || 0) + 1;
+      }
+      return true;
+    });
+  },
+  // Clone the events (we want to preserve them for dumps).
+  function cloneDeep(input) {
+    if (typeof input === 'object') {
+      if (Array.isArray(input)) {
+        return input.map(cloneDeep);
+      } else {
+        var clone = {};
+        Object.keys(input).forEach(function (key) {
+          clone[key] = cloneDeep(input[key]);
+        });
+        return clone;
+      }
+    }
+    return input;
+  },
+  // Group nested sync begin end sequences on every thread.
+  //
+  // Example:
+  // Input = [B,B,E,B,E,E,B,E,B,B,E,E]
+  // Output = [[B,B,E,B,E,E],[B,E],[B,B,E,E]]
+  function groupIsolatedPerThreadSequences(events) {
+    var sequences = [],
+      timeless = [],
+      threadOpen = {};
+      threadSequences = {};
+    events.forEach(function (event) {
+      if (event.ph === 'M') {
+        timeless.push(event);
+      } else if (event.ph === 'B') {
+        threadOpen[event.tid] = Math.max(threadOpen[event.tid] || 0) + 1;
+        threadSequences[event.tid] = threadSequences[event.tid] || [];
+        threadSequences[event.tid].push(event);
+      } else if (event.ph === 'E') {
+        threadSequences[event.tid].push(event);
+        threadOpen[event.tid] -= 1;
+        if (threadOpen[event.tid] == 0) {
+          threadSequences[event.tid].sort()
+          sequences.push(threadSequences[event.tid]);
+          threadSequences[event.tid] = [];
+        }
+      } else if (threadSequences[event.tid]){
+        threadSequences[event.tid] = threadSequences[event.tid] || [];
+        threadSequences[event.tid].push(event);
+      }
+    })
+    return {
+      timeless: timeless,
+      sequences: sequences
+    };
+  },
+  // Transform every sequence into an object for rapid begin end analysis and
+  // block types lookup.
+  //
+  // Example:
+  // Input = [B1,B2,E2,B3,E3,E1]
+  // Output = {
+  //   begin: B1,
+  //   end: E1,
+  //   events: [B1,B2,E2,B3,E3,E1],
+  //   isGPU: ...,
+  //   isVSync: ...,
+  //   isFramework: ...,
+  //   isShiftable: ...,
+  // }
+  function sequenceToBlockDescriptor(input) {
+    return {
+      timeless: input.timeless,
+      blocks: input.sequences.map(function (events) {
+        var begin,
+            end,
+            isGPU,
+            isVSync,
+            isFramework;
+        events.forEach(function (event) {
+          if (event.ph === 'B') {
+            begin = begin || event;
+          } else if (event.ph === 'E') {
+            end = event;
+          }
+        });
+        isGPU = begin.name === 'GPU Workload';
+        isVSync = begin.name === 'VSYNC';
+        isFramework = begin.name === 'Framework Workload';
+        return {
+          begin: begin,
+          end: end,
+          events: events,
+          isGPU: isGPU,
+          isVSync: isVSync,
+          isFramework: isFramework,
+          isShiftable: !(isGPU || isVSync || isFramework)
+        };
+      })
+    };
+  },
+  // Remove all the blocks that ended before the first VSYNC.
+  // These events do not give any information to the analysis.
+  function removePreVSyncBlocks(input) {
+    input.blocks.sort(compareEndTimestamp);
+    var sawVSyncBlock = false;
+    return {
+      timeless: input.timeless,
+      blocks: input.blocks.filter(function (block) {
+        sawVSyncBlock = sawVSyncBlock || block.isVSync;
+        return sawVSyncBlock;
+      })
+    };
+  },
+  // Remove all the GPU blocks that started before the first Framework block.
+  // They are orphans of other frames.
+  function removePreFrameworkGPUBlocks(input) {
+    input.blocks.sort(compareBeginTimestamp);
+    var firstFrameworkBlockBeginTimestamp = 0;
+    return {
+      timeless: input.timeless,
+      blocks: input.blocks.filter(function (block) {
+        if (block.isFramework) {
+          firstFrameworkBlockBeginTimestamp =
+              firstFrameworkBlockBeginTimestamp || block.begin.ts;
+        } else if (block.isGPU) {
+          if (!firstFrameworkBlockBeginTimestamp) {
+            return false;
+          } else if (block.begin.ts < firstFrameworkBlockBeginTimestamp) {
+            return false;
+          }
+        }
+        return true;
+      })
+    };
+  },
+  // Merge all shiftable blocks that are between two Framework blocks.
+  // By merging them we preserve their relative timing.
+  function mergeShiftableBlocks(input) {
+    input.blocks.sort(compareEndTimestamp);
+    var begin,
+        end,
+        events = [],
+        shiftableBlocks = [],
+        blocks;
+    blocks = input.blocks.filter(function (block) {
+      if (block.isShiftable) {
+        begin = begin || block.begin;
+        end = block.end;
+        events = events.concat(block.events);
+        return false;
+      } else if (block.isFramework) {
+        if (events.length) {
+          shiftableBlocks.push({
+            begin: begin,
+            end: end,
+            events: events
+          });
+        }
+      }
+      return true;
+    });
+    if (events.length) {
+      shiftableBlocks.push({
+        begin: begin,
+        end: end,
+        events: events
+      });
+    }
+    return {
+      timeless: input.timeless,
+      blocks: blocks.concat(shiftableBlocks)
+    };
+  },
+  // Remove all VSyncs that didn't started an actual frame.
+  function filterFramelessVSyncs(input) {
+    input.blocks.sort(compareBeginTimestamp);
+    var lastVSyncBlock,
+      blocks,
+      vSyncBlocks = [];
+    blocks = input.blocks.filter(function (block) {
+      if (block.isVSync) {
+        lastVSyncBlock = block;
+        return false;
+      } else if (block.isFramework) {
+        vSyncBlocks.push(lastVSyncBlock);
+      }
+      return true;
+    });
+    return {
+      timeless: input.timeless,
+      blocks: blocks.concat(vSyncBlocks)
+    };
+  },
+  // Group blocks by type.
+  //
+  // Example:
+  // Input = [S1, V1, F1, V2, G1, F2, V3, G2, F3]
+  // Output = {
+  //   gpu: [G1, G2],
+  //   vsync: [V1, V2, V3],
+  //   framework: [F1, F2, F3],
+  //   shiftable: [S1]
+  // }
+  function groupBlocksByFrames(input) {
+    return {
+      timeless: input.timeless,
+      gpu: input.blocks.filter(function (b) { return b.isGPU; }),
+      vsync: input.blocks.filter(function (b) { return b.isVSync; }),
+      framework: input.blocks.filter(function (b) { return b.isFramework; }),
+      shiftable: input.blocks.filter(function (b) { return b.isShiftable; })
+    };
+  },
+  // Remove possible out of sync GPU Blocks.
+  // If the buffer has already delete the VSync and the Framework, but not the
+  // GPU it can potentially be still alive.
+  function groupBlocksByFrames(input) {
+    var gpu = input.gpu,
+        framework = input.framework;
+    while (gpu.length &&
+           gpu[0].begin.args.frame !== framework[0].begin.args.frame) {
+      gpu.shift();
+    }
+    return input;
+  },
+  // Group blocks related to the same frame.
+  // Input = {
+  //   gpu: [G1, G2],
+  //   vsync: [V1, V2],
+  //   framework: [F1, F2],
+  //   shiftable: [S1]
+  // }
+  // Output = [{V1, F1, G1, S1}, {V2, F2, G2}, {V3, F3, G3}]
+  function groupBlocksByFrames(input) {
+    var shiftable = input.shiftable.slice();
+    return {
+      timeless: input.timeless,
+      frames: input.vsync.map(function (vsync, i) {
+        var frame = {
+          begin: vsync.begin,
+          vsync: vsync,
+          framework: input.framework[i],
+          deadline: parseInt(vsync.begin.args.deadline) + 1000
+        };
+        if (i < input.gpu.length) {
+          frame.gpu = input.gpu[i];
+        }
+        if (shiftable.length && shiftable[0].begin.ts < framework.begin.ts ) {
+          frame.shiftable = shiftable.shift();
+        }
+        return frame;
+      })
+    };
+  },
+  // Move Framework and GPU as back in time as possible
+  //
+  // Example:
+  // Before
+  //                               [GPU]
+  //     [VSYNC]
+  //  [SHIFTABLE]     [FRAMEWORK]
+  // After
+  //             |[GPU]
+  //     [VSYNC] |
+  //  [SHIFTABLE]|[FRAMEWORK]
+  function shiftEvents(input) {
+    input.frames.forEach(function (frame) {
+      var earlierTimestamp = frame.vsync.end.ts,
+          shift;
+      if (frame.shiftable) {
+        frame.shiftable.events.forEach(function (event) {
+          if (event.tid === frame.framework.begin.tid) {
+            earlierTimestamp = Math.max(earlierTimestamp, event.ts);
+          }
+        });
+      }
+      if (frame.gpu) {
+        if (frame.shiftable) {
+          frame.shiftable.events.forEach(function (event) {
+            if (event.tid === frame.gpu.begin.tid) {
+              earlierTimestamp = Math.max(earlierTimestamp, event.ts);
+            }
+          });
+        }
+        shift = earlierTimestamp - frame.gpu.begin.ts;
+        frame.gpu.events.forEach(function (event) {
+          event.ts += shift;
+        });
+      }
+      shift = earlierTimestamp - frame.framework.begin.ts;
+      frame.framework.events.forEach(function (event) {
+        event.ts += shift;
+      });
+      frame.end = frame.framework.end;
+      if (frame.gpu && frame.framework.end.ts < frame.gpu.end.ts) {
+        frame.end = frame.gpu.end;
+      }
+    });
+    return input;
+  },
+  // Group events in frame (precomputation for next stage).
+  function groupEventsInFrame(input) {
+    input.frames.forEach(function (frame) {
+      var events = frame.vsync.events;
+      events = events.concat(frame.framework.events);
+      if (frame.gpu) {
+        events = events.concat(frame.gpu.events);
+      }
+      if (frame.shiftable) {
+        events = events.concat(frame.shiftable.events);
+      }
+      events.sort(compareTimestamp);
+      frame.events = events;
+    });
+    return input;
+  },
+  // Move frames in order to do not overlap.
+  //
+  // Example:
+  // Before
+  //              |[GPU1--------------------]
+  //                                     |[GPU2----]
+  //     [VSYNC1] |             [VSYNC2] |
+  //  [SHIFTABLE1]|[FRAMEWORK1]          |[FRAMEWORK2]
+  // After
+  //              |[GPU1--------------------]|         |[GPU2-----]
+  //     [VSYNC1] |                          |[VSYNC2] |
+  //  [SHIFTABLE1]|[FRAMEWORK1]              |         |[FRAMEWORK2]
+  // OtherExample:
+  // Before
+  //     {FRAME BUDGET1-------------------------}
+  //                            {FRAME BUDGET2-------------------------}
+  //              |[GPU1]
+  //                                     |[GPU2]
+  //     [VSYNC1] |             [VSYNC2] |
+  //  [SHIFTABLE1]|[FRAMEWORK1]          |[FRAMEWORK2]
+  // After
+  //     {FRAME BUDGET1-------------------------}|{FRAME BUDGET2----------------
+  //              |[GPU1]                        |         |[GPU2-----]
+  //     [VSYNC1] |                              |[VSYNC2] |
+  //  [SHIFTABLE1]|[FRAMEWORK1]                  |         |[FRAMEWORK2]
+  function shiftBlocks(input) {
+    function minThreadtimestamps(frame) {
+      var timestamps = {};
+      frame.events.forEach(function (event) {
+        if (event.tid != undefined) {
+          timestamps[event.tid] = timestamps[event.tid] || event.ts;
+        }
+      });
+      return timestamps;
+    }
+    function maxThreadTimestamps(frame) {
+      var timestamps = {};
+      frame.events.forEach(function (event) {
+        if (event.tid != undefined) {
+          timestamps[event.tid] = event.ts;
+        }
+      });
+      return timestamps;
+    }
+    input.frames.slice(1).forEach(function (current, index) {
+      var previous = input.frames[index],
+        shift = Math.max(previous.end.ts, previous.deadline) - current.begin.ts,
+        maxThreadTimestamp = maxThreadTimestamps(previous),
+        minThreadTimestamp = minThreadtimestamps(current);
+      Object.keys(maxThreadTimestamp).forEach(function (tid) {
+        if (minThreadTimestamp[tid]) {
+          var delta = maxThreadTimestamp[tid] - minThreadTimestamp[tid];
+          shift = Math.max(shift, delta);
+        }
+      });
+      current.events.forEach(function (event) {
+        event.ts += shift;
+      });
+      current.deadline += shift;
+    });
+    return input;
+  },
+  // Add auxilary events to frame (Frame Budget and Frame Length).
+  // Example:
+  // Before
+  //              |[GPU1--------------------]|         |[GPU2-----]
+  //     [VSYNC1] |                          |[VSYNC2] |
+  //  [SHIFTABLE1]|[FRAMEWORK1]              |         |[FRAMEWORK2]
+  // After
+  //     [Budget1---------------------------]|[Budget2--------------------------
+  //     [Length1---------------------------]|[Length2------------]
+  //              |[GPU1--------------------]|         |[GPU2-----]
+  //     [VSYNC1] |                          |[VSYNC2] |
+  //  [SHIFTABLE1]|[FRAMEWORK1]              |         |[FRAMEWORK2]
+  function addAuxilaryEvents(input) {
+    input.frames.forEach(function (frame) {
+      frame.events.unshift({
+        args: {name: "Frame Budgets"},
+        name: "thread_name",
+        ph: "M",
+        pid: frame.begin.pid,
+        tid: "budgets",
+      });
+      frame.events.unshift({
+        args: {name: "Frames"},
+        name: "thread_name",
+        ph: "M",
+        pid: frame.begin.pid,
+        tid: "frames",
+      });
+      var duration = Math.floor((frame.end.ts - frame.begin.ts) / 1000),
+          frameName = "Frame " + duration + "ms";
+      frame.events = frame.events.concat({
+          ph: "B",
+          name: "Frame Budget",
+          cat: "budgets",
+          pid: frame.begin.pid,
+          tid: "budgets",
+          ts: frame.begin.ts
+        }, {
+          ph: "E",
+          name: "Frame Budget",
+          cat: "budgets",
+          pid: frame.begin.pid,
+          tid: "budgets",
+          ts: frame.deadline,
+          cname: 'rail_response'
+        }, {
+          ph: "B",
+          name: frameName,
+          cat: "frames",
+          pid: frame.begin.pid,
+          tid: "frames",
+          ts: frame.begin.ts
+        }, {
+          ph: "E",
+          name: frameName,
+          cat: "frames",
+          pid: frame.begin.pid,
+          tid: "frames",
+          ts: frame.end.ts,
+          cname: frame.end.ts > frame.deadline ? 'terrible' : 'good'
+        });
+    });
+    return input;
+  },
+  // Restore the events array used by catapult.
+  function linearizeBlocks(input) {
+    return input.frames.reduce(function (events, frame) {
+      return events.concat(frame.events);
+    }, input.timeless);
+  }
+];
+
+function basicModelEventsMap(events) {
+  return basicModelEventsWaterfall.reduce(function (input, step) {
+    return step(input);
+  }, events);
+}
+
+function frameModelEventsMap(events) {
+  return frameModelEventsWaterfall.reduce(function (input, step) {
+    return step(input);
+  }, events);
+}
+
+function updateTimeline(events) {
+  if (window.location.hash.indexOf('mode=basic') > -1) {
+    events = {
+      'stackFrames': events['stackFrames'],
+      'traceEvents': basicModelEventsMap(events['traceEvents'])
+    };
+  }
+  if (window.location.hash.indexOf('view=frame') > -1) {
+    events = {
+      'stackFrames': events['stackFrames'],
+      'traceEvents': frameModelEventsMap(events['traceEvents'])
+    };
+  }
+  var model = new tr.Model();
+  var importer = new tr.importer.Import(model);
+  var p = importer.importTracesWithProgressDialog([events]);
+  p.then(onModelLoaded.bind(undefined, model), onImportFail);
+}
+
+function showLoadingOverlay(msg) {
+  if (!loadingOverlay) {
+    loadingOverlay = new tr.ui.b.Overlay();
+  }
+  loadingOverlay.textContent = msg;
+  loadingOverlay.title = 'Loading...';
+  loadingOverlay.visible = true;
+}
+
+function hideLoadingOverlay() {
+  if (!loadingOverlay) {
+    return;
+  }
+  loadingOverlay.visible = false;
+  loadingOverlay = undefined;
+}
+
+function populateTimeline() {
+  updateTimeline(traceObject);
+  hideLoadingOverlay();
+}
+
+function saveTimeline() {
+  if (pendingRequests > 0) {
+    var overlay = new tr.ui.b.Overlay();
+    overlay.textContent = 'Cannot save timeline while fetching one.';
+    overlay.title = 'Save error';
+    overlay.visible = true;
+    console.log('cannot save timeline while fetching one.');
+    return;
+  }
+  if (!traceObject ||
+      !traceObject.traceEvents ||
+      (traceObject.traceEvents.length === 0)) {
+    var overlay = new tr.ui.b.Overlay();
+    overlay.textContent = 'Cannot save an empty timeline.';
+    overlay.title = 'Save error';
+    overlay.visible = true;
+    console.log('Cannot save an empty timeline.');
+    return;
+  }
+  var blob = new Blob([JSON.stringify(traceObject)],
+                      {type: 'application/json'});
+  var blobUrl = URL.createObjectURL(blob);
+  var link = document.createElementNS('http://www.w3.org/1999/xhtml', 'a');
+  link.href = blobUrl;
+  var now = new Date();
+  var defaultFilename = 'dart-timeline-' +
+                        now.getFullYear() +
+                        '-' +
+                        (now.getMonth() + 1) +
+                        '-' +
+                        now.getDate() +
+                        '.json';
+  var filename = window.prompt('Save as', defaultFilename);
+  if (filename) {
+    link.download = filename;
+    link.click();
+  }
+}
+
+function loadTimeline() {
+  if (pendingRequests > 0) {
+    var overlay = new tr.ui.b.Overlay();
+    overlay.textContent = 'Cannot load timeline while fetching one.';
+    overlay.title = 'Save error';
+    overlay.visible = true;
+    console.log('Cannot load timeline while fetching one.');
+    return;
+  }
+  var inputElement = document.createElement('input');
+  inputElement.type = 'file';
+  inputElement.multiple = false;
+
+  var changeFired = false;
+  inputElement.addEventListener('change', function(e) {
+    if (changeFired)
+      return;
+    changeFired = true;
+
+    var file = inputElement.files[0];
+    var reader = new FileReader();
+    reader.onload = function(event) {
+      try {
+        traceObject = JSON.parse(event.target.result);
+        updateTimeline(traceObject);
+      } catch (error) {
+        tr.ui.b.Overlay.showError('Error while loading file: ' + error);
+      }
+    };
+    reader.onerror = function(event) {
+      tr.ui.b.Overlay.showError('Error while loading file: ' + event);
+    };
+    reader.onabort = function(event) {
+      tr.ui.b.Overlay.showError('Error while loading file: ' + event);
+    }
+    reader.readAsText(file);
+  });
+  inputElement.click();
+}
+
+function refreshTimeline() {
+  updateTimeline(traceObject);
+}
+
+window.addEventListener('DOMContentLoaded', function() {
+  var container = document.createElement('track-view-container');
+  container.id = 'track_view_container';
+  viewer = document.createElement('tr-ui-timeline-view');
+  viewer.track_view_container = container;
+  viewer.appendChild(container);
+  viewer.id = 'trace-viewer';
+  viewer.globalMode = true;
+  document.body.appendChild(viewer);
+  timeline_loaded = true;
+  console.log('DOMContentLoaded');
+  document.getElementById('trace-viewer').highlightVSync = true;
+  if (traceObject != undefined) {
+    refreshTimeline();
+    traceObject = undefined;
+  }
+});
+
+console.log('timeline.js loaded');
diff --git a/runtime/observatory_2/web/timeline_message_handler.js b/runtime/observatory_2/web/timeline_message_handler.js
new file mode 100644
index 0000000..4b3f11a
--- /dev/null
+++ b/runtime/observatory_2/web/timeline_message_handler.js
@@ -0,0 +1,52 @@
+// Copyright (c) 2016, 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.
+
+// This file is loaded before the about:tracing code is loaded so that we have
+// an event listener registered early.
+
+var traceObject;
+
+function registerForMessages() {
+  window.addEventListener("message", onMessage, false);
+  window.addEventListener("hashchange", onHashChange, false);
+}
+
+registerForMessages();
+
+function onMessage(event) {
+  var request = JSON.parse(event.data);
+  var method = request['method'];
+  var params = request['params'];
+  console.log('method: ' + method)
+  switch (method) {
+    case 'loading':
+      showLoadingOverlay('Fetching timeline...');
+    break;
+    case 'refresh':
+      traceObject = params;
+      if (typeof populateTimeline != 'undefined') {
+        populateTimeline();
+      } else {
+        console.log('populateTimeline is not yet defined');
+      }
+    break;
+    case 'clear':
+      clearTimeline();
+    break;
+    case 'save':
+      saveTimeline();
+    break;
+    case 'load':
+      loadTimeline();
+    break;
+    default:
+      console.log('Unknown method:' + method + '.');
+  }
+}
+
+function onHashChange() {
+  refreshTimeline();
+}
+
+console.log('message handler registered');
diff --git a/runtime/tests/vm/dart/regress_big_regexp_test.dart b/runtime/tests/vm/dart/regress_big_regexp_test.dart
new file mode 100644
index 0000000..09a7aa4
--- /dev/null
+++ b/runtime/tests/vm/dart/regress_big_regexp_test.dart
@@ -0,0 +1,23 @@
+// Copyright (c) 2020, 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.
+
+// Verifies that RegExp compilation doesn't crash on a huge source string.
+
+import 'package:expect/expect.dart';
+
+void testBigRegExp(String source) {
+  try {
+    var re = new RegExp(source);
+    Expect.isTrue(re.hasMatch(source));
+  } catch (e) {
+    // May throw a compile-time error, but shouldn't crash.
+    Expect.isTrue(e.toString().contains('RegExp too big'));
+  }
+}
+
+main() {
+  testBigRegExp("a" * (0x10000 - 128));
+  testBigRegExp(
+      String.fromCharCodes(List.generate(0x10000 - 128, (x) => x + 128)));
+}
diff --git a/runtime/tests/vm/dart_2/regress_big_regexp_test.dart b/runtime/tests/vm/dart_2/regress_big_regexp_test.dart
new file mode 100644
index 0000000..13e2689
--- /dev/null
+++ b/runtime/tests/vm/dart_2/regress_big_regexp_test.dart
@@ -0,0 +1,23 @@
+// Copyright (c) 2020, 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.
+
+// Verifies that RegExp compilation doesn't crash on a huge source string.
+
+import 'package:expect/expect.dart';
+
+void testBigRegExp(String source) {
+  try {
+    var re = new RegExp(source);
+    Expect.isTrue(re.hasMatch(source));
+  } catch (e) {
+    // May throw an error containing
+    Expect.isTrue(e.toString().contains('RegExp too big'));
+  }
+}
+
+main() {
+  testBigRegExp("a" * (0x10000 - 128));
+  testBigRegExp(
+      String.fromCharCodes(List.generate(0x10000 - 128, (x) => x + 128)));
+}
diff --git a/runtime/vm/compiler/jit/compiler.cc b/runtime/vm/compiler/jit/compiler.cc
index 1139cdc..72e16f6 100644
--- a/runtime/vm/compiler/jit/compiler.cc
+++ b/runtime/vm/compiler/jit/compiler.cc
@@ -179,6 +179,10 @@
   RegExpEngine::CompilationResult result =
       RegExpEngine::CompileIR(parsed_function->regexp_compile_data(),
                               parsed_function, *ic_data_array, osr_id);
+  if (result.error_message != nullptr) {
+    Report::LongJump(LanguageError::Handle(
+        LanguageError::New(String::Handle(String::New(result.error_message)))));
+  }
   backtrack_goto_ = result.backtrack_goto;
 
   // Allocate variables now that we know the number of locals.
diff --git a/runtime/vm/regexp_assembler_bytecode.cc b/runtime/vm/regexp_assembler_bytecode.cc
index a7e2ff3..6db75e3 100644
--- a/runtime/vm/regexp_assembler_bytecode.cc
+++ b/runtime/vm/regexp_assembler_bytecode.cc
@@ -452,6 +452,9 @@
 
     RegExpEngine::CompilationResult result = RegExpEngine::CompileBytecode(
         compile_data, regexp, is_one_byte, sticky, zone);
+    if (result.error_message != nullptr) {
+      Exceptions::ThrowUnsupportedError(result.error_message);
+    }
     ASSERT(result.bytecode != NULL);
     ASSERT(regexp.num_registers(is_one_byte) == -1 ||
            regexp.num_registers(is_one_byte) == result.num_registers);
diff --git a/tools/VERSION b/tools/VERSION
index 245fc79..a308234 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 11
 PATCH 0
-PRERELEASE 198
+PRERELEASE 199
 PRERELEASE_PATCH 0
\ No newline at end of file