Coverage Report

Created: 2018-09-25 14:53

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