blob: 2e6f02910315649eab240f4ab8ee7d3fc35fa52c [file] [log] [blame]
// Copyright 2022 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/user_notes/browser/user_note_utils.h"
#include "base/unguessable_token.h"
#include "components/user_notes/browser/frame_user_note_changes.h"
#include "components/user_notes/browser/user_note_manager.h"
#include "components/user_notes/browser/user_note_service.h"
#include "components/user_notes/interfaces/user_note_metadata_snapshot.h"
#include "components/user_notes/model/user_note_metadata.h"
#include "content/public/browser/render_frame_host.h"
#include "url/gurl.h"
namespace user_notes {
std::vector<std::unique_ptr<FrameUserNoteChanges>> CalculateNoteChanges(
const UserNoteService& note_service,
const std::vector<content::RenderFrameHost*>& rfhs,
const UserNoteMetadataSnapshot& metadata_snapshot) {
std::vector<std::unique_ptr<FrameUserNoteChanges>> result;
// Create an empty metadata map to simplify the algorithm below for cases
// where there's no entry in the metadata snapshot for a frame's URL.
UserNoteMetadataSnapshot::IdToMetadataMap empty_map;
for (content::RenderFrameHost* rfh : rfhs) {
// Notes should only be processed for the primary page.
DCHECK(rfh->GetMainFrame()->IsInPrimaryMainFrame());
UserNoteManager* notes_manager =
UserNoteManager::GetForPage(rfh->GetPage());
if (!notes_manager) {
// Frame is part of an uninteresting page (for User Notes purposes), such
// as the error document.
continue;
}
const std::vector<UserNoteInstance*>& instances =
notes_manager->GetAllNoteInstances();
GURL url = rfh->GetLastCommittedURL();
const UserNoteMetadataSnapshot::IdToMetadataMap* map_for_url =
metadata_snapshot.GetMapForUrl(url);
const UserNoteMetadataSnapshot::IdToMetadataMap* metadata_map =
map_for_url ? map_for_url : &empty_map;
std::vector<base::UnguessableToken> added;
std::vector<base::UnguessableToken> modified;
std::vector<base::UnguessableToken> removed;
// First, iterate through the existing notes in the frame to detect notes
// that have been removed and notes that have been modified.
for (UserNoteInstance* instance : instances) {
const UserNote& model = instance->model();
const auto& metadata_it = metadata_map->find(model.id());
if (metadata_it == metadata_map->end()) {
// In-progress notes have an instance in the manager, but are not part
// of the metadata snapshot because they haven't been persisted to disk
// yet. Ignore them.
if (note_service.IsNoteInProgress(model.id())) {
continue;
}
if (url == model.target().target_page()) {
// Note has been removed.
removed.emplace_back(model.id());
}
} else if (metadata_it->second->modification_date() >
model.metadata().modification_date() ||
note_service.IsNoteInProgress(model.id())) {
// This note has been modified or created locally (which is treated as a
// modification since both the instance and the partial model exist).
modified.emplace_back(model.id());
}
}
// Finally, iterate through the notes in the metadata to detect notes that
// have been added.
for (const auto& metadata_it : *metadata_map) {
const base::UnguessableToken& id = metadata_it.first;
if (!notes_manager->GetNoteInstance(id)) {
// This is a new note.
added.emplace_back(id);
}
}
if (!added.empty() || !removed.empty() || !modified.empty()) {
result.emplace_back(std::make_unique<FrameUserNoteChanges>(
note_service.GetSafeRef(), rfh, std::move(added), std::move(modified),
std::move(removed)));
}
}
return result;
}
} // namespace user_notes