How to open URL on FB Instant & IOS platform

Hi everyone,
I’m using cocos creator with typescript and i have a problem about opening URL from game. Now i’m using cc.sys.openURL(str) but it only works on Android without work on IOS and FB Instant platform.
This is error on FB Instant when i tried to open URL " Blocked opening ‘https://xxx’ in a new window because the request was made in a XXX frame whose ‘allow-popups’ permission is not set."

Browsers like Firefox or iOS disallow JavaScript code to open new tabs, unless the code is initiated by a click event to protect the user from sites that open popups without user interaction.

Therefore, the only cross-platform way of opening URLs in a new tab from a user interaction is to dynamically place a HTML button element at the clickable location, and perform the URL opening in its onclick handler.

Here’s the code I came up with - it’s TypeScript, but you should be able to adapt it to your liking or convert it to JS.
Note that this assumes a canvas size of 1080*1920, so you need to adapt this to your canvas size.
It also only positions the button element when enabling the component, so it’s not well suited for moving nodes.

const {ccclass, property} = cc._decorator;

@ccclass
export default class HTMLButton extends cc.Component {

    @property(cc.Component.EventHandler)
    public clickEvents: cc.Component.EventHandler[] = [];

    private readonly button: HTMLButtonElement = document.createElement('button');
    private readonly clickListener = this.onMouseDown.bind(this);

    start() {
        this.node.on(cc.Node.EventType.TOUCH_START, this.onMouseDown, this);
    }

    onEnable() {
        const canvasWidth = 1080;
        const canvasHeight = 1920;

        function xPerc(pos: number): number {
            return pos / canvasWidth;
        }

        function yPerc(pos: number): number {
            return 1 - (pos / canvasHeight);
        }

        function perc2CSS(perc: number): string {
            return `${perc * 100}%`;
        }
        
        // get bounds of button in world space, 
        // with the canvas' lower left corner being positioned at 0, 0
        const bounds = this.node.getBoundingBoxToWorld();
        
        // convert bounds into relative html coordinates
        const x = xPerc(bounds.x);
        const y = yPerc(bounds.yMax);

        const w = xPerc(bounds.xMax) - x;
        const h = yPerc(bounds.y) - y;
        
        this.button.onclick = this.onMouseDown.bind(this);
        this.button.style.position = "absolute";
        this.button.style.backgroundColor = "#0000"; // transparent
        this.button.style.border = "none";
        this.button.style.outline = "none"; // disables the focus outline on chrome

        this.button.style.left = perc2CSS(x);
        this.button.style.top = perc2CSS(y);
        this.button.style.width = perc2CSS(w);
        this.button.style.height = perc2CSS(h);

        cc.game.canvas.parentElement.appendChild(this.button);
    }

    onDisable() {
        cc.game.canvas.parentElement.removeChild(this.button);
    }

    private onMouseDown(event: MouseEvent) {
        cc.Component.EventHandler.emitEvents(this.clickEvents, event);
    }
}

This is the result:

I’ve tested this solution on iOS Safari and Desktop Firefox and Chrome, and it works flawlessly on all of these browsers.

I hope this helps someone else who’s facing the same issue!

1 Like