Bug 785667 - Make plugins work with HiDPI mode on the Mac. r=bgirard
authorSteven Michaud <smichaud@pobox.com>
Tue, 02 Oct 2012 14:48:05 -0500
changeset 108926 d6f8b964ca275847feed01c5a3f96e19773a8854
parent 108925 443e8610362813d6bcbf8797673c528d6922acbb
child 108927 f63b7f854ce400192d5f155821b063ffb472ad86
push id15761
push usersmichaud@pobox.com
push dateTue, 02 Oct 2012 19:48:30 +0000
treeherdermozilla-inbound@d6f8b964ca27 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbgirard
bugs785667
milestone18.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 785667 - Make plugins work with HiDPI mode on the Mac. r=bgirard
dom/plugins/base/nsIPluginInstanceOwner.idl
dom/plugins/base/nsNPAPIPlugin.cpp
dom/plugins/base/nsNPAPIPluginInstance.cpp
dom/plugins/base/nsNPAPIPluginInstance.h
dom/plugins/base/nsPluginInstanceOwner.cpp
dom/plugins/ipc/NPEventOSX.h
dom/plugins/ipc/PPluginInstance.ipdl
dom/plugins/ipc/PluginInstanceChild.cpp
dom/plugins/ipc/PluginInstanceChild.h
dom/plugins/ipc/PluginInstanceParent.cpp
dom/plugins/ipc/PluginMessageUtils.cpp
dom/plugins/ipc/PluginMessageUtils.h
dom/plugins/ipc/PluginUtilsOSX.h
dom/plugins/ipc/PluginUtilsOSX.mm
dom/plugins/test/mochitest/cocoa_focus.html
gfx/2d/MacIOSurface.h
gfx/2d/QuartzSupport.h
gfx/2d/QuartzSupport.mm
layout/generic/nsObjectFrame.cpp
layout/generic/test/plugin_focus_helper.html
widget/cocoa/nsScreenCocoa.h
widget/cocoa/nsScreenCocoa.mm
widget/nsIScreen.idl
widget/xpwidgets/nsBaseScreen.cpp
widget/xpwidgets/nsBaseScreen.h
--- a/dom/plugins/base/nsIPluginInstanceOwner.idl
+++ b/dom/plugins/base/nsIPluginInstanceOwner.idl
@@ -14,17 +14,17 @@ interface nsIDocument;
 #include "nsNPAPIPluginInstance.h"
 class nsPluginEvent;
 %}
 
 [ptr] native nsNPAPIPluginInstancePtr(nsNPAPIPluginInstance);
 
 // Do not make this interface scriptable, because the virtual functions in C++
 // blocks will make script call the wrong functions.
-[uuid(23bd0a76-a5dc-4a1d-be76-13d7a0dfd9ff)]
+[uuid(CE1EE148-B201-4DC7-8A65-311143EA01BF)]
 interface nsIPluginInstanceOwner : nsISupports
 {
   /**
    * Let the owner know what its instance is
    */
   void setInstance(in nsNPAPIPluginInstancePtr aInstance);
 
   /**
@@ -114,9 +114,15 @@ interface nsIPluginInstanceOwner : nsISu
 %{C++
   virtual void SendIdleEvent() = 0;
 %}
 
   /**
    * Call NPP_SetWindow on the plugin.
    */
   void callSetWindow();
+
+  /**
+   * Get the contents scale factor for the screen the plugin is
+   * drawn on.
+   */
+  double getContentsScaleFactor();
 };
--- a/dom/plugins/base/nsNPAPIPlugin.cpp
+++ b/dom/plugins/base/nsNPAPIPlugin.cpp
@@ -2138,16 +2138,24 @@ NPError NP_CALLBACK
 
     return NPERR_NO_ERROR;
   }
 
   case NPNVsupportsUpdatedCocoaTextInputBool: {
     *(NPBool*)result = true;
     return NPERR_NO_ERROR;
   }
+
+  case NPNVcontentsScaleFactor: {
+    nsNPAPIPluginInstance *inst =
+      (nsNPAPIPluginInstance *) (npp ? npp->ndata : nullptr);
+    double scaleFactor = inst ? inst->GetContentsScaleFactor() : 1.0;
+    *(double*)result = scaleFactor;
+    return NPERR_NO_ERROR;
+  }
 #endif
 
 #ifdef MOZ_WIDGET_ANDROID
     case kLogInterfaceV0_ANPGetValue: {
       LOG("get log interface");
       ANPLogInterfaceV0 *i = (ANPLogInterfaceV0 *) result;
       InitLogInterface(i);
       return NPERR_NO_ERROR;
--- a/dom/plugins/base/nsNPAPIPluginInstance.cpp
+++ b/dom/plugins/base/nsNPAPIPluginInstance.cpp
@@ -1711,8 +1711,18 @@ nsNPAPIPluginInstance::CarbonNPAPIFailur
   }
 
   nsCOMPtr<nsIRunnable> e = new CarbonEventModelFailureEvent(content);
   nsresult rv = NS_DispatchToCurrentThread(e);
   if (NS_FAILED(rv)) {
     NS_WARNING("Failed to dispatch CarbonEventModelFailureEvent.");
   }
 }
+
+double
+nsNPAPIPluginInstance::GetContentsScaleFactor()
+{
+  double scaleFactor = 1.0;
+  if (mOwner) {
+    mOwner->GetContentsScaleFactor(&scaleFactor);
+  }
+  return scaleFactor;
+}
--- a/dom/plugins/base/nsNPAPIPluginInstance.h
+++ b/dom/plugins/base/nsNPAPIPluginInstance.h
@@ -261,16 +261,19 @@ public:
                            void *initData, NPAsyncSurface *surface);
   NPError FinalizeAsyncSurface(NPAsyncSurface *surface);
   void SetCurrentAsyncSurface(NPAsyncSurface *surface, NPRect *changed);
 
   // Called when the instance fails to instantiate beceause the Carbon
   // event model is not supported.
   void CarbonNPAPIFailure();
 
+  // Returns the contents scale factor of the screen the plugin is drawn on.
+  double GetContentsScaleFactor();
+
 protected:
 
   nsresult GetTagType(nsPluginTagType *result);
   nsresult GetAttributes(uint16_t& n, const char*const*& names,
                          const char*const*& values);
   nsresult GetParameters(uint16_t& n, const char*const*& names,
                          const char*const*& values);
   nsresult GetMode(int32_t *result);
--- a/dom/plugins/base/nsPluginInstanceOwner.cpp
+++ b/dom/plugins/base/nsPluginInstanceOwner.cpp
@@ -659,16 +659,22 @@ NS_IMETHODIMP nsPluginInstanceOwner::Inv
                                   invalidRect->bottom - invalidRect->top));
     return NS_OK;
   }
 #endif
   nsIntRect rect(invalidRect->left,
                  invalidRect->top,
                  invalidRect->right - invalidRect->left,
                  invalidRect->bottom - invalidRect->top);
+  // invalidRect is in "display pixels".  In non-HiDPI modes "display pixels"
+  // are device pixels.  But in HiDPI modes each display pixel corresponds
+  // to more than one device pixel.
+  double scaleFactor = 1.0;
+  GetContentsScaleFactor(&scaleFactor);
+  rect.ScaleRoundOut(scaleFactor);
   mObjectFrame->InvalidateLayer(nsDisplayItem::TYPE_PLUGIN, &rect);
   return NS_OK;
 }
 
 NS_IMETHODIMP nsPluginInstanceOwner::InvalidateRegion(NPRegion invalidRegion)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
@@ -1492,26 +1498,34 @@ void nsPluginInstanceOwner::RenderCoreAn
 {
   if (aWidth == 0 || aHeight == 0)
     return;
 
   if (!mCARenderer) {
     mCARenderer = new nsCARenderer();
   }
 
+  // aWidth and aHeight are in "display pixels".  In non-HiDPI modes
+  // "display pixels" are device pixels.  But in HiDPI modes each
+  // display pixel corresponds to more than one device pixel.
+  double scaleFactor = 1.0;
+  GetContentsScaleFactor(&scaleFactor);
+
   if (!mIOSurface ||
       (mIOSurface->GetWidth() != (size_t)aWidth ||
-       mIOSurface->GetHeight() != (size_t)aHeight)) {
+       mIOSurface->GetHeight() != (size_t)aHeight ||
+       mIOSurface->GetContentsScaleFactor() != scaleFactor)) {
     mIOSurface = nullptr;
 
     // If the renderer is backed by an IOSurface, resize it as required.
-    mIOSurface = MacIOSurface::CreateIOSurface(aWidth, aHeight);
+    mIOSurface = MacIOSurface::CreateIOSurface(aWidth, aHeight, scaleFactor);
     if (mIOSurface) {
       RefPtr<MacIOSurface> attachSurface = MacIOSurface::LookupSurface(
-                                              mIOSurface->GetIOSurfaceID());
+                                              mIOSurface->GetIOSurfaceID(),
+                                              scaleFactor);
       if (attachSurface) {
         mCARenderer->AttachIOSurface(attachSurface);
       } else {
         NS_ERROR("IOSurface attachment failed");
         mIOSurface = nullptr;
       }
     }
   }
@@ -1524,34 +1538,35 @@ void nsPluginInstanceOwner::RenderCoreAn
     void *caLayer = NULL;
     nsresult rv = mInstance->GetValueFromPlugin(NPPVpluginCoreAnimationLayer, &caLayer);
     if (NS_FAILED(rv) || !caLayer) {
       return;
     }
 
     // We don't run Flash in-process so we can unconditionally disallow
     // the offliner renderer.
-    mCARenderer->SetupRenderer(caLayer, aWidth, aHeight, DISALLOW_OFFLINE_RENDERER);
+    mCARenderer->SetupRenderer(caLayer, aWidth, aHeight, scaleFactor,
+                               DISALLOW_OFFLINE_RENDERER);
 
     // Setting up the CALayer requires resetting the painting otherwise we
     // get garbage for the first few frames.
     FixUpPluginWindow(ePluginPaintDisable);
     FixUpPluginWindow(ePluginPaintEnable);
   }
 
   CGImageRef caImage = NULL;
-  nsresult rt = mCARenderer->Render(aWidth, aHeight, &caImage);
+  nsresult rt = mCARenderer->Render(aWidth, aHeight, scaleFactor, &caImage);
   if (rt == NS_OK && mIOSurface && mColorProfile) {
     nsCARenderer::DrawSurfaceToCGContext(aCGContext, mIOSurface, mColorProfile,
                                          0, 0, aWidth, aHeight);
   } else if (rt == NS_OK && caImage != NULL) {
     // Significant speed up by resetting the scaling
     ::CGContextSetInterpolationQuality(aCGContext, kCGInterpolationNone );
-    ::CGContextTranslateCTM(aCGContext, 0, aHeight);
-    ::CGContextScaleCTM(aCGContext, 1.0, -1.0);
+    ::CGContextTranslateCTM(aCGContext, 0, (double) aHeight * scaleFactor);
+    ::CGContextScaleCTM(aCGContext, scaleFactor, -scaleFactor);
 
     ::CGContextDrawImage(aCGContext, CGRectMake(0,0,aWidth,aHeight), caImage);
   } else {
     NS_NOTREACHED("nsCARenderer::Render failure");
   }
 }
 
 void* nsPluginInstanceOwner::GetPluginPortCopy()
@@ -2146,22 +2161,27 @@ nsEventStatus nsPluginInstanceOwner::Pro
   EventRecord synthCarbonEvent;
 #endif
   NPCocoaEvent synthCocoaEvent;
   void* event = anEvent.pluginEvent;
   nsPoint pt =
   nsLayoutUtils::GetEventCoordinatesRelativeTo(&anEvent, mObjectFrame) -
   mObjectFrame->GetContentRectRelativeToSelf().TopLeft();
   nsPresContext* presContext = mObjectFrame->PresContext();
-  nsIntPoint ptPx(presContext->AppUnitsToDevPixels(pt.x),
-                  presContext->AppUnitsToDevPixels(pt.y));
+  // Plugin event coordinates need to be translated from device pixels
+  // into "display pixels" in HiDPI modes.
+  double scaleFactor = 1.0;
+  GetContentsScaleFactor(&scaleFactor);
+  size_t intScaleFactor = ceil(scaleFactor);
+  nsIntPoint ptPx(presContext->AppUnitsToDevPixels(pt.x) / intScaleFactor,
+                  presContext->AppUnitsToDevPixels(pt.y) / intScaleFactor);
 #ifndef NP_NO_CARBON
   nsIntPoint geckoScreenCoords = mWidget->WidgetToScreenOffset();
-  ::Point carbonPt = { static_cast<short>(ptPx.y + geckoScreenCoords.y),
-                       static_cast<short>(ptPx.x + geckoScreenCoords.x) };
+  ::Point carbonPt = { static_cast<short>(ptPx.y + geckoScreenCoords.y / intScaleFactor),
+                       static_cast<short>(ptPx.x + geckoScreenCoords.x / intScaleFactor) };
   if (eventModel == NPEventModelCarbon) {
     if (event && anEvent.eventStructType == NS_MOUSE_EVENT) {
       static_cast<EventRecord*>(event)->where = carbonPt;
     }
   }
 #endif
   if (!event) {
 #ifndef NP_NO_CARBON
@@ -2802,32 +2822,42 @@ nsPluginInstanceOwner::Destroy()
 
 // Paints are handled differently, so we just simulate an update event.
 
 #ifdef XP_MACOSX
 void nsPluginInstanceOwner::Paint(const gfxRect& aDirtyRect, CGContextRef cgContext)
 {
   if (!mInstance || !mObjectFrame)
     return;
- 
+
+  gfxRect dirtyRectCopy = aDirtyRect; 
+  double scaleFactor = 1.0;
+  GetContentsScaleFactor(&scaleFactor);
+  if (scaleFactor != 1.0) {
+    ::CGContextScaleCTM(cgContext, scaleFactor, scaleFactor);
+    // Convert aDirtyRect from device pixels to "display pixels"
+    // for HiDPI modes
+    dirtyRectCopy.ScaleRoundOut(1.0 / scaleFactor);
+  }
+
   nsCOMPtr<nsIPluginWidget> pluginWidget = do_QueryInterface(mWidget);
   if (pluginWidget && NS_SUCCEEDED(pluginWidget->StartDrawPlugin())) {
 #ifndef NP_NO_CARBON
     void* window = FixUpPluginWindow(ePluginPaintEnable);
     if (GetEventModel() == NPEventModelCarbon && window) {
       EventRecord updateEvent;
       InitializeEventRecord(&updateEvent, nullptr);
       updateEvent.what = updateEvt;
       updateEvent.message = UInt32(window);
 
       mInstance->HandleEvent(&updateEvent, nullptr);
     } else if (GetEventModel() == NPEventModelCocoa)
 #endif
     {
-      DoCocoaEventDrawRect(aDirtyRect, cgContext);
+      DoCocoaEventDrawRect(dirtyRectCopy, cgContext);
     }
     pluginWidget->EndDrawPlugin();
   }
 }
 
 void nsPluginInstanceOwner::DoCocoaEventDrawRect(const gfxRect& aDrawRect, CGContextRef cgContext)
 {
   if (!mInstance || !mObjectFrame)
@@ -3465,18 +3495,24 @@ void* nsPluginInstanceOwner::FixUpPlugin
   if (eventModel == NPEventModelCarbon) {
     NS_NPAPI_CarbonWindowFrame(static_cast<WindowRef>(static_cast<NP_CGContext*>(pluginPort)->window), windowRect);
   } else
 #endif
   {
     NS_NPAPI_CocoaWindowFrame(cocoaTopLevelWindow, windowRect);
   }
 
-  mPluginWindow->x = geckoScreenCoords.x - windowRect.x;
-  mPluginWindow->y = geckoScreenCoords.y - windowRect.y;
+  double scaleFactor = 1.0;
+  GetContentsScaleFactor(&scaleFactor);
+  int intScaleFactor = ceil(scaleFactor);
+
+  // Convert geckoScreenCoords from device pixels to "display pixels"
+  // for HiDPI modes.
+  mPluginWindow->x = geckoScreenCoords.x/intScaleFactor - windowRect.x;
+  mPluginWindow->y = geckoScreenCoords.y/intScaleFactor - windowRect.y;
 
   NPRect oldClipRect = mPluginWindow->clipRect;
   
   // fix up the clipping region
   mPluginWindow->clipRect.top    = widgetClip.y;
   mPluginWindow->clipRect.left   = widgetClip.x;
 
   if (!mWidgetVisible || inPaintState == ePluginPaintDisable) {
@@ -3684,16 +3720,44 @@ void nsPluginInstanceOwner::RemoveScroll
         sf->RemoveScrollPositionListener(this);
       }
     }
     mRegisteredScrollPositionListener = false;
   }
 }
 #endif
 
+NS_IMETHODIMP
+nsPluginInstanceOwner::GetContentsScaleFactor(double *result)
+{
+  NS_ENSURE_ARG_POINTER(result);
+  double scaleFactor = 1.0;
+  if (mWidget) {
+    scaleFactor = mWidget->GetDefaultScale();
+  } else {
+    nsCOMPtr<nsIScreenManager> screenMgr =
+      do_GetService("@mozilla.org/gfx/screenmanager;1");
+    if (screenMgr) {
+      nsCOMPtr<nsIScreen> screen;
+      nsIntRect screenRect = mObjectFrame->GetScreenRect();
+      screenMgr->ScreenForRect(screenRect.x, screenRect.y,
+                               screenRect.width, screenRect.height,
+                               getter_AddRefs(screen));
+      if (screen) {
+        nsresult rv = screen->GetContentsScaleFactor(&scaleFactor);
+        if (NS_FAILED(rv)) {
+          scaleFactor = 1.0;
+        }
+      }
+    }
+  }
+  *result = scaleFactor;
+  return NS_OK;
+}
+
 void nsPluginInstanceOwner::SetFrame(nsObjectFrame *aFrame)
 {
   // Don't do anything if the frame situation hasn't changed.
   if (mObjectFrame == aFrame) {
     return;
   }
 
   // If we already have a frame that is changing or going away...
--- a/dom/plugins/ipc/NPEventOSX.h
+++ b/dom/plugins/ipc/NPEventOSX.h
@@ -11,16 +11,17 @@
 #include "npapi.h"
 
 namespace mozilla {
 
 namespace plugins {
 
 struct NPRemoteEvent {
     NPCocoaEvent event;
+    double contentsScaleFactor;
 };
 
 } // namespace plugins
 
 } // namespace mozilla
 
 namespace IPC {
 
@@ -73,16 +74,17 @@ struct ParamTraits<mozilla::plugins::NPR
                 break;
             case NPCocoaEventTextInput:
                 WriteParam(aMsg, aParam.event.data.text.text);
                 break;
             default:
                 NS_NOTREACHED("Attempted to serialize unknown event type.");
                 return;
         }
+        aMsg->WriteDouble(aParam.contentsScaleFactor);
     }
 
     static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
     {
         int type = 0;
         if (!aMsg->ReadInt(aIter, &type)) {
             return false;
         }
@@ -169,16 +171,19 @@ struct ParamTraits<mozilla::plugins::NPR
                 if (!ReadParam(aMsg, aIter, &aResult->event.data.text.text)) {
                     return false;
                 }
                 break;
             default:
                 NS_NOTREACHED("Attempted to de-serialize unknown event type.");
                 return false;
         }
+        if (!aMsg->ReadDouble(aIter, &aResult->contentsScaleFactor)) {
+            return false;
+        }
 
         return true;
     }
 
     static void Log(const paramType& aParam, std::wstring* aLog)
     {
         aLog->append(L"(NPCocoaEvent)");
     }
--- a/dom/plugins/ipc/PPluginInstance.ipdl
+++ b/dom/plugins/ipc/PPluginInstance.ipdl
@@ -34,16 +34,17 @@ using nsIntRect;
 using nsTextEvent;
 using nsKeyEvent;
 
 namespace mozilla {
 namespace plugins {
 
 struct IOSurfaceDescriptor {
   uint32_t surfaceId;
+  double contentsScaleFactor;
 };
 
 union SurfaceDescriptor {
   Shmem;
   SurfaceDescriptorX11;
   PPluginSurface; // used on Windows
   IOSurfaceDescriptor; // used on OSX 10.5+
   // Descriptor can be null here in case
--- a/dom/plugins/ipc/PluginInstanceChild.cpp
+++ b/dom/plugins/ipc/PluginInstanceChild.cpp
@@ -98,16 +98,19 @@ template<>
 struct RunnableMethodTraits<PluginInstanceChild>
 {
     static void RetainCallee(PluginInstanceChild* obj) { }
     static void ReleaseCallee(PluginInstanceChild* obj) { }
 };
 
 PluginInstanceChild::PluginInstanceChild(const NPPluginFuncs* aPluginIface)
     : mPluginIface(aPluginIface)
+#if defined(XP_MACOSX)
+    , mContentsScaleFactor(1.0)
+#endif
     , mDrawingModel(kDefaultDrawingModel)
     , mCurrentAsyncSurface(0)
     , mAsyncInvalidateMutex("PluginInstanceChild::mAsyncInvalidateMutex")
     , mAsyncInvalidateTask(0)
     , mCachedWindowActor(nullptr)
     , mCachedElementActor(nullptr)
 #if defined(MOZ_WIDGET_GTK)
     , mXEmbed(false)
@@ -428,16 +431,21 @@ PluginInstanceChild::NPN_GetValue(NPNVar
       return NPERR_NO_ERROR;
     }
 
 #ifndef NP_NO_QUICKDRAW
     case NPNVsupportsQuickDrawBool: {
         *((NPBool*)aValue) = false;
         return NPERR_NO_ERROR;
     }
+
+    case NPNVcontentsScaleFactor: {
+        *static_cast<double*>(aValue) = mContentsScaleFactor;
+        return NPERR_NO_ERROR;
+    }
 #endif /* NP_NO_QUICKDRAW */
 #endif /* XP_MACOSX */
 
 #ifdef DEBUG
     case NPNVjavascriptEnabledBool:
     case NPNVasdEnabledBool:
     case NPNVisOfflineBool:
     case NPNVSupportsXEmbedBool:
@@ -728,16 +736,22 @@ PluginInstanceChild::AnswerNPP_HandleEve
     if (GraphicsExpose == event.event.type)
         PLUGIN_LOG_DEBUG(("  received drawable 0x%lx\n",
                           event.event.xgraphicsexpose.drawable));
 #endif
 
 #ifdef XP_MACOSX
     // Mac OS X does not define an NPEvent structure. It defines more specific types.
     NPCocoaEvent evcopy = event.event;
+    // event.contentsScaleFactor <= 0 is a signal we shouldn't use it,
+    // for example when AnswerNPP_HandleEvent() is called from elsewhere
+    // in the child process (not via rpc code from the parent process).
+    if (event.contentsScaleFactor > 0) {
+      mContentsScaleFactor = event.contentsScaleFactor;
+    }
 
     // Make sure we reset mCurrentEvent in case of an exception
     AutoRestore<const NPCocoaEvent*> savePreviousEvent(mCurrentEvent);
 
     // Track the current event for NPN_PopUpContextMenu.
     mCurrentEvent = &event.event;
 #else
     // Make a copy since we may modify values.
@@ -813,32 +827,35 @@ PluginInstanceChild::AnswerNPP_HandleEve
                                                  Shmem* rtnmem)
 {
     PLUGIN_LOG_DEBUG_FUNCTION;
     AssertPluginThread();
 
     PaintTracker pt;
 
     NPCocoaEvent evcopy = event.event;
+    mContentsScaleFactor = event.contentsScaleFactor;
 
     if (evcopy.type == NPCocoaEventDrawRect) {
+        int scaleFactor = ceil(mContentsScaleFactor);
         if (!mShColorSpace) {
             mShColorSpace = CreateSystemColorSpace();
             if (!mShColorSpace) {
                 PLUGIN_LOG_DEBUG(("Could not allocate ColorSpace."));
                 *handled = false;
                 *rtnmem = mem;
                 return true;
             } 
         }
         if (!mShContext) {
             void* cgContextByte = mem.get<char>();
             mShContext = ::CGBitmapContextCreate(cgContextByte, 
-                              mWindow.width, mWindow.height, 8, 
-                              mWindow.width * 4, mShColorSpace, 
+                              mWindow.width * scaleFactor,
+                              mWindow.height * scaleFactor, 8, 
+                              mWindow.width * 4 * scaleFactor, mShColorSpace, 
                               kCGImageAlphaPremultipliedFirst |
                               kCGBitmapByteOrder32Host);
     
             if (!mShContext) {
                 PLUGIN_LOG_DEBUG(("Could not allocate CGBitmapContext."));
                 *handled = false;
                 *rtnmem = mem;
                 return true;
@@ -895,16 +912,18 @@ PluginInstanceChild::CGDraw(CGContextRef
   drawEvent.version = 0;
   drawEvent.data.draw.x = aUpdateRect.x;
   drawEvent.data.draw.y = aUpdateRect.y;
   drawEvent.data.draw.width = aUpdateRect.width;
   drawEvent.data.draw.height = aUpdateRect.height;
   drawEvent.data.draw.context = ref;
 
   NPRemoteEvent remoteDrawEvent = {drawEvent};
+  // Signal to AnswerNPP_HandleEvent() not to use this value
+  remoteDrawEvent.contentsScaleFactor = -1.0;
 
   int16_t handled;
   AnswerNPP_HandleEvent(remoteDrawEvent, &handled);
   return handled == true;
 }
 
 bool
 PluginInstanceChild::AnswerNPP_HandleEvent_IOSurface(const NPRemoteEvent& event,
@@ -912,17 +931,19 @@ PluginInstanceChild::AnswerNPP_HandleEve
                                                      int16_t* handled)
 {
     PLUGIN_LOG_DEBUG_FUNCTION;
     AssertPluginThread();
 
     PaintTracker pt;
 
     NPCocoaEvent evcopy = event.event;
-    RefPtr<MacIOSurface> surf = MacIOSurface::LookupSurface(surfaceid);
+    mContentsScaleFactor = event.contentsScaleFactor;
+    RefPtr<MacIOSurface> surf = MacIOSurface::LookupSurface(surfaceid,
+                                                            mContentsScaleFactor);
     if (!surf) {
         NS_ERROR("Invalid IOSurface.");
         *handled = false;
         return false;
     }
 
     if (!mCARenderer) {
       mCARenderer = new nsCARenderer();
@@ -939,31 +960,33 @@ PluginInstanceChild::AnswerNPP_HandleEve
             if (result != NPERR_NO_ERROR || !caLayer) {
                 PLUGIN_LOG_DEBUG(("Plugin requested CoreAnimation but did not "
                                   "provide CALayer."));
                 *handled = false;
                 return false;
             }
 
             mCARenderer->SetupRenderer(caLayer, mWindow.width, mWindow.height,
+                            mContentsScaleFactor,
                             GetQuirks() & PluginModuleChild::QUIRK_ALLOW_OFFLINE_RENDERER ?
                             ALLOW_OFFLINE_RENDERER : DISALLOW_OFFLINE_RENDERER);
 
             // Flash needs to have the window set again after this step
             if (mPluginIface->setwindow)
                 (void) mPluginIface->setwindow(&mData, &mWindow);
         }
     } else {
         PLUGIN_LOG_DEBUG(("Invalid event type for "
                           "AnswerNNP_HandleEvent_IOSurface."));
         *handled = false;
         return false;
     } 
 
-    mCARenderer->Render(mWindow.width, mWindow.height, nullptr);
+    mCARenderer->Render(mWindow.width, mWindow.height,
+                        mContentsScaleFactor, nullptr);
 
     return true;
 
 }
 
 #else
 bool
 PluginInstanceChild::AnswerNPP_HandleEvent_IOSurface(const NPRemoteEvent& event,
@@ -1179,16 +1202,17 @@ PluginInstanceChild::AnswerNPP_SetWindow
 #elif defined(XP_MACOSX)
 
     mWindow.x = aWindow.x;
     mWindow.y = aWindow.y;
     mWindow.width = aWindow.width;
     mWindow.height = aWindow.height;
     mWindow.clipRect = aWindow.clipRect;
     mWindow.type = aWindow.type;
+    mContentsScaleFactor = aWindow.contentsScaleFactor;
 
     if (mShContext) {
         // Release the shared context so that it is reallocated
         // with the new size. 
         ::CGContextRelease(mShContext);
         mShContext = nullptr;
     }
 
@@ -2763,16 +2787,19 @@ PluginInstanceChild::DoAsyncSetWindow(co
         mAccumulatedInvalidRect = nsIntRect(0, 0, aWindow.width, aWindow.height);
 
     mWindow.x = aWindow.x;
     mWindow.y = aWindow.y;
     mWindow.width = aWindow.width;
     mWindow.height = aWindow.height;
     mWindow.clipRect = aWindow.clipRect;
     mWindow.type = aWindow.type;
+#ifdef XP_MACOSX
+    mContentsScaleFactor = aWindow.contentsScaleFactor;
+#endif
 
     if (GetQuirks() & PluginModuleChild::QUIRK_SILVERLIGHT_DEFAULT_TRANSPARENT)
         mIsTransparent = true;
 
     mLayersRendering = true;
     mSurfaceType = aSurfaceType;
     UpdateWindowAttributes(true);
 
@@ -3019,23 +3046,24 @@ PluginInstanceChild::EnsureCurrentBuffer
                 return false;
             }
         }
         mDoubleBufferCARenderer.SetCALayer(caLayer);
     }
 
     if (mDoubleBufferCARenderer.HasFrontSurface() &&
         (mDoubleBufferCARenderer.GetFrontSurfaceWidth() != mWindow.width ||
-         mDoubleBufferCARenderer.GetFrontSurfaceHeight() != mWindow.height) ) {
+         mDoubleBufferCARenderer.GetFrontSurfaceHeight() != mWindow.height ||
+         mDoubleBufferCARenderer.GetContentsScaleFactor() != mContentsScaleFactor)) {
         mDoubleBufferCARenderer.ClearFrontSurface();
     }
 
     if (!mDoubleBufferCARenderer.HasFrontSurface()) {
         bool allocSurface = mDoubleBufferCARenderer.InitFrontSurface(
-                                mWindow.width, mWindow.height,
+                                mWindow.width, mWindow.height, mContentsScaleFactor,
                                 GetQuirks() & PluginModuleChild::QUIRK_ALLOW_OFFLINE_RENDERER ?
                                 ALLOW_OFFLINE_RENDERER : DISALLOW_OFFLINE_RENDERER);
         if (!allocSurface) {
             PLUGIN_LOG_DEBUG(("Fail to allocate front IOSurface"));
             return false;
         }
 
         if (mPluginIface->setwindow)
@@ -3475,17 +3503,18 @@ PluginInstanceChild::ShowPluginFrame()
             mozilla::plugins::PluginUtilsOSX::Repaint(mCGLayer, rect);
         }
 
         mDoubleBufferCARenderer.Render();
 
         NPRect r = { (uint16_t)rect.y, (uint16_t)rect.x,
                      (uint16_t)rect.YMost(), (uint16_t)rect.XMost() };
         SurfaceDescriptor currSurf;
-        currSurf = IOSurfaceDescriptor(mDoubleBufferCARenderer.GetFrontSurfaceID());
+        currSurf = IOSurfaceDescriptor(mDoubleBufferCARenderer.GetFrontSurfaceID(),
+                                       mDoubleBufferCARenderer.GetContentsScaleFactor());
 
         mHasPainted = true;
 
         SurfaceDescriptor returnSurf;
 
         if (!SendShow(r, currSurf, &returnSurf)) {
             return false;
         }
--- a/dom/plugins/ipc/PluginInstanceChild.h
+++ b/dom/plugins/ipc/PluginInstanceChild.h
@@ -352,16 +352,19 @@ private:
         LPARAM               mLParam;
         bool                 mWindowed;
     };
 
 #endif
     const NPPluginFuncs* mPluginIface;
     NPP_t mData;
     NPWindow mWindow;
+#if defined(XP_MACOSX)
+    double mContentsScaleFactor;
+#endif
     int16_t               mDrawingModel;
     NPAsyncSurface* mCurrentAsyncSurface;
     struct AsyncBitmapData {
       void *mRemotePtr;
       Shmem mShmem;
     };
 
     static PLDHashOperator DeleteSurface(NPAsyncSurface* surf, nsAutoPtr<AsyncBitmapData> &data, void* userArg);
--- a/dom/plugins/ipc/PluginInstanceParent.cpp
+++ b/dom/plugins/ipc/PluginInstanceParent.cpp
@@ -563,26 +563,29 @@ PluginInstanceParent::RecvShow(const NPR
             NS_WARNING("back surface not readable");
             return false;
         }
         surface = gfxSharedImageSurface::Open(newSurface.get_Shmem());
     }
 #ifdef XP_MACOSX
     else if (newSurface.type() == SurfaceDescriptor::TIOSurfaceDescriptor) {
         IOSurfaceDescriptor iodesc = newSurface.get_IOSurfaceDescriptor();
-    
-        RefPtr<MacIOSurface> newIOSurface = MacIOSurface::LookupSurface(iodesc.surfaceId());
+
+        RefPtr<MacIOSurface> newIOSurface =
+          MacIOSurface::LookupSurface(iodesc.surfaceId(),
+                                      iodesc.contentsScaleFactor());
 
         if (!newIOSurface) {
             NS_WARNING("Got bad IOSurfaceDescriptor in RecvShow");
             return false;
         }
       
         if (mFrontIOSurface)
-            *prevSurface = IOSurfaceDescriptor(mFrontIOSurface->GetIOSurfaceID());
+            *prevSurface = IOSurfaceDescriptor(mFrontIOSurface->GetIOSurfaceID(),
+                                               mFrontIOSurface->GetContentsScaleFactor());
         else
             *prevSurface = null_t();
 
         mFrontIOSurface = newIOSurface;
 
         RecvNPN_InvalidateRect(updatedRect);
 
         PLUGIN_LOG_DEBUG(("   (RecvShow invalidated for surface %p)",
@@ -652,16 +655,21 @@ PluginInstanceParent::AsyncSetWindow(NPW
     mWindowType = aWindow->type;
     window.window = reinterpret_cast<uint64_t>(aWindow->window);
     window.x = aWindow->x;
     window.y = aWindow->y;
     window.width = aWindow->width;
     window.height = aWindow->height;
     window.clipRect = aWindow->clipRect;
     window.type = aWindow->type;
+#ifdef XP_MACOSX
+    double scaleFactor = 1.0;
+    mNPNIface->getvalue(mNPP, NPNVcontentsScaleFactor, &scaleFactor);
+    window.contentsScaleFactor = scaleFactor;
+#endif
     if (!SendAsyncSetWindow(gfxPlatform::GetPlatform()->ScreenReferenceSurface()->GetType(),
                             window))
         return NS_ERROR_FAILURE;
 
     return NS_OK;
 }
 
 #if defined(MOZ_WIDGET_QT) && (MOZ_PLATFORM_MAEMO == 6)
@@ -995,37 +1003,44 @@ PluginInstanceParent::NPP_SetWindow(cons
     window.y = aWindow->y;
     window.width = aWindow->width;
     window.height = aWindow->height;
     window.clipRect = aWindow->clipRect; // MacOS specific
     window.type = aWindow->type;
 #endif
 
 #if defined(XP_MACOSX)
-    if (mShWidth != window.width || mShHeight != window.height) {
+    double floatScaleFactor = 1.0;
+    mNPNIface->getvalue(mNPP, NPNVcontentsScaleFactor, &floatScaleFactor);
+    int scaleFactor = ceil(floatScaleFactor);
+    window.contentsScaleFactor = floatScaleFactor;
+
+    if (mShWidth != window.width * scaleFactor || mShHeight != window.height * scaleFactor) {
         if (mDrawingModel == NPDrawingModelCoreAnimation || 
             mDrawingModel == NPDrawingModelInvalidatingCoreAnimation) {
-            mIOSurface = MacIOSurface::CreateIOSurface(window.width, window.height);
-        } else if (uint32_t(mShWidth * mShHeight) != window.width * window.height) {
+            mIOSurface = MacIOSurface::CreateIOSurface(window.width, window.height,
+                                                       floatScaleFactor);
+        } else if (uint32_t(mShWidth * mShHeight) !=
+                   window.width * scaleFactor * window.height * scaleFactor) {
             if (mShWidth != 0 && mShHeight != 0) {
                 DeallocShmem(mShSurface);
                 mShWidth = 0;
                 mShHeight = 0;
             }
 
             if (window.width != 0 && window.height != 0) {
-                if (!AllocShmem(window.width * window.height*4, 
+                if (!AllocShmem(window.width * scaleFactor * window.height*4 * scaleFactor,
                                 SharedMemory::TYPE_BASIC, &mShSurface)) {
                     PLUGIN_LOG_DEBUG(("Shared memory could not be allocated."));
                     return NPERR_GENERIC_ERROR;
                 } 
             }
         }
-        mShWidth = window.width;
-        mShHeight = window.height;
+        mShWidth = window.width * scaleFactor;
+        mShHeight = window.height * scaleFactor;
     }
 #endif
 
 #if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
     const NPSetWindowCallbackStruct* ws_info =
       static_cast<NPSetWindowCallbackStruct*>(aWindow->ws_info);
     window.visualID = ws_info->visual ? ws_info->visual->visualid : None;
     window.colormap = ws_info->colormap;
@@ -1171,16 +1186,21 @@ PluginInstanceParent::NPP_HandleEvent(vo
 
 #if defined(XP_MACOSX)
     NPCocoaEvent* npevent = reinterpret_cast<NPCocoaEvent*>(event);
 #else
     NPEvent* npevent = reinterpret_cast<NPEvent*>(event);
 #endif
     NPRemoteEvent npremoteevent;
     npremoteevent.event = *npevent;
+#if defined(XP_MACOSX)
+    double scaleFactor = 1.0;
+    mNPNIface->getvalue(mNPP, NPNVcontentsScaleFactor, &scaleFactor);
+    npremoteevent.contentsScaleFactor = scaleFactor;
+#endif
     int16_t handled = 0;
 
 #if defined(OS_WIN)
     if (mWindowType == NPWindowTypeDrawable) {
         if (IsAsyncDrawing()) {
             if (npevent->event == WM_PAINT || npevent->event == DoublePassRenderingEvent()) {
                 // This plugin maintains its own async drawing.
                 return handled;
--- a/dom/plugins/ipc/PluginMessageUtils.cpp
+++ b/dom/plugins/ipc/PluginMessageUtils.cpp
@@ -52,16 +52,19 @@ NPRemoteWindow::NPRemoteWindow() :
   window(0), x(0), y(0), width(0), height(0), type(NPWindowTypeDrawable)
 #if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
   , visualID(0)
   , colormap(0)
 #endif /* XP_UNIX */
 #if defined(XP_WIN)
   ,surfaceHandle(0)
 #endif
+#if defined(XP_MACOSX)
+  ,contentsScaleFactor(1.0)
+#endif
 {
   clipRect.top = 0;
   clipRect.left = 0;
   clipRect.bottom = 0;
   clipRect.right = 0;
 }
 
 RPCChannel::RacyRPCPolicy
--- a/dom/plugins/ipc/PluginMessageUtils.h
+++ b/dom/plugins/ipc/PluginMessageUtils.h
@@ -102,16 +102,19 @@ struct NPRemoteWindow
   NPWindowType type;
 #if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
   VisualID visualID;
   Colormap colormap;
 #endif /* XP_UNIX */
 #if defined(XP_WIN)
   base::SharedMemoryHandle surfaceHandle;
 #endif
+#if defined(XP_MACOSX)
+  double contentsScaleFactor;
+#endif
 };
 
 #ifdef XP_WIN
 typedef HWND NativeWindowHandle;
 #elif defined(MOZ_X11)
 typedef XID NativeWindowHandle;
 #elif defined(XP_MACOSX) || defined(ANDROID) || defined(MOZ_WIDGET_QT)
 typedef intptr_t NativeWindowHandle; // never actually used, will always be 0
@@ -377,16 +380,19 @@ struct ParamTraits<mozilla::plugins::NPR
     WriteParam(aMsg, aParam.type);
 #if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
     aMsg->WriteULong(aParam.visualID);
     aMsg->WriteULong(aParam.colormap);
 #endif
 #if defined(XP_WIN)
     WriteParam(aMsg, aParam.surfaceHandle);
 #endif
+#if defined(XP_MACOSX)
+    aMsg->WriteDouble(aParam.contentsScaleFactor);
+#endif
   }
 
   static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
   {
     uint64_t window;
     int32_t x, y;
     uint32_t width, height;
     NPRect clipRect;
@@ -409,30 +415,39 @@ struct ParamTraits<mozilla::plugins::NPR
 #endif
 
 #if defined(XP_WIN)
     base::SharedMemoryHandle surfaceHandle;
     if (!ReadParam(aMsg, aIter, &surfaceHandle))
       return false;
 #endif
 
+#if defined(XP_MACOSX)
+    double contentsScaleFactor;
+    if (!aMsg->ReadDouble(aIter, &contentsScaleFactor))
+      return false;
+#endif
+
     aResult->window = window;
     aResult->x = x;
     aResult->y = y;
     aResult->width = width;
     aResult->height = height;
     aResult->clipRect = clipRect;
     aResult->type = type;
 #if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
     aResult->visualID = visualID;
     aResult->colormap = colormap;
 #endif
 #if defined(XP_WIN)
     aResult->surfaceHandle = surfaceHandle;
 #endif
+#if defined(XP_MACOSX)
+    aResult->contentsScaleFactor = contentsScaleFactor;
+#endif
     return true;
   }
 
   static void Log(const paramType& aParam, std::wstring* aLog)
   {
     aLog->append(StringPrintf(L"[%u, %d, %d, %u, %u, %d",
                               (unsigned long)aParam.window,
                               aParam.x, aParam.y, aParam.width,
--- a/dom/plugins/ipc/PluginUtilsOSX.h
+++ b/dom/plugins/ipc/PluginUtilsOSX.h
@@ -36,38 +36,55 @@ bool SetProcessName(const char* aProcess
  * without having to unbind nsCARenderer on every surface swaps.
  *
  * The double buffer renderer begins with no initialize surfaces.
  * The buffers can be initialized and cleared individually.
  * Swapping still occurs regardless if the buffers are initialized.
  */
 class THEBES_API nsDoubleBufferCARenderer {
 public:
-  nsDoubleBufferCARenderer() : mCALayer(nullptr) {}
+  nsDoubleBufferCARenderer() : mCALayer(nullptr), mContentsScaleFactor(1.0) {}
+  // Returns width in "display pixels".  A "display pixel" is the smallest
+  // fully addressable part of a display.  But in HiDPI modes each "display
+  // pixel" corresponds to more than one device pixel.  Multiply display pixels
+  // by mContentsScaleFactor to get device pixels.
   size_t GetFrontSurfaceWidth();
+  // Returns height in "display pixels".  Multiply by
+  // mContentsScaleFactor to get device pixels.
   size_t GetFrontSurfaceHeight();
+  // Returns width in "display pixels".  Multiply by
+  // mContentsScaleFactor to get device pixels.
   size_t GetBackSurfaceWidth();
+  // Returns height in "display pixels".  Multiply by
+  // mContentsScaleFactor to get device pixels.
   size_t GetBackSurfaceHeight();
   IOSurfaceID GetFrontSurfaceID();
 
   bool HasBackSurface();
   bool HasFrontSurface();
   bool HasCALayer();
 
   void SetCALayer(void *aCALayer);
-  bool InitFrontSurface(size_t aWidth, size_t aHeight, AllowOfflineRendererEnum aAllowOfflineRenderer);
+  // aWidth and aHeight are in "display pixels".  Multiply by
+  // aContentsScaleFactor to get device pixels.
+  bool InitFrontSurface(size_t aWidth, size_t aHeight,
+                        double aContentsScaleFactor,
+                        AllowOfflineRendererEnum aAllowOfflineRenderer);
   void Render();
   void SwapSurfaces();
   void ClearFrontSurface();
   void ClearBackSurface();
 
+  double GetContentsScaleFactor() { return mContentsScaleFactor; }
+
 private:
   void *mCALayer;
   RefPtr<nsCARenderer> mCARenderer;
   RefPtr<MacIOSurface> mFrontSurface;
   RefPtr<MacIOSurface> mBackSurface;
+  double mContentsScaleFactor;
 };
 
 } // namespace PluginUtilsOSX
 } // namespace plugins
 } // namespace mozilla
 
 #endif //dom_plugins_PluginUtilsOSX_h
--- a/dom/plugins/ipc/PluginUtilsOSX.mm
+++ b/dom/plugins/ipc/PluginUtilsOSX.mm
@@ -9,16 +9,21 @@
 #include "PluginUtilsOSX.h"
 
 // Remove definitions for try/catch interfering with ObjCException macros.
 #include "nsObjCExceptions.h"
 #include "nsCocoaUtils.h"
 
 #include "nsDebug.h"
 
+@interface CALayer (ContentsScale)
+- (double)contentsScale;
+- (void)setContentsScale:(double)scale;
+@end
+
 using namespace mozilla::plugins::PluginUtilsOSX;
 
 @interface CGBridgeLayer : CALayer {
   DrawPluginFunc mDrawFunc;
   void* mPluginInstance;
   nsIntRect mUpdateRect;
 }
 - (void) setDrawFunc: (DrawPluginFunc)aFunc pluginInstance:(void*) aPluginInstance;
@@ -300,22 +305,24 @@ bool nsDoubleBufferCARenderer::HasCALaye
   return !!mCALayer;
 }
 
 void nsDoubleBufferCARenderer::SetCALayer(void *aCALayer) {
   mCALayer = aCALayer;
 }
 
 bool nsDoubleBufferCARenderer::InitFrontSurface(size_t aWidth, size_t aHeight,
+                                                double aContentsScaleFactor,
                                                 AllowOfflineRendererEnum aAllowOfflineRenderer) {
   if (!mCALayer) {
     return false;
   }
 
-  mFrontSurface = MacIOSurface::CreateIOSurface(aWidth, aHeight);
+  mContentsScaleFactor = aContentsScaleFactor;
+  mFrontSurface = MacIOSurface::CreateIOSurface(aWidth, aHeight, mContentsScaleFactor);
   if (!mFrontSurface) {
     mCARenderer = nullptr;
     return false;
   }
 
   if (!mCARenderer) {
     mCARenderer = new nsCARenderer();
     if (!mCARenderer) {
@@ -323,16 +330,17 @@ bool nsDoubleBufferCARenderer::InitFront
       return false;
     }
 
     mCARenderer->AttachIOSurface(mFrontSurface);
 
     nsresult result = mCARenderer->SetupRenderer(mCALayer,
                         mFrontSurface->GetWidth(),
                         mFrontSurface->GetHeight(),
+                        mContentsScaleFactor,
                         aAllowOfflineRenderer);
 
     if (result != NS_OK) {
       mCARenderer = nullptr;
       mFrontSurface = nullptr;
       return false;
     }
   } else {
@@ -342,17 +350,18 @@ bool nsDoubleBufferCARenderer::InitFront
   return true;
 }
 
 void nsDoubleBufferCARenderer::Render() {
   if (!HasFrontSurface() || !mCARenderer) {
     return;
   }
 
-  mCARenderer->Render(GetFrontSurfaceWidth(), GetFrontSurfaceHeight(), nullptr);
+  mCARenderer->Render(GetFrontSurfaceWidth(), GetFrontSurfaceHeight(),
+                      mContentsScaleFactor, nullptr);
 }
 
 void nsDoubleBufferCARenderer::SwapSurfaces() {
   RefPtr<MacIOSurface> prevFrontSurface = mFrontSurface;
   mFrontSurface = mBackSurface;
   mBackSurface = prevFrontSurface;
 
   if (mFrontSurface) {
--- a/dom/plugins/test/mochitest/cocoa_focus.html
+++ b/dom/plugins/test/mochitest/cocoa_focus.html
@@ -20,20 +20,20 @@
                                         getInterface(Components.interfaces.nsIDOMWindowUtils);
 
       var plugin1 = document.getElementById("plugin1"); // What we're testing.
       var plugin2 = document.getElementById("plugin2"); // Dummy.
 
       var plugin1Bounds = plugin1.getBoundingClientRect();
       var plugin2Bounds = plugin2.getBoundingClientRect();
 
-      var plugin1X = (window.mozInnerScreenX + plugin1Bounds.left + 10) * utils.screenPixelsPerCSSPixel;
-      var plugin1Y = (window.mozInnerScreenY + plugin1Bounds.top + 10) * utils.screenPixelsPerCSSPixel;
-      var plugin2X = (window.mozInnerScreenX + plugin2Bounds.left + 10) * utils.screenPixelsPerCSSPixel;
-      var plugin2Y = (window.mozInnerScreenY + plugin2Bounds.top + 10) * utils.screenPixelsPerCSSPixel;
+      var plugin1X = (window.mozInnerScreenX + plugin1Bounds.left + 10);
+      var plugin1Y = (window.mozInnerScreenY + plugin1Bounds.top + 10);
+      var plugin2X = (window.mozInnerScreenX + plugin2Bounds.left + 10);
+      var plugin2Y = (window.mozInnerScreenY + plugin2Bounds.top + 10);
 
       const NSLeftMouseDown          = 1,
             NSLeftMouseUp            = 2;
 
       if (plugin1.getEventModel() != 1) {
         window.opener.todo(false, "Skipping this test when not testing the Cocoa event model");
         window.opener.testsFinished();
         return;
--- a/gfx/2d/MacIOSurface.h
+++ b/gfx/2d/MacIOSurface.h
@@ -19,40 +19,51 @@ typedef _CGLContextObject* CGLContextObj
 typedef struct CGContext* CGContextRef;
 typedef struct CGImage* CGImageRef;
 typedef uint32_t IOSurfaceID;
 
 class MacIOSurface : public mozilla::RefCounted<MacIOSurface> {
 public:
   typedef mozilla::gfx::SourceSurface SourceSurface;
 
-  static mozilla::TemporaryRef<MacIOSurface> CreateIOSurface(int aWidth, int aHeight);
+  static mozilla::TemporaryRef<MacIOSurface> CreateIOSurface(int aWidth, int aHeight,
+                                                             double aContentsScaleFactor = 1.0);
   static void ReleaseIOSurface(MacIOSurface *aIOSurface);
-  static mozilla::TemporaryRef<MacIOSurface> LookupSurface(IOSurfaceID aSurfaceID);
+  static mozilla::TemporaryRef<MacIOSurface> LookupSurface(IOSurfaceID aSurfaceID,
+                                                           double aContentsScaleFactor = 1.0);
 
-  MacIOSurface(const void *aIOSurfacePtr) : mIOSurfacePtr(aIOSurfacePtr) {}
+  MacIOSurface(const void *aIOSurfacePtr, double aContentsScaleFactor = 1.0)
+    : mIOSurfacePtr(aIOSurfacePtr), mContentsScaleFactor(aContentsScaleFactor) {}
   ~MacIOSurface();
   IOSurfaceID GetIOSurfaceID();
   void *GetBaseAddress();
+  // GetWidth() and GetHeight() return values in "display pixels".  A
+  // "display pixel" is the smallest fully addressable part of a display.
+  // But in HiDPI modes each "display pixel" corresponds to more than one
+  // device pixel.  Multiply display pixels by mContentsScaleFactor to
+  // get device pixels.
   size_t GetWidth();
   size_t GetHeight();
+  double GetContentsScaleFactor() { return mContentsScaleFactor; }
   size_t GetBytesPerRow();
   void Lock();
   void Unlock();
   // We would like to forward declare NSOpenGLContext, but it is an @interface
   // and this file is also used from c++, so we use a void *.
   CGLError CGLTexImageIOSurface2D(void *ctxt,
                                   GLenum internalFormat, GLenum format,
                                   GLenum type, GLuint plane);
   mozilla::TemporaryRef<SourceSurface> GetAsSurface();
   CGContextRef CreateIOSurfaceContext();
 
   // FIXME This doesn't really belong here
   static CGImageRef CreateImageFromIOSurfaceContext(CGContextRef aContext);
-  static mozilla::TemporaryRef<MacIOSurface> IOSurfaceContextGetSurface(CGContextRef aContext);
+  static mozilla::TemporaryRef<MacIOSurface> IOSurfaceContextGetSurface(CGContextRef aContext,
+                                                                        double aContentsScaleFactor = 1.0);
 
 private:
   friend class nsCARenderer;
   const void* mIOSurfacePtr;
+  double mContentsScaleFactor;
 };
 
 #endif
 #endif
--- a/gfx/2d/QuartzSupport.h
+++ b/gfx/2d/QuartzSupport.h
@@ -24,58 +24,75 @@ struct _CGLContextObject;
 enum AllowOfflineRendererEnum { ALLOW_OFFLINE_RENDERER, DISALLOW_OFFLINE_RENDERER };
 
 class nsCARenderer : public mozilla::RefCounted<nsCARenderer> {
 public:
   nsCARenderer() : mCARenderer(nullptr), mFBOTexture(0), mOpenGLContext(nullptr),
                    mCGImage(nullptr), mCGData(nullptr), mIOSurface(nullptr), mFBO(0),
                    mIOTexture(0),
                    mUnsupportedWidth(UINT32_MAX), mUnsupportedHeight(UINT32_MAX),
-                   mAllowOfflineRenderer(DISALLOW_OFFLINE_RENDERER) {}
+                   mAllowOfflineRenderer(DISALLOW_OFFLINE_RENDERER),
+                   mContentsScaleFactor(1.0) {}
   ~nsCARenderer();
+  // aWidth and aHeight are in "display pixels".  A "display pixel" is the
+  // smallest fully addressable part of a display.  But in HiDPI modes each
+  // "display pixel" corresponds to more than one device pixel.  Multiply
+  // display pixels by aContentsScaleFactor to get device pixels.
   nsresult SetupRenderer(void* aCALayer, int aWidth, int aHeight,
+                         double aContentsScaleFactor,
                          AllowOfflineRendererEnum aAllowOfflineRenderer);
-  nsresult Render(int aWidth, int aHeight, CGImageRef *aOutCAImage);
+  // aWidth and aHeight are in "display pixels".  Multiply by
+  // aContentsScaleFactor to get device pixels.
+  nsresult Render(int aWidth, int aHeight,
+                  double aContentsScaleFactor,
+                  CGImageRef *aOutCAImage);
   bool isInit() { return mCARenderer != nullptr; }
   /*
    * Render the CALayer to an IOSurface. If no IOSurface
    * is attached then an internal pixel buffer will be
    * used.
    */
   void AttachIOSurface(mozilla::RefPtr<MacIOSurface> aSurface);
   IOSurfaceID GetIOSurfaceID();
+  // aX, aY, aWidth and aHeight are in "display pixels".  Multiply by
+  // surf->GetContentsScaleFactor() to get device pixels.
   static nsresult DrawSurfaceToCGContext(CGContextRef aContext,
                                          MacIOSurface *surf,
                                          CGColorSpaceRef aColorSpace,
                                          int aX, int aY,
                                          size_t aWidth, size_t aHeight);
 
   // Remove & Add the layer without destroying
   // the renderer for fast back buffer swapping.
   void DettachCALayer();
   void AttachCALayer(void *aCALayer);
 #ifdef DEBUG
   static void SaveToDisk(MacIOSurface *surf);
 #endif
 private:
+  // aWidth and aHeight are in "display pixels".  Multiply by
+  // mContentsScaleFactor to get device pixels.
   void SetBounds(int aWidth, int aHeight);
+  // aWidth and aHeight are in "display pixels".  Multiply by
+  // mContentsScaleFactor to get device pixels.
   void SetViewport(int aWidth, int aHeight);
   void Destroy();
 
   void *mCARenderer;
   GLuint                    mFBOTexture;
   _CGLContextObject        *mOpenGLContext;
   CGImageRef                mCGImage;
   void                     *mCGData;
   mozilla::RefPtr<MacIOSurface> mIOSurface;
   uint32_t                  mFBO;
   uint32_t                  mIOTexture;
   uint32_t                  mUnsupportedWidth;
   uint32_t                  mUnsupportedHeight;
   AllowOfflineRendererEnum  mAllowOfflineRenderer;
+  double                    mContentsScaleFactor;
 };
 
 enum CGContextType {
   CG_CONTEXT_TYPE_UNKNOWN = 0,
   // These are found by inspection, it's possible they could be changed
   CG_CONTEXT_TYPE_BITMAP = 4,
   CG_CONTEXT_TYPE_IOSURFACE = 8
 };
--- a/gfx/2d/QuartzSupport.mm
+++ b/gfx/2d/QuartzSupport.mm
@@ -13,16 +13,21 @@
 
 #define IOSURFACE_FRAMEWORK_PATH \
   "/System/Library/Frameworks/IOSurface.framework/IOSurface"
 #define OPENGL_FRAMEWORK_PATH \
   "/System/Library/Frameworks/OpenGL.framework/OpenGL"
 #define COREGRAPHICS_FRAMEWORK_PATH \
   "/System/Library/Frameworks/ApplicationServices.framework/Frameworks/CoreGraphics.framework/CoreGraphics"
 
+@interface CALayer (ContentsScale)
+- (double)contentsScale;
+- (void)setContentsScale:(double)scale;
+@end
+
 using mozilla::RefPtr;
 using mozilla::TemporaryRef;
 
 // IOSurface signatures
 typedef CFTypeRef IOSurfacePtr;
 typedef IOSurfacePtr (*IOSurfaceCreateFunc) (CFDictionaryRef properties);
 typedef IOSurfacePtr (*IOSurfaceLookupFunc) (uint32_t io_surface_id);
 typedef IOSurfaceID (*IOSurfaceGetIDFunc) (CFTypeRef io_surface);
@@ -297,28 +302,32 @@ void MacIOSurfaceLib::CloseLibrary() {
   sIOSurfaceFramework = nullptr;
   sOpenGLFramework = nullptr;
 }
 
 MacIOSurface::~MacIOSurface() {
   CFRelease(mIOSurfacePtr);
 }
 
-TemporaryRef<MacIOSurface> MacIOSurface::CreateIOSurface(int aWidth, int aHeight) {
-  if (!MacIOSurfaceLib::isInit())
+TemporaryRef<MacIOSurface> MacIOSurface::CreateIOSurface(int aWidth, int aHeight,
+                                                         double aContentsScaleFactor) {
+  if (!MacIOSurfaceLib::isInit() || aContentsScaleFactor <= 0)
     return nullptr;
 
   CFMutableDictionaryRef props = ::CFDictionaryCreateMutable(
                       kCFAllocatorDefault, 4,
                       &kCFTypeDictionaryKeyCallBacks,
                       &kCFTypeDictionaryValueCallBacks);
   if (!props)
     return nullptr;
 
   int32_t bytesPerElem = 4;
+  size_t intScaleFactor = ceil(aContentsScaleFactor);
+  aWidth *= intScaleFactor;
+  aHeight *= intScaleFactor;
   CFNumberRef cfWidth = ::CFNumberCreate(nullptr, kCFNumberSInt32Type, &aWidth);
   CFNumberRef cfHeight = ::CFNumberCreate(nullptr, kCFNumberSInt32Type, &aHeight);
   CFNumberRef cfBytesPerElem = ::CFNumberCreate(nullptr, kCFNumberSInt32Type, &bytesPerElem);
   ::CFDictionaryAddValue(props, MacIOSurfaceLib::kPropWidth,
                                 cfWidth);
   ::CFRelease(cfWidth);
   ::CFDictionaryAddValue(props, MacIOSurfaceLib::kPropHeight,
                                 cfHeight);
@@ -330,55 +339,58 @@ TemporaryRef<MacIOSurface> MacIOSurface:
                                 kCFBooleanTrue);
 
   IOSurfacePtr surfaceRef = MacIOSurfaceLib::IOSurfaceCreate(props);
   ::CFRelease(props);
 
   if (!surfaceRef)
     return nullptr;
 
-  RefPtr<MacIOSurface> ioSurface = new MacIOSurface(surfaceRef);
+  RefPtr<MacIOSurface> ioSurface = new MacIOSurface(surfaceRef, aContentsScaleFactor);
   if (!ioSurface) {
     ::CFRelease(surfaceRef);
     return nullptr;
   }
 
   return ioSurface.forget();
 }
 
-TemporaryRef<MacIOSurface> MacIOSurface::LookupSurface(IOSurfaceID aIOSurfaceID) { 
-  if (!MacIOSurfaceLib::isInit())
+TemporaryRef<MacIOSurface> MacIOSurface::LookupSurface(IOSurfaceID aIOSurfaceID,
+                                                       double aContentsScaleFactor) { 
+  if (!MacIOSurfaceLib::isInit() || aContentsScaleFactor <= 0)
     return nullptr;
 
   IOSurfacePtr surfaceRef = MacIOSurfaceLib::IOSurfaceLookup(aIOSurfaceID);
   if (!surfaceRef)
     return nullptr;
 
-  RefPtr<MacIOSurface> ioSurface = new MacIOSurface(surfaceRef);
+  RefPtr<MacIOSurface> ioSurface = new MacIOSurface(surfaceRef, aContentsScaleFactor);
   if (!ioSurface) {
     ::CFRelease(surfaceRef);
     return nullptr;
   }
   return ioSurface.forget();
 }
 
 IOSurfaceID MacIOSurface::GetIOSurfaceID() { 
   return MacIOSurfaceLib::IOSurfaceGetID(mIOSurfacePtr);
 }
 
 void* MacIOSurface::GetBaseAddress() { 
   return MacIOSurfaceLib::IOSurfaceGetBaseAddress(mIOSurfacePtr);
 }
 
-size_t MacIOSurface::GetWidth() { 
-  return MacIOSurfaceLib::IOSurfaceGetWidth(mIOSurfacePtr);
+size_t MacIOSurface::GetWidth() {
+  size_t intScaleFactor = ceil(mContentsScaleFactor);
+  return MacIOSurfaceLib::IOSurfaceGetWidth(mIOSurfacePtr) / intScaleFactor;
 }
 
-size_t MacIOSurface::GetHeight() { 
-  return MacIOSurfaceLib::IOSurfaceGetHeight(mIOSurfacePtr);
+size_t MacIOSurface::GetHeight() {
+  size_t intScaleFactor = ceil(mContentsScaleFactor);
+  return MacIOSurfaceLib::IOSurfaceGetHeight(mIOSurfacePtr) / intScaleFactor;
 }
 
 size_t MacIOSurface::GetBytesPerRow() { 
   return MacIOSurfaceLib::IOSurfaceGetBytesPerRow(mIOSurfacePtr);
 }
 
 #define READ_ONLY 0x1
 void MacIOSurface::Lock() {
@@ -393,18 +405,19 @@ void MacIOSurface::Unlock() {
 using mozilla::gfx::SourceSurface;
 using mozilla::gfx::SourceSurfaceRawData;
 using mozilla::gfx::IntSize;
 
 TemporaryRef<SourceSurface>
 MacIOSurface::GetAsSurface() {
   Lock();
   size_t bytesPerRow = GetBytesPerRow();
-  size_t ioWidth = GetWidth();
-  size_t ioHeight = GetHeight();
+  size_t intScaleFactor = ceil(mContentsScaleFactor);
+  size_t ioWidth = GetWidth() * intScaleFactor;
+  size_t ioHeight = GetHeight() * intScaleFactor;
 
   unsigned char* ioData = (unsigned char*)GetBaseAddress();
   unsigned char* dataCpy = (unsigned char*)malloc(bytesPerRow*ioHeight);
   for (size_t i = 0; i < ioHeight; i++) {
     memcpy(dataCpy + i * bytesPerRow,
            ioData + i * bytesPerRow, ioWidth * 4);
   }
 
@@ -417,20 +430,22 @@ MacIOSurface::GetAsSurface() {
 }
 
 CGLError 
 MacIOSurface::CGLTexImageIOSurface2D(void *c,
                                     GLenum internalFormat, GLenum format, 
                                     GLenum type, GLuint plane)
 {
   NSOpenGLContext *ctxt = static_cast<NSOpenGLContext*>(c);
+  size_t intScaleFactor = ceil(mContentsScaleFactor);
   return MacIOSurfaceLib::CGLTexImageIOSurface2D((CGLContextObj)[ctxt CGLContextObj],
                                                 GL_TEXTURE_RECTANGLE_ARB,
                                                 internalFormat,
-                                                GetWidth(), GetHeight(),
+                                                GetWidth() * intScaleFactor,
+                                                GetHeight() * intScaleFactor,
                                                 format, type,
                                                 mIOSurfacePtr, plane);
 }
 
 CGColorSpaceRef CreateSystemColorSpace() {
     CMProfileRef system_profile = nullptr;
     CGColorSpaceRef cspace = nullptr;
 
@@ -442,17 +457,20 @@ CGColorSpaceRef CreateSystemColorSpace()
       // Default to generic
       cspace = ::CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
     }
 
     return cspace;
 }
 
 CGContextRef MacIOSurface::CreateIOSurfaceContext() {
-  CGContextRef ref = MacIOSurfaceLib::IOSurfaceContextCreate(mIOSurfacePtr, GetWidth(), GetHeight(),
+  size_t intScaleFactor = ceil(mContentsScaleFactor);
+  CGContextRef ref = MacIOSurfaceLib::IOSurfaceContextCreate(mIOSurfacePtr,
+                                                GetWidth() * intScaleFactor,
+                                                GetHeight() * intScaleFactor,
                                                 8, 32, CreateSystemColorSpace(), 0x2002);
   return ref;
 }
 
 nsCARenderer::~nsCARenderer() {
   Destroy();
 }
 
@@ -503,20 +521,21 @@ void nsCARenderer::Destroy() {
   mOpenGLContext = nullptr;
   mCGImage = nullptr;
   mIOSurface = nullptr;
   mFBO = 0;
   mIOTexture = 0;
 }
 
 nsresult nsCARenderer::SetupRenderer(void *aCALayer, int aWidth, int aHeight,
+                                     double aContentsScaleFactor,
                                      AllowOfflineRendererEnum aAllowOfflineRenderer) {
   mAllowOfflineRenderer = aAllowOfflineRenderer;
 
-  if (aWidth == 0 || aHeight == 0)
+  if (aWidth == 0 || aHeight == 0 || aContentsScaleFactor <= 0)
     return NS_ERROR_FAILURE;
 
   if (aWidth == mUnsupportedWidth &&
       aHeight == mUnsupportedHeight) {
     return NS_ERROR_FAILURE;
   }
 
   CALayer* layer = (CALayer*)aCALayer;
@@ -556,44 +575,48 @@ nsresult nsCARenderer::SetupRenderer(voi
   if (caRenderer == nil) {
     mUnsupportedWidth = aWidth;
     mUnsupportedHeight = aHeight;
     Destroy();
     return NS_ERROR_FAILURE;
   }
 
   caRenderer.layer = layer;
+  mContentsScaleFactor = aContentsScaleFactor;
+  size_t intScaleFactor = ceil(mContentsScaleFactor);
   SetBounds(aWidth, aHeight);
 
   // We target rendering to a CGImage if no shared IOSurface are given.
   if (!mIOSurface) {
-    mCGData = malloc(aWidth*aHeight*4);
+    mCGData = malloc(aWidth*intScaleFactor*aHeight*4*intScaleFactor);
     if (!mCGData) {
       mUnsupportedWidth = aWidth;
       mUnsupportedHeight = aHeight;
       Destroy();
       return NS_ERROR_FAILURE;
     }
-    memset(mCGData, 0, aWidth*aHeight*4);
+    memset(mCGData, 0, aWidth*intScaleFactor*aHeight*4*intScaleFactor);
 
     CGDataProviderRef dataProvider = nullptr;
     dataProvider = ::CGDataProviderCreateWithData(mCGData,
-                                        mCGData, aHeight*aWidth*4,
+                                        mCGData, aHeight*intScaleFactor*aWidth*4*intScaleFactor,
                                         cgdata_release_callback);
     if (!dataProvider) {
-      cgdata_release_callback(mCGData, mCGData, aHeight*aWidth*4);
+      cgdata_release_callback(mCGData, mCGData,
+                              aHeight*intScaleFactor*aWidth*4*intScaleFactor);
       mUnsupportedWidth = aWidth;
       mUnsupportedHeight = aHeight;
       Destroy();
       return NS_ERROR_FAILURE;
     }
 
     CGColorSpaceRef colorSpace = CreateSystemColorSpace();
 
-    mCGImage = ::CGImageCreate(aWidth, aHeight, 8, 32, aWidth * 4, colorSpace,
+    mCGImage = ::CGImageCreate(aWidth * intScaleFactor, aHeight * intScaleFactor,
+                8, 32, aWidth * intScaleFactor * 4, colorSpace,
                 kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host,
                 dataProvider, nullptr, true, kCGRenderingIntentDefault);
 
     ::CGDataProviderRelease(dataProvider);
     if (colorSpace) {
       ::CGColorSpaceRelease(colorSpace);
     }
     if (!mCGImage) {
@@ -609,17 +632,18 @@ nsresult nsCARenderer::SetupRenderer(voi
 
   if (mIOSurface) {
     // Create the IOSurface mapped texture.
     ::glGenTextures(1, &mIOTexture);
     ::glBindTexture(GL_TEXTURE_RECTANGLE_ARB, mIOTexture);
     ::glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
     ::glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
     MacIOSurfaceLib::CGLTexImageIOSurface2D(mOpenGLContext, GL_TEXTURE_RECTANGLE_ARB,
-                                           GL_RGBA, aWidth, aHeight,
+                                           GL_RGBA, aWidth * intScaleFactor,
+                                           aHeight * intScaleFactor,
                                            GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
                                            mIOSurface->mIOSurfacePtr, 0);
     ::glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
   } else {
     ::glGenTextures(1, &mFBOTexture);
     ::glBindTexture(GL_TEXTURE_RECTANGLE_ARB, mFBOTexture);
     ::glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
     ::glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
@@ -682,26 +706,58 @@ void nsCARenderer::SetBounds(int aWidth,
                                    [NSNull null], @"sublayers",
                                    [NSNull null], @"contents",
                                    [NSNull null], @"position",
                                    [NSNull null], @"bounds",
                                    nil];
   layer.actions = newActions;
   [newActions release];
 
+  // If we're in HiDPI mode, mContentsScaleFactor will (presumably) be 2.0.
+  // For some reason, to make things work properly in HiDPI mode we need to
+  // make caRenderer's 'bounds' and 'layer' different sizes -- to set 'bounds'
+  // to the size of 'layer's backing store.  To make plugins display at HiDPI
+  // resolution we also need to set 'layer's contentScale to
+  // mContentsScaleFactor.
+  size_t intScaleFactor = ceil(mContentsScaleFactor);
   [CATransaction setValue: [NSNumber numberWithFloat:0.0f] forKey: kCATransactionAnimationDuration];
   [CATransaction setValue: (id) kCFBooleanTrue forKey: kCATransactionDisableActions];
   [layer setBounds:CGRectMake(0, 0, aWidth, aHeight)];
   [layer setPosition:CGPointMake(aWidth/2.0, aHeight/2.0)];
-  caRenderer.bounds = CGRectMake(0, 0, aWidth, aHeight);
+  caRenderer.bounds = CGRectMake(0, 0, aWidth * intScaleFactor, aHeight * intScaleFactor);
+  if (mContentsScaleFactor != 1.0) {
+    CGAffineTransform affineTransform = [layer affineTransform];
+    affineTransform.a = mContentsScaleFactor;
+    affineTransform.d = mContentsScaleFactor;
+    affineTransform.tx = ((double)aWidth)/mContentsScaleFactor;
+    affineTransform.ty = ((double)aHeight)/mContentsScaleFactor;
+    [layer setAffineTransform:affineTransform];
+    if ([layer respondsToSelector:@selector(setContentsScale:)]) {
+      // For reasons that aren't clear (perhaps one or more OS bugs), OOP
+      // Core Graphics plugins (ones that use CGBridgeLayer) can only use
+      // HiDPI mode if the tree is built with the 10.7 SDK or up.
+#if !defined(MAC_OS_X_VERSION_10_7) || \
+    MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
+      Class CGBridgeLayerClass = ::NSClassFromString(@"CGBridgeLayer");
+      if (!CGBridgeLayerClass || ![layer isKindOfClass:CGBridgeLayerClass])
+#endif
+      {
+        layer.contentsScale = mContentsScaleFactor;
+      }
+    }
+  }
   [CATransaction commit];
 
 }
 
 void nsCARenderer::SetViewport(int aWidth, int aHeight) {
+  size_t intScaleFactor = ceil(mContentsScaleFactor);
+  aWidth *= intScaleFactor;
+  aHeight *= intScaleFactor;
+
   ::glViewport(0.0, 0.0, aWidth, aHeight);
   ::glMatrixMode(GL_PROJECTION);
   ::glLoadIdentity();
   ::glOrtho (0.0, aWidth, 0.0, aHeight, -1, 1);
 
   // Render upside down to speed up CGContextDrawImage
   ::glTranslatef(0.0f, aHeight, 0.0);
   ::glScalef(1.0, -1.0, 1.0);
@@ -716,24 +772,26 @@ void nsCARenderer::AttachIOSurface(RefPt
     return;
   }
 
   mIOSurface = aSurface;
 
   // Update the framebuffer and viewport
   if (mCARenderer) {
     CARenderer* caRenderer = (CARenderer*)mCARenderer;
-    int width = caRenderer.bounds.size.width;
-    int height = caRenderer.bounds.size.height;
+    size_t intScaleFactor = ceil(mContentsScaleFactor);
+    int width = caRenderer.bounds.size.width / intScaleFactor;
+    int height = caRenderer.bounds.size.height / intScaleFactor;
 
     CGLContextObj oldContext = ::CGLGetCurrentContext();
     ::CGLSetCurrentContext(mOpenGLContext);
     ::glBindTexture(GL_TEXTURE_RECTANGLE_ARB, mIOTexture);
     MacIOSurfaceLib::CGLTexImageIOSurface2D(mOpenGLContext, GL_TEXTURE_RECTANGLE_ARB,
-                                           GL_RGBA, mIOSurface->GetWidth(), mIOSurface->GetHeight(),
+                                           GL_RGBA, mIOSurface->GetWidth() * intScaleFactor,
+                                           mIOSurface->GetHeight() * intScaleFactor,
                                            GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
                                            mIOSurface->mIOSurfacePtr, 0);
     ::glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
 
     // Rebind the FBO to make it live
     ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, mFBO);
 
     if (mIOSurface->GetWidth() != width || mIOSurface->GetHeight() != height) {
@@ -753,42 +811,44 @@ IOSurfaceID nsCARenderer::GetIOSurfaceID
   if (!mIOSurface) {
     return 0;
   }
 
   return mIOSurface->GetIOSurfaceID();
 }
 
 nsresult nsCARenderer::Render(int aWidth, int aHeight, 
+                              double aContentsScaleFactor,
                               CGImageRef *aOutCGImage) {
   if (!aOutCGImage && !mIOSurface) {
     NS_ERROR("No target destination for rendering");
   } else if (aOutCGImage) {
     // We are expected to return a CGImageRef, we will set
     // it to nullptr in case we fail before the image is ready.
     *aOutCGImage = nullptr;
   }
 
-  if (aWidth == 0 || aHeight == 0)
+  if (aWidth == 0 || aHeight == 0 || aContentsScaleFactor <= 0)
     return NS_OK;
 
   if (!mCARenderer) {
     return NS_ERROR_FAILURE;
   }
 
   CARenderer* caRenderer = (CARenderer*)mCARenderer;
-  int renderer_width = caRenderer.bounds.size.width;
-  int renderer_height = caRenderer.bounds.size.height;
+  size_t intScaleFactor = ceil(aContentsScaleFactor);
+  int renderer_width = caRenderer.bounds.size.width / intScaleFactor;
+  int renderer_height = caRenderer.bounds.size.height / intScaleFactor;
 
   if (renderer_width != aWidth || renderer_height != aHeight) {
     // XXX: This should be optimized to not rescale the buffer
     //      if we are resizing down.
     CALayer* caLayer = [caRenderer layer];
     Destroy();
-    if (SetupRenderer(caLayer, aWidth, aHeight,
+    if (SetupRenderer(caLayer, aWidth, aHeight, aContentsScaleFactor,
                       mAllowOfflineRenderer) != NS_OK) {
       return NS_ERROR_FAILURE;
     }
 
     caRenderer = (CARenderer*)mCARenderer;
   }
 
   CGLContextObj oldContext = ::CGLGetCurrentContext();
@@ -809,30 +869,32 @@ nsresult nsCARenderer::Render(int aWidth
   }
 
   ::glClearColor(0.0, 0.0, 0.0, 0.0);
   ::glClear(GL_COLOR_BUFFER_BIT);
 
   [CATransaction commit];
   double caTime = ::CACurrentMediaTime();
   [caRenderer beginFrameAtTime:caTime timeStamp:nullptr];
-  [caRenderer addUpdateRect:CGRectMake(0,0, aWidth, aHeight)];
+  [caRenderer addUpdateRect:CGRectMake(0,0, aWidth * intScaleFactor,
+                                       aHeight * intScaleFactor)];
   [caRenderer render];
   [caRenderer endFrame];
 
   // Read the data back either to the IOSurface or mCGImage.
   if (mIOSurface) {
     ::glFlush();
   } else {
     ::glPixelStorei(GL_PACK_ALIGNMENT, 4);
     ::glPixelStorei(GL_PACK_ROW_LENGTH, 0);
     ::glPixelStorei(GL_PACK_SKIP_ROWS, 0);
     ::glPixelStorei(GL_PACK_SKIP_PIXELS, 0);
 
-    ::glReadPixels(0.0f, 0.0f, aWidth, aHeight,
+    ::glReadPixels(0.0f, 0.0f, aWidth * intScaleFactor,
+                        aHeight * intScaleFactor,
                         GL_BGRA, GL_UNSIGNED_BYTE,
                         mCGData);
 
     *aOutCGImage = mCGImage;
   }
 
   if (oldContext) {
     ::CGLSetCurrentContext(oldContext);
@@ -860,44 +922,52 @@ nsresult nsCARenderer::DrawSurfaceToCGCo
 
   if (aX < 0 || aX >= ioWidth ||
       aY < 0 || aY >= ioHeight) {
     surf->Unlock();
     return NS_ERROR_FAILURE;
   }
 
   void* ioData = surf->GetBaseAddress();
+  double scaleFactor = surf->GetContentsScaleFactor();
+  size_t intScaleFactor = ceil(surf->GetContentsScaleFactor());
   CGDataProviderRef dataProvider = ::CGDataProviderCreateWithData(ioData,
-                                      ioData, ioHeight*(bytesPerRow)*4, 
+                                      ioData, ioHeight*intScaleFactor*(bytesPerRow)*4, 
                                       nullptr); //No release callback 
   if (!dataProvider) {
     surf->Unlock();
     return NS_ERROR_FAILURE;
   }
 
-  CGImageRef cgImage = ::CGImageCreate(ioWidth, ioHeight, 8, 32, bytesPerRow,
-              aColorSpace, kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host,
+  CGImageRef cgImage = ::CGImageCreate(ioWidth * intScaleFactor,
+              ioHeight * intScaleFactor, 8, 32, bytesPerRow, aColorSpace,
+              kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host,
               dataProvider, nullptr, true, kCGRenderingIntentDefault);
   ::CGDataProviderRelease(dataProvider);
   if (!cgImage) {
     surf->Unlock();
     return NS_ERROR_FAILURE;
   }
   CGImageRef subImage = ::CGImageCreateWithImageInRect(cgImage,
-                                       ::CGRectMake(aX, aY, aWidth, aHeight));
+                                       ::CGRectMake(aX * scaleFactor,
+                                                    aY * scaleFactor,
+                                                    aWidth * scaleFactor,
+                                                    aHeight * scaleFactor));
   if (!subImage) {
     ::CGImageRelease(cgImage);
     surf->Unlock();
     return NS_ERROR_FAILURE;
   }
 
   ::CGContextScaleCTM(aContext, 1.0f, -1.0f);
   ::CGContextDrawImage(aContext, 
-                       CGRectMake(aX, -(CGFloat)aY - (CGFloat)aHeight, 
-                                  aWidth, aHeight), 
+                       CGRectMake(aX * scaleFactor,
+                                  (-(CGFloat)aY - (CGFloat)aHeight) * scaleFactor, 
+                                  aWidth * scaleFactor,
+                                  aHeight * scaleFactor), 
                        subImage);
 
   ::CGImageRelease(subImage);
   ::CGImageRelease(cgImage);
   surf->Unlock();
   return NS_OK;
 }
 
@@ -974,28 +1044,29 @@ void nsCARenderer::SaveToDisk(MacIOSurfa
 
 CGImageRef MacIOSurface::CreateImageFromIOSurfaceContext(CGContextRef aContext) {
   if (!MacIOSurfaceLib::isInit())
     return nullptr;
 
   return MacIOSurfaceLib::IOSurfaceContextCreateImage(aContext);
 }
 
-TemporaryRef<MacIOSurface> MacIOSurface::IOSurfaceContextGetSurface(CGContextRef aContext) {
-  if (!MacIOSurfaceLib::isInit())
+TemporaryRef<MacIOSurface> MacIOSurface::IOSurfaceContextGetSurface(CGContextRef aContext,
+                                                                    double aContentsScaleFactor) {
+  if (!MacIOSurfaceLib::isInit() || aContentsScaleFactor <= 0)
     return nullptr;
 
   IOSurfacePtr surfaceRef = MacIOSurfaceLib::IOSurfaceContextGetSurface(aContext);
   if (!surfaceRef)
     return nullptr;
 
   // Retain the IOSurface because MacIOSurface will release it
   CFRetain(surfaceRef);
 
-  RefPtr<MacIOSurface> ioSurface = new MacIOSurface(surfaceRef);
+  RefPtr<MacIOSurface> ioSurface = new MacIOSurface(surfaceRef, aContentsScaleFactor);
   if (!ioSurface) {
     ::CFRelease(surfaceRef);
     return nullptr;
   }
   return ioSurface.forget();
 }
 
 
--- a/layout/generic/nsObjectFrame.cpp
+++ b/layout/generic/nsObjectFrame.cpp
@@ -644,21 +644,26 @@ nsObjectFrame::FixupWindow(const nsSize&
     return;
   }
 #endif
 
   bool windowless = (window->type == NPWindowTypeDrawable);
 
   nsIntPoint origin = GetWindowOriginInPixels(windowless);
 
-  window->x = origin.x;
-  window->y = origin.y;
-
-  window->width = presContext->AppUnitsToDevPixels(aSize.width);
-  window->height = presContext->AppUnitsToDevPixels(aSize.height);
+  // window must be in "display pixels"
+  double scaleFactor = 1.0;
+  if (NS_FAILED(mInstanceOwner->GetContentsScaleFactor(&scaleFactor))) {
+    scaleFactor = 1.0;
+  }
+  int intScaleFactor = ceil(scaleFactor);
+  window->x = origin.x / intScaleFactor;
+  window->y = origin.y / intScaleFactor;
+  window->width = presContext->AppUnitsToDevPixels(aSize.width) / intScaleFactor;
+  window->height = presContext->AppUnitsToDevPixels(aSize.height) / intScaleFactor;
 
   // on the Mac we need to set the clipRect to { 0, 0, 0, 0 } for now. This will keep
   // us from drawing on screen until the widget is properly positioned, which will not
   // happen until we have finished the reflow process.
 #ifdef XP_MACOSX
   window->clipRect.top = 0;
   window->clipRect.left = 0;
   window->clipRect.bottom = 0;
@@ -704,20 +709,27 @@ nsObjectFrame::CallSetWindow(bool aCheck
   nsPresContext* presContext = PresContext();
   nsRootPresContext* rootPC = presContext->GetRootPresContext();
   if (!rootPC)
     return NS_ERROR_FAILURE;
   int32_t appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
   nsIFrame* rootFrame = rootPC->PresShell()->FrameManager()->GetRootFrame();
   nsRect bounds = GetContentRectRelativeToSelf() + GetOffsetToCrossDoc(rootFrame);
   nsIntRect intBounds = bounds.ToNearestPixels(appUnitsPerDevPixel);
-  window->x = intBounds.x;
-  window->y = intBounds.y;
-  window->width = intBounds.width;
-  window->height = intBounds.height;
+
+  // window must be in "display pixels"
+  double scaleFactor = 1.0;
+  if (NS_FAILED(mInstanceOwner->GetContentsScaleFactor(&scaleFactor))) {
+    scaleFactor = 1.0;
+  }
+  size_t intScaleFactor = ceil(scaleFactor);
+  window->x = intBounds.x / intScaleFactor;
+  window->y = intBounds.y / intScaleFactor;
+  window->width = intBounds.width / intScaleFactor;
+  window->height = intBounds.height / intScaleFactor;
 
   // Calling SetWindow might destroy this frame. We need to use the instance
   // owner to clean up so hold a ref.
   nsRefPtr<nsPluginInstanceOwner> instanceOwnerRef(mInstanceOwner);
 
   // This will call pi->SetWindow and take care of window subclassing
   // if needed, see bug 132759. Calling SetWindow can destroy this frame
   // so check for that before doing anything else with this frame's memory.
@@ -1626,17 +1638,23 @@ nsObjectFrame::BuildLayer(nsDisplayListB
   // Create image
   nsRefPtr<ImageContainer> container = mInstanceOwner->GetImageContainer();
 
   if (!container) {
     // This can occur if our instance is gone.
     return nullptr;
   }
 
-  gfxIntSize size(window->width, window->height);
+  // window is in "display pixels", but size needs to be in device pixels
+  double scaleFactor = 1.0;
+  if (NS_FAILED(mInstanceOwner->GetContentsScaleFactor(&scaleFactor))) {
+    scaleFactor = 1.0;
+  }
+  int intScaleFactor = ceil(scaleFactor);
+  gfxIntSize size(window->width * intScaleFactor, window->height * intScaleFactor);
 
   nsRect area = GetContentRectRelativeToSelf() + aItem->ToReferenceFrame();
   gfxRect r = nsLayoutUtils::RectToGfxRect(area, PresContext()->AppUnitsPerDevPixel());
   // to provide crisper and faster drawing.
   r.Round();
   nsRefPtr<Layer> layer =
     (aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, aItem));
 
--- a/layout/generic/test/plugin_focus_helper.html
+++ b/layout/generic/test/plugin_focus_helper.html
@@ -28,18 +28,18 @@ var nativeMouseUp;
 SimpleTest.waitForExplicitFinish();
 
 function activatePlugin(id) {
   return function() {
     netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
 
     var element = document.getElementById(id);
     var bounds = element.getBoundingClientRect();
-    var x = (bounds.left + window.mozInnerScreenX + 10)*gWindowUtils.screenPixelsPerCSSPixel;
-    var y = (bounds.top + window.mozInnerScreenY + 10)*gWindowUtils.screenPixelsPerCSSPixel;
+    var x = (bounds.left + window.mozInnerScreenX + 10);
+    var y = (bounds.top + window.mozInnerScreenY + 10);
 
     gWindowUtils.sendNativeMouseEvent(x, y, nativeMouseDown, 0, element);
     gWindowUtils.sendNativeMouseEvent(x, y, nativeMouseUp, 0, element);
   };
 }
 
 function done() {
   SimpleTest.finish();
--- a/widget/cocoa/nsScreenCocoa.h
+++ b/widget/cocoa/nsScreenCocoa.h
@@ -15,16 +15,17 @@ class nsScreenCocoa : public nsBaseScree
 public:
     nsScreenCocoa (NSScreen *screen);
     ~nsScreenCocoa ();
 
     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 GetPixelDepth(int32_t* aPixelDepth);
     NS_IMETHOD GetColorDepth(int32_t* aColorDepth);
+    NS_IMETHOD GetContentsScaleFactor(double* aContentsScaleFactor);
 
     NSScreen *CocoaScreen() { return mScreen; }
 
 private:
     CGFloat BackingScaleFactor();
 
     NSScreen *mScreen;
 };
--- a/widget/cocoa/nsScreenCocoa.mm
+++ b/widget/cocoa/nsScreenCocoa.mm
@@ -78,13 +78,24 @@ nsScreenCocoa::GetColorDepth(int32_t *aC
   int bpp = NSBitsPerPixelFromDepth (depth);
 
   *aColorDepth = bpp;
   return NS_OK;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
 }
 
+NS_IMETHODIMP
+nsScreenCocoa::GetContentsScaleFactor(double *aContentsScaleFactor)
+{
+  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+  *aContentsScaleFactor = (double) BackingScaleFactor();
+  return NS_OK;
+
+  NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
 CGFloat
 nsScreenCocoa::BackingScaleFactor()
 {
   return nsCocoaUtils::GetBackingScaleFactor(mScreen);
 }
--- a/widget/nsIScreen.idl
+++ b/widget/nsIScreen.idl
@@ -1,17 +1,17 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  *
  * 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 "nsISupports.idl"
 
-[scriptable, uuid(d961f76e-8437-4bc6-9ada-a1c98ace9560)]
+[scriptable, uuid(D6F13AF4-8ACA-4A10-8687-3F99C3692AC0)]
 interface nsIScreen : nsISupports
 {
   /**
    * Levels of brightness for the screen, from off to full brightness.
    */
   const unsigned long BRIGHTNESS_DIM = 0;
   const unsigned long BRIGHTNESS_FULL = 1;
 
@@ -52,9 +52,15 @@ interface nsIScreen : nsISupports
   
   readonly attribute long pixelDepth;
   readonly attribute long colorDepth;
   /**
    * Get/set the screen rotation, on platforms that support changing
    * screen rotation.
    */
   attribute unsigned long rotation;
+
+  /**
+   * The number of device pixels per screen point in HiDPI mode.
+   * Returns 1.0 if HiDPI mode is disabled or unsupported.
+   */
+  readonly attribute double contentsScaleFactor;
 };
--- a/widget/xpwidgets/nsBaseScreen.cpp
+++ b/widget/xpwidgets/nsBaseScreen.cpp
@@ -54,8 +54,15 @@ nsBaseScreen::CheckMinimumBrightness()
 {
   uint32_t brightness = nsIScreen::BRIGHTNESS_LEVELS;
   for (uint32_t i = 0; i < nsIScreen::BRIGHTNESS_LEVELS; i++)
     if (mBrightnessLocks[i] > 0)
       brightness = i;
 
   ApplyMinimumBrightness(brightness);
 }
+
+NS_IMETHODIMP
+nsBaseScreen::GetContentsScaleFactor(double* aContentsScaleFactor)
+{
+  *aContentsScaleFactor = 1.0;
+  return NS_OK;
+}
--- a/widget/xpwidgets/nsBaseScreen.h
+++ b/widget/xpwidgets/nsBaseScreen.h
@@ -29,16 +29,18 @@ public:
   NS_IMETHOD UnlockMinimumBrightness(uint32_t aBrightness);
 
   NS_IMETHOD GetRotation(uint32_t* aRotation) {
     *aRotation = nsIScreen::ROTATION_0_DEG;
     return NS_OK;
   }
   NS_IMETHOD SetRotation(uint32_t aRotation) { return NS_ERROR_NOT_AVAILABLE; }
 
+  NS_IMETHOD GetContentsScaleFactor(double* aContentsScaleFactor);
+
 protected:
   /**
    * Manually set the current level of brightness locking. This is called after
    * we determine, based on the current active locks, what the strongest
    * lock is. You should normally not call this function - it will be
    * called automatically by this class.
    *
    * Each widget implementation should implement this in a way that