Bug 1682766 [wpt PR 26921] - Fix nullptr dereference accessing PolicyContainer in...
[gecko.git] / image / SVGDocumentWrapper.cpp
blob8dc3f0814fd7399ae1d6f8e1d76a5bb3b0936e33
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/Document.h"
12 #include "mozilla/dom/DocumentTimeline.h"
13 #include "mozilla/dom/Element.h"
14 #include "mozilla/dom/ImageTracker.h"
15 #include "mozilla/dom/SVGDocument.h"
16 #include "mozilla/dom/SVGSVGElement.h"
17 #include "nsICategoryManager.h"
18 #include "nsIChannel.h"
19 #include "nsIContentViewer.h"
20 #include "nsIDocumentLoaderFactory.h"
21 #include "nsIHttpChannel.h"
22 #include "nsIObserverService.h"
23 #include "nsIParser.h"
24 #include "nsIRequest.h"
25 #include "nsIStreamListener.h"
26 #include "nsIXMLContentSink.h"
27 #include "nsNetCID.h"
28 #include "nsComponentManagerUtils.h"
29 #include "nsServiceManagerUtils.h"
30 #include "nsMimeTypes.h"
31 #include "nsRefreshDriver.h"
33 namespace mozilla {
35 using namespace dom;
36 using namespace gfx;
38 namespace image {
40 NS_IMPL_ISUPPORTS(SVGDocumentWrapper, nsIStreamListener, nsIRequestObserver,
41 nsIObserver, nsISupportsWeakReference)
43 SVGDocumentWrapper::SVGDocumentWrapper()
44 : mIgnoreInvalidation(false), mRegisteredForXPCOMShutdown(false) {}
46 SVGDocumentWrapper::~SVGDocumentWrapper() {
47 DestroyViewer();
48 if (mRegisteredForXPCOMShutdown) {
49 UnregisterForXPCOMShutdown();
53 void SVGDocumentWrapper::DestroyViewer() {
54 if (mViewer) {
55 mViewer->GetDocument()->OnPageHide(false, nullptr);
56 mViewer->Close(nullptr);
57 mViewer->Destroy();
58 mViewer = nullptr;
62 nsIFrame* SVGDocumentWrapper::GetRootLayoutFrame() {
63 Element* rootElem = GetRootSVGElem();
64 return rootElem ? rootElem->GetPrimaryFrame() : nullptr;
67 void SVGDocumentWrapper::UpdateViewportBounds(const nsIntSize& aViewportSize) {
68 MOZ_ASSERT(!mIgnoreInvalidation, "shouldn't be reentrant");
69 mIgnoreInvalidation = true;
71 nsIntRect currentBounds;
72 mViewer->GetBounds(currentBounds);
74 // If the bounds have changed, we need to do a layout flush.
75 if (currentBounds.Size() != aViewportSize) {
76 mViewer->SetBounds(IntRect(IntPoint(0, 0), aViewportSize));
77 FlushLayout();
80 mIgnoreInvalidation = false;
83 void SVGDocumentWrapper::FlushImageTransformInvalidation() {
84 MOZ_ASSERT(!mIgnoreInvalidation, "shouldn't be reentrant");
86 SVGSVGElement* svgElem = GetRootSVGElem();
87 if (!svgElem) {
88 return;
91 mIgnoreInvalidation = true;
92 svgElem->FlushImageTransformInvalidation();
93 FlushLayout();
94 mIgnoreInvalidation = false;
97 bool SVGDocumentWrapper::IsAnimated() {
98 // Can be called for animated images during shutdown, after we've
99 // already Observe()'d XPCOM shutdown and cleared out our mViewer pointer.
100 if (!mViewer) {
101 return false;
104 Document* doc = mViewer->GetDocument();
105 if (!doc) {
106 return false;
108 if (doc->Timeline()->HasAnimations()) {
109 // CSS animations (technically HasAnimations() also checks for CSS
110 // transitions and Web animations but since SVG-as-an-image doesn't run
111 // script they will never run in the document that we wrap).
112 return true;
114 if (doc->HasAnimationController() &&
115 doc->GetAnimationController()->HasRegisteredAnimations()) {
116 // SMIL animations
117 return true;
119 return false;
122 void SVGDocumentWrapper::StartAnimation() {
123 // Can be called for animated images during shutdown, after we've
124 // already Observe()'d XPCOM shutdown and cleared out our mViewer pointer.
125 if (!mViewer) {
126 return;
129 Document* doc = mViewer->GetDocument();
130 if (doc) {
131 SMILAnimationController* controller = doc->GetAnimationController();
132 if (controller) {
133 controller->Resume(SMILTimeContainer::PAUSE_IMAGE);
135 doc->ImageTracker()->SetAnimatingState(true);
139 void SVGDocumentWrapper::StopAnimation() {
140 // Can be called for animated images during shutdown, after we've
141 // already Observe()'d XPCOM shutdown and cleared out our mViewer pointer.
142 if (!mViewer) {
143 return;
146 Document* doc = mViewer->GetDocument();
147 if (doc) {
148 SMILAnimationController* controller = doc->GetAnimationController();
149 if (controller) {
150 controller->Pause(SMILTimeContainer::PAUSE_IMAGE);
152 doc->ImageTracker()->SetAnimatingState(false);
156 void SVGDocumentWrapper::ResetAnimation() {
157 SVGSVGElement* svgElem = GetRootSVGElem();
158 if (!svgElem) {
159 return;
162 svgElem->SetCurrentTime(0.0f);
165 float SVGDocumentWrapper::GetCurrentTimeAsFloat() {
166 SVGSVGElement* svgElem = GetRootSVGElem();
167 return svgElem ? svgElem->GetCurrentTimeAsFloat() : 0.0f;
170 void SVGDocumentWrapper::SetCurrentTime(float aTime) {
171 SVGSVGElement* svgElem = GetRootSVGElem();
172 if (svgElem && svgElem->GetCurrentTimeAsFloat() != aTime) {
173 svgElem->SetCurrentTime(aTime);
177 void SVGDocumentWrapper::TickRefreshDriver() {
178 if (RefPtr<PresShell> presShell = mViewer->GetPresShell()) {
179 if (RefPtr<nsPresContext> presContext = presShell->GetPresContext()) {
180 presContext->RefreshDriver()->DoTick();
185 /** nsIStreamListener methods **/
187 NS_IMETHODIMP
188 SVGDocumentWrapper::OnDataAvailable(nsIRequest* aRequest, nsIInputStream* inStr,
189 uint64_t sourceOffset, uint32_t count) {
190 return mListener->OnDataAvailable(aRequest, inStr, sourceOffset, count);
193 /** nsIRequestObserver methods **/
195 NS_IMETHODIMP
196 SVGDocumentWrapper::OnStartRequest(nsIRequest* aRequest) {
197 nsresult rv = SetupViewer(aRequest, getter_AddRefs(mViewer),
198 getter_AddRefs(mLoadGroup));
200 if (NS_SUCCEEDED(rv) && NS_SUCCEEDED(mListener->OnStartRequest(aRequest))) {
201 mViewer->GetDocument()->SetIsBeingUsedAsImage();
202 StopAnimation(); // otherwise animations start automatically in helper doc
204 rv = mViewer->Init(nullptr, nsIntRect(0, 0, 0, 0), nullptr);
205 if (NS_SUCCEEDED(rv)) {
206 rv = mViewer->Open(nullptr, nullptr);
209 return rv;
212 NS_IMETHODIMP
213 SVGDocumentWrapper::OnStopRequest(nsIRequest* aRequest, nsresult status) {
214 if (mListener) {
215 mListener->OnStopRequest(aRequest, status);
216 mListener = nullptr;
219 return NS_OK;
222 /** nsIObserver Methods **/
223 NS_IMETHODIMP
224 SVGDocumentWrapper::Observe(nsISupports* aSubject, const char* aTopic,
225 const char16_t* aData) {
226 if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
227 // Sever ties from rendering observers to helper-doc's root SVG node
228 SVGSVGElement* svgElem = GetRootSVGElem();
229 if (svgElem) {
230 SVGObserverUtils::RemoveAllRenderingObservers(svgElem);
233 // Clean up at XPCOM shutdown time.
234 DestroyViewer();
235 if (mListener) {
236 mListener = nullptr;
238 if (mLoadGroup) {
239 mLoadGroup = nullptr;
242 // Turn off "registered" flag, or else we'll try to unregister when we die.
243 // (No need for that now, and the try would fail anyway -- it's too late.)
244 mRegisteredForXPCOMShutdown = false;
245 } else {
246 NS_ERROR("Unexpected observer topic.");
248 return NS_OK;
251 /** Private helper methods **/
253 // This method is largely cribbed from
254 // nsExternalResourceMap::PendingLoad::SetupViewer.
255 nsresult SVGDocumentWrapper::SetupViewer(nsIRequest* aRequest,
256 nsIContentViewer** aViewer,
257 nsILoadGroup** aLoadGroup) {
258 nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
259 NS_ENSURE_TRUE(chan, NS_ERROR_UNEXPECTED);
261 // Check for HTTP error page
262 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequest));
263 if (httpChannel) {
264 bool requestSucceeded;
265 if (NS_FAILED(httpChannel->GetRequestSucceeded(&requestSucceeded)) ||
266 !requestSucceeded) {
267 return NS_ERROR_FAILURE;
271 // Give this document its own loadgroup
272 nsCOMPtr<nsILoadGroup> loadGroup;
273 chan->GetLoadGroup(getter_AddRefs(loadGroup));
275 nsCOMPtr<nsILoadGroup> newLoadGroup =
276 do_CreateInstance(NS_LOADGROUP_CONTRACTID);
277 NS_ENSURE_TRUE(newLoadGroup, NS_ERROR_OUT_OF_MEMORY);
278 newLoadGroup->SetLoadGroup(loadGroup);
280 nsCOMPtr<nsICategoryManager> catMan =
281 do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
282 NS_ENSURE_TRUE(catMan, NS_ERROR_NOT_AVAILABLE);
283 nsCString contractId;
284 nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", IMAGE_SVG_XML,
285 contractId);
286 NS_ENSURE_SUCCESS(rv, rv);
287 nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory =
288 do_GetService(contractId.get());
289 NS_ENSURE_TRUE(docLoaderFactory, NS_ERROR_NOT_AVAILABLE);
291 nsCOMPtr<nsIContentViewer> viewer;
292 nsCOMPtr<nsIStreamListener> listener;
293 rv = docLoaderFactory->CreateInstance(
294 "external-resource", chan, newLoadGroup, nsLiteralCString(IMAGE_SVG_XML),
295 nullptr, nullptr, getter_AddRefs(listener), getter_AddRefs(viewer));
296 NS_ENSURE_SUCCESS(rv, rv);
298 NS_ENSURE_TRUE(viewer, NS_ERROR_UNEXPECTED);
300 // Create a navigation time object and pass it to the SVG document through
301 // the viewer.
302 // The timeline(DocumentTimeline, used in CSS animation) of this SVG
303 // document needs this navigation timing object for time computation, such
304 // as to calculate current time stamp based on the start time of navigation
305 // time object.
307 // For a root document, DocShell would do these sort of things
308 // automatically. Since there is no DocShell for this wrapped SVG document,
309 // we must set it up manually.
310 RefPtr<nsDOMNavigationTiming> timing = new nsDOMNavigationTiming(nullptr);
311 timing->NotifyNavigationStart(
312 nsDOMNavigationTiming::DocShellState::eInactive);
313 viewer->SetNavigationTiming(timing);
315 nsCOMPtr<nsIParser> parser = do_QueryInterface(listener);
316 NS_ENSURE_TRUE(parser, NS_ERROR_UNEXPECTED);
318 // XML-only, because this is for SVG content
319 nsCOMPtr<nsIContentSink> sink = parser->GetContentSink();
320 NS_ENSURE_TRUE(sink, NS_ERROR_UNEXPECTED);
322 listener.swap(mListener);
323 viewer.forget(aViewer);
324 newLoadGroup.forget(aLoadGroup);
326 RegisterForXPCOMShutdown();
327 return NS_OK;
330 void SVGDocumentWrapper::RegisterForXPCOMShutdown() {
331 MOZ_ASSERT(!mRegisteredForXPCOMShutdown, "re-registering for XPCOM shutdown");
332 // Listen for xpcom-shutdown so that we can drop references to our
333 // helper-document at that point. (Otherwise, we won't get cleaned up
334 // until imgLoader::Shutdown, which can happen after the JAR service
335 // and RDF service have been unregistered.)
336 nsresult rv;
337 nsCOMPtr<nsIObserverService> obsSvc = do_GetService(OBSERVER_SVC_CID, &rv);
338 if (NS_FAILED(rv) || NS_FAILED(obsSvc->AddObserver(
339 this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, true))) {
340 NS_WARNING("Failed to register as observer of XPCOM shutdown");
341 } else {
342 mRegisteredForXPCOMShutdown = true;
346 void SVGDocumentWrapper::UnregisterForXPCOMShutdown() {
347 MOZ_ASSERT(mRegisteredForXPCOMShutdown,
348 "unregistering for XPCOM shutdown w/out being registered");
350 nsresult rv;
351 nsCOMPtr<nsIObserverService> obsSvc = do_GetService(OBSERVER_SVC_CID, &rv);
352 if (NS_FAILED(rv) ||
353 NS_FAILED(obsSvc->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID))) {
354 NS_WARNING("Failed to unregister as observer of XPCOM shutdown");
355 } else {
356 mRegisteredForXPCOMShutdown = false;
360 void SVGDocumentWrapper::FlushLayout() {
361 if (SVGDocument* doc = GetDocument()) {
362 doc->FlushPendingNotifications(FlushType::Layout);
366 SVGDocument* SVGDocumentWrapper::GetDocument() {
367 if (!mViewer) {
368 return nullptr;
370 Document* doc = mViewer->GetDocument();
371 if (!doc) {
372 return nullptr;
374 return doc->AsSVGDocument();
377 SVGSVGElement* SVGDocumentWrapper::GetRootSVGElem() {
378 if (!mViewer) {
379 return nullptr; // Can happen during destruction
382 Document* doc = mViewer->GetDocument();
383 if (!doc) {
384 return nullptr; // Can happen during destruction
387 Element* rootElem = mViewer->GetDocument()->GetRootElement();
388 if (!rootElem || !rootElem->IsSVGElement(nsGkAtoms::svg)) {
389 return nullptr;
392 return static_cast<SVGSVGElement*>(rootElem);
395 } // namespace image
396 } // namespace mozilla