Backed out changeset 496886cb30a5 (bug 1867152) for bc failures on browser_user_input...
[gecko.git] / layout / svg / SVGContextPaint.cpp
blob41d2c8af83e7d66fd75589f5d9024e34eafe8241
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "SVGContextPaint.h"
9 #include "gfxContext.h"
10 #include "gfxUtils.h"
11 #include "mozilla/gfx/2D.h"
12 #include "mozilla/BasePrincipal.h"
13 #include "mozilla/dom/Document.h"
14 #include "mozilla/extensions/WebExtensionPolicy.h"
15 #include "mozilla/StaticPrefs_svg.h"
16 #include "mozilla/SVGObserverUtils.h"
17 #include "mozilla/SVGUtils.h"
18 #include "SVGPaintServerFrame.h"
20 using namespace mozilla::gfx;
21 using namespace mozilla::image;
23 namespace mozilla {
25 using image::imgDrawingParams;
27 /* static */
28 bool SVGContextPaint::IsAllowedForImageFromURI(nsIURI* aURI) {
29 if (StaticPrefs::svg_context_properties_content_enabled()) {
30 return true;
33 // Context paint is pref'ed off for Web content. Ideally we'd have some
34 // easy means to determine whether the frame that has linked to the image
35 // is a frame for a content node that originated from Web content.
36 // Unfortunately different types of anonymous content, about: documents
37 // such as about:reader, etc. that are "our" code that we ship are
38 // sometimes hard to distinguish from real Web content. As a result,
39 // instead of trying to figure out what content is "ours" we instead let
40 // any content provide image context paint, but only if the image is
41 // chrome:// or resource:// do we return true. This should be sufficient
42 // to stop the image context paint feature being useful to (and therefore
43 // used by and relied upon by) Web content. (We don't want Web content to
44 // use this feature because we're not sure that image context paint is a
45 // good mechanism for wider use, or suitable for specification.)
47 // Because the default favicon used in the browser UI needs context paint, we
48 // also allow it for:
49 // - page-icon:<page-url> (used in history and bookmark items)
50 // - moz-anno:favicon:<page-url> (used in the awesomebar)
51 // This allowance does also inadvertently expose context-paint to 3rd-party
52 // favicons, which is not great, but that hasn't caused trouble as far as we
53 // know. Also: other places such as the tab bar don't use these protocols to
54 // load favicons, so they help to ensure that 3rd-party favicons don't grow
55 // to depend on this feature.
57 // One case that is not covered by chrome:// or resource:// are WebExtensions,
58 // specifically ones that are "ours". WebExtensions are moz-extension://
59 // regardless if the extension is in-tree or not. Since we don't want
60 // extension developers coming to rely on image context paint either, we only
61 // enable context-paint for extensions that are owned by Mozilla
62 // (based on the extension permission "internal:svgContextPropertiesAllowed").
64 // We also allow this for browser UI icons that are served up from
65 // Mozilla-controlled domains listed in the
66 // svg.context-properties.content.allowed-domains pref.
68 nsAutoCString scheme;
69 if (NS_SUCCEEDED(aURI->GetScheme(scheme)) &&
70 (scheme.EqualsLiteral("chrome") || scheme.EqualsLiteral("resource") ||
71 scheme.EqualsLiteral("page-icon") || scheme.EqualsLiteral("moz-anno"))) {
72 return true;
74 RefPtr<BasePrincipal> principal =
75 BasePrincipal::CreateContentPrincipal(aURI, OriginAttributes());
77 RefPtr<extensions::WebExtensionPolicy> addonPolicy = principal->AddonPolicy();
78 if (addonPolicy) {
79 // Only allowed for extensions that have the
80 // internal:svgContextPropertiesAllowed permission (added internally from
81 // to Mozilla-owned extensions, see `isMozillaExtension` function
82 // defined in Extension.jsm for the exact criteria).
83 return addonPolicy->HasPermission(
84 nsGkAtoms::svgContextPropertiesAllowedPermission);
87 bool isInAllowList = false;
88 principal->IsURIInPrefList("svg.context-properties.content.allowed-domains",
89 &isInAllowList);
90 return isInAllowList;
93 /**
94 * Stores in |aTargetPaint| information on how to reconstruct the current
95 * fill or stroke pattern. Will also set the paint opacity to transparent if
96 * the paint is set to "none".
97 * @param aOuterContextPaint pattern information from the outer text context
98 * @param aTargetPaint where to store the current pattern information
99 * @param aFillOrStroke member pointer to the paint we are setting up
101 static void SetupInheritablePaint(const DrawTarget* aDrawTarget,
102 const gfxMatrix& aContextMatrix,
103 nsIFrame* aFrame, float& aOpacity,
104 SVGContextPaint* aOuterContextPaint,
105 SVGContextPaintImpl::Paint& aTargetPaint,
106 StyleSVGPaint nsStyleSVG::*aFillOrStroke,
107 nscolor aDefaultFallbackColor,
108 imgDrawingParams& aImgParams) {
109 const nsStyleSVG* style = aFrame->StyleSVG();
110 SVGPaintServerFrame* ps =
111 SVGObserverUtils::GetAndObservePaintServer(aFrame, aFillOrStroke);
113 if (ps) {
114 RefPtr<gfxPattern> pattern =
115 ps->GetPaintServerPattern(aFrame, aDrawTarget, aContextMatrix,
116 aFillOrStroke, aOpacity, aImgParams);
118 if (pattern) {
119 aTargetPaint.SetPaintServer(aFrame, aContextMatrix, ps);
120 return;
124 if (aOuterContextPaint) {
125 RefPtr<gfxPattern> pattern;
126 auto tag = SVGContextPaintImpl::Paint::Tag::None;
127 switch ((style->*aFillOrStroke).kind.tag) {
128 case StyleSVGPaintKind::Tag::ContextFill:
129 tag = SVGContextPaintImpl::Paint::Tag::ContextFill;
130 pattern = aOuterContextPaint->GetFillPattern(
131 aDrawTarget, aOpacity, aContextMatrix, aImgParams);
132 break;
133 case StyleSVGPaintKind::Tag::ContextStroke:
134 tag = SVGContextPaintImpl::Paint::Tag::ContextStroke;
135 pattern = aOuterContextPaint->GetStrokePattern(
136 aDrawTarget, aOpacity, aContextMatrix, aImgParams);
137 break;
138 default:;
140 if (pattern) {
141 aTargetPaint.SetContextPaint(aOuterContextPaint, tag);
142 return;
146 nscolor color = SVGUtils::GetFallbackOrPaintColor(
147 *aFrame->Style(), aFillOrStroke, aDefaultFallbackColor);
148 aTargetPaint.SetColor(color);
151 DrawMode SVGContextPaintImpl::Init(const DrawTarget* aDrawTarget,
152 const gfxMatrix& aContextMatrix,
153 nsIFrame* aFrame,
154 SVGContextPaint* aOuterContextPaint,
155 imgDrawingParams& aImgParams) {
156 DrawMode toDraw = DrawMode(0);
158 const nsStyleSVG* style = aFrame->StyleSVG();
160 // fill:
161 if (style->mFill.kind.IsNone()) {
162 SetFillOpacity(0.0f);
163 } else {
164 float opacity =
165 SVGUtils::GetOpacity(style->mFillOpacity, aOuterContextPaint);
167 SetupInheritablePaint(aDrawTarget, aContextMatrix, aFrame, opacity,
168 aOuterContextPaint, mFillPaint, &nsStyleSVG::mFill,
169 NS_RGB(0, 0, 0), aImgParams);
171 SetFillOpacity(opacity);
173 toDraw |= DrawMode::GLYPH_FILL;
176 // stroke:
177 if (style->mStroke.kind.IsNone()) {
178 SetStrokeOpacity(0.0f);
179 } else {
180 float opacity =
181 SVGUtils::GetOpacity(style->mStrokeOpacity, aOuterContextPaint);
183 SetupInheritablePaint(
184 aDrawTarget, aContextMatrix, aFrame, opacity, aOuterContextPaint,
185 mStrokePaint, &nsStyleSVG::mStroke, NS_RGBA(0, 0, 0, 0), aImgParams);
187 SetStrokeOpacity(opacity);
189 toDraw |= DrawMode::GLYPH_STROKE;
192 return toDraw;
195 void SVGContextPaint::InitStrokeGeometry(gfxContext* aContext,
196 float devUnitsPerSVGUnit) {
197 mStrokeWidth = aContext->CurrentLineWidth() / devUnitsPerSVGUnit;
198 aContext->CurrentDash(mDashes, &mDashOffset);
199 for (uint32_t i = 0; i < mDashes.Length(); i++) {
200 mDashes[i] /= devUnitsPerSVGUnit;
202 mDashOffset /= devUnitsPerSVGUnit;
205 SVGContextPaint* SVGContextPaint::GetContextPaint(nsIContent* aContent) {
206 dom::Document* ownerDoc = aContent->OwnerDoc();
208 const auto* contextPaint = ownerDoc->GetCurrentContextPaint();
210 // XXX The SVGContextPaint that Document keeps around is const. We could
211 // and should keep that constness to the SVGContextPaint that we get here
212 // (SVGImageContext is never changed after it is initialized).
214 // Unfortunately lazy initialization of SVGContextPaint (which is a member of
215 // SVGImageContext, and also conceptually never changes after construction)
216 // prevents some of SVGContextPaint's conceptually const methods from being
217 // const. Trying to fix SVGContextPaint (perhaps by using |mutable|) is a
218 // bit of a headache so for now we punt on that, don't reapply the constness
219 // to the SVGContextPaint here, and trust that no one will add code that
220 // actually modifies the object.
221 return const_cast<SVGContextPaint*>(contextPaint);
224 already_AddRefed<gfxPattern> SVGContextPaintImpl::GetFillPattern(
225 const DrawTarget* aDrawTarget, float aOpacity, const gfxMatrix& aCTM,
226 imgDrawingParams& aImgParams) {
227 return mFillPaint.GetPattern(aDrawTarget, aOpacity, &nsStyleSVG::mFill, aCTM,
228 aImgParams);
231 already_AddRefed<gfxPattern> SVGContextPaintImpl::GetStrokePattern(
232 const DrawTarget* aDrawTarget, float aOpacity, const gfxMatrix& aCTM,
233 imgDrawingParams& aImgParams) {
234 return mStrokePaint.GetPattern(aDrawTarget, aOpacity, &nsStyleSVG::mStroke,
235 aCTM, aImgParams);
238 already_AddRefed<gfxPattern> SVGContextPaintImpl::Paint::GetPattern(
239 const DrawTarget* aDrawTarget, float aOpacity,
240 StyleSVGPaint nsStyleSVG::*aFillOrStroke, const gfxMatrix& aCTM,
241 imgDrawingParams& aImgParams) {
242 RefPtr<gfxPattern> pattern;
243 if (mPatternCache.Get(aOpacity, getter_AddRefs(pattern))) {
244 // Set the pattern matrix just in case it was messed with by a previous
245 // caller. We should get the same matrix each time a pattern is constructed
246 // so this should be fine.
247 pattern->SetMatrix(aCTM * mPatternMatrix);
248 return pattern.forget();
251 switch (mPaintType) {
252 case Tag::None:
253 pattern = new gfxPattern(DeviceColor());
254 mPatternMatrix = gfxMatrix();
255 break;
256 case Tag::Color: {
257 DeviceColor color = ToDeviceColor(mPaintDefinition.mColor);
258 color.a *= aOpacity;
259 pattern = new gfxPattern(color);
260 mPatternMatrix = gfxMatrix();
261 break;
263 case Tag::PaintServer:
264 pattern = mPaintDefinition.mPaintServerFrame->GetPaintServerPattern(
265 mFrame, aDrawTarget, mContextMatrix, aFillOrStroke, aOpacity,
266 aImgParams);
267 if (!pattern) {
268 return nullptr;
271 // m maps original-user-space to pattern space
272 gfxMatrix m = pattern->GetMatrix();
273 gfxMatrix deviceToOriginalUserSpace = mContextMatrix;
274 if (!deviceToOriginalUserSpace.Invert()) {
275 return nullptr;
277 // mPatternMatrix maps device space to pattern space via original user
278 // space
279 mPatternMatrix = deviceToOriginalUserSpace * m;
281 pattern->SetMatrix(aCTM * mPatternMatrix);
282 break;
283 case Tag::ContextFill:
284 pattern = mPaintDefinition.mContextPaint->GetFillPattern(
285 aDrawTarget, aOpacity, aCTM, aImgParams);
286 // Don't cache this. mContextPaint will have cached it anyway. If we
287 // cache it, we'll have to compute mPatternMatrix, which is annoying.
288 return pattern.forget();
289 case Tag::ContextStroke:
290 pattern = mPaintDefinition.mContextPaint->GetStrokePattern(
291 aDrawTarget, aOpacity, aCTM, aImgParams);
292 // Don't cache this. mContextPaint will have cached it anyway. If we
293 // cache it, we'll have to compute mPatternMatrix, which is annoying.
294 return pattern.forget();
295 default:
296 MOZ_ASSERT(false, "invalid paint type");
297 return nullptr;
300 mPatternCache.InsertOrUpdate(aOpacity, RefPtr{pattern});
301 return pattern.forget();
304 AutoSetRestoreSVGContextPaint::AutoSetRestoreSVGContextPaint(
305 const SVGContextPaint* aContextPaint, dom::Document* aDocument)
306 : mDocument(aDocument),
307 mOuterContextPaint(aDocument->GetCurrentContextPaint()) {
308 mDocument->SetCurrentContextPaint(aContextPaint);
311 AutoSetRestoreSVGContextPaint::~AutoSetRestoreSVGContextPaint() {
312 mDocument->SetCurrentContextPaint(mOuterContextPaint);
315 // SVGEmbeddingContextPaint
317 already_AddRefed<gfxPattern> SVGEmbeddingContextPaint::GetFillPattern(
318 const DrawTarget* aDrawTarget, float aFillOpacity, const gfxMatrix& aCTM,
319 imgDrawingParams& aImgParams) {
320 if (!mFill) {
321 return nullptr;
323 // The gfxPattern that we create below depends on aFillOpacity, and since
324 // different elements in the SVG image may pass in different values for
325 // fill opacities we don't try to cache the gfxPattern that we create.
326 DeviceColor fill = *mFill;
327 fill.a *= aFillOpacity;
328 return do_AddRef(new gfxPattern(fill));
331 already_AddRefed<gfxPattern> SVGEmbeddingContextPaint::GetStrokePattern(
332 const DrawTarget* aDrawTarget, float aStrokeOpacity, const gfxMatrix& aCTM,
333 imgDrawingParams& aImgParams) {
334 if (!mStroke) {
335 return nullptr;
337 DeviceColor stroke = *mStroke;
338 stroke.a *= aStrokeOpacity;
339 return do_AddRef(new gfxPattern(stroke));
342 uint32_t SVGEmbeddingContextPaint::Hash() const {
343 uint32_t hash = 0;
345 if (mFill) {
346 hash = HashGeneric(hash, mFill->ToABGR());
347 } else {
348 // Arbitrary number, just to avoid trivial hash collisions between pairs of
349 // instances where one embedding context has fill set to the same value as
350 // another context has stroke set to.
351 hash = 1;
354 if (mStroke) {
355 hash = HashGeneric(hash, mStroke->ToABGR());
358 if (mFillOpacity != 1.0f) {
359 hash = HashGeneric(hash, mFillOpacity);
362 if (mStrokeOpacity != 1.0f) {
363 hash = HashGeneric(hash, mStrokeOpacity);
366 return hash;
369 } // namespace mozilla