910e62b5创建于 1月15日历史提交
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/bookmarks/browser/url_index.h"

#include <iterator>

#include "base/containers/adapters.h"
#include "base/logging.h"
#include "base/memory/raw_ptr.h"
#include "base/uuid.h"
#include "components/bookmarks/browser/url_and_title.h"
#include "components/bookmarks/common/url_load_stats.h"

namespace bookmarks {

namespace {

void AddTimeStatsForBookmark(BookmarkNode* node,
                             base::Time now,
                             UrlLoadStats* stats) {
  stats->avg_num_days_since_added += (now - node->date_added()).InDays();

  // It's possible that date_last_used hasn't been populated for this node.
  if (node->date_last_used() != base::Time()) {
    stats->used_url_bookmark_count += 1;
    int mru_days = std::max(0, (now - node->date_last_used()).InDays());
    stats->most_recently_used_bookmark_days =
        std::min<size_t>(mru_days, stats->most_recently_used_bookmark_days);
    stats->per_bookmark_num_days_since_used.push_back(mru_days);
  } else if (node->is_url()) {
    int mra_days = std::max(0, (now - node->date_added()).InDays());
    stats->per_bookmark_num_days_since_used.push_back(mra_days);
  }

  stats->most_recently_saved_bookmark_days =
      std::min<size_t>(std::max(0, (now - node->date_added()).InDays()),
                       stats->most_recently_saved_bookmark_days);

  stats->most_recently_saved_folder_days = std::min<size_t>(
      std::max(0, (now - node->parent()->date_added()).InDays()),
      stats->most_recently_saved_folder_days);
}

}  // namespace

UrlIndex::UrlIndex(std::unique_ptr<BookmarkNode> root)
    : root_(std::move(root)) {
  base::AutoLock url_lock(url_lock_);
  AddImpl(root_.get());
}

void UrlIndex::Add(BookmarkNode* parent,
                   size_t index,
                   std::unique_ptr<BookmarkNode> node) {
  base::AutoLock url_lock(url_lock_);
  AddImpl(parent->Add(std::move(node), index));
}

std::unique_ptr<BookmarkNode> UrlIndex::RemoveChildAt(
    BookmarkNode* parent,
    size_t index,
    std::set<GURL>* removed_urls) {
  base::AutoLock url_lock(url_lock_);
  RemoveImpl(parent->children()[index].get(), removed_urls);
  return parent->Remove(index);
}

void UrlIndex::SetUrl(BookmarkNode* node, const GURL& url) {
  base::AutoLock url_lock(url_lock_);
  RemoveImpl(node, nullptr);
  node->set_url(url);
  AddImpl(node);
}

void UrlIndex::SetTitle(BookmarkNode* node, const std::u16string& title) {
  // Acquiring the lock is necessary to avoid races with
  // UrlIndex::GetBookmarks().
  base::AutoLock url_lock(url_lock_);
  node->SetTitle(title);
}

void UrlIndex::GetNodesWithIconUrl(const GURL& icon_url,
                                   std::set<const BookmarkNode*>* nodes) {
  base::AutoLock url_lock(url_lock_);
  for (const BookmarkNode* node : nodes_ordered_by_url_set_) {
    if (node->icon_url() && icon_url == *node->icon_url()) {
      nodes->insert(node);
    }
  }
}

void UrlIndex::GetNodesByUrl(
    const GURL& url,
    std::vector<raw_ptr<const BookmarkNode, VectorExperimental>>* nodes) {
  base::AutoLock url_lock(url_lock_);
  auto range = nodes_ordered_by_url_set_.equal_range<GURL>(url);
  nodes->insert(nodes->end(), range.first, range.second);
}

bool UrlIndex::HasBookmarks() const {
  base::AutoLock url_lock(url_lock_);
  return !nodes_ordered_by_url_set_.empty();
}

UrlLoadStats UrlIndex::ComputeStats() const {
  base::AutoLock url_lock(url_lock_);

  base::Time now = base::Time::Now();
  UrlLoadStats stats;
  if (!nodes_ordered_by_url_set_.empty()) {
    stats.total_url_bookmark_count = nodes_ordered_by_url_set_.size();
    for (BookmarkNode* node : nodes_ordered_by_url_set_) {
      AddTimeStatsForBookmark(node, now, &stats);
    }
    stats.avg_num_days_since_added /= nodes_ordered_by_url_set_.size();
  }
  return stats;
}

bool UrlIndex::IsBookmarked(const GURL& url) {
  base::AutoLock url_lock(url_lock_);
  return IsBookmarkedNoLock(url);
}

std::vector<UrlAndTitle> UrlIndex::GetUniqueUrls() {
  std::vector<UrlAndTitle> bookmarks;
  base::AutoLock url_lock(url_lock_);
  const GURL* last_url = nullptr;
  for (auto i = nodes_ordered_by_url_set_.begin();
       i != nodes_ordered_by_url_set_.end(); ++i) {
    const GURL* url = &((*i)->url());
    // Only add unique URLs.
    if (!last_url || *url != *last_url) {
      UrlAndTitle bookmark;
      bookmark.url = *url;
      bookmark.title = (*i)->GetTitle();
      bookmarks.push_back(bookmark);
    }
    last_url = url;
  }
  return bookmarks;
}

UrlIndex::~UrlIndex() = default;

bool UrlIndex::IsBookmarkedNoLock(const GURL& url) {
  url_lock_.AssertAcquired();
  return (nodes_ordered_by_url_set_.find(url) !=
          nodes_ordered_by_url_set_.end());
}

void UrlIndex::AddImpl(BookmarkNode* node) {
  url_lock_.AssertAcquired();
  if (node->is_url()) {
    nodes_ordered_by_url_set_.insert(node);
  }
  for (const auto& child : node->children()) {
    AddImpl(child.get());
  }
}

void UrlIndex::RemoveImpl(BookmarkNode* node, std::set<GURL>* removed_urls) {
  url_lock_.AssertAcquired();
  if (node->is_url()) {
    auto i = nodes_ordered_by_url_set_.lower_bound(node);
    CHECK(i != nodes_ordered_by_url_set_.end());
    // i points to the first node with the URL, advance until we find the
    // node we're removing.
    while (*i != node) {
      ++i;
    }
    nodes_ordered_by_url_set_.erase(i);
    if (removed_urls && !IsBookmarkedNoLock(node->url())) {
      removed_urls->insert(node->url());
    }
  }
  for (const auto& child : base::Reversed(node->children())) {
    RemoveImpl(child.get(), removed_urls);
  }
}

}  // namespace bookmarks