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