1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/image/src/SVGDocumentWrapper.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,446 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "SVGDocumentWrapper.h" 1.10 + 1.11 +#include "mozilla/dom/Element.h" 1.12 +#include "nsICategoryManager.h" 1.13 +#include "nsIChannel.h" 1.14 +#include "nsIContentViewer.h" 1.15 +#include "nsIDocument.h" 1.16 +#include "nsIDocumentLoaderFactory.h" 1.17 +#include "nsIDOMSVGLength.h" 1.18 +#include "nsIHttpChannel.h" 1.19 +#include "nsIObserverService.h" 1.20 +#include "nsIParser.h" 1.21 +#include "nsIPresShell.h" 1.22 +#include "nsIRequest.h" 1.23 +#include "nsIStreamListener.h" 1.24 +#include "nsIXMLContentSink.h" 1.25 +#include "nsNetCID.h" 1.26 +#include "nsComponentManagerUtils.h" 1.27 +#include "nsSMILAnimationController.h" 1.28 +#include "nsServiceManagerUtils.h" 1.29 +#include "mozilla/dom/SVGSVGElement.h" 1.30 +#include "nsSVGEffects.h" 1.31 +#include "mozilla/dom/SVGAnimatedLength.h" 1.32 +#include "nsMimeTypes.h" 1.33 +#include "DOMSVGLength.h" 1.34 + 1.35 +// undef the GetCurrentTime macro defined in WinBase.h from the MS Platform SDK 1.36 +#undef GetCurrentTime 1.37 + 1.38 +using namespace mozilla::dom; 1.39 + 1.40 +namespace mozilla { 1.41 +namespace image { 1.42 + 1.43 +NS_IMPL_ISUPPORTS(SVGDocumentWrapper, 1.44 + nsIStreamListener, 1.45 + nsIRequestObserver, 1.46 + nsIObserver, 1.47 + nsISupportsWeakReference) 1.48 + 1.49 +SVGDocumentWrapper::SVGDocumentWrapper() 1.50 + : mIgnoreInvalidation(false), 1.51 + mRegisteredForXPCOMShutdown(false) 1.52 +{ 1.53 +} 1.54 + 1.55 +SVGDocumentWrapper::~SVGDocumentWrapper() 1.56 +{ 1.57 + DestroyViewer(); 1.58 + if (mRegisteredForXPCOMShutdown) { 1.59 + UnregisterForXPCOMShutdown(); 1.60 + } 1.61 +} 1.62 + 1.63 +void 1.64 +SVGDocumentWrapper::DestroyViewer() 1.65 +{ 1.66 + if (mViewer) { 1.67 + mViewer->GetDocument()->OnPageHide(false, nullptr); 1.68 + mViewer->Close(nullptr); 1.69 + mViewer->Destroy(); 1.70 + mViewer = nullptr; 1.71 + } 1.72 +} 1.73 + 1.74 +bool 1.75 +SVGDocumentWrapper::GetWidthOrHeight(Dimension aDimension, 1.76 + int32_t& aResult) 1.77 +{ 1.78 + SVGSVGElement* rootElem = GetRootSVGElem(); 1.79 + NS_ABORT_IF_FALSE(rootElem, "root elem missing or of wrong type"); 1.80 + 1.81 + // Get the width or height SVG object 1.82 + nsRefPtr<SVGAnimatedLength> domAnimLength; 1.83 + if (aDimension == eWidth) { 1.84 + domAnimLength = rootElem->Width(); 1.85 + } else { 1.86 + NS_ABORT_IF_FALSE(aDimension == eHeight, "invalid dimension"); 1.87 + domAnimLength = rootElem->Height(); 1.88 + } 1.89 + NS_ENSURE_TRUE(domAnimLength, false); 1.90 + 1.91 + // Get the animated value from the object 1.92 + nsRefPtr<DOMSVGLength> domLength = domAnimLength->AnimVal(); 1.93 + NS_ENSURE_TRUE(domLength, false); 1.94 + 1.95 + // Check if it's a percent value (and fail if so) 1.96 + uint16_t unitType; 1.97 + nsresult rv = domLength->GetUnitType(&unitType); 1.98 + NS_ENSURE_SUCCESS(rv, false); 1.99 + if (unitType == nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE) { 1.100 + return false; 1.101 + } 1.102 + 1.103 + // Non-percent value - woot! Grab it & return it. 1.104 + float floatLength; 1.105 + rv = domLength->GetValue(&floatLength); 1.106 + NS_ENSURE_SUCCESS(rv, false); 1.107 + 1.108 + aResult = nsSVGUtils::ClampToInt(floatLength); 1.109 + 1.110 + return true; 1.111 +} 1.112 + 1.113 +nsIFrame* 1.114 +SVGDocumentWrapper::GetRootLayoutFrame() 1.115 +{ 1.116 + Element* rootElem = GetRootSVGElem(); 1.117 + return rootElem ? rootElem->GetPrimaryFrame() : nullptr; 1.118 +} 1.119 + 1.120 +void 1.121 +SVGDocumentWrapper::UpdateViewportBounds(const nsIntSize& aViewportSize) 1.122 +{ 1.123 + NS_ABORT_IF_FALSE(!mIgnoreInvalidation, "shouldn't be reentrant"); 1.124 + mIgnoreInvalidation = true; 1.125 + 1.126 + nsIntRect currentBounds; 1.127 + mViewer->GetBounds(currentBounds); 1.128 + 1.129 + // If the bounds have changed, we need to do a layout flush. 1.130 + if (currentBounds.Size() != aViewportSize) { 1.131 + mViewer->SetBounds(nsIntRect(nsIntPoint(0, 0), aViewportSize)); 1.132 + FlushLayout(); 1.133 + } 1.134 + 1.135 + mIgnoreInvalidation = false; 1.136 +} 1.137 + 1.138 +void 1.139 +SVGDocumentWrapper::FlushImageTransformInvalidation() 1.140 +{ 1.141 + NS_ABORT_IF_FALSE(!mIgnoreInvalidation, "shouldn't be reentrant"); 1.142 + 1.143 + SVGSVGElement* svgElem = GetRootSVGElem(); 1.144 + if (!svgElem) 1.145 + return; 1.146 + 1.147 + mIgnoreInvalidation = true; 1.148 + svgElem->FlushImageTransformInvalidation(); 1.149 + FlushLayout(); 1.150 + mIgnoreInvalidation = false; 1.151 +} 1.152 + 1.153 +bool 1.154 +SVGDocumentWrapper::IsAnimated() 1.155 +{ 1.156 + nsIDocument* doc = mViewer->GetDocument(); 1.157 + return doc && doc->HasAnimationController() && 1.158 + doc->GetAnimationController()->HasRegisteredAnimations(); 1.159 +} 1.160 + 1.161 +void 1.162 +SVGDocumentWrapper::StartAnimation() 1.163 +{ 1.164 + // Can be called for animated images during shutdown, after we've 1.165 + // already Observe()'d XPCOM shutdown and cleared out our mViewer pointer. 1.166 + if (!mViewer) 1.167 + return; 1.168 + 1.169 + nsIDocument* doc = mViewer->GetDocument(); 1.170 + if (doc) { 1.171 + nsSMILAnimationController* controller = doc->GetAnimationController(); 1.172 + if (controller) { 1.173 + controller->Resume(nsSMILTimeContainer::PAUSE_IMAGE); 1.174 + } 1.175 + doc->SetImagesNeedAnimating(true); 1.176 + } 1.177 +} 1.178 + 1.179 +void 1.180 +SVGDocumentWrapper::StopAnimation() 1.181 +{ 1.182 + // Can be called for animated images during shutdown, after we've 1.183 + // already Observe()'d XPCOM shutdown and cleared out our mViewer pointer. 1.184 + if (!mViewer) 1.185 + return; 1.186 + 1.187 + nsIDocument* doc = mViewer->GetDocument(); 1.188 + if (doc) { 1.189 + nsSMILAnimationController* controller = doc->GetAnimationController(); 1.190 + if (controller) { 1.191 + controller->Pause(nsSMILTimeContainer::PAUSE_IMAGE); 1.192 + } 1.193 + doc->SetImagesNeedAnimating(false); 1.194 + } 1.195 +} 1.196 + 1.197 +void 1.198 +SVGDocumentWrapper::ResetAnimation() 1.199 +{ 1.200 + SVGSVGElement* svgElem = GetRootSVGElem(); 1.201 + if (!svgElem) 1.202 + return; 1.203 + 1.204 + svgElem->SetCurrentTime(0.0f); 1.205 +} 1.206 + 1.207 +float 1.208 +SVGDocumentWrapper::GetCurrentTime() 1.209 +{ 1.210 + SVGSVGElement* svgElem = GetRootSVGElem(); 1.211 + return svgElem ? svgElem->GetCurrentTime() 1.212 + : 0.0f; 1.213 +} 1.214 + 1.215 +void 1.216 +SVGDocumentWrapper::SetCurrentTime(float aTime) 1.217 +{ 1.218 + SVGSVGElement* svgElem = GetRootSVGElem(); 1.219 + if (svgElem && svgElem->GetCurrentTime() != aTime) { 1.220 + svgElem->SetCurrentTime(aTime); 1.221 + } 1.222 +} 1.223 + 1.224 +/** nsIStreamListener methods **/ 1.225 + 1.226 +/* void onDataAvailable (in nsIRequest request, in nsISupports ctxt, 1.227 + in nsIInputStream inStr, in unsigned long sourceOffset, 1.228 + in unsigned long count); */ 1.229 +NS_IMETHODIMP 1.230 +SVGDocumentWrapper::OnDataAvailable(nsIRequest* aRequest, nsISupports* ctxt, 1.231 + nsIInputStream* inStr, 1.232 + uint64_t sourceOffset, 1.233 + uint32_t count) 1.234 +{ 1.235 + return mListener->OnDataAvailable(aRequest, ctxt, inStr, 1.236 + sourceOffset, count); 1.237 +} 1.238 + 1.239 +/** nsIRequestObserver methods **/ 1.240 + 1.241 +/* void onStartRequest (in nsIRequest request, in nsISupports ctxt); */ 1.242 +NS_IMETHODIMP 1.243 +SVGDocumentWrapper::OnStartRequest(nsIRequest* aRequest, nsISupports* ctxt) 1.244 +{ 1.245 + nsresult rv = SetupViewer(aRequest, 1.246 + getter_AddRefs(mViewer), 1.247 + getter_AddRefs(mLoadGroup)); 1.248 + 1.249 + if (NS_SUCCEEDED(rv) && 1.250 + NS_SUCCEEDED(mListener->OnStartRequest(aRequest, nullptr))) { 1.251 + mViewer->GetDocument()->SetIsBeingUsedAsImage(); 1.252 + StopAnimation(); // otherwise animations start automatically in helper doc 1.253 + 1.254 + rv = mViewer->Init(nullptr, nsIntRect(0, 0, 0, 0)); 1.255 + if (NS_SUCCEEDED(rv)) { 1.256 + rv = mViewer->Open(nullptr, nullptr); 1.257 + } 1.258 + } 1.259 + return rv; 1.260 +} 1.261 + 1.262 + 1.263 +/* void onStopRequest (in nsIRequest request, in nsISupports ctxt, 1.264 + in nsresult status); */ 1.265 +NS_IMETHODIMP 1.266 +SVGDocumentWrapper::OnStopRequest(nsIRequest* aRequest, nsISupports* ctxt, 1.267 + nsresult status) 1.268 +{ 1.269 + if (mListener) { 1.270 + mListener->OnStopRequest(aRequest, ctxt, status); 1.271 + mListener = nullptr; 1.272 + } 1.273 + 1.274 + return NS_OK; 1.275 +} 1.276 + 1.277 +/** nsIObserver Methods **/ 1.278 +NS_IMETHODIMP 1.279 +SVGDocumentWrapper::Observe(nsISupports* aSubject, 1.280 + const char* aTopic, 1.281 + const char16_t *aData) 1.282 +{ 1.283 + if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) { 1.284 + // Sever ties from rendering observers to helper-doc's root SVG node 1.285 + SVGSVGElement* svgElem = GetRootSVGElem(); 1.286 + if (svgElem) { 1.287 + nsSVGEffects::RemoveAllRenderingObservers(svgElem); 1.288 + } 1.289 + 1.290 + // Clean up at XPCOM shutdown time. 1.291 + DestroyViewer(); 1.292 + if (mListener) 1.293 + mListener = nullptr; 1.294 + if (mLoadGroup) 1.295 + mLoadGroup = nullptr; 1.296 + 1.297 + // Turn off "registered" flag, or else we'll try to unregister when we die. 1.298 + // (No need for that now, and the try would fail anyway -- it's too late.) 1.299 + mRegisteredForXPCOMShutdown = false; 1.300 + } else { 1.301 + NS_ERROR("Unexpected observer topic."); 1.302 + } 1.303 + return NS_OK; 1.304 +} 1.305 + 1.306 +/** Private helper methods **/ 1.307 + 1.308 +// This method is largely cribbed from 1.309 +// nsExternalResourceMap::PendingLoad::SetupViewer. 1.310 +nsresult 1.311 +SVGDocumentWrapper::SetupViewer(nsIRequest* aRequest, 1.312 + nsIContentViewer** aViewer, 1.313 + nsILoadGroup** aLoadGroup) 1.314 +{ 1.315 + nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest)); 1.316 + NS_ENSURE_TRUE(chan, NS_ERROR_UNEXPECTED); 1.317 + 1.318 + // Check for HTTP error page 1.319 + nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequest)); 1.320 + if (httpChannel) { 1.321 + bool requestSucceeded; 1.322 + if (NS_FAILED(httpChannel->GetRequestSucceeded(&requestSucceeded)) || 1.323 + !requestSucceeded) { 1.324 + return NS_ERROR_FAILURE; 1.325 + } 1.326 + } 1.327 + 1.328 + // Give this document its own loadgroup 1.329 + nsCOMPtr<nsILoadGroup> loadGroup; 1.330 + chan->GetLoadGroup(getter_AddRefs(loadGroup)); 1.331 + 1.332 + nsCOMPtr<nsILoadGroup> newLoadGroup = 1.333 + do_CreateInstance(NS_LOADGROUP_CONTRACTID); 1.334 + NS_ENSURE_TRUE(newLoadGroup, NS_ERROR_OUT_OF_MEMORY); 1.335 + newLoadGroup->SetLoadGroup(loadGroup); 1.336 + 1.337 + nsCOMPtr<nsICategoryManager> catMan = 1.338 + do_GetService(NS_CATEGORYMANAGER_CONTRACTID); 1.339 + NS_ENSURE_TRUE(catMan, NS_ERROR_NOT_AVAILABLE); 1.340 + nsXPIDLCString contractId; 1.341 + nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", IMAGE_SVG_XML, 1.342 + getter_Copies(contractId)); 1.343 + NS_ENSURE_SUCCESS(rv, rv); 1.344 + nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory = 1.345 + do_GetService(contractId); 1.346 + NS_ENSURE_TRUE(docLoaderFactory, NS_ERROR_NOT_AVAILABLE); 1.347 + 1.348 + nsCOMPtr<nsIContentViewer> viewer; 1.349 + nsCOMPtr<nsIStreamListener> listener; 1.350 + rv = docLoaderFactory->CreateInstance("external-resource", chan, 1.351 + newLoadGroup, 1.352 + IMAGE_SVG_XML, nullptr, nullptr, 1.353 + getter_AddRefs(listener), 1.354 + getter_AddRefs(viewer)); 1.355 + NS_ENSURE_SUCCESS(rv, rv); 1.356 + 1.357 + NS_ENSURE_TRUE(viewer, NS_ERROR_UNEXPECTED); 1.358 + 1.359 + nsCOMPtr<nsIParser> parser = do_QueryInterface(listener); 1.360 + NS_ENSURE_TRUE(parser, NS_ERROR_UNEXPECTED); 1.361 + 1.362 + // XML-only, because this is for SVG content 1.363 + nsIContentSink* sink = parser->GetContentSink(); 1.364 + nsCOMPtr<nsIXMLContentSink> xmlSink = do_QueryInterface(sink); 1.365 + NS_ENSURE_TRUE(sink, NS_ERROR_UNEXPECTED); 1.366 + 1.367 + listener.swap(mListener); 1.368 + viewer.forget(aViewer); 1.369 + newLoadGroup.forget(aLoadGroup); 1.370 + 1.371 + RegisterForXPCOMShutdown(); 1.372 + return NS_OK; 1.373 +} 1.374 + 1.375 +void 1.376 +SVGDocumentWrapper::RegisterForXPCOMShutdown() 1.377 +{ 1.378 + NS_ABORT_IF_FALSE(!mRegisteredForXPCOMShutdown, 1.379 + "re-registering for XPCOM shutdown"); 1.380 + // Listen for xpcom-shutdown so that we can drop references to our 1.381 + // helper-document at that point. (Otherwise, we won't get cleaned up 1.382 + // until imgLoader::Shutdown, which can happen after the JAR service 1.383 + // and RDF service have been unregistered.) 1.384 + nsresult rv; 1.385 + nsCOMPtr<nsIObserverService> obsSvc = do_GetService(OBSERVER_SVC_CID, &rv); 1.386 + if (NS_FAILED(rv) || 1.387 + NS_FAILED(obsSvc->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, 1.388 + true))) { 1.389 + NS_WARNING("Failed to register as observer of XPCOM shutdown"); 1.390 + } else { 1.391 + mRegisteredForXPCOMShutdown = true; 1.392 + } 1.393 +} 1.394 + 1.395 +void 1.396 +SVGDocumentWrapper::UnregisterForXPCOMShutdown() 1.397 +{ 1.398 + NS_ABORT_IF_FALSE(mRegisteredForXPCOMShutdown, 1.399 + "unregistering for XPCOM shutdown w/out being registered"); 1.400 + 1.401 + nsresult rv; 1.402 + nsCOMPtr<nsIObserverService> obsSvc = do_GetService(OBSERVER_SVC_CID, &rv); 1.403 + if (NS_FAILED(rv) || 1.404 + NS_FAILED(obsSvc->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID))) { 1.405 + NS_WARNING("Failed to unregister as observer of XPCOM shutdown"); 1.406 + } else { 1.407 + mRegisteredForXPCOMShutdown = false; 1.408 + } 1.409 +} 1.410 + 1.411 +void 1.412 +SVGDocumentWrapper::FlushLayout() 1.413 +{ 1.414 + nsCOMPtr<nsIPresShell> presShell; 1.415 + mViewer->GetPresShell(getter_AddRefs(presShell)); 1.416 + if (presShell) { 1.417 + presShell->FlushPendingNotifications(Flush_Layout); 1.418 + } 1.419 +} 1.420 + 1.421 +nsIDocument* 1.422 +SVGDocumentWrapper::GetDocument() 1.423 +{ 1.424 + if (!mViewer) 1.425 + return nullptr; 1.426 + 1.427 + return mViewer->GetDocument(); // May be nullptr. 1.428 +} 1.429 + 1.430 +SVGSVGElement* 1.431 +SVGDocumentWrapper::GetRootSVGElem() 1.432 +{ 1.433 + if (!mViewer) 1.434 + return nullptr; // Can happen during destruction 1.435 + 1.436 + nsIDocument* doc = mViewer->GetDocument(); 1.437 + if (!doc) 1.438 + return nullptr; // Can happen during destruction 1.439 + 1.440 + Element* rootElem = mViewer->GetDocument()->GetRootElement(); 1.441 + if (!rootElem || !rootElem->IsSVG(nsGkAtoms::svg)) { 1.442 + return nullptr; 1.443 + } 1.444 + 1.445 + return static_cast<SVGSVGElement*>(rootElem); 1.446 +} 1.447 + 1.448 +} // namespace image 1.449 +} // namespace mozilla