Bug 1642588 - Make Triple click only select within editing host r=masayuki,emilio
☠☠ backed out by 3f947729cb8a ☠ ☠
authorKagami Sascha Rosylight <saschanaz@outlook.com>
Fri, 05 Jun 2020 22:45:30 +0000
changeset 534227 6e99a3e6c77b16b41f40d0edaada99a3e47a1927
parent 534226 f5f4f276e1711757b993dd454def121df87cf3ec
child 534228 8c26c9055e3d136512ffcd168b7cc8769b34ce08
push id37484
push userdluca@mozilla.com
push dateSat, 06 Jun 2020 09:46:03 +0000
treeherdermozilla-central@6237102f005d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmasayuki, emilio
bugs1642588
milestone79.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1642588 - Make Triple click only select within editing host r=masayuki,emilio Differential Revision: https://phabricator.services.mozilla.com/D78315
editor/libeditor/tests/mochitest.ini
editor/libeditor/tests/test_bug1642588.html
layout/generic/nsFrame.cpp
--- a/editor/libeditor/tests/mochitest.ini
+++ b/editor/libeditor/tests/mochitest.ini
@@ -223,16 +223,17 @@ skip-if = headless
 skip-if = toolkit == 'android'
 [test_bug1543312.html]
 [test_bug1568996.html]
 [test_bug1574596.html]
 skip-if = os == "android" #Bug 1575739
 [test_bug1581337.html]
 [test_bug1619852.html]
 [test_bug1620778.html]
+[test_bug1642588.html]
 [test_abs_positioner_appearance.html]
 [test_abs_positioner_positioning_elements.html]
 skip-if = os == 'android' # Bug 1525959
 [test_CF_HTML_clipboard.html]
 skip-if = os != 'mac' # bug 574005
 [test_cmd_fontFace_with_tt.html]
 [test_composition_event_created_in_chrome.html]
 [test_contenteditable_focus.html]
new file mode 100644
--- /dev/null
+++ b/editor/libeditor/tests/test_bug1642588.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<title>Test for Bug 1642588</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css">
+<style>
+  [contenteditable] {
+    padding: .5em 40%;
+  }
+</style>
+
+<div>
+  <p>
+    Intentionally not in WPT as triple click is not guaranteed to have same
+    behavior on every browser.
+  </p>
+  <span contenteditable></span><hr>
+  <div contenteditable style="display: inline;"></div><hr>
+  <div contenteditable style="display: inline-block;"></div><hr>
+  <table contenteditable style="display: inline-table;"><tr><td></table><hr>
+  <div contenteditable style="display: inline-flex;"></div><hr>
+  <div contenteditable style="display: inline-grid;"></div>
+</div>
+
+<script>
+function selectHostByClicks(host, number) {
+  for (let i = 1; i <= number; i++) {
+    synthesizeMouseAtCenter(host, { clickCount: i });
+  }
+}
+
+function nonCollapsedDeletionTest(host, clickCount) {
+  host.textContent = "contenteditable";
+  selectHostByClicks(host, clickCount);
+  const selection = getSelection();
+
+  is(selection.isCollapsed, false, "Noncollapsed selection should occur");
+
+  synthesizeKey("KEY_Backspace");
+  is(host.textContent, "", "Backspace should delete the content of the editing host");
+}
+
+function collapsedSelectionTest(host, clickCount) {
+  host.textContent = "";
+  selectHostByClicks(host, clickCount);
+  const selection = getSelection();
+
+  is(selection.isCollapsed, true, "Collapsed selection should occur");
+  is(selection.anchorNode, host, "Caret should be in host");
+}
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(() => {
+  const hosts = document.querySelectorAll("[contenteditable]");
+  for (const host of hosts) {
+    nonCollapsedDeletionTest(host, 2);
+    nonCollapsedDeletionTest(host, 3);
+    collapsedSelectionTest(host, 3);
+  }
+  SimpleTest.finish();
+});
+</script>
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -8168,33 +8168,33 @@ static nsContentAndOffset FindLineBreaki
   }
   return result;
 }
 
 nsresult nsIFrame::PeekOffsetParagraph(nsPeekOffsetStruct* aPos) {
   nsIFrame* frame = this;
   nsContentAndOffset blockFrameOrBR;
   blockFrameOrBR.mContent = nullptr;
-  bool reachedBlockAncestor = frame->IsBlockOutside();
+  bool reachedLimit = frame->IsBlockOutside() || IsEditingHost(frame);
 
   auto traverse = [&aPos](nsIFrame* current) {
     return aPos->mDirection == eDirPrevious ? current->GetPrevSibling()
                                             : current->GetNextSibling();
   };
 
   // Go through containing frames until reaching a block frame.
   // In each step, search the previous (or next) siblings for the closest
   // "stop frame" (a block frame or a BRFrame).
   // If found, set it to be the selection boundary and abort.
-  while (!reachedBlockAncestor) {
+  while (!reachedLimit) {
     nsIFrame* parent = frame->GetParent();
     // Treat a frame associated with the root content as if it were a block
     // frame.
     if (!frame->mContent || !frame->mContent->GetParent()) {
-      reachedBlockAncestor = true;
+      reachedLimit = true;
       break;
     }
 
     if (aPos->mDirection == eDirNext) {
       // Try to find our own line-break before looking at our siblings.
       blockFrameOrBR = FindLineBreakInText(frame, eDirNext);
     }
 
@@ -8204,20 +8204,20 @@ nsresult nsIFrame::PeekOffsetParagraph(n
       sibling = traverse(sibling);
     }
     if (blockFrameOrBR.mContent) {
       aPos->mResultContent = blockFrameOrBR.mContent;
       aPos->mContentOffset = blockFrameOrBR.mOffset;
       break;
     }
     frame = parent;
-    reachedBlockAncestor = frame && frame->IsBlockOutside();
-  }
-
-  if (reachedBlockAncestor) {  // no "stop frame" found
+    reachedLimit = frame && (frame->IsBlockOutside() || IsEditingHost(frame));
+  }
+
+  if (reachedLimit) {  // no "stop frame" found
     aPos->mResultContent = frame->GetContent();
     if (aPos->mDirection == eDirPrevious) {
       aPos->mContentOffset = 0;
     } else if (aPos->mResultContent) {
       aPos->mContentOffset = aPos->mResultContent->GetChildCount();
     }
   }
   return NS_OK;