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 "storage/browser/fileapi/file_system_context.h"
25#include "storage/browser/fileapi/file_system_url.h"
26#include "storage/browser/quota/quota_manager.h"
27#include "storage/common/fileapi/file_system_types.h"
28#include "storage/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