Backed out changeset b71c8c052463 (bug 1943846) for causing mass failures. CLOSED...
[gecko.git] / image / SVGDocumentWrapper.cpp
blobb2f9f6c26b98630b4a706efd9e83b412126bf719
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 "nsIDocumentViewer.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),
46 mRegisteredForXPCOMShutdown(false),
47 mIsDrawing(false) {}
49 SVGDocumentWrapper::~SVGDocumentWrapper() {
50 DestroyViewer();
51 if (mRegisteredForXPCOMShutdown) {
52 UnregisterForXPCOMShutdown();
56 void SVGDocumentWrapper::DestroyViewer() {
57 MOZ_ASSERT(NS_IsMainThread());
58 if (mViewer) {
59 mViewer->GetDocument()->OnPageHide(false, nullptr);
60 mViewer->Close(nullptr);
61 mViewer->Destroy();
62 mViewer = nullptr;
66 nsIFrame* SVGDocumentWrapper::GetRootLayoutFrame() {
67 Element* rootElem = GetRootSVGElem();
68 return rootElem ? rootElem->GetPrimaryFrame() : nullptr;
71 void SVGDocumentWrapper::UpdateViewportBounds(const nsIntSize& aViewportSize) {
72 MOZ_ASSERT(!mIgnoreInvalidation, "shouldn't be reentrant");
73 mIgnoreInvalidation = true;
75 LayoutDeviceIntRect currentBounds;
76 mViewer->GetBounds(currentBounds);
78 // If the bounds have changed, we need to do a layout flush.
79 if (currentBounds.Size().ToUnknownSize() != aViewportSize) {
80 mViewer->SetBounds(LayoutDeviceIntRect(
81 LayoutDeviceIntPoint(),
82 LayoutDeviceIntSize::FromUnknownSize(aViewportSize)));
83 FlushLayout();
86 mIgnoreInvalidation = false;
89 void SVGDocumentWrapper::FlushImageTransformInvalidation() {
90 MOZ_ASSERT(!mIgnoreInvalidation, "shouldn't be reentrant");
92 SVGSVGElement* svgElem = GetRootSVGElem();
93 if (!svgElem) {
94 return;
97 mIgnoreInvalidation = true;
98 svgElem->FlushImageTransformInvalidation();
99 FlushLayout();
100 mIgnoreInvalidation = false;
103 bool SVGDocumentWrapper::IsAnimated() {
104 // Can be called for animated images during shutdown, after we've
105 // already Observe()'d XPCOM shutdown and cleared out our mViewer pointer.
106 if (!mViewer) {
107 return false;
110 Document* doc = mViewer->GetDocument();
111 if (!doc) {
112 return false;
114 if (doc->Timeline()->HasAnimations()) {
115 // CSS animations (technically HasAnimations() also checks for CSS
116 // transitions and Web animations but since SVG-as-an-image doesn't run
117 // script they will never run in the document that we wrap).
118 return true;
120 if (doc->HasAnimationController() &&
121 doc->GetAnimationController()->HasRegisteredAnimations()) {
122 // SMIL animations
123 return true;
125 return false;
128 void SVGDocumentWrapper::StartAnimation() {
129 // Can be called for animated images during shutdown, after we've
130 // already Observe()'d XPCOM shutdown and cleared out our mViewer pointer.
131 if (!mViewer) {
132 return;
135 Document* doc = mViewer->GetDocument();
136 if (doc) {
137 SMILAnimationController* controller = doc->GetAnimationController();
138 if (controller) {
139 controller->Resume(SMILTimeContainer::PAUSE_IMAGE);
141 doc->ImageTracker()->SetAnimatingState(true);
145 void SVGDocumentWrapper::StopAnimation() {
146 // Can be called for animated images during shutdown, after we've
147 // already Observe()'d XPCOM shutdown and cleared out our mViewer pointer.
148 if (!mViewer) {
149 return;
152 Document* doc = mViewer->GetDocument();
153 if (doc) {
154 SMILAnimationController* controller = doc->GetAnimationController();
155 if (controller) {
156 controller->Pause(SMILTimeContainer::PAUSE_IMAGE);
158 doc->ImageTracker()->SetAnimatingState(false);
162 void SVGDocumentWrapper::ResetAnimation() {
163 SVGSVGElement* svgElem = GetRootSVGElem();
164 if (!svgElem) {
165 return;
168 svgElem->SetCurrentTime(0.0f);
171 float SVGDocumentWrapper::GetCurrentTimeAsFloat() {
172 SVGSVGElement* svgElem = GetRootSVGElem();
173 return svgElem ? svgElem->GetCurrentTimeAsFloat() : 0.0f;
176 void SVGDocumentWrapper::SetCurrentTime(float aTime) {
177 SVGSVGElement* svgElem = GetRootSVGElem();
178 if (svgElem && svgElem->GetCurrentTimeAsFloat() != aTime) {
179 svgElem->SetCurrentTime(aTime);
183 void SVGDocumentWrapper::TickRefreshDriver() {
184 if (RefPtr<PresShell> presShell = mViewer->GetPresShell()) {
185 if (RefPtr<nsPresContext> presContext = presShell->GetPresContext()) {
186 if (RefPtr<nsRefreshDriver> driver = presContext->RefreshDriver()) {
187 driver->DoTick();
193 /** nsIStreamListener methods **/
195 NS_IMETHODIMP
196 SVGDocumentWrapper::OnDataAvailable(nsIRequest* aRequest, nsIInputStream* inStr,
197 uint64_t sourceOffset, uint32_t count) {
198 return mListener->OnDataAvailable(aRequest, inStr, sourceOffset, count);
201 /** nsIRequestObserver methods **/
203 NS_IMETHODIMP
204 SVGDocumentWrapper::OnStartRequest(nsIRequest* aRequest) {
205 nsresult rv = SetupViewer(aRequest, getter_AddRefs(mViewer),
206 getter_AddRefs(mLoadGroup));
208 if (NS_SUCCEEDED(rv) && NS_SUCCEEDED(mListener->OnStartRequest(aRequest))) {
209 mViewer->GetDocument()->SetIsBeingUsedAsImage();
210 StopAnimation(); // otherwise animations start automatically in helper doc
212 rv = mViewer->Init(nullptr, LayoutDeviceIntRect(), nullptr);
213 if (NS_SUCCEEDED(rv)) {
214 rv = mViewer->Open(nullptr, nullptr);
217 return rv;
220 NS_IMETHODIMP
221 SVGDocumentWrapper::OnStopRequest(nsIRequest* aRequest, nsresult status) {
222 if (mListener) {
223 mListener->OnStopRequest(aRequest, status);
224 mListener = nullptr;
227 return NS_OK;
230 /** nsIObserver Methods **/
231 NS_IMETHODIMP
232 SVGDocumentWrapper::Observe(nsISupports* aSubject, const char* aTopic,
233 const char16_t* aData) {
234 if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
235 // Sever ties from rendering observers to helper-doc's root SVG node
236 SVGSVGElement* svgElem = GetRootSVGElem();
237 if (svgElem) {
238 SVGObserverUtils::RemoveAllRenderingObservers(svgElem);
241 // Clean up at XPCOM shutdown time.
242 DestroyViewer();
243 if (mListener) {
244 mListener = nullptr;
246 if (mLoadGroup) {
247 mLoadGroup = nullptr;
250 // Turn off "registered" flag, or else we'll try to unregister when we die.
251 // (No need for that now, and the try would fail anyway -- it's too late.)
252 mRegisteredForXPCOMShutdown = false;
253 } else {
254 NS_ERROR("Unexpected observer topic.");
256 return NS_OK;
259 /** Private helper methods **/
261 // This method is largely cribbed from
262 // nsExternalResourceMap::PendingLoad::SetupViewer.
263 nsresult SVGDocumentWrapper::SetupViewer(nsIRequest* aRequest,
264 nsIDocumentViewer** aViewer,
265 nsILoadGroup** aLoadGroup) {
266 nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
267 NS_ENSURE_TRUE(chan, NS_ERROR_UNEXPECTED);
269 // Check for HTTP error page
270 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequest));
271 if (httpChannel) {
272 bool requestSucceeded;
273 if (NS_FAILED(httpChannel->GetRequestSucceeded(&requestSucceeded)) ||
274 !requestSucceeded) {
275 return NS_ERROR_FAILURE;
279 // Give this document its own loadgroup
280 nsCOMPtr<nsILoadGroup> loadGroup;
281 chan->GetLoadGroup(getter_AddRefs(loadGroup));
283 nsCOMPtr<nsILoadGroup> newLoadGroup =
284 do_CreateInstance(NS_LOADGROUP_CONTRACTID);
285 NS_ENSURE_TRUE(newLoadGroup, NS_ERROR_OUT_OF_MEMORY);
286 newLoadGroup->SetLoadGroup(loadGroup);
288 nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory =
289 nsContentUtils::FindInternalDocumentViewer(
290 nsLiteralCString(IMAGE_SVG_XML));
291 NS_ENSURE_TRUE(docLoaderFactory, NS_ERROR_NOT_AVAILABLE);
293 nsCOMPtr<nsIDocumentViewer> viewer;
294 nsCOMPtr<nsIStreamListener> listener;
295 nsresult rv = docLoaderFactory->CreateInstance(
296 "external-resource", chan, newLoadGroup, nsLiteralCString(IMAGE_SVG_XML),
297 nullptr, nullptr, getter_AddRefs(listener), getter_AddRefs(viewer));
298 NS_ENSURE_SUCCESS(rv, rv);
300 NS_ENSURE_TRUE(viewer, NS_ERROR_UNEXPECTED);
302 // Create a navigation time object and pass it to the SVG document through
303 // the viewer.
304 // The timeline(DocumentTimeline, used in CSS animation) of this SVG
305 // document needs this navigation timing object for time computation, such
306 // as to calculate current time stamp based on the start time of navigation
307 // time object.
309 // For a root document, DocShell would do these sort of things
310 // automatically. Since there is no DocShell for this wrapped SVG document,
311 // we must set it up manually.
312 RefPtr<nsDOMNavigationTiming> timing = new nsDOMNavigationTiming(nullptr);
313 timing->NotifyNavigationStart(
314 nsDOMNavigationTiming::DocShellState::eInactive);
315 viewer->SetNavigationTiming(timing);
317 nsCOMPtr<nsIParser> parser = do_QueryInterface(listener);
318 NS_ENSURE_TRUE(parser, NS_ERROR_UNEXPECTED);
320 // XML-only, because this is for SVG content
321 nsCOMPtr<nsIContentSink> sink = parser->GetContentSink();
322 NS_ENSURE_TRUE(sink, NS_ERROR_UNEXPECTED);
324 listener.swap(mListener);
325 viewer.forget(aViewer);
326 newLoadGroup.forget(aLoadGroup);
328 RegisterForXPCOMShutdown();
329 return NS_OK;
332 void SVGDocumentWrapper::RegisterForXPCOMShutdown() {
333 MOZ_ASSERT(!mRegisteredForXPCOMShutdown, "re-registering for XPCOM shutdown");
334 // Listen for xpcom-shutdown so that we can drop references to our
335 // helper-document at that point. (Otherwise, we won't get cleaned up
336 // until imgLoader::Shutdown, which can happen after the JAR service
337 // and RDF service have been unregistered.)
338 nsresult rv;
339 nsCOMPtr<nsIObserverService> obsSvc = do_GetService(OBSERVER_SVC_CID, &rv);
340 if (NS_FAILED(rv) || NS_FAILED(obsSvc->AddObserver(
341 this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, true))) {
342 NS_WARNING("Failed to register as observer of XPCOM shutdown");
343 } else {
344 mRegisteredForXPCOMShutdown = true;
348 void SVGDocumentWrapper::UnregisterForXPCOMShutdown() {
349 MOZ_ASSERT(mRegisteredForXPCOMShutdown,
350 "unregistering for XPCOM shutdown w/out being registered");
352 nsresult rv;
353 nsCOMPtr<nsIObserverService> obsSvc = do_GetService(OBSERVER_SVC_CID, &rv);
354 if (NS_FAILED(rv) ||
355 NS_FAILED(obsSvc->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID))) {
356 NS_WARNING("Failed to unregister as observer of XPCOM shutdown");
357 } else {
358 mRegisteredForXPCOMShutdown = false;
362 void SVGDocumentWrapper::FlushLayout() {
363 if (SVGDocument* doc = GetDocument()) {
364 doc->FlushPendingNotifications(FlushType::Layout);
368 SVGDocument* SVGDocumentWrapper::GetDocument() {
369 if (!mViewer) {
370 return nullptr;
372 Document* doc = mViewer->GetDocument();
373 if (!doc) {
374 return nullptr;
376 return doc->AsSVGDocument();
379 SVGSVGElement* SVGDocumentWrapper::GetRootSVGElem() {
380 if (!mViewer) {
381 return nullptr; // Can happen during destruction
384 Document* doc = mViewer->GetDocument();
385 if (!doc) {
386 return nullptr; // Can happen during destruction
389 Element* rootElem = mViewer->GetDocument()->GetRootElement();
390 if (!rootElem || !rootElem->IsSVGElement(nsGkAtoms::svg)) {
391 return nullptr;
394 return static_cast<SVGSVGElement*>(rootElem);
397 } // namespace image
398 } // namespace mozilla