blob: c75fe54d5d823c75f067dd225267a97bce26f396 [file] [log] [blame]
/*
* Copyright (C) 2012 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "third_party/blink/renderer/core/inspector/dom_editor.h"
#include "base/macros.h"
#include "base/memory/scoped_refptr.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/dom/dom_exception.h"
#include "third_party/blink/renderer/core/dom/element.h"
#include "third_party/blink/renderer/core/dom/node.h"
#include "third_party/blink/renderer/core/dom/text.h"
#include "third_party/blink/renderer/core/editing/serializers/serialization.h"
#include "third_party/blink/renderer/core/inspector/dom_patch_support.h"
#include "third_party/blink/renderer/core/inspector/inspector_history.h"
#include "third_party/blink/renderer/core/inspector/protocol/Protocol.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
namespace blink {
using protocol::Response;
class DOMEditor::RemoveChildAction final : public InspectorHistory::Action {
public:
RemoveChildAction(ContainerNode* parent_node, Node* node)
: InspectorHistory::Action("RemoveChild"),
parent_node_(parent_node),
node_(node) {}
bool Perform(ExceptionState& exception_state) override {
anchor_node_ = node_->nextSibling();
return Redo(exception_state);
}
bool Undo(ExceptionState& exception_state) override {
parent_node_->InsertBefore(node_.Get(), anchor_node_.Get(),
exception_state);
return !exception_state.HadException();
}
bool Redo(ExceptionState& exception_state) override {
parent_node_->RemoveChild(node_.Get(), exception_state);
return !exception_state.HadException();
}
void Trace(Visitor* visitor) const override {
visitor->Trace(parent_node_);
visitor->Trace(node_);
visitor->Trace(anchor_node_);
InspectorHistory::Action::Trace(visitor);
}
private:
Member<ContainerNode> parent_node_;
Member<Node> node_;
Member<Node> anchor_node_;
DISALLOW_COPY_AND_ASSIGN(RemoveChildAction);
};
class DOMEditor::InsertBeforeAction final : public InspectorHistory::Action {
public:
InsertBeforeAction(ContainerNode* parent_node, Node* node, Node* anchor_node)
: InspectorHistory::Action("InsertBefore"),
parent_node_(parent_node),
node_(node),
anchor_node_(anchor_node) {}
bool Perform(ExceptionState& exception_state) override {
if (node_->parentNode()) {
remove_child_action_ = MakeGarbageCollected<RemoveChildAction>(
node_->parentNode(), node_.Get());
if (!remove_child_action_->Perform(exception_state))
return false;
}
parent_node_->InsertBefore(node_.Get(), anchor_node_.Get(),
exception_state);
return !exception_state.HadException();
}
bool Undo(ExceptionState& exception_state) override {
parent_node_->RemoveChild(node_.Get(), exception_state);
if (exception_state.HadException())
return false;
if (remove_child_action_)
return remove_child_action_->Undo(exception_state);
return true;
}
bool Redo(ExceptionState& exception_state) override {
if (remove_child_action_ && !remove_child_action_->Redo(exception_state))
return false;
parent_node_->InsertBefore(node_.Get(), anchor_node_.Get(),
exception_state);
return !exception_state.HadException();
}
void Trace(Visitor* visitor) const override {
visitor->Trace(parent_node_);
visitor->Trace(node_);
visitor->Trace(anchor_node_);
visitor->Trace(remove_child_action_);
InspectorHistory::Action::Trace(visitor);
}
private:
Member<ContainerNode> parent_node_;
Member<Node> node_;
Member<Node> anchor_node_;
Member<RemoveChildAction> remove_child_action_;
DISALLOW_COPY_AND_ASSIGN(InsertBeforeAction);
};
class DOMEditor::RemoveAttributeAction final : public InspectorHistory::Action {
public:
RemoveAttributeAction(Element* element, const AtomicString& name)
: InspectorHistory::Action("RemoveAttribute"),
element_(element),
name_(name) {}
bool Perform(ExceptionState& exception_state) override {
value_ = element_->getAttribute(name_);
return Redo(exception_state);
}
bool Undo(ExceptionState& exception_state) override {
element_->setAttribute(name_, value_, exception_state);
return true;
}
bool Redo(ExceptionState&) override {
element_->removeAttribute(name_);
return true;
}
void Trace(Visitor* visitor) const override {
visitor->Trace(element_);
InspectorHistory::Action::Trace(visitor);
}
private:
Member<Element> element_;
AtomicString name_;
AtomicString value_;
DISALLOW_COPY_AND_ASSIGN(RemoveAttributeAction);
};
class DOMEditor::SetAttributeAction final : public InspectorHistory::Action {
public:
SetAttributeAction(Element* element,
const AtomicString& name,
const AtomicString& value)
: InspectorHistory::Action("SetAttribute"),
element_(element),
name_(name),
value_(value),
had_attribute_(false) {}
bool Perform(ExceptionState& exception_state) override {
const AtomicString& value = element_->getAttribute(name_);
had_attribute_ = !value.IsNull();
if (had_attribute_)
old_value_ = value;
return Redo(exception_state);
}
bool Undo(ExceptionState& exception_state) override {
if (had_attribute_)
element_->setAttribute(name_, old_value_, exception_state);
else
element_->removeAttribute(name_);
return true;
}
bool Redo(ExceptionState& exception_state) override {
element_->setAttribute(name_, value_, exception_state);
return true;
}
void Trace(Visitor* visitor) const override {
visitor->Trace(element_);
InspectorHistory::Action::Trace(visitor);
}
private:
Member<Element> element_;
AtomicString name_;
AtomicString value_;
bool had_attribute_;
AtomicString old_value_;
DISALLOW_COPY_AND_ASSIGN(SetAttributeAction);
};
class DOMEditor::SetOuterHTMLAction final : public InspectorHistory::Action {
public:
SetOuterHTMLAction(Node* node, const String& html)
: InspectorHistory::Action("SetOuterHTML"),
node_(node),
next_sibling_(node->nextSibling()),
html_(html),
new_node_(nullptr),
history_(MakeGarbageCollected<InspectorHistory>()),
dom_editor_(MakeGarbageCollected<DOMEditor>(history_.Get())) {}
bool Perform(ExceptionState& exception_state) override {
old_html_ = CreateMarkup(node_.Get());
Document* document = IsA<Document>(node_.Get()) ? To<Document>(node_.Get())
: node_->ownerDocument();
DCHECK(document);
if (!document->documentElement())
return false;
DOMPatchSupport dom_patch_support(dom_editor_.Get(), *document);
new_node_ =
dom_patch_support.PatchNode(node_.Get(), html_, exception_state);
return !exception_state.HadException();
}
bool Undo(ExceptionState& exception_state) override {
return history_->Undo(exception_state);
}
bool Redo(ExceptionState& exception_state) override {
return history_->Redo(exception_state);
}
Node* NewNode() { return new_node_; }
void Trace(Visitor* visitor) const override {
visitor->Trace(node_);
visitor->Trace(next_sibling_);
visitor->Trace(new_node_);
visitor->Trace(history_);
visitor->Trace(dom_editor_);
InspectorHistory::Action::Trace(visitor);
}
private:
Member<Node> node_;
Member<Node> next_sibling_;
String html_;
String old_html_;
Member<Node> new_node_;
Member<InspectorHistory> history_;
Member<DOMEditor> dom_editor_;
DISALLOW_COPY_AND_ASSIGN(SetOuterHTMLAction);
};
class DOMEditor::ReplaceWholeTextAction final
: public InspectorHistory::Action {
public:
ReplaceWholeTextAction(Text* text_node, const String& text)
: InspectorHistory::Action("ReplaceWholeText"),
text_node_(text_node),
text_(text) {}
bool Perform(ExceptionState& exception_state) override {
old_text_ = text_node_->wholeText();
return Redo(exception_state);
}
bool Undo(ExceptionState&) override {
text_node_->ReplaceWholeText(old_text_);
return true;
}
bool Redo(ExceptionState&) override {
text_node_->ReplaceWholeText(text_);
return true;
}
void Trace(Visitor* visitor) const override {
visitor->Trace(text_node_);
InspectorHistory::Action::Trace(visitor);
}
private:
Member<Text> text_node_;
String text_;
String old_text_;
DISALLOW_COPY_AND_ASSIGN(ReplaceWholeTextAction);
};
class DOMEditor::ReplaceChildNodeAction final
: public InspectorHistory::Action {
public:
ReplaceChildNodeAction(ContainerNode* parent_node,
Node* new_node,
Node* old_node)
: InspectorHistory::Action("ReplaceChildNode"),
parent_node_(parent_node),
new_node_(new_node),
old_node_(old_node) {}
bool Perform(ExceptionState& exception_state) override {
return Redo(exception_state);
}
bool Undo(ExceptionState& exception_state) override {
parent_node_->ReplaceChild(old_node_, new_node_.Get(), exception_state);
return !exception_state.HadException();
}
bool Redo(ExceptionState& exception_state) override {
parent_node_->ReplaceChild(new_node_, old_node_.Get(), exception_state);
return !exception_state.HadException();
}
void Trace(Visitor* visitor) const override {
visitor->Trace(parent_node_);
visitor->Trace(new_node_);
visitor->Trace(old_node_);
InspectorHistory::Action::Trace(visitor);
}
private:
Member<ContainerNode> parent_node_;
Member<Node> new_node_;
Member<Node> old_node_;
DISALLOW_COPY_AND_ASSIGN(ReplaceChildNodeAction);
};
class DOMEditor::SetNodeValueAction final : public InspectorHistory::Action {
public:
SetNodeValueAction(Node* node, const String& value)
: InspectorHistory::Action("SetNodeValue"), node_(node), value_(value) {}
bool Perform(ExceptionState&) override {
old_value_ = node_->nodeValue();
return Redo(IGNORE_EXCEPTION_FOR_TESTING);
}
bool Undo(ExceptionState&) override {
node_->setNodeValue(old_value_);
return true;
}
bool Redo(ExceptionState&) override {
node_->setNodeValue(value_);
return true;
}
void Trace(Visitor* visitor) const override {
visitor->Trace(node_);
InspectorHistory::Action::Trace(visitor);
}
private:
Member<Node> node_;
String value_;
String old_value_;
DISALLOW_COPY_AND_ASSIGN(SetNodeValueAction);
};
DOMEditor::DOMEditor(InspectorHistory* history) : history_(history) {}
bool DOMEditor::InsertBefore(ContainerNode* parent_node,
Node* node,
Node* anchor_node,
ExceptionState& exception_state) {
return history_->Perform(
MakeGarbageCollected<InsertBeforeAction>(parent_node, node, anchor_node),
exception_state);
}
bool DOMEditor::RemoveChild(ContainerNode* parent_node,
Node* node,
ExceptionState& exception_state) {
return history_->Perform(
MakeGarbageCollected<RemoveChildAction>(parent_node, node),
exception_state);
}
bool DOMEditor::SetAttribute(Element* element,
const String& name,
const String& value,
ExceptionState& exception_state) {
return history_->Perform(
MakeGarbageCollected<SetAttributeAction>(element, AtomicString(name),
AtomicString(value)),
exception_state);
}
bool DOMEditor::RemoveAttribute(Element* element,
const String& name,
ExceptionState& exception_state) {
return history_->Perform(
MakeGarbageCollected<RemoveAttributeAction>(element, AtomicString(name)),
exception_state);
}
bool DOMEditor::SetOuterHTML(Node* node,
const String& html,
Node** new_node,
ExceptionState& exception_state) {
SetOuterHTMLAction* action =
MakeGarbageCollected<SetOuterHTMLAction>(node, html);
bool result = history_->Perform(action, exception_state);
if (result)
*new_node = action->NewNode();
return result;
}
bool DOMEditor::ReplaceWholeText(Text* text_node,
const String& text,
ExceptionState& exception_state) {
return history_->Perform(
MakeGarbageCollected<ReplaceWholeTextAction>(text_node, text),
exception_state);
}
bool DOMEditor::ReplaceChild(ContainerNode* parent_node,
Node* new_node,
Node* old_node,
ExceptionState& exception_state) {
return history_->Perform(MakeGarbageCollected<ReplaceChildNodeAction>(
parent_node, new_node, old_node),
exception_state);
}
bool DOMEditor::SetNodeValue(Node* node,
const String& value,
ExceptionState& exception_state) {
return history_->Perform(
MakeGarbageCollected<SetNodeValueAction>(node, value), exception_state);
}
static Response ToResponse(ExceptionState& exception_state) {
if (exception_state.HadException()) {
String name_prefix = IsDOMExceptionCode(exception_state.Code())
? DOMException::GetErrorName(
exception_state.CodeAs<DOMExceptionCode>()) +
" "
: g_empty_string;
String msg = name_prefix + exception_state.Message();
return Response::ServerError(msg.Utf8());
}
return Response::Success();
}
Response DOMEditor::InsertBefore(ContainerNode* parent_node,
Node* node,
Node* anchor_node) {
DummyExceptionStateForTesting exception_state;
InsertBefore(parent_node, node, anchor_node, exception_state);
return ToResponse(exception_state);
}
Response DOMEditor::RemoveChild(ContainerNode* parent_node, Node* node) {
DummyExceptionStateForTesting exception_state;
RemoveChild(parent_node, node, exception_state);
return ToResponse(exception_state);
}
Response DOMEditor::SetAttribute(Element* element,
const String& name,
const String& value) {
DummyExceptionStateForTesting exception_state;
SetAttribute(element, name, value, exception_state);
return ToResponse(exception_state);
}
Response DOMEditor::RemoveAttribute(Element* element, const String& name) {
DummyExceptionStateForTesting exception_state;
RemoveAttribute(element, name, exception_state);
return ToResponse(exception_state);
}
Response DOMEditor::SetOuterHTML(Node* node,
const String& html,
Node** new_node) {
DummyExceptionStateForTesting exception_state;
SetOuterHTML(node, html, new_node, exception_state);
return ToResponse(exception_state);
}
Response DOMEditor::ReplaceWholeText(Text* text_node, const String& text) {
DummyExceptionStateForTesting exception_state;
ReplaceWholeText(text_node, text, exception_state);
return ToResponse(exception_state);
}
void DOMEditor::Trace(Visitor* visitor) const {
visitor->Trace(history_);
}
} // namespace blink