Some tools to dump other trees. (#9999)

diff --git a/packages/flutter/lib/src/rendering/binding.dart b/packages/flutter/lib/src/rendering/binding.dart
index a3fb394..bfeef09 100644
--- a/packages/flutter/lib/src/rendering/binding.dart
+++ b/packages/flutter/lib/src/rendering/binding.dart
@@ -68,6 +68,16 @@
       callback: () { debugDumpRenderTree(); return debugPrintDone; }
     );
 
+    registerSignalServiceExtension(
+      name: 'debugDumpLayerTree',
+      callback: () { debugDumpLayerTree(); return debugPrintDone; }
+    );
+
+    registerSignalServiceExtension(
+      name: 'debugDumpSemanticsTree',
+      callback: () { debugDumpSemanticsTree(); return debugPrintDone; }
+    );
+
     assert(() {
       // this service extension only works in checked mode
       registerBoolServiceExtension(
diff --git a/packages/flutter/test/foundation/service_extensions_test.dart b/packages/flutter/test/foundation/service_extensions_test.dart
index bc40947..d94ccd2 100644
--- a/packages/flutter/test/foundation/service_extensions_test.dart
+++ b/packages/flutter/test/foundation/service_extensions_test.dart
@@ -142,16 +142,52 @@
     expect(result, <String, String>{});
     expect(console, <Matcher>[
       matches(
+        r'^'
         r'RenderView#[0-9]+\n'
         r'   debug mode enabled - [a-zA-Z]+\n'
         r'   window size: Size\(800\.0, 600\.0\) \(in physical pixels\)\n'
         r'   device pixel ratio: 1\.0 \(physical pixels per logical pixel\)\n'
         r'   configuration: Size\(800\.0, 600\.0\) at 1\.0x \(in logical pixels\)\n'
+        r'$'
       ),
     ]);
     console.clear();
   });
 
+  test('Service extensions - debugDumpLayerTree', () async {
+    Map<String, String> result;
+
+    await binding.doFrame();
+    result = await binding.testExtension('debugDumpLayerTree', <String, String>{});
+    expect(result, <String, String>{});
+    expect(console, <Matcher>[
+      matches(
+        r'^'
+        r'TransformLayer#[0-9]+\n'
+        r'   owner: RenderView#[0-9]+\n'
+        r'   creator: RenderView\n'
+        r'   offset: Offset\(0\.0, 0\.0\)\n'
+        r'   transform:\n'
+        r'     \[0] 1\.0,0\.0,0\.0,0\.0\n'
+        r'     \[1] 0\.0,1\.0,0\.0,0\.0\n'
+        r'     \[2] 0\.0,0\.0,1\.0,0\.0\n'
+        r'     \[3] 0\.0,0\.0,0\.0,1\.0\n'
+        r'$'
+      ),
+    ]);
+    console.clear();
+  });
+
+  test('Service extensions - debugDumpSemanticsTree', () async {
+    Map<String, String> result;
+
+    await binding.doFrame();
+    result = await binding.testExtension('debugDumpSemanticsTree', <String, String>{});
+    expect(result, <String, String>{});
+    expect(console, <String>['Semantics not collected.']);
+    console.clear();
+  });
+
   test('Service extensions - debugPaint', () async {
     Map<String, String> result;
     Future<Map<String, String>> pendingResult;
@@ -381,7 +417,7 @@
   test('Service extensions - posttest', () async {
     // If you add a service extension... TEST IT! :-)
     // ...then increment this number.
-    expect(binding.extensions.length, 12);
+    expect(binding.extensions.length, 14);
 
     expect(console, isEmpty);
     debugPrint = debugPrintThrottled;
diff --git a/packages/flutter_tools/lib/src/resident_runner.dart b/packages/flutter_tools/lib/src/resident_runner.dart
index e4f07d1..a568d62 100644
--- a/packages/flutter_tools/lib/src/resident_runner.dart
+++ b/packages/flutter_tools/lib/src/resident_runner.dart
@@ -147,12 +147,22 @@
       await view.uiIsolate.flutterDebugDumpRenderTree();
   }
 
+  Future<Null> debugDumpLayerTree() async {
+    for (FlutterView view in views)
+      await view.uiIsolate.flutterDebugDumpLayerTree();
+  }
+
+  Future<Null> debugDumpSemanticsTree() async {
+    for (FlutterView view in views)
+      await view.uiIsolate.flutterDebugDumpSemanticsTree();
+  }
+
   Future<Null> toggleDebugPaintSizeEnabled() async {
     for (FlutterView view in views)
       await view.uiIsolate.flutterToggleDebugPaintSizeEnabled();
   }
 
-  Future<String> togglePlatform({String from}) async {
+  Future<String> togglePlatform({ String from }) async {
     String to;
     switch (from) {
       case 'iOS':
@@ -163,9 +173,8 @@
         to = 'iOS';
         break;
     }
-    for (FlutterView view in views) {
+    for (FlutterView view in views)
       await view.uiIsolate.flutterPlatformOverride(to);
-    }
     return to;
   }
 
@@ -415,6 +424,18 @@
       await device.debugDumpRenderTree();
   }
 
+  Future<Null> _debugDumpLayerTree() async {
+    await refreshViews();
+    for (FlutterDevice device in flutterDevices)
+      await device.debugDumpLayerTree();
+  }
+
+  Future<Null> _debugDumpSemanticsTree() async {
+    await refreshViews();
+    for (FlutterDevice device in flutterDevices)
+      await device.debugDumpSemanticsTree();
+  }
+
   Future<Null> _debugToggleDebugPaintSizeEnabled() async {
     await refreshViews();
     for (FlutterDevice device in flutterDevices)
@@ -505,9 +526,8 @@
   }
 
   Future<Null> connectToServiceProtocol({String viewFilter}) async {
-    if (!debuggingOptions.debuggingEnabled) {
+    if (!debuggingOptions.debuggingEnabled)
       return new Future<Null>.error('Error the service protocol is not enabled.');
-    }
 
     bool viewFound = false;
     for (FlutterDevice device in flutterDevices) {
@@ -564,12 +584,22 @@
         await _debugDumpRenderTree();
         return true;
       }
+    } else if (character == 'L') {
+      if (supportsServiceProtocol) {
+        await _debugDumpLayerTree();
+        return true;
+      }
+    } else if (character == 'S') {
+      if (supportsServiceProtocol) {
+        await _debugDumpSemanticsTree();
+        return true;
+      }
     } else if (lower == 'p') {
       if (supportsServiceProtocol && isRunningDebug) {
         await _debugToggleDebugPaintSizeEnabled();
         return true;
       }
-    } else if (lower == 's') {
+    } else if (character == 's') {
       for (FlutterDevice device in flutterDevices) {
         if (device.device.supportsScreenshot)
           await _screenshot(device);
@@ -656,16 +686,13 @@
     final DependencyChecker dependencyChecker =
         new DependencyChecker(dartDependencySetBuilder, assetBundle);
     final String path = device.package.packagePath;
-    if (path == null) {
+    if (path == null)
       return true;
-    }
     final FileStat stat = fs.file(path).statSync();
-    if (stat.type != FileSystemEntityType.FILE) {
+    if (stat.type != FileSystemEntityType.FILE)
       return true;
-    }
-    if (!fs.file(path).existsSync()) {
+    if (!fs.file(path).existsSync())
       return true;
-    }
     final DateTime lastBuildTime = stat.modified;
     return dependencyChecker.check(lastBuildTime);
   }
@@ -683,8 +710,9 @@
 
   void printHelpDetails() {
     if (supportsServiceProtocol) {
-      printStatus('To dump the widget hierarchy of the app (debugDumpApp), press "w".');
+      printStatus('You can dump the widget hierarchy of the app (debugDumpApp) by pressing "w".');
       printStatus('To dump the rendering tree of the app (debugDumpRenderTree), press "t".');
+      printStatus('For layers (debugDumpLayerTree), use "L"; accessibility (debugDumpSemantics), "S".');
       if (isRunningDebug) {
         printStatus('To toggle the display of construction lines (debugPaintSizeEnabled), press "p".');
         printStatus('To simulate different operating systems, (defaultTargetPlatform), press "o".');
diff --git a/packages/flutter_tools/lib/src/vmservice.dart b/packages/flutter_tools/lib/src/vmservice.dart
index 9cf7675..fa3899b 100644
--- a/packages/flutter_tools/lib/src/vmservice.dart
+++ b/packages/flutter_tools/lib/src/vmservice.dart
@@ -998,6 +998,14 @@
     return invokeFlutterExtensionRpcRaw('ext.flutter.debugDumpRenderTree', timeout: kLongRequestTimeout);
   }
 
+  Future<Map<String, dynamic>> flutterDebugDumpLayerTree() {
+    return invokeFlutterExtensionRpcRaw('ext.flutter.debugDumpLayerTree', timeout: kLongRequestTimeout);
+  }
+
+  Future<Map<String, dynamic>> flutterDebugDumpSemanticsTree() {
+    return invokeFlutterExtensionRpcRaw('ext.flutter.debugDumpSemanticsTree', timeout: kLongRequestTimeout);
+  }
+
   Future<Map<String, dynamic>> flutterToggleDebugPaintSizeEnabled() async {
     Map<String, dynamic> state = await invokeFlutterExtensionRpcRaw('ext.flutter.debugPaint');
     if (state != null && state.containsKey('enabled') && state['enabled'] is String) {