1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * the Mozilla Foundation.
19 : * Portions created by the Initial Developer are Copyright (C) 2010
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Daniel Holbert <dholbert@mozilla.com>
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either the GNU General Public License Version 2 or later (the "GPL"), or
27 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include "SVGDocumentWrapper.h"
40 :
41 : #include "mozilla/dom/Element.h"
42 : #include "nsIAtom.h"
43 : #include "nsICategoryManager.h"
44 : #include "nsIChannel.h"
45 : #include "nsIContentViewer.h"
46 : #include "nsIDocument.h"
47 : #include "nsIDocumentLoaderFactory.h"
48 : #include "nsIDOMSVGAnimatedLength.h"
49 : #include "nsIDOMSVGLength.h"
50 : #include "nsIHttpChannel.h"
51 : #include "nsIObserverService.h"
52 : #include "nsIParser.h"
53 : #include "nsIPresShell.h"
54 : #include "nsIRequest.h"
55 : #include "nsIStreamListener.h"
56 : #include "nsIXMLContentSink.h"
57 : #include "nsNetCID.h"
58 : #include "nsComponentManagerUtils.h"
59 : #include "nsServiceManagerUtils.h"
60 : #include "nsSize.h"
61 : #include "gfxRect.h"
62 : #include "nsSVGSVGElement.h"
63 : #include "nsSVGLength2.h"
64 : #include "nsSVGEffects.h"
65 :
66 : using namespace mozilla::dom;
67 :
68 : namespace mozilla {
69 : namespace image {
70 :
71 0 : NS_IMPL_ISUPPORTS4(SVGDocumentWrapper,
72 : nsIStreamListener,
73 : nsIRequestObserver,
74 : nsIObserver,
75 : nsISupportsWeakReference)
76 :
77 0 : SVGDocumentWrapper::SVGDocumentWrapper()
78 : : mIgnoreInvalidation(false),
79 0 : mRegisteredForXPCOMShutdown(false)
80 : {
81 0 : }
82 :
83 0 : SVGDocumentWrapper::~SVGDocumentWrapper()
84 : {
85 0 : DestroyViewer();
86 0 : if (mRegisteredForXPCOMShutdown) {
87 0 : UnregisterForXPCOMShutdown();
88 : }
89 0 : }
90 :
91 : void
92 0 : SVGDocumentWrapper::DestroyViewer()
93 : {
94 0 : if (mViewer) {
95 0 : mViewer->GetDocument()->OnPageHide(false, nsnull);
96 0 : mViewer->Close(nsnull);
97 0 : mViewer->Destroy();
98 0 : mViewer = nsnull;
99 : }
100 0 : }
101 :
102 : bool
103 0 : SVGDocumentWrapper::GetWidthOrHeight(Dimension aDimension,
104 : PRInt32& aResult)
105 : {
106 0 : nsSVGSVGElement* rootElem = GetRootSVGElem();
107 0 : NS_ABORT_IF_FALSE(rootElem, "root elem missing or of wrong type");
108 : nsresult rv;
109 :
110 : // Get the width or height SVG object
111 0 : nsRefPtr<nsIDOMSVGAnimatedLength> domAnimLength;
112 0 : if (aDimension == eWidth) {
113 0 : rv = rootElem->GetWidth(getter_AddRefs(domAnimLength));
114 : } else {
115 0 : NS_ABORT_IF_FALSE(aDimension == eHeight, "invalid dimension");
116 0 : rv = rootElem->GetHeight(getter_AddRefs(domAnimLength));
117 : }
118 0 : NS_ENSURE_SUCCESS(rv, false);
119 0 : NS_ENSURE_TRUE(domAnimLength, false);
120 :
121 : // Get the animated value from the object
122 0 : nsRefPtr<nsIDOMSVGLength> domLength;
123 0 : rv = domAnimLength->GetAnimVal(getter_AddRefs(domLength));
124 0 : NS_ENSURE_SUCCESS(rv, false);
125 0 : NS_ENSURE_TRUE(domLength, false);
126 :
127 : // Check if it's a percent value (and fail if so)
128 : PRUint16 unitType;
129 0 : rv = domLength->GetUnitType(&unitType);
130 0 : NS_ENSURE_SUCCESS(rv, false);
131 0 : if (unitType == nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE) {
132 0 : return false;
133 : }
134 :
135 : // Non-percent value - woot! Grab it & return it.
136 : float floatLength;
137 0 : rv = domLength->GetValue(&floatLength);
138 0 : NS_ENSURE_SUCCESS(rv, false);
139 :
140 0 : aResult = nsSVGUtils::ClampToInt(floatLength);
141 :
142 0 : return true;
143 : }
144 :
145 : nsIFrame*
146 0 : SVGDocumentWrapper::GetRootLayoutFrame()
147 : {
148 0 : Element* rootElem = GetRootSVGElem();
149 0 : return rootElem ? rootElem->GetPrimaryFrame() : nsnull;
150 : }
151 :
152 : void
153 0 : SVGDocumentWrapper::UpdateViewportBounds(const nsIntSize& aViewportSize)
154 : {
155 0 : NS_ABORT_IF_FALSE(!mIgnoreInvalidation, "shouldn't be reentrant");
156 0 : mIgnoreInvalidation = true;
157 0 : mViewer->SetBounds(nsIntRect(nsIntPoint(0, 0), aViewportSize));
158 0 : FlushLayout();
159 0 : mIgnoreInvalidation = false;
160 0 : }
161 :
162 : void
163 0 : SVGDocumentWrapper::FlushImageTransformInvalidation()
164 : {
165 0 : NS_ABORT_IF_FALSE(!mIgnoreInvalidation, "shouldn't be reentrant");
166 :
167 0 : nsSVGSVGElement* svgElem = GetRootSVGElem();
168 0 : if (!svgElem)
169 0 : return;
170 :
171 0 : mIgnoreInvalidation = true;
172 0 : svgElem->FlushImageTransformInvalidation();
173 0 : FlushLayout();
174 0 : mIgnoreInvalidation = false;
175 : }
176 :
177 : bool
178 0 : SVGDocumentWrapper::IsAnimated()
179 : {
180 0 : nsIDocument* doc = mViewer->GetDocument();
181 0 : return doc && doc->HasAnimationController() &&
182 0 : doc->GetAnimationController()->HasRegisteredAnimations();
183 : }
184 :
185 : void
186 0 : SVGDocumentWrapper::StartAnimation()
187 : {
188 : // Can be called for animated images during shutdown, after we've
189 : // already Observe()'d XPCOM shutdown and cleared out our mViewer pointer.
190 0 : if (!mViewer)
191 0 : return;
192 :
193 0 : nsIDocument* doc = mViewer->GetDocument();
194 0 : if (doc) {
195 0 : nsSMILAnimationController* controller = doc->GetAnimationController();
196 0 : if (controller) {
197 0 : controller->Resume(nsSMILTimeContainer::PAUSE_IMAGE);
198 : }
199 0 : doc->SetImagesNeedAnimating(true);
200 : }
201 : }
202 :
203 : void
204 0 : SVGDocumentWrapper::StopAnimation()
205 : {
206 : // Can be called for animated images during shutdown, after we've
207 : // already Observe()'d XPCOM shutdown and cleared out our mViewer pointer.
208 0 : if (!mViewer)
209 0 : return;
210 :
211 0 : nsIDocument* doc = mViewer->GetDocument();
212 0 : if (doc) {
213 0 : nsSMILAnimationController* controller = doc->GetAnimationController();
214 0 : if (controller) {
215 0 : controller->Pause(nsSMILTimeContainer::PAUSE_IMAGE);
216 : }
217 0 : doc->SetImagesNeedAnimating(false);
218 : }
219 : }
220 :
221 : void
222 0 : SVGDocumentWrapper::ResetAnimation()
223 : {
224 0 : nsSVGSVGElement* svgElem = GetRootSVGElem();
225 0 : if (!svgElem)
226 0 : return;
227 :
228 : #ifdef DEBUG
229 : nsresult rv =
230 : #endif
231 0 : svgElem->SetCurrentTime(0.0f);
232 0 : NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "SetCurrentTime failed");
233 : }
234 :
235 :
236 : /** nsIStreamListener methods **/
237 :
238 : /* void onDataAvailable (in nsIRequest request, in nsISupports ctxt,
239 : in nsIInputStream inStr, in unsigned long sourceOffset,
240 : in unsigned long count); */
241 : NS_IMETHODIMP
242 0 : SVGDocumentWrapper::OnDataAvailable(nsIRequest* aRequest, nsISupports* ctxt,
243 : nsIInputStream* inStr,
244 : PRUint32 sourceOffset,
245 : PRUint32 count)
246 : {
247 0 : return mListener->OnDataAvailable(aRequest, ctxt, inStr,
248 0 : sourceOffset, count);
249 : }
250 :
251 : /** nsIRequestObserver methods **/
252 :
253 : /* void onStartRequest (in nsIRequest request, in nsISupports ctxt); */
254 : NS_IMETHODIMP
255 0 : SVGDocumentWrapper::OnStartRequest(nsIRequest* aRequest, nsISupports* ctxt)
256 : {
257 : nsresult rv = SetupViewer(aRequest,
258 0 : getter_AddRefs(mViewer),
259 0 : getter_AddRefs(mLoadGroup));
260 :
261 0 : if (NS_SUCCEEDED(rv) &&
262 0 : NS_SUCCEEDED(mListener->OnStartRequest(aRequest, nsnull))) {
263 0 : mViewer->GetDocument()->SetIsBeingUsedAsImage();
264 0 : StopAnimation(); // otherwise animations start automatically in helper doc
265 :
266 0 : rv = mViewer->Init(nsnull, nsIntRect(0, 0, 0, 0));
267 0 : if (NS_SUCCEEDED(rv)) {
268 0 : rv = mViewer->Open(nsnull, nsnull);
269 : }
270 : }
271 0 : return rv;
272 : }
273 :
274 :
275 : /* void onStopRequest (in nsIRequest request, in nsISupports ctxt,
276 : in nsresult status); */
277 : NS_IMETHODIMP
278 0 : SVGDocumentWrapper::OnStopRequest(nsIRequest* aRequest, nsISupports* ctxt,
279 : nsresult status)
280 : {
281 0 : if (mListener) {
282 0 : mListener->OnStopRequest(aRequest, ctxt, status);
283 : // A few levels up the stack, imgRequest::OnStopRequest is about to tell
284 : // all of its observers that we know our size and are ready to paint. That
285 : // might not be true at this point, though -- so here, we synchronously
286 : // finish parsing & layout in our helper-document to make sure we can hold
287 : // up to this promise.
288 0 : nsCOMPtr<nsIParser> parser = do_QueryInterface(mListener);
289 0 : while (!parser->IsComplete()) {
290 0 : parser->CancelParsingEvents();
291 0 : parser->ContinueInterruptedParsing();
292 : }
293 0 : FlushLayout();
294 0 : mListener = nsnull;
295 :
296 : // In a normal document, this would be called by nsDocShell - but we don't
297 : // have a nsDocShell. So we do it ourselves. (If we don't, painting will
298 : // stay suppressed for a little while longer, for no good reason).
299 0 : mViewer->LoadComplete(NS_OK);
300 : }
301 :
302 0 : return NS_OK;
303 : }
304 :
305 : /** nsIObserver Methods **/
306 : NS_IMETHODIMP
307 0 : SVGDocumentWrapper::Observe(nsISupports* aSubject,
308 : const char* aTopic,
309 : const PRUnichar *aData)
310 : {
311 0 : if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
312 : // Sever ties from rendering observers to helper-doc's root SVG node
313 0 : nsSVGSVGElement* svgElem = GetRootSVGElem();
314 0 : if (svgElem) {
315 0 : nsSVGEffects::RemoveAllRenderingObservers(svgElem);
316 : }
317 :
318 : // Clean up at XPCOM shutdown time.
319 0 : DestroyViewer();
320 0 : if (mListener)
321 0 : mListener = nsnull;
322 0 : if (mLoadGroup)
323 0 : mLoadGroup = nsnull;
324 :
325 : // Turn off "registered" flag, or else we'll try to unregister when we die.
326 : // (No need for that now, and the try would fail anyway -- it's too late.)
327 0 : mRegisteredForXPCOMShutdown = false;
328 : } else {
329 0 : NS_ERROR("Unexpected observer topic.");
330 : }
331 0 : return NS_OK;
332 : }
333 :
334 : /** Private helper methods **/
335 :
336 : // This method is largely cribbed from
337 : // nsExternalResourceMap::PendingLoad::SetupViewer.
338 : nsresult
339 0 : SVGDocumentWrapper::SetupViewer(nsIRequest* aRequest,
340 : nsIContentViewer** aViewer,
341 : nsILoadGroup** aLoadGroup)
342 : {
343 0 : nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
344 0 : NS_ENSURE_TRUE(chan, NS_ERROR_UNEXPECTED);
345 :
346 : // Check for HTTP error page
347 0 : nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequest));
348 0 : if (httpChannel) {
349 : bool requestSucceeded;
350 0 : if (NS_FAILED(httpChannel->GetRequestSucceeded(&requestSucceeded)) ||
351 0 : !requestSucceeded) {
352 0 : return NS_ERROR_FAILURE;
353 : }
354 : }
355 :
356 : // Give this document its own loadgroup
357 0 : nsCOMPtr<nsILoadGroup> loadGroup;
358 0 : chan->GetLoadGroup(getter_AddRefs(loadGroup));
359 :
360 : nsCOMPtr<nsILoadGroup> newLoadGroup =
361 0 : do_CreateInstance(NS_LOADGROUP_CONTRACTID);
362 0 : NS_ENSURE_TRUE(newLoadGroup, NS_ERROR_OUT_OF_MEMORY);
363 0 : newLoadGroup->SetLoadGroup(loadGroup);
364 :
365 : nsCOMPtr<nsICategoryManager> catMan =
366 0 : do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
367 0 : NS_ENSURE_TRUE(catMan, NS_ERROR_NOT_AVAILABLE);
368 0 : nsXPIDLCString contractId;
369 0 : nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", SVG_MIMETYPE,
370 0 : getter_Copies(contractId));
371 0 : NS_ENSURE_SUCCESS(rv, rv);
372 : nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory =
373 0 : do_GetService(contractId);
374 0 : NS_ENSURE_TRUE(docLoaderFactory, NS_ERROR_NOT_AVAILABLE);
375 :
376 0 : nsCOMPtr<nsIContentViewer> viewer;
377 0 : nsCOMPtr<nsIStreamListener> listener;
378 0 : rv = docLoaderFactory->CreateInstance("external-resource", chan,
379 : newLoadGroup,
380 : SVG_MIMETYPE, nsnull, nsnull,
381 0 : getter_AddRefs(listener),
382 0 : getter_AddRefs(viewer));
383 0 : NS_ENSURE_SUCCESS(rv, rv);
384 :
385 0 : NS_ENSURE_TRUE(viewer, NS_ERROR_UNEXPECTED);
386 :
387 0 : nsCOMPtr<nsIParser> parser = do_QueryInterface(listener);
388 0 : NS_ENSURE_TRUE(parser, NS_ERROR_UNEXPECTED);
389 :
390 : // XML-only, because this is for SVG content
391 0 : nsIContentSink* sink = parser->GetContentSink();
392 0 : nsCOMPtr<nsIXMLContentSink> xmlSink = do_QueryInterface(sink);
393 0 : NS_ENSURE_TRUE(sink, NS_ERROR_UNEXPECTED);
394 :
395 0 : listener.swap(mListener);
396 0 : viewer.forget(aViewer);
397 0 : newLoadGroup.forget(aLoadGroup);
398 :
399 0 : RegisterForXPCOMShutdown();
400 0 : return NS_OK;
401 : }
402 :
403 : void
404 0 : SVGDocumentWrapper::RegisterForXPCOMShutdown()
405 : {
406 0 : NS_ABORT_IF_FALSE(!mRegisteredForXPCOMShutdown,
407 : "re-registering for XPCOM shutdown");
408 : // Listen for xpcom-shutdown so that we can drop references to our
409 : // helper-document at that point. (Otherwise, we won't get cleaned up
410 : // until imgLoader::Shutdown, which can happen after the JAR service
411 : // and RDF service have been unregistered.)
412 : nsresult rv;
413 0 : nsCOMPtr<nsIObserverService> obsSvc = do_GetService(OBSERVER_SVC_CID, &rv);
414 0 : if (NS_FAILED(rv) ||
415 0 : NS_FAILED(obsSvc->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID,
416 : true))) {
417 0 : NS_WARNING("Failed to register as observer of XPCOM shutdown");
418 : } else {
419 0 : mRegisteredForXPCOMShutdown = true;
420 : }
421 0 : }
422 :
423 : void
424 0 : SVGDocumentWrapper::UnregisterForXPCOMShutdown()
425 : {
426 0 : NS_ABORT_IF_FALSE(mRegisteredForXPCOMShutdown,
427 : "unregistering for XPCOM shutdown w/out being registered");
428 :
429 : nsresult rv;
430 0 : nsCOMPtr<nsIObserverService> obsSvc = do_GetService(OBSERVER_SVC_CID, &rv);
431 0 : if (NS_FAILED(rv) ||
432 0 : NS_FAILED(obsSvc->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID))) {
433 0 : NS_WARNING("Failed to unregister as observer of XPCOM shutdown");
434 : } else {
435 0 : mRegisteredForXPCOMShutdown = false;
436 : }
437 0 : }
438 :
439 : void
440 0 : SVGDocumentWrapper::FlushLayout()
441 : {
442 0 : nsCOMPtr<nsIPresShell> presShell;
443 0 : mViewer->GetPresShell(getter_AddRefs(presShell));
444 0 : if (presShell) {
445 0 : presShell->FlushPendingNotifications(Flush_Layout);
446 : }
447 0 : }
448 :
449 : nsSVGSVGElement*
450 0 : SVGDocumentWrapper::GetRootSVGElem()
451 : {
452 0 : if (!mViewer)
453 0 : return nsnull; // Can happen during destruction
454 :
455 0 : nsIDocument* doc = mViewer->GetDocument();
456 0 : if (!doc)
457 0 : return nsnull; // Can happen during destruction
458 :
459 0 : Element* rootElem = mViewer->GetDocument()->GetRootElement();
460 0 : if (!rootElem || !rootElem->IsSVG(nsGkAtoms::svg)) {
461 0 : return nsnull;
462 : }
463 :
464 0 : return static_cast<nsSVGSVGElement*>(rootElem);
465 : }
466 :
467 : } // namespace image
468 : } // namespace mozilla
|