Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / drive / gdata_wapi_service.cc
blobe9d9a45456d32335ea25c4528a05ed8665f723c8
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/drive/gdata_wapi_service.h"
7 #include <string>
8 #include <vector>
10 #include "base/bind.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/sequenced_task_runner.h"
13 #include "base/values.h"
14 #include "chrome/browser/drive/drive_api_util.h"
15 #include "content/public/browser/browser_thread.h"
16 #include "google_apis/drive/auth_service.h"
17 #include "google_apis/drive/drive_api_parser.h"
18 #include "google_apis/drive/gdata_errorcode.h"
19 #include "google_apis/drive/gdata_wapi_parser.h"
20 #include "google_apis/drive/gdata_wapi_requests.h"
21 #include "google_apis/drive/gdata_wapi_url_generator.h"
22 #include "google_apis/drive/request_sender.h"
23 #include "net/url_request/url_request_context_getter.h"
25 using content::BrowserThread;
26 using google_apis::AboutResource;
27 using google_apis::AboutResourceCallback;
28 using google_apis::AccountMetadata;
29 using google_apis::AddResourceToDirectoryRequest;
30 using google_apis::AppList;
31 using google_apis::AppListCallback;
32 using google_apis::AuthService;
33 using google_apis::AuthStatusCallback;
34 using google_apis::AuthorizeAppCallback;
35 using google_apis::AuthorizeAppRequest;
36 using google_apis::CancelCallback;
37 using google_apis::CreateDirectoryRequest;
38 using google_apis::DeleteResourceRequest;
39 using google_apis::DownloadActionCallback;
40 using google_apis::DownloadFileRequest;
41 using google_apis::EntryActionCallback;
42 using google_apis::GDATA_PARSE_ERROR;
43 using google_apis::GDataErrorCode;
44 using google_apis::GetAccountMetadataRequest;
45 using google_apis::GetContentCallback;
46 using google_apis::GetResourceEntryCallback;
47 using google_apis::GetResourceEntryRequest;
48 using google_apis::GetResourceListCallback;
49 using google_apis::GetResourceListRequest;
50 using google_apis::GetShareUrlCallback;
51 using google_apis::GetUploadStatusRequest;
52 using google_apis::HTTP_NOT_IMPLEMENTED;
53 using google_apis::InitiateUploadCallback;
54 using google_apis::InitiateUploadExistingFileRequest;
55 using google_apis::InitiateUploadNewFileRequest;
56 using google_apis::Link;
57 using google_apis::ProgressCallback;
58 using google_apis::RemoveResourceFromDirectoryRequest;
59 using google_apis::RenameResourceRequest;
60 using google_apis::RequestSender;
61 using google_apis::ResourceEntry;
62 using google_apis::ResumeUploadRequest;
63 using google_apis::SearchByTitleRequest;
64 using google_apis::UploadRangeCallback;
66 namespace drive {
68 namespace {
70 // OAuth2 scopes for the documents API.
71 const char kSpreadsheetsScope[] = "https://spreadsheets.google.com/feeds/";
72 const char kUserContentScope[] = "https://docs.googleusercontent.com/";
74 // Parses the JSON value to ResourceEntry runs |callback|.
75 void ParseResourceEntryAndRun(const GetResourceEntryCallback& callback,
76 GDataErrorCode error,
77 scoped_ptr<base::Value> value) {
78 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
80 if (!value) {
81 callback.Run(error, scoped_ptr<ResourceEntry>());
82 return;
85 // Parsing ResourceEntry is cheap enough to do on UI thread.
86 scoped_ptr<ResourceEntry> entry =
87 google_apis::ResourceEntry::ExtractAndParse(*value);
88 if (!entry) {
89 callback.Run(GDATA_PARSE_ERROR, scoped_ptr<ResourceEntry>());
90 return;
93 callback.Run(error, entry.Pass());
96 void ConvertAboutResourceAndRun(
97 const AboutResourceCallback& callback,
98 GDataErrorCode error,
99 scoped_ptr<AccountMetadata> account_metadata) {
100 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
101 DCHECK(!callback.is_null());
103 scoped_ptr<AboutResource> about_resource;
104 if (account_metadata) {
105 about_resource = util::ConvertAccountMetadataToAboutResource(
106 *account_metadata, util::kWapiRootDirectoryResourceId);
109 callback.Run(error, about_resource.Pass());
112 void ConvertAppListAndRun(
113 const AppListCallback& callback,
114 GDataErrorCode error,
115 scoped_ptr<AccountMetadata> account_metadata) {
116 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
117 DCHECK(!callback.is_null());
119 scoped_ptr<AppList> app_list;
120 if (account_metadata)
121 app_list = util::ConvertAccountMetadataToAppList(*account_metadata);
123 callback.Run(error, app_list.Pass());
126 } // namespace
128 GDataWapiService::GDataWapiService(
129 OAuth2TokenService* oauth2_token_service,
130 net::URLRequestContextGetter* url_request_context_getter,
131 base::SequencedTaskRunner* blocking_task_runner,
132 const GURL& base_url,
133 const GURL& base_download_url,
134 const std::string& custom_user_agent)
135 : oauth2_token_service_(oauth2_token_service),
136 url_request_context_getter_(url_request_context_getter),
137 blocking_task_runner_(blocking_task_runner),
138 url_generator_(base_url, base_download_url),
139 custom_user_agent_(custom_user_agent) {
140 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
143 GDataWapiService::~GDataWapiService() {
144 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
145 if (sender_.get())
146 sender_->auth_service()->RemoveObserver(this);
149 void GDataWapiService::Initialize(const std::string& account_id) {
150 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
152 std::vector<std::string> scopes;
153 scopes.push_back(util::kDocsListScope);
154 scopes.push_back(kSpreadsheetsScope);
155 scopes.push_back(kUserContentScope);
156 // Drive App scope is required for even WAPI v3 apps access.
157 scopes.push_back(util::kDriveAppsScope);
158 sender_.reset(new RequestSender(
159 new AuthService(oauth2_token_service_,
160 account_id,
161 url_request_context_getter_.get(),
162 scopes),
163 url_request_context_getter_.get(),
164 blocking_task_runner_.get(),
165 custom_user_agent_));
167 sender_->auth_service()->AddObserver(this);
170 void GDataWapiService::AddObserver(DriveServiceObserver* observer) {
171 observers_.AddObserver(observer);
174 void GDataWapiService::RemoveObserver(DriveServiceObserver* observer) {
175 observers_.RemoveObserver(observer);
178 bool GDataWapiService::CanSendRequest() const {
179 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
181 return HasRefreshToken();
184 ResourceIdCanonicalizer GDataWapiService::GetResourceIdCanonicalizer() const {
185 return util::GetIdentityResourceIdCanonicalizer();
188 std::string GDataWapiService::GetRootResourceId() const {
189 return util::kWapiRootDirectoryResourceId;
192 // Because GData WAPI support is expected to be gone somehow soon by migration
193 // to the Drive API v2, so we'll reuse GetResourceListRequest to implement
194 // following methods, instead of cleaning the request class.
196 CancelCallback GDataWapiService::GetAllResourceList(
197 const GetResourceListCallback& callback) {
198 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
199 DCHECK(!callback.is_null());
201 return sender_->StartRequestWithRetry(
202 new GetResourceListRequest(sender_.get(),
203 url_generator_,
204 GURL(), // No override url
205 0, // start changestamp
206 std::string(), // empty search query
207 std::string(), // no directory resource id
208 callback));
211 CancelCallback GDataWapiService::GetResourceListInDirectory(
212 const std::string& directory_resource_id,
213 const GetResourceListCallback& callback) {
214 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
215 DCHECK(!directory_resource_id.empty());
216 DCHECK(!callback.is_null());
218 return sender_->StartRequestWithRetry(
219 new GetResourceListRequest(sender_.get(),
220 url_generator_,
221 GURL(), // No override url
222 0, // start changestamp
223 std::string(), // empty search query
224 directory_resource_id,
225 callback));
228 CancelCallback GDataWapiService::Search(
229 const std::string& search_query,
230 const GetResourceListCallback& callback) {
231 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
232 DCHECK(!search_query.empty());
233 DCHECK(!callback.is_null());
235 return sender_->StartRequestWithRetry(
236 new GetResourceListRequest(sender_.get(),
237 url_generator_,
238 GURL(), // No override url
239 0, // start changestamp
240 search_query,
241 std::string(), // no directory resource id
242 callback));
245 CancelCallback GDataWapiService::SearchByTitle(
246 const std::string& title,
247 const std::string& directory_resource_id,
248 const GetResourceListCallback& callback) {
249 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
250 DCHECK(!title.empty());
251 DCHECK(!callback.is_null());
253 return sender_->StartRequestWithRetry(
254 new SearchByTitleRequest(sender_.get(),
255 url_generator_,
256 title,
257 directory_resource_id,
258 callback));
261 CancelCallback GDataWapiService::GetChangeList(
262 int64 start_changestamp,
263 const GetResourceListCallback& callback) {
264 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
265 DCHECK(!callback.is_null());
267 return sender_->StartRequestWithRetry(
268 new GetResourceListRequest(sender_.get(),
269 url_generator_,
270 GURL(), // No override url
271 start_changestamp,
272 std::string(), // empty search query
273 std::string(), // no directory resource id
274 callback));
277 CancelCallback GDataWapiService::GetRemainingChangeList(
278 const GURL& next_link,
279 const GetResourceListCallback& callback) {
280 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
281 DCHECK(!next_link.is_empty());
282 DCHECK(!callback.is_null());
284 return GetRemainingResourceList(next_link, callback);
287 CancelCallback GDataWapiService::GetRemainingFileList(
288 const GURL& next_link,
289 const GetResourceListCallback& callback) {
290 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
291 DCHECK(!next_link.is_empty());
292 DCHECK(!callback.is_null());
294 return GetRemainingResourceList(next_link, callback);
297 CancelCallback GDataWapiService::GetResourceEntry(
298 const std::string& resource_id,
299 const GetResourceEntryCallback& callback) {
300 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
301 DCHECK(!callback.is_null());
303 return sender_->StartRequestWithRetry(
304 new GetResourceEntryRequest(sender_.get(),
305 url_generator_,
306 resource_id,
307 GURL(),
308 base::Bind(&ParseResourceEntryAndRun,
309 callback)));
312 CancelCallback GDataWapiService::GetShareUrl(
313 const std::string& resource_id,
314 const GURL& embed_origin,
315 const GetShareUrlCallback& callback) {
316 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
317 DCHECK(!callback.is_null());
319 return sender_->StartRequestWithRetry(
320 new GetResourceEntryRequest(sender_.get(),
321 url_generator_,
322 resource_id,
323 embed_origin,
324 base::Bind(&util::ParseShareUrlAndRun,
325 callback)));
328 CancelCallback GDataWapiService::GetAboutResource(
329 const AboutResourceCallback& callback) {
330 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
331 DCHECK(!callback.is_null());
333 return sender_->StartRequestWithRetry(
334 new GetAccountMetadataRequest(
335 sender_.get(),
336 url_generator_,
337 base::Bind(&ConvertAboutResourceAndRun, callback),
338 false)); // Exclude installed apps.
341 CancelCallback GDataWapiService::GetAppList(const AppListCallback& callback) {
342 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
343 DCHECK(!callback.is_null());
345 return sender_->StartRequestWithRetry(
346 new GetAccountMetadataRequest(sender_.get(),
347 url_generator_,
348 base::Bind(&ConvertAppListAndRun, callback),
349 true)); // Include installed apps.
352 CancelCallback GDataWapiService::DownloadFile(
353 const base::FilePath& local_cache_path,
354 const std::string& resource_id,
355 const DownloadActionCallback& download_action_callback,
356 const GetContentCallback& get_content_callback,
357 const ProgressCallback& progress_callback) {
358 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
359 DCHECK(!download_action_callback.is_null());
360 // get_content_callback and progress_callback may be null.
362 return sender_->StartRequestWithRetry(
363 new DownloadFileRequest(sender_.get(),
364 url_generator_,
365 download_action_callback,
366 get_content_callback,
367 progress_callback,
368 resource_id,
369 local_cache_path));
372 CancelCallback GDataWapiService::DeleteResource(
373 const std::string& resource_id,
374 const std::string& etag,
375 const EntryActionCallback& callback) {
376 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
377 DCHECK(!callback.is_null());
379 NOTIMPLEMENTED();
380 callback.Run(google_apis::GDATA_OTHER_ERROR);
381 return CancelCallback();
384 CancelCallback GDataWapiService::TrashResource(
385 const std::string& resource_id,
386 const EntryActionCallback& callback) {
387 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
388 DCHECK(!callback.is_null());
390 const std::string empty_etag;
391 return sender_->StartRequestWithRetry(
392 new DeleteResourceRequest(sender_.get(),
393 url_generator_,
394 callback,
395 resource_id,
396 empty_etag));
399 CancelCallback GDataWapiService::AddNewDirectory(
400 const std::string& parent_resource_id,
401 const std::string& directory_title,
402 const GetResourceEntryCallback& callback) {
403 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
404 DCHECK(!callback.is_null());
406 return sender_->StartRequestWithRetry(
407 new CreateDirectoryRequest(sender_.get(),
408 url_generator_,
409 base::Bind(&ParseResourceEntryAndRun,
410 callback),
411 parent_resource_id,
412 directory_title));
415 CancelCallback GDataWapiService::CopyResource(
416 const std::string& resource_id,
417 const std::string& parent_resource_id,
418 const std::string& new_title,
419 const base::Time& last_modified,
420 const GetResourceEntryCallback& callback) {
421 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
422 DCHECK(!callback.is_null());
424 // GData WAPI doesn't support "copy" of regular files.
425 // This method should never be called if GData WAPI is enabled.
426 // Instead, client code should download the file (if needed) and upload it.
427 NOTREACHED();
428 return CancelCallback();
431 CancelCallback GDataWapiService::UpdateResource(
432 const std::string& resource_id,
433 const std::string& parent_resource_id,
434 const std::string& new_title,
435 const base::Time& last_modified,
436 const base::Time& last_viewed_by_me,
437 const google_apis::GetResourceEntryCallback& callback) {
438 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
439 DCHECK(!callback.is_null());
441 // GData WAPI doesn't support to "move" resources.
442 // This method should never be called if GData WAPI is enabled.
443 // Instead, client code should rename the file, add new parent, and then
444 // remove the old parent.
445 NOTREACHED();
446 return CancelCallback();
449 CancelCallback GDataWapiService::RenameResource(
450 const std::string& resource_id,
451 const std::string& new_title,
452 const EntryActionCallback& callback) {
453 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
454 DCHECK(!callback.is_null());
456 return sender_->StartRequestWithRetry(
457 new RenameResourceRequest(sender_.get(),
458 url_generator_,
459 callback,
460 resource_id,
461 new_title));
464 CancelCallback GDataWapiService::AddResourceToDirectory(
465 const std::string& parent_resource_id,
466 const std::string& resource_id,
467 const EntryActionCallback& callback) {
468 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
469 DCHECK(!callback.is_null());
471 return sender_->StartRequestWithRetry(
472 new AddResourceToDirectoryRequest(sender_.get(),
473 url_generator_,
474 callback,
475 parent_resource_id,
476 resource_id));
479 CancelCallback GDataWapiService::RemoveResourceFromDirectory(
480 const std::string& parent_resource_id,
481 const std::string& resource_id,
482 const EntryActionCallback& callback) {
483 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
484 DCHECK(!callback.is_null());
486 return sender_->StartRequestWithRetry(
487 new RemoveResourceFromDirectoryRequest(sender_.get(),
488 url_generator_,
489 callback,
490 parent_resource_id,
491 resource_id));
494 CancelCallback GDataWapiService::InitiateUploadNewFile(
495 const std::string& content_type,
496 int64 content_length,
497 const std::string& parent_resource_id,
498 const std::string& title,
499 const InitiateUploadCallback& callback) {
500 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
501 DCHECK(!callback.is_null());
502 DCHECK(!parent_resource_id.empty());
504 return sender_->StartRequestWithRetry(
505 new InitiateUploadNewFileRequest(sender_.get(),
506 url_generator_,
507 callback,
508 content_type,
509 content_length,
510 parent_resource_id,
511 title));
514 CancelCallback GDataWapiService::InitiateUploadExistingFile(
515 const std::string& content_type,
516 int64 content_length,
517 const std::string& resource_id,
518 const std::string& etag,
519 const InitiateUploadCallback& callback) {
520 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
521 DCHECK(!callback.is_null());
522 DCHECK(!resource_id.empty());
524 return sender_->StartRequestWithRetry(
525 new InitiateUploadExistingFileRequest(sender_.get(),
526 url_generator_,
527 callback,
528 content_type,
529 content_length,
530 resource_id,
531 etag));
534 CancelCallback GDataWapiService::ResumeUpload(
535 const GURL& upload_url,
536 int64 start_position,
537 int64 end_position,
538 int64 content_length,
539 const std::string& content_type,
540 const base::FilePath& local_file_path,
541 const UploadRangeCallback& callback,
542 const ProgressCallback& progress_callback) {
543 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
544 DCHECK(!callback.is_null());
546 return sender_->StartRequestWithRetry(
547 new ResumeUploadRequest(sender_.get(),
548 callback,
549 progress_callback,
550 upload_url,
551 start_position,
552 end_position,
553 content_length,
554 content_type,
555 local_file_path));
558 CancelCallback GDataWapiService::GetUploadStatus(
559 const GURL& upload_url,
560 int64 content_length,
561 const UploadRangeCallback& callback) {
562 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
563 DCHECK(!callback.is_null());
565 return sender_->StartRequestWithRetry(
566 new GetUploadStatusRequest(sender_.get(),
567 callback,
568 upload_url,
569 content_length));
572 CancelCallback GDataWapiService::AuthorizeApp(
573 const std::string& resource_id,
574 const std::string& app_id,
575 const AuthorizeAppCallback& callback) {
576 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
577 DCHECK(!callback.is_null());
579 return sender_->StartRequestWithRetry(
580 new AuthorizeAppRequest(sender_.get(),
581 url_generator_,
582 callback,
583 resource_id,
584 app_id));
587 CancelCallback GDataWapiService::UninstallApp(
588 const std::string& app_id,
589 const EntryActionCallback& callback) {
590 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
591 DCHECK(!callback.is_null());
593 // GData WAPI doesn't support app uninstallation.
594 // This method should never be called if GData WAPI is enabled.
595 NOTREACHED();
596 return CancelCallback();
599 CancelCallback GDataWapiService::GetResourceListInDirectoryByWapi(
600 const std::string& directory_resource_id,
601 const google_apis::GetResourceListCallback& callback) {
602 return GetResourceListInDirectory(directory_resource_id, callback);
605 CancelCallback GDataWapiService::GetRemainingResourceList(
606 const GURL& next_link,
607 const google_apis::GetResourceListCallback& callback) {
608 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
609 DCHECK(!next_link.is_empty());
610 DCHECK(!callback.is_null());
612 return sender_->StartRequestWithRetry(
613 new GetResourceListRequest(sender_.get(),
614 url_generator_,
615 next_link,
616 0, // start changestamp
617 std::string(), // empty search query
618 std::string(), // no directory resource id
619 callback));
622 bool GDataWapiService::HasAccessToken() const {
623 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
625 return sender_->auth_service()->HasAccessToken();
628 void GDataWapiService::RequestAccessToken(const AuthStatusCallback& callback) {
629 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
630 DCHECK(!callback.is_null());
632 const std::string access_token = sender_->auth_service()->access_token();
633 if (!access_token.empty()) {
634 callback.Run(google_apis::HTTP_NOT_MODIFIED, access_token);
635 return;
638 // Retrieve the new auth token.
639 sender_->auth_service()->StartAuthentication(callback);
642 bool GDataWapiService::HasRefreshToken() const {
643 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
644 return sender_->auth_service()->HasRefreshToken();
647 void GDataWapiService::ClearAccessToken() {
648 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
649 sender_->auth_service()->ClearAccessToken();
652 void GDataWapiService::ClearRefreshToken() {
653 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
654 sender_->auth_service()->ClearRefreshToken();
657 void GDataWapiService::OnOAuth2RefreshTokenChanged() {
658 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
659 if (CanSendRequest()) {
660 FOR_EACH_OBSERVER(
661 DriveServiceObserver, observers_, OnReadyToSendRequests());
662 } else if (!HasRefreshToken()) {
663 FOR_EACH_OBSERVER(
664 DriveServiceObserver, observers_, OnRefreshTokenInvalid());
668 } // namespace drive