d7310f2a1fda63b3d510c99708493110398d2cf1
[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
119             this.addSubview(this._dataGrid);
120             this._dataGrid.updateLayout();
121         }
122
123         console.assert(this._dataGrid);
124         this._dataGrid.removeChildren();
125
126         for (var cookie of this._cookies) {
127             const checkmark = "\u2713";
128             var data = {
129                 "name": cookie.name,
130                 "value": cookie.value,
131                 "domain": cookie.domain || "",
132                 "path": cookie.path || "",
133                 "expires": "",
134                 "size": Number.bytesToString(cookie.size),
135                 "http": cookie.httpOnly ? checkmark : "",
136                 "secure": cookie.secure ? checkmark : "",
137             };
138
139             if (cookie.type !== WI.CookieType.Request)
140                 data["expires"] = cookie.session ? WI.UIString("Session") : new Date(cookie.expires).toLocaleString();
141
142             var node = new WI.DataGridNode(data);
143             node.cookie = cookie;
144
145             this._dataGrid.appendChild(node);
146         }
147
148         this._dataGrid.sortColumnIdentifier = "name";
149         this._dataGrid.createSettings("cookie-storage-content-view");
150     }
151
152     _filterCookies(cookies)
153     {
154         let resourceMatchesStorageDomain = (resource) => {
155             let urlComponents = resource.urlComponents;
156             return urlComponents && urlComponents.host && urlComponents.host === this.representedObject.host;
157         };
158
159         let allResources = [];
160         for (let frame of WI.frameResourceManager.frames) {
161             // The main resource isn't in the list of resources, so add it as a candidate.
162             allResources.push(frame.mainResource);
163             allResources = allResources.concat(frame.resourceCollection.toArray());
164         }
165
166         let resourcesForDomain = allResources.filter(resourceMatchesStorageDomain);
167
168         let cookiesForDomain = cookies.filter((cookie) => {
169             return resourcesForDomain.some((resource) => {
170                 return WI.CookieStorageObject.cookieMatchesResourceURL(cookie, resource.url);
171             });
172         });
173         return cookiesForDomain;
174     }
175
176     _sortDataGrid()
177     {
178         function localeCompare(field, nodeA, nodeB)
179         {
180             return (nodeA.data[field] + "").extendedLocaleCompare(nodeB.data[field] + "");
181         }
182
183         function numberCompare(field, nodeA, nodeB)
184         {
185             return nodeA.cookie[field] - nodeB.cookie[field];
186         }
187
188         function expiresCompare(nodeA, nodeB)
189         {
190             if (nodeA.cookie.session !== nodeB.cookie.session)
191                 return nodeA.cookie.session ? -1 : 1;
192
193             if (nodeA.cookie.session)
194                 return 0;
195
196             return nodeA.cookie.expires - nodeB.cookie.expires;
197         }
198
199         var comparator;
200         switch (this._dataGrid.sortColumnIdentifier) {
201             case "value": comparator = localeCompare.bind(this, "value"); break;
202             case "domain": comparator = localeCompare.bind(this, "domain"); break;
203             case "path": comparator = localeCompare.bind(this, "path"); break;
204             case "expires": comparator = expiresCompare; break;
205             case "size": comparator = numberCompare.bind(this, "size"); break;
206             case "http": comparator = localeCompare.bind(this, "http"); break;
207             case "secure": comparator = localeCompare.bind(this, "secure"); break;
208             case "name":
209             default: comparator = localeCompare.bind(this, "name"); break;
210         }
211
212         console.assert(comparator);
213         this._dataGrid.sortNodes(comparator);
214     }
215
216     _deleteCallback(node)
217     {
218         if (!node || !node.cookie)
219             return;
220
221         var cookie = node.cookie;
222         var cookieURL = (cookie.secure ? "https://" : "http://") + cookie.domain + cookie.path;
223         PageAgent.deleteCookie(cookie.name, cookieURL);
224
225         this.update();
226     }
227
228     _refreshButtonClicked(event)
229     {
230         this.update();
231     }
232 };
233
234 WI.CookieType = {
235     Request: 0,
236     Response: 1
237 };