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_system/move_operation.h"
6
7#include "base/sequenced_task_runner.h"
8#include "chrome/browser/chromeos/drive/drive.pb.h"
9#include "chrome/browser/chromeos/drive/file_change.h"
10#include "chrome/browser/chromeos/drive/file_system/operation_delegate.h"
11#include "chrome/browser/chromeos/drive/resource_metadata.h"
12#include "content/public/browser/browser_thread.h"
13
14using content::BrowserThread;
15
16namespace drive {
17namespace file_system {
18namespace {
19
20// Looks up ResourceEntry for source entry and the destination directory.
21FileError UpdateLocalState(internal::ResourceMetadata* metadata,
22                           const base::FilePath& src_path,
23                           const base::FilePath& dest_path,
24                           FileChange* changed_files,
25                           std::string* local_id) {
26  ResourceEntry entry;
27  FileError error = metadata->GetResourceEntryByPath(src_path, &entry);
28  if (error != FILE_ERROR_OK)
29    return error;
30  *local_id = entry.local_id();
31
32  ResourceEntry parent_entry;
33  error = metadata->GetResourceEntryByPath(dest_path.DirName(), &parent_entry);
34  if (error != FILE_ERROR_OK)
35    return error;
36
37  // The parent must be a directory.
38  if (!parent_entry.file_info().is_directory())
39    return FILE_ERROR_NOT_A_DIRECTORY;
40
41  changed_files->Update(src_path, entry, FileChange::DELETE);
42
43  // Strip the extension for a hosted document if necessary.
44  const std::string new_extension =
45      base::FilePath(dest_path.Extension()).AsUTF8Unsafe();
46  const bool has_hosted_document_extension =
47      entry.has_file_specific_info() &&
48      entry.file_specific_info().is_hosted_document() &&
49      new_extension == entry.file_specific_info().document_extension();
50  const std::string new_title =
51      has_hosted_document_extension ?
52      dest_path.BaseName().RemoveExtension().AsUTF8Unsafe() :
53      dest_path.BaseName().AsUTF8Unsafe();
54
55  entry.set_title(new_title);
56  entry.set_parent_local_id(parent_entry.local_id());
57  entry.set_metadata_edit_state(ResourceEntry::DIRTY);
58  entry.set_modification_date(base::Time::Now().ToInternalValue());
59  error = metadata->RefreshEntry(entry);
60  if (error != FILE_ERROR_OK)
61    return error;
62
63  changed_files->Update(dest_path, entry, FileChange::ADD_OR_UPDATE);
64  return FILE_ERROR_OK;
65}
66
67}  // namespace
68
69MoveOperation::MoveOperation(base::SequencedTaskRunner* blocking_task_runner,
70                             OperationDelegate* delegate,
71                             internal::ResourceMetadata* metadata)
72    : blocking_task_runner_(blocking_task_runner),
73      delegate_(delegate),
74      metadata_(metadata),
75      weak_ptr_factory_(this) {
76  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
77}
78
79MoveOperation::~MoveOperation() {
80  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
81}
82
83void MoveOperation::Move(const base::FilePath& src_file_path,
84                         const base::FilePath& dest_file_path,
85                         const FileOperationCallback& callback) {
86  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
87  DCHECK(!callback.is_null());
88
89  FileChange* changed_files = new FileChange;
90  std::string* local_id = new std::string;
91  base::PostTaskAndReplyWithResult(
92      blocking_task_runner_.get(),
93      FROM_HERE,
94      base::Bind(&UpdateLocalState,
95                 metadata_,
96                 src_file_path,
97                 dest_file_path,
98                 changed_files,
99                 local_id),
100      base::Bind(&MoveOperation::MoveAfterUpdateLocalState,
101                 weak_ptr_factory_.GetWeakPtr(),
102                 callback,
103                 base::Owned(changed_files),
104                 base::Owned(local_id)));
105}
106
107void MoveOperation::MoveAfterUpdateLocalState(
108    const FileOperationCallback& callback,
109    const FileChange* changed_files,
110    const std::string* local_id,
111    FileError error) {
112  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
113  if (error == FILE_ERROR_OK) {
114    // Notify the change of directory.
115    delegate_->OnFileChangedByOperation(*changed_files);
116    delegate_->OnEntryUpdatedByOperation(*local_id);
117  }
118  callback.Run(error);
119}
120
121}  // namespace file_system
122}  // namespace drive
123