-
-
Notifications
You must be signed in to change notification settings - Fork 339
Expand file tree
/
Copy pathcreateIframe.ts
More file actions
141 lines (129 loc) · 5.1 KB
/
createIframe.ts
File metadata and controls
141 lines (129 loc) · 5.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
import { ISentence } from '@/Core/controller/scene/sceneInterface';
import { IPerform } from '@/Core/Modules/perform/performInterface';
import { getBooleanArgByKey, getStringArgByKey } from '../util/getSentenceArg';
import { IIFrame } from '@/store/stageInterface';
import { webgalStore } from '@/store/store';
import { stageActions } from '@/store/stageReducer';
import { CSSProperties } from 'react';
const allSandboxProperties = {
'allow-forms': 'allowForms', // 允许iframe内提交表单
'allow-scripts': 'allowScripts', // 允许iframe内执行JavaScript脚本(包括定时器、事件等)
'allow-same-origin': 'allowSameOrigin', // 允许iframe内容拥有同源身份,可访问自身Cookie/LocalStorage等
'allow-top-navigation': 'allowTopNavigation', // 允许iframe内的链接跳转到父页面(主页面)的上下文
'allow-popups': 'allowPopups', // 允许iframe通过window.open()等方式弹出新窗口
'allow-modals': 'allowModals', // 允许iframe弹出模态窗口(如alert()、confirm()、prompt())
'allow-pointer-lock': 'allowPointerLock', // 允许iframe使用Pointer Lock API(如游戏鼠标锁定)
'allow-popups-to-escape-sandbox': 'allowPopupsToEscapeSandbox', // 允许iframe弹出的新窗口不受当前沙箱限制
'allow-downloads': 'allowDownloads', // 允许iframe内触发文件下载操作
'allow-presentation': 'allowPresentation', // 允许iframe使用Presentation API(投屏/演示功能)
'allow-top-navigation-by-user-activation': 'allowTopNavigationByUserActivation', // 仅允许用户主动触发(如点击)的顶级导航操作
'allow-storage-access-by-user-activation': 'allowStorageAccessByUserActivation', // 允许用户主动触发后访问父页面的存储权限
'allow-orientation-lock': 'allowOrientationLock', // 允许iframe使用Screen Orientation API锁定屏幕方向
};
/**
* 创建框架
* @param sentence
*/
export const createIframe = (sentence: ISentence): IPerform => {
const src = sentence.content;
const id = getStringArgByKey(sentence, 'id') ?? '';
const wait = getBooleanArgByKey(sentence, 'wait') ?? false;
const hidden = getBooleanArgByKey(sentence, 'hidden') ?? false;
const returnValue = getStringArgByKey(sentence, 'returnValue') ?? null;
const width = getStringArgByKey(sentence, 'width') ?? undefined;
const height = getStringArgByKey(sentence, 'height') ?? undefined;
if (!id || !src) {
return {
performName: 'none',
duration: 0,
isHoldOn: false,
stopFunction: () => {},
blockingNext: () => false,
blockingAuto: () => true,
stopTimeout: undefined,
};
}
// 可能后续会增加更多的样式,所以我们先定义一个空对象
// 还没想好如何优雅的使用指令定义样式,所以暂时使用已知参数定义样式
let styleCSSProperties: CSSProperties = {};
if (hidden) {
styleCSSProperties.display = 'none';
styleCSSProperties.opacity = 0;
}
let rawSrc = src;
// 处理src
if (!rawSrc.startsWith('http://') && !rawSrc.startsWith('https://')) {
rawSrc = './game/' + rawSrc;
}
// 查询所有参数(以@开头)
const args = sentence.args;
const injectArgs =
args.filter((arg) => arg.key.startsWith('@')).map((arg) => ({ key: arg.key.slice(1), value: arg.value })) ?? {};
const frameData: IIFrame = {
id,
src: rawSrc,
sandbox: '',
width,
height,
isActive: true,
wait,
returnValue,
injectArgs,
style: styleCSSProperties,
};
for (const [key, value] of Object.entries(allSandboxProperties)) {
const v = getStringArgByKey(sentence, value) ?? '';
if (v) {
frameData.sandbox += key + ' ';
}
}
webgalStore.dispatch(stageActions.addIframe(frameData));
// 如果需要等待iframe完成,则返回阻塞的perform
if (wait) {
let isCompleted = false;
// 监听iframe完成消息
const handleFrameComplete = (event: MessageEvent) => {
if (
event.data &&
typeof event.data === 'object' &&
event.data.type === 'webgal-frame-complete' &&
event.data.frameId === id
) {
isCompleted = true;
// 如果有returnValue,则存储到游戏变量中
if (returnValue && event.data.returnValue !== undefined) {
webgalStore.dispatch(
stageActions.setStageVar({
key: returnValue,
value: event.data.returnValue,
}),
);
}
// 移除事件监听器
window.removeEventListener('message', handleFrameComplete);
}
};
// 添加事件监听器
window.addEventListener('message', handleFrameComplete);
return {
performName: `frame-wait-${id}`,
duration: 0,
isHoldOn: true,
stopFunction: () => {
window.removeEventListener('message', handleFrameComplete);
},
blockingNext: () => !isCompleted,
blockingAuto: () => !isCompleted,
stopTimeout: undefined,
};
}
return {
performName: 'none',
duration: 0,
isHoldOn: false,
stopFunction: () => {},
blockingNext: () => false,
blockingAuto: () => true,
stopTimeout: undefined,
};
};