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