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