LCOV - code coverage report
Current view: top level - image - SVGDocumentWrapper.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 126 200 63.0 %
Date: 2017-07-14 16:53:18 Functions: 18 26 69.2 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13