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