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.
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 | } |