krakenjs / post-robot

Cross domain post-messaging on the client side using a simple listener/client pattern.
Apache License 2.0
741 stars 92 forks source link

Window did not load after 5000ms #73

Closed ricklang closed 5 years ago

ricklang commented 5 years ago

I have two Angular web applications. App_A is like a listener and will set/get the data in its sessionStorage when receiving messages. App_B is like a client and will send message to App_A. App_A and App_B are running in different domains.

At the startup the App_B will try to load App_A into an iframe. The communication was working properly in the past. Recently I upgraded the Angular for v5 to v7 and the latest post-robot. Everything was still working fine when testing in local environment. But after I deployed both App_A and App_B to Azure web services, I got the error "Window did not load after 5000ms" when running App_B in a browser. Also if I only deploy App_A to Azure, and running App_B in the local, everything works fine. Do you have any idea what might be the problem(s)?

// listener.ts

export class StorageListenerService {
    private readListener: any;
    private writeListener: any;
    private clearListener: any;

    constructor() {
        this.init();
    }

    private init(): void {
        try {
            this.readListener = postRobot.on('read', { domain: '*' }, ({ origin, data }) => {
                return { result: this.readStore(data.key) };
            });
        } catch (e) { }

        try {
            this.writeListener = postRobot.on('write', { domain: '*' }, ({ origin, data }) => {
                this.writeStore(data.key, data.value);
            });
        } catch (e) { }

        try {
            this.clearListener = postRobot.on('clear', { domain: '*' }, ({ origin, data }) => {
                this.clearStore(data ? data.key : null);
            });
        } catch (e) { }

    }

    public destroyListener(): void {
        try {
            this.readListener.cancel();
            this.writeListener.cancel();
            this.clearListener.cancel();
        } catch (e) { }
    }

    private readStore(key: string): any {
        const value = window.sessionStorage.getItem(key);
        return value ? JSON.parse(value) : null;
    }

    private writeStore(key: string, value: any): void {
        const serializedData = JSON.stringify(value);
        window.sessionStorage.setItem(key, serializedData);
    }

    private clearStore(key?: string): void {
        if (key) {
            window.sessionStorage.removeItem(key);
        } else {
            window.sessionStorage.clear();
        }
    }

    public setLoginStaff(loginStaff: LoginStaff): void {
        try {
            this.writeStore(StorageKey.loginStaff, loginStaff);
        } catch (e) {
            console.error('write to storage failed.');
        }
    }

    public getLoginStaff(): Promise<LoginStaff> {
        try {
            return Promise.resolve(this.readStore(StorageKey.loginStaff));
        } catch (e) {
            return null;
        }
    }

    public clearLoginStaff(): void {
        try {
            this.clearStore(StorageKey.loginStaff);
        } catch (e) {
            console.error('clear login staff failed.');
        }
    }
}

// client.ts

export class StorageClientService {
    private win: any;
    constructor(@Inject(DOCUMENT) private document: any,
                       @Inject(STORAGE_LISTENER_URL) private StorageListenerUrl: string) {
        this.init();
    }

    private init(): void {
        const frame = this.document.createElement('iframe');
        frame.src = this.StorageListenerUrl;
        frame.id = 'childframe';
        frame.name = Math.random().toString();
        frame.onload = null;
        frame.style.visibility = 'hidden';
        this.document.body.appendChild(frame);
        this.win = frame.contentWindow;
    }

    public setLoginStaff(loginStaff: LoginStaff): void {
        try {
        postRobot.send(this.win, 'write',
        { key: StorageKey.loginStaff, value: loginStaff },
        { domain: this.StorageListenerUrl, timeout: 15000 })
        .then(() => {
    }

    public getLoginStaff(): Promise {
        try {
            return postRobot.send(this.win, 'read',
                { key: StorageKey.loginStaff },
                { timeout: 15000 })
            .then(
                ({ data }) => data.result as LoginStaff,
                () => null);
        } catch (e) {
            console.log(e);
            return null;
        }
    }

    public clearLoginStaff(): void {
        try {
            postRobot.send(this.win, 'clear', { key: StorageKey.loginStaff },
                          { domain: this.StorageListenerUrl }).then(() => {
        catch(e) {
            console.error(e);
        }
    }
}
bluepnume commented 5 years ago

The code looks fine to me. Can you link me to somewhere I can try it out?

ricklang commented 5 years ago

Actually you can run my application from https://smarttest.csosadev.net. Look at the console log for the error message.

bluepnume commented 5 years ago

Looks like it's making me log in -- can you make this app public for a while?

ricklang commented 5 years ago

Sorry about that. I don't want to disable the authentication because it might be culprit that caused the problem. You can use user: rathi.krish@csosadev.net pwd: Qocu72548 to login.

Do worry everything there is "fake" data.

bluepnume commented 5 years ago

Looks like you have different versions of post-robot in the parent and child windows.

ricklang commented 5 years ago

Oh, sorry. Because it was working for post-robot v8.0.30 when I was using Angular 5. so I was trying to see whether it is the version issue. I will make the two using the latest post-robot and will let you know when it is done.

ricklang commented 5 years ago

Ok, both applications are using post-robot 10.0.10 now.

bluepnume commented 5 years ago

Hmm nope, I still see an old version in the parent window

ricklang commented 5 years ago

How do you see the version?

bluepnume commented 5 years ago

window.__postRobot__ is only defined for older versions

ricklang commented 5 years ago

Thanks, you are right. It is the version issue. Angular 7 build has some changes which causes my CI/CD on Azure DevOps actually failed, though it reported as successfully deployed.

claustres commented 5 years ago

I can confirm the issue about version mismatch even with patch updates, we encountered this one with a parent using 10.0.14 (master) and child using 10.0.10.