From a645ac4653b254ac48b7d244ebf192e7a230cf82 Mon Sep 17 00:00:00 2001 From: David Reed Date: Fri, 24 Feb 2023 22:47:40 -0500 Subject: [PATCH] add idb api --- src/client/hook.js | 24 ++++++++++++++++++ src/client/idb.js | 62 +++++++++++++++++++++++++++++++++++++++++++++ src/client/index.js | 26 ++++++++++++++++++- src/uv.handler.js | 13 ++++++++++ 4 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 src/client/idb.js diff --git a/src/client/hook.js b/src/client/hook.js index 5b9539a..b89876c 100644 --- a/src/client/hook.js +++ b/src/client/hook.js @@ -1,11 +1,35 @@ +/** + * + * @template Data + * @template Target + * @template That + * @property {Data} data + * @property {Target} target + * @property {That} that + */ class HookEvent { #intercepted; #returnValue; + /** + * + * @param {Data} data + * @param {Target} target + * @param {That} that + */ constructor(data = {}, target = null, that = null) { this.#intercepted = false; this.#returnValue = null; + /** + * @type {Data} + */ this.data = data; + /** + * @type {Target} + */ this.target = target; + /** + * @type {That} + */ this.that = that; } get intercepted() { diff --git a/src/client/idb.js b/src/client/idb.js new file mode 100644 index 0000000..3b2cfb9 --- /dev/null +++ b/src/client/idb.js @@ -0,0 +1,62 @@ +import EventEmitter from 'events'; +import HookEvent from './hook.js'; + +/** + * @typedef {import('./index').default} Ultraviolet + */ + +class IDBApi extends EventEmitter { + /** + * + * @param {Ultraviolet} ctx + */ + constructor(ctx) { + super(); + this.ctx = ctx; + this.window = this.ctx.window; + this.IDBDatabase = this.window.IDBDatabase || {}; + this.idbDatabaseProto = this.IDBDatabase.prototype || {}; + this.IDBFactory = this.window.IDBFactory || {}; + this.idbFactoryProto = this.IDBFactory.prototype || {}; + this.open = this.idbFactoryProto.open; + } + overrideOpen() { + this.ctx.override( + this.IDBFactory.prototype, + 'open', + (target, that, args) => { + if (!args.length) return target.apply(that, args); + + if (!args.length) return target.apply(that, args); + const [name, version] = args; + + const event = new HookEvent({ name, version }, target, that); + this.emit('idbFactoryOpen', event); + + if (event.intercepted) return event.returnValue; + return event.target.call( + event.that, + event.data.name, + event.data.version + ); + } + ); + } + overrideName() { + this.ctx.overrideDescriptor(this.idbDatabaseProto, 'name', { + get: (target, that) => { + const event = new HookEvent( + { value: target.call(that) }, + target, + that + ); + this.emit('idbFactoryName', event); + + if (event.intercepted) return event.returnValue; + return event.data.value; + }, + }); + } +} + +export default IDBApi; diff --git a/src/client/index.js b/src/client/index.js index 2f48ff9..edec6c9 100644 --- a/src/client/index.js +++ b/src/client/index.js @@ -16,6 +16,7 @@ import URLApi from './url.js'; import EventEmitter from 'events'; import StorageApi from './storage.js'; import StyleApi from './dom/style.js'; +import IDBApi from './idb.js'; class UVClient extends EventEmitter { constructor(window = self, worker = !window.window) { @@ -43,6 +44,7 @@ class UVClient extends EventEmitter { this.worker = worker; this.fetch = new Fetch(this); this.xhr = new Xhr(this); + this.idb = new IDBApi(this); this.history = new History(this); this.element = new ElementApi(this); this.node = new NodeApi(this); @@ -67,10 +69,19 @@ class UVClient extends EventEmitter { this.worker ); } + /** + * + * @param {*} obj + * @param {*} prop + * @param {WrapFun} wrapper + * @param {*} construct + * @returns + */ override(obj, prop, wrapper, construct) { // if (!(prop in obj)) return false; const wrapped = this.wrap(obj, prop, wrapper, construct); - return (obj[prop] = wrapped); + obj[prop] = wrapped; + return wrapped; } overrideDescriptor(obj, prop, wrapObj = {}) { const wrapped = this.wrapDescriptor(obj, prop, wrapObj); @@ -78,6 +89,19 @@ class UVClient extends EventEmitter { this.nativeMethods.defineProperty(obj, prop, wrapped); return wrapped; } + /** + * @template {Function} [T=Function] + * @typedef {(fn: T, that: any, args: any[]) => {}} WrapFun + */ + /** + * + * @template T + * @param {*} obj + * @param {*} prop + * @param {WrapFun} wrap + * @param {boolean} construct + * @returns {T} + */ wrap(obj, prop, wrap, construct) { const fn = obj[prop]; if (!fn) return fn; diff --git a/src/uv.handler.js b/src/uv.handler.js index c0178b6..c19412d 100644 --- a/src/uv.handler.js +++ b/src/uv.handler.js @@ -409,6 +409,17 @@ function __uvHook(window) { event.data.url = __uv.rewriteUrl(event.data.url); }); + // IDB + client.idb.on('idbFactoryOpen', (event) => { + event.data.name = `${__uv.meta.url.origin}@${event.data.name}`; + }); + + client.idb.on('idbFactoryName', (event) => { + event.data.value = event.data.value.slice( + __uv.meta.url.origin.length + 1 /*the @*/ + ); + }); + // History client.history.on('replaceState', (event) => { if (event.data.url) @@ -1400,6 +1411,8 @@ function __uvHook(window) { //client.document.overrideQuerySelector(); client.object.overrideGetPropertyNames(); client.object.overrideGetOwnPropertyDescriptors(); + client.idb.overrideName(); + client.idb.overrideOpen(); client.history.overridePushState(); client.history.overrideReplaceState(); client.eventSource.overrideConstruct();