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 "InspectorDOMAgent.h"
33
34 #if ENABLE(INSPECTOR)
35
36 #include "Attr.h"
37 #include "CSSComputedStyleDeclaration.h"
38 #include "CSSMutableStyleDeclaration.h"
39 #include "CSSPropertyNames.h"
40 #include "CSSPropertySourceData.h"
41 #include "CSSRule.h"
42 #include "CSSRuleList.h"
43 #include "CSSStyleRule.h"
44 #include "CSSStyleSelector.h"
45 #include "CSSStyleSheet.h"
46 #include "CharacterData.h"
47 #include "ContainerNode.h"
48 #include "Cookie.h"
49 #include "CookieJar.h"
50 #include "DOMNodeHighlighter.h"
51 #include "DOMWindow.h"
52 #include "Document.h"
53 #include "DocumentType.h"
54 #include "Event.h"
55 #include "EventContext.h"
56 #include "EventListener.h"
57 #include "EventNames.h"
58 #include "EventTarget.h"
59 #include "Frame.h"
60 #include "FrameTree.h"
61 #include "HitTestResult.h"
62 #include "HTMLElement.h"
63 #include "HTMLFrameOwnerElement.h"
64 #include "InjectedScriptManager.h"
65 #include "InspectorClient.h"
66 #include "InspectorFrontend.h"
67 #include "InspectorResourceAgent.h"
68 #include "InspectorState.h"
69 #include "InstrumentingAgents.h"
70 #include "MutationEvent.h"
71 #include "Node.h"
72 #include "NodeList.h"
73 #include "Page.h"
74 #include "Pasteboard.h"
75 #include "PlatformString.h"
76 #include "RenderStyle.h"
77 #include "RenderStyleConstants.h"
78 #include "ScriptEventListener.h"
79 #include "StyleSheetList.h"
80 #include "Text.h"
81
82 #if ENABLE(XPATH)
83 #include "XPathResult.h"
84 #endif
85
86 #include "markup.h"
87
88 #include <wtf/text/CString.h>
89 #include <wtf/text/StringConcatenate.h>
90 #include <wtf/HashSet.h>
91 #include <wtf/ListHashSet.h>
92 #include <wtf/OwnPtr.h>
93 #include <wtf/Vector.h>
94 #include <wtf/text/AtomicString.h>
95
96 namespace WebCore {
97
98 namespace DOMAgentState {
99 static const char documentRequested[] = "documentRequested";
100 };
101
102 class MatchJob {
103 public:
104 virtual void match(ListHashSet<Node*>& resultCollector) = 0;
~MatchJob()105 virtual ~MatchJob() { }
106
107 protected:
MatchJob(Document * document,const String & query)108 MatchJob(Document* document, const String& query)
109 : m_document(document)
110 , m_query(query) { }
111
addNodesToResults(PassRefPtr<NodeList> nodes,ListHashSet<Node * > & resultCollector)112 void addNodesToResults(PassRefPtr<NodeList> nodes, ListHashSet<Node*>& resultCollector)
113 {
114 for (unsigned i = 0; nodes && i < nodes->length(); ++i)
115 resultCollector.add(nodes->item(i));
116 }
117
118 RefPtr<Document> m_document;
119 String m_query;
120 };
121
122 class RevalidateStyleAttributeTask {
123 public:
124 RevalidateStyleAttributeTask(InspectorDOMAgent*);
125 void scheduleFor(Element*);
reset()126 void reset() { m_timer.stop(); }
127 void onTimer(Timer<RevalidateStyleAttributeTask>*);
128
129 private:
130 InspectorDOMAgent* m_domAgent;
131 Timer<RevalidateStyleAttributeTask> m_timer;
132 HashSet<RefPtr<Element> > m_elements;
133 };
134
135 namespace {
136
137 class MatchExactIdJob : public WebCore::MatchJob {
138 public:
MatchExactIdJob(Document * document,const String & query)139 MatchExactIdJob(Document* document, const String& query) : WebCore::MatchJob(document, query) { }
~MatchExactIdJob()140 virtual ~MatchExactIdJob() { }
141
142 protected:
match(ListHashSet<Node * > & resultCollector)143 virtual void match(ListHashSet<Node*>& resultCollector)
144 {
145 if (m_query.isEmpty())
146 return;
147
148 Element* element = m_document->getElementById(m_query);
149 if (element)
150 resultCollector.add(element);
151 }
152 };
153
154 class MatchExactClassNamesJob : public WebCore::MatchJob {
155 public:
MatchExactClassNamesJob(Document * document,const String & query)156 MatchExactClassNamesJob(Document* document, const String& query) : WebCore::MatchJob(document, query) { }
~MatchExactClassNamesJob()157 virtual ~MatchExactClassNamesJob() { }
158
match(ListHashSet<Node * > & resultCollector)159 virtual void match(ListHashSet<Node*>& resultCollector)
160 {
161 if (!m_query.isEmpty())
162 addNodesToResults(m_document->getElementsByClassName(m_query), resultCollector);
163 }
164 };
165
166 class MatchExactTagNamesJob : public WebCore::MatchJob {
167 public:
MatchExactTagNamesJob(Document * document,const String & query)168 MatchExactTagNamesJob(Document* document, const String& query) : WebCore::MatchJob(document, query) { }
~MatchExactTagNamesJob()169 virtual ~MatchExactTagNamesJob() { }
170
match(ListHashSet<Node * > & resultCollector)171 virtual void match(ListHashSet<Node*>& resultCollector)
172 {
173 if (!m_query.isEmpty())
174 addNodesToResults(m_document->getElementsByName(m_query), resultCollector);
175 }
176 };
177
178 class MatchQuerySelectorAllJob : public WebCore::MatchJob {
179 public:
MatchQuerySelectorAllJob(Document * document,const String & query)180 MatchQuerySelectorAllJob(Document* document, const String& query) : WebCore::MatchJob(document, query) { }
~MatchQuerySelectorAllJob()181 virtual ~MatchQuerySelectorAllJob() { }
182
match(ListHashSet<Node * > & resultCollector)183 virtual void match(ListHashSet<Node*>& resultCollector)
184 {
185 if (m_query.isEmpty())
186 return;
187
188 ExceptionCode ec = 0;
189 RefPtr<NodeList> list = m_document->querySelectorAll(m_query, ec);
190 if (!ec)
191 addNodesToResults(list, resultCollector);
192 }
193 };
194
195 class MatchXPathJob : public WebCore::MatchJob {
196 public:
MatchXPathJob(Document * document,const String & query)197 MatchXPathJob(Document* document, const String& query) : WebCore::MatchJob(document, query) { }
~MatchXPathJob()198 virtual ~MatchXPathJob() { }
199
match(ListHashSet<Node * > & resultCollector)200 virtual void match(ListHashSet<Node*>& resultCollector)
201 {
202 #if ENABLE(XPATH)
203 if (m_query.isEmpty())
204 return;
205
206 ExceptionCode ec = 0;
207 RefPtr<XPathResult> result = m_document->evaluate(m_query, m_document.get(), 0, XPathResult::ORDERED_NODE_SNAPSHOT_TYPE, 0, ec);
208 if (ec || !result)
209 return;
210
211 unsigned long size = result->snapshotLength(ec);
212 for (unsigned long i = 0; !ec && i < size; ++i) {
213 Node* node = result->snapshotItem(i, ec);
214 if (ec)
215 break;
216
217 if (node->nodeType() == Node::ATTRIBUTE_NODE)
218 node = static_cast<Attr*>(node)->ownerElement();
219 resultCollector.add(node);
220 }
221 #else
222 UNUSED_PARAM(resultCollector);
223 #endif
224 }
225 };
226
227 class MatchPlainTextJob : public MatchXPathJob {
228 public:
MatchPlainTextJob(Document * document,const String & query)229 MatchPlainTextJob(Document* document, const String& query) : MatchXPathJob(document, query)
230 {
231 m_query = "//text()[contains(., '" + m_query + "')] | //comment()[contains(., '" + m_query + "')]";
232 }
~MatchPlainTextJob()233 virtual ~MatchPlainTextJob() { }
234 };
235
236 }
237
RevalidateStyleAttributeTask(InspectorDOMAgent * domAgent)238 RevalidateStyleAttributeTask::RevalidateStyleAttributeTask(InspectorDOMAgent* domAgent)
239 : m_domAgent(domAgent)
240 , m_timer(this, &RevalidateStyleAttributeTask::onTimer)
241 {
242 }
243
scheduleFor(Element * element)244 void RevalidateStyleAttributeTask::scheduleFor(Element* element)
245 {
246 m_elements.add(element);
247 if (!m_timer.isActive())
248 m_timer.startOneShot(0);
249 }
250
onTimer(Timer<RevalidateStyleAttributeTask> *)251 void RevalidateStyleAttributeTask::onTimer(Timer<RevalidateStyleAttributeTask>*)
252 {
253 // The timer is stopped on m_domAgent destruction, so this method will never be called after m_domAgent has been destroyed.
254 for (HashSet<RefPtr<Element> >::iterator it = m_elements.begin(), end = m_elements.end(); it != end; ++it)
255 m_domAgent->didModifyDOMAttr(it->get());
256
257 m_elements.clear();
258 }
259
InspectorDOMAgent(InstrumentingAgents * instrumentingAgents,Page * inspectedPage,InspectorClient * client,InspectorState * inspectorState,InjectedScriptManager * injectedScriptManager)260 InspectorDOMAgent::InspectorDOMAgent(InstrumentingAgents* instrumentingAgents, Page* inspectedPage, InspectorClient* client, InspectorState* inspectorState, InjectedScriptManager* injectedScriptManager)
261 : m_instrumentingAgents(instrumentingAgents)
262 , m_inspectedPage(inspectedPage)
263 , m_client(client)
264 , m_inspectorState(inspectorState)
265 , m_injectedScriptManager(injectedScriptManager)
266 , m_frontend(0)
267 , m_domListener(0)
268 , m_lastNodeId(1)
269 , m_matchJobsTimer(this, &InspectorDOMAgent::onMatchJobsTimer)
270 , m_searchingForNode(false)
271 {
272 }
273
~InspectorDOMAgent()274 InspectorDOMAgent::~InspectorDOMAgent()
275 {
276 reset();
277 ASSERT(!m_highlightedNode);
278 ASSERT(!m_searchingForNode);
279 }
280
setFrontend(InspectorFrontend * frontend)281 void InspectorDOMAgent::setFrontend(InspectorFrontend* frontend)
282 {
283 ASSERT(!m_frontend);
284 m_frontend = frontend->dom();
285 m_instrumentingAgents->setInspectorDOMAgent(this);
286 m_document = m_inspectedPage->mainFrame()->document();
287
288 if (m_nodeToFocus)
289 focusNode();
290 }
291
clearFrontend()292 void InspectorDOMAgent::clearFrontend()
293 {
294 ASSERT(m_frontend);
295 setSearchingForNode(false);
296
297 ErrorString error;
298 hideHighlight(&error);
299
300 m_frontend = 0;
301 m_instrumentingAgents->setInspectorDOMAgent(0);
302 m_inspectorState->setBoolean(DOMAgentState::documentRequested, false);
303 reset();
304 }
305
restore()306 void InspectorDOMAgent::restore()
307 {
308 // Reset document to avoid early return from setDocument.
309 m_document = 0;
310 setDocument(m_inspectedPage->mainFrame()->document());
311 }
312
documents()313 Vector<Document*> InspectorDOMAgent::documents()
314 {
315 Vector<Document*> result;
316 for (Frame* frame = m_document->frame(); frame; frame = frame->tree()->traverseNext()) {
317 Document* document = frame->document();
318 if (!document)
319 continue;
320 result.append(document);
321 }
322 return result;
323 }
324
reset()325 void InspectorDOMAgent::reset()
326 {
327 ErrorString error;
328 cancelSearch(&error);
329 discardBindings();
330 if (m_revalidateStyleAttrTask)
331 m_revalidateStyleAttrTask->reset();
332 m_document = 0;
333 }
334
setDOMListener(DOMListener * listener)335 void InspectorDOMAgent::setDOMListener(DOMListener* listener)
336 {
337 m_domListener = listener;
338 }
339
setDocument(Document * doc)340 void InspectorDOMAgent::setDocument(Document* doc)
341 {
342 if (doc == m_document.get())
343 return;
344
345 reset();
346
347 m_document = doc;
348
349 if (!m_inspectorState->getBoolean(DOMAgentState::documentRequested))
350 return;
351
352 // Immediately communicate 0 document or document that has finished loading.
353 if (!doc || !doc->parsing())
354 m_frontend->documentUpdated();
355 }
356
releaseDanglingNodes()357 void InspectorDOMAgent::releaseDanglingNodes()
358 {
359 deleteAllValues(m_danglingNodeToIdMaps);
360 m_danglingNodeToIdMaps.clear();
361 }
362
bind(Node * node,NodeToIdMap * nodesMap)363 int InspectorDOMAgent::bind(Node* node, NodeToIdMap* nodesMap)
364 {
365 int id = nodesMap->get(node);
366 if (id)
367 return id;
368 id = m_lastNodeId++;
369 nodesMap->set(node, id);
370 m_idToNode.set(id, node);
371 m_idToNodesMap.set(id, nodesMap);
372 return id;
373 }
374
unbind(Node * node,NodeToIdMap * nodesMap)375 void InspectorDOMAgent::unbind(Node* node, NodeToIdMap* nodesMap)
376 {
377 if (node->isFrameOwnerElement()) {
378 const HTMLFrameOwnerElement* frameOwner = static_cast<const HTMLFrameOwnerElement*>(node);
379 if (m_domListener)
380 m_domListener->didRemoveDocument(frameOwner->contentDocument());
381 }
382
383 int id = nodesMap->get(node);
384 if (!id)
385 return;
386 m_idToNode.remove(id);
387 nodesMap->remove(node);
388 bool childrenRequested = m_childrenRequested.contains(id);
389 if (childrenRequested) {
390 // Unbind subtree known to client recursively.
391 m_childrenRequested.remove(id);
392 Node* child = innerFirstChild(node);
393 while (child) {
394 unbind(child, nodesMap);
395 child = innerNextSibling(child);
396 }
397 }
398 }
399
assertNode(ErrorString * errorString,int nodeId)400 Node* InspectorDOMAgent::assertNode(ErrorString* errorString, int nodeId)
401 {
402 Node* node = nodeForId(nodeId);
403 if (!node) {
404 *errorString = "Could not find node with given id";
405 return 0;
406 }
407 return node;
408 }
409
assertElement(ErrorString * errorString,int nodeId)410 Element* InspectorDOMAgent::assertElement(ErrorString* errorString, int nodeId)
411 {
412 Node* node = assertNode(errorString, nodeId);
413 if (!node)
414 return 0;
415
416 if (node->nodeType() != Node::ELEMENT_NODE) {
417 *errorString = "Node is not an Element";
418 return 0;
419 }
420 return toElement(node);
421 }
422
423
assertHTMLElement(ErrorString * errorString,int nodeId)424 HTMLElement* InspectorDOMAgent::assertHTMLElement(ErrorString* errorString, int nodeId)
425 {
426 Element* element = assertElement(errorString, nodeId);
427 if (!element)
428 return 0;
429
430 if (!element->isHTMLElement()) {
431 *errorString = "Node is not an HTML Element";
432 return 0;
433 }
434 return toHTMLElement(element);
435 }
436
getDocument(ErrorString *,RefPtr<InspectorObject> * root)437 void InspectorDOMAgent::getDocument(ErrorString*, RefPtr<InspectorObject>* root)
438 {
439 m_inspectorState->setBoolean(DOMAgentState::documentRequested, true);
440
441 if (!m_document)
442 return;
443
444 // Reset backend state.
445 RefPtr<Document> doc = m_document;
446 reset();
447 m_document = doc;
448
449 *root = buildObjectForNode(m_document.get(), 2, &m_documentNodeToIdMap);
450 }
451
pushChildNodesToFrontend(int nodeId)452 void InspectorDOMAgent::pushChildNodesToFrontend(int nodeId)
453 {
454 Node* node = nodeForId(nodeId);
455 if (!node || (node->nodeType() != Node::ELEMENT_NODE && node->nodeType() != Node::DOCUMENT_NODE && node->nodeType() != Node::DOCUMENT_FRAGMENT_NODE))
456 return;
457 if (m_childrenRequested.contains(nodeId))
458 return;
459
460 NodeToIdMap* nodeMap = m_idToNodesMap.get(nodeId);
461 RefPtr<InspectorArray> children = buildArrayForContainerChildren(node, 1, nodeMap);
462 m_frontend->setChildNodes(nodeId, children.release());
463 }
464
discardBindings()465 void InspectorDOMAgent::discardBindings()
466 {
467 m_documentNodeToIdMap.clear();
468 m_idToNode.clear();
469 releaseDanglingNodes();
470 m_childrenRequested.clear();
471 }
472
nodeForId(int id)473 Node* InspectorDOMAgent::nodeForId(int id)
474 {
475 if (!id)
476 return 0;
477
478 HashMap<int, Node*>::iterator it = m_idToNode.find(id);
479 if (it != m_idToNode.end())
480 return it->second;
481 return 0;
482 }
483
getChildNodes(ErrorString *,int nodeId)484 void InspectorDOMAgent::getChildNodes(ErrorString*, int nodeId)
485 {
486 pushChildNodesToFrontend(nodeId);
487 }
488
querySelector(ErrorString * errorString,int nodeId,const String & selectors,int * elementId)489 void InspectorDOMAgent::querySelector(ErrorString* errorString, int nodeId, const String& selectors, int* elementId)
490 {
491 *elementId = 0;
492 Node* node = assertNode(errorString, nodeId);
493 if (!node)
494 return;
495
496 ExceptionCode ec = 0;
497 RefPtr<Element> element = node->querySelector(selectors, ec);
498 if (ec) {
499 *errorString = "DOM Error while querying";
500 return;
501 }
502
503 if (element)
504 *elementId = pushNodePathToFrontend(element.get());
505 }
506
querySelectorAll(ErrorString * errorString,int nodeId,const String & selectors,RefPtr<InspectorArray> * result)507 void InspectorDOMAgent::querySelectorAll(ErrorString* errorString, int nodeId, const String& selectors, RefPtr<InspectorArray>* result)
508 {
509 Node* node = assertNode(errorString, nodeId);
510 if (!node)
511 return;
512
513 ExceptionCode ec = 0;
514 RefPtr<NodeList> nodes = node->querySelectorAll(selectors, ec);
515 if (ec) {
516 *errorString = "DOM Error while querying";
517 return;
518 }
519
520 for (unsigned i = 0; i < nodes->length(); ++i)
521 (*result)->pushNumber(pushNodePathToFrontend(nodes->item(i)));
522 }
523
pushNodePathToFrontend(Node * nodeToPush)524 int InspectorDOMAgent::pushNodePathToFrontend(Node* nodeToPush)
525 {
526 ASSERT(nodeToPush); // Invalid input
527
528 if (!m_document)
529 return 0;
530 if (!m_documentNodeToIdMap.contains(m_document))
531 return 0;
532
533 // Return id in case the node is known.
534 int result = m_documentNodeToIdMap.get(nodeToPush);
535 if (result)
536 return result;
537
538 Node* node = nodeToPush;
539 Vector<Node*> path;
540 NodeToIdMap* danglingMap = 0;
541
542 while (true) {
543 Node* parent = innerParentNode(node);
544 if (!parent) {
545 // Node being pushed is detached -> push subtree root.
546 danglingMap = new NodeToIdMap();
547 m_danglingNodeToIdMaps.append(danglingMap);
548 RefPtr<InspectorArray> children = InspectorArray::create();
549 children->pushObject(buildObjectForNode(node, 0, danglingMap));
550 m_frontend->setChildNodes(0, children);
551 break;
552 } else {
553 path.append(parent);
554 if (m_documentNodeToIdMap.get(parent))
555 break;
556 else
557 node = parent;
558 }
559 }
560
561 NodeToIdMap* map = danglingMap ? danglingMap : &m_documentNodeToIdMap;
562 for (int i = path.size() - 1; i >= 0; --i) {
563 int nodeId = map->get(path.at(i));
564 ASSERT(nodeId);
565 pushChildNodesToFrontend(nodeId);
566 }
567 return map->get(nodeToPush);
568 }
569
boundNodeId(Node * node)570 int InspectorDOMAgent::boundNodeId(Node* node)
571 {
572 return m_documentNodeToIdMap.get(node);
573 }
574
setAttribute(ErrorString * errorString,int elementId,const String & name,const String & value)575 void InspectorDOMAgent::setAttribute(ErrorString* errorString, int elementId, const String& name, const String& value)
576 {
577 Element* element = assertElement(errorString, elementId);
578 if (element) {
579 ExceptionCode ec = 0;
580 element->setAttribute(name, value, ec);
581 if (ec)
582 *errorString = "Exception while setting attribute value";
583 }
584 }
585
removeAttribute(ErrorString * errorString,int elementId,const String & name)586 void InspectorDOMAgent::removeAttribute(ErrorString* errorString, int elementId, const String& name)
587 {
588 Element* element = assertElement(errorString, elementId);
589 if (element) {
590 ExceptionCode ec = 0;
591 element->removeAttribute(name, ec);
592 if (ec)
593 *errorString = "Exception while removing attribute";
594 }
595 }
596
removeNode(ErrorString * errorString,int nodeId)597 void InspectorDOMAgent::removeNode(ErrorString* errorString, int nodeId)
598 {
599 Node* node = assertNode(errorString, nodeId);
600 if (!node)
601 return;
602
603 ContainerNode* parentNode = node->parentNode();
604 if (!parentNode) {
605 *errorString = "Can not remove detached node";
606 return;
607 }
608
609 ExceptionCode ec = 0;
610 parentNode->removeChild(node, ec);
611 if (ec)
612 *errorString = "Could not remove node due to DOM exception";
613 }
614
setNodeName(ErrorString *,int nodeId,const String & tagName,int * newId)615 void InspectorDOMAgent::setNodeName(ErrorString*, int nodeId, const String& tagName, int* newId)
616 {
617 *newId = 0;
618
619 Node* oldNode = nodeForId(nodeId);
620 if (!oldNode || !oldNode->isElementNode())
621 return;
622
623 ExceptionCode ec = 0;
624 RefPtr<Element> newElem = oldNode->document()->createElement(tagName, ec);
625 if (ec)
626 return;
627
628 // Copy over the original node's attributes.
629 Element* oldElem = static_cast<Element*>(oldNode);
630 newElem->copyNonAttributeProperties(oldElem);
631 if (oldElem->attributes())
632 newElem->attributes()->setAttributes(*(oldElem->attributes(true)));
633
634 // Copy over the original node's children.
635 Node* child;
636 while ((child = oldNode->firstChild()))
637 newElem->appendChild(child, ec);
638
639 // Replace the old node with the new node
640 ContainerNode* parent = oldNode->parentNode();
641 parent->insertBefore(newElem, oldNode->nextSibling(), ec);
642 parent->removeChild(oldNode, ec);
643
644 if (ec)
645 return;
646
647 *newId = pushNodePathToFrontend(newElem.get());
648 if (m_childrenRequested.contains(nodeId))
649 pushChildNodesToFrontend(*newId);
650 }
651
getOuterHTML(ErrorString * errorString,int nodeId,WTF::String * outerHTML)652 void InspectorDOMAgent::getOuterHTML(ErrorString* errorString, int nodeId, WTF::String* outerHTML)
653 {
654 HTMLElement* element = assertHTMLElement(errorString, nodeId);
655 if (element)
656 *outerHTML = element->outerHTML();
657 }
658
setOuterHTML(ErrorString * errorString,int nodeId,const String & outerHTML,int * newId)659 void InspectorDOMAgent::setOuterHTML(ErrorString* errorString, int nodeId, const String& outerHTML, int* newId)
660 {
661 HTMLElement* htmlElement = assertHTMLElement(errorString, nodeId);
662 if (!htmlElement)
663 return;
664
665 bool requiresTotalUpdate = htmlElement->tagName() == "HTML" || htmlElement->tagName() == "BODY" || htmlElement->tagName() == "HEAD";
666
667 bool childrenRequested = m_childrenRequested.contains(nodeId);
668 Node* previousSibling = htmlElement->previousSibling();
669 ContainerNode* parentNode = htmlElement->parentNode();
670
671 ExceptionCode ec = 0;
672 htmlElement->setOuterHTML(outerHTML, ec);
673 if (ec)
674 return;
675
676 if (requiresTotalUpdate) {
677 RefPtr<Document> document = m_document;
678 reset();
679 setDocument(document.get());
680 *newId = 0;
681 return;
682 }
683
684 Node* newNode = previousSibling ? previousSibling->nextSibling() : parentNode->firstChild();
685 if (!newNode) {
686 // The only child node has been deleted.
687 *newId = 0;
688 return;
689 }
690
691 *newId = pushNodePathToFrontend(newNode);
692 if (childrenRequested)
693 pushChildNodesToFrontend(*newId);
694 }
695
setNodeValue(ErrorString * errorString,int nodeId,const String & value)696 void InspectorDOMAgent::setNodeValue(ErrorString* errorString, int nodeId, const String& value)
697 {
698 Node* node = assertNode(errorString, nodeId);
699 if (!node)
700 return;
701
702 if (node->nodeType() != Node::TEXT_NODE) {
703 *errorString = "Can only set value of text nodes";
704 return;
705 }
706
707 Text* textNode = static_cast<Text*>(node);
708 ExceptionCode ec = 0;
709 textNode->replaceWholeText(value, ec);
710 if (ec)
711 *errorString = "DOM Error while setting the node value";
712 }
713
getEventListenersForNode(ErrorString *,int nodeId,RefPtr<InspectorArray> * listenersArray)714 void InspectorDOMAgent::getEventListenersForNode(ErrorString*, int nodeId, RefPtr<InspectorArray>* listenersArray)
715 {
716 Node* node = nodeForId(nodeId);
717 EventTargetData* d;
718
719 // Quick break if a null node or no listeners at all
720 if (!node || !(d = node->eventTargetData()))
721 return;
722
723 // Get the list of event types this Node is concerned with
724 Vector<AtomicString> eventTypes;
725 const EventListenerMap& listenerMap = d->eventListenerMap;
726 EventListenerMap::const_iterator end = listenerMap.end();
727 for (EventListenerMap::const_iterator iter = listenerMap.begin(); iter != end; ++iter)
728 eventTypes.append(iter->first);
729
730 // Quick break if no useful listeners
731 size_t eventTypesLength = eventTypes.size();
732 if (!eventTypesLength)
733 return;
734
735 // The Node's Ancestors (not including self)
736 Vector<ContainerNode*> ancestors;
737 for (ContainerNode* ancestor = node->parentOrHostNode(); ancestor; ancestor = ancestor->parentOrHostNode())
738 ancestors.append(ancestor);
739
740 // Nodes and their Listeners for the concerned event types (order is top to bottom)
741 Vector<EventListenerInfo> eventInformation;
742 for (size_t i = ancestors.size(); i; --i) {
743 ContainerNode* ancestor = ancestors[i - 1];
744 for (size_t j = 0; j < eventTypesLength; ++j) {
745 AtomicString& type = eventTypes[j];
746 if (ancestor->hasEventListeners(type))
747 eventInformation.append(EventListenerInfo(ancestor, type, ancestor->getEventListeners(type)));
748 }
749 }
750
751 // Insert the Current Node at the end of that list (last in capturing, first in bubbling)
752 for (size_t i = 0; i < eventTypesLength; ++i) {
753 const AtomicString& type = eventTypes[i];
754 eventInformation.append(EventListenerInfo(node, type, node->getEventListeners(type)));
755 }
756
757 // Get Capturing Listeners (in this order)
758 size_t eventInformationLength = eventInformation.size();
759 for (size_t i = 0; i < eventInformationLength; ++i) {
760 const EventListenerInfo& info = eventInformation[i];
761 const EventListenerVector& vector = info.eventListenerVector;
762 for (size_t j = 0; j < vector.size(); ++j) {
763 const RegisteredEventListener& listener = vector[j];
764 if (listener.useCapture)
765 (*listenersArray)->pushObject(buildObjectForEventListener(listener, info.eventType, info.node));
766 }
767 }
768
769 // Get Bubbling Listeners (reverse order)
770 for (size_t i = eventInformationLength; i; --i) {
771 const EventListenerInfo& info = eventInformation[i - 1];
772 const EventListenerVector& vector = info.eventListenerVector;
773 for (size_t j = 0; j < vector.size(); ++j) {
774 const RegisteredEventListener& listener = vector[j];
775 if (!listener.useCapture)
776 (*listenersArray)->pushObject(buildObjectForEventListener(listener, info.eventType, info.node));
777 }
778 }
779 }
780
performSearch(ErrorString * error,const String & whitespaceTrimmedQuery,const bool * const runSynchronously)781 void InspectorDOMAgent::performSearch(ErrorString* error, const String& whitespaceTrimmedQuery, const bool* const runSynchronously)
782 {
783 // FIXME: Few things are missing here:
784 // 1) Search works with node granularity - number of matches within node is not calculated.
785 // 2) There is no need to push all search results to the front-end at a time, pushing next / previous result
786 // is sufficient.
787
788 unsigned queryLength = whitespaceTrimmedQuery.length();
789 bool startTagFound = !whitespaceTrimmedQuery.find('<');
790 bool endTagFound = whitespaceTrimmedQuery.reverseFind('>') + 1 == queryLength;
791
792 String tagNameQuery = whitespaceTrimmedQuery;
793 if (startTagFound || endTagFound)
794 tagNameQuery = tagNameQuery.substring(startTagFound ? 1 : 0, endTagFound ? queryLength - 1 : queryLength);
795 if (!Document::isValidName(tagNameQuery))
796 tagNameQuery = "";
797
798 String attributeNameQuery = whitespaceTrimmedQuery;
799 if (!Document::isValidName(attributeNameQuery))
800 attributeNameQuery = "";
801
802 String escapedQuery = whitespaceTrimmedQuery;
803 escapedQuery.replace("'", "\\'");
804 String escapedTagNameQuery = tagNameQuery;
805 escapedTagNameQuery.replace("'", "\\'");
806
807 // Clear pending jobs.
808 cancelSearch(error);
809
810 // Find all frames, iframes and object elements to search their documents.
811 Vector<Document*> docs = documents();
812 for (Vector<Document*>::iterator it = docs.begin(); it != docs.end(); ++it) {
813 Document* document = *it;
814
815 if (!tagNameQuery.isEmpty() && startTagFound && endTagFound) {
816 m_pendingMatchJobs.append(new MatchExactTagNamesJob(document, tagNameQuery));
817 m_pendingMatchJobs.append(new MatchPlainTextJob(document, escapedQuery));
818 continue;
819 }
820
821 if (!tagNameQuery.isEmpty() && startTagFound) {
822 m_pendingMatchJobs.append(new MatchXPathJob(document, "//*[starts-with(name(), '" + escapedTagNameQuery + "')]"));
823 m_pendingMatchJobs.append(new MatchPlainTextJob(document, escapedQuery));
824 continue;
825 }
826
827 if (!tagNameQuery.isEmpty() && endTagFound) {
828 // FIXME: we should have a matchEndOfTagNames search function if endTagFound is true but not startTagFound.
829 // This requires ends-with() support in XPath, WebKit only supports starts-with() and contains().
830 m_pendingMatchJobs.append(new MatchXPathJob(document, "//*[contains(name(), '" + escapedTagNameQuery + "')]"));
831 m_pendingMatchJobs.append(new MatchPlainTextJob(document, escapedQuery));
832 continue;
833 }
834
835 bool matchesEveryNode = whitespaceTrimmedQuery == "//*" || whitespaceTrimmedQuery == "*";
836 if (matchesEveryNode) {
837 // These queries will match every node. Matching everything isn't useful and can be slow for large pages,
838 // so limit the search functions list to plain text and attribute matching for these.
839 m_pendingMatchJobs.append(new MatchXPathJob(document, "//*[contains(@*, '" + escapedQuery + "')]"));
840 m_pendingMatchJobs.append(new MatchPlainTextJob(document, escapedQuery));
841 continue;
842 }
843
844 m_pendingMatchJobs.append(new MatchExactIdJob(document, whitespaceTrimmedQuery));
845 m_pendingMatchJobs.append(new MatchExactClassNamesJob(document, whitespaceTrimmedQuery));
846 m_pendingMatchJobs.append(new MatchExactTagNamesJob(document, tagNameQuery));
847 m_pendingMatchJobs.append(new MatchQuerySelectorAllJob(document, "[" + attributeNameQuery + "]"));
848 m_pendingMatchJobs.append(new MatchQuerySelectorAllJob(document, whitespaceTrimmedQuery));
849 m_pendingMatchJobs.append(new MatchXPathJob(document, "//*[contains(@*, '" + escapedQuery + "')]"));
850 if (!tagNameQuery.isEmpty())
851 m_pendingMatchJobs.append(new MatchXPathJob(document, "//*[contains(name(), '" + escapedTagNameQuery + "')]"));
852 m_pendingMatchJobs.append(new MatchPlainTextJob(document, escapedQuery));
853 m_pendingMatchJobs.append(new MatchXPathJob(document, whitespaceTrimmedQuery));
854 }
855
856 if (runSynchronously && *runSynchronously) {
857 // For tests.
858 ListHashSet<Node*> resultCollector;
859 for (Deque<MatchJob*>::iterator it = m_pendingMatchJobs.begin(); it != m_pendingMatchJobs.end(); ++it)
860 (*it)->match(resultCollector);
861 reportNodesAsSearchResults(resultCollector);
862 cancelSearch(error);
863 return;
864 }
865 m_matchJobsTimer.startOneShot(0);
866 }
867
cancelSearch(ErrorString *)868 void InspectorDOMAgent::cancelSearch(ErrorString*)
869 {
870 if (m_matchJobsTimer.isActive())
871 m_matchJobsTimer.stop();
872 deleteAllValues(m_pendingMatchJobs);
873 m_pendingMatchJobs.clear();
874 m_searchResults.clear();
875 }
876
handleMousePress()877 bool InspectorDOMAgent::handleMousePress()
878 {
879 if (!m_searchingForNode)
880 return false;
881
882 if (m_highlightedNode) {
883 RefPtr<Node> node = m_highlightedNode;
884 setSearchingForNode(false);
885 inspect(node.get());
886 }
887 return true;
888 }
889
inspect(Node * node)890 void InspectorDOMAgent::inspect(Node* node)
891 {
892 if (node->nodeType() != Node::ELEMENT_NODE && node->nodeType() != Node::DOCUMENT_NODE)
893 node = node->parentNode();
894 m_nodeToFocus = node;
895
896 focusNode();
897 }
898
focusNode()899 void InspectorDOMAgent::focusNode()
900 {
901 if (!m_frontend)
902 return;
903
904 ASSERT(m_nodeToFocus);
905
906 RefPtr<Node> node = m_nodeToFocus.get();
907 m_nodeToFocus = 0;
908
909 Document* document = node->ownerDocument();
910 if (!document)
911 return;
912 Frame* frame = document->frame();
913 if (!frame)
914 return;
915
916 InjectedScript injectedScript = m_injectedScriptManager->injectedScriptFor(mainWorldScriptState(frame));
917 if (injectedScript.hasNoValue())
918 return;
919
920 injectedScript.inspectNode(node.get());
921 }
922
mouseDidMoveOverElement(const HitTestResult & result,unsigned)923 void InspectorDOMAgent::mouseDidMoveOverElement(const HitTestResult& result, unsigned)
924 {
925 if (!m_searchingForNode)
926 return;
927
928 Node* node = result.innerNode();
929 while (node && node->nodeType() == Node::TEXT_NODE)
930 node = node->parentNode();
931 if (node) {
932 ErrorString error;
933 highlight(&error, node);
934 }
935 }
936
setSearchingForNode(bool enabled)937 void InspectorDOMAgent::setSearchingForNode(bool enabled)
938 {
939 if (m_searchingForNode == enabled)
940 return;
941 m_searchingForNode = enabled;
942 if (!enabled) {
943 ErrorString error;
944 hideHighlight(&error);
945 }
946 }
947
setSearchingForNode(ErrorString *,bool enabled)948 void InspectorDOMAgent::setSearchingForNode(ErrorString*, bool enabled)
949 {
950 setSearchingForNode(enabled);
951 }
952
highlight(ErrorString *,Node * node)953 void InspectorDOMAgent::highlight(ErrorString*, Node* node)
954 {
955 ASSERT_ARG(node, node);
956 m_highlightedNode = node;
957 m_client->highlight(node);
958 }
959
highlightDOMNode(ErrorString * error,int nodeId)960 void InspectorDOMAgent::highlightDOMNode(ErrorString* error, int nodeId)
961 {
962 if (Node* node = nodeForId(nodeId))
963 highlight(error, node);
964 }
965
highlightFrame(ErrorString * error,const String & frameId)966 void InspectorDOMAgent::highlightFrame(ErrorString* error, const String& frameId)
967 {
968 Frame* frame = m_instrumentingAgents->inspectorResourceAgent()->frameForId(frameId);
969 if (frame && frame->ownerElement())
970 highlight(error, frame->ownerElement());
971 }
972
hideHighlight(ErrorString *)973 void InspectorDOMAgent::hideHighlight(ErrorString*)
974 {
975 m_highlightedNode = 0;
976 m_client->hideHighlight();
977 }
978
resolveNode(ErrorString * error,int nodeId,RefPtr<InspectorObject> * result)979 void InspectorDOMAgent::resolveNode(ErrorString* error, int nodeId, RefPtr<InspectorObject>* result)
980 {
981 Node* node = nodeForId(nodeId);
982 if (!node) {
983 *error = "No node with given id found.";
984 return;
985 }
986 *result = resolveNode(node);
987 }
988
pushNodeToFrontend(ErrorString *,const String & objectId,int * nodeId)989 void InspectorDOMAgent::pushNodeToFrontend(ErrorString*, const String& objectId, int* nodeId)
990 {
991 InjectedScript injectedScript = m_injectedScriptManager->injectedScriptForObjectId(objectId);
992 Node* node = injectedScript.nodeForObjectId(objectId);
993 if (node)
994 *nodeId = pushNodePathToFrontend(node);
995 else
996 *nodeId = 0;
997 }
998
documentURLString(Document * document) const999 String InspectorDOMAgent::documentURLString(Document* document) const
1000 {
1001 if (!document || document->url().isNull())
1002 return "";
1003 return document->url().string();
1004 }
1005
buildObjectForNode(Node * node,int depth,NodeToIdMap * nodesMap)1006 PassRefPtr<InspectorObject> InspectorDOMAgent::buildObjectForNode(Node* node, int depth, NodeToIdMap* nodesMap)
1007 {
1008 RefPtr<InspectorObject> value = InspectorObject::create();
1009
1010 int id = bind(node, nodesMap);
1011 String nodeName;
1012 String localName;
1013 String nodeValue;
1014
1015 switch (node->nodeType()) {
1016 case Node::TEXT_NODE:
1017 case Node::COMMENT_NODE:
1018 case Node::CDATA_SECTION_NODE:
1019 nodeValue = node->nodeValue();
1020 break;
1021 case Node::ATTRIBUTE_NODE:
1022 localName = node->localName();
1023 break;
1024 case Node::DOCUMENT_FRAGMENT_NODE:
1025 break;
1026 case Node::DOCUMENT_NODE:
1027 case Node::ELEMENT_NODE:
1028 default:
1029 nodeName = node->nodeName();
1030 localName = node->localName();
1031 break;
1032 }
1033
1034 value->setNumber("id", id);
1035 value->setNumber("nodeType", node->nodeType());
1036 value->setString("nodeName", nodeName);
1037 value->setString("localName", localName);
1038 value->setString("nodeValue", nodeValue);
1039
1040 if (node->nodeType() == Node::ELEMENT_NODE || node->nodeType() == Node::DOCUMENT_NODE || node->nodeType() == Node::DOCUMENT_FRAGMENT_NODE) {
1041 int nodeCount = innerChildNodeCount(node);
1042 value->setNumber("childNodeCount", nodeCount);
1043 RefPtr<InspectorArray> children = buildArrayForContainerChildren(node, depth, nodesMap);
1044 if (children->length() > 0)
1045 value->setArray("children", children.release());
1046
1047 if (node->nodeType() == Node::ELEMENT_NODE) {
1048 Element* element = static_cast<Element*>(node);
1049 value->setArray("attributes", buildArrayForElementAttributes(element));
1050 if (node->isFrameOwnerElement()) {
1051 HTMLFrameOwnerElement* frameOwner = static_cast<HTMLFrameOwnerElement*>(node);
1052 value->setString("documentURL", documentURLString(frameOwner->contentDocument()));
1053 }
1054 } else if (node->nodeType() == Node::DOCUMENT_NODE) {
1055 Document* document = static_cast<Document*>(node);
1056 value->setString("documentURL", documentURLString(document));
1057 }
1058 } else if (node->nodeType() == Node::DOCUMENT_TYPE_NODE) {
1059 DocumentType* docType = static_cast<DocumentType*>(node);
1060 value->setString("publicId", docType->publicId());
1061 value->setString("systemId", docType->systemId());
1062 value->setString("internalSubset", docType->internalSubset());
1063 } else if (node->nodeType() == Node::ATTRIBUTE_NODE) {
1064 Attr* attribute = static_cast<Attr*>(node);
1065 value->setString("name", attribute->name());
1066 value->setString("value", attribute->value());
1067 }
1068 return value.release();
1069 }
1070
buildArrayForElementAttributes(Element * element)1071 PassRefPtr<InspectorArray> InspectorDOMAgent::buildArrayForElementAttributes(Element* element)
1072 {
1073 RefPtr<InspectorArray> attributesValue = InspectorArray::create();
1074 // Go through all attributes and serialize them.
1075 const NamedNodeMap* attrMap = element->attributes(true);
1076 if (!attrMap)
1077 return attributesValue.release();
1078 unsigned numAttrs = attrMap->length();
1079 for (unsigned i = 0; i < numAttrs; ++i) {
1080 // Add attribute pair
1081 const Attribute *attribute = attrMap->attributeItem(i);
1082 attributesValue->pushString(attribute->name().toString());
1083 attributesValue->pushString(attribute->value());
1084 }
1085 return attributesValue.release();
1086 }
1087
buildArrayForContainerChildren(Node * container,int depth,NodeToIdMap * nodesMap)1088 PassRefPtr<InspectorArray> InspectorDOMAgent::buildArrayForContainerChildren(Node* container, int depth, NodeToIdMap* nodesMap)
1089 {
1090 RefPtr<InspectorArray> children = InspectorArray::create();
1091 Node* child = innerFirstChild(container);
1092
1093 if (depth == 0) {
1094 // Special-case the only text child - pretend that container's children have been requested.
1095 if (child && child->nodeType() == Node::TEXT_NODE && !innerNextSibling(child))
1096 return buildArrayForContainerChildren(container, 1, nodesMap);
1097 return children.release();
1098 }
1099
1100 depth--;
1101 m_childrenRequested.add(bind(container, nodesMap));
1102
1103 while (child) {
1104 children->pushObject(buildObjectForNode(child, depth, nodesMap));
1105 child = innerNextSibling(child);
1106 }
1107 return children.release();
1108 }
1109
buildObjectForEventListener(const RegisteredEventListener & registeredEventListener,const AtomicString & eventType,Node * node)1110 PassRefPtr<InspectorObject> InspectorDOMAgent::buildObjectForEventListener(const RegisteredEventListener& registeredEventListener, const AtomicString& eventType, Node* node)
1111 {
1112 RefPtr<EventListener> eventListener = registeredEventListener.listener;
1113 RefPtr<InspectorObject> value = InspectorObject::create();
1114 value->setString("type", eventType);
1115 value->setBoolean("useCapture", registeredEventListener.useCapture);
1116 value->setBoolean("isAttribute", eventListener->isAttribute());
1117 value->setNumber("nodeId", pushNodePathToFrontend(node));
1118 value->setString("listenerBody", eventListenerHandlerBody(node->document(), eventListener.get()));
1119 String sourceName;
1120 int lineNumber;
1121 if (eventListenerHandlerLocation(node->document(), eventListener.get(), sourceName, lineNumber)) {
1122 value->setString("sourceName", sourceName);
1123 value->setNumber("lineNumber", lineNumber);
1124 }
1125 return value.release();
1126 }
1127
innerFirstChild(Node * node)1128 Node* InspectorDOMAgent::innerFirstChild(Node* node)
1129 {
1130 if (node->isFrameOwnerElement()) {
1131 HTMLFrameOwnerElement* frameOwner = static_cast<HTMLFrameOwnerElement*>(node);
1132 Document* doc = frameOwner->contentDocument();
1133 if (doc)
1134 return doc->firstChild();
1135 }
1136 node = node->firstChild();
1137 while (isWhitespace(node))
1138 node = node->nextSibling();
1139 return node;
1140 }
1141
innerNextSibling(Node * node)1142 Node* InspectorDOMAgent::innerNextSibling(Node* node)
1143 {
1144 do {
1145 node = node->nextSibling();
1146 } while (isWhitespace(node));
1147 return node;
1148 }
1149
innerPreviousSibling(Node * node)1150 Node* InspectorDOMAgent::innerPreviousSibling(Node* node)
1151 {
1152 do {
1153 node = node->previousSibling();
1154 } while (isWhitespace(node));
1155 return node;
1156 }
1157
innerChildNodeCount(Node * node)1158 unsigned InspectorDOMAgent::innerChildNodeCount(Node* node)
1159 {
1160 unsigned count = 0;
1161 Node* child = innerFirstChild(node);
1162 while (child) {
1163 count++;
1164 child = innerNextSibling(child);
1165 }
1166 return count;
1167 }
1168
innerParentNode(Node * node)1169 Node* InspectorDOMAgent::innerParentNode(Node* node)
1170 {
1171 ContainerNode* parent = node->parentNode();
1172 if (parent && parent->isDocumentNode())
1173 return static_cast<Document*>(parent)->ownerElement();
1174 return parent;
1175 }
1176
isWhitespace(Node * node)1177 bool InspectorDOMAgent::isWhitespace(Node* node)
1178 {
1179 //TODO: pull ignoreWhitespace setting from the frontend and use here.
1180 return node && node->nodeType() == Node::TEXT_NODE && node->nodeValue().stripWhiteSpace().length() == 0;
1181 }
1182
mainFrameDOMContentLoaded()1183 void InspectorDOMAgent::mainFrameDOMContentLoaded()
1184 {
1185 // Re-push document once it is loaded.
1186 discardBindings();
1187 if (m_inspectorState->getBoolean(DOMAgentState::documentRequested))
1188 m_frontend->documentUpdated();
1189 }
1190
loadEventFired(Document * document)1191 void InspectorDOMAgent::loadEventFired(Document* document)
1192 {
1193 Element* frameOwner = document->ownerElement();
1194 if (!frameOwner)
1195 return;
1196
1197 int frameOwnerId = m_documentNodeToIdMap.get(frameOwner);
1198 if (!frameOwnerId)
1199 return;
1200
1201 if (!m_childrenRequested.contains(frameOwnerId)) {
1202 // No children are mapped yet -> only notify on changes of hasChildren.
1203 m_frontend->childNodeCountUpdated(frameOwnerId, innerChildNodeCount(frameOwner));
1204 } else {
1205 // Re-add frame owner element together with its new children.
1206 int parentId = m_documentNodeToIdMap.get(innerParentNode(frameOwner));
1207 m_frontend->childNodeRemoved(parentId, frameOwnerId);
1208 RefPtr<InspectorObject> value = buildObjectForNode(frameOwner, 0, &m_documentNodeToIdMap);
1209 Node* previousSibling = innerPreviousSibling(frameOwner);
1210 int prevId = previousSibling ? m_documentNodeToIdMap.get(previousSibling) : 0;
1211 m_frontend->childNodeInserted(parentId, prevId, value.release());
1212 // Invalidate children requested flag for the element.
1213 m_childrenRequested.remove(m_childrenRequested.find(frameOwnerId));
1214 }
1215 }
1216
didInsertDOMNode(Node * node)1217 void InspectorDOMAgent::didInsertDOMNode(Node* node)
1218 {
1219 if (isWhitespace(node))
1220 return;
1221
1222 // We could be attaching existing subtree. Forget the bindings.
1223 unbind(node, &m_documentNodeToIdMap);
1224
1225 ContainerNode* parent = node->parentNode();
1226 int parentId = m_documentNodeToIdMap.get(parent);
1227 // Return if parent is not mapped yet.
1228 if (!parentId)
1229 return;
1230
1231 if (!m_childrenRequested.contains(parentId)) {
1232 // No children are mapped yet -> only notify on changes of hasChildren.
1233 m_frontend->childNodeCountUpdated(parentId, innerChildNodeCount(parent));
1234 } else {
1235 // Children have been requested -> return value of a new child.
1236 Node* prevSibling = innerPreviousSibling(node);
1237 int prevId = prevSibling ? m_documentNodeToIdMap.get(prevSibling) : 0;
1238 RefPtr<InspectorObject> value = buildObjectForNode(node, 0, &m_documentNodeToIdMap);
1239 m_frontend->childNodeInserted(parentId, prevId, value.release());
1240 }
1241 }
1242
didRemoveDOMNode(Node * node)1243 void InspectorDOMAgent::didRemoveDOMNode(Node* node)
1244 {
1245 if (isWhitespace(node))
1246 return;
1247
1248 ContainerNode* parent = node->parentNode();
1249 int parentId = m_documentNodeToIdMap.get(parent);
1250 // If parent is not mapped yet -> ignore the event.
1251 if (!parentId)
1252 return;
1253
1254 if (m_domListener)
1255 m_domListener->didRemoveDOMNode(node);
1256
1257 if (!m_childrenRequested.contains(parentId)) {
1258 // No children are mapped yet -> only notify on changes of hasChildren.
1259 if (innerChildNodeCount(parent) == 1)
1260 m_frontend->childNodeCountUpdated(parentId, 0);
1261 } else
1262 m_frontend->childNodeRemoved(parentId, m_documentNodeToIdMap.get(node));
1263 unbind(node, &m_documentNodeToIdMap);
1264 }
1265
didModifyDOMAttr(Element * element)1266 void InspectorDOMAgent::didModifyDOMAttr(Element* element)
1267 {
1268 int id = m_documentNodeToIdMap.get(element);
1269 // If node is not mapped yet -> ignore the event.
1270 if (!id)
1271 return;
1272
1273 if (m_domListener)
1274 m_domListener->didModifyDOMAttr(element);
1275
1276 m_frontend->attributesUpdated(id, buildArrayForElementAttributes(element));
1277 }
1278
characterDataModified(CharacterData * characterData)1279 void InspectorDOMAgent::characterDataModified(CharacterData* characterData)
1280 {
1281 int id = m_documentNodeToIdMap.get(characterData);
1282 if (!id)
1283 return;
1284 m_frontend->characterDataModified(id, characterData->data());
1285 }
1286
didInvalidateStyleAttr(Node * node)1287 void InspectorDOMAgent::didInvalidateStyleAttr(Node* node)
1288 {
1289 int id = m_documentNodeToIdMap.get(node);
1290 // If node is not mapped yet -> ignore the event.
1291 if (!id)
1292 return;
1293
1294 if (!m_revalidateStyleAttrTask)
1295 m_revalidateStyleAttrTask = new RevalidateStyleAttributeTask(this);
1296 m_revalidateStyleAttrTask->scheduleFor(static_cast<Element*>(node));
1297 }
1298
nodeForPath(const String & path)1299 Node* InspectorDOMAgent::nodeForPath(const String& path)
1300 {
1301 // The path is of form "1,HTML,2,BODY,1,DIV"
1302 if (!m_document)
1303 return 0;
1304
1305 Node* node = m_document.get();
1306 Vector<String> pathTokens;
1307 path.split(",", false, pathTokens);
1308 if (!pathTokens.size())
1309 return 0;
1310 for (size_t i = 0; i < pathTokens.size() - 1; i += 2) {
1311 bool success = true;
1312 unsigned childNumber = pathTokens[i].toUInt(&success);
1313 if (!success)
1314 return 0;
1315 if (childNumber >= innerChildNodeCount(node))
1316 return 0;
1317
1318 Node* child = innerFirstChild(node);
1319 String childName = pathTokens[i + 1];
1320 for (size_t j = 0; child && j < childNumber; ++j)
1321 child = innerNextSibling(child);
1322
1323 if (!child || child->nodeName() != childName)
1324 return 0;
1325 node = child;
1326 }
1327 return node;
1328 }
1329
toArray(const Vector<String> & data)1330 PassRefPtr<InspectorArray> InspectorDOMAgent::toArray(const Vector<String>& data)
1331 {
1332 RefPtr<InspectorArray> result = InspectorArray::create();
1333 for (unsigned i = 0; i < data.size(); ++i)
1334 result->pushString(data[i]);
1335 return result.release();
1336 }
1337
onMatchJobsTimer(Timer<InspectorDOMAgent> *)1338 void InspectorDOMAgent::onMatchJobsTimer(Timer<InspectorDOMAgent>*)
1339 {
1340 if (!m_pendingMatchJobs.size()) {
1341 ErrorString error;
1342 cancelSearch(&error);
1343 return;
1344 }
1345
1346 ListHashSet<Node*> resultCollector;
1347 MatchJob* job = m_pendingMatchJobs.takeFirst();
1348 job->match(resultCollector);
1349 delete job;
1350
1351 reportNodesAsSearchResults(resultCollector);
1352
1353 m_matchJobsTimer.startOneShot(0.025);
1354 }
1355
reportNodesAsSearchResults(ListHashSet<Node * > & resultCollector)1356 void InspectorDOMAgent::reportNodesAsSearchResults(ListHashSet<Node*>& resultCollector)
1357 {
1358 RefPtr<InspectorArray> nodeIds = InspectorArray::create();
1359 for (ListHashSet<Node*>::iterator it = resultCollector.begin(); it != resultCollector.end(); ++it) {
1360 if (m_searchResults.contains(*it))
1361 continue;
1362 m_searchResults.add(*it);
1363 nodeIds->pushNumber(pushNodePathToFrontend(*it));
1364 }
1365 m_frontend->searchResults(nodeIds.release());
1366 }
1367
copyNode(ErrorString *,int nodeId)1368 void InspectorDOMAgent::copyNode(ErrorString*, int nodeId)
1369 {
1370 Node* node = nodeForId(nodeId);
1371 if (!node)
1372 return;
1373 String markup = createMarkup(node);
1374 Pasteboard::generalPasteboard()->writePlainText(markup);
1375 }
1376
pushNodeByPathToFrontend(ErrorString *,const String & path,int * nodeId)1377 void InspectorDOMAgent::pushNodeByPathToFrontend(ErrorString*, const String& path, int* nodeId)
1378 {
1379 if (Node* node = nodeForPath(path))
1380 *nodeId = pushNodePathToFrontend(node);
1381 }
1382
resolveNode(Node * node)1383 PassRefPtr<InspectorObject> InspectorDOMAgent::resolveNode(Node* node)
1384 {
1385 Document* document = node->ownerDocument();
1386 Frame* frame = document ? document->frame() : 0;
1387 if (!frame)
1388 return 0;
1389
1390 InjectedScript injectedScript = m_injectedScriptManager->injectedScriptFor(mainWorldScriptState(frame));
1391 if (injectedScript.hasNoValue())
1392 return 0;
1393
1394 return injectedScript.wrapNode(node);
1395 }
1396
drawNodeHighlight(GraphicsContext & context) const1397 void InspectorDOMAgent::drawNodeHighlight(GraphicsContext& context) const
1398 {
1399 if (!m_highlightedNode)
1400 return;
1401
1402 DOMNodeHighlighter::DrawNodeHighlight(context, m_highlightedNode.get());
1403 }
1404
1405 } // namespace WebCore
1406
1407 #endif // ENABLE(INSPECTOR)
1408