Web Inspector: Cookies table needs copy keyboard shortcut and context menu support
authormattbaker@apple.com <mattbaker@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 27 Nov 2018 21:06:52 +0000 (21:06 +0000)
committermattbaker@apple.com <mattbaker@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 27 Nov 2018 21:06:52 +0000 (21:06 +0000)
https://bugs.webkit.org/show_bug.cgi?id=191482
<rdar://problem/45953002>

Reviewed by Joseph Pecoraro.

* UserInterface/Views/CookieStorageContentView.js:
(WI.CookieStorageContentView.prototype.handleCopyEvent):
(WI.CookieStorageContentView.prototype.tableCellContextMenuClicked):
As with Delete, if the target row is selected, all selected rows are copied.
Otherwise only the target row is copied. This distinction will be surfaced
in the UI in https://webkit.org/b/191095.

(WI.CookieStorageContentView.prototype.tablePopulateCell):
(WI.CookieStorageContentView.prototype._cookiesAtIndexes):
(WI.CookieStorageContentView.prototype._formatCookiesAsText):
(WI.CookieStorageContentView.prototype._formatCookiePropertyForColumn):
(WI.CookieStorageContentView):
Break Cookie property formatting into a helper method, which is used for
formatting Table cells and creating plain text for the clipboard.

* UserInterface/Views/Table.js:
(WI.Table.prototype.get columns):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@238569 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebInspectorUI/ChangeLog
Source/WebInspectorUI/UserInterface/Views/CookieStorageContentView.js
Source/WebInspectorUI/UserInterface/Views/Table.js

index b012682..7fcc49b 100644 (file)
@@ -1,5 +1,31 @@
 2018-11-27  Matt Baker  <mattbaker@apple.com>
 
+        Web Inspector: Cookies table needs copy keyboard shortcut and context menu support
+        https://bugs.webkit.org/show_bug.cgi?id=191482
+        <rdar://problem/45953002>
+
+        Reviewed by Joseph Pecoraro.
+
+        * UserInterface/Views/CookieStorageContentView.js:
+        (WI.CookieStorageContentView.prototype.handleCopyEvent):
+        (WI.CookieStorageContentView.prototype.tableCellContextMenuClicked):
+        As with Delete, if the target row is selected, all selected rows are copied.
+        Otherwise only the target row is copied. This distinction will be surfaced
+        in the UI in https://webkit.org/b/191095.
+
+        (WI.CookieStorageContentView.prototype.tablePopulateCell):
+        (WI.CookieStorageContentView.prototype._cookiesAtIndexes):
+        (WI.CookieStorageContentView.prototype._formatCookiesAsText):
+        (WI.CookieStorageContentView.prototype._formatCookiePropertyForColumn):
+        (WI.CookieStorageContentView):
+        Break Cookie property formatting into a helper method, which is used for
+        formatting Table cells and creating plain text for the clipboard.
+
+        * UserInterface/Views/Table.js:
+        (WI.Table.prototype.get columns):
+
+2018-11-27  Matt Baker  <mattbaker@apple.com>
+
         Web Inspector: Table selection should be handled by a SelectionController
         https://bugs.webkit.org/show_bug.cgi?id=191977
         <rdar://problem/46253093>
index d91601f..d0c2b43 100644 (file)
@@ -59,6 +59,20 @@ WI.CookieStorageContentView = class CookieStorageContentView extends WI.ContentV
         return [this._table.scrollContainer];
     }
 
+    handleCopyEvent(event)
+    {
+        if (!this._table || !this._table.selectedRows.length)
+            return;
+
+        let cookies = this._cookiesAtIndexes(this._table.selectedRows);
+        if (!cookies.length)
+            return;
+
+        event.clipboardData.setData("text/plain", this._formatCookiesAsText(cookies));
+        event.stopPropagation();
+        event.preventDefault();
+    }
+
     // Table dataSource
 
     tableNumberOfRows(table)
@@ -84,6 +98,17 @@ WI.CookieStorageContentView = class CookieStorageContentView extends WI.ContentV
         let contextMenu = WI.ContextMenu.createFromEvent(event);
 
         contextMenu.appendSeparator();
+        contextMenu.appendItem(WI.UIString("Copy"), () => {
+            let rowIndexes;
+            if (table.isRowSelected(rowIndex))
+                rowIndexes = table.selectedRows;
+            else
+                rowIndexes = [rowIndex];
+
+            let cookies = this._cookiesAtIndexes(rowIndexes);
+            InspectorFrontendHost.copyText(this._formatCookiesAsText(cookies));
+        });
+
         contextMenu.appendItem(WI.UIString("Delete"), () => {
             if (table.isRowSelected(rowIndex))
                 table.removeSelectedRows();
@@ -117,39 +142,7 @@ WI.CookieStorageContentView = class CookieStorageContentView extends WI.ContentV
     tablePopulateCell(table, cell, column, rowIndex)
     {
         let cookie = this._cookies[rowIndex];
-
-        const checkmark = "\u2713";
-
-        switch (column.identifier) {
-        case "name":
-            cell.textContent = cookie.name;
-            break;
-        case "value":
-            cell.textContent = cookie.value;
-            break;
-        case "domain":
-            cell.textContent = cookie.domain || emDash;
-            break;
-        case "path":
-            cell.textContent = cookie.path || emDash;
-            break;
-        case "expires":
-            cell.textContent = cookie.expires ? new Date(cookie.expires).toLocaleString() : WI.UIString("Session");
-            break;
-        case "size":
-            cell.textContent = Number.bytesToString(cookie.size);
-            break;
-        case "secure":
-            cell.textContent = cookie.secure ? checkmark : zeroWidthSpace;
-            break;
-        case "httpOnly":
-            cell.textContent = cookie.httpOnly ? checkmark : zeroWidthSpace;
-            break;
-        case "sameSite":
-            cell.textContent = cookie.sameSite === WI.Cookie.SameSiteType.None ? emDash : WI.Cookie.displayNameForSameSiteType(cookie.sameSite);
-            break;
-        }
-
+        cell.textContent = this._formatCookiePropertyForColumn(cookie, column);
         return cell;
     }
 
@@ -340,6 +333,59 @@ WI.CookieStorageContentView = class CookieStorageContentView extends WI.ContentV
         if (event.keyCode === WI.KeyboardShortcut.Key.Backspace.keyCode || event.keyCode === WI.KeyboardShortcut.Key.Delete.keyCode)
             this._table.removeSelectedRows();
     }
+
+    _cookiesAtIndexes(indexes)
+    {
+        if (!this._cookies.length)
+            return [];
+        return indexes.map((index) => this._cookies[index]);
+    }
+
+    _formatCookiesAsText(cookies)
+    {
+        let visibleColumns = this._table.columns.filter((column) => !column.hidden);
+        if (!visibleColumns.length)
+            return "";
+
+        let lines = cookies.map((cookie) => {
+            const usePunctuation = false;
+            let values = visibleColumns.map((column) => this._formatCookiePropertyForColumn(cookie, column, usePunctuation));
+            return values.join("\t");
+        });
+
+        return lines.join("\n");
+    }
+
+    _formatCookiePropertyForColumn(cookie, column, usePunctuation = true)
+    {
+        const checkmark = "\u2713";
+        const missingValue = usePunctuation ? emDash : "";
+        const missingCheckmark = usePunctuation ? zeroWidthSpace : "";
+
+        switch (column.identifier) {
+        case "name":
+            return cookie.name;
+        case "value":
+            return cookie.value;
+        case "domain":
+            return cookie.domain || missingValue;
+        case "path":
+            return cookie.path || missingValue;
+        case "expires":
+            return cookie.expires ? new Date(cookie.expires).toLocaleString() : WI.UIString("Session");
+        case "size":
+            return Number.bytesToString(cookie.size);
+        case "secure":
+            return cookie.secure ? checkmark : missingCheckmark;
+        case "httpOnly":
+            return cookie.httpOnly ? checkmark : missingCheckmark;
+        case "sameSite":
+            return cookie.sameSite === WI.Cookie.SameSiteType.None ? missingValue : WI.Cookie.displayNameForSameSiteType(cookie.sameSite);
+        }
+
+        console.assert("Unexpected table column " + column.identifier);
+        return "";
+    }
 };
 
 WI.CookieType = {
index a9700f1..acd0cf4 100644 (file)
@@ -229,6 +229,11 @@ WI.Table = class Table extends WI.View
         this._selectionController.allowsMultipleSelection = flag;
     }
 
+    get columns()
+    {
+        return Array.from(this._columnSpecs.values());
+    }
+
     isRowSelected(rowIndex)
     {
         return this._selectionController.hasSelectedItem(rowIndex);