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 75479 ab68ef485d775c88114cc1b4cccc33fd66798efb
parent 75478 4ab18c9e38b1a8cac77cb5db5fe8687f11094a1a
child 75480 b6b6c8e74766b7616d9f603a63528b7501b04f2c
push id2
push userbsmedberg@mozilla.com
push dateFri, 19 Aug 2011 14:38:13 +0000
reviewersEnn
bugs555482
milestone9.0a1
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" });