in components/places/src/history_sync/plan.rs [67:178]
fn plan_incoming_record(conn: &PlacesDb, record: HistoryRecord, max_visits: usize) -> IncomingPlan {
let url = match Url::parse(&record.hist_uri) {
Ok(u) => u,
Err(e) => return IncomingPlan::Invalid(e.into()),
};
if !record.id.is_valid_for_places() {
return IncomingPlan::Invalid(InvalidPlaceInfo::InvalidGuid.into());
}
match can_add_url(&url) {
Ok(can) => {
if !can {
return IncomingPlan::Skip;
}
}
Err(e) => return IncomingPlan::Failed(e),
}
// Let's get what we know about it, if anything - last 20, like desktop?
let visit_tuple = match fetch_visits(conn, &url, max_visits) {
Ok(v) => v,
Err(e) => return IncomingPlan::Failed(e),
};
// This all seems more messy than it should be - struggling to find the
// correct signature for fetch_visits.
// An improvement might be to do this via a temp table so we can dedupe
// and apply in one operation rather than the fetch, rust-merge and update
// we are doing here.
let (existing_page, existing_visits): (Option<FetchedVisitPage>, Vec<FetchedVisit>) =
match visit_tuple {
None => (None, Vec::new()),
Some((p, v)) => (Some(p), v),
};
let guid_changed = match existing_page {
Some(p) => p.guid != record.id,
None => false,
};
let mut cur_visit_map: HashSet<(VisitType, Timestamp)> =
HashSet::with_capacity(existing_visits.len());
for visit in &existing_visits {
// it should be impossible for us to have invalid visits locally, but...
let transition = match visit.visit_type {
Some(t) => t,
None => continue,
};
match clamp_visit_date(visit.visit_date) {
Ok(date_use) => {
cur_visit_map.insert((transition, date_use));
}
Err(_) => {
log::warn!("Ignored visit before 1993-01-23");
}
}
}
// If we already have MAX_RECORDS visits, then we will ignore incoming
// visits older than that, to avoid adding dupes of earlier visits.
// (Not really clear why 20 is magic, but what's good enough for desktop
// is good enough for us at this stage.)
// We should also consider pushing this deduping down into storage, where
// it can possibly do a better job directly in SQL or similar.
let earliest_allowed: SystemTime = if existing_visits.len() == max_visits {
existing_visits[existing_visits.len() - 1].visit_date.into()
} else {
UNIX_EPOCH
};
// work out which of the incoming visits we should apply.
let mut to_apply = Vec::with_capacity(record.visits.len());
for incoming_visit in record.visits {
let transition = match VisitType::from_primitive(incoming_visit.transition) {
Some(v) => v,
None => continue,
};
match clamp_visit_date(incoming_visit.date.into()) {
Ok(timestamp) => {
if earliest_allowed > timestamp.into() {
continue;
}
// If the entry isn't in our map we should add it.
let key = (transition, timestamp);
if !cur_visit_map.contains(&key) {
to_apply.push(HistoryRecordVisit {
date: timestamp.into(),
transition: transition as u8,
unknown_fields: incoming_visit.unknown_fields,
});
cur_visit_map.insert(key);
}
}
Err(()) => {
log::warn!("Ignored visit before 1993-01-23");
}
}
}
// Now we need to check the other attributes.
// Check if we should update title? For now, assume yes. It appears
// as though desktop always updates it.
if guid_changed || !to_apply.is_empty() {
let new_title = Some(record.title);
IncomingPlan::Apply {
url,
new_title,
visits: to_apply,
unknown_fields: record.unknown_fields,
}
} else {
IncomingPlan::Reconciled
}
}