layout/xul/nsResizerFrame.cpp

Thu, 15 Jan 2015 21:03:48 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 21:03:48 +0100
branch
TOR_BUG_9701
changeset 11
deefc01c0e14
permissions
-rw-r--r--

Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "nsCOMPtr.h"
     7 #include "nsIServiceManager.h"
     8 #include "nsResizerFrame.h"
     9 #include "nsIContent.h"
    10 #include "nsIDocument.h"
    11 #include "nsIDOMNodeList.h"
    12 #include "nsGkAtoms.h"
    13 #include "nsNameSpaceManager.h"
    14 #include "nsIDOMElementCSSInlineStyle.h"
    15 #include "nsIDOMCSSStyleDeclaration.h"
    17 #include "nsPresContext.h"
    18 #include "nsFrameManager.h"
    19 #include "nsIDocShell.h"
    20 #include "nsIDocShellTreeOwner.h"
    21 #include "nsIBaseWindow.h"
    22 #include "nsPIDOMWindow.h"
    23 #include "mozilla/MouseEvents.h"
    24 #include "nsContentUtils.h"
    25 #include "nsMenuPopupFrame.h"
    26 #include "nsIScreenManager.h"
    27 #include "mozilla/dom/Element.h"
    28 #include "nsError.h"
    29 #include <algorithm>
    31 using namespace mozilla;
    33 //
    34 // NS_NewResizerFrame
    35 //
    36 // Creates a new Resizer frame and returns it
    37 //
    38 nsIFrame*
    39 NS_NewResizerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
    40 {
    41   return new (aPresShell) nsResizerFrame(aPresShell, aContext);
    42 }
    44 NS_IMPL_FRAMEARENA_HELPERS(nsResizerFrame)
    46 nsResizerFrame::nsResizerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
    47 :nsTitleBarFrame(aPresShell, aContext)
    48 {
    49 }
    51 nsresult
    52 nsResizerFrame::HandleEvent(nsPresContext* aPresContext,
    53                             WidgetGUIEvent* aEvent,
    54                             nsEventStatus* aEventStatus)
    55 {
    56   NS_ENSURE_ARG_POINTER(aEventStatus);
    57   if (nsEventStatus_eConsumeNoDefault == *aEventStatus) {
    58     return NS_OK;
    59   }
    61   nsWeakFrame weakFrame(this);
    62   bool doDefault = true;
    64   switch (aEvent->message) {
    65     case NS_TOUCH_START:
    66     case NS_MOUSE_BUTTON_DOWN: {
    67       if (aEvent->eventStructType == NS_TOUCH_EVENT ||
    68           (aEvent->eventStructType == NS_MOUSE_EVENT &&
    69            aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton)) {
    70         nsCOMPtr<nsIBaseWindow> window;
    71         nsIPresShell* presShell = aPresContext->GetPresShell();
    72         nsIContent* contentToResize =
    73           GetContentToResize(presShell, getter_AddRefs(window));
    74         if (contentToResize) {
    75           nsIFrame* frameToResize = contentToResize->GetPrimaryFrame();
    76           if (!frameToResize)
    77             break;
    79           // cache the content rectangle for the frame to resize
    80           // GetScreenRectInAppUnits returns the border box rectangle, so
    81           // adjust to get the desired content rectangle.
    82           nsRect rect = frameToResize->GetScreenRectInAppUnits();
    83           switch (frameToResize->StylePosition()->mBoxSizing) {
    84             case NS_STYLE_BOX_SIZING_CONTENT:
    85               rect.Deflate(frameToResize->GetUsedPadding());
    86             case NS_STYLE_BOX_SIZING_PADDING:
    87               rect.Deflate(frameToResize->GetUsedBorder());
    88             default:
    89               break;
    90           }
    92           mMouseDownRect = rect.ToNearestPixels(aPresContext->AppUnitsPerDevPixel());
    93           doDefault = false;
    94         }
    95         else {
    96           // If there is no window, then resizing isn't allowed.
    97           if (!window)
    98             break;
   100           doDefault = false;
   102           // ask the widget implementation to begin a resize drag if it can
   103           Direction direction = GetDirection();
   104           nsresult rv = aEvent->widget->BeginResizeDrag(aEvent,
   105                         direction.mHorizontal, direction.mVertical);
   106           // for native drags, don't set the fields below
   107           if (rv != NS_ERROR_NOT_IMPLEMENTED)
   108              break;
   110           // if there's no native resize support, we need to do window
   111           // resizing ourselves
   112           window->GetPositionAndSize(&mMouseDownRect.x, &mMouseDownRect.y,
   113                                      &mMouseDownRect.width, &mMouseDownRect.height);
   114         }
   116         // remember current mouse coordinates
   117         nsIntPoint refPoint;
   118         if (!GetEventPoint(aEvent, refPoint))
   119           return NS_OK;
   120         mMouseDownPoint = refPoint + aEvent->widget->WidgetToScreenOffset();
   122         // we're tracking
   123         mTrackingMouseMove = true;
   125         nsIPresShell::SetCapturingContent(GetContent(), CAPTURE_IGNOREALLOWED);
   126       }
   127     }
   128     break;
   130   case NS_TOUCH_END:
   131   case NS_MOUSE_BUTTON_UP: {
   132     if (aEvent->eventStructType == NS_TOUCH_EVENT ||
   133         (aEvent->eventStructType == NS_MOUSE_EVENT &&
   134          aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton)) {
   135       // we're done tracking.
   136       mTrackingMouseMove = false;
   138       nsIPresShell::SetCapturingContent(nullptr, 0);
   140       doDefault = false;
   141     }
   142   }
   143   break;
   145   case NS_TOUCH_MOVE:
   146   case NS_MOUSE_MOVE: {
   147     if (mTrackingMouseMove)
   148     {
   149       nsCOMPtr<nsIBaseWindow> window;
   150       nsIPresShell* presShell = aPresContext->GetPresShell();
   151       nsCOMPtr<nsIContent> contentToResize =
   152         GetContentToResize(presShell, getter_AddRefs(window));
   154       // check if the returned content really is a menupopup
   155       nsMenuPopupFrame* menuPopupFrame = nullptr;
   156       if (contentToResize) {
   157         menuPopupFrame = do_QueryFrame(contentToResize->GetPrimaryFrame());
   158       }
   160       // both MouseMove and direction are negative when pointing to the
   161       // top and left, and positive when pointing to the bottom and right
   163       // retrieve the offset of the mousemove event relative to the mousedown.
   164       // The difference is how much the resize needs to be
   165       nsIntPoint refPoint;
   166       if (!GetEventPoint(aEvent, refPoint))
   167         return NS_OK;
   168       nsIntPoint screenPoint(refPoint + aEvent->widget->WidgetToScreenOffset());
   169       nsIntPoint mouseMove(screenPoint - mMouseDownPoint);
   171       // Determine which direction to resize by checking the dir attribute.
   172       // For windows and menus, ensure that it can be resized in that direction.
   173       Direction direction = GetDirection();
   174       if (window || menuPopupFrame) {
   175         if (menuPopupFrame) {
   176           menuPopupFrame->CanAdjustEdges(
   177             (direction.mHorizontal == -1) ? NS_SIDE_LEFT : NS_SIDE_RIGHT,
   178             (direction.mVertical == -1) ? NS_SIDE_TOP : NS_SIDE_BOTTOM, mouseMove);
   179         }
   180       }
   181       else if (!contentToResize) {
   182         break; // don't do anything if there's nothing to resize
   183       }
   185       nsIntRect rect = mMouseDownRect;
   187       // Check if there are any size constraints on this window.
   188       widget::SizeConstraints sizeConstraints;
   189       if (window) {
   190         nsCOMPtr<nsIWidget> widget;
   191         window->GetMainWidget(getter_AddRefs(widget));
   192         sizeConstraints = widget->GetSizeConstraints();
   193       }
   195       AdjustDimensions(&rect.x, &rect.width, sizeConstraints.mMinSize.width,
   196                        sizeConstraints.mMaxSize.width, mouseMove.x, direction.mHorizontal);
   197       AdjustDimensions(&rect.y, &rect.height, sizeConstraints.mMinSize.height,
   198                        sizeConstraints.mMaxSize.height, mouseMove.y, direction.mVertical);
   200       // Don't allow resizing a window or a popup past the edge of the screen,
   201       // so adjust the rectangle to fit within the available screen area.
   202       if (window) {
   203         nsCOMPtr<nsIScreen> screen;
   204         nsCOMPtr<nsIScreenManager> sm(do_GetService("@mozilla.org/gfx/screenmanager;1"));
   205         if (sm) {
   206           nsIntRect frameRect = GetScreenRect();
   207           // ScreenForRect requires display pixels, so scale from device pix
   208           double scale;
   209           window->GetUnscaledDevicePixelsPerCSSPixel(&scale);
   210           sm->ScreenForRect(NSToIntRound(frameRect.x / scale),
   211                             NSToIntRound(frameRect.y / scale), 1, 1,
   212                             getter_AddRefs(screen));
   213           if (screen) {
   214             nsIntRect screenRect;
   215             screen->GetRect(&screenRect.x, &screenRect.y,
   216                             &screenRect.width, &screenRect.height);
   217             rect.IntersectRect(rect, screenRect);
   218           }
   219         }
   220       }
   221       else if (menuPopupFrame) {
   222         nsRect frameRect = menuPopupFrame->GetScreenRectInAppUnits();
   223         nsIFrame* rootFrame = aPresContext->PresShell()->FrameManager()->GetRootFrame();
   224         nsRect rootScreenRect = rootFrame->GetScreenRectInAppUnits();
   226         nsPopupLevel popupLevel = menuPopupFrame->PopupLevel();
   227         nsRect screenRect = menuPopupFrame->GetConstraintRect(frameRect, rootScreenRect, popupLevel);
   228         // round using ToInsidePixels as it's better to be a pixel too small
   229         // than be too large. If the popup is too large it could get flipped
   230         // to the opposite side of the anchor point while resizing.
   231         nsIntRect screenRectPixels = screenRect.ToInsidePixels(aPresContext->AppUnitsPerDevPixel());
   232         rect.IntersectRect(rect, screenRectPixels);
   233       }
   235       if (contentToResize) {
   236         // convert the rectangle into css pixels. When changing the size in a
   237         // direction, don't allow the new size to be less that the resizer's
   238         // size. This ensures that content isn't resized too small as to make
   239         // the resizer invisible.
   240         nsRect appUnitsRect = rect.ToAppUnits(aPresContext->AppUnitsPerDevPixel());
   241         if (appUnitsRect.width < mRect.width && mouseMove.x)
   242           appUnitsRect.width = mRect.width;
   243         if (appUnitsRect.height < mRect.height && mouseMove.y)
   244           appUnitsRect.height = mRect.height;
   245         nsIntRect cssRect = appUnitsRect.ToInsidePixels(nsPresContext::AppUnitsPerCSSPixel());
   247         nsIntRect oldRect;
   248         nsWeakFrame weakFrame(menuPopupFrame);
   249         if (menuPopupFrame) {
   250           nsCOMPtr<nsIWidget> widget = menuPopupFrame->GetWidget();
   251           if (widget)
   252             widget->GetScreenBounds(oldRect);
   254           // convert the new rectangle into outer window coordinates
   255           nsIntPoint clientOffset = widget->GetClientOffset();
   256           rect.x -= clientOffset.x;
   257           rect.y -= clientOffset.y;
   258         }
   260         SizeInfo sizeInfo, originalSizeInfo;
   261         sizeInfo.width.AppendInt(cssRect.width);
   262         sizeInfo.height.AppendInt(cssRect.height);
   263         ResizeContent(contentToResize, direction, sizeInfo, &originalSizeInfo);
   264         MaybePersistOriginalSize(contentToResize, originalSizeInfo);
   266         // Move the popup to the new location unless it is anchored, since
   267         // the position shouldn't change. nsMenuPopupFrame::SetPopupPosition
   268         // will instead ensure that the popup's position is anchored at the
   269         // right place.
   270         if (weakFrame.IsAlive() &&
   271             (oldRect.x != rect.x || oldRect.y != rect.y) &&
   272             (!menuPopupFrame->IsAnchored() ||
   273              menuPopupFrame->PopupLevel() != ePopupLevelParent)) {
   275           rect.x = aPresContext->DevPixelsToIntCSSPixels(rect.x);
   276           rect.y = aPresContext->DevPixelsToIntCSSPixels(rect.y);
   277           menuPopupFrame->MoveTo(rect.x, rect.y, true);
   278         }
   279       }
   280       else {
   281         window->SetPositionAndSize(rect.x, rect.y, rect.width, rect.height, true); // do the repaint.
   282       }
   284       doDefault = false;
   285     }
   286   }
   287   break;
   289   case NS_MOUSE_CLICK: {
   290     WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
   291     if (mouseEvent->IsLeftClickEvent()) {
   292       MouseClicked(aPresContext, mouseEvent);
   293     }
   294     break;
   295   }
   296   case NS_MOUSE_DOUBLECLICK:
   297     if (aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) {
   298       nsCOMPtr<nsIBaseWindow> window;
   299       nsIPresShell* presShell = aPresContext->GetPresShell();
   300       nsIContent* contentToResize =
   301         GetContentToResize(presShell, getter_AddRefs(window));
   302       if (contentToResize) {
   303         nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(contentToResize->GetPrimaryFrame());
   304         if (menuPopupFrame)
   305           break; // Don't restore original sizing for menupopup frames until
   306                  // we handle screen constraints here. (Bug 357725)
   308         RestoreOriginalSize(contentToResize);
   309       }
   310     }
   311     break;
   312   }
   314   if (!doDefault)
   315     *aEventStatus = nsEventStatus_eConsumeNoDefault;
   317   if (doDefault && weakFrame.IsAlive())
   318     return nsTitleBarFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
   320   return NS_OK;
   321 }
   323 nsIContent*
   324 nsResizerFrame::GetContentToResize(nsIPresShell* aPresShell, nsIBaseWindow** aWindow)
   325 {
   326   *aWindow = nullptr;
   328   nsAutoString elementid;
   329   mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::element, elementid);
   330   if (elementid.IsEmpty()) {
   331     // If the resizer is in a popup, resize the popup's widget, otherwise
   332     // resize the widget associated with the window.
   333     nsIFrame* popup = GetParent();
   334     while (popup) {
   335       nsMenuPopupFrame* popupFrame = do_QueryFrame(popup);
   336       if (popupFrame) {
   337         return popupFrame->GetContent();
   338       }
   339       popup = popup->GetParent();
   340     }
   342     // don't allow resizing windows in content shells
   343     nsCOMPtr<nsIDocShellTreeItem> dsti = aPresShell->GetPresContext()->GetDocShell();
   344     if (!dsti || dsti->ItemType() != nsIDocShellTreeItem::typeChrome) {
   345       // don't allow resizers in content shells, except for the viewport
   346       // scrollbar which doesn't have a parent
   347       nsIContent* nonNativeAnon = mContent->FindFirstNonChromeOnlyAccessContent();
   348       if (!nonNativeAnon || nonNativeAnon->GetParent()) {
   349         return nullptr;
   350       }
   351     }
   353     // get the document and the window - should this be cached?
   354     nsPIDOMWindow *domWindow = aPresShell->GetDocument()->GetWindow();
   355     if (domWindow) {
   356       nsCOMPtr<nsIDocShell> docShell = domWindow->GetDocShell();
   357       if (docShell) {
   358         nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
   359         docShell->GetTreeOwner(getter_AddRefs(treeOwner));
   360         if (treeOwner) {
   361           CallQueryInterface(treeOwner, aWindow);
   362         }
   363       }
   364     }
   366     return nullptr;
   367   }
   369   if (elementid.EqualsLiteral("_parent")) {
   370     // return the parent, but skip over native anonymous content
   371     nsIContent* parent = mContent->GetParent();
   372     return parent ? parent->FindFirstNonChromeOnlyAccessContent() : nullptr;
   373   }
   375   return aPresShell->GetDocument()->GetElementById(elementid);
   376 }
   378 void
   379 nsResizerFrame::AdjustDimensions(int32_t* aPos, int32_t* aSize,
   380                                  int32_t aMinSize, int32_t aMaxSize,
   381                                  int32_t aMovement, int8_t aResizerDirection)
   382 {
   383   int32_t oldSize = *aSize;
   385   *aSize += aResizerDirection * aMovement;
   386   // use one as a minimum size or the element could disappear
   387   if (*aSize < 1)
   388     *aSize = 1;
   390   // Constrain the size within the minimum and maximum size.
   391   *aSize = std::max(aMinSize, std::min(aMaxSize, *aSize));
   393   // For left and top resizers, the window must be moved left by the same
   394   // amount that the window was resized.
   395   if (aResizerDirection == -1)
   396     *aPos += oldSize - *aSize;
   397 }
   399 /* static */ void
   400 nsResizerFrame::ResizeContent(nsIContent* aContent, const Direction& aDirection,
   401                               const SizeInfo& aSizeInfo, SizeInfo* aOriginalSizeInfo)
   402 {
   403   // for XUL elements, just set the width and height attributes. For
   404   // other elements, set style.width and style.height
   405   if (aContent->IsXUL()) {
   406     if (aOriginalSizeInfo) {
   407       aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::width,
   408                         aOriginalSizeInfo->width);
   409       aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::height,
   410                         aOriginalSizeInfo->height);
   411     }
   412     // only set the property if the element could have changed in that direction
   413     if (aDirection.mHorizontal) {
   414       aContent->SetAttr(kNameSpaceID_None, nsGkAtoms::width, aSizeInfo.width, true);
   415     }
   416     if (aDirection.mVertical) {
   417       aContent->SetAttr(kNameSpaceID_None, nsGkAtoms::height, aSizeInfo.height, true);
   418     }
   419   }
   420   else {
   421     nsCOMPtr<nsIDOMElementCSSInlineStyle> inlineStyleContent =
   422       do_QueryInterface(aContent);
   423     if (inlineStyleContent) {
   424       nsCOMPtr<nsIDOMCSSStyleDeclaration> decl;
   425       inlineStyleContent->GetStyle(getter_AddRefs(decl));
   427       if (aOriginalSizeInfo) {
   428         decl->GetPropertyValue(NS_LITERAL_STRING("width"),
   429                                aOriginalSizeInfo->width);
   430         decl->GetPropertyValue(NS_LITERAL_STRING("height"),
   431                                aOriginalSizeInfo->height);
   432       }
   434       // only set the property if the element could have changed in that direction
   435       if (aDirection.mHorizontal) {
   436         nsAutoString widthstr(aSizeInfo.width);
   437         if (!widthstr.IsEmpty() &&
   438             !Substring(widthstr, widthstr.Length() - 2, 2).EqualsLiteral("px"))
   439           widthstr.AppendLiteral("px");
   440         decl->SetProperty(NS_LITERAL_STRING("width"), widthstr, EmptyString());
   441       }
   442       if (aDirection.mVertical) {
   443         nsAutoString heightstr(aSizeInfo.height);
   444         if (!heightstr.IsEmpty() &&
   445             !Substring(heightstr, heightstr.Length() - 2, 2).EqualsLiteral("px"))
   446           heightstr.AppendLiteral("px");
   447         decl->SetProperty(NS_LITERAL_STRING("height"), heightstr, EmptyString());
   448       }
   449     }
   450   }
   451 }
   453 /* static */ void
   454 nsResizerFrame::MaybePersistOriginalSize(nsIContent* aContent,
   455                                          const SizeInfo& aSizeInfo)
   456 {
   457   nsresult rv;
   459   aContent->GetProperty(nsGkAtoms::_moz_original_size, &rv);
   460   if (rv != NS_PROPTABLE_PROP_NOT_THERE)
   461     return;
   463   nsAutoPtr<SizeInfo> sizeInfo(new SizeInfo(aSizeInfo));
   464   rv = aContent->SetProperty(nsGkAtoms::_moz_original_size, sizeInfo.get(),
   465                              nsINode::DeleteProperty<nsResizerFrame::SizeInfo>);
   466   if (NS_SUCCEEDED(rv))
   467     sizeInfo.forget();
   468 }
   470 /* static */ void
   471 nsResizerFrame::RestoreOriginalSize(nsIContent* aContent)
   472 {
   473   nsresult rv;
   474   SizeInfo* sizeInfo =
   475     static_cast<SizeInfo*>(aContent->GetProperty(nsGkAtoms::_moz_original_size,
   476                            &rv));
   477   if (NS_FAILED(rv))
   478     return;
   480   NS_ASSERTION(sizeInfo, "We set a null sizeInfo!?");
   481   Direction direction = {1, 1};
   482   ResizeContent(aContent, direction, *sizeInfo, nullptr);
   483   aContent->DeleteProperty(nsGkAtoms::_moz_original_size);
   484 }
   486 /* returns a Direction struct containing the horizontal and vertical direction
   487  */
   488 nsResizerFrame::Direction
   489 nsResizerFrame::GetDirection()
   490 {
   491   static const nsIContent::AttrValuesArray strings[] =
   492     {&nsGkAtoms::topleft,    &nsGkAtoms::top,    &nsGkAtoms::topright,
   493      &nsGkAtoms::left,                           &nsGkAtoms::right,
   494      &nsGkAtoms::bottomleft, &nsGkAtoms::bottom, &nsGkAtoms::bottomright,
   495      &nsGkAtoms::bottomstart,                    &nsGkAtoms::bottomend,
   496      nullptr};
   498   static const Direction directions[] =
   499     {{-1, -1}, {0, -1}, {1, -1},
   500      {-1,  0},          {1,  0},
   501      {-1,  1}, {0,  1}, {1,  1},
   502      {-1,  1},          {1,  1}
   503     };
   505   if (!GetContent())
   506     return directions[0]; // default: topleft
   508   int32_t index = GetContent()->FindAttrValueIn(kNameSpaceID_None,
   509                                                 nsGkAtoms::dir,
   510                                                 strings, eCaseMatters);
   511   if(index < 0)
   512     return directions[0]; // default: topleft
   513   else if (index >= 8 && StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) {
   514     // Directions 8 and higher are RTL-aware directions and should reverse the
   515     // horizontal component if RTL.
   516     Direction direction = directions[index];
   517     direction.mHorizontal *= -1;
   518     return direction;
   519   }
   520   return directions[index];
   521 }
   523 void
   524 nsResizerFrame::MouseClicked(nsPresContext* aPresContext,
   525                              WidgetMouseEvent* aEvent)
   526 {
   527   // Execute the oncommand event handler.
   528   nsContentUtils::DispatchXULCommand(mContent,
   529                                      aEvent && aEvent->mFlags.mIsTrusted);
   530 }

mercurial