Bug 1422234 - part 1: HTMLEditRules::ReturnInParagraph() should adjust split point if caret position is positioned at edge of anchor element r=m_kato

When Enter key is pressed at start or end of <a href="foo"> element, we
shouldn't split it (in other words, we shouldn't create empty <a href="foo">
element in new paragraph) because users must not want to keep editing *same*
link in new paragraph in most cases.

This patch adjusts HTMLEditRules::ReturnInParagraph() selection start point
locally when it gets selection.  If caret is at start of an <a href="foo">
element, moves caret to before the element.  If caret is at end of an
<a href="foo"> element, moves to after the element.

MozReview-Commit-ID: 3L3eDzc6Dk

--HG--
extra : rebase_source : dfb22486d2709e84ef7ec1eee3a9bfdef11cbe99
This commit is contained in:
Masayuki Nakano 2018-01-23 11:14:37 +09:00
parent 7f8c5cae44
commit dc245c4fee
3 changed files with 103 additions and 6 deletions

View File

@ -14,6 +14,7 @@
#include "mozilla/dom/Text.h"
#include "nsAtom.h"
#include "nsCOMPtr.h"
#include "nsGkAtoms.h"
#include "nsIContent.h"
#include "nsIDOMNode.h"
#include "nsINode.h"
@ -399,6 +400,26 @@ public:
mIsChildInitialized = true;
}
/**
* SetAfter() sets mChild to next sibling of aChild.
*/
void
SetAfter(const nsINode* aChild)
{
MOZ_ASSERT(aChild);
nsIContent* nextSibling = aChild->GetNextSibling();
if (nextSibling) {
Set(nextSibling);
return;
}
nsINode* parentNode = aChild->GetParentNode();
if (NS_WARN_IF(!parentNode)) {
Clear();
return;
}
SetToEndOf(parentNode);
}
/**
* Clear() makes the instance not point anywhere.
*/
@ -586,6 +607,23 @@ public:
return mOffset.value() == mParent->Length();
}
bool
IsBRElementAtEndOfContainer() const
{
if (NS_WARN_IF(!mParent)) {
return false;
}
if (!mParent->IsContainerNode()) {
return false;
}
const_cast<SelfType*>(this)->EnsureChild();
if (!mChild ||
mChild->GetNextSibling()) {
return false;
}
return mChild->IsHTMLElement(nsGkAtoms::br);
}
// Convenience methods for switching between the two types
// of EditorDOMPointBase.
EditorDOMPointBase<nsINode*, nsIContent*>

View File

@ -6883,6 +6883,71 @@ HTMLEditRules::ReturnInParagraph(Selection& aSelection,
}
MOZ_ASSERT(atStartOfSelection.IsSetAndValid());
// We shouldn't create new anchor element which has non-empty href unless
// splitting middle of it because we assume that users don't want to create
// *same* anchor element across two or more paragraphs in most cases.
// So, adjust selection start if it's edge of anchor element(s).
// XXX We don't support whitespace collapsing in these cases since it needs
// some additional work with WSRunObject but it's not usual case.
// E.g., |<a href="foo"><b>foo []</b> </a>|
if (atStartOfSelection.IsStartOfContainer()) {
for (nsIContent* container = atStartOfSelection.GetContainerAsContent();
container && container != &aParentDivOrP;
container = container->GetParent()) {
if (HTMLEditUtils::IsLink(container)) {
// Found link should be only in right node. So, we shouldn't split it.
atStartOfSelection.Set(container);
// Even if we found an anchor element, don't break because DOM API
// allows to nest anchor elements.
}
// If the container is middle of its parent, stop adjusting split point.
if (container->GetPreviousSibling()) {
// XXX Should we check if previous sibling is visible content?
// E.g., should we ignore comment node, invisible <br> element?
break;
}
}
}
// We also need to check if selection is at invisible <br> element at end
// of an <a href="foo"> element because editor inserts a <br> element when
// user types Enter key after a whitespace which is at middle of
// <a href="foo"> element and when setting selection at end of the element,
// selection becomes referring the <br> element. We may need to change this
// behavior later if it'd be standardized.
else if (atStartOfSelection.IsEndOfContainer() ||
atStartOfSelection.IsBRElementAtEndOfContainer()) {
// If there are 2 <br> elements, the first <br> element is visible. E.g.,
// |<a href="foo"><b>boo[]<br></b><br></a>|, we should split the <a>
// element. Otherwise, E.g., |<a href="foo"><b>boo[]<br></b></a>|,
// we should not split the <a> element and ignore inline elements in it.
bool foundBRElement = atStartOfSelection.IsBRElementAtEndOfContainer();
for (nsIContent* container = atStartOfSelection.GetContainerAsContent();
container && container != &aParentDivOrP;
container = container->GetParent()) {
if (HTMLEditUtils::IsLink(container)) {
// Found link should be only in left node. So, we shouldn't split it.
atStartOfSelection.SetAfter(container);
// Even if we found an anchor element, don't break because DOM API
// allows to nest anchor elements.
}
// If the container is middle of its parent, stop adjusting split point.
if (nsIContent* nextSibling = container->GetNextSibling()) {
if (foundBRElement) {
// If we've already found a <br> element, we assume found node is
// visible <br> or something other node.
// XXX Should we check if non-text data node like comment?
break;
}
// XXX Should we check if non-text data node like comment?
if (!nextSibling->IsHTMLElement(nsGkAtoms::br)) {
break;
}
foundBRElement = true;
}
}
}
bool doesCRCreateNewP = htmlEditor->GetReturnInParagraphCreatesNewParagraph();
bool splitAfterNewBR = false;

View File

@ -120,12 +120,6 @@
[[["stylewithcss","false"\],["defaultparagraphseparator","p"\],["insertparagraph",""\]\] "<p><b id=x class=y>foo[\]bar</b></p>" compare innerHTML]
expected: FAIL
[[["defaultparagraphseparator","div"\],["insertparagraph",""\]\] "foo<a href=foo>[\]bar</a>" compare innerHTML]
expected: FAIL
[[["defaultparagraphseparator","p"\],["insertparagraph",""\]\] "foo<a href=foo>[\]bar</a>" compare innerHTML]
expected: FAIL
[[["defaultparagraphseparator","div"\],["insertparagraph",""\]\] "<p>foo[\]<!--bar-->" compare innerHTML]
expected: FAIL