execUICommands function
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();
}