Bug 1686855 [wpt PR 27197] - PlzDedicatedWorker: WPT for clients.matchAll() with...
[gecko.git] / image / SVGDocumentWrapper.cpp
blob386e209ea90ae68d772d1ddfc58cc3e15daaa9c3
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "SVGDocumentWrapper.h"
8 #include "mozilla/PresShell.h"
9 #include "mozilla/SMILAnimationController.h"
10 #include "mozilla/SVGObserverUtils.h"
11 #include "mozilla/dom/Animation.h"
12 #include "mozilla/dom/Document.h"
13 #include "mozilla/dom/DocumentTimeline.h"
14 #include "mozilla/dom/Element.h"
15 #include "mozilla/dom/ImageTracker.h"
16 #include "mozilla/dom/SVGDocument.h"
17 #include "mozilla/dom/SVGSVGElement.h"
18 #include "nsICategoryManager.h"
19 #include "nsIChannel.h"
20 #include "nsIContentViewer.h"
21 #include "nsIDocumentLoaderFactory.h"
22 #include "nsIHttpChannel.h"
23 #include "nsIObserverService.h"
24 #include "nsIParser.h"
25 #include "nsIRequest.h"
26 #include "nsIStreamListener.h"
27 #include "nsIXMLContentSink.h"
28 #include "nsNetCID.h"
29 #include "nsComponentManagerUtils.h"
30 #include "nsServiceManagerUtils.h"
31 #include "nsMimeTypes.h"
32 #include "nsRefreshDriver.h"
34 namespace mozilla {
36 using namespace dom;
37 using namespace gfx;
39 namespace image {
41 NS_IMPL_ISUPPORTS(SVGDocumentWrapper, nsIStreamListener, nsIRequestObserver,
42 nsIObserver, nsISupportsWeakReference)
44 SVGDocumentWrapper::SVGDocumentWrapper()
45 : mIgnoreInvalidation(false), mRegisteredForXPCOMShutdown(false) {}
47 SVGDocumentWrapper::~SVGDocumentWrapper() {
48 DestroyViewer();
49 if (mRegisteredForXPCOMShutdown) {
50 UnregisterForXPCOMShutdown();
54 void SVGDocumentWrapper::DestroyViewer() {
55 if (mViewer) {
56 mViewer->GetDocument()->OnPageHide(false, nullptr);
57 mViewer->Close(nullptr);
58 mViewer->Destroy();
59 mViewer = nullptr;
63 nsIFrame* SVGDocumentWrapper::GetRootLayoutFrame() {
64 Element* rootElem = GetRootSVGElem();
65 return rootElem ? rootElem->GetPrimaryFrame() : nullptr;
68 void SVGDocumentWrapper::UpdateViewportBounds(const nsIntSize& aViewportSize) {
69 MOZ_ASSERT(!mIgnoreInvalidation, "shouldn't be reentrant");
70 mIgnoreInvalidation = true;
72 nsIntRect currentBounds;
73 mViewer->GetBounds(currentBounds);
75 // If the bounds have changed, we need to do a layout flush.
76 if (currentBounds.Size() != aViewportSize) {
77 mViewer->SetBounds(IntRect(IntPoint(0, 0), aViewportSize));
78 FlushLayout();
81 mIgnoreInvalidation = false;
84 void SVGDocumentWrapper::FlushImageTransformInvalidation() {
85 MOZ_ASSERT(!mIgnoreInvalidation, "shouldn't be reentrant");
87 SVGSVGElement* svgElem = GetRootSVGElem();
88 if (!svgElem) {
89 return;
92 mIgnoreInvalidation = true;
93 svgElem->FlushImageTransformInvalidation();
94 FlushLayout();
95 mIgnoreInvalidation = false;
98 bool SVGDocumentWrapper::IsAnimated() {
99 // Can be called for animated images during shutdown, after we've
100 // already Observe()'d XPCOM shutdown and cleared out our mViewer pointer.
101 if (!mViewer) {
102 return false;
105 Document* doc = mViewer->GetDocument();
106 if (!doc) {
107 return false;
109 if (doc->Timeline()->HasAnimations()) {
110 // CSS animations (technically HasAnimations() also checks for CSS
111 // transitions and Web animations but since SVG-as-an-image doesn't run
112 // script they will never run in the document that we wrap).
113 return true;
115 if (doc->HasAnimationController() &&
116 doc->GetAnimationController()->HasRegisteredAnimations()) {
117 // SMIL animations
118 return true;
120 return false;
123 void SVGDocumentWrapper::StartAnimation() {
124 // Can be called for animated images during shutdown, after we've
125 // already Observe()'d XPCOM shutdown and cleared out our mViewer pointer.
126 if (!mViewer) {
127 return;
130 Document* doc = mViewer->GetDocument();
131 if (doc) {
132 SMILAnimationController* controller = doc->GetAnimationController();
133 if (controller) {
134 controller->Resume(SMILTimeContainer::PAUSE_IMAGE);
136 doc->ImageTracker()->SetAnimatingState(true);
140 void SVGDocumentWrapper::StopAnimation() {
141 // Can be called for animated images during shutdown, after we've
142 // already Observe()'d XPCOM shutdown and cleared out our mViewer pointer.
143 if (!mViewer) {
144 return;
147 Document* doc = mViewer->GetDocument();
148 if (doc) {
149 SMILAnimationController* controller = doc->GetAnimationController();
150 if (controller) {
151 controller->Pause(SMILTimeContainer::PAUSE_IMAGE);
153 doc->ImageTracker()->SetAnimatingState(false);
157 void SVGDocumentWrapper::ResetAnimation() {
158 SVGSVGElement* svgElem = GetRootSVGElem();
159 if (!svgElem) {
160 return;
163 svgElem->SetCurrentTime(0.0f);
166 float SVGDocumentWrapper::GetCurrentTimeAsFloat() {
167 SVGSVGElement* svgElem = GetRootSVGElem();
168 return svgElem ? svgElem->GetCurrentTimeAsFloat() : 0.0f;
171 void SVGDocumentWrapper::SetCurrentTime(float aTime) {
172 SVGSVGElement* svgElem = GetRootSVGElem();
173 if (svgElem && svgElem->GetCurrentTimeAsFloat() != aTime) {
174 svgElem->SetCurrentTime(aTime);
178 void SVGDocumentWrapper::TickRefreshDriver() {
179 if (RefPtr<PresShell> presShell = mViewer->GetPresShell()) {
180 if (RefPtr<nsPresContext> presContext = presShell->GetPresContext()) {
181 presContext->RefreshDriver()->DoTick();
186 /** nsIStreamListener methods **/
188 NS_IMETHODIMP
189 SVGDocumentWrapper::OnDataAvailable(nsIRequest* aRequest, nsIInputStream* inStr,
190 uint64_t sourceOffset, uint32_t count) {
191 return mListener->OnDataAvailable(aRequest, inStr, sourceOffset, count);
194 /** nsIRequestObserver methods **/
196 NS_IMETHODIMP
197 SVGDocumentWrapper::OnStartRequest(nsIRequest* aRequest) {
198 nsresult rv = SetupViewer(aRequest, getter_AddRefs(mViewer),
199 getter_AddRefs(mLoadGroup));
201 if (NS_SUCCEEDED(rv) && NS_SUCCEEDED(mListener->OnStartRequest(aRequest))) {
202 mViewer->GetDocument()->SetIsBeingUsedAsImage();
203 StopAnimation(); // otherwise animations start automatically in helper doc
205 rv = mViewer->Init(nullptr, nsIntRect(0, 0, 0, 0), nullptr);
206 if (NS_SUCCEEDED(rv)) {
207 rv = mViewer->Open(nullptr, nullptr);
210 return rv;
213 NS_IMETHODIMP
214 SVGDocumentWrapper::OnStopRequest(nsIRequest* aRequest, nsresult status) {
215 if (mListener) {
216 mListener->OnStopRequest(aRequest, status);
217 mListener = nullptr;
220 return NS_OK;
223 /** nsIObserver Methods **/
224 NS_IMETHODIMP
225 SVGDocumentWrapper::Observe(nsISupports* aSubject, const char* aTopic,
226 const char16_t* aData) {
227 if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
228 // Sever ties from rendering observers to helper-doc's root SVG node
229 SVGSVGElement* svgElem = GetRootSVGElem();
230 if (svgElem) {
231 SVGObserverUtils::RemoveAllRenderingObservers(svgElem);
234 // Clean up at XPCOM shutdown time.
235 DestroyViewer();
236 if (mListener) {
237 mListener = nullptr;
239 if (mLoadGroup) {
240 mLoadGroup = nullptr;
243 // Turn off "registered" flag, or else we'll try to unregister when we die.
244 // (No need for that now, and the try would fail anyway -- it's too late.)
245 mRegisteredForXPCOMShutdown = false;
246 } else {
247 NS_ERROR("Unexpected observer topic.");
249 return NS_OK;
252 /** Private helper methods **/
254 // This method is largely cribbed from
255 // nsExternalResourceMap::PendingLoad::SetupViewer.
256 nsresult SVGDocumentWrapper::SetupViewer(nsIRequest* aRequest,
257 nsIContentViewer** aViewer,
258 nsILoadGroup** aLoadGroup) {
259 nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
260 NS_ENSURE_TRUE(chan, NS_ERROR_UNEXPECTED);
262 // Check for HTTP error page
263 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequest));
264 if (httpChannel) {
265 bool requestSucceeded;
266 if (NS_FAILED(httpChannel->GetRequestSucceeded(&requestSucceeded)) ||
267 !requestSucceeded) {
268 return NS_ERROR_FAILURE;
272 // Give this document its own loadgroup
273 nsCOMPtr<nsILoadGroup> loadGroup;
274 chan->GetLoadGroup(getter_AddRefs(loadGroup));
276 nsCOMPtr<nsILoadGroup> newLoadGroup =
277 do_CreateInstance(NS_LOADGROUP_CONTRACTID);
278 NS_ENSURE_TRUE(newLoadGroup, NS_ERROR_OUT_OF_MEMORY);
279 newLoadGroup->SetLoadGroup(loadGroup);
281 nsCOMPtr<nsICategoryManager> catMan =
282 do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
283 NS_ENSURE_TRUE(catMan, NS_ERROR_NOT_AVAILABLE);
284 nsCString contractId;
285 nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", IMAGE_SVG_XML,
286 contractId);
287 NS_ENSURE_SUCCESS(rv, rv);
288 nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory =
289 do_GetService(contractId.get());
290 NS_ENSURE_TRUE(docLoaderFactory, NS_ERROR_NOT_AVAILABLE);
292 nsCOMPtr<nsIContentViewer> viewer;
293 nsCOMPtr<nsIStreamListener> listener;
294 rv = docLoaderFactory->CreateInstance(
295 "external-resource", chan, newLoadGroup, nsLiteralCString(IMAGE_SVG_XML),
296 nullptr, nullptr, getter_AddRefs(listener), getter_AddRefs(viewer));
297 NS_ENSURE_SUCCESS(rv, rv);
299 NS_ENSURE_TRUE(viewer, NS_ERROR_UNEXPECTED);
301 // Create a navigation time object and pass it to the SVG document through
302 // the viewer.
303 // The timeline(DocumentTimeline, used in CSS animation) of this SVG
304 // document needs this navigation timing object for time computation, such
305 // as to calculate current time stamp based on the start time of navigation
306 // time object.
308 // For a root document, DocShell would do these sort of things
309 // automatically. Since there is no DocShell for this wrapped SVG document,
310 // we must set it up manually.
311 RefPtr<nsDOMNavigationTiming> timing = new nsDOMNavigationTiming(nullptr);
312 timing->NotifyNavigationStart(
313 nsDOMNavigationTiming::DocShellState::eInactive);
314 viewer->SetNavigationTiming(timing);
316 nsCOMPtr<nsIParser> parser = do_QueryInterface(listener);
317 NS_ENSURE_TRUE(parser, NS_ERROR_UNEXPECTED);
319 // XML-only, because this is for SVG content
320 nsCOMPtr<nsIContentSink> sink = parser->GetContentSink();
321 NS_ENSURE_TRUE(sink, NS_ERROR_UNEXPECTED);
323 listener.swap(mListener);
324 viewer.forget(aViewer);
325 newLoadGroup.forget(aLoadGroup);
327 RegisterForXPCOMShutdown();
328 return NS_OK;
331 void SVGDocumentWrapper::RegisterForXPCOMShutdown() {
332 MOZ_ASSERT(!mRegisteredForXPCOMShutdown, "re-registering for XPCOM shutdown");
333 // Listen for xpcom-shutdown so that we can drop references to our
334 // helper-document at that point. (Otherwise, we won't get cleaned up
335 // until imgLoader::Shutdown, which can happen after the JAR service
336 // and RDF service have been unregistered.)
337 nsresult rv;
338 nsCOMPtr<nsIObserverService> obsSvc = do_GetService(OBSERVER_SVC_CID, &rv);
339 if (NS_FAILED(rv) || NS_FAILED(obsSvc->AddObserver(
340 this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, true))) {
341 NS_WARNING("Failed to register as observer of XPCOM shutdown");
342 } else {
343 mRegisteredForXPCOMShutdown = true;
347 void SVGDocumentWrapper::UnregisterForXPCOMShutdown() {
348 MOZ_ASSERT(mRegisteredForXPCOMShutdown,
349 "unregistering for XPCOM shutdown w/out being registered");
351 nsresult rv;
352 nsCOMPtr<nsIObserverService> obsSvc = do_GetService(OBSERVER_SVC_CID, &rv);
353 if (NS_FAILED(rv) ||
354 NS_FAILED(obsSvc->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID))) {
355 NS_WARNING("Failed to unregister as observer of XPCOM shutdown");
356 } else {
357 mRegisteredForXPCOMShutdown = false;
361 void SVGDocumentWrapper::FlushLayout() {
362 if (SVGDocument* doc = GetDocument()) {
363 doc->FlushPendingNotifications(FlushType::Layout);
367 SVGDocument* SVGDocumentWrapper::GetDocument() {
368 if (!mViewer) {
369 return nullptr;
371 Document* doc = mViewer->GetDocument();
372 if (!doc) {
373 return nullptr;
375 return doc->AsSVGDocument();
378 SVGSVGElement* SVGDocumentWrapper::GetRootSVGElem() {
379 if (!mViewer) {
380 return nullptr; // Can happen during destruction
383 Document* doc = mViewer->GetDocument();
384 if (!doc) {
385 return nullptr; // Can happen during destruction
388 Element* rootElem = mViewer->GetDocument()->GetRootElement();
389 if (!rootElem || !rootElem->IsSVGElement(nsGkAtoms::svg)) {
390 return nullptr;
393 return static_cast<SVGSVGElement*>(rootElem);
396 } // namespace image
397 } // namespace mozilla