Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Firefox/gfx/thebes/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 140 kB image not shown  

Quelle  gfxPlatform.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "mozilla/FontPropertyTypes.h"
#include "mozilla/RDDProcessManager.h"
#include "mozilla/image/ImageMemoryReporter.h"
#include "mozilla/layers/CompositorManagerChild.h"
#include "mozilla/layers/CompositorThread.h"
#include "mozilla/layers/ImageBridgeChild.h"
#include "mozilla/layers/ISurfaceAllocator.h"  // for GfxMemoryImageReporter
#include "mozilla/layers/CompositorBridgeChild.h"
#include "mozilla/layers/RemoteTextureMap.h"
#include "mozilla/layers/VideoBridgeParent.h"
#include "mozilla/webrender/RenderThread.h"
#include "mozilla/webrender/WebRenderAPI.h"
#include "mozilla/webrender/webrender_ffi.h"
#include "mozilla/gfx/BuildConstants.h"
#include "mozilla/gfx/gfxConfigManager.h"
#include "mozilla/gfx/gfxVars.h"
#include "mozilla/gfx/GPUProcessManager.h"
#include "mozilla/gfx/GraphicsMessages.h"
#include "mozilla/gfx/CanvasRenderThread.h"
#include "mozilla/gfx/CanvasShutdownManager.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/EnumTypeTraits.h"
#include "mozilla/StaticPrefs_accessibility.h"
#include "mozilla/StaticPrefs_apz.h"
#include "mozilla/StaticPrefs_bidi.h"
#include "mozilla/StaticPrefs_gfx.h"
#include "mozilla/StaticPrefs_layout.h"
#include "mozilla/StaticPrefs_layers.h"
#include "mozilla/StaticPrefs_media.h"
#include "mozilla/StaticPrefs_privacy.h"
#include "mozilla/StaticPrefs_webgl.h"
#include "mozilla/StaticPrefs_widget.h"
#include "mozilla/Telemetry.h"
#include "mozilla/glean/GfxMetrics.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/Unused.h"
#include "mozilla/IntegerPrintfMacros.h"
#include "mozilla/Base64.h"
#include "mozilla/VsyncDispatcher.h"

#include "mozilla/Logging.h"
#include "mozilla/Components.h"
#include "nsAppRunner.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsCSSProps.h"
#include "nsContentUtils.h"

#include "gfxCrashReporterUtils.h"
#include "gfxPlatform.h"
#include "gfxPlatformWorker.h"

#include "gfxBlur.h"
#include "gfxEnv.h"
#include "gfxTextRun.h"
#include "gfxUserFontSet.h"
#include "gfxConfig.h"
#include "GfxDriverInfo.h"
#include "VRProcessManager.h"
#include "VRThread.h"

#ifdef XP_WIN
#  include <process.h>
#  define getpid _getpid
#else
#  include <unistd.h>
#endif

#include "nsXULAppAPI.h"
#include "nsIXULAppInfo.h"
#include "nsDirectoryServiceUtils.h"
#include "nsDirectoryServiceDefs.h"

#if defined(XP_WIN)
#  include "gfxWindowsPlatform.h"
#  include "mozilla/widget/WinWindowOcclusionTracker.h"
#elif defined(XP_DARWIN)
#  include "gfxPlatformMac.h"
#  include "gfxQuartzSurface.h"
#elif defined(MOZ_WIDGET_GTK)
#  include "gfxPlatformGtk.h"
#elif defined(ANDROID)
#  include "gfxAndroidPlatform.h"
#endif
#if defined(MOZ_WIDGET_ANDROID)
#  include "mozilla/jni/Utils.h"  // for IsFennec
#endif

#ifdef XP_WIN
#  include "mozilla/WindowsVersion.h"
#  include "WinUtils.h"
#endif

#include "nsGkAtoms.h"
#include "gfxPlatformFontList.h"
#include "gfxContext.h"
#include "gfxImageSurface.h"
#include "nsUnicodeProperties.h"
#include "harfbuzz/hb.h"
#include "gfxGraphiteShaper.h"
#include "gfx2DGlue.h"
#include "gfxGradientCache.h"
#include "gfxUtils.h"  // for NextPowerOfTwo
#include "gfxFontMissingGlyphs.h"

#include "nsExceptionHandler.h"
#include "nsServiceManagerUtils.h"
#include "nsTArray.h"
#include "nsIObserverService.h"
#include "mozilla/widget/Screen.h"
#include "mozilla/widget/ScreenManager.h"
#include "MainThreadUtils.h"

#include "nsWeakReference.h"

#include "cairo.h"
#include "qcms.h"

#include "imgITools.h"

#include "nsCRT.h"
#include "GLContext.h"
#include "GLContextProvider.h"
#include "mozilla/gfx/Logging.h"

#ifdef __GNUC__
#  pragma GCC diagnostic push
#  pragma GCC diagnostic ignored "-Wshadow"
#endif
#include "skia/include/core/SkGraphics.h"
#ifdef MOZ_ENABLE_FREETYPE
#  include "skia/include/ports/SkTypeface_cairo.h"
#endif
#include "mozilla/gfx/SkMemoryReporter.h"
#ifdef __GNUC__
#  pragma GCC diagnostic pop  // -Wshadow
#endif
static const uint32_t kDefaultGlyphCacheSize = -1;

#include "mozilla/Preferences.h"
#include "mozilla/Assertions.h"
#include "mozilla/Atomics.h"
#include "mozilla/Attributes.h"
#include "mozilla/Mutex.h"

#include "nsIGfxInfo.h"
#include "nsIXULRuntime.h"
#include "VsyncSource.h"
#include "SoftwareVsyncSource.h"
#include "nscore.h"  // for NS_FREE_PERMANENT_DATA
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/TouchEvent.h"
#include "gfxVR.h"
#include "VRManager.h"
#include "VRManagerChild.h"
#include "mozilla/gfx/GPUParent.h"
#include "prsystem.h"

#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/SourceSurfaceCairo.h"

using namespace mozilla;
using namespace mozilla::layers;
using namespace mozilla::gl;
using namespace mozilla::gfx;

static bool gEverInitialized = false;
gfxPlatform* gfxPlatform::gPlatform = nullptr;

Atomic<bool, ReleaseAcquire> gfxPlatform::gCMSInitialized;
CMSMode gfxPlatform::gCMSMode = CMSMode::Off;

const ContentDeviceData* gContentDeviceInitData = nullptr;

/// This override of the LogForwarder, initially used for the critical graphics
/// errors, is sending the log to the crash annotations as well, but only
/// if the capacity set with the method below is >= 2.  We always retain the
/// very first critical message, and the latest capacity-1 messages are
/// rotated through. Note that we don't expect the total number of times
/// this gets called to be large - it is meant for critical errors only.

class CrashStatsLogForwarder : public mozilla::gfx::LogForwarder {
 public:
  explicit CrashStatsLogForwarder(CrashReporter::Annotation aKey);
  void Log(const std::string& aString) override;
  void CrashAction(LogReason aReason) override;
  bool UpdateStringsVector(const std::string& aString) override;

  LoggingRecord LoggingRecordCopy() override;

  void SetCircularBufferSize(uint32_t aCapacity);

 private:
  // Helper for the Log()
  void UpdateCrashReport();

 private:
  LoggingRecord mBuffer;
  CrashReporter::Annotation mCrashCriticalKey;
  uint32_t mMaxCapacity;
  int32_t mIndex;
  Mutex mMutex MOZ_UNANNOTATED;
};

CrashStatsLogForwarder::CrashStatsLogForwarder(CrashReporter::Annotation aKey)
    : mCrashCriticalKey(aKey),
      mMaxCapacity(0),
      mIndex(-1),
      mMutex("CrashStatsLogForwarder") {}

void CrashStatsLogForwarder::SetCircularBufferSize(uint32_t aCapacity) {
  MutexAutoLock lock(mMutex);

  mMaxCapacity = aCapacity;
  mBuffer.reserve(static_cast<size_t>(aCapacity));
}

LoggingRecord CrashStatsLogForwarder::LoggingRecordCopy() {
  MutexAutoLock lock(mMutex);
  return mBuffer;
}

bool CrashStatsLogForwarder::UpdateStringsVector(const std::string& aString) {
  // We want at least the first one and the last one.  Otherwise, no point.
  if (mMaxCapacity < 2) {
    return false;
  }

  mIndex += 1;
  MOZ_ASSERT(mIndex >= 0);

  // index will count 0, 1, 2, ..., max-1, 1, 2, ..., max-1, 1, 2, ...
  int32_t index = mIndex ? (mIndex - 1) % (mMaxCapacity - 1) + 1 : 0;
  MOZ_ASSERT(index >= 0 && index < (int32_t)mMaxCapacity);
  MOZ_ASSERT(index <= mIndex && index <= (int32_t)mBuffer.size());

  double tStamp = (TimeStamp::NowLoRes() - TimeStamp::ProcessCreation())
                      .ToSecondsSigDigits();

  // Checking for index >= mBuffer.size(), rather than index == mBuffer.size()
  // just out of paranoia, but we know index <= mBuffer.size().
  LoggingRecordEntry newEntry(mIndex, aString, tStamp);
  if (index >= static_cast<int32_t>(mBuffer.size())) {
    mBuffer.push_back(newEntry);
  } else {
    mBuffer[index] = newEntry;
  }
  return true;
}

void CrashStatsLogForwarder::UpdateCrashReport() {
  std::stringstream message;
  std::string logAnnotation;

  switch (XRE_GetProcessType()) {
    case GeckoProcessType_Default:
      logAnnotation = "|[";
      break;
    case GeckoProcessType_Content:
      logAnnotation = "|[C";
      break;
    case GeckoProcessType_GPU:
      logAnnotation = "|[G";
      break;
    default:
      logAnnotation = "|[X";
      break;
  }

  for (auto& it : mBuffer) {
    message << logAnnotation << std::get<0>(it) << "]" << std::get<1>(it)
            << " (t=" << std::get<2>(it) << ") ";
  }

  nsresult annotated = CrashReporter::RecordAnnotationCString(
      mCrashCriticalKey, message.str().c_str());

  if (annotated != NS_OK) {
    printf("Crash Annotation %s: %s",
           CrashReporter::AnnotationToString(mCrashCriticalKey),
           message.str().c_str());
  }
}

class LogForwarderEvent : public Runnable {
  virtual ~LogForwarderEvent() = default;

 public:
  NS_INLINE_DECL_REFCOUNTING_INHERITED(LogForwarderEvent, Runnable)

  explicit LogForwarderEvent(const nsCString& aMessage)
      : mozilla::Runnable("LogForwarderEvent"), mMessage(aMessage) {}

  NS_IMETHOD Run() override {
    MOZ_ASSERT(NS_IsMainThread() &&
               (XRE_IsContentProcess() || XRE_IsGPUProcess()));

    if (XRE_IsContentProcess()) {
      dom::ContentChild* cc = dom::ContentChild::GetSingleton();
      Unused << cc->SendGraphicsError(mMessage);
    } else if (XRE_IsGPUProcess()) {
      GPUParent* gp = GPUParent::GetSingleton();
      Unused << gp->SendGraphicsError(mMessage);
    }

    return NS_OK;
  }

 protected:
  nsCString mMessage;
};

void CrashStatsLogForwarder::Log(const std::string& aString) {
  MutexAutoLock lock(mMutex);
  PROFILER_MARKER_TEXT("gfx::CriticalError", GRAPHICS, {},
                       nsDependentCString(aString.c_str()));

  if (UpdateStringsVector(aString)) {
    UpdateCrashReport();
  }

  // Add it to the parent strings
  if (!XRE_IsParentProcess()) {
    nsCString stringToSend(aString.c_str());
    if (NS_IsMainThread()) {
      if (XRE_IsContentProcess()) {
        dom::ContentChild* cc = dom::ContentChild::GetSingleton();
        Unused << cc->SendGraphicsError(stringToSend);
      } else if (XRE_IsGPUProcess()) {
        GPUParent* gp = GPUParent::GetSingleton();
        Unused << gp->SendGraphicsError(stringToSend);
      }
    } else {
      nsCOMPtr<nsIRunnable> r1 = new LogForwarderEvent(stringToSend);
      NS_DispatchToMainThread(r1);
    }
  }
}

class CrashTelemetryEvent : public Runnable {
  virtual ~CrashTelemetryEvent() = default;

 public:
  NS_INLINE_DECL_REFCOUNTING_INHERITED(CrashTelemetryEvent, Runnable)

  explicit CrashTelemetryEvent(uint32_t aReason)
      : mozilla::Runnable("CrashTelemetryEvent"), mReason(aReason) {}

  NS_IMETHOD Run() override {
    MOZ_ASSERT(NS_IsMainThread());
    Telemetry::Accumulate(Telemetry::GFX_CRASH, mReason);
    return NS_OK;
  }

 protected:
  uint32_t mReason;
};

void CrashStatsLogForwarder::CrashAction(LogReason aReason) {
#ifndef RELEASE_OR_BETA
  // Non-release builds crash by default, but will use telemetry
  // if this environment variable is present.
  static bool useTelemetry = gfxEnv::MOZ_GFX_CRASH_TELEMETRY();
#else
  // Release builds use telemetry by default, but will crash instead
  // if this environment variable is present.
  static bool useTelemetry = !gfxEnv::MOZ_GFX_CRASH_MOZ_CRASH();
#endif

  if (useTelemetry) {
    // The callers need to assure that aReason is in the range
    // that the telemetry call below supports.
    if (NS_IsMainThread()) {
      Telemetry::Accumulate(Telemetry::GFX_CRASH, (uint32_t)aReason);
    } else {
      nsCOMPtr<nsIRunnable> r1 = new CrashTelemetryEvent((uint32_t)aReason);
      NS_DispatchToMainThread(r1);
    }
  } else {
    // ignoring aReason, we can get the information we need from the stack
    MOZ_CRASH("GFX_CRASH");
  }
}

#define GFX_DOWNLOADABLE_FONTS_ENABLED "gfx.downloadable_fonts.enabled"

#define GFX_PREF_FALLBACK_USE_CMAPS \
  "gfx.font_rendering.fallback.always_use_cmaps"

#define GFX_PREF_OPENTYPE_SVG "gfx.font_rendering.opentype_svg.enabled"

#define GFX_PREF_WORD_CACHE_CHARLIMIT "gfx.font_rendering.wordcache.charlimit"
#define GFX_PREF_WORD_CACHE_MAXENTRIES "gfx.font_rendering.wordcache.maxentries"

#define GFX_PREF_GRAPHITE_SHAPING "gfx.font_rendering.graphite.enabled"
#if defined(XP_DARWIN)
#  define GFX_PREF_CORETEXT_SHAPING "gfx.font_rendering.coretext.enabled"
#endif

#define FONT_VARIATIONS_PREF "layout.css.font-variations.enabled"

static const char* kObservedPrefs[] = {"gfx.downloadable_fonts.",
                                       "gfx.font_rendering.", nullptr};

static void FontPrefChanged(const char* aPref, void* aData) {
  MOZ_ASSERT(aPref);
  NS_ASSERTION(gfxPlatform::GetPlatform(), "the singleton instance has gone");
  gfxPlatform::GetPlatform()->FontsPrefsChanged(aPref);
}

void gfxPlatform::OnMemoryPressure(layers::MemoryPressureReason aWhy) {
  Factory::PurgeAllCaches();
  gfxGradientCache::PurgeAllCaches();
  gfxFontMissingGlyphs::Purge();
  PurgeSkiaFontCache();
  if (XRE_IsParentProcess()) {
    layers::CompositorManagerChild* manager =
        CompositorManagerChild::GetInstance();
    if (manager) {
      manager->SendNotifyMemoryPressure();
    }
  }
}

gfxPlatform::gfxPlatform()
    : mAzureCanvasBackendCollector(this, &gfxPlatform::GetAzureBackendInfo),
      mApzSupportCollector(this, &gfxPlatform::GetApzSupportInfo),
      mFrameStatsCollector(this, &gfxPlatform::GetFrameStats),
      mCMSInfoCollector(this, &gfxPlatform::GetCMSSupportInfo),
      mDisplayInfoCollector(this, &gfxPlatform::GetDisplayInfo),
      mOverlayInfoCollector(this, &gfxPlatform::GetOverlayInfo),
      mSwapChainInfoCollector(this, &gfxPlatform::GetSwapChainInfo),
      mCompositorBackend(layers::LayersBackend::LAYERS_NONE) {
  mAllowDownloadableFonts = UNINITIALIZED_VALUE;

  InitBackendPrefs(GetBackendPrefs());
  VRManager::ManagerInit();
}

bool gfxPlatform::Initialized() { return !!gPlatform; }

/* static */
void gfxPlatform::InitChild(const ContentDeviceData& aData) {
  MOZ_ASSERT(XRE_IsContentProcess());
  MOZ_ASSERT(!gPlatform,
             "InitChild() should be called before first GetPlatform()");
  // Make the provided initial ContentDeviceData available to the init
  // routines.
  gContentDeviceInitData = &aData;
  Init();
  gContentDeviceInitData = nullptr;
}

#define WR_DEBUG_PREF "gfx.webrender.debug"

static void SwapIntervalPrefChangeCallback(const char* aPrefName, void*) {
  bool egl = Preferences::GetBool("gfx.swap-interval.egl"false);
  bool glx = Preferences::GetBool("gfx.swap-interval.glx"false);
  gfxVars::SetSwapIntervalEGL(egl);
  gfxVars::SetSwapIntervalGLX(glx);
}

static void WebRendeProfilerUIPrefChangeCallback(const char* aPrefName, void*) {
  nsCString uiString;
  if (NS_SUCCEEDED(Preferences::GetCString("gfx.webrender.debug.profiler-ui",
                                           uiString))) {
    gfxVars::SetWebRenderProfilerUI(uiString);
  }
}

// List of boolean dynamic parameter for WebRender.
//
// The parameters in this list are:
//  - The pref name.
//  - The BoolParameter enum variant (see webrender_api/src/lib.rs)
//  - A default value.
#define WR_BOOL_PARAMETER_LIST(_)                                     \
  _("gfx.webrender.batched-texture-uploads",                          \
    wr::BoolParameter::BatchedUploads, true)                          \
  _("gfx.webrender.draw-calls-for-texture-copy",                      \
    wr::BoolParameter::DrawCallsForTextureCopy, true)                 \
  _("gfx.webrender.pbo-uploads", wr::BoolParameter::PboUploads, true) \
  _("gfx.webrender.multithreading", wr::BoolParameter::Multithreading, true)

static void WebRenderBoolParameterChangeCallback(const char*, void*) {
  uint32_t bits = 0;

#define WR_BOOL_PARAMETER(name, key, default_val) \
  if (Preferences::GetBool(name, default_val)) {  \
    bits |= 1 << (uint32_t)key;                   \
  }

  WR_BOOL_PARAMETER_LIST(WR_BOOL_PARAMETER)
#undef WR_BOOL_PARAMETER

  gfx::gfxVars::SetWebRenderBoolParameters(bits);
}

static void RegisterWebRenderBoolParamCallback() {
#define WR_BOOL_PARAMETER(name, _key, _default_val) \
  Preferences::RegisterCallback(WebRenderBoolParameterChangeCallback, name);

  WR_BOOL_PARAMETER_LIST(WR_BOOL_PARAMETER)
#undef WR_BOOL_PARAMETER

  WebRenderBoolParameterChangeCallback(nullptr, nullptr);
}

static void WebRenderDebugPrefChangeCallback(const char* aPrefName, void*) {
  wr::DebugFlags flags{0};
#define GFX_WEBRENDER_DEBUG(suffix, bit)                   \
  if (Preferences::GetBool(WR_DEBUG_PREF suffix, false)) { \
    flags |= (bit);                                        \
  }

  GFX_WEBRENDER_DEBUG(".profiler", wr::DebugFlags::PROFILER_DBG)
  GFX_WEBRENDER_DEBUG(".render-targets", wr::DebugFlags::RENDER_TARGET_DBG)
  GFX_WEBRENDER_DEBUG(".texture-cache", wr::DebugFlags::TEXTURE_CACHE_DBG)
  GFX_WEBRENDER_DEBUG(".gpu-time-queries", wr::DebugFlags::GPU_TIME_QUERIES)
  GFX_WEBRENDER_DEBUG(".gpu-sample-queries", wr::DebugFlags::GPU_SAMPLE_QUERIES)
  GFX_WEBRENDER_DEBUG(".disable-batching", wr::DebugFlags::DISABLE_BATCHING)
  GFX_WEBRENDER_DEBUG(".epochs", wr::DebugFlags::EPOCHS)
  GFX_WEBRENDER_DEBUG(".smart-profiler", wr::DebugFlags::SMART_PROFILER)
  GFX_WEBRENDER_DEBUG(".echo-driver-messages",
                      wr::DebugFlags::ECHO_DRIVER_MESSAGES)
  GFX_WEBRENDER_DEBUG(".show-overdraw", wr::DebugFlags::SHOW_OVERDRAW)
  GFX_WEBRENDER_DEBUG(".gpu-cache", wr::DebugFlags::GPU_CACHE_DBG)
  GFX_WEBRENDER_DEBUG(".texture-cache.clear-evicted",
                      wr::DebugFlags::TEXTURE_CACHE_DBG_CLEAR_EVICTED)
  GFX_WEBRENDER_DEBUG(".picture-caching", wr::DebugFlags::PICTURE_CACHING_DBG)
  GFX_WEBRENDER_DEBUG(".picture-borders", wr::DebugFlags::PICTURE_BORDERS)
  GFX_WEBRENDER_DEBUG(".force-picture-invalidation",
                      wr::DebugFlags::FORCE_PICTURE_INVALIDATION)
  GFX_WEBRENDER_DEBUG(".primitives", wr::DebugFlags::PRIMITIVE_DBG)
  // Bit 18 is for the zoom display, which requires the mouse position and thus
  // currently only works in wrench.
  GFX_WEBRENDER_DEBUG(".small-screen", wr::DebugFlags::SMALL_SCREEN)
  GFX_WEBRENDER_DEBUG(".disable-opaque-pass",
                      wr::DebugFlags::DISABLE_OPAQUE_PASS)
  GFX_WEBRENDER_DEBUG(".disable-alpha-pass", wr::DebugFlags::DISABLE_ALPHA_PASS)
  GFX_WEBRENDER_DEBUG(".disable-clip-masks", wr::DebugFlags::DISABLE_CLIP_MASKS)
  GFX_WEBRENDER_DEBUG(".disable-text-prims", wr::DebugFlags::DISABLE_TEXT_PRIMS)
  GFX_WEBRENDER_DEBUG(".disable-gradient-prims",
                      wr::DebugFlags::DISABLE_GRADIENT_PRIMS)
  GFX_WEBRENDER_DEBUG(".obscure-images", wr::DebugFlags::OBSCURE_IMAGES)
  GFX_WEBRENDER_DEBUG(".glyph-flashing", wr::DebugFlags::GLYPH_FLASHING)
  GFX_WEBRENDER_DEBUG(".capture-profiler", wr::DebugFlags::PROFILER_CAPTURE)
  GFX_WEBRENDER_DEBUG(".window-visibility",
                      wr::DebugFlags::WINDOW_VISIBILITY_DBG)
  GFX_WEBRENDER_DEBUG(".restrict-blob-size", wr::DebugFlags::RESTRICT_BLOB_SIZE)
  GFX_WEBRENDER_DEBUG(".surface-promotion-logging",
                      wr::DebugFlags::SURFACE_PROMOTION_LOGGING)
#undef GFX_WEBRENDER_DEBUG
  gfx::gfxVars::SetWebRenderDebugFlags(flags._0);

  uint32_t threshold = Preferences::GetFloat(
      StaticPrefs::GetPrefName_gfx_webrender_debug_slow_cpu_frame_threshold(),
      10.0);
  gfx::gfxVars::SetWebRenderSlowCpuFrameThreshold(threshold);
}

static void WebRenderQualityPrefChangeCallback(const char* aPref, void*) {
  gfxPlatform::GetPlatform()->UpdateForceSubpixelAAWherePossible();
}

static void WebRenderBatchingPrefChangeCallback(const char* aPrefName, void*) {
  uint32_t count = Preferences::GetUint(
      StaticPrefs::GetPrefName_gfx_webrender_batching_lookback(), 10);

  gfx::gfxVars::SetWebRenderBatchingLookback(count);
}

static void WebRenderBlobTileSizePrefChangeCallback(const char* aPrefName,
                                                    void*) {
  uint32_t tileSize = Preferences::GetUint(
      StaticPrefs::GetPrefName_gfx_webrender_blob_tile_size(), 256);
  gfx::gfxVars::SetWebRenderBlobTileSize(tileSize);
}

static void WebRenderUploadThresholdPrefChangeCallback(const char* aPrefName,
                                                       void*) {
  int value = Preferences::GetInt(
      StaticPrefs::GetPrefName_gfx_webrender_batched_upload_threshold(),
      512 * 512);

  gfxVars::SetWebRenderBatchedUploadThreshold(value);
}

static uint32_t GetSkiaGlyphCacheSize() {
  // Only increase font cache size on non-android to save memory.
#if !defined(MOZ_WIDGET_ANDROID)
  // 10mb as the default pref cache size on desktop due to talos perf tweaking.
  // Chromium uses 20mb and skia default uses 2mb.
  // We don't need to change the font cache count since we usually
  // cache thrash due to asian character sets in talos.
  // Only increase memory on the content process
  uint32_t cacheSize =
      StaticPrefs::gfx_content_skia_font_cache_size_AtStartup() * 1024 * 1024;
  if (mozilla::BrowserTabsRemoteAutostart()) {
    return XRE_IsContentProcess() ? cacheSize : kDefaultGlyphCacheSize;
  }

  return cacheSize;
#else
  return kDefaultGlyphCacheSize;
#endif  // MOZ_WIDGET_ANDROID
}

class WebRenderMemoryReporter final : public nsIMemoryReporter {
 public:
  NS_DECL_ISUPPORTS
  NS_DECL_NSIMEMORYREPORTER

 private:
  ~WebRenderMemoryReporter() = default;
};

// Memory reporter for WebRender.
//
// The reporting within WebRender is manual and incomplete. We could do a much
// more thorough job by depending on the malloc_size_of crate, but integrating
// that into WebRender is tricky [1].
//
// So the idea is to start with manual reporting for the large allocations
// detected by DMD, and see how much that can cover in practice (which may
// require a few rounds of iteration). If that approach turns out to be
// fundamentally insufficient, we can either duplicate more of the
// malloc_size_of functionality in WebRender, or deal with the complexity of a
// gecko-only crate dependency.
//
// [1] See https://bugzilla.mozilla.org/show_bug.cgi?id=1480293#c1
struct WebRenderMemoryReporterHelper {
  WebRenderMemoryReporterHelper(nsIHandleReportCallback* aCallback,
                                nsISupports* aData)
      : mCallback(aCallback), mData(aData) {}
  nsCOMPtr<nsIHandleReportCallback> mCallback;
  nsCOMPtr<nsISupports> mData;

  void Report(size_t aBytes, const char* aName) const {
    nsPrintfCString path("explicit/gfx/webrender/%s", aName);
    nsCString desc("CPU heap memory used by WebRender"_ns);
    ReportInternal(aBytes, path, desc, nsIMemoryReporter::KIND_HEAP);
  }

  void ReportTexture(size_t aBytes, const char* aName) const {
    nsPrintfCString path("gfx/webrender/textures/%s", aName);
    nsCString desc("GPU texture memory used by WebRender"_ns);
    ReportInternal(aBytes, path, desc, nsIMemoryReporter::KIND_OTHER);
  }

  void ReportTotalGPUBytes(size_t aBytes) const {
    nsCString path("gfx/webrender/total-gpu-bytes"_ns);
    nsCString desc(nsLiteralCString(
        "Total GPU bytes used by WebRender (should match textures/ sum)"));
    ReportInternal(aBytes, path, desc, nsIMemoryReporter::KIND_OTHER);
  }

  void ReportInternal(size_t aBytes, nsACString& aPath, nsACString& aDesc,
                      int32_t aKind) const {
    // Generally, memory reporters pass the empty string as the process name to
    // indicate "current process". However, if we're using a GPU process, the
    // measurements will actually take place in that process, and it's easier to
    // just note that here rather than trying to invoke the memory reporter in
    // the GPU process.
    nsAutoCString processName;
    if (gfxConfig::IsEnabled(Feature::GPU_PROCESS)) {
      GPUParent::GetGPUProcessName(processName);
    }

    mCallback->Callback(processName, aPath, aKind,
                        nsIMemoryReporter::UNITS_BYTES, aBytes, aDesc, mData);
  }
};

static void FinishAsyncMemoryReport() {
  nsCOMPtr<nsIMemoryReporterManager> imgr =
      do_GetService("@mozilla.org/memory-reporter-manager;1");
  if (imgr) {
    imgr->EndReport();
  }
}

// clang-format off
// (For some reason, clang-format gets the second macro right, but totally mangles the first).
#define REPORT_INTERNER(id)                      \
  helper.Report(aReport.interning.interners.id, \
                "interning/" #id "/interners");
// clang-format on

#define REPORT_DATA_STORE(id)                     \
  helper.Report(aReport.interning.data_stores.id, \
                "interning/" #id "/data-stores");

NS_IMPL_ISUPPORTS(WebRenderMemoryReporter, nsIMemoryReporter)

NS_IMETHODIMP
WebRenderMemoryReporter::CollectReports(nsIHandleReportCallback* aHandleReport,
                                        nsISupports* aData, bool aAnonymize) {
  MOZ_ASSERT(XRE_IsParentProcess());
  MOZ_ASSERT(NS_IsMainThread());
  layers::CompositorManagerChild* manager =
      CompositorManagerChild::GetInstance();
  if (!manager) {
    FinishAsyncMemoryReport();
    return NS_OK;
  }

  WebRenderMemoryReporterHelper helper(aHandleReport, aData);
  manager->SendReportMemory(
      [=](wr::MemoryReport aReport) {
        // CPU Memory.
        helper.Report(aReport.clip_stores, "clip-stores");
        helper.Report(aReport.gpu_cache_metadata, "gpu-cache/metadata");
        helper.Report(aReport.gpu_cache_cpu_mirror, "gpu-cache/cpu-mirror");
        helper.Report(aReport.hit_testers, "hit-testers");
        helper.Report(aReport.fonts, "resource-cache/fonts");
        helper.Report(aReport.weak_fonts, "resource-cache/weak-fonts");
        helper.Report(aReport.images, "resource-cache/images");
        helper.Report(aReport.rasterized_blobs,
                      "resource-cache/rasterized-blobs");
        helper.Report(aReport.texture_cache_structures,
                      "texture-cache/structures");
        helper.Report(aReport.shader_cache, "shader-cache");
        helper.Report(aReport.display_list, "display-list");
        helper.Report(aReport.swgl, "swgl");
        helper.Report(aReport.upload_staging_memory, "upload-stagin-memory");
        helper.Report(aReport.frame_allocator, "frame-allocator");
        helper.Report(aReport.render_tasks, "frame-allocator/render-tasks");

        WEBRENDER_FOR_EACH_INTERNER(REPORT_INTERNER, );
        WEBRENDER_FOR_EACH_INTERNER(REPORT_DATA_STORE, );

        // GPU Memory.
        helper.ReportTexture(aReport.gpu_cache_textures, "gpu-cache");
        helper.ReportTexture(aReport.vertex_data_textures, "vertex-data");
        helper.ReportTexture(aReport.render_target_textures, "render-targets");
        helper.ReportTexture(aReport.depth_target_textures, "depth-targets");
        helper.ReportTexture(aReport.picture_tile_textures, "picture-tiles");
        helper.ReportTexture(aReport.atlas_textures, "texture-cache/atlas");
        helper.ReportTexture(aReport.standalone_textures,
                             "texture-cache/standalone");
        helper.ReportTexture(aReport.texture_upload_pbos,
                             "texture-upload-pbos");
        helper.ReportTexture(aReport.swap_chain, "swap-chains");
        helper.ReportTexture(aReport.render_texture_hosts,
                             "render-texture-hosts");
        helper.ReportTexture(aReport.upload_staging_textures,
                             "upload-staging-textures");

        FinishAsyncMemoryReport();
      },
      [](mozilla::ipc::ResponseRejectReason&& aReason) {
        FinishAsyncMemoryReport();
      });

  return NS_OK;
}

#undef REPORT_INTERNER
#undef REPORT_DATA_STORE

std::atomic<int8_t> gfxPlatform::sHasVariationFontSupport = -1;

bool gfxPlatform::HasVariationFontSupport() {
  // We record the status here: 0 for not supported, 1 for supported.
  if (sHasVariationFontSupport < 0) {
    // It doesn't actually matter if we race with another thread setting this,
    // as any thread will set it to the same value.
#if defined(XP_WIN)
    sHasVariationFontSupport = gfxWindowsPlatform::CheckVariationFontSupport();
#elif defined(XP_DARWIN)
    sHasVariationFontSupport = gfxPlatformMac::CheckVariationFontSupport();
#elif defined(MOZ_WIDGET_GTK)
    sHasVariationFontSupport = gfxPlatformGtk::CheckVariationFontSupport();
#elif defined(ANDROID)
    sHasVariationFontSupport = gfxAndroidPlatform::CheckVariationFontSupport();
#else
#  error "No gfxPlatform implementation available"
#endif
  }
  return sHasVariationFontSupport > 0;
}

void gfxPlatform::Init() {
  AUTO_PROFILER_MARKER_TEXT("gfxPlatform", GRAPHICS, {},
                            "gfxPlatform::Init"_ns);
  MOZ_RELEASE_ASSERT(!XRE_IsGPUProcess(), "GFX: Not allowed in GPU process.");
  MOZ_RELEASE_ASSERT(!XRE_IsRDDProcess(), "GFX: Not allowed in RDD process.");
  MOZ_RELEASE_ASSERT(NS_IsMainThread(), "GFX: Not in main thread.");
  MOZ_RELEASE_ASSERT(!gEverInitialized);
  if (XRE_IsContentProcess()) {
    MOZ_RELEASE_ASSERT(gContentDeviceInitData,
                       "Content Process should cal InitChild() before "
                       "first GetPlatform()");
  }
  gEverInitialized = true;

  gfxVars::Initialize();

  gfxConfig::Init();

  if (XRE_IsParentProcess()) {
    GPUProcessManager::Initialize();
    RDDProcessManager::Initialize();

    nsCOMPtr<nsIFile> file;
    nsresult rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(file));
    if (NS_FAILED(rv)) {
      gfxVars::SetGREDirectory(nsString());
    } else {
      nsAutoString path;
      file->GetPath(path);
      gfxVars::SetGREDirectory(nsString(path));
    }
  }

  if (XRE_IsParentProcess()) {
    nsCOMPtr<nsIFile> profDir;
    nsresult rv = NS_GetSpecialDirectory(NS_APP_PROFILE_DIR_STARTUP,
                                         getter_AddRefs(profDir));
    if (NS_FAILED(rv)) {
      gfxVars::SetProfDirectory(nsString());
    } else {
      nsAutoString path;
      profDir->GetPath(path);
      gfxVars::SetProfDirectory(nsString(path));
    }

    nsAutoCString path;
    Preferences::GetCString("layers.windowrecording.path", path);
    gfxVars::SetLayersWindowRecordingPath(path);

    if (gFxREmbedded) {
      gfxVars::SetFxREmbedded(true);
    }
  }

  // Drop a note in the crash report if we end up forcing an option that could
  // destabilize things.  New items should be appended at the end (of an
  // existing or in a new section), so that we don't have to know the version to
  // interpret these cryptic strings.
  {
    nsAutoCString forcedPrefs;
    // D2D prefs
    forcedPrefs.AppendPrintf(
        "FP(D%d%d", StaticPrefs::gfx_direct2d_disabled_AtStartup(),
        StaticPrefs::gfx_direct2d_force_enabled_AtStartup());
    // Layers prefs
    forcedPrefs.AppendPrintf(
        "-L%d%d%d%d",
        StaticPrefs::layers_amd_switchable_gfx_enabled_AtStartup(),
        StaticPrefs::layers_acceleration_disabled_AtStartup_DoNotUseDirectly(),
        StaticPrefs::
            layers_acceleration_force_enabled_AtStartup_DoNotUseDirectly(),
        StaticPrefs::layers_d3d11_force_warp_AtStartup());
    // WebGL prefs
    forcedPrefs.AppendPrintf(
        "-W%d%d%d%d%d%d%d", StaticPrefs::webgl_angle_force_d3d11(),
        StaticPrefs::webgl_angle_force_warp(), StaticPrefs::webgl_disabled(),
        StaticPrefs::webgl_disable_angle(), StaticPrefs::webgl_dxgl_enabled(),
        StaticPrefs::webgl_force_enabled(), StaticPrefs::webgl_msaa_force());
    // Prefs that don't fit into any of the other sections
    forcedPrefs.AppendPrintf("-T%d%d) ",
                             StaticPrefs::gfx_android_rgb16_force_AtStartup(),
                             StaticPrefs::gfx_canvas_accelerated());
    ScopedGfxFeatureReporter::AppNote(forcedPrefs);
  }

  InitMoz2DLogging();

  /* Initialize the GfxInfo service.
   * Note: we can't call functions on GfxInfo that depend
   * on gPlatform until after it has been initialized
   * below. GfxInfo initialization annotates our
   * crash reports so we want to do it before
   * we try to load any drivers and do device detection
   * incase that code crashes. See bug #591561. */

  nsCOMPtr<nsIGfxInfo> gfxInfo;
  /* this currently will only succeed on Windows */
  gfxInfo = components::GfxInfo::Service();

  if (XRE_IsParentProcess()) {
    // Some gfxVars must be initialized prior gPlatform for coherent results.
    gfxVars::SetDXInterop2Blocked(IsDXInterop2Blocked());
    gfxVars::SetDXNV12Blocked(IsDXNV12Blocked());
    gfxVars::SetDXP010Blocked(IsDXP010Blocked());
    gfxVars::SetDXP016Blocked(IsDXP016Blocked());
  }

#if defined(XP_WIN)
  gPlatform = new gfxWindowsPlatform;
#elif defined(XP_DARWIN)
  gPlatform = new gfxPlatformMac;
#elif defined(MOZ_WIDGET_GTK)
  gPlatform = new gfxPlatformGtk;
#elif defined(ANDROID)
  gPlatform = new gfxAndroidPlatform;
#else
#  error "No gfxPlatform implementation available"
#endif
  gPlatform->PopulateScreenInfo();
  gPlatform->InitAcceleration();
  gPlatform->InitWebRenderConfig();

  gPlatform->InitHardwareVideoConfig();
  gPlatform->InitWebGLConfig();
  gPlatform->InitWebGPUConfig();
  gPlatform->InitWindowOcclusionConfig();
  gPlatform->InitBackdropFilterConfig();
  gPlatform->InitAcceleratedCanvas2DConfig();

#if defined(XP_WIN)
  // When using WebRender, we defer initialization of the D3D11 devices until
  // the (rare) cases where they're used. Note that the GPU process where
  // WebRender runs doesn't initialize gfxPlatform and performs explicit
  // initialization of the bits it needs.
  if (XRE_IsParentProcess() && !gfxConfig::IsEnabled(Feature::GPU_PROCESS) &&
      StaticPrefs::
          gfx_webrender_enabled_no_gpu_process_with_angle_win_AtStartup()) {
    gPlatform->EnsureDevicesInitialized();
  }
#endif

  if (XRE_IsParentProcess()) {
    mozilla::glean::gpu_process::feature_status.Set(
        gfxConfig::GetFeature(Feature::GPU_PROCESS)
            .GetStatusAndFailureIdString());
  }

  if (gfxConfig::IsEnabled(Feature::GPU_PROCESS)) {
    GPUProcessManager* gpu = GPUProcessManager::Get();
    Unused << gpu->LaunchGPUProcess();
  }

  if (XRE_IsParentProcess()) {
    // Create the global vsync source and dispatcher.
    RefPtr<VsyncSource> vsyncSource =
        gfxPlatform::ForceSoftwareVsync()
            ? gPlatform->GetSoftwareVsyncSource()
            : gPlatform->GetGlobalHardwareVsyncSource();
    gPlatform->mVsyncDispatcher = new VsyncDispatcher(vsyncSource);

    // Listen for layout.frame_rate pref changes.
    Preferences::RegisterCallback(
        gfxPlatform::ReInitFrameRate,
        nsDependentCString(StaticPrefs::GetPrefName_layout_frame_rate()));
    Preferences::RegisterCallback(
        gfxPlatform::ReInitFrameRate,
        nsDependentCString(
            StaticPrefs::GetPrefName_privacy_resistFingerprinting()));
  }

  // Create the sRGB to output display profile transforms. They can be accessed
  // off the main thread so we want to avoid a race condition.
  gPlatform->InitializeCMS();

  SkGraphics::Init();
#ifdef MOZ_ENABLE_FREETYPE
  SkInitCairoFT(gPlatform->FontHintingEnabled());
#endif
  gfxGradientCache::Init();

  InitLayersIPC();

  // This *create* the platform font list instance, but may not *initialize* it
  // yet if the gfx.font-list.lazy-init.enabled pref is set. The first *use*
  // of the list will ensure it is initialized.
  if (!gPlatform->CreatePlatformFontList()) {
    MOZ_CRASH("Could not initialize gfxPlatformFontList");
  }

  gPlatform->mScreenReferenceDrawTarget =
      gPlatform->CreateOffscreenContentDrawTarget(IntSize(1, 1),
                                                  SurfaceFormat::B8G8R8A8);
  if (!gPlatform->mScreenReferenceDrawTarget ||
      !gPlatform->mScreenReferenceDrawTarget->IsValid()) {
    // If TDR is detected, create a draw target with software backend
    // and it should be replaced later when the process gets the device
    // reset notification.
    if (!gPlatform->DidRenderingDeviceReset()) {
      gfxCriticalError() << "Could not initialize mScreenReferenceDrawTarget";
    }
  }

  if (NS_FAILED(gfxFontCache::Init())) {
    MOZ_CRASH("Could not initialize gfxFontCache");
  }

  Preferences::RegisterPrefixCallbacks(FontPrefChanged, kObservedPrefs);

  GLContext::PlatformStartup();

  // Listen to memory pressure event so we can purge DrawTarget caches
  gPlatform->mMemoryPressureObserver =
      layers::MemoryPressureObserver::Create(gPlatform);

  // Request the imgITools service, implicitly initializing ImageLib.
  nsCOMPtr<imgITools> imgTools = do_GetService("@mozilla.org/image/tools;1");
  if (!imgTools) {
    MOZ_CRASH("Could not initialize ImageLib");
  }

  RegisterStrongMemoryReporter(new GfxMemoryImageReporter());
  if (XRE_IsParentProcess()) {
    RegisterStrongAsyncMemoryReporter(new WebRenderMemoryReporter());
  }

  RegisterStrongMemoryReporter(new SkMemoryReporter());

  uint32_t skiaCacheSize = GetSkiaGlyphCacheSize();
  if (skiaCacheSize != kDefaultGlyphCacheSize) {
    SkGraphics::SetFontCacheLimit(skiaCacheSize);
  }

  InitNullMetadata();
  InitOpenGLConfig();

  if (XRE_IsParentProcess()) {
    Preferences::Unlock(FONT_VARIATIONS_PREF);
    if (!gfxPlatform::HasVariationFontSupport()) {
      // Ensure variation fonts are disabled and the pref is locked.
      Preferences::SetBool(FONT_VARIATIONS_PREF, false, PrefValueKind::Default);
      Preferences::SetBool(FONT_VARIATIONS_PREF, false);
      Preferences::Lock(FONT_VARIATIONS_PREF);
    }
  }

  if (XRE_IsParentProcess()) {
    ReportTelemetry();
  }

  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
  if (obs) {
    obs->NotifyObservers(nullptr, "gfx-features-ready", nullptr);
  }
}

void gfxPlatform::ReportTelemetry() {
  MOZ_RELEASE_ASSERT(XRE_IsParentProcess(),
                     "GFX: Only allowed to be called from parent process.");

  nsCOMPtr<nsIGfxInfo> gfxInfo = components::GfxInfo::Service();

  {
    auto& screenManager = widget::ScreenManager::GetSingleton();
    const uint32_t screenCount = screenManager.CurrentScreenList().Length();
    RefPtr<widget::Screen> primaryScreen = screenManager.GetPrimaryScreen();
    const LayoutDeviceIntRect rect = primaryScreen->GetRect();

    mozilla::glean::gfx_display::count.Set(screenCount);
    mozilla::glean::gfx_display::primary_height.Set(rect.Height());
    mozilla::glean::gfx_display::primary_width.Set(rect.Width());

    // Check if any screen known by screenManager supports HDR.
    bool supportsHDR = false;
    for (const auto& screen : screenManager.CurrentScreenList()) {
      supportsHDR |= screen->GetIsHDR();
    }
    glean::gfx::supports_hdr.Set(supportsHDR);
  }

  nsString adapterDesc;
  gfxInfo->GetAdapterDescription(adapterDesc);

// Android description is constructed in a way that makes it possible to exceed
// the metric's length limit.
#if defined(ANDROID)
  if (!adapterDesc.IsEmpty()) {
    adapterDesc.Truncate(99);
  }
#endif

  mozilla::glean::gfx_adapter_primary::description.Set(
      NS_ConvertUTF16toUTF8(adapterDesc));

  nsString adapterVendorId;
  gfxInfo->GetAdapterVendorID(adapterVendorId);
  mozilla::glean::gfx_adapter_primary::vendor_id.Set(
      NS_ConvertUTF16toUTF8(adapterVendorId));

  nsString adapterDeviceId;
  gfxInfo->GetAdapterDeviceID(adapterDeviceId);
  mozilla::glean::gfx_adapter_primary::device_id.Set(
      NS_ConvertUTF16toUTF8(adapterDeviceId));

  nsString adapterSubsystemId;
  gfxInfo->GetAdapterSubsysID(adapterSubsystemId);
  mozilla::glean::gfx_adapter_primary::subsystem_id.Set(
      NS_ConvertUTF16toUTF8(adapterSubsystemId));

  uint32_t adapterRam = 0;
  gfxInfo->GetAdapterRAM(&adapterRam);
  mozilla::glean::gfx_adapter_primary::ram.Set(adapterRam);

  nsString adapterDriver;
  gfxInfo->GetAdapterDriver(adapterDriver);
  mozilla::glean::gfx_adapter_primary::driver_files.Set(
      NS_ConvertUTF16toUTF8(adapterDriver));

  nsString adapterDriverVendor;
  gfxInfo->GetAdapterDriverVendor(adapterDriverVendor);
  mozilla::glean::gfx_adapter_primary::driver_vendor.Set(
      NS_ConvertUTF16toUTF8(adapterDriverVendor));

  nsString adapterDriverVersion;
  gfxInfo->GetAdapterDriverVersion(adapterDriverVersion);
  mozilla::glean::gfx_adapter_primary::driver_version.Set(
      NS_ConvertUTF16toUTF8(adapterDriverVersion));

  nsString adapterDriverDate;
  gfxInfo->GetAdapterDriverDate(adapterDriverDate);
  mozilla::glean::gfx_adapter_primary::driver_date.Set(
      NS_ConvertUTF16toUTF8(adapterDriverDate));

  mozilla::glean::gfx_status::headless.Set(IsHeadless());
}

static bool IsFeatureSupported(long aFeature, bool aDefault) {
  nsCOMPtr<nsIGfxInfo> gfxInfo = components::GfxInfo::Service();
  nsCString blockId;
  int32_t status;
  if (!NS_SUCCEEDED(gfxInfo->GetFeatureStatus(aFeature, blockId, &status))) {
    return aDefault;
  }
  return status == nsIGfxInfo::FEATURE_STATUS_OK;
}

/* static*/
bool gfxPlatform::IsDXInterop2Blocked() {
  return !IsFeatureSupported(nsIGfxInfo::FEATURE_DX_INTEROP2, false);
}

/* static*/
bool gfxPlatform::IsDXNV12Blocked() {
  return !IsFeatureSupported(nsIGfxInfo::FEATURE_DX_NV12, false);
}

/* static*/
bool gfxPlatform::IsDXP010Blocked() {
  return !IsFeatureSupported(nsIGfxInfo::FEATURE_DX_P010, false);
}

/* static*/
bool gfxPlatform::IsDXP016Blocked() {
  return !IsFeatureSupported(nsIGfxInfo::FEATURE_DX_P016, false);
}

/* static */
int32_t gfxPlatform::MaxTextureSize() {
  // Make sure we don't completely break rendering because of a typo in the
  // pref or whatnot.
  const int32_t kMinSizePref = 2048;
  return std::max(
      kMinSizePref,
      StaticPrefs::gfx_max_texture_size_AtStartup_DoNotUseDirectly());
}

/* static */
int32_t gfxPlatform::MaxAllocSize() {
  // Make sure we don't completely break rendering because of a typo in the
  // pref or whatnot.
  const int32_t kMinAllocPref = 10000000;
  return std::max(kMinAllocPref,
                  StaticPrefs::gfx_max_alloc_size_AtStartup_DoNotUseDirectly());
}

void gfxPlatform::MaybeInitializeCMS() {
  if (XRE_IsGPUProcess()) {
    // Colors in the GPU process should already be managed, so we don't need to
    // perform color management there.
    gCMSInitialized = true;
    return;
  }
  Unused << GetPlatform();
}

/* static */
void gfxPlatform::InitMoz2DLogging() {
  auto fwd = new CrashStatsLogForwarder(
      CrashReporter::Annotation::GraphicsCriticalError);
  fwd->SetCircularBufferSize(StaticPrefs::gfx_logging_crash_length_AtStartup());

  mozilla::gfx::Config cfg;
  cfg.mLogForwarder = fwd;
  cfg.mMaxTextureSize = gfxPlatform::MaxTextureSize();
  cfg.mMaxAllocSize = gfxPlatform::MaxAllocSize();

  gfx::Factory::Init(cfg);
}

/* static */
bool gfxPlatform::IsHeadless() {
  static bool initialized = false;
  static bool headless = false;
  if (!initialized) {
    initialized = true;
    headless = PR_GetEnv("MOZ_HEADLESS");
  }
  return headless;
}

/* static */
bool gfxPlatform::UseRemoteCanvas() {
  return XRE_IsContentProcess() && (gfx::gfxVars::RemoteCanvasEnabled() ||
                                    gfx::gfxVars::UseAcceleratedCanvas2D());
}

/* static */
bool gfxPlatform::IsBackendAccelerated(
    const mozilla::gfx::BackendType aBackendType) {
  return aBackendType == BackendType::DIRECT2D ||
         aBackendType == BackendType::DIRECT2D1_1;
}

/* static */
bool gfxPlatform::CanMigrateMacGPUs() {
  int32_t pMigration = StaticPrefs::gfx_compositor_gpu_migration();

  bool forceDisable = pMigration == 0;
  bool forceEnable = pMigration == 2;

  return forceEnable || !forceDisable;
}

static bool sLayersIPCIsUp = false;

/* static */
void gfxPlatform::InitNullMetadata() {
  ScrollMetadata::sNullMetadata = new ScrollMetadata();
  ClearOnShutdown(&ScrollMetadata::sNullMetadata);
}

void gfxPlatform::Shutdown() {
  // In some cases, gPlatform may not be created but Shutdown() called,
  // e.g., during xpcshell tests.
  if (!gPlatform) {
    return;
  }

  MOZ_ASSERT(!sLayersIPCIsUp);

  // These may be called before the corresponding subsystems have actually
  // started up. That's OK, they can handle it.
  gfxFontCache::Shutdown();
  gfxGradientCache::Shutdown();
  gfxAlphaBoxBlur::ShutdownBlurCache();
  gfxGraphiteShaper::Shutdown();
  gfxPlatformFontList::Shutdown();
  gfxFontMissingGlyphs::Shutdown();

  // Free the various non-null transforms and loaded profiles
  gPlatform->ShutdownCMS();

  Preferences::UnregisterPrefixCallbacks(FontPrefChanged, kObservedPrefs);

  NS_ASSERTION(gPlatform->mMemoryPressureObserver,
               "mMemoryPressureObserver has already gone");
  if (gPlatform->mMemoryPressureObserver) {
    gPlatform->mMemoryPressureObserver->Unregister();
    gPlatform->mMemoryPressureObserver = nullptr;
  }

  if (XRE_IsParentProcess()) {
    if (gPlatform->mGlobalHardwareVsyncSource) {
      gPlatform->mGlobalHardwareVsyncSource->Shutdown();
    }
    if (gPlatform->mSoftwareVsyncSource &&
        gPlatform->mSoftwareVsyncSource !=
            gPlatform->mGlobalHardwareVsyncSource) {
      gPlatform->mSoftwareVsyncSource->Shutdown();
    }
  }

  gPlatform->mGlobalHardwareVsyncSource = nullptr;
  gPlatform->mSoftwareVsyncSource = nullptr;
  gPlatform->mVsyncDispatcher = nullptr;

  // Shut down the default GL context provider.
  GLContextProvider::Shutdown();

#if defined(XP_WIN)
  // The above shutdown calls operate on the available context providers on
  // most platforms.  Windows is a "special snowflake", though, and has three
  // context providers available, so we have to shut all of them down.
  // We should only support the default GL provider on Windows; then, this
  // could go away. Unfortunately, we currently support WGL (the default) for
  // WebGL on Optimus.
  GLContextProviderEGL::Shutdown();
#endif

  if (XRE_IsParentProcess()) {
    GPUProcessManager::Shutdown();
    VRProcessManager::Shutdown();
    RDDProcessManager::Shutdown();
  }

  gfx::Factory::ShutDown();
  gfxVars::Shutdown();
  gfxFont::DestroySingletons();

  gfxConfig::Shutdown();

  gPlatform->WillShutdown();

  delete gPlatform;
  gPlatform = nullptr;
}

/* static */
void gfxPlatform::InitLayersIPC() {
  if (sLayersIPCIsUp) {
    return;
  }
  sLayersIPCIsUp = true;

  if (XRE_IsParentProcess()) {
#if defined(XP_WIN)
    if (gfxConfig::IsEnabled(gfx::Feature::WINDOW_OCCLUSION)) {
      widget::WinWindowOcclusionTracker::Ensure();
    }
#endif
    if (!gfxConfig::IsEnabled(Feature::GPU_PROCESS)) {
      RemoteTextureMap::Init();
      wr::RenderThread::Start(GPUProcessManager::Get()->AllocateNamespace());
      image::ImageMemoryReporter::InitForWebRender();
    }

    layers::CompositorThreadHolder::Start();

    if (!gfxConfig::IsEnabled(Feature::GPU_PROCESS)) {
      gfx::CanvasRenderThread::Start();
    }
  }
}

/* static */
void gfxPlatform::ShutdownLayersIPC() {
  if (!sLayersIPCIsUp) {
    return;
  }
  sLayersIPCIsUp = false;

  if (XRE_IsContentProcess()) {
    gfx::VRManagerChild::ShutDown();
    gfx::CanvasShutdownManager::Shutdown();
    layers::CompositorManagerChild::Shutdown();
    layers::ImageBridgeChild::ShutDown();
  } else if (XRE_IsParentProcess()) {
    VideoBridgeParent::Shutdown();
    RDDProcessManager::RDDProcessShutdown();
    gfx::VRManagerChild::ShutDown();
    gfx::CanvasShutdownManager::Shutdown();
    layers::CompositorManagerChild::Shutdown();
    layers::ImageBridgeChild::ShutDown();
    // This could be running on either the Compositor thread, the Renderer
    // thread, or the dedicated CanvasRender thread, so we need to shutdown
    // before the former two.
    gfx::CanvasRenderThread::Shutdown();
    // This has to happen after shutting down the child protocols.
    layers::CompositorThreadHolder::Shutdown();
    RemoteTextureMap::Shutdown();
    image::ImageMemoryReporter::ShutdownForWebRender();
    // There is a case that RenderThread exists when UseWebRender() is
    // false. This could happen when WebRender was fallbacked to compositor.
    if (wr::RenderThread::Get()) {
      wr::RenderThread::ShutDown();

      Preferences::UnregisterCallback(WebRenderDebugPrefChangeCallback,
                                      WR_DEBUG_PREF);
      Preferences::UnregisterCallback(WebRendeProfilerUIPrefChangeCallback,
                                      "gfx.webrender.debug.profiler-ui");
      Preferences::UnregisterCallback(
          WebRenderBlobTileSizePrefChangeCallback,
          nsDependentCString(
              StaticPrefs::GetPrefName_gfx_webrender_blob_tile_size()));
    }
#if defined(XP_WIN)
    widget::WinWindowOcclusionTracker::ShutDown();
#endif
  } else {
    // TODO: There are other kind of processes and we should make sure gfx
    // stuff is either not created there or shut down properly.
  }
}

void gfxPlatform::WillShutdown() {
  // Destoy these first in case they depend on backend-specific resources.
  // Otherwise, the backend's destructor would be called before the
  // base gfxPlatform destructor.
  mScreenReferenceSurface = nullptr;
  mScreenReferenceDrawTarget = nullptr;

  // Always clear out the Skia font cache here, in case it is referencing any
  // SharedFTFaces that would otherwise outlive destruction of the FT_Library
  // that owns them.
  SkGraphics::PurgeFontCache();

  // The cairo folks think we should only clean up in debug builds,
  // but we're generally in the habit of trying to shut down as
  // cleanly as possible even in production code, so call this
  // cairo_debug_* function unconditionally.
  //
  // because cairo can assert and thus crash on shutdown, don't do this in
  // release builds
#ifdef NS_FREE_PERMANENT_DATA
  cairo_debug_reset_static_data();
#endif
}

gfxPlatform::~gfxPlatform() = default;

/* static */
already_AddRefed<DrawTarget> gfxPlatform::CreateDrawTargetForSurface(
    gfxASurface* aSurface, const IntSize& aSize) {
  SurfaceFormat format = aSurface->GetSurfaceFormat();
  RefPtr<DrawTarget> drawTarget = Factory::CreateDrawTargetForCairoSurface(
      aSurface->CairoSurface(), aSize, &format);
  if (!drawTarget) {
    gfxWarning() << "gfxPlatform::CreateDrawTargetForSurface failed in "
                    "CreateDrawTargetForCairoSurface";
    return nullptr;
  }
  return drawTarget.forget();
}

cairo_user_data_key_t kSourceSurface;

/**
 * Record the backend that was used to construct the SourceSurface.
 * When getting the cached SourceSurface for a gfxASurface/DrawTarget pair,
 * we check to make sure the DrawTarget's backend matches the backend
 * for the cached SourceSurface, and only use it if they match. This
 * can avoid expensive and unnecessary readbacks.
 */

struct SourceSurfaceUserData {
  RefPtr<SourceSurface> mSrcSurface;
  BackendType mBackendType;
};

static void SourceBufferDestroy(void* srcSurfUD) {
  delete static_cast<SourceSurfaceUserData*>(srcSurfUD);
}

UserDataKey kThebesSurface;

struct DependentSourceSurfaceUserData {
  RefPtr<gfxASurface> mSurface;
};

static void SourceSurfaceDestroyed(void* aData) {
  delete static_cast<DependentSourceSurfaceUserData*>(aData);
}

void gfxPlatform::ClearSourceSurfaceForSurface(gfxASurface* aSurface) {
  aSurface->SetData(&kSourceSurface, nullptr, nullptr);
}

/* static */
already_AddRefed<SourceSurface> gfxPlatform::GetSourceSurfaceForSurface(
    RefPtr<DrawTarget> aTarget, gfxASurface* aSurface, bool aIsPlugin) {
  if (!aSurface->CairoSurface() || aSurface->CairoStatus()) {
    return nullptr;
  }

  if (!aTarget) {
    aTarget = gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
  }

  void* userData = aSurface->GetData(&kSourceSurface);

  if (userData) {
    SourceSurfaceUserData* surf = static_cast<SourceSurfaceUserData*>(userData);

    if (surf->mSrcSurface->IsValid() &&
        surf->mBackendType == aTarget->GetBackendType()) {
      RefPtr<SourceSurface> srcSurface(surf->mSrcSurface);
      return srcSurface.forget();
    }
    // We can just continue here as when setting new user data the destroy
    // function will be called for the old user data.
  }

  SurfaceFormat format = aSurface->GetSurfaceFormat();

  if (aTarget->GetBackendType() == BackendType::CAIRO) {
    // If we're going to be used with a CAIRO DrawTarget, then just create a
    // SourceSurfaceCairo since we don't know the underlying type of the CAIRO
    // DrawTarget and can't pick a better surface type. Doing this also avoids
    // readback of aSurface's surface into memory if, for example, aSurface
    // wraps an xlib cairo surface (which can be important to avoid a major
    // slowdown).
    //
    // We return here regardless of whether CreateSourceSurfaceFromNativeSurface
    // succeeds or not since we don't expect to be able to do any better below
    // if it fails.
    //
    // Note that the returned SourceSurfaceCairo holds a strong reference to
    // the cairo_surface_t* that it wraps, which essencially means it holds a
    // strong reference to aSurface since aSurface shares its
    // cairo_surface_t*'s reference count variable. As a result we can't cache
    // srcBuffer on aSurface (see below) since aSurface would then hold a
    // strong reference back to srcBuffer, creating a reference loop and a
    // memory leak. Not caching is fine since wrapping is cheap enough (no
    // copying) so we can just wrap again next time we're called.
    return Factory::CreateSourceSurfaceForCairoSurface(
        aSurface->CairoSurface(), aSurface->GetSize(), format);
  }

  RefPtr<SourceSurface> srcBuffer;

  // Currently no other DrawTarget types implement
  // CreateSourceSurfaceFromNativeSurface

  if (!srcBuffer) {
    // If aSurface wraps data, we can create a SourceSurfaceRawData that wraps
    // the same data, then optimize it for aTarget:
    RefPtr<DataSourceSurface> surf = GetWrappedDataSourceSurface(aSurface);
    if (surf) {
      srcBuffer = aIsPlugin
                      ? aTarget->OptimizeSourceSurfaceForUnknownAlpha(surf)
                      : aTarget->OptimizeSourceSurface(surf);

      if (srcBuffer == surf) {
        // GetWrappedDataSourceSurface returns a SourceSurface that holds a
        // strong reference to aSurface since it wraps aSurface's data and
        // needs it to stay alive. As a result we can't cache srcBuffer on
        // aSurface (below) since aSurface would then hold a strong reference
        // back to srcBuffer, creating a reference loop and a memory leak. Not
        // caching is fine since wrapping is cheap enough (no copying) so we
        // can just wrap again next time we're called.
        //
        // Note that the check below doesn't catch this since srcBuffer will be
        // a SourceSurfaceRawData object (even if aSurface is not a
        // gfxImageSurface object), which is why we need this separate check.
        return srcBuffer.forget();
      }
    }
  }

  if (!srcBuffer) {
    MOZ_ASSERT(aTarget->GetBackendType() != BackendType::CAIRO,
               "We already tried CreateSourceSurfaceFromNativeSurface with a "
               "DrawTargetCairo above");
    // We've run out of performant options. We now try creating a SourceSurface
    // using a temporary DrawTargetCairo and then optimizing it to aTarget's
    // actual type. The CreateSourceSurfaceFromNativeSurface() call will
    // likely create a DataSourceSurface (possibly involving copying and/or
    // readback), and the OptimizeSourceSurface may well copy again and upload
    // to the GPU. So, while this code path is rarely hit, hitting it may be
    // very slow.
    srcBuffer = Factory::CreateSourceSurfaceForCairoSurface(
        aSurface->CairoSurface(), aSurface->GetSize(), format);
    if (srcBuffer) {
      srcBuffer = aTarget->OptimizeSourceSurface(srcBuffer);
    }
  }

  if (!srcBuffer) {
    return nullptr;
  }

  if ((srcBuffer->GetType() == SurfaceType::CAIRO &&
       static_cast<SourceSurfaceCairo*>(srcBuffer.get())->GetSurface() ==
           aSurface->CairoSurface()) ||
      (srcBuffer->GetType() == SurfaceType::CAIRO_IMAGE &&
       static_cast<DataSourceSurfaceCairo*>(srcBuffer.get())->GetSurface() ==
           aSurface->CairoSurface())) {
    // See the "Note that the returned SourceSurfaceCairo..." comment above.
    return srcBuffer.forget();
  }

  // Add user data to aSurface so we can cache lookups in the future.
  auto* srcSurfUD = new SourceSurfaceUserData;
  srcSurfUD->mBackendType = aTarget->GetBackendType();
  srcSurfUD->mSrcSurface = srcBuffer;
  aSurface->SetData(&kSourceSurface, srcSurfUD, SourceBufferDestroy);

  return srcBuffer.forget();
}

already_AddRefed<DataSourceSurface> gfxPlatform::GetWrappedDataSourceSurface(
    gfxASurface* aSurface) {
  RefPtr<gfxImageSurface> image = aSurface->GetAsImageSurface();
  if (!image) {
    return nullptr;
  }
  RefPtr<DataSourceSurface> result = Factory::CreateWrappingDataSourceSurface(
      image->Data(), image->Stride(), image->GetSize(),
      ImageFormatToSurfaceFormat(image->Format()));

  if (!result) {
    return nullptr;
  }

  // If we wrapped the underlying data of aSurface, then we need to add user
  // data to make sure aSurface stays alive until we are done with the data.
  auto* srcSurfUD = new DependentSourceSurfaceUserData;
  srcSurfUD->mSurface = aSurface;
  result->AddUserData(&kThebesSurface, srcSurfUD, SourceSurfaceDestroyed);

  return result.forget();
}

void gfxPlatform::PopulateScreenInfo() {
  // We're only going to set some gfxVars here, which is only possible from
  // the parent process.
  if (!XRE_IsParentProcess()) {
    return;
  }

  nsCOMPtr<nsIScreenManager> manager =
      do_GetService("@mozilla.org/gfx/screenmanager;1");
  MOZ_ASSERT(manager, "failed to get nsIScreenManager");

  nsCOMPtr<nsIScreen> screen;
  manager->GetPrimaryScreen(getter_AddRefs(screen));
  if (!screen) {
    // This can happen in xpcshell, for instance
    return;
  }

  int32_t screenDepth;
  screen->GetColorDepth(&screenDepth);
  gfxVars::SetPrimaryScreenDepth(screenDepth);
}

bool gfxPlatform::SupportsAzureContentForDrawTarget(DrawTarget* aTarget) {
  if (!aTarget || !aTarget->IsValid()) {
    return false;
  }

  return SupportsAzureContentForType(aTarget->GetBackendType());
}

void gfxPlatform::PurgeSkiaFontCache() {
  if (gfxPlatform::GetPlatform()->GetDefaultContentBackend() ==
      BackendType::SKIA) {
    SkGraphics::PurgeFontCache();
  }
}

already_AddRefed<DrawTarget> gfxPlatform::CreateDrawTargetForBackend(
    BackendType aBackend, const IntSize& aSize, SurfaceFormat aFormat) {
  // There is a bunch of knowledge in the gfxPlatform heirarchy about how to
  // create the best offscreen surface for the current system and situation. We
  // can easily take advantage of this for the Cairo backend, so that's what we
  // do.
  // mozilla::gfx::Factory can get away without having all this knowledge for
  // now, but this might need to change in the future (using
  // CreateOffscreenSurface() and CreateDrawTargetForSurface() for all
  // backends).
  if (aBackend == BackendType::CAIRO) {
    RefPtr<gfxASurface> surf =
        CreateOffscreenSurface(aSize, SurfaceFormatToImageFormat(aFormat));
    if (!surf || surf->CairoStatus()) {
      return nullptr;
    }
    return CreateDrawTargetForSurface(surf, aSize);
  }
  return Factory::CreateDrawTarget(aBackend, aSize, aFormat);
}

already_AddRefed<DrawTarget> gfxPlatform::CreateOffscreenCanvasDrawTarget(
    const IntSize& aSize, SurfaceFormat aFormat, bool aRequireSoftwareRender) {
  NS_ASSERTION(mPreferredCanvasBackend != BackendType::NONE, "No backend.");

  BackendType backend = mFallbackCanvasBackend;
  // If we are using remote canvas we don't want to use acceleration in
  // canvas DrawTargets we are not remoting, so we always use the fallback
  // software one.
  if (!gfxPlatform::UseRemoteCanvas() ||
      !gfxPlatform::IsBackendAccelerated(mPreferredCanvasBackend)) {
    backend = mPreferredCanvasBackend;
  }

  if (aRequireSoftwareRender) {
    backend = gfxPlatform::IsBackendAccelerated(mPreferredCanvasBackend)
                  ? mFallbackCanvasBackend
                  : mPreferredCanvasBackend;
  }

#ifdef XP_WIN
  // On Windows, the fallback backend (Cairo) should use its image backend.
  RefPtr<DrawTarget> target =
      Factory::CreateDrawTarget(backend, aSize, aFormat);
#else
  RefPtr<DrawTarget> target =
      CreateDrawTargetForBackend(backend, aSize, aFormat);
#endif

  if (target || mFallbackCanvasBackend == BackendType::NONE) {
    return target.forget();
  }

#ifdef XP_WIN
  // On Windows, the fallback backend (Cairo) should use its image backend.
  return Factory::CreateDrawTarget(mFallbackCanvasBackend, aSize, aFormat);
#else
  return CreateDrawTargetForBackend(mFallbackCanvasBackend, aSize, aFormat);
#endif
}

already_AddRefed<DrawTarget> gfxPlatform::CreateOffscreenContentDrawTarget(
    const IntSize& aSize, SurfaceFormat aFormat, bool aFallback) {
  BackendType backend = (aFallback) ? mSoftwareBackend : mContentBackend;
  NS_ASSERTION(backend != BackendType::NONE, "No backend.");
  RefPtr<DrawTarget> dt = CreateDrawTargetForBackend(backend, aSize, aFormat);

  if (!dt) {
    return nullptr;
  }

  // We'd prefer this to take proper care and return a CaptureDT, but for the
  // moment since we can't and this means we're going to be drawing on the main
  // thread force it's initialization. See bug 1526045 and bug 1521368.
  dt->ClearRect(gfx::Rect());
  if (!dt->IsValid()) {
    return nullptr;
  }
  return dt.forget();
}

already_AddRefed<DrawTarget> gfxPlatform::CreateSimilarSoftwareDrawTarget(
    DrawTarget* aDT, const IntSize& aSize, SurfaceFormat aFormat) {
  RefPtr<DrawTarget> dt;

  if (Factory::DoesBackendSupportDataDrawtarget(aDT->GetBackendType())) {
    dt = aDT->CreateSimilarDrawTarget(aSize, aFormat);
  } else {
    BackendType backendType = BackendType::SKIA;
    dt = Factory::CreateDrawTarget(backendType, aSize, aFormat);
  }

  return dt.forget();
}

/* static */
already_AddRefed<DrawTarget> gfxPlatform::CreateDrawTargetForData(
    unsigned char* aData, const IntSize& aSize, int32_t aStride,
    SurfaceFormat aFormat, bool aUninitialized) {
  BackendType backendType = gfxVars::ContentBackend();
  NS_ASSERTION(backendType != BackendType::NONE, "No backend.");

  if (!Factory::DoesBackendSupportDataDrawtarget(backendType)) {
    backendType = BackendType::SKIA;
  }

  RefPtr<DrawTarget> dt = Factory::CreateDrawTargetForData(
      backendType, aData, aSize, aStride, aFormat, aUninitialized);

  return dt.forget();
}

/* static */
BackendType gfxPlatform::BackendTypeForName(const nsCString& aName) {
  if (aName.EqualsLiteral("cairo")) return BackendType::CAIRO;
  if (aName.EqualsLiteral("skia")) return BackendType::SKIA;
  if (aName.EqualsLiteral("direct2d")) return BackendType::DIRECT2D;
  if (aName.EqualsLiteral("direct2d1.1")) return BackendType::DIRECT2D1_1;
  return BackendType::NONE;
}

nsresult gfxPlatform::GetFontList(nsAtom* aLangGroup,
                                  const nsACString& aGenericFamily,
                                  nsTArray<nsString>& aListOfFonts) {
  gfxPlatformFontList::PlatformFontList()->GetFontList(
      aLangGroup, aGenericFamily, aListOfFonts);
  return NS_OK;
}

nsresult gfxPlatform::UpdateFontList(bool aFullRebuild) {
  gfxPlatformFontList::PlatformFontList()->UpdateFontList(aFullRebuild);
  return NS_OK;
}

void gfxPlatform::GetStandardFamilyName(const nsCString& aFontName,
                                        nsACString& aFamilyName) {
  gfxPlatformFontList::PlatformFontList()->GetStandardFamilyName(aFontName,
                                                                 aFamilyName);
}

nsAutoCString gfxPlatform::GetDefaultFontName(
    const nsACString& aLangGroup, const nsACString& aGenericFamily) {
  // To benefit from Return Value Optimization, all paths here must return
  // this one variable:
  nsAutoCString result;

  auto* pfl = gfxPlatformFontList::PlatformFontList();
  FamilyAndGeneric fam = pfl->GetDefaultFontFamily(aLangGroup, aGenericFamily);
  if (!pfl->GetLocalizedFamilyName(fam.mFamily, result)) {
    NS_WARNING("missing default font-family name");
  }

  return result;
}

bool gfxPlatform::DownloadableFontsEnabled() {
  if (mAllowDownloadableFonts == UNINITIALIZED_VALUE) {
    mAllowDownloadableFonts =
        Preferences::GetBool(GFX_DOWNLOADABLE_FONTS_ENABLED, false);
  }

  return mAllowDownloadableFonts;
}

bool gfxPlatform::UseCmapsDuringSystemFallback() {
  return StaticPrefs::gfx_font_rendering_fallback_always_use_cmaps();
}

bool gfxPlatform::OpenTypeSVGEnabled() {
  return StaticPrefs::gfx_font_rendering_opentype_svg_enabled();
}

uint32_t gfxPlatform::WordCacheCharLimit() {
  return StaticPrefs::gfx_font_rendering_wordcache_charlimit();
}

uint32_t gfxPlatform::WordCacheMaxEntries() {
  return StaticPrefs::gfx_font_rendering_wordcache_maxentries();
}

bool gfxPlatform::UseGraphiteShaping() {
  return StaticPrefs::gfx_font_rendering_graphite_enabled();
}

bool gfxPlatform::IsFontFormatSupported(
    StyleFontFaceSourceFormatKeyword aFormatHint,
    StyleFontFaceSourceTechFlags aTechFlags) {
  // By default, font resources are assumed to be supported; but if the format
  // hint or technology flags explicitly indicate something we don't support,
  // then return false.
  switch (aFormatHint) {
    case StyleFontFaceSourceFormatKeyword::None:
      break;
    case StyleFontFaceSourceFormatKeyword::Collection:
      return false;
    case StyleFontFaceSourceFormatKeyword::Opentype:
    case StyleFontFaceSourceFormatKeyword::Truetype:
      break;
    case StyleFontFaceSourceFormatKeyword::EmbeddedOpentype:
      return false;
    case StyleFontFaceSourceFormatKeyword::Svg:
      return false;
    case StyleFontFaceSourceFormatKeyword::Woff:
      break;
    case StyleFontFaceSourceFormatKeyword::Woff2:
      break;
    case StyleFontFaceSourceFormatKeyword::Unknown:
--> --------------------

--> maximum size reached

--> --------------------

100%


¤ Dauer der Verarbeitung: 0.12 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.