tor-browser

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

gfxSVGGlyphs.cpp (15292B)


      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 "gfxSVGGlyphs.h"
      6 
      7 #include "mozilla/BasePrincipal.h"
      8 #include "mozilla/LoadInfo.h"
      9 #include "mozilla/NullPrincipal.h"
     10 #include "mozilla/PresShell.h"
     11 #include "mozilla/SMILAnimationController.h"
     12 #include "mozilla/SVGContextPaint.h"
     13 #include "mozilla/SVGUtils.h"
     14 #include "mozilla/dom/Document.h"
     15 #include "mozilla/dom/Element.h"
     16 #include "mozilla/dom/SVGDocument.h"
     17 #include "nsError.h"
     18 #include "nsString.h"
     19 #include "nsICategoryManager.h"
     20 #include "nsIDocumentLoaderFactory.h"
     21 #include "nsIDocumentViewer.h"
     22 #include "nsIStreamListener.h"
     23 #include "nsServiceManagerUtils.h"
     24 #include "nsNetUtil.h"
     25 #include "nsIInputStream.h"
     26 #include "nsStringStream.h"
     27 #include "nsStreamUtils.h"
     28 #include "nsIPrincipal.h"
     29 #include "nsContentUtils.h"
     30 #include "gfxFont.h"
     31 #include "gfxContext.h"
     32 #include "harfbuzz/hb.h"
     33 #include "zlib.h"
     34 
     35 #define SVG_CONTENT_TYPE "image/svg+xml"_ns
     36 #define UTF8_CHARSET "utf-8"_ns
     37 
     38 using namespace mozilla;
     39 using mozilla::dom::Document;
     40 using mozilla::dom::Element;
     41 
     42 gfxSVGGlyphs::gfxSVGGlyphs(hb_blob_t* aSVGTable, gfxFontEntry* aFontEntry)
     43    : mSVGData(aSVGTable), mFontEntry(aFontEntry) {
     44  unsigned int length;
     45  const char* svgData = hb_blob_get_data(mSVGData, &length);
     46  mHeader = reinterpret_cast<const Header*>(svgData);
     47  mDocIndex = nullptr;
     48 
     49  if (sizeof(Header) <= length && uint16_t(mHeader->mVersion) == 0 &&
     50      uint64_t(mHeader->mDocIndexOffset) + 2 <= length) {
     51    const DocIndex* docIndex =
     52        reinterpret_cast<const DocIndex*>(svgData + mHeader->mDocIndexOffset);
     53    // Limit the number of documents to avoid overflow
     54    if (uint64_t(mHeader->mDocIndexOffset) + 2 +
     55            uint16_t(docIndex->mNumEntries) * sizeof(IndexEntry) <=
     56        length) {
     57      mDocIndex = docIndex;
     58    }
     59  }
     60 }
     61 
     62 gfxSVGGlyphs::~gfxSVGGlyphs() { hb_blob_destroy(mSVGData); }
     63 
     64 void gfxSVGGlyphs::DidRefresh() { mFontEntry->NotifyGlyphsChanged(); }
     65 
     66 /*
     67 * Comparison operator for finding a range containing a given glyph ID. Simply
     68 *   checks whether |key| is less (greater) than every element of |range|, in
     69 *   which case return |key| < |range| (|key| > |range|). Otherwise |key| is in
     70 *   |range|, in which case return equality.
     71 * The total ordering here is guaranteed by
     72 *   (1) the index ranges being disjoint; and
     73 *   (2) the (sole) key always being a singleton, so intersection => containment
     74 *       (note that this is wrong if we have more than one intersection or two
     75 *        sets intersecting of size > 1 -- so... don't do that)
     76 */
     77 /* static */
     78 int gfxSVGGlyphs::CompareIndexEntries(const void* aKey, const void* aEntry) {
     79  const uint32_t key = *(uint32_t*)aKey;
     80  const IndexEntry* entry = (const IndexEntry*)aEntry;
     81 
     82  if (key < uint16_t(entry->mStartGlyph)) {
     83    return -1;
     84  }
     85  if (key > uint16_t(entry->mEndGlyph)) {
     86    return 1;
     87  }
     88  return 0;
     89 }
     90 
     91 gfxSVGGlyphsDocument* gfxSVGGlyphs::FindOrCreateGlyphsDocument(
     92    uint32_t aGlyphId) {
     93  if (!mDocIndex) {
     94    // Invalid table
     95    return nullptr;
     96  }
     97 
     98  IndexEntry* entry = (IndexEntry*)bsearch(
     99      &aGlyphId, mDocIndex->mEntries, uint16_t(mDocIndex->mNumEntries),
    100      sizeof(IndexEntry), CompareIndexEntries);
    101  if (!entry) {
    102    return nullptr;
    103  }
    104 
    105  return mGlyphDocs.WithEntryHandle(
    106      entry->mDocOffset, [&](auto&& glyphDocsEntry) -> gfxSVGGlyphsDocument* {
    107        if (!glyphDocsEntry) {
    108          unsigned int length;
    109          const uint8_t* data =
    110              (const uint8_t*)hb_blob_get_data(mSVGData, &length);
    111          if (entry->mDocOffset > 0 && uint64_t(mHeader->mDocIndexOffset) +
    112                                               entry->mDocOffset +
    113                                               entry->mDocLength <=
    114                                           length) {
    115            return glyphDocsEntry
    116                .Insert(MakeUnique<gfxSVGGlyphsDocument>(
    117                    data + mHeader->mDocIndexOffset + entry->mDocOffset,
    118                    entry->mDocLength, this))
    119                .get();
    120          }
    121 
    122          return nullptr;
    123        }
    124 
    125        return glyphDocsEntry->get();
    126      });
    127 }
    128 
    129 nsresult gfxSVGGlyphsDocument::SetupPresentation() {
    130  nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory =
    131      nsContentUtils::FindInternalDocumentViewer(SVG_CONTENT_TYPE);
    132  NS_ASSERTION(docLoaderFactory, "Couldn't get DocumentLoaderFactory");
    133 
    134  nsCOMPtr<nsIDocumentViewer> viewer;
    135  nsresult rv = docLoaderFactory->CreateInstanceForDocument(
    136      nullptr, mDocument, nullptr, getter_AddRefs(viewer));
    137  NS_ENSURE_SUCCESS(rv, rv);
    138 
    139  auto upem = mOwner->FontEntry()->UnitsPerEm();
    140  rv = viewer->Init(nullptr, LayoutDeviceIntRect(0, 0, upem, upem), nullptr);
    141  if (NS_SUCCEEDED(rv)) {
    142    rv = viewer->Open(nullptr, nullptr);
    143    NS_ENSURE_SUCCESS(rv, rv);
    144  }
    145 
    146  RefPtr<PresShell> presShell = viewer->GetPresShell();
    147  if (!presShell->DidInitialize()) {
    148    rv = presShell->Initialize();
    149    NS_ENSURE_SUCCESS(rv, rv);
    150  }
    151 
    152  mDocument->FlushPendingNotifications(FlushType::Layout);
    153 
    154  if (mDocument->HasAnimationController()) {
    155    mDocument->GetAnimationController()->Resume(SMILTimeContainer::PAUSE_IMAGE);
    156  }
    157  mDocument->SetImageAnimationState(true);
    158 
    159  mViewer = viewer;
    160  mPresShell = presShell;
    161  mPresShell->AddPostRefreshObserver(this);
    162 
    163  return NS_OK;
    164 }
    165 
    166 void gfxSVGGlyphsDocument::DidRefresh() { mOwner->DidRefresh(); }
    167 
    168 /**
    169 * Walk the DOM tree to find all glyph elements and insert them into the lookup
    170 * table
    171 * @param aElem The element to search from
    172 */
    173 void gfxSVGGlyphsDocument::FindGlyphElements(Element* aElem) {
    174  for (nsIContent* child = aElem->GetLastChild(); child;
    175       child = child->GetPreviousSibling()) {
    176    if (!child->IsElement()) {
    177      continue;
    178    }
    179    FindGlyphElements(child->AsElement());
    180  }
    181 
    182  InsertGlyphId(aElem);
    183 }
    184 
    185 /**
    186 * If there exists an SVG glyph with the specified glyph id, render it and
    187 * return true If no such glyph exists, or in the case of an error return false
    188 * @param aContext The thebes aContext to draw to
    189 * @param aGlyphId The glyph id
    190 * @return true iff rendering succeeded
    191 */
    192 void gfxSVGGlyphs::RenderGlyph(gfxContext* aContext, uint32_t aGlyphId,
    193                               SVGContextPaint* aContextPaint) {
    194  gfxContextAutoSaveRestore aContextRestorer(aContext);
    195 
    196  Element* glyph = mGlyphIdMap.Get(aGlyphId);
    197  MOZ_ASSERT(glyph, "No glyph element. Should check with HasSVGGlyph() first!");
    198 
    199  AutoSetRestoreSVGContextPaint autoSetRestore(aContextPaint,
    200                                               glyph->OwnerDoc());
    201 
    202  SVGUtils::PaintSVGGlyph(glyph, aContext);
    203 
    204 #if DEBUG
    205  // This will not have any effect, because we're about to restore the state
    206  // via the aContextRestorer destructor, but it prevents debug builds from
    207  // asserting if it turns out that PaintSVGGlyph didn't actually do anything.
    208  // This happens if the SVG document consists of just an image, and the image
    209  // hasn't finished loading yet so we can't draw it.
    210  aContext->SetOp(gfx::CompositionOp::OP_OVER);
    211 #endif
    212 }
    213 
    214 bool gfxSVGGlyphs::GetGlyphExtents(uint32_t aGlyphId,
    215                                   const gfxMatrix& aSVGToAppSpace,
    216                                   gfxRect* aResult) {
    217  Element* glyph = mGlyphIdMap.Get(aGlyphId);
    218  NS_ASSERTION(glyph,
    219               "No glyph element. Should check with HasSVGGlyph() first!");
    220 
    221  return SVGUtils::GetSVGGlyphExtents(glyph, aSVGToAppSpace, aResult);
    222 }
    223 
    224 Element* gfxSVGGlyphs::GetGlyphElement(uint32_t aGlyphId) {
    225  return mGlyphIdMap.LookupOrInsertWith(aGlyphId, [&] {
    226    Element* elem = nullptr;
    227    if (gfxSVGGlyphsDocument* set = FindOrCreateGlyphsDocument(aGlyphId)) {
    228      elem = set->GetGlyphElement(aGlyphId);
    229    }
    230    return elem;
    231  });
    232 }
    233 
    234 bool gfxSVGGlyphs::HasSVGGlyph(uint32_t aGlyphId) {
    235  return !!GetGlyphElement(aGlyphId);
    236 }
    237 
    238 size_t gfxSVGGlyphs::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
    239  // We don't include the size of mSVGData here, because (depending on the
    240  // font backend implementation) it will either wrap a block of data owned
    241  // by the system (and potentially shared), or a table that's in our font
    242  // table cache and therefore already counted.
    243  size_t result = aMallocSizeOf(this) +
    244                  mGlyphDocs.ShallowSizeOfExcludingThis(aMallocSizeOf) +
    245                  mGlyphIdMap.ShallowSizeOfExcludingThis(aMallocSizeOf);
    246  for (const auto& entry : mGlyphDocs.Values()) {
    247    result += entry->SizeOfIncludingThis(aMallocSizeOf);
    248  }
    249  return result;
    250 }
    251 
    252 Element* gfxSVGGlyphsDocument::GetGlyphElement(uint32_t aGlyphId) {
    253  return mGlyphIdMap.Get(aGlyphId);
    254 }
    255 
    256 gfxSVGGlyphsDocument::gfxSVGGlyphsDocument(const uint8_t* aBuffer,
    257                                           uint32_t aBufLen,
    258                                           gfxSVGGlyphs* aSVGGlyphs)
    259    : mOwner(aSVGGlyphs) {
    260  if (aBufLen >= 14 && aBuffer[0] == 31 && aBuffer[1] == 139) {
    261    // It's a gzip-compressed document; decompress it before parsing.
    262    // The original length (modulo 2^32) is found in the last 4 bytes
    263    // of the data, stored in little-endian format. We read it as
    264    // individual bytes to avoid possible alignment issues.
    265    // (Note that if the original length was >2^32, then origLen here
    266    // will be incorrect; but then the inflate() call will not return
    267    // Z_STREAM_END and we'll bail out safely.)
    268    size_t origLen = (size_t(aBuffer[aBufLen - 1]) << 24) +
    269                     (size_t(aBuffer[aBufLen - 2]) << 16) +
    270                     (size_t(aBuffer[aBufLen - 3]) << 8) +
    271                     size_t(aBuffer[aBufLen - 4]);
    272    AutoTArray<uint8_t, 4096> outBuf;
    273    if (outBuf.SetLength(origLen, mozilla::fallible)) {
    274      z_stream s = {0};
    275      s.next_in = const_cast<Byte*>(aBuffer);
    276      s.avail_in = aBufLen;
    277      s.next_out = outBuf.Elements();
    278      s.avail_out = outBuf.Length();
    279      // The magic number 16 here is the zlib flag to expect gzip format,
    280      // see http://www.zlib.net/manual.html#Advanced
    281      if (Z_OK == inflateInit2(&s, 16 + MAX_WBITS)) {
    282        int result = inflate(&s, Z_FINISH);
    283        if (Z_STREAM_END == result) {
    284          ParseDocument(outBuf.Elements(), s.total_out);
    285        } else {
    286          NS_WARNING("Failed to decompress SVG glyphs document");
    287        }
    288        inflateEnd(&s);
    289      }
    290    } else {
    291      NS_WARNING("Failed to allocate memory for SVG glyphs document");
    292    }
    293  } else {
    294    ParseDocument(aBuffer, aBufLen);
    295  }
    296 
    297  if (!mDocument) {
    298    NS_WARNING("Could not parse SVG glyphs document");
    299    return;
    300  }
    301 
    302  Element* root = mDocument->GetRootElement();
    303  if (!root) {
    304    NS_WARNING("Could not parse SVG glyphs document");
    305    return;
    306  }
    307 
    308  nsresult rv = SetupPresentation();
    309  if (NS_FAILED(rv)) {
    310    NS_WARNING("Couldn't setup presentation for SVG glyphs document");
    311    return;
    312  }
    313 
    314  FindGlyphElements(root);
    315 }
    316 
    317 gfxSVGGlyphsDocument::~gfxSVGGlyphsDocument() {
    318  if (mDocument) {
    319    mDocument->OnPageHide(false, nullptr);
    320  }
    321  if (mPresShell) {
    322    mPresShell->RemovePostRefreshObserver(this);
    323  }
    324  if (mViewer) {
    325    mViewer->Close(nullptr);
    326    mViewer->Destroy();
    327  }
    328 }
    329 
    330 static nsresult CreateBufferedStream(const uint8_t* aBuffer, uint32_t aBufLen,
    331                                     nsCOMPtr<nsIInputStream>& aResult) {
    332  nsCOMPtr<nsIInputStream> stream;
    333  nsresult rv = NS_NewByteInputStream(
    334      getter_AddRefs(stream),
    335      Span(reinterpret_cast<const char*>(aBuffer), aBufLen),
    336      NS_ASSIGNMENT_DEPEND);
    337  NS_ENSURE_SUCCESS(rv, rv);
    338 
    339  nsCOMPtr<nsIInputStream> aBufferedStream;
    340  if (!NS_InputStreamIsBuffered(stream)) {
    341    rv = NS_NewBufferedInputStream(getter_AddRefs(aBufferedStream),
    342                                   stream.forget(), 4096);
    343    NS_ENSURE_SUCCESS(rv, rv);
    344    stream = aBufferedStream;
    345  }
    346 
    347  aResult = stream;
    348 
    349  return NS_OK;
    350 }
    351 
    352 nsresult gfxSVGGlyphsDocument::ParseDocument(const uint8_t* aBuffer,
    353                                             uint32_t aBufLen) {
    354  // Mostly pulled from nsDOMParser::ParseFromStream
    355 
    356  nsCOMPtr<nsIInputStream> stream;
    357  nsresult rv = CreateBufferedStream(aBuffer, aBufLen, stream);
    358  NS_ENSURE_SUCCESS(rv, rv);
    359 
    360  // We just need a dummy URI.
    361  nsCOMPtr<nsIURI> uri;
    362  rv = NS_NewURI(getter_AddRefs(uri), "moz-svg-glyphs://"_ns);
    363  NS_ENSURE_SUCCESS(rv, rv);
    364 
    365  nsCOMPtr<nsIPrincipal> principal =
    366      NullPrincipal::CreateWithoutOriginAttributes();
    367 
    368  RefPtr<Document> document;
    369  rv = NS_NewDOMDocument(getter_AddRefs(document),
    370                         u""_ns,   // aNamespaceURI
    371                         u""_ns,   // aQualifiedName
    372                         nullptr,  // aDoctype
    373                         uri, uri, principal,
    374                         mozilla::dom::LoadedAsData::No,  // aLoadedAsData
    375                         nullptr,                         // aEventObject
    376                         DocumentFlavor::SVG);
    377  NS_ENSURE_SUCCESS(rv, rv);
    378 
    379  nsCOMPtr<nsIChannel> channel;
    380  rv = NS_NewInputStreamChannel(
    381      getter_AddRefs(channel), uri,
    382      nullptr,  // aStream
    383      principal, nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL,
    384      nsIContentPolicy::TYPE_OTHER, SVG_CONTENT_TYPE, UTF8_CHARSET);
    385  NS_ENSURE_SUCCESS(rv, rv);
    386 
    387  // Set this early because various decisions during page-load depend on it.
    388  document->SetIsBeingUsedAsImage();
    389  document->SetIsSVGGlyphsDocument();
    390  document->SetReadyStateInternal(Document::READYSTATE_UNINITIALIZED);
    391 
    392  nsCOMPtr<nsIStreamListener> listener;
    393  rv = document->StartDocumentLoad("external-resource", channel,
    394                                   nullptr,  // aLoadGroup
    395                                   nullptr,  // aContainer
    396                                   getter_AddRefs(listener), true /* aReset */);
    397  if (NS_FAILED(rv) || !listener) {
    398    return NS_ERROR_FAILURE;
    399  }
    400 
    401  rv = listener->OnStartRequest(channel);
    402  if (NS_FAILED(rv)) {
    403    channel->Cancel(rv);
    404  }
    405 
    406  nsresult status;
    407  channel->GetStatus(&status);
    408  if (NS_SUCCEEDED(rv) && NS_SUCCEEDED(status)) {
    409    rv = listener->OnDataAvailable(channel, stream, 0, aBufLen);
    410    if (NS_FAILED(rv)) {
    411      channel->Cancel(rv);
    412    }
    413    channel->GetStatus(&status);
    414  }
    415 
    416  rv = listener->OnStopRequest(channel, status);
    417  NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
    418 
    419  document.swap(mDocument);
    420 
    421  return NS_OK;
    422 }
    423 
    424 void gfxSVGGlyphsDocument::InsertGlyphId(Element* aGlyphElement) {
    425  nsAutoString glyphIdStr;
    426  static const uint32_t glyphPrefixLength = 5;
    427  // The maximum glyph ID is 65535 so the maximum length of the numeric part
    428  // is 5.
    429  if (!aGlyphElement->GetAttr(nsGkAtoms::id, glyphIdStr) ||
    430      !StringBeginsWith(glyphIdStr, u"glyph"_ns) ||
    431      glyphIdStr.Length() > glyphPrefixLength + 5) {
    432    return;
    433  }
    434 
    435  uint32_t id = 0;
    436  for (uint32_t i = glyphPrefixLength; i < glyphIdStr.Length(); ++i) {
    437    char16_t ch = glyphIdStr.CharAt(i);
    438    if (ch < '0' || ch > '9') {
    439      return;
    440    }
    441    if (ch == '0' && i == glyphPrefixLength) {
    442      return;
    443    }
    444    id = id * 10 + (ch - '0');
    445  }
    446 
    447  mGlyphIdMap.InsertOrUpdate(id, aGlyphElement);
    448 }
    449 
    450 size_t gfxSVGGlyphsDocument::SizeOfIncludingThis(
    451    mozilla::MallocSizeOf aMallocSizeOf) const {
    452  return aMallocSizeOf(this) +
    453         mGlyphIdMap.ShallowSizeOfExcludingThis(aMallocSizeOf);
    454 }