tor-browser

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

BlobSurfaceProvider.cpp (10970B)


      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 "BlobSurfaceProvider.h"
      8 #include "AutoRestoreSVGState.h"
      9 #include "ImageRegion.h"
     10 #include "SVGDocumentWrapper.h"
     11 #include "mozilla/PresShell.h"
     12 #include "mozilla/dom/Document.h"
     13 #include "mozilla/gfx/2D.h"
     14 #include "mozilla/layers/IpcResourceUpdateQueue.h"
     15 #include "mozilla/layers/WebRenderBridgeChild.h"
     16 #include "mozilla/layers/WebRenderDrawEventRecorder.h"
     17 
     18 using namespace mozilla::gfx;
     19 using namespace mozilla::layers;
     20 
     21 namespace mozilla::image {
     22 
     23 BlobSurfaceProvider::BlobSurfaceProvider(
     24    const ImageKey aImageKey, const SurfaceKey& aSurfaceKey,
     25    image::SVGDocumentWrapper* aSVGDocumentWrapper, uint32_t aImageFlags)
     26    : ISurfaceProvider(aImageKey, aSurfaceKey,
     27                       AvailabilityState::StartAvailable()),
     28      mSVGDocumentWrapper(aSVGDocumentWrapper),
     29      mImageFlags(aImageFlags) {
     30  MOZ_ASSERT(mSVGDocumentWrapper);
     31  MOZ_ASSERT(aImageFlags & imgIContainer::FLAG_RECORD_BLOB);
     32 }
     33 
     34 BlobSurfaceProvider::~BlobSurfaceProvider() {
     35  if (NS_IsMainThread()) {
     36    DestroyKeys(mKeys);
     37    return;
     38  }
     39 
     40  NS_ReleaseOnMainThread("SourceSurfaceBlobImage::mSVGDocumentWrapper",
     41                         mSVGDocumentWrapper.forget());
     42  NS_DispatchToMainThread(
     43      NS_NewRunnableFunction("SourceSurfaceBlobImage::DestroyKeys",
     44                             [keys = std::move(mKeys)] { DestroyKeys(keys); }));
     45 }
     46 
     47 /* static */ void BlobSurfaceProvider::DestroyKeys(
     48    const AutoTArray<BlobImageKeyData, 1>& aKeys) {
     49  for (const auto& entry : aKeys) {
     50    if (entry.mManager->IsDestroyed()) {
     51      continue;
     52    }
     53 
     54    WebRenderBridgeChild* wrBridge = entry.mManager->WrBridge();
     55    if (!wrBridge || !wrBridge->MatchesNamespace(entry.mBlobKey)) {
     56      continue;
     57    }
     58 
     59    entry.mManager->GetRenderRootStateManager()->AddBlobImageKeyForDiscard(
     60        entry.mBlobKey);
     61  }
     62 }
     63 
     64 nsresult BlobSurfaceProvider::UpdateKey(
     65    layers::RenderRootStateManager* aManager,
     66    wr::IpcResourceUpdateQueue& aResources, wr::ImageKey& aKey) {
     67  MOZ_ASSERT(NS_IsMainThread());
     68 
     69  layers::WebRenderLayerManager* manager = aManager->LayerManager();
     70  MOZ_ASSERT(manager);
     71 
     72  Maybe<wr::BlobImageKey> key;
     73  auto i = mKeys.Length();
     74  while (i > 0) {
     75    --i;
     76    BlobImageKeyData& entry = mKeys[i];
     77    if (entry.mManager->IsDestroyed()) {
     78      mKeys.RemoveElementAt(i);
     79    } else if (entry.mManager == manager) {
     80      WebRenderBridgeChild* wrBridge = manager->WrBridge();
     81      MOZ_ASSERT(wrBridge);
     82 
     83      bool ownsKey = wrBridge->MatchesNamespace(entry.mBlobKey);
     84      if (ownsKey && !entry.mDirty) {
     85        key.emplace(entry.mBlobKey);
     86        continue;
     87      }
     88 
     89      // Even if the manager is the same, its underlying WebRenderBridgeChild
     90      // can change state. Either our namespace differs, and our old key has
     91      // already been discarded, or the blob has changed. Either way, we need
     92      // to rerecord it.
     93      auto newEntry = RecordDrawing(manager, aResources,
     94                                    ownsKey ? Some(entry.mBlobKey) : Nothing());
     95      if (!newEntry) {
     96        if (ownsKey) {
     97          aManager->AddBlobImageKeyForDiscard(entry.mBlobKey);
     98        }
     99        mKeys.RemoveElementAt(i);
    100        continue;
    101      }
    102 
    103      key.emplace(newEntry.ref().mBlobKey);
    104      entry = std::move(newEntry.ref());
    105      MOZ_ASSERT(!entry.mDirty);
    106    }
    107  }
    108 
    109  // We didn't find an entry. Attempt to record the blob with a new key.
    110  if (!key) {
    111    auto newEntry = RecordDrawing(manager, aResources, Nothing());
    112    if (newEntry) {
    113      key.emplace(newEntry.ref().mBlobKey);
    114      mKeys.AppendElement(std::move(newEntry.ref()));
    115    }
    116  }
    117 
    118  if (key) {
    119    aKey = wr::AsImageKey(key.value());
    120    return NS_OK;
    121  }
    122 
    123  return NS_ERROR_FAILURE;
    124 }
    125 
    126 void BlobSurfaceProvider::InvalidateSurface() {
    127  MOZ_ASSERT(NS_IsMainThread());
    128 
    129  auto i = mKeys.Length();
    130  while (i > 0) {
    131    --i;
    132    BlobImageKeyData& entry = mKeys[i];
    133    if (entry.mManager->IsDestroyed()) {
    134      mKeys.RemoveElementAt(i);
    135    } else {
    136      entry.mDirty = true;
    137    }
    138  }
    139 }
    140 
    141 Maybe<BlobImageKeyData> BlobSurfaceProvider::RecordDrawing(
    142    WebRenderLayerManager* aManager, wr::IpcResourceUpdateQueue& aResources,
    143    Maybe<wr::BlobImageKey> aBlobKey) {
    144  MOZ_ASSERT(!aManager->IsDestroyed());
    145 
    146  if (mSVGDocumentWrapper->IsDrawing()) {
    147    return Nothing();
    148  }
    149 
    150  // This is either our first pass, or we have a stale key requiring us to
    151  // re-record the SVG image draw commands.
    152  auto* rootManager = aManager->GetRenderRootStateManager();
    153  auto* wrBridge = aManager->WrBridge();
    154 
    155  const auto& size = GetSurfaceKey().Size();
    156  const auto& region = GetSurfaceKey().Region();
    157  const auto& svgContext = GetSurfaceKey().SVGContext();
    158 
    159  IntRect imageRect = region ? region->Rect() : IntRect(IntPoint(0, 0), size);
    160  IntRect imageRectOrigin = imageRect - imageRect.TopLeft();
    161 
    162  std::vector<RefPtr<ScaledFont>> fonts;
    163  bool validFonts = true;
    164  RefPtr<WebRenderDrawEventRecorder> recorder =
    165      MakeAndAddRef<WebRenderDrawEventRecorder>(
    166          [&](MemStream& aStream,
    167              std::vector<RefPtr<ScaledFont>>& aScaledFonts) {
    168            auto count = aScaledFonts.size();
    169            aStream.write((const char*)&count, sizeof(count));
    170 
    171            for (auto& scaled : aScaledFonts) {
    172              Maybe<wr::FontInstanceKey> key =
    173                  wrBridge->GetFontKeyForScaledFont(scaled, aResources);
    174              if (key.isNothing()) {
    175                validFonts = false;
    176                break;
    177              }
    178              BlobFont font = {key.value(), scaled};
    179              aStream.write((const char*)&font, sizeof(font));
    180            }
    181 
    182            fonts = std::move(aScaledFonts);
    183          });
    184 
    185  RefPtr<DrawTarget> dummyDt =
    186      gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
    187  RefPtr<DrawTarget> dt =
    188      Factory::CreateRecordingDrawTarget(recorder, dummyDt, imageRectOrigin);
    189 
    190  if (!dt || !dt->IsValid()) {
    191    return Nothing();
    192  }
    193 
    194  bool contextPaint = svgContext.GetContextPaint();
    195 
    196  float animTime = (GetSurfaceKey().Playback() == PlaybackType::eStatic)
    197                       ? 0.0f
    198                       : mSVGDocumentWrapper->GetCurrentTimeAsFloat();
    199 
    200  IntSize viewportSize = size;
    201  if (auto cssViewportSize = svgContext.GetViewportSize()) {
    202    // XXX losing unit
    203    viewportSize.SizeTo(cssViewportSize->width, cssViewportSize->height);
    204  }
    205 
    206  {
    207    // Get (& sanity-check) the helper-doc's presShell
    208    RefPtr<PresShell> presShell = mSVGDocumentWrapper->GetPresShell();
    209    MOZ_ASSERT(presShell, "GetPresShell returned null for an SVG image?");
    210 
    211    nsPresContext* presContext = presShell->GetPresContext();
    212    MOZ_ASSERT(presContext, "pres shell w/out pres context");
    213 
    214    auto* doc = presShell->GetDocument();
    215    [[maybe_unused]] nsIURI* uri = doc ? doc->GetDocumentURI() : nullptr;
    216    AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING(
    217        "SVG Image recording", GRAPHICS,
    218        nsPrintfCString("(%d,%d) %dx%d from %dx%d %s", imageRect.x, imageRect.y,
    219                        imageRect.width, imageRect.height, size.width,
    220                        size.height,
    221                        uri ? uri->GetSpecOrDefault().get() : "N/A"));
    222 
    223    AutoRestoreSVGState autoRestore(svgContext, animTime, mSVGDocumentWrapper,
    224                                    contextPaint);
    225 
    226    mSVGDocumentWrapper->UpdateViewportBounds(viewportSize);
    227    mSVGDocumentWrapper->FlushImageTransformInvalidation();
    228 
    229    gfxContext ctx(dt);
    230 
    231    nsRect svgRect;
    232    auto auPerDevPixel = presContext->AppUnitsPerDevPixel();
    233    if (size != viewportSize) {
    234      auto scaleX = double(size.width) / viewportSize.width;
    235      auto scaleY = double(size.height) / viewportSize.height;
    236      ctx.SetMatrix(Matrix::Scaling(float(scaleX), float(scaleY)));
    237 
    238      auto scaledVisibleRect = IntRectToRect(imageRect);
    239      scaledVisibleRect.Scale(float(auPerDevPixel / scaleX),
    240                              float(auPerDevPixel / scaleY));
    241      scaledVisibleRect.Round();
    242      svgRect.SetRect(
    243          int32_t(scaledVisibleRect.x), int32_t(scaledVisibleRect.y),
    244          int32_t(scaledVisibleRect.width), int32_t(scaledVisibleRect.height));
    245    } else {
    246      auto scaledVisibleRect(imageRect);
    247      scaledVisibleRect.Scale(auPerDevPixel);
    248      svgRect.SetRect(scaledVisibleRect.x, scaledVisibleRect.y,
    249                      scaledVisibleRect.width, scaledVisibleRect.height);
    250    }
    251 
    252    RenderDocumentFlags renderDocFlags =
    253        RenderDocumentFlags::IgnoreViewportScrolling;
    254    if (!(mImageFlags & imgIContainer::FLAG_SYNC_DECODE)) {
    255      renderDocFlags |= RenderDocumentFlags::AsyncDecodeImages;
    256    }
    257    if (mImageFlags & imgIContainer::FLAG_HIGH_QUALITY_SCALING) {
    258      renderDocFlags |= RenderDocumentFlags::UseHighQualityScaling;
    259    }
    260 
    261    presShell->RenderDocument(svgRect, renderDocFlags,
    262                              NS_RGBA(0, 0, 0, 0),  // transparent
    263                              &ctx);
    264  }
    265 
    266  recorder->FlushItem(imageRectOrigin);
    267  recorder->Finish();
    268 
    269  if (!validFonts) {
    270    gfxCriticalNote << "Failed serializing fonts for blob vector image";
    271    return Nothing();
    272  }
    273 
    274  Range<uint8_t> bytes((uint8_t*)recorder->mOutputStream.mData,
    275                       recorder->mOutputStream.mLength);
    276  wr::BlobImageKey key = aBlobKey
    277                             ? aBlobKey.value()
    278                             : wr::BlobImageKey{wrBridge->GetNextImageKey()};
    279  wr::ImageDescriptor descriptor(imageRect.Size(), 0, SurfaceFormat::OS_RGBA,
    280                                 wr::OpacityType::HasAlphaChannel);
    281 
    282  auto visibleRect = ImageIntRect::FromUnknownRect(imageRectOrigin);
    283  if (aBlobKey) {
    284    if (!aResources.UpdateBlobImage(key, descriptor, bytes, visibleRect,
    285                                    visibleRect)) {
    286      return Nothing();
    287    }
    288  } else if (!aResources.AddBlobImage(key, descriptor, bytes, visibleRect)) {
    289    return Nothing();
    290  }
    291 
    292  DrawEventRecorderPrivate::ExternalSurfacesHolder externalSurfaces;
    293  recorder->TakeExternalSurfaces(externalSurfaces);
    294 
    295  for (auto& entry : externalSurfaces) {
    296    // While we don't use the image key with the surface, because the blob image
    297    // renderer doesn't have easy access to the resource set, we still want to
    298    // ensure one is generated. That will ensure the surface remains alive until
    299    // at least the last epoch which the blob image could be used in.
    300    wr::ImageKey key = {};
    301    DebugOnly<nsresult> rv = SharedSurfacesChild::Share(
    302        entry.mSurface, rootManager, aResources, key);
    303    MOZ_ASSERT(rv.value != NS_ERROR_NOT_IMPLEMENTED);
    304  }
    305 
    306  return Some(BlobImageKeyData(aManager, key, std::move(fonts),
    307                               std::move(externalSurfaces)));
    308 }
    309 
    310 }  // namespace mozilla::image