Commit 975f86a4 authored by Shuhei Takahashi's avatar Shuhei Takahashi Committed by Commit Bot

recent: Introduce RecentModel.

This change introduces core concepts of Recent file system:

RecentModel:

  The most important class owning RecentSources.

  The only API it provides is GetRecentFiles() which queries
  RecentSources for recently modified files and build a list of
  recently modified files across file systems, and also caches the
  result for better file system operation performance.

  Files are represented as FileSystemURL.

RecentSource:

  Interface class for a source of recent files.

  This interface will be implemented for several sources such as local
  storages and cloud storages so that they appear in Recent file
  system.

RecentContext:

  Holds several "context" objects necessary for operations in
  RecentModel and RecentSource.

Bug: 742722
Test: unit_tests
Change-Id: I2a2ff1fa28e8cf5c705ad1ee6bc75a39b49b2f0b
Reviewed-on: https://chromium-review.googlesource.com/588950Reviewed-by: default avatarTomasz Mikolajewski <mtomasz@chromium.org>
Commit-Queue: Shuhei Takahashi <nya@chromium.org>
Cr-Commit-Position: refs/heads/master@{#491968}
parent 81a9a66b
...@@ -714,6 +714,14 @@ source_set("chromeos") { ...@@ -714,6 +714,14 @@ source_set("chromeos") {
"fileapi/recent_async_file_util.h", "fileapi/recent_async_file_util.h",
"fileapi/recent_backend_delegate.cc", "fileapi/recent_backend_delegate.cc",
"fileapi/recent_backend_delegate.h", "fileapi/recent_backend_delegate.h",
"fileapi/recent_context.cc",
"fileapi/recent_context.h",
"fileapi/recent_model.cc",
"fileapi/recent_model.h",
"fileapi/recent_model_factory.cc",
"fileapi/recent_model_factory.h",
"fileapi/recent_source.cc",
"fileapi/recent_source.h",
"first_run/drive_first_run_controller.cc", "first_run/drive_first_run_controller.cc",
"first_run/drive_first_run_controller.h", "first_run/drive_first_run_controller.h",
"first_run/first_run.cc", "first_run/first_run.cc",
...@@ -1783,6 +1791,8 @@ source_set("unit_tests") { ...@@ -1783,6 +1791,8 @@ source_set("unit_tests") {
"fileapi/external_file_url_util_unittest.cc", "fileapi/external_file_url_util_unittest.cc",
"fileapi/file_access_permissions_unittest.cc", "fileapi/file_access_permissions_unittest.cc",
"fileapi/file_system_backend_unittest.cc", "fileapi/file_system_backend_unittest.cc",
"fileapi/recent_model_unittest.cc",
"fileapi/test/fake_recent_source.cc",
"hats/hats_finch_helper_unittest.cc", "hats/hats_finch_helper_unittest.cc",
"hats/hats_notification_controller_unittest.cc", "hats/hats_notification_controller_unittest.cc",
"input_method/browser_state_monitor_unittest.cc", "input_method/browser_state_monitor_unittest.cc",
......
// Copyright 2017 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 "chrome/browser/chromeos/fileapi/recent_context.h"
#include <utility>
namespace chromeos {
RecentContext::RecentContext() : is_valid_(false) {}
RecentContext::RecentContext(storage::FileSystemContext* file_system_context,
const GURL& origin)
: is_valid_(true),
file_system_context_(file_system_context),
origin_(origin) {}
RecentContext::RecentContext(const RecentContext& other) = default;
RecentContext::~RecentContext() = default;
RecentContext& RecentContext::operator=(const RecentContext& other) = default;
} // namespace chromeos
// Copyright 2017 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.
#ifndef CHROME_BROWSER_CHROMEOS_FILEAPI_RECENT_CONTEXT_H_
#define CHROME_BROWSER_CHROMEOS_FILEAPI_RECENT_CONTEXT_H_
#include "base/memory/ref_counted.h"
#include "storage/browser/fileapi/file_system_context.h"
#include "url/gurl.h"
namespace chromeos {
// Holds several "context" objects to be used for operations in Recent file
// system.
class RecentContext {
public:
RecentContext();
RecentContext(storage::FileSystemContext* file_system_context,
const GURL& origin);
RecentContext(const RecentContext& other);
~RecentContext();
RecentContext& operator=(const RecentContext& other);
bool is_valid() const { return is_valid_; }
storage::FileSystemContext* file_system_context() const {
return file_system_context_.get();
}
const GURL& origin() const { return origin_; }
private:
bool is_valid_;
scoped_refptr<storage::FileSystemContext> file_system_context_;
GURL origin_;
};
} // namespace chromeos
#endif // CHROME_BROWSER_CHROMEOS_FILEAPI_RECENT_CONTEXT_H_
// Copyright 2017 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 "chrome/browser/chromeos/fileapi/recent_model.h"
#include <algorithm>
#include <iterator>
#include <string>
#include <utility>
#include "base/files/file_path.h"
#include "base/memory/ptr_util.h"
#include "base/strings/stringprintf.h"
#include "base/time/time.h"
#include "chrome/browser/chromeos/fileapi/recent_context.h"
#include "chrome/browser/chromeos/fileapi/recent_model_factory.h"
#include "chrome/browser/chromeos/fileapi/recent_source.h"
#include "content/public/browser/browser_thread.h"
#include "storage/browser/fileapi/file_system_context.h"
using content::BrowserThread;
namespace chromeos {
namespace {
// Recent file cache will be cleared this duration after it is built.
constexpr base::TimeDelta kCacheExpiration = base::TimeDelta::FromSeconds(10);
std::vector<std::unique_ptr<RecentSource>> CreateDefaultSources(
Profile* profile) {
std::vector<std::unique_ptr<RecentSource>> sources;
// TODO(nya): Add source implementations.
return sources;
}
} // namespace
const size_t kMaxFilesFromSingleSource = 1000;
// static
RecentModel* RecentModel::GetForProfile(Profile* profile) {
return RecentModelFactory::GetForProfile(profile);
}
// static
std::unique_ptr<RecentModel> RecentModel::CreateForTest(
std::vector<std::unique_ptr<RecentSource>> sources) {
return base::WrapUnique(new RecentModel(std::move(sources)));
}
RecentModel::RecentModel(Profile* profile)
: RecentModel(CreateDefaultSources(profile)) {}
RecentModel::RecentModel(std::vector<std::unique_ptr<RecentSource>> sources)
: sources_(std::move(sources)), weak_ptr_factory_(this) {}
RecentModel::~RecentModel() {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
}
void RecentModel::GetRecentFiles(RecentContext context,
GetRecentFilesCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
// Use cache if available.
if (cached_files_.has_value()) {
std::move(callback).Run(cached_files_.value());
return;
}
bool builder_already_running = !pending_callbacks_.empty();
pending_callbacks_.emplace_back(std::move(callback));
// If a builder is already running, just enqueue the callback and return.
if (builder_already_running)
return;
// Start building a recent file list.
DCHECK_EQ(0, num_inflight_sources_);
DCHECK(intermediate_files_.empty());
num_inflight_sources_ = sources_.size();
if (sources_.empty()) {
OnGetRecentFilesCompleted();
return;
}
for (const auto& source : sources_) {
source->GetRecentFiles(context,
base::BindOnce(&RecentModel::OnGetRecentFiles,
weak_ptr_factory_.GetWeakPtr()));
}
}
void RecentModel::OnGetRecentFiles(RecentFileList files) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK_LT(0, num_inflight_sources_);
intermediate_files_.insert(intermediate_files_.end(),
std::make_move_iterator(files.begin()),
std::make_move_iterator(files.end()));
--num_inflight_sources_;
if (num_inflight_sources_ == 0)
OnGetRecentFilesCompleted();
}
void RecentModel::OnGetRecentFilesCompleted() {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK_EQ(0, num_inflight_sources_);
DCHECK(!cached_files_.has_value());
cached_files_ = RecentFileList();
cached_files_.value().swap(intermediate_files_);
DCHECK(cached_files_.has_value());
DCHECK(intermediate_files_.empty());
// Starts a timer to clear cache.
cache_clear_timer_.Start(
FROM_HERE, kCacheExpiration,
base::Bind(&RecentModel::ClearCache, weak_ptr_factory_.GetWeakPtr()));
// Invoke all pending callbacks.
std::vector<GetRecentFilesCallback> callbacks_to_call;
callbacks_to_call.swap(pending_callbacks_);
DCHECK(pending_callbacks_.empty());
DCHECK(!callbacks_to_call.empty());
for (auto& callback : callbacks_to_call)
std::move(callback).Run(cached_files_.value());
}
void RecentModel::ClearCache() {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
cached_files_.reset();
}
} // namespace chromeos
// Copyright 2017 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.
#ifndef CHROME_BROWSER_CHROMEOS_FILEAPI_RECENT_MODEL_H_
#define CHROME_BROWSER_CHROMEOS_FILEAPI_RECENT_MODEL_H_
#include <memory>
#include <vector>
#include "base/callback_forward.h"
#include "base/files/file_path.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "components/keyed_service/core/keyed_service.h"
#include "storage/browser/fileapi/file_system_url.h"
class Profile;
namespace chromeos {
class RecentContext;
class RecentModelFactory;
class RecentSource;
// The maximum number of files from a single source.
extern const size_t kMaxFilesFromSingleSource;
// Provides a list of recently modified files.
//
// All member functions must be called on the IO thread unless stated otherwise.
class RecentModel : public KeyedService {
public:
using RecentFileList = std::vector<storage::FileSystemURL>;
using GetRecentFilesCallback =
base::OnceCallback<void(const RecentFileList& files)>;
~RecentModel() override;
// Returns an instance for the given profile.
// This function must be called on the UI thread.
static RecentModel* GetForProfile(Profile* profile);
// Creates an instance with given sources. Only for testing.
// This function can be called on any thread.
static std::unique_ptr<RecentModel> CreateForTest(
std::vector<std::unique_ptr<RecentSource>> sources);
// Returns a list of recent files by querying sources. Results are internally
// cached for better performance.
void GetRecentFiles(RecentContext context, GetRecentFilesCallback callback);
private:
friend class RecentModelFactory;
explicit RecentModel(Profile* profile);
explicit RecentModel(std::vector<std::unique_ptr<RecentSource>> sources);
void OnGetRecentFiles(RecentFileList files);
void OnGetRecentFilesCompleted();
void ClearCache();
const std::vector<std::unique_ptr<RecentSource>> sources_;
// Cached RecentFileList.
base::Optional<RecentFileList> cached_files_ = base::nullopt;
// Timer to clear the cache.
base::OneShotTimer cache_clear_timer_;
// While a recent file list is built, this vector contains callbacks to be
// invoked with the new list.
std::vector<GetRecentFilesCallback> pending_callbacks_;
// Number of in-flight sources building recent file lists.
int num_inflight_sources_ = 0;
// Intermediate container of recent files while building a list.
RecentFileList intermediate_files_;
base::WeakPtrFactory<RecentModel> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(RecentModel);
};
} // namespace chromeos
#endif // CHROME_BROWSER_CHROMEOS_FILEAPI_RECENT_MODEL_H_
// Copyright 2017 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 "chrome/browser/chromeos/fileapi/recent_model_factory.h"
#include <algorithm>
#include <iterator>
#include <string>
#include <utility>
#include "chrome/browser/chromeos/fileapi/recent_model.h"
#include "chrome/browser/profiles/incognito_helpers.h"
#include "chrome/browser/profiles/profile.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
namespace chromeos {
// static
RecentModel* RecentModelFactory::model_for_test_ = nullptr;
// static
RecentModel* RecentModelFactory::GetForProfile(Profile* profile) {
return static_cast<RecentModel*>(
GetInstance()->GetServiceForBrowserContext(profile, true));
}
// static
RecentModel* RecentModelFactory::SetForProfileAndUseForTest(
Profile* profile,
std::unique_ptr<RecentModel> model) {
DCHECK(model);
DCHECK(!model_for_test_);
RecentModel* saved_model = model.get();
model_for_test_ = model.release();
KeyedService* used_model = GetInstance()->SetTestingFactoryAndUse(
profile,
[](content::BrowserContext* context) -> std::unique_ptr<KeyedService> {
std::unique_ptr<KeyedService> model(model_for_test_);
model_for_test_ = nullptr;
return model;
});
DCHECK_EQ(used_model, saved_model);
DCHECK(!model_for_test_);
return saved_model;
}
RecentModelFactory::RecentModelFactory()
: BrowserContextKeyedServiceFactory(
"RecentModel",
BrowserContextDependencyManager::GetInstance()) {}
RecentModelFactory::~RecentModelFactory() = default;
// static
RecentModelFactory* RecentModelFactory::GetInstance() {
return base::Singleton<RecentModelFactory>::get();
}
content::BrowserContext* RecentModelFactory::GetBrowserContextToUse(
content::BrowserContext* context) const {
return chrome::GetBrowserContextRedirectedInIncognito(context);
}
KeyedService* RecentModelFactory::BuildServiceInstanceFor(
content::BrowserContext* context) const {
Profile* profile = Profile::FromBrowserContext(context);
return new RecentModel(profile);
}
} // namespace chromeos
// Copyright 2017 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.
#ifndef CHROME_BROWSER_CHROMEOS_FILEAPI_RECENT_MODEL_FACTORY_H_
#define CHROME_BROWSER_CHROMEOS_FILEAPI_RECENT_MODEL_FACTORY_H_
#include <memory>
#include "base/macros.h"
#include "base/memory/singleton.h"
#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
class Profile;
namespace chromeos {
class RecentModel;
class RecentModelFactory : public BrowserContextKeyedServiceFactory {
public:
// Returns the RecentModel for |profile|, creating it if not created yet.
static RecentModel* GetForProfile(Profile* profile);
// Sets the RecentModel for |profile| for testing.
static RecentModel* SetForProfileAndUseForTest(
Profile* profile,
std::unique_ptr<RecentModel> model);
// Returns the singleton RecentModelFactory instance.
static RecentModelFactory* GetInstance();
private:
friend struct base::DefaultSingletonTraits<RecentModelFactory>;
RecentModelFactory();
~RecentModelFactory() override;
// BrowserContextKeyedServiceFactory overrides.
content::BrowserContext* GetBrowserContextToUse(
content::BrowserContext* context) const override;
KeyedService* BuildServiceInstanceFor(
content::BrowserContext* context) const override;
static RecentModel* model_for_test_;
DISALLOW_COPY_AND_ASSIGN(RecentModelFactory);
};
} // namespace chromeos
#endif // CHROME_BROWSER_CHROMEOS_FILEAPI_RECENT_MODEL_FACTORY_H_
// Copyright 2017 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 <algorithm>
#include <string>
#include <utility>
#include "base/files/file_path.h"
#include "base/memory/ptr_util.h"
#include "chrome/browser/chromeos/fileapi/recent_context.h"
#include "chrome/browser/chromeos/fileapi/recent_model.h"
#include "chrome/browser/chromeos/fileapi/recent_model_factory.h"
#include "chrome/browser/chromeos/fileapi/test/fake_recent_source.h"
#include "chrome/test/base/testing_profile.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "storage/browser/fileapi/file_system_url.h"
#include "storage/common/fileapi/file_system_types.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace chromeos {
namespace {
using RecentFileList = RecentModel::RecentFileList;
class RecentModelTest : public testing::Test {
public:
RecentModelTest() = default;
protected:
RecentFileList BuildModelAndGetRecentFiles(
std::vector<std::unique_ptr<RecentSource>> sources) {
RecentModel* model = RecentModelFactory::SetForProfileAndUseForTest(
&profile_, RecentModel::CreateForTest(std::move(sources)));
RecentFileList files_out;
base::RunLoop run_loop;
model->GetRecentFiles(
RecentContext(),
base::BindOnce(
[](base::RunLoop* run_loop, RecentModel::RecentFileList* files_out,
const RecentModel::RecentFileList& files) {
*files_out = files;
run_loop->Quit();
},
&run_loop, &files_out));
run_loop.Run();
return files_out;
}
content::TestBrowserThreadBundle thread_bundle_;
TestingProfile profile_;
};
storage::FileSystemURL MakeFileSystemURL(const std::string& name) {
return storage::FileSystemURL::CreateForTest(
GURL(), // origin
storage::kFileSystemTypeNativeLocal, base::FilePath(name));
}
TEST_F(RecentModelTest, GetRecentFiles) {
auto source1 = base::MakeUnique<FakeRecentSource>();
source1->AddFile(MakeFileSystemURL("aaa.jpg"));
source1->AddFile(MakeFileSystemURL("ccc.jpg"));
auto source2 = base::MakeUnique<FakeRecentSource>();
source2->AddFile(MakeFileSystemURL("bbb.jpg"));
source2->AddFile(MakeFileSystemURL("ddd.jpg"));
std::vector<std::unique_ptr<RecentSource>> sources;
sources.emplace_back(std::move(source1));
sources.emplace_back(std::move(source2));
RecentFileList files = BuildModelAndGetRecentFiles(std::move(sources));
std::sort(files.begin(), files.end(), storage::FileSystemURL::Comparator());
ASSERT_EQ(4u, files.size());
EXPECT_EQ("aaa.jpg", files[0].path().value());
EXPECT_EQ("bbb.jpg", files[1].path().value());
EXPECT_EQ("ccc.jpg", files[2].path().value());
EXPECT_EQ("ddd.jpg", files[3].path().value());
}
} // namespace
} // namespace chromeos
// Copyright 2017 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 "chrome/browser/chromeos/fileapi/recent_source.h"
namespace chromeos {
RecentSource::RecentSource() = default;
RecentSource::~RecentSource() = default;
} // namespace chromeos
// Copyright 2017 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.
#ifndef CHROME_BROWSER_CHROMEOS_FILEAPI_RECENT_SOURCE_H_
#define CHROME_BROWSER_CHROMEOS_FILEAPI_RECENT_SOURCE_H_
#include <memory>
#include <string>
#include <vector>
#include "base/callback_forward.h"
#include "storage/browser/fileapi/file_system_url.h"
namespace chromeos {
class RecentContext;
// Interface class for a source of recent files.
//
// Recent file system retrieves recent files from several sources such as
// local directories and cloud storages. To provide files to Recent file
// system, this interface should be implemented for each source.
//
// All member functions must be called on the IO thread.
class RecentSource {
public:
using RecentFileList = std::vector<storage::FileSystemURL>;
using GetRecentFilesCallback = base::OnceCallback<void(RecentFileList files)>;
virtual ~RecentSource();
// Retrieves a list of recent files from this source.
//
// You can assumed that, once this function is called, it is not called again
// until the callback is invoked. This means that you can safely save internal
// states to compute recent files in member variables.
virtual void GetRecentFiles(RecentContext context,
GetRecentFilesCallback callback) = 0;
protected:
RecentSource();
};
} // namespace chromeos
#endif // CHROME_BROWSER_CHROMEOS_FILEAPI_RECENT_SOURCE_H_
// Copyright 2017 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 "chrome/browser/chromeos/fileapi/test/fake_recent_source.h"
#include <utility>
#include "chrome/browser/chromeos/fileapi/recent_context.h"
namespace chromeos {
FakeRecentSource::FakeRecentSource() = default;
FakeRecentSource::~FakeRecentSource() = default;
void FakeRecentSource::AddFile(const storage::FileSystemURL& file) {
canned_files_.emplace_back(file);
}
void FakeRecentSource::GetRecentFiles(RecentContext context,
GetRecentFilesCallback callback) {
std::move(callback).Run(canned_files_);
}
} // namespace chromeos
// Copyright 2017 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.
#ifndef CHROME_BROWSER_CHROMEOS_FILEAPI_TEST_FAKE_RECENT_SOURCE_H_
#define CHROME_BROWSER_CHROMEOS_FILEAPI_TEST_FAKE_RECENT_SOURCE_H_
#include <vector>
#include "base/macros.h"
#include "chrome/browser/chromeos/fileapi/recent_source.h"
#include "storage/browser/fileapi/file_system_url.h"
namespace chromeos {
// Fake implementation of RecentSource that returns a canned set of files.
//
// All member functions must be called on the IO thread.
class FakeRecentSource : public RecentSource {
public:
FakeRecentSource();
~FakeRecentSource() override;
// Add a file to the canned set.
void AddFile(const storage::FileSystemURL& file);
// RecentSource overrides:
void GetRecentFiles(RecentContext context,
GetRecentFilesCallback callback) override;
private:
std::vector<storage::FileSystemURL> canned_files_;
DISALLOW_COPY_AND_ASSIGN(FakeRecentSource);
};
} // namespace chromeos
#endif // CHROME_BROWSER_CHROMEOS_FILEAPI_TEST_FAKE_RECENT_SOURCE_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment