├── .gitignore ├── lzip128.png ├── lzip16.png ├── lzip48.png ├── devtools.html ├── devtools.js ├── README.md ├── manifest.json ├── LICENSE.md ├── background.js ├── getstorage.js ├── panel.js ├── panel.html └── lz-string.js /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | -------------------------------------------------------------------------------- /lzip128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriserickson/lz-localstorage-chrome-extension/HEAD/lzip128.png -------------------------------------------------------------------------------- /lzip16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriserickson/lz-localstorage-chrome-extension/HEAD/lzip16.png -------------------------------------------------------------------------------- /lzip48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriserickson/lz-localstorage-chrome-extension/HEAD/lzip48.png -------------------------------------------------------------------------------- /devtools.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /devtools.js: -------------------------------------------------------------------------------- 1 | chrome.devtools.panels.create("LZ Storage", "lzip128.png", "panel.html", function(panel) { 2 | 3 | }); 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## About 2 | 3 | This is a way to look at Local Storage when the storage is being encrypted with [LZString.js](https://github.com/pieroxy/lz-string). Also 4 | might be useful since it pretty prints JSON (double click on any row to get the Pretty Printed value). Very feature bare, banged 5 | out in a couple of hours as a tool I needed. 6 | 7 | ## Download 8 | 9 | Download from the [Chrome Web Store](https://chrome.google.com/webstore/detail/lzipped-local-storage/beicplgjaeliclenmidelkloajghllll). 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "name": "LZipped Local Storage", 4 | "version": "0.2.2", 5 | "description": "LZipped Local Storage", 6 | "icons": { 7 | "16": "lzip16.png", 8 | "48": "lzip48.png", 9 | "128": "lzip128.png" 10 | }, 11 | "devtools_page": "devtools.html", 12 | "background": { 13 | "persistent": false, 14 | "scripts": [ 15 | "background.js" 16 | ] 17 | }, 18 | "permissions": [ 19 | "background", 20 | "http://*/*", 21 | "https://*/*" 22 | ], 23 | "web_accessible_resources": [ 24 | "getstorage.js", 25 | "background.js" 26 | ] 27 | } -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Kris Erickson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /background.js: -------------------------------------------------------------------------------- 1 | // Chrome automatically creates a background.html page for this to execute. 2 | // This can access the inspected page via executeScript 3 | // 4 | // Can use: 5 | // chrome.tabs.* 6 | // chrome.extension.* 7 | 8 | 9 | chrome.extension.onConnect.addListener(function (port) { 10 | 11 | var _loadedTabId; 12 | 13 | var extensionListener = function (message, sender, sendResponse) { 14 | 15 | function getStorage(tabId, type) { 16 | chrome.tabs.executeScript(tabId, {code: 'lzLocalStorageGetLocalStorage.getStorage("' + type + '");'}) 17 | } 18 | 19 | //port.postMessage('test'); 20 | if (message.action) { 21 | var tabId = message.tabId; 22 | if (message.action == 'load' || (message.action == 'changeStorage' && !_loadedTabId)) { 23 | chrome.tabs.executeScript(tabId, {code: 'window.lzLocalStorageGetLocalStorage'}, function (res) { 24 | if (!res || !res[0]) { 25 | chrome.tabs.executeScript(tabId, {file: 'getstorage.js'}, function () { 26 | _loadedTabId = tabId; 27 | getStorage(tabId, message.storageType); 28 | }); 29 | } else { 30 | getStorage(tabId, message.storageType); 31 | } 32 | }); 33 | } else if (message.action == 'changeStorage' && _loadedTabId) { 34 | getStorage(tabId, message.storageType); 35 | } else if (message.action == 'storage') { 36 | port.postMessage(message); 37 | } 38 | } 39 | }; 40 | 41 | // Listens to messages sent from the panel 42 | chrome.extension.onMessage.addListener(extensionListener); 43 | 44 | port.onDisconnect.addListener(function(port) { 45 | chrome.extension.onMessage.removeListener(extensionListener); 46 | if (_loadedTabId) { 47 | chrome.tabs.executeScript(_loadedTabId, {code: 'lzLocalStorageGetLocalStorage.stopStorage();'}); 48 | } 49 | }); 50 | 51 | 52 | 53 | }); 54 | 55 | -------------------------------------------------------------------------------- /getstorage.js: -------------------------------------------------------------------------------- 1 | if (!window.lzLocalStorageGetLocalStorage) { 2 | 3 | (function () { 4 | 5 | function stringHash(str) { 6 | var hash = 5381; 7 | var length = str.length; 8 | 9 | while (length) { 10 | hash = (hash * 33) ^ str.charCodeAt(length); 11 | length -= 1; 12 | } 13 | 14 | /* JavaScript does bitwise operations (like XOR, above) on 32-bit signed 15 | * integers. Since we want the results to be always positive, convert the 16 | * signed int to an unsigned by doing an unsigned bitshift. */ 17 | return hash >>> 0; 18 | } 19 | 20 | 21 | function LZStorageGetLocalStorage() { 22 | 23 | var _hashResult = ''; 24 | var _timeout = 0; 25 | var _type; 26 | var self = this; 27 | 28 | function sendObjectToDevTools(message) { 29 | // The callback here can be used to execute something on receipt 30 | chrome.extension.sendMessage(message, function (message) { 31 | }); 32 | } 33 | 34 | function checkStorage() { 35 | var storageEngine = _type == 'session' ? window.sessionStorage : window.localStorage; 36 | var hash = ''; 37 | for (var i in storageEngine) { 38 | if (storageEngine.hasOwnProperty(i)) { 39 | hash += stringHash(storageEngine[i]).toString(); 40 | } 41 | } 42 | var hashResult = stringHash(hash); 43 | if (_hashResult != hashResult) { 44 | self.getStorage(); 45 | } 46 | _timeout = setTimeout(checkStorage, 5000); 47 | } 48 | 49 | this.getStorage = function (type) { 50 | if (_timeout) { 51 | clearTimeout(_timeout); 52 | _timeout = 0; 53 | 54 | } 55 | if (type) { 56 | _type = type; 57 | } 58 | var storageEngine = _type == 'session' ? window.sessionStorage : window.localStorage; 59 | var storageInfo = {}; 60 | var hash = ''; 61 | for (var i in storageEngine) { 62 | if (storageEngine.hasOwnProperty(i)) { 63 | storageInfo[i] = storageEngine[i]; 64 | hash += stringHash(storageInfo[i]).toString(); 65 | } 66 | } 67 | _hashResult = stringHash(hash); 68 | sendObjectToDevTools({action: 'storage', data: storageInfo, engine: _type}); 69 | _timeout = setTimeout(checkStorage, 5000); 70 | }; 71 | 72 | this.stopStorage = function () { 73 | if (_timeout) { 74 | clearTimeout(_timeout); 75 | _timeout = 0; 76 | } 77 | }; 78 | 79 | } 80 | 81 | window.lzLocalStorageGetLocalStorage = new LZStorageGetLocalStorage(); 82 | 83 | 84 | })(); 85 | } -------------------------------------------------------------------------------- /panel.js: -------------------------------------------------------------------------------- 1 | // dirty globals here :( 2 | var compressionType = 'utf16'; 3 | var lastData = {}; 4 | 5 | /** 6 | * Convert a json string into 7 | * @param jsonStr 8 | * @returns {string|XML} 9 | */ 10 | function syntaxHighlightJson(jsonStr) { 11 | try { 12 | var jsonObj = JSON.parse(jsonStr); 13 | var json = JSON.stringify(jsonObj, null, 4); 14 | } catch (e) { 15 | // If the json parsing fails... 16 | json = jsonStr; 17 | } 18 | json = json.replace(/&/g, '&').replace(//g, '>'); 19 | return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) { 20 | var cls = 'number'; 21 | if (/^"/.test(match)) { 22 | if (/:$/.test(match)) { 23 | cls = 'key'; 24 | } else { 25 | cls = 'string'; 26 | } 27 | } else if (/true|false/.test(match)) { 28 | cls = 'boolean'; 29 | } else if (/null/.test(match)) { 30 | cls = 'null'; 31 | } 32 | return '' + match + ''; 33 | }); 34 | } 35 | 36 | function humanFileSize(size) { 37 | if (size > 0) { 38 | var i = Math.floor(Math.log(size) / Math.log(1024)); 39 | return ( size / Math.pow(1024, i) ).toFixed(2) * 1 + ['B', 'KB', 'MB', 'GB', 'TB'][i]; 40 | } else { 41 | return '0 Bytes'; 42 | } 43 | } 44 | 45 | /** 46 | * On - Do event delegation for dynamic elements. 47 | * @param elSelector 48 | * @param eventName 49 | * @param selector 50 | * @param fn 51 | */ 52 | function on(elSelector, eventName, selector, fn) { 53 | var element = document.querySelector(elSelector); 54 | 55 | element.addEventListener(eventName, function(event) { 56 | var possibleTargets = element.querySelectorAll(selector); 57 | var target = event.target; 58 | 59 | for (var i = 0, l = possibleTargets.length; i < l; i++) { 60 | var el = target; 61 | var p = possibleTargets[i]; 62 | 63 | while(el && el !== element) { 64 | if (el === p) { 65 | return fn.call(p, event); 66 | } 67 | 68 | el = el.parentNode; 69 | } 70 | } 71 | }); 72 | } 73 | 74 | function decodeString(string) { 75 | var types = { 76 | utf16: 'decompressFromUTF16', 77 | utf16_unsafe: 'decompress', 78 | base64: 'decompressFromBase64', 79 | uriSafe: 'decompressFromEncodedURIComponent', 80 | uint8: 'decompressFromUint8Array' 81 | }; 82 | 83 | if(types[compressionType]) { 84 | try { 85 | var decoded = LZString[types[compressionType]](string); 86 | 87 | if(decoded === null) { 88 | // decoding failed 89 | return string; 90 | } 91 | 92 | return decoded; 93 | 94 | } catch(e) { 95 | return string 96 | } 97 | } 98 | 99 | return string; 100 | } 101 | 102 | function sanitize(str) { 103 | return str.replace(//g, '>'); 105 | } 106 | function updateUI(data) { 107 | var html = ''; 108 | var uncompressedSize = 0; 109 | var compressedSize = 0; 110 | 111 | for (var key in data) { 112 | if (data.hasOwnProperty(key)) { 113 | var value = data[key]; 114 | var compressed = false; 115 | if (value.charCodeAt(0) > 255 || /base64|uri/.test(compressionType)) { 116 | value = decodeString(data[key]); 117 | compressed = value !== data[key]; 118 | } 119 | 120 | compressedSize += data[key].length; 121 | uncompressedSize += value.length; 122 | var size = compressed ? '' + humanFileSize(data[key].length * 2) + ' (' + humanFileSize(value.length * 2) + ')' : 123 | '' + humanFileSize(value.length * 2) + ''; 124 | html += 'Key |
263 | Size |
264 | Value |
265 | 266 | |
|---|