1 /* 2 * Copyright (C) 2009 Apple Inc. All rights reserved. 3 * Copyright (C) 2011 Google Inc. All rights reserved. 4 * Copyright (C) 2009 Joseph Pecoraro 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 16 * its contributors may be used to endorse or promote products derived 17 * from this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include "config.h" 32 #include "core/inspector/InspectorDOMAgent.h" 33 34 #include "HTMLNames.h" 35 #include "bindings/v8/ExceptionState.h" 36 #include "bindings/v8/ScriptEventListener.h" 37 #include "core/dom/Attr.h" 38 #include "core/dom/CharacterData.h" 39 #include "core/dom/ContainerNode.h" 40 #include "core/dom/DOMException.h" 41 #include "core/dom/Document.h" 42 #include "core/dom/DocumentFragment.h" 43 #include "core/dom/DocumentType.h" 44 #include "core/dom/Element.h" 45 #include "core/events/EventListener.h" 46 #include "core/events/EventTarget.h" 47 #include "core/dom/Node.h" 48 #include "core/dom/NodeList.h" 49 #include "core/dom/NodeTraversal.h" 50 #include "core/dom/PseudoElement.h" 51 #include "core/dom/Text.h" 52 #include "core/dom/shadow/ElementShadow.h" 53 #include "core/dom/shadow/ShadowRoot.h" 54 #include "core/editing/markup.h" 55 #include "core/fileapi/File.h" 56 #include "core/fileapi/FileList.h" 57 #include "core/html/HTMLFrameOwnerElement.h" 58 #include "core/html/HTMLInputElement.h" 59 #include "core/html/HTMLTemplateElement.h" 60 #include "core/inspector/DOMEditor.h" 61 #include "core/inspector/DOMPatchSupport.h" 62 #include "core/inspector/IdentifiersFactory.h" 63 #include "core/inspector/InspectorHistory.h" 64 #include "core/inspector/InspectorOverlay.h" 65 #include "core/inspector/InspectorPageAgent.h" 66 #include "core/inspector/InspectorState.h" 67 #include "core/inspector/InstrumentingAgents.h" 68 #include "core/loader/DocumentLoader.h" 69 #include "core/frame/Frame.h" 70 #include "core/page/FrameTree.h" 71 #include "core/page/Page.h" 72 #include "core/rendering/HitTestResult.h" 73 #include "core/rendering/RenderView.h" 74 #include "core/xml/DocumentXPathEvaluator.h" 75 #include "core/xml/XPathResult.h" 76 #include "platform/PlatformGestureEvent.h" 77 #include "platform/PlatformMouseEvent.h" 78 #include "platform/PlatformTouchEvent.h" 79 #include "wtf/ListHashSet.h" 80 #include "wtf/text/CString.h" 81 #include "wtf/text/WTFString.h" 82 83 namespace WebCore { 84 85 using namespace HTMLNames; 86 87 namespace DOMAgentState { 88 static const char documentRequested[] = "documentRequested"; 89 }; 90 91 static const size_t maxTextSize = 10000; 92 static const UChar ellipsisUChar[] = { 0x2026, 0 }; 93 94 static Color parseColor(const RefPtr<JSONObject>* colorObject) 95 { 96 if (!colorObject || !(*colorObject)) 97 return Color::transparent; 98 99 int r; 100 int g; 101 int b; 102 bool success = (*colorObject)->getNumber("r", &r); 103 success |= (*colorObject)->getNumber("g", &g); 104 success |= (*colorObject)->getNumber("b", &b); 105 if (!success) 106 return Color::transparent; 107 108 double a; 109 success = (*colorObject)->getNumber("a", &a); 110 if (!success) 111 return Color(r, g, b); 112 113 // Clamp alpha to the [0..1] range. 114 if (a < 0) 115 a = 0; 116 else if (a > 1) 117 a = 1; 118 119 return Color(r, g, b, static_cast<int>(a * 255)); 120 } 121 122 static Color parseConfigColor(const String& fieldName, JSONObject* configObject) 123 { 124 const RefPtr<JSONObject> colorObject = configObject->getObject(fieldName); 125 return parseColor(&colorObject); 126 } 127 128 static bool parseQuad(const RefPtr<JSONArray>& quadArray, FloatQuad* quad) 129 { 130 if (!quadArray) 131 return false; 132 const size_t coordinatesInQuad = 8; 133 double coordinates[coordinatesInQuad]; 134 if (quadArray->length() != coordinatesInQuad) 135 return false; 136 for (size_t i = 0; i < coordinatesInQuad; ++i) { 137 if (!quadArray->get(i)->asNumber(coordinates + i)) 138 return false; 139 } 140 quad->setP1(FloatPoint(coordinates[0], coordinates[1])); 141 quad->setP2(FloatPoint(coordinates[2], coordinates[3])); 142 quad->setP3(FloatPoint(coordinates[4], coordinates[5])); 143 quad->setP4(FloatPoint(coordinates[6], coordinates[7])); 144 145 return true; 146 } 147 148 static Node* hoveredNodeForPoint(Frame* frame, const IntPoint& point, bool ignorePointerEventsNone) 149 { 150 HitTestRequest::HitTestRequestType hitType = HitTestRequest::Move | HitTestRequest::ReadOnly | HitTestRequest::AllowChildFrameContent; 151 if (ignorePointerEventsNone) 152 hitType |= HitTestRequest::IgnorePointerEventsNone; 153 HitTestRequest request(hitType); 154 HitTestResult result(frame->view()->windowToContents(point)); 155 frame->contentRenderer()->hitTest(request, result); 156 Node* node = result.innerPossiblyPseudoNode(); 157 while (node && node->nodeType() == Node::TEXT_NODE) 158 node = node->parentNode(); 159 return node; 160 } 161 162 static Node* hoveredNodeForEvent(Frame* frame, const PlatformGestureEvent& event, bool ignorePointerEventsNone) 163 { 164 return hoveredNodeForPoint(frame, event.position(), ignorePointerEventsNone); 165 } 166 167 static Node* hoveredNodeForEvent(Frame* frame, const PlatformMouseEvent& event, bool ignorePointerEventsNone) 168 { 169 return hoveredNodeForPoint(frame, event.position(), ignorePointerEventsNone); 170 } 171 172 static Node* hoveredNodeForEvent(Frame* frame, const PlatformTouchEvent& event, bool ignorePointerEventsNone) 173 { 174 const Vector<PlatformTouchPoint>& points = event.touchPoints(); 175 if (!points.size()) 176 return 0; 177 return hoveredNodeForPoint(frame, points[0].pos(), ignorePointerEventsNone); 178 } 179 180 class RevalidateStyleAttributeTask { 181 WTF_MAKE_FAST_ALLOCATED; 182 public: 183 RevalidateStyleAttributeTask(InspectorDOMAgent*); 184 void scheduleFor(Element*); 185 void reset() { m_timer.stop(); } 186 void onTimer(Timer<RevalidateStyleAttributeTask>*); 187 188 private: 189 InspectorDOMAgent* m_domAgent; 190 Timer<RevalidateStyleAttributeTask> m_timer; 191 HashSet<RefPtr<Element> > m_elements; 192 }; 193 194 RevalidateStyleAttributeTask::RevalidateStyleAttributeTask(InspectorDOMAgent* domAgent) 195 : m_domAgent(domAgent) 196 , m_timer(this, &RevalidateStyleAttributeTask::onTimer) 197 { 198 } 199 200 void RevalidateStyleAttributeTask::scheduleFor(Element* element) 201 { 202 m_elements.add(element); 203 if (!m_timer.isActive()) 204 m_timer.startOneShot(0); 205 } 206 207 void RevalidateStyleAttributeTask::onTimer(Timer<RevalidateStyleAttributeTask>*) 208 { 209 // The timer is stopped on m_domAgent destruction, so this method will never be called after m_domAgent has been destroyed. 210 Vector<Element*> elements; 211 for (HashSet<RefPtr<Element> >::iterator it = m_elements.begin(), end = m_elements.end(); it != end; ++it) 212 elements.append(it->get()); 213 m_domAgent->styleAttributeInvalidated(elements); 214 215 m_elements.clear(); 216 } 217 218 String InspectorDOMAgent::toErrorString(ExceptionState& exceptionState) 219 { 220 if (exceptionState.hadException()) 221 return DOMException::getErrorName(exceptionState.code()); 222 return ""; 223 } 224 225 InspectorDOMAgent::InspectorDOMAgent(InstrumentingAgents* instrumentingAgents, InspectorPageAgent* pageAgent, InspectorCompositeState* inspectorState, InjectedScriptManager* injectedScriptManager, InspectorOverlay* overlay, InspectorClient* client) 226 : InspectorBaseAgent<InspectorDOMAgent>("DOM", instrumentingAgents, inspectorState) 227 , m_pageAgent(pageAgent) 228 , m_injectedScriptManager(injectedScriptManager) 229 , m_overlay(overlay) 230 , m_client(client) 231 , m_frontend(0) 232 , m_domListener(0) 233 , m_lastNodeId(1) 234 , m_lastBackendNodeId(-1) 235 , m_searchingForNode(NotSearching) 236 , m_suppressAttributeModifiedEvent(false) 237 { 238 } 239 240 InspectorDOMAgent::~InspectorDOMAgent() 241 { 242 reset(); 243 ASSERT(m_searchingForNode == NotSearching); 244 } 245 246 void InspectorDOMAgent::setFrontend(InspectorFrontend* frontend) 247 { 248 ASSERT(!m_frontend); 249 m_history = adoptPtr(new InspectorHistory()); 250 m_domEditor = adoptPtr(new DOMEditor(m_history.get())); 251 252 m_frontend = frontend->dom(); 253 m_instrumentingAgents->setInspectorDOMAgent(this); 254 m_document = m_pageAgent->mainFrame()->document(); 255 } 256 257 void InspectorDOMAgent::clearFrontend() 258 { 259 ASSERT(m_frontend); 260 261 m_history.clear(); 262 m_domEditor.clear(); 263 264 ErrorString error; 265 setSearchingForNode(&error, NotSearching, 0); 266 hideHighlight(&error); 267 268 m_frontend = 0; 269 m_instrumentingAgents->setInspectorDOMAgent(0); 270 m_state->setBoolean(DOMAgentState::documentRequested, false); 271 reset(); 272 } 273 274 void InspectorDOMAgent::restore() 275 { 276 // Reset document to avoid early return from setDocument. 277 m_document = 0; 278 setDocument(m_pageAgent->mainFrame()->document()); 279 } 280 281 Vector<Document*> InspectorDOMAgent::documents() 282 { 283 Vector<Document*> result; 284 for (Frame* frame = m_document->frame(); frame; frame = frame->tree().traverseNext()) { 285 Document* document = frame->document(); 286 if (!document) 287 continue; 288 result.append(document); 289 } 290 return result; 291 } 292 293 void InspectorDOMAgent::reset() 294 { 295 discardFrontendBindings(); 296 discardBackendBindings(); 297 m_document = 0; 298 } 299 300 void InspectorDOMAgent::setDOMListener(DOMListener* listener) 301 { 302 m_domListener = listener; 303 } 304 305 void InspectorDOMAgent::setDocument(Document* doc) 306 { 307 if (doc == m_document.get()) 308 return; 309 310 reset(); 311 312 m_document = doc; 313 314 if (!m_state->getBoolean(DOMAgentState::documentRequested)) 315 return; 316 317 // Immediately communicate 0 document or document that has finished loading. 318 if (!doc || !doc->parsing()) 319 m_frontend->documentUpdated(); 320 } 321 322 void InspectorDOMAgent::releaseDanglingNodes() 323 { 324 m_danglingNodeToIdMaps.clear(); 325 } 326 327 int InspectorDOMAgent::bind(Node* node, NodeToIdMap* nodesMap) 328 { 329 int id = nodesMap->get(node); 330 if (id) 331 return id; 332 id = m_lastNodeId++; 333 nodesMap->set(node, id); 334 m_idToNode.set(id, node); 335 m_idToNodesMap.set(id, nodesMap); 336 return id; 337 } 338 339 void InspectorDOMAgent::unbind(Node* node, NodeToIdMap* nodesMap) 340 { 341 int id = nodesMap->get(node); 342 if (!id) 343 return; 344 345 m_idToNode.remove(id); 346 347 if (node->isFrameOwnerElement()) { 348 Document* contentDocument = toHTMLFrameOwnerElement(node)->contentDocument(); 349 if (m_domListener) 350 m_domListener->didRemoveDocument(contentDocument); 351 if (contentDocument) 352 unbind(contentDocument, nodesMap); 353 } 354 355 for (ShadowRoot* root = node->youngestShadowRoot(); root; root = root->olderShadowRoot()) 356 unbind(root, nodesMap); 357 358 if (node->isElementNode()) { 359 Element* element = toElement(node); 360 if (element->pseudoElement(BEFORE)) 361 unbind(element->pseudoElement(BEFORE), nodesMap); 362 if (element->pseudoElement(AFTER)) 363 unbind(element->pseudoElement(AFTER), nodesMap); 364 } 365 366 nodesMap->remove(node); 367 if (m_domListener) 368 m_domListener->didRemoveDOMNode(node); 369 370 bool childrenRequested = m_childrenRequested.contains(id); 371 if (childrenRequested) { 372 // Unbind subtree known to client recursively. 373 m_childrenRequested.remove(id); 374 Node* child = innerFirstChild(node); 375 while (child) { 376 unbind(child, nodesMap); 377 child = innerNextSibling(child); 378 } 379 } 380 } 381 382 Node* InspectorDOMAgent::assertNode(ErrorString* errorString, int nodeId) 383 { 384 Node* node = nodeForId(nodeId); 385 if (!node) { 386 *errorString = "Could not find node with given id"; 387 return 0; 388 } 389 return node; 390 } 391 392 Document* InspectorDOMAgent::assertDocument(ErrorString* errorString, int nodeId) 393 { 394 Node* node = assertNode(errorString, nodeId); 395 if (!node) 396 return 0; 397 398 if (!(node->isDocumentNode())) { 399 *errorString = "Document is not available"; 400 return 0; 401 } 402 return toDocument(node); 403 } 404 405 Element* InspectorDOMAgent::assertElement(ErrorString* errorString, int nodeId) 406 { 407 Node* node = assertNode(errorString, nodeId); 408 if (!node) 409 return 0; 410 411 if (node->nodeType() != Node::ELEMENT_NODE) { 412 *errorString = "Node is not an Element"; 413 return 0; 414 } 415 return toElement(node); 416 } 417 418 Node* InspectorDOMAgent::assertEditableNode(ErrorString* errorString, int nodeId) 419 { 420 Node* node = assertNode(errorString, nodeId); 421 if (!node) 422 return 0; 423 424 if (node->isInShadowTree()) { 425 *errorString = "Cannot edit nodes from shadow trees"; 426 return 0; 427 } 428 429 if (node->isPseudoElement()) { 430 *errorString = "Cannot edit pseudo elements"; 431 return 0; 432 } 433 434 return node; 435 } 436 437 Element* InspectorDOMAgent::assertEditableElement(ErrorString* errorString, int nodeId) 438 { 439 Element* element = assertElement(errorString, nodeId); 440 if (!element) 441 return 0; 442 443 if (element->isInShadowTree()) { 444 *errorString = "Cannot edit elements from shadow trees"; 445 return 0; 446 } 447 448 if (element->isPseudoElement()) { 449 *errorString = "Cannot edit pseudo elements"; 450 return 0; 451 } 452 453 return element; 454 } 455 456 void InspectorDOMAgent::getDocument(ErrorString* errorString, RefPtr<TypeBuilder::DOM::Node>& root) 457 { 458 m_state->setBoolean(DOMAgentState::documentRequested, true); 459 460 if (!m_document) { 461 *errorString = "Document is not available"; 462 return; 463 } 464 465 discardFrontendBindings(); 466 467 root = buildObjectForNode(m_document.get(), 2, &m_documentNodeToIdMap); 468 } 469 470 void InspectorDOMAgent::pushChildNodesToFrontend(int nodeId, int depth) 471 { 472 Node* node = nodeForId(nodeId); 473 if (!node || (node->nodeType() != Node::ELEMENT_NODE && node->nodeType() != Node::DOCUMENT_NODE && node->nodeType() != Node::DOCUMENT_FRAGMENT_NODE)) 474 return; 475 476 NodeToIdMap* nodeMap = m_idToNodesMap.get(nodeId); 477 478 if (m_childrenRequested.contains(nodeId)) { 479 if (depth <= 1) 480 return; 481 482 depth--; 483 484 for (node = innerFirstChild(node); node; node = innerNextSibling(node)) { 485 int childNodeId = nodeMap->get(node); 486 ASSERT(childNodeId); 487 pushChildNodesToFrontend(childNodeId, depth); 488 } 489 490 return; 491 } 492 493 RefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > children = buildArrayForContainerChildren(node, depth, nodeMap); 494 m_frontend->setChildNodes(nodeId, children.release()); 495 } 496 497 void InspectorDOMAgent::discardFrontendBindings() 498 { 499 if (m_history) 500 m_history->reset(); 501 m_searchResults.clear(); 502 m_documentNodeToIdMap.clear(); 503 m_idToNode.clear(); 504 releaseDanglingNodes(); 505 m_childrenRequested.clear(); 506 if (m_revalidateStyleAttrTask) 507 m_revalidateStyleAttrTask->reset(); 508 } 509 510 void InspectorDOMAgent::discardBackendBindings() 511 { 512 m_backendIdToNode.clear(); 513 m_nodeGroupToBackendIdMap.clear(); 514 } 515 516 int InspectorDOMAgent::pushNodeToFrontend(ErrorString* errorString, int documentNodeId, Node* nodeToPush) 517 { 518 Document* document = assertDocument(errorString, documentNodeId); 519 if (!document) 520 return 0; 521 if (nodeToPush->document() != document) { 522 *errorString = "Node is not part of the document with given id"; 523 return 0; 524 } 525 526 return pushNodePathToFrontend(nodeToPush); 527 } 528 529 Node* InspectorDOMAgent::nodeForId(int id) 530 { 531 if (!id) 532 return 0; 533 534 HashMap<int, Node*>::iterator it = m_idToNode.find(id); 535 if (it != m_idToNode.end()) 536 return it->value; 537 return 0; 538 } 539 540 void InspectorDOMAgent::requestChildNodes(ErrorString* errorString, int nodeId, const int* depth) 541 { 542 int sanitizedDepth; 543 544 if (!depth) 545 sanitizedDepth = 1; 546 else if (*depth == -1) 547 sanitizedDepth = INT_MAX; 548 else if (*depth > 0) 549 sanitizedDepth = *depth; 550 else { 551 *errorString = "Please provide a positive integer as a depth or -1 for entire subtree"; 552 return; 553 } 554 555 pushChildNodesToFrontend(nodeId, sanitizedDepth); 556 } 557 558 void InspectorDOMAgent::querySelector(ErrorString* errorString, int nodeId, const String& selectors, int* elementId) 559 { 560 *elementId = 0; 561 Node* node = assertNode(errorString, nodeId); 562 if (!node) 563 return; 564 565 TrackExceptionState exceptionState; 566 RefPtr<Element> element = node->querySelector(selectors, exceptionState); 567 if (exceptionState.hadException()) { 568 *errorString = "DOM Error while querying"; 569 return; 570 } 571 572 if (element) 573 *elementId = pushNodePathToFrontend(element.get()); 574 } 575 576 void InspectorDOMAgent::querySelectorAll(ErrorString* errorString, int nodeId, const String& selectors, RefPtr<TypeBuilder::Array<int> >& result) 577 { 578 Node* node = assertNode(errorString, nodeId); 579 if (!node) 580 return; 581 582 TrackExceptionState exceptionState; 583 RefPtr<NodeList> nodes = node->querySelectorAll(selectors, exceptionState); 584 if (exceptionState.hadException()) { 585 *errorString = "DOM Error while querying"; 586 return; 587 } 588 589 result = TypeBuilder::Array<int>::create(); 590 591 for (unsigned i = 0; i < nodes->length(); ++i) 592 result->addItem(pushNodePathToFrontend(nodes->item(i))); 593 } 594 595 int InspectorDOMAgent::pushNodePathToFrontend(Node* nodeToPush) 596 { 597 ASSERT(nodeToPush); // Invalid input 598 599 if (!m_document) 600 return 0; 601 if (!m_documentNodeToIdMap.contains(m_document)) 602 return 0; 603 604 // Return id in case the node is known. 605 int result = m_documentNodeToIdMap.get(nodeToPush); 606 if (result) 607 return result; 608 609 Node* node = nodeToPush; 610 Vector<Node*> path; 611 NodeToIdMap* danglingMap = 0; 612 613 while (true) { 614 Node* parent = innerParentNode(node); 615 if (!parent) { 616 // Node being pushed is detached -> push subtree root. 617 OwnPtr<NodeToIdMap> newMap = adoptPtr(new NodeToIdMap); 618 danglingMap = newMap.get(); 619 m_danglingNodeToIdMaps.append(newMap.release()); 620 RefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > children = TypeBuilder::Array<TypeBuilder::DOM::Node>::create(); 621 children->addItem(buildObjectForNode(node, 0, danglingMap)); 622 m_frontend->setChildNodes(0, children); 623 break; 624 } else { 625 path.append(parent); 626 if (m_documentNodeToIdMap.get(parent)) 627 break; 628 else 629 node = parent; 630 } 631 } 632 633 NodeToIdMap* map = danglingMap ? danglingMap : &m_documentNodeToIdMap; 634 for (int i = path.size() - 1; i >= 0; --i) { 635 int nodeId = map->get(path.at(i)); 636 ASSERT(nodeId); 637 pushChildNodesToFrontend(nodeId); 638 } 639 return map->get(nodeToPush); 640 } 641 642 int InspectorDOMAgent::boundNodeId(Node* node) 643 { 644 return m_documentNodeToIdMap.get(node); 645 } 646 647 BackendNodeId InspectorDOMAgent::backendNodeIdForNode(Node* node, const String& nodeGroup) 648 { 649 if (!node) 650 return 0; 651 652 if (!m_nodeGroupToBackendIdMap.contains(nodeGroup)) 653 m_nodeGroupToBackendIdMap.set(nodeGroup, NodeToBackendIdMap()); 654 655 NodeToBackendIdMap& map = m_nodeGroupToBackendIdMap.find(nodeGroup)->value; 656 BackendNodeId id = map.get(node); 657 if (!id) { 658 id = --m_lastBackendNodeId; 659 map.set(node, id); 660 m_backendIdToNode.set(id, std::make_pair(node, nodeGroup)); 661 } 662 663 return id; 664 } 665 666 void InspectorDOMAgent::releaseBackendNodeIds(ErrorString* errorString, const String& nodeGroup) 667 { 668 if (m_nodeGroupToBackendIdMap.contains(nodeGroup)) { 669 NodeToBackendIdMap& map = m_nodeGroupToBackendIdMap.find(nodeGroup)->value; 670 for (NodeToBackendIdMap::iterator it = map.begin(); it != map.end(); ++it) 671 m_backendIdToNode.remove(it->value); 672 m_nodeGroupToBackendIdMap.remove(nodeGroup); 673 return; 674 } 675 *errorString = "Group name not found"; 676 } 677 678 void InspectorDOMAgent::setAttributeValue(ErrorString* errorString, int elementId, const String& name, const String& value) 679 { 680 Element* element = assertEditableElement(errorString, elementId); 681 if (!element) 682 return; 683 684 m_domEditor->setAttribute(element, name, value, errorString); 685 } 686 687 void InspectorDOMAgent::setAttributesAsText(ErrorString* errorString, int elementId, const String& text, const String* const name) 688 { 689 Element* element = assertEditableElement(errorString, elementId); 690 if (!element) 691 return; 692 693 String markup = "<span " + text + "></span>"; 694 RefPtr<DocumentFragment> fragment = element->document().createDocumentFragment(); 695 696 bool shouldIgnoreCase = element->document().isHTMLDocument() && element->isHTMLElement(); 697 // Not all elements can represent the context (i.e. IFRAME), hence using document.body. 698 if (shouldIgnoreCase && element->document().body()) 699 fragment->parseHTML(markup, element->document().body(), DisallowScriptingContent); 700 else 701 fragment->parseXML(markup, 0, DisallowScriptingContent); 702 703 Element* parsedElement = fragment->firstChild() && fragment->firstChild()->isElementNode() ? toElement(fragment->firstChild()) : 0; 704 if (!parsedElement) { 705 *errorString = "Could not parse value as attributes"; 706 return; 707 } 708 709 String caseAdjustedName = name ? (shouldIgnoreCase ? name->lower() : *name) : String(); 710 711 if (!parsedElement->hasAttributes() && name) { 712 m_domEditor->removeAttribute(element, caseAdjustedName, errorString); 713 return; 714 } 715 716 bool foundOriginalAttribute = false; 717 unsigned numAttrs = parsedElement->attributeCount(); 718 for (unsigned i = 0; i < numAttrs; ++i) { 719 // Add attribute pair 720 const Attribute* attribute = parsedElement->attributeItem(i); 721 String attributeName = attribute->name().toString(); 722 if (shouldIgnoreCase) 723 attributeName = attributeName.lower(); 724 foundOriginalAttribute |= name && attributeName == caseAdjustedName; 725 if (!m_domEditor->setAttribute(element, attributeName, attribute->value(), errorString)) 726 return; 727 } 728 729 if (!foundOriginalAttribute && name && !name->stripWhiteSpace().isEmpty()) 730 m_domEditor->removeAttribute(element, caseAdjustedName, errorString); 731 } 732 733 void InspectorDOMAgent::removeAttribute(ErrorString* errorString, int elementId, const String& name) 734 { 735 Element* element = assertEditableElement(errorString, elementId); 736 if (!element) 737 return; 738 739 m_domEditor->removeAttribute(element, name, errorString); 740 } 741 742 void InspectorDOMAgent::removeNode(ErrorString* errorString, int nodeId) 743 { 744 Node* node = assertEditableNode(errorString, nodeId); 745 if (!node) 746 return; 747 748 ContainerNode* parentNode = node->parentNode(); 749 if (!parentNode) { 750 *errorString = "Cannot remove detached node"; 751 return; 752 } 753 754 m_domEditor->removeChild(parentNode, node, errorString); 755 } 756 757 void InspectorDOMAgent::setNodeName(ErrorString* errorString, int nodeId, const String& tagName, int* newId) 758 { 759 *newId = 0; 760 761 Node* oldNode = nodeForId(nodeId); 762 if (!oldNode || !oldNode->isElementNode()) 763 return; 764 765 TrackExceptionState exceptionState; 766 RefPtr<Element> newElem = oldNode->document().createElement(tagName, exceptionState); 767 if (exceptionState.hadException()) 768 return; 769 770 // Copy over the original node's attributes. 771 newElem->cloneAttributesFromElement(*toElement(oldNode)); 772 773 // Copy over the original node's children. 774 Node* child; 775 while ((child = oldNode->firstChild())) { 776 if (!m_domEditor->insertBefore(newElem.get(), child, 0, errorString)) 777 return; 778 } 779 780 // Replace the old node with the new node 781 ContainerNode* parent = oldNode->parentNode(); 782 if (!m_domEditor->insertBefore(parent, newElem.get(), oldNode->nextSibling(), errorString)) 783 return; 784 if (!m_domEditor->removeChild(parent, oldNode, errorString)) 785 return; 786 787 *newId = pushNodePathToFrontend(newElem.get()); 788 if (m_childrenRequested.contains(nodeId)) 789 pushChildNodesToFrontend(*newId); 790 } 791 792 void InspectorDOMAgent::getOuterHTML(ErrorString* errorString, int nodeId, WTF::String* outerHTML) 793 { 794 Node* node = assertNode(errorString, nodeId); 795 if (!node) 796 return; 797 798 *outerHTML = createMarkup(node); 799 } 800 801 void InspectorDOMAgent::setOuterHTML(ErrorString* errorString, int nodeId, const String& outerHTML) 802 { 803 if (!nodeId) { 804 ASSERT(m_document); 805 DOMPatchSupport domPatchSupport(m_domEditor.get(), *m_document.get()); 806 domPatchSupport.patchDocument(outerHTML); 807 return; 808 } 809 810 Node* node = assertEditableNode(errorString, nodeId); 811 if (!node) 812 return; 813 814 Document* document = node->isDocumentNode() ? toDocument(node) : node->ownerDocument(); 815 if (!document || (!document->isHTMLDocument() && !document->isXHTMLDocument() && !document->isSVGDocument())) { 816 *errorString = "Not an HTML/XML document"; 817 return; 818 } 819 820 Node* newNode = 0; 821 if (!m_domEditor->setOuterHTML(node, outerHTML, &newNode, errorString)) 822 return; 823 824 if (!newNode) { 825 // The only child node has been deleted. 826 return; 827 } 828 829 int newId = pushNodePathToFrontend(newNode); 830 831 bool childrenRequested = m_childrenRequested.contains(nodeId); 832 if (childrenRequested) 833 pushChildNodesToFrontend(newId); 834 } 835 836 void InspectorDOMAgent::setNodeValue(ErrorString* errorString, int nodeId, const String& value) 837 { 838 Node* node = assertEditableNode(errorString, nodeId); 839 if (!node) 840 return; 841 842 if (node->nodeType() != Node::TEXT_NODE) { 843 *errorString = "Can only set value of text nodes"; 844 return; 845 } 846 847 m_domEditor->replaceWholeText(toText(node), value, errorString); 848 } 849 850 void InspectorDOMAgent::getEventListenersForNode(ErrorString* errorString, int nodeId, const String* objectGroup, RefPtr<TypeBuilder::Array<TypeBuilder::DOM::EventListener> >& listenersArray) 851 { 852 listenersArray = TypeBuilder::Array<TypeBuilder::DOM::EventListener>::create(); 853 Node* node = assertNode(errorString, nodeId); 854 if (!node) 855 return; 856 Vector<EventListenerInfo> eventInformation; 857 getEventListeners(node, eventInformation, true); 858 859 // Get Capturing Listeners (in this order) 860 size_t eventInformationLength = eventInformation.size(); 861 for (size_t i = 0; i < eventInformationLength; ++i) { 862 const EventListenerInfo& info = eventInformation[i]; 863 const EventListenerVector& vector = info.eventListenerVector; 864 for (size_t j = 0; j < vector.size(); ++j) { 865 const RegisteredEventListener& listener = vector[j]; 866 if (listener.useCapture) 867 listenersArray->addItem(buildObjectForEventListener(listener, info.eventType, info.node, objectGroup)); 868 } 869 } 870 871 // Get Bubbling Listeners (reverse order) 872 for (size_t i = eventInformationLength; i; --i) { 873 const EventListenerInfo& info = eventInformation[i - 1]; 874 const EventListenerVector& vector = info.eventListenerVector; 875 for (size_t j = 0; j < vector.size(); ++j) { 876 const RegisteredEventListener& listener = vector[j]; 877 if (!listener.useCapture) 878 listenersArray->addItem(buildObjectForEventListener(listener, info.eventType, info.node, objectGroup)); 879 } 880 } 881 } 882 883 void InspectorDOMAgent::getEventListeners(Node* node, Vector<EventListenerInfo>& eventInformation, bool includeAncestors) 884 { 885 // The Node's Ancestors including self. 886 Vector<Node*> ancestors; 887 // Push this node as the firs element. 888 ancestors.append(node); 889 if (includeAncestors) { 890 for (ContainerNode* ancestor = node->parentOrShadowHostNode(); ancestor; ancestor = ancestor->parentOrShadowHostNode()) 891 ancestors.append(ancestor); 892 } 893 894 // Nodes and their Listeners for the concerned event types (order is top to bottom) 895 for (size_t i = ancestors.size(); i; --i) { 896 Node* ancestor = ancestors[i - 1]; 897 EventTargetData* d = ancestor->eventTargetData(); 898 if (!d) 899 continue; 900 // Get the list of event types this Node is concerned with 901 Vector<AtomicString> eventTypes = d->eventListenerMap.eventTypes(); 902 for (size_t j = 0; j < eventTypes.size(); ++j) { 903 AtomicString& type = eventTypes[j]; 904 const EventListenerVector& listeners = ancestor->getEventListeners(type); 905 EventListenerVector filteredListeners; 906 filteredListeners.reserveCapacity(listeners.size()); 907 for (size_t k = 0; k < listeners.size(); ++k) { 908 if (listeners[k].listener->type() == EventListener::JSEventListenerType) 909 filteredListeners.append(listeners[k]); 910 } 911 if (!filteredListeners.isEmpty()) 912 eventInformation.append(EventListenerInfo(ancestor, type, filteredListeners)); 913 } 914 } 915 } 916 917 void InspectorDOMAgent::performSearch(ErrorString*, const String& whitespaceTrimmedQuery, String* searchId, int* resultCount) 918 { 919 // FIXME: Few things are missing here: 920 // 1) Search works with node granularity - number of matches within node is not calculated. 921 // 2) There is no need to push all search results to the front-end at a time, pushing next / previous result 922 // is sufficient. 923 924 unsigned queryLength = whitespaceTrimmedQuery.length(); 925 bool startTagFound = !whitespaceTrimmedQuery.find('<'); 926 bool endTagFound = whitespaceTrimmedQuery.reverseFind('>') + 1 == queryLength; 927 bool startQuoteFound = !whitespaceTrimmedQuery.find('"'); 928 bool endQuoteFound = whitespaceTrimmedQuery.reverseFind('"') + 1 == queryLength; 929 bool exactAttributeMatch = startQuoteFound && endQuoteFound; 930 931 String tagNameQuery = whitespaceTrimmedQuery; 932 String attributeQuery = whitespaceTrimmedQuery; 933 if (startTagFound) 934 tagNameQuery = tagNameQuery.right(tagNameQuery.length() - 1); 935 if (endTagFound) 936 tagNameQuery = tagNameQuery.left(tagNameQuery.length() - 1); 937 if (startQuoteFound) 938 attributeQuery = attributeQuery.right(attributeQuery.length() - 1); 939 if (endQuoteFound) 940 attributeQuery = attributeQuery.left(attributeQuery.length() - 1); 941 942 Vector<Document*> docs = documents(); 943 ListHashSet<Node*> resultCollector; 944 945 for (Vector<Document*>::iterator it = docs.begin(); it != docs.end(); ++it) { 946 Document* document = *it; 947 Node* node = document->documentElement(); 948 if (!node) 949 continue; 950 951 // Manual plain text search. 952 while ((node = NodeTraversal::next(*node, document->documentElement()))) { 953 switch (node->nodeType()) { 954 case Node::TEXT_NODE: 955 case Node::COMMENT_NODE: 956 case Node::CDATA_SECTION_NODE: { 957 String text = node->nodeValue(); 958 if (text.findIgnoringCase(whitespaceTrimmedQuery) != kNotFound) 959 resultCollector.add(node); 960 break; 961 } 962 case Node::ELEMENT_NODE: { 963 if ((!startTagFound && !endTagFound && (node->nodeName().findIgnoringCase(tagNameQuery) != kNotFound)) 964 || (startTagFound && endTagFound && equalIgnoringCase(node->nodeName(), tagNameQuery)) 965 || (startTagFound && !endTagFound && node->nodeName().startsWith(tagNameQuery, false)) 966 || (!startTagFound && endTagFound && node->nodeName().endsWith(tagNameQuery, false))) { 967 resultCollector.add(node); 968 break; 969 } 970 // Go through all attributes and serialize them. 971 const Element* element = toElement(node); 972 if (!element->hasAttributes()) 973 break; 974 975 unsigned numAttrs = element->attributeCount(); 976 for (unsigned i = 0; i < numAttrs; ++i) { 977 // Add attribute pair 978 const Attribute* attribute = element->attributeItem(i); 979 if (attribute->localName().find(whitespaceTrimmedQuery, 0, false) != kNotFound) { 980 resultCollector.add(node); 981 break; 982 } 983 size_t foundPosition = attribute->value().find(attributeQuery, 0, false); 984 if (foundPosition != kNotFound) { 985 if (!exactAttributeMatch || (!foundPosition && attribute->value().length() == attributeQuery.length())) { 986 resultCollector.add(node); 987 break; 988 } 989 } 990 } 991 break; 992 } 993 default: 994 break; 995 } 996 } 997 998 // XPath evaluation 999 for (Vector<Document*>::iterator it = docs.begin(); it != docs.end(); ++it) { 1000 Document* document = *it; 1001 TrackExceptionState exceptionState; 1002 RefPtr<XPathResult> result = DocumentXPathEvaluator::evaluate(document, whitespaceTrimmedQuery, document, 0, XPathResult::ORDERED_NODE_SNAPSHOT_TYPE, 0, exceptionState); 1003 if (exceptionState.hadException() || !result) 1004 continue; 1005 1006 unsigned long size = result->snapshotLength(exceptionState); 1007 for (unsigned long i = 0; !exceptionState.hadException() && i < size; ++i) { 1008 Node* node = result->snapshotItem(i, exceptionState); 1009 if (exceptionState.hadException()) 1010 break; 1011 1012 if (node->nodeType() == Node::ATTRIBUTE_NODE) 1013 node = toAttr(node)->ownerElement(); 1014 resultCollector.add(node); 1015 } 1016 } 1017 1018 // Selector evaluation 1019 for (Vector<Document*>::iterator it = docs.begin(); it != docs.end(); ++it) { 1020 Document* document = *it; 1021 TrackExceptionState exceptionState; 1022 RefPtr<NodeList> nodeList = document->querySelectorAll(whitespaceTrimmedQuery, exceptionState); 1023 if (exceptionState.hadException() || !nodeList) 1024 continue; 1025 1026 unsigned size = nodeList->length(); 1027 for (unsigned i = 0; i < size; ++i) 1028 resultCollector.add(nodeList->item(i)); 1029 } 1030 } 1031 1032 *searchId = IdentifiersFactory::createIdentifier(); 1033 SearchResults::iterator resultsIt = m_searchResults.add(*searchId, Vector<RefPtr<Node> >()).iterator; 1034 1035 for (ListHashSet<Node*>::iterator it = resultCollector.begin(); it != resultCollector.end(); ++it) 1036 resultsIt->value.append(*it); 1037 1038 *resultCount = resultsIt->value.size(); 1039 } 1040 1041 void InspectorDOMAgent::getSearchResults(ErrorString* errorString, const String& searchId, int fromIndex, int toIndex, RefPtr<TypeBuilder::Array<int> >& nodeIds) 1042 { 1043 SearchResults::iterator it = m_searchResults.find(searchId); 1044 if (it == m_searchResults.end()) { 1045 *errorString = "No search session with given id found"; 1046 return; 1047 } 1048 1049 int size = it->value.size(); 1050 if (fromIndex < 0 || toIndex > size || fromIndex >= toIndex) { 1051 *errorString = "Invalid search result range"; 1052 return; 1053 } 1054 1055 nodeIds = TypeBuilder::Array<int>::create(); 1056 for (int i = fromIndex; i < toIndex; ++i) 1057 nodeIds->addItem(pushNodePathToFrontend((it->value)[i].get())); 1058 } 1059 1060 void InspectorDOMAgent::discardSearchResults(ErrorString*, const String& searchId) 1061 { 1062 m_searchResults.remove(searchId); 1063 } 1064 1065 bool InspectorDOMAgent::handleMousePress() 1066 { 1067 if (m_searchingForNode == NotSearching) 1068 return false; 1069 1070 if (Node* node = m_overlay->highlightedNode()) { 1071 inspect(node); 1072 return true; 1073 } 1074 return false; 1075 } 1076 1077 bool InspectorDOMAgent::handleGestureEvent(Frame* frame, const PlatformGestureEvent& event) 1078 { 1079 if (m_searchingForNode == NotSearching || event.type() != PlatformEvent::GestureTap) 1080 return false; 1081 Node* node = hoveredNodeForEvent(frame, event, false); 1082 if (node && m_inspectModeHighlightConfig) { 1083 m_overlay->highlightNode(node, 0 /* eventTarget */, *m_inspectModeHighlightConfig); 1084 inspect(node); 1085 return true; 1086 } 1087 return false; 1088 } 1089 1090 bool InspectorDOMAgent::handleTouchEvent(Frame* frame, const PlatformTouchEvent& event) 1091 { 1092 if (m_searchingForNode == NotSearching) 1093 return false; 1094 Node* node = hoveredNodeForEvent(frame, event, false); 1095 if (node && m_inspectModeHighlightConfig) { 1096 m_overlay->highlightNode(node, 0 /* eventTarget */, *m_inspectModeHighlightConfig); 1097 inspect(node); 1098 return true; 1099 } 1100 return false; 1101 } 1102 1103 void InspectorDOMAgent::inspect(Node* inspectedNode) 1104 { 1105 if (!m_frontend || !inspectedNode) 1106 return; 1107 1108 Node* node = inspectedNode; 1109 if (node->nodeType() != Node::ELEMENT_NODE && node->nodeType() != Node::DOCUMENT_NODE) 1110 node = node->parentNode(); 1111 1112 int nodeId = pushNodePathToFrontend(node); 1113 if (nodeId) 1114 m_frontend->inspectNodeRequested(nodeId); 1115 } 1116 1117 void InspectorDOMAgent::handleMouseMove(Frame* frame, const PlatformMouseEvent& event) 1118 { 1119 if (m_searchingForNode == NotSearching) 1120 return; 1121 1122 if (!frame->view() || !frame->contentRenderer()) 1123 return; 1124 Node* node = hoveredNodeForEvent(frame, event, event.shiftKey()); 1125 1126 while (m_searchingForNode != SearchingForShadow && node && node->isInShadowTree()) 1127 node = node->parentOrShadowHostNode(); 1128 1129 Node* eventTarget = event.shiftKey() ? hoveredNodeForEvent(frame, event, false) : 0; 1130 if (eventTarget == node) 1131 eventTarget = 0; 1132 1133 if (node && m_inspectModeHighlightConfig) 1134 m_overlay->highlightNode(node, eventTarget, *m_inspectModeHighlightConfig); 1135 } 1136 1137 void InspectorDOMAgent::setSearchingForNode(ErrorString* errorString, SearchMode searchMode, JSONObject* highlightInspectorObject) 1138 { 1139 if (m_searchingForNode == searchMode) 1140 return; 1141 1142 m_searchingForNode = searchMode; 1143 m_overlay->setInspectModeEnabled(searchMode != NotSearching); 1144 if (searchMode != NotSearching) { 1145 m_inspectModeHighlightConfig = highlightConfigFromInspectorObject(errorString, highlightInspectorObject); 1146 if (!m_inspectModeHighlightConfig) 1147 return; 1148 } else 1149 hideHighlight(errorString); 1150 } 1151 1152 PassOwnPtr<HighlightConfig> InspectorDOMAgent::highlightConfigFromInspectorObject(ErrorString* errorString, JSONObject* highlightInspectorObject) 1153 { 1154 if (!highlightInspectorObject) { 1155 *errorString = "Internal error: highlight configuration parameter is missing"; 1156 return nullptr; 1157 } 1158 1159 OwnPtr<HighlightConfig> highlightConfig = adoptPtr(new HighlightConfig()); 1160 bool showInfo = false; // Default: false (do not show a tooltip). 1161 highlightInspectorObject->getBoolean("showInfo", &showInfo); 1162 highlightConfig->showInfo = showInfo; 1163 bool showRulers = false; // Default: false (do not show rulers). 1164 highlightInspectorObject->getBoolean("showRulers", &showRulers); 1165 highlightConfig->showRulers = showRulers; 1166 highlightConfig->content = parseConfigColor("contentColor", highlightInspectorObject); 1167 highlightConfig->contentOutline = parseConfigColor("contentOutlineColor", highlightInspectorObject); 1168 highlightConfig->padding = parseConfigColor("paddingColor", highlightInspectorObject); 1169 highlightConfig->border = parseConfigColor("borderColor", highlightInspectorObject); 1170 highlightConfig->margin = parseConfigColor("marginColor", highlightInspectorObject); 1171 highlightConfig->eventTarget = parseConfigColor("eventTargetColor", highlightInspectorObject); 1172 return highlightConfig.release(); 1173 } 1174 1175 void InspectorDOMAgent::setInspectModeEnabled(ErrorString* errorString, bool enabled, const bool* inspectShadowDOM, const RefPtr<JSONObject>* highlightConfig) 1176 { 1177 if (enabled && !pushDocumentUponHandlelessOperation(errorString)) 1178 return; 1179 SearchMode searchMode = enabled ? (inspectShadowDOM && *inspectShadowDOM ? SearchingForShadow : SearchingForNormal) : NotSearching; 1180 setSearchingForNode(errorString, searchMode, highlightConfig ? highlightConfig->get() : 0); 1181 } 1182 1183 void InspectorDOMAgent::highlightRect(ErrorString*, int x, int y, int width, int height, const RefPtr<JSONObject>* color, const RefPtr<JSONObject>* outlineColor) 1184 { 1185 OwnPtr<FloatQuad> quad = adoptPtr(new FloatQuad(FloatRect(x, y, width, height))); 1186 innerHighlightQuad(quad.release(), color, outlineColor); 1187 } 1188 1189 void InspectorDOMAgent::highlightQuad(ErrorString* errorString, const RefPtr<JSONArray>& quadArray, const RefPtr<JSONObject>* color, const RefPtr<JSONObject>* outlineColor) 1190 { 1191 OwnPtr<FloatQuad> quad = adoptPtr(new FloatQuad()); 1192 if (!parseQuad(quadArray, quad.get())) { 1193 *errorString = "Invalid Quad format"; 1194 return; 1195 } 1196 innerHighlightQuad(quad.release(), color, outlineColor); 1197 } 1198 1199 void InspectorDOMAgent::innerHighlightQuad(PassOwnPtr<FloatQuad> quad, const RefPtr<JSONObject>* color, const RefPtr<JSONObject>* outlineColor) 1200 { 1201 OwnPtr<HighlightConfig> highlightConfig = adoptPtr(new HighlightConfig()); 1202 highlightConfig->content = parseColor(color); 1203 highlightConfig->contentOutline = parseColor(outlineColor); 1204 m_overlay->highlightQuad(quad, *highlightConfig); 1205 } 1206 1207 void InspectorDOMAgent::highlightNode(ErrorString* errorString, const RefPtr<JSONObject>& highlightInspectorObject, const int* nodeId, const String* objectId) 1208 { 1209 Node* node = 0; 1210 if (nodeId) { 1211 node = assertNode(errorString, *nodeId); 1212 } else if (objectId) { 1213 InjectedScript injectedScript = m_injectedScriptManager->injectedScriptForObjectId(*objectId); 1214 node = injectedScript.nodeForObjectId(*objectId); 1215 if (!node) 1216 *errorString = "Node for given objectId not found"; 1217 } else 1218 *errorString = "Either nodeId or objectId must be specified"; 1219 1220 if (!node) 1221 return; 1222 1223 OwnPtr<HighlightConfig> highlightConfig = highlightConfigFromInspectorObject(errorString, highlightInspectorObject.get()); 1224 if (!highlightConfig) 1225 return; 1226 1227 m_overlay->highlightNode(node, 0 /* eventTarget */, *highlightConfig); 1228 } 1229 1230 void InspectorDOMAgent::highlightFrame( 1231 ErrorString*, 1232 const String& frameId, 1233 const RefPtr<JSONObject>* color, 1234 const RefPtr<JSONObject>* outlineColor) 1235 { 1236 Frame* frame = m_pageAgent->frameForId(frameId); 1237 if (frame && frame->ownerElement()) { 1238 OwnPtr<HighlightConfig> highlightConfig = adoptPtr(new HighlightConfig()); 1239 highlightConfig->showInfo = true; // Always show tooltips for frames. 1240 highlightConfig->content = parseColor(color); 1241 highlightConfig->contentOutline = parseColor(outlineColor); 1242 m_overlay->highlightNode(frame->ownerElement(), 0 /* eventTarget */, *highlightConfig); 1243 } 1244 } 1245 1246 void InspectorDOMAgent::hideHighlight(ErrorString*) 1247 { 1248 m_overlay->hideHighlight(); 1249 } 1250 1251 void InspectorDOMAgent::moveTo(ErrorString* errorString, int nodeId, int targetElementId, const int* const anchorNodeId, int* newNodeId) 1252 { 1253 Node* node = assertEditableNode(errorString, nodeId); 1254 if (!node) 1255 return; 1256 1257 Element* targetElement = assertEditableElement(errorString, targetElementId); 1258 if (!targetElement) 1259 return; 1260 1261 Node* anchorNode = 0; 1262 if (anchorNodeId && *anchorNodeId) { 1263 anchorNode = assertEditableNode(errorString, *anchorNodeId); 1264 if (!anchorNode) 1265 return; 1266 if (anchorNode->parentNode() != targetElement) { 1267 *errorString = "Anchor node must be child of the target element"; 1268 return; 1269 } 1270 } 1271 1272 if (!m_domEditor->insertBefore(targetElement, node, anchorNode, errorString)) 1273 return; 1274 1275 *newNodeId = pushNodePathToFrontend(node); 1276 } 1277 1278 void InspectorDOMAgent::undo(ErrorString* errorString) 1279 { 1280 TrackExceptionState exceptionState; 1281 m_history->undo(exceptionState); 1282 *errorString = InspectorDOMAgent::toErrorString(exceptionState); 1283 } 1284 1285 void InspectorDOMAgent::redo(ErrorString* errorString) 1286 { 1287 TrackExceptionState exceptionState; 1288 m_history->redo(exceptionState); 1289 *errorString = InspectorDOMAgent::toErrorString(exceptionState); 1290 } 1291 1292 void InspectorDOMAgent::markUndoableState(ErrorString*) 1293 { 1294 m_history->markUndoableState(); 1295 } 1296 1297 void InspectorDOMAgent::focus(ErrorString* errorString, int nodeId) 1298 { 1299 Element* element = assertElement(errorString, nodeId); 1300 if (!element) 1301 return; 1302 if (!element->isFocusable()) { 1303 *errorString = "Element is not focusable"; 1304 return; 1305 } 1306 element->focus(); 1307 } 1308 1309 void InspectorDOMAgent::setFileInputFiles(ErrorString* errorString, int nodeId, const RefPtr<JSONArray>& files) 1310 { 1311 Node* node = assertNode(errorString, nodeId); 1312 if (!node) 1313 return; 1314 if (!node->hasTagName(inputTag) || !toHTMLInputElement(node)->isFileUpload()) { 1315 *errorString = "Node is not a file input element"; 1316 return; 1317 } 1318 1319 RefPtr<FileList> fileList = FileList::create(); 1320 for (JSONArray::const_iterator iter = files->begin(); iter != files->end(); ++iter) { 1321 String path; 1322 if (!(*iter)->asString(&path)) { 1323 *errorString = "Files must be strings"; 1324 return; 1325 } 1326 fileList->append(File::create(path)); 1327 } 1328 toHTMLInputElement(node)->setFiles(fileList); 1329 } 1330 1331 static RefPtr<TypeBuilder::Array<double> > buildArrayForQuad(const FloatQuad& quad) 1332 { 1333 RefPtr<TypeBuilder::Array<double> > array = TypeBuilder::Array<double>::create(); 1334 array->addItem(quad.p1().x()); 1335 array->addItem(quad.p1().y()); 1336 array->addItem(quad.p2().x()); 1337 array->addItem(quad.p2().y()); 1338 array->addItem(quad.p3().x()); 1339 array->addItem(quad.p3().y()); 1340 array->addItem(quad.p4().x()); 1341 array->addItem(quad.p4().y()); 1342 return array.release(); 1343 } 1344 1345 void InspectorDOMAgent::getBoxModel(ErrorString* errorString, int nodeId, RefPtr<TypeBuilder::DOM::BoxModel>& model) 1346 { 1347 Node* node = assertNode(errorString, nodeId); 1348 if (!node) 1349 return; 1350 1351 Vector<FloatQuad> quads; 1352 bool isInlineOrBox = m_overlay->getBoxModel(node, &quads); 1353 if (!isInlineOrBox) { 1354 *errorString = "Could not compute box model."; 1355 return; 1356 } 1357 1358 RenderObject* renderer = node->renderer(); 1359 Frame* frame = node->document().frame(); 1360 FrameView* view = frame->view(); 1361 1362 IntRect boundingBox = pixelSnappedIntRect(view->contentsToRootView(renderer->absoluteBoundingBoxRect())); 1363 RenderBoxModelObject* modelObject = renderer->isBoxModelObject() ? toRenderBoxModelObject(renderer) : 0; 1364 1365 model = TypeBuilder::DOM::BoxModel::create() 1366 .setContent(buildArrayForQuad(quads.at(3))) 1367 .setPadding(buildArrayForQuad(quads.at(2))) 1368 .setBorder(buildArrayForQuad(quads.at(1))) 1369 .setMargin(buildArrayForQuad(quads.at(0))) 1370 .setWidth(modelObject ? adjustForAbsoluteZoom(modelObject->pixelSnappedOffsetWidth(), modelObject) : boundingBox.width()) 1371 .setHeight(modelObject ? adjustForAbsoluteZoom(modelObject->pixelSnappedOffsetHeight(), modelObject) : boundingBox.height()); 1372 } 1373 1374 void InspectorDOMAgent::getNodeForLocation(ErrorString* errorString, int x, int y, int* nodeId) 1375 { 1376 if (!pushDocumentUponHandlelessOperation(errorString)) 1377 return; 1378 1379 Node* node = hoveredNodeForPoint(m_document->frame(), IntPoint(x, y), false); 1380 if (!node) { 1381 *errorString = "No node found at given location"; 1382 return; 1383 } 1384 *nodeId = pushNodePathToFrontend(node); 1385 } 1386 1387 void InspectorDOMAgent::resolveNode(ErrorString* errorString, int nodeId, const String* const objectGroup, RefPtr<TypeBuilder::Runtime::RemoteObject>& result) 1388 { 1389 String objectGroupName = objectGroup ? *objectGroup : ""; 1390 Node* node = nodeForId(nodeId); 1391 if (!node) { 1392 *errorString = "No node with given id found"; 1393 return; 1394 } 1395 RefPtr<TypeBuilder::Runtime::RemoteObject> object = resolveNode(node, objectGroupName); 1396 if (!object) { 1397 *errorString = "Node with given id does not belong to the document"; 1398 return; 1399 } 1400 result = object; 1401 } 1402 1403 void InspectorDOMAgent::getAttributes(ErrorString* errorString, int nodeId, RefPtr<TypeBuilder::Array<String> >& result) 1404 { 1405 Element* element = assertElement(errorString, nodeId); 1406 if (!element) 1407 return; 1408 1409 result = buildArrayForElementAttributes(element); 1410 } 1411 1412 void InspectorDOMAgent::requestNode(ErrorString*, const String& objectId, int* nodeId) 1413 { 1414 InjectedScript injectedScript = m_injectedScriptManager->injectedScriptForObjectId(objectId); 1415 Node* node = injectedScript.nodeForObjectId(objectId); 1416 if (node) 1417 *nodeId = pushNodePathToFrontend(node); 1418 else 1419 *nodeId = 0; 1420 } 1421 1422 // static 1423 String InspectorDOMAgent::documentURLString(Document* document) 1424 { 1425 if (!document || document->url().isNull()) 1426 return ""; 1427 return document->url().string(); 1428 } 1429 1430 static String documentBaseURLString(Document* document) 1431 { 1432 return document->completeURL("").string(); 1433 } 1434 1435 static TypeBuilder::DOM::ShadowRootType::Enum shadowRootType(ShadowRoot* shadowRoot) 1436 { 1437 switch (shadowRoot->type()) { 1438 case ShadowRoot::UserAgentShadowRoot: 1439 return TypeBuilder::DOM::ShadowRootType::User_agent; 1440 case ShadowRoot::AuthorShadowRoot: 1441 return TypeBuilder::DOM::ShadowRootType::Author; 1442 } 1443 ASSERT_NOT_REACHED(); 1444 return TypeBuilder::DOM::ShadowRootType::User_agent; 1445 } 1446 1447 PassRefPtr<TypeBuilder::DOM::Node> InspectorDOMAgent::buildObjectForNode(Node* node, int depth, NodeToIdMap* nodesMap) 1448 { 1449 int id = bind(node, nodesMap); 1450 String nodeName; 1451 String localName; 1452 String nodeValue; 1453 1454 switch (node->nodeType()) { 1455 case Node::TEXT_NODE: 1456 case Node::COMMENT_NODE: 1457 case Node::CDATA_SECTION_NODE: 1458 nodeValue = node->nodeValue(); 1459 if (nodeValue.length() > maxTextSize) { 1460 nodeValue = nodeValue.left(maxTextSize); 1461 nodeValue.append(ellipsisUChar); 1462 } 1463 break; 1464 case Node::ATTRIBUTE_NODE: 1465 localName = node->localName(); 1466 break; 1467 case Node::DOCUMENT_FRAGMENT_NODE: 1468 case Node::DOCUMENT_NODE: 1469 case Node::ELEMENT_NODE: 1470 default: 1471 nodeName = node->nodeName(); 1472 localName = node->localName(); 1473 break; 1474 } 1475 1476 RefPtr<TypeBuilder::DOM::Node> value = TypeBuilder::DOM::Node::create() 1477 .setNodeId(id) 1478 .setNodeType(static_cast<int>(node->nodeType())) 1479 .setNodeName(nodeName) 1480 .setLocalName(localName) 1481 .setNodeValue(nodeValue); 1482 1483 bool forcePushChildren = false; 1484 if (node->isElementNode()) { 1485 Element* element = toElement(node); 1486 value->setAttributes(buildArrayForElementAttributes(element)); 1487 if (node->isFrameOwnerElement()) { 1488 HTMLFrameOwnerElement* frameOwner = toHTMLFrameOwnerElement(node); 1489 if (Frame* frame = frameOwner->contentFrame()) 1490 value->setFrameId(m_pageAgent->frameId(frame)); 1491 if (Document* doc = frameOwner->contentDocument()) 1492 value->setContentDocument(buildObjectForNode(doc, 0, nodesMap)); 1493 } 1494 1495 ElementShadow* shadow = element->shadow(); 1496 if (shadow) { 1497 RefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > shadowRoots = TypeBuilder::Array<TypeBuilder::DOM::Node>::create(); 1498 for (ShadowRoot* root = shadow->youngestShadowRoot(); root; root = root->olderShadowRoot()) 1499 shadowRoots->addItem(buildObjectForNode(root, 0, nodesMap)); 1500 value->setShadowRoots(shadowRoots); 1501 forcePushChildren = true; 1502 } 1503 1504 if (element->hasTagName(templateTag)) { 1505 value->setTemplateContent(buildObjectForNode(toHTMLTemplateElement(element)->content(), 0, nodesMap)); 1506 forcePushChildren = true; 1507 } 1508 1509 switch (element->pseudoId()) { 1510 case BEFORE: 1511 value->setPseudoType(TypeBuilder::DOM::PseudoType::Before); 1512 break; 1513 case AFTER: 1514 value->setPseudoType(TypeBuilder::DOM::PseudoType::After); 1515 break; 1516 default: { 1517 RefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > pseudoElements = buildArrayForPseudoElements(element, nodesMap); 1518 if (pseudoElements) { 1519 value->setPseudoElements(pseudoElements.release()); 1520 forcePushChildren = true; 1521 } 1522 break; 1523 } 1524 } 1525 } else if (node->isDocumentNode()) { 1526 Document* document = toDocument(node); 1527 value->setDocumentURL(documentURLString(document)); 1528 value->setBaseURL(documentBaseURLString(document)); 1529 value->setXmlVersion(document->xmlVersion()); 1530 } else if (node->nodeType() == Node::DOCUMENT_TYPE_NODE) { 1531 DocumentType* docType = toDocumentType(node); 1532 value->setPublicId(docType->publicId()); 1533 value->setSystemId(docType->systemId()); 1534 value->setInternalSubset(docType->internalSubset()); 1535 } else if (node->isAttributeNode()) { 1536 Attr* attribute = toAttr(node); 1537 value->setName(attribute->name()); 1538 value->setValue(attribute->value()); 1539 } else if (node->isShadowRoot()) { 1540 value->setShadowRootType(shadowRootType(toShadowRoot(node))); 1541 } 1542 1543 if (node->isContainerNode()) { 1544 int nodeCount = innerChildNodeCount(node); 1545 value->setChildNodeCount(nodeCount); 1546 if (forcePushChildren && !depth) 1547 depth = 1; 1548 RefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > children = buildArrayForContainerChildren(node, depth, nodesMap); 1549 if (children->length() > 0 || depth) // Push children along with shadow in any case. 1550 value->setChildren(children.release()); 1551 } 1552 1553 return value.release(); 1554 } 1555 1556 PassRefPtr<TypeBuilder::Array<String> > InspectorDOMAgent::buildArrayForElementAttributes(Element* element) 1557 { 1558 RefPtr<TypeBuilder::Array<String> > attributesValue = TypeBuilder::Array<String>::create(); 1559 // Go through all attributes and serialize them. 1560 if (!element->hasAttributes()) 1561 return attributesValue.release(); 1562 unsigned numAttrs = element->attributeCount(); 1563 for (unsigned i = 0; i < numAttrs; ++i) { 1564 // Add attribute pair 1565 const Attribute* attribute = element->attributeItem(i); 1566 attributesValue->addItem(attribute->name().toString()); 1567 attributesValue->addItem(attribute->value()); 1568 } 1569 return attributesValue.release(); 1570 } 1571 1572 PassRefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > InspectorDOMAgent::buildArrayForContainerChildren(Node* container, int depth, NodeToIdMap* nodesMap) 1573 { 1574 RefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > children = TypeBuilder::Array<TypeBuilder::DOM::Node>::create(); 1575 if (depth == 0) { 1576 // Special-case the only text child - pretend that container's children have been requested. 1577 Node* firstChild = container->firstChild(); 1578 if (firstChild && firstChild->nodeType() == Node::TEXT_NODE && !firstChild->nextSibling()) { 1579 children->addItem(buildObjectForNode(firstChild, 0, nodesMap)); 1580 m_childrenRequested.add(bind(container, nodesMap)); 1581 } 1582 return children.release(); 1583 } 1584 1585 Node* child = innerFirstChild(container); 1586 depth--; 1587 m_childrenRequested.add(bind(container, nodesMap)); 1588 1589 while (child) { 1590 children->addItem(buildObjectForNode(child, depth, nodesMap)); 1591 child = innerNextSibling(child); 1592 } 1593 return children.release(); 1594 } 1595 1596 PassRefPtr<TypeBuilder::DOM::EventListener> InspectorDOMAgent::buildObjectForEventListener(const RegisteredEventListener& registeredEventListener, const AtomicString& eventType, Node* node, const String* objectGroupId) 1597 { 1598 RefPtr<EventListener> eventListener = registeredEventListener.listener; 1599 Document& document = node->document(); 1600 RefPtr<TypeBuilder::DOM::EventListener> value = TypeBuilder::DOM::EventListener::create() 1601 .setType(eventType) 1602 .setUseCapture(registeredEventListener.useCapture) 1603 .setIsAttribute(eventListener->isAttribute()) 1604 .setNodeId(pushNodePathToFrontend(node)) 1605 .setHandlerBody(eventListenerHandlerBody(&document, eventListener.get())); 1606 if (objectGroupId) { 1607 ScriptValue functionValue = eventListenerHandler(&document, eventListener.get()); 1608 if (!functionValue.hasNoValue()) { 1609 Frame* frame = document.frame(); 1610 if (frame) { 1611 ScriptState* scriptState = eventListenerHandlerScriptState(frame, eventListener.get()); 1612 if (scriptState) { 1613 InjectedScript injectedScript = m_injectedScriptManager->injectedScriptFor(scriptState); 1614 if (!injectedScript.hasNoValue()) { 1615 RefPtr<TypeBuilder::Runtime::RemoteObject> valueJson = injectedScript.wrapObject(functionValue, *objectGroupId); 1616 value->setHandler(valueJson); 1617 } 1618 } 1619 } 1620 } 1621 } 1622 String sourceName; 1623 String scriptId; 1624 int lineNumber; 1625 if (eventListenerHandlerLocation(&node->document(), eventListener.get(), sourceName, scriptId, lineNumber)) { 1626 RefPtr<TypeBuilder::Debugger::Location> location = TypeBuilder::Debugger::Location::create() 1627 .setScriptId(scriptId) 1628 .setLineNumber(lineNumber); 1629 value->setLocation(location); 1630 if (!sourceName.isEmpty()) 1631 value->setSourceName(sourceName); 1632 } 1633 return value.release(); 1634 } 1635 1636 PassRefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > InspectorDOMAgent::buildArrayForPseudoElements(Element* element, NodeToIdMap* nodesMap) 1637 { 1638 if (!element->pseudoElement(BEFORE) && !element->pseudoElement(AFTER)) 1639 return 0; 1640 1641 RefPtr<TypeBuilder::Array<TypeBuilder::DOM::Node> > pseudoElements = TypeBuilder::Array<TypeBuilder::DOM::Node>::create(); 1642 if (element->pseudoElement(BEFORE)) 1643 pseudoElements->addItem(buildObjectForNode(element->pseudoElement(BEFORE), 0, nodesMap)); 1644 if (element->pseudoElement(AFTER)) 1645 pseudoElements->addItem(buildObjectForNode(element->pseudoElement(AFTER), 0, nodesMap)); 1646 return pseudoElements.release(); 1647 } 1648 1649 Node* InspectorDOMAgent::innerFirstChild(Node* node) 1650 { 1651 node = node->firstChild(); 1652 while (isWhitespace(node)) 1653 node = node->nextSibling(); 1654 return node; 1655 } 1656 1657 Node* InspectorDOMAgent::innerNextSibling(Node* node) 1658 { 1659 do { 1660 node = node->nextSibling(); 1661 } while (isWhitespace(node)); 1662 return node; 1663 } 1664 1665 Node* InspectorDOMAgent::innerPreviousSibling(Node* node) 1666 { 1667 do { 1668 node = node->previousSibling(); 1669 } while (isWhitespace(node)); 1670 return node; 1671 } 1672 1673 unsigned InspectorDOMAgent::innerChildNodeCount(Node* node) 1674 { 1675 unsigned count = 0; 1676 Node* child = innerFirstChild(node); 1677 while (child) { 1678 count++; 1679 child = innerNextSibling(child); 1680 } 1681 return count; 1682 } 1683 1684 Node* InspectorDOMAgent::innerParentNode(Node* node) 1685 { 1686 if (node->isDocumentNode()) { 1687 Document* document = toDocument(node); 1688 return document->ownerElement(); 1689 } 1690 return node->parentOrShadowHostNode(); 1691 } 1692 1693 bool InspectorDOMAgent::isWhitespace(Node* node) 1694 { 1695 //TODO: pull ignoreWhitespace setting from the frontend and use here. 1696 return node && node->nodeType() == Node::TEXT_NODE && node->nodeValue().stripWhiteSpace().length() == 0; 1697 } 1698 1699 void InspectorDOMAgent::domContentLoadedEventFired(Frame* frame) 1700 { 1701 if (!frame->isMainFrame()) 1702 return; 1703 1704 // Re-push document once it is loaded. 1705 discardFrontendBindings(); 1706 if (m_state->getBoolean(DOMAgentState::documentRequested)) 1707 m_frontend->documentUpdated(); 1708 } 1709 1710 void InspectorDOMAgent::invalidateFrameOwnerElement(Frame* frame) 1711 { 1712 Element* frameOwner = frame->document()->ownerElement(); 1713 if (!frameOwner) 1714 return; 1715 1716 int frameOwnerId = m_documentNodeToIdMap.get(frameOwner); 1717 if (!frameOwnerId) 1718 return; 1719 1720 // Re-add frame owner element together with its new children. 1721 int parentId = m_documentNodeToIdMap.get(innerParentNode(frameOwner)); 1722 m_frontend->childNodeRemoved(parentId, frameOwnerId); 1723 unbind(frameOwner, &m_documentNodeToIdMap); 1724 1725 RefPtr<TypeBuilder::DOM::Node> value = buildObjectForNode(frameOwner, 0, &m_documentNodeToIdMap); 1726 Node* previousSibling = innerPreviousSibling(frameOwner); 1727 int prevId = previousSibling ? m_documentNodeToIdMap.get(previousSibling) : 0; 1728 m_frontend->childNodeInserted(parentId, prevId, value.release()); 1729 } 1730 1731 void InspectorDOMAgent::didCommitLoad(Frame* frame, DocumentLoader* loader) 1732 { 1733 // FIXME: If "frame" is always guarenteed to be in the same Page as loader->frame() 1734 // then all we need to check here is loader->frame()->isMainFrame() 1735 // and we don't need "frame" at all. 1736 Frame* mainFrame = frame->page()->mainFrame(); 1737 if (loader->frame() != mainFrame) { 1738 invalidateFrameOwnerElement(loader->frame()); 1739 return; 1740 } 1741 1742 setDocument(mainFrame->document()); 1743 } 1744 1745 void InspectorDOMAgent::didInsertDOMNode(Node* node) 1746 { 1747 if (isWhitespace(node)) 1748 return; 1749 1750 // We could be attaching existing subtree. Forget the bindings. 1751 unbind(node, &m_documentNodeToIdMap); 1752 1753 ContainerNode* parent = node->parentNode(); 1754 if (!parent) 1755 return; 1756 1757 int parentId = m_documentNodeToIdMap.get(parent); 1758 // Return if parent is not mapped yet. 1759 if (!parentId) 1760 return; 1761 1762 if (!m_childrenRequested.contains(parentId)) { 1763 // No children are mapped yet -> only notify on changes of hasChildren. 1764 m_frontend->childNodeCountUpdated(parentId, innerChildNodeCount(parent)); 1765 } else { 1766 // Children have been requested -> return value of a new child. 1767 Node* prevSibling = innerPreviousSibling(node); 1768 int prevId = prevSibling ? m_documentNodeToIdMap.get(prevSibling) : 0; 1769 RefPtr<TypeBuilder::DOM::Node> value = buildObjectForNode(node, 0, &m_documentNodeToIdMap); 1770 m_frontend->childNodeInserted(parentId, prevId, value.release()); 1771 } 1772 } 1773 1774 void InspectorDOMAgent::willRemoveDOMNode(Node* node) 1775 { 1776 if (isWhitespace(node)) 1777 return; 1778 1779 ContainerNode* parent = node->parentNode(); 1780 1781 // If parent is not mapped yet -> ignore the event. 1782 if (!m_documentNodeToIdMap.contains(parent)) 1783 return; 1784 1785 int parentId = m_documentNodeToIdMap.get(parent); 1786 1787 if (!m_childrenRequested.contains(parentId)) { 1788 // No children are mapped yet -> only notify on changes of hasChildren. 1789 if (innerChildNodeCount(parent) == 1) 1790 m_frontend->childNodeCountUpdated(parentId, 0); 1791 } else 1792 m_frontend->childNodeRemoved(parentId, m_documentNodeToIdMap.get(node)); 1793 unbind(node, &m_documentNodeToIdMap); 1794 } 1795 1796 void InspectorDOMAgent::willModifyDOMAttr(Element*, const AtomicString& oldValue, const AtomicString& newValue) 1797 { 1798 m_suppressAttributeModifiedEvent = (oldValue == newValue); 1799 } 1800 1801 void InspectorDOMAgent::didModifyDOMAttr(Element* element, const AtomicString& name, const AtomicString& value) 1802 { 1803 bool shouldSuppressEvent = m_suppressAttributeModifiedEvent; 1804 m_suppressAttributeModifiedEvent = false; 1805 if (shouldSuppressEvent) 1806 return; 1807 1808 int id = boundNodeId(element); 1809 // If node is not mapped yet -> ignore the event. 1810 if (!id) 1811 return; 1812 1813 if (m_domListener) 1814 m_domListener->didModifyDOMAttr(element); 1815 1816 m_frontend->attributeModified(id, name, value); 1817 } 1818 1819 void InspectorDOMAgent::didRemoveDOMAttr(Element* element, const AtomicString& name) 1820 { 1821 int id = boundNodeId(element); 1822 // If node is not mapped yet -> ignore the event. 1823 if (!id) 1824 return; 1825 1826 if (m_domListener) 1827 m_domListener->didModifyDOMAttr(element); 1828 1829 m_frontend->attributeRemoved(id, name); 1830 } 1831 1832 void InspectorDOMAgent::styleAttributeInvalidated(const Vector<Element*>& elements) 1833 { 1834 RefPtr<TypeBuilder::Array<int> > nodeIds = TypeBuilder::Array<int>::create(); 1835 for (unsigned i = 0, size = elements.size(); i < size; ++i) { 1836 Element* element = elements.at(i); 1837 int id = boundNodeId(element); 1838 // If node is not mapped yet -> ignore the event. 1839 if (!id) 1840 continue; 1841 1842 if (m_domListener) 1843 m_domListener->didModifyDOMAttr(element); 1844 nodeIds->addItem(id); 1845 } 1846 m_frontend->inlineStyleInvalidated(nodeIds.release()); 1847 } 1848 1849 void InspectorDOMAgent::characterDataModified(CharacterData* characterData) 1850 { 1851 int id = m_documentNodeToIdMap.get(characterData); 1852 if (!id) { 1853 // Push text node if it is being created. 1854 didInsertDOMNode(characterData); 1855 return; 1856 } 1857 m_frontend->characterDataModified(id, characterData->data()); 1858 } 1859 1860 void InspectorDOMAgent::didInvalidateStyleAttr(Node* node) 1861 { 1862 int id = m_documentNodeToIdMap.get(node); 1863 // If node is not mapped yet -> ignore the event. 1864 if (!id) 1865 return; 1866 1867 if (!m_revalidateStyleAttrTask) 1868 m_revalidateStyleAttrTask = adoptPtr(new RevalidateStyleAttributeTask(this)); 1869 m_revalidateStyleAttrTask->scheduleFor(toElement(node)); 1870 } 1871 1872 void InspectorDOMAgent::didPushShadowRoot(Element* host, ShadowRoot* root) 1873 { 1874 if (!host->ownerDocument()) 1875 return; 1876 1877 int hostId = m_documentNodeToIdMap.get(host); 1878 if (!hostId) 1879 return; 1880 1881 pushChildNodesToFrontend(hostId, 1); 1882 m_frontend->shadowRootPushed(hostId, buildObjectForNode(root, 0, &m_documentNodeToIdMap)); 1883 } 1884 1885 void InspectorDOMAgent::willPopShadowRoot(Element* host, ShadowRoot* root) 1886 { 1887 if (!host->ownerDocument()) 1888 return; 1889 1890 int hostId = m_documentNodeToIdMap.get(host); 1891 int rootId = m_documentNodeToIdMap.get(root); 1892 if (hostId && rootId) 1893 m_frontend->shadowRootPopped(hostId, rootId); 1894 } 1895 1896 void InspectorDOMAgent::frameDocumentUpdated(Frame* frame) 1897 { 1898 Document* document = frame->document(); 1899 if (!document) 1900 return; 1901 1902 Page* page = frame->page(); 1903 ASSERT(page); 1904 if (frame != page->mainFrame()) 1905 return; 1906 1907 // Only update the main frame document, nested frame document updates are not required 1908 // (will be handled by invalidateFrameOwnerElement()). 1909 setDocument(document); 1910 } 1911 1912 void InspectorDOMAgent::pseudoElementCreated(PseudoElement* pseudoElement) 1913 { 1914 Element* parent = pseudoElement->parentOrShadowHostElement(); 1915 if (!parent) 1916 return; 1917 int parentId = m_documentNodeToIdMap.get(parent); 1918 if (!parentId) 1919 return; 1920 1921 pushChildNodesToFrontend(parentId, 1); 1922 m_frontend->pseudoElementAdded(parentId, buildObjectForNode(pseudoElement, 0, &m_documentNodeToIdMap)); 1923 } 1924 1925 void InspectorDOMAgent::pseudoElementDestroyed(PseudoElement* pseudoElement) 1926 { 1927 int pseudoElementId = m_documentNodeToIdMap.get(pseudoElement); 1928 if (!pseudoElementId) 1929 return; 1930 1931 // If a PseudoElement is bound, its parent element must be bound, too. 1932 Element* parent = pseudoElement->parentOrShadowHostElement(); 1933 ASSERT(parent); 1934 int parentId = m_documentNodeToIdMap.get(parent); 1935 ASSERT(parentId); 1936 1937 unbind(pseudoElement, &m_documentNodeToIdMap); 1938 m_frontend->pseudoElementRemoved(parentId, pseudoElementId); 1939 } 1940 1941 Node* InspectorDOMAgent::nodeForPath(const String& path) 1942 { 1943 // The path is of form "1,HTML,2,BODY,1,DIV" 1944 if (!m_document) 1945 return 0; 1946 1947 Node* node = m_document.get(); 1948 Vector<String> pathTokens; 1949 path.split(",", false, pathTokens); 1950 if (!pathTokens.size()) 1951 return 0; 1952 for (size_t i = 0; i < pathTokens.size() - 1; i += 2) { 1953 bool success = true; 1954 unsigned childNumber = pathTokens[i].toUInt(&success); 1955 if (!success) 1956 return 0; 1957 if (childNumber >= innerChildNodeCount(node)) 1958 return 0; 1959 1960 Node* child = innerFirstChild(node); 1961 String childName = pathTokens[i + 1]; 1962 for (size_t j = 0; child && j < childNumber; ++j) 1963 child = innerNextSibling(child); 1964 1965 if (!child || child->nodeName() != childName) 1966 return 0; 1967 node = child; 1968 } 1969 return node; 1970 } 1971 1972 void InspectorDOMAgent::pushNodeByPathToFrontend(ErrorString* errorString, const String& path, int* nodeId) 1973 { 1974 if (Node* node = nodeForPath(path)) 1975 *nodeId = pushNodePathToFrontend(node); 1976 else 1977 *errorString = "No node with given path found"; 1978 } 1979 1980 void InspectorDOMAgent::pushNodeByBackendIdToFrontend(ErrorString* errorString, BackendNodeId backendNodeId, int* nodeId) 1981 { 1982 if (!m_backendIdToNode.contains(backendNodeId)) { 1983 *errorString = "No node with given backend id found"; 1984 return; 1985 } 1986 1987 Node* node = m_backendIdToNode.get(backendNodeId).first; 1988 String nodeGroup = m_backendIdToNode.get(backendNodeId).second; 1989 *nodeId = pushNodePathToFrontend(node); 1990 1991 if (nodeGroup == "") { 1992 m_backendIdToNode.remove(backendNodeId); 1993 m_nodeGroupToBackendIdMap.find(nodeGroup)->value.remove(node); 1994 } 1995 } 1996 1997 void InspectorDOMAgent::getRelayoutBoundary(ErrorString* errorString, int nodeId, int* relayoutBoundaryNodeId) 1998 { 1999 Node* node = assertNode(errorString, nodeId); 2000 if (!node) 2001 return; 2002 RenderObject* renderer = node->renderer(); 2003 if (!renderer) { 2004 *errorString = "No renderer for node, perhaps orphan or hidden node"; 2005 return; 2006 } 2007 while (renderer && !renderer->isRoot() && !renderer->isRelayoutBoundaryForInspector()) 2008 renderer = renderer->container(); 2009 Node* resultNode = renderer ? renderer->generatingNode() : node->ownerDocument(); 2010 *relayoutBoundaryNodeId = pushNodePathToFrontend(resultNode); 2011 } 2012 2013 PassRefPtr<TypeBuilder::Runtime::RemoteObject> InspectorDOMAgent::resolveNode(Node* node, const String& objectGroup) 2014 { 2015 Document* document = node->isDocumentNode() ? &node->document() : node->ownerDocument(); 2016 Frame* frame = document ? document->frame() : 0; 2017 if (!frame) 2018 return 0; 2019 2020 InjectedScript injectedScript = m_injectedScriptManager->injectedScriptFor(mainWorldScriptState(frame)); 2021 if (injectedScript.hasNoValue()) 2022 return 0; 2023 2024 return injectedScript.wrapNode(node, objectGroup); 2025 } 2026 2027 bool InspectorDOMAgent::pushDocumentUponHandlelessOperation(ErrorString* errorString) 2028 { 2029 if (!m_documentNodeToIdMap.contains(m_document)) { 2030 RefPtr<TypeBuilder::DOM::Node> root; 2031 getDocument(errorString, root); 2032 return errorString->isEmpty(); 2033 } 2034 return true; 2035 } 2036 2037 } // namespace WebCore 2038 2039