Bug 1861709 replace AudioCallbackDriver::ThreadRunning() assertions that mean to...
[gecko.git] / widget / windows / IMMHandler.h
blobe012541faedf78140e2ba1427bda600c6c4a5d25
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #ifndef IMMHandler_h_
7 #define IMMHandler_h_
9 #include "mozilla/ContentData.h"
10 #include "mozilla/EventForwards.h"
11 #include "mozilla/TextEventDispatcher.h"
12 #include "mozilla/WritingModes.h"
14 #include "windef.h"
15 #include "winnetwk.h"
16 #include "npapi.h"
18 #include "nsCOMPtr.h"
19 #include "nsIWidget.h"
20 #include "nsRect.h"
21 #include "nsString.h"
22 #include "nsTArray.h"
24 #include <windows.h>
26 class nsWindow;
28 namespace mozilla {
29 namespace widget {
31 struct MSGResult;
33 class IMEContext final {
34 public:
35 IMEContext() : mWnd(nullptr), mIMC(nullptr) {}
37 explicit IMEContext(HWND aWnd);
38 explicit IMEContext(nsWindow* aWindowBase);
40 ~IMEContext() { Clear(); }
42 HIMC get() const { return mIMC; }
44 void Init(HWND aWnd);
45 void Init(nsWindow* aWindowBase);
46 void Clear();
48 bool IsValid() const { return !!mIMC; }
50 void SetOpenState(bool aOpen) const {
51 if (!mIMC) {
52 return;
54 ::ImmSetOpenStatus(mIMC, aOpen);
57 bool GetOpenState() const {
58 if (!mIMC) {
59 return false;
61 return (::ImmGetOpenStatus(mIMC) != FALSE);
64 bool AssociateDefaultContext() {
65 // We assume that there is only default IMC, no new IMC has been created.
66 if (mIMC) {
67 return false;
69 if (!::ImmAssociateContextEx(mWnd, nullptr, IACE_DEFAULT)) {
70 return false;
72 mIMC = ::ImmGetContext(mWnd);
73 return (mIMC != nullptr);
76 bool Disassociate() {
77 if (!mIMC) {
78 return false;
80 if (!::ImmAssociateContextEx(mWnd, nullptr, 0)) {
81 return false;
83 ::ImmReleaseContext(mWnd, mIMC);
84 mIMC = nullptr;
85 return true;
88 protected:
89 IMEContext(const IMEContext& aOther) { MOZ_CRASH("Don't copy IMEContext"); }
91 HWND mWnd;
92 HIMC mIMC;
95 class IMMHandler final {
96 public:
97 static void Initialize();
98 static void Terminate();
100 // If Process*() returns true, the caller shouldn't do anything anymore.
101 static bool ProcessMessage(nsWindow* aWindow, UINT msg, WPARAM& wParam,
102 LPARAM& lParam, MSGResult& aResult);
103 static bool IsComposing() { return IsComposingOnOurEditor(); }
104 static bool IsComposingOn(nsWindow* aWindow) {
105 return IsComposing() && IsComposingWindow(aWindow);
108 #ifdef DEBUG
110 * IsIMEAvailable() returns TRUE when current keyboard layout has IME.
111 * Otherwise, FALSE.
113 static bool IsIMEAvailable() { return !!::ImmIsIME(::GetKeyboardLayout(0)); }
114 #endif
116 // If aForce is TRUE, these methods doesn't check whether we have composition
117 // or not. If you don't set it to TRUE, these method doesn't commit/cancel
118 // the composition on uexpected window.
119 static void CommitComposition(nsWindow* aWindow, bool aForce = false);
120 static void CancelComposition(nsWindow* aWindow, bool aForce = false);
121 static void OnFocusChange(bool aFocus, nsWindow* aWindow);
122 static void OnUpdateComposition(nsWindow* aWindow);
123 static void OnSelectionChange(nsWindow* aWindow,
124 const IMENotification& aIMENotification,
125 bool aIsIMMActive);
127 static IMENotificationRequests GetIMENotificationRequests();
129 // Returns NS_SUCCESS_EVENT_CONSUMED if the mouse button event is consumed by
130 // IME. Otherwise, NS_OK.
131 static nsresult OnMouseButtonEvent(nsWindow* aWindow,
132 const IMENotification& aIMENotification);
134 #define DECL_IS_IME_ACTIVE(aReadableName) \
135 static bool Is##aReadableName##Active();
137 // Japanese IMEs
138 DECL_IS_IME_ACTIVE(ATOK2006)
139 DECL_IS_IME_ACTIVE(ATOK2007)
140 DECL_IS_IME_ACTIVE(ATOK2008)
141 DECL_IS_IME_ACTIVE(ATOK2009)
142 DECL_IS_IME_ACTIVE(ATOK2010)
143 DECL_IS_IME_ACTIVE(GoogleJapaneseInput)
144 DECL_IS_IME_ACTIVE(Japanist2003)
146 #undef DECL_IS_IME_ACTIVE
149 * IsActiveIMEInBlockList() returns true if we know active keyboard layout's
150 * IME has some crash bugs or something which make some damage to us. When
151 * this returns true, IMC shouldn't be associated with any windows.
153 static bool IsActiveIMEInBlockList();
155 protected:
156 static void EnsureHandlerInstance();
158 static bool IsComposingOnOurEditor();
159 static bool IsComposingWindow(nsWindow* aWindow);
161 static bool ShouldDrawCompositionStringOurselves();
162 static bool IsVerticalWritingSupported();
163 // aWindow can be nullptr if it's called without receiving WM_INPUTLANGCHANGE.
164 static void InitKeyboardLayout(nsWindow* aWindow, HKL aKeyboardLayout);
165 static UINT GetKeyboardCodePage();
168 * Checks whether the window is top level window of the composing window.
169 * In this method, the top level window means in all windows, not only in all
170 * OUR windows. I.e., if the aWindow is embedded, this always returns FALSE.
172 static bool IsTopLevelWindowOfComposition(nsWindow* aWindow);
174 static bool ProcessInputLangChangeMessage(nsWindow* aWindow, WPARAM wParam,
175 LPARAM lParam, MSGResult& aResult);
177 IMMHandler();
178 ~IMMHandler();
180 // On*() methods return true if the caller of message handler shouldn't do
181 // anything anymore. Otherwise, false.
182 static bool OnKeyDownEvent(nsWindow* aWindow, WPARAM wParam, LPARAM lParam,
183 MSGResult& aResult);
185 bool OnIMEStartComposition(nsWindow* aWindow, MSGResult& aResult);
186 bool OnIMEComposition(nsWindow* aWindow, WPARAM wParam, LPARAM lParam,
187 MSGResult& aResult);
188 bool OnIMEEndComposition(nsWindow* aWindow, MSGResult& aResult);
189 bool OnIMERequest(nsWindow* aWindow, WPARAM wParam, LPARAM lParam,
190 MSGResult& aResult);
191 bool OnChar(nsWindow* aWindow, WPARAM wParam, LPARAM lParam,
192 MSGResult& aResult);
193 void OnInputLangChange(nsWindow* aWindow, WPARAM wParam, LPARAM lParam,
194 MSGResult& aResult);
196 // These message handlers don't use instance members, we should not create
197 // the instance by the messages. So, they should be static.
198 static bool OnIMEChar(nsWindow* aWindow, WPARAM wParam, LPARAM lParam,
199 MSGResult& aResult);
200 static bool OnIMESetContext(nsWindow* aWindow, WPARAM wParam, LPARAM lParam,
201 MSGResult& aResult);
202 static bool OnIMECompositionFull(nsWindow* aWindow, MSGResult& aResult);
203 static bool OnIMENotify(nsWindow* aWindow, WPARAM wParam, LPARAM lParam,
204 MSGResult& aResult);
205 static bool OnIMESelect(nsWindow* aWindow, WPARAM wParam, LPARAM lParam,
206 MSGResult& aResult);
208 // The result of Handle* method mean "Processed" when it's TRUE.
209 void HandleStartComposition(nsWindow* aWindow, const IMEContext& aContext);
210 bool HandleComposition(nsWindow* aWindow, const IMEContext& aContext,
211 LPARAM lParam);
212 // If aCommitString is null, this commits composition with the latest
213 // dispatched data. Otherwise, commits composition with the value.
214 void HandleEndComposition(nsWindow* aWindow,
215 const nsAString* aCommitString = nullptr);
216 bool HandleReconvert(nsWindow* aWindow, LPARAM lParam, LRESULT* oResult);
217 bool HandleQueryCharPosition(nsWindow* aWindow, LPARAM lParam,
218 LRESULT* oResult);
219 bool HandleDocumentFeed(nsWindow* aWindow, LPARAM lParam, LRESULT* oResult);
222 * When a window's IME context is activating but we have composition on
223 * another window, we should commit our composition because IME context is
224 * shared by all our windows (including plug-ins).
225 * @param aWindow is a new activated window.
226 * If aWindow is our composing window, this method does nothing.
227 * Otherwise, this commits the composition on the previous window.
228 * If this method did commit a composition, this returns TRUE.
230 bool CommitCompositionOnPreviousWindow(nsWindow* aWindow);
233 * ResolveIMECaretPos
234 * Convert the caret rect of a composition event to another widget's
235 * coordinate system.
237 * @param aReferenceWidget The origin widget of aCursorRect.
238 * Typically, this is mReferenceWidget of the
239 * composing events. If the aCursorRect is in screen
240 * coordinates, set nullptr.
241 * @param aCursorRect The cursor rect.
242 * @param aNewOriginWidget aOutRect will be in this widget's coordinates. If
243 * this is nullptr, aOutRect will be in screen
244 * coordinates.
245 * @param aOutRect The converted cursor rect.
247 void ResolveIMECaretPos(nsIWidget* aReferenceWidget,
248 mozilla::LayoutDeviceIntRect& aCursorRect,
249 nsIWidget* aNewOriginWidget,
250 mozilla::LayoutDeviceIntRect& aOutRect);
252 bool ConvertToANSIString(const nsString& aStr, UINT aCodePage,
253 nsACString& aANSIStr);
255 bool SetIMERelatedWindowsPos(nsWindow* aWindow, const IMEContext& aContext);
257 * GetCharacterRectOfSelectedTextAt() returns character rect of the offset
258 * from the selection start or the start of composition string if there is
259 * a composition.
261 * @param aWindow The window which has focus.
262 * @param aOffset Offset from the selection start or the start of
263 * composition string when there is a composition.
264 * This must be in the selection range or
265 * the composition string.
266 * @param aCharRect The result.
267 * @param aWritingMode The writing mode of current selection. When this
268 * is nullptr, this assumes that the selection is in
269 * horizontal writing mode.
270 * @return true if this succeeded to retrieve the rect.
271 * Otherwise, false.
273 bool GetCharacterRectOfSelectedTextAt(
274 nsWindow* aWindow, uint32_t aOffset,
275 mozilla::LayoutDeviceIntRect& aCharRect,
276 mozilla::WritingMode* aWritingMode = nullptr);
278 * GetCaretRect() returns caret rect at current selection start.
280 * @param aWindow The window which has focus.
281 * @param aCaretRect The result.
282 * @param aWritingMode The writing mode of current selection. When this
283 * is nullptr, this assumes that the selection is in
284 * horizontal writing mode.
285 * @return true if this succeeded to retrieve the rect.
286 * Otherwise, false.
288 bool GetCaretRect(nsWindow* aWindow, mozilla::LayoutDeviceIntRect& aCaretRect,
289 mozilla::WritingMode* aWritingMode = nullptr);
290 void GetCompositionString(const IMEContext& aContext, DWORD aIndex,
291 nsAString& aCompositionString) const;
294 * AdjustCompositionFont() makes IME vertical writing mode if it's supported.
295 * If aForceUpdate is true, it will update composition font even if writing
296 * mode isn't being changed.
298 void AdjustCompositionFont(nsWindow* aWindow, const IMEContext& aContext,
299 const mozilla::WritingMode& aWritingMode,
300 bool aForceUpdate = false);
303 * MaybeAdjustCompositionFont() calls AdjustCompositionFont() when the
304 * locale of active IME is CJK. Note that this creates an instance even
305 * when there is no composition but the locale is CJK.
307 static void MaybeAdjustCompositionFont(
308 nsWindow* aWindow, const mozilla::WritingMode& aWritingMode,
309 bool aForceUpdate = false);
312 * Get the current target clause of composition string.
313 * If there are one or more characters whose attribute is ATTR_TARGET_*,
314 * this returns the first character's offset and its length.
315 * Otherwise, e.g., the all characters are ATTR_INPUT, this returns
316 * the composition string range because the all is the current target.
318 * aLength can be null (default), but aOffset must not be null.
320 * The aOffset value is offset in the contents. So, when you need offset
321 * in the composition string, you need to subtract mCompositionStart from it.
323 bool GetTargetClauseRange(uint32_t* aOffset, uint32_t* aLength = nullptr);
326 * DispatchEvent() dispatches aEvent if aWidget hasn't been destroyed yet.
328 static void DispatchEvent(nsWindow* aWindow, WidgetGUIEvent& aEvent);
331 * DispatchCompositionChangeEvent() dispatches eCompositionChange event
332 * with clause information (it'll be retrieved by CreateTextRangeArray()).
333 * I.e., this should be called only during composing. If a composition is
334 * being committed, only HandleCompositionEnd() should be called.
336 * @param aWindow The window which has the composition.
337 * @param aContext Native IME context which has the composition.
339 void DispatchCompositionChangeEvent(nsWindow* aWindow,
340 const IMEContext& aContext);
342 nsresult EnsureClauseArray(int32_t aCount);
343 nsresult EnsureAttributeArray(int32_t aCount);
346 * When WM_IME_CHAR is received and passed to DefWindowProc, we need to
347 * record the messages. In other words, we should record the messages
348 * when we receive WM_IME_CHAR on windowless plug-in (if we have focus,
349 * we always eat them). When focus is moved from a windowless plug-in to
350 * our window during composition, WM_IME_CHAR messages were received when
351 * the plug-in has focus. However, WM_CHAR messages are received after the
352 * plug-in lost focus. So, we need to ignore the WM_CHAR messages because
353 * they make unexpected text input events on us.
355 nsTArray<MSG> mPassedIMEChar;
357 bool IsIMECharRecordsEmpty() { return mPassedIMEChar.IsEmpty(); }
358 void ResetIMECharRecords() { mPassedIMEChar.Clear(); }
359 void DequeueIMECharRecords(WPARAM& wParam, LPARAM& lParam) {
360 MSG msg = mPassedIMEChar.ElementAt(0);
361 wParam = msg.wParam;
362 lParam = msg.lParam;
363 mPassedIMEChar.RemoveElementAt(0);
365 void EnqueueIMECharRecords(WPARAM wParam, LPARAM lParam) {
366 MSG msg;
367 msg.wParam = wParam;
368 msg.lParam = lParam;
369 mPassedIMEChar.AppendElement(msg);
372 TextEventDispatcher* GetTextEventDispatcherFor(nsWindow* aWindow);
374 nsWindow* mComposingWindow;
375 RefPtr<TextEventDispatcher> mDispatcher;
376 nsString mCompositionString;
377 nsTArray<uint32_t> mClauseArray;
378 nsTArray<uint8_t> mAttributeArray;
380 int32_t mCursorPosition;
381 uint32_t mCompositionStart;
383 // mContentSelection stores the latest selection data only when sHasFocus is
384 // true. Don't access mContentSelection directly. You should use
385 // GetContentSelectionWithQueryIfNothing() for getting proper state.
386 Maybe<ContentSelection> mContentSelection;
388 const Maybe<ContentSelection>& GetContentSelectionWithQueryIfNothing(
389 nsWindow* aWindow) {
390 // When IME has focus, mContentSelection is automatically updated by
391 // NOTIFY_IME_OF_SELECTION_CHANGE.
392 if (sHasFocus) {
393 if (mContentSelection.isNothing()) {
394 // But if this is the first access of mContentSelection, we need to
395 // query selection now.
396 mContentSelection = QueryContentSelection(aWindow);
398 return mContentSelection;
400 // Otherwise, i.e., While IME doesn't have focus, we cannot observe
401 // selection changes. So, in such case, we need to query selection
402 // when it's necessary.
403 static Maybe<ContentSelection> sTempContentSelection;
404 sTempContentSelection = QueryContentSelection(aWindow);
405 return sTempContentSelection;
409 * Query content selection on aWindow with WidgetQueryContent event.
411 static Maybe<ContentSelection> QueryContentSelection(nsWindow* aWindow);
413 bool mIsComposing;
415 static mozilla::WritingMode sWritingModeOfCompositionFont;
416 static nsString sIMEName;
417 static UINT sCodePage;
418 static DWORD sIMEProperty;
419 static DWORD sIMEUIProperty;
420 static bool sAssumeVerticalWritingModeNotSupported;
421 static bool sHasFocus;
424 } // namespace widget
425 } // namespace mozilla
427 #endif // IMMHandler_h_