// Copyright 2015 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. cr.define('appcache', function() { 'use strict'; var VIEW_DETAILS_TEXT = 'View Details'; var HIDE_DETAILS_TEXT = 'Hide Details'; var GET_ALL_APPCACHE = 'getAllAppCache'; var DELETE_APPCACHE = 'deleteAppCache'; var GET_APPCACHE_DETAILS = 'getAppCacheDetails'; var GET_FILE_DETAILS = 'getFileDetails'; var manifestsToView = []; var manifestsToDelete = []; var fileDetailsRequests = []; function Manifest(url, path, link) { this.url = url; this.path = path; this.link = link; } function FileRequest(fileURL, manifestURL, path, groupId, responseId) { this.fileURL = fileURL; this.partitionPath = path; this.manifestURL = manifestURL; this.groupId = groupId; this.responseId = responseId; } function getFirstAncestor(node, selector) { while(node) { if (selector(node)) { break; } node = node.parentNode; } return node; } function getItemByProperties(list, properties, values) { return list.find(function(candidate) { return properties.every(function(key, i) { return candidate[key] == values[i]; }); }) || null; } function removeFromList(list, item, properties) { var pos = 0; while (pos < list.length) { var candidate = list[pos]; if (properties.every(function(key) { return candidate[key] == item[key]; })) { list.splice(pos, 1); } else { pos++; } } } function initialize() { chrome.send(GET_ALL_APPCACHE); } function onAllAppCacheInfoReady(partition_path, data) { var template = jstGetTemplate('appcache-list-template'); var container = $('appcache-list'); container.appendChild(template); jstProcess(new JsEvalContext( {appcache_vector: data, partition_path: partition_path}), template); var removeLinks = container.querySelectorAll('a.remove-manifest'); for (var i = 0; i < removeLinks.length; ++i) { removeLinks[i].onclick = deleteAppCacheInfoEventHandler; } var viewLinks = container.querySelectorAll('a.view-details'); for (i = 0; i < viewLinks.length; ++i) { viewLinks[i].onclick = getAppCacheInfoEventHandler; } } function getAppCacheInfoEventHandler(event) { var link = event.target; if (link.text.indexOf(VIEW_DETAILS_TEXT) === -1) { hideAppCacheInfo(link); } else { var manifestURL = getFirstAncestor(link, function(node) { return !!node.manifestURL; }).manifestURL; var partitionPath = getFirstAncestor(link, function(node) { return !!node.partitionPath; }).partitionPath; var manifest = new Manifest(manifestURL, partitionPath, link); if (getItemByProperties(manifestsToView, ['url', 'path'], [manifestURL, partitionPath])) { return; } manifestsToView.push(manifest); chrome.send(GET_APPCACHE_DETAILS, [partitionPath, manifestURL]); } } function hideAppCacheInfo(link) { getFirstAncestor(link, function(node) { return node.className === 'appcache-info-item'; }).querySelector('.appcache-details').innerHTML = ''; link.text = VIEW_DETAILS_TEXT; } function onAppCacheDetailsReady(manifestURL, partitionPath, details) { if (!details) { console.log('Cannot show details for "' + manifestURL + '" on partition ' + '"' + partitionPath + '".'); return; } var manifest = getItemByProperties( manifestsToView, ['url', 'path'], [manifestURL, partitionPath]); var link = manifest.link; removeFromList(manifestsToView, manifest, ['url', 'path']); var container = getFirstAncestor(link, function(node) { return node.className === 'appcache-info-item'; }).querySelector('.appcache-details'); var template = jstGetTemplate('appcache-info-template'); container.appendChild(template); jstProcess( new JsEvalContext({items: simplifyAppCacheInfo(details)}), template); var fileLinks = container.querySelectorAll('a.appcache-info-template-file-url'); for (var i = 0; i < fileLinks.length; ++i) { fileLinks[i].onclick = getFileContentsEventHandler; } link.text = HIDE_DETAILS_TEXT; } function simplifyAppCacheInfo(detailsVector) { var simpleVector = []; for (var index = 0; index < detailsVector.length; ++index) { var details = detailsVector[index]; var properties = []; if (details.isManifest) { properties.push('Manifest'); } if (details.isExplicit) { properties.push('Explicit'); } if (details.isMaster) { properties.push('Master'); } if (details.isForeign) { properties.push('Foreign'); } if (details.isIntercept) { properties.push('Intercept'); } if (details.isFallback) { properties.push('Fallback'); } properties = properties.join(','); simpleVector.push({ size : details.size, properties : properties, fileUrl : details.url, responseId : details.responseId }); } return simpleVector; } function deleteAppCacheInfoEventHandler(event) { var link = event.target; var manifestURL = getFirstAncestor(link, function(node) { return !!node.manifestURL; }).manifestURL; var partitionPath = getFirstAncestor(link, function(node) { return !!node.partitionPath; }).partitionPath; var manifest = new Manifest(manifestURL, partitionPath, link); manifestsToDelete.push(manifest); chrome.send(DELETE_APPCACHE, [partitionPath, manifestURL]); } function onAppCacheInfoDeleted(partitionPath, manifestURL, deleted) { var manifest = getItemByProperties( manifestsToDelete, ['url', 'path'], [manifestURL, partitionPath]); if (manifest && deleted) { var link = manifest.link; var appcacheItemNode = getFirstAncestor(link, function(node) { return node.className === 'appcache-info-item'; }); var appcacheNode = getFirstAncestor(link, function(node) { return node.className === 'appcache-item'; }); appcacheItemNode.parentNode.removeChild(appcacheItemNode); if (appcacheNode.querySelectorAll('.appcache-info-item').length === 0) { appcacheNode.parentNode.removeChild(appcacheNode); } } else if (!deleted) { // For some reason, the delete command failed. console.log('Manifest "' + manifestURL + '" on partition "' + partitionPath + ' cannot be accessed.'); } } function getFileContentsEventHandler(event) { var link = event.target; var partitionPath = getFirstAncestor(link, function(node) { return !!node.partitionPath; }).partitionPath; var manifestURL = getFirstAncestor(link, function(node) { return !!node.manifestURL; }).manifestURL; var groupId = getFirstAncestor(link, function(node) { return !!node.groupId; }).groupId; var responseId = link.responseId; if (!getItemByProperties(fileDetailsRequests, ['manifestURL', 'groupId', 'responseId'], [manifestURL, groupId, responseId])) { var fileRequest = new FileRequest(link.innerText, manifestURL, partitionPath, groupId, responseId); fileDetailsRequests.push(fileRequest); chrome.send(GET_FILE_DETAILS, [partitionPath, manifestURL, groupId, responseId]); } } function onFileDetailsFailed(response, code) { var request = getItemByProperties( fileDetailsRequests, ['manifestURL', 'groupId', 'responseId'], [response.manifestURL, response.groupId, response.responseId]); console.log('Failed to get file information for file "' + request.fileURL + '" from partition "' + request.partitionPath + '" (net result code:' + code +').'); removeFromList( fileDetailsRequests, request, ['manifestURL', 'groupId', 'responseId']); } function onFileDetailsReady(response, headers, raw_data) { var request = getItemByProperties( fileDetailsRequests, ['manifestURL', 'groupId', 'responseId'], [response.manifestURL, response.groupId, response.responseId]); removeFromList( fileDetailsRequests, request, ['manifestURL', 'groupId', 'responseId']); var doc = window.open().document; var head = document.createElement('head'); doc.title = 'File Details: '.concat(request.fileURL); var headersDiv = doc.createElement('div'); headersDiv.innerHTML = headers; doc.body.appendChild(headersDiv); var hexDumpDiv = doc.createElement('div'); hexDumpDiv.innerHTML = raw_data; var linkToManifest = doc.createElement('a'); linkToManifest.style.color = "#3C66DD"; linkToManifest.href = request.fileURL; linkToManifest.target = '_blank'; linkToManifest.innerHTML = request.fileURL; doc.body.appendChild(linkToManifest); doc.body.appendChild(headersDiv); doc.body.appendChild(hexDumpDiv); copyStylesFrom(document, doc); } function copyStylesFrom(src, dest) { var styles = src.querySelector('style'); if (dest.getElementsByTagName('style').length < 1) { dest.head.appendChild(dest.createElement('style')); } var destStyle= dest.querySelector('style'); var tmp = ''; for (var i = 0; i < styles.length; ++i) { tmp += styles[i].innerHTML; } destStyle.innerHTML = tmp; } return { initialize: initialize, onAllAppCacheInfoReady: onAllAppCacheInfoReady, onAppCacheInfoDeleted: onAppCacheInfoDeleted, onAppCacheDetailsReady : onAppCacheDetailsReady, onFileDetailsReady : onFileDetailsReady, onFileDetailsFailed: onFileDetailsFailed }; }); document.addEventListener('DOMContentLoaded', appcache.initialize);