Bug 1839170 - Refactor Snap pulling, Add Firefox Snap Core22 and GNOME 42 SDK symbols...
[gecko.git] / gfx / src / nsDeviceContext.cpp
blobf9fba5841839b05b28e74469cacb789c5709bdac
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/. */
7 #include "nsDeviceContext.h"
8 #include <algorithm> // for max
9 #include "gfxContext.h"
10 #include "gfxImageSurface.h" // for gfxImageSurface
11 #include "gfxPoint.h" // for gfxSize
12 #include "gfxTextRun.h" // for gfxFontGroup
13 #include "mozilla/LookAndFeel.h"
14 #include "mozilla/gfx/PathHelpers.h"
15 #include "mozilla/gfx/PrintTarget.h"
16 #include "mozilla/Preferences.h" // for Preferences
17 #include "mozilla/Services.h" // for GetObserverService
18 #include "mozilla/StaticPrefs_layout.h"
19 #include "mozilla/mozalloc.h" // for operator new
20 #include "mozilla/widget/Screen.h" // for Screen
21 #include "nsCRT.h" // for nsCRT
22 #include "nsDebug.h" // for NS_ASSERTION, etc
23 #include "nsFont.h" // for nsFont
24 #include "nsFontCache.h" // for nsFontCache
25 #include "nsFontMetrics.h" // for nsFontMetrics
26 #include "nsAtom.h" // for nsAtom, NS_Atomize
27 #include "nsID.h"
28 #include "nsIDeviceContextSpec.h" // for nsIDeviceContextSpec
29 #include "nsLanguageAtomService.h" // for nsLanguageAtomService
30 #include "nsIObserver.h" // for nsIObserver, etc
31 #include "nsIObserverService.h" // for nsIObserverService
32 #include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
33 #include "nsISupportsUtils.h" // for NS_ADDREF, NS_RELEASE
34 #include "nsIWidget.h" // for nsIWidget, NS_NATIVE_WINDOW
35 #include "nsRect.h" // for nsRect
36 #include "nsServiceManagerUtils.h" // for do_GetService
37 #include "nsString.h" // for nsDependentString
38 #include "nsTArray.h" // for nsTArray, nsTArray_Impl
39 #include "nsThreadUtils.h" // for NS_IsMainThread
40 #include "mozilla/gfx/Logging.h"
41 #include "mozilla/widget/ScreenManager.h" // for ScreenManager
43 using namespace mozilla;
44 using namespace mozilla::gfx;
45 using mozilla::widget::ScreenManager;
47 nsDeviceContext::nsDeviceContext()
48 : mWidth(0),
49 mHeight(0),
50 mAppUnitsPerDevPixel(-1),
51 mAppUnitsPerDevPixelAtUnitFullZoom(-1),
52 mAppUnitsPerPhysicalInch(-1),
53 mFullZoom(1.0f),
54 mPrintingScale(1.0f),
55 mPrintingTranslate(gfxPoint(0, 0)),
56 mIsCurrentlyPrintingDoc(false) {
57 MOZ_ASSERT(NS_IsMainThread(), "nsDeviceContext created off main thread");
60 nsDeviceContext::~nsDeviceContext() = default;
62 void nsDeviceContext::SetDPI() {
63 float dpi;
65 // Use the printing DC to determine DPI values, if we have one.
66 if (mDeviceContextSpec) {
67 dpi = mDeviceContextSpec->GetDPI();
68 mPrintingScale = mDeviceContextSpec->GetPrintingScale();
69 mPrintingTranslate = mDeviceContextSpec->GetPrintingTranslate();
70 mAppUnitsPerDevPixelAtUnitFullZoom =
71 NS_lround((AppUnitsPerCSSPixel() * 96) / dpi);
72 } else {
73 // A value of -1 means use the maximum of 96 and the system DPI.
74 // A value of 0 means use the system DPI. A positive value is used as the
75 // DPI. This sets the physical size of a device pixel and thus controls the
76 // interpretation of physical units.
77 int32_t prefDPI = StaticPrefs::layout_css_dpi();
78 if (prefDPI > 0) {
79 dpi = prefDPI;
80 } else if (mWidget) {
81 dpi = mWidget->GetDPI();
82 MOZ_ASSERT(dpi > 0);
83 if (prefDPI < 0) {
84 dpi = std::max(96.0f, dpi);
86 } else {
87 dpi = 96.0f;
90 CSSToLayoutDeviceScale scale =
91 mWidget ? mWidget->GetDefaultScale() : CSSToLayoutDeviceScale(1.0);
92 MOZ_ASSERT(scale.scale > 0.0);
93 mAppUnitsPerDevPixelAtUnitFullZoom =
94 std::max(1, NS_lround(AppUnitsPerCSSPixel() / scale.scale));
97 NS_ASSERTION(dpi != -1.0, "no dpi set");
99 mAppUnitsPerPhysicalInch =
100 NS_lround(dpi * mAppUnitsPerDevPixelAtUnitFullZoom);
101 UpdateAppUnitsForFullZoom();
104 void nsDeviceContext::Init(nsIWidget* aWidget) {
105 if (mIsInitialized && mWidget == aWidget) {
106 return;
109 // We can't assert |!mIsInitialized| here since EndSwapDocShellsForDocument
110 // re-initializes nsDeviceContext objects. We can only assert in
111 // InitForPrinting (below).
112 mIsInitialized = true;
114 mWidget = aWidget;
115 SetDPI();
118 // XXX This is only for printing. We should make that obvious in the name.
119 UniquePtr<gfxContext> nsDeviceContext::CreateRenderingContext() {
120 return CreateRenderingContextCommon(/* not a reference context */ false);
123 UniquePtr<gfxContext> nsDeviceContext::CreateReferenceRenderingContext() {
124 return CreateRenderingContextCommon(/* a reference context */ true);
127 UniquePtr<gfxContext> nsDeviceContext::CreateRenderingContextCommon(
128 bool aWantReferenceContext) {
129 MOZ_ASSERT(IsPrinterContext());
130 MOZ_ASSERT(mWidth > 0 && mHeight > 0);
132 if (NS_WARN_IF(!mPrintTarget)) {
133 // Printing canceled already.
134 return nullptr;
137 RefPtr<gfx::DrawTarget> dt;
138 if (aWantReferenceContext) {
139 dt = mPrintTarget->GetReferenceDrawTarget();
140 } else {
141 // This will be null if printing a page from the parent process.
142 RefPtr<DrawEventRecorder> recorder;
143 mDeviceContextSpec->GetDrawEventRecorder(getter_AddRefs(recorder));
144 dt = mPrintTarget->MakeDrawTarget(gfx::IntSize(mWidth, mHeight), recorder);
147 if (!dt || !dt->IsValid()) {
148 gfxCriticalNote << "Failed to create draw target in device context sized "
149 << mWidth << "x" << mHeight << " and pointer "
150 << hexa(mPrintTarget);
151 return nullptr;
154 dt->AddUserData(&sDisablePixelSnapping, (void*)0x1, nullptr);
156 auto pContext = MakeUnique<gfxContext>(dt);
158 gfxMatrix transform;
159 transform.PreTranslate(mPrintingTranslate);
160 transform.PreScale(mPrintingScale, mPrintingScale);
161 pContext->SetMatrixDouble(transform);
162 return pContext;
165 uint32_t nsDeviceContext::GetDepth() {
166 RefPtr<widget::Screen> screen = FindScreen();
167 if (!screen) {
168 ScreenManager& screenManager = ScreenManager::GetSingleton();
169 screen = screenManager.GetPrimaryScreen();
170 MOZ_ASSERT(screen);
172 int32_t depth = 0;
173 screen->GetColorDepth(&depth);
174 return uint32_t(depth);
177 dom::ScreenColorGamut nsDeviceContext::GetColorGamut() {
178 RefPtr<widget::Screen> screen = FindScreen();
179 if (!screen) {
180 auto& screenManager = ScreenManager::GetSingleton();
181 screen = screenManager.GetPrimaryScreen();
182 MOZ_ASSERT(screen);
184 dom::ScreenColorGamut colorGamut;
185 screen->GetColorGamut(&colorGamut);
186 return colorGamut;
189 hal::ScreenOrientation nsDeviceContext::GetScreenOrientationType() {
190 RefPtr<widget::Screen> screen = FindScreen();
191 if (!screen) {
192 auto& screenManager = ScreenManager::GetSingleton();
193 screen = screenManager.GetPrimaryScreen();
194 MOZ_ASSERT(screen);
196 return screen->GetOrientationType();
199 uint16_t nsDeviceContext::GetScreenOrientationAngle() {
200 RefPtr<widget::Screen> screen = FindScreen();
201 if (!screen) {
202 auto& screenManager = ScreenManager::GetSingleton();
203 screen = screenManager.GetPrimaryScreen();
204 MOZ_ASSERT(screen);
206 return screen->GetOrientationAngle();
209 nsresult nsDeviceContext::GetDeviceSurfaceDimensions(nscoord& aWidth,
210 nscoord& aHeight) {
211 if (IsPrinterContext()) {
212 aWidth = mWidth;
213 aHeight = mHeight;
214 } else {
215 nsRect area;
216 ComputeFullAreaUsingScreen(&area);
217 aWidth = area.Width();
218 aHeight = area.Height();
221 return NS_OK;
224 nsresult nsDeviceContext::GetRect(nsRect& aRect) {
225 if (IsPrinterContext()) {
226 aRect.SetRect(0, 0, mWidth, mHeight);
227 } else
228 ComputeFullAreaUsingScreen(&aRect);
230 return NS_OK;
233 nsresult nsDeviceContext::GetClientRect(nsRect& aRect) {
234 if (IsPrinterContext()) {
235 aRect.SetRect(0, 0, mWidth, mHeight);
236 } else
237 ComputeClientRectUsingScreen(&aRect);
239 return NS_OK;
242 nsresult nsDeviceContext::InitForPrinting(nsIDeviceContextSpec* aDevice) {
243 NS_ENSURE_ARG_POINTER(aDevice);
245 MOZ_ASSERT(!mIsInitialized,
246 "Only initialize once, immediately after construction");
248 // We don't set mIsInitialized here. The Init() call below does that.
250 mPrintTarget = aDevice->MakePrintTarget();
251 if (!mPrintTarget) {
252 return NS_ERROR_FAILURE;
255 mDeviceContextSpec = aDevice;
257 Init(nullptr);
259 if (!CalcPrintingSize()) {
260 return NS_ERROR_FAILURE;
263 return NS_OK;
266 nsresult nsDeviceContext::BeginDocument(const nsAString& aTitle,
267 const nsAString& aPrintToFileName,
268 int32_t aStartPage, int32_t aEndPage) {
269 MOZ_DIAGNOSTIC_ASSERT(!mIsCurrentlyPrintingDoc,
270 "Mismatched BeginDocument/EndDocument calls");
272 nsresult rv = mPrintTarget->BeginPrinting(aTitle, aPrintToFileName,
273 aStartPage, aEndPage);
275 if (NS_SUCCEEDED(rv)) {
276 if (mDeviceContextSpec) {
277 rv = mDeviceContextSpec->BeginDocument(aTitle, aPrintToFileName,
278 aStartPage, aEndPage);
280 mIsCurrentlyPrintingDoc = true;
283 // Warn about any failure (except user cancelling):
284 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv) || rv == NS_ERROR_ABORT,
285 "nsDeviceContext::BeginDocument failed");
287 return rv;
290 RefPtr<PrintEndDocumentPromise> nsDeviceContext::EndDocument() {
291 MOZ_DIAGNOSTIC_ASSERT(mIsCurrentlyPrintingDoc,
292 "Mismatched BeginDocument/EndDocument calls");
293 MOZ_DIAGNOSTIC_ASSERT(mPrintTarget);
295 mIsCurrentlyPrintingDoc = false;
297 if (mPrintTarget) {
298 auto result = mPrintTarget->EndPrinting();
299 if (NS_FAILED(result)) {
300 return PrintEndDocumentPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE,
301 __func__);
303 mPrintTarget->Finish();
304 mPrintTarget = nullptr;
307 if (mDeviceContextSpec) {
308 return mDeviceContextSpec->EndDocument();
311 return PrintEndDocumentPromise::CreateAndResolve(true, __func__);
314 nsresult nsDeviceContext::AbortDocument() {
315 MOZ_DIAGNOSTIC_ASSERT(mIsCurrentlyPrintingDoc,
316 "Mismatched BeginDocument/EndDocument calls");
318 nsresult rv = mPrintTarget->AbortPrinting();
319 mIsCurrentlyPrintingDoc = false;
321 if (mDeviceContextSpec) {
322 Unused << mDeviceContextSpec->EndDocument();
325 mPrintTarget = nullptr;
327 return rv;
330 nsresult nsDeviceContext::BeginPage() {
331 MOZ_DIAGNOSTIC_ASSERT(!mIsCurrentlyPrintingDoc || mPrintTarget,
332 "What nulled out our print target while printing?");
333 if (mDeviceContextSpec) {
334 MOZ_TRY(mDeviceContextSpec->BeginPage());
336 if (mPrintTarget) {
337 MOZ_TRY(mPrintTarget->BeginPage());
339 return NS_OK;
342 nsresult nsDeviceContext::EndPage() {
343 MOZ_DIAGNOSTIC_ASSERT(!mIsCurrentlyPrintingDoc || mPrintTarget,
344 "What nulled out our print target while printing?");
345 if (mPrintTarget) {
346 MOZ_TRY(mPrintTarget->EndPage());
348 if (mDeviceContextSpec) {
349 MOZ_TRY(mDeviceContextSpec->EndPage());
351 return NS_OK;
354 void nsDeviceContext::ComputeClientRectUsingScreen(nsRect* outRect) {
355 // we always need to recompute the clientRect
356 // because the window may have moved onto a different screen. In the single
357 // monitor case, we only need to do the computation if we haven't done it
358 // once already, and remember that we have because we're assured it won't
359 // change.
360 if (RefPtr<widget::Screen> screen = FindScreen()) {
361 *outRect = LayoutDeviceIntRect::ToAppUnits(screen->GetAvailRect(),
362 AppUnitsPerDevPixel());
366 void nsDeviceContext::ComputeFullAreaUsingScreen(nsRect* outRect) {
367 // if we have more than one screen, we always need to recompute the clientRect
368 // because the window may have moved onto a different screen. In the single
369 // monitor case, we only need to do the computation if we haven't done it
370 // once already, and remember that we have because we're assured it won't
371 // change.
372 if (RefPtr<widget::Screen> screen = FindScreen()) {
373 *outRect = LayoutDeviceIntRect::ToAppUnits(screen->GetRect(),
374 AppUnitsPerDevPixel());
375 mWidth = outRect->Width();
376 mHeight = outRect->Height();
381 // FindScreen
383 // Determines which screen intersects the largest area of the given surface.
385 already_AddRefed<widget::Screen> nsDeviceContext::FindScreen() {
386 if (!mWidget) {
387 return nullptr;
390 CheckDPIChange();
392 if (RefPtr<widget::Screen> screen = mWidget->GetWidgetScreen()) {
393 return screen.forget();
396 ScreenManager& screenManager = ScreenManager::GetSingleton();
397 return screenManager.GetPrimaryScreen();
400 bool nsDeviceContext::CalcPrintingSize() {
401 gfxSize size(mPrintTarget->GetSize());
402 // For printing, CSS inches and physical inches are identical
403 // so it doesn't matter which we use here
404 mWidth = NSToCoordRound(size.width * AppUnitsPerPhysicalInch() /
405 POINTS_PER_INCH_FLOAT);
406 mHeight = NSToCoordRound(size.height * AppUnitsPerPhysicalInch() /
407 POINTS_PER_INCH_FLOAT);
409 return (mWidth > 0 && mHeight > 0);
412 bool nsDeviceContext::CheckDPIChange() {
413 int32_t oldDevPixels = mAppUnitsPerDevPixelAtUnitFullZoom;
414 int32_t oldInches = mAppUnitsPerPhysicalInch;
416 SetDPI();
418 return oldDevPixels != mAppUnitsPerDevPixelAtUnitFullZoom ||
419 oldInches != mAppUnitsPerPhysicalInch;
422 bool nsDeviceContext::SetFullZoom(float aScale) {
423 if (aScale <= 0) {
424 MOZ_ASSERT_UNREACHABLE("Invalid full zoom value");
425 return false;
427 int32_t oldAppUnitsPerDevPixel = mAppUnitsPerDevPixel;
428 mFullZoom = aScale;
429 UpdateAppUnitsForFullZoom();
430 return oldAppUnitsPerDevPixel != mAppUnitsPerDevPixel;
433 static int32_t ApplyFullZoom(int32_t aUnzoomedAppUnits, float aFullZoom) {
434 if (aFullZoom == 1.0f) {
435 return aUnzoomedAppUnits;
437 return std::max(1, NSToIntRound(float(aUnzoomedAppUnits) / aFullZoom));
440 int32_t nsDeviceContext::AppUnitsPerDevPixelInTopLevelChromePage() const {
441 // The only zoom that applies to chrome pages is the system zoom, if any.
442 return ApplyFullZoom(mAppUnitsPerDevPixelAtUnitFullZoom,
443 LookAndFeel::SystemZoomSettings().mFullZoom);
446 void nsDeviceContext::UpdateAppUnitsForFullZoom() {
447 mAppUnitsPerDevPixel =
448 ApplyFullZoom(mAppUnitsPerDevPixelAtUnitFullZoom, mFullZoom);
449 // adjust mFullZoom to reflect appunit rounding
450 mFullZoom = float(mAppUnitsPerDevPixelAtUnitFullZoom) / mAppUnitsPerDevPixel;
453 DesktopToLayoutDeviceScale nsDeviceContext::GetDesktopToDeviceScale() {
454 if (RefPtr<widget::Screen> screen = FindScreen()) {
455 return screen->GetDesktopToLayoutDeviceScale();
457 return DesktopToLayoutDeviceScale(1.0);