blob: 09d0ecd55aca1077252e12cfa5a7ebbe34d41028 [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
rniwa@webkit.org7afdb0b2019-10-25 20:41:15 +000029#include "CharacterData.h"
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +000030#include "Editing.h"
rniwa@webkit.org7afdb0b2019-10-25 20:41:15 +000031#include "ElementAncestorIterator.h"
rniwa@webkit.orgc46d21d2019-12-16 22:36:55 +000032#include "EventLoop.h"
rniwa@webkit.org900f5602019-12-21 02:09:50 +000033#include "NodeTraversal.h"
34#include "PseudoElement.h"
35#include "Range.h"
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +000036#include "ScriptDisallowedScope.h"
rniwa@webkit.org900f5602019-12-21 02:09:50 +000037#include "Text.h"
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +000038#include "TextIterator.h"
39#include "VisibleUnits.h"
40
41namespace WebCore {
42
rniwa@webkit.org7afdb0b2019-10-25 20:41:15 +000043inline bool TextManipulationController::ExclusionRule::match(const Element& element) const
44{
45 return switchOn(rule, [&element] (ElementRule rule) {
46 return rule.localName == element.localName();
47 }, [&element] (AttributeRule rule) {
48 return equalIgnoringASCIICase(element.getAttribute(rule.name), rule.value);
commit-queue@webkit.orgd8730752019-12-04 18:36:15 +000049 }, [&element] (ClassRule rule) {
50 return element.hasClass() && element.classNames().contains(rule.className);
rniwa@webkit.org7afdb0b2019-10-25 20:41:15 +000051 });
52}
53
54class ExclusionRuleMatcher {
55public:
56 using ExclusionRule = TextManipulationController::ExclusionRule;
57 using Type = TextManipulationController::ExclusionRule::Type;
58
59 ExclusionRuleMatcher(const Vector<ExclusionRule>& rules)
60 : m_rules(rules)
61 { }
62
63 bool isExcluded(Node* node)
64 {
65 if (!node)
66 return false;
67
68 RefPtr<Element> startingElement = is<Element>(*node) ? downcast<Element>(node) : node->parentElement();
69 if (!startingElement)
70 return false;
71
72 Type type = Type::Include;
73 RefPtr<Element> matchingElement;
darin@apple.com25c0c842020-02-23 04:56:03 +000074 for (auto& element : lineageOfType<Element>(*startingElement)) {
rniwa@webkit.org7afdb0b2019-10-25 20:41:15 +000075 if (auto typeOrNullopt = typeForElement(element)) {
76 type = *typeOrNullopt;
77 matchingElement = &element;
78 break;
79 }
80 }
81
darin@apple.com25c0c842020-02-23 04:56:03 +000082 for (auto& element : lineageOfType<Element>(*startingElement)) {
rniwa@webkit.org7afdb0b2019-10-25 20:41:15 +000083 m_cache.set(element, type);
84 if (&element == matchingElement)
85 break;
86 }
87
88 return type == Type::Exclude;
89 }
90
91 Optional<Type> typeForElement(Element& element)
92 {
93 auto it = m_cache.find(element);
94 if (it != m_cache.end())
95 return it->value;
96
97 for (auto& rule : m_rules) {
98 if (rule.match(element))
99 return rule.type;
100 }
101
102 return WTF::nullopt;
103 }
104
105private:
106 const Vector<ExclusionRule>& m_rules;
107 HashMap<Ref<Element>, ExclusionRule::Type> m_cache;
108};
109
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000110TextManipulationController::TextManipulationController(Document& document)
111 : m_document(makeWeakPtr(document))
112{
113}
114
rniwa@webkit.org95386982020-02-28 03:37:22 +0000115bool TextManipulationController::isInManipulatedElement(Element& element)
116{
117 if (!m_manipulatedElements.capacity())
118 return false; // Fast path for startObservingParagraphs.
119 for (auto& ancestorOrSelf : lineageOfType<Element>(element)) {
120 if (m_manipulatedElements.contains(ancestorOrSelf))
121 return true;
122 }
123 return false;
124}
125
rniwa@webkit.org7afdb0b2019-10-25 20:41:15 +0000126void TextManipulationController::startObservingParagraphs(ManipulationItemCallback&& callback, Vector<ExclusionRule>&& exclusionRules)
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000127{
128 auto document = makeRefPtr(m_document.get());
129 if (!document)
130 return;
131
132 m_callback = WTFMove(callback);
rniwa@webkit.org7afdb0b2019-10-25 20:41:15 +0000133 m_exclusionRules = WTFMove(exclusionRules);
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000134
135 VisiblePosition start = firstPositionInNode(m_document.get());
136 VisiblePosition end = lastPositionInNode(m_document.get());
rniwa@webkit.orgc46d21d2019-12-16 22:36:55 +0000137
138 observeParagraphs(start, end);
rniwa@webkit.orge44b91352020-03-04 05:22:14 +0000139 flushPendingItemsForCallback();
rniwa@webkit.orgc46d21d2019-12-16 22:36:55 +0000140}
141
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000142class ParagraphContentIterator {
143public:
144 ParagraphContentIterator(const Position& start, const Position& end)
145 : m_iterator(start, end)
146 , m_iteratorNode(m_iterator.atEnd() ? nullptr : m_iterator.range()->firstNode())
147 , m_currentNodeForFindingInvisibleContent(start.firstNode())
148 , m_pastEndNode(end.firstNode())
149 {
150 }
151
152 void advance()
153 {
154 // FIXME: Add a node callback to TextIterator instead of traversing the node tree twice like this.
155 if (m_currentNodeForFindingInvisibleContent != m_iteratorNode && m_currentNodeForFindingInvisibleContent != m_pastEndNode) {
156 moveCurrentNodeForward();
157 return;
158 }
159
160 if (m_iterator.atEnd())
161 return;
162
163 auto previousIteratorNode = m_iteratorNode;
164
165 m_iterator.advance();
166 m_iteratorNode = m_iterator.atEnd() ? nullptr : m_iterator.range()->firstNode();
167 if (previousIteratorNode != m_iteratorNode)
168 moveCurrentNodeForward();
169 }
170
171 struct CurrentContent {
172 RefPtr<Node> node;
173 StringView text;
174 bool isTextContent { false };
175 bool isReplacedContent { false };
176 };
177
178 CurrentContent currentContent()
179 {
180 CurrentContent content;
181 if (m_currentNodeForFindingInvisibleContent && m_currentNodeForFindingInvisibleContent != m_iteratorNode)
182 content = { m_currentNodeForFindingInvisibleContent.copyRef(), StringView { } };
183 else
184 content = { m_iterator.node(), m_iterator.text(), true };
185 if (content.node) {
186 if (auto* renderer = content.node->renderer()) {
187 if (renderer->isRenderReplaced()) {
188 content.isTextContent = false;
189 content.isReplacedContent = true;
190 }
191 }
192 }
193 return content;
194 }
195
196 Position startPosition()
197 {
198 return m_iterator.range()->startPosition();
199 }
200
201 Position endPosition()
202 {
203 return m_iterator.range()->endPosition();
204 }
205
206 bool atEnd() const { return m_iterator.atEnd() && m_currentNodeForFindingInvisibleContent == m_pastEndNode; }
207
208private:
209 void moveCurrentNodeForward()
210 {
211 m_currentNodeForFindingInvisibleContent = NodeTraversal::next(*m_currentNodeForFindingInvisibleContent);
212 if (!m_currentNodeForFindingInvisibleContent)
213 m_currentNodeForFindingInvisibleContent = m_pastEndNode;
214 }
215
216 TextIterator m_iterator;
217 RefPtr<Node> m_iteratorNode;
218 RefPtr<Node> m_currentNodeForFindingInvisibleContent;
219 RefPtr<Node> m_pastEndNode;
220};
221
rniwa@webkit.orgc46d21d2019-12-16 22:36:55 +0000222void TextManipulationController::observeParagraphs(VisiblePosition& start, VisiblePosition& end)
223{
224 auto document = makeRefPtr(start.deepEquivalent().document());
225 ASSERT(document);
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000226 ParagraphContentIterator iterator { start.deepEquivalent(), end.deepEquivalent() };
rniwa@webkit.orgc46d21d2019-12-16 22:36:55 +0000227 if (document != start.deepEquivalent().document() || document != end.deepEquivalent().document())
228 return; // TextIterator's constructor may have updated the layout and executed arbitrary scripts.
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000229
rniwa@webkit.org7afdb0b2019-10-25 20:41:15 +0000230 ExclusionRuleMatcher exclusionRuleMatcher(m_exclusionRules);
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000231 Vector<ManipulationToken> tokensInCurrentParagraph;
232 Position startOfCurrentParagraph = start.deepEquivalent();
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000233 for (; !iterator.atEnd(); iterator.advance()) {
234 auto content = iterator.currentContent();
235 if (content.node) {
236 if (RefPtr<Element> currentElementAncestor = is<Element>(*content.node) ? downcast<Element>(content.node.get()) : content.node->parentOrShadowHostElement()) {
rniwa@webkit.org95386982020-02-28 03:37:22 +0000237 if (isInManipulatedElement(*currentElementAncestor))
238 return; // We can exit early here because scheduleObservartionUpdate calls this function on each paragraph separately.
239 }
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000240
241 if (startOfCurrentParagraph.isNull())
242 startOfCurrentParagraph = iterator.startPosition();
rniwa@webkit.org95386982020-02-28 03:37:22 +0000243 }
244
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000245 if (content.isReplacedContent) {
246 tokensInCurrentParagraph.append(ManipulationToken { m_tokenIdentifier.generate(), "[]", true /* isExcluded */});
247 continue;
248 }
249
250 if (!content.isTextContent)
251 continue;
252
253 size_t startOfCurrentLine = 0;
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000254 size_t offsetOfNextNewLine = 0;
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000255 StringView currentText = content.text;
256 while ((offsetOfNextNewLine = currentText.find('\n', startOfCurrentLine)) != notFound) {
257 if (startOfCurrentLine < offsetOfNextNewLine) {
258 auto stringUntilEndOfLine = currentText.substring(startOfCurrentLine, offsetOfNextNewLine - startOfCurrentLine).toString();
259 tokensInCurrentParagraph.append(ManipulationToken { m_tokenIdentifier.generate(), stringUntilEndOfLine, exclusionRuleMatcher.isExcluded(content.node.get()) });
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000260 }
261
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000262 if (!tokensInCurrentParagraph.isEmpty()) {
263 Position endOfCurrentParagraph = iterator.endPosition();
264 if (is<Text>(content.node)) {
265 auto& textNode = downcast<Text>(*content.node);
266 endOfCurrentParagraph = Position(&textNode, offsetOfNextNewLine);
267 startOfCurrentParagraph = Position(&textNode, offsetOfNextNewLine + 1);
268 }
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000269 addItem(startOfCurrentParagraph, endOfCurrentParagraph, WTFMove(tokensInCurrentParagraph));
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000270 startOfCurrentParagraph.clear();
271 }
272 startOfCurrentLine = offsetOfNextNewLine + 1;
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000273 }
274
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000275 auto remainingText = currentText.substring(startOfCurrentLine);
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000276 if (remainingText.length())
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000277 tokensInCurrentParagraph.append(ManipulationToken { m_tokenIdentifier.generate(), remainingText.toString(), exclusionRuleMatcher.isExcluded(content.node.get()) });
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000278 }
279
280 if (!tokensInCurrentParagraph.isEmpty())
281 addItem(startOfCurrentParagraph, end.deepEquivalent(), WTFMove(tokensInCurrentParagraph));
282}
283
rniwa@webkit.orgc46d21d2019-12-16 22:36:55 +0000284void TextManipulationController::didCreateRendererForElement(Element& element)
285{
rniwa@webkit.org95386982020-02-28 03:37:22 +0000286 if (isInManipulatedElement(element))
wenson_hsieh@apple.com54632b72020-01-10 00:40:14 +0000287 return;
288
rniwa@webkit.org95386982020-02-28 03:37:22 +0000289 if (m_elementsWithNewRenderer.computesEmpty())
rniwa@webkit.orgc46d21d2019-12-16 22:36:55 +0000290 scheduleObservartionUpdate();
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000291
292 if (is<PseudoElement>(element)) {
293 if (auto* host = downcast<PseudoElement>(element).hostElement())
rniwa@webkit.org95386982020-02-28 03:37:22 +0000294 m_elementsWithNewRenderer.add(*host);
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000295 } else
rniwa@webkit.org95386982020-02-28 03:37:22 +0000296 m_elementsWithNewRenderer.add(element);
rniwa@webkit.orgc46d21d2019-12-16 22:36:55 +0000297}
298
299using PositionTuple = std::tuple<RefPtr<Node>, unsigned, unsigned>;
300static const PositionTuple makePositionTuple(const Position& position)
301{
302 return { position.anchorNode(), static_cast<unsigned>(position.anchorType()), position.anchorType() == Position::PositionIsOffsetInAnchor ? position.offsetInContainerNode() : 0 };
303}
304
305static const std::pair<PositionTuple, PositionTuple> makeHashablePositionRange(const VisiblePosition& start, const VisiblePosition& end)
306{
307 return { makePositionTuple(start.deepEquivalent()), makePositionTuple(end.deepEquivalent()) };
308}
309
310void TextManipulationController::scheduleObservartionUpdate()
311{
312 if (!m_document)
313 return;
314
315 m_document->eventLoop().queueTask(TaskSource::InternalAsyncTask, [weakThis = makeWeakPtr(*this)] {
316 auto* controller = weakThis.get();
317 if (!controller)
318 return;
319
320 HashSet<Ref<Element>> mutatedElements;
rniwa@webkit.org95386982020-02-28 03:37:22 +0000321 for (auto& weakElement : controller->m_elementsWithNewRenderer)
rniwa@webkit.orgc46d21d2019-12-16 22:36:55 +0000322 mutatedElements.add(weakElement);
rniwa@webkit.org95386982020-02-28 03:37:22 +0000323 controller->m_elementsWithNewRenderer.clear();
rniwa@webkit.orgc46d21d2019-12-16 22:36:55 +0000324
325 HashSet<Ref<Element>> filteredElements;
326 for (auto& element : mutatedElements) {
327 auto* parentElement = element->parentElement();
328 if (!parentElement || !mutatedElements.contains(parentElement))
329 filteredElements.add(element.copyRef());
330 }
331 mutatedElements.clear();
332
333 HashSet<std::pair<PositionTuple, PositionTuple>> paragraphSets;
334 for (auto& element : filteredElements) {
335 auto start = startOfParagraph(firstPositionInOrBeforeNode(element.ptr()));
336 auto end = endOfParagraph(lastPositionInOrAfterNode(element.ptr()));
337
338 auto key = makeHashablePositionRange(start, end);
339 if (!paragraphSets.add(key).isNewEntry)
340 continue;
341
342 auto* controller = weakThis.get();
343 if (!controller)
344 return; // Finding the start/end of paragraph may have updated layout & executed arbitrary scripts.
345
346 controller->observeParagraphs(start, end);
347 }
rniwa@webkit.orge44b91352020-03-04 05:22:14 +0000348 controller->flushPendingItemsForCallback();
rniwa@webkit.orgc46d21d2019-12-16 22:36:55 +0000349 });
350}
351
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000352void TextManipulationController::addItem(const Position& startOfParagraph, const Position& endOfParagraph, Vector<ManipulationToken>&& tokens)
353{
rniwa@webkit.orge44b91352020-03-04 05:22:14 +0000354 const unsigned itemCallbackBatchingSize = 128;
355
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000356 ASSERT(m_document);
rniwa@webkit.orge44b91352020-03-04 05:22:14 +0000357 auto newID = m_itemIdentifier.generate();
358 m_pendingItemsForCallback.append(ManipulationItem {
359 newID,
360 tokens.map([](auto& token) { return token; })
361 });
362 m_items.add(newID, ManipulationItemData { startOfParagraph, endOfParagraph, WTFMove(tokens) });
363
364 if (m_pendingItemsForCallback.size() >= itemCallbackBatchingSize)
365 flushPendingItemsForCallback();
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000366}
367
rniwa@webkit.orge44b91352020-03-04 05:22:14 +0000368void TextManipulationController::flushPendingItemsForCallback()
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000369{
rniwa@webkit.orge44b91352020-03-04 05:22:14 +0000370 m_callback(*m_document, m_pendingItemsForCallback);
371 m_pendingItemsForCallback.clear();
372}
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000373
rniwa@webkit.orge44b91352020-03-04 05:22:14 +0000374auto TextManipulationController::completeManipulation(const Vector<WebCore::TextManipulationController::ManipulationItem>& completionItems) -> Vector<ManipulationFailure>
375{
376 Vector<ManipulationFailure> failures;
377 for (unsigned i = 0; i < completionItems.size(); ++i) {
378 auto& itemToComplete = completionItems[i];
379 auto identifier = itemToComplete.identifier;
380 if (!identifier) {
381 failures.append(ManipulationFailure { identifier, i, ManipulationFailureType::InvalidItem });
382 continue;
383 }
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000384
rniwa@webkit.orge44b91352020-03-04 05:22:14 +0000385 auto itemDataIterator = m_items.find(identifier);
386 if (itemDataIterator == m_items.end()) {
387 failures.append(ManipulationFailure { identifier, i, ManipulationFailureType::InvalidItem });
388 continue;
389 }
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000390
rniwa@webkit.orge44b91352020-03-04 05:22:14 +0000391 ManipulationItemData itemData;
392 std::exchange(itemData, itemDataIterator->value);
393 m_items.remove(itemDataIterator);
394
395 auto failureOrNullopt = replace(itemData, itemToComplete.tokens);
396 if (failureOrNullopt)
397 failures.append(ManipulationFailure { identifier, i, *failureOrNullopt });
398 }
399 return failures;
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000400}
401
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000402struct TokenExchangeData {
403 RefPtr<Node> node;
404 String originalContent;
405 bool isExcluded { false };
406 bool isConsumed { false };
407};
408
409struct ReplacementData {
410 Ref<Node> originalNode;
rniwa@webkit.org7afdb0b2019-10-25 20:41:15 +0000411 String newData;
412};
413
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000414struct NodeInsertion {
415 RefPtr<Node> parentIfDifferentFromCommonAncestor;
416 Ref<Node> child;
417};
418
rniwa@webkit.orge44b91352020-03-04 05:22:14 +0000419auto TextManipulationController::replace(const ManipulationItemData& item, const Vector<ManipulationToken>& replacementTokens) -> Optional<ManipulationFailureType>
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000420{
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000421 if (item.start.isOrphan() || item.end.isOrphan())
rniwa@webkit.orge44b91352020-03-04 05:22:14 +0000422 return ManipulationFailureType::ContentChanged;
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000423
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000424 size_t currentTokenIndex = 0;
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000425 HashMap<TokenIdentifier, TokenExchangeData> tokenExchangeMap;
rniwa@webkit.org7afdb0b2019-10-25 20:41:15 +0000426
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000427 RefPtr<Node> commonAncestor;
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000428 ParagraphContentIterator iterator { item.start, item.end };
429 HashSet<Ref<Node>> excludedNodes;
430 for (; !iterator.atEnd(); iterator.advance()) {
431 auto content = iterator.currentContent();
432
433 if (!content.isReplacedContent && !content.isTextContent)
434 continue;
435
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000436 if (currentTokenIndex >= item.tokens.size())
rniwa@webkit.orge44b91352020-03-04 05:22:14 +0000437 return ManipulationFailureType::ContentChanged;
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000438
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000439 auto& currentToken = item.tokens[currentTokenIndex];
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000440 if (!content.isReplacedContent && content.text != currentToken.content)
rniwa@webkit.orge44b91352020-03-04 05:22:14 +0000441 return ManipulationFailureType::ContentChanged;
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000442
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000443 tokenExchangeMap.set(currentToken.identifier, TokenExchangeData { content.node.copyRef(), currentToken.content, currentToken.isExcluded });
444 ++currentTokenIndex;
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000445
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000446 // FIXME: Take care of when currentNode is nullptr.
447 if (content.node) {
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000448 if (!commonAncestor)
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000449 commonAncestor = content.node;
450 else if (!content.node->isDescendantOf(commonAncestor.get())) {
451 commonAncestor = Range::commonAncestorContainer(commonAncestor.get(), content.node.get());
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000452 ASSERT(commonAncestor);
453 }
454 }
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000455 }
456
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000457 RefPtr<Node> nodeAfterStart = item.start.computeNodeAfterPosition();
458 if (!nodeAfterStart)
459 nodeAfterStart = item.start.containerNode();
460
461 RefPtr<Node> nodeAfterEnd = item.end.computeNodeAfterPosition();
462 if (!nodeAfterEnd)
463 nodeAfterEnd = NodeTraversal::nextSkippingChildren(*item.end.containerNode());
464
465 HashSet<Ref<Node>> nodesToRemove;
466 for (RefPtr<Node> currentNode = nodeAfterStart; currentNode && currentNode != nodeAfterEnd; currentNode = NodeTraversal::next(*currentNode)) {
467 if (commonAncestor == currentNode)
468 commonAncestor = currentNode->parentNode();
469 nodesToRemove.add(*currentNode);
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000470 }
471
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000472 Vector<Ref<Node>> currentElementStack;
473 HashSet<Ref<Node>> reusedOriginalNodes;
474 Vector<NodeInsertion> insertions;
475 for (auto& newToken : replacementTokens) {
476 auto it = tokenExchangeMap.find(newToken.identifier);
477 if (it == tokenExchangeMap.end())
rniwa@webkit.orge44b91352020-03-04 05:22:14 +0000478 return ManipulationFailureType::InvalidToken;
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000479
480 auto& exchangeData = it->value;
481
482 RefPtr<Node> contentNode;
483 if (exchangeData.isExcluded) {
484 if (exchangeData.isConsumed)
rniwa@webkit.orge44b91352020-03-04 05:22:14 +0000485 return ManipulationFailureType::ExclusionViolation;
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000486 exchangeData.isConsumed = true;
487 if (!newToken.content.isNull() && newToken.content != exchangeData.originalContent)
rniwa@webkit.orge44b91352020-03-04 05:22:14 +0000488 return ManipulationFailureType::ExclusionViolation;
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000489 contentNode = exchangeData.node;
490 for (RefPtr<Node> currentDescendentNode = NodeTraversal::next(*contentNode, contentNode.get()); currentDescendentNode; currentDescendentNode = NodeTraversal::next(*currentDescendentNode, contentNode.get()))
491 nodesToRemove.remove(*currentDescendentNode);
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000492 } else
493 contentNode = Text::create(commonAncestor->document(), newToken.content);
494
495 auto& originalNode = exchangeData.node ? *exchangeData.node : *commonAncestor;
496 RefPtr<ContainerNode> currentNode = is<ContainerNode>(originalNode) ? &downcast<ContainerNode>(originalNode) : originalNode.parentNode();
497
498 Vector<Ref<Node>> currentAncestors;
499 for (; currentNode && currentNode != commonAncestor; currentNode = currentNode->parentNode())
500 currentAncestors.append(*currentNode);
501 currentAncestors.reverse();
502
503 size_t i =0;
504 while (i < currentElementStack.size() && i < currentAncestors.size() && currentElementStack[i].ptr() == currentAncestors[i].ptr())
505 ++i;
506
507 if (i == currentElementStack.size() && i == currentAncestors.size())
508 insertions.append(NodeInsertion { currentElementStack.size() ? currentElementStack.last().ptr() : nullptr, contentNode.releaseNonNull() });
509 else {
510 if (i < currentElementStack.size())
511 currentElementStack.shrink(i);
512 for (;i < currentAncestors.size(); ++i) {
513 Ref<Node> currentNode = currentAncestors[i].copyRef();
514 if (!reusedOriginalNodes.add(currentNode.copyRef()).isNewEntry) {
515 auto clonedNode = currentNode->cloneNodeInternal(currentNode->document(), Node::CloningOperation::OnlySelf);
516 if (auto* data = currentNode->eventTargetData())
517 data->eventListenerMap.copyEventListenersNotCreatedFromMarkupToTarget(clonedNode.ptr());
518 currentNode = WTFMove(clonedNode);
519 }
520
521 insertions.append(NodeInsertion { currentElementStack.size() ? currentElementStack.last().ptr() : nullptr, currentNode.copyRef() });
522 currentElementStack.append(WTFMove(currentNode));
523 }
524 insertions.append(NodeInsertion { currentElementStack.size() ? currentElementStack.last().ptr() : nullptr, contentNode.releaseNonNull() });
525 }
526 }
527
528 Position insertionPoint = item.start;
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000529 if (insertionPoint.anchorNode() != insertionPoint.containerNode())
530 insertionPoint = insertionPoint.parentAnchoredEquivalent();
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000531 while (insertionPoint.containerNode() != commonAncestor)
532 insertionPoint = positionInParentBeforeNode(insertionPoint.containerNode());
533 ASSERT(!insertionPoint.isNull());
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000534 ASSERT(!insertionPoint.isOrphan());
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000535
536 for (auto& node : nodesToRemove)
537 node->remove();
538
539 for (auto& insertion : insertions) {
rniwa@webkit.orgcffb7892020-03-07 01:14:23 +0000540 if (!insertion.parentIfDifferentFromCommonAncestor) {
541 insertionPoint.containerNode()->insertBefore(insertion.child, insertionPoint.computeNodeAfterPosition());
542 insertionPoint = positionInParentAfterNode(insertion.child.ptr());
543 } else
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000544 insertion.parentIfDifferentFromCommonAncestor->appendChild(insertion.child);
wenson_hsieh@apple.com54632b72020-01-10 00:40:14 +0000545 if (is<Element>(insertion.child.get()))
rniwa@webkit.org95386982020-02-28 03:37:22 +0000546 m_manipulatedElements.add(downcast<Element>(insertion.child.get()));
rniwa@webkit.org900f5602019-12-21 02:09:50 +0000547 }
rniwa@webkit.org7afdb0b2019-10-25 20:41:15 +0000548
rniwa@webkit.orge44b91352020-03-04 05:22:14 +0000549 return WTF::nullopt;
rniwa@webkit.orge9da7a82019-10-25 00:08:13 +0000550}
551
552} // namespace WebCore