tor-browser

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

Moz2DImageRenderer.cpp (16771B)


      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 "mozilla/StaticPrefs_gfx.h"
      8 #include "gfxUtils.h"
      9 #include "mozilla/Mutex.h"
     10 #include "mozilla/Range.h"
     11 #include "mozilla/gfx/2D.h"
     12 #include "mozilla/gfx/RectAbsolute.h"
     13 #include "mozilla/gfx/Logging.h"
     14 #include "mozilla/gfx/RecordedEvent.h"
     15 #include "mozilla/layers/WebRenderDrawEventRecorder.h"
     16 #include "WebRenderTypes.h"
     17 #include "webrender_ffi.h"
     18 #include "GeckoProfiler.h"
     19 
     20 #include <unordered_map>
     21 #ifdef FUZZING
     22 #  include "prenv.h"
     23 #endif
     24 
     25 #ifdef XP_DARWIN
     26 #  include "mozilla/gfx/UnscaledFontMac.h"
     27 #elif defined(XP_WIN)
     28 #  include "mozilla/gfx/UnscaledFontDWrite.h"
     29 #else
     30 #  include "mozilla/gfx/UnscaledFontFreeType.h"
     31 #endif
     32 
     33 namespace std {
     34 template <>
     35 struct hash<mozilla::wr::FontKey> {
     36  size_t operator()(const mozilla::wr::FontKey& key) const {
     37    return hash<size_t>()(mozilla::wr::AsUint64(key));
     38  }
     39 };
     40 
     41 template <>
     42 struct hash<mozilla::wr::FontInstanceKey> {
     43  size_t operator()(const mozilla::wr::FontInstanceKey& key) const {
     44    return hash<size_t>()(mozilla::wr::AsUint64(key));
     45  }
     46 };
     47 };  // namespace std
     48 
     49 namespace mozilla {
     50 
     51 using namespace gfx;
     52 
     53 namespace wr {
     54 
     55 struct FontTemplate {
     56  const uint8_t* mData;
     57  size_t mSize;
     58  uint32_t mIndex;
     59  const VecU8* mVec;
     60  RefPtr<UnscaledFont> mUnscaledFont;
     61 
     62  FontTemplate() : mData(nullptr), mSize(0), mIndex(0), mVec(nullptr) {}
     63 
     64  ~FontTemplate() {
     65    if (mVec) {
     66      wr_dec_ref_arc(mVec);
     67    }
     68  }
     69 };
     70 
     71 struct FontInstanceData {
     72  WrFontKey mFontKey;
     73  float mSize;
     74  Maybe<FontInstanceOptions> mOptions;
     75  Maybe<FontInstancePlatformOptions> mPlatformOptions;
     76  UniquePtr<gfx::FontVariation[]> mVariations;
     77  size_t mNumVariations;
     78  RefPtr<ScaledFont> mScaledFont;
     79 
     80  FontInstanceData() : mSize(0), mNumVariations(0) {}
     81 };
     82 
     83 StaticMutex sFontDataTableLock;
     84 MOZ_RUNINIT std::unordered_map<WrFontKey, FontTemplate> sFontDataTable;
     85 MOZ_RUNINIT std::unordered_map<WrFontInstanceKey, FontInstanceData>
     86    sBlobFontTable;
     87 
     88 // Fixed-size ring buffer logging font deletion events to aid debugging.
     89 static struct FontDeleteLog {
     90  static const size_t MAX_ENTRIES = 256;
     91 
     92  uint64_t mEntries[MAX_ENTRIES] = {0};
     93  size_t mNextEntry = 0;
     94 
     95  void AddEntry(uint64_t aEntry) {
     96    mEntries[mNextEntry] = aEntry;
     97    mNextEntry = (mNextEntry + 1) % MAX_ENTRIES;
     98  }
     99 
    100  void Add(WrFontKey aKey) { AddEntry(AsUint64(aKey)); }
    101 
    102  // Store namespace clears as font id 0, since this will never be allocated.
    103  void Add(WrIdNamespace aNamespace) {
    104    AddEntry(AsUint64(WrFontKey{aNamespace, 0}));
    105  }
    106 
    107  void AddAll() { AddEntry(~0); }
    108 
    109  // Find a matching entry in the log, searching backwards starting at the
    110  // newest entry and finishing with the oldest entry. Returns a brief
    111  // description of why the font was deleted, if known.
    112  const char* Find(WrFontKey aKey) {
    113    uint64_t keyEntry = AsUint64(aKey);
    114    uint64_t namespaceEntry = AsUint64(WrFontKey{aKey.mNamespace, 0});
    115    size_t offset = mNextEntry;
    116    do {
    117      offset = (offset + MAX_ENTRIES - 1) % MAX_ENTRIES;
    118      if (mEntries[offset] == keyEntry) {
    119        return "deleted font";
    120      } else if (mEntries[offset] == namespaceEntry) {
    121        return "cleared namespace";
    122      } else if (mEntries[offset] == (uint64_t)~0) {
    123        return "cleared all";
    124      }
    125    } while (offset != mNextEntry);
    126    return "unknown font";
    127  }
    128 } sFontDeleteLog;
    129 
    130 void ClearAllBlobImageResources() {
    131  StaticMutexAutoLock lock(sFontDataTableLock);
    132  sFontDeleteLog.AddAll();
    133  sBlobFontTable.clear();
    134  sFontDataTable.clear();
    135 }
    136 
    137 extern "C" {
    138 void ClearBlobImageResources(WrIdNamespace aNamespace) {
    139  StaticMutexAutoLock lock(sFontDataTableLock);
    140  sFontDeleteLog.Add(aNamespace);
    141  for (auto i = sBlobFontTable.begin(); i != sBlobFontTable.end();) {
    142    if (i->first.mNamespace == aNamespace) {
    143      i = sBlobFontTable.erase(i);
    144    } else {
    145      i++;
    146    }
    147  }
    148  for (auto i = sFontDataTable.begin(); i != sFontDataTable.end();) {
    149    if (i->first.mNamespace == aNamespace) {
    150      i = sFontDataTable.erase(i);
    151    } else {
    152      i++;
    153    }
    154  }
    155 }
    156 
    157 bool HasFontData(WrFontKey aKey) {
    158  StaticMutexAutoLock lock(sFontDataTableLock);
    159  return sFontDataTable.find(aKey) != sFontDataTable.end();
    160 }
    161 
    162 void AddFontData(WrFontKey aKey, const uint8_t* aData, size_t aSize,
    163                 uint32_t aIndex, const ArcVecU8* aVec) {
    164  StaticMutexAutoLock lock(sFontDataTableLock);
    165  auto i = sFontDataTable.find(aKey);
    166  if (i == sFontDataTable.end()) {
    167    FontTemplate& font = sFontDataTable[aKey];
    168    font.mData = aData;
    169    font.mSize = aSize;
    170    font.mIndex = aIndex;
    171    font.mVec = wr_add_ref_arc(aVec);
    172  }
    173 }
    174 
    175 void AddNativeFontHandle(WrFontKey aKey, void* aHandle, uint32_t aIndex) {
    176  StaticMutexAutoLock lock(sFontDataTableLock);
    177  auto i = sFontDataTable.find(aKey);
    178  if (i == sFontDataTable.end()) {
    179    FontTemplate& font = sFontDataTable[aKey];
    180 #ifdef XP_DARWIN
    181    font.mUnscaledFont =
    182        new UnscaledFontMac(reinterpret_cast<CGFontRef>(aHandle), false);
    183 #elif defined(XP_WIN)
    184    font.mUnscaledFont = new UnscaledFontDWrite(
    185        reinterpret_cast<IDWriteFontFace*>(aHandle), nullptr);
    186 #elif defined(ANDROID)
    187    font.mUnscaledFont = new UnscaledFontFreeType(
    188        reinterpret_cast<const char*>(aHandle), aIndex);
    189 #else
    190    font.mUnscaledFont = new UnscaledFontFontconfig(
    191        reinterpret_cast<const char*>(aHandle), aIndex);
    192 #endif
    193  }
    194 }
    195 
    196 void DeleteFontData(WrFontKey aKey) {
    197  StaticMutexAutoLock lock(sFontDataTableLock);
    198  sFontDeleteLog.Add(aKey);
    199  auto i = sFontDataTable.find(aKey);
    200  if (i != sFontDataTable.end()) {
    201    sFontDataTable.erase(i);
    202  }
    203 }
    204 
    205 void AddBlobFont(WrFontInstanceKey aInstanceKey, WrFontKey aFontKey,
    206                 float aSize, const FontInstanceOptions* aOptions,
    207                 const FontInstancePlatformOptions* aPlatformOptions,
    208                 const FontVariation* aVariations, size_t aNumVariations) {
    209  StaticMutexAutoLock lock(sFontDataTableLock);
    210  auto i = sBlobFontTable.find(aInstanceKey);
    211  if (i == sBlobFontTable.end()) {
    212    FontInstanceData& font = sBlobFontTable[aInstanceKey];
    213    font.mFontKey = aFontKey;
    214    font.mSize = aSize;
    215    if (aOptions) {
    216      font.mOptions = Some(*aOptions);
    217    }
    218    if (aPlatformOptions) {
    219      font.mPlatformOptions = Some(*aPlatformOptions);
    220    }
    221    if (aNumVariations) {
    222      font.mNumVariations = aNumVariations;
    223      font.mVariations.reset(new gfx::FontVariation[aNumVariations]);
    224      PodCopy(font.mVariations.get(),
    225              reinterpret_cast<const gfx::FontVariation*>(aVariations),
    226              aNumVariations);
    227    }
    228  }
    229 }
    230 
    231 void DeleteBlobFont(WrFontInstanceKey aKey) {
    232  StaticMutexAutoLock lock(sFontDataTableLock);
    233  auto i = sBlobFontTable.find(aKey);
    234  if (i != sBlobFontTable.end()) {
    235    sBlobFontTable.erase(i);
    236  }
    237 }
    238 
    239 }  // extern
    240 
    241 static RefPtr<UnscaledFont> GetUnscaledFont(Translator* aTranslator,
    242                                            WrFontKey aKey) {
    243  auto i = sFontDataTable.find(aKey);
    244  if (i == sFontDataTable.end()) {
    245    gfxDevCrash(LogReason::UnscaledFontNotFound)
    246        << "Failed to get UnscaledFont entry for FontKey " << aKey.mHandle
    247        << " because " << sFontDeleteLog.Find(aKey);
    248    return nullptr;
    249  }
    250  FontTemplate& data = i->second;
    251  if (data.mUnscaledFont) {
    252    return data.mUnscaledFont;
    253  }
    254  MOZ_ASSERT(data.mData);
    255  FontType type =
    256 #ifdef XP_DARWIN
    257      FontType::MAC;
    258 #elif defined(XP_WIN)
    259      FontType::DWRITE;
    260 #elif defined(ANDROID)
    261      FontType::FREETYPE;
    262 #else
    263      FontType::FONTCONFIG;
    264 #endif
    265  // makes a copy of the data
    266  RefPtr<NativeFontResource> fontResource = Factory::CreateNativeFontResource(
    267      (uint8_t*)data.mData, data.mSize, type, aTranslator->GetFontContext());
    268  RefPtr<UnscaledFont> unscaledFont;
    269  if (!fontResource) {
    270    gfxDevCrash(LogReason::NativeFontResourceNotFound)
    271        << "Failed to create NativeFontResource for FontKey " << aKey.mHandle;
    272  } else {
    273    // Instance data is only needed for GDI fonts which webrender does not
    274    // support.
    275    unscaledFont = fontResource->CreateUnscaledFont(data.mIndex, nullptr, 0);
    276    if (!unscaledFont) {
    277      gfxDevCrash(LogReason::UnscaledFontNotFound)
    278          << "Failed to create UnscaledFont for FontKey " << aKey.mHandle;
    279    }
    280  }
    281  data.mUnscaledFont = unscaledFont;
    282  return unscaledFont;
    283 }
    284 
    285 static RefPtr<ScaledFont> GetScaledFont(Translator* aTranslator,
    286                                        WrFontInstanceKey aKey) {
    287  StaticMutexAutoLock lock(sFontDataTableLock);
    288  auto i = sBlobFontTable.find(aKey);
    289  if (i == sBlobFontTable.end()) {
    290    gfxDevCrash(LogReason::ScaledFontNotFound)
    291        << "Failed to get ScaledFont entry for FontInstanceKey "
    292        << aKey.mHandle;
    293    return nullptr;
    294  }
    295  FontInstanceData& data = i->second;
    296  if (data.mScaledFont) {
    297    return data.mScaledFont;
    298  }
    299  RefPtr<UnscaledFont> unscaled = GetUnscaledFont(aTranslator, data.mFontKey);
    300  if (!unscaled) {
    301    return nullptr;
    302  }
    303  RefPtr<ScaledFont> scaled = unscaled->CreateScaledFontFromWRFont(
    304      data.mSize, data.mOptions.ptrOr(nullptr),
    305      data.mPlatformOptions.ptrOr(nullptr), data.mVariations.get(),
    306      data.mNumVariations);
    307  if (!scaled) {
    308    gfxDevCrash(LogReason::ScaledFontNotFound)
    309        << "Failed to create ScaledFont for FontKey " << aKey.mHandle;
    310  }
    311  data.mScaledFont = scaled;
    312  return data.mScaledFont;
    313 }
    314 
    315 template <typename T>
    316 T ConvertFromBytes(const uint8_t* bytes) {
    317  T t;
    318  memcpy(&t, bytes, sizeof(T));
    319  return t;
    320 }
    321 
    322 struct Reader {
    323  const uint8_t* buf;
    324  size_t len;
    325  size_t pos;
    326 
    327  Reader(const uint8_t* buf, size_t len) : buf(buf), len(len), pos(0) {}
    328 
    329  template <typename T>
    330  T Read() {
    331    MOZ_RELEASE_ASSERT(pos + sizeof(T) <= len);
    332    T ret = ConvertFromBytes<T>(buf + pos);
    333    pos += sizeof(T);
    334    return ret;
    335  }
    336 
    337  size_t ReadSize() { return Read<size_t>(); }
    338  int ReadInt() { return Read<int>(); }
    339 
    340  IntRectAbsolute ReadBounds() { return Read<IntRectAbsolute>(); }
    341 
    342  layers::BlobFont ReadBlobFont() { return Read<layers::BlobFont>(); }
    343 };
    344 
    345 static bool Moz2DRenderCallback(const Range<const uint8_t> aBlob,
    346                                gfx::SurfaceFormat aFormat,
    347                                const mozilla::wr::DeviceIntRect* aVisibleRect,
    348                                const mozilla::wr::LayoutIntRect* aRenderRect,
    349                                const uint16_t aTileSize,
    350                                const mozilla::wr::TileOffset* aTileOffset,
    351                                const mozilla::wr::LayoutIntRect* aDirtyRect,
    352                                Range<uint8_t> aOutput) {
    353  IntSize size(aRenderRect->width(), aRenderRect->height());
    354  AUTO_PROFILER_MARKER("RasterizeSingleBlob", GRAPHICS);
    355 #ifndef FUZZING
    356  MOZ_RELEASE_ASSERT(size.width > 0 && size.height > 0);
    357 #endif
    358  if (size.width <= 0 || size.height <= 0) {
    359    return false;
    360  }
    361 
    362  auto stride = size.width * gfx::BytesPerPixel(aFormat);
    363 
    364  if (aOutput.length() < static_cast<size_t>(size.height * stride)) {
    365    return false;
    366  }
    367 
    368  // In bindings.rs we allocate a buffer filled with opaque white.
    369  bool uninitialized = false;
    370 
    371  RefPtr<gfx::DrawTarget> dt = gfx::Factory::CreateDrawTargetForData(
    372      gfx::BackendType::SKIA, aOutput.begin().get(), size, stride, aFormat,
    373      uninitialized);
    374 
    375  if (!dt) {
    376    return false;
    377  }
    378 
    379  // aRenderRect is the part of the blob that we are currently rendering
    380  // (for example a tile) in the same coordinate space as aVisibleRect.
    381  IntPoint origin = gfx::IntPoint(aRenderRect->min.x, aRenderRect->min.y);
    382 
    383  dt = gfx::Factory::CreateOffsetDrawTarget(dt, origin);
    384  if (!dt) {
    385    return false;
    386  }
    387 
    388  // We try hard to not have empty blobs but we can end up with
    389  // them because of CompositorHitTestInfo and merging.
    390  size_t footerSize = sizeof(size_t);
    391  MOZ_RELEASE_ASSERT(aBlob.length() >= footerSize);
    392  size_t indexOffset = ConvertFromBytes<size_t>(aBlob.end().get() - footerSize);
    393 
    394  MOZ_RELEASE_ASSERT(indexOffset <= aBlob.length() - footerSize);
    395  Reader reader(aBlob.begin().get() + indexOffset,
    396                aBlob.length() - footerSize - indexOffset);
    397 
    398  auto bounds = gfx::IntRect(origin, size);
    399 
    400  if (aDirtyRect) {
    401    gfx::Rect dirty(aDirtyRect->min.x, aDirtyRect->min.y, aDirtyRect->width(),
    402                    aDirtyRect->height());
    403    dt->PushClipRect(dirty);
    404    bounds =
    405        bounds.Intersect(IntRect(aDirtyRect->min.x, aDirtyRect->min.y,
    406                                 aDirtyRect->width(), aDirtyRect->height()));
    407  }
    408 
    409  bool ret = true;
    410  size_t offset = 0;
    411  auto absBounds = IntRectAbsolute::FromRect(bounds);
    412  while (reader.pos < reader.len) {
    413    size_t end = reader.ReadSize();
    414    size_t extra_end = reader.ReadSize();
    415    MOZ_RELEASE_ASSERT(offset <= end);
    416    MOZ_RELEASE_ASSERT(extra_end >= end);
    417    MOZ_RELEASE_ASSERT(extra_end < aBlob.length());
    418 
    419    auto combinedBounds = absBounds.Intersect(reader.ReadBounds());
    420    if (combinedBounds.IsEmpty()) {
    421      offset = extra_end;
    422      continue;
    423    }
    424 
    425    layers::WebRenderTranslator translator(dt);
    426    Reader fontReader(aBlob.begin().get() + end, extra_end - end);
    427    size_t count = fontReader.ReadSize();
    428    for (size_t i = 0; i < count; i++) {
    429      layers::BlobFont blobFont = fontReader.ReadBlobFont();
    430      RefPtr<ScaledFont> scaledFont =
    431          GetScaledFont(&translator, blobFont.mFontInstanceKey);
    432      translator.AddScaledFont(blobFont.mScaledFontPtr, scaledFont);
    433    }
    434 
    435    Range<const uint8_t> blob(aBlob.begin() + offset, aBlob.begin() + end);
    436    ret =
    437        translator.TranslateRecording((char*)blob.begin().get(), blob.length());
    438    if (!ret) {
    439      gfxCriticalNote << "Replay failure: " << translator.GetError();
    440      MOZ_RELEASE_ASSERT(false);
    441    }
    442    offset = extra_end;
    443  }
    444 
    445  if (StaticPrefs::gfx_webrender_debug_blob_paint_flashing()) {
    446    dt->SetTransform(gfx::Matrix());
    447    float r = float(rand()) / float(RAND_MAX);
    448    float g = float(rand()) / float(RAND_MAX);
    449    float b = float(rand()) / float(RAND_MAX);
    450    dt->FillRect(gfx::Rect(origin.x, origin.y, size.width, size.height),
    451                 gfx::ColorPattern(gfx::DeviceColor(r, g, b, 0.5)));
    452  }
    453 
    454  if (aDirtyRect) {
    455    dt->PopClip();
    456  }
    457 
    458 #if 0
    459  static int i = 0;
    460  char filename[40];
    461  sprintf(filename, "out%d.png", i++);
    462  gfxUtils::WriteAsPNG(dt, filename);
    463 #endif
    464 
    465  return ret;
    466 }
    467 
    468 }  // namespace wr
    469 }  // namespace mozilla
    470 
    471 #ifdef FUZZING
    472 mozilla::StaticMutex sFuzzDumpLock;
    473 static void writehex(const void* bytes, size_t len) {
    474  const uint8_t* ptr = static_cast<const uint8_t*>(bytes);
    475  while (len--) printf("%02X", *ptr++ & 0xFF);
    476 }
    477 #endif
    478 
    479 extern "C" {
    480 
    481 bool wr_moz2d_render_cb(const mozilla::wr::ByteSlice blob,
    482                        mozilla::wr::ImageFormat aFormat,
    483                        const mozilla::wr::LayoutIntRect* aRenderRect,
    484                        const mozilla::wr::DeviceIntRect* aVisibleRect,
    485                        const uint16_t aTileSize,
    486                        const mozilla::wr::TileOffset* aTileOffset,
    487                        const mozilla::wr::LayoutIntRect* aDirtyRect,
    488                        mozilla::wr::MutByteSlice output) {
    489 #ifdef FUZZING
    490  if (!!PR_GetEnv("MOZ2D_RECORD")) {
    491    sFuzzDumpLock.Lock();
    492    printf("<dump>");
    493    writehex(&aFormat, sizeof(uint8_t));
    494    writehex(&aRenderRect->min.x, sizeof(int32_t));
    495    writehex(&aRenderRect->min.y, sizeof(int32_t));
    496    writehex(&aRenderRect->max.x, sizeof(int32_t));
    497    writehex(&aRenderRect->max.y, sizeof(int32_t));
    498    writehex(&aVisibleRect->min.x, sizeof(int32_t));
    499    writehex(&aVisibleRect->min.y, sizeof(int32_t));
    500    writehex(&aVisibleRect->max.x, sizeof(int32_t));
    501    writehex(&aVisibleRect->max.y, sizeof(int32_t));
    502    writehex(&aTileSize, sizeof(uint16_t));
    503    if (aTileSize) {
    504      writehex(&aTileOffset->x, sizeof(int32_t));
    505      writehex(&aTileOffset->y, sizeof(int32_t));
    506    }
    507    uint8_t byte = aDirtyRect ? 1 : 0;
    508    writehex(&byte, 1);
    509    if (byte) {
    510      writehex(&aDirtyRect->min.x, sizeof(int32_t));
    511      writehex(&aDirtyRect->min.y, sizeof(int32_t));
    512      writehex(&aDirtyRect->max.x, sizeof(int32_t));
    513      writehex(&aDirtyRect->max.y, sizeof(int32_t));
    514    }
    515    writehex(&output.len, sizeof(uint32_t));
    516    writehex(blob.buffer, blob.len);
    517    printf("</dump>\n");
    518    sFuzzDumpLock.Unlock();
    519  }
    520 #endif
    521 
    522  return mozilla::wr::Moz2DRenderCallback(
    523      mozilla::wr::ByteSliceToRange(blob),
    524      mozilla::wr::ImageFormatToSurfaceFormat(aFormat), aVisibleRect,
    525      aRenderRect, aTileSize, aTileOffset, aDirtyRect,
    526      mozilla::wr::MutByteSliceToRange(output));
    527 }
    528 
    529 }  // extern