Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/widget/windows/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 90 kB image not shown  

Quelle  IMMHandler.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sts=2 sw=2 et cin: */
/* 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/. */


#include "mozilla/Logging.h"

#include "IMMHandler.h"
#include "nsWindow.h"
#include "nsWindowDefs.h"
#include "WinIMEHandler.h"
#include "WinUtils.h"
#include "KeyboardLayout.h"
#include <algorithm>

#include "mozilla/CheckedInt.h"
#include "mozilla/MiscEvents.h"
#include "mozilla/TextEvents.h"
#include "mozilla/ToString.h"

#ifndef IME_PROP_ACCEPT_WIDE_VKEY
#  define IME_PROP_ACCEPT_WIDE_VKEY 0x20
#endif

//-------------------------------------------------------------------------
//
// from
// http://download.microsoft.com/download/6/0/9/60908e9e-d2c1-47db-98f6-216af76a235f/msime.h
// The document for this has been removed from MSDN...
//
//-------------------------------------------------------------------------

#define RWM_MOUSE TEXT("MSIMEMouseOperation")

#define IMEMOUSE_NONE 0x00  // no mouse button was pushed
#define IMEMOUSE_LDOWN 0x01
#define IMEMOUSE_RDOWN 0x02
#define IMEMOUSE_MDOWN 0x04
#define IMEMOUSE_WUP 0x10    // wheel up
#define IMEMOUSE_WDOWN 0x20  // wheel down

// For collecting other people's log, tell `MOZ_LOG=IMEHandler:4,sync`
// rather than `MOZ_LOG=IMEHandler:5,sync` since using `5` may create too
// big file.
// Therefore you shouldn't use `LogLevel::Verbose` for logging usual behavior.
extern mozilla::LazyLogModule gIMELog;

static const char* GetBoolName(bool aBool) { return aBool ? "true" : "false"; }

static void HandleSeparator(nsACString& aDesc) {
  if (!aDesc.IsEmpty()) {
    aDesc.AppendLiteral(" | ");
  }
}

class GetIMEGeneralPropertyName : public nsAutoCString {
 public:
  explicit GetIMEGeneralPropertyName(DWORD aFlags) {
    if (!aFlags) {
      AppendLiteral("no flags");
      return;
    }
    if (aFlags & IME_PROP_AT_CARET) {
      AppendLiteral("IME_PROP_AT_CARET");
    }
    if (aFlags & IME_PROP_SPECIAL_UI) {
      HandleSeparator(*this);
      AppendLiteral("IME_PROP_SPECIAL_UI");
    }
    if (aFlags & IME_PROP_CANDLIST_START_FROM_1) {
      HandleSeparator(*this);
      AppendLiteral("IME_PROP_CANDLIST_START_FROM_1");
    }
    if (aFlags & IME_PROP_UNICODE) {
      HandleSeparator(*this);
      AppendLiteral("IME_PROP_UNICODE");
    }
    if (aFlags & IME_PROP_COMPLETE_ON_UNSELECT) {
      HandleSeparator(*this);
      AppendLiteral("IME_PROP_COMPLETE_ON_UNSELECT");
    }
    if (aFlags & IME_PROP_ACCEPT_WIDE_VKEY) {
      HandleSeparator(*this);
      AppendLiteral("IME_PROP_ACCEPT_WIDE_VKEY");
    }
  }
  virtual ~GetIMEGeneralPropertyName() {}
};

class GetIMEUIPropertyName : public nsAutoCString {
 public:
  explicit GetIMEUIPropertyName(DWORD aFlags) {
    if (!aFlags) {
      AppendLiteral("no flags");
      return;
    }
    if (aFlags & UI_CAP_2700) {
      AppendLiteral("UI_CAP_2700");
    }
    if (aFlags & UI_CAP_ROT90) {
      HandleSeparator(*this);
      AppendLiteral("UI_CAP_ROT90");
    }
    if (aFlags & UI_CAP_ROTANY) {
      HandleSeparator(*this);
      AppendLiteral("UI_CAP_ROTANY");
    }
  }
  virtual ~GetIMEUIPropertyName() {}
};

class GetReconvertStringLog : public nsAutoCString {
 public:
  explicit GetReconvertStringLog(RECONVERTSTRING* aReconv) {
    AssignLiteral("{ dwSize=");
    AppendInt(static_cast<uint32_t>(aReconv->dwSize));
    AppendLiteral(", dwVersion=");
    AppendInt(static_cast<uint32_t>(aReconv->dwVersion));
    AppendLiteral(", dwStrLen=");
    AppendInt(static_cast<uint32_t>(aReconv->dwStrLen));
    AppendLiteral(", dwStrOffset=");
    AppendInt(static_cast<uint32_t>(aReconv->dwStrOffset));
    AppendLiteral(", dwCompStrLen=");
    AppendInt(static_cast<uint32_t>(aReconv->dwCompStrLen));
    AppendLiteral(", dwCompStrOffset=");
    AppendInt(static_cast<uint32_t>(aReconv->dwCompStrOffset));
    AppendLiteral(", dwTargetStrLen=");
    AppendInt(static_cast<uint32_t>(aReconv->dwTargetStrLen));
    AppendLiteral(", dwTargetStrOffset=");
    AppendInt(static_cast<uint32_t>(aReconv->dwTargetStrOffset));
    AppendLiteral(", result str=\"");
    if (aReconv->dwStrLen) {
      char16_t* strStart = reinterpret_cast<char16_t*>(
          reinterpret_cast<char*>(aReconv) + aReconv->dwStrOffset);
      nsDependentString str(strStart, aReconv->dwStrLen);
      Append(NS_ConvertUTF16toUTF8(str));
    }
    AppendLiteral("\" }");
  }
  virtual ~GetReconvertStringLog() {}
};

namespace mozilla {
namespace widget {

static IMMHandler* gIMMHandler = nullptr;

/******************************************************************************
 * IMEContext
 ******************************************************************************/


IMEContext::IMEContext(HWND aWnd) : mWnd(aWnd), mIMC(::ImmGetContext(aWnd)) {}

IMEContext::IMEContext(nsWindow* aWindowBase)
    : mWnd(aWindowBase->GetWindowHandle()),
      mIMC(::ImmGetContext(aWindowBase->GetWindowHandle())) {}

void IMEContext::Init(HWND aWnd) {
  Clear();
  mWnd = aWnd;
  mIMC = ::ImmGetContext(mWnd);
}

void IMEContext::Init(nsWindow* aWindowBase) {
  Init(aWindowBase->GetWindowHandle());
}

void IMEContext::Clear() {
  if (mWnd && mIMC) {
    ::ImmReleaseContext(mWnd, mIMC);
  }
  mWnd = nullptr;
  mIMC = nullptr;
}

/******************************************************************************
 * IMMHandler
 ******************************************************************************/


static UINT sWM_MSIME_MOUSE = 0;  // mouse message for MSIME 98/2000

MOZ_RUNINIT WritingMode IMMHandler::sWritingModeOfCompositionFont;
MOZ_RUNINIT nsString IMMHandler::sIMEName;
UINT IMMHandler::sCodePage = 0;
DWORD IMMHandler::sIMEProperty = 0;
DWORD IMMHandler::sIMEUIProperty = 0;
bool IMMHandler::sAssumeVerticalWritingModeNotSupported = false;
bool IMMHandler::sHasFocus = false;

#define IMPL_IS_IME_ACTIVE(aReadableName, aActualName) \
  bool IMMHandler::Is##aReadableName##Active() {       \
    return sIMEName.Equals(aActualName);               \
  }

IMPL_IS_IME_ACTIVE(ATOK2006, u"ATOK 2006")
IMPL_IS_IME_ACTIVE(ATOK2007, u"ATOK 2007")
IMPL_IS_IME_ACTIVE(ATOK2008, u"ATOK 2008")
IMPL_IS_IME_ACTIVE(ATOK2009, u"ATOK 2009")
IMPL_IS_IME_ACTIVE(ATOK2010, u"ATOK 2010")
// NOTE: Even on Windows for en-US, the name of Google Japanese Input is
//       written in Japanese.
IMPL_IS_IME_ACTIVE(GoogleJapaneseInput,
                   u"Google \x65E5\x672C\x8A9E\x5165\x529B "
                   u"IMM32 \x30E2\x30B8\x30E5\x30FC\x30EB")
IMPL_IS_IME_ACTIVE(Japanist2003, u"Japanist 2003")

#undef IMPL_IS_IME_ACTIVE

// static
bool IMMHandler::IsActiveIMEInBlockList() {
  if (sIMEName.IsEmpty()) {
    return false;
  }
#ifdef _WIN64
  // ATOK started to be TIP of TSF since 2011.  Older than it, i.e., ATOK 2010
  // and earlier have a lot of problems even for daily use.  Perhaps, the
  // reason is Win 8 has a lot of changes around IMM-IME support and TSF,
  // and ATOK 2010 is released earlier than Win 8.
  // ATOK 2006 crashes while converting a word with candidate window.
  // ATOK 2007 doesn't paint and resize suggest window and candidate window
  // correctly (showing white window or too big window).
  // ATOK 2008 and ATOK 2009 crash when user just opens their open state.
  // ATOK 2010 isn't installable newly on Win 7 or later, but we have a lot of
  // crash reports.
  if ((IsATOK2006Active() || IsATOK2007Active() || IsATOK2008Active() ||
       IsATOK2009Active() || IsATOK2010Active())) {
    return true;
  }
#endif  // #ifdef _WIN64
  return false;
}

// static
void IMMHandler::EnsureHandlerInstance() {
  if (!gIMMHandler) {
    gIMMHandler = new IMMHandler();
  }
}

// static
void IMMHandler::Initialize() {
  if (!sWM_MSIME_MOUSE) {
    sWM_MSIME_MOUSE = ::RegisterWindowMessage(RWM_MOUSE);
  }
  sAssumeVerticalWritingModeNotSupported = Preferences::GetBool(
      "intl.imm.vertical_writing.always_assume_not_supported"false);
  InitKeyboardLayout(nullptr, ::GetKeyboardLayout(0));
}

// static
void IMMHandler::Terminate() {
  if (!gIMMHandler) return;
  delete gIMMHandler;
  gIMMHandler = nullptr;
}

// static
bool IMMHandler::IsComposingOnOurEditor() {
  return gIMMHandler && gIMMHandler->mIsComposing;
}

// static
bool IMMHandler::IsComposingWindow(nsWindow* aWindow) {
  return gIMMHandler && gIMMHandler->mComposingWindow == aWindow;
}

// static
bool IMMHandler::IsTopLevelWindowOfComposition(nsWindow* aWindow) {
  if (!gIMMHandler || !gIMMHandler->mComposingWindow) {
    return false;
  }
  HWND wnd = gIMMHandler->mComposingWindow->GetWindowHandle();
  return WinUtils::GetTopLevelHWND(wnd, true) == aWindow->GetWindowHandle();
}

// static
bool IMMHandler::ShouldDrawCompositionStringOurselves() {
  // If current IME has special UI or its composition window should not
  // positioned to caret position, we should now draw composition string
  // ourselves.
  return !(sIMEProperty & IME_PROP_SPECIAL_UI) &&
         (sIMEProperty & IME_PROP_AT_CARET);
}

// static
bool IMMHandler::IsVerticalWritingSupported() {
  // Even if IME claims that they support vertical writing mode but it may not
  // support vertical writing mode for its candidate window.
  if (sAssumeVerticalWritingModeNotSupported) {
    return false;
  }
  // Google Japanese Input doesn't support vertical writing mode.  We should
  // return false if it's active IME.
  if (IsGoogleJapaneseInputActive()) {
    return false;
  }
  return !!(sIMEUIProperty & (UI_CAP_2700 | UI_CAP_ROT90 | UI_CAP_ROTANY));
}

// static
void IMMHandler::InitKeyboardLayout(nsWindow* aWindow, HKL aKeyboardLayout) {
  UINT IMENameLength = ::ImmGetDescriptionW(aKeyboardLayout, nullptr, 0);
  if (IMENameLength) {
    // Add room for the terminating null character
    sIMEName.SetLength(++IMENameLength);
    IMENameLength =
        ::ImmGetDescriptionW(aKeyboardLayout, sIMEName.get(), IMENameLength);
    // Adjust the length to ignore the terminating null character
    sIMEName.SetLength(IMENameLength);
  } else {
    sIMEName.Truncate();
  }

  WORD langID = LOWORD(aKeyboardLayout);
  ::GetLocaleInfoW(MAKELCID(langID, SORT_DEFAULT),
                   LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER,
                   (PWSTR)&sCodePage, sizeof(sCodePage) / sizeof(WCHAR));
  sIMEProperty = ::ImmGetProperty(aKeyboardLayout, IGP_PROPERTY);
  sIMEUIProperty = ::ImmGetProperty(aKeyboardLayout, IGP_UI);

  // If active IME is a TIP of TSF, we cannot retrieve the name with IMM32 API.
  // For hacking some bugs of some TIP, we should set an IME name from the
  // pref.
  if (sCodePage == 932 && sIMEName.IsEmpty()) {
    Preferences::GetString("intl.imm.japanese.assume_active_tip_name_as",
                           sIMEName);
  }

  // Whether the IME supports vertical writing mode might be changed or
  // some IMEs may need specific font for their UI.  Therefore, we should
  // update composition font forcibly here.
  if (aWindow) {
    MaybeAdjustCompositionFont(aWindow, sWritingModeOfCompositionFont, true);
  }

  MOZ_LOG(gIMELog, LogLevel::Info,
          ("IMMHandler::InitKeyboardLayout, aKeyboardLayout=%p (\"%s\"), "
           "sCodePage=%u, sIMEProperty=%s, sIMEUIProperty=%s",
           aKeyboardLayout, NS_ConvertUTF16toUTF8(sIMEName).get(), sCodePage,
           GetIMEGeneralPropertyName(sIMEProperty).get(),
           GetIMEUIPropertyName(sIMEUIProperty).get()));
}

// static
UINT IMMHandler::GetKeyboardCodePage() { return sCodePage; }

// static
IMENotificationRequests IMMHandler::GetIMENotificationRequests() {
  return IMENotificationRequests(
      IMENotificationRequests::NOTIFY_POSITION_CHANGE |
      IMENotificationRequests::NOTIFY_MOUSE_BUTTON_EVENT_ON_CHAR);
}

// used for checking the lParam of WM_IME_COMPOSITION
#define IS_COMPOSING_LPARAM(lParam) \
  ((lParam) & (GCS_COMPSTR | GCS_COMPATTR | GCS_COMPCLAUSE | GCS_CURSORPOS))
#define IS_COMMITTING_LPARAM(lParam) ((lParam) & GCS_RESULTSTR)
// Some IMEs (e.g., the standard IME for Korean) don't have caret position,
// then, we should not set caret position to compositionchange event.
#define NO_IME_CARET -1

IMMHandler::IMMHandler()
    : mComposingWindow(nullptr),
      mCursorPosition(NO_IME_CARET),
      mCompositionStart(0),
      mIsComposing(false) {
  MOZ_LOG(gIMELog, LogLevel::Debug, ("IMMHandler::IMMHandler is created"));
}

IMMHandler::~IMMHandler() {
  if (mIsComposing) {
    MOZ_LOG(
        gIMELog, LogLevel::Error,
        (" IMMHandler::~IMMHandler, ERROR, the instance is still composing"));
  }
  MOZ_LOG(gIMELog, LogLevel::Debug, ("IMMHandler::IMMHandler is destroyed"));
}

nsresult IMMHandler::EnsureClauseArray(int32_t aCount) {
  NS_ENSURE_ARG_MIN(aCount, 0);
  mClauseArray.SetCapacity(aCount + 32);
  return NS_OK;
}

nsresult IMMHandler::EnsureAttributeArray(int32_t aCount) {
  NS_ENSURE_ARG_MIN(aCount, 0);
  mAttributeArray.SetCapacity(aCount + 64);
  return NS_OK;
}

// static
void IMMHandler::CommitComposition(nsWindow* aWindow, bool aForce) {
  MOZ_LOG(gIMELog, LogLevel::Info,
          ("IMMHandler::CommitComposition, aForce=%s, aWindow=%p, hWnd=%p, "
           "mComposingWindow=%p%s",
           GetBoolName(aForce), aWindow, aWindow->GetWindowHandle(),
           gIMMHandler ? gIMMHandler->mComposingWindow : nullptr,
           gIMMHandler && gIMMHandler->mComposingWindow
               ? IsComposingOnOurEditor() ? " (composing on editor)"
                                          : " (composing on plug-in)"
               : ""));
  if (!aForce && !IsComposingWindow(aWindow)) {
    return;
  }

  IMEContext context(aWindow);
  bool associated = context.AssociateDefaultContext();
  MOZ_LOG(gIMELog, LogLevel::Info,
          (" IMMHandler::CommitComposition, associated=%s",
           GetBoolName(associated)));

  if (context.IsValid()) {
    ::ImmNotifyIME(context.get(), NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
    ::ImmNotifyIME(context.get(), NI_COMPOSITIONSTR, CPS_CANCEL, 0);
  }

  if (associated) {
    context.Disassociate();
  }
}

// static
void IMMHandler::CancelComposition(nsWindow* aWindow, bool aForce) {
  MOZ_LOG(gIMELog, LogLevel::Info,
          ("IMMHandler::CancelComposition, aForce=%s, aWindow=%p, hWnd=%p, "
           "mComposingWindow=%p%s",
           GetBoolName(aForce), aWindow, aWindow->GetWindowHandle(),
           gIMMHandler ? gIMMHandler->mComposingWindow : nullptr,
           gIMMHandler && gIMMHandler->mComposingWindow
               ? IsComposingOnOurEditor() ? " (composing on editor)"
                                          : " (composing on plug-in)"
               : ""));
  if (!aForce && !IsComposingWindow(aWindow)) {
    return;
  }

  IMEContext context(aWindow);
  bool associated = context.AssociateDefaultContext();
  MOZ_LOG(gIMELog, LogLevel::Info,
          (" IMMHandler::CancelComposition, associated=%s",
           GetBoolName(associated)));

  if (context.IsValid()) {
    ::ImmNotifyIME(context.get(), NI_COMPOSITIONSTR, CPS_CANCEL, 0);
  }

  if (associated) {
    context.Disassociate();
  }
}

// static
void IMMHandler::OnFocusChange(bool aFocus, nsWindow* aWindow) {
  MOZ_LOG(gIMELog, LogLevel::Info,
          ("IMMHandler::OnFocusChange(aFocus=%s, aWindow=%p), sHasFocus=%s, "
           "IsComposingWindow(aWindow)=%s, aWindow->Destroyed()=%s",
           GetBoolName(aFocus), aWindow, GetBoolName(sHasFocus),
           GetBoolName(IsComposingWindow(aWindow)),
           GetBoolName(aWindow->Destroyed())));

  if (!aFocus) {
    IMEHandler::MaybeDestroyNativeCaret();
    if (IsComposingWindow(aWindow) && aWindow->Destroyed()) {
      CancelComposition(aWindow);
    }
  }
  if (gIMMHandler) {
    gIMMHandler->mContentSelection.reset();
  }
  sHasFocus = aFocus;
}

// static
void IMMHandler::OnUpdateComposition(nsWindow* aWindow) {
  if (!gIMMHandler) {
    return;
  }

  IMEContext context(aWindow);
  gIMMHandler->SetIMERelatedWindowsPos(aWindow, context);
}

// static
void IMMHandler::OnSelectionChange(nsWindow* aWindow,
                                   const IMENotification& aIMENotification,
                                   bool aIsIMMActive) {
  if (!aIMENotification.mSelectionChangeData.mCausedByComposition &&
      aIsIMMActive) {
    MaybeAdjustCompositionFont(
        aWindow, aIMENotification.mSelectionChangeData.GetWritingMode());
  }
  // MaybeAdjustCompositionFont() may create gIMMHandler.  So, check it
  // after a call of MaybeAdjustCompositionFont().
  if (gIMMHandler) {
    gIMMHandler->mContentSelection =
        Some(ContentSelection(aIMENotification.mSelectionChangeData));
  }
}

// static
void IMMHandler::MaybeAdjustCompositionFont(nsWindow* aWindow,
                                            const WritingMode& aWritingMode,
                                            bool aForceUpdate) {
  switch (sCodePage) {
    case 932:  // Japanese Shift-JIS
    case 936:  // Simlified Chinese GBK
    case 949:  // Korean
    case 950:  // Traditional Chinese Big5
      EnsureHandlerInstance();
      break;
    default:
      // If there is no instance of nsIMM32Hander, we shouldn't waste footprint.
      if (!gIMMHandler) {
        return;
      }
  }

  // Like Navi-Bar of ATOK, some IMEs may require proper composition font even
  // before sending WM_IME_STARTCOMPOSITION.
  IMEContext context(aWindow);
  gIMMHandler->AdjustCompositionFont(aWindow, context, aWritingMode,
                                     aForceUpdate);
}

// static
bool IMMHandler::ProcessInputLangChangeMessage(nsWindow* aWindow, WPARAM wParam,
                                               LPARAM lParam,
                                               MSGResult& aResult) {
  aResult.mResult = 0;
  aResult.mConsumed = false;
  // We don't need to create the instance of the handler here.
  if (gIMMHandler) {
    gIMMHandler->OnInputLangChange(aWindow, wParam, lParam, aResult);
  }
  InitKeyboardLayout(aWindow, reinterpret_cast<HKL>(lParam));
  // We can release the instance here, because the instance may be never
  // used. E.g., the new keyboard layout may not use IME, or it may use TSF.
  Terminate();
  // Don't return as "processed", the messages should be processed on nsWindow
  // too.
  return false;
}

// static
bool IMMHandler::ProcessMessage(nsWindow* aWindow, UINT msg, WPARAM& wParam,
                                LPARAM& lParam, MSGResult& aResult) {
  // XXX We store the composing window in mComposingWindow.  If IME messages are
  // sent to different window, we should commit the old transaction.  And also
  // if the new window handle is not focused, probably, we should not start
  // the composition, however, such case should not be, it's just bad scenario.

  aResult.mResult = 0;
  switch (msg) {
    case WM_INPUTLANGCHANGE:
      return ProcessInputLangChangeMessage(aWindow, wParam, lParam, aResult);
    case WM_IME_STARTCOMPOSITION:
      EnsureHandlerInstance();
      return gIMMHandler->OnIMEStartComposition(aWindow, aResult);
    case WM_IME_COMPOSITION:
      EnsureHandlerInstance();
      return gIMMHandler->OnIMEComposition(aWindow, wParam, lParam, aResult);
    case WM_IME_ENDCOMPOSITION:
      EnsureHandlerInstance();
      return gIMMHandler->OnIMEEndComposition(aWindow, aResult);
    case WM_IME_CHAR:
      return OnIMEChar(aWindow, wParam, lParam, aResult);
    case WM_IME_NOTIFY:
      return OnIMENotify(aWindow, wParam, lParam, aResult);
    case WM_IME_REQUEST:
      EnsureHandlerInstance();
      return gIMMHandler->OnIMERequest(aWindow, wParam, lParam, aResult);
    case WM_IME_SELECT:
      return OnIMESelect(aWindow, wParam, lParam, aResult);
    case WM_IME_SETCONTEXT:
      return OnIMESetContext(aWindow, wParam, lParam, aResult);
    case WM_KEYDOWN:
      return OnKeyDownEvent(aWindow, wParam, lParam, aResult);
    case WM_CHAR:
      if (!gIMMHandler) {
        return false;
      }
      return gIMMHandler->OnChar(aWindow, wParam, lParam, aResult);
    default:
      return false;
  };
}

/****************************************************************************
 * message handlers
 ****************************************************************************/


void IMMHandler::OnInputLangChange(nsWindow* aWindow, WPARAM wParam,
                                   LPARAM lParam, MSGResult& aResult) {
  MOZ_LOG(gIMELog, LogLevel::Info,
          ("IMMHandler::OnInputLangChange, hWnd=%p, wParam=%08zx, "
           "lParam=%08" PRIxLPTR,
           aWindow->GetWindowHandle(), wParam, lParam));

  aWindow->NotifyIME(REQUEST_TO_COMMIT_COMPOSITION);
  NS_ASSERTION(!mIsComposing, "ResetInputState failed");

  if (mIsComposing) {
    HandleEndComposition(aWindow);
  }

  aResult.mConsumed = false;
}

bool IMMHandler::OnIMEStartComposition(nsWindow* aWindow, MSGResult& aResult) {
  MOZ_LOG(gIMELog, LogLevel::Info,
          ("IMMHandler::OnIMEStartComposition, hWnd=%p, mIsComposing=%s",
           aWindow->GetWindowHandle(), GetBoolName(mIsComposing)));
  aResult.mConsumed = ShouldDrawCompositionStringOurselves();
  if (mIsComposing) {
    NS_WARNING("Composition has been already started");
    return true;
  }

  IMEContext context(aWindow);
  HandleStartComposition(aWindow, context);
  return true;
}

bool IMMHandler::OnIMEComposition(nsWindow* aWindow, WPARAM wParam,
                                  LPARAM lParam, MSGResult& aResult) {
  MOZ_LOG(
      gIMELog, LogLevel::Info,
      ("IMMHandler::OnIMEComposition, hWnd=%p, lParam=%08" PRIxLPTR
       ", mIsComposing=%s, "
       "GCS_RESULTSTR=%s, GCS_COMPSTR=%s, GCS_COMPATTR=%s, GCS_COMPCLAUSE=%s, "
       "GCS_CURSORPOS=%s,",
       aWindow->GetWindowHandle(), lParam, GetBoolName(mIsComposing),
       GetBoolName(lParam & GCS_RESULTSTR), GetBoolName(lParam & GCS_COMPSTR),
       GetBoolName(lParam & GCS_COMPATTR), GetBoolName(lParam & GCS_COMPCLAUSE),
       GetBoolName(lParam & GCS_CURSORPOS)));

  IMEContext context(aWindow);
  aResult.mConsumed = HandleComposition(aWindow, context, lParam);
  return true;
}

bool IMMHandler::OnIMEEndComposition(nsWindow* aWindow, MSGResult& aResult) {
  MOZ_LOG(gIMELog, LogLevel::Info,
          ("IMMHandler::OnIMEEndComposition, hWnd=%p, mIsComposing=%s",
           aWindow->GetWindowHandle(), GetBoolName(mIsComposing)));

  aResult.mConsumed = ShouldDrawCompositionStringOurselves();
  if (!mIsComposing) {
    return true;
  }

  // Korean IME posts WM_IME_ENDCOMPOSITION first when we hit space during
  // composition. Then, we should ignore the message and commit the composition
  // string at following WM_IME_COMPOSITION.
  MSG compositionMsg;
  if (WinUtils::PeekMessage(&compositionMsg, aWindow->GetWindowHandle(),
                            WM_IME_STARTCOMPOSITION, WM_IME_COMPOSITION,
                            PM_NOREMOVE) &&
      compositionMsg.message == WM_IME_COMPOSITION &&
      IS_COMMITTING_LPARAM(compositionMsg.lParam)) {
    MOZ_LOG(gIMELog, LogLevel::Info,
            (" IMMHandler::OnIMEEndComposition, WM_IME_ENDCOMPOSITION is "
             "followed by WM_IME_COMPOSITION, ignoring the message..."));
    return true;
  }

  // Otherwise, e.g., ChangJie doesn't post WM_IME_COMPOSITION before
  // WM_IME_ENDCOMPOSITION when composition string becomes empty.
  // Then, we should dispatch a compositionupdate event, a compositionchange
  // event and a compositionend event.
  // XXX Shouldn't we dispatch the compositionchange event with actual or
  //     latest composition string?
  MOZ_LOG(gIMELog, LogLevel::Info,
          (" IMMHandler::OnIMEEndComposition, mCompositionString=\"%s\"%s",
           NS_ConvertUTF16toUTF8(mCompositionString).get(),
           mCompositionString.IsEmpty() ? "" : ", but canceling it..."));

  HandleEndComposition(aWindow, &EmptyString());

  return true;
}

// static
bool IMMHandler::OnIMEChar(nsWindow* aWindow, WPARAM wParam, LPARAM lParam,
                           MSGResult& aResult) {
  MOZ_LOG(gIMELog, LogLevel::Info,
          ("IMMHandler::OnIMEChar, hWnd=%p, char=%08zx",
           aWindow->GetWindowHandle(), wParam));

  // We don't need to fire any compositionchange events from here. This method
  // will be called when the composition string of the current IME is not drawn
  // by us and some characters are committed. In that case, the committed
  // string was processed in nsWindow::OnIMEComposition already.

  // We need to consume the message so that Windows don't send two WM_CHAR msgs
  aResult.mConsumed = true;
  return true;
}

// static
bool IMMHandler::OnIMECompositionFull(nsWindow* aWindow, MSGResult& aResult) {
  MOZ_LOG(gIMELog, LogLevel::Info,
          ("IMMHandler::OnIMECompositionFull, hWnd=%p",
           aWindow->GetWindowHandle()));

  // not implement yet
  aResult.mConsumed = false;
  return true;
}

// static
bool IMMHandler::OnIMENotify(nsWindow* aWindow, WPARAM wParam, LPARAM lParam,
                             MSGResult& aResult) {
  switch (wParam) {
    case IMN_CHANGECANDIDATE:
      MOZ_LOG(gIMELog, LogLevel::Info,
              ("IMMHandler::OnIMENotify, hWnd=%p, IMN_CHANGECANDIDATE, "
               "lParam=%08" PRIxLPTR,
               aWindow->GetWindowHandle(), lParam));
      break;
    case IMN_CLOSECANDIDATE:
      MOZ_LOG(gIMELog, LogLevel::Info,
              ("IMMHandler::OnIMENotify, hWnd=%p, IMN_CLOSECANDIDATE, "
               "lParam=%08" PRIxLPTR,
               aWindow->GetWindowHandle(), lParam));
      break;
    case IMN_CLOSESTATUSWINDOW:
      MOZ_LOG(gIMELog, LogLevel::Info,
              ("IMMHandler::OnIMENotify, hWnd=%p, IMN_CLOSESTATUSWINDOW",
               aWindow->GetWindowHandle()));
      break;
    case IMN_GUIDELINE:
      MOZ_LOG(gIMELog, LogLevel::Info,
              ("IMMHandler::OnIMENotify, hWnd=%p, IMN_GUIDELINE",
               aWindow->GetWindowHandle()));
      break;
    case IMN_OPENCANDIDATE:
      MOZ_LOG(gIMELog, LogLevel::Info,
              ("IMMHandler::OnIMENotify, hWnd=%p, IMN_OPENCANDIDATE, "
               "lParam=%08" PRIxLPTR,
               aWindow->GetWindowHandle(), lParam));
      break;
    case IMN_OPENSTATUSWINDOW:
      MOZ_LOG(gIMELog, LogLevel::Info,
              ("IMMHandler::OnIMENotify, hWnd=%p, IMN_OPENSTATUSWINDOW",
               aWindow->GetWindowHandle()));
      break;
    case IMN_SETCANDIDATEPOS:
      MOZ_LOG(gIMELog, LogLevel::Info,
              ("IMMHandler::OnIMENotify, hWnd=%p, IMN_SETCANDIDATEPOS, "
               "lParam=%08" PRIxLPTR,
               aWindow->GetWindowHandle(), lParam));
      break;
    case IMN_SETCOMPOSITIONFONT:
      MOZ_LOG(gIMELog, LogLevel::Info,
              ("IMMHandler::OnIMENotify, hWnd=%p, IMN_SETCOMPOSITIONFONT",
               aWindow->GetWindowHandle()));
      break;
    case IMN_SETCOMPOSITIONWINDOW:
      MOZ_LOG(gIMELog, LogLevel::Info,
              ("IMMHandler::OnIMENotify, hWnd=%p, IMN_SETCOMPOSITIONWINDOW",
               aWindow->GetWindowHandle()));
      break;
    case IMN_SETCONVERSIONMODE:
      MOZ_LOG(gIMELog, LogLevel::Info,
              ("IMMHandler::OnIMENotify, hWnd=%p, IMN_SETCONVERSIONMODE",
               aWindow->GetWindowHandle()));
      break;
    case IMN_SETOPENSTATUS:
      MOZ_LOG(gIMELog, LogLevel::Info,
              ("IMMHandler::OnIMENotify, hWnd=%p, IMN_SETOPENSTATUS",
               aWindow->GetWindowHandle()));
      break;
    case IMN_SETSENTENCEMODE:
      MOZ_LOG(gIMELog, LogLevel::Info,
              ("IMMHandler::OnIMENotify, hWnd=%p, IMN_SETSENTENCEMODE",
               aWindow->GetWindowHandle()));
      break;
    case IMN_SETSTATUSWINDOWPOS:
      MOZ_LOG(gIMELog, LogLevel::Info,
              ("IMMHandler::OnIMENotify, hWnd=%p, IMN_SETSTATUSWINDOWPOS",
               aWindow->GetWindowHandle()));
      break;
    case IMN_PRIVATE:
      MOZ_LOG(gIMELog, LogLevel::Info,
              ("IMMHandler::OnIMENotify, hWnd=%p, IMN_PRIVATE",
               aWindow->GetWindowHandle()));
      break;
  }

  // not implement yet
  aResult.mConsumed = false;
  return true;
}

bool IMMHandler::OnIMERequest(nsWindow* aWindow, WPARAM wParam, LPARAM lParam,
                              MSGResult& aResult) {
  switch (wParam) {
    case IMR_RECONVERTSTRING:
      MOZ_LOG(gIMELog, LogLevel::Info,
              ("IMMHandler::OnIMERequest, hWnd=%p, IMR_RECONVERTSTRING",
               aWindow->GetWindowHandle()));
      aResult.mConsumed = HandleReconvert(aWindow, lParam, &aResult.mResult);
      return true;
    case IMR_QUERYCHARPOSITION:
      MOZ_LOG(gIMELog, LogLevel::Info,
              ("IMMHandler::OnIMERequest, hWnd=%p, IMR_QUERYCHARPOSITION",
               aWindow->GetWindowHandle()));
      aResult.mConsumed =
          HandleQueryCharPosition(aWindow, lParam, &aResult.mResult);
      return true;
    case IMR_DOCUMENTFEED:
      MOZ_LOG(gIMELog, LogLevel::Info,
              ("IMMHandler::OnIMERequest, hWnd=%p, IMR_DOCUMENTFEED",
               aWindow->GetWindowHandle()));
      aResult.mConsumed = HandleDocumentFeed(aWindow, lParam, &aResult.mResult);
      return true;
    default:
      MOZ_LOG(gIMELog, LogLevel::Info,
              ("IMMHandler::OnIMERequest, hWnd=%p, wParam=%08zx",
               aWindow->GetWindowHandle(), wParam));
      aResult.mConsumed = false;
      return true;
  }
}

// static
bool IMMHandler::OnIMESelect(nsWindow* aWindow, WPARAM wParam, LPARAM lParam,
                             MSGResult& aResult) {
  MOZ_LOG(
      gIMELog, LogLevel::Info,
      ("IMMHandler::OnIMESelect, hWnd=%p, wParam=%08zx, lParam=%08" PRIxLPTR,
       aWindow->GetWindowHandle(), wParam, lParam));

  // not implement yet
  aResult.mConsumed = false;
  return true;
}

// static
bool IMMHandler::OnIMESetContext(nsWindow* aWindow, WPARAM wParam,
                                 LPARAM lParam, MSGResult& aResult) {
  MOZ_LOG(gIMELog, LogLevel::Info,
          ("IMMHandler::OnIMESetContext, hWnd=%p, %s, lParam=%08" PRIxLPTR,
           aWindow->GetWindowHandle(), wParam ? "Active" : "Deactive", lParam));

  aResult.mConsumed = false;

  // NOTE: If the aWindow is top level window of the composing window because
  // when a window on deactive window gets focus, WM_IME_SETCONTEXT (wParam is
  // TRUE) is sent to the top level window first.  After that,
  // WM_IME_SETCONTEXT (wParam is FALSE) is sent to the top level window.
  // Finally, WM_IME_SETCONTEXT (wParam is TRUE) is sent to the focused window.
  // The top level window never becomes composing window, so, we can ignore
  // the WM_IME_SETCONTEXT on the top level window.
  if (IsTopLevelWindowOfComposition(aWindow)) {
    MOZ_LOG(gIMELog, LogLevel::Info,
            (" IMMHandler::OnIMESetContext, hWnd=%p is top level window",
             aWindow->GetWindowHandle()));
    return true;
  }

  // When IME context is activating on another window,
  // we should commit the old composition on the old window.
  bool cancelComposition = false;
  if (wParam && gIMMHandler) {
    cancelComposition = gIMMHandler->CommitCompositionOnPreviousWindow(aWindow);
  }

  if (wParam && (lParam & ISC_SHOWUICOMPOSITIONWINDOW) &&
      ShouldDrawCompositionStringOurselves()) {
    MOZ_LOG(gIMELog, LogLevel::Info,
            (" IMMHandler::OnIMESetContext, ISC_SHOWUICOMPOSITIONWINDOW is "
             "removed"));
    lParam &= ~ISC_SHOWUICOMPOSITIONWINDOW;
  }

  // We should sent WM_IME_SETCONTEXT to the DefWndProc here because the
  // ancestor windows shouldn't receive this message.  If they receive the
  // message, we cannot know whether which window is the target of the message.
  aResult.mResult = ::DefWindowProc(aWindow->GetWindowHandle(),
                                    WM_IME_SETCONTEXT, wParam, lParam);

  // Cancel composition on the new window if we committed our composition on
  // another window.
  if (cancelComposition) {
    CancelComposition(aWindow, true);
  }

  aResult.mConsumed = true;
  return true;
}

bool IMMHandler::OnChar(nsWindow* aWindow, WPARAM wParam, LPARAM lParam,
                        MSGResult& aResult) {
  // The return value must be same as aResult.mConsumed because only when we
  // consume the message, the caller shouldn't do anything anymore but
  // otherwise, the caller should handle the message.
  aResult.mConsumed = false;
  if (IsIMECharRecordsEmpty()) {
    return aResult.mConsumed;
  }
  WPARAM recWParam;
  LPARAM recLParam;
  DequeueIMECharRecords(recWParam, recLParam);
  MOZ_LOG(
      gIMELog, LogLevel::Info,
      ("IMMHandler::OnChar, aWindow=%p, wParam=%08zx, lParam=%08" PRIxLPTR ", "
       "recorded: wParam=%08zx, lParam=%08" PRIxLPTR,
       aWindow->GetWindowHandle(), wParam, lParam, recWParam, recLParam));
  // If an unexpected char message comes, we should reset the records,
  // of course, this shouldn't happen.
  if (recWParam != wParam || recLParam != lParam) {
    ResetIMECharRecords();
    return aResult.mConsumed;
  }
  // Eat the char message which is caused by WM_IME_CHAR because we should
  // have processed the IME messages, so, this message could be come from
  // a windowless plug-in.
  aResult.mConsumed = true;
  return aResult.mConsumed;
}

/****************************************************************************
 * others
 ****************************************************************************/


TextEventDispatcher* IMMHandler::GetTextEventDispatcherFor(nsWindow* aWindow) {
  return aWindow == mComposingWindow && mDispatcher
             ? mDispatcher.get()
             : aWindow->GetTextEventDispatcher();
}

void IMMHandler::HandleStartComposition(nsWindow* aWindow,
                                        const IMEContext& aContext) {
  MOZ_ASSERT(!mIsComposing,
             "HandleStartComposition is called but mIsComposing is TRUE");

  const Maybe<ContentSelection>& contentSelection =
      GetContentSelectionWithQueryIfNothing(aWindow);
  if (contentSelection.isNothing()) {
    MOZ_LOG(gIMELog, LogLevel::Error,
            (" IMMHandler::HandleStartComposition, FAILED, due to "
             "Selection::GetContentSelectionWithQueryIfNothing() failure"));
    return;
  }
  if (!contentSelection->HasRange()) {
    MOZ_LOG(gIMELog, LogLevel::Error,
            (" IMMHandler::HandleStartComposition, FAILED, due to "
             "there is no selection"));
    return;
  }

  AdjustCompositionFont(aWindow, aContext, contentSelection->WritingModeRef());

  mCompositionStart = contentSelection->OffsetAndDataRef().StartOffset();
  mCursorPosition = NO_IME_CARET;

  RefPtr<TextEventDispatcher> dispatcher = GetTextEventDispatcherFor(aWindow);
  nsresult rv = dispatcher->BeginNativeInputTransaction();
  if (NS_WARN_IF(NS_FAILED(rv))) {
    MOZ_LOG(gIMELog, LogLevel::Error,
            (" IMMHandler::HandleStartComposition, FAILED due to "
             "TextEventDispatcher::BeginNativeInputTransaction() failure"));
    return;
  }
  WidgetEventTime eventTime = aWindow->CurrentMessageWidgetEventTime();
  nsEventStatus status;
  rv = dispatcher->StartComposition(status, &eventTime);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    MOZ_LOG(gIMELog, LogLevel::Error,
            (" IMMHandler::HandleStartComposition, FAILED, due to "
             "TextEventDispatcher::StartComposition() failure"));
    return;
  }

  mIsComposing = true;
  mComposingWindow = aWindow;
  mDispatcher = dispatcher;

  MOZ_LOG(gIMELog, LogLevel::Info,
          ("IMMHandler::HandleStartComposition, START composition, "
           "mCompositionStart=%u",
           mCompositionStart));
}

bool IMMHandler::HandleComposition(nsWindow* aWindow,
                                   const IMEContext& aContext, LPARAM lParam) {
  // for bug #60050
  // MS-IME 95/97/98/2000 may send WM_IME_COMPOSITION with non-conversion
  // mode before it send WM_IME_STARTCOMPOSITION.
  // However, ATOK sends a WM_IME_COMPOSITION before WM_IME_STARTCOMPOSITION,
  // and if we access ATOK via some APIs, ATOK will sometimes fail to
  // initialize its state.  If WM_IME_STARTCOMPOSITION is already in the
  // message queue, we should ignore the strange WM_IME_COMPOSITION message and
  // skip to the next.  So, we should look for next composition message
  // (WM_IME_STARTCOMPOSITION or WM_IME_ENDCOMPOSITION or WM_IME_COMPOSITION),
  // and if it's WM_IME_STARTCOMPOSITION, and one more next composition message
  // is WM_IME_COMPOSITION, current IME is ATOK, probably.  Otherwise, we
  // should start composition forcibly.
  if (!mIsComposing) {
    MSG msg1, msg2;
    HWND wnd = aWindow->GetWindowHandle();
    if (WinUtils::PeekMessage(&msg1, wnd, WM_IME_STARTCOMPOSITION,
                              WM_IME_COMPOSITION, PM_NOREMOVE) &&
        msg1.message == WM_IME_STARTCOMPOSITION &&
        WinUtils::PeekMessage(&msg2, wnd, WM_IME_ENDCOMPOSITION,
                              WM_IME_COMPOSITION, PM_NOREMOVE) &&
        msg2.message == WM_IME_COMPOSITION) {
      MOZ_LOG(gIMELog, LogLevel::Info,
              ("IMMHandler::HandleComposition, Ignores due to find a "
               "WM_IME_STARTCOMPOSITION"));
      return ShouldDrawCompositionStringOurselves();
    }
  }

  bool startCompositionMessageHasBeenSent = mIsComposing;

  //
  // This catches a fixed result
  //
  if (IS_COMMITTING_LPARAM(lParam)) {
    if (!mIsComposing) {
      HandleStartComposition(aWindow, aContext);
    }

    GetCompositionString(aContext, GCS_RESULTSTR, mCompositionString);

    MOZ_LOG(gIMELog, LogLevel::Info,
            ("IMMHandler::HandleComposition, GCS_RESULTSTR"));

    HandleEndComposition(aWindow, &mCompositionString);

    if (!IS_COMPOSING_LPARAM(lParam)) {
      return ShouldDrawCompositionStringOurselves();
    }
  }

  //
  // This provides us with a composition string
  //
  if (!mIsComposing) {
    HandleStartComposition(aWindow, aContext);
  }

  //--------------------------------------------------------
  // 1. Get GCS_COMPSTR
  //--------------------------------------------------------
  MOZ_LOG(gIMELog, LogLevel::Info,
          ("IMMHandler::HandleComposition, GCS_COMPSTR"));

  nsAutoString previousCompositionString(mCompositionString);
  GetCompositionString(aContext, GCS_COMPSTR, mCompositionString);

  if (!IS_COMPOSING_LPARAM(lParam)) {
    MOZ_LOG(
        gIMELog, LogLevel::Info,
        (" IMMHandler::HandleComposition, lParam doesn't indicate composing, "
         "mCompositionString=\"%s\", previousCompositionString=\"%s\"",
         NS_ConvertUTF16toUTF8(mCompositionString).get(),
         NS_ConvertUTF16toUTF8(previousCompositionString).get()));

    // If composition string isn't changed, we can trust the lParam.
    // So, we need to do nothing.
    if (previousCompositionString == mCompositionString) {
      return ShouldDrawCompositionStringOurselves();
    }

    // IME may send WM_IME_COMPOSITION without composing lParam values
    // when composition string becomes empty (e.g., using Backspace key).
    // If composition string is empty, we should dispatch a compositionchange
    // event with empty string and clear the clause information.
    if (mCompositionString.IsEmpty()) {
      mClauseArray.Clear();
      mAttributeArray.Clear();
      mCursorPosition = 0;
      DispatchCompositionChangeEvent(aWindow, aContext);
      return ShouldDrawCompositionStringOurselves();
    }

    // Otherwise, we cannot trust the lParam value.  We might need to
    // dispatch compositionchange event with the latest composition string
    // information.
  }

  // See https://bugzilla.mozilla.org/show_bug.cgi?id=296339
  if (mCompositionString.IsEmpty() && !startCompositionMessageHasBeenSent) {
    // In this case, maybe, the sender is MSPinYin. That sends *only*
    // WM_IME_COMPOSITION with GCS_COMP* and GCS_RESULT* when
    // user inputted the Chinese full stop. So, that doesn't send
    // WM_IME_STARTCOMPOSITION and WM_IME_ENDCOMPOSITION.
    // If WM_IME_STARTCOMPOSITION was not sent and the composition
    // string is null (it indicates the composition transaction ended),
    // WM_IME_ENDCOMPOSITION may not be sent. If so, we cannot run
    // HandleEndComposition() in other place.
    MOZ_LOG(gIMELog, LogLevel::Info,
            (" IMMHandler::HandleComposition, Aborting GCS_COMPSTR"));
    HandleEndComposition(aWindow);
    return IS_COMMITTING_LPARAM(lParam);
  }

  //--------------------------------------------------------
  // 2. Get GCS_COMPCLAUSE
  //--------------------------------------------------------
  long clauseArrayLength =
      ::ImmGetCompositionStringW(aContext.get(), GCS_COMPCLAUSE, nullptr, 0);
  clauseArrayLength /= sizeof(uint32_t);

  if (clauseArrayLength > 0) {
    nsresult rv = EnsureClauseArray(clauseArrayLength);
    NS_ENSURE_SUCCESS(rv, false);

    // Intelligent ABC IME (Simplified Chinese IME, the code page is 936)
    // will crash in ImmGetCompositionStringW for GCS_COMPCLAUSE (bug 424663).
    // See comment 35 of the bug for the detail. Therefore, we should use A
    // API for it, however, we should not kill Unicode support on all IMEs.
    bool useA_API = !(sIMEProperty & IME_PROP_UNICODE);

    MOZ_LOG(gIMELog, LogLevel::Info,
            (" IMMHandler::HandleComposition, GCS_COMPCLAUSE, useA_API=%s",
             useA_API ? "TRUE" : "FALSE"));

    long clauseArrayLength2 =
        useA_API ? ::ImmGetCompositionStringA(
                       aContext.get(), GCS_COMPCLAUSE, mClauseArray.Elements(),
                       mClauseArray.Capacity() * sizeof(uint32_t))
                 : ::ImmGetCompositionStringW(
                       aContext.get(), GCS_COMPCLAUSE, mClauseArray.Elements(),
                       mClauseArray.Capacity() * sizeof(uint32_t));
    clauseArrayLength2 /= sizeof(uint32_t);

    if (clauseArrayLength != clauseArrayLength2) {
      MOZ_LOG(gIMELog, LogLevel::Info,
              (" IMMHandler::HandleComposition, GCS_COMPCLAUSE, "
               "clauseArrayLength=%ld but clauseArrayLength2=%ld",
               clauseArrayLength, clauseArrayLength2));
      if (clauseArrayLength > clauseArrayLength2)
        clauseArrayLength = clauseArrayLength2;
    }

    if (useA_API && clauseArrayLength > 0) {
      // Convert each values of sIMECompClauseArray. The values mean offset of
      // the clauses in ANSI string. But we need the values in Unicode string.
      nsAutoCString compANSIStr;
      if (ConvertToANSIString(mCompositionString, GetKeyboardCodePage(),
                              compANSIStr)) {
        uint32_t maxlen = compANSIStr.Length();
        mClauseArray.SetLength(clauseArrayLength);
        mClauseArray[0] = 0;  // first value must be 0
        for (int32_t i = 1; i < clauseArrayLength; i++) {
          uint32_t len = std::min(mClauseArray[i], maxlen);
          mClauseArray[i] =
              ::MultiByteToWideChar(GetKeyboardCodePage(), MB_PRECOMPOSED,
                                    (LPCSTR)compANSIStr.get(), len, nullptr, 0);
        }
      }
    }
  }
  // compClauseArrayLength may be negative. I.e., ImmGetCompositionStringW
  // may return an error code.
  mClauseArray.SetLength(std::max<long>(0, clauseArrayLength));

  MOZ_LOG(gIMELog, LogLevel::Info,
          (" IMMHandler::HandleComposition, GCS_COMPCLAUSE, mClauseLength=%zu",
           mClauseArray.Length()));

  //--------------------------------------------------------
  // 3. Get GCS_COMPATTR
  //--------------------------------------------------------
  // This provides us with the attribute string necessary
  // for doing hiliting
  long attrArrayLength =
      ::ImmGetCompositionStringW(aContext.get(), GCS_COMPATTR, nullptr, 0);
  attrArrayLength /= sizeof(uint8_t);

  if (attrArrayLength > 0) {
    nsresult rv = EnsureAttributeArray(attrArrayLength);
    NS_ENSURE_SUCCESS(rv, false);
    attrArrayLength = ::ImmGetCompositionStringW(
        aContext.get(), GCS_COMPATTR, mAttributeArray.Elements(),
        mAttributeArray.Capacity() * sizeof(uint8_t));
  }

  // attrStrLen may be negative. I.e., ImmGetCompositionStringW may return an
  // error code.
  mAttributeArray.SetLength(std::max<long>(0, attrArrayLength));

  MOZ_LOG(
      gIMELog, LogLevel::Info,
      (" IMMHandler::HandleComposition, GCS_COMPATTR, mAttributeLength=%zu",
       mAttributeArray.Length()));

  //--------------------------------------------------------
  // 4. Get GCS_CURSOPOS
  //--------------------------------------------------------
  // Some IMEs (e.g., the standard IME for Korean) don't have caret position.
  if (lParam & GCS_CURSORPOS) {
    mCursorPosition =
        ::ImmGetCompositionStringW(aContext.get(), GCS_CURSORPOS, nullptr, 0);
    if (mCursorPosition < 0) {
      mCursorPosition = NO_IME_CARET;  // The result is error
    }
  } else {
    mCursorPosition = NO_IME_CARET;
  }

  NS_ASSERTION(mCursorPosition <= (long)mCompositionString.Length(),
               "illegal pos");

  MOZ_LOG(gIMELog, LogLevel::Info,
          (" IMMHandler::HandleComposition, GCS_CURSORPOS, mCursorPosition=%d",
           mCursorPosition));

  //--------------------------------------------------------
  // 5. Send the compositionchange event
  //--------------------------------------------------------
  DispatchCompositionChangeEvent(aWindow, aContext);

  return ShouldDrawCompositionStringOurselves();
}

void IMMHandler::HandleEndComposition(nsWindow* aWindow,
                                      const nsAString* aCommitString) {
  MOZ_ASSERT(mIsComposing,
             "HandleEndComposition is called but mIsComposing is FALSE");

  MOZ_LOG(gIMELog, LogLevel::Info,
          ("IMMHandler::HandleEndComposition(aWindow=0x%p, aCommitString=0x%p "
           "(\"%s\"))",
           aWindow, aCommitString,
           aCommitString ? NS_ConvertUTF16toUTF8(*aCommitString).get() : ""));

  IMEHandler::MaybeDestroyNativeCaret();

  RefPtr<TextEventDispatcher> dispatcher = GetTextEventDispatcherFor(aWindow);
  nsresult rv = dispatcher->BeginNativeInputTransaction();
  if (NS_WARN_IF(NS_FAILED(rv))) {
    MOZ_LOG(gIMELog, LogLevel::Error,
            (" IMMHandler::HandleEndComposition, FAILED due to "
             "TextEventDispatcher::BeginNativeInputTransaction() failure"));
    return;
  }
  WidgetEventTime eventTime = aWindow->CurrentMessageWidgetEventTime();
  nsEventStatus status;
  rv = dispatcher->CommitComposition(status, aCommitString, &eventTime);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    MOZ_LOG(gIMELog, LogLevel::Error,
            (" IMMHandler::HandleStartComposition, FAILED, due to "
             "TextEventDispatcher::CommitComposition() failure"));
    return;
  }
  mIsComposing = false;
  // XXX aWindow and mComposingWindow are always same??
  mComposingWindow = nullptr;
  mDispatcher = nullptr;
}

bool IMMHandler::HandleReconvert(nsWindow* aWindow, LPARAM lParam,
                                 LRESULT* oResult) {
  *oResult = 0;
  RECONVERTSTRING* pReconv = reinterpret_cast<RECONVERTSTRING*>(lParam);

  const Maybe<ContentSelection>& contentSelection =
      GetContentSelectionWithQueryIfNothing(aWindow);
  if (contentSelection.isNothing()) {
    MOZ_LOG(gIMELog, LogLevel::Error,
            ("IMMHandler::HandleReconvert, FAILED, due to "
             "Selection::GetContentSelectionWithQueryIfNothing() failure"));
    return false;
  }

  const uint32_t len = contentSelection->HasRange()
                           ? contentSelection->OffsetAndDataRef().Length()
                           : 0u;
  uint32_t needSize = sizeof(RECONVERTSTRING) + len * sizeof(WCHAR);

  if (!pReconv) {
    // Return need size to reconvert.
    if (len == 0) {
      MOZ_LOG(gIMELog, LogLevel::Error,
              ("IMMHandler::HandleReconvert, There are not selected text"));
      return false;
    }
    *oResult = needSize;
    MOZ_LOG(gIMELog, LogLevel::Info,
            ("IMMHandler::HandleReconvert, succeeded, result=%" PRIdLPTR,
             *oResult));
    return true;
  }

  if (pReconv->dwSize < needSize) {
    MOZ_LOG(gIMELog, LogLevel::Info,
            ("IMMHandler::HandleReconvert, FAILED, pReconv->dwSize=%ld, "
             "needSize=%u",
             pReconv->dwSize, needSize));
    return false;
  }

  *oResult = needSize;

  // Fill reconvert struct
  pReconv->dwVersion = 0;
  pReconv->dwStrLen = len;
  pReconv->dwStrOffset = sizeof(RECONVERTSTRING);
  pReconv->dwCompStrLen = len;
  pReconv->dwCompStrOffset = 0;
  pReconv->dwTargetStrLen = len;
  pReconv->dwTargetStrOffset = 0;

  if (len) {
    ::CopyMemory(reinterpret_cast<LPVOID>(lParam + sizeof(RECONVERTSTRING)),
                 contentSelection->OffsetAndDataRef().DataRef().get(),
                 len * sizeof(WCHAR));
  }

  MOZ_LOG(
      gIMELog, LogLevel::Info,
      ("IMMHandler::HandleReconvert, SUCCEEDED, pReconv=%s, result=%" PRIdLPTR,
       GetReconvertStringLog(pReconv).get(), *oResult));

  return true;
}

bool IMMHandler::HandleQueryCharPosition(nsWindow* aWindow, LPARAM lParam,
                                         LRESULT* oResult) {
  uint32_t len = mIsComposing ? mCompositionString.Length() : 0;
  *oResult = false;
  IMECHARPOSITION* pCharPosition = reinterpret_cast<IMECHARPOSITION*>(lParam);
  if (!pCharPosition) {
    MOZ_LOG(gIMELog, LogLevel::Error,
            ("IMMHandler::HandleQueryCharPosition, FAILED, due to "
             "pCharPosition is null"));
    return false;
  }
  if (pCharPosition->dwSize < sizeof(IMECHARPOSITION)) {
    MOZ_LOG(gIMELog, LogLevel::Error,
            ("IMMHandler::HandleReconvert, FAILED, pCharPosition->dwSize=%lu, "
             "sizeof(IMECHARPOSITION)=%zu",
             pCharPosition->dwSize, sizeof(IMECHARPOSITION)));
    return false;
  }
  if (::GetFocus() != aWindow->GetWindowHandle()) {
    MOZ_LOG(gIMELog, LogLevel::Error,
            ("IMMHandler::HandleReconvert, FAILED, ::GetFocus()=%p, "
             "OurWindowHandle=%p",
             ::GetFocus(), aWindow->GetWindowHandle()));
    return false;
  }
  if (pCharPosition->dwCharPos > len) {
    MOZ_LOG(gIMELog, LogLevel::Error,
            ("IMMHandler::HandleQueryCharPosition, FAILED, "
             "pCharPosition->dwCharPos=%ld, len=%u",
             pCharPosition->dwCharPos, len));
    return false;
  }

  LayoutDeviceIntRect r;
  bool ret =
      GetCharacterRectOfSelectedTextAt(aWindow, pCharPosition->dwCharPos, r);
  NS_ENSURE_TRUE(ret, false);

  LayoutDeviceIntRect screenRect;
  // We always need top level window that is owner window of the popup window
  // even if the content of the popup window has focus.
  ResolveIMECaretPos(aWindow->GetTopLevelWindow(false), r, nullptr, screenRect);

  // XXX This might need to check writing mode.  However, MSDN doesn't explain
  //     how to set the values in vertical writing mode. Additionally, IME
  //     doesn't work well with top-left of the character (this is explicitly
  //     documented) and its horizontal width.  So, it might be better to set
  //     top-right corner of the character and horizontal width, but we're not
  //     sure if it doesn't cause any problems with a lot of IMEs...
  pCharPosition->pt.x = screenRect.X();
  pCharPosition->pt.y = screenRect.Y();

  pCharPosition->cLineHeight = r.Height();

  WidgetQueryContentEvent queryEditorRectEvent(true, eQueryEditorRect, aWindow);
  aWindow->InitEvent(queryEditorRectEvent);
  DispatchEvent(aWindow, queryEditorRectEvent);
  if (NS_WARN_IF(queryEditorRectEvent.Failed())) {
    MOZ_LOG(gIMELog, LogLevel::Error,
            (" IMMHandler::HandleQueryCharPosition, eQueryEditorRect failed"));
    ::GetWindowRect(aWindow->GetWindowHandle(), &pCharPosition->rcDocument);
  } else {
    LayoutDeviceIntRect editorRectInWindow = queryEditorRectEvent.mReply->mRect;
    nsWindow* window = !!queryEditorRectEvent.mReply->mFocusedWidget
                           ? static_cast<nsWindow*>(
                                 queryEditorRectEvent.mReply->mFocusedWidget)
                           : aWindow;
    LayoutDeviceIntRect editorRectInScreen;
    ResolveIMECaretPos(window, editorRectInWindow, nullptr, editorRectInScreen);
    ::SetRect(&pCharPosition->rcDocument, editorRectInScreen.X(),
              editorRectInScreen.Y(), editorRectInScreen.XMost(),
              editorRectInScreen.YMost());
  }

  *oResult = TRUE;

  MOZ_LOG(
      gIMELog, LogLevel::Info,
      ("IMMHandler::HandleQueryCharPosition, SUCCEEDED, pCharPosition={ "
       "pt={ x=%ld, y=%ld }, cLineHeight=%d, rcDocument={ left=%ld, top=%ld, "
       "right=%ld, bottom=%ld } }",
       pCharPosition->pt.x, pCharPosition->pt.y, pCharPosition->cLineHeight,
       pCharPosition->rcDocument.left, pCharPosition->rcDocument.top,
       pCharPosition->rcDocument.right, pCharPosition->rcDocument.bottom));
  return true;
}

bool IMMHandler::HandleDocumentFeed(nsWindow* aWindow, LPARAM lParam,
                                    LRESULT* oResult) {
  *oResult = 0;
  RECONVERTSTRING* pReconv = reinterpret_cast<RECONVERTSTRING*>(lParam);

  LayoutDeviceIntPoint point(0, 0);

  bool hasCompositionString =
      mIsComposing && ShouldDrawCompositionStringOurselves();

  int32_t targetOffset, targetLength;
  if (!hasCompositionString) {
    const Maybe<ContentSelection>& contentSelection =
        GetContentSelectionWithQueryIfNothing(aWindow);
    if (contentSelection.isNothing()) {
      MOZ_LOG(gIMELog, LogLevel::Error,
              ("IMMHandler::HandleDocumentFeed, FAILED, due to "
               "Selection::GetContentSelectionWithQueryIfNothing() failure"));
      return false;
    }
    if (contentSelection->HasRange()) {
      targetOffset = static_cast<int32_t>(
          contentSelection->OffsetAndDataRef().StartOffset());
      targetLength =
          static_cast<int32_t>(contentSelection->OffsetAndDataRef().Length());
    } else {
      // If there is no selection range, let's return all text in the editor.
      targetOffset = 0;
      targetLength = INT32_MAX;
    }
  } else {
    targetOffset = int32_t(mCompositionStart);
    targetLength = int32_t(mCompositionString.Length());
  }

  // XXX nsString::Find and nsString::RFind take int32_t for offset, so,
  //     we cannot support this message when the current offset is larger than
  //     INT32_MAX.
  if (targetOffset < 0 || targetLength < 0 || targetOffset + targetLength < 0) {
    MOZ_LOG(gIMELog, LogLevel::Error,
            ("IMMHandler::HandleDocumentFeed, FAILED, "
             "due to the selection is out of range"));
    return false;
  }

  // Get all contents of the focused editor.
  WidgetQueryContentEvent queryTextContentEvent(true, eQueryTextContent,
                                                aWindow);
  queryTextContentEvent.InitForQueryTextContent(0, UINT32_MAX);
  aWindow->InitEvent(queryTextContentEvent, &point);
  DispatchEvent(aWindow, queryTextContentEvent);
  if (NS_WARN_IF(queryTextContentEvent.Failed())) {
    MOZ_LOG(gIMELog, LogLevel::Error,
            ("IMMHandler::HandleDocumentFeed, FAILED, "
             "due to eQueryTextContent failure"));
    return false;
  }

  nsAutoString str(queryTextContentEvent.mReply->DataRef());
  if (targetOffset > static_cast<int32_t>(str.Length())) {
    MOZ_LOG(gIMELog, LogLevel::Error,
            (" IMMHandler::HandleDocumentFeed, FAILED, "
             "due to the caret offset is invalid"));
    return false;
  }

  // Get the focused paragraph, we decide that it starts from the previous CRLF
  // (or start of the editor) to the next one (or the end of the editor).
  int32_t paragraphStart = 0;
  if (targetOffset > 0) {
    paragraphStart = Substring(str, 0, targetOffset).RFind(u"\n") + 1;
  }
  int32_t paragraphEnd = str.Find(u"\r", targetOffset + targetLength);
  if (paragraphEnd < 0) {
    paragraphEnd = str.Length();
  }
  nsDependentSubstring paragraph(str, paragraphStart,
                                 paragraphEnd - paragraphStart);

  uint32_t len = paragraph.Length();
  uint32_t needSize = sizeof(RECONVERTSTRING) + len * sizeof(WCHAR);

  if (!pReconv) {
    *oResult = needSize;
    MOZ_LOG(gIMELog, LogLevel::Info,
            ("IMMHandler::HandleDocumentFeed, succeeded, result=%" PRIdLPTR,
             *oResult));
    return true;
  }

  if (pReconv->dwSize < needSize) {
    MOZ_LOG(gIMELog, LogLevel::Error,
            ("IMMHandler::HandleDocumentFeed, FAILED, "
             "pReconv->dwSize=%ld, needSize=%u",
             pReconv->dwSize, needSize));
    return false;
  }

  // Fill reconvert struct
  pReconv->dwVersion = 0;
  pReconv->dwStrLen = len;
  pReconv->dwStrOffset = sizeof(RECONVERTSTRING);
  if (hasCompositionString) {
    pReconv->dwCompStrLen = targetLength;
    pReconv->dwCompStrOffset = (targetOffset - paragraphStart) * sizeof(WCHAR);
    // Set composition target clause information
    uint32_t offset, length;
    if (!GetTargetClauseRange(&offset, &length)) {
      MOZ_LOG(gIMELog, LogLevel::Error,
              ("IMMHandler::HandleDocumentFeed, FAILED, "
               "due to IMMHandler::GetTargetClauseRange() failure"));
      return false;
    }
    pReconv->dwTargetStrLen = length;
    pReconv->dwTargetStrOffset = (offset - paragraphStart) * sizeof(WCHAR);
  } else {
    pReconv->dwTargetStrLen = targetLength;
    pReconv->dwTargetStrOffset =
        (targetOffset - paragraphStart) * sizeof(WCHAR);
    // There is no composition string, so, the length is zero but we should
    // set the cursor offset to the composition str offset.
    pReconv->dwCompStrLen = 0;
    pReconv->dwCompStrOffset = pReconv->dwTargetStrOffset;
  }

  *oResult = needSize;
  ::CopyMemory(reinterpret_cast<LPVOID>(lParam + sizeof(RECONVERTSTRING)),
               paragraph.BeginReading(), len * sizeof(WCHAR));

  MOZ_LOG(gIMELog, LogLevel::Info,
          ("IMMHandler::HandleDocumentFeed, SUCCEEDED, pReconv=%s, "
           "result=%" PRIdLPTR,
           GetReconvertStringLog(pReconv).get(), *oResult));

  return true;
}

bool IMMHandler::CommitCompositionOnPreviousWindow(nsWindow* aWindow) {
  if (!mComposingWindow || mComposingWindow == aWindow) {
    return false;
  }

  MOZ_LOG(gIMELog, LogLevel::Info,
          ("IMMHandler::CommitCompositionOnPreviousWindow, mIsComposing=%s",
           GetBoolName(mIsComposing)));

  // If we have composition, we should dispatch composition events internally.
  if (mIsComposing) {
    IMEContext context(mComposingWindow);
    NS_ASSERTION(context.IsValid(), "IME context must be valid");

    HandleEndComposition(mComposingWindow);
    return true;
  }

  return false;
}

static TextRangeType PlatformToNSAttr(uint8_t aAttr) {
  switch (aAttr) {
    case ATTR_INPUT_ERROR:
    // case ATTR_FIXEDCONVERTED:
    case ATTR_INPUT:
      return TextRangeType::eRawClause;
    case ATTR_CONVERTED:
      return TextRangeType::eConvertedClause;
    case ATTR_TARGET_NOTCONVERTED:
      return TextRangeType::eSelectedRawClause;
    case ATTR_TARGET_CONVERTED:
      return TextRangeType::eSelectedClause;
    default:
      NS_ASSERTION(false"unknown attribute");
      return TextRangeType::eCaret;
  }
}

// static
void IMMHandler::DispatchEvent(nsWindow* aWindow, WidgetGUIEvent& aEvent) {
  MOZ_LOG(
      gIMELog, LogLevel::Info,
      ("IMMHandler::DispatchEvent(aWindow=0x%p, aEvent={ mMessage=%s }, "
       "aWindow->Destroyed()=%s",
       aWindow, ToChar(aEvent.mMessage), GetBoolName(aWindow->Destroyed())));

  if (aWindow->Destroyed()) {
    return;
  }

  aWindow->DispatchWindowEvent(aEvent);
}

void IMMHandler::DispatchCompositionChangeEvent(nsWindow* aWindow,
                                                const IMEContext& aContext) {
  NS_ASSERTION(mIsComposing, "conflict state");
  MOZ_LOG(gIMELog, LogLevel::Info,
          ("IMMHandler::DispatchCompositionChangeEvent"));

  // If we don't need to draw composition string ourselves, we don't need to
  // fire compositionchange event during composing.
  if (!ShouldDrawCompositionStringOurselves()) {
    // But we need to adjust composition window pos and native caret pos, here.
    SetIMERelatedWindowsPos(aWindow, aContext);
    return;
  }

  RefPtr<nsWindow> kungFuDeathGrip(aWindow);
  RefPtr<TextEventDispatcher> dispatcher = GetTextEventDispatcherFor(aWindow);
  nsresult rv = dispatcher->BeginNativeInputTransaction();
  if (NS_WARN_IF(NS_FAILED(rv))) {
    MOZ_LOG(gIMELog, LogLevel::Error,
            (" IMMHandler::DispatchCompositionChangeEvent, FAILED due to "
             "TextEventDispatcher::BeginNativeInputTransaction() failure"));
    return;
  }

  // NOTE: Calling SetIMERelatedWindowsPos() from this method will be failure
  //       in e10s mode.  compositionchange event will notify this of
  //       NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED, then
  //       SetIMERelatedWindowsPos() will be called.

  // XXX Sogou (Simplified Chinese IME) returns contradictory values:
  //     The cursor position is actual cursor position. However, other values
  //     (composition string and attributes) are empty.

  if (mCompositionString.IsEmpty()) {
    // Don't append clause information if composition string is empty.
  } else if (mClauseArray.IsEmpty()) {
    // Some IMEs don't return clause array information, then, we assume that
    // all characters in the composition string are in one clause.
    MOZ_LOG(gIMELog, LogLevel::Info,
            (" IMMHandler::DispatchCompositionChangeEvent, "
             "mClauseArray.Length()=0"));
    rv = dispatcher->SetPendingComposition(mCompositionString, nullptr);
    if (NS_WARN_IF(NS_FAILED(rv))) {
      MOZ_LOG(gIMELog, LogLevel::Error,
              (" IMMHandler::DispatchCompositionChangeEvent, FAILED due to"
               "TextEventDispatcher::SetPendingComposition() failure"));
      return;
    }
  } else {
    // iterate over the attributes
    rv = dispatcher->SetPendingCompositionString(mCompositionString);
    if (NS_WARN_IF(NS_FAILED(rv))) {
      MOZ_LOG(gIMELog, LogLevel::Error,
              (" IMMHandler::DispatchCompositionChangeEvent, FAILED due to"
               "TextEventDispatcher::SetPendingCompositionString() failure"));
      return;
    }
    uint32_t lastOffset = 0;
    for (uint32_t i = 0; i < mClauseArray.Length() - 1; i++) {
      uint32_t current = mClauseArray[i + 1];
      if (current > mCompositionString.Length()) {
        MOZ_LOG(gIMELog, LogLevel::Info,
                (" IMMHandler::DispatchCompositionChangeEvent, "
                 "mClauseArray[%u]=%u. "
                 "This is larger than mCompositionString.Length()=%zu",
                 i + 1, current, mCompositionString.Length()));
        current = int32_t(mCompositionString.Length());
      }

      uint32_t length = current - lastOffset;
      if (NS_WARN_IF(lastOffset >= mAttributeArray.Length())) {
        MOZ_LOG(gIMELog, LogLevel::Error,
                (" IMMHandler::DispatchCompositionChangeEvent, FAILED due to "
                 "invalid data of mClauseArray or mAttributeArray"));
        return;
      }
      TextRangeType textRangeType =
          PlatformToNSAttr(mAttributeArray[lastOffset]);
      rv = dispatcher->AppendClauseToPendingComposition(length, textRangeType);
      if (NS_WARN_IF(NS_FAILED(rv))) {
        MOZ_LOG(gIMELog, LogLevel::Error,
                (" IMMHandler::DispatchCompositionChangeEvent, FAILED due to"
                 "TextEventDispatcher::AppendClauseToPendingComposition() "
                 "failure"));
        return;
      }

      lastOffset = current;

      MOZ_LOG(gIMELog, LogLevel::Info,
              (" IMMHandler::DispatchCompositionChangeEvent, index=%u, "
               "rangeType=%s, range length=%u",
               i, ToChar(textRangeType), length));
    }
  }

  if (mCursorPosition == NO_IME_CARET) {
    MOZ_LOG(gIMELog, LogLevel::Info,
            (" IMMHandler::DispatchCompositionChangeEvent, no caret"));
  } else {
    uint32_t cursor = static_cast<uint32_t>(mCursorPosition);
    if (cursor > mCompositionString.Length()) {
      MOZ_LOG(gIMELog, LogLevel::Info,
              (" IMMHandler::CreateTextRangeArray, mCursorPosition=%d. "
               "This is larger than mCompositionString.Length()=%zu",
               mCursorPosition, mCompositionString.Length()));
      cursor = mCompositionString.Length();
    }

    // If caret is in the target clause, the target clause will be painted as
    // normal selection range.  Since caret shouldn't be in selection range on
    // Windows, we shouldn't append caret range in such case.
    const TextRangeArray* clauses = dispatcher->GetPendingCompositionClauses();
    const TextRange* targetClause =
        clauses ? clauses->GetTargetClause() : nullptr;
    if (targetClause && cursor >= targetClause->mStartOffset &&
        cursor <= targetClause->mEndOffset) {
      // Forget the caret position specified by IME since Gecko's caret position
      // will be at the end of composition string.
      mCursorPosition = NO_IME_CARET;
      MOZ_LOG(gIMELog, LogLevel::Info,
              (" IMMHandler::CreateTextRangeArray, no caret due to it's in "
               "the target clause, now, mCursorPosition is NO_IME_CARET"));
    }

    if (mCursorPosition != NO_IME_CARET) {
      rv = dispatcher->SetCaretInPendingComposition(cursor, 0);
      if (NS_WARN_IF(NS_FAILED(rv))) {
        MOZ_LOG(
            gIMELog, LogLevel::Error,
            (" IMMHandler::DispatchCompositionChangeEvent, FAILED due to"
             "TextEventDispatcher::SetCaretInPendingComposition() failure"));
        return;
      }
    }
  }

  WidgetEventTime eventTime = aWindow->CurrentMessageWidgetEventTime();
  nsEventStatus status;
  rv = dispatcher->FlushPendingComposition(status, &eventTime);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    MOZ_LOG(gIMELog, LogLevel::Error,
--> --------------------

--> maximum size reached

--> --------------------

100%


¤ Dauer der Verarbeitung: 0.30 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.