pub const HOST_API_BRIDGE_SCRIPT: &str = "\n(function() {\n \'use strict\';\n if (window.__hostApiBridge) { return; }\n window.__hostApiBridge = true;\n window.__HOST_WEBVIEW_MARK__ = true;\n var ch = new MessageChannel();\n window.__HOST_API_PORT__ = ch.port2;\n ch.port2.start();\n var port1 = ch.port1;\n port1.start();\n if (!window.host) { window.host = {}; }\n if (!window.host.storage) { window.host.storage = {}; }\n port1.onmessage = function(ev) {\n var data = ev.data;\n if (!data) { console.warn(\'[host-bridge] data is falsy, dropping\'); return; }\n var bytes;\n if (data instanceof Uint8Array) { bytes = data; }\n else if (data instanceof ArrayBuffer) { bytes = new Uint8Array(data); }\n else if (ArrayBuffer.isView(data)) { bytes = new Uint8Array(data.buffer, data.byteOffset, data.byteLength); }\n else { console.warn(\'[host-bridge] unknown data type: \' + typeof data + \' constructor=\' + (data.constructor ? data.constructor.name : \'?\') + \', dropping\'); return; }\n var binary = \'\';\n for (var i = 0; i < bytes.length; i++) { binary += String.fromCharCode(bytes[i]); }\n try {\n window.webkit.messageHandlers.hostApi.postMessage(btoa(binary));\n } catch(e) {\n console.error(\'[host-bridge] postMessage to hostApi FAILED:\', e.message);\n }\n };\n window.__hostApiReply = function(b64) {\n try {\n var binary = atob(b64);\n var bytes = new Uint8Array(binary.length);\n for (var i = 0; i < binary.length; i++) { bytes[i] = binary.charCodeAt(i); }\n port1.postMessage(bytes);\n } catch(e) { console.error(\'[host-bridge] reply failed:\', e.message); }\n };\n // --- Storage bridge (shared between native and wry bridges) ---\n var storagePending = new Map();\n var nextStorageCallId = 1;\n var storageTextEncoder = typeof TextEncoder !== \'undefined\' ? new TextEncoder() : null;\n var storageTextDecoder = typeof TextDecoder !== \'undefined\' ? new TextDecoder() : null;\n\n function makeBridgeError(code, message) {\n var error = new Error(message);\n error.code = code;\n return error;\n }\n\n function toUint8Array(data) {\n if (data instanceof Uint8Array) return data;\n if (data instanceof ArrayBuffer) return new Uint8Array(data);\n if (ArrayBuffer.isView(data))\n return new Uint8Array(data.buffer, data.byteOffset, data.byteLength);\n return null;\n }\n\n function concatBytes(parts) {\n var total = 0;\n for (var i = 0; i < parts.length; i++) total += parts[i].length;\n var out = new Uint8Array(total);\n var offset = 0;\n for (var j = 0; j < parts.length; j++) {\n out.set(parts[j], offset);\n offset += parts[j].length;\n }\n return out;\n }\n\n function encodeCompactU32(value) {\n if (value < 0x40) return new Uint8Array([(value << 2) & 0xff]);\n if (value < 0x4000) {\n var twoByte = (value << 2) | 0x01;\n return new Uint8Array([twoByte & 0xff, (twoByte >>> 8) & 0xff]);\n }\n if (value < 0x40000000) {\n var fourByte = ((value << 2) | 0x02) >>> 0;\n return new Uint8Array([\n fourByte & 0xff,\n (fourByte >>> 8) & 0xff,\n (fourByte >>> 16) & 0xff,\n (fourByte >>> 24) & 0xff\n ]);\n }\n return new Uint8Array([\n 0x03,\n value & 0xff,\n (value >>> 8) & 0xff,\n (value >>> 16) & 0xff,\n (value >>> 24) & 0xff\n ]);\n }\n\n function readCompactU32(bytes, offset) {\n var first = bytes[offset++];\n switch (first & 0x03) {\n case 0x00:\n return { value: first >> 2, offset: offset };\n case 0x01:\n if (offset >= bytes.length) throw makeBridgeError(\'ERR_INVALID_ARGUMENT\', \'unexpected end of compact integer\');\n return { value: ((first | (bytes[offset++] << 8)) >> 2) & 0x3fff, offset: offset };\n case 0x02:\n if (offset + 2 >= bytes.length) throw makeBridgeError(\'ERR_INVALID_ARGUMENT\', \'unexpected end of compact integer\');\n var value = (first | (bytes[offset] << 8) | (bytes[offset + 1] << 16) | (bytes[offset + 2] << 24)) >>> 0;\n offset += 3;\n return { value: value >>> 2, offset: offset };\n default:\n var byteCount = (first >> 2) + 4;\n if (byteCount > 4 || offset + byteCount > bytes.length) {\n throw makeBridgeError(\'ERR_INVALID_ARGUMENT\', \'compact integer exceeds bridge limit\');\n }\n var large = 0;\n for (var i = 0; i < byteCount; i++) {\n large |= bytes[offset++] << (i * 8);\n }\n return { value: large >>> 0, offset: offset };\n }\n }\n\n function encodeStringValue(value) {\n if (!storageTextEncoder) {\n throw makeBridgeError(\'ERR_HOST_RUNTIME_UNAVAILABLE\', \'TextEncoder is unavailable\');\n }\n var bytes = storageTextEncoder.encode(String(value));\n return concatBytes([encodeCompactU32(bytes.length), bytes]);\n }\n\n function encodeVarBytes(bytes) {\n return concatBytes([encodeCompactU32(bytes.length), bytes]);\n }\n\n function readStringValue(bytes, offset) {\n if (!storageTextDecoder) {\n throw makeBridgeError(\'ERR_HOST_RUNTIME_UNAVAILABLE\', \'TextDecoder is unavailable\');\n }\n var length = readCompactU32(bytes, offset);\n offset = length.offset;\n if (offset + length.value > bytes.length) {\n throw makeBridgeError(\'ERR_INVALID_ARGUMENT\', \'unexpected end of string payload\');\n }\n var value = storageTextDecoder.decode(bytes.slice(offset, offset + length.value));\n return { value: value, offset: offset + length.value };\n }\n\n function readVarBytes(bytes, offset) {\n var length = readCompactU32(bytes, offset);\n offset = length.offset;\n if (offset + length.value > bytes.length) {\n throw makeBridgeError(\'ERR_INVALID_ARGUMENT\', \'unexpected end of byte payload\');\n }\n return { value: bytes.slice(offset, offset + length.value), offset: offset + length.value };\n }\n\n function buildStorageRequest(tag, requestId, key, valueBytes) {\n var parts = [encodeStringValue(requestId), new Uint8Array([tag, 0]), encodeStringValue(key)];\n if (valueBytes) parts.push(encodeVarBytes(valueBytes));\n return concatBytes(parts);\n }\n\n function decodeStorageResponse(bytes, requestId) {\n var cursor = readStringValue(bytes, 0);\n if (cursor.value !== requestId) {\n throw makeBridgeError(\'ERR_INVALID_ARGUMENT\', \'host storage response requestId mismatch\');\n }\n var offset = cursor.offset;\n if (offset + 3 > bytes.length) {\n throw makeBridgeError(\'ERR_INVALID_ARGUMENT\', \'host storage response is truncated\');\n }\n var tag = bytes[offset++];\n offset += 1; // v1\n var resultTag = bytes[offset++];\n if (resultTag !== 0) {\n var reason = \'host storage request failed\';\n try {\n offset += 1; // skip error variant tag\n var reasonResult = readStringValue(bytes, offset);\n if (reasonResult.value) reason = reasonResult.value;\n } catch (e) { /* use default if reason cannot be decoded */ }\n throw makeBridgeError(\'ERR_HOST_REJECTED\', reason);\n }\n if (tag === 13) {\n if (offset >= bytes.length) {\n throw makeBridgeError(\'ERR_INVALID_ARGUMENT\', \'host storage read response is truncated\');\n }\n var optionTag = bytes[offset++];\n if (optionTag === 0) return null;\n if (optionTag !== 1) {\n throw makeBridgeError(\'ERR_INVALID_ARGUMENT\', \'invalid host storage option tag\');\n }\n var data = readVarBytes(bytes, offset);\n if (!storageTextDecoder) {\n throw makeBridgeError(\'ERR_HOST_RUNTIME_UNAVAILABLE\', \'TextDecoder is unavailable\');\n }\n return storageTextDecoder.decode(data.value);\n }\n if (tag === 15 || tag === 17) return true;\n throw makeBridgeError(\'ERR_INVALID_ARGUMENT\', \'unexpected host storage response tag: \' + tag);\n }\n\n function sendStorageRequest(tag, key, valueBytes) {\n return new Promise(function(resolve, reject) {\n if (!window.__HOST_API_PORT__) {\n reject(makeBridgeError(\'ERR_BINARY_BRIDGE_UNAVAILABLE\', \'Binary host-api bridge is unavailable\'));\n return;\n }\n var requestId = \'host-storage-\' + (nextStorageCallId++);\n storagePending.set(requestId, { resolve: resolve, reject: reject });\n try {\n window.__HOST_API_PORT__.postMessage(buildStorageRequest(tag, requestId, key, valueBytes));\n } catch (e) {\n storagePending.delete(requestId);\n reject(e);\n }\n });\n }\n\n function handleStorageMessage(ev) {\n if (storagePending.size === 0) return;\n var bytes = toUint8Array(ev.data);\n if (!bytes) return;\n var requestId;\n try {\n requestId = readStringValue(bytes, 0).value;\n } catch (e) {\n return;\n }\n var entry = storagePending.get(requestId);\n if (!entry) return;\n storagePending.delete(requestId);\n try {\n entry.resolve(decodeStorageResponse(bytes, requestId));\n } catch (e) {\n entry.reject(e);\n }\n }\n\n if (window.__HOST_API_PORT__.addEventListener) {\n window.__HOST_API_PORT__.addEventListener(\'message\', handleStorageMessage);\n }\n\n if (typeof window.host.storage.get !== \'function\') {\n window.host.storage.get = function(key) {\n return sendStorageRequest(12, key);\n };\n }\n if (typeof window.host.storage.set !== \'function\') {\n window.host.storage.set = function(key, value) {\n if (!storageTextEncoder) {\n return Promise.reject(makeBridgeError(\'ERR_HOST_RUNTIME_UNAVAILABLE\', \'TextEncoder is unavailable\'));\n }\n return sendStorageRequest(14, key, storageTextEncoder.encode(String(value)));\n };\n }\n if (typeof window.host.storage.remove !== \'function\') {\n window.host.storage.remove = function(key) {\n return sendStorageRequest(16, key);\n };\n }\n\n})();\n";Expand description
JavaScript injected before the Polkadot app loads. Sets up:
window.__HOST_WEBVIEW_MARK__ = true— SDK webview detectionMessageChannelwith port2 aswindow.__HOST_API_PORT__- Binary message forwarding between port1 and native (base64)