Web Inspector: implement undo for setOuterHTML via undo-ing nested primitive commands.
[WebKit-https.git] / Source / WebCore / inspector / DOMEditor.cpp
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 #if ENABLE(INSPECTOR)
35
36 #include "DOMPatchSupport.h"
37 #include "Document.h"
38 #include "Element.h"
39 #include "ExceptionCode.h"
40 #include "InspectorHistory.h"
41 #include "Node.h"
42 #include "Text.h"
43
44 #include "markup.h"
45
46 #include <wtf/RefPtr.h>
47
48 using namespace std;
49
50 namespace WebCore {
51
52 class DOMEditor::RemoveChildAction : public InspectorHistory::Action {
53     WTF_MAKE_NONCOPYABLE(RemoveChildAction);
54 public:
55     RemoveChildAction(Node* parentNode, Node* node)
56         : InspectorHistory::Action("RemoveChild")
57         , m_parentNode(parentNode)
58         , m_node(node)
59     {
60     }
61
62     virtual bool perform(ExceptionCode& ec)
63     {
64         m_anchorNode = m_node->nextSibling();
65         return m_parentNode->removeChild(m_node.get(), ec);
66     }
67
68     virtual bool undo(ExceptionCode& ec)
69     {
70         return m_parentNode->insertBefore(m_node.get(), m_anchorNode.get(), ec);
71     }
72
73 private:
74     RefPtr<Node> m_parentNode;
75     RefPtr<Node> m_node;
76     RefPtr<Node> m_anchorNode;
77 };
78
79 class DOMEditor::InsertBeforeAction : public InspectorHistory::Action {
80     WTF_MAKE_NONCOPYABLE(InsertBeforeAction);
81 public:
82     InsertBeforeAction(Node* parentNode, PassRefPtr<Node> node, Node* anchorNode)
83         : InspectorHistory::Action("InsertBefore")
84         , m_parentNode(parentNode)
85         , m_node(node)
86         , m_anchorNode(anchorNode)
87     {
88     }
89
90     virtual bool perform(ExceptionCode& ec)
91     {
92         if (m_node->parentNode()) {
93             m_removeChildAction = adoptPtr(new RemoveChildAction(m_node->parentNode(), m_node.get()));
94             if (!m_removeChildAction->perform(ec))
95                 return false;
96         }
97         return m_parentNode->insertBefore(m_node.get(), m_anchorNode.get(), ec);
98     }
99
100     virtual bool undo(ExceptionCode& ec)
101     {
102         if (!m_parentNode->removeChild(m_node.get(), ec))
103             return false;
104         if (m_removeChildAction)
105             return m_removeChildAction->undo(ec);
106         return true;
107     }
108
109 private:
110     RefPtr<Node> m_parentNode;
111     RefPtr<Node> m_node;
112     RefPtr<Node> m_anchorNode;
113     OwnPtr<RemoveChildAction> m_removeChildAction;
114 };
115
116 class DOMEditor::RemoveAttributeAction : public InspectorHistory::Action {
117     WTF_MAKE_NONCOPYABLE(RemoveAttributeAction);
118 public:
119     RemoveAttributeAction(Element* element, const String& name)
120         : InspectorHistory::Action("RemoveAttribute")
121         , m_element(element)
122         , m_name(name)
123     {
124     }
125
126     virtual bool perform(ExceptionCode&)
127     {
128         m_value = m_element->getAttribute(m_name);
129         m_element->removeAttribute(m_name);
130         return true;
131     }
132
133     virtual bool undo(ExceptionCode& ec)
134     {
135         m_element->setAttribute(m_name, m_value, ec);
136         return true;
137     }
138
139 private:
140     RefPtr<Element> m_element;
141     String m_name;
142     String m_value;
143 };
144
145 class DOMEditor::SetAttributeAction : public InspectorHistory::Action {
146     WTF_MAKE_NONCOPYABLE(SetAttributeAction);
147 public:
148     SetAttributeAction(Element* element, const String& name, const String& value)
149         : InspectorHistory::Action("SetAttribute")
150         , m_element(element)
151         , m_name(name)
152         , m_value(value)
153         , m_hadAttribute(false)
154     {
155     }
156
157     virtual bool perform(ExceptionCode& ec)
158     {
159         m_hadAttribute = m_element->hasAttribute(m_name);
160         if (m_hadAttribute)
161             m_oldValue = m_element->getAttribute(m_name);
162         m_element->setAttribute(m_name, m_value, ec);
163         return !ec;
164     }
165
166     virtual bool undo(ExceptionCode& ec)
167     {
168         if (m_hadAttribute)
169             m_element->setAttribute(m_name, m_oldValue, ec);
170         else
171             m_element->removeAttribute(m_name);
172         return true;
173     }
174
175 private:
176     RefPtr<Element> m_element;
177     String m_name;
178     String m_value;
179     bool m_hadAttribute;
180     String m_oldValue;
181 };
182
183 class DOMEditor::SetOuterHTMLAction : public InspectorHistory::Action {
184     WTF_MAKE_NONCOPYABLE(SetOuterHTMLAction);
185 public:
186     SetOuterHTMLAction(Node* node, const String& html)
187         : InspectorHistory::Action("SetOuterHTML")
188         , m_node(node)
189         , m_nextSibling(node->nextSibling())
190         , m_html(html)
191         , m_newNode(0)
192         , m_history(adoptPtr(new InspectorHistory()))
193         , m_domEditor(adoptPtr(new DOMEditor(m_history.get())))
194     {
195     }
196
197     virtual bool perform(ExceptionCode& ec)
198     {
199         m_oldHTML = createMarkup(m_node.get());
200         DOMPatchSupport domPatchSupport(m_domEditor.get(), m_node->ownerDocument());
201         m_newNode = domPatchSupport.patchNode(m_node.get(), m_html, ec);
202         return !ec;
203     }
204
205     virtual bool undo(ExceptionCode& ec)
206     {
207         return m_history->undo(ec);
208     }
209
210     Node* newNode()
211     {
212         return m_newNode;
213     }
214
215 private:
216     RefPtr<Node> m_node;
217     RefPtr<Node> m_nextSibling;
218     String m_html;
219     String m_oldHTML;
220     Node* m_newNode;
221     OwnPtr<InspectorHistory> m_history;
222     OwnPtr<DOMEditor> m_domEditor;
223 };
224
225 class DOMEditor::ReplaceWholeTextAction : public InspectorHistory::Action {
226     WTF_MAKE_NONCOPYABLE(ReplaceWholeTextAction);
227 public:
228     ReplaceWholeTextAction(Text* textNode, const String& text)
229         : InspectorHistory::Action("ReplaceWholeText")
230         , m_textNode(textNode)
231         , m_text(text)
232     {
233     }
234
235     virtual bool perform(ExceptionCode& ec)
236     {
237         m_oldText = m_textNode->wholeText();
238         m_textNode->replaceWholeText(m_text, ec);
239         return true;
240     }
241
242     virtual bool undo(ExceptionCode& ec)
243     {
244         m_textNode->replaceWholeText(m_oldText, ec);
245         return true;
246     }
247
248 private:
249     RefPtr<Text> m_textNode;
250     String m_text;
251     String m_oldText;
252 };
253
254 class DOMEditor::ReplaceChildNodeAction : public InspectorHistory::Action {
255     WTF_MAKE_NONCOPYABLE(ReplaceChildNodeAction);
256 public:
257     ReplaceChildNodeAction(Node* parentNode, PassRefPtr<Node> newNode, Node* oldNode)
258         : InspectorHistory::Action("ReplaceChildNode")
259         , m_parentNode(parentNode)
260         , m_newNode(newNode)
261         , m_oldNode(oldNode)
262     {
263     }
264
265     virtual bool perform(ExceptionCode& ec)
266     {
267         return m_parentNode->replaceChild(m_newNode, m_oldNode.get(), ec);
268     }
269
270     virtual bool undo(ExceptionCode& ec)
271     {
272         return m_parentNode->replaceChild(m_oldNode, m_newNode.get(), ec);
273     }
274
275 private:
276     RefPtr<Node> m_parentNode;
277     RefPtr<Node> m_newNode;
278     RefPtr<Node> m_oldNode;
279 };
280
281 class DOMEditor::SetNodeValueAction : public InspectorHistory::Action {
282     WTF_MAKE_NONCOPYABLE(SetNodeValueAction);
283 public:
284     SetNodeValueAction(Node* node, const String& value)
285         : InspectorHistory::Action("SetNodeValue")
286         , m_node(node)
287         , m_value(value)
288     {
289     }
290
291     virtual bool perform(ExceptionCode& ec)
292     {
293         m_oldValue = m_node->nodeValue();
294         m_node->setNodeValue(m_value, ec);
295         return !ec;
296     }
297
298     virtual bool undo(ExceptionCode& ec)
299     {
300         m_node->setNodeValue(m_oldValue, ec);
301         return !ec;
302     }
303
304 private:
305     RefPtr<Node> m_node;
306     String m_value;
307     String m_oldValue;
308 };
309
310 DOMEditor::DOMEditor(InspectorHistory* history) : m_history(history) { }
311
312 DOMEditor::~DOMEditor() { }
313
314 bool DOMEditor::insertBefore(Node* parentNode, PassRefPtr<Node> node, Node* anchorNode, ExceptionCode& ec)
315 {
316     return m_history->perform(adoptPtr(new InsertBeforeAction(parentNode, node, anchorNode)), ec);
317 }
318
319 bool DOMEditor::removeChild(Node* parentNode, Node* node, ExceptionCode& ec)
320 {
321     return m_history->perform(adoptPtr(new RemoveChildAction(parentNode, node)), ec);
322 }
323
324 bool DOMEditor::setAttribute(Element* element, const String& name, const String& value, ExceptionCode& ec)
325 {
326     return m_history->perform(adoptPtr(new SetAttributeAction(element, name, value)), ec);
327 }
328
329 bool DOMEditor::removeAttribute(Element* element, const String& name, ExceptionCode& ec)
330 {
331     return m_history->perform(adoptPtr(new RemoveAttributeAction(element, name)), ec);
332 }
333
334 bool DOMEditor::setOuterHTML(Node* node, const String& html, Node** newNode, ExceptionCode& ec)
335 {
336     OwnPtr<SetOuterHTMLAction> action = adoptPtr(new SetOuterHTMLAction(node, html));
337     SetOuterHTMLAction* rawAction = action.get();
338     bool result = m_history->perform(action.release(), ec);
339     if (result)
340         *newNode = rawAction->newNode();
341     return result;
342 }
343
344 bool DOMEditor::replaceWholeText(Text* textNode, const String& text, ExceptionCode& ec)
345 {
346     return m_history->perform(adoptPtr(new ReplaceWholeTextAction(textNode, text)), ec);
347 }
348
349 bool DOMEditor::replaceChild(Node* parentNode, PassRefPtr<Node> newNode, Node* oldNode, ExceptionCode& ec)
350 {
351     return m_history->perform(adoptPtr(new ReplaceChildNodeAction(parentNode, newNode, oldNode)), ec);
352 }
353
354 bool DOMEditor::setNodeValue(Node* node, const String& value, ExceptionCode& ec)
355 {
356     return m_history->perform(adoptPtr(new SetNodeValueAction(node, value)), ec);
357 }
358
359 static void populateErrorString(const ExceptionCode& ec, ErrorString* errorString)
360 {
361     if (ec) {
362         ExceptionCodeDescription description(ec);
363         *errorString = description.name;
364     }
365 }
366
367 bool DOMEditor::insertBefore(Node* parentNode, PassRefPtr<Node> node, Node* anchorNode, ErrorString* errorString)
368 {
369     ExceptionCode ec = 0;
370     bool result = insertBefore(parentNode, node, anchorNode, ec);
371     populateErrorString(ec, errorString);
372     return result;
373 }
374
375 bool DOMEditor::removeChild(Node* parentNode, Node* node, ErrorString* errorString)
376 {
377     ExceptionCode ec = 0;
378     bool result = removeChild(parentNode, node, ec);
379     populateErrorString(ec, errorString);
380     return result;
381 }
382
383 bool DOMEditor::setAttribute(Element* element, const String& name, const String& value, ErrorString* errorString)
384 {
385     ExceptionCode ec = 0;
386     bool result = setAttribute(element, name, value, ec);
387     populateErrorString(ec, errorString);
388     return result;
389 }
390
391 bool DOMEditor::removeAttribute(Element* element, const String& name, ErrorString* errorString)
392 {
393     ExceptionCode ec = 0;
394     bool result = removeAttribute(element, name, ec);
395     populateErrorString(ec, errorString);
396     return result;
397 }
398
399 bool DOMEditor::setOuterHTML(Node* node, const String& html, Node** newNode, ErrorString* errorString)
400 {
401     ExceptionCode ec = 0;
402     bool result = setOuterHTML(node, html, newNode, ec);
403     populateErrorString(ec, errorString);
404     return result;
405 }
406
407 bool DOMEditor::replaceWholeText(Text* textNode, const String& text, ErrorString* errorString)
408 {
409     ExceptionCode ec = 0;
410     bool result = replaceWholeText(textNode, text, ec);
411     populateErrorString(ec, errorString);
412     return result;
413 }
414
415 } // namespace WebCore
416
417 #endif // ENABLE(INSPECTOR)