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