12a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// found in the LICENSE file.
42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/extensions/api/file_handlers/app_file_handler_util.h"
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
723730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)#include "base/files/file.h"
8424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)#include "base/files/file_path.h"
91320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "base/files/file_util.h"
10424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)#include "content/public/browser/browser_thread.h"
112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "content/public/browser/child_process_security_policy.h"
12a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include "content/public/browser/render_process_host.h"
135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "extensions/browser/extension_prefs.h"
14116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#include "extensions/browser/granted_file_entry.h"
1546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)#include "extensions/common/permissions/permissions_data.h"
162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "net/base/mime_util.h"
171320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "storage/browser/fileapi/isolated_context.h"
181320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "storage/common/fileapi/file_system_mount_option.h"
191320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "storage/common/fileapi/file_system_types.h"
202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
21424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)#if defined(OS_CHROMEOS)
22cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "chrome/browser/chromeos/file_manager/filesystem_api_util.h"
23424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)#endif
24424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace extensions {
262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace app_file_handler_util {
282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
29a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)const char kInvalidParameters[] = "Invalid parameters";
30a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)const char kSecurityError[] = "Security error";
31a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
32c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)namespace {
33c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
34c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)bool FileHandlerCanHandleFileWithExtension(
35c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const FileHandlerInfo& handler,
36c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const base::FilePath& path) {
37c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  for (std::set<std::string>::const_iterator extension =
38c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)       handler.extensions.begin();
39c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)       extension != handler.extensions.end(); ++extension) {
40c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (*extension == "*")
41c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      return true;
42c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
43c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (path.MatchesExtension(
44c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        base::FilePath::kExtensionSeparator +
45c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        base::FilePath::FromUTF8Unsafe(*extension).value())) {
46c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      return true;
47c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    }
48c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
49c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // Also accept files with no extension for handlers that support an
50c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // empty extension, i.e. both "foo" and "foo." match.
51c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (extension->empty() &&
52c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        path.MatchesExtension(base::FilePath::StringType())) {
53c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      return true;
54c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    }
55c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
56c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return false;
57c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
58c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
59b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)bool FileHandlerCanHandleFileWithMimeType(
60b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    const FileHandlerInfo& handler,
61b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    const std::string& mime_type) {
62b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  for (std::set<std::string>::const_iterator type = handler.types.begin();
63b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)       type != handler.types.end(); ++type) {
64b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    if (net::MatchesMimeType(*type, mime_type))
65b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      return true;
66b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  }
67b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  return false;
68b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)}
69b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
70424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)bool DoCheckWritableFile(const base::FilePath& path, bool is_directory) {
71424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  // Don't allow links.
72a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (base::PathExists(path) && base::IsLink(path))
73424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    return false;
74424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
75424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  if (is_directory)
76424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    return base::DirectoryExists(path);
77424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
78424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  // Create the file if it doesn't already exist.
7946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  int creation_flags = base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_READ;
8023730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  base::File file(path, creation_flags);
8146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  return file.IsValid();
82424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)}
83424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
84424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)// Checks whether a list of paths are all OK for writing and calls a provided
85424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)// on_success or on_failure callback when done. A file is OK for writing if it
86424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)// is not a symlink, is not in a blacklisted path and can be opened for writing;
87424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)// files are created if they do not exist.
88424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)class WritableFileChecker
89424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    : public base::RefCountedThreadSafe<WritableFileChecker> {
90424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) public:
91424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  WritableFileChecker(
92424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      const std::vector<base::FilePath>& paths,
93424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      Profile* profile,
94424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      bool is_directory,
95424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      const base::Closure& on_success,
96424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      const base::Callback<void(const base::FilePath&)>& on_failure);
97424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
98424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  void Check();
99424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
100424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) private:
101424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  friend class base::RefCountedThreadSafe<WritableFileChecker>;
102424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  virtual ~WritableFileChecker();
103424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
104424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  // Called when a work item is completed. If all work items are done, this
105424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  // calls the success or failure callback.
106424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  void TaskDone();
107424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
108424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  // Reports an error in completing a work item. This may be called more than
109424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  // once, but only the last message will be retained.
110424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  void Error(const base::FilePath& error_path);
111424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
112424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  void CheckLocalWritableFiles();
113424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
114424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)#if defined(OS_CHROMEOS)
115cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  void NonNativeLocalPathCheckDone(const base::FilePath& path, bool success);
116424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)#endif
117424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
118424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  const std::vector<base::FilePath> paths_;
119424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  Profile* profile_;
120424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  const bool is_directory_;
121424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  int outstanding_tasks_;
122424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  base::FilePath error_path_;
123424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  base::Closure on_success_;
124424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  base::Callback<void(const base::FilePath&)> on_failure_;
125424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)};
126424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
127424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)WritableFileChecker::WritableFileChecker(
128424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    const std::vector<base::FilePath>& paths,
129424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    Profile* profile,
130424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    bool is_directory,
131424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    const base::Closure& on_success,
132424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    const base::Callback<void(const base::FilePath&)>& on_failure)
133424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    : paths_(paths),
134424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      profile_(profile),
135424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      is_directory_(is_directory),
136424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      outstanding_tasks_(1),
137424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      on_success_(on_success),
138424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      on_failure_(on_failure) {}
139424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
140424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)void WritableFileChecker::Check() {
141424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)#if defined(OS_CHROMEOS)
142cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (file_manager::util::IsUnderNonNativeLocalPath(profile_, paths_[0])) {
143424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    outstanding_tasks_ = paths_.size();
144424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    for (std::vector<base::FilePath>::const_iterator it = paths_.begin();
145424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)         it != paths_.end();
146424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)         ++it) {
1475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      if (is_directory_) {
148cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        file_manager::util::IsNonNativeLocalPathDirectory(
1495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            profile_,
1505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            *it,
151cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)            base::Bind(&WritableFileChecker::NonNativeLocalPathCheckDone,
152cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                       this, *it));
1535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      } else {
15446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)        file_manager::util::PrepareNonNativeLocalFileForWritableApp(
1555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            profile_,
1565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            *it,
157cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)            base::Bind(&WritableFileChecker::NonNativeLocalPathCheckDone,
158cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                       this, *it));
1595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      }
160424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    }
161424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    return;
162424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  }
163424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)#endif
164424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  content::BrowserThread::PostTask(
165424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      content::BrowserThread::FILE,
166424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      FROM_HERE,
167424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      base::Bind(&WritableFileChecker::CheckLocalWritableFiles, this));
168424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)}
169424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
170424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)WritableFileChecker::~WritableFileChecker() {}
171424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
172424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)void WritableFileChecker::TaskDone() {
173effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
174424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  if (--outstanding_tasks_ == 0) {
175424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    if (error_path_.empty())
176424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      on_success_.Run();
177424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    else
178424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      on_failure_.Run(error_path_);
179424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  }
180424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)}
181424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
182424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)// Reports an error in completing a work item. This may be called more than
183424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)// once, but only the last message will be retained.
184424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)void WritableFileChecker::Error(const base::FilePath& error_path) {
185424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  DCHECK(!error_path.empty());
186424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  error_path_ = error_path;
187424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  TaskDone();
188424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)}
189424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
190424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)void WritableFileChecker::CheckLocalWritableFiles() {
191effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
192424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  std::string error;
193424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  for (std::vector<base::FilePath>::const_iterator it = paths_.begin();
194424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)       it != paths_.end();
195424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)       ++it) {
196424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    if (!DoCheckWritableFile(*it, is_directory_)) {
197424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      content::BrowserThread::PostTask(
198424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)          content::BrowserThread::UI,
199424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)          FROM_HERE,
200424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)          base::Bind(&WritableFileChecker::Error, this, *it));
201424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      return;
202424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    }
203424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  }
204424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  content::BrowserThread::PostTask(
205424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      content::BrowserThread::UI,
206424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      FROM_HERE,
207424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      base::Bind(&WritableFileChecker::TaskDone, this));
208424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)}
209424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
210424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)#if defined(OS_CHROMEOS)
211cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void WritableFileChecker::NonNativeLocalPathCheckDone(
212cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    const base::FilePath& path,
213cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    bool success) {
214cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (success)
215cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    TaskDone();
216cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  else
217cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    Error(path);
218424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)}
219424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)#endif
220424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
221c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}  // namespace
222c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
2232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const FileHandlerInfo* FileHandlerForId(const Extension& app,
2242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                        const std::string& handler_id) {
225cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  const FileHandlersInfo* file_handlers = FileHandlers::GetFileHandlers(&app);
2262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!file_handlers)
2272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return NULL;
2282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
229cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  for (FileHandlersInfo::const_iterator i = file_handlers->begin();
2302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       i != file_handlers->end(); i++) {
2312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (i->id == handler_id)
2322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return &*i;
2332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return NULL;
2352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
237c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)const FileHandlerInfo* FirstFileHandlerForFile(
238c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const Extension& app,
239c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const std::string& mime_type,
240c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const base::FilePath& path) {
241cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  const FileHandlersInfo* file_handlers = FileHandlers::GetFileHandlers(&app);
2422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!file_handlers)
2432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return NULL;
2442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
245cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  for (FileHandlersInfo::const_iterator i = file_handlers->begin();
2462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       i != file_handlers->end(); i++) {
247c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (FileHandlerCanHandleFile(*i, mime_type, path))
248c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      return &*i;
2492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return NULL;
2512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
253b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)std::vector<const FileHandlerInfo*> FindFileHandlersForFiles(
254b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    const Extension& app, const PathAndMimeTypeSet& files) {
2552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::vector<const FileHandlerInfo*> handlers;
256b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  if (files.empty())
2572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return handlers;
2582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Look for file handlers which can handle all the MIME types specified.
260cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  const FileHandlersInfo* file_handlers = FileHandlers::GetFileHandlers(&app);
2612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!file_handlers)
2622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return handlers;
2632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
264cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  for (FileHandlersInfo::const_iterator data = file_handlers->begin();
2652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       data != file_handlers->end(); ++data) {
2662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    bool handles_all_types = true;
267b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    for (PathAndMimeTypeSet::const_iterator it = files.begin();
268b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)         it != files.end(); ++it) {
269b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      if (!FileHandlerCanHandleFile(*data, it->second, it->first)) {
2702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        handles_all_types = false;
2712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        break;
2722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
2732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
2742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (handles_all_types)
2752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      handlers.push_back(&*data);
2762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return handlers;
2782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
280c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)bool FileHandlerCanHandleFile(
281c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const FileHandlerInfo& handler,
282c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const std::string& mime_type,
283c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const base::FilePath& path) {
284c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return FileHandlerCanHandleFileWithMimeType(handler, mime_type) ||
285c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      FileHandlerCanHandleFileWithExtension(handler, path);
286c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
287c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
2882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)GrantedFileEntry CreateFileEntry(
2892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    Profile* profile,
290424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    const Extension* extension,
2912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    int renderer_id,
2922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const base::FilePath& path,
293424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    bool is_directory) {
2942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  GrantedFileEntry result;
29503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  storage::IsolatedContext* isolated_context =
29603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      storage::IsolatedContext::GetInstance();
2972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(isolated_context);
2982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  result.filesystem_id = isolated_context->RegisterFileSystemForPath(
30003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      storage::kFileSystemTypeNativeForPlatformApp,
30103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      std::string(),
30203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      path,
3032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      &result.registered_name);
3042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  content::ChildProcessSecurityPolicy* policy =
3062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      content::ChildProcessSecurityPolicy::GetInstance();
3072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  policy->GrantReadFileSystem(renderer_id, result.filesystem_id);
308424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  if (HasFileSystemWritePermission(extension)) {
3095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (is_directory) {
3105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      policy->GrantCreateReadWriteFileSystem(renderer_id, result.filesystem_id);
3115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    } else {
3125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      policy->GrantWriteFileSystem(renderer_id, result.filesystem_id);
3135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      policy->GrantDeleteFromFileSystem(renderer_id, result.filesystem_id);
3145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
315424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  }
3162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  result.id = result.filesystem_id + ":" + result.registered_name;
3182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return result;
3192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
32146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)void PrepareFilesForWritableApp(
322424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    const std::vector<base::FilePath>& paths,
323424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    Profile* profile,
324424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    bool is_directory,
325424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    const base::Closure& on_success,
326424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    const base::Callback<void(const base::FilePath&)>& on_failure) {
327424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  scoped_refptr<WritableFileChecker> checker(new WritableFileChecker(
328424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      paths, profile, is_directory, on_success, on_failure));
329424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  checker->Check();
330424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)}
331424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
332424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)bool HasFileSystemWritePermission(const Extension* extension) {
33346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  return extension->permissions_data()->HasAPIPermission(
33446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)      APIPermission::kFileSystemWrite);
335424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)}
336424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
337a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)bool ValidateFileEntryAndGetPath(
338a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    const std::string& filesystem_name,
339a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    const std::string& filesystem_path,
340a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    const content::RenderViewHost* render_view_host,
341a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    base::FilePath* file_path,
342a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    std::string* error) {
3435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (filesystem_path.empty()) {
3445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    *error = kInvalidParameters;
3455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return false;
3465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
3475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
348a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  std::string filesystem_id;
34903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  if (!storage::CrackIsolatedFileSystemName(filesystem_name, &filesystem_id)) {
350a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    *error = kInvalidParameters;
351a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    return false;
352a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
353a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
354a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // Only return the display path if the process has read access to the
355a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // filesystem.
356a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  content::ChildProcessSecurityPolicy* policy =
357a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      content::ChildProcessSecurityPolicy::GetInstance();
358a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (!policy->CanReadFileSystem(render_view_host->GetProcess()->GetID(),
359a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                 filesystem_id)) {
360a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    *error = kSecurityError;
361a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    return false;
362a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
363a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
36403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  storage::IsolatedContext* context = storage::IsolatedContext::GetInstance();
365a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  base::FilePath relative_path =
366a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      base::FilePath::FromUTF8Unsafe(filesystem_path);
367a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  base::FilePath virtual_path = context->CreateVirtualRootPath(filesystem_id)
368a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      .Append(relative_path);
36903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  storage::FileSystemType type;
37003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  storage::FileSystemMountOption mount_option;
3716d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  std::string cracked_id;
372a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (!context->CrackVirtualPath(
3736d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)          virtual_path, &filesystem_id, &type, &cracked_id, file_path,
3746d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)          &mount_option)) {
375a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    *error = kInvalidParameters;
376a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    return false;
377a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
378a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
379a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // The file system API is only intended to operate on file entries that
380a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // correspond to a native file, selected by the user so only allow file
381a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // systems returned by the file system API or from a drag and drop operation.
38203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  if (type != storage::kFileSystemTypeNativeForPlatformApp &&
38303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      type != storage::kFileSystemTypeDragged) {
384a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    *error = kInvalidParameters;
385a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    return false;
386a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
387a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
388a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  return true;
389a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}
390a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
3912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}  // namespace app_file_handler_util
3922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}  // namespace extensions
394