Bug 1878224 - Put it off to load keyboard layout when first needed r=m_kato

As far as I've tested on Win 11, we can put it off to load it when first needed
of the keyboard layout data.  `ToUnicodeEx` API changes the dead key state and
it's called a lot in `KeyboardLayout::LoadLayout`, however, this patch does
not newly call `LoadLayout` after `TranslateMessage` API at least while handling
a key message.  Therefore, this should be safe but regressions of this change
could be serious, so this makes it enabled only in early beta and earlier for
now.

Differential Revision: https://phabricator.services.mozilla.com/D200600
This commit is contained in:
Masayuki Nakano 2024-02-07 00:24:16 +00:00
parent 84e0f8920d
commit 8b02ebad10
5 changed files with 96 additions and 54 deletions

View File

@ -14765,6 +14765,13 @@
#endif
mirror: always
#ifdef XP_WIN
- name: ui.key.layout.load_when_first_needed
type: bool
value: @IS_EARLY_BETA_OR_EARLIER@
mirror: always
#endif
# Does the access key by itself focus the menu bar?
- name: ui.key.menuAccessKeyFocuses
type: bool

View File

@ -9,6 +9,7 @@
#include "mozilla/AutoRestore.h"
#include "mozilla/MouseEvents.h"
#include "mozilla/MiscEvents.h"
#include "mozilla/StaticPrefs_ui.h"
#include "mozilla/TextEvents.h"
#include "mozilla/widget/WinRegistry.h"
@ -848,7 +849,7 @@ void ModifierKeyState::Update() {
// MODIFIER_ALTGRAPH should be set. Otherwise, i.e., if both Ctrl and Alt
// keys are pressed to emulate AltGr key, MODIFIER_CONTROL and MODIFIER_ALT
// keys should be set separately.
if (KeyboardLayout::GetInstance()->HasAltGr() && IS_VK_DOWN(VK_RMENU)) {
if (IS_VK_DOWN(VK_RMENU) && KeyboardLayout::GetInstance()->HasAltGr()) {
mModifiers |= MODIFIER_ALTGRAPH;
} else {
if (IS_VK_DOWN(VK_CONTROL)) {
@ -1278,10 +1279,10 @@ NativeKey::NativeKey(nsWindow* aWidget, const MSG& aMessage,
MOZ_ASSERT(mDispatcher);
sLatestInstance = this;
KeyboardLayout* keyboardLayout = KeyboardLayout::GetInstance();
mKeyboardLayout = keyboardLayout->GetLayout();
mKeyboardLayout = KeyboardLayout::GetLayout();
if (aOverrideKeyboardLayout && mKeyboardLayout != aOverrideKeyboardLayout) {
keyboardLayout->OverrideLayout(aOverrideKeyboardLayout);
mKeyboardLayout = keyboardLayout->GetLayout();
mKeyboardLayout = keyboardLayout->GetLoadedLayout();
MOZ_ASSERT(mKeyboardLayout == aOverrideKeyboardLayout);
mIsOverridingKeyboardLayout = true;
} else {
@ -3229,9 +3230,10 @@ bool NativeKey::GetFollowingCharMessage(MSG& aCharMsg) {
"\nWM_NULL has been removed: %d, "
"\nNext key message in all windows: %s, "
"time=%ld, ",
KeyboardLayout::GetActiveLayout(),
KeyboardLayout::GetActiveLayoutName().get(), ToString(mMsg).get(),
GetResultOfInSendMessageEx().get(), ToString(kFoundCharMsg).get(), i,
KeyboardLayout::GetInstance()->GetLoadedLayout(),
KeyboardLayout::GetInstance()->GetLoadedLayoutName().get(),
ToString(mMsg).get(), GetResultOfInSendMessageEx().get(),
ToString(kFoundCharMsg).get(), i,
ToString(nextKeyMsgInAllWindows).get(), nextKeyMsgInAllWindows.time);
CrashReporter::AppendAppNotesToCrashReport(info);
MSG nextMsg;
@ -3375,10 +3377,10 @@ bool NativeKey::GetFollowingCharMessage(MSG& aCharMsg) {
"\nHandling message: %s, InSendMessageEx()=%s, "
"\nFound message: %s, "
"\nRemoved message: %s, ",
KeyboardLayout::GetActiveLayout(),
KeyboardLayout::GetActiveLayoutName().get(), ToString(mMsg).get(),
GetResultOfInSendMessageEx().get(), ToString(kFoundCharMsg).get(),
ToString(removedMsg).get());
KeyboardLayout::GetInstance()->GetLoadedLayout(),
KeyboardLayout::GetInstance()->GetLoadedLayoutName().get(),
ToString(mMsg).get(), GetResultOfInSendMessageEx().get(),
ToString(kFoundCharMsg).get(), ToString(removedMsg).get());
CrashReporter::AppendAppNotesToCrashReport(info);
// What's the next key message?
MSG nextKeyMsgAfter;
@ -3417,9 +3419,10 @@ bool NativeKey::GetFollowingCharMessage(MSG& aCharMsg) {
"\nActive keyboard layout=0x%p (%s), "
"\nHandling message: %s, InSendMessageEx()=%s, \n"
"Found message: %s, removed a lot of WM_NULL",
KeyboardLayout::GetActiveLayout(),
KeyboardLayout::GetActiveLayoutName().get(), ToString(mMsg).get(),
GetResultOfInSendMessageEx().get(), ToString(kFoundCharMsg).get());
KeyboardLayout::GetInstance()->GetLoadedLayout(),
KeyboardLayout::GetInstance()->GetLoadedLayoutName().get(),
ToString(mMsg).get(), GetResultOfInSendMessageEx().get(),
ToString(kFoundCharMsg).get());
CrashReporter::AppendAppNotesToCrashReport(info);
MOZ_CRASH("We lost the following char message");
return false;
@ -3814,16 +3817,12 @@ void NativeKey::WillDispatchKeyboardEvent(WidgetKeyboardEvent& aKeyboardEvent,
*****************************************************************************/
KeyboardLayout* KeyboardLayout::sInstance = nullptr;
nsIUserIdleServiceInternal* KeyboardLayout::sIdleService = nullptr;
StaticRefPtr<nsIUserIdleServiceInternal> KeyboardLayout::sIdleService;
// static
KeyboardLayout* KeyboardLayout::GetInstance() {
if (!sInstance) {
sInstance = new KeyboardLayout();
nsCOMPtr<nsIUserIdleServiceInternal> idleService =
do_GetService("@mozilla.org/widget/useridleservice;1");
// The refcount will be decreased at shut down.
sIdleService = idleService.forget().take();
}
return sInstance;
}
@ -3832,19 +3831,23 @@ KeyboardLayout* KeyboardLayout::GetInstance() {
void KeyboardLayout::Shutdown() {
delete sInstance;
sInstance = nullptr;
NS_IF_RELEASE(sIdleService);
sIdleService = nullptr;
}
// static
void KeyboardLayout::NotifyIdleServiceOfUserActivity() {
if (!sIdleService) {
sIdleService = nsCOMPtr<nsIUserIdleServiceInternal>(
do_GetService("@mozilla.org/widget/useridleservice;1"))
.forget();
if (NS_WARN_IF(!sIdleService)) {
return;
}
}
sIdleService->ResetIdleTimeOut(0);
}
KeyboardLayout::KeyboardLayout()
: mKeyboardLayout(0),
mIsOverridden(false),
mIsPendingToRestoreKeyboardLayout(false),
mHasAltGr(false) {
KeyboardLayout::KeyboardLayout() {
mDeadKeyTableListHead = nullptr;
// A dead key sequence should be made from up to 5 keys. Therefore, 4 is
// enough and makes sense because the item is uint8_t.
@ -3853,7 +3856,12 @@ KeyboardLayout::KeyboardLayout()
mActiveDeadKeys.SetCapacity(4);
mDeadKeyShiftStates.SetCapacity(4);
// NOTE: LoadLayout() should be called via OnLayoutChange().
// If we put it off to load active keyboard layout when first needed, we need
// to load it at instanciation. That makes us save the cost of a call of
// GetKeyboardLayout() API.
if (StaticPrefs::ui_key_layout_load_when_first_needed()) {
OnLayoutChange(::GetKeyboardLayout(0));
}
}
KeyboardLayout::~KeyboardLayout() { ReleaseDeadKeyTables(); }
@ -3864,8 +3872,8 @@ bool KeyboardLayout::IsPrintableCharKey(uint8_t aVirtualKey) {
WORD KeyboardLayout::ComputeScanCodeForVirtualKeyCode(
uint8_t aVirtualKeyCode) const {
return static_cast<WORD>(
::MapVirtualKeyEx(aVirtualKeyCode, MAPVK_VK_TO_VSC, GetLayout()));
return static_cast<WORD>(::MapVirtualKeyEx(aVirtualKeyCode, MAPVK_VK_TO_VSC,
KeyboardLayout::GetLayout()));
}
bool KeyboardLayout::IsDeadKey(uint8_t aVirtualKey,
@ -4245,14 +4253,6 @@ char16_t KeyboardLayout::GetCompositeChar(char16_t aBaseChar) const {
return mVirtualKeys[key].GetCompositeChar(mDeadKeyShiftStates[0], aBaseChar);
}
// static
HKL KeyboardLayout::GetActiveLayout() { return GetInstance()->mKeyboardLayout; }
// static
nsCString KeyboardLayout::GetActiveLayoutName() {
return GetInstance()->GetLayoutName(GetActiveLayout());
}
static bool IsValidKeyboardLayoutsChild(const nsAString& aChildName) {
if (aChildName.Length() != 8) {
return false;
@ -4268,7 +4268,8 @@ static bool IsValidKeyboardLayoutsChild(const nsAString& aChildName) {
return true;
}
nsCString KeyboardLayout::GetLayoutName(HKL aLayout) const {
// static
nsCString KeyboardLayout::GetLayoutName(HKL aLayout) {
constexpr auto kKeyboardLayouts =
u"SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\"_ns;
uint16_t language = reinterpret_cast<uintptr_t>(aLayout) & 0xFFFF;
@ -5076,7 +5077,7 @@ KeyNameIndex KeyboardLayout::ConvertNativeKeyCodeToKeyNameIndex(
break;
}
HKL layout = GetLayout();
HKL layout = KeyboardLayout::GetLayout();
WORD langID = LOWORD(static_cast<HKL>(layout));
WORD primaryLangID = PRIMARYLANGID(langID);

View File

@ -14,6 +14,7 @@
#include "nsWindowDefs.h"
#include "mozilla/Attributes.h"
#include "mozilla/EventForwards.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/TextEventDispatcher.h"
#include "mozilla/widget/WinMessages.h"
#include "mozilla/widget/WinModifierKeyState.h"
@ -824,8 +825,34 @@ class KeyboardLayout {
public:
static KeyboardLayout* GetInstance();
static void Shutdown();
static HKL GetActiveLayout();
static nsCString GetActiveLayoutName();
/**
* GetLayout() returns a keyboard layout which has already been loaded in the
* singleton instance or active keyboard layout.
*/
static HKL GetLayout() {
if (!sInstance || sInstance->mIsPendingToRestoreKeyboardLayout) {
return ::GetKeyboardLayout(0);
}
return sInstance->mKeyboardLayout;
}
/**
* GetLoadedLayout() returns a keyboard layout which was loaded in the
* singleton instance. This may be different from the active keyboard layout
* on the system if we override the keyboard layout for synthesizing native
* key events for tests.
*/
HKL GetLoadedLayout() { return mKeyboardLayout; }
/**
* GetLoadedLayoutName() returns the name of the loaded keyboard layout in the
* singleton instance.
*/
nsCString GetLoadedLayoutName() {
return KeyboardLayout::GetLayoutName(mKeyboardLayout);
}
static void NotifyIdleServiceOfUserActivity();
static bool IsPrintableCharKey(uint8_t aVirtualKey);
@ -925,11 +952,6 @@ class KeyboardLayout {
*/
static CodeNameIndex ConvertScanCodeToCodeNameIndex(UINT aScanCode);
HKL GetLayout() const {
return mIsPendingToRestoreKeyboardLayout ? ::GetKeyboardLayout(0)
: mKeyboardLayout;
}
/**
* This wraps MapVirtualKeyEx() API with MAPVK_VK_TO_VSC.
*/
@ -950,17 +972,17 @@ class KeyboardLayout {
~KeyboardLayout();
static KeyboardLayout* sInstance;
static nsIUserIdleServiceInternal* sIdleService;
static StaticRefPtr<nsIUserIdleServiceInternal> sIdleService;
struct DeadKeyTableListEntry {
DeadKeyTableListEntry* next;
uint8_t data[1];
};
HKL mKeyboardLayout;
HKL mKeyboardLayout = nullptr;
VirtualKey mVirtualKeys[NS_NUM_OF_KEYS];
DeadKeyTableListEntry* mDeadKeyTableListHead;
VirtualKey mVirtualKeys[NS_NUM_OF_KEYS] = {};
DeadKeyTableListEntry* mDeadKeyTableListHead = nullptr;
// When mActiveDeadKeys is empty, it's not in dead key sequence.
// Otherwise, it contains virtual keycodes which are pressed in current
// dead key sequence.
@ -970,9 +992,9 @@ class KeyboardLayout {
// mActiveDeadKeys.
nsTArray<VirtualKey::ShiftState> mDeadKeyShiftStates;
bool mIsOverridden;
bool mIsPendingToRestoreKeyboardLayout;
bool mHasAltGr;
bool mIsOverridden = false;
bool mIsPendingToRestoreKeyboardLayout = false;
bool mHasAltGr = false;
static inline int32_t GetKeyIndex(uint8_t aVirtualKey);
static bool AddDeadKeyEntry(char16_t aBaseChar, char16_t aCompositeChar,
@ -1003,7 +1025,7 @@ class KeyboardLayout {
* Gets the keyboard layout name of aLayout. Be careful, this may be too
* slow to call at handling user input.
*/
nsCString GetLayoutName(HKL aLayout) const;
static nsCString GetLayoutName(HKL aLayout);
/**
* InitNativeKey() must be called when actually widget receives WM_KEYDOWN or

View File

@ -6,9 +6,12 @@
#include "WinIMEHandler.h"
#include "IMMHandler.h"
#include "KeyboardLayout.h"
#include "mozilla/Preferences.h"
#include "mozilla/StaticPrefs_intl.h"
#include "mozilla/StaticPrefs_ui.h"
#include "mozilla/TextEvents.h"
#include "mozilla/Unused.h"
#include "mozilla/WindowsVersion.h"
#include "nsWindowDefs.h"
#include "WinTextEventDispatcherListener.h"
@ -134,6 +137,12 @@ void* IMEHandler::GetNativeData(nsWindow* aWindow, uint32_t aDataType) {
// static
bool IMEHandler::ProcessRawKeyMessage(const MSG& aMsg) {
if (StaticPrefs::ui_key_layout_load_when_first_needed()) {
// Getting instance creates the singleton instance and that will
// automatically load active keyboard layout data. We should do that
// before TSF or TranslateMessage handles a key message.
Unused << KeyboardLayout::GetInstance();
}
if (IsTSFAvailable()) {
return TSFTextStore::ProcessRawKeyMessage(aMsg);
}

View File

@ -162,6 +162,7 @@
#include "mozilla/StaticPrefs_dom.h"
#include "mozilla/StaticPrefs_gfx.h"
#include "mozilla/StaticPrefs_layout.h"
#include "mozilla/StaticPrefs_ui.h"
#include "mozilla/StaticPrefs_widget.h"
#include "nsNativeAppSupportWin.h"
#include "mozilla/browser/NimbusFeatures.h"
@ -682,7 +683,9 @@ nsWindow::nsWindow(bool aIsChildWindow)
if (!WinUtils::HasPackageIdentity()) {
mozilla::widget::WinTaskbar::RegisterAppUserModelID();
}
KeyboardLayout::GetInstance()->OnLayoutChange(::GetKeyboardLayout(0));
if (!StaticPrefs::ui_key_layout_load_when_first_needed()) {
KeyboardLayout::GetInstance()->OnLayoutChange(::GetKeyboardLayout(0));
}
#if defined(ACCESSIBILITY)
mozilla::TIPMessageHandler::Initialize();
#endif // defined(ACCESSIBILITY)
@ -5738,7 +5741,7 @@ bool nsWindow::ProcessMessageInternal(UINT msg, WPARAM& wParam, LPARAM& lParam,
sJustGotDeactivate = true;
}
if (mIsTopWidgetWindow) {
mLastKeyboardLayout = KeyboardLayout::GetInstance()->GetLayout();
mLastKeyboardLayout = KeyboardLayout::GetLayout();
}
} else {
StopFlashing();