Changeset 127776 in webkit
- Timestamp:
- Sep 6, 2012 12:52:07 PM (12 years ago)
- Location:
- trunk/Source/WebKit/chromium
- Files:
-
- 1 added
- 5 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebKit/chromium/ChangeLog
r127767 r127776 1 2012-09-06 Iain Merrick <husky@chromium.org> 2 3 [chromium] Add WebFrame::moveSelectionStart, moveSelectionEnd, moveCaret 4 https://bugs.webkit.org/show_bug.cgi?id=93998 5 6 Reviewed by Adam Barth. 7 8 These provide the same functionality selectRange(WebPoint, WebPoint), 9 with finer-grained control needed on the Android platform. By passing 10 allowCollapsedSelection=false, we can ensure the selection stays at 11 least one character wide. 12 13 I have reimplemented WebFrameImpl::selectRange(WebPoint, WebPoint) by 14 calling the new methods. The existing test passes, and I've added new 15 tests for the new methods. 16 17 * public/WebFrame.h: 18 (WebFrame): 19 * src/WebFrameImpl.cpp: 20 (WebKit::WebFrameImpl::selectRange): 21 (WebKit): 22 (WebKit::WebFrameImpl::moveSelectionStart): 23 (WebKit::WebFrameImpl::moveSelectionEnd): 24 (WebKit::WebFrameImpl::moveCaret): 25 * src/WebFrameImpl.h: 26 (WebFrameImpl): 27 * tests/WebFrameTest.cpp: 28 * tests/data/text_selection.html: Added. 29 1 30 2012-09-06 Robert Kroeger <rjkroege@chromium.org> 2 31 -
trunk/Source/WebKit/chromium/public/WebFrame.h
r126609 r127776 473 473 virtual bool selectWordAroundCaret() = 0; 474 474 475 // DEPRECATED: Use moveSelectionStart / moveSelectionEnd / moveCaret 476 // This method is intended for touch-based UIs, but it's missing some 477 // functionality needed on Android, like preventing collapsed selections. 475 478 virtual void selectRange(const WebPoint& start, const WebPoint& end) = 0; 476 479 477 480 virtual void selectRange(const WebRange&) = 0; 478 481 482 // The methods below are for adjusting the start and/or end of the current 483 // selection by direct manipulation on a touch-based UI. To enter selection 484 // mode in the first place, call selectRange() or send a fake mouse event. 485 486 // Moves the start of the current selection, keeping the end fixed. 487 // Returns true on success, false if there is no selection to modify. 488 virtual bool moveSelectionStart(const WebPoint&, bool allowCollapsedSelection) = 0; 489 490 // Moves the end of the current selection, keeping the start fixed. 491 // Returns true on success, false if there is no selection to modify. 492 virtual bool moveSelectionEnd(const WebPoint&, bool allowCollapsedSelection) = 0; 493 494 // Move both endpoints of the current selection to the given position. 495 // The caret will remain pinned inside the current editable region. 496 // Returns true on success, false if there is no selection or if we're not editing. 497 virtual bool moveCaret(const WebPoint&) = 0; 479 498 480 499 // Printing ------------------------------------------------------------ -
trunk/Source/WebKit/chromium/src/WebFrameImpl.cpp
r126609 r127776 171 171 #include "WebViewImpl.h" 172 172 #include "XPathResult.h" 173 #include "htmlediting.h" 173 174 #include "markup.h" 174 175 #include "painting/GraphicsContextBuilder.h" … … 1466 1467 void WebFrameImpl::selectRange(const WebPoint& start, const WebPoint& end) 1467 1468 { 1468 VisiblePosition startPosition = visiblePositionForWindowPoint(start); 1469 VisiblePosition endPosition = visiblePositionForWindowPoint(end); 1470 1471 // To correctly handle editable boundaries, we adjust the selection by setting its extent 1472 // while keeping its base fixed. For a touch-based UI, this means that moving the selection 1473 // handles behaves like a drag-select with the mouse, which is what we want here. If both 1474 // endpoints changed, we need to set the extent twice. 1475 // FIXME: the WebFrame::SelectRange API should explicitly state which endpoint is moving. 1476 VisibleSelection newSelection = frame()->selection()->selection(); 1477 if (startPosition != newSelection.visibleStart()) 1478 newSelection = VisibleSelection(newSelection.visibleEnd(), startPosition); 1479 if (endPosition != newSelection.visibleEnd()) 1480 newSelection = VisibleSelection(newSelection.visibleStart(), endPosition); 1481 1469 if (start == end && moveCaret(start)) 1470 return; 1471 1472 if (moveSelectionStart(start, true) && moveSelectionEnd(end, true)) 1473 return; 1474 1475 // Failed to move endpoints, probably because there's no current selection. 1476 // Just set the selection explicitly (but this won't handle editable boundaries correctly). 1477 VisibleSelection newSelection(visiblePositionForWindowPoint(start), visiblePositionForWindowPoint(end)); 1482 1478 if (frame()->selection()->shouldChangeSelection(newSelection)) 1483 1479 frame()->selection()->setSelection(newSelection, CharacterGranularity); 1480 } 1481 1482 bool WebFrameImpl::moveSelectionStart(const WebPoint& point, bool allowCollapsedSelection) 1483 { 1484 const VisibleSelection& selection = frame()->selection()->selection(); 1485 if (selection.isNone()) 1486 return false; 1487 1488 VisiblePosition start = visiblePositionForWindowPoint(point); 1489 if (!allowCollapsedSelection) { 1490 VisiblePosition maxStart = selection.visibleEnd().previous(); 1491 if (comparePositions(start, maxStart) > 0) 1492 start = maxStart; 1493 } 1494 1495 // start is moving, so base=end, extent=start 1496 VisibleSelection newSelection = VisibleSelection(selection.visibleEnd(), start); 1497 frame()->selection()->setNonDirectionalSelectionIfNeeded(newSelection, CharacterGranularity); 1498 return true; 1499 } 1500 1501 bool WebFrameImpl::moveSelectionEnd(const WebPoint& point, bool allowCollapsedSelection) 1502 { 1503 const VisibleSelection& selection = frame()->selection()->selection(); 1504 if (selection.isNone()) 1505 return false; 1506 1507 VisiblePosition end = visiblePositionForWindowPoint(point); 1508 if (!allowCollapsedSelection) { 1509 VisiblePosition minEnd = selection.visibleStart().next(); 1510 if (comparePositions(end, minEnd) < 0) 1511 end = minEnd; 1512 } 1513 1514 // end is moving, so base=start, extent=end 1515 VisibleSelection newSelection = VisibleSelection(selection.visibleStart(), end); 1516 frame()->selection()->setNonDirectionalSelectionIfNeeded(newSelection, CharacterGranularity); 1517 return true; 1518 } 1519 1520 bool WebFrameImpl::moveCaret(const WebPoint& point) 1521 { 1522 FrameSelection* frameSelection = frame()->selection(); 1523 if (frameSelection->isNone() || !frameSelection->isContentEditable()) 1524 return false; 1525 1526 VisiblePosition pos = visiblePositionForWindowPoint(point); 1527 frameSelection->setExtent(pos, UserTriggered); 1528 frameSelection->setBase(frameSelection->extent(), UserTriggered); 1529 return true; 1484 1530 } 1485 1531 -
trunk/Source/WebKit/chromium/src/WebFrameImpl.h
r127757 r127776 187 187 virtual void selectRange(const WebPoint& start, const WebPoint& end); 188 188 virtual void selectRange(const WebRange&); 189 virtual bool moveSelectionStart(const WebPoint&, bool allowCollapsedSelection); 190 virtual bool moveSelectionEnd(const WebPoint&, bool allowCollapsedSelection); 191 virtual bool moveCaret(const WebPoint&); 189 192 virtual int printBegin(const WebPrintParams&, 190 193 const WebNode& constrainToNode, -
trunk/Source/WebKit/chromium/tests/WebFrameTest.cpp
r127690 r127776 991 991 } 992 992 993 static WebView* selectRangeTestCreateWebView(const std::string& url)993 static WebView* createWebViewForTextSelection(const std::string& url) 994 994 { 995 995 WebView* webView = FrameTestHelpers::createWebViewAndLoad(url, true); 996 996 webView->settings()->setDefaultFontSize(12); 997 webView->enableFixedLayoutMode(false); 997 998 webView->resize(WebSize(640, 480)); 998 999 return webView; … … 1013 1014 } 1014 1015 1016 static WebRect elementBounds(WebFrame* frame, const WebString& id) 1017 { 1018 return frame->document().getElementById(id).boundsInViewportSpace(); 1019 } 1020 1015 1021 static std::string selectionAsString(WebFrame* frame) 1016 1022 { … … 1030 1036 registerMockedHttpURLLoad("select_range_editable.html"); 1031 1037 1032 webView = selectRangeTestCreateWebView(m_baseURL + "select_range_basic.html");1038 webView = createWebViewForTextSelection(m_baseURL + "select_range_basic.html"); 1033 1039 frame = webView->mainFrame(); 1034 1040 EXPECT_EQ("Some test text for testing.", selectionAsString(frame)); … … 1040 1046 webView->close(); 1041 1047 1042 webView = selectRangeTestCreateWebView(m_baseURL + "select_range_scroll.html");1048 webView = createWebViewForTextSelection(m_baseURL + "select_range_scroll.html"); 1043 1049 frame = webView->mainFrame(); 1044 1050 EXPECT_EQ("Some offscreen test text for testing.", selectionAsString(frame)); … … 1050 1056 webView->close(); 1051 1057 1052 webView = selectRangeTestCreateWebView(m_baseURL + "select_range_iframe.html");1058 webView = createWebViewForTextSelection(m_baseURL + "select_range_iframe.html"); 1053 1059 frame = webView->mainFrame(); 1054 1060 WebFrame* subframe = frame->findChildByExpression(WebString::fromUTF8("/html/body/iframe")); … … 1063 1069 // Select the middle of an editable element, then try to extend the selection to the top of the document. 1064 1070 // The selection range should be clipped to the bounds of the editable element. 1065 webView = selectRangeTestCreateWebView(m_baseURL + "select_range_editable.html");1071 webView = createWebViewForTextSelection(m_baseURL + "select_range_editable.html"); 1066 1072 frame = webView->mainFrame(); 1067 1073 EXPECT_EQ("This text is initially selected.", selectionAsString(frame)); … … 1072 1078 1073 1079 // As above, but extending the selection to the bottom of the document. 1074 webView = selectRangeTestCreateWebView(m_baseURL + "select_range_editable.html");1080 webView = createWebViewForTextSelection(m_baseURL + "select_range_editable.html"); 1075 1081 frame = webView->mainFrame(); 1076 1082 EXPECT_EQ("This text is initially selected.", selectionAsString(frame)); … … 1078 1084 frame->selectRange(topLeft(startWebRect), WebPoint(640, 480)); 1079 1085 EXPECT_EQ("This text is initially selected. 16-char footer.", selectionAsString(frame)); 1086 webView->close(); 1087 } 1088 1089 TEST_F(WebFrameTest, MoveSelectionStart) 1090 { 1091 registerMockedHttpURLLoad("text_selection.html"); 1092 WebView* webView = createWebViewForTextSelection(m_baseURL + "text_selection.html"); 1093 WebFrame* frame = webView->mainFrame(); 1094 1095 // moveSelectionStart() always returns false if there's no selection. 1096 EXPECT_FALSE(frame->moveSelectionStart(topLeft(elementBounds(frame, "header_1")), false)); 1097 EXPECT_FALSE(frame->moveSelectionStart(topLeft(elementBounds(frame, "header_1")), true)); 1098 1099 frame->executeScript(WebScriptSource("selectElement('header_1');")); 1100 EXPECT_EQ("Header 1.", selectionAsString(frame)); 1101 EXPECT_TRUE(frame->moveSelectionEnd(bottomRightMinusOne(elementBounds(frame, "header_2")), false)); 1102 EXPECT_EQ("Header 1. Header 2.", selectionAsString(frame)); 1103 1104 // Select second span. We can move the start to include the first span. 1105 frame->executeScript(WebScriptSource("selectElement('header_2');")); 1106 EXPECT_EQ("Header 2.", selectionAsString(frame)); 1107 EXPECT_TRUE(frame->moveSelectionStart(topLeft(elementBounds(frame, "header_1")), false)); 1108 EXPECT_EQ("Header 1. Header 2.", selectionAsString(frame)); 1109 1110 // If allowCollapsedSelection=false we can't move the selection start beyond the current end. 1111 // We end up with a single character selected. 1112 frame->executeScript(WebScriptSource("selectElement('header_1');")); 1113 EXPECT_EQ("Header 1.", selectionAsString(frame)); 1114 EXPECT_TRUE(frame->moveSelectionStart(bottomRightMinusOne(elementBounds(frame, "header_1")), false)); 1115 EXPECT_EQ(".", selectionAsString(frame)); 1116 1117 // If allowCollapsedSelection=true we can move the start and end together. 1118 frame->executeScript(WebScriptSource("selectElement('header_1');")); 1119 EXPECT_EQ("Header 1.", selectionAsString(frame)); 1120 EXPECT_TRUE(frame->moveSelectionStart(bottomRightMinusOne(elementBounds(frame, "header_1")), true)); 1121 EXPECT_EQ("", selectionAsString(frame)); 1122 // Selection is a caret, not empty. 1123 EXPECT_FALSE(frame->selectionRange().isNull()); 1124 EXPECT_TRUE(frame->moveSelectionStart(topLeft(elementBounds(frame, "header_1")), true)); 1125 EXPECT_EQ("Header 1.", selectionAsString(frame)); 1126 1127 // If allowCollapsedSelection=true we can move the start across the end. 1128 frame->executeScript(WebScriptSource("selectElement('header_1');")); 1129 EXPECT_EQ("Header 1.", selectionAsString(frame)); 1130 EXPECT_TRUE(frame->moveSelectionStart(bottomRightMinusOne(elementBounds(frame, "header_2")), true)); 1131 EXPECT_EQ(" Header 2.", selectionAsString(frame)); 1132 1133 // Can't extend the selection part-way into an editable element. 1134 frame->executeScript(WebScriptSource("selectElement('footer_2');")); 1135 EXPECT_EQ("Footer 2.", selectionAsString(frame)); 1136 EXPECT_TRUE(frame->moveSelectionStart(topLeft(elementBounds(frame, "editable_2")), true)); 1137 EXPECT_EQ(" [ Footer 1. Footer 2.", selectionAsString(frame)); 1138 1139 // Can extend the selection completely across editable elements. 1140 frame->executeScript(WebScriptSource("selectElement('footer_2');")); 1141 EXPECT_EQ("Footer 2.", selectionAsString(frame)); 1142 EXPECT_TRUE(frame->moveSelectionStart(topLeft(elementBounds(frame, "header_2")), true)); 1143 EXPECT_EQ("Header 2. ] [ Editable 1. Editable 2. ] [ Footer 1. Footer 2.", selectionAsString(frame)); 1144 1145 // If the selection is editable text, we can't extend it into non-editable text. 1146 frame->executeScript(WebScriptSource("selectElement('editable_2');")); 1147 EXPECT_EQ("Editable 2.", selectionAsString(frame)); 1148 EXPECT_TRUE(frame->moveSelectionStart(topLeft(elementBounds(frame, "header_2")), true)); 1149 EXPECT_EQ("[ Editable 1. Editable 2.", selectionAsString(frame)); 1150 1151 webView->close(); 1152 } 1153 1154 TEST_F(WebFrameTest, MoveSelectionEnd) 1155 { 1156 registerMockedHttpURLLoad("text_selection.html"); 1157 WebView* webView = createWebViewForTextSelection(m_baseURL + "text_selection.html"); 1158 WebFrame* frame = webView->mainFrame(); 1159 1160 // moveSelectionEnd() always returns false if there's no selection. 1161 EXPECT_FALSE(frame->moveSelectionEnd(topLeft(elementBounds(frame, "header_1")), false)); 1162 EXPECT_FALSE(frame->moveSelectionEnd(topLeft(elementBounds(frame, "header_1")), true)); 1163 1164 // Select first span. We can move the end to include the second span. 1165 frame->executeScript(WebScriptSource("selectElement('header_1');")); 1166 EXPECT_EQ("Header 1.", selectionAsString(frame)); 1167 EXPECT_TRUE(frame->moveSelectionEnd(bottomRightMinusOne(elementBounds(frame, "header_2")), false)); 1168 EXPECT_EQ("Header 1. Header 2.", selectionAsString(frame)); 1169 1170 // If allowCollapsedSelection=false we can't move the selection end beyond the current start. 1171 // We end up with a single character selected. 1172 frame->executeScript(WebScriptSource("selectElement('header_2');")); 1173 EXPECT_EQ("Header 2.", selectionAsString(frame)); 1174 EXPECT_TRUE(frame->moveSelectionEnd(topLeft(elementBounds(frame, "header_2")), false)); 1175 EXPECT_EQ("H", selectionAsString(frame)); 1176 1177 // If allowCollapsedSelection=true we can move the start and end together. 1178 frame->executeScript(WebScriptSource("selectElement('header_2');")); 1179 EXPECT_EQ("Header 2.", selectionAsString(frame)); 1180 EXPECT_TRUE(frame->moveSelectionEnd(topLeft(elementBounds(frame, "header_2")), true)); 1181 EXPECT_EQ("", selectionAsString(frame)); 1182 // Selection is a caret, not empty. 1183 EXPECT_FALSE(frame->selectionRange().isNull()); 1184 EXPECT_TRUE(frame->moveSelectionEnd(bottomRightMinusOne(elementBounds(frame, "header_2")), true)); 1185 EXPECT_EQ("Header 2.", selectionAsString(frame)); 1186 1187 // If allowCollapsedSelection=true we can move the end across the start. 1188 frame->executeScript(WebScriptSource("selectElement('header_2');")); 1189 EXPECT_EQ("Header 2.", selectionAsString(frame)); 1190 EXPECT_TRUE(frame->moveSelectionEnd(topLeft(elementBounds(frame, "header_1")), true)); 1191 EXPECT_EQ("Header 1. ", selectionAsString(frame)); 1192 1193 // Can't extend the selection part-way into an editable element. 1194 frame->executeScript(WebScriptSource("selectElement('header_1');")); 1195 EXPECT_EQ("Header 1.", selectionAsString(frame)); 1196 EXPECT_TRUE(frame->moveSelectionEnd(bottomRightMinusOne(elementBounds(frame, "editable_1")), true)); 1197 EXPECT_EQ("Header 1. Header 2. ] ", selectionAsString(frame)); 1198 1199 // Can extend the selection completely across editable elements. 1200 frame->executeScript(WebScriptSource("selectElement('header_1');")); 1201 EXPECT_EQ("Header 1.", selectionAsString(frame)); 1202 EXPECT_TRUE(frame->moveSelectionEnd(bottomRightMinusOne(elementBounds(frame, "footer_1")), true)); 1203 EXPECT_EQ("Header 1. Header 2. ] [ Editable 1. Editable 2. ] [ Footer 1.", selectionAsString(frame)); 1204 1205 // If the selection is editable text, we can't extend it into non-editable text. 1206 frame->executeScript(WebScriptSource("selectElement('editable_1');")); 1207 EXPECT_EQ("Editable 1.", selectionAsString(frame)); 1208 EXPECT_TRUE(frame->moveSelectionEnd(bottomRightMinusOne(elementBounds(frame, "footer_1")), true)); 1209 EXPECT_EQ("Editable 1. Editable 2. ]", selectionAsString(frame)); 1210 1211 webView->close(); 1212 } 1213 1214 TEST_F(WebFrameTest, MoveCaret) 1215 { 1216 registerMockedHttpURLLoad("text_selection.html"); 1217 WebView* webView = createWebViewForTextSelection(m_baseURL + "text_selection.html"); 1218 WebFrame* frame = webView->mainFrame(); 1219 1220 // moveCaret() returns false if there's no selection, or if it isn't editable. 1221 EXPECT_FALSE(frame->moveCaret(topLeft(elementBounds(frame, "editable")))); 1222 frame->executeScript(WebScriptSource("selectElement('header_1');")); 1223 EXPECT_EQ("Header 1.", selectionAsString(frame)); 1224 EXPECT_FALSE(frame->moveCaret(topLeft(elementBounds(frame, "editable")))); 1225 1226 // Select the editable text span. Now moveCaret() works. 1227 frame->executeScript(WebScriptSource("selectElement('editable_1');")); 1228 EXPECT_EQ("Editable 1.", selectionAsString(frame)); 1229 1230 EXPECT_TRUE(frame->moveCaret(topLeft(elementBounds(frame, "editable_1")))); 1231 EXPECT_EQ("", selectionAsString(frame)); 1232 EXPECT_FALSE(frame->selectionRange().isNull()); 1233 EXPECT_TRUE(frame->moveSelectionEnd(bottomRightMinusOne(elementBounds(frame, "editable_1")), false)); 1234 EXPECT_EQ("Editable 1.", selectionAsString(frame)); 1235 1236 EXPECT_TRUE(frame->moveCaret(bottomRightMinusOne(elementBounds(frame, "editable_2")))); 1237 EXPECT_EQ("", selectionAsString(frame)); 1238 EXPECT_FALSE(frame->selectionRange().isNull()); 1239 EXPECT_TRUE(frame->moveSelectionStart(topLeft(elementBounds(frame, "editable_2")), false)); 1240 EXPECT_EQ("Editable 2.", selectionAsString(frame)); 1241 1242 // Caret is pinned at the start of the editable region. 1243 EXPECT_TRUE(frame->moveCaret(topLeft(elementBounds(frame, "header_1")))); 1244 EXPECT_EQ("", selectionAsString(frame)); 1245 EXPECT_FALSE(frame->selectionRange().isNull()); 1246 EXPECT_TRUE(frame->moveSelectionEnd(bottomRightMinusOne(elementBounds(frame, "editable_1")), false)); 1247 EXPECT_EQ("[ Editable 1.", selectionAsString(frame)); 1248 1249 // Caret is pinned at the end of the editable region. 1250 EXPECT_TRUE(frame->moveCaret(bottomRightMinusOne(elementBounds(frame, "footer_2")))); 1251 EXPECT_EQ("", selectionAsString(frame)); 1252 EXPECT_FALSE(frame->selectionRange().isNull()); 1253 EXPECT_TRUE(frame->moveSelectionStart(topLeft(elementBounds(frame, "editable_2")), false)); 1254 EXPECT_EQ("Editable 2. ]", selectionAsString(frame)); 1255 1080 1256 webView->close(); 1081 1257 }
Note: See TracChangeset
for help on using the changeset viewer.