Web Inspector: Sort by size issues with Cookies and ApplicationCache DataGrids
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / Views / CookieStorageContentView.js
1 /*
2  * Copyright (C) 2013, 2015 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 WI.CookieStorageContentView = class CookieStorageContentView extends WI.ContentView
27 {
28     constructor(representedObject)
29     {
30         super(representedObject);
31
32         this.element.classList.add("cookie-storage");
33
34         this._refreshButtonNavigationItem = new WI.ButtonNavigationItem("cookie-storage-refresh", WI.UIString("Refresh"), "Images/ReloadFull.svg", 13, 13);
35         this._refreshButtonNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._refreshButtonClicked, this);
36
37         this.update();
38     }
39
40     // Public
41
42     get navigationItems()
43     {
44         return [this._refreshButtonNavigationItem];
45     }
46
47     update()
48     {
49         PageAgent.getCookies().then((payload) => {
50             this._cookies = this._filterCookies(payload.cookies);
51             this._rebuildTable();
52         }).catch((error) => {
53             console.error("Could not fetch cookies: ", error);
54         });
55     }
56
57     saveToCookie(cookie)
58     {
59         cookie.type = WI.ContentViewCookieType.CookieStorage;
60         cookie.host = this.representedObject.host;
61     }
62
63     get scrollableElements()
64     {
65         if (!this._dataGrid)
66             return [];
67         return [this._dataGrid.scrollContainer];
68     }
69
70     // Private
71
72     _rebuildTable()
73     {
74         // FIXME <https://webkit.org/b/151400>: If there are no cookies, add placeholder explanatory text.
75         if (!this._dataGrid) {
76             var columns = {name: {}, value: {}, domain: {}, path: {}, expires: {}, size: {}, http: {}, secure: {}};
77
78             columns.name.title = WI.UIString("Name");
79             columns.name.sortable = true;
80             columns.name.width = "24%";
81             columns.name.locked = true;
82
83             columns.value.title = WI.UIString("Value");
84             columns.value.sortable = true;
85             columns.value.width = "34%";
86             columns.value.locked = true;
87
88             columns.domain.title = WI.UIString("Domain");
89             columns.domain.sortable = true;
90             columns.domain.width = "7%";
91
92             columns.path.title = WI.UIString("Path");
93             columns.path.sortable = true;
94             columns.path.width = "7%";
95
96             columns.expires.title = WI.UIString("Expires");
97             columns.expires.sortable = true;
98             columns.expires.width = "7%";
99
100             columns.size.title = WI.UIString("Size");
101             columns.size.aligned = "right";
102             columns.size.sortable = true;
103             columns.size.width = "7%";
104
105             columns.http.title = WI.UIString("HTTP");
106             columns.http.aligned = "centered";
107             columns.http.sortable = true;
108             columns.http.width = "7%";
109
110             columns.secure.title = WI.UIString("Secure");
111             columns.secure.aligned = "centered";
112             columns.secure.sortable = true;
113             columns.secure.width = "7%";
114
115             this._dataGrid = new WI.DataGrid(columns, null, this._deleteCallback.bind(this));
116             this._dataGrid.columnChooserEnabled = true;
117             this._dataGrid.addEventListener(WI.DataGrid.Event.SortChanged, this._sortDataGrid, this);
118             this._dataGrid.sortColumnIdentifier = "name";
119             this._dataGrid.createSettings("cookie-storage-content-view");
120
121             this.addSubview(this._dataGrid);
122             this._dataGrid.updateLayout();
123         }
124
125         console.assert(this._dataGrid);
126         this._dataGrid.removeChildren();
127
128         for (var cookie of this._cookies) {
129             const checkmark = "\u2713";
130             var data = {
131                 name: cookie.name,
132                 value: cookie.value,
133                 domain: cookie.domain || "",
134                 path: cookie.path || "",
135                 expires: "",
136                 size: Number.bytesToString(cookie.size),
137                 http: cookie.httpOnly ? checkmark : "",
138                 secure: cookie.secure ? checkmark : "",
139             };
140
141             if (cookie.type !== WI.CookieType.Request)
142                 data["expires"] = cookie.session ? WI.UIString("Session") : new Date(cookie.expires).toLocaleString();
143
144             var node = new WI.DataGridNode(data);
145             node.cookie = cookie;
146
147             this._dataGrid.appendChild(node);
148         }
149     }
150
151     _filterCookies(cookies)
152     {
153         let resourceMatchesStorageDomain = (resource) => {
154             let urlComponents = resource.urlComponents;
155             return urlComponents && urlComponents.host && urlComponents.host === this.representedObject.host;
156         };
157
158         let allResources = [];
159         for (let frame of WI.frameResourceManager.frames) {
160             // The main resource isn't in the list of resources, so add it as a candidate.
161             allResources.push(frame.mainResource);
162             allResources = allResources.concat(frame.resourceCollection.toArray());
163         }
164
165         let resourcesForDomain = allResources.filter(resourceMatchesStorageDomain);
166
167         let cookiesForDomain = cookies.filter((cookie) => {
168             return resourcesForDomain.some((resource) => {
169                 return WI.CookieStorageObject.cookieMatchesResourceURL(cookie, resource.url);
170             });
171         });
172         return cookiesForDomain;
173     }
174
175     _sortDataGrid()
176     {
177         function localeCompare(field, nodeA, nodeB)
178         {
179             return (nodeA.data[field] + "").extendedLocaleCompare(nodeB.data[field] + "");
180         }
181
182         function numberCompare(field, nodeA, nodeB)
183         {
184             return nodeA.cookie[field] - nodeB.cookie[field];
185         }
186
187         function expiresCompare(nodeA, nodeB)
188         {
189             if (nodeA.cookie.session !== nodeB.cookie.session)
190                 return nodeA.cookie.session ? -1 : 1;
191
192             if (nodeA.cookie.session)
193                 return 0;
194
195             return nodeA.cookie.expires - nodeB.cookie.expires;
196         }
197
198         var comparator;
199         switch (this._dataGrid.sortColumnIdentifier) {
200             case "value": comparator = localeCompare.bind(this, "value"); break;
201             case "domain": comparator = localeCompare.bind(this, "domain"); break;
202             case "path": comparator = localeCompare.bind(this, "path"); break;
203             case "expires": comparator = expiresCompare; break;
204             case "size": comparator = numberCompare.bind(this, "size"); break;
205             case "http": comparator = localeCompare.bind(this, "http"); break;
206             case "secure": comparator = localeCompare.bind(this, "secure"); break;
207             case "name":
208             default: comparator = localeCompare.bind(this, "name"); break;
209         }
210
211         console.assert(comparator);
212         this._dataGrid.sortNodes(comparator);
213     }
214
215     _deleteCallback(node)
216     {
217         if (!node || !node.cookie)
218             return;
219
220         var cookie = node.cookie;
221         var cookieURL = (cookie.secure ? "https://" : "http://") + cookie.domain + cookie.path;
222         PageAgent.deleteCookie(cookie.name, cookieURL);
223
224         this.update();
225     }
226
227     _refreshButtonClicked(event)
228     {
229         this.update();
230     }
231 };
232
233 WI.CookieType = {
234     Request: 0,
235     Response: 1
236 };