Bug 555482: Allow resetting resizers by double-clicking on the resizer. r=Enn
authorKyle Huey <khuey@kylehuey.com>
Thu, 18 Aug 2011 10:25:36 -0400
changeset 75477 ab68ef485d775c88114cc1b4cccc33fd66798efb
parent 75476 4ab18c9e38b1a8cac77cb5db5fe8687f11094a1a
child 75478 b6b6c8e74766b7616d9f603a63528b7501b04f2c
push id21028
push userkhuey@mozilla.com
push dateThu, 18 Aug 2011 20:03:24 +0000
treeherdermozilla-central@b6b6c8e74766 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersEnn
bugs555482
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 555482: Allow resetting resizers by double-clicking on the resizer. r=Enn
content/base/src/nsGkAtomList.h
layout/xul/base/src/nsResizerFrame.cpp
layout/xul/base/src/nsResizerFrame.h
layout/xul/base/test/window_resizer_element.xul
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -65,16 +65,17 @@ GK_ATOM(_empty, "")
 GK_ATOM(moz, "_moz")
 GK_ATOM(moztype, "_moz-type")
 GK_ATOM(mozdirty, "_moz_dirty")
 GK_ATOM(mozdonotsend, "moz-do-not-send")
 GK_ATOM(mozeditorbogusnode, "_moz_editor_bogus_node")
 GK_ATOM(mozgeneratedcontentbefore, "_moz_generated_content_before")
 GK_ATOM(mozgeneratedcontentafter, "_moz_generated_content_after")
 GK_ATOM(mozgeneratedcontentimage, "_moz_generated_content_image")
+GK_ATOM(_moz_original_size, "_moz_original_size")
 GK_ATOM(_moz_target, "_moz_target")
 GK_ATOM(_moz_type, "_moz-type")
 GK_ATOM(menuactive, "_moz-menuactive")
 GK_ATOM(_poundDefault, "#default")
 GK_ATOM(_asterix, "*")
 GK_ATOM(a, "a")
 GK_ATOM(abbr, "abbr")
 GK_ATOM(abort, "abort")
--- a/layout/xul/base/src/nsResizerFrame.cpp
+++ b/layout/xul/base/src/nsResizerFrame.cpp
@@ -56,16 +56,17 @@
 #include "nsIBaseWindow.h"
 #include "nsPIDOMWindow.h"
 #include "nsGUIEvent.h"
 #include "nsEventDispatcher.h"
 #include "nsContentUtils.h"
 #include "nsMenuPopupFrame.h"
 #include "nsIScreenManager.h"
 #include "mozilla/dom/Element.h"
+#include "nsContentErrors.h"
 
 
 //
 // NS_NewResizerFrame
 //
 // Creates a new Resizer frame and returns it
 //
 nsIFrame*
@@ -248,69 +249,41 @@ nsResizerFrame::HandleEvent(nsPresContex
         // the resizer invisible.
         nsRect appUnitsRect = rect.ToAppUnits(aPresContext->AppUnitsPerDevPixel());
         if (appUnitsRect.width < mRect.width && mouseMove.x)
           appUnitsRect.width = mRect.width;
         if (appUnitsRect.height < mRect.height && mouseMove.y)
           appUnitsRect.height = mRect.height;
         nsIntRect cssRect = appUnitsRect.ToInsidePixels(nsPresContext::AppUnitsPerCSSPixel());
 
-        nsAutoString widthstr, heightstr;
-        widthstr.AppendInt(cssRect.width);
-        heightstr.AppendInt(cssRect.height);
-
-        // for XUL elements, just set the width and height attributes. For
-        // other elements, set style.width and style.height
-        if (contentToResize->IsXUL()) {
-          nsIntRect oldRect;
-          nsWeakFrame weakFrame(menuPopupFrame);
-          if (menuPopupFrame) {
-            nsCOMPtr<nsIWidget> widget;
-            menuPopupFrame->GetWidget(getter_AddRefs(widget));
-            if (widget)
-              widget->GetScreenBounds(oldRect);
-
-            // convert the new rectangle into outer window coordinates
-            nsIntPoint clientOffset = widget->GetClientOffset();
-            rect.x -= clientOffset.x; 
-            rect.y -= clientOffset.y; 
-          }
+        nsIntRect oldRect;
+        nsWeakFrame weakFrame(menuPopupFrame);
+        if (menuPopupFrame) {
+          nsCOMPtr<nsIWidget> widget;
+          menuPopupFrame->GetWidget(getter_AddRefs(widget));
+          if (widget)
+            widget->GetScreenBounds(oldRect);
 
-          // only set the property if the element could have changed in that direction
-          if (direction.mHorizontal) {
-            contentToResize->SetAttr(kNameSpaceID_None, nsGkAtoms::width, widthstr, PR_TRUE);
-          }
-          if (direction.mVertical) {
-            contentToResize->SetAttr(kNameSpaceID_None, nsGkAtoms::height, heightstr, PR_TRUE);
-          }
-
-          if (weakFrame.IsAlive() &&
-              (oldRect.x != rect.x || oldRect.y != rect.y) &&
-              (!menuPopupFrame->IsAnchored() ||
-               menuPopupFrame->PopupLevel() != ePopupLevelParent)) {
-            menuPopupFrame->MoveTo(rect.x, rect.y, PR_TRUE);
-          }
+          // convert the new rectangle into outer window coordinates
+          nsIntPoint clientOffset = widget->GetClientOffset();
+          rect.x -= clientOffset.x; 
+          rect.y -= clientOffset.y; 
         }
-        else {
-          nsCOMPtr<nsIDOMElementCSSInlineStyle> inlineStyleContent =
-            do_QueryInterface(contentToResize);
-          if (inlineStyleContent) {
-            nsCOMPtr<nsIDOMCSSStyleDeclaration> decl;
-            inlineStyleContent->GetStyle(getter_AddRefs(decl));
 
-            // only set the property if the element could have changed in that direction
-            if (direction.mHorizontal) {
-              widthstr.AppendLiteral("px");
-              decl->SetProperty(NS_LITERAL_STRING("width"), widthstr, EmptyString());
-            }
-            if (direction.mVertical) {
-              heightstr.AppendLiteral("px");
-              decl->SetProperty(NS_LITERAL_STRING("height"), heightstr, EmptyString());
-            }
-          }
+        SizeInfo sizeInfo, originalSizeInfo;
+        sizeInfo.width.AppendInt(cssRect.width);
+        sizeInfo.height.AppendInt(cssRect.height);
+        ResizeContent(contentToResize, direction, sizeInfo, &originalSizeInfo);
+        MaybePersistOriginalSize(contentToResize, originalSizeInfo);
+
+        if (weakFrame.IsAlive() &&
+            (oldRect.x != rect.x || oldRect.y != rect.y) &&
+            (!menuPopupFrame->IsAnchored() ||
+             menuPopupFrame->PopupLevel() != ePopupLevelParent)) {
+          menuPopupFrame->MoveTo(rect.x, rect.y, PR_TRUE);
         }
       }
       else {
         window->SetPositionAndSize(rect.x, rect.y, rect.width, rect.height, PR_TRUE); // do the repaint.
       }
 
       doDefault = PR_FALSE;
     }
@@ -318,16 +291,35 @@ nsResizerFrame::HandleEvent(nsPresContex
   break;
 
   case NS_MOUSE_CLICK:
     if (NS_IS_MOUSE_LEFT_CLICK(aEvent))
     {
       MouseClicked(aPresContext, aEvent);
     }
     break;
+
+  case NS_MOUSE_DOUBLECLICK:
+    if (aEvent->eventStructType == NS_MOUSE_EVENT &&
+        static_cast<nsMouseEvent*>(aEvent)->button == nsMouseEvent::eLeftButton)
+    {
+      nsCOMPtr<nsIBaseWindow> window;
+      nsIPresShell* presShell = aPresContext->GetPresShell();
+      nsIContent* contentToResize =
+        GetContentToResize(presShell, getter_AddRefs(window));
+      if (contentToResize) {
+        nsIFrame* frameToResize = contentToResize->GetPrimaryFrame();
+        if (frameToResize && frameToResize->GetType() == nsGkAtoms::menuPopupFrame)
+          break; // Don't restore original sizing for menupopup frames until
+                 // we handle screen constraints here. (Bug 357725)
+
+        RestoreOriginalSize(contentToResize);
+      }
+    }
+    break;
   }
 
   if (!doDefault)
     *aEventStatus = nsEventStatus_eConsumeNoDefault;
 
   if (doDefault && weakFrame.IsAlive())
     return nsTitleBarFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
 
@@ -413,16 +405,112 @@ nsResizerFrame::AdjustDimensions(PRInt32
     case 1:
       *aSize+= aResizerDirection*aMovement;
       // use one as a minimum size or the element could disappear
       if (*aSize < 1)
         *aSize = 1;
   }
 }
 
+/* static */ void
+nsResizerFrame::ResizeContent(nsIContent* aContent, const Direction& aDirection,
+                              const SizeInfo& aSizeInfo, SizeInfo* aOriginalSizeInfo)
+{
+  // for XUL elements, just set the width and height attributes. For
+  // other elements, set style.width and style.height
+  if (aContent->IsXUL()) {
+    if (aOriginalSizeInfo) {
+      aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::width,
+                        aOriginalSizeInfo->width);
+      aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::height,
+                        aOriginalSizeInfo->height);
+    }
+    // only set the property if the element could have changed in that direction
+    if (aDirection.mHorizontal) {
+      aContent->SetAttr(kNameSpaceID_None, nsGkAtoms::width, aSizeInfo.width, PR_TRUE);
+    }
+    if (aDirection.mVertical) {
+      aContent->SetAttr(kNameSpaceID_None, nsGkAtoms::height, aSizeInfo.height, PR_TRUE);
+    }
+  }
+  else {
+    nsCOMPtr<nsIDOMElementCSSInlineStyle> inlineStyleContent =
+      do_QueryInterface(aContent);
+    if (inlineStyleContent) {
+      nsCOMPtr<nsIDOMCSSStyleDeclaration> decl;
+      inlineStyleContent->GetStyle(getter_AddRefs(decl));
+
+      if (aOriginalSizeInfo) {
+        decl->GetPropertyValue(NS_LITERAL_STRING("width"),
+                               aOriginalSizeInfo->width);
+        decl->GetPropertyValue(NS_LITERAL_STRING("height"),
+                               aOriginalSizeInfo->height);
+      }
+
+      // only set the property if the element could have changed in that direction
+      if (aDirection.mHorizontal) {
+        nsAutoString widthstr(aSizeInfo.width);
+        if (!widthstr.IsEmpty() &&
+            !Substring(widthstr, widthstr.Length() - 2, 2).EqualsLiteral("px"))
+          widthstr.AppendLiteral("px");
+        decl->SetProperty(NS_LITERAL_STRING("width"), widthstr, EmptyString());
+      }
+      if (aDirection.mVertical) {
+        nsAutoString heightstr(aSizeInfo.height);
+        if (!heightstr.IsEmpty() &&
+            !Substring(heightstr, heightstr.Length() - 2, 2).EqualsLiteral("px"))
+          heightstr.AppendLiteral("px");
+        decl->SetProperty(NS_LITERAL_STRING("height"), heightstr, EmptyString());
+      }
+    }
+  }
+}
+
+/* static */ void
+nsResizerFrame::SizeInfoDtorFunc(void *aObject, nsIAtom *aPropertyName,
+                                 void *aPropertyValue, void *aData)
+{
+  nsResizerFrame::SizeInfo *propertyValue =
+    static_cast<nsResizerFrame::SizeInfo*>(aPropertyValue);
+  delete propertyValue;
+}
+
+/* static */ void
+nsResizerFrame::MaybePersistOriginalSize(nsIContent* aContent,
+                                         const SizeInfo& aSizeInfo)
+{
+  nsresult rv;
+
+  aContent->GetProperty(nsGkAtoms::_moz_original_size, &rv);
+  if (rv != NS_PROPTABLE_PROP_NOT_THERE)
+    return;
+
+  nsAutoPtr<SizeInfo> sizeInfo(new SizeInfo(aSizeInfo));
+  rv = aContent->SetProperty(nsGkAtoms::_moz_original_size, sizeInfo.get(),
+                             &SizeInfoDtorFunc);
+  if (NS_SUCCEEDED(rv))
+    sizeInfo.forget();
+}
+
+/* static */ void
+nsResizerFrame::RestoreOriginalSize(nsIContent* aContent)
+{
+  nsresult rv;
+  SizeInfo* sizeInfo =
+    static_cast<SizeInfo*>(aContent->GetProperty(nsGkAtoms::_moz_original_size,
+                           &rv));
+  if (NS_FAILED(rv))
+    return;
+
+  NS_ASSERTION(sizeInfo, "We set a null sizeInfo!?");
+  Direction direction = {1, 1};
+  ResizeContent(aContent, direction, *sizeInfo, nsnull);
+  aContent->DeleteProperty(nsGkAtoms::_moz_original_size);
+}
+
 /* returns a Direction struct containing the horizontal and vertical direction
  */
 nsResizerFrame::Direction
 nsResizerFrame::GetDirection()
 {
   static const nsIContent::AttrValuesArray strings[] =
     {&nsGkAtoms::topleft,    &nsGkAtoms::top,    &nsGkAtoms::topright,
      &nsGkAtoms::left,                           &nsGkAtoms::right,
--- a/layout/xul/base/src/nsResizerFrame.h
+++ b/layout/xul/base/src/nsResizerFrame.h
@@ -66,14 +66,23 @@ public:
 
 protected:
   nsIContent* GetContentToResize(nsIPresShell* aPresShell, nsIBaseWindow** aWindow);
 
   Direction GetDirection();
   static void AdjustDimensions(PRInt32* aPos, PRInt32* aSize,
                         PRInt32 aMovement, PRInt8 aResizerDirection);
 
+  struct SizeInfo {
+    nsString width, height;
+  };
+  static void SizeInfoDtorFunc(void *aObject, nsIAtom *aPropertyName,
+                               void *aPropertyValue, void *aData);
+  static void ResizeContent(nsIContent* aContent, const Direction& aDirection,
+                            const SizeInfo& aSizeInfo, SizeInfo* aOriginalSizeInfo);
+  static void MaybePersistOriginalSize(nsIContent* aContent, const SizeInfo& aSizeInfo);
+  static void RestoreOriginalSize(nsIContent* aContent);
 protected:
 	nsIntRect mMouseDownRect;
 	nsIntPoint mMouseDownPoint;
 }; // class nsResizerFrame
 
 #endif /* nsResizerFrame_h___ */
--- a/layout/xul/base/test/window_resizer_element.xul
+++ b/layout/xul/base/test/window_resizer_element.xul
@@ -36,20 +36,27 @@ function testResizer(resizerid, noShrink
       synthesizeMouse(document.documentElement, originalX + 5 + mouseX * scale,
                       originalY + 5 + mouseY * scale, { type:"mousemove" });
 
       var newrect = document.getElementById(resizerid + "-container").getBoundingClientRect();
       is(Math.round(newrect.width), Math.round(expectedWidth), "resize element " + resizerid +
          " " + testid + " width moving " + mouseX + "," + mouseY + ",,," + hResize);
       is(Math.round(newrect.height), Math.round(expectedHeight), "resize element " + resizerid +
          " " + testid + " height moving " + mouseX + "," + mouseY);
-      // move it back before we release!
-      synthesizeMouse(document.documentElement, originalX + 5, originalY + 5, { type:"mousemove" });
-      synthesizeMouse(document.documentElement, originalX + 5, originalY + 5, { type:"mouseup" });
-    }
+      // release
+      synthesizeMouse(document.documentElement, originalX + 5 + mouseX * scale,
+                      originalY + 5 + mouseY * scale, { type:"mouseup" });    }
+      // return to the original size
+      synthesizeMouse(document.documentElement, originalX + 5 + mouseX * scale,
+                      originalY + 5 + mouseY * scale, { type:"dblclick" });    }
+      var newrect = document.getElementById(resizerid + "-container").getBoundingClientRect();
+      is(Math.round(newrect.width), Math.round(rect.width), "resize element " + resizerid +
+         " " + testid + " doubleclicking to restore original size");
+      is(Math.round(newrect.height), Math.round(rect.height), "resize element " + resizerid +
+         " " + testid + " doubleclicking to restore original size");
   }
 }
 
 function doTest() {
   // first, check if a resizer with a element attribute set to an element that
   // does not exist does not cause a problem
   var resizer = document.getElementById("notfound");
   synthesizeMouse(resizer, 5, 5, { type:"mousedown" });