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 namespace WebCore {
     46 
     47 class DOMEditor::RemoveChildAction FINAL : public InspectorHistory::Action {
     48     WTF_MAKE_NONCOPYABLE(RemoveChildAction);
     49 public:
     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 
     83 private:
     84     RefPtrWillBeMember<Node> m_parentNode;
     85     RefPtrWillBeMember<Node> m_node;
     86     RefPtrWillBeMember<Node> m_anchorNode;
     87 };
     88 
     89 class DOMEditor::InsertBeforeAction FINAL : public InspectorHistory::Action {
     90     WTF_MAKE_NONCOPYABLE(InsertBeforeAction);
     91 public:
     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 
    138 private:
    139     RefPtrWillBeMember<Node> m_parentNode;
    140     RefPtrWillBeMember<Node> m_node;
    141     RefPtrWillBeMember<Node> m_anchorNode;
    142     RefPtrWillBeMember<RemoveChildAction> m_removeChildAction;
    143 };
    144 
    145 class DOMEditor::RemoveAttributeAction FINAL : public InspectorHistory::Action {
    146     WTF_MAKE_NONCOPYABLE(RemoveAttributeAction);
    147 public:
    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 
    179 private:
    180     RefPtrWillBeMember<Element> m_element;
    181     AtomicString m_name;
    182     AtomicString m_value;
    183 };
    184 
    185 class DOMEditor::SetAttributeAction FINAL : public InspectorHistory::Action {
    186     WTF_MAKE_NONCOPYABLE(SetAttributeAction);
    187 public:
    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 
    227 private:
    228     RefPtrWillBeMember<Element> m_element;
    229     AtomicString m_name;
    230     AtomicString m_value;
    231     bool m_hadAttribute;
    232     AtomicString m_oldValue;
    233 };
    234 
    235 class DOMEditor::SetOuterHTMLAction FINAL : public InspectorHistory::Action {
    236     WTF_MAKE_NONCOPYABLE(SetOuterHTMLAction);
    237 public:
    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 
    283 private:
    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 
    293 class DOMEditor::ReplaceWholeTextAction FINAL : public InspectorHistory::Action {
    294     WTF_MAKE_NONCOPYABLE(ReplaceWholeTextAction);
    295 public:
    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 
    327 private:
    328     RefPtrWillBeMember<Text> m_textNode;
    329     String m_text;
    330     String m_oldText;
    331 };
    332 
    333 class DOMEditor::ReplaceChildNodeAction FINAL : public InspectorHistory::Action {
    334     WTF_MAKE_NONCOPYABLE(ReplaceChildNodeAction);
    335 public:
    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 
    369 private:
    370     RefPtrWillBeMember<Node> m_parentNode;
    371     RefPtrWillBeMember<Node> m_newNode;
    372     RefPtrWillBeMember<Node> m_oldNode;
    373 };
    374 
    375 class DOMEditor::SetNodeValueAction FINAL : public InspectorHistory::Action {
    376     WTF_MAKE_NONCOPYABLE(SetNodeValueAction);
    377 public:
    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 
    409 private:
    410     RefPtrWillBeMember<Node> m_node;
    411     String m_value;
    412     String m_oldValue;
    413 };
    414 
    415 DOMEditor::DOMEditor(InspectorHistory* history) : m_history(history) { }
    416 
    417 bool 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 
    422 bool DOMEditor::removeChild(Node* parentNode, Node* node, ExceptionState& exceptionState)
    423 {
    424     return m_history->perform(adoptRefWillBeNoop(new RemoveChildAction(parentNode, node)), exceptionState);
    425 }
    426 
    427 bool 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 
    432 bool DOMEditor::removeAttribute(Element* element, const String& name, ExceptionState& exceptionState)
    433 {
    434     return m_history->perform(adoptRefWillBeNoop(new RemoveAttributeAction(element, AtomicString(name))), exceptionState);
    435 }
    436 
    437 bool 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 
    446 bool DOMEditor::replaceWholeText(Text* textNode, const String& text, ExceptionState& exceptionState)
    447 {
    448     return m_history->perform(adoptRefWillBeNoop(new ReplaceWholeTextAction(textNode, text)), exceptionState);
    449 }
    450 
    451 bool 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 
    456 bool DOMEditor::setNodeValue(Node* node, const String& value, ExceptionState& exceptionState)
    457 {
    458     return m_history->perform(adoptRefWillBeNoop(new SetNodeValueAction(node, value)), exceptionState);
    459 }
    460 
    461 static void populateErrorString(ExceptionState& exceptionState, ErrorString* errorString)
    462 {
    463     if (exceptionState.hadException())
    464         *errorString = DOMException::getErrorName(exceptionState.code());
    465 }
    466 
    467 bool 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 
    475 bool 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 
    483 bool 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 
    491 bool 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 
    499 bool 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 
    507 bool 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 
    515 void DOMEditor::trace(Visitor* visitor)
    516 {
    517     visitor->trace(m_history);
    518 }
    519 
    520 } // namespace WebCore
    521 
    522