Web Inspector: rename DOMEditor to DOMPatchSupport, move undoable actions from
[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::DOMAction : public InspectorHistory::Action {
53 public:
54     DOMAction(const String& name) : InspectorHistory::Action(name) { }
55
56     virtual bool perform(ErrorString* errorString)
57     {
58         ExceptionCode ec = 0;
59         bool result = perform(ec);
60         if (ec) {
61             ExceptionCodeDescription description(ec);
62             *errorString = description.name;
63         }
64         return result && !ec;
65     }
66
67     virtual bool undo(ErrorString* errorString)
68     {
69         ExceptionCode ec = 0;
70         bool result = undo(ec);
71         if (ec) {
72             ExceptionCodeDescription description(ec);
73             *errorString = description.name;
74         }
75         return result && !ec;
76     }
77
78     virtual bool perform(ExceptionCode&) = 0;
79
80     virtual bool undo(ExceptionCode&) = 0;
81
82 private:
83     RefPtr<Node> m_parentNode;
84     RefPtr<Node> m_node;
85     RefPtr<Node> m_anchorNode;
86 };
87
88 class DOMEditor::RemoveChildAction : public DOMEditor::DOMAction {
89     WTF_MAKE_NONCOPYABLE(RemoveChildAction);
90 public:
91     RemoveChildAction(Node* parentNode, Node* node)
92         : DOMEditor::DOMAction("RemoveChild")
93         , m_parentNode(parentNode)
94         , m_node(node)
95     {
96     }
97
98     virtual bool perform(ExceptionCode& ec)
99     {
100         m_anchorNode = m_node->nextSibling();
101         return m_parentNode->removeChild(m_node.get(), ec);
102     }
103
104     virtual bool undo(ExceptionCode& ec)
105     {
106         return m_parentNode->insertBefore(m_node.get(), m_anchorNode.get(), ec);
107     }
108
109 private:
110     RefPtr<Node> m_parentNode;
111     RefPtr<Node> m_node;
112     RefPtr<Node> m_anchorNode;
113 };
114
115 class DOMEditor::InsertBeforeAction : public DOMEditor::DOMAction {
116     WTF_MAKE_NONCOPYABLE(InsertBeforeAction);
117 public:
118     InsertBeforeAction(Node* parentNode, Node* node, Node* anchorNode)
119         : DOMEditor::DOMAction("InsertBefore")
120         , m_parentNode(parentNode)
121         , m_node(node)
122         , m_anchorNode(anchorNode)
123     {
124     }
125
126     virtual bool perform(ExceptionCode& ec)
127     {
128         if (m_node->parentNode()) {
129             m_removeChildAction = adoptPtr(new RemoveChildAction(m_node->parentNode(), m_node.get()));
130             if (!m_removeChildAction->perform(ec))
131                 return false;
132         }
133         return m_parentNode->insertBefore(m_node.get(), m_anchorNode.get(), ec);
134     }
135
136     virtual bool undo(ExceptionCode& ec)
137     {
138         if (m_removeChildAction)
139             return m_removeChildAction->undo(ec);
140
141         return m_parentNode->removeChild(m_node.get(), ec);
142     }
143
144 private:
145     RefPtr<Node> m_parentNode;
146     RefPtr<Node> m_node;
147     RefPtr<Node> m_anchorNode;
148     OwnPtr<RemoveChildAction> m_removeChildAction;
149 };
150
151 class DOMEditor::RemoveAttributeAction : public DOMEditor::DOMAction {
152     WTF_MAKE_NONCOPYABLE(RemoveAttributeAction);
153 public:
154     RemoveAttributeAction(Element* element, const String& name)
155         : DOMEditor::DOMAction("RemoveAttribute")
156         , m_element(element)
157         , m_name(name)
158     {
159     }
160
161     virtual bool perform(ExceptionCode&)
162     {
163         m_value = m_element->getAttribute(m_name);
164         m_element->removeAttribute(m_name);
165         return true;
166     }
167
168     virtual bool undo(ExceptionCode& ec)
169     {
170         m_element->setAttribute(m_name, m_value, ec);
171         return true;
172     }
173
174 private:
175     RefPtr<Element> m_element;
176     String m_name;
177     String m_value;
178 };
179
180 class DOMEditor::SetAttributeAction : public DOMEditor::DOMAction {
181     WTF_MAKE_NONCOPYABLE(SetAttributeAction);
182 public:
183     SetAttributeAction(Element* element, const String& name, const String& value)
184         : DOMEditor::DOMAction("SetAttribute")
185         , m_element(element)
186         , m_name(name)
187         , m_value(value)
188         , m_hadAttribute(false)
189     {
190     }
191
192     virtual bool perform(ExceptionCode& ec)
193     {
194         m_hadAttribute = m_element->hasAttribute(m_name);
195         if (m_hadAttribute)
196             m_oldValue = m_element->getAttribute(m_name);
197         m_element->setAttribute(m_name, m_value, ec);
198         return !ec;
199     }
200
201     virtual bool undo(ExceptionCode& ec)
202     {
203         if (m_hadAttribute)
204             m_element->setAttribute(m_name, m_oldValue, ec);
205         else
206             m_element->removeAttribute(m_name);
207         return true;
208     }
209
210 private:
211     RefPtr<Element> m_element;
212     String m_name;
213     String m_value;
214     bool m_hadAttribute;
215     String m_oldValue;
216 };
217
218 class DOMEditor::SetOuterHTMLAction : public DOMEditor::DOMAction {
219     WTF_MAKE_NONCOPYABLE(SetOuterHTMLAction);
220 public:
221     SetOuterHTMLAction(Node* node, const String& html)
222         : DOMEditor::DOMAction("SetOuterHTML")
223         , m_node(node)
224         , m_nextSibling(node->nextSibling())
225         , m_html(html)
226         , m_newNode(0)
227     {
228     }
229
230     virtual bool perform(ExceptionCode& ec)
231     {
232         m_oldHTML = createMarkup(m_node.get());
233         DOMPatchSupport domPatchSupport(m_node->ownerDocument());
234         m_newNode = domPatchSupport.patchNode(m_node.get(), m_html, ec);
235         return !ec;
236     }
237
238     virtual bool undo(ExceptionCode& ec)
239     {
240         DOMPatchSupport domPatchSupport(m_newNode->ownerDocument());
241         Node* node = domPatchSupport.patchNode(m_newNode, m_oldHTML, ec);
242         if (ec || !node)
243             return false;
244         // HTML editing could have produced extra nodes. Remove them if necessary.
245         node = node->nextSibling();
246
247         while (!ec && node && node != m_nextSibling.get()) {
248             Node* nodeToRemove = node;
249             node = node->nextSibling();
250             nodeToRemove->remove(ec);
251         }
252         return !ec;
253     }
254
255     Node* newNode()
256     {
257         return m_newNode;
258     }
259
260 private:
261     RefPtr<Node> m_node;
262     RefPtr<Node> m_nextSibling;
263     String m_html;
264     String m_oldHTML;
265     Node* m_newNode;
266 };
267
268 class DOMEditor::ReplaceWholeTextAction : public DOMEditor::DOMAction {
269     WTF_MAKE_NONCOPYABLE(ReplaceWholeTextAction);
270 public:
271     ReplaceWholeTextAction(Text* textNode, const String& text)
272         : DOMAction("ReplaceWholeText")
273         , m_textNode(textNode)
274         , m_text(text)
275     {
276     }
277
278     virtual bool perform(ExceptionCode& ec)
279     {
280         m_oldText = m_textNode->wholeText();
281         m_textNode->replaceWholeText(m_text, ec);
282         return true;
283     }
284
285     virtual bool undo(ExceptionCode& ec)
286     {
287         m_textNode->replaceWholeText(m_oldText, ec);
288         return true;
289     }
290
291 private:
292     RefPtr<Text> m_textNode;
293     String m_text;
294     String m_oldText;
295 };
296
297 DOMEditor::DOMEditor(InspectorHistory* history) : m_history(history) { }
298
299 DOMEditor::~DOMEditor() { }
300
301 bool DOMEditor::insertBefore(Node* parentNode, Node* node, Node* anchorNode, ErrorString* errorString)
302 {
303     return m_history->perform(adoptPtr(new InsertBeforeAction(parentNode, node, anchorNode)), errorString);
304 }
305
306 bool DOMEditor::removeChild(Node* parentNode, Node* node, ErrorString* errorString)
307 {
308     return m_history->perform(adoptPtr(new RemoveChildAction(parentNode, node)), errorString);
309 }
310
311 bool DOMEditor::setAttribute(Element* element, const String& name, const String& value, ErrorString* errorString)
312 {
313     return m_history->perform(adoptPtr(new SetAttributeAction(element, name, value)), errorString);
314 }
315
316 bool DOMEditor::removeAttribute(Element* element, const String& name, ErrorString* errorString)
317 {
318     return m_history->perform(adoptPtr(new RemoveAttributeAction(element, name)), errorString);
319 }
320
321 bool DOMEditor::setOuterHTML(Node* node, const String& html, Node** newNode, ErrorString* errorString)
322 {
323     OwnPtr<SetOuterHTMLAction> action = adoptPtr(new SetOuterHTMLAction(node, html));
324     SetOuterHTMLAction* rawAction = action.get();
325     bool result = m_history->perform(action.release(), errorString);
326     if (result)
327         *newNode = rawAction->newNode();
328     return result;
329 }
330
331 bool DOMEditor::replaceWholeText(Text* textNode, const String& text, ErrorString* errorString)
332 {
333     return m_history->perform(adoptPtr(new ReplaceWholeTextAction(textNode, text)), errorString);
334 }
335
336 } // namespace WebCore
337
338 #endif // ENABLE(INSPECTOR)