Bug 1154183 part.1 Move shortcut/access key candidate list creators from nsContentUtils to WidgetKeyboardEvent r=smaug

MozReview-Commit-ID: Ied6qEUc2Kz
This commit is contained in:
Masayuki Nakano 2016-03-18 11:22:37 +09:00
Родитель 358dd6f8a1
Коммит 963976b46d
10 изменённых файлов: 234 добавлений и 227 удалений

Просмотреть файл

@ -4905,188 +4905,6 @@ nsContentUtils::GetLocalizedEllipsis()
return nsDependentString(sBuf);
}
static bool
HasASCIIDigit(const nsTArray<nsShortcutCandidate>& aCandidates)
{
for (uint32_t i = 0; i < aCandidates.Length(); ++i) {
uint32_t ch = aCandidates[i].mCharCode;
if (ch >= '0' && ch <= '9')
return true;
}
return false;
}
static bool
CharsCaseInsensitiveEqual(uint32_t aChar1, uint32_t aChar2)
{
return aChar1 == aChar2 ||
(IS_IN_BMP(aChar1) && IS_IN_BMP(aChar2) &&
ToLowerCase(char16_t(aChar1)) == ToLowerCase(char16_t(aChar2)));
}
static bool
IsCaseChangeableChar(uint32_t aChar)
{
return IS_IN_BMP(aChar) &&
ToLowerCase(char16_t(aChar)) != ToUpperCase(char16_t(aChar));
}
/* static */
void
nsContentUtils::GetAccelKeyCandidates(nsIDOMKeyEvent* aDOMKeyEvent,
nsTArray<nsShortcutCandidate>& aCandidates)
{
NS_PRECONDITION(aCandidates.IsEmpty(), "aCandidates must be empty");
nsAutoString eventType;
aDOMKeyEvent->AsEvent()->GetType(eventType);
// Don't process if aDOMKeyEvent is not a keypress event.
if (!eventType.EqualsLiteral("keypress"))
return;
WidgetKeyboardEvent* nativeKeyEvent =
aDOMKeyEvent->AsEvent()->WidgetEventPtr()->AsKeyboardEvent();
if (nativeKeyEvent) {
NS_ASSERTION(nativeKeyEvent->mClass == eKeyboardEventClass,
"wrong type of native event");
// nsShortcutCandidate::mCharCode is a candidate charCode.
// nsShoftcutCandidate::mIgnoreShift means the mCharCode should be tried to
// execute a command with/without shift key state. If this is TRUE, the
// shifted key state should be ignored. Otherwise, don't ignore the state.
// the priority of the charCodes are (shift key is not pressed):
// 0: charCode/false,
// 1: unshiftedCharCodes[0]/false, 2: unshiftedCharCodes[1]/false...
// the priority of the charCodes are (shift key is pressed):
// 0: charCode/false,
// 1: shiftedCharCodes[0]/false, 2: shiftedCharCodes[0]/true,
// 3: shiftedCharCodes[1]/false, 4: shiftedCharCodes[1]/true...
if (nativeKeyEvent->charCode) {
nsShortcutCandidate key(nativeKeyEvent->charCode, false);
aCandidates.AppendElement(key);
}
uint32_t len = nativeKeyEvent->alternativeCharCodes.Length();
if (!nativeKeyEvent->IsShift()) {
for (uint32_t i = 0; i < len; ++i) {
uint32_t ch =
nativeKeyEvent->alternativeCharCodes[i].mUnshiftedCharCode;
if (!ch || ch == nativeKeyEvent->charCode)
continue;
nsShortcutCandidate key(ch, false);
aCandidates.AppendElement(key);
}
// If unshiftedCharCodes doesn't have numeric but shiftedCharCode has it,
// this keyboard layout is AZERTY or similar layout, probably.
// In this case, Accel+[0-9] should be accessible without shift key.
// However, the priority should be lowest.
if (!HasASCIIDigit(aCandidates)) {
for (uint32_t i = 0; i < len; ++i) {
uint32_t ch =
nativeKeyEvent->alternativeCharCodes[i].mShiftedCharCode;
if (ch >= '0' && ch <= '9') {
nsShortcutCandidate key(ch, false);
aCandidates.AppendElement(key);
break;
}
}
}
} else {
for (uint32_t i = 0; i < len; ++i) {
uint32_t ch = nativeKeyEvent->alternativeCharCodes[i].mShiftedCharCode;
if (!ch)
continue;
if (ch != nativeKeyEvent->charCode) {
nsShortcutCandidate key(ch, false);
aCandidates.AppendElement(key);
}
// If the char is an alphabet, the shift key state should not be
// ignored. E.g., Ctrl+Shift+C should not execute Ctrl+C.
// And checking the charCode is same as unshiftedCharCode too.
// E.g., for Ctrl+Shift+(Plus of Numpad) should not run Ctrl+Plus.
uint32_t unshiftCh =
nativeKeyEvent->alternativeCharCodes[i].mUnshiftedCharCode;
if (CharsCaseInsensitiveEqual(ch, unshiftCh))
continue;
// On the Hebrew keyboard layout on Windows, the unshifted char is a
// localized character but the shifted char is a Latin alphabet,
// then, we should not execute without the shift state. See bug 433192.
if (IsCaseChangeableChar(ch))
continue;
// Setting the alternative charCode candidates for retry without shift
// key state only when the shift key is pressed.
nsShortcutCandidate key(ch, true);
aCandidates.AppendElement(key);
}
}
// Special case for "Space" key. With some keyboard layouts, "Space" with
// or without Shift key causes non-ASCII space. For such keyboard layouts,
// we should guarantee that the key press works as an ASCII white space key
// press.
if (nativeKeyEvent->mCodeNameIndex == CODE_NAME_INDEX_Space &&
nativeKeyEvent->charCode != static_cast<uint32_t>(' ')) {
nsShortcutCandidate spaceKey(static_cast<uint32_t>(' '), false);
aCandidates.AppendElement(spaceKey);
}
} else {
uint32_t charCode;
aDOMKeyEvent->GetCharCode(&charCode);
if (charCode) {
nsShortcutCandidate key(charCode, false);
aCandidates.AppendElement(key);
}
}
}
/* static */
void
nsContentUtils::GetAccessKeyCandidates(WidgetKeyboardEvent* aNativeKeyEvent,
nsTArray<uint32_t>& aCandidates)
{
NS_PRECONDITION(aCandidates.IsEmpty(), "aCandidates must be empty");
// return the lower cased charCode candidates for access keys.
// the priority of the charCodes are:
// 0: charCode, 1: unshiftedCharCodes[0], 2: shiftedCharCodes[0]
// 3: unshiftedCharCodes[1], 4: shiftedCharCodes[1],...
if (aNativeKeyEvent->charCode) {
uint32_t ch = aNativeKeyEvent->charCode;
if (IS_IN_BMP(ch))
ch = ToLowerCase(char16_t(ch));
aCandidates.AppendElement(ch);
}
for (uint32_t i = 0;
i < aNativeKeyEvent->alternativeCharCodes.Length(); ++i) {
uint32_t ch[2] =
{ aNativeKeyEvent->alternativeCharCodes[i].mUnshiftedCharCode,
aNativeKeyEvent->alternativeCharCodes[i].mShiftedCharCode };
for (uint32_t j = 0; j < 2; ++j) {
if (!ch[j])
continue;
if (IS_IN_BMP(ch[j]))
ch[j] = ToLowerCase(char16_t(ch[j]));
// Don't append the charCode that was already appended.
if (aCandidates.IndexOf(ch[j]) == aCandidates.NoIndex)
aCandidates.AppendElement(ch[j]);
}
}
// Special case for "Space" key. With some keyboard layouts, "Space" with
// or without Shift key causes non-ASCII space. For such keyboard layouts,
// we should guarantee that the key press works as an ASCII white space key
// press.
if (aNativeKeyEvent->mCodeNameIndex == CODE_NAME_INDEX_Space &&
aNativeKeyEvent->charCode != static_cast<uint32_t>(' ')) {
aCandidates.AppendElement(static_cast<uint32_t>(' '));
}
return;
}
/* static */
void
nsContentUtils::AddScriptBlocker()

Просмотреть файл

@ -171,15 +171,6 @@ struct EventNameMapping
mozilla::EventClassID mEventClassID;
};
struct nsShortcutCandidate {
nsShortcutCandidate(uint32_t aCharCode, bool aIgnoreShift) :
mCharCode(aCharCode), mIgnoreShift(aIgnoreShift)
{
}
uint32_t mCharCode;
bool mIgnoreShift;
};
typedef void (*CallOnRemoteChildFunction) (mozilla::dom::TabParent* aTabParent,
void* aArg);
@ -1511,27 +1502,6 @@ public:
*/
static const nsDependentString GetLocalizedEllipsis();
/**
* Get the candidates for accelkeys for aDOMKeyEvent.
*
* @param aDOMKeyEvent [in] the key event for accelkey handling.
* @param aCandidates [out] the candidate shortcut key combination list.
* the first item is most preferred.
*/
static void GetAccelKeyCandidates(nsIDOMKeyEvent* aDOMKeyEvent,
nsTArray<nsShortcutCandidate>& aCandidates);
/**
* Get the candidates for accesskeys for aNativeKeyEvent.
*
* @param aNativeKeyEvent [in] the key event for accesskey handling.
* @param aCandidates [out] the candidate access key list.
* the first item is most preferred.
*/
static void GetAccessKeyCandidates(
mozilla::WidgetKeyboardEvent* aNativeKeyEvent,
nsTArray<uint32_t>& aCandidates);
/**
* Hide any XUL popups associated with aDocument, including any documents
* displayed in child frames. Does nothing if aDocument is null.

Просмотреть файл

@ -733,7 +733,7 @@ EventStateManager::PreHandleEvent(nsPresContext* aPresContext,
(modifierMask == Prefs::ChromeAccessModifierMask() ||
modifierMask == Prefs::ContentAccessModifierMask())) {
AutoTArray<uint32_t, 10> accessCharCodes;
nsContentUtils::GetAccessKeyCandidates(keyEvent, accessCharCodes);
keyEvent->GetAccessKeyCandidates(accessCharCodes);
if (HandleAccessKey(aPresContext, accessCharCodes,
keyEvent->mFlags.mIsTrusted, modifierMask)) {

Просмотреть файл

@ -140,18 +140,21 @@ nsXBLKeyEventHandler::HandleEvent(nsIDOMEvent* aEvent)
if (!key)
return NS_OK;
AutoTArray<nsShortcutCandidate, 10> accessKeys;
nsContentUtils::GetAccelKeyCandidates(key, accessKeys);
WidgetKeyboardEvent* nativeKeyboardEvent =
aEvent->WidgetEventPtr()->AsKeyboardEvent();
MOZ_ASSERT(nativeKeyboardEvent);
AutoShortcutKeyCandidateArray shortcutKeys;
nativeKeyboardEvent->GetShortcutKeyCandidates(shortcutKeys);
if (accessKeys.IsEmpty()) {
if (shortcutKeys.IsEmpty()) {
ExecuteMatchedHandlers(key, 0, IgnoreModifierState());
return NS_OK;
}
for (uint32_t i = 0; i < accessKeys.Length(); ++i) {
for (uint32_t i = 0; i < shortcutKeys.Length(); ++i) {
IgnoreModifierState ignoreModifierState;
ignoreModifierState.mShift = accessKeys[i].mIgnoreShift;
if (ExecuteMatchedHandlers(key, accessKeys[i].mCharCode,
ignoreModifierState.mShift = shortcutKeys[i].mIgnoreShift;
if (ExecuteMatchedHandlers(key, shortcutKeys[i].mCharCode,
ignoreModifierState)) {
return NS_OK;
}

Просмотреть файл

@ -543,17 +543,21 @@ nsXBLWindowKeyHandler::WalkHandlersInternal(nsIDOMKeyEvent* aKeyEvent,
bool aExecute,
bool* aOutReservedForChrome)
{
AutoTArray<nsShortcutCandidate, 10> accessKeys;
nsContentUtils::GetAccelKeyCandidates(aKeyEvent, accessKeys);
WidgetKeyboardEvent* nativeKeyboardEvent =
aKeyEvent->AsEvent()->WidgetEventPtr()->AsKeyboardEvent();
MOZ_ASSERT(nativeKeyboardEvent);
if (accessKeys.IsEmpty()) {
AutoShortcutKeyCandidateArray shortcutKeys;
nativeKeyboardEvent->GetShortcutKeyCandidates(shortcutKeys);
if (shortcutKeys.IsEmpty()) {
return WalkHandlersAndExecute(aKeyEvent, aEventType, aHandler,
0, IgnoreModifierState(),
aExecute, aOutReservedForChrome);
}
for (uint32_t i = 0; i < accessKeys.Length(); ++i) {
nsShortcutCandidate &key = accessKeys[i];
for (uint32_t i = 0; i < shortcutKeys.Length(); ++i) {
ShortcutKeyCandidate& key = shortcutKeys[i];
IgnoreModifierState ignoreModifierState;
ignoreModifierState.mShift = key.mIgnoreShift;
if (WalkHandlersAndExecute(aKeyEvent, aEventType, aHandler,

Просмотреть файл

@ -167,8 +167,9 @@ nsMenuBarFrame::FindMenuWithShortcut(nsIDOMKeyEvent* aKeyEvent)
AutoTArray<uint32_t, 10> accessKeys;
WidgetKeyboardEvent* nativeKeyEvent =
aKeyEvent->AsEvent()->WidgetEventPtr()->AsKeyboardEvent();
if (nativeKeyEvent)
nsContentUtils::GetAccessKeyCandidates(nativeKeyEvent, accessKeys);
if (nativeKeyEvent) {
nativeKeyEvent->GetAccessKeyCandidates(accessKeys);
}
if (accessKeys.IsEmpty() && charCode)
accessKeys.AppendElement(charCode);

Просмотреть файл

@ -222,7 +222,7 @@ nsMenuBarListener::KeyPress(nsIDOMEvent* aKeyEvent)
aKeyEvent->WidgetEventPtr()->AsKeyboardEvent();
if (nativeKeyEvent) {
AutoTArray<uint32_t, 10> keys;
nsContentUtils::GetAccessKeyCandidates(nativeKeyEvent, keys);
nativeKeyEvent->GetAccessKeyCandidates(keys);
hasAccessKeyCandidates = !keys.IsEmpty();
}
}

Просмотреть файл

@ -8,6 +8,8 @@
#include <stdint.h>
#include "nsTArray.h"
/**
* XXX Following enums should be in BasicEvents.h. However, currently, it's
* impossible to use foward delearation for enum.
@ -139,6 +141,10 @@ class WidgetEventTime;
// TextEvents.h
struct AlternativeCharCode;
struct ShortcutKeyCandidate;
typedef nsTArray<ShortcutKeyCandidate> ShortcutKeyCandidateArray;
typedef AutoTArray<ShortcutKeyCandidate, 10> AutoShortcutKeyCandidateArray;
// TextRange.h
struct TextRangeStyle;

Просмотреть файл

@ -70,6 +70,26 @@ struct AlternativeCharCode
uint32_t mShiftedCharCode;
};
/******************************************************************************
* mozilla::ShortcutKeyCandidate
*
* This stores a candidate of shortcut key combination.
******************************************************************************/
struct ShortcutKeyCandidate
{
ShortcutKeyCandidate(uint32_t aCharCode, bool aIgnoreShift)
: mCharCode(aCharCode)
, mIgnoreShift(aIgnoreShift)
{
}
// The charCode value which must match keyboard shortcut definition.
uint32_t mCharCode;
// true if Shift state can be ignored. Otherwise, Shift key state must
// match keyboard shortcut definition.
bool mIgnoreShift;
};
/******************************************************************************
* mozilla::WidgetKeyboardEvent
******************************************************************************/
@ -211,6 +231,22 @@ public:
return GetModifierForKeyName(mKeyNameIndex) != MODIFIER_NONE;
}
/**
* Get the candidates for shortcut key.
*
* @param aCandidates [out] the candidate shortcut key combination list.
* the first item is most preferred.
*/
void GetShortcutKeyCandidates(ShortcutKeyCandidateArray& aCandidates);
/**
* Get the candidates for access key.
*
* @param aCandidates [out] the candidate access key list.
* the first item is most preferred.
*/
void GetAccessKeyCandidates(nsTArray<uint32_t>& aCandidates);
static void Shutdown();
/**

Просмотреть файл

@ -449,6 +449,175 @@ WidgetKeyboardEvent::ShouldCauseKeypressEvents() const
}
}
static bool
HasASCIIDigit(const ShortcutKeyCandidateArray& aCandidates)
{
for (uint32_t i = 0; i < aCandidates.Length(); ++i) {
uint32_t ch = aCandidates[i].mCharCode;
if (ch >= '0' && ch <= '9')
return true;
}
return false;
}
static bool
CharsCaseInsensitiveEqual(uint32_t aChar1, uint32_t aChar2)
{
return aChar1 == aChar2 ||
(IS_IN_BMP(aChar1) && IS_IN_BMP(aChar2) &&
ToLowerCase(static_cast<char16_t>(aChar1)) ==
ToLowerCase(static_cast<char16_t>(aChar2)));
}
static bool
IsCaseChangeableChar(uint32_t aChar)
{
return IS_IN_BMP(aChar) &&
ToLowerCase(static_cast<char16_t>(aChar)) !=
ToUpperCase(static_cast<char16_t>(aChar));
}
void
WidgetKeyboardEvent::GetShortcutKeyCandidates(
ShortcutKeyCandidateArray& aCandidates)
{
MOZ_ASSERT(aCandidates.IsEmpty(), "aCandidates must be empty");
if (mMessage != eKeyPress) {
return;
}
// ShortcutKeyCandidate::mCharCode is a candidate charCode.
// ShortcutKeyCandidate::mIgnoreShift means the mCharCode should be tried to
// execute a command with/without shift key state. If this is TRUE, the
// shifted key state should be ignored. Otherwise, don't ignore the state.
// the priority of the charCodes are (shift key is not pressed):
// 0: charCode/false,
// 1: unshiftedCharCodes[0]/false, 2: unshiftedCharCodes[1]/false...
// the priority of the charCodes are (shift key is pressed):
// 0: charCode/false,
// 1: shiftedCharCodes[0]/false, 2: shiftedCharCodes[0]/true,
// 3: shiftedCharCodes[1]/false, 4: shiftedCharCodes[1]/true...
if (charCode) {
ShortcutKeyCandidate key(charCode, false);
aCandidates.AppendElement(key);
}
uint32_t len = alternativeCharCodes.Length();
if (!IsShift()) {
for (uint32_t i = 0; i < len; ++i) {
uint32_t ch = alternativeCharCodes[i].mUnshiftedCharCode;
if (!ch || ch == charCode) {
continue;
}
ShortcutKeyCandidate key(ch, false);
aCandidates.AppendElement(key);
}
// If unshiftedCharCodes doesn't have numeric but shiftedCharCode has it,
// this keyboard layout is AZERTY or similar layout, probably.
// In this case, Accel+[0-9] should be accessible without shift key.
// However, the priority should be lowest.
if (!HasASCIIDigit(aCandidates)) {
for (uint32_t i = 0; i < len; ++i) {
uint32_t ch = alternativeCharCodes[i].mShiftedCharCode;
if (ch >= '0' && ch <= '9') {
ShortcutKeyCandidate key(ch, false);
aCandidates.AppendElement(key);
break;
}
}
}
} else {
for (uint32_t i = 0; i < len; ++i) {
uint32_t ch = alternativeCharCodes[i].mShiftedCharCode;
if (!ch) {
continue;
}
if (ch != charCode) {
ShortcutKeyCandidate key(ch, false);
aCandidates.AppendElement(key);
}
// If the char is an alphabet, the shift key state should not be
// ignored. E.g., Ctrl+Shift+C should not execute Ctrl+C.
// And checking the charCode is same as unshiftedCharCode too.
// E.g., for Ctrl+Shift+(Plus of Numpad) should not run Ctrl+Plus.
uint32_t unshiftCh = alternativeCharCodes[i].mUnshiftedCharCode;
if (CharsCaseInsensitiveEqual(ch, unshiftCh)) {
continue;
}
// On the Hebrew keyboard layout on Windows, the unshifted char is a
// localized character but the shifted char is a Latin alphabet,
// then, we should not execute without the shift state. See bug 433192.
if (IsCaseChangeableChar(ch)) {
continue;
}
// Setting the alternative charCode candidates for retry without shift
// key state only when the shift key is pressed.
ShortcutKeyCandidate key(ch, true);
aCandidates.AppendElement(key);
}
}
// Special case for "Space" key. With some keyboard layouts, "Space" with
// or without Shift key causes non-ASCII space. For such keyboard layouts,
// we should guarantee that the key press works as an ASCII white space key
// press.
if (mCodeNameIndex == CODE_NAME_INDEX_Space &&
charCode != static_cast<uint32_t>(' ')) {
ShortcutKeyCandidate spaceKey(static_cast<uint32_t>(' '), false);
aCandidates.AppendElement(spaceKey);
}
}
void
WidgetKeyboardEvent::GetAccessKeyCandidates(nsTArray<uint32_t>& aCandidates)
{
MOZ_ASSERT(aCandidates.IsEmpty(), "aCandidates must be empty");
// return the lower cased charCode candidates for access keys.
// the priority of the charCodes are:
// 0: charCode, 1: unshiftedCharCodes[0], 2: shiftedCharCodes[0]
// 3: unshiftedCharCodes[1], 4: shiftedCharCodes[1],...
if (charCode) {
uint32_t ch = charCode;
if (IS_IN_BMP(ch)) {
ch = ToLowerCase(static_cast<char16_t>(ch));
}
aCandidates.AppendElement(ch);
}
for (uint32_t i = 0; i < alternativeCharCodes.Length(); ++i) {
uint32_t ch[2] =
{ alternativeCharCodes[i].mUnshiftedCharCode,
alternativeCharCodes[i].mShiftedCharCode };
for (uint32_t j = 0; j < 2; ++j) {
if (!ch[j]) {
continue;
}
if (IS_IN_BMP(ch[j])) {
ch[j] = ToLowerCase(static_cast<char16_t>(ch[j]));
}
// Don't append the charCode that was already appended.
if (aCandidates.IndexOf(ch[j]) == aCandidates.NoIndex) {
aCandidates.AppendElement(ch[j]);
}
}
}
// Special case for "Space" key. With some keyboard layouts, "Space" with
// or without Shift key causes non-ASCII space. For such keyboard layouts,
// we should guarantee that the key press works as an ASCII white space key
// press.
if (mCodeNameIndex == CODE_NAME_INDEX_Space &&
charCode != static_cast<uint32_t>(' ')) {
aCandidates.AppendElement(static_cast<uint32_t>(' '));
}
return;
}
/* static */ void
WidgetKeyboardEvent::Shutdown()
{