commit 7dd093abb5c68728b5ba6c940cca224345d89f3e Author: Kathy Brade brade@pearlcrescent.com Date: Wed Mar 18 15:21:30 2015 -0400
Bug 12827: Create preference to disable SVG.
If the svg.inContent.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. --- browser/app/profile/000-tor-browser.js | 1 + content/base/src/nsNameSpaceManager.cpp | 3 ++- content/base/src/nsObjectLoadingContent.cpp | 16 +++++++++--- content/svg/content/src/nsSVGFeatures.cpp | 13 ++++++++++ gfx/thebes/gfxFont.cpp | 6 ++++- image/src/ImageFactory.cpp | 33 ++++++++++++++++-------- layout/base/nsCSSFrameConstructor.cpp | 15 +++++++---- layout/build/nsContentDLF.cpp | 27 +++++++++++-------- layout/svg/nsSVGUtils.cpp | 37 +++++++++++++++++++++++++++ layout/svg/nsSVGUtils.h | 3 +++ uriloader/base/nsURILoader.cpp | 13 +++++++--- 11 files changed, 131 insertions(+), 36 deletions(-)
diff --git a/browser/app/profile/000-tor-browser.js b/browser/app/profile/000-tor-browser.js index ceb9394..e87ccff 100644 --- a/browser/app/profile/000-tor-browser.js +++ b/browser/app/profile/000-tor-browser.js @@ -116,6 +116,7 @@ pref("plugins.click_to_play", true); pref("plugin.state.flash", 1); pref("plugins.hide_infobar_for_missing_plugin", true); pref("media.peerconnection.enabled", false); // Disable WebRTC interfaces +pref("svg.inContent.enabled", true); // May be disabled via Torbutton's security slider.
// Network and performance pref("network.http.pipelining", true); diff --git a/content/base/src/nsNameSpaceManager.cpp b/content/base/src/nsNameSpaceManager.cpp index 409e08c..3f4f9f8 100644 --- a/content/base/src/nsNameSpaceManager.cpp +++ b/content/base/src/nsNameSpaceManager.cpp @@ -17,6 +17,7 @@ #include "nsContentCreatorFunctions.h" #include "nsString.h" #include "nsINodeInfo.h" +#include "nsSVGUtils.h" #include "mozilla/ClearOnShutdown.h" #include "mozilla/dom/XBLChildrenElement.h" #include "mozilla/dom/Element.h" @@ -153,7 +154,7 @@ NS_NewElement(Element** aResult, if (ns == kNameSpaceID_MathML) { return NS_NewMathMLElement(aResult, ni.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/content/base/src/nsObjectLoadingContent.cpp b/content/base/src/nsObjectLoadingContent.cpp index 8d9bdaa..d14c83e 100644 --- a/content/base/src/nsObjectLoadingContent.cpp +++ b/content/base/src/nsObjectLoadingContent.cpp @@ -57,6 +57,7 @@ #include "nsMimeTypes.h" #include "nsStyleUtil.h" #include "nsUnicharUtils.h" +#include "nsSVGUtils.h" #include "mozilla/Preferences.h" #include "nsSandboxFlags.h"
@@ -2530,9 +2531,18 @@ nsObjectLoadingContent::GetTypeOfContent(const nsCString& aMIMEType)
// SVGs load as documents, but are their own capability bool isSVG = aMIMEType.LowerCaseEqualsLiteral("image/svg+xml"); - Capabilities supportType = isSVG ? eSupportSVG : eSupportDocuments; - if ((caps & supportType) && IsSupportedDocument(aMIMEType)) { - return eType_Document; + bool isSVGEnabled = false; + if (isSVG) { + nsCOMPtr<nsIContent> thisContent = + do_QueryInterface(static_cast<nsIImageLoadingContent*>(this)); + isSVGEnabled = NS_SVGEnabled(thisContent->OwnerDoc()); + } + + if (isSVGEnabled || !isSVG) { + Capabilities supportType = isSVG ? eSupportSVG : eSupportDocuments; + if ((caps & supportType) && IsSupportedDocument(aMIMEType)) { + return eType_Document; + } }
if (caps & eSupportPlugins && PluginExistsForType(aMIMEType.get())) { diff --git a/content/svg/content/src/nsSVGFeatures.cpp b/content/svg/content/src/nsSVGFeatures.cpp index 153a00c..04e9917 100644 --- a/content/svg/content/src/nsSVGFeatures.cpp +++ b/content/svg/content/src/nsSVGFeatures.cpp @@ -13,6 +13,7 @@ */
#include "nsSVGFeatures.h" +#include "nsSVGUtils.h" #include "nsIContent.h" #include "nsIDocument.h" #include "mozilla/Preferences.h" @@ -22,6 +23,12 @@ using namespace mozilla; /*static*/ bool nsSVGFeatures::HasFeature(nsISupports* aObject, const nsAString& aFeature) { + // Since we do not have access to the document here we pass nullptr, which + // means only the svg.inContent.enabled pref is checked. This is OK since + // we do not expect chrome code to use the HasFeature() API. + if (!NS_SVGEnabled(nullptr)) + return false; + if (aFeature.EqualsLiteral("http://www.w3.org/TR/SVG11/feature#Script")) { nsCOMPtr<nsIContent> content(do_QueryInterface(aObject)); if (content) { @@ -44,6 +51,12 @@ nsSVGFeatures::HasFeature(nsISupports* aObject, const nsAString& aFeature) /*static*/ bool nsSVGFeatures::HasExtension(const nsAString& aExtension) { + // Since we do not have access to the document here we pass nullptr, which + // means only the svg.inContent.enabled pref is checked. This is OK since + // we do not expect chrome code to use the HasExtension() API. + if (!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") SVG_SUPPORTED_EXTENSION("http://www.w3.org/1998/Math/MathML") diff --git a/gfx/thebes/gfxFont.cpp b/gfx/thebes/gfxFont.cpp index c5e2957..da25374 100644 --- a/gfx/thebes/gfxFont.cpp +++ b/gfx/thebes/gfxFont.cpp @@ -38,6 +38,7 @@ #include "mozilla/Preferences.h" #include "mozilla/Services.h" #include "mozilla/Telemetry.h" +#include "nsSVGUtils.h" #include "gfxSVGGlyphs.h" #include "gfxMathTable.h" #include "gfx2DGlue.h" @@ -364,7 +365,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/src/ImageFactory.cpp b/image/src/ImageFactory.cpp index 2daba13..578f95c 100644 --- a/image/src/ImageFactory.cpp +++ b/image/src/ImageFactory.cpp @@ -20,6 +20,7 @@ #include "Image.h" #include "nsMediaFragmentURIParser.h" #include "nsContentUtils.h" +#include "nsSVGUtils.h" #include "nsIScriptSecurityManager.h"
#include "ImageFactory.h" @@ -110,6 +111,17 @@ ImageFactory::CanRetargetOnDataAvailable(ImageURL* aURI, bool aIsMultiPart) return true; }
+// Marks an image as having an error before returning it. Used with macros like +// NS_ENSURE_SUCCESS, since we guarantee to always return an image even if an +// error occurs, but callers need to be able to tell that this happened. +template <typename T> +static already_AddRefed<Image> +BadImage(nsRefPtr<T>& image) +{ + image->SetHasError(); + return image.forget(); +} + /* static */ already_AddRefed<Image> ImageFactory::CreateImage(nsIRequest* aRequest, imgStatusTracker* aStatusTracker, @@ -126,6 +138,16 @@ ImageFactory::CreateImage(nsIRequest* aRequest,
// Select the type of image to create based on MIME type. if (aMimeType.EqualsLiteral(IMAGE_SVG_XML)) { + nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest)); + if (!NS_SVGEnabledForChannel(channel)) { + // 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. + nsRefPtr<RasterImage> badImage = new RasterImage(aStatusTracker, aURI); + (void)badImage->Init(IMAGE_PNG, Image::INIT_FLAG_NONE); + return BadImage(badImage); + } + return CreateVectorImage(aRequest, aStatusTracker, aMimeType, aURI, imageFlags, aInnerWindowId); } else { @@ -134,17 +156,6 @@ ImageFactory::CreateImage(nsIRequest* aRequest, } }
-// Marks an image as having an error before returning it. Used with macros like -// NS_ENSURE_SUCCESS, since we guarantee to always return an image even if an -// error occurs, but callers need to be able to tell that this happened. -template <typename T> -static already_AddRefed<Image> -BadImage(nsRefPtr<T>& image) -{ - image->SetHasError(); - return image.forget(); -} - /* static */ already_AddRefed<Image> ImageFactory::CreateAnonymousImage(const nsCString& aMimeType) { diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp index 5cab7ca..786e3c2 100644 --- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -89,6 +89,7 @@ #include "nsMathMLParts.h" #include "mozilla/dom/SVGTests.h" #include "nsSVGUtils.h" +#include "nsIDOMSVGElement.h"
#include "nsRefreshDriver.h" #include "nsRuleProcessorData.h" @@ -2423,7 +2424,8 @@ nsCSSFrameConstructor::ConstructDocElementFrame(Element* aDocEle else #endif if (aDocElement->IsSVG()) { - if (aDocElement->Tag() != nsGkAtoms::svg) { + nsCOMPtr<nsIDOMSVGElement> svgElem = do_QueryInterface(aDocElement); + if (!svgElem || (aDocElement->Tag() != nsGkAtoms::svg)) { return nullptr; } // We're going to call the right function ourselves, so no need to give a @@ -5296,10 +5298,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<nsIDOMSVGElement> 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 d3081dd..1275590 100644 --- a/layout/build/nsContentDLF.cpp +++ b/layout/build/nsContentDLF.cpp @@ -25,6 +25,7 @@ #include "nsCRT.h" #include "nsIViewSourceChannel.h" #include "nsContentUtils.h" +#include "nsSVGUtils.h" #include "imgLoader.h" #include "nsCharsetSource.h" #include "nsMimeTypes.h" @@ -171,9 +172,11 @@ nsContentDLF::CreateInstance(const char* aCommand, } }
- for (typeIndex = 0; gSVGTypes[typeIndex] && !knownType; ++typeIndex) { - if (type.Equals(gSVGTypes[typeIndex])) { - knownType = true; + if (NS_SVGEnabledForChannel(aChannel)) { + for (typeIndex = 0; gSVGTypes[typeIndex] && !knownType; ++typeIndex) { + if (type.Equals(gSVGTypes[typeIndex])) { + knownType = true; + } } }
@@ -218,14 +221,16 @@ nsContentDLF::CreateInstance(const char* aCommand, } }
- // Try SVG - typeIndex = 0; - while(gSVGTypes[typeIndex]) { - if (!PL_strcmp(gSVGTypes[typeIndex++], aContentType)) { - return CreateDocument(aCommand, - aChannel, aLoadGroup, - aContainer, kSVGDocumentCID, - aDocListener, aDocViewer); + if (NS_SVGEnabledForChannel(aChannel)) { + // Try SVG + typeIndex = 0; + while(gSVGTypes[typeIndex]) { + if (!PL_strcmp(gSVGTypes[typeIndex++], aContentType)) { + return CreateDocument(aCommand, + aChannel, aLoadGroup, + aContainer, kSVGDocumentCID, + aDocListener, aDocViewer); + } } }
diff --git a/layout/svg/nsSVGUtils.cpp b/layout/svg/nsSVGUtils.cpp index 3ebfbd7..da55070 100644 --- a/layout/svg/nsSVGUtils.cpp +++ b/layout/svg/nsSVGUtils.cpp @@ -48,6 +48,8 @@ #include "nsSVGPaintServerFrame.h" #include "mozilla/dom/SVGSVGElement.h" #include "nsTextFrame.h" +#include "nsNetUtil.h" +#include "nsContentUtils.h" #include "SVGContentUtils.h" #include "mozilla/unused.h"
@@ -55,9 +57,41 @@ using namespace mozilla; using namespace mozilla::dom; using namespace mozilla::gfx;
+static bool sSVGEnabledInContent; static bool sSVGDisplayListHitTestingEnabled; static bool sSVGDisplayListPaintingEnabled;
+// Determine if SVG should be enabled for aDoc. The svg.inContent.enabled +// preference is checked as well as whether aDoc is a content or chrome doc. +// If aChannel is NULL, the pref. value is returned. +bool +NS_SVGEnabled(nsIDocument *aDoc) +{ + return NS_SVGEnabledForChannel(aDoc ? aDoc->GetChannel() : nullptr); +} + +// Determine if SVG should be enabled for aChannel. The svg.inContent.enabled +// preference is checked as well as whether the load context associated with +// aChannel is content or chrome. +// If aChannel is NULL, the pref. value is returned. +bool +NS_SVGEnabledForChannel(nsIChannel *aChannel) +{ + if (sSVGEnabledInContent) + return true; + + if (!aChannel) + return false; + + bool isContent = true; + nsCOMPtr<nsILoadContext> ctx; + NS_QueryNotificationCallbacks(aChannel, ctx); + if (ctx) + ctx->GetIsContent(&isContent); + + return !isContent; +} + bool NS_SVGDisplayListHitTestingEnabled() { @@ -125,6 +159,9 @@ SVGAutoRenderState::IsPaintingToWindow(nsRenderingContext *aContext) void nsSVGUtils::Init() { + Preferences::AddBoolVarCache(&sSVGEnabledInContent, + "svg.inContent.enabled"); + Preferences::AddBoolVarCache(&sSVGDisplayListHitTestingEnabled, "svg.display-lists.hit-testing.enabled");
diff --git a/layout/svg/nsSVGUtils.h b/layout/svg/nsSVGUtils.h index 5f99fa7..0cd67b8 100644 --- a/layout/svg/nsSVGUtils.h +++ b/layout/svg/nsSVGUtils.h @@ -77,6 +77,9 @@ class SourceSurface; bool NS_SVGDisplayListHitTestingEnabled(); bool NS_SVGDisplayListPaintingEnabled();
+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/uriloader/base/nsURILoader.cpp b/uriloader/base/nsURILoader.cpp index e1545f2..1871784 100644 --- a/uriloader/base/nsURILoader.cpp +++ b/uriloader/base/nsURILoader.cpp @@ -37,6 +37,7 @@ #include "nsNetUtil.h" #include "nsThreadUtils.h" #include "nsReadableUtils.h" +#include "nsSVGUtils.h" #include "nsError.h"
#include "nsICategoryManager.h" @@ -442,11 +443,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<nsIContentHandler> 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<nsIContentHandler> contentHandler = - do_CreateInstance(handlerContractID.get()); if (contentHandler) { LOG((" Content handler found")); rv = contentHandler->HandleContent(mContentType.get(),
tbb-commits@lists.torproject.org