in components/webext-storage/src/sync/mod.rs [37:134]
fn merge(
ext_id: String,
mut other: JsonMap,
mut ours: JsonMap,
parent: Option<JsonMap>,
) -> IncomingAction {
if other == ours {
return IncomingAction::Same { ext_id };
}
let old_incoming = other.clone();
// worst case is keys in each are unique.
let mut changes = StorageChanges::with_capacity(other.len() + ours.len());
if let Some(parent) = parent {
// Perform 3-way merge. First, for every key in parent,
// compare the parent value with the incoming value to compute
// an implicit "diff".
for (key, parent_value) in parent.into_iter() {
if let Some(incoming_value) = other.remove(&key) {
if incoming_value != parent_value {
log::trace!(
"merge: key {} was updated in incoming - copying value locally",
key
);
let old_value = ours.remove(&key);
let new_value = Some(incoming_value.clone());
if old_value != new_value {
changes.push(StorageValueChange {
key: key.clone(),
old_value,
new_value,
});
}
ours.insert(key, incoming_value);
}
} else {
// Key was not present in incoming value.
// Another client must have deleted it.
log::trace!(
"merge: key {} no longer present in incoming - removing it locally",
key
);
if let Some(old_value) = ours.remove(&key) {
changes.push(StorageValueChange {
key,
old_value: Some(old_value),
new_value: None,
});
}
}
}
// Then, go through every remaining key in incoming. These are
// the ones where a corresponding key does not exist in
// parent, so it is a new key, and we need to add it.
for (key, incoming_value) in other.into_iter() {
log::trace!(
"merge: key {} doesn't occur in parent - copying from incoming",
key
);
changes.push(StorageValueChange {
key: key.clone(),
old_value: None,
new_value: Some(incoming_value.clone()),
});
ours.insert(key, incoming_value);
}
} else {
// No parent. Server wins. Overwrite every key in ours with
// the corresponding value in other.
log::trace!("merge: no parent - copying all keys from incoming");
for (key, incoming_value) in other.into_iter() {
let old_value = ours.remove(&key);
let new_value = Some(incoming_value.clone());
if old_value != new_value {
changes.push(StorageValueChange {
key: key.clone(),
old_value,
new_value,
});
}
ours.insert(key, incoming_value);
}
}
if ours == old_incoming {
IncomingAction::TakeRemote {
ext_id,
data: old_incoming,
changes,
}
} else {
IncomingAction::Merge {
ext_id,
data: ours,
changes,
}
}
}