Bug 564669 Remove nsIPlaintextEditor::handleKeyPress() r=smaug+ehsan, sr=roc

This commit is contained in:
Masayuki Nakano 2010-06-10 10:16:58 +09:00
parent a6b5d07d03
commit 2ed7d1345b
16 changed files with 1406 additions and 282 deletions

View File

@ -67,7 +67,7 @@ interface nsIEventListenerInfo : nsISupports
nsISupports getDebugObject();
};
[scriptable, uuid(551cac0f-31ed-45e0-8d67-bc0d6e117b31)]
[scriptable, uuid(7be78bb6-33f7-4f31-b3f3-97eefaff2762)]
interface nsIEventListenerService : nsISupports
{
/**
@ -91,5 +91,10 @@ interface nsIEventListenerService : nsISupports
[optional] out unsigned long aCount,
[retval, array, size_is(aCount)] out
nsIDOMEventTarget aOutArray);
/**
* Returns system event group.
*/
readonly attribute nsIDOMEventGroup systemEventGroup;
};

View File

@ -296,7 +296,6 @@ nsEventListenerManager::~nsEventListenerManager()
--mInstanceCount;
if(mInstanceCount == 0) {
NS_IF_RELEASE(gSystemEventGroup);
NS_IF_RELEASE(gDOM2EventGroup);
}
}
@ -311,10 +310,20 @@ nsEventListenerManager::RemoveAllListeners()
void
nsEventListenerManager::Shutdown()
{
NS_IF_RELEASE(gSystemEventGroup);
sAddListenerID = JSVAL_VOID;
nsDOMEvent::Shutdown();
}
nsIDOMEventGroup*
nsEventListenerManager::GetSystemEventGroup()
{
if (!gSystemEventGroup) {
CallCreateInstance(kDOMEventGroupCID, &gSystemEventGroup);
}
return gSystemEventGroup;
}
NS_IMPL_CYCLE_COLLECTION_CLASS(nsEventListenerManager)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsEventListenerManager)
@ -1220,17 +1229,8 @@ nsEventListenerManager::SetListenerTarget(nsISupports* aTarget)
NS_IMETHODIMP
nsEventListenerManager::GetSystemEventGroupLM(nsIDOMEventGroup **aGroup)
{
if (!gSystemEventGroup) {
nsresult result;
nsCOMPtr<nsIDOMEventGroup> group(do_CreateInstance(kDOMEventGroupCID,&result));
if (NS_FAILED(result))
return result;
gSystemEventGroup = group;
NS_ADDREF(gSystemEventGroup);
}
*aGroup = gSystemEventGroup;
*aGroup = GetSystemEventGroup();
NS_ENSURE_TRUE(*aGroup, NS_ERROR_OUT_OF_MEMORY);
NS_ADDREF(*aGroup);
return NS_OK;
}

View File

@ -47,6 +47,7 @@
#include "nsIScriptContext.h"
#include "nsCycleCollectionParticipant.h"
#include "nsTObserverArray.h"
#include "nsGUIEvent.h"
class nsIDOMEvent;
class nsIAtom;
@ -54,6 +55,7 @@ class nsIWidget;
struct nsPoint;
struct EventTypeData;
class nsEventTargetChainItem;
class nsPIDOMWindow;
typedef struct {
nsRefPtr<nsIDOMEventListener> mListener;
@ -180,6 +182,8 @@ public:
static void Shutdown();
static nsIDOMEventGroup* GetSystemEventGroup();
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsEventListenerManager,
nsIEventListenerManager)

View File

@ -36,7 +36,7 @@
* ***** END LICENSE BLOCK ***** */
#include "nsEventListenerService.h"
#include "nsCOMArray.h"
#include "nsIEventListenerManager.h"
#include "nsEventListenerManager.h"
#include "nsPIDOMEventTarget.h"
#include "nsIVariant.h"
#include "nsIServiceManager.h"
@ -51,6 +51,7 @@
#include "nsGUIEvent.h"
#include "nsEventDispatcher.h"
#include "nsIJSEventListener.h"
#include "nsIDOMEventGroup.h"
#ifdef MOZ_JSDEBUGGER
#include "jsdIDebuggerService.h"
#endif
@ -247,6 +248,16 @@ nsEventListenerService::GetEventTargetChainFor(nsIDOMEventTarget* aEventTarget,
return NS_OK;
}
NS_IMETHODIMP
nsEventListenerService::GetSystemEventGroup(nsIDOMEventGroup** aSystemGroup)
{
NS_ENSURE_ARG_POINTER(aSystemGroup);
*aSystemGroup = nsEventListenerManager::GetSystemEventGroup();
NS_ENSURE_TRUE(*aSystemGroup, NS_ERROR_OUT_OF_MEMORY);
NS_ADDREF(*aSystemGroup);
return NS_OK;
}
nsresult
NS_NewEventListenerService(nsIEventListenerService** aResult)
{

View File

@ -37,9 +37,7 @@
#include "nsISupports.idl"
interface nsIDOMKeyEvent;
[scriptable, uuid(1480e196-0d5c-40cf-8563-ed8a33eabcf2)]
[scriptable, uuid(05d312ef-8914-494e-91c9-2be8ed7f8e29)]
interface nsIPlaintextEditor : nsISupports
{
@ -120,12 +118,6 @@ interface nsIPlaintextEditor : nsISupports
*/
attribute long newlineHandling;
/**
* EditorKeyPress consumes a keyevent.
* @param aKeyEvent key event to consume
*/
void handleKeyPress(in nsIDOMKeyEvent aKeyEvent);
/**
* Inserts a string at the current location,
* given by the selection.

View File

@ -74,7 +74,8 @@
#include "nsCaret.h"
#include "nsIWidget.h"
#include "nsIPlaintextEditor.h"
#include "nsGUIEvent.h" // nsTextEventReply
#include "nsIPrivateDOMEvent.h"
#include "nsGUIEvent.h"
#include "nsIFrame.h" // Needed by IME code
@ -5046,6 +5047,61 @@ nsEditor::RemoveAttributeOrEquivalent(nsIDOMElement * aElement,
return RemoveAttribute(aElement, aAttribute);
}
nsresult
nsEditor::HandleKeyPressEvent(nsIDOMKeyEvent* aKeyEvent)
{
// NOTE: When you change this method, you should also change:
// * editor/libeditor/text/tests/test_texteditor_keyevent_handling.html
// * editor/libeditor/html/tests/test_htmleditor_keyevent_handling.html
//
// And also when you add new key handling, you need to change the subclass's
// HandleKeyPressEvent()'s switch statement.
nsKeyEvent* nativeKeyEvent = GetNativeKeyEvent(aKeyEvent);
NS_ENSURE_TRUE(nativeKeyEvent, NS_ERROR_UNEXPECTED);
NS_ASSERTION(nativeKeyEvent->message == NS_KEY_PRESS,
"HandleKeyPressEvent gets non-keypress event");
// if we are readonly or disabled, then do nothing.
if (IsReadonly() || IsDisabled()) {
// consume backspace for disabled and readonly textfields, to prevent
// back in history, which could be confusing to users
if (nativeKeyEvent->keyCode == nsIDOMKeyEvent::DOM_VK_BACK_SPACE) {
aKeyEvent->PreventDefault();
}
return NS_OK;
}
switch (nativeKeyEvent->keyCode) {
case nsIDOMKeyEvent::DOM_VK_META:
case nsIDOMKeyEvent::DOM_VK_SHIFT:
case nsIDOMKeyEvent::DOM_VK_CONTROL:
case nsIDOMKeyEvent::DOM_VK_ALT:
aKeyEvent->PreventDefault(); // consumed
return NS_OK;
case nsIDOMKeyEvent::DOM_VK_BACK_SPACE:
if (nativeKeyEvent->isControl || nativeKeyEvent->isAlt ||
nativeKeyEvent->isMeta) {
return NS_OK;
}
DeleteSelection(nsIEditor::ePrevious);
aKeyEvent->PreventDefault(); // consumed
return NS_OK;
case nsIDOMKeyEvent::DOM_VK_DELETE:
// on certain platforms (such as windows) the shift key
// modifies what delete does (cmd_cut in this case).
// bailing here to allow the keybindings to do the cut.
if (nativeKeyEvent->isShift || nativeKeyEvent->isControl ||
nativeKeyEvent->isAlt || nativeKeyEvent->isMeta) {
return NS_OK;
}
DeleteSelection(nsIEditor::eNext);
aKeyEvent->PreventDefault(); // consumed
return NS_OK;
}
return NS_OK;
}
nsresult
nsEditor::HandleInlineSpellCheck(PRInt32 action,
nsISelection *aSelection,
@ -5201,6 +5257,17 @@ nsEditor::IsModifiableNode(nsIDOMNode *aNode)
return PR_TRUE;
}
nsKeyEvent*
nsEditor::GetNativeKeyEvent(nsIDOMKeyEvent* aDOMKeyEvent)
{
nsCOMPtr<nsIPrivateDOMEvent> privDOMEvent = do_QueryInterface(aDOMKeyEvent);
NS_ENSURE_TRUE(privDOMEvent, nsnull);
nsEvent* nativeEvent = privDOMEvent->GetInternalNSEvent();
NS_ENSURE_TRUE(nativeEvent, nsnull);
NS_ENSURE_TRUE(nativeEvent->eventStructType == NS_KEY_EVENT, nsnull);
return static_cast<nsKeyEvent*>(nativeEvent);
}
PRBool
nsEditor::HasFocus()
{

View File

@ -86,6 +86,7 @@ class nsIFile;
class nsISelectionController;
class nsIDOMEventTarget;
class nsCSSStyleSheet;
class nsKeyEvent;
#define kMOZEditorBogusNodeAttrAtom nsEditProperty::mozEditorBogusNode
#define kMOZEditorBogusNodeValue NS_LITERAL_STRING("TRUE")
@ -356,6 +357,8 @@ protected:
*/
PRBool GetDesiredSpellCheckState();
nsKeyEvent* GetNativeKeyEvent(nsIDOMKeyEvent* aDOMKeyEvent);
public:
/** All editor operations which alter the doc should be prefaced
@ -567,6 +570,8 @@ public:
PRBool GetShouldTxnSetSelection();
virtual nsresult HandleKeyPressEvent(nsIDOMKeyEvent* aKeyEvent);
nsresult HandleInlineSpellCheck(PRInt32 action,
nsISelection *aSelection,
nsIDOMNode *previousSelectedNode,
@ -647,6 +652,12 @@ public:
return (mFlags & nsIPlaintextEditor::eEditorDontEchoPassword) != 0;
}
PRBool IsTabbable() const
{
return IsSingleLineEditor() || IsPasswordEditor() || IsFormWidget() ||
IsInteractionAllowed();
}
// Whether the editor has focus or not.
virtual PRBool HasFocus();

View File

@ -39,7 +39,6 @@
* ***** END LICENSE BLOCK ***** */
#include "nsEditorEventListener.h"
#include "nsEditor.h"
#include "nsIPlaintextEditor.h"
#include "nsIDOMDOMStringList.h"
#include "nsIDOMEvent.h"
@ -317,142 +316,28 @@ nsEditorEventListener::KeyPress(nsIDOMEvent* aKeyEvent)
{
NS_ENSURE_TRUE(mEditor, NS_ERROR_NOT_AVAILABLE);
nsCOMPtr<nsIDOMKeyEvent>keyEvent = do_QueryInterface(aKeyEvent);
if (!keyEvent)
{
//non-key event passed to keypress. bad things.
return NS_OK;
}
// Don't handle events which do not belong to us (by making sure that the
// target of the event is actually editable).
nsCOMPtr<nsIDOMEventTarget> target;
nsresult rv = keyEvent->GetTarget(getter_AddRefs(target));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIDOMNode> targetNode = do_QueryInterface(target);
if (!mEditor->IsModifiableNode(targetNode))
{
return NS_OK;
}
// DOM event handling happens in two passes, the client pass and the system
// pass. We do all of our processing in the system pass, to allow client
// handlers the opportunity to cancel events and prevent typing in the editor.
// If the client pass cancelled the event, defaultPrevented will be true
// below.
nsCOMPtr<nsIDOMNSUIEvent> nsUIEvent = do_QueryInterface(aKeyEvent);
if(nsUIEvent)
{
nsCOMPtr<nsIDOMNSUIEvent> UIEvent = do_QueryInterface(aKeyEvent);
if(UIEvent) {
PRBool defaultPrevented;
nsUIEvent->GetPreventDefault(&defaultPrevented);
if(defaultPrevented)
UIEvent->GetPreventDefault(&defaultPrevented);
if(defaultPrevented) {
return NS_OK;
}
}
PRUint32 keyCode;
keyEvent->GetKeyCode(&keyCode);
// if we are readonly or disabled, then do nothing.
if (mEditor->IsReadonly() || mEditor->IsDisabled())
{
// consume backspace for disabled and readonly textfields, to prevent
// back in history, which could be confusing to users
if (keyCode == nsIDOMKeyEvent::DOM_VK_BACK_SPACE)
aKeyEvent->PreventDefault();
nsCOMPtr<nsIDOMKeyEvent>keyEvent = do_QueryInterface(aKeyEvent);
if (!keyEvent) {
//non-key event passed to keypress. bad things.
return NS_OK;
}
nsCOMPtr<nsIPlaintextEditor> textEditor =
do_QueryInterface(static_cast<nsIEditor*>(mEditor));
NS_ASSERTION(textEditor, "nsEditor must have nsIPlaintextEditor");
// if there is no charCode, then it's a key that doesn't map to a character,
// so look for special keys using keyCode.
if (0 != keyCode)
{
PRBool isAnyModifierKeyButShift;
nsresult rv;
rv = keyEvent->GetAltKey(&isAnyModifierKeyButShift);
if (NS_FAILED(rv)) return rv;
if (!isAnyModifierKeyButShift)
{
rv = keyEvent->GetMetaKey(&isAnyModifierKeyButShift);
if (NS_FAILED(rv)) return rv;
if (!isAnyModifierKeyButShift)
{
rv = keyEvent->GetCtrlKey(&isAnyModifierKeyButShift);
if (NS_FAILED(rv)) return rv;
}
}
switch (keyCode)
{
case nsIDOMKeyEvent::DOM_VK_META:
case nsIDOMKeyEvent::DOM_VK_SHIFT:
case nsIDOMKeyEvent::DOM_VK_CONTROL:
case nsIDOMKeyEvent::DOM_VK_ALT:
aKeyEvent->PreventDefault(); // consumed
return NS_OK;
break;
case nsIDOMKeyEvent::DOM_VK_BACK_SPACE:
if (isAnyModifierKeyButShift)
return NS_OK;
mEditor->DeleteSelection(nsIEditor::ePrevious);
aKeyEvent->PreventDefault(); // consumed
return NS_OK;
break;
case nsIDOMKeyEvent::DOM_VK_DELETE:
/* on certain platforms (such as windows) the shift key
modifies what delete does (cmd_cut in this case).
bailing here to allow the keybindings to do the cut.*/
PRBool isShiftModifierKey;
rv = keyEvent->GetShiftKey(&isShiftModifierKey);
if (NS_FAILED(rv)) return rv;
if (isAnyModifierKeyButShift || isShiftModifierKey)
return NS_OK;
mEditor->DeleteSelection(nsIEditor::eNext);
aKeyEvent->PreventDefault(); // consumed
return NS_OK;
break;
case nsIDOMKeyEvent::DOM_VK_TAB:
if (mEditor->IsSingleLineEditor() || mEditor->IsPasswordEditor() ||
mEditor->IsFormWidget() || mEditor->IsInteractionAllowed()) {
return NS_OK; // let it be used for focus switching
}
if (isAnyModifierKeyButShift)
return NS_OK;
// else we insert the tab straight through
textEditor->HandleKeyPress(keyEvent);
// let HandleKeyPress consume the event
return NS_OK;
case nsIDOMKeyEvent::DOM_VK_RETURN:
case nsIDOMKeyEvent::DOM_VK_ENTER:
if (isAnyModifierKeyButShift)
return NS_OK;
if (!mEditor->IsSingleLineEditor())
{
textEditor->HandleKeyPress(keyEvent);
aKeyEvent->PreventDefault(); // consumed
}
return NS_OK;
}
}
textEditor->HandleKeyPress(keyEvent);
return NS_OK; // we don't PreventDefault() here or keybindings like control-x won't work
return mEditor->HandleKeyPressEvent(keyEvent);
}
/**

View File

@ -516,6 +516,136 @@ nsHTMLEditor::BeginningOfDocument()
return selection->Collapse(selNode, selOffset);
}
nsresult
nsHTMLEditor::HandleKeyPressEvent(nsIDOMKeyEvent* aKeyEvent)
{
// NOTE: When you change this method, you should also change:
// * editor/libeditor/html/tests/test_htmleditor_keyevent_handling.html
if (IsReadonly() || IsDisabled()) {
// When we're not editable, the events are handled on nsEditor, so, we can
// bypass nsPlaintextEditor.
return nsEditor::HandleKeyPressEvent(aKeyEvent);
}
// Don't handle events which do not belong to us (by making sure that the
// target of the event is actually editable).
// XXX we can remove this check after bug 389372
nsCOMPtr<nsIDOMEventTarget> target;
nsresult rv = aKeyEvent->GetTarget(getter_AddRefs(target));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIDOMNode> targetNode = do_QueryInterface(target);
if (!IsModifiableNode(targetNode)) {
return NS_OK;
}
nsKeyEvent* nativeKeyEvent = GetNativeKeyEvent(aKeyEvent);
NS_ENSURE_TRUE(nativeKeyEvent, NS_ERROR_UNEXPECTED);
NS_ASSERTION(nativeKeyEvent->message == NS_KEY_PRESS,
"HandleKeyPressEvent gets non-keypress event");
switch (nativeKeyEvent->keyCode) {
case nsIDOMKeyEvent::DOM_VK_META:
case nsIDOMKeyEvent::DOM_VK_SHIFT:
case nsIDOMKeyEvent::DOM_VK_CONTROL:
case nsIDOMKeyEvent::DOM_VK_ALT:
case nsIDOMKeyEvent::DOM_VK_BACK_SPACE:
case nsIDOMKeyEvent::DOM_VK_DELETE:
// These keys are handled on nsEditor, so, we can bypass
// nsPlaintextEditor.
return nsEditor::HandleKeyPressEvent(aKeyEvent);
case nsIDOMKeyEvent::DOM_VK_ESCAPE:
// XXX nsPlaintextEditor doesn't consume the event by bug 569988,
// but nsHTMLEditor should eat the processed keypress event.
aKeyEvent->PreventDefault();
// This key is handled on nsPlaintextEditor.
return nsPlaintextEditor::HandleKeyPressEvent(aKeyEvent);
case nsIDOMKeyEvent::DOM_VK_TAB: {
if (IsPlaintextEditor()) {
// If this works as plain text editor, e.g., mail editor for plain
// text, should be handled on nsPlaintextEditor.
return nsPlaintextEditor::HandleKeyPressEvent(aKeyEvent);
}
if (IsTabbable()) {
return NS_OK; // let it be used for focus switching
}
if (nativeKeyEvent->isControl || nativeKeyEvent->isAlt ||
nativeKeyEvent->isMeta) {
return NS_OK;
}
nsCOMPtr<nsISelection> selection;
nsresult rv = GetSelection(getter_AddRefs(selection));
NS_ENSURE_SUCCESS(rv, rv);
PRInt32 offset;
nsCOMPtr<nsIDOMNode> node, blockParent;
rv = GetStartNodeAndOffset(selection, address_of(node), &offset);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
PRBool isBlock = PR_FALSE;
NodeIsBlock(node, &isBlock);
if (isBlock) {
blockParent = node;
} else {
blockParent = GetBlockNodeParent(node);
}
if (!blockParent) {
break;
}
PRBool handled = PR_FALSE;
if (nsHTMLEditUtils::IsTableElement(blockParent)) {
rv = TabInTable(nativeKeyEvent->isShift, &handled);
if (handled) {
ScrollSelectionIntoView(PR_FALSE);
}
} else if (nsHTMLEditUtils::IsListItem(blockParent)) {
rv = Indent(nativeKeyEvent->isShift ?
NS_LITERAL_STRING("outdent") :
NS_LITERAL_STRING("indent"));
handled = PR_TRUE;
}
NS_ENSURE_SUCCESS(rv, rv);
if (handled) {
return aKeyEvent->PreventDefault(); // consumed
}
if (nativeKeyEvent->isShift) {
return NS_OK; // don't type text for shift tabs
}
aKeyEvent->PreventDefault();
return TypedText(NS_LITERAL_STRING("\t"), eTypedText);
}
case nsIDOMKeyEvent::DOM_VK_RETURN:
case nsIDOMKeyEvent::DOM_VK_ENTER:
if (nativeKeyEvent->isControl || nativeKeyEvent->isAlt ||
nativeKeyEvent->isMeta) {
return NS_OK;
}
aKeyEvent->PreventDefault(); // consumed
if (nativeKeyEvent->isShift && !IsPlaintextEditor()) {
// only inserts a br node
return TypedText(EmptyString(), eTypedBR);
}
// uses rules to figure out what to insert
return TypedText(EmptyString(), eTypedBreak);
}
// NOTE: On some keyboard layout, some characters are inputted with Control
// key or Alt key, but at that time, widget sets FALSE to these keys.
if (nativeKeyEvent->charCode == 0 || nativeKeyEvent->isControl ||
nativeKeyEvent->isAlt || nativeKeyEvent->isMeta) {
// we don't PreventDefault() here or keybindings like control-x won't work
return NS_OK;
}
aKeyEvent->PreventDefault();
nsAutoString str(nativeKeyEvent->charCode);
return TypedText(str, eTypedText);
}
/**
* Returns true if the id represents an element of block type.
* Can be used to determine if a new paragraph should be started.
@ -1194,103 +1324,6 @@ nsHTMLEditor::UpdateBaseURL()
return NS_OK;
}
NS_IMETHODIMP nsHTMLEditor::HandleKeyPress(nsIDOMKeyEvent* aKeyEvent)
{
PRUint32 keyCode, character;
PRBool isShift, ctrlKey, altKey, metaKey;
nsresult res;
if (!aKeyEvent) return NS_ERROR_NULL_POINTER;
if (NS_SUCCEEDED(aKeyEvent->GetKeyCode(&keyCode)) &&
NS_SUCCEEDED(aKeyEvent->GetShiftKey(&isShift)) &&
NS_SUCCEEDED(aKeyEvent->GetCtrlKey(&ctrlKey)) &&
NS_SUCCEEDED(aKeyEvent->GetAltKey(&altKey)) &&
NS_SUCCEEDED(aKeyEvent->GetMetaKey(&metaKey)))
{
// this royally blows: because tabs come in from keyDowns instead
// of keyPress, and because GetCharCode refuses to work for keyDown
// i have to play games.
if (keyCode == nsIDOMKeyEvent::DOM_VK_TAB) character = '\t';
else aKeyEvent->GetCharCode(&character);
if (keyCode == nsIDOMKeyEvent::DOM_VK_TAB)
{
if (!IsPlaintextEditor()) {
nsCOMPtr<nsISelection>selection;
res = GetSelection(getter_AddRefs(selection));
if (NS_FAILED(res)) return res;
PRInt32 offset;
nsCOMPtr<nsIDOMNode> node, blockParent;
res = GetStartNodeAndOffset(selection, address_of(node), &offset);
if (NS_FAILED(res)) return res;
if (!node) return NS_ERROR_FAILURE;
PRBool isBlock = PR_FALSE;
NodeIsBlock(node, &isBlock);
if (isBlock) blockParent = node;
else blockParent = GetBlockNodeParent(node);
if (blockParent)
{
PRBool bHandled = PR_FALSE;
if (nsHTMLEditUtils::IsTableElement(blockParent))
{
res = TabInTable(isShift, &bHandled);
if (bHandled)
ScrollSelectionIntoView(PR_FALSE);
}
else if (nsHTMLEditUtils::IsListItem(blockParent))
{
nsAutoString indentstr;
if (isShift) indentstr.AssignLiteral("outdent");
else indentstr.AssignLiteral("indent");
res = Indent(indentstr);
bHandled = PR_TRUE;
}
if (NS_FAILED(res)) return res;
if (bHandled)
return aKeyEvent->PreventDefault(); // consumed
}
}
if (isShift)
return NS_OK; // don't type text for shift tabs
}
else if (keyCode == nsIDOMKeyEvent::DOM_VK_RETURN
|| keyCode == nsIDOMKeyEvent::DOM_VK_ENTER)
{
aKeyEvent->PreventDefault();
nsString empty;
if (isShift && !IsPlaintextEditor())
{
return TypedText(empty, eTypedBR); // only inserts a br node
}
else
{
return TypedText(empty, eTypedBreak); // uses rules to figure out what to insert
}
}
else if (keyCode == nsIDOMKeyEvent::DOM_VK_ESCAPE)
{
aKeyEvent->PreventDefault();
// pass escape keypresses through as empty strings: needed forime support
nsString empty;
return TypedText(empty, eTypedText);
}
// if we got here we either fell out of the tab case or have a normal character.
// Either way, treat as normal character.
if (character && !altKey && !ctrlKey && !metaKey)
{
aKeyEvent->PreventDefault();
nsAutoString key(character);
return TypedText(key, eTypedText);
}
}
return NS_ERROR_FAILURE;
}
/* This routine is needed to provide a bottleneck for typing for logging
purposes. Can't use HandleKeyPress() (above) for that since it takes
a nsIDOMKeyEvent* parameter. So instead we pass enough info through

View File

@ -144,9 +144,9 @@ public:
virtual ~nsHTMLEditor();
/* ------------ nsPlaintextEditor overrides -------------- */
NS_IMETHODIMP HandleKeyPress(nsIDOMKeyEvent* aKeyEvent);
NS_IMETHOD GetIsDocumentEditable(PRBool *aIsDocumentEditable);
NS_IMETHODIMP BeginningOfDocument();
virtual nsresult HandleKeyPressEvent(nsIDOMKeyEvent* aKeyEvent);
virtual PRBool HasFocus();
/* ------------ nsIEditorIMESupport overrides ------------ */

View File

@ -58,6 +58,7 @@ _TEST_FILES = \
test_bug525389.html \
test_bug537046.html \
test_contenteditable_focus.html \
test_htmleditor_keyevent_handling.html \
test_select_all_without_body.html \
file_select_all_without_body.html \
$(NULL)

View File

@ -0,0 +1,666 @@
<html>
<head>
<title>Test for key event handler of HTML editor</title>
<script type="text/javascript"
src="chrome://mochikit/content/MochiKit/packed.js"></script>
<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>
<link rel="stylesheet" type="text/css"
href="chrome://mochikit/content/tests/SimpleTest/test.css" />
</head>
<body>
<div id="display">
<div id="htmlEditor" contenteditable="true"><br></div>
</div>
<div id="content" style="display: none">
</div>
<pre id="test">
</pre>
<script class="testbody" type="application/javascript">
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(runTests, window);
var htmlEditor = document.getElementById("htmlEditor");
const kIsMac = navigator.platform.indexOf("Mac") == 0;
const kIsWin = navigator.platform.indexOf("Win") == 0;
const kIsLinux = navigator.platform.indexOf("Linux") == 0;
function runTests()
{
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
var fm = Components.classes["@mozilla.org/focus-manager;1"].
getService(Components.interfaces.nsIFocusManager);
var capturingPhase = { fired: false, prevented: false };
var bubblingPhase = { fired: false, prevented: false };
var listener = {
handleEvent: function _hv(aEvent)
{
is(aEvent.type, "keypress", "unexpected event is handled");
switch (aEvent.eventPhase) {
case aEvent.CAPTURING_PHASE:
capturingPhase.fired = true;
capturingPhase.prevented = aEvent.getPreventDefault();
break;
case aEvent.BUBBLING_PHASE:
bubblingPhase.fired = true;
bubblingPhase.prevented = aEvent.getPreventDefault();
aEvent.preventDefault(); // prevent the browser default behavior
break;
default:
ok(false, "event is handled in unexpected phase");
}
}
};
function check(aDescription,
aFiredOnCapture, aPreventedOnCapture,
aFiredOnBubbling, aPreventedOnBubbling)
{
function getDesciption(aExpected)
{
return aDescription + (aExpected ? " wasn't " : " was ");
}
is(capturingPhase.fired, aFiredOnCapture,
getDesciption(aFiredOnCapture) + "fired on capture phase");
is(capturingPhase.prevented, aPreventedOnCapture,
getDesciption(aPreventedOnCapture) + "prevented on capture phase");
is(bubblingPhase.fired, aFiredOnBubbling,
getDesciption(aFiredOnBubbling) + "fired on bubbling phase");
is(bubblingPhase.prevented, aPreventedOnBubbling,
getDesciption(aPreventedOnBubbling) + "prevented on bubbling phase");
}
var systemGroup =
Components.classes["@mozilla.org/eventlistenerservice;1"].
getService(Components.interfaces.nsIEventListenerService).
systemEventGroup;
window.QueryInterface(Components.interfaces.nsIDOM3EventTarget);
window.addGroupedEventListener("keypress", listener, true, systemGroup);
window.addGroupedEventListener("keypress", listener, false, systemGroup);
function doTest(aElement, aDescription,
aIsReadonly, aIsTabbable, aIsPlaintext)
{
function reset(aText)
{
capturingPhase.fired = false;
capturingPhase.prevented = false;
bubblingPhase.fired = false;
bubblingPhase.prevented = false;
aElement.innerHTML = aText;
var sel = window.getSelection();
var range = document.createRange();
range.setStart(aElement, aElement.childNodes.length);
sel.removeAllRanges();
sel.addRange(range);
}
function resetForIndent(aText)
{
capturingPhase.fired = false;
capturingPhase.prevented = false;
bubblingPhase.fired = false;
bubblingPhase.prevented = false;
aElement.innerHTML = aText;
var sel = window.getSelection();
var range = document.createRange();
var target = document.getElementById("target").firstChild;
range.setStart(target, target.length);
sel.removeAllRanges();
sel.addRange(range);
}
if (document.activeElement) {
document.activeElement.blur();
}
aDescription += ": "
aElement.focus();
is(fm.focusedElement, aElement, aDescription + "failed to move focus");
// Modifier keys:
// Only when editor is editable, it consumes.
reset("");
synthesizeKey("VK_META", { });
check(aDescription + "Meta", true, false, true, !aIsReadonly);
reset("");
synthesizeKey("VK_SHIFT", { });
check(aDescription + "Shift", true, false, true, !aIsReadonly);
reset("");
synthesizeKey("VK_CONTROL", { });
check(aDescription + "Control", true, false, true, !aIsReadonly);
// Alt key press event installs menubar key event listener, so,
// we should pass Alt key testing on Windows and Linux.
if (!kIsWin && !kIsLinux) {
reset("");
synthesizeKey("VK_ALT", { });
check(aDescription + "Alt", true, false, true, !aIsReadonly);
}
// Backspace key:
// If editor is readonly, it doesn't consume.
// If editor is editable, it consumes backspace and shift+backspace.
// Otherwise, editor doesn't consume the event.
reset("");
synthesizeKey("VK_BACK_SPACE", { });
check(aDescription + "Backspace", true, false, true, true);
reset("");
synthesizeKey("VK_BACK_SPACE", { shiftKey: true });
check(aDescription + "Shift+Backspace", true, false, true, true);
reset("");
synthesizeKey("VK_BACK_SPACE", { ctrlKey: true });
check(aDescription + "Ctrl+Backspace", true, false, true, aIsReadonly);
reset("");
synthesizeKey("VK_BACK_SPACE", { altKey: true });
check(aDescription + "Alt+Backspace", true, false, true, aIsReadonly);
reset("");
synthesizeKey("VK_BACK_SPACE", { metaKey: true });
check(aDescription + "Meta+Backspace", true, false, true, aIsReadonly);
// Delete key:
// If editor is readonly, it doesn't consume.
// If editor is editable, delete is consumed.
// Otherwise, editor doesn't consume the event.
reset("");
synthesizeKey("VK_DELETE", { });
check(aDescription + "Delete", true, false, true, !aIsReadonly);
reset("");
synthesizeKey("VK_DELETE", { shiftKey: true });
check(aDescription + "Shift+Delete", true, false, true, false);
reset("");
synthesizeKey("VK_DELETE", { ctrlKey: true });
check(aDescription + "Ctrl+Delete", true, false, true, false);
reset("");
synthesizeKey("VK_DELETE", { altKey: true });
check(aDescription + "Alt+Delete", true, false, true, false);
reset("");
synthesizeKey("VK_DELETE", { metaKey: true });
check(aDescription + "Meta+Delete", true, false, true, false);
// Return key:
// If editor is readonly, it doesn't consume.
// If editor is editable and not single line editor, it consumes Return
// and Shift+Return.
// Otherwise, editor doesn't consume the event.
reset("a");
synthesizeKey("VK_RETURN", { });
check(aDescription + "Return",
true, false, true, !aIsReadonly);
is(aElement.innerHTML, aIsReadonly ? "a" : "a<br><br>",
aDescription + "Return");
reset("a");
synthesizeKey("VK_RETURN", { shiftKey: true });
check(aDescription + "Shift+Return",
true, false, true, !aIsReadonly);
is(aElement.innerHTML, aIsReadonly ? "a" : "a<br><br>",
aDescription + "Shift+Return");
reset("a");
synthesizeKey("VK_RETURN", { ctrlKey: true });
check(aDescription + "Ctrl+Return", true, false, true, false);
is(aElement.innerHTML, "a", aDescription + "Ctrl+Return");
reset("a");
synthesizeKey("VK_RETURN", { altKey: true });
check(aDescription + "Alt+Return", true, false, true, false);
is(aElement.innerHTML, "a", aDescription + "Alt+Return");
reset("a");
synthesizeKey("VK_RETURN", { metaKey: true });
check(aDescription + "Meta+Return", true, false, true, false);
is(aElement.innerHTML, "a", aDescription + "Meta+Return");
// Enter key (same as Return key):
// If editor is readonly, it doesn't consume.
// If editor is editable and not single line editor, it consumes Return
// and Shift+Return.
// Otherwise, editor doesn't consume the event.
reset("a");
synthesizeKey("VK_ENTER", { });
check(aDescription + "Enter",
true, false, true, !aIsReadonly);
is(aElement.innerHTML, aIsReadonly ? "a" : "a<br><br>",
aDescription + "Enter");
reset("a");
synthesizeKey("VK_ENTER", { shiftKey: true });
check(aDescription + "Shift+Enter",
true, false, true, !aIsReadonly);
is(aElement.innerHTML, aIsReadonly ? "a" : "a<br><br>",
aDescription + "Shift+Enter");
reset("a");
synthesizeKey("VK_ENTER", { ctrlKey: true });
check(aDescription + "Ctrl+Enter", true, false, true, false);
is(aElement.innerHTML, "a", aDescription + "Ctrl+Enter");
reset("a");
synthesizeKey("VK_ENTER", { altKey: true });
check(aDescription + "Alt+Enter", true, false, true, false);
is(aElement.innerHTML, "a", aDescription + "Alt+Enter");
reset("a");
synthesizeKey("VK_ENTER", { metaKey: true });
check(aDescription + "Meta+Enter", true, false, true, false);
is(aElement.innerHTML, "a", aDescription + "Meta+Enter");
// Tab key:
// If editor is tabbable, editor doesn't consume all tab key events.
// Otherwise, editor consumes tab key event without any modifier keys.
reset("a");
synthesizeKey("VK_TAB", { });
check(aDescription + "Tab",
true, false, true, !aIsTabbable && !aIsReadonly);
is(aElement.innerHTML,
aIsTabbable || aIsReadonly ? "a" :
aIsPlaintext ? "a\t" : "a&nbsp;&nbsp;&nbsp; <br>",
aDescription + "Tab");
is(fm.focusedElement, aElement,
aDescription + "focus moved unexpectedly (Tab)");
reset("a");
synthesizeKey("VK_TAB", { shiftKey: true });
check(aDescription + "Shift+Tab", true, false, true, false);
is(aElement.innerHTML, "a", aDescription + "Shift+Tab");
is(fm.focusedElement, aElement,
aDescription + "focus moved unexpectedly (Shift+Tab)");
// Ctrl+Tab is consumed by tabbrowser.
reset("a");
synthesizeKey("VK_TAB", { ctrlKey: true });
check(aDescription + "Ctrl+Tab", true, true, true, true);
is(aElement.innerHTML, "a", aDescription + "Ctrl+Tab");
is(fm.focusedElement, aElement,
aDescription + "focus moved unexpectedly (Ctrl+Tab)");
reset("a");
synthesizeKey("VK_TAB", { altKey: true });
check(aDescription + "Alt+Tab", true, false, true, false);
is(aElement.innerHTML, "a", aDescription + "Alt+Tab");
is(fm.focusedElement, aElement,
aDescription + "focus moved unexpectedly (Alt+Tab)");
reset("a");
synthesizeKey("VK_TAB", { metaKey: true });
check(aDescription + "Meta+Tab", true, false, true, false);
is(aElement.innerHTML, "a", aDescription + "Meta+Tab");
is(fm.focusedElement, aElement,
aDescription + "focus moved unexpectedly (Meta+Tab)");
// Indent/Outdent tests:
// UL
resetForIndent("<ul><li id=\"target\">ul list item</li></ul>");
synthesizeKey("VK_TAB", { });
check(aDescription + "Tab on UL",
true, false, true, !aIsTabbable && !aIsReadonly);
is(aElement.innerHTML,
aIsReadonly || aIsTabbable ?
"<ul><li id=\"target\">ul list item</li></ul>" :
aIsPlaintext ? "<ul><li id=\"target\">ul list item\t</li></ul>" :
"<ul><ul><li id=\"target\">ul list item</li></ul></ul>",
aDescription + "Tab on UL");
is(fm.focusedElement, aElement,
aDescription + "focus moved unexpectedly (Tab on UL)");
synthesizeKey("VK_TAB", { shiftKey: true });
check(aDescription + "Shift+Tab after Tab on UL",
true, false, true, !aIsTabbable && !aIsReadonly && !aIsPlaintext);
// XXX why do we fail to outdent on non-tabbable HTML editor?
is(aElement.innerHTML,
aIsReadonly || aIsTabbable ?
"<ul><li id=\"target\">ul list item</li></ul>" :
aIsPlaintext ? "<ul><li id=\"target\">ul list item\t</li></ul>" :
"<ul><ul><li id=\"target\">ul list item</li></ul></ul>",
aDescription + "Shift+Tab after Tab on UL");
is(fm.focusedElement, aElement,
aDescription + "focus moved unexpectedly (Shift+Tab after Tab on UL)");
resetForIndent("<ul><li id=\"target\">ul list item</li></ul>");
synthesizeKey("VK_TAB", { shiftKey: true });
check(aDescription + "Shift+Tab on UL",
true, false, true, !aIsTabbable && !aIsReadonly && !aIsPlaintext);
is(aElement.innerHTML,
aIsReadonly || aIsTabbable ?
"<ul><li id=\"target\">ul list item</li></ul>" :
aIsPlaintext ? "<ul><li id=\"target\">ul list item</li></ul>" :
"<ul><li id=\"target\">ul list item</li></ul>",
aDescription + "Shift+Tab on UL");
is(fm.focusedElement, aElement,
aDescription + "focus moved unexpectedly (Shift+Tab on UL)");
// Ctrl+Tab is consumed by tabbrowser.
resetForIndent("<ul><li id=\"target\">ul list item</li></ul>");
synthesizeKey("VK_TAB", { ctrlKey: true });
check(aDescription + "Ctrl+Tab on UL", true, true, true, true);
is(aElement.innerHTML, "<ul><li id=\"target\">ul list item</li></ul>",
aDescription + "Ctrl+Tab on UL");
is(fm.focusedElement, aElement,
aDescription + "focus moved unexpectedly (Ctrl+Tab on UL)");
resetForIndent("<ul><li id=\"target\">ul list item</li></ul>");
synthesizeKey("VK_TAB", { altKey: true });
check(aDescription + "Alt+Tab on UL", true, false, true, false);
is(aElement.innerHTML, "<ul><li id=\"target\">ul list item</li></ul>",
aDescription + "Alt+Tab on UL");
is(fm.focusedElement, aElement,
aDescription + "focus moved unexpectedly (Alt+Tab on UL)");
resetForIndent("<ul><li id=\"target\">ul list item</li></ul>");
synthesizeKey("VK_TAB", { metaKey: true });
check(aDescription + "Meta+Tab on UL", true, false, true, false);
is(aElement.innerHTML, "<ul><li id=\"target\">ul list item</li></ul>",
aDescription + "Meta+Tab on UL");
is(fm.focusedElement, aElement,
aDescription + "focus moved unexpectedly (Meta+Tab on UL)");
// OL
resetForIndent("<ol><li id=\"target\">ol list item</li></ol>");
synthesizeKey("VK_TAB", { });
check(aDescription + "Tab on OL",
true, false, true, !aIsTabbable && !aIsReadonly);
is(aElement.innerHTML,
aIsReadonly || aIsTabbable ?
"<ol><li id=\"target\">ol list item</li></ol>" :
aIsPlaintext ? "<ol><li id=\"target\">ol list item\t</li></ol>" :
"<ol><ol><li id=\"target\">ol list item</li></ol></ol>",
aDescription + "Tab on OL");
is(fm.focusedElement, aElement,
aDescription + "focus moved unexpectedly (Tab on OL)");
synthesizeKey("VK_TAB", { shiftKey: true });
check(aDescription + "Shift+Tab after Tab on OL",
true, false, true, !aIsTabbable && !aIsReadonly && !aIsPlaintext);
// XXX why do we fail to outdent on non-tabbable HTML editor?
is(aElement.innerHTML,
aIsReadonly || aIsTabbable ?
"<ol><li id=\"target\">ol list item</li></ol>" :
aIsPlaintext ? "<ol><li id=\"target\">ol list item\t</li></ol>" :
"<ol><ol><li id=\"target\">ol list item</li></ol></ol>",
aDescription + "Shift+Tab after Tab on OL");
is(fm.focusedElement, aElement,
aDescription + "focus moved unexpectedly (Shift+Tab after Tab on OL)");
resetForIndent("<ol><li id=\"target\">ol list item</li></ol>");
synthesizeKey("VK_TAB", { shiftKey: true });
check(aDescription + "Shift+Tab on OL",
true, false, true, !aIsTabbable && !aIsReadonly && !aIsPlaintext);
is(aElement.innerHTML,
aIsReadonly || aIsTabbable ?
"<ol><li id=\"target\">ol list item</li></ol>" :
aIsPlaintext ? "<ol><li id=\"target\">ol list item</li></ol>" :
"<ol><li id=\"target\">ol list item</li></ol>",
aDescription + "Shfit+Tab on OL");
is(fm.focusedElement, aElement,
aDescription + "focus moved unexpectedly (Shift+Tab on OL)");
// Ctrl+Tab is consumed by tabbrowser.
resetForIndent("<ol><li id=\"target\">ol list item</li></ol>");
synthesizeKey("VK_TAB", { ctrlKey: true });
check(aDescription + "Ctrl+Tab on OL", true, true, true, true);
is(aElement.innerHTML, "<ol><li id=\"target\">ol list item</li></ol>",
aDescription + "Ctrl+Tab on OL");
is(fm.focusedElement, aElement,
aDescription + "focus moved unexpectedly (Ctrl+Tab on OL)");
resetForIndent("<ol><li id=\"target\">ol list item</li></ol>");
synthesizeKey("VK_TAB", { altKey: true });
check(aDescription + "Alt+Tab on OL", true, false, true, false);
is(aElement.innerHTML, "<ol><li id=\"target\">ol list item</li></ol>",
aDescription + "Alt+Tab on OL");
is(fm.focusedElement, aElement,
aDescription + "focus moved unexpectedly (Alt+Tab on OL)");
resetForIndent("<ol><li id=\"target\">ol list item</li></ol>");
synthesizeKey("VK_TAB", { metaKey: true });
check(aDescription + "Meta+Tab on OL", true, false, true, false);
is(aElement.innerHTML, "<ol><li id=\"target\">ol list item</li></ol>",
aDescription + "Meta+Tab on OL");
is(fm.focusedElement, aElement,
aDescription + "focus moved unexpectedly (Meta+Tab on OL)");
// TD
resetForIndent("<table><tr><td id=\"target\">td</td></tr></table>");
synthesizeKey("VK_TAB", { });
check(aDescription + "Tab on TD",
true, false, true, !aIsTabbable && !aIsReadonly);
is(aElement.innerHTML,
aIsTabbable || aIsReadonly ?
"<table><tbody><tr><td id=\"target\">td</td></tr></tbody></table>" :
aIsPlaintext ? "<table><tbody><tr><td id=\"target\">td\t</td></tr></tbody></table>" :
"<table><tbody><tr><td id=\"target\">td</td></tr><tr><td style=\"vertical-align: top;\"><br></td></tr></tbody></table>",
aDescription + "Tab on TD");
is(fm.focusedElement, aElement,
aDescription + "focus moved unexpectedly (Tab on TD)");
synthesizeKey("VK_TAB", { shiftKey: true });
check(aDescription + "Shift+Tab after Tab on TD",
true, false, true, !aIsTabbable && !aIsReadonly && !aIsPlaintext);
is(aElement.innerHTML,
aIsTabbable || aIsReadonly ?
"<table><tbody><tr><td id=\"target\">td</td></tr></tbody></table>" :
aIsPlaintext ? "<table><tbody><tr><td id=\"target\">td\t</td></tr></tbody></table>" :
"<table><tbody><tr><td id=\"target\">td</td></tr><tr><td style=\"vertical-align: top;\"><br></td></tr></tbody></table>",
aDescription + "Shift+Tab after Tab on TD");
is(fm.focusedElement, aElement,
aDescription + "focus moved unexpectedly (Shift+Tab after Tab on TD)");
resetForIndent("<table><tr><td id=\"target\">td</td></tr></table>");
synthesizeKey("VK_TAB", { shiftKey: true });
check(aDescription + "Shift+Tab on TD", true, false, true, false);
is(aElement.innerHTML,
"<table><tbody><tr><td id=\"target\">td</td></tr></tbody></table>",
aDescription + "Shift+Tab on TD");
is(fm.focusedElement, aElement,
aDescription + "focus moved unexpectedly (Shift+Tab on TD)");
// Ctrl+Tab is consumed by tabbrowser.
resetForIndent("<table><tr><td id=\"target\">td</td></tr></table>");
synthesizeKey("VK_TAB", { ctrlKey: true });
check(aDescription + "Ctrl+Tab on TD", true, true, true, true);
is(aElement.innerHTML,
"<table><tbody><tr><td id=\"target\">td</td></tr></tbody></table>",
aDescription + "Ctrl+Tab on TD");
is(fm.focusedElement, aElement,
aDescription + "focus moved unexpectedly (Ctrl+Tab on TD)");
resetForIndent("<table><tr><td id=\"target\">td</td></tr></table>");
synthesizeKey("VK_TAB", { altKey: true });
check(aDescription + "Alt+Tab on TD", true, false, true, false);
is(aElement.innerHTML,
"<table><tbody><tr><td id=\"target\">td</td></tr></tbody></table>",
aDescription + "Alt+Tab on TD");
is(fm.focusedElement, aElement,
aDescription + "focus moved unexpectedly (Alt+Tab on TD)");
resetForIndent("<table><tr><td id=\"target\">td</td></tr></table>");
synthesizeKey("VK_TAB", { metaKey: true });
check(aDescription + "Meta+Tab on TD", true, false, true, false);
is(aElement.innerHTML,
"<table><tbody><tr><td id=\"target\">td</td></tr></tbody></table>",
aDescription + "Meta+Tab on TD");
is(fm.focusedElement, aElement,
aDescription + "focus moved unexpectedly (Meta+Tab on TD)");
// TH
resetForIndent("<table><tr><th id=\"target\">th</th></tr></table>");
synthesizeKey("VK_TAB", { });
check(aDescription + "Tab on TH",
true, false, true, !aIsTabbable && !aIsReadonly);
is(aElement.innerHTML,
aIsTabbable || aIsReadonly ?
"<table><tbody><tr><th id=\"target\">th</th></tr></tbody></table>" :
aIsPlaintext ? "<table><tbody><tr><th id=\"target\">th\t</th></tr></tbody></table>" :
"<table><tbody><tr><th id=\"target\">th</th></tr><tr><td style=\"vertical-align: top;\"><br></td></tr></tbody></table>",
aDescription + "Tab on TH");
is(fm.focusedElement, aElement,
aDescription + "focus moved unexpectedly (Tab on TH)");
synthesizeKey("VK_TAB", { shiftKey: true });
check(aDescription + "Shift+Tab after Tab on TH",
true, false, true, !aIsTabbable && !aIsReadonly && !aIsPlaintext);
is(aElement.innerHTML,
aIsTabbable || aIsReadonly ?
"<table><tbody><tr><th id=\"target\">th</th></tr></tbody></table>" :
aIsPlaintext ? "<table><tbody><tr><th id=\"target\">th\t</th></tr></tbody></table>" :
"<table><tbody><tr><th id=\"target\">th</th></tr><tr><td style=\"vertical-align: top;\"><br></td></tr></tbody></table>",
aDescription + "Shift+Tab after Tab on TH");
is(fm.focusedElement, aElement,
aDescription + "focus moved unexpectedly (Shift+Tab after Tab on TH)");
resetForIndent("<table><tr><th id=\"target\">th</th></tr></table>");
synthesizeKey("VK_TAB", { shiftKey: true });
check(aDescription + "Shift+Tab on TH", true, false, true, false);
is(aElement.innerHTML,
"<table><tbody><tr><th id=\"target\">th</th></tr></tbody></table>",
aDescription + "Shift+Tab on TH");
is(fm.focusedElement, aElement,
aDescription + "focus moved unexpectedly (Shift+Tab on TH)");
// Ctrl+Tab is consumed by tabbrowser.
resetForIndent("<table><tr><th id=\"target\">th</th></tr></table>");
synthesizeKey("VK_TAB", { ctrlKey: true });
check(aDescription + "Ctrl+Tab on TH", true, true, true, true);
is(aElement.innerHTML,
"<table><tbody><tr><th id=\"target\">th</th></tr></tbody></table>",
aDescription + "Ctrl+Tab on TH");
is(fm.focusedElement, aElement,
aDescription + "focus moved unexpectedly (Ctrl+Tab on TH)");
resetForIndent("<table><tr><th id=\"target\">th</th></tr></table>");
synthesizeKey("VK_TAB", { altKey: true });
check(aDescription + "Alt+Tab on TH", true, false, true, false);
is(aElement.innerHTML,
"<table><tbody><tr><th id=\"target\">th</th></tr></tbody></table>",
aDescription + "Alt+Tab on TH");
is(fm.focusedElement, aElement,
aDescription + "focus moved unexpectedly (Alt+Tab on TH)");
resetForIndent("<table><tr><th id=\"target\">th</th></tr></table>");
synthesizeKey("VK_TAB", { metaKey: true });
check(aDescription + "Meta+Tab on TH", true, false, true, false);
is(aElement.innerHTML,
"<table><tbody><tr><th id=\"target\">th</th></tr></tbody></table>",
aDescription + "Meta+Tab on TH");
is(fm.focusedElement, aElement,
aDescription + "focus moved unexpectedly (Meta+Tab on TH)");
// Esc key:
// In all cases, esc key events are not consumed
reset("abc");
synthesizeKey("VK_ESCAPE", { });
check(aDescription + "Esc", true, false, true, !aIsReadonly);
reset("abc");
synthesizeKey("VK_ESCAPE", { shiftKey: true });
check(aDescription + "Shift+Esc", true, false, true, !aIsReadonly);
reset("abc");
synthesizeKey("VK_ESCAPE", { ctrlKey: true });
check(aDescription + "Ctrl+Esc", true, false, true, !aIsReadonly);
reset("abc");
synthesizeKey("VK_ESCAPE", { altKey: true });
check(aDescription + "Alt+Esc", true, false, true, !aIsReadonly);
reset("abc");
synthesizeKey("VK_ESCAPE", { metaKey: true });
check(aDescription + "Meta+Esc", true, false, true, !aIsReadonly);
// typical typing tests:
reset("");
synthesizeKey("M", { shiftKey: true });
check(aDescription + "M", true, false, true, !aIsReadonly);
synthesizeKey("o", { });
check(aDescription + "o", true, false, true, !aIsReadonly);
synthesizeKey("z", { });
check(aDescription + "z", true, false, true, !aIsReadonly);
synthesizeKey("i", { });
check(aDescription + "i", true, false, true, !aIsReadonly);
synthesizeKey("l", { });
check(aDescription + "l", true, false, true, !aIsReadonly);
synthesizeKey("l", { });
check(aDescription + "l", true, false, true, !aIsReadonly);
synthesizeKey("a", { });
check(aDescription + "a", true, false, true, !aIsReadonly);
synthesizeKey(" ", { });
check(aDescription + "' '", true, false, true, !aIsReadonly);
is(aElement.innerHTML,
aIsReadonly ? "" : aIsPlaintext ? "Mozilla " : "Mozilla <br>",
aDescription + "typed \"Mozilla \"");
}
doTest(htmlEditor, "contenteditable=\"true\"", false, true, false);
const nsIPlaintextEditor = Components.interfaces.nsIPlaintextEditor;
var editor =
window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
getInterface(Components.interfaces.nsIWebNavigation).
QueryInterface(Components.interfaces.nsIEditorDocShell).editor;
var flags = editor.flags;
// readonly
editor.flags = flags | nsIPlaintextEditor.eEditorReadonlyMask;
doTest(htmlEditor, "readonly HTML editor", true, true, false);
// non-tabbable
editor.flags = flags & ~(nsIPlaintextEditor.eEditorAllowInteraction);
doTest(htmlEditor, "non-tabbable HTML editor", false, false, false);
// readonly and non-tabbable
editor.flags =
(flags | nsIPlaintextEditor.eEditorReadonlyMask) &
~(nsIPlaintextEditor.eEditorAllowInteraction);
doTest(htmlEditor, "readonly and non-tabbable HTML editor",
true, false, false);
// plaintext
editor.flags = flags | nsIPlaintextEditor.eEditorPlaintextMask;
doTest(htmlEditor, "HTML editor but plaintext mode", false, true, true);
// plaintext and non-tabbable
editor.flags = (flags | nsIPlaintextEditor.eEditorPlaintextMask) &
~(nsIPlaintextEditor.eEditorAllowInteraction);
doTest(htmlEditor, "non-tabbable HTML editor but plaintext mode",
false, false, true);
// readonly and plaintext
editor.flags = flags | nsIPlaintextEditor.eEditorPlaintextMask |
nsIPlaintextEditor.eEditorReadonlyMask;
doTest(htmlEditor, "readonly HTML editor but plaintext mode",
true, true, true);
// readonly, plaintext and non-tabbable
editor.flags = (flags | nsIPlaintextEditor.eEditorPlaintextMask |
nsIPlaintextEditor.eEditorReadonlyMask) &
~(nsIPlaintextEditor.eEditorAllowInteraction);
doTest(htmlEditor, "readonly and non-tabbable HTML editor but plaintext mode",
true, false, true);
window.removeGroupedEventListener("keypress", listener, true, systemGroup);
window.removeGroupedEventListener("keypress", listener, false, systemGroup);
SimpleTest.finish();
}
</script>
</body>
</html>

View File

@ -354,6 +354,77 @@ PRBool nsPlaintextEditor::IsModifiable()
return !IsReadonly();
}
nsresult
nsPlaintextEditor::HandleKeyPressEvent(nsIDOMKeyEvent* aKeyEvent)
{
// NOTE: When you change this method, you should also change:
// * editor/libeditor/text/tests/test_texteditor_keyevent_handling.html
// * editor/libeditor/html/tests/test_htmleditor_keyevent_handling.html
//
// And also when you add new key handling, you need to change the subclass's
// HandleKeyPressEvent()'s switch statement.
if (IsReadonly() || IsDisabled()) {
// When we're not editable, the events handled on nsEditor.
return nsEditor::HandleKeyPressEvent(aKeyEvent);
}
nsKeyEvent* nativeKeyEvent = GetNativeKeyEvent(aKeyEvent);
NS_ENSURE_TRUE(nativeKeyEvent, NS_ERROR_UNEXPECTED);
NS_ASSERTION(nativeKeyEvent->message == NS_KEY_PRESS,
"HandleKeyPressEvent gets non-keypress event");
switch (nativeKeyEvent->keyCode) {
case nsIDOMKeyEvent::DOM_VK_META:
case nsIDOMKeyEvent::DOM_VK_SHIFT:
case nsIDOMKeyEvent::DOM_VK_CONTROL:
case nsIDOMKeyEvent::DOM_VK_ALT:
case nsIDOMKeyEvent::DOM_VK_BACK_SPACE:
case nsIDOMKeyEvent::DOM_VK_DELETE:
// These keys are handled on nsEditor
return nsEditor::HandleKeyPressEvent(aKeyEvent);
case nsIDOMKeyEvent::DOM_VK_TAB: {
if (IsTabbable()) {
return NS_OK; // let it be used for focus switching
}
if (nativeKeyEvent->isShift || nativeKeyEvent->isControl ||
nativeKeyEvent->isAlt || nativeKeyEvent->isMeta) {
return NS_OK;
}
// else we insert the tab straight through
aKeyEvent->PreventDefault();
return TypedText(NS_LITERAL_STRING("\t"), eTypedText);
}
case nsIDOMKeyEvent::DOM_VK_RETURN:
case nsIDOMKeyEvent::DOM_VK_ENTER:
if (IsSingleLineEditor() || nativeKeyEvent->isControl ||
nativeKeyEvent->isAlt || nativeKeyEvent->isMeta) {
return NS_OK;
}
aKeyEvent->PreventDefault();
return TypedText(EmptyString(), eTypedBreak);
case nsIDOMKeyEvent::DOM_VK_ESCAPE:
// pass escape keypresses through as empty strings: needed for IME support
// XXX This might be broken, we should check the behavior, see bug 471322.
// XXX Even if this keypress event is handled, this doesn't consume the
// event. This is wrong behavior but we have serious problem,
// see bug 569988 and 570455.
return TypedText(EmptyString(), eTypedText);
}
// NOTE: On some keyboard layout, some characters are inputted with Control
// key or Alt key, but at that time, widget sets FALSE to these keys.
if (nativeKeyEvent->charCode == 0 || nativeKeyEvent->isControl ||
nativeKeyEvent->isAlt || nativeKeyEvent->isMeta) {
// we don't PreventDefault() here or keybindings like control-x won't work
return NS_OK;
}
aKeyEvent->PreventDefault();
nsAutoString str(nativeKeyEvent->charCode);
return TypedText(str, eTypedText);
}
#ifdef XP_MAC
#pragma mark -
@ -361,42 +432,6 @@ PRBool nsPlaintextEditor::IsModifiable()
#pragma mark -
#endif
NS_IMETHODIMP nsPlaintextEditor::HandleKeyPress(nsIDOMKeyEvent* aKeyEvent)
{
PRUint32 keyCode, character;
PRBool ctrlKey, altKey, metaKey;
if (!aKeyEvent) return NS_ERROR_NULL_POINTER;
if (NS_SUCCEEDED(aKeyEvent->GetKeyCode(&keyCode)) &&
NS_SUCCEEDED(aKeyEvent->GetCtrlKey(&ctrlKey)) &&
NS_SUCCEEDED(aKeyEvent->GetAltKey(&altKey)) &&
NS_SUCCEEDED(aKeyEvent->GetMetaKey(&metaKey)))
{
aKeyEvent->GetCharCode(&character);
if (keyCode == nsIDOMKeyEvent::DOM_VK_RETURN
|| keyCode == nsIDOMKeyEvent::DOM_VK_ENTER)
{
nsString empty;
return TypedText(empty, eTypedBreak);
}
else if (keyCode == nsIDOMKeyEvent::DOM_VK_ESCAPE)
{
// pass escape keypresses through as empty strings: needed for ime support
nsString empty;
return TypedText(empty, eTypedText);
}
if (character && !altKey && !ctrlKey && !metaKey)
{
aKeyEvent->PreventDefault();
nsAutoString key(character);
return TypedText(key, eTypedText);
}
}
return NS_ERROR_FAILURE;
}
/* This routine is needed to provide a bottleneck for typing for logging
purposes. Can't use HandleKeyPress() (above) for that since it takes
a nsIDOMKeyEvent* parameter. So instead we pass enough info through

View File

@ -148,6 +148,8 @@ public:
/** make the given selection span the entire document */
NS_IMETHOD SelectEntireDocument(nsISelection *aSelection);
virtual nsresult HandleKeyPressEvent(nsIDOMKeyEvent* aKeyEvent);
/* ------------ Utility Routines, not part of public API -------------- */
NS_IMETHOD TypedText(const nsAString& aString, PRInt32 aAction);

View File

@ -48,6 +48,14 @@ _TEST_FILES = \
test_bug471722.html \
$(NULL)
# disables the key handling test on gtk2 because gtk2 overrides some key events
# on our editor, and the combinations depend on the system.
ifneq ($(MOZ_WIDGET_TOOLKIT),gtk2)
_TEST_FILES += \
test_texteditor_keyevent_handling.html \
$(NULL)
endif
libs:: $(_TEST_FILES)
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)

View File

@ -0,0 +1,404 @@
<html>
<head>
<title>Test for key event handler of text editor</title>
<script type="text/javascript"
src="chrome://mochikit/content/MochiKit/packed.js"></script>
<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>
<link rel="stylesheet" type="text/css"
href="chrome://mochikit/content/tests/SimpleTest/test.css" />
</head>
<body>
<div id="display">
<input type="text" id="inputField">
<input type="password" id="passwordField">
<textarea id="textarea"></textarea>
</div>
<div id="content" style="display: none">
</div>
<pre id="test">
</pre>
<script class="testbody" type="application/javascript">
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(runTests, window);
var inputField = document.getElementById("inputField");
var passwordField = document.getElementById("passwordField");
var textarea = document.getElementById("textarea");
const kIsMac = navigator.platform.indexOf("Mac") == 0;
const kIsWin = navigator.platform.indexOf("Win") == 0;
const kIsLinux = navigator.platform.indexOf("Linux") == 0;
function runTests()
{
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
var fm = Components.classes["@mozilla.org/focus-manager;1"].
getService(Components.interfaces.nsIFocusManager);
var capturingPhase = { fired: false, prevented: false };
var bubblingPhase = { fired: false, prevented: false };
var listener = {
handleEvent: function _hv(aEvent)
{
is(aEvent.type, "keypress", "unexpected event is handled");
switch (aEvent.eventPhase) {
case aEvent.CAPTURING_PHASE:
capturingPhase.fired = true;
capturingPhase.prevented = aEvent.getPreventDefault();
break;
case aEvent.BUBBLING_PHASE:
bubblingPhase.fired = true;
bubblingPhase.prevented = aEvent.getPreventDefault();
aEvent.preventDefault(); // prevent the browser default behavior
break;
default:
ok(false, "event is handled in unexpected phase");
}
}
};
function check(aDescription,
aFiredOnCapture, aPreventedOnCapture,
aFiredOnBubbling, aPreventedOnBubbling)
{
function getDesciption(aExpected)
{
return aDescription + (aExpected ? " wasn't " : " was ");
}
is(capturingPhase.fired, aFiredOnCapture,
getDesciption(aFiredOnCapture) + "fired on capture phase");
is(capturingPhase.prevented, aPreventedOnCapture,
getDesciption(aPreventedOnCapture) + "prevented on capture phase");
is(bubblingPhase.fired, aFiredOnBubbling,
getDesciption(aFiredOnBubbling) + "fired on bubbling phase");
is(bubblingPhase.prevented, aPreventedOnBubbling,
getDesciption(aPreventedOnBubbling) + "prevented on bubbling phase");
}
var systemGroup =
Components.classes["@mozilla.org/eventlistenerservice;1"].
getService(Components.interfaces.nsIEventListenerService).
systemEventGroup;
var parentElement = document.getElementById("display");
parentElement.QueryInterface(Components.interfaces.nsIDOM3EventTarget);
parentElement.addGroupedEventListener("keypress", listener, true, systemGroup);
parentElement.addGroupedEventListener("keypress", listener, false, systemGroup);
function doTest(aElement, aDescription, aIsSingleLine, aIsReadonly,
aIsTabbable)
{
function reset(aText)
{
capturingPhase.fired = false;
capturingPhase.prevented = false;
bubblingPhase.fired = false;
bubblingPhase.prevented = false;
aElement.value = aText;
}
if (document.activeElement) {
document.activeElement.blur();
}
aDescription += ": "
aElement.focus();
is(fm.focusedElement, aElement, aDescription + "failed to move focus");
// Modifier keys:
// Only when editor is editable, it consumes.
reset("");
synthesizeKey("VK_META", { });
check(aDescription + "Meta", true, false, true, !aIsReadonly);
reset("");
synthesizeKey("VK_SHIFT", { });
check(aDescription + "Shift", true, false, true, !aIsReadonly);
reset("");
synthesizeKey("VK_CONTROL", { });
check(aDescription + "Control", true, false, true, !aIsReadonly);
// Alt key press event installs menubar key event listener, so,
// we should pass Alt key testing on Windows and Linux.
if (!kIsWin && !kIsLinux) {
reset("");
synthesizeKey("VK_ALT", { });
check(aDescription + "Alt", true, false, true, !aIsReadonly);
}
// Backspace key:
// If editor is readonly, it doesn't consume.
// If editor is editable, it consumes backspace and shift+backspace.
// Otherwise, editor doesn't consume the event but the native key
// bindings on nsTextControlFrame may consume it.
reset("");
synthesizeKey("VK_BACK_SPACE", { });
check(aDescription + "Backspace", true, false, true, true);
reset("");
synthesizeKey("VK_BACK_SPACE", { shiftKey: true });
check(aDescription + "Shift+Backspace", true, false, true, true);
reset("");
synthesizeKey("VK_BACK_SPACE", { ctrlKey: true });
// Win: cmd_deleteWordBackward
check(aDescription + "Ctrl+Backspace",
true, false, true, aIsReadonly || kIsWin);
reset("");
synthesizeKey("VK_BACK_SPACE", { altKey: true });
// Win: cmd_undo
// Mac: cmd_deleteWordBackward
check(aDescription + "Alt+Backspace",
true, false, true, aIsReadonly || kIsWin || kIsMac);
reset("");
synthesizeKey("VK_BACK_SPACE", { metaKey: true });
check(aDescription + "Meta+Backspace", true, false, true, aIsReadonly);
// Delete key:
// If editor is readonly, it doesn't consume.
// If editor is editable, delete is consumed.
// Otherwise, editor doesn't consume the event but the native key
// bindings on nsTextControlFrame may consume it.
reset("");
synthesizeKey("VK_DELETE", { });
// Linux: native handler
check(aDescription + "Delete",
true, false, true, !aIsReadonly || kIsLinux);
reset("");
// Win: cmd_cutOrDelete
// Linux: cmd_cut
synthesizeKey("VK_DELETE", { shiftKey: true });
check(aDescription + "Shift+Delete",
true, false, true, kIsWin || kIsLinux);
reset("");
synthesizeKey("VK_DELETE", { ctrlKey: true });
// Win: cmd_deleteWordForward
// Linux: cmd_copy
check(aDescription + "Ctrl+Delete",
true, false, true, kIsWin || kIsLinux);
reset("");
synthesizeKey("VK_DELETE", { altKey: true });
// Mac: cmd_deleteWordForward
check(aDescription + "Alt+Delete",
true, false, true, kIsMac);
reset("");
synthesizeKey("VK_DELETE", { metaKey: true });
// Linux: native handler consumed.
check(aDescription + "Meta+Delete",
true, false, true, kIsLinux);
// XXX input.value returns "\n" when it's empty, so, we should use dummy
// value ("a") for the following tests.
// Return key:
// If editor is readonly, it doesn't consume.
// If editor is editable and not single line editor, it consumes Return
// and Shift+Return.
// Otherwise, editor doesn't consume the event.
reset("a");
synthesizeKey("VK_RETURN", { });
check(aDescription + "Return",
true, false, true, !aIsSingleLine && !aIsReadonly);
is(aElement.value, !aIsSingleLine && !aIsReadonly ? "a\n" : "a",
aDescription + "Return");
reset("a");
synthesizeKey("VK_RETURN", { shiftKey: true });
check(aDescription + "Shift+Return",
true, false, true, !aIsSingleLine && !aIsReadonly);
is(aElement.value, !aIsSingleLine && !aIsReadonly ? "a\n" : "a",
aDescription + "Shift+Return");
reset("a");
synthesizeKey("VK_RETURN", { ctrlKey: true });
check(aDescription + "Ctrl+Return", true, false, true, false);
is(aElement.value, "a", aDescription + "Ctrl+Return");
reset("a");
synthesizeKey("VK_RETURN", { altKey: true });
check(aDescription + "Alt+Return", true, false, true, false);
is(aElement.value, "a", aDescription + "Alt+Return");
reset("a");
synthesizeKey("VK_RETURN", { metaKey: true });
check(aDescription + "Meta+Return", true, false, true, false);
is(aElement.value, "a", aDescription + "Meta+Return");
// Enter key (same as Return key):
// If editor is readonly, it doesn't consume.
// If editor is editable and not single line editor, it consumes Return
// and Shift+Return.
// Otherwise, editor doesn't consume the event.
reset("a");
synthesizeKey("VK_ENTER", { });
check(aDescription + "Enter",
true, false, true, !aIsSingleLine && !aIsReadonly);
is(aElement.value, !aIsSingleLine && !aIsReadonly ? "a\n" : "a",
aDescription + "Enter");
reset("a");
synthesizeKey("VK_ENTER", { shiftKey: true });
check(aDescription + "Shift+Enter",
true, false, true, !aIsSingleLine && !aIsReadonly);
is(aElement.value, !aIsSingleLine && !aIsReadonly ? "a\n" : "a",
aDescription + "Shift+Enter");
reset("a");
synthesizeKey("VK_ENTER", { ctrlKey: true });
check(aDescription + "Ctrl+Enter", true, false, true, false);
is(aElement.value, "a", aDescription + "Ctrl+Enter");
reset("a");
synthesizeKey("VK_ENTER", { altKey: true });
check(aDescription + "Alt+Enter", true, false, true, false);
is(aElement.value, "a", aDescription + "Alt+Enter");
reset("a");
synthesizeKey("VK_ENTER", { metaKey: true });
check(aDescription + "Meta+Enter", true, false, true, false);
is(aElement.value, "a", aDescription + "Meta+Enter");
// Tab key:
// If editor is tabbable, editor doesn't consume all tab key events.
// Otherwise, editor consumes tab key event without any modifier keys.
reset("a");
synthesizeKey("VK_TAB", { });
check(aDescription + "Tab",
true, false, true, !aIsTabbable && !aIsReadonly);
// The tab char is converted to 4 space characters because textarea/input
// elements are not preformatted editor.
is(aElement.value, !aIsTabbable && !aIsReadonly ? "a " : "a",
aDescription + "Tab");
is(fm.focusedElement, aElement,
aDescription + "focus moved unexpectedly (Tab)");
reset("a");
synthesizeKey("VK_TAB", { shiftKey: true });
check(aDescription + "Shift+Tab", true, false, true, false);
is(aElement.value, "a", aDescription + "Shift+Tab");
is(fm.focusedElement, aElement,
aDescription + "focus moved unexpectedly (Shift+Tab)");
// Ctrl+Tab is consumed by tabbrowser.
reset("a");
synthesizeKey("VK_TAB", { ctrlKey: true });
check(aDescription + "Ctrl+Tab", true, true, true, true);
is(aElement.value, "a", aDescription + "Ctrl+Tab");
is(fm.focusedElement, aElement,
aDescription + "focus moved unexpectedly (Ctrl+Tab)");
reset("a");
synthesizeKey("VK_TAB", { altKey: true });
check(aDescription + "Alt+Tab", true, false, true, false);
is(aElement.value, "a", aDescription + "Alt+Tab");
is(fm.focusedElement, aElement,
aDescription + "focus moved unexpectedly (Alt+Tab)");
reset("a");
synthesizeKey("VK_TAB", { metaKey: true });
check(aDescription + "Meta+Tab", true, false, true, false);
is(aElement.value, "a", aDescription + "Meta+Tab");
is(fm.focusedElement, aElement,
aDescription + "focus moved unexpectedly (Meta+Tab)");
// Esc key:
// In all cases, esc key events are not consumed
// XXX ESC key events should be consumed when it's handled,
// however, there is a serious problem, see bug 569988.
reset("abc");
synthesizeKey("VK_ESCAPE", { });
check(aDescription + "Esc", true, false, true, false);
reset("abc");
synthesizeKey("VK_ESCAPE", { shiftKey: true });
check(aDescription + "Shift+Esc", true, false, true, false);
reset("abc");
synthesizeKey("VK_ESCAPE", { ctrlKey: true });
check(aDescription + "Ctrl+Esc", true, false, true, false);
reset("abc");
synthesizeKey("VK_ESCAPE", { altKey: true });
check(aDescription + "Alt+Esc", true, false, true, false);
reset("abc");
synthesizeKey("VK_ESCAPE", { metaKey: true });
check(aDescription + "Meta+Esc", true, false, true, false);
// typical typing tests:
reset("");
synthesizeKey("M", { shiftKey: true });
check(aDescription + "M", true, false, true, !aIsReadonly);
synthesizeKey("o", { });
check(aDescription + "o", true, false, true, !aIsReadonly);
synthesizeKey("z", { });
check(aDescription + "z", true, false, true, !aIsReadonly);
synthesizeKey("i", { });
check(aDescription + "i", true, false, true, !aIsReadonly);
synthesizeKey("l", { });
check(aDescription + "l", true, false, true, !aIsReadonly);
synthesizeKey("l", { });
check(aDescription + "l", true, false, true, !aIsReadonly);
synthesizeKey("a", { });
check(aDescription + "a", true, false, true, !aIsReadonly);
synthesizeKey(" ", { });
check(aDescription + "' '", true, false, true, !aIsReadonly);
is(aElement.value, !aIsReadonly ? "Mozilla " : "",
aDescription + "typed \"Mozilla \"");
}
doTest(inputField, "<input type=\"text\">", true, false, true);
inputField.setAttribute("readonly", "readonly");
doTest(inputField, "<input type=\"text\" readonly>", true, true, true);
doTest(passwordField, "<input type=\"password\">", true, false, true);
passwordField.setAttribute("readonly", "readonly");
doTest(passwordField, "<input type=\"password\" readonly>", true, true, true);
doTest(textarea, "<textarea>", false, false, true);
textarea.setAttribute("readonly", "readonly");
doTest(textarea, "<textarea readonly>", false, true, true);
// make non-tabbable plaintext editor
textarea.removeAttribute("readonly");
const nsIPlaintextEditor = Components.interfaces.nsIPlaintextEditor;
const nsIDOMNSEditableElement = Components.interfaces.nsIDOMNSEditableElement;
var editor = textarea.QueryInterface(nsIDOMNSEditableElement).editor;
var flags = editor.flags;
editor.flags = flags & ~(nsIPlaintextEditor.eEditorWidgetMask |
nsIPlaintextEditor.eEditorAllowInteraction);
doTest(textarea, "non-tabbable <textarea>", false, false, false);
textarea.setAttribute("readonly", "readonly");
doTest(textarea, "non-tabbable <textarea readonly>", false, true, false);
editor.flags = flags;
parentElement.removeGroupedEventListener("keypress", listener, true, systemGroup);
parentElement.removeGroupedEventListener("keypress", listener, false, systemGroup);
SimpleTest.finish();
}
</script>
</body>
</html>