fixup! [M120 Migration][NaCl][PPFWK] Upgradable pepper plugin requirement
[platform/framework/web/chromium-efl.git] / pdf / paint_manager.cc
1 // Copyright 2010 The Chromium Authors
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 "pdf/paint_manager.h"
6
7 #include <stddef.h>
8 #include <stdint.h>
9
10 #include <algorithm>
11 #include <cmath>
12 #include <utility>
13
14 #include "base/auto_reset.h"
15 #include "base/check.h"
16 #include "base/functional/bind.h"
17 #include "base/functional/callback.h"
18 #include "base/location.h"
19 #include "base/notreached.h"
20 #include "base/task/sequenced_task_runner.h"
21 #include "base/task/single_thread_task_runner.h"
22 #include "pdf/paint_ready_rect.h"
23 #include "third_party/skia/include/core/SkCanvas.h"
24 #include "third_party/skia/include/core/SkImage.h"
25 #include "third_party/skia/include/core/SkRect.h"
26 #include "third_party/skia/include/core/SkRefCnt.h"
27 #include "third_party/skia/include/core/SkSamplingOptions.h"
28 #include "third_party/skia/include/core/SkSurface.h"
29 #include "ui/gfx/blit.h"
30 #include "ui/gfx/geometry/point.h"
31 #include "ui/gfx/geometry/rect.h"
32 #include "ui/gfx/geometry/size.h"
33 #include "ui/gfx/geometry/skia_conversions.h"
34 #include "ui/gfx/geometry/vector2d.h"
35 #include "ui/gfx/geometry/vector2d_f.h"
36
37 namespace chrome_pdf {
38
39 PaintManager::PaintManager(Client* client) : client_(client) {
40   DCHECK(client_);
41 }
42
43 PaintManager::~PaintManager() = default;
44
45 // static
46 gfx::Size PaintManager::GetNewContextSize(const gfx::Size& current_context_size,
47                                           const gfx::Size& plugin_size) {
48   // The amount of additional space in pixels to allocate to the right/bottom of
49   // the context.
50   constexpr int kBufferSize = 50;
51
52   // Default to returning the same size.
53   gfx::Size result = current_context_size;
54
55   // The minimum size of the plugin before resizing the context to ensure we
56   // aren't wasting too much memory. We deduct twice the kBufferSize from the
57   // current context size which gives a threshhold that is kBufferSize below
58   // the plugin size when the context size was last computed.
59   gfx::Size min_size(
60       std::max(current_context_size.width() - 2 * kBufferSize, 0),
61       std::max(current_context_size.height() - 2 * kBufferSize, 0));
62
63   // If the plugin size is bigger than the current context size, we need to
64   // resize the context. If the plugin size is smaller than the current
65   // context size by a given threshhold then resize the context so that we
66   // aren't wasting too much memory.
67   if (plugin_size.width() > current_context_size.width() ||
68       plugin_size.height() > current_context_size.height() ||
69       plugin_size.width() < min_size.width() ||
70       plugin_size.height() < min_size.height()) {
71     // Create a larger context than needed so that if we only resize by a
72     // small margin, we don't need a new context.
73     result = gfx::Size(plugin_size.width() + kBufferSize,
74                        plugin_size.height() + kBufferSize);
75   }
76
77   return result;
78 }
79
80 void PaintManager::SetSize(const gfx::Size& new_size, float device_scale) {
81   if (GetEffectiveSize() == new_size &&
82       GetEffectiveDeviceScale() == device_scale) {
83     return;
84   }
85
86   has_pending_resize_ = true;
87   pending_size_ = new_size;
88   pending_device_scale_ = device_scale;
89
90   view_size_changed_waiting_for_paint_ = true;
91
92   Invalidate();
93 }
94
95 void PaintManager::SetTransform(float scale,
96                                 const gfx::Point& origin,
97                                 const gfx::Vector2d& translate,
98                                 bool schedule_flush) {
99   if (!surface_)
100     return;
101
102   if (scale <= 0.0f) {
103     NOTREACHED();
104   } else {
105     // translate_with_origin = origin - scale * origin - translate
106     gfx::Vector2dF translate_with_origin = origin.OffsetFromOrigin();
107     translate_with_origin.Scale(1.0f - scale);
108     translate_with_origin.Subtract(translate);
109
110     // TODO(crbug.com/1263614): Should update be deferred until `Flush()`?
111     client_->UpdateLayerTransform(scale, translate_with_origin);
112   }
113
114   if (!schedule_flush)
115     return;
116
117   if (flush_pending_) {
118     flush_requested_ = true;
119     return;
120   }
121   Flush();
122 }
123
124 void PaintManager::ClearTransform() {
125   SetTransform(1.f, gfx::Point(), gfx::Vector2d(), false);
126 }
127
128 void PaintManager::Invalidate() {
129   if (!surface_ && !has_pending_resize_)
130     return;
131
132   EnsureCallbackPending();
133   aggregator_.InvalidateRect(gfx::Rect(GetEffectiveSize()));
134 }
135
136 void PaintManager::InvalidateRect(const gfx::Rect& rect) {
137   DCHECK(!in_paint_);
138
139   if (!surface_ && !has_pending_resize_)
140     return;
141
142   // Clip the rect to the device area.
143   gfx::Rect clipped_rect =
144       gfx::IntersectRects(rect, gfx::Rect(GetEffectiveSize()));
145   if (clipped_rect.IsEmpty())
146     return;  // Nothing to do.
147
148   EnsureCallbackPending();
149   aggregator_.InvalidateRect(clipped_rect);
150 }
151
152 void PaintManager::ScrollRect(const gfx::Rect& clip_rect,
153                               const gfx::Vector2d& amount) {
154   DCHECK(!in_paint_);
155
156   if (!surface_ && !has_pending_resize_)
157     return;
158
159   EnsureCallbackPending();
160
161   aggregator_.ScrollRect(clip_rect, amount);
162 }
163
164 gfx::Size PaintManager::GetEffectiveSize() const {
165   return has_pending_resize_ ? pending_size_ : plugin_size_;
166 }
167
168 float PaintManager::GetEffectiveDeviceScale() const {
169   return has_pending_resize_ ? pending_device_scale_ : device_scale_;
170 }
171
172 void PaintManager::EnsureCallbackPending() {
173   // The best way for us to do the next update is to get a notification that
174   // a previous one has completed. So if we're already waiting for one, we
175   // don't have to do anything differently now.
176   if (flush_pending_)
177     return;
178
179   // If no flush is pending, we need to do a manual call to get back to the
180   // main thread. We may have one already pending, or we may need to schedule.
181   if (manual_callback_pending_)
182     return;
183
184   base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
185       FROM_HERE, base::BindOnce(&PaintManager::OnManualCallbackComplete,
186                                 weak_factory_.GetWeakPtr()));
187   manual_callback_pending_ = true;
188 }
189
190 void PaintManager::DoPaint() {
191   base::AutoReset<bool> auto_reset_in_paint(&in_paint_, true);
192
193   std::vector<PaintReadyRect> ready_rects;
194   std::vector<gfx::Rect> pending_rects;
195
196   DCHECK(aggregator_.HasPendingUpdate());
197
198   // Apply any pending resize. Setting the graphics to this class must happen
199   // before asking the plugin to paint in case it requests invalides or resizes.
200   // However, the bind must not happen until afterward since we don't want to
201   // have an unpainted device bound. The needs_binding flag tells us whether to
202   // do this later.
203   //
204   // Note that `has_pending_resize_` will always be set on the first DoPaint().
205   DCHECK(surface_ || has_pending_resize_);
206   if (has_pending_resize_) {
207     plugin_size_ = pending_size_;
208     // Only create a new graphics context if the current context isn't big
209     // enough or if it is far too big. This avoids creating a new context if
210     // we only resize by a small amount.
211     gfx::Size old_size = surface_
212                              ? gfx::Size(surface_->width(), surface_->height())
213                              : gfx::Size();
214     gfx::Size new_size = GetNewContextSize(old_size, pending_size_);
215     if (old_size != new_size || !surface_) {
216       surface_ = SkSurfaces::Raster(
217           SkImageInfo::MakeN32Premul(new_size.width(), new_size.height()));
218       DCHECK(surface_);
219
220       // TODO(crbug.com/1317832): Can we guarantee repainting some other way?
221       client_->InvalidatePluginContainer();
222
223       device_scale_ = 1.0f;
224
225       // Since we're binding a new one, all of the callbacks have been canceled.
226       manual_callback_pending_ = false;
227       flush_pending_ = false;
228       weak_factory_.InvalidateWeakPtrs();
229     }
230
231     if (pending_device_scale_ != device_scale_)
232       client_->UpdateScale(1.0f / pending_device_scale_);
233     device_scale_ = pending_device_scale_;
234
235     // This must be cleared before calling into the plugin since it may do
236     // additional invalidation or sizing operations.
237     has_pending_resize_ = false;
238     pending_size_ = gfx::Size();
239   }
240
241   PaintAggregator::PaintUpdate update = aggregator_.GetPendingUpdate();
242   client_->OnPaint(update.paint_rects, ready_rects, pending_rects);
243
244   if (ready_rects.empty() && pending_rects.empty())
245     return;  // Nothing was painted, don't schedule a flush.
246
247   std::vector<PaintReadyRect> ready_now;
248   if (pending_rects.empty()) {
249     aggregator_.SetIntermediateResults(ready_rects, pending_rects);
250     ready_now = aggregator_.GetReadyRects();
251     aggregator_.ClearPendingUpdate();
252
253     // First, apply any scroll amount less than the surface's size.
254     if (update.has_scroll &&
255         std::abs(update.scroll_delta.x()) < surface_->width() &&
256         std::abs(update.scroll_delta.y()) < surface_->height()) {
257       // TODO(crbug.com/1263614): Use `SkSurface::notifyContentWillChange()`.
258       gfx::ScrollCanvas(surface_->getCanvas(), update.scroll_rect,
259                         update.scroll_delta);
260     }
261
262     view_size_changed_waiting_for_paint_ = false;
263   } else {
264     std::vector<PaintReadyRect> ready_later;
265     for (const auto& ready_rect : ready_rects) {
266       // Don't flush any part (i.e. scrollbars) if we're resizing the browser,
267       // as that'll lead to flashes.  Until we flush, the browser will use the
268       // previous image, but if we flush, it'll revert to using the blank image.
269       // We make an exception for the first paint since we want to show the
270       // default background color instead of the pepper default of black.
271       if (ready_rect.flush_now() &&
272           (!view_size_changed_waiting_for_paint_ || first_paint_)) {
273         ready_now.push_back(ready_rect);
274       } else {
275         ready_later.push_back(ready_rect);
276       }
277     }
278     // Take the rectangles, except the ones that need to be flushed right away,
279     // and save them so that everything is flushed at once.
280     aggregator_.SetIntermediateResults(ready_later, pending_rects);
281
282     if (ready_now.empty()) {
283       EnsureCallbackPending();
284       return;
285     }
286   }
287
288   for (const auto& ready_rect : ready_now) {
289     SkRect skia_rect = gfx::RectToSkRect(ready_rect.rect());
290     surface_->getCanvas()->drawImageRect(
291         &ready_rect.image(), skia_rect, skia_rect, SkSamplingOptions(), nullptr,
292         SkCanvas::kStrict_SrcRectConstraint);
293   }
294
295   Flush();
296
297   first_paint_ = false;
298 }
299
300 void PaintManager::Flush() {
301   flush_requested_ = false;
302
303   sk_sp<SkImage> snapshot = surface_->makeImageSnapshot();
304   surface_->getCanvas()->drawImage(snapshot.get(), /*x=*/0, /*y=*/0,
305                                    SkSamplingOptions(), /*paint=*/nullptr);
306   client_->UpdateSnapshot(std::move(snapshot));
307
308   // TODO(crbug.com/1403311): Complete flush synchronously.
309   base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
310       FROM_HERE, base::BindOnce(&PaintManager::OnFlushComplete,
311                                 weak_factory_.GetWeakPtr()));
312   flush_pending_ = true;
313 }
314
315 void PaintManager::OnFlushComplete() {
316   DCHECK(flush_pending_);
317   flush_pending_ = false;
318
319   // If more paints were enqueued while we were waiting for the flush to
320   // complete, execute them now.
321   if (aggregator_.HasPendingUpdate())
322     DoPaint();
323
324   // If there was another flush request while flushing we flush again.
325   if (flush_requested_) {
326     Flush();
327   }
328 }
329
330 void PaintManager::OnManualCallbackComplete() {
331   DCHECK(manual_callback_pending_);
332   manual_callback_pending_ = false;
333
334   // Just because we have a manual callback doesn't mean there are actually any
335   // invalid regions. Even though we only schedule this callback when something
336   // is pending, a Flush callback could have come in before this callback was
337   // executed and that could have cleared the queue.
338   if (aggregator_.HasPendingUpdate())
339     DoPaint();
340 }
341
342 }  // namespace chrome_pdf