1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 cr.define('appcache', function() {
8 var VIEW_DETAILS_TEXT = 'View Details';
9 var HIDE_DETAILS_TEXT = 'Hide Details';
10 var GET_ALL_APPCACHE = 'getAllAppCache';
11 var DELETE_APPCACHE = 'deleteAppCache';
12 var GET_APPCACHE_DETAILS = 'getAppCacheDetails';
13 var GET_FILE_DETAILS = 'getFileDetails';
15 var manifestsToView = [];
16 var manifestsToDelete = [];
17 var fileDetailsRequests = [];
19 function Manifest(url, path, link) {
25 function FileRequest(fileURL, manifestURL, path, groupId, responseId) {
26 this.fileURL = fileURL;
27 this.partitionPath = path;
28 this.manifestURL = manifestURL;
29 this.groupId = groupId;
30 this.responseId = responseId;
33 function getFirstAncestor(node, selector) {
38 node = node.parentNode;
43 function getItemByProperties(list, properties, values) {
44 return list.find(function(candidate) {
45 return properties.every(function(key, i) {
46 return candidate[key] == values[i];
51 function removeFromList(list, item, properties) {
53 while (pos < list.length) {
54 var candidate = list[pos];
55 if (properties.every(function(key) {
56 return candidate[key] == item[key];
65 function initialize() {
66 chrome.send(GET_ALL_APPCACHE);
70 function onAllAppCacheInfoReady(partition_path, data) {
71 var template = jstGetTemplate('appcache-list-template');
72 var container = $('appcache-list');
73 container.appendChild(template);
74 jstProcess(new JsEvalContext(
75 {appcache_vector: data, partition_path: partition_path}),
77 var removeLinks = container.querySelectorAll('a.remove-manifest');
78 for (var i = 0; i < removeLinks.length; ++i) {
79 removeLinks[i].onclick = deleteAppCacheInfoEventHandler;
81 var viewLinks = container.querySelectorAll('a.view-details');
82 for (i = 0; i < viewLinks.length; ++i) {
83 viewLinks[i].onclick = getAppCacheInfoEventHandler;
87 function getAppCacheInfoEventHandler(event) {
88 var link = event.target;
89 if (link.text.indexOf(VIEW_DETAILS_TEXT) === -1) {
90 hideAppCacheInfo(link);
92 var manifestURL = getFirstAncestor(link, function(node) {
93 return !!node.manifestURL;
95 var partitionPath = getFirstAncestor(link, function(node) {
96 return !!node.partitionPath;
98 var manifest = new Manifest(manifestURL, partitionPath, link);
99 if (getItemByProperties(manifestsToView,
101 [manifestURL, partitionPath])) {
104 manifestsToView.push(manifest);
105 chrome.send(GET_APPCACHE_DETAILS, [partitionPath, manifestURL]);
109 function hideAppCacheInfo(link) {
110 getFirstAncestor(link, function(node) {
111 return node.className === 'appcache-info-item';
112 }).querySelector('.appcache-details').innerHTML = '';
113 link.text = VIEW_DETAILS_TEXT;
116 function onAppCacheDetailsReady(manifestURL, partitionPath, details) {
118 console.log('Cannot show details for "' + manifestURL + '" on partition '
119 + '"' + partitionPath + '".');
122 var manifest = getItemByProperties(
123 manifestsToView, ['url', 'path'], [manifestURL, partitionPath]);
124 var link = manifest.link;
125 removeFromList(manifestsToView, manifest, ['url', 'path']);
126 var container = getFirstAncestor(link, function(node) {
127 return node.className === 'appcache-info-item';
128 }).querySelector('.appcache-details');
129 var template = jstGetTemplate('appcache-info-template');
130 container.appendChild(template);
132 new JsEvalContext({items: simplifyAppCacheInfo(details)}), template);
134 container.querySelectorAll('a.appcache-info-template-file-url');
135 for (var i = 0; i < fileLinks.length; ++i) {
136 fileLinks[i].onclick = getFileContentsEventHandler;
138 link.text = HIDE_DETAILS_TEXT;
141 function simplifyAppCacheInfo(detailsVector) {
142 var simpleVector = [];
143 for (var index = 0; index < detailsVector.length; ++index) {
144 var details = detailsVector[index];
146 if (details.isManifest) {
147 properties.push('Manifest');
149 if (details.isExplicit) {
150 properties.push('Explicit');
152 if (details.isMaster) {
153 properties.push('Master');
155 if (details.isForeign) {
156 properties.push('Foreign');
158 if (details.isIntercept) {
159 properties.push('Intercept');
161 if (details.isFallback) {
162 properties.push('Fallback');
164 properties = properties.join(',');
167 properties : properties,
168 fileUrl : details.url,
169 responseId : details.responseId
175 function deleteAppCacheInfoEventHandler(event) {
176 var link = event.target;
177 var manifestURL = getFirstAncestor(link, function(node) {
178 return !!node.manifestURL;
180 var partitionPath = getFirstAncestor(link, function(node) {
181 return !!node.partitionPath;
183 var manifest = new Manifest(manifestURL, partitionPath, link);
184 manifestsToDelete.push(manifest);
185 chrome.send(DELETE_APPCACHE, [partitionPath, manifestURL]);
188 function onAppCacheInfoDeleted(partitionPath, manifestURL, deleted) {
189 var manifest = getItemByProperties(
190 manifestsToDelete, ['url', 'path'], [manifestURL, partitionPath]);
191 if (manifest && deleted) {
192 var link = manifest.link;
193 var appcacheItemNode = getFirstAncestor(link, function(node) {
194 return node.className === 'appcache-info-item';
196 var appcacheNode = getFirstAncestor(link, function(node) {
197 return node.className === 'appcache-item';
199 appcacheItemNode.parentNode.removeChild(appcacheItemNode);
200 if (appcacheNode.querySelectorAll('.appcache-info-item').length === 0) {
201 appcacheNode.parentNode.removeChild(appcacheNode);
203 } else if (!deleted) {
204 // For some reason, the delete command failed.
205 console.log('Manifest "' + manifestURL + '" on partition "'
206 + partitionPath + ' cannot be accessed.');
210 function getFileContentsEventHandler(event) {
211 var link = event.target;
212 var partitionPath = getFirstAncestor(link, function(node) {
213 return !!node.partitionPath;
215 var manifestURL = getFirstAncestor(link, function(node) {
216 return !!node.manifestURL;
218 var groupId = getFirstAncestor(link, function(node) {
219 return !!node.groupId;
221 var responseId = link.responseId;
223 if (!getItemByProperties(fileDetailsRequests,
224 ['manifestURL', 'groupId', 'responseId'],
225 [manifestURL, groupId, responseId])) {
226 var fileRequest = new FileRequest(link.innerText, manifestURL,
227 partitionPath, groupId, responseId);
228 fileDetailsRequests.push(fileRequest);
229 chrome.send(GET_FILE_DETAILS,
230 [partitionPath, manifestURL, groupId, responseId]);
234 function onFileDetailsFailed(response, code) {
238 ['manifestURL', 'groupId', 'responseId'],
239 [response.manifestURL, response.groupId, response.responseId]);
240 console.log('Failed to get file information for file "'
241 + request.fileURL + '" from partition "'
242 + request.partitionPath + '" (net result code:' + code +').');
246 ['manifestURL', 'groupId', 'responseId']);
249 function onFileDetailsReady(response, headers, raw_data) {
253 ['manifestURL', 'groupId', 'responseId'],
254 [response.manifestURL, response.groupId, response.responseId]);
258 ['manifestURL', 'groupId', 'responseId']);
259 var doc = window.open().document;
260 var head = document.createElement('head');
261 doc.title = 'File Details: '.concat(request.fileURL);
262 var headersDiv = doc.createElement('div');
263 headersDiv.innerHTML = headers;
264 doc.body.appendChild(headersDiv);
265 var hexDumpDiv = doc.createElement('div');
266 hexDumpDiv.innerHTML = raw_data;
267 var linkToManifest = doc.createElement('a');
268 linkToManifest.style.color = "#3C66DD";
269 linkToManifest.href = request.fileURL;
270 linkToManifest.target = '_blank';
271 linkToManifest.innerHTML = request.fileURL;
273 doc.body.appendChild(linkToManifest);
274 doc.body.appendChild(headersDiv);
275 doc.body.appendChild(hexDumpDiv);
277 copyStylesFrom(document, doc);
280 function copyStylesFrom(src, dest) {
281 var styles = src.querySelector('style');
282 if (dest.getElementsByTagName('style').length < 1) {
283 dest.head.appendChild(dest.createElement('style'));
285 var destStyle= dest.querySelector('style');
287 for (var i = 0; i < styles.length; ++i) {
288 tmp += styles[i].innerHTML;
290 destStyle.innerHTML = tmp;
294 initialize: initialize,
295 onAllAppCacheInfoReady: onAllAppCacheInfoReady,
296 onAppCacheInfoDeleted: onAppCacheInfoDeleted,
297 onAppCacheDetailsReady : onAppCacheDetailsReady,
298 onFileDetailsReady : onFileDetailsReady,
299 onFileDetailsFailed: onFileDetailsFailed
304 document.addEventListener('DOMContentLoaded', appcache.initialize);