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
);