Fix forwardDelete regression
authorAryeh Gregor <AryehGregor+gitcommit@gmail.com>
Wed, 22 Jun 2011 13:42:55 -0600
changeset 310 0278f12af2f4
parent 309 c0fc2d85fb82
child 311 9495a6858c2f
Fix forwardDelete regression

It was refusing to jump over <br>'s in cases like <p>{}<br></p>, so it
would just give up and do nothing.
editcommands.html
implementation.js
source.html
tests.js
--- a/editcommands.html	Wed Jun 22 13:08:34 2011 -0600
+++ b/editcommands.html	Wed Jun 22 13:42:55 2011 -0600
@@ -459,6 +459,24 @@
 browsers (at least not in Opera 11.11), and also it breaks assumptions
 elsewhere.  E.g., if it gets turned into a p.
 
+<p>A <dfn id=visible-node>visible node</dfn> is a <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>node</a> that either is a <a href=#prohibited-paragraph-child>prohibited
+paragraph child</a>, or a <code class=external data-anolis-spec=domcore><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#text>Text</a></code> node whose <code class=external data-anolis-spec=domcore title=dom-CharacterData-data><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-characterdata-data>data</a></code> is not empty, or
+an <code class=external data-anolis-spec=html title="the img element"><a href=http://www.whatwg.org/html/#the-img-element>img</a></code>, or a <code class=external data-anolis-spec=html title="the br element"><a href=http://www.whatwg.org/html/#the-br-element>br</a></code> that is not an <a href=#extraneous-line-break>extraneous line break</a>, or
+any <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>node</a> with a <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-descendant title=concept-tree-descendant>descendant</a> that is a <a href=#visible-node>visible node</a>.
+
+<p>An <dfn id=invisible-node>invisible node</dfn> is a <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>node</a> that is not a <a href=#visible-node>visible
+node</a>.
+
+<p class=XXX>I don't know if the visible/invisible node definitions are really
+the way we want to do things.  If they are, they need some adjustment, like to
+handle collapsed whitespace nodes.
+
+<p>A <dfn id=collapsed-block-prop>collapsed block prop</dfn> is either a <a href=#collapsed-line-break>collapsed line
+break</a> that is not an <a href=#extraneous-line-break>extraneous line break</a>, or an
+<code class=external data-anolis-spec=domcore><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#element>Element</a></code> that is an <a href=#inline-node>inline node</a> and whose <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>children</a> are all
+either <a href=#invisible-node title="invisible node">invisible nodes</a> or <a href=#collapsed-block-prop title="collapsed block prop">collapsed block props</a> and that has at least
+one <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a> that is a <a href=#collapsed-block-prop>collapsed block prop</a>.
+
 <p>Each <code class=external data-anolis-spec=html><a href=http://www.whatwg.org/html/#htmldocument>HTMLDocument</a></code> has a boolean <dfn id=css-styling-flag>CSS styling flag</dfn> associated
 with it, which must initially be false.  (<a href=#the-stylewithcss-command>The <code title="">styleWithCSS</code> command</a> can be used to modify or query it, by
 means of the <code><a href=#execcommand()>execCommand()</a></code> and <code><a href=#querycommandstate()>queryCommandState()</a></code>
@@ -2645,18 +2663,6 @@
 <p>The <dfn id=default-single-line-container-name>default single-line container name</dfn> is "p".
 <!-- Possibly to be made configurable later. -->
 
-<p>A <dfn id=visible-node>visible node</dfn> is a <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>node</a> that either is a <a href=#prohibited-paragraph-child>prohibited
-paragraph child</a>, or a <code class=external data-anolis-spec=domcore><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#text>Text</a></code> node whose <code class=external data-anolis-spec=domcore title=dom-CharacterData-data><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-characterdata-data>data</a></code> is not empty, or
-an <code class=external data-anolis-spec=html title="the img element"><a href=http://www.whatwg.org/html/#the-img-element>img</a></code>, or a <code class=external data-anolis-spec=html title="the br element"><a href=http://www.whatwg.org/html/#the-br-element>br</a></code> that is not an <a href=#extraneous-line-break>extraneous line break</a>, or
-any <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>node</a> with a <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-descendant title=concept-tree-descendant>descendant</a> that is a <a href=#visible-node>visible node</a>.
-
-<p>An <dfn id=invisible-node>invisible node</dfn> is a <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>node</a> that is not a <a href=#visible-node>visible
-node</a>.
-
-<p class=XXX>I don't know if the visible/invisible node definitions are really
-the way we want to do things.  If they are, they need some adjustment, like to
-handle collapsed whitespace nodes and collapsed br's.
-
 
 <h3 id=assorted-block-formatting-command-algorithms><span class=secno>7.2 </span>Assorted block formatting command algorithms</h3>
 
@@ -3515,11 +3521,11 @@
   winds up empty after merging, we'll add a new br child at the end so it
   doesn't collapse.
   -->
-  <li>If <var title="">start block</var> has one <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a>, which is a <code class=external data-anolis-spec=html title="the br element"><a href=http://www.whatwg.org/html/#the-br-element>br</a></code>, remove
-  its <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a> from it.
-
-  <li>If <var title="">end block</var> has one <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a>, which is a <code class=external data-anolis-spec=html title="the br element"><a href=http://www.whatwg.org/html/#the-br-element>br</a></code>, remove its
-  <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a> from it.
+  <li>If <var title="">start block</var> has one <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a>, which is a <a href=#collapsed-block-prop>collapsed
+  block prop</a>, remove its <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a> from it.
+
+  <li>If <var title="">end block</var> has one <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a>, which is a <a href=#collapsed-block-prop>collapsed
+  block prop</a>, remove its <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a> from it.
 
   <li>If <var title="">start block</var> is an <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-ancestor title=concept-tree-ancestor>ancestor</a> of <var title="">end block</var>:
   <!-- Just repeatedly blow up the end block. -->
@@ -4987,6 +4993,10 @@
     <var title="">offset</var> and that <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a> is an <a href=#editable>editable</a>
     <a href=#invisible-node>invisible node</a>, remove that <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a> from <var title="">node</var>.
 
+    <li>Otherwise, if <var title="">node</var> has a <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a> with <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-indexof title=concept-indexof>index</a>
+    <var title="">offset</var> and that <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a> is a <a href=#collapsed-block-prop>collapsed block
+    prop</a>, add one to <var title="">offset</var>.
+
     <li>Otherwise, if <var title="">offset</var> is the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-node-length title=concept-node-length>length</a> of
     <var title="">node</var> and <var title="">node</var> is not a <a href=#prohibited-paragraph-child>prohibited paragraph
     child</a>, or if <var title="">node</var> is an <a href=#invisible-node>invisible node</a>, set
--- a/implementation.js	Wed Jun 22 13:08:34 2011 -0600
+++ b/implementation.js	Wed Jun 22 13:42:55 2011 -0600
@@ -713,6 +713,62 @@
 	return origHeight == finalHeight;
 }
 
+// "A visible node is a node that either is a prohibited paragraph child, or a
+// Text node whose data is not empty, or an img, or a br that is not an
+// extraneous line break, or any node with a descendant that is a visible
+// node."
+function isVisibleNode(node) {
+	if (!node) {
+		return false;
+	}
+	if (isProhibitedParagraphChild(node)
+	|| (node.nodeType == Node.TEXT_NODE && node.length)
+	|| isHtmlElement(node, "img")
+	|| (isHtmlElement(node, "br") && !isExtraneousLineBreak(node))) {
+		return true;
+	}
+	for (var i = 0; i < node.childNodes.length; i++) {
+		if (isVisibleNode(node.childNodes[i])) {
+			return true;
+		}
+	}
+	return false;
+}
+
+// "An invisible node is a node that is not a visible node."
+function isInvisibleNode(node) {
+	return node && !isVisibleNode(node);
+}
+
+// "A collapsed block prop is either a collapsed line break that is not an
+// extraneous line break, or an Element that is an inline node and whose
+// children are all either invisible nodes or collapsed block props and that
+// has at least one child that is a collapsed block prop."
+function isCollapsedBlockProp(node) {
+	if (isCollapsedLineBreak(node)
+	&& !isExtraneousLineBreak(node)) {
+		return true;
+	}
+
+	if (!isInlineNode(node)
+	|| node.nodeType != Node.ELEMENT_NODE) {
+		return false;
+	}
+
+	var hasCollapsedBlockPropChild = false;
+	for (var i = 0; i < node.childNodes.length; i++) {
+		if (!isInvisibleNode(node.childNodes[i])
+		&& !isCollapsedBlockProp(node.childNodes[i])) {
+			return false;
+		}
+		if (isCollapsedBlockProp(node.childNodes[i])) {
+			hasCollapsedBlockPropChild = true;
+		}
+	}
+
+	return hasCollapsedBlockPropChild;
+}
+
 //@}
 
 /////////////////////////////
@@ -3120,33 +3176,6 @@
 // "The default single-line container name is "p"."
 var defaultSingleLineContainerName = "p";
 
-// "A visible node is a node that either is a prohibited paragraph child, or a
-// Text node whose data is not empty, or an img, or a br that is not an
-// extraneous line break, or any node with a descendant that is a visible
-// node."
-function isVisibleNode(node) {
-	if (!node) {
-		return false;
-	}
-	if (isProhibitedParagraphChild(node)
-	|| (node.nodeType == Node.TEXT_NODE && node.length)
-	|| isHtmlElement(node, "img")
-	|| (isHtmlElement(node, "br") && !isExtraneousLineBreak(node))) {
-		return true;
-	}
-	for (var i = 0; i < node.childNodes.length; i++) {
-		if (isVisibleNode(node.childNodes[i])) {
-			return true;
-		}
-	}
-	return false;
-}
-
-// "An invisible node is a node that is not a visible node."
-function isInvisibleNode(node) {
-	return node && !isVisibleNode(node);
-}
-
 //@}
 
 ///// Assorted block formatting command algorithms /////
@@ -4025,15 +4054,17 @@
 		return;
 	}
 
-	// "If start block has one child, which is a br, remove its child from it."
+	// "If start block has one child, which is a collapsed block prop, remove
+	// its child from it."
 	if (startBlock.children.length == 1
-	&& isHtmlElement(startBlock.firstChild, "br")) {
+	&& isCollapsedBlockProp(startBlock.firstChild)) {
 		startBlock.removeChild(startBlock.firstChild);
 	}
 
-	// "If end block has one child, which is a br, remove its child from it."
+	// "If end block has one child, which is a collapsed block prop, remove its
+	// child from it."
 	if (endBlock.children.length == 1
-	&& isHtmlElement(endBlock.firstChild, "br")) {
+	&& isCollapsedBlockProp(endBlock.firstChild)) {
 		endBlock.removeChild(endBlock.firstChild);
 	}
 
@@ -5244,6 +5275,12 @@
 			&& isInvisibleNode(node.childNodes[offset])) {
 				node.removeChild(node.childNodes[offset]);
 
+			// "Otherwise, if node has a child with index offset and that child
+			// is a collapsed block prop, add one to offset."
+			} else if (offset < node.childNodes.length
+			&& isCollapsedBlockProp(node.childNodes[offset])) {
+				offset++;
+
 			// "Otherwise, if offset is the length of node and node is not a
 			// prohibited paragraph child, or if node is an invisible node, set
 			// offset to one plus the index of node, then set node to its
--- a/source.html	Wed Jun 22 13:08:34 2011 -0600
+++ b/source.html	Wed Jun 22 13:42:55 2011 -0600
@@ -401,6 +401,25 @@
 browsers (at least not in Opera 11.11), and also it breaks assumptions
 elsewhere.  E.g., if it gets turned into a p.
 
+<p>A <dfn>visible node</dfn> is a [[node]] that either is a <span>prohibited
+paragraph child</span>, or a [[text]] node whose [[cddata]] is not empty, or
+an [[img]], or a [[br]] that is not an <span>extraneous line break</span>, or
+any [[node]] with a [[descendant]] that is a <span>visible node</span>.
+
+<p>An <dfn>invisible node</dfn> is a [[node]] that is not a <span>visible
+node</span>.
+
+<p class=XXX>I don't know if the visible/invisible node definitions are really
+the way we want to do things.  If they are, they need some adjustment, like to
+handle collapsed whitespace nodes.
+
+<p>A <dfn>collapsed block prop</dfn> is either a <span>collapsed line
+break</span> that is not an <span>extraneous line break</span>, or an
+[[element]] that is an <span>inline node</span> and whose [[children]] are all
+either <span title="invisible node">invisible nodes</span> or <span
+title="collapsed block prop">collapsed block props</span> and that has at least
+one [[child]] that is a <span>collapsed block prop</span>.
+
 <p>Each [[htmldocument]] has a boolean <dfn>CSS styling flag</dfn> associated
 with it, which must initially be false.  (<span>The <code
 title>styleWithCSS</code> command</span> can be used to modify or query it, by
@@ -2621,18 +2640,6 @@
 
 <p>The <dfn>default single-line container name</dfn> is "p".
 <!-- Possibly to be made configurable later. -->
-
-<p>A <dfn>visible node</dfn> is a [[node]] that either is a <span>prohibited
-paragraph child</span>, or a [[text]] node whose [[cddata]] is not empty, or
-an [[img]], or a [[br]] that is not an <span>extraneous line break</span>, or
-any [[node]] with a [[descendant]] that is a <span>visible node</span>.
-
-<p>An <dfn>invisible node</dfn> is a [[node]] that is not a <span>visible
-node</span>.
-
-<p class=XXX>I don't know if the visible/invisible node definitions are really
-the way we want to do things.  If they are, they need some adjustment, like to
-handle collapsed whitespace nodes and collapsed br's.
 <!-- @} -->
 
 <h3>Assorted block formatting command algorithms</h3>
@@ -3499,11 +3506,11 @@
   winds up empty after merging, we'll add a new br child at the end so it
   doesn't collapse.
   -->
-  <li>If <var>start block</var> has one [[child]], which is a [[br]], remove
-  its [[child]] from it.
-
-  <li>If <var>end block</var> has one [[child]], which is a [[br]], remove its
-  [[child]] from it.
+  <li>If <var>start block</var> has one [[child]], which is a <span>collapsed
+  block prop</span>, remove its [[child]] from it.
+
+  <li>If <var>end block</var> has one [[child]], which is a <span>collapsed
+  block prop</span>, remove its [[child]] from it.
 
   <li>If <var>start block</var> is an [[ancestor]] of <var>end block</var>:
   <!-- Just repeatedly blow up the end block. -->
@@ -4989,6 +4996,10 @@
     <var>offset</var> and that [[child]] is an <span>editable</span>
     <span>invisible node</span>, remove that [[child]] from <var>node</var>.
 
+    <li>Otherwise, if <var>node</var> has a [[child]] with [[index]]
+    <var>offset</var> and that [[child]] is a <span>collapsed block
+    prop</span>, add one to <var>offset</var>.
+
     <li>Otherwise, if <var>offset</var> is the [[nodelength]] of
     <var>node</var> and <var>node</var> is not a <span>prohibited paragraph
     child</span>, or if <var>node</var> is an <span>invisible node</span>, set
--- a/tests.js	Wed Jun 22 13:08:34 2011 -0600
+++ b/tests.js	Wed Jun 22 13:42:55 2011 -0600
@@ -923,6 +923,19 @@
 		'<p>foo[]<br></p>bar',
 		'foo[]<br><p>bar</p>',
 
+		'<p>{}<br></p>foo',
+		'<p>{}<span><br></span></p>foo',
+		'foo{}<p><br>',
+		'foo{}<p><span><br></span>',
+		'foo{}<br><p><br>',
+		'foo{}<span><br></span><p><br>',
+		'foo{}<br><p><span><br></span>',
+		'foo{}<span><br></span><p><span><br></span>',
+		'foo{}<p>',
+		'<table><tr><td>{}</table>foo',
+		'<table><tr><td>{}<br></table>foo',
+		'<table><tr><td>{}<span><br></span></table>foo',
+
 		'<div><p>foo[]</p></div><p>bar</p>',
 		'<p>foo[]</p><div><p>bar</p></div>',
 		'<div><p>foo[]</p></div><div><p>bar</p></div>',