blob: 243b527be0a044368a478d1b6186a13c344d75a1 [file] [log] [blame]
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +00001/*
2 * Copyright (C) 2019 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "TextManipulationController.h"
28
wenson_hsieh@apple.come2c4adf2020-04-27 21:12:52 +000029#include "AccessibilityObject.h"
rniwa@webkit.org7afdb0b2019-10-25 20:41:15 +000030#include "CharacterData.h"
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +000031#include "Editing.h"
rniwa@webkit.org7afdb0b2019-10-25 20:41:15 +000032#include "ElementAncestorIterator.h"
rniwa@webkit.orgc46d21d2019-12-16 22:36:55 +000033#include "EventLoop.h"
sihui_liu@apple.com5a2c1d32020-04-23 17:23:54 +000034#include "HTMLBRElement.h"
don.olmstead@sony.comf5e4de72020-03-12 03:19:09 +000035#include "HTMLElement.h"
wenson_hsieh@apple.com5f24d542020-05-02 02:26:23 +000036#include "HTMLInputElement.h"
rniwa@webkit.org640b0732020-03-08 03:52:06 +000037#include "HTMLNames.h"
sihui_liu@apple.com5a2c1d32020-04-23 17:23:54 +000038#include "HTMLParserIdioms.h"
wenson_hsieh@apple.comf0ad7db2020-06-03 21:08:21 +000039#include "InputTypeNames.h"
wenson_hsieh@apple.comf2cc5ed12020-06-25 20:37:34 +000040#include "NodeRenderStyle.h"
rniwa@webkit.org900f5602019-12-21 02:09:50 +000041#include "NodeTraversal.h"
42#include "PseudoElement.h"
43#include "Range.h"
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +000044#include "ScriptDisallowedScope.h"
rniwa@webkit.org900f5602019-12-21 02:09:50 +000045#include "Text.h"
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +000046#include "TextIterator.h"
47#include "VisibleUnits.h"
48
49namespace WebCore {
50
rniwa@webkit.org7afdb0b2019-10-25 20:41:15 +000051inline bool TextManipulationController::ExclusionRule::match(const Element& element) const
52{
53 return switchOn(rule, [&element] (ElementRule rule) {
54 return rule.localName == element.localName();
55 }, [&element] (AttributeRule rule) {
56 return equalIgnoringASCIICase(element.getAttribute(rule.name), rule.value);
commit-queue@webkit.orgd8730752019-12-04 18:36:15 +000057 }, [&element] (ClassRule rule) {
58 return element.hasClass() && element.classNames().contains(rule.className);
rniwa@webkit.org7afdb0b2019-10-25 20:41:15 +000059 });
60}
61
62class ExclusionRuleMatcher {
63public:
64 using ExclusionRule = TextManipulationController::ExclusionRule;
65 using Type = TextManipulationController::ExclusionRule::Type;
66
67 ExclusionRuleMatcher(const Vector<ExclusionRule>& rules)
68 : m_rules(rules)
69 { }
70
71 bool isExcluded(Node* node)
72 {
73 if (!node)
74 return false;
75
76 RefPtr<Element> startingElement = is<Element>(*node) ? downcast<Element>(node) : node->parentElement();
77 if (!startingElement)
78 return false;
79
80 Type type = Type::Include;
81 RefPtr<Element> matchingElement;
darin@apple.com25c0c842020-02-23 04:56:03 +000082 for (auto& element : lineageOfType<Element>(*startingElement)) {
rniwa@webkit.org7afdb0b2019-10-25 20:41:15 +000083 if (auto typeOrNullopt = typeForElement(element)) {
84 type = *typeOrNullopt;
85 matchingElement = &element;
86 break;
87 }
88 }
89
darin@apple.com25c0c842020-02-23 04:56:03 +000090 for (auto& element : lineageOfType<Element>(*startingElement)) {
rniwa@webkit.org7afdb0b2019-10-25 20:41:15 +000091 m_cache.set(element, type);
92 if (&element == matchingElement)
93 break;
94 }
95
96 return type == Type::Exclude;
97 }
98
99 Optional<Type> typeForElement(Element& element)
100 {
101 auto it = m_cache.find(element);
102 if (it != m_cache.end())
103 return it->value;
104
105 for (auto& rule : m_rules) {
106 if (rule.match(element))
107 return rule.type;
108 }
109
110 return WTF::nullopt;
111 }
112
113private:
114 const Vector<ExclusionRule>& m_rules;
115 HashMap<Ref<Element>, ExclusionRule::Type> m_cache;
116};
117
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000118TextManipulationController::TextManipulationController(Document& document)
119 : m_document(makeWeakPtr(document))
120{
121}
122
rniwa@webkit.org7afdb0b2019-10-25 20:41:15 +0000123void TextManipulationController::startObservingParagraphs(ManipulationItemCallback&& callback, Vector<ExclusionRule>&& exclusionRules)
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000124{
125 auto document = makeRefPtr(m_document.get());
126 if (!document)
127 return;
128
129 m_callback = WTFMove(callback);
rniwa@webkit.org7afdb0b2019-10-25 20:41:15 +0000130 m_exclusionRules = WTFMove(exclusionRules);
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000131
rniwa@webkit.org640b0732020-03-08 03:52:06 +0000132 observeParagraphs(firstPositionInNode(m_document.get()), lastPositionInNode(m_document.get()));
rniwa@webkit.orge44b91352020-03-04 05:22:14 +0000133 flushPendingItemsForCallback();
rniwa@webkit.orgc46d21d2019-12-16 22:36:55 +0000134}
135
sihui_liu@apple.com0e39ec02020-06-09 06:54:20 +0000136static bool isInPrivateUseArea(UChar character)
137{
138 return 0xE000 <= character && character <= 0xF8FF;
139}
140
141static bool isTokenDelimiter(UChar character)
142{
143 return isHTMLLineBreak(character) || isInPrivateUseArea(character);
144}
145
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000146class ParagraphContentIterator {
147public:
148 ParagraphContentIterator(const Position& start, const Position& end)
wenson_hsieh@apple.comd0ed2bd2020-04-24 00:07:25 +0000149 : m_iterator({ *makeBoundaryPoint(start), *makeBoundaryPoint(end) }, TextIteratorIgnoresStyleVisibility)
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000150 , m_node(start.firstNode())
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000151 , m_pastEndNode(end.firstNode())
152 {
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000153 if (shouldAdvanceIteratorPastCurrentNode())
154 advanceIteratorNodeAndUpdateText();
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000155 }
156
157 void advance()
158 {
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000159 m_text = WTF::nullopt;
160 advanceNode();
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000161
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000162 if (shouldAdvanceIteratorPastCurrentNode())
163 advanceIteratorNodeAndUpdateText();
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000164 }
165
166 struct CurrentContent {
167 RefPtr<Node> node;
sihui_liu@apple.com0e39ec02020-06-09 06:54:20 +0000168 Vector<String> text;
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000169 bool isTextContent { false };
170 bool isReplacedContent { false };
171 };
172
173 CurrentContent currentContent()
174 {
sihui_liu@apple.com0e39ec02020-06-09 06:54:20 +0000175 CurrentContent content = { m_node.copyRef(), m_text ? m_text.value() : Vector<String> { }, !!m_text };
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000176 if (content.node) {
177 if (auto* renderer = content.node->renderer()) {
178 if (renderer->isRenderReplaced()) {
179 content.isTextContent = false;
180 content.isReplacedContent = true;
181 }
182 }
183 }
184 return content;
185 }
186
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000187 bool atEnd() const { return !m_text && m_iterator.atEnd() && m_node == m_pastEndNode; }
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000188
189private:
commit-queue@webkit.org10778122020-07-08 18:30:44 +0000190 bool shouldAdvanceIteratorPastCurrentNode() const
191 {
192 if (m_iterator.atEnd())
193 return false;
194
195 auto* iteratorNode = m_iterator.node();
196 return !iteratorNode || iteratorNode == m_node;
197 }
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000198
199 void advanceNode()
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000200 {
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000201 if (m_node == m_pastEndNode)
sihui_liu@apple.com5a2c1d32020-04-23 17:23:54 +0000202 return;
203
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000204 m_node = NodeTraversal::next(*m_node);
205 if (!m_node)
206 m_node = m_pastEndNode;
207 }
208
sihui_liu@apple.com0e39ec02020-06-09 06:54:20 +0000209 void appendToText(Vector<String>& text, StringBuilder& stringBuilder)
210 {
211 if (!stringBuilder.isEmpty()) {
212 text.append(stringBuilder.toString());
213 stringBuilder.clear();
214 }
215 }
216
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000217 void advanceIteratorNodeAndUpdateText()
218 {
219 ASSERT(shouldAdvanceIteratorPastCurrentNode());
220
221 StringBuilder stringBuilder;
sihui_liu@apple.com0e39ec02020-06-09 06:54:20 +0000222 Vector<String> text;
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000223 while (shouldAdvanceIteratorPastCurrentNode()) {
commit-queue@webkit.org10778122020-07-08 18:30:44 +0000224 auto iteratorText = m_iterator.text();
225 if (m_iterator.range().collapsed()) {
226 if (iteratorText == "\n") {
sihui_liu@apple.com0e39ec02020-06-09 06:54:20 +0000227 appendToText(text, stringBuilder);
228 text.append({ });
229 }
230 } else
commit-queue@webkit.org10778122020-07-08 18:30:44 +0000231 stringBuilder.append(iteratorText);
sihui_liu@apple.com0e39ec02020-06-09 06:54:20 +0000232
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000233 m_iterator.advance();
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000234 }
sihui_liu@apple.com0e39ec02020-06-09 06:54:20 +0000235 appendToText(text, stringBuilder);
236 m_text = text;
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000237 }
238
239 TextIterator m_iterator;
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000240 RefPtr<Node> m_node;
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000241 RefPtr<Node> m_pastEndNode;
sihui_liu@apple.com0e39ec02020-06-09 06:54:20 +0000242 Optional<Vector<String>> m_text;
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000243};
244
wenson_hsieh@apple.comf0ad7db2020-06-03 21:08:21 +0000245static bool shouldExtractValueForTextManipulation(const HTMLInputElement& input)
246{
aperez@igalia.comcb5f46712020-06-05 15:47:41 +0000247 if (input.isSearchField() || equalIgnoringASCIICase(input.attributeWithoutSynchronization(HTMLNames::typeAttr), InputTypeNames::text()))
wenson_hsieh@apple.comf0ad7db2020-06-03 21:08:21 +0000248 return !input.lastChangeWasUserEdit();
249
250 return input.isTextButton();
251}
252
253static bool isAttributeForTextManipulation(const QualifiedName& nameToCheck)
rniwa@webkit.orgc46d21d2019-12-16 22:36:55 +0000254{
rniwa@webkit.org640b0732020-03-08 03:52:06 +0000255 using namespace HTMLNames;
256 static const QualifiedName* const attributeNames[] = {
257 &titleAttr.get(),
258 &altAttr.get(),
259 &placeholderAttr.get(),
260 &aria_labelAttr.get(),
261 &aria_placeholderAttr.get(),
262 &aria_roledescriptionAttr.get(),
263 &aria_valuetextAttr.get(),
264 };
265 for (auto& entry : attributeNames) {
266 if (*entry == nameToCheck)
267 return true;
268 }
269 return false;
270}
271
wenson_hsieh@apple.com2d16b752020-04-20 20:19:54 +0000272static bool canPerformTextManipulationByReplacingEntireTextContent(const Element& element)
273{
274 return element.hasTagName(HTMLNames::titleTag) || element.hasTagName(HTMLNames::optionTag);
275}
276
wenson_hsieh@apple.comeeb352d2020-04-29 05:16:23 +0000277static Optional<TextManipulationController::ManipulationTokenInfo> tokenInfo(Node* node)
278{
279 if (!node)
280 return WTF::nullopt;
281
282 TextManipulationController::ManipulationTokenInfo result;
283 result.documentURL = node->document().url();
284 if (auto element = is<Element>(node) ? makeRefPtr(downcast<Element>(*node)) : makeRefPtr(node->parentElement())) {
285 result.tagName = element->tagName();
286 if (element->hasAttributeWithoutSynchronization(HTMLNames::roleAttr))
287 result.roleAttribute = element->attributeWithoutSynchronization(HTMLNames::roleAttr);
288 }
289 return result;
290}
291
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000292static bool isEnclosingItemBoundaryElement(const Element& element)
293{
294 auto* renderer = element.renderer();
295 if (!renderer)
296 return false;
297
298 auto role = [](const Element& element) -> AccessibilityRole {
299 return AccessibilityObject::ariaRoleToWebCoreRole(element.attributeWithoutSynchronization(HTMLNames::roleAttr));
300 };
301
302 if (element.hasTagName(HTMLNames::buttonTag) || role(element) == AccessibilityRole::Button)
303 return true;
304
commit-queue@webkit.orgcd8dd152020-07-05 23:32:57 +0000305 auto displayType = renderer->style().display();
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000306 if (element.hasTagName(HTMLNames::liTag) || element.hasTagName(HTMLNames::aTag)) {
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000307 if (displayType == DisplayType::Block || displayType == DisplayType::InlineBlock)
308 return true;
309
310 for (auto parent = makeRefPtr(element.parentElement()); parent; parent = parent->parentElement()) {
311 if (parent->hasTagName(HTMLNames::navTag) || role(*parent) == AccessibilityRole::LandmarkNavigation)
312 return true;
313 }
314 }
315
commit-queue@webkit.orgcd8dd152020-07-05 23:32:57 +0000316 if (element.hasTagName(HTMLNames::spanTag) && displayType == DisplayType::InlineBlock)
317 return true;
318
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000319 return false;
320}
321
sihui_liu@apple.com0e39ec02020-06-09 06:54:20 +0000322TextManipulationController::ManipulationUnit TextManipulationController::createUnit(const Vector<String>& text, Node& textNode)
wenson_hsieh@apple.com600d5b62020-06-05 19:59:00 +0000323{
sihui_liu@apple.com0e39ec02020-06-09 06:54:20 +0000324 ManipulationUnit unit = { textNode, { } };
325 for (auto& textEntry : text) {
326 if (!textEntry.isNull())
327 parse(unit, textEntry, textNode);
328 else {
329 if (unit.tokens.isEmpty())
330 unit.firstTokenContainsDelimiter = true;
331 unit.lastTokenContainsDelimiter = true;
332 }
333 }
334 return unit;
wenson_hsieh@apple.com600d5b62020-06-05 19:59:00 +0000335}
336
wenson_hsieh@apple.comf2cc5ed12020-06-25 20:37:34 +0000337bool TextManipulationController::shouldExcludeNodeBasedOnStyle(const Node& node)
338{
339 auto* style = node.renderStyle();
340 if (!style)
341 return false;
342
343 auto& font = style->fontCascade().primaryFont();
344 auto familyName = font.platformData().familyName();
345 if (familyName.isEmpty())
346 return false;
347
348 auto iter = m_cachedFontFamilyExclusionResults.find(familyName);
349 if (iter != m_cachedFontFamilyExclusionResults.end())
350 return iter->value;
351
352 // FIXME: We should reconsider whether a node should be excluded if the primary font
353 // used to render the node changes, since this "icon font" heuristic may return a
354 // different result.
355 bool result = font.isProbablyOnlyUsedToRenderIcons();
356 m_cachedFontFamilyExclusionResults.set(familyName, result);
357 return result;
358}
359
sihui_liu@apple.com0e39ec02020-06-09 06:54:20 +0000360void TextManipulationController::parse(ManipulationUnit& unit, const String& text, Node& textNode)
wenson_hsieh@apple.com600d5b62020-06-05 19:59:00 +0000361{
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000362 ExclusionRuleMatcher exclusionRuleMatcher(m_exclusionRules);
wenson_hsieh@apple.comf2cc5ed12020-06-25 20:37:34 +0000363 bool isNodeExcluded = exclusionRuleMatcher.isExcluded(&textNode) || shouldExcludeNodeBasedOnStyle(textNode);
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000364 size_t positionOfLastNonHTMLSpace = WTF::notFound;
365 size_t startPositionOfCurrentToken = 0;
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000366 size_t index = 0;
367 for (; index < text.length(); ++index) {
368 auto character = text[index];
wenson_hsieh@apple.com600d5b62020-06-05 19:59:00 +0000369 if (isTokenDelimiter(character)) {
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000370 if (positionOfLastNonHTMLSpace != WTF::notFound && startPositionOfCurrentToken <= positionOfLastNonHTMLSpace) {
sihui_liu@apple.com0e39ec02020-06-09 06:54:20 +0000371 auto stringForToken = text.substring(startPositionOfCurrentToken, positionOfLastNonHTMLSpace + 1 - startPositionOfCurrentToken);
372 unit.tokens.append(ManipulationToken { m_tokenIdentifier.generate(), stringForToken, tokenInfo(&textNode), isNodeExcluded });
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000373 startPositionOfCurrentToken = positionOfLastNonHTMLSpace + 1;
374 }
375
wenson_hsieh@apple.com600d5b62020-06-05 19:59:00 +0000376 while (index < text.length() && (isHTMLSpace(text[index]) || isInPrivateUseArea(text[index])))
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000377 ++index;
378
379 --index;
380
sihui_liu@apple.com0e39ec02020-06-09 06:54:20 +0000381 auto stringForToken = text.substring(startPositionOfCurrentToken, index + 1 - startPositionOfCurrentToken);
382 if (unit.tokens.isEmpty() && !unit.firstTokenContainsDelimiter)
383 unit.firstTokenContainsDelimiter = true;
384 unit.tokens.append(ManipulationToken { m_tokenIdentifier.generate(), stringForToken, tokenInfo(&textNode), true });
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000385 startPositionOfCurrentToken = index + 1;
sihui_liu@apple.com0e39ec02020-06-09 06:54:20 +0000386 unit.lastTokenContainsDelimiter = true;
wenson_hsieh@apple.com600d5b62020-06-05 19:59:00 +0000387 } else if (isNotHTMLSpace(character)) {
sihui_liu@apple.com0e39ec02020-06-09 06:54:20 +0000388 if (!isNodeExcluded)
389 unit.areAllTokensExcluded = false;
wenson_hsieh@apple.com600d5b62020-06-05 19:59:00 +0000390 positionOfLastNonHTMLSpace = index;
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000391 }
392 }
393
394 if (startPositionOfCurrentToken < text.length()) {
sihui_liu@apple.com0e39ec02020-06-09 06:54:20 +0000395 auto stringForToken = text.substring(startPositionOfCurrentToken, index + 1 - startPositionOfCurrentToken);
396 unit.tokens.append(ManipulationToken { m_tokenIdentifier.generate(), stringForToken, tokenInfo(&textNode), isNodeExcluded });
397 unit.lastTokenContainsDelimiter = false;
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000398 }
sihui_liu@apple.com33d45222020-06-05 06:13:30 +0000399}
400
401void TextManipulationController::addItemIfPossible(Vector<ManipulationUnit>&& units)
402{
403 if (units.isEmpty())
404 return;
405
406 size_t index = 0;
407 size_t end = units.size();
408 while (index < units.size() && units[index].areAllTokensExcluded)
409 ++index;
410
411 while (end > 0 && units[end - 1].areAllTokensExcluded)
412 --end;
413
414 if (index == end)
415 return;
416
sihui_liu@apple.coma6421802020-06-09 07:11:12 +0000417 ASSERT(end);
418 auto startPosition = firstPositionInOrBeforeNode(units[index].node.ptr());
419 auto endPosition = positionAfterNode(units[end - 1].node.ptr());
sihui_liu@apple.com33d45222020-06-05 06:13:30 +0000420 Vector<ManipulationToken> tokens;
421 for (; index < end; ++index)
422 tokens.appendVector(WTFMove(units[index].tokens));
423
424 addItem(ManipulationItemData { startPosition, endPosition, nullptr, nullQName(), WTFMove(tokens) });
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000425}
426
rniwa@webkit.org640b0732020-03-08 03:52:06 +0000427void TextManipulationController::observeParagraphs(const Position& start, const Position& end)
428{
wenson_hsieh@apple.comf0a232a2020-07-07 00:54:23 +0000429 if (start.isNull() || end.isNull() || start.isOrphan() || end.isOrphan())
wenson_hsieh@apple.comc626f5c2020-04-08 23:59:31 +0000430 return;
431
rniwa@webkit.org640b0732020-03-08 03:52:06 +0000432 auto document = makeRefPtr(start.document());
rniwa@webkit.orgc46d21d2019-12-16 22:36:55 +0000433 ASSERT(document);
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000434 // TextIterator's constructor may have updated the layout and executed arbitrary scripts.
rniwa@webkit.org640b0732020-03-08 03:52:06 +0000435 if (document != start.document() || document != end.document())
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000436 return;
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000437
sihui_liu@apple.com33d45222020-06-05 06:13:30 +0000438 Vector<ManipulationUnit> unitsInCurrentParagraph;
wenson_hsieh@apple.com5f606e42020-06-16 18:11:23 +0000439 Vector<Ref<Element>> enclosingItemBoundaryElements;
sihui_liu@apple.com33d45222020-06-05 06:13:30 +0000440 ParagraphContentIterator iterator { start, end };
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000441 for (; !iterator.atEnd(); iterator.advance()) {
442 auto content = iterator.currentContent();
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000443 auto* contentNode = content.node.get();
444 ASSERT(contentNode);
wenson_hsieh@apple.comf9a72332020-04-24 23:18:05 +0000445
wenson_hsieh@apple.com5f606e42020-06-16 18:11:23 +0000446 while (!enclosingItemBoundaryElements.isEmpty() && !enclosingItemBoundaryElements.last()->contains(contentNode)) {
sihui_liu@apple.com33d45222020-06-05 06:13:30 +0000447 addItemIfPossible(std::exchange(unitsInCurrentParagraph, { }));
wenson_hsieh@apple.com5f606e42020-06-16 18:11:23 +0000448 enclosingItemBoundaryElements.removeLast();
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000449 }
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000450
sihui_liu@apple.com65a25812020-06-26 16:41:15 +0000451 if (m_manipulatedNodes.contains(contentNode)) {
452 addItemIfPossible(std::exchange(unitsInCurrentParagraph, { }));
453 continue;
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000454 }
455
456 if (is<Element>(*contentNode)) {
457 auto& currentElement = downcast<Element>(*contentNode);
458 if (!content.isTextContent && canPerformTextManipulationByReplacingEntireTextContent(currentElement))
459 addItem(ManipulationItemData { Position(), Position(), makeWeakPtr(currentElement), nullQName(), { ManipulationToken { m_tokenIdentifier.generate(), currentElement.textContent(), tokenInfo(&currentElement) } } });
460
461 if (currentElement.hasAttributes()) {
462 for (auto& attribute : currentElement.attributesIterator()) {
wenson_hsieh@apple.comf0ad7db2020-06-03 21:08:21 +0000463 if (isAttributeForTextManipulation(attribute.name()))
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000464 addItem(ManipulationItemData { Position(), Position(), makeWeakPtr(currentElement), attribute.name(), { ManipulationToken { m_tokenIdentifier.generate(), attribute.value(), tokenInfo(&currentElement) } } });
rniwa@webkit.org640b0732020-03-08 03:52:06 +0000465 }
rniwa@webkit.org640b0732020-03-08 03:52:06 +0000466 }
wenson_hsieh@apple.comf0ad7db2020-06-03 21:08:21 +0000467
468 if (is<HTMLInputElement>(currentElement)) {
469 auto& input = downcast<HTMLInputElement>(currentElement);
470 if (shouldExtractValueForTextManipulation(input))
aperez@igalia.comcb5f46712020-06-05 15:47:41 +0000471 addItem(ManipulationItemData { { }, { }, makeWeakPtr(currentElement), HTMLNames::valueAttr, { ManipulationToken { m_tokenIdentifier.generate(), input.value(), tokenInfo(&currentElement) } } });
wenson_hsieh@apple.comf0ad7db2020-06-03 21:08:21 +0000472 }
473
wenson_hsieh@apple.com5f606e42020-06-16 18:11:23 +0000474 if (isEnclosingItemBoundaryElement(currentElement)) {
475 addItemIfPossible(std::exchange(unitsInCurrentParagraph, { }));
476 enclosingItemBoundaryElements.append(currentElement);
477 }
rniwa@webkit.org95386982020-02-28 03:37:22 +0000478 }
479
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000480 if (content.isReplacedContent) {
sihui_liu@apple.com33d45222020-06-05 06:13:30 +0000481 if (!unitsInCurrentParagraph.isEmpty())
sihui_liu@apple.com0e39ec02020-06-09 06:54:20 +0000482 unitsInCurrentParagraph.append(ManipulationUnit { *contentNode, { ManipulationToken { m_tokenIdentifier.generate(), "[]", tokenInfo(content.node.get()), true } } });
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000483 continue;
484 }
485
486 if (!content.isTextContent)
487 continue;
488
sihui_liu@apple.com0e39ec02020-06-09 06:54:20 +0000489 auto currentUnit = createUnit(content.text, *contentNode);
490 if (currentUnit.firstTokenContainsDelimiter)
sihui_liu@apple.com33d45222020-06-05 06:13:30 +0000491 addItemIfPossible(std::exchange(unitsInCurrentParagraph, { }));
sihui_liu@apple.com5a2c1d32020-04-23 17:23:54 +0000492
sihui_liu@apple.com0e39ec02020-06-09 06:54:20 +0000493 if (unitsInCurrentParagraph.isEmpty() && currentUnit.areAllTokensExcluded)
494 continue;
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000495
sihui_liu@apple.com0e39ec02020-06-09 06:54:20 +0000496 bool currentUnitEndsWithDelimiter = currentUnit.lastTokenContainsDelimiter;
497 unitsInCurrentParagraph.append(WTFMove(currentUnit));
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000498
sihui_liu@apple.com0e39ec02020-06-09 06:54:20 +0000499 if (currentUnitEndsWithDelimiter)
sihui_liu@apple.com33d45222020-06-05 06:13:30 +0000500 addItemIfPossible(std::exchange(unitsInCurrentParagraph, { }));
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000501 }
502
sihui_liu@apple.com33d45222020-06-05 06:13:30 +0000503 addItemIfPossible(std::exchange(unitsInCurrentParagraph, { }));
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000504}
505
rniwa@webkit.orgc46d21d2019-12-16 22:36:55 +0000506void TextManipulationController::didCreateRendererForElement(Element& element)
507{
sihui_liu@apple.com65a25812020-06-26 16:41:15 +0000508 if (m_manipulatedNodes.contains(&element))
wenson_hsieh@apple.com54632b72020-01-10 00:40:14 +0000509 return;
510
commit-queue@webkit.org3468c732020-07-13 17:36:21 +0000511 scheduleObservationUpdate();
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000512
513 if (is<PseudoElement>(element)) {
514 if (auto* host = downcast<PseudoElement>(element).hostElement())
rniwa@webkit.org95386982020-02-28 03:37:22 +0000515 m_elementsWithNewRenderer.add(*host);
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000516 } else
rniwa@webkit.org95386982020-02-28 03:37:22 +0000517 m_elementsWithNewRenderer.add(element);
rniwa@webkit.orgc46d21d2019-12-16 22:36:55 +0000518}
519
commit-queue@webkit.org3468c732020-07-13 17:36:21 +0000520void TextManipulationController::didUpdateContentForText(Text& text)
521{
522 if (!m_manipulatedNodes.contains(&text))
523 return;
524
525 scheduleObservationUpdate();
526
527 m_manipulatedTextsWithNewContent.add(&text);
528}
529
wenson_hsieh@apple.comd0ed2bd2020-04-24 00:07:25 +0000530void TextManipulationController::scheduleObservationUpdate()
rniwa@webkit.orgc46d21d2019-12-16 22:36:55 +0000531{
commit-queue@webkit.org3468c732020-07-13 17:36:21 +0000532 // An update is already scheduled.
533 if (!m_manipulatedTextsWithNewContent.isEmpty() || !m_elementsWithNewRenderer.computesEmpty())
534 return;
535
rniwa@webkit.orgc46d21d2019-12-16 22:36:55 +0000536 if (!m_document)
537 return;
538
539 m_document->eventLoop().queueTask(TaskSource::InternalAsyncTask, [weakThis = makeWeakPtr(*this)] {
540 auto* controller = weakThis.get();
541 if (!controller)
542 return;
543
commit-queue@webkit.org3468c732020-07-13 17:36:21 +0000544 HashSet<Ref<Node>> nodesToObserve;
rniwa@webkit.org95386982020-02-28 03:37:22 +0000545 for (auto& weakElement : controller->m_elementsWithNewRenderer)
commit-queue@webkit.org3468c732020-07-13 17:36:21 +0000546 nodesToObserve.add(weakElement);
rniwa@webkit.org95386982020-02-28 03:37:22 +0000547 controller->m_elementsWithNewRenderer.clear();
rniwa@webkit.orgc46d21d2019-12-16 22:36:55 +0000548
commit-queue@webkit.org3468c732020-07-13 17:36:21 +0000549 for (auto* text : controller->m_manipulatedTextsWithNewContent) {
550 if (!controller->m_manipulatedNodes.contains(text))
551 continue;
552 controller->m_manipulatedNodes.remove(text);
553 nodesToObserve.add(*text);
554 }
555 controller->m_manipulatedTextsWithNewContent.clear();
556
557 if (nodesToObserve.isEmpty())
sihui_liu@apple.com65a25812020-06-26 16:41:15 +0000558 return;
559
560 RefPtr<Node> commonAncestor;
commit-queue@webkit.org3468c732020-07-13 17:36:21 +0000561 for (auto& node : nodesToObserve) {
sihui_liu@apple.com65a25812020-06-26 16:41:15 +0000562 if (!commonAncestor)
commit-queue@webkit.org3468c732020-07-13 17:36:21 +0000563 commonAncestor = is<ContainerNode>(node.get()) ? node.ptr() : node->parentNode();
564 else if (!node->isDescendantOf(commonAncestor.get()))
565 commonAncestor = commonInclusiveAncestor(*commonAncestor, node.get());
rniwa@webkit.orgc46d21d2019-12-16 22:36:55 +0000566 }
commit-queue@webkit.org3468c732020-07-13 17:36:21 +0000567
sihui_liu@apple.com65a25812020-06-26 16:41:15 +0000568 auto start = firstPositionInOrBeforeNode(commonAncestor.get());
569 auto end = lastPositionInOrAfterNode(commonAncestor.get());
570 controller->observeParagraphs(start, end);
rniwa@webkit.orgc46d21d2019-12-16 22:36:55 +0000571
sihui_liu@apple.com65a25812020-06-26 16:41:15 +0000572 if (controller->m_items.isEmpty()) {
573 controller->m_manipulatedNodes.add(commonAncestor.get());
574 return;
rniwa@webkit.orgc46d21d2019-12-16 22:36:55 +0000575 }
sihui_liu@apple.com65a25812020-06-26 16:41:15 +0000576
rniwa@webkit.orge44b91352020-03-04 05:22:14 +0000577 controller->flushPendingItemsForCallback();
rniwa@webkit.orgc46d21d2019-12-16 22:36:55 +0000578 });
579}
580
rniwa@webkit.org640b0732020-03-08 03:52:06 +0000581void TextManipulationController::addItem(ManipulationItemData&& itemData)
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000582{
rniwa@webkit.orge44b91352020-03-04 05:22:14 +0000583 const unsigned itemCallbackBatchingSize = 128;
584
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000585 ASSERT(m_document);
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000586 ASSERT(!itemData.tokens.isEmpty());
rniwa@webkit.orge44b91352020-03-04 05:22:14 +0000587 auto newID = m_itemIdentifier.generate();
588 m_pendingItemsForCallback.append(ManipulationItem {
589 newID,
rniwa@webkit.org640b0732020-03-08 03:52:06 +0000590 itemData.tokens.map([](auto& token) { return token; })
rniwa@webkit.orge44b91352020-03-04 05:22:14 +0000591 });
rniwa@webkit.org640b0732020-03-08 03:52:06 +0000592 m_items.add(newID, WTFMove(itemData));
rniwa@webkit.orge44b91352020-03-04 05:22:14 +0000593
594 if (m_pendingItemsForCallback.size() >= itemCallbackBatchingSize)
595 flushPendingItemsForCallback();
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000596}
597
rniwa@webkit.orge44b91352020-03-04 05:22:14 +0000598void TextManipulationController::flushPendingItemsForCallback()
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000599{
rniwa@webkit.orge44b91352020-03-04 05:22:14 +0000600 m_callback(*m_document, m_pendingItemsForCallback);
601 m_pendingItemsForCallback.clear();
602}
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000603
rniwa@webkit.orge44b91352020-03-04 05:22:14 +0000604auto TextManipulationController::completeManipulation(const Vector<WebCore::TextManipulationController::ManipulationItem>& completionItems) -> Vector<ManipulationFailure>
605{
606 Vector<ManipulationFailure> failures;
wenson_hsieh@apple.com3cfd1f12020-07-17 19:58:35 +0000607 HashSet<Ref<Node>> containersWithoutVisualOverflowBeforeReplacement;
rniwa@webkit.orge44b91352020-03-04 05:22:14 +0000608 for (unsigned i = 0; i < completionItems.size(); ++i) {
609 auto& itemToComplete = completionItems[i];
610 auto identifier = itemToComplete.identifier;
611 if (!identifier) {
612 failures.append(ManipulationFailure { identifier, i, ManipulationFailureType::InvalidItem });
613 continue;
614 }
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000615
rniwa@webkit.orge44b91352020-03-04 05:22:14 +0000616 auto itemDataIterator = m_items.find(identifier);
617 if (itemDataIterator == m_items.end()) {
618 failures.append(ManipulationFailure { identifier, i, ManipulationFailureType::InvalidItem });
619 continue;
620 }
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000621
rniwa@webkit.orge44b91352020-03-04 05:22:14 +0000622 ManipulationItemData itemData;
623 std::exchange(itemData, itemDataIterator->value);
624 m_items.remove(itemDataIterator);
625
wenson_hsieh@apple.com3cfd1f12020-07-17 19:58:35 +0000626 auto failureOrNullopt = replace(itemData, itemToComplete.tokens, containersWithoutVisualOverflowBeforeReplacement);
rniwa@webkit.orge44b91352020-03-04 05:22:14 +0000627 if (failureOrNullopt)
628 failures.append(ManipulationFailure { identifier, i, *failureOrNullopt });
629 }
wenson_hsieh@apple.com3cfd1f12020-07-17 19:58:35 +0000630
631 if (!containersWithoutVisualOverflowBeforeReplacement.isEmpty()) {
632 if (m_document)
633 m_document->updateLayoutIgnorePendingStylesheets();
634
635 for (auto& container : containersWithoutVisualOverflowBeforeReplacement) {
636 if (!is<StyledElement>(container))
637 continue;
638
639 auto& element = downcast<StyledElement>(container.get());
640 auto* box = element.renderBox();
641 if (!box || !box->hasVisualOverflow())
642 continue;
643
644 auto* style = element.renderStyle();
645 if (style && style->width().isFixed() && style->height().isFixed() && !style->hasOutOfFlowPosition() && !style->hasClip())
646 element.setInlineStyleProperty(CSSPropertyOverflow, CSSValueHidden);
647 }
648 }
649
rniwa@webkit.orge44b91352020-03-04 05:22:14 +0000650 return failures;
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000651}
652
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000653struct TokenExchangeData {
654 RefPtr<Node> node;
655 String originalContent;
656 bool isExcluded { false };
657 bool isConsumed { false };
658};
659
660struct ReplacementData {
661 Ref<Node> originalNode;
rniwa@webkit.org7afdb0b2019-10-25 20:41:15 +0000662 String newData;
663};
664
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000665Vector<Ref<Node>> TextManipulationController::getPath(Node* ancestor, Node* node)
666{
667 Vector<Ref<Node>> path;
668 RefPtr<ContainerNode> containerNode = is<ContainerNode>(*node) ? &downcast<ContainerNode>(*node) : node->parentNode();
669 for (; containerNode && containerNode != ancestor; containerNode = containerNode->parentNode())
670 path.append(*containerNode);
671 path.reverse();
672 return path;
673}
674
sihui_liu@apple.com65a25812020-06-26 16:41:15 +0000675void TextManipulationController::updateInsertions(Vector<NodeEntry>& lastTopDownPath, const Vector<Ref<Node>>& currentTopDownPath, Node* currentNode, HashSet<Ref<Node>>& insertedNodes, Vector<NodeInsertion>& insertions)
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000676{
wenson_hsieh@apple.come0dde552020-06-17 01:34:01 +0000677 size_t i = 0;
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000678 while (i < lastTopDownPath.size() && i < currentTopDownPath.size() && lastTopDownPath[i].first.ptr() == currentTopDownPath[i].ptr())
679 ++i;
680
681 if (i != lastTopDownPath.size() || i != currentTopDownPath.size()) {
682 if (i < lastTopDownPath.size())
683 lastTopDownPath.shrink(i);
684
685 for (;i < currentTopDownPath.size(); ++i) {
686 Ref<Node> node = currentTopDownPath[i];
687 if (!insertedNodes.add(node.copyRef()).isNewEntry) {
688 auto clonedNode = node->cloneNodeInternal(node->document(), Node::CloningOperation::OnlySelf);
689 if (auto* data = node->eventTargetData())
690 data->eventListenerMap.copyEventListenersNotCreatedFromMarkupToTarget(clonedNode.ptr());
691 node = WTFMove(clonedNode);
692 }
693 insertions.append(NodeInsertion { lastTopDownPath.size() ? lastTopDownPath.last().second.ptr() : nullptr, node.copyRef() });
694 lastTopDownPath.append({ currentTopDownPath[i].copyRef(), WTFMove(node) });
695 }
696 }
697
698 if (currentNode)
sihui_liu@apple.com65a25812020-06-26 16:41:15 +0000699 insertions.append(NodeInsertion { lastTopDownPath.size() ? lastTopDownPath.last().second.ptr() : nullptr, *currentNode });
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000700}
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000701
wenson_hsieh@apple.com3cfd1f12020-07-17 19:58:35 +0000702auto TextManipulationController::replace(const ManipulationItemData& item, const Vector<ManipulationToken>& replacementTokens, HashSet<Ref<Node>>& containersWithoutVisualOverflowBeforeReplacement) -> Optional<ManipulationFailureType>
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000703{
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000704 if (item.start.isOrphan() || item.end.isOrphan())
rniwa@webkit.orge44b91352020-03-04 05:22:14 +0000705 return ManipulationFailureType::ContentChanged;
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000706
darin@apple.comd2f1a002020-03-16 23:02:10 +0000707 if (item.start.isNull() || item.end.isNull()) {
rniwa@webkit.org640b0732020-03-08 03:52:06 +0000708 RELEASE_ASSERT(item.tokens.size() == 1);
709 auto element = makeRefPtr(item.element.get());
710 if (!element)
711 return ManipulationFailureType::ContentChanged;
sihui_liu@apple.com33fbdee2020-06-03 19:04:28 +0000712 if (replacementTokens.size() > 1 && !canPerformTextManipulationByReplacingEntireTextContent(*element) && item.attributeName == nullQName())
rniwa@webkit.org640b0732020-03-08 03:52:06 +0000713 return ManipulationFailureType::InvalidToken;
wenson_hsieh@apple.com2d16b752020-04-20 20:19:54 +0000714 auto expectedTokenIdentifier = item.tokens[0].identifier;
715 StringBuilder newValue;
716 for (size_t i = 0; i < replacementTokens.size(); ++i) {
717 if (replacementTokens[i].identifier != expectedTokenIdentifier)
rniwa@webkit.org640b0732020-03-08 03:52:06 +0000718 return ManipulationFailureType::InvalidToken;
wenson_hsieh@apple.com2d16b752020-04-20 20:19:54 +0000719 if (i)
720 newValue.append(' ');
721 newValue.append(replacementTokens[i].content);
rniwa@webkit.org640b0732020-03-08 03:52:06 +0000722 }
723 if (item.attributeName == nullQName())
wenson_hsieh@apple.com2d16b752020-04-20 20:19:54 +0000724 element->setTextContent(newValue.toString());
aperez@igalia.comcb5f46712020-06-05 15:47:41 +0000725 else if (item.attributeName == HTMLNames::valueAttr && is<HTMLInputElement>(*element))
wenson_hsieh@apple.comf0ad7db2020-06-03 21:08:21 +0000726 downcast<HTMLInputElement>(*element).setValue(newValue.toString());
rniwa@webkit.org640b0732020-03-08 03:52:06 +0000727 else
wenson_hsieh@apple.com2d16b752020-04-20 20:19:54 +0000728 element->setAttribute(item.attributeName, newValue.toString());
rniwa@webkit.org640b0732020-03-08 03:52:06 +0000729 return WTF::nullopt;
730 }
731
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000732 size_t currentTokenIndex = 0;
733 HashMap<TokenIdentifier, TokenExchangeData> tokenExchangeMap;
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000734 RefPtr<Node> commonAncestor;
rniwa@webkit.orgbf66d0a2020-03-12 23:29:31 +0000735 RefPtr<Node> firstContentNode;
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000736 RefPtr<Node> lastChildOfCommonAncestorInRange;
rniwa@webkit.orgbf66d0a2020-03-12 23:29:31 +0000737 HashSet<Ref<Node>> nodesToRemove;
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000738 ParagraphContentIterator iterator { item.start, item.end };
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000739 for (; !iterator.atEnd(); iterator.advance()) {
740 auto content = iterator.currentContent();
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000741 ASSERT(content.node);
742
743 lastChildOfCommonAncestorInRange = content.node;
744 nodesToRemove.add(*content.node);
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000745
746 if (!content.isReplacedContent && !content.isTextContent)
747 continue;
748
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000749 Vector<ManipulationToken> tokensInCurrentNode;
sihui_liu@apple.com5f91c3b2020-06-04 23:58:32 +0000750 if (content.isReplacedContent) {
751 if (currentTokenIndex >= item.tokens.size())
752 return ManipulationFailureType::ContentChanged;
753
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000754 tokensInCurrentNode.append(item.tokens[currentTokenIndex]);
sihui_liu@apple.com5f91c3b2020-06-04 23:58:32 +0000755 } else
sihui_liu@apple.com0e39ec02020-06-09 06:54:20 +0000756 tokensInCurrentNode = createUnit(content.text, *content.node).tokens;
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000757
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000758 bool isNodeIncluded = WTF::anyOf(tokensInCurrentNode, [] (auto& token) {
759 return !token.isExcluded;
760 });
761 for (auto& token : tokensInCurrentNode) {
sihui_liu@apple.com5f91c3b2020-06-04 23:58:32 +0000762 if (currentTokenIndex >= item.tokens.size())
sihui_liu@apple.com5a2c1d32020-04-23 17:23:54 +0000763 return ManipulationFailureType::ContentChanged;
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000764
765 auto& currentToken = item.tokens[currentTokenIndex++];
766 if (!content.isReplacedContent && currentToken.content != token.content)
767 return ManipulationFailureType::ContentChanged;
768
769 tokenExchangeMap.set(currentToken.identifier, TokenExchangeData { content.node.copyRef(), currentToken.content, !isNodeIncluded });
sihui_liu@apple.com5a2c1d32020-04-23 17:23:54 +0000770 }
771
rniwa@webkit.orgbf66d0a2020-03-12 23:29:31 +0000772 if (!firstContentNode)
773 firstContentNode = content.node;
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000774
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000775 auto parentNode = content.node->parentNode();
776 if (!commonAncestor)
777 commonAncestor = parentNode;
778 else if (!parentNode->isDescendantOf(commonAncestor.get())) {
779 commonAncestor = commonInclusiveAncestor(*commonAncestor, *parentNode);
780 ASSERT(commonAncestor);
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000781 }
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000782 }
783
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000784 while (lastChildOfCommonAncestorInRange && lastChildOfCommonAncestorInRange->parentNode() != commonAncestor)
785 lastChildOfCommonAncestorInRange = lastChildOfCommonAncestorInRange->parentNode();
786
rniwa@webkit.orgbf66d0a2020-03-12 23:29:31 +0000787 for (auto node = commonAncestor; node; node = node->parentNode())
788 nodesToRemove.remove(*node);
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000789
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000790 HashSet<Ref<Node>> reusedOriginalNodes;
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000791 Vector<NodeEntry> lastTopDownPath;
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000792 Vector<NodeInsertion> insertions;
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000793 for (size_t index = 0; index < replacementTokens.size(); ++index) {
794 auto& replacementToken = replacementTokens[index];
795 auto it = tokenExchangeMap.find(replacementToken.identifier);
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000796 if (it == tokenExchangeMap.end())
rniwa@webkit.orge44b91352020-03-04 05:22:14 +0000797 return ManipulationFailureType::InvalidToken;
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000798
799 auto& exchangeData = it->value;
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000800 auto* originalNode = exchangeData.node.get();
801 ASSERT(originalNode);
802 auto replacementText = replacementToken.content;
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000803
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000804 RefPtr<Node> replacementNode;
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000805 if (exchangeData.isExcluded) {
806 if (exchangeData.isConsumed)
rniwa@webkit.orge44b91352020-03-04 05:22:14 +0000807 return ManipulationFailureType::ExclusionViolation;
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000808 exchangeData.isConsumed = true;
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000809
810 if (!replacementToken.content.isNull() && replacementToken.content != exchangeData.originalContent)
rniwa@webkit.orge44b91352020-03-04 05:22:14 +0000811 return ManipulationFailureType::ExclusionViolation;
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000812
813 replacementNode = originalNode;
814 for (RefPtr<Node> descendentNode = NodeTraversal::next(*originalNode, originalNode); descendentNode; descendentNode = NodeTraversal::next(*descendentNode, originalNode))
815 nodesToRemove.remove(*descendentNode);
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000816 } else
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000817 replacementNode = Text::create(commonAncestor->document(), replacementText);
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000818
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000819 auto topDownPath = getPath(commonAncestor.get(), originalNode);
820 updateInsertions(lastTopDownPath, topDownPath, replacementNode.get(), reusedOriginalNodes, insertions);
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000821 }
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000822
823 auto end = lastChildOfCommonAncestorInRange ? positionInParentAfterNode(lastChildOfCommonAncestorInRange.get()) : positionAfterNode(commonAncestor.get());
824 RefPtr<Node> node = item.end.firstNode();
825 RefPtr<Node> endNode = end.firstNode();
826 if (node && node != endNode) {
827 auto topDownPath = getPath(commonAncestor.get(), node->parentNode());
sihui_liu@apple.com65a25812020-06-26 16:41:15 +0000828 updateInsertions(lastTopDownPath, topDownPath, nullptr, reusedOriginalNodes, insertions);
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000829 }
830 while (node != endNode) {
831 Ref<Node> parentNode = *node->parentNode();
832 while (!lastTopDownPath.isEmpty() && lastTopDownPath.last().first.ptr() != parentNode.ptr())
833 lastTopDownPath.removeLast();
834
sihui_liu@apple.com2016ded2020-06-15 17:51:38 +0000835 insertions.append(NodeInsertion { lastTopDownPath.size() ? lastTopDownPath.last().second.ptr() : nullptr, *node, IsNodeManipulated::No });
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000836 lastTopDownPath.append({ *node, *node });
837 node = NodeTraversal::next(*node);
838 }
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000839
rniwa@webkit.orgbf66d0a2020-03-12 23:29:31 +0000840 Position insertionPoint = positionBeforeNode(firstContentNode.get()).parentAnchoredEquivalent();
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000841 while (insertionPoint.containerNode() != commonAncestor)
842 insertionPoint = positionInParentBeforeNode(insertionPoint.containerNode());
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000843
844 for (auto& node : nodesToRemove)
845 node->remove();
846
847 for (auto& insertion : insertions) {
wenson_hsieh@apple.com3cfd1f12020-07-17 19:58:35 +0000848 auto parentContainer = insertion.parentIfDifferentFromCommonAncestor;
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000849 if (!insertion.parentIfDifferentFromCommonAncestor) {
wenson_hsieh@apple.com3cfd1f12020-07-17 19:58:35 +0000850 parentContainer = insertionPoint.containerNode();
851 parentContainer->insertBefore(insertion.child, insertionPoint.computeNodeAfterPosition());
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000852 insertionPoint = positionInParentAfterNode(insertion.child.ptr());
sihui_liu@apple.com65a25812020-06-26 16:41:15 +0000853 } else
854 insertion.parentIfDifferentFromCommonAncestor->appendChild(insertion.child);
wenson_hsieh@apple.come0dde552020-06-17 01:34:01 +0000855
wenson_hsieh@apple.com3cfd1f12020-07-17 19:58:35 +0000856 if (auto* box = parentContainer->renderBox()) {
857 if (!box->hasVisualOverflow())
858 containersWithoutVisualOverflowBeforeReplacement.add(*parentContainer);
859 }
860
sihui_liu@apple.com65a25812020-06-26 16:41:15 +0000861 if (insertion.isChildManipulated == IsNodeManipulated::Yes)
862 m_manipulatedNodes.add(insertion.child.ptr());
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000863 }
rniwa@webkit.org7afdb0b2019-10-25 20:41:15 +0000864
rniwa@webkit.orge44b91352020-03-04 05:22:14 +0000865 return WTF::nullopt;
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000866}
867
sihui_liu@apple.com65a25812020-06-26 16:41:15 +0000868void TextManipulationController::removeNode(Node* node)
869{
870 m_manipulatedNodes.remove(node);
871}
872
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000873} // namespace WebCore