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