tor-browser

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

gfxPlatformGtk.cpp (33217B)


      1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #define PANGO_ENABLE_BACKEND
      7 #define PANGO_ENABLE_ENGINE
      8 
      9 #include "gfxPlatformGtk.h"
     10 
     11 #include <gtk/gtk.h>
     12 #include <fontconfig/fontconfig.h>
     13 
     14 #include "base/task.h"
     15 #include "base/thread.h"
     16 #include "base/message_loop.h"
     17 #include "cairo.h"
     18 #include "gfx2DGlue.h"
     19 #include "gfxFcPlatformFontList.h"
     20 #include "gfxConfig.h"
     21 #include "gfxContext.h"
     22 #include "gfxImageSurface.h"
     23 #include "gfxUserFontSet.h"
     24 #include "gfxUtils.h"
     25 #include "gfxFT2FontBase.h"
     26 #include "gfxTextRun.h"
     27 #include "GLContextProvider.h"
     28 #include "mozilla/Components.h"
     29 #include "mozilla/dom/ContentChild.h"
     30 #include "mozilla/FontPropertyTypes.h"
     31 #include "mozilla/gfx/2D.h"
     32 #include "mozilla/gfx/Logging.h"
     33 #include "mozilla/Monitor.h"
     34 #include "mozilla/Preferences.h"
     35 #include "mozilla/StaticPrefs_gfx.h"
     36 #include "mozilla/StaticPrefs_layers.h"
     37 #include "mozilla/StaticPrefs_media.h"
     38 #include "nsAppRunner.h"
     39 #include "nsIGfxInfo.h"
     40 #include "nsMathUtils.h"
     41 #include "nsUnicharUtils.h"
     42 #include "nsUnicodeProperties.h"
     43 #include "prenv.h"
     44 #include "VsyncSource.h"
     45 #include "mozilla/WidgetUtilsGtk.h"
     46 
     47 #ifdef MOZ_X11
     48 #  include "mozilla/gfx/XlibDisplay.h"
     49 #  include <gdk/gdkx.h>
     50 #  include <X11/extensions/Xrandr.h>
     51 #  include "cairo-xlib.h"
     52 #  include "gfxXlibSurface.h"
     53 #  include "GLContextGLX.h"
     54 #  include "GLXLibrary.h"
     55 #  include "mozilla/X11Util.h"
     56 #  include "SoftwareVsyncSource.h"
     57 
     58 /* Undefine the Status from Xlib since it will conflict with system headers on
     59 * OSX */
     60 #  if defined(__APPLE__) && defined(Status)
     61 #    undef Status
     62 #  endif
     63 #endif /* MOZ_X11 */
     64 
     65 #ifdef MOZ_WAYLAND
     66 #  include <gdk/gdkwayland.h>
     67 #  include "mozilla/widget/nsWaylandDisplay.h"
     68 #endif
     69 #ifdef MOZ_WIDGET_GTK
     70 #  include "mozilla/widget/DMABufDevice.h"
     71 #  include "mozilla/StaticPrefs_widget.h"
     72 #endif
     73 
     74 #define GDK_PIXMAP_SIZE_MAX 32767
     75 
     76 #define GFX_PREF_MAX_GENERIC_SUBSTITUTIONS \
     77  "gfx.font_rendering.fontconfig.max_generic_substitutions"
     78 
     79 using namespace mozilla;
     80 using namespace mozilla::gfx;
     81 using namespace mozilla::unicode;
     82 using namespace mozilla::widget;
     83 
     84 static FT_Library gPlatformFTLibrary = nullptr;
     85 
     86 #if defined(MOZ_X11)
     87 // TODO(aosmond): The envvar is deprecated. We should remove it once EGL is the
     88 // default in release.
     89 static bool IsX11EGLEnvvarEnabled() {
     90  const char* eglPref = PR_GetEnv("MOZ_X11_EGL");
     91  return (eglPref && *eglPref);
     92 }
     93 #endif
     94 
     95 gfxPlatformGtk::gfxPlatformGtk() {
     96  if (!gfxPlatform::IsHeadless()) {
     97    if (!gtk_init_check(nullptr, nullptr)) {
     98      gfxCriticalNote << "Failed to init Gtk, missing display? DISPLAY="
     99                      << getenv("DISPLAY")
    100                      << " WAYLAND_DISPLAY=" << getenv("WAYLAND_DISPLAY")
    101                      << "\n";
    102      abort();
    103    }
    104  }
    105 
    106  mIsX11Display = gfxPlatform::IsHeadless() ? false : GdkIsX11Display();
    107  if (XRE_IsParentProcess()) {
    108    InitX11EGLConfig();
    109    if (IsWaylandDisplay() || gfxConfig::IsEnabled(Feature::X11_EGL)) {
    110      gfxVars::SetUseEGL(true);
    111    }
    112    InitDmabufConfig();
    113    if (gfxConfig::IsEnabled(Feature::DMABUF)) {
    114      gfxVars::SetUseDMABuf(true);
    115    }
    116  }
    117 
    118  InitBackendPrefs(GetBackendPrefs());
    119 
    120  gPlatformFTLibrary = Factory::NewFTLibrary();
    121  MOZ_RELEASE_ASSERT(gPlatformFTLibrary);
    122  Factory::SetFTLibrary(gPlatformFTLibrary);
    123 
    124  // Bug 1714483: Force disable FXAA Antialiasing on NV drivers. This is a
    125  // temporary workaround for a driver bug.
    126  PR_SetEnv("__GL_ALLOW_FXAA_USAGE=0");
    127 
    128  InitMesaThreading();
    129 }
    130 
    131 gfxPlatformGtk::~gfxPlatformGtk() {
    132  Factory::ReleaseFTLibrary(gPlatformFTLibrary);
    133  gPlatformFTLibrary = nullptr;
    134 }
    135 
    136 void gfxPlatformGtk::InitAcceleration() {
    137  gfxPlatform::InitAcceleration();
    138 
    139  if (XRE_IsContentProcess()) {
    140    ImportCachedContentDeviceData();
    141  }
    142 }
    143 
    144 void gfxPlatformGtk::InitX11EGLConfig() {
    145  FeatureState& feature = gfxConfig::GetFeature(Feature::X11_EGL);
    146 #ifdef MOZ_X11
    147  feature.EnableByDefault();
    148 
    149  if (StaticPrefs::gfx_x11_egl_force_enabled_AtStartup()) {
    150    feature.UserForceEnable("Force enabled by pref");
    151  } else if (IsX11EGLEnvvarEnabled()) {
    152    feature.UserForceEnable("Force enabled by envvar");
    153  } else if (StaticPrefs::gfx_x11_egl_force_disabled_AtStartup()) {
    154    feature.UserDisable("Force disabled by pref",
    155                        "FEATURE_FAILURE_USER_FORCE_DISABLED"_ns);
    156  }
    157 
    158  nsCString failureId;
    159  int32_t status;
    160  nsCOMPtr<nsIGfxInfo> gfxInfo = components::GfxInfo::Service();
    161  if (NS_FAILED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_X11_EGL,
    162                                          failureId, &status))) {
    163    feature.Disable(FeatureStatus::BlockedNoGfxInfo, "gfxInfo is broken",
    164                    "FEATURE_FAILURE_NO_GFX_INFO"_ns);
    165  } else if (status != nsIGfxInfo::FEATURE_STATUS_OK) {
    166    feature.Disable(FeatureStatus::Blocklisted, "Blocklisted by gfxInfo",
    167                    failureId);
    168  }
    169 
    170  nsAutoString testType;
    171  gfxInfo->GetTestType(testType);
    172  // We can only use X11/EGL if we actually found the EGL library and
    173  // successfully use it to determine system information in glxtest.
    174  if (testType != u"EGL") {
    175    feature.ForceDisable(FeatureStatus::Broken, "glxtest could not use EGL",
    176                         "FEATURE_FAILURE_GLXTEST_NO_EGL"_ns);
    177  }
    178 #else
    179  feature.DisableByDefault(FeatureStatus::Unavailable, "X11 support missing",
    180                           "FEATURE_FAILURE_NO_X11"_ns);
    181 #endif
    182 }
    183 
    184 void gfxPlatformGtk::InitDmabufConfig() {
    185  FeatureState& feature = gfxConfig::GetFeature(Feature::DMABUF);
    186  feature.EnableByDefault();
    187 
    188  nsCString failureId;
    189  int32_t status;
    190  nsCOMPtr<nsIGfxInfo> gfxInfo = components::GfxInfo::Service();
    191  if (NS_FAILED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DMABUF, failureId,
    192                                          &status))) {
    193    feature.Disable(FeatureStatus::BlockedNoGfxInfo, "gfxInfo is broken",
    194                    "FEATURE_FAILURE_NO_GFX_INFO"_ns);
    195  } else if (status != nsIGfxInfo::FEATURE_STATUS_OK) {
    196    feature.Disable(FeatureStatus::Blocklisted, "Blocklisted by gfxInfo",
    197                    failureId);
    198  }
    199 
    200  if (StaticPrefs::widget_dmabuf_force_enabled_AtStartup()) {
    201    feature.UserForceEnable("Force enabled by pref");
    202  } else if (!StaticPrefs::widget_dmabuf_enabled_AtStartup()) {
    203    feature.UserDisable("Force disable by pref",
    204                        "FEATURE_FAILURE_USER_FORCE_DISABLED"_ns);
    205  }
    206 
    207  if (!gfxVars::UseEGL()) {
    208    feature.ForceDisable(FeatureStatus::Unavailable, "Requires EGL",
    209                         "FEATURE_FAILURE_REQUIRES_EGL"_ns);
    210  }
    211 
    212  if (!gfxVars::WebglUseHardware()) {
    213    feature.Disable(FeatureStatus::Blocklisted,
    214                    "DMABuf disabled with software rendering", failureId);
    215  }
    216 
    217  nsAutoCString drmRenderDevice;
    218  gfxInfo->GetDrmRenderDevice(drmRenderDevice);
    219  gfxVars::SetDrmRenderDevice(drmRenderDevice);
    220 
    221  if (feature.IsEnabled()) {
    222    DMABufDeviceLock device;
    223    if (!device.GetDMABufDevice()->IsEnabled(failureId)) {
    224      feature.ForceDisable(FeatureStatus::Failed, "Failed to configure",
    225                           failureId);
    226    }
    227    // Make sure we have DMABuf formats available.
    228    (void)GetGlobalDMABufFormats();
    229  }
    230 }
    231 
    232 void gfxPlatformGtk::InitPlatformHardwareVideoConfig() {
    233  FeatureState& featureDec =
    234      gfxConfig::GetFeature(Feature::HARDWARE_VIDEO_DECODING);
    235  if (!gfxVars::UseEGL()) {
    236    featureDec.ForceDisable(FeatureStatus::Unavailable, "Requires EGL",
    237                            "FEATURE_FAILURE_REQUIRES_EGL"_ns);
    238    gfxConfig::ForceDisable(Feature::HARDWARE_VIDEO_ENCODING,
    239                            FeatureStatus::Unavailable, "Requires EGL",
    240                            "FEATURE_FAILURE_REQUIRES_EGL"_ns);
    241  }
    242 
    243  if (!featureDec.IsEnabled()) {
    244    return;
    245  }
    246 
    247  // Configure zero-copy playback feature.
    248  FeatureState& featureZeroCopy =
    249      gfxConfig::GetFeature(Feature::HW_DECODED_VIDEO_ZERO_COPY);
    250 
    251  featureZeroCopy.EnableByDefault();
    252  uint32_t state =
    253      StaticPrefs::media_ffmpeg_vaapi_force_surface_zero_copy_AtStartup();
    254  if (state == 0) {
    255    featureZeroCopy.UserDisable("Force disable by pref",
    256                                "FEATURE_FAILURE_USER_FORCE_DISABLED"_ns);
    257  } else if (state == 1) {
    258    featureZeroCopy.UserEnable("Force enabled by pref");
    259  } else {
    260    nsCString failureId;
    261    int32_t status = nsIGfxInfo::FEATURE_STATUS_UNKNOWN;
    262    nsCOMPtr<nsIGfxInfo> gfxInfo = components::GfxInfo::Service();
    263    if (NS_FAILED(gfxInfo->GetFeatureStatus(
    264            nsIGfxInfo::FEATURE_HW_DECODED_VIDEO_ZERO_COPY, failureId,
    265            &status))) {
    266      featureZeroCopy.Disable(FeatureStatus::BlockedNoGfxInfo,
    267                              "gfxInfo is broken",
    268                              "FEATURE_FAILURE_NO_GFX_INFO"_ns);
    269    } else if (status == nsIGfxInfo::FEATURE_BLOCKED_PLATFORM_TEST) {
    270      featureZeroCopy.ForceDisable(FeatureStatus::Unavailable,
    271                                   "Force disabled by gfxInfo", failureId);
    272    } else if (status != nsIGfxInfo::FEATURE_ALLOW_ALWAYS) {
    273      featureZeroCopy.Disable(FeatureStatus::Blocklisted,
    274                              "Blocklisted by gfxInfo", failureId);
    275    }
    276  }
    277  if (featureZeroCopy.IsEnabled()) {
    278    gfxVars::SetHwDecodedVideoZeroCopy(true);
    279  }
    280 }
    281 
    282 void gfxPlatformGtk::InitWebRenderConfig() {
    283  gfxPlatform::InitWebRenderConfig();
    284 
    285  if (!XRE_IsParentProcess()) {
    286    return;
    287  }
    288 
    289  FeatureState& feature = gfxConfig::GetFeature(Feature::WEBRENDER_COMPOSITOR);
    290  // HDR requires compositor to work
    291 #if defined(MOZ_WAYLAND)
    292  if (feature.IsEnabled()) {
    293    if (!IsWaylandDisplay()) {
    294      feature.ForceDisable(FeatureStatus::Unavailable,
    295                           "Wayland support missing",
    296                           "FEATURE_FAILURE_NO_WAYLAND"_ns);
    297    } else if (gfxConfig::IsEnabled(Feature::WEBRENDER) &&
    298               !gfxConfig::IsEnabled(Feature::DMABUF)) {
    299      // We use zwp_linux_dmabuf_v1 and GBM directly to manage FBOs. In theory
    300      // this is also possible vie EGLstreams, but we don't bother to implement
    301      // it as recent NVidia drivers support GBM and DMABuf as well.
    302      feature.ForceDisable(FeatureStatus::Unavailable,
    303                           "Hardware Webrender requires DMAbuf support",
    304                           "FEATURE_FAILURE_NO_DMABUF"_ns);
    305    } else if (!widget::WaylandDisplayGet()->GetViewporter()) {
    306      feature.ForceDisable(FeatureStatus::Unavailable,
    307                           "Requires wp_viewporter protocol support",
    308                           "FEATURE_FAILURE_REQUIRES_WPVIEWPORTER"_ns);
    309    }
    310  }
    311 #else  // MOZ_WAYLAND
    312  feature.ForceDisable(FeatureStatus::Unavailable, "Not available on X11",
    313                       "FEATURE_FAILURE_NO_WAYLAND"_ns);
    314 #endif
    315 
    316  gfxVars::SetUseWebRenderCompositor(feature.IsEnabled());
    317 }
    318 
    319 void gfxPlatformGtk::InitPlatformGPUProcessPrefs() {
    320 #ifdef MOZ_WAYLAND
    321  if (IsWaylandDisplay()) {
    322    FeatureState& gpuProc = gfxConfig::GetFeature(Feature::GPU_PROCESS);
    323    gpuProc.ForceDisable(FeatureStatus::Blocked,
    324                         "Wayland does not work in the GPU process",
    325                         "FEATURE_FAILURE_WAYLAND"_ns);
    326  }
    327 #endif
    328 }
    329 
    330 void gfxPlatformGtk::InitMesaThreading() {
    331  FeatureState& featureMesaThreading =
    332      gfxConfig::GetFeature(Feature::MESA_THREADING);
    333  featureMesaThreading.EnableByDefault();
    334 
    335  nsCString failureId;
    336  int32_t status;
    337  nsCOMPtr<nsIGfxInfo> gfxInfo = components::GfxInfo::Service();
    338  if (NS_FAILED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_MESA_THREADING,
    339                                          failureId, &status))) {
    340    featureMesaThreading.Disable(FeatureStatus::BlockedNoGfxInfo,
    341                                 "gfxInfo is broken",
    342                                 "FEATURE_FAILURE_NO_GFX_INFO"_ns);
    343  } else if (status != nsIGfxInfo::FEATURE_STATUS_OK) {
    344    featureMesaThreading.Disable(FeatureStatus::Blocklisted,
    345                                 "Blocklisted by gfxInfo", failureId);
    346  }
    347 
    348  // Enabling glthread crashes on X11/EGL, see bug 1670545
    349  if (gfxConfig::IsEnabled(Feature::X11_EGL) && IsX11Display()) {
    350    featureMesaThreading.Disable(FeatureStatus::Failed,
    351                                 "No glthread with EGL and X11",
    352                                 "FEATURE_FAILURE_EGL_X11"_ns);
    353  }
    354 
    355  if (!featureMesaThreading.IsEnabled()) {
    356    PR_SetEnv("mesa_glthread=false");
    357  }
    358 }
    359 
    360 already_AddRefed<gfxASurface> gfxPlatformGtk::CreateOffscreenSurface(
    361    const IntSize& aSize, gfxImageFormat aFormat) {
    362  if (!Factory::AllowedSurfaceSize(aSize)) {
    363    return nullptr;
    364  }
    365 
    366  RefPtr<gfxASurface> newSurface;
    367  bool needsClear = true;
    368  // XXX we really need a different interface here, something that passes
    369  // in more context, including the display and/or target surface type that
    370  // we should try to match
    371  GdkScreen* gdkScreen = gdk_screen_get_default();
    372  if (gdkScreen) {
    373    newSurface = new gfxImageSurface(aSize, aFormat);
    374    // The gfxImageSurface ctor zeroes this for us, no need to
    375    // waste time clearing again
    376    needsClear = false;
    377  }
    378 
    379  if (!newSurface) {
    380    // We couldn't create a native surface for whatever reason;
    381    // e.g., no display, no RENDER, bad size, etc.
    382    // Fall back to image surface for the data.
    383    newSurface = new gfxImageSurface(aSize, aFormat);
    384  }
    385 
    386  if (newSurface->CairoStatus()) {
    387    newSurface = nullptr;  // surface isn't valid for some reason
    388  }
    389 
    390  if (newSurface && needsClear) {
    391    gfxUtils::ClearThebesSurface(newSurface);
    392  }
    393 
    394  return newSurface.forget();
    395 }
    396 
    397 nsresult gfxPlatformGtk::GetFontList(nsAtom* aLangGroup,
    398                                     const nsACString& aGenericFamily,
    399                                     nsTArray<nsString>& aListOfFonts) {
    400  gfxPlatformFontList::PlatformFontList()->GetFontList(
    401      aLangGroup, aGenericFamily, aListOfFonts);
    402  return NS_OK;
    403 }
    404 
    405 // xxx - this is ubuntu centric, need to go through other distros and flesh
    406 // out a more general list
    407 static const char kFontDejaVuSans[] = "DejaVu Sans";
    408 static const char kFontDejaVuSerif[] = "DejaVu Serif";
    409 static const char kFontFreeSans[] = "FreeSans";
    410 static const char kFontFreeSerif[] = "FreeSerif";
    411 static const char kFontTakaoPGothic[] = "TakaoPGothic";
    412 static const char kFontTwemojiMozilla[] = "Twemoji Mozilla";
    413 static const char kFontDroidSansFallback[] = "Droid Sans Fallback";
    414 static const char kFontWenQuanYiMicroHei[] = "WenQuanYi Micro Hei";
    415 static const char kFontNanumGothic[] = "NanumGothic";
    416 static const char kFontSymbola[] = "Symbola";
    417 static const char kFontNotoSansSymbols[] = "Noto Sans Symbols";
    418 static const char kFontNotoSansSymbols2[] = "Noto Sans Symbols2";
    419 
    420 void gfxPlatformGtk::GetCommonFallbackFonts(uint32_t aCh, Script aRunScript,
    421                                            FontPresentation aPresentation,
    422                                            nsTArray<const char*>& aFontList) {
    423  if (PrefersColor(aPresentation)) {
    424    aFontList.AppendElement(kFontTwemojiMozilla);
    425  }
    426 
    427  aFontList.AppendElement(kFontDejaVuSerif);
    428  aFontList.AppendElement(kFontFreeSerif);
    429  aFontList.AppendElement(kFontDejaVuSans);
    430  aFontList.AppendElement(kFontFreeSans);
    431  aFontList.AppendElement(kFontSymbola);
    432  aFontList.AppendElement(kFontNotoSansSymbols);
    433  aFontList.AppendElement(kFontNotoSansSymbols2);
    434 
    435  // add fonts for CJK ranges
    436  // xxx - this isn't really correct, should use the same CJK font ordering
    437  // as the pref font code
    438  if (aCh >= 0x3000 && ((aCh < 0xe000) || (aCh >= 0xf900 && aCh < 0xfff0) ||
    439                        ((aCh >> 16) == 2))) {
    440    aFontList.AppendElement(kFontTakaoPGothic);
    441    aFontList.AppendElement(kFontDroidSansFallback);
    442    aFontList.AppendElement(kFontWenQuanYiMicroHei);
    443    aFontList.AppendElement(kFontNanumGothic);
    444  }
    445 }
    446 
    447 void gfxPlatformGtk::ReadSystemFontList(
    448    mozilla::dom::SystemFontList* retValue) {
    449  gfxFcPlatformFontList::PlatformFontList()->ReadSystemFontList(retValue);
    450 }
    451 
    452 bool gfxPlatformGtk::CreatePlatformFontList() {
    453  return gfxPlatformFontList::Initialize(new gfxFcPlatformFontList);
    454 }
    455 
    456 gfxImageFormat gfxPlatformGtk::GetOffscreenFormat() {
    457  // Make sure there is a screen
    458  GdkScreen* screen = gdk_screen_get_default();
    459  if (screen && gdk_visual_get_depth(gdk_visual_get_system()) == 16) {
    460    return SurfaceFormat::R5G6B5_UINT16;
    461  }
    462 
    463  return SurfaceFormat::X8R8G8B8_UINT32;
    464 }
    465 
    466 void gfxPlatformGtk::FontsPrefsChanged(const char* aPref) {
    467  // only checking for generic substitions, pass other changes up
    468  if (strcmp(GFX_PREF_MAX_GENERIC_SUBSTITUTIONS, aPref) != 0) {
    469    gfxPlatform::FontsPrefsChanged(aPref);
    470    return;
    471  }
    472 
    473  gfxFcPlatformFontList* pfl = gfxFcPlatformFontList::PlatformFontList();
    474  pfl->ClearGenericMappings();
    475  FlushFontAndWordCaches();
    476 }
    477 
    478 bool gfxPlatformGtk::AccelerateLayersByDefault() { return true; }
    479 
    480 #if defined(MOZ_X11)
    481 
    482 static nsTArray<uint8_t> GetDisplayICCProfile(Display* dpy, Window& root) {
    483  const char kIccProfileAtomName[] = "_ICC_PROFILE";
    484  Atom iccAtom = XInternAtom(dpy, kIccProfileAtomName, TRUE);
    485  if (!iccAtom) {
    486    return nsTArray<uint8_t>();
    487  }
    488 
    489  Atom retAtom;
    490  int retFormat;
    491  unsigned long retLength, retAfter;
    492  unsigned char* retProperty;
    493 
    494  if (XGetWindowProperty(dpy, root, iccAtom, 0, INT_MAX /* length */, X11False,
    495                         AnyPropertyType, &retAtom, &retFormat, &retLength,
    496                         &retAfter, &retProperty) != Success) {
    497    return nsTArray<uint8_t>();
    498  }
    499 
    500  nsTArray<uint8_t> result;
    501 
    502  if (retLength > 0) {
    503    result.AppendElements(static_cast<uint8_t*>(retProperty), retLength);
    504  }
    505 
    506  XFree(retProperty);
    507 
    508  return result;
    509 }
    510 
    511 nsTArray<uint8_t> gfxPlatformGtk::GetPlatformCMSOutputProfileData() {
    512  nsTArray<uint8_t> prefProfileData = GetPrefCMSOutputProfileData();
    513  if (!prefProfileData.IsEmpty()) {
    514    return prefProfileData;
    515  }
    516 
    517  if (XRE_IsContentProcess()) {
    518    auto& cmsOutputProfileData = GetCMSOutputProfileData();
    519    // We should have set our profile data when we received our initial
    520    // ContentDeviceData.
    521    MOZ_ASSERT(cmsOutputProfileData.isSome(),
    522               "Should have created output profile data when we received "
    523               "initial content device data.");
    524    if (cmsOutputProfileData.isSome()) {
    525      return cmsOutputProfileData.ref().Clone();
    526    }
    527    return nsTArray<uint8_t>();
    528  }
    529 
    530  if (!mIsX11Display) {
    531    return nsTArray<uint8_t>();
    532  }
    533 
    534  GdkDisplay* display = gdk_display_get_default();
    535  Display* dpy = GDK_DISPLAY_XDISPLAY(display);
    536  // In xpcshell tests, we never initialize X and hence don't have a Display.
    537  // In this case, there's no output colour management to be done, so we just
    538  // return with nullptr.
    539  if (!dpy) {
    540    return nsTArray<uint8_t>();
    541  }
    542 
    543  Window root = gdk_x11_get_default_root_xwindow();
    544 
    545  // First try ICC Profile
    546  nsTArray<uint8_t> iccResult = GetDisplayICCProfile(dpy, root);
    547  if (!iccResult.IsEmpty()) {
    548    return iccResult;
    549  }
    550 
    551  // If ICC doesn't work, then try EDID
    552  const char kEdid1AtomName[] = "XFree86_DDC_EDID1_RAWDATA";
    553  Atom edidAtom = XInternAtom(dpy, kEdid1AtomName, TRUE);
    554  if (!edidAtom) {
    555    return nsTArray<uint8_t>();
    556  }
    557 
    558  Atom retAtom;
    559  int retFormat;
    560  unsigned long retLength, retAfter;
    561  unsigned char* retProperty;
    562 
    563  if (XGetWindowProperty(dpy, root, edidAtom, 0, 32, X11False, AnyPropertyType,
    564                         &retAtom, &retFormat, &retLength, &retAfter,
    565                         &retProperty) != Success) {
    566    return nsTArray<uint8_t>();
    567  }
    568 
    569  if (retLength != 128) {
    570    return nsTArray<uint8_t>();
    571  }
    572 
    573  // Format documented in "VESA E-EDID Implementation Guide"
    574  float gamma = (100 + (float)retProperty[0x17]) / 100.0f;
    575 
    576  qcms_CIE_xyY whitePoint;
    577  whitePoint.x =
    578      ((retProperty[0x21] << 2) | (retProperty[0x1a] >> 2 & 3)) / 1024.0;
    579  whitePoint.y =
    580      ((retProperty[0x22] << 2) | (retProperty[0x1a] >> 0 & 3)) / 1024.0;
    581  whitePoint.Y = 1.0;
    582 
    583  qcms_CIE_xyYTRIPLE primaries;
    584  primaries.red.x =
    585      ((retProperty[0x1b] << 2) | (retProperty[0x19] >> 6 & 3)) / 1024.0;
    586  primaries.red.y =
    587      ((retProperty[0x1c] << 2) | (retProperty[0x19] >> 4 & 3)) / 1024.0;
    588  primaries.red.Y = 1.0;
    589 
    590  primaries.green.x =
    591      ((retProperty[0x1d] << 2) | (retProperty[0x19] >> 2 & 3)) / 1024.0;
    592  primaries.green.y =
    593      ((retProperty[0x1e] << 2) | (retProperty[0x19] >> 0 & 3)) / 1024.0;
    594  primaries.green.Y = 1.0;
    595 
    596  primaries.blue.x =
    597      ((retProperty[0x1f] << 2) | (retProperty[0x1a] >> 6 & 3)) / 1024.0;
    598  primaries.blue.y =
    599      ((retProperty[0x20] << 2) | (retProperty[0x1a] >> 4 & 3)) / 1024.0;
    600  primaries.blue.Y = 1.0;
    601 
    602  XFree(retProperty);
    603 
    604  void* mem = nullptr;
    605  size_t size = 0;
    606  qcms_data_create_rgb_with_gamma(whitePoint, primaries, gamma, &mem, &size);
    607  if (!mem) {
    608    return nsTArray<uint8_t>();
    609  }
    610 
    611  nsTArray<uint8_t> result;
    612  result.AppendElements(static_cast<uint8_t*>(mem), size);
    613  free(mem);
    614 
    615  // XXX: It seems like we get wrong colors when using this constructed profile:
    616  // See bug 1696819. For now just forget that we made it.
    617  return nsTArray<uint8_t>();
    618 }
    619 
    620 #else  // defined(MOZ_X11)
    621 
    622 nsTArray<uint8_t> gfxPlatformGtk::GetPlatformCMSOutputProfileData() {
    623  return nsTArray<uint8_t>();
    624 }
    625 
    626 #endif
    627 
    628 bool gfxPlatformGtk::CheckVariationFontSupport() {
    629  // Although there was some variation/multiple-master support in FreeType
    630  // in older versions, it seems too incomplete/unstable for us to use
    631  // until at least 2.7.1.
    632  FT_Int major, minor, patch;
    633  FT_Library_Version(Factory::GetFTLibrary(), &major, &minor, &patch);
    634  return major * 1000000 + minor * 1000 + patch >= 2007001;
    635 }
    636 
    637 #ifdef MOZ_X11
    638 
    639 class GtkVsyncSource final : public VsyncSource {
    640 public:
    641  GtkVsyncSource()
    642      : mGLContext(nullptr),
    643        mXDisplay(nullptr),
    644        mSetupLock("GLXVsyncSetupLock"),
    645        mVsyncThread("GLXVsyncThread"),
    646        mVsyncTask(nullptr),
    647        mVsyncEnabledLock("GLXVsyncEnabledLock"),
    648        mVsyncEnabled(false) {
    649    MOZ_ASSERT(NS_IsMainThread());
    650  }
    651 
    652  virtual ~GtkVsyncSource() { MOZ_ASSERT(NS_IsMainThread()); }
    653 
    654  // Sets up the display's GL context on a worker thread.
    655  // Required as GLContexts may only be used by the creating thread.
    656  // Returns true if setup was a success.
    657  bool Setup() {
    658    MonitorAutoLock lock(mSetupLock);
    659    MOZ_ASSERT(NS_IsMainThread());
    660    if (!mVsyncThread.Start()) return false;
    661 
    662    RefPtr<Runnable> vsyncSetup =
    663        NewRunnableMethod("GtkVsyncSource::SetupGLContext", this,
    664                          &GtkVsyncSource::SetupGLContext);
    665    mVsyncThread.message_loop()->PostTask(vsyncSetup.forget());
    666    // Wait until the setup has completed.
    667    lock.Wait();
    668    return mGLContext != nullptr;
    669  }
    670 
    671  // Called on the Vsync thread to setup the GL context.
    672  void SetupGLContext() {
    673    MonitorAutoLock lock(mSetupLock);
    674    MOZ_ASSERT(!NS_IsMainThread());
    675    MOZ_ASSERT(!mGLContext, "GLContext already setup!");
    676 
    677    // Create video sync timer on a separate Display to prevent locking the
    678    // main thread X display.
    679    mXDisplay = XOpenDisplay(nullptr);
    680    if (!mXDisplay) {
    681      lock.NotifyAll();
    682      return;
    683    }
    684 
    685    // Most compositors wait for vsync events on the root window.
    686    Window root = DefaultRootWindow(mXDisplay);
    687    int screen = DefaultScreen(mXDisplay);
    688 
    689    GLXFBConfig config;
    690    int visid;
    691    bool forWebRender = false;
    692    if (!gl::GLContextGLX::FindFBConfigForWindow(
    693            mXDisplay, screen, root, &config, &visid, forWebRender)) {
    694      lock.NotifyAll();
    695      return;
    696    }
    697 
    698    mGLContext = gl::GLContextGLX::CreateGLContext(
    699        {}, gfx::XlibDisplay::Borrow(mXDisplay), root, config);
    700 
    701    if (!mGLContext) {
    702      lock.NotifyAll();
    703      return;
    704    }
    705 
    706    mGLContext->MakeCurrent();
    707 
    708    // Test that SGI_video_sync lets us get the counter.
    709    unsigned int syncCounter = 0;
    710    if (gl::sGLXLibrary.fGetVideoSync(&syncCounter) != 0) {
    711      mGLContext = nullptr;
    712    }
    713 
    714    lock.NotifyAll();
    715  }
    716 
    717  virtual void EnableVsync() override {
    718    MOZ_ASSERT(NS_IsMainThread());
    719    MOZ_ASSERT(mGLContext, "GLContext not setup!");
    720 
    721    MonitorAutoLock lock(mVsyncEnabledLock);
    722    if (mVsyncEnabled) {
    723      return;
    724    }
    725    mVsyncEnabled = true;
    726 
    727    // If the task has not nulled itself out, it hasn't yet realized
    728    // that vsync was disabled earlier, so continue its execution.
    729    if (!mVsyncTask) {
    730      mVsyncTask = NewRunnableMethod("GtkVsyncSource::RunVsync", this,
    731                                     &GtkVsyncSource::RunVsync);
    732      RefPtr<Runnable> addrefedTask = mVsyncTask;
    733      mVsyncThread.message_loop()->PostTask(addrefedTask.forget());
    734    }
    735  }
    736 
    737  virtual void DisableVsync() override {
    738    MonitorAutoLock lock(mVsyncEnabledLock);
    739    mVsyncEnabled = false;
    740  }
    741 
    742  virtual bool IsVsyncEnabled() override {
    743    MonitorAutoLock lock(mVsyncEnabledLock);
    744    return mVsyncEnabled;
    745  }
    746 
    747  virtual void Shutdown() override {
    748    MOZ_ASSERT(NS_IsMainThread());
    749    DisableVsync();
    750 
    751    // Cleanup thread-specific resources before shutting down.
    752    RefPtr<Runnable> shutdownTask = NewRunnableMethod(
    753        "GtkVsyncSource::Cleanup", this, &GtkVsyncSource::Cleanup);
    754    mVsyncThread.message_loop()->PostTask(shutdownTask.forget());
    755 
    756    // Stop, waiting for the cleanup task to finish execution.
    757    mVsyncThread.Stop();
    758  }
    759 
    760 private:
    761  void RunVsync() {
    762    MOZ_ASSERT(!NS_IsMainThread());
    763 
    764    mGLContext->MakeCurrent();
    765 
    766    unsigned int syncCounter = 0;
    767    gl::sGLXLibrary.fGetVideoSync(&syncCounter);
    768    for (;;) {
    769      {
    770        MonitorAutoLock lock(mVsyncEnabledLock);
    771        if (!mVsyncEnabled) {
    772          mVsyncTask = nullptr;
    773          return;
    774        }
    775      }
    776 
    777      TimeStamp lastVsync = TimeStamp::Now();
    778      bool useSoftware = false;
    779 
    780      // Wait until the video sync counter reaches the next value by waiting
    781      // until the parity of the counter value changes.
    782      unsigned int nextSync = syncCounter + 1;
    783      int status;
    784      if ((status = gl::sGLXLibrary.fWaitVideoSync(2, (int)nextSync % 2,
    785                                                   &syncCounter)) != 0) {
    786        gfxWarningOnce() << "glXWaitVideoSync returned " << status;
    787        useSoftware = true;
    788      }
    789 
    790      if (syncCounter == (nextSync - 1)) {
    791        gfxWarningOnce()
    792            << "glXWaitVideoSync failed to increment the sync counter.";
    793        useSoftware = true;
    794      }
    795 
    796      if (useSoftware) {
    797        double remaining =
    798            (1000.f / 60.f) - (TimeStamp::Now() - lastVsync).ToMilliseconds();
    799        if (remaining > 0) {
    800          AUTO_PROFILER_THREAD_SLEEP;
    801          PlatformThread::Sleep((int)remaining);
    802        }
    803      }
    804 
    805      lastVsync = TimeStamp::Now();
    806      TimeStamp outputTime = lastVsync + GetVsyncRate();
    807      NotifyVsync(lastVsync, outputTime);
    808    }
    809  }
    810 
    811  void Cleanup() {
    812    MOZ_ASSERT(!NS_IsMainThread());
    813 
    814    mGLContext = nullptr;
    815    if (mXDisplay) XCloseDisplay(mXDisplay);
    816  }
    817 
    818  // Owned by the vsync thread.
    819  RefPtr<gl::GLContextGLX> mGLContext;
    820  _XDisplay* mXDisplay;
    821  Monitor mSetupLock MOZ_UNANNOTATED;
    822  base::Thread mVsyncThread;
    823  RefPtr<Runnable> mVsyncTask;
    824  Monitor mVsyncEnabledLock MOZ_UNANNOTATED;
    825  bool mVsyncEnabled;
    826 };
    827 
    828 class XrandrSoftwareVsyncSource final
    829    : public mozilla::gfx::SoftwareVsyncSource {
    830 public:
    831  XrandrSoftwareVsyncSource() : SoftwareVsyncSource(ComputeVsyncRate()) {
    832    MOZ_ASSERT(NS_IsMainThread());
    833 
    834    GdkScreen* defaultScreen = gdk_screen_get_default();
    835    g_signal_connect(defaultScreen, "monitors-changed",
    836                     G_CALLBACK(monitors_changed), this);
    837  }
    838 
    839 private:
    840  // Request the current refresh rate via xrandr. It is hard to find the
    841  // "correct" one, thus choose the highest one, assuming this will usually
    842  // give the best user experience.
    843  static mozilla::TimeDuration ComputeVsyncRate() {
    844    struct _XDisplay* dpy = gdk_x11_get_default_xdisplay();
    845 
    846    // Use the default software refresh rate as lower bound. Allowing lower
    847    // rates makes a bunch of tests start to fail on CI. The main goal of this
    848    // VsyncSource is to support refresh rates greater than the default one.
    849    double highestRefreshRate = gfxPlatform::GetSoftwareVsyncRate();
    850 
    851    // When running on remote X11 the xrandr version may be stuck on an
    852    // ancient version. There are still setups using remote X11 out there, so
    853    // make sure we don't crash.
    854    int eventBase, errorBase, major, minor;
    855    if (XRRQueryExtension(dpy, &eventBase, &errorBase) &&
    856        XRRQueryVersion(dpy, &major, &minor) &&
    857        (major > 1 || (major == 1 && minor >= 3))) {
    858      Window root = gdk_x11_get_default_root_xwindow();
    859      XRRScreenResources* res = XRRGetScreenResourcesCurrent(dpy, root);
    860 
    861      if (res) {
    862        // We can't use refresh rates far below the default one (60Hz) because
    863        // otherwise random CI tests start to fail. However, many users have
    864        // screens just below the default rate, e.g. 59.95Hz. So slightly
    865        // decrease the lower bound.
    866        highestRefreshRate -= 1.0;
    867 
    868        for (int i = 0; i < res->noutput; i++) {
    869          XRROutputInfo* outputInfo =
    870              XRRGetOutputInfo(dpy, res, res->outputs[i]);
    871          if (outputInfo) {
    872            if (outputInfo->crtc) {
    873              XRRCrtcInfo* crtcInfo =
    874                  XRRGetCrtcInfo(dpy, res, outputInfo->crtc);
    875              if (crtcInfo) {
    876                for (int j = 0; j < res->nmode; j++) {
    877                  if (res->modes[j].id == crtcInfo->mode) {
    878                    double refreshRate = mode_refresh(&res->modes[j]);
    879                    if (refreshRate > highestRefreshRate) {
    880                      highestRefreshRate = refreshRate;
    881                    }
    882                    break;
    883                  }
    884                }
    885 
    886                XRRFreeCrtcInfo(crtcInfo);
    887              }
    888            }
    889 
    890            XRRFreeOutputInfo(outputInfo);
    891          }
    892        }
    893      }
    894      XRRFreeScreenResources(res);
    895    }
    896 
    897    const double rate = 1000.0 / highestRefreshRate;
    898    return mozilla::TimeDuration::FromMilliseconds(rate);
    899  }
    900 
    901  static void monitors_changed(GdkScreen* aScreen, gpointer aClosure) {
    902    XrandrSoftwareVsyncSource* self =
    903        static_cast<XrandrSoftwareVsyncSource*>(aClosure);
    904    self->SetVsyncRate(ComputeVsyncRate());
    905  }
    906 
    907  // from xrandr.c
    908  static double mode_refresh(const XRRModeInfo* mode_info) {
    909    double rate;
    910    double vTotal = mode_info->vTotal;
    911 
    912    if (mode_info->modeFlags & RR_DoubleScan) {
    913      /* doublescan doubles the number of lines */
    914      vTotal *= 2;
    915    }
    916 
    917    if (mode_info->modeFlags & RR_Interlace) {
    918      /* interlace splits the frame into two fields */
    919      /* the field rate is what is typically reported by monitors */
    920      vTotal /= 2;
    921    }
    922 
    923    if (mode_info->hTotal && vTotal) {
    924      rate = ((double)mode_info->dotClock /
    925              ((double)mode_info->hTotal * (double)vTotal));
    926    } else {
    927      rate = 0;
    928    }
    929    return rate;
    930  }
    931 };
    932 #endif
    933 
    934 already_AddRefed<gfx::VsyncSource>
    935 gfxPlatformGtk::CreateGlobalHardwareVsyncSource() {
    936 #ifdef MOZ_X11
    937  if (IsHeadless() || IsWaylandDisplay()) {
    938    // On Wayland we can not create a global hardware based vsync source, thus
    939    // use a software based one here. We create window specific ones later.
    940    return GetSoftwareVsyncSource();
    941  }
    942 
    943  nsCOMPtr<nsIGfxInfo> gfxInfo = components::GfxInfo::Service();
    944  nsString windowProtocol;
    945  gfxInfo->GetWindowProtocol(windowProtocol);
    946  bool isXwayland = windowProtocol.Find(u"xwayland") != -1;
    947  nsString adapterDriverVendor;
    948  gfxInfo->GetAdapterDriverVendor(adapterDriverVendor);
    949  bool isMesa = adapterDriverVendor.Find(u"mesa") != -1;
    950 
    951  // Only use GLX vsync when the OpenGL compositor / WebRender is being used.
    952  // The extra cost of initializing a GLX context while blocking the main thread
    953  // is not worth it when using basic composition. Do not use it on Xwayland, as
    954  // Xwayland will give us a software timer as we are listening for the root
    955  // window, which does not have a Wayland equivalent. Don't call
    956  // gl::sGLXLibrary.SupportsVideoSync() when EGL is used as NVIDIA drivers
    957  // refuse to use EGL GL context when GLX was initialized first and fail
    958  // silently.
    959  if (StaticPrefs::gfx_x11_glx_sgi_video_sync_AtStartup() &&
    960      gfxConfig::IsEnabled(Feature::HW_COMPOSITING) && !isXwayland &&
    961      (!gfxVars::UseEGL() || isMesa) &&
    962      gl::sGLXLibrary.SupportsVideoSync(DefaultXDisplay())) {
    963    RefPtr<GtkVsyncSource> vsyncSource = new GtkVsyncSource();
    964    if (!vsyncSource->Setup()) {
    965      NS_WARNING("Failed to setup GLContext, falling back to software vsync.");
    966      return GetSoftwareVsyncSource();
    967    }
    968    return vsyncSource.forget();
    969  }
    970 
    971  RefPtr<VsyncSource> softwareVsync = new XrandrSoftwareVsyncSource();
    972  return softwareVsync.forget();
    973 #else
    974  return GetSoftwareVsyncSource();
    975 #endif
    976 }
    977 
    978 void gfxPlatformGtk::BuildContentDeviceData(ContentDeviceData* aOut) {
    979  gfxPlatform::BuildContentDeviceData(aOut);
    980 
    981  aOut->cmsOutputProfileData() = GetPlatformCMSOutputProfileData();
    982 }
    983 
    984 // Wrapper for third party code (WebRTC for instance) where
    985 // gfxVars can't be included.
    986 namespace mozilla::gfx {
    987 bool IsDMABufEnabled() { return gfxVars::UseDMABuf(); }
    988 }  // namespace mozilla::gfx