From d0fffcf6d310267fa8e3217bc21b3ca013587771 Mon Sep 17 00:00:00 2001 From: Kathy Brade Date: Wed, 18 Mar 2015 15:21:30 -0400 Subject: [PATCH] Bug 12827: Create preference to disable SVG. If the svg.in-content.enabled preference is false, disallow all use of SVG within content pages. In the following situations it is very difficult to determine if code is executing within a chrome context or not: SVG hasFeature() API. SVG hasExtension() API. Use of SVG glyphs within custom OpenType fonts. In these cases, everything is assumed to be content; that is, setting the pref. to false will block use of the above features from chrome as well. This is OK because these features are unlikely to be used by core browser code. Also disallow use of SVG in favicons (bug 18602). The browser does some negative caching of failed favicon loads, so SVG favicons may not begin to work on all pages immediately after svg.in-content.enabled is changed from false to true. --- dom/base/Element.cpp | 20 +++ dom/base/nsDocument.cpp | 3 + dom/base/nsIContent.h | 10 +- dom/base/nsIDocument.h | 25 +++ dom/base/nsNameSpaceManager.cpp | 3 +- dom/base/nsObjectLoadingContent.cpp | 15 +- dom/svg/nsSVGFeatures.cpp | 6 + dom/xml/nsXMLContentSink.cpp | 12 +- dom/xml/nsXMLFragmentContentSink.cpp | 4 +- dom/xslt/xslt/txMozillaXMLOutput.cpp | 15 +- gfx/thebes/gfxFont.cpp | 1 + gfx/thebes/gfxFontEntry.cpp | 5 +- image/ImageFactory.cpp | 49 ++++++ layout/base/nsCSSFrameConstructor.cpp | 15 +- layout/build/nsContentDLF.cpp | 6 +- layout/svg/moz.build | 2 + layout/svg/nsSVGUtils.cpp | 166 ++++++++++++++++++- layout/svg/nsSVGUtils.h | 4 + parser/html/nsHtml5DocumentBuilder.cpp | 25 +-- parser/html/nsHtml5TreeOpExecutor.cpp | 2 + parser/html/nsHtml5TreeOperation.cpp | 12 +- toolkit/components/places/FaviconHelpers.cpp | 8 +- uriloader/base/nsURILoader.cpp | 13 +- 23 files changed, 364 insertions(+), 57 deletions(-) diff --git a/dom/base/Element.cpp b/dom/base/Element.cpp index 886acc670a840..8d0edf6e7aebf 100644 --- a/dom/base/Element.cpp +++ b/dom/base/Element.cpp @@ -155,6 +155,26 @@ using namespace mozilla; using namespace mozilla::dom; +// These IsSVGElement() methods were moved here from nsIContent.h because including +// nsSVGUtils.h in nsIContent.h (needed to pick up the NS_SVGEnabled() +// prototype) creates a circular dependency: nsSVGUtils.h includes other +// headers that define functions that require the complete definition of +// nsPresContext... but nsPresContext.h includes nsIPresShell.h, which in turn +// includes nsIContent.h. +bool +nsIContent::IsSVGElement() const +{ + return NS_SVGEnabled(mNodeInfo->GetDocument()) && + IsInNamespace(kNameSpaceID_SVG); +} + +bool +nsIContent::IsSVGElement(nsIAtom* aTag) const +{ + return NS_SVGEnabled(mNodeInfo->GetDocument()) && + mNodeInfo->Equals(aTag, kNameSpaceID_SVG); +} + nsIAtom* nsIContent::DoGetID() const { diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index 8e6920a0e99b4..25e5d28d6c762 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -1310,6 +1310,7 @@ nsIDocument::nsIDocument() mGetUserFontSetCalled(false), mPostedFlushUserFontSet(false), mDidFireDOMContentLoaded(true), + mSVGStatus(mozilla::dom::SVGStatus_Unknown), mHasScrollLinkedEffect(false), mFrameRequestCallbacksScheduled(false), mBidiOptions(IBMBIDI_DEFAULT_BIDI_OPTIONS), @@ -2108,6 +2109,8 @@ nsDocument::ResetToURI(nsIURI *aURI, nsILoadGroup *aLoadGroup, mXMLDeclarationBits = 0; + mSVGStatus = SVGStatus_Unknown; + // Now get our new principal if (aPrincipal) { SetPrincipal(aPrincipal); diff --git a/dom/base/nsIContent.h b/dom/base/nsIContent.h index f05c47a61552c..4b3988a3b797f 100644 --- a/dom/base/nsIContent.h +++ b/dom/base/nsIContent.h @@ -274,15 +274,9 @@ public: return IsHTMLElement() && IsNodeInternal(aFirst, aArgs...); } - inline bool IsSVGElement() const - { - return IsInNamespace(kNameSpaceID_SVG); - } + bool IsSVGElement() const; - inline bool IsSVGElement(nsIAtom* aTag) const - { - return mNodeInfo->Equals(aTag, kNameSpaceID_SVG); - } + bool IsSVGElement(nsIAtom* aTag) const; template inline bool IsAnyOfSVGElements(First aFirst, Args... aArgs) const diff --git a/dom/base/nsIDocument.h b/dom/base/nsIDocument.h index 5b10c9914c175..5715fd233477f 100644 --- a/dom/base/nsIDocument.h +++ b/dom/base/nsIDocument.h @@ -166,6 +166,12 @@ template class Sequence; template class CallbackObjectHolder; typedef CallbackObjectHolder NodeFilterHolder; +typedef enum { + SVGStatus_Unknown = 0, + SVGStatus_Enabled, + SVGStatus_Disabled +} SVGStatus; + } // namespace dom } // namespace mozilla @@ -798,6 +804,22 @@ public: mSandboxFlags = sandboxFlags; } + /** + * Get the cached SVG status for this document. + */ + mozilla::dom::SVGStatus GetSVGStatus() const + { + return mSVGStatus; + } + + /** + * Set the cached SVG status for this document. + */ + void SetSVGStatus(mozilla::dom::SVGStatus svgStatus) + { + mSVGStatus = svgStatus; + } + /** * Access HTTP header data (this may also get set from other * sources, like HTML META tags). @@ -3321,6 +3343,9 @@ protected: // Our live MediaQueryLists PRCList mDOMMediaQueryLists; + // Cached value that indicates whether SVG is enabled for this document. + mozilla::dom::SVGStatus mSVGStatus; + // Flags for use counters used directly by this document. std::bitset mUseCounters; // Flags for use counters used by any child documents of this document. diff --git a/dom/base/nsNameSpaceManager.cpp b/dom/base/nsNameSpaceManager.cpp index 0130bb5d28a8f..317eee8030f39 100644 --- a/dom/base/nsNameSpaceManager.cpp +++ b/dom/base/nsNameSpaceManager.cpp @@ -20,6 +20,7 @@ #include "nsIDocument.h" #include "nsString.h" #include "mozilla/dom/NodeInfo.h" +#include "nsSVGUtils.h" #include "mozilla/ClearOnShutdown.h" #include "mozilla/dom/XBLChildrenElement.h" #include "mozilla/dom/Element.h" @@ -196,7 +197,7 @@ NS_NewElement(Element** aResult, kNameSpaceID_disabled_MathML, ni->NodeType(), ni->GetExtraName()); return NS_NewXMLElement(aResult, genericXMLNI.forget()); } - if (ns == kNameSpaceID_SVG) { + if (ns == kNameSpaceID_SVG && NS_SVGEnabled(ni->GetDocument())) { return NS_NewSVGElement(aResult, ni.forget(), aFromParser); } if (ns == kNameSpaceID_XBL && ni->Equals(nsGkAtoms::children)) { diff --git a/dom/base/nsObjectLoadingContent.cpp b/dom/base/nsObjectLoadingContent.cpp index 9e9dacf0140a5..0d2bc91249ce2 100644 --- a/dom/base/nsObjectLoadingContent.cpp +++ b/dom/base/nsObjectLoadingContent.cpp @@ -59,6 +59,7 @@ #include "nsMimeTypes.h" #include "nsStyleUtil.h" #include "nsUnicharUtils.h" +#include "nsSVGUtils.h" #include "mozilla/Preferences.h" #include "nsSandboxFlags.h" @@ -2874,8 +2875,18 @@ nsObjectLoadingContent::GetTypeOfContent(const nsCString& aMIMEType) return eType_Document; } - if ((caps & eSupportDocuments) && IsSupportedDocument(aMIMEType)) { - return eType_Document; + bool isSVG = aMIMEType.LowerCaseEqualsLiteral("image/svg+xml"); + bool isSVGEnabled = false; + if (isSVG) { + nsCOMPtr thisContent = + do_QueryInterface(static_cast(this)); + isSVGEnabled = NS_SVGEnabled(thisContent->OwnerDoc()); + } + + if (isSVGEnabled || !isSVG) { + if ((caps & eSupportDocuments) && IsSupportedDocument(aMIMEType)) { + return eType_Document; + } } RefPtr pluginHost = nsPluginHost::GetInst(); diff --git a/dom/svg/nsSVGFeatures.cpp b/dom/svg/nsSVGFeatures.cpp index c7d3bf22ac228..d3200fcfbd440 100644 --- a/dom/svg/nsSVGFeatures.cpp +++ b/dom/svg/nsSVGFeatures.cpp @@ -13,6 +13,7 @@ */ #include "nsSVGFeatures.h" +#include "nsSVGUtils.h" #include "nsIContent.h" #include "nsIDocument.h" #include "nsNameSpaceManager.h" @@ -23,6 +24,11 @@ using namespace mozilla; /*static*/ bool nsSVGFeatures::HasExtension(const nsAString& aExtension, const bool aIsInChrome) { + // Since we do not have access to the document here we pass nullptr, which + // means only the svg.in-content.enabled pref is checked. + if (!aIsInChrome && !NS_SVGEnabled(nullptr)) { + return false; + } #define SVG_SUPPORTED_EXTENSION(str) if (aExtension.EqualsLiteral(str)) return true; SVG_SUPPORTED_EXTENSION("http://www.w3.org/1999/xhtml") nsNameSpaceManager* nameSpaceManager = nsNameSpaceManager::GetInstance(); diff --git a/dom/xml/nsXMLContentSink.cpp b/dom/xml/nsXMLContentSink.cpp index 7c9d308fd60f2..344e62eea104b 100644 --- a/dom/xml/nsXMLContentSink.cpp +++ b/dom/xml/nsXMLContentSink.cpp @@ -466,8 +466,10 @@ nsXMLContentSink::CreateElement(const char16_t** aAtts, uint32_t aAttsCount, || aNodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_SVG) ) { nsCOMPtr sele = do_QueryInterface(content); - sele->SetScriptLineNumber(aLineNumber); - sele->SetCreatorParser(GetParser()); + if (sele) { + sele->SetScriptLineNumber(aLineNumber); + sele->SetCreatorParser(GetParser()); + } } // XHTML needs some special attention @@ -547,13 +549,17 @@ nsXMLContentSink::CloseElement(nsIContent* aContent) nsCOMPtr sele = do_QueryInterface(aContent); if (mPreventScriptExecution) { - sele->PreventExecution(); + if (sele) + sele->PreventExecution(); return NS_OK; } // Always check the clock in nsContentSink right after a script StopDeflecting(); + if (!sele) + return NS_OK; + // Now tell the script that it's ready to go. This may execute the script // or return true, or neither if the script doesn't need executing. bool block = sele->AttemptToExecute(); diff --git a/dom/xml/nsXMLFragmentContentSink.cpp b/dom/xml/nsXMLFragmentContentSink.cpp index 7fa815c84a84a..b2f14a3c0798d 100644 --- a/dom/xml/nsXMLFragmentContentSink.cpp +++ b/dom/xml/nsXMLFragmentContentSink.cpp @@ -229,8 +229,8 @@ nsXMLFragmentContentSink::CloseElement(nsIContent* aContent) (aContent->IsHTMLElement(nsGkAtoms::script) || aContent->IsSVGElement(nsGkAtoms::script))) { nsCOMPtr sele = do_QueryInterface(aContent); - NS_ASSERTION(sele, "script did QI correctly!"); - sele->PreventExecution(); + if (sele) + sele->PreventExecution(); } return NS_OK; } diff --git a/dom/xslt/xslt/txMozillaXMLOutput.cpp b/dom/xslt/xslt/txMozillaXMLOutput.cpp index 069413d976eae..610675e35e610 100644 --- a/dom/xslt/xslt/txMozillaXMLOutput.cpp +++ b/dom/xslt/xslt/txMozillaXMLOutput.cpp @@ -296,13 +296,14 @@ txMozillaXMLOutput::endElement() } else if (element->IsSVGElement(nsGkAtoms::script) || element->IsHTMLElement(nsGkAtoms::script)) { nsCOMPtr sele = do_QueryInterface(element); - MOZ_ASSERT(sele, "script elements need to implement nsIScriptElement"); - bool block = sele->AttemptToExecute(); - // If the act of insertion evaluated the script, we're fine. - // Else, add this script element to the array of loading scripts. - if (block) { - rv = mNotifier->AddScriptElement(sele); - NS_ENSURE_SUCCESS(rv, rv); + if (sele) { + bool block = sele->AttemptToExecute(); + // If the act of insertion evaluated the script, we're fine. + // Else, add this script element to the array of loading scripts. + if (block) { + rv = mNotifier->AddScriptElement(sele); + NS_ENSURE_SUCCESS(rv, rv); + } } } else if (element->IsAnyOfHTMLElements(nsGkAtoms::input, nsGkAtoms::button, diff --git a/gfx/thebes/gfxFont.cpp b/gfx/thebes/gfxFont.cpp index d0b747fff15e4..494a9ad5e2f56 100644 --- a/gfx/thebes/gfxFont.cpp +++ b/gfx/thebes/gfxFont.cpp @@ -37,6 +37,7 @@ #include "mozilla/Preferences.h" #include "mozilla/Services.h" #include "mozilla/Telemetry.h" +#include "nsSVGUtils.h" #include "gfxMathTable.h" #include "gfxSVGGlyphs.h" #include "gfx2DGlue.h" diff --git a/gfx/thebes/gfxFontEntry.cpp b/gfx/thebes/gfxFontEntry.cpp index f33d6a9d856ca..c85634fd3175e 100644 --- a/gfx/thebes/gfxFontEntry.cpp +++ b/gfx/thebes/gfxFontEntry.cpp @@ -357,7 +357,10 @@ gfxFontEntry::RenderSVGGlyph(gfxContext *aContext, uint32_t aGlyphId, bool gfxFontEntry::TryGetSVGData(gfxFont* aFont) { - if (!gfxPlatform::GetPlatform()->OpenTypeSVGEnabled()) { + // For the NS_SVGEnabled() check, we pass nullptr because we do not have + // access to the document here. That is OK because we do not expect + // chrome documents to use custom fonts that contain embedded SVG glyphs. + if (!gfxPlatform::GetPlatform()->OpenTypeSVGEnabled() || !NS_SVGEnabled(nullptr)) { return false; } diff --git a/image/ImageFactory.cpp b/image/ImageFactory.cpp index 428be1424e704..25d800adcfc5b 100644 --- a/image/ImageFactory.cpp +++ b/image/ImageFactory.cpp @@ -22,6 +22,7 @@ #include "Image.h" #include "nsMediaFragmentURIParser.h" #include "nsContentUtils.h" +#include "nsSVGUtils.h" #include "nsIScriptSecurityManager.h" #include "gfxPrefs.h" @@ -93,7 +94,55 @@ ImageFactory::CreateImage(nsIRequest* aRequest, uint32_t imageFlags = ComputeImageFlags(aURI, aMimeType, aIsMultiPart); // Select the type of image to create based on MIME type. + bool isBlocked = false; if (aMimeType.EqualsLiteral(IMAGE_SVG_XML)) { + nsCOMPtr channel(do_QueryInterface(aRequest)); + isBlocked = !NS_SVGEnabledForChannel(channel); + if (!isBlocked && channel && !NS_SVGEnabledForChannel(nullptr)) { + // This SVG was not blocked based on the channel but SVGs are + // disallowed from content. Check some special cases. + + // For favicons, block this SVG image load if it originated from the + // favicon xul:image node within a tab. + nsCOMPtr loadInfo; + nsresult rv = channel->GetLoadInfo(getter_AddRefs(loadInfo)); + if (NS_SUCCEEDED(rv)) { + nsINode *node = loadInfo->LoadingNode(); + if (node) { + nsCOMPtr elem(do_QueryInterface(node)); + if (elem) { + nsAutoString anonid; + elem->GetAttribute(NS_LITERAL_STRING("anonid"), anonid); + isBlocked = anonid.EqualsLiteral("tab-icon-image"); + + if (!isBlocked) { + // For the Page Info "Media" tab, block this SVG image load if + // it originated from a node within a chrome document that has + // an id of "thepreviewimage". + if (nsContentUtils::IsChromeDoc(node->OwnerDoc())) { + nsAutoString loadingID; + elem->GetId(loadingID); + isBlocked = loadingID.EqualsLiteral("thepreviewimage"); + } + } + } + } + } + } + + if (isBlocked) { + // SVG is disabled. We must return an image object that is marked + // "bad", but we want to avoid invoking the VectorImage class (SVG code), + // so we return a PNG with the error flag set. + RefPtr badImage = new RasterImage(aURI); + (void)badImage->Init(IMAGE_PNG, Image::INIT_FLAG_NONE); + if (aProgressTracker) { + aProgressTracker->SetImage(badImage); + badImage->SetProgressTracker(aProgressTracker); + } + badImage->SetHasError(); + return badImage.forget(); + } return CreateVectorImage(aRequest, aProgressTracker, aMimeType, aURI, imageFlags, aInnerWindowId); } else { diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp index a118c38f932b7..ce666756baeb4 100644 --- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -111,6 +111,7 @@ #include "nsMathMLParts.h" #include "mozilla/dom/SVGTests.h" #include "nsSVGUtils.h" +#include "nsIDOMSVGElement.h" #include "nsRefreshDriver.h" #include "nsRuleProcessorData.h" @@ -2539,7 +2540,8 @@ nsCSSFrameConstructor::ConstructDocElementFrame(Element* aDocEle else #endif if (aDocElement->IsSVGElement()) { - if (!aDocElement->IsSVGElement(nsGkAtoms::svg)) { + nsCOMPtr svgElem = do_QueryInterface(aDocElement); + if (!svgElem || !aDocElement->IsSVGElement(nsGkAtoms::svg)) { return nullptr; } // We're going to call the right function ourselves, so no need to give a @@ -5835,10 +5837,13 @@ nsCSSFrameConstructor::AddFrameConstructionItemsInternal(nsFrameConstructorState data = FindMathMLData(element, aTag, aNameSpaceID, styleContext); } if (!data) { - data = FindSVGData(element, aTag, aNameSpaceID, aParentFrame, - aFlags & ITEM_IS_WITHIN_SVG_TEXT, - aFlags & ITEM_ALLOWS_TEXT_PATH_CHILD, - styleContext); + nsCOMPtr svgElem = do_QueryInterface(element); + if (svgElem) { + data = FindSVGData(element, aTag, aNameSpaceID, aParentFrame, + aFlags & ITEM_IS_WITHIN_SVG_TEXT, + aFlags & ITEM_ALLOWS_TEXT_PATH_CHILD, + styleContext); + } } // Now check for XUL display types diff --git a/layout/build/nsContentDLF.cpp b/layout/build/nsContentDLF.cpp index 5c4cef21030cd..26675e317d9d4 100644 --- a/layout/build/nsContentDLF.cpp +++ b/layout/build/nsContentDLF.cpp @@ -24,6 +24,7 @@ #include "nsCRT.h" #include "nsIViewSourceChannel.h" #include "nsContentUtils.h" +#include "nsSVGUtils.h" #include "imgLoader.h" #include "nsCharsetSource.h" #include "nsMimeTypes.h" @@ -161,7 +162,7 @@ nsContentDLF::CreateInstance(const char* aCommand, IsTypeInList(type, gHTMLTypes)) || nsContentUtils::IsPlainTextType(type) || IsTypeInList(type, gXMLTypes) || - IsTypeInList(type, gSVGTypes) || + (NS_SVGEnabledForChannel(aChannel) && IsTypeInList(type, gSVGTypes)) || IsTypeInList(type, gXMLTypes); if (knownType) { @@ -196,7 +197,8 @@ nsContentDLF::CreateInstance(const char* aCommand, } // Try SVG - if (IsTypeInList(contentType, gSVGTypes)) { + if (NS_SVGEnabledForChannel(aChannel) && + IsTypeInList(contentType, gSVGTypes)) { return CreateDocument(aCommand, aChannel, aLoadGroup, aContainer, kSVGDocumentCID, diff --git a/layout/svg/moz.build b/layout/svg/moz.build index b1481f4ef216a..a0dd4f6ae890f 100644 --- a/layout/svg/moz.build +++ b/layout/svg/moz.build @@ -60,6 +60,8 @@ if CONFIG['CPU_ARCH'] == 'arm' and CONFIG['BUILD_ARM_NEON']: SOURCES += ['nsSVGMaskFrameNEON.cpp'] SOURCES['nsSVGMaskFrameNEON.cpp'].flags += CONFIG['NEON_FLAGS'] +#DEFINES['DEBUG_SVG_ENABLE'] = True + FINAL_LIBRARY = 'xul' LOCAL_INCLUDES += [ '../../widget', diff --git a/layout/svg/nsSVGUtils.cpp b/layout/svg/nsSVGUtils.cpp index ff74d5bafc8ee..98dd287ebec77 100644 --- a/layout/svg/nsSVGUtils.cpp +++ b/layout/svg/nsSVGUtils.cpp @@ -51,19 +51,164 @@ #include "nsSVGPaintServerFrame.h" #include "mozilla/dom/SVGSVGElement.h" #include "nsTextFrame.h" +#include "nsNetUtil.h" +#include "nsContentUtils.h" #include "SVGContentUtils.h" #include "SVGTextFrame.h" #include "mozilla/Unused.h" +#include "nsIDocument.h" +#include "mozIThirdPartyUtil.h" using namespace mozilla; using namespace mozilla::dom; using namespace mozilla::gfx; +static bool sSVGEnabledInContent; static bool sSVGPathCachingEnabled; static bool sSVGDisplayListHitTestingEnabled; static bool sSVGDisplayListPaintingEnabled; static bool sSVGNewGetBBoxEnabled; +static bool IsWhitelisted(nsIURI *aURI); + +// Determine if SVG should be enabled for aDoc. The svg.in-content.enabled +// preference is checked, then we check whether aDoc is a chrome doc, and +// finally as a fallback we check whether the top-level document is +// whitelisted (e.g., about:preferences). +// If aDoc is NULL, the pref. value is returned. +// Once we determine whether SVG is allowed for a given document, we record +// that fact inside the document. This is necessary to avoid crashes due +// to code that uses static_cast to cast an element object to an nsSVGElement +// object. When SVG is disabled, and related tags are not represented +// by nsSVGElement objects. +bool +NS_SVGEnabled(nsIDocument *aDoc) +{ + if (!aDoc) + return NS_SVGEnabledForChannel(nullptr); + + mozilla::dom::SVGStatus svgStatus = aDoc->GetSVGStatus(); + if (svgStatus == mozilla::dom::SVGStatus_Unknown) + { + svgStatus = NS_SVGEnabledForChannel(aDoc->GetChannel()) ? + mozilla::dom::SVGStatus_Enabled : mozilla::dom::SVGStatus_Disabled; + aDoc->SetSVGStatus(svgStatus); + } + + return (svgStatus == mozilla::dom::SVGStatus_Enabled); +} + +// Determine if SVG should be enabled for aChannel. The svg.in-content.enabled +// preference is checked, then we check whether the load context associated +// with aChannel is a chrome doc, and finally as a fallback we check whether +// the top-level document is whitelisted (e.g., about:preferences). +// If aChannel is NULL, the pref. value is returned. +bool +NS_SVGEnabledForChannel(nsIChannel *aChannel) +{ + if (sSVGEnabledInContent) + return true; + + if (!aChannel) + return false; + +#ifdef DEBUG_SVG_ENABLE + nsAutoCString topDocSpec; // Set if approved via a whitelisted top doc. + bool checkedSystemPrincipal = false; +#endif + + bool isSVGAllowed = false; + nsCOMPtr ctx; + NS_QueryNotificationCallbacks(aChannel, ctx); + if (ctx) { + bool isContent = true; + ctx->GetIsContent(&isContent); + if (!isContent) { + isSVGAllowed = true; + } else { + // Disallowed. As a fallback, check for whitelisted URLs. + uint32_t loadFlags = 0; + aChannel->GetLoadFlags(&loadFlags); + if (loadFlags & nsIChannel::LOAD_INITIAL_DOCUMENT_URI) { + // This is the top-level load; just check the channel's URL. + nsCOMPtr uri; + aChannel->GetOriginalURI(getter_AddRefs(uri)); + isSVGAllowed = IsWhitelisted(uri); +#ifdef DEBUG_SVG_ENABLE + if (isSVGAllowed) + uri->GetSpec(topDocSpec); +#endif + } else { + // Obtain the top window and check it's URI. + nsCOMPtr topWin; + ctx->GetTopWindow(getter_AddRefs(topWin)); + nsCOMPtr topDocURI; + if (topWin) { + nsCOMPtr util = do_GetService(THIRDPARTYUTIL_CONTRACTID); + if (util) { + nsresult rv = util->GetURIFromWindow(topWin, getter_AddRefs(topDocURI)); + if (NS_SUCCEEDED(rv) && topDocURI) { + isSVGAllowed = IsWhitelisted(topDocURI); +#ifdef DEBUG_SVG_ENABLE + if (isSVGAllowed) + topDocURI->GetSpec(topDocSpec); +#endif + } else { + // Unable to retrieve the top window's URI. Fallback to checking + // the system principal (see bug 21962). +#ifdef DEBUG_SVG_ENABLE + checkedSystemPrincipal = true; +#endif + nsCOMPtr scriptObjPrin = + do_QueryInterface(topWin); + if (scriptObjPrin) { + isSVGAllowed = nsContentUtils::IsSystemPrincipal( + scriptObjPrin->GetPrincipal()); + } + } + } + } + } + } + } + +#ifdef DEBUG_SVG_ENABLE + nsAutoCString spec; + nsCOMPtr uri; + aChannel->GetOriginalURI(getter_AddRefs(uri)); + if (uri) + uri->GetSpec(spec); + + if (checkedSystemPrincipal) { + printf("NS_SVGEnabledForChannel for %s: %s (via system principal check)\n", + spec.get(), isSVGAllowed ? "YES" : "NO"); + } else if (topDocSpec.IsEmpty()) { + printf("NS_SVGEnabledForChannel for %s: %s\n", spec.get(), + isSVGAllowed ? "YES" : "NO"); + } else { + printf("NS_SVGEnabledForChannel for %s: %s (via whitelisted top doc %s)\n", + spec.get(), isSVGAllowed ? "YES" : "NO", topDocSpec.get()); + } +#endif + + return isSVGAllowed; +} + +// Always allow SVG for about: URLs so that about:preferences and other +// built-in browser pages function correctly. Exclude about:blank because +// iframes initially contain about:blank documents, and since we cache the +// SVG status we must avoid enabling SVG in that case. +static bool +IsWhitelisted(nsIURI *aURI) +{ + if (!aURI) + return false; + + nsAutoCString scheme; + aURI->GetScheme(scheme); + return scheme.EqualsLiteral("about") && !NS_IsAboutBlank(aURI); +} + bool NS_SVGPathCachingEnabled() { @@ -134,6 +279,9 @@ SVGAutoRenderState::IsPaintingToWindow(DrawTarget* aDrawTarget) void nsSVGUtils::Init() { + Preferences::AddBoolVarCache(&sSVGEnabledInContent, + "svg.in-content.enabled"); + Preferences::AddBoolVarCache(&sSVGPathCachingEnabled, "svg.path-caching.enabled"); @@ -1093,9 +1241,10 @@ nsSVGUtils::GetBBox(nsIFrame *aFrame, uint32_t aFlags) // needs investigation to check that we won't break too much content. // NOTE: When changing this to apply to other frame types, make sure to // also update nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset. - MOZ_ASSERT(content->IsSVGElement(), "bad cast"); - nsSVGElement *element = static_cast(content); - matrix = element->PrependLocalTransformsTo(matrix, eChildToUserSpace); + if (content->IsSVGElement()) { + nsSVGElement *element = static_cast(content); + matrix = element->PrependLocalTransformsTo(matrix, eChildToUserSpace); + } } bbox = svg->GetBBoxContribution(ToMatrix(matrix), aFlags).ToThebesRect(); // Account for 'clipped'. @@ -1301,7 +1450,9 @@ nsSVGUtils::GetNonScalingStrokeTransform(nsIFrame *aFrame, } nsIContent *content = aFrame->GetContent(); - MOZ_ASSERT(content->IsSVGElement(), "bad cast"); + if (!content->IsSVGElement()) { + return false; + } *aUserToOuterSVG = ThebesMatrix(SVGContentUtils::GetCTM( static_cast(content), true)); @@ -1597,6 +1748,10 @@ nsSVGUtils::GetStrokeWidth(nsIFrame* aFrame, SVGContextPaint* aContextPaint) content = content->GetParent(); } + if (!aFrame->GetContent()->IsSVGElement()) { + return 0.0; + } + nsSVGElement *ctx = static_cast(content); return SVGContentUtils::CoordToFloat(ctx, style->mStrokeWidth); @@ -1610,6 +1765,9 @@ GetStrokeDashData(nsIFrame* aFrame, { const nsStyleSVG* style = aFrame->StyleSVG(); nsIContent *content = aFrame->GetContent(); + if (!content->IsSVGElement()) { + return false; + } nsSVGElement *ctx = static_cast (content->IsNodeOfType(nsINode::eTEXT) ? content->GetParent() : content); diff --git a/layout/svg/nsSVGUtils.h b/layout/svg/nsSVGUtils.h index edfc43bee7dcb..786714e11a583 100644 --- a/layout/svg/nsSVGUtils.h +++ b/layout/svg/nsSVGUtils.h @@ -28,6 +28,7 @@ class gfxContext; class nsFrameList; +class nsIChannel; class nsIContent; class nsIDocument; class nsIFrame; @@ -74,6 +75,9 @@ bool NS_SVGDisplayListHitTestingEnabled(); bool NS_SVGDisplayListPaintingEnabled(); bool NS_SVGNewGetBBoxEnabled(); +bool NS_SVGEnabled(nsIDocument *aDoc); +bool NS_SVGEnabledForChannel(nsIChannel *aChannel); + /** * Sometimes we need to distinguish between an empty box and a box * that contains an element that has no size e.g. a point at the origin. diff --git a/parser/html/nsHtml5DocumentBuilder.cpp b/parser/html/nsHtml5DocumentBuilder.cpp index ba8a333c49c34..7a47c958eb5ea 100644 --- a/parser/html/nsHtml5DocumentBuilder.cpp +++ b/parser/html/nsHtml5DocumentBuilder.cpp @@ -67,18 +67,19 @@ nsHtml5DocumentBuilder::UpdateStyleSheet(nsIContent* aElement) } nsCOMPtr ssle(do_QueryInterface(aElement)); - NS_ASSERTION(ssle, "Node didn't QI to style."); - - ssle->SetEnableUpdates(true); - - bool willNotify; - bool isAlternate; - nsresult rv = ssle->UpdateStyleSheet(mRunsToCompletion ? nullptr : this, - &willNotify, - &isAlternate); - if (NS_SUCCEEDED(rv) && willNotify && !isAlternate && !mRunsToCompletion) { - ++mPendingSheetCount; - mScriptLoader->AddParserBlockingScriptExecutionBlocker(); + + if (ssle) { + ssle->SetEnableUpdates(true); + + bool willNotify; + bool isAlternate; + nsresult rv = ssle->UpdateStyleSheet(mRunsToCompletion ? nullptr : this, + &willNotify, + &isAlternate); + if (NS_SUCCEEDED(rv) && willNotify && !isAlternate && !mRunsToCompletion) { + ++mPendingSheetCount; + mScriptLoader->AddParserBlockingScriptExecutionBlocker(); + } } // Re-open update diff --git a/parser/html/nsHtml5TreeOpExecutor.cpp b/parser/html/nsHtml5TreeOpExecutor.cpp index b0eabb13d6e34..0cd1e84ca2e09 100644 --- a/parser/html/nsHtml5TreeOpExecutor.cpp +++ b/parser/html/nsHtml5TreeOpExecutor.cpp @@ -639,6 +639,8 @@ nsHtml5TreeOpExecutor::RunScript(nsIContent* aScriptElement) NS_ASSERTION(aScriptElement, "No script to run"); nsCOMPtr sele = do_QueryInterface(aScriptElement); + if (!sele) + return; if (!mParser) { NS_ASSERTION(sele->IsMalformed(), "Script wasn't marked as malformed."); diff --git a/parser/html/nsHtml5TreeOperation.cpp b/parser/html/nsHtml5TreeOperation.cpp index af246a2539298..42896273835d4 100644 --- a/parser/html/nsHtml5TreeOperation.cpp +++ b/parser/html/nsHtml5TreeOperation.cpp @@ -596,8 +596,8 @@ void nsHtml5TreeOperation::PreventScriptExecution(nsIContent* aNode) { nsCOMPtr sele = do_QueryInterface(aNode); - MOZ_ASSERT(sele); - sele->PreventExecution(); + if (sele) + sele->PreventExecution(); } void @@ -824,14 +824,18 @@ nsHtml5TreeOperation::Perform(nsHtml5TreeOpExecutor* aBuilder, case eTreeOpSetStyleLineNumber: { nsIContent* node = *(mOne.node); nsCOMPtr ssle = do_QueryInterface(node); - NS_ASSERTION(ssle, "Node didn't QI to style."); + if (!ssle) + return NS_ERROR_DOM_NOT_SUPPORTED_ERR; + ssle->SetLineNumber(mFour.integer); return NS_OK; } case eTreeOpSetScriptLineNumberAndFreeze: { nsIContent* node = *(mOne.node); nsCOMPtr sele = do_QueryInterface(node); - NS_ASSERTION(sele, "Node didn't QI to script."); + if (!sele) + return NS_ERROR_DOM_NOT_SUPPORTED_ERR; + sele->SetScriptLineNumber(mFour.integer); sele->FreezeUriAsyncDefer(); return NS_OK; diff --git a/toolkit/components/places/FaviconHelpers.cpp b/toolkit/components/places/FaviconHelpers.cpp index 69c2023380283..b25ef675a5c88 100644 --- a/toolkit/components/places/FaviconHelpers.cpp +++ b/toolkit/components/places/FaviconHelpers.cpp @@ -22,6 +22,7 @@ #include "nsISupportsPriority.h" #include "nsContentUtils.h" #include +#include "nsSVGUtils.h" using namespace mozilla::places; using namespace mozilla::storage; @@ -569,8 +570,11 @@ AsyncFetchAndSetIconForPage::OnStopRequest(nsIRequest* aRequest, mIcon.mimeType); } - // If the icon does not have a valid MIME type, add it to the failed cache. - if (mIcon.mimeType.IsEmpty()) { + // If the icon does not have a valid MIME type, or if it is an SVG and + // SVG images are disabled for content, add it to the failed cache. + if (mIcon.mimeType.IsEmpty() || + (mIcon.mimeType.EqualsLiteral("image/svg+xml") + && !NS_SVGEnabled(nullptr))) { nsCOMPtr iconURI; rv = NS_NewURI(getter_AddRefs(iconURI), mIcon.spec); NS_ENSURE_SUCCESS(rv, rv); diff --git a/uriloader/base/nsURILoader.cpp b/uriloader/base/nsURILoader.cpp index 69475d68fdcfc..b59e7bd6c801b 100644 --- a/uriloader/base/nsURILoader.cpp +++ b/uriloader/base/nsURILoader.cpp @@ -36,6 +36,7 @@ #include "nsString.h" #include "nsThreadUtils.h" #include "nsReadableUtils.h" +#include "nsSVGUtils.h" #include "nsError.h" #include "nsICategoryManager.h" @@ -470,11 +471,15 @@ nsresult nsDocumentOpenInfo::DispatchContent(nsIRequest *request, nsISupports * // // Fourth step: try to find an nsIContentHandler for our type. // - nsAutoCString handlerContractID (NS_CONTENT_HANDLER_CONTRACTID_PREFIX); - handlerContractID += mContentType; + nsCOMPtr contentHandler; + if (!mContentType.EqualsASCII(IMAGE_SVG_XML) || + NS_SVGEnabledForChannel(aChannel)) { + nsAutoCString handlerContractID (NS_CONTENT_HANDLER_CONTRACTID_PREFIX); + handlerContractID += mContentType; + + contentHandler = do_CreateInstance(handlerContractID.get()); + } - nsCOMPtr contentHandler = - do_CreateInstance(handlerContractID.get()); if (contentHandler) { LOG((" Content handler found")); rv = contentHandler->HandleContent(mContentType.get(), -- GitLab