Bug 1891710: part 2) Enable <Element-outerHTML.html> WPT for Trusted Types. r=smaug
[gecko.git] / gfx / src / nsDeviceContext.cpp
blob2bb53010a8e90887a57cb233f6c6210a541d5bc5
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/ProfilerMarkers.h"
18 #include "mozilla/Services.h" // for GetObserverService
19 #include "mozilla/StaticPrefs_layout.h"
20 #include "mozilla/Try.h" // for MOZ_TRY
21 #include "mozilla/mozalloc.h" // for operator new
22 #include "mozilla/widget/Screen.h" // for Screen
23 #include "nsCRT.h" // for nsCRT
24 #include "nsDebug.h" // for NS_ASSERTION, etc
25 #include "nsFont.h" // for nsFont
26 #include "nsFontCache.h" // for nsFontCache
27 #include "nsFontMetrics.h" // for nsFontMetrics
28 #include "nsAtom.h" // for nsAtom, NS_Atomize
29 #include "nsID.h"
30 #include "nsIDeviceContextSpec.h" // for nsIDeviceContextSpec
31 #include "nsLanguageAtomService.h" // for nsLanguageAtomService
32 #include "nsIObserver.h" // for nsIObserver, etc
33 #include "nsIObserverService.h" // for nsIObserverService
34 #include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
35 #include "nsISupportsUtils.h" // for NS_ADDREF, NS_RELEASE
36 #include "nsIWidget.h" // for nsIWidget, NS_NATIVE_WINDOW
37 #include "nsRect.h" // for nsRect
38 #include "nsServiceManagerUtils.h" // for do_GetService
39 #include "nsString.h" // for nsDependentString
40 #include "nsTArray.h" // for nsTArray, nsTArray_Impl
41 #include "nsThreadUtils.h" // for NS_IsMainThread
42 #include "mozilla/gfx/Logging.h"
43 #include "mozilla/widget/ScreenManager.h" // for ScreenManager
45 using namespace mozilla;
46 using namespace mozilla::gfx;
47 using mozilla::widget::ScreenManager;
49 nsDeviceContext::nsDeviceContext()
50 : mWidth(0),
51 mHeight(0),
52 mAppUnitsPerDevPixel(-1),
53 mAppUnitsPerDevPixelAtUnitFullZoom(-1),
54 mAppUnitsPerPhysicalInch(-1),
55 mFullZoom(1.0f),
56 mPrintingScale(1.0f),
57 mPrintingTranslate(gfxPoint(0, 0)),
58 mIsCurrentlyPrintingDoc(false) {
59 MOZ_ASSERT(NS_IsMainThread(), "nsDeviceContext created off main thread");
62 nsDeviceContext::~nsDeviceContext() = default;
64 void nsDeviceContext::SetDPI() {
65 float dpi;
67 // Use the printing DC to determine DPI values, if we have one.
68 if (mDeviceContextSpec) {
69 dpi = mDeviceContextSpec->GetDPI();
70 mPrintingScale = mDeviceContextSpec->GetPrintingScale();
71 mPrintingTranslate = mDeviceContextSpec->GetPrintingTranslate();
72 mAppUnitsPerDevPixelAtUnitFullZoom =
73 NS_lround((AppUnitsPerCSSPixel() * 96) / dpi);
74 } else {
75 // A value of -1 means use the maximum of 96 and the system DPI.
76 // A value of 0 means use the system DPI. A positive value is used as the
77 // DPI. This sets the physical size of a device pixel and thus controls the
78 // interpretation of physical units.
79 int32_t prefDPI = StaticPrefs::layout_css_dpi();
80 if (prefDPI > 0) {
81 dpi = prefDPI;
82 } else if (mWidget) {
83 dpi = mWidget->GetDPI();
84 MOZ_ASSERT(dpi > 0);
85 if (prefDPI < 0) {
86 dpi = std::max(96.0f, dpi);
88 } else {
89 dpi = 96.0f;
92 CSSToLayoutDeviceScale scale =
93 mWidget ? mWidget->GetDefaultScale() : CSSToLayoutDeviceScale(1.0);
94 MOZ_ASSERT(scale.scale > 0.0);
95 mAppUnitsPerDevPixelAtUnitFullZoom =
96 std::max(1, NS_lround(AppUnitsPerCSSPixel() / scale.scale));
99 NS_ASSERTION(dpi != -1.0, "no dpi set");
101 mAppUnitsPerPhysicalInch =
102 NS_lround(dpi * mAppUnitsPerDevPixelAtUnitFullZoom);
103 UpdateAppUnitsForFullZoom();
106 void nsDeviceContext::Init(nsIWidget* aWidget) {
107 if (mIsInitialized && mWidget == aWidget) {
108 return;
111 // We can't assert |!mIsInitialized| here since EndSwapDocShellsForDocument
112 // re-initializes nsDeviceContext objects. We can only assert in
113 // InitForPrinting (below).
114 mIsInitialized = true;
116 mWidget = aWidget;
117 SetDPI();
120 // XXX This is only for printing. We should make that obvious in the name.
121 UniquePtr<gfxContext> nsDeviceContext::CreateRenderingContext() {
122 return CreateRenderingContextCommon(/* not a reference context */ false);
125 UniquePtr<gfxContext> nsDeviceContext::CreateReferenceRenderingContext() {
126 return CreateRenderingContextCommon(/* a reference context */ true);
129 UniquePtr<gfxContext> nsDeviceContext::CreateRenderingContextCommon(
130 bool aWantReferenceContext) {
131 MOZ_ASSERT(IsPrinterContext());
132 MOZ_ASSERT(mWidth > 0 && mHeight > 0);
134 if (NS_WARN_IF(!mPrintTarget)) {
135 // Printing canceled already.
136 return nullptr;
139 RefPtr<gfx::DrawTarget> dt;
140 if (aWantReferenceContext) {
141 dt = mPrintTarget->GetReferenceDrawTarget();
142 } else {
143 // This will be null if printing a page from the parent process.
144 RefPtr<DrawEventRecorder> recorder;
145 mDeviceContextSpec->GetDrawEventRecorder(getter_AddRefs(recorder));
146 dt = mPrintTarget->MakeDrawTarget(gfx::IntSize(mWidth, mHeight), recorder);
149 if (!dt || !dt->IsValid()) {
150 gfxCriticalNote << "Failed to create draw target in device context sized "
151 << mWidth << "x" << mHeight << " and pointer "
152 << hexa(mPrintTarget);
153 return nullptr;
156 dt->AddUserData(&sDisablePixelSnapping, (void*)0x1, nullptr);
158 auto pContext = MakeUnique<gfxContext>(dt);
160 gfxMatrix transform;
161 transform.PreTranslate(mPrintingTranslate);
162 transform.PreScale(mPrintingScale, mPrintingScale);
163 pContext->SetMatrixDouble(transform);
164 return pContext;
167 uint32_t nsDeviceContext::GetDepth() {
168 RefPtr<widget::Screen> screen = FindScreen();
169 if (!screen) {
170 ScreenManager& screenManager = ScreenManager::GetSingleton();
171 screen = screenManager.GetPrimaryScreen();
172 MOZ_ASSERT(screen);
174 int32_t depth = 0;
175 screen->GetColorDepth(&depth);
176 return uint32_t(depth);
179 dom::ScreenColorGamut nsDeviceContext::GetColorGamut() {
180 RefPtr<widget::Screen> screen = FindScreen();
181 if (!screen) {
182 auto& screenManager = ScreenManager::GetSingleton();
183 screen = screenManager.GetPrimaryScreen();
184 MOZ_ASSERT(screen);
186 dom::ScreenColorGamut colorGamut;
187 screen->GetColorGamut(&colorGamut);
188 return colorGamut;
191 hal::ScreenOrientation nsDeviceContext::GetScreenOrientationType() {
192 RefPtr<widget::Screen> screen = FindScreen();
193 if (!screen) {
194 auto& screenManager = ScreenManager::GetSingleton();
195 screen = screenManager.GetPrimaryScreen();
196 MOZ_ASSERT(screen);
198 return screen->GetOrientationType();
201 uint16_t nsDeviceContext::GetScreenOrientationAngle() {
202 RefPtr<widget::Screen> screen = FindScreen();
203 if (!screen) {
204 auto& screenManager = ScreenManager::GetSingleton();
205 screen = screenManager.GetPrimaryScreen();
206 MOZ_ASSERT(screen);
208 return screen->GetOrientationAngle();
211 bool nsDeviceContext::GetScreenIsHDR() {
212 RefPtr<widget::Screen> screen = FindScreen();
213 if (!screen) {
214 auto& screenManager = ScreenManager::GetSingleton();
215 screen = screenManager.GetPrimaryScreen();
216 MOZ_ASSERT(screen);
218 return screen->GetIsHDR();
221 nsresult nsDeviceContext::GetDeviceSurfaceDimensions(nscoord& aWidth,
222 nscoord& aHeight) {
223 if (IsPrinterContext()) {
224 aWidth = mWidth;
225 aHeight = mHeight;
226 } else {
227 nsRect area;
228 ComputeFullAreaUsingScreen(&area);
229 aWidth = area.Width();
230 aHeight = area.Height();
233 return NS_OK;
236 nsresult nsDeviceContext::GetRect(nsRect& aRect) {
237 if (IsPrinterContext()) {
238 aRect.SetRect(0, 0, mWidth, mHeight);
239 } else
240 ComputeFullAreaUsingScreen(&aRect);
242 return NS_OK;
245 nsresult nsDeviceContext::GetClientRect(nsRect& aRect) {
246 if (IsPrinterContext()) {
247 aRect.SetRect(0, 0, mWidth, mHeight);
248 } else
249 ComputeClientRectUsingScreen(&aRect);
251 return NS_OK;
254 nsresult nsDeviceContext::InitForPrinting(nsIDeviceContextSpec* aDevice) {
255 NS_ENSURE_ARG_POINTER(aDevice);
257 MOZ_ASSERT(!mIsInitialized,
258 "Only initialize once, immediately after construction");
260 // We don't set mIsInitialized here. The Init() call below does that.
262 mPrintTarget = aDevice->MakePrintTarget();
263 if (!mPrintTarget) {
264 return NS_ERROR_FAILURE;
267 mDeviceContextSpec = aDevice;
269 Init(nullptr);
271 if (!CalcPrintingSize()) {
272 return NS_ERROR_FAILURE;
275 return NS_OK;
278 nsresult nsDeviceContext::BeginDocument(const nsAString& aTitle,
279 const nsAString& aPrintToFileName,
280 int32_t aStartPage, int32_t aEndPage) {
281 MOZ_DIAGNOSTIC_ASSERT(!mIsCurrentlyPrintingDoc,
282 "Mismatched BeginDocument/EndDocument calls");
283 AUTO_PROFILER_MARKER_TEXT("DeviceContext Printing", LAYOUT_Printing, {},
284 "nsDeviceContext::BeginDocument"_ns);
286 nsresult rv = mPrintTarget->BeginPrinting(aTitle, aPrintToFileName,
287 aStartPage, aEndPage);
289 if (NS_SUCCEEDED(rv)) {
290 if (mDeviceContextSpec) {
291 rv = mDeviceContextSpec->BeginDocument(aTitle, aPrintToFileName,
292 aStartPage, aEndPage);
294 mIsCurrentlyPrintingDoc = true;
297 // Warn about any failure (except user cancelling):
298 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv) || rv == NS_ERROR_ABORT,
299 "nsDeviceContext::BeginDocument failed");
301 return rv;
304 RefPtr<PrintEndDocumentPromise> nsDeviceContext::EndDocument() {
305 MOZ_DIAGNOSTIC_ASSERT(mIsCurrentlyPrintingDoc,
306 "Mismatched BeginDocument/EndDocument calls");
307 MOZ_DIAGNOSTIC_ASSERT(mPrintTarget);
308 AUTO_PROFILER_MARKER_TEXT("DeviceContext Printing", LAYOUT_Printing, {},
309 "nsDeviceContext::EndDocument"_ns);
311 mIsCurrentlyPrintingDoc = false;
313 if (mPrintTarget) {
314 auto result = mPrintTarget->EndPrinting();
315 if (NS_FAILED(result)) {
316 return PrintEndDocumentPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE,
317 __func__);
319 mPrintTarget->Finish();
320 mPrintTarget = nullptr;
323 if (mDeviceContextSpec) {
324 return mDeviceContextSpec->EndDocument();
327 return PrintEndDocumentPromise::CreateAndResolve(true, __func__);
330 nsresult nsDeviceContext::AbortDocument() {
331 MOZ_DIAGNOSTIC_ASSERT(mIsCurrentlyPrintingDoc,
332 "Mismatched BeginDocument/EndDocument calls");
333 AUTO_PROFILER_MARKER_TEXT("DeviceContext Printing", LAYOUT_Printing, {},
334 "nsDeviceContext::AbortDocument"_ns);
336 nsresult rv = mPrintTarget->AbortPrinting();
337 mIsCurrentlyPrintingDoc = false;
339 if (mDeviceContextSpec) {
340 Unused << mDeviceContextSpec->EndDocument();
343 mPrintTarget = nullptr;
345 return rv;
348 nsresult nsDeviceContext::BeginPage(const IntSize& aSizeInPoints) {
349 MOZ_DIAGNOSTIC_ASSERT(!mIsCurrentlyPrintingDoc || mPrintTarget,
350 "What nulled out our print target while printing?");
351 AUTO_PROFILER_MARKER_TEXT("DeviceContext Printing", LAYOUT_Printing, {},
352 "nsDeviceContext::BeginPage"_ns);
354 if (mDeviceContextSpec) {
355 MOZ_TRY(mDeviceContextSpec->BeginPage(aSizeInPoints));
357 if (mPrintTarget) {
358 MOZ_TRY(mPrintTarget->BeginPage(aSizeInPoints));
360 return NS_OK;
363 nsresult nsDeviceContext::EndPage() {
364 MOZ_DIAGNOSTIC_ASSERT(!mIsCurrentlyPrintingDoc || mPrintTarget,
365 "What nulled out our print target while printing?");
366 AUTO_PROFILER_MARKER_TEXT("DeviceContext Printing", LAYOUT_Printing, {},
367 "nsDeviceContext::EndPage"_ns);
369 if (mPrintTarget) {
370 MOZ_TRY(mPrintTarget->EndPage());
372 if (mDeviceContextSpec) {
373 MOZ_TRY(mDeviceContextSpec->EndPage());
375 return NS_OK;
378 void nsDeviceContext::ComputeClientRectUsingScreen(nsRect* outRect) {
379 // we always need to recompute the clientRect
380 // because the window may have moved onto a different screen. In the single
381 // monitor case, we only need to do the computation if we haven't done it
382 // once already, and remember that we have because we're assured it won't
383 // change.
384 if (RefPtr<widget::Screen> screen = FindScreen()) {
385 *outRect = LayoutDeviceIntRect::ToAppUnits(screen->GetAvailRect(),
386 AppUnitsPerDevPixel());
390 void nsDeviceContext::ComputeFullAreaUsingScreen(nsRect* outRect) {
391 // if we have more than one screen, we always need to recompute the clientRect
392 // because the window may have moved onto a different screen. In the single
393 // monitor case, we only need to do the computation if we haven't done it
394 // once already, and remember that we have because we're assured it won't
395 // change.
396 if (RefPtr<widget::Screen> screen = FindScreen()) {
397 *outRect = LayoutDeviceIntRect::ToAppUnits(screen->GetRect(),
398 AppUnitsPerDevPixel());
399 mWidth = outRect->Width();
400 mHeight = outRect->Height();
405 // FindScreen
407 // Determines which screen intersects the largest area of the given surface.
409 already_AddRefed<widget::Screen> nsDeviceContext::FindScreen() {
410 if (!mWidget) {
411 return nullptr;
414 CheckDPIChange();
416 if (RefPtr<widget::Screen> screen = mWidget->GetWidgetScreen()) {
417 return screen.forget();
420 ScreenManager& screenManager = ScreenManager::GetSingleton();
421 return screenManager.GetPrimaryScreen();
424 bool nsDeviceContext::CalcPrintingSize() {
425 gfxSize size(mPrintTarget->GetSize());
426 // For printing, CSS inches and physical inches are identical
427 // so it doesn't matter which we use here
428 mWidth = NSToCoordRound(size.width * AppUnitsPerPhysicalInch() /
429 POINTS_PER_INCH_FLOAT);
430 mHeight = NSToCoordRound(size.height * AppUnitsPerPhysicalInch() /
431 POINTS_PER_INCH_FLOAT);
433 return (mWidth > 0 && mHeight > 0);
436 bool nsDeviceContext::CheckDPIChange() {
437 int32_t oldDevPixels = mAppUnitsPerDevPixelAtUnitFullZoom;
438 int32_t oldInches = mAppUnitsPerPhysicalInch;
440 SetDPI();
442 return oldDevPixels != mAppUnitsPerDevPixelAtUnitFullZoom ||
443 oldInches != mAppUnitsPerPhysicalInch;
446 bool nsDeviceContext::SetFullZoom(float aScale) {
447 if (aScale <= 0) {
448 MOZ_ASSERT_UNREACHABLE("Invalid full zoom value");
449 return false;
451 int32_t oldAppUnitsPerDevPixel = mAppUnitsPerDevPixel;
452 mFullZoom = aScale;
453 UpdateAppUnitsForFullZoom();
454 return oldAppUnitsPerDevPixel != mAppUnitsPerDevPixel;
457 static int32_t ApplyFullZoom(int32_t aUnzoomedAppUnits, float aFullZoom) {
458 if (aFullZoom == 1.0f) {
459 return aUnzoomedAppUnits;
461 return std::max(1, NSToIntRound(float(aUnzoomedAppUnits) / aFullZoom));
464 int32_t nsDeviceContext::AppUnitsPerDevPixelInTopLevelChromePage() const {
465 // The only zoom that applies to chrome pages is the system zoom, if any.
466 return ApplyFullZoom(mAppUnitsPerDevPixelAtUnitFullZoom,
467 LookAndFeel::SystemZoomSettings().mFullZoom);
470 void nsDeviceContext::UpdateAppUnitsForFullZoom() {
471 mAppUnitsPerDevPixel =
472 ApplyFullZoom(mAppUnitsPerDevPixelAtUnitFullZoom, mFullZoom);
473 // adjust mFullZoom to reflect appunit rounding
474 mFullZoom = float(mAppUnitsPerDevPixelAtUnitFullZoom) / mAppUnitsPerDevPixel;
477 DesktopToLayoutDeviceScale nsDeviceContext::GetDesktopToDeviceScale() {
478 if (RefPtr<widget::Screen> screen = FindScreen()) {
479 return screen->GetDesktopToLayoutDeviceScale();
481 return DesktopToLayoutDeviceScale(1.0);