Bug 1156062 part 8 - Clean up nsHTMLEditRules::JoinBlocks; r=ehsan

--HG--
extra : rebase_source : 6b76252e1e569fd792829a0dbe31e8412cd2fa9d
This commit is contained in:
Aryeh Gregor 2016-04-23 19:20:20 +09:00
parent 79ea112b87
commit 2dc492c957
2 changed files with 167 additions and 182 deletions

View File

@ -2196,7 +2196,9 @@ nsHTMLEditRules::WillDeleteSelection(Selection* aSelection,
NS_ENSURE_STATE(mHTMLEditor);
nsAutoTrackDOMPoint tracker(mHTMLEditor->mRangeUpdater,
address_of(selPointNode), &selPointOffset);
res = JoinBlocks(GetAsDOMNode(leftNode), GetAsDOMNode(rightNode),
NS_ENSURE_STATE(leftNode && leftNode->IsContent() &&
rightNode && rightNode->IsContent());
res = JoinBlocks(*leftNode->AsContent(), *rightNode->AsContent(),
aCancel);
*aHandled = true;
NS_ENSURE_SUCCESS(res, res);
@ -2246,7 +2248,8 @@ nsHTMLEditRules::WillDeleteSelection(Selection* aSelection,
NS_ENSURE_STATE(mHTMLEditor);
nsAutoTrackDOMPoint tracker(mHTMLEditor->mRangeUpdater,
address_of(selPointNode), &selPointOffset);
res = JoinBlocks(GetAsDOMNode(leftNode), GetAsDOMNode(rightNode),
NS_ENSURE_STATE(leftNode->IsContent() && rightNode->IsContent());
res = JoinBlocks(*leftNode->AsContent(), *rightNode->AsContent(),
aCancel);
*aHandled = true;
NS_ENSURE_SUCCESS(res, res);
@ -2432,8 +2435,7 @@ nsHTMLEditRules::WillDeleteSelection(Selection* aSelection,
}
if (join) {
res = JoinBlocks(GetAsDOMNode(leftParent), GetAsDOMNode(rightParent),
aCancel);
res = JoinBlocks(*leftParent, *rightParent, aCancel);
NS_ENSURE_SUCCESS(res, res);
}
}
@ -2562,209 +2564,186 @@ nsHTMLEditRules::GetGoodSelPointForNode(nsINode& aNode,
}
/*****************************************************************************************************
* JoinBlocks: this method is used to join two block elements. The right element is always joined
* to the left element. If the elements are the same type and not nested within each other,
* JoinNodesSmart is called (example, joining two list items together into one). If the elements
* are not the same type, or one is a descendant of the other, we instead destroy the right block
* placing its children into leftblock. DTD containment rules are followed throughout.
* nsCOMPtr<nsIDOMNode> *aLeftBlock pointer to the left block
* nsCOMPtr<nsIDOMNode> *aRightBlock pointer to the right block; will have contents moved to left block
* bool *aCanceled return TRUE if we had to cancel operation
*/
/**
* This method is used to join two block elements. The right element is always
* joined to the left element. If the elements are the same type and not
* nested within each other, JoinNodesSmart is called (example, joining two
* list items together into one). If the elements are not the same type, or
* one is a descendant of the other, we instead destroy the right block placing
* its children into leftblock. DTD containment rules are followed throughout.
*/
nsresult
nsHTMLEditRules::JoinBlocks(nsIDOMNode *aLeftNode,
nsIDOMNode *aRightNode,
bool *aCanceled)
nsHTMLEditRules::JoinBlocks(nsIContent& aLeftNode, nsIContent& aRightNode,
bool* aCanceled)
{
NS_ENSURE_ARG_POINTER(aLeftNode && aRightNode);
MOZ_ASSERT(aCanceled);
nsCOMPtr<nsIDOMNode> aLeftBlock, aRightBlock;
NS_ENSURE_STATE(mHTMLEditor);
nsCOMPtr<nsIEditor> kungFuDeathGrip(mHTMLEditor);
if (IsBlockNode(aLeftNode)) {
aLeftBlock = aLeftNode;
} else if (aLeftNode) {
NS_ENSURE_STATE(mHTMLEditor);
aLeftBlock = mHTMLEditor->GetBlockNodeParent(aLeftNode);
}
nsCOMPtr<Element> leftBlock = mHTMLEditor->GetBlock(aLeftNode);
nsCOMPtr<Element> rightBlock = mHTMLEditor->GetBlock(aRightNode);
if (IsBlockNode(aRightNode)) {
aRightBlock = aRightNode;
} else if (aRightNode) {
NS_ENSURE_STATE(mHTMLEditor);
aRightBlock = mHTMLEditor->GetBlockNodeParent(aRightNode);
}
// Sanity checks
NS_ENSURE_TRUE(leftBlock && rightBlock, NS_ERROR_NULL_POINTER);
NS_ENSURE_STATE(leftBlock != rightBlock);
// sanity checks
NS_ENSURE_TRUE(aLeftBlock && aRightBlock, NS_ERROR_NULL_POINTER);
NS_ENSURE_STATE(aLeftBlock != aRightBlock);
if (nsHTMLEditUtils::IsTableElement(aLeftBlock) ||
nsHTMLEditUtils::IsTableElement(aRightBlock)) {
// do not try to merge table elements
if (nsHTMLEditUtils::IsTableElement(leftBlock) ||
nsHTMLEditUtils::IsTableElement(rightBlock)) {
// Do not try to merge table elements
*aCanceled = true;
return NS_OK;
}
// make sure we don't try to move thing's into HR's, which look like blocks but aren't containers
if (nsHTMLEditUtils::IsHR(aLeftBlock)) {
NS_ENSURE_STATE(mHTMLEditor);
nsCOMPtr<nsIDOMNode> realLeft = mHTMLEditor->GetBlockNodeParent(aLeftBlock);
aLeftBlock = realLeft;
// Make sure we don't try to move things into HR's, which look like blocks
// but aren't containers
if (leftBlock->IsHTMLElement(nsGkAtoms::hr)) {
leftBlock = mHTMLEditor->GetBlockNodeParent(leftBlock);
}
if (nsHTMLEditUtils::IsHR(aRightBlock)) {
NS_ENSURE_STATE(mHTMLEditor);
nsCOMPtr<nsIDOMNode> realRight = mHTMLEditor->GetBlockNodeParent(aRightBlock);
aRightBlock = realRight;
if (rightBlock->IsHTMLElement(nsGkAtoms::hr)) {
rightBlock = mHTMLEditor->GetBlockNodeParent(rightBlock);
}
NS_ENSURE_STATE(aLeftBlock && aRightBlock);
NS_ENSURE_STATE(leftBlock && rightBlock);
// bail if both blocks the same
if (aLeftBlock == aRightBlock) {
// Bail if both blocks the same
if (leftBlock == rightBlock) {
*aCanceled = true;
return NS_OK;
}
// Joining a list item to its parent is a NOP.
if (nsHTMLEditUtils::IsList(aLeftBlock) &&
nsHTMLEditUtils::IsListItem(aRightBlock)) {
nsCOMPtr<nsIDOMNode> rightParent;
aRightBlock->GetParentNode(getter_AddRefs(rightParent));
if (rightParent == aLeftBlock) {
return NS_OK;
}
if (nsHTMLEditUtils::IsList(leftBlock) &&
nsHTMLEditUtils::IsListItem(rightBlock) &&
rightBlock->GetParentNode() == leftBlock) {
return NS_OK;
}
// special rule here: if we are trying to join list items, and they are in different lists,
// join the lists instead.
bool bMergeLists = false;
// Special rule here: if we are trying to join list items, and they are in
// different lists, join the lists instead.
bool mergeLists = false;
nsIAtom* existingList = nsGkAtoms::_empty;
int32_t theOffset;
nsCOMPtr<nsIDOMNode> leftList, rightList;
if (nsHTMLEditUtils::IsListItem(aLeftBlock) &&
nsHTMLEditUtils::IsListItem(aRightBlock)) {
aLeftBlock->GetParentNode(getter_AddRefs(leftList));
aRightBlock->GetParentNode(getter_AddRefs(rightList));
if (leftList && rightList && (leftList!=rightList))
{
// there are some special complications if the lists are descendants of
// the other lists' items. Note that it is ok for them to be descendants
// of the other lists themselves, which is the usual case for sublists
// in our impllementation.
if (!nsEditorUtils::IsDescendantOf(leftList, aRightBlock, &theOffset) &&
!nsEditorUtils::IsDescendantOf(rightList, aLeftBlock, &theOffset))
{
aLeftBlock = leftList;
aRightBlock = rightList;
bMergeLists = true;
NS_ENSURE_STATE(mHTMLEditor);
existingList = mHTMLEditor->GetTag(leftList);
}
int32_t offset;
nsCOMPtr<Element> leftList, rightList;
if (nsHTMLEditUtils::IsListItem(leftBlock) &&
nsHTMLEditUtils::IsListItem(rightBlock)) {
leftList = leftBlock->GetParentElement();
rightList = rightBlock->GetParentElement();
if (leftList && rightList && leftList != rightList &&
!nsEditorUtils::IsDescendantOf(leftList, rightBlock, &offset) &&
!nsEditorUtils::IsDescendantOf(rightList, leftBlock, &offset)) {
// There are some special complications if the lists are descendants of
// the other lists' items. Note that it is okay for them to be
// descendants of the other lists themselves, which is the usual case for
// sublists in our implementation.
leftBlock = leftList;
rightBlock = rightList;
mergeLists = true;
existingList = leftList->NodeInfo()->NameAtom();
}
}
NS_ENSURE_STATE(mHTMLEditor);
nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor);
nsresult res = NS_OK;
int32_t rightOffset = 0;
int32_t leftOffset = -1;
int32_t rightOffset = 0;
int32_t leftOffset = -1;
// theOffset below is where you find yourself in aRightBlock when you traverse upwards
// from aLeftBlock
if (nsEditorUtils::IsDescendantOf(aLeftBlock, aRightBlock, &rightOffset)) {
// tricky case. left block is inside right block.
// Do ws adjustment. This just destroys non-visible ws at boundaries we will be joining.
// offset below is where you find yourself in rightBlock when you traverse
// upwards from leftBlock
if (nsEditorUtils::IsDescendantOf(leftBlock, rightBlock, &rightOffset)) {
// Tricky case. Left block is inside right block. Do ws adjustment. This
// just destroys non-visible ws at boundaries we will be joining.
rightOffset++;
NS_ENSURE_STATE(mHTMLEditor);
nsCOMPtr<nsINode> leftBlock(do_QueryInterface(aLeftBlock));
res = nsWSRunObject::ScrubBlockBoundary(mHTMLEditor,
nsWSRunObject::kBlockEnd,
leftBlock);
NS_ENSURE_SUCCESS(res, res);
{
// We can't just track rightBlock because it's an Element.
nsCOMPtr<nsINode> trackingRightBlock(rightBlock);
nsAutoTrackDOMPoint tracker(mHTMLEditor->mRangeUpdater,
address_of(aRightBlock), &rightOffset);
nsCOMPtr<nsINode> rightBlock(do_QueryInterface(aRightBlock));
address_of(trackingRightBlock),
&rightOffset);
res = nsWSRunObject::ScrubBlockBoundary(mHTMLEditor,
nsWSRunObject::kAfterBlock,
rightBlock, rightOffset);
NS_ENSURE_SUCCESS(res, res);
if (trackingRightBlock->IsElement()) {
rightBlock = trackingRightBlock->AsElement();
} else {
NS_ENSURE_STATE(trackingRightBlock->GetParentElement());
rightBlock = trackingRightBlock->GetParentElement();
}
}
// Do br adjustment.
nsCOMPtr<nsIDOMNode> brNode;
res = CheckForInvisibleBR(aLeftBlock, kBlockEnd, address_of(brNode));
res = CheckForInvisibleBR(GetAsDOMNode(leftBlock), kBlockEnd,
address_of(brNode));
NS_ENSURE_SUCCESS(res, res);
if (bMergeLists)
{
// idea here is to take all children in rightList that are past
// theOffset, and pull them into leftlist.
nsCOMPtr<nsIContent> parent(do_QueryInterface(rightList));
NS_ENSURE_TRUE(parent, NS_ERROR_NULL_POINTER);
nsIContent *child = parent->GetChildAt(theOffset);
nsCOMPtr<nsINode> leftList_ = do_QueryInterface(leftList);
NS_ENSURE_STATE(leftList_);
while (child)
{
NS_ENSURE_STATE(mHTMLEditor);
res = mHTMLEditor->MoveNode(child, leftList_, -1);
if (mergeLists) {
// The idea here is to take all children in rightList that are past
// offset, and pull them into leftlist.
for (nsCOMPtr<nsIContent> child = rightList->GetChildAt(offset);
child; child = rightList->GetChildAt(rightOffset)) {
res = mHTMLEditor->MoveNode(child, leftList, -1);
NS_ENSURE_SUCCESS(res, res);
child = parent->GetChildAt(rightOffset);
}
} else {
res = MoveBlock(GetAsDOMNode(leftBlock), GetAsDOMNode(rightBlock),
leftOffset, rightOffset);
}
else
{
res = MoveBlock(aLeftBlock, aRightBlock, leftOffset, rightOffset);
if (brNode) {
mHTMLEditor->DeleteNode(brNode);
}
NS_ENSURE_STATE(mHTMLEditor);
if (brNode) mHTMLEditor->DeleteNode(brNode);
// theOffset below is where you find yourself in aLeftBlock when you traverse upwards
// from aRightBlock
} else if (nsEditorUtils::IsDescendantOf(aRightBlock, aLeftBlock, &leftOffset)) {
// tricky case. right block is inside left block.
// Do ws adjustment. This just destroys non-visible ws at boundaries we will be joining.
NS_ENSURE_STATE(mHTMLEditor);
nsCOMPtr<nsINode> rightBlock(do_QueryInterface(aRightBlock));
// Offset below is where you find yourself in leftBlock when you traverse
// upwards from rightBlock
} else if (nsEditorUtils::IsDescendantOf(rightBlock, leftBlock,
&leftOffset)) {
// Tricky case. Right block is inside left block. Do ws adjustment. This
// just destroys non-visible ws at boundaries we will be joining.
res = nsWSRunObject::ScrubBlockBoundary(mHTMLEditor,
nsWSRunObject::kBlockStart,
rightBlock);
NS_ENSURE_SUCCESS(res, res);
NS_ENSURE_STATE(mHTMLEditor);
{
// We can't just track leftBlock because it's an Element, so track
// something else.
nsCOMPtr<nsINode> trackingLeftBlock(leftBlock);
nsAutoTrackDOMPoint tracker(mHTMLEditor->mRangeUpdater,
address_of(aLeftBlock), &leftOffset);
nsCOMPtr<nsINode> leftBlock(do_QueryInterface(aLeftBlock));
address_of(trackingLeftBlock), &leftOffset);
res = nsWSRunObject::ScrubBlockBoundary(mHTMLEditor,
nsWSRunObject::kBeforeBlock,
leftBlock, leftOffset);
NS_ENSURE_SUCCESS(res, res);
if (trackingLeftBlock->IsElement()) {
leftBlock = trackingLeftBlock->AsElement();
} else {
NS_ENSURE_STATE(trackingLeftBlock->GetParentElement());
leftBlock = trackingLeftBlock->GetParentElement();
}
}
// Do br adjustment.
nsCOMPtr<nsIDOMNode> brNode;
res = CheckForInvisibleBR(aLeftBlock, kBeforeBlock, address_of(brNode),
leftOffset);
res = CheckForInvisibleBR(GetAsDOMNode(leftBlock), kBeforeBlock,
address_of(brNode), leftOffset);
NS_ENSURE_SUCCESS(res, res);
if (bMergeLists)
{
res = MoveContents(rightList, leftList, &leftOffset);
}
else
{
if (mergeLists) {
res = MoveContents(GetAsDOMNode(rightList), GetAsDOMNode(leftList),
&leftOffset);
} else {
// Left block is a parent of right block, and the parent of the previous
// visible content. Right block is a child and contains the contents we
// want to move.
int32_t previousContentOffset;
nsCOMPtr<nsIDOMNode> previousContentParent;
nsCOMPtr<nsINode> previousContentParent;
if (aLeftNode == aLeftBlock) {
if (&aLeftNode == leftBlock) {
// We are working with valid HTML, aLeftNode is a block node, and is
// therefore allowed to contain aRightBlock. This is the simple case,
// we will simply move the content in aRightBlock out of its block.
previousContentParent = aLeftBlock;
// therefore allowed to contain rightBlock. This is the simple case,
// we will simply move the content in rightBlock out of its block.
previousContentParent = leftBlock;
previousContentOffset = leftOffset;
} else {
// We try to work as well as possible with HTML that's already invalid.
@ -2777,8 +2756,9 @@ nsHTMLEditRules::JoinBlocks(nsIDOMNode *aLeftNode,
// unexpected position. (see bug 200416) The new idea is to make the
// moving content a sibling, next to the previous visible content.
previousContentParent =
nsEditor::GetNodeLocation(aLeftNode, &previousContentOffset);
previousContentParent = aLeftNode.GetParentNode();
previousContentOffset = previousContentParent ?
previousContentParent->IndexOf(&aLeftNode) : -1;
// We want to move our content just after the previous visible node.
previousContentOffset++;
@ -2787,69 +2767,73 @@ nsHTMLEditRules::JoinBlocks(nsIDOMNode *aLeftNode,
// Because we don't want the moving content to receive the style of the
// previous content, we split the previous content's style.
NS_ENSURE_STATE(mHTMLEditor);
nsCOMPtr<nsINode> editorRoot = mHTMLEditor->GetEditorRoot();
if (!editorRoot || aLeftNode != editorRoot->AsDOMNode()) {
nsCOMPtr<nsIDOMNode> splittedPreviousContent;
NS_ENSURE_STATE(mHTMLEditor);
res = mHTMLEditor->SplitStyleAbovePoint(address_of(previousContentParent),
nsCOMPtr<Element> editorRoot = mHTMLEditor->GetEditorRoot();
if (!editorRoot || &aLeftNode != editorRoot) {
nsCOMPtr<nsIDOMNode> previousContentParentDOM =
GetAsDOMNode(previousContentParent);
nsCOMPtr<nsIDOMNode> splittedPreviousContentDOM;
res = mHTMLEditor->SplitStyleAbovePoint(address_of(previousContentParentDOM),
&previousContentOffset,
nullptr, nullptr, nullptr,
address_of(splittedPreviousContent));
address_of(splittedPreviousContentDOM));
NS_ENSURE_SUCCESS(res, res);
previousContentParent = do_QueryInterface(previousContentParentDOM);
NS_ENSURE_STATE(previousContentParent || !previousContentParentDOM);
if (splittedPreviousContent) {
previousContentParent =
nsEditor::GetNodeLocation(splittedPreviousContent,
&previousContentOffset);
if (splittedPreviousContentDOM) {
nsCOMPtr<nsINode> splittedPreviousContent =
do_QueryInterface(splittedPreviousContentDOM);
NS_ENSURE_STATE(splittedPreviousContent ||
!splittedPreviousContentDOM);
previousContentParent = splittedPreviousContent->GetParentNode();
previousContentOffset = previousContentParent ?
previousContentParent->IndexOf(splittedPreviousContent) : -1;
}
}
res = MoveBlock(previousContentParent, aRightBlock,
res = MoveBlock(GetAsDOMNode(previousContentParent),
GetAsDOMNode(rightBlock),
previousContentOffset, rightOffset);
NS_ENSURE_SUCCESS(res, res);
}
NS_ENSURE_STATE(mHTMLEditor);
if (brNode) mHTMLEditor->DeleteNode(brNode);
}
else
{
// normal case. blocks are siblings, or at least close enough to siblings. An example
// of the latter is a <p>paragraph</p><ul><li>one<li>two<li>three</ul>. The first
// li and the p are not true siblings, but we still want to join them if you backspace
// from li into p.
if (brNode) {
mHTMLEditor->DeleteNode(brNode);
}
} else {
// Normal case. Blocks are siblings, or at least close enough. An example
// of the latter is <p>paragraph</p><ul><li>one<li>two<li>three</ul>. The
// first li and the p are not true siblings, but we still want to join them
// if you backspace from li into p.
// adjust whitespace at block boundaries
NS_ENSURE_STATE(mHTMLEditor);
nsCOMPtr<Element> leftBlock(do_QueryInterface(aLeftBlock));
nsCOMPtr<Element> rightBlock(do_QueryInterface(aRightBlock));
// Adjust whitespace at block boundaries
res = nsWSRunObject::PrepareToJoinBlocks(mHTMLEditor, leftBlock, rightBlock);
NS_ENSURE_SUCCESS(res, res);
// Do br adjustment.
nsCOMPtr<nsIDOMNode> brNode;
res = CheckForInvisibleBR(aLeftBlock, kBlockEnd, address_of(brNode));
res = CheckForInvisibleBR(GetAsDOMNode(leftBlock), kBlockEnd,
address_of(brNode));
NS_ENSURE_SUCCESS(res, res);
NS_ENSURE_STATE(mHTMLEditor);
if (bMergeLists || mHTMLEditor->NodesSameType(aLeftBlock, aRightBlock)) {
// nodes are same type. merge them.
if (mergeLists || leftBlock->NodeInfo()->NameAtom() ==
rightBlock->NodeInfo()->NameAtom()) {
// Nodes are same type. Merge them.
::DOMPoint pt = JoinNodesSmart(*leftBlock, *rightBlock);
if (pt.node && bMergeLists) {
nsCOMPtr<nsIDOMNode> newBlock;
res = ConvertListType(aRightBlock, address_of(newBlock),
if (pt.node && mergeLists) {
nsCOMPtr<Element> newBlock;
res = ConvertListType(rightBlock, getter_AddRefs(newBlock),
existingList, nsGkAtoms::li);
}
} else {
// Nodes are dissimilar types.
res = MoveBlock(GetAsDOMNode(leftBlock), GetAsDOMNode(rightBlock),
leftOffset, rightOffset);
NS_ENSURE_SUCCESS(res, res);
}
else
{
// nodes are disimilar types.
res = MoveBlock(aLeftBlock, aRightBlock, leftOffset, rightOffset);
}
if (NS_SUCCEEDED(res) && brNode)
{
NS_ENSURE_STATE(mHTMLEditor);
if (brNode) {
res = mHTMLEditor->DeleteNode(brNode);
NS_ENSURE_SUCCESS(res, res);
}
}
return res;
return NS_OK;
}

View File

@ -152,7 +152,8 @@ protected:
nsresult InsertBRIfNeeded(mozilla::dom::Selection* aSelection);
::DOMPoint GetGoodSelPointForNode(nsINode& aNode,
nsIEditor::EDirection aAction);
nsresult JoinBlocks(nsIDOMNode *aLeftNode, nsIDOMNode *aRightNode, bool *aCanceled);
nsresult JoinBlocks(nsIContent& aLeftNode, nsIContent& aRightNode,
bool* aCanceled);
nsresult MoveBlock(nsIDOMNode *aLeft, nsIDOMNode *aRight, int32_t aLeftOffset, int32_t aRightOffset);
nsresult MoveNodeSmart(nsIDOMNode *aSource, nsIDOMNode *aDest, int32_t *aOffset);
nsresult MoveContents(nsIDOMNode *aSource, nsIDOMNode *aDest, int32_t *aOffset);