1 /*
2 * Copyright (C) 2006 Apple Inc. All rights reserved.
3 * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org>
4 * Copyright (C) 2007 Rob Buis <buis@kde.org>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB. If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 */
21
22 #include "third_party/blink/renderer/core/svg/svg_document_extensions.h"
23
24 #include "base/auto_reset.h"
25 #include "third_party/blink/renderer/core/dom/document.h"
26 #include "third_party/blink/renderer/core/svg/animation/smil_time_container.h"
27 #include "third_party/blink/renderer/core/svg/svg_svg_element.h"
28
29 namespace blink {
30
SVGDocumentExtensions(Document * document)31 SVGDocumentExtensions::SVGDocumentExtensions(Document* document)
32 : document_(document) {}
33
34 SVGDocumentExtensions::~SVGDocumentExtensions() = default;
35
AddTimeContainer(SVGSVGElement * element)36 void SVGDocumentExtensions::AddTimeContainer(SVGSVGElement* element) {
37 time_containers_.insert(element);
38 }
39
RemoveTimeContainer(SVGSVGElement * element)40 void SVGDocumentExtensions::RemoveTimeContainer(SVGSVGElement* element) {
41 time_containers_.erase(element);
42 }
43
AddWebAnimationsPendingSVGElement(SVGElement & element)44 void SVGDocumentExtensions::AddWebAnimationsPendingSVGElement(
45 SVGElement& element) {
46 web_animations_pending_svg_elements_.insert(&element);
47 }
48
ServiceOnAnimationFrame(Document & document)49 void SVGDocumentExtensions::ServiceOnAnimationFrame(Document& document) {
50 if (!document.SvgExtensions())
51 return;
52 document.AccessSVGExtensions().ServiceAnimations();
53 }
54
ServiceAnimations()55 void SVGDocumentExtensions::ServiceAnimations() {
56 HeapVector<Member<SVGSVGElement>> time_containers;
57 CopyToVector(time_containers_, time_containers);
58 for (const auto& container : time_containers)
59 container->TimeContainer()->ServiceAnimations();
60
61 SVGElementSet web_animations_pending_svg_elements;
62 web_animations_pending_svg_elements.swap(
63 web_animations_pending_svg_elements_);
64
65 // TODO(alancutter): Make SVG animation effect application a separate document
66 // lifecycle phase from servicing animations to be responsive to Javascript
67 // manipulation of exposed animation objects.
68 for (auto& svg_element : web_animations_pending_svg_elements)
69 svg_element->ApplyActiveWebAnimations();
70
71 DCHECK(web_animations_pending_svg_elements_.IsEmpty());
72 }
73
StartAnimations()74 void SVGDocumentExtensions::StartAnimations() {
75 // FIXME: Eventually every "Time Container" will need a way to latch on to
76 // some global timer starting animations for a document will do this
77 // "latching"
78 // FIXME: We hold a ref pointers to prevent a shadow tree from getting removed
79 // out from underneath us. In the future we should refactor the use-element
80 // to avoid this. See https://webkit.org/b/53704
81 HeapVector<Member<SVGSVGElement>> time_containers;
82 CopyToVector(time_containers_, time_containers);
83 for (const auto& container : time_containers) {
84 SMILTimeContainer* time_container = container->TimeContainer();
85 if (!time_container->IsStarted())
86 time_container->Start();
87 }
88 }
89
PauseAnimations()90 void SVGDocumentExtensions::PauseAnimations() {
91 for (SVGSVGElement* element : time_containers_)
92 element->pauseAnimations();
93 }
94
DispatchSVGLoadEventToOutermostSVGElements()95 void SVGDocumentExtensions::DispatchSVGLoadEventToOutermostSVGElements() {
96 HeapVector<Member<SVGSVGElement>> time_containers;
97 CopyToVector(time_containers_, time_containers);
98 for (const auto& container : time_containers) {
99 SVGSVGElement* outer_svg = container.Get();
100 if (!outer_svg->IsOutermostSVGSVGElement())
101 continue;
102
103 // Don't dispatch the load event document is not wellformed (for
104 // XML/standalone svg).
105 if (outer_svg->GetDocument().WellFormed() ||
106 !outer_svg->GetDocument().IsSVGDocument())
107 outer_svg->SendSVGLoadEventIfPossible();
108 }
109 }
110
AddSVGRootWithRelativeLengthDescendents(SVGSVGElement * svg_root)111 void SVGDocumentExtensions::AddSVGRootWithRelativeLengthDescendents(
112 SVGSVGElement* svg_root) {
113 #if DCHECK_IS_ON()
114 DCHECK(!in_relative_length_svg_roots_invalidation_);
115 #endif
116 relative_length_svg_roots_.insert(svg_root);
117 }
118
RemoveSVGRootWithRelativeLengthDescendents(SVGSVGElement * svg_root)119 void SVGDocumentExtensions::RemoveSVGRootWithRelativeLengthDescendents(
120 SVGSVGElement* svg_root) {
121 #if DCHECK_IS_ON()
122 DCHECK(!in_relative_length_svg_roots_invalidation_);
123 #endif
124 relative_length_svg_roots_.erase(svg_root);
125 }
126
InvalidateSVGRootsWithRelativeLengthDescendents(SubtreeLayoutScope * scope)127 void SVGDocumentExtensions::InvalidateSVGRootsWithRelativeLengthDescendents(
128 SubtreeLayoutScope* scope) {
129 #if DCHECK_IS_ON()
130 DCHECK(!in_relative_length_svg_roots_invalidation_);
131 base::AutoReset<bool> in_relative_length_svg_roots_change(
132 &in_relative_length_svg_roots_invalidation_, true);
133 #endif
134
135 for (SVGSVGElement* element : relative_length_svg_roots_)
136 element->InvalidateRelativeLengthClients(scope);
137 }
138
ZoomAndPanEnabled() const139 bool SVGDocumentExtensions::ZoomAndPanEnabled() const {
140 SVGSVGElement* svg = rootElement(*document_);
141 return !svg || svg->ZoomAndPanEnabled();
142 }
143
StartPan(const FloatPoint & start)144 void SVGDocumentExtensions::StartPan(const FloatPoint& start) {
145 if (SVGSVGElement* svg = rootElement(*document_))
146 translate_ = FloatPoint(start.X() - svg->CurrentTranslate().X(),
147 start.Y() - svg->CurrentTranslate().Y());
148 }
149
UpdatePan(const FloatPoint & pos) const150 void SVGDocumentExtensions::UpdatePan(const FloatPoint& pos) const {
151 if (SVGSVGElement* svg = rootElement(*document_))
152 svg->SetCurrentTranslate(
153 FloatPoint(pos.X() - translate_.X(), pos.Y() - translate_.Y()));
154 }
155
rootElement(const Document & document)156 SVGSVGElement* SVGDocumentExtensions::rootElement(const Document& document) {
157 return DynamicTo<SVGSVGElement>(document.documentElement());
158 }
159
rootElement() const160 SVGSVGElement* SVGDocumentExtensions::rootElement() const {
161 DCHECK(document_);
162 return rootElement(*document_);
163 }
164
Trace(Visitor * visitor)165 void SVGDocumentExtensions::Trace(Visitor* visitor) {
166 visitor->Trace(document_);
167 visitor->Trace(time_containers_);
168 visitor->Trace(web_animations_pending_svg_elements_);
169 visitor->Trace(relative_length_svg_roots_);
170 }
171
172 } // namespace blink
173