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 25 using content::BrowserThread; 26 27 namespace sync_file_system { 28 29 namespace { 30 31 bool 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 40 SyncFileSystemBackend::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 48 void 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 59 Profile* SyncFileSystemBackend::ProfileHolder::GetProfile() { 60 DCHECK(CalledOnUIThread()); 61 return profile_; 62 } 63 64 SyncFileSystemBackend::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 76 SyncFileSystemBackend::~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 89 SyncFileSystemBackend* 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 96 bool SyncFileSystemBackend::CanHandleType(storage::FileSystemType type) const { 97 return type == storage::kFileSystemTypeSyncable || 98 type == storage::kFileSystemTypeSyncableForInternalSync; 99 } 100 101 void 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 112 void 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 138 storage::AsyncFileUtil* SyncFileSystemBackend::GetAsyncFileUtil( 139 storage::FileSystemType type) { 140 return GetDelegate()->file_util(); 141 } 142 143 storage::WatcherManager* SyncFileSystemBackend::GetWatcherManager( 144 storage::FileSystemType type) { 145 return NULL; 146 } 147 148 storage::CopyOrMoveFileValidatorFactory* 149 SyncFileSystemBackend::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 157 storage::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 179 bool SyncFileSystemBackend::SupportsStreaming( 180 const storage::FileSystemURL& url) const { 181 return false; 182 } 183 184 bool SyncFileSystemBackend::HasInplaceCopyImplementation( 185 storage::FileSystemType type) const { 186 return false; 187 } 188 189 scoped_ptr<storage::FileStreamReader> 190 SyncFileSystemBackend::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 201 scoped_ptr<storage::FileStreamWriter> 202 SyncFileSystemBackend::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 211 storage::FileSystemQuotaUtil* SyncFileSystemBackend::GetQuotaUtil() { 212 return GetDelegate(); 213 } 214 215 const storage::UpdateObserverList* SyncFileSystemBackend::GetUpdateObservers( 216 storage::FileSystemType type) const { 217 return GetDelegate()->GetUpdateObservers(type); 218 } 219 220 const storage::ChangeObserverList* SyncFileSystemBackend::GetChangeObservers( 221 storage::FileSystemType type) const { 222 return GetDelegate()->GetChangeObservers(type); 223 } 224 225 const storage::AccessObserverList* SyncFileSystemBackend::GetAccessObservers( 226 storage::FileSystemType type) const { 227 return GetDelegate()->GetAccessObservers(type); 228 } 229 230 // static 231 SyncFileSystemBackend* 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 239 void 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 254 void SyncFileSystemBackend::set_sync_context( 255 LocalFileSyncContext* sync_context) { 256 DCHECK(!sync_context_.get()); 257 sync_context_ = sync_context; 258 } 259 260 storage::SandboxFileSystemBackendDelegate* SyncFileSystemBackend::GetDelegate() 261 const { 262 DCHECK(context_); 263 DCHECK(context_->sandbox_delegate()); 264 return context_->sandbox_delegate(); 265 } 266 267 void 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 293 void 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