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