Bug 1700051: part 46) Const-qualify `mozInlineSpellStatus::mAnchorRange`. r=smaug
[gecko.git] / gfx / src / nsDeviceContext.cpp
blob3f55c4fdba7d58b8a209537998c9f7cee8ee08ea
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
25 #include "nsID.h"
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 {
48 public:
49 nsFontCache() : mContext(nullptr) {}
51 NS_DECL_THREADSAFE_ISUPPORTS
52 NS_DECL_NSIOBSERVER
54 void Init(nsDeviceContext* aContext);
55 void Destroy();
57 already_AddRefed<nsFontMetrics> GetMetricsFor(
58 const nsFont& aFont, const nsFontMetrics::Params& aParams);
60 void FontMetricsDeleted(const nsFontMetrics* aFontMetrics);
61 void Compact();
63 // Flush aFlushCount oldest entries, or all if aFlushCount is negative
64 void Flush(int32_t aFlushCount = -1);
66 void UpdateUserFonts(gfxUserFontSet* aUserFontSet);
68 protected:
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
86 // main thread.
87 AutoTArray<nsFontMetrics*, kMaxCacheEntries * 2> mFontMetrics;
89 bool mFlushPending = false;
91 class FlushFontMetricsTask : public mozilla::Runnable {
92 public:
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
97 // recent entries.
98 mCache->Flush(mCache->mFontMetrics.Length() - kMaxCacheEntries / 2);
99 mCache->mFlushPending = false;
100 return NS_OK;
103 private:
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) {
114 mContext = 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");
129 Flush();
132 NS_IMETHODIMP
133 nsFontCache::Observe(nsISupports*, const char* aTopic, const char16_t*) {
134 if (!nsCRT::strcmp(aTopic, "memory-pressure")) Compact();
135 return NS_OK;
138 already_AddRefed<nsFontMetrics> nsFontCache::GetMetricsFor(
139 const nsFont& aFont, const nsFontMetrics::Params& aParams) {
140 nsAtom* language = aParams.language && !aParams.language->IsEmpty()
141 ? aParams.language
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) {
155 if (i != n) {
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);
172 } else {
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());
185 return fm.forget();
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
203 // the way
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
208 // notified
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
214 NS_ADDREF(oldfm);
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
229 fm->Destroy();
230 NS_RELEASE(fm);
232 mFontMetrics.RemoveElementsAt(0, n);
235 nsDeviceContext::nsDeviceContext()
236 : mWidth(0),
237 mHeight(0),
238 mAppUnitsPerDevPixel(-1),
239 mAppUnitsPerDevPixelAtUnitFullZoom(-1),
240 mAppUnitsPerPhysicalInch(-1),
241 mFullZoom(1.0f),
242 mPrintingScale(1.0f),
243 mPrintingTranslate(gfxPoint(0, 0)),
244 mIsCurrentlyPrintingDoc(false)
245 #ifdef DEBUG
247 mIsInitialized(false)
248 #endif
250 MOZ_ASSERT(NS_IsMainThread(), "nsDeviceContext created off main thread");
253 nsDeviceContext::~nsDeviceContext() {
254 if (mFontCache) {
255 mFontCache->Destroy();
259 void nsDeviceContext::InitFontCache() {
260 if (!mFontCache) {
261 mFontCache = new nsFontCache();
262 mFontCache->Init(this);
266 void nsDeviceContext::UpdateFontCacheUserFonts(gfxUserFontSet* aUserFontSet) {
267 if (mFontCache) {
268 mFontCache->UpdateUserFonts(aUserFontSet);
272 already_AddRefed<nsFontMetrics> nsDeviceContext::GetMetricsFor(
273 const nsFont& aFont, const nsFontMetrics::Params& aParams) {
274 InitFontCache();
275 return mFontCache->GetMetricsFor(aFont, aParams);
278 nsresult nsDeviceContext::FlushFontCache(void) {
279 if (mFontCache) mFontCache->Flush();
280 return NS_OK;
283 nsresult nsDeviceContext::FontMetricsDeleted(
284 const nsFontMetrics* aFontMetrics) {
285 if (mFontCache) {
286 mFontCache->FontMetricsDeleted(aFontMetrics);
288 return NS_OK;
291 bool nsDeviceContext::IsPrinterContext() { return mPrintTarget != nullptr; }
293 void nsDeviceContext::SetDPI(double* aScale) {
294 float dpi;
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);
303 } else {
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);
315 if (prefDPI > 0) {
316 dpi = prefDPI;
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
321 // value as default.
322 if (dpi < 0) {
323 primaryScreen->GetDpi(&dpi);
325 if (prefDPI < 0) {
326 dpi = std::max(96.0f, dpi);
328 } else {
329 dpi = 96.0f;
332 double devPixelsPerCSSPixel;
333 if (aScale && *aScale > 0.0) {
334 // if caller provided a scale, we just use it
335 devPixelsPerCSSPixel = *aScale;
336 } else {
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
343 // value as default.
344 if (devPixelsPerCSSPixel < 0) {
345 primaryScreen->GetDefaultCSSScaleFactor(&devPixelsPerCSSPixel);
347 if (aScale) {
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) {
364 #ifdef DEBUG
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;
369 #endif
371 nsresult rv = NS_OK;
372 if (mScreenManager && mWidget == aWidget) return rv;
374 mWidget = aWidget;
375 SetDPI();
377 if (mScreenManager) return rv;
379 mScreenManager = do_GetService("@mozilla.org/gfx/screenmanager;1", &rv);
381 return 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();
402 } else {
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);
413 return nullptr;
416 #ifdef XP_MACOSX
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);
423 #endif
424 dt->AddUserData(&sDisablePixelSnapping, (void*)0x1, nullptr);
426 RefPtr<gfxContext> pContext = gfxContext::CreateOrNull(dt);
427 MOZ_ASSERT(pContext); // already checked draw target above
429 gfxMatrix transform;
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));
447 if (!screen) {
448 ScreenManager& screenManager = ScreenManager::GetSingleton();
449 screenManager.GetPrimaryScreen(getter_AddRefs(screen));
450 MOZ_ASSERT(screen);
452 screen->GetColorDepth(reinterpret_cast<int32_t*>(&aDepth));
454 return NS_OK;
457 nsresult nsDeviceContext::GetDeviceSurfaceDimensions(nscoord& aWidth,
458 nscoord& aHeight) {
459 if (IsPrinterContext()) {
460 aWidth = mWidth;
461 aHeight = mHeight;
462 } else {
463 nsRect area;
464 ComputeFullAreaUsingScreen(&area);
465 aWidth = area.Width();
466 aHeight = area.Height();
469 return NS_OK;
472 nsresult nsDeviceContext::GetRect(nsRect& aRect) {
473 if (IsPrinterContext()) {
474 aRect.SetRect(0, 0, mWidth, mHeight);
475 } else
476 ComputeFullAreaUsingScreen(&aRect);
478 return NS_OK;
481 nsresult nsDeviceContext::GetClientRect(nsRect& aRect) {
482 if (IsPrinterContext()) {
483 aRect.SetRect(0, 0, mWidth, mHeight);
484 } else
485 ComputeClientRectUsingScreen(&aRect);
487 return NS_OK;
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();
499 if (!mPrintTarget) {
500 return NS_ERROR_FAILURE;
503 mDeviceContextSpec = aDevice;
505 Init(nullptr);
507 if (!CalcPrintingSize()) {
508 return NS_ERROR_FAILURE;
511 return NS_OK;
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");
535 return rv;
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;
553 return rv;
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;
568 return rv;
571 nsresult nsDeviceContext::BeginPage(void) {
572 nsresult rv = NS_OK;
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();
586 return rv;
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
594 // change.
595 nsCOMPtr<nsIScreen> screen;
596 FindScreen(getter_AddRefs(screen));
597 if (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
614 // change.
615 nsCOMPtr<nsIScreen> screen;
616 FindScreen(getter_AddRefs(screen));
617 if (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();
632 // FindScreen
634 // Determines which screen intersects the largest area of the given surface.
636 void nsDeviceContext::FindScreen(nsIScreen** outScreen) {
637 if (!mWidget || !mScreenManager) {
638 return;
641 CheckDPIChange();
643 nsCOMPtr<nsIScreen> screen = mWidget->GetWidgetScreen();
644 screen.forget(outScreen);
646 if (!(*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;
667 SetDPI(aScale);
669 return oldDevPixels != mAppUnitsPerDevPixelAtUnitFullZoom ||
670 oldInches != mAppUnitsPerPhysicalInch;
673 bool nsDeviceContext::SetFullZoom(float aScale) {
674 if (aScale <= 0) {
675 MOZ_ASSERT_UNREACHABLE("Invalid full zoom value");
676 return false;
678 int32_t oldAppUnitsPerDevPixel = mAppUnitsPerDevPixel;
679 mFullZoom = aScale;
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));
695 if (screen) {
696 double scale;
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() {
715 if (mPrintTarget) {
716 mPrintTarget->UnregisterPageDoneCallback();