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