<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[164052] trunk/Source/WebInspectorUI</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/164052">164052</a></dd>
<dt>Author</dt> <dd>bburg@apple.com</dd>
<dt>Date</dt> <dd>2014-02-13 13:38:05 -0800 (Thu, 13 Feb 2014)</dd>
</dl>

<h3>Log Message</h3>
<pre>Web Inspector: DataGrid should support editing tables with arbitrary columns
https://bugs.webkit.org/show_bug.cgi?id=128619

Reviewed by Timothy Hatcher.

The data grid editing code should work with any columns, but was previously
hardcoded with the column identifiers used by DOMStorageView. This patch
makes the editing code work with any column identifiers. It also fixes some
bugs in previous/next navigation during data entry.

* UserInterface/DOMStorageContentView.css: Highlight missing keys and values.
(.content-view.dom-storage &gt; .data-grid tr.missing-value td.value-column):
(.content-view.dom-storage &gt; .data-grid:focus tr.selected.missing-value td.value-column):

* UserInterface/DOMStorageContentView.js: Don't blow away the entire table
whenever the backend notifies us of an update to the storage object. This
caused the editing state to be destroyed when values were entered interactively.

(WebInspector.DOMStorageContentView.prototype.reset): Inline the callback
passed to DOMStorageObject.getEntries().
(WebInspector.DOMStorageContentView.prototype.itemsCleared):
(WebInspector.DOMStorageContentView.prototype.itemAdded): Request a sort
of the table rows after backend tells us about item additions or updates.

(WebInspector.DOMStorageContentView.prototype.itemUpdated): Request a sort
of the table rows after backend tells us about item additions or updates.

(WebInspector.DOMStorageContentView.prototype._sortDataGrid): Use DataGrid's
built-in sorting function.

(WebInspector.DOMStorageContentView.prototype._deleteCallback):
(WebInspector.DOMStorageContentView.prototype._editingCallback): Don't force
recreation of the entire table whenever editing finishes. Perform most
of the error checking and editing logic here, including when to insert a
new placeholder row, and when to commit the entered values to DOM storage.

* UserInterface/DOMStorageObject.js: Keep track of the DOM storage entries
in the model so we can detect duplicate entries. Remove unused `id` arguments.

(WebInspector.DOMStorageObject):
(WebInspector.DOMStorageObject.prototype.get entries):
(WebInspector.DOMStorageObject.prototype.itemsCleared):
(WebInspector.DOMStorageObject.prototype.itemRemoved):
(WebInspector.DOMStorageObject.prototype.set this):
(WebInspector.DOMStorageObject.prototype.itemAdded):
(WebInspector.DOMStorageObject.prototype.set var):
(WebInspector.DOMStorageObject.prototype.itemUpdated):

* UserInterface/DataGrid.js:
(.sortDataGrid):
(WebInspector.DataGrid.createSortableDataGrid): Use the built-in DataGrid
sortNodes() implementation.
(WebInspector.DataGrid.prototype._startEditingNodeAtColumnIndex): Renamed
from _startEditingColumnOfDataGridNode. It's easier to understand the navigation
code when presented in terms of adjusting column indexes rather than identifiers.

(WebInspector.DataGrid.prototype._startEditing):
(WebInspector.DataGrid.prototype.determineNextCell): Added. Decides which
column and row to edit next and whether the table can be sorted before the
next cell edit begins.

(WebInspector.DataGrid.prototype.moveToNextCell): Added. Wrapper method for
the above which handles sorting and beginning the next cell edit.

(WebInspector.DataGrid.prototype._editingCommitted): Use better helper methods.
(WebInspector.DataGrid.prototype._editingCancelled): Add an assert.
(WebInspector.DataGrid.prototype.get sortColumnIdentifier): Shorten.
(WebInspector.DataGrid.prototype.addPlaceholderNode):
(WebInspector.DataGrid.prototype.removeChild):
(WebInspector.DataGrid.prototype.):
(WebInspector.DataGrid.prototype.sortNodes): Remove unecessary copying
of all nodes into a separate array. Defer sorting if the user is in the
middle of editing a cell. Make placeholder nodes always sort to the bottom
regardless of the sort column identifier and sort direction.

(WebInspector.DataGridNode.prototype._attach):
(WebInspector.PlaceholderDataGridNode): Renamed from CreationDataGridNode.
(WebInspector.PlaceholderDataGridNode.prototype.makeNormal):

* UserInterface/StorageManager.js: Don't pass unused id argument.
(WebInspector.StorageManager.prototype.itemRemoved):
(WebInspector.StorageManager.prototype.itemAdded):
(WebInspector.StorageManager.prototype.itemUpdated):</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebInspectorUIChangeLog">trunk/Source/WebInspectorUI/ChangeLog</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceDOMStorageContentViewcss">trunk/Source/WebInspectorUI/UserInterface/DOMStorageContentView.css</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceDOMStorageContentViewjs">trunk/Source/WebInspectorUI/UserInterface/DOMStorageContentView.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceDOMStorageObjectjs">trunk/Source/WebInspectorUI/UserInterface/DOMStorageObject.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceDataGridjs">trunk/Source/WebInspectorUI/UserInterface/DataGrid.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceStorageManagerjs">trunk/Source/WebInspectorUI/UserInterface/StorageManager.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebInspectorUIChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/ChangeLog (164051 => 164052)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/ChangeLog        2014-02-13 21:37:59 UTC (rev 164051)
+++ trunk/Source/WebInspectorUI/ChangeLog        2014-02-13 21:38:05 UTC (rev 164052)
</span><span class="lines">@@ -1,5 +1,91 @@
</span><span class="cx"> 2014-02-13  Brian Burg  &lt;bburg@apple.com&gt;
</span><span class="cx"> 
</span><ins>+        Web Inspector: DataGrid should support editing tables with arbitrary columns
+        https://bugs.webkit.org/show_bug.cgi?id=128619
+
+        Reviewed by Timothy Hatcher.
+
+        The data grid editing code should work with any columns, but was previously
+        hardcoded with the column identifiers used by DOMStorageView. This patch
+        makes the editing code work with any column identifiers. It also fixes some
+        bugs in previous/next navigation during data entry.
+
+        * UserInterface/DOMStorageContentView.css: Highlight missing keys and values.
+        (.content-view.dom-storage &gt; .data-grid tr.missing-value td.value-column):
+        (.content-view.dom-storage &gt; .data-grid:focus tr.selected.missing-value td.value-column):
+
+        * UserInterface/DOMStorageContentView.js: Don't blow away the entire table
+        whenever the backend notifies us of an update to the storage object. This
+        caused the editing state to be destroyed when values were entered interactively.
+
+        (WebInspector.DOMStorageContentView.prototype.reset): Inline the callback
+        passed to DOMStorageObject.getEntries().
+        (WebInspector.DOMStorageContentView.prototype.itemsCleared):
+        (WebInspector.DOMStorageContentView.prototype.itemAdded): Request a sort
+        of the table rows after backend tells us about item additions or updates.
+
+        (WebInspector.DOMStorageContentView.prototype.itemUpdated): Request a sort
+        of the table rows after backend tells us about item additions or updates.
+
+        (WebInspector.DOMStorageContentView.prototype._sortDataGrid): Use DataGrid's
+        built-in sorting function.
+
+        (WebInspector.DOMStorageContentView.prototype._deleteCallback):
+        (WebInspector.DOMStorageContentView.prototype._editingCallback): Don't force
+        recreation of the entire table whenever editing finishes. Perform most
+        of the error checking and editing logic here, including when to insert a
+        new placeholder row, and when to commit the entered values to DOM storage.
+
+        * UserInterface/DOMStorageObject.js: Keep track of the DOM storage entries
+        in the model so we can detect duplicate entries. Remove unused `id` arguments.
+
+        (WebInspector.DOMStorageObject):
+        (WebInspector.DOMStorageObject.prototype.get entries):
+        (WebInspector.DOMStorageObject.prototype.itemsCleared):
+        (WebInspector.DOMStorageObject.prototype.itemRemoved):
+        (WebInspector.DOMStorageObject.prototype.set this):
+        (WebInspector.DOMStorageObject.prototype.itemAdded):
+        (WebInspector.DOMStorageObject.prototype.set var):
+        (WebInspector.DOMStorageObject.prototype.itemUpdated):
+
+        * UserInterface/DataGrid.js:
+        (.sortDataGrid):
+        (WebInspector.DataGrid.createSortableDataGrid): Use the built-in DataGrid
+        sortNodes() implementation.
+        (WebInspector.DataGrid.prototype._startEditingNodeAtColumnIndex): Renamed
+        from _startEditingColumnOfDataGridNode. It's easier to understand the navigation
+        code when presented in terms of adjusting column indexes rather than identifiers.
+
+        (WebInspector.DataGrid.prototype._startEditing):
+        (WebInspector.DataGrid.prototype.determineNextCell): Added. Decides which
+        column and row to edit next and whether the table can be sorted before the
+        next cell edit begins.
+
+        (WebInspector.DataGrid.prototype.moveToNextCell): Added. Wrapper method for
+        the above which handles sorting and beginning the next cell edit.
+
+        (WebInspector.DataGrid.prototype._editingCommitted): Use better helper methods.
+        (WebInspector.DataGrid.prototype._editingCancelled): Add an assert.
+        (WebInspector.DataGrid.prototype.get sortColumnIdentifier): Shorten.
+        (WebInspector.DataGrid.prototype.addPlaceholderNode):
+        (WebInspector.DataGrid.prototype.removeChild):
+        (WebInspector.DataGrid.prototype.):
+        (WebInspector.DataGrid.prototype.sortNodes): Remove unecessary copying
+        of all nodes into a separate array. Defer sorting if the user is in the
+        middle of editing a cell. Make placeholder nodes always sort to the bottom
+        regardless of the sort column identifier and sort direction.
+
+        (WebInspector.DataGridNode.prototype._attach):
+        (WebInspector.PlaceholderDataGridNode): Renamed from CreationDataGridNode.
+        (WebInspector.PlaceholderDataGridNode.prototype.makeNormal):
+
+        * UserInterface/StorageManager.js: Don't pass unused id argument.
+        (WebInspector.StorageManager.prototype.itemRemoved):
+        (WebInspector.StorageManager.prototype.itemAdded):
+        (WebInspector.StorageManager.prototype.itemUpdated):
+
+2014-02-13  Brian Burg  &lt;bburg@apple.com&gt;
+
</ins><span class="cx">         Web Inspector: long script names should be have text-overflow:ellipsis in probe details sidebar
</span><span class="cx">         https://bugs.webkit.org/show_bug.cgi?id=128550
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceDOMStorageContentViewcss"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/DOMStorageContentView.css (164051 => 164052)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/DOMStorageContentView.css        2014-02-13 21:37:59 UTC (rev 164051)
+++ trunk/Source/WebInspectorUI/UserInterface/DOMStorageContentView.css        2014-02-13 21:38:05 UTC (rev 164052)
</span><span class="lines">@@ -32,3 +32,15 @@
</span><span class="cx">     outline: none;
</span><span class="cx">     border: none;
</span><span class="cx"> }
</span><ins>+
+.content-view.dom-storage &gt; .data-grid tr.duplicate-key td.key-column,
+.content-view.dom-storage &gt; .data-grid tr.missing-key td.key-column,
+.content-view.dom-storage &gt; .data-grid tr.missing-value td.value-column {
+    background-color: #fee;
+}
+
+.content-view.dom-storage &gt; .data-grid:focus tr.selected.duplicate-key td.key-column,
+.content-view.dom-storage &gt; .data-grid:focus tr.selected.missing-key td.key-column,
+.content-view.dom-storage &gt; .data-grid:focus tr.selected.missing-value td.value-column {
+    background-color: #daa;
+}
</ins></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceDOMStorageContentViewjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/DOMStorageContentView.js (164051 => 164052)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/DOMStorageContentView.js        2014-02-13 21:37:59 UTC (rev 164051)
+++ trunk/Source/WebInspectorUI/UserInterface/DOMStorageContentView.js        2014-02-13 21:38:05 UTC (rev 164052)
</span><span class="lines">@@ -39,7 +39,11 @@
</span><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> WebInspector.DOMStorageContentView.StyleClassName = &quot;dom-storage&quot;;
</span><ins>+WebInspector.DOMStorageContentView.DuplicateKeyStyleClassName = &quot;duplicate-key&quot;;
+WebInspector.DOMStorageContentView.MissingKeyStyleClassName = &quot;missing-key&quot;;
+WebInspector.DOMStorageContentView.MissingValueStyleClassName = &quot;missing-value&quot;;
</ins><span class="cx"> 
</span><ins>+
</ins><span class="cx"> WebInspector.DOMStorageContentView.prototype = {
</span><span class="cx">     constructor: WebInspector.DOMStorageContentView,
</span><span class="cx">     __proto__: WebInspector.ContentView.prototype,
</span><span class="lines">@@ -48,7 +52,37 @@
</span><span class="cx"> 
</span><span class="cx">     reset: function()
</span><span class="cx">     {
</span><del>-        this.representedObject.getEntries(this._showDOMStorageEntries.bind(this));
</del><ins>+        this.representedObject.getEntries(function(error, entries) {
+            if (error)
+                return;
+
+            if (!this._dataGrid) {
+                var columns = {};
+                columns.key = {title: WebInspector.UIString(&quot;Key&quot;), sortable: true};
+                columns.value = {title: WebInspector.UIString(&quot;Value&quot;), sortable: true};
+
+                this._dataGrid = new WebInspector.DataGrid(columns, this._editingCallback.bind(this), this._deleteCallback.bind(this));
+                this._dataGrid.addEventListener(WebInspector.DataGrid.Event.SortChanged, this._sortDataGrid, this);
+
+                this.element.appendChild(this._dataGrid.element);
+            }
+
+            console.assert(this._dataGrid);
+
+            var nodes = [];
+            for (var entry of entries) {
+                if (!entry[0] || !entry[1])
+                    continue;
+                var data = {key: entry[0], value: entry[1]};
+                var node = new WebInspector.DataGridNode(data, false);
+                node.selectable = true;
+                this._dataGrid.appendChild(node);
+            }
+
+            this._sortDataGrid();
+            this._dataGrid.addPlaceholderNode();
+            this._dataGrid.updateLayout();
+        }.bind(this));
</ins><span class="cx">     },
</span><span class="cx"> 
</span><span class="cx">     saveToCookie: function(cookie)
</span><span class="lines">@@ -61,13 +95,13 @@
</span><span class="cx">     itemsCleared: function(event)
</span><span class="cx">     {
</span><span class="cx">         this._dataGrid.removeChildren();
</span><del>-        this._dataGrid.addCreationNode(false);
</del><ins>+        this._dataGrid.addPlaceholderNode();
</ins><span class="cx">     },
</span><span class="cx"> 
</span><span class="cx">     itemRemoved: function(event)
</span><span class="cx">     {
</span><span class="cx">         for (var node of this._dataGrid.children) {
</span><del>-            if (node.data[0] === event.data.key)
</del><ins>+            if (node.data.key === event.data.key)
</ins><span class="cx">                 return this._dataGrid.removeChild(node);
</span><span class="cx">         }
</span><span class="cx">     },
</span><span class="lines">@@ -79,19 +113,13 @@
</span><span class="cx"> 
</span><span class="cx">         // Enforce key uniqueness.
</span><span class="cx">         for (var node of this._dataGrid.children) {
</span><del>-            if (node.data[0] === key)
</del><ins>+            if (node.data.key === key)
</ins><span class="cx">                 return;
</span><span class="cx">         }
</span><span class="cx"> 
</span><del>-        var data = {};
-        data[0] = key;
-        data[1] = value;
-
-        var childNode = new WebInspector.DataGridNode(data, false);
-
-        this._dataGrid.insertChild(childNode, this._dataGrid.children.length - 1);
-        if (this._dataGrid.sortOrder)
-            this._sortDataGrid();
</del><ins>+        var data = {key: key, value: value};
+        this._dataGrid.appendChild(new WebInspector.DataGridNode(data, false));
+        this._sortDataGrid();
</ins><span class="cx">     },
</span><span class="cx"> 
</span><span class="cx">     itemUpdated: function(event)
</span><span class="lines">@@ -101,7 +129,7 @@
</span><span class="cx"> 
</span><span class="cx">         var keyFound = false;
</span><span class="cx">         for (var childNode of this._dataGrid.children) {
</span><del>-            if (childNode.data[0] === key) {
</del><ins>+            if (childNode.data.key === key) {
</ins><span class="cx">                 // Remove any rows that are now duplicates.
</span><span class="cx">                 if (keyFound) {
</span><span class="cx">                     this._dataGrid.removeChild(childNode);
</span><span class="lines">@@ -109,10 +137,11 @@
</span><span class="cx">                 }
</span><span class="cx"> 
</span><span class="cx">                 keyFound = true;
</span><del>-                childNode.data[1] = value;
</del><ins>+                childNode.data.value = value;
</ins><span class="cx">                 childNode.refresh();
</span><span class="cx">             }
</span><span class="cx">         }
</span><ins>+        this._sortDataGrid();
</ins><span class="cx">     },
</span><span class="cx"> 
</span><span class="cx">     updateLayout: function()
</span><span class="lines">@@ -130,109 +159,84 @@
</span><span class="cx"> 
</span><span class="cx">     // Private
</span><span class="cx"> 
</span><del>-    _showDOMStorageEntries: function(error, entries)
</del><ins>+    _sortDataGrid: function()
</ins><span class="cx">     {
</span><del>-        if (error)
</del><ins>+        if (!this._dataGrid.sortOrder)
</ins><span class="cx">             return;
</span><span class="cx"> 
</span><del>-        this._updateDataGridForDOMStorageEntries(entries);
</del><ins>+        var sortColumnIdentifier = this._dataGrid.sortColumnIdentifier || &quot;key&quot;;
</ins><span class="cx"> 
</span><del>-        this._dataGrid.updateLayout();
-    },
-
-    _updateDataGridForDOMStorageEntries: function(entries)
-    {
-        if (!this._dataGrid) {
-            var columns = {};
-            columns[0] = {title: WebInspector.UIString(&quot;Key&quot;), sortable: true};
-            columns[1] = {title: WebInspector.UIString(&quot;Value&quot;), sortable: true};
-
-            this._dataGrid = new WebInspector.DataGrid(columns, this._editingCallback.bind(this), this._deleteCallback.bind(this));
-            this._dataGrid.addEventListener(WebInspector.DataGrid.Event.SortChanged, this._sortDataGrid, this);
-
-            this.element.appendChild(this._dataGrid.element);
</del><ins>+        function comparator(a, b)
+        {
+            return b.data[sortColumnIdentifier].localeCompare(a.data[sortColumnIdentifier]);
</ins><span class="cx">         }
</span><span class="cx"> 
</span><del>-        console.assert(this._dataGrid);
-
-        var nodes = [];
-        for (var i = 0; i &lt; entries.length; ++i) {
-            var data = {};
-
-            var key = entries[i][0];
-            var value = entries[i][1];
-
-            data[0] = key;
-            data[1] = value;
-
-            var node = new WebInspector.DataGridNode(data, false);
-            node.selectable = true;
-
-            nodes.push(node);
-        }
-
-        if (this._dataGrid.creationNode)
-            this._dataGrid.removeChild(this._dataGrid.creationNode);
-
-        this._insertNodesIntoDataGridWithSort(nodes);
-
-        if (nodes.length &gt; 0)
-            nodes[0].selected = true;
</del><ins>+        this._dataGrid.sortNodes(comparator, this._dataGrid.sortOrder);
</ins><span class="cx">     },
</span><span class="cx"> 
</span><del>-    _sortDataGrid: function()
</del><ins>+    _deleteCallback: function(node)
</ins><span class="cx">     {
</span><del>-        if (this._dataGrid.creationNode)
-            this._dataGrid.removeChild(this._dataGrid.creationNode);
</del><ins>+        if (!node || node.isPlaceholderNode)
+            return;
</ins><span class="cx"> 
</span><del>-        var nodes = this._dataGrid.children.slice();
-        this._insertNodesIntoDataGridWithSort(nodes);
</del><ins>+        this._dataGrid.removeChild(node);
+        this.representedObject.removeItem(node.data[&quot;key&quot;]);
</ins><span class="cx">     },
</span><span class="cx"> 
</span><del>-    _insertNodesIntoDataGridWithSort: function(nodes)
</del><ins>+    _editingCallback: function(editingNode, columnIdentifier, oldText, newText, moveDirection)
</ins><span class="cx">     {
</span><del>-        console.assert(!this._dataGrid.creationNode);
</del><ins>+        var key = editingNode.data[&quot;key&quot;].trim();
+        var value = editingNode.data[&quot;value&quot;].trim();
+        var previousValue = oldText.trim();
+        var enteredValue = newText.trim();
+        var columnIndex = this._dataGrid.orderedColumns.indexOf(columnIdentifier);
+        var mayMoveToNextRow = moveDirection === &quot;forward&quot; &amp;&amp; columnIndex == this._dataGrid.orderedColumns.length - 1;
+        var mayMoveToPreviousRow = moveDirection === &quot;backward&quot; &amp;&amp; columnIndex == 0;
+        var willMoveRow = mayMoveToNextRow || mayMoveToPreviousRow;
+        var shouldCommitRow = willMoveRow &amp;&amp; key.length &amp;&amp; value.length;
</ins><span class="cx"> 
</span><del>-        var sortColumnIdentifier = this._dataGrid.sortColumnIdentifier;
-        var sortAscending = this._dataGrid.sortOrder === &quot;ascending&quot;;
</del><ins>+        // Remove the row if its values are newly cleared, and it's not a placeholder.
+        if (!key.length &amp;&amp; !value.length &amp;&amp; willMoveRow) {
+            if (previousValue.length &amp;&amp; !editingNode.isPlaceholderNode)
+                this._dataGrid.removeChild(editingNode);
+            return;
+        }
</ins><span class="cx"> 
</span><del>-        function comparator(a, b)
-        {
-            var result = b.data[sortColumnIdentifier].localeCompare(a.data[sortColumnIdentifier]);
-            return sortAscending ? -result : result;
</del><ins>+        // If the key field was deleted, restore it when committing the row.
+        if (key === enteredValue &amp;&amp; !key.length) {
+            if (willMoveRow &amp;&amp; !editingNode.isPlaceholderNode) {
+                editingNode.data.key = previousValue;
+                editingNode.refresh();
+            } else
+                editingNode.element.classList.add(WebInspector.DOMStorageContentView.MissingKeyStyleClassName);
+        } else if (key.length) {
+            editingNode.element.classList.remove(WebInspector.DOMStorageContentView.MissingKeyStyleClassName);
+            editingNode.__previousKeyValue = previousValue;
</ins><span class="cx">         }
</span><span class="cx"> 
</span><del>-        if (sortColumnIdentifier)
-            nodes.sort(comparator);
</del><ins>+        if (value === enteredValue &amp;&amp; !value.length)
+            editingNode.element.classList.add(WebInspector.DOMStorageContentView.MissingValueStyleClassName);
+        else
+            editingNode.element.classList.remove(WebInspector.DOMStorageContentView.MissingValueStyleClassName);
</ins><span class="cx"> 
</span><del>-        this._dataGrid.removeChildren();
-        for (var i = 0; i &lt; nodes.length; i++)
-            this._dataGrid.appendChild(nodes[i]);
-        this._dataGrid.addCreationNode(false);
-    },
</del><ins>+        if (editingNode.isPlaceholderNode &amp;&amp; previousValue !== enteredValue)
+            this._dataGrid.addPlaceholderNode();
</ins><span class="cx"> 
</span><del>-    _deleteCallback: function(node)
-    {
-        if (!node || node.isCreationNode)
</del><ins>+        if (!shouldCommitRow)
+            return; // One of the inputs is missing, or we aren't moving between rows.
+
+        var domStorage = this.representedObject;
+        if (domStorage.entries.has(key)) {
+            editingNode.element.classList.add(WebInspector.DOMStorageContentView.DuplicateKeyStyleClassName);
</ins><span class="cx">             return;
</span><ins>+        }
</ins><span class="cx"> 
</span><del>-        if (this.representedObject)
-            this.representedObject.removeItem(node.data[0]);
</del><ins>+        editingNode.element.classList.remove(WebInspector.DOMStorageContentView.DuplicateKeySyleClassName);
</ins><span class="cx"> 
</span><del>-        this.reset();
-    },
</del><ins>+        if (editingNode.__previousKeyValue != key)
+            domStorage.removeItem(editingNode.__previousKeyValue);
</ins><span class="cx"> 
</span><del>-    _editingCallback: function(editingNode, columnIdentifier, oldText, newText)
-    {
-        var domStorage = this.representedObject;
-        if (columnIdentifier === &quot;0&quot;) {
-            if (oldText)
-                domStorage.removeItem(oldText);
-
-            domStorage.setItem(newText, editingNode.data[1]);
-        } else
-            domStorage.setItem(editingNode.data[0], newText);
-
-        this.reset();
</del><ins>+        domStorage.setItem(key, value);
+        // The table will be re-sorted when the backend fires the itemUpdated event.
</ins><span class="cx">     }
</span><span class="cx"> };
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceDOMStorageObjectjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/DOMStorageObject.js (164051 => 164052)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/DOMStorageObject.js        2014-02-13 21:37:59 UTC (rev 164051)
+++ trunk/Source/WebInspectorUI/UserInterface/DOMStorageObject.js        2014-02-13 21:38:05 UTC (rev 164052)
</span><span class="lines">@@ -28,6 +28,7 @@
</span><span class="cx">     this._id = id;
</span><span class="cx">     this._host = host;
</span><span class="cx">     this._isLocalStorage = isLocalStorage;
</span><ins>+    this._entries = new Map;
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> WebInspector.DOMStorageObject.TypeIdentifier = &quot;dom-storage&quot;;
</span><span class="lines">@@ -55,6 +56,11 @@
</span><span class="cx">         return this._host;
</span><span class="cx">     },
</span><span class="cx"> 
</span><ins>+    get entries()
+    {
+        return this._entries;
+    },
+
</ins><span class="cx">     saveIdentityToCookie: function(cookie)
</span><span class="cx">     {
</span><span class="cx">         cookie[WebInspector.DOMStorageObject.HostCookieKey] = this.host;
</span><span class="lines">@@ -85,24 +91,28 @@
</span><span class="cx">         DOMStorageAgent.setDOMStorageItem(this._id, key, value);
</span><span class="cx">     },
</span><span class="cx"> 
</span><del>-    itemsCleared: function(id)
</del><ins>+    itemsCleared: function()
</ins><span class="cx">     {
</span><del>-        this.dispatchEventToListeners(WebInspector.DOMStorageObject.Event.ItemsCleared, {id: id});
</del><ins>+        this._entries.clear();
+        this.dispatchEventToListeners(WebInspector.DOMStorageObject.Event.ItemsCleared);
</ins><span class="cx">     },
</span><span class="cx"> 
</span><del>-    itemRemoved: function(id, key)
</del><ins>+    itemRemoved: function(key)
</ins><span class="cx">     {
</span><del>-        this.dispatchEventToListeners(WebInspector.DOMStorageObject.Event.ItemRemoved, {id: id, key: key});
</del><ins>+        this._entries.delete(key);
+        this.dispatchEventToListeners(WebInspector.DOMStorageObject.Event.ItemRemoved, {key: key});
</ins><span class="cx">     },
</span><span class="cx"> 
</span><del>-    itemAdded: function(id, key, value)
</del><ins>+    itemAdded: function(key, value)
</ins><span class="cx">     {
</span><del>-        this.dispatchEventToListeners(WebInspector.DOMStorageObject.Event.ItemAdded, {id: id, key: key, value: value});
</del><ins>+        this._entries.set(key, value);
+        this.dispatchEventToListeners(WebInspector.DOMStorageObject.Event.ItemAdded, {key: key, value: value});
</ins><span class="cx">     },
</span><span class="cx"> 
</span><del>-    itemUpdated: function(id, key, oldValue, value)
</del><ins>+    itemUpdated: function(key, oldValue, value)
</ins><span class="cx">     {
</span><del>-        var data = {id: id, key: key, oldValue: oldValue, value: value};
</del><ins>+        this._entries.set(key, value);
+        var data = {key: key, oldValue: oldValue, value: value};
</ins><span class="cx">         this.dispatchEventToListeners(WebInspector.DOMStorageObject.Event.ItemUpdated, data);
</span><span class="cx">     }
</span><span class="cx"> };
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceDataGridjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/DataGrid.js (164051 => 164052)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/DataGrid.js        2014-02-13 21:37:59 UTC (rev 164051)
+++ trunk/Source/WebInspectorUI/UserInterface/DataGrid.js        2014-02-13 21:38:05 UTC (rev 164052)
</span><span class="lines">@@ -119,7 +119,7 @@
</span><span class="cx">         columnsData[columnName] = column;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    var nodes = [];
</del><ins>+    var dataGrid = new WebInspector.DataGrid(columnsData);
</ins><span class="cx">     for (var i = 0; i &lt; values.length / numColumns; ++i) {
</span><span class="cx">         var data = {};
</span><span class="cx">         for (var j = 0; j &lt; columnNames.length; ++j)
</span><span class="lines">@@ -127,23 +127,15 @@
</span><span class="cx"> 
</span><span class="cx">         var node = new WebInspector.DataGridNode(data, false);
</span><span class="cx">         node.selectable = false;
</span><del>-        nodes.push(node);
</del><ins>+        dataGrid.appendChild(node);
</ins><span class="cx">     }
</span><span class="cx"> 
</span><del>-    var dataGrid = new WebInspector.DataGrid(columnsData);
-    for (var node of nodes)
-        dataGrid.appendChild(node);
-
-    dataGrid.addEventListener(WebInspector.DataGrid.Event.SortChanged, sortDataGrid, this);
-
</del><span class="cx">     function sortDataGrid()
</span><span class="cx">     {
</span><del>-        var nodes = dataGrid.children.slice();
</del><span class="cx">         var sortColumnIdentifier = dataGrid.sortColumnIdentifier;
</span><del>-        var sortDirection = dataGrid.sortOrder === &quot;ascending&quot; ? 1 : -1;
-        var columnIsNumeric = true;
</del><ins>+        var sortAscending = dataGrid.sortOrder === &quot;ascending&quot; ? 1 : -1;
</ins><span class="cx"> 
</span><del>-        for (var node of nodes) {
</del><ins>+        for (var node of dataGrid.children) {
</ins><span class="cx">             if (isNaN(Number(node.data[sortColumnIdentifier] || &quot;&quot;)))
</span><span class="cx">                 columnIsNumeric = false;
</span><span class="cx">         }
</span><span class="lines">@@ -165,11 +157,10 @@
</span><span class="cx">             return sortDirection * comparison;
</span><span class="cx">         }
</span><span class="cx"> 
</span><del>-        nodes.sort(comparator);
-        dataGrid.removeChildren();
-        for (var node of nodes)
-            dataGrid.appendChild(node);
</del><ins>+        dataGrid.sortNodes(comparator);
</ins><span class="cx">     }
</span><ins>+
+    dataGrid.addEventListener(WebInspector.DataGrid.Event.SortChanged, sortDataGrid, this);
</ins><span class="cx">     return dataGrid;
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -192,13 +183,15 @@
</span><span class="cx">         this._startEditing(event.target);
</span><span class="cx">     },
</span><span class="cx"> 
</span><del>-    _startEditingColumnOfDataGridNode: function(node, column)
</del><ins>+    _startEditingNodeAtColumnIndex: function(node, columnIndex)
</ins><span class="cx">     {
</span><ins>+        console.assert(node, &quot;Invalid argument: must provide DataGridNode to edit.&quot;);
+
</ins><span class="cx">         this._editing = true;
</span><span class="cx">         this._editingNode = node;
</span><span class="cx">         this._editingNode.select();
</span><span class="cx"> 
</span><del>-        var element = this._editingNode._element.children[column];
</del><ins>+        var element = this._editingNode._element.children[columnIndex];
</ins><span class="cx">         WebInspector.startEditing(element, this._startEditingConfig(element));
</span><span class="cx">         window.getSelection().setBaseAndExtent(element, 0, element, 1);
</span><span class="cx">     },
</span><span class="lines">@@ -211,14 +204,14 @@
</span><span class="cx"> 
</span><span class="cx">         this._editingNode = this.dataGridNodeFromNode(target);
</span><span class="cx">         if (!this._editingNode) {
</span><del>-            if (!this.creationNode)
</del><ins>+            if (!this.placeholderNode)
</ins><span class="cx">                 return;
</span><del>-            this._editingNode = this.creationNode;
</del><ins>+            this._editingNode = this.placeholderNode;
</ins><span class="cx">         }
</span><span class="cx"> 
</span><del>-        // Force editing the 1st column when editing the creation node
-        if (this._editingNode.isCreationNode)
-            return this._startEditingColumnOfDataGridNode(this._editingNode, 0);
</del><ins>+        // Force editing the 1st column when editing the placeholder node
+        if (this._editingNode.isPlaceholderNode)
+            return this._startEditingNodeAtColumnIndex(this._editingNode, 0);
</ins><span class="cx"> 
</span><span class="cx">         this._editing = true;
</span><span class="cx">         WebInspector.startEditing(element, this._startEditingConfig(element));
</span><span class="lines">@@ -233,77 +226,66 @@
</span><span class="cx"> 
</span><span class="cx">     _editingCommitted: function(element, newText, oldText, context, moveDirection)
</span><span class="cx">     {
</span><del>-        // FIXME: We need more column identifiers here throughout this function.
-        // Not needed yet since only editable DataGrid is DOM Storage, which is Key - Value.
-
</del><span class="cx">         var columnIdentifier = element.__columnIdentifier;
</span><ins>+        var columnIndex = this.orderedColumns.indexOf(columnIdentifier);
</ins><span class="cx"> 
</span><span class="cx">         var textBeforeEditing = this._editingNode.data[columnIdentifier] || &quot;&quot;;
</span><span class="cx">         var currentEditingNode = this._editingNode;
</span><span class="cx"> 
</span><del>-        function moveToNextIfNeeded(wasChange) {
-            if (!moveDirection)
-                return;
-
</del><ins>+        // Returns an object with the next node and column index to edit, and whether it
+        // is an appropriate time to re-sort the table rows. When editing, we want to
+        // postpone sorting until we switch rows or wrap around a row.
+        function determineNextCell(valueDidChange) {
</ins><span class="cx">             if (moveDirection === &quot;forward&quot;) {
</span><del>-                if (currentEditingNode.isCreationNode &amp;&amp; columnIdentifier === &quot;0&quot; &amp;&amp; !wasChange)
-                    return;
</del><ins>+                if (columnIndex &lt; this.orderedColumns.length - 1)
+                    return {shouldSort: false, editingNode: currentEditingNode, columnIndex: columnIndex + 1};
</ins><span class="cx"> 
</span><del>-                if (columnIdentifier === &quot;0&quot;)
-                    return this._startEditingColumnOfDataGridNode(currentEditingNode, &quot;1&quot;);
-
</del><ins>+                // Continue by editing the first column of the next row if it exists.
</ins><span class="cx">                 var nextDataGridNode = currentEditingNode.traverseNextNode(true, null, true);
</span><del>-                if (nextDataGridNode)
-                    return this._startEditingColumnOfDataGridNode(nextDataGridNode, &quot;0&quot;);
-                if (currentEditingNode.isCreationNode &amp;&amp; wasChange) {
-                    this.addCreationNode(false);
-                    return this._startEditingColumnOfDataGridNode(this.creationNode, &quot;0&quot;);
-                }
-                return;
</del><ins>+                return {shouldSort: true, editingNode: nextDataGridNode || currentEditingNode, columnIndex: 0};
</ins><span class="cx">             }
</span><span class="cx"> 
</span><span class="cx">             if (moveDirection === &quot;backward&quot;) {
</span><del>-                if (columnIdentifier === &quot;1&quot;)
-                    return this._startEditingColumnOfDataGridNode(currentEditingNode, &quot;0&quot;);
-                    var nextDataGridNode = currentEditingNode.traversePreviousNode(true, null, true);
</del><ins>+                if (columnIndex &gt; 0)
+                    return {shouldSort: false, editingNode: currentEditingNode, columnIndex: columnIndex - 1};
</ins><span class="cx"> 
</span><del>-                if (nextDataGridNode)
-                    return this._startEditingColumnOfDataGridNode(nextDataGridNode, &quot;1&quot;);
-                return;
</del><ins>+                var previousDataGridNode = currentEditingNode.traversePreviousNode(true, null, true);
+                return {shouldSort: true, editingNode: previousDataGridNode || currentEditingNode, columnIndex: this.orderedColumns.length - 1};
</ins><span class="cx">             }
</span><ins>+
+            // If we are not moving in any direction, then sort but don't move.
+            return {shouldSort: true, editingNode: currentEditingNode, columnIndex: columnIndex};
</ins><span class="cx">         }
</span><span class="cx"> 
</span><del>-        if (textBeforeEditing == newText) {
-            this._editingCancelled(element);
-            moveToNextIfNeeded.call(this, false);
-            return;
</del><ins>+        function moveToNextCell(valueDidChange) {
+            var moveCommand = determineNextCell.call(this, valueDidChange);
+            if (moveCommand.shouldSort &amp;&amp; this._sortAfterEditingCallback) {
+                this._sortAfterEditingCallback();
+                delete this._sortAfterEditingCallback;
+            }
+            this._startEditingNodeAtColumnIndex(moveCommand.editingNode, moveCommand.columnIndex);
</ins><span class="cx">         }
</span><span class="cx"> 
</span><del>-        // Update the text in the datagrid that we typed
-        this._editingNode.data[columnIdentifier] = newText;
</del><ins>+        this._editingCancelled(element);
</ins><span class="cx"> 
</span><del>-        // Make the callback - expects an editing node (table row), the column number that is being edited,
-        // the text that used to be there, and the new text.
-        this._editCallback(this._editingNode, columnIdentifier, textBeforeEditing, newText);
</del><ins>+        // Update table's data model, and delegate to the callback to update other models.
+        currentEditingNode.data[columnIdentifier] = newText.trim();
+        this._editCallback(currentEditingNode, columnIdentifier, textBeforeEditing, newText, moveDirection);
</ins><span class="cx"> 
</span><del>-        if (this._editingNode.isCreationNode)
-            this.addCreationNode(false);
-
-        this._editingCancelled(element);
-        moveToNextIfNeeded.call(this, true);
</del><ins>+        var textDidChange = textBeforeEditing.trim() !== newText.trim();
+        moveToNextCell.call(this, textDidChange);
</ins><span class="cx">     },
</span><span class="cx"> 
</span><span class="cx">     _editingCancelled: function(element)
</span><span class="cx">     {
</span><ins>+        console.assert(this._editingNode.element === element.enclosingNodeOrSelfWithNodeName(&quot;tr&quot;));
</ins><span class="cx">         delete this._editing;
</span><span class="cx">         this._editingNode = null;
</span><span class="cx">     },
</span><span class="cx"> 
</span><span class="cx">     get sortColumnIdentifier()
</span><span class="cx">     {
</span><del>-        if (!this._sortColumnCell)
-            return null;
-        return this._sortColumnCell.columnIdentifier;
</del><ins>+        return this._sortColumnCell ? this._sortColumnCell.columnIdentifier : null;
</ins><span class="cx">     },
</span><span class="cx"> 
</span><span class="cx">     get sortOrder()
</span><span class="lines">@@ -668,16 +650,16 @@
</span><span class="cx">             previousResizerElement.rightNeighboringColumnID = this.orderedColumns.length - 1;
</span><span class="cx">     },
</span><span class="cx"> 
</span><del>-    addCreationNode: function(hasChildren)
</del><ins>+    addPlaceholderNode: function()
</ins><span class="cx">     {
</span><del>-        if (this.creationNode)
-            this.creationNode.makeNormal();
</del><ins>+        if (this.placeholderNode)
+            this.placeholderNode.makeNormal();
</ins><span class="cx"> 
</span><span class="cx">         var emptyData = {};
</span><span class="cx">         for (var identifier of this.columns.keys())
</span><span class="cx">             emptyData[identifier] = '';
</span><del>-        this.creationNode = new WebInspector.CreationDataGridNode(emptyData, hasChildren);
-        this.appendChild(this.creationNode);
</del><ins>+        this.placeholderNode = new WebInspector.PlaceholderDataGridNode(emptyData);
+        this.appendChild(this.placeholderNode);
</ins><span class="cx">     },
</span><span class="cx"> 
</span><span class="cx">     appendChild: function(child)
</span><span class="lines">@@ -746,8 +728,7 @@
</span><span class="cx">         if (this.children.length &lt;= 0)
</span><span class="cx">             this.hasChildren = false;
</span><span class="cx"> 
</span><del>-        if (this.creationNode === child)
-            delete this.creationNode;
</del><ins>+        console.assert(!child.isPlaceholderNode, &quot;Shouldn't delete the placeholder node.&quot;)
</ins><span class="cx">     },
</span><span class="cx"> 
</span><span class="cx">     removeChildren: function()
</span><span class="lines">@@ -793,47 +774,49 @@
</span><span class="cx">         this.children = [];
</span><span class="cx">     },
</span><span class="cx"> 
</span><del>-    sortNodes: function(comparator, reverseMode)
</del><ins>+    sortNodes: function(comparator)
</ins><span class="cx">     {
</span><del>-        function comparatorWrapper(a, b)
</del><ins>+        function comparatorWrapper(aRow, bRow)
</ins><span class="cx">         {
</span><del>-            if (a._dataGridNode._data.summaryRow)
</del><ins>+            var reverseFactor = this.sortOrder !== &quot;asceding&quot; ? -1 : 1;
+            var aNode = aRow._dataGridNode;
+            var bNode = bRow._dataGridNode;
+            if (aNode._data.summaryRow || aNode.isPlaceholderNode)
</ins><span class="cx">                 return 1;
</span><del>-            if (b._dataGridNode._data.summaryRow)
</del><ins>+            if (bNode._data.summaryRow || bNode.isPlaceholderNode)
</ins><span class="cx">                 return -1;
</span><span class="cx"> 
</span><del>-            var aDataGridNode = a._dataGridNode;
-            var bDataGridNode = b._dataGridNode;
-            return reverseMode ? comparator(bDataGridNode, aDataGridNode) : comparator(aDataGridNode, bDataGridNode);
</del><ins>+            return reverseFactor * comparator(aNode, bNode);
</ins><span class="cx">         }
</span><span class="cx"> 
</span><del>-        var tbody = this.dataTableBodyElement;
-        var tbodyParent = tbody.parentElement;
-        tbodyParent.removeChild(tbody);
</del><ins>+        if (this._editing) {
+            this._sortAfterEditingCallback = this.sortNodes.bind(this, comparator);
+            return;
+        }
</ins><span class="cx"> 
</span><ins>+        var tbody = this.dataTableBodyElement;
</ins><span class="cx">         var childNodes = tbody.childNodes;
</span><del>-        var fillerRow = childNodes.lastValue;
</del><ins>+        var fillerRowElement = tbody.lastChild;
</ins><span class="cx"> 
</span><del>-        var sortedRows = Array.prototype.slice.call(childNodes, 0, childNodes.length - 1);
-        sortedRows.sort(comparatorWrapper);
-        var sortedRowsLength = sortedRows.length;
</del><ins>+        var sortedRowElements = Array.prototype.slice.call(childNodes, 0, childNodes.length - 1);
+        sortedRowElements.sort(comparatorWrapper);
</ins><span class="cx"> 
</span><span class="cx">         tbody.removeChildren();
</span><ins>+
</ins><span class="cx">         var previousSiblingNode = null;
</span><del>-        for (var i = 0; i &lt; sortedRowsLength; ++i) {
-            var row = sortedRows[i];
-            var node = row._dataGridNode;
</del><ins>+        for (var rowElement of sortedRowElements) {
+            var node = rowElement._dataGridNode;
</ins><span class="cx">             node.previousSibling = previousSiblingNode;
</span><span class="cx">             if (previousSiblingNode)
</span><span class="cx">                 previousSiblingNode.nextSibling = node;
</span><del>-            tbody.appendChild(row);
</del><ins>+            tbody.appendChild(rowElement);
</ins><span class="cx">             previousSiblingNode = node;
</span><span class="cx">         }
</span><ins>+
</ins><span class="cx">         if (previousSiblingNode)
</span><span class="cx">             previousSiblingNode.nextSibling = null;
</span><span class="cx"> 
</span><del>-        tbody.appendChild(fillerRow);
-        tbodyParent.appendChild(tbody);
</del><ins>+        tbody.appendChild(fillerRowElement); // We expect to find a filler row when attaching nodes.
</ins><span class="cx">     },
</span><span class="cx"> 
</span><span class="cx">     _keyDown: function(event)
</span><span class="lines">@@ -1113,23 +1096,23 @@
</span><span class="cx">         var contextMenu = new WebInspector.ContextMenu(event);
</span><span class="cx"> 
</span><span class="cx">         var gridNode = this.dataGridNodeFromNode(event.target);
</span><del>-        if (this.dataGrid._refreshCallback &amp;&amp; (!gridNode || gridNode !== this.creationNode))
</del><ins>+        if (this.dataGrid._refreshCallback &amp;&amp; (!gridNode || gridNode !== this.placeholderNode))
</ins><span class="cx">             contextMenu.appendItem(WebInspector.UIString(&quot;Refresh&quot;), this._refreshCallback.bind(this));
</span><span class="cx"> 
</span><span class="cx">         if (gridNode &amp;&amp; gridNode.selectable &amp;&amp; !gridNode.isEventWithinDisclosureTriangle(event)) {
</span><span class="cx">             contextMenu.appendItem(WebInspector.UIString(&quot;Copy Row&quot;), this._copyRow.bind(this, event.target));
</span><span class="cx"> 
</span><span class="cx">             if (this.dataGrid._editCallback) {
</span><del>-                if (gridNode === this.creationNode)
</del><ins>+                if (gridNode === this.placeholderNode)
</ins><span class="cx">                     contextMenu.appendItem(WebInspector.UIString(&quot;Add New&quot;), this._startEditing.bind(this, event.target));
</span><span class="cx">                 else {
</span><span class="cx">                     var element = event.target.enclosingNodeOrSelfWithNodeName(&quot;td&quot;);
</span><span class="cx">                     var columnIdentifier = element.__columnIdentifier;
</span><del>-                    var columnTitle = this.dataGrid.columns[columnIdentifier].title;
</del><ins>+                    var columnTitle = this.dataGrid.columns.get(columnIdentifier).get('title');
</ins><span class="cx">                     contextMenu.appendItem(WebInspector.UIString(&quot;Edit ā€œ%sā€&quot;).format(columnTitle), this._startEditing.bind(this, event.target));
</span><span class="cx">                 }
</span><span class="cx">             }
</span><del>-            if (this.dataGrid._deleteCallback &amp;&amp; gridNode !== this.creationNode)
</del><ins>+            if (this.dataGrid._deleteCallback &amp;&amp; gridNode !== this.placeholderNode)
</ins><span class="cx">                 contextMenu.appendItem(WebInspector.UIString(&quot;Delete&quot;), this._deleteCallback.bind(this, gridNode));
</span><span class="cx">         }
</span><span class="cx"> 
</span><span class="lines">@@ -1818,6 +1801,7 @@
</span><span class="cx"> 
</span><span class="cx">         // If there is no next grid node, then append before the last child since the last child is the filler row.
</span><span class="cx">         console.assert(this.dataGrid.dataTableBodyElement.lastChild.classList.contains(&quot;filler&quot;));
</span><ins>+
</ins><span class="cx">         if (!nextElement)
</span><span class="cx">             nextElement = this.dataGrid.dataTableBodyElement.lastChild;
</span><span class="cx"> 
</span><span class="lines">@@ -1869,22 +1853,20 @@
</span><span class="cx"> 
</span><span class="cx"> WebInspector.DataGridNode.prototype.__proto__ = WebInspector.Object.prototype;
</span><span class="cx"> 
</span><del>-/**
- * @constructor
- * @extends {WebInspector.DataGridNode}
- */
-WebInspector.CreationDataGridNode = function(data, hasChildren)
</del><ins>+// Used to create a new table row when entering new data by editing cells.
+WebInspector.PlaceholderDataGridNode = function(data)
</ins><span class="cx"> {
</span><del>-    WebInspector.DataGridNode.call(this, data, hasChildren);
-    this.isCreationNode = true;
</del><ins>+    WebInspector.DataGridNode.call(this, data, false);
+    this.isPlaceholderNode = true;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-WebInspector.CreationDataGridNode.prototype = {
</del><ins>+WebInspector.PlaceholderDataGridNode.prototype = {
+    constructor: WebInspector.PlaceholderDataGridNode,
+    __proto__: WebInspector.DataGridNode.prototype,
+
</ins><span class="cx">     makeNormal: function()
</span><span class="cx">     {
</span><del>-        delete this.isCreationNode;
</del><ins>+        delete this.isPlaceholderNode;
</ins><span class="cx">         delete this.makeNormal;
</span><span class="cx">     }
</span><span class="cx"> }
</span><del>-
-WebInspector.CreationDataGridNode.prototype.__proto__ = WebInspector.DataGridNode.prototype;
</del></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceStorageManagerjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/StorageManager.js (164051 => 164052)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/StorageManager.js        2014-02-13 21:37:59 UTC (rev 164051)
+++ trunk/Source/WebInspectorUI/UserInterface/StorageManager.js        2014-02-13 21:38:05 UTC (rev 164052)
</span><span class="lines">@@ -94,17 +94,17 @@
</span><span class="cx"> 
</span><span class="cx">     itemRemoved: function(storageId, key)
</span><span class="cx">     {
</span><del>-        this._domStorageForIdentifier(storageId).itemRemoved(storageId, key);
</del><ins>+        this._domStorageForIdentifier(storageId).itemRemoved(key);
</ins><span class="cx">     },
</span><span class="cx"> 
</span><span class="cx">     itemAdded: function(storageId, key, value)
</span><span class="cx">     {
</span><del>-        this._domStorageForIdentifier(storageId).itemAdded(storageId, key, value);
</del><ins>+        this._domStorageForIdentifier(storageId).itemAdded(key, value);
</ins><span class="cx">     },
</span><span class="cx"> 
</span><span class="cx">     itemUpdated: function(storageId, key, oldValue, value)
</span><span class="cx">     {
</span><del>-        this._domStorageForIdentifier(storageId).itemUpdated(storageId, key, oldValue, value);
</del><ins>+        this._domStorageForIdentifier(storageId).itemUpdated(key, oldValue, value);
</ins><span class="cx">     },
</span><span class="cx"> 
</span><span class="cx">     inspectDatabase: function(id)
</span></span></pre>
</div>
</div>

</body>
</html>