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/chromeos/drive/file_task_executor.h"
6
7#include <string>
8#include <vector>
9
10#include "chrome/browser/chromeos/drive/drive.pb.h"
11#include "chrome/browser/chromeos/drive/drive_integration_service.h"
12#include "chrome/browser/chromeos/drive/file_system_interface.h"
13#include "chrome/browser/drive/drive_service_interface.h"
14#include "chrome/browser/profiles/profile_manager.h"
15#include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
16#include "chrome/browser/ui/browser.h"
17#include "chrome/browser/ui/browser_tabstrip.h"
18#include "chrome/browser/ui/browser_window.h"
19#include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
20#include "chrome/common/extensions/api/file_manager_private.h"
21#include "content/public/browser/browser_thread.h"
22#include "storage/browser/fileapi/file_system_url.h"
23
24using storage::FileSystemURL;
25
26namespace drive {
27
28namespace {
29
30class FileTaskExecutorDelegateImpl : public FileTaskExecutorDelegate {
31 public:
32  explicit FileTaskExecutorDelegateImpl(Profile* profile) : profile_(profile) {
33  }
34
35  virtual FileSystemInterface* GetFileSystem() OVERRIDE {
36    return util::GetFileSystemByProfile(profile_);
37  }
38
39  virtual DriveServiceInterface* GetDriveService() OVERRIDE {
40    return util::GetDriveServiceByProfile(profile_);
41  }
42
43  virtual void OpenBrowserWindow(const GURL& open_link) OVERRIDE {
44    chrome::ScopedTabbedBrowserDisplayer displayer(
45         profile_, chrome::HOST_DESKTOP_TYPE_ASH);
46    chrome::AddSelectedTabWithURL(displayer.browser(), open_link,
47                                  ui::PAGE_TRANSITION_LINK);
48    // Since the ScopedTabbedBrowserDisplayer does not guarantee that the
49    // browser will be shown on the active desktop, we ensure the visibility.
50    multi_user_util::MoveWindowToCurrentDesktop(
51        displayer.browser()->window()->GetNativeWindow());
52  }
53
54 private:
55  Profile* const profile_;
56};
57
58}  // namespace
59
60FileTaskExecutor::FileTaskExecutor(Profile* profile, const std::string& app_id)
61  : delegate_(new FileTaskExecutorDelegateImpl(profile)),
62    app_id_(app_id),
63    current_index_(0),
64    weak_ptr_factory_(this) {
65}
66
67FileTaskExecutor::FileTaskExecutor(
68    scoped_ptr<FileTaskExecutorDelegate> delegate,
69    const std::string& app_id)
70  : delegate_(delegate.Pass()),
71    app_id_(app_id),
72    current_index_(0),
73    weak_ptr_factory_(this) {
74}
75
76FileTaskExecutor::~FileTaskExecutor() {
77}
78
79void FileTaskExecutor::Execute(
80    const std::vector<FileSystemURL>& file_urls,
81    const file_manager::file_tasks::FileTaskFinishedCallback& done) {
82  DCHECK(!file_urls.empty());
83
84  done_ = done;
85
86  std::vector<base::FilePath> paths;
87  for (size_t i = 0; i < file_urls.size(); ++i) {
88    base::FilePath path = util::ExtractDrivePathFromFileSystemUrl(file_urls[i]);
89    if (path.empty()) {
90      Done(false);
91      return;
92    }
93    paths.push_back(path);
94  }
95
96  FileSystemInterface* const file_system = delegate_->GetFileSystem();
97  if (!file_system) {
98    Done(false);
99    return;
100  }
101
102  // Reset the index, so we know when we're done.
103  DCHECK_EQ(current_index_, 0);
104  current_index_ = paths.size();
105
106  for (size_t i = 0; i < paths.size(); ++i) {
107    file_system->GetResourceEntry(
108        paths[i],
109        base::Bind(&FileTaskExecutor::OnFileEntryFetched,
110                   weak_ptr_factory_.GetWeakPtr()));
111  }
112}
113
114void FileTaskExecutor::OnFileEntryFetched(FileError error,
115                                          scoped_ptr<ResourceEntry> entry) {
116  // Here, we are only interested in files.
117  if (entry.get() && !entry->has_file_specific_info())
118    error = FILE_ERROR_NOT_FOUND;
119
120  DriveServiceInterface* const drive_service = delegate_->GetDriveService();
121  if (!drive_service || error != FILE_ERROR_OK) {
122    Done(false);
123    return;
124  }
125
126  // Send off a request for the drive service to authorize the apps for the
127  // current document entry for this document so we can get the
128  // open-with-<app_id> urls from the document entry.
129  drive_service->AuthorizeApp(entry->resource_id(),
130                              app_id_,
131                              base::Bind(&FileTaskExecutor::OnAppAuthorized,
132                                         weak_ptr_factory_.GetWeakPtr(),
133                                         entry->resource_id()));
134}
135
136void FileTaskExecutor::OnAppAuthorized(const std::string& resource_id,
137                                       google_apis::GDataErrorCode error,
138                                       const GURL& open_link) {
139  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
140
141  if (error != google_apis::HTTP_SUCCESS || open_link.is_empty()) {
142    Done(false);
143    return;
144  }
145
146  delegate_->OpenBrowserWindow(open_link);
147
148  // We're done with this file.  If this is the last one, then we're done.
149  current_index_--;
150  DCHECK_GE(current_index_, 0);
151  if (current_index_ == 0)
152    Done(true);
153}
154
155void FileTaskExecutor::Done(bool success) {
156  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
157  if (!done_.is_null())
158    done_.Run(success
159                  ? extensions::api::file_manager_private::TASK_RESULT_OPENED
160                  : extensions::api::file_manager_private::TASK_RESULT_FAILED);
161  delete this;
162}
163
164}  // namespace drive
165