--- a/widget/gtk/IMContextWrapper.cpp
+++ b/widget/gtk/IMContextWrapper.cpp
@@ -9,16 +9,17 @@
#include "IMContextWrapper.h"
#include "nsGtkKeyUtils.h"
#include "nsWindow.h"
#include "mozilla/AutoRestore.h"
#include "mozilla/Likely.h"
#include "mozilla/MiscEvents.h"
#include "mozilla/Preferences.h"
+#include "mozilla/Telemetry.h"
#include "mozilla/TextEventDispatcher.h"
#include "mozilla/TextEvents.h"
#include "WritingModes.h"
namespace mozilla {
namespace widget {
LazyLogModule gGtkIMLog("nsGtkIMModuleWidgets");
@@ -309,16 +310,56 @@ static bool
IsFcitxInSyncMode()
{
// See fcitx_im_context_class_init() in src/frontend/gtk2/fcitximcontext.c
// https://github.com/fcitx/fcitx/blob/78b98d9230dc9630e99d52e3172bdf440ffd08c4/src/frontend/gtk2/fcitximcontext.c#L395-L398
return GetFcitxBoolEnv("IBUS_ENABLE_SYNC_MODE") ||
GetFcitxBoolEnv("FCITX_ENABLE_SYNC_MODE");
}
+nsDependentCSubstring
+IMContextWrapper::GetIMName() const
+{
+ const char* contextIDChar =
+ gtk_im_multicontext_get_context_id(GTK_IM_MULTICONTEXT(mContext));
+ if (!contextIDChar) {
+ return nsDependentCSubstring();
+ }
+
+ nsDependentCSubstring im(contextIDChar, strlen(contextIDChar));
+
+ // If the context is XIM, actual engine must be specified with
+ // |XMODIFIERS=@im=foo|.
+ const char* xmodifiersChar = PR_GetEnv("XMODIFIERS");
+ if (!im.EqualsLiteral("xim") || !xmodifiersChar) {
+ return im;
+ }
+
+ nsDependentCString xmodifiers(xmodifiersChar);
+ int32_t atIMValueStart = xmodifiers.Find("@im=") + 4;
+ if (atIMValueStart < 4 ||
+ xmodifiers.Length() <= static_cast<size_t>(atIMValueStart)) {
+ return im;
+ }
+
+ int32_t atIMValueEnd =
+ xmodifiers.Find("@", false, atIMValueStart);
+ if (atIMValueEnd > atIMValueStart) {
+ return nsDependentCSubstring(xmodifiersChar + atIMValueStart,
+ atIMValueEnd - atIMValueStart);
+ }
+
+ if (atIMValueEnd == kNotFound) {
+ return nsDependentCSubstring(xmodifiersChar + atIMValueStart,
+ strlen(xmodifiersChar) - atIMValueStart);
+ }
+
+ return im;
+}
+
void
IMContextWrapper::Init()
{
MozContainer* container = mOwnerWindow->GetMozContainer();
MOZ_ASSERT(container, "container is null");
GdkWindow* gdkWindow = gtk_widget_get_window(GTK_WIDGET(container));
// NOTE: gtk_im_*_new() abort (kill) the whole process when it fails.
@@ -334,41 +375,17 @@ IMContextWrapper::Init()
g_signal_connect(mContext, "delete_surrounding",
G_CALLBACK(IMContextWrapper::OnDeleteSurroundingCallback), this);
g_signal_connect(mContext, "commit",
G_CALLBACK(IMContextWrapper::OnCommitCompositionCallback), this);
g_signal_connect(mContext, "preedit_start",
G_CALLBACK(IMContextWrapper::OnStartCompositionCallback), this);
g_signal_connect(mContext, "preedit_end",
G_CALLBACK(IMContextWrapper::OnEndCompositionCallback), this);
- nsDependentCSubstring im;
- const char* contextIDChar =
- gtk_im_multicontext_get_context_id(GTK_IM_MULTICONTEXT(mContext));
- const char* xmodifiersChar = PR_GetEnv("XMODIFIERS");
- if (contextIDChar) {
- im.Rebind(contextIDChar, strlen(contextIDChar));
- // If the context is XIM, actual engine must be specified with
- // |XMODIFIERS=@im=foo|.
- if (im.EqualsLiteral("xim") && xmodifiersChar) {
- nsDependentCString xmodifiers(xmodifiersChar);
- int32_t atIMValueStart = xmodifiers.Find("@im=") + 4;
- if (atIMValueStart >= 4 &&
- xmodifiers.Length() > static_cast<size_t>(atIMValueStart)) {
- int32_t atIMValueEnd =
- xmodifiers.Find("@", false, atIMValueStart);
- if (atIMValueEnd > atIMValueStart) {
- im.Rebind(xmodifiersChar + atIMValueStart,
- atIMValueEnd - atIMValueStart);
- } else if (atIMValueEnd == kNotFound) {
- im.Rebind(xmodifiersChar + atIMValueStart,
- strlen(xmodifiersChar) - atIMValueStart);
- }
- }
- }
- }
+ nsDependentCSubstring im = GetIMName();
if (im.EqualsLiteral("ibus")) {
mIMContextID = IMContextID::eIBus;
mIsIMInAsyncKeyHandlingMode = !IsIBusInSyncMode();
// Although ibus has key snooper mode, it's forcibly disabled on Firefox
// in default settings by its whitelist since we always send key events
// to IME before handling shortcut keys. The whitelist can be
// customized with env, IBUS_NO_SNOOPER_APPS, but we don't need to
// support such rare cases for reducing maintenance cost.
@@ -431,21 +448,24 @@ IMContextWrapper::Init()
// Dummy context
mDummyContext = gtk_im_multicontext_new();
gtk_im_context_set_client_window(mDummyContext, gdkWindow);
MOZ_LOG(gGtkIMLog, LogLevel::Info,
("0x%p Init(), mOwnerWindow=%p, mContext=%p (im=\"%s\"), "
"mIsIMInAsyncKeyHandlingMode=%s, mIsKeySnooped=%s, "
- "mSimpleContext=%p, mDummyContext=%p, contextIDChar=\"%s\", "
- "xmodifiersChar=\"%s\"",
+ "mSimpleContext=%p, mDummyContext=%p, "
+ "gtk_im_multicontext_get_context_id()=\"%s\", "
+ "PR_GetEnv(\"XMODIFIERS\")=\"%s\"",
this, mOwnerWindow, mContext, nsAutoCString(im).get(),
ToChar(mIsIMInAsyncKeyHandlingMode), ToChar(mIsKeySnooped),
- mSimpleContext, mDummyContext, contextIDChar, xmodifiersChar));
+ mSimpleContext, mDummyContext,
+ gtk_im_multicontext_get_context_id(GTK_IM_MULTICONTEXT(mContext)),
+ PR_GetEnv("XMODIFIERS")));
}
IMContextWrapper::~IMContextWrapper()
{
if (this == sLastFocusedContext) {
sLastFocusedContext = nullptr;
}
MOZ_LOG(gGtkIMLog, LogLevel::Info,
@@ -1971,16 +1991,35 @@ IMContextWrapper::DispatchCompositionSta
if (NS_WARN_IF(NS_FAILED(rv))) {
MOZ_LOG(gGtkIMLog, LogLevel::Error,
("0x%p DispatchCompositionStart(), FAILED, "
"due to BeginNativeInputTransaction() failure",
this));
return false;
}
+ static bool sHasSetTelemetry = false;
+ if (!sHasSetTelemetry) {
+ sHasSetTelemetry = true;
+ NS_ConvertUTF8toUTF16 im(GetIMName());
+ // 72 is kMaximumKeyStringLength in TelemetryScalar.cpp
+ if (im.Length() > 72) {
+ if (NS_IS_LOW_SURROGATE(im[72 - 1]) &&
+ NS_IS_HIGH_SURROGATE(im[72 - 2])) {
+ im.Truncate(72 - 2);
+ } else {
+ im.Truncate(72 - 1);
+ }
+ // U+2026 is "..."
+ im.Append(char16_t(0x2026));
+ }
+ Telemetry::ScalarSet(Telemetry::ScalarID::WIDGET_IME_NAME_ON_LINUX,
+ im, true);
+ }
+
MOZ_LOG(gGtkIMLog, LogLevel::Debug,
("0x%p DispatchCompositionStart(), dispatching "
"compositionstart... (mCompositionStart=%u)",
this, mCompositionStart));
mCompositionState = eCompositionState_CompositionStartDispatched;
nsEventStatus status;
dispatcher->StartComposition(status);
if (lastFocusedWindow->IsDestroyed() ||