Changeset 262601 in webkit
- Timestamp:
- Jun 4, 2020 11:13:30 PM (4 years ago)
- Location:
- trunk
- Files:
-
- 5 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebCore/ChangeLog
r262599 r262601 1 2020-06-04 Sihui Liu <sihui_liu@apple.com> 2 3 Text manipulation: first and last unit in a paragraph should not contain only excluded tokens 4 https://bugs.webkit.org/show_bug.cgi?id=212759 5 6 Reviewed by Wenson Hsieh. 7 8 In r262398, we literally made text of one Node as the minimum unit for text manipulation. This patches introduce 9 a struct ManipulationUnit for that. Now a paragraph can be represented as multiple ManipulationUnits. When all 10 tokens in a ManipulationUnit are excluded, it means the ManipulationUnit is excluded and should not be 11 manipulated. To record ManipulationUnits in a paragraph based on our current implementation, we need to keep the 12 excluded ManipulationUnits surrounded by non-excluded ManipulationUnits, but we can safely remove the leading 13 and trailing excluded ManipulationUnits. In this case, we can limit the range of paragraph further and thus less 14 text replacement work. 15 16 Covered by existing test. 17 18 * editing/TextManipulationController.cpp: 19 (WebCore::TextManipulationController::parse): 20 (WebCore::TextManipulationController::addItemIfPossible): 21 (WebCore::TextManipulationController::observeParagraphs): 22 * editing/TextManipulationController.h: 23 1 24 2020-06-04 Peng Liu <peng.liu6@apple.com> 2 25 -
trunk/Source/WebCore/editing/TextManipulationController.cpp
r262587 r262601 295 295 } 296 296 297 TextManipulationController::Manipulation TokensTextManipulationController::parse(StringView text, Node* textNode)297 TextManipulationController::ManipulationUnit TextManipulationController::parse(StringView text, Node* textNode) 298 298 { 299 299 Vector<ManipulationToken> tokens; … … 301 301 size_t positionOfLastNonHTMLSpace = WTF::notFound; 302 302 size_t startPositionOfCurrentToken = 0; 303 bool isNodeExcluded = exclusionRuleMatcher.isExcluded(textNode); 303 304 bool containsOnlyHTMLSpace = true; 304 305 bool containsLineBreak = false; … … 316 317 if (positionOfLastNonHTMLSpace != WTF::notFound && startPositionOfCurrentToken <= positionOfLastNonHTMLSpace) { 317 318 auto tokenString = text.substring(startPositionOfCurrentToken, positionOfLastNonHTMLSpace + 1 - startPositionOfCurrentToken).toString(); 318 tokens.append(ManipulationToken { m_tokenIdentifier.generate(), tokenString, tokenInfo(textNode), exclusionRuleMatcher.isExcluded(textNode)});319 tokens.append(ManipulationToken { m_tokenIdentifier.generate(), tokenString, tokenInfo(textNode), isNodeExcluded }); 319 320 startPositionOfCurrentToken = positionOfLastNonHTMLSpace + 1; 320 321 } … … 338 339 if (startPositionOfCurrentToken < text.length()) { 339 340 auto tokenString = text.substring(startPositionOfCurrentToken, index + 1 - startPositionOfCurrentToken).toString(); 340 tokens.append(ManipulationToken { m_tokenIdentifier.generate(), tokenString, tokenInfo(textNode), exclusionRuleMatcher.isExcluded(textNode)});341 tokens.append(ManipulationToken { m_tokenIdentifier.generate(), tokenString, tokenInfo(textNode), isNodeExcluded }); 341 342 lastTokenContainsLineBreak = false; 342 343 } 343 344 344 return { WTFMove(tokens), containsOnlyHTMLSpace, containsLineBreak, firstTokenContainsLineBreak, lastTokenContainsLineBreak }; 345 return { WTFMove(tokens), *textNode, containsOnlyHTMLSpace || isNodeExcluded, containsLineBreak, firstTokenContainsLineBreak, lastTokenContainsLineBreak }; 346 } 347 348 void TextManipulationController::addItemIfPossible(Vector<ManipulationUnit>&& units) 349 { 350 if (units.isEmpty()) 351 return; 352 353 size_t index = 0; 354 size_t end = units.size(); 355 while (index < units.size() && units[index].areAllTokensExcluded) 356 ++index; 357 358 while (end > 0 && units[end - 1].areAllTokensExcluded) 359 --end; 360 361 if (index == end) 362 return; 363 364 auto startPosition = firstPositionInOrBeforeNode(units.first().node.ptr()); 365 auto endPosition = positionAfterNode(units.last().node.ptr()); 366 Vector<ManipulationToken> tokens; 367 for (; index < end; ++index) 368 tokens.appendVector(WTFMove(units[index].tokens)); 369 370 addItem(ManipulationItemData { startPosition, endPosition, nullptr, nullQName(), WTFMove(tokens) }); 345 371 } 346 372 … … 352 378 auto document = makeRefPtr(start.document()); 353 379 ASSERT(document); 354 ParagraphContentIterator iterator { start, end };355 380 // TextIterator's constructor may have updated the layout and executed arbitrary scripts. 356 381 if (document != start.document() || document != end.document()) 357 382 return; 358 383 359 Vector<ManipulationToken> tokensInCurrentParagraph; 360 Position startOfCurrentParagraph; 361 Position endOfCurrentParagraph; 384 Vector<ManipulationUnit> unitsInCurrentParagraph; 362 385 RefPtr<Element> enclosingItemBoundaryElement; 363 386 ParagraphContentIterator iterator { start, end }; 364 387 for (; !iterator.atEnd(); iterator.advance()) { 365 388 auto content = iterator.currentContent(); … … 368 391 369 392 if (enclosingItemBoundaryElement && !enclosingItemBoundaryElement->contains(contentNode)) { 370 if (!tokensInCurrentParagraph.isEmpty()) 371 addItem(ManipulationItemData { startOfCurrentParagraph, endOfCurrentParagraph, nullptr, nullQName(), std::exchange(tokensInCurrentParagraph, { }) }); 393 addItemIfPossible(std::exchange(unitsInCurrentParagraph, { })); 372 394 enclosingItemBoundaryElement = nullptr; 373 395 } … … 402 424 403 425 if (content.isReplacedContent) { 404 if (!tokensInCurrentParagraph.isEmpty()) { 405 tokensInCurrentParagraph.append(ManipulationToken { m_tokenIdentifier.generate(), "[]", tokenInfo(content.node.get()), true }); 406 endOfCurrentParagraph = positionAfterNode(contentNode); 407 } 426 if (!unitsInCurrentParagraph.isEmpty()) 427 unitsInCurrentParagraph.append(ManipulationUnit { { ManipulationToken { m_tokenIdentifier.generate(), "[]", tokenInfo(content.node.get()), true } }, *contentNode }); 408 428 continue; 409 429 } … … 412 432 continue; 413 433 414 auto tokensInCurrentNode = parse(content.text, contentNode); 415 if (!tokensInCurrentParagraph.isEmpty() && tokensInCurrentNode.firstTokenContainsLineBreak) 416 addItem(ManipulationItemData { startOfCurrentParagraph, endOfCurrentParagraph, nullptr, nullQName(), std::exchange(tokensInCurrentParagraph, { }) }); 417 418 if (tokensInCurrentParagraph.isEmpty()) { 419 if (tokensInCurrentNode.containsOnlyHTMLSpace) 434 auto unitsInCurrentNode = parse(content.text, contentNode); 435 if (unitsInCurrentNode.firstTokenContainsLineBreak) 436 addItemIfPossible(std::exchange(unitsInCurrentParagraph, { })); 437 438 if (unitsInCurrentParagraph.isEmpty() && unitsInCurrentNode.areAllTokensExcluded) 420 439 continue; 421 startOfCurrentParagraph = firstPositionInOrBeforeNode(contentNode); 422 } 423 424 tokensInCurrentParagraph.appendVector(tokensInCurrentNode.tokens); 425 endOfCurrentParagraph = positionAfterNode(contentNode); 426 427 if (!tokensInCurrentParagraph.isEmpty() && tokensInCurrentNode.lastTokenContainsLineBreak) { 428 ASSERT(!tokensInCurrentParagraph.isEmpty()); 429 addItem(ManipulationItemData { startOfCurrentParagraph, endOfCurrentParagraph, nullptr, nullQName(), std::exchange(tokensInCurrentParagraph, { }) }); 430 } 431 } 432 433 if (!tokensInCurrentParagraph.isEmpty()) 434 addItem(ManipulationItemData { startOfCurrentParagraph, endOfCurrentParagraph, nullptr, nullQName(), WTFMove(tokensInCurrentParagraph) }); 440 441 unitsInCurrentParagraph.append(WTFMove(unitsInCurrentNode)); 442 443 if (unitsInCurrentNode.lastTokenContainsLineBreak) 444 addItemIfPossible(std::exchange(unitsInCurrentParagraph, { })); 445 } 446 447 addItemIfPossible(std::exchange(unitsInCurrentParagraph, { })); 435 448 } 436 449 -
trunk/Source/WebCore/editing/TextManipulationController.h
r262398 r262601 151 151 }; 152 152 153 struct Manipulation Tokens{153 struct ManipulationUnit { 154 154 Vector<ManipulationToken> tokens; 155 bool containsOnlyHTMLSpace { true }; 155 Ref<Node> node; 156 bool areAllTokensExcluded { true }; 156 157 bool containsLineBreak { false }; 157 158 bool firstTokenContainsLineBreak { false }; 158 159 bool lastTokenContainsLineBreak { false }; 159 160 }; 160 Manipulation Tokensparse(StringView, Node*);161 ManipulationUnit parse(StringView, Node*); 161 162 162 163 void addItem(ManipulationItemData&&); 164 void addItemIfPossible(Vector<ManipulationUnit>&&); 163 165 void flushPendingItemsForCallback(); 164 166 -
trunk/Tools/ChangeLog
r262588 r262601 1 2020-06-04 Sihui Liu <sihui_liu@apple.com> 2 3 Text manipulation: first and last unit in a paragraph should not contain only excluded tokens 4 https://bugs.webkit.org/show_bug.cgi?id=212759 5 6 Reviewed by Wenson Hsieh. 7 8 Modify existing test for changed behavior that leading and trailing excluded units are not included in paragraph 9 now. 10 11 * TestWebKitAPI/Tests/WebKitCocoa/TextManipulation.mm: 12 (TestWebKitAPI::TEST): 13 1 14 2020-06-04 Chris Dumez <cdumez@apple.com> 2 15 -
trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/TextManipulation.mm
r262587 r262601 244 244 EXPECT_FALSE(items[2].tokens[0].isExcluded); 245 245 246 EXPECT_EQ(items[3].tokens.count, 2UL);246 EXPECT_EQ(items[3].tokens.count, 1UL); 247 247 EXPECT_STREQ("hello", items[3].tokens[0].content.UTF8String); 248 248 EXPECT_FALSE(items[3].tokens[0].isExcluded); 249 EXPECT_STREQ("[]", items[3].tokens[1].content.UTF8String);250 EXPECT_TRUE(items[3].tokens[1].isExcluded);251 249 } 252 250 … … 452 450 auto *items = [delegate items]; 453 451 EXPECT_EQ(items.count, 1UL); 454 EXPECT_EQ(items[0].tokens.count, 2UL); 455 EXPECT_STREQ("hello, ", items[0].tokens[0].content.UTF8String); 456 EXPECT_TRUE(items[0].tokens[0].isExcluded); 457 EXPECT_STREQ("world", items[0].tokens[1].content.UTF8String); 458 EXPECT_FALSE(items[0].tokens[1].isExcluded); 452 EXPECT_EQ(items[0].tokens.count, 1UL); 453 EXPECT_STREQ("world", items[0].tokens[0].content.UTF8String); 454 EXPECT_FALSE(items[0].tokens[0].isExcluded); 459 455 } 460 456 … … 466 462 467 463 [webView synchronouslyLoadHTMLString:@"<!DOCTYPE html>" 468 "<html><body> <span class='someClass exclude'>Message:<b>hello, </b><span>world</span></span></body></html>"];464 "<html><body>Message: <span class='someClass exclude'><b>hello, </b><span>world</span></span></body></html>"]; 469 465 470 466 auto configuration = adoptNS([[_WKTextManipulationConfiguration alloc] init]); … … 481 477 auto *items = [delegate items]; 482 478 EXPECT_EQ(items.count, 1UL); 483 EXPECT_EQ(items[0].tokens.count, 3UL);479 EXPECT_EQ(items[0].tokens.count, 1UL); 484 480 EXPECT_STREQ("Message: ", items[0].tokens[0].content.UTF8String); 485 EXPECT_TRUE(items[0].tokens[0].isExcluded); 486 EXPECT_STREQ("hello, ", items[0].tokens[1].content.UTF8String); 487 EXPECT_TRUE(items[0].tokens[1].isExcluded); 488 EXPECT_STREQ("world", items[0].tokens[2].content.UTF8String); 489 EXPECT_TRUE(items[0].tokens[2].isExcluded); 481 EXPECT_FALSE(items[0].tokens[0].isExcluded); 490 482 } 491 483 … … 514 506 auto *items = [delegate items]; 515 507 EXPECT_EQ(items.count, 1UL); 516 EXPECT_EQ(items[0].tokens.count, 3UL); 517 EXPECT_STREQ("Message: ", items[0].tokens[0].content.UTF8String); 518 EXPECT_TRUE(items[0].tokens[0].isExcluded); 519 EXPECT_STREQ("hello, ", items[0].tokens[1].content.UTF8String); 520 EXPECT_FALSE(items[0].tokens[1].isExcluded); 521 EXPECT_STREQ("world", items[0].tokens[2].content.UTF8String); 522 EXPECT_TRUE(items[0].tokens[2].isExcluded); 508 EXPECT_EQ(items[0].tokens.count, 1UL); 509 EXPECT_STREQ("hello, ", items[0].tokens[0].content.UTF8String); 510 EXPECT_FALSE(items[0].tokens[0].isExcluded); 523 511 } 524 512 … … 1294 1282 1295 1283 [webView synchronouslyLoadHTMLString:@"<!DOCTYPE html><html><body>" 1296 "<section><div style=\"display: inline-block;\">hello </div>"1297 "< div style=\"display: inline-block;\"><span style=\"display: inline-flex;\">"1298 " <svg viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><rect width=\"20\" height=\"20\" fill=\"#06f\"></rect></svg>"1299 "< /span></div></section><p>world</p></body></html>"];1284 "<section><div style=\"display: inline-block;\">hello" 1285 "<span style=\"display: inline-flex;\"><svg viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><rect width=\"20\" height=\"20\" fill=\"#06f\"></rect></svg></span>" 1286 "webkit</div></section>" 1287 "<p>world</p></body></html>"]; 1300 1288 1301 1289 done = false; … … 1308 1296 EXPECT_EQ(items.count, 2UL); 1309 1297 auto *tokens = items[0].tokens; 1310 EXPECT_EQ(tokens.count, 2UL);1298 EXPECT_EQ(tokens.count, 3UL); 1311 1299 EXPECT_STREQ("hello", tokens[0].content.UTF8String); 1312 1300 EXPECT_FALSE(tokens[0].isExcluded); 1313 1301 EXPECT_STREQ("[]", tokens[1].content.UTF8String); 1314 1302 EXPECT_TRUE(tokens[1].isExcluded); 1303 EXPECT_STREQ("webkit", tokens[2].content.UTF8String); 1304 EXPECT_FALSE(tokens[2].isExcluded); 1315 1305 1316 1306 EXPECT_EQ(items[1].tokens.count, 1UL); … … 1322 1312 { tokens[0].identifier, @"hey" }, 1323 1313 { tokens[1].identifier, nil }, 1314 { tokens[2].identifier, @"WebKit" }, 1324 1315 })] completion:^(NSArray<NSError *> *errors) { 1325 1316 EXPECT_EQ(errors, nil); … … 1327 1318 }]; 1328 1319 TestWebKitAPI::Util::run(&done); 1329 EXPECT_WK_STREQ("<section><div style=\"display: inline-block;\">hey</div>" 1330 "<div style=\"display: inline-block;\"><span style=\"display: inline-flex;\">" 1331 "<svg viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><rect width=\"20\" height=\"20\" fill=\"#06f\"></rect></svg>" 1332 "</span></div></section><p>world</p>", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]); 1320 EXPECT_WK_STREQ("<section><div style=\"display: inline-block;\">hey<span style=\"display: inline-flex;\"><svg viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><rect width=\"20\" height=\"20\" fill=\"#06f\"></rect></svg></span>WebKit</div></section><p>world</p>", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]); 1333 1321 } 1334 1322 … … 1396 1384 EXPECT_STREQ("fruit", items[2].tokens[0].content.UTF8String); 1397 1385 1398 EXPECT_EQ(items[3].tokens.count, 2UL);1386 EXPECT_EQ(items[3].tokens.count, 1UL); 1399 1387 EXPECT_STREQ("hello", items[3].tokens[0].content.UTF8String); 1400 EXPECT_STREQ("[]", items[3].tokens[1].content.UTF8String);1401 1388 1402 1389 done = false; … … 1476 1463 EXPECT_EQ(items[3].tokens.count, 1UL); 1477 1464 EXPECT_EQ(items[4].tokens.count, 1UL); 1478 EXPECT_EQ(items[5].tokens.count, 2UL);1465 EXPECT_EQ(items[5].tokens.count, 1UL); 1479 1466 EXPECT_WK_STREQ("This is a test", items[0].tokens[0].content); 1480 1467 EXPECT_WK_STREQ("Hello world", items[1].tokens[0].content); … … 1483 1470 EXPECT_WK_STREQ("image", items[4].tokens[0].content); 1484 1471 EXPECT_WK_STREQ("Text", items[5].tokens[0].content); 1485 EXPECT_WK_STREQ("[]", items[5].tokens[1].content);1486 1472 1487 1473 auto replacementItems = retainPtr(@[ … … 1926 1912 1927 1913 [webView synchronouslyLoadTestPageNamed:@"simple"]; 1928 [webView stringByEvaluatingJavaScript:@"document.body.innerHTML = '<p>hi, <em>WebKitten</em> </p>'"];1914 [webView stringByEvaluatingJavaScript:@"document.body.innerHTML = '<p>hi, <em>WebKitten</em> bye</p>'"]; 1929 1915 1930 1916 RetainPtr<_WKTextManipulationConfiguration> configuration = adoptNS([[_WKTextManipulationConfiguration alloc] init]); … … 1941 1927 auto *items = [delegate items]; 1942 1928 EXPECT_EQ(items.count, 1UL); 1943 EXPECT_EQ(items[0].tokens.count, 2UL);1929 EXPECT_EQ(items[0].tokens.count, 3UL); 1944 1930 EXPECT_STREQ("hi, ", items[0].tokens[0].content.UTF8String); 1945 1931 EXPECT_STREQ("WebKitten", items[0].tokens[1].content.UTF8String); 1932 EXPECT_STREQ(" bye", items[0].tokens[2].content.UTF8String); 1946 1933 1947 1934 done = false; … … 1949 1936 { items[0].tokens[0].identifier, @"Hello," }, 1950 1937 { items[0].tokens[1].identifier, @"WebKit" }, 1938 { items[0].tokens[2].identifier, @"Bye" }, 1951 1939 }); 1952 1940 [webView _completeTextManipulationForItems:@[item.get()] completion:^(NSArray<NSError *> *errors) { … … 1959 1947 1960 1948 TestWebKitAPI::Util::run(&done); 1961 EXPECT_WK_STREQ("<p>hi, <em>WebKitten</em> </p>", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]);1949 EXPECT_WK_STREQ("<p>hi, <em>WebKitten</em> bye</p>", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]); 1962 1950 } 1963 1951 … … 1969 1957 1970 1958 [webView synchronouslyLoadTestPageNamed:@"simple"]; 1971 [webView stringByEvaluatingJavaScript:@"document.body.innerHTML = '<p>hi, <em>WebKitten</em> </p>'"];1959 [webView stringByEvaluatingJavaScript:@"document.body.innerHTML = '<p>hi, <em>WebKitten</em> bye</p>'"]; 1972 1960 1973 1961 RetainPtr<_WKTextManipulationConfiguration> configuration = adoptNS([[_WKTextManipulationConfiguration alloc] init]); … … 1984 1972 auto *items = [delegate items]; 1985 1973 EXPECT_EQ(items.count, 1UL); 1986 EXPECT_EQ(items[0].tokens.count, 2UL);1974 EXPECT_EQ(items[0].tokens.count, 3UL); 1987 1975 EXPECT_STREQ("hi, ", items[0].tokens[0].content.UTF8String); 1988 1976 EXPECT_STREQ("WebKitten", items[0].tokens[1].content.UTF8String); 1977 EXPECT_STREQ(" bye", items[0].tokens[2].content.UTF8String); 1989 1978 1990 1979 done = false; … … 1993 1982 { items[0].tokens[0].identifier, @"Hello," }, 1994 1983 { items[0].tokens[1].identifier, nil }, 1984 { items[0].tokens[2].identifier, @"Bye" }, 1995 1985 }); 1996 1986 [webView _completeTextManipulationForItems:@[item.get()] completion:^(NSArray<NSError *> *errors) { … … 2003 1993 2004 1994 TestWebKitAPI::Util::run(&done); 2005 EXPECT_WK_STREQ("<p>hi, <em>WebKitten</em> </p>", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]);1995 EXPECT_WK_STREQ("<p>hi, <em>WebKitten</em> bye</p>", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]); 2006 1996 } 2007 1997 … … 2012 2002 [webView _setTextManipulationDelegate:delegate.get()]; 2013 2003 2014 [webView synchronouslyLoadHTMLString:@"<!DOCTYPE html><html><body><p>hi, <em>WebKitten</em> </p></body></html>"];2004 [webView synchronouslyLoadHTMLString:@"<!DOCTYPE html><html><body><p>hi, <em>WebKitten</em> bye</p></body></html>"]; 2015 2005 2016 2006 RetainPtr<_WKTextManipulationConfiguration> configuration = adoptNS([[_WKTextManipulationConfiguration alloc] init]); … … 2027 2017 auto *items = [delegate items]; 2028 2018 EXPECT_EQ(items.count, 1UL); 2029 EXPECT_EQ(items[0].tokens.count, 2UL);2019 EXPECT_EQ(items[0].tokens.count, 3UL); 2030 2020 EXPECT_STREQ("hi, ", items[0].tokens[0].content.UTF8String); 2031 2021 EXPECT_STREQ("WebKitten", items[0].tokens[1].content.UTF8String); 2022 EXPECT_STREQ(" bye", items[0].tokens[2].content.UTF8String); 2032 2023 2033 2024 done = false; … … 2035 2026 { items[0].tokens[0].identifier, @"Hello, " }, 2036 2027 { items[0].tokens[1].identifier, nil }, 2028 { items[0].tokens[2].identifier, @" Bye" }, 2037 2029 })] completion:^(NSArray<NSError *> *errors) { 2038 2030 EXPECT_EQ(errors, nil); … … 2041 2033 2042 2034 TestWebKitAPI::Util::run(&done); 2043 EXPECT_WK_STREQ("<p>Hello, <em>WebKitten</em> </p>", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]);2035 EXPECT_WK_STREQ("<p>Hello, <em>WebKitten</em> Bye</p>", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]); 2044 2036 } 2045 2037
Note: See TracChangeset
for help on using the changeset viewer.