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/Element.h"
9 #include "nsICategoryManager.h"
10 #include "nsIChannel.h"
11 #include "nsIContentViewer.h"
12 #include "nsIDocument.h"
13 #include "nsIDocumentLoaderFactory.h"
14 #include "nsIDOMSVGLength.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 "nsSVGEffects.h"
28 #include "mozilla/dom/SVGAnimatedLength.h"
29 #include "nsMimeTypes.h"
30 #include "DOMSVGLength.h"
32 // undef the GetCurrentTime macro defined in WinBase.h from the MS Platform SDK
35 using namespace mozilla::dom
;
40 NS_IMPL_ISUPPORTS(SVGDocumentWrapper
,
44 nsISupportsWeakReference
)
46 SVGDocumentWrapper::SVGDocumentWrapper()
47 : mIgnoreInvalidation(false),
48 mRegisteredForXPCOMShutdown(false)
52 SVGDocumentWrapper::~SVGDocumentWrapper()
55 if (mRegisteredForXPCOMShutdown
) {
56 UnregisterForXPCOMShutdown();
61 SVGDocumentWrapper::DestroyViewer()
64 mViewer
->GetDocument()->OnPageHide(false, nullptr);
65 mViewer
->Close(nullptr);
72 SVGDocumentWrapper::GetRootLayoutFrame()
74 Element
* rootElem
= GetRootSVGElem();
75 return rootElem
? rootElem
->GetPrimaryFrame() : nullptr;
79 SVGDocumentWrapper::UpdateViewportBounds(const nsIntSize
& aViewportSize
)
81 MOZ_ASSERT(!mIgnoreInvalidation
, "shouldn't be reentrant");
82 mIgnoreInvalidation
= true;
84 nsIntRect currentBounds
;
85 mViewer
->GetBounds(currentBounds
);
87 // If the bounds have changed, we need to do a layout flush.
88 if (currentBounds
.Size() != aViewportSize
) {
89 mViewer
->SetBounds(nsIntRect(nsIntPoint(0, 0), aViewportSize
));
93 mIgnoreInvalidation
= false;
97 SVGDocumentWrapper::FlushImageTransformInvalidation()
99 MOZ_ASSERT(!mIgnoreInvalidation
, "shouldn't be reentrant");
101 SVGSVGElement
* svgElem
= GetRootSVGElem();
105 mIgnoreInvalidation
= true;
106 svgElem
->FlushImageTransformInvalidation();
108 mIgnoreInvalidation
= false;
112 SVGDocumentWrapper::IsAnimated()
114 nsIDocument
* doc
= mViewer
->GetDocument();
115 return doc
&& doc
->HasAnimationController() &&
116 doc
->GetAnimationController()->HasRegisteredAnimations();
120 SVGDocumentWrapper::StartAnimation()
122 // Can be called for animated images during shutdown, after we've
123 // already Observe()'d XPCOM shutdown and cleared out our mViewer pointer.
127 nsIDocument
* doc
= mViewer
->GetDocument();
129 nsSMILAnimationController
* controller
= doc
->GetAnimationController();
131 controller
->Resume(nsSMILTimeContainer::PAUSE_IMAGE
);
133 doc
->SetImagesNeedAnimating(true);
138 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.
145 nsIDocument
* doc
= mViewer
->GetDocument();
147 nsSMILAnimationController
* controller
= doc
->GetAnimationController();
149 controller
->Pause(nsSMILTimeContainer::PAUSE_IMAGE
);
151 doc
->SetImagesNeedAnimating(false);
156 SVGDocumentWrapper::ResetAnimation()
158 SVGSVGElement
* svgElem
= GetRootSVGElem();
162 svgElem
->SetCurrentTime(0.0f
);
166 SVGDocumentWrapper::GetCurrentTime()
168 SVGSVGElement
* svgElem
= GetRootSVGElem();
169 return svgElem
? svgElem
->GetCurrentTime()
174 SVGDocumentWrapper::SetCurrentTime(float aTime
)
176 SVGSVGElement
* svgElem
= GetRootSVGElem();
177 if (svgElem
&& svgElem
->GetCurrentTime() != aTime
) {
178 svgElem
->SetCurrentTime(aTime
);
183 SVGDocumentWrapper::TickRefreshDriver()
185 nsCOMPtr
<nsIPresShell
> presShell
;
186 mViewer
->GetPresShell(getter_AddRefs(presShell
));
188 nsPresContext
* presContext
= presShell
->GetPresContext();
190 presContext
->RefreshDriver()->DoTick();
195 /** nsIStreamListener methods **/
197 /* void onDataAvailable (in nsIRequest request, in nsISupports ctxt,
198 in nsIInputStream inStr, in unsigned long sourceOffset,
199 in unsigned long count); */
201 SVGDocumentWrapper::OnDataAvailable(nsIRequest
* aRequest
, nsISupports
* ctxt
,
202 nsIInputStream
* inStr
,
203 uint64_t sourceOffset
,
206 return mListener
->OnDataAvailable(aRequest
, ctxt
, inStr
,
207 sourceOffset
, count
);
210 /** nsIRequestObserver methods **/
212 /* void onStartRequest (in nsIRequest request, in nsISupports ctxt); */
214 SVGDocumentWrapper::OnStartRequest(nsIRequest
* aRequest
, nsISupports
* ctxt
)
216 nsresult rv
= SetupViewer(aRequest
,
217 getter_AddRefs(mViewer
),
218 getter_AddRefs(mLoadGroup
));
220 if (NS_SUCCEEDED(rv
) &&
221 NS_SUCCEEDED(mListener
->OnStartRequest(aRequest
, nullptr))) {
222 mViewer
->GetDocument()->SetIsBeingUsedAsImage();
223 StopAnimation(); // otherwise animations start automatically in helper doc
225 rv
= mViewer
->Init(nullptr, nsIntRect(0, 0, 0, 0));
226 if (NS_SUCCEEDED(rv
)) {
227 rv
= mViewer
->Open(nullptr, nullptr);
234 /* void onStopRequest (in nsIRequest request, in nsISupports ctxt,
235 in nsresult status); */
237 SVGDocumentWrapper::OnStopRequest(nsIRequest
* aRequest
, nsISupports
* ctxt
,
241 mListener
->OnStopRequest(aRequest
, ctxt
, status
);
248 /** nsIObserver Methods **/
250 SVGDocumentWrapper::Observe(nsISupports
* aSubject
,
252 const char16_t
*aData
)
254 if (!strcmp(aTopic
, NS_XPCOM_SHUTDOWN_OBSERVER_ID
)) {
255 // Sever ties from rendering observers to helper-doc's root SVG node
256 SVGSVGElement
* svgElem
= GetRootSVGElem();
258 nsSVGEffects::RemoveAllRenderingObservers(svgElem
);
261 // Clean up at XPCOM shutdown time.
266 mLoadGroup
= nullptr;
268 // Turn off "registered" flag, or else we'll try to unregister when we die.
269 // (No need for that now, and the try would fail anyway -- it's too late.)
270 mRegisteredForXPCOMShutdown
= false;
272 NS_ERROR("Unexpected observer topic.");
277 /** Private helper methods **/
279 // This method is largely cribbed from
280 // nsExternalResourceMap::PendingLoad::SetupViewer.
282 SVGDocumentWrapper::SetupViewer(nsIRequest
* aRequest
,
283 nsIContentViewer
** aViewer
,
284 nsILoadGroup
** aLoadGroup
)
286 nsCOMPtr
<nsIChannel
> chan(do_QueryInterface(aRequest
));
287 NS_ENSURE_TRUE(chan
, NS_ERROR_UNEXPECTED
);
289 // Check for HTTP error page
290 nsCOMPtr
<nsIHttpChannel
> httpChannel(do_QueryInterface(aRequest
));
292 bool requestSucceeded
;
293 if (NS_FAILED(httpChannel
->GetRequestSucceeded(&requestSucceeded
)) ||
295 return NS_ERROR_FAILURE
;
299 // Give this document its own loadgroup
300 nsCOMPtr
<nsILoadGroup
> loadGroup
;
301 chan
->GetLoadGroup(getter_AddRefs(loadGroup
));
303 nsCOMPtr
<nsILoadGroup
> newLoadGroup
=
304 do_CreateInstance(NS_LOADGROUP_CONTRACTID
);
305 NS_ENSURE_TRUE(newLoadGroup
, NS_ERROR_OUT_OF_MEMORY
);
306 newLoadGroup
->SetLoadGroup(loadGroup
);
308 nsCOMPtr
<nsICategoryManager
> catMan
=
309 do_GetService(NS_CATEGORYMANAGER_CONTRACTID
);
310 NS_ENSURE_TRUE(catMan
, NS_ERROR_NOT_AVAILABLE
);
311 nsXPIDLCString contractId
;
312 nsresult rv
= catMan
->GetCategoryEntry("Goanna-Content-Viewers", IMAGE_SVG_XML
,
313 getter_Copies(contractId
));
314 NS_ENSURE_SUCCESS(rv
, rv
);
315 nsCOMPtr
<nsIDocumentLoaderFactory
> docLoaderFactory
=
316 do_GetService(contractId
);
317 NS_ENSURE_TRUE(docLoaderFactory
, NS_ERROR_NOT_AVAILABLE
);
319 nsCOMPtr
<nsIContentViewer
> viewer
;
320 nsCOMPtr
<nsIStreamListener
> listener
;
321 rv
= docLoaderFactory
->CreateInstance("external-resource", chan
,
323 NS_LITERAL_CSTRING(IMAGE_SVG_XML
),
325 getter_AddRefs(listener
),
326 getter_AddRefs(viewer
));
327 NS_ENSURE_SUCCESS(rv
, rv
);
329 NS_ENSURE_TRUE(viewer
, NS_ERROR_UNEXPECTED
);
331 nsCOMPtr
<nsIParser
> parser
= do_QueryInterface(listener
);
332 NS_ENSURE_TRUE(parser
, NS_ERROR_UNEXPECTED
);
334 // XML-only, because this is for SVG content
335 nsIContentSink
* sink
= parser
->GetContentSink();
336 nsCOMPtr
<nsIXMLContentSink
> xmlSink
= do_QueryInterface(sink
);
337 NS_ENSURE_TRUE(sink
, NS_ERROR_UNEXPECTED
);
339 listener
.swap(mListener
);
340 viewer
.forget(aViewer
);
341 newLoadGroup
.forget(aLoadGroup
);
343 RegisterForXPCOMShutdown();
348 SVGDocumentWrapper::RegisterForXPCOMShutdown()
350 MOZ_ASSERT(!mRegisteredForXPCOMShutdown
,
351 "re-registering for XPCOM shutdown");
352 // Listen for xpcom-shutdown so that we can drop references to our
353 // helper-document at that point. (Otherwise, we won't get cleaned up
354 // until imgLoader::Shutdown, which can happen after the JAR service
355 // and RDF service have been unregistered.)
357 nsCOMPtr
<nsIObserverService
> obsSvc
= do_GetService(OBSERVER_SVC_CID
, &rv
);
359 NS_FAILED(obsSvc
->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID
,
361 NS_WARNING("Failed to register as observer of XPCOM shutdown");
363 mRegisteredForXPCOMShutdown
= true;
368 SVGDocumentWrapper::UnregisterForXPCOMShutdown()
370 MOZ_ASSERT(mRegisteredForXPCOMShutdown
,
371 "unregistering for XPCOM shutdown w/out being registered");
374 nsCOMPtr
<nsIObserverService
> obsSvc
= do_GetService(OBSERVER_SVC_CID
, &rv
);
376 NS_FAILED(obsSvc
->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID
))) {
377 NS_WARNING("Failed to unregister as observer of XPCOM shutdown");
379 mRegisteredForXPCOMShutdown
= false;
384 SVGDocumentWrapper::FlushLayout()
386 nsCOMPtr
<nsIPresShell
> presShell
;
387 mViewer
->GetPresShell(getter_AddRefs(presShell
));
389 presShell
->FlushPendingNotifications(Flush_Layout
);
394 SVGDocumentWrapper::GetDocument()
399 return mViewer
->GetDocument(); // May be nullptr.
403 SVGDocumentWrapper::GetRootSVGElem()
406 return nullptr; // Can happen during destruction
408 nsIDocument
* doc
= mViewer
->GetDocument();
410 return nullptr; // Can happen during destruction
412 Element
* rootElem
= mViewer
->GetDocument()->GetRootElement();
413 if (!rootElem
|| !rootElem
->IsSVG(nsGkAtoms::svg
)) {
417 return static_cast<SVGSVGElement
*>(rootElem
);
421 } // namespace mozilla