michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: michael@0: /* This tests Mozilla's Text Services Framework implementation (bug #88831) michael@0: * michael@0: * The Mozilla implementation interacts with the TSF system through a michael@0: * system-provided COM interface, ITfThreadMgr. This tests works by swapping michael@0: * the system version of the interface with a custom version implemented in michael@0: * here. This way the Mozilla implementation thinks it's interacting with the michael@0: * system but in fact is interacting with this test program. This allows the michael@0: * test program to access and test every aspect of the Mozilla implementation. michael@0: */ michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include "TestHarness.h" michael@0: #include michael@0: michael@0: #define WM_USER_TSF_TEXTCHANGE (WM_USER + 0x100) michael@0: michael@0: #ifndef MOZILLA_INTERNAL_API michael@0: // some of the includes make use of internal string types michael@0: #define nsAString_h___ michael@0: #define nsString_h___ michael@0: #define nsStringFwd_h___ michael@0: #define nsReadableUtils_h___ michael@0: class nsACString; michael@0: class nsAString; michael@0: class nsAFlatString; michael@0: class nsAFlatCString; michael@0: class nsAdoptingString; michael@0: class nsAdoptingCString; michael@0: class nsXPIDLString; michael@0: template class nsReadingIterator; michael@0: #endif michael@0: michael@0: #include "nscore.h" michael@0: #include "nsWeakReference.h" michael@0: #include "nsIAppShell.h" michael@0: #include "nsWidgetsCID.h" michael@0: #include "nsIAppShellService.h" michael@0: #include "nsAppShellCID.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsIWebBrowserChrome.h" michael@0: #include "nsIXULWindow.h" michael@0: #include "nsIBaseWindow.h" michael@0: #include "nsIDOMWindow.h" michael@0: #include "nsIDocShell.h" michael@0: #include "nsIWidget.h" michael@0: #include "nsIPresShell.h" michael@0: #include "nsPresContext.h" michael@0: #include "nsIFrame.h" michael@0: #include "nsIWebProgress.h" michael@0: #include "nsIWebProgressListener.h" michael@0: #include "nsIInterfaceRequestorUtils.h" michael@0: #include "nsIDOMHTMLDocument.h" michael@0: #include "mozilla/dom/HTMLBodyElement.h" michael@0: #include "nsIDOMHTMLElement.h" michael@0: #include "nsIDOMHTMLInputElement.h" michael@0: #include "nsIDOMHTMLTextAreaElement.h" michael@0: #include "nsIDOMElement.h" michael@0: #include "nsISelectionController.h" michael@0: #include "nsViewManager.h" michael@0: #include "nsTArray.h" michael@0: michael@0: #ifndef MOZILLA_INTERNAL_API michael@0: #undef nsString_h___ michael@0: #undef nsAString_h___ michael@0: #undef nsReadableUtils_h___ michael@0: #endif michael@0: michael@0: static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID); michael@0: michael@0: class TSFMgrImpl; michael@0: class TSFDocumentMgrImpl; michael@0: class TSFContextImpl; michael@0: class TSFRangeImpl; michael@0: class TSFEnumRangeImpl; michael@0: class TSFAttrPropImpl; michael@0: michael@0: class TestApp : public nsIWebProgressListener, public nsSupportsWeakReference michael@0: { michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSIWEBPROGRESSLISTENER michael@0: michael@0: TestApp() : mFailed(false) {} michael@0: ~TestApp() {} michael@0: michael@0: nsresult Run(void); michael@0: bool CheckFailed(void); michael@0: michael@0: typedef bool (TestApp::*test_type)(void); michael@0: michael@0: protected: michael@0: nsresult Init(void); michael@0: nsresult Term(void); michael@0: bool RunTest(test_type aTest, bool aLock = true); michael@0: michael@0: bool TestFocus(void); michael@0: bool TestClustering(void); michael@0: bool TestSelection(void); michael@0: bool TestText(void); michael@0: bool TestExtents(void); michael@0: bool TestComposition(void); michael@0: bool TestNotification(void); michael@0: bool TestEditMessages(void); michael@0: bool TestScrollMessages(void); michael@0: michael@0: bool TestSelectionInternal(char* aTestName, michael@0: LONG aStart, michael@0: LONG aEnd, michael@0: TsActiveSelEnd aSelEnd); michael@0: bool TestCompositionSelectionAndText(char* aTestName, michael@0: LONG aExpectedSelStart, michael@0: LONG aExpectedSelEnd, michael@0: nsString& aReferenceString); michael@0: bool TestNotificationTextChange(nsIWidget* aWidget, michael@0: uint32_t aCode, michael@0: const nsAString& aCharacter, michael@0: LONG aStart, michael@0: LONG aOldEnd, michael@0: LONG aNewEnd); michael@0: nsresult GetSelCon(nsISelectionController** aSelCon); michael@0: michael@0: bool GetWidget(nsIWidget** aWidget); michael@0: michael@0: bool mFailed; michael@0: nsString mTestString; michael@0: nsRefPtr mMgr; michael@0: nsCOMPtr mAppShell; michael@0: nsCOMPtr mWindow; michael@0: nsCOMPtr mCurrentNode; michael@0: nsCOMPtr mInput; michael@0: nsCOMPtr mTextArea; michael@0: nsCOMPtr mButton; michael@0: }; michael@0: michael@0: NS_IMETHODIMP michael@0: TestApp::OnProgressChange(nsIWebProgress *aWebProgress, michael@0: nsIRequest *aRequest, michael@0: int32_t aCurSelfProgress, michael@0: int32_t aMaxSelfProgress, michael@0: int32_t aCurTotalProgress, michael@0: int32_t aMaxTotalProgress) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: TestApp::OnLocationChange(nsIWebProgress *aWebProgress, michael@0: nsIRequest *aRequest, michael@0: nsIURI *aLocation, michael@0: uint32_t aFlags) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: TestApp::OnStatusChange(nsIWebProgress *aWebProgress, michael@0: nsIRequest *aRequest, michael@0: nsresult aStatus, michael@0: const char16_t *aMessage) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: TestApp::OnSecurityChange(nsIWebProgress *aWebProgress, michael@0: nsIRequest *aRequest, michael@0: uint32_t aState) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: static HRESULT michael@0: GetRegularExtent(ITfRange *aRange, LONG &aStart, LONG &aEnd) michael@0: { michael@0: NS_ENSURE_TRUE(aRange, E_INVALIDARG); michael@0: nsRefPtr rangeACP; michael@0: HRESULT hr = aRange->QueryInterface(IID_ITfRangeACP, michael@0: getter_AddRefs(rangeACP)); michael@0: NS_ENSURE_TRUE(SUCCEEDED(hr) && rangeACP, E_FAIL); michael@0: michael@0: LONG start, length; michael@0: hr = rangeACP->GetExtent(&start, &length); michael@0: NS_ENSURE_TRUE(SUCCEEDED(hr), hr); michael@0: if (length >= 0) { michael@0: aStart = start; michael@0: aEnd = start + length; michael@0: } else { michael@0: aEnd = start; michael@0: aStart = start + length; michael@0: } michael@0: return S_OK; michael@0: } michael@0: michael@0: // {3B2DFDF5-2485-4858-8185-5C6B4EFD38F5} michael@0: static const GUID GUID_COMPOSING_SELECTION_ATTR = michael@0: { 0x3b2dfdf5, 0x2485, 0x4858, michael@0: { 0x81, 0x85, 0x5c, 0x6b, 0x4e, 0xfd, 0x38, 0xf5 } }; michael@0: #define GUID_ATOM_COMPOSING_SELECTION_ATTR \ michael@0: (static_cast(0x3b2dfdf5)) michael@0: michael@0: /****************************************************************************** michael@0: * TSFRangeImpl michael@0: ******************************************************************************/ michael@0: michael@0: class TSFRangeImpl : public ITfRangeACP michael@0: { michael@0: private: michael@0: ULONG mRefCnt; michael@0: michael@0: public: michael@0: LONG mStart; michael@0: LONG mLength; michael@0: michael@0: TSFRangeImpl(LONG aStart = 0, LONG aLength = 0) : michael@0: mRefCnt(0), mStart(aStart), mLength(aLength) michael@0: { michael@0: } michael@0: michael@0: ~TSFRangeImpl() michael@0: { michael@0: } michael@0: michael@0: public: // IUnknown michael@0: michael@0: STDMETHODIMP QueryInterface(REFIID riid, void** ppUnk) michael@0: { michael@0: *ppUnk = nullptr; michael@0: if (IID_IUnknown == riid || IID_ITfRange == riid || IID_ITfRangeACP == riid) michael@0: *ppUnk = static_cast(this); michael@0: if (*ppUnk) michael@0: AddRef(); michael@0: return *ppUnk ? S_OK : E_NOINTERFACE; michael@0: } michael@0: michael@0: STDMETHODIMP_(ULONG) AddRef(void) michael@0: { michael@0: return ++mRefCnt; michael@0: } michael@0: michael@0: STDMETHODIMP_(ULONG) Release(void) michael@0: { michael@0: if (--mRefCnt) return mRefCnt; michael@0: delete this; michael@0: return 0; michael@0: } michael@0: michael@0: public: // ITfRange michael@0: michael@0: STDMETHODIMP GetText(TfEditCookie ec, DWORD dwFlags, WCHAR *pchText, michael@0: ULONG cchMax, ULONG *pcch) michael@0: { michael@0: NS_NOTREACHED("ITfRange::GetText"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP SetText(TfEditCookie ec, DWORD dwFlags, const WCHAR *pchText, michael@0: LONG cch) michael@0: { michael@0: NS_NOTREACHED("ITfRange::SetText"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP GetFormattedText(TfEditCookie ec, IDataObject **ppDataObject) michael@0: { michael@0: NS_NOTREACHED("ITfRange::GetFormattedText"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP GetEmbedded(TfEditCookie ec, REFGUID rguidService, REFIID riid, michael@0: IUnknown **ppunk) michael@0: { michael@0: NS_NOTREACHED("ITfRange::GetEmbedded"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP InsertEmbedded(TfEditCookie ec, DWORD dwFlags, michael@0: IDataObject *pDataObject) michael@0: { michael@0: NS_NOTREACHED("ITfRange::InsertEmbedded"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP ShiftStart(TfEditCookie ec, LONG cchReq, LONG *pcch, michael@0: const TF_HALTCOND *pHalt) michael@0: { michael@0: NS_NOTREACHED("ITfRange::ShiftStart"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP ShiftEnd(TfEditCookie ec, LONG cchReq, LONG *pcch, michael@0: const TF_HALTCOND *pHalt) michael@0: { michael@0: NS_NOTREACHED("ITfRange::ShiftEnd"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP ShiftStartToRange(TfEditCookie ec, ITfRange *pRange, michael@0: TfAnchor aPos) michael@0: { michael@0: NS_NOTREACHED("ITfRange::ShiftStartToRange"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP ShiftEndToRange(TfEditCookie ec, ITfRange *pRange, TfAnchor aPos) michael@0: { michael@0: NS_NOTREACHED("ITfRange::ShiftEndToRange"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP ShiftStartRegion(TfEditCookie ec, TfShiftDir dir, michael@0: BOOL *pfNoRegion) michael@0: { michael@0: NS_NOTREACHED("ITfRange::ShiftStartRegion"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP ShiftEndRegion(TfEditCookie ec, TfShiftDir dir, BOOL *pfNoRegion) michael@0: { michael@0: NS_NOTREACHED("ITfRange::ShiftEndRegion"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP IsEmpty(TfEditCookie ec, BOOL *pfEmpty) michael@0: { michael@0: NS_NOTREACHED("ITfRange::IsEmpty"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP Collapse(TfEditCookie ec, TfAnchor aPos) michael@0: { michael@0: NS_NOTREACHED("ITfRange::Collapse"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP IsEqualStart(TfEditCookie ec, ITfRange *pWith, TfAnchor aPos, michael@0: BOOL *pfEqual) michael@0: { michael@0: NS_NOTREACHED("ITfRange::IsEqualStart"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP IsEqualEnd(TfEditCookie ec, ITfRange *pWith, TfAnchor aPos, michael@0: BOOL *pfEqual) michael@0: { michael@0: NS_NOTREACHED("ITfRange::IsEqualEnd"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP CompareStart(TfEditCookie ec, ITfRange *pWith, TfAnchor aPos, michael@0: LONG *plResult) michael@0: { michael@0: NS_NOTREACHED("ITfRange::CompareStart"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP CompareEnd(TfEditCookie ec, ITfRange *pWith, TfAnchor aPos, michael@0: LONG *plResult) michael@0: { michael@0: NS_NOTREACHED("ITfRange::CompareEnd"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP AdjustForInsert(TfEditCookie ec, ULONG cchInsert, michael@0: BOOL *pfInsertOk) michael@0: { michael@0: NS_NOTREACHED("ITfRange::AdjustForInsert"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP GetGravity(TfGravity *pgStart, TfGravity *pgEnd) michael@0: { michael@0: NS_NOTREACHED("ITfRange::GetGravity"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP SetGravity(TfEditCookie ec, TfGravity gStart, TfGravity gEnd) michael@0: { michael@0: NS_NOTREACHED("ITfRange::SetGravity"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP Clone(ITfRange **ppClone) michael@0: { michael@0: NS_NOTREACHED("ITfRange::Clone"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP GetContext(ITfContext **ppContext) michael@0: { michael@0: NS_NOTREACHED("ITfRange::GetContext"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: public: // ITfRangeACP michael@0: michael@0: STDMETHODIMP GetExtent(LONG *pacpAnchor, LONG *pcch) michael@0: { michael@0: NS_ENSURE_TRUE(pacpAnchor, E_FAIL); michael@0: NS_ENSURE_TRUE(pcch, E_FAIL); michael@0: *pacpAnchor = mStart; michael@0: *pcch = mLength; michael@0: return S_OK; michael@0: } michael@0: michael@0: STDMETHODIMP SetExtent(LONG acpAnchor, LONG cch) michael@0: { michael@0: mStart = acpAnchor; michael@0: mLength = cch; michael@0: return S_OK; michael@0: } michael@0: }; michael@0: michael@0: /****************************************************************************** michael@0: * TSFEnumRangeImpl michael@0: ******************************************************************************/ michael@0: michael@0: class TSFEnumRangeImpl : public IEnumTfRanges michael@0: { michael@0: private: michael@0: ULONG mRefCnt; michael@0: uint32_t mCurrentIndex; michael@0: michael@0: public: michael@0: nsTArray > mRanges; michael@0: michael@0: TSFEnumRangeImpl() : michael@0: mRefCnt(0), mCurrentIndex(0) michael@0: { michael@0: } michael@0: michael@0: ~TSFEnumRangeImpl() michael@0: { michael@0: } michael@0: michael@0: public: // IUnknown michael@0: michael@0: STDMETHODIMP QueryInterface(REFIID riid, void** ppUnk) michael@0: { michael@0: *ppUnk = nullptr; michael@0: if (IID_IUnknown == riid || IID_IEnumTfRanges == riid) michael@0: *ppUnk = static_cast(this); michael@0: if (*ppUnk) michael@0: AddRef(); michael@0: return *ppUnk ? S_OK : E_NOINTERFACE; michael@0: } michael@0: michael@0: STDMETHODIMP_(ULONG) AddRef(void) michael@0: { michael@0: return ++mRefCnt; michael@0: } michael@0: michael@0: STDMETHODIMP_(ULONG) Release(void) michael@0: { michael@0: if (--mRefCnt) return mRefCnt; michael@0: delete this; michael@0: return 0; michael@0: } michael@0: michael@0: public: // IEnumTfRanges michael@0: michael@0: STDMETHODIMP Clone(IEnumTfRanges **ppEnum) michael@0: { michael@0: NS_NOTREACHED("IEnumTfRanges::Clone"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP Next(ULONG ulCount, ITfRange **ppRange, ULONG *pcFetched) michael@0: { michael@0: NS_ENSURE_TRUE(ppRange, E_FAIL); michael@0: if (pcFetched) michael@0: *pcFetched = 0; michael@0: if (mCurrentIndex + ulCount - 1 >= mRanges.Length()) michael@0: return E_FAIL; michael@0: for (uint32_t i = 0; i < ulCount; i++) { michael@0: ppRange[i] = mRanges[mCurrentIndex++]; michael@0: ppRange[i]->AddRef(); michael@0: if (pcFetched) michael@0: (*pcFetched)++; michael@0: } michael@0: return S_OK; michael@0: } michael@0: michael@0: STDMETHODIMP Reset() michael@0: { michael@0: mCurrentIndex = 0; michael@0: return S_OK; michael@0: } michael@0: michael@0: STDMETHODIMP Skip(ULONG ulCount) michael@0: { michael@0: mCurrentIndex += ulCount; michael@0: return S_OK; michael@0: } michael@0: }; michael@0: michael@0: /****************************************************************************** michael@0: * TSFDispAttrInfoImpl michael@0: ******************************************************************************/ michael@0: michael@0: class TSFDispAttrInfoImpl : public ITfDisplayAttributeInfo michael@0: { michael@0: private: michael@0: ULONG mRefCnt; michael@0: TF_DISPLAYATTRIBUTE mAttr; michael@0: michael@0: public: michael@0: michael@0: TSFDispAttrInfoImpl(REFGUID aGUID) : michael@0: mRefCnt(0) michael@0: { michael@0: if (aGUID == GUID_COMPOSING_SELECTION_ATTR) { michael@0: mAttr.crText.type = TF_CT_NONE; michael@0: mAttr.crBk.type = TF_CT_NONE; michael@0: mAttr.lsStyle = TF_LS_SQUIGGLE; michael@0: mAttr.fBoldLine = FALSE; michael@0: mAttr.crLine.type = TF_CT_NONE; michael@0: mAttr.bAttr = TF_ATTR_INPUT; michael@0: } else { michael@0: NS_NOTREACHED("TSFDispAttrInfoImpl::TSFDispAttrInfoImpl"); michael@0: } michael@0: } michael@0: michael@0: ~TSFDispAttrInfoImpl() michael@0: { michael@0: } michael@0: michael@0: public: // IUnknown michael@0: michael@0: STDMETHODIMP QueryInterface(REFIID riid, void** ppUnk) michael@0: { michael@0: *ppUnk = nullptr; michael@0: if (IID_IUnknown == riid || IID_ITfDisplayAttributeInfo == riid) michael@0: *ppUnk = static_cast(this); michael@0: if (*ppUnk) michael@0: AddRef(); michael@0: return *ppUnk ? S_OK : E_NOINTERFACE; michael@0: } michael@0: michael@0: STDMETHODIMP_(ULONG) AddRef(void) michael@0: { michael@0: return ++mRefCnt; michael@0: } michael@0: michael@0: STDMETHODIMP_(ULONG) Release(void) michael@0: { michael@0: if (--mRefCnt) return mRefCnt; michael@0: delete this; michael@0: return 0; michael@0: } michael@0: michael@0: public: // ITfDisplayAttributeInfo michael@0: michael@0: STDMETHODIMP GetGUID(GUID *pguid) michael@0: { michael@0: NS_NOTREACHED("ITfDisplayAttributeInfo::GetGUID"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP GetDescription(BSTR *pbstrDesc) michael@0: { michael@0: NS_NOTREACHED("ITfDisplayAttributeInfo::GetDescription"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP GetAttributeInfo(TF_DISPLAYATTRIBUTE *pda) michael@0: { michael@0: NS_ENSURE_TRUE(pda, E_INVALIDARG); michael@0: *pda = mAttr; michael@0: return S_OK; michael@0: } michael@0: michael@0: STDMETHODIMP SetAttributeInfo(const TF_DISPLAYATTRIBUTE *pda) michael@0: { michael@0: NS_NOTREACHED("ITfDisplayAttributeInfo::SetAttributeInfo"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP Reset() michael@0: { michael@0: NS_NOTREACHED("ITfDisplayAttributeInfo::Reset"); michael@0: return E_NOTIMPL; michael@0: } michael@0: }; michael@0: michael@0: /****************************************************************************** michael@0: * TSFAttrPropImpl michael@0: ******************************************************************************/ michael@0: michael@0: class TSFAttrPropImpl : public ITfProperty michael@0: { michael@0: private: michael@0: ULONG mRefCnt; michael@0: michael@0: public: michael@0: nsTArray > mRanges; michael@0: michael@0: TSFAttrPropImpl() : michael@0: mRefCnt(0) michael@0: { michael@0: } michael@0: michael@0: ~TSFAttrPropImpl() michael@0: { michael@0: } michael@0: michael@0: public: // IUnknown michael@0: michael@0: STDMETHODIMP QueryInterface(REFIID riid, void** ppUnk) michael@0: { michael@0: *ppUnk = nullptr; michael@0: if (IID_IUnknown == riid || IID_ITfProperty == riid || michael@0: IID_ITfReadOnlyProperty == riid) michael@0: *ppUnk = static_cast(this); michael@0: if (*ppUnk) michael@0: AddRef(); michael@0: return *ppUnk ? S_OK : E_NOINTERFACE; michael@0: } michael@0: michael@0: STDMETHODIMP_(ULONG) AddRef(void) michael@0: { michael@0: return ++mRefCnt; michael@0: } michael@0: michael@0: STDMETHODIMP_(ULONG) Release(void) michael@0: { michael@0: if (--mRefCnt) return mRefCnt; michael@0: delete this; michael@0: return 0; michael@0: } michael@0: michael@0: public: // ITfProperty michael@0: michael@0: STDMETHODIMP FindRange(TfEditCookie ec, ITfRange *pRange, ITfRange **ppRange, michael@0: TfAnchor aPos) michael@0: { michael@0: NS_NOTREACHED("ITfProperty::FindRange"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP SetValueStore(TfEditCookie ec, ITfRange *pRange, michael@0: ITfPropertyStore *pPropStore) michael@0: { michael@0: NS_NOTREACHED("ITfProperty::SetValueStore"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP SetValue(TfEditCookie ec, ITfRange *pRange, michael@0: const VARIANT *pvarValue) michael@0: { michael@0: NS_NOTREACHED("ITfProperty::SetValue"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP Clear(TfEditCookie ec, ITfRange *pRange) michael@0: { michael@0: NS_NOTREACHED("ITfProperty::Clear"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: public: // ITfReadOnlyProperty michael@0: michael@0: STDMETHODIMP GetType(GUID *pguid) michael@0: { michael@0: NS_NOTREACHED("ITfReadOnlyProperty::GetType"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP EnumRanges(TfEditCookie ec, IEnumTfRanges **ppEnum, michael@0: ITfRange *pTargetRange) michael@0: { michael@0: NS_ENSURE_TRUE(ppEnum, E_INVALIDARG); michael@0: NS_ENSURE_TRUE(pTargetRange, E_INVALIDARG); michael@0: michael@0: // XXX ec checking is not implemented yet. michael@0: michael@0: LONG targetStart = 0, targetEnd = 0; michael@0: if (pTargetRange) { michael@0: HRESULT hr = GetRegularExtent(pTargetRange, targetStart, targetEnd); michael@0: NS_ENSURE_TRUE(SUCCEEDED(hr), hr); michael@0: } michael@0: nsRefPtr er = new TSFEnumRangeImpl(); michael@0: NS_ENSURE_TRUE(er, E_OUTOFMEMORY); michael@0: for (uint32_t i = 0; i < mRanges.Length(); i++) { michael@0: LONG start, end; michael@0: HRESULT hr = GetRegularExtent(mRanges[i], start, end); michael@0: NS_ENSURE_TRUE(SUCCEEDED(hr), hr); michael@0: if (pTargetRange) { michael@0: // If pTargetRange is not null and the current range is not overlapped michael@0: // with it, we don't need to add range. michael@0: if (targetStart > end || targetEnd < start) michael@0: continue; michael@0: // Otherwise, shrink to the target range. michael@0: start = std::max(targetStart, start); michael@0: end = std::min(targetEnd, end); michael@0: } michael@0: nsRefPtr range = new TSFRangeImpl(start, end - start); michael@0: NS_ENSURE_TRUE(range, E_OUTOFMEMORY); michael@0: er->mRanges.AppendElement(range); michael@0: } michael@0: *ppEnum = er; michael@0: (*ppEnum)->AddRef(); michael@0: return S_OK; michael@0: } michael@0: michael@0: STDMETHODIMP GetValue(TfEditCookie ec, ITfRange *pRange, VARIANT *pvarValue) michael@0: { michael@0: NS_ENSURE_TRUE(pvarValue, E_INVALIDARG); michael@0: michael@0: LONG givenStart, givenEnd; michael@0: HRESULT hr = GetRegularExtent(pRange, givenStart, givenEnd); michael@0: NS_ENSURE_TRUE(SUCCEEDED(hr), hr); michael@0: for (uint32_t i = 0; i < mRanges.Length(); i++) { michael@0: LONG start, end; michael@0: HRESULT hr = GetRegularExtent(mRanges[i], start, end); michael@0: NS_ENSURE_TRUE(SUCCEEDED(hr), hr); michael@0: // pRange must be same as (or included in) a range of mRanges. michael@0: if (givenStart > start || givenEnd < end) michael@0: continue; michael@0: pvarValue->vt = VT_I4; michael@0: pvarValue->lVal = static_cast(GUID_ATOM_COMPOSING_SELECTION_ATTR); michael@0: return S_OK; michael@0: } michael@0: return E_FAIL; michael@0: } michael@0: michael@0: STDMETHODIMP GetContext(ITfContext **ppContext) michael@0: { michael@0: NS_NOTREACHED("ITfReadOnlyProperty::GetContext"); michael@0: return E_NOTIMPL; michael@0: } michael@0: }; michael@0: michael@0: /****************************************************************************** michael@0: * TSFContextImpl michael@0: ******************************************************************************/ michael@0: michael@0: class TSFContextImpl : public ITfContext, michael@0: public ITfCompositionView, public ITextStoreACPSink michael@0: { michael@0: private: michael@0: ULONG mRefCnt; michael@0: michael@0: public: michael@0: nsRefPtr mAttrProp; michael@0: nsRefPtr mDocMgr; michael@0: bool mTextChanged; michael@0: bool mSelChanged; michael@0: TS_TEXTCHANGE mTextChangeData; michael@0: michael@0: public: michael@0: TSFContextImpl(TSFDocumentMgrImpl* aDocMgr) : michael@0: mDocMgr(aDocMgr), mRefCnt(0), mTextChanged(false), michael@0: mSelChanged(false) michael@0: { michael@0: mAttrProp = new TSFAttrPropImpl(); michael@0: if (!mAttrProp) { michael@0: NS_NOTREACHED("TSFContextImpl::TSFContextImpl (OOM)"); michael@0: return; michael@0: } michael@0: } michael@0: michael@0: ~TSFContextImpl() michael@0: { michael@0: } michael@0: michael@0: public: // IUnknown michael@0: michael@0: STDMETHODIMP QueryInterface(REFIID riid, void** ppUnk) michael@0: { michael@0: *ppUnk = nullptr; michael@0: if (IID_IUnknown == riid || IID_ITfContext == riid) michael@0: *ppUnk = static_cast(this); michael@0: else if (IID_ITextStoreACPSink == riid) michael@0: *ppUnk = static_cast(this); michael@0: if (*ppUnk) michael@0: AddRef(); michael@0: return *ppUnk ? S_OK : E_NOINTERFACE; michael@0: } michael@0: michael@0: STDMETHODIMP_(ULONG) AddRef(void) michael@0: { michael@0: return ++mRefCnt; michael@0: } michael@0: michael@0: STDMETHODIMP_(ULONG) Release(void) michael@0: { michael@0: if (--mRefCnt) return mRefCnt; michael@0: delete this; michael@0: return 0; michael@0: } michael@0: michael@0: public: // ITfContext michael@0: michael@0: STDMETHODIMP RequestEditSession(TfClientId tid, ITfEditSession *pes, michael@0: DWORD dwFlags, HRESULT *phrSession) michael@0: { michael@0: NS_NOTREACHED("ITfContext::RequestEditSession"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP InWriteSession(TfClientId tid, BOOL *pfWriteSession) michael@0: { michael@0: NS_NOTREACHED("ITfContext::InWriteSession"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP GetSelection(TfEditCookie ec, ULONG ulIndex, ULONG ulCount, michael@0: TF_SELECTION *pSelection, ULONG *pcFetched) michael@0: { michael@0: NS_NOTREACHED("ITfContext::GetSelection"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP SetSelection(TfEditCookie ec, ULONG ulCount, michael@0: const TF_SELECTION *pSelection) michael@0: { michael@0: NS_NOTREACHED("ITfContext::SetSelection"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP GetStart(TfEditCookie ec, ITfRange **ppStart) michael@0: { michael@0: NS_NOTREACHED("ITfContext::GetStart"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP GetEnd(TfEditCookie ec, ITfRange **ppEnd) michael@0: { michael@0: NS_NOTREACHED("ITfContext::GetEnd"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP GetActiveView(ITfContextView **ppView) michael@0: { michael@0: NS_NOTREACHED("ITfContext::GetActiveView"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP EnumViews(IEnumTfContextViews **ppEnum) michael@0: { michael@0: NS_NOTREACHED("ITfContext::EnumViews"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP GetStatus(TF_STATUS *pdcs) michael@0: { michael@0: NS_NOTREACHED("ITfContext::GetStatus"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP GetProperty(REFGUID guidProp, ITfProperty **ppProp) michael@0: { michael@0: NS_ENSURE_TRUE(ppProp, E_INVALIDARG); michael@0: if (guidProp == GUID_PROP_ATTRIBUTE) { michael@0: (*ppProp) = mAttrProp; michael@0: (*ppProp)->AddRef(); michael@0: return S_OK; michael@0: } michael@0: NS_NOTREACHED("ITfContext::GetProperty"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP GetAppProperty(REFGUID guidProp, ITfReadOnlyProperty **ppProp) michael@0: { michael@0: NS_NOTREACHED("ITfContext::GetAppProperty"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP TrackProperties(const GUID **prgProp, ULONG cProp, michael@0: const GUID **prgAppProp, ULONG cAppProp, michael@0: ITfReadOnlyProperty **ppProperty) michael@0: { michael@0: NS_NOTREACHED("ITfContext::TrackProperties"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP EnumProperties(IEnumTfProperties **ppEnum) michael@0: { michael@0: NS_NOTREACHED("ITfContext::EnumProperties"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP GetDocumentMgr(ITfDocumentMgr **ppDm) michael@0: { michael@0: NS_NOTREACHED("ITfContext::GetDocumentMgr"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP CreateRangeBackup(TfEditCookie ec, ITfRange *pRange, michael@0: ITfRangeBackup **ppBackup) michael@0: { michael@0: NS_NOTREACHED("ITfContext::CreateRangeBackup"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: public: // ITfCompositionView michael@0: michael@0: STDMETHODIMP GetOwnerClsid(CLSID* pclsid) michael@0: { michael@0: NS_NOTREACHED("ITfCompositionView::GetOwnerClsid"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP GetRange(ITfRange** ppRange) michael@0: { michael@0: NS_ENSURE_TRUE(ppRange, E_INVALIDARG); michael@0: NS_ENSURE_TRUE(mAttrProp->mRanges.Length() > 0, E_FAIL); michael@0: LONG start = LONG_MAX, end = 0; michael@0: for (uint32_t i = 0; i < mAttrProp->mRanges.Length(); i++) { michael@0: LONG tmpStart, tmpEnd; michael@0: HRESULT hr = GetRegularExtent(mAttrProp->mRanges[i], tmpStart, tmpEnd); michael@0: NS_ENSURE_TRUE(SUCCEEDED(hr), hr); michael@0: start = std::min(start, tmpStart); michael@0: end = std::max(end, tmpEnd); michael@0: } michael@0: nsRefPtr range = new TSFRangeImpl(); michael@0: NS_ENSURE_TRUE(range, E_OUTOFMEMORY); michael@0: HRESULT hr = range->SetExtent(start, end - start); michael@0: NS_ENSURE_TRUE(SUCCEEDED(hr), hr); michael@0: (*ppRange) = range; michael@0: (*ppRange)->AddRef(); michael@0: return S_OK; michael@0: } michael@0: michael@0: public: // ITextStoreACPSink michael@0: michael@0: STDMETHODIMP OnTextChange(DWORD dwFlags, const TS_TEXTCHANGE *pChange) michael@0: { michael@0: mTextChanged = true; michael@0: mTextChangeData = *pChange; michael@0: return S_OK; michael@0: } michael@0: michael@0: STDMETHODIMP OnSelectionChange(void) michael@0: { michael@0: mSelChanged = true; michael@0: return S_OK; michael@0: } michael@0: michael@0: STDMETHODIMP OnLayoutChange(TsLayoutCode lcode, TsViewCookie vcView) michael@0: { michael@0: return S_OK; michael@0: } michael@0: michael@0: STDMETHODIMP OnStatusChange(DWORD dwFlags) michael@0: { michael@0: return S_OK; michael@0: } michael@0: michael@0: STDMETHODIMP OnAttrsChange(LONG acpStart, LONG acpEnd, ULONG cAttrs, michael@0: const TS_ATTRID *paAttrs) michael@0: { michael@0: return S_OK; michael@0: } michael@0: michael@0: STDMETHODIMP OnLockGranted(DWORD dwLockFlags); michael@0: michael@0: STDMETHODIMP OnStartEditTransaction(void) michael@0: { michael@0: return S_OK; michael@0: } michael@0: michael@0: STDMETHODIMP OnEndEditTransaction(void) michael@0: { michael@0: return S_OK; michael@0: } michael@0: }; michael@0: michael@0: /****************************************************************************** michael@0: * TSFDocumentMgrImpl michael@0: ******************************************************************************/ michael@0: michael@0: class TSFDocumentMgrImpl : public ITfDocumentMgr michael@0: { michael@0: private: michael@0: ULONG mRefCnt; michael@0: michael@0: public: michael@0: nsRefPtr mMgr; michael@0: nsRefPtr mStore; michael@0: nsRefPtr mContextBase; michael@0: nsRefPtr mContextTop; // XXX currently, we don't support this. michael@0: michael@0: public: michael@0: TSFDocumentMgrImpl(TSFMgrImpl* aMgr) : michael@0: mRefCnt(0), mMgr(aMgr) michael@0: { michael@0: } michael@0: michael@0: ~TSFDocumentMgrImpl() michael@0: { michael@0: } michael@0: michael@0: public: // IUnknown michael@0: michael@0: STDMETHODIMP QueryInterface(REFIID riid, void** ppUnk) michael@0: { michael@0: *ppUnk = nullptr; michael@0: if (IID_IUnknown == riid || IID_ITfDocumentMgr == riid) michael@0: *ppUnk = static_cast(this); michael@0: if (*ppUnk) michael@0: AddRef(); michael@0: return *ppUnk ? S_OK : E_NOINTERFACE; michael@0: } michael@0: michael@0: STDMETHODIMP_(ULONG) AddRef(void) michael@0: { michael@0: return ++mRefCnt; michael@0: } michael@0: michael@0: STDMETHODIMP_(ULONG) Release(void); michael@0: michael@0: public: // ITfDocumentMgr michael@0: michael@0: STDMETHODIMP CreateContext(TfClientId tidOwner, DWORD dwFlags, michael@0: IUnknown *punk, ITfContext **ppic, michael@0: TfEditCookie *pecTextStore) michael@0: { michael@0: nsRefPtr context = new TSFContextImpl(this); michael@0: punk->QueryInterface(IID_ITextStoreACP, getter_AddRefs(mStore)); michael@0: NS_ENSURE_TRUE(mStore, E_FAIL); michael@0: HRESULT hr = michael@0: mStore->AdviseSink(IID_ITextStoreACPSink, michael@0: static_cast(context.get()), michael@0: TS_AS_ALL_SINKS); michael@0: if (FAILED(hr)) mStore = nullptr; michael@0: NS_ENSURE_TRUE(SUCCEEDED(hr), E_FAIL); michael@0: (*ppic) = context; michael@0: (*ppic)->AddRef(); michael@0: *pecTextStore = 1; michael@0: return S_OK; michael@0: } michael@0: michael@0: STDMETHODIMP Push(ITfContext *pic) michael@0: { michael@0: if (mContextTop) { michael@0: NS_NOTREACHED("TSFDocumentMgrImpl::Push stack is already full"); michael@0: return E_FAIL; michael@0: } michael@0: if (mContextBase) { michael@0: NS_WARNING("TSFDocumentMgrImpl::Push additional context is pushed, but we don't support that yet."); michael@0: if (mContextBase == pic) { michael@0: NS_NOTREACHED("TSFDocumentMgrImpl::Push same context is pused again"); michael@0: return E_FAIL; michael@0: } michael@0: mContextTop = static_cast(pic); michael@0: return S_OK; michael@0: } michael@0: mContextBase = static_cast(pic); michael@0: return S_OK; michael@0: } michael@0: michael@0: STDMETHODIMP Pop(DWORD dwFlags) michael@0: { michael@0: if (!mStore) michael@0: return E_FAIL; michael@0: if (dwFlags == TF_POPF_ALL) { michael@0: NS_ENSURE_TRUE(mContextBase, E_FAIL); michael@0: mStore->UnadviseSink(static_cast(mContextBase.get())); michael@0: mStore = nullptr; michael@0: mContextBase = nullptr; michael@0: mContextTop = nullptr; michael@0: return S_OK; michael@0: } michael@0: if (dwFlags == 0) { michael@0: if (!mContextTop) { michael@0: NS_NOTREACHED("TSFDocumentMgrImpl::Pop there is non-base context"); michael@0: return E_FAIL; michael@0: } michael@0: mContextTop = nullptr; michael@0: return S_OK; michael@0: } michael@0: NS_NOTREACHED("TSFDocumentMgrImpl::Pop invalid flag"); michael@0: return E_FAIL; michael@0: } michael@0: michael@0: STDMETHODIMP GetTop(ITfContext **ppic) michael@0: { michael@0: (*ppic) = mContextTop ? mContextTop : mContextBase; michael@0: if (*ppic) (*ppic)->AddRef(); michael@0: return S_OK; michael@0: } michael@0: michael@0: STDMETHODIMP GetBase(ITfContext **ppic) michael@0: { michael@0: (*ppic) = mContextBase; michael@0: if (*ppic) (*ppic)->AddRef(); michael@0: return S_OK; michael@0: } michael@0: michael@0: STDMETHODIMP EnumContexts(IEnumTfContexts **ppEnum) michael@0: { michael@0: NS_NOTREACHED("ITfDocumentMgr::EnumContexts"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: }; michael@0: michael@0: /****************************************************************************** michael@0: * TSFMgrImpl michael@0: ******************************************************************************/ michael@0: michael@0: class TSFMgrImpl : public ITfThreadMgr, michael@0: public ITfDisplayAttributeMgr, public ITfCategoryMgr michael@0: { michael@0: private: michael@0: ULONG mRefCnt; michael@0: michael@0: public: michael@0: nsRefPtr mTestApp; michael@0: TestApp::test_type mTest; michael@0: bool mDeactivated; michael@0: TSFDocumentMgrImpl* mFocusedDocument; // Must be raw pointer, but strong. michael@0: int32_t mFocusCount; michael@0: michael@0: TSFMgrImpl(TestApp* test) : mTestApp(test), mTest(nullptr), mRefCnt(0), michael@0: mDeactivated(false), mFocusedDocument(nullptr), mFocusCount(0) michael@0: { michael@0: } michael@0: michael@0: ~TSFMgrImpl() michael@0: { michael@0: } michael@0: michael@0: public: // IUnknown michael@0: michael@0: STDMETHODIMP QueryInterface(REFIID riid, void** ppUnk) michael@0: { michael@0: *ppUnk = nullptr; michael@0: if (IID_IUnknown == riid || IID_ITfThreadMgr == riid) michael@0: *ppUnk = static_cast(this); michael@0: else if (IID_ITfDisplayAttributeMgr == riid) michael@0: *ppUnk = static_cast(this); michael@0: else if (IID_ITfCategoryMgr == riid) michael@0: *ppUnk = static_cast(this); michael@0: if (*ppUnk) michael@0: AddRef(); michael@0: return *ppUnk ? S_OK : E_NOINTERFACE; michael@0: } michael@0: michael@0: STDMETHODIMP_(ULONG) AddRef(void) michael@0: { michael@0: return ++mRefCnt; michael@0: } michael@0: michael@0: STDMETHODIMP_(ULONG) Release(void) michael@0: { michael@0: if (--mRefCnt) return mRefCnt; michael@0: delete this; michael@0: return 0; michael@0: } michael@0: michael@0: public: // ITfThreadMgr michael@0: michael@0: STDMETHODIMP Activate(TfClientId *ptid) michael@0: { michael@0: *ptid = 1; michael@0: return S_OK; michael@0: } michael@0: michael@0: STDMETHODIMP Deactivate(void) michael@0: { michael@0: mDeactivated = true; michael@0: return S_OK; michael@0: } michael@0: michael@0: STDMETHODIMP CreateDocumentMgr(ITfDocumentMgr **ppdim) michael@0: { michael@0: nsRefPtr docMgr = new TSFDocumentMgrImpl(this); michael@0: if (!docMgr) { michael@0: NS_NOTREACHED("TSFMgrImpl::CreateDocumentMgr (OOM)"); michael@0: return E_OUTOFMEMORY; michael@0: } michael@0: (*ppdim) = docMgr; michael@0: (*ppdim)->AddRef(); michael@0: return S_OK; michael@0: } michael@0: michael@0: STDMETHODIMP EnumDocumentMgrs(IEnumTfDocumentMgrs **ppEnum) michael@0: { michael@0: NS_NOTREACHED("ITfThreadMgr::EnumDocumentMgrs"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP GetFocus(ITfDocumentMgr **ppdimFocus) michael@0: { michael@0: (*ppdimFocus) = mFocusedDocument; michael@0: if (*ppdimFocus) (*ppdimFocus)->AddRef(); michael@0: return S_OK; michael@0: } michael@0: michael@0: STDMETHODIMP SetFocus(ITfDocumentMgr *pdimFocus) michael@0: { michael@0: if (!pdimFocus) { michael@0: NS_NOTREACHED("ITfThreadMgr::SetFocus must not be called with NULL"); michael@0: return E_FAIL; michael@0: } michael@0: mFocusCount++; michael@0: if (mFocusedDocument == pdimFocus) { michael@0: return S_OK; michael@0: } michael@0: mFocusedDocument = static_cast(pdimFocus); michael@0: mFocusedDocument->AddRef(); michael@0: return S_OK; michael@0: } michael@0: michael@0: STDMETHODIMP AssociateFocus(HWND hwnd, ITfDocumentMgr *pdimNew, michael@0: ITfDocumentMgr **ppdimPrev) michael@0: { michael@0: NS_NOTREACHED("ITfThreadMgr::AssociateFocus"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP IsThreadFocus(BOOL *pfThreadFocus) michael@0: { michael@0: *pfThreadFocus = TRUE; michael@0: return S_OK; michael@0: } michael@0: michael@0: STDMETHODIMP GetFunctionProvider(REFCLSID clsid, michael@0: ITfFunctionProvider **ppFuncProv) michael@0: { michael@0: NS_NOTREACHED("ITfThreadMgr::GetFunctionProvider"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP EnumFunctionProviders(IEnumTfFunctionProviders **ppEnum) michael@0: { michael@0: NS_NOTREACHED("ITfThreadMgr::EnumFunctionProviders"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP GetGlobalCompartment(ITfCompartmentMgr **ppCompMgr) michael@0: { michael@0: NS_NOTREACHED("ITfThreadMgr::GetGlobalCompartment"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: public: // ITfCategoryMgr michael@0: michael@0: STDMETHODIMP RegisterCategory(REFCLSID rclsid, REFGUID rcatid, REFGUID rguid) michael@0: { michael@0: NS_NOTREACHED("ITfCategoryMgr::RegisterCategory"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP UnregisterCategory(REFCLSID rclsid, REFGUID rcatid, REFGUID rguid) michael@0: { michael@0: NS_NOTREACHED("ITfCategoryMgr::UnregisterCategory"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP EnumCategoriesInItem(REFGUID rguid, IEnumGUID **ppEnum) michael@0: { michael@0: NS_NOTREACHED("ITfCategoryMgr::EnumCategoriesInItem"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP EnumItemsInCategory(REFGUID rcatid, IEnumGUID **ppEnum) michael@0: { michael@0: NS_NOTREACHED("ITfCategoryMgr::EnumItemsInCategory"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP FindClosestCategory(REFGUID rguid, GUID *pcatid, michael@0: const GUID **ppcatidList, ULONG ulCount) michael@0: { michael@0: NS_NOTREACHED("ITfCategoryMgr::FindClosestCategory"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP RegisterGUIDDescription(REFCLSID rclsid, REFGUID rguid, michael@0: const WCHAR *pchDesc, ULONG cch) michael@0: { michael@0: NS_NOTREACHED("ITfCategoryMgr::RegisterGUIDDescription"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP UnregisterGUIDDescription(REFCLSID rclsid, REFGUID rguid) michael@0: { michael@0: NS_NOTREACHED("ITfCategoryMgr::UnregisterGUIDDescription"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP GetGUIDDescription(REFGUID rguid, BSTR *pbstrDesc) michael@0: { michael@0: NS_NOTREACHED("ITfCategoryMgr::GetGUIDDescription"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP RegisterGUIDDWORD(REFCLSID rclsid, REFGUID rguid, DWORD dw) michael@0: { michael@0: NS_NOTREACHED("ITfCategoryMgr::RegisterGUIDDWORD"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP UnregisterGUIDDWORD(REFCLSID rclsid, REFGUID rguid) michael@0: { michael@0: NS_NOTREACHED("ITfCategoryMgr::UnregisterGUIDDWORD"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP GetGUIDDWORD(REFGUID rguid, DWORD *pdw) michael@0: { michael@0: NS_NOTREACHED("ITfCategoryMgr::GetGUIDDWORD"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP RegisterGUID(REFGUID rguid, TfGuidAtom *pguidatom) michael@0: { michael@0: NS_NOTREACHED("ITfCategoryMgr::RegisterGUID"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP GetGUID(TfGuidAtom guidatom, GUID *pguid) michael@0: { michael@0: if (guidatom == GUID_ATOM_COMPOSING_SELECTION_ATTR) { michael@0: *pguid = GUID_COMPOSING_SELECTION_ATTR; michael@0: return S_OK; michael@0: } michael@0: NS_NOTREACHED("ITfCategoryMgr::GetGUID"); michael@0: return E_FAIL; michael@0: } michael@0: michael@0: STDMETHODIMP IsEqualTfGuidAtom(TfGuidAtom guidatom, REFGUID rguid, michael@0: BOOL *pfEqual) michael@0: { michael@0: NS_NOTREACHED("ITfCategoryMgr::IsEqualTfGuidAtom"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: public: // ITfDisplayAttributeMgr michael@0: michael@0: STDMETHODIMP OnUpdateInfo() michael@0: { michael@0: NS_NOTREACHED("ITfDisplayAttributeMgr::OnUpdateInfo"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP EnumDisplayAttributeInfo(IEnumTfDisplayAttributeInfo **ppEnum) michael@0: { michael@0: NS_NOTREACHED("ITfDisplayAttributeMgr::EnumDisplayAttributeInfo"); michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP GetDisplayAttributeInfo(REFGUID guid, michael@0: ITfDisplayAttributeInfo **ppInfo, michael@0: CLSID *pclsidOwner) michael@0: { michael@0: NS_ENSURE_TRUE(ppInfo, E_INVALIDARG); michael@0: NS_ENSURE_TRUE(!pclsidOwner, E_INVALIDARG); michael@0: if (guid == GUID_COMPOSING_SELECTION_ATTR) { michael@0: (*ppInfo) = new TSFDispAttrInfoImpl(guid); michael@0: (*ppInfo)->AddRef(); michael@0: return S_OK; michael@0: } michael@0: NS_NOTREACHED("ITfDisplayAttributeMgr::GetDisplayAttributeInfo"); michael@0: return E_FAIL; michael@0: } michael@0: michael@0: public: michael@0: michael@0: ITextStoreACP* GetFocusedStore() michael@0: { michael@0: return mFocusedDocument ? mFocusedDocument->mStore : nullptr; michael@0: } michael@0: michael@0: TSFContextImpl* GetFocusedContext() michael@0: { michael@0: return mFocusedDocument ? mFocusedDocument->mContextBase : nullptr; michael@0: } michael@0: michael@0: TSFAttrPropImpl* GetFocusedAttrProp() michael@0: { michael@0: TSFContextImpl* context = GetFocusedContext(); michael@0: return context ? context->mAttrProp : nullptr; michael@0: } michael@0: michael@0: }; michael@0: michael@0: STDMETHODIMP michael@0: TSFContextImpl::OnLockGranted(DWORD dwLockFlags) michael@0: { michael@0: // If we have a test, run it michael@0: if (mDocMgr->mMgr->mTest && michael@0: !((*mDocMgr->mMgr->mTestApp).*(mDocMgr->mMgr->mTest))()) michael@0: return S_FALSE; michael@0: return S_OK; michael@0: } michael@0: michael@0: michael@0: STDMETHODIMP_(ULONG) michael@0: TSFDocumentMgrImpl::Release(void) michael@0: { michael@0: --mRefCnt; michael@0: if (mRefCnt == 1 && mMgr->mFocusedDocument == this) { michael@0: mMgr->mFocusedDocument = nullptr; michael@0: --mRefCnt; michael@0: } michael@0: if (mRefCnt) return mRefCnt; michael@0: delete this; michael@0: return 0; michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(TestApp, nsIWebProgressListener, michael@0: nsISupportsWeakReference) michael@0: michael@0: nsresult michael@0: TestApp::Run(void) michael@0: { michael@0: // Create a test window michael@0: // We need a full-fledged window to test for TSF functionality michael@0: nsresult rv; michael@0: mAppShell = do_GetService(kAppShellCID); michael@0: NS_ENSURE_TRUE(mAppShell, NS_ERROR_UNEXPECTED); michael@0: michael@0: nsCOMPtr appShellService( michael@0: do_GetService(NS_APPSHELLSERVICE_CONTRACTID)); michael@0: NS_ENSURE_TRUE(appShellService, NS_ERROR_UNEXPECTED); michael@0: michael@0: nsCOMPtr uri; michael@0: rv = NS_NewURI(getter_AddRefs(uri), "about:blank", nullptr); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = appShellService->CreateTopLevelWindow(nullptr, uri, michael@0: nsIWebBrowserChrome::CHROME_DEFAULT, michael@0: 800 /*nsIAppShellService::SIZE_TO_CONTENT*/, michael@0: 600 /*nsIAppShellService::SIZE_TO_CONTENT*/, michael@0: getter_AddRefs(mWindow)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsCOMPtr docShell; michael@0: rv = mWindow->GetDocShell(getter_AddRefs(docShell)); michael@0: NS_ENSURE_TRUE(docShell, NS_ERROR_UNEXPECTED); michael@0: nsCOMPtr progress(do_GetInterface(docShell)); michael@0: NS_ENSURE_TRUE(progress, NS_ERROR_UNEXPECTED); michael@0: rv = progress->AddProgressListener(this, michael@0: nsIWebProgress::NOTIFY_STATE_WINDOW | michael@0: nsIWebProgress::NOTIFY_STATUS); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: mAppShell->Run(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool michael@0: TestApp::CheckFailed(void) michael@0: { michael@0: // All windows should be closed by now michael@0: if (mMgr && !mMgr->mDeactivated) { michael@0: fail("TSF not terminated properly"); michael@0: mFailed = true; michael@0: } michael@0: mMgr = nullptr; michael@0: return mFailed; michael@0: } michael@0: michael@0: nsresult michael@0: TestApp::Init(void) michael@0: { michael@0: // Replace TSF manager pointer, category manager pointer and display michael@0: // attribute manager pointer. michael@0: nsCOMPtr baseWindow(do_QueryInterface(mWindow)); michael@0: NS_ENSURE_TRUE(baseWindow, NS_ERROR_UNEXPECTED); michael@0: nsCOMPtr widget; michael@0: nsresult rv = baseWindow->GetMainWidget(getter_AddRefs(widget)); michael@0: NS_ENSURE_TRUE(widget, NS_ERROR_UNEXPECTED); michael@0: michael@0: ITfThreadMgr **threadMgr = reinterpret_cast( michael@0: widget->GetNativeData(NS_NATIVE_TSF_THREAD_MGR)); michael@0: if (!threadMgr) { michael@0: fail("nsIWidget::GetNativeData(NS_NATIVE_TSF_THREAD_MGR) not supported"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: if (*threadMgr) { michael@0: (*threadMgr)->Deactivate(); michael@0: (*threadMgr)->Release(); michael@0: (*threadMgr) = nullptr; michael@0: } else { michael@0: // This is only for information. The test does not need TSF to run. michael@0: printf("TSF not initialized properly (TSF is not enabled/installed?)\n"); michael@0: } michael@0: michael@0: ITfCategoryMgr **catMgr = reinterpret_cast( michael@0: widget->GetNativeData(NS_NATIVE_TSF_CATEGORY_MGR)); michael@0: if (*catMgr) { michael@0: (*catMgr)->Release(); michael@0: (*catMgr) = nullptr; michael@0: } michael@0: ITfDisplayAttributeMgr **daMgr = reinterpret_cast( michael@0: widget->GetNativeData(NS_NATIVE_TSF_DISPLAY_ATTR_MGR)); michael@0: if (*daMgr) { michael@0: (*daMgr)->Release(); michael@0: (*daMgr) = nullptr; michael@0: } michael@0: michael@0: mMgr = new TSFMgrImpl(this); michael@0: if (!mMgr) { michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: (*threadMgr) = mMgr; michael@0: (*threadMgr)->AddRef(); michael@0: (*catMgr) = mMgr; michael@0: (*catMgr)->AddRef(); michael@0: (*daMgr) = mMgr; michael@0: (*daMgr)->AddRef(); michael@0: michael@0: // Apply the change michael@0: reinterpret_cast( michael@0: widget->GetNativeData(NS_NATIVE_TSF_THREAD_MGR)); michael@0: michael@0: // Create a couple of text boxes for testing michael@0: nsCOMPtr win(do_GetInterface(mWindow)); michael@0: NS_ENSURE_TRUE(win, NS_ERROR_UNEXPECTED); michael@0: nsCOMPtr document; michael@0: rv = win->GetDocument(getter_AddRefs(document)); michael@0: NS_ENSURE_TRUE(document, NS_ERROR_UNEXPECTED); michael@0: nsCOMPtr htmlDoc(do_QueryInterface(document)); michael@0: NS_ENSURE_TRUE(htmlDoc, NS_ERROR_UNEXPECTED); michael@0: nsCOMPtr htmlBody; michael@0: rv = htmlDoc->GetBody(getter_AddRefs(htmlBody)); michael@0: NS_ENSURE_TRUE(htmlBody, NS_ERROR_UNEXPECTED); michael@0: michael@0: nsCOMPtr form; michael@0: rv = htmlDoc->CreateElementNS( michael@0: NS_LITERAL_STRING("http://www.w3.org/1999/xhtml"), michael@0: NS_LITERAL_STRING("form"), michael@0: getter_AddRefs(form)); michael@0: nsCOMPtr elem; michael@0: rv = htmlDoc->CreateElementNS( michael@0: NS_LITERAL_STRING("http://www.w3.org/1999/xhtml"), michael@0: NS_LITERAL_STRING("input"), michael@0: getter_AddRefs(elem)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: elem->SetAttribute(NS_LITERAL_STRING("type"), michael@0: NS_LITERAL_STRING("text")); michael@0: mInput = do_QueryInterface(elem); michael@0: NS_ENSURE_TRUE(mInput, NS_ERROR_UNEXPECTED); michael@0: rv = htmlDoc->CreateElementNS( michael@0: NS_LITERAL_STRING("http://www.w3.org/1999/xhtml"), michael@0: NS_LITERAL_STRING("textarea"), michael@0: getter_AddRefs(elem)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: mTextArea = do_QueryInterface(elem); michael@0: NS_ENSURE_TRUE(mTextArea, NS_ERROR_UNEXPECTED); michael@0: rv = htmlDoc->CreateElementNS( michael@0: NS_LITERAL_STRING("http://www.w3.org/1999/xhtml"), michael@0: NS_LITERAL_STRING("input"), michael@0: getter_AddRefs(elem)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: elem->SetAttribute(NS_LITERAL_STRING("type"), michael@0: NS_LITERAL_STRING("button")); michael@0: mButton = do_QueryInterface(elem); michael@0: NS_ENSURE_TRUE(mButton, NS_ERROR_UNEXPECTED); michael@0: michael@0: nsCOMPtr node; michael@0: rv = form->AppendChild(mInput, getter_AddRefs(node)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: rv = form->AppendChild(mTextArea, getter_AddRefs(node)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: rv = form->AppendChild(mButton, getter_AddRefs(node)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: rv = htmlBody->AppendChild(form, getter_AddRefs(node)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // set a background color manually, michael@0: // otherwise the window might be transparent michael@0: static_cast(htmlBody)-> michael@0: SetBgColor(NS_LITERAL_STRING("white")); michael@0: michael@0: widget->Show(true); michael@0: widget->SetFocus(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: TestApp::Term(void) michael@0: { michael@0: mCurrentNode = nullptr; michael@0: mInput = nullptr; michael@0: mTextArea = nullptr; michael@0: mButton = nullptr; michael@0: michael@0: nsCOMPtr win(do_GetInterface(mWindow)); michael@0: if (win) michael@0: win->Close(); michael@0: win = nullptr; michael@0: mWindow = nullptr; michael@0: michael@0: if (mAppShell) michael@0: mAppShell->Exit(); michael@0: mAppShell = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool michael@0: TestApp::RunTest(test_type aTest, bool aLock) michael@0: { michael@0: bool succeeded; michael@0: if (aLock && mMgr && mMgr->GetFocusedStore()) { michael@0: mMgr->mTest = aTest; michael@0: HRESULT hr = E_FAIL; michael@0: mMgr->GetFocusedStore()->RequestLock(TS_LF_READWRITE | TS_LF_SYNC, &hr); michael@0: succeeded = hr == S_OK; michael@0: } else { michael@0: succeeded = (this->*aTest)(); michael@0: } michael@0: mFailed |= !succeeded; michael@0: return succeeded; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: TestApp::OnStateChange(nsIWebProgress *aWebProgress, michael@0: nsIRequest *aRequest, michael@0: uint32_t aStateFlags, michael@0: nsresult aStatus) michael@0: { michael@0: NS_ASSERTION(aStateFlags & nsIWebProgressListener::STATE_IS_WINDOW && michael@0: aStateFlags & nsIWebProgressListener::STATE_STOP, "wrong state"); michael@0: if (NS_SUCCEEDED(Init())) { michael@0: mCurrentNode = mTextArea; michael@0: mTextArea->Focus(); michael@0: michael@0: if (RunTest(&TestApp::TestEditMessages)) michael@0: passed("TestEditMessages"); michael@0: if (RunTest(&TestApp::TestScrollMessages)) michael@0: passed("TestScrollMessages"); michael@0: michael@0: if (RunTest(&TestApp::TestFocus, false)) michael@0: passed("TestFocus"); michael@0: michael@0: mCurrentNode = mInput; michael@0: mInput->Focus(); michael@0: if (mMgr->GetFocusedStore()) { michael@0: if (RunTest(&TestApp::TestClustering)) michael@0: passed("TestClustering"); michael@0: } else { michael@0: fail("no text store (clustering)"); michael@0: mFailed = true; michael@0: } michael@0: michael@0: printf("Testing TSF support in text input element...\n"); michael@0: mCurrentNode = mInput; michael@0: mTestString = NS_LITERAL_STRING( michael@0: "This is a test of the Text Services Framework implementation."); michael@0: mInput->SetValue(mTestString); michael@0: mInput->Focus(); michael@0: if (mMgr->GetFocusedStore()) { michael@0: if (RunTest(&TestApp::TestSelection)) michael@0: passed("TestSelection (input)"); michael@0: if (RunTest(&TestApp::TestText)) michael@0: passed("TestText (input)"); michael@0: if (RunTest(&TestApp::TestExtents)) michael@0: passed("TestExtents (input)"); michael@0: if (RunTest(&TestApp::TestComposition)) michael@0: passed("TestComposition (input)"); michael@0: if (RunTest(&TestApp::TestNotification, false)) michael@0: passed("TestNotification (input)"); michael@0: } else { michael@0: fail("no text store (input)"); michael@0: mFailed = true; michael@0: } michael@0: michael@0: printf("Testing TSF support in textarea element...\n"); michael@0: mCurrentNode = mTextArea; michael@0: mTestString = NS_LITERAL_STRING( michael@0: "This is a test of the\r\nText Services Framework\r\nimplementation."); michael@0: mTextArea->SetValue(mTestString); michael@0: mTextArea->Focus(); michael@0: if (mMgr->GetFocusedStore()) { michael@0: if (RunTest(&TestApp::TestSelection)) michael@0: passed("TestSelection (textarea)"); michael@0: if (RunTest(&TestApp::TestText)) michael@0: passed("TestText (textarea)"); michael@0: if (RunTest(&TestApp::TestExtents)) michael@0: passed("TestExtents (textarea)"); michael@0: if (RunTest(&TestApp::TestComposition)) michael@0: passed("TestComposition (textarea)"); michael@0: if (RunTest(&TestApp::TestNotification, false)) michael@0: passed("TestNotification (textarea)"); michael@0: } else { michael@0: fail("no text store (textarea)"); michael@0: mFailed = true; michael@0: } michael@0: } else { michael@0: fail("initialization"); michael@0: mFailed = true; michael@0: } michael@0: Term(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool michael@0: TestApp::TestFocus(void) michael@0: { michael@0: uint32_t focus = mMgr->mFocusCount; michael@0: nsresult rv; michael@0: michael@0: /* If these fail the cause is probably one or more of: michael@0: * - nsIMEStateManager::OnTextStateFocus not called by nsEventStateManager michael@0: * - nsIMEStateManager::OnTextStateBlur not called by nsEventStateManager michael@0: * - nsWindow::OnIMEFocusChange (nsIWidget) not called by nsIMEStateManager michael@0: * - nsTextStore::Create/Focus/Destroy not called by nsWindow michael@0: * - ITfThreadMgr::CreateDocumentMgr/SetFocus not called by nsTextStore michael@0: * - ITfDocumentMgr::CreateContext/Push not called by nsTextStore michael@0: */ michael@0: michael@0: rv = mInput->Focus(); michael@0: if (!(NS_SUCCEEDED(rv) && michael@0: mMgr->mFocusedDocument && michael@0: mMgr->mFocusCount - focus == 1 && michael@0: mMgr->GetFocusedStore())) { michael@0: fail("TestFocus: document focus was not set"); michael@0: return false; michael@0: } michael@0: michael@0: rv = mTextArea->Focus(); michael@0: if (!(NS_SUCCEEDED(rv) && michael@0: mMgr->mFocusedDocument && michael@0: mMgr->mFocusCount - focus == 2 && michael@0: mMgr->GetFocusedStore())) { michael@0: fail("TestFocus: document focus was not changed"); michael@0: return false; michael@0: } michael@0: michael@0: rv = mButton->Focus(); michael@0: if (!(NS_SUCCEEDED(rv) && michael@0: !mMgr->mFocusedDocument && michael@0: mMgr->mFocusCount - focus == 2 && michael@0: !mMgr->GetFocusedStore())) { michael@0: fail("TestFocus: document focus was changed"); michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: TestApp::TestClustering(void) michael@0: { michael@0: // Text for testing michael@0: const uint32_t STRING_LENGTH = 2; michael@0: char16_t string[3]; michael@0: string[0] = 'e'; michael@0: string[1] = 0x0301; // U+0301 'acute accent' michael@0: string[2] = nullptr; michael@0: michael@0: if (!mMgr->GetFocusedStore()) { michael@0: fail("TestClustering: GetFocusedStore returns null #1"); michael@0: return false; michael@0: } michael@0: michael@0: // Replace entire string with our string michael@0: TS_TEXTCHANGE textChange; michael@0: HRESULT hr = michael@0: mMgr->GetFocusedStore()->SetText(0, 0, -1, string, STRING_LENGTH, michael@0: &textChange); michael@0: if (!(SUCCEEDED(hr) && michael@0: 0 == textChange.acpStart && michael@0: STRING_LENGTH == textChange.acpNewEnd)) { michael@0: fail("TestClustering: SetText"); michael@0: return false; michael@0: } michael@0: michael@0: TsViewCookie view; michael@0: RECT rectLetter, rectAccent, rectWhole, rectCombined; michael@0: BOOL clipped, nonEmpty; michael@0: michael@0: if (!mMgr->GetFocusedStore()) { michael@0: fail("TestClustering: GetFocusedStore returns null #2"); michael@0: return false; michael@0: } michael@0: michael@0: hr = mMgr->GetFocusedStore()->GetActiveView(&view); michael@0: if (!(SUCCEEDED(hr))) { michael@0: fail("TestClustering: GetActiveView"); michael@0: return false; michael@0: } michael@0: michael@0: if (!mMgr->GetFocusedStore()) { michael@0: fail("TestClustering: GetFocusedStore returns null #3"); michael@0: return false; michael@0: } michael@0: michael@0: // Get rect of first char (the letter) michael@0: hr = mMgr->GetFocusedStore()->GetTextExt(view, 0, STRING_LENGTH / 2, michael@0: &rectLetter, &clipped); michael@0: if (!(SUCCEEDED(hr))) { michael@0: fail("TestClustering: GetTextExt (letter)"); michael@0: return false; michael@0: } michael@0: michael@0: if (!mMgr->GetFocusedStore()) { michael@0: fail("TestClustering: GetFocusedStore returns null #4"); michael@0: return false; michael@0: } michael@0: michael@0: // Get rect of second char (the accent) michael@0: hr = mMgr->GetFocusedStore()->GetTextExt(view, STRING_LENGTH / 2, michael@0: STRING_LENGTH, michael@0: &rectAccent, &clipped); michael@0: if (!(SUCCEEDED(hr))) { michael@0: fail("TestClustering: GetTextExt (accent)"); michael@0: return false; michael@0: } michael@0: michael@0: if (!mMgr->GetFocusedStore()) { michael@0: fail("TestClustering: GetFocusedStore returns null #5"); michael@0: return false; michael@0: } michael@0: michael@0: // Get rect of combined char michael@0: hr = mMgr->GetFocusedStore()->GetTextExt(view, 0, STRING_LENGTH, michael@0: &rectWhole, &clipped); michael@0: if (!(SUCCEEDED(hr))) { michael@0: fail("TestClustering: GetTextExt (whole)"); michael@0: return false; michael@0: } michael@0: michael@0: nonEmpty = ::UnionRect(&rectCombined, &rectLetter, &rectAccent); michael@0: if (!(nonEmpty && michael@0: ::EqualRect(&rectCombined, &rectWhole))) { michael@0: fail("TestClustering: unexpected combined rect"); michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: TestApp::TestSelectionInternal(char* aTestName, michael@0: LONG aStart, michael@0: LONG aEnd, michael@0: TsActiveSelEnd aSelEnd) michael@0: { michael@0: bool succeeded = true, continueTest = true; michael@0: TS_SELECTION_ACP sel, testSel; michael@0: ULONG selFetched; michael@0: michael@0: if (!mMgr->GetFocusedStore()) { michael@0: fail("TestSelectionInternal: GetFocusedStore returns null #1"); michael@0: return false; michael@0: } michael@0: michael@0: sel.acpStart = aStart; michael@0: sel.acpEnd = aEnd; michael@0: sel.style.ase = aSelEnd; michael@0: sel.style.fInterimChar = FALSE; michael@0: HRESULT hr = mMgr->GetFocusedStore()->SetSelection(1, &sel); michael@0: if (!(SUCCEEDED(hr))) { michael@0: fail("TestSelection: SetSelection (%s)", aTestName); michael@0: continueTest = succeeded = false; michael@0: } michael@0: michael@0: if (continueTest) { michael@0: if (!mMgr->GetFocusedStore()) { michael@0: fail("TestSelectionInternal: GetFocusedStore returns null #2"); michael@0: return false; michael@0: } michael@0: michael@0: hr = mMgr->GetFocusedStore()->GetSelection(TS_DEFAULT_SELECTION, 1, michael@0: &testSel, &selFetched); michael@0: if (!(SUCCEEDED(hr) && michael@0: selFetched == 1 && michael@0: !memcmp(&sel, &testSel, sizeof(sel)))) { michael@0: fail("TestSelection: unexpected GetSelection result (%s)", aTestName); michael@0: succeeded = false; michael@0: } michael@0: } michael@0: return succeeded; michael@0: } michael@0: michael@0: bool michael@0: TestApp::TestSelection(void) michael@0: { michael@0: bool succeeded = true; michael@0: michael@0: /* If these fail the cause is probably one or more of: michael@0: * nsTextStore::GetSelection not sending NS_QUERY_SELECTED_TEXT michael@0: * NS_QUERY_SELECTED_TEXT not handled by ContentEventHandler michael@0: * Bug in NS_QUERY_SELECTED_TEXT handler michael@0: * nsTextStore::SetSelection not sending NS_SELECTION_SET michael@0: * NS_SELECTION_SET not handled by ContentEventHandler michael@0: * Bug in NS_SELECTION_SET handler michael@0: */ michael@0: michael@0: TS_SELECTION_ACP testSel; michael@0: ULONG selFetched; michael@0: michael@0: if (!mMgr->GetFocusedStore()) { michael@0: fail("TestSelection: GetFocusedStore returns null"); michael@0: return false; michael@0: } michael@0: michael@0: HRESULT hr = michael@0: mMgr->GetFocusedStore()->GetSelection(0, 1, &testSel, &selFetched); michael@0: if (!(SUCCEEDED(hr) && michael@0: selFetched == 1)) { michael@0: fail("TestSelection: GetSelection"); michael@0: succeeded = false; michael@0: } michael@0: michael@0: const LONG SELECTION1_START = 0; michael@0: const LONG SELECTION1_END = mTestString.Length(); michael@0: const TsActiveSelEnd SELECTION1_SELEND = TS_AE_END; michael@0: michael@0: if (!TestSelectionInternal("normal", michael@0: SELECTION1_START, michael@0: SELECTION1_END, michael@0: SELECTION1_SELEND)) { michael@0: succeeded = false; michael@0: } michael@0: michael@0: const LONG SELECTION2_START = mTestString.Length() / 2; michael@0: const LONG SELECTION2_END = SELECTION2_START; michael@0: const TsActiveSelEnd SELECTION2_SELEND = TS_AE_END; michael@0: michael@0: if (!TestSelectionInternal("collapsed", michael@0: SELECTION2_START, michael@0: SELECTION2_END, michael@0: SELECTION2_SELEND)) { michael@0: succeeded = false; michael@0: } michael@0: michael@0: const LONG SELECTION3_START = 12; michael@0: const LONG SELECTION3_END = mTestString.Length() - 20; michael@0: const TsActiveSelEnd SELECTION3_SELEND = TS_AE_START; michael@0: michael@0: if (!TestSelectionInternal("reversed", michael@0: SELECTION3_START, michael@0: SELECTION3_END, michael@0: SELECTION3_SELEND)) { michael@0: succeeded = false; michael@0: } michael@0: return succeeded; michael@0: } michael@0: michael@0: bool michael@0: TestApp::TestText(void) michael@0: { michael@0: const uint32_t BUFFER_SIZE = (0x100); michael@0: const uint32_t RUNINFO_SIZE = (0x10); michael@0: michael@0: bool succeeded = true, continueTest; michael@0: char16_t buffer[BUFFER_SIZE]; michael@0: TS_RUNINFO runInfo[RUNINFO_SIZE]; michael@0: ULONG bufferRet, runInfoRet; michael@0: LONG acpRet, acpCurrent; michael@0: TS_TEXTCHANGE textChange; michael@0: HRESULT hr; michael@0: michael@0: /* If these fail the cause is probably one or more of: michael@0: * nsTextStore::GetText not sending NS_QUERY_TEXT_CONTENT michael@0: * NS_QUERY_TEXT_CONTENT not handled by ContentEventHandler michael@0: * Bug in NS_QUERY_TEXT_CONTENT handler michael@0: * nsTextStore::SetText not calling SetSelection or InsertTextAtSelection michael@0: * Bug in SetSelection or InsertTextAtSelection michael@0: * NS_SELECTION_SET bug or NS_COMPOSITION_* / NS_TEXT_TEXT bug michael@0: */ michael@0: michael@0: if (!mMgr->GetFocusedStore()) { michael@0: fail("TestText: GetFocusedStore returns null #1"); michael@0: return false; michael@0: } michael@0: michael@0: // Get all text michael@0: hr = mMgr->GetFocusedStore()->GetText(0, -1, buffer, BUFFER_SIZE, &bufferRet, michael@0: runInfo, RUNINFO_SIZE, &runInfoRet, michael@0: &acpRet); michael@0: if (!(SUCCEEDED(hr) && michael@0: bufferRet <= mTestString.Length() && michael@0: !wcsncmp(mTestString.get(), buffer, bufferRet) && michael@0: acpRet == LONG(bufferRet) && michael@0: runInfoRet > 0)) { michael@0: fail("TestText: GetText 1"); michael@0: succeeded = false; michael@0: } michael@0: michael@0: michael@0: if (!mMgr->GetFocusedStore()) { michael@0: fail("TestText: GetFocusedStore returns null #2"); michael@0: return false; michael@0: } michael@0: michael@0: // Get text from GETTEXT2_START to GETTEXT2_END michael@0: const uint32_t GETTEXT2_START = (18); michael@0: const uint32_t GETTEXT2_END = (mTestString.Length() - 16); michael@0: const uint32_t GETTEXT2_BUFFER_SIZE = (0x10); michael@0: michael@0: hr = mMgr->GetFocusedStore()->GetText(GETTEXT2_START, GETTEXT2_END, michael@0: buffer, GETTEXT2_BUFFER_SIZE, michael@0: &bufferRet, runInfo, RUNINFO_SIZE, michael@0: &runInfoRet, &acpRet); michael@0: if (!(SUCCEEDED(hr) && michael@0: bufferRet <= GETTEXT2_BUFFER_SIZE && michael@0: !wcsncmp(mTestString.get() + GETTEXT2_START, buffer, bufferRet) && michael@0: acpRet == LONG(bufferRet) + GETTEXT2_START && michael@0: runInfoRet > 0)) { michael@0: fail("TestText: GetText 2"); michael@0: succeeded = false; michael@0: } michael@0: michael@0: michael@0: if (!mMgr->GetFocusedStore()) { michael@0: fail("TestText: GetFocusedStore returns null #3"); michael@0: return false; michael@0: } michael@0: michael@0: // Replace text from SETTEXT1_START to SETTEXT1_END with insertString michael@0: const uint32_t SETTEXT1_START = (8); michael@0: const uint32_t SETTEXT1_TAIL_LENGTH = (40); michael@0: const uint32_t SETTEXT1_END = (mTestString.Length() - michael@0: SETTEXT1_TAIL_LENGTH); michael@0: NS_NAMED_LITERAL_STRING(insertString, "(Inserted string)"); michael@0: michael@0: continueTest = true; michael@0: hr = mMgr->GetFocusedStore()->SetText(0, SETTEXT1_START, SETTEXT1_END, michael@0: insertString.get(), michael@0: insertString.Length(), &textChange); michael@0: if (!(SUCCEEDED(hr) && michael@0: textChange.acpStart == SETTEXT1_START && michael@0: textChange.acpOldEnd == LONG(SETTEXT1_END) && michael@0: textChange.acpNewEnd == LONG(SETTEXT1_START + michael@0: insertString.Length()))) { michael@0: fail("TestText: SetText 1"); michael@0: continueTest = succeeded = false; michael@0: } michael@0: michael@0: const uint32_t SETTEXT1_FINAL_LENGTH = (SETTEXT1_START + michael@0: SETTEXT1_TAIL_LENGTH + michael@0: insertString.Length()); michael@0: michael@0: if (continueTest) { michael@0: acpCurrent = 0; michael@0: while (acpCurrent < LONG(SETTEXT1_FINAL_LENGTH)) { michael@0: if (!mMgr->GetFocusedStore()) { michael@0: fail("TestText: GetFocusedStore returns null #4"); michael@0: return false; michael@0: } michael@0: michael@0: hr = mMgr->GetFocusedStore()->GetText(acpCurrent, -1, &buffer[acpCurrent], michael@0: BUFFER_SIZE, &bufferRet, runInfo, michael@0: RUNINFO_SIZE, &runInfoRet, &acpRet); michael@0: if (!(SUCCEEDED(hr) && michael@0: acpRet > acpCurrent && michael@0: bufferRet <= SETTEXT1_FINAL_LENGTH && michael@0: runInfoRet > 0)) { michael@0: fail("TestText: GetText failed after SetTest 1"); michael@0: continueTest = succeeded = false; michael@0: break; michael@0: } michael@0: acpCurrent = acpRet; michael@0: } michael@0: } michael@0: michael@0: if (continueTest) { michael@0: if (!(acpCurrent == LONG(SETTEXT1_FINAL_LENGTH) && michael@0: !wcsncmp(buffer, mTestString.get(), SETTEXT1_START) && michael@0: !wcsncmp(&buffer[SETTEXT1_START], insertString.get(), michael@0: insertString.Length()) && michael@0: !wcsncmp(&buffer[SETTEXT1_START + insertString.Length()], michael@0: mTestString.get() + SETTEXT1_END, SETTEXT1_TAIL_LENGTH))) { michael@0: fail("TestText: unexpected GetText result after SetText 1"); michael@0: succeeded = false; michael@0: } michael@0: } michael@0: michael@0: michael@0: if (!mMgr->GetFocusedStore()) { michael@0: fail("TestText: GetFocusedStore returns null #5"); michael@0: return false; michael@0: } michael@0: michael@0: // Restore entire text to original text (mTestString) michael@0: continueTest = true; michael@0: hr = mMgr->GetFocusedStore()->SetText(0, 0, -1, mTestString.get(), michael@0: mTestString.Length(), &textChange); michael@0: if (!(SUCCEEDED(hr) && michael@0: textChange.acpStart == 0 && michael@0: textChange.acpOldEnd == LONG(SETTEXT1_FINAL_LENGTH) && michael@0: textChange.acpNewEnd == LONG(mTestString.Length()))) { michael@0: fail("TestText: SetText 2"); michael@0: continueTest = succeeded = false; michael@0: } michael@0: michael@0: if (continueTest) { michael@0: acpCurrent = 0; michael@0: while (acpCurrent < LONG(mTestString.Length())) { michael@0: if (!mMgr->GetFocusedStore()) { michael@0: fail("TestText: GetFocusedStore returns null #6"); michael@0: return false; michael@0: } michael@0: michael@0: hr = mMgr->GetFocusedStore()->GetText(acpCurrent, -1, &buffer[acpCurrent], michael@0: BUFFER_SIZE, &bufferRet, runInfo, michael@0: RUNINFO_SIZE, &runInfoRet, &acpRet); michael@0: if (!(SUCCEEDED(hr) && michael@0: acpRet > acpCurrent && michael@0: bufferRet <= mTestString.Length() && michael@0: runInfoRet > 0)) { michael@0: fail("TestText: GetText failed after SetText 2"); michael@0: continueTest = succeeded = false; michael@0: break; michael@0: } michael@0: acpCurrent = acpRet; michael@0: } michael@0: } michael@0: michael@0: if (continueTest) { michael@0: if (!(acpCurrent == LONG(mTestString.Length()) && michael@0: !wcsncmp(buffer, mTestString.get(), mTestString.Length()))) { michael@0: fail("TestText: unexpected GetText result after SetText 2"); michael@0: succeeded = false; michael@0: } michael@0: } michael@0: return succeeded; michael@0: } michael@0: michael@0: bool michael@0: TestApp::TestExtents(void) michael@0: { michael@0: if (!mMgr->GetFocusedStore()) { michael@0: fail("TestExtents: GetFocusedStore returns null #1"); michael@0: return false; michael@0: } michael@0: michael@0: TS_SELECTION_ACP sel; michael@0: sel.acpStart = 0; michael@0: sel.acpEnd = 0; michael@0: sel.style.ase = TS_AE_END; michael@0: sel.style.fInterimChar = FALSE; michael@0: mMgr->GetFocusedStore()->SetSelection(1, &sel); michael@0: michael@0: nsCOMPtr selCon; michael@0: if (!(NS_SUCCEEDED(GetSelCon(getter_AddRefs(selCon))) && selCon)) { michael@0: fail("TestExtents: get nsISelectionController"); michael@0: return false; michael@0: } michael@0: selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL, michael@0: nsISelectionController::SELECTION_FOCUS_REGION, true); michael@0: michael@0: nsCOMPtr window(do_GetInterface(mWindow)); michael@0: if (!window) { michael@0: fail("TestExtents: get nsIDOMWindow"); michael@0: return false; michael@0: } michael@0: RECT windowRect, screenRect, textRect1, textRect2; michael@0: BOOL clipped; michael@0: int32_t val; michael@0: TsViewCookie view; michael@0: HRESULT hr; michael@0: michael@0: nsresult nsr = window->GetScreenX(&val); michael@0: windowRect.left = val; michael@0: nsr |= window->GetScreenY(&val); michael@0: windowRect.top = val; michael@0: nsr |= window->GetOuterWidth(&val); michael@0: windowRect.right = windowRect.left + val; michael@0: nsr |= window->GetOuterHeight(&val); michael@0: windowRect.bottom = windowRect.top + val; michael@0: if (!(NS_SUCCEEDED(nsr))) { michael@0: fail("TestExtents: get window rect failed"); michael@0: return false; michael@0: } michael@0: michael@0: if (!mMgr->GetFocusedStore()) { michael@0: fail("TestExtents: GetFocusedStore returns null #2"); michael@0: return false; michael@0: } michael@0: michael@0: hr = mMgr->GetFocusedStore()->GetActiveView(&view); michael@0: if (!(SUCCEEDED(hr))) { michael@0: fail("TestExtents: GetActiveView"); michael@0: return false; michael@0: } michael@0: michael@0: if (!mMgr->GetFocusedStore()) { michael@0: fail("TestExtents: GetFocusedStore returns null #3"); michael@0: return false; michael@0: } michael@0: michael@0: bool succeeded = true; michael@0: HWND hwnd; michael@0: hr = mMgr->GetFocusedStore()->GetWnd(view, &hwnd); michael@0: if (!(SUCCEEDED(hr) && michael@0: ::IsWindow(hwnd))) { michael@0: fail("TestExtents: GetWnd"); michael@0: succeeded = false; michael@0: } michael@0: michael@0: ::SetRectEmpty(&screenRect); michael@0: michael@0: if (!mMgr->GetFocusedStore()) { michael@0: fail("TestExtents: GetFocusedStore returns null #4"); michael@0: return false; michael@0: } michael@0: michael@0: hr = mMgr->GetFocusedStore()->GetScreenExt(view, &screenRect); michael@0: if (!(SUCCEEDED(hr) && michael@0: screenRect.left > windowRect.left && michael@0: screenRect.top > windowRect.top && michael@0: screenRect.right > screenRect.left && michael@0: screenRect.bottom > screenRect.top && michael@0: screenRect.right < windowRect.right && michael@0: screenRect.bottom < windowRect.bottom)) { michael@0: fail("TestExtents: GetScreenExt"); michael@0: succeeded = false; michael@0: } michael@0: michael@0: const LONG GETTEXTEXT1_START = 0; michael@0: const LONG GETTEXTEXT1_END = 0; michael@0: michael@0: ::SetRectEmpty(&textRect1); michael@0: michael@0: if (!mMgr->GetFocusedStore()) { michael@0: fail("TestExtents: GetFocusedStore returns null #5"); michael@0: return false; michael@0: } michael@0: michael@0: hr = mMgr->GetFocusedStore()->GetTextExt(view, GETTEXTEXT1_START, michael@0: GETTEXTEXT1_END, &textRect1, michael@0: &clipped); michael@0: if (!(SUCCEEDED(hr) && michael@0: textRect1.left >= screenRect.left && michael@0: textRect1.top >= screenRect.top && michael@0: textRect1.right < screenRect.right && michael@0: textRect1.bottom <= screenRect.bottom && michael@0: textRect1.right >= textRect1.left && michael@0: textRect1.bottom > textRect1.top)) { michael@0: fail("TestExtents: GetTextExt (offset %ld to %ld)", michael@0: GETTEXTEXT1_START, GETTEXTEXT1_END); michael@0: succeeded = false; michael@0: } michael@0: michael@0: const LONG GETTEXTEXT2_START = 10; michael@0: const LONG GETTEXTEXT2_END = 25; michael@0: michael@0: ::SetRectEmpty(&textRect2); michael@0: michael@0: if (!mMgr->GetFocusedStore()) { michael@0: fail("TestExtents: GetFocusedStore returns null #6"); michael@0: return false; michael@0: } michael@0: michael@0: hr = mMgr->GetFocusedStore()->GetTextExt(view, GETTEXTEXT2_START, michael@0: GETTEXTEXT2_END, &textRect2, michael@0: &clipped); michael@0: if (!(SUCCEEDED(hr) && michael@0: textRect2.left >= screenRect.left && michael@0: textRect2.top >= screenRect.top && michael@0: textRect2.right <= screenRect.right && michael@0: textRect2.bottom <= screenRect.bottom && michael@0: textRect2.right > textRect2.left && michael@0: textRect2.bottom > textRect2.top)) { michael@0: fail("TestExtents: GetTextExt (offset %ld to %ld)", michael@0: GETTEXTEXT2_START, GETTEXTEXT2_END); michael@0: succeeded = false; michael@0: } michael@0: michael@0: // Offsets must be between GETTEXTEXT2_START and GETTEXTEXT2_END michael@0: const LONG GETTEXTEXT3_START = 23; michael@0: const LONG GETTEXTEXT3_END = 23; michael@0: michael@0: ::SetRectEmpty(&textRect1); michael@0: michael@0: if (!mMgr->GetFocusedStore()) { michael@0: fail("TestExtents: GetFocusedStore returns null #7"); michael@0: return false; michael@0: } michael@0: michael@0: hr = mMgr->GetFocusedStore()->GetTextExt(view, GETTEXTEXT3_START, michael@0: GETTEXTEXT3_END, &textRect1, michael@0: &clipped); michael@0: // Rectangle must be entirely inside the previous rectangle, michael@0: // since GETTEXTEXT3_START and GETTEXTEXT3_END are between michael@0: // GETTEXTEXT2_START and GETTEXTEXT2_START michael@0: if (!(SUCCEEDED(hr) && ::IsRectEmpty(&textRect1) || michael@0: (textRect1.left >= textRect2.left && michael@0: textRect1.top >= textRect2.top && michael@0: textRect1.right <= textRect2.right && michael@0: textRect1.bottom <= textRect2.bottom && michael@0: textRect1.right >= textRect1.left && michael@0: textRect1.bottom > textRect1.top))) { michael@0: fail("TestExtents: GetTextExt (offset %ld to %ld)", michael@0: GETTEXTEXT3_START, GETTEXTEXT3_END); michael@0: succeeded = false; michael@0: } michael@0: return succeeded; michael@0: } michael@0: michael@0: bool michael@0: TestApp::TestCompositionSelectionAndText(char* aTestName, michael@0: LONG aExpectedSelStart, michael@0: LONG aExpectedSelEnd, michael@0: nsString& aReferenceString) michael@0: { michael@0: if (!mMgr->GetFocusedStore()) { michael@0: fail("TestCompositionSelectionAndText: GetFocusedStore returns null #1"); michael@0: return false; michael@0: } michael@0: michael@0: TS_SELECTION_ACP currentSel; michael@0: ULONG selFetched = 0; michael@0: HRESULT hr = mMgr->GetFocusedStore()->GetSelection(TF_DEFAULT_SELECTION, 1, michael@0: ¤tSel, &selFetched); michael@0: if (!(SUCCEEDED(hr) && michael@0: 1 == selFetched && michael@0: currentSel.acpStart == aExpectedSelStart && michael@0: currentSel.acpEnd == aExpectedSelEnd)) { michael@0: fail("TestComposition: GetSelection (%s)", aTestName); michael@0: return false; michael@0: } michael@0: michael@0: const uint32_t bufferSize = 0x100, runInfoSize = 0x10; michael@0: char16_t buffer[bufferSize]; michael@0: TS_RUNINFO runInfo[runInfoSize]; michael@0: ULONG bufferRet, runInfoRet; michael@0: LONG acpRet, acpCurrent = 0; michael@0: while (acpCurrent < LONG(aReferenceString.Length())) { michael@0: if (!mMgr->GetFocusedStore()) { michael@0: fail("TestCompositionSelectionAndText: GetFocusedStore returns null #2"); michael@0: return false; michael@0: } michael@0: michael@0: hr = mMgr->GetFocusedStore()->GetText(acpCurrent, aReferenceString.Length(), michael@0: &buffer[acpCurrent], bufferSize, michael@0: &bufferRet, runInfo, runInfoSize, michael@0: &runInfoRet, &acpRet); michael@0: if (!(SUCCEEDED(hr) && michael@0: acpRet > acpCurrent && michael@0: bufferRet <= aReferenceString.Length() && michael@0: runInfoRet > 0)) { michael@0: fail("TestComposition: GetText (%s)", aTestName); michael@0: return false; michael@0: } michael@0: acpCurrent = acpRet; michael@0: } michael@0: if (!(acpCurrent == aReferenceString.Length() && michael@0: !wcsncmp(buffer, aReferenceString.get(), aReferenceString.Length()))) { michael@0: fail("TestComposition: unexpected GetText result (%s)", aTestName); michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: TestApp::TestComposition(void) michael@0: { michael@0: if (!mMgr->GetFocusedStore()) { michael@0: fail("TestComposition: GetFocusedStore returns null #1"); michael@0: return false; michael@0: } michael@0: michael@0: nsRefPtr sink; michael@0: HRESULT hr = michael@0: mMgr->GetFocusedStore()->QueryInterface(IID_ITfContextOwnerCompositionSink, michael@0: getter_AddRefs(sink)); michael@0: if (!(SUCCEEDED(hr))) { michael@0: fail("TestComposition: QueryInterface"); michael@0: return false; michael@0: } michael@0: michael@0: const LONG PRECOMPOSITION_SEL_START = 2; michael@0: const LONG PRECOMPOSITION_SEL_END = PRECOMPOSITION_SEL_START; michael@0: const TsActiveSelEnd PRECOMPOSITION_SEL_SELEND = TS_AE_END; michael@0: michael@0: TS_SELECTION_ACP sel; michael@0: sel.acpStart = PRECOMPOSITION_SEL_START; michael@0: sel.acpEnd = PRECOMPOSITION_SEL_END; michael@0: sel.style.ase = PRECOMPOSITION_SEL_SELEND; michael@0: sel.style.fInterimChar = FALSE; michael@0: hr = mMgr->GetFocusedStore()->SetSelection(1, &sel); michael@0: if (!(SUCCEEDED(hr))) { michael@0: fail("TestComposition: SetSelection (pre-composition)"); michael@0: return false; michael@0: } michael@0: michael@0: if (!mMgr->GetFocusedStore()) { michael@0: fail("TestComposition: GetFocusedStore returns null #2"); michael@0: return false; michael@0: } michael@0: michael@0: TS_TEXTCHANGE textChange; michael@0: NS_NAMED_LITERAL_STRING(insertString1, "Compo1"); michael@0: hr = mMgr->GetFocusedStore()->InsertTextAtSelection(TF_IAS_NOQUERY, michael@0: insertString1.get(), michael@0: insertString1.Length(), michael@0: nullptr, nullptr, michael@0: &textChange); michael@0: if (!(SUCCEEDED(hr) && michael@0: sel.acpEnd == textChange.acpStart && michael@0: sel.acpEnd == textChange.acpOldEnd && michael@0: sel.acpEnd + insertString1.Length() == textChange.acpNewEnd)) { michael@0: fail("TestComposition: InsertTextAtSelection"); michael@0: return false; michael@0: } michael@0: sel.acpEnd = textChange.acpNewEnd; michael@0: michael@0: if (!mMgr->GetFocusedAttrProp()) { michael@0: fail("TestComposition: GetFocusedAttrProp returns null #1"); michael@0: return false; michael@0: } michael@0: mMgr->GetFocusedAttrProp()->mRanges.Clear(); michael@0: nsRefPtr range = michael@0: new TSFRangeImpl(textChange.acpStart, michael@0: textChange.acpNewEnd - textChange.acpOldEnd); michael@0: if (!mMgr->GetFocusedAttrProp()) { michael@0: fail("TestComposition: GetFocusedAttrProp returns null #2"); michael@0: return false; michael@0: } michael@0: mMgr->GetFocusedAttrProp()->mRanges.AppendElement(range); michael@0: michael@0: BOOL okay = FALSE; michael@0: hr = sink->OnStartComposition(mMgr->GetFocusedContext(), &okay); michael@0: if (!(SUCCEEDED(hr) && michael@0: okay)) { michael@0: fail("TestComposition: OnStartComposition"); michael@0: return false; michael@0: } michael@0: michael@0: michael@0: if (!mMgr->GetFocusedStore()) { michael@0: fail("TestComposition: GetFocusedStore returns null #3"); michael@0: return false; michael@0: } michael@0: michael@0: NS_NAMED_LITERAL_STRING(insertString2, "Composition2"); michael@0: hr = mMgr->GetFocusedStore()->SetText(0, range->mStart + range->mLength, michael@0: range->mStart + range->mLength, michael@0: insertString2.get(), michael@0: insertString2.Length(), michael@0: &textChange); michael@0: if (!(SUCCEEDED(hr) && michael@0: sel.acpEnd == textChange.acpStart && michael@0: sel.acpEnd == textChange.acpOldEnd && michael@0: sel.acpEnd + insertString2.Length() == textChange.acpNewEnd)) { michael@0: fail("TestComposition: SetText 1"); michael@0: return false; michael@0: } michael@0: sel.acpEnd = textChange.acpNewEnd; michael@0: range->mLength += textChange.acpNewEnd - textChange.acpOldEnd; michael@0: michael@0: michael@0: if (!mMgr->GetFocusedStore()) { michael@0: fail("TestComposition: GetFocusedStore returns null #4"); michael@0: return false; michael@0: } michael@0: michael@0: const LONG COMPOSITION3_TEXT_START_OFFSET = -8; // offset 8 from the end michael@0: const LONG COMPOSITION3_TEXT_END_OFFSET = 4; michael@0: michael@0: const LONG COMPOSITION3_TEXT_START = range->mStart + range->mLength + michael@0: COMPOSITION3_TEXT_START_OFFSET; michael@0: const LONG COMPOSITION3_TEXT_END = COMPOSITION3_TEXT_START + michael@0: COMPOSITION3_TEXT_END_OFFSET; michael@0: michael@0: NS_NAMED_LITERAL_STRING(insertString3, "Compo3"); michael@0: hr = mMgr->GetFocusedStore()->SetText(0, COMPOSITION3_TEXT_START, michael@0: COMPOSITION3_TEXT_END, michael@0: insertString3.get(), michael@0: insertString3.Length(), michael@0: &textChange); michael@0: if (!(SUCCEEDED(hr) && michael@0: sel.acpEnd + COMPOSITION3_TEXT_START_OFFSET == textChange.acpStart && michael@0: sel.acpEnd + COMPOSITION3_TEXT_START_OFFSET + michael@0: COMPOSITION3_TEXT_END_OFFSET == textChange.acpOldEnd && michael@0: sel.acpEnd + insertString3.Length() + COMPOSITION3_TEXT_START_OFFSET == michael@0: textChange.acpNewEnd)) { michael@0: fail("TestComposition: SetText 2"); michael@0: return false; michael@0: } michael@0: sel.acpEnd = textChange.acpNewEnd; michael@0: range->mLength += textChange.acpNewEnd - textChange.acpOldEnd; michael@0: michael@0: michael@0: nsString referenceString; michael@0: referenceString.Append(mTestString.get(), sel.acpStart); michael@0: referenceString.Append(insertString1); michael@0: referenceString.Append(insertString2.get(), michael@0: insertString2.Length() + COMPOSITION3_TEXT_START_OFFSET); michael@0: referenceString.Append(insertString3); michael@0: referenceString.Append(insertString2.get() + insertString2.Length() - michael@0: COMPOSITION3_TEXT_END_OFFSET, COMPOSITION3_TEXT_END_OFFSET); michael@0: referenceString.Append(mTestString.get() + sel.acpStart, michael@0: COMPOSITION3_TEXT_END_OFFSET); michael@0: michael@0: if (!TestCompositionSelectionAndText("composition", michael@0: sel.acpEnd, sel.acpEnd, michael@0: referenceString)) michael@0: return false; michael@0: michael@0: michael@0: if (!mMgr->GetFocusedStore()) { michael@0: fail("TestComposition: GetFocusedStore returns null #5"); michael@0: return false; michael@0: } michael@0: michael@0: const LONG POSTCOMPOSITION_SEL_START = sel.acpEnd - 8; michael@0: const LONG POSTCOMPOSITION_SEL_END = POSTCOMPOSITION_SEL_START + 2; michael@0: michael@0: sel.acpStart = POSTCOMPOSITION_SEL_START; michael@0: sel.acpEnd = POSTCOMPOSITION_SEL_END; michael@0: hr = mMgr->GetFocusedStore()->SetSelection(1, &sel); michael@0: if (!(SUCCEEDED(hr))) { michael@0: fail("TestComposition: SetSelection (composition)"); michael@0: return false; michael@0: } michael@0: michael@0: if (!mMgr->GetFocusedAttrProp()) { michael@0: fail("TestComposition: GetFocusedAttrProp returns null #3"); michael@0: return false; michael@0: } michael@0: mMgr->GetFocusedAttrProp()->mRanges.Clear(); michael@0: michael@0: hr = sink->OnEndComposition(mMgr->GetFocusedContext()); michael@0: if (!(SUCCEEDED(hr))) { michael@0: fail("TestComposition: OnEndComposition"); michael@0: return false; michael@0: } michael@0: michael@0: if (!TestCompositionSelectionAndText("post-composition", michael@0: sel.acpStart, sel.acpEnd, michael@0: referenceString)) michael@0: return false; michael@0: michael@0: const LONG EMPTYCOMPOSITION_START = range->mStart + 2; michael@0: const LONG EMPTYCOMPOSITION_LENGTH = range->mLength - 4; michael@0: michael@0: range->mStart = EMPTYCOMPOSITION_START; michael@0: range->mLength = EMPTYCOMPOSITION_LENGTH; michael@0: if (!mMgr->GetFocusedAttrProp()) { michael@0: fail("TestComposition: GetFocusedAttrProp returns null #4"); michael@0: return false; michael@0: } michael@0: mMgr->GetFocusedAttrProp()->mRanges.AppendElement(range); michael@0: michael@0: okay = FALSE; michael@0: hr = sink->OnStartComposition(mMgr->GetFocusedContext(), &okay); michael@0: if (!(SUCCEEDED(hr) && michael@0: okay)) { michael@0: fail("TestComposition: OnStartComposition (empty composition)"); michael@0: return false; michael@0: } michael@0: michael@0: if (!mMgr->GetFocusedAttrProp()) { michael@0: fail("TestComposition: GetFocusedAttrProp returns null #5"); michael@0: return false; michael@0: } michael@0: mMgr->GetFocusedAttrProp()->mRanges.Clear(); michael@0: michael@0: hr = sink->OnEndComposition(mMgr->GetFocusedContext()); michael@0: if (!(SUCCEEDED(hr))) { michael@0: fail("TestComposition: OnEndComposition (empty composition)"); michael@0: return false; michael@0: } michael@0: michael@0: if (!TestCompositionSelectionAndText("empty composition", michael@0: range->mStart, range->mStart + range->mLength, michael@0: referenceString)) michael@0: return false; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: TestApp::TestNotificationTextChange(nsIWidget* aWidget, michael@0: uint32_t aCode, michael@0: const nsAString& aCharacter, michael@0: LONG aStart, michael@0: LONG aOldEnd, michael@0: LONG aNewEnd) michael@0: { michael@0: MSG msg; michael@0: if (::PeekMessageW(&msg, nullptr, WM_USER_TSF_TEXTCHANGE, michael@0: WM_USER_TSF_TEXTCHANGE, PM_REMOVE)) michael@0: ::DispatchMessageW(&msg); michael@0: if (!mMgr->GetFocusedContext()) { michael@0: fail("TestNotificationTextChange: GetFocusedContext returns null"); michael@0: return false; michael@0: } michael@0: mMgr->GetFocusedContext()->mTextChanged = false; michael@0: nsresult nsr = aWidget->SynthesizeNativeKeyEvent(0, aCode, 0, michael@0: aCharacter, aCharacter); michael@0: if (::PeekMessageW(&msg, nullptr, WM_USER_TSF_TEXTCHANGE, michael@0: WM_USER_TSF_TEXTCHANGE, PM_REMOVE)) michael@0: ::DispatchMessageW(&msg); michael@0: return NS_SUCCEEDED(nsr) && michael@0: mMgr->GetFocusedContext()->mTextChanged && michael@0: aStart == mMgr->GetFocusedContext()->mTextChangeData.acpStart && michael@0: aOldEnd == mMgr->GetFocusedContext()->mTextChangeData.acpOldEnd && michael@0: aNewEnd == mMgr->GetFocusedContext()->mTextChangeData.acpNewEnd; michael@0: } michael@0: michael@0: bool michael@0: TestApp::TestNotification(void) michael@0: { michael@0: nsresult nsr; michael@0: // get selection to test notification support michael@0: nsCOMPtr selCon; michael@0: if (!(NS_SUCCEEDED(GetSelCon(getter_AddRefs(selCon))) && selCon)) { michael@0: fail("TestNotification: get nsISelectionController"); michael@0: return false; michael@0: } michael@0: michael@0: nsr = selCon->CompleteMove(false, false); michael@0: if (!(NS_SUCCEEDED(nsr))) { michael@0: fail("TestNotification: CompleteMove"); michael@0: return false; michael@0: } michael@0: michael@0: if (!mMgr->GetFocusedContext()) { michael@0: fail("TestNotification: GetFocusedContext returns null #1"); michael@0: return false; michael@0: } michael@0: michael@0: mMgr->GetFocusedContext()->mSelChanged = false; michael@0: nsr = selCon->CharacterMove(true, false); michael@0: if (!(NS_SUCCEEDED(nsr) && michael@0: mMgr->GetFocusedContext()->mSelChanged)) { michael@0: fail("TestNotification: CharacterMove"); michael@0: return false; michael@0: } michael@0: michael@0: if (!mMgr->GetFocusedContext()) { michael@0: fail("TestNotification: GetFocusedContext returns null #2"); michael@0: return false; michael@0: } michael@0: michael@0: mMgr->GetFocusedContext()->mSelChanged = false; michael@0: nsr = selCon->CharacterMove(true, true); michael@0: if (!(NS_SUCCEEDED(nsr) && michael@0: mMgr->GetFocusedContext()->mSelChanged)) { michael@0: fail("TestNotification: CharacterMove (extend)"); michael@0: return false; michael@0: } michael@0: michael@0: nsCOMPtr widget; michael@0: if (!GetWidget(getter_AddRefs(widget))) { michael@0: fail("TestNotification: get nsIWidget"); michael@0: return false; michael@0: } michael@0: michael@0: NS_NAMED_LITERAL_STRING(character, ""); michael@0: NS_NAMED_LITERAL_STRING(characterA, "A"); michael@0: michael@0: // The selection test code above placed the selection at offset 1 to 2 michael@0: const LONG TEXTCHANGE1_START = 1; michael@0: const LONG TEXTCHANGE1_OLDEND = 2; michael@0: const LONG TEXTCHANGE1_NEWEND = 2; michael@0: michael@0: // replace single selected character with 'A' michael@0: if (!TestNotificationTextChange(widget, 'A', characterA, michael@0: TEXTCHANGE1_START, TEXTCHANGE1_OLDEND, TEXTCHANGE1_NEWEND)) { michael@0: fail("TestNotification: text change 1"); michael@0: return false; michael@0: } michael@0: michael@0: const LONG TEXTCHANGE2_START = TEXTCHANGE1_NEWEND; michael@0: const LONG TEXTCHANGE2_OLDEND = TEXTCHANGE1_NEWEND; michael@0: const LONG TEXTCHANGE2_NEWEND = TEXTCHANGE1_NEWEND + 1; michael@0: michael@0: // insert 'A' michael@0: if (!TestNotificationTextChange(widget, 'A', characterA, michael@0: TEXTCHANGE2_START, TEXTCHANGE2_OLDEND, TEXTCHANGE2_NEWEND)) { michael@0: fail("TestNotification: text change 2"); michael@0: return false; michael@0: } michael@0: michael@0: const LONG TEXTCHANGE3_START = TEXTCHANGE2_NEWEND - 1; michael@0: const LONG TEXTCHANGE3_OLDEND = TEXTCHANGE2_NEWEND; michael@0: const LONG TEXTCHANGE3_NEWEND = TEXTCHANGE2_NEWEND - 1; michael@0: michael@0: // backspace michael@0: if (!TestNotificationTextChange(widget, '\b', character, michael@0: TEXTCHANGE3_START, TEXTCHANGE3_OLDEND, TEXTCHANGE3_NEWEND)) { michael@0: fail("TestNotification: text change 3"); michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: TestApp::TestEditMessages(void) michael@0: { michael@0: mTestString = NS_LITERAL_STRING( michael@0: "This is a test of\nthe native editing command messages"); michael@0: // 0123456789012345678901 2345678901234567890123456789012 michael@0: // 0 1 2 3 4 5 michael@0: michael@0: // The native text string is increased by converting \n to \r\n. michael@0: uint32_t testStringLength = mTestString.Length() + 1; michael@0: michael@0: mTextArea->SetValue(mTestString); michael@0: mTextArea->Focus(); michael@0: michael@0: nsCOMPtr widget; michael@0: if (!GetWidget(getter_AddRefs(widget))) { michael@0: fail("TestEditMessages: get nsIWidget"); michael@0: return false; michael@0: } michael@0: michael@0: HWND wnd = (HWND)widget->GetNativeData(NS_NATIVE_WINDOW); michael@0: bool result = true; michael@0: michael@0: if (!::SendMessage(wnd, EM_CANUNDO, 0, 0)) { michael@0: fail("TestEditMessages: EM_CANUNDO"); michael@0: return false; michael@0: } michael@0: michael@0: if (::SendMessage(wnd, EM_CANREDO, 0, 0)) { michael@0: fail("TestEditMessages: EM_CANREDO #1"); michael@0: return false; michael@0: } michael@0: michael@0: michael@0: if (!::SendMessage(wnd, EM_UNDO, 0, 0)) { michael@0: fail("TestEditMessages: EM_UNDO #1"); michael@0: return false; michael@0: } michael@0: michael@0: nsAutoString str; michael@0: mTextArea->GetValue(str); michael@0: if (str == mTestString) { michael@0: fail("TestEditMessage: EM_UNDO #1, failed to execute"); michael@0: printf("Current Str: \"%s\"\n", NS_ConvertUTF16toUTF8(str).get()); michael@0: return false; michael@0: } michael@0: michael@0: if (!::SendMessage(wnd, EM_CANREDO, 0, 0)) { michael@0: fail("TestEditMessages: EM_CANREDO #2"); michael@0: return false; michael@0: } michael@0: michael@0: if (!::SendMessage(wnd, EM_REDO, 0, 0)) { michael@0: fail("TestEditMessages: EM_REDO #1"); michael@0: return false; michael@0: } michael@0: michael@0: mTextArea->GetValue(str); michael@0: if (str != mTestString) { michael@0: fail("TestEditMessage: EM_REDO #1, failed to execute"); michael@0: printf("Current Str: \"%s\"\n", NS_ConvertUTF16toUTF8(str).get()); michael@0: return false; michael@0: } michael@0: michael@0: TS_SELECTION_ACP sel; michael@0: HRESULT hr; michael@0: michael@0: sel.acpStart = 0; michael@0: sel.acpEnd = testStringLength; michael@0: sel.style.ase = TS_AE_END; michael@0: sel.style.fInterimChar = FALSE; michael@0: hr = mMgr->GetFocusedStore()->SetSelection(1, &sel); michael@0: if (!(SUCCEEDED(hr))) { michael@0: fail("TestEditMessages: SetSelection #1"); michael@0: return false; michael@0: } michael@0: michael@0: ::SendMessage(wnd, WM_CUT, 0, 0); michael@0: mTextArea->GetValue(str); michael@0: if (!str.IsEmpty()) { michael@0: fail("TestEditMessages: WM_CUT"); michael@0: printf("Current Str: \"%s\"\n", NS_ConvertUTF16toUTF8(str).get()); michael@0: return false; michael@0: } michael@0: michael@0: ::SendMessage(wnd, WM_PASTE, 0, 0); michael@0: mTextArea->GetValue(str); michael@0: if (str != mTestString) { michael@0: fail("TestEditMessages: WM_PASTE #1"); michael@0: printf("Current Str: \"%s\"\n", NS_ConvertUTF16toUTF8(str).get()); michael@0: return false; michael@0: } michael@0: michael@0: ::SendMessage(wnd, WM_PASTE, 0, 0); michael@0: mTextArea->GetValue(str); michael@0: nsAutoString expectedStr(mTestString); michael@0: expectedStr += mTestString; michael@0: if (str != expectedStr) { michael@0: fail("TestEditMessages: WM_PASTE #2"); michael@0: printf("Current Str: \"%s\"\n", NS_ConvertUTF16toUTF8(str).get()); michael@0: return false; michael@0: } michael@0: michael@0: sel.acpStart = 0; michael@0: sel.acpEnd = testStringLength; michael@0: hr = mMgr->GetFocusedStore()->SetSelection(1, &sel); michael@0: if (!(SUCCEEDED(hr))) { michael@0: fail("TestEditMessages: SetSelection #2"); michael@0: return false; michael@0: } michael@0: michael@0: ::SendMessage(wnd, WM_CLEAR, 0, 0); michael@0: mTextArea->GetValue(str); michael@0: if (str != mTestString) { michael@0: fail("TestEditMessages: WM_CLEAR #1"); michael@0: printf("Current Str: \"%s\"\n", NS_ConvertUTF16toUTF8(str).get()); michael@0: return false; michael@0: } michael@0: michael@0: sel.acpStart = 4; michael@0: sel.acpEnd = testStringLength; michael@0: hr = mMgr->GetFocusedStore()->SetSelection(1, &sel); michael@0: if (!(SUCCEEDED(hr))) { michael@0: fail("TestEditMessages: SetSelection #3"); michael@0: return false; michael@0: } michael@0: michael@0: ::SendMessage(wnd, WM_COPY, 0, 0); michael@0: mTextArea->GetValue(str); michael@0: if (str != mTestString) { michael@0: fail("TestEditMessages: WM_COPY"); michael@0: printf("Current Str: \"%s\"\n", NS_ConvertUTF16toUTF8(str).get()); michael@0: return false; michael@0: } michael@0: michael@0: if (!::SendMessage(wnd, EM_CANPASTE, 0, 0)) { michael@0: fail("TestEditMessages: EM_CANPASTE #1"); michael@0: return false; michael@0: } michael@0: michael@0: if (!::SendMessage(wnd, EM_CANPASTE, CF_TEXT, 0)) { michael@0: fail("TestEditMessages: EM_CANPASTE #2"); michael@0: return false; michael@0: } michael@0: michael@0: if (!::SendMessage(wnd, EM_CANPASTE, CF_UNICODETEXT, 0)) { michael@0: fail("TestEditMessages: EM_CANPASTE #3"); michael@0: return false; michael@0: } michael@0: michael@0: ::SendMessage(wnd, WM_PASTE, 0, 0); michael@0: mTextArea->GetValue(str); michael@0: if (str != mTestString) { michael@0: fail("TestEditMessages: WM_PASTE #3"); michael@0: printf("Current Str: \"%s\"\n", NS_ConvertUTF16toUTF8(str).get()); michael@0: return false; michael@0: } michael@0: michael@0: sel.acpStart = 4; michael@0: sel.acpEnd = testStringLength; michael@0: hr = mMgr->GetFocusedStore()->SetSelection(1, &sel); michael@0: if (!(SUCCEEDED(hr))) { michael@0: fail("TestEditMessages: SetSelection #3"); michael@0: return false; michael@0: } michael@0: michael@0: ::SendMessage(wnd, WM_CLEAR, 0, 0); michael@0: mTextArea->GetValue(str); michael@0: if (str != NS_LITERAL_STRING("This")) { michael@0: fail("TestEditMessages: WM_CLEAR #2"); michael@0: printf("Current Str: \"%s\"\n", NS_ConvertUTF16toUTF8(str).get()); michael@0: return false; michael@0: } michael@0: michael@0: ::SendMessage(wnd, WM_PASTE, 0, 0); michael@0: mTextArea->GetValue(str); michael@0: if (str != mTestString) { michael@0: fail("TestEditMessages: WM_PASTE #4"); michael@0: printf("Current Str: \"%s\"\n", NS_ConvertUTF16toUTF8(str).get()); michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: TestApp::TestScrollMessages(void) michael@0: { michael@0: NS_NAMED_LITERAL_STRING(kLine, "ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss\n"); michael@0: mTestString.Truncate(); michael@0: for (uint32_t i = 0; i < 30; i++) { michael@0: mTestString.Append(kLine); michael@0: } michael@0: michael@0: mTextArea->SetAttribute(NS_LITERAL_STRING("style"), michael@0: NS_LITERAL_STRING("width:3em;height:3em;word-wrap:normal;")); michael@0: mTextArea->SetValue(mTestString); michael@0: mTextArea->Focus(); michael@0: michael@0: nsCOMPtr widget; michael@0: if (!GetWidget(getter_AddRefs(widget))) { michael@0: fail("TestScrollMessages: get nsIWidget"); michael@0: mTextArea->SetAttribute(NS_LITERAL_STRING("style"), EmptyString()); michael@0: return false; michael@0: } michael@0: michael@0: nsCOMPtr textArea = do_QueryInterface(mTextArea); michael@0: if (!textArea) { michael@0: fail("TestScrollMessages: get nsIDOMElement"); michael@0: mTextArea->SetAttribute(NS_LITERAL_STRING("style"), EmptyString()); michael@0: return false; michael@0: } michael@0: michael@0: #define DO_CHECK(aFailureCondition, aDescription) \ michael@0: if (aFailureCondition) { \ michael@0: nsAutoCString str(aDescription); \ michael@0: str.Append(": "); \ michael@0: str.Append(#aFailureCondition); \ michael@0: fail(str.get()); \ michael@0: mTextArea->SetAttribute(NS_LITERAL_STRING("style"), EmptyString()); \ michael@0: return false; \ michael@0: } michael@0: michael@0: HWND wnd = (HWND)widget->GetNativeData(NS_NATIVE_WINDOW); michael@0: michael@0: textArea->SetScrollTop(0); michael@0: textArea->SetScrollLeft(0); michael@0: michael@0: if (::SendMessage(wnd, WM_VSCROLL, SB_LINEDOWN, 0) != 0) { michael@0: fail("TestScrollMessages: SendMessage WM_VSCROLL #1"); michael@0: mTextArea->SetAttribute(NS_LITERAL_STRING("style"), EmptyString()); michael@0: return false; michael@0: } michael@0: michael@0: int32_t x, y, prevX, prevY; michael@0: textArea->GetScrollTop(&y); michael@0: textArea->GetScrollLeft(&x); michael@0: michael@0: DO_CHECK(x != 0, "TestScrollMessages: SendMessage WM_VSCROLL #1"); michael@0: DO_CHECK(y == 0, "TestScrollMessages: SendMessage WM_VSCROLL #1"); michael@0: michael@0: if (::SendMessage(wnd, WM_HSCROLL, SB_LINERIGHT, 0) != 0) { michael@0: fail("TestScrollMessages: SendMessage WM_HSCROLL #1"); michael@0: mTextArea->SetAttribute(NS_LITERAL_STRING("style"), EmptyString()); michael@0: return false; michael@0: } michael@0: michael@0: prevX = x; michael@0: prevY = y; michael@0: textArea->GetScrollTop(&y); michael@0: textArea->GetScrollLeft(&x); michael@0: michael@0: const int32_t kLineWidth = x; michael@0: const int32_t kLineHeight = y; michael@0: michael@0: DO_CHECK(x == 0, "TestScrollMessages: SendMessage WM_HSCROLL #1"); michael@0: DO_CHECK(y != prevY, "TestScrollMessages: SendMessage WM_HSCROLL #1"); michael@0: michael@0: if (::SendMessage(wnd, WM_VSCROLL, SB_LINEUP, 0) != 0) { michael@0: fail("TestScrollMessages: SendMessage WM_VSCROLL #2"); michael@0: mTextArea->SetAttribute(NS_LITERAL_STRING("style"), EmptyString()); michael@0: return false; michael@0: } michael@0: michael@0: prevX = x; michael@0: prevY = y; michael@0: textArea->GetScrollTop(&y); michael@0: textArea->GetScrollLeft(&x); michael@0: michael@0: DO_CHECK(x != prevX, "TestScrollMessages: SendMessage WM_VSCROLL #2"); michael@0: DO_CHECK(y != 0, "TestScrollMessages: SendMessage WM_VSCROLL #2"); michael@0: michael@0: if (::SendMessage(wnd, WM_HSCROLL, SB_LINELEFT, 0) != 0) { michael@0: fail("TestScrollMessages: SendMessage WM_HSCROLL #2"); michael@0: mTextArea->SetAttribute(NS_LITERAL_STRING("style"), EmptyString()); michael@0: return false; michael@0: } michael@0: michael@0: prevX = x; michael@0: prevY = y; michael@0: textArea->GetScrollTop(&y); michael@0: textArea->GetScrollLeft(&x); michael@0: michael@0: DO_CHECK(x != 0, "TestScrollMessages: SendMessage WM_HSCROLL #2"); michael@0: DO_CHECK(y != 0, "TestScrollMessages: SendMessage WM_HSCROLL #2"); michael@0: michael@0: if (::SendMessage(wnd, WM_VSCROLL, SB_PAGEDOWN, 0) != 0) { michael@0: fail("TestScrollMessages: SendMessage WM_VSCROLL #3"); michael@0: mTextArea->SetAttribute(NS_LITERAL_STRING("style"), EmptyString()); michael@0: return false; michael@0: } michael@0: michael@0: prevX = x; michael@0: prevY = y; michael@0: textArea->GetScrollTop(&y); michael@0: textArea->GetScrollLeft(&x); michael@0: michael@0: DO_CHECK(x != 0, "TestScrollMessages: SendMessage WM_VSCROLL #3"); michael@0: DO_CHECK(y <= kLineHeight, "TestScrollMessages: SendMessage WM_VSCROLL #3"); michael@0: michael@0: if (::SendMessage(wnd, WM_HSCROLL, SB_PAGERIGHT, 0) != 0) { michael@0: fail("TestScrollMessages: SendMessage WM_HSCROLL #3"); michael@0: mTextArea->SetAttribute(NS_LITERAL_STRING("style"), EmptyString()); michael@0: return false; michael@0: } michael@0: michael@0: prevX = x; michael@0: prevY = y; michael@0: textArea->GetScrollTop(&y); michael@0: textArea->GetScrollLeft(&x); michael@0: michael@0: DO_CHECK(x <= kLineWidth, "TestScrollMessages: SendMessage WM_HSCROLL #3"); michael@0: DO_CHECK(y != prevY, "TestScrollMessages: SendMessage WM_HSCROLL #3"); michael@0: michael@0: const int32_t kPageWidth = x; michael@0: const int32_t kPageHeight = y; michael@0: michael@0: ::SendMessage(wnd, WM_VSCROLL, SB_LINEDOWN, 0); michael@0: ::SendMessage(wnd, WM_VSCROLL, SB_LINEUP, 0); michael@0: ::SendMessage(wnd, WM_HSCROLL, SB_LINERIGHT, 0); michael@0: ::SendMessage(wnd, WM_HSCROLL, SB_LINELEFT, 0); michael@0: michael@0: prevX = x; michael@0: prevY = y; michael@0: textArea->GetScrollTop(&y); michael@0: textArea->GetScrollLeft(&x); michael@0: michael@0: DO_CHECK(x != prevX, "TestScrollMessages: SB_LINELEFT scrolled wrong amount"); michael@0: DO_CHECK(y != prevY, "TestScrollMessages: SB_LINEUP scrolled wrong amount"); michael@0: michael@0: if (::SendMessage(wnd, WM_VSCROLL, SB_PAGEUP, 0) != 0) { michael@0: fail("TestScrollMessages: SendMessage WM_VSCROLL #4"); michael@0: mTextArea->SetAttribute(NS_LITERAL_STRING("style"), EmptyString()); michael@0: return false; michael@0: } michael@0: michael@0: prevX = x; michael@0: prevY = y; michael@0: textArea->GetScrollTop(&y); michael@0: textArea->GetScrollLeft(&x); michael@0: michael@0: DO_CHECK(x != prevX, "TestScrollMessages: SendMessage WM_VSCROLL #4"); michael@0: DO_CHECK(y != 0, "TestScrollMessages: SendMessage WM_VSCROLL #4"); michael@0: michael@0: if (::SendMessage(wnd, WM_HSCROLL, SB_PAGELEFT, 0) != 0) { michael@0: fail("TestScrollMessages: SendMessage WM_HSCROLL #4"); michael@0: mTextArea->SetAttribute(NS_LITERAL_STRING("style"), EmptyString()); michael@0: return false; michael@0: } michael@0: michael@0: prevX = x; michael@0: prevY = y; michael@0: textArea->GetScrollTop(&y); michael@0: textArea->GetScrollLeft(&x); michael@0: michael@0: DO_CHECK(x != 0, "TestScrollMessages: SendMessage WM_HSCROLL #4"); michael@0: DO_CHECK(y != 0, "TestScrollMessages: SendMessage WM_HSCROLL #4"); michael@0: michael@0: if (::SendMessage(wnd, WM_VSCROLL, SB_BOTTOM, 0) != 0) { michael@0: fail("TestScrollMessages: SendMessage WM_VSCROLL #5"); michael@0: mTextArea->SetAttribute(NS_LITERAL_STRING("style"), EmptyString()); michael@0: return false; michael@0: } michael@0: michael@0: prevX = x; michael@0: prevY = y; michael@0: textArea->GetScrollTop(&y); michael@0: textArea->GetScrollLeft(&x); michael@0: michael@0: DO_CHECK(x != 0, "TestScrollMessages: SendMessage WM_VSCROLL #5"); michael@0: DO_CHECK(y <= kPageHeight, "TestScrollMessages: SendMessage WM_VSCROLL #5"); michael@0: michael@0: if (::SendMessage(wnd, WM_HSCROLL, SB_RIGHT, 0) != 0) { michael@0: fail("TestScrollMessages: SendMessage WM_HSCROLL #6"); michael@0: mTextArea->SetAttribute(NS_LITERAL_STRING("style"), EmptyString()); michael@0: return false; michael@0: } michael@0: michael@0: prevX = x; michael@0: prevY = y; michael@0: textArea->GetScrollTop(&y); michael@0: textArea->GetScrollLeft(&x); michael@0: michael@0: DO_CHECK(x <= kPageWidth, "TestScrollMessages: SendMessage WM_HSCROLL #5"); michael@0: DO_CHECK(y != prevY, "TestScrollMessages: SendMessage WM_HSCROLL #5"); michael@0: michael@0: ::SendMessage(wnd, WM_VSCROLL, SB_LINEDOWN, 0); michael@0: ::SendMessage(wnd, WM_HSCROLL, SB_LINERIGHT, 0); michael@0: michael@0: prevX = x; michael@0: prevY = y; michael@0: textArea->GetScrollTop(&y); michael@0: textArea->GetScrollLeft(&x); michael@0: michael@0: DO_CHECK(x != prevX, "SB_RIGHT didn't scroll to right most"); michael@0: DO_CHECK(y != prevY, "SB_BOTTOM didn't scroll to bottom most"); michael@0: michael@0: ::SendMessage(wnd, WM_VSCROLL, SB_PAGEUP, 0); michael@0: ::SendMessage(wnd, WM_VSCROLL, SB_PAGEDOWN, 0); michael@0: ::SendMessage(wnd, WM_HSCROLL, SB_PAGELEFT, 0); michael@0: ::SendMessage(wnd, WM_HSCROLL, SB_PAGERIGHT, 0); michael@0: michael@0: prevX = x; michael@0: prevY = y; michael@0: textArea->GetScrollTop(&y); michael@0: textArea->GetScrollLeft(&x); michael@0: michael@0: DO_CHECK(x != prevX, "TestScrollMessages: SB_PAGELEFT scrolled wrong amount"); michael@0: DO_CHECK(y != prevY, "TestScrollMessages: SB_PAGEUP scrolled wrong amount"); michael@0: michael@0: if (::SendMessage(wnd, WM_VSCROLL, SB_TOP, 0) != 0) { michael@0: fail("TestScrollMessages: SendMessage WM_VSCROLL #6"); michael@0: mTextArea->SetAttribute(NS_LITERAL_STRING("style"), EmptyString()); michael@0: return false; michael@0: } michael@0: michael@0: prevX = x; michael@0: prevY = y; michael@0: textArea->GetScrollTop(&y); michael@0: textArea->GetScrollLeft(&x); michael@0: michael@0: DO_CHECK(x != prevX, "TestScrollMessages: SendMessage WM_VSCROLL #6"); michael@0: DO_CHECK(y != 0, "TestScrollMessages: SendMessage WM_VSCROLL #6"); michael@0: michael@0: if (::SendMessage(wnd, WM_HSCROLL, SB_LEFT, 0) != 0) { michael@0: fail("TestScrollMessages: SendMessage WM_HSCROLL #4"); michael@0: mTextArea->SetAttribute(NS_LITERAL_STRING("style"), EmptyString()); michael@0: return false; michael@0: } michael@0: michael@0: prevX = x; michael@0: prevY = y; michael@0: textArea->GetScrollTop(&y); michael@0: textArea->GetScrollLeft(&x); michael@0: michael@0: DO_CHECK(x != 0, "TestScrollMessages: SendMessage WM_HSCROLL #6"); michael@0: DO_CHECK(y != 0, "TestScrollMessages: SendMessage WM_HSCROLL #6"); michael@0: #undef DO_CHECK michael@0: michael@0: mTextArea->SetAttribute(NS_LITERAL_STRING("style"), EmptyString()); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: TestApp::GetWidget(nsIWidget** aWidget) michael@0: { michael@0: nsCOMPtr docShell; michael@0: nsresult rv = mWindow->GetDocShell(getter_AddRefs(docShell)); michael@0: if (NS_FAILED(rv) || !docShell) { michael@0: return false; michael@0: } michael@0: michael@0: nsCOMPtr presShell; michael@0: rv = docShell->GetPresShell(getter_AddRefs(presShell)); michael@0: if (NS_FAILED(rv) || !presShell) { michael@0: return false; michael@0: } michael@0: michael@0: nsRefPtr viewManager = presShell->GetViewManager(); michael@0: if (!viewManager) { michael@0: return false; michael@0: } michael@0: michael@0: rv = viewManager->GetRootWidget(aWidget); michael@0: return (NS_SUCCEEDED(rv) && aWidget); michael@0: } michael@0: michael@0: nsresult michael@0: TestApp::GetSelCon(nsISelectionController** aSelCon) michael@0: { michael@0: nsCOMPtr docShell; michael@0: nsresult nsr = mWindow->GetDocShell(getter_AddRefs(docShell)); michael@0: if (NS_SUCCEEDED(nsr) && docShell) { michael@0: nsCOMPtr presShell; michael@0: nsr = docShell->GetPresShell(getter_AddRefs(presShell)); michael@0: if (NS_SUCCEEDED(nsr) && presShell) { michael@0: nsIFrame* frame = michael@0: nsCOMPtr(do_QueryInterface(mCurrentNode))->GetPrimaryFrame(); michael@0: if (frame) { michael@0: nsPresContext* presContext = presShell->GetPresContext(); michael@0: if (presContext) { michael@0: nsr = frame->GetSelectionController(presContext, aSelCon); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: return nsr; michael@0: } michael@0: michael@0: int main(int argc, char** argv) michael@0: { michael@0: ScopedXPCOM xpcom("TestWinTSF (bug #88831)"); michael@0: if (xpcom.failed()) michael@0: return 1; michael@0: michael@0: nsRefPtr tests = new TestApp(); michael@0: if (!tests) michael@0: return 1; michael@0: michael@0: if (NS_FAILED(tests->Run())) { michael@0: fail("run failed"); michael@0: return 1; michael@0: } michael@0: return int(tests->CheckFailed()); michael@0: }