tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

SVGContextPaint.cpp (14238B)


      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 "SVGPaintServerFrame.h"
     10 #include "gfxContext.h"
     11 #include "gfxUtils.h"
     12 #include "mozilla/BasePrincipal.h"
     13 #include "mozilla/SVGObserverUtils.h"
     14 #include "mozilla/SVGUtils.h"
     15 #include "mozilla/StaticPrefs_svg.h"
     16 #include "mozilla/dom/Document.h"
     17 #include "mozilla/extensions/WebExtensionPolicy.h"
     18 #include "mozilla/gfx/2D.h"
     19 
     20 using namespace mozilla::gfx;
     21 using namespace mozilla::image;
     22 
     23 namespace mozilla {
     24 
     25 using image::imgDrawingParams;
     26 
     27 /* static */
     28 bool SVGContextPaint::IsAllowedForImageFromURI(nsIURI* aURI) {
     29  if (StaticPrefs::svg_context_properties_content_enabled()) {
     30    return true;
     31  }
     32 
     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.)
     46  //
     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  // - cached-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.
     56  //
     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").
     63  //
     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.
     67  //
     68  nsAutoCString scheme;
     69  if (NS_SUCCEEDED(aURI->GetScheme(scheme)) &&
     70      (scheme.EqualsLiteral("chrome") || scheme.EqualsLiteral("resource") ||
     71       scheme.EqualsLiteral("page-icon") ||
     72       scheme.EqualsLiteral("cached-favicon"))) {
     73    return true;
     74  }
     75  RefPtr<BasePrincipal> principal =
     76      BasePrincipal::CreateContentPrincipal(aURI, OriginAttributes());
     77 
     78  RefPtr<extensions::WebExtensionPolicy> addonPolicy = principal->AddonPolicy();
     79  if (addonPolicy) {
     80    // Only allowed for extensions that have the
     81    // internal:svgContextPropertiesAllowed permission (added internally from
     82    // to Mozilla-owned extensions, see `isMozillaExtension` function
     83    // defined in Extension.sys.mjs for the exact criteria).
     84    return addonPolicy->HasPermission(
     85        nsGkAtoms::svgContextPropertiesAllowedPermission);
     86  }
     87 
     88  bool isInAllowList = false;
     89  principal->IsURIInPrefList("svg.context-properties.content.allowed-domains",
     90                             &isInAllowList);
     91  return isInAllowList;
     92 }
     93 
     94 /**
     95 * Stores in |aTargetPaint| information on how to reconstruct the current
     96 * fill or stroke pattern. Will also set the paint opacity to transparent if
     97 * the paint is set to "none".
     98 * @param aOuterContextPaint pattern information from the outer text context
     99 * @param aTargetPaint where to store the current pattern information
    100 * @param aFillOrStroke member pointer to the paint we are setting up
    101 */
    102 static void SetupInheritablePaint(const DrawTarget* aDrawTarget,
    103                                  const gfxMatrix& aContextMatrix,
    104                                  nsIFrame* aFrame, float& aOpacity,
    105                                  SVGContextPaint* aOuterContextPaint,
    106                                  SVGContextPaintImpl::Paint& aTargetPaint,
    107                                  StyleSVGPaint nsStyleSVG::* aFillOrStroke,
    108                                  nscolor aDefaultFallbackColor,
    109                                  imgDrawingParams& aImgParams) {
    110  const nsStyleSVG* style = aFrame->StyleSVG();
    111  SVGPaintServerFrame* ps =
    112      SVGObserverUtils::GetAndObservePaintServer(aFrame, aFillOrStroke);
    113 
    114  if (ps) {
    115    RefPtr<gfxPattern> pattern =
    116        ps->GetPaintServerPattern(aFrame, aDrawTarget, aContextMatrix,
    117                                  aFillOrStroke, aOpacity, aImgParams);
    118 
    119    if (pattern) {
    120      aTargetPaint.SetPaintServer(aFrame, aContextMatrix, ps);
    121      return;
    122    }
    123  }
    124 
    125  if (aOuterContextPaint) {
    126    RefPtr<gfxPattern> pattern;
    127    auto tag = SVGContextPaintImpl::Paint::Tag::None;
    128    switch ((style->*aFillOrStroke).kind.tag) {
    129      case StyleSVGPaintKind::Tag::ContextFill:
    130        tag = SVGContextPaintImpl::Paint::Tag::ContextFill;
    131        pattern = aOuterContextPaint->GetFillPattern(
    132            aDrawTarget, aOpacity, aContextMatrix, aImgParams);
    133        break;
    134      case StyleSVGPaintKind::Tag::ContextStroke:
    135        tag = SVGContextPaintImpl::Paint::Tag::ContextStroke;
    136        pattern = aOuterContextPaint->GetStrokePattern(
    137            aDrawTarget, aOpacity, aContextMatrix, aImgParams);
    138        break;
    139      default:;
    140    }
    141    if (pattern) {
    142      aTargetPaint.SetContextPaint(aOuterContextPaint, tag);
    143      return;
    144    }
    145  }
    146 
    147  nscolor color = SVGUtils::GetFallbackOrPaintColor(
    148      *aFrame->Style(), aFillOrStroke, aDefaultFallbackColor);
    149  aTargetPaint.SetColor(color);
    150 }
    151 
    152 DrawMode SVGContextPaintImpl::Init(const DrawTarget* aDrawTarget,
    153                                   const gfxMatrix& aContextMatrix,
    154                                   nsIFrame* aFrame,
    155                                   SVGContextPaint* aOuterContextPaint,
    156                                   imgDrawingParams& aImgParams) {
    157  DrawMode toDraw = DrawMode(0);
    158 
    159  const nsStyleSVG* style = aFrame->StyleSVG();
    160 
    161  // fill:
    162  if (style->mFill.kind.IsNone()) {
    163    SetFillOpacity(0.0f);
    164  } else {
    165    float opacity =
    166        SVGUtils::GetOpacity(style->mFillOpacity, aOuterContextPaint);
    167 
    168    SetupInheritablePaint(aDrawTarget, aContextMatrix, aFrame, opacity,
    169                          aOuterContextPaint, mFillPaint, &nsStyleSVG::mFill,
    170                          NS_RGB(0, 0, 0), aImgParams);
    171 
    172    SetFillOpacity(opacity);
    173 
    174    toDraw |= DrawMode::GLYPH_FILL;
    175  }
    176 
    177  // stroke:
    178  if (style->mStroke.kind.IsNone()) {
    179    SetStrokeOpacity(0.0f);
    180  } else {
    181    float opacity =
    182        SVGUtils::GetOpacity(style->mStrokeOpacity, aOuterContextPaint);
    183 
    184    SetupInheritablePaint(
    185        aDrawTarget, aContextMatrix, aFrame, opacity, aOuterContextPaint,
    186        mStrokePaint, &nsStyleSVG::mStroke, NS_RGBA(0, 0, 0, 0), aImgParams);
    187 
    188    SetStrokeOpacity(opacity);
    189 
    190    toDraw |= DrawMode::GLYPH_STROKE;
    191  }
    192 
    193  return toDraw;
    194 }
    195 
    196 void SVGContextPaint::InitStrokeGeometry(gfxContext* aContext,
    197                                         float devUnitsPerSVGUnit) {
    198  mStrokeWidth = aContext->CurrentLineWidth() / devUnitsPerSVGUnit;
    199  aContext->CurrentDash(mDashes, &mDashOffset);
    200  for (uint32_t i = 0; i < mDashes.Length(); i++) {
    201    mDashes[i] /= devUnitsPerSVGUnit;
    202  }
    203  mDashOffset /= devUnitsPerSVGUnit;
    204 }
    205 
    206 SVGContextPaint* SVGContextPaint::GetContextPaint(nsIContent* aContent) {
    207  dom::Document* ownerDoc = aContent->OwnerDoc();
    208 
    209  const auto* contextPaint = ownerDoc->GetCurrentContextPaint();
    210 
    211  // XXX The SVGContextPaint that Document keeps around is const. We could
    212  // and should keep that constness to the SVGContextPaint that we get here
    213  // (SVGImageContext is never changed after it is initialized).
    214  //
    215  // Unfortunately lazy initialization of SVGContextPaint (which is a member of
    216  // SVGImageContext, and also conceptually never changes after construction)
    217  // prevents some of SVGContextPaint's conceptually const methods from being
    218  // const.  Trying to fix SVGContextPaint (perhaps by using |mutable|) is a
    219  // bit of a headache so for now we punt on that, don't reapply the constness
    220  // to the SVGContextPaint here, and trust that no one will add code that
    221  // actually modifies the object.
    222  return const_cast<SVGContextPaint*>(contextPaint);
    223 }
    224 
    225 already_AddRefed<gfxPattern> SVGContextPaintImpl::GetFillPattern(
    226    const DrawTarget* aDrawTarget, float aOpacity, const gfxMatrix& aCTM,
    227    imgDrawingParams& aImgParams) {
    228  return mFillPaint.GetPattern(aDrawTarget, aOpacity, &nsStyleSVG::mFill, aCTM,
    229                               aImgParams);
    230 }
    231 
    232 already_AddRefed<gfxPattern> SVGContextPaintImpl::GetStrokePattern(
    233    const DrawTarget* aDrawTarget, float aOpacity, const gfxMatrix& aCTM,
    234    imgDrawingParams& aImgParams) {
    235  return mStrokePaint.GetPattern(aDrawTarget, aOpacity, &nsStyleSVG::mStroke,
    236                                 aCTM, aImgParams);
    237 }
    238 
    239 already_AddRefed<gfxPattern> SVGContextPaintImpl::Paint::GetPattern(
    240    const DrawTarget* aDrawTarget, float aOpacity,
    241    StyleSVGPaint nsStyleSVG::* aFillOrStroke, const gfxMatrix& aCTM,
    242    imgDrawingParams& aImgParams) {
    243  RefPtr<gfxPattern> pattern;
    244  if (mPatternCache.Get(aOpacity, getter_AddRefs(pattern))) {
    245    // Set the pattern matrix just in case it was messed with by a previous
    246    // caller. We should get the same matrix each time a pattern is constructed
    247    // so this should be fine.
    248    pattern->SetMatrix(aCTM * mPatternMatrix);
    249    return pattern.forget();
    250  }
    251 
    252  switch (mPaintType) {
    253    case Tag::None:
    254      pattern = new gfxPattern(DeviceColor());
    255      mPatternMatrix = gfxMatrix();
    256      break;
    257    case Tag::Color: {
    258      DeviceColor color = ToDeviceColor(mPaintDefinition.mColor);
    259      color.a *= aOpacity;
    260      pattern = new gfxPattern(color);
    261      mPatternMatrix = gfxMatrix();
    262      break;
    263    }
    264    case Tag::PaintServer:
    265      pattern = mPaintDefinition.mPaintServerFrame->GetPaintServerPattern(
    266          mFrame, aDrawTarget, mContextMatrix, aFillOrStroke, aOpacity,
    267          aImgParams);
    268      if (!pattern) {
    269        return nullptr;
    270      }
    271      {
    272        // m maps original-user-space to pattern space
    273        gfxMatrix m = pattern->GetMatrix();
    274        gfxMatrix deviceToOriginalUserSpace = mContextMatrix;
    275        if (!deviceToOriginalUserSpace.Invert()) {
    276          return nullptr;
    277        }
    278        // mPatternMatrix maps device space to pattern space via original user
    279        // space
    280        mPatternMatrix = deviceToOriginalUserSpace * m;
    281      }
    282      pattern->SetMatrix(aCTM * mPatternMatrix);
    283      break;
    284    case Tag::ContextFill:
    285      pattern = mPaintDefinition.mContextPaint->GetFillPattern(
    286          aDrawTarget, aOpacity, aCTM, aImgParams);
    287      // Don't cache this. mContextPaint will have cached it anyway. If we
    288      // cache it, we'll have to compute mPatternMatrix, which is annoying.
    289      return pattern.forget();
    290    case Tag::ContextStroke:
    291      pattern = mPaintDefinition.mContextPaint->GetStrokePattern(
    292          aDrawTarget, aOpacity, aCTM, 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      return pattern.forget();
    296    default:
    297      MOZ_ASSERT(false, "invalid paint type");
    298      return nullptr;
    299  }
    300 
    301  mPatternCache.InsertOrUpdate(aOpacity, RefPtr{pattern});
    302  return pattern.forget();
    303 }
    304 
    305 AutoSetRestoreSVGContextPaint::AutoSetRestoreSVGContextPaint(
    306    const SVGContextPaint* aContextPaint, dom::Document* aDocument)
    307    : mDocument(aDocument),
    308      mOuterContextPaint(aDocument->GetCurrentContextPaint()) {
    309  mDocument->SetCurrentContextPaint(aContextPaint);
    310 }
    311 
    312 AutoSetRestoreSVGContextPaint::~AutoSetRestoreSVGContextPaint() {
    313  mDocument->SetCurrentContextPaint(mOuterContextPaint);
    314 }
    315 
    316 // SVGEmbeddingContextPaint
    317 
    318 already_AddRefed<gfxPattern> SVGEmbeddingContextPaint::GetFillPattern(
    319    const DrawTarget* aDrawTarget, float aFillOpacity, const gfxMatrix& aCTM,
    320    imgDrawingParams& aImgParams) {
    321  if (!mFill) {
    322    return nullptr;
    323  }
    324  // The gfxPattern that we create below depends on aFillOpacity, and since
    325  // different elements in the SVG image may pass in different values for
    326  // fill opacities we don't try to cache the gfxPattern that we create.
    327  DeviceColor fill = *mFill;
    328  fill.a *= aFillOpacity;
    329  return do_AddRef(new gfxPattern(fill));
    330 }
    331 
    332 already_AddRefed<gfxPattern> SVGEmbeddingContextPaint::GetStrokePattern(
    333    const DrawTarget* aDrawTarget, float aStrokeOpacity, const gfxMatrix& aCTM,
    334    imgDrawingParams& aImgParams) {
    335  if (!mStroke) {
    336    return nullptr;
    337  }
    338  DeviceColor stroke = *mStroke;
    339  stroke.a *= aStrokeOpacity;
    340  return do_AddRef(new gfxPattern(stroke));
    341 }
    342 
    343 uint32_t SVGEmbeddingContextPaint::Hash() const {
    344  uint32_t hash = 0;
    345 
    346  if (mFill) {
    347    hash = HashGeneric(hash, mFill->ToABGR());
    348  } else {
    349    // Arbitrary number, just to avoid trivial hash collisions between pairs of
    350    // instances where one embedding context has fill set to the same value as
    351    // another context has stroke set to.
    352    hash = 1;
    353  }
    354 
    355  if (mStroke) {
    356    hash = HashGeneric(hash, mStroke->ToABGR());
    357  }
    358 
    359  if (mFillOpacity != 1.0f) {
    360    hash = HashGeneric(hash, mFillOpacity);
    361  }
    362 
    363  if (mStrokeOpacity != 1.0f) {
    364    hash = HashGeneric(hash, mStrokeOpacity);
    365  }
    366 
    367  return hash;
    368 }
    369 
    370 }  // namespace mozilla