1 <html> 2 <head> 3 <style> 4 table { 5 border-collapse:collapse; 6 } 7 8 td { 9 border: 1px solid black; 10 padding-left: 5px; 11 } 12 13 td.button { 14 border: none; 15 } 16 17 td.cookie_count { 18 text-align: right; 19 } 20 21 </style> 22 23 <script> 24 25 if (!chrome.cookies) { 26 chrome.cookies = chrome.experimental.cookies; 27 } 28 29 // A simple Timer class. 30 function Timer() { 31 this.start_ = new Date(); 32 33 this.elapsed = function() { 34 return (new Date()) - this.start_; 35 } 36 37 this.reset = function() { 38 this.start_ = new Date(); 39 } 40 } 41 42 // Compares cookies for "key" (name, domain, etc.) equality, but not "value" 43 // equality. 44 function cookieMatch(c1, c2) { 45 return (c1.name == c2.name) && (c1.domain == c2.domain) && 46 (c1.hostOnly == c2.hostOnly) && (c1.path == c2.path) && 47 (c1.secure == c2.secure) && (c1.httpOnly == c2.httpOnly) && 48 (c1.session == c2.session) && (c1.storeId == c2.storeId); 49 } 50 51 // Returns an array of sorted keys from an associative array. 52 function sortedKeys(array) { 53 var keys = []; 54 for (var i in array) { 55 keys.push(i); 56 } 57 keys.sort(); 58 return keys; 59 } 60 61 // Shorthand for document.querySelector. 62 function select(selector) { 63 return document.querySelector(selector); 64 } 65 66 // An object used for caching data about the browser's cookies, which we update 67 // as notifications come in. 68 function CookieCache() { 69 this.cookies_ = {}; 70 71 this.reset = function() { 72 this.cookies_ = {}; 73 } 74 75 this.add = function(cookie) { 76 var domain = cookie.domain; 77 if (!this.cookies_[domain]) { 78 this.cookies_[domain] = []; 79 } 80 this.cookies_[domain].push(cookie); 81 }; 82 83 this.remove = function(cookie) { 84 var domain = cookie.domain; 85 if (this.cookies_[domain]) { 86 var i = 0; 87 while (i < this.cookies_[domain].length) { 88 if (cookieMatch(this.cookies_[domain][i], cookie)) { 89 this.cookies_[domain].splice(i, 1); 90 } else { 91 i++; 92 } 93 } 94 if (this.cookies_[domain].length == 0) { 95 delete this.cookies_[domain]; 96 } 97 } 98 }; 99 100 // Returns a sorted list of cookie domains that match |filter|. If |filter| is 101 // null, returns all domains. 102 this.getDomains = function(filter) { 103 var result = []; 104 sortedKeys(this.cookies_).forEach(function(domain) { 105 if (!filter || domain.indexOf(filter) != -1) { 106 result.push(domain); 107 } 108 }); 109 return result; 110 } 111 112 this.getCookies = function(domain) { 113 return this.cookies_[domain]; 114 }; 115 } 116 117 118 var cache = new CookieCache(); 119 120 121 function removeAllForFilter() { 122 var filter = select("#filter").value; 123 var timer = new Timer(); 124 cache.getDomains(filter).forEach(function(domain) { 125 removeCookiesForDomain(domain); 126 }); 127 } 128 129 function removeAll() { 130 var all_cookies = []; 131 cache.getDomains().forEach(function(domain) { 132 cache.getCookies(domain).forEach(function(cookie) { 133 all_cookies.push(cookie); 134 }); 135 }); 136 cache.reset(); 137 var count = all_cookies.length; 138 var timer = new Timer(); 139 for (var i = 0; i < count; i++) { 140 removeCookie(all_cookies[i]); 141 } 142 timer.reset(); 143 chrome.cookies.getAll({}, function(cookies) { 144 for (var i in cookies) { 145 cache.add(cookies[i]); 146 removeCookie(cookies[i]); 147 } 148 }); 149 } 150 151 function removeCookie(cookie) { 152 var url = "http" + (cookie.secure ? "s" : "") + "://" + cookie.domain + 153 cookie.path; 154 chrome.cookies.remove({"url": url, "name": cookie.name}); 155 } 156 157 function removeCookiesForDomain(domain) { 158 var timer = new Timer(); 159 cache.getCookies(domain).forEach(function(cookie) { 160 removeCookie(cookie); 161 }); 162 } 163 164 function resetTable() { 165 var table = select("#cookies"); 166 while (table.rows.length > 1) { 167 table.deleteRow(table.rows.length - 1); 168 } 169 } 170 171 var reload_scheduled = false; 172 173 function scheduleReloadCookieTable() { 174 if (!reload_scheduled) { 175 reload_scheduled = true; 176 setTimeout(reloadCookieTable, 250); 177 } 178 } 179 180 function reloadCookieTable() { 181 reload_scheduled = false; 182 183 var filter = select("#filter").value; 184 185 var domains = cache.getDomains(filter); 186 187 select("#filter_count").innerText = domains.length; 188 select("#total_count").innerText = cache.getDomains().length; 189 190 select("#delete_all_button").innerHTML = ""; 191 if (domains.length) { 192 var button = document.createElement("button"); 193 button.onclick = removeAllForFilter; 194 button.innerText = "delete all " + domains.length; 195 select("#delete_all_button").appendChild(button); 196 } 197 198 resetTable(); 199 var table = select("#cookies"); 200 201 domains.forEach(function(domain) { 202 var cookies = cache.getCookies(domain); 203 var row = table.insertRow(-1); 204 row.insertCell(-1).innerText = domain; 205 var cell = row.insertCell(-1); 206 cell.innerText = cookies.length; 207 cell.setAttribute("class", "cookie_count"); 208 209 var button = document.createElement("button"); 210 button.innerText = "delete"; 211 button.onclick = (function(dom){ 212 return function() { 213 removeCookiesForDomain(dom); 214 }; 215 }(domain)); 216 var cell = row.insertCell(-1); 217 cell.appendChild(button); 218 cell.setAttribute("class", "button"); 219 }); 220 } 221 222 function focusFilter() { 223 select("#filter").focus(); 224 } 225 226 function resetFilter() { 227 var filter = select("#filter"); 228 filter.focus(); 229 if (filter.value.length > 0) { 230 filter.value = ""; 231 reloadCookieTable(); 232 } 233 } 234 235 var ESCAPE_KEY = 27; 236 window.onkeydown = function(event) { 237 if (event.keyCode == ESCAPE_KEY) { 238 resetFilter(); 239 } 240 } 241 242 function listener(info) { 243 cache.remove(info.cookie); 244 if (!info.removed) { 245 cache.add(info.cookie); 246 } 247 scheduleReloadCookieTable(); 248 } 249 250 function startListening() { 251 chrome.cookies.onChanged.addListener(listener); 252 } 253 254 function stopListening() { 255 chrome.cookies.onChanged.removeListener(listener); 256 } 257 258 function onload() { 259 focusFilter(); 260 var timer = new Timer(); 261 chrome.cookies.getAll({}, function(cookies) { 262 startListening(); 263 start = new Date(); 264 for (var i in cookies) { 265 cache.add(cookies[i]); 266 } 267 timer.reset(); 268 reloadCookieTable(); 269 }); 270 } 271 272 273 </script> 274 </head> 275 276 <body onload="onload()" onclick="focusFilter()"> 277 <h2>Cookies! ... Nom Nom Nom...</h2> 278 <button onclick="removeAll()">DELETE ALL!</button> 279 <div id="filter_div"> 280 Filter: <input id="filter" type="text" oninput="reloadCookieTable()"> 281 <button onclick="resetFilter()">x</button> 282 </div> 283 <br> 284 <div id="summary_div"> 285 Showing <span id="filter_count"></span> of <span id="total_count"></span> cookie domains. 286 <span id="delete_all_button"></span> 287 </div> 288 <br> 289 <table id="cookies"> 290 <tr class="header"> 291 <th>Name</th> 292 <th>#Cookies</th> 293 </tr> 294 </table> 295 296 </body> 297 </html> 298