Bug 282097 - Part 4: Create NativeKeyBindings for Cocoa. r=masayuki, r=smichaud

This commit is contained in:
J. Ryan Stinnett 2013-07-10 09:08:55 -05:00
parent f4d2c62553
commit 6a5a431446
9 changed files with 596 additions and 9 deletions

View File

@ -52,6 +52,7 @@ CMMSRCS = \
nsMacWebAppUtils.mm \
GfxInfo.mm \
WidgetTraceEvent.mm \
NativeKeyBindings.mm \
$(NULL)
ifeq (x86_64,$(TARGET_CPU))

View File

@ -0,0 +1,72 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef NativeKeyBindings_h_
#define NativeKeyBindings_h_
#include "nsINativeKeyBindings.h"
#import <Cocoa/Cocoa.h>
#include "mozilla/Attributes.h"
#include "nsDataHashtable.h"
// 8477f934-febf-4c79-b7fe-bb7f9ebb9b4f
#define NS_NATIVEKEYBINDINGS_INPUT_CID \
{ 0x8477f934, 0xfebf, 0x4c79, \
{ 0xb7, 0xfe, 0xbb, 0x7f, 0x9e, 0xbb, 0x9b, 0x4f } }
// 13a6e56f-f00b-4e19-8cf6-1a51ee7cc4bf
#define NS_NATIVEKEYBINDINGS_TEXTAREA_CID \
{ 0x13a6e56f, 0xf00b, 0x4e19, \
{ 0x8c, 0xf6, 0x1a, 0x51, 0xee, 0x7c, 0xc4, 0xbf } }
// 36bfbd29-4e02-40f4-8fff-094f1a9ec97c
#define NS_NATIVEKEYBINDINGS_EDITOR_CID \
{ 0x36bfbd29, 0x4e02, 0x40f4, \
{ 0x8f, 0xff, 0x09, 0x4f, 0x1a, 0x9e, 0xc9, 0x7c } }
namespace mozilla {
namespace widget {
enum NativeKeyBindingsType
{
eNativeKeyBindingsType_Input,
eNativeKeyBindingsType_TextArea,
eNativeKeyBindingsType_Editor
};
typedef nsDataHashtable<nsPtrHashKey<struct objc_selector>, const char *>
SelectorCommandHashtable;
class NativeKeyBindings MOZ_FINAL : public nsINativeKeyBindings
{
public:
NativeKeyBindings();
NS_DECL_ISUPPORTS
NS_IMETHOD Init(NativeKeyBindingsType aType);
// nsINativeKeyBindings
NS_IMETHOD_(bool) KeyDown(const nsNativeKeyEvent& aEvent,
DoCommandCallback aCallback,
void* aCallbackData);
NS_IMETHOD_(bool) KeyPress(const nsNativeKeyEvent& aEvent,
DoCommandCallback aCallback,
void* aCallbackData);
NS_IMETHOD_(bool) KeyUp(const nsNativeKeyEvent& aEvent,
DoCommandCallback aCallback,
void* aCallbackData);
private:
SelectorCommandHashtable mSelectorToCommand;
}; // NativeKeyBindings
} // namespace widget
} // namespace mozilla
#endif /* NativeKeyBindings_h_ */

View File

@ -0,0 +1,290 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "NativeKeyBindings.h"
#include "nsTArray.h"
#include "nsCocoaUtils.h"
#include "nsGUIEvent.h"
#include "prlog.h"
using namespace mozilla;
using namespace mozilla::widget;
#ifdef PR_LOGGING
PRLogModuleInfo* gNativeKeyBindingsLog = nullptr;
#endif
NativeKeyBindings::NativeKeyBindings()
{
}
#define SEL_TO_COMMAND(aSel, aCommand) \
mSelectorToCommand.Put( \
reinterpret_cast<struct objc_selector *>(@selector(aSel)), aCommand)
NS_IMETHODIMP
NativeKeyBindings::Init(NativeKeyBindingsType aType)
{
#ifdef PR_LOGGING
if (!gNativeKeyBindingsLog) {
gNativeKeyBindingsLog = PR_NewLogModule("NativeKeyBindings");
}
#endif
PR_LOG(gNativeKeyBindingsLog, PR_LOG_ALWAYS,
("%p NativeKeyBindings::Init", this));
mSelectorToCommand.Init();
// Many selectors have a one-to-one mapping to a Gecko command. Those mappings
// are registered in mSelectorToCommand.
// Selectors from NSResponder's "Responding to Action Messages" section and
// from NSText's "Action Methods for Editing" section
// TODO: Improves correctness of left / right meaning
// TODO: Add real paragraph motions
// SEL_TO_COMMAND(cancelOperation:, );
// SEL_TO_COMMAND(capitalizeWord:, );
// SEL_TO_COMMAND(centerSelectionInVisibleArea:, );
// SEL_TO_COMMAND(changeCaseOfLetter:, );
// SEL_TO_COMMAND(complete:, );
SEL_TO_COMMAND(copy:, "cmd_copy");
// SEL_TO_COMMAND(copyFont:, );
// SEL_TO_COMMAND(copyRuler:, );
SEL_TO_COMMAND(cut:, "cmd_cut");
SEL_TO_COMMAND(delete:, "cmd_delete");
SEL_TO_COMMAND(deleteBackward:, "cmd_deleteCharBackward");
// SEL_TO_COMMAND(deleteBackwardByDecomposingPreviousCharacter:, );
SEL_TO_COMMAND(deleteForward:, "cmd_deleteCharForward");
// TODO: deleteTo* selectors are also supposed to add text to a kill buffer
SEL_TO_COMMAND(deleteToBeginningOfLine:, "cmd_deleteToBeginningOfLine");
if (aType == eNativeKeyBindingsType_Input) {
SEL_TO_COMMAND(deleteToBeginningOfParagraph:,
"cmd_deleteToBeginningOfLine");
}
SEL_TO_COMMAND(deleteToEndOfLine:, "cmd_deleteToEndOfLine");
if (aType == eNativeKeyBindingsType_Input) {
SEL_TO_COMMAND(deleteToEndOfParagraph:, "cmd_deleteToEndOfLine");
}
// SEL_TO_COMMAND(deleteToMark:, );
SEL_TO_COMMAND(deleteWordBackward:, "cmd_deleteWordBackward");
SEL_TO_COMMAND(deleteWordForward:, "cmd_deleteWordForward");
// SEL_TO_COMMAND(indent:, );
// SEL_TO_COMMAND(insertBacktab:, );
// SEL_TO_COMMAND(insertContainerBreak:, );
// SEL_TO_COMMAND(insertLineBreak:, );
// SEL_TO_COMMAND(insertNewline:, );
// SEL_TO_COMMAND(insertNewlineIgnoringFieldEditor:, );
// SEL_TO_COMMAND(insertParagraphSeparator:, );
// SEL_TO_COMMAND(insertTab:, );
// SEL_TO_COMMAND(insertTabIgnoringFieldEditor:, );
// SEL_TO_COMMAND(insertDoubleQuoteIgnoringSubstitution:, );
// SEL_TO_COMMAND(insertSingleQuoteIgnoringSubstitution:, );
// SEL_TO_COMMAND(lowercaseWord:, );
SEL_TO_COMMAND(moveBackward:, "cmd_charPrevious");
SEL_TO_COMMAND(moveBackwardAndModifySelection:, "cmd_selectCharPrevious");
if (aType == eNativeKeyBindingsType_Input) {
SEL_TO_COMMAND(moveDown:, "cmd_endLine");
} else {
SEL_TO_COMMAND(moveDown:, "cmd_lineNext");
}
SEL_TO_COMMAND(moveDownAndModifySelection:, "cmd_selectLineNext");
SEL_TO_COMMAND(moveForward:, "cmd_charNext");
SEL_TO_COMMAND(moveForwardAndModifySelection:, "cmd_selectCharNext");
SEL_TO_COMMAND(moveLeft:, "cmd_charPrevious");
SEL_TO_COMMAND(moveLeftAndModifySelection:, "cmd_selectCharPrevious");
if (aType == eNativeKeyBindingsType_Input) {
SEL_TO_COMMAND(moveParagraphBackwardAndModifySelection:,
"cmd_selectBeginLine");
SEL_TO_COMMAND(moveParagraphForwardAndModifySelection:,
"cmd_selectEndLine");
}
SEL_TO_COMMAND(moveRight:, "cmd_charNext");
SEL_TO_COMMAND(moveRightAndModifySelection:, "cmd_selectCharNext");
SEL_TO_COMMAND(moveToBeginningOfDocument:, "cmd_moveTop");
SEL_TO_COMMAND(moveToBeginningOfDocumentAndModifySelection:, "cmd_selectTop");
SEL_TO_COMMAND(moveToBeginningOfLine:, "cmd_beginLine");
SEL_TO_COMMAND(moveToBeginningOfLineAndModifySelection:,
"cmd_selectBeginLine");
if (aType == eNativeKeyBindingsType_Input) {
SEL_TO_COMMAND(moveToBeginningOfParagraph:, "cmd_beginLine");
SEL_TO_COMMAND(moveToBeginningOfParagraphAndModifySelection:,
"cmd_selectBeginLine");
}
SEL_TO_COMMAND(moveToEndOfDocument:, "cmd_moveBottom");
SEL_TO_COMMAND(moveToEndOfDocumentAndModifySelection:, "cmd_selectBottom");
SEL_TO_COMMAND(moveToEndOfLine:, "cmd_endLine");
SEL_TO_COMMAND(moveToEndOfLineAndModifySelection:, "cmd_selectEndLine");
if (aType == eNativeKeyBindingsType_Input) {
SEL_TO_COMMAND(moveToEndOfParagraph:, "cmd_endLine");
SEL_TO_COMMAND(moveToEndOfParagraphAndModifySelection:,
"cmd_selectEndLine");
}
SEL_TO_COMMAND(moveToLeftEndOfLine:, "cmd_beginLine");
SEL_TO_COMMAND(moveToLeftEndOfLineAndModifySelection:, "cmd_selectBeginLine");
SEL_TO_COMMAND(moveToRightEndOfLine:, "cmd_endLine");
SEL_TO_COMMAND(moveToRightEndOfLineAndModifySelection:, "cmd_selectEndLine");
if (aType == eNativeKeyBindingsType_Input) {
SEL_TO_COMMAND(moveUp:, "cmd_beginLine");
} else {
SEL_TO_COMMAND(moveUp:, "cmd_linePrevious");
}
SEL_TO_COMMAND(moveUpAndModifySelection:, "cmd_selectLinePrevious");
SEL_TO_COMMAND(moveWordBackward:, "cmd_wordPrevious");
SEL_TO_COMMAND(moveWordBackwardAndModifySelection:, "cmd_selectWordPrevious");
SEL_TO_COMMAND(moveWordForward:, "cmd_wordNext");
SEL_TO_COMMAND(moveWordForwardAndModifySelection:, "cmd_selectWordNext");
SEL_TO_COMMAND(moveWordLeft:, "cmd_wordPrevious");
SEL_TO_COMMAND(moveWordLeftAndModifySelection:, "cmd_selectWordPrevious");
SEL_TO_COMMAND(moveWordRight:, "cmd_wordNext");
SEL_TO_COMMAND(moveWordRightAndModifySelection:, "cmd_selectWordNext");
SEL_TO_COMMAND(pageDown:, "cmd_movePageDown");
SEL_TO_COMMAND(pageDownAndModifySelection:, "cmd_selectPageDown");
SEL_TO_COMMAND(pageUp:, "cmd_movePageUp");
SEL_TO_COMMAND(pageUpAndModifySelection:, "cmd_selectPageUp");
SEL_TO_COMMAND(paste:, "cmd_paste");
// SEL_TO_COMMAND(pasteFont:, );
// SEL_TO_COMMAND(pasteRuler:, );
SEL_TO_COMMAND(scrollLineDown:, "cmd_scrollLineDown");
SEL_TO_COMMAND(scrollLineUp:, "cmd_scrollLineUp");
SEL_TO_COMMAND(scrollPageDown:, "cmd_scrollPageDown");
SEL_TO_COMMAND(scrollPageUp:, "cmd_scrollPageUp");
SEL_TO_COMMAND(scrollToBeginningOfDocument:, "cmd_scrollTop");
SEL_TO_COMMAND(scrollToEndOfDocument:, "cmd_scrollBottom");
SEL_TO_COMMAND(selectAll:, "cmd_selectAll");
// selectLine: is complex, see KeyDown
if (aType == eNativeKeyBindingsType_Input) {
SEL_TO_COMMAND(selectParagraph:, "cmd_selectAll");
}
// SEL_TO_COMMAND(selectToMark:, );
// selectWord: is complex, see KeyDown
// SEL_TO_COMMAND(setMark:, );
// SEL_TO_COMMAND(showContextHelp:, );
// SEL_TO_COMMAND(supplementalTargetForAction:sender:, );
// SEL_TO_COMMAND(swapWithMark:, );
// SEL_TO_COMMAND(transpose:, );
// SEL_TO_COMMAND(transposeWords:, );
// SEL_TO_COMMAND(uppercaseWord:, );
// SEL_TO_COMMAND(yank:, );
return NS_OK;
}
#undef SEL_TO_COMMAND
NS_IMPL_ISUPPORTS1(NativeKeyBindings, nsINativeKeyBindings)
NS_IMETHODIMP_(bool)
NativeKeyBindings::KeyDown(const nsNativeKeyEvent& aEvent,
DoCommandCallback aCallback, void* aCallbackData)
{
return false;
}
NS_IMETHODIMP_(bool)
NativeKeyBindings::KeyPress(const nsNativeKeyEvent& aEvent,
DoCommandCallback aCallback, void* aCallbackData)
{
PR_LOG(gNativeKeyBindingsLog, PR_LOG_ALWAYS,
("%p NativeKeyBindings::KeyPress", this));
// Recover the current event, which should always be the key down we are
// responding to.
nsKeyEvent* geckoEvent = aEvent.mGeckoEvent;
MOZ_ASSERT(geckoEvent);
NSEvent* cocoaEvent = reinterpret_cast<NSEvent*>(geckoEvent->mNativeKeyEvent);
if (!cocoaEvent || [cocoaEvent type] != NSKeyDown) {
PR_LOG(gNativeKeyBindingsLog, PR_LOG_ALWAYS,
("%p NativeKeyBindings::KeyPress, no Cocoa key down event", this));
return false;
}
PR_LOG(gNativeKeyBindingsLog, PR_LOG_ALWAYS,
("%p NativeKeyBindings::KeyPress, interpreting", this));
nsAutoTArray<KeyBindingsCommand, 2> bindingCommands;
nsCocoaUtils::GetCommandsFromKeyEvent(cocoaEvent, bindingCommands);
PR_LOG(gNativeKeyBindingsLog, PR_LOG_ALWAYS,
("%p NativeKeyBindings::KeyPress, bindingCommands=%u",
this, bindingCommands.Length()));
nsAutoTArray<const char*, 4> geckoCommands;
for (uint32_t i = 0; i < bindingCommands.Length(); i++) {
SEL selector = bindingCommands[i].selector;
#ifdef PR_LOGGING
if (PR_LOG_TEST(gNativeKeyBindingsLog, PR_LOG_ALWAYS)) {
NSString* selectorString = NSStringFromSelector(selector);
nsAutoString nsSelectorString;
nsCocoaUtils::GetStringForNSString(selectorString, nsSelectorString);
PR_LOG(gNativeKeyBindingsLog, PR_LOG_ALWAYS,
("%p NativeKeyBindings::KeyPress, selector=%s",
this, ToNewCString(nsSelectorString)));
}
#endif
// Try to find a simple mapping in the hashtable
const char* commandStr = mSelectorToCommand.Get(
reinterpret_cast<struct objc_selector*>(selector));
if (commandStr) {
geckoCommands.AppendElement(commandStr);
} else if (selector == @selector(selectLine:)) {
// This is functional, but Cocoa's version is direction-less in that
// selection direction is not determined until some future directed action
// is taken. See bug 282097, comment 79 for more details.
geckoCommands.AppendElement("cmd_beginLine");
geckoCommands.AppendElement("cmd_selectEndLine");
} else if (selector == @selector(selectWord:)) {
// This is functional, but Cocoa's version is direction-less in that
// selection direction is not determined until some future directed action
// is taken. See bug 282097, comment 79 for more details.
geckoCommands.AppendElement("cmd_wordPrevious");
geckoCommands.AppendElement("cmd_selectWordNext");
}
}
if (geckoCommands.IsEmpty()) {
PR_LOG(gNativeKeyBindingsLog, PR_LOG_ALWAYS,
("%p NativeKeyBindings::KeyPress, handled=false", this));
return false;
}
for (uint32_t i = 0; i < geckoCommands.Length(); i++) {
const char* geckoCommand = geckoCommands[i];
PR_LOG(gNativeKeyBindingsLog, PR_LOG_ALWAYS,
("%p NativeKeyBindings::KeyPress, command=%s",
this, geckoCommand));
// Execute the Gecko command
aCallback(geckoCommand, aCallbackData);
}
PR_LOG(gNativeKeyBindingsLog, PR_LOG_ALWAYS,
("%p NativeKeyBindings::KeyPress, handled=true", this));
return true;
}
NS_IMETHODIMP_(bool)
NativeKeyBindings::KeyUp(const nsNativeKeyEvent& aEvent,
DoCommandCallback aCallback, void* aCallbackData)
{
return false;
}

View File

@ -777,6 +777,10 @@ TISInputSourceWrapper::InitKeyEvent(NSEvent *aNativeKeyEvent,
nsCocoaUtils::InitInputEvent(aKeyEvent, aNativeKeyEvent);
// This is used only while dispatching the event (which is a synchronous
// call), so there is no need to retain and release this data.
aKeyEvent.mNativeKeyEvent = aNativeKeyEvent;
aKeyEvent.refPoint = nsIntPoint(0, 0);
// If a keyboard layout override is set, we also need to force the keyboard

View File

@ -76,6 +76,58 @@ NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(GfxInfo, Init)
}
}
#include "NativeKeyBindings.h"
namespace mozilla {
namespace widget {
static nsresult
NativeKeyBindingsConstructor(nsISupports* aOuter, REFNSIID aIID,
void** aResult, NativeKeyBindingsType aType)
{
NativeKeyBindings* inst;
*aResult = NULL;
if (NULL != aOuter) {
return NS_ERROR_NO_AGGREGATION;
}
inst = new NativeKeyBindings();
NS_ADDREF(inst);
nsresult rv = inst->Init(aType);
if (NS_SUCCEEDED(rv)) {
rv = inst->QueryInterface(aIID, aResult);
}
NS_RELEASE(inst);
return rv;
}
static nsresult
NativeKeyBindingsInputConstructor(nsISupports* aOuter, REFNSIID aIID,
void** aResult)
{
return NativeKeyBindingsConstructor(aOuter, aIID, aResult,
eNativeKeyBindingsType_Input);
}
static nsresult
NativeKeyBindingsTextAreaConstructor(nsISupports* aOuter, REFNSIID aIID,
void** aResult)
{
return NativeKeyBindingsConstructor(aOuter, aIID, aResult,
eNativeKeyBindingsType_TextArea);
}
static nsresult
NativeKeyBindingsEditorConstructor(nsISupports* aOuter, REFNSIID aIID,
void** aResult)
{
return NativeKeyBindingsConstructor(aOuter, aIID, aResult,
eNativeKeyBindingsType_Editor);
}
} // namespace widget
} // namespace mozilla
NS_DEFINE_NAMED_CID(NS_WINDOW_CID);
NS_DEFINE_NAMED_CID(NS_POPUP_CID);
@ -101,6 +153,9 @@ NS_DEFINE_NAMED_CID(NS_MACDOCKSUPPORT_CID);
NS_DEFINE_NAMED_CID(NS_MACWEBAPPUTILS_CID);
NS_DEFINE_NAMED_CID(NS_STANDALONENATIVEMENU_CID);
NS_DEFINE_NAMED_CID(NS_GFXINFO_CID);
NS_DEFINE_NAMED_CID(NS_NATIVEKEYBINDINGS_INPUT_CID);
NS_DEFINE_NAMED_CID(NS_NATIVEKEYBINDINGS_TEXTAREA_CID);
NS_DEFINE_NAMED_CID(NS_NATIVEKEYBINDINGS_EDITOR_CID);
static const mozilla::Module::CIDEntry kWidgetCIDs[] = {
@ -128,6 +183,12 @@ static const mozilla::Module::CIDEntry kWidgetCIDs[] = {
{ &kNS_MACWEBAPPUTILS_CID, false, NULL, nsMacWebAppUtilsConstructor },
{ &kNS_STANDALONENATIVEMENU_CID, false, NULL, nsStandaloneNativeMenuConstructor },
{ &kNS_GFXINFO_CID, false, NULL, mozilla::widget::GfxInfoConstructor },
{ &kNS_NATIVEKEYBINDINGS_INPUT_CID, false, NULL,
mozilla::widget::NativeKeyBindingsInputConstructor },
{ &kNS_NATIVEKEYBINDINGS_TEXTAREA_CID, false, NULL,
mozilla::widget::NativeKeyBindingsTextAreaConstructor },
{ &kNS_NATIVEKEYBINDINGS_EDITOR_CID, false, NULL,
mozilla::widget::NativeKeyBindingsEditorConstructor },
{ NULL }
};
@ -156,6 +217,10 @@ static const mozilla::Module::ContractIDEntry kWidgetContracts[] = {
{ "@mozilla.org/widget/mac-web-app-utils;1", &kNS_MACWEBAPPUTILS_CID },
{ "@mozilla.org/widget/standalonenativemenu;1", &kNS_STANDALONENATIVEMENU_CID },
{ "@mozilla.org/gfx/info;1", &kNS_GFXINFO_CID },
{ NS_NATIVEKEYBINDINGSINPUT_CONTRACTID, &kNS_NATIVEKEYBINDINGS_INPUT_CID },
{ NS_NATIVEKEYBINDINGSTEXTAREA_CONTRACTID,
&kNS_NATIVEKEYBINDINGS_TEXTAREA_CID },
{ NS_NATIVEKEYBINDINGSEDITOR_CONTRACTID, &kNS_NATIVEKEYBINDINGS_EDITOR_CID },
{ NULL }
};

View File

@ -23,21 +23,12 @@ enum NativeKeyBindingsType {
#define NS_NATIVEKEYBINDINGSINPUT_CID \
{0x5c337258, 0xa580, 0x472e, {0x86, 0x15, 0xf2, 0x77, 0xdd, 0xc5, 0xbb, 0x06}}
#define NS_NATIVEKEYBINDINGSINPUT_CONTRACTID \
NS_NATIVEKEYBINDINGS_CONTRACTID_PREFIX "input"
#define NS_NATIVEKEYBINDINGSTEXTAREA_CID \
{0x2a898043, 0x180f, 0x4c8b, {0x8e, 0x54, 0x41, 0x0c, 0x7a, 0x54, 0x0f, 0x27}}
#define NS_NATIVEKEYBINDINGSTEXTAREA_CONTRACTID \
NS_NATIVEKEYBINDINGS_CONTRACTID_PREFIX "textarea"
#define NS_NATIVEKEYBINDINGSEDITOR_CID \
{0xf916ebfb, 0x78ef, 0x464b, {0x94, 0xd0, 0xa6, 0xf2, 0xca, 0x32, 0x00, 0xae}}
#define NS_NATIVEKEYBINDINGSEDITOR_CONTRACTID \
NS_NATIVEKEYBINDINGS_CONTRACTID_PREFIX "editor"
class nsNativeKeyBindings MOZ_FINAL : public nsINativeKeyBindings
{
public:

View File

@ -15,6 +15,15 @@
#define NS_NATIVEKEYBINDINGS_CONTRACTID_PREFIX \
"@mozilla.org/widget/native-key-bindings;1?type="
#define NS_NATIVEKEYBINDINGSINPUT_CONTRACTID \
NS_NATIVEKEYBINDINGS_CONTRACTID_PREFIX "input"
#define NS_NATIVEKEYBINDINGSTEXTAREA_CONTRACTID \
NS_NATIVEKEYBINDINGS_CONTRACTID_PREFIX "textarea"
#define NS_NATIVEKEYBINDINGSEDITOR_CONTRACTID \
NS_NATIVEKEYBINDINGS_CONTRACTID_PREFIX "editor"
struct nsNativeKeyEvent
{
nsKeyEvent* mGeckoEvent; // see bug 406407 to see how this is used

View File

@ -79,6 +79,7 @@ MOCHITEST_CHROME_FILES += native_menus_window.xul \
test_bug673301.xul \
test_taskbar_progress.xul \
test_secure_input.html \
test_native_key_bindings_mac.html \
$(NULL)
endif

View File

@ -0,0 +1,154 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset='utf-8'/>
<title>Native Key Bindings for Cocoa Test</title>
<!-- Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ -->
<script type="text/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript"
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
<script type="text/javascript"
src="chrome://mochikit/content/tests/SimpleTest/NativeKeyCodes.js"></script>
</head>
<body>
<div id="editable" contenteditable>
<p>Stretching attack nullam stuck in a tree zzz, suspendisse cras nec
suspendisse lick suscipit. Nunc egestas amet litter box, nullam climb the
curtains biting I don't like that food tristique biting sleep on your
keyboard non. Lay down in your way cras nec tempus chase the red dot cras
nec, pharetra pharetra eat the grass leap run orci turpis attack.
Consectetur sleep in the sink eat I don't like that food, knock over the
lamp catnip in viverra tail flick zzz meow etiam enim. Ac ac hiss shed
everywhere kittens rhoncus, attack your ankles zzz iaculis kittens. Nullam
pellentesque rip the couch iaculis rhoncus nibh, give me fish orci turpis
purr sleep on your face quis nunc bibendum.</p>
<p>Neque jump on the table bat iaculis, adipiscing sleep on your keyboard
jump vel justo shed everywhere suspendisse lick. Zzz enim faucibus
hairball faucibus, pharetra sunbathe biting bat leap rip the couch attack.
Tortor nibh in viverra quis hairball nam, vulputate adipiscing sleep on
your keyboard purr knock over the lamp orci turpis. Vestibulum I don't
like that food et chase the red dot, adipiscing neque bibendum rutrum
accumsan quis rhoncus claw. Leap accumsan vehicula enim biting sleep on
your face, pharetra nam accumsan egestas kittens sunbathe. Pharetra chase
the red dot sniff non eat the grass, vulputate fluffy fur aliquam puking
judging you.</p>
<p>Claw purr sollicitudin sollicitudin lay down in your way consectetur,
pellentesque vehicula zzz orci turpis consectetur. I don't like that food
rhoncus pellentesque sniff attack, rhoncus tortor attack your ankles
iaculis scratched hiss vel. Tortor zzz tortor nullam rip the couch rutrum,
bat enim ut leap hairball iaculis. Bibendum sunbathe elit suspendisse
nibh, puking adipiscing sleep on your face sleep on your face zzz catnip.
Judging you rutrum bat sunbathe sleep on your face, jump on the table leap
tincidunt a faucibus sleep in the sink. Stuck in a tree tristique zzz hiss
in viverra nullam, quis tortor pharetra attack.</p>
</div>
<script type="text/javascript;version=1.8">
SimpleTest.waitForExplicitFinish();
let editNode = document.getElementById("editable");
let utils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
getInterface(Components.interfaces.nsIDOMWindowUtils);
let layouts = {
"US": 0,
"Greek": 1,
"German": 2,
"Swedish": 3,
"Dvorak-Qwerty": 4,
"Thai": 5
};
function synthesizeNativeKey(aLayout, aKeyCode, aModifiers, aSystemChars,
aSystemUnmodifiedChars)
{
let modifiers = 0;
if (aModifiers.capsLock) modifiers |= 0x01;
if (aModifiers.numLock) modifiers |= 0x02;
if (aModifiers.shift) modifiers |= 0x0100;
if (aModifiers.shiftRight) modifiers |= 0x0200;
if (aModifiers.ctrl) modifiers |= 0x0400;
if (aModifiers.ctrlRight) modifiers |= 0x0800;
if (aModifiers.alt) modifiers |= 0x1000;
if (aModifiers.altRight) modifiers |= 0x2000;
if (aModifiers.command) modifiers |= 0x4000;
if (aModifiers.commandRight) modifiers |= 0x8000;
if (aModifiers.help) modifiers |= 0x10000;
if (aModifiers.function) modifiers |= 0x100000;
if (aModifiers.numericKeyPad) modifiers |= 0x01000000;
utils.sendNativeKeyEvent(aLayout, aKeyCode, modifiers,
aSystemChars, aSystemUnmodifiedChars);
}
function testSelection(aAnchorOffset, aFocusOffset)
{
let selection = window.getSelection();
is(selection.anchorOffset, aAnchorOffset, "Incorrect anchor offset");
is(selection.focusOffset, aFocusOffset, "Incorrect focus offset");
}
function doTest()
{
editNode.focus();
// Move to beginning of line
synthesizeNativeKey(layouts.US, MAC_VK_LeftArrow,
{ctrl: true}, "\uf702", "\uf702");
testSelection(0, 0);
// Move to end of line
synthesizeNativeKey(layouts.US, MAC_VK_RightArrow,
{ctrl: true}, "\uf703", "\uf703");
testSelection(73, 73);
// Move down
synthesizeNativeKey(layouts.US, MAC_VK_ANSI_N, {ctrl: true}, "n", "n");
testSelection(140, 140);
// Move to beginning of line
synthesizeNativeKey(layouts.US, MAC_VK_LeftArrow,
{ctrl: true}, "\uf702", "\uf702");
testSelection(73, 73);
// Move word right and modify selection
synthesizeNativeKey(layouts.US, MAC_VK_RightArrow,
{alt: true, shift: true}, "\uf703", "\uf703");
testSelection(73, 84);
// Move word right
synthesizeNativeKey(layouts.US, MAC_VK_RightArrow,
{alt: true}, "\uf703", "\uf703");
testSelection(84, 84);
// Move word right
synthesizeNativeKey(layouts.US, MAC_VK_RightArrow,
{alt: true}, "\uf703", "\uf703");
testSelection(89, 89);
// Move down and modify selection
synthesizeNativeKey(layouts.US, MAC_VK_DownArrow,
{shift: true}, "\uf701", "\uf701");
testSelection(89, 171);
// Move backward and modify selection
synthesizeNativeKey(layouts.US, MAC_VK_ANSI_B,
{ctrl: true, shift: true}, "B", "B");
testSelection(89, 170);
SimpleTest.finish();
}
SimpleTest.waitForFocus(doTest);
</script>
</body>
</html>