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