]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
fiddle: corrected a piece of far-corner-case error handling and made the various...
authorstephan <stephan@noemail.net>
Sat, 28 May 2022 11:29:00 +0000 (11:29 +0000)
committerstephan <stephan@noemail.net>
Sat, 28 May 2022 11:29:00 +0000 (11:29 +0000)
FossilOrigin-Name: 2ba429a4f8300b981b23d54c2bdb54bd4863522c1c18bf9a67a82e3dce845b10

ext/fiddle/fiddle-worker.js
ext/fiddle/fiddle.js
manifest
manifest.uuid

index 302452f1c341dd113ced0a5d5a96c44b55d7c9c1..e71955a8a8dd2c52a1c8c200d5eb8b826f13f973 100644 (file)
             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 = {
index d2ad7091cd7c0a746e31e818e21dcb94fc0393c6..98caa6b3c2836e2e2ebaaf35ccf95bd9b6375cc7 100644 (file)
 */
 (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
                 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));
                 }
                 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
index f69de28b398084878e1b96bf53f4a6347ea44b6c..f2d22cea59fa1e8979ea5531ddf5a226b7748b3e 100644 (file)
--- 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.
index d26e1339692512202f069b33bf2389e94c4d7f9c..3cb0668914381759b4d9a29a6379d6f45c7d2fd6 100644 (file)
@@ -1 +1 @@
-3492fe8a212cbe02b9016866e2499b99c3b566a4b0bc91fba267e6e1fe1b8943
\ No newline at end of file
+2ba429a4f8300b981b23d54c2bdb54bd4863522c1c18bf9a67a82e3dce845b10
\ No newline at end of file