Merge remote-tracking branch 'remotes/workadventure-main/develop' into gamestate-api-read

This commit is contained in:
jonny 2021-05-10 10:01:53 +02:00
commit ce0c7ea3eb
146 changed files with 7765 additions and 1891 deletions

View file

@ -1,7 +1,56 @@
export interface IframeEvent {
type: string;
data: unknown;
import { GameStateEvent } from './ApiGameStateEvent';
import { ButtonClickedEvent } from './ButtonClickedEvent';
import { ChatEvent } from './ChatEvent';
import { ClosePopupEvent } from './ClosePopupEvent';
import { EnterLeaveEvent } from './EnterLeaveEvent';
import { GoToPageEvent } from './GoToPageEvent';
import { OpenCoWebSiteEvent } from './OpenCoWebSiteEvent';
import { OpenPopupEvent } from './OpenPopupEvent';
import { OpenTabEvent } from './OpenTabEvent';
import { UserInputChatEvent } from './UserInputChatEvent';
export interface TypedMessageEvent<T> extends MessageEvent {
data: T
}
export type IframeEventMap = {
getState: GameStateEvent,
// updateTile: UpdateTileEvent
chat: ChatEvent,
openPopup: OpenPopupEvent
closePopup: ClosePopupEvent
openTab: OpenTabEvent
goToPage: GoToPageEvent
openCoWebSite: OpenCoWebSiteEvent
closeCoWebSite: null
disablePlayerControl: null
restorePlayerControl: null
displayBubble: null
removeBubble: null
}
export interface IframeEvent<T extends keyof IframeEventMap> {
type: T;
data: IframeEventMap[T];
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const isIframeEventWrapper = (event: any): event is IframeEvent<keyof IframeEventMap> => typeof event.type === 'string';
export interface IframeResponseEventMap {
userInputChat: UserInputChatEvent
enterEvent: EnterLeaveEvent
leaveEvent: EnterLeaveEvent
buttonClickedEvent: ButtonClickedEvent
gameState: GameStateEvent
}
export interface IframeResponseEvent<T extends keyof IframeResponseEventMap> {
type: T;
data: IframeResponseEventMap[T];
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const isIframeEventWrapper = (event: any): event is IframeEvent => typeof event.type === 'string';
export const isIframeResponseEventWrapper = (event: { type?: string }): event is IframeResponseEvent<keyof IframeResponseEventMap> => typeof event.type === 'string';

View file

@ -1,17 +1,17 @@
import {Subject} from "rxjs";
import {ChatEvent, isChatEvent} from "./Events/ChatEvent";
import {IframeEvent, isIframeEventWrapper} from "./Events/IframeEvent";
import {UserInputChatEvent} from "./Events/UserInputChatEvent";
import { Subject } from "rxjs";
import { ChatEvent, isChatEvent } from "./Events/ChatEvent";
import * as crypto from "crypto";
import {HtmlUtils} from "../WebRtc/HtmlUtils";
import {EnterLeaveEvent} from "./Events/EnterLeaveEvent";
import {isOpenPopupEvent, OpenPopupEvent} from "./Events/OpenPopupEvent";
import {isOpenTabEvent, OpenTabEvent} from "./Events/OpenTabEvent";
import {ButtonClickedEvent} from "./Events/ButtonClickedEvent";
import {ClosePopupEvent, isClosePopupEvent} from "./Events/ClosePopupEvent";
import {scriptUtils} from "./ScriptUtils";
import {GoToPageEvent, isGoToPageEvent} from "./Events/GoToPageEvent";
import {isOpenCoWebsite, OpenCoWebSiteEvent} from "./Events/OpenCoWebSiteEvent";
import { HtmlUtils } from "../WebRtc/HtmlUtils";
import { EnterLeaveEvent } from "./Events/EnterLeaveEvent";
import { isOpenPopupEvent, OpenPopupEvent } from "./Events/OpenPopupEvent";
import { isOpenTabEvent, OpenTabEvent } from "./Events/OpenTabEvent";
import { ButtonClickedEvent } from "./Events/ButtonClickedEvent";
import { ClosePopupEvent, isClosePopupEvent } from "./Events/ClosePopupEvent";
import { scriptUtils } from "./ScriptUtils";
import { GoToPageEvent, isGoToPageEvent } from "./Events/GoToPageEvent";
import { isOpenCoWebsite, OpenCoWebSiteEvent } from "./Events/OpenCoWebSiteEvent";
import { IframeEventMap, IframeEvent, IframeResponseEvent, IframeResponseEventMap, isIframeEventWrapper, TypedMessageEvent } from "./Events/IframeEvent";
import { UserInputChatEvent } from "./Events/UserInputChatEvent";
import { GameStateEvent } from './Events/ApiGameStateEvent';
import { deepFreezeClone as deepFreezeClone } from '../utility';
@ -62,18 +62,18 @@ class IframeListener {
private readonly scripts = new Map<string, HTMLIFrameElement>();
init() {
window.addEventListener("message", (message) => {
window.addEventListener("message", (message: TypedMessageEvent<IframeEvent<keyof IframeEventMap>>) => {
// Do we trust the sender of this message?
// Let's only accept messages from the iframe that are allowed.
// Note: maybe we could restrict on the domain too for additional security (in case the iframe goes to another domain).
let found = false;
let foundSrc: string | null = null;
for (const iframe of this.iframes) {
if (iframe.contentWindow === message.source) {
found = true;
foundSrc = iframe.src;
break;
}
}
if (!found) {
if (!foundSrc) {
return;
}
@ -86,28 +86,32 @@ class IframeListener {
} else if (payload.type === 'closePopup' && isClosePopupEvent(payload.data)) {
this._closePopupStream.next(payload.data);
}
else if(payload.type === 'openTab' && isOpenTabEvent(payload.data)) {
else if (payload.type === 'openTab' && isOpenTabEvent(payload.data)) {
scriptUtils.openTab(payload.data.url);
}
else if(payload.type === 'goToPage' && isGoToPageEvent(payload.data)) {
else if (payload.type === 'goToPage' && isGoToPageEvent(payload.data)) {
scriptUtils.goToPage(payload.data.url);
}
else if(payload.type === 'openCoWebSite' && isOpenCoWebsite(payload.data)) {
scriptUtils.openCoWebsite(payload.data.url);
else if (payload.type === 'openCoWebSite' && isOpenCoWebsite(payload.data)) {
const scriptUrl = [...this.scripts.keys()].find(key => {
return this.scripts.get(key)?.contentWindow == message.source
})
scriptUtils.openCoWebsite(payload.data.url, scriptUrl || foundSrc);
}
else if(payload.type === 'closeCoWebSite') {
else if (payload.type === 'closeCoWebSite') {
scriptUtils.closeCoWebSite();
}
else if (payload.type === 'disablePlayerControl'){
else if (payload.type === 'disablePlayerControl') {
this._disablePlayerControlStream.next();
}
else if (payload.type === 'restorePlayerControl'){
else if (payload.type === 'restorePlayerControl') {
this._enablePlayerControlStream.next();
}
else if (payload.type === 'displayBubble'){
else if (payload.type === 'displayBubble') {
this._displayBubbleStream.next();
}
else if (payload.type === 'removeBubble'){
else if (payload.type === 'removeBubble') {
this._removeBubbleStream.next();
}else if(payload.type=="getState"){
this._gameStateStream.next();
@ -146,7 +150,7 @@ class IframeListener {
const iframe = document.createElement('iframe');
iframe.id = this.getIFrameId(scriptUrl);
iframe.style.display = 'none';
iframe.src = '/iframe.html?script='+encodeURIComponent(scriptUrl);
iframe.src = '/iframe.html?script=' + encodeURIComponent(scriptUrl);
// We are putting a sandbox on this script because it will run in the same domain as the main website.
iframe.sandbox.add('allow-scripts');
@ -170,8 +174,8 @@ class IframeListener {
'\n' +
'<html lang="en">\n' +
'<head>\n' +
'<script src="'+window.location.protocol+'//'+window.location.host+'/iframe_api.js" ></script>\n' +
'<script src="'+scriptUrl+'" ></script>\n' +
'<script src="' + window.location.protocol + '//' + window.location.host + '/iframe_api.js" ></script>\n' +
'<script src="' + scriptUrl + '" ></script>\n' +
'</head>\n' +
'</html>\n';
@ -188,14 +192,14 @@ class IframeListener {
}
private getIFrameId(scriptUrl: string): string {
return 'script'+crypto.createHash('md5').update(scriptUrl).digest("hex");
return 'script' + crypto.createHash('md5').update(scriptUrl).digest("hex");
}
unregisterScript(scriptUrl: string): void {
const iFrameId = this.getIFrameId(scriptUrl);
const iframe = HtmlUtils.getElementByIdOrFail<HTMLIFrameElement>(iFrameId);
if (!iframe) {
throw new Error('Unknown iframe for script "'+scriptUrl+'"');
throw new Error('Unknown iframe for script "' + scriptUrl + '"');
}
this.unregisterIframe(iframe);
iframe.remove();
@ -243,7 +247,7 @@ class IframeListener {
/**
* Sends the message... to all allowed iframes.
*/
private postMessage(message: IframeEvent) {
private postMessage(message: IframeResponseEvent<keyof IframeResponseEventMap>) {
for (const iframe of this.iframes) {
iframe.contentWindow?.postMessage(message, '*');
}

View file

@ -11,8 +11,8 @@ class ScriptUtils {
}
public openCoWebsite(url : string){
coWebsiteManager.loadCoWebsite(url,url);
public openCoWebsite(url: string, base: string) {
coWebsiteManager.loadCoWebsite(url, base);
}
public closeCoWebSite(){