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