summaryrefslogtreecommitdiffstats
path: root/gfx/src/nsDeviceContext.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/src/nsDeviceContext.cpp')
-rw-r--r--gfx/src/nsDeviceContext.cpp717
1 files changed, 717 insertions, 0 deletions
diff --git a/gfx/src/nsDeviceContext.cpp b/gfx/src/nsDeviceContext.cpp
new file mode 100644
index 0000000000..daa592e1bf
--- /dev/null
+++ b/gfx/src/nsDeviceContext.cpp
@@ -0,0 +1,717 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=4 expandtab: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsDeviceContext.h"
+#include <algorithm> // for max
+#include "gfxASurface.h" // for gfxASurface, etc
+#include "gfxContext.h"
+#include "gfxImageSurface.h" // for gfxImageSurface
+#include "gfxPoint.h" // for gfxSize
+#include "gfxTextRun.h" // for gfxFontGroup
+#include "mozilla/Attributes.h" // for final
+#include "mozilla/gfx/PathHelpers.h"
+#include "mozilla/gfx/PrintTarget.h"
+#include "mozilla/Preferences.h" // for Preferences
+#include "mozilla/Services.h" // for GetObserverService
+#include "mozilla/mozalloc.h" // for operator new
+#include "nsCRT.h" // for nsCRT
+#include "nsDebug.h" // for NS_ASSERTION, etc
+#include "nsFont.h" // for nsFont
+#include "nsFontMetrics.h" // for nsFontMetrics
+#include "nsAtom.h" // for nsAtom, NS_Atomize
+#include "nsID.h"
+#include "nsIDeviceContextSpec.h" // for nsIDeviceContextSpec
+#include "nsLanguageAtomService.h" // for nsLanguageAtomService
+#include "nsIObserver.h" // for nsIObserver, etc
+#include "nsIObserverService.h" // for nsIObserverService
+#include "nsIScreen.h" // for nsIScreen
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsISupportsUtils.h" // for NS_ADDREF, NS_RELEASE
+#include "nsIWidget.h" // for nsIWidget, NS_NATIVE_WINDOW
+#include "nsRect.h" // for nsRect
+#include "nsServiceManagerUtils.h" // for do_GetService
+#include "nsString.h" // for nsDependentString
+#include "nsTArray.h" // for nsTArray, nsTArray_Impl
+#include "nsThreadUtils.h" // for NS_IsMainThread
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/widget/ScreenManager.h" // for ScreenManager
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+using mozilla::services::GetObserverService;
+using mozilla::widget::ScreenManager;
+
+class nsFontCache final : public nsIObserver {
+ public:
+ nsFontCache() : mContext(nullptr) {}
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+ void Init(nsDeviceContext* aContext);
+ void Destroy();
+
+ already_AddRefed<nsFontMetrics> GetMetricsFor(
+ const nsFont& aFont, const nsFontMetrics::Params& aParams);
+
+ void FontMetricsDeleted(const nsFontMetrics* aFontMetrics);
+ void Compact();
+
+ // Flush aFlushCount oldest entries, or all if aFlushCount is negative
+ void Flush(int32_t aFlushCount = -1);
+
+ void UpdateUserFonts(gfxUserFontSet* aUserFontSet);
+
+ protected:
+ // If the array of cached entries is about to exceed this threshold,
+ // we'll discard the oldest ones so as to keep the size reasonable.
+ // In practice, the great majority of cache hits are among the last
+ // few entries; keeping thousands of older entries becomes counter-
+ // productive because it can then take too long to scan the cache.
+ static const int32_t kMaxCacheEntries = 128;
+
+ ~nsFontCache() = default;
+
+ nsDeviceContext* mContext; // owner
+ RefPtr<nsAtom> mLocaleLanguage;
+
+ // We may not flush older entries immediately the array reaches
+ // kMaxCacheEntries length, because this usually happens on a stylo
+ // thread where we can't safely delete metrics objects. So we allocate an
+ // oversized autoarray buffer here, so that we're unlikely to overflow
+ // it and need separate heap allocation before the flush happens on the
+ // main thread.
+ AutoTArray<nsFontMetrics*, kMaxCacheEntries * 2> mFontMetrics;
+
+ bool mFlushPending = false;
+
+ class FlushFontMetricsTask : public mozilla::Runnable {
+ public:
+ explicit FlushFontMetricsTask(nsFontCache* aCache)
+ : mozilla::Runnable("FlushFontMetricsTask"), mCache(aCache) {}
+ NS_IMETHOD Run() override {
+ // Partially flush the cache, leaving the kMaxCacheEntries/2 most
+ // recent entries.
+ mCache->Flush(mCache->mFontMetrics.Length() - kMaxCacheEntries / 2);
+ mCache->mFlushPending = false;
+ return NS_OK;
+ }
+
+ private:
+ RefPtr<nsFontCache> mCache;
+ };
+};
+
+NS_IMPL_ISUPPORTS(nsFontCache, nsIObserver)
+
+// The Init and Destroy methods are necessary because it's not
+// safe to call AddObserver from a constructor or RemoveObserver
+// from a destructor. That should be fixed.
+void nsFontCache::Init(nsDeviceContext* aContext) {
+ mContext = aContext;
+ // register as a memory-pressure observer to free font resources
+ // in low-memory situations.
+ nsCOMPtr<nsIObserverService> obs = GetObserverService();
+ if (obs) obs->AddObserver(this, "memory-pressure", false);
+
+ mLocaleLanguage = nsLanguageAtomService::GetService()->GetLocaleLanguage();
+ if (!mLocaleLanguage) {
+ mLocaleLanguage = NS_Atomize("x-western");
+ }
+}
+
+void nsFontCache::Destroy() {
+ nsCOMPtr<nsIObserverService> obs = GetObserverService();
+ if (obs) obs->RemoveObserver(this, "memory-pressure");
+ Flush();
+}
+
+NS_IMETHODIMP
+nsFontCache::Observe(nsISupports*, const char* aTopic, const char16_t*) {
+ if (!nsCRT::strcmp(aTopic, "memory-pressure")) Compact();
+ return NS_OK;
+}
+
+already_AddRefed<nsFontMetrics> nsFontCache::GetMetricsFor(
+ const nsFont& aFont, const nsFontMetrics::Params& aParams) {
+ nsAtom* language = aParams.language && !aParams.language->IsEmpty()
+ ? aParams.language
+ : mLocaleLanguage.get();
+
+ // First check our cache
+ // start from the end, which is where we put the most-recent-used element
+ const int32_t n = mFontMetrics.Length() - 1;
+ for (int32_t i = n; i >= 0; --i) {
+ nsFontMetrics* fm = mFontMetrics[i];
+ if (fm->Font().Equals(aFont) &&
+ fm->GetUserFontSet() == aParams.userFontSet &&
+ fm->Language() == language &&
+ fm->Orientation() == aParams.orientation &&
+ fm->ExplicitLanguage() == aParams.explicitLanguage) {
+ if (i != n) {
+ // promote it to the end of the cache
+ mFontMetrics.RemoveElementAt(i);
+ mFontMetrics.AppendElement(fm);
+ }
+ fm->GetThebesFontGroup()->UpdateUserFonts();
+ return do_AddRef(fm);
+ }
+ }
+
+ // It's not in the cache. Get font metrics and then cache them.
+ // If the cache has reached its size limit, drop the older half of the
+ // entries; but if we're on a stylo thread (the usual case), we have
+ // to post a task back to the main thread to do the flush.
+ if (n >= kMaxCacheEntries - 1 && !mFlushPending) {
+ if (NS_IsMainThread()) {
+ Flush(mFontMetrics.Length() - kMaxCacheEntries / 2);
+ } else {
+ mFlushPending = true;
+ nsCOMPtr<nsIRunnable> flushTask = new FlushFontMetricsTask(this);
+ MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(flushTask));
+ }
+ }
+
+ nsFontMetrics::Params params = aParams;
+ params.language = language;
+ RefPtr<nsFontMetrics> fm = new nsFontMetrics(aFont, params, mContext);
+ // the mFontMetrics list has the "head" at the end, because append
+ // is cheaper than insert
+ mFontMetrics.AppendElement(do_AddRef(fm).take());
+ return fm.forget();
+}
+
+void nsFontCache::UpdateUserFonts(gfxUserFontSet* aUserFontSet) {
+ for (nsFontMetrics* fm : mFontMetrics) {
+ gfxFontGroup* fg = fm->GetThebesFontGroup();
+ if (fg->GetUserFontSet() == aUserFontSet) {
+ fg->UpdateUserFonts();
+ }
+ }
+}
+
+void nsFontCache::FontMetricsDeleted(const nsFontMetrics* aFontMetrics) {
+ mFontMetrics.RemoveElement(aFontMetrics);
+}
+
+void nsFontCache::Compact() {
+ // Need to loop backward because the running element can be removed on
+ // the way
+ for (int32_t i = mFontMetrics.Length() - 1; i >= 0; --i) {
+ nsFontMetrics* fm = mFontMetrics[i];
+ nsFontMetrics* oldfm = fm;
+ // Destroy() isn't here because we want our device context to be
+ // notified
+ NS_RELEASE(fm); // this will reset fm to nullptr
+ // if the font is really gone, it would have called back in
+ // FontMetricsDeleted() and would have removed itself
+ if (mFontMetrics.IndexOf(oldfm) != mFontMetrics.NoIndex) {
+ // nope, the font is still there, so let's hold onto it too
+ NS_ADDREF(oldfm);
+ }
+ }
+}
+
+// Flush the aFlushCount oldest entries, or all if (aFlushCount < 0)
+void nsFontCache::Flush(int32_t aFlushCount) {
+ int32_t n = aFlushCount < 0
+ ? mFontMetrics.Length()
+ : std::min<int32_t>(aFlushCount, mFontMetrics.Length());
+ for (int32_t i = n - 1; i >= 0; --i) {
+ nsFontMetrics* fm = mFontMetrics[i];
+ // Destroy() will unhook our device context from the fm so that we
+ // won't waste time in triggering the notification of
+ // FontMetricsDeleted() in the subsequent release
+ fm->Destroy();
+ NS_RELEASE(fm);
+ }
+ mFontMetrics.RemoveElementsAt(0, n);
+}
+
+nsDeviceContext::nsDeviceContext()
+ : mWidth(0),
+ mHeight(0),
+ mAppUnitsPerDevPixel(-1),
+ mAppUnitsPerDevPixelAtUnitFullZoom(-1),
+ mAppUnitsPerPhysicalInch(-1),
+ mFullZoom(1.0f),
+ mPrintingScale(1.0f),
+ mPrintingTranslate(gfxPoint(0, 0)),
+ mIsCurrentlyPrintingDoc(false)
+#ifdef DEBUG
+ ,
+ mIsInitialized(false)
+#endif
+{
+ MOZ_ASSERT(NS_IsMainThread(), "nsDeviceContext created off main thread");
+}
+
+nsDeviceContext::~nsDeviceContext() {
+ if (mFontCache) {
+ mFontCache->Destroy();
+ }
+}
+
+void nsDeviceContext::InitFontCache() {
+ if (!mFontCache) {
+ mFontCache = new nsFontCache();
+ mFontCache->Init(this);
+ }
+}
+
+void nsDeviceContext::UpdateFontCacheUserFonts(gfxUserFontSet* aUserFontSet) {
+ if (mFontCache) {
+ mFontCache->UpdateUserFonts(aUserFontSet);
+ }
+}
+
+already_AddRefed<nsFontMetrics> nsDeviceContext::GetMetricsFor(
+ const nsFont& aFont, const nsFontMetrics::Params& aParams) {
+ InitFontCache();
+ return mFontCache->GetMetricsFor(aFont, aParams);
+}
+
+nsresult nsDeviceContext::FlushFontCache(void) {
+ if (mFontCache) mFontCache->Flush();
+ return NS_OK;
+}
+
+nsresult nsDeviceContext::FontMetricsDeleted(
+ const nsFontMetrics* aFontMetrics) {
+ if (mFontCache) {
+ mFontCache->FontMetricsDeleted(aFontMetrics);
+ }
+ return NS_OK;
+}
+
+bool nsDeviceContext::IsPrinterContext() { return mPrintTarget != nullptr; }
+
+void nsDeviceContext::SetDPI(double* aScale) {
+ float dpi;
+
+ // Use the printing DC to determine DPI values, if we have one.
+ if (mDeviceContextSpec) {
+ dpi = mDeviceContextSpec->GetDPI();
+ mPrintingScale = mDeviceContextSpec->GetPrintingScale();
+ mPrintingTranslate = mDeviceContextSpec->GetPrintingTranslate();
+ mAppUnitsPerDevPixelAtUnitFullZoom =
+ NS_lround((AppUnitsPerCSSPixel() * 96) / dpi);
+ } else {
+ nsCOMPtr<nsIScreen> primaryScreen;
+ ScreenManager& screenManager = ScreenManager::GetSingleton();
+ screenManager.GetPrimaryScreen(getter_AddRefs(primaryScreen));
+ MOZ_ASSERT(primaryScreen);
+
+ // A value of -1 means use the maximum of 96 and the system DPI.
+ // A value of 0 means use the system DPI. A positive value is used as the
+ // DPI. This sets the physical size of a device pixel and thus controls the
+ // interpretation of physical units.
+ int32_t prefDPI = Preferences::GetInt("layout.css.dpi", -1);
+
+ if (prefDPI > 0) {
+ dpi = prefDPI;
+ } else if (mWidget) {
+ // PuppetWidget could return -1 if the value's not available yet.
+ dpi = mWidget->GetDPI();
+ // In case that the widget returns -1, use the primary screen's
+ // value as default.
+ if (dpi < 0) {
+ primaryScreen->GetDpi(&dpi);
+ }
+ if (prefDPI < 0) {
+ dpi = std::max(96.0f, dpi);
+ }
+ } else {
+ dpi = 96.0f;
+ }
+
+ double devPixelsPerCSSPixel;
+ if (aScale && *aScale > 0.0) {
+ // if caller provided a scale, we just use it
+ devPixelsPerCSSPixel = *aScale;
+ } else {
+ // otherwise get from the widget, and return it in aScale for
+ // the caller to pass to child contexts if needed
+ CSSToLayoutDeviceScale scale =
+ mWidget ? mWidget->GetDefaultScale() : CSSToLayoutDeviceScale(1.0);
+ devPixelsPerCSSPixel = scale.scale;
+ // In case that the widget returns -1, use the primary screen's
+ // value as default.
+ if (devPixelsPerCSSPixel < 0) {
+ primaryScreen->GetDefaultCSSScaleFactor(&devPixelsPerCSSPixel);
+ }
+ if (aScale) {
+ *aScale = devPixelsPerCSSPixel;
+ }
+ }
+
+ mAppUnitsPerDevPixelAtUnitFullZoom =
+ std::max(1, NS_lround(AppUnitsPerCSSPixel() / devPixelsPerCSSPixel));
+ }
+
+ NS_ASSERTION(dpi != -1.0, "no dpi set");
+
+ mAppUnitsPerPhysicalInch =
+ NS_lround(dpi * mAppUnitsPerDevPixelAtUnitFullZoom);
+ UpdateAppUnitsForFullZoom();
+}
+
+nsresult nsDeviceContext::Init(nsIWidget* aWidget) {
+#ifdef DEBUG
+ // We can't assert |!mIsInitialized| here since EndSwapDocShellsForDocument
+ // re-initializes nsDeviceContext objects. We can only assert in
+ // InitForPrinting (below).
+ mIsInitialized = true;
+#endif
+
+ nsresult rv = NS_OK;
+ if (mScreenManager && mWidget == aWidget) return rv;
+
+ mWidget = aWidget;
+ SetDPI();
+
+ if (mScreenManager) return rv;
+
+ mScreenManager = do_GetService("@mozilla.org/gfx/screenmanager;1", &rv);
+
+ return rv;
+}
+
+// XXX This is only for printing. We should make that obvious in the name.
+already_AddRefed<gfxContext> nsDeviceContext::CreateRenderingContext() {
+ return CreateRenderingContextCommon(/* not a reference context */ false);
+}
+
+already_AddRefed<gfxContext>
+nsDeviceContext::CreateReferenceRenderingContext() {
+ return CreateRenderingContextCommon(/* a reference context */ true);
+}
+
+already_AddRefed<gfxContext> nsDeviceContext::CreateRenderingContextCommon(
+ bool aWantReferenceContext) {
+ MOZ_ASSERT(IsPrinterContext());
+ MOZ_ASSERT(mWidth > 0 && mHeight > 0);
+
+ RefPtr<gfx::DrawTarget> dt;
+ if (aWantReferenceContext) {
+ dt = mPrintTarget->GetReferenceDrawTarget();
+ } else {
+ // This will be null if e10s is disabled or print.print_via_parent=false.
+ RefPtr<DrawEventRecorder> recorder;
+ mDeviceContextSpec->GetDrawEventRecorder(getter_AddRefs(recorder));
+ dt = mPrintTarget->MakeDrawTarget(gfx::IntSize(mWidth, mHeight), recorder);
+ }
+
+ if (!dt || !dt->IsValid()) {
+ gfxCriticalNote << "Failed to create draw target in device context sized "
+ << mWidth << "x" << mHeight << " and pointer "
+ << hexa(mPrintTarget);
+ return nullptr;
+ }
+
+#ifdef XP_MACOSX
+ // The CGContextRef provided by PMSessionGetCGGraphicsContext is
+ // write-only, so we need to prevent gfxContext::PushGroupAndCopyBackground
+ // trying to read from it or else we'll crash.
+ // XXXjwatt Consider adding a MakeDrawTarget override to PrintTargetCG and
+ // moving this AddUserData call there.
+ dt->AddUserData(&gfxContext::sDontUseAsSourceKey, dt, nullptr);
+#endif
+ dt->AddUserData(&sDisablePixelSnapping, (void*)0x1, nullptr);
+
+ RefPtr<gfxContext> pContext = gfxContext::CreateOrNull(dt);
+ MOZ_ASSERT(pContext); // already checked draw target above
+
+ gfxMatrix transform;
+ transform.PreTranslate(mPrintingTranslate);
+ if (mPrintTarget->RotateNeededForLandscape()) {
+ // Rotate page 90 degrees to draw landscape page on portrait paper
+ IntSize size = mPrintTarget->GetSize();
+ transform.PreTranslate(gfxPoint(0, size.width));
+ gfxMatrix rotate(0, -1, 1, 0, 0, 0);
+ transform = rotate * transform;
+ }
+ transform.PreScale(mPrintingScale, mPrintingScale);
+
+ pContext->SetMatrixDouble(transform);
+ return pContext.forget();
+}
+
+nsresult nsDeviceContext::GetDepth(uint32_t& aDepth) {
+ nsCOMPtr<nsIScreen> screen;
+ FindScreen(getter_AddRefs(screen));
+ if (!screen) {
+ ScreenManager& screenManager = ScreenManager::GetSingleton();
+ screenManager.GetPrimaryScreen(getter_AddRefs(screen));
+ MOZ_ASSERT(screen);
+ }
+ screen->GetColorDepth(reinterpret_cast<int32_t*>(&aDepth));
+
+ return NS_OK;
+}
+
+nsresult nsDeviceContext::GetDeviceSurfaceDimensions(nscoord& aWidth,
+ nscoord& aHeight) {
+ if (IsPrinterContext()) {
+ aWidth = mWidth;
+ aHeight = mHeight;
+ } else {
+ nsRect area;
+ ComputeFullAreaUsingScreen(&area);
+ aWidth = area.Width();
+ aHeight = area.Height();
+ }
+
+ return NS_OK;
+}
+
+nsresult nsDeviceContext::GetRect(nsRect& aRect) {
+ if (IsPrinterContext()) {
+ aRect.SetRect(0, 0, mWidth, mHeight);
+ } else
+ ComputeFullAreaUsingScreen(&aRect);
+
+ return NS_OK;
+}
+
+nsresult nsDeviceContext::GetClientRect(nsRect& aRect) {
+ if (IsPrinterContext()) {
+ aRect.SetRect(0, 0, mWidth, mHeight);
+ } else
+ ComputeClientRectUsingScreen(&aRect);
+
+ return NS_OK;
+}
+
+nsresult nsDeviceContext::InitForPrinting(nsIDeviceContextSpec* aDevice) {
+ NS_ENSURE_ARG_POINTER(aDevice);
+
+ MOZ_ASSERT(!mIsInitialized,
+ "Only initialize once, immediately after construction");
+
+ // We don't set mIsInitialized here. The Init() call below does that.
+
+ mPrintTarget = aDevice->MakePrintTarget();
+ if (!mPrintTarget) {
+ return NS_ERROR_FAILURE;
+ }
+
+ mDeviceContextSpec = aDevice;
+
+ Init(nullptr);
+
+ if (!CalcPrintingSize()) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+nsresult nsDeviceContext::BeginDocument(const nsAString& aTitle,
+ const nsAString& aPrintToFileName,
+ int32_t aStartPage, int32_t aEndPage) {
+ MOZ_ASSERT(!mIsCurrentlyPrintingDoc,
+ "Mismatched BeginDocument/EndDocument calls");
+
+ nsresult rv = mPrintTarget->BeginPrinting(aTitle, aPrintToFileName,
+ aStartPage, aEndPage);
+
+ if (NS_SUCCEEDED(rv)) {
+ if (mDeviceContextSpec) {
+ rv = mDeviceContextSpec->BeginDocument(aTitle, aPrintToFileName,
+ aStartPage, aEndPage);
+ }
+ mIsCurrentlyPrintingDoc = true;
+ }
+
+ // Warn about any failure (except user cancelling):
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv) || rv == NS_ERROR_ABORT,
+ "nsDeviceContext::BeginDocument failed");
+
+ return rv;
+}
+
+nsresult nsDeviceContext::EndDocument(void) {
+ MOZ_ASSERT(mIsCurrentlyPrintingDoc,
+ "Mismatched BeginDocument/EndDocument calls");
+
+ mIsCurrentlyPrintingDoc = false;
+
+ nsresult rv = mPrintTarget->EndPrinting();
+ if (NS_SUCCEEDED(rv)) {
+ mPrintTarget->Finish();
+ }
+
+ if (mDeviceContextSpec) mDeviceContextSpec->EndDocument();
+
+ mPrintTarget = nullptr;
+
+ return rv;
+}
+
+nsresult nsDeviceContext::AbortDocument(void) {
+ MOZ_ASSERT(mIsCurrentlyPrintingDoc,
+ "Mismatched BeginDocument/EndDocument calls");
+
+ nsresult rv = mPrintTarget->AbortPrinting();
+
+ mIsCurrentlyPrintingDoc = false;
+
+ if (mDeviceContextSpec) mDeviceContextSpec->EndDocument();
+
+ mPrintTarget = nullptr;
+
+ return rv;
+}
+
+nsresult nsDeviceContext::BeginPage(void) {
+ nsresult rv = NS_OK;
+
+ if (mDeviceContextSpec) rv = mDeviceContextSpec->BeginPage();
+
+ if (NS_FAILED(rv)) return rv;
+
+ return mPrintTarget->BeginPage();
+}
+
+nsresult nsDeviceContext::EndPage(void) {
+ nsresult rv = mPrintTarget->EndPage();
+
+ if (mDeviceContextSpec) mDeviceContextSpec->EndPage();
+
+ return rv;
+}
+
+void nsDeviceContext::ComputeClientRectUsingScreen(nsRect* outRect) {
+ // we always need to recompute the clientRect
+ // because the window may have moved onto a different screen. In the single
+ // monitor case, we only need to do the computation if we haven't done it
+ // once already, and remember that we have because we're assured it won't
+ // change.
+ nsCOMPtr<nsIScreen> screen;
+ FindScreen(getter_AddRefs(screen));
+ if (screen) {
+ int32_t x, y, width, height;
+ screen->GetAvailRect(&x, &y, &width, &height);
+
+ // convert to device units
+ outRect->SetRect(NSIntPixelsToAppUnits(x, AppUnitsPerDevPixel()),
+ NSIntPixelsToAppUnits(y, AppUnitsPerDevPixel()),
+ NSIntPixelsToAppUnits(width, AppUnitsPerDevPixel()),
+ NSIntPixelsToAppUnits(height, AppUnitsPerDevPixel()));
+ }
+}
+
+void nsDeviceContext::ComputeFullAreaUsingScreen(nsRect* outRect) {
+ // if we have more than one screen, we always need to recompute the clientRect
+ // because the window may have moved onto a different screen. In the single
+ // monitor case, we only need to do the computation if we haven't done it
+ // once already, and remember that we have because we're assured it won't
+ // change.
+ nsCOMPtr<nsIScreen> screen;
+ FindScreen(getter_AddRefs(screen));
+ if (screen) {
+ int32_t x, y, width, height;
+ screen->GetRect(&x, &y, &width, &height);
+
+ // convert to device units
+ outRect->SetRect(NSIntPixelsToAppUnits(x, AppUnitsPerDevPixel()),
+ NSIntPixelsToAppUnits(y, AppUnitsPerDevPixel()),
+ NSIntPixelsToAppUnits(width, AppUnitsPerDevPixel()),
+ NSIntPixelsToAppUnits(height, AppUnitsPerDevPixel()));
+ mWidth = outRect->Width();
+ mHeight = outRect->Height();
+ }
+}
+
+//
+// FindScreen
+//
+// Determines which screen intersects the largest area of the given surface.
+//
+void nsDeviceContext::FindScreen(nsIScreen** outScreen) {
+ if (!mWidget || !mScreenManager) {
+ return;
+ }
+
+ CheckDPIChange();
+
+ nsCOMPtr<nsIScreen> screen = mWidget->GetWidgetScreen();
+ screen.forget(outScreen);
+
+ if (!(*outScreen)) {
+ mScreenManager->GetPrimaryScreen(outScreen);
+ }
+}
+
+bool nsDeviceContext::CalcPrintingSize() {
+ gfxSize size(mPrintTarget->GetSize());
+ // For printing, CSS inches and physical inches are identical
+ // so it doesn't matter which we use here
+ mWidth = NSToCoordRound(size.width * AppUnitsPerPhysicalInch() /
+ POINTS_PER_INCH_FLOAT);
+ mHeight = NSToCoordRound(size.height * AppUnitsPerPhysicalInch() /
+ POINTS_PER_INCH_FLOAT);
+
+ return (mWidth > 0 && mHeight > 0);
+}
+
+bool nsDeviceContext::CheckDPIChange(double* aScale) {
+ int32_t oldDevPixels = mAppUnitsPerDevPixelAtUnitFullZoom;
+ int32_t oldInches = mAppUnitsPerPhysicalInch;
+
+ SetDPI(aScale);
+
+ return oldDevPixels != mAppUnitsPerDevPixelAtUnitFullZoom ||
+ oldInches != mAppUnitsPerPhysicalInch;
+}
+
+bool nsDeviceContext::SetFullZoom(float aScale) {
+ if (aScale <= 0) {
+ MOZ_ASSERT_UNREACHABLE("Invalid full zoom value");
+ return false;
+ }
+ int32_t oldAppUnitsPerDevPixel = mAppUnitsPerDevPixel;
+ mFullZoom = aScale;
+ UpdateAppUnitsForFullZoom();
+ return oldAppUnitsPerDevPixel != mAppUnitsPerDevPixel;
+}
+
+void nsDeviceContext::UpdateAppUnitsForFullZoom() {
+ mAppUnitsPerDevPixel = std::max(
+ 1, NSToIntRound(float(mAppUnitsPerDevPixelAtUnitFullZoom) / mFullZoom));
+ // adjust mFullZoom to reflect appunit rounding
+ mFullZoom = float(mAppUnitsPerDevPixelAtUnitFullZoom) / mAppUnitsPerDevPixel;
+}
+
+DesktopToLayoutDeviceScale nsDeviceContext::GetDesktopToDeviceScale() {
+ nsCOMPtr<nsIScreen> screen;
+ FindScreen(getter_AddRefs(screen));
+
+ if (screen) {
+ double scale;
+ screen->GetContentsScaleFactor(&scale);
+ return DesktopToLayoutDeviceScale(scale);
+ }
+
+ return DesktopToLayoutDeviceScale(1.0);
+}
+
+bool nsDeviceContext::IsSyncPagePrinting() const {
+ MOZ_ASSERT(mPrintTarget);
+ return mPrintTarget->IsSyncPagePrinting();
+}
+
+void nsDeviceContext::RegisterPageDoneCallback(
+ PrintTarget::PageDoneCallback&& aCallback) {
+ MOZ_ASSERT(mPrintTarget && aCallback && !IsSyncPagePrinting());
+ mPrintTarget->RegisterPageDoneCallback(std::move(aCallback));
+}
+void nsDeviceContext::UnregisterPageDoneCallback() {
+ if (mPrintTarget) {
+ mPrintTarget->UnregisterPageDoneCallback();
+ }
+}