Skip to content

Commit

Permalink
feat: support indexedDB
Browse files Browse the repository at this point in the history
surunzi committed Jul 26, 2024
1 parent a2bcb0f commit 0ad70d3
Showing 4 changed files with 302 additions and 70 deletions.
101 changes: 32 additions & 69 deletions devtools/target.html
Original file line number Diff line number Diff line change
@@ -24,80 +24,43 @@
window.reload = function () {
location.reload()
}

const targetOrigin = location.protocol + '//' + location.host
function sendToDevtools(message) {
devtoolsIframe.contentWindow.postMessage(
JSON.stringify(message),
targetOrigin
)
}
let id = 1
function sendToChobitsu(message) {
message.id = 'tmp' + id++
chobitsu.sendRawMessage(JSON.stringify(message))
</script>
<script>
// https://gist.github.com/enjalot/6472041
var indexedDB = window.indexedDB
var open = indexedDB.open('MyDatabase', 1)
open.onupgradeneeded = function () {
var db = open.result
var store = db.createObjectStore('MyObjectStore', { keyPath: 'id' })
var index = store.createIndex('NameIndex', ['name.last', 'name.first'])
}
chobitsu.setOnMessage(message => {
if (message.indexOf('"id":"tmp') > -1) {
return
open.onsuccess = function () {
var db = open.result
var tx = db.transaction('MyObjectStore', 'readwrite')
var store = tx.objectStore('MyObjectStore')
var index = store.index('NameIndex')

store.put({ id: 12345, name: { first: 'John', last: 'Doe' }, age: 42 })
store.put({ id: 67890, name: { first: 'Bob', last: 'Smith' }, age: 35 })

var getJohn = store.get(12345)
var getBob = index.get(['Smith', 'Bob'])

getJohn.onsuccess = function () {
console.log(getJohn.result.name.first) // => "John"
}
devtoolsIframe.contentWindow.postMessage(message, targetOrigin)
})
window.addEventListener('message', event => {
if (event.origin !== targetOrigin) {
return

getBob.onsuccess = function () {
console.log(getBob.result.name.first) // => "Bob"
}
if (event.data && typeof event.data === 'string') {
chobitsu.sendRawMessage(event.data)

tx.oncomplete = function () {
db.close()
}
})
window.onload = function () {
setTimeout(function () {
if (
typeof devtoolsIframe !== 'undefined' &&
devtoolsIframe.contentWindow.runtime
) {
resetDevtools()
}
}, 0)
}
function resetDevtools() {
const window = devtoolsIframe.contentWindow
setTimeout(() => {
window.runtime.loadLegacyModule('core/sdk/sdk-legacy.js').then(() => {
const SDK = window.SDK
for (const resourceTreeModel of SDK.TargetManager.instance().models(
SDK.ResourceTreeModel
)) {
resourceTreeModel.dispatchEventToListeners(
SDK.ResourceTreeModel.Events.WillReloadPage,
resourceTreeModel
)
}
})
sendToDevtools({
method: 'Page.frameNavigated',
params: {
frame: {
id: '1',
mimeType: 'text/html',
securityOrigin: location.origin,
url: location.href,
},
type: 'Navigation',
},
})
sendToChobitsu({ method: 'Network.enable' })
sendToDevtools({ method: 'Runtime.executionContextsCleared' })
sendToChobitsu({ method: 'Runtime.enable' })
sendToChobitsu({ method: 'Debugger.enable' })
sendToChobitsu({ method: 'DOMStorage.enable' })
sendToChobitsu({ method: 'DOM.enable' })
sendToChobitsu({ method: 'CSS.enable' })
sendToChobitsu({ method: 'Overlay.enable' })
sendToDevtools({ method: 'DOM.documentUpdated' })
}, 0)
}
</script>
<script src="target.js"></script>
<script>
console.log('console right after target injected')
throw Error('exception right after target injected')
</script>
73 changes: 73 additions & 0 deletions devtools/target.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
const targetOrigin = location.protocol + '//' + location.host
function sendToDevtools(message) {
devtoolsIframe.contentWindow.postMessage(
JSON.stringify(message),
targetOrigin
)
}
let id = 1
function sendToChobitsu(message) {
message.id = 'tmp' + id++
chobitsu.sendRawMessage(JSON.stringify(message))
}
chobitsu.setOnMessage(message => {
if (message.indexOf('"id":"tmp') > -1) {
return
}
devtoolsIframe.contentWindow.postMessage(message, targetOrigin)
})
window.addEventListener('message', event => {
if (event.origin !== targetOrigin) {
return
}
if (event.data && typeof event.data === 'string') {
chobitsu.sendRawMessage(event.data)
}
})
window.onload = function () {
setTimeout(function () {
if (
typeof devtoolsIframe !== 'undefined' &&
devtoolsIframe.contentWindow.runtime
) {
resetDevtools()
}
}, 0)
}
function resetDevtools() {
const window = devtoolsIframe.contentWindow
setTimeout(() => {
window.runtime.loadLegacyModule('core/sdk/sdk-legacy.js').then(() => {
const SDK = window.SDK
for (const resourceTreeModel of SDK.TargetManager.instance().models(
SDK.ResourceTreeModel
)) {
resourceTreeModel.dispatchEventToListeners(
SDK.ResourceTreeModel.Events.WillReloadPage,
resourceTreeModel
)
}
})
sendToDevtools({
method: 'Page.frameNavigated',
params: {
frame: {
id: '1',
mimeType: 'text/html',
securityOrigin: location.origin,
url: location.href,
},
type: 'Navigation',
},
})
sendToChobitsu({ method: 'Network.enable' })
sendToDevtools({ method: 'Runtime.executionContextsCleared' })
sendToChobitsu({ method: 'Runtime.enable' })
sendToChobitsu({ method: 'Debugger.enable' })
sendToChobitsu({ method: 'DOMStorage.enable' })
sendToChobitsu({ method: 'DOM.enable' })
sendToChobitsu({ method: 'CSS.enable' })
sendToChobitsu({ method: 'Overlay.enable' })
sendToDevtools({ method: 'DOM.documentUpdated' })
}, 0)
}
195 changes: 195 additions & 0 deletions src/domains/IndexedDB.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
import each from 'licia/each'
import map from 'licia/map'
import isStr from 'licia/isStr'
import isArr from 'licia/isArr'
import * as objManager from '../lib/objManager'

const indexedDB = window.indexedDB

let databaseVersions: any = {}

export async function requestDatabaseNames() {
const databases = await indexedDB.databases()
const databaseNames: string[] = []

databaseVersions = {}
each(databases, database => {
if (!database.name) {
return
}
databaseVersions[database.name] = database.version
databaseNames.push(database.name)
})

return {
databaseNames,
}
}

export async function requestDatabase(params: any) {
const { databaseName } = params
const version = databaseVersions[databaseName]
const objectStores: any[] = []

const db = await promisify(indexedDB.open(databaseName))
if (db.objectStoreNames.length) {
const storeList = map(db.objectStoreNames, name => {
return db.transaction(name, 'readonly').objectStore(name)
})
each(storeList, store => {
const indexes: any[] = []
each(store.indexNames, indexName => {
const index = store.index(indexName)
indexes.push({
name: index.name,
multiEntry: index.multiEntry,
keyPath: formatKeyPath(index.keyPath),
unique: index.unique,
})
})
objectStores.push({
name: store.name,
indexes,
keyPath: formatKeyPath(store.keyPath),
autoIncrement: store.autoIncrement,
})
})
}

return {
databaseWithObjectStores: {
name: databaseName,
objectStores,
version,
},
}
}

export async function requestData(params: any) {
const { databaseName, objectStoreName, indexName, pageSize, skipCount } =
params

const db = await promisify(indexedDB.open(databaseName))
const objectStore = db
.transaction(objectStoreName, 'readonly')
.objectStore(objectStoreName)
const count = await promisify(objectStore.count())

let currentIdx = 0
let cursorRequest: IDBRequest<IDBCursorWithValue | null>
if (indexName) {
const index = objectStore.index(indexName)
cursorRequest = index.openCursor()
} else {
cursorRequest = objectStore.openCursor()
}
return new Promise((resolve, reject) => {
const objectStoreDataEntries: any[] = []
cursorRequest.addEventListener('success', () => {
const cursor = cursorRequest.result
if (cursor && currentIdx < pageSize + skipCount) {
if (currentIdx >= skipCount) {
objectStoreDataEntries.push({
key: objManager.wrap(cursor.key, {
generatePreview: true,
}),
primaryKey: objManager.wrap(cursor.primaryKey),
value: objManager.wrap(cursor.value, {
generatePreview: true,
}),
})
}
cursor.continue()
currentIdx++
} else {
resolve({
hasMore: currentIdx < count,
objectStoreDataEntries,
})
}
})
cursorRequest.addEventListener('error', reject)
})
}

export async function getMetadata(params: any) {
const { databaseName, objectStoreName } = params

const objectStore = await getObjectStore(databaseName, objectStoreName)

return {
entriesCount: await promisify(objectStore.count()),
keyGeneratorValue: 1,
}
}

export async function deleteObjectStoreEntries(params: any) {
const { databaseName, objectStoreName, keyRange } = params

const objectStore = await getObjectStore(databaseName, objectStoreName)
await promisify(
objectStore.delete(
IDBKeyRange.bound(
getKeyRangeBound(keyRange.lower),
getKeyRangeBound(keyRange.upper),
keyRange.lowerOpen,
keyRange.upperOpen
)
)
)
}

export async function clearObjectStore(params: any) {
const { databaseName, objectStoreName } = params

const objectStore = await getObjectStore(databaseName, objectStoreName)
await promisify(objectStore.clear())
}

export async function deleteDatabase(params: any) {
await promisify(indexedDB.deleteDatabase(params.databaseName))
}

function getKeyRangeBound(key: any) {
return key.number || key.string || key.date || key.array || key
}

async function getObjectStore(databaseName: string, objectStoreName: string) {
const db = await promisify(indexedDB.open(databaseName))
const objectStore = db
.transaction(objectStoreName, 'readwrite')
.objectStore(objectStoreName)

return objectStore
}

function formatKeyPath(keyPath: any) {
if (isStr(keyPath)) {
return {
type: 'string',
string: keyPath,
}
}

if (isArr(keyPath)) {
return {
type: 'array',
array: keyPath,
}
}

return {
type: 'null',
}
}

function promisify<T = any>(req: IDBRequest<T>): Promise<T> {
return new Promise((resolve, reject) => {
req.addEventListener('success', () => {
resolve(req.result)
})
req.addEventListener('error', () => {
reject()
})
})
}
3 changes: 2 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -14,6 +14,7 @@ import * as DOMDebugger from './domains/DOMDebugger'
import * as Debugger from './domains/Debugger'
import * as Storage from './domains/Storage'
import * as CacheStorage from './domains/CacheStorage'
import * as IndexedDB from './domains/IndexedDB'

const chobitsu = new Chobitsu()
chobitsu.register('Network', {
@@ -122,7 +123,7 @@ chobitsu.register('DOMStorage', {
})
chobitsu.register('IndexedDB', {
enable: noop,
requestDatabaseNames: noop,
...IndexedDB,
})
chobitsu.register('ApplicationCache', {
enable: noop,

0 comments on commit 0ad70d3

Please sign in to comment.