Index: chrome/browser/browsing_data/browsing_data_remover.cc |
diff --git a/chrome/browser/browsing_data/browsing_data_remover.cc b/chrome/browser/browsing_data/browsing_data_remover.cc |
index cc28d811f5cf714894cc6e8b8088d55c4368443b..a8c7c0dfac9173d460fd37e69e0c14c38501930a 100644 |
--- a/chrome/browser/browsing_data/browsing_data_remover.cc |
+++ b/chrome/browser/browsing_data/browsing_data_remover.cc |
@@ -299,12 +299,20 @@ BrowsingDataRemover::BrowsingDataRemover( |
} |
BrowsingDataRemover::~BrowsingDataRemover() { |
- // If we are still removing data, notify observers so they can remove |
- // themselves from the observer list. |
+ if (!task_queue_.empty()) { |
+ VLOG(1) << "BrowsingDataRemover shuts down with " << task_queue_.size() |
+ << " pending tasks"; |
+ } |
+ |
+ // If we are still removing data, notify observers that their task has been |
+ // (albeit unsucessfuly) processed, so they can unregister themselves. |
// TODO(bauerb): If it becomes a problem that browsing data might not actually |
// be fully cleared when an observer is notified, add a success flag. |
- if (is_removing_) |
- Notify(); |
+ while (!task_queue_.empty()) { |
+ if (observer_list_.HasObserver(task_queue_.front().observer)) |
+ task_queue_.front().observer->OnBrowsingDataRemoverDone(); |
+ task_queue_.pop(); |
+ } |
} |
void BrowsingDataRemover::Shutdown() { |
@@ -322,20 +330,84 @@ void BrowsingDataRemover::SetRemoving(bool is_removing) { |
void BrowsingDataRemover::Remove(const TimeRange& time_range, |
int remove_mask, |
int origin_type_mask) { |
- // Any instance of BrowsingDataFilterBuilder that |IsEmptyBlacklist()| |
- // is OK to pass here. |
- RegistrableDomainFilterBuilder builder( |
- RegistrableDomainFilterBuilder::BLACKLIST); |
- DCHECK(builder.IsEmptyBlacklist()); |
- RemoveImpl(time_range, remove_mask, builder, origin_type_mask); |
+ RemoveInternal(time_range, remove_mask, origin_type_mask, |
+ std::unique_ptr<RegistrableDomainFilterBuilder>(), nullptr); |
+} |
+ |
+void BrowsingDataRemover::RemoveAndReply( |
+ const TimeRange& time_range, |
+ int remove_mask, |
+ int origin_type_mask, |
+ Observer* observer) { |
+ DCHECK(observer); |
+ RemoveInternal(time_range, remove_mask, origin_type_mask, |
+ std::unique_ptr<RegistrableDomainFilterBuilder>(), observer); |
} |
void BrowsingDataRemover::RemoveWithFilter( |
const TimeRange& time_range, |
int remove_mask, |
int origin_type_mask, |
- const BrowsingDataFilterBuilder& filter_builder) { |
- RemoveImpl(time_range, remove_mask, filter_builder, origin_type_mask); |
+ std::unique_ptr<BrowsingDataFilterBuilder> filter_builder) { |
+ DCHECK(filter_builder); |
+ RemoveInternal(time_range, remove_mask, origin_type_mask, |
+ std::move(filter_builder), nullptr); |
+} |
+ |
+void BrowsingDataRemover::RemoveWithFilterAndReply( |
+ const TimeRange& time_range, |
+ int remove_mask, |
+ int origin_type_mask, |
+ std::unique_ptr<BrowsingDataFilterBuilder> filter_builder, |
+ Observer* observer) { |
+ DCHECK(filter_builder); |
+ DCHECK(observer); |
+ RemoveInternal(time_range, remove_mask, origin_type_mask, |
+ std::move(filter_builder), observer); |
+} |
+ |
+void BrowsingDataRemover::RemoveInternal( |
+ const TimeRange& time_range, |
+ int remove_mask, |
+ int origin_type_mask, |
+ std::unique_ptr<BrowsingDataFilterBuilder> filter_builder, |
+ Observer* observer) { |
+ DCHECK(!observer || observer_list_.HasObserver(observer)) |
+ << "Every observer must register itself (by calling AddObserver()) " |
+ << "before observing a removal task."; |
+ |
+ // Remove() and RemoveAndReply() pass a null pointer to indicate no filter. |
+ // No filter is equivalent to one that |IsEmptyBlacklist()|. |
+ if (!filter_builder) { |
+ filter_builder = base::WrapUnique(new RegistrableDomainFilterBuilder( |
+ RegistrableDomainFilterBuilder::BLACKLIST)); |
+ DCHECK(filter_builder->IsEmptyBlacklist()); |
+ } |
+ |
+ task_queue_.emplace( |
+ time_range, |
+ remove_mask, |
+ origin_type_mask, |
+ std::move(filter_builder), |
+ observer); |
+ |
+ // If this is the only scheduled task, execute it immediately. Otherwise, |
+ // it will be automatically executed when all tasks scheduled before it |
+ // finish. |
+ if (task_queue_.size() == 1) { |
+ SetRemoving(true); |
+ RunNextTask(); |
+ } |
+} |
+ |
+void BrowsingDataRemover::RunNextTask() { |
+ DCHECK(!task_queue_.empty()); |
+ const RemovalTask& removal_task = task_queue_.front(); |
+ |
+ RemoveImpl(removal_task.time_range, |
+ removal_task.remove_mask, |
+ *removal_task.filter_builder, |
+ removal_task.origin_type_mask); |
} |
void BrowsingDataRemover::RemoveImpl( |
@@ -350,7 +422,6 @@ void BrowsingDataRemover::RemoveImpl( |
// delete_end, even though they should've used base::Time::Max(). |
DCHECK_NE(base::Time(), time_range.end); |
- SetRemoving(true); |
delete_begin_ = time_range.begin; |
delete_end_ = time_range.end; |
remove_mask_ = remove_mask; |
@@ -1049,6 +1120,19 @@ int BrowsingDataRemover::GetLastUsedOriginTypeMask() { |
return origin_type_mask_; |
} |
+BrowsingDataRemover::RemovalTask::RemovalTask( |
+ const TimeRange& time_range, |
+ int remove_mask, |
+ int origin_type_mask, |
+ std::unique_ptr<BrowsingDataFilterBuilder> filter_builder, |
+ Observer* observer) |
+ : time_range(time_range), |
+ remove_mask(remove_mask), |
+ origin_type_mask(origin_type_mask), |
+ filter_builder(std::move(filter_builder)), |
+ observer(observer) {} |
+ |
+BrowsingDataRemover::RemovalTask::~RemovalTask() {} |
void BrowsingDataRemover::ClearSettingsForOneTypeWithPredicate( |
HostContentSettingsMap* content_settings_map, |
@@ -1108,8 +1192,40 @@ void BrowsingDataRemover::OnKeywordsLoaded() { |
} |
void BrowsingDataRemover::Notify() { |
- SetRemoving(false); |
- FOR_EACH_OBSERVER(Observer, observer_list_, OnBrowsingDataRemoverDone()); |
+ // Some tests call |RemoveImpl| directly, without using the task scheduler. |
+ // TODO(msramek): Improve those tests so we don't have to do this. Tests |
+ // relying on |RemoveImpl| do so because they need to pass in |
+ // BrowsingDataFilterBuilder while still keeping ownership of it. Making |
+ // BrowsingDataFilterBuilder copyable would solve this. |
+ if (!is_removing_) { |
+ DCHECK(task_queue_.empty()); |
+ return; |
+ } |
+ |
+ // Inform the observer of the current task unless it has unregistered |
+ // itself in the meantime. |
+ DCHECK(!task_queue_.empty()); |
+ |
+ if (task_queue_.front().observer != nullptr && |
+ observer_list_.HasObserver(task_queue_.front().observer)) { |
+ task_queue_.front().observer->OnBrowsingDataRemoverDone(); |
+ } |
+ |
+ task_queue_.pop(); |
+ |
+ if (task_queue_.empty()) { |
+ // All removal tasks have finished. Inform the observers that we're idle. |
+ SetRemoving(false); |
+ return; |
+ } |
+ |
+ // Yield to the UI thread before executing the next removal task. |
+ // TODO(msramek): Consider also adding a backoff if too many tasks |
+ // are scheduled. |
+ BrowserThread::PostTask( |
+ BrowserThread::UI, FROM_HERE, |
+ base::Bind(&BrowsingDataRemover::RunNextTask, |
+ weak_ptr_factory_.GetWeakPtr())); |
} |
void BrowsingDataRemover::NotifyIfDone() { |