Bug 212750, position context menu for menus when the keyboard shortcut is used, also move code around so that the right targets are set for context menu key events, r=smaug,sr=roc
This commit is contained in:
@@ -68,15 +68,6 @@
|
|||||||
#include "nsIScriptGlobalObject.h"
|
#include "nsIScriptGlobalObject.h"
|
||||||
#include "nsIScriptRuntime.h"
|
#include "nsIScriptRuntime.h"
|
||||||
#include "nsLayoutUtils.h"
|
#include "nsLayoutUtils.h"
|
||||||
#ifdef MOZ_XUL
|
|
||||||
// XXXbz the fact that this is ifdef MOZ_XUL is good indication that
|
|
||||||
// it doesn't belong here...
|
|
||||||
#include "nsITreeBoxObject.h"
|
|
||||||
#include "nsITreeColumns.h"
|
|
||||||
#include "nsIDOMXULMultSelectCntrlEl.h"
|
|
||||||
#include "nsIDOMXULSelectCntrlItemEl.h"
|
|
||||||
#include "nsIDOMXULMenuListElement.h"
|
|
||||||
#endif
|
|
||||||
#include "nsINameSpaceManager.h"
|
#include "nsINameSpaceManager.h"
|
||||||
#include "nsIContent.h"
|
#include "nsIContent.h"
|
||||||
#include "nsIFrame.h"
|
#include "nsIFrame.h"
|
||||||
@@ -96,9 +87,7 @@
|
|||||||
#include "nsIScriptObjectOwner.h" // for nsIScriptEventHandlerOwner
|
#include "nsIScriptObjectOwner.h" // for nsIScriptEventHandlerOwner
|
||||||
#include "nsIFocusController.h"
|
#include "nsIFocusController.h"
|
||||||
#include "nsIDOMElement.h"
|
#include "nsIDOMElement.h"
|
||||||
#include "nsIBoxObject.h"
|
|
||||||
#include "nsIDOMNSDocument.h"
|
#include "nsIDOMNSDocument.h"
|
||||||
#include "nsIWidget.h"
|
|
||||||
#include "nsContentUtils.h"
|
#include "nsContentUtils.h"
|
||||||
#include "nsJSUtils.h"
|
#include "nsJSUtils.h"
|
||||||
#include "nsIDOMEventGroup.h"
|
#include "nsIDOMEventGroup.h"
|
||||||
@@ -1126,14 +1115,6 @@ nsEventListenerManager::HandleEvent(nsPresContext* aPresContext,
|
|||||||
}
|
}
|
||||||
PRUint16 currentGroup = aFlags & NS_EVENT_FLAG_SYSTEM_EVENT;
|
PRUint16 currentGroup = aFlags & NS_EVENT_FLAG_SYSTEM_EVENT;
|
||||||
|
|
||||||
// Beware! This may flush notifications via synchronous
|
|
||||||
// ScrollSelectionIntoView.
|
|
||||||
if (aEvent->message == NS_CONTEXTMENU &&
|
|
||||||
NS_FAILED(FixContextMenuEvent(aPresContext, aCurrentTarget, aEvent,
|
|
||||||
aDOMEvent))) {
|
|
||||||
NS_WARNING("failed to fix context menu event target");
|
|
||||||
}
|
|
||||||
|
|
||||||
const EventTypeData* typeData = nsnull;
|
const EventTypeData* typeData = nsnull;
|
||||||
const EventDispatchData* dispData = nsnull;
|
const EventDispatchData* dispData = nsnull;
|
||||||
if (aEvent->message != NS_USER_DEFINED_EVENT) {
|
if (aEvent->message != NS_USER_DEFINED_EVENT) {
|
||||||
@@ -1357,336 +1338,6 @@ nsEventListenerManager::IsRegisteredHere(const nsAString & type, PRBool *_retval
|
|||||||
return NS_ERROR_NOT_IMPLEMENTED;
|
return NS_ERROR_NOT_IMPLEMENTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
|
||||||
nsEventListenerManager::FixContextMenuEvent(nsPresContext* aPresContext,
|
|
||||||
nsISupports* aCurrentTarget,
|
|
||||||
nsEvent* aEvent,
|
|
||||||
nsIDOMEvent** aDOMEvent)
|
|
||||||
{
|
|
||||||
nsIPresShell* shell = aPresContext ? aPresContext->GetPresShell() : nsnull;
|
|
||||||
if (!shell) {
|
|
||||||
// Nothing to do.
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsresult ret = NS_OK;
|
|
||||||
|
|
||||||
PRBool contextMenuKey =
|
|
||||||
static_cast<nsMouseEvent*>(aEvent)->context == nsMouseEvent::eContextMenuKey;
|
|
||||||
if (nsnull == *aDOMEvent) {
|
|
||||||
// If we're here because of the key-equiv for showing context menus, we
|
|
||||||
// have to twiddle with the NS event to make sure the context menu comes
|
|
||||||
// up in the upper left of the relevant content area before we create
|
|
||||||
// the DOM event. Since we never call InitMouseEvent() on the event,
|
|
||||||
// the client X/Y will be 0,0. We can make use of that if the widget is null.
|
|
||||||
if (contextMenuKey) {
|
|
||||||
aPresContext->GetViewManager()->GetWidget(getter_AddRefs(((nsGUIEvent*)aEvent)->widget));
|
|
||||||
aEvent->refPoint.x = 0;
|
|
||||||
aEvent->refPoint.y = 0;
|
|
||||||
}
|
|
||||||
ret = NS_NewDOMMouseEvent(aDOMEvent, aPresContext, static_cast<nsInputEvent*>(aEvent));
|
|
||||||
NS_ENSURE_SUCCESS(ret, ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
// see if we should use the caret position for the popup
|
|
||||||
if (contextMenuKey) {
|
|
||||||
nsIntPoint caretPoint;
|
|
||||||
// Beware! This may flush notifications via synchronous
|
|
||||||
// ScrollSelectionIntoView.
|
|
||||||
if (PrepareToUseCaretPosition(((nsGUIEvent*)aEvent)->widget,
|
|
||||||
shell, caretPoint)) {
|
|
||||||
// caret position is good
|
|
||||||
aEvent->refPoint.x = caretPoint.x;
|
|
||||||
aEvent->refPoint.y = caretPoint.y;
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we're here because of the key-equiv for showing context menus, we
|
|
||||||
// have to reset the event target to the currently focused element. Get it
|
|
||||||
// from the focus controller.
|
|
||||||
nsCOMPtr<nsIDOMEventTarget> currentTarget = do_QueryInterface(aCurrentTarget);
|
|
||||||
nsCOMPtr<nsIDOMElement> currentFocus;
|
|
||||||
|
|
||||||
if (contextMenuKey) {
|
|
||||||
nsIDocument *doc = shell->GetDocument();
|
|
||||||
if (doc) {
|
|
||||||
nsPIDOMWindow* privWindow = doc->GetWindow();
|
|
||||||
if (privWindow) {
|
|
||||||
nsIFocusController *focusController =
|
|
||||||
privWindow->GetRootFocusController();
|
|
||||||
if (focusController)
|
|
||||||
focusController->GetFocusedElement(getter_AddRefs(currentFocus));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentFocus) {
|
|
||||||
// Reset event coordinates relative to focused frame in view
|
|
||||||
nsIntPoint targetPt;
|
|
||||||
GetCoordinatesFor(currentFocus, aPresContext, shell, targetPt);
|
|
||||||
aEvent->refPoint.x = targetPt.x;
|
|
||||||
aEvent->refPoint.y = targetPt.y;
|
|
||||||
|
|
||||||
currentTarget = do_QueryInterface(currentFocus);
|
|
||||||
nsCOMPtr<nsIPrivateDOMEvent> pEvent(do_QueryInterface(*aDOMEvent));
|
|
||||||
pEvent->SetTarget(currentTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
// nsEventListenerManager::PrepareToUseCaretPosition
|
|
||||||
//
|
|
||||||
// This checks to see if we should use the caret position for popup context
|
|
||||||
// menus. Returns true if the caret position should be used, and the
|
|
||||||
// coordinates of that position is returned in aTargetPt. This function
|
|
||||||
// will also scroll the window as needed to make the caret visible.
|
|
||||||
//
|
|
||||||
// The event widget should be the widget that generated the event, and
|
|
||||||
// whose coordinate system the resulting event's refPoint should be
|
|
||||||
// relative to. The returned point is in device pixels realtive to the
|
|
||||||
// widget passed in.
|
|
||||||
|
|
||||||
PRBool
|
|
||||||
nsEventListenerManager::PrepareToUseCaretPosition(nsIWidget* aEventWidget,
|
|
||||||
nsIPresShell* aShell,
|
|
||||||
nsIntPoint& aTargetPt)
|
|
||||||
{
|
|
||||||
nsresult rv;
|
|
||||||
|
|
||||||
// check caret visibility
|
|
||||||
nsRefPtr<nsCaret> caret;
|
|
||||||
rv = aShell->GetCaret(getter_AddRefs(caret));
|
|
||||||
NS_ENSURE_SUCCESS(rv, PR_FALSE);
|
|
||||||
NS_ENSURE_TRUE(caret, PR_FALSE);
|
|
||||||
|
|
||||||
PRBool caretVisible = PR_FALSE;
|
|
||||||
rv = caret->GetCaretVisible(&caretVisible);
|
|
||||||
if (NS_FAILED(rv) || ! caretVisible)
|
|
||||||
return PR_FALSE;
|
|
||||||
|
|
||||||
// caret selection, this is a temporary weak reference, so no refcounting is
|
|
||||||
// needed
|
|
||||||
nsISelection* domSelection = caret->GetCaretDOMSelection();
|
|
||||||
NS_ENSURE_TRUE(domSelection, PR_FALSE);
|
|
||||||
|
|
||||||
// since the match could be an anonymous textnode inside a
|
|
||||||
// <textarea> or text <input>, we need to get the outer frame
|
|
||||||
// note: frames are not refcounted
|
|
||||||
nsIFrame* frame = nsnull; // may be NULL
|
|
||||||
nsCOMPtr<nsIDOMNode> node;
|
|
||||||
rv = domSelection->GetFocusNode(getter_AddRefs(node));
|
|
||||||
NS_ENSURE_SUCCESS(rv, PR_FALSE);
|
|
||||||
NS_ENSURE_TRUE(node, PR_FALSE);
|
|
||||||
nsCOMPtr<nsIContent> content(do_QueryInterface(node));
|
|
||||||
if (content) {
|
|
||||||
nsIContent* nonNative = content->FindFirstNonNativeAnonymous();
|
|
||||||
content = nonNative;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (content) {
|
|
||||||
// It seems like selCon->ScrollSelectionIntoView should be enough, but it's
|
|
||||||
// not. The problem is that scrolling the selection into view when it is
|
|
||||||
// below the current viewport will align the top line of the frame exactly
|
|
||||||
// with the bottom of the window. This is fine, BUT, the popup event causes
|
|
||||||
// the control to be re-focused which does this exact call to
|
|
||||||
// ScrollContentIntoView, which has a one-pixel disagreement of whether the
|
|
||||||
// frame is actually in view. The result is that the frame is aligned with
|
|
||||||
// the top of the window, but the menu is still at the bottom.
|
|
||||||
//
|
|
||||||
// Doing this call first forces the frame to be in view, eliminating the
|
|
||||||
// problem. The only difference in the result is that if your cursor is in
|
|
||||||
// an edit box below the current view, you'll get the edit box aligned with
|
|
||||||
// the top of the window. This is arguably better behavior anyway.
|
|
||||||
rv = aShell->ScrollContentIntoView(content,
|
|
||||||
NS_PRESSHELL_SCROLL_IF_NOT_VISIBLE,
|
|
||||||
NS_PRESSHELL_SCROLL_IF_NOT_VISIBLE);
|
|
||||||
NS_ENSURE_SUCCESS(rv, PR_FALSE);
|
|
||||||
frame = aShell->GetPrimaryFrameFor(content);
|
|
||||||
NS_WARN_IF_FALSE(frame, "No frame for focused content?");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Actually scroll the selection (ie caret) into view. Note that this must
|
|
||||||
// be synchronous since we will be checking the caret position on the screen.
|
|
||||||
//
|
|
||||||
// Be easy about errors, and just don't scroll in those cases. Better to have
|
|
||||||
// the correct menu at a weird place than the wrong menu.
|
|
||||||
nsCOMPtr<nsISelectionController> selCon;
|
|
||||||
if (frame)
|
|
||||||
frame->GetSelectionController(aShell->GetPresContext(),
|
|
||||||
getter_AddRefs(selCon));
|
|
||||||
else
|
|
||||||
selCon = do_QueryInterface(aShell);
|
|
||||||
if (selCon) {
|
|
||||||
// After ScrollSelectionIntoView(), the pending notifications might be
|
|
||||||
// flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
|
|
||||||
rv = selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,
|
|
||||||
nsISelectionController::SELECTION_FOCUS_REGION, PR_TRUE);
|
|
||||||
NS_ENSURE_SUCCESS(rv, PR_FALSE);
|
|
||||||
}
|
|
||||||
|
|
||||||
// get caret position relative to some view (normally the same as the
|
|
||||||
// event widget view, but this is not guaranteed)
|
|
||||||
PRBool isCollapsed;
|
|
||||||
nsIView* view;
|
|
||||||
nsRect caretCoords;
|
|
||||||
rv = caret->GetCaretCoordinates(nsCaret::eRenderingViewCoordinates,
|
|
||||||
domSelection, &caretCoords, &isCollapsed,
|
|
||||||
&view);
|
|
||||||
NS_ENSURE_SUCCESS(rv, PR_FALSE);
|
|
||||||
|
|
||||||
// in case the view used for caret coordinates was something else, we need
|
|
||||||
// to bring those coordinates into the space of the widget view
|
|
||||||
nsIView* widgetView = nsIView::GetViewFor(aEventWidget);
|
|
||||||
NS_ENSURE_TRUE(widgetView, PR_FALSE);
|
|
||||||
nsPoint viewToWidget;
|
|
||||||
widgetView->GetNearestWidget(&viewToWidget);
|
|
||||||
nsPoint viewDelta = view->GetOffsetTo(widgetView) + viewToWidget;
|
|
||||||
|
|
||||||
// caret coordinates are in app units, convert to pixels
|
|
||||||
nsPresContext* presContext = aShell->GetPresContext();
|
|
||||||
aTargetPt.x = presContext->AppUnitsToDevPixels(viewDelta.x + caretCoords.x + caretCoords.width);
|
|
||||||
aTargetPt.y = presContext->AppUnitsToDevPixels(viewDelta.y + caretCoords.y + caretCoords.height);
|
|
||||||
|
|
||||||
return PR_TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get coordinates in device pixels relative to root view for element,
|
|
||||||
// first ensuring the element is onscreen
|
|
||||||
void
|
|
||||||
nsEventListenerManager::GetCoordinatesFor(nsIDOMElement *aCurrentEl,
|
|
||||||
nsPresContext *aPresContext,
|
|
||||||
nsIPresShell *aPresShell,
|
|
||||||
nsIntPoint& aTargetPt)
|
|
||||||
{
|
|
||||||
nsCOMPtr<nsIContent> focusedContent(do_QueryInterface(aCurrentEl));
|
|
||||||
aPresShell->ScrollContentIntoView(focusedContent,
|
|
||||||
NS_PRESSHELL_SCROLL_ANYWHERE,
|
|
||||||
NS_PRESSHELL_SCROLL_ANYWHERE);
|
|
||||||
|
|
||||||
PRBool istree = PR_FALSE, checkLineHeight = PR_TRUE;
|
|
||||||
PRInt32 extraPixelsY = 0, extraTreeY = 0;
|
|
||||||
|
|
||||||
#ifdef MOZ_XUL
|
|
||||||
// Set the position to just underneath the current item for multi-select
|
|
||||||
// lists or just underneath the selected item for single-select lists. If
|
|
||||||
// the element is not a list, or there is no selection, leave the position
|
|
||||||
// as is.
|
|
||||||
nsCOMPtr<nsIDOMXULSelectControlItemElement> item;
|
|
||||||
nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelect =
|
|
||||||
do_QueryInterface(aCurrentEl);
|
|
||||||
if (multiSelect) {
|
|
||||||
checkLineHeight = PR_FALSE;
|
|
||||||
|
|
||||||
PRInt32 currentIndex;
|
|
||||||
multiSelect->GetCurrentIndex(¤tIndex);
|
|
||||||
if (currentIndex >= 0) {
|
|
||||||
nsCOMPtr<nsIDOMXULElement> xulElement(do_QueryInterface(aCurrentEl));
|
|
||||||
if (xulElement) {
|
|
||||||
nsCOMPtr<nsIBoxObject> box;
|
|
||||||
xulElement->GetBoxObject(getter_AddRefs(box));
|
|
||||||
nsCOMPtr<nsITreeBoxObject> treeBox(do_QueryInterface(box));
|
|
||||||
// Tree view special case (tree items have no frames)
|
|
||||||
// Get the focused row and add its coordinates, which are already in pixels
|
|
||||||
// XXX Boris, should we create a new interface so that event listener manager doesn't
|
|
||||||
// need to know about trees? Something like nsINodelessChildCreator which
|
|
||||||
// could provide the current focus coordinates?
|
|
||||||
if (treeBox) {
|
|
||||||
treeBox->EnsureRowIsVisible(currentIndex);
|
|
||||||
PRInt32 firstVisibleRow, rowHeight;
|
|
||||||
treeBox->GetFirstVisibleRow(&firstVisibleRow);
|
|
||||||
treeBox->GetRowHeight(&rowHeight);
|
|
||||||
|
|
||||||
extraPixelsY = (currentIndex - firstVisibleRow + 1) * rowHeight;
|
|
||||||
istree = PR_TRUE;
|
|
||||||
|
|
||||||
nsCOMPtr<nsITreeColumns> cols;
|
|
||||||
treeBox->GetColumns(getter_AddRefs(cols));
|
|
||||||
if (cols) {
|
|
||||||
nsCOMPtr<nsITreeColumn> col;
|
|
||||||
cols->GetFirstColumn(getter_AddRefs(col));
|
|
||||||
if (col) {
|
|
||||||
nsCOMPtr<nsIDOMElement> colElement;
|
|
||||||
col->GetElement(getter_AddRefs(colElement));
|
|
||||||
nsCOMPtr<nsIContent> colContent(do_QueryInterface(colElement));
|
|
||||||
if (colContent) {
|
|
||||||
nsIFrame* frame = aPresShell->GetPrimaryFrameFor(colContent);
|
|
||||||
if (frame) {
|
|
||||||
extraTreeY = frame->GetSize().height;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
multiSelect->GetCurrentItem(getter_AddRefs(item));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// don't check menulists as the selected item will be inside a popup.
|
|
||||||
nsCOMPtr<nsIDOMXULMenuListElement> menulist = do_QueryInterface(aCurrentEl);
|
|
||||||
if (!menulist) {
|
|
||||||
checkLineHeight = PR_FALSE;
|
|
||||||
nsCOMPtr<nsIDOMXULSelectControlElement> select =
|
|
||||||
do_QueryInterface(aCurrentEl);
|
|
||||||
if (select)
|
|
||||||
select->GetSelectedItem(getter_AddRefs(item));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item)
|
|
||||||
focusedContent = do_QueryInterface(item);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
nsIFrame *frame = aPresShell->GetPrimaryFrameFor(focusedContent);
|
|
||||||
if (frame) {
|
|
||||||
nsPoint frameOrigin(0, 0);
|
|
||||||
|
|
||||||
// Get the frame's origin within its view
|
|
||||||
nsIView *view = frame->GetClosestView(&frameOrigin);
|
|
||||||
NS_ASSERTION(view, "No view for frame");
|
|
||||||
|
|
||||||
nsIViewManager* vm = aPresShell->GetViewManager();
|
|
||||||
nsIView *rootView = nsnull;
|
|
||||||
vm->GetRootView(rootView);
|
|
||||||
NS_ASSERTION(rootView, "No root view in pres shell");
|
|
||||||
|
|
||||||
// View's origin within its root view
|
|
||||||
frameOrigin += view->GetOffsetTo(rootView);
|
|
||||||
|
|
||||||
// Start context menu down and to the right from top left of frame
|
|
||||||
// use the lineheight. This is a good distance to move the context
|
|
||||||
// menu away from the top left corner of the frame. If we always
|
|
||||||
// used the frame height, the context menu could end up far away,
|
|
||||||
// for example when we're focused on linked images.
|
|
||||||
// On the other hand, we want to use the frame height if it's less
|
|
||||||
// than the current line height, so that the context menu appears
|
|
||||||
// associated with the correct frame.
|
|
||||||
nscoord extra = 0;
|
|
||||||
if (!istree) {
|
|
||||||
extra = frame->GetSize().height;
|
|
||||||
if (checkLineHeight) {
|
|
||||||
nsIScrollableView *scrollView =
|
|
||||||
nsLayoutUtils::GetNearestScrollingView(view, nsLayoutUtils::eEither);
|
|
||||||
if (scrollView) {
|
|
||||||
nscoord scrollViewLineHeight;
|
|
||||||
scrollView->GetLineHeight(&scrollViewLineHeight);
|
|
||||||
if (extra > scrollViewLineHeight) {
|
|
||||||
extra = scrollViewLineHeight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
aTargetPt.x = aPresContext->AppUnitsToDevPixels(frameOrigin.x);
|
|
||||||
aTargetPt.y = aPresContext->AppUnitsToDevPixels(
|
|
||||||
frameOrigin.y + extra + extraTreeY) + extraPixelsY;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsEventListenerManager::HasMutationListeners(PRBool* aListener)
|
nsEventListenerManager::HasMutationListeners(PRBool* aListener)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -180,15 +180,6 @@ protected:
|
|||||||
nsresult RemoveAllListeners();
|
nsresult RemoveAllListeners();
|
||||||
const EventTypeData* GetTypeDataForIID(const nsIID& aIID);
|
const EventTypeData* GetTypeDataForIID(const nsIID& aIID);
|
||||||
const EventTypeData* GetTypeDataForEventName(nsIAtom* aName);
|
const EventTypeData* GetTypeDataForEventName(nsIAtom* aName);
|
||||||
nsresult FixContextMenuEvent(nsPresContext* aPresContext,
|
|
||||||
nsISupports* aCurrentTarget,
|
|
||||||
nsEvent* aEvent,
|
|
||||||
nsIDOMEvent** aDOMEvent);
|
|
||||||
PRBool PrepareToUseCaretPosition(nsIWidget* aEventWidget,
|
|
||||||
nsIPresShell* aShell,
|
|
||||||
nsIntPoint& aTargetPt);
|
|
||||||
void GetCoordinatesFor(nsIDOMElement *aCurrentEl, nsPresContext *aPresContext,
|
|
||||||
nsIPresShell *aPresShell, nsIntPoint& aTargetPt);
|
|
||||||
nsresult GetDOM2EventGroup(nsIDOMEventGroup** aGroup);
|
nsresult GetDOM2EventGroup(nsIDOMEventGroup** aGroup);
|
||||||
PRBool ListenerCanHandle(nsListenerStruct* aLs, nsEvent* aEvent);
|
PRBool ListenerCanHandle(nsListenerStruct* aLs, nsEvent* aEvent);
|
||||||
nsPIDOMWindow* GetInnerWindowForTarget();
|
nsPIDOMWindow* GetInnerWindowForTarget();
|
||||||
|
|||||||
@@ -193,6 +193,14 @@
|
|||||||
#ifdef MOZ_XUL
|
#ifdef MOZ_XUL
|
||||||
#include "nsMenuFrame.h"
|
#include "nsMenuFrame.h"
|
||||||
#include "nsTreeBodyFrame.h"
|
#include "nsTreeBodyFrame.h"
|
||||||
|
#include "nsIBoxObject.h"
|
||||||
|
#include "nsITreeBoxObject.h"
|
||||||
|
#include "nsMenuPopupFrame.h"
|
||||||
|
#include "nsITreeColumns.h"
|
||||||
|
#include "nsIDOMXULMultSelectCntrlEl.h"
|
||||||
|
#include "nsIDOMXULSelectCntrlItemEl.h"
|
||||||
|
#include "nsIDOMXULMenuListElement.h"
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
#include "nsPlaceholderFrame.h"
|
#include "nsPlaceholderFrame.h"
|
||||||
|
|
||||||
@@ -1187,6 +1195,32 @@ private:
|
|||||||
nsGUIEvent* aEvent,
|
nsGUIEvent* aEvent,
|
||||||
nsEventStatus* aEventStatus);
|
nsEventStatus* aEventStatus);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This and the next two helper methods are used to target and position the
|
||||||
|
* context menu when the keyboard shortcut is used to open it.
|
||||||
|
*
|
||||||
|
* If another menu is open, the context menu is opened relative to the
|
||||||
|
* active menuitem within the menu, or the menu itself if no item is active.
|
||||||
|
* Otherwise, if the caret is visible, the menu is opened near the caret.
|
||||||
|
* Otherwise, if a selectable list such as a listbox is focused, the
|
||||||
|
* current item within the menu is opened relative to this item.
|
||||||
|
* Otherwise, the context menu is opened at the topleft corner of the
|
||||||
|
* view.
|
||||||
|
*
|
||||||
|
* Returns true if the context menu event should fire and false if it should
|
||||||
|
* not.
|
||||||
|
*/
|
||||||
|
PRBool AdjustContextMenuKeyEvent(nsMouseEvent* aEvent);
|
||||||
|
|
||||||
|
//
|
||||||
|
PRBool PrepareToUseCaretPosition(nsIWidget* aEventWidget, nsPoint& aTargetPt);
|
||||||
|
|
||||||
|
// Get the selected item and coordinates in device pixels relative to root
|
||||||
|
// view for element, first ensuring the element is onscreen
|
||||||
|
void GetCurrentItemAndPositionForElement(nsIDOMElement *aCurrentEl,
|
||||||
|
nsIContent **aTargetToUse,
|
||||||
|
nsPoint& aTargetPt);
|
||||||
|
|
||||||
void FireResizeEvent();
|
void FireResizeEvent();
|
||||||
nsRevocableEventPtr<nsRunnableMethod<PresShell> > mResizeEvent;
|
nsRevocableEventPtr<nsRunnableMethod<PresShell> > mResizeEvent;
|
||||||
|
|
||||||
@@ -5854,6 +5888,12 @@ PresShell::HandleEventInternal(nsEvent* aEvent, nsIView *aView,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (aEvent->message == NS_CONTEXTMENU &&
|
||||||
|
static_cast<nsMouseEvent*>(aEvent)->context == nsMouseEvent::eContextMenuKey) {
|
||||||
|
if (!AdjustContextMenuKeyEvent(static_cast<nsMouseEvent*>(aEvent)))
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
nsAutoHandlingUserInputStatePusher userInpStatePusher(isHandlingUserInput);
|
nsAutoHandlingUserInputStatePusher userInpStatePusher(isHandlingUserInput);
|
||||||
|
|
||||||
nsAutoPopupStatePusher popupStatePusher(nsDOMEvent::GetEventPopupControlState(aEvent));
|
nsAutoPopupStatePusher popupStatePusher(nsDOMEvent::GetEventPopupControlState(aEvent));
|
||||||
@@ -5929,6 +5969,336 @@ PresShell::HandleDOMEventWithTarget(nsIContent* aTargetContent, nsEvent* aEvent,
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PRBool
|
||||||
|
PresShell::AdjustContextMenuKeyEvent(nsMouseEvent* aEvent)
|
||||||
|
{
|
||||||
|
#ifdef MOZ_XUL
|
||||||
|
// if a menu is open, open the context menu relative to the active item on the menu.
|
||||||
|
// XXXndeakin Mac doesn't fire mouse-triggered context menus while another
|
||||||
|
// menu is open. Maybe we should prevent keyboard-tiggered context menu events too.
|
||||||
|
nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
|
||||||
|
if (pm) {
|
||||||
|
nsIFrame* popupFrame = pm->GetTopPopup(ePopupTypeMenu);
|
||||||
|
if (popupFrame) {
|
||||||
|
#ifdef XP_MACOSX
|
||||||
|
// context menus should not be opened while another menu is open on Mac,
|
||||||
|
// so return false so that the event is not fired.
|
||||||
|
return PR_FALSE;
|
||||||
|
#else
|
||||||
|
nsIFrame* itemFrame =
|
||||||
|
(static_cast<nsMenuPopupFrame *>(popupFrame))->GetCurrentMenuItem();
|
||||||
|
if (!itemFrame)
|
||||||
|
itemFrame = popupFrame;
|
||||||
|
|
||||||
|
nsCOMPtr<nsIWidget> widget = popupFrame->GetWindow();
|
||||||
|
aEvent->widget = widget;
|
||||||
|
nsRect widgetRect(0, 0, 1, 1);
|
||||||
|
widget->WidgetToScreen(widgetRect, widgetRect);
|
||||||
|
aEvent->refPoint = itemFrame->GetScreenRect().BottomLeft() - widgetRect.TopLeft();
|
||||||
|
|
||||||
|
mCurrentEventContent = itemFrame->GetContent();
|
||||||
|
mCurrentEventFrame = itemFrame;
|
||||||
|
|
||||||
|
return PR_TRUE;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// If we're here because of the key-equiv for showing context menus, we
|
||||||
|
// have to twiddle with the NS event to make sure the context menu comes
|
||||||
|
// up in the upper left of the relevant content area before we create
|
||||||
|
// the DOM event. Since we never call InitMouseEvent() on the event,
|
||||||
|
// the client X/Y will be 0,0. We can make use of that if the widget is null.
|
||||||
|
mViewManager->GetWidget(getter_AddRefs(aEvent->widget));
|
||||||
|
aEvent->refPoint.x = 0;
|
||||||
|
aEvent->refPoint.y = 0;
|
||||||
|
|
||||||
|
// see if we should use the caret position for the popup
|
||||||
|
nsPoint caretPoint;
|
||||||
|
// Beware! This may flush notifications via synchronous
|
||||||
|
// ScrollSelectionIntoView.
|
||||||
|
if (PrepareToUseCaretPosition(aEvent->widget, caretPoint)) {
|
||||||
|
// caret position is good
|
||||||
|
aEvent->refPoint = caretPoint;
|
||||||
|
return PR_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we're here because of the key-equiv for showing context menus, we
|
||||||
|
// have to reset the event target to the currently focused element. Get it
|
||||||
|
// from the focus controller.
|
||||||
|
nsIDocument *doc = GetDocument();
|
||||||
|
if (doc) {
|
||||||
|
nsPIDOMWindow* privWindow = doc->GetWindow();
|
||||||
|
if (privWindow) {
|
||||||
|
nsIFocusController *focusController =
|
||||||
|
privWindow->GetRootFocusController();
|
||||||
|
if (focusController) {
|
||||||
|
nsCOMPtr<nsIDOMElement> currentFocus;
|
||||||
|
focusController->GetFocusedElement(getter_AddRefs(currentFocus));
|
||||||
|
// Reset event coordinates relative to focused frame in view
|
||||||
|
if (currentFocus) {
|
||||||
|
nsCOMPtr<nsIContent> currentPointElement;
|
||||||
|
GetCurrentItemAndPositionForElement(currentFocus,
|
||||||
|
getter_AddRefs(currentPointElement),
|
||||||
|
aEvent->refPoint);
|
||||||
|
if (currentPointElement) {
|
||||||
|
mCurrentEventContent = currentPointElement;
|
||||||
|
mCurrentEventFrame = nsnull;
|
||||||
|
GetCurrentEventFrame();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return PR_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// nsEventListenerManager::PrepareToUseCaretPosition
|
||||||
|
//
|
||||||
|
// This checks to see if we should use the caret position for popup context
|
||||||
|
// menus. Returns true if the caret position should be used, and the
|
||||||
|
// coordinates of that position is returned in aTargetPt. This function
|
||||||
|
// will also scroll the window as needed to make the caret visible.
|
||||||
|
//
|
||||||
|
// The event widget should be the widget that generated the event, and
|
||||||
|
// whose coordinate system the resulting event's refPoint should be
|
||||||
|
// relative to. The returned point is in device pixels realtive to the
|
||||||
|
// widget passed in.
|
||||||
|
PRBool
|
||||||
|
PresShell::PrepareToUseCaretPosition(nsIWidget* aEventWidget, nsIntPoint& aTargetPt)
|
||||||
|
{
|
||||||
|
nsresult rv;
|
||||||
|
|
||||||
|
// check caret visibility
|
||||||
|
nsRefPtr<nsCaret> caret;
|
||||||
|
rv = GetCaret(getter_AddRefs(caret));
|
||||||
|
NS_ENSURE_SUCCESS(rv, PR_FALSE);
|
||||||
|
NS_ENSURE_TRUE(caret, PR_FALSE);
|
||||||
|
|
||||||
|
PRBool caretVisible = PR_FALSE;
|
||||||
|
rv = caret->GetCaretVisible(&caretVisible);
|
||||||
|
if (NS_FAILED(rv) || ! caretVisible)
|
||||||
|
return PR_FALSE;
|
||||||
|
|
||||||
|
// caret selection, this is a temporary weak reference, so no refcounting is
|
||||||
|
// needed
|
||||||
|
nsISelection* domSelection = caret->GetCaretDOMSelection();
|
||||||
|
NS_ENSURE_TRUE(domSelection, PR_FALSE);
|
||||||
|
|
||||||
|
// since the match could be an anonymous textnode inside a
|
||||||
|
// <textarea> or text <input>, we need to get the outer frame
|
||||||
|
// note: frames are not refcounted
|
||||||
|
nsIFrame* frame = nsnull; // may be NULL
|
||||||
|
nsCOMPtr<nsIDOMNode> node;
|
||||||
|
rv = domSelection->GetFocusNode(getter_AddRefs(node));
|
||||||
|
NS_ENSURE_SUCCESS(rv, PR_FALSE);
|
||||||
|
NS_ENSURE_TRUE(node, PR_FALSE);
|
||||||
|
nsCOMPtr<nsIContent> content(do_QueryInterface(node));
|
||||||
|
if (content) {
|
||||||
|
nsIContent* nonNative = content->FindFirstNonNativeAnonymous();
|
||||||
|
content = nonNative;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (content) {
|
||||||
|
// It seems like ScrollSelectionIntoView should be enough, but it's
|
||||||
|
// not. The problem is that scrolling the selection into view when it is
|
||||||
|
// below the current viewport will align the top line of the frame exactly
|
||||||
|
// with the bottom of the window. This is fine, BUT, the popup event causes
|
||||||
|
// the control to be re-focused which does this exact call to
|
||||||
|
// ScrollContentIntoView, which has a one-pixel disagreement of whether the
|
||||||
|
// frame is actually in view. The result is that the frame is aligned with
|
||||||
|
// the top of the window, but the menu is still at the bottom.
|
||||||
|
//
|
||||||
|
// Doing this call first forces the frame to be in view, eliminating the
|
||||||
|
// problem. The only difference in the result is that if your cursor is in
|
||||||
|
// an edit box below the current view, you'll get the edit box aligned with
|
||||||
|
// the top of the window. This is arguably better behavior anyway.
|
||||||
|
rv = ScrollContentIntoView(content, NS_PRESSHELL_SCROLL_IF_NOT_VISIBLE,
|
||||||
|
NS_PRESSHELL_SCROLL_IF_NOT_VISIBLE);
|
||||||
|
NS_ENSURE_SUCCESS(rv, PR_FALSE);
|
||||||
|
frame = GetPrimaryFrameFor(content);
|
||||||
|
NS_WARN_IF_FALSE(frame, "No frame for focused content?");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actually scroll the selection (ie caret) into view. Note that this must
|
||||||
|
// be synchronous since we will be checking the caret position on the screen.
|
||||||
|
//
|
||||||
|
// Be easy about errors, and just don't scroll in those cases. Better to have
|
||||||
|
// the correct menu at a weird place than the wrong menu.
|
||||||
|
// After ScrollSelectionIntoView(), the pending notifications might be
|
||||||
|
// flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
|
||||||
|
nsCOMPtr<nsISelectionController> selCon;
|
||||||
|
if (frame)
|
||||||
|
frame->GetSelectionController(GetPresContext(), getter_AddRefs(selCon));
|
||||||
|
else
|
||||||
|
selCon = static_cast<nsISelectionController *>(this);
|
||||||
|
if (selCon) {
|
||||||
|
rv = selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,
|
||||||
|
nsISelectionController::SELECTION_FOCUS_REGION, PR_TRUE);
|
||||||
|
NS_ENSURE_SUCCESS(rv, PR_FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// get caret position relative to some view (normally the same as the
|
||||||
|
// event widget view, but this is not guaranteed)
|
||||||
|
PRBool isCollapsed;
|
||||||
|
nsIView* view;
|
||||||
|
nsRect caretCoords;
|
||||||
|
rv = caret->GetCaretCoordinates(nsCaret::eRenderingViewCoordinates,
|
||||||
|
domSelection, &caretCoords, &isCollapsed,
|
||||||
|
&view);
|
||||||
|
NS_ENSURE_SUCCESS(rv, PR_FALSE);
|
||||||
|
|
||||||
|
// in case the view used for caret coordinates was something else, we need
|
||||||
|
// to bring those coordinates into the space of the widget view
|
||||||
|
nsIView* widgetView = nsIView::GetViewFor(aEventWidget);
|
||||||
|
NS_ENSURE_TRUE(widgetView, PR_FALSE);
|
||||||
|
nsPoint viewToWidget;
|
||||||
|
widgetView->GetNearestWidget(&viewToWidget);
|
||||||
|
nsPoint viewDelta = view->GetOffsetTo(widgetView) + viewToWidget;
|
||||||
|
|
||||||
|
// caret coordinates are in app units, convert to pixels
|
||||||
|
nsPresContext* presContext = GetPresContext();
|
||||||
|
aTargetPt.x = presContext->AppUnitsToDevPixels(viewDelta.x + caretCoords.x + caretCoords.width);
|
||||||
|
aTargetPt.y = presContext->AppUnitsToDevPixels(viewDelta.y + caretCoords.y + caretCoords.height);
|
||||||
|
|
||||||
|
return PR_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
PresShell::GetCurrentItemAndPositionForElement(nsIDOMElement *aCurrentEl,
|
||||||
|
nsIContent** aTargetToUse,
|
||||||
|
nsIntPoint& aTargetPt)
|
||||||
|
{
|
||||||
|
nsCOMPtr<nsIContent> focusedContent(do_QueryInterface(aCurrentEl));
|
||||||
|
ScrollContentIntoView(focusedContent, NS_PRESSHELL_SCROLL_ANYWHERE,
|
||||||
|
NS_PRESSHELL_SCROLL_ANYWHERE);
|
||||||
|
|
||||||
|
PRBool istree = PR_FALSE, checkLineHeight = PR_TRUE;
|
||||||
|
PRInt32 extraPixelsY = 0, extraTreeY = 0;
|
||||||
|
|
||||||
|
#ifdef MOZ_XUL
|
||||||
|
// Set the position to just underneath the current item for multi-select
|
||||||
|
// lists or just underneath the selected item for single-select lists. If
|
||||||
|
// the element is not a list, or there is no selection, leave the position
|
||||||
|
// as is.
|
||||||
|
nsCOMPtr<nsIDOMXULSelectControlItemElement> item;
|
||||||
|
nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelect =
|
||||||
|
do_QueryInterface(aCurrentEl);
|
||||||
|
if (multiSelect) {
|
||||||
|
checkLineHeight = PR_FALSE;
|
||||||
|
|
||||||
|
PRInt32 currentIndex;
|
||||||
|
multiSelect->GetCurrentIndex(¤tIndex);
|
||||||
|
if (currentIndex >= 0) {
|
||||||
|
nsCOMPtr<nsIDOMXULElement> xulElement(do_QueryInterface(aCurrentEl));
|
||||||
|
if (xulElement) {
|
||||||
|
nsCOMPtr<nsIBoxObject> box;
|
||||||
|
xulElement->GetBoxObject(getter_AddRefs(box));
|
||||||
|
nsCOMPtr<nsITreeBoxObject> treeBox(do_QueryInterface(box));
|
||||||
|
// Tree view special case (tree items have no frames)
|
||||||
|
// Get the focused row and add its coordinates, which are already in pixels
|
||||||
|
// XXX Boris, should we create a new interface so that this doesn't
|
||||||
|
// need to know about trees? Something like nsINodelessChildCreator which
|
||||||
|
// could provide the current focus coordinates?
|
||||||
|
if (treeBox) {
|
||||||
|
treeBox->EnsureRowIsVisible(currentIndex);
|
||||||
|
PRInt32 firstVisibleRow, rowHeight;
|
||||||
|
treeBox->GetFirstVisibleRow(&firstVisibleRow);
|
||||||
|
treeBox->GetRowHeight(&rowHeight);
|
||||||
|
|
||||||
|
extraPixelsY = (currentIndex - firstVisibleRow + 1) * rowHeight;
|
||||||
|
istree = PR_TRUE;
|
||||||
|
|
||||||
|
nsCOMPtr<nsITreeColumns> cols;
|
||||||
|
treeBox->GetColumns(getter_AddRefs(cols));
|
||||||
|
if (cols) {
|
||||||
|
nsCOMPtr<nsITreeColumn> col;
|
||||||
|
cols->GetFirstColumn(getter_AddRefs(col));
|
||||||
|
if (col) {
|
||||||
|
nsCOMPtr<nsIDOMElement> colElement;
|
||||||
|
col->GetElement(getter_AddRefs(colElement));
|
||||||
|
nsCOMPtr<nsIContent> colContent(do_QueryInterface(colElement));
|
||||||
|
if (colContent) {
|
||||||
|
nsIFrame* frame = GetPrimaryFrameFor(colContent);
|
||||||
|
if (frame) {
|
||||||
|
extraTreeY = frame->GetSize().height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
multiSelect->GetCurrentItem(getter_AddRefs(item));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// don't check menulists as the selected item will be inside a popup.
|
||||||
|
nsCOMPtr<nsIDOMXULMenuListElement> menulist = do_QueryInterface(aCurrentEl);
|
||||||
|
if (!menulist) {
|
||||||
|
checkLineHeight = PR_FALSE;
|
||||||
|
nsCOMPtr<nsIDOMXULSelectControlElement> select =
|
||||||
|
do_QueryInterface(aCurrentEl);
|
||||||
|
if (select)
|
||||||
|
select->GetSelectedItem(getter_AddRefs(item));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item)
|
||||||
|
focusedContent = do_QueryInterface(item);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
nsIFrame *frame = GetPrimaryFrameFor(focusedContent);
|
||||||
|
if (frame) {
|
||||||
|
nsPoint frameOrigin(0, 0);
|
||||||
|
|
||||||
|
// Get the frame's origin within its view
|
||||||
|
nsIView *view = frame->GetClosestView(&frameOrigin);
|
||||||
|
NS_ASSERTION(view, "No view for frame");
|
||||||
|
|
||||||
|
nsIView *rootView = nsnull;
|
||||||
|
mViewManager->GetRootView(rootView);
|
||||||
|
NS_ASSERTION(rootView, "No root view in pres shell");
|
||||||
|
|
||||||
|
// View's origin within its root view
|
||||||
|
frameOrigin += view->GetOffsetTo(rootView);
|
||||||
|
|
||||||
|
// Start context menu down and to the right from top left of frame
|
||||||
|
// use the lineheight. This is a good distance to move the context
|
||||||
|
// menu away from the top left corner of the frame. If we always
|
||||||
|
// used the frame height, the context menu could end up far away,
|
||||||
|
// for example when we're focused on linked images.
|
||||||
|
// On the other hand, we want to use the frame height if it's less
|
||||||
|
// than the current line height, so that the context menu appears
|
||||||
|
// associated with the correct frame.
|
||||||
|
nscoord extra = 0;
|
||||||
|
if (!istree) {
|
||||||
|
extra = frame->GetSize().height;
|
||||||
|
if (checkLineHeight) {
|
||||||
|
nsIScrollableView *scrollView =
|
||||||
|
nsLayoutUtils::GetNearestScrollingView(view, nsLayoutUtils::eEither);
|
||||||
|
if (scrollView) {
|
||||||
|
nscoord scrollViewLineHeight;
|
||||||
|
scrollView->GetLineHeight(&scrollViewLineHeight);
|
||||||
|
if (extra > scrollViewLineHeight) {
|
||||||
|
extra = scrollViewLineHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nsPresContext* presContext = GetPresContext();
|
||||||
|
aTargetPt.x = presContext->AppUnitsToDevPixels(frameOrigin.x);
|
||||||
|
aTargetPt.y = presContext->AppUnitsToDevPixels(
|
||||||
|
frameOrigin.y + extra + extraTreeY) + extraPixelsY;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IF_ADDREF(*aTargetToUse = focusedContent);
|
||||||
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
PresShell::ResizeReflow(nsIView *aView, nscoord aWidth, nscoord aHeight)
|
PresShell::ResizeReflow(nsIView *aView, nscoord aWidth, nscoord aHeight)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
<hbox style="padding-left: 10px;">
|
<hbox style="padding-left: 10px;">
|
||||||
<spacer width="5"/>
|
<spacer width="5"/>
|
||||||
<richlistbox id="list" context="themenu" oncontextmenu="checkContextMenu(event)">
|
<richlistbox id="list" context="themenu" oncontextmenu="checkContextMenu(event)">
|
||||||
<richlistitem id="item1"><button label="One"/></richlistitem>
|
<richlistitem id="item1" style="padding-top: 3px;"><button label="One"/></richlistitem>
|
||||||
<richlistitem id="item2" height="22"><checkbox label="Checkbox"/></richlistitem>
|
<richlistitem id="item2" height="22"><checkbox label="Checkbox"/></richlistitem>
|
||||||
<richlistitem id="item3"><button label="Three"/></richlistitem>
|
<richlistitem id="item3"><button label="Three"/></richlistitem>
|
||||||
<richlistitem id="item4"><checkbox label="Four"/></richlistitem>
|
<richlistitem id="item4"><checkbox label="Four"/></richlistitem>
|
||||||
@@ -54,10 +54,20 @@
|
|||||||
</treeitem>
|
</treeitem>
|
||||||
</treechildren>
|
</treechildren>
|
||||||
</tree>
|
</tree>
|
||||||
|
|
||||||
|
<menu id="menu" label="Menu">
|
||||||
|
<menupopup id="menupopup" onpopupshown="menuTests()" onpopuphidden="nextTest()"
|
||||||
|
oncontextmenu="checkContextMenuForMenu(event)">
|
||||||
|
<menuitem id="menu1" label="Menu 1"/>
|
||||||
|
<menuitem id="menu2" label="Menu 2"/>
|
||||||
|
<menuitem id="menu3" label="Menu 3"/>
|
||||||
|
</menupopup>
|
||||||
|
</menu>
|
||||||
|
|
||||||
</hbox>
|
</hbox>
|
||||||
|
|
||||||
<menupopup id="themenu" onpopupshowing="if (gTestId == -1) event.preventDefault()"
|
<menupopup id="themenu" onpopupshowing="if (gTestId == -1) event.preventDefault()"
|
||||||
onpopupshown="checkPopup()" onpopuphidden="nextTest()">
|
onpopupshown="checkPopup()" onpopuphidden="setTimeout(nextTest, 0);">
|
||||||
<menuitem label="Item"/>
|
<menuitem label="Item"/>
|
||||||
</menupopup>
|
</menupopup>
|
||||||
|
|
||||||
@@ -69,23 +79,43 @@ SimpleTest.waitForExplicitFinish();
|
|||||||
var gTestId = -1;
|
var gTestId = -1;
|
||||||
var gTestElement = "list";
|
var gTestElement = "list";
|
||||||
var gSelectionStep = 0;
|
var gSelectionStep = 0;
|
||||||
|
var gContextMenuFired = false;
|
||||||
|
|
||||||
function startTest()
|
function startTest()
|
||||||
{
|
{
|
||||||
// first, check if the richlistbox selection changes on a contextmenu mouse event
|
// first, check if the richlistbox selection changes on a contextmenu mouse event
|
||||||
var element = $("list");
|
var element = $("list");
|
||||||
synthesizeMouse(element.getItemAtIndex(3), 7, 1, { type : "mousedown", button: 2, ctrlKey: true });
|
synthesizeMouse(element.getItemAtIndex(3), 7, 1, { type : "mousedown", button: 2, ctrlKey: true });
|
||||||
synthesizeMouse(element, 7, 1, { type : "contextmenu", button: 2 });
|
synthesizeMouse(element, 7, 2, { type : "contextmenu", button: 2 });
|
||||||
|
|
||||||
gSelectionStep++;
|
gSelectionStep++;
|
||||||
synthesizeMouse(element.getItemAtIndex(1), 7, 1, { type : "mousedown", button: 2, ctrlKey: true, shiftKey: true });
|
synthesizeMouse(element.getItemAtIndex(1), 7, 1, { type : "mousedown", button: 2, ctrlKey: true, shiftKey: true });
|
||||||
synthesizeMouse(element, 7, 1, { type : "contextmenu", button: 2 });
|
synthesizeMouse(element, 7, 2, { type : "contextmenu", button: 2 });
|
||||||
|
|
||||||
gSelectionStep++;
|
gSelectionStep++;
|
||||||
synthesizeMouse(element.getItemAtIndex(1), 7, 1, { type : "mousedown", button: 2 });
|
synthesizeMouse(element.getItemAtIndex(1), 7, 1, { type : "mousedown", button: 2 });
|
||||||
synthesizeMouse(element, 7, 1, { type : "contextmenu", button: 2 });
|
synthesizeMouse(element, 7, 2, { type : "contextmenu", button: 2 });
|
||||||
|
|
||||||
nextTest();
|
$("menu").open = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function menuTests()
|
||||||
|
{
|
||||||
|
// context menus shouldn't open when a menu is open on Mac
|
||||||
|
var ismac = (navigator.platform.indexOf("Mac") >= 0);
|
||||||
|
|
||||||
|
gSelectionStep = 0;
|
||||||
|
var element = $("menu");
|
||||||
|
synthesizeMouse(element, 0, 0, { type : "contextmenu", button: 0 });
|
||||||
|
is(gContextMenuFired, !ismac, "context menu fired when menu open");
|
||||||
|
|
||||||
|
if (!ismac) {
|
||||||
|
gSelectionStep = 1;
|
||||||
|
$("menu").boxObject.QueryInterface(Components.interfaces.nsIMenuBoxObject).activeChild = $("menu2");
|
||||||
|
synthesizeMouse(element, 0, 0, { type : "contextmenu", button: 0 });
|
||||||
|
}
|
||||||
|
|
||||||
|
$("menu").open = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function nextTest()
|
function nextTest()
|
||||||
@@ -101,7 +131,6 @@ function nextTest()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var element = $(gTestElement);
|
var element = $(gTestElement);
|
||||||
element.focus();
|
element.focus();
|
||||||
if (gTestId == 0) {
|
if (gTestId == 0) {
|
||||||
@@ -111,7 +140,7 @@ function nextTest()
|
|||||||
synthesizeMouse(element, 0, 0, { type : "contextmenu", button: 0 });
|
synthesizeMouse(element, 0, 0, { type : "contextmenu", button: 0 });
|
||||||
}
|
}
|
||||||
else if (gTestId == 1) {
|
else if (gTestId == 1) {
|
||||||
synthesizeMouse(element, 7, 1, { type : "contextmenu", button: 2 });
|
synthesizeMouse(element, 7, 2, { type : "contextmenu", button: 2 });
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
element.currentIndex = -1;
|
element.currentIndex = -1;
|
||||||
@@ -122,13 +151,52 @@ function nextTest()
|
|||||||
|
|
||||||
function checkContextMenu(event)
|
function checkContextMenu(event)
|
||||||
{
|
{
|
||||||
|
var rect = $(gTestElement).getBoundingClientRect();
|
||||||
|
|
||||||
|
var frombase = (gTestId == -1 || gTestId == 1);
|
||||||
|
if (!frombase)
|
||||||
|
rect = event.originalTarget.getBoundingClientRect();
|
||||||
|
left = frombase ? rect.left + 7 : rect.left;
|
||||||
|
top = frombase ? rect.top + 2 : rect.bottom;
|
||||||
|
|
||||||
|
is(event.clientX, left, gTestElement + " clientX " + gSelectionStep + " " + gTestId + "," + frombase);
|
||||||
|
is(event.clientY, top, gTestElement + " clientY " + gSelectionStep + " " + gTestId);
|
||||||
|
ok(event.screenX > left, gTestElement + " screenX " + gSelectionStep + " " + gTestId);
|
||||||
|
ok(event.screenY > top, gTestElement + " screenY " + gSelectionStep + " " + gTestId);
|
||||||
|
|
||||||
// context menu from mouse click
|
// context menu from mouse click
|
||||||
if (gTestId == -1) {
|
switch (gTestId) {
|
||||||
|
case -1:
|
||||||
var expected = gSelectionStep == 2 ? 1 : (navigator.platform.indexOf("Mac") >= 0 ? 3 : 0);
|
var expected = gSelectionStep == 2 ? 1 : (navigator.platform.indexOf("Mac") >= 0 ? 3 : 0);
|
||||||
is($(gTestElement).selectedIndex, expected, "index after click " + gSelectionStep);
|
is($(gTestElement).selectedIndex, expected, "index after click " + gSelectionStep);
|
||||||
|
break;
|
||||||
|
case 0:
|
||||||
|
if (gTestElement == "list")
|
||||||
|
is(event.originalTarget, $("item3"), "list selection target");
|
||||||
|
else
|
||||||
|
is(event.originalTarget, $("treechildren"), "tree selection target");
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
is(event.originalTarget.id, $("item1").id, "list mouse selection target");
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
is(event.originalTarget, $("list"), "list no selection target");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function checkContextMenuForMenu(event)
|
||||||
|
{
|
||||||
|
gContextMenuFired = true;
|
||||||
|
|
||||||
|
var popuprect = (gSelectionStep ? $("menu2") : $("menupopup")).getBoundingClientRect();
|
||||||
|
is(event.clientX, Math.floor(popuprect.left), "menu left " + gSelectionStep);
|
||||||
|
// the clientY is off by one sometimes on Windows (when loaded in the testing iframe
|
||||||
|
// but not when loaded separately) so just check for both cases for now
|
||||||
|
ok(event.clientY == Math.floor(popuprect.bottom) ||
|
||||||
|
event.clientY - 1 == Math.floor(popuprect.bottom), "menu top " + gSelectionStep);
|
||||||
|
}
|
||||||
|
|
||||||
function checkPopup()
|
function checkPopup()
|
||||||
{
|
{
|
||||||
var menurect = $("themenu").getBoundingClientRect();
|
var menurect = $("themenu").getBoundingClientRect();
|
||||||
@@ -159,7 +227,7 @@ function checkPopup()
|
|||||||
var elementrect = $(gTestElement).getBoundingClientRect();
|
var elementrect = $(gTestElement).getBoundingClientRect();
|
||||||
is(Math.round(menurect.left), Math.round(elementrect.left) + 9,
|
is(Math.round(menurect.left), Math.round(elementrect.left) + 9,
|
||||||
gTestElement + " mouse left");
|
gTestElement + " mouse left");
|
||||||
is(Math.round(menurect.top), Math.round(elementrect.top) + 3,
|
is(Math.round(menurect.top), Math.round(elementrect.top) + 4,
|
||||||
gTestElement + " mouse top");
|
gTestElement + " mouse top");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|||||||
Reference in New Issue
Block a user