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/. */
6
7 #include "SVGContextPaint.h"
8
9 #include "gfxContext.h"
10 #include "gfxUtils.h"
11 #include "mozilla/gfx/2D.h"
12 #include "mozilla/Preferences.h"
13 #include "nsIDocument.h"
14 #include "nsSVGPaintServerFrame.h"
15 #include "SVGObserverUtils.h"
16 #include "nsSVGPaintServerFrame.h"
17
18 using namespace mozilla::gfx;
19 using namespace mozilla::image;
20
21 namespace mozilla {
22
23 using image::imgDrawingParams;
24
IsAllowedForImageFromURI(nsIURI * aURI)25 /* static */ bool SVGContextPaint::IsAllowedForImageFromURI(nsIURI* aURI) {
26 static bool sEnabledForContent = false;
27 static bool sEnabledForContentCached = false;
28
29 if (!sEnabledForContentCached) {
30 Preferences::AddBoolVarCache(
31 &sEnabledForContent, "svg.context-properties.content.enabled", false);
32 sEnabledForContentCached = true;
33 }
34
35 if (sEnabledForContent) {
36 return true;
37 }
38
39 // Context paint is pref'ed off for Web content. Ideally we'd have some
40 // easy means to determine whether the frame that has linked to the image
41 // is a frame for a content node that originated from Web content.
42 // Unfortunately different types of anonymous content, about: documents
43 // such as about:reader, etc. that are "our" code that we ship are
44 // sometimes hard to distinguish from real Web content. As a result,
45 // instead of trying to figure out what content is "ours" we instead let
46 // any content provide image context paint, but only if the image is
47 // chrome:// or resource:// do we return true. This should be sufficient
48 // to stop the image context paint feature being useful to (and therefore
49 // used by and relied upon by) Web content. (We don't want Web content to
50 // use this feature because we're not sure that image context paint is a
51 // good mechanism for wider use, or suitable for specification.)
52 //
53 // Because the default favicon used in the browser UI needs context paint, we
54 // also allow it for page-icon:<page-url>. This exposes context paint to
55 // 3rd-party favicons, but only for history and bookmark items. Other places
56 // such as the tab bar don't use the page-icon protocol to load favicons.
57 //
58 // One case that is not covered by chrome:// or resource:// are WebExtensions,
59 // specifically ones that are "ours". WebExtensions are moz-extension://
60 // regardless if the extension is in-tree or not. Since we don't want
61 // extension developers coming to rely on image context paint either, we only
62 // enable context-paint for extensions that are signed by Mozilla.
63 //
64 nsAutoCString scheme;
65 if (NS_SUCCEEDED(aURI->GetScheme(scheme)) &&
66 (scheme.EqualsLiteral("chrome") || scheme.EqualsLiteral("resource") ||
67 scheme.EqualsLiteral("page-icon"))) {
68 return true;
69 }
70 RefPtr<BasePrincipal> principal =
71 BasePrincipal::CreateCodebasePrincipal(aURI, OriginAttributes());
72 nsString addonId;
73 if (NS_SUCCEEDED(principal->GetAddonId(addonId))) {
74 if (StringEndsWith(addonId, NS_LITERAL_STRING("@mozilla.org")) ||
75 StringEndsWith(addonId, NS_LITERAL_STRING("@mozilla.com")) ||
76 StringBeginsWith(addonId, NS_LITERAL_STRING("@testpilot-"))) {
77 return true;
78 }
79 }
80 return false;
81 }
82
83 /**
84 * Stores in |aTargetPaint| information on how to reconstruct the current
85 * fill or stroke pattern. Will also set the paint opacity to transparent if
86 * the paint is set to "none".
87 * @param aOuterContextPaint pattern information from the outer text context
88 * @param aTargetPaint where to store the current pattern information
89 * @param aFillOrStroke member pointer to the paint we are setting up
90 * @param aProperty the frame property descriptor of the fill or stroke paint
91 * server frame
92 */
SetupInheritablePaint(const DrawTarget * aDrawTarget,const gfxMatrix & aContextMatrix,nsIFrame * aFrame,float & aOpacity,SVGContextPaint * aOuterContextPaint,SVGContextPaintImpl::Paint & aTargetPaint,nsStyleSVGPaint nsStyleSVG::* aFillOrStroke,SVGObserverUtils::PaintingPropertyDescriptor aProperty,imgDrawingParams & aImgParams)93 static void SetupInheritablePaint(
94 const DrawTarget* aDrawTarget, const gfxMatrix& aContextMatrix,
95 nsIFrame* aFrame, float& aOpacity, SVGContextPaint* aOuterContextPaint,
96 SVGContextPaintImpl::Paint& aTargetPaint,
97 nsStyleSVGPaint nsStyleSVG::*aFillOrStroke,
98 SVGObserverUtils::PaintingPropertyDescriptor aProperty,
99 imgDrawingParams& aImgParams) {
100 const nsStyleSVG* style = aFrame->StyleSVG();
101 nsSVGPaintServerFrame* ps =
102 SVGObserverUtils::GetPaintServer(aFrame, aFillOrStroke, aProperty);
103
104 if (ps) {
105 RefPtr<gfxPattern> pattern =
106 ps->GetPaintServerPattern(aFrame, aDrawTarget, aContextMatrix,
107 aFillOrStroke, aOpacity, aImgParams);
108
109 if (pattern) {
110 aTargetPaint.SetPaintServer(aFrame, aContextMatrix, ps);
111 return;
112 }
113 }
114
115 if (aOuterContextPaint) {
116 RefPtr<gfxPattern> pattern;
117 switch ((style->*aFillOrStroke).Type()) {
118 case eStyleSVGPaintType_ContextFill:
119 pattern = aOuterContextPaint->GetFillPattern(
120 aDrawTarget, aOpacity, aContextMatrix, aImgParams);
121 break;
122 case eStyleSVGPaintType_ContextStroke:
123 pattern = aOuterContextPaint->GetStrokePattern(
124 aDrawTarget, aOpacity, aContextMatrix, aImgParams);
125 break;
126 default:;
127 }
128 if (pattern) {
129 aTargetPaint.SetContextPaint(aOuterContextPaint,
130 (style->*aFillOrStroke).Type());
131 return;
132 }
133 }
134
135 nscolor color = nsSVGUtils::GetFallbackOrPaintColor(aFrame->StyleContext(),
136 aFillOrStroke);
137 aTargetPaint.SetColor(color);
138 }
139
Init(const DrawTarget * aDrawTarget,const gfxMatrix & aContextMatrix,nsIFrame * aFrame,SVGContextPaint * aOuterContextPaint,imgDrawingParams & aImgParams)140 DrawMode SVGContextPaintImpl::Init(const DrawTarget* aDrawTarget,
141 const gfxMatrix& aContextMatrix,
142 nsIFrame* aFrame,
143 SVGContextPaint* aOuterContextPaint,
144 imgDrawingParams& aImgParams) {
145 DrawMode toDraw = DrawMode(0);
146
147 const nsStyleSVG* style = aFrame->StyleSVG();
148
149 // fill:
150 if (style->mFill.Type() == eStyleSVGPaintType_None) {
151 SetFillOpacity(0.0f);
152 } else {
153 float opacity = nsSVGUtils::GetOpacity(
154 style->FillOpacitySource(), style->mFillOpacity, aOuterContextPaint);
155
156 SetupInheritablePaint(aDrawTarget, aContextMatrix, aFrame, opacity,
157 aOuterContextPaint, mFillPaint, &nsStyleSVG::mFill,
158 SVGObserverUtils::FillProperty(), aImgParams);
159
160 SetFillOpacity(opacity);
161
162 toDraw |= DrawMode::GLYPH_FILL;
163 }
164
165 // stroke:
166 if (style->mStroke.Type() == eStyleSVGPaintType_None) {
167 SetStrokeOpacity(0.0f);
168 } else {
169 float opacity =
170 nsSVGUtils::GetOpacity(style->StrokeOpacitySource(),
171 style->mStrokeOpacity, aOuterContextPaint);
172
173 SetupInheritablePaint(aDrawTarget, aContextMatrix, aFrame, opacity,
174 aOuterContextPaint, mStrokePaint,
175 &nsStyleSVG::mStroke,
176 SVGObserverUtils::StrokeProperty(), aImgParams);
177
178 SetStrokeOpacity(opacity);
179
180 toDraw |= DrawMode::GLYPH_STROKE;
181 }
182
183 return toDraw;
184 }
185
InitStrokeGeometry(gfxContext * aContext,float devUnitsPerSVGUnit)186 void SVGContextPaint::InitStrokeGeometry(gfxContext* aContext,
187 float devUnitsPerSVGUnit) {
188 mStrokeWidth = aContext->CurrentLineWidth() / devUnitsPerSVGUnit;
189 aContext->CurrentDash(mDashes, &mDashOffset);
190 for (uint32_t i = 0; i < mDashes.Length(); i++) {
191 mDashes[i] /= devUnitsPerSVGUnit;
192 }
193 mDashOffset /= devUnitsPerSVGUnit;
194 }
195
GetContextPaint(nsIContent * aContent)196 /* static */ SVGContextPaint* SVGContextPaint::GetContextPaint(
197 nsIContent* aContent) {
198 nsIDocument* ownerDoc = aContent->OwnerDoc();
199
200 if (!ownerDoc->IsBeingUsedAsImage()) {
201 return nullptr;
202 }
203
204 // XXX The SVGContextPaint that was passed to SetProperty was const. Ideally
205 // we could and should re-apply that constness to the SVGContextPaint that
206 // we get here (SVGImageContext is never changed after it is initialized).
207 // Unfortunately lazy initialization of SVGContextPaint (which is a member of
208 // SVGImageContext, and also conceptually never changes after construction)
209 // prevents some of SVGContextPaint's conceptually const methods from being
210 // const. Trying to fix SVGContextPaint (perhaps by using |mutable|) is a
211 // bit of a headache so for now we punt on that, don't reapply the constness
212 // to the SVGContextPaint here, and trust that no one will add code that
213 // actually modifies the object.
214
215 return static_cast<SVGContextPaint*>(
216 ownerDoc->GetProperty(nsGkAtoms::svgContextPaint));
217 }
218
GetFillPattern(const DrawTarget * aDrawTarget,float aOpacity,const gfxMatrix & aCTM,imgDrawingParams & aImgParams)219 already_AddRefed<gfxPattern> SVGContextPaintImpl::GetFillPattern(
220 const DrawTarget* aDrawTarget, float aOpacity, const gfxMatrix& aCTM,
221 imgDrawingParams& aImgParams) {
222 return mFillPaint.GetPattern(aDrawTarget, aOpacity, &nsStyleSVG::mFill, aCTM,
223 aImgParams);
224 }
225
GetStrokePattern(const DrawTarget * aDrawTarget,float aOpacity,const gfxMatrix & aCTM,imgDrawingParams & aImgParams)226 already_AddRefed<gfxPattern> SVGContextPaintImpl::GetStrokePattern(
227 const DrawTarget* aDrawTarget, float aOpacity, const gfxMatrix& aCTM,
228 imgDrawingParams& aImgParams) {
229 return mStrokePaint.GetPattern(aDrawTarget, aOpacity, &nsStyleSVG::mStroke,
230 aCTM, aImgParams);
231 }
232
GetPattern(const DrawTarget * aDrawTarget,float aOpacity,nsStyleSVGPaint nsStyleSVG::* aFillOrStroke,const gfxMatrix & aCTM,imgDrawingParams & aImgParams)233 already_AddRefed<gfxPattern> SVGContextPaintImpl::Paint::GetPattern(
234 const DrawTarget* aDrawTarget, float aOpacity,
235 nsStyleSVGPaint nsStyleSVG::*aFillOrStroke, const gfxMatrix& aCTM,
236 imgDrawingParams& aImgParams) {
237 RefPtr<gfxPattern> pattern;
238 if (mPatternCache.Get(aOpacity, getter_AddRefs(pattern))) {
239 // Set the pattern matrix just in case it was messed with by a previous
240 // caller. We should get the same matrix each time a pattern is constructed
241 // so this should be fine.
242 pattern->SetMatrix(aCTM * mPatternMatrix);
243 return pattern.forget();
244 }
245
246 switch (mPaintType) {
247 case eStyleSVGPaintType_None:
248 pattern = new gfxPattern(Color());
249 mPatternMatrix = gfxMatrix();
250 break;
251 case eStyleSVGPaintType_Color: {
252 Color color = Color::FromABGR(mPaintDefinition.mColor);
253 color.a *= aOpacity;
254 pattern = new gfxPattern(color);
255 mPatternMatrix = gfxMatrix();
256 break;
257 }
258 case eStyleSVGPaintType_Server:
259 pattern = mPaintDefinition.mPaintServerFrame->GetPaintServerPattern(
260 mFrame, aDrawTarget, mContextMatrix, aFillOrStroke, aOpacity,
261 aImgParams);
262 {
263 // m maps original-user-space to pattern space
264 gfxMatrix m = pattern->GetMatrix();
265 gfxMatrix deviceToOriginalUserSpace = mContextMatrix;
266 if (!deviceToOriginalUserSpace.Invert()) {
267 return nullptr;
268 }
269 // mPatternMatrix maps device space to pattern space via original user
270 // space
271 mPatternMatrix = deviceToOriginalUserSpace * m;
272 }
273 pattern->SetMatrix(aCTM * mPatternMatrix);
274 break;
275 case eStyleSVGPaintType_ContextFill:
276 pattern = mPaintDefinition.mContextPaint->GetFillPattern(
277 aDrawTarget, aOpacity, aCTM, aImgParams);
278 // Don't cache this. mContextPaint will have cached it anyway. If we
279 // cache it, we'll have to compute mPatternMatrix, which is annoying.
280 return pattern.forget();
281 case eStyleSVGPaintType_ContextStroke:
282 pattern = mPaintDefinition.mContextPaint->GetStrokePattern(
283 aDrawTarget, aOpacity, aCTM, aImgParams);
284 // Don't cache this. mContextPaint will have cached it anyway. If we
285 // cache it, we'll have to compute mPatternMatrix, which is annoying.
286 return pattern.forget();
287 default:
288 MOZ_ASSERT(false, "invalid paint type");
289 return nullptr;
290 }
291
292 mPatternCache.Put(aOpacity, pattern);
293 return pattern.forget();
294 }
295
AutoSetRestoreSVGContextPaint(const SVGContextPaint * aContextPaint,nsIDocument * aSVGDocument)296 AutoSetRestoreSVGContextPaint::AutoSetRestoreSVGContextPaint(
297 const SVGContextPaint* aContextPaint, nsIDocument* aSVGDocument)
298 : mSVGDocument(aSVGDocument),
299 mOuterContextPaint(
300 aSVGDocument->GetProperty(nsGkAtoms::svgContextPaint)) {
301 // The way that we supply context paint is to temporarily set the context
302 // paint on the owner document of the SVG that we're painting while it's
303 // being painted.
304
305 MOZ_ASSERT(aContextPaint);
306 MOZ_ASSERT(aSVGDocument->IsBeingUsedAsImage(),
307 "SVGContextPaint::GetContextPaint assumes this");
308
309 if (mOuterContextPaint) {
310 mSVGDocument->UnsetProperty(nsGkAtoms::svgContextPaint);
311 }
312
313 DebugOnly<nsresult> res = mSVGDocument->SetProperty(
314 nsGkAtoms::svgContextPaint, const_cast<SVGContextPaint*>(aContextPaint));
315
316 NS_WARNING_ASSERTION(NS_SUCCEEDED(res), "Failed to set context paint");
317 }
318
~AutoSetRestoreSVGContextPaint()319 AutoSetRestoreSVGContextPaint::~AutoSetRestoreSVGContextPaint() {
320 mSVGDocument->UnsetProperty(nsGkAtoms::svgContextPaint);
321 if (mOuterContextPaint) {
322 DebugOnly<nsresult> res = mSVGDocument->SetProperty(
323 nsGkAtoms::svgContextPaint, mOuterContextPaint);
324
325 NS_WARNING_ASSERTION(NS_SUCCEEDED(res), "Failed to restore context paint");
326 }
327 }
328
329 // SVGEmbeddingContextPaint
330
GetFillPattern(const DrawTarget * aDrawTarget,float aFillOpacity,const gfxMatrix & aCTM,imgDrawingParams & aImgParams)331 already_AddRefed<gfxPattern> SVGEmbeddingContextPaint::GetFillPattern(
332 const DrawTarget* aDrawTarget, float aFillOpacity, const gfxMatrix& aCTM,
333 imgDrawingParams& aImgParams) {
334 if (!mFill) {
335 return nullptr;
336 }
337 // The gfxPattern that we create below depends on aFillOpacity, and since
338 // different elements in the SVG image may pass in different values for
339 // fill opacities we don't try to cache the gfxPattern that we create.
340 Color fill = *mFill;
341 fill.a *= aFillOpacity;
342 return do_AddRef(new gfxPattern(fill));
343 }
344
GetStrokePattern(const DrawTarget * aDrawTarget,float aStrokeOpacity,const gfxMatrix & aCTM,imgDrawingParams & aImgParams)345 already_AddRefed<gfxPattern> SVGEmbeddingContextPaint::GetStrokePattern(
346 const DrawTarget* aDrawTarget, float aStrokeOpacity, const gfxMatrix& aCTM,
347 imgDrawingParams& aImgParams) {
348 if (!mStroke) {
349 return nullptr;
350 }
351 Color stroke = *mStroke;
352 stroke.a *= aStrokeOpacity;
353 return do_AddRef(new gfxPattern(stroke));
354 }
355
Hash() const356 uint32_t SVGEmbeddingContextPaint::Hash() const {
357 uint32_t hash = 0;
358
359 if (mFill) {
360 hash = HashGeneric(hash, mFill->ToABGR());
361 } else {
362 // Arbitrary number, just to avoid trivial hash collisions between pairs of
363 // instances where one embedding context has fill set to the same value as
364 // another context has stroke set to.
365 hash = 1;
366 }
367
368 if (mStroke) {
369 hash = HashGeneric(hash, mStroke->ToABGR());
370 }
371
372 if (mFillOpacity != 1.0f) {
373 hash = HashGeneric(hash, mFillOpacity);
374 }
375
376 if (mStrokeOpacity != 1.0f) {
377 hash = HashGeneric(hash, mStrokeOpacity);
378 }
379
380 return hash;
381 }
382
383 } // namespace mozilla
384