• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/file_manager/fileapi_util.h"
6 
7 #include "base/files/file.h"
8 #include "base/files/file_path.h"
9 #include "chrome/browser/chromeos/drive/file_system_util.h"
10 #include "chrome/browser/chromeos/file_manager/app_id.h"
11 #include "chrome/browser/extensions/extension_util.h"
12 #include "chrome/browser/profiles/profile.h"
13 #include "content/public/browser/browser_thread.h"
14 #include "content/public/browser/render_view_host.h"
15 #include "content/public/browser/site_instance.h"
16 #include "content/public/browser/storage_partition.h"
17 #include "extensions/common/extension.h"
18 #include "google_apis/drive/task_util.h"
19 #include "net/base/escape.h"
20 #include "url/gurl.h"
21 #include "webkit/browser/fileapi/file_system_context.h"
22 #include "webkit/browser/fileapi/open_file_system_mode.h"
23 #include "webkit/common/fileapi/file_system_util.h"
24 
25 using content::BrowserThread;
26 
27 namespace file_manager {
28 namespace util {
29 
30 namespace {
31 
ConvertRelativeFilePathToFileSystemUrl(const base::FilePath & relative_path,const std::string & extension_id)32 GURL ConvertRelativeFilePathToFileSystemUrl(const base::FilePath& relative_path,
33                                             const std::string& extension_id) {
34   GURL base_url = fileapi::GetFileSystemRootURI(
35       extensions::Extension::GetBaseURLFromExtensionId(extension_id),
36       fileapi::kFileSystemTypeExternal);
37   return GURL(base_url.spec() +
38               net::EscapeUrlEncodedData(relative_path.AsUTF8Unsafe(),
39                                         false));  // Space to %20 instead of +.
40 }
41 
42 // Creates an ErrorDefinition with an error set to |error|.
CreateEntryDefinitionWithError(base::File::Error error)43 EntryDefinition CreateEntryDefinitionWithError(base::File::Error error) {
44   EntryDefinition result;
45   result.error = error;
46   return result;
47 }
48 
49 // Helper class for performing conversions from file definitions to entry
50 // definitions. It is possible to do it without a class, but the code would be
51 // crazy and super tricky.
52 //
53 // This class copies the input |file_definition_list|,
54 // so there is no need to worry about validity of passed |file_definition_list|
55 // reference. Also, it automatically deletes itself after converting finished,
56 // or if shutdown is invoked during ResolveURL(). Must be called on UI thread.
57 class FileDefinitionListConverter {
58  public:
59   FileDefinitionListConverter(Profile* profile,
60                               const std::string& extension_id,
61                               const FileDefinitionList& file_definition_list,
62                               const EntryDefinitionListCallback& callback);
~FileDefinitionListConverter()63   ~FileDefinitionListConverter() {}
64 
65  private:
66   // Converts the element under the iterator to an entry. First, converts
67   // the virtual path to an URL, and calls OnResolvedURL(). In case of error
68   // calls OnIteratorConverted with an error entry definition.
69   void ConvertNextIterator(scoped_ptr<FileDefinitionListConverter> self_deleter,
70                            FileDefinitionList::const_iterator iterator);
71 
72   // Creates an entry definition from the URL as well as the file definition.
73   // Then, calls OnIteratorConverted with the created entry definition.
74   void OnResolvedURL(scoped_ptr<FileDefinitionListConverter> self_deleter,
75                      FileDefinitionList::const_iterator iterator,
76                      base::File::Error error,
77                      const fileapi::FileSystemInfo& info,
78                      const base::FilePath& file_path,
79                      fileapi::FileSystemContext::ResolvedEntryType type);
80 
81   // Called when the iterator is converted. Adds the |entry_definition| to
82   // |results_| and calls ConvertNextIterator() for the next element.
83   void OnIteratorConverted(scoped_ptr<FileDefinitionListConverter> self_deleter,
84                            FileDefinitionList::const_iterator iterator,
85                            const EntryDefinition& entry_definition);
86 
87   scoped_refptr<fileapi::FileSystemContext> file_system_context_;
88   const std::string extension_id_;
89   const FileDefinitionList file_definition_list_;
90   const EntryDefinitionListCallback callback_;
91   scoped_ptr<EntryDefinitionList> result_;
92 };
93 
FileDefinitionListConverter(Profile * profile,const std::string & extension_id,const FileDefinitionList & file_definition_list,const EntryDefinitionListCallback & callback)94 FileDefinitionListConverter::FileDefinitionListConverter(
95     Profile* profile,
96     const std::string& extension_id,
97     const FileDefinitionList& file_definition_list,
98     const EntryDefinitionListCallback& callback)
99     : extension_id_(extension_id),
100       file_definition_list_(file_definition_list),
101       callback_(callback),
102       result_(new EntryDefinitionList) {
103   DCHECK_CURRENTLY_ON(BrowserThread::UI);
104 
105   // File browser APIs are meant to be used only from extension context, so
106   // the extension's site is the one in whose file system context the virtual
107   // path should be found.
108   GURL site = extensions::util::GetSiteForExtensionId(extension_id_, profile);
109   file_system_context_ =
110       content::BrowserContext::GetStoragePartitionForSite(
111           profile, site)->GetFileSystemContext();
112 
113   // Deletes the converter, once the scoped pointer gets out of scope. It is
114   // either, if the conversion is finished, or ResolveURL() is terminated, and
115   // the callback not called because of shutdown.
116   scoped_ptr<FileDefinitionListConverter> self_deleter(this);
117   ConvertNextIterator(self_deleter.Pass(), file_definition_list_.begin());
118 }
119 
ConvertNextIterator(scoped_ptr<FileDefinitionListConverter> self_deleter,FileDefinitionList::const_iterator iterator)120 void FileDefinitionListConverter::ConvertNextIterator(
121     scoped_ptr<FileDefinitionListConverter> self_deleter,
122     FileDefinitionList::const_iterator iterator) {
123   if (iterator == file_definition_list_.end()) {
124     // The converter object will be destroyed since |self_deleter| gets out of
125     // scope.
126     callback_.Run(result_.Pass());
127     return;
128   }
129 
130   if (!file_system_context_.get()) {
131     OnIteratorConverted(self_deleter.Pass(),
132                         iterator,
133                         CreateEntryDefinitionWithError(
134                             base::File::FILE_ERROR_INVALID_OPERATION));
135     return;
136   }
137 
138   fileapi::FileSystemURL url = file_system_context_->CreateCrackedFileSystemURL(
139       extensions::Extension::GetBaseURLFromExtensionId(extension_id_),
140       fileapi::kFileSystemTypeExternal,
141       iterator->virtual_path);
142   DCHECK(url.is_valid());
143 
144   // The converter object will be deleted if the callback is not called because
145   // of shutdown during ResolveURL().
146   file_system_context_->ResolveURL(
147       url,
148       base::Bind(&FileDefinitionListConverter::OnResolvedURL,
149                  base::Unretained(this),
150                  base::Passed(&self_deleter),
151                  iterator));
152 }
153 
OnResolvedURL(scoped_ptr<FileDefinitionListConverter> self_deleter,FileDefinitionList::const_iterator iterator,base::File::Error error,const fileapi::FileSystemInfo & info,const base::FilePath & file_path,fileapi::FileSystemContext::ResolvedEntryType type)154 void FileDefinitionListConverter::OnResolvedURL(
155     scoped_ptr<FileDefinitionListConverter> self_deleter,
156     FileDefinitionList::const_iterator iterator,
157     base::File::Error error,
158     const fileapi::FileSystemInfo& info,
159     const base::FilePath& file_path,
160     fileapi::FileSystemContext::ResolvedEntryType type) {
161   DCHECK_CURRENTLY_ON(BrowserThread::UI);
162 
163   if (error != base::File::FILE_OK) {
164     OnIteratorConverted(self_deleter.Pass(),
165                         iterator,
166                         CreateEntryDefinitionWithError(error));
167     return;
168   }
169 
170   EntryDefinition entry_definition;
171   entry_definition.file_system_root_url = info.root_url.spec();
172   entry_definition.file_system_name = info.name;
173   switch (type) {
174     case fileapi::FileSystemContext::RESOLVED_ENTRY_FILE:
175       entry_definition.is_directory = false;
176       break;
177     case fileapi::FileSystemContext::RESOLVED_ENTRY_DIRECTORY:
178       entry_definition.is_directory = true;
179       break;
180     case fileapi::FileSystemContext::RESOLVED_ENTRY_NOT_FOUND:
181       entry_definition.is_directory = iterator->is_directory;
182       break;
183   }
184   entry_definition.error = base::File::FILE_OK;
185 
186   // Construct a target Entry.fullPath value from the virtual path and the
187   // root URL. Eg. Downloads/A/b.txt -> A/b.txt.
188   const base::FilePath root_virtual_path =
189       file_system_context_->CrackURL(info.root_url).virtual_path();
190   DCHECK(root_virtual_path == iterator->virtual_path ||
191          root_virtual_path.IsParent(iterator->virtual_path));
192   base::FilePath full_path;
193   root_virtual_path.AppendRelativePath(iterator->virtual_path, &full_path);
194   entry_definition.full_path = full_path;
195 
196   OnIteratorConverted(self_deleter.Pass(), iterator, entry_definition);
197 }
198 
OnIteratorConverted(scoped_ptr<FileDefinitionListConverter> self_deleter,FileDefinitionList::const_iterator iterator,const EntryDefinition & entry_definition)199 void FileDefinitionListConverter::OnIteratorConverted(
200     scoped_ptr<FileDefinitionListConverter> self_deleter,
201     FileDefinitionList::const_iterator iterator,
202     const EntryDefinition& entry_definition) {
203   result_->push_back(entry_definition);
204   ConvertNextIterator(self_deleter.Pass(), ++iterator);
205 }
206 
207 // Helper function to return the converted definition entry directly, without
208 // the redundant container.
OnConvertFileDefinitionDone(const EntryDefinitionCallback & callback,scoped_ptr<EntryDefinitionList> entry_definition_list)209 void OnConvertFileDefinitionDone(
210     const EntryDefinitionCallback& callback,
211     scoped_ptr<EntryDefinitionList> entry_definition_list) {
212   DCHECK_EQ(1u, entry_definition_list->size());
213   callback.Run(entry_definition_list->at(0));
214 }
215 
216 // Used to implement CheckIfDirectoryExists().
CheckIfDirectoryExistsOnIOThread(scoped_refptr<fileapi::FileSystemContext> file_system_context,const GURL & url,const fileapi::FileSystemOperationRunner::StatusCallback & callback)217 void CheckIfDirectoryExistsOnIOThread(
218     scoped_refptr<fileapi::FileSystemContext> file_system_context,
219     const GURL& url,
220     const fileapi::FileSystemOperationRunner::StatusCallback& callback) {
221   DCHECK_CURRENTLY_ON(BrowserThread::IO);
222 
223   fileapi::FileSystemURL file_system_url = file_system_context->CrackURL(url);
224   file_system_context->operation_runner()->DirectoryExists(
225       file_system_url, callback);
226 }
227 
228 }  // namespace
229 
EntryDefinition()230 EntryDefinition::EntryDefinition() {
231 }
232 
~EntryDefinition()233 EntryDefinition::~EntryDefinition() {
234 }
235 
GetFileSystemContextForExtensionId(Profile * profile,const std::string & extension_id)236 fileapi::FileSystemContext* GetFileSystemContextForExtensionId(
237     Profile* profile,
238     const std::string& extension_id) {
239   GURL site = extensions::util::GetSiteForExtensionId(extension_id, profile);
240   return content::BrowserContext::GetStoragePartitionForSite(profile, site)->
241       GetFileSystemContext();
242 }
243 
GetFileSystemContextForRenderViewHost(Profile * profile,content::RenderViewHost * render_view_host)244 fileapi::FileSystemContext* GetFileSystemContextForRenderViewHost(
245     Profile* profile,
246     content::RenderViewHost* render_view_host) {
247   content::SiteInstance* site_instance = render_view_host->GetSiteInstance();
248   return content::BrowserContext::GetStoragePartition(profile, site_instance)->
249       GetFileSystemContext();
250 }
251 
ConvertDrivePathToRelativeFileSystemPath(Profile * profile,const std::string & extension_id,const base::FilePath & drive_path)252 base::FilePath ConvertDrivePathToRelativeFileSystemPath(
253     Profile* profile,
254     const std::string& extension_id,
255     const base::FilePath& drive_path) {
256   // "/special/drive-xxx"
257   base::FilePath path = drive::util::GetDriveMountPointPath(profile);
258   // appended with (|drive_path| - "drive").
259   drive::util::GetDriveGrandRootPath().AppendRelativePath(drive_path, &path);
260 
261   base::FilePath relative_path;
262   ConvertAbsoluteFilePathToRelativeFileSystemPath(profile,
263                                                   extension_id,
264                                                   path,
265                                                   &relative_path);
266   return relative_path;
267 }
268 
ConvertDrivePathToFileSystemUrl(Profile * profile,const base::FilePath & drive_path,const std::string & extension_id)269 GURL ConvertDrivePathToFileSystemUrl(Profile* profile,
270                                      const base::FilePath& drive_path,
271                                      const std::string& extension_id) {
272   const base::FilePath relative_path =
273       ConvertDrivePathToRelativeFileSystemPath(profile, extension_id,
274                                                drive_path);
275   if (relative_path.empty())
276     return GURL();
277   return ConvertRelativeFilePathToFileSystemUrl(relative_path, extension_id);
278 }
279 
ConvertAbsoluteFilePathToFileSystemUrl(Profile * profile,const base::FilePath & absolute_path,const std::string & extension_id,GURL * url)280 bool ConvertAbsoluteFilePathToFileSystemUrl(Profile* profile,
281                                             const base::FilePath& absolute_path,
282                                             const std::string& extension_id,
283                                             GURL* url) {
284   base::FilePath relative_path;
285   if (!ConvertAbsoluteFilePathToRelativeFileSystemPath(profile,
286                                                        extension_id,
287                                                        absolute_path,
288                                                        &relative_path)) {
289     return false;
290   }
291   *url = ConvertRelativeFilePathToFileSystemUrl(relative_path, extension_id);
292   return true;
293 }
294 
ConvertAbsoluteFilePathToRelativeFileSystemPath(Profile * profile,const std::string & extension_id,const base::FilePath & absolute_path,base::FilePath * virtual_path)295 bool ConvertAbsoluteFilePathToRelativeFileSystemPath(
296     Profile* profile,
297     const std::string& extension_id,
298     const base::FilePath& absolute_path,
299     base::FilePath* virtual_path) {
300   // File browser APIs are meant to be used only from extension context, so the
301   // extension's site is the one in whose file system context the virtual path
302   // should be found.
303   GURL site = extensions::util::GetSiteForExtensionId(extension_id, profile);
304   fileapi::ExternalFileSystemBackend* backend =
305       content::BrowserContext::GetStoragePartitionForSite(profile, site)->
306           GetFileSystemContext()->external_backend();
307   if (!backend)
308     return false;
309 
310   // Find if this file path is managed by the external backend.
311   if (!backend->GetVirtualPath(absolute_path, virtual_path))
312     return false;
313 
314   return true;
315 }
316 
ConvertFileDefinitionListToEntryDefinitionList(Profile * profile,const std::string & extension_id,const FileDefinitionList & file_definition_list,const EntryDefinitionListCallback & callback)317 void ConvertFileDefinitionListToEntryDefinitionList(
318     Profile* profile,
319     const std::string& extension_id,
320     const FileDefinitionList& file_definition_list,
321     const EntryDefinitionListCallback& callback) {
322   DCHECK_CURRENTLY_ON(BrowserThread::UI);
323 
324   // The converter object destroys itself.
325   new FileDefinitionListConverter(
326       profile, extension_id, file_definition_list, callback);
327 }
328 
ConvertFileDefinitionToEntryDefinition(Profile * profile,const std::string & extension_id,const FileDefinition & file_definition,const EntryDefinitionCallback & callback)329 void ConvertFileDefinitionToEntryDefinition(
330     Profile* profile,
331     const std::string& extension_id,
332     const FileDefinition& file_definition,
333     const EntryDefinitionCallback& callback) {
334   DCHECK_CURRENTLY_ON(BrowserThread::UI);
335 
336   FileDefinitionList file_definition_list;
337   file_definition_list.push_back(file_definition);
338   ConvertFileDefinitionListToEntryDefinitionList(
339       profile,
340       extension_id,
341       file_definition_list,
342       base::Bind(&OnConvertFileDefinitionDone, callback));
343 }
344 
CheckIfDirectoryExists(scoped_refptr<fileapi::FileSystemContext> file_system_context,const GURL & url,const fileapi::FileSystemOperationRunner::StatusCallback & callback)345 void CheckIfDirectoryExists(
346     scoped_refptr<fileapi::FileSystemContext> file_system_context,
347     const GURL& url,
348     const fileapi::FileSystemOperationRunner::StatusCallback& callback) {
349   DCHECK_CURRENTLY_ON(BrowserThread::UI);
350 
351   // Check the existence of directory using file system API implementation on
352   // behalf of the file manager app. We need to grant access beforehand.
353   fileapi::ExternalFileSystemBackend* backend =
354       file_system_context->external_backend();
355   DCHECK(backend);
356   backend->GrantFullAccessToExtension(kFileManagerAppId);
357 
358   BrowserThread::PostTask(
359       BrowserThread::IO, FROM_HERE,
360       base::Bind(&CheckIfDirectoryExistsOnIOThread,
361                  file_system_context,
362                  url,
363                  google_apis::CreateRelayCallback(callback)));
364 }
365 
366 }  // namespace util
367 }  // namespace file_manager
368