1 // Copyright 2020 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 "components/paint_preview/browser/paint_preview_compositor_client_impl.h"
6
7 #include <utility>
8
9 #include "base/callback.h"
10
11 namespace paint_preview {
12
13 namespace {
14
15 // These methods bind a callback to a task runner. This simplifies situations
16 // where a caller provides a callback which should be passed to |compositor_|
17 // verbatim, but should be run on the caller's task runner rather than
18 // |compositor_task_runner_|.
19 //
20 // Based on the implementation in: chromecast/base/bind_to_task_runner.h
21
22 template <typename Sig>
23 struct BindToTaskRunnerTrampoline;
24
25 template <typename... Args>
26 struct BindToTaskRunnerTrampoline<void(Args...)> {
Runpaint_preview::__anone5fcb3470111::BindToTaskRunnerTrampoline27 static void Run(base::TaskRunner* task_runner,
28 base::OnceCallback<void(Args...)> callback,
29 Args... args) {
30 task_runner->PostTask(
31 FROM_HERE,
32 base::BindOnce(std::move(callback), std::forward<Args>(args)...));
33 }
34 };
35
36 template <typename T>
BindToTaskRunner(scoped_refptr<base::TaskRunner> task_runner,base::OnceCallback<T> callback)37 base::OnceCallback<T> BindToTaskRunner(
38 scoped_refptr<base::TaskRunner> task_runner,
39 base::OnceCallback<T> callback) {
40 return base::BindOnce(&BindToTaskRunnerTrampoline<T>::Run,
41 base::RetainedRef(std::move(task_runner)),
42 std::move(callback));
43 }
44
45 } // namespace
46
PaintPreviewCompositorClientImpl(scoped_refptr<base::SequencedTaskRunner> compositor_task_runner,base::WeakPtr<PaintPreviewCompositorServiceImpl> service)47 PaintPreviewCompositorClientImpl::PaintPreviewCompositorClientImpl(
48 scoped_refptr<base::SequencedTaskRunner> compositor_task_runner,
49 base::WeakPtr<PaintPreviewCompositorServiceImpl> service)
50 : compositor_task_runner_(compositor_task_runner),
51 default_task_runner_(base::SequencedTaskRunnerHandle::Get()),
52 service_(service),
53 compositor_(new mojo::Remote<mojom::PaintPreviewCompositor>(),
54 base::OnTaskRunnerDeleter(compositor_task_runner_)) {}
55
~PaintPreviewCompositorClientImpl()56 PaintPreviewCompositorClientImpl::~PaintPreviewCompositorClientImpl() {
57 DCHECK(default_task_runner_->RunsTasksInCurrentSequence());
58 NotifyServiceOfInvalidation();
59 }
60
61 const base::Optional<base::UnguessableToken>&
Token() const62 PaintPreviewCompositorClientImpl::Token() const {
63 DCHECK(default_task_runner_->RunsTasksInCurrentSequence());
64 return token_;
65 }
66
SetDisconnectHandler(base::OnceClosure closure)67 void PaintPreviewCompositorClientImpl::SetDisconnectHandler(
68 base::OnceClosure closure) {
69 DCHECK(default_task_runner_->RunsTasksInCurrentSequence());
70 user_disconnect_closure_ = std::move(closure);
71 }
72
73 // For the following methods the use of base::Unretained for |compositor_| and
74 // things |compositor_| owns is safe as:
75 // 1. |compositor_| is deleted on the |compositor_task_runner_| after other
76 // non-delayed tasks in the current sequence are run.
77 // 2. New tasks cannot be created that reference |compositor_| once it is
78 // deleted as its lifetime is tied to that of the
79 // PaintPreviewCompositorClient.
80 //
81 // NOTE: This is only safe as no delayed tasks are posted and there are no
82 // cases of base::Unretained(this) or other class members passed as pointers.
83
BeginSeparatedFrameComposite(mojom::PaintPreviewBeginCompositeRequestPtr request,mojom::PaintPreviewCompositor::BeginSeparatedFrameCompositeCallback callback)84 void PaintPreviewCompositorClientImpl::BeginSeparatedFrameComposite(
85 mojom::PaintPreviewBeginCompositeRequestPtr request,
86 mojom::PaintPreviewCompositor::BeginSeparatedFrameCompositeCallback
87 callback) {
88 DCHECK(default_task_runner_->RunsTasksInCurrentSequence());
89 compositor_task_runner_->PostTask(
90 FROM_HERE,
91 base::BindOnce(
92 &mojom::PaintPreviewCompositor::BeginSeparatedFrameComposite,
93 base::Unretained(compositor_.get()->get()), std::move(request),
94 BindToTaskRunner(default_task_runner_, std::move(callback))));
95 }
96
BitmapForSeparatedFrame(const base::UnguessableToken & frame_guid,const gfx::Rect & clip_rect,float scale_factor,mojom::PaintPreviewCompositor::BitmapForSeparatedFrameCallback callback)97 void PaintPreviewCompositorClientImpl::BitmapForSeparatedFrame(
98 const base::UnguessableToken& frame_guid,
99 const gfx::Rect& clip_rect,
100 float scale_factor,
101 mojom::PaintPreviewCompositor::BitmapForSeparatedFrameCallback callback) {
102 DCHECK(default_task_runner_->RunsTasksInCurrentSequence());
103
104 auto validate_bitmap = base::BindOnce(
105 [](mojom::PaintPreviewCompositor::BitmapForSeparatedFrameCallback
106 callback,
107 mojom::PaintPreviewCompositor::BitmapStatus status,
108 const SkBitmap& bitmap) {
109 // The paint preview service should be sending us N32 32bpp bitmaps in
110 // reply, otherwise this can lead to out-of-bounds mistakes when
111 // transferring the pixels out of the bitmap into other buffers.
112 CHECK_EQ(bitmap.colorType(), kN32_SkColorType);
113 std::move(callback).Run(status, bitmap);
114 },
115 BindToTaskRunner(default_task_runner_, std::move(callback)));
116
117 compositor_task_runner_->PostTask(
118 FROM_HERE,
119 base::BindOnce(&mojom::PaintPreviewCompositor::BitmapForSeparatedFrame,
120 base::Unretained(compositor_.get()->get()), frame_guid,
121 clip_rect, scale_factor, std::move(validate_bitmap)));
122 }
123
BeginMainFrameComposite(mojom::PaintPreviewBeginCompositeRequestPtr request,mojom::PaintPreviewCompositor::BeginMainFrameCompositeCallback callback)124 void PaintPreviewCompositorClientImpl::BeginMainFrameComposite(
125 mojom::PaintPreviewBeginCompositeRequestPtr request,
126 mojom::PaintPreviewCompositor::BeginMainFrameCompositeCallback callback) {
127 DCHECK(default_task_runner_->RunsTasksInCurrentSequence());
128 compositor_task_runner_->PostTask(
129 FROM_HERE,
130 base::BindOnce(
131 &mojom::PaintPreviewCompositor::BeginMainFrameComposite,
132 base::Unretained(compositor_.get()->get()), std::move(request),
133 BindToTaskRunner(default_task_runner_, std::move(callback))));
134 }
135
BitmapForMainFrame(const gfx::Rect & clip_rect,float scale_factor,mojom::PaintPreviewCompositor::BitmapForMainFrameCallback callback)136 void PaintPreviewCompositorClientImpl::BitmapForMainFrame(
137 const gfx::Rect& clip_rect,
138 float scale_factor,
139 mojom::PaintPreviewCompositor::BitmapForMainFrameCallback callback) {
140 DCHECK(default_task_runner_->RunsTasksInCurrentSequence());
141 compositor_task_runner_->PostTask(
142 FROM_HERE,
143 base::BindOnce(
144 &mojom::PaintPreviewCompositor::BitmapForMainFrame,
145 base::Unretained(compositor_.get()->get()), clip_rect, scale_factor,
146 BindToTaskRunner(default_task_runner_, std::move(callback))));
147 }
148
SetRootFrameUrl(const GURL & url)149 void PaintPreviewCompositorClientImpl::SetRootFrameUrl(const GURL& url) {
150 DCHECK(default_task_runner_->RunsTasksInCurrentSequence());
151 compositor_task_runner_->PostTask(
152 FROM_HERE,
153 base::BindOnce(&mojom::PaintPreviewCompositor::SetRootFrameUrl,
154 base::Unretained(compositor_.get()->get()), url));
155 }
156
IsBoundAndConnected(base::OnceCallback<void (bool)> callback)157 void PaintPreviewCompositorClientImpl::IsBoundAndConnected(
158 base::OnceCallback<void(bool)> callback) {
159 DCHECK(default_task_runner_->RunsTasksInCurrentSequence());
160 compositor_task_runner_->PostTask(
161 FROM_HERE, base::BindOnce(
162 [](mojo::Remote<mojom::PaintPreviewCompositor>* compositor,
163 scoped_refptr<base::SequencedTaskRunner> task_runner,
164 base::OnceCallback<void(bool)> callback) {
165 task_runner->PostTask(
166 FROM_HERE,
167 base::BindOnce(std::move(callback),
168 compositor->is_bound() &&
169 compositor->is_connected()));
170 },
171 base::Unretained(compositor_.get()), default_task_runner_,
172 std::move(callback)));
173 }
174
175 mojo::PendingReceiver<mojom::PaintPreviewCompositor>
BindNewPipeAndPassReceiver()176 PaintPreviewCompositorClientImpl::BindNewPipeAndPassReceiver() {
177 DCHECK(compositor_task_runner_->RunsTasksInCurrentSequence());
178 return compositor_->BindNewPipeAndPassReceiver();
179 }
180
181 PaintPreviewCompositorClientImpl::OnCompositorCreatedCallback
BuildCompositorCreatedCallback(base::OnceClosure user_closure,OnCompositorCreatedCallback service_callback)182 PaintPreviewCompositorClientImpl::BuildCompositorCreatedCallback(
183 base::OnceClosure user_closure,
184 OnCompositorCreatedCallback service_callback) {
185 DCHECK(default_task_runner_->RunsTasksInCurrentSequence());
186 return BindToTaskRunner(
187 default_task_runner_,
188 base::BindOnce(&PaintPreviewCompositorClientImpl::OnCompositorCreated,
189 weak_ptr_factory_.GetWeakPtr(), std::move(user_closure),
190 std::move(service_callback)));
191 }
192
OnCompositorCreated(base::OnceClosure user_closure,OnCompositorCreatedCallback service_callback,const base::UnguessableToken & token)193 void PaintPreviewCompositorClientImpl::OnCompositorCreated(
194 base::OnceClosure user_closure,
195 OnCompositorCreatedCallback service_callback,
196 const base::UnguessableToken& token) {
197 DCHECK(default_task_runner_->RunsTasksInCurrentSequence());
198 token_ = token;
199 std::move(user_closure).Run();
200 std::move(service_callback).Run(token);
201 compositor_task_runner_->PostTask(
202 FROM_HERE,
203 base::BindOnce(
204 &mojo::Remote<mojom::PaintPreviewCompositor>::set_disconnect_handler,
205 base::Unretained(compositor_.get()),
206 BindToTaskRunner(
207 default_task_runner_,
208 base::BindOnce(
209 &PaintPreviewCompositorClientImpl::DisconnectHandler,
210 weak_ptr_factory_.GetWeakPtr()))));
211 }
212
NotifyServiceOfInvalidation()213 void PaintPreviewCompositorClientImpl::NotifyServiceOfInvalidation() {
214 DCHECK(default_task_runner_->RunsTasksInCurrentSequence());
215 if (service_ && token_.has_value())
216 service_->MarkCompositorAsDeleted(token_.value());
217 }
218
DisconnectHandler()219 void PaintPreviewCompositorClientImpl::DisconnectHandler() {
220 DCHECK(default_task_runner_->RunsTasksInCurrentSequence());
221 if (user_disconnect_closure_)
222 std::move(user_disconnect_closure_).Run();
223 NotifyServiceOfInvalidation();
224 compositor_task_runner_->PostTask(
225 FROM_HERE,
226 base::BindOnce(&mojo::Remote<mojom::PaintPreviewCompositor>::reset,
227 base::Unretained(compositor_.get())));
228 }
229
230 } // namespace paint_preview
231