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