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/dom/DocumentTimeline.h"
9 #include "mozilla/dom/Element.h"
10 #include "mozilla/dom/SVGDocument.h"
11 #include "nsICategoryManager.h"
12 #include "nsIChannel.h"
13 #include "nsIContentViewer.h"
14 #include "nsIDocumentLoaderFactory.h"
15 #include "nsIHttpChannel.h"
16 #include "nsIObserverService.h"
17 #include "nsIParser.h"
18 #include "nsIPresShell.h"
19 #include "nsIRequest.h"
20 #include "nsIStreamListener.h"
21 #include "nsIXMLContentSink.h"
23 #include "nsComponentManagerUtils.h"
24 #include "nsSMILAnimationController.h"
25 #include "nsServiceManagerUtils.h"
26 #include "mozilla/dom/SVGSVGElement.h"
27 #include "SVGObserverUtils.h"
28 #include "mozilla/dom/SVGAnimatedLength.h"
29 #include "nsMimeTypes.h"
30 #include "DOMSVGLength.h"
31 #include "nsDocument.h"
32 #include "mozilla/dom/ImageTracker.h"
34 // undef the GetCurrentTime macro defined in WinBase.h from the MS Platform SDK
44 NS_IMPL_ISUPPORTS(SVGDocumentWrapper
,
48 nsISupportsWeakReference
)
50 SVGDocumentWrapper::SVGDocumentWrapper()
51 : mIgnoreInvalidation(false),
52 mRegisteredForXPCOMShutdown(false)
55 SVGDocumentWrapper::~SVGDocumentWrapper()
58 if (mRegisteredForXPCOMShutdown
) {
59 UnregisterForXPCOMShutdown();
64 SVGDocumentWrapper::DestroyViewer()
67 mViewer
->GetDocument()->OnPageHide(false, nullptr);
68 mViewer
->Close(nullptr);
75 SVGDocumentWrapper::GetRootLayoutFrame()
77 Element
* rootElem
= GetRootSVGElem();
78 return rootElem
? rootElem
->GetPrimaryFrame() : nullptr;
82 SVGDocumentWrapper::UpdateViewportBounds(const nsIntSize
& aViewportSize
)
84 MOZ_ASSERT(!mIgnoreInvalidation
, "shouldn't be reentrant");
85 mIgnoreInvalidation
= true;
87 nsIntRect currentBounds
;
88 mViewer
->GetBounds(currentBounds
);
90 // If the bounds have changed, we need to do a layout flush.
91 if (currentBounds
.Size() != aViewportSize
) {
92 mViewer
->SetBounds(IntRect(IntPoint(0, 0), aViewportSize
));
96 mIgnoreInvalidation
= false;
100 SVGDocumentWrapper::FlushImageTransformInvalidation()
102 MOZ_ASSERT(!mIgnoreInvalidation
, "shouldn't be reentrant");
104 SVGSVGElement
* svgElem
= GetRootSVGElem();
109 mIgnoreInvalidation
= true;
110 svgElem
->FlushImageTransformInvalidation();
112 mIgnoreInvalidation
= false;
116 SVGDocumentWrapper::IsAnimated()
118 // Can be called for animated images during shutdown, after we've
119 // already Observe()'d XPCOM shutdown and cleared out our mViewer pointer.
124 nsIDocument
* doc
= mViewer
->GetDocument();
128 if (doc
->Timeline()->HasAnimations()) {
129 // CSS animations (technically HasAnimations() also checks for CSS
130 // transitions and Web animations but since SVG-as-an-image doesn't run
131 // script they will never run in the document that we wrap).
134 if (doc
->HasAnimationController() &&
135 doc
->GetAnimationController()->HasRegisteredAnimations()) {
143 SVGDocumentWrapper::StartAnimation()
145 // Can be called for animated images during shutdown, after we've
146 // already Observe()'d XPCOM shutdown and cleared out our mViewer pointer.
151 nsIDocument
* doc
= mViewer
->GetDocument();
153 nsSMILAnimationController
* controller
= doc
->GetAnimationController();
155 controller
->Resume(nsSMILTimeContainer::PAUSE_IMAGE
);
157 doc
->ImageTracker()->SetAnimatingState(true);
162 SVGDocumentWrapper::StopAnimation()
164 // Can be called for animated images during shutdown, after we've
165 // already Observe()'d XPCOM shutdown and cleared out our mViewer pointer.
170 nsIDocument
* doc
= mViewer
->GetDocument();
172 nsSMILAnimationController
* controller
= doc
->GetAnimationController();
174 controller
->Pause(nsSMILTimeContainer::PAUSE_IMAGE
);
176 doc
->ImageTracker()->SetAnimatingState(false);
181 SVGDocumentWrapper::ResetAnimation()
183 SVGSVGElement
* svgElem
= GetRootSVGElem();
188 svgElem
->SetCurrentTime(0.0f
);
192 SVGDocumentWrapper::GetCurrentTime()
194 SVGSVGElement
* svgElem
= GetRootSVGElem();
195 return svgElem
? svgElem
->GetCurrentTime()
200 SVGDocumentWrapper::SetCurrentTime(float aTime
)
202 SVGSVGElement
* svgElem
= GetRootSVGElem();
203 if (svgElem
&& svgElem
->GetCurrentTime() != aTime
) {
204 svgElem
->SetCurrentTime(aTime
);
209 SVGDocumentWrapper::TickRefreshDriver()
211 nsCOMPtr
<nsIPresShell
> presShell
;
212 mViewer
->GetPresShell(getter_AddRefs(presShell
));
214 nsPresContext
* presContext
= presShell
->GetPresContext();
216 presContext
->RefreshDriver()->DoTick();
221 /** nsIStreamListener methods **/
224 SVGDocumentWrapper::OnDataAvailable(nsIRequest
* aRequest
, nsISupports
* ctxt
,
225 nsIInputStream
* inStr
,
226 uint64_t sourceOffset
,
229 return mListener
->OnDataAvailable(aRequest
, ctxt
, inStr
,
230 sourceOffset
, count
);
233 /** nsIRequestObserver methods **/
236 SVGDocumentWrapper::OnStartRequest(nsIRequest
* aRequest
, nsISupports
* ctxt
)
238 nsresult rv
= SetupViewer(aRequest
,
239 getter_AddRefs(mViewer
),
240 getter_AddRefs(mLoadGroup
));
242 if (NS_SUCCEEDED(rv
) &&
243 NS_SUCCEEDED(mListener
->OnStartRequest(aRequest
, nullptr))) {
244 mViewer
->GetDocument()->SetIsBeingUsedAsImage();
245 StopAnimation(); // otherwise animations start automatically in helper doc
247 rv
= mViewer
->Init(nullptr, nsIntRect(0, 0, 0, 0));
248 if (NS_SUCCEEDED(rv
)) {
249 rv
= mViewer
->Open(nullptr, nullptr);
257 SVGDocumentWrapper::OnStopRequest(nsIRequest
* aRequest
, nsISupports
* ctxt
,
261 mListener
->OnStopRequest(aRequest
, ctxt
, status
);
268 /** nsIObserver Methods **/
270 SVGDocumentWrapper::Observe(nsISupports
* aSubject
,
272 const char16_t
* aData
)
274 if (!strcmp(aTopic
, NS_XPCOM_SHUTDOWN_OBSERVER_ID
)) {
275 // Sever ties from rendering observers to helper-doc's root SVG node
276 SVGSVGElement
* svgElem
= GetRootSVGElem();
278 SVGObserverUtils::RemoveAllRenderingObservers(svgElem
);
281 // Clean up at XPCOM shutdown time.
287 mLoadGroup
= nullptr;
290 // Turn off "registered" flag, or else we'll try to unregister when we die.
291 // (No need for that now, and the try would fail anyway -- it's too late.)
292 mRegisteredForXPCOMShutdown
= false;
294 NS_ERROR("Unexpected observer topic.");
299 /** Private helper methods **/
301 // This method is largely cribbed from
302 // nsExternalResourceMap::PendingLoad::SetupViewer.
304 SVGDocumentWrapper::SetupViewer(nsIRequest
* aRequest
,
305 nsIContentViewer
** aViewer
,
306 nsILoadGroup
** aLoadGroup
)
308 nsCOMPtr
<nsIChannel
> chan(do_QueryInterface(aRequest
));
309 NS_ENSURE_TRUE(chan
, NS_ERROR_UNEXPECTED
);
311 // Check for HTTP error page
312 nsCOMPtr
<nsIHttpChannel
> httpChannel(do_QueryInterface(aRequest
));
314 bool requestSucceeded
;
315 if (NS_FAILED(httpChannel
->GetRequestSucceeded(&requestSucceeded
)) ||
317 return NS_ERROR_FAILURE
;
321 // Give this document its own loadgroup
322 nsCOMPtr
<nsILoadGroup
> loadGroup
;
323 chan
->GetLoadGroup(getter_AddRefs(loadGroup
));
325 nsCOMPtr
<nsILoadGroup
> newLoadGroup
=
326 do_CreateInstance(NS_LOADGROUP_CONTRACTID
);
327 NS_ENSURE_TRUE(newLoadGroup
, NS_ERROR_OUT_OF_MEMORY
);
328 newLoadGroup
->SetLoadGroup(loadGroup
);
330 nsCOMPtr
<nsICategoryManager
> catMan
=
331 do_GetService(NS_CATEGORYMANAGER_CONTRACTID
);
332 NS_ENSURE_TRUE(catMan
, NS_ERROR_NOT_AVAILABLE
);
333 nsCString contractId
;
334 nsresult rv
= catMan
->GetCategoryEntry("Gecko-Content-Viewers", IMAGE_SVG_XML
,
336 NS_ENSURE_SUCCESS(rv
, rv
);
337 nsCOMPtr
<nsIDocumentLoaderFactory
> docLoaderFactory
=
338 do_GetService(contractId
.get());
339 NS_ENSURE_TRUE(docLoaderFactory
, NS_ERROR_NOT_AVAILABLE
);
341 nsCOMPtr
<nsIContentViewer
> viewer
;
342 nsCOMPtr
<nsIStreamListener
> listener
;
343 rv
= docLoaderFactory
->CreateInstance("external-resource", chan
,
345 NS_LITERAL_CSTRING(IMAGE_SVG_XML
),
347 getter_AddRefs(listener
),
348 getter_AddRefs(viewer
));
349 NS_ENSURE_SUCCESS(rv
, rv
);
351 NS_ENSURE_TRUE(viewer
, NS_ERROR_UNEXPECTED
);
353 // Create a navigation time object and pass it to the SVG document through
355 // The timeline(DocumentTimeline, used in CSS animation) of this SVG
356 // document needs this navigation timing object for time computation, such
357 // as to calculate current time stamp based on the start time of navigation
360 // For a root document, DocShell would do these sort of things
361 // automatically. Since there is no DocShell for this wrapped SVG document,
362 // we must set it up manually.
363 RefPtr
<nsDOMNavigationTiming
> timing
= new nsDOMNavigationTiming(nullptr);
364 timing
->NotifyNavigationStart(nsDOMNavigationTiming::DocShellState::eInactive
);
365 viewer
->SetNavigationTiming(timing
);
367 nsCOMPtr
<nsIParser
> parser
= do_QueryInterface(listener
);
368 NS_ENSURE_TRUE(parser
, NS_ERROR_UNEXPECTED
);
370 // XML-only, because this is for SVG content
371 nsCOMPtr
<nsIContentSink
> sink
= parser
->GetContentSink();
372 NS_ENSURE_TRUE(sink
, NS_ERROR_UNEXPECTED
);
374 listener
.swap(mListener
);
375 viewer
.forget(aViewer
);
376 newLoadGroup
.forget(aLoadGroup
);
378 RegisterForXPCOMShutdown();
383 SVGDocumentWrapper::RegisterForXPCOMShutdown()
385 MOZ_ASSERT(!mRegisteredForXPCOMShutdown
,
386 "re-registering for XPCOM shutdown");
387 // Listen for xpcom-shutdown so that we can drop references to our
388 // helper-document at that point. (Otherwise, we won't get cleaned up
389 // until imgLoader::Shutdown, which can happen after the JAR service
390 // and RDF service have been unregistered.)
392 nsCOMPtr
<nsIObserverService
> obsSvc
= do_GetService(OBSERVER_SVC_CID
, &rv
);
394 NS_FAILED(obsSvc
->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID
,
396 NS_WARNING("Failed to register as observer of XPCOM shutdown");
398 mRegisteredForXPCOMShutdown
= true;
403 SVGDocumentWrapper::UnregisterForXPCOMShutdown()
405 MOZ_ASSERT(mRegisteredForXPCOMShutdown
,
406 "unregistering for XPCOM shutdown w/out being registered");
409 nsCOMPtr
<nsIObserverService
> obsSvc
= do_GetService(OBSERVER_SVC_CID
, &rv
);
411 NS_FAILED(obsSvc
->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID
))) {
412 NS_WARNING("Failed to unregister as observer of XPCOM shutdown");
414 mRegisteredForXPCOMShutdown
= false;
419 SVGDocumentWrapper::FlushLayout()
421 if (SVGDocument
* doc
= GetDocument()) {
422 doc
->FlushPendingNotifications(FlushType::Layout
);
427 SVGDocumentWrapper::GetDocument()
432 nsIDocument
* doc
= mViewer
->GetDocument();
436 return doc
->AsSVGDocument();
440 SVGDocumentWrapper::GetRootSVGElem()
443 return nullptr; // Can happen during destruction
446 nsIDocument
* doc
= mViewer
->GetDocument();
448 return nullptr; // Can happen during destruction
451 Element
* rootElem
= mViewer
->GetDocument()->GetRootElement();
452 if (!rootElem
|| !rootElem
->IsSVGElement(nsGkAtoms::svg
)) {
456 return static_cast<SVGSVGElement
*>(rootElem
);
460 } // namespace mozilla