Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsDeviceContext.h"
7 #include <algorithm> // for max
8 #include "gfxASurface.h" // for gfxASurface, etc
9 #include "gfxFont.h" // for gfxFontGroup
10 #include "gfxImageSurface.h" // for gfxImageSurface
11 #include "gfxPoint.h" // for gfxSize
12 #include "mozilla/Attributes.h" // for MOZ_FINAL
13 #include "mozilla/Preferences.h" // for Preferences
14 #include "mozilla/Services.h" // for GetObserverService
15 #include "mozilla/mozalloc.h" // for operator new
16 #include "nsCRT.h" // for nsCRT
17 #include "nsDebug.h" // for NS_NOTREACHED, NS_ASSERTION, etc
18 #include "nsFont.h" // for nsFont
19 #include "nsFontMetrics.h" // for nsFontMetrics
20 #include "nsIAtom.h" // for nsIAtom, do_GetAtom
21 #include "nsID.h"
22 #include "nsIDeviceContextSpec.h" // for nsIDeviceContextSpec
23 #include "nsILanguageAtomService.h" // for nsILanguageAtomService, etc
24 #include "nsIObserver.h" // for nsIObserver, etc
25 #include "nsIObserverService.h" // for nsIObserverService
26 #include "nsIScreen.h" // for nsIScreen
27 #include "nsIScreenManager.h" // for nsIScreenManager
28 #include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
29 #include "nsISupportsUtils.h" // for NS_ADDREF, NS_RELEASE
30 #include "nsIWidget.h" // for nsIWidget, NS_NATIVE_WINDOW
31 #include "nsRect.h" // for nsRect
32 #include "nsRenderingContext.h" // for nsRenderingContext
33 #include "nsServiceManagerUtils.h" // for do_GetService
34 #include "nsString.h" // for nsDependentString
35 #include "nsTArray.h" // for nsTArray, nsTArray_Impl
36 #include "nsThreadUtils.h" // for NS_IsMainThread
38 #if !XP_MACOSX
39 #include "gfxPDFSurface.h"
40 #endif
42 #ifdef MOZ_WIDGET_GTK
43 #include "gfxPSSurface.h"
44 #elif XP_WIN
45 #include "gfxWindowsSurface.h"
46 #elif XP_MACOSX
47 #include "gfxQuartzSurface.h"
48 #endif
50 using namespace mozilla;
51 using mozilla::services::GetObserverService;
53 class nsFontCache MOZ_FINAL : public nsIObserver
54 {
55 public:
56 nsFontCache() { MOZ_COUNT_CTOR(nsFontCache); }
57 ~nsFontCache() { MOZ_COUNT_DTOR(nsFontCache); }
59 NS_DECL_ISUPPORTS
60 NS_DECL_NSIOBSERVER
62 void Init(nsDeviceContext* aContext);
63 void Destroy();
65 nsresult GetMetricsFor(const nsFont& aFont, nsIAtom* aLanguage,
66 gfxUserFontSet* aUserFontSet,
67 gfxTextPerfMetrics* aTextPerf,
68 nsFontMetrics*& aMetrics);
70 void FontMetricsDeleted(const nsFontMetrics* aFontMetrics);
71 void Compact();
72 void Flush();
74 protected:
75 nsDeviceContext* mContext; // owner
76 nsCOMPtr<nsIAtom> mLocaleLanguage;
77 nsTArray<nsFontMetrics*> mFontMetrics;
78 };
80 NS_IMPL_ISUPPORTS(nsFontCache, nsIObserver)
82 // The Init and Destroy methods are necessary because it's not
83 // safe to call AddObserver from a constructor or RemoveObserver
84 // from a destructor. That should be fixed.
85 void
86 nsFontCache::Init(nsDeviceContext* aContext)
87 {
88 mContext = aContext;
89 // register as a memory-pressure observer to free font resources
90 // in low-memory situations.
91 nsCOMPtr<nsIObserverService> obs = GetObserverService();
92 if (obs)
93 obs->AddObserver(this, "memory-pressure", false);
95 nsCOMPtr<nsILanguageAtomService> langService;
96 langService = do_GetService(NS_LANGUAGEATOMSERVICE_CONTRACTID);
97 if (langService) {
98 mLocaleLanguage = langService->GetLocaleLanguage();
99 }
100 if (!mLocaleLanguage) {
101 mLocaleLanguage = do_GetAtom("x-western");
102 }
103 }
105 void
106 nsFontCache::Destroy()
107 {
108 nsCOMPtr<nsIObserverService> obs = GetObserverService();
109 if (obs)
110 obs->RemoveObserver(this, "memory-pressure");
111 Flush();
112 }
114 NS_IMETHODIMP
115 nsFontCache::Observe(nsISupports*, const char* aTopic, const char16_t*)
116 {
117 if (!nsCRT::strcmp(aTopic, "memory-pressure"))
118 Compact();
119 return NS_OK;
120 }
122 nsresult
123 nsFontCache::GetMetricsFor(const nsFont& aFont, nsIAtom* aLanguage,
124 gfxUserFontSet* aUserFontSet,
125 gfxTextPerfMetrics* aTextPerf,
126 nsFontMetrics*& aMetrics)
127 {
128 if (!aLanguage)
129 aLanguage = mLocaleLanguage;
131 // First check our cache
132 // start from the end, which is where we put the most-recent-used element
134 nsFontMetrics* fm;
135 int32_t n = mFontMetrics.Length() - 1;
136 for (int32_t i = n; i >= 0; --i) {
137 fm = mFontMetrics[i];
138 if (fm->Font().Equals(aFont) && fm->GetUserFontSet() == aUserFontSet &&
139 fm->Language() == aLanguage) {
140 if (i != n) {
141 // promote it to the end of the cache
142 mFontMetrics.RemoveElementAt(i);
143 mFontMetrics.AppendElement(fm);
144 }
145 fm->GetThebesFontGroup()->UpdateFontList();
146 NS_ADDREF(aMetrics = fm);
147 return NS_OK;
148 }
149 }
151 // It's not in the cache. Get font metrics and then cache them.
153 fm = new nsFontMetrics();
154 NS_ADDREF(fm);
155 nsresult rv = fm->Init(aFont, aLanguage, mContext, aUserFontSet, aTextPerf);
156 if (NS_SUCCEEDED(rv)) {
157 // the mFontMetrics list has the "head" at the end, because append
158 // is cheaper than insert
159 mFontMetrics.AppendElement(fm);
160 aMetrics = fm;
161 NS_ADDREF(aMetrics);
162 return NS_OK;
163 }
164 fm->Destroy();
165 NS_RELEASE(fm);
167 // One reason why Init() fails is because the system is running out of
168 // resources. e.g., on Win95/98 only a very limited number of GDI
169 // objects are available. Compact the cache and try again.
171 Compact();
172 fm = new nsFontMetrics();
173 NS_ADDREF(fm);
174 rv = fm->Init(aFont, aLanguage, mContext, aUserFontSet, aTextPerf);
175 if (NS_SUCCEEDED(rv)) {
176 mFontMetrics.AppendElement(fm);
177 aMetrics = fm;
178 return NS_OK;
179 }
180 fm->Destroy();
181 NS_RELEASE(fm);
183 // could not setup a new one, send an old one (XXX search a "best
184 // match"?)
186 n = mFontMetrics.Length() - 1; // could have changed in Compact()
187 if (n >= 0) {
188 aMetrics = mFontMetrics[n];
189 NS_ADDREF(aMetrics);
190 return NS_OK;
191 }
193 NS_POSTCONDITION(NS_SUCCEEDED(rv),
194 "font metrics should not be null - bug 136248");
195 return rv;
196 }
198 void
199 nsFontCache::FontMetricsDeleted(const nsFontMetrics* aFontMetrics)
200 {
201 mFontMetrics.RemoveElement(aFontMetrics);
202 }
204 void
205 nsFontCache::Compact()
206 {
207 // Need to loop backward because the running element can be removed on
208 // the way
209 for (int32_t i = mFontMetrics.Length()-1; i >= 0; --i) {
210 nsFontMetrics* fm = mFontMetrics[i];
211 nsFontMetrics* oldfm = fm;
212 // Destroy() isn't here because we want our device context to be
213 // notified
214 NS_RELEASE(fm); // this will reset fm to nullptr
215 // if the font is really gone, it would have called back in
216 // FontMetricsDeleted() and would have removed itself
217 if (mFontMetrics.IndexOf(oldfm) != mFontMetrics.NoIndex) {
218 // nope, the font is still there, so let's hold onto it too
219 NS_ADDREF(oldfm);
220 }
221 }
222 }
224 void
225 nsFontCache::Flush()
226 {
227 for (int32_t i = mFontMetrics.Length()-1; i >= 0; --i) {
228 nsFontMetrics* fm = mFontMetrics[i];
229 // Destroy() will unhook our device context from the fm so that we
230 // won't waste time in triggering the notification of
231 // FontMetricsDeleted() in the subsequent release
232 fm->Destroy();
233 NS_RELEASE(fm);
234 }
235 mFontMetrics.Clear();
236 }
238 nsDeviceContext::nsDeviceContext()
239 : mWidth(0), mHeight(0), mDepth(0),
240 mAppUnitsPerDevPixel(-1), mAppUnitsPerDevNotScaledPixel(-1),
241 mAppUnitsPerPhysicalInch(-1),
242 mPixelScale(1.0f), mPrintingScale(1.0f),
243 mFontCache(nullptr)
244 {
245 MOZ_ASSERT(NS_IsMainThread(), "nsDeviceContext created off main thread");
246 }
248 // Note: we use a bare pointer for mFontCache so that nsFontCache
249 // can be an incomplete type in nsDeviceContext.h.
250 // Therefore we have to do all the refcounting by hand.
251 nsDeviceContext::~nsDeviceContext()
252 {
253 if (mFontCache) {
254 mFontCache->Destroy();
255 NS_RELEASE(mFontCache);
256 }
257 }
259 nsresult
260 nsDeviceContext::GetMetricsFor(const nsFont& aFont,
261 nsIAtom* aLanguage,
262 gfxUserFontSet* aUserFontSet,
263 gfxTextPerfMetrics* aTextPerf,
264 nsFontMetrics*& aMetrics)
265 {
266 if (!mFontCache) {
267 mFontCache = new nsFontCache();
268 NS_ADDREF(mFontCache);
269 mFontCache->Init(this);
270 }
272 return mFontCache->GetMetricsFor(aFont, aLanguage, aUserFontSet,
273 aTextPerf, aMetrics);
274 }
276 nsresult
277 nsDeviceContext::FlushFontCache(void)
278 {
279 if (mFontCache)
280 mFontCache->Flush();
281 return NS_OK;
282 }
284 nsresult
285 nsDeviceContext::FontMetricsDeleted(const nsFontMetrics* aFontMetrics)
286 {
287 if (mFontCache) {
288 mFontCache->FontMetricsDeleted(aFontMetrics);
289 }
290 return NS_OK;
291 }
293 bool
294 nsDeviceContext::IsPrinterSurface()
295 {
296 return mPrintingSurface != nullptr;
297 }
299 void
300 nsDeviceContext::SetDPI()
301 {
302 float dpi = -1.0f;
304 // PostScript, PDF and Mac (when printing) all use 72 dpi
305 // Use a printing DC to determine the other dpi values
306 if (mPrintingSurface) {
307 switch (mPrintingSurface->GetType()) {
308 case gfxSurfaceType::PDF:
309 case gfxSurfaceType::PS:
310 case gfxSurfaceType::Quartz:
311 dpi = 72.0f;
312 break;
313 #ifdef XP_WIN
314 case gfxSurfaceType::Win32:
315 case gfxSurfaceType::Win32Printing: {
316 HDC dc = reinterpret_cast<gfxWindowsSurface*>(mPrintingSurface.get())->GetDC();
317 int32_t OSVal = GetDeviceCaps(dc, LOGPIXELSY);
318 dpi = 144.0f;
319 mPrintingScale = float(OSVal) / dpi;
320 break;
321 }
322 #endif
323 default:
324 NS_NOTREACHED("Unexpected printing surface type");
325 break;
326 }
328 mAppUnitsPerDevNotScaledPixel =
329 NS_lround((AppUnitsPerCSSPixel() * 96) / dpi);
330 } else {
331 // A value of -1 means use the maximum of 96 and the system DPI.
332 // A value of 0 means use the system DPI. A positive value is used as the DPI.
333 // This sets the physical size of a device pixel and thus controls the
334 // interpretation of physical units.
335 int32_t prefDPI = Preferences::GetInt("layout.css.dpi", -1);
337 if (prefDPI > 0) {
338 dpi = prefDPI;
339 } else if (mWidget) {
340 dpi = mWidget->GetDPI();
342 if (prefDPI < 0) {
343 dpi = std::max(96.0f, dpi);
344 }
345 } else {
346 dpi = 96.0f;
347 }
349 CSSToLayoutDeviceScale scale = mWidget ? mWidget->GetDefaultScale()
350 : CSSToLayoutDeviceScale(1.0);
351 double devPixelsPerCSSPixel = scale.scale;
353 mAppUnitsPerDevNotScaledPixel =
354 std::max(1, NS_lround(AppUnitsPerCSSPixel() / devPixelsPerCSSPixel));
355 }
357 NS_ASSERTION(dpi != -1.0, "no dpi set");
359 mAppUnitsPerPhysicalInch = NS_lround(dpi * mAppUnitsPerDevNotScaledPixel);
360 UpdateScaledAppUnits();
361 }
363 nsresult
364 nsDeviceContext::Init(nsIWidget *aWidget)
365 {
366 if (mScreenManager && mWidget == aWidget)
367 return NS_OK;
369 mWidget = aWidget;
370 SetDPI();
372 if (mScreenManager)
373 return NS_OK;
375 mScreenManager = do_GetService("@mozilla.org/gfx/screenmanager;1");
377 return NS_OK;
378 }
380 already_AddRefed<nsRenderingContext>
381 nsDeviceContext::CreateRenderingContext()
382 {
383 nsRefPtr<gfxASurface> printingSurface = mPrintingSurface;
384 #ifdef XP_MACOSX
385 // CreateRenderingContext() can be called (on reflow) after EndPage()
386 // but before BeginPage(). On OS X (and only there) mPrintingSurface
387 // will in this case be null, because OS X printing surfaces are
388 // per-page, and therefore only truly valid between calls to BeginPage()
389 // and EndPage(). But we can get away with fudging things here, if need
390 // be, by using a cached copy.
391 if (!printingSurface) {
392 printingSurface = mCachedPrintingSurface;
393 }
394 #endif
395 nsRefPtr<nsRenderingContext> pContext = new nsRenderingContext();
397 RefPtr<gfx::DrawTarget> dt =
398 gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(printingSurface,
399 gfx::IntSize(mWidth, mHeight));
401 pContext->Init(this, dt);
402 pContext->ThebesContext()->SetFlag(gfxContext::FLAG_DISABLE_SNAPPING);
403 pContext->Scale(mPrintingScale, mPrintingScale);
405 return pContext.forget();
406 }
408 nsresult
409 nsDeviceContext::GetDepth(uint32_t& aDepth)
410 {
411 if (mDepth == 0) {
412 nsCOMPtr<nsIScreen> primaryScreen;
413 mScreenManager->GetPrimaryScreen(getter_AddRefs(primaryScreen));
414 primaryScreen->GetColorDepth(reinterpret_cast<int32_t *>(&mDepth));
415 }
417 aDepth = mDepth;
418 return NS_OK;
419 }
421 nsresult
422 nsDeviceContext::GetDeviceSurfaceDimensions(nscoord &aWidth, nscoord &aHeight)
423 {
424 if (mPrintingSurface) {
425 // we have a printer device
426 aWidth = mWidth;
427 aHeight = mHeight;
428 } else {
429 nsRect area;
430 ComputeFullAreaUsingScreen(&area);
431 aWidth = area.width;
432 aHeight = area.height;
433 }
435 return NS_OK;
436 }
438 nsresult
439 nsDeviceContext::GetRect(nsRect &aRect)
440 {
441 if (mPrintingSurface) {
442 // we have a printer device
443 aRect.x = 0;
444 aRect.y = 0;
445 aRect.width = mWidth;
446 aRect.height = mHeight;
447 } else
448 ComputeFullAreaUsingScreen ( &aRect );
450 return NS_OK;
451 }
453 nsresult
454 nsDeviceContext::GetClientRect(nsRect &aRect)
455 {
456 if (mPrintingSurface) {
457 // we have a printer device
458 aRect.x = 0;
459 aRect.y = 0;
460 aRect.width = mWidth;
461 aRect.height = mHeight;
462 }
463 else
464 ComputeClientRectUsingScreen(&aRect);
466 return NS_OK;
467 }
469 nsresult
470 nsDeviceContext::InitForPrinting(nsIDeviceContextSpec *aDevice)
471 {
472 NS_ENSURE_ARG_POINTER(aDevice);
474 mDeviceContextSpec = aDevice;
476 nsresult rv = aDevice->GetSurfaceForPrinter(getter_AddRefs(mPrintingSurface));
477 if (NS_FAILED(rv))
478 return NS_ERROR_FAILURE;
480 Init(nullptr);
482 CalcPrintingSize();
484 return NS_OK;
485 }
487 nsresult
488 nsDeviceContext::BeginDocument(const nsAString& aTitle,
489 char16_t* aPrintToFileName,
490 int32_t aStartPage,
491 int32_t aEndPage)
492 {
493 static const char16_t kEmpty[] = { '\0' };
494 nsresult rv;
496 rv = mPrintingSurface->BeginPrinting(aTitle,
497 nsDependentString(aPrintToFileName ? aPrintToFileName : kEmpty));
499 if (NS_SUCCEEDED(rv) && mDeviceContextSpec)
500 rv = mDeviceContextSpec->BeginDocument(aTitle, aPrintToFileName, aStartPage, aEndPage);
502 return rv;
503 }
506 nsresult
507 nsDeviceContext::EndDocument(void)
508 {
509 nsresult rv = NS_OK;
511 if (mPrintingSurface) {
512 rv = mPrintingSurface->EndPrinting();
513 if (NS_SUCCEEDED(rv))
514 mPrintingSurface->Finish();
515 }
517 if (mDeviceContextSpec)
518 mDeviceContextSpec->EndDocument();
520 return rv;
521 }
524 nsresult
525 nsDeviceContext::AbortDocument(void)
526 {
527 nsresult rv = mPrintingSurface->AbortPrinting();
529 if (mDeviceContextSpec)
530 mDeviceContextSpec->EndDocument();
532 return rv;
533 }
536 nsresult
537 nsDeviceContext::BeginPage(void)
538 {
539 nsresult rv = NS_OK;
541 if (mDeviceContextSpec)
542 rv = mDeviceContextSpec->BeginPage();
544 if (NS_FAILED(rv)) return rv;
546 #ifdef XP_MACOSX
547 // We need to get a new surface for each page on the Mac, as the
548 // CGContextRefs are only good for one page.
549 mDeviceContextSpec->GetSurfaceForPrinter(getter_AddRefs(mPrintingSurface));
550 #endif
552 rv = mPrintingSurface->BeginPage();
554 return rv;
555 }
557 nsresult
558 nsDeviceContext::EndPage(void)
559 {
560 nsresult rv = mPrintingSurface->EndPage();
562 #ifdef XP_MACOSX
563 // We need to release the CGContextRef in the surface here, plus it's
564 // not something you would want anyway, as these CGContextRefs are only
565 // good for one page. But we need to keep a cached reference to it, since
566 // CreateRenderingContext() may try to access it when mPrintingSurface
567 // would normally be null. See bug 665218. If we just stop nulling out
568 // mPrintingSurface here (and thereby make that our cached copy), we'll
569 // break all our null checks on mPrintingSurface. See bug 684622.
570 mCachedPrintingSurface = mPrintingSurface;
571 mPrintingSurface = nullptr;
572 #endif
574 if (mDeviceContextSpec)
575 mDeviceContextSpec->EndPage();
577 return rv;
578 }
580 void
581 nsDeviceContext::ComputeClientRectUsingScreen(nsRect* outRect)
582 {
583 // we always need to recompute the clientRect
584 // because the window may have moved onto a different screen. In the single
585 // monitor case, we only need to do the computation if we haven't done it
586 // once already, and remember that we have because we're assured it won't change.
587 nsCOMPtr<nsIScreen> screen;
588 FindScreen (getter_AddRefs(screen));
589 if (screen) {
590 int32_t x, y, width, height;
591 screen->GetAvailRect(&x, &y, &width, &height);
593 // convert to device units
594 outRect->y = NSIntPixelsToAppUnits(y, AppUnitsPerDevPixel());
595 outRect->x = NSIntPixelsToAppUnits(x, AppUnitsPerDevPixel());
596 outRect->width = NSIntPixelsToAppUnits(width, AppUnitsPerDevPixel());
597 outRect->height = NSIntPixelsToAppUnits(height, AppUnitsPerDevPixel());
598 }
599 }
601 void
602 nsDeviceContext::ComputeFullAreaUsingScreen(nsRect* outRect)
603 {
604 // if we have more than one screen, we always need to recompute the clientRect
605 // because the window may have moved onto a different screen. In the single
606 // monitor case, we only need to do the computation if we haven't done it
607 // once already, and remember that we have because we're assured it won't change.
608 nsCOMPtr<nsIScreen> screen;
609 FindScreen ( getter_AddRefs(screen) );
610 if ( screen ) {
611 int32_t x, y, width, height;
612 screen->GetRect ( &x, &y, &width, &height );
614 // convert to device units
615 outRect->y = NSIntPixelsToAppUnits(y, AppUnitsPerDevPixel());
616 outRect->x = NSIntPixelsToAppUnits(x, AppUnitsPerDevPixel());
617 outRect->width = NSIntPixelsToAppUnits(width, AppUnitsPerDevPixel());
618 outRect->height = NSIntPixelsToAppUnits(height, AppUnitsPerDevPixel());
620 mWidth = outRect->width;
621 mHeight = outRect->height;
622 }
623 }
625 //
626 // FindScreen
627 //
628 // Determines which screen intersects the largest area of the given surface.
629 //
630 void
631 nsDeviceContext::FindScreen(nsIScreen** outScreen)
632 {
633 if (mWidget && mWidget->GetNativeData(NS_NATIVE_WINDOW))
634 mScreenManager->ScreenForNativeWidget(mWidget->GetNativeData(NS_NATIVE_WINDOW),
635 outScreen);
636 else
637 mScreenManager->GetPrimaryScreen(outScreen);
638 }
640 void
641 nsDeviceContext::CalcPrintingSize()
642 {
643 if (!mPrintingSurface)
644 return;
646 bool inPoints = true;
648 gfxSize size(0, 0);
649 switch (mPrintingSurface->GetType()) {
650 case gfxSurfaceType::Image:
651 inPoints = false;
652 size = reinterpret_cast<gfxImageSurface*>(mPrintingSurface.get())->GetSize();
653 break;
655 #if defined(MOZ_PDF_PRINTING)
656 case gfxSurfaceType::PDF:
657 inPoints = true;
658 size = reinterpret_cast<gfxPDFSurface*>(mPrintingSurface.get())->GetSize();
659 break;
660 #endif
662 #ifdef MOZ_WIDGET_GTK
663 case gfxSurfaceType::PS:
664 inPoints = true;
665 size = reinterpret_cast<gfxPSSurface*>(mPrintingSurface.get())->GetSize();
666 break;
667 #endif
669 #ifdef XP_MACOSX
670 case gfxSurfaceType::Quartz:
671 inPoints = true; // this is really only true when we're printing
672 size = reinterpret_cast<gfxQuartzSurface*>(mPrintingSurface.get())->GetSize();
673 break;
674 #endif
676 #ifdef XP_WIN
677 case gfxSurfaceType::Win32:
678 case gfxSurfaceType::Win32Printing:
679 {
680 inPoints = false;
681 HDC dc = reinterpret_cast<gfxWindowsSurface*>(mPrintingSurface.get())->GetDC();
682 if (!dc)
683 dc = GetDC((HWND)mWidget->GetNativeData(NS_NATIVE_WIDGET));
684 size.width = NSFloatPixelsToAppUnits(::GetDeviceCaps(dc, HORZRES)/mPrintingScale, AppUnitsPerDevPixel());
685 size.height = NSFloatPixelsToAppUnits(::GetDeviceCaps(dc, VERTRES)/mPrintingScale, AppUnitsPerDevPixel());
686 mDepth = (uint32_t)::GetDeviceCaps(dc, BITSPIXEL);
687 if (dc != reinterpret_cast<gfxWindowsSurface*>(mPrintingSurface.get())->GetDC())
688 ReleaseDC((HWND)mWidget->GetNativeData(NS_NATIVE_WIDGET), dc);
689 break;
690 }
691 #endif
693 default:
694 NS_ERROR("trying to print to unknown surface type");
695 }
697 if (inPoints) {
698 // For printing, CSS inches and physical inches are identical
699 // so it doesn't matter which we use here
700 mWidth = NSToCoordRound(float(size.width) * AppUnitsPerPhysicalInch() / 72);
701 mHeight = NSToCoordRound(float(size.height) * AppUnitsPerPhysicalInch() / 72);
702 } else {
703 mWidth = NSToIntRound(size.width);
704 mHeight = NSToIntRound(size.height);
705 }
706 }
708 bool nsDeviceContext::CheckDPIChange() {
709 int32_t oldDevPixels = mAppUnitsPerDevNotScaledPixel;
710 int32_t oldInches = mAppUnitsPerPhysicalInch;
712 SetDPI();
714 return oldDevPixels != mAppUnitsPerDevNotScaledPixel ||
715 oldInches != mAppUnitsPerPhysicalInch;
716 }
718 bool
719 nsDeviceContext::SetPixelScale(float aScale)
720 {
721 if (aScale <= 0) {
722 NS_NOTREACHED("Invalid pixel scale value");
723 return false;
724 }
725 int32_t oldAppUnitsPerDevPixel = mAppUnitsPerDevPixel;
726 mPixelScale = aScale;
727 UpdateScaledAppUnits();
728 return oldAppUnitsPerDevPixel != mAppUnitsPerDevPixel;
729 }
731 void
732 nsDeviceContext::UpdateScaledAppUnits()
733 {
734 mAppUnitsPerDevPixel =
735 std::max(1, NSToIntRound(float(mAppUnitsPerDevNotScaledPixel) / mPixelScale));
736 // adjust mPixelScale to reflect appunit rounding
737 mPixelScale = float(mAppUnitsPerDevNotScaledPixel) / mAppUnitsPerDevPixel;
738 }