Don't allow merging from/to table cells at all
authorAryeh Gregor <AryehGregor+gitcommit@gmail.com>
Mon, 06 Jun 2011 15:02:25 -0600
changeset 233 3fb7f026ad4c
parent 232 ec4e7a39d45e
child 234 28b09cd8c691
Don't allow merging from/to table cells at all

I changed my mind, partly based on the recommendation of the "behavior
when typing in contentEditable elements" document. I was ambivalent to
begin with, but on consideration, the new behavior is more reasonable.

I also refactored things in a few ways that should be unimportant,
although I'm pretty sure I fixed at least one latent bug.
editcommands.html
implementation.js
source.html
--- a/editcommands.html	Mon Jun 06 14:45:37 2011 -0600
+++ b/editcommands.html	Mon Jun 06 15:02:25 2011 -0600
@@ -1048,29 +1048,58 @@
   <var title="">start offset</var>) and its <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-end title=concept-range-end>end</a> to (<var title="">end node</var>,
   <var title="">end offset</var>).
 
+  <!--
+  When we delete a selection that spans multiple blocks, we merge the end
+  block's contents into the start block, like
+
+    <p>fo[o</p><pre>b]ar</pre>
+    -> <p>fo[]ar</p>.
+
+  Figure out what the start and end blocks are before we start deleting
+  anything.
+  -->
   <li>Let <var title="">start block</var> be the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-node title=concept-boundary-point-node>node</a> of
   <var title="">range</var>.
 
   <li>While <var title="">start block</var>'s <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a> is <a href=#in-the-same-editing-host>in the same editing
-  host</a>, and <var title="">start block</var> is not a <a href=#prohibited-paragraph-child>prohibited paragraph
-  child</a> or "span" is not an <a href=#allowed-child>allowed child</a> of <var title="">start
-  block</var>, set <var title="">start block</var> to its <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a>.
+  host</a> and <var title="">start block</var> is not a <a href=#prohibited-paragraph-child>prohibited paragraph
+  child</a>, set <var title="">start block</var> to its <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a>.
+
+  <li>If <var title="">start block</var> is neither a <a href=#prohibited-paragraph-child>prohibited paragraph
+  child</a> nor an <a href=#editing-host>editing host</a>, or "span" is not an
+  <a href=#allowed-child>allowed child</a> of <var title="">start block</var>, or <var title="">start
+  block</var> is a <code class=external data-anolis-spec=html title="the td element"><a href=http://www.whatwg.org/html/#the-td-element>td</a></code> or <code class=external data-anolis-spec=html title="the th element"><a href=http://www.whatwg.org/html/#the-th-element>th</a></code>, set <var title="">start block</var> to null.
+  <!--
+  We only merge to or from prohibited paragraph children or editing hosts.
+  (This is just in case someone makes a span into an editing host and sticks
+  paragraphs inside it or something . . . we could probably drop that proviso.)
+  Anything else is presumed to be an inline element, basically.  This might not
+  be ideal.
+
+  If span isn't an allowed child, it's probably something unpleasant like a
+  table row or a list or such.  We don't want to merge to or from something
+  like that, because we'd most likely wind up with the wrong type of child
+  somewhere.  Although we probably want to allow merging different lists or
+  such this way . . .
+
+  We don't let either start block or end block be a td or th.  This means we'll
+  never merge to or from a td or th.  This matches Firefox 5.0a2, and
+  reportedly Word as well.  Chrome 13 dev and Opera 11.11 allow merging from a
+  non-table cell end block to a table cell start block, but not vice versa.  In
+  IE9 the delete key just does nothing.
+  -->
 
   <li>Let <var title="">end block</var> be the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-end title=concept-range-end>end</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-node title=concept-boundary-point-node>node</a> of
   <var title="">range</var>.
 
   <li>While <var title="">end block</var>'s <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a> is <a href=#in-the-same-editing-host>in the same editing
-  host</a>, and <var title="">end block</var> is not a <a href=#prohibited-paragraph-child>prohibited paragraph
-  child</a> or "span" is not an <a href=#allowed-child>allowed child</a> of <var title="">end
-  block</var> or <var title="">end block</var> is a <code class=external data-anolis-spec=html title="the td element"><a href=http://www.whatwg.org/html/#the-td-element>td</a></code> or <code class=external data-anolis-spec=html title="the th element"><a href=http://www.whatwg.org/html/#the-th-element>th</a></code>, set <var title="">end
-  block</var> to its <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a>.
-  <!--
-  We're willing to let start block be a td or th, but not end block.  This
-  means if the selection starts in a table cell and ends in some other block,
-  we'll merge the contents of the latter block into the cell, but not vice
-  versa.  This matches Chrome 13 dev and Opera 11.11.  Firefox 5.0a2 doesn't
-  allow merging either way, and in IE9 the delete key just does nothing.
-  -->
+  host</a> and <var title="">end block</var> is not a <a href=#prohibited-paragraph-child>prohibited paragraph
+  child</a>, set <var title="">end block</var> to its <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a>.
+
+  <li>If <var title="">end block</var> is neither a <a href=#prohibited-paragraph-child>prohibited paragraph
+  child</a> nor an <a href=#editing-host>editing host</a>, or "span" is not an
+  <a href=#allowed-child>allowed child</a> of <var title="">end block</var>, or <var title="">end block</var>
+  is a <code class=external data-anolis-spec=html title="the td element"><a href=http://www.whatwg.org/html/#the-td-element>td</a></code> or <code class=external data-anolis-spec=html title="the th element"><a href=http://www.whatwg.org/html/#the-th-element>th</a></code>, set <var title="">end block</var> to null.
 
   <!-- This is based on deleteData() in DOM Range. -->
   <li>If <var title="">start node</var> and <var title="">end node</var> are the same, and
@@ -1147,16 +1176,11 @@
   where one descends from the other.
   -->
 
-  <li>If <var title="">start block</var> is not <a href=#in-the-same-editing-host>in the same editing host</a> as
-  <var title="">end block</var>, or <var title="">start block</var> is neither an <a href=#editing-host>editing
-  host</a> nor a <a href=#prohibited-paragraph-child>prohibited paragraph child</a>, or <var title="">end
-  block</var> is neither an <a href=#editing-host>editing host</a> nor a <a href=#prohibited-paragraph-child>prohibited
-  paragraph child</a>, or "span" is not an <a href=#allowed-child>allowed child</a> of
-  <var title="">start block</var>, or "span" is not an <a href=#allowed-child>allowed child</a> of
-  <var title="">end block</var>, or <var title="">end block</var> is a <code class=external data-anolis-spec=html title="the td element"><a href=http://www.whatwg.org/html/#the-td-element>td</a></code> or <code class=external data-anolis-spec=html title="the th element"><a href=http://www.whatwg.org/html/#the-th-element>th</a></code>, or
-  <var title="">start block</var> and <var title="">end block</var> are the same, set
-  <var title="">range</var>'s <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-end title=concept-range-end>end</a> to its <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a> and then abort these
-  steps.
+  <li>If <var title="">start block</var> or <var title="">end block</var> is null, or <var title="">start
+  block</var> is not <a href=#in-the-same-editing-host>in the same editing host</a> as <var title="">end
+  block</var>, or <var title="">start block</var> and <var title="">end block</var> are the same,
+  set <var title="">range</var>'s <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-end title=concept-range-end>end</a> to its <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a> and then abort
+  these steps.
 
   <!--
   We might have added a br to the start/end block in an earlier step.  Now
--- a/implementation.js	Mon Jun 06 14:45:37 2011 -0600
+++ b/implementation.js	Mon Jun 06 15:02:25 2011 -0600
@@ -894,26 +894,39 @@
 	// "Let start block be the start node of range."
 	var startBlock = range.startContainer;
 
-	// "While start block's parent is in the same editing host, and start block
-	// is not a prohibited paragraph child or "span" is not an allowed child of
-	// start block, set start block to its parent."
+	// "While start block's parent is in the same editing host and start block
+	// is not a prohibited paragraph child, set start block to its parent."
 	while (inSameEditingHost(startBlock, startBlock.parentNode)
-	&& (!isProhibitedParagraphChild(startBlock)
-	|| !isAllowedChild("span", startBlock))) {
+	&& !isProhibitedParagraphChild(startBlock)) {
 		startBlock = startBlock.parentNode;
 	}
 
+	// "If start block is neither a prohibited paragraph child nor an editing
+	// host, or "span" is not an allowed child of start block, or start block
+	// is a td or th, set start block to null."
+	if ((!isProhibitedParagraphChild(startBlock) && !isEditingHost(startBlock))
+	|| !isAllowedChild("span", startBlock)
+	|| isHtmlElement(startBlock, ["td", "th"])) {
+		startBlock = null;
+	}
+
 	// "Let end block be the end node of range."
 	var endBlock = range.endContainer;
 
-	// "While end block's parent is in the same editing host, and end block is
-	// not a prohibited paragraph child or "span" is not an allowed child of
-	// end block or end block is a td or th, set end block to its parent."
+	// "While end block's parent is in the same editing host and end block is
+	// not a prohibited paragraph child, set end block to its parent."
 	while (inSameEditingHost(endBlock, endBlock.parentNode)
-	&& (!isProhibitedParagraphChild(endBlock)
+	&& !isProhibitedParagraphChild(endBlock)) {
+		endBlock = endBlock.parentNode;
+	}
+
+	// "If end block is neither a prohibited paragraph child nor an editing
+	// host, or "span" is not an allowed child of end block, or end block is a
+	// td or th, set end block to null."
+	if ((!isProhibitedParagraphChild(endBlock) && !isEditingHost(endBlock))
 	|| !isAllowedChild("span", endBlock)
-	|| isHtmlElement(endBlock, ["td", "th"]))) {
-		endBlock = endBlock.parentNode;
+	|| isHtmlElement(endBlock, ["td", "th"])) {
+		endBlock = null;
 	}
 
 	// "If start node and end node are the same, and start node is an editable
@@ -988,19 +1001,12 @@
 		}
 	}
 
-	// "If start block is not in the same editing host as end block, or start
-	// block is neither an editing host nor a prohibited paragraph child, or
-	// end block is neither an editing host nor a prohibited paragraph child,
-	// or "span" is not an allowed child of start block, or "span" is not an
-	// allowed child of end block, or end block is a td or th, or start block
-	// and end block are the same, set range's end to its start and then abort
-	// these steps."
-	if (!inSameEditingHost(startBlock, endBlock)
-	|| (!isEditingHost(startBlock) && !isProhibitedParagraphChild(startBlock))
-	|| (!isEditingHost(endBlock) && !isProhibitedParagraphChild(endBlock))
-	|| !isAllowedChild("span", startBlock)
-	|| !isAllowedChild("span", endBlock)
-	|| isHtmlElement(endBlock, ["td", "th"])
+	// "If start block or end block is null, or start block is not in the same
+	// editing host as end block, or start block and end block are the same,
+	// set range's end to its start and then abort these steps."
+	if (!startBlock
+	|| !endBlock
+	|| !inSameEditingHost(startBlock, endBlock)
 	|| startBlock == endBlock) {
 		range.setEnd(range.startContainer, range.startOffset);
 		return;
--- a/source.html	Mon Jun 06 14:45:37 2011 -0600
+++ b/source.html	Mon Jun 06 15:02:25 2011 -0600
@@ -1009,29 +1009,58 @@
   <var>start offset</var>) and its [[rangeend]] to (<var>end node</var>,
   <var>end offset</var>).
 
+  <!--
+  When we delete a selection that spans multiple blocks, we merge the end
+  block's contents into the start block, like
+
+    <p>fo[o</p><pre>b]ar</pre>
+    -> <p>fo[]ar</p>.
+
+  Figure out what the start and end blocks are before we start deleting
+  anything.
+  -->
   <li>Let <var>start block</var> be the [[rangestart]] [[bpnode]] of
   <var>range</var>.
 
   <li>While <var>start block</var>'s [[parent]] is <span>in the same editing
-  host</span>, and <var>start block</var> is not a <span>prohibited paragraph
-  child</span> or "span" is not an <span>allowed child</span> of <var>start
-  block</var>, set <var>start block</var> to its [[parent]].
+  host</span> and <var>start block</var> is not a <span>prohibited paragraph
+  child</span>, set <var>start block</var> to its [[parent]].
+
+  <li>If <var>start block</var> is neither a <span>prohibited paragraph
+  child</span> nor an <span>editing host</span>, or "span" is not an
+  <span>allowed child</span> of <var>start block</var>, or <var>start
+  block</var> is a [[td]] or [[th]], set <var>start block</var> to null.
+  <!--
+  We only merge to or from prohibited paragraph children or editing hosts.
+  (This is just in case someone makes a span into an editing host and sticks
+  paragraphs inside it or something . . . we could probably drop that proviso.)
+  Anything else is presumed to be an inline element, basically.  This might not
+  be ideal.
+
+  If span isn't an allowed child, it's probably something unpleasant like a
+  table row or a list or such.  We don't want to merge to or from something
+  like that, because we'd most likely wind up with the wrong type of child
+  somewhere.  Although we probably want to allow merging different lists or
+  such this way . . .
+
+  We don't let either start block or end block be a td or th.  This means we'll
+  never merge to or from a td or th.  This matches Firefox 5.0a2, and
+  reportedly Word as well.  Chrome 13 dev and Opera 11.11 allow merging from a
+  non-table cell end block to a table cell start block, but not vice versa.  In
+  IE9 the delete key just does nothing.
+  -->
 
   <li>Let <var>end block</var> be the [[rangeend]] [[bpnode]] of
   <var>range</var>.
 
   <li>While <var>end block</var>'s [[parent]] is <span>in the same editing
-  host</span>, and <var>end block</var> is not a <span>prohibited paragraph
-  child</span> or "span" is not an <span>allowed child</span> of <var>end
-  block</var> or <var>end block</var> is a [[td]] or [[th]], set <var>end
-  block</var> to its [[parent]].
-  <!--
-  We're willing to let start block be a td or th, but not end block.  This
-  means if the selection starts in a table cell and ends in some other block,
-  we'll merge the contents of the latter block into the cell, but not vice
-  versa.  This matches Chrome 13 dev and Opera 11.11.  Firefox 5.0a2 doesn't
-  allow merging either way, and in IE9 the delete key just does nothing.
-  -->
+  host</span> and <var>end block</var> is not a <span>prohibited paragraph
+  child</span>, set <var>end block</var> to its [[parent]].
+
+  <li>If <var>end block</var> is neither a <span>prohibited paragraph
+  child</span> nor an <span>editing host</span>, or "span" is not an
+  <span>allowed child</span> of <var>end block</var>, or <var>end block</var>
+  is a [[td]] or [[th]], set <var>end block</var> to null.
 
   <!-- This is based on deleteData() in DOM Range. -->
   <li>If <var>start node</var> and <var>end node</var> are the same, and
@@ -1108,16 +1137,11 @@
   where one descends from the other.
   -->
 
-  <li>If <var>start block</var> is not <span>in the same editing host</span> as
-  <var>end block</var>, or <var>start block</var> is neither an <span>editing
-  host</span> nor a <span>prohibited paragraph child</span>, or <var>end
-  block</var> is neither an <span>editing host</span> nor a <span>prohibited
-  paragraph child</span>, or "span" is not an <span>allowed child</span> of
-  <var>start block</var>, or "span" is not an <span>allowed child</span> of
-  <var>end block</var>, or <var>end block</var> is a [[td]] or [[th]], or
-  <var>start block</var> and <var>end block</var> are the same, set
-  <var>range</var>'s [[rangeend]] to its [[rangestart]] and then abort these
-  steps.
+  <li>If <var>start block</var> or <var>end block</var> is null, or <var>start
+  block</var> is not <span>in the same editing host</span> as <var>end
+  block</var>, or <var>start block</var> and <var>end block</var> are the same,
+  set <var>range</var>'s [[rangeend]] to its [[rangestart]] and then abort
+  these steps.
 
   <!--
   We might have added a br to the start/end block in an earlier step.  Now