move-around-contenteditable-false.html (6265B)
1 <!doctype html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title>Don't move caret to non-editable node from a editable node</title> 6 <script src="/resources/testharness.js"></script> 7 <script src="/resources/testharnessreport.js"></script> 8 <script src="/resources/testdriver.js"></script> 9 <script src="/resources/testdriver-vendor.js"></script> 10 <script src="/resources/testdriver-actions.js"></script> 11 <script> 12 "use strict"; 13 14 function getRangeDescription(range) { 15 function getNodeDescription(node) { 16 if (!node) { 17 return "null"; 18 } 19 switch (node.nodeType) { 20 case Node.TEXT_NODE: 21 return `${node.nodeName} "${node.data}"`; 22 case Node.ELEMENT_NODE: 23 return `<${node.nodeName.toLowerCase()}>`; 24 default: 25 return `${node.nodeName}`; 26 } 27 } 28 if (range === null) { 29 return "null"; 30 } 31 if (range === undefined) { 32 return "undefined"; 33 } 34 return range.startContainer == range.endContainer && 35 range.startOffset == range.endOffset 36 ? `(${getNodeDescription(range.startContainer)}, ${range.startOffset})` 37 : `(${getNodeDescription(range.startContainer)}, ${ 38 range.startOffset 39 }) - (${getNodeDescription(range.endContainer)}, ${range.endOffset})`; 40 } 41 42 function sendArrowRightKey() { 43 const kArrowRight = "\uE014"; 44 return new test_driver.Actions() 45 .keyDown(kArrowRight) 46 .keyUp(kArrowRight) 47 .send(); 48 } 49 50 function sendArrowLeftKey() { 51 const kArrowLeft = "\uE012"; 52 return new test_driver.Actions() 53 .keyDown(kArrowLeft) 54 .keyUp(kArrowLeft) 55 .send(); 56 } 57 58 promise_test(async () => { 59 await new Promise(resolve => { 60 addEventListener("load", resolve, {once: true}); 61 }); 62 }, "Initializing tests"); 63 64 promise_test(async t => { 65 const editingHost = document.querySelector("div[contenteditable]"); 66 editingHost.focus(); 67 const p = editingHost.querySelector("p"); 68 getSelection().collapse(p.firstChild, "abc".length); 69 await sendArrowRightKey(); 70 test(() => { 71 assert_equals( 72 getRangeDescription(getSelection().getRangeAt(0)), 73 getRangeDescription({ 74 startContainer: p.nextSibling, 75 startOffset: 0, 76 endContainer: p.nextSibling, 77 endOffset: 0, 78 }), 79 ); 80 }, `${t.name}: first arrow-right should move caret before non-editable text`); 81 await sendArrowRightKey(); 82 test(() => { 83 assert_equals( 84 getRangeDescription(getSelection().getRangeAt(0)), 85 getRangeDescription({ 86 startContainer: p.nextSibling, 87 startOffset: 1, 88 endContainer: p.nextSibling, 89 endOffset: 1, 90 }), 91 ); 92 }, `${t.name}: second arrow-right should move caret after non-editable text`); 93 }, "Move caret from end of editable text node to <br> following non-editable text in next paragraph"); 94 95 promise_test(async t => { 96 const editingHost = document.querySelector("div[contenteditable]"); 97 editingHost.focus(); 98 const p = editingHost.querySelector("p"); 99 getSelection().collapse(p.nextSibling, 1); 100 await sendArrowLeftKey(); 101 assert_false( 102 editingHost.querySelector("[contenteditable=false]").contains(getSelection().focusNode), 103 "focus node should not be the non-editable nodes" 104 ); 105 assert_false( 106 editingHost.querySelector("[contenteditable=false]").contains(getSelection().anchorNode), 107 "anchor node should not be the non-editable nodes" 108 ); 109 test(() => { 110 assert_equals( 111 getRangeDescription(getSelection().getRangeAt(0)), 112 getRangeDescription({ 113 startContainer: p.nextSibling, 114 startOffset: 0, 115 endContainer: p.nextSibling, 116 endOffset: 0, 117 }), 118 ); 119 }, `${t.name}: first arrow-left should move caret before non-editable text`); 120 }, "Move caret from <br> following non-editable text to end of preceding editable text in next paragraph"); 121 122 promise_test(async t => { 123 const editingHost = document.querySelector("div[contenteditable] + div[contenteditable]"); 124 editingHost.focus(); 125 const p = editingHost.querySelector("p"); 126 getSelection().collapse(p.firstChild, 0); 127 await sendArrowRightKey(); 128 test(() => { 129 assert_equals( 130 getRangeDescription(getSelection().getRangeAt(0)), 131 getRangeDescription({ 132 startContainer: p.nextSibling, 133 startOffset: 0, 134 endContainer: p.nextSibling, 135 endOffset: 0, 136 }), 137 ); 138 }, `${t.name}: first arrow-right should move caret before non-editable text`); 139 await sendArrowRightKey(); 140 test(() => { 141 assert_equals( 142 getRangeDescription(getSelection().getRangeAt(0)), 143 getRangeDescription({ 144 startContainer: editingHost.querySelector("[contenteditable=false]").nextSibling, 145 startOffset: 0, 146 endContainer: editingHost.querySelector("[contenteditable=false]").nextSibling, 147 endOffset: 0, 148 }), 149 ); 150 }, `${t.name}: second arrow-right should move caret after non-editable text`); 151 }, "Move caret from empty editable paragraph to editable text following non-editable text in next paragraph"); 152 153 promise_test(async t => { 154 const editingHost = document.querySelector("div[contenteditable] + div[contenteditable]"); 155 editingHost.focus(); 156 const p = editingHost.querySelector("p"); 157 getSelection().collapse(editingHost.querySelector("[contenteditable=false]").nextSibling, 0); 158 await sendArrowLeftKey(); 159 assert_false( 160 editingHost.querySelector("[contenteditable=false]").contains(getSelection().focusNode), 161 "focus node should not be the non-editable nodes" 162 ); 163 assert_false( 164 editingHost.querySelector("[contenteditable=false]").contains(getSelection().anchorNode), 165 "anchor node should not be the non-editable nodes" 166 ); 167 test(() => { 168 assert_equals( 169 getRangeDescription(getSelection().getRangeAt(0)), 170 getRangeDescription({ 171 startContainer: p.nextSibling, 172 startOffset: 0, 173 endContainer: p.nextSibling, 174 endOffset: 0, 175 }), 176 ); 177 }, `${t.name}: first arrow-left should move caret before non-editable text`); 178 }, "Move caret from start of text following non-editable text to empty preceding editable paragraph"); 179 </script> 180 </head> 181 <body> 182 <div contenteditable> 183 <p>abc</p><p><span contenteditable="false">def</span><br></p> 184 </div> 185 <div contenteditable> 186 <p><br></p><p><span contenteditable="false">abc</span>def</p> 187 </div> 188 </body> 189 </html>