зеркало из https://github.com/mozilla/pjs.git
Bug 564669 Remove nsIPlaintextEditor::handleKeyPress() r=smaug+ehsan, sr=roc
This commit is contained in:
Родитель
fb4b7c2ebb
Коммит
76e7f1c2b7
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ------------ */
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 <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>
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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>
|
Загрузка…
Ссылка в новой задаче