merge method

bool merge(
  1. CSSStyleDeclaration other
)

Implementation

bool merge(CSSStyleDeclaration other) {
  bool updateStatus = false;
  final Map<String, CSSPropertyValue> otherPendingProperties =
      other._pendingProperties;
  final Map<String, CSSPropertyValue> otherProperties = other._properties;
  final Map<String, bool> otherImportants = other._importants;

  void mergePseudoStyle({
    required CSSStyleDeclaration? currentStyle,
    required CSSStyleDeclaration? incomingStyle,
    required void Function(CSSStyleDeclaration? value) assign,
    required VoidCallback markNeedsUpdate,
    required bool clearWhenMissing,
  }) {
    if (incomingStyle != null) {
      if (currentStyle == null) {
        assign(incomingStyle.cloneEffective());
      } else if (currentStyle.merge(incomingStyle)) {
        markNeedsUpdate();
      }
    } else if (clearWhenMissing && currentStyle != null) {
      assign(null);
    }
  }

  void mergeProperty(String propertyName,
      {CSSPropertyValue? prevValue,
      CSSPropertyValue? currentValue,
      required bool currentImportant}) {
    prevValue ??= _effectiveProperty(propertyName);
    currentValue ??=
        otherPendingProperties[propertyName] ?? otherProperties[propertyName];

    if ((prevValue == null || prevValue.value.isEmpty) &&
        (currentValue == null || currentValue.value.isEmpty)) {
      return;
    }

    if (prevValue != null &&
        prevValue.value.isNotEmpty &&
        (currentValue == null || currentValue.value.isEmpty)) {
      // Remove property.
      _pendingProperties[propertyName] =
          CSSPropertyValue(_removedPropertyFallbackValue(
        propertyName,
        currentImportant,
      ));
      updateStatus = true;
      return;
    }

    if (currentValue == null || currentValue.value.isEmpty) {
      return;
    }

    final bool sameSerializedValue =
        prevValue != null && prevValue.value == currentValue.value;
    if (sameSerializedValue &&
        !CSSVariable.isCSSVariableValue(currentValue.value)) {
      return;
    }

    if (_queueMergedPropertyValue(propertyName, currentValue,
        important: currentImportant)) {
      // Update property.
      updateStatus = true;
    }
  }

  if (otherProperties.isEmpty) {
    for (final String propertyName in _pendingProperties.keys.toList()) {
      mergeProperty(propertyName,
          prevValue: _pendingProperties[propertyName],
          currentValue: otherPendingProperties[propertyName],
          currentImportant: otherImportants[propertyName] == true);
    }
    for (final MapEntry<String, CSSPropertyValue> entry in _properties.entries) {
      final String propertyName = entry.key;
      if (_pendingProperties.containsKey(propertyName)) continue;
      mergeProperty(propertyName,
          prevValue: entry.value,
          currentValue: otherPendingProperties[propertyName],
          currentImportant: otherImportants[propertyName] == true);
    }
    for (final MapEntry<String, CSSPropertyValue> entry
        in otherPendingProperties.entries) {
      final String propertyName = entry.key;
      if (_pendingProperties.containsKey(propertyName) ||
          _properties.containsKey(propertyName)) {
        continue;
      }
      mergeProperty(propertyName,
          currentValue: entry.value,
          currentImportant: otherImportants[propertyName] == true);
    }
  } else {
    for (final String propertyName in _pendingProperties.keys.toList()) {
      mergeProperty(propertyName,
          currentImportant: otherImportants[propertyName] == true);
    }
    for (final String propertyName in _properties.keys) {
      if (_pendingProperties.containsKey(propertyName)) continue;
      mergeProperty(propertyName,
          currentImportant: otherImportants[propertyName] == true);
    }
    for (final String propertyName in otherPendingProperties.keys) {
      if (_hasEffectiveProperty(propertyName)) continue;
      mergeProperty(propertyName,
          currentImportant: otherImportants[propertyName] == true);
    }
    for (final String propertyName in otherProperties.keys) {
      if (otherPendingProperties.containsKey(propertyName) ||
          _hasEffectiveProperty(propertyName)) {
        continue;
      }
      mergeProperty(propertyName,
          currentImportant: otherImportants[propertyName] == true);
    }
  }

  // Merge pseudo-element styles. Ensure target side is initialized so rules from
  // 'other' are not dropped when this side is null. When pseudo rules were
  // processed on the other side, clear stale pseudo styles if no rule matches.
  if (other._didProcessPseudoRules) {
    mergePseudoStyle(
      currentStyle: pseudoBeforeStyle,
      incomingStyle: other.pseudoBeforeStyle,
      assign: (value) => pseudoBeforeStyle = value,
      markNeedsUpdate: () => target?.markBeforePseudoElementNeedsUpdate(),
      clearWhenMissing: true,
    );
    mergePseudoStyle(
      currentStyle: pseudoAfterStyle,
      incomingStyle: other.pseudoAfterStyle,
      assign: (value) => pseudoAfterStyle = value,
      markNeedsUpdate: () => target?.markAfterPseudoElementNeedsUpdate(),
      clearWhenMissing: true,
    );
    mergePseudoStyle(
      currentStyle: pseudoFirstLetterStyle,
      incomingStyle: other.pseudoFirstLetterStyle,
      assign: (value) => pseudoFirstLetterStyle = value,
      markNeedsUpdate: () => target?.markFirstLetterPseudoNeedsUpdate(),
      clearWhenMissing: true,
    );
    mergePseudoStyle(
      currentStyle: pseudoFirstLineStyle,
      incomingStyle: other.pseudoFirstLineStyle,
      assign: (value) => pseudoFirstLineStyle = value,
      markNeedsUpdate: () => target?.markFirstLinePseudoNeedsUpdate(),
      clearWhenMissing: true,
    );
  } else {
    mergePseudoStyle(
      currentStyle: pseudoBeforeStyle,
      incomingStyle: other.pseudoBeforeStyle,
      assign: (value) => pseudoBeforeStyle = value,
      markNeedsUpdate: () => target?.markBeforePseudoElementNeedsUpdate(),
      clearWhenMissing: false,
    );
    mergePseudoStyle(
      currentStyle: pseudoAfterStyle,
      incomingStyle: other.pseudoAfterStyle,
      assign: (value) => pseudoAfterStyle = value,
      markNeedsUpdate: () => target?.markAfterPseudoElementNeedsUpdate(),
      clearWhenMissing: false,
    );
    mergePseudoStyle(
      currentStyle: pseudoFirstLetterStyle,
      incomingStyle: other.pseudoFirstLetterStyle,
      assign: (value) => pseudoFirstLetterStyle = value,
      markNeedsUpdate: () => target?.markFirstLetterPseudoNeedsUpdate(),
      clearWhenMissing: false,
    );
    mergePseudoStyle(
      currentStyle: pseudoFirstLineStyle,
      incomingStyle: other.pseudoFirstLineStyle,
      assign: (value) => pseudoFirstLineStyle = value,
      markNeedsUpdate: () => target?.markFirstLinePseudoNeedsUpdate(),
      clearWhenMissing: false,
    );
  }

  return updateStatus;
}