1 /*
2  * Copyright (C) 2006 Eric Seidel <eric@webkit.org>
3  * Copyright (C) 2008, 2009 Apple Inc. All rights reserved.
4  * Copyright (C) Research In Motion Limited 2011. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
16  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
19  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include "third_party/blink/renderer/core/svg/graphics/svg_image.h"
29 
30 #include "base/memory/scoped_refptr.h"
31 #include "base/memory/weak_ptr.h"
32 #include "third_party/blink/public/platform/web_url.h"
33 #include "third_party/blink/public/platform/web_url_loader.h"
34 #include "third_party/blink/public/platform/web_url_loader_client.h"
35 #include "third_party/blink/public/platform/web_url_loader_factory.h"
36 #include "third_party/blink/renderer/core/animation/document_animations.h"
37 #include "third_party/blink/renderer/core/animation/document_timeline.h"
38 #include "third_party/blink/renderer/core/dom/document_parser.h"
39 #include "third_party/blink/renderer/core/dom/events/event_dispatch_forbidden_scope.h"
40 #include "third_party/blink/renderer/core/dom/flat_tree_traversal.h"
41 #include "third_party/blink/renderer/core/dom/node_traversal.h"
42 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
43 #include "third_party/blink/renderer/core/frame/local_frame.h"
44 #include "third_party/blink/renderer/core/frame/local_frame_client.h"
45 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
46 #include "third_party/blink/renderer/core/frame/settings.h"
47 #include "third_party/blink/renderer/core/frame/visual_viewport.h"
48 #include "third_party/blink/renderer/core/layout/intrinsic_sizing_info.h"
49 #include "third_party/blink/renderer/core/layout/layout_view.h"
50 #include "third_party/blink/renderer/core/layout/svg/layout_svg_root.h"
51 #include "third_party/blink/renderer/core/loader/frame_load_request.h"
52 #include "third_party/blink/renderer/core/paint/paint_layer.h"
53 #include "third_party/blink/renderer/core/style/computed_style.h"
54 #include "third_party/blink/renderer/core/svg/animation/smil_time_container.h"
55 #include "third_party/blink/renderer/core/svg/graphics/svg_image_chrome_client.h"
56 #include "third_party/blink/renderer/core/svg/svg_document_extensions.h"
57 #include "third_party/blink/renderer/core/svg/svg_fe_image_element.h"
58 #include "third_party/blink/renderer/core/svg/svg_image_element.h"
59 #include "third_party/blink/renderer/core/svg/svg_svg_element.h"
60 #include "third_party/blink/renderer/platform/bindings/script_forbidden_scope.h"
61 #include "third_party/blink/renderer/platform/geometry/int_rect.h"
62 #include "third_party/blink/renderer/platform/geometry/length_functions.h"
63 #include "third_party/blink/renderer/platform/graphics/color.h"
64 #include "third_party/blink/renderer/platform/graphics/dark_mode_image_classifier.h"
65 #include "third_party/blink/renderer/platform/graphics/graphics_context.h"
66 #include "third_party/blink/renderer/platform/graphics/image_observer.h"
67 #include "third_party/blink/renderer/platform/graphics/paint/cull_rect.h"
68 #include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h"
69 #include "third_party/blink/renderer/platform/graphics/paint/paint_canvas.h"
70 #include "third_party/blink/renderer/platform/graphics/paint/paint_record.h"
71 #include "third_party/blink/renderer/platform/graphics/paint/paint_record_builder.h"
72 #include "third_party/blink/renderer/platform/heap/heap.h"
73 #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
74 
75 namespace blink {
76 
77 namespace {
78 
79 using TaskRunnerHandle = scheduler::WebResourceLoadingTaskRunnerHandle;
80 
81 class FailingLoader final : public WebURLLoader {
82  public:
FailingLoader(std::unique_ptr<TaskRunnerHandle> task_runner_handle)83   explicit FailingLoader(std::unique_ptr<TaskRunnerHandle> task_runner_handle)
84       : task_runner_handle_(std::move(task_runner_handle)) {}
85   ~FailingLoader() override = default;
86 
87   // WebURLLoader implementation:
LoadSynchronously(std::unique_ptr<network::ResourceRequest> request,scoped_refptr<WebURLRequest::ExtraData> request_extra_data,int requestor_id,bool download_to_network_cache_only,bool pass_response_pipe_to_client,bool no_mime_sniffing,base::TimeDelta timeout_interval,WebURLLoaderClient *,WebURLResponse &,base::Optional<WebURLError> & error,WebData &,int64_t & encoded_data_length,int64_t & encoded_body_length,WebBlobInfo & downloaded_blob)88   void LoadSynchronously(
89       std::unique_ptr<network::ResourceRequest> request,
90       scoped_refptr<WebURLRequest::ExtraData> request_extra_data,
91       int requestor_id,
92       bool download_to_network_cache_only,
93       bool pass_response_pipe_to_client,
94       bool no_mime_sniffing,
95       base::TimeDelta timeout_interval,
96       WebURLLoaderClient*,
97       WebURLResponse&,
98       base::Optional<WebURLError>& error,
99       WebData&,
100       int64_t& encoded_data_length,
101       int64_t& encoded_body_length,
102       WebBlobInfo& downloaded_blob) override {
103     NOTREACHED();
104   }
LoadAsynchronously(std::unique_ptr<network::ResourceRequest> request,scoped_refptr<WebURLRequest::ExtraData> request_extra_data,int requestor_id,bool download_to_network_cache_only,bool no_mime_sniffing,WebURLLoaderClient * client)105   void LoadAsynchronously(
106       std::unique_ptr<network::ResourceRequest> request,
107       scoped_refptr<WebURLRequest::ExtraData> request_extra_data,
108       int requestor_id,
109       bool download_to_network_cache_only,
110       bool no_mime_sniffing,
111       WebURLLoaderClient* client) override {
112     NOTREACHED();
113   }
SetDefersLoading(bool)114   void SetDefersLoading(bool) override {}
DidChangePriority(WebURLRequest::Priority,int)115   void DidChangePriority(WebURLRequest::Priority, int) override {}
GetTaskRunner()116   scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner() override {
117     return task_runner_handle_->GetTaskRunner();
118   }
119 
120  private:
121   const std::unique_ptr<TaskRunnerHandle> task_runner_handle_;
122 };
123 
124 class FailingLoaderFactory final : public WebURLLoaderFactory {
125  public:
126   // WebURLLoaderFactory implementation:
CreateURLLoader(const WebURLRequest &,std::unique_ptr<TaskRunnerHandle> task_runner_handle)127   std::unique_ptr<WebURLLoader> CreateURLLoader(
128       const WebURLRequest&,
129       std::unique_ptr<TaskRunnerHandle> task_runner_handle) override {
130     return std::make_unique<FailingLoader>(std::move(task_runner_handle));
131   }
132 };
133 
134 }  // namespace
135 
136 // SVGImageLocalFrameClient is used to wait until SVG document's load event
137 // in the case where there are subresources asynchronously loaded.
138 //
139 // Reference cycle: SVGImage -(Persistent)-> Page -(Member)-> Frame -(Member)->
140 // FrameClient == SVGImageLocalFrameClient -(raw)-> SVGImage.
141 class SVGImage::SVGImageLocalFrameClient : public EmptyLocalFrameClient {
142  public:
SVGImageLocalFrameClient(SVGImage * image)143   SVGImageLocalFrameClient(SVGImage* image) : image_(image) {}
144 
ClearImage()145   void ClearImage() { image_ = nullptr; }
146 
147  private:
CreateURLLoaderFactory()148   std::unique_ptr<WebURLLoaderFactory> CreateURLLoaderFactory() override {
149     // SVG Images have unique security rules that prevent all subresource
150     // requests except for data urls.
151     return std::make_unique<FailingLoaderFactory>();
152   }
153 
DispatchDidHandleOnloadEvents()154   void DispatchDidHandleOnloadEvents() override {
155     // The SVGImage was destructed before SVG load completion.
156     if (!image_)
157       return;
158 
159     image_->LoadCompleted();
160   }
161 
162   // Cleared manually by SVGImage's destructor when |image_| is destructed.
163   SVGImage* image_;
164 };
165 
SVGImage(ImageObserver * observer,bool is_multipart)166 SVGImage::SVGImage(ImageObserver* observer, bool is_multipart)
167     : Image(observer, is_multipart),
168       paint_controller_(std::make_unique<PaintController>()),
169       has_pending_timeline_rewind_(false) {}
170 
~SVGImage()171 SVGImage::~SVGImage() {
172   if (frame_client_)
173     frame_client_->ClearImage();
174 
175   if (page_) {
176     // It is safe to allow UA events within this scope, because event
177     // dispatching inside the SVG image's document doesn't trigger JavaScript
178     // execution. All script execution is forbidden when an SVG is loaded as an
179     // image subresource - see SetScriptEnabled in SVGImage::DataChanged().
180     EventDispatchForbiddenScope::AllowUserAgentEvents allow_events;
181     // Store m_page in a local variable, clearing m_page, so that
182     // SVGImageChromeClient knows we're destructed.
183     Page* current_page = page_.Release();
184     // Break both the loader and view references to the frame
185     current_page->WillBeDestroyed();
186   }
187 
188   // Verify that page teardown destroyed the Chrome
189   DCHECK(!chrome_client_ || !chrome_client_->GetImage());
190 }
191 
IsInSVGImage(const Node * node)192 bool SVGImage::IsInSVGImage(const Node* node) {
193   DCHECK(node);
194 
195   Page* page = node->GetDocument().GetPage();
196   if (!page)
197     return false;
198 
199   return page->GetChromeClient().IsSVGImageChromeClient();
200 }
201 
CheckLoaded() const202 void SVGImage::CheckLoaded() const {
203   CHECK(page_);
204 
205   auto* frame = To<LocalFrame>(page_->MainFrame());
206 
207   // Failures of this assertion might result in wrong origin tainting checks,
208   // because CurrentFrameHasSingleSecurityOrigin() assumes all subresources of
209   // the SVG are loaded and thus ready for origin checks.
210   CHECK(frame->GetDocument()->LoadEventFinished());
211 }
212 
CurrentFrameHasSingleSecurityOrigin() const213 bool SVGImage::CurrentFrameHasSingleSecurityOrigin() const {
214   if (!page_)
215     return true;
216 
217   auto* frame = To<LocalFrame>(page_->MainFrame());
218 
219   CheckLoaded();
220 
221   SVGSVGElement* root_element =
222       frame->GetDocument()->AccessSVGExtensions().rootElement();
223   if (!root_element)
224     return true;
225 
226   // Don't allow foreignObject elements or images that are not known to be
227   // single-origin since these can leak cross-origin information.
228   for (Node* node = root_element; node; node = FlatTreeTraversal::Next(*node)) {
229     if (IsA<SVGForeignObjectElement>(*node))
230       return false;
231     if (auto* image = DynamicTo<SVGImageElement>(*node)) {
232       if (!image->CurrentFrameHasSingleSecurityOrigin())
233         return false;
234     } else if (auto* fe_image = DynamicTo<SVGFEImageElement>(*node)) {
235       if (!fe_image->CurrentFrameHasSingleSecurityOrigin())
236         return false;
237     }
238   }
239 
240   // Because SVG image rendering disallows external resources and links, these
241   // images effectively are restricted to a single security origin.
242   return true;
243 }
244 
SvgRootElement(Page * page)245 static SVGSVGElement* SvgRootElement(Page* page) {
246   if (!page)
247     return nullptr;
248   auto* frame = To<LocalFrame>(page->MainFrame());
249   return frame->GetDocument()->AccessSVGExtensions().rootElement();
250 }
251 
ContainerSize() const252 LayoutSize SVGImage::ContainerSize() const {
253   SVGSVGElement* root_element = SvgRootElement(page_.Get());
254   if (!root_element)
255     return LayoutSize();
256 
257   LayoutSVGRoot* layout_object =
258       ToLayoutSVGRoot(root_element->GetLayoutObject());
259   if (!layout_object)
260     return LayoutSize();
261 
262   // If a container size is available it has precedence.
263   LayoutSize container_size = layout_object->ContainerSize();
264   if (!container_size.IsEmpty())
265     return container_size;
266 
267   // Assure that a container size is always given for a non-identity zoom level.
268   DCHECK_EQ(layout_object->StyleRef().EffectiveZoom(), 1);
269 
270   // No set container size; use concrete object size.
271   return intrinsic_size_;
272 }
273 
Size() const274 IntSize SVGImage::Size() const {
275   return RoundedIntSize(intrinsic_size_);
276 }
277 
ResolveWidthForRatio(float height,const FloatSize & intrinsic_ratio)278 static float ResolveWidthForRatio(float height,
279                                   const FloatSize& intrinsic_ratio) {
280   return height * intrinsic_ratio.Width() / intrinsic_ratio.Height();
281 }
282 
ResolveHeightForRatio(float width,const FloatSize & intrinsic_ratio)283 static float ResolveHeightForRatio(float width,
284                                    const FloatSize& intrinsic_ratio) {
285   return width * intrinsic_ratio.Height() / intrinsic_ratio.Width();
286 }
287 
HasIntrinsicDimensions() const288 bool SVGImage::HasIntrinsicDimensions() const {
289   return !ConcreteObjectSize(FloatSize()).IsEmpty();
290 }
291 
HasIntrinsicSizingInfo() const292 bool SVGImage::HasIntrinsicSizingInfo() const {
293   SVGSVGElement* svg = SvgRootElement(page_.Get());
294   return svg && svg->GetLayoutObject();
295 }
296 
GetIntrinsicSizingInfo(IntrinsicSizingInfo & intrinsic_sizing_info) const297 bool SVGImage::GetIntrinsicSizingInfo(
298     IntrinsicSizingInfo& intrinsic_sizing_info) const {
299   SVGSVGElement* svg = SvgRootElement(page_.Get());
300   if (!svg)
301     return false;
302 
303   LayoutSVGRoot* layout_object = ToLayoutSVGRoot(svg->GetLayoutObject());
304   if (!layout_object)
305     return false;
306 
307   layout_object->UnscaledIntrinsicSizingInfo(intrinsic_sizing_info);
308   return true;
309 }
310 
ConcreteObjectSize(const FloatSize & default_object_size) const311 FloatSize SVGImage::ConcreteObjectSize(
312     const FloatSize& default_object_size) const {
313   IntrinsicSizingInfo intrinsic_sizing_info;
314   if (!GetIntrinsicSizingInfo(intrinsic_sizing_info))
315     return FloatSize();
316 
317   // https://www.w3.org/TR/css3-images/#default-sizing
318   if (intrinsic_sizing_info.has_width && intrinsic_sizing_info.has_height)
319     return intrinsic_sizing_info.size;
320 
321   // We're not using an intrinsic aspect ratio to resolve a missing
322   // intrinsic width or height when preserveAspectRatio is none.
323   // (Ref: crbug.com/584172)
324   SVGSVGElement* svg = SvgRootElement(page_.Get());
325   if (svg->preserveAspectRatio()->CurrentValue()->Align() ==
326       SVGPreserveAspectRatio::kSvgPreserveaspectratioNone)
327     return default_object_size;
328 
329   if (intrinsic_sizing_info.has_width) {
330     if (intrinsic_sizing_info.aspect_ratio.IsEmpty())
331       return FloatSize(intrinsic_sizing_info.size.Width(),
332                        default_object_size.Height());
333 
334     return FloatSize(intrinsic_sizing_info.size.Width(),
335                      ResolveHeightForRatio(intrinsic_sizing_info.size.Width(),
336                                            intrinsic_sizing_info.aspect_ratio));
337   }
338 
339   if (intrinsic_sizing_info.has_height) {
340     if (intrinsic_sizing_info.aspect_ratio.IsEmpty())
341       return FloatSize(default_object_size.Width(),
342                        intrinsic_sizing_info.size.Height());
343 
344     return FloatSize(ResolveWidthForRatio(intrinsic_sizing_info.size.Height(),
345                                           intrinsic_sizing_info.aspect_ratio),
346                      intrinsic_sizing_info.size.Height());
347   }
348 
349   if (!intrinsic_sizing_info.aspect_ratio.IsEmpty()) {
350     // "A contain constraint is resolved by setting the concrete object size to
351     //  the largest rectangle that has the object's intrinsic aspect ratio and
352     //  additionally has neither width nor height larger than the constraint
353     //  rectangle's width and height, respectively."
354     float solution_width = ResolveWidthForRatio(
355         default_object_size.Height(), intrinsic_sizing_info.aspect_ratio);
356     if (solution_width <= default_object_size.Width())
357       return FloatSize(solution_width, default_object_size.Height());
358 
359     float solution_height = ResolveHeightForRatio(
360         default_object_size.Width(), intrinsic_sizing_info.aspect_ratio);
361     return FloatSize(default_object_size.Width(), solution_height);
362   }
363 
364   return default_object_size;
365 }
366 
367 template <typename Func>
ForContainer(const FloatSize & container_size,Func && func)368 void SVGImage::ForContainer(const FloatSize& container_size, Func&& func) {
369   if (!page_)
370     return;
371 
372   // Temporarily disable the image observer to prevent changeInRect() calls due
373   // re-laying out the image.
374   ImageObserverDisabler image_observer_disabler(this);
375 
376   LayoutSize rounded_container_size = RoundedLayoutSize(container_size);
377 
378   if (SVGSVGElement* root_element = SvgRootElement(page_.Get())) {
379     if (LayoutSVGRoot* layout_object =
380             ToLayoutSVGRoot(root_element->GetLayoutObject()))
381       layout_object->SetContainerSize(rounded_container_size);
382   }
383 
384   func(FloatSize(rounded_container_size.Width() / container_size.Width(),
385                  rounded_container_size.Height() / container_size.Height()));
386 }
387 
DrawForContainer(cc::PaintCanvas * canvas,const PaintFlags & flags,const FloatSize & container_size,float zoom,const FloatRect & dst_rect,const FloatRect & src_rect,const KURL & url)388 void SVGImage::DrawForContainer(cc::PaintCanvas* canvas,
389                                 const PaintFlags& flags,
390                                 const FloatSize& container_size,
391                                 float zoom,
392                                 const FloatRect& dst_rect,
393                                 const FloatRect& src_rect,
394                                 const KURL& url) {
395   ForContainer(container_size, [&](const FloatSize& residual_scale) {
396     FloatRect scaled_src = src_rect;
397     scaled_src.Scale(1 / zoom);
398 
399     // Compensate for the container size rounding by adjusting the source rect.
400     FloatSize adjusted_src_size = scaled_src.Size();
401     adjusted_src_size.Scale(residual_scale.Width(), residual_scale.Height());
402     scaled_src.SetSize(adjusted_src_size);
403 
404     DrawInternal(canvas, flags, dst_rect, scaled_src, kRespectImageOrientation,
405                  kClampImageToSourceRect, url);
406   });
407 }
408 
PaintImageForCurrentFrame()409 PaintImage SVGImage::PaintImageForCurrentFrame() {
410   auto builder =
411       CreatePaintImageBuilder().set_completion_state(completion_state());
412   PopulatePaintRecordForCurrentFrameForContainer(builder, NullURL(), Size());
413   return builder.TakePaintImage();
414 }
415 
DrawPatternForContainer(GraphicsContext & context,const FloatSize container_size,float zoom,const FloatRect & src_rect,const FloatSize & tile_scale,const FloatPoint & phase,SkBlendMode composite_op,const FloatRect & dst_rect,const FloatSize & repeat_spacing,const KURL & url)416 void SVGImage::DrawPatternForContainer(GraphicsContext& context,
417                                        const FloatSize container_size,
418                                        float zoom,
419                                        const FloatRect& src_rect,
420                                        const FloatSize& tile_scale,
421                                        const FloatPoint& phase,
422                                        SkBlendMode composite_op,
423                                        const FloatRect& dst_rect,
424                                        const FloatSize& repeat_spacing,
425                                        const KURL& url) {
426   // Tile adjusted for scaling/stretch.
427   FloatRect tile(src_rect);
428   tile.Scale(tile_scale.Width(), tile_scale.Height());
429 
430   // Expand the tile to account for repeat spacing.
431   FloatRect spaced_tile(tile);
432   spaced_tile.Expand(FloatSize(repeat_spacing));
433 
434   PaintRecordBuilder builder(nullptr, &context);
435   {
436     DrawingRecorder recorder(builder.Context(), builder,
437                              DisplayItem::Type::kSVGImage);
438     // When generating an expanded tile, make sure we don't draw into the
439     // spacing area.
440     if (tile != spaced_tile)
441       builder.Context().Clip(tile);
442     PaintFlags flags;
443     DrawForContainer(builder.Context().Canvas(), flags, container_size, zoom,
444                      tile, src_rect, url);
445   }
446   sk_sp<PaintRecord> record = builder.EndRecording();
447 
448   SkMatrix pattern_transform;
449   pattern_transform.setTranslate(phase.X() + spaced_tile.X(),
450                                  phase.Y() + spaced_tile.Y());
451 
452   PaintFlags flags;
453   flags.setShader(
454       PaintShader::MakePaintRecord(record, spaced_tile, SkTileMode::kRepeat,
455                                    SkTileMode::kRepeat, &pattern_transform));
456   // If the shader could not be instantiated (e.g. non-invertible matrix),
457   // draw transparent.
458   // Note: we can't simply bail, because of arbitrary blend mode.
459   if (!flags.HasShader())
460     flags.setColor(SK_ColorTRANSPARENT);
461 
462   flags.setBlendMode(composite_op);
463   flags.setColorFilter(sk_ref_sp(context.GetColorFilter()));
464   context.DrawRect(dst_rect, flags);
465 
466   StartAnimation();
467 }
468 
PaintRecordForContainer(const KURL & url,const IntSize & container_size,const IntRect & draw_src_rect,const IntRect & draw_dst_rect,bool flip_y)469 sk_sp<PaintRecord> SVGImage::PaintRecordForContainer(
470     const KURL& url,
471     const IntSize& container_size,
472     const IntRect& draw_src_rect,
473     const IntRect& draw_dst_rect,
474     bool flip_y) {
475   if (!page_)
476     return nullptr;
477 
478   PaintRecorder recorder;
479   cc::PaintCanvas* canvas = recorder.beginRecording(draw_src_rect);
480   if (flip_y) {
481     canvas->translate(0, draw_dst_rect.Height());
482     canvas->scale(1, -1);
483   }
484   DrawForContainer(canvas, PaintFlags(), FloatSize(container_size), 1,
485                    FloatRect(draw_dst_rect), FloatRect(draw_src_rect), url);
486   return recorder.finishRecordingAsPicture();
487 }
488 
PopulatePaintRecordForCurrentFrameForContainer(PaintImageBuilder & builder,const KURL & url,const IntSize & container_size)489 void SVGImage::PopulatePaintRecordForCurrentFrameForContainer(
490     PaintImageBuilder& builder,
491     const KURL& url,
492     const IntSize& container_size) {
493   if (!page_)
494     return;
495 
496   const IntRect container_rect(IntPoint(), container_size);
497 
498   PaintRecorder recorder;
499   cc::PaintCanvas* canvas = recorder.beginRecording(container_rect);
500   DrawForContainer(canvas, PaintFlags(), FloatSize(container_rect.Size()), 1,
501                    FloatRect(container_rect), FloatRect(container_rect), url);
502   builder.set_paint_record(recorder.finishRecordingAsPicture(), container_rect,
503                            PaintImage::GetNextContentId());
504 }
505 
DrawNeedsLayer(const PaintFlags & flags)506 static bool DrawNeedsLayer(const PaintFlags& flags) {
507   if (SkColorGetA(flags.getColor()) < 255)
508     return true;
509 
510   // This is needed to preserve the dark mode filter that
511   // has been set in GraphicsContext.
512   if (flags.getColorFilter())
513     return true;
514 
515   return flags.getBlendMode() != SkBlendMode::kSrcOver;
516 }
517 
ApplyShaderInternal(PaintFlags & flags,const SkMatrix & local_matrix,const KURL & url)518 bool SVGImage::ApplyShaderInternal(PaintFlags& flags,
519                                    const SkMatrix& local_matrix,
520                                    const KURL& url) {
521   const FloatSize size(ContainerSize());
522   if (size.IsEmpty())
523     return false;
524 
525   FloatRect bounds(FloatPoint(), size);
526   flags.setShader(PaintShader::MakePaintRecord(
527       PaintRecordForCurrentFrame(url), bounds, SkTileMode::kRepeat,
528       SkTileMode::kRepeat, &local_matrix));
529 
530   // Animation is normally refreshed in draw() impls, which we don't reach when
531   // painting via shaders.
532   StartAnimation();
533 
534   return true;
535 }
536 
ApplyShader(PaintFlags & flags,const SkMatrix & local_matrix)537 bool SVGImage::ApplyShader(PaintFlags& flags, const SkMatrix& local_matrix) {
538   return ApplyShaderInternal(flags, local_matrix, NullURL());
539 }
540 
ApplyShaderForContainer(const FloatSize & container_size,float zoom,const KURL & url,PaintFlags & flags,const SkMatrix & local_matrix)541 bool SVGImage::ApplyShaderForContainer(const FloatSize& container_size,
542                                        float zoom,
543                                        const KURL& url,
544                                        PaintFlags& flags,
545                                        const SkMatrix& local_matrix) {
546   bool result = false;
547   ForContainer(container_size, [&](const FloatSize& residual_scale) {
548     // Compensate for the container size rounding.
549     auto adjusted_local_matrix = local_matrix;
550     adjusted_local_matrix.preScale(zoom * residual_scale.Width(),
551                                    zoom * residual_scale.Height());
552 
553     result = ApplyShaderInternal(flags, adjusted_local_matrix, url);
554   });
555 
556   return result;
557 }
558 
Draw(cc::PaintCanvas * canvas,const PaintFlags & flags,const FloatRect & dst_rect,const FloatRect & src_rect,RespectImageOrientationEnum should_respect_image_orientation,ImageClampingMode clamp_mode,ImageDecodingMode)559 void SVGImage::Draw(
560     cc::PaintCanvas* canvas,
561     const PaintFlags& flags,
562     const FloatRect& dst_rect,
563     const FloatRect& src_rect,
564     RespectImageOrientationEnum should_respect_image_orientation,
565     ImageClampingMode clamp_mode,
566     ImageDecodingMode) {
567   if (!page_)
568     return;
569 
570   DrawInternal(canvas, flags, dst_rect, src_rect,
571                should_respect_image_orientation, clamp_mode, NullURL());
572 }
573 
PaintRecordForCurrentFrame(const KURL & url)574 sk_sp<PaintRecord> SVGImage::PaintRecordForCurrentFrame(const KURL& url) {
575   DCHECK(page_);
576   LocalFrameView* view = To<LocalFrame>(page_->MainFrame())->View();
577   IntSize rounded_container_size = RoundedIntSize(ContainerSize());
578   view->Resize(rounded_container_size);
579   page_->GetVisualViewport().SetSize(rounded_container_size);
580 
581   // Always call processUrlFragment, even if the url is empty, because
582   // there may have been a previous url/fragment that needs to be reset.
583   view->ProcessUrlFragment(url, /*same_document_navigation=*/false);
584 
585   // If the image was reset, we need to rewind the timeline back to 0. This
586   // needs to be done before painting, or else we wouldn't get the correct
587   // reset semantics (we'd paint the "last" frame rather than the one at
588   // time=0.) The reason we do this here and not in resetAnimation() is to
589   // avoid setting timers from the latter.
590   FlushPendingTimelineRewind();
591 
592   if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
593     view->UpdateAllLifecyclePhases(DocumentUpdateReason::kSVGImage);
594     return view->GetPaintRecord();
595   }
596 
597   view->UpdateAllLifecyclePhasesExceptPaint(DocumentUpdateReason::kSVGImage);
598   PaintRecordBuilder builder(nullptr, nullptr, paint_controller_.get());
599   view->PaintOutsideOfLifecycle(builder.Context(), kGlobalPaintNormalPhase);
600   return builder.EndRecording();
601 }
602 
DrawInternal(cc::PaintCanvas * canvas,const PaintFlags & flags,const FloatRect & dst_rect,const FloatRect & src_rect,RespectImageOrientationEnum,ImageClampingMode,const KURL & url)603 void SVGImage::DrawInternal(cc::PaintCanvas* canvas,
604                             const PaintFlags& flags,
605                             const FloatRect& dst_rect,
606                             const FloatRect& src_rect,
607                             RespectImageOrientationEnum,
608                             ImageClampingMode,
609                             const KURL& url) {
610   {
611     PaintCanvasAutoRestore ar(canvas, false);
612     if (DrawNeedsLayer(flags)) {
613       SkRect layer_rect = dst_rect;
614       canvas->saveLayer(&layer_rect, &flags);
615     }
616     // We can only draw the entire frame, clipped to the rect we want. So
617     // compute where the top left of the image would be if we were drawing
618     // without clipping, and translate accordingly.
619     canvas->save();
620     canvas->clipRect(EnclosingIntRect(dst_rect));
621     canvas->concat(SkMatrix::MakeRectToRect(src_rect, dst_rect,
622                                             SkMatrix::kFill_ScaleToFit));
623     canvas->drawPicture(PaintRecordForCurrentFrame(url));
624     canvas->restore();
625   }
626 
627   // Start any (SMIL) animations if needed. This will restart or continue
628   // animations if preceded by calls to resetAnimation or stopAnimation
629   // respectively.
630   StartAnimation();
631 }
632 
ScheduleTimelineRewind()633 void SVGImage::ScheduleTimelineRewind() {
634   has_pending_timeline_rewind_ = true;
635 }
636 
FlushPendingTimelineRewind()637 void SVGImage::FlushPendingTimelineRewind() {
638   if (!has_pending_timeline_rewind_)
639     return;
640   if (SVGSVGElement* root_element = SvgRootElement(page_.Get()))
641     root_element->setCurrentTime(0);
642   has_pending_timeline_rewind_ = false;
643 }
644 
StartAnimation()645 void SVGImage::StartAnimation() {
646   SVGSVGElement* root_element = SvgRootElement(page_.Get());
647   if (!root_element)
648     return;
649   chrome_client_->ResumeAnimation();
650   if (root_element->animationsPaused())
651     root_element->unpauseAnimations();
652 }
653 
StopAnimation()654 void SVGImage::StopAnimation() {
655   SVGSVGElement* root_element = SvgRootElement(page_.Get());
656   if (!root_element)
657     return;
658   chrome_client_->SuspendAnimation();
659   root_element->pauseAnimations();
660 }
661 
ResetAnimation()662 void SVGImage::ResetAnimation() {
663   SVGSVGElement* root_element = SvgRootElement(page_.Get());
664   if (!root_element)
665     return;
666   chrome_client_->SuspendAnimation();
667   root_element->pauseAnimations();
668   ScheduleTimelineRewind();
669 }
670 
RestoreAnimation()671 void SVGImage::RestoreAnimation() {
672   // If the image has no animations then do nothing.
673   if (!MaybeAnimated())
674     return;
675   // If there are no clients, or no client is going to render, then do nothing.
676   ImageObserver* image_observer = GetImageObserver();
677   if (!image_observer || image_observer->ShouldPauseAnimation(this))
678     return;
679   StartAnimation();
680 }
681 
MaybeAnimated()682 bool SVGImage::MaybeAnimated() {
683   SVGSVGElement* root_element = SvgRootElement(page_.Get());
684   if (!root_element)
685     return false;
686   return root_element->TimeContainer()->HasAnimations() ||
687          To<LocalFrame>(page_->MainFrame())
688              ->GetDocument()
689              ->Timeline()
690              .HasPendingUpdates();
691 }
692 
ServiceAnimations(base::TimeTicks monotonic_animation_start_time)693 void SVGImage::ServiceAnimations(
694     base::TimeTicks monotonic_animation_start_time) {
695   if (!GetImageObserver())
696     return;
697 
698   // If none of our observers (sic!) are visible, or for some other reason
699   // does not want us to keep running animations, stop them until further
700   // notice (next paint.)
701   if (GetImageObserver()->ShouldPauseAnimation(this)) {
702     StopAnimation();
703     return;
704   }
705 
706   // serviceScriptedAnimations runs requestAnimationFrame callbacks, but SVG
707   // images can't have any so we assert there's no script.
708   ScriptForbiddenScope forbid_script;
709 
710   // The calls below may trigger GCs, so set up the required persistent
711   // reference on the ImageResourceContent which owns this SVGImage. By
712   // transitivity, that will keep the associated SVGImageChromeClient object
713   // alive.
714   Persistent<ImageObserver> protect(GetImageObserver());
715   page_->Animator().ServiceScriptedAnimations(monotonic_animation_start_time);
716 
717   // Do *not* update the paint phase. It's critical to paint only when
718   // actually generating painted output, not only for performance reasons,
719   // but to preserve correct coherence of the cache of the output with
720   // the needsRepaint bits of the PaintLayers in the image.
721   LocalFrameView* frame_view = To<LocalFrame>(page_->MainFrame())->View();
722   frame_view->UpdateAllLifecyclePhasesExceptPaint(
723       DocumentUpdateReason::kSVGImage);
724 
725   // We run UpdateAnimations after the paint phase, but per the above comment,
726   // we don't want to run lifecycle through to paint for SVG images. Since we
727   // know SVG images never have composited animations, we can update animations
728   // directly without worrying about including PaintArtifactCompositor's
729   // analysis of whether animations should be composited.
730   frame_view->GetLayoutView()
731       ->GetDocument()
732       .GetDocumentAnimations()
733       .UpdateAnimations(DocumentLifecycle::kLayoutClean, nullptr);
734 }
735 
AdvanceAnimationForTesting()736 void SVGImage::AdvanceAnimationForTesting() {
737   if (SVGSVGElement* root_element = SvgRootElement(page_.Get())) {
738     root_element->TimeContainer()->AdvanceFrameForTesting();
739 
740     // The following triggers animation updates which can issue a new draw
741     // and temporarily change the animation timeline. It's necessary to call
742     // reset before changing to a time value as animation clock does not
743     // expect to go backwards.
744     base::TimeTicks current_animation_time =
745         page_->Animator().Clock().CurrentTime();
746     page_->Animator().Clock().ResetTimeForTesting();
747     if (root_element->TimeContainer()->IsStarted())
748       root_element->TimeContainer()->ResetDocumentTime();
749     page_->Animator().ServiceScriptedAnimations(
750         root_element->GetDocument().Timeline().ZeroTime() +
751         base::TimeDelta::FromSecondsD(root_element->getCurrentTime()));
752     GetImageObserver()->Changed(this);
753     page_->Animator().Clock().ResetTimeForTesting();
754     page_->Animator().Clock().UpdateTime(current_animation_time);
755   }
756 }
757 
ChromeClientForTesting()758 SVGImageChromeClient& SVGImage::ChromeClientForTesting() {
759   return *chrome_client_;
760 }
761 
UpdateUseCounters(const Document & document) const762 void SVGImage::UpdateUseCounters(const Document& document) const {
763   if (SVGSVGElement* root_element = SvgRootElement(page_.Get())) {
764     if (root_element->TimeContainer()->HasAnimations()) {
765       document.CountUse(WebFeature::kSVGSMILAnimationInImageRegardlessOfCache);
766     }
767   }
768 }
769 
LoadCompleted()770 void SVGImage::LoadCompleted() {
771   switch (load_state_) {
772     case kInDataChanged:
773       load_state_ = kLoadCompleted;
774       break;
775 
776     case kWaitingForAsyncLoadCompletion:
777       load_state_ = kLoadCompleted;
778 
779       // Because LoadCompleted() is called synchronously from
780       // Document::ImplicitClose(), we defer AsyncLoadCompleted() to avoid
781       // potential bugs and timing dependencies around ImplicitClose() and
782       // to make LoadEventFinished() true when AsyncLoadCompleted() is called.
783       To<LocalFrame>(page_->MainFrame())
784           ->GetTaskRunner(TaskType::kInternalLoading)
785           ->PostTask(FROM_HERE, WTF::Bind(&SVGImage::NotifyAsyncLoadCompleted,
786                                           scoped_refptr<SVGImage>(this)));
787       break;
788 
789     case kDataChangedNotStarted:
790     case kLoadCompleted:
791       CHECK(false);
792       break;
793   }
794 }
795 
NotifyAsyncLoadCompleted()796 void SVGImage::NotifyAsyncLoadCompleted() {
797   if (GetImageObserver())
798     GetImageObserver()->AsyncLoadCompleted(this);
799 }
800 
DataChanged(bool all_data_received)801 Image::SizeAvailability SVGImage::DataChanged(bool all_data_received) {
802   TRACE_EVENT0("blink", "SVGImage::dataChanged");
803 
804   // Don't do anything if is an empty image.
805   if (!Data()->size())
806     return kSizeAvailable;
807 
808   if (!all_data_received)
809     return page_ ? kSizeAvailable : kSizeUnavailable;
810 
811   CHECK(!page_);
812 
813   // SVGImage will fire events (and the default C++ handlers run) but doesn't
814   // actually allow script to run so it's fine to call into it. We allow this
815   // since it means an SVG data url can synchronously load like other image
816   // types.
817   EventDispatchForbiddenScope::AllowUserAgentEvents allow_user_agent_events;
818 
819   CHECK_EQ(load_state_, kDataChangedNotStarted);
820   load_state_ = kInDataChanged;
821 
822   Page::PageClients page_clients;
823   FillWithEmptyClients(page_clients);
824   chrome_client_ = MakeGarbageCollected<SVGImageChromeClient>(this);
825   page_clients.chrome_client = chrome_client_.Get();
826 
827   // FIXME: If this SVG ends up loading itself, we might leak the world.
828   // The Cache code does not know about ImageResources holding Frames and
829   // won't know to break the cycle.
830   // This will become an issue when SVGImage will be able to load other
831   // SVGImage objects, but we're safe now, because SVGImage can only be
832   // loaded by a top-level document.
833   Page* page;
834   {
835     TRACE_EVENT0("blink", "SVGImage::dataChanged::createPage");
836     page = Page::CreateNonOrdinary(page_clients);
837     page->GetSettings().SetScriptEnabled(false);
838     page->GetSettings().SetPluginsEnabled(false);
839 
840     // Because this page is detached, it can't get default font settings
841     // from the embedder. Copy over font settings so we have sensible
842     // defaults. These settings are fixed and will not update if changed.
843     if (!Page::OrdinaryPages().IsEmpty()) {
844       Settings& default_settings =
845           (*Page::OrdinaryPages().begin())->GetSettings();
846       page->GetSettings().GetGenericFontFamilySettings() =
847           default_settings.GetGenericFontFamilySettings();
848       page->GetSettings().SetMinimumFontSize(
849           default_settings.GetMinimumFontSize());
850       page->GetSettings().SetMinimumLogicalFontSize(
851           default_settings.GetMinimumLogicalFontSize());
852       page->GetSettings().SetDefaultFontSize(
853           default_settings.GetDefaultFontSize());
854       page->GetSettings().SetDefaultFixedFontSize(
855           default_settings.GetDefaultFixedFontSize());
856 
857       // Also copy the preferred-color-scheme to ensure a responsiveness to
858       // dark/light color schemes.
859       page->GetSettings().SetPreferredColorScheme(
860           default_settings.GetPreferredColorScheme());
861     }
862   }
863 
864   LocalFrame* frame = nullptr;
865   {
866     TRACE_EVENT0("blink", "SVGImage::dataChanged::createFrame");
867     DCHECK(!frame_client_);
868     frame_client_ = MakeGarbageCollected<SVGImageLocalFrameClient>(this);
869     frame = MakeGarbageCollected<LocalFrame>(frame_client_, *page, nullptr,
870                                              nullptr, nullptr);
871     frame->SetView(MakeGarbageCollected<LocalFrameView>(*frame));
872     frame->Init();
873   }
874 
875   FrameLoader& loader = frame->Loader();
876   loader.ForceSandboxFlags(mojom::blink::WebSandboxFlags::kAll);
877 
878   // SVG Images will always synthesize a viewBox, if it's not available, and
879   // thus never see scrollbars.
880   frame->View()->SetCanHaveScrollbars(false);
881   // SVG Images are transparent.
882   frame->View()->SetBaseBackgroundColor(Color::kTransparent);
883 
884   page_ = page;
885 
886   TRACE_EVENT0("blink", "SVGImage::dataChanged::load");
887 
888   frame->ForceSynchronousDocumentInstall("image/svg+xml", Data());
889 
890   // Intrinsic sizing relies on computed style (e.g. font-size and
891   // writing-mode).
892   frame->GetDocument()->UpdateStyleAndLayoutTree();
893 
894   // Set the concrete object size before a container size is available.
895   intrinsic_size_ = RoundedLayoutSize(ConcreteObjectSize(FloatSize(
896       LayoutReplaced::kDefaultWidth, LayoutReplaced::kDefaultHeight)));
897 
898   DCHECK(page_);
899   switch (load_state_) {
900     case kInDataChanged:
901       load_state_ = kWaitingForAsyncLoadCompletion;
902       return SvgRootElement(page_.Get())
903                  ? kSizeAvailableAndLoadingAsynchronously
904                  : kSizeUnavailable;
905 
906     case kLoadCompleted:
907       return SvgRootElement(page_.Get()) ? kSizeAvailable : kSizeUnavailable;
908 
909     case kDataChangedNotStarted:
910     case kWaitingForAsyncLoadCompletion:
911       CHECK(false);
912       break;
913   }
914 
915   NOTREACHED();
916   return kSizeAvailable;
917 }
918 
IsSizeAvailable()919 bool SVGImage::IsSizeAvailable() {
920   return SvgRootElement(page_.Get());
921 }
922 
FilenameExtension() const923 String SVGImage::FilenameExtension() const {
924   return "svg";
925 }
926 
CheckTypeSpecificConditionsForDarkMode(const FloatRect & dest_rect,DarkModeImageClassifier * classifier)927 DarkModeClassification SVGImage::CheckTypeSpecificConditionsForDarkMode(
928     const FloatRect& dest_rect,
929     DarkModeImageClassifier* classifier) {
930   classifier->SetImageType(DarkModeImageClassifier::ImageType::kSvg);
931   return DarkModeClassification::kNotClassified;
932 }
933 
934 }  // namespace blink
935