1 // Copyright (c) 2012 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/extensions/api/sync_file_system/sync_file_system_api.h" 6 7 #include <string> 8 #include <utility> 9 10 #include "base/bind.h" 11 #include "base/logging.h" 12 #include "base/strings/stringprintf.h" 13 #include "chrome/browser/extensions/api/sync_file_system/extension_sync_event_observer.h" 14 #include "chrome/browser/extensions/api/sync_file_system/extension_sync_event_observer_factory.h" 15 #include "chrome/browser/extensions/api/sync_file_system/sync_file_system_api_helpers.h" 16 #include "chrome/browser/profiles/profile.h" 17 #include "chrome/browser/sync_file_system/drive_backend/drive_file_sync_service.h" 18 #include "chrome/browser/sync_file_system/sync_file_status.h" 19 #include "chrome/browser/sync_file_system/sync_file_system_service.h" 20 #include "chrome/browser/sync_file_system/sync_file_system_service_factory.h" 21 #include "chrome/common/extensions/api/sync_file_system.h" 22 #include "content/public/browser/browser_context.h" 23 #include "content/public/browser/render_view_host.h" 24 #include "content/public/browser/storage_partition.h" 25 #include "content/public/common/content_client.h" 26 #include "webkit/browser/fileapi/file_system_context.h" 27 #include "webkit/browser/fileapi/file_system_url.h" 28 #include "webkit/browser/quota/quota_manager.h" 29 #include "webkit/common/fileapi/file_system_types.h" 30 #include "webkit/common/fileapi/file_system_util.h" 31 32 using content::BrowserContext; 33 using content::BrowserThread; 34 using sync_file_system::ConflictResolutionPolicy; 35 using sync_file_system::SyncFileStatus; 36 using sync_file_system::SyncFileSystemServiceFactory; 37 using sync_file_system::SyncStatusCode; 38 39 namespace extensions { 40 41 namespace { 42 43 // Error messages. 44 const char kFileError[] = "File error %d."; 45 const char kQuotaError[] = "Quota error %d."; 46 const char kUnsupportedConflictResolutionPolicy[] = 47 "Policy %s is not supported."; 48 49 sync_file_system::SyncFileSystemService* GetSyncFileSystemService( 50 Profile* profile) { 51 sync_file_system::SyncFileSystemService* service = 52 SyncFileSystemServiceFactory::GetForProfile(profile); 53 DCHECK(service); 54 ExtensionSyncEventObserver* observer = 55 ExtensionSyncEventObserverFactory::GetForProfile(profile); 56 DCHECK(observer); 57 observer->InitializeForService(service); 58 return service; 59 } 60 61 } // namespace 62 63 bool SyncFileSystemDeleteFileSystemFunction::RunImpl() { 64 std::string url; 65 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &url)); 66 67 scoped_refptr<fileapi::FileSystemContext> file_system_context = 68 BrowserContext::GetStoragePartition( 69 profile(), 70 render_view_host()->GetSiteInstance())->GetFileSystemContext(); 71 fileapi::FileSystemURL file_system_url( 72 file_system_context->CrackURL(GURL(url))); 73 74 BrowserThread::PostTask( 75 BrowserThread::IO, 76 FROM_HERE, 77 Bind(&fileapi::FileSystemContext::DeleteFileSystem, 78 file_system_context, 79 source_url().GetOrigin(), 80 file_system_url.type(), 81 Bind(&SyncFileSystemDeleteFileSystemFunction::DidDeleteFileSystem, 82 this))); 83 return true; 84 } 85 86 void SyncFileSystemDeleteFileSystemFunction::DidDeleteFileSystem( 87 base::PlatformFileError error) { 88 // Repost to switch from IO thread to UI thread for SendResponse(). 89 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { 90 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 91 BrowserThread::PostTask( 92 BrowserThread::UI, 93 FROM_HERE, 94 Bind(&SyncFileSystemDeleteFileSystemFunction::DidDeleteFileSystem, this, 95 error)); 96 return; 97 } 98 99 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 100 if (error != base::PLATFORM_FILE_OK) { 101 error_ = base::StringPrintf(kFileError, static_cast<int>(error)); 102 SetResult(base::Value::CreateBooleanValue(false)); 103 SendResponse(false); 104 return; 105 } 106 107 SetResult(base::Value::CreateBooleanValue(true)); 108 SendResponse(true); 109 } 110 111 bool SyncFileSystemRequestFileSystemFunction::RunImpl() { 112 // Initializes sync context for this extension and continue to open 113 // a new file system. 114 GetSyncFileSystemService(profile())-> 115 InitializeForApp( 116 GetFileSystemContext(), 117 source_url().GetOrigin(), 118 base::Bind(&self::DidInitializeFileSystemContext, this)); 119 return true; 120 } 121 122 fileapi::FileSystemContext* 123 SyncFileSystemRequestFileSystemFunction::GetFileSystemContext() { 124 DCHECK(render_view_host()); 125 return BrowserContext::GetStoragePartition( 126 profile(), 127 render_view_host()->GetSiteInstance())->GetFileSystemContext(); 128 } 129 130 void SyncFileSystemRequestFileSystemFunction::DidInitializeFileSystemContext( 131 SyncStatusCode status) { 132 if (status != sync_file_system::SYNC_STATUS_OK) { 133 error_ = sync_file_system::SyncStatusCodeToString(status); 134 SendResponse(false); 135 return; 136 } 137 138 if (!render_view_host()) { 139 // The app seems to have been closed. 140 return; 141 } 142 143 BrowserThread::PostTask( 144 BrowserThread::IO, FROM_HERE, 145 Bind(&fileapi::FileSystemContext::OpenFileSystem, 146 GetFileSystemContext(), 147 source_url().GetOrigin(), 148 fileapi::kFileSystemTypeSyncable, 149 fileapi::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT, 150 base::Bind(&self::DidOpenFileSystem, this))); 151 } 152 153 void SyncFileSystemRequestFileSystemFunction::DidOpenFileSystem( 154 base::PlatformFileError error, 155 const std::string& file_system_name, 156 const GURL& root_url) { 157 // Repost to switch from IO thread to UI thread for SendResponse(). 158 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { 159 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 160 BrowserThread::PostTask( 161 BrowserThread::UI, FROM_HERE, 162 Bind(&SyncFileSystemRequestFileSystemFunction::DidOpenFileSystem, 163 this, error, file_system_name, root_url)); 164 return; 165 } 166 167 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 168 if (error != base::PLATFORM_FILE_OK) { 169 error_ = base::StringPrintf(kFileError, static_cast<int>(error)); 170 SendResponse(false); 171 return; 172 } 173 174 base::DictionaryValue* dict = new base::DictionaryValue(); 175 SetResult(dict); 176 dict->SetString("name", file_system_name); 177 dict->SetString("root", root_url.spec()); 178 SendResponse(true); 179 } 180 181 bool SyncFileSystemGetFileStatusFunction::RunImpl() { 182 std::string url; 183 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &url)); 184 185 scoped_refptr<fileapi::FileSystemContext> file_system_context = 186 BrowserContext::GetStoragePartition( 187 profile(), 188 render_view_host()->GetSiteInstance())->GetFileSystemContext(); 189 fileapi::FileSystemURL file_system_url( 190 file_system_context->CrackURL(GURL(url))); 191 192 SyncFileSystemServiceFactory::GetForProfile(profile())->GetFileSyncStatus( 193 file_system_url, 194 Bind(&SyncFileSystemGetFileStatusFunction::DidGetFileStatus, 195 this)); 196 return true; 197 } 198 199 void SyncFileSystemGetFileStatusFunction::DidGetFileStatus( 200 const SyncStatusCode sync_status_code, 201 const SyncFileStatus sync_file_status) { 202 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 203 if (sync_status_code != sync_file_system::SYNC_STATUS_OK) { 204 error_ = sync_file_system::SyncStatusCodeToString(sync_status_code); 205 SendResponse(false); 206 return; 207 } 208 209 // Convert from C++ to JavaScript enum. 210 results_ = api::sync_file_system::GetFileStatus::Results::Create( 211 SyncFileStatusToExtensionEnum(sync_file_status)); 212 SendResponse(true); 213 } 214 215 SyncFileSystemGetFileStatusesFunction::SyncFileSystemGetFileStatusesFunction() { 216 } 217 218 SyncFileSystemGetFileStatusesFunction::~SyncFileSystemGetFileStatusesFunction( 219 ) {} 220 221 bool SyncFileSystemGetFileStatusesFunction::RunImpl() { 222 // All FileEntries converted into array of URL Strings in JS custom bindings. 223 base::ListValue* file_entry_urls = NULL; 224 EXTENSION_FUNCTION_VALIDATE(args_->GetList(0, &file_entry_urls)); 225 226 scoped_refptr<fileapi::FileSystemContext> file_system_context = 227 BrowserContext::GetStoragePartition( 228 profile(), 229 render_view_host()->GetSiteInstance())->GetFileSystemContext(); 230 231 // Map each file path->SyncFileStatus in the callback map. 232 // TODO(calvinlo): Overload GetFileSyncStatus to take in URL array. 233 num_expected_results_ = file_entry_urls->GetSize(); 234 num_results_received_ = 0; 235 file_sync_statuses_.clear(); 236 sync_file_system::SyncFileSystemService* sync_file_system_service = 237 SyncFileSystemServiceFactory::GetForProfile(profile()); 238 for (unsigned int i = 0; i < num_expected_results_; i++) { 239 std::string url; 240 file_entry_urls->GetString(i, &url); 241 fileapi::FileSystemURL file_system_url( 242 file_system_context->CrackURL(GURL(url))); 243 244 sync_file_system_service->GetFileSyncStatus( 245 file_system_url, 246 Bind(&SyncFileSystemGetFileStatusesFunction::DidGetFileStatus, 247 this, file_system_url)); 248 } 249 250 return true; 251 } 252 253 void SyncFileSystemGetFileStatusesFunction::DidGetFileStatus( 254 const fileapi::FileSystemURL& file_system_url, 255 SyncStatusCode sync_status_code, 256 SyncFileStatus sync_file_status) { 257 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 258 num_results_received_++; 259 DCHECK_LE(num_results_received_, num_expected_results_); 260 261 file_sync_statuses_[file_system_url] = 262 std::make_pair(sync_status_code, sync_file_status); 263 264 // Keep mapping file statuses until all of them have been received. 265 // TODO(calvinlo): Get rid of this check when batch version of 266 // GetFileSyncStatus(GURL urls[]); is added. 267 if (num_results_received_ < num_expected_results_) 268 return; 269 270 // All results received. Dump array of statuses into extension enum values. 271 // Note that the enum types need to be set as strings manually as the 272 // autogenerated Results::Create function thinks the enum values should be 273 // returned as int values. 274 base::ListValue* status_array = new base::ListValue(); 275 for (URLToStatusMap::iterator it = file_sync_statuses_.begin(); 276 it != file_sync_statuses_.end(); ++it) { 277 base::DictionaryValue* dict = new base::DictionaryValue(); 278 status_array->Append(dict); 279 280 fileapi::FileSystemURL url = it->first; 281 SyncStatusCode file_error = it->second.first; 282 api::sync_file_system::FileStatus file_status = 283 SyncFileStatusToExtensionEnum(it->second.second); 284 285 dict->Set("entry", CreateDictionaryValueForFileSystemEntry( 286 url, sync_file_system::SYNC_FILE_TYPE_FILE)); 287 dict->SetString("status", ToString(file_status)); 288 289 if (file_error == sync_file_system::SYNC_STATUS_OK) 290 continue; 291 dict->SetString("error", 292 sync_file_system::SyncStatusCodeToString(file_error)); 293 } 294 SetResult(status_array); 295 296 SendResponse(true); 297 } 298 299 bool SyncFileSystemGetUsageAndQuotaFunction::RunImpl() { 300 std::string url; 301 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &url)); 302 303 scoped_refptr<fileapi::FileSystemContext> file_system_context = 304 BrowserContext::GetStoragePartition( 305 profile(), 306 render_view_host()->GetSiteInstance())->GetFileSystemContext(); 307 fileapi::FileSystemURL file_system_url( 308 file_system_context->CrackURL(GURL(url))); 309 310 scoped_refptr<quota::QuotaManager> quota_manager = 311 BrowserContext::GetStoragePartition( 312 profile(), 313 render_view_host()->GetSiteInstance())->GetQuotaManager(); 314 315 BrowserThread::PostTask( 316 BrowserThread::IO, 317 FROM_HERE, 318 Bind("a::QuotaManager::GetUsageAndQuotaForWebApps, 319 quota_manager, 320 source_url().GetOrigin(), 321 fileapi::FileSystemTypeToQuotaStorageType(file_system_url.type()), 322 Bind(&SyncFileSystemGetUsageAndQuotaFunction::DidGetUsageAndQuota, 323 this))); 324 325 return true; 326 } 327 328 void SyncFileSystemGetUsageAndQuotaFunction::DidGetUsageAndQuota( 329 quota::QuotaStatusCode status, int64 usage, int64 quota) { 330 // Repost to switch from IO thread to UI thread for SendResponse(). 331 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { 332 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 333 BrowserThread::PostTask( 334 BrowserThread::UI, 335 FROM_HERE, 336 Bind(&SyncFileSystemGetUsageAndQuotaFunction::DidGetUsageAndQuota, this, 337 status, usage, quota)); 338 return; 339 } 340 341 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 342 if (status != quota::kQuotaStatusOk) { 343 error_ = QuotaStatusCodeToString(status); 344 SendResponse(false); 345 return; 346 } 347 348 api::sync_file_system::StorageInfo info; 349 info.usage_bytes = usage; 350 info.quota_bytes = quota; 351 results_ = api::sync_file_system::GetUsageAndQuota::Results::Create(info); 352 SendResponse(true); 353 } 354 355 bool SyncFileSystemSetConflictResolutionPolicyFunction::RunImpl() { 356 std::string policy_string; 357 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &policy_string)); 358 ConflictResolutionPolicy policy = ExtensionEnumToConflictResolutionPolicy( 359 api::sync_file_system::ParseConflictResolutionPolicy(policy_string)); 360 if (policy == sync_file_system::CONFLICT_RESOLUTION_POLICY_UNKNOWN) { 361 SetError(base::StringPrintf(kUnsupportedConflictResolutionPolicy, 362 policy_string.c_str())); 363 return false; 364 } 365 sync_file_system::SyncFileSystemService* service = GetSyncFileSystemService( 366 profile()); 367 DCHECK(service); 368 SyncStatusCode status = service->SetConflictResolutionPolicy(policy); 369 if (status != sync_file_system::SYNC_STATUS_OK) { 370 SetError(sync_file_system::SyncStatusCodeToString(status)); 371 return false; 372 } 373 return true; 374 } 375 376 bool SyncFileSystemGetConflictResolutionPolicyFunction::RunImpl() { 377 sync_file_system::SyncFileSystemService* service = GetSyncFileSystemService( 378 profile()); 379 DCHECK(service); 380 api::sync_file_system::ConflictResolutionPolicy policy = 381 ConflictResolutionPolicyToExtensionEnum( 382 service->GetConflictResolutionPolicy()); 383 SetResult(Value::CreateStringValue( 384 api::sync_file_system::ToString(policy))); 385 return true; 386 } 387 388 } // namespace extensions 389