sync_file_system_backend.cc revision 58537e28ecd584eab876aee8be7156509866d23a
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 "chrome/browser/sync_file_system/local/sync_file_system_backend.h"
6
7#include "base/logging.h"
8#include "chrome/browser/chrome_notification_types.h"
9#include "chrome/browser/sync_file_system/local/local_file_change_tracker.h"
10#include "chrome/browser/sync_file_system/local/local_file_sync_context.h"
11#include "chrome/browser/sync_file_system/local/syncable_file_system_operation.h"
12#include "chrome/browser/sync_file_system/sync_file_system_service.h"
13#include "chrome/browser/sync_file_system/sync_file_system_service_factory.h"
14#include "chrome/browser/sync_file_system/syncable_file_system_util.h"
15#include "content/public/browser/browser_thread.h"
16#include "content/public/browser/notification_service.h"
17#include "webkit/browser/fileapi/file_system_context.h"
18#include "webkit/browser/fileapi/file_system_file_stream_reader.h"
19#include "webkit/browser/fileapi/file_system_operation.h"
20#include "webkit/browser/fileapi/sandbox_file_stream_writer.h"
21#include "webkit/browser/fileapi/sandbox_quota_observer.h"
22#include "webkit/common/fileapi/file_system_util.h"
23
24using content::BrowserThread;
25
26namespace sync_file_system {
27
28namespace {
29
30bool CalledOnUIThread() {
31  // Ensure that these methods are called on the UI thread, except for unittests
32  // where a UI thread might not have been created.
33  return BrowserThread::CurrentlyOn(BrowserThread::UI) ||
34         !BrowserThread::IsMessageLoopValid(BrowserThread::UI);
35}
36
37}  // namespace
38
39SyncFileSystemBackend::ProfileHolder::ProfileHolder(Profile* profile)
40    : profile_(profile) {
41  DCHECK(CalledOnUIThread());
42  registrar_.Add(this,
43                 chrome::NOTIFICATION_PROFILE_DESTROYED,
44                 content::Source<Profile>(profile_));
45}
46
47void SyncFileSystemBackend::ProfileHolder::Observe(
48    int type,
49    const content::NotificationSource& source,
50    const content::NotificationDetails& details) {
51  DCHECK(CalledOnUIThread());
52  DCHECK_EQ(chrome::NOTIFICATION_PROFILE_DESTROYED, type);
53  DCHECK_EQ(profile_, content::Source<Profile>(source).ptr());
54  profile_ = NULL;
55  registrar_.RemoveAll();
56}
57
58Profile* SyncFileSystemBackend::ProfileHolder::GetProfile() {
59  DCHECK(CalledOnUIThread());
60  return profile_;
61}
62
63SyncFileSystemBackend::SyncFileSystemBackend(Profile* profile)
64    : context_(NULL),
65      skip_initialize_syncfs_service_for_testing_(false) {
66  DCHECK(CalledOnUIThread());
67  if (profile)
68    profile_holder_.reset(new ProfileHolder(profile));
69}
70
71SyncFileSystemBackend::~SyncFileSystemBackend() {
72  if (change_tracker_) {
73    GetDelegate()->file_task_runner()->DeleteSoon(
74        FROM_HERE, change_tracker_.release());
75  }
76
77  if (profile_holder_ && !CalledOnUIThread()) {
78    BrowserThread::DeleteSoon(
79        BrowserThread::UI, FROM_HERE, profile_holder_.release());
80  }
81}
82
83// static
84SyncFileSystemBackend* SyncFileSystemBackend::CreateForTesting() {
85  DCHECK(CalledOnUIThread());
86  SyncFileSystemBackend* backend = new SyncFileSystemBackend(NULL);
87  backend->skip_initialize_syncfs_service_for_testing_ = true;
88  return backend;
89}
90
91bool SyncFileSystemBackend::CanHandleType(
92    fileapi::FileSystemType type) const {
93  return type == fileapi::kFileSystemTypeSyncable ||
94         type == fileapi::kFileSystemTypeSyncableForInternalSync;
95}
96
97void SyncFileSystemBackend::Initialize(fileapi::FileSystemContext* context) {
98  DCHECK(context);
99  DCHECK(!context_);
100  context_ = context;
101
102  fileapi::SandboxFileSystemBackendDelegate* delegate = GetDelegate();
103  delegate->AddFileUpdateObserver(
104      fileapi::kFileSystemTypeSyncable,
105      delegate->quota_observer(),
106      delegate->file_task_runner());
107  delegate->AddFileUpdateObserver(
108      fileapi::kFileSystemTypeSyncableForInternalSync,
109      delegate->quota_observer(),
110      delegate->file_task_runner());
111}
112
113void SyncFileSystemBackend::OpenFileSystem(
114    const GURL& origin_url,
115    fileapi::FileSystemType type,
116    fileapi::OpenFileSystemMode mode,
117    const OpenFileSystemCallback& callback) {
118  DCHECK(CanHandleType(type));
119
120  if (skip_initialize_syncfs_service_for_testing_) {
121    GetDelegate()->OpenFileSystem(origin_url, type, mode, callback,
122                                  GetSyncableFileSystemRootURI(origin_url));
123    return;
124  }
125
126  // It is safe to pass Unretained(this) since |context_| owns it.
127  SyncStatusCallback initialize_callback =
128      base::Bind(&SyncFileSystemBackend::DidInitializeSyncFileSystemService,
129                 base::Unretained(this), make_scoped_refptr(context_),
130                 origin_url, type, mode, callback);
131  InitializeSyncFileSystemService(origin_url, initialize_callback);
132}
133
134fileapi::AsyncFileUtil* SyncFileSystemBackend::GetAsyncFileUtil(
135    fileapi::FileSystemType type) {
136  return GetDelegate()->file_util();
137}
138
139fileapi::CopyOrMoveFileValidatorFactory*
140SyncFileSystemBackend::GetCopyOrMoveFileValidatorFactory(
141    fileapi::FileSystemType type,
142    base::PlatformFileError* error_code) {
143  DCHECK(error_code);
144  *error_code = base::PLATFORM_FILE_OK;
145  return NULL;
146}
147
148fileapi::FileSystemOperation*
149SyncFileSystemBackend::CreateFileSystemOperation(
150    const fileapi::FileSystemURL& url,
151    fileapi::FileSystemContext* context,
152    base::PlatformFileError* error_code) const {
153  DCHECK(CanHandleType(url.type()));
154  DCHECK(context);
155  DCHECK(error_code);
156
157  scoped_ptr<fileapi::FileSystemOperationContext> operation_context =
158      GetDelegate()->CreateFileSystemOperationContext(url, context, error_code);
159  if (!operation_context)
160    return NULL;
161
162  if (url.type() == fileapi::kFileSystemTypeSyncableForInternalSync) {
163    return fileapi::FileSystemOperation::Create(
164        url, context, operation_context.Pass());
165  }
166
167  return new SyncableFileSystemOperation(
168      url, context, operation_context.Pass());
169}
170
171scoped_ptr<webkit_blob::FileStreamReader>
172SyncFileSystemBackend::CreateFileStreamReader(
173    const fileapi::FileSystemURL& url,
174    int64 offset,
175    const base::Time& expected_modification_time,
176    fileapi::FileSystemContext* context) const {
177  DCHECK(CanHandleType(url.type()));
178  return GetDelegate()->CreateFileStreamReader(
179      url, offset, expected_modification_time, context);
180}
181
182scoped_ptr<fileapi::FileStreamWriter>
183SyncFileSystemBackend::CreateFileStreamWriter(
184    const fileapi::FileSystemURL& url,
185    int64 offset,
186    fileapi::FileSystemContext* context) const {
187  DCHECK(CanHandleType(url.type()));
188  return GetDelegate()->CreateFileStreamWriter(
189      url, offset, context, fileapi::kFileSystemTypeSyncableForInternalSync);
190}
191
192fileapi::FileSystemQuotaUtil* SyncFileSystemBackend::GetQuotaUtil() {
193  return GetDelegate();
194}
195
196// static
197SyncFileSystemBackend* SyncFileSystemBackend::GetBackend(
198    const fileapi::FileSystemContext* file_system_context) {
199  DCHECK(file_system_context);
200  return static_cast<SyncFileSystemBackend*>(
201      file_system_context->GetFileSystemBackend(
202          fileapi::kFileSystemTypeSyncable));
203}
204
205void SyncFileSystemBackend::SetLocalFileChangeTracker(
206    scoped_ptr<LocalFileChangeTracker> tracker) {
207  DCHECK(!change_tracker_);
208  DCHECK(tracker);
209  change_tracker_ = tracker.Pass();
210
211  fileapi::SandboxFileSystemBackendDelegate* delegate = GetDelegate();
212  delegate->AddFileUpdateObserver(
213      fileapi::kFileSystemTypeSyncable,
214      change_tracker_.get(),
215      delegate->file_task_runner());
216  delegate->AddFileChangeObserver(
217      fileapi::kFileSystemTypeSyncable,
218      change_tracker_.get(),
219      delegate->file_task_runner());
220}
221
222void SyncFileSystemBackend::set_sync_context(
223    LocalFileSyncContext* sync_context) {
224  DCHECK(!sync_context_);
225  sync_context_ = sync_context;
226}
227
228fileapi::SandboxFileSystemBackendDelegate*
229SyncFileSystemBackend::GetDelegate() const {
230  DCHECK(context_);
231  DCHECK(context_->sandbox_delegate());
232  return context_->sandbox_delegate();
233}
234
235void SyncFileSystemBackend::InitializeSyncFileSystemService(
236    const GURL& origin_url,
237    const SyncStatusCallback& callback) {
238  // Repost to switch from IO thread to UI thread.
239  if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
240    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
241    // It is safe to pass Unretained(this) (see comments in OpenFileSystem()).
242    BrowserThread::PostTask(
243        BrowserThread::UI, FROM_HERE,
244        base::Bind(&SyncFileSystemBackend::InitializeSyncFileSystemService,
245                   base::Unretained(this), origin_url, callback));
246    return;
247  }
248
249  if (!profile_holder_->GetProfile()) {
250    // Profile was destroyed.
251    callback.Run(SYNC_FILE_ERROR_FAILED);
252    return;
253  }
254
255  SyncFileSystemService* service = SyncFileSystemServiceFactory::GetForProfile(
256          profile_holder_->GetProfile());
257  DCHECK(service);
258  service->InitializeForApp(context_, origin_url, callback);
259}
260
261void SyncFileSystemBackend::DidInitializeSyncFileSystemService(
262    fileapi::FileSystemContext* context,
263    const GURL& origin_url,
264    fileapi::FileSystemType type,
265    fileapi::OpenFileSystemMode mode,
266    const OpenFileSystemCallback& callback,
267    SyncStatusCode status) {
268  // Repost to switch from UI thread to IO thread.
269  if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
270    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
271    // It is safe to pass Unretained(this) since |context| owns it.
272    BrowserThread::PostTask(
273        BrowserThread::IO, FROM_HERE,
274        base::Bind(&SyncFileSystemBackend::DidInitializeSyncFileSystemService,
275                   base::Unretained(this), make_scoped_refptr(context),
276                   origin_url, type, mode, callback, status));
277    return;
278  }
279
280  if (status != sync_file_system::SYNC_STATUS_OK) {
281    callback.Run(GURL(), std::string(),
282                 SyncStatusCodeToPlatformFileError(status));
283    return;
284  }
285
286  GetDelegate()->OpenFileSystem(origin_url, type, mode, callback,
287                                GetSyncableFileSystemRootURI(origin_url));
288}
289
290}  // namespace sync_file_system
291