Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / content / browser / resources / appcache / appcache_internals.js
blob49388eb51bbe79fba5d5451bfdba2a7332b4f997
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() {
6   'use strict';
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) {
20     this.url = url;
21     this.path = path;
22     this.link = link;
23   }
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;
31   }
33   function getFirstAncestor(node, selector) {
34     while(node) {
35       if (selector(node)) {
36         break;
37       }
38       node = node.parentNode;
39     }
40     return node;
41   }
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];
47       });
48     }) || null;
49   }
51   function removeFromList(list, item, properties) {
52     var pos = 0;
53     while (pos < list.length) {
54       var candidate = list[pos];
55       if (properties.every(function(key) {
56         return candidate[key] == item[key];
57       })) {
58         list.splice(pos, 1);
59       } else {
60         pos++;
61       }
62     }
63   }
65   function initialize() {
66     chrome.send(GET_ALL_APPCACHE);
67   }
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}),
76         template);
77     var removeLinks = container.querySelectorAll('a.remove-manifest');
78     for (var i = 0; i < removeLinks.length; ++i) {
79       removeLinks[i].onclick = deleteAppCacheInfoEventHandler;
80     }
81     var viewLinks = container.querySelectorAll('a.view-details');
82     for (i = 0; i < viewLinks.length; ++i) {
83       viewLinks[i].onclick = getAppCacheInfoEventHandler;
84     }
85   }
87   function getAppCacheInfoEventHandler(event) {
88     var link = event.target;
89     if (link.text.indexOf(VIEW_DETAILS_TEXT) === -1) {
90       hideAppCacheInfo(link);
91     } else {
92       var manifestURL = getFirstAncestor(link, function(node) {
93         return !!node.manifestURL;
94       }).manifestURL;
95       var partitionPath = getFirstAncestor(link, function(node) {
96         return !!node.partitionPath;
97       }).partitionPath;
98       var manifest = new Manifest(manifestURL, partitionPath, link);
99       if (getItemByProperties(manifestsToView,
100                               ['url', 'path'],
101                               [manifestURL, partitionPath])) {
102         return;
103       }
104       manifestsToView.push(manifest);
105       chrome.send(GET_APPCACHE_DETAILS, [partitionPath, manifestURL]);
106     }
107   }
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;
114   }
116   function onAppCacheDetailsReady(manifestURL, partitionPath, details) {
117     if (!details) {
118       console.log('Cannot show details for "' + manifestURL + '" on partition '
119                    + '"' + partitionPath + '".');
120       return;
121     }
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);
131     jstProcess(
132         new JsEvalContext({items: simplifyAppCacheInfo(details)}), template);
133     var fileLinks =
134         container.querySelectorAll('a.appcache-info-template-file-url');
135     for (var i = 0; i < fileLinks.length; ++i) {
136       fileLinks[i].onclick = getFileContentsEventHandler;
137     }
138     link.text = HIDE_DETAILS_TEXT;
139   }
141   function simplifyAppCacheInfo(detailsVector) {
142     var simpleVector = [];
143     for (var index = 0; index < detailsVector.length; ++index) {
144       var details = detailsVector[index];
145       var properties = [];
146       if (details.isManifest) {
147         properties.push('Manifest');
148       }
149       if (details.isExplicit) {
150         properties.push('Explicit');
151       }
152       if (details.isMaster) {
153         properties.push('Master');
154       }
155       if (details.isForeign) {
156         properties.push('Foreign');
157       }
158       if (details.isIntercept) {
159         properties.push('Intercept');
160       }
161       if (details.isFallback) {
162         properties.push('Fallback');
163       }
164       properties = properties.join(',');
165       simpleVector.push({
166         size : details.size,
167         properties : properties,
168         fileUrl : details.url,
169         responseId : details.responseId
170       });
171     }
172     return simpleVector;
173   }
175   function deleteAppCacheInfoEventHandler(event) {
176     var link = event.target;
177     var manifestURL = getFirstAncestor(link, function(node) {
178       return !!node.manifestURL;
179     }).manifestURL;
180     var partitionPath = getFirstAncestor(link, function(node) {
181       return !!node.partitionPath;
182     }).partitionPath;
183     var manifest = new Manifest(manifestURL, partitionPath, link);
184     manifestsToDelete.push(manifest);
185     chrome.send(DELETE_APPCACHE, [partitionPath, manifestURL]);
186   }
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';
195       });
196       var appcacheNode = getFirstAncestor(link, function(node) {
197         return node.className === 'appcache-item';
198       });
199       appcacheItemNode.parentNode.removeChild(appcacheItemNode);
200       if (appcacheNode.querySelectorAll('.appcache-info-item').length === 0) {
201         appcacheNode.parentNode.removeChild(appcacheNode);
202       }
203     } else if (!deleted) {
204       // For some reason, the delete command failed.
205       console.log('Manifest "' + manifestURL + '" on partition "'
206                   + partitionPath + ' cannot be accessed.');
207     }
208   }
210   function getFileContentsEventHandler(event) {
211     var link = event.target;
212     var partitionPath = getFirstAncestor(link, function(node) {
213       return !!node.partitionPath;
214     }).partitionPath;
215     var manifestURL = getFirstAncestor(link, function(node) {
216       return !!node.manifestURL;
217     }).manifestURL;
218     var groupId = getFirstAncestor(link, function(node) {
219       return !!node.groupId;
220     }).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]);
231     }
232   }
234   function onFileDetailsFailed(response, code) {
235     var request =
236       getItemByProperties(
237         fileDetailsRequests,
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 +').');
243     removeFromList(
244       fileDetailsRequests,
245       request,
246       ['manifestURL', 'groupId', 'responseId']);
247   }
249   function onFileDetailsReady(response, headers, raw_data) {
250     var request =
251       getItemByProperties(
252         fileDetailsRequests,
253         ['manifestURL', 'groupId', 'responseId'],
254         [response.manifestURL, response.groupId, response.responseId]);
255     removeFromList(
256       fileDetailsRequests,
257       request,
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);
278   }
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'));
284     }
285     var destStyle=  dest.querySelector('style');
286     var tmp = '';
287     for (var i = 0; i < styles.length; ++i) {
288       tmp += styles[i].innerHTML;
289     }
290     destStyle.innerHTML = tmp;
291   }
293   return {
294     initialize: initialize,
295     onAllAppCacheInfoReady: onAllAppCacheInfoReady,
296     onAppCacheInfoDeleted: onAppCacheInfoDeleted,
297     onAppCacheDetailsReady : onAppCacheDetailsReady,
298     onFileDetailsReady : onFileDetailsReady,
299     onFileDetailsFailed: onFileDetailsFailed
300   };
304 document.addEventListener('DOMContentLoaded', appcache.initialize);