Bug 488420 IME enabled state is not modified when a focused editor's readonly attribute is changed r=smaug

This commit is contained in:
Masayuki Nakano 2010-05-05 02:40:39 +09:00
Родитель a8da5c275d
Коммит dc7e7da106
13 изменённых файлов: 787 добавлений и 77 удалений

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

@ -615,20 +615,7 @@ public:
IME_STATUS_PASSWORD | IME_STATUS_PLUGIN, IME_STATUS_PASSWORD | IME_STATUS_PLUGIN,
IME_STATUS_MASK_OPENED = IME_STATUS_OPEN | IME_STATUS_CLOSE IME_STATUS_MASK_OPENED = IME_STATUS_OPEN | IME_STATUS_CLOSE
}; };
virtual PRUint32 GetDesiredIMEState() virtual PRUint32 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;
// This is in another editable content, use the result of it.
if (editableAncestor)
return editableAncestor->GetDesiredIMEState();
return IME_STATUS_ENABLE;
}
/** /**
* Gets content node with the binding (or native code, possibly on the * Gets content node with the binding (or native code, possibly on the

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

@ -125,6 +125,7 @@
#include "nsIDOMUserDataHandler.h" #include "nsIDOMUserDataHandler.h"
#include "nsGenericHTMLElement.h" #include "nsGenericHTMLElement.h"
#include "nsIEditor.h" #include "nsIEditor.h"
#include "nsIEditorIMESupport.h"
#include "nsIEditorDocShell.h" #include "nsIEditorDocShell.h"
#include "nsEventDispatcher.h" #include "nsEventDispatcher.h"
#include "nsContentCreatorFunctions.h" #include "nsContentCreatorFunctions.h"
@ -636,6 +637,47 @@ nsIContent::GetFlattenedTreeParent() const
return parent; return parent;
} }
PRUint32
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;
}
// This is in another editable content, use the result of it.
if (editableAncestor) {
return editableAncestor->GetDesiredIMEState();
}
nsIDocument* doc = GetCurrentDoc();
if (!doc) {
return IME_STATUS_DISABLE;
}
nsIPresShell* ps = doc->GetPrimaryShell();
if (!ps) {
return IME_STATUS_DISABLE;
}
nsPresContext* pc = ps->GetPresContext();
if (!pc) {
return IME_STATUS_DISABLE;
}
nsIEditor* editor = GetHTMLEditor(pc);
nsCOMPtr<nsIEditorIMESupport> imeEditor = do_QueryInterface(editor);
if (!imeEditor) {
return IME_STATUS_DISABLE;
}
// Use "enable" for the default value because IME is disabled unexpectedly,
// it makes serious a11y problem.
PRUint32 state = IME_STATUS_ENABLE;
nsresult rv = imeEditor->GetPreferredIMEState(&state);
NS_ENSURE_SUCCESS(rv, IME_STATUS_ENABLE);
return state;
}
//---------------------------------------------------------------------- //----------------------------------------------------------------------
NS_IMPL_ADDREF(nsChildContentList) NS_IMPL_ADDREF(nsChildContentList)

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

@ -83,7 +83,7 @@ nsIMEStateManager::OnDestroyPresContext(nsPresContext* aPresContext)
nsCOMPtr<nsIWidget> widget = GetWidget(sPresContext); nsCOMPtr<nsIWidget> widget = GetWidget(sPresContext);
if (widget) { if (widget) {
PRUint32 newState = GetNewIMEState(sPresContext, nsnull); PRUint32 newState = GetNewIMEState(sPresContext, nsnull);
SetIMEState(sPresContext, newState, widget); SetIMEState(newState, widget);
} }
sContent = nsnull; sContent = nsnull;
sPresContext = nsnull; sPresContext = nsnull;
@ -108,7 +108,7 @@ nsIMEStateManager::OnRemoveContent(nsPresContext* aPresContext,
if (NS_FAILED(rv)) if (NS_FAILED(rv))
widget->ResetInputState(); widget->ResetInputState();
PRUint32 newState = GetNewIMEState(sPresContext, nsnull); PRUint32 newState = GetNewIMEState(sPresContext, nsnull);
SetIMEState(sPresContext, newState, widget); SetIMEState(newState, widget);
} }
sContent = nsnull; sContent = nsnull;
@ -162,7 +162,7 @@ nsIMEStateManager::OnChangeFocus(nsPresContext* aPresContext,
if (newState != nsIContent::IME_STATUS_NONE) { if (newState != nsIContent::IME_STATUS_NONE) {
// Update IME state for new focus widget // Update IME state for new focus widget
SetIMEState(aPresContext, newState, widget); SetIMEState(newState, widget);
} }
sPresContext = aPresContext; sPresContext = aPresContext;
@ -178,6 +178,26 @@ nsIMEStateManager::OnInstalledMenuKeyboardListener(PRBool aInstalling)
OnChangeFocus(sPresContext, sContent); OnChangeFocus(sPresContext, sContent);
} }
void
nsIMEStateManager::ChangeIMEStateTo(PRUint32 aNewIMEState)
{
if (!sPresContext) {
NS_WARNING("ISM doesn't know which editor has focus");
return;
}
NS_PRECONDITION(aNewIMEState != 0, "aNewIMEState doesn't specify new state.");
nsCOMPtr<nsIWidget> widget = GetWidget(sPresContext);
if (!widget) {
NS_WARNING("focused widget is not found");
return;
}
// commit current composition
widget->ResetInputState();
SetIMEState(aNewIMEState, widget);
}
PRUint32 PRUint32
nsIMEStateManager::GetNewIMEState(nsPresContext* aPresContext, nsIMEStateManager::GetNewIMEState(nsPresContext* aPresContext,
nsIContent* aContent) nsIContent* aContent)
@ -204,18 +224,17 @@ nsIMEStateManager::GetNewIMEState(nsPresContext* aPresContext,
} }
void void
nsIMEStateManager::SetIMEState(nsPresContext* aPresContext, nsIMEStateManager::SetIMEState(PRUint32 aState,
PRUint32 aState, nsIWidget* aWidget)
nsIWidget* aKB)
{ {
if (aState & nsIContent::IME_STATUS_MASK_ENABLED) { if (aState & nsIContent::IME_STATUS_MASK_ENABLED) {
PRUint32 state = PRUint32 state =
nsContentUtils::GetWidgetStatusFromIMEStatus(aState); nsContentUtils::GetWidgetStatusFromIMEStatus(aState);
aKB->SetIMEEnabled(state); aWidget->SetIMEEnabled(state);
} }
if (aState & nsIContent::IME_STATUS_MASK_OPENED) { if (aState & nsIContent::IME_STATUS_MASK_OPENED) {
PRBool open = !!(aState & nsIContent::IME_STATUS_OPEN); PRBool open = !!(aState & nsIContent::IME_STATUS_OPEN);
aKB->SetIMEOpenState(open); aWidget->SetIMEOpenState(open);
} }
} }

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

@ -80,10 +80,11 @@ public:
// Get the focused editor's selection and root // Get the focused editor's selection and root
static nsresult GetFocusSelectionAndRoot(nsISelection** aSel, static nsresult GetFocusSelectionAndRoot(nsISelection** aSel,
nsIContent** aRoot); nsIContent** aRoot);
// This method changes the current IME state forcedly.
// So, the caller should check whether you're focused or not.
static void ChangeIMEStateTo(PRUint32 aNewIMEState);
protected: protected:
static void SetIMEState(nsPresContext* aPresContext, static void SetIMEState(PRUint32 aState, nsIWidget* aWidget);
PRUint32 aState,
nsIWidget* aKB);
static PRUint32 GetNewIMEState(nsPresContext* aPresContext, static PRUint32 GetNewIMEState(nsPresContext* aPresContext,
nsIContent* aContent); nsIContent* aContent);

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

@ -91,4 +91,5 @@ include $(topsrcdir)/config/rules.mk
INCLUDES += \ INCLUDES += \
-I$(topsrcdir)/content/base/src \ -I$(topsrcdir)/content/base/src \
-I$(topsrcdir)/content/events/src \
$(NULL) $(NULL)

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

@ -45,6 +45,8 @@
#include "nsIDOMHTMLElement.h" #include "nsIDOMHTMLElement.h"
#include "nsIDOMNSHTMLElement.h" #include "nsIDOMNSHTMLElement.h"
#include "nsPIDOMEventTarget.h" #include "nsPIDOMEventTarget.h"
#include "nsIMEStateManager.h"
#include "nsFocusManager.h"
#include "nsIPrefBranch.h" #include "nsIPrefBranch.h"
#include "nsIPrefService.h" #include "nsIPrefService.h"
#include "nsUnicharUtils.h" #include "nsUnicharUtils.h"
@ -219,13 +221,17 @@ nsEditor::Init(nsIDOMDocument *aDoc, nsIPresShell* aPresShell, nsIContent *aRoot
if ((nsnull==aDoc) || (nsnull==aPresShell)) if ((nsnull==aDoc) || (nsnull==aPresShell))
return NS_ERROR_NULL_POINTER; return NS_ERROR_NULL_POINTER;
// First only set flags, but other stuff shouldn't be initialized now.
// Don't move this call after initializing mDocWeak and mPresShellWeak.
// SetFlags() can check whether it's called during initialization or not by
// them. Note that SetFlags() will be called by PostCreate().
nsresult rv = SetFlags(aFlags);
NS_ASSERTION(NS_SUCCEEDED(rv), "SetFlags() failed");
mDocWeak = do_GetWeakReference(aDoc); // weak reference to doc mDocWeak = do_GetWeakReference(aDoc); // weak reference to doc
mPresShellWeak = do_GetWeakReference(aPresShell); // weak reference to pres shell mPresShellWeak = do_GetWeakReference(aPresShell); // weak reference to pres shell
mSelConWeak = do_GetWeakReference(aSelCon); // weak reference to selectioncontroller mSelConWeak = do_GetWeakReference(aSelCon); // weak reference to selectioncontroller
nsresult rv = SetFlags(aFlags);
NS_ASSERTION(NS_SUCCEEDED(rv), "SetFlags() failed");
nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak); nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
if (!ps) return NS_ERROR_NOT_INITIALIZED; if (!ps) return NS_ERROR_NOT_INITIALIZED;
@ -279,8 +285,8 @@ nsEditor::Init(nsIDOMDocument *aDoc, nsIPresShell* aPresShell, nsIContent *aRoot
NS_IMETHODIMP NS_IMETHODIMP
nsEditor::PostCreate() nsEditor::PostCreate()
{ {
// Set up spellchecking // Synchronize some stuff for the flags
nsresult rv = SyncRealTimeSpell(); nsresult rv = SetFlags(mFlags);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
// Set up listeners // Set up listeners
@ -437,8 +443,29 @@ nsEditor::SetFlags(PRUint32 aFlags)
{ {
mFlags = aFlags; mFlags = aFlags;
if (!mDocWeak || !mPresShellWeak) {
// If we're initializing, we shouldn't do anything now.
// SetFlags() will be called by PostCreate(),
// we should synchronize some stuff for the flags at that time.
return NS_OK;
}
// Changing the flags can change whether spellchecking is on, so re-sync it // Changing the flags can change whether spellchecking is on, so re-sync it
SyncRealTimeSpell(); nsresult rv = SyncRealTimeSpell();
NS_ENSURE_SUCCESS(rv, rv);
// Might be changing editable state, so, we need to reset current IME state
// if we're focused.
if (HasFocus()) {
// Use "enable" for the default value because if IME is disabled
// unexpectedly, it makes serious a11y problem.
PRUint32 newState = nsIContent::IME_STATUS_ENABLE;
rv = GetPreferredIMEState(&newState);
if (NS_SUCCEEDED(rv)) {
nsIMEStateManager::ChangeIMEStateTo(newState);
}
}
return NS_OK; return NS_OK;
} }
@ -5155,3 +5182,18 @@ nsEditor::IsModifiableNode(nsIDOMNode *aNode)
{ {
return PR_TRUE; return PR_TRUE;
} }
PRBool
nsEditor::HasFocus()
{
nsCOMPtr<nsPIDOMEventTarget> piTarget = GetPIDOMEventTarget();
if (!piTarget) {
return PR_FALSE;
}
nsFocusManager* fm = nsFocusManager::GetFocusManager();
NS_ENSURE_TRUE(fm, PR_FALSE);
nsCOMPtr<nsIContent> content = fm->GetFocusedContent();
return SameCOMIdentity(content, piTarget);
}

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

@ -647,6 +647,9 @@ public:
return (mFlags & nsIPlaintextEditor::eEditorDontEchoPassword) != 0; return (mFlags & nsIPlaintextEditor::eEditorDontEchoPassword) != 0;
} }
// Whether the editor has focus or not.
virtual PRBool HasFocus();
protected: protected:
PRUint32 mModCount; // number of modifications (for undo/redo stack) PRUint32 mModCount; // number of modifications (for undo/redo stack)

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

@ -917,6 +917,9 @@ FindSelectionRoot(nsEditor *aEditor, nsIContent *aContent)
return root; return root;
} }
// XXX If the editor is HTML editor and has readonly flag, shouldn't return
// the element which has contenteditable="true"? However, such case isn't
// there without chrome permission script.
if (aEditor->IsReadonly()) { if (aEditor->IsReadonly()) {
// We still want to allow selection in a readonly editor. // We still want to allow selection in a readonly editor.
nsCOMPtr<nsIDOMElement> rootElement; nsCOMPtr<nsIDOMElement> rootElement;
@ -936,6 +939,11 @@ FindSelectionRoot(nsEditor *aEditor, nsIContent *aContent)
// For non-readonly editors we want to find the root of the editable subtree // For non-readonly editors we want to find the root of the editable subtree
// containing aContent. // containing aContent.
// XXX This is wrong in meaning of this method if the editor is form control.
// The editable form controls are also have NODE_IS_EDITABLE flag but it can
// be in contenteditable elements. So, at this time, this climbs up to the
// root editable element. But fortunately, we don't have any problem by
// another issue, see the XXX comment in focus event handler.
nsIContent *parent, *content = aContent; nsIContent *parent, *content = aContent;
while ((parent = content->GetParent()) && parent->HasFlag(NODE_IS_EDITABLE)) { while ((parent = content->GetParent()) && parent->HasFlag(NODE_IS_EDITABLE)) {
content = parent; content = parent;
@ -965,6 +973,12 @@ nsEditorEventListener::Focus(nsIDOMEvent* aEvent)
PRBool targetIsEditableDoc = PR_FALSE; PRBool targetIsEditableDoc = PR_FALSE;
nsCOMPtr<nsIContent> editableRoot; nsCOMPtr<nsIContent> editableRoot;
if (content) { if (content) {
// XXX If the focus event target is a form control in contenteditable
// element, perhaps, the parent HTML editor should do nothing by this
// handler. However, FindSelectionRoot() returns the root element of the
// contenteditable editor. So, the editableRoot value is invalid for
// the plain text editor, and it will be set to the wrong limiter of
// the selection. However, fortunately, actual bugs are not found yet.
editableRoot = FindSelectionRoot(mEditor, content); editableRoot = FindSelectionRoot(mEditor, content);
// make sure that the element is really focused in case an earlier // make sure that the element is really focused in case an earlier

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

@ -97,6 +97,8 @@
#include "SetDocTitleTxn.h" #include "SetDocTitleTxn.h"
#include "nsGUIEvent.h" #include "nsGUIEvent.h"
#include "nsTextFragment.h" #include "nsTextFragment.h"
#include "nsFocusManager.h"
#include "nsPIDOMWindow.h"
// netwerk // netwerk
#include "nsIURI.h" #include "nsIURI.h"
@ -5632,3 +5634,78 @@ nsHTMLEditor::GetReturnInParagraphCreatesNewParagraph(PRBool *aCreatesNewParagra
*aCreatesNewParagraph = mCRInParagraphCreatesParagraph; *aCreatesNewParagraph = mCRInParagraphCreatesParagraph;
return NS_OK; return NS_OK;
} }
PRBool
nsHTMLEditor::HasFocus()
{
NS_ENSURE_TRUE(mDocWeak, PR_FALSE);
nsFocusManager* fm = nsFocusManager::GetFocusManager();
NS_ENSURE_TRUE(fm, PR_FALSE);
nsCOMPtr<nsIContent> focusedContent = fm->GetFocusedContent();
nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak);
PRBool inDesignMode = doc->HasFlag(NODE_IS_EDITABLE);
if (!focusedContent) {
// in designMode, nobody gets focus in most cases.
return inDesignMode ? OurWindowHasFocus() : PR_FALSE;
}
if (inDesignMode) {
return OurWindowHasFocus() ?
nsContentUtils::ContentIsDescendantOf(focusedContent, doc) : PR_FALSE;
}
// We're HTML editor for contenteditable
// 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)) {
return PR_FALSE;
}
nsCOMPtr<nsIContent> rootContent = do_QueryInterface(GetRoot());
if (!rootContent) {
return PR_FALSE;
}
// If the focused content is a descendant of our editor root, we're focused.
return nsContentUtils::ContentIsDescendantOf(focusedContent, rootContent);
}
PRBool
nsHTMLEditor::OurWindowHasFocus()
{
NS_ENSURE_TRUE(mDocWeak, PR_FALSE);
nsIFocusManager* fm = nsFocusManager::GetFocusManager();
NS_ENSURE_TRUE(fm, PR_FALSE);
nsCOMPtr<nsIDOMWindow> focusedWindow;
fm->GetFocusedWindow(getter_AddRefs(focusedWindow));
if (!focusedWindow) {
return PR_FALSE;
}
nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak);
nsCOMPtr<nsIDOMWindow> ourWindow = do_QueryInterface(doc->GetWindow());
return ourWindow == focusedWindow;
}
PRBool
nsHTMLEditor::IsIndependentSelectionContent(nsIContent* aContent)
{
NS_PRECONDITION(aContent, "aContent must not be null");
nsIFrame* frame = aContent->GetPrimaryFrame();
return (frame && (frame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION));
}
NS_IMETHODIMP
nsHTMLEditor::GetPreferredIMEState(PRUint32 *aState)
{
if (IsReadonly() || IsDisabled()) {
*aState = nsIContent::IME_STATUS_DISABLE;
return NS_OK;
}
// HTML editor don't prefer the CSS ime-mode because IE didn't do so too.
*aState = nsIContent::IME_STATUS_ENABLE;
return NS_OK;
}

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

@ -146,6 +146,10 @@ public:
NS_IMETHODIMP HandleKeyPress(nsIDOMKeyEvent* aKeyEvent); NS_IMETHODIMP HandleKeyPress(nsIDOMKeyEvent* aKeyEvent);
NS_IMETHOD GetIsDocumentEditable(PRBool *aIsDocumentEditable); NS_IMETHOD GetIsDocumentEditable(PRBool *aIsDocumentEditable);
NS_IMETHODIMP BeginningOfDocument(); NS_IMETHODIMP BeginningOfDocument();
virtual PRBool HasFocus();
/* ------------ nsIEditorIMESupport overrides ------------ */
NS_IMETHOD GetPreferredIMEState(PRUint32 *aState);
/* ------------ nsIHTMLEditor methods -------------- */ /* ------------ nsIHTMLEditor methods -------------- */
@ -725,6 +729,12 @@ protected:
nsresult HasStyleOrIdOrClass(nsIDOMElement * aElement, PRBool *aHasStyleOrIdOrClass); nsresult HasStyleOrIdOrClass(nsIDOMElement * aElement, PRBool *aHasStyleOrIdOrClass);
nsresult RemoveElementIfNoStyleOrIdOrClass(nsIDOMElement * aElement, nsIAtom * aTag); nsresult RemoveElementIfNoStyleOrIdOrClass(nsIDOMElement * aElement, nsIAtom * aTag);
// 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 // Data members
protected: protected:

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

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

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

@ -0,0 +1,216 @@
<html>
<head>
<title>Test for contenteditable focus</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>
<link rel="stylesheet" type="text/css"
href="chrome://mochikit/content/tests/SimpleTest/test.css" />
</head>
<body>
<div id="display">
First text in this document.<br>
<input id="inputText" type="text"><br>
<input id="inputTextReadonly" type="text" readonly><br>
<input id="inputButton" type="button" value="input[type=button]"><br>
<button id="button">button</button><br>
<div id="editor" contenteditable="true">
editable contents.<br>
<input id="inputTextInEditor" type="text"><br>
<input id="inputTextReadonlyInEditor" type="text" readonly><br>
<input id="inputButtonInEditor" type="button" value="input[type=button]"><br>
<button id="buttonInEditor">button</button><br>
<div id="noeditableInEditor" contenteditable="false">
<span id="spanInNoneditableInEditor">span element in noneditable in editor</span><br>
<input id="inputTextInNoneditableInEditor" type="text"><br>
<input id="inputTextReadonlyInNoneditableInEditor" type="text" readonly><br>
<input id="inputButtonInNoneditableInEditor" type="button" value="input[type=button]"><br>
<button id="buttonInNoneditableInEditor">button</button><br>
</div>
<span id="spanInEditor">span element in editor</span><br>
</div>
<div id="otherEditor" contenteditable="true">
other editor.
</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);
function runTests()
{
try {
runTestsInternal();
} catch (e) {
ok(false, "Unexpected error happened: " + e);
}
SimpleTest.finish();
}
function runTestsInternal()
{
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
var fm = Components.classes["@mozilla.org/focus-manager;1"].
getService(Components.interfaces.nsIFocusManager);
// XXX using selCon for checking the visibility of the caret, however,
// selCon is shared in document, cannot get the element of owner of the
// caret from javascript?
var selCon =
window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
getInterface(Components.interfaces.nsIWebNavigation).
QueryInterface(Components.interfaces.nsIInterfaceRequestor).
getInterface(Components.interfaces.nsISelectionDisplay).
QueryInterface(Components.interfaces.nsISelectionController);
var selection = window.getSelection();
var inputText = document.getElementById("inputText");
var inputTextReadonly = document.getElementById("inputTextReadonly");
var inputButton = document.getElementById("inputButton");
var button = document.getElementById("button");
var editor = document.getElementById("editor");
var inputTextInEditor = document.getElementById("inputTextInEditor");
var inputTextReadonlyInEditor = document.getElementById("inputTextReadonlyInEditor");
var inputButtonInEditor = document.getElementById("inputButtonInEditor");
var noeditableInEditor = document.getElementById("noeditableInEditor");
var spanInNoneditableInEditor = document.getElementById("spanInNoneditableInEditor");
var inputTextInNoneditableInEditor = document.getElementById("inputTextInNoneditableInEditor");
var inputTextReadonlyInNoneditableInEditor = document.getElementById("inputTextReadonlyInNoneditableInEditor");
var inputButtonInNoneditableInEditor = document.getElementById("inputButtonInNoneditableInEditor");
var buttonInNoneditableInEditor = document.getElementById("buttonInNoneditableInEditor");
var spanInEditor = document.getElementById("spanInEditor");
var otherEditor = document.getElementById("otherEditor");
// XXX if there is a contenteditable element, HTML editor sets dom selection
// to first editable node, but this makes inconsistency with normal document
// behavior.
todo_is(selection.rangeCount, 0, "unexpected selection range is there");
ok(!selCon.caretVisible, "caret is visible in the document");
// Move focus to inputTextInEditor
inputTextInEditor.focus();
is(fm.focusedElement, inputTextInEditor,
"inputTextInEditor didn't get focus");
todo_is(selection.rangeCount, 0, "unexpected selection range is there");
ok(selCon.caretVisible, "caret isn't visible in the inputTextInEditor");
// Move focus to the editor
editor.focus();
is(fm.focusedElement, editor,
"editor didn't get focus");
is(selection.rangeCount, 1,
"there is no selection range when editor has focus");
var range = selection.getRangeAt(0);
ok(range.collapsed, "the selection range isn't collapsed");
var startNode = range.startContainer;
is(startNode.nodeType, 1, "the caret isn't set to the div node");
is(startNode, editor, "the caret isn't set to the editor");
ok(selCon.caretVisible, "caret isn't visible in the editor");
// Move focus to other editor
otherEditor.focus();
is(fm.focusedElement, otherEditor,
"the other editor didn't get focus");
is(selection.rangeCount, 1,
"there is no selection range when the other editor has focus");
range = selection.getRangeAt(0);
ok(range.collapsed, "the selection range isn't collapsed");
var startNode = range.startContainer;
is(startNode.nodeType, 1, "the caret isn't set to the div node");
is(startNode, otherEditor, "the caret isn't set to the other editor");
ok(selCon.caretVisible, "caret isn't visible in the other editor");
// Move focus to inputTextInEditor
inputTextInEditor.focus();
is(fm.focusedElement, inputTextInEditor,
"inputTextInEditor didn't get focus #2");
is(selection.rangeCount, 1, "selection range is lost from the document");
range = selection.getRangeAt(0);
ok(range.collapsed, "the selection range isn't collapsed");
var startNode = range.startContainer;
is(startNode.nodeType, 1, "the caret isn't set to the div node");
// XXX maybe, the caret can stay on the other editor if it's better.
is(startNode, editor,
"the caret should stay on the other editor");
ok(selCon.caretVisible,
"caret isn't visible in the inputTextInEditor");
// Move focus to the other editor again
otherEditor.focus();
is(fm.focusedElement, otherEditor,
"the other editor didn't get focus #2");
// Set selection to the span element in the editor (unfocused)
range = document.createRange();
range.setStart(spanInEditor.firstChild, 5);
selection.removeAllRanges();
selection.addRange(range);
is(selection.rangeCount, 1, "selection range is lost from the document");
is(fm.focusedElement, otherEditor,
"the other editor shouldn't lose focus by selection range change");
ok(selCon.caretVisible, "caret isn't visible in inputTextInEditor");
// Move focus to the editor
editor.focus();
is(fm.focusedElement, editor,
"the editor didn't get focus #2");
is(selection.rangeCount, 1, "selection range is lost from the document");
range = selection.getRangeAt(0);
ok(range.collapsed, "the selection range isn't collapsed");
is(range.startOffset, 5,
"the caret is moved when the editor was focused (offset)");
var startNode = range.startContainer;
is(startNode.nodeType, 3, "the caret isn't in text node");
is(startNode.parentNode, spanInEditor,
"the caret is moved when the editor was focused (node)");
ok(selCon.caretVisible, "caret isn't visible in the editor (spanInEditor)");
// Move focus to each focusable element in the editor.
function testFocusMove(aSetFocusElementID, aFocusable, aCaretVisible)
{
editor.focus();
is(fm.focusedElement, editor,
"testFocusMove: the editor didn't get focus at initializing (" +
aSetFocusElementID + ")");
var setFocusElement = document.getElementById(aSetFocusElementID);
setFocusElement.focus();
if (aFocusable) {
is(fm.focusedElement, setFocusElement,
"testFocusMove: the " + aSetFocusElementID +
" didn't get focus");
} else {
is(fm.focusedElement, editor,
"testFocusMove: the editor lost focus by focus() of the " +
aSetFocusElementID);
}
if (aCaretVisible) {
ok(selCon.caretVisible,
"testFocusMove: caret isn't visible when the " +
aSetFocusElementID + " has focus");
} else {
ok(!selCon.caretVisible,
"testFocusMove: caret is visible when the " +
aSetFocusElementID + " has focus");
}
}
testFocusMove("inputTextInEditor", true, true);
testFocusMove("inputTextReadonlyInEditor", true, true);
// XXX shouldn't the caret become invisible?
testFocusMove("inputButtonInEditor", true, true);
testFocusMove("noeditableInEditor", false, true);
testFocusMove("spanInNoneditableInEditor", false, true);
testFocusMove("inputTextInNoneditableInEditor", true, true);
testFocusMove("inputTextReadonlyInNoneditableInEditor", true, true);
testFocusMove("inputButtonInNoneditableInEditor", true, false);
testFocusMove("buttonInNoneditableInEditor", true, false);
testFocusMove("spanInEditor", false, true);
testFocusMove("inputText", true, true);
testFocusMove("inputTextReadonly", true, true);
testFocusMove("inputButton", true, false);
testFocusMove("button", true, false);
}
</script>
</body>
</html>

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

@ -1,4 +1,4 @@
<html> <html style="ime-mode: disabled;">
<head> <head>
<title>Test for IME state controling</title> <title>Test for IME state controling</title>
<script type="text/javascript" <script type="text/javascript"
@ -8,8 +8,8 @@
<link rel="stylesheet" type="text/css" <link rel="stylesheet" type="text/css"
href="chrome://mochikit/content/tests/SimpleTest/test.css" /> href="chrome://mochikit/content/tests/SimpleTest/test.css" />
</head> </head>
<body onload="setTimeout(runTests, 0);"> <body onload="setTimeout(runTests, 0);" style="ime-mode: disabled;">
<p id="display"> <div id="display" style="ime-mode: disabled;">
<!-- input elements --> <!-- input elements -->
<input type="text" id="text"/><br/> <input type="text" id="text"/><br/>
<input type="text" id="text_readonly" readonly="readonly"/><br/> <input type="text" id="text_readonly" readonly="readonly"/><br/>
@ -36,7 +36,7 @@
<isindex id="isindex" prompt="isindex"/><br/> <isindex id="isindex" prompt="isindex"/><br/>
<!-- a element --> <!-- a element -->
<a id="a_href" href="">a[href]</a><br/> <a id="a_href" href="about:blank">a[href]</a><br/>
<!-- ime-mode test --> <!-- ime-mode test -->
<input type="text" id="ime_mode_auto" style="ime-mode: auto;"/><br/> <input type="text" id="ime_mode_auto" style="ime-mode: auto;"/><br/>
@ -57,7 +57,15 @@
<!-- plugin --> <!-- plugin -->
<object type="application/x-test" id="plugin"></object><br/> <object type="application/x-test" id="plugin"></object><br/>
</p>
<!-- contenteditable editor -->
<div id="contenteditableEditor" contenteditable="true"></div>
<!-- designMode editor -->
<iframe id="designModeEditor"
onload="document.getElementById('designModeEditor').contentDocument.designMode = 'on';"
src="data:text/html,<html><body></body></html>"></iframe><br/>
</div>
<div id="content" style="display: none"> <div id="content" style="display: none">
</div> </div>
@ -71,6 +79,8 @@ SimpleTest.waitForExplicitFinish();
var gUtils = window. var gUtils = window.
QueryInterface(Components.interfaces.nsIInterfaceRequestor). QueryInterface(Components.interfaces.nsIInterfaceRequestor).
getInterface(Components.interfaces.nsIDOMWindowUtils); getInterface(Components.interfaces.nsIDOMWindowUtils);
var gFM = Components.classes["@mozilla.org/focus-manager;1"].
getService(Components.interfaces.nsIFocusManager);
const kIMEEnabledSupported = navigator.platform.indexOf("Mac") == 0 || const kIMEEnabledSupported = navigator.platform.indexOf("Mac") == 0 ||
navigator.platform.indexOf("Win") == 0 || navigator.platform.indexOf("Win") == 0 ||
navigator.platform.indexOf("Linux") == 0; navigator.platform.indexOf("Linux") == 0;
@ -83,29 +93,51 @@ const kIMEOpenSupported = false;
function runBasicTest(aIsEditable, aInDesignMode, aDescription) function runBasicTest(aIsEditable, aInDesignMode, aDescription)
{ {
var defaultEnabledState =
aIsEditable ? gUtils.IME_STATUS_ENABLED : gUtils.IME_STATUS_DISABLED;
var nonFormControlNodeEnabledState =
aInDesignMode ? gUtils.IME_STATUS_ENABLED : gUtils.IME_STATUS_DISABLED;
function test(aTest) function test(aTest)
{ {
function moveFocus(aTest) function moveFocus(aTest)
{ {
if (aTest.expectedEnabled == gUtils.IME_STATUS_DISABLED) { if (aInDesignMode) {
document.getElementById("text").focus(); if (document.activeElement) {
document.activeElement.blur();
}
} else if (aIsEditable) {
document.getElementById("display").focus();
} else if (aTest.expectedEnabled == gUtils.IME_STATUS_ENABLED) {
document.getElementById("password").focus();
} else { } else {
document.activeElement.blur(); document.getElementById("text").focus();
} }
document.getElementById(aTest.id).focus(); var previousFocusedElement = gFM.focusedElement;
var element = document.getElementById(aTest.id);
if (element.contentDocument) {
element = element.contentDocument.documentElement;
}
element.focus();
var focusedElement = gFM.focusedElement;
if (focusedElement) {
var bindingParent = document.getBindingParent(focusedElement);
if (bindingParent) {
focusedElement = bindingParent;
}
}
if (aTest.focusable) {
is(focusedElement, element,
aDescription + ": " + aTest.description + ", focus didn't move");
return (element == focusedElement);
}
is(focusedElement, previousFocusedElement,
aDescription + ": " + aTest.description + ", focus moved as unexpected");
return (previousFocusedElement == focusedElement);
} }
function testOpened(aTest, aOpened) function testOpened(aTest, aOpened)
{ {
document.getElementById("text").focus(); document.getElementById("text").focus();
gUtils.IMEIsOpen = aOpened; gUtils.IMEIsOpen = aOpened;
moveFocus(aTest); if (!moveFocus(aTest)) {
return;
}
var message = aDescription + ": " + aTest.description + var message = aDescription + ": " + aTest.description +
", wrong opened state"; ", wrong opened state";
is(gUtils.IMEIsOpen, is(gUtils.IMEIsOpen,
@ -115,12 +147,11 @@ function runBasicTest(aIsEditable, aInDesignMode, aDescription)
// IME Enabled state testing // IME Enabled state testing
var enabled = gUtils.IME_STATUS_ENABLED; var enabled = gUtils.IME_STATUS_ENABLED;
if (kIMEEnabledSupported) { if (kIMEEnabledSupported) {
moveFocus(aTest); if (!moveFocus(aTest)) {
return;
}
enabled = gUtils.IMEStatus; enabled = gUtils.IMEStatus;
// no focusing of html elements is possible in design mode so ime should is(enabled, aTest.expectedEnabled,
// always be enabled for the document
var expected = aInDesignMode ? gUtils.IME_STATUS_ENABLED : aTest.expectedEnabled;
is(enabled, expected,
aDescription + ": " + aTest.description + ", wrong enabled state"); aDescription + ": " + aTest.description + ", wrong enabled state");
} }
@ -135,120 +166,176 @@ function runBasicTest(aIsEditable, aInDesignMode, aDescription)
} }
if (kIMEEnabledSupported) { if (kIMEEnabledSupported) {
// make sure there is an active element
document.getElementById("text").focus();
document.activeElement.blur(); document.activeElement.blur();
is(gUtils.IMEStatus, nonFormControlNodeEnabledState, is(gUtils.IMEStatus,
aInDesignMode ? gUtils.IME_STATUS_ENABLED : gUtils.IME_STATUS_DISABLED,
aDescription + ": unexpected enabled state when no element has focus"); aDescription + ": unexpected enabled state when no element has focus");
} }
// Form controls except text editable elements are "disable" in normal
// condition, however, if they are editable, they are "enabled".
// XXX Probably there are some bugs: If the form controls editable, they
// shouldn't be focusable.
const kEnabledStateOnNonEditableElement =
(aInDesignMode || aIsEditable) ? gUtils.IME_STATUS_ENABLED :
gUtils.IME_STATUS_DISABLED;
const kEnabledStateOnPasswordField =
aInDesignMode ? gUtils.IME_STATUS_ENABLED : gUtils.IME_STATUS_PASSWORD;
const kEnabledStateOnReadonlyField =
aInDesignMode ? gUtils.IME_STATUS_ENABLED : gUtils.IME_STATUS_DISABLED;
const kTests = [ const kTests = [
{ id: "text", { id: "text",
description: "input[type=text]", description: "input[type=text]",
focusable: !aInDesignMode,
expectedEnabled: gUtils.IME_STATUS_ENABLED }, expectedEnabled: gUtils.IME_STATUS_ENABLED },
{ id: "text_readonly", { id: "text_readonly",
description: "input[type=text][readonly]", description: "input[type=text][readonly]",
expectedEnabled: gUtils.IME_STATUS_DISABLED }, focusable: !aInDesignMode,
expectedEnabled: kEnabledStateOnReadonlyField },
{ id: "password", { id: "password",
description: "input[type=password]", description: "input[type=password]",
expectedEnabled: gUtils.IME_STATUS_PASSWORD }, focusable: !aInDesignMode,
expectedEnabled: kEnabledStateOnPasswordField },
{ id: "password_readonly", { id: "password_readonly",
description: "input[type=password][readonly]", description: "input[type=password][readonly]",
expectedEnabled: gUtils.IME_STATUS_DISABLED }, focusable: !aInDesignMode,
expectedEnabled: kEnabledStateOnReadonlyField },
{ id: "checkbox", { id: "checkbox",
description: "input[type=checkbox]", description: "input[type=checkbox]",
expectedEnabled: defaultEnabledState }, focusable: !aInDesignMode,
expectedEnabled: kEnabledStateOnNonEditableElement },
{ id: "radio", { id: "radio",
description: "input[type=radio]", description: "input[type=radio]",
expectedEnabled: defaultEnabledState }, focusable: !aInDesignMode,
expectedEnabled: kEnabledStateOnNonEditableElement },
{ id: "submit", { id: "submit",
description: "input[type=submit]", description: "input[type=submit]",
expectedEnabled: defaultEnabledState }, focusable: !aInDesignMode,
expectedEnabled: kEnabledStateOnNonEditableElement },
{ id: "reset", { id: "reset",
description: "input[type=reset]", description: "input[type=reset]",
expectedEnabled: defaultEnabledState }, focusable: !aInDesignMode,
expectedEnabled: kEnabledStateOnNonEditableElement },
{ id: "file", { id: "file",
description: "input[type=file]", description: "input[type=file]",
expectedEnabled: defaultEnabledState }, focusable: !aInDesignMode,
expectedEnabled: kEnabledStateOnNonEditableElement },
{ id: "button", { id: "button",
description: "input[type=button]", description: "input[type=button]",
expectedEnabled: defaultEnabledState }, focusable: !aInDesignMode,
expectedEnabled: kEnabledStateOnNonEditableElement },
{ id: "image", { id: "image",
description: "input[type=image]", description: "input[type=image]",
expectedEnabled: defaultEnabledState }, focusable: !aInDesignMode,
expectedEnabled: kEnabledStateOnNonEditableElement },
// form controls // form controls
{ id: "button", { id: "button",
description: "button", description: "button",
expectedEnabled: defaultEnabledState }, focusable: !aInDesignMode,
expectedEnabled: kEnabledStateOnNonEditableElement },
{ id: "textarea", { id: "textarea",
description: "textarea", description: "textarea",
focusable: !aInDesignMode,
expectedEnabled: gUtils.IME_STATUS_ENABLED }, expectedEnabled: gUtils.IME_STATUS_ENABLED },
{ id: "textarea_readonly", { id: "textarea_readonly",
description: "textarea[readonly]", description: "textarea[readonly]",
expectedEnabled: gUtils.IME_STATUS_DISABLED }, focusable: !aInDesignMode,
expectedEnabled: kEnabledStateOnReadonlyField },
{ id: "select", { id: "select",
description: "select (dropdown list)", description: "select (dropdown list)",
expectedEnabled: defaultEnabledState }, focusable: !aInDesignMode,
expectedEnabled: kEnabledStateOnNonEditableElement },
{ id: "select_multiple", { id: "select_multiple",
description: "select (list box)", description: "select (list box)",
expectedEnabled: defaultEnabledState }, focusable: !aInDesignMode,
expectedEnabled: kEnabledStateOnNonEditableElement },
// a element // a element
{ id: "a_href", { id: "a_href",
description: "a[href]", description: "a[href]",
expectedEnabled: nonFormControlNodeEnabledState }, focusable: !aIsEditable && !aInDesignMode,
expectedEnabled: kEnabledStateOnNonEditableElement },
// ime-mode // ime-mode
{ id: "ime_mode_auto", { id: "ime_mode_auto",
description: "input[type=text][style=\"ime-mode: auto;\"]", description: "input[type=text][style=\"ime-mode: auto;\"]",
focusable: !aInDesignMode,
expectedEnabled: gUtils.IME_STATUS_ENABLED }, expectedEnabled: gUtils.IME_STATUS_ENABLED },
{ id: "ime_mode_normal", { id: "ime_mode_normal",
description: "input[type=text][style=\"ime-mode: normal;\"]", description: "input[type=text][style=\"ime-mode: normal;\"]",
focusable: !aInDesignMode,
expectedEnabled: gUtils.IME_STATUS_ENABLED }, expectedEnabled: gUtils.IME_STATUS_ENABLED },
{ id: "ime_mode_active", { id: "ime_mode_active",
description: "input[type=text][style=\"ime-mode: active;\"]", description: "input[type=text][style=\"ime-mode: active;\"]",
expectedEnabled: gUtils.IME_STATUS_ENABLED, expectedEnabled: gUtils.IME_STATUS_ENABLED,
focusable: !aInDesignMode,
changeOpened: true, expectedOpened: true }, changeOpened: true, expectedOpened: true },
{ id: "ime_mode_inactive", { id: "ime_mode_inactive",
description: "input[type=text][style=\"ime-mode: inactive;\"]", description: "input[type=text][style=\"ime-mode: inactive;\"]",
expectedEnabled: gUtils.IME_STATUS_ENABLED, expectedEnabled: gUtils.IME_STATUS_ENABLED,
focusable: !aInDesignMode,
changeOpened: true, expectedOpened: false }, changeOpened: true, expectedOpened: false },
{ id: "ime_mode_disabled", { id: "ime_mode_disabled",
description: "input[type=text][style=\"ime-mode: disabled;\"]", description: "input[type=text][style=\"ime-mode: disabled;\"]",
expectedEnabled: gUtils.IME_STATUS_PASSWORD }, focusable: !aInDesignMode,
expectedEnabled: kEnabledStateOnPasswordField },
{ id: "ime_mode_auto_p", { id: "ime_mode_auto_p",
description: "input[type=password][style=\"ime-mode: auto;\"]", description: "input[type=password][style=\"ime-mode: auto;\"]",
expectedEnabled: gUtils.IME_STATUS_PASSWORD }, focusable: !aInDesignMode,
expectedEnabled: kEnabledStateOnPasswordField },
{ id: "ime_mode_normal_p", { id: "ime_mode_normal_p",
description: "input[type=password][style=\"ime-mode: normal;\"]", description: "input[type=password][style=\"ime-mode: normal;\"]",
focusable: !aInDesignMode,
expectedEnabled: gUtils.IME_STATUS_ENABLED }, expectedEnabled: gUtils.IME_STATUS_ENABLED },
{ id: "ime_mode_active_p", { id: "ime_mode_active_p",
description: "input[type=password][style=\"ime-mode: active;\"]", description: "input[type=password][style=\"ime-mode: active;\"]",
expectedEnabled: gUtils.IME_STATUS_ENABLED, expectedEnabled: gUtils.IME_STATUS_ENABLED,
focusable: !aInDesignMode,
changeOpened: true, expectedOpened: true }, changeOpened: true, expectedOpened: true },
{ id: "ime_mode_inactive_p", { id: "ime_mode_inactive_p",
description: "input[type=password][style=\"ime-mode: inactive;\"]", description: "input[type=password][style=\"ime-mode: inactive;\"]",
expectedEnabled: gUtils.IME_STATUS_ENABLED, expectedEnabled: gUtils.IME_STATUS_ENABLED,
focusable: !aInDesignMode,
changeOpened: true, expectedOpened: false }, changeOpened: true, expectedOpened: false },
{ id: "ime_mode_disabled_p", { id: "ime_mode_disabled_p",
description: "input[type=password][style=\"ime-mode: disabled;\"]", description: "input[type=password][style=\"ime-mode: disabled;\"]",
expectedEnabled: gUtils.IME_STATUS_PASSWORD }, focusable: !aInDesignMode,
expectedEnabled: kEnabledStateOnPasswordField },
{ id: "ime_mode_auto", { id: "ime_mode_auto",
description: "textarea[style=\"ime-mode: auto;\"]", description: "textarea[style=\"ime-mode: auto;\"]",
focusable: !aInDesignMode,
expectedEnabled: gUtils.IME_STATUS_ENABLED }, expectedEnabled: gUtils.IME_STATUS_ENABLED },
{ id: "ime_mode_normal", { id: "ime_mode_normal",
description: "textarea[style=\"ime-mode: normal;\"]", description: "textarea[style=\"ime-mode: normal;\"]",
focusable: !aInDesignMode,
expectedEnabled: gUtils.IME_STATUS_ENABLED }, expectedEnabled: gUtils.IME_STATUS_ENABLED },
{ id: "ime_mode_active", { id: "ime_mode_active",
description: "textarea[style=\"ime-mode: active;\"]", description: "textarea[style=\"ime-mode: active;\"]",
focusable: !aInDesignMode,
expectedEnabled: gUtils.IME_STATUS_ENABLED, expectedEnabled: gUtils.IME_STATUS_ENABLED,
changeOpened: true, expectedOpened: true }, changeOpened: true, expectedOpened: true },
{ id: "ime_mode_inactive", { id: "ime_mode_inactive",
description: "textarea[style=\"ime-mode: inactive;\"]", description: "textarea[style=\"ime-mode: inactive;\"]",
focusable: !aInDesignMode,
expectedEnabled: gUtils.IME_STATUS_ENABLED, expectedEnabled: gUtils.IME_STATUS_ENABLED,
changeOpened: true, expectedOpened: false }, changeOpened: true, expectedOpened: false },
{ id: "ime_mode_disabled", { id: "ime_mode_disabled",
description: "textarea[style=\"ime-mode: disabled;\"]", description: "textarea[style=\"ime-mode: disabled;\"]",
expectedEnabled: gUtils.IME_STATUS_PASSWORD } focusable: !aInDesignMode,
expectedEnabled: kEnabledStateOnPasswordField },
// HTML editors
{ id: "contenteditableEditor",
description: "div[contenteditable=\"true\"]",
focusable: !aIsEditable && !aInDesignMode,
expectedEnabled: gUtils.IME_STATUS_ENABLED },
{ id: "designModeEditor",
description: "designMode editor",
focusable: true,
expectedEnabled: gUtils.IME_STATUS_ENABLED },
]; ];
for (var i = 0; i < kTests.length; i++) { for (var i = 0; i < kTests.length; i++) {
@ -351,7 +438,7 @@ function runTypeChangingTest()
type: "text", expected: gUtils.IME_STATUS_PASSWORD, imeMode: true, type: "text", expected: gUtils.IME_STATUS_PASSWORD, imeMode: true,
description: "[type=\"text\"][ime-mode: disabled;]" }, description: "[type=\"text\"][ime-mode: disabled;]" },
{ id: "ime_mode_auto_p", { id: "ime_mode_auto_p",
type: "password", expected: gUtils.IME_STATUS_ENABLED, imeMode: true, type: "password", expected: gUtils.IME_STATUS_PASSWORD, imeMode: true,
description: "[type=\"password\"][ime-mode: auto;]" }, description: "[type=\"password\"][ime-mode: auto;]" },
{ id: "ime_mode_normal_p", { id: "ime_mode_normal_p",
type: "password", expected: gUtils.IME_STATUS_ENABLED, imeMode: true, type: "password", expected: gUtils.IME_STATUS_ENABLED, imeMode: true,
@ -383,7 +470,7 @@ function runTypeChangingTest()
function getExpectedIMEEnabled(aNewType, aInputControl) function getExpectedIMEEnabled(aNewType, aInputControl)
{ {
if (aNewType.expected == gUtils.IME_STATUS_DISABLED || if (aNewType.expected == gUtils.IME_STATUS_DISABLED ||
aInputControl.isReadOnly) aInputControl.isReadonly)
return gUtils.IME_STATUS_DISABLED; return gUtils.IME_STATUS_DISABLED;
return aInputControl.imeMode ? aInputControl.expected : aNewType.expected; return aInputControl.imeMode ? aInputControl.expected : aNewType.expected;
} }
@ -477,6 +564,214 @@ function runReadonlyChangingTest()
} }
} }
function runComplexContenteditableTests()
{
if (!kIMEEnabledSupported) {
return;
}
var description = "runReadonlyChangingOnContenteditable: ";
var container = document.getElementById("display");
var button = document.getElementById("button");
// the editor has focus directly.
container.setAttribute("contenteditable", "true");
container.focus();
is(gFM.focusedElement, container,
description + "The editor doesn't get focus");
is(gUtils.IMEStatus, gUtils.IME_STATUS_ENABLED,
description + "IME isn't enabled on HTML editor");
const kReadonly =
Components.interfaces.nsIPlaintextEditor.eEditorReadonlyMask;
var editor =
window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
getInterface(Components.interfaces.nsIWebNavigation).
QueryInterface(Components.interfaces.nsIEditorDocShell).editor;
var flags = editor.flags;
editor.flags = flags | kReadonly;
is(gFM.focusedElement, container,
description + "The editor loses focus by flag change");
is(gUtils.IMEStatus, gUtils.IME_STATUS_DISABLED,
description + "IME is still enabled on readonly HTML editor");
editor.flags = flags;
is(gFM.focusedElement, container,
description + "The editor loses focus by flag change #2");
is(gUtils.IMEStatus, gUtils.IME_STATUS_ENABLED,
description + "IME is still disabled, the editor isn't readonly now");
container.removeAttribute("contenteditable");
todo_is(gFM.focusedElement, null,
description + "The container still has focus, the editor has been no editable");
todo_is(gUtils.IMEStatus, gUtils.IME_STATUS_DISABLED,
description + "IME is still enabled on the editor, the editor has been no editable");
// a button which is in the editor has focus
button.focus();
is(gFM.focusedElement, button,
description + "The button doesn't get focus");
is(gUtils.IMEStatus, gUtils.IME_STATUS_DISABLED,
description + "IME is enabled on the button");
container.setAttribute("contenteditable", "true");
is(gFM.focusedElement, button,
description + "The button loses focus, the container is editable now");
todo_is(gUtils.IMEStatus, gUtils.IME_STATUS_ENABLED,
description + "IME is still disabled on the button, the container is editable now");
editor =
window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
getInterface(Components.interfaces.nsIWebNavigation).
QueryInterface(Components.interfaces.nsIEditorDocShell).editor;
flags = editor.flags;
editor.flags = flags | kReadonly;
is(gFM.focusedElement, button,
description + "The button loses focus by changing editor flags");
is(gUtils.IMEStatus, gUtils.IME_STATUS_DISABLED,
description + "IME is still enabled on the button, the container is readonly now");
editor.flags = flags;
is(gFM.focusedElement, button,
description + "The button loses focus by changing editor flags #2");
is(gUtils.IMEStatus, gUtils.IME_STATUS_ENABLED,
description + "IME is still disabled on the button, the container isn't readonly now");
container.removeAttribute("contenteditable");
is(gFM.focusedElement, button,
description + "The button loses focus, the container has been no editable");
todo_is(gUtils.IMEStatus, gUtils.IME_STATUS_DISABLED,
description + "IME is still enabled on the button, the container has been no editable");
description = "testOnIndependentEditor: ";
function testOnIndependentEditor(aEditor, aEditorDescription)
{
var isReadonly = aEditor.readOnly;
var expectedState =
aEditor.readOnly ? gUtils.IME_STATUS_DISABLED : gUtils.IME_STATUS_ENABLED;
var unexpectedStateDescription =
expectedState != gUtils.IME_STATUS_ENABLED ? "enabled" : "disabled";
aEditor.focus();
is(gFM.focusedElement, aEditor,
description + "The " + aEditorDescription + " doesn't get focus");
is(gUtils.IMEStatus, expectedState,
description + "IME is " + unexpectedStateDescription +
" on the " + aEditorDescription);
container.setAttribute("contenteditable", "true");
is(gFM.focusedElement, aEditor,
description + "The " + aEditorDescription +
" loses focus, the container is editable now");
is(gUtils.IMEStatus, expectedState,
description + "IME becomes " + unexpectedStateDescription +
" on the " + aEditorDescription + ", the container is editable now");
editor =
window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
getInterface(Components.interfaces.nsIWebNavigation).
QueryInterface(Components.interfaces.nsIEditorDocShell).editor;
flags = editor.flags;
editor.flags = flags | kReadonly;
is(gFM.focusedElement, aEditor,
description + "The " + aEditorDescription +
" loses focus by changing editor flags");
is(gUtils.IMEStatus, expectedState,
description + "IME becomes " + unexpectedStateDescription + " on the " +
aEditorDescription + ", the container is readonly now");
editor.flags = flags;
is(gFM.focusedElement, aEditor,
description + "The " + aEditorDescription +
" loses focus by changing editor flags #2");
is(gUtils.IMEStatus, expectedState,
description + "IME becomes " + unexpectedStateDescription + " on the " +
aEditorDescription + ", the container isn't readonly now");
container.removeAttribute("contenteditable");
is(gFM.focusedElement, aEditor,
description + "The " + aEditorDescription +
" loses focus, the container has been no editable");
is(gUtils.IMEStatus, expectedState,
description + "IME becomes " + unexpectedStateDescription + " on the " +
aEditorDescription + ", the container has been no editable");
}
// a textarea which is in the editor has focus
testOnIndependentEditor(document.getElementById("textarea"),
"textarea");
// a readonly textarea which is in the editor has focus
testOnIndependentEditor(document.getElementById("textarea_readonly"),
"textarea[readonly]");
// an input field which is in the editor has focus
testOnIndependentEditor(document.getElementById("text"),
"input[type=\"text\"]");
// a readonly input field which is in the editor has focus
testOnIndependentEditor(document.getElementById("text_readonly"),
"input[type=\"text\"][readonly]");
description = "testOnOutsideOfEditor: ";
function testOnOutsideOfEditor(aFocusNode, aFocusNodeDescription, aEditor)
{
if (aFocusNode) {
aFocusNode.focus();
is(gFM.focusedElement, aFocusNode,
description + "The " + aFocusNodeDescription + " doesn't get focus");
} else {
if (document.activeElement) {
document.activeElement.blur();
}
is(gFM.focusedElement, null,
description + "Unexpected element has focus");
}
var expectedState =
aFocusNode ? gUtils.IMEStatus : gUtils.IME_STATUS_DISABLED;
var unexpectedStateDescription =
expectedState != gUtils.IME_STATUS_ENABLED ? "enabled" : "disabled";
aEditor.setAttribute("contenteditable", "true");
is(gFM.focusedElement, aFocusNode,
description + "The " + aFocusNodeDescription +
" loses focus, a HTML editor is editable now");
is(gUtils.IMEStatus, expectedState,
description + "IME becomes " + unexpectedStateDescription +
" on the " + aFocusNodeDescription +
", the HTML editor is editable now");
editor =
window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
getInterface(Components.interfaces.nsIWebNavigation).
QueryInterface(Components.interfaces.nsIEditorDocShell).editor;
flags = editor.flags;
editor.flags = flags | kReadonly;
is(gFM.focusedElement, aFocusNode,
description + aFocusNodeDescription +
" loses focus by changing HTML editor flags");
is(gUtils.IMEStatus, expectedState,
description + "IME becomes " + unexpectedStateDescription + " on " +
aFocusNodeDescription + ", the HTML editor is readonly now");
editor.flags = flags;
is(gFM.focusedElement, aFocusNode,
description + aFocusNodeDescription +
" loses focus by changing HTML editor flags #2");
is(gUtils.IMEStatus, expectedState,
description + "IME becomes " + unexpectedStateDescription + " on " +
aFocusNodeDescription + ", the HTML editor isn't readonly now");
container.removeAttribute("contenteditable");
is(gFM.focusedElement, aFocusNode,
description + aFocusNodeDescription +
" loses focus, the HTML editor has been no editable");
is(gUtils.IMEStatus, expectedState,
description + "IME becomes " + unexpectedStateDescription + " on " +
aFocusNodeDescription + ", the HTML editor has been no editable");
}
var div = document.getElementById("contenteditableEditor");
// a textarea which is outside of the editor has focus
testOnOutsideOfEditor(document.getElementById("textarea"), "textarea", div);
// a readonly textarea which is outside of the editor has focus
testOnOutsideOfEditor(document.getElementById("textarea_readonly"),
"textarea[readonly]", div);
// an input field which is outside of the editor has focus
testOnOutsideOfEditor(document.getElementById("text"),
"input[type=\"text\"]", div);
// a readonly input field which outside of the editor has focus
testOnOutsideOfEditor(document.getElementById("text_readonly"),
"input[type=\"text\"][readonly]", div);
// a readonly input field which outside of the editor has focus
testOnOutsideOfEditor(document.getElementById("button"), "button", div);
// nobody has focus.
testOnOutsideOfEditor(null, "nobody", div);
}
function runEditableSubframeTests() function runEditableSubframeTests()
{ {
@ -519,12 +814,14 @@ function runTests()
runBasicTest(false, false, "Testing designMode=\"off\""); runBasicTest(false, false, "Testing designMode=\"off\"");
// changing input[type] values // changing input[type] values
// XXX currently, type attribute changing doesn't work fine. bug 488420. // XXX currently, type attribute changing doesn't work fine. bug 559728.
// runTypeChangingTest(); // runTypeChangingTest();
// changing readonly attribute // changing readonly attribute
// XXX currently, readonly attribute changing doesn't work fine. bug 488420. runReadonlyChangingTest();
// runReadonlyChangingTest();
// complex contenteditable editor's tests
runComplexContenteditableTests();
runASyncTests(); runASyncTests();
} }