Bug 1130935 part.2 Set proper composition font when writing mode is changed r=emk
authorMasayuki Nakano <masayuki@d-toybox.com>
Fri, 15 May 2015 10:18:07 +0900
changeset 243952 eb660def02c0f6a1568e98ea6f5b48f46a2a0929
parent 243951 9744edd77df434dedad42712181ba52aeeeb3b59
child 243953 3cca44bb0f434426685cebeadcb3e7568407439c
push id59807
push usermasayuki@d-toybox.com
push dateFri, 15 May 2015 01:18:23 +0000
treeherdermozilla-inbound@c6c67158efef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemk
bugs1130935
milestone41.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1130935 part.2 Set proper composition font when writing mode is changed r=emk
modules/libpref/init/all.js
widget/windows/nsIMM32Handler.cpp
widget/windows/nsIMM32Handler.h
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -3064,19 +3064,28 @@ pref("intl.tsf.hack.easy_changjei.do_not
 // of selected clause of composition string.
 pref("intl.tsf.hack.google_ja_input.do_not_return_no_layout_error_at_first_char", true);
 // Whether use previous character rect for the result of
 // ITfContextView::GetTextExt() if the specified range is the caret of
 // composition string.
 pref("intl.tsf.hack.google_ja_input.do_not_return_no_layout_error_at_caret", true);
 #endif
 
+// If composition_font is set, Gecko sets the font to IME.  IME may use
+// the fonts on their window like candidate window.  If they are empty,
+// Gecko uses the system default font which is set to the IM context.
+// The font name must not start with '@'.  When the writing mode is vertical,
+// Gecko inserts '@' to the start of the font name automatically.
+// FYI: Changing these prefs requires to restart.
+pref("intl.imm.composition_font", "");
+
 // Even if IME claims that they support vertical writing mode but it may not
 // support vertical writing mode for its candidate window.  This pref allows
 // to ignore the claim.
+// FYI: Changing this pref requires to restart.
 pref("intl.imm.vertical_writing.always_assume_not_supported", false);
 
 // See bug 448927, on topmost panel, some IMEs are not usable on Windows.
 pref("ui.panel.default_level_parent", false);
 
 pref("mousewheel.system_scroll_override_on_root_content.enabled", true);
 
 // High resolution scrolling with supported mouse drivers on Vista or later.
--- a/widget/windows/nsIMM32Handler.cpp
+++ b/widget/windows/nsIMM32Handler.cpp
@@ -6,16 +6,17 @@
 
 #include "prlog.h"
 
 #include "nsIMM32Handler.h"
 #include "nsWindow.h"
 #include "nsWindowDefs.h"
 #include "WinUtils.h"
 #include "KeyboardLayout.h"
+#include "WritingModes.h"
 #include <algorithm>
 
 #include "mozilla/MiscEvents.h"
 #include "mozilla/TextEvents.h"
 
 #ifndef IME_PROP_ACCEPT_WIDE_VKEY
 #define IME_PROP_ACCEPT_WIDE_VKEY 0x20
 #endif
@@ -108,16 +109,17 @@ static UINT sWM_MSIME_MOUSE = 0; // mous
 
 #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
 
+nsString nsIMM32Handler::sIMEName;
 UINT nsIMM32Handler::sCodePage = 0;
 DWORD nsIMM32Handler::sIMEProperty = 0;
 DWORD nsIMM32Handler::sIMEUIProperty = 0;
 bool nsIMM32Handler::sAssumeVerticalWritingModeNotSupported = false;
 
 /* static */ void
 nsIMM32Handler::EnsureHandlerInstance()
 {
@@ -197,42 +199,39 @@ nsIMM32Handler::IsVerticalWritingSupport
     return false;
   }
   return !!(sIMEUIProperty & (UI_CAP_2700 | UI_CAP_ROT90 | UI_CAP_ROTANY));
 }
 
 /* static */ void
 nsIMM32Handler::InitKeyboardLayout(HKL aKeyboardLayout)
 {
-#ifdef PR_LOGGING
-  nsAutoString IMEName;
-  if (PR_LOG_TEST(gIMM32Log, PR_LOG_ALWAYS)) {
-    UINT IMENameLength = ::ImmGetDescriptionW(aKeyboardLayout, nullptr, 0);
-    if (IMENameLength) {
-      // Add room for the terminating null character
-      IMEName.SetLength(++IMENameLength);
-      IMENameLength =
-        ::ImmGetDescriptionW(aKeyboardLayout, IMEName.BeginWriting(),
-                             IMENameLength);
-      // Adjust the length to ignore the terminating null character
-      IMEName.SetLength(IMENameLength);
-    }
+  UINT IMENameLength = ::ImmGetDescriptionW(aKeyboardLayout, nullptr, 0);
+  if (IMENameLength) {
+    // Add room for the terminating null character
+    sIMEName.SetLength(++IMENameLength);
+    IMENameLength =
+      ::ImmGetDescriptionW(aKeyboardLayout, sIMEName.BeginWriting(),
+                           IMENameLength);
+    // Adjust the length to ignore the terminating null character
+    sIMEName.SetLength(IMENameLength);
+  } else {
+    sIMEName.Truncate();
   }
-#endif // #ifdef PR_LOGGING
 
   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);
   PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
     ("IMM32: InitKeyboardLayout, aKeyboardLayout=%08x (\"%s\"), sCodePage=%lu, "
      "sIMEProperty=%s, sIMEUIProperty=%s",
-     aKeyboardLayout, NS_ConvertUTF16toUTF8(IMEName).get(),
+     aKeyboardLayout, NS_ConvertUTF16toUTF8(sIMEName).get(),
      sCodePage, GetIMEGeneralPropertyName(sIMEProperty).get(),
      GetIMEUIPropertyName(sIMEUIProperty).get()));
 }
 
 /* static */ UINT
 nsIMM32Handler::GetKeyboardCodePage()
 {
   return sCodePage;
@@ -867,16 +866,19 @@ nsIMM32Handler::OnIMEStartCompositionOnP
 {
   PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
     ("IMM32: OnIMEStartCompositionOnPlugin, hWnd=%08x, mIsComposingOnPlugin=%s\n",
      aWindow->GetWindowHandle(), mIsComposingOnPlugin ? "TRUE" : "FALSE"));
   mIsComposingOnPlugin = true;
   mComposingWindow = aWindow;
   nsIMEContext IMEContext(aWindow->GetWindowHandle());
   SetIMERelatedWindowsPosOnPlugin(aWindow, IMEContext);
+  // On widnowless plugin, we should assume that the focused editor is always
+  // in horizontal writing mode.
+  AdjustCompositionFont(IMEContext, WritingMode());
   aResult.mConsumed =
     aWindow->DispatchPluginEvent(WM_IME_STARTCOMPOSITION, wParam, lParam,
                                  false);
   return true;
 }
 
 bool
 nsIMM32Handler::OnIMECompositionOnPlugin(nsWindow* aWindow,
@@ -1043,16 +1045,18 @@ nsIMM32Handler::HandleStartComposition(n
   aWindow->InitEvent(selection, &point);
   aWindow->DispatchWindowEvent(&selection);
   if (!selection.mSucceeded) {
     PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
       ("IMM32: HandleStartComposition, FAILED (NS_QUERY_SELECTED_TEXT)\n"));
     return;
   }
 
+  AdjustCompositionFont(aIMEContext, selection.GetWritingMode());
+
   mCompositionStart = selection.mReply.mOffset;
 
   WidgetCompositionEvent event(true, NS_COMPOSITION_START, aWindow);
   aWindow->InitEvent(event, &point);
   aWindow->DispatchWindowEvent(&event);
 
   mIsComposing = true;
   mComposingWindow = aWindow;
@@ -2113,16 +2117,128 @@ nsIMM32Handler::ResolveIMECaretPos(nsIWi
 
   if (aReferenceWidget)
     aOutRect.MoveBy(aReferenceWidget->WidgetToScreenOffsetUntyped());
 
   if (aNewOriginWidget)
     aOutRect.MoveBy(-aNewOriginWidget->WidgetToScreenOffsetUntyped());
 }
 
+static void
+SetHorizontalFontToLogFont(const nsAString& aFontFace,
+                           LOGFONTW& aLogFont)
+{
+  aLogFont.lfEscapement = aLogFont.lfOrientation = 0;
+  if (NS_WARN_IF(aFontFace.Length() > LF_FACESIZE - 1)) {
+    memcpy(aLogFont.lfFaceName, L"System", sizeof(L"System"));
+    return;
+  }
+  memcpy(aLogFont.lfFaceName, aFontFace.BeginReading(),
+         aFontFace.Length() * sizeof(wchar_t));
+  aLogFont.lfFaceName[aFontFace.Length()] = 0;
+}
+
+static void
+SetVerticalFontToLogFont(const nsAString& aFontFace,
+                         LOGFONTW& aLogFont)
+{
+  aLogFont.lfEscapement = aLogFont.lfOrientation = 2700;
+  if (NS_WARN_IF(aFontFace.Length() > LF_FACESIZE - 2)) {
+    memcpy(aLogFont.lfFaceName, L"@System", sizeof(L"@System"));
+    return;
+  }
+  aLogFont.lfFaceName[0] = '@';
+  memcpy(&aLogFont.lfFaceName[1], aFontFace.BeginReading(),
+         aFontFace.Length() * sizeof(wchar_t));
+  aLogFont.lfFaceName[aFontFace.Length() + 1] = 0;
+}
+
+void
+nsIMM32Handler::AdjustCompositionFont(const nsIMEContext& aIMEContext,
+                                      const WritingMode& aWritingMode)
+{
+  // An instance of nsIMM32Handler is destroyed when active IME is changed.
+  // Therefore, we need to store the information which are set to the IM
+  // context to static variables since IM context is never recreated.
+  static bool sCompositionFontsInitialized = false;
+  static nsString sCompositionFont =
+    Preferences::GetString("intl.imm.composition_font");
+
+  // If composition font is customized by pref, we need to modify the
+  // composition font of the IME context at first time even if the writing mode
+  // is horizontal.
+  bool setCompositionFontForcibly =
+    !sCompositionFontsInitialized && !sCompositionFont.IsEmpty();
+
+  static WritingMode sCurrentWritingMode;
+  static nsString sCurrentIMEName;
+  if (!setCompositionFontForcibly &&
+      sCurrentWritingMode == aWritingMode &&
+      sCurrentIMEName == sIMEName) {
+    // Nothing to do if writing mode isn't being changed.
+    return;
+  }
+
+  // Decide composition fonts for both horizontal writing mode and vertical
+  // writing mode.  If the font isn't specified by the pref, use default
+  // font which is already set to the IM context.  And also in vertical writing
+  // mode, insert '@' to the start of the font.
+  if (!sCompositionFontsInitialized) {
+    sCompositionFontsInitialized = true;
+    // sCompositionFontH must not start with '@' and its length is less than
+    // LF_FACESIZE since it needs to end with null terminating character.
+    if (sCompositionFont.IsEmpty() ||
+        sCompositionFont.Length() > LF_FACESIZE - 1 ||
+        sCompositionFont[0] == '@') {
+      LOGFONTW defaultLogFont;
+      if (NS_WARN_IF(!::ImmGetCompositionFont(aIMEContext.get(),
+                                              &defaultLogFont))) {
+        PR_LOG(gIMM32Log, PR_LOG_ERROR,
+          ("IMM32: AdjustCompositionFont, ::ImmGetCompositionFont() failed"));
+        sCompositionFont.AssignLiteral("System");
+      } else {
+        // The font face is typically, "System".
+        sCompositionFont.Assign(defaultLogFont.lfFaceName);
+      }
+    }
+
+    PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
+      ("IMM32: AdjustCompositionFont, sCompositionFont=\"%s\" is initialized",
+       NS_ConvertUTF16toUTF8(sCompositionFont).get()));
+  }
+
+  sCurrentWritingMode = aWritingMode;
+  sCurrentIMEName = sIMEName;
+
+  LOGFONTW logFont;
+  memset(&logFont, 0, sizeof(logFont));
+  if (!::ImmGetCompositionFont(aIMEContext.get(), &logFont)) {
+    PR_LOG(gIMM32Log, PR_LOG_ERROR,
+      ("IMM32: AdjustCompositionFont, ::ImmGetCompositionFont() failed"));
+    logFont.lfFaceName[0] = 0;
+  }
+  // Need to reset some information which should be recomputed with new font.
+  logFont.lfWidth = 0;
+  logFont.lfWeight = FW_DONTCARE;
+  logFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
+  logFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
+  logFont.lfPitchAndFamily = DEFAULT_PITCH;
+
+  if (!mIsComposingOnPlugin &&
+      aWritingMode.IsVertical() && IsVerticalWritingSupported()) {
+    SetVerticalFontToLogFont(sCompositionFont, logFont);
+  } else {
+    SetHorizontalFontToLogFont(sCompositionFont, logFont);
+  }
+  PR_LOG(gIMM32Log, PR_LOG_WARNING,
+    ("IMM32: AdjustCompositionFont, calling ::ImmSetCompositionFont(\"%s\")",
+     NS_ConvertUTF16toUTF8(nsDependentString(logFont.lfFaceName)).get()));
+  ::ImmSetCompositionFontW(aIMEContext.get(), &logFont);
+}
+
 /* static */ nsresult
 nsIMM32Handler::OnMouseButtonEvent(nsWindow* aWindow,
                                    const IMENotification& aIMENotification)
 {
   // We don't need to create the instance of the handler here.
   if (!gIMM32Handler) {
     return NS_OK;
   }
--- a/widget/windows/nsIMM32Handler.h
+++ b/widget/windows/nsIMM32Handler.h
@@ -13,16 +13,19 @@
 #include "nsTArray.h"
 #include "nsIWidget.h"
 #include "mozilla/EventForwards.h"
 #include "nsRect.h"
 
 class nsWindow;
 
 namespace mozilla {
+
+class WritingMode;
+
 namespace widget {
 
 struct MSGResult;
 
 } // namespace widget
 } // namespace mozilla
 
 class nsIMEContext
@@ -278,16 +281,22 @@ protected:
                                        const nsIMEContext& aIMEContext);
   bool GetCharacterRectOfSelectedTextAt(nsWindow* aWindow,
                                           uint32_t aOffset,
                                           nsIntRect &aCharRect);
   bool GetCaretRect(nsWindow* aWindow, nsIntRect &aCaretRect);
   void GetCompositionString(const nsIMEContext &aIMEContext,
                             DWORD aIndex,
                             nsAString& aCompositionString) const;
+
+  /**
+   * AdjustCompositionFont() makes IME vertical writing mode if it's supported.
+   */
+  void AdjustCompositionFont(const nsIMEContext& aIMEContext,
+                             const mozilla::WritingMode& aWritingMode);
   /**
    *  Get the current target clause of composition string.
    *  If there are one or more characters whose attribute is ATTR_TARGET_*,
    *  this returns the first character's offset and its length.
    *  Otherwise, e.g., the all characters are ATTR_INPUT, this returns
    *  the composition string range because the all is the current target.
    *
    *  aLength can be null (default), but aOffset must not be null.
@@ -355,15 +364,16 @@ protected:
 
   int32_t mCursorPosition;
   uint32_t mCompositionStart;
 
   bool mIsComposing;
   bool mIsComposingOnPlugin;
   bool mNativeCaretIsCreated;
 
+  static nsString sIMEName;
   static UINT sCodePage;
   static DWORD sIMEProperty;
   static DWORD sIMEUIProperty;
   static bool sAssumeVerticalWritingModeNotSupported;
 };
 
 #endif // nsIMM32Handler_h__