From c498dd1344ac4ea6fb0e7c7f789c396db5e57121 Mon Sep 17 00:00:00 2001 From: Martin Stransky Date: Mon, 19 Jan 2015 18:32:52 +0100 Subject: [PATCH] Gtk3 - added patch for HiDPI support (mozbz#975919) --- firefox.spec | 9 +- mozilla-975919-gtk3-hidpi.patch | 798 ++++++++++++++++++++++++++++++++ 2 files changed, 805 insertions(+), 2 deletions(-) create mode 100644 mozilla-975919-gtk3-hidpi.patch diff --git a/firefox.spec b/firefox.spec index ca3a52a..4f2595b 100644 --- a/firefox.spec +++ b/firefox.spec @@ -107,7 +107,7 @@ Summary: Mozilla Firefox Web browser Name: firefox Version: 35.0 -Release: 4%{?pre_tag}%{?dist} +Release: 5%{?pre_tag}%{?dist} URL: http://www.mozilla.org/projects/firefox/ License: MPLv1.1 or GPLv2+ or LGPLv2+ Group: Applications/Internet @@ -156,6 +156,7 @@ Patch409: mozilla-1073117-entry-button-size.patch Patch410: mozilla-1073117-button-focus.patch Patch411: mozilla-1073117-focus-sizes.patch Patch412: mozilla-1073117-no-gap-tab.patch +Patch413: mozilla-975919-gtk3-hidpi.patch %if %{official_branding} # Required by Mozilla Corporation @@ -314,6 +315,7 @@ cd %{tarballdir} %patch410 -p1 -b .1073117-button-focus %patch411 -p1 -b .1073117-focus-sizes %patch412 -p1 -b .1073117-no-gap-tab +%patch413 -p1 -b .975919-gtk3-hidpi %endif %if %{official_branding} @@ -767,9 +769,12 @@ gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || : #--------------------------------------------------------------------- %changelog +* Mon Jan 19 2015 Martin Stransky - 35.0-5 +- Enable release build config +- Gtk3 - added patch for HiDPI support (mozbz#975919) + * Mon Jan 19 2015 Martin Stransky - 35.0-4 - Gtk3 - fixed tabs rendering -- Enable release build config * Wed Jan 14 2015 Martin Stransky - 35.0-3 - Gtk3 - replaced obsoleted focus properties diff --git a/mozilla-975919-gtk3-hidpi.patch b/mozilla-975919-gtk3-hidpi.patch new file mode 100644 index 0000000..283b7ac --- /dev/null +++ b/mozilla-975919-gtk3-hidpi.patch @@ -0,0 +1,798 @@ +# HG changeset patch +# Parent 7b33ee7fd162d784f382250d3fa811e86a1b7348 +# User Andrew Comminos +Bug 975919 - Added support for HiDPI on GTK 3.10+ + +diff --git a/widget/gtk/nsGtkUtils.h b/widget/gtk/nsGtkUtils.h +--- a/widget/gtk/nsGtkUtils.h ++++ b/widget/gtk/nsGtkUtils.h +@@ -4,16 +4,17 @@ + /* 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/. */ + + #ifndef nsGtkUtils_h__ + #define nsGtkUtils_h__ + + #include ++#include + + // Some gobject functions expect functions for gpointer arguments. + // gpointer is void* but C++ doesn't like casting functions to void*. + template static inline gpointer + FuncToGpointer(T aFunction) + { + return reinterpret_cast + (reinterpret_cast +diff --git a/widget/gtk/nsLookAndFeel.cpp b/widget/gtk/nsLookAndFeel.cpp +--- a/widget/gtk/nsLookAndFeel.cpp ++++ b/widget/gtk/nsLookAndFeel.cpp +@@ -728,16 +728,27 @@ GetSystemFontInfo(GtkWidget *aWidget, + + // |size| is now either pixels or pango-points (not Mozilla-points!) + + if (!pango_font_description_get_size_is_absolute(desc)) { + // |size| is in pango-points, so convert to pixels. + size *= float(gfxPlatformGtk::GetDPI()) / POINTS_PER_INCH_FLOAT; + } + ++ // Scale fonts up on HiDPI displays. ++ // This would be done automatically with cairo, but we manually manage ++ // the display scale for platform consistency. ++ static gint (*GdkScreenGetMonitorScaleFactorPtr)(GdkScreen*,gint) = ++ (gint (*)(GdkScreen*,gint)) dlsym(RTLD_DEFAULT, ++ "gdk_screen_get_monitor_scale_factor"); ++ if (GdkScreenGetMonitorScaleFactorPtr) { ++ GdkScreen *screen = gdk_screen_get_default(); ++ size *= (*GdkScreenGetMonitorScaleFactorPtr)(screen, 0); ++ } ++ + // |size| is now pixels + + aFontStyle->size = size; + + pango_font_description_free(desc); + } + + static void +diff --git a/widget/gtk/nsScreenGtk.cpp b/widget/gtk/nsScreenGtk.cpp +--- a/widget/gtk/nsScreenGtk.cpp ++++ b/widget/gtk/nsScreenGtk.cpp +@@ -6,20 +6,20 @@ + #include "nsScreenGtk.h" + + #include + #ifdef MOZ_X11 + #include + #include + #endif + #include ++#include + + static uint32_t sScreenId = 0; + +- + nsScreenGtk :: nsScreenGtk ( ) + : mScreenNum(0), + mRect(0, 0, 0, 0), + mAvailRect(0, 0, 0, 0), + mId(++sScreenId) + { + } + +@@ -35,37 +35,68 @@ nsScreenGtk :: GetId(uint32_t *aId) + *aId = mId; + return NS_OK; + } // GetId + + + NS_IMETHODIMP + nsScreenGtk :: GetRect(int32_t *outLeft, int32_t *outTop, int32_t *outWidth, int32_t *outHeight) + { ++ double scale; ++ GetContentsScaleFactor(&scale); ++ ++ *outLeft = NSToIntRound(mRect.x * scale); ++ *outTop = NSToIntRound(mRect.y * scale); ++ *outWidth = NSToIntRound(mRect.width * scale); ++ *outHeight = NSToIntRound(mRect.height * scale); ++ ++ return NS_OK; ++ ++} // GetRect ++ ++ ++NS_IMETHODIMP ++nsScreenGtk :: GetAvailRect(int32_t *outLeft, int32_t *outTop, int32_t *outWidth, int32_t *outHeight) ++{ ++ double scale; ++ GetContentsScaleFactor(&scale); ++ ++ *outLeft = NSToIntRound(mAvailRect.x * scale); ++ *outTop = NSToIntRound(mAvailRect.y * scale); ++ *outWidth = NSToIntRound(mAvailRect.width * scale); ++ *outHeight = NSToIntRound(mAvailRect.height * scale); ++ ++ return NS_OK; ++ ++} // GetAvailRect ++ ++NS_IMETHODIMP ++nsScreenGtk :: GetRectDisplayPix(int32_t *outLeft, int32_t *outTop, int32_t *outWidth, int32_t *outHeight) ++{ + *outLeft = mRect.x; + *outTop = mRect.y; + *outWidth = mRect.width; + *outHeight = mRect.height; + + return NS_OK; + +-} // GetRect ++} // GetRectDisplayPix + + + NS_IMETHODIMP +-nsScreenGtk :: GetAvailRect(int32_t *outLeft, int32_t *outTop, int32_t *outWidth, int32_t *outHeight) ++nsScreenGtk :: GetAvailRectDisplayPix(int32_t *outLeft, int32_t *outTop, int32_t *outWidth, int32_t *outHeight) + { + *outLeft = mAvailRect.x; + *outTop = mAvailRect.y; + *outWidth = mAvailRect.width; + *outHeight = mAvailRect.height; + + return NS_OK; + +-} // GetAvailRect ++} // GetAvailRectDisplayPix + + + NS_IMETHODIMP + nsScreenGtk :: GetPixelDepth(int32_t *aPixelDepth) + { + GdkVisual * visual = gdk_screen_get_system_visual(gdk_screen_get_default()); + *aPixelDepth = gdk_visual_get_depth(visual); + +@@ -77,16 +108,33 @@ nsScreenGtk :: GetPixelDepth(int32_t *aP + NS_IMETHODIMP + nsScreenGtk :: GetColorDepth(int32_t *aColorDepth) + { + return GetPixelDepth ( aColorDepth ); + + } // GetColorDepth + + ++NS_IMETHODIMP ++nsScreenGtk :: GetContentsScaleFactor(double* aContentsScaleFactor) ++{ ++ static gint (*GdkScreenGetMonitorScaleFactorPtr)(GdkScreen*,gint) = ++ (gint (*)(GdkScreen*,gint)) dlsym(RTLD_DEFAULT, ++ "gdk_screen_get_monitor_scale_factor"); ++ if (GdkScreenGetMonitorScaleFactorPtr) { ++ GdkScreen *screen = gdk_screen_get_default(); ++ *aContentsScaleFactor = (*GdkScreenGetMonitorScaleFactorPtr) ++ (screen, mScreenNum); ++ } else { ++ *aContentsScaleFactor = 1; ++ } ++ return NS_OK; ++} ++ ++ + void + nsScreenGtk :: Init (GdkWindow *aRootWindow) + { + // We listen for configure events on the root window to pick up + // changes to this rect. We could listen for "size_changed" signals + // on the default screen to do this, except that doesn't work with + // versions of GDK predating the GdkScreen object. See bug 256646. + mAvailRect = mRect = nsIntRect(0, 0, gdk_screen_width(), gdk_screen_height()); +diff --git a/widget/gtk/nsScreenGtk.h b/widget/gtk/nsScreenGtk.h +--- a/widget/gtk/nsScreenGtk.h ++++ b/widget/gtk/nsScreenGtk.h +@@ -28,18 +28,21 @@ class nsScreenGtk : public nsBaseScreen + { + public: + nsScreenGtk(); + ~nsScreenGtk(); + + NS_IMETHOD GetId(uint32_t* aId); + NS_IMETHOD GetRect(int32_t* aLeft, int32_t* aTop, int32_t* aWidth, int32_t* aHeight); + NS_IMETHOD GetAvailRect(int32_t* aLeft, int32_t* aTop, int32_t* aWidth, int32_t* aHeight); ++ NS_IMETHOD GetRectDisplayPix(int32_t* aLeft, int32_t* aTop, int32_t* aWidth, int32_t* aHeight); ++ NS_IMETHOD GetAvailRectDisplayPix(int32_t* aLeft, int32_t* aTop, int32_t* aWidth, int32_t* aHeight); + NS_IMETHOD GetPixelDepth(int32_t* aPixelDepth); + NS_IMETHOD GetColorDepth(int32_t* aColorDepth); ++ NS_IMETHOD GetContentsScaleFactor(double* aContentsScaleFactor); + + void Init(GdkWindow *aRootWindow); + #ifdef MOZ_X11 + void Init(XineramaScreenInfo *aScreenInfo); + #endif /* MOZ_X11 */ + + private: + uint32_t mScreenNum; +diff --git a/widget/gtk/nsWindow.cpp b/widget/gtk/nsWindow.cpp +--- a/widget/gtk/nsWindow.cpp ++++ b/widget/gtk/nsWindow.cpp +@@ -466,16 +466,19 @@ nsWindow::DispatchResized(int32_t aWidth + + nsresult + nsWindow::DispatchEvent(WidgetGUIEvent* aEvent, nsEventStatus& aStatus) + { + #ifdef DEBUG + debug_DumpEvent(stdout, aEvent->widget, aEvent, + nsAutoCString("something"), 0); + #endif ++ // Translate the mouse event into device pixels. ++ aEvent->refPoint.x = GdkCoordToDevicePixels(aEvent->refPoint.x); ++ aEvent->refPoint.y = GdkCoordToDevicePixels(aEvent->refPoint.y); + + aStatus = nsEventStatus_eIgnore; + nsIWidgetListener* listener = + mAttachedWidgetListener ? mAttachedWidgetListener : mWidgetListener; + if (listener) { + aStatus = listener->HandleEvent(aEvent, mUseAttachedEvents); + } + +@@ -724,16 +727,22 @@ nsWindow::GetDPI() + double heightInches = DisplayHeightMM(dpy, defaultScreen)/MM_PER_INCH_FLOAT; + if (heightInches < 0.25) { + // Something's broken, but we'd better not crash. + return 96.0f; + } + return float(DisplayHeight(dpy, defaultScreen)/heightInches); + } + ++double ++nsWindow::GetDefaultScaleInternal() ++{ ++ return GdkScaleFactor(); ++} ++ + NS_IMETHODIMP + nsWindow::SetParent(nsIWidget *aNewParent) + { + if (mContainer || !mGdkWindow) { + NS_NOTREACHED("nsWindow::SetParent called illegally"); + return NS_ERROR_NOT_IMPLEMENTED; + } + +@@ -822,18 +831,19 @@ nsWindow::ReparentNativeWidgetInternal(n + SetWidgetForHierarchy(mGdkWindow, aOldContainer, aNewContainer); + + if (aOldContainer == gInvisibleContainer) { + CheckDestroyInvisibleContainer(); + } + } + + if (!mIsTopLevel) { +- gdk_window_reparent(mGdkWindow, aNewParentWindow, mBounds.x, +- mBounds.y); ++ gdk_window_reparent(mGdkWindow, aNewParentWindow, ++ DevicePixelsToGdkCoordRoundDown(mBounds.x), ++ DevicePixelsToGdkCoordRoundDown(mBounds.y)); + } + } + + nsWindow* newParent = static_cast(aNewParent); + bool parentHasMappedToplevel = + newParent && newParent->mHasMappedToplevel; + if (mHasMappedToplevel != parentHasMappedToplevel) { + SetHasMappedToplevel(parentHasMappedToplevel); +@@ -858,52 +868,56 @@ nsWindow::IsVisible() const + { + return mIsShown; + } + + NS_IMETHODIMP + nsWindow::ConstrainPosition(bool aAllowSlop, int32_t *aX, int32_t *aY) + { + if (mIsTopLevel && mShell) { +- int32_t screenWidth = gdk_screen_width(); +- int32_t screenHeight = gdk_screen_height(); ++ int width = GdkCoordToDevicePixels(gdk_screen_width()); ++ int height = GdkCoordToDevicePixels(gdk_screen_height()); + if (aAllowSlop) { + if (*aX < (kWindowPositionSlop - mBounds.width)) + *aX = kWindowPositionSlop - mBounds.width; +- if (*aX > (screenWidth - kWindowPositionSlop)) +- *aX = screenWidth - kWindowPositionSlop; ++ if (*aX > (width - kWindowPositionSlop)) ++ *aX = width - kWindowPositionSlop; + if (*aY < (kWindowPositionSlop - mBounds.height)) + *aY = kWindowPositionSlop - mBounds.height; +- if (*aY > (screenHeight - kWindowPositionSlop)) +- *aY = screenHeight - kWindowPositionSlop; ++ if (*aY > (height - kWindowPositionSlop)) ++ *aY = height - kWindowPositionSlop; + } else { + if (*aX < 0) + *aX = 0; +- if (*aX > (screenWidth - mBounds.width)) +- *aX = screenWidth - mBounds.width; ++ if (*aX > (width - mBounds.width)) ++ *aX = width - mBounds.width; + if (*aY < 0) + *aY = 0; +- if (*aY > (screenHeight - mBounds.height)) +- *aY = screenHeight - mBounds.height; ++ if (*aY > (height - mBounds.height)) ++ *aY = height - mBounds.height; + } + } + return NS_OK; + } + + void nsWindow::SetSizeConstraints(const SizeConstraints& aConstraints) + { + mSizeConstraints.mMinSize = GetSafeWindowSize(aConstraints.mMinSize); + mSizeConstraints.mMaxSize = GetSafeWindowSize(aConstraints.mMaxSize); + + if (mShell) { + GdkGeometry geometry; +- geometry.min_width = mSizeConstraints.mMinSize.width; +- geometry.min_height = mSizeConstraints.mMinSize.height; +- geometry.max_width = mSizeConstraints.mMaxSize.width; +- geometry.max_height = mSizeConstraints.mMaxSize.height; ++ geometry.min_width = DevicePixelsToGdkCoordRoundUp( ++ mSizeConstraints.mMinSize.width); ++ geometry.min_height = DevicePixelsToGdkCoordRoundUp( ++ mSizeConstraints.mMinSize.height); ++ geometry.max_width = DevicePixelsToGdkCoordRoundUp( ++ mSizeConstraints.mMaxSize.width); ++ geometry.max_height = DevicePixelsToGdkCoordRoundUp( ++ mSizeConstraints.mMaxSize.height); + + uint32_t hints = GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE; + gtk_window_set_geometry_hints(GTK_WINDOW(mShell), nullptr, + &geometry, GdkWindowHints(hints)); + } + } + + NS_IMETHODIMP +@@ -1156,21 +1170,23 @@ nsWindow::Move(double aX, double aY) + mBounds.x = x; + mBounds.y = y; + + if (!mCreated) + return NS_OK; + + mNeedsMove = false; + ++ GdkPoint point = DevicePixelsToGdkPointRoundDown(nsIntPoint(x, y)); ++ + if (mIsTopLevel) { +- gtk_window_move(GTK_WINDOW(mShell), x, y); ++ gtk_window_move(GTK_WINDOW(mShell), point.x, point.y); + } + else if (mGdkWindow) { +- gdk_window_move(mGdkWindow, x, y); ++ gdk_window_move(mGdkWindow, point.x, point.y); + } + + NotifyRollupGeometryChange(); + return NS_OK; + } + + NS_IMETHODIMP + nsWindow::PlaceBehind(nsTopLevelWidgetZPlacement aPlacement, +@@ -1427,17 +1443,17 @@ nsWindow::SetFocus(bool aRaise) + + NS_IMETHODIMP + nsWindow::GetScreenBounds(nsIntRect &aRect) + { + if (mIsTopLevel && mContainer) { + // use the point including window decorations + gint x, y; + gdk_window_get_root_origin(gtk_widget_get_window(GTK_WIDGET(mContainer)), &x, &y); +- aRect.MoveTo(x, y); ++ aRect.MoveTo(GdkPointToDevicePixels({ x, y })); + } + else { + aRect.MoveTo(WidgetToScreenOffset()); + } + // mBounds.Size() is the window bounds, not the window-manager frame + // bounds (bug 581863). gdk_window_get_frame_extents would give the + // frame bounds, but mBounds.Size() is returned here for consistency + // with Resize. +@@ -1597,27 +1613,22 @@ nsWindow::SetCursor(imgIContainer* aCurs + } + + NS_IMETHODIMP + nsWindow::Invalidate(const nsIntRect &aRect) + { + if (!mGdkWindow) + return NS_OK; + +- GdkRectangle rect; +- rect.x = aRect.x; +- rect.y = aRect.y; +- rect.width = aRect.width; +- rect.height = aRect.height; ++ GdkRectangle rect = DevicePixelsToGdkRectRoundOut(aRect); ++ gdk_window_invalidate_rect(mGdkWindow, &rect, FALSE); + + LOGDRAW(("Invalidate (rect) [%p]: %d %d %d %d\n", (void *)this, + rect.x, rect.y, rect.width, rect.height)); + +- gdk_window_invalidate_rect(mGdkWindow, &rect, FALSE); +- + return NS_OK; + } + + void* + nsWindow::GetNativeData(uint32_t aDataType) + { + switch (aDataType) { + case NS_NATIVE_WINDOW: +@@ -1745,17 +1756,17 @@ nsIntPoint + nsWindow::WidgetToScreenOffset() + { + gint x = 0, y = 0; + + if (mGdkWindow) { + gdk_window_get_origin(mGdkWindow, &x, &y); + } + +- return nsIntPoint(x, y); ++ return GdkPointToDevicePixels({ x, y }); + } + + NS_IMETHODIMP + nsWindow::EnableDragDrop(bool aEnable) + { + return NS_OK; + } + +@@ -2037,17 +2048,19 @@ nsWindow::OnExposeEvent(cairo_t *cr) + #if (MOZ_WIDGET_GTK == 2) + if (!exposeRegion.Init(aEvent)) { + #else + if (!exposeRegion.Init(cr)) { + #endif + return FALSE; + } + +- nsIntRegion ®ion = exposeRegion.mRegion; ++ gint scale = GdkScaleFactor(); ++ nsIntRegion& region = exposeRegion.mRegion; ++ region.ScaleRoundOut(scale, scale); + + ClientLayerManager *clientLayers = + (GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_CLIENT) + ? static_cast(GetLayerManager()) + : nullptr; + + if (clientLayers && mCompositorParent) { + // We need to paint to the screen even if nothing changed, since if we +@@ -2377,31 +2390,34 @@ nsWindow::OnContainerUnrealize() + + void + nsWindow::OnSizeAllocate(GtkAllocation *aAllocation) + { + LOG(("size_allocate [%p] %d %d %d %d\n", + (void *)this, aAllocation->x, aAllocation->y, + aAllocation->width, aAllocation->height)); + +- nsIntSize size(aAllocation->width, aAllocation->height); ++ nsIntSize size = GdkRectToDevicePixels(*aAllocation).Size(); ++ + if (mBounds.Size() == size) + return; + ++ nsIntRect rect; ++ + // Invalidate the new part of the window now for the pending paint to + // minimize background flashes (GDK does not do this for external resizes + // of toplevels.) + if (mBounds.width < size.width) { +- GdkRectangle rect = +- { mBounds.width, 0, size.width - mBounds.width, size.height }; ++ GdkRectangle rect = DevicePixelsToGdkRectRoundOut( ++ { mBounds.width, 0, size.width - mBounds.width, size.height }); + gdk_window_invalidate_rect(mGdkWindow, &rect, FALSE); + } + if (mBounds.height < size.height) { +- GdkRectangle rect = +- { 0, mBounds.height, size.width, size.height - mBounds.height }; ++ GdkRectangle rect = DevicePixelsToGdkRectRoundOut( ++ { 0, mBounds.height, size.width, size.height - mBounds.height }); + gdk_window_invalidate_rect(mGdkWindow, &rect, FALSE); + } + + mBounds.SizeTo(size); + + if (!mGdkWindow) + return; + +@@ -3843,67 +3859,75 @@ nsWindow::SetWindowClass(const nsAString + nsMemory::Free(res_name); + + return NS_OK; + } + + void + nsWindow::NativeResize(int32_t aWidth, int32_t aHeight, bool aRepaint) + { ++ gint width = DevicePixelsToGdkCoordRoundUp(aWidth); ++ gint height = DevicePixelsToGdkCoordRoundUp(aHeight); ++ + LOG(("nsWindow::NativeResize [%p] %d %d\n", (void *)this, +- aWidth, aHeight)); ++ width, height)); + + // clear our resize flag + mNeedsResize = false; + + if (mIsTopLevel) { +- gtk_window_resize(GTK_WINDOW(mShell), aWidth, aHeight); ++ gtk_window_resize(GTK_WINDOW(mShell), width, height); + } + else if (mContainer) { + GtkWidget *widget = GTK_WIDGET(mContainer); + GtkAllocation allocation, prev_allocation; + gtk_widget_get_allocation(widget, &prev_allocation); + allocation.x = prev_allocation.x; + allocation.y = prev_allocation.y; +- allocation.width = aWidth; +- allocation.height = aHeight; ++ allocation.width = width; ++ allocation.height = height; + gtk_widget_size_allocate(widget, &allocation); + } + else if (mGdkWindow) { +- gdk_window_resize(mGdkWindow, aWidth, aHeight); ++ gdk_window_resize(mGdkWindow, width, height); + } + } + + void + nsWindow::NativeResize(int32_t aX, int32_t aY, + int32_t aWidth, int32_t aHeight, + bool aRepaint) + { ++ gint width = DevicePixelsToGdkCoordRoundUp(aWidth); ++ gint height = DevicePixelsToGdkCoordRoundUp(aHeight); ++ gint x = DevicePixelsToGdkCoordRoundDown(aX); ++ gint y = DevicePixelsToGdkCoordRoundDown(aY); ++ + mNeedsResize = false; + mNeedsMove = false; + + LOG(("nsWindow::NativeResize [%p] %d %d %d %d\n", (void *)this, +- aX, aY, aWidth, aHeight)); ++ x, y, width, height)); + + if (mIsTopLevel) { +- // aX and aY give the position of the window manager frame top-left. +- gtk_window_move(GTK_WINDOW(mShell), aX, aY); ++ // x and y give the position of the window manager frame top-left. ++ gtk_window_move(GTK_WINDOW(mShell), x, y); + // This sets the client window size. +- gtk_window_resize(GTK_WINDOW(mShell), aWidth, aHeight); ++ gtk_window_resize(GTK_WINDOW(mShell), width, height); + } + else if (mContainer) { + GtkAllocation allocation; +- allocation.x = aX; +- allocation.y = aY; +- allocation.width = aWidth; +- allocation.height = aHeight; ++ allocation.x = x; ++ allocation.y = y; ++ allocation.width = width; ++ allocation.height = height; + gtk_widget_size_allocate(GTK_WIDGET(mContainer), &allocation); + } + else if (mGdkWindow) { +- gdk_window_move_resize(mGdkWindow, aX, aY, aWidth, aHeight); ++ gdk_window_move_resize(mGdkWindow, x, y, width, height); + } + } + + void + nsWindow::NativeShow(bool aAction) + { + if (aAction) { + // unset our flag now that our window has been shown +@@ -6178,18 +6202,18 @@ nsWindow::GetThebesSurface(cairo_t *cr) + #if (MOZ_WIDGET_GTK == 2) + gdk_drawable_get_size(GDK_DRAWABLE(mGdkWindow), &width, &height); + #else + width = gdk_window_get_width(mGdkWindow); + height = gdk_window_get_height(mGdkWindow); + #endif + + // Owen Taylor says this is the right thing to do! +- width = std::min(32767, width); +- height = std::min(32767, height); ++ width = std::min(32767, (int)std::ceil(GdkCoordToDevicePixels(width))); ++ height = std::min(32767, (int)std::ceil(GdkCoordToDevicePixels(height))); + gfxIntSize size(width, height); + + GdkVisual *gdkVisual = gdk_window_get_visual(mGdkWindow); + Visual* visual = gdk_x11_visual_get_xvisual(gdkVisual); + + # ifdef MOZ_HAVE_SHMIMAGE + bool usingShm = false; + if (nsShmImage::UseShm()) { +@@ -6204,18 +6228,27 @@ nsWindow::GetThebesSurface(cairo_t *cr) + } + if (!usingShm) + # endif // MOZ_HAVE_SHMIMAGE + { + #if (MOZ_WIDGET_GTK == 3) + #if MOZ_TREE_CAIRO + #error "cairo-gtk3 target must be built with --enable-system-cairo" + #else ++ // Available as of Cairo 1.14 ++ static void (*CairoSurfaceSetDeviceScalePtr) (cairo_surface_t*,double,double) = ++ (void (*)(cairo_surface_t*,double,double)) dlsym(RTLD_DEFAULT, ++ "cairo_surface_set_device_scale"); ++ + if (cr) { + cairo_surface_t *surf = cairo_get_target(cr); ++ if (GdkScaleFactor() > 1) { ++ // Disable auto-scaling on HiDPI devices, let mozilla manage it. ++ (*CairoSurfaceSetDeviceScalePtr)(surf, 1, 1); ++ } + if (cairo_surface_status(surf) != CAIRO_STATUS_SUCCESS) { + NS_NOTREACHED("Missing cairo target?"); + return nullptr; + } + mThebesSurface = gfxASurface::Wrap(surf); + } else + #endif + #endif // (MOZ_WIDGET_GTK == 3) +@@ -6286,16 +6319,18 @@ nsWindow::BeginMoveDrag(WidgetMouseEvent + + GdkWindow *gdk_window; + gint button, screenX, screenY; + if (!GetDragInfo(aEvent, &gdk_window, &button, &screenX, &screenY)) { + return NS_ERROR_FAILURE; + } + + // tell the window manager to start the move ++ screenX = DevicePixelsToGdkCoordRoundDown(screenX); ++ screenY = DevicePixelsToGdkCoordRoundDown(screenY); + gdk_window_begin_move_drag(gdk_window, button, screenX, screenY, + aEvent->time); + + return NS_OK; + } + + NS_IMETHODIMP + nsWindow::BeginResizeDrag(WidgetGUIEvent* aEvent, +@@ -6377,16 +6412,80 @@ nsWindow::ClearCachedResources() + for (GList* list = children; list; list = list->next) { + nsWindow* window = get_window_for_gdk_window(GDK_WINDOW(list->data)); + if (window) { + window->ClearCachedResources(); + } + } + } + ++gint ++nsWindow::GdkScaleFactor() ++{ ++#if (MOZ_WIDGET_GTK >= 3) ++ // Available as of GTK 3.10+ ++ static gint (*GdkWindowGetScaleFactorPtr) (GdkWindow*) = ++ (gint (*)(GdkWindow*)) dlsym(RTLD_DEFAULT, ++ "gdk_window_get_scale_factor"); ++ if (GdkWindowGetScaleFactorPtr) ++ return (*GdkWindowGetScaleFactorPtr)(mGdkWindow); ++#endif ++ return 1; ++} ++ ++ ++gint ++nsWindow::DevicePixelsToGdkCoordRoundUp(int pixels) { ++ return NSToIntCeil(float(pixels)/float(GdkScaleFactor())); ++} ++ ++gint ++nsWindow::DevicePixelsToGdkCoordRoundDown(int pixels) { ++ return NSToIntFloor(float(pixels)/float(GdkScaleFactor())); ++} ++ ++GdkPoint ++nsWindow::DevicePixelsToGdkPointRoundDown(nsIntPoint point) { ++ float scale = GdkScaleFactor(); ++ return { NSToIntFloor(float(point.x)/scale), ++ NSToIntFloor(float(point.y)/scale) }; ++} ++ ++GdkRectangle ++nsWindow::DevicePixelsToGdkRectRoundOut(nsIntRect rect) { ++ gint scale = GdkScaleFactor(); ++ nsIntRect scaledRect = rect; ++ scaledRect.ScaleInverseRoundOut(scale); ++ return { scaledRect.x, ++ scaledRect.y, ++ scaledRect.width, ++ scaledRect.height }; ++} ++ ++int ++nsWindow::GdkCoordToDevicePixels(gint coords) { ++ return coords * GdkScaleFactor(); ++} ++ ++nsIntPoint ++nsWindow::GdkPointToDevicePixels(GdkPoint point) { ++ gint scale = GdkScaleFactor(); ++ return nsIntPoint(point.x * scale, ++ point.y * scale); ++} ++ ++nsIntRect ++nsWindow::GdkRectToDevicePixels(GdkRectangle rect) { ++ gint scale = GdkScaleFactor(); ++ return nsIntRect(rect.x * scale, ++ rect.y * scale, ++ rect.width * scale, ++ rect.height * scale); ++} ++ + nsresult + nsWindow::SynthesizeNativeMouseEvent(nsIntPoint aPoint, + uint32_t aNativeMessage, + uint32_t aModifierFlags) + { + if (!mGdkWindow) { + return NS_OK; + } +diff --git a/widget/gtk/nsWindow.h b/widget/gtk/nsWindow.h +--- a/widget/gtk/nsWindow.h ++++ b/widget/gtk/nsWindow.h +@@ -92,16 +92,17 @@ public: + NS_IMETHOD Create(nsIWidget *aParent, + nsNativeWidget aNativeParent, + const nsIntRect &aRect, + nsDeviceContext *aContext, + nsWidgetInitData *aInitData); + NS_IMETHOD Destroy(void); + virtual nsIWidget *GetParent(); + virtual float GetDPI(); ++ virtual double GetDefaultScaleInternal(); + virtual nsresult SetParent(nsIWidget* aNewParent); + NS_IMETHOD SetModal(bool aModal); + virtual bool IsVisible() const; + NS_IMETHOD ConstrainPosition(bool aAllowSlop, + int32_t *aX, + int32_t *aY); + virtual void SetSizeConstraints(const SizeConstraints& aConstraints); + NS_IMETHOD Move(double aX, +@@ -468,16 +469,30 @@ private: + * The instance is created when the top level widget is created. And when + * the widget is destroyed, it's released. All child windows refer its + * ancestor widget's instance. So, one set of IM contexts is created for + * all windows in a hierarchy. If the children are released after the top + * level window is released, the children still have a valid pointer, + * however, IME doesn't work at that time. + */ + nsRefPtr mIMModule; ++ ++ // HiDPI scale conversion ++ gint GdkScaleFactor(); ++ ++ // To GDK ++ gint DevicePixelsToGdkCoordRoundUp(int pixels); ++ gint DevicePixelsToGdkCoordRoundDown(int pixels); ++ GdkPoint DevicePixelsToGdkPointRoundDown(nsIntPoint point); ++ GdkRectangle DevicePixelsToGdkRectRoundOut(nsIntRect rect); ++ ++ // From GDK ++ int GdkCoordToDevicePixels(gint coords); ++ nsIntPoint GdkPointToDevicePixels(GdkPoint point); ++ nsIntRect GdkRectToDevicePixels(GdkRectangle rect); + }; + + class nsChildWindow : public nsWindow { + public: + nsChildWindow(); + ~nsChildWindow(); + }; +