sync_file_system_api.cc revision 424c4d7b64af9d0d8fd9624f381f469654d5e3d2
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
32using content::BrowserContext;
33using content::BrowserThread;
34using sync_file_system::ConflictResolutionPolicy;
35using sync_file_system::SyncFileStatus;
36using sync_file_system::SyncFileSystemServiceFactory;
37using sync_file_system::SyncStatusCode;
38
39namespace extensions {
40
41namespace {
42
43// Error messages.
44const char kFileError[] = "File error %d.";
45const char kQuotaError[] = "Quota error %d.";
46const char kUnsupportedConflictResolutionPolicy[] =
47    "Policy %s is not supported.";
48
49sync_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
63bool 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
86void 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(new base::FundamentalValue(false));
103    SendResponse(false);
104    return;
105  }
106
107  SetResult(new base::FundamentalValue(true));
108  SendResponse(true);
109}
110
111bool 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
122fileapi::FileSystemContext*
123SyncFileSystemRequestFileSystemFunction::GetFileSystemContext() {
124  DCHECK(render_view_host());
125  return BrowserContext::GetStoragePartition(
126      profile(),
127      render_view_host()->GetSiteInstance())->GetFileSystemContext();
128}
129
130void 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
153void 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
181bool 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  GetSyncFileSystemService(profile())->GetFileSyncStatus(
193      file_system_url,
194      Bind(&SyncFileSystemGetFileStatusFunction::DidGetFileStatus,
195           this));
196  return true;
197}
198
199void 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
215SyncFileSystemGetFileStatusesFunction::SyncFileSystemGetFileStatusesFunction() {
216}
217
218SyncFileSystemGetFileStatusesFunction::~SyncFileSystemGetFileStatusesFunction(
219    ) {}
220
221bool 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      GetSyncFileSystemService(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
253void 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
299bool 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(&quota::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
328void 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
355bool 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
376bool 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(new base::StringValue(
384          api::sync_file_system::ToString(policy)));
385  return true;
386}
387
388bool SyncFileSystemGetServiceStatusFunction::RunImpl() {
389  sync_file_system::SyncFileSystemService* service = GetSyncFileSystemService(
390      profile());
391  results_ = api::sync_file_system::GetServiceStatus::Results::Create(
392      SyncServiceStateToExtensionEnum(service->GetSyncServiceState()));
393  return true;
394}
395
396}  // namespace extensions
397