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 23 using content::BrowserThread; 24 25 namespace sync_file_system { 26 27 namespace { 28 29 bool 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 38 SyncFileSystemBackend::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 46 void 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 57 Profile* SyncFileSystemBackend::ProfileHolder::GetProfile() { 58 DCHECK(CalledOnUIThread()); 59 return profile_; 60 } 61 62 SyncFileSystemBackend::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 74 SyncFileSystemBackend::~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 87 SyncFileSystemBackend* 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 94 bool SyncFileSystemBackend::CanHandleType( 95 fileapi::FileSystemType type) const { 96 return type == fileapi::kFileSystemTypeSyncable || 97 type == fileapi::kFileSystemTypeSyncableForInternalSync; 98 } 99 100 void 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 111 void 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 132 fileapi::AsyncFileUtil* SyncFileSystemBackend::GetAsyncFileUtil( 133 fileapi::FileSystemType type) { 134 return GetDelegate()->file_util(); 135 } 136 137 fileapi::CopyOrMoveFileValidatorFactory* 138 SyncFileSystemBackend::GetCopyOrMoveFileValidatorFactory( 139 fileapi::FileSystemType type, 140 base::PlatformFileError* error_code) { 141 DCHECK(error_code); 142 *error_code = base::PLATFORM_FILE_OK; 143 return NULL; 144 } 145 146 fileapi::FileSystemOperation* 147 SyncFileSystemBackend::CreateFileSystemOperation( 148 const fileapi::FileSystemURL& url, 149 fileapi::FileSystemContext* context, 150 base::PlatformFileError* 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 169 scoped_ptr<webkit_blob::FileStreamReader> 170 SyncFileSystemBackend::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 180 scoped_ptr<fileapi::FileStreamWriter> 181 SyncFileSystemBackend::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 190 fileapi::FileSystemQuotaUtil* SyncFileSystemBackend::GetQuotaUtil() { 191 return GetDelegate(); 192 } 193 194 // static 195 SyncFileSystemBackend* 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 203 void 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 220 void SyncFileSystemBackend::set_sync_context( 221 LocalFileSyncContext* sync_context) { 222 DCHECK(!sync_context_); 223 sync_context_ = sync_context; 224 } 225 226 fileapi::SandboxFileSystemBackendDelegate* 227 SyncFileSystemBackend::GetDelegate() const { 228 DCHECK(context_); 229 DCHECK(context_->sandbox_delegate()); 230 return context_->sandbox_delegate(); 231 } 232 233 void 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 259 void 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 SyncStatusCodeToPlatformFileError(status)); 281 return; 282 } 283 284 callback.Run(GetSyncableFileSystemRootURI(origin_url), 285 GetFileSystemName(origin_url, type), 286 base::PLATFORM_FILE_OK); 287 } 288 289 } // namespace sync_file_system 290