blob: 909cf74e4f79050a07d734272f974220c113c901 [file] [log] [blame]
// Copyright (c) 2012 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.
#import "ios/chrome/browser/ui/external_file_remover_impl.h"
#include "base/bind_helpers.h"
#include "base/logging.h"
#import "base/mac/bind_objc_block.h"
#include "base/strings/sys_string_conversions.h"
#include "base/task_scheduler/post_task.h"
#include "components/bookmarks/browser/bookmark_model.h"
#include "components/sessions/core/tab_restore_service.h"
#include "ios/chrome/browser/bookmarks/bookmark_model_factory.h"
#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
#include "ios/chrome/browser/chrome_url_util.h"
#include "ios/chrome/browser/sessions/ios_chrome_tab_restore_service_factory.h"
#import "ios/chrome/browser/tabs/tab_model.h"
#import "ios/chrome/browser/tabs/tab_model_list.h"
#import "ios/chrome/browser/ui/external_file_controller.h"
#include "ios/web/public/web_thread.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace {
// Empty callback. The closure owned by |closure_runner| will be invoked as
// part of the destructor of base::ScopedClosureRunner (which takes care of
// checking for null closure).
void RunCallback(base::ScopedClosureRunner closure_runner) {}
} // namespace
ExternalFileRemoverImpl::ExternalFileRemoverImpl(
ios::ChromeBrowserState* browser_state,
sessions::TabRestoreService* tab_restore_service)
: tab_restore_service_(tab_restore_service),
browser_state_(browser_state),
weak_ptr_factory_(this) {
DCHECK(tab_restore_service_);
tab_restore_service_->AddObserver(this);
}
ExternalFileRemoverImpl::~ExternalFileRemoverImpl() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
void ExternalFileRemoverImpl::RemoveAfterDelay(base::TimeDelta delay,
base::OnceClosure callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::ScopedClosureRunner closure_runner =
base::ScopedClosureRunner(std::move(callback));
bool remove_all_files = delay == base::TimeDelta::FromSeconds(0);
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
base::Bind(&ExternalFileRemoverImpl::RemoveFiles,
weak_ptr_factory_.GetWeakPtr(), remove_all_files,
base::Passed(&closure_runner)),
delay);
}
void ExternalFileRemoverImpl::Shutdown() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (tab_restore_service_) {
tab_restore_service_->RemoveObserver(this);
tab_restore_service_ = nullptr;
}
delayed_file_remove_requests_.clear();
}
void ExternalFileRemoverImpl::TabRestoreServiceChanged(
sessions::TabRestoreService* service) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (service->IsLoaded())
return;
tab_restore_service_->RemoveObserver(this);
tab_restore_service_ = nullptr;
std::vector<DelayedFileRemoveRequest> delayed_file_remove_requests;
delayed_file_remove_requests = std::move(delayed_file_remove_requests_);
for (DelayedFileRemoveRequest& request : delayed_file_remove_requests) {
RemoveFiles(request.remove_all_files, std::move(request.closure_runner));
}
}
void ExternalFileRemoverImpl::TabRestoreServiceDestroyed(
sessions::TabRestoreService* service) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
NOTREACHED() << "Should never happen as unregistration happen in Shutdown";
}
void ExternalFileRemoverImpl::Remove(bool all_files,
base::ScopedClosureRunner closure_runner) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!tab_restore_service_) {
RemoveFiles(all_files, std::move(closure_runner));
return;
}
// Removal is delayed until tab restore loading completes.
DCHECK(!tab_restore_service_->IsLoaded());
DelayedFileRemoveRequest request = {all_files, std::move(closure_runner)};
delayed_file_remove_requests_.push_back(std::move(request));
if (delayed_file_remove_requests_.size() == 1)
tab_restore_service_->LoadTabsFromLastSession();
}
void ExternalFileRemoverImpl::RemoveFiles(
bool all_files,
base::ScopedClosureRunner closure_runner) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
NSSet* referenced_files = all_files ? GetReferencedExternalFiles() : nil;
const NSInteger kMinimumAgeInDays = 30;
NSInteger age_in_days = all_files ? 0 : kMinimumAgeInDays;
base::PostTaskWithTraitsAndReply(
FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
base::BindBlockArc(^{
[ExternalFileController removeFilesExcluding:referenced_files
olderThan:age_in_days];
}),
base::Bind(&RunCallback, base::Passed(&closure_runner)));
}
NSSet* ExternalFileRemoverImpl::GetReferencedExternalFiles() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Add files from all TabModels.
NSMutableSet* referenced_external_files = [NSMutableSet set];
for (TabModel* tab_model in TabModelList::GetTabModelsForChromeBrowserState(
browser_state_)) {
NSSet* tab_model_files = [tab_model currentlyReferencedExternalFiles];
if (tab_model_files) {
[referenced_external_files unionSet:tab_model_files];
}
}
bookmarks::BookmarkModel* bookmark_model =
ios::BookmarkModelFactory::GetForBrowserState(browser_state_);
// Check if the bookmark model is loaded.
if (!bookmark_model || !bookmark_model->loaded())
return referenced_external_files;
// Add files from Bookmarks.
std::vector<bookmarks::BookmarkModel::URLAndTitle> bookmarks;
bookmark_model->GetBookmarks(&bookmarks);
for (const auto& bookmark : bookmarks) {
GURL bookmark_url = bookmark.url;
if (UrlIsExternalFileReference(bookmark_url)) {
[referenced_external_files
addObject:base::SysUTF8ToNSString(bookmark_url.ExtractFileName())];
}
}
return referenced_external_files;
}