String addServiceExtensions()

in packages/devtools_app/assets/scripts/inspector_polyfill_script.dart [102:361]


String addServiceExtensions() {
  // INSPECTOR_POLYFILL_SCRIPT_START
  T toEnumEntry<T>(List<T> enumEntries, String name) {
    for (T entry in enumEntries) {
      if (entry.toString() == name) {
        return entry;
      }
    }
    throw 'Enum value $name not found';
  }

  /// This is identical to Flutter's getRootWidgetSummaryTree service extension,
  /// but with the added properties.
  Future<Map<String, dynamic>> getRootWidgetSummaryTreeWithPreviews(
      Map<String, String> parameters) {
    final instance = WidgetInspectorService.instance;
    final groupName = parameters['groupName'];

    final result = instance._nodeToJson(
      WidgetsBinding.instance?.renderViewElement?.toDiagnosticsNode(),
      InspectorSerializationDelegate(
        groupName: groupName,
        subtreeDepth: 1000000,
        summaryTree: true,
        service: instance,
        addAdditionalPropertiesCallback: (DiagnosticsNode node, delegate) {
          final additionalJson = <String, Object>{};

          final value = node.value;
          if (value is Element) {
            final renderObject = value.renderObject;
            if (renderObject is RenderParagraph) {
              additionalJson['textPreview'] = renderObject.text.toPlainText();
            }
          }

          return additionalJson;
        },
      ),
    );

    return Future.value(<String, dynamic>{
      'result': result,
    });
  }

  Future<Map<String, dynamic>> getLayoutExplorerNode(
      Map<String, String> parameters) {
    final id = parameters['id'];
    final subtreeDepth = int.parse(parameters['subtreeDepth'] as String);
    final groupName = parameters['groupName'];
    var result = <String, dynamic>{};
    final instance = WidgetInspectorService.instance;
    final root = instance.toObject(id);
    if (root == null) {
      return Future.value(<String, dynamic>{
        'result': result,
      });
    }
    result = instance._nodeToJson(
      root as DiagnosticsNode,
      InspectorSerializationDelegate(
          groupName: groupName,
          summaryTree: true,
          subtreeDepth: subtreeDepth,
          service: instance,
          addAdditionalPropertiesCallback: (node, delegate) {
            final Map<String, Object> additionalJson = <String, Object>{};
            final value = node.value;
            if (value is Element) {
              final renderObject = value.renderObject;
              if (renderObject != null) {
                additionalJson['renderObject'] =
                    renderObject.toDiagnosticsNode().toJsonMap(
                          delegate.copyWith(
                            subtreeDepth: 0,
                            includeProperties: true,
                          ),
                        ) as Object;

                final renderParent = renderObject.parent;
                if (renderParent is RenderObject && subtreeDepth > 0) {
                  final parentCreator = renderParent.debugCreator;
                  if (parentCreator is DebugCreator) {
                    additionalJson['parentRenderElement'] =
                        parentCreator.element.toDiagnosticsNode().toJsonMap(
                              delegate.copyWith(
                                subtreeDepth: 0,
                                includeProperties: true,
                              ),
                            ) as Object;
                    // TODO(jacobr): also describe the path back up the tree to
                    // the RenderParentElement from the current element. It
                    // could be a surprising distance up the tree if a lot of
                    // elements don't have their own RenderObjects.
                  }
                }

                try {
                  if (!renderObject.debugNeedsLayout) {
                    final constraints = renderObject.constraints;
                    final constraintsProperty = <String, Object>{
                      'type': constraints.runtimeType.toString(),
                      'description': constraints.toString(),
                    };
                    if (constraints is BoxConstraints) {
                      constraintsProperty.addAll(<String, Object>{
                        'minWidth': constraints.minWidth.toString(),
                        'minHeight': constraints.minHeight.toString(),
                        'maxWidth': constraints.maxWidth.toString(),
                        'maxHeight': constraints.maxHeight.toString(),
                      });
                    }
                    additionalJson['constraints'] = constraintsProperty;
                  }
                } catch (e) {
                  // Constraints are sometimes unavailable even though
                  // debugNeedsLayout is false.
                }

                try {
                  if (renderObject is RenderBox) {
                    additionalJson['isBox'] = true;
                    additionalJson['size'] = <String, Object>{
                      'width': renderObject.size.width.toString(),
                      'height': renderObject.size.height.toString(),
                    };

                    final parentData = renderObject.parentData;
                    if (parentData is FlexParentData) {
                      additionalJson['flexFactor'] = parentData.flex as int;
                      additionalJson['flexFit'] =
                          describeEnum(parentData.fit ?? FlexFit.tight);
                    } else if (parentData is BoxParentData) {
                      final offset = parentData.offset;
                      additionalJson['parentData'] = {
                        'offsetX': offset.dx.toString(),
                        'offsetY': offset.dy.toString(),
                      };
                    }
                  } else if (renderObject is RenderView) {
                    additionalJson['size'] = <String, Object>{
                      'width': renderObject.size.width.toString(),
                      'height': renderObject.size.height.toString(),
                    };
                  }
                } catch (e) {
                  // Not laid out yet.
                }
              }
            }
            return additionalJson;
          }),
    ) as Map<String, dynamic>;
    return Future.value(<String, dynamic>{
      'result': result,
    });
  }

  Future<Map<String, dynamic>> setFlexFit(Map<String, String> parameters) {
    final String id = parameters['id'] as String;
    final parameter = parameters['flexFit'] as String;
    final FlexFit flexFit = toEnumEntry<FlexFit>(FlexFit.values, parameter);
    final dynamic object = WidgetInspectorService.instance.toObject(id);
    bool succeed = false;
    if (object != null) {
      final render = object.renderObject;
      final parentData = render.parentData;
      if (parentData is FlexParentData) {
        parentData.fit = flexFit;
        render.markNeedsLayout();
        succeed = true;
      }
    }
    return Future.value(<String, Object>{
      'result': succeed,
    });
  }

  Future<Map<String, dynamic>> setFlexFactor(Map<String, String> parameters) {
    final String id = parameters['id'] as String;
    final String flexFactor = parameters['flexFactor'] as String;
    final factor = flexFactor == 'null' ? null : int.parse(flexFactor);
    final dynamic object = WidgetInspectorService.instance.toObject(id);
    bool succeed = false;
    if (object != null) {
      final render = object.renderObject;
      final parentData = render.parentData;
      if (parentData is FlexParentData) {
        parentData.flex = factor;
        render.markNeedsLayout();
        succeed = true;
      }
    }
    return Future.value({'result': succeed});
  }

  Future<Map<String, dynamic>> setFlexProperties(
      Map<String, String> parameters) {
    final String id = parameters['id'] as String;
    final MainAxisAlignment mainAxisAlignment = toEnumEntry<MainAxisAlignment>(
      MainAxisAlignment.values,
      parameters['mainAxisAlignment'] as String,
    );
    final CrossAxisAlignment crossAxisAlignment =
        toEnumEntry<CrossAxisAlignment>(
      CrossAxisAlignment.values,
      parameters['crossAxisAlignment'] as String,
    );
    final dynamic object = WidgetInspectorService.instance.toObject(id);
    bool succeed = false;
    if (object != null) {
      final render = object.renderObject;
      if (render is RenderFlex) {
        render.mainAxisAlignment = mainAxisAlignment;
        render.crossAxisAlignment = crossAxisAlignment;
        render.markNeedsLayout();
        render.markNeedsPaint();
        succeed = true;
      }
    }
    return Future.value(<String, Object>{'result': succeed});
  }

  Future<Map<String, dynamic>> getPubRootDirectories(
    Map<String, String> parameters,
  ) {
    return Future.value(<String, Object>{
      'result': WidgetInspectorService.instance._pubRootDirectories ?? [],
    });
  }

  final failures = <String, String>{};
  void registerHelper(String name, ServiceExtensionCallback callback) {
    try {
      WidgetInspectorService.instance.registerServiceExtension(
        name: name,
        callback: callback,
      );
    } catch (e) {
      // It is not a fatal error if some of the extensions fail to register
      // as could be the case if some are already defined directly within
      // package:flutter for the version of package:flutter being used.
      failures[name] = e.toString();
    }
  }

  registerHelper('getLayoutExplorerNode', getLayoutExplorerNode);
  registerHelper('setFlexFit', setFlexFit);
  registerHelper('setFlexFactor', setFlexFactor);
  registerHelper('setFlexProperties', setFlexProperties);
  registerHelper('getPubRootDirectories', getPubRootDirectories);
  registerHelper(
    'getRootWidgetSummaryTreeWithPreviews',
    getRootWidgetSummaryTreeWithPreviews,
  );

  return WidgetInspectorService.instance._safeJsonEncode(failures);
  // INSPECTOR_POLYFILL_SCRIPT_END
}