diff --git a/content/base/src/nsGenericElement.cpp b/content/base/src/nsGenericElement.cpp index 5fa68a145b32..471108ed33f9 100644 --- a/content/base/src/nsGenericElement.cpp +++ b/content/base/src/nsGenericElement.cpp @@ -118,7 +118,7 @@ #include "nsIDOMNSFeatureFactory.h" #include "nsIDOMDocumentType.h" #include "nsIDOMUserDataHandler.h" -#include "nsGenericHTMLElement.h" +#include "nsIDOMNSEditableElement.h" #include "nsIEditor.h" #include "nsIEditorDocShell.h" #include "nsEventDispatcher.h" @@ -343,15 +343,13 @@ nsINode::GetTextEditorRootContent(nsIEditor** aEditor) if (aEditor) *aEditor = nsnull; for (nsINode* node = this; node; node = node->GetNodeParent()) { - if (!node->IsNodeOfType(eHTML)) + nsCOMPtr editableElement(do_QueryInterface(node)); + if (!editableElement) continue; nsCOMPtr editor; - static_cast(node)-> - GetEditorInternal(getter_AddRefs(editor)); - if (!editor) - continue; - + editableElement->GetEditor(getter_AddRefs(editor)); + NS_ENSURE_TRUE(editor, nsnull); nsIContent* rootContent = GetEditorRootContent(editor); if (aEditor) editor.swap(*aEditor); diff --git a/content/base/src/nsGkAtomList.h b/content/base/src/nsGkAtomList.h index c856ad8bb66a..ad16a6b1162a 100644 --- a/content/base/src/nsGkAtomList.h +++ b/content/base/src/nsGkAtomList.h @@ -62,9 +62,7 @@ //--------------------------------------------------------------------------- GK_ATOM(_empty, "") -GK_ATOM(moz, "_moz") GK_ATOM(mozdirty, "_moz_dirty") -GK_ATOM(mozeditorbogusnode, "_moz_editor_bogus_node") GK_ATOM(mozgeneratedcontentbefore, "_moz_generated_content_before") GK_ATOM(mozgeneratedcontentafter, "_moz_generated_content_after") GK_ATOM(mozgeneratedcontentimage, "_moz_generated_content_image") diff --git a/content/events/src/Makefile.in b/content/events/src/Makefile.in index 6d69bddef2a7..7c5e73c1cf3c 100644 --- a/content/events/src/Makefile.in +++ b/content/events/src/Makefile.in @@ -91,7 +91,7 @@ CPPSRCS = \ nsPLDOMEvent.cpp \ nsEventDispatcher.cpp \ nsIMEStateManager.cpp \ - nsContentEventHandler.cpp \ + nsQueryContentEventHandler.cpp \ nsDOMProgressEvent.cpp \ nsDOMDataTransfer.cpp \ nsDOMNotifyPaintEvent.cpp \ diff --git a/content/events/src/nsEventStateManager.cpp b/content/events/src/nsEventStateManager.cpp index fa83d6bfe0c4..bf82ccafdc39 100644 --- a/content/events/src/nsEventStateManager.cpp +++ b/content/events/src/nsEventStateManager.cpp @@ -28,7 +28,6 @@ * Ginn Chen * Simon Bünzli * Ehsan Akhgari - * Ningjie Chen * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), @@ -48,7 +47,7 @@ #include "nsEventStateManager.h" #include "nsEventListenerManager.h" #include "nsIMEStateManager.h" -#include "nsContentEventHandler.h" +#include "nsQueryContentEventHandler.h" #include "nsIContent.h" #include "nsINodeInfo.h" #include "nsIDocument.h" @@ -960,8 +959,6 @@ nsEventStateManager::PreHandleEvent(nsPresContext* aPresContext, break; if (mDocument) { - nsIMEStateManager::OnTextStateBlur(mPresContext, mCurrentFocus); - if (gLastFocusedDocument && gLastFocusedPresContextWeak) { nsCOMPtr ourWindow = gLastFocusedDocument->GetWindow(); @@ -1079,8 +1076,6 @@ nsEventStateManager::PreHandleEvent(nsPresContext* aPresContext, NS_IF_RELEASE(gLastFocusedContent); gLastFocusedContent = mCurrentFocus; NS_IF_ADDREF(gLastFocusedContent); - - nsIMEStateManager::OnTextStateFocus(mPresContext, mCurrentFocus); } // Try to keep the focus controllers and the globals in synch @@ -1152,8 +1147,6 @@ nsEventStateManager::PreHandleEvent(nsPresContext* aPresContext, NS_RELEASE(gLastFocusedContent); } - nsIMEStateManager::OnTextStateBlur(nsnull, nsnull); - // Now fire blurs. We fire a blur on the focused document, element, // and window. @@ -1327,8 +1320,6 @@ nsEventStateManager::PreHandleEvent(nsPresContext* aPresContext, if (focusController) focusController->SetSuppressFocus(PR_TRUE, "Deactivate Suppression"); - nsIMEStateManager::OnTextStateBlur(nsnull, nsnull); - // Now fire blurs. Blur the content, then the document, then the window. if (gLastFocusedDocument && gLastFocusedDocument == mDocument && @@ -1503,40 +1494,28 @@ nsEventStateManager::PreHandleEvent(nsPresContext* aPresContext, break; case NS_QUERY_SELECTED_TEXT: { - nsContentEventHandler handler(mPresContext); + nsQueryContentEventHandler handler(mPresContext); handler.OnQuerySelectedText((nsQueryContentEvent*)aEvent); } break; case NS_QUERY_TEXT_CONTENT: { - nsContentEventHandler handler(mPresContext); + nsQueryContentEventHandler handler(mPresContext); handler.OnQueryTextContent((nsQueryContentEvent*)aEvent); } break; + case NS_QUERY_CHARACTER_RECT: + { + nsQueryContentEventHandler handler(mPresContext); + handler.OnQueryCharacterRect((nsQueryContentEvent*)aEvent); + } + break; case NS_QUERY_CARET_RECT: { - nsContentEventHandler handler(mPresContext); + nsQueryContentEventHandler handler(mPresContext); handler.OnQueryCaretRect((nsQueryContentEvent*)aEvent); } break; - case NS_QUERY_TEXT_RECT: - { - nsContentEventHandler handler(mPresContext); - handler.OnQueryTextRect((nsQueryContentEvent*)aEvent); - } - break; - case NS_QUERY_EDITOR_RECT: - { - nsContentEventHandler handler(mPresContext); - handler.OnQueryEditorRect((nsQueryContentEvent*)aEvent); - } - break; - case NS_SELECTION_SET: - { - nsContentEventHandler handler(mPresContext); - handler.OnSelectionEvent((nsSelectionEvent*)aEvent); - } - break; } return NS_OK; } @@ -5052,8 +5031,6 @@ nsEventStateManager::SendFocusBlur(nsPresContext* aPresContext, // Track the old focus controller if any focus suppressions is used on it. nsFocusSuppressor oldFocusSuppressor; - nsIMEStateManager::OnTextStateBlur(aPresContext, aContent); - if (nsnull != gLastFocusedPresContextWeak) { nsCOMPtr focusAfterBlur; @@ -5284,8 +5261,6 @@ nsEventStateManager::SendFocusBlur(nsPresContext* aPresContext, if (clearFirstFocusEvent) { mFirstFocusEvent = nsnull; } - - nsIMEStateManager::OnTextStateFocus(mPresContext, mCurrentFocus); } else if (!aContent) { //fire focus on document even if the content isn't focusable (ie. text) //see bugzilla bug 93521 diff --git a/content/events/src/nsIMEStateManager.cpp b/content/events/src/nsIMEStateManager.cpp index 8261130383c2..fa08fdd203ee 100644 --- a/content/events/src/nsIMEStateManager.cpp +++ b/content/events/src/nsIMEStateManager.cpp @@ -22,7 +22,6 @@ * * Contributor(s): * Masayuki Nakano - * Ningjie Chen * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), @@ -54,16 +53,6 @@ #include "nsIFocusController.h" #include "nsIDOMWindow.h" #include "nsContentUtils.h" -#include "nsINode.h" -#include "nsIFrame.h" -#include "nsRange.h" -#include "nsIDOMRange.h" -#include "nsISelection.h" -#include "nsISelectionPrivate.h" -#include "nsISelectionListener.h" -#include "nsISelectionController.h" -#include "nsIMutationObserver.h" -#include "nsContentEventHandler.h" /******************************************************************/ /* nsIMEStateManager */ @@ -74,8 +63,6 @@ nsPresContext* nsIMEStateManager::sPresContext = nsnull; nsPIDOMWindow* nsIMEStateManager::sActiveWindow = nsnull; PRBool nsIMEStateManager::sInstalledMenuKeyboardListener = PR_FALSE; -nsTextStateManager* nsIMEStateManager::sTextStateObserver = nsnull; - nsresult nsIMEStateManager::OnDestroyPresContext(nsPresContext* aPresContext) { @@ -84,7 +71,6 @@ nsIMEStateManager::OnDestroyPresContext(nsPresContext* aPresContext) return NS_OK; sContent = nsnull; sPresContext = nsnull; - OnTextStateBlur(nsnull, nsnull); return NS_OK; } @@ -283,301 +269,3 @@ nsIMEStateManager::GetWidget(nsPresContext* aPresContext) return widget; } - -// nsTextStateManager notifies widget of any text and selection changes -// in the currently focused editor -// sTextStateObserver points to the currently active nsTextStateManager -// sTextStateObserver is null if there is no focused editor - -class nsTextStateManager : public nsISelectionListener, - public nsStubMutationObserver -{ -public: - nsTextStateManager(); - - NS_DECL_ISUPPORTS - NS_DECL_NSISELECTIONLISTENER - NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED - NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED - NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED - NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED - - nsresult Init(nsIWidget* aWidget, - nsPresContext* aPresContext, - nsINode* aNode); - void Destroy(void); - - nsCOMPtr mWidget; - nsCOMPtr mSel; - nsCOMPtr mRootContent; - nsCOMPtr mEditableNode; - PRBool mDestroying; - -private: - void NotifyContentAdded(nsINode* aContainer, PRInt32 aStart, PRInt32 aEnd); -}; - -nsTextStateManager::nsTextStateManager() -{ - mDestroying = PR_FALSE; -} - -nsresult -nsTextStateManager::Init(nsIWidget* aWidget, - nsPresContext* aPresContext, - nsINode* aNode) -{ - mWidget = aWidget; - - nsIPresShell* presShell = aPresContext->PresShell(); - - // get selection and root content - nsCOMPtr selCon; - if (aNode->IsNodeOfType(nsINode::eCONTENT)) { - nsIFrame* frame = presShell->GetPrimaryFrameFor( - static_cast(aNode)); - NS_ENSURE_TRUE(frame, NS_ERROR_UNEXPECTED); - - frame->GetSelectionController(aPresContext, - getter_AddRefs(selCon)); - } else { - // aNode is a document - selCon = do_QueryInterface(presShell); - } - NS_ENSURE_TRUE(selCon, NS_ERROR_FAILURE); - - nsCOMPtr sel; - nsresult rv = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, - getter_AddRefs(sel)); - NS_ENSURE_TRUE(sel, NS_ERROR_UNEXPECTED); - - nsCOMPtr selDomRange; - rv = sel->GetRangeAt(0, getter_AddRefs(selDomRange)); - NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr selRange(do_QueryInterface(selDomRange)); - NS_ENSURE_TRUE(selRange && selRange->GetStartParent(), NS_ERROR_UNEXPECTED); - - mRootContent = selRange->GetStartParent()-> - GetSelectionRootContent(presShell); - - // add text change observer - mRootContent->AddMutationObserver(this); - - // add selection change listener - nsCOMPtr selPrivate(do_QueryInterface(sel)); - NS_ENSURE_TRUE(selPrivate, NS_ERROR_UNEXPECTED); - rv = selPrivate->AddSelectionListener(this); - NS_ENSURE_SUCCESS(rv, rv); - mSel = sel; - - mEditableNode = aNode; - return NS_OK; -} - -void -nsTextStateManager::Destroy(void) -{ - if (mSel) { - nsCOMPtr selPrivate(do_QueryInterface(mSel)); - if (selPrivate) - selPrivate->RemoveSelectionListener(this); - mSel = nsnull; - } - if (mRootContent) { - mRootContent->RemoveMutationObserver(this); - mRootContent = nsnull; - } - mEditableNode = nsnull; - mWidget = nsnull; -} - -NS_IMPL_ISUPPORTS2(nsTextStateManager, - nsIMutationObserver, - nsISelectionListener) - -nsresult -nsTextStateManager::NotifySelectionChanged(nsIDOMDocument* aDoc, - nsISelection* aSel, - PRInt16 aReason) -{ - PRInt32 count = 0; - nsresult rv = aSel->GetRangeCount(&count); - NS_ENSURE_SUCCESS(rv, rv); - if (count > 0) { - mWidget->OnIMESelectionChange(); - } - return NS_OK; -} - -void -nsTextStateManager::CharacterDataChanged(nsIDocument* aDocument, - nsIContent* aContent, - CharacterDataChangeInfo* aInfo) -{ - NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT), - "character data changed for non-text node"); - - PRUint32 offset = 0; - // get offsets of change and fire notification - if (NS_FAILED(nsContentEventHandler::GetFlatTextOffsetOfRange( - mRootContent, aContent, aInfo->mChangeStart, &offset))) - return; - - PRUint32 oldEnd = offset + aInfo->mChangeEnd - aInfo->mChangeStart; - PRUint32 newEnd = offset + aInfo->mReplaceLength; - mWidget->OnIMETextChange(offset, oldEnd, newEnd); -} - -void -nsTextStateManager::NotifyContentAdded(nsINode* aContainer, - PRInt32 aStartIndex, - PRInt32 aEndIndex) -{ - PRUint32 offset = 0, newOffset = 0; - if (NS_FAILED(nsContentEventHandler::GetFlatTextOffsetOfRange( - mRootContent, aContainer, aStartIndex, &offset))) - return; - - // get offset at the end of the last added node - if (NS_FAILED(nsContentEventHandler::GetFlatTextOffsetOfRange( - aContainer->GetChildAt(aStartIndex), - aContainer, aEndIndex, &newOffset))) - return; - - // fire notification - if (newOffset) - mWidget->OnIMETextChange(offset, offset, offset + newOffset); -} - -void -nsTextStateManager::ContentAppended(nsIDocument* aDocument, - nsIContent* aContainer, - PRInt32 aNewIndexInContainer) -{ - NotifyContentAdded(aContainer, aNewIndexInContainer, - aContainer->GetChildCount()); -} - -void -nsTextStateManager::ContentInserted(nsIDocument* aDocument, - nsIContent* aContainer, - nsIContent* aChild, - PRInt32 aIndexInContainer) -{ - NotifyContentAdded(NODE_FROM(aContainer, aDocument), - aIndexInContainer, aIndexInContainer + 1); -} - -void -nsTextStateManager::ContentRemoved(nsIDocument* aDocument, - nsIContent* aContainer, - nsIContent* aChild, - PRInt32 aIndexInContainer) -{ - PRUint32 offset = 0, childOffset = 1; - if (NS_FAILED(nsContentEventHandler::GetFlatTextOffsetOfRange( - mRootContent, NODE_FROM(aContainer, aDocument), - aIndexInContainer, &offset))) - return; - - // get offset at the end of the deleted node - if (aChild->IsNodeOfType(nsINode::eTEXT)) - childOffset = aChild->TextLength(); - else if (0 < aChild->GetChildCount()) - childOffset = aChild->GetChildCount(); - - if (NS_FAILED(nsContentEventHandler::GetFlatTextOffsetOfRange( - aChild, aChild, childOffset, &childOffset))) - return; - - // fire notification - if (childOffset) - mWidget->OnIMETextChange(offset, offset + childOffset, offset); -} - -static nsINode* GetRootEditableNode(nsPresContext* aPresContext, - nsIContent* aContent) -{ - if (aContent) { - nsINode* root = nsnull; - nsINode* node = aContent; - while (node && node->IsEditable()) { - root = node; - node = node->GetParent(); - } - return root; - } - if (aPresContext) { - nsIDocument* document = aPresContext->Document(); - if (document && document->IsEditable()) - return document; - } - return nsnull; -} - -nsresult -nsIMEStateManager::OnTextStateBlur(nsPresContext* aPresContext, - nsIContent* aContent) -{ - if (!sTextStateObserver || sTextStateObserver->mDestroying || - sTextStateObserver->mEditableNode == - GetRootEditableNode(aPresContext, aContent)) - return NS_OK; - - sTextStateObserver->mDestroying = PR_TRUE; - sTextStateObserver->mWidget->OnIMEFocusChange(PR_FALSE); - sTextStateObserver->Destroy(); - NS_RELEASE(sTextStateObserver); - return NS_OK; -} - -nsresult -nsIMEStateManager::OnTextStateFocus(nsPresContext* aPresContext, - nsIContent* aContent) -{ - if (sTextStateObserver) return NS_OK; - - nsINode *editableNode = GetRootEditableNode(aPresContext, aContent); - if (!editableNode) return NS_OK; - - nsIViewManager* vm = aPresContext->GetViewManager(); - NS_ENSURE_TRUE(vm, NS_ERROR_NOT_AVAILABLE); - - nsCOMPtr widget; - nsresult rv = vm->GetWidget(getter_AddRefs(widget)); - NS_ENSURE_SUCCESS(rv, NS_ERROR_NOT_AVAILABLE); - - rv = widget->OnIMEFocusChange(PR_TRUE); - NS_ENSURE_SUCCESS(rv, NS_OK); - - // OnIMEFocusChange may cause focus and sTextStateObserver to change - // In that case return and keep the current sTextStateObserver - NS_ENSURE_TRUE(!sTextStateObserver, NS_OK); - - sTextStateObserver = new nsTextStateManager(); - NS_ENSURE_TRUE(sTextStateObserver, NS_ERROR_OUT_OF_MEMORY); - NS_ADDREF(sTextStateObserver); - rv = sTextStateObserver->Init(widget, aPresContext, editableNode); - if (NS_FAILED(rv)) { - sTextStateObserver->mDestroying = PR_TRUE; - sTextStateObserver->Destroy(); - NS_RELEASE(sTextStateObserver); - widget->OnIMEFocusChange(PR_FALSE); - return rv; - } - return NS_OK; -} - -nsresult -nsIMEStateManager::GetFocusSelectionAndRoot(nsISelection** aSel, - nsIContent** aRoot) -{ - if (!sTextStateObserver || !sTextStateObserver->mEditableNode) - return NS_ERROR_NOT_AVAILABLE; - - NS_ASSERTION(sTextStateObserver->mSel && sTextStateObserver->mRootContent, - "uninitialized text state observer"); - NS_ADDREF(*aSel = sTextStateObserver->mSel); - NS_ADDREF(*aRoot = sTextStateObserver->mRootContent); - return NS_OK; -} diff --git a/content/events/src/nsIMEStateManager.h b/content/events/src/nsIMEStateManager.h index 9fec1c2ff2a7..e9d648f0f99e 100644 --- a/content/events/src/nsIMEStateManager.h +++ b/content/events/src/nsIMEStateManager.h @@ -40,15 +40,12 @@ #define nsIMEStateManager_h__ #include "nscore.h" -#include "nsGUIEvent.h" class nsIContent; class nsPIDOMWindow; class nsPresContext; class nsIWidget; class nsIFocusController; -class nsTextStateManager; -class nsISelection; /* * IME state manager @@ -65,25 +62,6 @@ public: static nsresult OnActivate(nsPresContext* aPresContext); static nsresult OnDeactivate(nsPresContext* aPresContext); static void OnInstalledMenuKeyboardListener(PRBool aInstalling); - - // These two methods manage focus and selection/text observers. - // They are separate from OnChangeFocus above because this offers finer - // control compared to having the two methods incorporated into OnChangeFocus - - // OnTextStateBlur should be called *before* NS_BLUR_CONTENT fires - // aPresContext is the nsPresContext receiving focus (not lost focus) - // aContent is the nsIContent receiving focus (not lost focus) - // aPresContext and/or aContent may be null - static nsresult OnTextStateBlur(nsPresContext* aPresContext, - nsIContent* aContent); - // OnTextStateFocus should be called *after* NS_FOCUS_CONTENT fires - // aPresContext is the nsPresContext receiving focus - // aContent is the nsIContent receiving focus - static nsresult OnTextStateFocus(nsPresContext* aPresContext, - nsIContent* aContent); - // Get the focused editor's selection and root - static nsresult GetFocusSelectionAndRoot(nsISelection** aSel, - nsIContent** aRoot); protected: static void SetIMEState(nsPresContext* aPresContext, PRUint32 aState, @@ -100,8 +78,6 @@ protected: static nsPresContext* sPresContext; static nsPIDOMWindow* sActiveWindow; static PRBool sInstalledMenuKeyboardListener; - - static nsTextStateManager* sTextStateObserver; }; #endif // nsIMEStateManager_h__ diff --git a/content/events/src/nsContentEventHandler.cpp b/content/events/src/nsQueryContentEventHandler.cpp similarity index 58% rename from content/events/src/nsContentEventHandler.cpp rename to content/events/src/nsQueryContentEventHandler.cpp index 80d6df5cabfe..1e4641a00f5e 100644 --- a/content/events/src/nsContentEventHandler.cpp +++ b/content/events/src/nsQueryContentEventHandler.cpp @@ -22,7 +22,6 @@ * * Contributor(s): * Masayuki Nakano - * Ningjie Chen * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), @@ -38,7 +37,7 @@ * * ***** END LICENSE BLOCK ***** */ -#include "nsContentEventHandler.h" +#include "nsQueryContentEventHandler.h" #include "nsCOMPtr.h" #include "nsPresContext.h" #include "nsIPresShell.h" @@ -54,19 +53,14 @@ #include "nsIContentIterator.h" #include "nsTextFragment.h" #include "nsTextFrame.h" -#include "nsISelectionController.h" -#include "nsISelectionPrivate.h" -#include "nsContentUtils.h" -#include "nsISelection2.h" -#include "nsIMEStateManager.h" nsresult NS_NewContentIterator(nsIContentIterator** aInstancePtrResult); /******************************************************************/ -/* nsContentEventHandler */ +/* nsQueryContentEventHandler */ /******************************************************************/ -nsContentEventHandler::nsContentEventHandler( +nsQueryContentEventHandler::nsQueryContentEventHandler( nsPresContext* aPresContext) : mPresContext(aPresContext), mPresShell(aPresContext->GetPresShell()), mSelection(nsnull), @@ -75,7 +69,7 @@ nsContentEventHandler::nsContentEventHandler( } nsresult -nsContentEventHandler::Init(nsQueryContentEvent* aEvent) +nsQueryContentEventHandler::Init(nsQueryContentEvent* aEvent) { NS_ASSERTION(aEvent, "aEvent must not be null"); @@ -123,24 +117,6 @@ nsContentEventHandler::Init(nsQueryContentEvent* aEvent) return NS_OK; } -// Editor places a bogus BR node under its root content if the editor doesn't -// have any text. This happens even for single line editors. -// When we get text content and when we change the selection, -// we don't want to include the bogus BRs at the end. -static PRBool IsContentBR(nsIContent* aContent) -{ - return aContent->IsNodeOfType(nsINode::eHTML) && - aContent->Tag() == nsGkAtoms::br && - !aContent->AttrValueIs(kNameSpaceID_None, - nsGkAtoms::type, - nsGkAtoms::moz, - eIgnoreCase) && - !aContent->AttrValueIs(kNameSpaceID_None, - nsGkAtoms::mozeditorbogusnode, - nsGkAtoms::_true, - eIgnoreCase); -} - static void ConvertToNativeNewlines(nsAFlatString& aString) { #if defined(XP_MACOSX) @@ -185,7 +161,8 @@ static PRUint32 GetNativeTextLength(nsIContent* aContent) nsAutoString str; if (aContent->IsNodeOfType(nsINode::eTEXT)) AppendString(str, aContent); - else if (IsContentBR(aContent)) + else if (aContent->IsNodeOfType(nsINode::eHTML) && + aContent->Tag() == nsGkAtoms::br) str.Assign(PRUnichar('\n')); ConvertToNativeNewlines(str); return str.Length(); @@ -204,8 +181,9 @@ static PRUint32 ConvertToXPOffset(nsIContent* aContent, PRUint32 aNativeOffset) return str.Length(); } -static nsresult GenerateFlatTextContent(nsIRange* aRange, - nsAFlatString& aString) +nsresult +nsQueryContentEventHandler::GenerateFlatTextContent(nsIRange* aRange, + nsAFlatString& aString) { nsCOMPtr iter; nsresult rv = NS_NewContentIterator(getter_AddRefs(iter)); @@ -243,7 +221,8 @@ static nsresult GenerateFlatTextContent(nsIRange* aRange, AppendSubString(aString, content, 0, aRange->EndOffset()); else AppendString(aString, content); - } else if (IsContentBR(content)) + } else if (content->IsNodeOfType(nsINode::eHTML) && + content->Tag() == nsGkAtoms::br) aString.Append(PRUnichar('\n')); } ConvertToNativeNewlines(aString); @@ -251,19 +230,18 @@ static nsresult GenerateFlatTextContent(nsIRange* aRange, } nsresult -nsContentEventHandler::ExpandToClusterBoundary(nsIContent* aContent, +nsQueryContentEventHandler::ExpandToClusterBoundary(nsIContent* aContent, PRBool aForward, PRUint32* aXPOffset) { + NS_ASSERTION(*aXPOffset >= 0 && *aXPOffset <= aContent->TextLength(), + "offset is out of range."); + // XXX This method assumes that the frame boundaries must be cluster // boundaries. It's false, but no problem now, maybe. if (!aContent->IsNodeOfType(nsINode::eTEXT) || *aXPOffset == 0 || *aXPOffset == aContent->TextLength()) return NS_OK; - - NS_ASSERTION(*aXPOffset >= 0 && *aXPOffset <= aContent->TextLength(), - "offset is out of range."); - nsCOMPtr fs = mPresShell->FrameSelection(); PRInt32 offsetInFrame; nsFrameSelection::HINT hint = @@ -287,7 +265,7 @@ nsContentEventHandler::ExpandToClusterBoundary(nsIContent* aContent, if (frame->GetType() != nsGkAtoms::textFrame) return NS_ERROR_FAILURE; nsTextFrame* textFrame = static_cast(frame); - PRInt32 newOffsetInFrame = *aXPOffset - startOffset; + PRInt32 newOffsetInFrame = offsetInFrame; newOffsetInFrame += aForward ? -1 : 1; textFrame->PeekOffsetCharacter(aForward, &newOffsetInFrame); *aXPOffset = startOffset + newOffsetInFrame; @@ -295,7 +273,7 @@ nsContentEventHandler::ExpandToClusterBoundary(nsIContent* aContent, } nsresult -nsContentEventHandler::SetRangeFromFlatTextOffset( +nsQueryContentEventHandler::SetRangeFromFlatTextOffset( nsIRange* aRange, PRUint32 aNativeOffset, PRUint32 aNativeLength, @@ -391,7 +369,7 @@ nsContentEventHandler::SetRangeFromFlatTextOffset( } nsresult -nsContentEventHandler::OnQuerySelectedText(nsQueryContentEvent* aEvent) +nsQueryContentEventHandler::OnQuerySelectedText(nsQueryContentEvent* aEvent) { nsresult rv = Init(aEvent); if (NS_FAILED(rv)) @@ -400,32 +378,21 @@ nsContentEventHandler::OnQuerySelectedText(nsQueryContentEvent* aEvent) NS_ASSERTION(aEvent->mReply.mString.IsEmpty(), "The reply string must be empty"); - rv = GetFlatTextOffsetOfRange(mRootContent, - mFirstSelectedRange, &aEvent->mReply.mOffset); + rv = GetFlatTextOffsetOfRange(mFirstSelectedRange, &aEvent->mReply.mOffset); NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr anchorDomNode, focusDomNode; - rv = mSelection->GetAnchorNode(getter_AddRefs(anchorDomNode)); - NS_ENSURE_TRUE(anchorDomNode, NS_ERROR_FAILURE); - rv = mSelection->GetFocusNode(getter_AddRefs(focusDomNode)); - NS_ENSURE_TRUE(focusDomNode, NS_ERROR_FAILURE); - - PRInt32 anchorOffset, focusOffset; - rv = mSelection->GetAnchorOffset(&anchorOffset); - NS_ENSURE_SUCCESS(rv, rv); - rv = mSelection->GetFocusOffset(&focusOffset); + PRBool isCollapsed; + rv = mSelection->GetIsCollapsed(&isCollapsed); NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr anchorNode(do_QueryInterface(anchorDomNode)); - nsCOMPtr focusNode(do_QueryInterface(focusDomNode)); - NS_ENSURE_TRUE(anchorNode && focusNode, NS_ERROR_UNEXPECTED); + if (!isCollapsed) { + nsCOMPtr domRange; + rv = mSelection->GetRangeAt(0, getter_AddRefs(domRange)); + NS_ENSURE_SUCCESS(rv, rv); + NS_ASSERTION(domRange, "GetRangeAt succeeded, but the result is null"); - PRInt16 compare = nsContentUtils::ComparePoints(anchorNode, anchorOffset, - focusNode, focusOffset); - aEvent->mReply.mReversed = compare > 0; - - if (compare) { - nsCOMPtr range = mFirstSelectedRange; + nsCOMPtr range(do_QueryInterface(domRange)); + NS_ENSURE_TRUE(range, NS_ERROR_FAILURE); rv = GenerateFlatTextContent(range, aEvent->mReply.mString); NS_ENSURE_SUCCESS(rv, rv); } @@ -435,7 +402,7 @@ nsContentEventHandler::OnQuerySelectedText(nsQueryContentEvent* aEvent) } nsresult -nsContentEventHandler::OnQueryTextContent(nsQueryContentEvent* aEvent) +nsQueryContentEventHandler::OnQueryTextContent(nsQueryContentEvent* aEvent) { nsresult rv = Init(aEvent); if (NS_FAILED(rv)) @@ -458,168 +425,65 @@ nsContentEventHandler::OnQueryTextContent(nsQueryContentEvent* aEvent) return NS_OK; } -// Adjust to use a child node if possible -// to make the returned rect more accurate -static nsINode* AdjustTextRectNode(nsINode* aNode, - PRInt32& aOffset) -{ - PRInt32 childCount = PRInt32(aNode->GetChildCount()); - nsINode* node = aNode; - if (childCount) { - if (aOffset < childCount) { - node = aNode->GetChildAt(aOffset); - aOffset = 0; - } else if (aOffset == childCount) { - node = aNode->GetChildAt(childCount - 1); - aOffset = node->IsNodeOfType(nsINode::eTEXT) ? - static_cast(node)->TextLength() : 1; - } - } - return node; -} - -// Similar to nsFrameSelection::GetFrameForNodeOffset, -// but this is more flexible for OnQueryTextRect to use -static nsresult GetFrameForTextRect(nsIPresShell* aPresShell, - nsINode* aNode, - PRInt32 aOffset, - PRBool aHint, - nsIFrame** aReturnFrame) -{ - NS_ENSURE_TRUE(aNode && aNode->IsNodeOfType(nsINode::eCONTENT), - NS_ERROR_UNEXPECTED); - nsIContent* content = static_cast(aNode); - nsIFrame* frame = aPresShell->GetPrimaryFrameFor(content); - NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE); - PRInt32 childOffset = 0; - return frame->GetChildFrameContainingOffset(aOffset, aHint, &childOffset, - aReturnFrame); -} - nsresult -nsContentEventHandler::OnQueryTextRect(nsQueryContentEvent* aEvent) +nsQueryContentEventHandler::QueryRectFor(nsQueryContentEvent* aEvent, + nsIRange* aRange, + nsCaret* aCaret) { - nsresult rv = Init(aEvent); - if (NS_FAILED(rv)) - return rv; - - nsRefPtr range = new nsRange(); - if (!range) { - return NS_ERROR_OUT_OF_MEMORY; - } - rv = SetRangeFromFlatTextOffset(range, aEvent->mInput.mOffset, - aEvent->mInput.mLength, PR_TRUE); + PRInt32 offsetInFrame; + nsIFrame* frame; + nsresult rv = GetStartFrameAndOffset(aRange, &frame, &offsetInFrame); NS_ENSURE_SUCCESS(rv, rv); - // used to iterate over all contents and their frames - nsCOMPtr iter; - rv = NS_NewContentIterator(getter_AddRefs(iter)); - NS_ENSURE_SUCCESS(rv, rv); - iter->Init(range); + nsPoint posInFrame; + rv = frame->GetPointFromOffset(aRange->StartOffset(), &posInFrame); NS_ENSURE_SUCCESS(rv, rv); - // get the starting frame - PRInt32 offset = range->StartOffset(); - nsINode* node = iter->GetCurrentNode(); - if (!node) { - node = AdjustTextRectNode(range->GetStartParent(), offset); - } - nsIFrame* firstFrame = nsnull; - rv = GetFrameForTextRect(mPresShell, node, offset, - PR_TRUE, &firstFrame); - NS_ENSURE_SUCCESS(rv, rv); + nsRect rect; + rect.y = posInFrame.y; + rect.height = frame->GetSize().height; - // get the starting frame rect - nsRect rect(nsPoint(0, 0), firstFrame->GetRect().Size()); - rv = ConvertToRootViewRelativeOffset(firstFrame, rect); - NS_ENSURE_SUCCESS(rv, rv); - nsRect frameRect = rect; - nsPoint ptOffset; - firstFrame->GetPointFromOffset(offset, &ptOffset); - // minus 1 to avoid creating an empty rect - rect.x += ptOffset.x - 1; - rect.width -= ptOffset.x - 1; - - // get the ending frame - offset = range->EndOffset(); - node = AdjustTextRectNode(range->GetEndParent(), offset); - nsIFrame* lastFrame = nsnull; - rv = GetFrameForTextRect(mPresShell, node, offset, - range->Collapsed(), &lastFrame); - NS_ENSURE_SUCCESS(rv, rv); - - // iterate over all covered frames - for (nsIFrame* frame = firstFrame; frame != lastFrame;) { - frame = frame->GetNextContinuation(); - if (!frame) { - do { - iter->Next(); - node = iter->GetCurrentNode(); - if (!node || !node->IsNodeOfType(nsINode::eCONTENT)) - continue; - frame = mPresShell->GetPrimaryFrameFor(static_cast(node)); - } while (!frame && !iter->IsDone()); - if (!frame) { - // this can happen when the end offset of the range is 0. - frame = lastFrame; - } - } - frameRect.SetRect(nsPoint(0, 0), frame->GetRect().Size()); - rv = ConvertToRootViewRelativeOffset(frame, frameRect); + if (aEvent->message == NS_QUERY_CHARACTER_RECT) { + nsPoint nextPos; + rv = frame->GetPointFromOffset(aRange->EndOffset(), &nextPos); NS_ENSURE_SUCCESS(rv, rv); - if (frame != lastFrame) { - // not last frame, so just add rect to previous result - rect.UnionRect(rect, frameRect); - } - } - - // get the ending frame rect - lastFrame->GetPointFromOffset(offset, &ptOffset); - // minus 1 to avoid creating an empty rect - frameRect.width -= lastFrame->GetRect().width - ptOffset.x - 1; - - if (firstFrame == lastFrame) { - rect.IntersectRect(rect, frameRect); + rect.x = PR_MIN(posInFrame.x, nextPos.x); + rect.width = PR_ABS(posInFrame.x - nextPos.x); } else { - rect.UnionRect(rect, frameRect); + rect.x = posInFrame.x; + rect.width = aCaret->GetCaretRect().width; } - aEvent->mReply.mRect = - nsRect::ToOutsidePixels(rect, mPresContext->AppUnitsPerDevPixel()); + + rv = ConvertToRootViewRelativeOffset(frame, rect); + NS_ENSURE_SUCCESS(rv, rv); + + aEvent->mReply.mRect = nsRect::ToOutsidePixels(rect, mPresContext->AppUnitsPerDevPixel()); aEvent->mSucceeded = PR_TRUE; return NS_OK; } nsresult -nsContentEventHandler::OnQueryEditorRect(nsQueryContentEvent* aEvent) +nsQueryContentEventHandler::OnQueryCharacterRect(nsQueryContentEvent* aEvent) { nsresult rv = Init(aEvent); if (NS_FAILED(rv)) return rv; - nsIFrame* frame = mPresShell->GetPrimaryFrameFor(mRootContent); - NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE); - - // get rect for first frame - nsRect resultRect(nsPoint(0, 0), frame->GetRect().Size()); - rv = ConvertToRootViewRelativeOffset(frame, resultRect); + nsCOMPtr range = new nsRange(); + NS_ENSURE_TRUE(range, NS_ERROR_OUT_OF_MEMORY); + rv = SetRangeFromFlatTextOffset(range, aEvent->mInput.mOffset, 1, PR_TRUE); NS_ENSURE_SUCCESS(rv, rv); - // account for any additional frames - while ((frame = frame->GetNextContinuation()) != nsnull) { - nsRect frameRect(nsPoint(0, 0), frame->GetRect().Size()); - rv = ConvertToRootViewRelativeOffset(frame, frameRect); - NS_ENSURE_SUCCESS(rv, rv); - resultRect.UnionRect(resultRect, frameRect); + if (range->Collapsed()) { + // There is no character at the offset. + return NS_OK; } - aEvent->mReply.mRect = - nsRect::ToOutsidePixels(resultRect, mPresContext->AppUnitsPerDevPixel()); - aEvent->mSucceeded = PR_TRUE; - return NS_OK; + return QueryRectFor(aEvent, range, nsnull); } nsresult -nsContentEventHandler::OnQueryCaretRect(nsQueryContentEvent* aEvent) +nsQueryContentEventHandler::OnQueryCaretRect(nsQueryContentEvent* aEvent) { nsresult rv = Init(aEvent); if (NS_FAILED(rv)) @@ -638,7 +502,7 @@ nsContentEventHandler::OnQueryCaretRect(nsQueryContentEvent* aEvent) if (selectionIsCollapsed) { PRUint32 offset; - rv = GetFlatTextOffsetOfRange(mRootContent, mFirstSelectedRange, &offset); + rv = GetFlatTextOffsetOfRange(mFirstSelectedRange, &offset); NS_ENSURE_SUCCESS(rv, rv); if (offset == aEvent->mInput.mOffset) { PRBool isCollapsed; @@ -646,8 +510,7 @@ nsContentEventHandler::OnQueryCaretRect(nsQueryContentEvent* aEvent) rv = caret->GetCaretCoordinates(nsCaret::eTopLevelWindowCoordinates, mSelection, &rect, &isCollapsed, nsnull); - aEvent->mReply.mRect = - nsRect::ToOutsidePixels(rect, mPresContext->AppUnitsPerDevPixel()); + aEvent->mReply.mRect = nsRect::ToOutsidePixels(rect, mPresContext->AppUnitsPerDevPixel()); NS_ENSURE_SUCCESS(rv, rv); aEvent->mSucceeded = PR_TRUE; return NS_OK; @@ -660,36 +523,12 @@ nsContentEventHandler::OnQueryCaretRect(nsQueryContentEvent* aEvent) rv = SetRangeFromFlatTextOffset(range, aEvent->mInput.mOffset, 0, PR_TRUE); NS_ENSURE_SUCCESS(rv, rv); - PRInt32 offsetInFrame; - nsIFrame* frame; - rv = GetStartFrameAndOffset(range, &frame, &offsetInFrame); - NS_ENSURE_SUCCESS(rv, rv); - - nsPoint posInFrame; - rv = frame->GetPointFromOffset(range->StartOffset(), &posInFrame); - NS_ENSURE_SUCCESS(rv, rv); - - nsRect rect; - rect.x = posInFrame.x; - rect.y = posInFrame.y; - rect.width = caret->GetCaretRect().width; - rect.height = frame->GetSize().height; - - rv = ConvertToRootViewRelativeOffset(frame, rect); - NS_ENSURE_SUCCESS(rv, rv); - - aEvent->mReply.mRect = - nsRect::ToOutsidePixels(rect, mPresContext->AppUnitsPerDevPixel()); - - aEvent->mSucceeded = PR_TRUE; - return NS_OK; + return QueryRectFor(aEvent, range, caret); } nsresult -nsContentEventHandler::GetFlatTextOffsetOfRange(nsIContent* aRootContent, - nsINode* aNode, - PRInt32 aNodeOffset, - PRUint32* aNativeOffset) +nsQueryContentEventHandler::GetFlatTextOffsetOfRange(nsIRange* aRange, + PRUint32* aNativeOffset) { NS_ASSERTION(aNativeOffset, "param is invalid"); @@ -697,12 +536,16 @@ nsContentEventHandler::GetFlatTextOffsetOfRange(nsIContent* aRootContent, NS_ENSURE_TRUE(prev, NS_ERROR_OUT_OF_MEMORY); nsCOMPtr domPrev(do_QueryInterface(prev)); NS_ASSERTION(domPrev, "nsRange doesn't have nsIDOMRange??"); - nsCOMPtr rootDOMNode(do_QueryInterface(aRootContent)); + nsCOMPtr rootDOMNode(do_QueryInterface(mRootContent)); domPrev->SetStart(rootDOMNode, 0); - nsCOMPtr startDOMNode(do_QueryInterface(aNode)); + nsINode* startNode = aRange->GetStartParent(); + NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE); + + PRInt32 startOffset = aRange->StartOffset(); + nsCOMPtr startDOMNode(do_QueryInterface(startNode)); NS_ASSERTION(startDOMNode, "startNode doesn't have nsIDOMNode"); - domPrev->SetEnd(startDOMNode, aNodeOffset); + domPrev->SetEnd(startDOMNode, startOffset); nsAutoString prevStr; nsresult rv = GenerateFlatTextContent(prev, prevStr); @@ -712,21 +555,9 @@ nsContentEventHandler::GetFlatTextOffsetOfRange(nsIContent* aRootContent, } nsresult -nsContentEventHandler::GetFlatTextOffsetOfRange(nsIContent* aRootContent, - nsIRange* aRange, - PRUint32* aNativeOffset) -{ - nsINode* startNode = aRange->GetStartParent(); - NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE); - PRInt32 startOffset = aRange->StartOffset(); - return GetFlatTextOffsetOfRange(aRootContent, startNode, startOffset, - aNativeOffset); -} - -nsresult -nsContentEventHandler::GetStartFrameAndOffset(nsIRange* aRange, - nsIFrame** aFrame, - PRInt32* aOffsetInFrame) +nsQueryContentEventHandler::GetStartFrameAndOffset(nsIRange* aRange, + nsIFrame** aFrame, + PRInt32* aOffsetInFrame) { NS_ASSERTION(aRange && aFrame && aOffsetInFrame, "params are invalid"); @@ -746,8 +577,8 @@ nsContentEventHandler::GetStartFrameAndOffset(nsIRange* aRange, } nsresult -nsContentEventHandler::ConvertToRootViewRelativeOffset(nsIFrame* aFrame, - nsRect& aRect) +nsQueryContentEventHandler::ConvertToRootViewRelativeOffset(nsIFrame* aFrame, + nsRect& aRect) { NS_ASSERTION(aFrame, "aFrame must not be null"); @@ -759,86 +590,3 @@ nsContentEventHandler::ConvertToRootViewRelativeOffset(nsIFrame* aFrame, aRect += posInView + view->GetOffsetTo(nsnull); return NS_OK; } - -static void AdjustRangeForSelection(nsIContent* aRoot, - nsINode** aNode, - PRInt32* aOffset) -{ - nsINode* node = *aNode; - PRInt32 offset = *aOffset; - if (aRoot != node && node->GetParent() && - !node->IsNodeOfType(nsINode::eTEXT)) { - node = node->GetParent(); - offset = node->IndexOf(*aNode) + (offset ? 1 : 0); - } - nsINode* brNode = node->GetChildAt(offset - 1); - while (brNode && brNode->IsNodeOfType(nsINode::eHTML)) { - nsIContent* brContent = static_cast(brNode); - if (brContent->Tag() != nsGkAtoms::br || IsContentBR(brContent)) - break; - brNode = node->GetChildAt(--offset - 1); - } - *aNode = node; - *aOffset = PR_MAX(offset, 0); -} - -nsresult -nsContentEventHandler::OnSelectionEvent(nsSelectionEvent* aEvent) -{ - aEvent->mSucceeded = PR_FALSE; - - // Get selection to manipulate - nsCOMPtr sel; - nsresult rv = nsIMEStateManager:: - GetFocusSelectionAndRoot(getter_AddRefs(sel), - getter_AddRefs(mRootContent)); - NS_ENSURE_SUCCESS(rv, rv); - - // Get range from offset and length - nsRefPtr range = new nsRange(); - NS_ENSURE_TRUE(range, NS_ERROR_OUT_OF_MEMORY); - rv = SetRangeFromFlatTextOffset(range, aEvent->mOffset, - aEvent->mLength, PR_TRUE); - NS_ENSURE_SUCCESS(rv, rv); - - nsINode* startNode = range->GetStartParent(); - nsINode* endNode = range->GetEndParent(); - PRInt32 startOffset = range->StartOffset(); - PRInt32 endOffset = range->EndOffset(); - AdjustRangeForSelection(mRootContent, &startNode, &startOffset); - AdjustRangeForSelection(mRootContent, &endNode, &endOffset); - - nsCOMPtr startDomNode(do_QueryInterface(startNode)); - nsCOMPtr endDomNode(do_QueryInterface(endNode)); - NS_ENSURE_TRUE(startDomNode && endDomNode, NS_ERROR_UNEXPECTED); - - nsCOMPtr selPrivate = do_QueryInterface(sel); - NS_ENSURE_TRUE(selPrivate, NS_ERROR_UNEXPECTED); - selPrivate->StartBatchChanges(); - - // Clear selection first before setting - rv = sel->RemoveAllRanges(); - // Need to call EndBatchChanges at the end even if call failed - if (NS_SUCCEEDED(rv)) { - if (aEvent->mReversed) { - rv = sel->Collapse(endDomNode, endOffset); - } else { - rv = sel->Collapse(startDomNode, startOffset); - } - if (NS_SUCCEEDED(rv) && - (startDomNode != endDomNode || startOffset != endOffset)) { - if (aEvent->mReversed) { - rv = sel->Extend(startDomNode, startOffset); - } else { - rv = sel->Extend(endDomNode, endOffset); - } - } - } - selPrivate->EndBatchChanges(); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr(do_QueryInterface(sel))->ScrollIntoView( - nsISelectionController::SELECTION_FOCUS_REGION, PR_FALSE, -1, -1); - aEvent->mSucceeded = PR_TRUE; - return NS_OK; -} diff --git a/content/events/src/nsContentEventHandler.h b/content/events/src/nsQueryContentEventHandler.h similarity index 73% rename from content/events/src/nsContentEventHandler.h rename to content/events/src/nsQueryContentEventHandler.h index b3f45b5149ca..3ecb299f61ab 100644 --- a/content/events/src/nsContentEventHandler.h +++ b/content/events/src/nsQueryContentEventHandler.h @@ -21,7 +21,6 @@ * * Contributor(s): * Masayuki Nakano - * Ningjie Chen * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), @@ -37,8 +36,8 @@ * * ***** END LICENSE BLOCK ***** */ -#ifndef nsContentEventHandler_h__ -#define nsContentEventHandler_h__ +#ifndef nsQueryContentEventHandler_h__ +#define nsQueryContentEventHandler_h__ #include "nscore.h" #include "nsCOMPtr.h" @@ -51,36 +50,29 @@ class nsPresContext; class nsIPresShell; class nsQueryContentEvent; -class nsSelectionEvent; class nsCaret; struct nsRect; /* * Query Content Event Handler - * nsContentEventHandler is a helper class for nsEventStateManager. + * nsQueryContentEventHandler is a helper class for nsEventStateManager. * The platforms request some content informations, e.g., the selected text, * the offset of the selected text and the text for specified range. * This class answers to NS_QUERY_* events from actual contents. */ -class NS_STACK_CLASS nsContentEventHandler { +class NS_STACK_CLASS nsQueryContentEventHandler { public: - nsContentEventHandler(nsPresContext *aPresContext); + nsQueryContentEventHandler(nsPresContext *aPresContext); // NS_QUERY_SELECTED_TEXT event handler nsresult OnQuerySelectedText(nsQueryContentEvent* aEvent); // NS_QUERY_TEXT_CONTENT event handler nsresult OnQueryTextContent(nsQueryContentEvent* aEvent); + // NS_QUERY_CHARACTER_RECT event handler + nsresult OnQueryCharacterRect(nsQueryContentEvent* aEvent); // NS_QUERY_CARET_RECT event handler nsresult OnQueryCaretRect(nsQueryContentEvent* aEvent); - // NS_QUERY_TEXT_RECT event handler - nsresult OnQueryTextRect(nsQueryContentEvent* aEvent); - // NS_QUERY_EDITOR_RECT event handler - nsresult OnQueryEditorRect(nsQueryContentEvent* aEvent); - - // NS_SELECTION_* event - nsresult OnSelectionEvent(nsSelectionEvent* aEvent); - protected: nsPresContext* mPresContext; nsIPresShell* mPresShell; @@ -90,19 +82,11 @@ protected: nsresult Init(nsQueryContentEvent* aEvent); -public: // FlatText means the text that is generated from DOM tree. The BR elements // are replaced to native linefeeds. Other elements are ignored. - // Get the offset in FlatText of the range. (also used by nsIMEStateManager) - static nsresult GetFlatTextOffsetOfRange(nsIContent* aRootContent, - nsINode* aNode, - PRInt32 aNodeOffset, - PRUint32* aOffset); - static nsresult GetFlatTextOffsetOfRange(nsIContent* aRootContent, - nsIRange* aRange, - PRUint32* aOffset); -protected: + // Generate the FlatText from DOM range. + nsresult GenerateFlatTextContent(nsIRange* aRange, nsAFlatString& aString); // Make the DOM range from the offset of FlatText and the text length. // If aExpandToClusterBoundaries is true, the start offset and the end one are // expanded to nearest cluster boundaries. @@ -110,18 +94,22 @@ protected: PRUint32 aNativeOffset, PRUint32 aNativeLength, PRBool aExpandToClusterBoundaries); + // Get the offset in FlatText of the range. + nsresult GetFlatTextOffsetOfRange(nsIRange* aRange, PRUint32* aOffset); // Find the first textframe for the range, and get the start offset in // the frame. nsresult GetStartFrameAndOffset(nsIRange* aRange, - nsIFrame** aFrame, - PRInt32* aOffsetInFrame); + nsIFrame** aFrame, PRInt32* aOffsetInFrame); // Convert the frame relative offset to the root view relative offset. - nsresult ConvertToRootViewRelativeOffset(nsIFrame* aFrame, - nsRect& aRect); + nsresult ConvertToRootViewRelativeOffset(nsIFrame* aFrame, nsRect& aRect); + // The helper for OnQueryCharacterRect/OnQueryCaretRect. + // Don't call for another event. + nsresult QueryRectFor(nsQueryContentEvent* aEvent, nsIRange* aRange, + nsCaret* aCaret); // Expand aXPOffset to the nearest offset in cluster boundary. aForward is // true, it is expanded to forward. nsresult ExpandToClusterBoundary(nsIContent* aContent, PRBool aForward, PRUint32* aXPOffset); }; -#endif // nsContentEventHandler_h__ +#endif // nsQueryContentEventHandler_h__ diff --git a/content/html/content/src/nsGenericHTMLElement.h b/content/html/content/src/nsGenericHTMLElement.h index 9b7296fcbe46..52d11c9af0ea 100644 --- a/content/html/content/src/nsGenericHTMLElement.h +++ b/content/html/content/src/nsGenericHTMLElement.h @@ -569,13 +569,6 @@ public: static nsresult GetHashFromHrefString(const nsAString &aHref, nsAString& aHash); - - /** - * Locate an nsIEditor rooted at this content node, if there is one. - */ - NS_HIDDEN_(nsresult) GetEditor(nsIEditor** aEditor); - NS_HIDDEN_(nsresult) GetEditorInternal(nsIEditor** aEditor); - protected: /** * Focus or blur the element. This is what you should call if you want to @@ -735,6 +728,12 @@ protected: */ NS_HIDDEN_(nsresult) GetURIListAttr(nsIAtom* aAttr, nsAString& aResult); + /** + * Locate an nsIEditor rooted at this content node, if there is one. + */ + NS_HIDDEN_(nsresult) GetEditor(nsIEditor** aEditor); + NS_HIDDEN_(nsresult) GetEditorInternal(nsIEditor** aEditor); + /** * Locates the nsIEditor associated with this node. In general this is * equivalent to GetEditorInternal(), but for designmode or contenteditable, diff --git a/view/src/nsViewManager.cpp b/view/src/nsViewManager.cpp index efb0db41121f..45fb56c0b0cc 100644 --- a/view/src/nsViewManager.cpp +++ b/view/src/nsViewManager.cpp @@ -1249,7 +1249,6 @@ NS_IMETHODIMP nsViewManager::DispatchEvent(nsGUIEvent *aEvent, nsEventStatus *aS if (!NS_IS_KEY_EVENT(aEvent) && !NS_IS_IME_EVENT(aEvent) && !NS_IS_CONTEXT_MENU_KEY(aEvent) && !NS_IS_FOCUS_EVENT(aEvent) && !NS_IS_QUERY_CONTENT_EVENT(aEvent) && !NS_IS_PLUGIN_EVENT(aEvent) && - !NS_IS_SELECTION_EVENT(aEvent) && aEvent->eventStructType != NS_ACCESSIBLE_EVENT) { // will dispatch using coordinates. Pretty bogus but it's consistent // with what presshell does. diff --git a/widget/Makefile.in b/widget/Makefile.in index ce8fa5c349cc..f495accb88ef 100644 --- a/widget/Makefile.in +++ b/widget/Makefile.in @@ -45,7 +45,7 @@ include $(DEPTH)/config/autoconf.mk DIRS = public src ifdef ENABLE_TESTS -TOOL_DIRS += tests +DIRS += tests endif include $(topsrcdir)/config/rules.mk diff --git a/widget/public/nsGUIEvent.h b/widget/public/nsGUIEvent.h index 244a19bf63f1..8a1f5fcd0063 100644 --- a/widget/public/nsGUIEvent.h +++ b/widget/public/nsGUIEvent.h @@ -105,7 +105,6 @@ class nsHashKey; #define NS_DRAG_EVENT 35 #define NS_NOTIFYPAINT_EVENT 36 #define NS_SIMPLE_GESTURE_EVENT 37 -#define NS_SELECTION_EVENT 38 // These flags are sort of a mess. They're sort of shared between event // listener flags and event flags, but only some of them. You've been @@ -345,18 +344,14 @@ class nsHashKey; #define NS_QUERY_SELECTED_TEXT (NS_QUERY_CONTENT_EVENT_START) // Query for the text content of specified range, it returns actual lengh (if // the specified range is too long) and the text of the specified range. -// Returns the entire text if requested length > actual length. #define NS_QUERY_TEXT_CONTENT (NS_QUERY_CONTENT_EVENT_START + 1) +// Query for the character rect of nth character. If there is no character at +// the offset, the query will be failed. The offset of the result is relative +// position from the top level widget. +#define NS_QUERY_CHARACTER_RECT (NS_QUERY_CONTENT_EVENT_START + 2) // Query for the caret rect of nth insertion point. The offset of the result is // relative position from the top level widget. #define NS_QUERY_CARET_RECT (NS_QUERY_CONTENT_EVENT_START + 3) -// Query for the bounding rect of a range of characters. This works on any -// valid character range given offset and length. Result is relative to top -// level widget coordinates -#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) // Video events #ifdef MOZ_MEDIA @@ -402,11 +397,6 @@ class nsHashKey; #define NS_PLUGIN_EVENT_START 3600 #define NS_PLUGIN_EVENT (NS_PLUGIN_EVENT_START) -// Events to manipulate selection (nsSelectionEvent) -#define NS_SELECTION_EVENT_START 3700 -// Clear any previous selection and set the given range as the selection -#define NS_SELECTION_SET (NS_SELECTION_EVENT_START) - /** * Return status for event processors, nsEventStatus, is defined in * nsEvent.h. @@ -868,11 +858,11 @@ class nsTextEvent : public nsInputEvent public: nsTextEvent(PRBool isTrusted, PRUint32 msg, nsIWidget *w) : nsInputEvent(isTrusted, msg, w, NS_TEXT_EVENT), - rangeCount(0), rangeArray(nsnull), isChar(PR_FALSE) + theText(nsnull), rangeCount(0), rangeArray(nsnull), isChar(PR_FALSE) { } - nsString theText; + const PRUnichar* theText; nsTextEventReply theReply; PRUint32 rangeCount; // Note that the range array may not specify a caret position; in that @@ -975,6 +965,13 @@ public: mInput.mLength = aLength; } + void InitForQueryCharacterRect(PRUint32 aOffset) + { + NS_ASSERTION(message == NS_QUERY_CHARACTER_RECT, + "wrong initializer is called"); + mInput.mOffset = aOffset; + } + void InitForQueryCaretRect(PRUint32 aOffset) { NS_ASSERTION(message == NS_QUERY_CARET_RECT, @@ -982,14 +979,6 @@ public: mInput.mOffset = aOffset; } - void InitForQueryTextRect(PRUint32 aOffset, PRUint32 aLength) - { - NS_ASSERTION(message == NS_QUERY_TEXT_RECT, - "wrong initializer is called"); - mInput.mOffset = aOffset; - mInput.mLength = aLength; - } - PRBool mSucceeded; struct { PRUint32 mOffset; @@ -1002,25 +991,9 @@ public: nsIntRect mRect; // Finally, the coordinates is system coordinates. // The return widget has the caret. This is set at all query events. nsIWidget* mFocusedWidget; - PRPackedBool mReversed; // true if selection is reversed (end < start) } mReply; }; -class nsSelectionEvent : public nsGUIEvent -{ -public: - nsSelectionEvent(PRBool aIsTrusted, PRUint32 aMsg, nsIWidget *aWidget) : - nsGUIEvent(aIsTrusted, aMsg, aWidget, NS_SELECTION_EVENT), - mSucceeded(PR_FALSE) - { - } - - PRUint32 mOffset; // start offset of selection - PRUint32 mLength; // length of selection - PRPackedBool mReversed; // selection "anchor" should be in front - PRPackedBool mSucceeded; -}; - /** * MenuItem event * @@ -1259,12 +1232,8 @@ enum nsDragDropEventStatus { #define NS_IS_QUERY_CONTENT_EVENT(evnt) \ (((evnt)->message == NS_QUERY_SELECTED_TEXT) || \ ((evnt)->message == NS_QUERY_TEXT_CONTENT) || \ - ((evnt)->message == NS_QUERY_CARET_RECT) || \ - ((evnt)->message == NS_QUERY_TEXT_RECT) || \ - ((evnt)->message == NS_QUERY_EDITOR_RECT)) - -#define NS_IS_SELECTION_EVENT(evnt) \ - (((evnt)->message == NS_SELECTION_SET)) + ((evnt)->message == NS_QUERY_CHARACTER_RECT) || \ + ((evnt)->message == NS_QUERY_CARET_RECT)) #define NS_IS_PLUGIN_EVENT(evnt) \ (((evnt)->message == NS_PLUGIN_EVENT)) diff --git a/widget/public/nsIWidget.h b/widget/public/nsIWidget.h index b14585eccdfe..b7ec83fc3252 100644 --- a/widget/public/nsIWidget.h +++ b/widget/public/nsIWidget.h @@ -93,14 +93,11 @@ typedef nsEventStatus (* EVENT_CALLBACK)(nsGUIEvent *event); #define NS_NATIVE_PLUGIN_PORT_QD 100 #define NS_NATIVE_PLUGIN_PORT_CG 101 #endif -#ifdef XP_WIN -#define NS_NATIVE_TSF_POINTER 100 -#endif -// {51C24E3B-229F-4c4f-ADA5-BE891FD9EFE9} +// a85944af-7fce-4e45-bf04-ac12c823394b #define NS_IWIDGET_IID \ -{ 0x51c24e3b, 0x229f, 0x4c4f, \ - { 0xad, 0xa5, 0xbe, 0x89, 0x1f, 0xd9, 0xef, 0xe9 } } +{ 0xa85944af, 0x7fce, 0x4e45, \ + { 0xbf, 0x04, 0xac, 0x12, 0xc8, 0x23, 0x39, 0x4b } } // Hide the native window systems real window type so as to avoid // including native window system types and APIs. This is necessary @@ -1245,29 +1242,6 @@ class nsIWidget : public nsISupports { */ NS_IMETHOD GetToggledKeyState(PRUint32 aKeyCode, PRBool* aLEDState) = 0; - /* - * An editable node (i.e. input/textarea/design mode document) - * is receiving or giving up focus - * aFocus is true if node is receiving focus - * aFocus is false if node is giving up focus (blur) - */ - NS_IMETHOD OnIMEFocusChange(PRBool aFocus) = 0; - - /* - * Text content of the focused node has changed - * aStart is the starting offset of the change - * aOldEnd is the ending offset of the change - * aNewEnd is the caret offset after the change - */ - NS_IMETHOD OnIMETextChange(PRUint32 aStart, - PRUint32 aOldEnd, - PRUint32 aNewEnd) = 0; - - /* - * Selection has changed in the focused node - */ - NS_IMETHOD OnIMESelectionChange(void) = 0; - protected: // keep the list of children. We also keep track of our siblings. // The ownership model is as follows: parent holds a strong ref to diff --git a/widget/src/cocoa/nsChildView.mm b/widget/src/cocoa/nsChildView.mm index 6cbfcd644962..c6d0f6e2959c 100644 --- a/widget/src/cocoa/nsChildView.mm +++ b/widget/src/cocoa/nsChildView.mm @@ -5490,8 +5490,8 @@ GetUSLayoutCharFromKeyTranslate(UInt32 aKeyCode, UInt32 aModifiers) nsIntRect r; PRBool useCaretRect = theRange.length == 0; if (!useCaretRect) { - nsQueryContentEvent charRect(PR_TRUE, NS_QUERY_TEXT_RECT, mGeckoChild); - charRect.InitForQueryTextRect(theRange.location, 1); + nsQueryContentEvent charRect(PR_TRUE, NS_QUERY_CHARACTER_RECT, mGeckoChild); + charRect.InitForQueryCharacterRect(theRange.location); mGeckoChild->DispatchWindowEvent(charRect); if (charRect.mSucceeded) r = charRect.mReply.mRect; diff --git a/widget/src/windows/Makefile.in b/widget/src/windows/Makefile.in index a608f43496e8..64887ab765fe 100644 --- a/widget/src/windows/Makefile.in +++ b/widget/src/windows/Makefile.in @@ -107,7 +107,6 @@ CPPSRCS += \ nsBidiKeyboard.cpp \ nsSound.cpp \ nsIdleServiceWin.cpp \ - nsTextStore.cpp \ $(NULL) endif diff --git a/widget/src/windows/nsTextStore.cpp b/widget/src/windows/nsTextStore.cpp deleted file mode 100644 index 97c4589171c0..000000000000 --- a/widget/src/windows/nsTextStore.cpp +++ /dev/null @@ -1,1274 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is mozilla.org code. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Ningjie Chen - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include - -#include "nscore.h" -#include "nsTextStore.h" -#include "nsWindow.h" -#include "nsIPrefBranch.h" -#include "nsIPrefService.h" -#include "prlog.h" - -/******************************************************************/ -/* nsTextStore */ -/******************************************************************/ - -ITfThreadMgr* nsTextStore::sTsfThreadMgr = NULL; -DWORD nsTextStore::sTsfClientId = 0; -nsTextStore* nsTextStore::sTsfTextStore = NULL; - -UINT nsTextStore::sFlushTIPInputMessage = 0; - -#ifdef PR_LOGGING -PRLogModuleInfo* sTextStoreLog = nsnull; -#endif - -nsTextStore::nsTextStore() -{ - mRefCnt = 1; - mEditCookie = 0; - mSinkMask = 0; - mWindow = nsnull; - mLock = 0; - mLockQueued = 0; - mTextChange.acpStart = PR_INT32_MAX; - mTextChange.acpOldEnd = mTextChange.acpNewEnd = 0; -} - -nsTextStore::~nsTextStore() -{ -} - -PRBool -nsTextStore::Create(nsWindow* aWindow, - PRUint32 aIMEState) -{ - if (!mDocumentMgr) { - // Create document manager - HRESULT hr = sTsfThreadMgr->CreateDocumentMgr( - getter_AddRefs(mDocumentMgr)); - NS_ENSURE_TRUE(SUCCEEDED(hr), PR_FALSE); - mWindow = aWindow; - // Create context and add it to document manager - hr = mDocumentMgr->CreateContext(sTsfClientId, 0, - static_cast(this), - getter_AddRefs(mContext), &mEditCookie); - if (SUCCEEDED(hr)) { - SetIMEEnabledInternal(aIMEState); - hr = mDocumentMgr->Push(mContext); - } - if (SUCCEEDED(hr)) { - PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, - ("TSF: Created, window=%08x\n", aWindow)); - return PR_TRUE; - } - mContext = NULL; - mDocumentMgr = NULL; - } - return PR_FALSE; -} - -PRBool -nsTextStore::Destroy(void) -{ - Blur(); - if (mWindow) { - // When blurred, Tablet Input Panel posts "blur" messages - // and try to insert text when the message is retrieved later. - // But by that time the text store is already destroyed, - // so try to get the message early - MSG msg; - if (::PeekMessageW(&msg, mWindow->GetWindowHandle(), - sFlushTIPInputMessage, sFlushTIPInputMessage, - PM_REMOVE)) { - ::DispatchMessageW(&msg); - } - } - mContext = NULL; - if (mDocumentMgr) { - mDocumentMgr->Pop(TF_POPF_ALL); - mDocumentMgr = NULL; - } - mSink = NULL; - PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, - ("TSF: Destroyed, window=%08x\n", mWindow)); - mWindow = NULL; - return PR_TRUE; -} - -PRBool -nsTextStore::Focus(void) -{ - HRESULT hr = sTsfThreadMgr->SetFocus(mDocumentMgr); - NS_ENSURE_TRUE(SUCCEEDED(hr), PR_FALSE); - PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, - ("TSF: Focused\n")); - return PR_TRUE; -} - -PRBool -nsTextStore::Blur(void) -{ - sTsfThreadMgr->SetFocus(NULL); - PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, - ("TSF: Blurred\n")); - return PR_TRUE; -} - -STDMETHODIMP -nsTextStore::QueryInterface(REFIID riid, - void** ppv) -{ - *ppv=NULL; - if ( (IID_IUnknown == riid) || (IID_ITextStoreACP == riid) ) { - *ppv = static_cast(this); - } else if (IID_ITfContextOwnerCompositionSink == riid) { - *ppv = static_cast(this); - } - if (*ppv) { - AddRef(); - return S_OK; - } - return E_NOINTERFACE; -} - -STDMETHODIMP_(ULONG) nsTextStore::AddRef() -{ - return ++mRefCnt; -} - -STDMETHODIMP_(ULONG) nsTextStore::Release() -{ - --mRefCnt; - if (0 != mRefCnt) - return mRefCnt; - delete this; - return 0; -} - -STDMETHODIMP -nsTextStore::AdviseSink(REFIID riid, - IUnknown *punk, - DWORD dwMask) -{ - NS_ENSURE_TRUE(punk && IID_ITextStoreACPSink == riid, E_INVALIDARG); - if (!mSink) { - // Install sink - punk->QueryInterface(IID_ITextStoreACPSink, getter_AddRefs(mSink)); - NS_ENSURE_TRUE(mSink, E_UNEXPECTED); - } else { - // If sink is already installed we check to see if they are the same - // Get IUnknown from both sides for comparison - nsRefPtr comparison1, comparison2; - punk->QueryInterface(IID_IUnknown, getter_AddRefs(comparison1)); - mSink->QueryInterface(IID_IUnknown, getter_AddRefs(comparison2)); - if (comparison1 != comparison2) - return CONNECT_E_ADVISELIMIT; - } - // Update mask either for a new sink or an existing sink - mSinkMask = dwMask; - PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, - ("TSF: Sink installed, punk=%08x\n", punk)); - return S_OK; -} - -STDMETHODIMP -nsTextStore::UnadviseSink(IUnknown *punk) -{ - NS_ENSURE_TRUE(punk, E_INVALIDARG); - NS_ENSURE_TRUE(mSink, CONNECT_E_NOCONNECTION); - // Get IUnknown from both sides for comparison - nsRefPtr comparison1, comparison2; - punk->QueryInterface(IID_IUnknown, getter_AddRefs(comparison1)); - mSink->QueryInterface(IID_IUnknown, getter_AddRefs(comparison2)); - // Unadvise only if sinks are the same - NS_ENSURE_TRUE(comparison1 == comparison2, CONNECT_E_NOCONNECTION); - mSink = NULL; - mSinkMask = 0; - PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, - ("TSF: Sink removed, punk=%08x\n", punk)); - return S_OK; -} - -STDMETHODIMP -nsTextStore::RequestLock(DWORD dwLockFlags, - HRESULT *phrSession) -{ - NS_ENSURE_TRUE(mSink, E_FAIL); - NS_ENSURE_TRUE(phrSession, E_INVALIDARG); - if (mLock) { - // only time when reentrant lock is allowed is when caller holds a - // read-only lock and is requesting an async write lock - if (TS_LF_READ == (mLock & TS_LF_READWRITE) && - TS_LF_READWRITE == (dwLockFlags & TS_LF_READWRITE) && - !(dwLockFlags & TS_LF_SYNC)) { - *phrSession = TS_S_ASYNC; - mLockQueued = dwLockFlags & (~TS_LF_SYNC); - } else { - // no more locks allowed - *phrSession = TS_E_SYNCHRONOUS; - return E_FAIL; - } - } else { - // put on lock - mLock = dwLockFlags & (~TS_LF_SYNC); - *phrSession = mSink->OnLockGranted(mLock); - while (mLockQueued) { - mLock = mLockQueued; - mLockQueued = 0; - mSink->OnLockGranted(mLock); - } - mLock = 0; - } - return S_OK; -} - -STDMETHODIMP -nsTextStore::GetStatus(TS_STATUS *pdcs) -{ - NS_ENSURE_TRUE(pdcs, E_INVALIDARG); - pdcs->dwDynamicFlags = 0; - // we use a "flat" text model for TSF support so no hidden text - pdcs->dwStaticFlags = TS_SS_NOHIDDENTEXT; - return S_OK; -} - -STDMETHODIMP -nsTextStore::QueryInsert(LONG acpTestStart, - LONG acpTestEnd, - ULONG cch, - LONG *pacpResultStart, - LONG *pacpResultEnd) -{ - PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, - ("TSF: QueryInsert, start=%ld end=%ld cch=%lu\n", - acpTestStart, acpTestEnd, cch)); - // We don't test to see if these positions are - // after the end of document for performance reasons - NS_ENSURE_TRUE(0 <= acpTestStart && acpTestStart <= acpTestEnd && - pacpResultStart && pacpResultEnd, E_INVALIDARG); - - // XXX need to adjust to cluster boundary - // Assume we are given good offsets for now - *pacpResultStart = acpTestStart; - *pacpResultEnd = acpTestStart + cch; - - PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, - ("TSF: QueryInsert SUCCEEDED\n")); - return S_OK; -} - -STDMETHODIMP -nsTextStore::GetSelection(ULONG ulIndex, - ULONG ulCount, - TS_SELECTION_ACP *pSelection, - ULONG *pcFetched) -{ - NS_ENSURE_TRUE(TS_LF_READ == (mLock & TS_LF_READ), TS_E_NOLOCK); - NS_ENSURE_TRUE(ulCount && pSelection && pcFetched, E_INVALIDARG); - - *pcFetched = 0; - NS_ENSURE_TRUE(TS_DEFAULT_SELECTION == ulIndex || 0 == ulIndex, - TS_E_NOSELECTION); - if (mCompositionView) { - // Emulate selection during compositions - *pSelection = mCompositionSelection; - } else { - // Construct and initialize an event to get selection info - nsQueryContentEvent event(PR_TRUE, NS_QUERY_SELECTED_TEXT, mWindow); - mWindow->InitEvent(event); - mWindow->DispatchWindowEvent(&event); - NS_ENSURE_TRUE(event.mSucceeded, E_FAIL); - // Usually the selection anchor (beginning) position corresponds to the - // TSF start and the selection focus (ending) position corresponds to - // the TSF end, but if selection is reversed the focus now corresponds - // to the TSF start and the anchor now corresponds to the TSF end - pSelection->acpStart = event.mReply.mOffset; - pSelection->acpEnd = pSelection->acpStart + event.mReply.mString.Length(); - pSelection->style.ase = event.mReply.mString.Length() && - event.mReply.mReversed ? TS_AE_START : TS_AE_END; - // No support for interim character - pSelection->style.fInterimChar = 0; - } - *pcFetched = 1; - return S_OK; -} - -HRESULT -nsTextStore::SetSelectionInternal(const TS_SELECTION_ACP* pSelection) -{ - PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, - ("TSF: SetSelection, sel=%ld-%ld\n", - pSelection->acpStart, pSelection->acpEnd)); - if (mCompositionView) { - // Emulate selection during compositions - NS_ENSURE_TRUE(pSelection->acpStart >= mCompositionStart && - pSelection->acpEnd <= mCompositionStart + - LONG(mCompositionString.Length()), TS_E_INVALIDPOS); - mCompositionSelection = *pSelection; - } else { - nsSelectionEvent event(PR_TRUE, NS_SELECTION_SET, mWindow); - event.mOffset = pSelection->acpStart; - event.mLength = PRUint32(pSelection->acpEnd - pSelection->acpStart); - event.mReversed = pSelection->style.ase == TS_AE_START; - mWindow->InitEvent(event); - mWindow->DispatchWindowEvent(&event); - NS_ENSURE_TRUE(event.mSucceeded, E_FAIL); - } - PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, - ("TSF: SetSelection SUCCEEDED\n")); - return S_OK; -} - -STDMETHODIMP -nsTextStore::SetSelection(ULONG ulCount, - const TS_SELECTION_ACP *pSelection) -{ - NS_ENSURE_TRUE(TS_LF_READWRITE == (mLock & TS_LF_READWRITE), TS_E_NOLOCK); - NS_ENSURE_TRUE(1 == ulCount && pSelection, E_INVALIDARG); - - return SetSelectionInternal(pSelection); -} - -STDMETHODIMP -nsTextStore::GetText(LONG acpStart, - LONG acpEnd, - WCHAR *pchPlain, - ULONG cchPlainReq, - ULONG *pcchPlainOut, - TS_RUNINFO *prgRunInfo, - ULONG ulRunInfoReq, - ULONG *pulRunInfoOut, - LONG *pacpNext) -{ - NS_ENSURE_TRUE(TS_LF_READ == (mLock & TS_LF_READ), TS_E_NOLOCK); - NS_ENSURE_TRUE(pcchPlainOut && (pchPlain || prgRunInfo) && - (!cchPlainReq == !pchPlain) && - (!ulRunInfoReq == !prgRunInfo), E_INVALIDARG); - NS_ENSURE_TRUE(0 <= acpStart && -1 <= acpEnd && - (-1 == acpEnd || acpStart <= acpEnd), TS_E_INVALIDPOS); - - // Making sure to NULL-terminate string just to be on the safe side - *pcchPlainOut = 0; - if (pchPlain && cchPlainReq) *pchPlain = NULL; - if (pulRunInfoOut) *pulRunInfoOut = 0; - if (pacpNext) *pacpNext = acpStart; - if (prgRunInfo && ulRunInfoReq) { - prgRunInfo->uCount = 0; - prgRunInfo->type = TS_RT_PLAIN; - } - PRUint32 length = -1 == acpEnd ? PR_UINT32_MAX : PRUint32(acpEnd - acpStart); - if (cchPlainReq && cchPlainReq - 1 < length) { - length = cchPlainReq - 1; - } - if (length) { - LONG compNewStart = 0, compOldEnd = 0, compNewEnd = 0; - if (mCompositionView) { - // Sometimes GetText gets called between InsertTextAtSelection and - // OnUpdateComposition. In this case the returned text would - // be out of sync because we haven't sent NS_TEXT_TEXT in - // OnUpdateComposition yet. Manually resync here. - compOldEnd = PR_MIN(LONG(length) + acpStart, - mCompositionLength + mCompositionStart); - compNewEnd = PR_MIN(LONG(length) + acpStart, - LONG(mCompositionString.Length()) + mCompositionStart); - compNewStart = PR_MAX(acpStart, mCompositionStart); - // Check if the range is affected - if (compOldEnd > compNewStart || compNewEnd > compNewStart) { - NS_ASSERTION(compOldEnd >= mCompositionStart && - compNewEnd >= mCompositionStart, "Range end is less than start\n"); - length = PRUint32(LONG(length) + compOldEnd - compNewEnd); - } - } - // Send NS_QUERY_TEXT_CONTENT to get text content - nsQueryContentEvent event(PR_TRUE, NS_QUERY_TEXT_CONTENT, mWindow); - mWindow->InitEvent(event); - event.InitForQueryTextContent(PRUint32(acpStart), length); - mWindow->DispatchWindowEvent(&event); - NS_ENSURE_TRUE(event.mSucceeded, E_FAIL); - - if (compOldEnd > compNewStart || compNewEnd > compNewStart) { - // Resync composition string - const PRUnichar* compStrStart = mCompositionString.BeginReading() + - PR_MAX(compNewStart - mCompositionStart, 0); - event.mReply.mString.Replace(compNewStart - acpStart, - compOldEnd - mCompositionStart, compStrStart, - compNewEnd - mCompositionStart); - length = PRUint32(LONG(length) - compOldEnd + compNewEnd); - } - NS_ENSURE_TRUE(-1 == acpEnd || event.mReply.mString.Length() == length, - TS_E_INVALIDPOS); - length = PR_MIN(length, event.mReply.mString.Length()); - - if (pchPlain && cchPlainReq) { - memcpy(pchPlain, event.mReply.mString.BeginReading(), - length * sizeof(*pchPlain)); - pchPlain[length] = NULL; - *pcchPlainOut = length; - } - if (prgRunInfo && ulRunInfoReq) { - prgRunInfo->uCount = length; - prgRunInfo->type = TS_RT_PLAIN; - if (pulRunInfoOut) *pulRunInfoOut = 1; - } - if (pacpNext) *pacpNext = acpStart + length; - } - return S_OK; -} - -STDMETHODIMP -nsTextStore::SetText(DWORD dwFlags, - LONG acpStart, - LONG acpEnd, - const WCHAR *pchText, - ULONG cch, - TS_TEXTCHANGE *pChange) -{ - // Per SDK documentation, and since we don't have better - // ways to do this, this method acts as a helper to - // call SetSelection followed by InsertTextAtSelection - NS_ENSURE_TRUE(TS_LF_READWRITE == (mLock & TS_LF_READWRITE), TS_E_NOLOCK); - TS_SELECTION_ACP selection; - selection.acpStart = acpStart; - selection.acpEnd = acpEnd; - selection.style.ase = TS_AE_END; - selection.style.fInterimChar = 0; - // Set selection to desired range - NS_ENSURE_TRUE(SUCCEEDED(SetSelectionInternal(&selection)), E_FAIL); - // Replace just selected text - return InsertTextAtSelection(TS_IAS_NOQUERY, pchText, cch, - NULL, NULL, pChange); -} - -STDMETHODIMP -nsTextStore::GetFormattedText(LONG acpStart, - LONG acpEnd, - IDataObject **ppDataObject) -{ - // no support for formatted text - return E_NOTIMPL; -} - -STDMETHODIMP -nsTextStore::GetEmbedded(LONG acpPos, - REFGUID rguidService, - REFIID riid, - IUnknown **ppunk) -{ - // embedded objects are not supported - return E_NOTIMPL; -} - -STDMETHODIMP -nsTextStore::QueryInsertEmbedded(const GUID *pguidService, - const FORMATETC *pFormatEtc, - BOOL *pfInsertable) -{ - // embedded objects are not supported - *pfInsertable = FALSE; - return S_OK; -} - -STDMETHODIMP -nsTextStore::InsertEmbedded(DWORD dwFlags, - LONG acpStart, - LONG acpEnd, - IDataObject *pDataObject, - TS_TEXTCHANGE *pChange) -{ - // embedded objects are not supported - return E_NOTIMPL; -} - -STDMETHODIMP -nsTextStore::RequestSupportedAttrs(DWORD dwFlags, - ULONG cFilterAttrs, - const TS_ATTRID *paFilterAttrs) -{ - // no attributes defined - return S_OK; -} - -STDMETHODIMP -nsTextStore::RequestAttrsAtPosition(LONG acpPos, - ULONG cFilterAttrs, - const TS_ATTRID *paFilterAttrs, - DWORD dwFlags) -{ - // no per character attributes defined - return S_OK; -} - -STDMETHODIMP -nsTextStore::RequestAttrsTransitioningAtPosition(LONG acpPos, - ULONG cFilterAttrs, - const TS_ATTRID *paFilterAttr, - DWORD dwFlags) -{ - // no per character attributes defined - return S_OK; -} - -STDMETHODIMP -nsTextStore::FindNextAttrTransition(LONG acpStart, - LONG acpHalt, - ULONG cFilterAttrs, - const TS_ATTRID *paFilterAttrs, - DWORD dwFlags, - LONG *pacpNext, - BOOL *pfFound, - LONG *plFoundOffset) -{ - NS_ENSURE_TRUE(pacpNext && pfFound && plFoundOffset, E_INVALIDARG); - // no per character attributes defined - *pacpNext = *plFoundOffset = acpHalt; - *pfFound = FALSE; - return S_OK; -} - -STDMETHODIMP -nsTextStore::RetrieveRequestedAttrs(ULONG ulCount, - TS_ATTRVAL *paAttrVals, - ULONG *pcFetched) -{ - NS_ENSURE_TRUE(pcFetched && ulCount && paAttrVals, E_INVALIDARG); - // no attributes defined - *pcFetched = 0; - return S_OK; -} - -STDMETHODIMP -nsTextStore::GetEndACP(LONG *pacp) -{ - NS_ENSURE_TRUE(TS_LF_READ == (mLock & TS_LF_READ), TS_E_NOLOCK); - NS_ENSURE_TRUE(pacp, E_INVALIDARG); - // Flattened text is retrieved and its length returned - nsQueryContentEvent event(PR_TRUE, NS_QUERY_TEXT_CONTENT, mWindow); - mWindow->InitEvent(event); - // Return entire text - event.InitForQueryTextContent(0, PR_INT32_MAX); - mWindow->DispatchWindowEvent(&event); - NS_ENSURE_TRUE(event.mSucceeded, E_FAIL); - *pacp = LONG(event.mReply.mString.Length()); - return S_OK; -} - -#define TEXTSTORE_DEFAULT_VIEW (1) - -STDMETHODIMP -nsTextStore::GetActiveView(TsViewCookie *pvcView) -{ - NS_ENSURE_TRUE(pvcView, E_INVALIDARG); - *pvcView = TEXTSTORE_DEFAULT_VIEW; - return S_OK; -} - -STDMETHODIMP -nsTextStore::GetACPFromPoint(TsViewCookie vcView, - const POINT *pt, - DWORD dwFlags, - LONG *pacp) -{ - NS_ENSURE_TRUE(TS_LF_READ == (mLock & TS_LF_READ), TS_E_NOLOCK); - NS_ENSURE_TRUE(TEXTSTORE_DEFAULT_VIEW == vcView, E_INVALIDARG); - // not supported for now - return E_NOTIMPL; -} - -STDMETHODIMP -nsTextStore::GetTextExt(TsViewCookie vcView, - LONG acpStart, - LONG acpEnd, - RECT *prc, - BOOL *pfClipped) -{ - NS_ENSURE_TRUE(TS_LF_READ == (mLock & TS_LF_READ), TS_E_NOLOCK); - NS_ENSURE_TRUE(TEXTSTORE_DEFAULT_VIEW == vcView && prc && pfClipped, - E_INVALIDARG); - NS_ENSURE_TRUE(acpStart >= 0 && acpEnd >= acpStart, TS_E_INVALIDPOS); - - // use NS_QUERY_TEXT_RECT to get rect in system, screen coordinates - nsQueryContentEvent event(PR_TRUE, NS_QUERY_TEXT_RECT, mWindow); - mWindow->InitEvent(event); - event.InitForQueryTextRect(acpStart, acpEnd - acpStart); - mWindow->DispatchWindowEvent(&event); - NS_ENSURE_TRUE(event.mSucceeded, TS_E_INVALIDPOS); - // IMEs don't like empty rects, fix here - if (event.mReply.mRect.width <= 0) - event.mReply.mRect.width = 1; - if (event.mReply.mRect.height <= 0) - event.mReply.mRect.height = 1; - - // convert to unclipped screen rect - nsWindow* refWindow = static_cast( - event.mReply.mFocusedWidget ? event.mReply.mFocusedWidget : mWindow); - // Result rect is in top level widget coordinates - refWindow = refWindow->GetTopLevelWindow(PR_FALSE); - NS_ENSURE_TRUE(refWindow, E_FAIL); - - nsresult rv = refWindow->WidgetToScreen(event.mReply.mRect, - event.mReply.mRect); - NS_ENSURE_SUCCESS(rv, E_FAIL); - - // get bounding screen rect to test for clipping - HRESULT hr = GetScreenExt(vcView, prc); - NS_ENSURE_TRUE(SUCCEEDED(hr), hr); - - // clip text rect to bounding rect - RECT textRect; - ::SetRect(&textRect, event.mReply.mRect.x, event.mReply.mRect.y, - event.mReply.mRect.XMost(), event.mReply.mRect.YMost()); - if (!::IntersectRect(prc, prc, &textRect)) - // Text is not visible - ::SetRectEmpty(prc); - - // not equal if text rect was clipped - *pfClipped = !::EqualRect(prc, &textRect); - return S_OK; -} - -STDMETHODIMP -nsTextStore::GetScreenExt(TsViewCookie vcView, - RECT *prc) -{ - NS_ENSURE_TRUE(TEXTSTORE_DEFAULT_VIEW == vcView && prc, E_INVALIDARG); - // use NS_QUERY_EDITOR_RECT to get rect in system, screen coordinates - nsQueryContentEvent event(PR_TRUE, NS_QUERY_EDITOR_RECT, mWindow); - mWindow->InitEvent(event); - mWindow->DispatchWindowEvent(&event); - NS_ENSURE_TRUE(event.mSucceeded, E_FAIL); - - nsWindow* refWindow = static_cast( - event.mReply.mFocusedWidget ? event.mReply.mFocusedWidget : mWindow); - // Result rect is in top level widget coordinates - refWindow = refWindow->GetTopLevelWindow(PR_FALSE); - NS_ENSURE_TRUE(refWindow, E_FAIL); - - nsIntRect boundRect; - nsresult rv = refWindow->GetClientBounds(boundRect); - NS_ENSURE_SUCCESS(rv, E_FAIL); - - // Clip frame rect to window rect - boundRect.IntersectRect(event.mReply.mRect, boundRect); - rv = refWindow->WidgetToScreen(boundRect, boundRect); - NS_ENSURE_SUCCESS(rv, E_FAIL); - - if (!boundRect.IsEmpty()) { - ::SetRect(prc, boundRect.x, boundRect.y, - boundRect.XMost(), boundRect.YMost()); - } else { - ::SetRectEmpty(prc); - } - return S_OK; -} - -STDMETHODIMP -nsTextStore::GetWnd(TsViewCookie vcView, - HWND *phwnd) -{ - NS_ENSURE_TRUE(TEXTSTORE_DEFAULT_VIEW == vcView && phwnd, E_INVALIDARG); - *phwnd = mWindow->GetWindowHandle(); - return S_OK; -} - -STDMETHODIMP -nsTextStore::InsertTextAtSelection(DWORD dwFlags, - const WCHAR *pchText, - ULONG cch, - LONG *pacpStart, - LONG *pacpEnd, - TS_TEXTCHANGE *pChange) -{ - PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, - ("TSF: InsertTextAtSelection, cch=%lu\n", cch)); - NS_ENSURE_TRUE(TS_LF_READWRITE == (mLock & TS_LF_READWRITE), TS_E_NOLOCK); - NS_ENSURE_TRUE(!cch || pchText, E_INVALIDARG); - - // Get selection first - TS_SELECTION_ACP sel; - ULONG selFetched; - NS_ENSURE_TRUE(SUCCEEDED(GetSelection( - TS_DEFAULT_SELECTION, 1, &sel, &selFetched)) && selFetched, E_FAIL); - if (TS_IAS_QUERYONLY == dwFlags) { - NS_ENSURE_TRUE(pacpStart && pacpEnd, E_INVALIDARG); - // Simulate text insertion - *pacpStart = sel.acpStart; - *pacpEnd = sel.acpEnd; - if (pChange) { - pChange->acpStart = sel.acpStart; - pChange->acpOldEnd = sel.acpEnd; - pChange->acpNewEnd = sel.acpStart + cch; - } - } else { - NS_ENSURE_TRUE(pChange, E_INVALIDARG); - NS_ENSURE_TRUE(TS_IAS_NOQUERY == dwFlags || (pacpStart && pacpEnd), - E_INVALIDARG); - if (mCompositionView) { - // Emulate text insertion during compositions, because during a - // composition, editor expects the whole composition string to - // be sent in NS_TEXT_TEXT, not just the inserted part. - // The actual NS_TEXT_TEXT is sent in OnUpdateComposition, which - // should get called by TSF after this returns - mCompositionString.Replace(PRUint32(sel.acpStart - mCompositionStart), - sel.acpEnd - sel.acpStart, pchText, cch); - - mCompositionSelection.acpStart += cch; - mCompositionSelection.acpEnd = mCompositionSelection.acpStart; - mCompositionSelection.style.ase = TS_AE_END; - // OnUpdateComposition is not called here because it will - // result in fun visual artifacts - PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, - ("TSF: InsertTextAtSelection, replaced=%lu-%lu\n", - sel.acpStart - mCompositionStart, - sel.acpEnd - mCompositionStart)); - } else { - // Use a temporary composition to contain the text - nsCompositionEvent compEvent(PR_TRUE, NS_COMPOSITION_START, mWindow); - mWindow->InitEvent(compEvent); - mWindow->DispatchWindowEvent(&compEvent); - nsTextEvent event(PR_TRUE, NS_TEXT_TEXT, mWindow); - mWindow->InitEvent(event); - if (!cch) { - // XXX See OnEndComposition comment on inserting empty strings - event.theText = NS_LITERAL_STRING(" "); - mWindow->DispatchWindowEvent(&event); - } - event.theText.Assign(pchText, cch); - event.theText.ReplaceSubstring(NS_LITERAL_STRING("\r\n"), - NS_LITERAL_STRING("\n")); - mWindow->DispatchWindowEvent(&event); - compEvent.message = NS_COMPOSITION_END; - mWindow->DispatchWindowEvent(&compEvent); - } - pChange->acpStart = sel.acpStart; - pChange->acpOldEnd = sel.acpEnd; - // Get new selection - NS_ENSURE_TRUE(SUCCEEDED(GetSelection( - TS_DEFAULT_SELECTION, 1, &sel, &selFetched)) && selFetched, E_FAIL); - pChange->acpNewEnd = sel.acpEnd; - if (TS_IAS_NOQUERY != dwFlags) { - *pacpStart = pChange->acpStart; - *pacpEnd = pChange->acpNewEnd; - } - } - PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, - ("TSF: InsertTextAtSelection SUCCEEDED\n")); - return S_OK; -} - -STDMETHODIMP -nsTextStore::InsertEmbeddedAtSelection(DWORD dwFlags, - IDataObject *pDataObject, - LONG *pacpStart, - LONG *pacpEnd, - TS_TEXTCHANGE *pChange) -{ - // embedded objects are not supported - return E_NOTIMPL; -} - -static HRESULT -GetRangeExtent(ITfRange* aRange, LONG* aStart, LONG* aLength) -{ - nsRefPtr rangeACP; - aRange->QueryInterface(IID_ITfRangeACP, getter_AddRefs(rangeACP)); - NS_ENSURE_TRUE(rangeACP, E_FAIL); - return rangeACP->GetExtent(aStart, aLength); -} - -HRESULT -nsTextStore::OnStartCompositionInternal(ITfCompositionView* pComposition, - ITfRange* aRange, - PRBool aPreserveSelection) -{ - mCompositionView = pComposition; - HRESULT hr = GetRangeExtent(aRange, &mCompositionStart, &mCompositionLength); - NS_ENSURE_TRUE(SUCCEEDED(hr), hr); - - PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, - ("TSF: OnStartComposition, range=%ld-%ld\n", mCompositionStart, - mCompositionStart + mCompositionLength)); - - // Select composition range so the new composition replaces the range - nsSelectionEvent selEvent(PR_TRUE, NS_SELECTION_SET, mWindow); - mWindow->InitEvent(selEvent); - selEvent.mOffset = PRUint32(mCompositionStart); - selEvent.mLength = PRUint32(mCompositionLength); - selEvent.mReversed = PR_FALSE; - mWindow->DispatchWindowEvent(&selEvent); - NS_ENSURE_TRUE(selEvent.mSucceeded, E_FAIL); - - // Set up composition - nsQueryContentEvent queryEvent(PR_TRUE, NS_QUERY_SELECTED_TEXT, mWindow); - mWindow->InitEvent(queryEvent); - mWindow->DispatchWindowEvent(&queryEvent); - NS_ENSURE_TRUE(queryEvent.mSucceeded, E_FAIL); - mCompositionString = queryEvent.mReply.mString; - if (!aPreserveSelection) { - mCompositionSelection.acpStart = mCompositionStart; - mCompositionSelection.acpEnd = mCompositionStart + mCompositionLength; - mCompositionSelection.style.ase = TS_AE_END; - mCompositionSelection.style.fInterimChar = FALSE; - } - nsCompositionEvent event(PR_TRUE, NS_COMPOSITION_START, mWindow); - mWindow->InitEvent(event); - mWindow->DispatchWindowEvent(&event); - return S_OK; -} - -STDMETHODIMP -nsTextStore::OnStartComposition(ITfCompositionView* pComposition, - BOOL* pfOk) -{ - *pfOk = FALSE; - - // Only one composition at a time - if (mCompositionView) - return S_OK; - - nsRefPtr range; - HRESULT hr = pComposition->GetRange(getter_AddRefs(range)); - NS_ENSURE_TRUE(SUCCEEDED(hr), hr); - hr = OnStartCompositionInternal(pComposition, range, PR_FALSE); - if (SUCCEEDED(hr)) - *pfOk = TRUE; - return hr; -} - -STDMETHODIMP -nsTextStore::OnUpdateComposition(ITfCompositionView* pComposition, - ITfRange* pRangeNew) -{ - NS_ENSURE_TRUE(mCompositionView && - mCompositionView == pComposition && - mDocumentMgr && mContext, E_UNEXPECTED); - - // Getting display attributes is *really* complicated! - // We first get the context and the property objects to query for - // attributes, but since a big range can have a variety of values for - // the attribute, we have to find out all the ranges that have distinct - // attribute values. Then we query for what the value represents through - // the display attribute manager and translate that to nsTextRange to be - // sent in NS_TEXT_TEXT - if (!pRangeNew) // pRangeNew is null when the update is not complete - return S_OK; - - // Get starting offset of the composition - LONG compStart = 0, compLength = 0; - HRESULT hr = GetRangeExtent(pRangeNew, &compStart, &compLength); - NS_ENSURE_TRUE(SUCCEEDED(hr), hr); - if (mCompositionStart != compStart || - mCompositionString.Length() != compLength) { - // If the queried composition length is different from the length - // of our composition string, OnUpdateComposition is being called - // because a part of the original composition was committed. - // Reflect that by committing existing composition and starting - // a new one. OnEndComposition followed by OnStartComposition - // will accomplish this automagically. - OnEndComposition(pComposition); - OnStartCompositionInternal(pComposition, pRangeNew, PR_TRUE); - PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, - ("TSF: OnUpdateComposition, (reset) range=%ld-%ld\n", - compStart, compStart + compLength)); - } else { - mCompositionLength = compLength; - PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, - ("TSF: OnUpdateComposition, range=%ld-%ld\n", - compStart, compStart + compLength)); - } - - nsRefPtr prop; - hr = mContext->GetProperty(GUID_PROP_ATTRIBUTE, getter_AddRefs(prop)); - NS_ENSURE_TRUE(SUCCEEDED(hr) && prop, hr); - hr = LoadManagers(); - NS_ENSURE_TRUE(SUCCEEDED(hr), hr); - - // Use NS_TEXT_TEXT to set composition string - nsTextEvent event(PR_TRUE, NS_TEXT_TEXT, mWindow); - mWindow->InitEvent(event); - - VARIANT propValue; - ::VariantInit(&propValue); - nsRefPtr range; - nsRefPtr enumRanges; - hr = prop->EnumRanges(TfEditCookie(mEditCookie), - getter_AddRefs(enumRanges), pRangeNew); - NS_ENSURE_TRUE(SUCCEEDED(hr) && enumRanges, hr); - - nsAutoTArray textRanges; - nsTextRange newRange; - newRange.mStartOffset = PRUint32(mCompositionSelection.acpStart - compStart); - newRange.mEndOffset = PRUint32(mCompositionSelection.acpEnd - compStart); - newRange.mRangeType = NS_TEXTRANGE_CARETPOSITION; - textRanges.AppendElement(newRange); - // No matter if we have display attribute info or not, - // we always pass in at least one range to NS_TEXT_TEXT - newRange.mStartOffset = 0; - newRange.mEndOffset = mCompositionString.Length(); - newRange.mRangeType = NS_TEXTRANGE_RAWINPUT; - textRanges.AppendElement(newRange); - - while (S_OK == enumRanges->Next(1, getter_AddRefs(range), NULL) && range) { - - LONG start = 0, length = 0; - if (FAILED(GetRangeExtent(range, &start, &length))) continue; - - newRange.mStartOffset = PRUint32(start - compStart); - // The end of the last range in the array is - // always kept at the end of composition - newRange.mEndOffset = mCompositionString.Length(); - - // Who came up with this convoluted way that we have to follow? - ::VariantClear(&propValue); - hr = prop->GetValue(TfEditCookie(mEditCookie), range, &propValue); - if (FAILED(hr) || VT_I4 != propValue.vt) continue; - - GUID guid; - hr = mCatMgr->GetGUID(DWORD(propValue.lVal), &guid); - if (FAILED(hr)) continue; - - nsRefPtr info; - hr = mDAMgr->GetDisplayAttributeInfo( - guid, getter_AddRefs(info), NULL); - if (FAILED(hr) || !info) continue; - - TF_DISPLAYATTRIBUTE attr; - hr = info->GetAttributeInfo(&attr); - if (FAILED(hr)) continue; - - switch (attr.bAttr) { - case TF_ATTR_TARGET_CONVERTED: - newRange.mRangeType = NS_TEXTRANGE_SELECTEDCONVERTEDTEXT; - break; - case TF_ATTR_CONVERTED: - newRange.mRangeType = NS_TEXTRANGE_CONVERTEDTEXT; - break; - case TF_ATTR_TARGET_NOTCONVERTED: - newRange.mRangeType = NS_TEXTRANGE_SELECTEDRAWTEXT; - break; - default: - newRange.mRangeType = NS_TEXTRANGE_RAWINPUT; - break; - } - - nsTextRange& lastRange = textRanges[textRanges.Length() - 1]; - if (lastRange.mStartOffset == newRange.mStartOffset) { - // Replace range if last range is the same as this one - // So that ranges don't overlap and confuse the editor - lastRange = newRange; - } else { - lastRange.mEndOffset = newRange.mStartOffset; - textRanges.AppendElement(newRange); - } - } - - event.theText = mCompositionString; - event.rangeArray = textRanges.Elements(); - event.rangeCount = textRanges.Length(); - mWindow->DispatchWindowEvent(&event); - ::VariantClear(&propValue); - return S_OK; -} - -STDMETHODIMP -nsTextStore::OnEndComposition(ITfCompositionView* pComposition) -{ - NS_ENSURE_TRUE(mCompositionView && - mCompositionView == pComposition, E_UNEXPECTED); - PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, - ("TSF: OnEndComposition\n")); - - // Use NS_TEXT_TEXT to commit composition string - nsTextEvent textEvent(PR_TRUE, NS_TEXT_TEXT, mWindow); - mWindow->InitEvent(textEvent); - if (!mCompositionString.Length()) { - // XXX HACK! HACK! NS_TEXT_TEXT handler specifically rejects - // first-time empty strings as workaround for another IME bug - // and our request will be rejected if this is the first time - // we are sending NS_TEXT_TEXT. The workaround is to send it a - // non-empty dummy string first. - textEvent.theText = NS_LITERAL_STRING(" "); - mWindow->DispatchWindowEvent(&textEvent); - } - textEvent.theText = mCompositionString; - textEvent.theText.ReplaceSubstring(NS_LITERAL_STRING("\r\n"), - NS_LITERAL_STRING("\n")); - mWindow->DispatchWindowEvent(&textEvent); - - nsCompositionEvent event(PR_TRUE, NS_COMPOSITION_END, mWindow); - mWindow->InitEvent(event); - mWindow->DispatchWindowEvent(&event); - - mCompositionView = NULL; - mCompositionString.Truncate(0); - // Maintain selection - SetSelectionInternal(&mCompositionSelection); - return S_OK; -} - -nsresult -nsTextStore::OnFocusChange(PRBool aFocus, - nsWindow* aWindow, - PRUint32 aIMEEnabled) -{ - // no change notifications if TSF is disabled - if (!sTsfThreadMgr || !sTsfTextStore) - return NS_ERROR_NOT_AVAILABLE; - - if (aFocus) { - if (sTsfTextStore->Create(aWindow, aIMEEnabled)) - sTsfTextStore->Focus(); - } else { - sTsfTextStore->Destroy(); - } - return NS_OK; -} - -nsresult -nsTextStore::OnTextChangeInternal(PRUint32 aStart, - PRUint32 aOldEnd, - PRUint32 aNewEnd) -{ - if (!mLock && mSink && 0 != (mSinkMask & TS_AS_TEXT_CHANGE)) { - mTextChange.acpStart = PR_MIN(mTextChange.acpStart, LONG(aStart)); - mTextChange.acpOldEnd = PR_MAX(mTextChange.acpOldEnd, LONG(aOldEnd)); - mTextChange.acpNewEnd = PR_MAX(mTextChange.acpNewEnd, LONG(aNewEnd)); - ::PostMessageW(mWindow->GetWindowHandle(), - WM_USER_TSF_TEXTCHANGE, 0, NULL); - } - return NS_OK; -} - -void -nsTextStore::OnTextChangeMsgInternal(void) -{ - if (!mLock && mSink && 0 != (mSinkMask & TS_AS_TEXT_CHANGE) && - PR_INT32_MAX > mTextChange.acpStart) { - mSink->OnTextChange(0, &mTextChange); - mTextChange.acpStart = PR_INT32_MAX; - mTextChange.acpOldEnd = mTextChange.acpNewEnd = 0; - } -} - -nsresult -nsTextStore::OnSelectionChangeInternal(void) -{ - if (!mLock && mSink && 0 != (mSinkMask & TS_AS_SEL_CHANGE)) { - mSink->OnSelectionChange(); - } - return NS_OK; -} - -void -nsTextStore::CommitCompositionInternal(PRBool aDiscard) -{ - if (mCompositionView && aDiscard) { - mCompositionString.Truncate(0); - if (mSink && !mLock) { - TS_TEXTCHANGE textChange; - textChange.acpStart = mCompositionStart; - textChange.acpOldEnd = mCompositionStart + mCompositionLength; - textChange.acpNewEnd = mCompositionStart; - mSink->OnTextChange(0, &textChange); - } - } - // Terminate two contexts, the base context (mContext) and the top - // if the top context is not the same as the base context - nsRefPtr context = mContext; - do { - if (context) { - nsRefPtr services; - context->QueryInterface(IID_ITfContextOwnerCompositionServices, - getter_AddRefs(services)); - if (services) - services->TerminateComposition(NULL); - } - if (context != mContext) - break; - if (mDocumentMgr) - mDocumentMgr->GetTop(getter_AddRefs(context)); - } while (context != mContext); -} - -static -PRBool -GetCompartment(IUnknown* pUnk, - const GUID& aID, - ITfCompartment** aCompartment) -{ - if (!pUnk) return PR_FALSE; - - nsRefPtr compMgr; - pUnk->QueryInterface(IID_ITfCompartmentMgr, getter_AddRefs(compMgr)); - if (!compMgr) return PR_FALSE; - - return SUCCEEDED(compMgr->GetCompartment(aID, aCompartment)) && - (*aCompartment) != NULL; -} - -void -nsTextStore::SetIMEOpenState(PRBool aState) -{ - PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, - ("TSF: SetIMEOpenState, state=%lu\n", aState)); - - nsRefPtr comp; - if (!GetCompartment(sTsfThreadMgr, - GUID_COMPARTMENT_KEYBOARD_OPENCLOSE, - getter_AddRefs(comp))) - return; - - VARIANT variant; - variant.vt = VT_I4; - variant.lVal = aState; - comp->SetValue(sTsfClientId, &variant); -} - -PRBool -nsTextStore::GetIMEOpenState(void) -{ - nsRefPtr comp; - if (!GetCompartment(sTsfThreadMgr, - GUID_COMPARTMENT_KEYBOARD_OPENCLOSE, - getter_AddRefs(comp))) - return PR_FALSE; - - VARIANT variant; - ::VariantInit(&variant); - if (SUCCEEDED(comp->GetValue(&variant)) && variant.vt == VT_I4) - return variant.lVal != 0; - - ::VariantClear(&variant); // clear up in case variant.vt != VT_I4 - return PR_FALSE; -} - -void -nsTextStore::SetIMEEnabledInternal(PRUint32 aState) -{ - PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, - ("TSF: SetIMEEnabled, state=%lu\n", aState)); - - VARIANT variant; - variant.vt = VT_I4; - variant.lVal = aState != nsIWidget::IME_STATUS_ENABLED; - - // Set two contexts, the base context (mContext) and the top - // if the top context is not the same as the base context - nsRefPtr context = mContext; - nsRefPtr comp; - do { - if (GetCompartment(context, GUID_COMPARTMENT_KEYBOARD_DISABLED, - getter_AddRefs(comp))) - comp->SetValue(sTsfClientId, &variant); - - if (context != mContext) - break; - if (mDocumentMgr) - mDocumentMgr->GetTop(getter_AddRefs(context)); - } while (context != mContext); -} - -HRESULT -nsTextStore::LoadManagers(void) -{ - HRESULT hr; - if (!mDAMgr) { - hr = ::CoCreateInstance(CLSID_TF_DisplayAttributeMgr, NULL, - CLSCTX_INPROC_SERVER, IID_ITfDisplayAttributeMgr, - getter_AddRefs(mDAMgr)); - NS_ENSURE_TRUE(SUCCEEDED(hr), hr); - } - if (!mCatMgr) { - hr = ::CoCreateInstance(CLSID_TF_CategoryMgr, NULL, CLSCTX_INPROC_SERVER, - IID_ITfCategoryMgr, getter_AddRefs(mCatMgr)); - NS_ENSURE_TRUE(SUCCEEDED(hr), hr); - } - return S_OK; -} - -void -nsTextStore::Initialize(void) -{ -#ifdef PR_LOGGING - if (!sTextStoreLog) - sTextStoreLog = PR_NewLogModule("nsTextStoreWidgets"); -#endif - if (!sTsfThreadMgr) { - PRBool enableTsf = PR_TRUE; - nsCOMPtr prefs = do_GetService(NS_PREFSERVICE_CONTRACTID); - if (prefs) { - nsCOMPtr prefBranch; - prefs->GetBranch(nsnull, getter_AddRefs(prefBranch)); - if (prefBranch && NS_FAILED(prefBranch->GetBoolPref( - "config.windows.use_tsf", &enableTsf))) - enableTsf = PR_TRUE; - } - if (enableTsf) { - if (SUCCEEDED(CoCreateInstance(CLSID_TF_ThreadMgr, NULL, - CLSCTX_INPROC_SERVER, IID_ITfThreadMgr, - reinterpret_cast(&sTsfThreadMgr)))) { - if (FAILED(sTsfThreadMgr->Activate(&sTsfClientId))) { - NS_RELEASE(sTsfThreadMgr); - NS_WARNING("failed to activate TSF\n"); - } - } else - // TSF not installed? - NS_WARNING("failed to create TSF manager\n"); - } - } - if (sTsfThreadMgr && !sTsfTextStore) { - sTsfTextStore = new nsTextStore(); - if (!sTsfTextStore) - NS_ERROR("failed to create text store\n"); - } - if (sTsfThreadMgr && !sFlushTIPInputMessage) { - sFlushTIPInputMessage = ::RegisterWindowMessageW( - NS_LITERAL_STRING("Flush TIP Input Message").get()); - } -} - -void -nsTextStore::Terminate(void) -{ - NS_IF_RELEASE(sTsfTextStore); - if (sTsfThreadMgr) { - sTsfThreadMgr->Deactivate(); - NS_RELEASE(sTsfThreadMgr); - } -} diff --git a/widget/src/windows/nsTextStore.h b/widget/src/windows/nsTextStore.h deleted file mode 100644 index cedc90feaa6d..000000000000 --- a/widget/src/windows/nsTextStore.h +++ /dev/null @@ -1,232 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is mozilla.org code. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Ningjie Chen - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef NSTEXTSTORE_H_ -#define NSTEXTSTORE_H_ - -#include "nsAutoPtr.h" -#include "nsString.h" - -#include -#include - -struct ITfThreadMgr; -struct ITfDocumentMgr; -class nsWindow; - -// It doesn't work well when we notify TSF of text change -// during a mutation observer call because things get broken. -// So we post a message and notify TSF when we get it later. -#define WM_USER_TSF_TEXTCHANGE (WM_USER + 0x100) - -/* - * Text Services Framework text store - */ - -class nsTextStore : public ITextStoreACP, - public ITfContextOwnerCompositionSink -{ -public: /*IUnknown*/ - STDMETHODIMP_(ULONG) AddRef(void); - STDMETHODIMP QueryInterface(REFIID, void**); - STDMETHODIMP_(ULONG) Release(void); - -public: /*ITextStoreACP*/ - STDMETHODIMP AdviseSink(REFIID, IUnknown*, DWORD); - STDMETHODIMP UnadviseSink(IUnknown*); - STDMETHODIMP RequestLock(DWORD, HRESULT*); - STDMETHODIMP GetStatus(TS_STATUS*); - STDMETHODIMP QueryInsert(LONG, LONG, ULONG, LONG*, LONG*); - STDMETHODIMP GetSelection(ULONG, ULONG, TS_SELECTION_ACP*, ULONG*); - STDMETHODIMP SetSelection(ULONG, const TS_SELECTION_ACP*); - STDMETHODIMP GetText(LONG, LONG, WCHAR*, ULONG, ULONG*, TS_RUNINFO*, ULONG, - ULONG*, LONG*); - STDMETHODIMP SetText(DWORD, LONG, LONG, const WCHAR*, ULONG, TS_TEXTCHANGE*); - STDMETHODIMP GetFormattedText(LONG, LONG, IDataObject**); - STDMETHODIMP GetEmbedded(LONG, REFGUID, REFIID, IUnknown**); - STDMETHODIMP QueryInsertEmbedded(const GUID*, const FORMATETC*, BOOL*); - STDMETHODIMP InsertEmbedded(DWORD, LONG, LONG, IDataObject*, TS_TEXTCHANGE*); - STDMETHODIMP RequestSupportedAttrs(DWORD, ULONG, const TS_ATTRID*); - STDMETHODIMP RequestAttrsAtPosition(LONG, ULONG, const TS_ATTRID*, DWORD); - STDMETHODIMP RequestAttrsTransitioningAtPosition(LONG, ULONG, - const TS_ATTRID*, DWORD); - STDMETHODIMP FindNextAttrTransition(LONG, LONG, ULONG, const TS_ATTRID*, - DWORD, LONG*, BOOL*, LONG*); - STDMETHODIMP RetrieveRequestedAttrs(ULONG, TS_ATTRVAL*, ULONG*); - STDMETHODIMP GetEndACP(LONG*); - STDMETHODIMP GetActiveView(TsViewCookie*); - STDMETHODIMP GetACPFromPoint(TsViewCookie, const POINT*, DWORD, LONG*); - STDMETHODIMP GetTextExt(TsViewCookie, LONG, LONG, RECT*, BOOL*); - STDMETHODIMP GetScreenExt(TsViewCookie, RECT*); - STDMETHODIMP GetWnd(TsViewCookie, HWND*); - STDMETHODIMP InsertTextAtSelection(DWORD, const WCHAR*, ULONG, LONG*, LONG*, - TS_TEXTCHANGE*); - STDMETHODIMP InsertEmbeddedAtSelection(DWORD, IDataObject*, LONG*, LONG*, - TS_TEXTCHANGE*); - -public: /*ITfContextOwnerCompositionSink*/ - STDMETHODIMP OnStartComposition(ITfCompositionView*, BOOL*); - STDMETHODIMP OnUpdateComposition(ITfCompositionView*, ITfRange*); - STDMETHODIMP OnEndComposition(ITfCompositionView*); - -public: - static void Initialize(void); - static void Terminate(void); - static void SetIMEOpenState(PRBool); - static PRBool GetIMEOpenState(void); - - static void CommitComposition(PRBool aDiscard) - { - if (!sTsfTextStore) return; - sTsfTextStore->CommitCompositionInternal(aDiscard); - } - - static void SetIMEEnabled(PRUint32 aState) - { - if (!sTsfTextStore) return; - sTsfTextStore->SetIMEEnabledInternal(aState); - } - - static nsresult OnFocusChange(PRBool, nsWindow*, PRUint32); - - static nsresult OnTextChange(PRUint32 aStart, - PRUint32 aOldEnd, - PRUint32 aNewEnd) - { - if (!sTsfTextStore) return NS_OK; - return sTsfTextStore->OnTextChangeInternal(aStart, aOldEnd, aNewEnd); - } - - static void OnTextChangeMsg(void) - { - if (!sTsfTextStore) return; - // Notify TSF text change - // (see comments on WM_USER_TSF_TEXTCHANGE in nsTextStore.h) - sTsfTextStore->OnTextChangeMsgInternal(); - } - - static nsresult OnSelectionChange(void) - { - if (!sTsfTextStore) return NS_OK; - return sTsfTextStore->OnSelectionChangeInternal(); - } - - static void* GetNativeData(void) - { - // Returns the address of the pointer so that the TSF automatic test can - // replace the system object with a custom implementation for testing. - return (void*) & sTsfThreadMgr; - } - -protected: - nsTextStore(); - ~nsTextStore(); - - PRBool Create(nsWindow*, PRUint32); - PRBool Destroy(void); - PRBool Focus(void); - PRBool Blur(void); - - HRESULT LoadManagers(void); - HRESULT SetSelectionInternal(const TS_SELECTION_ACP*); - HRESULT OnStartCompositionInternal(ITfCompositionView*, ITfRange*, PRBool); - void CommitCompositionInternal(PRBool); - void SetIMEEnabledInternal(PRUint32 aState); - nsresult OnTextChangeInternal(PRUint32, PRUint32, PRUint32); - void OnTextChangeMsgInternal(void); - nsresult OnSelectionChangeInternal(void); - - // TSF display attribute manager, loaded by LoadManagers - nsRefPtr mDAMgr; - // TSF category manager, loaded by LoadManagers - nsRefPtr mCatMgr; - - // Document manager for the currently focused editor - nsRefPtr mDocumentMgr; - // Edit cookie associated with the current editing context - DWORD mEditCookie; - // Editing context at the bottom of mDocumentMgr's context stack - nsRefPtr mContext; - // Currently installed notification sink - nsRefPtr mSink; - // TS_AS_* mask of what events to notify - DWORD mSinkMask; - // Window containing the focused editor - nsWindow* mWindow; - // 0 if not locked, otherwise TS_LF_* indicating the current lock - DWORD mLock; - // 0 if no lock is queued, otherwise TS_LF_* indicating the queue lock - DWORD mLockQueued; - // Cumulative text change offsets since the last notification - TS_TEXTCHANGE mTextChange; - // NULL if no composition is active, otherwise the current composition - nsRefPtr mCompositionView; - // Current copy of the active composition string. Only mCompositionString is - // changed during a InsertTextAtSelection call if we have a composition. - // mCompositionString acts as a buffer until OnUpdateComposition is called - // and mCompositionString is flushed to editor through NS_TEXT_TEXT. This - // way all changes are updated in batches to avoid inconsistencies/artifacts. - nsString mCompositionString; - // "Current selection" during a composition, in ACP offsets. - // We use a fake selection during a composition because editor code doesn't - // like us accessing the actual selection during a composition. So we leave - // the actual selection alone and get/set mCompositionSelection instead - // during GetSelection/SetSelection calls. - TS_SELECTION_ACP mCompositionSelection; - // The start and length of the current active composition, in ACP offsets - LONG mCompositionStart; - LONG mCompositionLength; - - // TSF thread manager object for the current application - static ITfThreadMgr* sTsfThreadMgr; - // TSF client ID for the current application - static DWORD sTsfClientId; - // Current text store. Currently only ONE nsTextStore instance is ever used, - // although Create is called when an editor is focused and Destroy called - // when the focused editor is blurred. - static nsTextStore* sTsfTextStore; - - // Message the Tablet Input Panel uses to flush text during blurring. - // See comments in Destroy - static UINT sFlushTIPInputMessage; - -private: - ULONG mRefCnt; -}; - -#endif /*NSTEXTSTORE_H_*/ diff --git a/widget/src/windows/nsWindow.cpp b/widget/src/windows/nsWindow.cpp index 1f953645bb63..f49fa63ebc63 100644 --- a/widget/src/windows/nsWindow.cpp +++ b/widget/src/windows/nsWindow.cpp @@ -34,7 +34,6 @@ * Dainis Jonitis * Christian Biesinger * Mats Palmgren - * Ningjie Chen * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -150,9 +149,7 @@ #include "prprf.h" #include "prmem.h" -#ifdef NS_ENABLE_TSF -#include "nsTextStore.h" -#endif //NS_ENABLE_TSF + #ifdef WINCE @@ -749,18 +746,14 @@ nsWindow::nsWindow() : nsBaseWidget() mIsTopWidgetWindow = PR_FALSE; mLastKeyboardLayout = 0; -#ifdef NS_ENABLE_TSF - if (!sInstanceCount) - nsTextStore::Initialize(); -#endif //NS_ENABLE_TSF - #ifndef WINCE if (!sInstanceCount && SUCCEEDED(::OleInitialize(NULL))) { sIsOleInitialized = TRUE; } NS_ASSERTION(sIsOleInitialized, "***** OLE is not initialized!\n"); -#endif + sInstanceCount++; +#endif } //------------------------------------------------------------------------- @@ -795,17 +788,11 @@ nsWindow::~nsWindow() SetCursor(eCursor_standard); } - sInstanceCount--; - -#ifdef NS_ENABLE_TSF - if (!sInstanceCount) - nsTextStore::Terminate(); -#endif //NS_ENABLE_TSF - #ifndef WINCE // // delete any of the IME structures that we allocated // + sInstanceCount--; if (sInstanceCount == 0) { if (sIMECompUnicode) delete sIMECompUnicode; @@ -2829,12 +2816,6 @@ void* nsWindow::GetNativeData(PRUint32 aDataType) #else return (void*)::GetDC(mWnd); #endif - -#ifdef NS_ENABLE_TSF - case NS_NATIVE_TSF_POINTER: - return nsTextStore::GetNativeData(); -#endif //NS_ENABLE_TSF - case NS_NATIVE_COLORMAP: default: break; @@ -5328,12 +5309,6 @@ PRBool nsWindow::ProcessMessage(UINT msg, WPARAM wParam, LPARAM lParam, LRESULT } #endif // WINCE -#ifdef NS_ENABLE_TSF - else if (msg == WM_USER_TSF_TEXTCHANGE) { - nsTextStore::OnTextChangeMsg(); - } -#endif //NS_ENABLE_TSF - } break; #ifndef WINCE @@ -7458,8 +7433,8 @@ PRBool nsWindow::OnIMEQueryCharPosition(LPARAM aData, LRESULT *oResult) nsIntRect r; if (!useCaretRect) { - nsQueryContentEvent charRect(PR_TRUE, NS_QUERY_TEXT_RECT, this); - charRect.InitForQueryTextRect(offset, 1); + nsQueryContentEvent charRect(PR_TRUE, NS_QUERY_CHARACTER_RECT, this); + charRect.InitForQueryCharacterRect(offset); InitEvent(charRect, &point); DispatchWindowEvent(&charRect); if (charRect.mSucceeded) @@ -7567,11 +7542,6 @@ NS_IMETHODIMP nsWindow::ResetInputState() #ifdef DEBUG_KBSTATE printf("ResetInputState\n"); #endif - -#ifdef NS_ENABLE_TSF - nsTextStore::CommitComposition(PR_FALSE); -#endif //NS_ENABLE_TSF - HIMC hIMC = ::ImmGetContext(mWnd); if (hIMC) { BOOL ret = FALSE; @@ -7589,11 +7559,6 @@ NS_IMETHODIMP nsWindow::SetIMEOpenState(PRBool aState) #ifdef DEBUG_KBSTATE printf("SetIMEOpenState %s\n", (aState ? "Open" : "Close")); #endif - -#ifdef NS_ENABLE_TSF - nsTextStore::SetIMEOpenState(aState); -#endif //NS_ENABLE_TSF - HIMC hIMC = ::ImmGetContext(mWnd); if (hIMC) { ::ImmSetOpenStatus(hIMC, aState ? TRUE : FALSE); @@ -7612,21 +7577,12 @@ NS_IMETHODIMP nsWindow::GetIMEOpenState(PRBool* aState) ::ImmReleaseContext(mWnd, hIMC); } else *aState = PR_FALSE; - -#ifdef NS_ENABLE_TSF - *aState |= nsTextStore::GetIMEOpenState(); -#endif //NS_ENABLE_TSF - return NS_OK; } //========================================================================== NS_IMETHODIMP nsWindow::SetIMEEnabled(PRUint32 aState) { -#ifdef NS_ENABLE_TSF - nsTextStore::SetIMEEnabled(aState); -#endif //NS_ENABLE_TSF - if (sIMEIsComposing) ResetInputState(); mIMEEnabled = aState; @@ -7653,11 +7609,6 @@ NS_IMETHODIMP nsWindow::CancelIMEComposition() #ifdef DEBUG_KBSTATE printf("CancelIMEComposition\n"); #endif - -#ifdef NS_ENABLE_TSF - nsTextStore::CommitComposition(PR_TRUE); -#endif //NS_ENABLE_TSF - HIMC hIMC = ::ImmGetContext(mWnd); if (hIMC) { BOOL ret = FALSE; @@ -7863,30 +7814,6 @@ static VOID CALLBACK nsGetAttentionTimerFunc(HWND hwnd, UINT uMsg, UINT idEvent, gAttentionTimerMonitor->KillTimer(hwnd); } - -#ifdef NS_ENABLE_TSF -NS_IMETHODIMP -nsWindow::OnIMEFocusChange(PRBool aFocus) -{ - return nsTextStore::OnFocusChange(aFocus, this, mIMEEnabled); -} - -NS_IMETHODIMP -nsWindow::OnIMETextChange(PRUint32 aStart, - PRUint32 aOldEnd, - PRUint32 aNewEnd) -{ - return nsTextStore::OnTextChange(aStart, aOldEnd, aNewEnd); -} - -NS_IMETHODIMP -nsWindow::OnIMESelectionChange(void) -{ - return nsTextStore::OnSelectionChange(); -} -#endif //NS_ENABLE_TSF - - // Draw user's attention to this window until it comes to foreground. NS_IMETHODIMP nsWindow::GetAttention(PRInt32 aCycleCount) diff --git a/widget/src/windows/nsWindow.h b/widget/src/windows/nsWindow.h index a58fd8cc12d2..5eed7a2d229e 100644 --- a/widget/src/windows/nsWindow.h +++ b/widget/src/windows/nsWindow.h @@ -25,7 +25,6 @@ * Makoto Kato * Dainis Jonitis * Masayuki Nakano - * Ningjie Chen * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -74,11 +73,6 @@ struct nsFakeCharMessage; #include "gfxWindowsSurface.h" -// Text Services Framework support -#ifndef WINCE -#define NS_ENABLE_TSF -#endif //WINCE - #define IME_MAX_CHAR_POS 64 #define NSRGB_2_COLOREF(color) \ @@ -241,12 +235,6 @@ public: NS_IMETHOD CancelIMEComposition(); NS_IMETHOD GetToggledKeyState(PRUint32 aKeyCode, PRBool* aLEDState); -#ifdef NS_ENABLE_TSF - NS_IMETHOD OnIMEFocusChange(PRBool aFocus); - NS_IMETHOD OnIMETextChange(PRUint32 aStart, PRUint32 aOldEnd, PRUint32 aNewEnd); - NS_IMETHOD OnIMESelectionChange(void); -#endif //NS_ENABLE_TSF - PRBool IMEMouseHandling(PRInt32 aAction, LPARAM lParam); PRBool IMECompositionHitTest(POINT * ptPos); PRBool HandleMouseActionOfIME(PRInt32 aAction, POINT* ptPos); @@ -262,8 +250,6 @@ public: LPARAM lParam, PRBool aIsContextMenuKey = PR_FALSE, PRInt16 aButton = nsMouseEvent::eLeftButton); - virtual PRBool DispatchWindowEvent(nsGUIEvent* event); - virtual PRBool DispatchWindowEvent(nsGUIEvent*event, nsEventStatus &aStatus); #ifdef ACCESSIBILITY virtual PRBool DispatchAccessibleEvent(PRUint32 aEventType, nsIAccessible** aAccessible, nsIntPoint* aPoint = nsnull); already_AddRefed GetRootAccessible(); @@ -317,6 +303,8 @@ protected: LRESULT ProcessKeyDownMessage(const MSG &aMsg, PRBool *aEventDispatched); + virtual PRBool DispatchWindowEvent(nsGUIEvent* event); + virtual PRBool DispatchWindowEvent(nsGUIEvent*event, nsEventStatus &aStatus); // Allow Derived classes to modify the height that is passed // when the window is created or resized. diff --git a/widget/src/xpwidgets/nsBaseWidget.h b/widget/src/xpwidgets/nsBaseWidget.h index b3da4b2c2c3b..b875c0005afd 100644 --- a/widget/src/xpwidgets/nsBaseWidget.h +++ b/widget/src/xpwidgets/nsBaseWidget.h @@ -139,16 +139,13 @@ public: NS_IMETHOD BeginResizeDrag(nsGUIEvent* aEvent, PRInt32 aHorizontal, PRInt32 aVertical); virtual nsresult ActivateNativeMenuItemAt(const nsAString& indexString) { return NS_ERROR_NOT_IMPLEMENTED; } virtual nsresult ForceUpdateNativeMenuAt(const nsAString& indexString) { return NS_ERROR_NOT_IMPLEMENTED; } - NS_IMETHOD ResetInputState() { return NS_OK; } + NS_IMETHOD ResetInputState() { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHOD SetIMEOpenState(PRBool aState) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHOD GetIMEOpenState(PRBool* aState) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHOD SetIMEEnabled(PRUint32 aState) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHOD GetIMEEnabled(PRUint32* aState) { return NS_ERROR_NOT_IMPLEMENTED; } - NS_IMETHOD CancelIMEComposition() { return NS_OK; } + NS_IMETHOD CancelIMEComposition() { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHOD GetToggledKeyState(PRUint32 aKeyCode, PRBool* aLEDState) { return NS_ERROR_NOT_IMPLEMENTED; } - NS_IMETHOD OnIMEFocusChange(PRBool aFocus) { return NS_ERROR_NOT_IMPLEMENTED; } - NS_IMETHOD OnIMETextChange(PRUint32 aStart, PRUint32 aOldEnd, PRUint32 aNewEnd) { return NS_ERROR_NOT_IMPLEMENTED; } - NS_IMETHOD OnIMESelectionChange(void) { return NS_ERROR_NOT_IMPLEMENTED; } protected: diff --git a/widget/tests/Makefile.in b/widget/tests/Makefile.in index 347c4c70d317..698e28cbef96 100644 --- a/widget/tests/Makefile.in +++ b/widget/tests/Makefile.in @@ -39,40 +39,9 @@ DEPTH = ../.. topsrcdir = @top_srcdir@ srcdir = @srcdir@ VPATH = @srcdir@ -relativesrcdir = widget/tests +relativesrcdir = widget/test include $(DEPTH)/config/autoconf.mk - -ifeq ($(MOZ_WIDGET_TOOLKIT),windows) -ifneq ($(OS_ARCH), WINCE) -ifndef MOZ_ENABLE_LIBXUL -MOZILLA_INTERNAL_API = 1 -else -LIBS += $(DIST)/lib/$(LIB_PREFIX)xpcomglue_s.$(LIB_SUFFIX) \ - $(NULL) -endif - -CPP_UNIT_TESTS += TestWinTSF.cpp \ - $(NULL) - -LOCAL_INCLUDES += -I$(topsrcdir)/xpcom/tests - -CPPSRCS += $(CPP_UNIT_TESTS) - -SIMPLE_PROGRAMS += $(CPP_UNIT_TESTS:.cpp=$(BIN_SUFFIX)) - -REQUIRES += appshell content docshell \ - dom embed_base gfx layout locale \ - necko string thebes uriloader view \ - webbrwsr widget xpcom \ - $(NULL) - -LIBS += $(XPCOM_LIBS) \ - $(NSPR_LIBS) \ - $(NULL) -endif -endif - include $(topsrcdir)/config/rules.mk _TEST_FILES = test_bug343416.xul \ @@ -91,13 +60,3 @@ endif libs:: $(_TEST_FILES) $(INSTALL) $^ $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir) - -ifeq ($(MOZ_WIDGET_TOOLKIT),windows) -ifneq ($(OS_ARCH), WINCE) -check:: - @$(EXIT_ON_ERROR) \ - for f in $(subst .cpp,,$(CPP_UNIT_TESTS)); do \ - XPCOM_DEBUG_BREAK=stack-and-abort $(RUN_TEST_PROGRAM) $(DIST)/bin/$$f; \ - done -endif -endif diff --git a/widget/tests/TestWinTSF.cpp b/widget/tests/TestWinTSF.cpp deleted file mode 100644 index 4ee5e15e582b..000000000000 --- a/widget/tests/TestWinTSF.cpp +++ /dev/null @@ -1,1822 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is mozilla.org code. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Ningjie Chen - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - - -/* This tests Mozilla's Text Services Framework implementation (bug #88831) - * - * The Mozilla implementation interacts with the TSF system through a - * system-provided COM interface, ITfThreadMgr. This tests works by swapping - * the system version of the interface with a custom version implemented in - * here. This way the Mozilla implementation thinks it's interacting with the - * system but in fact is interacting with this test program. This allows the - * test program to access and test every aspect of the Mozilla implementation. - */ - -#include -#include - -#include "TestHarness.h" - -#define WM_USER_TSF_TEXTCHANGE (WM_USER + 0x100) - -#ifndef MOZILLA_INTERNAL_API -// some of the includes make use of internal string types -#define nsAString_h___ -#define nsString_h___ -class nsAFlatString; -class nsAFlatCString; -#endif - -#include "nsWeakReference.h" -#include "nsIAppShell.h" -#include "nsWidgetsCID.h" -#include "nsIAppShellService.h" -#include "nsAppShellCID.h" -#include "nsNetUtil.h" -#include "nsIWebBrowserChrome.h" -#include "nsIXULWindow.h" -#include "nsIBaseWindow.h" -#include "nsIDOMWindowInternal.h" -#include "nsIDocShell.h" -#include "nsIWidget.h" -#include "nsIPresShell.h" -#include "nsPresContext.h" -#include "nsIFrame.h" -#include "nsIWebProgress.h" -#include "nsIWebProgressListener.h" -#include "nsIInterfaceRequestorUtils.h" -#include "nsIDOMHTMLDocument.h" -#include "nsIDOMHTMLBodyElement.h" -#include "nsIDOMHTMLElement.h" -#include "nsIDOMHTMLInputElement.h" -#include "nsIDOMHTMLTextAreaElement.h" -#include "nsISelectionController.h" -#include "nsIViewManager.h" - -#ifndef MOZILLA_INTERNAL_API -#undef nsString_h___ -#undef nsAString_h___ -#endif - -static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID); - -class TSFImpl; - -class TestApp : public nsIWebProgressListener, public nsSupportsWeakReference -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_NSIWEBPROGRESSLISTENER - - TestApp() : mFailed(PR_FALSE) {} - ~TestApp() {} - - nsresult Run(void); - PRBool CheckFailed(void); - - typedef PRBool (TestApp::*test_type)(void); - -protected: - nsresult Init(void); - nsresult Term(void); - PRBool RunTest(test_type aTest, PRBool aLock = PR_TRUE); - - PRBool TestFocus(void); - PRBool TestClustering(void); - PRBool TestSelection(void); - PRBool TestText(void); - PRBool TestExtents(void); - PRBool TestComposition(void); - PRBool TestNotification(void); - - PRBool TestApp::TestSelectionInternal(char* aTestName, - LONG aStart, - LONG aEnd, - TsActiveSelEnd aSelEnd); - PRBool TestCompositionSelectionAndText(char* aTestName, - LONG aExpectedSelStart, - LONG aExpectedSelEnd, - nsString& aReferenceString); - PRBool TestNotificationTextChange(nsIWidget* aWidget, - PRUint32 aCode, - const nsAString& aCharacter, - LONG aStart, - LONG aOldEnd, - LONG aNewEnd); - nsresult GetSelCon(nsISelectionController** aSelCon); - - PRBool mFailed; - nsString mTestString; - nsRefPtr mImpl; - nsCOMPtr mAppShell; - nsCOMPtr mWindow; - nsCOMPtr mCurrentNode; - nsCOMPtr mInput; - nsCOMPtr mTextArea; - nsCOMPtr mButton; -}; - -NS_IMETHODIMP -TestApp::OnProgressChange(nsIWebProgress *aWebProgress, - nsIRequest *aRequest, - PRInt32 aCurSelfProgress, - PRInt32 aMaxSelfProgress, - PRInt32 aCurTotalProgress, - PRInt32 aMaxTotalProgress) -{ - return NS_OK; -} - -NS_IMETHODIMP -TestApp::OnLocationChange(nsIWebProgress *aWebProgress, - nsIRequest *aRequest, - nsIURI *aLocation) -{ - return NS_OK; -} - -NS_IMETHODIMP -TestApp::OnStatusChange(nsIWebProgress *aWebProgress, - nsIRequest *aRequest, - nsresult aStatus, - const PRUnichar *aMessage) -{ - return NS_OK; -} - -NS_IMETHODIMP -TestApp::OnSecurityChange(nsIWebProgress *aWebProgress, - nsIRequest *aRequest, - PRUint32 aState) -{ - return NS_OK; -} - -// Simple TSF manager implementation for testing -// Most methods are not implemented, but the ones used by Mozilla are -// -// XXX Implement appropriate methods here as the Mozilla TSF code changes -// -class TSFImpl : public ITfThreadMgr, public ITfDocumentMgr, public ITfContext, - public ITfRangeACP, public ITfCompositionView, - public ITextStoreACPSink -{ -private: - ULONG mRefCnt; - nsRefPtr mTestApp; - -public: - TestApp::test_type mTest; - TestApp::test_type mOnFocus; - TestApp::test_type mOnBlur; - nsRefPtr mStore; - PRBool mFocused; - PRBool mContextPushed; - PRBool mDeactivated; - PRUint32 mFocusCount; - PRUint32 mBlurCount; - PRUint32 mRangeStart; - PRUint32 mRangeLength; - PRBool mTextChanged; - PRBool mSelChanged; - TS_TEXTCHANGE mTextChangeData; - -public: - TSFImpl(TestApp* test) : mTestApp(test), mTest(nsnull), - mRefCnt(0), mFocused(PR_FALSE), mDeactivated(PR_FALSE), - mFocusCount(0), mBlurCount(0), mRangeStart(0), mRangeLength(0), - mContextPushed(PR_FALSE), mOnFocus(nsnull), mOnBlur(nsnull), - mTextChanged(PR_FALSE), mSelChanged(PR_FALSE) - { - } - - ~TSFImpl() - { - } - -public: // IUnknown - - STDMETHODIMP QueryInterface(REFIID riid, void** ppUnk) - { - *ppUnk = NULL; - if (IID_IUnknown == riid || IID_ITfThreadMgr == riid) - *ppUnk = static_cast(this); - else if (IID_ITfDocumentMgr == riid) - *ppUnk = static_cast(this); - else if (IID_ITfContext == riid) - *ppUnk = static_cast(this); - else if (IID_ITfRange == riid || IID_ITfRangeACP == riid) - *ppUnk = static_cast(this); - else if (IID_ITextStoreACPSink == riid) - *ppUnk = static_cast(this); - if (*ppUnk) - AddRef(); - return *ppUnk ? S_OK : E_NOINTERFACE; - } - - STDMETHODIMP_(ULONG) AddRef(void) - { - return ++mRefCnt; - } - - STDMETHODIMP_(ULONG) Release(void) - { - if (--mRefCnt) return mRefCnt; - delete this; - return 0; - } - -public: // ITfThreadMgr - - STDMETHODIMP Activate(TfClientId *ptid) - { - *ptid = 1; - return S_OK; - } - - STDMETHODIMP Deactivate(void) - { - SetFocus(NULL); - mDeactivated = PR_TRUE; - return S_OK; - } - - STDMETHODIMP CreateDocumentMgr(ITfDocumentMgr **ppdim) - { - (*ppdim) = this; - (*ppdim)->AddRef(); - return S_OK; - } - - STDMETHODIMP EnumDocumentMgrs(IEnumTfDocumentMgrs **ppEnum) - { - NS_NOTREACHED("ITfThreadMgr::EnumDocumentMgrs"); - return E_NOTIMPL; - } - - STDMETHODIMP GetFocus(ITfDocumentMgr **ppdimFocus) - { - (*ppdimFocus) = mFocused ? this : NULL; - if (*ppdimFocus) (*ppdimFocus)->AddRef(); - return S_OK; - } - - STDMETHODIMP SetFocus(ITfDocumentMgr *pdimFocus) - { - mFocused = pdimFocus != NULL; - if (mFocused) { - ++mFocusCount; - if (mOnFocus) (mTestApp->*mOnFocus)(); - } else { - ++mBlurCount; - if (mOnBlur) (mTestApp->*mOnBlur)(); - } - return S_OK; - } - - STDMETHODIMP AssociateFocus(HWND hwnd, ITfDocumentMgr *pdimNew, - ITfDocumentMgr **ppdimPrev) - { - NS_NOTREACHED("ITfThreadMgr::AssociateFocus"); - return E_NOTIMPL; - } - - STDMETHODIMP IsThreadFocus(BOOL *pfThreadFocus) - { - *pfThreadFocus = TRUE; - return S_OK; - } - - STDMETHODIMP GetFunctionProvider(REFCLSID clsid, - ITfFunctionProvider **ppFuncProv) - { - NS_NOTREACHED("ITfThreadMgr::GetFunctionProvider"); - return E_NOTIMPL; - } - - STDMETHODIMP EnumFunctionProviders(IEnumTfFunctionProviders **ppEnum) - { - NS_NOTREACHED("ITfThreadMgr::EnumFunctionProviders"); - return E_NOTIMPL; - } - - STDMETHODIMP GetGlobalCompartment(ITfCompartmentMgr **ppCompMgr) - { - NS_NOTREACHED("ITfThreadMgr::GetGlobalCompartment"); - return E_NOTIMPL; - } - -public: // ITfDocumentMgr - - STDMETHODIMP CreateContext(TfClientId tidOwner, DWORD dwFlags, - IUnknown *punk, ITfContext **ppic, - TfEditCookie *pecTextStore) - { - punk->QueryInterface(IID_ITextStoreACP, getter_AddRefs(mStore)); - NS_ENSURE_TRUE(mStore, E_FAIL); - HRESULT hr = mStore->AdviseSink(IID_ITextStoreACPSink, - static_cast(this), - TS_AS_ALL_SINKS); - if (FAILED(hr)) mStore = NULL; - NS_ENSURE_TRUE(SUCCEEDED(hr), E_FAIL); - (*ppic) = this; - (*ppic)->AddRef(); - *pecTextStore = 1; - return S_OK; - } - - STDMETHODIMP Push(ITfContext *pic) - { - mContextPushed = PR_TRUE; - return S_OK; - } - - STDMETHODIMP Pop(DWORD dwFlags) - { - if (!mStore || dwFlags != TF_POPF_ALL) return E_FAIL; - mStore->UnadviseSink(static_cast(this)); - mStore = NULL; - mContextPushed = PR_FALSE; - return S_OK; - } - - STDMETHODIMP GetTop(ITfContext **ppic) - { - (*ppic) = mContextPushed ? this : NULL; - if (*ppic) (*ppic)->AddRef(); - return S_OK; - } - - STDMETHODIMP GetBase(ITfContext **ppic) - { - (*ppic) = mContextPushed ? this : NULL; - if (*ppic) (*ppic)->AddRef(); - return S_OK; - } - - STDMETHODIMP EnumContexts(IEnumTfContexts **ppEnum) - { - NS_NOTREACHED("ITfDocumentMgr::EnumContexts"); - return E_NOTIMPL; - } - -public: // ITfContext - - STDMETHODIMP RequestEditSession(TfClientId tid, ITfEditSession *pes, - DWORD dwFlags, HRESULT *phrSession) - { - NS_NOTREACHED("ITfContext::RequestEditSession"); - return E_NOTIMPL; - } - - STDMETHODIMP InWriteSession(TfClientId tid, BOOL *pfWriteSession) - { - NS_NOTREACHED("ITfContext::InWriteSession"); - return E_NOTIMPL; - } - - STDMETHODIMP GetSelection(TfEditCookie ec, ULONG ulIndex, ULONG ulCount, - TF_SELECTION *pSelection, ULONG *pcFetched) - { - NS_NOTREACHED("ITfContext::GetSelection"); - return E_NOTIMPL; - } - - STDMETHODIMP SetSelection(TfEditCookie ec, ULONG ulCount, - const TF_SELECTION *pSelection) - { - NS_NOTREACHED("ITfContext::SetSelection"); - return E_NOTIMPL; - } - - STDMETHODIMP GetStart(TfEditCookie ec, ITfRange **ppStart) - { - NS_NOTREACHED("ITfContext::GetStart"); - return E_NOTIMPL; - } - - STDMETHODIMP GetEnd(TfEditCookie ec, ITfRange **ppEnd) - { - NS_NOTREACHED("ITfContext::GetEnd"); - return E_NOTIMPL; - } - - STDMETHODIMP GetActiveView(ITfContextView **ppView) - { - NS_NOTREACHED("ITfContext::GetActiveView"); - return E_NOTIMPL; - } - - STDMETHODIMP EnumViews(IEnumTfContextViews **ppEnum) - { - NS_NOTREACHED("ITfContext::EnumViews"); - return E_NOTIMPL; - } - - STDMETHODIMP GetStatus(TF_STATUS *pdcs) - { - NS_NOTREACHED("ITfContext::GetStatus"); - return E_NOTIMPL; - } - - STDMETHODIMP GetProperty(REFGUID guidProp, ITfProperty **ppProp) - { - NS_NOTREACHED("ITfContext::GetProperty"); - return E_NOTIMPL; - } - - STDMETHODIMP GetAppProperty(REFGUID guidProp, ITfReadOnlyProperty **ppProp) - { - NS_NOTREACHED("ITfContext::GetAppProperty"); - return E_NOTIMPL; - } - - STDMETHODIMP TrackProperties(const GUID **prgProp, ULONG cProp, - const GUID **prgAppProp, ULONG cAppProp, - ITfReadOnlyProperty **ppProperty) - { - NS_NOTREACHED("ITfContext::TrackProperties"); - return E_NOTIMPL; - } - - STDMETHODIMP EnumProperties(IEnumTfProperties **ppEnum) - { - NS_NOTREACHED("ITfContext::EnumProperties"); - return E_NOTIMPL; - } - - STDMETHODIMP GetDocumentMgr(ITfDocumentMgr **ppDm) - { - NS_NOTREACHED("ITfContext::GetDocumentMgr"); - return E_NOTIMPL; - } - - STDMETHODIMP CreateRangeBackup(TfEditCookie ec, ITfRange *pRange, - ITfRangeBackup **ppBackup) - { - NS_NOTREACHED("ITfContext::CreateRangeBackup"); - return E_NOTIMPL; - } - -public: // ITfRangeACP - - STDMETHODIMP GetText(TfEditCookie ec, DWORD dwFlags, WCHAR *pchText, - ULONG cchMax, ULONG *pcch) - { - NS_NOTREACHED("ITfRangeACP::GetText"); - return E_NOTIMPL; - } - - STDMETHODIMP SetText(TfEditCookie ec, DWORD dwFlags, const WCHAR *pchText, - LONG cch) - { - NS_NOTREACHED("ITfRangeACP::SetText"); - return E_NOTIMPL; - } - - STDMETHODIMP GetFormattedText(TfEditCookie ec, IDataObject **ppDataObject) - { - NS_NOTREACHED("ITfRangeACP::GetFormattedText"); - return E_NOTIMPL; - } - - STDMETHODIMP GetEmbedded(TfEditCookie ec, REFGUID rguidService, REFIID riid, - IUnknown **ppunk) - { - NS_NOTREACHED("ITfRangeACP::GetEmbedded"); - return E_NOTIMPL; - } - - STDMETHODIMP InsertEmbedded(TfEditCookie ec, DWORD dwFlags, - IDataObject *pDataObject) - { - NS_NOTREACHED("ITfRangeACP::InsertEmbedded"); - return E_NOTIMPL; - } - - STDMETHODIMP ShiftStart(TfEditCookie ec, LONG cchReq, LONG *pcch, - const TF_HALTCOND *pHalt) - { - NS_NOTREACHED("ITfRangeACP::ShiftStart"); - return E_NOTIMPL; - } - - STDMETHODIMP ShiftEnd(TfEditCookie ec, LONG cchReq, LONG *pcch, - const TF_HALTCOND *pHalt) - { - NS_NOTREACHED("ITfRangeACP::ShiftEnd"); - return E_NOTIMPL; - } - - STDMETHODIMP ShiftStartToRange(TfEditCookie ec, ITfRange *pRange, - TfAnchor aPos) - { - NS_NOTREACHED("ITfRangeACP::ShiftStartToRange"); - return E_NOTIMPL; - } - - STDMETHODIMP ShiftEndToRange(TfEditCookie ec, ITfRange *pRange, - TfAnchor aPos) - { - NS_NOTREACHED("ITfRangeACP::ShiftEndToRange"); - return E_NOTIMPL; - } - - STDMETHODIMP ShiftStartRegion(TfEditCookie ec, TfShiftDir dir, - BOOL *pfNoRegion) - { - NS_NOTREACHED("ITfRangeACP::ShiftStartRegion"); - return E_NOTIMPL; - } - - STDMETHODIMP ShiftEndRegion(TfEditCookie ec, TfShiftDir dir, - BOOL *pfNoRegion) - { - NS_NOTREACHED("ITfRangeACP::ShiftEndRegion"); - return E_NOTIMPL; - } - - STDMETHODIMP IsEmpty(TfEditCookie ec, BOOL *pfEmpty) - { - NS_NOTREACHED("ITfRangeACP::IsEmpty"); - return E_NOTIMPL; - } - - STDMETHODIMP Collapse(TfEditCookie ec, TfAnchor aPos) - { - NS_NOTREACHED("ITfRangeACP::Collapse"); - return E_NOTIMPL; - } - - STDMETHODIMP IsEqualStart(TfEditCookie ec, ITfRange *pWith, - TfAnchor aPos, BOOL *pfEqual) - { - NS_NOTREACHED("ITfRangeACP::IsEqualStart"); - return E_NOTIMPL; - } - - STDMETHODIMP IsEqualEnd(TfEditCookie ec, ITfRange *pWith, - TfAnchor aPos, BOOL *pfEqual) - { - NS_NOTREACHED("ITfRangeACP::IsEqualEnd"); - return E_NOTIMPL; - } - - STDMETHODIMP CompareStart(TfEditCookie ec, ITfRange *pWith, - TfAnchor aPos, LONG *plResult) - { - NS_NOTREACHED("ITfRangeACP::CompareStart"); - return E_NOTIMPL; - } - - STDMETHODIMP CompareEnd(TfEditCookie ec, ITfRange *pWith, - TfAnchor aPos, LONG *plResult) - { - NS_NOTREACHED("ITfRangeACP::CompareEnd"); - return E_NOTIMPL; - } - - STDMETHODIMP AdjustForInsert(TfEditCookie ec, ULONG cchInsert, - BOOL *pfInsertOk) - { - NS_NOTREACHED("ITfRangeACP::AdjustForInsert"); - return E_NOTIMPL; - } - - STDMETHODIMP GetGravity(TfGravity *pgStart, TfGravity *pgEnd) - { - NS_NOTREACHED("ITfRangeACP::GetGravity"); - return E_NOTIMPL; - } - - STDMETHODIMP SetGravity(TfEditCookie ec, TfGravity gStart, TfGravity gEnd) - { - NS_NOTREACHED("ITfRangeACP::SetGravity"); - return E_NOTIMPL; - } - - STDMETHODIMP Clone(ITfRange **ppClone) - { - NS_NOTREACHED("ITfRangeACP::Clone"); - return E_NOTIMPL; - } - - STDMETHODIMP GetContext(ITfContext **ppContext) - { - NS_NOTREACHED("ITfRangeACP::GetContext"); - return E_NOTIMPL; - } - - STDMETHODIMP GetExtent(LONG *pacpAnchor, LONG *pcch) - { - *pacpAnchor = LONG(mRangeStart); - *pcch = LONG(mRangeLength); - return S_OK; - } - - STDMETHODIMP SetExtent(LONG acpAnchor, LONG cch) - { - mRangeStart = PRUint32(acpAnchor); - mRangeLength = PRUint32(cch); - return S_OK; - } - -public: // ITfCompositionView - - STDMETHODIMP GetOwnerClsid(CLSID* pclsid) - { - NS_NOTREACHED("ITfCompositionView::GetOwnerClsid"); - return E_NOTIMPL; - } - - STDMETHODIMP GetRange(ITfRange** ppRange) - { - (*ppRange) = this; - (*ppRange)->AddRef(); - return S_OK; - } - -public: // ITextStoreACPSink - - STDMETHODIMP OnTextChange(DWORD dwFlags, const TS_TEXTCHANGE *pChange) - { - mTextChanged = PR_TRUE; - mTextChangeData = *pChange; - return S_OK; - } - - STDMETHODIMP OnSelectionChange(void) - { - mSelChanged = PR_TRUE; - return S_OK; - } - - STDMETHODIMP OnLayoutChange(TsLayoutCode lcode, TsViewCookie vcView) - { - return S_OK; - } - - STDMETHODIMP OnStatusChange(DWORD dwFlags) - { - return S_OK; - } - - STDMETHODIMP OnAttrsChange(LONG acpStart, LONG acpEnd, ULONG cAttrs, - const TS_ATTRID *paAttrs) - { - return S_OK; - } - - STDMETHODIMP OnLockGranted(DWORD dwLockFlags) - { - // If we have a test, run it - if (mTest && !(mTestApp->*mTest)()) - return S_FALSE; - return S_OK; - } - - STDMETHODIMP OnStartEditTransaction(void) - { - return S_OK; - } - - STDMETHODIMP OnEndEditTransaction(void) - { - return S_OK; - } -}; - -NS_IMPL_ISUPPORTS2(TestApp, nsIWebProgressListener, - nsISupportsWeakReference) - -nsresult -TestApp::Run(void) -{ - // Create a test window - // We need a full-fledged window to test for TSF functionality - nsresult rv; - mAppShell = do_GetService(kAppShellCID); - NS_ENSURE_TRUE(mAppShell, NS_ERROR_UNEXPECTED); - - nsCOMPtr appShellService( - do_GetService(NS_APPSHELLSERVICE_CONTRACTID)); - NS_ENSURE_TRUE(appShellService, NS_ERROR_UNEXPECTED); - - nsCOMPtr uri; - rv = NS_NewURI(getter_AddRefs(uri), "about:blank", nsnull); - NS_ENSURE_SUCCESS(rv, rv); - - rv = appShellService->CreateTopLevelWindow(nsnull, uri, - nsIWebBrowserChrome::CHROME_DEFAULT, - 800 /*nsIAppShellService::SIZE_TO_CONTENT*/, - 600 /*nsIAppShellService::SIZE_TO_CONTENT*/, - mAppShell, getter_AddRefs(mWindow)); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr docShell; - rv = mWindow->GetDocShell(getter_AddRefs(docShell)); - NS_ENSURE_TRUE(docShell, NS_ERROR_UNEXPECTED); - nsCOMPtr progress(do_GetInterface(docShell)); - NS_ENSURE_TRUE(progress, NS_ERROR_UNEXPECTED); - rv = progress->AddProgressListener(this, - nsIWebProgress::NOTIFY_STATE_WINDOW | - nsIWebProgress::NOTIFY_STATUS); - NS_ENSURE_SUCCESS(rv, rv); - - mAppShell->Run(); - return NS_OK; -} - -PRBool -TestApp::CheckFailed(void) -{ - // All windows should be closed by now - if (mImpl && !mImpl->mDeactivated) { - fail("TSF not terminated properly"); - mFailed = PR_TRUE; - } - mImpl = nsnull; - return mFailed; -} - -nsresult -TestApp::Init(void) -{ - // Replace TSF manager pointer - nsCOMPtr baseWindow(do_QueryInterface(mWindow)); - NS_ENSURE_TRUE(baseWindow, NS_ERROR_UNEXPECTED); - nsCOMPtr widget; - nsresult rv = baseWindow->GetMainWidget(getter_AddRefs(widget)); - NS_ENSURE_TRUE(widget, NS_ERROR_UNEXPECTED); - - ITfThreadMgr **mgr = reinterpret_cast( - widget->GetNativeData(NS_NATIVE_TSF_POINTER)); - if (!mgr) { - fail("nsIWidget::GetNativeData(NS_NATIVE_TSF_POINTER) not supported"); - return E_FAIL; - } - if (*mgr) { - (*mgr)->Deactivate(); - (*mgr)->Release(); - (*mgr) = NULL; - } else - printf("TSF not initialized properly (TSF is not enabled/installed?)"); - mImpl = new TSFImpl(this); - if (!mImpl) - return E_OUTOFMEMORY; - (*mgr) = mImpl; - (*mgr)->AddRef(); - - // Create a couple of text boxes for testing - nsCOMPtr win(do_GetInterface(mWindow)); - NS_ENSURE_TRUE(win, NS_ERROR_UNEXPECTED); - nsCOMPtr document; - rv = win->GetDocument(getter_AddRefs(document)); - NS_ENSURE_TRUE(document, NS_ERROR_UNEXPECTED); - nsCOMPtr htmlDoc(do_QueryInterface(document)); - NS_ENSURE_TRUE(htmlDoc, NS_ERROR_UNEXPECTED); - nsCOMPtr htmlBody; - rv = htmlDoc->GetBody(getter_AddRefs(htmlBody)); - NS_ENSURE_TRUE(htmlBody, NS_ERROR_UNEXPECTED); - - nsCOMPtr form; - rv = htmlDoc->CreateElementNS( - NS_LITERAL_STRING("http://www.w3.org/1999/xhtml"), - NS_LITERAL_STRING("form"), - getter_AddRefs(form)); - nsCOMPtr elem; - rv = htmlDoc->CreateElementNS( - NS_LITERAL_STRING("http://www.w3.org/1999/xhtml"), - NS_LITERAL_STRING("input"), - getter_AddRefs(elem)); - NS_ENSURE_SUCCESS(rv, rv); - elem->SetAttribute(NS_LITERAL_STRING("type"), - NS_LITERAL_STRING("text")); - mInput = do_QueryInterface(elem); - NS_ENSURE_TRUE(mInput, NS_ERROR_UNEXPECTED); - rv = htmlDoc->CreateElementNS( - NS_LITERAL_STRING("http://www.w3.org/1999/xhtml"), - NS_LITERAL_STRING("textarea"), - getter_AddRefs(elem)); - NS_ENSURE_SUCCESS(rv, rv); - mTextArea = do_QueryInterface(elem); - NS_ENSURE_TRUE(mTextArea, NS_ERROR_UNEXPECTED); - rv = htmlDoc->CreateElementNS( - NS_LITERAL_STRING("http://www.w3.org/1999/xhtml"), - NS_LITERAL_STRING("input"), - getter_AddRefs(elem)); - NS_ENSURE_SUCCESS(rv, rv); - elem->SetAttribute(NS_LITERAL_STRING("type"), - NS_LITERAL_STRING("button")); - mButton = do_QueryInterface(elem); - NS_ENSURE_TRUE(mButton, NS_ERROR_UNEXPECTED); - - nsCOMPtr node; - rv = form->AppendChild(mInput, getter_AddRefs(node)); - NS_ENSURE_SUCCESS(rv, rv); - rv = form->AppendChild(mTextArea, getter_AddRefs(node)); - NS_ENSURE_SUCCESS(rv, rv); - rv = form->AppendChild(mButton, getter_AddRefs(node)); - NS_ENSURE_SUCCESS(rv, rv); - rv = htmlBody->AppendChild(form, getter_AddRefs(node)); - NS_ENSURE_SUCCESS(rv, rv); - - // set a background color manually, - // otherwise the window might be transparent - nsCOMPtr(do_QueryInterface(htmlBody))-> - SetBgColor(NS_LITERAL_STRING("white")); - - widget->Show(PR_TRUE); - widget->SetFocus(); - return NS_OK; -} - -nsresult -TestApp::Term(void) -{ - mCurrentNode = nsnull; - mInput = nsnull; - mTextArea = nsnull; - mButton = nsnull; - - nsCOMPtr win(do_GetInterface(mWindow)); - if (win) - win->Close(); - win = nsnull; - mWindow = nsnull; - - if (mAppShell) - mAppShell->Exit(); - mAppShell = nsnull; - return NS_OK; -} - -PRBool -TestApp::RunTest(test_type aTest, PRBool aLock) -{ - PRBool succeeded; - if (aLock && mImpl->mStore) { - mImpl->mTest = aTest; - HRESULT hr = E_FAIL; - mImpl->mStore->RequestLock(TS_LF_READWRITE | TS_LF_SYNC, &hr); - succeeded = hr == S_OK; - } else { - succeeded = (this->*aTest)(); - } - mFailed |= !succeeded; - return succeeded; -} - -NS_IMETHODIMP -TestApp::OnStateChange(nsIWebProgress *aWebProgress, - nsIRequest *aRequest, - PRUint32 aStateFlags, - nsresult aStatus) -{ - NS_ASSERTION(aStateFlags & nsIWebProgressListener::STATE_IS_WINDOW && - aStateFlags & nsIWebProgressListener::STATE_STOP, "wrong state"); - if (NS_SUCCEEDED(Init())) { - if (RunTest(&TestApp::TestFocus, PR_FALSE)) - passed("TestFocus"); - - mCurrentNode = mInput; - mInput->Focus(); - if (mImpl->mStore) { - if (RunTest(&TestApp::TestClustering)) - passed("TestClustering"); - } else { - fail("no text store (clustering)"); - mFailed = PR_TRUE; - } - - printf("Testing TSF support in text input element...\n"); - mCurrentNode = mInput; - mTestString = NS_LITERAL_STRING( - "This is a test of the Text Services Framework implementation."); - mInput->SetValue(mTestString); - mInput->Focus(); - if (mImpl->mStore) { - if (RunTest(&TestApp::TestSelection)) - passed("TestSelection (input)"); - if (RunTest(&TestApp::TestText)) - passed("TestText (input)"); - if (RunTest(&TestApp::TestExtents)) - passed("TestExtents (input)"); - if (RunTest(&TestApp::TestComposition)) - passed("TestComposition (input)"); - if (RunTest(&TestApp::TestNotification, PR_FALSE)) - passed("TestNotification (input)"); - } else { - fail("no text store (input)"); - mFailed = PR_TRUE; - } - - printf("Testing TSF support in textarea element...\n"); - mCurrentNode = mTextArea; - mTestString = NS_LITERAL_STRING( - "This is a test of the\r\nText Services Framework\r\nimplementation."); - mTextArea->SetValue(mTestString); - mTextArea->Focus(); - if (mImpl->mStore) { - if (RunTest(&TestApp::TestSelection)) - passed("TestSelection (textarea)"); - if (RunTest(&TestApp::TestText)) - passed("TestText (textarea)"); - if (RunTest(&TestApp::TestExtents)) - passed("TestExtents (textarea)"); - if (RunTest(&TestApp::TestComposition)) - passed("TestComposition (textarea)"); - if (RunTest(&TestApp::TestNotification, PR_FALSE)) - passed("TestNotification (textarea)"); - } else { - fail("no text store (textarea)"); - mFailed = PR_TRUE; - } - } else { - fail("initialization"); - mFailed = PR_TRUE; - } - Term(); - return NS_OK; -} - -PRBool -TestApp::TestFocus(void) -{ - PRUint32 focus = mImpl->mFocusCount, blur = mImpl->mBlurCount; - nsresult rv; - - /* If these fail the cause is probably one or more of: - * - nsIMEStateManager::OnTextStateFocus not called by nsEventStateManager - * - nsIMEStateManager::OnTextStateBlur not called by nsEventStateManager - * - nsWindow::OnIMEFocusChange (nsIWidget) not called by nsIMEStateManager - * - nsTextStore::Create/Focus/Destroy not called by nsWindow - * - ITfThreadMgr::CreateDocumentMgr/SetFocus not called by nsTextStore - * - ITfDocumentMgr::CreateContext/Push not called by nsTextStore - */ - - rv = mInput->Focus(); - if (!(NS_SUCCEEDED(rv) && - mImpl->mFocused && - mImpl->mStore && - mImpl->mFocusCount - focus == 1 && - mImpl->mBlurCount - blur == 0 && - mImpl->mStore)) { - fail("TestFocus: document focus was not set"); - return PR_FALSE; - } - - rv = mTextArea->Focus(); - if (!(NS_SUCCEEDED(rv) && - mImpl->mFocused && - mImpl->mStore && - mImpl->mFocusCount - focus == 2 && - mImpl->mBlurCount - blur == 1 && - mImpl->mStore)) { - fail("TestFocus: document focus was not changed"); - return PR_FALSE; - } - - rv = mButton->Focus(); - if (!(NS_SUCCEEDED(rv) && - !mImpl->mFocused && - !mImpl->mStore && - mImpl->mFocusCount - focus == 2 && - mImpl->mBlurCount - blur == 2 && - !mImpl->mStore)) { - fail("TestFocus: document was not blurred"); - return PR_FALSE; - } - return PR_TRUE; -} - -PRBool -TestApp::TestClustering(void) -{ - // Text for testing - const PRUint32 STRING_LENGTH = 2; - PRUnichar string[3]; - string[0] = 'e'; - string[1] = 0x0301; // U+0301 'acute accent' - string[2] = nsnull; - - // Replace entire string with our string - TS_TEXTCHANGE textChange; - HRESULT hr = mImpl->mStore->SetText(0, 0, -1, string, STRING_LENGTH, - &textChange); - if (!(SUCCEEDED(hr) && - 0 == textChange.acpStart && - STRING_LENGTH == textChange.acpNewEnd)) { - fail("TestClustering: SetText"); - return PR_FALSE; - } - - TsViewCookie view; - RECT rectLetter, rectAccent, rectWhole, rectCombined; - BOOL clipped, nonEmpty; - - hr = mImpl->mStore->GetActiveView(&view); - if (!(SUCCEEDED(hr))) { - fail("TestClustering: GetActiveView"); - return PR_FALSE; - } - - // Get rect of first char (the letter) - hr = mImpl->mStore->GetTextExt(view, 0, STRING_LENGTH / 2, - &rectLetter, &clipped); - if (!(SUCCEEDED(hr))) { - fail("TestClustering: GetTextExt (letter)"); - return PR_FALSE; - } - - // Get rect of second char (the accent) - hr = mImpl->mStore->GetTextExt(view, STRING_LENGTH / 2, STRING_LENGTH, - &rectAccent, &clipped); - if (!(SUCCEEDED(hr))) { - fail("TestClustering: GetTextExt (accent)"); - return PR_FALSE; - } - - // Get rect of combined char - hr = mImpl->mStore->GetTextExt(view, 0, STRING_LENGTH, - &rectWhole, &clipped); - if (!(SUCCEEDED(hr))) { - fail("TestClustering: GetTextExt (whole)"); - return PR_FALSE; - } - - nonEmpty = ::UnionRect(&rectCombined, &rectLetter, &rectAccent); - if (!(nonEmpty && - ::EqualRect(&rectCombined, &rectWhole))) { - fail("TestClustering: unexpected combined rect"); - return PR_FALSE; - } - return PR_TRUE; -} - -PRBool -TestApp::TestSelectionInternal(char* aTestName, - LONG aStart, - LONG aEnd, - TsActiveSelEnd aSelEnd) -{ - PRBool succeeded = PR_TRUE, continueTest = PR_TRUE; - TS_SELECTION_ACP sel, testSel; - ULONG selFetched; - - sel.acpStart = aStart; - sel.acpEnd = aEnd; - sel.style.ase = aSelEnd; - sel.style.fInterimChar = FALSE; - HRESULT hr = mImpl->mStore->SetSelection(1, &sel); - if (!(SUCCEEDED(hr))) { - fail("TestSelection: SetSelection (%s)", aTestName); - continueTest = succeeded = PR_FALSE; - } - - if (continueTest) { - hr = mImpl->mStore->GetSelection(TS_DEFAULT_SELECTION, 1, - &testSel, &selFetched); - if (!(SUCCEEDED(hr) && - selFetched == 1 && - !memcmp(&sel, &testSel, sizeof(sel)))) { - fail("TestSelection: unexpected GetSelection result (%s)", aTestName); - succeeded = PR_FALSE; - } - } - return succeeded; -} - -PRBool -TestApp::TestSelection(void) -{ - PRBool succeeded = PR_TRUE; - - /* If these fail the cause is probably one or more of: - * nsTextStore::GetSelection not sending NS_QUERY_SELECTED_TEXT - * NS_QUERY_SELECTED_TEXT not handled by nsContentEventHandler - * Bug in NS_QUERY_SELECTED_TEXT handler - * nsTextStore::SetSelection not sending NS_SELECTION_SET - * NS_SELECTION_SET not handled by nsContentEventHandler - * Bug in NS_SELECTION_SET handler - */ - - TS_SELECTION_ACP testSel; - ULONG selFetched; - - HRESULT hr = mImpl->mStore->GetSelection(0, 1, &testSel, &selFetched); - if (!(SUCCEEDED(hr) && - selFetched == 1)) { - fail("TestSelection: GetSelection"); - succeeded = PR_FALSE; - } - - const LONG SELECTION1_START = 0; - const LONG SELECTION1_END = mTestString.Length(); - const TsActiveSelEnd SELECTION1_SELEND = TS_AE_END; - - if (!TestSelectionInternal("normal", - SELECTION1_START, - SELECTION1_END, - SELECTION1_SELEND)) { - succeeded = PR_FALSE; - } - - const LONG SELECTION2_START = mTestString.Length() / 2; - const LONG SELECTION2_END = SELECTION2_START; - const TsActiveSelEnd SELECTION2_SELEND = TS_AE_END; - - if (!TestSelectionInternal("collapsed", - SELECTION2_START, - SELECTION2_END, - SELECTION2_SELEND)) { - succeeded = PR_FALSE; - } - - const LONG SELECTION3_START = 12; - const LONG SELECTION3_END = mTestString.Length() - 20; - const TsActiveSelEnd SELECTION3_SELEND = TS_AE_START; - - if (!TestSelectionInternal("reversed", - SELECTION3_START, - SELECTION3_END, - SELECTION3_SELEND)) { - succeeded = PR_FALSE; - } - return succeeded; -} - -PRBool -TestApp::TestText(void) -{ - const PRUint32 BUFFER_SIZE = (0x100); - const PRUint32 RUNINFO_SIZE = (0x10); - - PRBool succeeded = PR_TRUE, continueTest; - PRUnichar buffer[BUFFER_SIZE]; - TS_RUNINFO runInfo[RUNINFO_SIZE]; - ULONG bufferRet, runInfoRet; - LONG acpRet, acpCurrent; - TS_TEXTCHANGE textChange; - HRESULT hr; - - /* If these fail the cause is probably one or more of: - * nsTextStore::GetText not sending NS_QUERY_TEXT_CONTENT - * NS_QUERY_TEXT_CONTENT not handled by nsContentEventHandler - * Bug in NS_QUERY_TEXT_CONTENT handler - * nsTextStore::SetText not calling SetSelection or InsertTextAtSelection - * Bug in SetSelection or InsertTextAtSelection - * NS_SELECTION_SET bug or NS_COMPOSITION_* / NS_TEXT_TEXT bug - */ - - // Get all text - hr = mImpl->mStore->GetText(0, -1, buffer, BUFFER_SIZE, &bufferRet, - runInfo, RUNINFO_SIZE, &runInfoRet, &acpRet); - if (!(SUCCEEDED(hr) && - bufferRet <= mTestString.Length() && - !wcsncmp(mTestString.get(), buffer, bufferRet) && - acpRet == LONG(bufferRet) && - runInfoRet > 0)) { - fail("TestText: GetText 1"); - succeeded = PR_FALSE; - } - - - // Get text from GETTEXT2_START to GETTEXT2_END - const PRUint32 GETTEXT2_START = (18); - const PRUint32 GETTEXT2_END = (mTestString.Length() - 16); - const PRUint32 GETTEXT2_BUFFER_SIZE = (0x10); - - hr = mImpl->mStore->GetText(GETTEXT2_START, GETTEXT2_END, - buffer, GETTEXT2_BUFFER_SIZE, &bufferRet, - runInfo, RUNINFO_SIZE, &runInfoRet, &acpRet); - if (!(SUCCEEDED(hr) && - bufferRet <= GETTEXT2_BUFFER_SIZE && - !wcsncmp(mTestString.get() + GETTEXT2_START, buffer, bufferRet) && - acpRet == LONG(bufferRet) + GETTEXT2_START && - runInfoRet > 0)) { - fail("TestText: GetText 2"); - succeeded = PR_FALSE; - } - - - // Replace text from SETTEXT1_START to SETTEXT1_END with insertString - const PRUint32 SETTEXT1_START = (8); - const PRUint32 SETTEXT1_TAIL_LENGTH = (40); - const PRUint32 SETTEXT1_END = (mTestString.Length() - - SETTEXT1_TAIL_LENGTH); - NS_NAMED_LITERAL_STRING(insertString, "(Inserted string)"); - - continueTest = PR_TRUE; - hr = mImpl->mStore->SetText(0, SETTEXT1_START, SETTEXT1_END, - insertString.get(), insertString.Length(), &textChange); - if (!(SUCCEEDED(hr) && - textChange.acpStart == SETTEXT1_START && - textChange.acpOldEnd == LONG(SETTEXT1_END) && - textChange.acpNewEnd == LONG(SETTEXT1_START + - insertString.Length()))) { - fail("TestText: SetText 1"); - continueTest = succeeded = PR_FALSE; - } - - const PRUint32 SETTEXT1_FINAL_LENGTH = (SETTEXT1_START + - SETTEXT1_TAIL_LENGTH + - insertString.Length()); - - if (continueTest) { - acpCurrent = 0; - while (acpCurrent < LONG(SETTEXT1_FINAL_LENGTH)) { - hr = mImpl->mStore->GetText(acpCurrent, -1, &buffer[acpCurrent], - BUFFER_SIZE, &bufferRet, runInfo, - RUNINFO_SIZE, &runInfoRet, &acpRet); - if (!(SUCCEEDED(hr) && - acpRet > acpCurrent && - bufferRet <= SETTEXT1_FINAL_LENGTH && - runInfoRet > 0)) { - fail("TestText: GetText failed after SetTest 1"); - continueTest = succeeded = PR_FALSE; - break; - } - acpCurrent = acpRet; - } - } - - if (continueTest) { - if (!(acpCurrent == LONG(SETTEXT1_FINAL_LENGTH) && - !wcsncmp(buffer, mTestString.get(), SETTEXT1_START) && - !wcsncmp(&buffer[SETTEXT1_START], insertString.get(), - insertString.Length()) && - !wcsncmp(&buffer[SETTEXT1_START + insertString.Length()], - mTestString.get() + SETTEXT1_END, SETTEXT1_TAIL_LENGTH))) { - fail("TestText: unexpected GetText result after SetText 1"); - succeeded = PR_FALSE; - } - } - - - // Restore entire text to original text (mTestString) - continueTest = PR_TRUE; - hr = mImpl->mStore->SetText(0, 0, -1, mTestString.get(), - mTestString.Length(), &textChange); - if (!(SUCCEEDED(hr) && - textChange.acpStart == 0 && - textChange.acpOldEnd == LONG(SETTEXT1_FINAL_LENGTH) && - textChange.acpNewEnd == LONG(mTestString.Length()))) { - fail("TestText: SetText 2"); - continueTest = succeeded = PR_FALSE; - } - - if (continueTest) { - acpCurrent = 0; - while (acpCurrent < LONG(mTestString.Length())) { - hr = mImpl->mStore->GetText(acpCurrent, -1, &buffer[acpCurrent], - BUFFER_SIZE, &bufferRet, runInfo, RUNINFO_SIZE, &runInfoRet, &acpRet); - if (!(SUCCEEDED(hr) && - acpRet > acpCurrent && - bufferRet <= mTestString.Length() && - runInfoRet > 0)) { - fail("TestText: GetText failed after SetText 2"); - continueTest = succeeded = PR_FALSE; - break; - } - acpCurrent = acpRet; - } - } - - if (continueTest) { - if (!(acpCurrent == LONG(mTestString.Length()) && - !wcsncmp(buffer, mTestString.get(), mTestString.Length()))) { - fail("TestText: unexpected GetText result after SetText 2"); - succeeded = PR_FALSE; - } - } - return succeeded; -} - -PRBool -TestApp::TestExtents(void) -{ - TS_SELECTION_ACP sel; - sel.acpStart = 0; - sel.acpEnd = 0; - sel.style.ase = TS_AE_END; - sel.style.fInterimChar = FALSE; - mImpl->mStore->SetSelection(1, &sel); - - nsCOMPtr selCon; - if (!(NS_SUCCEEDED(GetSelCon(getter_AddRefs(selCon))) && selCon)) { - fail("TestExtents: get nsISelectionController"); - return PR_FALSE; - } - selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL, - nsISelectionController::SELECTION_FOCUS_REGION, PR_TRUE); - - nsCOMPtr window(do_GetInterface(mWindow)); - if (!window) { - fail("TestExtents: get nsIDOMWindowInternal"); - return PR_FALSE; - } - RECT windowRect, screenRect, textRect1, textRect2; - BOOL clipped; - PRInt32 val; - TsViewCookie view; - HRESULT hr; - - nsresult nsr = window->GetScreenX(&val); - windowRect.left = val; - nsr |= window->GetScreenY(&val); - windowRect.top = val; - nsr |= window->GetOuterWidth(&val); - windowRect.right = windowRect.left + val; - nsr |= window->GetOuterHeight(&val); - windowRect.bottom = windowRect.top + val; - if (!(NS_SUCCEEDED(nsr))) { - fail("TestExtents: get window rect failed"); - return PR_FALSE; - } - - hr = mImpl->mStore->GetActiveView(&view); - if (!(SUCCEEDED(hr))) { - fail("TestExtents: GetActiveView"); - return PR_FALSE; - } - - PRBool succeeded = PR_TRUE; - HWND hwnd; - hr = mImpl->mStore->GetWnd(view, &hwnd); - if (!(SUCCEEDED(hr) && - ::IsWindow(hwnd))) { - fail("TestExtents: GetWnd"); - succeeded = PR_FALSE; - } - - ::SetRectEmpty(&screenRect); - hr = mImpl->mStore->GetScreenExt(view, &screenRect); - if (!(SUCCEEDED(hr) && - screenRect.left > windowRect.left && - screenRect.top > windowRect.top && - screenRect.right > screenRect.left && - screenRect.bottom > screenRect.top && - screenRect.right < windowRect.right && - screenRect.bottom < windowRect.bottom)) { - fail("TestExtents: GetScreenExt"); - succeeded = PR_FALSE; - } - - const LONG GETTEXTEXT1_START = 0; - const LONG GETTEXTEXT1_END = 0; - - ::SetRectEmpty(&textRect1); - hr = mImpl->mStore->GetTextExt(view, GETTEXTEXT1_START, GETTEXTEXT1_END, - &textRect1, &clipped); - if (!(SUCCEEDED(hr) && - textRect1.left >= screenRect.left && - textRect1.top >= screenRect.top && - textRect1.right < screenRect.right && - textRect1.bottom <= screenRect.bottom && - textRect1.right >= textRect1.left && - textRect1.bottom > textRect1.top)) { - fail("TestExtents: GetTextExt (offset %ld to %ld)", - GETTEXTEXT1_START, GETTEXTEXT1_END); - succeeded = PR_FALSE; - } - - const LONG GETTEXTEXT2_START = 10; - const LONG GETTEXTEXT2_END = 25; - - ::SetRectEmpty(&textRect2); - hr = mImpl->mStore->GetTextExt(view, GETTEXTEXT2_START, GETTEXTEXT2_END, - &textRect2, &clipped); - if (!(SUCCEEDED(hr) && - textRect2.left >= screenRect.left && - textRect2.top >= screenRect.top && - textRect2.right <= screenRect.right && - textRect2.bottom <= screenRect.bottom && - textRect2.right > textRect2.left && - textRect2.bottom > textRect2.top)) { - fail("TestExtents: GetTextExt (offset %ld to %ld)", - GETTEXTEXT2_START, GETTEXTEXT2_END); - succeeded = PR_FALSE; - } - - // Offsets must be between GETTEXTEXT2_START and GETTEXTEXT2_END - const LONG GETTEXTEXT3_START = 23; - const LONG GETTEXTEXT3_END = 23; - - ::SetRectEmpty(&textRect1); - hr = mImpl->mStore->GetTextExt(view, GETTEXTEXT3_START, GETTEXTEXT3_END, - &textRect1, &clipped); - // Rectangle must be entirely inside the previous rectangle, - // since GETTEXTEXT3_START and GETTEXTEXT3_END are between - // GETTEXTEXT2_START and GETTEXTEXT2_START - if (!(SUCCEEDED(hr) && ::IsRectEmpty(&textRect1) || - (textRect1.left >= textRect2.left && - textRect1.top >= textRect2.top && - textRect1.right <= textRect2.right && - textRect1.bottom <= textRect2.bottom && - textRect1.right >= textRect1.left && - textRect1.bottom > textRect1.top))) { - fail("TestExtents: GetTextExt (offset %ld to %ld)", - GETTEXTEXT3_START, GETTEXTEXT3_END); - succeeded = PR_FALSE; - } - return succeeded; -} - -PRBool -TestApp::TestCompositionSelectionAndText(char* aTestName, - LONG aExpectedSelStart, - LONG aExpectedSelEnd, - nsString& aReferenceString) -{ - TS_SELECTION_ACP currentSel; - ULONG selFetched = 0; - HRESULT hr = mImpl->mStore->GetSelection(TF_DEFAULT_SELECTION, 1, - ¤tSel, &selFetched); - if (!(SUCCEEDED(hr) && - 1 == selFetched && - currentSel.acpStart == aExpectedSelStart && - currentSel.acpEnd == aExpectedSelEnd)) { - fail("TestComposition: GetSelection (%s)", aTestName); - return PR_FALSE; - } - - const PRUint32 bufferSize = 0x100, runInfoSize = 0x10; - PRUnichar buffer[bufferSize]; - TS_RUNINFO runInfo[runInfoSize]; - ULONG bufferRet, runInfoRet; - LONG acpRet, acpCurrent = 0; - while (acpCurrent < LONG(aReferenceString.Length())) { - hr = mImpl->mStore->GetText(acpCurrent, aReferenceString.Length(), - &buffer[acpCurrent], bufferSize, &bufferRet, runInfo, runInfoSize, - &runInfoRet, &acpRet); - if (!(SUCCEEDED(hr) && - acpRet > acpCurrent && - bufferRet <= aReferenceString.Length() && - runInfoRet > 0)) { - fail("TestComposition: GetText (%s)", aTestName); - return PR_FALSE; - } - acpCurrent = acpRet; - } - if (!(acpCurrent == aReferenceString.Length() && - !wcsncmp(buffer, aReferenceString.get(), aReferenceString.Length()))) { - fail("TestComposition: unexpected GetText result (%s)", aTestName); - return PR_FALSE; - } - return PR_TRUE; -} - -PRBool -TestApp::TestComposition(void) -{ - nsRefPtr sink; - HRESULT hr = mImpl->mStore->QueryInterface( - IID_ITfContextOwnerCompositionSink, - getter_AddRefs(sink)); - if (!(SUCCEEDED(hr))) { - fail("TestComposition: QueryInterface"); - return PR_FALSE; - } - - const LONG PRECOMPOSITION_SEL_START = 2; - const LONG PRECOMPOSITION_SEL_END = PRECOMPOSITION_SEL_START; - const TsActiveSelEnd PRECOMPOSITION_SEL_SELEND = TS_AE_END; - - TS_SELECTION_ACP sel; - sel.acpStart = PRECOMPOSITION_SEL_START; - sel.acpEnd = PRECOMPOSITION_SEL_END; - sel.style.ase = PRECOMPOSITION_SEL_SELEND; - sel.style.fInterimChar = FALSE; - hr = mImpl->mStore->SetSelection(1, &sel); - if (!(SUCCEEDED(hr))) { - fail("TestComposition: SetSelection (pre-composition)"); - return PR_FALSE; - } - - - TS_TEXTCHANGE textChange; - NS_NAMED_LITERAL_STRING(insertString1, "Compo1"); - hr = mImpl->mStore->InsertTextAtSelection(TF_IAS_NOQUERY, - insertString1.get(), - insertString1.Length(), - NULL, NULL, &textChange); - if (!(SUCCEEDED(hr) && - sel.acpEnd == textChange.acpStart && - sel.acpEnd == textChange.acpOldEnd && - sel.acpEnd + insertString1.Length() == textChange.acpNewEnd)) { - fail("TestComposition: InsertTextAtSelection"); - return PR_FALSE; - } - sel.acpEnd = textChange.acpNewEnd; - - mImpl->mRangeStart = textChange.acpStart; - mImpl->mRangeLength = textChange.acpNewEnd - textChange.acpOldEnd; - BOOL okay = FALSE; - hr = sink->OnStartComposition(mImpl, &okay); - if (!(SUCCEEDED(hr) && - okay)) { - fail("TestComposition: OnStartComposition"); - return PR_FALSE; - } - - - NS_NAMED_LITERAL_STRING(insertString2, "Composition2"); - hr = mImpl->mStore->SetText(0, mImpl->mRangeStart + mImpl->mRangeLength, - mImpl->mRangeStart + mImpl->mRangeLength, - insertString2.get(), insertString2.Length(), - &textChange); - if (!(SUCCEEDED(hr) && - sel.acpEnd == textChange.acpStart && - sel.acpEnd == textChange.acpOldEnd && - sel.acpEnd + insertString2.Length() == textChange.acpNewEnd)) { - fail("TestComposition: SetText 1"); - return PR_FALSE; - } - sel.acpEnd = textChange.acpNewEnd; - mImpl->mRangeLength += textChange.acpNewEnd - textChange.acpOldEnd; - - - const LONG COMPOSITION3_TEXT_START_OFFSET = -8; // offset 8 from the end - const LONG COMPOSITION3_TEXT_END_OFFSET = 4; - - const LONG COMPOSITION3_TEXT_START = mImpl->mRangeStart + - mImpl->mRangeLength + - COMPOSITION3_TEXT_START_OFFSET; - const LONG COMPOSITION3_TEXT_END = COMPOSITION3_TEXT_START + - COMPOSITION3_TEXT_END_OFFSET; - - NS_NAMED_LITERAL_STRING(insertString3, "Compo3"); - hr = mImpl->mStore->SetText(0, COMPOSITION3_TEXT_START, - COMPOSITION3_TEXT_END, - insertString3.get(), insertString3.Length(), - &textChange); - if (!(SUCCEEDED(hr) && - sel.acpEnd + COMPOSITION3_TEXT_START_OFFSET == textChange.acpStart && - sel.acpEnd + COMPOSITION3_TEXT_START_OFFSET + - COMPOSITION3_TEXT_END_OFFSET == textChange.acpOldEnd && - sel.acpEnd + insertString3.Length() + COMPOSITION3_TEXT_START_OFFSET == - textChange.acpNewEnd)) { - fail("TestComposition: SetText 2"); - return PR_FALSE; - } - sel.acpEnd = textChange.acpNewEnd; - mImpl->mRangeLength += textChange.acpNewEnd - textChange.acpOldEnd; - - - nsString referenceString; - referenceString.Append(mTestString.get(), sel.acpStart); - referenceString.Append(insertString1); - referenceString.Append(insertString2.get(), - insertString2.Length() + COMPOSITION3_TEXT_START_OFFSET); - referenceString.Append(insertString3); - referenceString.Append(insertString2.get() + insertString2.Length() - - COMPOSITION3_TEXT_END_OFFSET, COMPOSITION3_TEXT_END_OFFSET); - referenceString.Append(mTestString.get() + sel.acpStart, - COMPOSITION3_TEXT_END_OFFSET); - - if (!TestCompositionSelectionAndText("composition", - sel.acpEnd, sel.acpEnd, - referenceString)) - return PR_FALSE; - - - const LONG POSTCOMPOSITION_SEL_START = sel.acpEnd - 8; - const LONG POSTCOMPOSITION_SEL_END = POSTCOMPOSITION_SEL_START + 2; - - sel.acpStart = POSTCOMPOSITION_SEL_START; - sel.acpEnd = POSTCOMPOSITION_SEL_END; - hr = mImpl->mStore->SetSelection(1, &sel); - if (!(SUCCEEDED(hr))) { - fail("TestComposition: SetSelection (composition)"); - return PR_FALSE; - } - - hr = sink->OnEndComposition(mImpl); - if (!(SUCCEEDED(hr))) { - fail("TestComposition: OnEndComposition"); - return PR_FALSE; - } - - if (!TestCompositionSelectionAndText("post-composition", - sel.acpStart, sel.acpEnd, - referenceString)) - return PR_FALSE; - - - const LONG EMPTYCOMPOSITION_START = mImpl->mRangeStart + 2; - const LONG EMPTYCOMPOSITION_LENGTH = mImpl->mRangeLength - 4; - - mImpl->mRangeStart = EMPTYCOMPOSITION_START; - mImpl->mRangeLength = EMPTYCOMPOSITION_LENGTH; - okay = FALSE; - hr = sink->OnStartComposition(mImpl, &okay); - if (!(SUCCEEDED(hr) && - okay)) { - fail("TestComposition: OnStartComposition (empty composition)"); - return PR_FALSE; - } - - hr = sink->OnEndComposition(mImpl); - if (!(SUCCEEDED(hr))) { - fail("TestComposition: OnEndComposition (empty composition)"); - return PR_FALSE; - } - - if (!TestCompositionSelectionAndText("empty composition", - mImpl->mRangeStart, - mImpl->mRangeStart + mImpl->mRangeLength, - referenceString)) - return PR_FALSE; - - return PR_TRUE; -} - -PRBool -TestApp::TestNotificationTextChange(nsIWidget* aWidget, - PRUint32 aCode, - const nsAString& aCharacter, - LONG aStart, - LONG aOldEnd, - LONG aNewEnd) -{ - MSG msg; - if (::PeekMessageW(&msg, NULL, WM_USER_TSF_TEXTCHANGE, - WM_USER_TSF_TEXTCHANGE, PM_REMOVE)) - ::DispatchMessageW(&msg); - mImpl->mTextChanged = PR_FALSE; - nsresult nsr = aWidget->SynthesizeNativeKeyEvent(0, aCode, 0, - aCharacter, aCharacter); - if (::PeekMessageW(&msg, NULL, WM_USER_TSF_TEXTCHANGE, - WM_USER_TSF_TEXTCHANGE, PM_REMOVE)) - ::DispatchMessageW(&msg); - return NS_SUCCEEDED(nsr) && - mImpl->mTextChanged && - aStart == mImpl->mTextChangeData.acpStart && - aOldEnd == mImpl->mTextChangeData.acpOldEnd && - aNewEnd == mImpl->mTextChangeData.acpNewEnd; -} - -PRBool -TestApp::TestNotification(void) -{ - nsresult nsr; - // get selection to test notification support - nsCOMPtr selCon; - if (!(NS_SUCCEEDED(GetSelCon(getter_AddRefs(selCon))) && selCon)) { - fail("TestNotification: get nsISelectionController"); - return PR_FALSE; - } - - nsr = selCon->CompleteMove(PR_FALSE, PR_FALSE); - if (!(NS_SUCCEEDED(nsr))) { - fail("TestNotification: CompleteMove"); - return PR_FALSE; - } - - mImpl->mSelChanged = PR_FALSE; - nsr = selCon->CharacterMove(PR_TRUE, PR_FALSE); - if (!(NS_SUCCEEDED(nsr) && - mImpl->mSelChanged)) { - fail("TestNotification: CharacterMove"); - return PR_FALSE; - } - - mImpl->mSelChanged = PR_FALSE; - nsr = selCon->CharacterMove(PR_TRUE, PR_TRUE); - if (!(NS_SUCCEEDED(nsr) && - mImpl->mSelChanged)) { - fail("TestNotification: CharacterMove (extend)"); - return PR_FALSE; - } - - nsCOMPtr widget; - nsCOMPtr docShell; - 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_SUCCEEDED(nsr) && widget)) { - fail("TestNotification: get nsIWidget"); - return PR_FALSE; - } - - NS_NAMED_LITERAL_STRING(character, ""); - NS_NAMED_LITERAL_STRING(characterA, "A"); - - // The selection test code above placed the selection at offset 1 to 2 - const LONG TEXTCHANGE1_START = 1; - const LONG TEXTCHANGE1_OLDEND = 2; - const LONG TEXTCHANGE1_NEWEND = 2; - - // replace single selected character with 'A' - if (!TestNotificationTextChange(widget, 'A', characterA, - TEXTCHANGE1_START, TEXTCHANGE1_OLDEND, TEXTCHANGE1_NEWEND)) { - fail("TestNotification: text change 1"); - return PR_FALSE; - } - - const LONG TEXTCHANGE2_START = TEXTCHANGE1_NEWEND; - const LONG TEXTCHANGE2_OLDEND = TEXTCHANGE1_NEWEND; - const LONG TEXTCHANGE2_NEWEND = TEXTCHANGE1_NEWEND + 1; - - // insert 'A' - if (!TestNotificationTextChange(widget, 'A', characterA, - TEXTCHANGE2_START, TEXTCHANGE2_OLDEND, TEXTCHANGE2_NEWEND)) { - fail("TestNotification: text change 2"); - return PR_FALSE; - } - - const LONG TEXTCHANGE3_START = TEXTCHANGE2_NEWEND - 1; - const LONG TEXTCHANGE3_OLDEND = TEXTCHANGE2_NEWEND; - const LONG TEXTCHANGE3_NEWEND = TEXTCHANGE2_NEWEND - 1; - - // backspace - if (!TestNotificationTextChange(widget, '\b', character, - TEXTCHANGE3_START, TEXTCHANGE3_OLDEND, TEXTCHANGE3_NEWEND)) { - fail("TestNotification: text change 3"); - return PR_FALSE; - } - return PR_TRUE; -} - -nsresult -TestApp::GetSelCon(nsISelectionController** aSelCon) -{ - 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) { - nsIFrame* frame = presShell->GetPrimaryFrameFor( - nsCOMPtr(do_QueryInterface(mCurrentNode))); - if (frame) { - nsPresContext* presContext = presShell->GetPresContext(); - if (presContext) { - nsr = frame->GetSelectionController(presContext, aSelCon); - } - } - } - } - return nsr; -} - -int main(int argc, char** argv) -{ - ScopedXPCOM xpcom("TestWinTSF (bug #88831)"); - if (xpcom.failed()) - return 1; - - nsRefPtr tests = new TestApp(); - if (!tests) - return 1; - - if (NS_FAILED(tests->Run())) { - fail("run failed"); - return 1; - } - return int(tests->CheckFailed()); -}