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