Bug 389372 Contenteditable node is still editable without focus r=ehsan+roc, sr=jst

This commit is contained in:
Masayuki Nakano 2010-07-22 08:46:52 +09:00
parent 717098c537
commit a6e67f7876
11 changed files with 575 additions and 70 deletions

View File

@ -71,8 +71,8 @@ enum nsLinkState {
// IID for the nsIContent interface
#define NS_ICONTENT_IID \
{ 0x1450010b, 0xcdca, 0x451c, \
{ 0xba, 0xdc, 0x07, 0x90, 0x89, 0x7b, 0xce, 0xb8 } }
{ 0x2ac19ac3, 0x1dac, 0x42dc, \
{ 0xb2, 0x43, 0x78, 0x46, 0xed, 0x6f, 0x1c, 0x89 } }
/**
* A node of content in a document's content model. This interface
@ -928,6 +928,19 @@ public:
virtual PRBool IsEqualNode(nsINode* aOther);
/**
* If this content has independent selection, e.g., if this is input field
* or textarea, this return TRUE. Otherwise, false.
*/
PRBool HasIndependentSelection();
/**
* If the content is a part of HTML editor, this returns editing
* host content. When the content is in designMode, this returns its body
* element. Also, when the content isn't editable, this returns null.
*/
nsIContent* GetEditingHost();
protected:
/**
* Hook for implementing GetID. This is guaranteed to only be

View File

@ -366,8 +366,7 @@ nsINode::GetSelectionRootContent(nsIPresShell* aPresShell)
return nsnull;
}
nsIFrame* frame = static_cast<nsIContent*>(this)->GetPrimaryFrame();
if (frame && frame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION) {
if (static_cast<nsIContent*>(this)->HasIndependentSelection()) {
// This node should be a descendant of input/textarea editor.
nsIContent* content = GetTextEditorRootContent();
if (content)
@ -388,15 +387,9 @@ nsINode::GetSelectionRootContent(nsIPresShell* aPresShell)
editorRoot :
GetRootForContentSubtree(static_cast<nsIContent*>(this));
}
// If the current document is not editable, but current content is
// editable, we should assume that the child of the nearest non-editable
// ancestor is selection root.
nsIContent* content = static_cast<nsIContent*>(this);
for (nsIContent* parent = GetParent();
parent && parent->HasFlag(NODE_IS_EDITABLE);
parent = content->GetParent())
content = parent;
return content;
// If the document isn't editable but this is editable, this is in
// contenteditable. Use the editing host element for selection root.
return static_cast<nsIContent*>(this)->GetEditingHost();
}
}
@ -830,14 +823,13 @@ nsIContent::GetDesiredIMEState()
if (!IsEditableInternal()) {
return IME_STATUS_DISABLE;
}
nsIContent *editableAncestor = nsnull;
for (nsIContent* parent = GetParent();
parent && parent->HasFlag(NODE_IS_EDITABLE);
parent = parent->GetParent()) {
editableAncestor = parent;
}
// NOTE: The content for independent editors (e.g., input[type=text],
// textarea) must override this method, so, we don't need to worry about
// that here.
nsIContent *editableAncestor = GetEditingHost();
// This is in another editable content, use the result of it.
if (editableAncestor) {
if (editableAncestor && editableAncestor != this) {
return editableAncestor->GetDesiredIMEState();
}
nsIDocument* doc = GetCurrentDoc();
@ -865,6 +857,35 @@ nsIContent::GetDesiredIMEState()
return state;
}
PRBool
nsIContent::HasIndependentSelection()
{
nsIFrame* frame = GetPrimaryFrame();
return (frame && frame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION);
}
nsIContent*
nsIContent::GetEditingHost()
{
// If this isn't editable, return NULL.
NS_ENSURE_TRUE(HasFlag(NODE_IS_EDITABLE), nsnull);
nsIDocument* doc = GetCurrentDoc();
NS_ENSURE_TRUE(doc, nsnull);
// If this is in designMode, we should return <body>
if (doc->HasFlag(NODE_IS_EDITABLE)) {
return doc->GetBodyElement();
}
nsIContent* content = this;
for (nsIContent* parent = GetParent();
parent && parent->HasFlag(NODE_IS_EDITABLE);
parent = content->GetParent()) {
content = parent;
}
return content;
}
nsresult
nsIContent::LookupNamespaceURI(const nsAString& aNamespacePrefix,
nsAString& aNamespaceURI) const

View File

@ -30,6 +30,12 @@ function onLoad() {
}
function onReload() {
var iframe = window.frames[0].frameElement;
SimpleTest.waitForFocus(doTest, iframe.contentWindow);
iframe.contentDocument.body.focus();
}
function doTest() {
var bodyElement = window.frames[0].frameElement.contentDocument.body;
sendChar('S', bodyElement);
sendChar('t', bodyElement);

View File

@ -43,6 +43,8 @@
#include "nsIDOMDocument.h"
#include "nsIDOMHTMLElement.h"
#include "nsIDOMNSHTMLElement.h"
#include "nsIDOMEventTarget.h"
#include "nsIDOMNSEvent.h"
#include "nsPIDOMEventTarget.h"
#include "nsIMEStateManager.h"
#include "nsFocusManager.h"
@ -5166,3 +5168,41 @@ nsEditor::HasFocus()
nsCOMPtr<nsIContent> content = fm->GetFocusedContent();
return SameCOMIdentity(content, piTarget);
}
PRBool
nsEditor::IsActiveInDOMWindow()
{
nsCOMPtr<nsPIDOMEventTarget> piTarget = GetPIDOMEventTarget();
if (!piTarget) {
return PR_FALSE;
}
nsFocusManager* fm = nsFocusManager::GetFocusManager();
NS_ENSURE_TRUE(fm, PR_FALSE);
nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak);
nsPIDOMWindow* ourWindow = doc->GetWindow();
nsCOMPtr<nsPIDOMWindow> win;
nsIContent* content =
nsFocusManager::GetFocusedDescendant(ourWindow, PR_FALSE,
getter_AddRefs(win));
return SameCOMIdentity(content, piTarget);
}
PRBool
nsEditor::IsAcceptableInputEvent(nsIDOMEvent* aEvent)
{
// If the event is trusted, the event should always cause input.
nsCOMPtr<nsIDOMNSEvent> NSEvent = do_QueryInterface(aEvent);
NS_ENSURE_TRUE(NSEvent, PR_FALSE);
PRBool isTrusted;
nsresult rv = NSEvent->GetIsTrusted(&isTrusted);
NS_ENSURE_SUCCESS(rv, PR_FALSE);
if (isTrusted) {
return PR_TRUE;
}
// Otherwise, we shouldn't handle any input events when we're not an active
// element of the DOM window.
return IsActiveInDOMWindow();
}

View File

@ -659,9 +659,20 @@ public:
IsInteractionAllowed();
}
// Whether the editor has focus or not.
// Whether the editor has application level focus or not.
virtual PRBool HasFocus();
// Whether the editor is active on the DOM window. Note that when this
// returns true but HasFocus() returns false, it means that this editor was
// focused when the DOM window was active.
virtual PRBool IsActiveInDOMWindow();
// Whether the aEvent should be handled by this editor or not. When this
// returns FALSE, The aEvent shouldn't be handled on this editor,
// i.e., The aEvent should be handled by another inner editor or ancestor
// elements.
virtual PRBool IsAcceptableInputEvent(nsIDOMEvent* aEvent);
// FindSelectionRoot() returns a selection root of this editor when aNode
// gets focus. aNode must be a content node or a document node. When the
// target isn't a part of this editor, returns NULL. If this is for

View File

@ -316,6 +316,10 @@ nsEditorEventListener::KeyPress(nsIDOMEvent* aKeyEvent)
{
NS_ENSURE_TRUE(mEditor, NS_ERROR_NOT_AVAILABLE);
if (!mEditor->IsAcceptableInputEvent(aKeyEvent)) {
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.
@ -483,6 +487,10 @@ nsEditorEventListener::HandleText(nsIDOMEvent* aTextEvent)
{
NS_ENSURE_TRUE(mEditor, NS_ERROR_NOT_AVAILABLE);
if (!mEditor->IsAcceptableInputEvent(aTextEvent)) {
return NS_OK;
}
nsCOMPtr<nsIPrivateTextEvent> textEvent = do_QueryInterface(aTextEvent);
if (!textEvent) {
//non-ui event passed in. bad things.
@ -773,6 +781,9 @@ NS_IMETHODIMP
nsEditorEventListener::HandleStartComposition(nsIDOMEvent* aCompositionEvent)
{
NS_ENSURE_TRUE(mEditor, NS_ERROR_NOT_AVAILABLE);
if (!mEditor->IsAcceptableInputEvent(aCompositionEvent)) {
return NS_OK;
}
return mEditor->BeginIMEComposition();
}
@ -780,6 +791,9 @@ NS_IMETHODIMP
nsEditorEventListener::HandleEndComposition(nsIDOMEvent* aCompositionEvent)
{
NS_ENSURE_TRUE(mEditor, NS_ERROR_NOT_AVAILABLE);
if (!mEditor->IsAcceptableInputEvent(aCompositionEvent)) {
return NS_OK;
}
return mEditor->EndIMEComposition();
}

View File

@ -426,10 +426,7 @@ nsHTMLEditor::FindSelectionRoot(nsINode *aNode)
// For non-readonly editors we want to find the root of the editable subtree
// containing aContent.
nsIContent *parent;
while ((parent = content->GetParent()) && parent->HasFlag(NODE_IS_EDITABLE)) {
content = parent;
}
content = content->GetEditingHost();
return content.forget();
}
@ -630,17 +627,6 @@ nsHTMLEditor::HandleKeyPressEvent(nsIDOMKeyEvent* aKeyEvent)
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,
@ -4200,8 +4186,7 @@ nsHTMLEditor::SelectAll()
// If the anchor content has independent selection, we never need to explicitly
// select its children.
nsIFrame* frame = anchorContent->GetPrimaryFrame();
if (frame && frame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION) {
if (anchorContent->HasIndependentSelection()) {
nsCOMPtr<nsISelectionPrivate> selPriv = do_QueryInterface(selection);
NS_ENSURE_TRUE(selPriv, NS_ERROR_UNEXPECTED);
rv = selPriv->SetAncestorLimiter(nsnull);
@ -5804,13 +5789,49 @@ nsHTMLEditor::HasFocus()
// If the focused content isn't editable, or it has independent selection,
// we don't have focus.
if (!focusedContent->HasFlag(NODE_IS_EDITABLE) ||
IsIndependentSelectionContent(focusedContent)) {
focusedContent->HasIndependentSelection()) {
return PR_FALSE;
}
// If our window is focused, we're focused.
return OurWindowHasFocus();
}
PRBool
nsHTMLEditor::IsActiveInDOMWindow()
{
NS_ENSURE_TRUE(mDocWeak, PR_FALSE);
nsFocusManager* fm = nsFocusManager::GetFocusManager();
NS_ENSURE_TRUE(fm, PR_FALSE);
nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak);
PRBool inDesignMode = doc->HasFlag(NODE_IS_EDITABLE);
// If we're in designMode, we're always active in the DOM window.
if (inDesignMode) {
return PR_TRUE;
}
nsPIDOMWindow* ourWindow = doc->GetWindow();
nsCOMPtr<nsPIDOMWindow> win;
nsIContent* content =
nsFocusManager::GetFocusedDescendant(ourWindow, PR_FALSE,
getter_AddRefs(win));
if (!content) {
return PR_FALSE;
}
// We're HTML editor for contenteditable
// If the active content isn't editable, or it has independent selection,
// we're not active).
if (!content->HasFlag(NODE_IS_EDITABLE) ||
content->HasIndependentSelection()) {
return PR_FALSE;
}
return PR_TRUE;
}
already_AddRefed<nsPIDOMEventTarget>
nsHTMLEditor::GetPIDOMEventTarget()
{
@ -5920,11 +5941,46 @@ nsHTMLEditor::OurWindowHasFocus()
}
PRBool
nsHTMLEditor::IsIndependentSelectionContent(nsIContent* aContent)
nsHTMLEditor::IsAcceptableInputEvent(nsIDOMEvent* aEvent)
{
NS_PRECONDITION(aContent, "aContent must not be null");
nsIFrame* frame = aContent->GetPrimaryFrame();
return (frame && (frame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION));
if (!nsEditor::IsAcceptableInputEvent(aEvent)) {
return PR_FALSE;
}
NS_ENSURE_TRUE(mDocWeak, PR_FALSE);
nsCOMPtr<nsIDOMEventTarget> target;
aEvent->GetTarget(getter_AddRefs(target));
NS_ENSURE_TRUE(target, PR_FALSE);
nsCOMPtr<nsIDocument> document = do_QueryReferent(mDocWeak);
if (document->HasFlag(NODE_IS_EDITABLE)) {
// If this editor is in designMode and the event target is the document,
// the event is for this editor.
nsCOMPtr<nsIDocument> targetDocument = do_QueryInterface(target);
if (targetDocument) {
return targetDocument == document;
}
// Otherwise, check whether the event target is in this document or not.
nsCOMPtr<nsIContent> targetContent = do_QueryInterface(target);
NS_ENSURE_TRUE(targetContent, PR_FALSE);
return document == targetContent->GetCurrentDoc();
}
// If this is for contenteditable, we should check whether the target is
// editable or not.
nsCOMPtr<nsIContent> targetContent = do_QueryInterface(target);
NS_ENSURE_TRUE(targetContent, PR_FALSE);
if (!targetContent->HasFlag(NODE_IS_EDITABLE) ||
targetContent->HasIndependentSelection()) {
return PR_FALSE;
}
// Finally, check whether we're actually focused or not. When we're not
// focused, we should ignore the dispatched event by script (or something)
// because content editable element needs selection in itself for editing.
// However, when we're not focused, it's not guaranteed.
return IsActiveInDOMWindow();
}
NS_IMETHODIMP

View File

@ -149,8 +149,10 @@ public:
NS_IMETHODIMP BeginningOfDocument();
virtual nsresult HandleKeyPressEvent(nsIDOMKeyEvent* aKeyEvent);
virtual PRBool HasFocus();
virtual PRBool IsActiveInDOMWindow();
virtual already_AddRefed<nsPIDOMEventTarget> GetPIDOMEventTarget();
virtual already_AddRefed<nsIContent> FindSelectionRoot(nsINode *aNode);
virtual PRBool IsAcceptableInputEvent(nsIDOMEvent* aEvent);
/* ------------ nsStubMutationObserver overrides --------- */
NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
@ -451,6 +453,9 @@ protected:
PRBool ShouldReplaceRootElement();
void ResetRootElementAndEventTarget();
nsresult GetBodyElement(nsIDOMHTMLElement** aBody);
// Get the focused node of this editor.
// @return If the editor has focus, this returns the focused node.
// Otherwise, returns null.
already_AddRefed<nsINode> GetFocusedNode();
// Return TRUE if aElement is a table-related elemet and caret was set
@ -745,9 +750,6 @@ protected:
// Whether the outer window of the DOM event target has focus or not.
PRBool OurWindowHasFocus();
// Whether the content has independent selection or not. E.g., input field,
// password field and textarea element. At that time, this returns TRUE.
PRBool IsIndependentSelectionContent(nsIContent* aContent);
// Data members
protected:

View File

@ -61,6 +61,7 @@ _TEST_FILES = \
test_bug550434.html \
test_CF_HTML_clipboard.html \
test_contenteditable_focus.html \
test_contenteditable_text_input_handling.html \
test_htmleditor_keyevent_handling.html \
test_select_all_without_body.html \
file_select_all_without_body.html \

View File

@ -0,0 +1,336 @@
<html>
<head>
<title>Test for text input event handling on contenteditable 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">
<p id="static">static content<input id="inputInStatic"><textarea id="textareaInStatic"></textarea></p>
<p id="editor"contenteditable="true">content editable<input id="inputInEditor"><textarea id="textareaInEditor"></textarea></p>
</div>
<div id="content" style="display: none">
</div>
<pre id="test">
</pre>
<script class="testbody" type="application/javascript">
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(runTests);
function runTests()
{
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
var fm = Components.classes["@mozilla.org/focus-manager;1"].
getService(Components.interfaces.nsIFocusManager);
var listener = {
handleEvent: function _hv(aEvent)
{
aEvent.preventDefault(); // prevent the browser default behavior
}
};
var systemGroup =
Components.classes["@mozilla.org/eventlistenerservice;1"].
getService(Components.interfaces.nsIEventListenerService).
systemEventGroup;
window.QueryInterface(Components.interfaces.nsIDOM3EventTarget);
window.addGroupedEventListener("keypress", listener, false, systemGroup);
var staticContent = document.getElementById("static");
staticContent._defaultValue = getTextValue(staticContent);
staticContent._isFocusable = false;
staticContent._isEditable = false;
staticContent._isContentEditable = false;
staticContent._description = "non-editable p element";
var inputInStatic = document.getElementById("inputInStatic");
inputInStatic._defaultValue = getTextValue(inputInStatic);
inputInStatic._isFocusable = true;
inputInStatic._isEditable = true;
inputInStatic._isContentEditable = false;
inputInStatic._description = "input element in static content";
var textareaInStatic = document.getElementById("textareaInStatic");
textareaInStatic._defaultValue = getTextValue(textareaInStatic);
textareaInStatic._isFocusable = true;
textareaInStatic._isEditable = true;
textareaInStatic._isContentEditable = false;
textareaInStatic._description = "textarea element in static content";
var editor = document.getElementById("editor");
editor._defaultValue = getTextValue(editor);
editor._isFocusable = true;
editor._isEditable = true;
editor._isContentEditable = true;
editor._description = "contenteditable editor";
var inputInEditor = document.getElementById("inputInEditor");
inputInEditor._defaultValue = getTextValue(inputInEditor);
inputInEditor._isFocusable = true;
inputInEditor._isEditable = true;
inputInEditor._isContentEditable = false;
inputInEditor._description = "input element in contenteditable editor";
var textareaInEditor = document.getElementById("textareaInEditor");
textareaInEditor._defaultValue = getTextValue(textareaInEditor);
textareaInEditor._isFocusable = true;
textareaInEditor._isEditable = true;
textareaInEditor._isContentEditable = false;
textareaInEditor._description = "textarea element in contenteditable editor";
function getTextValue(aElement)
{
if (aElement == editor) {
var value = "";
for (var node = aElement.firstChild; node; node = node.nextSibling) {
if (node.nodeType == 3) {
value += node.data;
}
}
return value;
}
return aElement.value;
}
function testTextInput(aFocus)
{
var when = " when " +
((aFocus && aFocus._isFocusable) ? aFocus._description + " has focus" :
"nobody has focus");
function checkValue(aElement, aInsertedText)
{
if (aElement == aFocus && aElement._isEditable) {
is(getTextValue(aElement), aInsertedText + aElement._defaultValue,
aElement._description +
" wasn't edited by synthesized key events" + when);
return;
}
is(getTextValue(aElement), aElement._defaultValue,
aElement._description +
" was edited by synthesized key events" + when);
}
if (aFocus && aFocus._isFocusable) {
aFocus.focus();
is(fm.focusedElement, aFocus,
aFocus._description + " didn't get focus at preparing tests" + when);
} else {
var focusedElement = fm.focusedElement;
if (focusedElement) {
focusedElement.blur();
}
ok(!fm.focusedElement,
"Failed to blur at preparing tests" + when);
}
if (aFocus && aFocus._isFocusable) {
synthesizeKey("A", { });
synthesizeKey("B", { });
synthesizeKey("C", { });
checkValue(staticContent, "ABC");
checkValue(inputInStatic, "ABC");
checkValue(textareaInStatic, "ABC");
checkValue(editor, "ABC");
checkValue(inputInEditor, "ABC");
checkValue(textareaInEditor, "ABC");
if (aFocus._isEditable) {
synthesizeKey("VK_BACK_SPACE", { });
synthesizeKey("VK_BACK_SPACE", { });
synthesizeKey("VK_BACK_SPACE", { });
checkValue(staticContent, "");
checkValue(inputInStatic, "");
checkValue(textareaInStatic, "");
checkValue(editor, "");
checkValue(inputInEditor, "");
checkValue(textareaInEditor, "");
}
}
// When key events are fired on unfocused editor.
function testDispatchedKeyEvent(aTarget)
{
var targetDescription = " (dispatched to " + aTarget._description + ")";
function dispatchKeyEvent(aKeyCode, aChar, aTarget)
{
var keyEvent = document.createEvent("KeyEvents");
keyEvent.initKeyEvent("keypress", true, true, null, false, false,
false, false, aKeyCode,
aChar ? aChar.charCodeAt(0) : 0);
aTarget.dispatchEvent(keyEvent);
}
function checkValueForDispatchedKeyEvent(aElement, aInsertedText)
{
if (aElement == aTarget && aElement._isEditable &&
(!aElement._isContentEditable || aElement == aFocus)) {
is(getTextValue(aElement), aInsertedText + aElement._defaultValue,
aElement._description +
" wasn't edited by dispatched key events" +
when + targetDescription);
return;
}
if (aElement == aTarget) {
is(getTextValue(aElement), aElement._defaultValue,
aElement._description +
" was edited by dispatched key events" +
when + targetDescription);
return;
}
is(getTextValue(aElement), aElement._defaultValue,
aElement._description +
" was edited by key events unexpectedly" +
when + targetDescription);
}
dispatchKeyEvent(0, "A", aTarget);
dispatchKeyEvent(0, "B", aTarget);
dispatchKeyEvent(0, "C", aTarget);
checkValueForDispatchedKeyEvent(staticContent, "ABC");
checkValueForDispatchedKeyEvent(inputInStatic, "ABC");
checkValueForDispatchedKeyEvent(textareaInStatic, "ABC");
checkValueForDispatchedKeyEvent(editor, "ABC");
checkValueForDispatchedKeyEvent(inputInEditor, "ABC");
checkValueForDispatchedKeyEvent(textareaInEditor, "ABC");
const nsIDOMKeyEvent = Components.interfaces.nsIDOMKeyEvent;
dispatchKeyEvent(nsIDOMKeyEvent.DOM_VK_BACK_SPACE, 0, aTarget);
dispatchKeyEvent(nsIDOMKeyEvent.DOM_VK_BACK_SPACE, 0, aTarget);
dispatchKeyEvent(nsIDOMKeyEvent.DOM_VK_BACK_SPACE, 0, aTarget);
checkValueForDispatchedKeyEvent(staticContent, "");
checkValueForDispatchedKeyEvent(inputInStatic, "");
checkValueForDispatchedKeyEvent(textareaInStatic, "");
checkValueForDispatchedKeyEvent(editor, "");
checkValueForDispatchedKeyEvent(inputInEditor, "");
checkValueForDispatchedKeyEvent(textareaInEditor, "");
}
testDispatchedKeyEvent(staticContent);
testDispatchedKeyEvent(inputInStatic);
testDispatchedKeyEvent(textareaInStatic);
testDispatchedKeyEvent(editor);
testDispatchedKeyEvent(inputInEditor);
testDispatchedKeyEvent(textareaInEditor);
if (!aFocus._isEditable) {
return;
}
// IME
const nsIDOMWindowUtils = Components.interfaces.nsIDOMWindowUtils;
// start composition
synthesizeComposition(true);
// input first character
synthesizeText(
{ "composition":
{ "string": "\u3089",
"clauses":
[
{ "length": 1, "attr": nsIDOMWindowUtils.COMPOSITION_ATTR_RAWINPUT }
]
},
"caret": { "start": 1, "length": 0 }
});
var queryText = synthesizeQueryTextContent(0, 100);
ok(queryText, "query text event result is null" + when);
if (!queryText) {
return;
}
ok(queryText.succeeded, "query text event failed" + when);
if (!queryText.succeeded) {
return;
}
is(queryText.text, "\u3089" + aFocus._defaultValue,
"composing text is incorrect" + when);
var querySelectedText = synthesizeQuerySelectedText();
ok(querySelectedText, "query selected text event result is null" + when);
if (!querySelectedText) {
return;
}
ok(querySelectedText.succeeded, "query selected text event failed" + when);
if (!querySelectedText.succeeded) {
return;
}
is(querySelectedText.offset, 1,
"query selected text event returns wrong offset" + when);
is(querySelectedText.text, "",
"query selected text event returns wrong selected text" + when);
// commit composition
synthesizeText(
{ "composition":
{ "string": "\u3089",
"clauses":
[
{ "length": 0, "attr": 0 }
]
},
"caret": { "start": 1, "length": 0 }
});
queryText = synthesizeQueryTextContent(0, 100);
ok(queryText, "query text event result is null after commit" + when);
if (!queryText) {
return;
}
ok(queryText.succeeded, "query text event failed after commit" + when);
if (!queryText.succeeded) {
return;
}
is(queryText.text, "\u3089" + aFocus._defaultValue,
"composing text is incorrect after commit" + when);
querySelectedText = synthesizeQuerySelectedText();
ok(querySelectedText,
"query selected text event result is null after commit" + when);
if (!querySelectedText) {
return;
}
ok(querySelectedText.succeeded,
"query selected text event failed after commit" + when);
if (!querySelectedText.succeeded) {
return;
}
is(querySelectedText.offset, 1,
"query selected text event returns wrong offset after commit" + when);
is(querySelectedText.text, "",
"query selected text event returns wrong selected text after commit" +
when);
// end composition
synthesizeComposition(false);
checkValue(staticContent, "\u3089");
checkValue(inputInStatic, "\u3089");
checkValue(textareaInStatic, "\u3089");
checkValue(editor, "\u3089");
checkValue(inputInEditor, "\u3089");
checkValue(textareaInEditor, "\u3089");
synthesizeKey("VK_BACK_SPACE", { });
checkValue(staticContent, "");
checkValue(inputInStatic, "");
checkValue(textareaInStatic, "");
checkValue(editor, "");
checkValue(inputInEditor, "");
checkValue(textareaInEditor, "");
}
testTextInput(inputInStatic);
testTextInput(textareaInStatic);
testTextInput(editor);
testTextInput(inputInEditor);
testTextInput(textareaInEditor);
window.removeGroupedEventListener("keypress", listener, false, systemGroup);
SimpleTest.finish();
}
</script>
</body>
</html>

View File

@ -7,6 +7,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=468167
<title>Test for Bug 468167</title>
<script type="application/javascript" src="/MochiKit/packed.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
@ -19,31 +20,35 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=468167
/** Test for Bug 468167 **/
var ta = document.getElementById("ta");
is(ta.scrollTop, 0, "initially scrolled to top");
var s = "";
for (var i = 0; i < 40; ++i) {
// three characters per line
if (i < 10)
s += "0";
s += i + "\n";
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(runTests);
function runTests()
{
var ta = document.getElementById("ta");
ta.focus();
is(ta.scrollTop, 0, "initially scrolled to top");
var s = "";
for (var i = 0; i < 40; ++i) {
// three characters per line
if (i < 10)
s += "0";
s += i + "\n";
}
ta.value = s;
is(ta.scrollTop, 0, "scrolled to top after adding content");
ta.scrollTop = 9999; // scroll down as far as we can
isnot(ta.scrollTop, 0, "scrolled down after scrolling down");
ta.setSelectionRange(0, 99); // 33 lines out of 40
// Send a backspace key event to delete the selection
synthesizeKey("VK_BACK_SPACE", { });
is(ta.scrollTop, 0, "scrolled to top after deleting selection");
SimpleTest.finish();
}
ta.value = s;
is(ta.scrollTop, 0, "scrolled to top after adding content");
ta.scrollTop = 9999; // scroll down as far as we can
isnot(ta.scrollTop, 0, "scrolled down after scrolling down");
ta.setSelectionRange(0, 99); // 33 lines out of 40
// Send a backspace key event to delete the selection
var e = document.createEvent("KeyEvents");
e.initKeyEvent("keypress", true, true, document.defaultView,
false, false, false, false,
KeyEvent.DOM_VK_BACK_SPACE, 0);
ta.dispatchEvent(e);
is(ta.scrollTop, 0, "scrolled to top after deleting selection");
</script>
</pre>