diff --git a/content/events/src/nsContentEventHandler.cpp b/content/events/src/nsContentEventHandler.cpp index 9fa13a29356f..a299ca8aad0c 100644 --- a/content/events/src/nsContentEventHandler.cpp +++ b/content/events/src/nsContentEventHandler.cpp @@ -58,6 +58,7 @@ #include "nsISelectionController.h" #include "nsISelectionPrivate.h" #include "nsContentUtils.h" +#include "nsLayoutUtils.h" #include "nsISelection2.h" #include "nsIMEStateManager.h" @@ -730,6 +731,46 @@ nsContentEventHandler::OnQuerySelectionAsTransferable(nsQueryContentEvent* aEven return NS_OK; } +nsresult +nsContentEventHandler::OnQueryCharacterAtPoint(nsQueryContentEvent* aEvent) +{ + nsresult rv = Init(aEvent); + if (NS_FAILED(rv)) + return rv; + + nsIFrame* rootFrame = mPresShell->GetRootFrame(); + nsPoint ptInRoot = + nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, rootFrame); + nsIFrame* targetFrame = nsLayoutUtils::GetFrameForPoint(rootFrame, ptInRoot); + if (!targetFrame || targetFrame->GetType() != nsGkAtoms::textFrame) { + // there is no character at the point. + aEvent->mReply.mOffset = nsQueryContentEvent::NOT_FOUND; + aEvent->mSucceeded = PR_TRUE; + return NS_OK; + } + nsPoint ptInTarget = ptInRoot - targetFrame->GetOffsetTo(rootFrame); + nsTextFrame* textframe = static_cast(targetFrame); + nsIFrame::ContentOffsets offsets = + textframe->GetCharacterOffsetAtFramePoint(ptInTarget); + NS_ENSURE_TRUE(offsets.content, NS_ERROR_FAILURE); + PRUint32 nativeOffset; + rv = GetFlatTextOffsetOfRange(mRootContent, offsets.content, offsets.offset, + &nativeOffset); + NS_ENSURE_SUCCESS(rv, rv); + + nsQueryContentEvent textRect(PR_TRUE, NS_QUERY_TEXT_RECT, aEvent->widget); + textRect.InitForQueryTextRect(nativeOffset, 1); + rv = OnQueryTextRect(&textRect); + NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_TRUE(textRect.mSucceeded, NS_ERROR_FAILURE); + + // currently, we don't need to get the actual text. + aEvent->mReply.mOffset = nativeOffset; + aEvent->mReply.mRect = textRect.mReply.mRect; + aEvent->mSucceeded = PR_TRUE; + return NS_OK; +} + nsresult nsContentEventHandler::GetFlatTextOffsetOfRange(nsIContent* aRootContent, nsINode* aNode, diff --git a/content/events/src/nsContentEventHandler.h b/content/events/src/nsContentEventHandler.h index 2f862ede5db0..44de920c7f5d 100644 --- a/content/events/src/nsContentEventHandler.h +++ b/content/events/src/nsContentEventHandler.h @@ -81,6 +81,8 @@ public: nsresult OnQueryContentState(nsQueryContentEvent* aEvent); // NS_QUERY_SELECTION_AS_TRANSFERABLE event handler nsresult OnQuerySelectionAsTransferable(nsQueryContentEvent* aEvent); + // NS_QUERY_CHARACTER_AT_POINT event handler + nsresult OnQueryCharacterAtPoint(nsQueryContentEvent* aEvent); // NS_SELECTION_* event nsresult OnSelectionEvent(nsSelectionEvent* aEvent); diff --git a/content/events/src/nsEventStateManager.cpp b/content/events/src/nsEventStateManager.cpp index 6e40cd137f04..ee3cdfbe15b0 100644 --- a/content/events/src/nsEventStateManager.cpp +++ b/content/events/src/nsEventStateManager.cpp @@ -1758,6 +1758,12 @@ nsEventStateManager::PreHandleEvent(nsPresContext* aPresContext, handler.OnQuerySelectionAsTransferable(static_cast(aEvent)); } break; + case NS_QUERY_CHARACTER_AT_POINT: + { + nsContentEventHandler handler(mPresContext); + handler.OnQueryCharacterAtPoint(static_cast(aEvent)); + } + break; case NS_SELECTION_SET: { nsContentEventHandler handler(mPresContext); diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index d7cacc2944b1..9cbf1f2be714 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -630,7 +630,8 @@ nsLayoutUtils::GetEventCoordinatesRelativeTo(const nsEvent* aEvent, nsIFrame* aF if (!aEvent || (aEvent->eventStructType != NS_MOUSE_EVENT && aEvent->eventStructType != NS_MOUSE_SCROLL_EVENT && aEvent->eventStructType != NS_DRAG_EVENT && - aEvent->eventStructType != NS_SIMPLE_GESTURE_EVENT)) + aEvent->eventStructType != NS_SIMPLE_GESTURE_EVENT && + aEvent->eventStructType != NS_QUERY_CONTENT_EVENT)) return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); const nsGUIEvent* GUIEvent = static_cast(aEvent); diff --git a/layout/generic/nsTextFrame.h b/layout/generic/nsTextFrame.h index ab7791c0fbdb..d8952d52355b 100644 --- a/layout/generic/nsTextFrame.h +++ b/layout/generic/nsTextFrame.h @@ -153,7 +153,8 @@ public: #endif virtual ContentOffsets CalcContentOffsetsFromFramePoint(nsPoint aPoint); - + ContentOffsets GetCharacterOffsetAtFramePoint(const nsPoint &aPoint); + NS_IMETHOD SetSelected(nsPresContext* aPresContext, nsIDOMRange *aRange, PRBool aSelected, @@ -448,6 +449,9 @@ protected: nsRect& aRect); PRBool IsFloatingFirstLetterChild(); + + ContentOffsets GetCharacterOffsetAtFramePointInternal(const nsPoint &aPoint, + PRBool aForInsertionPoint); }; #endif diff --git a/layout/generic/nsTextFrameThebes.cpp b/layout/generic/nsTextFrameThebes.cpp index 80d48e3ca702..7507594e1d26 100644 --- a/layout/generic/nsTextFrameThebes.cpp +++ b/layout/generic/nsTextFrameThebes.cpp @@ -4812,7 +4812,21 @@ CountCharsFit(gfxTextRun* aTextRun, PRUint32 aStart, PRUint32 aLength, } nsIFrame::ContentOffsets -nsTextFrame::CalcContentOffsetsFromFramePoint(nsPoint aPoint) { +nsTextFrame::CalcContentOffsetsFromFramePoint(nsPoint aPoint) +{ + return GetCharacterOffsetAtFramePointInternal(aPoint, PR_TRUE); +} + +nsIFrame::ContentOffsets +nsTextFrame::GetCharacterOffsetAtFramePoint(const nsPoint &aPoint) +{ + return GetCharacterOffsetAtFramePointInternal(aPoint, PR_FALSE); +} + +nsIFrame::ContentOffsets +nsTextFrame::GetCharacterOffsetAtFramePointInternal(const nsPoint &aPoint, + PRBool aForInsertionPoint) +{ ContentOffsets offsets; gfxSkipCharsIterator iter = EnsureTextRun(); @@ -4844,7 +4858,7 @@ nsTextFrame::CalcContentOffsetsFromFramePoint(nsPoint aPoint) { mTextRun->GetAdvanceWidth(extraCluster.GetSkippedOffset(), GetSkippedDistance(extraCluster, extraClusterLastChar) + 1, &provider); - selectedOffset = width <= fitWidth + charWidth/2 + selectedOffset = !aForInsertionPoint || width <= fitWidth + charWidth/2 ? extraCluster.GetOriginalOffset() : extraClusterLastChar.GetOriginalOffset() + 1; } else { diff --git a/widget/public/nsGUIEvent.h b/widget/public/nsGUIEvent.h index 1329c108c4a3..d0ceb15d7366 100644 --- a/widget/public/nsGUIEvent.h +++ b/widget/public/nsGUIEvent.h @@ -362,12 +362,15 @@ class nsHashKey; #define NS_QUERY_TEXT_RECT (NS_QUERY_CONTENT_EVENT_START + 4) // Query for the bounding rect of the current focused frame. Result is relative // to top level widget coordinates -#define NS_QUERY_EDITOR_RECT (NS_QUERY_CONTENT_EVENT_START + 5) +#define NS_QUERY_EDITOR_RECT (NS_QUERY_CONTENT_EVENT_START + 5) // Query for the current state of the content. The particular members of // mReply that are set for each query content event will be valid on success. -#define NS_QUERY_CONTENT_STATE (NS_QUERY_CONTENT_EVENT_START + 6) +#define NS_QUERY_CONTENT_STATE (NS_QUERY_CONTENT_EVENT_START + 6) // Query for the selection in the form of a nsITransferable. #define NS_QUERY_SELECTION_AS_TRANSFERABLE (NS_QUERY_CONTENT_EVENT_START + 7) +// Query for character at a point. This returns the character offset and its +// rect. The point is specified by nsEvent::refPoint. +#define NS_QUERY_CHARACTER_AT_POINT (NS_QUERY_CONTENT_EVENT_START + 8) // Video events #ifdef MOZ_MEDIA @@ -1124,6 +1127,10 @@ public: // used by NS_QUERY_SELECTION_AS_TRANSFERABLE nsCOMPtr mTransferable; } mReply; + + enum { + NOT_FOUND = PR_UINT32_MAX + }; }; class nsSelectionEvent : public nsGUIEvent @@ -1381,7 +1388,10 @@ enum nsDragDropEventStatus { ((evnt)->message == NS_QUERY_TEXT_CONTENT) || \ ((evnt)->message == NS_QUERY_CARET_RECT) || \ ((evnt)->message == NS_QUERY_TEXT_RECT) || \ - ((evnt)->message == NS_QUERY_EDITOR_RECT)) + ((evnt)->message == NS_QUERY_EDITOR_RECT) || \ + ((evnt)->message == NS_QUERY_CONTENT_STATE) || \ + ((evnt)->message == NS_QUERY_SELECTION_AS_TRANSFERABLE) || \ + ((evnt)->message == NS_QUERY_CHARACTER_AT_POINT)) #define NS_IS_SELECTION_EVENT(evnt) \ (((evnt)->message == NS_SELECTION_SET)) diff --git a/widget/src/windows/nsIMM32Handler.cpp b/widget/src/windows/nsIMM32Handler.cpp index 574881ed9bd0..bc1a5246a4a1 100644 --- a/widget/src/windows/nsIMM32Handler.cpp +++ b/widget/src/windows/nsIMM32Handler.cpp @@ -205,7 +205,7 @@ nsIMM32Handler::GetKeyboardCodePage() #define NO_IME_CARET -1 nsIMM32Handler::nsIMM32Handler() : - mCursorPosition(NO_IME_CARET), mIsComposing(PR_FALSE), + mCursorPosition(NO_IME_CARET), mCompositionStart(0), mIsComposing(PR_FALSE), mNativeCaretIsCreated(PR_FALSE) { PR_LOG(gIMM32Log, PR_LOG_ALWAYS, ("IMM32: nsIMM32Handler is created\n")); @@ -645,42 +645,29 @@ nsIMM32Handler::HandleStartComposition(nsWindow* aWindow, NS_PRECONDITION(!aWindow->PluginHasFocus(), "HandleStartComposition should not be called when a plug-in has focus"); + nsQueryContentEvent selection(PR_TRUE, NS_QUERY_SELECTED_TEXT, aWindow); + aWindow->InitEvent(selection, &nsIntPoint(0, 0)); + aWindow->DispatchWindowEvent(&selection); + if (!selection.mSucceeded) { + PR_LOG(gIMM32Log, PR_LOG_ALWAYS, + ("IMM32: HandleStartComposition, FAILED (NS_QUERY_SELECTED_TEXT)\n")); + return; + } + + mCompositionStart = selection.mReply.mOffset; + nsCompositionEvent event(PR_TRUE, NS_COMPOSITION_START, aWindow); nsIntPoint point(0, 0); aWindow->InitEvent(event, &point); aWindow->DispatchWindowEvent(&event); - // - // Post process event - // - SetIMERelatedWindowsPos(aWindow, aIMEContext); mIsComposing = PR_TRUE; PR_LOG(gIMM32Log, PR_LOG_ALWAYS, - ("IMM32: HandleStartComposition, START composition\n")); - - if (event.theReply.mCursorPosition.width <= 0 && - event.theReply.mCursorPosition.height <= 0) { - PR_LOG(gIMM32Log, PR_LOG_ALWAYS, - ("IMM32: HandleStartComposition, mCursorPosition is empty\n")); - // for some reason we don't know yet, theReply may contain invalid result - // need more debugging in nsCaret to find out the reason - // the best we can do now is to ignore the invalid result - return; - } - - nsIntRect cursorPosition; - ResolveIMECaretPos(event.theReply.mReferenceWidget, - event.theReply.mCursorPosition, aWindow, cursorPosition); - -#ifdef ENABLE_IME_MOUSE_HANDLING - memset(mCompCharPos, -1, sizeof(RECT) * IME_MAX_CHAR_POS); - mCompCharPos[0].left = cursorPosition.x; - mCompCharPos[0].top = cursorPosition.y; - mCompCharPos[0].bottom = cursorPosition.YMost(); -#endif // ENABLE_IME_MOUSE_HANDLING + ("IMM32: HandleStartComposition, START composition, mCompositionStart=%ld\n", + mCompositionStart)); } PRBool @@ -1126,45 +1113,7 @@ nsIMM32Handler::DispatchTextEvent(nsWindow* aWindow, aWindow->DispatchWindowEvent(&event); - // - // Post process event - // - SetIMERelatedWindowsPos(aWindow, aIMEContext); - - if (event.theReply.mCursorPosition.width <= 0 && - event.theReply.mCursorPosition.height <= 0) { - PR_LOG(gIMM32Log, PR_LOG_ALWAYS, - ("IMM32: DispatchTextEvent, mCursorPosition is empty\n")); - // for some reason we don't know yet, theReply may contain invalid result - // need more debugging in nsCaret to find out the reason - // the best we can do now is to ignore the invalid result - return; - } - - nsIntRect cursorPosition; - ResolveIMECaretPos(event.theReply.mReferenceWidget, - event.theReply.mCursorPosition, aWindow, cursorPosition); - -#ifdef ENABLE_IME_MOUSE_HANDLING - if (mCursorPosition <= 0 || mCursorPosition >= IME_MAX_CHAR_POS) { - return; - } - - // Record previous composing char position - // The cursor is always on the right char before it, but not necessarily on - // the left of next char, as what happens in wrapping. - mCompCharPos[mCursorPosition-1].right = cursorPosition.x; - mCompCharPos[mCursorPosition-1].top = cursorPosition.y; - mCompCharPos[mCursorPosition-1].bottom = cursorPosition.YMost(); - if (mCompCharPos[mCursorPosition-1].top != cursorPosition.y) { - // wrapping, invalidate left position - mCompCharPos[mCursorPosition-1].left = -1; - } - mCompCharPos[mCursorPosition].left = cursorPosition.x; - mCompCharPos[mCursorPosition].top = cursorPosition.y; - mCompCharPos[mCursorPosition].bottom = cursorPosition.YMost(); -#endif // ENABLE_IME_MOUSE_HANDLING } void @@ -1475,10 +1424,6 @@ nsIMM32Handler::ResolveIMECaretPos(nsIWidget* aReferenceWidget, #ifdef ENABLE_IME_MOUSE_HANDLING -#define PT_IN_RECT(pt, rc) \ - ((pt).x>(rc).left && (pt).x <(rc).right && \ - (pt).y>(rc).top && (pt).y<(rc).bottom) - PRBool nsIMM32Handler::OnMouseEvent(nsWindow* aWindow, LPARAM lParam, int aAction) { @@ -1486,38 +1431,37 @@ nsIMM32Handler::OnMouseEvent(nsWindow* aWindow, LPARAM lParam, int aAction) return PR_FALSE; } - POINT ptPos; - ptPos.x = (short)LOWORD(lParam); - ptPos.y = (short)HIWORD(lParam); - - if (!IMECompositionHitTest(ptPos)) { + nsIntPoint cursor(LOWORD(lParam), HIWORD(lParam)); + nsQueryContentEvent charAtPt(PR_TRUE, NS_QUERY_CHARACTER_AT_POINT, aWindow); + aWindow->InitEvent(charAtPt, &cursor); + aWindow->DispatchWindowEvent(&charAtPt); + if (!charAtPt.mSucceeded || + charAtPt.mReply.mOffset == nsQueryContentEvent::NOT_FOUND || + charAtPt.mReply.mOffset < mCompositionStart || + charAtPt.mReply.mOffset > + mCompositionStart + mCompositionString.Length()) { return PR_FALSE; } - int positioning = 0; - int offset = 0; - // calcurate positioning and offset // char : JCH1|JCH2|JCH3 // offset: 0011 1122 2233 // positioning: 2301 2301 2301 + nsIntRect cursorInTopLevel; + ResolveIMECaretPos(aWindow, nsIntRect(cursor, nsIntSize(0, 0)), + aWindow->GetTopLevelWindow(PR_FALSE), cursorInTopLevel); + PRInt32 cursorXInChar = cursorInTopLevel.x - charAtPt.mReply.mRect.x; + int positioning = cursorXInChar * 4 / charAtPt.mReply.mRect.width; + positioning = (positioning + 2) % 4; - // Note: hitText has been done, so no check of mCompCharPos - // and composing char maximum limit is necessary. - PRUint32 len = mCompositionString.Length(); - PRUint32 i = 0; - for (i = 0; i < len; ++i) { - if (PT_IN_RECT(ptPos, mCompCharPos[i])) - break; - } - offset = i; - if (ptPos.x - mCompCharPos[i].left > mCompCharPos[i].right - ptPos.x) { + int offset = charAtPt.mReply.mOffset - mCompositionStart; + if (positioning < 2) { offset++; } - positioning = (ptPos.x - mCompCharPos[i].left) * 4 / - (mCompCharPos[i].right - mCompCharPos[i].left); - positioning = (positioning + 2) % 4; + PR_LOG(gIMM32Log, PR_LOG_ALWAYS, + ("IMM32: OnMouseEvent, x,y=%ld,%ld, offset=%ld, positioning=%ld\n", + cursor.x, cursor.y, offset, positioning)); // send MS_MSIME_MOUSE message to default IME window. HWND imeWnd = ::ImmGetDefaultIMEWnd(aWindow->GetWindowHandle()); @@ -1527,45 +1471,4 @@ nsIMM32Handler::OnMouseEvent(nsWindow* aWindow, LPARAM lParam, int aAction) (LPARAM) IMEContext.get()) == 1; } -//The coordinate is relative to the upper-left corner of the client area. -PRBool -nsIMM32Handler::IMECompositionHitTest(const POINT& aPos) -{ - // figure out how many char in composing string, - // but keep it below the limit we can handle - PRInt32 len = mCompositionString.Length(); - if (len > IME_MAX_CHAR_POS) - len = IME_MAX_CHAR_POS; - - PRInt32 i; - PRInt32 aveWidth = 0; - // found per char width - for (i = 0; i < len; i++) { - if (mCompCharPos[i].left >= 0 && mCompCharPos[i].right > 0) { - aveWidth = mCompCharPos[i].right - mCompCharPos[i].left; - break; - } - } - - // validate each rect and test - for (i = 0; i < len; i++) { - if (mCompCharPos[i].left < 0) { - if (i != 0 && mCompCharPos[i-1].top == mCompCharPos[i].top) - mCompCharPos[i].left = mCompCharPos[i-1].right; - else - mCompCharPos[i].left = mCompCharPos[i].right - aveWidth; - } - if (mCompCharPos[i].right < 0) - mCompCharPos[i].right = mCompCharPos[i].left + aveWidth; - if (mCompCharPos[i].top < 0) { - mCompCharPos[i].top = mCompCharPos[i-1].top; - mCompCharPos[i].bottom = mCompCharPos[i-1].bottom; - } - - if (PT_IN_RECT(aPos, mCompCharPos[i])) { - return PR_TRUE; - } - } - return PR_FALSE; -} #endif // ENABLE_IME_MOUSE_HANDLING diff --git a/widget/src/windows/nsIMM32Handler.h b/widget/src/windows/nsIMM32Handler.h index 5d93560df680..493f889b4904 100644 --- a/widget/src/windows/nsIMM32Handler.h +++ b/widget/src/windows/nsIMM32Handler.h @@ -208,6 +208,7 @@ protected: nsTArray mAttributeArray; PRInt32 mCursorPosition; + PRUint32 mCompositionStart; PRPackedBool mIsComposing; PRPackedBool mNativeCaretIsCreated; @@ -221,15 +222,7 @@ protected: #endif // #ifndef WINCE #ifdef ENABLE_IME_MOUSE_HANDLING - -#define IME_MAX_CHAR_POS 64 - // For describing composing frame - // XXX mnakano - We should remove this, because its value may be wrong in - // some cases, and we should query it when it is needed. - RECT mCompCharPos[IME_MAX_CHAR_POS]; - PRBool OnMouseEvent(nsWindow* aWindow, LPARAM lParam, int aAction); - PRBool IMECompositionHitTest(const POINT& aPos); #endif // ENABLE_IME_MOUSE_HANDLING }; diff --git a/widget/src/windows/nsToolkit.h b/widget/src/windows/nsToolkit.h index ec3318517038..905b053e9f9e 100644 --- a/widget/src/windows/nsToolkit.h +++ b/widget/src/windows/nsToolkit.h @@ -169,7 +169,8 @@ private: //------------------------------------------------------------------------- // -// from http://msdn.microsoft.com/library/en-us/dnime/html/msime.asp +// from http://download.microsoft.com/download/6/0/9/60908e9e-d2c1-47db-98f6-216af76a235f/msime.h +// The document for this has been removed from MSDN... // //------------------------------------------------------------------------- diff --git a/widget/tests/TestWinTSF.cpp b/widget/tests/TestWinTSF.cpp index 80bfaa68d997..2fbfd3bd1417 100644 --- a/widget/tests/TestWinTSF.cpp +++ b/widget/tests/TestWinTSF.cpp @@ -88,6 +88,7 @@ class nsAFlatCString; #include "nsISelectionController.h" #include "nsIViewManager.h" #include "nsTArray.h" +#include "nsGUIEvent.h" #ifndef MOZILLA_INTERNAL_API #undef nsString_h___ @@ -129,6 +130,7 @@ protected: PRBool TestExtents(void); PRBool TestComposition(void); PRBool TestNotification(void); + PRBool TestContentEvents(void); PRBool TestApp::TestSelectionInternal(char* aTestName, LONG aStart, @@ -1642,6 +1644,10 @@ TestApp::OnStateChange(nsIWebProgress *aWebProgress, NS_ASSERTION(aStateFlags & nsIWebProgressListener::STATE_IS_WINDOW && aStateFlags & nsIWebProgressListener::STATE_STOP, "wrong state"); if (NS_SUCCEEDED(Init())) { + printf("Testing content events...\n"); + if (TestContentEvents()) + passed("TestContentEvents"); + if (RunTest(&TestApp::TestFocus, PR_FALSE)) passed("TestFocus"); @@ -2704,6 +2710,155 @@ TestApp::TestNotification(void) return PR_TRUE; } +PRBool +TestApp::TestContentEvents(void) +{ + mTestString = NS_LITERAL_STRING( + "This is a test of the\r\nContent Events"); + // 0123456789012345678901 2 34567890123456 + // 0 1 2 3 + mTextArea->SetValue(mTestString); + mTextArea->Focus(); + + nsCOMPtr widget; + nsCOMPtr docShell; + nsresult nsr = mWindow->GetDocShell(getter_AddRefs(docShell)); + if (NS_SUCCEEDED(nsr) && docShell) { + nsCOMPtr presShell; + nsr = docShell->GetPresShell(getter_AddRefs(presShell)); + if (NS_SUCCEEDED(nsr) && presShell) { + nsCOMPtr viewManager = presShell->GetViewManager(); + if (viewManager) { + nsr = viewManager->GetWidget(getter_AddRefs(widget)); + } + } + } + if (NS_FAILED(nsr) || !widget) { + fail("TestContentEvents: get nsIWidget"); + return PR_FALSE; + } + + nsCOMPtr topLevel = widget->GetTopLevelWidget(nsnull); + if (!topLevel) { + fail("TestContentEvents: get top level widget"); + return PR_FALSE; + } + + nsIntRect widgetRect, topLevelRect; + nsr = widget->GetScreenBounds(widgetRect); + if (NS_FAILED(nsr)) { + fail("TestContentEvents: get widget rect"); + return PR_FALSE; + } + nsr = topLevel->GetScreenBounds(topLevelRect); + if (NS_FAILED(nsr)) { + fail("TestContentEvents: get top level widget rect"); + return PR_FALSE; + } + nsIntPoint widgetOffset = widgetRect.TopLeft() - topLevelRect.TopLeft(); + nsEventStatus eventStatus; + PRBool result = PR_TRUE; + + const PRUint32 kNone = nsQueryContentEvent::NOT_FOUND; + PRUint32 testingOffset[] = { 0, 10, 20, 23, 36 }; + PRUint32 leftSideOffset[] = { kNone, 9, 19, kNone, 35 }; + PRUint32 rightSideOffset[] = { 1, 11, kNone, 24, kNone }; + for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(testingOffset); i++) { + nsQueryContentEvent textRect(PR_TRUE, NS_QUERY_TEXT_RECT, widget); + textRect.InitForQueryTextRect(testingOffset[i], 1); + nsr = widget->DispatchEvent(&textRect, eventStatus); + if (NS_FAILED(nsr) || !textRect.mSucceeded || + textRect.mReply.mRect.IsEmpty()) { + fail("TestContentEvents: get text rect"); + return PR_FALSE; + } + nsIntRect &charRect = textRect.mReply.mRect; + charRect.MoveBy(widgetOffset); + // Note that charRect might be inflated at rounding to pixels! + printf("TestContentEvents: testing... i=%lu, pt={ %ld, %ld }, size={ %ld, %ld }\n", + i, charRect.x, charRect.y, charRect.width, charRect.height); + + nsQueryContentEvent charAtPt1(PR_TRUE, NS_QUERY_CHARACTER_AT_POINT, widget); + charAtPt1.refPoint.x = charRect.x + 1; + charAtPt1.refPoint.y = charRect.y + 1; + nsr = widget->DispatchEvent(&charAtPt1, eventStatus); + if (NS_FAILED(nsr) || !charAtPt1.mSucceeded) { + fail(" TestContentEvents: get char at point1"); + return PR_FALSE; + } + printf(" NS_QUERY_CHARACTER_AT_POINT: pt={ %ld, %ld }, offset=%lu, rect={ %ld, %ld, %ld, %ld }\n", + charAtPt1.refPoint.x, charAtPt1.refPoint.y, + charAtPt1.mReply.mOffset, charAtPt1.mReply.mRect.x, + charAtPt1.mReply.mRect.y, charAtPt1.mReply.mRect.width, + charAtPt1.mReply.mRect.height); + if (charAtPt1.mReply.mOffset != testingOffset[i]) { + fail(" TestContentEvents: get char at point1 (wrong offset)"); + result = PR_FALSE; + } else if (charAtPt1.mReply.mRect != textRect.mReply.mRect) { + fail(" TestContentEvents: get char at point1 (rect mismatch)"); + result = PR_FALSE; + } + + nsQueryContentEvent charAtPt2(PR_TRUE, NS_QUERY_CHARACTER_AT_POINT, widget); + charAtPt2.refPoint.x = charRect.XMost() - 2; + charAtPt2.refPoint.y = charRect.YMost() - 2; + nsr = widget->DispatchEvent(&charAtPt2, eventStatus); + if (NS_FAILED(nsr) || !charAtPt2.mSucceeded) { + fail(" TestContentEvents: get char at point2"); + return PR_FALSE; + } + printf(" NS_QUERY_CHARACTER_AT_POINT: pt={ %ld, %ld }, offset=%lu, rect={ %ld, %ld, %ld, %ld }\n", + charAtPt2.refPoint.x, charAtPt2.refPoint.y, + charAtPt2.mReply.mOffset, charAtPt2.mReply.mRect.x, + charAtPt2.mReply.mRect.y, charAtPt2.mReply.mRect.width, + charAtPt2.mReply.mRect.height); + if (charAtPt2.mReply.mOffset != testingOffset[i]) { + fail(" TestContentEvents: get char at point2 (wrong offset)"); + result = PR_FALSE; + } else if (charAtPt2.mReply.mRect != textRect.mReply.mRect) { + fail(" TestContentEvents: get char at point2 (rect mismatch)"); + result = PR_FALSE; + } + + nsQueryContentEvent charAtPt3(PR_TRUE, NS_QUERY_CHARACTER_AT_POINT, widget); + charAtPt3.refPoint.x = charRect.x - 2; + charAtPt3.refPoint.y = charRect.y + 1; + nsr = widget->DispatchEvent(&charAtPt3, eventStatus); + if (NS_FAILED(nsr) || !charAtPt3.mSucceeded) { + fail(" TestContentEvents: get char at point3"); + return PR_FALSE; + } + printf(" NS_QUERY_CHARACTER_AT_POINT: pt={ %ld, %ld }, offset=%lu, rect={ %ld, %ld, %ld, %ld }\n", + charAtPt3.refPoint.x, charAtPt3.refPoint.y, + charAtPt3.mReply.mOffset, charAtPt3.mReply.mRect.x, + charAtPt3.mReply.mRect.y, charAtPt3.mReply.mRect.width, + charAtPt3.mReply.mRect.height); + if (charAtPt3.mReply.mOffset != leftSideOffset[i]) { + fail(" TestContentEvents: get left side char at point (wrong offset)"); + result = PR_FALSE; + } + + nsQueryContentEvent charAtPt4(PR_TRUE, NS_QUERY_CHARACTER_AT_POINT, widget); + charAtPt4.refPoint.x = charRect.XMost() + 1; + charAtPt4.refPoint.y = charRect.YMost() - 2; + nsr = widget->DispatchEvent(&charAtPt4, eventStatus); + if (NS_FAILED(nsr) || !charAtPt4.mSucceeded) { + fail(" TestContentEvents: get char at point4"); + return PR_FALSE; + } + printf(" NS_QUERY_CHARACTER_AT_POINT: pt={ %ld, %ld }, offset=%lu, rect={ %ld, %ld, %ld, %ld }\n", + charAtPt4.refPoint.x, charAtPt4.refPoint.y, + charAtPt4.mReply.mOffset, charAtPt4.mReply.mRect.x, + charAtPt4.mReply.mRect.y, charAtPt4.mReply.mRect.width, + charAtPt4.mReply.mRect.height); + if (charAtPt4.mReply.mOffset != rightSideOffset[i]) { + fail(" TestContentEvents: get right side char at point4 (wrong offset)"); + result = PR_FALSE; + } + } + return result; +} + nsresult TestApp::GetSelCon(nsISelectionController** aSelCon) {