gfx/src/nsDeviceContext.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

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.

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

mercurial