1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "content/public/test/test_file_system_backend.h"
6
7#include <set>
8#include <string>
9#include <vector>
10
11#include "base/files/file.h"
12#include "base/files/file_util.h"
13#include "base/memory/weak_ptr.h"
14#include "base/observer_list.h"
15#include "base/sequenced_task_runner.h"
16#include "base/thread_task_runner_handle.h"
17#include "storage/browser/blob/file_stream_reader.h"
18#include "storage/browser/fileapi/copy_or_move_file_validator.h"
19#include "storage/browser/fileapi/file_observers.h"
20#include "storage/browser/fileapi/file_system_operation.h"
21#include "storage/browser/fileapi/file_system_operation_context.h"
22#include "storage/browser/fileapi/file_system_quota_util.h"
23#include "storage/browser/fileapi/local_file_util.h"
24#include "storage/browser/fileapi/native_file_util.h"
25#include "storage/browser/fileapi/quota/quota_reservation.h"
26#include "storage/browser/fileapi/sandbox_file_stream_writer.h"
27#include "storage/browser/fileapi/watcher_manager.h"
28#include "storage/browser/quota/quota_manager.h"
29#include "storage/common/fileapi/file_system_util.h"
30
31using storage::FileSystemContext;
32using storage::FileSystemOperation;
33using storage::FileSystemOperationContext;
34using storage::FileSystemURL;
35
36namespace content {
37
38namespace {
39
40// Stub implementation of storage::LocalFileUtil.
41class TestFileUtil : public storage::LocalFileUtil {
42 public:
43  explicit TestFileUtil(const base::FilePath& base_path)
44      : base_path_(base_path) {}
45  virtual ~TestFileUtil() {}
46
47  // LocalFileUtil overrides.
48  virtual base::File::Error GetLocalFilePath(
49      FileSystemOperationContext* context,
50      const FileSystemURL& file_system_url,
51      base::FilePath* local_file_path) OVERRIDE {
52    *local_file_path = base_path_.Append(file_system_url.path());
53    return base::File::FILE_OK;
54  }
55
56 private:
57  base::FilePath base_path_;
58};
59
60// Stub implementation of storage::WatcherManager. Emits a fake notification
61// after a directory watcher is set successfully.
62class TestWatcherManager : public storage::WatcherManager {
63 public:
64  TestWatcherManager() : weak_ptr_factory_(this) {}
65  virtual ~TestWatcherManager() {}
66
67  // storage::WatcherManager overrides.
68  virtual void AddObserver(Observer* observer) OVERRIDE {
69    observers_.AddObserver(observer);
70  }
71
72  virtual void RemoveObserver(Observer* observer) OVERRIDE {
73    observers_.RemoveObserver(observer);
74  }
75
76  virtual bool HasObserver(Observer* observer) const OVERRIDE {
77    return observers_.HasObserver(observer);
78  }
79
80  virtual void WatchDirectory(const storage::FileSystemURL& url,
81                              bool recursive,
82                              const StatusCallback& callback) OVERRIDE {
83    if (recursive) {
84      base::ThreadTaskRunnerHandle::Get()->PostTask(
85          FROM_HERE,
86          base::Bind(callback, base::File::FILE_ERROR_INVALID_OPERATION));
87      return;
88    }
89
90    const GURL gurl = url.ToGURL();
91    if (watched_urls_.find(gurl) != watched_urls_.end()) {
92      base::ThreadTaskRunnerHandle::Get()->PostTask(
93          FROM_HERE, base::Bind(callback, base::File::FILE_ERROR_EXISTS));
94      return;
95    }
96
97    watched_urls_.insert(gurl);
98    base::ThreadTaskRunnerHandle::Get()->PostTask(
99        FROM_HERE, base::Bind(callback, base::File::FILE_OK));
100
101    // Send a fake changed notification.
102    base::ThreadTaskRunnerHandle::Get()->PostTask(
103        FROM_HERE,
104        base::Bind(&TestWatcherManager::SendFakeChangeNotification,
105                   weak_ptr_factory_.GetWeakPtr(),
106                   url));
107
108    // Send a fake removed notification.
109    base::ThreadTaskRunnerHandle::Get()->PostTask(
110        FROM_HERE,
111        base::Bind(&TestWatcherManager::SendFakeRemoveNotification,
112                   weak_ptr_factory_.GetWeakPtr(),
113                   url));
114  }
115
116  virtual void UnwatchEntry(const storage::FileSystemURL& url,
117                            const StatusCallback& callback) OVERRIDE {
118    const GURL gurl = url.ToGURL();
119    if (watched_urls_.find(gurl) == watched_urls_.end()) {
120      base::ThreadTaskRunnerHandle::Get()->PostTask(
121          FROM_HERE, base::Bind(callback, base::File::FILE_ERROR_NOT_FOUND));
122      return;
123    }
124
125    watched_urls_.erase(gurl);
126    base::ThreadTaskRunnerHandle::Get()->PostTask(
127        FROM_HERE, base::Bind(callback, base::File::FILE_OK));
128  }
129
130 private:
131  // Sends a fake notification to each observer about a changed entry
132  // represented by |url|, as long as it is still being watched.
133  void SendFakeChangeNotification(const storage::FileSystemURL& url) {
134    if (watched_urls_.find(url.ToGURL()) == watched_urls_.end())
135      return;
136
137    FOR_EACH_OBSERVER(Observer, observers_, OnEntryChanged(url));
138  }
139
140  // Sends a fake notification to each observer about a removed entry
141  // represented by |url|, as long as it is still being watched.
142  void SendFakeRemoveNotification(const storage::FileSystemURL& url) {
143    if (watched_urls_.find(url.ToGURL()) == watched_urls_.end())
144      return;
145
146    FOR_EACH_OBSERVER(Observer, observers_, OnEntryRemoved(url));
147  }
148
149  ObserverList<Observer> observers_;
150  std::set<GURL> watched_urls_;
151
152  base::WeakPtrFactory<TestWatcherManager> weak_ptr_factory_;
153};
154
155}  // namespace
156
157// This only supports single origin.
158class TestFileSystemBackend::QuotaUtil : public storage::FileSystemQuotaUtil,
159                                         public storage::FileUpdateObserver {
160 public:
161  QuotaUtil() : usage_(0) {}
162  virtual ~QuotaUtil() {}
163
164  // FileSystemQuotaUtil overrides.
165  virtual base::File::Error DeleteOriginDataOnFileTaskRunner(
166      FileSystemContext* context,
167      storage::QuotaManagerProxy* proxy,
168      const GURL& origin_url,
169      storage::FileSystemType type) OVERRIDE {
170    NOTREACHED();
171    return base::File::FILE_OK;
172  }
173
174  virtual scoped_refptr<storage::QuotaReservation>
175  CreateQuotaReservationOnFileTaskRunner(
176      const GURL& origin_url,
177      storage::FileSystemType type) OVERRIDE {
178    NOTREACHED();
179    return scoped_refptr<storage::QuotaReservation>();
180  }
181
182  virtual void GetOriginsForTypeOnFileTaskRunner(
183      storage::FileSystemType type,
184      std::set<GURL>* origins) OVERRIDE {
185    NOTREACHED();
186  }
187
188  virtual void GetOriginsForHostOnFileTaskRunner(
189      storage::FileSystemType type,
190      const std::string& host,
191      std::set<GURL>* origins) OVERRIDE {
192    NOTREACHED();
193  }
194
195  virtual int64 GetOriginUsageOnFileTaskRunner(
196      FileSystemContext* context,
197      const GURL& origin_url,
198      storage::FileSystemType type) OVERRIDE {
199    return usage_;
200  }
201
202  // FileUpdateObserver overrides.
203  virtual void OnStartUpdate(const FileSystemURL& url) OVERRIDE {}
204  virtual void OnUpdate(const FileSystemURL& url, int64 delta) OVERRIDE {
205    usage_ += delta;
206  }
207  virtual void OnEndUpdate(const FileSystemURL& url) OVERRIDE {}
208
209 private:
210  int64 usage_;
211  DISALLOW_COPY_AND_ASSIGN(QuotaUtil);
212};
213
214TestFileSystemBackend::TestFileSystemBackend(
215    base::SequencedTaskRunner* task_runner,
216    const base::FilePath& base_path)
217    : base_path_(base_path),
218      task_runner_(task_runner),
219      file_util_(
220          new storage::AsyncFileUtilAdapter(new TestFileUtil(base_path))),
221      watcher_manager_(new TestWatcherManager()),
222      quota_util_(new QuotaUtil),
223      require_copy_or_move_validator_(false) {
224  update_observers_ =
225      update_observers_.AddObserver(quota_util_.get(), task_runner_.get());
226}
227
228TestFileSystemBackend::~TestFileSystemBackend() {
229}
230
231bool TestFileSystemBackend::CanHandleType(storage::FileSystemType type) const {
232  return (type == storage::kFileSystemTypeTest);
233}
234
235void TestFileSystemBackend::Initialize(FileSystemContext* context) {
236}
237
238void TestFileSystemBackend::ResolveURL(const FileSystemURL& url,
239                                       storage::OpenFileSystemMode mode,
240                                       const OpenFileSystemCallback& callback) {
241  callback.Run(GetFileSystemRootURI(url.origin(), url.type()),
242               GetFileSystemName(url.origin(), url.type()),
243               base::File::FILE_OK);
244}
245
246storage::AsyncFileUtil* TestFileSystemBackend::GetAsyncFileUtil(
247    storage::FileSystemType type) {
248  return file_util_.get();
249}
250
251storage::WatcherManager* TestFileSystemBackend::GetWatcherManager(
252    storage::FileSystemType type) {
253  return watcher_manager_.get();
254}
255
256storage::CopyOrMoveFileValidatorFactory*
257TestFileSystemBackend::GetCopyOrMoveFileValidatorFactory(
258    storage::FileSystemType type,
259    base::File::Error* error_code) {
260  DCHECK(error_code);
261  *error_code = base::File::FILE_OK;
262  if (require_copy_or_move_validator_) {
263    if (!copy_or_move_file_validator_factory_)
264      *error_code = base::File::FILE_ERROR_SECURITY;
265    return copy_or_move_file_validator_factory_.get();
266  }
267  return NULL;
268}
269
270void TestFileSystemBackend::InitializeCopyOrMoveFileValidatorFactory(
271    scoped_ptr<storage::CopyOrMoveFileValidatorFactory> factory) {
272  if (!copy_or_move_file_validator_factory_)
273    copy_or_move_file_validator_factory_ = factory.Pass();
274}
275
276FileSystemOperation* TestFileSystemBackend::CreateFileSystemOperation(
277    const FileSystemURL& url,
278    FileSystemContext* context,
279    base::File::Error* error_code) const {
280  scoped_ptr<FileSystemOperationContext> operation_context(
281      new FileSystemOperationContext(context));
282  operation_context->set_update_observers(*GetUpdateObservers(url.type()));
283  operation_context->set_change_observers(*GetChangeObservers(url.type()));
284  return FileSystemOperation::Create(url, context, operation_context.Pass());
285}
286
287bool TestFileSystemBackend::SupportsStreaming(
288    const storage::FileSystemURL& url) const {
289  return false;
290}
291
292bool TestFileSystemBackend::HasInplaceCopyImplementation(
293    storage::FileSystemType type) const {
294  return true;
295}
296
297scoped_ptr<storage::FileStreamReader>
298TestFileSystemBackend::CreateFileStreamReader(
299    const FileSystemURL& url,
300    int64 offset,
301    int64 max_bytes_to_read,
302    const base::Time& expected_modification_time,
303    FileSystemContext* context) const {
304  return scoped_ptr<storage::FileStreamReader>(
305      storage::FileStreamReader::CreateForFileSystemFile(
306          context, url, offset, expected_modification_time));
307}
308
309scoped_ptr<storage::FileStreamWriter>
310TestFileSystemBackend::CreateFileStreamWriter(
311    const FileSystemURL& url,
312    int64 offset,
313    FileSystemContext* context) const {
314  return scoped_ptr<storage::FileStreamWriter>(
315      new storage::SandboxFileStreamWriter(
316          context, url, offset, *GetUpdateObservers(url.type())));
317}
318
319storage::FileSystemQuotaUtil* TestFileSystemBackend::GetQuotaUtil() {
320  return quota_util_.get();
321}
322
323const storage::UpdateObserverList* TestFileSystemBackend::GetUpdateObservers(
324    storage::FileSystemType type) const {
325  return &update_observers_;
326}
327
328const storage::ChangeObserverList* TestFileSystemBackend::GetChangeObservers(
329    storage::FileSystemType type) const {
330  return &change_observers_;
331}
332
333const storage::AccessObserverList* TestFileSystemBackend::GetAccessObservers(
334    storage::FileSystemType type) const {
335  return NULL;
336}
337
338void TestFileSystemBackend::AddFileChangeObserver(
339    storage::FileChangeObserver* observer) {
340  change_observers_ =
341      change_observers_.AddObserver(observer, task_runner_.get());
342}
343
344}  // namespace content
345