execUICommands function

void execUICommands(
  1. WebFViewController view,
  2. List<UICommand> commands
)

Implementation

void execUICommands(WebFViewController view, List<UICommand> commands) {
  Map<int, bool> pendingStylePropertiesTargets = {};

  String blinkStylePropertyNameFromId(int propertyId) {
    if (propertyId <= 0 || propertyId >= blinkCSSPropertyIdToStyleName.length) return '';
    return blinkCSSPropertyIdToStyleName[propertyId];
  }

  String blinkKeywordFromValueId(int valueId) {
    if (valueId <= 0 || valueId >= blinkCSSValueIdToKeyword.length) return '';
    return blinkCSSValueIdToKeyword[valueId];
  }

  for(UICommand command in commands) {
    UICommandType commandType = command.type;

    if (enableWebFCommandLog) {
      String printMsg;
      switch(command.type) {
        case UICommandType.setInlineStyle:
          String? valueLog;
          String? baseHrefLog;
          int? importantLog;
          if (command.nativePtr2 != nullptr) {
            try {
              final Pointer<NativeStyleValueWithHref> payload =
                  command.nativePtr2.cast<NativeStyleValueWithHref>();
              final Pointer<NativeString> valuePtr = payload.ref.value;
              final Pointer<NativeString> hrefPtr = payload.ref.href;
              importantLog = payload.ref.important;
              if (valuePtr != nullptr) {
                valueLog = nativeStringToString(valuePtr);
              }
              if (hrefPtr != nullptr) {
                baseHrefLog = nativeStringToString(hrefPtr);
              }
            } catch (_) {
              valueLog = '<error>';
              baseHrefLog = '<error>';
              importantLog = null;
            }
          }
          printMsg =
              'nativePtr: ${command.nativePtr} type: ${command.type} key: ${command.args} value: $valueLog important: ${importantLog ?? 0} baseHref: ${baseHrefLog ?? 'null'}';
          break;
        case UICommandType.setStyleById:
          final String keyLog = blinkStylePropertyNameFromId(command.stylePropertyId);
          String? valueLog;
          String? baseHrefLog;
          final int slot = command.styleValueSlot;
          if (slot < 0) {
            valueLog = blinkKeywordFromValueId(-slot - 1);
          } else if (slot > 0) {
            try {
              valueLog = nativeStringToString(Pointer<NativeString>.fromAddress(slot));
            } catch (_) {
              valueLog = '<error>';
            }
          }
          if (command.nativePtr2 != nullptr) {
            try {
              baseHrefLog = nativeStringToString(command.nativePtr2.cast<NativeString>());
            } catch (_) {
              baseHrefLog = '<error>';
            }
          }
          printMsg =
              'nativePtr: ${command.nativePtr} type: ${command.type} propertyId: ${command.stylePropertyId} key: $keyLog value: ${valueLog ?? '<null>'} baseHref: ${baseHrefLog ?? 'null'}';
          break;
        case UICommandType.setSheetStyleById:
          final int encoded = command.stylePropertyId;
          final bool important = (encoded & 0x80000000) != 0;
          final int propertyId = encoded & 0x7fffffff;
          final String keyLog = blinkStylePropertyNameFromId(propertyId);
          String? valueLog;
          String? baseHrefLog;
          final int slot = command.styleValueSlot;
          if (slot < 0) {
            valueLog = blinkKeywordFromValueId(-slot - 1);
          } else if (slot > 0) {
            try {
              valueLog = nativeStringToString(Pointer<NativeString>.fromAddress(slot));
            } catch (_) {
              valueLog = '<error>';
            }
          }
          if (command.nativePtr2 != nullptr) {
            try {
              baseHrefLog = nativeStringToString(command.nativePtr2.cast<NativeString>());
            } catch (_) {
              baseHrefLog = '<error>';
            }
          }
          printMsg =
              'nativePtr: ${command.nativePtr} type: ${command.type} propertyId: $propertyId important: ${important ? 1 : 0} key: $keyLog value: ${valueLog ?? '<null>'} baseHref: ${baseHrefLog ?? 'null'}';
          break;
        case UICommandType.setPseudoStyle:
          if (command.nativePtr2 != nullptr) {
            try {
              final Pointer<NativePseudoStyleWithHref> payload =
                  command.nativePtr2.cast<NativePseudoStyleWithHref>();
              final Pointer<NativeString> keyPtr = payload.ref.key;
              final Pointer<NativeString> valuePtr = payload.ref.value;
              final Pointer<NativeString> hrefPtr = payload.ref.href;
              final String key = keyPtr != nullptr ? nativeStringToString(keyPtr) : '';
              final String value = valuePtr != nullptr ? nativeStringToString(valuePtr) : '';
              final String? href = hrefPtr != nullptr ? nativeStringToString(hrefPtr) : null;
              printMsg =
                  'nativePtr: ${command.nativePtr} type: ${command.type} pseudo: ${command.args} property: $key=$value baseHref: ${href ?? 'null'}';
            } catch (_) {
              printMsg =
                  'nativePtr: ${command.nativePtr} type: ${command.type} pseudo: ${command.args} property: <error>';
            }
          } else {
            printMsg =
                'nativePtr: ${command.nativePtr} type: ${command.type} pseudo: ${command.args} property: <null>';
          }
          break;
        case UICommandType.removePseudoStyle:
          printMsg = 'nativePtr: ${command.nativePtr} type: ${command.type} pseudo: ${command.args} remove: ${command.nativePtr2 != nullptr ? nativeStringToString(command.nativePtr2.cast<NativeString>()) : null}';
          break;
        case UICommandType.clearPseudoStyle:
          printMsg = 'nativePtr: ${command.nativePtr} type: ${command.type} pseudo: ${command.args}';
          break;
        case UICommandType.setPseudoStyle:
          printMsg =
              'nativePtr: ${command.nativePtr} type: ${command.type} pseudo: ${command.args} cssText: ${command.nativePtr2 != nullptr ? nativeStringToString(command.nativePtr2.cast<NativeString>()) : null}';
          break;
        case UICommandType.removePseudoStyle:
          printMsg = 'nativePtr: ${command.nativePtr} type: ${command.type} pseudo: ${command.args} remove: ${command.nativePtr2 != nullptr ? nativeStringToString(command.nativePtr2.cast<NativeString>()) : null}';
          break;
        case UICommandType.clearPseudoStyle:
          printMsg = 'nativePtr: ${command.nativePtr} type: ${command.type} pseudo: ${command.args}';
          break;
        case UICommandType.setAttribute:
          printMsg = 'nativePtr: ${command.nativePtr} type: ${command.type} key: ${nativeStringToString(command.nativePtr2.cast<NativeString>())} value: ${command.args}';
          break;
        case UICommandType.createTextNode:
          printMsg = 'nativePtr: ${command.nativePtr} type: ${command.type} data: ${command.args}';
          break;
        default:
          printMsg = 'nativePtr: ${command.nativePtr} type: ${command.type} args: ${command.args} nativePtr2: ${command.nativePtr2}';
      }
      bridgeLogger.fine(printMsg);
    }

    if (commandType == UICommandType.startRecordingCommand || commandType == UICommandType.finishRecordingCommand) continue;

    Pointer nativePtr = command.nativePtr;

    try {
      switch (commandType) {
        case UICommandType.requestAnimationFrame:
          // args carries the requestId generated on C++ side
          int requestId = 0;
          if (command.args.isNotEmpty) {
            requestId = int.tryParse(command.args) ?? 0;
          }
          // nativePtr2 is a function pointer: NativeRAFAsyncCallback
          final Pointer<NativeFunction<NativeRAFAsyncCallback>> rafCallbackPtr =
              command.nativePtr2.cast<NativeFunction<NativeRAFAsyncCallback>>();
          final DartRAFAsyncCallback rafCallback = rafCallbackPtr.asFunction();

          // Schedule a frame and invoke native callback on frame
          view.rootController.module.requestAnimationFrame(requestId, (double highResTimeStamp) {
            try {
              rafCallback(command.nativePtr.cast<Void>(), view.contextId, highResTimeStamp, nullptr);
            } catch (e, stack) {
              Pointer<Utf8> nativeErrorMessage = ('Error: $e\n$stack').toNativeUtf8();
              rafCallback(command.nativePtr.cast<Void>(), view.contextId, highResTimeStamp, nativeErrorMessage);
            }
          });
          break;
        case UICommandType.createElement:
          view.createElement(nativePtr.cast<NativeBindingObject>(), command.args);

          break;
        case UICommandType.createDocument:
          view.initDocument(view, nativePtr.cast<NativeBindingObject>());
          break;
        case UICommandType.createWindow:
          view.initWindow(view, nativePtr.cast<NativeBindingObject>());
          break;
        case UICommandType.createTextNode:
          view.createTextNode(nativePtr.cast<NativeBindingObject>(), command.args);
          break;
        case UICommandType.createComment:
          view.createComment(nativePtr.cast<NativeBindingObject>());
          break;
        case UICommandType.disposeBindingObject:
          WebFViewController.disposeBindingObject(view, nativePtr.cast<NativeBindingObject>());
          break;
        case UICommandType.addEvent:
          Pointer<AddEventListenerOptions> eventListenerOptions = command.nativePtr2.cast<AddEventListenerOptions>();
          view.addEvent(nativePtr.cast<NativeBindingObject>(), command.args,
              addEventListenerOptions: eventListenerOptions);
          break;
        case UICommandType.removeEvent:
          bool isCapture = command.nativePtr2.address == 1;
          view.removeEvent(nativePtr.cast<NativeBindingObject>(), command.args, isCapture: isCapture);
          break;
        case UICommandType.insertAdjacentNode:
          view.insertAdjacentNode(
              nativePtr.cast<NativeBindingObject>(), command.args, command.nativePtr2.cast<NativeBindingObject>());
          break;
        case UICommandType.removeNode:
          view.removeNode(nativePtr.cast<NativeBindingObject>());
          break;
        case UICommandType.cloneNode:
          view.cloneNode(nativePtr.cast<NativeBindingObject>(), command.nativePtr2.cast<NativeBindingObject>());
          break;
        case UICommandType.setInlineStyle:
          String value = '';
          String? baseHref;
          bool important = false;
          if (command.nativePtr2 != nullptr) {
            final Pointer<NativeStyleValueWithHref> payload =
                command.nativePtr2.cast<NativeStyleValueWithHref>();
            final Pointer<NativeString> valuePtr = payload.ref.value;
            final Pointer<NativeString> hrefPtr = payload.ref.href;
            important = payload.ref.important == 1;
            if (valuePtr != nullptr) {
              final Pointer<NativeString> nativeValue = valuePtr.cast<NativeString>();
              value = nativeStringToString(nativeValue);
              freeNativeString(nativeValue);
            }
            if (hrefPtr != nullptr) {
              final Pointer<NativeString> nativeHref = hrefPtr.cast<NativeString>();
              final String raw = nativeStringToString(nativeHref);
              freeNativeString(nativeHref);
              baseHref = raw.isEmpty ? null : raw;
            }
            malloc.free(payload);
          }

          // Legacy inline style updates are forwarded to Dart; validate there so
          // invalid CSSOM assignments don't clobber previous values.
          view.setInlineStyle(
            nativePtr,
            command.args,
            value,
            baseHref: baseHref,
            important: important,
            validate: true,
          );
          pendingStylePropertiesTargets[nativePtr.address] = true;
          break;
        case UICommandType.setSheetStyle:
          String value = '';
          String? baseHref;
          bool important = false;
          if (command.nativePtr2 != nullptr) {
            final Pointer<NativeStyleValueWithHref> payload =
                command.nativePtr2.cast<NativeStyleValueWithHref>();
            final Pointer<NativeString> valuePtr = payload.ref.value;
            final Pointer<NativeString> hrefPtr = payload.ref.href;
            important = payload.ref.important == 1;
            if (valuePtr != nullptr) {
              final Pointer<NativeString> nativeValue = valuePtr.cast<NativeString>();
              value = nativeStringToString(nativeValue);
              freeNativeString(nativeValue);
            }
            if (hrefPtr != nullptr) {
              final Pointer<NativeString> nativeHref = hrefPtr.cast<NativeString>();
              final String raw = nativeStringToString(nativeHref);
              freeNativeString(nativeHref);
              baseHref = raw.isEmpty ? null : raw;
            }
            malloc.free(payload);
          }

          view.setSheetStyle(nativePtr, command.args, value, baseHref: baseHref, important: important);
          pendingStylePropertiesTargets[nativePtr.address] = true;
          break;
        case UICommandType.setStyleById:
          final String key = blinkStylePropertyNameFromId(command.stylePropertyId);
          if (key.isEmpty) break;

          String value = '';
          String? baseHref;

          final int slot = command.styleValueSlot;
          if (slot < 0) {
            value = blinkKeywordFromValueId(-slot - 1);
          } else if (slot > 0) {
            final Pointer<NativeString> nativeValue = Pointer<NativeString>.fromAddress(slot);
            value = nativeStringToString(nativeValue);
            freeNativeString(nativeValue);
          }

          if (command.nativePtr2 != nullptr) {
            final Pointer<NativeString> nativeHref = command.nativePtr2.cast<NativeString>();
            final String raw = nativeStringToString(nativeHref);
            freeNativeString(nativeHref);
            baseHref = raw.isEmpty ? null : raw;
          }

          // Blink style exports are already validated; skip Dart-side validation
          // to avoid rejecting valid values not yet covered by the Dart validator.
          view.setInlineStyle(nativePtr, key, value, baseHref: baseHref, validate: false);
          pendingStylePropertiesTargets[nativePtr.address] = true;
          break;
        case UICommandType.setSheetStyleById:
          final int encoded = command.stylePropertyId;
          final bool important = (encoded & 0x80000000) != 0;
          final int propertyId = encoded & 0x7fffffff;
          final String key = blinkStylePropertyNameFromId(propertyId);
          if (key.isEmpty) break;

          String value = '';
          String? baseHref;

          final int slot = command.styleValueSlot;
          if (slot < 0) {
            value = blinkKeywordFromValueId(-slot - 1);
          } else if (slot > 0) {
            final Pointer<NativeString> nativeValue = Pointer<NativeString>.fromAddress(slot);
            value = nativeStringToString(nativeValue);
            freeNativeString(nativeValue);
          }

          if (command.nativePtr2 != nullptr) {
            final Pointer<NativeString> nativeHref = command.nativePtr2.cast<NativeString>();
            final String raw = nativeStringToString(nativeHref);
            freeNativeString(nativeHref);
            baseHref = raw.isEmpty ? null : raw;
          }

          view.setSheetStyle(nativePtr, key, value, baseHref: baseHref, important: important);
          pendingStylePropertiesTargets[nativePtr.address] = true;
          break;
        case UICommandType.setPseudoStyle:
          if (command.nativePtr2 != nullptr) {
            final keyValue = nativePairToPairRecord(command.nativePtr2.cast());
            if (keyValue.key.isNotEmpty) {
              view.setPseudoStyle(nativePtr, command.args, keyValue.key, keyValue.value);
            }
          }
          break;
        case UICommandType.removePseudoStyle:
          if (command.nativePtr2 != nullptr) {
            Pointer<NativeString> nativeKey = command.nativePtr2.cast<NativeString>();
            String key = nativeStringToString(nativeKey);
            freeNativeString(nativeKey);
            view.removePseudoStyle(nativePtr, command.args, key);
          }
          break;
        case UICommandType.clearPseudoStyle:
          view.clearPseudoStyle(nativePtr, command.args);
          break;
        case UICommandType.clearStyle:
          view.clearInlineStyle(nativePtr);
          pendingStylePropertiesTargets[nativePtr.address] = true;
          break;
        case UICommandType.clearSheetStyle:
          view.clearSheetStyle(nativePtr);
          pendingStylePropertiesTargets[nativePtr.address] = true;
          break;
        case UICommandType.setPseudoStyle:
          if (command.nativePtr2 != nullptr) {
            final Pointer<NativePseudoStyleWithHref> payload =
                command.nativePtr2.cast<NativePseudoStyleWithHref>();
            final Pointer<NativeString> keyPtr = payload.ref.key;
            final Pointer<NativeString> valuePtr = payload.ref.value;
            final Pointer<NativeString> hrefPtr = payload.ref.href;

            String key = '';
            String value = '';
            String? baseHref;

            if (keyPtr != nullptr) {
              final Pointer<NativeString> nativeKey = keyPtr.cast<NativeString>();
              key = nativeStringToString(nativeKey);
              freeNativeString(nativeKey);
            }
            if (valuePtr != nullptr) {
              final Pointer<NativeString> nativeValue = valuePtr.cast<NativeString>();
              value = nativeStringToString(nativeValue);
              freeNativeString(nativeValue);
            }
            if (hrefPtr != nullptr) {
              final Pointer<NativeString> nativeHref = hrefPtr.cast<NativeString>();
              final String raw = nativeStringToString(nativeHref);
              freeNativeString(nativeHref);
              baseHref = raw.isEmpty ? null : raw;
            }
            malloc.free(payload);

            if (key.isNotEmpty) {
              view.setPseudoStyle(nativePtr, command.args, key, value, baseHref: baseHref);
            }
          }
          break;
        case UICommandType.removePseudoStyle:
          if (command.nativePtr2 != nullptr) {
            Pointer<NativeString> nativeKey = command.nativePtr2.cast<NativeString>();
            String key = nativeStringToString(nativeKey);
            freeNativeString(nativeKey);
            view.removePseudoStyle(nativePtr, command.args, key);
          }
          break;
        case UICommandType.clearPseudoStyle:
          view.clearPseudoStyle(nativePtr, command.args);
          break;
        case UICommandType.setAttribute:
          Pointer<NativeString> nativeKey = command.nativePtr2.cast<NativeString>();
          String key = nativeStringToString(nativeKey);
          freeNativeString(nativeKey);
          view.setAttribute(nativePtr.cast<NativeBindingObject>(), key, command.args);
          break;
        case UICommandType.setProperty:
          BindingObject? target = view.getBindingObject<BindingObject>(nativePtr.cast<NativeBindingObject>());
          if (target == null) {
            break;
          }

          List<dynamic> args = [command.args, fromNativeValue(view, command.nativePtr2.cast<NativeValue>())];
          setterBindingCall(target, args);
          break;
        case UICommandType.removeAttribute:
          String key = command.args;
          view.removeAttribute(nativePtr, key);
          break;
        case UICommandType.createDocumentFragment:
          view.createDocumentFragment(nativePtr.cast<NativeBindingObject>());
          break;
        case UICommandType.createSVGElement:
          view.createElementNS(nativePtr.cast<NativeBindingObject>(), svgElementUri, command.args);
          break;
        case UICommandType.createElementNS:
          Pointer<NativeString> nativeNameSpaceUri = command.nativePtr2.cast<NativeString>();
          String namespaceUri = nativeStringToString(nativeNameSpaceUri);
          freeNativeString(nativeNameSpaceUri);

          view.createElementNS(nativePtr.cast<NativeBindingObject>(), namespaceUri, command.args);
          break;
        case UICommandType.asyncCaller:
          Pointer<BindingObjectAsyncCallContext> asyncCallContext =
              command.nativePtr2.cast<BindingObjectAsyncCallContext>();

          asyncInvokeBindingMethodFromNativeImpl(
            view, asyncCallContext,
            command.nativePtr.cast<NativeBindingObject>(),
          );
          break;
        case UICommandType.requestCanvasPaint:
          view.requestCanvasPaint(nativePtr.cast<NativeBindingObject>());
          break;
        case UICommandType.addIntersectionObserver:
          view.addIntersectionObserver(
              nativePtr.cast<NativeBindingObject>(), command.nativePtr2.cast<NativeBindingObject>());
          break;
        case UICommandType.removeIntersectionObserver:
          view.removeIntersectionObserver(
              nativePtr.cast<NativeBindingObject>(), command.nativePtr2.cast<NativeBindingObject>());
          break;
        case UICommandType.disconnectIntersectionObserver:
          view.disconnectIntersectionObserver(nativePtr.cast<NativeBindingObject>());
          break;
        default:
          break;
      }
    } catch (e, stack) {
      bridgeLogger.severe('Error executing UI command', e, stack);
    }
  }
  // For pending style properties, we needs to flush to render style.
  for (int address in pendingStylePropertiesTargets.keys) {
    try {
      view.flushPendingStyleProperties(address);
    } catch (e, stack) {
      bridgeLogger.severe('Error executing UI command', e, stack);
    }
  }
  pendingStylePropertiesTargets.clear();
}