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.
5 #include "chrome/browser/google_apis/gdata_wapi_service.h"
10 #include "base/bind.h"
11 #include "base/message_loop_proxy.h"
12 #include "base/values.h"
13 #include "chrome/browser/google_apis/auth_service.h"
14 #include "chrome/browser/google_apis/drive_api_parser.h"
15 #include "chrome/browser/google_apis/gdata_wapi_operations.h"
16 #include "chrome/browser/google_apis/gdata_wapi_parser.h"
17 #include "chrome/browser/google_apis/gdata_wapi_url_generator.h"
18 #include "chrome/browser/google_apis/operation_runner.h"
19 #include "chrome/browser/google_apis/time_util.h"
20 #include "content/public/browser/browser_thread.h"
21 #include "net/base/url_util.h"
23 using content::BrowserThread
;
25 namespace google_apis
{
29 // OAuth2 scopes for the documents API.
30 const char kDocsListScope
[] = "https://docs.google.com/feeds/";
31 const char kSpreadsheetsScope
[] = "https://spreadsheets.google.com/feeds/";
32 const char kUserContentScope
[] = "https://docs.googleusercontent.com/";
33 const char kDriveAppsScope
[] = "https://www.googleapis.com/auth/drive.apps";
35 // The resource ID for the root directory for WAPI is defined in the spec:
36 // https://developers.google.com/google-apps/documents-list/
37 const char kWapiRootDirectoryResourceId
[] = "folder:root";
39 // Parses the JSON value to ResourceEntry runs |callback|.
40 void ParseResourceEntryAndRun(const GetResourceEntryCallback
& callback
,
42 scoped_ptr
<base::Value
> value
) {
43 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
46 callback
.Run(error
, scoped_ptr
<ResourceEntry
>());
50 // Parsing ResourceEntry is cheap enough to do on UI thread.
51 scoped_ptr
<ResourceEntry
> entry
=
52 google_apis::ResourceEntry::ExtractAndParse(*value
);
54 callback
.Run(GDATA_PARSE_ERROR
, scoped_ptr
<ResourceEntry
>());
58 callback
.Run(error
, entry
.Pass());
61 void ParseAboutResourceAndRun(
62 const GetAboutResourceCallback
& callback
,
64 scoped_ptr
<AccountMetadata
> account_metadata
) {
65 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
66 DCHECK(!callback
.is_null());
68 scoped_ptr
<AboutResource
> about_resource
;
69 if (account_metadata
) {
70 about_resource
= AboutResource::CreateFromAccountMetadata(
71 *account_metadata
, kWapiRootDirectoryResourceId
);
74 callback
.Run(error
, about_resource
.Pass());
77 void ParseAppListAndRun(
78 const GetAppListCallback
& callback
,
80 scoped_ptr
<AccountMetadata
> account_metadata
) {
81 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
82 DCHECK(!callback
.is_null());
84 scoped_ptr
<AppList
> app_list
;
85 if (account_metadata
) {
86 app_list
= AppList::CreateFromAccountMetadata(*account_metadata
);
89 callback
.Run(error
, app_list
.Pass());
94 GDataWapiService::GDataWapiService(
95 net::URLRequestContextGetter
* url_request_context_getter
,
97 const std::string
& custom_user_agent
)
98 : url_request_context_getter_(url_request_context_getter
),
100 url_generator_(base_url
),
101 custom_user_agent_(custom_user_agent
) {
102 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
105 GDataWapiService::~GDataWapiService() {
106 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
108 runner_
->operation_registry()->RemoveObserver(this);
109 runner_
->auth_service()->RemoveObserver(this);
113 AuthService
* GDataWapiService::auth_service_for_testing() {
114 return runner_
->auth_service();
117 void GDataWapiService::Initialize(Profile
* profile
) {
118 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
120 std::vector
<std::string
> scopes
;
121 scopes
.push_back(kDocsListScope
);
122 scopes
.push_back(kSpreadsheetsScope
);
123 scopes
.push_back(kUserContentScope
);
124 // Drive App scope is required for even WAPI v3 apps access.
125 scopes
.push_back(kDriveAppsScope
);
126 runner_
.reset(new OperationRunner(profile
,
127 url_request_context_getter_
,
129 custom_user_agent_
));
130 runner_
->Initialize();
132 runner_
->auth_service()->AddObserver(this);
133 runner_
->operation_registry()->AddObserver(this);
136 void GDataWapiService::AddObserver(DriveServiceObserver
* observer
) {
137 observers_
.AddObserver(observer
);
140 void GDataWapiService::RemoveObserver(DriveServiceObserver
* observer
) {
141 observers_
.RemoveObserver(observer
);
144 bool GDataWapiService::CanStartOperation() const {
145 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
147 return HasRefreshToken();
150 void GDataWapiService::CancelAll() {
151 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
152 runner_
->CancelAll();
155 bool GDataWapiService::CancelForFilePath(const base::FilePath
& file_path
) {
156 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
157 return operation_registry()->CancelForFilePath(file_path
);
160 OperationProgressStatusList
GDataWapiService::GetProgressStatusList() const {
161 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
162 return operation_registry()->GetProgressStatusList();
165 std::string
GDataWapiService::GetRootResourceId() const {
166 return kWapiRootDirectoryResourceId
;
169 // Because GData WAPI support is expected to be gone somehow soon by migration
170 // to the Drive API v2, so we'll reuse GetResourceListOperation to implement
171 // following methods, instead of cleaning the operation class.
173 void GDataWapiService::GetAllResourceList(
174 const GetResourceListCallback
& callback
) {
175 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
176 DCHECK(!callback
.is_null());
178 runner_
->StartOperationWithRetry(
179 new GetResourceListOperation(operation_registry(),
180 url_request_context_getter_
,
182 GURL(), // No override url
183 0, // start changestamp
184 std::string(), // empty search query
185 std::string(), // no directory resource id
189 void GDataWapiService::GetResourceListInDirectory(
190 const std::string
& directory_resource_id
,
191 const GetResourceListCallback
& callback
) {
192 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
193 DCHECK(!directory_resource_id
.empty());
194 DCHECK(!callback
.is_null());
196 runner_
->StartOperationWithRetry(
197 new GetResourceListOperation(operation_registry(),
198 url_request_context_getter_
,
200 GURL(), // No override url
201 0, // start changestamp
202 std::string(), // empty search query
203 directory_resource_id
,
207 void GDataWapiService::Search(const std::string
& search_query
,
208 const GetResourceListCallback
& callback
) {
209 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
210 DCHECK(!search_query
.empty());
211 DCHECK(!callback
.is_null());
213 runner_
->StartOperationWithRetry(
214 new GetResourceListOperation(operation_registry(),
215 url_request_context_getter_
,
217 GURL(), // No override url
218 0, // start changestamp
220 std::string(), // no directory resource id
224 void GDataWapiService::SearchInDirectory(
225 const std::string
& search_query
,
226 const std::string
& directory_resource_id
,
227 const GetResourceListCallback
& callback
) {
228 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
229 DCHECK(!search_query
.empty());
230 DCHECK(!directory_resource_id
.empty());
231 DCHECK(!callback
.is_null());
233 runner_
->StartOperationWithRetry(
234 new GetResourceListOperation(
235 operation_registry(),
236 url_request_context_getter_
,
238 GURL(), // No override url
239 0, // start changestamp
241 directory_resource_id
,
245 void GDataWapiService::GetChangeList(int64 start_changestamp
,
246 const GetResourceListCallback
& callback
) {
247 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
248 DCHECK(!callback
.is_null());
250 runner_
->StartOperationWithRetry(
251 new GetResourceListOperation(operation_registry(),
252 url_request_context_getter_
,
254 GURL(), // No override url
256 std::string(), // empty search query
257 std::string(), // no directory resource id
261 void GDataWapiService::ContinueGetResourceList(
262 const GURL
& override_url
,
263 const GetResourceListCallback
& callback
) {
264 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
265 DCHECK(!override_url
.is_empty());
266 DCHECK(!callback
.is_null());
268 runner_
->StartOperationWithRetry(
269 new GetResourceListOperation(operation_registry(),
270 url_request_context_getter_
,
273 0, // start changestamp
274 std::string(), // empty search query
275 std::string(), // no directory resource id
279 void GDataWapiService::GetResourceEntry(
280 const std::string
& resource_id
,
281 const GetResourceEntryCallback
& callback
) {
282 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
283 DCHECK(!callback
.is_null());
285 runner_
->StartOperationWithRetry(
286 new GetResourceEntryOperation(
287 operation_registry(),
288 url_request_context_getter_
,
291 base::Bind(&ParseResourceEntryAndRun
, callback
)));
294 void GDataWapiService::GetAccountMetadata(
295 const GetAccountMetadataCallback
& callback
) {
296 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
297 DCHECK(!callback
.is_null());
299 runner_
->StartOperationWithRetry(
300 new GetAccountMetadataOperation(
301 operation_registry(),
302 url_request_context_getter_
,
305 true)); // Include installed apps.
308 void GDataWapiService::GetAboutResource(
309 const GetAboutResourceCallback
& callback
) {
310 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
311 DCHECK(!callback
.is_null());
313 runner_
->StartOperationWithRetry(
314 new GetAccountMetadataOperation(
315 operation_registry(),
316 url_request_context_getter_
,
318 base::Bind(&ParseAboutResourceAndRun
, callback
),
319 false)); // Exclude installed apps.
322 void GDataWapiService::GetAppList(const GetAppListCallback
& callback
) {
323 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
324 DCHECK(!callback
.is_null());
326 runner_
->StartOperationWithRetry(
327 new GetAccountMetadataOperation(
328 operation_registry(),
329 url_request_context_getter_
,
331 base::Bind(&ParseAppListAndRun
, callback
),
332 true)); // Include installed apps.
335 void GDataWapiService::DownloadFile(
336 const base::FilePath
& virtual_path
,
337 const base::FilePath
& local_cache_path
,
338 const GURL
& download_url
,
339 const DownloadActionCallback
& download_action_callback
,
340 const GetContentCallback
& get_content_callback
,
341 const ProgressCallback
& progress_callback
) {
342 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
343 DCHECK(!download_action_callback
.is_null());
344 // get_content_callback and progress_callback may be null.
346 runner_
->StartOperationWithRetry(
347 new DownloadFileOperation(operation_registry(),
348 url_request_context_getter_
,
349 download_action_callback
,
350 get_content_callback
,
357 void GDataWapiService::DeleteResource(
358 const std::string
& resource_id
,
359 const std::string
& etag
,
360 const EntryActionCallback
& callback
) {
361 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
362 DCHECK(!callback
.is_null());
364 runner_
->StartOperationWithRetry(
365 new DeleteResourceOperation(operation_registry(),
366 url_request_context_getter_
,
373 void GDataWapiService::AddNewDirectory(
374 const std::string
& parent_resource_id
,
375 const std::string
& directory_name
,
376 const GetResourceEntryCallback
& callback
) {
377 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
378 DCHECK(!callback
.is_null());
380 runner_
->StartOperationWithRetry(
381 new CreateDirectoryOperation(operation_registry(),
382 url_request_context_getter_
,
384 base::Bind(&ParseResourceEntryAndRun
,
390 void GDataWapiService::CopyHostedDocument(
391 const std::string
& resource_id
,
392 const std::string
& new_name
,
393 const GetResourceEntryCallback
& callback
) {
394 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
395 DCHECK(!callback
.is_null());
397 runner_
->StartOperationWithRetry(
398 new CopyHostedDocumentOperation(
399 operation_registry(),
400 url_request_context_getter_
,
402 base::Bind(&ParseResourceEntryAndRun
, callback
),
407 void GDataWapiService::RenameResource(
408 const std::string
& resource_id
,
409 const std::string
& new_name
,
410 const EntryActionCallback
& callback
) {
411 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
412 DCHECK(!callback
.is_null());
414 runner_
->StartOperationWithRetry(
415 new RenameResourceOperation(operation_registry(),
416 url_request_context_getter_
,
423 void GDataWapiService::AddResourceToDirectory(
424 const std::string
& parent_resource_id
,
425 const std::string
& resource_id
,
426 const EntryActionCallback
& callback
) {
427 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
428 DCHECK(!callback
.is_null());
430 runner_
->StartOperationWithRetry(
431 new AddResourceToDirectoryOperation(operation_registry(),
432 url_request_context_getter_
,
439 void GDataWapiService::RemoveResourceFromDirectory(
440 const std::string
& parent_resource_id
,
441 const std::string
& resource_id
,
442 const EntryActionCallback
& callback
) {
443 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
444 DCHECK(!callback
.is_null());
446 runner_
->StartOperationWithRetry(
447 new RemoveResourceFromDirectoryOperation(operation_registry(),
448 url_request_context_getter_
,
455 void GDataWapiService::InitiateUploadNewFile(
456 const base::FilePath
& drive_file_path
,
457 const std::string
& content_type
,
458 int64 content_length
,
459 const std::string
& parent_resource_id
,
460 const std::string
& title
,
461 const InitiateUploadCallback
& callback
) {
462 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
463 DCHECK(!callback
.is_null());
464 DCHECK(!parent_resource_id
.empty());
466 runner_
->StartOperationWithRetry(
467 new InitiateUploadNewFileOperation(operation_registry(),
468 url_request_context_getter_
,
478 void GDataWapiService::InitiateUploadExistingFile(
479 const base::FilePath
& drive_file_path
,
480 const std::string
& content_type
,
481 int64 content_length
,
482 const std::string
& resource_id
,
483 const std::string
& etag
,
484 const InitiateUploadCallback
& callback
) {
485 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
486 DCHECK(!callback
.is_null());
487 DCHECK(!resource_id
.empty());
489 runner_
->StartOperationWithRetry(
490 new InitiateUploadExistingFileOperation(operation_registry(),
491 url_request_context_getter_
,
501 void GDataWapiService::ResumeUpload(
502 UploadMode upload_mode
,
503 const base::FilePath
& drive_file_path
,
504 const GURL
& upload_url
,
505 int64 start_position
,
507 int64 content_length
,
508 const std::string
& content_type
,
509 const scoped_refptr
<net::IOBuffer
>& buf
,
510 const UploadRangeCallback
& callback
,
511 const ProgressCallback
& progress_callback
) {
512 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
513 DCHECK(!callback
.is_null());
515 runner_
->StartOperationWithRetry(
516 new ResumeUploadOperation(operation_registry(),
517 url_request_context_getter_
,
530 void GDataWapiService::GetUploadStatus(
531 UploadMode upload_mode
,
532 const base::FilePath
& drive_file_path
,
533 const GURL
& upload_url
,
534 int64 content_length
,
535 const UploadRangeCallback
& callback
) {
536 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
537 DCHECK(!callback
.is_null());
539 runner_
->StartOperationWithRetry(
540 new GetUploadStatusOperation(operation_registry(),
541 url_request_context_getter_
,
549 void GDataWapiService::AuthorizeApp(const std::string
& resource_id
,
550 const std::string
& app_id
,
551 const AuthorizeAppCallback
& callback
) {
552 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
553 DCHECK(!callback
.is_null());
555 runner_
->StartOperationWithRetry(
556 new AuthorizeAppOperation(
557 operation_registry(),
558 url_request_context_getter_
,
565 bool GDataWapiService::HasAccessToken() const {
566 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
568 return runner_
->auth_service()->HasAccessToken();
571 bool GDataWapiService::HasRefreshToken() const {
572 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
574 return runner_
->auth_service()->HasRefreshToken();
577 void GDataWapiService::ClearAccessToken() {
578 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
579 return runner_
->auth_service()->ClearAccessToken();
582 void GDataWapiService::ClearRefreshToken() {
583 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
584 return runner_
->auth_service()->ClearRefreshToken();
587 OperationRegistry
* GDataWapiService::operation_registry() const {
588 return runner_
->operation_registry();
591 void GDataWapiService::OnOAuth2RefreshTokenChanged() {
592 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
593 if (CanStartOperation()) {
595 DriveServiceObserver
, observers_
, OnReadyToPerformOperations());
596 } else if (!HasRefreshToken()) {
598 DriveServiceObserver
, observers_
, OnRefreshTokenInvalid());
602 void GDataWapiService::OnProgressUpdate(
603 const OperationProgressStatusList
& list
) {
604 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
606 DriveServiceObserver
, observers_
, OnProgressUpdate(list
));
609 } // namespace google_apis