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