From 3f666a1254cd28d8aa3e0ab1ee96ea2e9999d967 Mon Sep 17 00:00:00 2001 From: Esteban Ordano Date: Sat, 18 Apr 2020 18:00:31 -0300 Subject: [PATCH] feat: incoming, outgoing, running working --- debugger/devtool/explorer/setup.ts | 49 +++++++---- debugger/devtool/layout/Sections.ts | 10 +-- debugger/devtool/page.tsx | 13 +++ debugger/devtool/renderer/hooks/incoming.ts | 82 ++++++++++--------- debugger/devtool/renderer/hooks/outgoing.ts | 90 ++++++++++----------- debugger/devtool/renderer/reducer.ts | 2 +- debugger/devtool/renderer/setup.ts | 2 - debugger/devtool/scenes/BUILD.bazel | 7 ++ debugger/devtool/scenes/present.tsx | 76 +++++++++++++++++ debugger/devtool/scenes/setup.ts | 72 +++++++++++++++++ debugger/devtool/scenes/store.ts | 54 +++++++++++++ debugger/types/explorer.ts | 1 + debugger/types/layout.ts | 3 +- 13 files changed, 348 insertions(+), 113 deletions(-) create mode 100644 debugger/devtool/scenes/present.tsx create mode 100644 debugger/devtool/scenes/setup.ts create mode 100644 debugger/devtool/scenes/store.ts diff --git a/debugger/devtool/explorer/setup.ts b/debugger/devtool/explorer/setup.ts index 41c258970..f8688cfd8 100644 --- a/debugger/devtool/explorer/setup.ts +++ b/debugger/devtool/explorer/setup.ts @@ -5,23 +5,40 @@ import { setInspectedTab } from './actions/actionCreators' export declare const chrome: GlobalChrome +function sendStoreInfo(object, path) { + const parts = path.split('.').filter((_) => _ !== '') + const end = parts.reduce((prev, next) => prev[next], object) + const canExpand = end !== undefined && end !== null && typeof end === 'object' + return { + path: path, + hasKeys: true, + keys: canExpand ? Object.keys(end) : [], + values: canExpand + ? Object.keys(end).reduce((prev, next) => { + prev[next] = + typeof end[next] === 'object' + ? { hasKeys: false } + : typeof end[next] === 'function' + ? 'function' + : typeof end[next] === 'undefined' + ? 'undefined' + : end[next] === null + ? 'null' + : end[next] + return prev + }, {}) + : typeof end === 'function' + ? 'function' + : typeof end === 'undefined' + ? 'undefined' + : end === null + ? 'null' + : end, + } +} + export function setup(connection: any, tabId: number) { - chrome.devtools.inspectedWindow.eval(`window.__sendStoreInfo = function (object, path) { - const parts = path.split('.').filter((_) => _ !== '') - const end = parts.reduce((prev, next) => prev[next], object) - return { - path: path, - hasKeys: true, - keys: typeof end === 'object' ? Object.keys(end) : [], - values: - typeof end === 'object' - ? Object.keys(end).reduce((prev, next) => { - prev[next] = typeof end[next] === 'object' ? { hasKeys: false } : end[next] - return prev - }, {}) - : end, - } - }`) + chrome.devtools.inspectedWindow.eval(`window.__sendStoreInfo = ${sendStoreInfo.toString()}`) connection.onMessage.addListener((event: any) => { try { if (typeof event === 'object' && event.name === 'dcl-explorer-state') { diff --git a/debugger/devtool/layout/Sections.ts b/debugger/devtool/layout/Sections.ts index 8e4e88bdb..5c158f254 100644 --- a/debugger/devtool/layout/Sections.ts +++ b/debugger/devtool/layout/Sections.ts @@ -39,15 +39,7 @@ export const Sections: Section[] = [ { section: 'Running scenes', logo: '👟', - }, - { - section: 'ECS State', - logo: '⏯', - }, - { - section: 'Messages', - logo: '📨', - }, + } ], }, { diff --git a/debugger/devtool/page.tsx b/debugger/devtool/page.tsx index aa36de28f..8255d2d2f 100644 --- a/debugger/devtool/page.tsx +++ b/debugger/devtool/page.tsx @@ -22,6 +22,12 @@ import { setup as setupRenderer } from './renderer/setup' */ import { createStore as createRenderStore } from './renderer/store' import * as Renderer from './renderer/present' +/** + * Scenes module + */ +import { setup as setupScenes } from './scenes/setup' +import { createStore as createSceneStore } from './scenes/store' +import { Scenes } from './scenes/present' export declare const chrome: GlobalChrome const FILE_LOCAL_VERBOSE_BUGS = false @@ -60,11 +66,17 @@ function setupBackground() { */ setupRenderer(backgroundPageConnection) const rendererStore = createRenderStore() + /** + * Tap for scene messages + */ + setupScenes(backgroundPageConnection, chrome.devtools.inspectedWindow.tabId) + const scenesStore = createSceneStore() mapSections.Status.component = Explorer mapSections.Networking.component = CommsPresenter mapSections.Outgoing.component = Renderer.Outgoing mapSections.Incoming.component = Renderer.Incoming + mapSections['Running scenes'].component = Scenes /** * Create a panel and continue there @@ -77,6 +89,7 @@ function setupBackground() { explorerStore={explorerStore} commsStore={commsStore} rendererStore={rendererStore} + scenesStore={scenesStore} />, panelWin.document.getElementById('root') ) diff --git a/debugger/devtool/renderer/hooks/incoming.ts b/debugger/devtool/renderer/hooks/incoming.ts index 9c146074b..ca64b0308 100644 --- a/debugger/devtool/renderer/hooks/incoming.ts +++ b/debugger/devtool/renderer/hooks/incoming.ts @@ -1,43 +1,47 @@ import { tapFunction } from '../../jslibs/tapFunction' +declare var window: any + +function tryInjectIn() { + try { + if (typeof window === undefined) { + console.log('ℹ️ tried to inject into undefined window :/') + } + if (window.browserInterface && !window.__browserSendMessage) { + console.log('ℹ️ browser interface intervened') + var browserBackup = window.browserInterface + var backup = window.browserInterface.onMessage + Object.defineProperty(window, 'browserInterface', { + get: () => browserBackup, + set: (newBrowser) => { + console.log('ℹ️ browser interface reinsert') + browserBackup = newBrowser + backup = newBrowser.onMessage + newBrowser.onMessage = tapOnMessage + }, + }) + function tapOnMessage(a, b, c, d) { + window.postMessage({ + name: 'dcl-explorer-incoming', + source: 'dcl-debugger', + payload: { + name: a, + key: b, + }, + }) + backup.apply(backup, [a, b, c, d]) + } + } else if (window.browserInterface) { + console.log('ℹ️ browser interface inject called twice') + } else { + console.log('ℹ️ browser interface not found yet') + setTimeout(tryInjectIn, 1000) + } + } catch (e) { + console.log('🥂', e) + } +} + export function setup(connection: any, tap: Function) { - tapFunction( - connection, - 'incoming', - tap, - ` - function tryInject() { - if (typeof window === undefined) { - console.log("ℹ️ tried to inject into undefined window :/"); - } - if (window.browserInterface && !window.__browserSendMessage) { - console.log("ℹ️ browser interface intervened"); - const backup = window.browserInterface.OnMessage; - window.browserInterface.OnMessage = function(a, b, c, d) { - console.log("ℹ️ ---", a, b) - try { - window.postMessage({ - name: 'dcl-explorer-incoming', - source: 'dcl-debugger', - payload: { - name: a - key: b - } - }) - } catch (e) { - console.log(e) - } - backup.apply(window.browserInterface, [a, b, c, d]) - } - } else if (window.browserInterface) { - console.log("ℹ️ browser interface inject called twice"); - } else { - console.log("ℹ️ browser interface not found yet"); - setTimeout(tryInject, 1000) - } - } - console.log('🥂') - tryInject() - ` - ) + tapFunction(connection, 'incoming', tap, `${tryInjectIn.toString()};tryInjectIn()`) } diff --git a/debugger/devtool/renderer/hooks/outgoing.ts b/debugger/devtool/renderer/hooks/outgoing.ts index 671390bc2..bb33280d2 100644 --- a/debugger/devtool/renderer/hooks/outgoing.ts +++ b/debugger/devtool/renderer/hooks/outgoing.ts @@ -1,49 +1,49 @@ import { tapFunction } from '../../jslibs/tapFunction' +declare var window: any + +function tryInjectOut() { + try { + if (typeof window === undefined) { + console.log('ℹ️ unity tried to inject into undefined window :/') + } + if (window.unityInterface && window.unityInterface.SendGenericMessage) { + console.log('ℹ️ unity interface intervened') + for (let triggerSend in window.unityInterface) { + if (window.unityInterface.hasOwnProperty(triggerSend) && triggerSend !== 'debug') { + const backup = window.unityInterface[triggerSend] + window.unityInterface[triggerSend] = function (a) { + console.log('ℹ️ unity interface outgoing', a) + try { + backup.apply(window.unityInterface, arguments) + window.postMessage( + { + name: 'dcl-explorer-outgoing', + source: 'dcl-debugger', + payload: { + name: triggerSend, + value: a, + }, + }, + '*' + ) + } catch (e) { + console.log('🥂', e.stack) + } + } + } + } + } else if (window.unityInterface) { + console.log('ℹ️ unity interface exists -- no SendMessage though') + setTimeout(tryInjectOut, 1000) + } else { + console.log('ℹ️ unity interface not found yet') + setTimeout(tryInjectOut, 1000) + } + } catch (e) { + console.log('ℹ️ unity intercept problem:', e) + } +} export function setup(connection: any, tap: Function) { - tapFunction( - connection, - 'outgoing', - tap, - `function tryInject() { - if (typeof window === undefined) { - console.log("ℹ️ tried to inject into undefined window :/"); - } - if (window.unityInterface && window.unityInterface.SendGenericMessage) { - console.log("ℹ️ unity interface intervened"); - for (let triggerSend in window.unityInterface) { - if (window.unityInterface.hasOwnProperty(triggerSend) && triggerSend !== 'debug') { - const backup = window.unityInterface[triggerSend] - window.unityInterface[triggerSend] = function(a, b, c, d, e) { - if (b || c || d || e) { - console.log('ℹ️ unexpected extra argument') - } - backup.apply(window.unityInterface, arguments) - try { - window.postMessage({ - name: 'dcl-explorer-outgoing', - source: 'dcl-debugger', - payload: { - name: triggerSend, - value: a, - extra: [b, c, d, e] - } - }, '*') - } catch (e) { - console.log("ℹ️ unity intercept problem:", triggerSend, e, arguments); - } - } - } - } - } else if (window.unityInterface) { - console.log("ℹ️ unity interface exists -- no SendMessage though"); - setTimeout(tryInject, 1000) - } else { - console.log("ℹ️ unity interface not found yet"); - setTimeout(tryInject, 1000) - } - } - tryInject() - ` - ) + tapFunction(connection, 'outgoing', tap, `${tryInjectOut.toString()};tryInjectOut();`) } diff --git a/debugger/devtool/renderer/reducer.ts b/debugger/devtool/renderer/reducer.ts index adc7d03d6..05e62e45b 100644 --- a/debugger/devtool/renderer/reducer.ts +++ b/debugger/devtool/renderer/reducer.ts @@ -16,7 +16,7 @@ export function Reducer(state: RendererState, action: RendererAction | AnyAction case INCOMING_MESSAGE: return { ...state, incoming: [action.payload, ...state.incoming.slice(0, 9)] } case OUTGOING_MESSAGE: - return { ...state, incoming: [action.payload, ...state.incoming.slice(0, 9)] } + return { ...state, outgoing: [action.payload, ...state.outgoing.slice(0, 9)] } default: return state } diff --git a/debugger/devtool/renderer/setup.ts b/debugger/devtool/renderer/setup.ts index 0f9329711..c2e92da03 100644 --- a/debugger/devtool/renderer/setup.ts +++ b/debugger/devtool/renderer/setup.ts @@ -7,14 +7,12 @@ const store = createStore() export function setup(connection: any) { outgoing(connection, (data: any) => { - console.log('a', data) store.dispatch({ type: OUTGOING_MESSAGE, payload: data, }) }) incoming(connection, (data: any) => { - console.log('b', data) store.dispatch({ type: INCOMING_MESSAGE, payload: data, diff --git a/debugger/devtool/scenes/BUILD.bazel b/debugger/devtool/scenes/BUILD.bazel index b6ce33826..0e7152216 100644 --- a/debugger/devtool/scenes/BUILD.bazel +++ b/debugger/devtool/scenes/BUILD.bazel @@ -7,12 +7,19 @@ ts_library( srcs = glob( include = [ "*.ts", + "*.tsx", + "**/*.ts", + "**/*.tsx", ], ), module_name = "@dcl/debugger/devtool/scenes", deps = [ + "//debugger/devtool/explorer", + "//debugger/devtool/jslibs", "//debugger/types", "//jslibs/hooks", + "@npm//@types/react", + "@npm//react", "@npm//redux", ], ) diff --git a/debugger/devtool/scenes/present.tsx b/debugger/devtool/scenes/present.tsx new file mode 100644 index 000000000..6d829f80e --- /dev/null +++ b/debugger/devtool/scenes/present.tsx @@ -0,0 +1,76 @@ +import React, { useCallback } from 'react' +import { Store } from 'redux' +import { useStore2 } from '../../../jslibs/hooks/useStore2' +import { ExplorableTree, ScenesState } from '../../types/explorer' +import { collapseAction, expandAction, loadAction } from '../explorer/actions/actionCreators' + +export function ExploreTree(props: { + name: string + tree: ExplorableTree + dispatch: any + path: string + offset: number +}) { + if (!props.tree) { + return
Error
+ } + const { loading, hasKeys, expanded } = props.tree + + const toggle = useCallback(() => props.dispatch((expanded ? collapseAction : expandAction)(props.path)), [ + props.path, + expanded, + ]) + const reload = useCallback(() => { + props.dispatch(loadAction(props.path)) + }, [props.path]) + const isFinalValue = typeof props.tree.values !== 'object' + return ( +
+ + {loading ? 'loading' : expanded ? '-' : '+'}{' '} + {hasKeys + ? `${props.name}: ${isFinalValue ? props.tree.values : props.tree.keys?.length + ' values'}` + : `${props.name}: [?]`} + + {hasKeys && !!expanded && !isFinalValue ? ( + + [reload] + + ) : ( + + )} + {!loading && + hasKeys && + expanded && + typeof props.tree.values === 'object' && + props.tree.keys!.map((name) => { + const leaf = props.tree.values![name] + return ( + + ) + })} +
+ ) +} + +export function Scenes(props: { windowContext: Window; scenesStore: Store }) { + if (!props.scenesStore) { + return

Loading...

+ } + const [tree, dispatch] = useStore2(props.scenesStore) + return ( +
+

Scenes

+ +
+ ) +} + +export default Scenes diff --git a/debugger/devtool/scenes/setup.ts b/debugger/devtool/scenes/setup.ts new file mode 100644 index 000000000..32bfc61cf --- /dev/null +++ b/debugger/devtool/scenes/setup.ts @@ -0,0 +1,72 @@ +import { clientLog } from '../jslibs/clientLog' +import { store } from './store' +import { GlobalChrome } from '../../types/chrome' +import { setInspectedTab } from '../explorer/actions/actionCreators' + +export declare const chrome: GlobalChrome + +function sendSceneInfo(object: any, path: string) { + const parts = path.split('.').filter((_) => _ !== '') + if (!parts.length) { + const ret = { + path: path, + hasKeys: true, + keys: Array.from(object.keys()), + values: Array.from(object.keys()).reduce((prev, next: string) => { + prev[next] = { hasKeys: false } + return prev + }, {}), + } + return ret + } + const obj = object.get(parts[0]) + const end = parts.slice(1).reduce((prev, next) => prev[next], obj) + const canExpand = end !== undefined && end !== null && typeof end === 'object' + return { + path: path, + hasKeys: true, + keys: canExpand ? Object.keys(end) : [], + values: canExpand + ? Object.keys(end).reduce((prev, next) => { + prev[next] = + typeof end[next] === 'object' + ? { hasKeys: false } + : typeof end[next] === 'function' + ? 'function' + : typeof end[next] === 'undefined' + ? 'undefined' + : end[next] === null + ? 'null' + : end[next] + return prev + }, {}) + : typeof end === 'function' + ? 'function' + : typeof end === 'undefined' + ? 'undefined' + : end === null + ? 'null' + : end, + } +} + +export function setup(connection: any, tabId: number) { + chrome.devtools.inspectedWindow.eval(`window.__sendSceneInfo = ${sendSceneInfo.toString()}`) + connection.onMessage.addListener((event: any) => { + try { + if (typeof event === 'object' && event.name === 'dcl-explorer-scenes') { + const data = event.payload + if (typeof data !== 'object') { + throw new Error() + } + store.dispatch({ + type: 'Resolve', + payload: event.payload, + }) + } + } catch (e) { + clientLog(`Could not parse message from client:`, event) + } + }) + store.dispatch(setInspectedTab(tabId)) +} diff --git a/debugger/devtool/scenes/store.ts b/debugger/devtool/scenes/store.ts new file mode 100644 index 000000000..444630c9f --- /dev/null +++ b/debugger/devtool/scenes/store.ts @@ -0,0 +1,54 @@ +import { applyMiddleware, compose, createStore as reduxCreateStore, Store } from 'redux' +import { GlobalChrome } from '../../types/chrome' +import { ScenesState } from '../../types/explorer' +import { loadAction } from '../explorer/actions/actionCreators' +import { OtherAction, StateAction } from '../explorer/actions/actions' +import { reducer } from '../explorer/reducers/reducer' +import { shouldTriggerLoad } from '../explorer/selectors/shouldTriggerLoad' + +export declare var chrome: GlobalChrome +const middleWare = (api: any) => (next: any) => (action?: StateAction | OtherAction) => { + if (!action) { + return next(action) + } + if (action.type === 'Expand') { + const result = next(action) + if (shouldTriggerLoad(api.getState(), action.payload)) { + api.dispatch(loadAction(action.payload)) + } + return result + } + if (action.type === 'Loading') { + chrome.devtools.inspectedWindow.eval( + `(function() { + try { + const r = window.__sendSceneInfo(window.sceneWorkers, "${action.payload}") + window.postMessage({ name: 'dcl-explorer-scenes', + source: 'dcl-debugger', + payload: r + }, '*') + } catch(e) { + debugger + window.__sendSceneInfo(window.sceneWorkers, "${action.payload}") + debugger + alert('Error in \`dcl-debugger\`, please check the console'); + console.log(e.stack) + } + })()` + ) + } + return next(action) +} +declare var window: any +const composeEnhancers = + typeof window === 'object' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ + ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) + : compose + +const enhancer = composeEnhancers(applyMiddleware(middleWare)) + +export const store: Store = reduxCreateStore(reducer, enhancer) + +export function createStore(): Store { + return store +} diff --git a/debugger/types/explorer.ts b/debugger/types/explorer.ts index 0ff488f18..e85ffdaad 100644 --- a/debugger/types/explorer.ts +++ b/debugger/types/explorer.ts @@ -1,4 +1,5 @@ +export type ScenesState = ExplorableTree export type ExplorerState = ExplorableTree export const EmptyTree = { expanded: false, hasKeys: false } export const InitialExplorerState = { diff --git a/debugger/types/layout.ts b/debugger/types/layout.ts index 32d095677..7ee5d68c1 100644 --- a/debugger/types/layout.ts +++ b/debugger/types/layout.ts @@ -1,6 +1,6 @@ import { Store } from 'redux' import { CommsState } from './comms' -import { ExplorerState } from './explorer' +import { ExplorerState, ScenesState } from './explorer' import { RendererState } from './renderer' export type RootProps = { @@ -8,4 +8,5 @@ export type RootProps = { explorerStore: Store commsStore: Store rendererStore: Store + scenesStore: Store }