Fix typo in 9b54bd30006c008b4a951331b273613d5bac3abf
[pm.git] / image / src / SVGDocumentWrapper.cpp
blob9a6bc7f80108cdb3f5d6415a312d15c79a22fd70
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"
22 #include "nsNetCID.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
33 #undef GetCurrentTime
35 using namespace mozilla::dom;
37 namespace mozilla {
38 namespace image {
40 NS_IMPL_ISUPPORTS(SVGDocumentWrapper,
41 nsIStreamListener,
42 nsIRequestObserver,
43 nsIObserver,
44 nsISupportsWeakReference)
46 SVGDocumentWrapper::SVGDocumentWrapper()
47 : mIgnoreInvalidation(false),
48 mRegisteredForXPCOMShutdown(false)
52 SVGDocumentWrapper::~SVGDocumentWrapper()
54 DestroyViewer();
55 if (mRegisteredForXPCOMShutdown) {
56 UnregisterForXPCOMShutdown();
60 void
61 SVGDocumentWrapper::DestroyViewer()
63 if (mViewer) {
64 mViewer->GetDocument()->OnPageHide(false, nullptr);
65 mViewer->Close(nullptr);
66 mViewer->Destroy();
67 mViewer = nullptr;
71 nsIFrame*
72 SVGDocumentWrapper::GetRootLayoutFrame()
74 Element* rootElem = GetRootSVGElem();
75 return rootElem ? rootElem->GetPrimaryFrame() : nullptr;
78 void
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));
90 FlushLayout();
93 mIgnoreInvalidation = false;
96 void
97 SVGDocumentWrapper::FlushImageTransformInvalidation()
99 MOZ_ASSERT(!mIgnoreInvalidation, "shouldn't be reentrant");
101 SVGSVGElement* svgElem = GetRootSVGElem();
102 if (!svgElem)
103 return;
105 mIgnoreInvalidation = true;
106 svgElem->FlushImageTransformInvalidation();
107 FlushLayout();
108 mIgnoreInvalidation = false;
111 bool
112 SVGDocumentWrapper::IsAnimated()
114 nsIDocument* doc = mViewer->GetDocument();
115 return doc && doc->HasAnimationController() &&
116 doc->GetAnimationController()->HasRegisteredAnimations();
119 void
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.
124 if (!mViewer)
125 return;
127 nsIDocument* doc = mViewer->GetDocument();
128 if (doc) {
129 nsSMILAnimationController* controller = doc->GetAnimationController();
130 if (controller) {
131 controller->Resume(nsSMILTimeContainer::PAUSE_IMAGE);
133 doc->SetImagesNeedAnimating(true);
137 void
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.
142 if (!mViewer)
143 return;
145 nsIDocument* doc = mViewer->GetDocument();
146 if (doc) {
147 nsSMILAnimationController* controller = doc->GetAnimationController();
148 if (controller) {
149 controller->Pause(nsSMILTimeContainer::PAUSE_IMAGE);
151 doc->SetImagesNeedAnimating(false);
155 void
156 SVGDocumentWrapper::ResetAnimation()
158 SVGSVGElement* svgElem = GetRootSVGElem();
159 if (!svgElem)
160 return;
162 svgElem->SetCurrentTime(0.0f);
165 float
166 SVGDocumentWrapper::GetCurrentTime()
168 SVGSVGElement* svgElem = GetRootSVGElem();
169 return svgElem ? svgElem->GetCurrentTime()
170 : 0.0f;
173 void
174 SVGDocumentWrapper::SetCurrentTime(float aTime)
176 SVGSVGElement* svgElem = GetRootSVGElem();
177 if (svgElem && svgElem->GetCurrentTime() != aTime) {
178 svgElem->SetCurrentTime(aTime);
182 void
183 SVGDocumentWrapper::TickRefreshDriver()
185 nsCOMPtr<nsIPresShell> presShell;
186 mViewer->GetPresShell(getter_AddRefs(presShell));
187 if (presShell) {
188 nsPresContext* presContext = presShell->GetPresContext();
189 if (presContext) {
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); */
200 NS_IMETHODIMP
201 SVGDocumentWrapper::OnDataAvailable(nsIRequest* aRequest, nsISupports* ctxt,
202 nsIInputStream* inStr,
203 uint64_t sourceOffset,
204 uint32_t count)
206 return mListener->OnDataAvailable(aRequest, ctxt, inStr,
207 sourceOffset, count);
210 /** nsIRequestObserver methods **/
212 /* void onStartRequest (in nsIRequest request, in nsISupports ctxt); */
213 NS_IMETHODIMP
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);
230 return rv;
234 /* void onStopRequest (in nsIRequest request, in nsISupports ctxt,
235 in nsresult status); */
236 NS_IMETHODIMP
237 SVGDocumentWrapper::OnStopRequest(nsIRequest* aRequest, nsISupports* ctxt,
238 nsresult status)
240 if (mListener) {
241 mListener->OnStopRequest(aRequest, ctxt, status);
242 mListener = nullptr;
245 return NS_OK;
248 /** nsIObserver Methods **/
249 NS_IMETHODIMP
250 SVGDocumentWrapper::Observe(nsISupports* aSubject,
251 const char* aTopic,
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();
257 if (svgElem) {
258 nsSVGEffects::RemoveAllRenderingObservers(svgElem);
261 // Clean up at XPCOM shutdown time.
262 DestroyViewer();
263 if (mListener)
264 mListener = nullptr;
265 if (mLoadGroup)
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;
271 } else {
272 NS_ERROR("Unexpected observer topic.");
274 return NS_OK;
277 /** Private helper methods **/
279 // This method is largely cribbed from
280 // nsExternalResourceMap::PendingLoad::SetupViewer.
281 nsresult
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));
291 if (httpChannel) {
292 bool requestSucceeded;
293 if (NS_FAILED(httpChannel->GetRequestSucceeded(&requestSucceeded)) ||
294 !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,
322 newLoadGroup,
323 NS_LITERAL_CSTRING(IMAGE_SVG_XML),
324 nullptr, nullptr,
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();
344 return NS_OK;
347 void
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.)
356 nsresult rv;
357 nsCOMPtr<nsIObserverService> obsSvc = do_GetService(OBSERVER_SVC_CID, &rv);
358 if (NS_FAILED(rv) ||
359 NS_FAILED(obsSvc->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID,
360 true))) {
361 NS_WARNING("Failed to register as observer of XPCOM shutdown");
362 } else {
363 mRegisteredForXPCOMShutdown = true;
367 void
368 SVGDocumentWrapper::UnregisterForXPCOMShutdown()
370 MOZ_ASSERT(mRegisteredForXPCOMShutdown,
371 "unregistering for XPCOM shutdown w/out being registered");
373 nsresult rv;
374 nsCOMPtr<nsIObserverService> obsSvc = do_GetService(OBSERVER_SVC_CID, &rv);
375 if (NS_FAILED(rv) ||
376 NS_FAILED(obsSvc->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID))) {
377 NS_WARNING("Failed to unregister as observer of XPCOM shutdown");
378 } else {
379 mRegisteredForXPCOMShutdown = false;
383 void
384 SVGDocumentWrapper::FlushLayout()
386 nsCOMPtr<nsIPresShell> presShell;
387 mViewer->GetPresShell(getter_AddRefs(presShell));
388 if (presShell) {
389 presShell->FlushPendingNotifications(Flush_Layout);
393 nsIDocument*
394 SVGDocumentWrapper::GetDocument()
396 if (!mViewer)
397 return nullptr;
399 return mViewer->GetDocument(); // May be nullptr.
402 SVGSVGElement*
403 SVGDocumentWrapper::GetRootSVGElem()
405 if (!mViewer)
406 return nullptr; // Can happen during destruction
408 nsIDocument* doc = mViewer->GetDocument();
409 if (!doc)
410 return nullptr; // Can happen during destruction
412 Element* rootElem = mViewer->GetDocument()->GetRootElement();
413 if (!rootElem || !rootElem->IsSVG(nsGkAtoms::svg)) {
414 return nullptr;
417 return static_cast<SVGSVGElement*>(rootElem);
420 } // namespace image
421 } // namespace mozilla