Skip to content

Commit 883a577

Browse files
committed
Add "Convert to Simplified Chinese" and "Convert to Traditional Chinese" text transformations
https://bugs.webkit.org/show_bug.cgi?id=306845 rdar://156354464 Reviewed by Abrar Rahman Protyasha and Aditya Keerthi. When editing text in a NSTextView, "Convert to Simplified Chinese" and "Convert to Traditional Chinese" appears under the transformations submenu if the items are relevant to the selected text. These items are now available in WebKit. Add the two context menu items and use the same heuristic AppKit uses to determine which transformations should actually be listed in the menu. Analyze the first 200 characters in the selection to see if Simplified Chinese characters, Traditional Chinese characters, or latin characters are present. If the selection exceeds 200 characters, also include case-related transformations as a fallback. Conversions are performed in the same way AppKit performs them. The selection is converted to an NSString, and we then set the selection equal to `stringByApplyingTransform` with a transform of either "Hans-hant" when converting to Traditional Chinese, or "Hant-hans" when converting to Simplified Chinese. Tests: editing/mac/context-menu/text-transformations-menu-actions.html editing/mac/context-menu/text-transformations-menu-items.html * LayoutTests/editing/mac/context-menu/text-transformations-menu-actions-expected.txt: Added. * LayoutTests/editing/mac/context-menu/text-transformations-menu-actions.html: Added. * LayoutTests/editing/mac/context-menu/text-transformations-menu-items-expected.txt: Added. * LayoutTests/editing/mac/context-menu/text-transformations-menu-items.html: Added. * Source/WebCore/editing/Editor.cpp: (WebCore::Editor::canApplyCaseTransformations): (WebCore::Editor::canConvertToSimplifiedChinese): (WebCore::Editor::canConvertToTraditionalChinese): (WebCore::Editor::convertToTraditionalChinese): (WebCore::Editor::convertToSimplifiedChinese): * Source/WebCore/editing/Editor.h: * Source/WebCore/en.lproj/Localizable.strings: * Source/WebCore/loader/EmptyClients.cpp: * Source/WebCore/page/ContextMenuController.cpp: (WebCore::ContextMenuController::contextMenuItemSelected): (WebCore::ContextMenuController::createAndAppendTransformationsSubMenu): (WebCore::ContextMenuController::populate): (WebCore::ContextMenuController::checkOrEnableIfNeeded const): * Source/WebCore/page/ContextMenuController.h: * Source/WebCore/page/EditorClient.h: * Source/WebCore/platform/ContextMenuItem.cpp: (WebCore::isValidContextMenuAction): * Source/WebCore/platform/ContextMenuItem.h: * Source/WebCore/platform/LocalizedStrings.h: * Source/WebCore/platform/cocoa/LocalizedStringsCocoa.mm: (WebCore::contextMenuItemTagConvertToTraditionalChinese): (WebCore::contextMenuItemTagConvertToSimplifiedChinese): * Source/WebKit/Platform/mac/MenuUtilities.mm: (WebKit::symbolNameWithTypeForAction): * Source/WebKit/Shared/API/c/WKContextMenuItemTypes.h: * Source/WebKit/Shared/API/c/WKSharedAPICast.h: (WebKit::toAPI): (WebKit::toImpl): * Source/WebKit/UIProcess/WebPageProxy.cpp: (WebKit::WebPageProxy::convertToTraditionalChinese): (WebKit::WebPageProxy::convertToSimplifiedChinese): * Source/WebKit/UIProcess/WebPageProxy.h: * Source/WebKit/UIProcess/mac/WebContextMenuProxyMac.mm: (WebKit::WebContextMenuProxyMac::getContextMenuItem): * Source/WebKit/UIProcess/mac/WebViewImpl.h: * Source/WebKit/WebProcess/WebCoreSupport/WebEditorClient.h: * Source/WebKit/WebProcess/WebCoreSupport/mac/WebEditorClientMac.mm: (WebKit::applyTextTransformation): (WebKit::WebEditorClient::uppercaseWord): (WebKit::WebEditorClient::lowercaseWord): (WebKit::WebEditorClient::capitalizeWord): (WebKit::WebEditorClient::canApplyCaseTransformations): (WebKit::WebEditorClient::canConvertToTraditionalChinese): (WebKit::WebEditorClient::canConvertToSimplifiedChinese): (WebKit::WebEditorClient::convertToTraditionalChinese): (WebKit::WebEditorClient::convertToSimplifiedChinese): (WebKit::changeWordCase): Deleted. * Source/WebKit/WebProcess/WebPage/WebPage.cpp: (WebKit::WebPage::convertToTraditionalChinese): (WebKit::WebPage::convertToSimplifiedChinese): * Source/WebKit/WebProcess/WebPage/WebPage.h: * Source/WebKit/WebProcess/WebPage/WebPage.messages.in: * Source/WebKitLegacy/mac/WebCoreSupport/WebEditorClient.h: * Source/WebKitLegacy/mac/WebCoreSupport/WebEditorClient.mm: (WebEditorClient::canApplyCaseTransformations): (WebEditorClient::canConvertToTraditionalChinese): (WebEditorClient::canConvertToSimplifiedChinese): (WebEditorClient::convertToTraditionalChinese): (WebEditorClient::convertToSimplifiedChinese): Canonical link: https://commits.webkit.org/306761@main
1 parent f7f0f38 commit 883a577

29 files changed

+711
-22
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
This test verifies that the Transformations context menu items correctly transform the selected text.
2+
3+
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
4+
5+
6+
Test 1: Make Upper Case transforms text to uppercase
7+
PASS actualText is "HELLO WORLD"
8+
9+
Test 2: Make Lower Case transforms text to lowercase
10+
PASS actualText is "hello world"
11+
12+
Test 3: Capitalize transforms text to title case
13+
PASS actualText is "Hello World"
14+
15+
Test 4: Convert to Traditional Chinese transforms simplified to traditional
16+
PASS actualText is "圖書館"
17+
18+
Test 5: Convert to Simplified Chinese transforms traditional to simplified
19+
PASS actualText is "图书馆"
20+
PASS successfullyParsed is true
21+
22+
TEST COMPLETE
23+
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="UTF-8">
5+
<script src="../../../resources/js-test.js"></script>
6+
<style>
7+
.test-field {
8+
font-size: 20px;
9+
padding: 10px;
10+
margin: 10px;
11+
width: 300px;
12+
}
13+
</style>
14+
</head>
15+
<body>
16+
<p id="description"></p>
17+
<div id="editable" class="test-field" contenteditable>test</div>
18+
<div id="console"></div>
19+
<script>
20+
description("This test verifies that the Transformations context menu items correctly transform the selected text.");
21+
22+
window.jsTestIsAsync = true;
23+
24+
var actualText;
25+
26+
function findMenuItem(items, title) {
27+
for (let item of items) {
28+
if (item.title === title)
29+
return item;
30+
}
31+
return null;
32+
}
33+
34+
function findTransformationsSubmenu(items) {
35+
return findMenuItem(items, "Transformations");
36+
}
37+
38+
function getTransformationItems(items) {
39+
let menu = findTransformationsSubmenu(items);
40+
if (!menu)
41+
return null;
42+
return menu.children;
43+
}
44+
45+
async function selectAllAndContextClick(elementId) {
46+
let element = document.getElementById(elementId);
47+
let range = document.createRange();
48+
range.selectNodeContents(element);
49+
window.getSelection().removeAllRanges();
50+
window.getSelection().addRange(range);
51+
52+
let rect = element.getBoundingClientRect();
53+
eventSender.mouseMoveTo(rect.left + 2, rect.top + 2);
54+
return eventSender.contextClick();
55+
}
56+
57+
async function selectFirstCharacterAndContextClick(elementId) {
58+
let element = document.getElementById(elementId);
59+
let textNode = element.firstChild;
60+
let range = document.createRange();
61+
range.setStart(textNode, 0);
62+
range.setEnd(textNode, 1);
63+
window.getSelection().removeAllRanges();
64+
window.getSelection().addRange(range);
65+
66+
let rect = element.getBoundingClientRect();
67+
eventSender.mouseMoveTo(rect.left + 2, rect.top + 2);
68+
return eventSender.contextClick();
69+
}
70+
71+
async function runTests() {
72+
if (!window.eventSender || !window.testRunner) {
73+
debug("This test requires eventSender and testRunner.");
74+
finishJSTest();
75+
return;
76+
}
77+
78+
debug("Test 1: Make Upper Case transforms text to uppercase");
79+
editable.innerText = "hello world";
80+
let items = await selectAllAndContextClick("editable");
81+
let transformItems = getTransformationItems(items);
82+
if (!transformItems) {
83+
testFailed("Transformations submenu not found");
84+
} else {
85+
let menuItem = findMenuItem(transformItems, "Make Upper Case");
86+
if (!menuItem) {
87+
testFailed("Make Upper Case menu item not found");
88+
} else {
89+
menuItem.click();
90+
actualText = editable.innerText;
91+
shouldBeEqualToString("actualText", "HELLO WORLD");
92+
}
93+
}
94+
debug("");
95+
96+
debug("Test 2: Make Lower Case transforms text to lowercase");
97+
editable.innerText = "HELLO WORLD";
98+
items = await selectAllAndContextClick("editable");
99+
transformItems = getTransformationItems(items);
100+
if (!transformItems) {
101+
testFailed("Transformations submenu not found");
102+
} else {
103+
let menuItem = findMenuItem(transformItems, "Make Lower Case");
104+
if (!menuItem) {
105+
testFailed("Make Lower Case menu item not found");
106+
} else {
107+
menuItem.click();
108+
actualText = editable.innerText;
109+
shouldBeEqualToString("actualText", "hello world");
110+
}
111+
}
112+
debug("");
113+
114+
debug("Test 3: Capitalize transforms text to title case");
115+
editable.innerText = "hello world";
116+
items = await selectAllAndContextClick("editable");
117+
transformItems = getTransformationItems(items);
118+
if (!transformItems) {
119+
testFailed("Transformations submenu not found");
120+
} else {
121+
let menuItem = findMenuItem(transformItems, "Capitalize");
122+
if (!menuItem) {
123+
testFailed("Capitalize menu item not found");
124+
} else {
125+
menuItem.click();
126+
actualText = editable.innerText;
127+
shouldBeEqualToString("actualText", "Hello World");
128+
}
129+
}
130+
debug("");
131+
132+
debug("Test 4: Convert to Traditional Chinese transforms simplified to traditional");
133+
editable.innerText = "图书馆";
134+
items = await selectAllAndContextClick("editable");
135+
transformItems = getTransformationItems(items);
136+
if (!transformItems) {
137+
testFailed("Transformations submenu not found");
138+
} else {
139+
let menuItem = findMenuItem(transformItems, "Convert to Traditional Chinese");
140+
if (!menuItem) {
141+
testFailed("Convert to Traditional Chinese menu item not found");
142+
} else {
143+
menuItem.click();
144+
actualText = editable.innerText;
145+
shouldBeEqualToString("actualText", "圖書館");
146+
}
147+
}
148+
debug("");
149+
150+
debug("Test 5: Convert to Simplified Chinese transforms traditional to simplified");
151+
editable.innerText = "圖書館";
152+
items = await selectAllAndContextClick("editable");
153+
transformItems = getTransformationItems(items);
154+
if (!transformItems) {
155+
testFailed("Transformations submenu not found");
156+
} else {
157+
let menuItem = findMenuItem(transformItems, "Convert to Simplified Chinese");
158+
if (!menuItem) {
159+
testFailed("Convert to Simplified Chinese menu item not found");
160+
} else {
161+
menuItem.click();
162+
actualText = editable.innerText;
163+
shouldBeEqualToString("actualText", "图书馆");
164+
}
165+
}
166+
167+
editable.innerText = "";
168+
finishJSTest();
169+
}
170+
171+
window.addEventListener("load", runTests);
172+
</script>
173+
</body>
174+
</html>
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
This test verifies that the Transformations context menu shows appropriate items based on the selected text.
2+
3+
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
4+
5+
6+
Test 1: Latin characters should show case transformations but not Chinese conversions
7+
PASS hasMenuItem(transformItems, 'Make Upper Case') is true
8+
PASS hasMenuItem(transformItems, 'Make Lower Case') is true
9+
PASS hasMenuItem(transformItems, 'Capitalize') is true
10+
PASS hasMenuItem(transformItems, 'Convert to Traditional Chinese') is false
11+
PASS hasMenuItem(transformItems, 'Convert to Simplified Chinese') is false
12+
13+
Test 2: Simplified Chinese text should show conversion to Traditional Chinese
14+
PASS hasMenuItem(transformItems, 'Make Upper Case') is false
15+
PASS hasMenuItem(transformItems, 'Make Lower Case') is false
16+
PASS hasMenuItem(transformItems, 'Capitalize') is false
17+
PASS hasMenuItem(transformItems, 'Convert to Traditional Chinese') is true
18+
PASS hasMenuItem(transformItems, 'Convert to Simplified Chinese') is false
19+
20+
Test 3: Traditional Chinese text should show conversion to Simplified Chinese
21+
PASS hasMenuItem(transformItems, 'Make Upper Case') is false
22+
PASS hasMenuItem(transformItems, 'Make Lower Case') is false
23+
PASS hasMenuItem(transformItems, 'Capitalize') is false
24+
PASS hasMenuItem(transformItems, 'Convert to Traditional Chinese') is false
25+
PASS hasMenuItem(transformItems, 'Convert to Simplified Chinese') is true
26+
27+
Test 4: Traditional Chinese text and Simplified Chinese text should should show both conversion options
28+
PASS hasMenuItem(transformItems, 'Make Upper Case') is false
29+
PASS hasMenuItem(transformItems, 'Make Lower Case') is false
30+
PASS hasMenuItem(transformItems, 'Capitalize') is false
31+
PASS hasMenuItem(transformItems, 'Convert to Traditional Chinese') is true
32+
PASS hasMenuItem(transformItems, 'Convert to Simplified Chinese') is true
33+
34+
Test 5: Traditional Chinese characters, Simplified Chinese character, and latin characters should should show all transformation options.
35+
PASS hasMenuItem(transformItems, 'Make Upper Case') is true
36+
PASS hasMenuItem(transformItems, 'Make Lower Case') is true
37+
PASS hasMenuItem(transformItems, 'Capitalize') is true
38+
PASS hasMenuItem(transformItems, 'Convert to Traditional Chinese') is true
39+
PASS hasMenuItem(transformItems, 'Convert to Simplified Chinese') is true
40+
41+
Test 6: Case transformations are shown as fallback when selection exceeds 200 characters.
42+
PASS hasMenuItem(transformItems, 'Make Upper Case') is true
43+
PASS hasMenuItem(transformItems, 'Make Lower Case') is true
44+
PASS hasMenuItem(transformItems, 'Capitalize') is true
45+
PASS hasMenuItem(transformItems, 'Convert to Traditional Chinese') is true
46+
PASS hasMenuItem(transformItems, 'Convert to Simplified Chinese') is true
47+
48+
Test 7: Numbers only should not show Transformations submenu at all
49+
PASS transformationsMenu is null
50+
PASS successfullyParsed is true
51+
52+
TEST COMPLETE
53+

0 commit comments

Comments
 (0)