Web Inspector: Replace last use of ObjectPropertiesSection with ObjectTreeView
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / Views / DOMNodeDetailsSidebarPanel.js
1 /*
2  * Copyright (C) 2013, 2014 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 WebInspector.DOMNodeDetailsSidebarPanel = function()
27 {
28     WebInspector.DOMDetailsSidebarPanel.call(this, "dom-node-details", WebInspector.UIString("Node"), WebInspector.UIString("Node"), "Images/NavigationItemAngleBrackets.svg", "2");
29
30     WebInspector.domTreeManager.addEventListener(WebInspector.DOMTreeManager.Event.AttributeModified, this._attributesChanged, this);
31     WebInspector.domTreeManager.addEventListener(WebInspector.DOMTreeManager.Event.AttributeRemoved, this._attributesChanged, this);
32     WebInspector.domTreeManager.addEventListener(WebInspector.DOMTreeManager.Event.CharacterDataModified, this._characterDataModified, this);
33
34     this.element.classList.add(WebInspector.DOMNodeDetailsSidebarPanel.StyleClassName);
35
36     this._identityNodeTypeRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Type"));
37     this._identityNodeNameRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Name"));
38     this._identityNodeValueRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Value"));
39
40     var identityGroup = new WebInspector.DetailsSectionGroup([this._identityNodeTypeRow, this._identityNodeNameRow, this._identityNodeValueRow]);
41     var identitySection = new WebInspector.DetailsSection("dom-node-identity", WebInspector.UIString("Identity"), [identityGroup]);
42
43     this._attributesDataGridRow = new WebInspector.DetailsSectionDataGridRow(null, WebInspector.UIString("No Attributes"));
44
45     var attributesGroup = new WebInspector.DetailsSectionGroup([this._attributesDataGridRow]);
46     var attributesSection = new WebInspector.DetailsSection("dom-node-attributes", WebInspector.UIString("Attributes"), [attributesGroup]);
47
48     this._propertiesRow = new WebInspector.DetailsSectionRow;
49
50     var propertiesGroup = new WebInspector.DetailsSectionGroup([this._propertiesRow]);
51     var propertiesSection = new WebInspector.DetailsSection("dom-node-properties", WebInspector.UIString("Properties"), [propertiesGroup]);
52
53     this._eventListenersSectionGroup = new WebInspector.DetailsSectionGroup;
54     var eventListenersSection = new WebInspector.DetailsSection("dom-node-event-listeners", WebInspector.UIString("Event Listeners"), [this._eventListenersSectionGroup]);    
55
56     this.contentElement.appendChild(identitySection.element);
57     this.contentElement.appendChild(attributesSection.element);
58     this.contentElement.appendChild(propertiesSection.element);
59     this.contentElement.appendChild(eventListenersSection.element);
60
61     if (this._accessibilitySupported()) {
62         this._accessibilityEmptyRow = new WebInspector.DetailsSectionRow(WebInspector.UIString("No Accessibility Information"));
63         this._accessibilityNodeActiveDescendantRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Shared Focus"));
64         this._accessibilityNodeBusyRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Busy"));
65         this._accessibilityNodeCheckedRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Checked"));
66         this._accessibilityNodeChildrenRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Children"));
67         this._accessibilityNodeControlsRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Controls"));
68         this._accessibilityNodeDisabledRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Disabled"));
69         this._accessibilityNodeExpandedRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Expanded"));
70         this._accessibilityNodeFlowsRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Flows"));
71         this._accessibilityNodeFocusedRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Focused"));
72         this._accessibilityNodeIgnoredRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Ignored"));
73         this._accessibilityNodeInvalidRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Invalid"));
74         this._accessibilityNodeLiveRegionStatusRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Live"));
75         this._accessibilityNodeMouseEventRow = new WebInspector.DetailsSectionSimpleRow("");
76         this._accessibilityNodeLabelRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Label"));
77         this._accessibilityNodeOwnsRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Owns"));
78         this._accessibilityNodeParentRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Parent"));
79         this._accessibilityNodePressedRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Pressed"));
80         this._accessibilityNodeReadonlyRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Readonly"));
81         this._accessibilityNodeRequiredRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Required"));
82         this._accessibilityNodeRoleRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Role"));
83         this._accessibilityNodeSelectedRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Selected"));
84         this._accessibilityNodeSelectedChildrenRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Selected Items"));
85     
86         this._accessibilityGroup = new WebInspector.DetailsSectionGroup([this._accessibilityEmptyRow]);
87         var accessibilitySection = new WebInspector.DetailsSection("dom-node-accessibility", WebInspector.UIString("Accessibility"), [this._accessibilityGroup]);    
88
89         this.contentElement.appendChild(accessibilitySection.element);
90     }
91 };
92
93 WebInspector.DOMNodeDetailsSidebarPanel.StyleClassName = "dom-node";
94 WebInspector.DOMNodeDetailsSidebarPanel.PropertiesObjectGroupName = "dom-node-details-sidebar-properties-object-group";
95
96 WebInspector.DOMNodeDetailsSidebarPanel.prototype = {
97     constructor: WebInspector.DOMNodeDetailsSidebarPanel,
98     __proto__: WebInspector.DOMDetailsSidebarPanel.prototype,
99
100     // Public
101
102     refresh: function()
103     {
104         var domNode = this.domNode;
105         if (!domNode)
106             return;
107
108         this._identityNodeTypeRow.value = this._nodeTypeDisplayName();
109         this._identityNodeNameRow.value = domNode.nodeNameInCorrectCase();
110         this._identityNodeValueRow.value = domNode.nodeValue();
111
112         this._refreshAttributes();
113         this._refreshProperties();
114         this._refreshEventListeners();
115         this._refreshAccessibility();
116     },
117
118     // Private
119
120     _accessibilitySupported: function()
121     {
122         return window.DOMAgent && DOMAgent.getAccessibilityPropertiesForNode;
123     },
124
125     _refreshAttributes: function()
126     {
127         this._attributesDataGridRow.dataGrid = this._createAttributesDataGrid();
128     },
129
130     _refreshProperties: function()
131     {
132         var domNode = this.domNode;
133         if (!domNode)
134             return;
135
136         RuntimeAgent.releaseObjectGroup(WebInspector.DOMNodeDetailsSidebarPanel.PropertiesObjectGroupName);
137         WebInspector.RemoteObject.resolveNode(domNode, WebInspector.DOMNodeDetailsSidebarPanel.PropertiesObjectGroupName, nodeResolved.bind(this));
138
139         function nodeResolved(object)
140         {
141             if (!object)
142                 return;
143
144             // Bail if the DOM node changed while we were waiting for the async response.
145             if (this.domNode !== domNode)
146                 return;
147
148             function collectPrototypes()
149             {
150                 // This builds an object with numeric properties. This is easier than dealing with arrays
151                 // with the way RemoteObject works. Start at 1 since we use parseInt later and parseInt
152                 // returns 0 for non-numeric strings make it ambiguous.
153                 var prototype = this;
154                 var result = [];
155                 var counter = 1;
156
157                 while (prototype) {
158                     result[counter++] = prototype;
159                     prototype = prototype.__proto__;
160                 }
161
162                 return result;
163             }
164
165             object.callFunction(collectPrototypes, undefined, false, nodePrototypesReady.bind(this));
166             object.release();
167         }
168
169         function nodePrototypesReady(error, object, wasThrown)
170         {
171             if (error || wasThrown || !object)
172                 return;
173
174             // Bail if the DOM node changed while we were waiting for the async response.
175             if (this.domNode !== domNode)
176                 return;
177
178             object.deprecatedGetOwnProperties(fillSection.bind(this));
179         }
180
181         function fillSection(prototypes)
182         {
183             if (!prototypes)
184                 return;
185
186             // Bail if the DOM node changed while we were waiting for the async response.
187             if (this.domNode !== domNode)
188                 return;
189
190             var element = this._propertiesRow.element;
191             element.removeChildren();
192
193             // Get array of prototype user-friendly names.
194             for (var i = 0; i < prototypes.length; ++i) {
195                 // The only values we care about are numeric, as assigned in collectPrototypes.
196                 if (!parseInt(prototypes[i].name, 10))
197                     continue;
198
199                 var prototype = prototypes[i].value;
200                 var title = prototype.description;
201                 if (title.match(/Prototype$/))
202                     title = title.replace(/Prototype$/, WebInspector.UIString(" (Prototype)"));
203                 else if (title === "Object")
204                     title = title + WebInspector.UIString(" (Prototype)");
205
206                 // FIXME: <https://webkit.org/b/142833> Web Inspector: Node Details Sidebar Properties Section has "undefined" for all prototype properties
207
208                 var objectTree = new WebInspector.ObjectTreeView(prototype, WebInspector.ObjectTreeView.Mode.Properties);
209                 objectTree.showOnlyProperties();
210
211                 var detailsSection = new WebInspector.DetailsSection(prototype.description.hash + "-prototype-properties", title, null, null, true);
212                 detailsSection.groups[0].rows = [new WebInspector.DetailsSectionPropertiesRow(objectTree)];
213
214                 element.appendChild(detailsSection.element);
215             }
216         }
217     },
218
219     _refreshEventListeners: function()
220     {
221         var domNode = this.domNode;
222         if (!domNode)
223             return;
224
225         domNode.eventListeners(eventListenersCallback.bind(this));
226
227         function eventListenersCallback(error, eventListeners)
228         {
229             if (error)
230                 return;
231
232             // Bail if the DOM node changed while we were waiting for the async response.
233             if (this.domNode !== domNode)
234                 return;
235
236             var eventListenerTypes = [];
237             var eventListenerSections = {};
238             for (var i = 0; i < eventListeners.length; ++i) {
239                 var eventListener = eventListeners[i];
240                 eventListener.node = WebInspector.domTreeManager.nodeForId(eventListener.nodeId);
241
242                 var type = eventListener.type;
243                 var section = eventListenerSections[type];
244                 if (!section) {
245                     section = new WebInspector.EventListenerSection(type, domNode.id);
246                     eventListenerSections[type] = section;
247                     eventListenerTypes.push(type);
248                 }
249
250                 section.addListener(eventListener);
251             }
252
253             if (!eventListenerTypes.length) {
254                 var emptyRow = new WebInspector.DetailsSectionRow(WebInspector.UIString("No Event Listeners"));
255                 emptyRow.showEmptyMessage();
256                 this._eventListenersSectionGroup.rows = [emptyRow];
257                 return;
258             }
259
260             eventListenerTypes.sort();
261
262             var rows = [];
263             for (var i = 0; i < eventListenerTypes.length; ++i)
264                 rows.push(eventListenerSections[eventListenerTypes[i]]);
265             this._eventListenersSectionGroup.rows = rows;
266         }
267     },
268
269     _refreshAccessibility: (function() {
270         var properties = {};
271         var domNode;
272
273         function booleanValueToLocalizedStringIfTrue(property) {
274             if (properties[property])
275                 return WebInspector.UIString("Yes");
276             return "";
277         }
278
279         function booleanValueToLocalizedStringIfPropertyDefined(property) {
280             if (properties[property] !== undefined) {
281                 if (properties[property])
282                     return WebInspector.UIString("Yes");
283                 else
284                     return WebInspector.UIString("No");
285             }
286             return "";
287         }
288
289         function linkForNodeId(nodeId) {
290             var link = null;
291             if (nodeId !== undefined && typeof nodeId === "number") {
292                 var node = WebInspector.domTreeManager.nodeForId(nodeId);
293                 if (node)
294                     link = WebInspector.linkifyAccessibilityNodeReference(node);
295             }
296             return link;
297         }
298
299         function linkListForNodeIds(nodeIds) {
300             var hasLinks = false;
301             var linkList = null;
302             if (nodeIds !== undefined) {
303                 linkList = document.createElement("ul");
304                 linkList.className = "node-link-list";    
305                 for (var nodeId of nodeIds) {
306                     var node = WebInspector.domTreeManager.nodeForId(nodeId);
307                     if (node) {
308                         var link = WebInspector.linkifyAccessibilityNodeReference(node);
309                         if (link) {
310                             hasLinks = true;
311                             var listitem = document.createElement("li");
312                             listitem.appendChild(link);
313                             linkList.appendChild(listitem);
314                         }
315                     }
316                 }
317             }
318             return hasLinks ? linkList : null;
319         }
320
321         function accessibilityPropertiesCallback(accessibilityProperties) {
322             if (this.domNode !== domNode)
323                 return;
324
325             // Make sure the current set of properties is available in the closure scope for the helper functions.
326             properties = accessibilityProperties;
327
328             if (accessibilityProperties && accessibilityProperties.exists) {
329
330                 var activeDescendantLink = linkForNodeId(accessibilityProperties.activeDescendantNodeId);
331                 var busy = booleanValueToLocalizedStringIfPropertyDefined("busy");
332
333                 var checked = "";
334                 if (accessibilityProperties.checked !== undefined) {
335                     if (accessibilityProperties.checked === DOMAgent.AccessibilityPropertiesChecked.True)
336                         checked = WebInspector.UIString("Yes");
337                     else if (accessibilityProperties.checked === DOMAgent.AccessibilityPropertiesChecked.Mixed)
338                         checked = WebInspector.UIString("Mixed");
339                     else // DOMAgent.AccessibilityPropertiesChecked.False
340                         checked = WebInspector.UIString("No");
341                 }
342
343                 // Accessibility tree children are not a 1:1 mapping with DOM tree children.
344                 var childNodeLinkList = linkListForNodeIds(accessibilityProperties.childNodeIds);
345                 
346                 var controlledNodeLinkList = linkListForNodeIds(accessibilityProperties.controlledNodeIds);
347                 var disabled = booleanValueToLocalizedStringIfTrue("disabled");
348                 var expanded = booleanValueToLocalizedStringIfPropertyDefined("expanded");
349                 var flowedNodeLinkList = linkListForNodeIds(accessibilityProperties.flowedNodeIds);
350                 var focused = booleanValueToLocalizedStringIfPropertyDefined("focused");
351                 
352                 var ignored = "";
353                 if (accessibilityProperties.ignored) {
354                     ignored = WebInspector.UIString("Yes");
355                     if (accessibilityProperties.hidden)
356                         ignored = WebInspector.UIString("%s (hidden)").format(ignored);
357                     else if (accessibilityProperties.ignoredByDefault)
358                         ignored = WebInspector.UIString("%s (default)").format(ignored);
359                 }
360
361                 var invalid = "";
362                 if (accessibilityProperties.invalid === DOMAgent.AccessibilityPropertiesInvalid.True)
363                     invalid = WebInspector.UIString("Yes");
364                 else if (accessibilityProperties.invalid === DOMAgent.AccessibilityPropertiesInvalid.Grammar)
365                     invalid = WebInspector.UIString("Grammar");
366                 else if (accessibilityProperties.invalid === DOMAgent.AccessibilityPropertiesInvalid.Spelling)
367                     invalid = WebInspector.UIString("Spelling");
368
369                 var label = accessibilityProperties.label;
370
371                 var liveRegionStatus = "";
372                 var liveRegionStatusNode = null;
373                 var liveRegionStatusToken = accessibilityProperties.liveRegionStatus;
374                 switch(liveRegionStatusToken) {
375                 case DOMAgent.AccessibilityPropertiesLiveRegionStatus.Assertive:
376                     liveRegionStatus = WebInspector.UIString("Assertive");
377                     break;
378                 case DOMAgent.AccessibilityPropertiesLiveRegionStatus.Polite:
379                     liveRegionStatus = WebInspector.UIString("Polite");
380                     break;
381                 default:
382                     liveRegionStatus = "";
383                 }
384                 if (liveRegionStatus) {
385                     var liveRegionRelevant = accessibilityProperties.liveRegionRelevant;
386                     // Append @aria-relevant values. E.g. "Live: Assertive (Additions, Text)".
387                     if (liveRegionRelevant && liveRegionRelevant.length) {
388                         // @aria-relevant="all" is exposed as ["additions","removals","text"], in order.
389                         // This order is controlled in WebCore and expected in WebInspectorUI.
390                         if (liveRegionRelevant.length === 3 
391                             && liveRegionRelevant[0] === DOMAgent.LiveRegionRelevant.Additions
392                             && liveRegionRelevant[1] === DOMAgent.LiveRegionRelevant.Removals
393                             && liveRegionRelevant[2] === DOMAgent.LiveRegionRelevant.Text)
394                             liveRegionRelevant = [WebInspector.UIString("All Changes")];
395                         else {
396                             // Reassign localized strings in place: ["additions","text"] becomes ["Additions","Text"].
397                             liveRegionRelevant = liveRegionRelevant.map(function(value) {
398                                 switch (value) {
399                                 case DOMAgent.LiveRegionRelevant.Additions:
400                                     return WebInspector.UIString("Additions");
401                                 case DOMAgent.LiveRegionRelevant.Removals:
402                                     return WebInspector.UIString("Removals");
403                                 case DOMAgent.LiveRegionRelevant.Text:
404                                     return WebInspector.UIString("Text");
405                                 default: // If WebCore sends a new unhandled value, display as a String.
406                                     return "\"" + value + "\"";
407                                 }
408                             });
409                         }
410                         liveRegionStatus += " (" + liveRegionRelevant.join(", ") + ")";
411                     }
412                     // Clarify @aria-atomic if necessary.
413                     if (accessibilityProperties.liveRegionAtomic) {
414                         liveRegionStatusNode = document.createElement("div");
415                         liveRegionStatusNode.className = "value-with-clarification";
416                         liveRegionStatusNode.setAttribute("role", "text");
417                         liveRegionStatusNode.appendChild(document.createTextNode(liveRegionStatus));
418                         var clarificationNode = document.createElement("div");
419                         clarificationNode.className = "clarification";
420                         clarificationNode.appendChild(document.createTextNode(WebInspector.UIString("Region announced in its entirety.")));
421                         liveRegionStatusNode.appendChild(clarificationNode);
422                     }
423                 }
424
425                 var mouseEventNodeId = accessibilityProperties.mouseEventNodeId;
426                 var mouseEventTextValue = "";
427                 var mouseEventNodeLink = null;
428                 if (mouseEventNodeId) {
429                     if (mouseEventNodeId === accessibilityProperties.nodeId)
430                         mouseEventTextValue = WebInspector.UIString("Yes");
431                     else
432                         mouseEventNodeLink = linkForNodeId(mouseEventNodeId);
433                 }
434
435                 var ownedNodeLinkList = linkListForNodeIds(accessibilityProperties.ownedNodeIds);
436
437                 // Accessibility tree parent is not a 1:1 mapping with the DOM tree parent.
438                 var parentNodeLink = linkForNodeId(accessibilityProperties.parentNodeId);
439
440                 var pressed = booleanValueToLocalizedStringIfPropertyDefined("pressed");
441                 var readonly = booleanValueToLocalizedStringIfTrue("readonly");
442                 var required = booleanValueToLocalizedStringIfPropertyDefined("required");
443
444                 var role = accessibilityProperties.role;
445                 if (role === "" || role === "unknown")
446                     role = WebInspector.UIString("No exact ARIA role match.");
447                 else if (role) {
448                     if (!domNode.getAttribute("role"))
449                         role = WebInspector.UIString("%s (default)").format(role);
450                     else if (domNode.getAttribute("role") !== role)
451                         role = WebInspector.UIString("%s (computed)").format(role);
452                 }
453
454                 var selected = booleanValueToLocalizedStringIfTrue("selected");
455                 var selectedChildNodeLinkList = linkListForNodeIds(accessibilityProperties.selectedChildNodeIds);
456
457                 // Assign all the properties to their respective views.
458                 this._accessibilityNodeActiveDescendantRow.value = activeDescendantLink || "";
459                 this._accessibilityNodeBusyRow.value = busy;
460                 this._accessibilityNodeCheckedRow.value = checked;
461                 this._accessibilityNodeChildrenRow.value = childNodeLinkList || "";
462                 this._accessibilityNodeControlsRow.value = controlledNodeLinkList || "";
463                 this._accessibilityNodeDisabledRow.value = disabled;
464                 this._accessibilityNodeExpandedRow.value = expanded;
465                 this._accessibilityNodeFlowsRow.value = flowedNodeLinkList || "";
466                 this._accessibilityNodeFocusedRow.value = focused;
467                 this._accessibilityNodeIgnoredRow.value = ignored;
468                 this._accessibilityNodeInvalidRow.value = invalid;
469                 this._accessibilityNodeLabelRow.value = label;
470                 this._accessibilityNodeLiveRegionStatusRow.value = liveRegionStatusNode || liveRegionStatus;
471                 
472                 // Row label changes based on whether the value is a delegate node link.
473                 this._accessibilityNodeMouseEventRow.label = mouseEventNodeLink ? WebInspector.UIString("Click Listener") : WebInspector.UIString("Clickable");
474                 this._accessibilityNodeMouseEventRow.value = mouseEventNodeLink || mouseEventTextValue;
475
476                 this._accessibilityNodeOwnsRow.value = ownedNodeLinkList || "";
477                 this._accessibilityNodeParentRow.value = parentNodeLink || "";
478                 this._accessibilityNodePressedRow.value = pressed;
479                 this._accessibilityNodeReadonlyRow.value = readonly;
480                 this._accessibilityNodeRequiredRow.value = required;
481                 this._accessibilityNodeRoleRow.value = role;
482                 this._accessibilityNodeSelectedRow.value = selected;
483
484                 this._accessibilityNodeSelectedChildrenRow.label = WebInspector.UIString("Selected Items");
485                 this._accessibilityNodeSelectedChildrenRow.value = selectedChildNodeLinkList || "";
486                 if (selectedChildNodeLinkList && accessibilityProperties.selectedChildNodeIds.length === 1)
487                     this._accessibilityNodeSelectedChildrenRow.label = WebInspector.UIString("Selected Item");                
488
489                 // Display order, not alphabetical as above.
490                 this._accessibilityGroup.rows = [
491                     // Global properties for all elements.
492                     this._accessibilityNodeIgnoredRow,
493                     this._accessibilityNodeRoleRow,
494                     this._accessibilityNodeLabelRow,
495                     this._accessibilityNodeParentRow,
496                     this._accessibilityNodeActiveDescendantRow,
497                     this._accessibilityNodeSelectedChildrenRow,
498                     this._accessibilityNodeChildrenRow,
499                     this._accessibilityNodeOwnsRow,
500                     this._accessibilityNodeControlsRow,
501                     this._accessibilityNodeFlowsRow,
502                     this._accessibilityNodeMouseEventRow,
503                     this._accessibilityNodeFocusedRow,
504                     this._accessibilityNodeBusyRow,
505                     this._accessibilityNodeLiveRegionStatusRow,
506
507                     // Properties exposed for all input-type elements.
508                     this._accessibilityNodeDisabledRow,
509                     this._accessibilityNodeInvalidRow,
510                     this._accessibilityNodeRequiredRow,
511
512                     // Role-specific properties.
513                     this._accessibilityNodeCheckedRow,
514                     this._accessibilityNodeExpandedRow,
515                     this._accessibilityNodePressedRow,
516                     this._accessibilityNodeReadonlyRow,
517                     this._accessibilityNodeSelectedRow
518                 ];
519
520                 this._accessibilityEmptyRow.hideEmptyMessage();
521
522             } else {
523                 this._accessibilityGroup.rows = [this._accessibilityEmptyRow];
524                 this._accessibilityEmptyRow.showEmptyMessage();
525             }
526         }
527
528         function refreshAX() {
529             if (!this._accessibilitySupported())
530                 return;
531
532             // Make sure the domNode is available in the closure scope.
533             domNode = this.domNode;
534             if (!domNode)
535                 return;
536
537             domNode.accessibilityProperties(accessibilityPropertiesCallback.bind(this));
538         }
539
540         return refreshAX;
541     }()),
542
543     _attributesChanged: function(event)
544     {
545         if (event.data.node !== this.domNode)
546             return;
547         this._refreshAttributes();
548         this._refreshAccessibility();
549     },
550
551     _characterDataModified: function(event)
552     {
553         if (event.data.node !== this.domNode)
554             return;
555         this._identityNodeValueRow.value = this.domNode.nodeValue();
556     },
557
558     _nodeTypeDisplayName: function()
559     {
560         switch (this.domNode.nodeType()) {
561         case Node.ELEMENT_NODE:
562             return WebInspector.UIString("Element");
563         case Node.TEXT_NODE:
564             return WebInspector.UIString("Text Node");
565         case Node.COMMENT_NODE:
566             return WebInspector.UIString("Comment");
567         case Node.DOCUMENT_NODE:
568             return WebInspector.UIString("Document");
569         case Node.DOCUMENT_TYPE_NODE:
570             return WebInspector.UIString("Document Type");
571         case Node.DOCUMENT_FRAGMENT_NODE:
572             return WebInspector.UIString("Document Fragment");
573         case Node.CDATA_SECTION_NODE:
574             return WebInspector.UIString("Character Data");
575         case Node.PROCESSING_INSTRUCTION_NODE:
576             return WebInspector.UIString("Processing Instruction");
577         default:
578             console.error("Unknown DOM node type: ", this.domNode.nodeType());
579             return this.domNode.nodeType();
580         }
581     },
582
583     _createAttributesDataGrid: function()
584     {
585         var domNode = this.domNode;
586         if (!domNode || !domNode.hasAttributes())
587             return null;
588
589         var columns = {name: {title: WebInspector.UIString("Name"), width: "30%"}, value: {title: WebInspector.UIString("Value")}};
590         var dataGrid = new WebInspector.DataGrid(columns);
591
592         var attributes = domNode.attributes();
593         for (var i = 0; i < attributes.length; ++i) {
594             var attribute = attributes[i];
595
596             var node = new WebInspector.DataGridNode({name: attribute.name, value: attribute.value || ""}, false);
597             node.selectable = true;
598
599             dataGrid.appendChild(node);
600         }
601
602         return dataGrid;
603     }
604 };