Backed out 4 changesets (bug 1694056, bug 1590376) for causing failures at test_resizer_ctrl_click.xhtml. CLOSED TREE

Backed out changeset 656906f99173 (bug 1590376)
Backed out changeset 15e9b4e9070c (bug 1694056)
Backed out changeset f137d6e73641 (bug 1590376)
Backed out changeset 00ed65f43cb2 (bug 1590376)
This commit is contained in:
Butkovits Atila 2022-06-07 02:12:40 +03:00
Родитель a49aa34d6d
Коммит e3b4113a08
24 изменённых файлов: 1523 добавлений и 525 удалений

Просмотреть файл

@ -71,7 +71,6 @@
#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"
@ -3839,8 +3838,6 @@ 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 ||

Просмотреть файл

@ -1,10 +0,0 @@
/* -*- 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,7 +1012,6 @@ WEBIDL_FILES = [
"XULCommandEvent.webidl",
"XULElement.webidl",
"XULPopupElement.webidl",
"XULResizerElement.webidl",
]
if CONFIG["MOZ_DOM_STREAMS"]:

Просмотреть файл

@ -1,472 +0,0 @@
/* -*- 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()
#ifdef XP_MACOSX
// On Mac, ctrl-click will send a context menu event from the widget,
// so we don't want to dispatch widget command if it is redispatched
// from the mouse event with ctrl key is pressed.
&& !mouseEvent->IsControl()
#endif
) {
// Execute the oncommand event handler.
nsContentUtils::DispatchXULCommand(
this, false, nullptr, nullptr, mouseEvent->IsControl(),
mouseEvent->IsAlt(), mouseEvent->IsShift(), mouseEvent->IsMeta(),
mouseEvent->mInputSource);
}
} 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

Просмотреть файл

@ -26,7 +26,6 @@ EXPORTS.mozilla.dom += [
"XULMenuElement.h",
"XULPersist.h",
"XULPopupElement.h",
"XULResizerElement.h",
"XULTextElement.h",
"XULTooltipElement.h",
"XULTreeElement.h",
@ -46,7 +45,6 @@ UNIFIED_SOURCES += [
"XULMenuElement.cpp",
"XULPersist.cpp",
"XULPopupElement.cpp",
"XULResizerElement.cpp",
"XULTextElement.cpp",
"XULTooltipElement.cpp",
"XULTreeElement.cpp",

Просмотреть файл

@ -14,7 +14,6 @@
#include "XULFrameElement.h"
#include "XULMenuElement.h"
#include "XULPopupElement.h"
#include "XULResizerElement.h"
#include "XULTextElement.h"
#include "XULTooltipElement.h"
#include "XULTreeElement.h"
@ -156,10 +155,6 @@ 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();

Просмотреть файл

@ -242,6 +242,10 @@ nsIFrame* NS_NewMenuBarFrame(PresShell* aPresShell, ComputedStyle* aStyle);
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);
@ -4122,6 +4126,8 @@ nsCSSFrameConstructor::FindXULTagData(const Element& aElement,
SCROLLABLE_XUL_CREATE(thumb, NS_NewButtonBoxFrame),
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),

Просмотреть файл

@ -78,6 +78,7 @@ FRAME_CLASSES = [
Frame("nsPopupSetFrame", "PopupSet", NOT_LEAF),
Frame("nsProgressFrame", "Progress", LEAF),
Frame("nsRangeFrame", "Range", LEAF),
Frame("nsResizerFrame", "Box", NOT_LEAF),
Frame("nsRootBoxFrame", "XULRoot", NOT_LEAF),
Frame("nsRubyBaseContainerFrame", "RubyBaseContainer", NOT_LEAF),
Frame("nsRubyBaseFrame", "RubyBase", NOT_LEAF),
@ -133,6 +134,7 @@ FRAME_CLASSES = [
Frame("nsTextBoxFrame", "LeafBox", LEAF),
Frame("nsTextControlFrame", "TextInput", LEAF),
Frame("nsTextFrame", "Text", LEAF),
Frame("nsTitleBarFrame", "Box", NOT_LEAF),
Frame("nsTreeBodyFrame", "LeafBox", LEAF),
Frame("nsTreeColFrame", "Box", NOT_LEAF),
Frame("nsVideoFrame", "HTMLVideo", NOT_LEAF),

Просмотреть файл

@ -0,0 +1,10 @@
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<xul:titlebar id="a"/>
<script>
var html = document.firstChild;
var a = document.getElementById('a')
document.removeChild(html)
document.appendChild(a)
</script>
</html>

Просмотреть файл

@ -11,6 +11,7 @@ load chrome://reftest/content/crashtests/layout/xul/crashtests/291702-2.xhtml
load chrome://reftest/content/crashtests/layout/xul/crashtests/291702-3.xhtml
load chrome://reftest/content/crashtests/layout/xul/crashtests/294371-1.xhtml
load 311457-1.html
load 321056-1.xhtml
load chrome://reftest/content/crashtests/layout/xul/crashtests/322786-1.xhtml
skip-if(Android) load chrome://reftest/content/crashtests/layout/xul/crashtests/325377.xhtml
skip-if(Android) load chrome://reftest/content/crashtests/layout/xul/crashtests/326879-1.xhtml

Просмотреть файл

@ -37,6 +37,7 @@ UNIFIED_SOURCES += [
"nsMenuPopupFrame.cpp",
"nsPopupSetFrame.cpp",
"nsRepeatService.cpp",
"nsResizerFrame.cpp",
"nsRootBoxFrame.cpp",
"nsScrollbarButtonFrame.cpp",
"nsScrollbarFrame.cpp",
@ -45,6 +46,7 @@ UNIFIED_SOURCES += [
"nsSprocketLayout.cpp",
"nsStackLayout.cpp",
"nsTextBoxFrame.cpp",
"nsTitleBarFrame.cpp",
"nsXULPopupManager.cpp",
"nsXULTooltipListener.cpp",
]

Просмотреть файл

@ -0,0 +1,521 @@
/* -*- 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 "nsCOMPtr.h"
#include "nsResizerFrame.h"
#include "nsIContent.h"
#include "mozilla/PresShell.h"
#include "mozilla/dom/Document.h"
#include "mozilla/UniquePtr.h"
#include "nsGkAtoms.h"
#include "nsNameSpaceManager.h"
#include "nsPresContext.h"
#include "nsFrameManager.h"
#include "nsDocShell.h"
#include "nsIDocShellTreeOwner.h"
#include "nsIBaseWindow.h"
#include "nsPIDOMWindow.h"
#include "mozilla/MouseEvents.h"
#include "nsContentUtils.h"
#include "nsMenuPopupFrame.h"
#include "nsServiceManagerUtils.h"
#include "nsIScreenManager.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/MouseEventBinding.h"
#include "nsError.h"
#include "nsICSSDeclaration.h"
#include "nsStyledElement.h"
#include <algorithm>
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
//
// Creates a new Resizer frame and returns it
//
nsIFrame* NS_NewResizerFrame(PresShell* aPresShell, ComputedStyle* aStyle) {
return new (aPresShell) nsResizerFrame(aStyle, aPresShell->GetPresContext());
}
NS_IMPL_FRAMEARENA_HELPERS(nsResizerFrame)
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);
}

Просмотреть файл

@ -3,43 +3,49 @@
/* 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 nsResizerFrame_h___
#define nsResizerFrame_h___
#ifndef mozilla_dom_XULResizerElement_h
#define mozilla_dom_XULResizerElement_h
#include "mozilla/Attributes.h"
#include "mozilla/EventForwards.h"
#include "nsTitleBarFrame.h"
#include "nsXULElement.h"
#include "Units.h"
class nsIBaseWindow;
namespace mozilla {
namespace dom {
class PresShell;
} // namespace mozilla
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&);
class nsResizerFrame final : public nsTitleBarFrame {
protected:
typedef mozilla::LayoutDeviceIntPoint LayoutDeviceIntPoint;
typedef mozilla::LayoutDeviceIntRect LayoutDeviceIntRect;
struct Direction {
int8_t mHorizontal;
int8_t mVertical;
};
Direction GetDirection();
nsIContent* GetContentToResize(nsIBaseWindow** aWindow);
public:
NS_DECL_FRAMEARENA_HELPERS(nsResizerFrame)
friend nsIFrame* NS_NewResizerFrame(mozilla::PresShell* aPresShell,
ComputedStyle* aStyle);
explicit 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
@ -69,12 +75,9 @@ class XULResizerElement final : public nsXULElement {
const SizeInfo& aSizeInfo);
static void RestoreOriginalSize(nsIContent* aContent);
protected:
LayoutDeviceIntRect mMouseDownRect;
LayoutDeviceIntPoint mMouseDownPoint;
bool mTrackingMouseMove = false;
};
}; // class nsResizerFrame
} // namespace dom
} // namespace mozilla
#endif // XULResizerElement_h
#endif /* nsResizerFrame_h___ */

Просмотреть файл

@ -0,0 +1,160 @@
/* -*- 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 "nsCOMPtr.h"
#include "nsTitleBarFrame.h"
#include "nsIContent.h"
#include "mozilla/dom/Document.h"
#include "nsGkAtoms.h"
#include "nsIWidget.h"
#include "nsMenuPopupFrame.h"
#include "nsPresContext.h"
#include "nsIDocShell.h"
#include "nsPIDOMWindow.h"
#include "nsDisplayList.h"
#include "nsContentUtils.h"
#include "mozilla/MouseEvents.h"
#include "mozilla/PresShell.h"
#include "mozilla/dom/MouseEventBinding.h"
using namespace mozilla;
//
// NS_NewTitleBarFrame
//
// Creates a new TitleBar frame and returns it
//
nsIFrame* NS_NewTitleBarFrame(PresShell* aPresShell, ComputedStyle* aStyle) {
return new (aPresShell) nsTitleBarFrame(aStyle, aPresShell->GetPresContext());
}
NS_IMPL_FRAMEARENA_HELPERS(nsTitleBarFrame)
nsTitleBarFrame::nsTitleBarFrame(ComputedStyle* aStyle,
nsPresContext* aPresContext, ClassID aID)
: nsBoxFrame(aStyle, aPresContext, aID, false) {
mTrackingMouseMove = false;
}
void nsTitleBarFrame::BuildDisplayListForChildren(
nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists) {
// override, since we don't want children to get events
if (aBuilder->IsForEventDelivery()) {
if (!mContent->AsElement()->AttrValueIs(kNameSpaceID_None,
nsGkAtoms::allowevents,
nsGkAtoms::_true, eCaseMatters))
return;
}
nsBoxFrame::BuildDisplayListForChildren(aBuilder, aLists);
}
nsresult nsTitleBarFrame::HandleEvent(nsPresContext* aPresContext,
WidgetGUIEvent* aEvent,
nsEventStatus* aEventStatus) {
NS_ENSURE_ARG_POINTER(aEventStatus);
if (nsEventStatus_eConsumeNoDefault == *aEventStatus) {
return NS_OK;
}
bool doDefault = true;
switch (aEvent->mMessage) {
case eMouseDown: {
if (aEvent->AsMouseEvent()->mButton == MouseButton::ePrimary) {
// titlebar has no effect in non-chrome shells
if (aPresContext->IsChrome()) {
// we're tracking.
mTrackingMouseMove = true;
// start capture.
PresShell::SetCapturingContent(GetContent(),
CaptureFlags::IgnoreAllowedState);
// remember current mouse coordinates.
mLastPoint = aEvent->mRefPoint;
}
*aEventStatus = nsEventStatus_eConsumeNoDefault;
doDefault = false;
}
} break;
case eMouseUp: {
if (mTrackingMouseMove &&
aEvent->AsMouseEvent()->mButton == MouseButton::ePrimary) {
// we're done tracking.
mTrackingMouseMove = false;
// end capture
PresShell::ReleaseCapturingContent();
*aEventStatus = nsEventStatus_eConsumeNoDefault;
doDefault = false;
}
} break;
case eMouseMove: {
if (mTrackingMouseMove) {
LayoutDeviceIntPoint nsMoveBy = aEvent->mRefPoint - mLastPoint;
nsIFrame* parent = GetParent();
while (parent) {
nsMenuPopupFrame* popupFrame = do_QueryFrame(parent);
if (popupFrame) break;
parent = parent->GetParent();
}
// if the titlebar is in a popup, move the popup frame, otherwise
// move the widget associated with the window
if (parent) {
nsMenuPopupFrame* menuPopupFrame =
static_cast<nsMenuPopupFrame*>(parent);
nsCOMPtr<nsIWidget> widget = menuPopupFrame->GetWidget();
LayoutDeviceIntRect bounds = widget->GetScreenBounds();
CSSPoint cssPos = (bounds.TopLeft() + nsMoveBy) /
aPresContext->CSSToDevPixelScale();
menuPopupFrame->MoveTo(RoundedToInt(cssPos), false);
} else {
mozilla::PresShell* presShell = aPresContext->PresShell();
nsPIDOMWindowOuter* window = presShell->GetDocument()->GetWindow();
if (window) {
window->MoveBy(nsMoveBy.x, nsMoveBy.y);
}
}
*aEventStatus = nsEventStatus_eConsumeNoDefault;
doDefault = false;
}
} break;
case eMouseClick: {
WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
if (mouseEvent->IsLeftClickEvent()) {
MouseClicked(mouseEvent);
}
break;
}
default:
break;
}
if (doDefault)
return nsBoxFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
else
return NS_OK;
}
void nsTitleBarFrame::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);
}

Просмотреть файл

@ -0,0 +1,43 @@
/* -*- 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 nsTitleBarFrame_h___
#define nsTitleBarFrame_h___
#include "mozilla/Attributes.h"
#include "mozilla/EventForwards.h"
#include "nsBoxFrame.h"
namespace mozilla {
class PresShell;
} // namespace mozilla
class nsTitleBarFrame : public nsBoxFrame {
public:
NS_DECL_FRAMEARENA_HELPERS(nsTitleBarFrame)
friend nsIFrame* NS_NewTitleBarFrame(mozilla::PresShell* aPresShell,
ComputedStyle* aStyle);
explicit nsTitleBarFrame(ComputedStyle* aStyle, nsPresContext* aPresContext,
ClassID = kClassID);
virtual void BuildDisplayListForChildren(
nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists) override;
virtual nsresult HandleEvent(nsPresContext* aPresContext,
mozilla::WidgetGUIEvent* aEvent,
nsEventStatus* aEventStatus) override;
MOZ_CAN_RUN_SCRIPT_BOUNDARY
virtual void MouseClicked(mozilla::WidgetMouseEvent* aEvent);
protected:
bool mTrackingMouseMove;
mozilla::LayoutDeviceIntPoint mLastPoint;
}; // class nsTitleBarFrame
#endif /* nsTitleBarFrame_h___ */

Просмотреть файл

@ -1,6 +1,8 @@
[DEFAULT]
skip-if = os == 'android'
support-files =
window_resizer.xhtml
window_resizer_element.xhtml
windowminmaxsize1.xhtml
windowminmaxsize2.xhtml
windowminmaxsize3.xhtml
@ -27,11 +29,14 @@ 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]
[test_resizer_incontent.xhtml]
[test_splitter.xhtml]
skip-if = toolkit == 'android' # no XUL theme
[test_titlebar_ctrl_click.xhtml]
[test_toolbarbutton_ctrl_click.xhtml]
[test_menuitem_ctrl_click.xhtml]

Просмотреть файл

@ -0,0 +1,97 @@
<?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>

Просмотреть файл

@ -0,0 +1,53 @@
<?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 Widget Test for the titlebar element
-->
<window title="Titlebar" width="200" height="200"
onload="setTimeout(test_titlebar_ctrl_click, 0);"
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"/>
<titlebar id="titlebar">
<label id="label" value="Titlebar"/>
</titlebar>
<!-- test code goes here -->
<script type="application/javascript"><![CDATA[
const { AppConstants } = SpecialPowers.ChromeUtils.import(
"resource://gre/modules/AppConstants.jsm"
);
SimpleTest.waitForExplicitFinish();
function test_titlebar_ctrl_click()
{
let titlebar = document.getElementById("titlebar");
let isCommandFired = false;
titlebar.addEventListener("click", function(aEvent) {
// Delay check for command event, because it is fired after click event.
setTimeout(() => {
ok(isCommandFired, "Check if command event is fired");
SimpleTest.finish();
}, 0);
});
titlebar.addEventListener("command", function(aEvent) {
isCommandFired = true;
ok(aEvent.ctrlKey, "Check ctrlKey for command event");
ok(!aEvent.shiftKey, "Check shiftKey for command event");
ok(!aEvent.altKey, "Check altKey for command event");
ok(!aEvent.metaKey, "Check metaKey for command event");
is(aEvent.inputSource, MouseEvent.MOZ_SOURCE_MOUSE,
"Check inputSource for command event");
});
synthesizeMouseAtCenter(titlebar, { ctrlKey: true });
}
]]>
</script>
</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;">
<hbox id="popupresizer" dir="bottomright" flex="1" width="60" height="60"
<resizer id="popupresizer" dir="bottomright" flex="1" width="60" height="60"
style="appearance: none; margin: 0; border: 0; padding: 0;"/>
</panel>
@ -80,6 +80,10 @@ 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
}
];
@ -151,6 +155,24 @@ 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();
}
@ -174,6 +196,11 @@ 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(() => {

Просмотреть файл

@ -0,0 +1,113 @@
<?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>

Просмотреть файл

@ -0,0 +1,190 @@
<?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>

Просмотреть файл

@ -46,6 +46,7 @@ support-files =
window_screenPosSize.xhtml
window_showcaret.xhtml
window_subframe_origin.xhtml
window_titlebar.xhtml
window_tooltip.xhtml
xul_selectcontrol.js
rtlchrome/rtl.css
@ -183,6 +184,8 @@ support-files = window_preferences_onsyncfrompreference.xhtml
[test_tabbox.xhtml]
[test_tabindex.xhtml]
[test_textbox_search.xhtml]
[test_titlebar.xhtml]
skip-if = os == "linux" || (os == 'mac' && os_version == '10.15') # macosx1014/15 due to 1550078
[test_tooltip.xhtml]
skip-if = (os == 'mac' || os == 'win') # Bug 1141245, frequent timeouts on OSX 10.14, Windows
[test_tooltip_noautohide.xhtml]

Просмотреть файл

@ -0,0 +1,34 @@
<?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 Widget Test for the titlebar element and window dragging
-->
<window title="Titlebar" width="200" height="200"
onload="setTimeout(test_titlebar, 0);"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
<!-- test results are displayed in the html:body -->
<body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/>
<!-- test code goes here -->
<script type="application/javascript"><![CDATA[
SimpleTest.waitForExplicitFinish();
function test_titlebar()
{
window.openDialog("window_titlebar.xhtml", "_blank", "chrome,left=200,top=200,noopener", window);
}
function done(testWindow)
{
testWindow.close();
SimpleTest.finish();
}
]]>
</script>
</window>

Просмотреть файл

@ -0,0 +1,221 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<!--
XUL Widget Test for the titlebar element and window dragging
-->
<window title="Titlebar" width="200" height="200"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
<titlebar id="titlebar">
<label id="label" value="Titlebar"/>
</titlebar>
<!-- a non-noautohide panel is treated as anchored -->
<panel id="panel" onpopupshown="popupshown(this, false)" onpopuphidden="popuphidden('panelnoautohide')">
<titlebar>
<label id="panellabel" value="Titlebar"/>
</titlebar>
</panel>
<panel id="panelnoautohide" noautohide="true"
onpopupshown="popupshown(this, false)" onpopuphidden="popuphidden('panelanchored')">
<titlebar>
<label id="panellabelnoautohide" value="Titlebar"/>
</titlebar>
</panel>
<panel id="panelanchored" noautohide="true"
onpopupshown="popupshown(this, true)" onpopuphidden="popuphidden('paneltop')">
<titlebar>
<label id="panellabelanchored" value="Titlebar"/>
</titlebar>
</panel>
<panel id="paneltop" noautohide="true" level="top"
onpopupshown="popupshown(this, false)" onpopuphidden="popuphidden('panelfloating')">
<titlebar>
<label id="panellabeltop" value="Titlebar"/>
</titlebar>
</panel>
<panel id="panelfloating" noautohide="true" level="floating"
onpopupshown="popupshown(this, false)" onpopuphidden="popuphidden('')">
<titlebar>
<label id="panellabelfloating" value="Titlebar"/>
</titlebar>
</panel>
<button id="button" label="OK"/>
<script>
<![CDATA[
var SimpleTest = window.arguments[0].SimpleTest;
SimpleTest.waitForFocus(test_titlebar, window);
var mouseDownTarget;
var origoldx, origoldy, oldx, oldy, waitSteps = 0;
function waitForWindowMove(element, x, y, callback, arg, panel, anchored)
{
var isPanelMove = (element.id != "label");
if (!waitSteps) {
oldx = isPanelMove ? panel.getBoundingClientRect().left : window.screenX;
oldy = isPanelMove ? panel.getBoundingClientRect().top : window.screenY;
synthesizeMouse(element, x, y, { type: "mousemove" });
}
var newx = isPanelMove ? panel.getBoundingClientRect().left : window.screenX;
var newy = isPanelMove ? panel.getBoundingClientRect().top : window.screenY;
if (newx == oldx && newy == oldy) {
if (waitSteps++ > 10) {
SimpleTest.is(window.screenX + "," + window.screenY, oldx + "," + oldy + " ",
"Window never moved properly to " + x + "," + y + (panel ? " " + panel.id : ""));
window.arguments[0].SimpleTest.finish();
window.close();
return;
}
setTimeout(waitForWindowMove, 100, element, x, y, callback, arg, panel, anchored);
}
else {
waitSteps = 0;
// on Linux, we need to wait a bit for the popup to be moved as well
if (navigator.platform.includes("Linux")) {
setTimeout(callback, 0, arg, panel, anchored);
}
else {
callback(arg, panel, anchored);
}
}
}
function test_titlebar()
{
var titlebar = document.getElementById("titlebar");
var label = document.getElementById("label");
origoldx = window.screenX;
origoldy = window.screenY;
var mousedownListener = event => mouseDownTarget = event.originalTarget;
window.addEventListener("mousedown", mousedownListener);
synthesizeMouse(label, 2, 2, { type: "mousedown" });
SimpleTest.is(mouseDownTarget, titlebar, "movedown on titlebar");
waitForWindowMove(label, 22, 22, test_titlebar_step2, mousedownListener);
}
function test_titlebar_step2(mousedownListener)
{
var titlebar = document.getElementById("titlebar");
var label = document.getElementById("label");
SimpleTest.is(window.screenX, origoldx + 20, "move window horizontal");
SimpleTest.is(window.screenY, origoldy + 20, "move window vertical");
synthesizeMouse(label, 22, 22, { type: "mouseup" });
// with allowevents set to true, the mouse should target the label instead
// and not move the window
titlebar.setAttribute("allowevents", "true");
synthesizeMouse(label, 2, 2, { type: "mousedown" });
SimpleTest.is(mouseDownTarget, label, "movedown on titlebar with allowevents");
synthesizeMouse(label, 22, 22, { type: "mousemove" });
SimpleTest.is(window.screenX, origoldx + 20, "mouse on label move window horizontal");
SimpleTest.is(window.screenY, origoldy + 20, "mouse on label move window vertical");
synthesizeMouse(label, 22, 22, { type: "mouseup" });
window.removeEventListener("mousedown", mousedownListener);
document.getElementById("panel").openPopupAtScreen(window.screenX + 50, window.screenY + 60, false);
}
function popupshown(panel, anchored)
{
var rect = panel.getBoundingClientRect();
// skip this check for non-noautohide panels
if (panel.id == "panel") {
var panellabel = panel.firstChild.firstChild;
synthesizeMouse(panellabel, 2, 2, { type: "mousedown" });
waitForWindowMove(panellabel, 22, 22, popupshown_step3, rect, panel, anchored);
return;
}
// now, try moving the window. If anchored, the popup should move with the
// window. If not anchored, the popup should remain at its current screen location.
window.moveBy(10, 10);
waitSteps = 1;
waitForWindowMove(document.getElementById("label"), 1, 1, popupshown_step2, rect, panel, anchored);
}
function popupshown_step2(oldrect, panel, anchored)
{
var newrect = panel.getBoundingClientRect();
// The window movement that occured long ago at the beginning of the test
// on Linux is delayed and there isn't any way to tell when the move
// actually happened. This causes the checks here to fail. Instead, just
// wait a bit for the test to be ready.
if (navigator.platform.includes("Linux") &&
newrect.left != oldrect.left - (anchored ? 0 : 10)) {
setTimeout(popupshown_step2, 10, oldrect, panel, anchored);
return;
}
// anchored popups should still be at the same offset. Non-anchored popups will
// now be offset by 10 pixels less.
SimpleTest.is(newrect.left, oldrect.left - (anchored ? 0 : 10),
panel.id + " horizontal after window move");
SimpleTest.is(newrect.top, oldrect.top - (anchored ? 0 : 10),
panel.id + " vertical after window move");
var panellabel = panel.firstChild.firstChild;
synthesizeMouse(panellabel, 2, 2, { type: "mousedown" });
waitForWindowMove(panellabel, 22, 22, popupshown_step3, newrect, panel, anchored);
}
function popupshown_step3(oldrect, panel, anchored)
{
// skip this check on Linux for the same window positioning reasons as above
if (!navigator.platform.includes("Linux") || (panel.id != "panelanchored" && panel.id != "paneltop")) {
// next, drag the titlebar in the panel
var newrect = panel.getBoundingClientRect();
SimpleTest.is(newrect.left, oldrect.left + 20, panel.id + " move popup horizontal");
SimpleTest.is(newrect.top, oldrect.top + 20, panel.id + " move popup vertical");
synthesizeMouse(document.getElementById("panellabel"), 22, 22, { type: "mouseup" });
synthesizeMouse(document.getElementById("button"), 5, 5, { type: "mousemove" });
newrect = panel.getBoundingClientRect();
SimpleTest.is(newrect.left, oldrect.left + 20, panel.id + " horizontal after mouse on button");
SimpleTest.is(newrect.top, oldrect.top + 20, panel.id + " vertical after mouse on button");
}
else {
synthesizeMouse(document.getElementById("panellabel"), 22, 22, { type: "mouseup" });
}
panel.hidePopup();
}
function popuphidden(nextPopup)
{
if (nextPopup) {
var panel = document.getElementById(nextPopup);
if (panel.id == "panelnoautohide") {
panel.openPopupAtScreen(window.screenX + 50, window.screenY + 60, false);
} else {
panel.openPopup(document.getElementById("button"), "after_start");
}
} else {
window.arguments[0].done(window);
}
}
]]>
</script>
</window>