Bug 1590376 part 1 - Add a XULResizerElement and move all nsResizerFrame's event handling code there. r=ntim,smaug
I'm mostly moving the code verbatim, but I excluded a few bits that handled resizers inside menu popup frames, e.g. https://searchfox.org/mozilla-central/rev/7bb1cc6abf6634b2a20f71935e1e519e73402b63/layout/xul/nsResizerFrame.cpp#165-170 I don't think we need that functionallity anymore and it simplifies the code to exclude it. Differential Revision: https://phabricator.services.mozilla.com/D105926
This commit is contained in:
@@ -71,6 +71,7 @@
|
||||
#include "mozilla/dom/XULFrameElementBinding.h"
|
||||
#include "mozilla/dom/XULMenuElementBinding.h"
|
||||
#include "mozilla/dom/XULPopupElementBinding.h"
|
||||
#include "mozilla/dom/XULResizerElementBinding.h"
|
||||
#include "mozilla/dom/XULTextElementBinding.h"
|
||||
#include "mozilla/dom/XULTreeElementBinding.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
@@ -3838,6 +3839,8 @@ bool HTMLConstructor(JSContext* aCx, unsigned aArgc, JS::Value* aVp,
|
||||
if (definition->mLocalName == nsGkAtoms::description ||
|
||||
definition->mLocalName == nsGkAtoms::label) {
|
||||
cb = XULTextElement_Binding::GetConstructorObject;
|
||||
} else if (definition->mLocalName == nsGkAtoms::resizer) {
|
||||
cb = XULResizerElement_Binding::GetConstructorObject;
|
||||
} else if (definition->mLocalName == nsGkAtoms::menupopup ||
|
||||
definition->mLocalName == nsGkAtoms::popup ||
|
||||
definition->mLocalName == nsGkAtoms::panel ||
|
||||
|
||||
10
dom/webidl/XULResizerElement.webidl
Normal file
10
dom/webidl/XULResizerElement.webidl
Normal file
@@ -0,0 +1,10 @@
|
||||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
[ChromeOnly, Exposed=Window]
|
||||
interface XULResizerElement : XULElement
|
||||
{
|
||||
[HTMLConstructor] constructor();
|
||||
};
|
||||
@@ -1012,6 +1012,7 @@ WEBIDL_FILES = [
|
||||
"XULCommandEvent.webidl",
|
||||
"XULElement.webidl",
|
||||
"XULPopupElement.webidl",
|
||||
"XULResizerElement.webidl",
|
||||
]
|
||||
|
||||
if CONFIG["MOZ_DOM_STREAMS"]:
|
||||
|
||||
465
dom/xul/XULResizerElement.cpp
Normal file
465
dom/xul/XULResizerElement.cpp
Normal file
@@ -0,0 +1,465 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/dom/XULResizerElement.h"
|
||||
#include "mozilla/dom/XULResizerElementBinding.h"
|
||||
|
||||
#include "mozilla/EventDispatcher.h"
|
||||
#include "mozilla/PresShell.h"
|
||||
#include "mozilla/dom/Document.h"
|
||||
#include "mozilla/dom/DocumentInlines.h"
|
||||
#include "mozilla/MouseEvents.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsDocShell.h"
|
||||
#include "nsIBaseWindow.h"
|
||||
#include "nsICSSDeclaration.h"
|
||||
#include "nsIDocShellTreeOwner.h"
|
||||
#include "nsIFrame.h"
|
||||
#include "nsIScreenManager.h"
|
||||
#include "nsLayoutUtils.h"
|
||||
#include "nsPresContext.h"
|
||||
#include "nsStyledElement.h"
|
||||
|
||||
#ifdef MOZ_WAYLAND
|
||||
# include "mozilla/WidgetUtilsGtk.h"
|
||||
# define IS_WAYLAND_DISPLAY() mozilla::widget::GdkIsWaylandDisplay()
|
||||
#else
|
||||
# define IS_WAYLAND_DISPLAY() false
|
||||
#endif
|
||||
|
||||
namespace mozilla::dom {
|
||||
|
||||
nsXULElement* NS_NewXULResizerElement(
|
||||
already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo) {
|
||||
RefPtr<mozilla::dom::NodeInfo> nodeInfo(aNodeInfo);
|
||||
auto* nim = nodeInfo->NodeInfoManager();
|
||||
return new (nim) XULResizerElement(nodeInfo.forget());
|
||||
}
|
||||
|
||||
static bool GetEventPoint(const WidgetGUIEvent* aEvent,
|
||||
LayoutDeviceIntPoint& aPoint) {
|
||||
NS_ENSURE_TRUE(aEvent, false);
|
||||
|
||||
const WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
|
||||
if (touchEvent) {
|
||||
// return false if there is more than one touch on the page, or if
|
||||
// we can't find a touch point
|
||||
if (touchEvent->mTouches.Length() != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const dom::Touch* touch = touchEvent->mTouches.SafeElementAt(0);
|
||||
if (!touch) {
|
||||
return false;
|
||||
}
|
||||
aPoint = touch->mRefPoint;
|
||||
} else {
|
||||
aPoint = aEvent->mRefPoint;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
JSObject* XULResizerElement::WrapNode(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) {
|
||||
return XULResizerElement_Binding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
XULResizerElement::Direction XULResizerElement::GetDirection() {
|
||||
static const mozilla::dom::Element::AttrValuesArray strings[] = {
|
||||
// clang-format off
|
||||
nsGkAtoms::topleft, nsGkAtoms::top, nsGkAtoms::topright,
|
||||
nsGkAtoms::left, nsGkAtoms::right,
|
||||
nsGkAtoms::bottomleft, nsGkAtoms::bottom, nsGkAtoms::bottomright,
|
||||
nsGkAtoms::bottomstart, nsGkAtoms::bottomend,
|
||||
nullptr
|
||||
// clang-format on
|
||||
};
|
||||
|
||||
static const Direction directions[] = {
|
||||
// clang-format off
|
||||
{-1, -1}, {0, -1}, {1, -1},
|
||||
{-1, 0}, {1, 0},
|
||||
{-1, 1}, {0, 1}, {1, 1},
|
||||
{-1, 1}, {1, 1}
|
||||
// clang-format on
|
||||
};
|
||||
|
||||
const auto* frame = GetPrimaryFrame();
|
||||
if (!frame) {
|
||||
return directions[0]; // default: topleft
|
||||
}
|
||||
|
||||
int32_t index =
|
||||
FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::dir, strings, eCaseMatters);
|
||||
if (index < 0) {
|
||||
return directions[0]; // default: topleft
|
||||
}
|
||||
|
||||
if (index >= 8) {
|
||||
// Directions 8 and higher are RTL-aware directions and should reverse the
|
||||
// horizontal component if RTL.
|
||||
auto wm = frame->GetWritingMode();
|
||||
if (wm.IsPhysicalRTL()) {
|
||||
Direction direction = directions[index];
|
||||
direction.mHorizontal *= -1;
|
||||
return direction;
|
||||
}
|
||||
}
|
||||
|
||||
return directions[index];
|
||||
}
|
||||
|
||||
nsresult XULResizerElement::PostHandleEvent(EventChainPostVisitor& aVisitor) {
|
||||
if (aVisitor.mEventStatus != nsEventStatus_eConsumeNoDefault) {
|
||||
PostHandleEventInternal(aVisitor);
|
||||
}
|
||||
return nsXULElement::PostHandleEvent(aVisitor);
|
||||
}
|
||||
|
||||
void XULResizerElement::PostHandleEventInternal(
|
||||
EventChainPostVisitor& aVisitor) {
|
||||
bool doDefault = true;
|
||||
const WidgetEvent& event = *aVisitor.mEvent;
|
||||
switch (event.mMessage) {
|
||||
case eTouchStart:
|
||||
case eMouseDown: {
|
||||
if (event.mClass == eTouchEventClass ||
|
||||
(event.mClass == eMouseEventClass &&
|
||||
event.AsMouseEvent()->mButton == MouseButton::ePrimary)) {
|
||||
nsCOMPtr<nsIBaseWindow> window;
|
||||
nsIContent* contentToResize =
|
||||
GetContentToResize(getter_AddRefs(window));
|
||||
if (!window && !contentToResize) {
|
||||
break; // don't do anything if there's nothing to resize
|
||||
}
|
||||
auto* guiEvent = event.AsGUIEvent();
|
||||
if (contentToResize) {
|
||||
nsIFrame* frame = contentToResize->GetPrimaryFrame();
|
||||
if (!frame) {
|
||||
break;
|
||||
}
|
||||
// cache the content rectangle for the frame to resize
|
||||
// GetScreenRectInAppUnits returns the border box rectangle, so
|
||||
// adjust to get the desired content rectangle.
|
||||
nsRect rect = frame->GetScreenRectInAppUnits();
|
||||
if (frame->StylePosition()->mBoxSizing == StyleBoxSizing::Content) {
|
||||
rect.Deflate(frame->GetUsedBorderAndPadding());
|
||||
}
|
||||
|
||||
mMouseDownRect = LayoutDeviceIntRect::FromAppUnitsToNearest(
|
||||
rect, frame->PresContext()->AppUnitsPerDevPixel());
|
||||
} else {
|
||||
// ask the widget implementation to begin a resize drag if it can
|
||||
Direction direction = GetDirection();
|
||||
nsresult rv = guiEvent->mWidget->BeginResizeDrag(
|
||||
const_cast<WidgetGUIEvent*>(guiEvent), direction.mHorizontal,
|
||||
direction.mVertical);
|
||||
// for native drags, don't set the fields below
|
||||
if (rv != NS_ERROR_NOT_IMPLEMENTED) {
|
||||
break;
|
||||
}
|
||||
|
||||
// if there's no native resize support, we need to do window
|
||||
// resizing ourselves
|
||||
window->GetPositionAndSize(&mMouseDownRect.x, &mMouseDownRect.y,
|
||||
&mMouseDownRect.width,
|
||||
&mMouseDownRect.height);
|
||||
}
|
||||
|
||||
// remember current mouse coordinates
|
||||
LayoutDeviceIntPoint refPoint;
|
||||
if (!GetEventPoint(guiEvent, refPoint)) {
|
||||
break;
|
||||
}
|
||||
mMouseDownPoint = refPoint + guiEvent->mWidget->WidgetToScreenOffset();
|
||||
mTrackingMouseMove = true;
|
||||
PresShell::SetCapturingContent(this, CaptureFlags::IgnoreAllowedState);
|
||||
doDefault = false;
|
||||
}
|
||||
} break;
|
||||
|
||||
case eTouchMove:
|
||||
case eMouseMove: {
|
||||
if (mTrackingMouseMove) {
|
||||
nsCOMPtr<nsIBaseWindow> window;
|
||||
nsCOMPtr<nsIContent> contentToResize =
|
||||
GetContentToResize(getter_AddRefs(window));
|
||||
if (!window && !contentToResize) {
|
||||
break; // don't do anything if there's nothing to resize
|
||||
}
|
||||
nsIFrame* frame = contentToResize ? contentToResize->GetPrimaryFrame()
|
||||
: GetPrimaryFrame();
|
||||
if (!frame) {
|
||||
break;
|
||||
}
|
||||
|
||||
// both MouseMove and direction are negative when pointing to the
|
||||
// top and left, and positive when pointing to the bottom and right
|
||||
|
||||
// retrieve the offset of the mousemove event relative to the mousedown.
|
||||
// The difference is how much the resize needs to be
|
||||
LayoutDeviceIntPoint refPoint;
|
||||
auto* guiEvent = event.AsGUIEvent();
|
||||
if (!GetEventPoint(guiEvent, refPoint)) {
|
||||
break;
|
||||
}
|
||||
LayoutDeviceIntPoint screenPoint =
|
||||
refPoint + guiEvent->mWidget->WidgetToScreenOffset();
|
||||
LayoutDeviceIntPoint mouseMove(screenPoint - mMouseDownPoint);
|
||||
|
||||
// Determine which direction to resize by checking the dir attribute.
|
||||
// For windows and menus, ensure that it can be resized in that
|
||||
// direction.
|
||||
Direction direction = GetDirection();
|
||||
|
||||
LayoutDeviceIntRect rect = mMouseDownRect;
|
||||
|
||||
// Check if there are any size constraints on this window.
|
||||
widget::SizeConstraints sizeConstraints;
|
||||
if (window) {
|
||||
nsCOMPtr<nsIWidget> widget;
|
||||
window->GetMainWidget(getter_AddRefs(widget));
|
||||
sizeConstraints = widget->GetSizeConstraints();
|
||||
}
|
||||
|
||||
AdjustDimensions(&rect.x, &rect.width, sizeConstraints.mMinSize.width,
|
||||
sizeConstraints.mMaxSize.width, mouseMove.x,
|
||||
direction.mHorizontal);
|
||||
AdjustDimensions(&rect.y, &rect.height, sizeConstraints.mMinSize.height,
|
||||
sizeConstraints.mMaxSize.height, mouseMove.y,
|
||||
direction.mVertical);
|
||||
|
||||
// Don't allow resizing a window or a popup past the edge of the screen,
|
||||
// so adjust the rectangle to fit within the available screen area.
|
||||
// Don't check it on Wayland as we can't get absolute window position
|
||||
// there.
|
||||
if (window && !IS_WAYLAND_DISPLAY()) {
|
||||
nsCOMPtr<nsIScreen> screen;
|
||||
nsCOMPtr<nsIScreenManager> sm(
|
||||
do_GetService("@mozilla.org/gfx/screenmanager;1"));
|
||||
if (sm) {
|
||||
CSSIntRect frameRect = frame->GetScreenRect();
|
||||
// ScreenForRect requires display pixels, so scale from device pix
|
||||
double scale;
|
||||
window->GetUnscaledDevicePixelsPerCSSPixel(&scale);
|
||||
sm->ScreenForRect(NSToIntRound(frameRect.x / scale),
|
||||
NSToIntRound(frameRect.y / scale), 1, 1,
|
||||
getter_AddRefs(screen));
|
||||
if (screen) {
|
||||
LayoutDeviceIntRect screenRect;
|
||||
screen->GetRect(&screenRect.x, &screenRect.y, &screenRect.width,
|
||||
&screenRect.height);
|
||||
rect.IntersectRect(rect, screenRect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (contentToResize) {
|
||||
// convert the rectangle into css pixels. When changing the size in a
|
||||
// direction, don't allow the new size to be less that the resizer's
|
||||
// size. This ensures that content isn't resized too small as to make
|
||||
// the resizer invisible.
|
||||
nsRect appUnitsRect =
|
||||
ToAppUnits(rect.ToUnknownRect(),
|
||||
frame->PresContext()->AppUnitsPerDevPixel());
|
||||
if (auto* resizerFrame = GetPrimaryFrame()) {
|
||||
nsRect frameRect = resizerFrame->GetRect();
|
||||
if (appUnitsRect.width < frameRect.width && mouseMove.x)
|
||||
appUnitsRect.width = frameRect.width;
|
||||
if (appUnitsRect.height < frameRect.height && mouseMove.y)
|
||||
appUnitsRect.height = frameRect.height;
|
||||
}
|
||||
nsIntRect cssRect =
|
||||
appUnitsRect.ToInsidePixels(AppUnitsPerCSSPixel());
|
||||
|
||||
SizeInfo sizeInfo, originalSizeInfo;
|
||||
sizeInfo.width.AppendInt(cssRect.width);
|
||||
sizeInfo.height.AppendInt(cssRect.height);
|
||||
ResizeContent(contentToResize, direction, sizeInfo,
|
||||
&originalSizeInfo);
|
||||
MaybePersistOriginalSize(contentToResize, originalSizeInfo);
|
||||
} else {
|
||||
window->SetPositionAndSize(
|
||||
rect.x, rect.y, rect.width, rect.height,
|
||||
nsIBaseWindow::eRepaint); // do the repaint.
|
||||
}
|
||||
|
||||
doDefault = false;
|
||||
}
|
||||
} break;
|
||||
|
||||
case eMouseClick: {
|
||||
auto* mouseEvent = event.AsMouseEvent();
|
||||
if (mouseEvent->IsLeftClickEvent()) {
|
||||
// Execute the oncommand event handler.
|
||||
nsContentUtils::DispatchXULCommand(
|
||||
this, false, nullptr, nullptr, mouseEvent->IsControl(),
|
||||
mouseEvent->IsAlt(), mouseEvent->IsShift(), mouseEvent->IsMeta(),
|
||||
mouseEvent->mInputSource, mouseEvent->mButton);
|
||||
}
|
||||
} break;
|
||||
|
||||
case eTouchEnd:
|
||||
case eMouseUp: {
|
||||
if (event.mClass == eTouchEventClass ||
|
||||
(event.mClass == eMouseEventClass &&
|
||||
event.AsMouseEvent()->mButton == MouseButton::ePrimary)) {
|
||||
mTrackingMouseMove = false;
|
||||
PresShell::ReleaseCapturingContent();
|
||||
doDefault = false;
|
||||
}
|
||||
} break;
|
||||
|
||||
case eMouseDoubleClick: {
|
||||
if (event.AsMouseEvent()->mButton == MouseButton::ePrimary) {
|
||||
nsCOMPtr<nsIBaseWindow> window;
|
||||
nsIContent* contentToResize =
|
||||
GetContentToResize(getter_AddRefs(window));
|
||||
if (contentToResize) {
|
||||
RestoreOriginalSize(contentToResize);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!doDefault) {
|
||||
aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
|
||||
}
|
||||
}
|
||||
|
||||
nsIContent* XULResizerElement::GetContentToResize(nsIBaseWindow** aWindow) {
|
||||
*aWindow = nullptr;
|
||||
|
||||
Document* doc = GetComposedDoc();
|
||||
if (!doc) {
|
||||
return nullptr;
|
||||
}
|
||||
nsAutoString elementid;
|
||||
GetAttr(kNameSpaceID_None, nsGkAtoms::element, elementid);
|
||||
if (elementid.IsEmpty()) {
|
||||
// don't allow resizing windows in content shells
|
||||
if (nsPresContext* presContext = doc->GetPresContext();
|
||||
presContext && !presContext->IsChrome()) {
|
||||
// don't allow resizers in content shells, except for the viewport
|
||||
// scrollbar which doesn't have a parent
|
||||
nsIContent* nonNativeAnon = FindFirstNonChromeOnlyAccessContent();
|
||||
if (!nonNativeAnon || nonNativeAnon->GetParent()) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// get the document and the window - should this be cached?
|
||||
nsPIDOMWindowOuter* win = OwnerDoc()->GetWindow();
|
||||
nsCOMPtr<nsIDocShell> docShell = win ? win->GetDocShell() : nullptr;
|
||||
if (docShell) {
|
||||
nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
|
||||
docShell->GetTreeOwner(getter_AddRefs(treeOwner));
|
||||
if (treeOwner) {
|
||||
CallQueryInterface(treeOwner, aWindow);
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (elementid.EqualsLiteral("_parent")) {
|
||||
// return the parent, but skip over native anonymous content
|
||||
nsIContent* parent = GetParent();
|
||||
return parent ? parent->FindFirstNonChromeOnlyAccessContent() : nullptr;
|
||||
}
|
||||
|
||||
return doc->GetElementById(elementid);
|
||||
}
|
||||
|
||||
/* static */
|
||||
void XULResizerElement::AdjustDimensions(int32_t* aPos, int32_t* aSize,
|
||||
int32_t aMinSize, int32_t aMaxSize,
|
||||
int32_t aMovement,
|
||||
int8_t aResizerDirection) {
|
||||
int32_t oldSize = *aSize;
|
||||
|
||||
*aSize += aResizerDirection * aMovement;
|
||||
// use one as a minimum size or the element could disappear
|
||||
if (*aSize < 1) *aSize = 1;
|
||||
|
||||
// Constrain the size within the minimum and maximum size.
|
||||
*aSize = std::max(aMinSize, std::min(aMaxSize, *aSize));
|
||||
|
||||
// For left and top resizers, the window must be moved left by the same
|
||||
// amount that the window was resized.
|
||||
if (aResizerDirection == -1) *aPos += oldSize - *aSize;
|
||||
}
|
||||
|
||||
/* static */
|
||||
void XULResizerElement::ResizeContent(nsIContent* aContent,
|
||||
const Direction& aDirection,
|
||||
const SizeInfo& aSizeInfo,
|
||||
SizeInfo* aOriginalSizeInfo) {
|
||||
if (RefPtr<nsStyledElement> inlineStyleContent =
|
||||
nsStyledElement::FromNode(aContent)) {
|
||||
nsICSSDeclaration* decl = inlineStyleContent->Style();
|
||||
|
||||
if (aOriginalSizeInfo) {
|
||||
decl->GetPropertyValue("width"_ns, aOriginalSizeInfo->width);
|
||||
decl->GetPropertyValue("height"_ns, aOriginalSizeInfo->height);
|
||||
}
|
||||
|
||||
// only set the property if the element could have changed in that
|
||||
// direction
|
||||
if (aDirection.mHorizontal) {
|
||||
nsAutoCString widthstr(aSizeInfo.width);
|
||||
if (!widthstr.IsEmpty() &&
|
||||
!Substring(widthstr, widthstr.Length() - 2, 2).EqualsLiteral("px"))
|
||||
widthstr.AppendLiteral("px");
|
||||
decl->SetProperty("width"_ns, widthstr, ""_ns, IgnoreErrors());
|
||||
}
|
||||
if (aDirection.mVertical) {
|
||||
nsAutoCString heightstr(aSizeInfo.height);
|
||||
if (!heightstr.IsEmpty() &&
|
||||
!Substring(heightstr, heightstr.Length() - 2, 2).EqualsLiteral("px"))
|
||||
heightstr.AppendLiteral("px");
|
||||
decl->SetProperty("height"_ns, heightstr, ""_ns, IgnoreErrors());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* static */
|
||||
void XULResizerElement::MaybePersistOriginalSize(nsIContent* aContent,
|
||||
const SizeInfo& aSizeInfo) {
|
||||
nsresult rv;
|
||||
aContent->GetProperty(nsGkAtoms::_moz_original_size, &rv);
|
||||
if (rv != NS_PROPTABLE_PROP_NOT_THERE) {
|
||||
return;
|
||||
}
|
||||
|
||||
UniquePtr<SizeInfo> sizeInfo(new SizeInfo(aSizeInfo));
|
||||
rv = aContent->SetProperty(
|
||||
nsGkAtoms::_moz_original_size, sizeInfo.get(),
|
||||
nsINode::DeleteProperty<XULResizerElement::SizeInfo>);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
Unused << sizeInfo.release();
|
||||
}
|
||||
}
|
||||
|
||||
/* static */
|
||||
void XULResizerElement::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, nullptr);
|
||||
aContent->RemoveProperty(nsGkAtoms::_moz_original_size);
|
||||
}
|
||||
|
||||
} // namespace mozilla::dom
|
||||
80
dom/xul/XULResizerElement.h
Normal file
80
dom/xul/XULResizerElement.h
Normal file
@@ -0,0 +1,80 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_XULResizerElement_h
|
||||
#define mozilla_dom_XULResizerElement_h
|
||||
|
||||
#include "nsXULElement.h"
|
||||
#include "Units.h"
|
||||
class nsIBaseWindow;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
nsXULElement* NS_NewXULResizerElement(
|
||||
already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
|
||||
|
||||
class XULResizerElement final : public nsXULElement {
|
||||
public:
|
||||
explicit XULResizerElement(
|
||||
already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
|
||||
: nsXULElement(std::move(aNodeInfo)) {}
|
||||
|
||||
MOZ_CAN_RUN_SCRIPT
|
||||
nsresult PostHandleEvent(mozilla::EventChainPostVisitor&) override;
|
||||
|
||||
private:
|
||||
virtual ~XULResizerElement() = default;
|
||||
JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) final;
|
||||
|
||||
MOZ_CAN_RUN_SCRIPT
|
||||
void PostHandleEventInternal(mozilla::EventChainPostVisitor&);
|
||||
|
||||
struct Direction {
|
||||
int8_t mHorizontal;
|
||||
int8_t mVertical;
|
||||
};
|
||||
Direction GetDirection();
|
||||
|
||||
nsIContent* GetContentToResize(nsIBaseWindow** aWindow);
|
||||
|
||||
/**
|
||||
* Adjust the window position and size in a direction according to the mouse
|
||||
* movement and the resizer direction. The minimum and maximum size is used
|
||||
* to constrain the size.
|
||||
*
|
||||
* @param aPos left or top position
|
||||
* @param aSize width or height
|
||||
* @param aMinSize minimum width or height
|
||||
* @param aMacSize maximum width or height
|
||||
* @param aMovement the amount the mouse was moved
|
||||
* @param aResizerDirection resizer direction returned by GetDirection
|
||||
*/
|
||||
static void AdjustDimensions(int32_t* aPos, int32_t* aSize, int32_t aMinSize,
|
||||
int32_t aMaxSize, int32_t aMovement,
|
||||
int8_t aResizerDirection);
|
||||
|
||||
struct SizeInfo {
|
||||
nsCString width, height;
|
||||
};
|
||||
static void SizeInfoDtorFunc(void* aObject, nsAtom* 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);
|
||||
|
||||
LayoutDeviceIntRect mMouseDownRect;
|
||||
LayoutDeviceIntPoint mMouseDownPoint;
|
||||
bool mTrackingMouseMove = false;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // XULResizerElement_h
|
||||
@@ -26,6 +26,7 @@ EXPORTS.mozilla.dom += [
|
||||
"XULMenuElement.h",
|
||||
"XULPersist.h",
|
||||
"XULPopupElement.h",
|
||||
"XULResizerElement.h",
|
||||
"XULTextElement.h",
|
||||
"XULTooltipElement.h",
|
||||
"XULTreeElement.h",
|
||||
@@ -45,6 +46,7 @@ UNIFIED_SOURCES += [
|
||||
"XULMenuElement.cpp",
|
||||
"XULPersist.cpp",
|
||||
"XULPopupElement.cpp",
|
||||
"XULResizerElement.cpp",
|
||||
"XULTextElement.cpp",
|
||||
"XULTooltipElement.cpp",
|
||||
"XULTreeElement.cpp",
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "XULFrameElement.h"
|
||||
#include "XULMenuElement.h"
|
||||
#include "XULPopupElement.h"
|
||||
#include "XULResizerElement.h"
|
||||
#include "XULTextElement.h"
|
||||
#include "XULTooltipElement.h"
|
||||
#include "XULTreeElement.h"
|
||||
@@ -155,6 +156,10 @@ nsXULElement* nsXULElement::Construct(
|
||||
// them into account, otherwise you'll start getting "Illegal constructor"
|
||||
// exceptions in chrome code.
|
||||
RefPtr<mozilla::dom::NodeInfo> nodeInfo = aNodeInfo;
|
||||
if (nodeInfo->Equals(nsGkAtoms::resizer)) {
|
||||
return NS_NewXULResizerElement(nodeInfo.forget());
|
||||
}
|
||||
|
||||
if (nodeInfo->Equals(nsGkAtoms::label) ||
|
||||
nodeInfo->Equals(nsGkAtoms::description)) {
|
||||
auto* nim = nodeInfo->NodeInfoManager();
|
||||
|
||||
@@ -244,8 +244,6 @@ nsIFrame* NS_NewTreeBodyFrame(PresShell* aPresShell, ComputedStyle* aStyle);
|
||||
|
||||
nsIFrame* NS_NewTitleBarFrame(PresShell* aPresShell, ComputedStyle* aStyle);
|
||||
|
||||
nsIFrame* NS_NewResizerFrame(PresShell* aPresShell, ComputedStyle* aStyle);
|
||||
|
||||
nsHTMLScrollFrame* NS_NewHTMLScrollFrame(PresShell* aPresShell,
|
||||
ComputedStyle* aStyle, bool aIsRoot);
|
||||
|
||||
@@ -4127,7 +4125,6 @@ nsCSSFrameConstructor::FindXULTagData(const Element& aElement,
|
||||
SCROLLABLE_XUL_CREATE(checkbox, NS_NewButtonBoxFrame),
|
||||
SCROLLABLE_XUL_CREATE(radio, NS_NewButtonBoxFrame),
|
||||
SCROLLABLE_XUL_CREATE(titlebar, NS_NewTitleBarFrame),
|
||||
SCROLLABLE_XUL_CREATE(resizer, NS_NewResizerFrame),
|
||||
SCROLLABLE_XUL_CREATE(toolbarpaletteitem, NS_NewBoxFrame),
|
||||
SCROLLABLE_XUL_CREATE(treecolpicker, NS_NewButtonBoxFrame),
|
||||
SIMPLE_XUL_CREATE(image, NS_NewImageBoxFrame),
|
||||
|
||||
@@ -33,13 +33,6 @@
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
#ifdef MOZ_WAYLAND
|
||||
# include "mozilla/WidgetUtilsGtk.h"
|
||||
# define IS_WAYLAND_DISPLAY() mozilla::widget::GdkIsWaylandDisplay()
|
||||
#else
|
||||
# define IS_WAYLAND_DISPLAY() false
|
||||
#endif
|
||||
|
||||
//
|
||||
// NS_NewResizerFrame
|
||||
//
|
||||
@@ -55,467 +48,3 @@ nsResizerFrame::nsResizerFrame(ComputedStyle* aStyle,
|
||||
nsPresContext* aPresContext)
|
||||
: nsTitleBarFrame(aStyle, aPresContext, kClassID) {}
|
||||
|
||||
nsresult nsResizerFrame::HandleEvent(nsPresContext* aPresContext,
|
||||
WidgetGUIEvent* aEvent,
|
||||
nsEventStatus* aEventStatus) {
|
||||
NS_ENSURE_ARG_POINTER(aEventStatus);
|
||||
if (nsEventStatus_eConsumeNoDefault == *aEventStatus) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
AutoWeakFrame weakFrame(this);
|
||||
bool doDefault = true;
|
||||
|
||||
switch (aEvent->mMessage) {
|
||||
case eTouchStart:
|
||||
case eMouseDown: {
|
||||
if (aEvent->mClass == eTouchEventClass ||
|
||||
(aEvent->mClass == eMouseEventClass &&
|
||||
aEvent->AsMouseEvent()->mButton == MouseButton::ePrimary)) {
|
||||
nsCOMPtr<nsIBaseWindow> window;
|
||||
mozilla::PresShell* presShell = aPresContext->GetPresShell();
|
||||
nsIContent* contentToResize =
|
||||
GetContentToResize(presShell, getter_AddRefs(window));
|
||||
if (contentToResize) {
|
||||
nsIFrame* frameToResize = contentToResize->GetPrimaryFrame();
|
||||
if (!frameToResize) break;
|
||||
|
||||
// cache the content rectangle for the frame to resize
|
||||
// GetScreenRectInAppUnits returns the border box rectangle, so
|
||||
// adjust to get the desired content rectangle.
|
||||
nsRect rect = frameToResize->GetScreenRectInAppUnits();
|
||||
if (frameToResize->StylePosition()->mBoxSizing ==
|
||||
StyleBoxSizing::Content) {
|
||||
rect.Deflate(frameToResize->GetUsedBorderAndPadding());
|
||||
}
|
||||
|
||||
mMouseDownRect = LayoutDeviceIntRect::FromAppUnitsToNearest(
|
||||
rect, aPresContext->AppUnitsPerDevPixel());
|
||||
doDefault = false;
|
||||
} else {
|
||||
// If there is no window, then resizing isn't allowed.
|
||||
if (!window) break;
|
||||
|
||||
doDefault = false;
|
||||
|
||||
// ask the widget implementation to begin a resize drag if it can
|
||||
Direction direction = GetDirection();
|
||||
nsresult rv = aEvent->mWidget->BeginResizeDrag(
|
||||
aEvent, direction.mHorizontal, direction.mVertical);
|
||||
// for native drags, don't set the fields below
|
||||
if (rv != NS_ERROR_NOT_IMPLEMENTED) break;
|
||||
|
||||
// if there's no native resize support, we need to do window
|
||||
// resizing ourselves
|
||||
window->GetPositionAndSize(&mMouseDownRect.x, &mMouseDownRect.y,
|
||||
&mMouseDownRect.width,
|
||||
&mMouseDownRect.height);
|
||||
}
|
||||
|
||||
// remember current mouse coordinates
|
||||
LayoutDeviceIntPoint refPoint;
|
||||
if (!GetEventPoint(aEvent, refPoint)) return NS_OK;
|
||||
mMouseDownPoint = refPoint + aEvent->mWidget->WidgetToScreenOffset();
|
||||
|
||||
// we're tracking
|
||||
mTrackingMouseMove = true;
|
||||
|
||||
PresShell::SetCapturingContent(GetContent(),
|
||||
CaptureFlags::IgnoreAllowedState);
|
||||
}
|
||||
} break;
|
||||
|
||||
case eTouchEnd:
|
||||
case eMouseUp: {
|
||||
if (aEvent->mClass == eTouchEventClass ||
|
||||
(aEvent->mClass == eMouseEventClass &&
|
||||
aEvent->AsMouseEvent()->mButton == MouseButton::ePrimary)) {
|
||||
// we're done tracking.
|
||||
mTrackingMouseMove = false;
|
||||
|
||||
PresShell::ReleaseCapturingContent();
|
||||
|
||||
doDefault = false;
|
||||
}
|
||||
} break;
|
||||
|
||||
case eTouchMove:
|
||||
case eMouseMove: {
|
||||
if (mTrackingMouseMove) {
|
||||
nsCOMPtr<nsIBaseWindow> window;
|
||||
mozilla::PresShell* presShell = aPresContext->GetPresShell();
|
||||
nsCOMPtr<nsIContent> contentToResize =
|
||||
GetContentToResize(presShell, getter_AddRefs(window));
|
||||
|
||||
// check if the returned content really is a menupopup
|
||||
nsMenuPopupFrame* menuPopupFrame = nullptr;
|
||||
if (contentToResize) {
|
||||
menuPopupFrame = do_QueryFrame(contentToResize->GetPrimaryFrame());
|
||||
}
|
||||
|
||||
// both MouseMove and direction are negative when pointing to the
|
||||
// top and left, and positive when pointing to the bottom and right
|
||||
|
||||
// retrieve the offset of the mousemove event relative to the mousedown.
|
||||
// The difference is how much the resize needs to be
|
||||
LayoutDeviceIntPoint refPoint;
|
||||
if (!GetEventPoint(aEvent, refPoint)) return NS_OK;
|
||||
LayoutDeviceIntPoint screenPoint =
|
||||
refPoint + aEvent->mWidget->WidgetToScreenOffset();
|
||||
LayoutDeviceIntPoint mouseMove(screenPoint - mMouseDownPoint);
|
||||
|
||||
// Determine which direction to resize by checking the dir attribute.
|
||||
// For windows and menus, ensure that it can be resized in that
|
||||
// direction.
|
||||
Direction direction = GetDirection();
|
||||
if (window || menuPopupFrame) {
|
||||
if (menuPopupFrame) {
|
||||
menuPopupFrame->CanAdjustEdges(
|
||||
(direction.mHorizontal == -1) ? eSideLeft : eSideRight,
|
||||
(direction.mVertical == -1) ? eSideTop : eSideBottom,
|
||||
mouseMove);
|
||||
}
|
||||
} else if (!contentToResize) {
|
||||
break; // don't do anything if there's nothing to resize
|
||||
}
|
||||
|
||||
LayoutDeviceIntRect rect = mMouseDownRect;
|
||||
|
||||
// Check if there are any size constraints on this window.
|
||||
widget::SizeConstraints sizeConstraints;
|
||||
if (window) {
|
||||
nsCOMPtr<nsIWidget> widget;
|
||||
window->GetMainWidget(getter_AddRefs(widget));
|
||||
sizeConstraints = widget->GetSizeConstraints();
|
||||
}
|
||||
|
||||
AdjustDimensions(&rect.x, &rect.width, sizeConstraints.mMinSize.width,
|
||||
sizeConstraints.mMaxSize.width, mouseMove.x,
|
||||
direction.mHorizontal);
|
||||
AdjustDimensions(&rect.y, &rect.height, sizeConstraints.mMinSize.height,
|
||||
sizeConstraints.mMaxSize.height, mouseMove.y,
|
||||
direction.mVertical);
|
||||
|
||||
// Don't allow resizing a window or a popup past the edge of the screen,
|
||||
// so adjust the rectangle to fit within the available screen area.
|
||||
// Don't check it on Wayland as we can't get absolute window position
|
||||
// there.
|
||||
if (window && !IS_WAYLAND_DISPLAY()) {
|
||||
nsCOMPtr<nsIScreen> screen;
|
||||
nsCOMPtr<nsIScreenManager> sm(
|
||||
do_GetService("@mozilla.org/gfx/screenmanager;1"));
|
||||
if (sm) {
|
||||
CSSIntRect frameRect = GetScreenRect();
|
||||
// ScreenForRect requires display pixels, so scale from device pix
|
||||
double scale;
|
||||
window->GetUnscaledDevicePixelsPerCSSPixel(&scale);
|
||||
sm->ScreenForRect(NSToIntRound(frameRect.x / scale),
|
||||
NSToIntRound(frameRect.y / scale), 1, 1,
|
||||
getter_AddRefs(screen));
|
||||
if (screen) {
|
||||
LayoutDeviceIntRect screenRect;
|
||||
screen->GetRect(&screenRect.x, &screenRect.y, &screenRect.width,
|
||||
&screenRect.height);
|
||||
rect.IntersectRect(rect, screenRect);
|
||||
}
|
||||
}
|
||||
} else if (menuPopupFrame && !IS_WAYLAND_DISPLAY()) {
|
||||
nsRect frameRect = menuPopupFrame->GetScreenRectInAppUnits();
|
||||
nsIFrame* rootFrame = aPresContext->PresShell()->GetRootFrame();
|
||||
nsRect rootScreenRect = rootFrame->GetScreenRectInAppUnits();
|
||||
|
||||
nsPopupLevel popupLevel = menuPopupFrame->PopupLevel();
|
||||
int32_t appPerDev = aPresContext->AppUnitsPerDevPixel();
|
||||
LayoutDeviceIntRect screenRect = menuPopupFrame->GetConstraintRect(
|
||||
LayoutDeviceIntRect::FromAppUnitsToNearest(frameRect, appPerDev),
|
||||
// round using ...ToInside as it's better to be a pixel too small
|
||||
// than be too large. If the popup is too large it could get
|
||||
// flipped to the opposite side of the anchor point while
|
||||
// resizing.
|
||||
LayoutDeviceIntRect::FromAppUnitsToInside(rootScreenRect,
|
||||
appPerDev),
|
||||
popupLevel);
|
||||
rect.IntersectRect(rect, screenRect);
|
||||
}
|
||||
|
||||
if (contentToResize) {
|
||||
// convert the rectangle into css pixels. When changing the size in a
|
||||
// direction, don't allow the new size to be less that the resizer's
|
||||
// size. This ensures that content isn't resized too small as to make
|
||||
// the resizer invisible.
|
||||
nsRect appUnitsRect = ToAppUnits(rect.ToUnknownRect(),
|
||||
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(AppUnitsPerCSSPixel());
|
||||
|
||||
LayoutDeviceIntRect oldRect;
|
||||
AutoWeakFrame weakFrame(menuPopupFrame);
|
||||
if (menuPopupFrame) {
|
||||
nsCOMPtr<nsIWidget> widget = menuPopupFrame->GetWidget();
|
||||
if (widget) oldRect = widget->GetScreenBounds();
|
||||
|
||||
// convert the new rectangle into outer window coordinates
|
||||
LayoutDeviceIntPoint clientOffset = widget->GetClientOffset();
|
||||
rect.x -= clientOffset.x;
|
||||
rect.y -= clientOffset.y;
|
||||
}
|
||||
|
||||
SizeInfo sizeInfo, originalSizeInfo;
|
||||
sizeInfo.width.AppendInt(cssRect.width);
|
||||
sizeInfo.height.AppendInt(cssRect.height);
|
||||
ResizeContent(contentToResize, direction, sizeInfo,
|
||||
&originalSizeInfo);
|
||||
MaybePersistOriginalSize(contentToResize, originalSizeInfo);
|
||||
|
||||
// Move the popup to the new location unless it is anchored, since
|
||||
// the position shouldn't change. nsMenuPopupFrame::SetPopupPosition
|
||||
// will instead ensure that the popup's position is anchored at the
|
||||
// right place.
|
||||
if (weakFrame.IsAlive() &&
|
||||
(oldRect.x != rect.x || oldRect.y != rect.y) &&
|
||||
(!menuPopupFrame->IsAnchored() ||
|
||||
menuPopupFrame->PopupLevel() != ePopupLevelParent)) {
|
||||
CSSPoint cssPos =
|
||||
rect.TopLeft() / aPresContext->CSSToDevPixelScale();
|
||||
menuPopupFrame->MoveTo(RoundedToInt(cssPos), true);
|
||||
}
|
||||
} else {
|
||||
window->SetPositionAndSize(
|
||||
rect.x, rect.y, rect.width, rect.height,
|
||||
nsIBaseWindow::eRepaint); // do the repaint.
|
||||
}
|
||||
|
||||
doDefault = false;
|
||||
}
|
||||
} break;
|
||||
|
||||
case eMouseClick: {
|
||||
WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
|
||||
if (mouseEvent->IsLeftClickEvent()) {
|
||||
MouseClicked(mouseEvent);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case eMouseDoubleClick:
|
||||
if (aEvent->AsMouseEvent()->mButton == MouseButton::ePrimary) {
|
||||
nsCOMPtr<nsIBaseWindow> window;
|
||||
mozilla::PresShell* presShell = aPresContext->GetPresShell();
|
||||
nsIContent* contentToResize =
|
||||
GetContentToResize(presShell, getter_AddRefs(window));
|
||||
if (contentToResize) {
|
||||
nsMenuPopupFrame* menuPopupFrame =
|
||||
do_QueryFrame(contentToResize->GetPrimaryFrame());
|
||||
if (menuPopupFrame)
|
||||
break; // Don't restore original sizing for menupopup frames until
|
||||
// we handle screen constraints here. (Bug 357725)
|
||||
|
||||
RestoreOriginalSize(contentToResize);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!doDefault) *aEventStatus = nsEventStatus_eConsumeNoDefault;
|
||||
|
||||
if (doDefault && weakFrame.IsAlive())
|
||||
return nsTitleBarFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsIContent* nsResizerFrame::GetContentToResize(mozilla::PresShell* aPresShell,
|
||||
nsIBaseWindow** aWindow) {
|
||||
*aWindow = nullptr;
|
||||
|
||||
nsAutoString elementid;
|
||||
mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::element,
|
||||
elementid);
|
||||
if (elementid.IsEmpty()) {
|
||||
// If the resizer is in a popup, resize the popup's widget, otherwise
|
||||
// resize the widget associated with the window.
|
||||
nsIFrame* popup = GetParent();
|
||||
while (popup) {
|
||||
nsMenuPopupFrame* popupFrame = do_QueryFrame(popup);
|
||||
if (popupFrame) {
|
||||
return popupFrame->GetContent();
|
||||
}
|
||||
popup = popup->GetParent();
|
||||
}
|
||||
|
||||
// don't allow resizing windows in content shells
|
||||
if (!aPresShell->GetPresContext()->IsChrome()) {
|
||||
// don't allow resizers in content shells, except for the viewport
|
||||
// scrollbar which doesn't have a parent
|
||||
nsIContent* nonNativeAnon =
|
||||
mContent->FindFirstNonChromeOnlyAccessContent();
|
||||
if (!nonNativeAnon || nonNativeAnon->GetParent()) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// get the document and the window - should this be cached?
|
||||
if (nsPIDOMWindowOuter* domWindow =
|
||||
aPresShell->GetDocument()->GetWindow()) {
|
||||
nsCOMPtr<nsIDocShell> docShell = domWindow->GetDocShell();
|
||||
if (docShell) {
|
||||
nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
|
||||
docShell->GetTreeOwner(getter_AddRefs(treeOwner));
|
||||
if (treeOwner) {
|
||||
CallQueryInterface(treeOwner, aWindow);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (elementid.EqualsLiteral("_parent")) {
|
||||
// return the parent, but skip over native anonymous content
|
||||
nsIContent* parent = mContent->GetParent();
|
||||
return parent ? parent->FindFirstNonChromeOnlyAccessContent() : nullptr;
|
||||
}
|
||||
|
||||
return aPresShell->GetDocument()->GetElementById(elementid);
|
||||
}
|
||||
|
||||
void nsResizerFrame::AdjustDimensions(int32_t* aPos, int32_t* aSize,
|
||||
int32_t aMinSize, int32_t aMaxSize,
|
||||
int32_t aMovement,
|
||||
int8_t aResizerDirection) {
|
||||
int32_t oldSize = *aSize;
|
||||
|
||||
*aSize += aResizerDirection * aMovement;
|
||||
// use one as a minimum size or the element could disappear
|
||||
if (*aSize < 1) *aSize = 1;
|
||||
|
||||
// Constrain the size within the minimum and maximum size.
|
||||
*aSize = std::max(aMinSize, std::min(aMaxSize, *aSize));
|
||||
|
||||
// For left and top resizers, the window must be moved left by the same
|
||||
// amount that the window was resized.
|
||||
if (aResizerDirection == -1) *aPos += oldSize - *aSize;
|
||||
}
|
||||
|
||||
/* static */
|
||||
void nsResizerFrame::ResizeContent(nsIContent* aContent,
|
||||
const Direction& aDirection,
|
||||
const SizeInfo& aSizeInfo,
|
||||
SizeInfo* aOriginalSizeInfo) {
|
||||
if (RefPtr<nsStyledElement> inlineStyleContent =
|
||||
nsStyledElement::FromNode(aContent)) {
|
||||
nsICSSDeclaration* decl = inlineStyleContent->Style();
|
||||
|
||||
if (aOriginalSizeInfo) {
|
||||
decl->GetPropertyValue("width"_ns, aOriginalSizeInfo->width);
|
||||
decl->GetPropertyValue("height"_ns, aOriginalSizeInfo->height);
|
||||
}
|
||||
|
||||
// only set the property if the element could have changed in that
|
||||
// direction
|
||||
if (aDirection.mHorizontal) {
|
||||
nsAutoCString widthstr(aSizeInfo.width);
|
||||
if (!widthstr.IsEmpty() &&
|
||||
!Substring(widthstr, widthstr.Length() - 2, 2).EqualsLiteral("px"))
|
||||
widthstr.AppendLiteral("px");
|
||||
decl->SetProperty("width"_ns, widthstr, ""_ns, IgnoreErrors());
|
||||
}
|
||||
if (aDirection.mVertical) {
|
||||
nsAutoCString heightstr(aSizeInfo.height);
|
||||
if (!heightstr.IsEmpty() &&
|
||||
!Substring(heightstr, heightstr.Length() - 2, 2).EqualsLiteral("px"))
|
||||
heightstr.AppendLiteral("px");
|
||||
decl->SetProperty("height"_ns, heightstr, ""_ns, IgnoreErrors());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
UniquePtr<SizeInfo> sizeInfo(new SizeInfo(aSizeInfo));
|
||||
rv = aContent->SetProperty(nsGkAtoms::_moz_original_size, sizeInfo.get(),
|
||||
nsINode::DeleteProperty<nsResizerFrame::SizeInfo>);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
Unused << sizeInfo.release();
|
||||
}
|
||||
}
|
||||
|
||||
/* 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, nullptr);
|
||||
aContent->RemoveProperty(nsGkAtoms::_moz_original_size);
|
||||
}
|
||||
|
||||
/* returns a Direction struct containing the horizontal and vertical direction
|
||||
*/
|
||||
nsResizerFrame::Direction nsResizerFrame::GetDirection() {
|
||||
static const mozilla::dom::Element::AttrValuesArray strings[] = {
|
||||
// clang-format off
|
||||
nsGkAtoms::topleft, nsGkAtoms::top, nsGkAtoms::topright,
|
||||
nsGkAtoms::left, nsGkAtoms::right,
|
||||
nsGkAtoms::bottomleft, nsGkAtoms::bottom, nsGkAtoms::bottomright,
|
||||
nsGkAtoms::bottomstart, nsGkAtoms::bottomend,
|
||||
nullptr
|
||||
// clang-format on
|
||||
};
|
||||
|
||||
static const Direction directions[] = {
|
||||
// clang-format off
|
||||
{-1, -1}, {0, -1}, {1, -1},
|
||||
{-1, 0}, {1, 0},
|
||||
{-1, 1}, {0, 1}, {1, 1},
|
||||
{-1, 1}, {1, 1}
|
||||
// clang-format on
|
||||
};
|
||||
|
||||
if (!GetContent()) {
|
||||
return directions[0]; // default: topleft
|
||||
}
|
||||
|
||||
int32_t index = mContent->AsElement()->FindAttrValueIn(
|
||||
kNameSpaceID_None, nsGkAtoms::dir, strings, eCaseMatters);
|
||||
if (index < 0) {
|
||||
return directions[0]; // default: topleft
|
||||
}
|
||||
|
||||
if (index >= 8) {
|
||||
// Directions 8 and higher are RTL-aware directions and should reverse the
|
||||
// horizontal component if RTL.
|
||||
WritingMode wm = GetWritingMode();
|
||||
if (wm.IsPhysicalRTL()) {
|
||||
Direction direction = directions[index];
|
||||
direction.mHorizontal *= -1;
|
||||
return direction;
|
||||
}
|
||||
}
|
||||
|
||||
return directions[index];
|
||||
}
|
||||
|
||||
void nsResizerFrame::MouseClicked(WidgetMouseEvent* aEvent) {
|
||||
// Execute the oncommand event handler.
|
||||
nsCOMPtr<nsIContent> content = mContent;
|
||||
nsContentUtils::DispatchXULCommand(content, false, nullptr, nullptr,
|
||||
aEvent->IsControl(), aEvent->IsAlt(),
|
||||
aEvent->IsShift(), aEvent->IsMeta(),
|
||||
aEvent->mInputSource, aEvent->mButton);
|
||||
}
|
||||
|
||||
@@ -6,78 +6,21 @@
|
||||
#ifndef nsResizerFrame_h___
|
||||
#define nsResizerFrame_h___
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/EventForwards.h"
|
||||
#include "nsTitleBarFrame.h"
|
||||
|
||||
class nsIBaseWindow;
|
||||
|
||||
namespace mozilla {
|
||||
class PresShell;
|
||||
} // namespace mozilla
|
||||
|
||||
class nsResizerFrame final : public nsTitleBarFrame {
|
||||
protected:
|
||||
typedef mozilla::LayoutDeviceIntPoint LayoutDeviceIntPoint;
|
||||
typedef mozilla::LayoutDeviceIntRect LayoutDeviceIntRect;
|
||||
|
||||
struct Direction {
|
||||
int8_t mHorizontal;
|
||||
int8_t mVertical;
|
||||
};
|
||||
|
||||
public:
|
||||
NS_DECL_FRAMEARENA_HELPERS(nsResizerFrame)
|
||||
|
||||
friend nsIFrame* NS_NewResizerFrame(mozilla::PresShell* aPresShell,
|
||||
ComputedStyle* aStyle);
|
||||
|
||||
explicit nsResizerFrame(ComputedStyle* aStyle, nsPresContext* aPresContext);
|
||||
nsResizerFrame(ComputedStyle* aStyle, nsPresContext* aPresContext);
|
||||
|
||||
virtual nsresult HandleEvent(nsPresContext* aPresContext,
|
||||
mozilla::WidgetGUIEvent* aEvent,
|
||||
nsEventStatus* aEventStatus) override;
|
||||
|
||||
MOZ_CAN_RUN_SCRIPT_BOUNDARY
|
||||
virtual void MouseClicked(mozilla::WidgetMouseEvent* aEvent) override;
|
||||
|
||||
protected:
|
||||
nsIContent* GetContentToResize(mozilla::PresShell* aPresShell,
|
||||
nsIBaseWindow** aWindow);
|
||||
|
||||
Direction GetDirection();
|
||||
|
||||
/**
|
||||
* Adjust the window position and size in a direction according to the mouse
|
||||
* movement and the resizer direction. The minimum and maximum size is used
|
||||
* to constrain the size.
|
||||
*
|
||||
* @param aPos left or top position
|
||||
* @param aSize width or height
|
||||
* @param aMinSize minimum width or height
|
||||
* @param aMacSize maximum width or height
|
||||
* @param aMovement the amount the mouse was moved
|
||||
* @param aResizerDirection resizer direction returned by GetDirection
|
||||
*/
|
||||
static void AdjustDimensions(int32_t* aPos, int32_t* aSize, int32_t aMinSize,
|
||||
int32_t aMaxSize, int32_t aMovement,
|
||||
int8_t aResizerDirection);
|
||||
|
||||
struct SizeInfo {
|
||||
nsCString width, height;
|
||||
};
|
||||
static void SizeInfoDtorFunc(void* aObject, nsAtom* 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:
|
||||
LayoutDeviceIntRect mMouseDownRect;
|
||||
LayoutDeviceIntPoint mMouseDownPoint;
|
||||
}; // class nsResizerFrame
|
||||
|
||||
#endif /* nsResizerFrame_h___ */
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
[DEFAULT]
|
||||
skip-if = os == 'android'
|
||||
support-files =
|
||||
window_resizer.xhtml
|
||||
window_resizer_element.xhtml
|
||||
windowminmaxsize1.xhtml
|
||||
windowminmaxsize2.xhtml
|
||||
windowminmaxsize3.xhtml
|
||||
@@ -29,8 +27,6 @@ skip-if = os == 'linux' # No native mousedown event on Linux
|
||||
[test_popupReflowPos.xhtml]
|
||||
[test_popupSizeTo.xhtml]
|
||||
[test_popupZoom.xhtml]
|
||||
[test_resizer.xhtml]
|
||||
skip-if = (verify && (os == 'win'))
|
||||
[test_submenuClose.xhtml]
|
||||
[test_windowminmaxsize.xhtml]
|
||||
[test_resizer_ctrl_click.xhtml]
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
|
||||
<!--
|
||||
XUL <resizer> tests
|
||||
-->
|
||||
<window title="XUL resizer tests"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
|
||||
<script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
|
||||
|
||||
<!-- test results are displayed in the html:body -->
|
||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
||||
</body>
|
||||
|
||||
<!-- test code goes here -->
|
||||
<script type="application/javascript"><![CDATA[
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SimpleTest.ignoreAllUncaughtExceptions();
|
||||
|
||||
function openPopup()
|
||||
{
|
||||
document.getElementById("panel").
|
||||
openPopupAtScreen(Math.round(window.mozInnerScreenX) + window.innerWidth - 130,
|
||||
Math.round(window.mozInnerScreenY) + window.innerHeight - 130);
|
||||
}
|
||||
|
||||
var step = 0;
|
||||
function popupShown(event)
|
||||
{
|
||||
let panel = document.getElementById("panel");
|
||||
if (step == 0) {
|
||||
// check to make sure that the popup cannot be resized past the edges of
|
||||
// the content area
|
||||
var resizerrect = document.getElementById("resizer").getBoundingClientRect();
|
||||
synthesizeMouse(document.documentElement, resizerrect.left + 5, resizerrect.top + 5, { type:"mousedown" });
|
||||
synthesizeMouse(document.documentElement, resizerrect.left + 2000, resizerrect.top + 2000, { type:"mousemove" });
|
||||
|
||||
// allow a one pixel variance as rounding is always done to the inside
|
||||
// of a rectangle.
|
||||
var popuprect = panel.getBoundingClientRect();
|
||||
ok(Math.round(popuprect.right) == window.innerWidth ||
|
||||
Math.round(popuprect.right) == window.innerWidth - 1,
|
||||
"resized to content edge width");
|
||||
ok(Math.round(popuprect.bottom) == window.innerHeight ||
|
||||
Math.round(popuprect.bottom) == window.innerHeight - 1,
|
||||
"resized to content edge height");
|
||||
|
||||
resizerrect = document.getElementById("resizer").getBoundingClientRect();
|
||||
synthesizeMouse(document.documentElement, resizerrect.left + 5, resizerrect.top + 5, { type:"mouseup" });
|
||||
}
|
||||
else {
|
||||
// the popup is opened twice. Make sure that for the second time, the
|
||||
// resized popup opens in the same direction as there should still be
|
||||
// room for it
|
||||
var popuprect = panel.getBoundingClientRect();
|
||||
var marginLeft = parseFloat(getComputedStyle(panel).marginLeft);
|
||||
var marginTop = parseFloat(getComputedStyle(panel).marginTop);
|
||||
is(Math.round(popuprect.left - marginLeft), window.innerWidth - 130, "reopen popup left");
|
||||
is(Math.round(popuprect.top - marginTop), window.innerHeight - 130, "reopen popup top");
|
||||
}
|
||||
|
||||
event.target.hidePopup();
|
||||
}
|
||||
|
||||
function doResizerWindowTests() {
|
||||
step++;
|
||||
if (step == 1) {
|
||||
openPopup();
|
||||
return;
|
||||
}
|
||||
|
||||
if (/Mac/.test(navigator.platform)) {
|
||||
window.openDialog("window_resizer.xhtml", "_blank", "left=200,top=200,outerWidth=300,outerHeight=300,chrome,noopener", window);
|
||||
}
|
||||
else {
|
||||
// Skip window_resizer.xhtml tests.
|
||||
todo(false, "We can't test GTK and Windows native drag resizing implementations.");
|
||||
// Run window_resizer_element.xhtml test only.
|
||||
lastResizerTest();
|
||||
}
|
||||
}
|
||||
|
||||
function lastResizerTest()
|
||||
{
|
||||
window.openDialog("window_resizer_element.xhtml", "_blank", "left=200,top=200,outerWidth=300,outerHeight=300,chrome,noopener", window);
|
||||
}
|
||||
|
||||
SimpleTest.waitForFocus(openPopup);
|
||||
]]></script>
|
||||
|
||||
<panel id="panel" onpopupshown="popupShown(event)" onpopuphidden="doResizerWindowTests()">
|
||||
<resizer id="resizer" dir="bottomend" width="16" height="16"/>
|
||||
<hbox width="50" height="50" flex="1"/>
|
||||
</panel>
|
||||
|
||||
</window>
|
||||
@@ -22,7 +22,7 @@
|
||||
<panel id="panel" onpopupshown="doPanelTest(this)" onpopuphidden="nextPopupTest(this)"
|
||||
orient="vertical"
|
||||
align="start" pack="start" style="appearance: none; margin: 0; border: 0; padding: 0;">
|
||||
<resizer id="popupresizer" dir="bottomright" flex="1" width="60" height="60"
|
||||
<hbox id="popupresizer" dir="bottomright" flex="1" width="60" height="60"
|
||||
style="appearance: none; margin: 0; border: 0; padding: 0;"/>
|
||||
</panel>
|
||||
|
||||
@@ -80,10 +80,6 @@ var popupTests = [
|
||||
{ testname: "popup with maximum size",
|
||||
maxwidth: 50, maxheight: 45,
|
||||
width: 50, height: 45,
|
||||
},
|
||||
{ testname: "popup with minimum and size",
|
||||
minwidth: 80, minheight: 70, maxwidth: 250, maxheight: 220,
|
||||
width: 80, height: 70, last: true
|
||||
}
|
||||
];
|
||||
|
||||
@@ -155,24 +151,6 @@ function doPanelTest(panel)
|
||||
is(rect.width, popupTests[gTestId].width, popupTests[gTestId].testname + " width");
|
||||
is(rect.height, popupTests[gTestId].height, popupTests[gTestId].testname + " height");
|
||||
|
||||
if ('last' in popupTests[gTestId]) {
|
||||
var resizer = document.getElementById("popupresizer");
|
||||
synthesizeMouse(resizer, 4, 4, { type:"mousedown" });
|
||||
synthesizeMouse(resizer, 800, 800, { type:"mousemove" });
|
||||
|
||||
rect = panel.getBoundingClientRect();
|
||||
is(rect.width, 250, "Popup width after maximum resize");
|
||||
is(rect.height, 220, "Popup height after maximum resize");
|
||||
|
||||
synthesizeMouse(resizer, -100, -100, { type:"mousemove" });
|
||||
|
||||
rect = panel.getBoundingClientRect();
|
||||
is(rect.width, 80, "Popup width after minimum resize");
|
||||
is(rect.height, 70, "Popup height after minimum resize");
|
||||
|
||||
synthesizeMouse(resizer, 4, 4, { type:"mouseup" });
|
||||
}
|
||||
|
||||
panel.hidePopup();
|
||||
}
|
||||
|
||||
@@ -196,11 +174,6 @@ function nextPopupTest(panel)
|
||||
setattr("maxwidth");
|
||||
setattr("maxheight");
|
||||
|
||||
// Remove the flexibility as it causes the resizer to not shrink down
|
||||
// when resizing.
|
||||
if ("last" in popupTests[gTestId])
|
||||
document.getElementById("popupresizer").removeAttribute("flex");
|
||||
|
||||
// Prevent event loop starvation as a result of popup events being
|
||||
// synchronous. See bug 1131576.
|
||||
SimpleTest.executeSoon(() => {
|
||||
|
||||
@@ -1,113 +0,0 @@
|
||||
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
|
||||
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
screenX="200" screenY="200" width="300" height="300"
|
||||
onload="setTimeout(doTest, 0)">
|
||||
<script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script><![CDATA[
|
||||
var is = window.arguments[0].SimpleTest.is;
|
||||
|
||||
function doTest() {
|
||||
// from test_resizer.xhtml
|
||||
var expectX = 200;
|
||||
var expectY = 200;
|
||||
var expectXMost = 500;
|
||||
var expectYMost = 500;
|
||||
var screenScale = expectX/window.screenX;
|
||||
var root = document.documentElement;
|
||||
|
||||
var oldScreenX = window.screenX;
|
||||
var oldScreenY = window.screenY;
|
||||
var oldWidth = window.outerWidth;
|
||||
var oldHeight = window.outerHeight;
|
||||
|
||||
function testResizer(dx, dy) {
|
||||
var offset = 20;
|
||||
var scale = 5;
|
||||
// target the centre of the resizer
|
||||
var offsetX = window.innerWidth/2 + (window.innerWidth/3)*dx;
|
||||
var offsetY = window.innerHeight/2 + (window.innerHeight/3)*dy;
|
||||
|
||||
for (var mouseX = -1; mouseX <= 1; ++mouseX) {
|
||||
for (var mouseY = -1; mouseY <= 1; ++mouseY) {
|
||||
var newExpectX = expectX;
|
||||
var newExpectXMost = expectXMost;
|
||||
var newExpectY = expectY;
|
||||
var newExpectYMost = expectYMost;
|
||||
if (dx < 0) {
|
||||
newExpectX += mouseX*scale;
|
||||
} else if (dx > 0) {
|
||||
newExpectXMost += mouseX*scale;
|
||||
}
|
||||
if (dy < 0) {
|
||||
newExpectY += mouseY*scale;
|
||||
} else if (dy > 0) {
|
||||
newExpectYMost += mouseY*scale;
|
||||
}
|
||||
|
||||
synthesizeMouse(root, offsetX, offsetY, { type:"mousedown" });
|
||||
synthesizeMouse(root, offsetX + mouseX*scale, offsetY + mouseY*scale, { type:"mousemove" });
|
||||
is(window.screenX*screenScale, newExpectX,
|
||||
"Bad x for " + dx + "," + dy + " moving " + mouseX + "," + mouseY);
|
||||
is(window.screenY*screenScale, newExpectY,
|
||||
"Bad y for " + dx + "," + dy + " moving " + mouseX + "," + mouseY);
|
||||
is(window.outerWidth, newExpectXMost - newExpectX,
|
||||
"Bad width for " + dx + "," + dy + " moving " + mouseX + "," + mouseY);
|
||||
is(window.outerHeight, newExpectYMost - newExpectY,
|
||||
"Bad height for " + dx + "," + dy + " moving " + mouseX + "," + mouseY);
|
||||
|
||||
// move it back before we release! Adjust for any window movement
|
||||
synthesizeMouse(root, offsetX - (newExpectX - expectX),
|
||||
offsetY - (newExpectY - expectY), { type:"mousemove" });
|
||||
synthesizeMouse(root, offsetX, offsetY, { type:"mouseup" });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
testResizer(-1, -1);
|
||||
testResizer(-1, 0);
|
||||
testResizer(-1, 1);
|
||||
testResizer(0, -1);
|
||||
testResizer(0, 1);
|
||||
testResizer(1, -1);
|
||||
testResizer(1, 0);
|
||||
testResizer(1, 1);
|
||||
|
||||
var resizers = document.getElementsByTagName("resizer");
|
||||
Array.prototype.forEach.call(resizers, function (element) {
|
||||
is(getComputedStyle(element, "").cursor,
|
||||
element.getAttribute("expectedcursor"),
|
||||
"cursor for " + element.getAttribute("dir"));
|
||||
});
|
||||
|
||||
// now check the cursors in rtl. The bottomend resizer
|
||||
// should be reversed
|
||||
document.documentElement.setAttribute("localedir", "rtl");
|
||||
Array.prototype.forEach.call(resizers, function (element) {
|
||||
is(getComputedStyle(element, "").cursor,
|
||||
element.getAttribute("dir") == "bottomend" ? "sw-resize" :
|
||||
element.getAttribute("expectedcursor"),
|
||||
"cursor for " + element.getAttribute("dir"));
|
||||
});
|
||||
|
||||
window.close();
|
||||
window.arguments[0].lastResizerTest();
|
||||
}
|
||||
]]></script>
|
||||
<hbox id="container" flex="1">
|
||||
<vbox flex="1">
|
||||
<resizer dir="topleft" expectedcursor="nw-resize" flex="1"/>
|
||||
<resizer dir="left" expectedcursor="ew-resize" flex="1"/>
|
||||
<resizer dir="bottomleft" expectedcursor="sw-resize" flex="1"/>
|
||||
</vbox>
|
||||
<vbox flex="1">
|
||||
<resizer dir="top" expectedcursor="ns-resize" flex="1"/>
|
||||
<resizer id="bottomend" dir="bottomend" expectedcursor="se-resize" flex="1"/>
|
||||
<resizer dir="bottom" expectedcursor="ns-resize" flex="1"/>
|
||||
</vbox>
|
||||
<vbox flex="1">
|
||||
<resizer dir="topright" expectedcursor="ne-resize" flex="1"/>
|
||||
<resizer dir="right" expectedcursor="ew-resize" flex="1"/>
|
||||
<resizer dir="bottomright" expectedcursor="se-resize" flex="1"/>
|
||||
</vbox>
|
||||
</hbox>
|
||||
</window>
|
||||
@@ -1,190 +0,0 @@
|
||||
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
|
||||
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
align="start">
|
||||
<script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script><![CDATA[
|
||||
var is = window.arguments[0].SimpleTest.is;
|
||||
window.onerror = window.arguments[0].onerror;
|
||||
|
||||
const anchorPositions =
|
||||
[ "before_start", "before_end", "after_start", "after_end",
|
||||
"start_before", "start_after", "end_before", "end_after", "overlap", "screen"];
|
||||
var currentPosition;
|
||||
|
||||
function testResizer(resizerid, noShrink, hResize, vResize, testid)
|
||||
{
|
||||
var rect = document.getElementById(resizerid + "-container").getBoundingClientRect();
|
||||
var resizer = document.getElementById(resizerid);
|
||||
var resizerrect = resizer.getBoundingClientRect();
|
||||
|
||||
var originalX = resizerrect.left;
|
||||
var originalY = resizerrect.top;
|
||||
|
||||
const scale = 20;
|
||||
for (var mouseX = -1; mouseX <= 1; ++mouseX) {
|
||||
for (var mouseY = -1; mouseY <= 1; ++mouseY) {
|
||||
var expectedWidth = rect.width + hResize * mouseX * scale;
|
||||
var expectedHeight = rect.height + vResize * mouseY * scale;
|
||||
|
||||
if (noShrink) {
|
||||
if (mouseX == -1)
|
||||
expectedWidth = rect.width;
|
||||
if (mouseY == -1)
|
||||
expectedHeight = rect.height;
|
||||
}
|
||||
|
||||
synthesizeMouse(document.documentElement, originalX + 5, originalY + 5, { type:"mousedown" });
|
||||
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);
|
||||
// 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" });
|
||||
synthesizeMouse(resizer, 10, 10, { type:"mousemove" });
|
||||
synthesizeMouse(resizer, 5, 5, { type:"mouseup" });
|
||||
|
||||
testResizer("outside", true, 1, 1, "");
|
||||
testResizer("html", true, 1, 1, "");
|
||||
testResizer("inside", true, 1, 1, "");
|
||||
testResizer("inside-large", false, 1, 1, "");
|
||||
testResizer("inside-with-border", true, 1, 1, "");
|
||||
|
||||
document.getElementById("inside-popup-container").
|
||||
openPopupAtScreen(Math.ceil(window.mozInnerScreenX) + 100, Math.ceil(window.mozInnerScreenY) + 100);
|
||||
}
|
||||
|
||||
function popupShown(event)
|
||||
{
|
||||
testResizer("inside-popup", false, 1, 1, "");
|
||||
document.getElementById("inside-popup-container").id = "outside-popup-container";
|
||||
testResizer("outside-popup", false, 1, 1, "");
|
||||
|
||||
var resizerrect = document.getElementById("inside-popup").getBoundingClientRect();
|
||||
synthesizeMouse(document.documentElement, resizerrect.left + 5, resizerrect.top + 5, { type:"mousedown" });
|
||||
synthesizeMouse(document.documentElement, resizerrect.left + 2000, resizerrect.top + 2000, { type:"mousemove" });
|
||||
|
||||
var isMac = (navigator.platform.includes("Mac"));
|
||||
var popuprect = document.getElementById("outside-popup-container").getBoundingClientRect();
|
||||
// subtract 3 due to space left for panel dropshadow
|
||||
is(Math.ceil(window.mozInnerScreenX) + popuprect.right,
|
||||
(isMac ? screen.availLeft + screen.availWidth : screen.left + screen.width) - 3, "resized to edge width");
|
||||
is(Math.ceil(window.mozInnerScreenY) + popuprect.bottom,
|
||||
(isMac ? screen.availTop + screen.availHeight : screen.top + screen.height) - 3, "resized to edge height");
|
||||
|
||||
resizerrect = document.getElementById("inside-popup").getBoundingClientRect();
|
||||
synthesizeMouse(document.documentElement, resizerrect.left + 5, resizerrect.top + 5, { type:"mouseup" });
|
||||
|
||||
event.target.hidePopup();
|
||||
}
|
||||
|
||||
function popupHidden()
|
||||
{
|
||||
if (anchorPositions.length == 0) {
|
||||
window.close();
|
||||
window.arguments[0].SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
|
||||
currentPosition = anchorPositions.shift();
|
||||
var anchor = document.getElementById("anchor");
|
||||
var popup = document.getElementById("anchored-panel-container");
|
||||
|
||||
if (currentPosition == "screen")
|
||||
popup.openPopupAtScreen(window.screenX + 100, window.screenY + 100);
|
||||
else
|
||||
popup.openPopup(anchor, currentPosition);
|
||||
}
|
||||
|
||||
function anchoredPopupShown(event)
|
||||
{
|
||||
var leftAllowed = (!currentPosition.includes("end_") && !currentPosition.includes("_start"));
|
||||
var rightAllowed = (!currentPosition.includes("start_") && !currentPosition.includes("_end"));
|
||||
var topAllowed = (!currentPosition.includes("after_") && !currentPosition.includes("_before"));
|
||||
var bottomAllowed = (!currentPosition.includes("before_") && !currentPosition.includes("_after"));
|
||||
|
||||
if (currentPosition == "overlap") {
|
||||
leftAllowed = topAllowed = false;
|
||||
rightAllowed = bottomAllowed = true;
|
||||
}
|
||||
|
||||
var resizerTypes = [ "topleft", "top", "topright", "left", "right",
|
||||
"bottomleft", "bottom", "bottomright", "bottomend" ];
|
||||
for (var r = 0; r < resizerTypes.length; r++) {
|
||||
var resizerType = resizerTypes[r];
|
||||
var horiz = 0, vert = 0;
|
||||
if (leftAllowed && resizerType.includes("left")) horiz = -1;
|
||||
else if (rightAllowed && (resizerType.includes("right") || resizerType == "bottomend")) horiz = 1;
|
||||
|
||||
if (topAllowed && resizerType.includes("top")) vert = -1;
|
||||
else if (bottomAllowed && resizerType.includes("bottom")) vert = 1;
|
||||
|
||||
document.getElementById("anchored-panel").dir = resizerType;
|
||||
testResizer("anchored-panel", false, horiz, vert, currentPosition + " " + resizerType);
|
||||
}
|
||||
|
||||
event.target.hidePopup();
|
||||
}
|
||||
|
||||
SimpleTest.executeSoon(() => {
|
||||
window.arguments[0].SimpleTest.waitForFocus(doTest, window);
|
||||
});
|
||||
]]></script>
|
||||
|
||||
<resizer id="outside" dir="bottomend" element="outside-container"/>
|
||||
<resizer id="notfound" dir="bottomend" element="nothing"/>
|
||||
<hbox id="outside-container">
|
||||
<hbox minwidth="46" minheight="39"/>
|
||||
</hbox>
|
||||
<html:div id="html-container" xmlns:html="http://www.w3.org/1999/xhtml">
|
||||
<html:button>One</html:button><html:br/>
|
||||
<resizer id="html" dir="bottomend" element="_parent"/>
|
||||
</html:div>
|
||||
<hbox id="anchor" align="start" style="margin-left: 100px;">
|
||||
<hbox id="inside-container" align="start">
|
||||
<hbox minwidth="45" minheight="41"/>
|
||||
<resizer id="inside" dir="bottomend" element="_parent"/>
|
||||
</hbox>
|
||||
<hbox id="inside-large-container" width="70" height="70" align="start">
|
||||
<resizer id="inside-large" dir="bottomend" element="_parent"/>
|
||||
</hbox>
|
||||
<hbox id="inside-with-border-container" style="border: 5px solid red; padding: 2px; margin: 2px;" align="start">
|
||||
<hbox minwidth="35" minheight="30"/>
|
||||
<resizer id="inside-with-border" dir="bottomend" element="_parent"/>
|
||||
</hbox>
|
||||
</hbox>
|
||||
|
||||
<panel id="inside-popup-container" align="start" onpopupshown="popupShown(event)" onpopuphidden="popupHidden()">
|
||||
<resizer id="inside-popup" dir="bottomend"/>
|
||||
<hbox width="50" height="50" flex="1"/>
|
||||
</panel>
|
||||
<resizer id="outside-popup" dir="bottomend" element="outside-popup-container"/>
|
||||
|
||||
<panel id="anchored-panel-container" align="start" onpopupshown="anchoredPopupShown(event)"
|
||||
onpopuphidden="popupHidden()">
|
||||
<hbox width="50" height="50" flex="1"/>
|
||||
<resizer id="anchored-panel" width="20" height="20"/>
|
||||
</panel>
|
||||
|
||||
</window>
|
||||
Reference in New Issue
Block a user