1 // Copyright 2019 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 #ifndef CONTENT_BROWSER_NATIVE_FILE_SYSTEM_NATIVE_FILE_SYSTEM_HANDLE_BASE_H_
6 #define CONTENT_BROWSER_NATIVE_FILE_SYSTEM_NATIVE_FILE_SYSTEM_HANDLE_BASE_H_
7 
8 #include <vector>
9 
10 #include "base/memory/weak_ptr.h"
11 #include "base/sequence_checker.h"
12 #include "base/threading/sequence_bound.h"
13 #include "content/browser/native_file_system/native_file_system_manager_impl.h"
14 #include "content/browser/web_contents/web_contents_impl.h"
15 #include "content/common/content_export.h"
16 #include "content/public/browser/web_contents_observer.h"
17 #include "storage/browser/file_system/file_system_operation_runner.h"
18 #include "storage/browser/file_system/file_system_url.h"
19 #include "storage/browser/file_system/isolated_context.h"
20 #include "third_party/blink/public/mojom/permissions/permission_status.mojom.h"
21 
22 namespace storage {
23 class FileSystemContext;
24 class FileSystemOperationRunner;
25 }  // namespace storage
26 
27 namespace content {
28 
29 // Base class for File and Directory handle implementations. Holds data that is
30 // common to both and (will) deal with functionality that is common as well,
31 // such as permission requests. Instances of this class should be owned by the
32 // NativeFileSystemManagerImpl instance passed in to the constructor.
33 //
34 // This class is not thread safe, all methods must be called from the same
35 // sequence. That sequence also has to be the same sequence on which the
36 // NativeFileSystemPermissionContext expects to be interacted with, which
37 // is the UI thread.
38 class CONTENT_EXPORT NativeFileSystemHandleBase
39     : public NativeFileSystemPermissionGrant::Observer,
40       public WebContentsObserver {
41  public:
42   using BindingContext = NativeFileSystemManagerImpl::BindingContext;
43   using SharedHandleState = NativeFileSystemManagerImpl::SharedHandleState;
44   using PermissionStatus = blink::mojom::PermissionStatus;
45 
46   NativeFileSystemHandleBase(NativeFileSystemManagerImpl* manager,
47                              const BindingContext& context,
48                              const storage::FileSystemURL& url,
49                              const SharedHandleState& handle_state,
50                              bool is_directory);
51   ~NativeFileSystemHandleBase() override;
52 
url()53   const storage::FileSystemURL& url() const { return url_; }
handle_state()54   const SharedHandleState& handle_state() const { return handle_state_; }
file_system()55   const storage::IsolatedContext::ScopedFSHandle& file_system() const {
56     return handle_state_.file_system;
57   }
58 
59   PermissionStatus GetReadPermissionStatus();
60   PermissionStatus GetWritePermissionStatus();
61 
62   // Implementation for the GetPermissionStatus method in the
63   // blink::mojom::NativeFileSystemFileHandle and DirectoryHandle interfaces.
64   void DoGetPermissionStatus(
65       bool writable,
66       base::OnceCallback<void(PermissionStatus)> callback);
67   // Implementation for the RequestPermission method in the
68   // blink::mojom::NativeFileSystemFileHandle and DirectoryHandle interfaces.
69   void DoRequestPermission(
70       bool writable,
71       base::OnceCallback<void(blink::mojom::NativeFileSystemErrorPtr,
72                               PermissionStatus)> callback);
73 
74   // Invokes |callback|, possibly after first requesting write permission. If
75   // permission isn't granted, |permission_denied| is invoked instead. The
76   // callbacks can be invoked synchronously.
77   template <typename CallbackArgType>
78   void RunWithWritePermission(
79       base::OnceCallback<void(CallbackArgType)> callback,
80       base::OnceCallback<void(CallbackArgType)> no_permission_callback,
81       CallbackArgType callback_arg);
82 
83  protected:
manager()84   NativeFileSystemManagerImpl* manager() { return manager_; }
context()85   const BindingContext& context() { return context_; }
file_system_context()86   storage::FileSystemContext* file_system_context() {
87     return manager()->context();
88   }
89 
web_contents()90   WebContentsImpl* web_contents() const {
91     return static_cast<WebContentsImpl*>(WebContentsObserver::web_contents());
92   }
93 
94   virtual base::WeakPtr<NativeFileSystemHandleBase> AsWeakPtr() = 0;
95 
96   // NativeFileSystemPermissionGrant::Observer:
97   void OnPermissionStatusChanged() override;
98 
99   // Invokes |method| on the correct sequence on this handle's
100   // FileSystemOperationRunner, passing |args| and a callback to the method. The
101   // passed in |callback| is wrapped to make sure it is called on the correct
102   // sequence before passing it off to the |method|.
103   //
104   // Note that |callback| is passed to this method before other arguments, while
105   // the wrapped callback will be passed as last argument to the underlying
106   // FileSystemOperation |method|.
107   //
108   // TODO(mek): Once Promises are a thing, this can be done a lot cleaner, and
109   // mostly just be integrated in base::SequenceBound, eliminating the need for
110   // these helper methods.
111   template <typename... MethodArgs,
112             typename... ArgsMinusCallback,
113             typename... CallbackArgs>
DoFileSystemOperation(const base::Location & from_here,storage::FileSystemOperationRunner::OperationID (storage::FileSystemOperationRunner::* method)(MethodArgs...),base::OnceCallback<void (CallbackArgs...)> callback,ArgsMinusCallback &&...args)114   void DoFileSystemOperation(
115       const base::Location& from_here,
116       storage::FileSystemOperationRunner::OperationID (
117           storage::FileSystemOperationRunner::*method)(MethodArgs...),
118       base::OnceCallback<void(CallbackArgs...)> callback,
119       ArgsMinusCallback&&... args) {
120     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
121     // Wrap the passed in callback in one that posts a task back to the current
122     // sequence.
123     auto wrapped_callback = base::BindOnce(
124         [](scoped_refptr<base::SequencedTaskRunner> runner,
125            base::OnceCallback<void(CallbackArgs...)> callback,
126            CallbackArgs... args) {
127           runner->PostTask(FROM_HERE,
128                            base::BindOnce(std::move(callback),
129                                           std::forward<CallbackArgs>(args)...));
130         },
131         base::SequencedTaskRunnerHandle::Get(), std::move(callback));
132 
133     // And then post a task to the sequence bound operation runner to run the
134     // provided method with the provided arguments (and the wrapped callback).
135     manager()->operation_runner().PostTaskWithThisObject(
136         from_here,
137         base::BindOnce(
138             [](scoped_refptr<storage::FileSystemContext>,
139                storage::FileSystemOperationRunner::OperationID (
140                    storage::FileSystemOperationRunner::*method)(MethodArgs...),
141                MethodArgs... args, storage::FileSystemOperationRunner* runner) {
142               (runner->*method)(std::forward<MethodArgs>(args)...);
143             },
144             base::WrapRefCounted(file_system_context()), method,
145             std::forward<ArgsMinusCallback>(args)...,
146             std::move(wrapped_callback)));
147   }
148   // Same as the previous overload, but using RepeatingCallback and
149   // BindRepeating instead.
150   template <typename... MethodArgs,
151             typename... ArgsMinusCallback,
152             typename... CallbackArgs>
DoFileSystemOperation(const base::Location & from_here,storage::FileSystemOperationRunner::OperationID (storage::FileSystemOperationRunner::* method)(MethodArgs...),base::RepeatingCallback<void (CallbackArgs...)> callback,ArgsMinusCallback &&...args)153   void DoFileSystemOperation(
154       const base::Location& from_here,
155       storage::FileSystemOperationRunner::OperationID (
156           storage::FileSystemOperationRunner::*method)(MethodArgs...),
157       base::RepeatingCallback<void(CallbackArgs...)> callback,
158       ArgsMinusCallback&&... args) {
159     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
160     // Wrap the passed in callback in one that posts a task back to the current
161     // sequence.
162     auto wrapped_callback = base::BindRepeating(
163         [](scoped_refptr<base::SequencedTaskRunner> runner,
164            const base::RepeatingCallback<void(CallbackArgs...)>& callback,
165            CallbackArgs... args) {
166           runner->PostTask(
167               FROM_HERE,
168               base::BindOnce(callback, std::forward<CallbackArgs>(args)...));
169         },
170         base::SequencedTaskRunnerHandle::Get(), std::move(callback));
171 
172     // And then post a task to the sequence bound operation runner to run the
173     // provided method with the provided arguments (and the wrapped callback).
174     manager()->operation_runner().PostTaskWithThisObject(
175         from_here,
176         base::BindOnce(
177             [](scoped_refptr<storage::FileSystemContext>,
178                storage::FileSystemOperationRunner::OperationID (
179                    storage::FileSystemOperationRunner::*method)(MethodArgs...),
180                MethodArgs... args, storage::FileSystemOperationRunner* runner) {
181               (runner->*method)(std::forward<MethodArgs>(args)...);
182             },
183             base::WrapRefCounted(file_system_context()), method,
184             std::forward<ArgsMinusCallback>(args)...,
185             std::move(wrapped_callback)));
186   }
187 
188   SEQUENCE_CHECKER(sequence_checker_);
189 
190  private:
191   void DidRequestPermission(
192       bool writable,
193       base::OnceCallback<void(blink::mojom::NativeFileSystemErrorPtr,
194                               PermissionStatus)> callback,
195       NativeFileSystemPermissionGrant::PermissionRequestOutcome outcome);
196 
ShouldTrackUsage()197   bool ShouldTrackUsage() const {
198     return url_.type() == storage::kFileSystemTypeNativeLocal;
199   }
200 
201   // The NativeFileSystemManagerImpl that owns this instance.
202   NativeFileSystemManagerImpl* const manager_;
203   const BindingContext context_;
204   const storage::FileSystemURL url_;
205   const SharedHandleState handle_state_;
206 
207   base::FilePath directory_for_usage_tracking_;
208   bool was_readable_at_last_check_ = false;
209   bool was_writable_at_last_check_ = false;
210 
211   void UpdateUsage();
212 
213   DISALLOW_COPY_AND_ASSIGN(NativeFileSystemHandleBase);
214 };
215 
216 template <typename CallbackArgType>
RunWithWritePermission(base::OnceCallback<void (CallbackArgType)> callback,base::OnceCallback<void (CallbackArgType)> no_permission_callback,CallbackArgType callback_arg)217 void NativeFileSystemHandleBase::RunWithWritePermission(
218     base::OnceCallback<void(CallbackArgType)> callback,
219     base::OnceCallback<void(CallbackArgType)> no_permission_callback,
220     CallbackArgType callback_arg) {
221   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
222   DoRequestPermission(
223       /*writable=*/true,
224       base::BindOnce(
225           [](base::OnceCallback<void(CallbackArgType)> callback,
226              base::OnceCallback<void(CallbackArgType)> no_permission_callback,
227              CallbackArgType callback_arg,
228              blink::mojom::NativeFileSystemErrorPtr result,
229              blink::mojom::PermissionStatus status) {
230             if (status == blink::mojom::PermissionStatus::GRANTED) {
231               std::move(callback).Run(std::move(callback_arg));
232               return;
233             }
234             std::move(no_permission_callback).Run(std::move(callback_arg));
235           },
236           std::move(callback), std::move(no_permission_callback),
237           std::move(callback_arg)));
238 }
239 
240 }  // namespace content
241 
242 #endif  // CONTENT_BROWSER_NATIVE_FILE_SYSTEM_NATIVE_FILE_SYSTEM_HANDLE_BASE_H_
243