1/*
2 * Copyright (C) 2012 Google 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 are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include "config.h"
32#include "DOMEditor.h"
33
34#include "DOMException.h"
35#include "DOMPatchSupport.h"
36#include "Document.h"
37#include "Element.h"
38#include "InspectorHistory.h"
39#include "Node.h"
40#include "Text.h"
41#include "markup.h"
42#include <wtf/RefPtr.h>
43
44namespace WebCore {
45
46class DOMEditor::RemoveChildAction final : public InspectorHistory::Action {
47 WTF_MAKE_NONCOPYABLE(RemoveChildAction);
48public:
49 RemoveChildAction(Node& parentNode, Node& node)
50 : InspectorHistory::Action()
51 , m_parentNode(parentNode)
52 , m_node(node)
53 {
54 }
55
56 ExceptionOr<void> perform() final
57 {
58 m_anchorNode = m_node->nextSibling();
59 return redo();
60 }
61
62 ExceptionOr<void> undo() final
63 {
64 return m_parentNode->insertBefore(m_node, m_anchorNode.get());
65 }
66
67 ExceptionOr<void> redo() final
68 {
69 return m_parentNode->removeChild(m_node);
70 }
71
72private:
73 Ref<Node> m_parentNode;
74 Ref<Node> m_node;
75 RefPtr<Node> m_anchorNode;
76};
77
78class DOMEditor::InsertBeforeAction final : public InspectorHistory::Action {
79public:
80 InsertBeforeAction(Node& parentNode, Ref<Node>&& node, Node* anchorNode)
81 : InspectorHistory::Action()
82 , m_parentNode(parentNode)
83 , m_node(WTFMove(node))
84 , m_anchorNode(anchorNode)
85 {
86 }
87
88private:
89 ExceptionOr<void> perform() final
90 {
91 if (m_node->parentNode()) {
92 m_removeChildAction = std::make_unique<RemoveChildAction>(*m_node->parentNode(), m_node);
93 auto result = m_removeChildAction->perform();
94 if (result.hasException())
95 return result.releaseException();
96 }
97 return m_parentNode->insertBefore(m_node, m_anchorNode.get());
98 }
99
100 ExceptionOr<void> undo() final
101 {
102 auto result = m_parentNode->removeChild(m_node);
103 if (result.hasException())
104 return result.releaseException();
105 if (!m_removeChildAction)
106 return { };
107 return m_removeChildAction->undo();
108 }
109
110 ExceptionOr<void> redo() final
111 {
112 if (m_removeChildAction) {
113 auto result = m_removeChildAction->redo();
114 if (result.hasException())
115 return result.releaseException();
116 }
117 return m_parentNode->insertBefore(m_node, m_anchorNode.get());
118 }
119
120 Ref<Node> m_parentNode;
121 Ref<Node> m_node;
122 RefPtr<Node> m_anchorNode;
123 std::unique_ptr<RemoveChildAction> m_removeChildAction;
124};
125
126class DOMEditor::RemoveAttributeAction final : public InspectorHistory::Action {
127 WTF_MAKE_NONCOPYABLE(RemoveAttributeAction);
128public:
129 RemoveAttributeAction(Element& element, const String& name)
130 : InspectorHistory::Action()
131 , m_element(element)
132 , m_name(name)
133 {
134 }
135
136private:
137 ExceptionOr<void> perform() final
138 {
139 m_value = m_element->getAttribute(m_name);
140 return redo();
141 }
142
143 ExceptionOr<void> undo() final
144 {
145 return m_element->setAttribute(m_name, m_value);
146 }
147
148 ExceptionOr<void> redo() final
149 {
150 m_element->removeAttribute(m_name);
151 return { };
152 }
153
154 Ref<Element> m_element;
155 String m_name;
156 String m_value;
157};
158
159class DOMEditor::SetAttributeAction final : public InspectorHistory::Action {
160 WTF_MAKE_NONCOPYABLE(SetAttributeAction);
161public:
162 SetAttributeAction(Element& element, const AtomicString& name, const AtomicString& value)
163 : InspectorHistory::Action()
164 , m_element(element)
165 , m_name(name)
166 , m_value(value)
167 {
168 }
169
170private:
171 ExceptionOr<void> perform() final
172 {
173 m_oldValue = m_element->getAttribute(m_name);
174 return redo();
175 }
176
177 ExceptionOr<void> undo() final
178 {
179 if (m_oldValue.isNull()) {
180 m_element->removeAttribute(m_name);
181 return { };
182 }
183 return m_element->setAttribute(m_name, m_oldValue);
184 }
185
186 ExceptionOr<void> redo() final
187 {
188 return m_element->setAttribute(m_name, m_value);
189 }
190
191 Ref<Element> m_element;
192 AtomicString m_name;
193 AtomicString m_value;
194 AtomicString m_oldValue;
195};
196
197class DOMEditor::SetOuterHTMLAction final : public InspectorHistory::Action {
198public:
199 SetOuterHTMLAction(Node& node, const String& html)
200 : InspectorHistory::Action()
201 , m_node(node)
202 , m_nextSibling(node.nextSibling())
203 , m_html(html)
204 {
205 }
206
207 Node* newNode() const
208 {
209 return m_newNode.get();
210 }
211
212private:
213 ExceptionOr<void> perform() final
214 {
215 m_oldHTML = serializeFragment(m_node.get(), SerializedNodes::SubtreeIncludingNode);
216 auto result = DOMPatchSupport { m_domEditor, m_node->document() }.patchNode(m_node, m_html);
217 if (result.hasException())
218 return result.releaseException();
219 m_newNode = result.releaseReturnValue();
220 return { };
221 }
222
223 ExceptionOr<void> undo() final
224 {
225 return m_history.undo();
226 }
227
228 ExceptionOr<void> redo() final
229 {
230 return m_history.redo();
231 }
232
233 Ref<Node> m_node;
234 RefPtr<Node> m_nextSibling;
235 String m_html;
236 String m_oldHTML;
237 RefPtr<Node> m_newNode { nullptr };
238 InspectorHistory m_history;
239 DOMEditor m_domEditor { m_history };
240};
241
242class DOMEditor::InsertAdjacentHTMLAction final : public InspectorHistory::Action {
243 WTF_MAKE_NONCOPYABLE(InsertAdjacentHTMLAction);
244public:
245 InsertAdjacentHTMLAction(Element& element, const String& position, const String& html)
246 : InspectorHistory::Action()
247 , m_element(element)
248 , m_position(position)
249 , m_html(html)
250 {
251 }
252
253private:
254 ExceptionOr<void> perform() final
255 {
256 return redo();
257 }
258
259 ExceptionOr<void> undo() final
260 {
261 for (auto& addedNode : m_addedNodes)
262 addedNode->remove();
263 m_addedNodes.clear();
264 return { };
265 }
266
267 ExceptionOr<void> redo() final
268 {
269 auto result = m_element->insertAdjacentHTML(m_position, m_html, &m_addedNodes);
270 if (result.hasException())
271 return result.releaseException();
272 return { };
273 }
274
275 Ref<Element> m_element;
276 NodeVector m_addedNodes;
277 String m_position;
278 String m_html;
279};
280
281class DOMEditor::ReplaceWholeTextAction final : public InspectorHistory::Action {
282 WTF_MAKE_NONCOPYABLE(ReplaceWholeTextAction);
283public:
284 ReplaceWholeTextAction(Text& textNode, const String& text)
285 : InspectorHistory::Action()
286 , m_textNode(textNode)
287 , m_text(text)
288 {
289 }
290
291private:
292 ExceptionOr<void> perform() final
293 {
294 m_oldText = m_textNode->wholeText();
295 return redo();
296 }
297
298 ExceptionOr<void> undo() final
299 {
300 m_textNode->replaceWholeText(m_oldText);
301 return { };
302 }
303
304 ExceptionOr<void> redo() final
305 {
306 m_textNode->replaceWholeText(m_text);
307 return { };
308 }
309
310 Ref<Text> m_textNode;
311 String m_text;
312 String m_oldText;
313};
314
315class DOMEditor::ReplaceChildNodeAction final: public InspectorHistory::Action {
316 WTF_MAKE_NONCOPYABLE(ReplaceChildNodeAction);
317public:
318 ReplaceChildNodeAction(Node& parentNode, Ref<Node>&& newNode, Node& oldNode)
319 : InspectorHistory::Action()
320 , m_parentNode(parentNode)
321 , m_newNode(WTFMove(newNode))
322 , m_oldNode(oldNode)
323 {
324 }
325
326private:
327 ExceptionOr<void> perform() final
328 {
329 return redo();
330 }
331
332 ExceptionOr<void> undo() final
333 {
334 return m_parentNode->replaceChild(m_oldNode, m_newNode);
335 }
336
337 ExceptionOr<void> redo() final
338 {
339 return m_parentNode->replaceChild(m_newNode, m_oldNode);
340 }
341
342 Ref<Node> m_parentNode;
343 Ref<Node> m_newNode;
344 Ref<Node> m_oldNode;
345};
346
347class DOMEditor::SetNodeValueAction final : public InspectorHistory::Action {
348 WTF_MAKE_NONCOPYABLE(SetNodeValueAction);
349public:
350 SetNodeValueAction(Node& node, const String& value)
351 : InspectorHistory::Action()
352 , m_node(node)
353 , m_value(value)
354 {
355 }
356
357private:
358 ExceptionOr<void> perform() final
359 {
360 m_oldValue = m_node->nodeValue();
361 return redo();
362 }
363
364 ExceptionOr<void> undo() final
365 {
366 return m_node->setNodeValue(m_oldValue);
367 }
368
369 ExceptionOr<void> redo() final
370 {
371 return m_node->setNodeValue(m_value);
372 }
373
374 Ref<Node> m_node;
375 String m_value;
376 String m_oldValue;
377};
378
379DOMEditor::DOMEditor(InspectorHistory& history)
380 : m_history(history)
381{
382}
383
384DOMEditor::~DOMEditor() = default;
385
386ExceptionOr<void> DOMEditor::insertBefore(Node& parentNode, Ref<Node>&& node, Node* anchorNode)
387{
388 return m_history.perform(std::make_unique<InsertBeforeAction>(parentNode, WTFMove(node), anchorNode));
389}
390
391ExceptionOr<void> DOMEditor::removeChild(Node& parentNode, Node& node)
392{
393 return m_history.perform(std::make_unique<RemoveChildAction>(parentNode, node));
394}
395
396ExceptionOr<void> DOMEditor::setAttribute(Element& element, const String& name, const String& value)
397{
398 return m_history.perform(std::make_unique<SetAttributeAction>(element, name, value));
399}
400
401ExceptionOr<void> DOMEditor::removeAttribute(Element& element, const String& name)
402{
403 return m_history.perform(std::make_unique<RemoveAttributeAction>(element, name));
404}
405
406ExceptionOr<void> DOMEditor::setOuterHTML(Node& node, const String& html, Node*& newNode)
407{
408 auto action = std::make_unique<SetOuterHTMLAction>(node, html);
409 auto& rawAction = *action;
410 auto result = m_history.perform(WTFMove(action));
411 if (!result.hasException())
412 newNode = rawAction.newNode();
413 return result;
414}
415
416ExceptionOr<void> DOMEditor::insertAdjacentHTML(Element& element, const String& where, const String& html)
417{
418 return m_history.perform(std::make_unique<InsertAdjacentHTMLAction>(element, where, html));
419}
420
421ExceptionOr<void> DOMEditor::replaceWholeText(Text& textNode, const String& text)
422{
423 return m_history.perform(std::make_unique<ReplaceWholeTextAction>(textNode, text));
424}
425
426ExceptionOr<void> DOMEditor::replaceChild(Node& parentNode, Ref<Node>&& newNode, Node& oldNode)
427{
428 return m_history.perform(std::make_unique<ReplaceChildNodeAction>(parentNode, WTFMove(newNode), oldNode));
429}
430
431ExceptionOr<void> DOMEditor::setNodeValue(Node& node, const String& value)
432{
433 return m_history.perform(std::make_unique<SetNodeValueAction>(node, value));
434}
435
436static bool populateErrorString(ExceptionOr<void>&& result, ErrorString& errorString)
437{
438 if (!result.hasException())
439 return true;
440 errorString = DOMException::name(result.releaseException().code());
441 return false;
442}
443
444bool DOMEditor::insertBefore(Node& parentNode, Ref<Node>&& node, Node* anchorNode, ErrorString& errorString)
445{
446 return populateErrorString(insertBefore(parentNode, WTFMove(node), anchorNode), errorString);
447}
448
449bool DOMEditor::removeChild(Node& parentNode, Node& node, ErrorString& errorString)
450{
451 return populateErrorString(removeChild(parentNode, node), errorString);
452}
453
454bool DOMEditor::setAttribute(Element& element, const String& name, const String& value, ErrorString& errorString)
455{
456 return populateErrorString(setAttribute(element, name, value), errorString);
457}
458
459bool DOMEditor::removeAttribute(Element& element, const String& name, ErrorString& errorString)
460{
461 return populateErrorString(removeAttribute(element, name), errorString);
462}
463
464bool DOMEditor::setOuterHTML(Node& node, const String& html, Node*& newNode, ErrorString& errorString)
465{
466 return populateErrorString(setOuterHTML(node, html, newNode), errorString);
467}
468
469bool DOMEditor::insertAdjacentHTML(Element& element, const String& where, const String& html, ErrorString& errorString)
470{
471 return populateErrorString(insertAdjacentHTML(element, where, html), errorString);
472}
473
474bool DOMEditor::replaceWholeText(Text& textNode, const String& text, ErrorString& errorString)
475{
476 return populateErrorString(replaceWholeText(textNode, text), errorString);
477}
478
479} // namespace WebCore
480