├── icons ├── logo.png ├── cetus16.png ├── cetus48.png └── cetus128.png ├── extension ├── background.html ├── css │ ├── devpanel.css │ ├── popup.css │ ├── prism-line-highlight.css │ ├── prism-live.css │ ├── prism.css │ ├── normalize.css │ └── styles.css ├── devtools.html ├── devtools.js ├── libs │ ├── patch.js │ ├── theme.js │ ├── navigation.js │ └── disassembler.js ├── thirdparty │ ├── prism │ │ ├── LICENSE │ │ ├── prism-line-numbers.min.js │ │ ├── prism-line-highlight.min.js │ │ ├── prism.js │ │ └── prism-live.js │ └── bliss │ │ ├── LICENSE │ │ └── bliss.js ├── options.html ├── optionspage.js └── extension.js ├── .gitmodules ├── .github └── workflows │ └── CI.yml ├── content ├── thirdparty │ └── stacktrace │ │ ├── LICENSE │ │ └── stacktrace.min.js └── content.js ├── manifest.json ├── shared ├── contentutils.js └── utils.js ├── README.md └── LICENSE /icons/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Qwokka/Cetus/HEAD/icons/logo.png -------------------------------------------------------------------------------- /icons/cetus16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Qwokka/Cetus/HEAD/icons/cetus16.png -------------------------------------------------------------------------------- /icons/cetus48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Qwokka/Cetus/HEAD/icons/cetus48.png -------------------------------------------------------------------------------- /icons/cetus128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Qwokka/Cetus/HEAD/icons/cetus128.png -------------------------------------------------------------------------------- /extension/background.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "shared/wail.min.js"] 2 | path = shared/wail.min.js 3 | url = https://github.com/Qwokka/wail.min.js 4 | -------------------------------------------------------------------------------- /extension/css/devpanel.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: var(--background); 3 | border-radius: 4px; 4 | color: #fff; 5 | } 6 | 7 | -------------------------------------------------------------------------------- /extension/devtools.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /extension/css/popup.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: var(--background); 3 | border-radius: 4px; 4 | height: 700px; 5 | width: 460px; 6 | color: #fff; 7 | } -------------------------------------------------------------------------------- /extension/devtools.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright 2020 Jack Baker 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | chrome.devtools.panels.create("Cetus", 18 | "/icons/cetus48.png", 19 | "/extension/devpanelview.html" 20 | ); 21 | -------------------------------------------------------------------------------- /.github/workflows/CI.yml: -------------------------------------------------------------------------------- 1 | name: Cetus CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | run_ci_tests: 10 | name: Run Regression Tests 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v3 14 | - name: Install Dependencies 15 | uses: actions/setup-node@v2 16 | with: 17 | args: install 18 | - name: Clone Tests 19 | run: | 20 | git clone --recursive https://github.com/Qwokka/Cetus 21 | git clone --recursive https://github.com/Qwokka/CetusCITests 22 | - name: Install Dependencies 23 | run: | 24 | cd CetusCITests 25 | npm i 26 | - name: Run Tests 27 | uses: mujo-code/puppeteer-headful@16.6.0 28 | env: 29 | CI: "true" 30 | with: 31 | args: node CetusCITests/index.js -d Cetus/ 32 | -------------------------------------------------------------------------------- /extension/libs/patch.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright 2020 Jack Baker 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | const disassembleFunction = function(funcBytes) { 18 | const disass = new Disassembler(funcBytes, extension.symbols); 19 | 20 | return disass.disassemble(); 21 | }; 22 | 23 | const assembleFunction = function(funcText) { 24 | const asm = new Assembler(funcText, extension.symbols); 25 | 26 | return asm.assemble(); 27 | }; 28 | -------------------------------------------------------------------------------- /extension/thirdparty/prism/LICENSE: -------------------------------------------------------------------------------- 1 | MIT LICENSE 2 | 3 | Copyright (c) 2012 Lea Verou 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /content/thirdparty/stacktrace/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Eric Wendelin and other contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /extension/options.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 11 |
12 | 13 | 14 |
15 | 16 | 21 |
22 | 23 | 24 |
25 | 26 | 3
27 | 28 |
29 | 30 |
31 | 32 |
33 | 34 | 35 | -------------------------------------------------------------------------------- /extension/thirdparty/bliss/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Lea Verou 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 | -------------------------------------------------------------------------------- /extension/css/prism-line-highlight.css: -------------------------------------------------------------------------------- 1 | pre[data-line] { 2 | position: relative; 3 | /*padding: 1em 0 1em 3em;*/ 4 | } 5 | 6 | .line-highlight { 7 | position: absolute; 8 | left: 0; 9 | right: 0; 10 | padding: inherit 0; 11 | /* margin-top: 1em; /* Same as .prism’s padding-top */ 12 | 13 | background: hsla(24, 20%, 50%,.08); 14 | background: linear-gradient(to right, hsla(24, 20%, 50%,.1) 70%, hsla(24, 20%, 50%,0)); 15 | 16 | pointer-events: none; 17 | 18 | line-height: inherit; 19 | white-space: pre; 20 | } 21 | 22 | .line-highlight:before, 23 | .line-highlight[data-end]:after { 24 | content: attr(data-start); 25 | position: absolute; 26 | top: .4em; 27 | left: .6em; 28 | min-width: 1em; 29 | padding: 0 .5em; 30 | background-color: hsla(24, 20%, 50%,.4); 31 | color: hsl(24, 20%, 95%); 32 | font: bold 65%/1.5 sans-serif; 33 | text-align: center; 34 | vertical-align: .3em; 35 | border-radius: 999px; 36 | text-shadow: none; 37 | box-shadow: 0 1px white; 38 | } 39 | 40 | .line-highlight[data-end]:after { 41 | content: attr(data-end); 42 | top: auto; 43 | bottom: .4em; 44 | } 45 | 46 | .line-numbers .line-highlight:before, 47 | .line-numbers .line-highlight:after { 48 | content: none; 49 | } 50 | -------------------------------------------------------------------------------- /extension/optionspage.js: -------------------------------------------------------------------------------- 1 | const updateOptions = function(optionValues) { 2 | const selectEnableWatch = document.getElementById("selectEnableWatchpoints"); 3 | selectEnableWatch.value = optionValues.enableWatchpoints; 4 | 5 | const selectLogLevel = document.getElementById("selectLogLevel"); 6 | selectLogLevel.value = optionValues.logLevel; 7 | 8 | const rangeWPCount = document.getElementById("rangeWPCount"); 9 | rangeWPCount.value = optionValues.value; 10 | } 11 | 12 | document.getElementById("buttonSaveOptions").onclick = function() { 13 | const newOptions = {}; 14 | 15 | const selectLogLevel = document.getElementById("selectLogLevel"); 16 | newOptions.logLevel = selectLogLevel.value; 17 | 18 | const selectEnableWatch = document.getElementById("selectEnableWatchpoints"); 19 | newOptions.enableWatchpoints = selectEnableWatch.value; 20 | 21 | const rangeWPCount = document.getElementById("rangeWPCount"); 22 | newOptions.wpCount = rangeWPCount.value; 23 | 24 | saveOptions(newOptions); 25 | } 26 | 27 | document.getElementById("rangeWPCount").oninput = function(e) { 28 | document.getElementById("outputWPCount").innerText = e.target.value; 29 | } 30 | 31 | loadOptions(updateOptions); 32 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Cetus", 3 | "version": "1.05", 4 | "description": "Dynamic analysis extension for WASM", 5 | "manifest_version": 2, 6 | "content_scripts": [ 7 | { 8 | "js": ["shared/contentutils.js"], 9 | "matches": [""], 10 | "all_frames": true, 11 | "run_at": "document_start" 12 | }, 13 | { 14 | "js": ["content/content.js"], 15 | "matches": [""], 16 | "all_frames": true, 17 | "run_at": "document_start" 18 | } 19 | ], 20 | "permissions": ["activeTab", "storage"], 21 | "content_security_policy": "default-src 'self'; style-src 'unsafe-inline';", 22 | "web_accessible_resources": [ 23 | "content/cetus.js", 24 | "content/init.js", 25 | "shared/wail.min.js/wail.min.js", 26 | "shared/utils.js", 27 | "content/thirdparty/stacktrace/stacktrace.min.js" 28 | ], 29 | "browser_action": { 30 | "default_title": "Cetus", 31 | "default_popup": "extension/popupview.html" 32 | }, 33 | "devtools_page": "extension/devtools.html", 34 | "background": { 35 | "page": "extension/background.html", 36 | "persistent": true 37 | }, 38 | "icons": { 39 | "16": "icons/cetus16.png", 40 | "48": "icons/cetus48.png", 41 | "128": "icons/cetus128.png" 42 | }, 43 | "options_ui": { 44 | "page": "extension/options.html", 45 | "open_in_tab": false 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /extension/css/prism-live.css: -------------------------------------------------------------------------------- 1 | div.prism-live { 2 | position: relative; 3 | box-sizing: border-box; 4 | display: flex; 5 | flex-flow: column; 6 | } 7 | 8 | textarea.prism-live, 9 | pre.prism-live { 10 | padding: .2rem 2rem; 11 | box-sizing: border-box; 12 | margin: 0; 13 | } 14 | 15 | textarea.prism-live { 16 | position: absolute; 17 | top: 0; 18 | right: 0; 19 | width: 100%; 20 | height: 100%; 21 | z-index: 1; 22 | color: transparent; 23 | /* color: hsla(0,0%,100%,.4); */ 24 | cursor: text; 25 | white-space: pre; 26 | border: 0; 27 | outline: none; 28 | background: transparent; 29 | resize: none; 30 | --selection-background: hsl(320, 80%, 25%); 31 | --selection-color: hsla(0, 0%, 100%, .8); 32 | } 33 | 34 | @supports (not (caret-color: black)) and (-webkit-text-fill-color: black) { 35 | textarea.prism-live { 36 | color: inherit; 37 | -webkit-text-fill-color: transparent; 38 | } 39 | } 40 | 41 | /* Setting specific colors is needed 42 | * because otherwise Firefox shows blank text */ 43 | textarea.prism-live::-moz-selection { 44 | background: var(--selection-background); 45 | color: var(--selection-color); 46 | } 47 | 48 | textarea.prism-live::selection { 49 | background: var(--selection-background); 50 | color: var(--selection-color); 51 | } 52 | 53 | pre.prism-live { 54 | flex: 1; 55 | position: relative; 56 | pointer-events: none; 57 | overflow: hidden; 58 | max-height: 100%; 59 | --scrollbar-width: 17px; 60 | padding-bottom: var(--scrollbar-width); 61 | padding-right: var(--scrollbar-width); 62 | color: var(--caretColor); 63 | } 64 | 65 | pre.prism-live > code:empty::before { 66 | content: " " 67 | } 68 | 69 | -------------------------------------------------------------------------------- /extension/thirdparty/prism/prism-line-numbers.min.js: -------------------------------------------------------------------------------- 1 | !function(){if("undefined"!=typeof self&&self.Prism&&self.document){var l="line-numbers",c=/\n(?!$)/g,m=function(e){var t=a(e)["white-space"];if("pre-wrap"===t||"pre-line"===t){var n=e.querySelector("code"),r=e.querySelector(".line-numbers-rows"),s=e.querySelector(".line-numbers-sizer"),i=n.textContent.split(c);s||((s=document.createElement("span")).className="line-numbers-sizer",n.appendChild(s)),s.style.display="block",i.forEach(function(e,t){s.textContent=e||"\n";var n=s.getBoundingClientRect().height;r.children[t].style.height=n+"px"}),s.textContent="",s.style.display="none"}},a=function(e){return e?window.getComputedStyle?getComputedStyle(e):e.currentStyle||null:null};window.addEventListener("resize",function(){Array.prototype.forEach.call(document.querySelectorAll("pre."+l),m)}),Prism.hooks.add("complete",function(e){if(e.code){var t=e.element,n=t.parentNode;if(n&&/pre/i.test(n.nodeName)&&!t.querySelector(".line-numbers-rows")){for(var r=!1,s=/(?:^|\s)line-numbers(?:\s|$)/,i=t;i;i=i.parentNode)if(s.test(i.className)){r=!0;break}if(r){t.className=t.className.replace(s," "),s.test(n.className)||(n.className+=" line-numbers");var l,a=e.code.match(c),o=a?a.length+1:1,u=new Array(o+1).join("");(l=document.createElement("span")).setAttribute("aria-hidden","true"),l.className="line-numbers-rows",l.innerHTML=u,n.hasAttribute("data-start")&&(n.style.counterReset="linenumber "+(parseInt(n.getAttribute("data-start"),10)-1)),e.element.appendChild(l),m(n),Prism.hooks.run("line-numbers",e)}}}}),Prism.hooks.add("line-numbers",function(e){e.plugins=e.plugins||{},e.plugins.lineNumbers=!0}),Prism.plugins.lineNumbers={getLine:function(e,t){if("PRE"===e.tagName&&e.classList.contains(l)){var n=e.querySelector(".line-numbers-rows"),r=parseInt(e.getAttribute("data-start"),10)||1,s=r+(n.children.length-1);t 49 | document.documentElement.style.setProperty(`--${entity[0]}`, entity[1]) 50 | ); 51 | }; 52 | 53 | document.getElementById("toggleColorScheme").onclick = function() { 54 | storageGet("colorScheme", function(result) { 55 | const colorScheme = result.colorScheme; 56 | 57 | if (colorScheme == "DARK") { 58 | storageSet({colorScheme: "WHITE"}); 59 | setColorScheme("WHITE"); 60 | } 61 | else { 62 | storageSet({colorScheme: "DARK"}); 63 | setColorScheme("DARK"); 64 | } 65 | }); 66 | }; 67 | 68 | // Set the selected theme when the extension window opens 69 | storageGet("colorScheme", function(result) { 70 | const colorScheme = result.colorScheme; 71 | 72 | if (colorScheme == "WHITE") { 73 | setColorScheme("WHITE"); 74 | } 75 | else { 76 | setColorScheme("DARK"); 77 | } 78 | }); 79 | -------------------------------------------------------------------------------- /extension/thirdparty/prism/prism-line-highlight.min.js: -------------------------------------------------------------------------------- 1 | !function(){if("undefined"!=typeof self&&self.Prism&&self.document&&document.querySelector){var t,n=function(){if(void 0===t){var e=document.createElement("div");e.style.fontSize="13px",e.style.lineHeight="1.5",e.style.padding=0,e.style.border=0,e.innerHTML=" 
 ",document.body.appendChild(e),t=38===e.offsetHeight,document.body.removeChild(e)}return t},a=0;Prism.hooks.add("before-sanity-check",function(e){var t=e.element.parentNode,n=t&&t.getAttribute("data-line");if(t&&n&&/pre/i.test(t.nodeName)){var i=0;r(".line-highlight",t).forEach(function(e){i+=e.textContent.length,e.parentNode.removeChild(e)}),i&&/^( \n)+$/.test(e.code.slice(-i))&&(e.code=e.code.slice(0,-i))}}),Prism.hooks.add("complete",function e(t){var n=t.element.parentNode,i=n&&n.getAttribute("data-line");if(n&&i&&/pre/i.test(n.nodeName)){clearTimeout(a);var r=Prism.plugins.lineNumbers,o=t.plugins&&t.plugins.lineNumbers;if(l(n,"line-numbers")&&r&&!o)Prism.hooks.add("line-numbers",e);else s(n,i)(),a=setTimeout(u,1)}}),window.addEventListener("hashchange",u),window.addEventListener("resize",function(){var t=[];r("pre[data-line]").forEach(function(e){t.push(s(e))}),t.forEach(i)})}function r(e,t){return Array.prototype.slice.call((t||document).querySelectorAll(e))}function l(e,t){return t=" "+t+" ",-1<(" "+e.className+" ").replace(/[\n\t]/g," ").indexOf(t)}function i(e){e()}function s(u,e,d){var t=(e="string"==typeof e?e:u.getAttribute("data-line")).replace(/\s+/g,"").split(","),c=+u.getAttribute("data-line-offset")||0,f=(n()?parseInt:parseFloat)(getComputedStyle(u).lineHeight),h=l(u,"line-numbers"),p=h?u:u.querySelector("code")||u,m=[];return t.forEach(function(e){var t=e.split("-"),n=+t[0],i=+t[1]||n,r=u.querySelector('.line-highlight[data-range="'+e+'"]')||document.createElement("div");if(m.push(function(){r.setAttribute("aria-hidden","true"),r.setAttribute("data-range",e),r.className=(d||"")+" line-highlight"}),h&&Prism.plugins.lineNumbers){var o=Prism.plugins.lineNumbers.getLine(u,n),a=Prism.plugins.lineNumbers.getLine(u,i);if(o){var l=o.offsetTop+"px";m.push(function(){r.style.top=l})}if(a){var s=a.offsetTop-o.offsetTop+a.offsetHeight+"px";m.push(function(){r.style.height=s})}}else m.push(function(){r.setAttribute("data-start",n),n 0 || preinstantiateCallbacks.length > 0) { 123 | const injectPatchesStr = JSON.stringify(injectPatches); 124 | 125 | const allCallbacks = { 126 | processor: processorCallbacks, 127 | preinstantiate: preinstantiateCallbacks, 128 | } 129 | 130 | const allCallbacksStr = JSON.stringify(allCallbacks); 131 | 132 | const code = ` 133 | const cetusPatches = ${injectPatchesStr}; 134 | const cetusCallbacks = ${allCallbacksStr}; 135 | `; 136 | 137 | injectCode(code); 138 | } 139 | } 140 | } 141 | 142 | injectScript("/shared/utils.js"); 143 | injectScript("/shared/wail.min.js/wail.min.js"); 144 | injectScript("/content/thirdparty/stacktrace/stacktrace.min.js"); 145 | injectScript("/content/cetus.js"); 146 | injectScript("/content/init.js"); 147 | }); 148 | -------------------------------------------------------------------------------- /shared/utils.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright 2020 Jack Baker 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | const toHex = function(i) { 18 | return '0x' + parseInt(i).toString(16).padStart(8, '0'); 19 | }; 20 | 21 | const toHexByte = function(i) { 22 | return parseInt(i).toString(16).padStart(2, '0'); 23 | }; 24 | 25 | // This function controls how values are displayed to the user 26 | // This could be improved, but as is all integers > 0x1000 are 27 | // displayed as hex 28 | const formatValue = function(value, memType) { 29 | switch (memType) { 30 | case "i8": 31 | case "i16": 32 | case "i32": 33 | case "i64": 34 | return formatInteger(value); 35 | case "f32": 36 | case "f64": 37 | return value; 38 | case "ascii": 39 | return value; 40 | default: 41 | throw new Error("Invalid memory type " + type + " in formatValue()"); 42 | } 43 | }; 44 | 45 | const formatInteger = function(value) { 46 | if (value > 0x1000) { 47 | return toHex(value); 48 | } 49 | 50 | return value; 51 | }; 52 | 53 | const storageSet = function(valueObj) { 54 | chrome.storage.local.set(valueObj); 55 | }; 56 | 57 | const storageGet = function(key, callback) { 58 | chrome.storage.local.get(key, callback); 59 | }; 60 | 61 | const isValidMemType = function(memType) { 62 | switch (memType) { 63 | case "i8": 64 | case "i16": 65 | case "i32": 66 | case "f32": 67 | case "i64": 68 | case "f64": 69 | case "ascii": 70 | case "utf-8": 71 | case "bytes": 72 | return true; 73 | default: 74 | return false; 75 | } 76 | }; 77 | 78 | const getElementSize = function(type) { 79 | let indexSize; 80 | 81 | switch (type) { 82 | case "i8": 83 | case "ascii": 84 | case "bytes": 85 | indexSize = 1; 86 | break; 87 | case "utf-8": 88 | case "i16": 89 | indexSize = 2; 90 | break; 91 | case "i32": 92 | indexSize = 4; 93 | break; 94 | case "i64": 95 | indexSize = 8; 96 | break; 97 | case "f32": 98 | indexSize = 4; 99 | break; 100 | case "f64": 101 | indexSize = 8; 102 | break; 103 | default: 104 | throw new Error("Invalid memory type " + type + " in getElementSize()"); 105 | } 106 | 107 | return indexSize; 108 | }; 109 | 110 | const getMemoryType = function(memType) { 111 | switch (memType) { 112 | case "i8": 113 | case "ascii": 114 | case "bytes": 115 | return Uint8Array; 116 | case "i16": 117 | case "utf-8": 118 | return Uint16Array; 119 | case "i32": 120 | return Uint32Array; 121 | case "i64": 122 | return BigInt64Array; 123 | case "f32": 124 | return Float32Array; 125 | case "f64": 126 | return Float64Array; 127 | default: 128 | throw new Error("Invalid memory type " + memType + " in getMemoryType()"); 129 | } 130 | } 131 | 132 | // Converts the index into a TypedArray into the "real" memory address 133 | // For example, index 0x1000 into the Int32Array translates to address 0x4000 134 | // (0x1000 * sizeof(Int32)) 135 | const indexToRealAddress = function(memIndex, memType) { 136 | const memSize = getElementSize(memType); 137 | 138 | return memIndex * memSize; 139 | }; 140 | 141 | const realAddressToIndex = function(memAddr, memType) { 142 | const memSize = getElementSize(memType); 143 | 144 | return Math.floor(memAddr / memSize); 145 | }; 146 | 147 | // Converts a value to its i64 representation so it can more easily 148 | // be handled from within WASM 149 | const convertToI64 = function(value, type) { 150 | let i32Array; 151 | 152 | const result = { 153 | lower: 0, 154 | upper: 0 155 | }; 156 | 157 | switch (type) { 158 | case "i8": 159 | case "i16": 160 | case "i32": 161 | result.lower = value; 162 | 163 | break; 164 | case "f32": 165 | const floatArray = new Float32Array([value]); 166 | i32Array = new Uint32Array(floatArray.buffer); 167 | 168 | result.lower = i32Array[0]; 169 | 170 | break; 171 | case "i64": 172 | case "f64": 173 | const float64Array = new Float64Array([value]); 174 | i32Array = new Uint32Array(float64Array.buffer); 175 | 176 | result.lower = i32Array[0]; 177 | result.upper = i32Array[1]; 178 | 179 | break; 180 | default: 181 | throw new Error("Conversion " + type + " not implemented in convertToI64()"); 182 | } 183 | 184 | return result; 185 | }; 186 | 187 | // https://stackoverflow.com/questions/3665115/how-to-create-a-file-in-memory-for-user-to-download-but-not-through-server 188 | const downloadText = function(filename, text) { 189 | const anchorElement = document.createElement('a'); 190 | anchorElement.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text)); 191 | anchorElement.setAttribute('download', filename); 192 | 193 | anchorElement.style.display = 'none'; 194 | document.body.appendChild(anchorElement); 195 | 196 | anchorElement.click(); 197 | 198 | document.body.removeChild(anchorElement); 199 | }; 200 | 201 | 202 | // BigInt capable JSON handlers from 203 | // https://golb.hplar.ch/2018/09/javascript-bigint.html 204 | const bigintJsonParse = function(jsonString) { 205 | return JSON.parse(jsonString, (key, value) => { 206 | if (typeof value === 'string' && /^\d+n$/.test(value)) { 207 | return BigInt(value.slice(0, -1)); 208 | } 209 | return value; 210 | }); 211 | } 212 | 213 | const bigintJsonStringify = function(jsonObject) { 214 | return JSON.stringify(jsonObject, (key, value) => { 215 | if (typeof value === 'bigint') { 216 | return value.toString() + 'n'; 217 | } else { 218 | return value; 219 | } 220 | }); 221 | } 222 | 223 | const bigintIsNaN = function(value) { 224 | if (typeof value === "bigint") { 225 | return false; 226 | } 227 | 228 | return isNaN(value); 229 | } 230 | -------------------------------------------------------------------------------- /extension/css/prism.css: -------------------------------------------------------------------------------- 1 | /* PrismJS 1.17.1 2 | https://prismjs.com/download.html#themes=prism-tomorrow&languages=clike+wasm */ 3 | 4 | /** 5 | * prism.js tomorrow night eighties for JavaScript, CoffeeScript, CSS and HTML 6 | * Based on https://github.com/chriskempson/tomorrow-theme 7 | * @author Rose Pritchard 8 | */ 9 | 10 | code[class*="language-"], pre[class*="language-"] { 11 | color: #ccc; 12 | background: none; 13 | font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; 14 | font-size: 16px; 15 | text-align: left; 16 | white-space: pre; 17 | word-spacing: normal; 18 | word-break: normal; 19 | word-wrap: normal; 20 | line-height: 1.5; 21 | -moz-tab-size: 4; 22 | -o-tab-size: 4; 23 | tab-size: 4; 24 | -webkit-hyphens: none; 25 | -moz-hyphens: none; 26 | -ms-hyphens: none; 27 | hyphens: none; 28 | } 29 | 30 | /* Code blocks */ 31 | 32 | pre[class*="language-"] { 33 | padding: 0; 34 | margin: 0 0 24px; 35 | overflow: auto; 36 | } 37 | 38 | :not(pre)>code[class*="language-"], pre[class*="language-"] { 39 | background: #2d2d2d; 40 | } 41 | 42 | /* Inline code */ 43 | 44 | :not(pre)>code[class*="language-"] { 45 | padding: .1em; 46 | border-radius: .3em; 47 | white-space: normal; 48 | } 49 | 50 | .token.comment, .token.block-comment, .token.prolog, .token.doctype, .token.cdata { 51 | color: #999; 52 | } 53 | 54 | .token.punctuation { 55 | color: #ccc; 56 | } 57 | 58 | .token.tag, .token.attr-name, .token.namespace, .token.deleted { 59 | color: #e2777a; 60 | } 61 | 62 | .token.function-name { 63 | color: #6196cc; 64 | } 65 | 66 | .token.boolean, .token.number, .token.function { 67 | color: #f08d49; 68 | } 69 | 70 | .token.property, .token.class-name, .token.constant, .token.symbol { 71 | color: #5352ED; 72 | } 73 | 74 | .token.selector, .token.important, .token.atrule, .token.keyword, .token.builtin { 75 | color: #cc99cd; 76 | } 77 | 78 | .token.string, .token.char, .token.attr-value, .token.regex, .token.variable { 79 | color: #7ec699; 80 | } 81 | 82 | .token.operator, .token.entity, .token.url { 83 | color: #67cdcc; 84 | } 85 | 86 | .token.important, .token.bold { 87 | font-weight: bold; 88 | } 89 | 90 | .token.italic { 91 | font-style: italic; 92 | } 93 | 94 | .token.entity { 95 | cursor: help; 96 | } 97 | 98 | .token.inserted { 99 | color: green; 100 | } 101 | 102 | /** 103 | * prism.js Twilight theme 104 | * Based (more or less) on the Twilight theme originally of Textmate fame. 105 | * @author Remy Bach 106 | */ 107 | 108 | code[class*="language-"], pre[class*="language-"] { 109 | color: white; 110 | background: none; 111 | /* font-family: 'Andale Mono', 'Ubuntu Mono', monospace; */ 112 | font-family: 'Proxima Nova', sans-serif; 113 | font-weight: 600; 114 | font-size: 16px; 115 | text-align: left; 116 | /* text-shadow: 0 -.1em .2em black; */ 117 | white-space: pre; 118 | word-spacing: normal; 119 | word-break: normal; 120 | word-wrap: normal; 121 | line-height: 1.5; 122 | -moz-tab-size: 4; 123 | -o-tab-size: 4; 124 | tab-size: 4; 125 | -webkit-hyphens: none; 126 | -moz-hyphens: none; 127 | -ms-hyphens: none; 128 | hyphens: none; 129 | } 130 | 131 | pre[class*="language-"], :not(pre)>code[class*="language-"] { 132 | background: var(--background); 133 | /* #141414 */ 134 | } 135 | 136 | /* Code blocks */ 137 | 138 | pre[class*="language-"] { 139 | border-radius: 3px; 140 | border: 1px solid var(--inputBorder); 141 | /* #282A2B */ 142 | overflow: auto; 143 | } 144 | 145 | pre[class*="language-"]::-moz-selection { 146 | /* Firefox */ 147 | background: hsl(200, 4%, 16%); 148 | /* #282A2B */ 149 | } 150 | 151 | pre[class*="language-"]::selection { 152 | /* Safari */ 153 | background: hsl(200, 4%, 16%); 154 | /* #282A2B */ 155 | } 156 | 157 | /* Text Selection colour */ 158 | 159 | pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection, code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection { 160 | text-shadow: none; 161 | background: hsla(0, 0%, 93%, 0.15); 162 | /* #EDEDED */ 163 | } 164 | 165 | pre[class*="language-"]::selection, pre[class*="language-"] ::selection, code[class*="language-"]::selection, code[class*="language-"] ::selection { 166 | text-shadow: none; 167 | background: hsla(0, 0%, 93%, 0.15); 168 | /* #EDEDED */ 169 | } 170 | 171 | /* Inline code */ 172 | 173 | :not(pre)>code[class*="language-"] { 174 | border-radius: 3px; 175 | border: .13em solid hsl(0, 0%, 33%); 176 | /* #545454 */ 177 | padding: .15em .2em .05em; 178 | white-space: normal; 179 | } 180 | 181 | .token.comment, .token.prolog, .token.doctype, .token.cdata { 182 | color: hsl(0, 0%, 47%); 183 | /* #777777 */ 184 | } 185 | 186 | .token.punctuation { 187 | opacity: .7; 188 | } 189 | 190 | .namespace { 191 | opacity: .7; 192 | } 193 | 194 | .token.tag, .token.boolean, .token.number, .token.deleted { 195 | color: #5352ED; 196 | /* #CF6A4C */ 197 | } 198 | 199 | .token.keyword, .token.property, .token.selector, .token.constant, .token.symbol, .token.builtin { 200 | color: #F0506C; 201 | /* #F9EE98 */ 202 | } 203 | 204 | .token.attr-name, .token.attr-value, .token.string, .token.char, .token.operator, .token.entity, .token.url, .language-css .token.string, .style .token.string, .token.variable, .token.inserted { 205 | color: hsl(76, 21%, 52%); 206 | /* #8F9D6A */ 207 | } 208 | 209 | .token.atrule { 210 | color: hsl(218, 22%, 55%); 211 | /* #7587A6 */ 212 | } 213 | 214 | .token.regex, .token.important { 215 | color: hsl(42, 75%, 65%); 216 | /* #E9C062 */ 217 | } 218 | 219 | .token.important, .token.bold { 220 | font-weight: bold; 221 | } 222 | 223 | .token.italic { 224 | font-style: italic; 225 | } 226 | 227 | .token.entity { 228 | cursor: help; 229 | } 230 | 231 | pre[data-line] { 232 | padding: 1em 0 1em 3em; 233 | position: relative; 234 | } 235 | 236 | /* Markup */ 237 | 238 | .language-markup .token.tag, .language-markup .token.attr-name, .language-markup .token.punctuation { 239 | color: hsl(33, 33%, 52%); 240 | /* #AC885B */ 241 | } 242 | 243 | /* Make the tokens sit above the line highlight so the colours don't look faded. */ 244 | 245 | .token { 246 | position: relative; 247 | z-index: 1; 248 | } 249 | 250 | pre[class*="language-"].line-numbers { 251 | position: relative; 252 | padding-left: 3.8em; 253 | counter-reset: linenumber; 254 | } 255 | 256 | pre[class*="language-"].line-numbers>code { 257 | position: relative; 258 | white-space: inherit; 259 | } 260 | 261 | .line-numbers .line-numbers-rows { 262 | position: absolute; 263 | pointer-events: none; 264 | top: 0; 265 | font-size: 100%; 266 | left: -3.8em; 267 | width: 3em; 268 | /* works for line-numbers below 1000 lines */ 269 | letter-spacing: -1px; 270 | /* border-right: 1px solid #2A303E; */ 271 | -webkit-user-select: none; 272 | -moz-user-select: none; 273 | -ms-user-select: none; 274 | user-select: none; 275 | } 276 | 277 | .line-numbers-rows>span { 278 | pointer-events: none; 279 | display: block; 280 | counter-increment: linenumber; 281 | } 282 | 283 | .line-numbers-rows>span:before { 284 | content: counter(linenumber); 285 | color: #797C83; 286 | display: block; 287 | padding-right: 0.8em; 288 | text-align: right; 289 | } 290 | -------------------------------------------------------------------------------- /extension/libs/navigation.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright 2020 Jack Baker 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Navigation logic 18 | const changeTab = function(id) { 19 | switch (id) { 20 | case "tabSearchButton": 21 | document.getElementById('tabSearch').style.display = 'block'; 22 | document.getElementById('tabStrings').style.display = 'none'; 23 | document.getElementById('tabPatch').style.display = 'none'; 24 | document.getElementById('tabSpeedHack').style.display = 'none'; 25 | document.getElementById('tabBookmarks').style.display = 'none'; 26 | document.getElementById('tabMemView').style.display = 'none'; 27 | 28 | document.getElementById('liTabSearch').className = 'tabs-item is-active'; 29 | document.getElementById('liTabStrings').className = 'tabs-item'; 30 | document.getElementById('liTabPatch').className = 'tabs-item'; 31 | document.getElementById('liTabSpeedHack').className = 'tabs-item'; 32 | document.getElementById('liTabBookmarks').className = 'tabs-item'; 33 | document.getElementById('liTabMemView').className = 'tabs-item'; 34 | 35 | break; 36 | case "tabStringsButton": 37 | document.getElementById('tabSearch').style.display = 'none'; 38 | document.getElementById('tabStrings').style.display = 'block'; 39 | document.getElementById('tabPatch').style.display = 'none'; 40 | document.getElementById('tabSpeedHack').style.display = 'none'; 41 | document.getElementById('tabBookmarks').style.display = 'none'; 42 | document.getElementById('tabMemView').style.display = 'none'; 43 | 44 | document.getElementById('liTabSearch').className = 'tabs-item'; 45 | document.getElementById('liTabStrings').className = 'tabs-item is-active'; 46 | document.getElementById('liTabPatch').className = 'tabs-item'; 47 | document.getElementById('liTabSpeedHack').className = 'tabs-item'; 48 | document.getElementById('liTabBookmarks').className = 'tabs-item'; 49 | document.getElementById('liTabMemView').className = 'tabs-item'; 50 | 51 | break; 52 | case "tabPatchButton": 53 | document.getElementById('tabSearch').style.display = 'none'; 54 | document.getElementById('tabStrings').style.display = 'none'; 55 | document.getElementById('tabPatch').style.display = 'block'; 56 | document.getElementById('tabSpeedHack').style.display = 'none'; 57 | document.getElementById('tabBookmarks').style.display = 'none'; 58 | document.getElementById('tabMemView').style.display = 'none'; 59 | 60 | document.getElementById('liTabSearch').className = 'tabs-item'; 61 | document.getElementById('liTabStrings').className = 'tabs-item'; 62 | document.getElementById('liTabPatch').className = 'tabs-item is-active'; 63 | document.getElementById('liTabSpeedHack').className = 'tabs-item'; 64 | document.getElementById('liTabBookmarks').className = 'tabs-item'; 65 | document.getElementById('liTabMemView').className = 'tabs-item'; 66 | 67 | break; 68 | case "tabSpeedHackButton": 69 | document.getElementById('tabSearch').style.display = 'none'; 70 | document.getElementById('tabStrings').style.display = 'none'; 71 | document.getElementById('tabPatch').style.display = 'none'; 72 | document.getElementById('tabSpeedHack').style.display = 'block'; 73 | document.getElementById('tabBookmarks').style.display = 'none'; 74 | document.getElementById('tabMemView').style.display = 'none'; 75 | 76 | document.getElementById('liTabSearch').className = 'tabs-item'; 77 | document.getElementById('liTabStrings').className = 'tabs-item'; 78 | document.getElementById('liTabPatch').className = 'tabs-item'; 79 | document.getElementById('liTabSpeedHack').className = 'tabs-item is-active'; 80 | document.getElementById('liTabBookmarks').className = 'tabs-item'; 81 | document.getElementById('liTabMemView').className = 'tabs-item'; 82 | 83 | break; 84 | case "tabBookmarksButton": 85 | document.getElementById('tabSearch').style.display = 'none'; 86 | document.getElementById('tabStrings').style.display = 'none'; 87 | document.getElementById('tabPatch').style.display = 'none'; 88 | document.getElementById('tabSpeedHack').style.display = 'none'; 89 | document.getElementById('tabBookmarks').style.display = 'block'; 90 | document.getElementById('tabMemView').style.display = 'none'; 91 | 92 | document.getElementById('liTabSearch').className = 'tabs-item'; 93 | document.getElementById('liTabStrings').className = 'tabs-item'; 94 | document.getElementById('liTabPatch').className = 'tabs-item'; 95 | document.getElementById('liTabSpeedHack').className = 'tabs-item'; 96 | document.getElementById('liTabBookmarks').className = 'tabs-item is-active'; 97 | document.getElementById('liTabMemView').className = 'tabs-item'; 98 | 99 | break; 100 | case "tabMemViewButton": 101 | document.getElementById('tabSearch').style.display = 'none'; 102 | document.getElementById('tabStrings').style.display = 'none'; 103 | document.getElementById('tabPatch').style.display = 'none'; 104 | document.getElementById('tabSpeedHack').style.display = 'none'; 105 | document.getElementById('tabBookmarks').style.display = 'none'; 106 | document.getElementById('tabMemView').style.display = 'block'; 107 | 108 | document.getElementById('liTabSearch').className = 'tabs-item'; 109 | document.getElementById('liTabStrings').className = 'tabs-item'; 110 | document.getElementById('liTabPatch').className = 'tabs-item'; 111 | document.getElementById('liTabSpeedHack').className = 'tabs-item'; 112 | document.getElementById('liTabBookmarks').className = 'tabs-item'; 113 | document.getElementById('liTabMemView').className = 'tabs-item is-active'; 114 | 115 | break; 116 | default: 117 | throw new Error("Bad tab ID " + id); 118 | } 119 | }; 120 | 121 | const buttons = document.getElementsByName('tabButton'); 122 | 123 | for (let i = 0; i < buttons.length; i++) { 124 | buttons[i].onclick = function(e) { 125 | e.preventDefault(); 126 | 127 | const id = e.target.id; 128 | 129 | changeTab(id); 130 | }; 131 | } 132 | -------------------------------------------------------------------------------- /extension/thirdparty/prism/prism.js: -------------------------------------------------------------------------------- 1 | /* PrismJS 1.17.1 2 | https://prismjs.com/download.html#themes=prism-tomorrow&languages=clike+wasm */ 3 | var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(g){var c=/\blang(?:uage)?-([\w-]+)\b/i,a=0,C={manual:g.Prism&&g.Prism.manual,disableWorkerMessageHandler:g.Prism&&g.Prism.disableWorkerMessageHandler,util:{encode:function(e){return e instanceof M?new M(e.type,C.util.encode(e.content),e.alias):Array.isArray(e)?e.map(C.util.encode):e.replace(/&/g,"&").replace(/e.length)return;if(!(k instanceof M)){if(f&&y!=a.length-1){if(c.lastIndex=v,!(x=c.exec(e)))break;for(var b=x.index+(h?x[1].length:0),w=x.index+x[0].length,A=y,P=v,O=a.length;A"+n.content+""},!g.document)return g.addEventListener&&(C.disableWorkerMessageHandler||g.addEventListener("message",function(e){var a=JSON.parse(e.data),n=a.language,t=a.code,r=a.immediateClose;g.postMessage(C.highlight(t,C.languages[n],n)),r&&g.close()},!1)),C;var e=document.currentScript||[].slice.call(document.getElementsByTagName("script")).pop();return e&&(C.filename=e.src,C.manual||e.hasAttribute("data-manual")||("loading"!==document.readyState?window.requestAnimationFrame?window.requestAnimationFrame(C.highlightAll):window.setTimeout(C.highlightAll,16):document.addEventListener("DOMContentLoaded",C.highlightAll))),C}(_self);"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism); 4 | Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/((?:\b(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,boolean:/\b(?:true|false)\b/,function:/\w+(?=\()/,number:/\b0x[\da-f]+\b|(?:\b\d+\.?\d*|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/--?|\+\+?|!=?=?|<=?|>=?|==?=?|&&?|\|\|?|\?|\*|\/|~|\^|%/,punctuation:/[{}[\];(),.:]/}; 5 | Prism.languages.wasm={comment:[/\(;[\s\S]*?;\)/,{pattern:/;;.*/,greedy:!0}],string:{pattern:/"(?:\\[\s\S]|[^"\\])*"/,greedy:!0},keyword:[{pattern:/\b(?:align|offset)=/,inside:{operator:/=/}},{pattern:/\b(?:(?:f32|f64|i32|i64)(?:\.(?:abs|add|and|ceil|clz|const|convert_[su]\/i(?:32|64)|copysign|ctz|demote\/f64|div(?:_[su])?|eqz?|extend_[su]\/i32|floor|ge(?:_[su])?|gt(?:_[su])?|le(?:_[su])?|load(?:(?:8|16|32)_[su])?|lt(?:_[su])?|max|min|mul|nearest|neg?|or|popcnt|promote\/f32|reinterpret\/[fi](?:32|64)|rem_[su]|rot[lr]|shl|shr_[su]|store(?:8|16|32)?|sqrt|sub|trunc(?:_[su]\/f(?:32|64))?|wrap\/i64|xor))?|memory\.(?:grow|size))\b/,inside:{punctuation:/\./}},/\b(?:anyfunc|block|br(?:_if|_table)?|call(?:_indirect)?|data|drop|elem|else|end|export|func|get_(?:global|local)|global|if|import|local|loop|memory|module|mut|nop|offset|param|result|return|select|set_(?:global|local)|start|table|tee_local|then|type|unreachable)\b/],variable:/\$[\w!#$%&'*+\-./:<=>?@\\^_`|~]+/i,number:/[+-]?\b(?:\d(?:_?\d)*(?:\.\d(?:_?\d)*)?(?:[eE][+-]?\d(?:_?\d)*)?|0x[\da-fA-F](?:_?[\da-fA-F])*(?:\.[\da-fA-F](?:_?[\da-fA-D])*)?(?:[pP][+-]?\d(?:_?\d)*)?)\b|\binf\b|\bnan(?::0x[\da-fA-F](?:_?[\da-fA-D])*)?\b/,punctuation:/[()]/}; 6 | -------------------------------------------------------------------------------- /extension/css/normalize.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ 2 | 3 | /* Document 4 | ========================================================================== */ 5 | 6 | /** 7 | * 1. Correct the line height in all browsers. 8 | * 2. Prevent adjustments of font size after orientation changes in iOS. 9 | */ 10 | 11 | html { 12 | line-height: 1.15; 13 | /* 1 */ 14 | -webkit-text-size-adjust: 100%; 15 | /* 2 */ 16 | } 17 | 18 | /* Sections 19 | ========================================================================== */ 20 | 21 | /** 22 | * Remove the margin in all browsers. 23 | */ 24 | 25 | body { 26 | margin: 0; 27 | } 28 | 29 | /** 30 | * Render the `main` element consistently in IE. 31 | */ 32 | 33 | main { 34 | display: block; 35 | } 36 | 37 | /** 38 | * Correct the font size and margin on `h1` elements within `section` and 39 | * `article` contexts in Chrome, Firefox, and Safari. 40 | */ 41 | 42 | h1 { 43 | font-size: 2em; 44 | margin: 0.67em 0; 45 | } 46 | 47 | /* Grouping content 48 | ========================================================================== */ 49 | 50 | /** 51 | * 1. Add the correct box sizing in Firefox. 52 | * 2. Show the overflow in Edge and IE. 53 | */ 54 | 55 | hr { 56 | box-sizing: content-box; 57 | /* 1 */ 58 | height: 0; 59 | /* 1 */ 60 | overflow: visible; 61 | /* 2 */ 62 | } 63 | 64 | /** 65 | * 1. Correct the inheritance and scaling of font size in all browsers. 66 | * 2. Correct the odd `em` font sizing in all browsers. 67 | */ 68 | 69 | pre { 70 | font-family: monospace, monospace; 71 | /* 1 */ 72 | font-size: 1em; 73 | /* 2 */ 74 | } 75 | 76 | /* Text-level semantics 77 | ========================================================================== */ 78 | 79 | /** 80 | * Remove the gray background on active links in IE 10. 81 | */ 82 | 83 | a { 84 | background-color: transparent; 85 | } 86 | 87 | /** 88 | * 1. Remove the bottom border in Chrome 57- 89 | * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. 90 | */ 91 | 92 | abbr[title] { 93 | border-bottom: none; 94 | /* 1 */ 95 | text-decoration: underline; 96 | /* 2 */ 97 | text-decoration: underline dotted; 98 | /* 2 */ 99 | } 100 | 101 | /** 102 | * Add the correct font weight in Chrome, Edge, and Safari. 103 | */ 104 | 105 | b, strong { 106 | font-weight: bolder; 107 | } 108 | 109 | /** 110 | * 1. Correct the inheritance and scaling of font size in all browsers. 111 | * 2. Correct the odd `em` font sizing in all browsers. 112 | */ 113 | 114 | code, kbd, samp { 115 | font-family: monospace, monospace; 116 | /* 1 */ 117 | font-size: 1em; 118 | /* 2 */ 119 | } 120 | 121 | /** 122 | * Add the correct font size in all browsers. 123 | */ 124 | 125 | small { 126 | font-size: 80%; 127 | } 128 | 129 | /** 130 | * Prevent `sub` and `sup` elements from affecting the line height in 131 | * all browsers. 132 | */ 133 | 134 | sub, sup { 135 | font-size: 75%; 136 | line-height: 0; 137 | position: relative; 138 | vertical-align: baseline; 139 | } 140 | 141 | sub { 142 | bottom: -0.25em; 143 | } 144 | 145 | sup { 146 | top: -0.5em; 147 | } 148 | 149 | /* Embedded content 150 | ========================================================================== */ 151 | 152 | /** 153 | * Remove the border on images inside links in IE 10. 154 | */ 155 | 156 | img { 157 | border-style: none; 158 | } 159 | 160 | /* Forms 161 | ========================================================================== */ 162 | 163 | /** 164 | * 1. Change the font styles in all browsers. 165 | * 2. Remove the margin in Firefox and Safari. 166 | */ 167 | 168 | button, input, optgroup, select, textarea { 169 | font-family: inherit; 170 | /* 1 */ 171 | font-size: 100%; 172 | /* 1 */ 173 | line-height: 1.15; 174 | /* 1 */ 175 | margin: 0; 176 | /* 2 */ 177 | } 178 | 179 | /** 180 | * Show the overflow in IE. 181 | * 1. Show the overflow in Edge. 182 | */ 183 | 184 | button, input { 185 | /* 1 */ 186 | overflow: visible; 187 | } 188 | 189 | /** 190 | * Remove the inheritance of text transform in Edge, Firefox, and IE. 191 | * 1. Remove the inheritance of text transform in Firefox. 192 | */ 193 | 194 | button, select { 195 | /* 1 */ 196 | text-transform: none; 197 | } 198 | 199 | /** 200 | * Correct the inability to style clickable types in iOS and Safari. 201 | */ 202 | 203 | button, [type='button'], [type='reset'], [type='submit'] { 204 | -webkit-appearance: button; 205 | } 206 | 207 | /** 208 | * Remove the inner border and padding in Firefox. 209 | */ 210 | 211 | button::-moz-focus-inner, [type='button']::-moz-focus-inner, [type='reset']::-moz-focus-inner, [type='submit']::-moz-focus-inner { 212 | border-style: none; 213 | padding: 0; 214 | } 215 | 216 | /** 217 | * Restore the focus styles unset by the previous rule. 218 | */ 219 | 220 | button:-moz-focusring, [type='button']:-moz-focusring, [type='reset']:-moz-focusring, [type='submit']:-moz-focusring { 221 | outline: 1px dotted ButtonText; 222 | } 223 | 224 | /** 225 | * Correct the padding in Firefox. 226 | */ 227 | 228 | fieldset { 229 | padding: 0.35em 0.75em 0.625em; 230 | } 231 | 232 | /** 233 | * 1. Correct the text wrapping in Edge and IE. 234 | * 2. Correct the color inheritance from `fieldset` elements in IE. 235 | * 3. Remove the padding so developers are not caught out when they zero out 236 | * `fieldset` elements in all browsers. 237 | */ 238 | 239 | legend { 240 | box-sizing: border-box; 241 | /* 1 */ 242 | color: inherit; 243 | /* 2 */ 244 | display: table; 245 | /* 1 */ 246 | max-width: 100%; 247 | /* 1 */ 248 | padding: 0; 249 | /* 3 */ 250 | white-space: normal; 251 | /* 1 */ 252 | } 253 | 254 | /** 255 | * Add the correct vertical alignment in Chrome, Firefox, and Opera. 256 | */ 257 | 258 | progress { 259 | vertical-align: baseline; 260 | } 261 | 262 | /** 263 | * Remove the default vertical scrollbar in IE 10+. 264 | */ 265 | 266 | textarea { 267 | overflow: auto; 268 | } 269 | 270 | /** 271 | * 1. Add the correct box sizing in IE 10. 272 | * 2. Remove the padding in IE 10. 273 | */ 274 | 275 | [type='checkbox'], [type='radio'] { 276 | box-sizing: border-box; 277 | /* 1 */ 278 | padding: 0; 279 | /* 2 */ 280 | } 281 | 282 | /** 283 | * Correct the cursor style of increment and decrement buttons in Chrome. 284 | */ 285 | 286 | [type='number']::-webkit-inner-spin-button, [type='number']::-webkit-outer-spin-button { 287 | height: auto; 288 | } 289 | 290 | /** 291 | * 1. Correct the odd appearance in Chrome and Safari. 292 | * 2. Correct the outline style in Safari. 293 | */ 294 | 295 | [type='search'] { 296 | -webkit-appearance: textfield; 297 | /* 1 */ 298 | outline-offset: -2px; 299 | /* 2 */ 300 | } 301 | 302 | /** 303 | * Remove the inner padding in Chrome and Safari on macOS. 304 | */ 305 | 306 | [type='search']::-webkit-search-decoration { 307 | -webkit-appearance: none; 308 | } 309 | 310 | /** 311 | * 1. Correct the inability to style clickable types in iOS and Safari. 312 | * 2. Change font properties to `inherit` in Safari. 313 | */ 314 | 315 | ::-webkit-file-upload-button { 316 | -webkit-appearance: button; 317 | /* 1 */ 318 | font: inherit; 319 | /* 2 */ 320 | } 321 | 322 | /* Interactive 323 | ========================================================================== */ 324 | 325 | /* 326 | * Add the correct display in Edge, IE 10+, and Firefox. 327 | */ 328 | 329 | details { 330 | display: block; 331 | } 332 | 333 | /* 334 | * Add the correct display in all browsers. 335 | */ 336 | 337 | summary { 338 | display: list-item; 339 | } 340 | 341 | /* Misc 342 | ========================================================================== */ 343 | 344 | /** 345 | * Add the correct display in IE 10+. 346 | */ 347 | 348 | template { 349 | display: none; 350 | } 351 | 352 | /** 353 | * Add the correct display in IE 10. 354 | */ 355 | 356 | [hidden] { 357 | display: none; 358 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /extension/extension.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright 2020 Jack Baker 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | const MAX_STACKTRACES = 10; 18 | 19 | class PopupExtension { 20 | constructor() { 21 | if (typeof chrome.extension.connect !== "undefined") { 22 | this._bgChannel = chrome.extension.connect({ name: "Cetus Background Page"}); 23 | 24 | this._bgChannel.onMessage.addListener(bgMessageListener); 25 | this.sendBGMessage("popupConnected"); 26 | } 27 | else { 28 | this._bgChannel = browser.runtime.connect({ name: "Cetus Background Page"}); 29 | 30 | this._bgChannel.onMessage.addListener(bgMessageListener); 31 | this.sendBGMessage("popupConnected"); 32 | } 33 | 34 | this._patches = []; 35 | this._loadPatchesFromStorage(); 36 | 37 | this._stackTraces = []; 38 | 39 | this.symbols = {}; 40 | 41 | this.unlocked = false; 42 | 43 | let _this = this; 44 | loadOptions(function(savedOptions) { 45 | _this.options = savedOptions; 46 | }); 47 | } 48 | 49 | sendBGMessage(type, msgBody) { 50 | if (this._bgChannel !== null) { 51 | const msg = { 52 | type: type 53 | }; 54 | 55 | if (typeof msgBody !== "undefined") { 56 | msg.body = msgBody; 57 | } 58 | 59 | // TODO Actually handle port closed error 60 | try { 61 | this._bgChannel.postMessage(msg); 62 | } 63 | catch (err) { 64 | return; 65 | } 66 | } 67 | } 68 | 69 | unlock() { 70 | const overlay = document.getElementById("lockOverlay"); 71 | overlay.classList.remove("overlay"); 72 | const button = document.getElementById("overlayLoadPatchModalButton"); 73 | button.style.display = "none"; 74 | this.unlocked = true; 75 | } 76 | 77 | reset() { 78 | updateBookmarkTable({}, this.options.enableWatchpoints); 79 | 80 | document.getElementById("restartBtn").click(); 81 | 82 | document.getElementById("functionInput").value = ""; 83 | document.getElementById("codeDisassembly").innerText = ""; 84 | 85 | const textArea = document.querySelector("textarea.prism-live"); 86 | textArea.value = ""; 87 | 88 | // Prism Live only updates the text area on an input event 89 | // So we issue a fake input event to make it update 90 | textArea.dispatchEvent(new Event("input")); 91 | 92 | const overlay = document.getElementById("lockOverlay"); 93 | overlay.classList.add("overlay"); 94 | const button = document.getElementById("overlayLoadPatchModalButton"); 95 | button.style.display = "block"; 96 | 97 | closeLoadPatchModal(); 98 | closeSavePatchModal(); 99 | closeStackTraceModal(); 100 | 101 | this.unlocked = false; 102 | 103 | let _this = this; 104 | loadOptions(function(savedOptions) { 105 | _this.options = savedOptions; 106 | }); 107 | } 108 | 109 | addBookmark(memAddr, memType) { 110 | const msgBody = { 111 | memAddr: memAddr, 112 | memType: memType 113 | }; 114 | 115 | this.sendBGMessage("addBookmark", msgBody); 116 | } 117 | 118 | removeBookmark(memAddr) { 119 | this.sendBGMessage("removeBookmark", { 120 | memAddr: memAddr, 121 | }); 122 | } 123 | 124 | _loadPatchesFromStorage() { 125 | chrome.storage.local.get("savedPatches", function(result) { 126 | if (result !== null) { 127 | if (typeof result.savedPatches !== "undefined") { 128 | extension._patches = result.savedPatches; 129 | } 130 | } 131 | }); 132 | } 133 | 134 | _updatePatches() { 135 | chrome.storage.local.set({ savedPatches: this._patches }); 136 | } 137 | 138 | // TODO Remove backwards compatibility code once we've stopped supporting the old patch spec 139 | savePatch(options) { 140 | const patchName = options.name; 141 | 142 | let functionPatches; 143 | 144 | if (typeof options.version === "undefined" && typeof options.index !== "undefined" && typeof options.bytes !== "undefined") { 145 | const funcIndex = options.index; 146 | const funcBytes = options.bytes; 147 | 148 | functionPatches = [ 149 | { 150 | index: funcIndex, 151 | bytes: funcBytes, 152 | } 153 | ]; 154 | } 155 | else { 156 | functionPatches = options.functionPatches; 157 | } 158 | 159 | const binaryUrl = options.url; 160 | 161 | const newPatchBody = { 162 | name: patchName, 163 | url: binaryUrl, 164 | version: options.version, 165 | enabled: true, 166 | functionPatches: functionPatches, 167 | callbacks: options.callbacks, 168 | }; 169 | 170 | this._patches.push(newPatchBody); 171 | this._updatePatches(); 172 | } 173 | 174 | getPatches() { 175 | return this._patches; 176 | } 177 | 178 | getPatchesByUrl(url) { 179 | const allPatches = this.getPatches(); 180 | 181 | const results = []; 182 | 183 | for (let i = 0; i < allPatches.length; i++) { 184 | const thisPatch = allPatches[i]; 185 | 186 | if (thisPatch.url === url) { 187 | results.push(thisPatch); 188 | } 189 | } 190 | 191 | return results; 192 | } 193 | 194 | getPatchByName(patchName) { 195 | if (typeof patchName !== 'string') { 196 | return; 197 | } 198 | 199 | const allPatches = this.getPatches(); 200 | 201 | for (let i = 0; i < allPatches.length; i++) { 202 | const thisPatch = allPatches[i]; 203 | 204 | if (thisPatch.name === patchName) { 205 | return thisPatch; 206 | } 207 | } 208 | } 209 | 210 | enablePatchByName(patchName) { 211 | if (typeof patchName !== 'string') { 212 | return; 213 | } 214 | 215 | const allPatches = this.getPatches(); 216 | 217 | for (let i = 0; i < allPatches.length; i++) { 218 | const thisPatch = allPatches[i]; 219 | 220 | if (thisPatch.name === patchName) { 221 | this._patches[i].enabled = true; 222 | 223 | this._updatePatches(); 224 | 225 | break; 226 | } 227 | } 228 | } 229 | 230 | disablePatchByName(patchName) { 231 | if (typeof patchName !== 'string') { 232 | return; 233 | } 234 | 235 | const allPatches = this.getPatches(); 236 | 237 | for (let i = 0; i < allPatches.length; i++) { 238 | const thisPatch = allPatches[i]; 239 | 240 | if (thisPatch.name === patchName) { 241 | this._patches[i].enabled = false; 242 | 243 | this._updatePatches(); 244 | 245 | break; 246 | } 247 | } 248 | } 249 | 250 | exportPatchByName(patchName) { 251 | if (typeof patchName !== 'string') { 252 | return false; 253 | } 254 | 255 | const allPatches = this.getPatches(); 256 | 257 | for (let i = 0; i < allPatches.length; i++) { 258 | const thisPatch = allPatches[i]; 259 | 260 | if (thisPatch.name === patchName) { 261 | return JSON.stringify(thisPatch); 262 | } 263 | } 264 | } 265 | 266 | downloadPatchByName(patchName) { 267 | const patchFilename = patchName + ".json"; 268 | const patchString = this.exportPatchByName(patchName); 269 | 270 | downloadText(patchFilename, patchString); 271 | } 272 | 273 | deletePatchByName(patchName) { 274 | if (typeof patchName !== 'string') { 275 | return false; 276 | } 277 | 278 | const allPatches = this.getPatches(); 279 | 280 | for (let i = 0; i < allPatches.length; i++) { 281 | const thisPatch = allPatches[i]; 282 | 283 | if (thisPatch.name === patchName) { 284 | this._patches.splice(i, 1); 285 | 286 | this._updatePatches(); 287 | 288 | break; 289 | } 290 | } 291 | } 292 | 293 | clearPatches() { 294 | this._patches = []; 295 | 296 | this._updatePatches(); 297 | } 298 | 299 | getStackTraces() { 300 | return this._stackTraces; 301 | } 302 | 303 | addStackTrace(newStackTrace) { 304 | this._stackTraces.push(newStackTrace); 305 | 306 | while (this._stackTraces.length > MAX_STACKTRACES) { 307 | this._stackTraces.shift(); 308 | } 309 | } 310 | } 311 | 312 | const bgMessageListener = function(msgRaw) { 313 | const msg = bigintJsonParse(msgRaw); 314 | 315 | const type = msg.type; 316 | const msgBody = msg.body; 317 | const instanceId = msg.id; 318 | 319 | switch (type) { 320 | case "init": 321 | if (!extension.unlocked) { 322 | extension.unlock(); 323 | 324 | extension.url = msgBody.url; 325 | extension.symbols = msgBody.symbols; 326 | 327 | updateInstances({ 328 | url: msgBody.url, 329 | id: instanceId, 330 | }); 331 | } 332 | else { 333 | addNewInstanceSelector(msgBody.url, instanceId); 334 | } 335 | 336 | break; 337 | case "popupRestore": 338 | if (!extension.unlocked) { 339 | extension.unlock(); 340 | } 341 | 342 | const instanceData = msgBody.instanceData; 343 | 344 | extension.url = instanceData.url; 345 | extension.symbols = instanceData.symbols; 346 | extension.searchMemType = instanceData.searchForm.valueType; 347 | 348 | updateSearchForm(instanceData.searchForm); 349 | updateStringSearchForm(instanceData.stringForm); 350 | updateSpeedhackForm(instanceData.speedhack); 351 | updateStackTraceTable(instanceData.stackTraces); 352 | updateBookmarkTable(instanceData.bookmarks, extension.options.enableWatchpoints); 353 | 354 | updateInstances(msgBody.instances); 355 | 356 | break; 357 | case "searchResult": 358 | const resultCount = msgBody.count; 359 | const resultObject = msgBody.results; 360 | const resultMemType = msgBody.memType; 361 | 362 | if (typeof resultCount !== "number" || typeof resultObject !== "object") { 363 | return true; 364 | } 365 | 366 | updateSearchResults(resultCount, resultObject, resultMemType); 367 | 368 | break; 369 | case "updateBookmarks": 370 | const bookmarks = msgBody.bookmarks; 371 | 372 | updateBookmarkTable(bookmarks, extension.options.enableWatchpoints); 373 | 374 | break; 375 | case "updateMemView": 376 | const memData = msgBody.data; 377 | const memDataStartAddress = msgBody.startAddress; 378 | 379 | let len = Object.keys(memData).length; 380 | document.getElementById("memViewData").value = ""; 381 | 382 | let counter = 0; 383 | let row = ""; 384 | let txt = ""; 385 | let c = 0; 386 | for (let i=0; i<32; i++) 387 | { 388 | row += toHex(memDataStartAddress+16*i) + " "; 389 | txt = ""; 390 | for (let k=0; k<16; k++) 391 | { 392 | row += toHexByte(memData[c]) + " "; 393 | if (memData[c] > 32 && memData[c] < 127) 394 | txt += String.fromCharCode(memData[c]); 395 | else 396 | txt += "."; 397 | 398 | c++; 399 | } 400 | row += " " + txt + "\n"; 401 | } 402 | document.getElementById("memViewData").value = row; 403 | 404 | break; 405 | case "stringSearchResult": 406 | const strResultCount = msgBody.count; 407 | const strResultObj = msgBody.results; 408 | 409 | updateStringSearchResults(strResultCount, strResultObj); 410 | 411 | break; 412 | case "queryFunctionResult": 413 | if (typeof msgBody.bytes !== "object") { 414 | return true; 415 | } 416 | 417 | const funcIndex = msgBody.funcIndex; 418 | const funcArray = Object.values(msgBody.bytes); 419 | const funcBytes = new Uint8Array(funcArray); 420 | 421 | const givenLineNum = msgBody.lineNum; 422 | 423 | const disassembly = disassembleFunction(funcBytes); 424 | const funcText = disassembly.text; 425 | 426 | const realLineNum = disassembly.lineNums[givenLineNum]; 427 | 428 | updatePatchWindow(funcIndex, funcText, realLineNum); 429 | 430 | changeTab("tabPatchButton"); 431 | 432 | break; 433 | case "watchPointHit": 434 | const stackTrace = msgBody.stackTrace; 435 | 436 | extension.addStackTrace(stackTrace); 437 | 438 | updateStackTraceTable(extension.getStackTraces()); 439 | 440 | break; 441 | case "reset": 442 | extension.reset(); 443 | 444 | break; 445 | } 446 | 447 | return true; 448 | }; 449 | 450 | // TODO Rename this variable (Maybe "popup"?) 451 | extension = new PopupExtension(); 452 | -------------------------------------------------------------------------------- /extension/css/styles.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --brand: #5352ed; 3 | --background: #1f2430; 4 | --lightBackground: #232934; 5 | --text: #ffffff; 6 | --textGray: #d8d8d8; 7 | --grayInactive: #2a303e; 8 | --placeholder: rgba(255, 255, 255, 0.8); 9 | --label: rgba(255, 255, 255, 0.4); 10 | --toggleColorBackground: #fff; 11 | --toggleColorFill: #a4b0be; 12 | --inputBorder: #434b5b; 13 | --caretColor: rgb(0, 0, 0); 14 | } 15 | 16 | * { 17 | font-family: 'Proxima Nova', sans-serif; 18 | box-sizing: border-box; 19 | -webkit-font-smoothing: antialiased; 20 | margin: 0; 21 | outline: none; 22 | padding: 0; 23 | } 24 | 25 | html { 26 | border-radius: 4px; 27 | } 28 | 29 | .modal { 30 | width: 100%; 31 | height: 100%; 32 | left: 0; 33 | top: 0; 34 | z-index: 999; 35 | display: flex; 36 | flex-flow: column nowrap; 37 | justify-content: flex-end; 38 | } 39 | 40 | .modal-hidden { 41 | display: none; 42 | } 43 | 44 | .modal-inner { 45 | position: relative; 46 | z-index: 5; 47 | background: var(--lightBackground); 48 | padding: 16px; 49 | /* 50 | z-index: 5; 51 | background: var(--lightBackground); 52 | padding: 16px; 53 | overflow-y:scroll; 54 | overflow-x:hidden; 55 | */ 56 | } 57 | 58 | .modal-hidden .modal-inner { 59 | transform: translateY(100%); 60 | } 61 | 62 | .modal-space { 63 | flex: 1; 64 | width: 100%; 65 | } 66 | 67 | .modal-overlay { 68 | height: 100%; 69 | width: 100%; 70 | background: #000; 71 | opacity: 0.6; 72 | position: absolute; 73 | top: 0; 74 | left: 0; 75 | } 76 | 77 | .modal-row { 78 | display: flex; 79 | flex-flow: row nowrap; 80 | justify-content: space-between; 81 | align-items: center; 82 | } 83 | 84 | .modal-title { 85 | font-style: normal; 86 | font-weight: 600; 87 | font-size: 18px; 88 | line-height: 24px; 89 | align-items: center; 90 | letter-spacing: 0.01em; 91 | } 92 | 93 | .modal-close { 94 | cursor: pointer; 95 | } 96 | 97 | .modal .input { 98 | margin: 16px 0 24px; 99 | } 100 | 101 | .modal .button { 102 | margin: 0; 103 | } 104 | 105 | .modal-table { 106 | margin-top: 16px; 107 | } 108 | 109 | .modal-table td:nth-child(2) { 110 | width: 100%; 111 | overflow:scroll; 112 | } 113 | 114 | .modal-table .load-button, 115 | .modal-table .remove-button { 116 | width: 32px; 117 | height: 32px; 118 | cursor: pointer; 119 | opacity: 0.7; 120 | transition: opacity 0.2s ease; 121 | will-change: opacity; 122 | display: flex; 123 | flex-flow: column nowrap; 124 | justify-content: center; 125 | align-items: center; 126 | } 127 | 128 | .modal-table .load-button:hover, 129 | .modal-table .remove-button:hover { 130 | opacity: 1; 131 | } 132 | 133 | .toggleColorScheme { 134 | position: absolute; 135 | right: 14px; 136 | top: 11px; 137 | cursor: pointer; 138 | width: 24px; 139 | height: 24px; 140 | border-radius: 16px; 141 | display: flex; 142 | flex-flow: column nowrap; 143 | justify-content: center; 144 | align-items: center; 145 | background: var(--toggleColorBackground); 146 | box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.2); 147 | } 148 | 149 | .toggleColorScheme svg { 150 | width: 80%; 151 | height: 80%; 152 | } 153 | 154 | .toggleColorScheme path { 155 | fill: var(--toggleColorFill); 156 | } 157 | 158 | .container { 159 | width: 100%; 160 | padding: 16px; 161 | background: var(--background); 162 | } 163 | 164 | .select { 165 | position: fixed; 166 | z-index: 5; 167 | top: 0; 168 | left: 0; 169 | width: 100%; 170 | background: var(--lightBackground); 171 | padding: 0 16px; 172 | outline: none; 173 | } 174 | 175 | .select select { 176 | -moz-appearance: none; 177 | -webkit-appearance: none; 178 | appearance: none; 179 | text-decoration: none; 180 | } 181 | 182 | .tabs { 183 | position: fixed; 184 | z-index: 5; 185 | top: 10; 186 | left: 0; 187 | width: 100%; 188 | background: var(--lightBackground); 189 | display: flex; 190 | flex-flow: row nowrap; 191 | padding: 0 16px; 192 | } 193 | 194 | .tabs-content { 195 | padding-top: 60px; 196 | height: 100%; 197 | overflow-y: scroll; 198 | -ms-overflow-style: none; 199 | scrollbar-width: none; 200 | } 201 | 202 | .tabs-content::-webkit-scrollbar { 203 | display: none; 204 | } 205 | 206 | .tabs-item { 207 | padding: 14px 0 10px; 208 | margin-right: 24px; 209 | font-weight: 600; 210 | font-size: 14px; 211 | line-height: 18px; 212 | text-align: center; 213 | letter-spacing: 0.02em; 214 | user-select: none; 215 | border-bottom: 2px transparent solid; 216 | transition: border-bottom 0.2s ease; 217 | will-change: border-bottom; 218 | } 219 | 220 | .tabs-item a { 221 | text-decoration: none; 222 | color: var(--text); 223 | user-select: none; 224 | transition: color 0.2s ease; 225 | will-change: color; 226 | } 227 | 228 | .tabs-item.is-active { 229 | border-bottom: 2px var(--brand) solid; 230 | } 231 | 232 | .tabs-item.is-active a { 233 | color: var(--brand); 234 | } 235 | 236 | .form-field { 237 | margin-bottom: 16px; 238 | display: block; 239 | font-weight: bold; 240 | line-height: 20px; 241 | font-size: 13px; 242 | color: var(--label); 243 | } 244 | 245 | .input { 246 | margin-top: 4px; 247 | width: 100%; 248 | flex: 1; 249 | outline: none; 250 | background: none; 251 | display: block; 252 | padding: 10px 8px; 253 | font-family: 'Proxima Nova', sans-serif; 254 | font-style: normal; 255 | font-weight: normal; 256 | line-height: 22px; 257 | font-size: 16px; 258 | color: var(--text); 259 | border: 1px solid var(--inputBorder); 260 | transition: border 0.2s ease; 261 | will-change: border; 262 | border-radius: 4px; 263 | } 264 | 265 | .input:focus { 266 | border: 1px solid var(--brand); 267 | } 268 | 269 | ::placeholder { 270 | /* Chrome, Firefox, Opera, Safari 10.1+ */ 271 | color: var(--placeholder); 272 | opacity: 1; 273 | /* Firefox */ 274 | } 275 | 276 | :-ms-input-placeholder { 277 | /* Internet Explorer 10-11 */ 278 | color: var(--placeholder); 279 | } 280 | 281 | ::-ms-input-placeholder { 282 | /* Microsoft Edge */ 283 | color: var(--placeholder); 284 | } 285 | 286 | .function-form { 287 | display: flex; 288 | flex-flow: row nowrap; 289 | } 290 | 291 | .form-field--group { 292 | display: flex; 293 | flex-flow: row wrap; 294 | justify-content: space-between; 295 | align-items: center; 296 | } 297 | 298 | .form-field--group .input { 299 | margin-right: 8px; 300 | } 301 | 302 | .form-field--group .input:last-child, 303 | .form-field--group .input:nth-child(4n) { 304 | margin-right: 0; 305 | } 306 | 307 | .radio { 308 | margin-top: 4px; 309 | margin-bottom: 4px; 310 | flex: 0 0 15%; 311 | line-height: 23px; 312 | font-size: 16px; 313 | font-weight: normal; 314 | color: var(--textGray); 315 | height: 40px; 316 | position: relative; 317 | } 318 | 319 | .radio-input { 320 | opacity: 0; 321 | width: 100%; 322 | height: 100%; 323 | position: absolute; 324 | top: 0; 325 | left: 0; 326 | z-index: 1; 327 | cursor: pointer; 328 | } 329 | 330 | .radio-body { 331 | padding: 11px 8px; 332 | position: absolute; 333 | display: flex; 334 | flex-flow: row nowrap; 335 | align-items: center; 336 | left: 0; 337 | top: 0; 338 | width: 100%; 339 | height: 100%; 340 | background: var(--grayInactive); 341 | border-radius: 4px; 342 | transition: background 0.2s ease; 343 | will-change: background; 344 | } 345 | 346 | .radio-body span { 347 | width: 8px; 348 | height: 8px; 349 | background: #7e8797; 350 | transition: background 0.2s ease; 351 | will-change: background; 352 | border-radius: 4px; 353 | margin-right: 4px; 354 | } 355 | 356 | .radio-input:checked + .radio-body { 357 | background: var(--brand); 358 | color: #fff; 359 | } 360 | 361 | .radio-input:checked + .radio-body span { 362 | background: #ffffff; 363 | } 364 | 365 | .button { 366 | text-align: center; 367 | margin-top: 4px; 368 | width: 100%; 369 | flex: 1; 370 | outline: none; 371 | border-radius: 4px; 372 | padding: 14px 16px; 373 | background: var(--brand); 374 | border: 1px var(--brand) solid; 375 | color: #ffffff; 376 | font-weight: bold; 377 | line-height: 23px; 378 | font-size: 16px; 379 | cursor: pointer; 380 | } 381 | 382 | .button--text { 383 | background: none; 384 | border-radius: 0; 385 | border: none; 386 | opacity: 0.6; 387 | } 388 | 389 | .restart-container { 390 | display: flex; 391 | flex-flow: row nowrap; 392 | justify-content: space-between; 393 | align-items: center; 394 | } 395 | 396 | .results { 397 | padding: 0 8px; 398 | } 399 | 400 | .results-count { 401 | font-weight: bold; 402 | line-height: 26px; 403 | font-size: 18px; 404 | color: var(--textGray); 405 | } 406 | 407 | .results-clear { 408 | cursor: pointer; 409 | font-style: normal; 410 | font-weight: normal; 411 | line-height: 23px; 412 | font-size: 16px; 413 | color: #ff6b81; 414 | background: none; 415 | border: none; 416 | outline: none; 417 | } 418 | 419 | .results-clear[disabled] { 420 | display: none; 421 | } 422 | 423 | table { 424 | width: 100%; 425 | border-collapse: collapse; 426 | } 427 | 428 | table tbody tr:nth-child(even) { 429 | padding-bottom: 4px; 430 | background: var(--grayInactive); 431 | } 432 | 433 | table tbody tr:nth-child(odd) { 434 | background: var(--lightBackground); 435 | } 436 | 437 | thead td { 438 | font-style: normal; 439 | font-weight: bold; 440 | line-height: 20px; 441 | font-size: 13px; 442 | color: var(--label); 443 | padding: 0 4px 4px; 444 | } 445 | 446 | table tbody td { 447 | font-weight: normal; 448 | line-height: 22px; 449 | font-size: 14px; 450 | color: var(--textGray); 451 | padding: 12px 8px 10px; 452 | } 453 | 454 | .table-input { 455 | width: 100%; 456 | outline: none; 457 | background: none; 458 | display: block; 459 | font-family: 'Proxima Nova', sans-serif; 460 | font-style: normal; 461 | font-weight: normal; 462 | line-height: 22px; 463 | border: none; 464 | font-size: 14px; 465 | color: var(--text); 466 | } 467 | 468 | .button-add { 469 | font-style: normal; 470 | font-weight: normal; 471 | line-height: 23px; 472 | font-size: 16px; 473 | color: var(--brand); 474 | display: flex; 475 | flex-flow: row nowrap; 476 | justify-content: flex-end; 477 | align-items: center; 478 | background: none; 479 | border: none; 480 | outline: none; 481 | width: 100%; 482 | cursor: pointer; 483 | } 484 | 485 | .button-add--icon { 486 | margin-top: 8px; 487 | margin-right: 7px; 488 | } 489 | 490 | .speedometer { 491 | margin: 0px 0 16px; 492 | position: relative; 493 | display: flex; 494 | flex-flow: column nowrap; 495 | justify-content: center; 496 | align-items: center; 497 | } 498 | 499 | .speedometer-current { 500 | position: absolute; 501 | top: calc(35% + 48px); 502 | left: calc(52%); 503 | transform: translate(-50%, -50%); 504 | max-width: 122px; 505 | text-align: center; 506 | margin-left: -8px; 507 | width: 100%; 508 | font-weight: bold; 509 | line-height: 104px; 510 | font-size: 72px; 511 | color: var(--textGray); 512 | } 513 | 514 | .speedometer svg .paint { 515 | fill: var(--grayInactive); 516 | transition: fill 0.2s ease; 517 | will-change: fill; 518 | } 519 | 520 | .speedometer svg .paint-bold { 521 | transition: fill 0.2s ease; 522 | will-change: fill; 523 | } 524 | 525 | .speedometer-arrow { 526 | transform: translate(0px, -157px) rotate(-67.8deg); 527 | transform-origin: 50% 100px; 528 | transition: transform 0.2s ease; 529 | will-change: transform; 530 | } 531 | 532 | @media (max-width: 440px) { 533 | .button-add span { 534 | display: none; 535 | } 536 | } 537 | 538 | .slider { 539 | margin-bottom: 32px; 540 | position: relative; 541 | } 542 | 543 | .slider-input { 544 | position: relative; 545 | } 546 | 547 | .slider-input--line { 548 | position: absolute; 549 | top: -3px; 550 | left: 0; 551 | width: 2px; 552 | height: 7px; 553 | border-radius: 100px; 554 | background: var(--brand); 555 | } 556 | 557 | .slider-input--line:nth-child(1) { 558 | left: 12%; 559 | } 560 | 561 | .slider-input--line:nth-child(2) { 562 | left: 21%; 563 | } 564 | 565 | .slider-input--line:nth-child(3) { 566 | left: 30%; 567 | } 568 | 569 | .slider-input--line:nth-child(4) { 570 | left: 39%; 571 | } 572 | 573 | .slider-input--line:nth-child(5) { 574 | left: 50%; 575 | } 576 | 577 | .slider-input--line:nth-child(6) { 578 | left: 60%; 579 | } 580 | 581 | .slider-input--line:nth-child(7) { 582 | left: 70%; 583 | } 584 | 585 | .slider-input--line:nth-child(8) { 586 | left: 80%; 587 | } 588 | 589 | .slider-input--line:nth-child(9) { 590 | left: 90%; 591 | } 592 | 593 | .slider #shRange { 594 | width: 100%; 595 | height: 24px; 596 | position: absolute; 597 | right: 0; 598 | top: -12px; 599 | opacity: 0; 600 | cursor: pointer; 601 | } 602 | 603 | .slider-inner { 604 | height: 1px; 605 | background: var(--grayInactive); 606 | width: 100%; 607 | position: relative; 608 | overflow: hidden; 609 | } 610 | 611 | .slider-inner-fill { 612 | position: absolute; 613 | top: 0; 614 | left: 0; 615 | width: 100%; 616 | height: 100%; 617 | background: var(--brand); 618 | transform: translateX(-80%); 619 | transition: transform 0.2s ease; 620 | will-change: transition; 621 | } 622 | 623 | .slider-round { 624 | width: 24px; 625 | height: 24px; 626 | border-radius: 24px; 627 | background: var(--grayInactive); 628 | border: 1px solid var(--brand); 629 | box-sizing: border-box; 630 | box-shadow: 0px 4px 8px rgba(121, 121, 121, 0.05); 631 | position: absolute; 632 | left: 0; 633 | top: 50%; 634 | transform: translate(80px, -50%); 635 | transition: transform 0.2s ease; 636 | will-change: transform; 637 | pointer-events: none; 638 | } 639 | 640 | .button-checkbox { 641 | cursor: pointer; 642 | border: 1px solid var(--brand); 643 | width: 16px; 644 | height: 16px; 645 | border-radius: 3px; 646 | background: none; 647 | position: relative; 648 | display: block; 649 | margin: 0 auto; 650 | } 651 | 652 | .button-checkbox svg { 653 | opacity: 0; 654 | position: absolute; 655 | top: 50%; 656 | left: 50%; 657 | transform: translate(-50%, -50%); 658 | } 659 | 660 | .button-checkbox--active svg { 661 | opacity: 1; 662 | } 663 | 664 | .button-remove { 665 | cursor: pointer; 666 | margin: 0 auto; 667 | opacity: 0.7; 668 | transition: opacity 0.1s ease; 669 | will-change: opacity; 670 | width: 18px; 671 | height: 24px; 672 | background: none; 673 | border: none; 674 | } 675 | 676 | .button-remove:hover { 677 | opacity: 1; 678 | } 679 | 680 | .language-wasm { 681 | display: block; 682 | outline: none; 683 | } 684 | 685 | .nothing-to-show { 686 | position: absolute; 687 | left: 50%; 688 | top: 50%; 689 | transform: translate(-50%, -50%); 690 | display: flex; 691 | flex-flow: column nowrap; 692 | align-items: center; 693 | justify-content: center; 694 | height: 100vh; 695 | width: 100%; 696 | text-align: center; 697 | font-weight: bold; 698 | line-height: 26px; 699 | font-size: 18px; 700 | color: var(--textGray); 701 | } 702 | 703 | .nothing-to-show span { 704 | margin-top: 6px; 705 | display: block; 706 | font-weight: 400; 707 | font-size: 14px; 708 | line-height: 22px; 709 | } 710 | 711 | .nothing-to-show svg { 712 | margin-bottom: 4px; 713 | width: 120px; 714 | height: 120px; 715 | } 716 | 717 | .bookmarks { 718 | padding: 0 8px; 719 | } 720 | 721 | .bookmarks tr td { 722 | width: 80px; 723 | } 724 | 725 | .bookmarks tr td:nth-child(3), 726 | .bookmarks tr td:nth-child(4), 727 | .bookmarks tr td:nth-child(5) { 728 | width: 100px; 729 | text-align: center; 730 | } 731 | 732 | .bookmarks tr td:nth-child(6) { 733 | width: 45px; 734 | } 735 | 736 | .overlay { 737 | position: fixed; 738 | width: 100%; 739 | height: 100%; 740 | top: 0; 741 | left: 0; 742 | right: 0; 743 | bottom: 0; 744 | background-color: var(--background); 745 | z-index: 10; 746 | cursor: pointer; 747 | display: flex; 748 | flex-flow: row nowrap; 749 | justify-content: center; 750 | align-items: center; 751 | font-style: normal; 752 | font-weight: 600; 753 | font-size: 24px; 754 | line-height: 100%; 755 | letter-spacing: 0.02em; 756 | color: var(--textGray); 757 | } 758 | 759 | .overlay-container { 760 | text-align: center 761 | } 762 | 763 | .patch-controls { 764 | display: flex; 765 | flex-flow: row nowrap; 766 | align-items: center; 767 | } 768 | 769 | .openSavePatchModalButton, 770 | .openLoadPatchModalButton { 771 | font-size: 13px; 772 | cursor: pointer; 773 | padding: 32px 0 8px; 774 | opacity: 0.7; 775 | transition: opacity 0.2s ease; 776 | will-change: opacity; 777 | } 778 | 779 | .openSavePatchModalButton:hover, 780 | .openLoadPatchModalButton:hover { 781 | opacity: 1; 782 | } 783 | 784 | .openSavePatchModalButton { 785 | margin-right: 16px; 786 | } 787 | -------------------------------------------------------------------------------- /extension/thirdparty/prism/prism-live.js: -------------------------------------------------------------------------------- 1 | /** 2 | Prism Live: Code editor based on Prism.js 3 | Works best in Chrome. Currently only very basic support in other browsers (no snippets, no shortcuts) 4 | @author Lea Verou 5 | */ 6 | (async function() { 7 | 8 | if (!window.Bliss) { 9 | // Load Bliss if not loaded 10 | console.log("Bliss not loaded. Loading remotely from blissfuljs.com"); 11 | 12 | let bliss = document.createElement("script"); 13 | bliss.src = "https://blissfuljs.com/bliss.shy.min.js"; 14 | document.head.appendChild(bliss); 15 | 16 | await new Promise(resolve => bliss.onload = resolve); 17 | } 18 | 19 | var $ = Bliss, $$ = Bliss.$; 20 | var ready = Promise.resolve(); 21 | 22 | if (document.currentScript) { 23 | // Tiny dynamic loader. Use e.g. ?load=css,markup,javascript to load components 24 | var base = document.currentScript.src; 25 | var load = new URL(base).searchParams.get("load"); 26 | 27 | if (load !== null) { 28 | var files = ["../prism-live.css"]; 29 | 30 | if (load) { 31 | files.push(...load.split(/,/).map(c => /\./.test(c)? c : `prism-live-${c}.js`)); 32 | } 33 | 34 | ready = Promise.all(files.map(url => $.load(url, base))); 35 | } 36 | } 37 | 38 | var superKey = navigator.platform.indexOf("Mac") === 0? "metaKey" : "ctrlKey"; 39 | 40 | var _ = Prism.Live = class PrismLive { 41 | constructor(source) { 42 | this.source = source; 43 | this.sourceType = source.nodeName.toLowerCase(); 44 | 45 | this.wrapper = $.create({ 46 | className: "prism-live", 47 | around: this.source 48 | }); 49 | 50 | if (this.sourceType === "textarea") { 51 | this.textarea = this.source; 52 | this.code = $.create("code"); 53 | 54 | this.pre = $.create("pre", { 55 | className: this.textarea.className + " no-whitespace-normalization", 56 | contents: this.code, 57 | before: this.textarea 58 | }); 59 | } 60 | else { 61 | this.pre = this.source; 62 | 63 | this.textarea = $.create("textarea", { 64 | className: this.pre.className, 65 | value: this.pre.textContent, 66 | after: this.pre 67 | }); 68 | } 69 | 70 | _.all.set(this.textarea, this); 71 | _.all.set(this.pre, this); 72 | _.all.set(this.code, this); 73 | 74 | this.pre.classList.add("prism-live"); 75 | this.textarea.classList.add("prism-live"); 76 | 77 | if (self.Incrementable) { 78 | // TODO data-* attribute for modifier 79 | // TODO load dynamically if not present 80 | new Incrementable(this.textarea); 81 | } 82 | 83 | $.bind(this.textarea, { 84 | input: evt => this.update(), 85 | 86 | keyup: evt => { 87 | if (evt.key == "Enter") { // Enter 88 | // Maintain indent on line breaks 89 | this.insert(this.currentIndent); 90 | this.syncScroll(); 91 | } 92 | }, 93 | 94 | keydown: evt => { 95 | if (evt.key == "Tab" && !evt.altKey) { 96 | // Default is to move focus off the textarea 97 | // this is never desirable in an editor 98 | evt.preventDefault(); 99 | 100 | if (this.tabstops && this.tabstops.length > 0) { 101 | // We have tabstops to go 102 | this.moveCaret(this.tabstops.shift()); 103 | } 104 | else if (this.hasSelection) { 105 | var before = this.beforeCaret("\n"); 106 | var outdent = evt.shiftKey; 107 | 108 | this.selectionStart -= before.length; 109 | 110 | var selection = _.adjustIndentation(this.selection, { 111 | relative: true, 112 | indentation: outdent? -1 : 1 113 | }); 114 | 115 | this.replace(selection); 116 | 117 | if (outdent) { 118 | var indentStart = _.regexp.gm`^${this.indent}`; 119 | var isBeforeIndented = indentStart.test(before); 120 | this.selectionStart += before.length + 1 - (outdent + isBeforeIndented); 121 | } 122 | else { // Indent 123 | var hasLineAbove = before.length == this.selectionStart; 124 | this.selectionStart += before.length + 1 + !hasLineAbove; 125 | } 126 | } 127 | else { 128 | // Nothing selected, expand snippet 129 | var selector = _.match(this.beforeCaret(), /\S*$/); 130 | var snippetExpanded = this.expandSnippet(selector); 131 | 132 | if (snippetExpanded) { 133 | requestAnimationFrame(() => $.fire(this.textarea, "input")); 134 | } 135 | else { 136 | this.insert(this.indent); 137 | } 138 | } 139 | } 140 | else if (_.pairs[evt.key]) { 141 | var other = _.pairs[evt.key]; 142 | this.wrapSelection({ 143 | before: evt.key, 144 | after: other, 145 | outside: true 146 | }); 147 | evt.preventDefault(); 148 | } 149 | else { 150 | for (let shortcut in _.shortcuts) { 151 | if (_.checkShortcut(shortcut, evt)) { 152 | _.shortcuts[shortcut].call(this, evt); 153 | evt.preventDefault(); 154 | } 155 | } 156 | } 157 | }, 158 | 159 | click: evt => { 160 | var l = this.getLine(); 161 | var v = this.value; 162 | var ss = this.selectionStart; 163 | //console.log(ss, v[ss], l, v.slice(l.start, l.end)); 164 | }, 165 | 166 | "click keyup": evt => { 167 | if (!evt.key || evt.key.lastIndexOf("Arrow") > -1) { 168 | // Caret moved 169 | this.tabstops = null; 170 | } 171 | } 172 | }); 173 | 174 | // this.syncScroll(); 175 | this.textarea.addEventListener("scroll", this, {passive: true}); 176 | 177 | $.bind(window, { 178 | "resize": evt => this.syncStyles() 179 | }); 180 | 181 | // Copy styles with a delay 182 | requestAnimationFrame(() => { 183 | this.syncStyles(); 184 | 185 | var sourceCS = getComputedStyle(this.source); 186 | 187 | this.pre.style.height = this.source.style.height || sourceCS.getPropertyValue("--height"); 188 | this.pre.style.maxHeight = this.source.style.maxHeight || sourceCS.getPropertyValue("--max-height"); 189 | }); 190 | 191 | this.update(); 192 | this.lang = this.code.className.match(/lang(?:uage)?-(\w+)/i)[1]; 193 | } 194 | 195 | handleEvent(evt) { 196 | if (evt.type === "scroll") { 197 | this.syncScroll(); 198 | } 199 | } 200 | 201 | expandSnippet(text) { 202 | if (!text) { 203 | return false; 204 | } 205 | 206 | var context = this.context; 207 | 208 | if (text in context.snippets || text in _.snippets) { 209 | // Static Snippets 210 | var expansion = context.snippets[text] || _.snippets[text]; 211 | } 212 | else if (context.snippets.custom) { 213 | var expansion = context.snippets.custom.call(this, text); 214 | } 215 | 216 | if (expansion) { 217 | // Insert snippet 218 | var stops = []; 219 | var replacement = []; 220 | var str = expansion; 221 | var match; 222 | 223 | while (match = _.CARET_INDICATOR.exec(str)) { 224 | stops.push(match.index + 1); 225 | replacement.push(str.slice(0, match.index + match[1].length)); 226 | str = str.slice(match.index + match[0].length); 227 | _.CARET_INDICATOR.lastIndex = 0; 228 | } 229 | 230 | replacement.push(str); 231 | replacement = replacement.join(""); 232 | 233 | if (stops.length > 0) { 234 | // make first stop relative to end, all others relative to previous stop 235 | stops[0] -= replacement.length; 236 | } 237 | 238 | this.delete(text); 239 | this.insert(replacement, {matchIndentation: true}); 240 | this.tabstops = stops; 241 | this.moveCaret(this.tabstops.shift()); 242 | } 243 | 244 | return !!expansion; 245 | } 246 | 247 | get selectionStart() { 248 | return this.textarea.selectionStart; 249 | } 250 | set selectionStart(v) { 251 | this.textarea.selectionStart = v; 252 | } 253 | 254 | get selectionEnd() { 255 | return this.textarea.selectionEnd; 256 | } 257 | set selectionEnd(v) { 258 | this.textarea.selectionEnd = v; 259 | } 260 | 261 | get hasSelection() { 262 | return this.selectionStart != this.selectionEnd; 263 | } 264 | 265 | get selection() { 266 | return this.value.slice(this.selectionStart, this.selectionEnd); 267 | } 268 | 269 | get value() { 270 | return this.textarea.value; 271 | } 272 | set value(v) { 273 | this.textarea.value = v; 274 | } 275 | 276 | get indent() { 277 | return _.match(this.value, /^[\t ]+/m, _.DEFAULT_INDENT); 278 | } 279 | 280 | get currentIndent() { 281 | var before = this.value.slice(0, this.selectionStart-1); 282 | return _.match(before, /^[\t ]*/mg, "", -1); 283 | } 284 | 285 | // Current language at caret position 286 | get currentLanguage() { 287 | var node = this.getNode(); 288 | node = node? node.parentNode : this.code; 289 | var lang = _.match(node.closest('[class*="language-"]').className, /language-(\w+)/, 1); 290 | return _.aliases[lang] || lang; 291 | } 292 | 293 | // Get settings based on current language 294 | get context() { 295 | var lang = this.currentLanguage; 296 | return _.languages[lang] || _.languages.DEFAULT; 297 | } 298 | 299 | update() { 300 | var code = this.value; 301 | 302 | if (/\n$/.test(this.value)) { 303 | code += "\u200b"; 304 | } 305 | 306 | this.code.textContent = code; 307 | 308 | Prism.highlightElement(this.code); 309 | } 310 | 311 | syncStyles() { 312 | // Copy pre metrics over to textarea 313 | var cs = getComputedStyle(this.pre); 314 | 315 | // Copy styles from
 to textarea
316 |         this.textarea.style.caretColor = cs.color;
317 | 
318 |         var properties = /^(font|lineHeight)|[tT]abSize/gi;
319 | 
320 |         for (var prop in cs) {
321 |             if (cs[prop] && prop in this.textarea.style && properties.test(prop)) {
322 |                 this.wrapper.style[prop] = cs[prop];
323 |                 this.textarea.style[prop] = this.pre.style[prop] = "inherit";
324 |             }
325 |         }
326 | 
327 |         this.textarea.style.paddingLeft = cs.paddingLeft;
328 |         this.textarea.style.paddingTop = cs.paddingTop;
329 | 
330 |         this.update();
331 |     }
332 | 
333 |     syncScroll() {
334 |         if (this.pre.clientWidth === 0 && this.pre.clientHeight === 0) {
335 |             return;
336 |         }
337 | 
338 |         this.pre.scrollTop = this.textarea.scrollTop;
339 |         this.pre.scrollLeft = this.textarea.scrollLeft;
340 |     }
341 | 
342 |     beforeCaretIndex(until = "") {
343 |         return this.value.lastIndexOf(until, this.selectionStart);
344 |     }
345 | 
346 |     afterCaretIndex(until = "") {
347 |         return this.value.indexOf(until, this.selectionEnd);
348 |     }
349 | 
350 |     beforeCaret(until = "") {
351 |         var index = this.beforeCaretIndex(until);
352 | 
353 |         if (index === -1 || !until) {
354 |             index = 0;
355 |         }
356 | 
357 |         return this.value.slice(index, this.selectionStart);
358 |     }
359 | 
360 |     getLine(offset = this.selectionStart) {
361 |         var value = this.value;
362 |         var lf = "\n", cr = "\r";
363 |         var start, end, char;
364 | 
365 |         for (var start = this.selectionStart; char = value[start]; start--) {
366 |             if (char === lf || char === cr || !start) {
367 |                 break;
368 |             }
369 |         }
370 | 
371 |         for (var end = this.selectionStart; char = value[end]; end++) {
372 |             if (char === lf || char === cr) {
373 |                 break;
374 |             }
375 |         }
376 | 
377 |         return {start, end};
378 |     }
379 | 
380 |     afterCaret(until = "") {
381 |         var index = this.afterCaretIndex(until);
382 | 
383 |         if (index === -1 || !until) {
384 |             index = undefined;
385 |         }
386 | 
387 |         return this.value.slice(this.selectionEnd, index);
388 |     }
389 | 
390 |     setCaret(pos) {
391 |         this.selectionStart = this.selectionEnd = pos;
392 |     }
393 | 
394 |     moveCaret(chars) {
395 |         if (chars) {
396 |             this.setCaret(this.selectionEnd + chars);
397 |         }
398 |     }
399 | 
400 |     insert(text, {index} = {}) {
401 |         if (!text) {
402 |             return;
403 |         }
404 | 
405 |         this.textarea.focus();
406 | 
407 |         if (index === undefined) {
408 |             // No specified index, insert in current caret position
409 |             this.replace(text);
410 |         }
411 |         else {
412 |             // Specified index, first move caret there
413 |             var start = this.selectionStart;
414 |             var end = this.selectionEnd;
415 | 
416 |             this.selectionStart = this.selectionEnd = index;
417 |             this.replace(text);
418 | 
419 |             this.selectionStart = start + (index < start? text.length : 0);
420 |             this.selectionEnd = end + (index <= end? text.length : 0);
421 |         }
422 |     }
423 | 
424 |     // Replace currently selected text
425 |     replace(text) {
426 |         if (_.supportsExecCommand) {
427 |             var hadSelection = this.hasSelection;
428 |             document.execCommand("insertText", false, text);
429 |             if (hadSelection) {
430 |                 // By default inserText places the caret at the end, losing any selection
431 |                 // What we want instead is the replaced text to be selected
432 |                 this.selectionStart = this.selectionEnd - text.length;
433 |             }
434 |         }
435 |         else {
436 |             this.textarea.setRangeText(text);
437 |             this.update();
438 |         }
439 |     }
440 | 
441 |     // Set text between indexes and restore caret position
442 |     set(text, {start, end} = {}) {
443 |         if (_.supportsExecCommand) {
444 |             var ss = this.selectionStart;
445 |             var se = this.selectionEnd;
446 | 
447 |             this.selectionStart = start;
448 |             this.selectionEnd = end;
449 | 
450 |             document.execCommand("insertText", false, text);
451 | 
452 |             this.selectionStart = ss;
453 |             this.selectionEnd = se;
454 |         }
455 |         else {
456 |             this.textarea.setRangeText(text);
457 |             this.update();
458 |         }
459 |     }
460 | 
461 |     /**
462 |      * Wrap text with strings
463 |      * @param before {String} The text to insert before
464 |      * @param after {String} The text to insert after
465 |      * @param start {Number} Character offset
466 |      * @param end {Number} Character offset
467 |      */
468 |     wrap({before, after, start = this.selectionStart, end = this.selectionEnd} = {}) {
469 |         var ss = this.selectionStart;
470 |         var se = this.selectionEnd;
471 |         var between = this.value.slice(start, end);
472 | 
473 |         this.set(before + between + after, {start, end});
474 | 
475 |         if (ss > start) {
476 |             ss += before.length;
477 |         }
478 | 
479 |         if (se > start) {
480 |             se += before.length;
481 |         }
482 | 
483 |         if (ss > end) {
484 |             ss += after.length;
485 |         }
486 | 
487 |         if (se > end) {
488 |             se += after.length;
489 |         }
490 | 
491 |         this.selectionStart = ss;
492 |         this.selectionEnd = se;
493 |     }
494 | 
495 |     wrapSelection(o = {}) {
496 |         var hadSelection = this.hasSelection;
497 | 
498 |         this.replace(o.before + this.selection + o.after);
499 | 
500 |         if (hadSelection) {
501 |             if (o.outside) {
502 |                 // Do not include new text in selection
503 |                 this.selectionStart += o.before.length;
504 |                 this.selectionEnd -= o.after.length;
505 |             }
506 |         }
507 |         else {
508 |             this.moveCaret(-o.after.length);
509 |         }
510 |     }
511 | 
512 |     toggleComment() {
513 |         var comments = this.context.comments;
514 | 
515 |         // Are we inside a comment?
516 |         var node = this.getNode();
517 |         var commentNode = node.parentNode.closest(".token.comment");
518 | 
519 |         if (commentNode) {
520 |             // Remove comment
521 |             var start = this.getOffset(commentNode);
522 |             var commentText = commentNode.textContent;
523 | 
524 |             if (comments.singleline && commentText.indexOf(comments.singleLine) === 0) {
525 |                 // TODO
526 |             }
527 |             else {
528 |                 comments = comments.multiline || comments;
529 |                 var end = start + commentText.length - comments[1].length;
530 |                 this.set(this.value.slice(start + comments[0].length, end), {start, end: end + comments[1].length});
531 |             }
532 |         }
533 |         else {
534 |             // Not inside comment, add
535 |             if (this.hasSelection) {
536 |                 comments = comments.multiline || comments;
537 | 
538 |                 this.wrapSelection({
539 |                     before: comments[0],
540 |                     after: comments[1]
541 |                 });
542 |             }
543 |             else {
544 |                 // No selection, wrap line
545 |                 // FIXME *inside indent*
546 |                 comments = comments.singleline? [comments.singleline, "\n"] : comments.multiline || comments;
547 |                 end = this.afterCaretIndex("\n");
548 |                 this.wrap({
549 |                     before: comments[0],
550 |                     after: comments[1],
551 |                     start: this.beforeCaretIndex("\n") + 1,
552 |                     end: end < 0? this.value.length : end
553 |                 });
554 |             }
555 |         }
556 |     }
557 | 
558 |     duplicateContent() {
559 |         var before = this.beforeCaret("\n");
560 |         var after = this.afterCaret("\n");
561 |         var text = before + this.selection + after;
562 | 
563 |         this.insert(text, {index: this.selectionStart - before.length});
564 |     }
565 | 
566 |     delete(characters, {forward, pos} = {}) {
567 |         var i = characters = characters > 0? characters : (characters + "").length;
568 | 
569 |         if (pos) {
570 |             var selectionStart = this.selectionStart;
571 |             this.selectionStart = pos;
572 |             this.selectionEnd = pos + this.selectionEnd - selectionStart;
573 |         }
574 | 
575 |         while (i--) {
576 |             document.execCommand(forward? "forwardDelete" : "delete");
577 |         }
578 | 
579 |         if (pos) {
580 |             // Restore caret
581 |             this.selectionStart = selectionStart - characters;
582 |             this.selectionEnd = this.selectionEnd - pos + this.selectionStart;
583 |         }
584 |     }
585 | 
586 |     /**
587 |      * Get the text node at a given chracter offset
588 |      */
589 |     getNode(offset = this.selectionStart, container = this.code) {
590 |         var node, sum = 0;
591 |         var walk = document.createTreeWalker(container, NodeFilter.SHOW_TEXT);
592 | 
593 |         while (node = walk.nextNode()) {
594 |             sum += node.data.length;
595 | 
596 |             if (sum >= offset) {
597 |                 return node;
598 |             }
599 |         }
600 | 
601 |         // if here, offset is larger than maximum
602 |         return null;
603 |     }
604 | 
605 |     /**
606 |      * Get the character offset of a given node in the highlighted source
607 |      */
608 |     getOffset(node) {
609 |         var range = document.createRange();
610 |         range.selectNodeContents(this.code);
611 |         range.setEnd(node, 0);
612 |         return range.toString().length;
613 |     }
614 | 
615 |     // Utility method to get regex matches
616 |     static match(str, regex, def, index = 0) {
617 |         if (typeof def === "number" && arguments.length === 3) {
618 |             index = def;
619 |             def = undefined;
620 |         }
621 | 
622 |         var match = str.match(regex);
623 | 
624 |         if (index < 0) {
625 |             index = match.length + index;
626 |         }
627 | 
628 |         return match? match[index] : def;
629 |     }
630 | 
631 |     static checkShortcut(shortcut, evt) {
632 |         return shortcut.trim().split(/\s*\+\s*/).every(key => {
633 |             switch (key) {
634 |                 case "Cmd":   return evt[superKey];
635 |                 case "Ctrl":  return evt.ctrlKey;
636 |                 case "Shift": return evt.shiftKey;
637 |                 case "Alt":   return evt.altKey;
638 |                 default: return evt.key === key;
639 |             }
640 |         });
641 |     }
642 | 
643 |     static registerLanguage(name, context, parent = _.languages.DEFAULT) {
644 |         Object.setPrototypeOf(context, parent);
645 |         return _.languages[name] = context;
646 |     }
647 | 
648 |     static matchIndentation(text, currentIndent) {
649 |         // FIXME this assumes that text has no indentation of its own
650 |         // to make this more generally useful beyond snippets, we should first
651 |         // strip text's own indentation.
652 |         text = text.replace(/\r?\n/g, "$&" + currentIndent);
653 |     }
654 | 
655 |     static adjustIndentation(text, {indentation, relative = true, indent = _.DEFAULT_INDENT}) {
656 |         if (!relative) {
657 |             // First strip min indentation
658 |             var minIndent = text.match(_.regexp.gm`^(${indent})+`).sort()[0];
659 | 
660 |             if (minIndent) {
661 |                 text.replace(_.regexp.gm`^${minIndent}`, "");
662 |             }
663 |         }
664 | 
665 |         if (indentation < 0) {
666 |             return text.replace(_.regexp.gm`^${indent}`, "");
667 |         }
668 |         else if (indentation > 0) { // Indent
669 |             return text.replace(/^/gm, indent);
670 |         }
671 |     }
672 | };
673 | 
674 | // Static properties
675 | Object.assign(_, {
676 |     all: new WeakMap(),
677 |     ready,
678 |     DEFAULT_INDENT: "\t",
679 |     CARET_INDICATOR: /(^|[^\\])\$(\d+)/g,
680 |     snippets: {
681 |         "test": "Snippets work!",
682 |     },
683 |     pairs: {
684 |         "(": ")",
685 |         "[": "]",
686 |         "{": "}",
687 |         '"': '"',
688 |         "'": "'",
689 |         "`": "`"
690 |     },
691 |     shortcuts: {
692 |         "Cmd + /": function() {
693 |             this.toggleComment();
694 |         },
695 |         "Ctrl + Shift + D": function() {
696 |             this.duplicateContent();
697 |         }
698 |     },
699 |     languages: {
700 |         DEFAULT: {
701 |             comments: {
702 |                 multiline: ["/*", "*/"]
703 |             },
704 |             snippets: {}
705 |         }
706 |     },
707 |     // Map of Prism language ids and their canonical name
708 |     aliases: (() => {
709 |         var ret = {};
710 |         var canonical = new WeakMap(Object.entries(Prism.languages).map(x => x.reverse()).reverse());
711 | 
712 |         for (var id in Prism.languages) {
713 |             var grammar = Prism.languages[id];
714 | 
715 |             if (typeof grammar !== "function") {
716 |                 ret[id] = canonical.get(grammar);
717 |             }
718 |         }
719 | 
720 |         return ret;
721 |     })(),
722 | 
723 |     regexp: (() => {
724 |         var escape = s => s.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&");
725 |         var _regexp = (flags, strings, ...values) => {
726 |             var pattern = strings[0] + values.map((v, i) => escape(v) + strings[i+1]).join("");
727 |             return RegExp(pattern, flags);
728 |         };
729 |         var cache = {};
730 | 
731 |         return new Proxy(_regexp.bind(_, ""), {
732 |             get: (t, property) => {
733 |                 return t[property] || cache[property]
734 |                        || (cache[property] = _regexp.bind(_, property));
735 |             }
736 |         });
737 |     })()
738 | });
739 | 
740 | $.ready().then(() => {
741 |     var t = $.create("textarea", {inside: document.body});
742 |     t.focus();
743 |     document.execCommand("insertText", false, "a");
744 |     _.supportsExecCommand = !!t.value;
745 |     t.remove();
746 | 
747 |     $$(":not(.prism-live) > textarea.prism-live").forEach(textarea => {
748 |         if (!_.all.get(textarea)) {
749 |             new _(textarea);
750 |         }
751 |     });
752 | });
753 | 
754 | })();
755 | 
756 | 


--------------------------------------------------------------------------------
/extension/thirdparty/bliss/bliss.js:
--------------------------------------------------------------------------------
  1 | (function() {
  2 | "use strict";
  3 | 
  4 | function overload(callback, start, end) {
  5 | 	start = start === undefined ? 1 : start;
  6 | 	end = end || start + 1;
  7 | 
  8 | 	if (end - start <= 1) {
  9 | 		return function() {
 10 | 			if (arguments.length <= start || $.type(arguments[start]) === "string") {
 11 | 				return callback.apply(this, arguments);
 12 | 			}
 13 | 
 14 | 			var obj = arguments[start];
 15 | 			var ret;
 16 | 
 17 | 			for (var key in obj) {
 18 | 				var args = Array.prototype.slice.call(arguments);
 19 | 				args.splice(start, 1, key, obj[key]);
 20 | 				ret = callback.apply(this, args);
 21 | 			}
 22 | 
 23 | 			return ret;
 24 | 		};
 25 | 	}
 26 | 
 27 | 	return overload(overload(callback, start + 1, end), start, end - 1);
 28 | }
 29 | 
 30 | // Copy properties from one object to another. Overwrites allowed.
 31 | // Subtle difference of array vs string whitelist: If property doesn't exist in from, array will not define it.
 32 | function extend(to, from, whitelist) {
 33 | 	var whitelistType = type(whitelist);
 34 | 
 35 | 	if (whitelistType === "string") {
 36 | 		// To copy gettters/setters, preserve flags etc
 37 | 		var descriptor = Object.getOwnPropertyDescriptor(from, whitelist);
 38 | 
 39 | 		if (descriptor && (!descriptor.writable || !descriptor.configurable || !descriptor.enumerable || descriptor.get || descriptor.set)) {
 40 | 			delete to[whitelist];
 41 | 			Object.defineProperty(to, whitelist, descriptor);
 42 | 		}
 43 | 		else {
 44 | 			to[whitelist] = from[whitelist];
 45 | 		}
 46 | 	}
 47 | 	else if (whitelistType === "array") {
 48 | 		whitelist.forEach(function(property) {
 49 | 			if (property in from) {
 50 | 				extend(to, from, property);
 51 | 			}
 52 | 		});
 53 | 	}
 54 | 	else {
 55 | 		for (var property in from) {
 56 | 			if (whitelist) {
 57 | 				if (whitelistType === "regexp" && !whitelist.test(property) ||
 58 | 					whitelistType === "function" && !whitelist.call(from, property)) {
 59 | 					continue;
 60 | 				}
 61 | 			}
 62 | 
 63 | 			extend(to, from, property);
 64 | 		}
 65 | 	}
 66 | 
 67 | 	return to;
 68 | }
 69 | 
 70 | /**
 71 |  * Returns the [[Class]] of an object in lowercase (eg. array, date, regexp, string etc)
 72 |  */
 73 | function type(obj) {
 74 | 	if (obj === null) {
 75 | 		return "null";
 76 | 	}
 77 | 
 78 | 	if (obj === undefined) {
 79 | 		return "undefined";
 80 | 	}
 81 | 
 82 | 	var ret = (Object.prototype.toString.call(obj).match(/^\[object\s+(.*?)\]$/)[1] || "").toLowerCase();
 83 | 
 84 | 	if (ret == "number" && isNaN(obj)) {
 85 | 		return "nan";
 86 | 	}
 87 | 
 88 | 	return ret;
 89 | }
 90 | 
 91 | var $ = self.Bliss = extend(function(expr, context) {
 92 | 	if (arguments.length == 2 && !context || !expr) {
 93 | 		return null;
 94 | 	}
 95 | 
 96 | 	return $.type(expr) === "string"? (context || document).querySelector(expr) : expr || null;
 97 | }, self.Bliss);
 98 | 
 99 | extend($, {
100 | 	extend: extend,
101 | 	overload: overload,
102 | 	type: type,
103 | 
104 | 	property: $.property || "_",
105 | 	listeners: self.WeakMap? new WeakMap() : new Map(),
106 | 
107 | 	original: {
108 | 		addEventListener: (self.EventTarget || Node).prototype.addEventListener,
109 | 		removeEventListener: (self.EventTarget || Node).prototype.removeEventListener
110 | 	},
111 | 
112 | 	sources: {},
113 | 
114 | 	noop: function() {},
115 | 
116 | 	$: function(expr, context) {
117 | 		if (expr instanceof Node || expr instanceof Window) {
118 | 			return [expr];
119 | 		}
120 | 
121 | 		if (arguments.length == 2 && !context) {
122 | 			return [];
123 | 		}
124 | 
125 | 		return Array.prototype.slice.call(typeof expr == "string"? (context || document).querySelectorAll(expr) : expr || []);
126 | 	},
127 | 
128 | 	/*
129 | 	 * Return first non-undefined value. Mainly used internally.
130 | 	 */
131 | 	defined: function () {
132 | 		for (var i=0; i= 200 && env.xhr.status < 300 || env.xhr.status === 304) {
440 | 					// Success!
441 | 					resolve(env.xhr);
442 | 				}
443 | 				else {
444 | 					reject($.extend(Error(env.xhr.statusText), {
445 | 						xhr: env.xhr,
446 | 						get status() {
447 | 							return this.xhr.status;
448 | 						}
449 | 					}));
450 | 				}
451 | 			};
452 | 
453 | 			env.xhr.onerror = function() {
454 | 				document.body.removeAttribute("data-loading");
455 | 				reject($.extend(Error("Network Error"), {xhr: env.xhr}));
456 | 			};
457 | 
458 | 			env.xhr.ontimeout = function() {
459 | 				document.body.removeAttribute("data-loading");
460 | 				reject($.extend(Error("Network Timeout"), {xhr: env.xhr}));
461 | 			};
462 | 
463 | 			env.xhr.send(env.method === "GET"? null : env.data);
464 | 		});
465 | 		// Hack: Expose xhr.abort(), by attaching xhr to the promise.
466 | 		promise.xhr = env.xhr;
467 | 		return promise;
468 | 	},
469 | 
470 | 	value: function(obj) {
471 | 		var hasRoot = typeof obj !== "string";
472 | 
473 | 		return $.$(arguments).slice(+hasRoot).reduce(function(obj, property) {
474 | 			return obj && obj[property];
475 | 		}, hasRoot? obj : self);
476 | 	}
477 | });
478 | 
479 | $.Hooks = new $.Class({
480 | 	add: function (name, callback, first) {
481 | 		if (typeof arguments[0] != "string") {
482 | 			// Multiple hooks
483 | 			for (var name in arguments[0]) {
484 | 				this.add(name, arguments[0][name], arguments[1]);
485 | 			}
486 | 
487 | 			return;
488 | 		}
489 | 
490 | 		(Array.isArray(name)? name : [name]).forEach(function(name) {
491 | 			this[name] = this[name] || [];
492 | 
493 | 			if (callback) {
494 | 				this[name][first? "unshift" : "push"](callback);
495 | 			}
496 | 		}, this);
497 | 	},
498 | 
499 | 	run: function (name, env) {
500 | 		this[name] = this[name] || [];
501 | 		this[name].forEach(function(callback) {
502 | 			callback.call(env && env.context? env.context : env, env);
503 | 		});
504 | 	}
505 | });
506 | 
507 | $.hooks = new $.Hooks();
508 | 
509 | var _ = $.property;
510 | 
511 | $.Element = function (subject) {
512 | 	this.subject = subject;
513 | 
514 | 	// Author-defined element-related data
515 | 	this.data = {};
516 | 
517 | 	// Internal Bliss element-related data
518 | 	this.bliss = {};
519 | };
520 | 
521 | $.Element.prototype = {
522 | 	set: overload(function(property, value) {
523 | 		if (property in $.setProps) {
524 | 			$.setProps[property].call(this, value);
525 | 		}
526 | 		else if (property in this) {
527 | 			this[property] = value;
528 | 		}
529 | 		else {
530 | 			this.setAttribute(property, value);
531 | 		}
532 | 
533 | 	}, 0),
534 | 
535 | 	// Run a CSS transition, return promise
536 | 	transition: function(props, duration) {
537 | 		return new Promise(function(resolve, reject) {
538 | 			if ("transition" in this.style && duration !== 0) {
539 | 				// Get existing style
540 | 				var previous = $.extend({}, this.style, /^transition(Duration|Property)$/);
541 | 
542 | 				$.style(this, {
543 | 					transitionDuration: (duration || 400) + "ms",
544 | 					transitionProperty: Object.keys(props).join(", ")
545 | 				});
546 | 
547 | 				$.once(this, "transitionend", function() {
548 | 					clearTimeout(i);
549 | 					$.style(this, previous);
550 | 					resolve(this);
551 | 				});
552 | 
553 | 				// Failsafe, in case transitionend doesn’t fire
554 | 				var i = setTimeout(resolve, duration+50, this);
555 | 
556 | 				$.style(this, props);
557 | 			}
558 | 			else {
559 | 				$.style(this, props);
560 | 				resolve(this);
561 | 			}
562 | 		}.bind(this));
563 | 	},
564 | 
565 | 	// Fire a synthesized event on the element
566 | 	fire: function (type, properties) {
567 | 		var evt = document.createEvent("HTMLEvents");
568 | 
569 | 		evt.initEvent(type, true, true );
570 | 
571 | 		// Return the result of dispatching the event, so we
572 | 		// can know if `e.preventDefault` was called inside it
573 | 		return this.dispatchEvent($.extend(evt, properties));
574 | 	},
575 | 
576 | 	bind: overload(function(types, options) {
577 | 		if (arguments.length > 1 && ($.type(options) === "function" || options.handleEvent)) {
578 | 			// options is actually callback
579 | 			var callback = options;
580 | 			options = $.type(arguments[2]) === "object"? arguments[2] : {
581 | 				capture: !!arguments[2] // in case it's passed as a boolean 3rd arg
582 | 			};
583 | 			options.callback = callback;
584 | 		}
585 | 
586 | 		var listeners = $.listeners.get(this) || {};
587 | 
588 | 		types.trim().split(/\s+/).forEach(function (type) {
589 | 			if (type.indexOf(".") > -1) {
590 | 				type = type.split(".");
591 | 				var className = type[1];
592 | 				type = type[0];
593 | 			}
594 | 
595 | 			listeners[type] = listeners[type] || [];
596 | 
597 | 			if (listeners[type].filter(function(l) {
598 | 				return l.callback === options.callback && l.capture == options.capture;
599 | 			}).length === 0) {
600 | 				listeners[type].push($.extend({className: className}, options));
601 | 			}
602 | 
603 | 			$.original.addEventListener.call(this, type, options.callback, options);
604 | 		}, this);
605 | 
606 | 		$.listeners.set(this, listeners);
607 | 	}, 0),
608 | 
609 | 	unbind: overload(function(types, options) {
610 | 		if (options && ($.type(options) === "function" || options.handleEvent)) {
611 | 			var callback = options;
612 | 			options = arguments[2];
613 | 		}
614 | 
615 | 		if ($.type(options) == "boolean") {
616 | 			options = {capture: options};
617 | 		}
618 | 
619 | 		options = options || {};
620 | 		options.callback = options.callback || callback;
621 | 
622 | 		var listeners = $.listeners.get(this);
623 | 
624 | 		(types || "").trim().split(/\s+/).forEach(function (type) {
625 | 			if (type.indexOf(".") > -1) {
626 | 				type = type.split(".");
627 | 				var className = type[1];
628 | 				type = type[0];
629 | 			}
630 | 
631 | 			//if listeners exist, always go through listeners to clean up
632 | 			if (!listeners) {
633 | 				if (type && options.callback) {
634 | 					return $.original.removeEventListener.call(this, type, options.callback, options.capture);
635 | 				}
636 | 				return;
637 | 			}
638 | 
639 | 			// Mass unbinding, need to go through listeners
640 | 			for (var ltype in listeners) {
641 | 				if (!type || ltype === type) {
642 | 					// No forEach, because we’re mutating the array
643 | 					for (var i=0, l; l=listeners[ltype][i]; i++) {
644 | 						if ((!className || className === l.className)
645 | 							&& (!options.callback || options.callback === l.callback)
646 | 							&& (!!options.capture == !!l.capture || 
647 | 						    		!type && !options.callback && undefined === options.capture)
648 | 						   ) {
649 | 								listeners[ltype].splice(i, 1);
650 | 								$.original.removeEventListener.call(this, ltype, l.callback, l.capture);
651 | 								i--;
652 | 						}
653 | 					}
654 | 				}
655 | 			}
656 | 		}, this);
657 | 	}, 0),
658 | 
659 | 	// Return a promise that resolves when an event fires, then unbind
660 | 	when: function(type, test) {
661 | 		var me = this;
662 | 		return new Promise(function(resolve) {
663 | 			me.addEventListener(type, function callee(evt) {
664 | 				if (!test || test.call(this, evt)) {
665 | 					this.removeEventListener(type, callee);
666 | 					resolve(evt);
667 | 				}
668 | 			});
669 | 		});
670 | 	},
671 | 
672 | 	toggleAttribute: function(name, value, test) {
673 | 		if (arguments.length < 3) {
674 | 			test = value !== null;
675 | 		}
676 | 
677 | 		if (test) {
678 | 			this.setAttribute(name, value);
679 | 		}
680 | 		else {
681 | 			this.removeAttribute(name);
682 | 		}
683 | 	}
684 | };
685 | 
686 | /*
687 |  * Properties with custom handling in $.set()
688 |  * Also available as functions directly on element._ and on $
689 |  */
690 | $.setProps = {
691 | 	// Set a bunch of inline CSS styles
692 | 	style: function (val) {
693 | 		for (var property in val) {
694 | 			if (property in this.style) {
695 | 				// camelCase versions
696 | 				this.style[property] = val[property];
697 | 			}
698 | 			else {
699 | 				// This way we can set CSS Variables too and use normal property names
700 | 				this.style.setProperty(property, val[property]);
701 | 			}
702 | 		}
703 | 	},
704 | 
705 | 	// Set a bunch of attributes
706 | 	attributes: function (o) {
707 | 		for (var attribute in o) {
708 | 			this.setAttribute(attribute, o[attribute]);
709 | 		}
710 | 	},
711 | 
712 | 	// Set a bunch of properties on the element
713 | 	properties: function (val) {
714 | 		$.extend(this, val);
715 | 	},
716 | 
717 | 	// Bind one or more events to the element
718 | 	events: function (val) {
719 | 		if (arguments.length == 1 && val && val.addEventListener) {
720 | 			// Copy events from other element (requires Bliss Full)
721 | 			var me = this;
722 | 
723 | 			// Copy listeners
724 | 			if ($.listeners) {
725 | 				var listeners = $.listeners.get(val);
726 | 
727 | 				for (var type in listeners) {
728 | 					listeners[type].forEach(function(l) {
729 | 						$.bind(me, type, l.callback, l.capture);
730 | 					});
731 | 				}
732 | 			}
733 | 
734 | 			// Copy inline events
735 | 			for (var onevent in val) {
736 | 				if (onevent.indexOf("on") === 0) {
737 | 					this[onevent] = val[onevent];
738 | 				}
739 | 			}
740 | 		}
741 | 		else {
742 | 			return $.bind.apply(this, [this].concat($.$(arguments)));
743 | 		}
744 | 	},
745 | 
746 | 	once: overload(function(types, callback) {
747 | 		var me = this;
748 | 		var once = function() {
749 | 			$.unbind(me, types, once);
750 | 
751 | 			return callback.apply(me, arguments);
752 | 		};
753 | 
754 | 		$.bind(this, types, once, {once: true});
755 | 	}, 0),
756 | 
757 | 	// Event delegation
758 | 	delegate: overload(function (type, selector, callback) {
759 | 		$.bind(this, type, function(evt) {
760 | 			if (evt.target.closest(selector)) {
761 | 				callback.call(this, evt);
762 | 			}
763 | 		});
764 | 	}, 0, 2),
765 | 
766 | 	// Set the contents as a string, an element, an object to create an element or an array of these
767 | 	contents: function (val) {
768 | 		if (val || val === 0) {
769 | 			(Array.isArray(val)? val : [val]).forEach(function (child) {
770 | 				var type = $.type(child);
771 | 
772 | 				if (/^(string|number)$/.test(type)) {
773 | 					child = document.createTextNode(child + "");
774 | 				}
775 | 				else if (type === "object") {
776 | 					child = $.create(child);
777 | 				}
778 | 
779 | 				if (child instanceof Node) {
780 | 					this.appendChild(child);
781 | 				}
782 | 			}, this);
783 | 		}
784 | 	},
785 | 
786 | 	// Append the element inside another element
787 | 	inside: function (element) {
788 | 		element && element.appendChild(this);
789 | 	},
790 | 
791 | 	// Insert the element before another element
792 | 	before: function (element) {
793 | 		element && element.parentNode.insertBefore(this, element);
794 | 	},
795 | 
796 | 	// Insert the element after another element
797 | 	after: function (element) {
798 | 		element && element.parentNode.insertBefore(this, element.nextSibling);
799 | 	},
800 | 
801 | 	// Insert the element before another element's contents
802 | 	start: function (element) {
803 | 		element && element.insertBefore(this, element.firstChild);
804 | 	},
805 | 
806 | 	// Wrap the element around another element
807 | 	around: function (element) {
808 | 		if (element && element.parentNode) {
809 | 			$.before(this, element);
810 | 		}
811 | 
812 | 		this.appendChild(element);
813 | 	}
814 | };
815 | 
816 | $.Array = function (subject) {
817 | 	this.subject = subject;
818 | };
819 | 
820 | $.Array.prototype = {
821 | 	all: function(method) {
822 | 		var args = $.$(arguments).slice(1);
823 | 
824 | 		return this[method].apply(this, args);
825 | 	}
826 | };
827 | 
828 | // Extends Bliss with more methods
829 | $.add = overload(function(method, callback, on, noOverwrite) {
830 | 	on = $.extend({$: true, element: true, array: true}, on);
831 | 
832 | 	if ($.type(callback) == "function") {
833 | 		if (on.element && (!(method in $.Element.prototype) || !noOverwrite)) {
834 | 			$.Element.prototype[method] = function () {
835 | 				return this.subject && $.defined(callback.apply(this.subject, arguments), this.subject);
836 | 			};
837 | 		}
838 | 
839 | 		if (on.array && (!(method in $.Array.prototype) || !noOverwrite)) {
840 | 			$.Array.prototype[method] = function() {
841 | 				var args = arguments;
842 | 				return this.subject.map(function(element) {
843 | 					return element && $.defined(callback.apply(element, args), element);
844 | 				});
845 | 			};
846 | 		}
847 | 
848 | 		if (on.$) {
849 | 			$.sources[method] = $[method] = callback;
850 | 
851 | 			if (on.array || on.element) {
852 | 				$[method] = function () {
853 | 					var args = [].slice.apply(arguments);
854 | 					var subject = args.shift();
855 | 					var Type = on.array && Array.isArray(subject)? "Array" : "Element";
856 | 
857 | 					return $[Type].prototype[method].apply({subject: subject}, args);
858 | 				};
859 | 			}
860 | 		}
861 | 	}
862 | }, 0);
863 | 
864 | $.add($.Array.prototype, {element: false});
865 | $.add($.Element.prototype);
866 | $.add($.setProps);
867 | $.add($.classProps, {element: false, array: false});
868 | 
869 | // Add native methods on $ and _
870 | var dummy = document.createElement("_");
871 | $.add($.extend({}, HTMLElement.prototype, function(method) {
872 | 	return $.type(dummy[method]) === "function";
873 | }), null, true);
874 | 
875 | 
876 | })();
877 | 
878 | (function($) {
879 | "use strict";
880 | 
881 | if (!Bliss || Bliss.shy) {
882 | 	return;
883 | }
884 | 
885 | var _ = Bliss.property;
886 | 
887 | // Methods requiring Bliss Full
888 | $.add({
889 | 	// Clone elements, with events and data
890 | 	clone: function () {
891 | 		console.warn("$.clone() is deprecated and will be removed in a future version of Bliss.");
892 | 		var clone = this.cloneNode(true);
893 | 		var descendants = $.$("*", clone).concat(clone);
894 | 
895 | 		$.$("*", this).concat(this).forEach(function(element, i, arr) {
896 | 			$.events(descendants[i], element);
897 | 			descendants[i]._.data = $.extend({}, element._.data);
898 | 		});
899 | 
900 | 		return clone;
901 | 	}
902 | }, {array: false});
903 | 
904 | // Define the _ property on arrays and elements
905 | 
906 | Object.defineProperty(Node.prototype, _, {
907 | 	// Written for IE compatability (see #49)
908 | 	get: function getter () {
909 | 		Object.defineProperty(Node.prototype, _, {
910 | 			get: undefined
911 | 		});
912 | 		Object.defineProperty(this, _, {
913 | 			value: new $.Element(this)
914 | 		});
915 | 		Object.defineProperty(Node.prototype, _, {
916 | 			get: getter
917 | 		});
918 | 		return this[_];
919 | 	},
920 | 	configurable: true
921 | });
922 | 
923 | Object.defineProperty(Array.prototype, _, {
924 | 	get: function () {
925 | 		Object.defineProperty(this, _, {
926 | 			value: new $.Array(this)
927 | 		});
928 | 
929 | 		return this[_];
930 | 	},
931 | 	configurable: true
932 | });
933 | 
934 | // Hijack addEventListener and removeEventListener to store callbacks
935 | 
936 | if (self.EventTarget && "addEventListener" in EventTarget.prototype) {
937 | 	EventTarget.prototype.addEventListener = function(type, callback, options) {
938 | 		return $.bind(this, type, callback, options);
939 | 	};
940 | 
941 | 	EventTarget.prototype.removeEventListener = function(type, callback, options) {
942 | 		return $.unbind(this, type, callback, options);
943 | 	};
944 | }
945 | 
946 | // Set $ and $$ convenience methods, if not taken
947 | self.$ = self.$ || $;
948 | self.$$ = self.$$ || $.$;
949 | 
950 | })(Bliss);
951 | 


--------------------------------------------------------------------------------
/content/thirdparty/stacktrace/stacktrace.min.js:
--------------------------------------------------------------------------------
1 | !function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var n;n="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,n.StackTrace=e()}}(function(){var e;return function n(e,r,t){function o(a,s){if(!r[a]){if(!e[a]){var u="function"==typeof require&&require;if(!s&&u)return u(a,!0);if(i)return i(a,!0);var c=new Error("Cannot find module '"+a+"'");throw c.code="MODULE_NOT_FOUND",c}var l=r[a]={exports:{}};e[a][0].call(l.exports,function(n){var r=e[a][1][n];return o(r?r:n)},l,l.exports,n,e,r,t)}return r[a].exports}for(var i="function"==typeof require&&require,a=0;a-1&&(n=n.replace(/eval code/g,"eval").replace(/(\(eval at [^\()]*)|(\)\,.*$)/g,""));var r=n.replace(/^\s+/,"").replace(/\(eval code/g,"(").split(/\s+/).slice(1),t=this.extractLocation(r.pop()),o=r.join(" ")||void 0,i=["eval",""].indexOf(t[0])>-1?void 0:t[0];return new e({functionName:o,fileName:i,lineNumber:t[1],columnNumber:t[2],source:n})},this)},parseFFOrSafari:function(n){var r=n.stack.split("\n").filter(function(e){return!e.match(t)},this);return r.map(function(n){if(n.indexOf(" > eval")>-1&&(n=n.replace(/ line (\d+)(?: > eval line \d+)* > eval\:\d+\:\d+/g,":$1")),n.indexOf("@")===-1&&n.indexOf(":")===-1)return new e({functionName:n});var r=n.split("@"),t=this.extractLocation(r.pop()),o=r.join("@")||void 0;return new e({functionName:o,fileName:t[0],lineNumber:t[1],columnNumber:t[2],source:n})},this)},parseOpera:function(e){return!e.stacktrace||e.message.indexOf("\n")>-1&&e.message.split("\n").length>e.stacktrace.split("\n").length?this.parseOpera9(e):e.stack?this.parseOpera11(e):this.parseOpera10(e)},parseOpera9:function(n){for(var r=/Line (\d+).*script (?:in )?(\S+)/i,t=n.message.split("\n"),o=[],i=2,a=t.length;i/,"$2").replace(/\([^\)]*\)/g,"")||void 0;i.match(/\(([^\)]*)\)/)&&(r=i.replace(/^[^\(]+\(([^\)]*)\)$/,"$1"));var s=void 0===r||"[arguments not available]"===r?void 0:r.split(",");return new e({functionName:a,args:s,fileName:o[0],lineNumber:o[1],columnNumber:o[2],source:n})},this)}}})},{stackframe:10}],2:[function(e,n,r){function t(){this._array=[],this._set=Object.create(null)}var o=e("./util"),i=Object.prototype.hasOwnProperty;t.fromArray=function(e,n){for(var r=new t,o=0,i=e.length;o=0&&e>1;return n?-r:r}var i=e("./base64"),a=5,s=1<>>=a,o>0&&(n|=c),r+=i.encode(n);while(o>0);return r},r.decode=function(e,n,r){var t,s,l=e.length,f=0,p=0;do{if(n>=l)throw new Error("Expected more digits in base 64 VLQ value.");if(s=i.decode(e.charCodeAt(n++)),s===-1)throw new Error("Invalid base64 digit: "+e.charAt(n-1));t=!!(s&c),s&=u,f+=s<0?n-u>1?t(u,n,o,i,a,s):s==r.LEAST_UPPER_BOUND?n1?t(e,u,o,i,a,s):s==r.LEAST_UPPER_BOUND?u:e<0?-1:e}r.GREATEST_LOWER_BOUND=1,r.LEAST_UPPER_BOUND=2,r.search=function(e,n,o,i){if(0===n.length)return-1;var a=t(-1,n.length,e,n,o,i||r.GREATEST_LOWER_BOUND);if(a<0)return-1;for(;a-1>=0&&0===o(n[a],n[a-1],!0);)--a;return a}},{}],6:[function(e,n,r){function t(e,n,r){var t=e[n];e[n]=e[r],e[r]=t}function o(e,n){return Math.round(e+Math.random()*(n-e))}function i(e,n,r,a){if(r=0){var i=this._originalMappings[o];if(void 0===e.column)for(var a=i.originalLine;i&&i.originalLine===a;)t.push({line:s.getArg(i,"generatedLine",null),column:s.getArg(i,"generatedColumn",null),lastColumn:s.getArg(i,"lastGeneratedColumn",null)}),i=this._originalMappings[++o];else for(var c=i.originalColumn;i&&i.originalLine===n&&i.originalColumn==c;)t.push({line:s.getArg(i,"generatedLine",null),column:s.getArg(i,"generatedColumn",null),lastColumn:s.getArg(i,"lastGeneratedColumn",null)}),i=this._originalMappings[++o]}return t},r.SourceMapConsumer=t,o.prototype=Object.create(t.prototype),o.prototype.consumer=t,o.fromSourceMap=function(e){var n=Object.create(o.prototype),r=n._names=c.fromArray(e._names.toArray(),!0),t=n._sources=c.fromArray(e._sources.toArray(),!0);n.sourceRoot=e._sourceRoot,n.sourcesContent=e._generateSourcesContent(n._sources.toArray(),n.sourceRoot),n.file=e._file;for(var a=e._mappings.toArray().slice(),u=n.__generatedMappings=[],l=n.__originalMappings=[],p=0,g=a.length;p1&&(r.source=m+o[1],m+=o[1],r.originalLine=g+o[2],g=r.originalLine,r.originalLine+=1,r.originalColumn=h+o[3],h=r.originalColumn,o.length>4&&(r.name=d+o[4],d+=o[4])),C.push(r),"number"==typeof r.originalLine&&b.push(r)}f(C,s.compareByGeneratedPositionsDeflated),this.__generatedMappings=C,f(b,s.compareByOriginalPositions),this.__originalMappings=b},o.prototype._findMapping=function(e,n,r,t,o,i){if(e[r]<=0)throw new TypeError("Line must be greater than or equal to 1, got "+e[r]);if(e[t]<0)throw new TypeError("Column must be greater than or equal to 0, got "+e[t]);return u.search(e,n,o,i)},o.prototype.computeColumnSpans=function(){for(var e=0;e=0){var o=this._generatedMappings[r];if(o.generatedLine===n.generatedLine){var i=s.getArg(o,"source",null);null!==i&&(i=this._sources.at(i),null!=this.sourceRoot&&(i=s.join(this.sourceRoot,i)));var a=s.getArg(o,"name",null);return null!==a&&(a=this._names.at(a)),{source:i,line:s.getArg(o,"originalLine",null),column:s.getArg(o,"originalColumn",null),name:a}}}return{source:null,line:null,column:null,name:null}},o.prototype.hasContentsOfAllSources=function(){return!!this.sourcesContent&&(this.sourcesContent.length>=this._sources.size()&&!this.sourcesContent.some(function(e){return null==e}))},o.prototype.sourceContentFor=function(e,n){if(!this.sourcesContent)return null;if(null!=this.sourceRoot&&(e=s.relative(this.sourceRoot,e)),this._sources.has(e))return this.sourcesContent[this._sources.indexOf(e)];var r;if(null!=this.sourceRoot&&(r=s.urlParse(this.sourceRoot))){var t=e.replace(/^file:\/\//,"");if("file"==r.scheme&&this._sources.has(t))return this.sourcesContent[this._sources.indexOf(t)];if((!r.path||"/"==r.path)&&this._sources.has("/"+e))return this.sourcesContent[this._sources.indexOf("/"+e)]}if(n)return null;throw new Error('"'+e+'" is not in the SourceMap.')},o.prototype.generatedPositionFor=function(e){var n=s.getArg(e,"source");if(null!=this.sourceRoot&&(n=s.relative(this.sourceRoot,n)),!this._sources.has(n))return{line:null,column:null,lastColumn:null};n=this._sources.indexOf(n);var r={source:n,originalLine:s.getArg(e,"line"),originalColumn:s.getArg(e,"column")},o=this._findMapping(r,this._originalMappings,"originalLine","originalColumn",s.compareByOriginalPositions,s.getArg(e,"bias",t.GREATEST_LOWER_BOUND));if(o>=0){var i=this._originalMappings[o];if(i.source===r.source)return{line:s.getArg(i,"generatedLine",null),column:s.getArg(i,"generatedColumn",null),lastColumn:s.getArg(i,"lastGeneratedColumn",null)}}return{line:null,column:null,lastColumn:null}},r.BasicSourceMapConsumer=o,a.prototype=Object.create(t.prototype),a.prototype.constructor=t,a.prototype._version=3,Object.defineProperty(a.prototype,"sources",{get:function(){for(var e=[],n=0;n=0;l--)a=u[l],"."===a?u.splice(l,1):".."===a?c++:c>0&&(""===a?(u.splice(l+1,c),c=0):(u.splice(l,2),c--));return n=u.join("/"),""===n&&(n=s?"/":"."),t?(t.path=n,i(t)):n}function s(e,n){""===e&&(e="."),""===n&&(n=".");var r=o(n),t=o(e);if(t&&(e=t.path||"/"),r&&!r.scheme)return t&&(r.scheme=t.scheme),i(r);if(r||n.match(_))return n;if(t&&!t.host&&!t.path)return t.host=n,i(t);var s="/"===n.charAt(0)?n:a(e.replace(/\/+$/,"")+"/"+n);return t?(t.path=s,i(t)):s}function u(e,n){""===e&&(e="."),e=e.replace(/\/$/,"");for(var r=0;0!==n.indexOf(e+"/");){var t=e.lastIndexOf("/");if(t<0)return n;if(e=e.slice(0,t),e.match(/^([^\/]+:\/)?\/*$/))return n;++r}return Array(r+1).join("../")+n.substr(e.length+1)}function c(e){return e}function l(e){return p(e)?"$"+e:e}function f(e){return p(e)?e.slice(1):e}function p(e){if(!e)return!1;var n=e.length;if(n<9)return!1;if(95!==e.charCodeAt(n-1)||95!==e.charCodeAt(n-2)||111!==e.charCodeAt(n-3)||116!==e.charCodeAt(n-4)||111!==e.charCodeAt(n-5)||114!==e.charCodeAt(n-6)||112!==e.charCodeAt(n-7)||95!==e.charCodeAt(n-8)||95!==e.charCodeAt(n-9))return!1;for(var r=n-10;r>=0;r--)if(36!==e.charCodeAt(r))return!1;return!0}function g(e,n,r){var t=e.source-n.source;return 0!==t?t:(t=e.originalLine-n.originalLine,0!==t?t:(t=e.originalColumn-n.originalColumn,0!==t||r?t:(t=e.generatedColumn-n.generatedColumn,0!==t?t:(t=e.generatedLine-n.generatedLine,0!==t?t:e.name-n.name))))}function h(e,n,r){var t=e.generatedLine-n.generatedLine;return 0!==t?t:(t=e.generatedColumn-n.generatedColumn,0!==t||r?t:(t=e.source-n.source,0!==t?t:(t=e.originalLine-n.originalLine,0!==t?t:(t=e.originalColumn-n.originalColumn,0!==t?t:e.name-n.name))))}function m(e,n){return e===n?0:e>n?1:-1}function d(e,n){var r=e.generatedLine-n.generatedLine;return 0!==r?r:(r=e.generatedColumn-n.generatedColumn,0!==r?r:(r=m(e.source,n.source),0!==r?r:(r=e.originalLine-n.originalLine,0!==r?r:(r=e.originalColumn-n.originalColumn,0!==r?r:m(e.name,n.name)))))}r.getArg=t;var v=/^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.]*)(?::(\d+))?(\S*)$/,_=/^data:.+\,.+$/;r.urlParse=o,r.urlGenerate=i,r.normalize=a,r.join=s,r.isAbsolute=function(e){return"/"===e.charAt(0)||!!e.match(v)},r.relative=u;var y=function(){var e=Object.create(null);return!("__proto__"in e)}();r.toSetString=y?c:l,r.fromSetString=y?c:f,r.compareByOriginalPositions=g,r.compareByGeneratedPositionsDeflated=h,r.compareByGeneratedPositionsInflated=d},{}],9:[function(n,r,t){!function(o,i){"use strict";"function"==typeof e&&e.amd?e("stack-generator",["stackframe"],i):"object"==typeof t?r.exports=i(n("stackframe")):o.StackGenerator=i(o.StackFrame)}(this,function(e){return{backtrace:function(n){var r=[],t=10;"object"==typeof n&&"number"==typeof n.maxStackSize&&(t=n.maxStackSize);for(var o=arguments.callee;o&&r.length=200&&t.status<300||"file://"===e.substr(0,7)&&t.responseText?n(t.responseText):r(new Error("HTTP status: "+t.status+" retrieving "+e)))},t.send()})}function t(e){if("undefined"!=typeof window&&window.atob)return window.atob(e);throw new Error("You must supply a polyfill for window.atob in this environment")}function o(e){if("undefined"!=typeof JSON&&JSON.parse)return JSON.parse(e);throw new Error("You must supply a polyfill for JSON.parse in this environment")}function i(e,n){for(var r=[/['"]?([$_A-Za-z][$_A-Za-z0-9]*)['"]?\s*[:=]\s*function\b/,/function\s+([^('"`]*?)\s*\(([^)]*)\)/,/['"]?([$_A-Za-z][$_A-Za-z0-9]*)['"]?\s*[:=]\s*(?:eval|new Function)\b/,/\b(?!(?:if|for|switch|while|with|catch)\b)(?:(?:static)\s+)?(\S+)\s*\(.*?\)\s*\{/,/['"]?([$_A-Za-z][$_A-Za-z0-9]*)['"]?\s*[:=]\s*\(.*?\)\s*=>/],t=e.split("\n"),o="",i=Math.min(n,20),a=0;a=0&&(s=s.substr(0,u)),s){o=s+o;for(var c=r.length,l=0;l=200&&a.status<400?o(a.responseText):i(new Error("POST to "+n+" failed with status: "+a.status)))},a.open("post",n),a.setRequestHeader("Content-Type","application/json"),t&&"object"==typeof t.headers){var s=t.headers;for(var u in s)s.hasOwnProperty(u)&&a.setRequestHeader(u,s[u])}var c={stack:e};void 0!==r&&null!==r&&(c.message=r),a.send(JSON.stringify(c))})}}})},{"error-stack-parser":1,"stack-generator":9,"stacktrace-gps":11}]},{},[12])(12)});
2 | 


--------------------------------------------------------------------------------
/extension/libs/disassembler.js:
--------------------------------------------------------------------------------
  1 | /**
  2 | Copyright 2020 Jack Baker
  3 | 
  4 | Licensed under the Apache License, Version 2.0 (the "License");
  5 | you may not use this file except in compliance with the License.
  6 | You may obtain a copy of the License at
  7 | 
  8 |     http://www.apache.org/licenses/LICENSE-2.0
  9 | 
 10 | Unless required by applicable law or agreed to in writing, software
 11 | distributed under the License is distributed on an "AS IS" BASIS,
 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13 | See the License for the specific language governing permissions and
 14 | limitations under the License.
 15 | */
 16 | 
 17 | const Instruction = class {
 18 |     constructor() {
 19 |         this.opcode = null;
 20 | 
 21 |         this.immediates = [];
 22 |     }
 23 | };
 24 | 
 25 | const InstructionReader = class extends BufferReader {
 26 |     readKeywords(nameArray) {
 27 |         const results = [];
 28 | 
 29 |         for (let i = 0; i < nameArray.length; i++) {
 30 |             const thisName = nameArray[i];
 31 | 
 32 |             if (typeof thisName !== "string") {
 33 |                 throw new Error("Invalid keyword name in InstructionReader.readKeywords()");
 34 |             }
 35 | 
 36 |             const thisValue = this.readVarUint32();
 37 | 
 38 |             const thisString = `${thisName}=${thisValue}`;
 39 | 
 40 |             results.push(thisString);
 41 |         }
 42 | 
 43 |         return results;
 44 |     }
 45 | 
 46 |     readBlockType() {
 47 |         const blockType = this.readUint8();
 48 | 
 49 |         switch (blockType) {
 50 |             case VALUE_TYPE_I32:
 51 |                 return "i32";
 52 |             case VALUE_TYPE_I64:
 53 |                 return "i64";
 54 |             case VALUE_TYPE_F32:
 55 |                 return "f32";
 56 |             case VALUE_TYPE_F64:
 57 |                 return "f64";
 58 |             case VALUE_TYPE_ANYFUNC:
 59 |             case VALUE_TYPE_FUNC:
 60 |             case VALUE_TYPE_BLOCK:
 61 |                 return "";
 62 |             default:
 63 |                 throw new Error("Bad block type in InstructionReader.readBlockType()");
 64 |         }
 65 |     }
 66 | };
 67 | 
 68 | const Disassembler = class {
 69 |     constructor(bytes, symbols = {}) {
 70 |         this.reader = new InstructionReader(bytes);
 71 | 
 72 |         this.indentStr = "  ";
 73 | 
 74 |         this.symbols = symbols;
 75 |     }
 76 | 
 77 |     disassemble() {
 78 |         const instructions = [];
 79 |         const outputLines = [];
 80 | 
 81 |         let indentDepth = 0;
 82 |         let lineNumber = 0;
 83 | 
 84 |         const byteIndexToLineNum = {};
 85 | 
 86 |         while (this.reader.inPos < this.reader.inBuffer.length) {
 87 |             byteIndexToLineNum[this.reader.inPos] = lineNumber++;
 88 | 
 89 |             const instruction = this.disassembleInstruction();
 90 | 
 91 |             instructions.push(instruction);
 92 |         }
 93 | 
 94 |         for (let i = 0; i < instructions.length; i++) {
 95 |             const thisInstr = instructions[i];
 96 | 
 97 |             const immediateString = thisInstr.immediates.join(" ");
 98 |             const instrString = `${thisInstr.opstring} ${immediateString}`;
 99 | 
100 |             if (thisInstr.opcode == OP_END) {
101 |                 indentDepth--;
102 |             }
103 | 
104 |             if (indentDepth < 0) {
105 |                 indentDepth = 0;
106 |             }
107 | 
108 |             const instrFullString = (this.indentStr.repeat(indentDepth) + instrString);
109 | 
110 |             switch (thisInstr.opcode) {
111 |                 case OP_BLOCK:
112 |                 case OP_LOOP:
113 |                 case OP_IF:
114 |                 case OP_ELSE:
115 |                     indentDepth++;
116 |                     break;
117 |             }
118 | 
119 |             outputLines.push(instrFullString);
120 |         }
121 | 
122 |         const resultObj = {};
123 | 
124 |         resultObj.text = outputLines.join("\n");
125 |         resultObj.lineNums = byteIndexToLineNum;
126 | 
127 |         return resultObj;
128 |     }
129 | 
130 |     disassembleInstruction() {
131 |         const instruction = {};
132 | 
133 |         instruction.opcode = this.reader.readUint8();
134 |         instruction.immediates = [];
135 | 
136 |         switch (instruction.opcode) {
137 |             case OP_UNREACHABLE:
138 |                 instruction.opstring = "unreachable";
139 |                 break;
140 |             case OP_NOP:
141 |                 instruction.opstring = "nop";
142 |                 break;
143 |             case OP_BLOCK:
144 |                 instruction.opstring = "block";
145 |                 instruction.immediates.push(this.reader.readBlockType());
146 |                 break;
147 |             case OP_LOOP:
148 |                 instruction.opstring = "loop";
149 |                 instruction.immediates.push(this.reader.readBlockType());
150 |                 break;
151 |             case OP_IF:
152 |                 instruction.opstring = "if";
153 |                 instruction.immediates.push(this.reader.readBlockType());
154 |                 break;
155 |             case OP_ELSE:
156 |                 instruction.opstring = "else";
157 |                 break;
158 |             case OP_END:
159 |                 instruction.opstring = "end";
160 |                 break;
161 |             case OP_BR:
162 |                 instruction.opstring = "br";
163 |                 instruction.immediates.push(this.reader.readVarUint32());
164 |                 break;
165 |             case OP_BR_IF:
166 |                 instruction.opstring = "br_if";
167 |                 instruction.immediates.push(this.reader.readVarUint32());
168 |                 break;
169 |             case OP_BR_TABLE:
170 |                 instruction.opstring = "br_table";
171 |                 let count = this.reader.readVarUint32();
172 | 
173 |                 instruction.immediates.push(count);
174 | 
175 |                 for (let i = 0; i < count; i++) {
176 |                     instruction.immediates.push(this.reader.readVarUint32());
177 |                 }
178 | 
179 |                 instruction.immediates.push(this.reader.readVarUint32());
180 |                 break;
181 |             case OP_RETURN:
182 |                 instruction.opstring = "return";
183 |                 break;
184 |             case OP_CALL:
185 |                 instruction.opstring = "call";
186 |                 const callTarget = this.reader.readVarUint32();
187 | 
188 |                 if (typeof this.symbols[callTarget] !== "undefined") {
189 |                     instruction.immediates.push(this.symbols[callTarget]);
190 |                 }
191 |                 else {
192 |                     instruction.immediates.push(callTarget);
193 |                 }
194 |                 break;
195 |             case OP_CALL_INDIRECT:
196 |                 instruction.opstring = "call_indirect";
197 |                 instruction.immediates.push(this.reader.readVarUint32());
198 |                 instruction.immediates.push(this.reader.readUint8());
199 |                 break;
200 |             case OP_DROP:
201 |                 instruction.opstring = "drop";
202 |                 break;
203 |             case OP_SELECT:
204 |                 instruction.opstring = "select";
205 |                 break;
206 |             case OP_GET_LOCAL:
207 |                 instruction.opstring = "get_local";
208 |                 instruction.immediates.push(this.reader.readVarUint32());
209 |                 break;
210 |             case OP_SET_LOCAL:
211 |                 instruction.opstring = "set_local";
212 |                 instruction.immediates.push(this.reader.readVarUint32());
213 |                 break;
214 |             case OP_TEE_LOCAL:
215 |                 instruction.opstring = "tee_local";
216 |                 instruction.immediates.push(this.reader.readVarUint32());
217 |                 break;
218 |             case OP_GET_GLOBAL:
219 |                 instruction.opstring = "get_global";
220 |                 instruction.immediates.push(this.reader.readVarUint32());
221 |                 break;
222 |             case OP_SET_GLOBAL:
223 |                 instruction.opstring = "set_global";
224 |                 instruction.immediates.push(this.reader.readVarUint32());
225 |                 break;
226 |             case OP_I32_LOAD:
227 |                 instruction.opstring = "i32.load";
228 |                 instruction.immediates = this.reader.readKeywords(["align", "offset"]);
229 |                 break;
230 |             case OP_I64_LOAD:
231 |                 instruction.opstring = "i64.load";
232 |                 instruction.immediates = this.reader.readKeywords(["align", "offset"]);
233 |                 break;
234 |             case OP_F32_LOAD:
235 |                 instruction.opstring = "f32.load";
236 |                 instruction.immediates = this.reader.readKeywords(["align", "offset"]);
237 |                 break;
238 |             case OP_F64_LOAD:
239 |                 instruction.opstring = "f64.load";
240 |                 instruction.immediates = this.reader.readKeywords(["align", "offset"]);
241 |                 break;
242 |             case OP_I32_LOAD8_S:
243 |                 instruction.opstring = "i32.load8_s";
244 |                 instruction.immediates = this.reader.readKeywords(["align", "offset"]);
245 |                 break;
246 |             case OP_I32_LOAD8_U:
247 |                 instruction.opstring = "i32.load8_u";
248 |                 instruction.immediates = this.reader.readKeywords(["align", "offset"]);
249 |                 break;
250 |             case OP_I32_LOAD16_S:
251 |                 instruction.opstring = "i32.load16_s";
252 |                 instruction.immediates = this.reader.readKeywords(["align", "offset"]);
253 |                 break;
254 |             case OP_I32_LOAD16_U:
255 |                 instruction.opstring = "i32.load16_u";
256 |                 instruction.immediates = this.reader.readKeywords(["align", "offset"]);
257 |                 break;
258 |             case OP_I64_LOAD8_S:
259 |                 instruction.opstring = "i64.load8_s";
260 |                 instruction.immediates = this.reader.readKeywords(["align", "offset"]);
261 |                 break;
262 |             case OP_I64_LOAD8_U:
263 |                 instruction.opstring = "i64.load8_u";
264 |                 instruction.immediates = this.reader.readKeywords(["align", "offset"]);
265 |                 break;
266 |             case OP_I64_LOAD16_S:
267 |                 instruction.opstring = "i64.load16_s";
268 |                 instruction.immediates = this.reader.readKeywords(["align", "offset"]);
269 |                 break;
270 |             case OP_I64_LOAD16_U:
271 |                 instruction.opstring = "i64.load16_u";
272 |                 instruction.immediates = this.reader.readKeywords(["align", "offset"]);
273 |                 break;
274 |             case OP_I64_LOAD32_S:
275 |                 instruction.opstring = "i64.load32_s";
276 |                 instruction.immediates = this.reader.readKeywords(["align", "offset"]);
277 |                 break;
278 |             case OP_I64_LOAD32_U:
279 |                 instruction.opstring = "i64.load32_u";
280 |                 instruction.immediates = this.reader.readKeywords(["align", "offset"]);
281 |                 break;
282 |             case OP_I32_STORE:
283 |                 instruction.opstring = "i32.store";
284 |                 instruction.immediates = this.reader.readKeywords(["align", "offset"]);
285 |                 break;
286 |             case OP_I64_STORE:
287 |                 instruction.opstring = "i64.store";
288 |                 instruction.immediates = this.reader.readKeywords(["align", "offset"]);
289 |                 break;
290 |             case OP_F32_STORE:
291 |                 instruction.opstring = "f32.store";
292 |                 instruction.immediates = this.reader.readKeywords(["align", "offset"]);
293 |                 break;
294 |             case OP_F64_STORE:
295 |                 instruction.opstring = "f64.store";
296 |                 instruction.immediates = this.reader.readKeywords(["align", "offset"]);
297 |                 break;
298 |             case OP_I32_STORE8:
299 |                 instruction.opstring = "i32.store8";
300 |                 instruction.immediates = this.reader.readKeywords(["align", "offset"]);
301 |                 break;
302 |             case OP_I32_STORE16:
303 |                 instruction.opstring = "i32.store16";
304 |                 instruction.immediates = this.reader.readKeywords(["align", "offset"]);
305 |                 break;
306 |             case OP_I64_STORE8:
307 |                 instruction.opstring = "i64.store8";
308 |                 instruction.immediates = this.reader.readKeywords(["align", "offset"]);
309 |                 break;
310 |             case OP_I64_STORE16:
311 |                 instruction.opstring = "i64.store16";
312 |                 instruction.immediates = this.reader.readKeywords(["align", "offset"]);
313 |                 break;
314 |             case OP_I64_STORE32:
315 |                 instruction.opstring = "i64.store32";
316 |                 instruction.immediates = this.reader.readKeywords(["align", "offset"]);
317 |                 break;
318 |             case OP_MEMORY_SIZE:
319 |                 instruction.opstring = "current_memory";
320 |                 instruction.immediates.push(this.reader.readUint8());
321 |                 break;
322 |             case OP_MEMORY_GROW:
323 |                 instruction.opstring = "grow_memory";
324 |                 instruction.immediates.push(this.reader.readUint8());
325 |                 break;
326 |             case OP_I32_CONST:
327 |                 instruction.opstring = "i32.const";
328 |                 instruction.immediates.push(this.reader.readVarUint32());
329 |                 break;
330 |             // TODO Fix
331 |             case OP_I64_CONST:
332 |                 instruction.opstring = "i64.const";
333 |                 instruction.immediates.push(this.reader.readVarUint32());
334 |                 break;
335 |             case OP_F32_CONST:
336 |                 instruction.opstring = "f32.const";
337 | 
338 |                 const f32Bytes = this.reader.readBytes(4);
339 | 
340 |                 const f32ByteBuffer = new Uint8Array(f32Bytes);
341 | 
342 |                 const f32Array = new Float32Array(f32ByteBuffer.buffer);
343 | 
344 |                 instruction.immediates.push(f32Array[0]);
345 |                 break;
346 |             case OP_F64_CONST:
347 |                 instruction.opstring = "f64.const";
348 | 
349 |                 const f64Bytes = this.reader.readBytes(8);
350 | 
351 |                 const f64ByteBuffer = new Uint8Array(f64Bytes);
352 | 
353 |                 const f64Array = new Float64Array(f64ByteBuffer.buffer);
354 | 
355 |                 instruction.immediates.push(f64Array[0]);
356 |                 break;
357 |             case OP_I32_EQZ:
358 |                 instruction.opstring = "i32.eqz";
359 |                 break;
360 |             case OP_I32_EQ:
361 |                 instruction.opstring = "i32.eq";
362 |                 break;
363 |             case OP_I32_NE:
364 |                 instruction.opstring = "i32.ne";
365 |                 break;
366 |             case OP_I32_LT_S:
367 |                 instruction.opstring = "i32.lt_s";
368 |                 break;
369 |             case OP_I32_LT_U:
370 |                 instruction.opstring = "i32.lt_u";
371 |                 break;
372 |             case OP_I32_GT_S:
373 |                 instruction.opstring = "i32.gt_s";
374 |                 break;
375 |             case OP_I32_GT_U:
376 |                 instruction.opstring = "i32.gt_u";
377 |                 break;
378 |             case OP_I32_LE_S:
379 |                 instruction.opstring = "i32.le_s";
380 |                 break;
381 |             case OP_I32_LE_U:
382 |                 instruction.opstring = "i32.le_u";
383 |                 break;
384 |             case OP_I32_GE_S:
385 |                 instruction.opstring = "i32.ge_s";
386 |                 break;
387 |             case OP_I32_GE_U:
388 |                 instruction.opstring = "i32.ge_u";
389 |                 break;
390 |             case OP_I64_EQZ:
391 |                 instruction.opstring = "i64.eqz";
392 |                 break;
393 |             case OP_I64_EQ:
394 |                 instruction.opstring = "i64.eq";
395 |                 break;
396 |             case OP_I64_NE:
397 |                 instruction.opstring = "i64.ne";
398 |                 break;
399 |             case OP_I64_LT_S:
400 |                 instruction.opstring = "i64.lt_s";
401 |                 break;
402 |             case OP_I64_LT_U:
403 |                 instruction.opstring = "i64.lt_u";
404 |                 break;
405 |             case OP_I64_GT_S:
406 |                 instruction.opstring = "i64.gt_s";
407 |                 break;
408 |             case OP_I64_GT_U:
409 |                 instruction.opstring = "i64.gt_u";
410 |                 break;
411 |             case OP_I64_LE_S:
412 |                 instruction.opstring = "i64.le_s";
413 |                 break;
414 |             case OP_I64_LE_U:
415 |                 instruction.opstring = "i64.le_u";
416 |                 break;
417 |             case OP_I64_GE_S:
418 |                 instruction.opstring = "i64.ge_s";
419 |                 break;
420 |             case OP_I64_GE_U:
421 |                 instruction.opstring = "i64.ge_u";
422 |                 break;
423 |             case OP_F32_EQ:
424 |                 instruction.opstring = "f32.eq";
425 |                 break;
426 |             case OP_F32_NE:
427 |                 instruction.opstring = "f32.ne";
428 |                 break;
429 |             case OP_F32_LT:
430 |                 instruction.opstring = "f32.lt";
431 |                 break;
432 |             case OP_F32_GT:
433 |                 instruction.opstring = "f32.gt";
434 |                 break;
435 |             case OP_F32_LE:
436 |                 instruction.opstring = "f32.le";
437 |                 break;
438 |             case OP_F32_GE:
439 |                 instruction.opstring = "f32.ge";
440 |                 break;
441 |             case OP_F64_EQ:
442 |                 instruction.opstring = "f64.eq";
443 |                 break;
444 |             case OP_F64_NE:
445 |                 instruction.opstring = "f64.ne";
446 |                 break;
447 |             case OP_F64_LT:
448 |                 instruction.opstring = "f64.lt";
449 |                 break;
450 |             case OP_F64_GT:
451 |                 instruction.opstring = "f64.gt";
452 |                 break;
453 |             case OP_F64_LE:
454 |                 instruction.opstring = "f64.le";
455 |                 break;
456 |             case OP_F64_GE:
457 |                 instruction.opstring = "f64.ge";
458 |                 break;
459 |             case OP_I32_CLZ:
460 |                 instruction.opstring = "i32.clz";
461 |                 break;
462 |             case OP_I32_CTZ:
463 |                 instruction.opstring = "i32.ctz";
464 |                 break;
465 |             case OP_I32_POPCNT:
466 |                 instruction.opstring = "i32.popcnt";
467 |                 break;
468 |             case OP_I32_ADD:
469 |                 instruction.opstring = "i32.add";
470 |                 break;
471 |             case OP_I32_SUB:
472 |                 instruction.opstring = "i32.sub";
473 |                 break;
474 |             case OP_I32_MUL:
475 |                 instruction.opstring = "i32.mul";
476 |                 break;
477 |             case OP_I32_DIV_S:
478 |                 instruction.opstring = "i32.div_s";
479 |                 break;
480 |             case OP_I32_DIV_U:
481 |                 instruction.opstring = "i32.div_u";
482 |                 break;
483 |             case OP_I32_REM_S:
484 |                 instruction.opstring = "i32.rem_s";
485 |                 break;
486 |             case OP_I32_REM_U:
487 |                 instruction.opstring = "i32.rem_u";
488 |                 break;
489 |             case OP_I32_AND:
490 |                 instruction.opstring = "i32.and";
491 |                 break;
492 |             case OP_I32_OR:
493 |                 instruction.opstring = "i32.or";
494 |                 break;
495 |             case OP_I32_XOR:
496 |                 instruction.opstring = "i32.xor";
497 |                 break;
498 |             case OP_I32_SHL:
499 |                 instruction.opstring = "i32.shl";
500 |                 break;
501 |             case OP_I32_SHR_S:
502 |                 instruction.opstring = "i32.shr_s";
503 |                 break;
504 |             case OP_I32_SHR_U:
505 |                 instruction.opstring = "i32.shr_u";
506 |                 break;
507 |             case OP_I32_ROTL:
508 |                 instruction.opstring = "i32.rotl";
509 |                 break;
510 |             case OP_I32_ROTR:
511 |                 instruction.opstring = "i32.rotr";
512 |                 break;
513 |             case OP_I64_CLZ:
514 |                 instruction.opstring = "i64.clz";
515 |                 break;
516 |             case OP_I64_CTZ:
517 |                 instruction.opstring = "i64.ctz";
518 |                 break;
519 |             case OP_I64_POPCNT:
520 |                 instruction.opstring = "i64.popcnt";
521 |                 break;
522 |             case OP_I64_ADD:
523 |                 instruction.opstring = "i64.add";
524 |                 break;
525 |             case OP_I64_SUB:
526 |                 instruction.opstring = "i64.sub";
527 |                 break;
528 |             case OP_I64_MUL:
529 |                 instruction.opstring = "i64.mul";
530 |                 break;
531 |             case OP_I64_DIV_S:
532 |                 instruction.opstring = "i64.div_s";
533 |                 break;
534 |             case OP_I64_DIV_U:
535 |                 instruction.opstring = "i64.div_u";
536 |                 break;
537 |             case OP_I64_REM_S:
538 |                 instruction.opstring = "i64.rem_s";
539 |                 break;
540 |             case OP_I64_REM_U:
541 |                 instruction.opstring = "i64.rem_u";
542 |                 break;
543 |             case OP_I64_AND:
544 |                 instruction.opstring = "i64.and";
545 |                 break;
546 |             case OP_I64_OR:
547 |                 instruction.opstring = "i64.or";
548 |                 break;
549 |             case OP_I64_XOR:
550 |                 instruction.opstring = "i64.xor";
551 |                 break;
552 |             case OP_I64_SHL:
553 |                 instruction.opstring = "i64.shl";
554 |                 break;
555 |             case OP_I64_SHR_S:
556 |                 instruction.opstring = "i64.shr_s";
557 |                 break;
558 |             case OP_I64_SHR_U:
559 |                 instruction.opstring = "i64.shr_u";
560 |                 break;
561 |             case OP_I64_ROTL:
562 |                 instruction.opstring = "i64.rotl";
563 |                 break;
564 |             case OP_I64_ROTR:
565 |                 instruction.opstring = "i64.rotr";
566 |                 break;
567 |             case OP_F32_ABS:
568 |                 instruction.opstring = "f32.abs";
569 |                 break;
570 |             case OP_F32_NEG:
571 |                 instruction.opstring = "f32.neg";
572 |                 break;
573 |             case OP_F32_CEIL:
574 |                 instruction.opstring = "f32.ceil";
575 |                 break;
576 |             case OP_F32_FLOOR:
577 |                 instruction.opstring = "f32.floor";
578 |                 break;
579 |             case OP_F32_TRUNC:
580 |                 instruction.opstring = "f32.trunc";
581 |                 break;
582 |             case OP_F32_NEAREST:
583 |                 instruction.opstring = "f32.nearest";
584 |                 break;
585 |             case OP_F32_SQRT:
586 |                 instruction.opstring = "f32.sqrt";
587 |                 break;
588 |             case OP_F32_ADD:
589 |                 instruction.opstring = "f32.add";
590 |                 break;
591 |             case OP_F32_SUB:
592 |                 instruction.opstring = "f32.sub";
593 |                 break;
594 |             case OP_F32_MUL:
595 |                 instruction.opstring = "f32.mul";
596 |                 break;
597 |             case OP_F32_DIV:
598 |                 instruction.opstring = "f32.div";
599 |                 break;
600 |             case OP_F32_MIN:
601 |                 instruction.opstring = "f32.min";
602 |                 break;
603 |             case OP_F32_MAX:
604 |                 instruction.opstring = "f32.max";
605 |                 break;
606 |             case OP_F32_COPYSIGN:
607 |                 instruction.opstring = "f32.copysign";
608 |                 break;
609 |             case OP_F64_ABS:
610 |                 instruction.opstring = "f64.abs";
611 |                 break;
612 |             case OP_F64_NEG:
613 |                 instruction.opstring = "f64.neg";
614 |                 break;
615 |             case OP_F64_CEIL:
616 |                 instruction.opstring = "f64.ceil";
617 |                 break;
618 |             case OP_F64_FLOOR:
619 |                 instruction.opstring = "f64.floor";
620 |                 break;
621 |             case OP_F64_TRUNC:
622 |                 instruction.opstring = "f64.trunc";
623 |                 break;
624 |             case OP_F64_NEAREST:
625 |                 instruction.opstring = "f64.nearest";
626 |                 break;
627 |             case OP_F64_SQRT:
628 |                 instruction.opstring = "f64.sqrt";
629 |                 break;
630 |             case OP_F64_ADD:
631 |                 instruction.opstring = "f64.add";
632 |                 break;
633 |             case OP_F64_SUB:
634 |                 instruction.opstring = "f64.sub";
635 |                 break;
636 |             case OP_F64_MUL:
637 |                 instruction.opstring = "f64.mul";
638 |                 break;
639 |             case OP_F64_DIV:
640 |                 instruction.opstring = "f64.div";
641 |                 break;
642 |             case OP_F64_MIN:
643 |                 instruction.opstring = "f64.min";
644 |                 break;
645 |             case OP_F64_MAX:
646 |                 instruction.opstring = "f64.max";
647 |                 break;
648 |             case OP_F64_COPYSIGN:
649 |                 instruction.opstring = "f64.copysign";
650 |                 break;
651 |             case OP_I32_WRAP_I64:
652 |                 instruction.opstring = "i32.wrap/i64";
653 |                 break;
654 |             case OP_I32_TRUNC_S_F32:
655 |                 instruction.opstring = "i32.trunc_s/f32";
656 |                 break;
657 |             case OP_I32_TRUNC_U_F32:
658 |                 instruction.opstring = "i32.trunc_u/f32";
659 |                 break;
660 |             case OP_I32_TRUNC_S_F64:
661 |                 instruction.opstring = "i32.trunc_s/f64";
662 |                 break;
663 |             case OP_I32_TRUNC_U_F64:
664 |                 instruction.opstring = "i32.trunc_u/f64";
665 |                 break;
666 |             case OP_I64_EXTEND_S_I32:
667 |                 instruction.opstring = "i64.extend_s/i32";
668 |                 break;
669 |             case OP_I64_EXTEND_U_I32:
670 |                 instruction.opstring = "i64.extend_u/i32";
671 |                 break;
672 |             case OP_I64_TRUNC_S_F32:
673 |                 instruction.opstring = "i64.trunc_s/f32";
674 |                 break;
675 |             case OP_I64_TRUNC_U_F32:
676 |                 instruction.opstring = "i64.trunc_u/f32";
677 |                 break;
678 |             case OP_I64_TRUNC_S_F64:
679 |                 instruction.opstring = "i64.trunc_s/f64";
680 |                 break;
681 |             case OP_I64_TRUNC_U_F64:
682 |                 instruction.opstring = "i64.trunc_u/f64";
683 |                 break;
684 |             case OP_F32_CONVERT_S_I32:
685 |                 instruction.opstring = "f32.convert_s/i32";
686 |                 break;
687 |             case OP_F32_CONVERT_U_I32:
688 |                 instruction.opstring = "f32.convert_u/i32";
689 |                 break;
690 |             case OP_F32_CONVERT_S_I64:
691 |                 instruction.opstring = "f32.convert_s/i64";
692 |                 break;
693 |             case OP_F32_CONVERT_U_I64:
694 |                 instruction.opstring = "f32.convert_u/i64";
695 |                 break;
696 |             case OP_F32_DEMOTE_F64:
697 |                 instruction.opstring = "f32.demote/f64";
698 |                 break;
699 |             case OP_F64_CONVERT_S_I32:
700 |                 instruction.opstring = "f64.convert_s/i32";
701 |                 break;
702 |             case OP_F64_CONVERT_U_I32:
703 |                 instruction.opstring = "f64.convert_u/i32";
704 |                 break;
705 |             case OP_F64_CONVERT_S_I64:
706 |                 instruction.opstring = "f64.convert_s/i64";
707 |                 break;
708 |             case OP_F64_CONVERT_U_I64:
709 |                 instruction.opstring = "f64.convert_u/i64";
710 |                 break;
711 |             case OP_F64_PROMOTE_F32:
712 |                 instruction.opstring = "f64.promote/f32";
713 |                 break;
714 |             case OP_I32_REINTERPRET_F32:
715 |                 instruction.opstring = "i32.reinterpret/f32";
716 |                 break;
717 |             case OP_I64_REINTERPRET_F64:
718 |                 instruction.opstring = "i64.reinterpret/f64";
719 |                 break;
720 |             case OP_F32_REINTERPRET_I32:
721 |                 instruction.opstring = "f32.reinterpret/i32";
722 |                 break;
723 |             case OP_F64_REINTERPRET_I64:
724 |                 instruction.opstring = "f64.reinterpret/i64";
725 |                 break;
726 |             case OP_I32_EXTEND8_S:
727 |                 instruction.opstring = "i32.extend8_s";
728 |                 break;
729 |             case OP_I32_EXTEND16_S:
730 |                 instruction.opstring = "i32.extend16_s";
731 |                 break;
732 |             case OP_I64_EXTEND8_S:
733 |                 instruction.opstring = "i64.extend8_s";
734 |                 break;
735 |             case OP_I64_EXTEND16_S:
736 |                 instruction.opstring = "i64.extend16_s";
737 |                 break;
738 |             case OP_I64_EXTEND32_S:
739 |                 instruction.opstring = "i64.extend32_s";
740 |                 break;
741 |             // TODO Handle BULK_MEMORY, SIMD, and ATOMIC instructions
742 |             default:
743 |                 throw "Invalid/unknown opcode " + instruction.opcode;
744 |         }
745 | 
746 |         return instruction;
747 |     }
748 | };
749 | 


--------------------------------------------------------------------------------