Bug 1215818 - part 2: Add telemetry probe to collect IME usage on macOS r?m_kato draft
authorMasayuki Nakano <masayuki@d-toybox.com>
Wed, 20 Jun 2018 11:33:42 +0900
changeset 809093 d7927cc4eebbdb3a03fc7ad95c572711c8581693
parent 809092 8420e65ed43456b1c6f655072feae54da2e004bc
child 809094 46861f427e5df0ad322f19e9955f9abf051cdacd
push id113543
push usermasayuki@d-toybox.com
push dateThu, 21 Jun 2018 07:08:37 +0000
reviewersm_kato
bugs1215818
milestone62.0a1
Bug 1215818 - part 2: Add telemetry probe to collect IME usage on macOS r?m_kato This patch adds a telemetry probe to collect Input Source ID or Bundle ID of IME when an IME open mode is selected by user. Input Source ID includes input mode of IME, but Bundle ID does not so. In most languages, we need to collect the former, but only for Japanese IME, we need to collect the latter because non-Japanese IME's input mode is "how to input characters". So, the input mode is important. However, Japanese IME's input mode is "to input which type of characters". So, Japanese IME user may use multiple input modes but we need only the IME mode. If we'd collect number of each input mode users of Japanese language, it'd be difficult to count how many users actually used typical Japanese IME since somebody may use only a mode, some others may use only different modes. So, this patch collects Input Source ID when non-Japanese IME is open and Bundle ID when Japanese IME is open. MozReview-Commit-ID: CltLrWVGyRk
toolkit/components/telemetry/Scalars.yaml
widget/cocoa/TextInputHandler.h
widget/cocoa/TextInputHandler.mm
--- a/toolkit/components/telemetry/Scalars.yaml
+++ b/toolkit/components/telemetry/Scalars.yaml
@@ -1746,16 +1746,33 @@ widget:
     expires: never
     kind: boolean
     notification_emails:
       - mnakano@mozilla.com
     release_channel_collection: opt-out
     record_in_processes:
       - 'main'
 
+  ime_name_on_mac:
+    bug_numbers:
+      - 1215818
+    description: >
+      Name of IME which was selected by users on macOS.  The value is Input
+      Source ID if non-Japanese IME was open.  Otherwise, if Japanese IME was
+      open, the value is Bundle ID.  Input Source ID includes input mode, but
+      Bundle ID does not include input mode.
+    keyed: true
+    expires: never
+    kind: boolean
+    notification_emails:
+      - mnakano@mozilla.com
+    release_channel_collection: opt-out
+    record_in_processes:
+      - 'main'
+
 # The following section contains memory reporter counters.
 memoryreporter:
   max_ghost_windows:
     bug_numbers:
       - 1454724
     description: >
       The maximum number of leaked ghost windows seen.
     expires: "66"
--- a/widget/cocoa/TextInputHandler.h
+++ b/widget/cocoa/TextInputHandler.h
@@ -219,16 +219,17 @@ public:
 
   bool GetInputSourceType(nsAString &aType)
   {
     NS_ENSURE_TRUE(mInputSource, false);
     return GetStringProperty(kTISPropertyInputSourceType, aType);
   }
 
   bool IsForRTLLanguage();
+  bool IsForJapaneseLanguage();
   bool IsInitializedByCurrentInputSource();
 
   enum {
     // 40 is an actual result of the ::LMGetKbdType() when we connect an
     // unknown keyboard and set the keyboard type to ANSI manually on the
     // set up dialog.
     eKbdType_ANSI = 40
   };
--- a/widget/cocoa/TextInputHandler.mm
+++ b/widget/cocoa/TextInputHandler.mm
@@ -7,16 +7,17 @@
 #include "TextInputHandler.h"
 
 #include "mozilla/Logging.h"
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/AutoRestore.h"
 #include "mozilla/MiscEvents.h"
 #include "mozilla/MouseEvents.h"
+#include "mozilla/Telemetry.h"
 #include "mozilla/TextEventDispatcher.h"
 #include "mozilla/TextEvents.h"
 
 #include "nsChildView.h"
 #include "nsObjCExceptions.h"
 #include "nsBidiUtils.h"
 #include "nsToolkit.h"
 #include "nsCocoaUtils.h"
@@ -828,16 +829,24 @@ TISInputSourceWrapper::IsForRTLLanguage(
     NS_ENSURE_TRUE(ret, ret);
     char16_t ch = str.IsEmpty() ? char16_t(0) : str.CharAt(0);
     mIsRTL = UTF16_CODE_UNIT_IS_BIDI(ch);
   }
   return mIsRTL != 0;
 }
 
 bool
+TISInputSourceWrapper::IsForJapaneseLanguage()
+{
+  nsAutoString lang;
+  GetPrimaryLanguage(lang);
+  return lang.EqualsLiteral("ja");
+}
+
+bool
 TISInputSourceWrapper::IsInitializedByCurrentInputSource()
 {
   return mInputSource == ::TISCopyCurrentKeyboardInputSource();
 }
 
 void
 TISInputSourceWrapper::Select()
 {
@@ -3045,16 +3054,45 @@ IMEInputHandler::OnCurrentTextInputSourc
                                                 const void* aObject,
                                                 CFDictionaryRef aUserInfo)
 {
   // Cache the latest IME opened mode to sLatestIMEOpenedModeInputSourceID.
   TISInputSourceWrapper tis;
   tis.InitByCurrentInputSource();
   if (tis.IsOpenedIMEMode()) {
     tis.GetInputSourceID(sLatestIMEOpenedModeInputSourceID);
+    // Collect Input Source ID which includes input mode in most cases.
+    // However, if it's Japanese IME, collecting input mode (e.g.,
+    // "HiraganaKotei") does not make sense because in most languages,
+    // input mode changes "how to input", but Japanese IME changes
+    // "which type of characters to input".  I.e., only Japanese IME
+    // users may use multiple input modes.  If we'd collect each type of
+    // input mode of Japanese IMEs, it'd be difficult to count actual
+    // users of each IME from the result.  So, only when active IME is
+    // a Japanese IME, we should use Bundle ID which does not contain
+    // input mode instead.
+    nsAutoString key;
+    if (tis.IsForJapaneseLanguage()) {
+      tis.GetBundleID(key);
+    } else {
+      tis.GetInputSourceID(key);
+    }
+    // 72 is kMaximumKeyStringLength in TelemetryScalar.cpp
+    if (key.Length() > 72) {
+      if (NS_IS_LOW_SURROGATE(key[72 - 1]) &&
+          NS_IS_HIGH_SURROGATE(key[72 - 2])) {
+        key.Truncate(72 - 2);
+      } else {
+        key.Truncate(72 - 1);
+      }
+      // U+2026 is "..."
+      key.Append(char16_t(0x2026));
+    }
+    Telemetry::ScalarSet(Telemetry::ScalarID::WIDGET_IME_NAME_ON_MAC,
+                         key, true);
   }
 
   if (MOZ_LOG_TEST(gLog, LogLevel::Info)) {
     static CFStringRef sLastTIS = nullptr;
     CFStringRef newTIS;
     tis.GetInputSourceID(newTIS);
     if (!sLastTIS ||
         ::CFStringCompare(sLastTIS, newTIS, 0) != kCFCompareEqualTo) {
@@ -3140,25 +3178,28 @@ IMEInputHandler::DebugPrintAllIMEModes()
     CFArrayRef list = CreateAllIMEModeList();
     MOZ_LOG(gLog, LogLevel::Info, ("IME mode configuration:"));
     CFIndex idx = ::CFArrayGetCount(list);
     TISInputSourceWrapper tis;
     for (CFIndex i = 0; i < idx; ++i) {
       TISInputSourceRef inputSource = static_cast<TISInputSourceRef>(
         const_cast<void *>(::CFArrayGetValueAtIndex(list, i)));
       tis.InitByTISInputSourceRef(inputSource);
-      nsAutoString name, isid;
+      nsAutoString name, isid, bundleID;
       tis.GetLocalizedName(name);
       tis.GetInputSourceID(isid);
+      tis.GetBundleID(bundleID);
       MOZ_LOG(gLog, LogLevel::Info,
-        ("  %s\t<%s>%s%s\n",
+        ("  %s\t<%s>%s%s\n"
+         "    bundled in <%s>\n",
          NS_ConvertUTF16toUTF8(name).get(),
          NS_ConvertUTF16toUTF8(isid).get(),
          tis.IsASCIICapable() ? "" : "\t(Isn't ASCII capable)",
-         tis.IsEnabled() ? "" : "\t(Isn't Enabled)"));
+         tis.IsEnabled() ? "" : "\t(Isn't Enabled)",
+         NS_ConvertUTF16toUTF8(bundleID).get()));
     }
     ::CFRelease(list);
   }
 }
 
 //static
 TSMDocumentID
 IMEInputHandler::GetCurrentTSMDocumentID()