merge method
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;
}