using System; using System.Collections.Generic; using System.IO; using System.Runtime.InteropServices.JavaScript; using System.Runtime.Versioning; using System.Threading.Tasks; // Required for the [JSImport]/[JSExport] source generator to compile. [assembly: SupportedOSPlatform("browser")] namespace Glass.WebGL2 { /// /// The browser's local storage, which keeps text for the site across visits and holds only a few megabytes. Only /// keys that start with a given prefix are visible, so one page can keep several separate stores. /// public sealed partial class WebGL2LocalStorage : IKeyValueStore { private static Task loadTask; private static partial class Interop { [JSImport("getKeys", "glass-webgl2")] [return: JSMarshalAs>] public static partial string[] GetKeys(); [JSImport("get", "glass-webgl2")] public static partial string Get(string key); [JSImport("set", "glass-webgl2")] public static partial bool Set(string key, string value); [JSImport("remove", "glass-webgl2")] public static partial void Remove(string key); } private readonly string keyPrefix; /// /// Must be awaited exactly once, from the browser host's own startup code, before any store is used. /// JSHost.ImportAsync can't be awaited synchronously, since nothing can pump the browser's microtask queue /// while the single WASM thread is blocked on it. /// public static Task EnsureLoadedAsync() { return loadTask ??= JSHost.ImportAsync("glass-webgl2", "../Glass.WebGL2.js"); } /// /// Makes a store over the local storage keys that start with . An empty prefix /// covers all of them. /// public WebGL2LocalStorage(string keyPrefix) { this.keyPrefix = keyPrefix ?? throw new ArgumentNullException(nameof(keyPrefix)); } public IReadOnlyList GetKeys() { var returnMe = new List(); foreach (string key in Interop.GetKeys()) { if (key.StartsWith(keyPrefix, StringComparison.Ordinal)) { returnMe.Add(key.Substring(keyPrefix.Length)); } } return returnMe; } public string Get(string key) { if (key == null) { throw new ArgumentNullException(nameof(key)); } return Interop.Get(keyPrefix + key); } public void Set(string key, string value) { if (key == null) { throw new ArgumentNullException(nameof(key)); } if (value == null) { throw new ArgumentNullException(nameof(value)); } if (!Interop.Set(keyPrefix + key, value)) { throw new IOException("The browser's storage is full or turned off."); } } public void Remove(string key) { if (key == null) { throw new ArgumentNullException(nameof(key)); } Interop.Remove(keyPrefix + key); } } }