blob: b5ffb9d5cd97efba2315f6e8b88f8faa411b9641 [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)
darin@apple.comaf46d1b2020-03-11 04:07:19 +0000150 , m_iteratorNode(m_iterator.atEnd() ? nullptr : createLiveRange(m_iterator.range())->firstNode())
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000151 , m_node(start.firstNode())
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000152 , m_pastEndNode(end.firstNode())
153 {
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000154 if (shouldAdvanceIteratorPastCurrentNode())
155 advanceIteratorNodeAndUpdateText();
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000156 }
157
158 void advance()
159 {
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000160 m_text = WTF::nullopt;
161 advanceNode();
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000162
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000163 if (shouldAdvanceIteratorPastCurrentNode())
164 advanceIteratorNodeAndUpdateText();
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000165 }
166
167 struct CurrentContent {
168 RefPtr<Node> node;
sihui_liu@apple.com0e39ec02020-06-09 06:54:20 +0000169 Vector<String> text;
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000170 bool isTextContent { false };
171 bool isReplacedContent { false };
172 };
173
174 CurrentContent currentContent()
175 {
sihui_liu@apple.com0e39ec02020-06-09 06:54:20 +0000176 CurrentContent content = { m_node.copyRef(), m_text ? m_text.value() : Vector<String> { }, !!m_text };
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000177 if (content.node) {
178 if (auto* renderer = content.node->renderer()) {
179 if (renderer->isRenderReplaced()) {
180 content.isTextContent = false;
181 content.isReplacedContent = true;
182 }
183 }
184 }
185 return content;
186 }
187
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000188 bool atEnd() const { return !m_text && m_iterator.atEnd() && m_node == m_pastEndNode; }
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000189
190private:
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000191 bool shouldAdvanceIteratorPastCurrentNode() const { return !m_iterator.atEnd() && m_iteratorNode == m_node; }
192
193 void advanceNode()
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000194 {
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000195 if (m_node == m_pastEndNode)
sihui_liu@apple.com5a2c1d32020-04-23 17:23:54 +0000196 return;
197
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000198 m_node = NodeTraversal::next(*m_node);
199 if (!m_node)
200 m_node = m_pastEndNode;
201 }
202
sihui_liu@apple.com0e39ec02020-06-09 06:54:20 +0000203 void appendToText(Vector<String>& text, StringBuilder& stringBuilder)
204 {
205 if (!stringBuilder.isEmpty()) {
206 text.append(stringBuilder.toString());
207 stringBuilder.clear();
208 }
209 }
210
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000211 void advanceIteratorNodeAndUpdateText()
212 {
213 ASSERT(shouldAdvanceIteratorPastCurrentNode());
214
215 StringBuilder stringBuilder;
sihui_liu@apple.com0e39ec02020-06-09 06:54:20 +0000216 Vector<String> text;
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000217 while (shouldAdvanceIteratorPastCurrentNode()) {
sihui_liu@apple.com0e39ec02020-06-09 06:54:20 +0000218 if (!m_iterator.node()) {
219 auto iteratorText = m_iterator.text();
220 bool containsDelimiter = false;
221 for (unsigned index = 0; index < iteratorText.length() && !containsDelimiter; ++index)
222 containsDelimiter = isTokenDelimiter(iteratorText[index]);
223
224 if (containsDelimiter) {
225 appendToText(text, stringBuilder);
226 text.append({ });
227 }
228 } else
229 stringBuilder.append(m_iterator.text());
230
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000231 m_iterator.advance();
232 m_iteratorNode = m_iterator.atEnd() ? nullptr : createLiveRange(m_iterator.range())->firstNode();
233 }
sihui_liu@apple.com0e39ec02020-06-09 06:54:20 +0000234 appendToText(text, stringBuilder);
235 m_text = text;
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000236 }
237
238 TextIterator m_iterator;
239 RefPtr<Node> m_iteratorNode;
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
305 if (element.hasTagName(HTMLNames::liTag) || element.hasTagName(HTMLNames::aTag)) {
306 auto displayType = renderer->style().display();
307 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
316 return false;
317}
318
sihui_liu@apple.com0e39ec02020-06-09 06:54:20 +0000319TextManipulationController::ManipulationUnit TextManipulationController::createUnit(const Vector<String>& text, Node& textNode)
wenson_hsieh@apple.com600d5b62020-06-05 19:59:00 +0000320{
sihui_liu@apple.com0e39ec02020-06-09 06:54:20 +0000321 ManipulationUnit unit = { textNode, { } };
322 for (auto& textEntry : text) {
323 if (!textEntry.isNull())
324 parse(unit, textEntry, textNode);
325 else {
326 if (unit.tokens.isEmpty())
327 unit.firstTokenContainsDelimiter = true;
328 unit.lastTokenContainsDelimiter = true;
329 }
330 }
331 return unit;
wenson_hsieh@apple.com600d5b62020-06-05 19:59:00 +0000332}
333
wenson_hsieh@apple.comf2cc5ed12020-06-25 20:37:34 +0000334bool TextManipulationController::shouldExcludeNodeBasedOnStyle(const Node& node)
335{
336 auto* style = node.renderStyle();
337 if (!style)
338 return false;
339
340 auto& font = style->fontCascade().primaryFont();
341 auto familyName = font.platformData().familyName();
342 if (familyName.isEmpty())
343 return false;
344
345 auto iter = m_cachedFontFamilyExclusionResults.find(familyName);
346 if (iter != m_cachedFontFamilyExclusionResults.end())
347 return iter->value;
348
349 // FIXME: We should reconsider whether a node should be excluded if the primary font
350 // used to render the node changes, since this "icon font" heuristic may return a
351 // different result.
352 bool result = font.isProbablyOnlyUsedToRenderIcons();
353 m_cachedFontFamilyExclusionResults.set(familyName, result);
354 return result;
355}
356
sihui_liu@apple.com0e39ec02020-06-09 06:54:20 +0000357void TextManipulationController::parse(ManipulationUnit& unit, const String& text, Node& textNode)
wenson_hsieh@apple.com600d5b62020-06-05 19:59:00 +0000358{
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000359 ExclusionRuleMatcher exclusionRuleMatcher(m_exclusionRules);
wenson_hsieh@apple.comf2cc5ed12020-06-25 20:37:34 +0000360 bool isNodeExcluded = exclusionRuleMatcher.isExcluded(&textNode) || shouldExcludeNodeBasedOnStyle(textNode);
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000361 size_t positionOfLastNonHTMLSpace = WTF::notFound;
362 size_t startPositionOfCurrentToken = 0;
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000363 size_t index = 0;
364 for (; index < text.length(); ++index) {
365 auto character = text[index];
wenson_hsieh@apple.com600d5b62020-06-05 19:59:00 +0000366 if (isTokenDelimiter(character)) {
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000367 if (positionOfLastNonHTMLSpace != WTF::notFound && startPositionOfCurrentToken <= positionOfLastNonHTMLSpace) {
sihui_liu@apple.com0e39ec02020-06-09 06:54:20 +0000368 auto stringForToken = text.substring(startPositionOfCurrentToken, positionOfLastNonHTMLSpace + 1 - startPositionOfCurrentToken);
369 unit.tokens.append(ManipulationToken { m_tokenIdentifier.generate(), stringForToken, tokenInfo(&textNode), isNodeExcluded });
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000370 startPositionOfCurrentToken = positionOfLastNonHTMLSpace + 1;
371 }
372
wenson_hsieh@apple.com600d5b62020-06-05 19:59:00 +0000373 while (index < text.length() && (isHTMLSpace(text[index]) || isInPrivateUseArea(text[index])))
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000374 ++index;
375
376 --index;
377
sihui_liu@apple.com0e39ec02020-06-09 06:54:20 +0000378 auto stringForToken = text.substring(startPositionOfCurrentToken, index + 1 - startPositionOfCurrentToken);
379 if (unit.tokens.isEmpty() && !unit.firstTokenContainsDelimiter)
380 unit.firstTokenContainsDelimiter = true;
381 unit.tokens.append(ManipulationToken { m_tokenIdentifier.generate(), stringForToken, tokenInfo(&textNode), true });
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000382 startPositionOfCurrentToken = index + 1;
sihui_liu@apple.com0e39ec02020-06-09 06:54:20 +0000383 unit.lastTokenContainsDelimiter = true;
wenson_hsieh@apple.com600d5b62020-06-05 19:59:00 +0000384 } else if (isNotHTMLSpace(character)) {
sihui_liu@apple.com0e39ec02020-06-09 06:54:20 +0000385 if (!isNodeExcluded)
386 unit.areAllTokensExcluded = false;
wenson_hsieh@apple.com600d5b62020-06-05 19:59:00 +0000387 positionOfLastNonHTMLSpace = index;
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000388 }
389 }
390
391 if (startPositionOfCurrentToken < text.length()) {
sihui_liu@apple.com0e39ec02020-06-09 06:54:20 +0000392 auto stringForToken = text.substring(startPositionOfCurrentToken, index + 1 - startPositionOfCurrentToken);
393 unit.tokens.append(ManipulationToken { m_tokenIdentifier.generate(), stringForToken, tokenInfo(&textNode), isNodeExcluded });
394 unit.lastTokenContainsDelimiter = false;
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000395 }
sihui_liu@apple.com33d45222020-06-05 06:13:30 +0000396}
397
398void TextManipulationController::addItemIfPossible(Vector<ManipulationUnit>&& units)
399{
400 if (units.isEmpty())
401 return;
402
403 size_t index = 0;
404 size_t end = units.size();
405 while (index < units.size() && units[index].areAllTokensExcluded)
406 ++index;
407
408 while (end > 0 && units[end - 1].areAllTokensExcluded)
409 --end;
410
411 if (index == end)
412 return;
413
sihui_liu@apple.coma6421802020-06-09 07:11:12 +0000414 ASSERT(end);
415 auto startPosition = firstPositionInOrBeforeNode(units[index].node.ptr());
416 auto endPosition = positionAfterNode(units[end - 1].node.ptr());
sihui_liu@apple.com33d45222020-06-05 06:13:30 +0000417 Vector<ManipulationToken> tokens;
418 for (; index < end; ++index)
419 tokens.appendVector(WTFMove(units[index].tokens));
420
421 addItem(ManipulationItemData { startPosition, endPosition, nullptr, nullQName(), WTFMove(tokens) });
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000422}
423
rniwa@webkit.org640b0732020-03-08 03:52:06 +0000424void TextManipulationController::observeParagraphs(const Position& start, const Position& end)
425{
wenson_hsieh@apple.comc626f5c2020-04-08 23:59:31 +0000426 if (start.isNull() || end.isNull())
427 return;
428
rniwa@webkit.org640b0732020-03-08 03:52:06 +0000429 auto document = makeRefPtr(start.document());
rniwa@webkit.orgc46d21d2019-12-16 22:36:55 +0000430 ASSERT(document);
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000431 // TextIterator's constructor may have updated the layout and executed arbitrary scripts.
rniwa@webkit.org640b0732020-03-08 03:52:06 +0000432 if (document != start.document() || document != end.document())
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000433 return;
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000434
sihui_liu@apple.com33d45222020-06-05 06:13:30 +0000435 Vector<ManipulationUnit> unitsInCurrentParagraph;
wenson_hsieh@apple.com5f606e42020-06-16 18:11:23 +0000436 Vector<Ref<Element>> enclosingItemBoundaryElements;
sihui_liu@apple.com33d45222020-06-05 06:13:30 +0000437 ParagraphContentIterator iterator { start, end };
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000438 for (; !iterator.atEnd(); iterator.advance()) {
439 auto content = iterator.currentContent();
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000440 auto* contentNode = content.node.get();
441 ASSERT(contentNode);
wenson_hsieh@apple.comf9a72332020-04-24 23:18:05 +0000442
wenson_hsieh@apple.com5f606e42020-06-16 18:11:23 +0000443 while (!enclosingItemBoundaryElements.isEmpty() && !enclosingItemBoundaryElements.last()->contains(contentNode)) {
sihui_liu@apple.com33d45222020-06-05 06:13:30 +0000444 addItemIfPossible(std::exchange(unitsInCurrentParagraph, { }));
wenson_hsieh@apple.com5f606e42020-06-16 18:11:23 +0000445 enclosingItemBoundaryElements.removeLast();
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000446 }
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000447
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000448 if (RefPtr<Element> currentElementAncestor = is<Element>(*contentNode) ? downcast<Element>(contentNode) : contentNode->parentOrShadowHostElement()) {
sihui_liu@apple.com2016ded2020-06-15 17:51:38 +0000449 if (m_manipulatedElements.contains(*currentElementAncestor))
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000450 return;
451 }
452
453 if (is<Element>(*contentNode)) {
454 auto& currentElement = downcast<Element>(*contentNode);
455 if (!content.isTextContent && canPerformTextManipulationByReplacingEntireTextContent(currentElement))
456 addItem(ManipulationItemData { Position(), Position(), makeWeakPtr(currentElement), nullQName(), { ManipulationToken { m_tokenIdentifier.generate(), currentElement.textContent(), tokenInfo(&currentElement) } } });
457
458 if (currentElement.hasAttributes()) {
459 for (auto& attribute : currentElement.attributesIterator()) {
wenson_hsieh@apple.comf0ad7db2020-06-03 21:08:21 +0000460 if (isAttributeForTextManipulation(attribute.name()))
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000461 addItem(ManipulationItemData { Position(), Position(), makeWeakPtr(currentElement), attribute.name(), { ManipulationToken { m_tokenIdentifier.generate(), attribute.value(), tokenInfo(&currentElement) } } });
rniwa@webkit.org640b0732020-03-08 03:52:06 +0000462 }
rniwa@webkit.org640b0732020-03-08 03:52:06 +0000463 }
wenson_hsieh@apple.comf0ad7db2020-06-03 21:08:21 +0000464
465 if (is<HTMLInputElement>(currentElement)) {
466 auto& input = downcast<HTMLInputElement>(currentElement);
467 if (shouldExtractValueForTextManipulation(input))
aperez@igalia.comcb5f46712020-06-05 15:47:41 +0000468 addItem(ManipulationItemData { { }, { }, makeWeakPtr(currentElement), HTMLNames::valueAttr, { ManipulationToken { m_tokenIdentifier.generate(), input.value(), tokenInfo(&currentElement) } } });
wenson_hsieh@apple.comf0ad7db2020-06-03 21:08:21 +0000469 }
470
wenson_hsieh@apple.com5f606e42020-06-16 18:11:23 +0000471 if (isEnclosingItemBoundaryElement(currentElement)) {
472 addItemIfPossible(std::exchange(unitsInCurrentParagraph, { }));
473 enclosingItemBoundaryElements.append(currentElement);
474 }
rniwa@webkit.org95386982020-02-28 03:37:22 +0000475 }
476
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000477 if (content.isReplacedContent) {
sihui_liu@apple.com33d45222020-06-05 06:13:30 +0000478 if (!unitsInCurrentParagraph.isEmpty())
sihui_liu@apple.com0e39ec02020-06-09 06:54:20 +0000479 unitsInCurrentParagraph.append(ManipulationUnit { *contentNode, { ManipulationToken { m_tokenIdentifier.generate(), "[]", tokenInfo(content.node.get()), true } } });
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000480 continue;
481 }
482
483 if (!content.isTextContent)
484 continue;
485
sihui_liu@apple.com0e39ec02020-06-09 06:54:20 +0000486 auto currentUnit = createUnit(content.text, *contentNode);
487 if (currentUnit.firstTokenContainsDelimiter)
sihui_liu@apple.com33d45222020-06-05 06:13:30 +0000488 addItemIfPossible(std::exchange(unitsInCurrentParagraph, { }));
sihui_liu@apple.com5a2c1d32020-04-23 17:23:54 +0000489
sihui_liu@apple.com0e39ec02020-06-09 06:54:20 +0000490 if (unitsInCurrentParagraph.isEmpty() && currentUnit.areAllTokensExcluded)
491 continue;
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000492
sihui_liu@apple.com0e39ec02020-06-09 06:54:20 +0000493 bool currentUnitEndsWithDelimiter = currentUnit.lastTokenContainsDelimiter;
494 unitsInCurrentParagraph.append(WTFMove(currentUnit));
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000495
sihui_liu@apple.com0e39ec02020-06-09 06:54:20 +0000496 if (currentUnitEndsWithDelimiter)
sihui_liu@apple.com33d45222020-06-05 06:13:30 +0000497 addItemIfPossible(std::exchange(unitsInCurrentParagraph, { }));
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000498 }
499
sihui_liu@apple.com33d45222020-06-05 06:13:30 +0000500 addItemIfPossible(std::exchange(unitsInCurrentParagraph, { }));
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000501}
502
rniwa@webkit.orgc46d21d2019-12-16 22:36:55 +0000503void TextManipulationController::didCreateRendererForElement(Element& element)
504{
sihui_liu@apple.com2016ded2020-06-15 17:51:38 +0000505 if (m_manipulatedElements.contains(element))
wenson_hsieh@apple.com54632b72020-01-10 00:40:14 +0000506 return;
507
rniwa@webkit.org95386982020-02-28 03:37:22 +0000508 if (m_elementsWithNewRenderer.computesEmpty())
wenson_hsieh@apple.comd0ed2bd2020-04-24 00:07:25 +0000509 scheduleObservationUpdate();
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000510
511 if (is<PseudoElement>(element)) {
512 if (auto* host = downcast<PseudoElement>(element).hostElement())
rniwa@webkit.org95386982020-02-28 03:37:22 +0000513 m_elementsWithNewRenderer.add(*host);
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000514 } else
rniwa@webkit.org95386982020-02-28 03:37:22 +0000515 m_elementsWithNewRenderer.add(element);
rniwa@webkit.orgc46d21d2019-12-16 22:36:55 +0000516}
517
518using PositionTuple = std::tuple<RefPtr<Node>, unsigned, unsigned>;
519static const PositionTuple makePositionTuple(const Position& position)
520{
aperez@igalia.comcb5f46712020-06-05 15:47:41 +0000521 return { position.anchorNode(), static_cast<unsigned>(position.anchorType()), position.anchorType() == Position::PositionIsOffsetInAnchor ? position.offsetInContainerNode() : 0 };
rniwa@webkit.orgc46d21d2019-12-16 22:36:55 +0000522}
523
sihui_liu@apple.com8f347602020-06-11 00:28:06 +0000524static const std::pair<PositionTuple, PositionTuple> makeHashablePositionRange(const Position& start, const Position& end)
rniwa@webkit.orgc46d21d2019-12-16 22:36:55 +0000525{
sihui_liu@apple.com8f347602020-06-11 00:28:06 +0000526 return { makePositionTuple(start), makePositionTuple(end) };
rniwa@webkit.orgc46d21d2019-12-16 22:36:55 +0000527}
528
wenson_hsieh@apple.comd0ed2bd2020-04-24 00:07:25 +0000529void TextManipulationController::scheduleObservationUpdate()
rniwa@webkit.orgc46d21d2019-12-16 22:36:55 +0000530{
531 if (!m_document)
532 return;
533
534 m_document->eventLoop().queueTask(TaskSource::InternalAsyncTask, [weakThis = makeWeakPtr(*this)] {
535 auto* controller = weakThis.get();
536 if (!controller)
537 return;
538
539 HashSet<Ref<Element>> mutatedElements;
rniwa@webkit.org95386982020-02-28 03:37:22 +0000540 for (auto& weakElement : controller->m_elementsWithNewRenderer)
rniwa@webkit.orgc46d21d2019-12-16 22:36:55 +0000541 mutatedElements.add(weakElement);
rniwa@webkit.org95386982020-02-28 03:37:22 +0000542 controller->m_elementsWithNewRenderer.clear();
rniwa@webkit.orgc46d21d2019-12-16 22:36:55 +0000543
544 HashSet<Ref<Element>> filteredElements;
545 for (auto& element : mutatedElements) {
546 auto* parentElement = element->parentElement();
547 if (!parentElement || !mutatedElements.contains(parentElement))
548 filteredElements.add(element.copyRef());
549 }
550 mutatedElements.clear();
551
552 HashSet<std::pair<PositionTuple, PositionTuple>> paragraphSets;
553 for (auto& element : filteredElements) {
sihui_liu@apple.com8f347602020-06-11 00:28:06 +0000554 auto start = firstPositionInOrBeforeNode(element.ptr());
555 auto end = lastPositionInOrAfterNode(element.ptr());
rniwa@webkit.orgc46d21d2019-12-16 22:36:55 +0000556
wenson_hsieh@apple.com6b6c8fb2020-04-09 20:16:38 +0000557 if (start.isNull() || end.isNull())
558 continue;
559
rniwa@webkit.orgc46d21d2019-12-16 22:36:55 +0000560 auto key = makeHashablePositionRange(start, end);
561 if (!paragraphSets.add(key).isNewEntry)
562 continue;
563
564 auto* controller = weakThis.get();
565 if (!controller)
sihui_liu@apple.com8f347602020-06-11 00:28:06 +0000566 return;
rniwa@webkit.orgc46d21d2019-12-16 22:36:55 +0000567
sihui_liu@apple.com8f347602020-06-11 00:28:06 +0000568 controller->observeParagraphs(start, end);
rniwa@webkit.orgc46d21d2019-12-16 22:36:55 +0000569 }
rniwa@webkit.orge44b91352020-03-04 05:22:14 +0000570 controller->flushPendingItemsForCallback();
rniwa@webkit.orgc46d21d2019-12-16 22:36:55 +0000571 });
572}
573
rniwa@webkit.org640b0732020-03-08 03:52:06 +0000574void TextManipulationController::addItem(ManipulationItemData&& itemData)
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000575{
rniwa@webkit.orge44b91352020-03-04 05:22:14 +0000576 const unsigned itemCallbackBatchingSize = 128;
577
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000578 ASSERT(m_document);
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000579 ASSERT(!itemData.tokens.isEmpty());
rniwa@webkit.orge44b91352020-03-04 05:22:14 +0000580 auto newID = m_itemIdentifier.generate();
581 m_pendingItemsForCallback.append(ManipulationItem {
582 newID,
rniwa@webkit.org640b0732020-03-08 03:52:06 +0000583 itemData.tokens.map([](auto& token) { return token; })
rniwa@webkit.orge44b91352020-03-04 05:22:14 +0000584 });
rniwa@webkit.org640b0732020-03-08 03:52:06 +0000585 m_items.add(newID, WTFMove(itemData));
rniwa@webkit.orge44b91352020-03-04 05:22:14 +0000586
587 if (m_pendingItemsForCallback.size() >= itemCallbackBatchingSize)
588 flushPendingItemsForCallback();
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000589}
590
rniwa@webkit.orge44b91352020-03-04 05:22:14 +0000591void TextManipulationController::flushPendingItemsForCallback()
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000592{
rniwa@webkit.orge44b91352020-03-04 05:22:14 +0000593 m_callback(*m_document, m_pendingItemsForCallback);
594 m_pendingItemsForCallback.clear();
595}
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000596
rniwa@webkit.orge44b91352020-03-04 05:22:14 +0000597auto TextManipulationController::completeManipulation(const Vector<WebCore::TextManipulationController::ManipulationItem>& completionItems) -> Vector<ManipulationFailure>
598{
599 Vector<ManipulationFailure> failures;
600 for (unsigned i = 0; i < completionItems.size(); ++i) {
601 auto& itemToComplete = completionItems[i];
602 auto identifier = itemToComplete.identifier;
603 if (!identifier) {
604 failures.append(ManipulationFailure { identifier, i, ManipulationFailureType::InvalidItem });
605 continue;
606 }
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000607
rniwa@webkit.orge44b91352020-03-04 05:22:14 +0000608 auto itemDataIterator = m_items.find(identifier);
609 if (itemDataIterator == m_items.end()) {
610 failures.append(ManipulationFailure { identifier, i, ManipulationFailureType::InvalidItem });
611 continue;
612 }
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000613
rniwa@webkit.orge44b91352020-03-04 05:22:14 +0000614 ManipulationItemData itemData;
615 std::exchange(itemData, itemDataIterator->value);
616 m_items.remove(itemDataIterator);
617
618 auto failureOrNullopt = replace(itemData, itemToComplete.tokens);
619 if (failureOrNullopt)
620 failures.append(ManipulationFailure { identifier, i, *failureOrNullopt });
621 }
622 return failures;
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000623}
624
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000625struct TokenExchangeData {
626 RefPtr<Node> node;
627 String originalContent;
628 bool isExcluded { false };
629 bool isConsumed { false };
630};
631
632struct ReplacementData {
633 Ref<Node> originalNode;
rniwa@webkit.org7afdb0b2019-10-25 20:41:15 +0000634 String newData;
635};
636
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000637Vector<Ref<Node>> TextManipulationController::getPath(Node* ancestor, Node* node)
638{
639 Vector<Ref<Node>> path;
640 RefPtr<ContainerNode> containerNode = is<ContainerNode>(*node) ? &downcast<ContainerNode>(*node) : node->parentNode();
641 for (; containerNode && containerNode != ancestor; containerNode = containerNode->parentNode())
642 path.append(*containerNode);
643 path.reverse();
644 return path;
645}
646
sihui_liu@apple.com2016ded2020-06-15 17:51:38 +0000647void TextManipulationController::updateInsertions(Vector<NodeEntry>& lastTopDownPath, const Vector<Ref<Node>>& currentTopDownPath, Node* currentNode, HashSet<Ref<Node>>& insertedNodes, Vector<NodeInsertion>& insertions, IsNodeManipulated isNodeManipulated)
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000648{
wenson_hsieh@apple.come0dde552020-06-17 01:34:01 +0000649 size_t i = 0;
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000650 while (i < lastTopDownPath.size() && i < currentTopDownPath.size() && lastTopDownPath[i].first.ptr() == currentTopDownPath[i].ptr())
651 ++i;
652
653 if (i != lastTopDownPath.size() || i != currentTopDownPath.size()) {
654 if (i < lastTopDownPath.size())
655 lastTopDownPath.shrink(i);
656
657 for (;i < currentTopDownPath.size(); ++i) {
658 Ref<Node> node = currentTopDownPath[i];
659 if (!insertedNodes.add(node.copyRef()).isNewEntry) {
660 auto clonedNode = node->cloneNodeInternal(node->document(), Node::CloningOperation::OnlySelf);
661 if (auto* data = node->eventTargetData())
662 data->eventListenerMap.copyEventListenersNotCreatedFromMarkupToTarget(clonedNode.ptr());
663 node = WTFMove(clonedNode);
664 }
665 insertions.append(NodeInsertion { lastTopDownPath.size() ? lastTopDownPath.last().second.ptr() : nullptr, node.copyRef() });
666 lastTopDownPath.append({ currentTopDownPath[i].copyRef(), WTFMove(node) });
667 }
668 }
669
670 if (currentNode)
sihui_liu@apple.com2016ded2020-06-15 17:51:38 +0000671 insertions.append(NodeInsertion { lastTopDownPath.size() ? lastTopDownPath.last().second.ptr() : nullptr, *currentNode, isNodeManipulated });
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000672}
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000673
rniwa@webkit.orge44b91352020-03-04 05:22:14 +0000674auto TextManipulationController::replace(const ManipulationItemData& item, const Vector<ManipulationToken>& replacementTokens) -> Optional<ManipulationFailureType>
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000675{
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000676 if (item.start.isOrphan() || item.end.isOrphan())
rniwa@webkit.orge44b91352020-03-04 05:22:14 +0000677 return ManipulationFailureType::ContentChanged;
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000678
darin@apple.comd2f1a002020-03-16 23:02:10 +0000679 if (item.start.isNull() || item.end.isNull()) {
rniwa@webkit.org640b0732020-03-08 03:52:06 +0000680 RELEASE_ASSERT(item.tokens.size() == 1);
681 auto element = makeRefPtr(item.element.get());
682 if (!element)
683 return ManipulationFailureType::ContentChanged;
sihui_liu@apple.com33fbdee2020-06-03 19:04:28 +0000684 if (replacementTokens.size() > 1 && !canPerformTextManipulationByReplacingEntireTextContent(*element) && item.attributeName == nullQName())
rniwa@webkit.org640b0732020-03-08 03:52:06 +0000685 return ManipulationFailureType::InvalidToken;
wenson_hsieh@apple.com2d16b752020-04-20 20:19:54 +0000686 auto expectedTokenIdentifier = item.tokens[0].identifier;
687 StringBuilder newValue;
688 for (size_t i = 0; i < replacementTokens.size(); ++i) {
689 if (replacementTokens[i].identifier != expectedTokenIdentifier)
rniwa@webkit.org640b0732020-03-08 03:52:06 +0000690 return ManipulationFailureType::InvalidToken;
wenson_hsieh@apple.com2d16b752020-04-20 20:19:54 +0000691 if (i)
692 newValue.append(' ');
693 newValue.append(replacementTokens[i].content);
rniwa@webkit.org640b0732020-03-08 03:52:06 +0000694 }
695 if (item.attributeName == nullQName())
wenson_hsieh@apple.com2d16b752020-04-20 20:19:54 +0000696 element->setTextContent(newValue.toString());
aperez@igalia.comcb5f46712020-06-05 15:47:41 +0000697 else if (item.attributeName == HTMLNames::valueAttr && is<HTMLInputElement>(*element))
wenson_hsieh@apple.comf0ad7db2020-06-03 21:08:21 +0000698 downcast<HTMLInputElement>(*element).setValue(newValue.toString());
rniwa@webkit.org640b0732020-03-08 03:52:06 +0000699 else
wenson_hsieh@apple.com2d16b752020-04-20 20:19:54 +0000700 element->setAttribute(item.attributeName, newValue.toString());
rniwa@webkit.org640b0732020-03-08 03:52:06 +0000701 return WTF::nullopt;
702 }
703
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000704 size_t currentTokenIndex = 0;
705 HashMap<TokenIdentifier, TokenExchangeData> tokenExchangeMap;
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000706 RefPtr<Node> commonAncestor;
rniwa@webkit.orgbf66d0a2020-03-12 23:29:31 +0000707 RefPtr<Node> firstContentNode;
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000708 RefPtr<Node> lastChildOfCommonAncestorInRange;
rniwa@webkit.orgbf66d0a2020-03-12 23:29:31 +0000709 HashSet<Ref<Node>> nodesToRemove;
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000710 ParagraphContentIterator iterator { item.start, item.end };
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000711 for (; !iterator.atEnd(); iterator.advance()) {
712 auto content = iterator.currentContent();
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000713 ASSERT(content.node);
714
715 lastChildOfCommonAncestorInRange = content.node;
716 nodesToRemove.add(*content.node);
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000717
718 if (!content.isReplacedContent && !content.isTextContent)
719 continue;
720
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000721 Vector<ManipulationToken> tokensInCurrentNode;
sihui_liu@apple.com5f91c3b2020-06-04 23:58:32 +0000722 if (content.isReplacedContent) {
723 if (currentTokenIndex >= item.tokens.size())
724 return ManipulationFailureType::ContentChanged;
725
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000726 tokensInCurrentNode.append(item.tokens[currentTokenIndex]);
sihui_liu@apple.com5f91c3b2020-06-04 23:58:32 +0000727 } else
sihui_liu@apple.com0e39ec02020-06-09 06:54:20 +0000728 tokensInCurrentNode = createUnit(content.text, *content.node).tokens;
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000729
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000730 bool isNodeIncluded = WTF::anyOf(tokensInCurrentNode, [] (auto& token) {
731 return !token.isExcluded;
732 });
733 for (auto& token : tokensInCurrentNode) {
sihui_liu@apple.com5f91c3b2020-06-04 23:58:32 +0000734 if (currentTokenIndex >= item.tokens.size())
sihui_liu@apple.com5a2c1d32020-04-23 17:23:54 +0000735 return ManipulationFailureType::ContentChanged;
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000736
737 auto& currentToken = item.tokens[currentTokenIndex++];
738 if (!content.isReplacedContent && currentToken.content != token.content)
739 return ManipulationFailureType::ContentChanged;
740
741 tokenExchangeMap.set(currentToken.identifier, TokenExchangeData { content.node.copyRef(), currentToken.content, !isNodeIncluded });
sihui_liu@apple.com5a2c1d32020-04-23 17:23:54 +0000742 }
743
rniwa@webkit.orgbf66d0a2020-03-12 23:29:31 +0000744 if (!firstContentNode)
745 firstContentNode = content.node;
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000746
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000747 auto parentNode = content.node->parentNode();
748 if (!commonAncestor)
749 commonAncestor = parentNode;
750 else if (!parentNode->isDescendantOf(commonAncestor.get())) {
751 commonAncestor = commonInclusiveAncestor(*commonAncestor, *parentNode);
752 ASSERT(commonAncestor);
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000753 }
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000754 }
755
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000756 while (lastChildOfCommonAncestorInRange && lastChildOfCommonAncestorInRange->parentNode() != commonAncestor)
757 lastChildOfCommonAncestorInRange = lastChildOfCommonAncestorInRange->parentNode();
758
rniwa@webkit.orgbf66d0a2020-03-12 23:29:31 +0000759 for (auto node = commonAncestor; node; node = node->parentNode())
760 nodesToRemove.remove(*node);
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000761
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000762 HashSet<Ref<Node>> reusedOriginalNodes;
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000763 Vector<NodeEntry> lastTopDownPath;
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000764 Vector<NodeInsertion> insertions;
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000765 for (size_t index = 0; index < replacementTokens.size(); ++index) {
766 auto& replacementToken = replacementTokens[index];
767 auto it = tokenExchangeMap.find(replacementToken.identifier);
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000768 if (it == tokenExchangeMap.end())
rniwa@webkit.orge44b91352020-03-04 05:22:14 +0000769 return ManipulationFailureType::InvalidToken;
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000770
771 auto& exchangeData = it->value;
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000772 auto* originalNode = exchangeData.node.get();
773 ASSERT(originalNode);
774 auto replacementText = replacementToken.content;
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000775
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000776 RefPtr<Node> replacementNode;
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000777 if (exchangeData.isExcluded) {
778 if (exchangeData.isConsumed)
rniwa@webkit.orge44b91352020-03-04 05:22:14 +0000779 return ManipulationFailureType::ExclusionViolation;
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000780 exchangeData.isConsumed = true;
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000781
782 if (!replacementToken.content.isNull() && replacementToken.content != exchangeData.originalContent)
rniwa@webkit.orge44b91352020-03-04 05:22:14 +0000783 return ManipulationFailureType::ExclusionViolation;
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000784
785 replacementNode = originalNode;
786 for (RefPtr<Node> descendentNode = NodeTraversal::next(*originalNode, originalNode); descendentNode; descendentNode = NodeTraversal::next(*descendentNode, originalNode))
787 nodesToRemove.remove(*descendentNode);
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000788 } else
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000789 replacementNode = Text::create(commonAncestor->document(), replacementText);
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000790
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000791 auto topDownPath = getPath(commonAncestor.get(), originalNode);
792 updateInsertions(lastTopDownPath, topDownPath, replacementNode.get(), reusedOriginalNodes, insertions);
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000793 }
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000794
795 auto end = lastChildOfCommonAncestorInRange ? positionInParentAfterNode(lastChildOfCommonAncestorInRange.get()) : positionAfterNode(commonAncestor.get());
796 RefPtr<Node> node = item.end.firstNode();
797 RefPtr<Node> endNode = end.firstNode();
798 if (node && node != endNode) {
799 auto topDownPath = getPath(commonAncestor.get(), node->parentNode());
sihui_liu@apple.com2016ded2020-06-15 17:51:38 +0000800 updateInsertions(lastTopDownPath, topDownPath, nullptr, reusedOriginalNodes, insertions, IsNodeManipulated::No);
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000801 }
802 while (node != endNode) {
803 Ref<Node> parentNode = *node->parentNode();
804 while (!lastTopDownPath.isEmpty() && lastTopDownPath.last().first.ptr() != parentNode.ptr())
805 lastTopDownPath.removeLast();
806
sihui_liu@apple.com2016ded2020-06-15 17:51:38 +0000807 insertions.append(NodeInsertion { lastTopDownPath.size() ? lastTopDownPath.last().second.ptr() : nullptr, *node, IsNodeManipulated::No });
sihui_liu@apple.com12ab7a72020-06-01 23:54:34 +0000808 lastTopDownPath.append({ *node, *node });
809 node = NodeTraversal::next(*node);
810 }
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000811
rniwa@webkit.orgbf66d0a2020-03-12 23:29:31 +0000812 Position insertionPoint = positionBeforeNode(firstContentNode.get()).parentAnchoredEquivalent();
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000813 while (insertionPoint.containerNode() != commonAncestor)
814 insertionPoint = positionInParentBeforeNode(insertionPoint.containerNode());
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000815
816 for (auto& node : nodesToRemove)
817 node->remove();
818
wenson_hsieh@apple.come0dde552020-06-17 01:34:01 +0000819 HashSet<Ref<Node>> parentNodesWithOnlyManipulatedChildren;
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000820 for (auto& insertion : insertions) {
wenson_hsieh@apple.come0dde552020-06-17 01:34:01 +0000821 RefPtr<Node> parent;
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000822 if (!insertion.parentIfDifferentFromCommonAncestor) {
wenson_hsieh@apple.come0dde552020-06-17 01:34:01 +0000823 parent = insertionPoint.containerNode();
824 parent->insertBefore(insertion.child, insertionPoint.computeNodeAfterPosition());
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000825 insertionPoint = positionInParentAfterNode(insertion.child.ptr());
wenson_hsieh@apple.come0dde552020-06-17 01:34:01 +0000826 } else {
827 parent = insertion.parentIfDifferentFromCommonAncestor;
828 parent->appendChild(insertion.child);
829 }
830
831 if (insertion.isChildManipulated == IsNodeManipulated::No) {
832 parentNodesWithOnlyManipulatedChildren.remove(*parent);
833 continue;
834 }
835
836 if (is<Element>(insertion.child.get()))
rniwa@webkit.org95386982020-02-28 03:37:22 +0000837 m_manipulatedElements.add(downcast<Element>(insertion.child.get()));
wenson_hsieh@apple.come0dde552020-06-17 01:34:01 +0000838 else if (parent->firstChild() == parent->lastChild())
839 parentNodesWithOnlyManipulatedChildren.add(*parent);
840 }
841
842 for (auto& node : parentNodesWithOnlyManipulatedChildren) {
843 if (is<Element>(node.get()))
844 m_manipulatedElements.add(downcast<Element>(node.get()));
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000845 }
rniwa@webkit.org7afdb0b2019-10-25 20:41:15 +0000846
rniwa@webkit.orge44b91352020-03-04 05:22:14 +0000847 return WTF::nullopt;
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000848}
849
850} // namespace WebCore