Files
palemoon27/widget/windows/TSFTextStore.h
T
roytam1 8a0a002cf2 import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1148708: Add missing 'override' annotations in DocAccessibleChild.h. rs=ehsan (d606358545)
- Bug 1210408 - make nsMaiInterfaceAction work with proxies, r=tbsaunde (f7c819c6ae)
- Bug 1210407 - teach nsMaiInterfaceTable to use proxies, r=tbsaunde (4ca4f10b5f)
- bug 1185157 make sure we don't send an event to a destroyed ipc document r=billm (23acf53f75)
- bug 1214864 - make SetCarretOffset() async r=davidb (e3079e9b2d)
- missing of Bug 1139972 - IPC Proxy for charAt, r=tbsaunde (e9593ed752)
- bug 1191598 - Pass MOZ_CURRENT_PROJECT in environment when running post-build automation steps for universal mac builds. r=gps (fc342c6ced)
- Bug 1164596 - Add mach android-emulator command; r=ahal (afeb9b27d1)
- Bug 1223149 - Add basic usage documentation for mach build; r=glandium (bfb802d175)
- Bug 1182301 - Improve 'mach build' notifications. r=gps (2c65a122d1)
- Bug 1184696 - Add clobber targets to |mach clobber|; Ability to clobber compiled python files, r=gps (35d8be292e)
- Bug 1117958 - Allow any debugging options to the run or gtest mach subcommands to automatically enable debugging. r=gps (32f986af4b)
- Bug 1180081 - Properly rebuild gtest/libxul before running gtests. r=gps (80db9a3d49)
- Bug 1171647, part 1 - Define a new function to convert the mode to a string. r=njn (61ad16f5ba)
- Bug 1171647, part 2 - Remove redundant assertion for dark matter mode. r=njn (b5ac9519f3)
- Bug 1058178, part 1 - Implement DMD heap scanning mode. r=njn (60e1079536)
- Bug 1058178, part 2 - Implement address clamping analysis for DMD scan logs. r=njn (45c0326b93)
- Bug 1102388 - Fix DMD static constructor ordering dependency. r=mccr8 (59b87897a1)
- Bug 1128705 - Don't redefine PAGE_SIZE in DMD if it's already defined. r=erahm (49216348ee)
- Bug 1179042 - Add a script for analyzing memory blocks using a heap scan DMD log. r=njn DONTBUILD (1c08d2d66e)
- Bug 1207245 - part 6 - rename nsRefPtr<T> to RefPtr<T>; r=ehsan; a=Tomcat (1c999d139b)
- Bug 1158772 - fix non-idiomatic memset call in nsDeque.cpp; r=erahm (de6b555245)
- Bug 1199400 - Part 1: Use CheckedInt when growing nsDeque capacity. r=froydnj (dfdf6814a3)
- Bug 1199400 - Part 0: Remove unused nsDequeIterator. r=froydnj (38d69d7f47)
- Bug 1199400 - Part 2: Add tests for possible nsDeque corner cases. r=froydnj (931baff195)
- Bug 1201997 - Part 1 - Converted compiled test to gtest for nsDeque class. Added tests to test untested methods. r=froydn (e893916651)
- Bug 1201997 - Part 2 - Removing unused methods from the nsDeque class. r=froydn (41595a90ac)
- Bug 1201997 - Part 3 - Make internally used methods private. r=froydn (8cd3afd96f)
- Bug 1201997 - Part 4 - Change size and offset variables to size_t.r=froydn (73eabc8d60)
- Bug 1215140 P1 Add an nsIConsoleReportCollector interface to support navigation channel logging. r=bz (8a41535e2b)
- Bug 1215140 P2 Make HttpBaseChannel implement nsIConsoleReportCollector. r=bz (75fca301f2)
- Bug 1197679 - If nsUnknownDecoder is involved in e10s DivertToParent can break. r=jduell (5d94a12504)
- Bug 1178991 - smartptr for http converter r=hurley (8e7fbc8443)
- bug 366559 - patch 2, fix nsHTTPCompressConv indentation r=bagder (ba762da587)
- bug 366559 - patch 3, fix nsHTTPCompressConv bracing style r=bagder (54195ab451)
- bits of  bug 366559 - patch 7, content-encoding brotli for http (f0b4051022)
- Bug 1205112 - Make PushEvent.data nullable. r=mt,smaug (775db32856)
- Bug 1193414 - SharedWorkers thread should be kept alive also when the SharedWorker object is CCed, r=khuey (b77ea8125c)
- Bug 1206520: Add about:config prefs to enable throwing on asm.js validation failures; r=bz (c42126665d)
- Bug 1193414 - Telemetry for SharedWorker spawning. r=bkelly (77984b7bcc)
- Bug 1205676 - Enable WPT service-worker/unregister-then-register-new-script.https.html in e10s, r=nsm (ec24939cf6)
- Bug 1193133 - Throw when calling postMessage from a Service Worker dom object with no global. r=bkelly (526dcacfab)
- Bug 1181871 P1 Only enforce Cache Context shared data destruction on target thread after init. r=ehsan (cdbf3ed3a8)
- Bug 1181871 P2 Fix ServiceWorkerManager usage of stack-based ErrorResult. r=ehsan (c449195d90)
- minor cleanup and missing bit of 1198230 (02f459db05)
- Bug 1143717 - Implement the ServiceWorkerMessageEvent interface. r=baku (027b3465f2)
- fix misspatch (708eee4e84)
- Bug 1188545 - Disentangle service workers from shared workers and refactor event dispatching code into a separate class. r=nsm,mrbkap (fb5b5341c9)
- Bug 1205228 - Change PackagedAppVerifier to notify the verification result asynchronously. r=valentin. (9edda0fa00)
- Bug 1178518 - Packaged App Utils. r=valentin (f60f3b7a93)
- Bug 1213150 - Part 1: Add a nsContentUtils::IsNonSubresourceRequest helper; r=jdm (b509cc3cc9)
- Bug 1213150 - Part 2: Rework ShouldPrepareForIntercept() in terms of subresource requests; r=jdm (2e92fe8780)
- Bug 1213150 - Part 3: Remove nsIInterceptedChannel.isNavigation; r=jdm (becf1cc12f)
- Bug 1213150 follow-up: fix build bustage (8d73d6ca73)
- Bug 1198394 - Part 1: Allow interception of HSTS upgraded connections in non-e10s mode; r=mcmanus (f504c5be08)
- Bug 1198394 - Part 2: Add a test for interception of HSTS upgraded connections; r=jdm (054e984eef)
- Bug 1187011 - Don't allow response body with null body status. r=bkelly (b1860741d1)
- missing bit of 1140788 (29d319712e)
- Bug 1213436 - Reject core dumps with node IDs that don't fit in an IEEE 754 double; r=sfink (3c1f6fdda0)
- Bug 1211006 - Add Debugger.Source.prototype.canonicalId; r=ejpbruel (eef7b79fce)
- Bug 1199218 - Implement JS::ubi::Node::size for js::LazyScript referents; r=sfink (098a48d240)
- Bug 1220031 - Add JS::ubi::Node::scriptFilename; r=sfink (6b824ae680)
- Bug 1143575. Remove unused MediaQueue::Empty. r=cpearce (de737f3433)
- Bug 1209933 - Make sure all parent runtime pointers are the topmost parent, r=billm. (fe824d967d)
- Bug 1197012 - Fix ThrowTypeError in Notification. r=mccr8 (0b1a097526)
- Bug 1197893 - Check the number of arguments for ThrowTypeError() and ThrowRangeError() at compile time. r=peterv (d98c7d78a0)
- Bug 1142083 - Add test for IDN Unicode domain redirect. r=mcmanus (0c8961fe17)
- Bug 1187159 - Add mochitest for loading packaged apps (iframe+fetch+mozapp) r=jduell (ce90ea561b)
- Bug 1186290 - Notify TabParent to switch process when loading a signed package. r=honzab, r=kanru. (c58a14554a)
- fix (15e2df75eb)
- Bug 1206124 P1 Fix "same-origin" CORS credentials in FetchDriver. r=ehsan (fae1bb6ab3)
- Bug 1206124 P2 Test fetch() with credentials and redirects. r=ehsan (ffc6254112)
- Bug 1211751: Remove nsIChannelEventSink-forwarding from EventSource and FetchDriver. It's never needed. r=smaug (adafe5737a)
- Bug 1212433 Fail fetch() calls that require preflight and also redirect. r=sicking a=abillings (c0d6742b9e)
- Bug 1193128 - Fix base64 decoding when fetching data URIs. r=baku (80bafa291a)
- Bug 1195167 part 1: Let necko handle all protocols. r=bkelly (bb932b0ada)
- Bug 1195167 part 2: Remove redundant aCORSFlag argument and instead use mCORSFlagEverSet. r=bkelly (beadafcad0)
- Bug 1195167 part 3: Remove more scheme-specific handling from FetchDriver. r=bkelly (d00b38db9e)
- Bug 1195167 part 4: Remove FetchDriver::BasicFetch since it is empty. r=bkelly (c5ed097267)
- Bug 1210413 P2 Test CORS credentials on cross-origin redirects. r=sicking a=dveditz (b4eeb8aac0)
- Bug 1210413 P1 Propagate new channel load flags from child to parent on redirect. r=jduell a=dveditz (8b329af4fa)
- Bug 1195167 part 5: Make FetchDriver use AsyncOpen2. r=bkelly (cc217c4cc1)
- Bug 1195167 part 6: Some code simplification since necko handles fetch recursion. r=bkelly (f3b6da2262)
- Bug 1195167: Followup to fix test which I forgot to change (81e7439a2e)
- Bug 1215746: Remove RequestMode::Cors_with_forced_preflight. r=bkelly (0336e812b6)
- Bug 1211000: Move CORS preflight logic from nsCORSListenerProxy to nsCORSPreflightListener. r=ehsan (bf2f71cf22)
- missing bit of Bug 1211443 - Drop scheduled update if decoder initialization isn't done yet. r=jya (f6bc074e33)
- Bug 1182571: Fix nsILoadInfo->GetContentPolicyType API to be less ambigious. Audit and fix all users of it. r=ckerschb (5af6fa7442)
- fix (e40c8e7625)
- Bug 1173811 - Part 1: Propagate the response URL to intercepted channels when necessary (non-e10s). r=mayhemer,bkelly (26f4f13c28)
- Bug 1173811 - Part 2: Propagate the response URL to intercepted channels when necessary (e10s). r=mayhemer,bkelly (a603fe1df2)
- Bug 1154309 - Add New Resource Timing Fields r=bz,hurley (1d14eb6bef)
- Bug 1175685 - add OriginAttribute to LoadInfo. r=jonas, r=ckerschb, r=michal (a5d18bb637)
- Bug 1175685 - add OriginAttribute to LoadInfo. r=jonas, r=ckerschb, r=michal (fb07d2c8aa)
- Bug 1212904 P1 Add a LoadTainting enumeration. r=jduell (a1db8a3e99)
- Bug 1212904 P2 Add LoadTainting information to nsILoadInfo. r=jduell (2482e5e334)
- Bug 1221151 - use [infallible] in nsILoadInfo.idl instead of manual %{C++ blocks; r=jduell (aae73129b6)
- Bug 1045891 - CSP 2 child-src implementation r=ckerschb (792920aeb9)
- Bug 1219931 - CSP: Don't allow removing a policy (r=sicking) (9daaab4186)
- Bug 1208661 - Dump client-side layer textures. r=BenWa (1f2d17d515)
2022-10-17 11:08:37 +08:00

850 lines
28 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
#ifndef TSFTextStore_h_
#define TSFTextStore_h_
#include "nsAutoPtr.h"
#include "nsString.h"
#include "nsCOMPtr.h"
#include "nsIWidget.h"
#include "nsWindowBase.h"
#include "WinUtils.h"
#include "mozilla/Attributes.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/TextRange.h"
#include "mozilla/WindowsVersion.h"
#include <msctf.h>
#include <textstor.h>
// 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
#ifdef TEXTATTRS_INIT_GUID
#include <tsattrs.h>
#endif
#include <inputscope.h>
// TSF InputScope, for earlier SDK 8
#define IS_SEARCH static_cast<InputScope>(50)
struct ITfThreadMgr;
struct ITfDocumentMgr;
struct ITfDisplayAttributeMgr;
struct ITfCategoryMgr;
class nsWindow;
namespace mozilla {
namespace widget {
struct MSGResult;
/*
* Text Services Framework text store
*/
class TSFTextStore final : public ITextStoreACP
, public ITfContextOwnerCompositionSink
, public ITfMouseTrackerACP
{
public: /*IUnknown*/
STDMETHODIMP QueryInterface(REFIID, void**);
NS_INLINE_DECL_IUNKNOWN_REFCOUNTING(TSFTextStore)
public: /*ITextStoreACP*/
STDMETHODIMP AdviseSink(REFIID, IUnknown*, DWORD);
STDMETHODIMP UnadviseSink(IUnknown*);
STDMETHODIMP RequestLock(DWORD, HRESULT*);
STDMETHODIMP GetStatus(TS_STATUS*);
STDMETHODIMP QueryInsert(LONG, LONG, ULONG, LONG*, LONG*);
STDMETHODIMP GetSelection(ULONG, ULONG, TS_SELECTION_ACP*, ULONG*);
STDMETHODIMP SetSelection(ULONG, const TS_SELECTION_ACP*);
STDMETHODIMP GetText(LONG, LONG, WCHAR*, ULONG, ULONG*, TS_RUNINFO*, ULONG,
ULONG*, LONG*);
STDMETHODIMP SetText(DWORD, LONG, LONG, const WCHAR*, ULONG, TS_TEXTCHANGE*);
STDMETHODIMP GetFormattedText(LONG, LONG, IDataObject**);
STDMETHODIMP GetEmbedded(LONG, REFGUID, REFIID, IUnknown**);
STDMETHODIMP QueryInsertEmbedded(const GUID*, const FORMATETC*, BOOL*);
STDMETHODIMP InsertEmbedded(DWORD, LONG, LONG, IDataObject*, TS_TEXTCHANGE*);
STDMETHODIMP RequestSupportedAttrs(DWORD, ULONG, const TS_ATTRID*);
STDMETHODIMP RequestAttrsAtPosition(LONG, ULONG, const TS_ATTRID*, DWORD);
STDMETHODIMP RequestAttrsTransitioningAtPosition(LONG, ULONG,
const TS_ATTRID*, DWORD);
STDMETHODIMP FindNextAttrTransition(LONG, LONG, ULONG, const TS_ATTRID*,
DWORD, LONG*, BOOL*, LONG*);
STDMETHODIMP RetrieveRequestedAttrs(ULONG, TS_ATTRVAL*, ULONG*);
STDMETHODIMP GetEndACP(LONG*);
STDMETHODIMP GetActiveView(TsViewCookie*);
STDMETHODIMP GetACPFromPoint(TsViewCookie, const POINT*, DWORD, LONG*);
STDMETHODIMP GetTextExt(TsViewCookie, LONG, LONG, RECT*, BOOL*);
STDMETHODIMP GetScreenExt(TsViewCookie, RECT*);
STDMETHODIMP GetWnd(TsViewCookie, HWND*);
STDMETHODIMP InsertTextAtSelection(DWORD, const WCHAR*, ULONG, LONG*, LONG*,
TS_TEXTCHANGE*);
STDMETHODIMP InsertEmbeddedAtSelection(DWORD, IDataObject*, LONG*, LONG*,
TS_TEXTCHANGE*);
public: /*ITfContextOwnerCompositionSink*/
STDMETHODIMP OnStartComposition(ITfCompositionView*, BOOL*);
STDMETHODIMP OnUpdateComposition(ITfCompositionView*, ITfRange*);
STDMETHODIMP OnEndComposition(ITfCompositionView*);
public: /*ITfMouseTrackerACP*/
STDMETHODIMP AdviseMouseSink(ITfRangeACP*, ITfMouseSink*, DWORD*);
STDMETHODIMP UnadviseMouseSink(DWORD);
public:
static void Initialize(void);
static void Terminate(void);
static bool ProcessRawKeyMessage(const MSG& aMsg);
static void ProcessMessage(nsWindowBase* aWindow, UINT aMessage,
WPARAM& aWParam, LPARAM& aLParam,
MSGResult& aResult);
static void SetIMEOpenState(bool);
static bool GetIMEOpenState(void);
static void CommitComposition(bool aDiscard)
{
NS_ASSERTION(IsInTSFMode(), "Not in TSF mode, shouldn't be called");
if (sEnabledTextStore) {
sEnabledTextStore->CommitCompositionInternal(aDiscard);
}
}
static void SetInputContext(nsWindowBase* aWidget,
const InputContext& aContext,
const InputContextAction& aAction);
static nsresult OnFocusChange(bool aGotFocus,
nsWindowBase* aFocusedWidget,
const InputContext& aContext);
static nsresult OnTextChange(const IMENotification& aIMENotification)
{
NS_ASSERTION(IsInTSFMode(), "Not in TSF mode, shouldn't be called");
return sEnabledTextStore ?
sEnabledTextStore->OnTextChangeInternal(aIMENotification) : NS_OK;
}
static nsresult OnSelectionChange(const IMENotification& aIMENotification)
{
NS_ASSERTION(IsInTSFMode(), "Not in TSF mode, shouldn't be called");
return sEnabledTextStore ?
sEnabledTextStore->OnSelectionChangeInternal(aIMENotification) : NS_OK;
}
static nsresult OnLayoutChange()
{
NS_ASSERTION(IsInTSFMode(), "Not in TSF mode, shouldn't be called");
return sEnabledTextStore ?
sEnabledTextStore->OnLayoutChangeInternal() : NS_OK;
}
static nsresult OnUpdateComposition()
{
NS_ASSERTION(IsInTSFMode(), "Not in TSF mode, shouldn't be called");
return sEnabledTextStore ?
sEnabledTextStore->OnUpdateCompositionInternal() : NS_OK;
}
static nsresult OnMouseButtonEvent(const IMENotification& aIMENotification)
{
NS_ASSERTION(IsInTSFMode(), "Not in TSF mode, shouldn't be called");
return sEnabledTextStore ?
sEnabledTextStore->OnMouseButtonEventInternal(aIMENotification) : NS_OK;
}
static nsIMEUpdatePreference GetIMEUpdatePreference();
// Returns the address of the pointer so that the TSF automatic test can
// replace the system object with a custom implementation for testing.
// XXX TSF doesn't work now. Should we remove it?
static void* GetNativeData(uint32_t aDataType)
{
switch (aDataType) {
case NS_NATIVE_TSF_THREAD_MGR:
Initialize(); // Apply any previous changes
return static_cast<void*>(&sThreadMgr);
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;
}
}
static ITfMessagePump* GetMessagePump()
{
return sMessagePump;
}
static void* GetThreadManager()
{
return static_cast<void*>(sThreadMgr);
}
static bool ThinksHavingFocus()
{
return (sEnabledTextStore && sEnabledTextStore->mContext);
}
static bool IsInTSFMode()
{
return sThreadMgr != nullptr;
}
static bool IsComposing()
{
return (sEnabledTextStore && sEnabledTextStore->mComposition.IsComposing());
}
static bool IsComposingOn(nsWindowBase* aWidget)
{
return (IsComposing() && sEnabledTextStore->mWidget == aWidget);
}
static bool IsIMM_IME();
#ifdef DEBUG
// Returns true when keyboard layout has IME (TIP).
static bool CurrentKeyboardLayoutHasIME();
#endif // #ifdef DEBUG
protected:
TSFTextStore();
~TSFTextStore();
static bool CreateAndSetFocus(nsWindowBase* aFocusedWidget,
const InputContext& aContext);
static void MarkContextAsKeyboardDisabled(ITfContext* aContext);
static void MarkContextAsEmpty(ITfContext* aContext);
bool Init(nsWindowBase* aWidget);
bool Destroy();
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); }
// This is called immediately after a call of OnLockGranted() of mSink.
// Note that mLock isn't cleared yet when this is called.
void DidLockGranted();
bool GetScreenExtInternal(RECT& aScreenExt);
// 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.
HRESULT SetSelectionInternal(const TS_SELECTION_ACP*,
bool aDispatchCompositionChangeEvent = false);
bool InsertTextAtSelectionInternal(const nsAString& aInsertStr,
TS_TEXTCHANGE* aTextChange);
void CommitCompositionInternal(bool);
nsresult OnTextChangeInternal(const IMENotification& aIMENotification);
nsresult OnSelectionChangeInternal(const IMENotification& aIMENotification);
nsresult OnMouseButtonEventInternal(const IMENotification& aIMENotification);
HRESULT GetDisplayAttribute(ITfProperty* aProperty,
ITfRange* aRange,
TF_DISPLAYATTRIBUTE* aResult);
HRESULT RestartCompositionIfNecessary(ITfRange* pRangeNew = nullptr);
HRESULT RestartComposition(ITfCompositionView* aCompositionView,
ITfRange* aNewRange);
// Following methods record composing action(s) to mPendingActions.
// They will be flushed FlushPendingActions().
HRESULT RecordCompositionStartAction(ITfCompositionView* aCompositionView,
ITfRange* aRange,
bool aPreserveSelection);
HRESULT RecordCompositionStartAction(ITfCompositionView* aComposition,
LONG aStart,
LONG aLength,
bool aPreserveSelection);
HRESULT RecordCompositionUpdateAction();
HRESULT RecordCompositionEndAction();
// 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.
void DispatchEvent(WidgetGUIEvent& aEvent);
void OnLayoutInformationAvaliable();
// FlushPendingActions() performs pending actions recorded in mPendingActions
// and clear it.
void FlushPendingActions();
// MaybeFlushPendingNotifications() performs pending notifications to TSF.
void MaybeFlushPendingNotifications();
nsresult OnLayoutChangeInternal();
nsresult OnUpdateCompositionInternal();
void NotifyTSFOfTextChange(const TS_TEXTCHANGE& aTextChange);
void NotifyTSFOfSelectionChange();
bool NotifyTSFOfLayoutChange(bool aFlush);
HRESULT HandleRequestAttrs(DWORD aFlags,
ULONG aFilterCount,
const TS_ATTRID* aFilterAttrs);
void SetInputScope(const nsString& aHTMLInputType);
// Creates native caret over our caret. This method only works on desktop
// application. Otherwise, this does nothing.
void CreateNativeCaret();
// Destroys native caret if there is.
void MaybeDestroyNativeCaret();
// Holds the pointer to our current win32 widget
RefPtr<nsWindowBase> mWidget;
// Document manager for the currently focused editor
RefPtr<ITfDocumentMgr> mDocumentMgr;
// Edit cookie associated with the current editing context
DWORD mEditCookie;
// Editing context at the bottom of mDocumentMgr's context stack
RefPtr<ITfContext> mContext;
// Currently installed notification sink
RefPtr<ITextStoreACPSink> mSink;
// 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;
class Composition final
{
public:
// nullptr if no composition is active, otherwise the current composition
RefPtr<ITfCompositionView> mView;
// 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
// and mString is flushed to editor through eCompositionChange.
// This way all changes are updated in batches to avoid
// inconsistencies/artifacts.
nsString mString;
// The start of the current active composition, in ACP offsets
LONG mStart;
bool IsComposing() const
{
return (mView != nullptr);
}
LONG EndOffset() const
{
return mStart + static_cast<LONG>(mString.Length());
}
// 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);
void End();
};
// 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.
// For that, TSFTextStore modifies mComposition even while the document is
// locked. With mComposition, query methods can returns the text content
// information.
Composition mComposition;
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;
}
void SetSelection(uint32_t aStart,
uint32_t aLength,
bool aReversed,
WritingMode aWritingMode)
{
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;
mWritingMode = aWritingMode;
}
bool IsCollapsed() const
{
MOZ_ASSERT(!mDirty);
return (mACP.acpStart == mACP.acpEnd);
}
void CollapseAt(uint32_t aOffset)
{
// 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.
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);
}
WritingMode GetWritingMode() const
{
MOZ_ASSERT(!mDirty);
return mWritingMode;
}
private:
TS_SELECTION_ACP mACP;
WritingMode mWritingMode;
bool mDirty;
};
// Don't access mSelection directly except at calling MarkDirty().
// Use CurrentSelection() instead. This is marked as dirty when the
// selection or content is changed without document lock.
Selection mSelection;
// 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
// modified. Note that this is also called by LockedContent().
Selection& CurrentSelection();
struct PendingAction final
{
enum ActionType : uint8_t
{
COMPOSITION_START,
COMPOSITION_UPDATE,
COMPOSITION_END,
SET_SELECTION
};
ActionType mType;
// For compositionstart and selectionset
LONG mSelectionStart;
LONG mSelectionLength;
// For compositionupdate and compositionend
nsString mData;
// For compositionupdate
RefPtr<TextRangeArray> mRanges;
// For selectionset
bool mSelectionReversed;
// For compositionupdate
bool mIncomplete;
// For compositionstart
bool mAdjustSelection;
};
// 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;
PendingAction* LastOrNewPendingCompositionUpdate()
{
if (!mPendingActions.IsEmpty()) {
PendingAction& lastAction = mPendingActions.LastElement();
if (lastAction.mType == PendingAction::COMPOSITION_UPDATE) {
return &lastAction;
}
}
PendingAction* newAction = mPendingActions.AppendElement();
newAction->mType = PendingAction::COMPOSITION_UPDATE;
newAction->mRanges = new TextRangeArray();
newAction->mIncomplete = true;
return newAction;
}
bool IsPendingCompositionUpdateIncomplete() const
{
if (mPendingActions.IsEmpty()) {
return false;
}
const PendingAction& lastAction = mPendingActions.LastElement();
return lastAction.mType == PendingAction::COMPOSITION_UPDATE &&
lastAction.mIncomplete;
}
void CompleteLastActionIfStillIncomplete()
{
if (!IsPendingCompositionUpdateIncomplete()) {
return;
}
RecordCompositionUpdateAction();
}
// When On*Composition() is called without document lock, we need to flush
// the recorded actions at quitting the method.
// AutoPendingActionAndContentFlusher class is usedful for it.
class MOZ_STACK_CLASS AutoPendingActionAndContentFlusher final
{
public:
AutoPendingActionAndContentFlusher(TSFTextStore* aTextStore)
: mTextStore(aTextStore)
{
MOZ_ASSERT(!mTextStore->mIsRecordingActionsWithoutLock);
if (!mTextStore->IsReadWriteLocked()) {
mTextStore->mIsRecordingActionsWithoutLock = true;
}
}
~AutoPendingActionAndContentFlusher()
{
if (!mTextStore->mIsRecordingActionsWithoutLock) {
return;
}
mTextStore->FlushPendingActions();
mTextStore->mIsRecordingActionsWithoutLock = false;
}
private:
AutoPendingActionAndContentFlusher() {}
RefPtr<TSFTextStore> mTextStore;
};
class Content final
{
public:
Content(TSFTextStore::Composition& aComposition,
TSFTextStore::Selection& aSelection) :
mComposition(aComposition), mSelection(aSelection)
{
Clear();
}
void Clear()
{
mText.Truncate();
mLastCompositionString.Truncate();
mInitialized = false;
}
bool IsInitialized() const { return mInitialized; }
void Init(const nsAString& aText)
{
mText = aText;
if (mComposition.IsComposing()) {
mLastCompositionString = mComposition.mString;
} else {
mLastCompositionString.Truncate();
}
mMinTextModifiedOffset = NOT_MODIFIED;
mInitialized = true;
}
void OnLayoutChanged()
{
mMinTextModifiedOffset = NOT_MODIFIED;
}
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,
const PendingAction& aCompStart,
bool aPreserveSelection);
void EndComposition(const PendingAction& aCompEnd);
const nsString& Text() const
{
MOZ_ASSERT(mInitialized);
return mText;
}
const nsString& LastCompositionString() const
{
MOZ_ASSERT(mInitialized);
return mLastCompositionString;
}
uint32_t MinTextModifiedOffset() const
{
MOZ_ASSERT(mInitialized);
return mMinTextModifiedOffset;
}
// 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);
}
// Returns minimum offset of modified text range.
uint32_t MinOffsetOfLayoutChanged() const
{
return mInitialized ? mMinTextModifiedOffset : NOT_MODIFIED;
}
TSFTextStore::Composition& Composition() { return mComposition; }
TSFTextStore::Selection& Selection() { return mSelection; }
private:
nsString mText;
// mLastCompositionString stores the composition string when the document
// is locked. This is necessary to compute mMinTextModifiedOffset.
nsString mLastCompositionString;
TSFTextStore::Composition& mComposition;
TSFTextStore::Selection& mSelection;
// The minimum offset of modified part of the text.
enum : uint32_t
{
NOT_MODIFIED = UINT32_MAX
};
uint32_t mMinTextModifiedOffset;
bool mInitialized;
};
// mLockedContent starts to cache content of the document at first query of
// the content during a document lock. This is abandoned after document is
// unlocked and dispatched events are handled. This is initialized by
// LockedContent() automatically. So, don't access this member directly
// except at calling Clear(), IsInitialized(), IsLayoutChangedAfter() or
// IsLayoutChanged().
Content mLockedContent;
Content& LockedContent();
// While the documet is locked, this returns the text stored by
// mLockedContent. Otherwise, return the current text content.
bool GetCurrentText(nsAString& aTextContent);
class MouseTracker final
{
public:
static const DWORD kInvalidCookie = static_cast<DWORD>(-1);
MouseTracker();
HRESULT Init(TSFTextStore* aTextStore);
HRESULT AdviseSink(TSFTextStore* aTextStore,
ITfRangeACP* aTextRange, ITfMouseSink* aMouseSink);
void UnadviseSink();
bool IsUsing() const { return mSink != nullptr; }
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);
}
DWORD Cookie() const { return mCookie; }
bool OnMouseButtonEvent(ULONG aEdge, ULONG aQuadrant, DWORD aButtonStatus);
LONG RangeStart() const { return mStart; }
private:
RefPtr<ITfMouseSink> mSink;
LONG mStart;
LONG mLength;
DWORD mCookie;
};
// mMouseTrackers is an array to store each information of installed
// ITfMouseSink instance.
nsTArray<MouseTracker> mMouseTrackers;
// The input scopes for this context, defaults to IS_DEFAULT.
nsTArray<InputScope> mInputScopes;
// Support retrieving attributes.
// TODO: We should support RightToLeft, perhaps.
enum
{
// Used for result of GetRequestedAttrIndex()
eNotSupported = -1,
// Supported attributes
eInputScope = 0,
eTextVerticalWriting,
eTextOrientation,
// 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;
// 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
// 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
// calculated yet, these methods return TS_E_NOLAYOUT. Then, RequestLock()
// will call mSink->OnLayoutChange() and
// ITfContextOwnerServices::OnLayoutChange() after the layout is fixed and
// the document is unlocked.
bool mPendingOnLayoutChange;
// During the documet is locked, we shouldn't destroy the instance.
// If this is true, the instance will be destroyed after unlocked.
bool mPendingDestroy;
// If this is false, MaybeFlushPendingNotifications() will clear the
// mLockedContent.
bool mDeferClearingLockedContent;
// While there is native caret, this is true. Otherwise, false.
bool mNativeCaretIsCreated;
// 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;
// TSF thread manager object for the current application
static StaticRefPtr<ITfThreadMgr> sThreadMgr;
// sMessagePump is QI'ed from sThreadMgr
static StaticRefPtr<ITfMessagePump> sMessagePump;
// sKeystrokeMgr is QI'ed from sThreadMgr
static StaticRefPtr<ITfKeystrokeMgr> sKeystrokeMgr;
// TSF display attribute manager
static StaticRefPtr<ITfDisplayAttributeMgr> sDisplayAttrMgr;
// TSF category manager
static StaticRefPtr<ITfCategoryMgr> sCategoryMgr;
// Current text store which is managing a keyboard enabled editor (i.e.,
// editable editor). Currently only ONE TSFTextStore instance is ever used,
// although Create is called when an editor is focused and Destroy called
// when the focused editor is blurred.
static StaticRefPtr<TSFTextStore> sEnabledTextStore;
// For IME (keyboard) disabled state:
static StaticRefPtr<ITfDocumentMgr> sDisabledDocumentMgr;
static StaticRefPtr<ITfContext> sDisabledContext;
static StaticRefPtr<ITfInputProcessorProfiles> sInputProcessorProfiles;
// TSF client ID for the current application
static DWORD sClientId;
// Enables/Disables hack for specific TIP.
static bool sCreateNativeCaretForATOK;
static bool sDoNotReturnNoLayoutErrorToMSSimplifiedTIP;
static bool sDoNotReturnNoLayoutErrorToMSTraditionalTIP;
static bool sDoNotReturnNoLayoutErrorToFreeChangJie;
static bool sDoNotReturnNoLayoutErrorToEasyChangjei;
static bool sDoNotReturnNoLayoutErrorToGoogleJaInputAtFirstChar;
static bool sDoNotReturnNoLayoutErrorToGoogleJaInputAtCaret;
};
} // namespace widget
} // namespace mozilla
#endif // #ifndef TSFTextStore_h_