Bug 1522848 - Add Lock Aspect Ratio feature. r=mconley,jmathies.
authormeandave <djustice@mozilla.com>
Mon, 18 Mar 2019 20:07:31 +0000
changeset 464900 946839452979779d172ac4e85d2c65730d553da7
parent 464899 dd235c6dd881e45afb01a1320d4c7567a7dd1067
child 464901 9e58ba9d9dd73bd3ae04d7d0ad6bca4471014e8c
push id35727
push userdvarga@mozilla.com
push dateTue, 19 Mar 2019 09:48:59 +0000
treeherdermozilla-central@70baa37ae1eb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmconley, jmathies
bugs1522848
milestone68.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 1522848 - Add Lock Aspect Ratio feature. r=mconley,jmathies. *** Bug 1522848 - Smoothing out Lock Aspect Ratio feature r?mconley. Differential Revision: https://phabricator.services.mozilla.com/D20713
toolkit/components/pictureinpicture/PictureInPicture.jsm
toolkit/components/windowwatcher/nsWindowWatcher.cpp
toolkit/content/license.html
widget/nsIWidget.h
widget/windows/nsWindow.cpp
widget/windows/nsWindow.h
xpfe/appshell/nsIXULWindow.idl
xpfe/appshell/nsXULWindow.cpp
--- a/toolkit/components/pictureinpicture/PictureInPicture.jsm
+++ b/toolkit/components/pictureinpicture/PictureInPicture.jsm
@@ -4,17 +4,17 @@
 
 "use strict";
 
 var EXPORTED_SYMBOLS = ["PictureInPicture"];
 
 const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 const PLAYER_URI = "chrome://global/content/pictureinpicture/player.xhtml";
-const PLAYER_FEATURES = `chrome,titlebar=no,alwaysontop,resizable`;
+const PLAYER_FEATURES = `chrome,titlebar=no,alwaysontop,lockaspectratio,resizable`;
 const WINDOW_TYPE = "Toolkit:PictureInPicture";
 
 /**
  * This module is responsible for creating a Picture in Picture window to host
  * a clone of a video element running in web content.
  */
 
 var PictureInPicture = {
--- a/toolkit/components/windowwatcher/nsWindowWatcher.cpp
+++ b/toolkit/components/windowwatcher/nsWindowWatcher.cpp
@@ -304,32 +304,34 @@ struct SizeSpec {
         mInnerWidth(0),
         mInnerHeight(0),
         mLeftSpecified(false),
         mTopSpecified(false),
         mOuterWidthSpecified(false),
         mOuterHeightSpecified(false),
         mInnerWidthSpecified(false),
         mInnerHeightSpecified(false),
+        mLockAspectRatio(false),
         mUseDefaultWidth(false),
         mUseDefaultHeight(false) {}
 
   int32_t mLeft;
   int32_t mTop;
   int32_t mOuterWidth;   // Total window width
   int32_t mOuterHeight;  // Total window height
   int32_t mInnerWidth;   // Content area width
   int32_t mInnerHeight;  // Content area height
 
   bool mLeftSpecified;
   bool mTopSpecified;
   bool mOuterWidthSpecified;
   bool mOuterHeightSpecified;
   bool mInnerWidthSpecified;
   bool mInnerHeightSpecified;
+  bool mLockAspectRatio;
 
   // If these booleans are true, don't look at the corresponding width values
   // even if they're specified -- they'll be bogus
   bool mUseDefaultWidth;
   bool mUseDefaultHeight;
 
   bool PositionSpecified() const { return mLeftSpecified || mTopSpecified; }
 
@@ -2077,16 +2079,20 @@ void nsWindowWatcher::CalcSizeSpec(const
                   WinHasOption(aFeatures, "innerHeight", INT32_MIN, nullptr))) {
     if (temp == INT32_MIN) {
       aResult.mUseDefaultHeight = true;
     } else {
       aResult.mInnerHeight = temp;
     }
     aResult.mInnerHeightSpecified = true;
   }
+
+  if (WinHasOption(aFeatures, "lockaspectratio", 0, nullptr)) {
+    aResult.mLockAspectRatio = true;
+  }
 }
 
 /* Size and position a new window according to aSizeSpec. This method
    is assumed to be called after the window has already been given
    a default position and size; thus its current position and size are
    accurate defaults. The new window is made visible at method end.
    @param aTreeOwner
           The top-level nsIDocShellTreeOwner of the newly opened window.
@@ -2352,16 +2358,24 @@ void nsWindowWatcher::SizeOpenedWindow(n
         width += chromeWidth;
       }
       if (!sizeChromeHeight) {
         height += chromeHeight;
       }
       treeOwnerAsWin->SetSize(width * scale, height * scale, false);
     }
   }
+
+  if (aIsCallerChrome) {
+    nsCOMPtr<nsIXULWindow> xulWin = do_GetInterface(treeOwnerAsWin);
+    if (xulWin && aSizeSpec.mLockAspectRatio) {
+      xulWin->LockAspectRatio(true);
+    }
+  }
+
   treeOwnerAsWin->SetVisibility(true);
 }
 
 void nsWindowWatcher::GetWindowTreeItem(mozIDOMWindowProxy* aWindow,
                                         nsIDocShellTreeItem** aResult) {
   *aResult = 0;
 
   if (aWindow) {
--- a/toolkit/content/license.html
+++ b/toolkit/content/license.html
@@ -3020,16 +3020,17 @@ WITH THE USE OR PERFORMANCE OF THIS SOFT
     <ul>
         <li><code>browser/extensions/formautofill/content/heuristicsRegexp.js</code></li>
         <li><code>browser/extensions/formautofill/FormAutofillHeuristics.jsm</code></li>
         <li><code>browser/extensions/formautofill/FormAutofillNameUtils.jsm</code></li>
         <li><code>editor/libeditor/EditorEventListener.cpp</code></li>
         <li><code>mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/StrictModeContext.java</code></li>
         <li><code>security/sandbox/</code></li>
         <li><code>widget/cocoa/GfxInfo.mm</code></li>
+        <li><code>widget/windows/nsWindow.cpp</code></li>
     </ul>
     <p>and also some files in these directories:</p>
     <ul>
         <li><code>dom/media/webspeech/recognition/</code></li>
         <li><code>dom/plugins/</code></li>
         <li><code>gfx/ots/</code></li>
         <li><code>gfx/ycbcr/</code></li>
         <li><code>ipc/chromium/</code></li>
--- a/widget/nsIWidget.h
+++ b/widget/nsIWidget.h
@@ -730,16 +730,24 @@ class nsIWidget : public nsISupports {
    * @param aWidth  the new width expressed in the parent's coordinate system
    * @param aHeight the new height expressed in the parent's coordinate
    *                system
    * @param aRepaint whether the widget should be repainted
    */
   virtual void Resize(double aWidth, double aHeight, bool aRepaint) = 0;
 
   /**
+   * Lock the aspect ratio of a Window
+   *
+   * @param aShouldLock bool
+   *
+   */
+  virtual void LockAspectRatio(bool aShouldLock) {};
+
+  /**
    * Move or resize this widget. Any size constraints set for the window by
    * a previous call to SetSizeConstraints will be applied.
    *
    * @param aX       the new x position expressed in the parent's coordinate
    *                 system
    * @param aY       the new y position expressed in the parent's coordinate
    *                 system
    * @param aWidth   the new width expressed in the parent's coordinate system
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -618,16 +618,17 @@ nsWindow::nsWindow(bool aIsChildWindow)
 #ifdef MOZ_XUL
   mTransparencyMode = eTransparencyOpaque;
   memset(&mGlassMargins, 0, sizeof mGlassMargins);
 #endif
   DWORD background = ::GetSysColor(COLOR_BTNFACE);
   mBrush = ::CreateSolidBrush(NSRGB_2_COLOREF(background));
   mSendingSetText = false;
   mDefaultScale = -1.0;  // not yet set, will be calculated on first use
+  mAspectRatio = 0.0;    // not yet set, will be calculated on first use
 
   mTaskbarPreview = nullptr;
 
   mCompositorWidgetDelegate = nullptr;
 
   // Global initialization
   if (!sInstanceCount) {
     // Global app registration id for Win7 and up. See
@@ -1662,16 +1663,24 @@ void nsWindow::RegisterTouchWindow() {
 BOOL CALLBACK nsWindow::RegisterTouchForDescendants(HWND aWnd, LPARAM aMsg) {
   nsWindow* win = WinUtils::GetNSWindowPtr(aWnd);
   if (win) {
     ::RegisterTouchWindow(aWnd, TWF_WANTPALM);
   }
   return TRUE;
 }
 
+void nsWindow::LockAspectRatio(bool aShouldLock) {
+  if (aShouldLock) {
+    mAspectRatio = (float)mBounds.Height() / (float)mBounds.Width();
+  } else {
+    mAspectRatio = 0.0;
+  }
+}
+
 /**************************************************************
  *
  * SECTION: nsIWidget::Move, nsIWidget::Resize,
  * nsIWidget::Size, nsIWidget::BeginResizeDrag
  *
  * Repositioning and sizing a window.
  *
  **************************************************************/
@@ -5563,16 +5572,62 @@ bool nsWindow::ProcessMessage(UINT msg, 
           result = !Preferences::GetBool("mousebutton.5th.enabled", true);
           break;
         default:
           break;
       }
       break;
 
     case WM_SIZING: {
+      if (mAspectRatio > 0) {
+        LPRECT rect = (LPRECT)lParam;
+        int32_t newWidth, newHeight;
+
+        // The following conditions and switch statement borrow heavily from the
+        // Chromium source code from
+        // https://chromium.googlesource.com/chromium/src/+/456d6e533cfb4531995e0ef52c279d4b5aa8a352/ui/views/window/window_resize_utils.cc#45
+        if (wParam == WMSZ_LEFT || wParam == WMSZ_RIGHT ||
+            wParam == WMSZ_TOPLEFT || wParam == WMSZ_BOTTOMLEFT) {
+          newWidth = rect->right - rect->left;
+          newHeight = newWidth * mAspectRatio;
+        } else {
+          newHeight = rect->bottom - rect->top;
+          newWidth = newHeight / mAspectRatio;
+        }
+
+        switch (wParam) {
+          case WMSZ_RIGHT:
+          case WMSZ_BOTTOM:
+            rect->right = newWidth + rect->left;
+            rect->bottom = rect->top + newHeight;
+            break;
+          case WMSZ_TOP:
+            rect->right = newWidth + rect->left;
+            rect->top = rect->bottom - newHeight;
+            break;
+          case WMSZ_LEFT:
+          case WMSZ_TOPLEFT:
+            rect->left = rect->right - newWidth;
+            rect->top = rect->bottom - newHeight;
+            break;
+          case WMSZ_TOPRIGHT:
+            rect->right = rect->left + newWidth;
+            rect->top = rect->bottom - newHeight;
+            break;
+          case WMSZ_BOTTOMLEFT:
+            rect->left = rect->right - newWidth;
+            rect->bottom = rect->top + newHeight;
+            break;
+          case WMSZ_BOTTOMRIGHT:
+            rect->right = rect->left + newWidth;
+            rect->bottom = rect->top + newHeight;
+            break;
+        }
+      }
+
       // When we get WM_ENTERSIZEMOVE we don't know yet if we're in a live
       // resize or move event. Instead we wait for first VM_SIZING message
       // within a ENTERSIZEMOVE to consider this a live resize event.
       if (mResizeState == IN_SIZEMOVE) {
         mResizeState = RESIZING;
         NotifyLiveResizeStarted();
       }
       break;
--- a/widget/windows/nsWindow.h
+++ b/widget/windows/nsWindow.h
@@ -123,16 +123,17 @@ class nsWindow final : public nsWindowBa
     }
   }
 
   virtual void Show(bool aState) override;
   virtual bool IsVisible() const override;
   virtual void ConstrainPosition(bool aAllowSlop, int32_t* aX,
                                  int32_t* aY) override;
   virtual void SetSizeConstraints(const SizeConstraints& aConstraints) override;
+  virtual void LockAspectRatio(bool aShouldLock) override;
   virtual const SizeConstraints GetSizeConstraints() override;
   virtual void Move(double aX, double aY) override;
   virtual void Resize(double aWidth, double aHeight, bool aRepaint) override;
   virtual void Resize(double aX, double aY, double aWidth, double aHeight,
                       bool aRepaint) override;
   virtual mozilla::Maybe<bool> IsResizingNativeWidget() override;
   virtual MOZ_MUST_USE nsresult BeginResizeDrag(mozilla::WidgetGUIEvent* aEvent,
                                                 int32_t aHorizontal,
@@ -599,16 +600,18 @@ class nsWindow final : public nsWindowBa
   // Cached copy of L&F's resize border
   int32_t mHorResizeMargin;
   int32_t mVertResizeMargin;
   // Height of the caption plus border
   int32_t mCaptionHeight;
 
   double mDefaultScale;
 
+  float mAspectRatio;
+
   nsCOMPtr<nsIIdleServiceInternal> mIdleService;
 
   // Draggable titlebar region maintained by UpdateWindowDraggingRegion
   LayoutDeviceIntRegion mDraggableRegion;
 
   // Hook Data Memebers for Dropdowns. sProcessHook Tells the
   // hook methods whether they should be processing the hook
   // messages.
--- a/xpfe/appshell/nsIXULWindow.idl
+++ b/xpfe/appshell/nsIXULWindow.idl
@@ -96,16 +96,22 @@ interface nsIXULWindow : nsISupports
   void center(in nsIXULWindow aRelative, in boolean aScreen, in boolean aAlert);
 
   /**
    * Shows the window as a modal window. That is, ensures that it is visible
    * and runs a local event loop, exiting only once the window has been closed.
    */
   void showModal();
 
+  /**
+   * Locks the aspect ratio for a window.
+   * @param aShouldLock boolean
+   */
+  void lockAspectRatio(in bool aShouldLock);
+
   const unsigned long lowestZ = 0;
   const unsigned long loweredZ = 4;  /* "alwaysLowered" attribute */
   const unsigned long normalZ = 5;
   const unsigned long raisedZ = 6;   /* "alwaysRaised" attribute */
   const unsigned long highestZ = 9;
 
   attribute unsigned long zLevel;
 
--- a/xpfe/appshell/nsXULWindow.cpp
+++ b/xpfe/appshell/nsXULWindow.cpp
@@ -2292,16 +2292,23 @@ nsXULWindow::BeforeStartLayout() {
   LoadPersistentWindowState();
   SyncAttributesToWidget();
   if (mWindow) {
     SizeShell();
   }
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsXULWindow::LockAspectRatio(bool aShouldLock)
+{
+  mWindow->LockAspectRatio(aShouldLock);
+  return NS_OK;
+}
+
 void nsXULWindow::LoadPersistentWindowState() {
   nsCOMPtr<dom::Element> docShellElement = GetWindowDOMElement();
   if (!docShellElement) {
     return;
   }
 
   // Check if the window wants to persist anything.
   nsAutoString persist;