tor-browser

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

nsDeviceContext.cpp (13166B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set sw=2 ts=2 expandtab: */
      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 "nsDeviceContext.h"
      8 #include <algorithm>  // for max
      9 #include "gfxContext.h"
     10 #include "gfxPoint.h"    // for gfxSize
     11 #include "gfxTextRun.h"  // for gfxFontGroup
     12 #include "mozilla/LookAndFeel.h"
     13 #include "mozilla/gfx/PathHelpers.h"
     14 #include "mozilla/gfx/PrintTarget.h"
     15 #include "mozilla/ProfilerMarkers.h"
     16 #include "mozilla/StaticPrefs_layout.h"
     17 #include "mozilla/Try.h"            // for MOZ_TRY
     18 #include "mozilla/widget/Screen.h"  // for Screen
     19 #include "nsDebug.h"                // for NS_ASSERTION, etc
     20 #include "nsFontMetrics.h"          // for nsFontMetrics
     21 #include "nsIDeviceContextSpec.h"   // for nsIDeviceContextSpec
     22 #include "nsIWidget.h"              // for nsIWidget, NS_NATIVE_WINDOW
     23 #include "nsRect.h"                 // for nsRect
     24 #include "nsTArray.h"               // for nsTArray, nsTArray_Impl
     25 #include "mozilla/gfx/Logging.h"
     26 #include "mozilla/widget/ScreenManager.h"  // for ScreenManager
     27 
     28 using namespace mozilla;
     29 using namespace mozilla::gfx;
     30 using mozilla::widget::ScreenManager;
     31 
     32 nsDeviceContext::nsDeviceContext()
     33    : mWidth(0),
     34      mHeight(0),
     35      mAppUnitsPerDevPixel(-1),
     36      mAppUnitsPerDevPixelAtUnitFullZoom(-1),
     37      mAppUnitsPerPhysicalInch(-1),
     38      mFullZoom(1.0f),
     39      mPrintingScale(1.0f),
     40      mPrintingTranslate(gfxPoint(0, 0)),
     41      mIsCurrentlyPrintingDoc(false) {
     42  MOZ_ASSERT(NS_IsMainThread(), "nsDeviceContext created off main thread");
     43 }
     44 
     45 nsDeviceContext::~nsDeviceContext() = default;
     46 
     47 int32_t nsDeviceContext::ComputeAppUnitsPerDevPixelForWidgetScale(
     48    CSSToLayoutDeviceScale aScale) {
     49  return std::max(1, NS_lround(AppUnitsPerCSSPixel() / aScale.scale));
     50 }
     51 
     52 int32_t nsDeviceContext::ApplyFullZoomToAPD(int32_t aUnzoomedAppUnits,
     53                                            float aFullZoom) {
     54  if (aFullZoom == 1.0f) {
     55    return aUnzoomedAppUnits;
     56  }
     57  return std::max(1, NSToIntRound(float(aUnzoomedAppUnits) / aFullZoom));
     58 }
     59 
     60 void nsDeviceContext::SetDPI() {
     61  float dpi;
     62 
     63  // Use the printing DC to determine DPI values, if we have one.
     64  if (mDeviceContextSpec) {
     65    dpi = mDeviceContextSpec->GetDPI();
     66    mPrintingScale = mDeviceContextSpec->GetPrintingScale();
     67    mPrintingTranslate = mDeviceContextSpec->GetPrintingTranslate();
     68    mAppUnitsPerDevPixelAtUnitFullZoom =
     69        ComputeAppUnitsPerDevPixelForWidgetScale(
     70            CSSToLayoutDeviceScale(dpi / 96.0));
     71  } else {
     72    // A value of -1 means use the maximum of 96 and the system DPI.
     73    // A value of 0 means use the system DPI. A positive value is used as the
     74    // DPI. This sets the physical size of a device pixel and thus controls the
     75    // interpretation of physical units.
     76    int32_t prefDPI = StaticPrefs::layout_css_dpi();
     77    if (prefDPI > 0) {
     78      dpi = prefDPI;
     79    } else if (mWidget) {
     80      dpi = mWidget->GetDPI();
     81      MOZ_ASSERT(dpi > 0);
     82      if (prefDPI < 0) {
     83        dpi = std::max(96.0f, dpi);
     84      }
     85    } else {
     86      dpi = 96.0f;
     87    }
     88 
     89    CSSToLayoutDeviceScale scale =
     90        mWidget ? mWidget->GetDefaultScale() : CSSToLayoutDeviceScale(1.0);
     91    MOZ_ASSERT(scale.scale > 0.0);
     92    mAppUnitsPerDevPixelAtUnitFullZoom =
     93        ComputeAppUnitsPerDevPixelForWidgetScale(scale);
     94  }
     95 
     96  NS_ASSERTION(dpi != -1.0, "no dpi set");
     97 
     98  mAppUnitsPerPhysicalInch =
     99      NS_lround(dpi * mAppUnitsPerDevPixelAtUnitFullZoom);
    100  UpdateAppUnitsForFullZoom();
    101 }
    102 
    103 void nsDeviceContext::Init(nsIWidget* aWidget) {
    104  if (mIsInitialized && mWidget == aWidget) {
    105    return;
    106  }
    107 
    108  // We can't assert |!mIsInitialized| here since EndSwapDocShellsForDocument
    109  // re-initializes nsDeviceContext objects.  We can only assert in
    110  // InitForPrinting (below).
    111  mIsInitialized = true;
    112 
    113  mWidget = aWidget;
    114  SetDPI();
    115 }
    116 
    117 // XXX This is only for printing. We should make that obvious in the name.
    118 UniquePtr<gfxContext> nsDeviceContext::CreateRenderingContext() {
    119  return CreateRenderingContextCommon(/* not a reference context */ false);
    120 }
    121 
    122 UniquePtr<gfxContext> nsDeviceContext::CreateReferenceRenderingContext() {
    123  return CreateRenderingContextCommon(/* a reference context */ true);
    124 }
    125 
    126 UniquePtr<gfxContext> nsDeviceContext::CreateRenderingContextCommon(
    127    bool aWantReferenceContext) {
    128  MOZ_ASSERT(IsPrinterContext());
    129  MOZ_ASSERT(mWidth > 0 && mHeight > 0);
    130 
    131  if (NS_WARN_IF(!mPrintTarget)) {
    132    // Printing canceled already.
    133    return nullptr;
    134  }
    135 
    136  RefPtr<gfx::DrawTarget> dt;
    137  if (aWantReferenceContext) {
    138    dt = mPrintTarget->GetReferenceDrawTarget();
    139  } else {
    140    // This will be null if printing a page from the parent process.
    141    RefPtr<DrawEventRecorder> recorder;
    142    mDeviceContextSpec->GetDrawEventRecorder(getter_AddRefs(recorder));
    143    dt = mPrintTarget->MakeDrawTarget(gfx::IntSize(mWidth, mHeight), recorder);
    144  }
    145 
    146  if (!dt || !dt->IsValid()) {
    147    gfxCriticalNote << "Failed to create draw target in device context sized "
    148                    << mWidth << "x" << mHeight << " and pointer "
    149                    << hexa(mPrintTarget);
    150    return nullptr;
    151  }
    152 
    153  dt->AddUserData(&sDisablePixelSnapping, (void*)0x1, nullptr);
    154 
    155  auto pContext = MakeUnique<gfxContext>(dt);
    156 
    157  gfxMatrix transform;
    158  transform.PreTranslate(mPrintingTranslate);
    159  transform.PreScale(mPrintingScale, mPrintingScale);
    160  pContext->SetMatrixDouble(transform);
    161  return pContext;
    162 }
    163 
    164 uint32_t nsDeviceContext::GetDepth() {
    165  RefPtr<widget::Screen> screen = FindScreen();
    166  int32_t depth = 0;
    167  screen->GetColorDepth(&depth);
    168  return uint32_t(depth);
    169 }
    170 
    171 dom::ScreenColorGamut nsDeviceContext::GetColorGamut() {
    172  RefPtr<widget::Screen> screen = FindScreen();
    173  dom::ScreenColorGamut colorGamut;
    174  screen->GetColorGamut(&colorGamut);
    175  return colorGamut;
    176 }
    177 
    178 hal::ScreenOrientation nsDeviceContext::GetScreenOrientationType() {
    179  RefPtr<widget::Screen> screen = FindScreen();
    180  return screen->GetOrientationType();
    181 }
    182 
    183 uint16_t nsDeviceContext::GetScreenOrientationAngle() {
    184  RefPtr<widget::Screen> screen = FindScreen();
    185  return screen->GetOrientationAngle();
    186 }
    187 
    188 bool nsDeviceContext::GetScreenIsHDR() {
    189  RefPtr<widget::Screen> screen = FindScreen();
    190  return screen->GetIsHDR();
    191 }
    192 
    193 nsSize nsDeviceContext::GetDeviceSurfaceDimensions() {
    194  return GetRect().Size();
    195 }
    196 
    197 nsRect nsDeviceContext::GetRect() {
    198  if (IsPrinterContext()) {
    199    return {0, 0, mWidth, mHeight};
    200  }
    201  RefPtr<widget::Screen> screen = FindScreen();
    202  return LayoutDeviceIntRect::ToAppUnits(screen->GetRect(),
    203                                         AppUnitsPerDevPixel());
    204 }
    205 
    206 nsRect nsDeviceContext::GetClientRect() {
    207  if (IsPrinterContext()) {
    208    return {0, 0, mWidth, mHeight};
    209  }
    210  RefPtr<widget::Screen> screen = FindScreen();
    211  return LayoutDeviceIntRect::ToAppUnits(screen->GetAvailRect(),
    212                                         AppUnitsPerDevPixel());
    213 }
    214 
    215 nsresult nsDeviceContext::InitForPrinting(nsIDeviceContextSpec* aDevice) {
    216  NS_ENSURE_ARG_POINTER(aDevice);
    217 
    218  MOZ_ASSERT(!mIsInitialized,
    219             "Only initialize once, immediately after construction");
    220 
    221  // We don't set mIsInitialized here. The Init() call below does that.
    222 
    223  mPrintTarget = aDevice->MakePrintTarget();
    224  if (!mPrintTarget) {
    225    return NS_ERROR_FAILURE;
    226  }
    227 
    228  mDeviceContextSpec = aDevice;
    229 
    230  Init(nullptr);
    231 
    232  if (!CalcPrintingSize()) {
    233    return NS_ERROR_FAILURE;
    234  }
    235 
    236  return NS_OK;
    237 }
    238 
    239 nsresult nsDeviceContext::BeginDocument(const nsAString& aTitle,
    240                                        const nsAString& aPrintToFileName,
    241                                        int32_t aStartPage, int32_t aEndPage) {
    242  MOZ_DIAGNOSTIC_ASSERT(!mIsCurrentlyPrintingDoc,
    243                        "Mismatched BeginDocument/EndDocument calls");
    244  AUTO_PROFILER_MARKER_TEXT("DeviceContext Printing", LAYOUT_Printing, {},
    245                            "nsDeviceContext::BeginDocument"_ns);
    246 
    247  nsresult rv = mPrintTarget->BeginPrinting(aTitle, aPrintToFileName,
    248                                            aStartPage, aEndPage);
    249 
    250  if (NS_SUCCEEDED(rv)) {
    251    if (mDeviceContextSpec) {
    252      rv = mDeviceContextSpec->BeginDocument(aTitle, aPrintToFileName,
    253                                             aStartPage, aEndPage);
    254    }
    255    mIsCurrentlyPrintingDoc = true;
    256  }
    257 
    258  // Warn about any failure (except user cancelling):
    259  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv) || rv == NS_ERROR_ABORT,
    260                       "nsDeviceContext::BeginDocument failed");
    261 
    262  return rv;
    263 }
    264 
    265 RefPtr<PrintEndDocumentPromise> nsDeviceContext::EndDocument() {
    266  MOZ_DIAGNOSTIC_ASSERT(mIsCurrentlyPrintingDoc,
    267                        "Mismatched BeginDocument/EndDocument calls");
    268  MOZ_DIAGNOSTIC_ASSERT(mPrintTarget);
    269  AUTO_PROFILER_MARKER_TEXT("DeviceContext Printing", LAYOUT_Printing, {},
    270                            "nsDeviceContext::EndDocument"_ns);
    271 
    272  mIsCurrentlyPrintingDoc = false;
    273 
    274  if (mPrintTarget) {
    275    auto result = mPrintTarget->EndPrinting();
    276    if (NS_FAILED(result)) {
    277      return PrintEndDocumentPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE,
    278                                                      __func__);
    279    }
    280    mPrintTarget->Finish();
    281    mPrintTarget = nullptr;
    282  }
    283 
    284  if (mDeviceContextSpec) {
    285    return mDeviceContextSpec->EndDocument();
    286  }
    287 
    288  return PrintEndDocumentPromise::CreateAndResolve(true, __func__);
    289 }
    290 
    291 nsresult nsDeviceContext::AbortDocument() {
    292  MOZ_DIAGNOSTIC_ASSERT(mIsCurrentlyPrintingDoc,
    293                        "Mismatched BeginDocument/EndDocument calls");
    294  AUTO_PROFILER_MARKER_TEXT("DeviceContext Printing", LAYOUT_Printing, {},
    295                            "nsDeviceContext::AbortDocument"_ns);
    296 
    297  nsresult rv = mPrintTarget->AbortPrinting();
    298  mIsCurrentlyPrintingDoc = false;
    299 
    300  if (mDeviceContextSpec) {
    301    (void)mDeviceContextSpec->EndDocument();
    302  }
    303 
    304  mPrintTarget = nullptr;
    305 
    306  return rv;
    307 }
    308 
    309 nsresult nsDeviceContext::BeginPage(const IntSize& aSizeInPoints) {
    310  MOZ_DIAGNOSTIC_ASSERT(!mIsCurrentlyPrintingDoc || mPrintTarget,
    311                        "What nulled out our print target while printing?");
    312  AUTO_PROFILER_MARKER_TEXT("DeviceContext Printing", LAYOUT_Printing, {},
    313                            "nsDeviceContext::BeginPage"_ns);
    314 
    315  if (mDeviceContextSpec) {
    316    MOZ_TRY(mDeviceContextSpec->BeginPage(aSizeInPoints));
    317  }
    318  if (mPrintTarget) {
    319    MOZ_TRY(mPrintTarget->BeginPage(aSizeInPoints));
    320  }
    321  return NS_OK;
    322 }
    323 
    324 nsresult nsDeviceContext::EndPage() {
    325  MOZ_DIAGNOSTIC_ASSERT(!mIsCurrentlyPrintingDoc || mPrintTarget,
    326                        "What nulled out our print target while printing?");
    327  AUTO_PROFILER_MARKER_TEXT("DeviceContext Printing", LAYOUT_Printing, {},
    328                            "nsDeviceContext::EndPage"_ns);
    329 
    330  if (mPrintTarget) {
    331    MOZ_TRY(mPrintTarget->EndPage());
    332  }
    333  if (mDeviceContextSpec) {
    334    MOZ_TRY(mDeviceContextSpec->EndPage());
    335  }
    336  return NS_OK;
    337 }
    338 
    339 already_AddRefed<widget::Screen> nsDeviceContext::FindScreen() {
    340  if (mWidget) {
    341    CheckDPIChange();
    342    if (RefPtr<widget::Screen> screen = mWidget->GetWidgetScreen()) {
    343      return screen.forget();
    344    }
    345  }
    346  return ScreenManager::GetSingleton().GetPrimaryScreen();
    347 }
    348 
    349 bool nsDeviceContext::CalcPrintingSize() {
    350  gfxSize size(mPrintTarget->GetSize());
    351  // For printing, CSS inches and physical inches are identical
    352  // so it doesn't matter which we use here
    353  mWidth = NSToCoordRound(size.width * AppUnitsPerPhysicalInch() /
    354                          POINTS_PER_INCH_FLOAT);
    355  mHeight = NSToCoordRound(size.height * AppUnitsPerPhysicalInch() /
    356                           POINTS_PER_INCH_FLOAT);
    357 
    358  return (mWidth > 0 && mHeight > 0);
    359 }
    360 
    361 bool nsDeviceContext::CheckDPIChange() {
    362  int32_t oldDevPixels = mAppUnitsPerDevPixelAtUnitFullZoom;
    363  int32_t oldInches = mAppUnitsPerPhysicalInch;
    364 
    365  SetDPI();
    366 
    367  return oldDevPixels != mAppUnitsPerDevPixelAtUnitFullZoom ||
    368         oldInches != mAppUnitsPerPhysicalInch;
    369 }
    370 
    371 bool nsDeviceContext::SetFullZoom(float aScale) {
    372  if (aScale <= 0) {
    373    MOZ_ASSERT_UNREACHABLE("Invalid full zoom value");
    374    return false;
    375  }
    376  int32_t oldAppUnitsPerDevPixel = mAppUnitsPerDevPixel;
    377  mFullZoom = aScale;
    378  UpdateAppUnitsForFullZoom();
    379  return oldAppUnitsPerDevPixel != mAppUnitsPerDevPixel;
    380 }
    381 
    382 int32_t nsDeviceContext::AppUnitsPerDevPixelInTopLevelChromePage() const {
    383  // The only zoom that applies to chrome pages is the system zoom, if any.
    384  return ApplyFullZoomToAPD(mAppUnitsPerDevPixelAtUnitFullZoom,
    385                            LookAndFeel::SystemZoomSettings().mFullZoom);
    386 }
    387 
    388 void nsDeviceContext::UpdateAppUnitsForFullZoom() {
    389  mAppUnitsPerDevPixel =
    390      ApplyFullZoomToAPD(mAppUnitsPerDevPixelAtUnitFullZoom, mFullZoom);
    391  // adjust mFullZoom to reflect appunit rounding
    392  mFullZoom = float(mAppUnitsPerDevPixelAtUnitFullZoom) / mAppUnitsPerDevPixel;
    393 }
    394 
    395 DesktopToLayoutDeviceScale nsDeviceContext::GetDesktopToDeviceScale() {
    396  if (mWidget) {
    397    RefPtr<widget::Screen> screen = FindScreen();
    398    return screen->GetDesktopToLayoutDeviceScale();
    399  }
    400  return DesktopToLayoutDeviceScale(1.0);
    401 }