image/src/SVGDocumentWrapper.cpp

Thu, 15 Jan 2015 15:59:08 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:59:08 +0100
branch
TOR_BUG_9701
changeset 10
ac0c01689b40
permissions
-rw-r--r--

Implement a real Private Browsing Mode condition by changing the API/ABI;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

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

mercurial