2009-02-10 23:56:51 +03:00
|
|
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
2012-05-21 15:12:37 +04:00
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
2009-02-10 23:56:51 +03:00
|
|
|
|
2015-07-24 08:07:39 +03:00
|
|
|
#ifndef TSFTextStore_h_
|
|
|
|
#define TSFTextStore_h_
|
2009-02-10 23:56:51 +03:00
|
|
|
|
2016-04-19 23:51:25 +03:00
|
|
|
#include "mozilla/RefPtr.h"
|
2009-02-10 23:56:51 +03:00
|
|
|
#include "nsString.h"
|
2009-02-24 09:36:20 +03:00
|
|
|
#include "nsCOMPtr.h"
|
2010-11-23 09:48:03 +03:00
|
|
|
#include "nsIWidget.h"
|
2013-01-07 19:18:11 +04:00
|
|
|
#include "nsWindowBase.h"
|
2014-07-11 21:09:59 +04:00
|
|
|
#include "WinUtils.h"
|
2012-06-14 22:28:06 +04:00
|
|
|
#include "mozilla/Attributes.h"
|
2014-09-02 04:27:24 +04:00
|
|
|
#include "mozilla/StaticPtr.h"
|
2016-03-16 07:47:48 +03:00
|
|
|
#include "mozilla/TextEventDispatcher.h"
|
2013-10-22 17:27:36 +04:00
|
|
|
#include "mozilla/TextRange.h"
|
2014-01-14 06:01:00 +04:00
|
|
|
#include "mozilla/WindowsVersion.h"
|
2009-02-10 23:56:51 +03:00
|
|
|
|
|
|
|
#include <msctf.h>
|
|
|
|
#include <textstor.h>
|
2013-01-30 14:45:42 +04:00
|
|
|
|
|
|
|
// GUID_PROP_INPUTSCOPE is declared in inputscope.h using INIT_GUID.
|
|
|
|
// With initguid.h, we get its instance instead of extern declaration.
|
|
|
|
#ifdef INPUTSCOPE_INIT_GUID
|
|
|
|
#include <initguid.h>
|
|
|
|
#endif
|
2014-08-13 07:55:59 +04:00
|
|
|
#ifdef TEXTATTRS_INIT_GUID
|
|
|
|
#include <tsattrs.h>
|
|
|
|
#endif
|
2013-01-30 14:45:42 +04:00
|
|
|
#include <inputscope.h>
|
2009-02-10 23:56:51 +03:00
|
|
|
|
2013-05-02 19:37:35 +04:00
|
|
|
// TSF InputScope, for earlier SDK 8
|
|
|
|
#define IS_SEARCH static_cast<InputScope>(50)
|
|
|
|
|
2009-02-10 23:56:51 +03:00
|
|
|
struct ITfThreadMgr;
|
|
|
|
struct ITfDocumentMgr;
|
2009-02-23 11:56:33 +03:00
|
|
|
struct ITfDisplayAttributeMgr;
|
|
|
|
struct ITfCategoryMgr;
|
2009-02-10 23:56:51 +03:00
|
|
|
class nsWindow;
|
|
|
|
|
2014-01-14 06:01:00 +04:00
|
|
|
namespace mozilla {
|
|
|
|
namespace widget {
|
2015-07-24 08:07:39 +03:00
|
|
|
|
2014-01-14 06:01:00 +04:00
|
|
|
struct MSGResult;
|
|
|
|
|
2009-02-10 23:56:51 +03:00
|
|
|
/*
|
|
|
|
* Text Services Framework text store
|
|
|
|
*/
|
|
|
|
|
2015-07-24 08:07:39 +03:00
|
|
|
class TSFTextStore final : public ITextStoreACP
|
|
|
|
, public ITfContextOwnerCompositionSink
|
|
|
|
, public ITfMouseTrackerACP
|
2009-02-10 23:56:51 +03:00
|
|
|
{
|
|
|
|
public: /*IUnknown*/
|
|
|
|
STDMETHODIMP QueryInterface(REFIID, void**);
|
2014-07-11 21:09:59 +04:00
|
|
|
|
2015-07-24 08:07:39 +03:00
|
|
|
NS_INLINE_DECL_IUNKNOWN_REFCOUNTING(TSFTextStore)
|
2009-02-10 23:56:51 +03:00
|
|
|
|
|
|
|
public: /*ITextStoreACP*/
|
|
|
|
STDMETHODIMP AdviseSink(REFIID, IUnknown*, DWORD);
|
|
|
|
STDMETHODIMP UnadviseSink(IUnknown*);
|
|
|
|
STDMETHODIMP RequestLock(DWORD, HRESULT*);
|
|
|
|
STDMETHODIMP GetStatus(TS_STATUS*);
|
|
|
|
STDMETHODIMP QueryInsert(LONG, LONG, ULONG, LONG*, LONG*);
|
|
|
|
STDMETHODIMP GetSelection(ULONG, ULONG, TS_SELECTION_ACP*, ULONG*);
|
|
|
|
STDMETHODIMP SetSelection(ULONG, const TS_SELECTION_ACP*);
|
|
|
|
STDMETHODIMP GetText(LONG, LONG, WCHAR*, ULONG, ULONG*, TS_RUNINFO*, ULONG,
|
|
|
|
ULONG*, LONG*);
|
|
|
|
STDMETHODIMP SetText(DWORD, LONG, LONG, const WCHAR*, ULONG, TS_TEXTCHANGE*);
|
|
|
|
STDMETHODIMP GetFormattedText(LONG, LONG, IDataObject**);
|
|
|
|
STDMETHODIMP GetEmbedded(LONG, REFGUID, REFIID, IUnknown**);
|
|
|
|
STDMETHODIMP QueryInsertEmbedded(const GUID*, const FORMATETC*, BOOL*);
|
|
|
|
STDMETHODIMP InsertEmbedded(DWORD, LONG, LONG, IDataObject*, TS_TEXTCHANGE*);
|
|
|
|
STDMETHODIMP RequestSupportedAttrs(DWORD, ULONG, const TS_ATTRID*);
|
|
|
|
STDMETHODIMP RequestAttrsAtPosition(LONG, ULONG, const TS_ATTRID*, DWORD);
|
|
|
|
STDMETHODIMP RequestAttrsTransitioningAtPosition(LONG, ULONG,
|
|
|
|
const TS_ATTRID*, DWORD);
|
|
|
|
STDMETHODIMP FindNextAttrTransition(LONG, LONG, ULONG, const TS_ATTRID*,
|
|
|
|
DWORD, LONG*, BOOL*, LONG*);
|
|
|
|
STDMETHODIMP RetrieveRequestedAttrs(ULONG, TS_ATTRVAL*, ULONG*);
|
|
|
|
STDMETHODIMP GetEndACP(LONG*);
|
|
|
|
STDMETHODIMP GetActiveView(TsViewCookie*);
|
|
|
|
STDMETHODIMP GetACPFromPoint(TsViewCookie, const POINT*, DWORD, LONG*);
|
|
|
|
STDMETHODIMP GetTextExt(TsViewCookie, LONG, LONG, RECT*, BOOL*);
|
|
|
|
STDMETHODIMP GetScreenExt(TsViewCookie, RECT*);
|
|
|
|
STDMETHODIMP GetWnd(TsViewCookie, HWND*);
|
|
|
|
STDMETHODIMP InsertTextAtSelection(DWORD, const WCHAR*, ULONG, LONG*, LONG*,
|
|
|
|
TS_TEXTCHANGE*);
|
|
|
|
STDMETHODIMP InsertEmbeddedAtSelection(DWORD, IDataObject*, LONG*, LONG*,
|
|
|
|
TS_TEXTCHANGE*);
|
|
|
|
|
|
|
|
public: /*ITfContextOwnerCompositionSink*/
|
|
|
|
STDMETHODIMP OnStartComposition(ITfCompositionView*, BOOL*);
|
|
|
|
STDMETHODIMP OnUpdateComposition(ITfCompositionView*, ITfRange*);
|
|
|
|
STDMETHODIMP OnEndComposition(ITfCompositionView*);
|
|
|
|
|
2014-08-29 14:08:42 +04:00
|
|
|
public: /*ITfMouseTrackerACP*/
|
|
|
|
STDMETHODIMP AdviseMouseSink(ITfRangeACP*, ITfMouseSink*, DWORD*);
|
|
|
|
STDMETHODIMP UnadviseMouseSink(DWORD);
|
|
|
|
|
2009-02-10 23:56:51 +03:00
|
|
|
public:
|
|
|
|
static void Initialize(void);
|
|
|
|
static void Terminate(void);
|
2013-03-18 08:41:24 +04:00
|
|
|
|
|
|
|
static bool ProcessRawKeyMessage(const MSG& aMsg);
|
2014-01-14 06:01:00 +04:00
|
|
|
static void ProcessMessage(nsWindowBase* aWindow, UINT aMessage,
|
|
|
|
WPARAM& aWParam, LPARAM& aLParam,
|
2015-07-24 08:07:39 +03:00
|
|
|
MSGResult& aResult);
|
2014-01-14 06:01:00 +04:00
|
|
|
|
2013-03-18 08:41:24 +04:00
|
|
|
|
2011-09-29 10:19:26 +04:00
|
|
|
static void SetIMEOpenState(bool);
|
|
|
|
static bool GetIMEOpenState(void);
|
2009-02-10 23:56:51 +03:00
|
|
|
|
2011-09-29 10:19:26 +04:00
|
|
|
static void CommitComposition(bool aDiscard)
|
2009-02-10 23:56:51 +03:00
|
|
|
{
|
2014-09-03 05:38:19 +04:00
|
|
|
NS_ASSERTION(IsInTSFMode(), "Not in TSF mode, shouldn't be called");
|
2016-04-06 20:17:05 +03:00
|
|
|
if (!sEnabledTextStore) {
|
|
|
|
return;
|
2014-09-03 05:38:19 +04:00
|
|
|
}
|
2016-04-12 13:37:18 +03:00
|
|
|
RefPtr<TSFTextStore> textStore(sEnabledTextStore);
|
2016-04-06 20:17:05 +03:00
|
|
|
textStore->CommitCompositionInternal(aDiscard);
|
2009-02-10 23:56:51 +03:00
|
|
|
}
|
|
|
|
|
2013-03-27 04:04:02 +04:00
|
|
|
static void SetInputContext(nsWindowBase* aWidget,
|
|
|
|
const InputContext& aContext,
|
|
|
|
const InputContextAction& aAction);
|
2009-02-10 23:56:51 +03:00
|
|
|
|
2013-01-07 19:18:11 +04:00
|
|
|
static nsresult OnFocusChange(bool aGotFocus,
|
|
|
|
nsWindowBase* aFocusedWidget,
|
2014-09-03 05:38:20 +04:00
|
|
|
const InputContext& aContext);
|
2014-02-18 04:00:15 +04:00
|
|
|
static nsresult OnTextChange(const IMENotification& aIMENotification)
|
2009-02-10 23:56:51 +03:00
|
|
|
{
|
2014-09-03 05:38:19 +04:00
|
|
|
NS_ASSERTION(IsInTSFMode(), "Not in TSF mode, shouldn't be called");
|
2016-04-06 20:17:05 +03:00
|
|
|
if (!sEnabledTextStore) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2016-04-12 13:37:18 +03:00
|
|
|
RefPtr<TSFTextStore> textStore(sEnabledTextStore);
|
2016-04-06 20:17:05 +03:00
|
|
|
return textStore->OnTextChangeInternal(aIMENotification);
|
2009-02-10 23:56:51 +03:00
|
|
|
}
|
|
|
|
|
2015-06-17 08:00:34 +03:00
|
|
|
static nsresult OnSelectionChange(const IMENotification& aIMENotification)
|
2009-02-10 23:56:51 +03:00
|
|
|
{
|
2014-09-03 05:38:19 +04:00
|
|
|
NS_ASSERTION(IsInTSFMode(), "Not in TSF mode, shouldn't be called");
|
2016-04-06 20:17:05 +03:00
|
|
|
if (!sEnabledTextStore) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2016-04-12 13:37:18 +03:00
|
|
|
RefPtr<TSFTextStore> textStore(sEnabledTextStore);
|
2016-04-06 20:17:05 +03:00
|
|
|
return textStore->OnSelectionChangeInternal(aIMENotification);
|
2009-02-10 23:56:51 +03:00
|
|
|
}
|
|
|
|
|
2014-02-28 11:45:16 +04:00
|
|
|
static nsresult OnLayoutChange()
|
|
|
|
{
|
2014-09-03 05:38:19 +04:00
|
|
|
NS_ASSERTION(IsInTSFMode(), "Not in TSF mode, shouldn't be called");
|
2016-04-06 20:17:05 +03:00
|
|
|
if (!sEnabledTextStore) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2016-04-12 13:37:18 +03:00
|
|
|
RefPtr<TSFTextStore> textStore(sEnabledTextStore);
|
2016-04-06 20:17:05 +03:00
|
|
|
return textStore->OnLayoutChangeInternal();
|
2014-02-28 11:45:16 +04:00
|
|
|
}
|
|
|
|
|
2015-06-17 08:00:33 +03:00
|
|
|
static nsresult OnUpdateComposition()
|
|
|
|
{
|
|
|
|
NS_ASSERTION(IsInTSFMode(), "Not in TSF mode, shouldn't be called");
|
2016-04-06 20:17:05 +03:00
|
|
|
if (!sEnabledTextStore) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2016-04-12 13:37:18 +03:00
|
|
|
RefPtr<TSFTextStore> textStore(sEnabledTextStore);
|
2016-04-06 20:17:05 +03:00
|
|
|
return textStore->OnUpdateCompositionInternal();
|
2015-06-17 08:00:33 +03:00
|
|
|
}
|
|
|
|
|
2014-08-29 14:08:43 +04:00
|
|
|
static nsresult OnMouseButtonEvent(const IMENotification& aIMENotification)
|
|
|
|
{
|
2014-09-03 05:38:19 +04:00
|
|
|
NS_ASSERTION(IsInTSFMode(), "Not in TSF mode, shouldn't be called");
|
2016-04-06 20:17:05 +03:00
|
|
|
if (!sEnabledTextStore) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2016-04-12 13:37:18 +03:00
|
|
|
RefPtr<TSFTextStore> textStore(sEnabledTextStore);
|
2016-04-06 20:17:05 +03:00
|
|
|
return textStore->OnMouseButtonEventInternal(aIMENotification);
|
2014-08-29 14:08:43 +04:00
|
|
|
}
|
|
|
|
|
2012-11-13 17:04:45 +04:00
|
|
|
static nsIMEUpdatePreference GetIMEUpdatePreference();
|
|
|
|
|
2009-02-23 11:56:33 +03:00
|
|
|
// Returns the address of the pointer so that the TSF automatic test can
|
|
|
|
// replace the system object with a custom implementation for testing.
|
2014-09-11 07:56:09 +04:00
|
|
|
// XXX TSF doesn't work now. Should we remove it?
|
2013-02-25 08:00:07 +04:00
|
|
|
static void* GetNativeData(uint32_t aDataType)
|
2009-02-10 23:56:51 +03:00
|
|
|
{
|
2013-02-25 08:00:07 +04:00
|
|
|
switch (aDataType) {
|
|
|
|
case NS_NATIVE_TSF_THREAD_MGR:
|
|
|
|
Initialize(); // Apply any previous changes
|
2014-09-11 07:56:09 +04:00
|
|
|
return static_cast<void*>(&sThreadMgr);
|
2013-02-25 08:00:07 +04:00
|
|
|
case NS_NATIVE_TSF_CATEGORY_MGR:
|
|
|
|
return static_cast<void*>(&sCategoryMgr);
|
|
|
|
case NS_NATIVE_TSF_DISPLAY_ATTR_MGR:
|
|
|
|
return static_cast<void*>(&sDisplayAttrMgr);
|
|
|
|
default:
|
|
|
|
return nullptr;
|
|
|
|
}
|
2009-02-23 11:56:33 +03:00
|
|
|
}
|
|
|
|
|
2013-03-18 08:41:24 +04:00
|
|
|
static ITfMessagePump* GetMessagePump()
|
|
|
|
{
|
|
|
|
return sMessagePump;
|
|
|
|
}
|
|
|
|
|
2014-09-03 05:38:19 +04:00
|
|
|
static void* GetThreadManager()
|
2013-02-25 08:00:06 +04:00
|
|
|
{
|
2014-09-11 07:56:09 +04:00
|
|
|
return static_cast<void*>(sThreadMgr);
|
2013-02-25 08:00:06 +04:00
|
|
|
}
|
|
|
|
|
2014-09-03 05:38:19 +04:00
|
|
|
static bool ThinksHavingFocus()
|
2013-02-25 08:00:07 +04:00
|
|
|
{
|
2014-09-02 04:27:24 +04:00
|
|
|
return (sEnabledTextStore && sEnabledTextStore->mContext);
|
2013-02-25 08:00:07 +04:00
|
|
|
}
|
|
|
|
|
2014-09-03 05:38:19 +04:00
|
|
|
static bool IsInTSFMode()
|
2013-02-25 08:00:05 +04:00
|
|
|
{
|
2014-09-11 07:56:09 +04:00
|
|
|
return sThreadMgr != nullptr;
|
2013-02-25 08:00:05 +04:00
|
|
|
}
|
|
|
|
|
2014-09-03 05:38:19 +04:00
|
|
|
static bool IsComposing()
|
2013-02-25 08:00:05 +04:00
|
|
|
{
|
2014-09-02 04:27:24 +04:00
|
|
|
return (sEnabledTextStore && sEnabledTextStore->mComposition.IsComposing());
|
2013-02-25 08:00:05 +04:00
|
|
|
}
|
|
|
|
|
2014-09-03 05:38:19 +04:00
|
|
|
static bool IsComposingOn(nsWindowBase* aWidget)
|
2013-02-25 08:00:05 +04:00
|
|
|
{
|
2014-09-02 04:27:24 +04:00
|
|
|
return (IsComposing() && sEnabledTextStore->mWidget == aWidget);
|
2013-02-25 08:00:05 +04:00
|
|
|
}
|
|
|
|
|
2014-09-03 05:38:20 +04:00
|
|
|
static bool IsIMM_IME();
|
2014-01-14 06:01:00 +04:00
|
|
|
|
2013-02-25 08:00:05 +04:00
|
|
|
#ifdef DEBUG
|
|
|
|
// Returns true when keyboard layout has IME (TIP).
|
|
|
|
static bool CurrentKeyboardLayoutHasIME();
|
|
|
|
#endif // #ifdef DEBUG
|
|
|
|
|
2009-02-10 23:56:51 +03:00
|
|
|
protected:
|
2015-07-24 08:07:39 +03:00
|
|
|
TSFTextStore();
|
|
|
|
~TSFTextStore();
|
2009-02-10 23:56:51 +03:00
|
|
|
|
2014-09-03 05:38:20 +04:00
|
|
|
static bool CreateAndSetFocus(nsWindowBase* aFocusedWidget,
|
|
|
|
const InputContext& aContext);
|
2013-03-27 04:04:02 +04:00
|
|
|
static void MarkContextAsKeyboardDisabled(ITfContext* aContext);
|
|
|
|
static void MarkContextAsEmpty(ITfContext* aContext);
|
|
|
|
|
2014-09-03 05:38:20 +04:00
|
|
|
bool Init(nsWindowBase* aWidget);
|
2016-06-08 13:12:07 +03:00
|
|
|
void Destroy();
|
2016-06-06 15:07:24 +03:00
|
|
|
void ReleaseTSFObjects();
|
2009-02-10 23:56:51 +03:00
|
|
|
|
2012-09-12 03:53:12 +04:00
|
|
|
bool IsReadLock(DWORD aLock) const
|
|
|
|
{
|
|
|
|
return (TS_LF_READ == (aLock & TS_LF_READ));
|
|
|
|
}
|
|
|
|
bool IsReadWriteLock(DWORD aLock) const
|
|
|
|
{
|
|
|
|
return (TS_LF_READWRITE == (aLock & TS_LF_READWRITE));
|
|
|
|
}
|
|
|
|
bool IsReadLocked() const { return IsReadLock(mLock); }
|
|
|
|
bool IsReadWriteLocked() const { return IsReadWriteLock(mLock); }
|
|
|
|
|
2014-01-31 06:17:24 +04:00
|
|
|
// This is called immediately after a call of OnLockGranted() of mSink.
|
|
|
|
// Note that mLock isn't cleared yet when this is called.
|
|
|
|
void DidLockGranted();
|
|
|
|
|
2015-07-24 08:07:39 +03:00
|
|
|
bool GetScreenExtInternal(RECT& aScreenExt);
|
2014-10-07 14:01:49 +04:00
|
|
|
// If aDispatchCompositionChangeEvent is true, this method will dispatch
|
|
|
|
// compositionchange event if this is called during IME composing.
|
|
|
|
// aDispatchCompositionChangeEvent should be true only when this is called
|
|
|
|
// from SetSelection. Because otherwise, the compositionchange event should
|
|
|
|
// not be sent from here.
|
2009-02-23 11:56:33 +03:00
|
|
|
HRESULT SetSelectionInternal(const TS_SELECTION_ACP*,
|
2014-10-07 14:01:49 +04:00
|
|
|
bool aDispatchCompositionChangeEvent = false);
|
2015-07-24 08:07:39 +03:00
|
|
|
bool InsertTextAtSelectionInternal(const nsAString& aInsertStr,
|
2012-09-14 08:51:00 +04:00
|
|
|
TS_TEXTCHANGE* aTextChange);
|
2011-09-29 10:19:26 +04:00
|
|
|
void CommitCompositionInternal(bool);
|
2014-02-18 04:00:15 +04:00
|
|
|
nsresult OnTextChangeInternal(const IMENotification& aIMENotification);
|
2015-06-17 08:00:34 +03:00
|
|
|
nsresult OnSelectionChangeInternal(const IMENotification& aIMENotification);
|
2014-08-29 14:08:43 +04:00
|
|
|
nsresult OnMouseButtonEventInternal(const IMENotification& aIMENotification);
|
2009-02-23 11:56:33 +03:00
|
|
|
HRESULT GetDisplayAttribute(ITfProperty* aProperty,
|
|
|
|
ITfRange* aRange,
|
|
|
|
TF_DISPLAYATTRIBUTE* aResult);
|
2013-03-13 07:01:30 +04:00
|
|
|
HRESULT RestartCompositionIfNecessary(ITfRange* pRangeNew = nullptr);
|
2014-08-28 08:42:25 +04:00
|
|
|
HRESULT RestartComposition(ITfCompositionView* aCompositionView,
|
|
|
|
ITfRange* aNewRange);
|
2013-03-13 07:01:29 +04:00
|
|
|
|
2013-03-13 07:01:30 +04:00
|
|
|
// Following methods record composing action(s) to mPendingActions.
|
|
|
|
// They will be flushed FlushPendingActions().
|
|
|
|
HRESULT RecordCompositionStartAction(ITfCompositionView* aCompositionView,
|
|
|
|
ITfRange* aRange,
|
|
|
|
bool aPreserveSelection);
|
2014-09-16 17:46:39 +04:00
|
|
|
HRESULT RecordCompositionStartAction(ITfCompositionView* aComposition,
|
|
|
|
LONG aStart,
|
|
|
|
LONG aLength,
|
|
|
|
bool aPreserveSelection);
|
2013-03-13 07:01:29 +04:00
|
|
|
HRESULT RecordCompositionUpdateAction();
|
2013-03-13 07:01:30 +04:00
|
|
|
HRESULT RecordCompositionEndAction();
|
2013-03-13 07:01:30 +04:00
|
|
|
|
2015-06-17 08:00:33 +03:00
|
|
|
// DispatchEvent() dispatches the event and if it may not be handled
|
|
|
|
// synchronously, this makes the instance not notify TSF of pending
|
|
|
|
// notifications until next notification from content.
|
2015-07-24 08:07:39 +03:00
|
|
|
void DispatchEvent(WidgetGUIEvent& aEvent);
|
2015-06-17 08:00:33 +03:00
|
|
|
void OnLayoutInformationAvaliable();
|
|
|
|
|
2013-03-13 07:01:30 +04:00
|
|
|
// FlushPendingActions() performs pending actions recorded in mPendingActions
|
|
|
|
// and clear it.
|
2013-03-13 07:01:29 +04:00
|
|
|
void FlushPendingActions();
|
2015-06-17 08:00:33 +03:00
|
|
|
// MaybeFlushPendingNotifications() performs pending notifications to TSF.
|
|
|
|
void MaybeFlushPendingNotifications();
|
2013-03-13 07:01:29 +04:00
|
|
|
|
2014-02-28 11:45:16 +04:00
|
|
|
nsresult OnLayoutChangeInternal();
|
2015-06-17 08:00:33 +03:00
|
|
|
nsresult OnUpdateCompositionInternal();
|
|
|
|
|
|
|
|
void NotifyTSFOfTextChange(const TS_TEXTCHANGE& aTextChange);
|
|
|
|
void NotifyTSFOfSelectionChange();
|
2015-12-29 19:05:46 +03:00
|
|
|
bool NotifyTSFOfLayoutChange();
|
|
|
|
void NotifyTSFOfLayoutChangeAgain();
|
2015-06-17 08:00:33 +03:00
|
|
|
|
2014-08-13 07:55:59 +04:00
|
|
|
HRESULT HandleRequestAttrs(DWORD aFlags,
|
|
|
|
ULONG aFilterCount,
|
|
|
|
const TS_ATTRID* aFilterAttrs);
|
2016-02-01 20:57:29 +03:00
|
|
|
void SetInputScope(const nsString& aHTMLInputType,
|
|
|
|
const nsString& aHTMLInputInputmode);
|
2009-02-10 23:56:51 +03:00
|
|
|
|
2014-01-31 06:17:24 +04:00
|
|
|
// Creates native caret over our caret. This method only works on desktop
|
|
|
|
// application. Otherwise, this does nothing.
|
|
|
|
void CreateNativeCaret();
|
2015-07-26 06:29:47 +03:00
|
|
|
// Destroys native caret if there is.
|
|
|
|
void MaybeDestroyNativeCaret();
|
2014-01-31 06:17:24 +04:00
|
|
|
|
2015-04-23 22:10:30 +03:00
|
|
|
// Holds the pointer to our current win32 widget
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<nsWindowBase> mWidget;
|
2016-03-16 07:47:48 +03:00
|
|
|
// mDispatcher is a helper class to dispatch composition events.
|
|
|
|
RefPtr<TextEventDispatcher> mDispatcher;
|
2009-02-10 23:56:51 +03:00
|
|
|
// Document manager for the currently focused editor
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<ITfDocumentMgr> mDocumentMgr;
|
2009-02-10 23:56:51 +03:00
|
|
|
// Edit cookie associated with the current editing context
|
|
|
|
DWORD mEditCookie;
|
|
|
|
// Editing context at the bottom of mDocumentMgr's context stack
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<ITfContext> mContext;
|
2009-02-10 23:56:51 +03:00
|
|
|
// Currently installed notification sink
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<ITextStoreACPSink> mSink;
|
2009-02-10 23:56:51 +03:00
|
|
|
// TS_AS_* mask of what events to notify
|
|
|
|
DWORD mSinkMask;
|
|
|
|
// 0 if not locked, otherwise TS_LF_* indicating the current lock
|
|
|
|
DWORD mLock;
|
|
|
|
// 0 if no lock is queued, otherwise TS_LF_* indicating the queue lock
|
|
|
|
DWORD mLockQueued;
|
2013-03-13 07:01:29 +04:00
|
|
|
|
2016-06-06 15:07:24 +03:00
|
|
|
uint32_t mHandlingKeyMessage;
|
|
|
|
void OnStartToHandleKeyMessage() { ++mHandlingKeyMessage; }
|
|
|
|
void OnEndHandlingKeyMessage()
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(mHandlingKeyMessage);
|
|
|
|
if (--mHandlingKeyMessage) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// If TSFTextStore instance is destroyed during handling key message(s),
|
|
|
|
// release all TSF objects when all nested key messages have been handled.
|
|
|
|
if (mDestroyed) {
|
|
|
|
ReleaseTSFObjects();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-21 19:28:04 +03:00
|
|
|
class Composition final
|
2013-03-13 07:01:29 +04:00
|
|
|
{
|
|
|
|
public:
|
2013-10-08 22:48:20 +04:00
|
|
|
// nullptr if no composition is active, otherwise the current composition
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<ITfCompositionView> mView;
|
2013-03-13 07:01:29 +04:00
|
|
|
|
|
|
|
// Current copy of the active composition string. Only mString is
|
|
|
|
// changed during a InsertTextAtSelection call if we have a composition.
|
|
|
|
// mString acts as a buffer until OnUpdateComposition is called
|
2015-09-11 15:21:27 +03:00
|
|
|
// and mString is flushed to editor through eCompositionChange.
|
2014-10-07 14:01:47 +04:00
|
|
|
// This way all changes are updated in batches to avoid
|
2013-03-13 07:01:29 +04:00
|
|
|
// inconsistencies/artifacts.
|
|
|
|
nsString mString;
|
|
|
|
|
2013-03-13 07:01:30 +04:00
|
|
|
// The start of the current active composition, in ACP offsets
|
2013-03-13 07:01:29 +04:00
|
|
|
LONG mStart;
|
|
|
|
|
2013-03-13 07:01:29 +04:00
|
|
|
bool IsComposing() const
|
|
|
|
{
|
|
|
|
return (mView != nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
LONG EndOffset() const
|
|
|
|
{
|
2013-03-13 07:01:30 +04:00
|
|
|
return mStart + static_cast<LONG>(mString.Length());
|
2013-03-13 07:01:29 +04:00
|
|
|
}
|
|
|
|
|
2013-03-13 07:01:29 +04:00
|
|
|
// Start() and End() updates the members for emulating the latest state.
|
|
|
|
// Unless flush the pending actions, this data never matches the actual
|
|
|
|
// content.
|
|
|
|
void Start(ITfCompositionView* aCompositionView,
|
|
|
|
LONG aCompositionStartOffset,
|
|
|
|
const nsAString& aCompositionString);
|
2013-03-13 07:01:29 +04:00
|
|
|
void End();
|
2013-03-13 07:01:29 +04:00
|
|
|
};
|
2013-03-13 07:01:29 +04:00
|
|
|
// While the document is locked, we cannot dispatch any events which cause
|
|
|
|
// DOM events since the DOM events' handlers may modify the locked document.
|
|
|
|
// However, even while the document is locked, TSF may queries us.
|
2015-07-24 08:07:39 +03:00
|
|
|
// For that, TSFTextStore modifies mComposition even while the document is
|
2013-03-13 07:01:29 +04:00
|
|
|
// locked. With mComposition, query methods can returns the text content
|
|
|
|
// information.
|
2013-03-13 07:01:29 +04:00
|
|
|
Composition mComposition;
|
|
|
|
|
2016-06-29 11:39:59 +03:00
|
|
|
/**
|
|
|
|
* IsComposingInContent() returns true if there is a composition in the
|
|
|
|
* focused editor and it's caused by native IME (either TIP of TSF or IME of
|
|
|
|
* IMM). I.e., returns true between eCompositionStart and
|
|
|
|
* eCompositionCommit(AsIs).
|
|
|
|
*/
|
|
|
|
bool IsComposingInContent() const;
|
|
|
|
|
2013-03-13 07:01:29 +04:00
|
|
|
class Selection
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
Selection() : mDirty(true) {}
|
|
|
|
|
|
|
|
bool IsDirty() const { return mDirty; };
|
|
|
|
void MarkDirty() { mDirty = true; }
|
|
|
|
|
|
|
|
TS_SELECTION_ACP& ACP()
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(!mDirty);
|
|
|
|
return mACP;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetSelection(const TS_SELECTION_ACP& aSelection)
|
|
|
|
{
|
|
|
|
mDirty = false;
|
|
|
|
mACP = aSelection;
|
|
|
|
// Selection end must be active in our editor.
|
|
|
|
if (mACP.style.ase != TS_AE_START) {
|
|
|
|
mACP.style.ase = TS_AE_END;
|
|
|
|
}
|
|
|
|
// We're not support interim char selection for now.
|
|
|
|
// XXX Probably, this is necessary for supporting South Asian languages.
|
|
|
|
mACP.style.fInterimChar = FALSE;
|
|
|
|
}
|
|
|
|
|
2016-05-11 12:18:51 +03:00
|
|
|
bool SetSelection(uint32_t aStart,
|
2015-07-24 08:07:39 +03:00
|
|
|
uint32_t aLength,
|
|
|
|
bool aReversed,
|
|
|
|
WritingMode aWritingMode)
|
2013-03-13 07:01:29 +04:00
|
|
|
{
|
2016-05-11 12:18:51 +03:00
|
|
|
bool changed = mDirty ||
|
|
|
|
mACP.acpStart != static_cast<LONG>(aStart) ||
|
|
|
|
mACP.acpEnd != static_cast<LONG>(aStart + aLength);
|
|
|
|
|
2013-03-13 07:01:29 +04:00
|
|
|
mDirty = false;
|
|
|
|
mACP.acpStart = static_cast<LONG>(aStart);
|
|
|
|
mACP.acpEnd = static_cast<LONG>(aStart + aLength);
|
|
|
|
mACP.style.ase = aReversed ? TS_AE_START : TS_AE_END;
|
|
|
|
mACP.style.fInterimChar = FALSE;
|
2015-02-10 17:00:02 +03:00
|
|
|
mWritingMode = aWritingMode;
|
2016-05-11 12:18:51 +03:00
|
|
|
|
|
|
|
return changed;
|
2013-03-13 07:01:29 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
bool IsCollapsed() const
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(!mDirty);
|
|
|
|
return (mACP.acpStart == mACP.acpEnd);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CollapseAt(uint32_t aOffset)
|
|
|
|
{
|
2015-02-10 17:00:02 +03:00
|
|
|
// XXX This does not update the selection's mWritingMode.
|
|
|
|
// If it is ever used to "collapse" to an entirely new location,
|
|
|
|
// we may need to fix that.
|
2013-03-13 07:01:29 +04:00
|
|
|
mDirty = false;
|
|
|
|
mACP.acpStart = mACP.acpEnd = static_cast<LONG>(aOffset);
|
|
|
|
mACP.style.ase = TS_AE_END;
|
|
|
|
mACP.style.fInterimChar = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
LONG MinOffset() const
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(!mDirty);
|
|
|
|
LONG min = std::min(mACP.acpStart, mACP.acpEnd);
|
|
|
|
MOZ_ASSERT(min >= 0);
|
|
|
|
return min;
|
|
|
|
}
|
|
|
|
|
|
|
|
LONG MaxOffset() const
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(!mDirty);
|
|
|
|
LONG max = std::max(mACP.acpStart, mACP.acpEnd);
|
|
|
|
MOZ_ASSERT(max >= 0);
|
|
|
|
return max;
|
|
|
|
}
|
|
|
|
|
|
|
|
LONG StartOffset() const
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(!mDirty);
|
|
|
|
MOZ_ASSERT(mACP.acpStart >= 0);
|
|
|
|
return mACP.acpStart;
|
|
|
|
}
|
|
|
|
|
|
|
|
LONG EndOffset() const
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(!mDirty);
|
|
|
|
MOZ_ASSERT(mACP.acpEnd >= 0);
|
|
|
|
return mACP.acpEnd;
|
|
|
|
}
|
|
|
|
|
|
|
|
LONG Length() const
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(!mDirty);
|
|
|
|
MOZ_ASSERT(mACP.acpEnd >= mACP.acpStart);
|
|
|
|
return std::abs(mACP.acpEnd - mACP.acpStart);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsReversed() const
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(!mDirty);
|
|
|
|
return (mACP.style.ase == TS_AE_START);
|
|
|
|
}
|
|
|
|
|
|
|
|
TsActiveSelEnd ActiveSelEnd() const
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(!mDirty);
|
|
|
|
return mACP.style.ase;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsInterimChar() const
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(!mDirty);
|
|
|
|
return (mACP.style.fInterimChar != FALSE);
|
|
|
|
}
|
|
|
|
|
2015-07-24 08:07:39 +03:00
|
|
|
WritingMode GetWritingMode() const
|
2015-02-10 17:00:02 +03:00
|
|
|
{
|
|
|
|
MOZ_ASSERT(!mDirty);
|
|
|
|
return mWritingMode;
|
|
|
|
}
|
|
|
|
|
2013-03-13 07:01:29 +04:00
|
|
|
private:
|
|
|
|
TS_SELECTION_ACP mACP;
|
2015-07-24 08:07:39 +03:00
|
|
|
WritingMode mWritingMode;
|
2013-03-13 07:01:29 +04:00
|
|
|
bool mDirty;
|
|
|
|
};
|
|
|
|
// Don't access mSelection directly except at calling MarkDirty().
|
2013-03-13 07:01:30 +04:00
|
|
|
// Use CurrentSelection() instead. This is marked as dirty when the
|
|
|
|
// selection or content is changed without document lock.
|
2013-03-13 07:01:29 +04:00
|
|
|
Selection mSelection;
|
|
|
|
|
2014-08-23 18:24:42 +04:00
|
|
|
// Get "current selection". If the document is locked, this initializes
|
|
|
|
// mSelection with the selection at the first call during a lock and returns
|
|
|
|
// it. However, mSelection is NOT modified immediately. When pending
|
|
|
|
// changes are flushed at unlocking the document, cached mSelection is
|
2016-06-29 11:18:44 +03:00
|
|
|
// modified. Note that this is also called by ContentForTSFRef().
|
2013-03-13 07:01:29 +04:00
|
|
|
Selection& CurrentSelection();
|
|
|
|
|
2015-03-21 19:28:04 +03:00
|
|
|
struct PendingAction final
|
2013-03-13 07:01:29 +04:00
|
|
|
{
|
2015-01-21 16:35:19 +03:00
|
|
|
enum ActionType : uint8_t
|
2013-03-13 07:01:29 +04:00
|
|
|
{
|
|
|
|
COMPOSITION_START,
|
|
|
|
COMPOSITION_UPDATE,
|
|
|
|
COMPOSITION_END,
|
2015-09-08 17:33:38 +03:00
|
|
|
SET_SELECTION
|
2013-03-13 07:01:29 +04:00
|
|
|
};
|
|
|
|
ActionType mType;
|
|
|
|
// For compositionstart and selectionset
|
|
|
|
LONG mSelectionStart;
|
|
|
|
LONG mSelectionLength;
|
2016-01-18 10:29:02 +03:00
|
|
|
// For compositionstart, compositionupdate and compositionend
|
2013-03-13 07:01:29 +04:00
|
|
|
nsString mData;
|
|
|
|
// For compositionupdate
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<TextRangeArray> mRanges;
|
2013-03-13 07:01:29 +04:00
|
|
|
// For selectionset
|
|
|
|
bool mSelectionReversed;
|
2014-08-07 10:50:21 +04:00
|
|
|
// For compositionupdate
|
|
|
|
bool mIncomplete;
|
2014-08-11 19:17:18 +04:00
|
|
|
// For compositionstart
|
|
|
|
bool mAdjustSelection;
|
2013-03-13 07:01:29 +04:00
|
|
|
};
|
|
|
|
// Items of mPendingActions are appended when TSF tells us to need to dispatch
|
|
|
|
// DOM composition events. However, we cannot dispatch while the document is
|
|
|
|
// locked because it can cause modifying the locked document. So, the pending
|
|
|
|
// actions should be performed when document lock is unlocked.
|
|
|
|
nsTArray<PendingAction> mPendingActions;
|
|
|
|
|
2014-08-07 10:50:21 +04:00
|
|
|
PendingAction* LastOrNewPendingCompositionUpdate()
|
2013-03-13 07:01:29 +04:00
|
|
|
{
|
|
|
|
if (!mPendingActions.IsEmpty()) {
|
|
|
|
PendingAction& lastAction = mPendingActions.LastElement();
|
|
|
|
if (lastAction.mType == PendingAction::COMPOSITION_UPDATE) {
|
|
|
|
return &lastAction;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
PendingAction* newAction = mPendingActions.AppendElement();
|
|
|
|
newAction->mType = PendingAction::COMPOSITION_UPDATE;
|
2015-07-24 08:07:39 +03:00
|
|
|
newAction->mRanges = new TextRangeArray();
|
2014-08-07 10:50:21 +04:00
|
|
|
newAction->mIncomplete = true;
|
2013-03-13 07:01:29 +04:00
|
|
|
return newAction;
|
|
|
|
}
|
|
|
|
|
2015-10-02 04:51:47 +03:00
|
|
|
/**
|
|
|
|
* WasTextInsertedWithoutCompositionAt() checks if text was inserted without
|
|
|
|
* composition immediately before (e.g., see InsertTextAtSelectionInternal()).
|
|
|
|
*
|
|
|
|
* @param aStart The inserted offset you expected.
|
|
|
|
* @param aLength The inserted text length you expected.
|
|
|
|
* @return true if the last pending actions are
|
|
|
|
* COMPOSITION_START and COMPOSITION_END and
|
|
|
|
* aStart and aLength match their information.
|
|
|
|
*/
|
|
|
|
bool WasTextInsertedWithoutCompositionAt(LONG aStart, LONG aLength) const
|
|
|
|
{
|
|
|
|
if (mPendingActions.Length() < 2) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
const PendingAction& pendingLastAction = mPendingActions.LastElement();
|
|
|
|
if (pendingLastAction.mType != PendingAction::COMPOSITION_END ||
|
|
|
|
pendingLastAction.mData.Length() != aLength) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
const PendingAction& pendingPreLastAction =
|
|
|
|
mPendingActions[mPendingActions.Length() - 2];
|
|
|
|
return pendingPreLastAction.mType == PendingAction::COMPOSITION_START &&
|
|
|
|
pendingPreLastAction.mSelectionStart == aStart;
|
|
|
|
}
|
|
|
|
|
2014-08-07 10:50:21 +04:00
|
|
|
bool IsPendingCompositionUpdateIncomplete() const
|
|
|
|
{
|
|
|
|
if (mPendingActions.IsEmpty()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
const PendingAction& lastAction = mPendingActions.LastElement();
|
|
|
|
return lastAction.mType == PendingAction::COMPOSITION_UPDATE &&
|
|
|
|
lastAction.mIncomplete;
|
|
|
|
}
|
|
|
|
|
2014-08-11 19:17:18 +04:00
|
|
|
void CompleteLastActionIfStillIncomplete()
|
|
|
|
{
|
|
|
|
if (!IsPendingCompositionUpdateIncomplete()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
RecordCompositionUpdateAction();
|
|
|
|
}
|
|
|
|
|
2013-03-13 07:01:29 +04:00
|
|
|
// When On*Composition() is called without document lock, we need to flush
|
|
|
|
// the recorded actions at quitting the method.
|
2013-03-13 07:01:30 +04:00
|
|
|
// AutoPendingActionAndContentFlusher class is usedful for it.
|
2015-03-21 19:28:04 +03:00
|
|
|
class MOZ_STACK_CLASS AutoPendingActionAndContentFlusher final
|
2013-03-13 07:01:29 +04:00
|
|
|
{
|
|
|
|
public:
|
2015-07-24 08:07:39 +03:00
|
|
|
AutoPendingActionAndContentFlusher(TSFTextStore* aTextStore)
|
2013-03-13 07:01:29 +04:00
|
|
|
: mTextStore(aTextStore)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(!mTextStore->mIsRecordingActionsWithoutLock);
|
|
|
|
if (!mTextStore->IsReadWriteLocked()) {
|
|
|
|
mTextStore->mIsRecordingActionsWithoutLock = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-13 07:01:30 +04:00
|
|
|
~AutoPendingActionAndContentFlusher()
|
2013-03-13 07:01:29 +04:00
|
|
|
{
|
|
|
|
if (!mTextStore->mIsRecordingActionsWithoutLock) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
mTextStore->FlushPendingActions();
|
|
|
|
mTextStore->mIsRecordingActionsWithoutLock = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2013-03-13 07:01:30 +04:00
|
|
|
AutoPendingActionAndContentFlusher() {}
|
2013-03-13 07:01:29 +04:00
|
|
|
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<TSFTextStore> mTextStore;
|
2013-03-13 07:01:29 +04:00
|
|
|
};
|
|
|
|
|
2015-03-21 19:28:04 +03:00
|
|
|
class Content final
|
2013-03-13 07:01:30 +04:00
|
|
|
{
|
|
|
|
public:
|
2015-07-24 08:07:39 +03:00
|
|
|
Content(TSFTextStore::Composition& aComposition,
|
|
|
|
TSFTextStore::Selection& aSelection) :
|
2013-03-13 07:01:30 +04:00
|
|
|
mComposition(aComposition), mSelection(aSelection)
|
|
|
|
{
|
|
|
|
Clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Clear()
|
|
|
|
{
|
|
|
|
mText.Truncate();
|
2015-03-13 15:51:00 +03:00
|
|
|
mLastCompositionString.Truncate();
|
2013-03-13 07:01:30 +04:00
|
|
|
mInitialized = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsInitialized() const { return mInitialized; }
|
|
|
|
|
|
|
|
void Init(const nsAString& aText)
|
|
|
|
{
|
|
|
|
mText = aText;
|
2015-03-13 15:51:00 +03:00
|
|
|
if (mComposition.IsComposing()) {
|
|
|
|
mLastCompositionString = mComposition.mString;
|
2015-08-03 09:15:30 +03:00
|
|
|
} else {
|
|
|
|
mLastCompositionString.Truncate();
|
2015-03-13 15:51:00 +03:00
|
|
|
}
|
2013-03-13 07:01:30 +04:00
|
|
|
mMinTextModifiedOffset = NOT_MODIFIED;
|
|
|
|
mInitialized = true;
|
|
|
|
}
|
|
|
|
|
2015-07-26 06:29:47 +03:00
|
|
|
void OnLayoutChanged()
|
|
|
|
{
|
|
|
|
mMinTextModifiedOffset = NOT_MODIFIED;
|
|
|
|
}
|
|
|
|
|
2013-03-13 07:01:30 +04:00
|
|
|
const nsDependentSubstring GetSelectedText() const;
|
|
|
|
const nsDependentSubstring GetSubstring(uint32_t aStart,
|
|
|
|
uint32_t aLength) const;
|
|
|
|
void ReplaceSelectedTextWith(const nsAString& aString);
|
|
|
|
void ReplaceTextWith(LONG aStart, LONG aLength, const nsAString& aString);
|
|
|
|
|
|
|
|
void StartComposition(ITfCompositionView* aCompositionView,
|
2014-08-25 17:40:23 +04:00
|
|
|
const PendingAction& aCompStart,
|
|
|
|
bool aPreserveSelection);
|
2015-10-02 04:51:47 +03:00
|
|
|
/**
|
|
|
|
* RestoreCommittedComposition() restores the committed string as
|
|
|
|
* composing string. If InsertTextAtSelection() or something is called
|
|
|
|
* before a call of OnStartComposition(), there is a pending
|
|
|
|
* compositionstart and a pending compositionend. In this case, we
|
|
|
|
* need to cancel the pending compositionend and continue the composition.
|
|
|
|
*
|
|
|
|
* @param aCompositionView The composition view.
|
|
|
|
* @param aPendingCompositionStart The pending compositionstart which
|
|
|
|
* started the committed composition.
|
|
|
|
* @param aCanceledCompositionEnd The pending compositionend which is
|
|
|
|
* canceled for restarting the composition.
|
|
|
|
*/
|
|
|
|
void RestoreCommittedComposition(
|
|
|
|
ITfCompositionView* aCompositionView,
|
|
|
|
const PendingAction& aPendingCompositionStart,
|
|
|
|
const PendingAction& aCanceledCompositionEnd);
|
2013-03-13 07:01:30 +04:00
|
|
|
void EndComposition(const PendingAction& aCompEnd);
|
|
|
|
|
|
|
|
const nsString& Text() const
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(mInitialized);
|
|
|
|
return mText;
|
|
|
|
}
|
2015-08-03 09:15:30 +03:00
|
|
|
const nsString& LastCompositionString() const
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(mInitialized);
|
|
|
|
return mLastCompositionString;
|
|
|
|
}
|
|
|
|
uint32_t MinTextModifiedOffset() const
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(mInitialized);
|
|
|
|
return mMinTextModifiedOffset;
|
|
|
|
}
|
2013-03-13 07:01:30 +04:00
|
|
|
|
|
|
|
// Returns true if layout of the character at the aOffset has not been
|
|
|
|
// calculated.
|
|
|
|
bool IsLayoutChangedAfter(uint32_t aOffset) const
|
|
|
|
{
|
|
|
|
return mInitialized && (mMinTextModifiedOffset < aOffset);
|
|
|
|
}
|
|
|
|
// Returns true if layout of the content has been changed, i.e., the new
|
|
|
|
// layout has not been calculated.
|
|
|
|
bool IsLayoutChanged() const
|
|
|
|
{
|
|
|
|
return mInitialized && (mMinTextModifiedOffset != NOT_MODIFIED);
|
|
|
|
}
|
2015-03-13 15:51:00 +03:00
|
|
|
// Returns minimum offset of modified text range.
|
|
|
|
uint32_t MinOffsetOfLayoutChanged() const
|
|
|
|
{
|
|
|
|
return mInitialized ? mMinTextModifiedOffset : NOT_MODIFIED;
|
|
|
|
}
|
2013-03-13 07:01:30 +04:00
|
|
|
|
2015-07-24 08:07:39 +03:00
|
|
|
TSFTextStore::Composition& Composition() { return mComposition; }
|
|
|
|
TSFTextStore::Selection& Selection() { return mSelection; }
|
2013-03-13 07:01:30 +04:00
|
|
|
|
|
|
|
private:
|
|
|
|
nsString mText;
|
2015-03-13 15:51:00 +03:00
|
|
|
// mLastCompositionString stores the composition string when the document
|
|
|
|
// is locked. This is necessary to compute mMinTextModifiedOffset.
|
|
|
|
nsString mLastCompositionString;
|
2015-07-24 08:07:39 +03:00
|
|
|
TSFTextStore::Composition& mComposition;
|
|
|
|
TSFTextStore::Selection& mSelection;
|
2013-03-13 07:01:30 +04:00
|
|
|
|
|
|
|
// The minimum offset of modified part of the text.
|
2015-01-21 16:35:19 +03:00
|
|
|
enum : uint32_t
|
2013-03-13 07:01:30 +04:00
|
|
|
{
|
|
|
|
NOT_MODIFIED = UINT32_MAX
|
|
|
|
};
|
|
|
|
uint32_t mMinTextModifiedOffset;
|
|
|
|
|
|
|
|
bool mInitialized;
|
|
|
|
};
|
2016-06-29 12:24:10 +03:00
|
|
|
// mContentForTSF is cache of content. The information is expected by TSF
|
2016-06-29 11:15:40 +03:00
|
|
|
// and TIP. Therefore, this is useful for answering the query from TSF or
|
2016-06-29 12:24:10 +03:00
|
|
|
// TIP.
|
|
|
|
// This is initialized by ContentForTSFRef() automatically (therefore, don't
|
|
|
|
// access this member directly except at calling Clear(), IsInitialized(),
|
|
|
|
// IsLayoutChangeAfter() or IsLayoutChanged()).
|
|
|
|
// This is cleared when:
|
|
|
|
// - When there is no composition, the document is unlocked.
|
|
|
|
// - When there is a composition, all dispatched events are handled by
|
|
|
|
// the focused editor which may be in a remote process.
|
|
|
|
// So, if two compositions are created very quickly, this cache may not be
|
|
|
|
// cleared between eCompositionCommit(AsIs) and eCompositionStart.
|
2016-06-29 11:15:40 +03:00
|
|
|
Content mContentForTSF;
|
2013-03-13 07:01:30 +04:00
|
|
|
|
2016-06-29 11:18:44 +03:00
|
|
|
Content& ContentForTSFRef();
|
2013-03-13 07:01:30 +04:00
|
|
|
|
2016-06-29 12:24:10 +03:00
|
|
|
// While mContentForTSF is valid, this returns the text stored by it.
|
|
|
|
// Otherwise, return the current text content retrieved by eQueryTextContent.
|
2014-08-23 18:24:42 +04:00
|
|
|
bool GetCurrentText(nsAString& aTextContent);
|
|
|
|
|
2015-03-21 19:28:04 +03:00
|
|
|
class MouseTracker final
|
2014-08-29 14:08:42 +04:00
|
|
|
{
|
|
|
|
public:
|
|
|
|
static const DWORD kInvalidCookie = static_cast<DWORD>(-1);
|
|
|
|
|
|
|
|
MouseTracker();
|
|
|
|
|
2015-07-24 08:07:39 +03:00
|
|
|
HRESULT Init(TSFTextStore* aTextStore);
|
|
|
|
HRESULT AdviseSink(TSFTextStore* aTextStore,
|
2014-08-29 14:08:42 +04:00
|
|
|
ITfRangeACP* aTextRange, ITfMouseSink* aMouseSink);
|
|
|
|
void UnadviseSink();
|
|
|
|
|
|
|
|
bool IsUsing() const { return mSink != nullptr; }
|
2014-08-29 14:08:43 +04:00
|
|
|
bool InRange(uint32_t aOffset) const
|
|
|
|
{
|
|
|
|
if (NS_WARN_IF(mStart < 0) ||
|
|
|
|
NS_WARN_IF(mLength <= 0)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return aOffset >= static_cast<uint32_t>(mStart) &&
|
|
|
|
aOffset < static_cast<uint32_t>(mStart + mLength);
|
|
|
|
}
|
2014-08-29 14:08:42 +04:00
|
|
|
DWORD Cookie() const { return mCookie; }
|
2014-08-29 14:08:43 +04:00
|
|
|
bool OnMouseButtonEvent(ULONG aEdge, ULONG aQuadrant, DWORD aButtonStatus);
|
|
|
|
LONG RangeStart() const { return mStart; }
|
2014-08-29 14:08:42 +04:00
|
|
|
|
|
|
|
private:
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<ITfMouseSink> mSink;
|
2014-08-29 14:08:42 +04:00
|
|
|
LONG mStart;
|
|
|
|
LONG mLength;
|
|
|
|
DWORD mCookie;
|
|
|
|
};
|
|
|
|
// mMouseTrackers is an array to store each information of installed
|
|
|
|
// ITfMouseSink instance.
|
|
|
|
nsTArray<MouseTracker> mMouseTrackers;
|
|
|
|
|
2013-01-26 18:35:21 +04:00
|
|
|
// The input scopes for this context, defaults to IS_DEFAULT.
|
|
|
|
nsTArray<InputScope> mInputScopes;
|
2014-08-13 07:55:59 +04:00
|
|
|
|
|
|
|
// Support retrieving attributes.
|
|
|
|
// TODO: We should support RightToLeft, perhaps.
|
|
|
|
enum
|
|
|
|
{
|
|
|
|
// Used for result of GetRequestedAttrIndex()
|
|
|
|
eNotSupported = -1,
|
|
|
|
|
|
|
|
// Supported attributes
|
|
|
|
eInputScope = 0,
|
|
|
|
eTextVerticalWriting,
|
2015-02-10 17:00:02 +03:00
|
|
|
eTextOrientation,
|
2014-08-13 07:55:59 +04:00
|
|
|
|
|
|
|
// Count of the supported attributes
|
|
|
|
NUM_OF_SUPPORTED_ATTRS
|
|
|
|
};
|
|
|
|
bool mRequestedAttrs[NUM_OF_SUPPORTED_ATTRS];
|
|
|
|
|
|
|
|
int32_t GetRequestedAttrIndex(const TS_ATTRID& aAttrID);
|
|
|
|
TS_ATTRID GetAttrID(int32_t aIndex);
|
|
|
|
|
|
|
|
bool mRequestedAttrValues;
|
|
|
|
|
2013-03-13 07:01:29 +04:00
|
|
|
// If edit actions are being recorded without document lock, this is true.
|
|
|
|
// Otherwise, false.
|
|
|
|
bool mIsRecordingActionsWithoutLock;
|
|
|
|
// During recording actions, we shouldn't call mSink->OnSelectionChange()
|
|
|
|
// because it may cause TSF request new lock. This is a problem if the
|
|
|
|
// selection change is caused by a call of On*Composition() without document
|
|
|
|
// lock since RequestLock() tries to flush the pending actions again (which
|
|
|
|
// are flushing). Therefore, OnSelectionChangeInternal() sets this true
|
2014-02-05 11:36:17 +04:00
|
|
|
// during recoding actions and then, RequestLock() will call
|
|
|
|
// mSink->OnSelectionChange() after mLock becomes 0.
|
|
|
|
bool mPendingOnSelectionChange;
|
|
|
|
// If GetTextExt() or GetACPFromPoint() is called and the layout hasn't been
|
2015-12-29 19:05:46 +03:00
|
|
|
// calculated yet, these methods return TS_E_NOLAYOUT. At that time,
|
|
|
|
// mHasReturnedNoLayoutError is set to true.
|
|
|
|
bool mHasReturnedNoLayoutError;
|
|
|
|
// Before calling ITextStoreACPSink::OnLayoutChange() and
|
|
|
|
// ITfContextOwnerServices::OnLayoutChange(), mWaitingQueryLayout is set to
|
|
|
|
// true. This is set to false when GetTextExt() or GetACPFromPoint() is
|
|
|
|
// called.
|
|
|
|
bool mWaitingQueryLayout;
|
2014-09-03 05:38:20 +04:00
|
|
|
// During the documet is locked, we shouldn't destroy the instance.
|
|
|
|
// If this is true, the instance will be destroyed after unlocked.
|
|
|
|
bool mPendingDestroy;
|
2015-08-28 17:17:37 +03:00
|
|
|
// If this is false, MaybeFlushPendingNotifications() will clear the
|
2016-06-29 11:15:40 +03:00
|
|
|
// mContentForTSF.
|
2016-06-29 11:22:09 +03:00
|
|
|
bool mDeferClearingContentForTSF;
|
2014-01-31 06:17:24 +04:00
|
|
|
// While there is native caret, this is true. Otherwise, false.
|
|
|
|
bool mNativeCaretIsCreated;
|
2015-06-17 08:00:33 +03:00
|
|
|
// While the instance is dispatching events, the event may not be handled
|
|
|
|
// synchronously in e10s mode. So, in such case, in strictly speaking,
|
|
|
|
// we shouldn't query layout information. However, TS_E_NOLAYOUT bugs of
|
|
|
|
// ITextStoreAPC::GetTextExt() blocks us to behave ideally.
|
|
|
|
// For preventing it to be called, we should put off notifying TSF of
|
|
|
|
// anything until layout information becomes available.
|
|
|
|
bool mDeferNotifyingTSF;
|
2016-01-27 09:09:13 +03:00
|
|
|
// While the document is locked, committing composition always fails since
|
|
|
|
// TSF needs another document lock for modifying the composition, selection
|
|
|
|
// and etc. So, committing composition should be performed after the
|
|
|
|
// document is unlocked.
|
|
|
|
bool mDeferCommittingComposition;
|
|
|
|
bool mDeferCancellingComposition;
|
2015-09-29 11:08:42 +03:00
|
|
|
// Immediately after a call of Destroy(), mDestroyed becomes true. If this
|
|
|
|
// is true, the instance shouldn't grant any requests from the TIP anymore.
|
|
|
|
bool mDestroyed;
|
2016-06-08 13:12:07 +03:00
|
|
|
// While the instance is being destroyed, this is set to true for avoiding
|
|
|
|
// recursive Destroy() calls.
|
|
|
|
bool mBeingDestroyed;
|
2015-06-17 08:00:33 +03:00
|
|
|
|
2009-02-10 23:56:51 +03:00
|
|
|
|
|
|
|
// TSF thread manager object for the current application
|
2015-07-24 08:07:39 +03:00
|
|
|
static StaticRefPtr<ITfThreadMgr> sThreadMgr;
|
2014-09-11 07:56:09 +04:00
|
|
|
// sMessagePump is QI'ed from sThreadMgr
|
2015-07-24 08:07:39 +03:00
|
|
|
static StaticRefPtr<ITfMessagePump> sMessagePump;
|
2014-09-11 07:56:09 +04:00
|
|
|
// sKeystrokeMgr is QI'ed from sThreadMgr
|
2015-07-24 08:07:39 +03:00
|
|
|
static StaticRefPtr<ITfKeystrokeMgr> sKeystrokeMgr;
|
2009-02-23 11:56:33 +03:00
|
|
|
// TSF display attribute manager
|
2015-07-24 08:07:39 +03:00
|
|
|
static StaticRefPtr<ITfDisplayAttributeMgr> sDisplayAttrMgr;
|
2009-02-23 11:56:33 +03:00
|
|
|
// TSF category manager
|
2015-07-24 08:07:39 +03:00
|
|
|
static StaticRefPtr<ITfCategoryMgr> sCategoryMgr;
|
2009-02-23 11:56:33 +03:00
|
|
|
|
2014-09-02 04:27:24 +04:00
|
|
|
// Current text store which is managing a keyboard enabled editor (i.e.,
|
2015-07-24 08:07:39 +03:00
|
|
|
// editable editor). Currently only ONE TSFTextStore instance is ever used,
|
2009-02-10 23:56:51 +03:00
|
|
|
// although Create is called when an editor is focused and Destroy called
|
|
|
|
// when the focused editor is blurred.
|
2015-07-24 08:07:39 +03:00
|
|
|
static StaticRefPtr<TSFTextStore> sEnabledTextStore;
|
2009-02-10 23:56:51 +03:00
|
|
|
|
2013-03-27 04:04:02 +04:00
|
|
|
// For IME (keyboard) disabled state:
|
2015-07-24 08:07:39 +03:00
|
|
|
static StaticRefPtr<ITfDocumentMgr> sDisabledDocumentMgr;
|
|
|
|
static StaticRefPtr<ITfContext> sDisabledContext;
|
2013-03-27 04:04:02 +04:00
|
|
|
|
2015-07-24 08:07:39 +03:00
|
|
|
static StaticRefPtr<ITfInputProcessorProfiles> sInputProcessorProfiles;
|
2014-01-14 06:01:00 +04:00
|
|
|
|
2014-09-11 07:56:10 +04:00
|
|
|
// TSF client ID for the current application
|
|
|
|
static DWORD sClientId;
|
|
|
|
|
2014-01-31 06:17:25 +04:00
|
|
|
// Enables/Disables hack for specific TIP.
|
|
|
|
static bool sCreateNativeCaretForATOK;
|
2015-08-03 09:15:30 +03:00
|
|
|
static bool sDoNotReturnNoLayoutErrorToMSSimplifiedTIP;
|
|
|
|
static bool sDoNotReturnNoLayoutErrorToMSTraditionalTIP;
|
2014-08-15 18:52:46 +04:00
|
|
|
static bool sDoNotReturnNoLayoutErrorToFreeChangJie;
|
|
|
|
static bool sDoNotReturnNoLayoutErrorToEasyChangjei;
|
2015-09-18 03:58:17 +03:00
|
|
|
static bool sDoNotReturnNoLayoutErrorToMSJapaneseIMEAtFirstChar;
|
|
|
|
static bool sDoNotReturnNoLayoutErrorToMSJapaneseIMEAtCaret;
|
2015-09-04 14:55:09 +03:00
|
|
|
static bool sHackQueryInsertForMSSimplifiedTIP;
|
|
|
|
static bool sHackQueryInsertForMSTraditionalTIP;
|
2009-02-10 23:56:51 +03:00
|
|
|
};
|
|
|
|
|
2015-07-24 08:07:39 +03:00
|
|
|
} // namespace widget
|
|
|
|
} // namespace mozilla
|
|
|
|
|
|
|
|
#endif // #ifndef TSFTextStore_h_
|