1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set sw=2 ts=4 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 "gfxASurface.h" // for gfxASurface, etc
10 #include "gfxContext.h"
11 #include "gfxImageSurface.h" // for gfxImageSurface
12 #include "gfxPoint.h" // for gfxSize
13 #include "gfxTextRun.h" // for gfxFontGroup
14 #include "mozilla/Attributes.h" // for final
15 #include "mozilla/gfx/PathHelpers.h"
16 #include "mozilla/gfx/PrintTarget.h"
17 #include "mozilla/Preferences.h" // for Preferences
18 #include "mozilla/Services.h" // for GetObserverService
19 #include "mozilla/mozalloc.h" // for operator new
20 #include "nsCRT.h" // for nsCRT
21 #include "nsDebug.h" // for NS_ASSERTION, etc
22 #include "nsFont.h" // for nsFont
23 #include "nsFontMetrics.h" // for nsFontMetrics
24 #include "nsAtom.h" // for nsAtom, NS_Atomize
26 #include "nsIDeviceContextSpec.h" // for nsIDeviceContextSpec
27 #include "nsLanguageAtomService.h" // for nsLanguageAtomService
28 #include "nsIObserver.h" // for nsIObserver, etc
29 #include "nsIObserverService.h" // for nsIObserverService
30 #include "nsIScreen.h" // for nsIScreen
31 #include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
32 #include "nsISupportsUtils.h" // for NS_ADDREF, NS_RELEASE
33 #include "nsIWidget.h" // for nsIWidget, NS_NATIVE_WINDOW
34 #include "nsRect.h" // for nsRect
35 #include "nsServiceManagerUtils.h" // for do_GetService
36 #include "nsString.h" // for nsDependentString
37 #include "nsTArray.h" // for nsTArray, nsTArray_Impl
38 #include "nsThreadUtils.h" // for NS_IsMainThread
39 #include "mozilla/gfx/Logging.h"
40 #include "mozilla/widget/ScreenManager.h" // for ScreenManager
42 using namespace mozilla
;
43 using namespace mozilla::gfx
;
44 using mozilla::services::GetObserverService
;
45 using mozilla::widget::ScreenManager
;
47 class nsFontCache final
: public nsIObserver
{
49 nsFontCache() : mContext(nullptr) {}
51 NS_DECL_THREADSAFE_ISUPPORTS
54 void Init(nsDeviceContext
* aContext
);
57 already_AddRefed
<nsFontMetrics
> GetMetricsFor(
58 const nsFont
& aFont
, const nsFontMetrics::Params
& aParams
);
60 void FontMetricsDeleted(const nsFontMetrics
* aFontMetrics
);
63 // Flush aFlushCount oldest entries, or all if aFlushCount is negative
64 void Flush(int32_t aFlushCount
= -1);
66 void UpdateUserFonts(gfxUserFontSet
* aUserFontSet
);
69 // If the array of cached entries is about to exceed this threshold,
70 // we'll discard the oldest ones so as to keep the size reasonable.
71 // In practice, the great majority of cache hits are among the last
72 // few entries; keeping thousands of older entries becomes counter-
73 // productive because it can then take too long to scan the cache.
74 static const int32_t kMaxCacheEntries
= 128;
76 ~nsFontCache() = default;
78 nsDeviceContext
* mContext
; // owner
79 RefPtr
<nsAtom
> mLocaleLanguage
;
81 // We may not flush older entries immediately the array reaches
82 // kMaxCacheEntries length, because this usually happens on a stylo
83 // thread where we can't safely delete metrics objects. So we allocate an
84 // oversized autoarray buffer here, so that we're unlikely to overflow
85 // it and need separate heap allocation before the flush happens on the
87 AutoTArray
<nsFontMetrics
*, kMaxCacheEntries
* 2> mFontMetrics
;
89 bool mFlushPending
= false;
91 class FlushFontMetricsTask
: public mozilla::Runnable
{
93 explicit FlushFontMetricsTask(nsFontCache
* aCache
)
94 : mozilla::Runnable("FlushFontMetricsTask"), mCache(aCache
) {}
95 NS_IMETHOD
Run() override
{
96 // Partially flush the cache, leaving the kMaxCacheEntries/2 most
98 mCache
->Flush(mCache
->mFontMetrics
.Length() - kMaxCacheEntries
/ 2);
99 mCache
->mFlushPending
= false;
104 RefPtr
<nsFontCache
> mCache
;
108 NS_IMPL_ISUPPORTS(nsFontCache
, nsIObserver
)
110 // The Init and Destroy methods are necessary because it's not
111 // safe to call AddObserver from a constructor or RemoveObserver
112 // from a destructor. That should be fixed.
113 void nsFontCache::Init(nsDeviceContext
* aContext
) {
115 // register as a memory-pressure observer to free font resources
116 // in low-memory situations.
117 nsCOMPtr
<nsIObserverService
> obs
= GetObserverService();
118 if (obs
) obs
->AddObserver(this, "memory-pressure", false);
120 mLocaleLanguage
= nsLanguageAtomService::GetService()->GetLocaleLanguage();
121 if (!mLocaleLanguage
) {
122 mLocaleLanguage
= NS_Atomize("x-western");
126 void nsFontCache::Destroy() {
127 nsCOMPtr
<nsIObserverService
> obs
= GetObserverService();
128 if (obs
) obs
->RemoveObserver(this, "memory-pressure");
133 nsFontCache::Observe(nsISupports
*, const char* aTopic
, const char16_t
*) {
134 if (!nsCRT::strcmp(aTopic
, "memory-pressure")) Compact();
138 already_AddRefed
<nsFontMetrics
> nsFontCache::GetMetricsFor(
139 const nsFont
& aFont
, const nsFontMetrics::Params
& aParams
) {
140 nsAtom
* language
= aParams
.language
&& !aParams
.language
->IsEmpty()
142 : mLocaleLanguage
.get();
144 // First check our cache
145 // start from the end, which is where we put the most-recent-used element
146 const int32_t n
= mFontMetrics
.Length() - 1;
147 for (int32_t i
= n
; i
>= 0; --i
) {
148 nsFontMetrics
* fm
= mFontMetrics
[i
];
149 if (fm
->Font().Equals(aFont
) &&
150 fm
->GetUserFontSet() == aParams
.userFontSet
&&
151 fm
->Language() == language
&&
152 fm
->Orientation() == aParams
.orientation
&&
153 fm
->ExplicitLanguage() == aParams
.explicitLanguage
&&
154 fm
->GetThebesFontGroup()->GetFontMatchingStats() == aParams
.fontStats
) {
156 // promote it to the end of the cache
157 mFontMetrics
.RemoveElementAt(i
);
158 mFontMetrics
.AppendElement(fm
);
160 fm
->GetThebesFontGroup()->UpdateUserFonts();
161 return do_AddRef(fm
);
165 // It's not in the cache. Get font metrics and then cache them.
166 // If the cache has reached its size limit, drop the older half of the
167 // entries; but if we're on a stylo thread (the usual case), we have
168 // to post a task back to the main thread to do the flush.
169 if (n
>= kMaxCacheEntries
- 1 && !mFlushPending
) {
170 if (NS_IsMainThread()) {
171 Flush(mFontMetrics
.Length() - kMaxCacheEntries
/ 2);
173 mFlushPending
= true;
174 nsCOMPtr
<nsIRunnable
> flushTask
= new FlushFontMetricsTask(this);
175 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(flushTask
));
179 nsFontMetrics::Params params
= aParams
;
180 params
.language
= language
;
181 RefPtr
<nsFontMetrics
> fm
= new nsFontMetrics(aFont
, params
, mContext
);
182 // the mFontMetrics list has the "head" at the end, because append
183 // is cheaper than insert
184 mFontMetrics
.AppendElement(do_AddRef(fm
).take());
188 void nsFontCache::UpdateUserFonts(gfxUserFontSet
* aUserFontSet
) {
189 for (nsFontMetrics
* fm
: mFontMetrics
) {
190 gfxFontGroup
* fg
= fm
->GetThebesFontGroup();
191 if (fg
->GetUserFontSet() == aUserFontSet
) {
192 fg
->UpdateUserFonts();
197 void nsFontCache::FontMetricsDeleted(const nsFontMetrics
* aFontMetrics
) {
198 mFontMetrics
.RemoveElement(aFontMetrics
);
201 void nsFontCache::Compact() {
202 // Need to loop backward because the running element can be removed on
204 for (int32_t i
= mFontMetrics
.Length() - 1; i
>= 0; --i
) {
205 nsFontMetrics
* fm
= mFontMetrics
[i
];
206 nsFontMetrics
* oldfm
= fm
;
207 // Destroy() isn't here because we want our device context to be
209 NS_RELEASE(fm
); // this will reset fm to nullptr
210 // if the font is really gone, it would have called back in
211 // FontMetricsDeleted() and would have removed itself
212 if (mFontMetrics
.IndexOf(oldfm
) != mFontMetrics
.NoIndex
) {
213 // nope, the font is still there, so let's hold onto it too
219 // Flush the aFlushCount oldest entries, or all if (aFlushCount < 0)
220 void nsFontCache::Flush(int32_t aFlushCount
) {
221 int32_t n
= aFlushCount
< 0
222 ? mFontMetrics
.Length()
223 : std::min
<int32_t>(aFlushCount
, mFontMetrics
.Length());
224 for (int32_t i
= n
- 1; i
>= 0; --i
) {
225 nsFontMetrics
* fm
= mFontMetrics
[i
];
226 // Destroy() will unhook our device context from the fm so that we
227 // won't waste time in triggering the notification of
228 // FontMetricsDeleted() in the subsequent release
232 mFontMetrics
.RemoveElementsAt(0, n
);
235 nsDeviceContext::nsDeviceContext()
238 mAppUnitsPerDevPixel(-1),
239 mAppUnitsPerDevPixelAtUnitFullZoom(-1),
240 mAppUnitsPerPhysicalInch(-1),
242 mPrintingScale(1.0f
),
243 mPrintingTranslate(gfxPoint(0, 0)),
244 mIsCurrentlyPrintingDoc(false)
247 mIsInitialized(false)
250 MOZ_ASSERT(NS_IsMainThread(), "nsDeviceContext created off main thread");
253 nsDeviceContext::~nsDeviceContext() {
255 mFontCache
->Destroy();
259 void nsDeviceContext::InitFontCache() {
261 mFontCache
= new nsFontCache();
262 mFontCache
->Init(this);
266 void nsDeviceContext::UpdateFontCacheUserFonts(gfxUserFontSet
* aUserFontSet
) {
268 mFontCache
->UpdateUserFonts(aUserFontSet
);
272 already_AddRefed
<nsFontMetrics
> nsDeviceContext::GetMetricsFor(
273 const nsFont
& aFont
, const nsFontMetrics::Params
& aParams
) {
275 return mFontCache
->GetMetricsFor(aFont
, aParams
);
278 nsresult
nsDeviceContext::FlushFontCache(void) {
279 if (mFontCache
) mFontCache
->Flush();
283 nsresult
nsDeviceContext::FontMetricsDeleted(
284 const nsFontMetrics
* aFontMetrics
) {
286 mFontCache
->FontMetricsDeleted(aFontMetrics
);
291 bool nsDeviceContext::IsPrinterContext() { return mPrintTarget
!= nullptr; }
293 void nsDeviceContext::SetDPI(double* aScale
) {
296 // Use the printing DC to determine DPI values, if we have one.
297 if (mDeviceContextSpec
) {
298 dpi
= mDeviceContextSpec
->GetDPI();
299 mPrintingScale
= mDeviceContextSpec
->GetPrintingScale();
300 mPrintingTranslate
= mDeviceContextSpec
->GetPrintingTranslate();
301 mAppUnitsPerDevPixelAtUnitFullZoom
=
302 NS_lround((AppUnitsPerCSSPixel() * 96) / dpi
);
304 nsCOMPtr
<nsIScreen
> primaryScreen
;
305 ScreenManager
& screenManager
= ScreenManager::GetSingleton();
306 screenManager
.GetPrimaryScreen(getter_AddRefs(primaryScreen
));
307 MOZ_ASSERT(primaryScreen
);
309 // A value of -1 means use the maximum of 96 and the system DPI.
310 // A value of 0 means use the system DPI. A positive value is used as the
311 // DPI. This sets the physical size of a device pixel and thus controls the
312 // interpretation of physical units.
313 int32_t prefDPI
= Preferences::GetInt("layout.css.dpi", -1);
317 } else if (mWidget
) {
318 // PuppetWidget could return -1 if the value's not available yet.
319 dpi
= mWidget
->GetDPI();
320 // In case that the widget returns -1, use the primary screen's
323 primaryScreen
->GetDpi(&dpi
);
326 dpi
= std::max(96.0f
, dpi
);
332 double devPixelsPerCSSPixel
;
333 if (aScale
&& *aScale
> 0.0) {
334 // if caller provided a scale, we just use it
335 devPixelsPerCSSPixel
= *aScale
;
337 // otherwise get from the widget, and return it in aScale for
338 // the caller to pass to child contexts if needed
339 CSSToLayoutDeviceScale scale
=
340 mWidget
? mWidget
->GetDefaultScale() : CSSToLayoutDeviceScale(1.0);
341 devPixelsPerCSSPixel
= scale
.scale
;
342 // In case that the widget returns -1, use the primary screen's
344 if (devPixelsPerCSSPixel
< 0) {
345 primaryScreen
->GetDefaultCSSScaleFactor(&devPixelsPerCSSPixel
);
348 *aScale
= devPixelsPerCSSPixel
;
352 mAppUnitsPerDevPixelAtUnitFullZoom
=
353 std::max(1, NS_lround(AppUnitsPerCSSPixel() / devPixelsPerCSSPixel
));
356 NS_ASSERTION(dpi
!= -1.0, "no dpi set");
358 mAppUnitsPerPhysicalInch
=
359 NS_lround(dpi
* mAppUnitsPerDevPixelAtUnitFullZoom
);
360 UpdateAppUnitsForFullZoom();
363 nsresult
nsDeviceContext::Init(nsIWidget
* aWidget
) {
365 // We can't assert |!mIsInitialized| here since EndSwapDocShellsForDocument
366 // re-initializes nsDeviceContext objects. We can only assert in
367 // InitForPrinting (below).
368 mIsInitialized
= true;
372 if (mScreenManager
&& mWidget
== aWidget
) return rv
;
377 if (mScreenManager
) return rv
;
379 mScreenManager
= do_GetService("@mozilla.org/gfx/screenmanager;1", &rv
);
384 // XXX This is only for printing. We should make that obvious in the name.
385 already_AddRefed
<gfxContext
> nsDeviceContext::CreateRenderingContext() {
386 return CreateRenderingContextCommon(/* not a reference context */ false);
389 already_AddRefed
<gfxContext
>
390 nsDeviceContext::CreateReferenceRenderingContext() {
391 return CreateRenderingContextCommon(/* a reference context */ true);
394 already_AddRefed
<gfxContext
> nsDeviceContext::CreateRenderingContextCommon(
395 bool aWantReferenceContext
) {
396 MOZ_ASSERT(IsPrinterContext());
397 MOZ_ASSERT(mWidth
> 0 && mHeight
> 0);
399 RefPtr
<gfx::DrawTarget
> dt
;
400 if (aWantReferenceContext
) {
401 dt
= mPrintTarget
->GetReferenceDrawTarget();
403 // This will be null if e10s is disabled or print.print_via_parent=false.
404 RefPtr
<DrawEventRecorder
> recorder
;
405 mDeviceContextSpec
->GetDrawEventRecorder(getter_AddRefs(recorder
));
406 dt
= mPrintTarget
->MakeDrawTarget(gfx::IntSize(mWidth
, mHeight
), recorder
);
409 if (!dt
|| !dt
->IsValid()) {
410 gfxCriticalNote
<< "Failed to create draw target in device context sized "
411 << mWidth
<< "x" << mHeight
<< " and pointer "
412 << hexa(mPrintTarget
);
417 // The CGContextRef provided by PMSessionGetCGGraphicsContext is
418 // write-only, so we need to prevent gfxContext::PushGroupAndCopyBackground
419 // trying to read from it or else we'll crash.
420 // XXXjwatt Consider adding a MakeDrawTarget override to PrintTargetCG and
421 // moving this AddUserData call there.
422 dt
->AddUserData(&gfxContext::sDontUseAsSourceKey
, dt
, nullptr);
424 dt
->AddUserData(&sDisablePixelSnapping
, (void*)0x1, nullptr);
426 RefPtr
<gfxContext
> pContext
= gfxContext::CreateOrNull(dt
);
427 MOZ_ASSERT(pContext
); // already checked draw target above
430 transform
.PreTranslate(mPrintingTranslate
);
431 if (mPrintTarget
->RotateNeededForLandscape()) {
432 // Rotate page 90 degrees to draw landscape page on portrait paper
433 IntSize size
= mPrintTarget
->GetSize();
434 transform
.PreTranslate(gfxPoint(0, size
.width
));
435 gfxMatrix
rotate(0, -1, 1, 0, 0, 0);
436 transform
= rotate
* transform
;
438 transform
.PreScale(mPrintingScale
, mPrintingScale
);
440 pContext
->SetMatrixDouble(transform
);
441 return pContext
.forget();
444 nsresult
nsDeviceContext::GetDepth(uint32_t& aDepth
) {
445 nsCOMPtr
<nsIScreen
> screen
;
446 FindScreen(getter_AddRefs(screen
));
448 ScreenManager
& screenManager
= ScreenManager::GetSingleton();
449 screenManager
.GetPrimaryScreen(getter_AddRefs(screen
));
452 screen
->GetColorDepth(reinterpret_cast<int32_t*>(&aDepth
));
457 nsresult
nsDeviceContext::GetDeviceSurfaceDimensions(nscoord
& aWidth
,
459 if (IsPrinterContext()) {
464 ComputeFullAreaUsingScreen(&area
);
465 aWidth
= area
.Width();
466 aHeight
= area
.Height();
472 nsresult
nsDeviceContext::GetRect(nsRect
& aRect
) {
473 if (IsPrinterContext()) {
474 aRect
.SetRect(0, 0, mWidth
, mHeight
);
476 ComputeFullAreaUsingScreen(&aRect
);
481 nsresult
nsDeviceContext::GetClientRect(nsRect
& aRect
) {
482 if (IsPrinterContext()) {
483 aRect
.SetRect(0, 0, mWidth
, mHeight
);
485 ComputeClientRectUsingScreen(&aRect
);
490 nsresult
nsDeviceContext::InitForPrinting(nsIDeviceContextSpec
* aDevice
) {
491 NS_ENSURE_ARG_POINTER(aDevice
);
493 MOZ_ASSERT(!mIsInitialized
,
494 "Only initialize once, immediately after construction");
496 // We don't set mIsInitialized here. The Init() call below does that.
498 mPrintTarget
= aDevice
->MakePrintTarget();
500 return NS_ERROR_FAILURE
;
503 mDeviceContextSpec
= aDevice
;
507 if (!CalcPrintingSize()) {
508 return NS_ERROR_FAILURE
;
514 nsresult
nsDeviceContext::BeginDocument(const nsAString
& aTitle
,
515 const nsAString
& aPrintToFileName
,
516 int32_t aStartPage
, int32_t aEndPage
) {
517 MOZ_ASSERT(!mIsCurrentlyPrintingDoc
,
518 "Mismatched BeginDocument/EndDocument calls");
520 nsresult rv
= mPrintTarget
->BeginPrinting(aTitle
, aPrintToFileName
,
521 aStartPage
, aEndPage
);
523 if (NS_SUCCEEDED(rv
)) {
524 if (mDeviceContextSpec
) {
525 rv
= mDeviceContextSpec
->BeginDocument(aTitle
, aPrintToFileName
,
526 aStartPage
, aEndPage
);
528 mIsCurrentlyPrintingDoc
= true;
531 // Warn about any failure (except user cancelling):
532 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
) || rv
== NS_ERROR_ABORT
,
533 "nsDeviceContext::BeginDocument failed");
538 nsresult
nsDeviceContext::EndDocument(void) {
539 MOZ_ASSERT(mIsCurrentlyPrintingDoc
,
540 "Mismatched BeginDocument/EndDocument calls");
542 mIsCurrentlyPrintingDoc
= false;
544 nsresult rv
= mPrintTarget
->EndPrinting();
545 if (NS_SUCCEEDED(rv
)) {
546 mPrintTarget
->Finish();
549 if (mDeviceContextSpec
) mDeviceContextSpec
->EndDocument();
551 mPrintTarget
= nullptr;
556 nsresult
nsDeviceContext::AbortDocument(void) {
557 MOZ_ASSERT(mIsCurrentlyPrintingDoc
,
558 "Mismatched BeginDocument/EndDocument calls");
560 nsresult rv
= mPrintTarget
->AbortPrinting();
562 mIsCurrentlyPrintingDoc
= false;
564 if (mDeviceContextSpec
) mDeviceContextSpec
->EndDocument();
566 mPrintTarget
= nullptr;
571 nsresult
nsDeviceContext::BeginPage(void) {
574 if (mDeviceContextSpec
) rv
= mDeviceContextSpec
->BeginPage();
576 if (NS_FAILED(rv
)) return rv
;
578 return mPrintTarget
->BeginPage();
581 nsresult
nsDeviceContext::EndPage(void) {
582 nsresult rv
= mPrintTarget
->EndPage();
584 if (mDeviceContextSpec
) mDeviceContextSpec
->EndPage();
589 void nsDeviceContext::ComputeClientRectUsingScreen(nsRect
* outRect
) {
590 // we always need to recompute the clientRect
591 // because the window may have moved onto a different screen. In the single
592 // monitor case, we only need to do the computation if we haven't done it
593 // once already, and remember that we have because we're assured it won't
595 nsCOMPtr
<nsIScreen
> screen
;
596 FindScreen(getter_AddRefs(screen
));
598 int32_t x
, y
, width
, height
;
599 screen
->GetAvailRect(&x
, &y
, &width
, &height
);
601 // convert to device units
602 outRect
->SetRect(NSIntPixelsToAppUnits(x
, AppUnitsPerDevPixel()),
603 NSIntPixelsToAppUnits(y
, AppUnitsPerDevPixel()),
604 NSIntPixelsToAppUnits(width
, AppUnitsPerDevPixel()),
605 NSIntPixelsToAppUnits(height
, AppUnitsPerDevPixel()));
609 void nsDeviceContext::ComputeFullAreaUsingScreen(nsRect
* outRect
) {
610 // if we have more than one screen, we always need to recompute the clientRect
611 // because the window may have moved onto a different screen. In the single
612 // monitor case, we only need to do the computation if we haven't done it
613 // once already, and remember that we have because we're assured it won't
615 nsCOMPtr
<nsIScreen
> screen
;
616 FindScreen(getter_AddRefs(screen
));
618 int32_t x
, y
, width
, height
;
619 screen
->GetRect(&x
, &y
, &width
, &height
);
621 // convert to device units
622 outRect
->SetRect(NSIntPixelsToAppUnits(x
, AppUnitsPerDevPixel()),
623 NSIntPixelsToAppUnits(y
, AppUnitsPerDevPixel()),
624 NSIntPixelsToAppUnits(width
, AppUnitsPerDevPixel()),
625 NSIntPixelsToAppUnits(height
, AppUnitsPerDevPixel()));
626 mWidth
= outRect
->Width();
627 mHeight
= outRect
->Height();
634 // Determines which screen intersects the largest area of the given surface.
636 void nsDeviceContext::FindScreen(nsIScreen
** outScreen
) {
637 if (!mWidget
|| !mScreenManager
) {
643 nsCOMPtr
<nsIScreen
> screen
= mWidget
->GetWidgetScreen();
644 screen
.forget(outScreen
);
647 mScreenManager
->GetPrimaryScreen(outScreen
);
651 bool nsDeviceContext::CalcPrintingSize() {
652 gfxSize
size(mPrintTarget
->GetSize());
653 // For printing, CSS inches and physical inches are identical
654 // so it doesn't matter which we use here
655 mWidth
= NSToCoordRound(size
.width
* AppUnitsPerPhysicalInch() /
656 POINTS_PER_INCH_FLOAT
);
657 mHeight
= NSToCoordRound(size
.height
* AppUnitsPerPhysicalInch() /
658 POINTS_PER_INCH_FLOAT
);
660 return (mWidth
> 0 && mHeight
> 0);
663 bool nsDeviceContext::CheckDPIChange(double* aScale
) {
664 int32_t oldDevPixels
= mAppUnitsPerDevPixelAtUnitFullZoom
;
665 int32_t oldInches
= mAppUnitsPerPhysicalInch
;
669 return oldDevPixels
!= mAppUnitsPerDevPixelAtUnitFullZoom
||
670 oldInches
!= mAppUnitsPerPhysicalInch
;
673 bool nsDeviceContext::SetFullZoom(float aScale
) {
675 MOZ_ASSERT_UNREACHABLE("Invalid full zoom value");
678 int32_t oldAppUnitsPerDevPixel
= mAppUnitsPerDevPixel
;
680 UpdateAppUnitsForFullZoom();
681 return oldAppUnitsPerDevPixel
!= mAppUnitsPerDevPixel
;
684 void nsDeviceContext::UpdateAppUnitsForFullZoom() {
685 mAppUnitsPerDevPixel
= std::max(
686 1, NSToIntRound(float(mAppUnitsPerDevPixelAtUnitFullZoom
) / mFullZoom
));
687 // adjust mFullZoom to reflect appunit rounding
688 mFullZoom
= float(mAppUnitsPerDevPixelAtUnitFullZoom
) / mAppUnitsPerDevPixel
;
691 DesktopToLayoutDeviceScale
nsDeviceContext::GetDesktopToDeviceScale() {
692 nsCOMPtr
<nsIScreen
> screen
;
693 FindScreen(getter_AddRefs(screen
));
697 screen
->GetContentsScaleFactor(&scale
);
698 return DesktopToLayoutDeviceScale(scale
);
701 return DesktopToLayoutDeviceScale(1.0);
704 bool nsDeviceContext::IsSyncPagePrinting() const {
705 MOZ_ASSERT(mPrintTarget
);
706 return mPrintTarget
->IsSyncPagePrinting();
709 void nsDeviceContext::RegisterPageDoneCallback(
710 PrintTarget::PageDoneCallback
&& aCallback
) {
711 MOZ_ASSERT(mPrintTarget
&& aCallback
&& !IsSyncPagePrinting());
712 mPrintTarget
->RegisterPageDoneCallback(std::move(aCallback
));
714 void nsDeviceContext::UnregisterPageDoneCallback() {
716 mPrintTarget
->UnregisterPageDoneCallback();