1// Copyright 2013 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_system/get_file_for_saving_operation.h"
6
7#include "base/bind.h"
8#include "base/bind_helpers.h"
9#include "base/callback_helpers.h"
10#include "chrome/browser/chromeos/drive/file_cache.h"
11#include "chrome/browser/chromeos/drive/file_system/create_file_operation.h"
12#include "chrome/browser/chromeos/drive/file_system/download_operation.h"
13#include "chrome/browser/chromeos/drive/file_system/operation_delegate.h"
14#include "chrome/browser/chromeos/drive/file_write_watcher.h"
15#include "chrome/browser/chromeos/drive/job_scheduler.h"
16#include "chrome/browser/drive/event_logger.h"
17#include "content/public/browser/browser_thread.h"
18
19using content::BrowserThread;
20
21namespace drive {
22namespace file_system {
23
24namespace {
25
26FileError OpenCacheFileForWrite(
27    internal::ResourceMetadata* metadata,
28    internal::FileCache* cache,
29    const std::string& local_id,
30    scoped_ptr<base::ScopedClosureRunner>* file_closer,
31    ResourceEntry* entry) {
32  FileError error = cache->OpenForWrite(local_id, file_closer);
33  if (error != FILE_ERROR_OK)
34    return error;
35  return metadata->GetResourceEntryById(local_id, entry);
36}
37
38}  // namespace
39
40GetFileForSavingOperation::GetFileForSavingOperation(
41    EventLogger* logger,
42    base::SequencedTaskRunner* blocking_task_runner,
43    OperationDelegate* delegate,
44    JobScheduler* scheduler,
45    internal::ResourceMetadata* metadata,
46    internal::FileCache* cache,
47    const base::FilePath& temporary_file_directory)
48    : logger_(logger),
49      create_file_operation_(new CreateFileOperation(blocking_task_runner,
50                                                     delegate,
51                                                     metadata)),
52      download_operation_(new DownloadOperation(blocking_task_runner,
53                                                delegate,
54                                                scheduler,
55                                                metadata,
56                                                cache,
57                                                temporary_file_directory)),
58      file_write_watcher_(new internal::FileWriteWatcher),
59      blocking_task_runner_(blocking_task_runner),
60      delegate_(delegate),
61      metadata_(metadata),
62      cache_(cache),
63      weak_ptr_factory_(this) {
64}
65
66GetFileForSavingOperation::~GetFileForSavingOperation() {
67}
68
69void GetFileForSavingOperation::GetFileForSaving(
70    const base::FilePath& file_path,
71    const GetFileCallback& callback) {
72  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
73  DCHECK(!callback.is_null());
74
75  create_file_operation_->CreateFile(
76      file_path,
77      false,  // error_if_already_exists
78      std::string(),  // no specific mime type
79      base::Bind(&GetFileForSavingOperation::GetFileForSavingAfterCreate,
80                 weak_ptr_factory_.GetWeakPtr(),
81                 file_path,
82                 callback));
83}
84
85void GetFileForSavingOperation::GetFileForSavingAfterCreate(
86    const base::FilePath& file_path,
87    const GetFileCallback& callback,
88    FileError error) {
89  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
90  DCHECK(!callback.is_null());
91
92  if (error != FILE_ERROR_OK) {
93    callback.Run(error, base::FilePath(), scoped_ptr<ResourceEntry>());
94    return;
95  }
96
97  download_operation_->EnsureFileDownloadedByPath(
98      file_path,
99      ClientContext(USER_INITIATED),
100      GetFileContentInitializedCallback(),
101      google_apis::GetContentCallback(),
102      base::Bind(&GetFileForSavingOperation::GetFileForSavingAfterDownload,
103                 weak_ptr_factory_.GetWeakPtr(),
104                 callback));
105}
106
107void GetFileForSavingOperation::GetFileForSavingAfterDownload(
108    const GetFileCallback& callback,
109    FileError error,
110    const base::FilePath& cache_path,
111    scoped_ptr<ResourceEntry> entry) {
112  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
113  DCHECK(!callback.is_null());
114
115  if (error != FILE_ERROR_OK) {
116    callback.Run(error, base::FilePath(), scoped_ptr<ResourceEntry>());
117    return;
118  }
119
120  const std::string& local_id = entry->local_id();
121  ResourceEntry* entry_ptr = entry.get();
122  scoped_ptr<base::ScopedClosureRunner>* file_closer =
123      new scoped_ptr<base::ScopedClosureRunner>;
124  base::PostTaskAndReplyWithResult(
125      blocking_task_runner_.get(),
126      FROM_HERE,
127      base::Bind(&OpenCacheFileForWrite,
128                 metadata_,
129                 cache_,
130                 local_id,
131                 file_closer,
132                 entry_ptr),
133      base::Bind(&GetFileForSavingOperation::GetFileForSavingAfterOpenForWrite,
134                 weak_ptr_factory_.GetWeakPtr(),
135                 callback,
136                 cache_path,
137                 base::Passed(&entry),
138                 base::Owned(file_closer)));
139}
140
141void GetFileForSavingOperation::GetFileForSavingAfterOpenForWrite(
142    const GetFileCallback& callback,
143    const base::FilePath& cache_path,
144    scoped_ptr<ResourceEntry> entry,
145    scoped_ptr<base::ScopedClosureRunner>* file_closer,
146    FileError error) {
147  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
148  DCHECK(!callback.is_null());
149
150  if (error != FILE_ERROR_OK) {
151    callback.Run(error, base::FilePath(), scoped_ptr<ResourceEntry>());
152    return;
153  }
154
155  const std::string& local_id = entry->local_id();
156  file_write_watcher_->StartWatch(
157      cache_path,
158      base::Bind(&GetFileForSavingOperation::GetFileForSavingAfterWatch,
159                 weak_ptr_factory_.GetWeakPtr(),
160                 callback,
161                 cache_path,
162                 base::Passed(&entry)),
163      base::Bind(&GetFileForSavingOperation::OnWriteEvent,
164                 weak_ptr_factory_.GetWeakPtr(),
165                 local_id,
166                 base::Passed(file_closer)));
167}
168
169void GetFileForSavingOperation::GetFileForSavingAfterWatch(
170    const GetFileCallback& callback,
171    const base::FilePath& cache_path,
172    scoped_ptr<ResourceEntry> entry,
173    bool success) {
174  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
175  DCHECK(!callback.is_null());
176
177  logger_->Log(logging::LOG_INFO, "Started watching modification to %s [%s].",
178               entry->local_id().c_str(),
179               success ? "ok" : "fail");
180
181  if (!success) {
182    callback.Run(FILE_ERROR_FAILED,
183                 base::FilePath(), scoped_ptr<ResourceEntry>());
184    return;
185  }
186
187  callback.Run(FILE_ERROR_OK, cache_path, entry.Pass());
188}
189
190void GetFileForSavingOperation::OnWriteEvent(
191    const std::string& local_id,
192    scoped_ptr<base::ScopedClosureRunner> file_closer) {
193  logger_->Log(logging::LOG_INFO, "Detected modification to %s.",
194               local_id.c_str());
195
196  delegate_->OnEntryUpdatedByOperation(local_id);
197
198  // Clients may have enlarged the file. By FreeDiskpSpaceIfNeededFor(0),
199  // we try to ensure (0 + the-minimum-safe-margin = 512MB as of now) space.
200  blocking_task_runner_->PostTask(
201      FROM_HERE,
202      base::Bind(base::IgnoreResult(
203          base::Bind(&internal::FileCache::FreeDiskSpaceIfNeededFor,
204                     base::Unretained(cache_),
205                     0))));
206}
207
208}  // namespace file_system
209}  // namespace drive
210