Bug 599938. Show select drop downs in the correct location if they are being translated by CSS transforms. r=roc
authorTimothy Nikkel <tnikkel@gmail.com>
Sun, 18 Sep 2011 13:16:47 -0500
changeset 78432 cd2472a58edac597e3b6f2b0488e60e853eeb251
parent 78430 fbc0c5c938a97abf3d6745a00a842ef036fc1bfc
child 78433 3610977e5f7e34593f78c8cceba56e491a07bf3e
push id78
push userclegnitto@mozilla.com
push dateFri, 16 Dec 2011 17:32:24 +0000
treeherdermozilla-release@79d24e644fdd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs599938
milestone9.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 599938. Show select drop downs in the correct location if they are being translated by CSS transforms. r=roc
layout/forms/nsComboboxControlFrame.cpp
layout/forms/nsComboboxControlFrame.h
--- a/layout/forms/nsComboboxControlFrame.cpp
+++ b/layout/forms/nsComboboxControlFrame.cpp
@@ -539,54 +539,96 @@ nsComboboxControlFrame::ReflowDropdown(n
                             ignoredStatus);
  
    // Set the child's width and height to it's desired size
   FinishReflowChild(mDropdownFrame, aPresContext, &kidReflowState,
                     desiredSize, rect.x, rect.y, flags);
   return rv;
 }
 
+nsPoint
+nsComboboxControlFrame::GetCSSTransformTranslate()
+{
+  nsIFrame* frame = this;
+  PRBool is3DTransform = PR_FALSE;
+  gfxMatrix transform;
+  while (frame) {
+    nsIFrame* parent = nsnull;
+    gfx3DMatrix ctm = frame->GetTransformMatrix(&parent);
+    gfxMatrix matrix;
+    if (ctm.Is2D(&matrix)) {
+      transform = transform * matrix;
+    } else {
+      is3DTransform = PR_TRUE;
+      break;
+    }
+    frame = parent;
+  }
+  nsPoint translation;
+  if (!is3DTransform && !transform.HasNonTranslation()) {
+    nsPresContext* pc = PresContext();
+    gfxPoint pixelTranslation = transform.GetTranslation();
+    PRInt32 apd = pc->AppUnitsPerDevPixel();
+    translation.x = NSFloatPixelsToAppUnits(float(pixelTranslation.x), apd);
+    translation.y = NSFloatPixelsToAppUnits(float(pixelTranslation.y), apd);
+    // To get the translation introduced only by transforms we subtract the
+    // regular non-transform translation.
+    nsRootPresContext* rootPC = pc->GetRootPresContext();
+    if (rootPC) {
+      translation -= GetOffsetToCrossDoc(rootPC->PresShell()->GetRootFrame());
+    } else {
+      translation.x = translation.y = 0;
+    }
+  }
+  return translation;
+}
+
 void
 nsComboboxControlFrame::AbsolutelyPositionDropDown()
 {
    // Position the dropdown list. It is positioned below the display frame if there is enough
    // room on the screen to display the entire list. Otherwise it is placed above the display
    // frame.
 
    // Note: As first glance, it appears that you could simply get the absolute bounding box for the
    // dropdown list by first getting its view, then getting the view's nsIWidget, then asking the nsIWidget
    // for it's AbsoluteBounds. The problem with this approach, is that the dropdown lists y location can
    // change based on whether the dropdown is placed below or above the display frame.
    // The approach, taken here is to get use the absolute position of the display frame and use it's location
    // to determine if the dropdown will go offscreen.
 
+  // Normal frame geometry (eg GetOffsetTo, mRect) doesn't include transforms.
+  // In the special case that our transform is only a 2D translation we
+  // introduce this hack so that the dropdown will show up in the right place.
+  nsPoint translation = GetCSSTransformTranslation();
+
    // Use the height calculated for the area frame so it includes both
    // the display and button heights.
   nscoord dropdownYOffset = GetRect().height;
   nsSize dropdownSize = mDropdownFrame->GetSize();
 
   nsRect screen = nsFormControlFrame::GetUsableScreenRect(PresContext());
 
   // Check to see if the drop-down list will go offscreen
-  if (GetScreenRectInAppUnits().YMost() + dropdownSize.height > screen.YMost()) {
+  if ((GetScreenRectInAppUnits() + translation).YMost() + dropdownSize.height > screen.YMost()) {
     // move the dropdown list up
     dropdownYOffset = - (dropdownSize.height);
   }
 
   nsPoint dropdownPosition;
   const nsStyleVisibility* vis = GetStyleVisibility();
   if (vis->mDirection == NS_STYLE_DIRECTION_RTL) {
     // Align the right edge of the drop-down with the right edge of the control.
     dropdownPosition.x = GetRect().width - dropdownSize.width;
   } else {
     dropdownPosition.x = 0;
   }
   dropdownPosition.y = dropdownYOffset; 
 
-  mDropdownFrame->SetPosition(dropdownPosition);
+  mDropdownFrame->SetPosition(dropdownPosition + translation);
 }
 
 //----------------------------------------------------------
 // 
 //----------------------------------------------------------
 #ifdef DO_REFLOW_DEBUG
 static int myCounter = 0;
 
--- a/layout/forms/nsComboboxControlFrame.h
+++ b/layout/forms/nsComboboxControlFrame.h
@@ -249,16 +249,22 @@ protected:
    */
   PRBool ShowList(PRBool aShowList);
   void CheckFireOnChange();
   void FireValueChangeEvent();
   nsresult RedisplayText(PRInt32 aIndex);
   void HandleRedisplayTextEvent();
   void ActuallyDisplayText(PRBool aNotify);
 
+private:
+  // If our total transform to the root frame of the root document is only a 2d
+  // translation then return that translation, otherwise returns (0,0).
+  nsPoint GetCSSTransformTranslation();
+
+protected:
   nsFrameList              mPopupFrames;             // additional named child list
   nsCOMPtr<nsIContent>     mDisplayContent;          // Anonymous content used to display the current selection
   nsCOMPtr<nsIContent>     mButtonContent;           // Anonymous content for the button
   nsIFrame*                mDisplayFrame;            // frame to display selection
   nsIFrame*                mButtonFrame;             // button frame
   nsIFrame*                mDropdownFrame;           // dropdown list frame
   nsIListControlFrame *    mListControlFrame;        // ListControl Interface for the dropdown frame