From: stephan Date: Sat, 28 May 2022 11:29:00 +0000 (+0000) Subject: fiddle: corrected a piece of far-corner-case error handling and made the various... X-Git-Tag: version-3.39.0~83 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=e9ab04a4469e96d3b4cce233972487d3a9a11490;p=thirdparty%2Fsqlite.git fiddle: corrected a piece of far-corner-case error handling and made the various checkbox config options persistent. FossilOrigin-Name: 2ba429a4f8300b981b23d54c2bdb54bd4863522c1c18bf9a67a82e3dce845b10 --- diff --git a/ext/fiddle/fiddle-worker.js b/ext/fiddle/fiddle-worker.js index 302452f1c3..e71955a8a8 100644 --- a/ext/fiddle/fiddle-worker.js +++ b/ext/fiddle/fiddle-worker.js @@ -115,10 +115,8 @@ stderr("Restarting the app requires reloading the page."); wMsg('error', err); } - fiddleModule.setStatus('Exception thrown, see JavaScript console:',text); - /*fiddleModule.setStatus = function(text) { - console.error('[post-exception status]', text); - };*/ + console.error(err); + fiddleModule.setStatus('Exception thrown, see JavaScript console: '+err); }; const Sqlite3Shell = { diff --git a/ext/fiddle/fiddle.js b/ext/fiddle/fiddle.js index d2ad7091cd..98caa6b3c2 100644 --- a/ext/fiddle/fiddle.js +++ b/ext/fiddle/fiddle.js @@ -16,10 +16,184 @@ */ (function(){ 'use strict'; - /* Recall that the 'self' symbol, except where locally overwritten, refers to the global window or worker object. */ + const storage = (function(NS/*namespace object in which to store this module*/){ + /* Pedantic licensing note: this code originated in the Fossil SCM + source tree, where it has a different license, but the person who + ported it into sqlite is the same one who wrote it for fossil. */ + 'use strict'; + NS = NS||{}; + + /** + This module provides a basic wrapper around localStorage + or sessionStorage or a dummy proxy object if neither + of those are available. + */ + const tryStorage = function f(obj){ + if(!f.key) f.key = 'storage.access.check'; + try{ + obj.setItem(f.key, 'f'); + const x = obj.getItem(f.key); + obj.removeItem(f.key); + if(x!=='f') throw new Error(f.key+" failed") + return obj; + }catch(e){ + return undefined; + } + }; + + /** Internal storage impl for this module. */ + const $storage = + tryStorage(window.localStorage) + || tryStorage(window.sessionStorage) + || tryStorage({ + // A basic dummy xyzStorage stand-in + $$$:{}, + setItem: function(k,v){this.$$$[k]=v}, + getItem: function(k){ + return this.$$$.hasOwnProperty(k) ? this.$$$[k] : undefined; + }, + removeItem: function(k){delete this.$$$[k]}, + clear: function(){this.$$$={}} + }); + + /** + For the dummy storage we need to differentiate between + $storage and its real property storage for hasOwnProperty() + to work properly... + */ + const $storageHolder = $storage.hasOwnProperty('$$$') ? $storage.$$$ : $storage; + + /** + A prefix which gets internally applied to all storage module + property keys so that localStorage and sessionStorage across the + same browser profile instance do not "leak" across multiple apps + being hosted by the same origin server. Such cross-polination is + still there but, with this key prefix applied, it won't be + immediately visible via the storage API. + + With this in place we can justify using localStorage instead of + sessionStorage. + + One implication of using localStorage and sessionStorage is that + their scope (the same "origin" and client application/profile) + allows multiple apps on the same origin to use the same + storage. Thus /appA/foo could then see changes made via + /appB/foo. The data do not cross user- or browser boundaries, + though, so it "might" arguably be called a + feature. storageKeyPrefix was added so that we can sandbox that + state for each separate app which shares an origin. + + See: https://fossil-scm.org/forum/forumpost/4afc4d34de + + Sidebar: it might seem odd to provide a key prefix and stick all + properties in the topmost level of the storage object. We do that + because adding a layer of object to sandbox each app would mean + (de)serializing that whole tree on every storage property change. + e.g. instead of storageObject.projectName.foo we have + storageObject[storageKeyPrefix+'foo']. That's soley for + efficiency's sake (in terms of battery life and + environment-internal storage-level effort). + */ + const storageKeyPrefix = ( + $storageHolder===$storage/*localStorage or sessionStorage*/ + ? ( + (NS.config ? + (NS.config.projectCode || NS.config.projectName + || NS.config.shortProjectName) + : false) + || window.location.pathname + )+'::' : ( + '' /* transient storage */ + ) + ); + + /** + A proxy for localStorage or sessionStorage or a + page-instance-local proxy, if neither one is availble. + + Which exact storage implementation is uses is unspecified, and + apps must not rely on it. + */ + NS.storage = { + storageKeyPrefix: storageKeyPrefix, + /** Sets the storage key k to value v, implicitly converting + it to a string. */ + set: (k,v)=>$storage.setItem(storageKeyPrefix+k,v), + /** Sets storage key k to JSON.stringify(v). */ + setJSON: (k,v)=>$storage.setItem(storageKeyPrefix+k,JSON.stringify(v)), + /** Returns the value for the given storage key, or + dflt if the key is not found in the storage. */ + get: (k,dflt)=>$storageHolder.hasOwnProperty( + storageKeyPrefix+k + ) ? $storage.getItem(storageKeyPrefix+k) : dflt, + /** Returns true if the given key has a value of "true". If the + key is not found, it returns true if the boolean value of dflt + is "true". (Remember that JS persistent storage values are all + strings.) */ + getBool: function(k,dflt){ + return 'true'===this.get(k,''+(!!dflt)); + }, + /** Returns the JSON.parse()'d value of the given + storage key's value, or dflt is the key is not + found or JSON.parse() fails. */ + getJSON: function f(k,dflt){ + try { + const x = this.get(k,f); + return x===f ? dflt : JSON.parse(x); + } + catch(e){return dflt} + }, + /** Returns true if the storage contains the given key, + else false. */ + contains: (k)=>$storageHolder.hasOwnProperty(storageKeyPrefix+k), + /** Removes the given key from the storage. Returns this. */ + remove: function(k){ + $storage.removeItem(storageKeyPrefix+k); + return this; + }, + /** Clears ALL keys from the storage. Returns this. */ + clear: function(){ + this.keys().forEach((k)=>$storage.removeItem(/*w/o prefix*/k)); + return this; + }, + /** Returns an array of all keys currently in the storage. */ + keys: ()=>Object.keys($storageHolder).filter((v)=>(v||'').startsWith(storageKeyPrefix)), + /** Returns true if this storage is transient (only available + until the page is reloaded), indicating that fileStorage + and sessionStorage are unavailable. */ + isTransient: ()=>$storageHolder!==$storage, + /** Returns a symbolic name for the current storage mechanism. */ + storageImplName: function(){ + if($storage===window.localStorage) return 'localStorage'; + else if($storage===window.sessionStorage) return 'sessionStorage'; + else return 'transient'; + }, + + /** + Returns a brief help text string for the currently-selected + storage type. + */ + storageHelpDescription: function(){ + return { + localStorage: "Browser-local persistent storage with an "+ + "unspecified long-term lifetime (survives closing the browser, "+ + "but maybe not a browser upgrade).", + sessionStorage: "Storage local to this browser tab, "+ + "lost if this tab is closed.", + "transient": "Transient storage local to this invocation of this page." + }[this.storageImplName()]; + } + }; + return NS.storage; + })({})/*storage API setup*/; + + + /** Name of the stored copy of SqliteFiddle.config. */ + const configStorageKey = 'fiddle-config'; + /** The SqliteFiddle object is intended to be the primary app-level object for the main-thread side of the sqlite @@ -130,9 +304,27 @@ this.wMsg('db-reset'); } return this; + }, + storeConfig: function(){ + storage.setJSON(configStorageKey,this.config); } }; + if(1){ /* set up SF.config */ + const storedConfig = storage.getJSON(configStorageKey); + if(storedConfig){ + /* Copy all properties to SF.config which are currently in + storedConfig. We don't bother copying any other + properties: those have been removed from the app in the + meantime. */ + Object.keys(SF.config).forEach(function(k){ + if(storedConfig.hasOwnProperty(k)){ + SF.config[k] = storedConfig[k]; + } + }); + } + } + SF.worker = new Worker('fiddle-worker.js'); SF.worker.onmessage = (ev)=>SF.runMsgHandlers(ev.data); SF.addMsgHandler(['stdout', 'stderr'], (ev)=>SF.echo(ev.data)); @@ -316,6 +508,7 @@ } e.addEventListener('change', function(){ SF.config[this.dataset.config] = this.checked; + SF.storeConfig(); }, false); }); /* For each button with data-cmd=X, map a click handler which diff --git a/manifest b/manifest index f69de28b39..f2d22cea59 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Mark\san\salways-true\sconditional\sas\sALWAYS(). -D 2022-05-27T18:06:49.146 +C fiddle:\scorrected\sa\spiece\sof\sfar-corner-case\serror\shandling\sand\smade\sthe\svarious\scheckbox\sconfig\soptions\spersistent. +D 2022-05-28T11:29:00.497 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -61,9 +61,9 @@ F ext/fiddle/EXPORTED_RUNTIME_METHODS b831017ba67ba993b34a27400cef2f6095bd6789c0 F ext/fiddle/Makefile de65d04bfb312e94dbd7a0e7d99fb126f0abc1db62f920159c4124b5a42347d8 F ext/fiddle/SqliteTestUtil.js 559731c3e8e0de330ec7d292e6c1846566408caee6637acc8a119ac338a8781c F ext/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f -F ext/fiddle/fiddle-worker.js e3e8c7af2d6d10d915c8d6b5c88bd48587afbfdab7cb76858829c3b83e83a810 +F ext/fiddle/fiddle-worker.js 3a19253dc026d1ad9064ee853f3c4da3385223ce4434dab1838837525d817371 F ext/fiddle/fiddle.html 724f1cd4126616bc87f5871f78d3f7aaaf41e45c9728724627baab87e6af35f0 -F ext/fiddle/fiddle.js 700df283e8c6e87519aba6839e1aa99d040b39ea545253cbe79da18e24af256a +F ext/fiddle/fiddle.js 503524eb132716c6c4777a1a2352a9bfc6d391b38de8d050f22f73a5df5993b1 F ext/fiddle/index.md d9c1c308d8074341bc3b11d1d39073cd77754cb3ca9aeb949f23fdd8323d81cf F ext/fiddle/sqlite3-api.js 8500698d2163f4a25f8e5e6810ad826487342579d6a321d82b244dbc8e6f6db6 F ext/fiddle/testing.css 750572dded671d2cf142bbcb27af5542522ac08db128245d0b9fe410aa1d7f2a @@ -1970,8 +1970,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 01beb0365c529481605f1864b1b6760e2484fad08d56a72e00e34acff37e23f8 -R 01dd885cb493be38067a73be4179a0ab -U drh -Z 416cd8c7c7dc19c9e93e8237f3081073 +P 3492fe8a212cbe02b9016866e2499b99c3b566a4b0bc91fba267e6e1fe1b8943 +R a18f2e003119227b8304b945c745c3c1 +U stephan +Z aed9dc05a3c9ef7bf20957969efcb8af # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index d26e133969..3cb0668914 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -3492fe8a212cbe02b9016866e2499b99c3b566a4b0bc91fba267e6e1fe1b8943 \ No newline at end of file +2ba429a4f8300b981b23d54c2bdb54bd4863522c1c18bf9a67a82e3dce845b10 \ No newline at end of file