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