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"
29 #include "nsComponentManagerUtils.h"
30 #include "nsServiceManagerUtils.h"
31 #include "nsMimeTypes.h"
32 #include "nsRefreshDriver.h"
41 NS_IMPL_ISUPPORTS(SVGDocumentWrapper
, nsIStreamListener
, nsIRequestObserver
,
42 nsIObserver
, nsISupportsWeakReference
)
44 SVGDocumentWrapper::SVGDocumentWrapper()
45 : mIgnoreInvalidation(false),
46 mRegisteredForXPCOMShutdown(false),
49 SVGDocumentWrapper::~SVGDocumentWrapper() {
51 if (mRegisteredForXPCOMShutdown
) {
52 UnregisterForXPCOMShutdown();
56 void SVGDocumentWrapper::DestroyViewer() {
58 mViewer
->GetDocument()->OnPageHide(false, nullptr);
59 mViewer
->Close(nullptr);
65 nsIFrame
* SVGDocumentWrapper::GetRootLayoutFrame() {
66 Element
* rootElem
= GetRootSVGElem();
67 return rootElem
? rootElem
->GetPrimaryFrame() : nullptr;
70 void SVGDocumentWrapper::UpdateViewportBounds(const nsIntSize
& aViewportSize
) {
71 MOZ_ASSERT(!mIgnoreInvalidation
, "shouldn't be reentrant");
72 mIgnoreInvalidation
= true;
74 nsIntRect currentBounds
;
75 mViewer
->GetBounds(currentBounds
);
77 // If the bounds have changed, we need to do a layout flush.
78 if (currentBounds
.Size() != aViewportSize
) {
79 mViewer
->SetBounds(IntRect(IntPoint(0, 0), aViewportSize
));
83 mIgnoreInvalidation
= false;
86 void SVGDocumentWrapper::FlushImageTransformInvalidation() {
87 MOZ_ASSERT(!mIgnoreInvalidation
, "shouldn't be reentrant");
89 SVGSVGElement
* svgElem
= GetRootSVGElem();
94 mIgnoreInvalidation
= true;
95 svgElem
->FlushImageTransformInvalidation();
97 mIgnoreInvalidation
= false;
100 bool SVGDocumentWrapper::IsAnimated() {
101 // Can be called for animated images during shutdown, after we've
102 // already Observe()'d XPCOM shutdown and cleared out our mViewer pointer.
107 Document
* doc
= mViewer
->GetDocument();
111 if (doc
->Timeline()->HasAnimations()) {
112 // CSS animations (technically HasAnimations() also checks for CSS
113 // transitions and Web animations but since SVG-as-an-image doesn't run
114 // script they will never run in the document that we wrap).
117 if (doc
->HasAnimationController() &&
118 doc
->GetAnimationController()->HasRegisteredAnimations()) {
125 void SVGDocumentWrapper::StartAnimation() {
126 // Can be called for animated images during shutdown, after we've
127 // already Observe()'d XPCOM shutdown and cleared out our mViewer pointer.
132 Document
* doc
= mViewer
->GetDocument();
134 SMILAnimationController
* controller
= doc
->GetAnimationController();
136 controller
->Resume(SMILTimeContainer::PAUSE_IMAGE
);
138 doc
->ImageTracker()->SetAnimatingState(true);
142 void SVGDocumentWrapper::StopAnimation() {
143 // Can be called for animated images during shutdown, after we've
144 // already Observe()'d XPCOM shutdown and cleared out our mViewer pointer.
149 Document
* doc
= mViewer
->GetDocument();
151 SMILAnimationController
* controller
= doc
->GetAnimationController();
153 controller
->Pause(SMILTimeContainer::PAUSE_IMAGE
);
155 doc
->ImageTracker()->SetAnimatingState(false);
159 void SVGDocumentWrapper::ResetAnimation() {
160 SVGSVGElement
* svgElem
= GetRootSVGElem();
165 svgElem
->SetCurrentTime(0.0f
);
168 float SVGDocumentWrapper::GetCurrentTimeAsFloat() {
169 SVGSVGElement
* svgElem
= GetRootSVGElem();
170 return svgElem
? svgElem
->GetCurrentTimeAsFloat() : 0.0f
;
173 void SVGDocumentWrapper::SetCurrentTime(float aTime
) {
174 SVGSVGElement
* svgElem
= GetRootSVGElem();
175 if (svgElem
&& svgElem
->GetCurrentTimeAsFloat() != aTime
) {
176 svgElem
->SetCurrentTime(aTime
);
180 void SVGDocumentWrapper::TickRefreshDriver() {
181 if (RefPtr
<PresShell
> presShell
= mViewer
->GetPresShell()) {
182 if (RefPtr
<nsPresContext
> presContext
= presShell
->GetPresContext()) {
183 if (RefPtr
<nsRefreshDriver
> driver
= presContext
->RefreshDriver()) {
190 /** nsIStreamListener methods **/
193 SVGDocumentWrapper::OnDataAvailable(nsIRequest
* aRequest
, nsIInputStream
* inStr
,
194 uint64_t sourceOffset
, uint32_t count
) {
195 return mListener
->OnDataAvailable(aRequest
, inStr
, sourceOffset
, count
);
198 /** nsIRequestObserver methods **/
201 SVGDocumentWrapper::OnStartRequest(nsIRequest
* aRequest
) {
202 nsresult rv
= SetupViewer(aRequest
, getter_AddRefs(mViewer
),
203 getter_AddRefs(mLoadGroup
));
205 if (NS_SUCCEEDED(rv
) && NS_SUCCEEDED(mListener
->OnStartRequest(aRequest
))) {
206 mViewer
->GetDocument()->SetIsBeingUsedAsImage();
207 StopAnimation(); // otherwise animations start automatically in helper doc
209 rv
= mViewer
->Init(nullptr, nsIntRect(0, 0, 0, 0), nullptr);
210 if (NS_SUCCEEDED(rv
)) {
211 rv
= mViewer
->Open(nullptr, nullptr);
218 SVGDocumentWrapper::OnStopRequest(nsIRequest
* aRequest
, nsresult status
) {
220 mListener
->OnStopRequest(aRequest
, status
);
227 /** nsIObserver Methods **/
229 SVGDocumentWrapper::Observe(nsISupports
* aSubject
, const char* aTopic
,
230 const char16_t
* aData
) {
231 if (!strcmp(aTopic
, NS_XPCOM_SHUTDOWN_OBSERVER_ID
)) {
232 // Sever ties from rendering observers to helper-doc's root SVG node
233 SVGSVGElement
* svgElem
= GetRootSVGElem();
235 SVGObserverUtils::RemoveAllRenderingObservers(svgElem
);
238 // Clean up at XPCOM shutdown time.
244 mLoadGroup
= nullptr;
247 // Turn off "registered" flag, or else we'll try to unregister when we die.
248 // (No need for that now, and the try would fail anyway -- it's too late.)
249 mRegisteredForXPCOMShutdown
= false;
251 NS_ERROR("Unexpected observer topic.");
256 /** Private helper methods **/
258 // This method is largely cribbed from
259 // nsExternalResourceMap::PendingLoad::SetupViewer.
260 nsresult
SVGDocumentWrapper::SetupViewer(nsIRequest
* aRequest
,
261 nsIDocumentViewer
** aViewer
,
262 nsILoadGroup
** aLoadGroup
) {
263 nsCOMPtr
<nsIChannel
> chan(do_QueryInterface(aRequest
));
264 NS_ENSURE_TRUE(chan
, NS_ERROR_UNEXPECTED
);
266 // Check for HTTP error page
267 nsCOMPtr
<nsIHttpChannel
> httpChannel(do_QueryInterface(aRequest
));
269 bool requestSucceeded
;
270 if (NS_FAILED(httpChannel
->GetRequestSucceeded(&requestSucceeded
)) ||
272 return NS_ERROR_FAILURE
;
276 // Give this document its own loadgroup
277 nsCOMPtr
<nsILoadGroup
> loadGroup
;
278 chan
->GetLoadGroup(getter_AddRefs(loadGroup
));
280 nsCOMPtr
<nsILoadGroup
> newLoadGroup
=
281 do_CreateInstance(NS_LOADGROUP_CONTRACTID
);
282 NS_ENSURE_TRUE(newLoadGroup
, NS_ERROR_OUT_OF_MEMORY
);
283 newLoadGroup
->SetLoadGroup(loadGroup
);
285 nsCOMPtr
<nsICategoryManager
> catMan
=
286 do_GetService(NS_CATEGORYMANAGER_CONTRACTID
);
287 NS_ENSURE_TRUE(catMan
, NS_ERROR_NOT_AVAILABLE
);
288 nsCString contractId
;
289 nsresult rv
= catMan
->GetCategoryEntry("Gecko-Content-Viewers", IMAGE_SVG_XML
,
291 NS_ENSURE_SUCCESS(rv
, rv
);
292 nsCOMPtr
<nsIDocumentLoaderFactory
> docLoaderFactory
=
293 do_GetService(contractId
.get());
294 NS_ENSURE_TRUE(docLoaderFactory
, NS_ERROR_NOT_AVAILABLE
);
296 nsCOMPtr
<nsIDocumentViewer
> viewer
;
297 nsCOMPtr
<nsIStreamListener
> listener
;
298 rv
= docLoaderFactory
->CreateInstance(
299 "external-resource", chan
, newLoadGroup
, nsLiteralCString(IMAGE_SVG_XML
),
300 nullptr, nullptr, getter_AddRefs(listener
), getter_AddRefs(viewer
));
301 NS_ENSURE_SUCCESS(rv
, rv
);
303 NS_ENSURE_TRUE(viewer
, NS_ERROR_UNEXPECTED
);
305 // Create a navigation time object and pass it to the SVG document through
307 // The timeline(DocumentTimeline, used in CSS animation) of this SVG
308 // document needs this navigation timing object for time computation, such
309 // as to calculate current time stamp based on the start time of navigation
312 // For a root document, DocShell would do these sort of things
313 // automatically. Since there is no DocShell for this wrapped SVG document,
314 // we must set it up manually.
315 RefPtr
<nsDOMNavigationTiming
> timing
= new nsDOMNavigationTiming(nullptr);
316 timing
->NotifyNavigationStart(
317 nsDOMNavigationTiming::DocShellState::eInactive
);
318 viewer
->SetNavigationTiming(timing
);
320 nsCOMPtr
<nsIParser
> parser
= do_QueryInterface(listener
);
321 NS_ENSURE_TRUE(parser
, NS_ERROR_UNEXPECTED
);
323 // XML-only, because this is for SVG content
324 nsCOMPtr
<nsIContentSink
> sink
= parser
->GetContentSink();
325 NS_ENSURE_TRUE(sink
, NS_ERROR_UNEXPECTED
);
327 listener
.swap(mListener
);
328 viewer
.forget(aViewer
);
329 newLoadGroup
.forget(aLoadGroup
);
331 RegisterForXPCOMShutdown();
335 void SVGDocumentWrapper::RegisterForXPCOMShutdown() {
336 MOZ_ASSERT(!mRegisteredForXPCOMShutdown
, "re-registering for XPCOM shutdown");
337 // Listen for xpcom-shutdown so that we can drop references to our
338 // helper-document at that point. (Otherwise, we won't get cleaned up
339 // until imgLoader::Shutdown, which can happen after the JAR service
340 // and RDF service have been unregistered.)
342 nsCOMPtr
<nsIObserverService
> obsSvc
= do_GetService(OBSERVER_SVC_CID
, &rv
);
343 if (NS_FAILED(rv
) || NS_FAILED(obsSvc
->AddObserver(
344 this, NS_XPCOM_SHUTDOWN_OBSERVER_ID
, true))) {
345 NS_WARNING("Failed to register as observer of XPCOM shutdown");
347 mRegisteredForXPCOMShutdown
= true;
351 void SVGDocumentWrapper::UnregisterForXPCOMShutdown() {
352 MOZ_ASSERT(mRegisteredForXPCOMShutdown
,
353 "unregistering for XPCOM shutdown w/out being registered");
356 nsCOMPtr
<nsIObserverService
> obsSvc
= do_GetService(OBSERVER_SVC_CID
, &rv
);
358 NS_FAILED(obsSvc
->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID
))) {
359 NS_WARNING("Failed to unregister as observer of XPCOM shutdown");
361 mRegisteredForXPCOMShutdown
= false;
365 void SVGDocumentWrapper::FlushLayout() {
366 if (SVGDocument
* doc
= GetDocument()) {
367 doc
->FlushPendingNotifications(FlushType::Layout
);
371 SVGDocument
* SVGDocumentWrapper::GetDocument() {
375 Document
* doc
= mViewer
->GetDocument();
379 return doc
->AsSVGDocument();
382 SVGSVGElement
* SVGDocumentWrapper::GetRootSVGElem() {
384 return nullptr; // Can happen during destruction
387 Document
* doc
= mViewer
->GetDocument();
389 return nullptr; // Can happen during destruction
392 Element
* rootElem
= mViewer
->GetDocument()->GetRootElement();
393 if (!rootElem
|| !rootElem
->IsSVGElement(nsGkAtoms::svg
)) {
397 return static_cast<SVGSVGElement
*>(rootElem
);
401 } // namespace mozilla