Home | History | Annotate | Download | only in sync_file_system
      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(&quota::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