tor-browser

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

gfxAndroidPlatform.cpp (14259B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "base/basictypes.h"
      8 
      9 #include "gfxAndroidPlatform.h"
     10 #include "mozilla/gfx/2D.h"
     11 #include "mozilla/gfx/gfxVars.h"
     12 #include "mozilla/CountingAllocatorBase.h"
     13 #include "mozilla/intl/LocaleService.h"
     14 #include "mozilla/intl/OSPreferences.h"
     15 #include "mozilla/java/GeckoAppShellWrappers.h"
     16 #include "mozilla/java/HardwareCodecCapabilityUtilsWrappers.h"
     17 #include "mozilla/jni/Utils.h"
     18 #include "mozilla/layers/AndroidHardwareBuffer.h"
     19 #include "mozilla/Preferences.h"
     20 #include "mozilla/StaticPrefs_gfx.h"
     21 #include "mozilla/StaticPrefs_webgl.h"
     22 #include "mozilla/widget/AndroidVsync.h"
     23 
     24 #include "AndroidBuild.h"
     25 #include "AndroidSystemFontIterator.h"
     26 #include "GeckoProfiler.h"
     27 #include "gfx2DGlue.h"
     28 #include "gfxFT2FontList.h"
     29 #include "gfxImageSurface.h"
     30 #include "gfxTextRun.h"
     31 #include "nsXULAppAPI.h"
     32 #include "nsIScreen.h"
     33 #include "nsServiceManagerUtils.h"
     34 #include "nsUnicodeProperties.h"
     35 #include "cairo.h"
     36 #include "VsyncSource.h"
     37 
     38 #include "ft2build.h"
     39 #include FT_FREETYPE_H
     40 #include FT_MODULE_H
     41 
     42 using namespace mozilla;
     43 using namespace mozilla::dom;
     44 using namespace mozilla::gfx;
     45 using namespace mozilla::unicode;
     46 using mozilla::intl::LocaleService;
     47 using mozilla::intl::OSPreferences;
     48 
     49 static FT_Library gPlatformFTLibrary = nullptr;
     50 
     51 class FreetypeReporter final : public nsIMemoryReporter,
     52                               public CountingAllocatorBase<FreetypeReporter> {
     53 private:
     54  ~FreetypeReporter() {}
     55 
     56 public:
     57  NS_DECL_ISUPPORTS
     58 
     59  static void* Malloc(FT_Memory, long size) { return CountingMalloc(size); }
     60 
     61  static void Free(FT_Memory, void* p) { return CountingFree(p); }
     62 
     63  static void* Realloc(FT_Memory, long cur_size, long new_size, void* p) {
     64    return CountingRealloc(p, new_size);
     65  }
     66 
     67  NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
     68                            nsISupports* aData, bool aAnonymize) override {
     69    MOZ_COLLECT_REPORT("explicit/freetype", KIND_HEAP, UNITS_BYTES,
     70                       MemoryAllocated(), "Memory used by Freetype.");
     71 
     72    return NS_OK;
     73  }
     74 };
     75 
     76 NS_IMPL_ISUPPORTS(FreetypeReporter, nsIMemoryReporter)
     77 
     78 static FT_MemoryRec_ sFreetypeMemoryRecord;
     79 
     80 PRThread* gfxAndroidPlatform::sFontAPIInitializeThread = nullptr;
     81 constinit nsCString gfxAndroidPlatform::sManufacturer;
     82 
     83 // static
     84 bool gfxAndroidPlatform::IsFontAPIDisabled(bool aDontCheckPref) {
     85  if (!aDontCheckPref &&
     86      StaticPrefs::gfx_font_list_use_font_match_api_force_enabled_AtStartup()) {
     87    return false;
     88  }
     89 
     90  // Some manufacturer devices seem to crash when using font match API
     91  // (Bug 1787551 / Bug 1990734).
     92 
     93  if (sManufacturer.IsEmpty()) {
     94    sManufacturer = java::sdk::Build::MANUFACTURER()->ToCString();
     95  }
     96  return (sManufacturer.EqualsLiteral("OPPO") ||
     97          sManufacturer.EqualsLiteral("realme") ||
     98          sManufacturer.EqualsLiteral("OnePlus") ||
     99          sManufacturer.EqualsLiteral("HONOR"));
    100 }
    101 
    102 // static
    103 void gfxAndroidPlatform::InitializeFontAPI() {
    104  if (XRE_GetProcessType() != GeckoProcessType_Default) {
    105    return;
    106  }
    107 
    108  // Android 12+ doesn't read font configuration XML files directly.
    109  // It means that it is slow to call font API at first.
    110  if (jni::GetAPIVersion() < 31) {
    111    return;
    112  }
    113 
    114  // This will be called before XPCOM service isn't started. So don't check
    115  // preferences.
    116  if (IsFontAPIDisabled(true)) {
    117    return;
    118  }
    119 
    120  if (__builtin_available(android 29, *)) {
    121    auto FontAPIInitializeCallback = [](void* aUnused) {
    122      AUTO_PROFILER_REGISTER_THREAD("InitializingFontAPI");
    123      PR_SetCurrentThreadName("InitializingFontAPI");
    124      AndroidSystemFontIterator::Preload();
    125    };
    126    sFontAPIInitializeThread = PR_CreateThread(
    127        PR_USER_THREAD, FontAPIInitializeCallback, nullptr, PR_PRIORITY_NORMAL,
    128        PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
    129  }
    130 }
    131 
    132 // static
    133 void gfxAndroidPlatform::WaitForInitializeFontAPI() {
    134  if (sFontAPIInitializeThread) {
    135    PR_JoinThread(sFontAPIInitializeThread);
    136    sFontAPIInitializeThread = nullptr;
    137  }
    138 }
    139 
    140 // static
    141 bool gfxAndroidPlatform::IsHwCodecSupported(media::MediaCodec aCodec,
    142                                            bool aEncoder) {
    143  switch (aCodec) {
    144    case media::MediaCodec::H264:
    145      return java::HardwareCodecCapabilityUtils::HasHWH264(aEncoder);
    146    case media::MediaCodec::VP8:
    147      return java::HardwareCodecCapabilityUtils::HasHWVP8(aEncoder);
    148    case media::MediaCodec::VP9:
    149      return java::HardwareCodecCapabilityUtils::HasHWVP9(aEncoder);
    150    case media::MediaCodec::AV1:
    151      return java::HardwareCodecCapabilityUtils::HasHWAV1(aEncoder);
    152    case media::MediaCodec::HEVC:
    153      return java::HardwareCodecCapabilityUtils::HasHWHEVC(aEncoder);
    154    default:
    155      return false;
    156  }
    157 }
    158 
    159 gfxAndroidPlatform::gfxAndroidPlatform() {
    160  // A custom allocator.  It counts allocations, enabling memory reporting.
    161  sFreetypeMemoryRecord.user = nullptr;
    162  sFreetypeMemoryRecord.alloc = FreetypeReporter::Malloc;
    163  sFreetypeMemoryRecord.free = FreetypeReporter::Free;
    164  sFreetypeMemoryRecord.realloc = FreetypeReporter::Realloc;
    165 
    166  // These two calls are equivalent to FT_Init_FreeType(), but allow us to
    167  // provide a custom memory allocator.
    168  FT_New_Library(&sFreetypeMemoryRecord, &gPlatformFTLibrary);
    169  FT_Add_Default_Modules(gPlatformFTLibrary);
    170 
    171  Factory::SetFTLibrary(gPlatformFTLibrary);
    172 
    173  RegisterStrongMemoryReporter(new FreetypeReporter());
    174 
    175  // Bug 1886573: At this point, we don't yet have primary screen depth.
    176  // This setting of screen depth to 0 is preserving existing behavior,
    177  // and should be fixed.
    178  int32_t screenDepth = 0;
    179  mOffscreenFormat = screenDepth == 16 ? SurfaceFormat::R5G6B5_UINT16
    180                                       : SurfaceFormat::X8R8G8B8_UINT32;
    181 }
    182 
    183 gfxAndroidPlatform::~gfxAndroidPlatform() {
    184  FT_Done_Library(gPlatformFTLibrary);
    185  gPlatformFTLibrary = nullptr;
    186  layers::AndroidHardwareBufferManager::Shutdown();
    187  layers::AndroidHardwareBufferApi::Shutdown();
    188 }
    189 
    190 void gfxAndroidPlatform::InitAcceleration() { gfxPlatform::InitAcceleration(); }
    191 
    192 already_AddRefed<gfxASurface> gfxAndroidPlatform::CreateOffscreenSurface(
    193    const IntSize& aSize, gfxImageFormat aFormat) {
    194  if (!Factory::AllowedSurfaceSize(aSize)) {
    195    return nullptr;
    196  }
    197 
    198  RefPtr<gfxASurface> newSurface;
    199  newSurface = new gfxImageSurface(aSize, aFormat);
    200 
    201  return newSurface.forget();
    202 }
    203 
    204 static bool IsJapaneseLocale() {
    205  static bool sInitialized = false;
    206  static bool sIsJapanese = false;
    207 
    208  if (!sInitialized) {
    209    sInitialized = true;
    210 
    211    nsAutoCString appLocale;
    212    LocaleService::GetInstance()->GetAppLocaleAsBCP47(appLocale);
    213 
    214    const nsDependentCSubstring lang(appLocale, 0, 2);
    215    if (lang.EqualsLiteral("ja")) {
    216      sIsJapanese = true;
    217    } else {
    218      OSPreferences::GetInstance()->GetSystemLocale(appLocale);
    219 
    220      const nsDependentCSubstring lang(appLocale, 0, 2);
    221      if (lang.EqualsLiteral("ja")) {
    222        sIsJapanese = true;
    223      }
    224    }
    225  }
    226 
    227  return sIsJapanese;
    228 }
    229 
    230 void gfxAndroidPlatform::GetCommonFallbackFonts(
    231    uint32_t aCh, Script aRunScript, FontPresentation aPresentation,
    232    nsTArray<const char*>& aFontList) {
    233  static const char kDroidSansJapanese[] = "Droid Sans Japanese";
    234  static const char kMotoyaLMaru[] = "MotoyaLMaru";
    235  static const char kNotoSansCJKJP[] = "Noto Sans CJK JP";
    236  static const char kNotoColorEmoji[] = "Noto Color Emoji";
    237 
    238  if (PrefersColor(aPresentation)) {
    239    aFontList.AppendElement(kNotoColorEmoji);
    240  }
    241 
    242  if (IS_IN_BMP(aCh)) {
    243    // try language-specific "Droid Sans *" and "Noto Sans *" fonts for
    244    // certain blocks, as most devices probably have these
    245    uint8_t block = (aCh >> 8) & 0xff;
    246    switch (block) {
    247      case 0x05:
    248        aFontList.AppendElement("Noto Sans Hebrew");
    249        aFontList.AppendElement("Droid Sans Hebrew");
    250        aFontList.AppendElement("Noto Sans Armenian");
    251        aFontList.AppendElement("Droid Sans Armenian");
    252        break;
    253      case 0x06:
    254        aFontList.AppendElement("Noto Sans Arabic");
    255        aFontList.AppendElement("Droid Sans Arabic");
    256        break;
    257      case 0x09:
    258        aFontList.AppendElement("Noto Sans Devanagari");
    259        aFontList.AppendElement("Noto Sans Bengali");
    260        aFontList.AppendElement("Droid Sans Devanagari");
    261        break;
    262      case 0x0a:
    263        aFontList.AppendElement("Noto Sans Gurmukhi");
    264        aFontList.AppendElement("Noto Sans Gujarati");
    265        break;
    266      case 0x0b:
    267        aFontList.AppendElement("Noto Sans Tamil");
    268        aFontList.AppendElement("Noto Sans Oriya");
    269        aFontList.AppendElement("Droid Sans Tamil");
    270        break;
    271      case 0x0c:
    272        aFontList.AppendElement("Noto Sans Telugu");
    273        aFontList.AppendElement("Noto Sans Kannada");
    274        break;
    275      case 0x0d:
    276        aFontList.AppendElement("Noto Sans Malayalam");
    277        aFontList.AppendElement("Noto Sans Sinhala");
    278        break;
    279      case 0x0e:
    280        aFontList.AppendElement("Noto Sans Thai");
    281        aFontList.AppendElement("Noto Sans Lao");
    282        aFontList.AppendElement("Droid Sans Thai");
    283        break;
    284      case 0x0f:
    285        aFontList.AppendElement("Noto Sans Tibetan");
    286        break;
    287      case 0x10:
    288      case 0x2d:
    289        aFontList.AppendElement("Noto Sans Georgian");
    290        aFontList.AppendElement("Droid Sans Georgian");
    291        break;
    292      case 0x12:
    293      case 0x13:
    294        aFontList.AppendElement("Noto Sans Ethiopic");
    295        aFontList.AppendElement("Droid Sans Ethiopic");
    296        break;
    297      case 0x21:
    298      case 0x23:
    299      case 0x24:
    300      case 0x26:
    301      case 0x27:
    302      case 0x29:
    303        aFontList.AppendElement("Noto Sans Symbols");
    304        break;
    305      case 0xf9:
    306      case 0xfa:
    307        if (IsJapaneseLocale()) {
    308          aFontList.AppendElement(kMotoyaLMaru);
    309          aFontList.AppendElement(kNotoSansCJKJP);
    310          aFontList.AppendElement(kDroidSansJapanese);
    311        }
    312        break;
    313      default:
    314        if (block >= 0x2e && block <= 0x9f && IsJapaneseLocale()) {
    315          aFontList.AppendElement(kMotoyaLMaru);
    316          aFontList.AppendElement(kNotoSansCJKJP);
    317          aFontList.AppendElement(kDroidSansJapanese);
    318        }
    319        break;
    320    }
    321  }
    322  // and try Droid Sans Fallback as a last resort
    323  aFontList.AppendElement("Droid Sans Fallback");
    324 }
    325 
    326 bool gfxAndroidPlatform::CreatePlatformFontList() {
    327  return gfxPlatformFontList::Initialize(new gfxFT2FontList);
    328 }
    329 
    330 void gfxAndroidPlatform::ReadSystemFontList(
    331    mozilla::dom::SystemFontList* aFontList) {
    332  gfxFT2FontList::PlatformFontList()->ReadSystemFontList(aFontList);
    333 }
    334 
    335 bool gfxAndroidPlatform::FontHintingEnabled() {
    336  // In "mobile" builds, we sometimes use non-reflow-zoom, so we
    337  // might not want hinting.  Let's see.
    338 
    339 #ifdef MOZ_WIDGET_ANDROID
    340  // On Android, we currently only use gecko to render web
    341  // content that can always be be non-reflow-zoomed.  So turn off
    342  // hinting.
    343  //
    344  // XXX when gecko-android-java is used as an "app runtime", we may
    345  // want to re-enable hinting for non-browser processes there.
    346  //
    347  // If this value is changed, we must ensure that the argument passed
    348  // to SkInitCairoFT() in GPUParent::RecvInit() is also updated.
    349  return false;
    350 #endif  //  MOZ_WIDGET_ANDROID
    351 
    352  // Currently, we don't have any other targets, but if/when we do,
    353  // decide how to handle them here.
    354 
    355  MOZ_ASSERT_UNREACHABLE("oops, what platform is this?");
    356  return gfxPlatform::FontHintingEnabled();
    357 }
    358 
    359 bool gfxAndroidPlatform::RequiresLinearZoom() {
    360 #ifdef MOZ_WIDGET_ANDROID
    361  // On Android, we currently only use gecko to render web
    362  // content that can always be be non-reflow-zoomed.
    363  //
    364  // XXX when gecko-android-java is used as an "app runtime", we may
    365  // want to use linear zoom only for the web browser process, not other apps.
    366  return true;
    367 #endif
    368 
    369  MOZ_ASSERT_UNREACHABLE("oops, what platform is this?");
    370  return gfxPlatform::RequiresLinearZoom();
    371 }
    372 
    373 class AndroidVsyncSource final : public VsyncSource,
    374                                 public widget::AndroidVsync::Observer {
    375 public:
    376  AndroidVsyncSource() : mAndroidVsync(widget::AndroidVsync::GetInstance()) {}
    377 
    378  bool IsVsyncEnabled() override {
    379    MOZ_ASSERT(NS_IsMainThread());
    380    return mObservingVsync;
    381  }
    382 
    383  void EnableVsync() override {
    384    MOZ_ASSERT(NS_IsMainThread());
    385 
    386    if (mObservingVsync) {
    387      return;
    388    }
    389 
    390    float fps = java::GeckoAppShell::GetScreenRefreshRate();
    391    MOZ_ASSERT(fps > 0.0f);
    392    mVsyncRate = TimeDuration::FromMilliseconds(1000.0 / fps);
    393    mAndroidVsync->RegisterObserver(this, widget::AndroidVsync::RENDER);
    394    mObservingVsync = true;
    395  }
    396 
    397  void DisableVsync() override {
    398    MOZ_ASSERT(NS_IsMainThread());
    399 
    400    if (!mObservingVsync) {
    401      return;
    402    }
    403    mAndroidVsync->UnregisterObserver(this, widget::AndroidVsync::RENDER);
    404    mObservingVsync = false;
    405  }
    406 
    407  TimeDuration GetVsyncRate() override { return mVsyncRate; }
    408 
    409  void Shutdown() override { DisableVsync(); }
    410 
    411  // Override for the widget::AndroidVsync::Observer method
    412  void OnVsync(const TimeStamp& aTimeStamp) override {
    413    // Use the timebase from the frame callback as the vsync time, unless it
    414    // is in the future.
    415    TimeStamp now = TimeStamp::Now();
    416    TimeStamp vsyncTime = aTimeStamp < now ? aTimeStamp : now;
    417    TimeStamp outputTime = vsyncTime + GetVsyncRate();
    418 
    419    NotifyVsync(vsyncTime, outputTime);
    420  }
    421 
    422  void OnMaybeUpdateRefreshRate() override {
    423    NS_DispatchToMainThread(
    424        NS_NewRunnableFunction(__func__, [self = RefPtr{this}]() {
    425          if (!self->mObservingVsync) {
    426            return;
    427          }
    428          self->DisableVsync();
    429          self->EnableVsync();
    430        }));
    431  }
    432 
    433 private:
    434  virtual ~AndroidVsyncSource() { DisableVsync(); }
    435 
    436  RefPtr<widget::AndroidVsync> mAndroidVsync;
    437  TimeDuration mVsyncRate;
    438  bool mObservingVsync = false;
    439 };
    440 
    441 already_AddRefed<mozilla::gfx::VsyncSource>
    442 gfxAndroidPlatform::CreateGlobalHardwareVsyncSource() {
    443  RefPtr<AndroidVsyncSource> vsyncSource = new AndroidVsyncSource();
    444  return vsyncSource.forget();
    445 }