diff --git a/js/assets.js b/js/assets.js index beaa73d..1267ad9 100644 --- a/js/assets.js +++ b/js/assets.js @@ -19,7 +19,7 @@ Home: https://github.com/gorhill/httpswitchboard */ -/* global chrome, HTTPSB */ +/* global chrome, HTTPSB, YaMD5 */ /******************************************************************************* @@ -63,10 +63,118 @@ HTTPSB.assets = (function() { var fileSystem; var fileSystemQuota = 40 * 1024 * 1024; var remoteRoot = HTTPSB.projectServerRoot; +var nullFunc = function() {}; /******************************************************************************/ -var nullFunc = function() { }; +var cachedAssetsManager = (function() { + var exports = {}; + var entries = null; + var cachedAssetPathPrefix = 'cached_asset_content://'; + + var getEntries = function(callback) { + if ( entries !== null ) { + callback(entries); + return; + } + var onLoaded = function(bin) { + if ( chrome.runtime.lastError ) { + console.error( + 'HTTP Switchboard> cachedAssetsManager> getEntries():', + chrome.runtime.lastError.message + ); + } + entries = bin.cached_asset_entries || {}; + callback(entries); + }; + chrome.storage.local.get('cached_asset_entries', onLoaded); + }; + + exports.load = function(path, cbSuccess, cbError) { + cbSuccess = cbSuccess || nullFunc; + cbError = cbError || cbSuccess; + var details = { + 'path': path, + 'content': '' + }; + var cachedContentPath = cachedAssetPathPrefix + path; + var onLoaded = function(bin) { + if ( chrome.runtime.lastError ) { + details.error = 'Error: ' + chrome.runtime.lastError.message; + console.error('HTTP Switchboard> cachedAssetsManager.load():', details.error); + cbError(details); + } else { + details.content = bin[cachedContentPath]; + cbSuccess(details); + } + }; + var onEntries = function(entries) { + if ( entries[path] === undefined ) { + details.error = 'Error: not found'; + cbError(details); + return; + } + chrome.storage.local.get(cachedContentPath, onLoaded); + }; + getEntries(onEntries); + }; + + exports.save = function(path, content, cbSuccess, cbError) { + cbSuccess = cbSuccess || nullFunc; + cbError = cbError || cbSuccess; + var details = { + path: path, + content: content + }; + var cachedContentPath = cachedAssetPathPrefix + path; + var bin = {}; + bin[cachedContentPath] = content; + var onSaved = function() { + if ( chrome.runtime.lastError ) { + details.error = 'Error: ' + chrome.runtime.lastError.message; + console.error('HTTP Switchboard> cachedAssetsManager.save():', details.error); + cbError(details); + } else { + cbSuccess(details); + } + }; + var onEntries = function(entries) { + if ( entries[path] === undefined ) { + entries[path] = true; + bin.cached_asset_entries = entries; + } + chrome.storage.local.set(bin, onSaved); + }; + getEntries(onEntries); + }; + + exports.remove = function(pattern) { + var onEntries = function(entries) { + var keystoRemove = []; + var paths = Object.keys(entries); + var i = paths.length; + var path; + while ( i-- ) { + path = paths[i]; + if ( typeof pattern === 'string' && path !== pattern ) { + continue; + } + if ( pattern instanceof RegExp && !pattern.test(path) ) { + continue; + } + keystoRemove.push(cachedAssetPathPrefix + path); + delete entries[path]; + } + if ( keystoRemove.length ) { + chrome.storage.local.remove(keystoRemove); + chrome.storage.local.set({ 'cached_asset_entries': entries }); + } + }; + getEntries(onEntries); + }; + + return exports; +})(); /******************************************************************************/ @@ -83,18 +191,20 @@ var getTextFileFromURL = function(url, onLoad, onError) { /******************************************************************************/ +// https://github.com/gorhill/httpswitchboard/issues/379 +// Remove when I am confident everybody moved to the new storage + // Useful to avoid having to manage a directory tree var cachePathFromPath = function(path) { return path.replace(/\//g, '___'); }; -var pathFromCachePath = function(path) { - return path.replace(/___/g, '/'); -}; - /******************************************************************************/ +// https://github.com/gorhill/httpswitchboard/issues/379 +// Remove when I am confident everybody moved to the new storage + var requestFileSystem = function(onSuccess, onError) { if ( fileSystem ) { onSuccess(fileSystem); @@ -115,6 +225,61 @@ var requestFileSystem = function(onSuccess, onError) { /******************************************************************************/ +// https://github.com/gorhill/httpswitchboard/issues/379 +// Remove when I am confident everybody moved to the new storage + +var oldReadCachedFile = function(path, callback) { + var reportBack = function(content, err) { + var details = { + 'path': path, + 'content': content, + 'error': err + }; + callback(details); + }; + + var onCacheFileLoaded = function() { + // console.log('HTTP Switchboard> readLocalFile() / onCacheFileLoaded()'); + reportBack(this.responseText); + this.onload = this.onerror = null; + }; + + var onCacheFileError = function() { + // This handler may be called under normal circumstances: it appears + // the entry may still be present even after the file was removed. + // console.error('HTTP Switchboard> readLocalFile() / onCacheFileError("%s")', path); + reportBack('', 'Error'); + this.onload = this.onerror = null; + }; + + var onCacheEntryFound = function(entry) { + // console.log('HTTP Switchboard> readLocalFile() / onCacheEntryFound():', entry.toURL()); + // rhill 2014-04-18: `httpsb` query parameter is added to ensure + // the browser cache is bypassed. + getTextFileFromURL(entry.toURL() + '?httpsb=' + Date.now(), onCacheFileLoaded, onCacheFileError); + }; + + var onCacheEntryError = function(err) { + if ( err.name !== 'NotFoundError' ) { + console.error('HTTP Switchboard> readLocalFile() / onCacheEntryError("%s"):', path, err.name); + } + reportBack('', 'Error'); + }; + + var onRequestFileSystemSuccess = function(fs) { + fs.root.getFile(cachePathFromPath(path), null, onCacheEntryFound, onCacheEntryError); + }; + + var onRequestFileSystemError = function(err) { + console.error('HTTP Switchboard> readLocalFile() / onRequestFileSystemError():', err.name); + reportBack('', 'Error'); + }; + + requestFileSystem(onRequestFileSystemSuccess, onRequestFileSystemError); +}; + +/******************************************************************************/ + // Flush cached non-user assets if these are from a prior version. // https://github.com/gorhill/httpswitchboard/issues/212 @@ -126,6 +291,9 @@ var synchronizeCache = function() { } cacheSynchronized = true; + // https://github.com/gorhill/httpswitchboard/issues/379 + // Remove when I am confident everybody moved to the new storage + var directoryReader; var done = function() { directoryReader = null; @@ -139,12 +307,9 @@ var synchronizeCache = function() { var entry; for ( var i = 0; i < n; i++ ) { entry = entries[i]; - // Ignore whatever is in 'user' folder: these are NOT cached entries. - if ( pathFromCachePath(entry.fullPath).indexOf('/assets/user/') >= 0 ) { - continue; - } entry.remove(nullFunc); } + // Read entries until none returned. directoryReader.readEntries(onReadEntries, onReadEntriesError); }; @@ -170,10 +335,33 @@ var synchronizeCache = function() { return done(); } chrome.storage.local.set({ 'extensionLastVersion': currentVersion }); + cachedAssetsManager.remove(/^assets\/(httpsb|thirdparties)\//); + cachedAssetsManager.remove('assets/checksums.txt'); requestFileSystem(onRequestFileSystemSuccess, onRequestFileSystemError); }; - chrome.storage.local.get('extensionLastVersion', onLastVersionRead); + // https://github.com/gorhill/httpswitchboard/issues/89 + // Backward compatiblity. + + var countDown = 2; + + var onUserFiltersSaved = function() { + countDown -= 1; + if ( countDown === 0 ) { + chrome.storage.local.get('extensionLastVersion', onLastVersionRead); + } + }; + + var onUserFiltersLoaded = function(details) { + if ( details.content !== '' ) { + cachedAssetsManager.save(details.path, details.content, onUserFiltersSaved); + } else { + onUserFiltersSaved(); + } + }; + + oldReadCachedFile('assets/user/ubiquitous-blacklisted-hosts.txt', onUserFiltersLoaded); + oldReadCachedFile('assets/user/ubiquitous-whitelisted-hosts.txt', onUserFiltersLoaded); }; /******************************************************************************/ @@ -182,9 +370,11 @@ var readLocalFile = function(path, callback) { var reportBack = function(content, err) { var details = { 'path': path, - 'content': content, - 'error': err + 'content': content }; + if ( err ) { + details.error = err; + } callback(details); }; @@ -194,50 +384,23 @@ var readLocalFile = function(path, callback) { this.onload = this.onerror = null; }; - var onLocalFileError = function(ev) { + var onLocalFileError = function() { console.error('HTTP Switchboard> readLocalFile() / onLocalFileError("%s")', path); reportBack('', 'Error'); this.onload = this.onerror = null; }; - var onCacheFileLoaded = function() { - // console.log('HTTP Switchboard> readLocalFile() / onCacheFileLoaded()'); - reportBack(this.responseText); - this.onload = this.onerror = null; - }; - - var onCacheFileError = function(ev) { - // This handler may be called under normal circumstances: it appears - // the entry may still be present even after the file was removed. - // console.error('HTTP Switchboard> readLocalFile() / onCacheFileError("%s")', path); - getTextFileFromURL(chrome.runtime.getURL(path), onLocalFileLoaded, onLocalFileError); - this.onload = this.onerror = null; - }; - - var onCacheEntryFound = function(entry) { - // console.log('HTTP Switchboard> readLocalFile() / onCacheEntryFound():', entry.toURL()); - // rhill 2014-04-18: `httpsb` query parameter is added to ensure - // the browser cache is bypassed. - getTextFileFromURL(entry.toURL() + '?httpsb=' + Date.now(), onCacheFileLoaded, onCacheFileError); + var onCachedContentLoaded = function(details) { + // console.log('HTTP Switchboard> readLocalFile() / onCachedContentLoaded()'); + reportBack(details.content); }; - var onCacheEntryError = function(err) { - if ( err.name !== 'NotFoundError' ) { - console.error('HTTP Switchboard> readLocalFile() / onCacheEntryError("%s"):', path, err.name); - } - getTextFileFromURL(chrome.runtime.getURL(path), onLocalFileLoaded, onLocalFileError); + var onCachedContentError = function(details) { + // console.error('HTTP Switchboard> readLocalFile() / onCachedContentError("%s")', path); + getTextFileFromURL(chrome.runtime.getURL(details.path), onLocalFileLoaded, onLocalFileError); }; - var onRequestFileSystemSuccess = function(fs) { - fs.root.getFile(cachePathFromPath(path), null, onCacheEntryFound, onCacheEntryError); - }; - - var onRequestFileSystemError = function(err) { - console.error('HTTP Switchboard> readLocalFile() / onRequestFileSystemError():', err.name); - getTextFileFromURL(chrome.runtime.getURL(path), onLocalFileLoaded, onLocalFileError); - }; - - requestFileSystem(onRequestFileSystemSuccess, onRequestFileSystemError); + cachedAssetsManager.load(path, onCachedContentLoaded, onCachedContentError); }; /******************************************************************************/ @@ -263,7 +426,7 @@ var readRemoteFile = function(path, callback) { this.onload = this.onerror = null; }; - var onRemoteFileError = function(ev) { + var onRemoteFileError = function() { console.error('HTTP Switchboard> readRemoteFile() / onRemoteFileError("%s")', path); reportBack('', 'Error'); this.onload = this.onerror = null; @@ -274,75 +437,13 @@ var readRemoteFile = function(path, callback) { remoteRoot + path + '?httpsb=' + Date.now(), onRemoteFileLoaded, onRemoteFileError - ); + ); }; /******************************************************************************/ var writeLocalFile = function(path, content, callback) { - var reportBack = function(err) { - var details = { - 'path': path, - 'content': content, - 'error': err - }; - callback(details); - }; - - var onFileWriteSuccess = function() { - // console.log('HTTP Switchboard> writeLocalFile() / onFileWriteSuccess("%s")', path); - reportBack(); - }; - - var onFileWriteError = function(err) { - console.error('HTTP Switchboard> writeLocalFile() / onFileWriteError("%s"):', path, err.name); - reportBack(err.name); - }; - - var onFileTruncateSuccess = function() { - // console.log('HTTP Switchboard> writeLocalFile() / onFileTruncateSuccess("%s")', path); - this.onwriteend = onFileWriteSuccess; - this.onerror = onFileWriteError; - var blob = new Blob([content], { type: 'text/plain' }); - this.write(blob); - }; - - var onFileTruncateError = function(err) { - console.error('HTTP Switchboard> writeLocalFile() / onFileTruncateError("%s"):', path, err.name); - reportBack(err.name); - }; - - var onCreateFileWriterSuccess = function(fwriter) { - fwriter.onwriteend = onFileTruncateSuccess; - fwriter.onerror = onFileTruncateError; - fwriter.truncate(0); - }; - - var onCreateFileWriterError = function(err) { - console.error('HTTP Switchboard> writeLocalFile() / onCreateFileWriterError("%s"):', path, err.name); - reportBack(err.name); - }; - - var onCacheEntryFound = function(file) { - // console.log('HTTP Switchboard> writeLocalFile() / onCacheEntryFound():', file.toURL()); - file.createWriter(onCreateFileWriterSuccess, onCreateFileWriterError); - }; - - var onCacheEntryError = function(err) { - console.error('HTTP Switchboard> writeLocalFile() / onCacheEntryError("%s"):', path, err.name); - reportBack(err.name); - }; - - var onRequestFileSystemError = function(err) { - console.error('HTTP Switchboard> writeLocalFile() / onRequestFileSystemError():', err.name); - reportBack(err.name); - }; - - var onRequestFileSystem = function(fs) { - fs.root.getFile(cachePathFromPath(path), { create: true }, onCacheEntryFound, onCacheEntryError); - }; - - requestFileSystem(onRequestFileSystem, onRequestFileSystemError); + cachedAssetsManager.save(path, content, callback); }; /******************************************************************************/ @@ -376,7 +477,7 @@ var updateFromRemote = function(details, callback) { writeLocalFile(targetPath, this.responseText, callback); }; - var onRemoteFileError = function(ev) { + var onRemoteFileError = function() { this.onload = this.onerror = null; console.error('HTTPSB> updateFromRemote() / onRemoteFileError("%s"):', remoteURL, this.statusText); reportBackError(); diff --git a/js/background.js b/js/background.js index eaa4d8b..39608e5 100644 --- a/js/background.js +++ b/js/background.js @@ -79,7 +79,7 @@ return { clearBrowserCacheCycle: 0, updateAssetsEvery: 5 * 24 * 60 * 60 * 1000, - projectServerRoot: 'https://raw2.github.com/gorhill/httpswitchboard/master/', + projectServerRoot: 'https://raw.githubusercontent.com/gorhill/httpswitchboard/master/', // list of remote blacklist locations remoteBlacklists: { diff --git a/manifest.json b/manifest.json index 5a08594..29552be 100644 --- a/manifest.json +++ b/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "__MSG_extName__", "short_name": "HTTPSB", - "version": "1.0.0.2", + "version": "1.0.0.3", "description": "__MSG_extShortDesc__", "icons": { "16": "icon_16.png",