123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461 |
- /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- #include "SVGDocumentWrapper.h"
- #include "mozilla/dom/DocumentTimeline.h"
- #include "mozilla/dom/Element.h"
- #include "nsICategoryManager.h"
- #include "nsIChannel.h"
- #include "nsIContentViewer.h"
- #include "nsIDocument.h"
- #include "nsIDocumentLoaderFactory.h"
- #include "nsIDOMSVGLength.h"
- #include "nsIHttpChannel.h"
- #include "nsIObserverService.h"
- #include "nsIParser.h"
- #include "nsIPresShell.h"
- #include "nsIRequest.h"
- #include "nsIStreamListener.h"
- #include "nsIXMLContentSink.h"
- #include "nsNetCID.h"
- #include "nsComponentManagerUtils.h"
- #include "nsSMILAnimationController.h"
- #include "nsServiceManagerUtils.h"
- #include "mozilla/dom/SVGSVGElement.h"
- #include "nsSVGEffects.h"
- #include "mozilla/dom/SVGAnimatedLength.h"
- #include "nsMimeTypes.h"
- #include "DOMSVGLength.h"
- #include "nsDocument.h"
- #include "mozilla/dom/ImageTracker.h"
- // undef the GetCurrentTime macro defined in WinBase.h from the MS Platform SDK
- #undef GetCurrentTime
- namespace mozilla {
- using namespace dom;
- using namespace gfx;
- namespace image {
- NS_IMPL_ISUPPORTS(SVGDocumentWrapper,
- nsIStreamListener,
- nsIRequestObserver,
- nsIObserver,
- nsISupportsWeakReference)
- SVGDocumentWrapper::SVGDocumentWrapper()
- : mIgnoreInvalidation(false),
- mRegisteredForXPCOMShutdown(false)
- { }
- SVGDocumentWrapper::~SVGDocumentWrapper()
- {
- DestroyViewer();
- if (mRegisteredForXPCOMShutdown) {
- UnregisterForXPCOMShutdown();
- }
- }
- void
- SVGDocumentWrapper::DestroyViewer()
- {
- if (mViewer) {
- mViewer->GetDocument()->OnPageHide(false, nullptr);
- mViewer->Close(nullptr);
- mViewer->Destroy();
- mViewer = nullptr;
- }
- }
- nsIFrame*
- SVGDocumentWrapper::GetRootLayoutFrame()
- {
- Element* rootElem = GetRootSVGElem();
- return rootElem ? rootElem->GetPrimaryFrame() : nullptr;
- }
- void
- SVGDocumentWrapper::UpdateViewportBounds(const nsIntSize& aViewportSize)
- {
- MOZ_ASSERT(!mIgnoreInvalidation, "shouldn't be reentrant");
- mIgnoreInvalidation = true;
- nsIntRect currentBounds;
- mViewer->GetBounds(currentBounds);
- // If the bounds have changed, we need to do a layout flush.
- if (currentBounds.Size() != aViewportSize) {
- mViewer->SetBounds(IntRect(IntPoint(0, 0), aViewportSize));
- FlushLayout();
- }
- mIgnoreInvalidation = false;
- }
- void
- SVGDocumentWrapper::FlushImageTransformInvalidation()
- {
- MOZ_ASSERT(!mIgnoreInvalidation, "shouldn't be reentrant");
- SVGSVGElement* svgElem = GetRootSVGElem();
- if (!svgElem) {
- return;
- }
- mIgnoreInvalidation = true;
- svgElem->FlushImageTransformInvalidation();
- FlushLayout();
- mIgnoreInvalidation = false;
- }
- bool
- SVGDocumentWrapper::IsAnimated()
- {
- // Can be called for animated images during shutdown, after we've
- // already Observe()'d XPCOM shutdown and cleared out our mViewer pointer.
- if (!mViewer) {
- return false;
- }
- nsIDocument* doc = mViewer->GetDocument();
- if (!doc) {
- return false;
- }
- if (doc->Timeline()->HasAnimations()) {
- // CSS animations (technically HasAnimations() also checks for CSS
- // transitions and Web animations but since SVG-as-an-image doesn't run
- // script they will never run in the document that we wrap).
- return true;
- }
- if (doc->HasAnimationController() &&
- doc->GetAnimationController()->HasRegisteredAnimations()) {
- // SMIL animations
- return true;
- }
- return false;
- }
- void
- SVGDocumentWrapper::StartAnimation()
- {
- // Can be called for animated images during shutdown, after we've
- // already Observe()'d XPCOM shutdown and cleared out our mViewer pointer.
- if (!mViewer) {
- return;
- }
- nsIDocument* doc = mViewer->GetDocument();
- if (doc) {
- nsSMILAnimationController* controller = doc->GetAnimationController();
- if (controller) {
- controller->Resume(nsSMILTimeContainer::PAUSE_IMAGE);
- }
- doc->ImageTracker()->SetAnimatingState(true);
- }
- }
- void
- SVGDocumentWrapper::StopAnimation()
- {
- // Can be called for animated images during shutdown, after we've
- // already Observe()'d XPCOM shutdown and cleared out our mViewer pointer.
- if (!mViewer) {
- return;
- }
- nsIDocument* doc = mViewer->GetDocument();
- if (doc) {
- nsSMILAnimationController* controller = doc->GetAnimationController();
- if (controller) {
- controller->Pause(nsSMILTimeContainer::PAUSE_IMAGE);
- }
- doc->ImageTracker()->SetAnimatingState(false);
- }
- }
- void
- SVGDocumentWrapper::ResetAnimation()
- {
- SVGSVGElement* svgElem = GetRootSVGElem();
- if (!svgElem) {
- return;
- }
- svgElem->SetCurrentTime(0.0f);
- }
- float
- SVGDocumentWrapper::GetCurrentTime()
- {
- SVGSVGElement* svgElem = GetRootSVGElem();
- return svgElem ? svgElem->GetCurrentTime()
- : 0.0f;
- }
- void
- SVGDocumentWrapper::SetCurrentTime(float aTime)
- {
- SVGSVGElement* svgElem = GetRootSVGElem();
- if (svgElem && svgElem->GetCurrentTime() != aTime) {
- svgElem->SetCurrentTime(aTime);
- }
- }
- void
- SVGDocumentWrapper::TickRefreshDriver()
- {
- nsCOMPtr<nsIPresShell> presShell;
- mViewer->GetPresShell(getter_AddRefs(presShell));
- if (presShell) {
- nsPresContext* presContext = presShell->GetPresContext();
- if (presContext) {
- presContext->RefreshDriver()->DoTick();
- }
- }
- }
- /** nsIStreamListener methods **/
- NS_IMETHODIMP
- SVGDocumentWrapper::OnDataAvailable(nsIRequest* aRequest, nsISupports* ctxt,
- nsIInputStream* inStr,
- uint64_t sourceOffset,
- uint32_t count)
- {
- return mListener->OnDataAvailable(aRequest, ctxt, inStr,
- sourceOffset, count);
- }
- /** nsIRequestObserver methods **/
- NS_IMETHODIMP
- SVGDocumentWrapper::OnStartRequest(nsIRequest* aRequest, nsISupports* ctxt)
- {
- nsresult rv = SetupViewer(aRequest,
- getter_AddRefs(mViewer),
- getter_AddRefs(mLoadGroup));
- if (NS_SUCCEEDED(rv) &&
- NS_SUCCEEDED(mListener->OnStartRequest(aRequest, nullptr))) {
- mViewer->GetDocument()->SetIsBeingUsedAsImage();
- StopAnimation(); // otherwise animations start automatically in helper doc
- rv = mViewer->Init(nullptr, nsIntRect(0, 0, 0, 0));
- if (NS_SUCCEEDED(rv)) {
- rv = mViewer->Open(nullptr, nullptr);
- }
- }
- return rv;
- }
- NS_IMETHODIMP
- SVGDocumentWrapper::OnStopRequest(nsIRequest* aRequest, nsISupports* ctxt,
- nsresult status)
- {
- if (mListener) {
- mListener->OnStopRequest(aRequest, ctxt, status);
- mListener = nullptr;
- }
- return NS_OK;
- }
- /** nsIObserver Methods **/
- NS_IMETHODIMP
- SVGDocumentWrapper::Observe(nsISupports* aSubject,
- const char* aTopic,
- const char16_t* aData)
- {
- if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
- // Sever ties from rendering observers to helper-doc's root SVG node
- SVGSVGElement* svgElem = GetRootSVGElem();
- if (svgElem) {
- nsSVGEffects::RemoveAllRenderingObservers(svgElem);
- }
- // Clean up at XPCOM shutdown time.
- DestroyViewer();
- if (mListener) {
- mListener = nullptr;
- }
- if (mLoadGroup) {
- mLoadGroup = nullptr;
- }
- // Turn off "registered" flag, or else we'll try to unregister when we die.
- // (No need for that now, and the try would fail anyway -- it's too late.)
- mRegisteredForXPCOMShutdown = false;
- } else {
- NS_ERROR("Unexpected observer topic.");
- }
- return NS_OK;
- }
- /** Private helper methods **/
- // This method is largely cribbed from
- // nsExternalResourceMap::PendingLoad::SetupViewer.
- nsresult
- SVGDocumentWrapper::SetupViewer(nsIRequest* aRequest,
- nsIContentViewer** aViewer,
- nsILoadGroup** aLoadGroup)
- {
- nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
- NS_ENSURE_TRUE(chan, NS_ERROR_UNEXPECTED);
- // Check for HTTP error page
- nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequest));
- if (httpChannel) {
- bool requestSucceeded;
- if (NS_FAILED(httpChannel->GetRequestSucceeded(&requestSucceeded)) ||
- !requestSucceeded) {
- return NS_ERROR_FAILURE;
- }
- }
- // Give this document its own loadgroup
- nsCOMPtr<nsILoadGroup> loadGroup;
- chan->GetLoadGroup(getter_AddRefs(loadGroup));
- nsCOMPtr<nsILoadGroup> newLoadGroup =
- do_CreateInstance(NS_LOADGROUP_CONTRACTID);
- NS_ENSURE_TRUE(newLoadGroup, NS_ERROR_OUT_OF_MEMORY);
- newLoadGroup->SetLoadGroup(loadGroup);
- nsCOMPtr<nsICategoryManager> catMan =
- do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
- NS_ENSURE_TRUE(catMan, NS_ERROR_NOT_AVAILABLE);
- nsXPIDLCString contractId;
- nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", IMAGE_SVG_XML,
- getter_Copies(contractId));
- NS_ENSURE_SUCCESS(rv, rv);
- nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory =
- do_GetService(contractId);
- NS_ENSURE_TRUE(docLoaderFactory, NS_ERROR_NOT_AVAILABLE);
- nsCOMPtr<nsIContentViewer> viewer;
- nsCOMPtr<nsIStreamListener> listener;
- rv = docLoaderFactory->CreateInstance("external-resource", chan,
- newLoadGroup,
- NS_LITERAL_CSTRING(IMAGE_SVG_XML),
- nullptr, nullptr,
- getter_AddRefs(listener),
- getter_AddRefs(viewer));
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ENSURE_TRUE(viewer, NS_ERROR_UNEXPECTED);
- // Create a navigation time object and pass it to the SVG document through
- // the viewer.
- // The timeline(DocumentTimeline, used in CSS animation) of this SVG
- // document needs this navigation timing object for time computation, such
- // as to calculate current time stamp based on the start time of navigation
- // time object.
- //
- // For a root document, DocShell would do these sort of things
- // automatically. Since there is no DocShell for this wrapped SVG document,
- // we must set it up manually.
- RefPtr<nsDOMNavigationTiming> timing = new nsDOMNavigationTiming();
- timing->NotifyNavigationStart(nsDOMNavigationTiming::DocShellState::eInactive);
- viewer->SetNavigationTiming(timing);
- nsCOMPtr<nsIParser> parser = do_QueryInterface(listener);
- NS_ENSURE_TRUE(parser, NS_ERROR_UNEXPECTED);
- // XML-only, because this is for SVG content
- nsCOMPtr<nsIContentSink> sink = parser->GetContentSink();
- NS_ENSURE_TRUE(sink, NS_ERROR_UNEXPECTED);
- listener.swap(mListener);
- viewer.forget(aViewer);
- newLoadGroup.forget(aLoadGroup);
- RegisterForXPCOMShutdown();
- return NS_OK;
- }
- void
- SVGDocumentWrapper::RegisterForXPCOMShutdown()
- {
- MOZ_ASSERT(!mRegisteredForXPCOMShutdown,
- "re-registering for XPCOM shutdown");
- // Listen for xpcom-shutdown so that we can drop references to our
- // helper-document at that point. (Otherwise, we won't get cleaned up
- // until imgLoader::Shutdown, which can happen after the JAR service
- // and RDF service have been unregistered.)
- nsresult rv;
- nsCOMPtr<nsIObserverService> obsSvc = do_GetService(OBSERVER_SVC_CID, &rv);
- if (NS_FAILED(rv) ||
- NS_FAILED(obsSvc->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID,
- true))) {
- NS_WARNING("Failed to register as observer of XPCOM shutdown");
- } else {
- mRegisteredForXPCOMShutdown = true;
- }
- }
- void
- SVGDocumentWrapper::UnregisterForXPCOMShutdown()
- {
- MOZ_ASSERT(mRegisteredForXPCOMShutdown,
- "unregistering for XPCOM shutdown w/out being registered");
- nsresult rv;
- nsCOMPtr<nsIObserverService> obsSvc = do_GetService(OBSERVER_SVC_CID, &rv);
- if (NS_FAILED(rv) ||
- NS_FAILED(obsSvc->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID))) {
- NS_WARNING("Failed to unregister as observer of XPCOM shutdown");
- } else {
- mRegisteredForXPCOMShutdown = false;
- }
- }
- void
- SVGDocumentWrapper::FlushLayout()
- {
- nsCOMPtr<nsIPresShell> presShell;
- mViewer->GetPresShell(getter_AddRefs(presShell));
- if (presShell) {
- presShell->FlushPendingNotifications(Flush_Layout);
- }
- }
- nsIDocument*
- SVGDocumentWrapper::GetDocument()
- {
- if (!mViewer) {
- return nullptr;
- }
- return mViewer->GetDocument(); // May be nullptr.
- }
- SVGSVGElement*
- SVGDocumentWrapper::GetRootSVGElem()
- {
- if (!mViewer) {
- return nullptr; // Can happen during destruction
- }
- nsIDocument* doc = mViewer->GetDocument();
- if (!doc) {
- return nullptr; // Can happen during destruction
- }
- Element* rootElem = mViewer->GetDocument()->GetRootElement();
- if (!rootElem || !rootElem->IsSVGElement(nsGkAtoms::svg)) {
- return nullptr;
- }
- return static_cast<SVGSVGElement*>(rootElem);
- }
- } // namespace image
- } // namespace mozilla
|