mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 22:01:30 +00:00
Bug 544277 IME became unusable when switching focus on Gmail RTF Editor r=enn
This commit is contained in:
parent
2bd8f8211d
commit
189d5d4756
@ -2749,9 +2749,33 @@ nsEventStateManager::PostHandleEvent(nsPresContext* aPresContext,
|
||||
activeContent = mCurrentTarget->GetContent();
|
||||
}
|
||||
|
||||
nsIFrame* currFrame = mCurrentTarget;
|
||||
|
||||
// When a root content which isn't editable but has an editable HTML
|
||||
// <body> element is clicked, we should redirect the focus to the
|
||||
// the <body> element. E.g., when an user click bottom of the editor
|
||||
// where is outside of the <body> element, the <body> should be focused
|
||||
// and the user can edit immediately after that.
|
||||
//
|
||||
// NOTE: The newFocus isn't editable that also means it's not in
|
||||
// designMode. In designMode, all contents are not focusable.
|
||||
if (newFocus && !newFocus->IsEditable()) {
|
||||
nsIDocument *doc = newFocus->GetCurrentDoc();
|
||||
if (doc && newFocus == doc->GetRootContent()) {
|
||||
nsIContent *bodyContent =
|
||||
nsLayoutUtils::GetEditableRootContentByContentEditable(doc);
|
||||
if (bodyContent) {
|
||||
nsIFrame* bodyFrame = bodyContent->GetPrimaryFrame();
|
||||
if (bodyFrame) {
|
||||
currFrame = bodyFrame;
|
||||
newFocus = bodyContent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// When the mouse is pressed, the default action is to focus the
|
||||
// target. Look for the nearest enclosing focusable frame.
|
||||
nsIFrame* currFrame = mCurrentTarget;
|
||||
while (currFrame) {
|
||||
// If the mousedown happened inside a popup, don't
|
||||
// try to set focus on one of its containing elements
|
||||
|
@ -1273,6 +1273,23 @@ nsFocusManager::IsWindowVisible(nsPIDOMWindow* aWindow)
|
||||
return visible;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsFocusManager::IsNonFocusableRoot(nsIContent* aContent)
|
||||
{
|
||||
NS_PRECONDITION(aContent, "aContent must not be NULL");
|
||||
NS_PRECONDITION(aContent->IsInDoc(), "aContent must be in a document");
|
||||
|
||||
// If aContent is in designMode, the root element is not focusable.
|
||||
// NOTE: in designMode, most elements are not focusable, just the document is
|
||||
// focusable.
|
||||
// Also, if aContent is not editable but it isn't in designMode, it's not
|
||||
// focusable.
|
||||
nsIDocument* doc = aContent->GetCurrentDoc();
|
||||
NS_ASSERTION(doc, "aContent must have current document");
|
||||
return aContent == doc->GetRootContent() &&
|
||||
(doc->HasFlag(NODE_IS_EDITABLE) || !aContent->IsEditable());
|
||||
}
|
||||
|
||||
nsIContent*
|
||||
nsFocusManager::CheckIfFocusable(nsIContent* aContent, PRUint32 aFlags)
|
||||
{
|
||||
@ -1396,9 +1413,11 @@ nsFocusManager::Blur(nsPIDOMWindow* aWindowToClear,
|
||||
PRINTTAGF("**Element %s has been blurred\n", content);
|
||||
#endif
|
||||
|
||||
PRBool isRootContent = content && content == content->GetCurrentDoc()->GetRootContent();
|
||||
// Don't fire blur event on the root content which isn't editable.
|
||||
PRBool sendBlurEvent =
|
||||
content && content->IsInDoc() && !IsNonFocusableRoot(content);
|
||||
if (content) {
|
||||
if (!isRootContent) {
|
||||
if (sendBlurEvent) {
|
||||
// unusual to pass a content node to SetContentState on a blur,
|
||||
// but we are just calling it to get the ContentStatesChanged notifications
|
||||
nsPresContext* presContext = presShell->GetPresContext();
|
||||
@ -1427,7 +1446,7 @@ nsFocusManager::Blur(nsPIDOMWindow* aWindowToClear,
|
||||
}
|
||||
|
||||
PRBool result = PR_TRUE;
|
||||
if (content && !isRootContent) {
|
||||
if (sendBlurEvent) {
|
||||
// if there is an active window, update commands. If there isn't an active
|
||||
// window, then this was a blur caused by the active window being lowered,
|
||||
// so there is no need to update the commands
|
||||
@ -1589,11 +1608,9 @@ nsFocusManager::Focus(nsPIDOMWindow* aWindow,
|
||||
mFocusedContent = aContent;
|
||||
aWindow->SetFocusedNode(aContent, focusMethod);
|
||||
|
||||
// don't fire events on the root content
|
||||
PRBool isRootContent = aContent &&
|
||||
aContent->IsInDoc() &&
|
||||
aContent == aContent->GetCurrentDoc()->GetRootContent();
|
||||
if (!isRootContent) {
|
||||
PRBool sendFocusEvent =
|
||||
aContent && aContent->IsInDoc() && !IsNonFocusableRoot(aContent);
|
||||
if (sendFocusEvent) {
|
||||
// if the focused element changed, scroll it into view
|
||||
if (aFocusChanged)
|
||||
ScrollIntoView(presShell, aContent, aFlags);
|
||||
@ -1628,6 +1645,13 @@ nsFocusManager::Focus(nsPIDOMWindow* aWindow,
|
||||
aContent, aFlags & FOCUSMETHOD_MASK, aWindowRaised);
|
||||
|
||||
nsIMEStateManager::OnTextStateFocus(presContext, aContent);
|
||||
} else {
|
||||
nsPresContext* presContext = presShell->GetPresContext();
|
||||
nsIMEStateManager::OnTextStateBlur(presContext, nsnull);
|
||||
nsIMEStateManager::OnChangeFocus(presContext, nsnull);
|
||||
if (!aWindowRaised) {
|
||||
aWindow->UpdateCommands(NS_LITERAL_STRING("focus"));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
@ -2190,8 +2214,19 @@ nsFocusManager::DetermineElementToMoveFocus(nsPIDOMWindow* aWindow,
|
||||
rootContent = popupFrame->GetContent();
|
||||
NS_ASSERTION(rootContent, "Popup frame doesn't have a content node");
|
||||
}
|
||||
else if (!forward && startContent == rootContent) {
|
||||
doNavigation = PR_FALSE;
|
||||
else if (!forward) {
|
||||
// If focus moves backward and when current focused node is root
|
||||
// content or <body> element which is editable by contenteditable
|
||||
// attribute, focus should move to its parent document.
|
||||
if (startContent == rootContent) {
|
||||
doNavigation = PR_FALSE;
|
||||
} else {
|
||||
nsIDocument* doc = startContent->GetCurrentDoc();
|
||||
if (startContent ==
|
||||
nsLayoutUtils::GetEditableRootContentByContentEditable(doc)) {
|
||||
doNavigation = PR_FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
@ -2463,8 +2498,11 @@ nsFocusManager::GetNextTabbableContent(nsIPresShell* aPresShell,
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (iterStartContent == aRootContent) {
|
||||
if (!aForward)
|
||||
if (!aForward) {
|
||||
frameTraversal->Last();
|
||||
} else if (aRootContent->IsFocusable()) {
|
||||
frameTraversal->Next();
|
||||
}
|
||||
}
|
||||
else if (getNextFrame &&
|
||||
(!iterStartContent || iterStartContent->Tag() != nsGkAtoms::area ||
|
||||
@ -2532,7 +2570,17 @@ nsFocusManager::GetNextTabbableContent(nsIPresShell* aPresShell,
|
||||
// frame so that the canvas becomes focused.
|
||||
nsCOMPtr<nsPIDOMWindow> subframe = subdoc->GetWindow();
|
||||
if (subframe) {
|
||||
*aResultContent = GetRootForFocus(subframe, subdoc, PR_FALSE, PR_TRUE);
|
||||
// If the subframe body is editable by contenteditable,
|
||||
// we should set the editor's root element rather than the
|
||||
// actual root element. Otherwise, we should set the focus
|
||||
// to the root content.
|
||||
*aResultContent =
|
||||
nsLayoutUtils::GetEditableRootContentByContentEditable(subdoc);
|
||||
if (!*aResultContent ||
|
||||
!((*aResultContent)->GetPrimaryFrame())) {
|
||||
*aResultContent =
|
||||
GetRootForFocus(subframe, subdoc, PR_FALSE, PR_TRUE);
|
||||
}
|
||||
if (*aResultContent) {
|
||||
NS_ADDREF(*aResultContent);
|
||||
return NS_OK;
|
||||
@ -2739,10 +2787,8 @@ nsFocusManager::GetRootForFocus(nsPIDOMWindow* aWindow,
|
||||
|
||||
nsIContent *rootContent = aDocument->GetRootContent();
|
||||
if (rootContent) {
|
||||
if (aCheckVisibility) {
|
||||
nsIPresShell* presShell = aDocument->GetPrimaryShell();
|
||||
if (!presShell || !rootContent->GetPrimaryFrame())
|
||||
return nsnull;
|
||||
if (aCheckVisibility && !rootContent->GetPrimaryFrame()) {
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
// Finally, check if this is a frameset
|
||||
|
@ -157,6 +157,15 @@ protected:
|
||||
*/
|
||||
PRBool IsWindowVisible(nsPIDOMWindow* aWindow);
|
||||
|
||||
/**
|
||||
* Returns true if aContent is a root element and not focusable.
|
||||
* I.e., even if aContent is editable root element, this returns true when
|
||||
* the document is in designMode.
|
||||
*
|
||||
* @param aContent must not be null and must be in a document.
|
||||
*/
|
||||
PRBool IsNonFocusableRoot(nsIContent* aContent);
|
||||
|
||||
/**
|
||||
* Checks and returns aContent if it may be focused, another content node if
|
||||
* the focus should be retargeted at another node, or null if the node
|
||||
|
@ -43,6 +43,8 @@
|
||||
#include "nsIFormControlFrame.h"
|
||||
#include "nsPresContext.h"
|
||||
#include "nsIContent.h"
|
||||
#include "nsIDOMDocument.h"
|
||||
#include "nsIDOMHTMLDocument.h"
|
||||
#include "nsFrameList.h"
|
||||
#include "nsGkAtoms.h"
|
||||
#include "nsIAtom.h"
|
||||
@ -3550,6 +3552,40 @@ nsLayoutUtils::SurfaceFromElement(nsIDOMElement *aElement,
|
||||
return result;
|
||||
}
|
||||
|
||||
/* static */
|
||||
nsIContent*
|
||||
nsLayoutUtils::GetEditableRootContentByContentEditable(nsIDocument* aDocument)
|
||||
{
|
||||
// If the document is in designMode we should return NULL.
|
||||
if (!aDocument || aDocument->HasFlag(NODE_IS_EDITABLE)) {
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
// contenteditable only works with HTML document.
|
||||
// Note: Use nsIDOMHTMLDocument rather than nsIHTMLDocument for getting the
|
||||
// body node because nsIDOMHTMLDocument::GetBody() does something
|
||||
// additional work for some cases and nsEditor uses them.
|
||||
nsCOMPtr<nsIDOMHTMLDocument> domHTMLDoc = do_QueryInterface(aDocument);
|
||||
if (!domHTMLDoc) {
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
nsIContent* rootContent = aDocument->GetRootContent();
|
||||
if (rootContent && rootContent->IsEditable()) {
|
||||
return rootContent;
|
||||
}
|
||||
|
||||
// If there are no editable root element, check its <body> element.
|
||||
// Note that the body element could be <frameset> element.
|
||||
nsCOMPtr<nsIDOMHTMLElement> body;
|
||||
nsresult rv = domHTMLDoc->GetBody(getter_AddRefs(body));
|
||||
nsCOMPtr<nsIContent> content = do_QueryInterface(body);
|
||||
if (NS_SUCCEEDED(rv) && content && content->IsEditable()) {
|
||||
return content;
|
||||
}
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
nsSetAttrRunnable::nsSetAttrRunnable(nsIContent* aContent, nsIAtom* aAttrName,
|
||||
const nsAString& aValue)
|
||||
: mContent(aContent),
|
||||
|
@ -1157,6 +1157,29 @@ public:
|
||||
|
||||
static SurfaceFromElementResult SurfaceFromElement(nsIDOMElement *aElement,
|
||||
PRUint32 aSurfaceFlags = 0);
|
||||
|
||||
/**
|
||||
* When the document is editable by contenteditable attribute of its root
|
||||
* content or body content.
|
||||
*
|
||||
* Be aware, this returns NULL if it's in designMode.
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* <html contenteditable="true"><body></body></html>
|
||||
* returns the <html>.
|
||||
*
|
||||
* <html><body contenteditable="true"></body></html>
|
||||
* <body contenteditable="true"></body>
|
||||
* With these cases, this returns the <body>.
|
||||
* NOTE: The latter case isn't created normally, however, it can be
|
||||
* created by script with XHTML.
|
||||
*
|
||||
* <body><p contenteditable="true"></p></body>
|
||||
* returns NULL because <body> isn't editable.
|
||||
*/
|
||||
static nsIContent*
|
||||
GetEditableRootContentByContentEditable(nsIDocument* aDocument);
|
||||
};
|
||||
|
||||
class nsSetAttrRunnable : public nsRunnable
|
||||
|
@ -69,6 +69,7 @@ _CHROME_FILES = test_bug343416.xul \
|
||||
test_wheeltransaction.xul \
|
||||
window_wheeltransaction.xul \
|
||||
test_imestate.html \
|
||||
window_imestate_iframes.html \
|
||||
test_plugin_scroll_consistency.html \
|
||||
test_composition_text_querycontent.xul \
|
||||
window_composition_text_querycontent.xul \
|
||||
|
@ -477,6 +477,13 @@ function runReadonlyChangingTest()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function runEditableSubframeTests()
|
||||
{
|
||||
window.open("window_imestate_iframes.html", "_blank",
|
||||
"width=600,height=600");
|
||||
}
|
||||
|
||||
function runTests()
|
||||
{
|
||||
if (!kIMEEnabledSupported && !kIMEOpenSupported)
|
||||
@ -519,6 +526,17 @@ function runTests()
|
||||
// XXX currently, readonly attribute changing doesn't work fine. bug 488420.
|
||||
// runReadonlyChangingTest();
|
||||
|
||||
runASyncTests();
|
||||
}
|
||||
|
||||
function runASyncTests()
|
||||
{
|
||||
// The tests must call onFinish() method.
|
||||
runEditableSubframeTests();
|
||||
}
|
||||
|
||||
function onFinish()
|
||||
{
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user