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.

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

mercurial