Bug 1839315: part 4) Link from `SheetLoadData::mWasAlternate` to spec. r=emilio DONTBUILD
[gecko.git] / gfx / src / nsDeviceContext.cpp
blob0171f892b280b5a301f946ef06218b766ffb9a63
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/Try.h" // for MOZ_TRY
20 #include "mozilla/mozalloc.h" // for operator new
21 #include "mozilla/widget/Screen.h" // for Screen
22 #include "nsCRT.h" // for nsCRT
23 #include "nsDebug.h" // for NS_ASSERTION, etc
24 #include "nsFont.h" // for nsFont
25 #include "nsFontCache.h" // for nsFontCache
26 #include "nsFontMetrics.h" // for nsFontMetrics
27 #include "nsAtom.h" // for nsAtom, NS_Atomize
28 #include "nsID.h"
29 #include "nsIDeviceContextSpec.h" // for nsIDeviceContextSpec
30 #include "nsLanguageAtomService.h" // for nsLanguageAtomService
31 #include "nsIObserver.h" // for nsIObserver, etc
32 #include "nsIObserverService.h" // for nsIObserverService
33 #include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
34 #include "nsISupportsUtils.h" // for NS_ADDREF, NS_RELEASE
35 #include "nsIWidget.h" // for nsIWidget, NS_NATIVE_WINDOW
36 #include "nsRect.h" // for nsRect
37 #include "nsServiceManagerUtils.h" // for do_GetService
38 #include "nsString.h" // for nsDependentString
39 #include "nsTArray.h" // for nsTArray, nsTArray_Impl
40 #include "nsThreadUtils.h" // for NS_IsMainThread
41 #include "mozilla/gfx/Logging.h"
42 #include "mozilla/widget/ScreenManager.h" // for ScreenManager
44 using namespace mozilla;
45 using namespace mozilla::gfx;
46 using mozilla::widget::ScreenManager;
48 nsDeviceContext::nsDeviceContext()
49 : mWidth(0),
50 mHeight(0),
51 mAppUnitsPerDevPixel(-1),
52 mAppUnitsPerDevPixelAtUnitFullZoom(-1),
53 mAppUnitsPerPhysicalInch(-1),
54 mFullZoom(1.0f),
55 mPrintingScale(1.0f),
56 mPrintingTranslate(gfxPoint(0, 0)),
57 mIsCurrentlyPrintingDoc(false) {
58 MOZ_ASSERT(NS_IsMainThread(), "nsDeviceContext created off main thread");
61 nsDeviceContext::~nsDeviceContext() = default;
63 void nsDeviceContext::SetDPI() {
64 float dpi;
66 // Use the printing DC to determine DPI values, if we have one.
67 if (mDeviceContextSpec) {
68 dpi = mDeviceContextSpec->GetDPI();
69 mPrintingScale = mDeviceContextSpec->GetPrintingScale();
70 mPrintingTranslate = mDeviceContextSpec->GetPrintingTranslate();
71 mAppUnitsPerDevPixelAtUnitFullZoom =
72 NS_lround((AppUnitsPerCSSPixel() * 96) / dpi);
73 } else {
74 // A value of -1 means use the maximum of 96 and the system DPI.
75 // A value of 0 means use the system DPI. A positive value is used as the
76 // DPI. This sets the physical size of a device pixel and thus controls the
77 // interpretation of physical units.
78 int32_t prefDPI = StaticPrefs::layout_css_dpi();
79 if (prefDPI > 0) {
80 dpi = prefDPI;
81 } else if (mWidget) {
82 dpi = mWidget->GetDPI();
83 MOZ_ASSERT(dpi > 0);
84 if (prefDPI < 0) {
85 dpi = std::max(96.0f, dpi);
87 } else {
88 dpi = 96.0f;
91 CSSToLayoutDeviceScale scale =
92 mWidget ? mWidget->GetDefaultScale() : CSSToLayoutDeviceScale(1.0);
93 MOZ_ASSERT(scale.scale > 0.0);
94 mAppUnitsPerDevPixelAtUnitFullZoom =
95 std::max(1, NS_lround(AppUnitsPerCSSPixel() / scale.scale));
98 NS_ASSERTION(dpi != -1.0, "no dpi set");
100 mAppUnitsPerPhysicalInch =
101 NS_lround(dpi * mAppUnitsPerDevPixelAtUnitFullZoom);
102 UpdateAppUnitsForFullZoom();
105 void nsDeviceContext::Init(nsIWidget* aWidget) {
106 if (mIsInitialized && mWidget == aWidget) {
107 return;
110 // We can't assert |!mIsInitialized| here since EndSwapDocShellsForDocument
111 // re-initializes nsDeviceContext objects. We can only assert in
112 // InitForPrinting (below).
113 mIsInitialized = true;
115 mWidget = aWidget;
116 SetDPI();
119 // XXX This is only for printing. We should make that obvious in the name.
120 UniquePtr<gfxContext> nsDeviceContext::CreateRenderingContext() {
121 return CreateRenderingContextCommon(/* not a reference context */ false);
124 UniquePtr<gfxContext> nsDeviceContext::CreateReferenceRenderingContext() {
125 return CreateRenderingContextCommon(/* a reference context */ true);
128 UniquePtr<gfxContext> nsDeviceContext::CreateRenderingContextCommon(
129 bool aWantReferenceContext) {
130 MOZ_ASSERT(IsPrinterContext());
131 MOZ_ASSERT(mWidth > 0 && mHeight > 0);
133 if (NS_WARN_IF(!mPrintTarget)) {
134 // Printing canceled already.
135 return nullptr;
138 RefPtr<gfx::DrawTarget> dt;
139 if (aWantReferenceContext) {
140 dt = mPrintTarget->GetReferenceDrawTarget();
141 } else {
142 // This will be null if printing a page from the parent process.
143 RefPtr<DrawEventRecorder> recorder;
144 mDeviceContextSpec->GetDrawEventRecorder(getter_AddRefs(recorder));
145 dt = mPrintTarget->MakeDrawTarget(gfx::IntSize(mWidth, mHeight), recorder);
148 if (!dt || !dt->IsValid()) {
149 gfxCriticalNote << "Failed to create draw target in device context sized "
150 << mWidth << "x" << mHeight << " and pointer "
151 << hexa(mPrintTarget);
152 return nullptr;
155 dt->AddUserData(&sDisablePixelSnapping, (void*)0x1, nullptr);
157 auto pContext = MakeUnique<gfxContext>(dt);
159 gfxMatrix transform;
160 transform.PreTranslate(mPrintingTranslate);
161 transform.PreScale(mPrintingScale, mPrintingScale);
162 pContext->SetMatrixDouble(transform);
163 return pContext;
166 uint32_t nsDeviceContext::GetDepth() {
167 RefPtr<widget::Screen> screen = FindScreen();
168 if (!screen) {
169 ScreenManager& screenManager = ScreenManager::GetSingleton();
170 screen = screenManager.GetPrimaryScreen();
171 MOZ_ASSERT(screen);
173 int32_t depth = 0;
174 screen->GetColorDepth(&depth);
175 return uint32_t(depth);
178 dom::ScreenColorGamut nsDeviceContext::GetColorGamut() {
179 RefPtr<widget::Screen> screen = FindScreen();
180 if (!screen) {
181 auto& screenManager = ScreenManager::GetSingleton();
182 screen = screenManager.GetPrimaryScreen();
183 MOZ_ASSERT(screen);
185 dom::ScreenColorGamut colorGamut;
186 screen->GetColorGamut(&colorGamut);
187 return colorGamut;
190 hal::ScreenOrientation nsDeviceContext::GetScreenOrientationType() {
191 RefPtr<widget::Screen> screen = FindScreen();
192 if (!screen) {
193 auto& screenManager = ScreenManager::GetSingleton();
194 screen = screenManager.GetPrimaryScreen();
195 MOZ_ASSERT(screen);
197 return screen->GetOrientationType();
200 uint16_t nsDeviceContext::GetScreenOrientationAngle() {
201 RefPtr<widget::Screen> screen = FindScreen();
202 if (!screen) {
203 auto& screenManager = ScreenManager::GetSingleton();
204 screen = screenManager.GetPrimaryScreen();
205 MOZ_ASSERT(screen);
207 return screen->GetOrientationAngle();
210 nsresult nsDeviceContext::GetDeviceSurfaceDimensions(nscoord& aWidth,
211 nscoord& aHeight) {
212 if (IsPrinterContext()) {
213 aWidth = mWidth;
214 aHeight = mHeight;
215 } else {
216 nsRect area;
217 ComputeFullAreaUsingScreen(&area);
218 aWidth = area.Width();
219 aHeight = area.Height();
222 return NS_OK;
225 nsresult nsDeviceContext::GetRect(nsRect& aRect) {
226 if (IsPrinterContext()) {
227 aRect.SetRect(0, 0, mWidth, mHeight);
228 } else
229 ComputeFullAreaUsingScreen(&aRect);
231 return NS_OK;
234 nsresult nsDeviceContext::GetClientRect(nsRect& aRect) {
235 if (IsPrinterContext()) {
236 aRect.SetRect(0, 0, mWidth, mHeight);
237 } else
238 ComputeClientRectUsingScreen(&aRect);
240 return NS_OK;
243 nsresult nsDeviceContext::InitForPrinting(nsIDeviceContextSpec* aDevice) {
244 NS_ENSURE_ARG_POINTER(aDevice);
246 MOZ_ASSERT(!mIsInitialized,
247 "Only initialize once, immediately after construction");
249 // We don't set mIsInitialized here. The Init() call below does that.
251 mPrintTarget = aDevice->MakePrintTarget();
252 if (!mPrintTarget) {
253 return NS_ERROR_FAILURE;
256 mDeviceContextSpec = aDevice;
258 Init(nullptr);
260 if (!CalcPrintingSize()) {
261 return NS_ERROR_FAILURE;
264 return NS_OK;
267 nsresult nsDeviceContext::BeginDocument(const nsAString& aTitle,
268 const nsAString& aPrintToFileName,
269 int32_t aStartPage, int32_t aEndPage) {
270 MOZ_DIAGNOSTIC_ASSERT(!mIsCurrentlyPrintingDoc,
271 "Mismatched BeginDocument/EndDocument calls");
273 nsresult rv = mPrintTarget->BeginPrinting(aTitle, aPrintToFileName,
274 aStartPage, aEndPage);
276 if (NS_SUCCEEDED(rv)) {
277 if (mDeviceContextSpec) {
278 rv = mDeviceContextSpec->BeginDocument(aTitle, aPrintToFileName,
279 aStartPage, aEndPage);
281 mIsCurrentlyPrintingDoc = true;
284 // Warn about any failure (except user cancelling):
285 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv) || rv == NS_ERROR_ABORT,
286 "nsDeviceContext::BeginDocument failed");
288 return rv;
291 RefPtr<PrintEndDocumentPromise> nsDeviceContext::EndDocument() {
292 MOZ_DIAGNOSTIC_ASSERT(mIsCurrentlyPrintingDoc,
293 "Mismatched BeginDocument/EndDocument calls");
294 MOZ_DIAGNOSTIC_ASSERT(mPrintTarget);
296 mIsCurrentlyPrintingDoc = false;
298 if (mPrintTarget) {
299 auto result = mPrintTarget->EndPrinting();
300 if (NS_FAILED(result)) {
301 return PrintEndDocumentPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE,
302 __func__);
304 mPrintTarget->Finish();
305 mPrintTarget = nullptr;
308 if (mDeviceContextSpec) {
309 return mDeviceContextSpec->EndDocument();
312 return PrintEndDocumentPromise::CreateAndResolve(true, __func__);
315 nsresult nsDeviceContext::AbortDocument() {
316 MOZ_DIAGNOSTIC_ASSERT(mIsCurrentlyPrintingDoc,
317 "Mismatched BeginDocument/EndDocument calls");
319 nsresult rv = mPrintTarget->AbortPrinting();
320 mIsCurrentlyPrintingDoc = false;
322 if (mDeviceContextSpec) {
323 Unused << mDeviceContextSpec->EndDocument();
326 mPrintTarget = nullptr;
328 return rv;
331 nsresult nsDeviceContext::BeginPage(const IntSize& aSizeInPoints) {
332 MOZ_DIAGNOSTIC_ASSERT(!mIsCurrentlyPrintingDoc || mPrintTarget,
333 "What nulled out our print target while printing?");
334 if (mDeviceContextSpec) {
335 MOZ_TRY(mDeviceContextSpec->BeginPage(aSizeInPoints));
337 if (mPrintTarget) {
338 MOZ_TRY(mPrintTarget->BeginPage(aSizeInPoints));
340 return NS_OK;
343 nsresult nsDeviceContext::EndPage() {
344 MOZ_DIAGNOSTIC_ASSERT(!mIsCurrentlyPrintingDoc || mPrintTarget,
345 "What nulled out our print target while printing?");
346 if (mPrintTarget) {
347 MOZ_TRY(mPrintTarget->EndPage());
349 if (mDeviceContextSpec) {
350 MOZ_TRY(mDeviceContextSpec->EndPage());
352 return NS_OK;
355 void nsDeviceContext::ComputeClientRectUsingScreen(nsRect* outRect) {
356 // we always need to recompute the clientRect
357 // because the window may have moved onto a different screen. In the single
358 // monitor case, we only need to do the computation if we haven't done it
359 // once already, and remember that we have because we're assured it won't
360 // change.
361 if (RefPtr<widget::Screen> screen = FindScreen()) {
362 *outRect = LayoutDeviceIntRect::ToAppUnits(screen->GetAvailRect(),
363 AppUnitsPerDevPixel());
367 void nsDeviceContext::ComputeFullAreaUsingScreen(nsRect* outRect) {
368 // if we have more than one screen, we always need to recompute the clientRect
369 // because the window may have moved onto a different screen. In the single
370 // monitor case, we only need to do the computation if we haven't done it
371 // once already, and remember that we have because we're assured it won't
372 // change.
373 if (RefPtr<widget::Screen> screen = FindScreen()) {
374 *outRect = LayoutDeviceIntRect::ToAppUnits(screen->GetRect(),
375 AppUnitsPerDevPixel());
376 mWidth = outRect->Width();
377 mHeight = outRect->Height();
382 // FindScreen
384 // Determines which screen intersects the largest area of the given surface.
386 already_AddRefed<widget::Screen> nsDeviceContext::FindScreen() {
387 if (!mWidget) {
388 return nullptr;
391 CheckDPIChange();
393 if (RefPtr<widget::Screen> screen = mWidget->GetWidgetScreen()) {
394 return screen.forget();
397 ScreenManager& screenManager = ScreenManager::GetSingleton();
398 return screenManager.GetPrimaryScreen();
401 bool nsDeviceContext::CalcPrintingSize() {
402 gfxSize size(mPrintTarget->GetSize());
403 // For printing, CSS inches and physical inches are identical
404 // so it doesn't matter which we use here
405 mWidth = NSToCoordRound(size.width * AppUnitsPerPhysicalInch() /
406 POINTS_PER_INCH_FLOAT);
407 mHeight = NSToCoordRound(size.height * AppUnitsPerPhysicalInch() /
408 POINTS_PER_INCH_FLOAT);
410 return (mWidth > 0 && mHeight > 0);
413 bool nsDeviceContext::CheckDPIChange() {
414 int32_t oldDevPixels = mAppUnitsPerDevPixelAtUnitFullZoom;
415 int32_t oldInches = mAppUnitsPerPhysicalInch;
417 SetDPI();
419 return oldDevPixels != mAppUnitsPerDevPixelAtUnitFullZoom ||
420 oldInches != mAppUnitsPerPhysicalInch;
423 bool nsDeviceContext::SetFullZoom(float aScale) {
424 if (aScale <= 0) {
425 MOZ_ASSERT_UNREACHABLE("Invalid full zoom value");
426 return false;
428 int32_t oldAppUnitsPerDevPixel = mAppUnitsPerDevPixel;
429 mFullZoom = aScale;
430 UpdateAppUnitsForFullZoom();
431 return oldAppUnitsPerDevPixel != mAppUnitsPerDevPixel;
434 static int32_t ApplyFullZoom(int32_t aUnzoomedAppUnits, float aFullZoom) {
435 if (aFullZoom == 1.0f) {
436 return aUnzoomedAppUnits;
438 return std::max(1, NSToIntRound(float(aUnzoomedAppUnits) / aFullZoom));
441 int32_t nsDeviceContext::AppUnitsPerDevPixelInTopLevelChromePage() const {
442 // The only zoom that applies to chrome pages is the system zoom, if any.
443 return ApplyFullZoom(mAppUnitsPerDevPixelAtUnitFullZoom,
444 LookAndFeel::SystemZoomSettings().mFullZoom);
447 void nsDeviceContext::UpdateAppUnitsForFullZoom() {
448 mAppUnitsPerDevPixel =
449 ApplyFullZoom(mAppUnitsPerDevPixelAtUnitFullZoom, mFullZoom);
450 // adjust mFullZoom to reflect appunit rounding
451 mFullZoom = float(mAppUnitsPerDevPixelAtUnitFullZoom) / mAppUnitsPerDevPixel;
454 DesktopToLayoutDeviceScale nsDeviceContext::GetDesktopToDeviceScale() {
455 if (RefPtr<widget::Screen> screen = FindScreen()) {
456 return screen->GetDesktopToLayoutDeviceScale();
458 return DesktopToLayoutDeviceScale(1.0);