sync_file_system_backend.cc revision 8bcbed890bc3ce4d7a057a8f32cab53fa534672e
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  RevokeSyncableFileSystem();
76
77  if (change_tracker_) {
78    GetDelegate()->file_task_runner()->DeleteSoon(
79        FROM_HERE, change_tracker_.release());
80  }
81
82  if (profile_holder_ && !CalledOnUIThread()) {
83    BrowserThread::DeleteSoon(
84        BrowserThread::UI, FROM_HERE, profile_holder_.release());
85  }
86}
87
88// static
89SyncFileSystemBackend* SyncFileSystemBackend::CreateForTesting() {
90  DCHECK(CalledOnUIThread());
91  SyncFileSystemBackend* backend = new SyncFileSystemBackend(NULL);
92  backend->skip_initialize_syncfs_service_for_testing_ = true;
93  return backend;
94}
95
96bool SyncFileSystemBackend::CanHandleType(
97    fileapi::FileSystemType type) const {
98  return type == fileapi::kFileSystemTypeSyncable ||
99         type == fileapi::kFileSystemTypeSyncableForInternalSync;
100}
101
102void SyncFileSystemBackend::Initialize(fileapi::FileSystemContext* context) {
103  DCHECK(context);
104  DCHECK(!context_);
105  context_ = context;
106
107  fileapi::SandboxFileSystemBackendDelegate* delegate = GetDelegate();
108  delegate->RegisterQuotaUpdateObserver(fileapi::kFileSystemTypeSyncable);
109  delegate->RegisterQuotaUpdateObserver(
110      fileapi::kFileSystemTypeSyncableForInternalSync);
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