1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
|
// Copyright 2022 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/commerce/core/bookmark_update_manager.h"
#include <algorithm>
#include <vector>
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/task/sequenced_task_runner.h"
#include "components/bookmarks/browser/bookmark_node.h"
#include "components/bookmarks/browser/bookmark_utils.h"
#include "components/commerce/core/commerce_feature_list.h"
#include "components/commerce/core/pref_names.h"
#include "components/commerce/core/price_tracking_utils.h"
#include "components/power_bookmarks/core/power_bookmark_utils.h"
#include "components/power_bookmarks/core/proto/shopping_specifics.pb.h"
#include "url/gurl.h"
namespace commerce {
BookmarkUpdateManager::BookmarkUpdateManager(ShoppingService* service,
bookmarks::BookmarkModel* model,
PrefService* prefs)
: shopping_service_(service),
bookmark_model_(model),
pref_service_(prefs) {}
BookmarkUpdateManager::~BookmarkUpdateManager() = default;
void BookmarkUpdateManager::ScheduleUpdate() {
// Check the kill switch. This enabled by default, but can be turned off in
// case we accidentally flood the backend with requests.
if (!base::FeatureList::IsEnabled(kCommerceAllowOnDemandBookmarkUpdates))
return;
// Make sure we don't double-schedule.
if (scheduled_task_)
return;
// By default, time is "null" meaning it is set to 0. In this state, read the
// preference once and then use the in-memory version from this point on.
if (last_update_time_.is_null()) {
last_update_time_ =
pref_service_->GetTime(kShoppingListBookmarkLastUpdateTime);
}
base::TimeDelta time_since_last = base::Time::Now() - last_update_time_;
base::TimeDelta interval = kShoppingListBookmarkpdateIntervalParam.Get();
int64_t ms_delay =
std::clamp((interval - time_since_last).InMilliseconds(),
base::Seconds(0L).InMilliseconds(), interval.InMilliseconds());
scheduled_task_ =
std::make_unique<base::CancelableOnceClosure>(base::BindOnce(
&BookmarkUpdateManager::RunUpdate, weak_ptr_factory_.GetWeakPtr()));
base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE, scheduled_task_->callback(), base::Milliseconds(ms_delay));
}
void BookmarkUpdateManager::CancelUpdates() {
if (scheduled_task_) {
scheduled_task_->Cancel();
scheduled_task_ = nullptr;
}
}
void BookmarkUpdateManager::RunUpdate() {
// Record the current time as last updated time and immediately schedule the
// next update.
last_update_time_ = base::Time::Now();
pref_service_->SetTime(kShoppingListBookmarkLastUpdateTime,
last_update_time_);
// If something like the enterprise policy was turned off, simply block the
// update logic. In the future we can observe the preference and remove or
// re-add the scheduled update, but this is easier for now.
if (!shopping_service_->IsShoppingListEligible())
return;
scheduled_task_ = nullptr;
ScheduleUpdate();
std::vector<const bookmarks::BookmarkNode*> nodes =
shopping_service_->GetAllShoppingBookmarks();
if (nodes.empty()) {
return;
}
size_t current_batch_count = 0;
size_t total_bookmarks_processed = 0;
pending_update_batches_.emplace();
for (auto* node : nodes) {
// If we've reached the max for a batch, push a new vector onto the queue
// and continue.
if (current_batch_count >=
shopping_service_->GetMaxProductBookmarkUpdatesPerBatch()) {
pending_update_batches_.emplace();
current_batch_count = 0;
}
pending_update_batches_.back().push_back(node->id());
current_batch_count++;
total_bookmarks_processed++;
// If we've reached the maximum number of bookmarks we're willing to
// update, stop.
if (total_bookmarks_processed >= kShoppingListBookmarkUpdateBatchMaxParam) {
break;
}
}
StartNextBatch();
}
void BookmarkUpdateManager::StartNextBatch() {
if (pending_update_batches_.empty()) {
return;
}
expected_bookmark_updates_ = pending_update_batches_.front().size();
received_bookmark_updates_ = 0;
std::vector<int64_t> ids = std::move(pending_update_batches_.front());
pending_update_batches_.pop();
shopping_service_->GetUpdatedProductInfoForBookmarks(
std::move(ids),
base::BindRepeating(&BookmarkUpdateManager::HandleOnDemandResponse,
weak_ptr_factory_.GetWeakPtr()));
}
void BookmarkUpdateManager::HandleOnDemandResponse(
const int64_t bookmark_id,
const GURL& url,
std::optional<ProductInfo> info) {
received_bookmark_updates_++;
if (received_bookmark_updates_ >= expected_bookmark_updates_) {
StartNextBatch();
}
if (!info.has_value())
return;
const bookmarks::BookmarkNode* node =
bookmarks::GetBookmarkNodeByID(bookmark_model_, bookmark_id);
std::unique_ptr<power_bookmarks::PowerBookmarkMeta> meta =
power_bookmarks::GetNodePowerBookmarkMeta(bookmark_model_, node);
if (!meta || !meta->has_shopping_specifics())
return;
if (PopulateOrUpdateBookmarkMetaIfNeeded(meta.get(), info.value())) {
power_bookmarks::SetNodePowerBookmarkMeta(bookmark_model_, node,
std::move(meta));
}
}
} // namespace commerce
|