Skip to content

Commit

Permalink
🔧 chore: separate inject and trap
Browse files Browse the repository at this point in the history
Signed-off-by: SimonShiki <[email protected]>
  • Loading branch information
SimonShiki committed Mar 17, 2024
1 parent 95ed7ff commit 9394a8b
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 55 deletions.
7 changes: 4 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ const open = window.open;
// @ts-expect-error defined in webpack define plugin
log(`eureka-loader ${__EUREKA_VERSION__}`);
// Try injecting chibi into the page.
await trap(open);
if (typeof window.eureka.vm !== 'undefined') {
const [vm, blockly] = await trap(open);
if (!!vm) {
// Alright we got the virtual machine, start the injection.
inject(window.eureka.vm);
window.eureka.vm = vm;
inject(vm, blockly);
} else {
// This is not a Scratch page, stop injecting.
log(`Cannot find vm in this page, stop injecting.`);
Expand Down
99 changes: 47 additions & 52 deletions src/injector/inject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ function getExtensionIdForOpcode (opcode: string) {
* @param vm Virtual machine instance. For some reasons we cannot use VM here.
* @returns Blockly instance.
*/
async function getBlocklyInstance (vm: EurekaCompatibleVM): Promise<any> {
async function getBlocklyInstance (vm: EurekaCompatibleVM): Promise<typeof Blockly | null> {
function getBlocklyInstanceInternal (): any | null {
// Hijack Function.prototype.apply to get React element instance.
function hijack (fn: (...args: unknown[]) => unknown) {
Expand Down Expand Up @@ -128,7 +128,7 @@ async function getBlocklyInstance (vm: EurekaCompatibleVM): Promise<any> {
* @param open window.open function (compatible with ccw).
* @return Callback promise. After that you could use window.eureka.vm to get the virtual machine.
*/
export function trap (open: typeof window.open): Promise<void> {
export function trap (open: typeof window.open): Promise<[EurekaCompatibleVM | null, typeof Blockly | null]> {
window.eureka = {
// @ts-expect-error defined in webpack define plugin
version: __EUREKA_VERSION__,
Expand All @@ -139,11 +139,12 @@ export function trap (open: typeof window.open): Promise<void> {

log('Listening bind function...');
const oldBind = Function.prototype.bind;
return new Promise<void>((resolve) => {
// @ts-expect-error lazy to fix
return new Promise<EurekaCompatibleVM>((resolve, reject) => {
const timeoutId = setTimeout(() => {
log('Cannot find vm instance, stop listening.');
Function.prototype.bind = oldBind;
resolve();
reject();
}, MAX_LISTENING_MS);

Function.prototype.bind = function (...args) {
Expand All @@ -155,22 +156,25 @@ export function trap (open: typeof window.open): Promise<void> {
Object.prototype.hasOwnProperty.call(args[0], 'runtime')
) {
log('VM detected!');
window.eureka.vm = args[0];
Function.prototype.bind = oldBind;
clearTimeout(timeoutId);
resolve();
resolve(args[0]);
return oldBind.apply(this, args);
}
return oldBind.apply(this, args);
};
}).then(async (vm) => {
return [vm, await getBlocklyInstance(vm)];
}).catch(() => {
return [null, null];
});
}

/**
* Inject into the original virtual machine.
* @param vm {EurekaCompatibleVM} Original virtual machine instance.
*/
export function inject (vm: EurekaCompatibleVM) {
export function inject (vm: EurekaCompatibleVM, blockly: any) {
const loader = (window.eureka.loader = new EurekaLoader(vm));
const originalLoadFunc = vm.extensionManager.loadExtensionURL;
const getLocale = vm.getLocale;
Expand Down Expand Up @@ -450,53 +454,44 @@ export function inject (vm: EurekaCompatibleVM) {
};
}

// Blockly stuffs
let initalized = false;
getBlocklyInstance(vm).then((blockly) => {
if (!initalized) {
window.eureka.blockly = blockly;
initalized = true;
const originalAddCreateButton_ = blockly.Procedures.addCreateButton_;
blockly.Procedures.addCreateButton_ = function (
workspace: EurekaCompatibleWorkspace,
xmlList: HTMLElement[],
...args: unknown[]
) {
originalAddCreateButton_.call(this, workspace, xmlList, ...args);
injectToolbox(xmlList, workspace, format);
};
const workspace = blockly.getMainWorkspace();
workspace.getToolbox().refreshSelection();
workspace.toolboxRefreshEnabled_ = true;
}
});
setTimeout(() => {
if (!initalized) {
warn('Cannot find real blockly instance, try alternative method...');
const originalProcedureCallback =
if (typeof blockly === 'object') {
window.eureka.blockly = blockly;
const originalAddCreateButton_ = blockly.Procedures.addCreateButton_;
blockly.Procedures.addCreateButton_ = function (
workspace: EurekaCompatibleWorkspace,
xmlList: HTMLElement[],
...args: unknown[]
) {
originalAddCreateButton_.call(this, workspace, xmlList, ...args);
injectToolbox(xmlList, workspace, format);
};
const workspace = blockly.getMainWorkspace();
workspace.getToolbox().refreshSelection();
workspace.toolboxRefreshEnabled_ = true;
} else {
warn('Cannot find real blockly instance, try alternative method...');
const originalProcedureCallback =
window.Blockly?.getMainWorkspace()?.toolboxCategoryCallbacks_?.PROCEDURE;
if (!originalProcedureCallback) {
error('alternative method failed, stop injecting');
return;
}
initalized = true;
window.Blockly.getMainWorkspace().toolboxCategoryCallbacks_.PROCEDURE = function (
workspace: EurekaCompatibleWorkspace,
...args: unknown[]
) {
const xmlList = originalProcedureCallback.call(
this,
workspace,
...args
) as HTMLElement[];
injectToolbox(xmlList, workspace, format);
return xmlList;
};
const workspace = window.Blockly.getMainWorkspace();
workspace.getToolbox().refreshSelection();
workspace.toolboxRefreshEnabled_ = true;
if (!originalProcedureCallback) {
error('alternative method failed, stop injecting');
return;
}
}, 3000);
window.Blockly.getMainWorkspace().toolboxCategoryCallbacks_.PROCEDURE = function (
workspace: EurekaCompatibleWorkspace,
...args: unknown[]
) {
const xmlList = originalProcedureCallback.call(
this,
workspace,
...args
) as HTMLElement[];
injectToolbox(xmlList, workspace, format);
return xmlList;
};
const workspace = window.Blockly.getMainWorkspace();
workspace.getToolbox().refreshSelection();
workspace.toolboxRefreshEnabled_ = true;
}
}
function injectToolbox (
xmlList: HTMLElement[],
Expand Down

0 comments on commit 9394a8b

Please sign in to comment.