|
| 1 | +import React from 'react' |
| 2 | + |
| 3 | +const { ObjectInspector } = require("./common"); |
| 4 | +const { extensionStorageSync } = ChromeUtils.importESModule( "resource://gre/modules/ExtensionStorageSync.sys.mjs"); |
| 5 | + |
| 6 | +export class WebExtStorage extends React.Component { |
| 7 | + constructor(props) { |
| 8 | + super(props); |
| 9 | + // We use 'about-sync' as an ID for this addon/scratch data. It will sync/merge etc. |
| 10 | + // Could use the addon-manager to turn ids into names? |
| 11 | + let ext_ids = new Set(["about-sync"].concat(props.serverRecords.filter(s => !s.deleted).map(s => s.extId))); |
| 12 | + this.state = {id: "about-sync", ext_ids, data: null, keys: "", error_get: "", set_error: ""}; |
| 13 | + } |
| 14 | + |
| 15 | + async update(state = {}) { |
| 16 | + // simple "truthy" doesn't keep an empty string. |
| 17 | + let keys_text = state.keys === undefined ? this.state.keys : state.keys; |
| 18 | + let id = state.id || this.state.id; |
| 19 | + let error_get = ""; |
| 20 | + console.log("webext: update", state); |
| 21 | + let keys = null; |
| 22 | + if (keys_text) { |
| 23 | + try { |
| 24 | + keys = JSON.parse(keys_text); |
| 25 | + } catch (e) { |
| 26 | + error_get = "Invalid keys: not JSON"; |
| 27 | + } |
| 28 | + } |
| 29 | + let data = null, bytesInUse = null; |
| 30 | + if (!error_get) { |
| 31 | + try { |
| 32 | + bytesInUse = await extensionStorageSync.getBytesInUse({id}, keys); |
| 33 | + data = await extensionStorageSync.get({id}, keys); |
| 34 | + } catch (e) { |
| 35 | + error_get = `Failed: ${e}`; |
| 36 | + } |
| 37 | + } |
| 38 | + this.setState({data, bytesInUse, error_get, ...state}); |
| 39 | + } |
| 40 | + |
| 41 | + async doSet() { |
| 42 | + let value; |
| 43 | + if (!this.state.toSet) { |
| 44 | + this.setState({ error_change: "No value to set" }); |
| 45 | + return; |
| 46 | + } |
| 47 | + try { |
| 48 | + value = JSON.parse(this.state.toSet) |
| 49 | + } catch (e) { |
| 50 | + this.setState({ error_change: "Invalid value" }); |
| 51 | + return; |
| 52 | + } |
| 53 | + let ext = { id: this.state.id } |
| 54 | + console.log("webext: setting", ext, value); |
| 55 | + try { |
| 56 | + await extensionStorageSync.set(ext, value); |
| 57 | + } catch (e) { |
| 58 | + console.error("webext: Failed to set extension storage", e); |
| 59 | + this.setState({error_change: `Failed to set: ${e}`}); |
| 60 | + return; |
| 61 | + } |
| 62 | + await this.update(); |
| 63 | + this.setState({error_change: ""}); |
| 64 | + } |
| 65 | + |
| 66 | + async doClear() { |
| 67 | + let ext = { id: this.state.id } |
| 68 | + console.log("webext: clearing", ext); |
| 69 | + await extensionStorageSync.clear(ext); |
| 70 | + await this.update(); |
| 71 | + this.setState({error_change: ""}); |
| 72 | + } |
| 73 | + |
| 74 | + async componentDidMount() { |
| 75 | + await this.update(); |
| 76 | + } |
| 77 | + |
| 78 | + render() { |
| 79 | + // The addon <select> element children. |
| 80 | + let extensions = []; // WTF can't I work out how to use .map on a set!? |
| 81 | + for (let id of this.state.ext_ids) { |
| 82 | + extensions.push(<option value={id}>{id}</option>); |
| 83 | + } |
| 84 | + let get = |
| 85 | + <fieldset> |
| 86 | + <legend>Current Data</legend> |
| 87 | + <div className="horiz-action-row"> |
| 88 | + <p>Keys to query</p> |
| 89 | + <textarea className="storage-input" |
| 90 | + value={this.state.keys} |
| 91 | + onChange={e => this.update({keys: e.target.value})} |
| 92 | + placeholder="String, array of strings, or leave empty for null" |
| 93 | + /> |
| 94 | + </div> |
| 95 | + <div> |
| 96 | + {this.state.error_get ? |
| 97 | + <p>{ this.state.error_get }</p> |
| 98 | + : |
| 99 | + <> |
| 100 | + <p>Current data ({this.state.bytesInUse} bytes)</p> |
| 101 | + <ObjectInspector data={this.state.data} expandLevel={1}/> |
| 102 | + </> |
| 103 | + } |
| 104 | + </div> |
| 105 | + </fieldset>; |
| 106 | + |
| 107 | + let change = |
| 108 | + <fieldset> |
| 109 | + <legend>Change Data</legend> |
| 110 | + {this.state.id != "about-sync" && |
| 111 | + <p className="storage-sync-ext-warning">You are messing with a real extension - you might break it!</p> |
| 112 | + } |
| 113 | + <div className="horiz-action-row"> |
| 114 | + <span>Clear all data</span> |
| 115 | + <button onClick={e => this.doClear()}>Clear</button> |
| 116 | + </div> |
| 117 | + <div className="horiz-action-row"> |
| 118 | + <span>Set data</span> |
| 119 | + <textarea |
| 120 | + className="storage-input" |
| 121 | + value={this.state.set_value} |
| 122 | + onChange={e => this.setState({toSet: e.target.value})} |
| 123 | + onAc |
| 124 | + placeholder="A JSON object, eg, {'foo': 'bar'}" |
| 125 | + /> |
| 126 | + <div> |
| 127 | + <button onClick={e => this.doSet()}>Set</button> |
| 128 | + </div> |
| 129 | + </div> |
| 130 | + <div> |
| 131 | + {this.state.error_change && <p className='error-message'>{this.state.error_change}</p>} |
| 132 | + </div> |
| 133 | + </fieldset>; |
| 134 | + |
| 135 | + return ( |
| 136 | + <> |
| 137 | + <select |
| 138 | + children={extensions} |
| 139 | + value={this.state.id} |
| 140 | + onChange={e => this.update({id: e.target.value })} |
| 141 | + /> |
| 142 | + <div className="storage-sync-sections"> |
| 143 | + {get} |
| 144 | + {change} |
| 145 | + </div> |
| 146 | + </> |
| 147 | + ); |
| 148 | + } |
| 149 | +} |
0 commit comments