'Firefox is terminating web socket connections when a mailto is triggered

I have a React application that establishes a web socket connection.

A part of my application is trying to open a mail client through mailto:, like this:

window.location.href = "mailto:[email protected]";

Unlike Chrome, Firefox is forcing websocket to close, every time an assignment to location.href occurs. To better illustrate the problem, here is a quick demo application.

import * as React from "react";
import "./styles.css";

export default function App() {
  React.useEffect(() => {
    console.log("component mount!");

    return () => {
      console.log("component unmounting");
    };
  }, []);

  React.useEffect(() => {
    const webSocket = new WebSocket("wss://echo.websocket.events/");

    webSocket.onopen = (...args) => {
      console.log("ws opened!", args);
    };
    webSocket.onerror = (err) => {
      console.log("ws err received!", err);
    };
    webSocket.onclose = (err) => {
      console.log("ws closing!", err);
    };

    return () => {
      webSocket.close();
    };
  }, []);

  const openInDocument = React.useCallback(() => {
    window.location.href = "mailto:[email protected]";
  }, []);


  return (
    <div className="App">

      <button onClick={openInDocument}>Open mail client</button>
    </div>
  );
}
  • Running the demo on Chrome will open your default mail client as expected.
  • Firefox, will close the WS communication first, as seen on the console. Also note that the React component never gets unmounted!

I don't know if this is a bug on Firefox, (or is Chrome's laziness to destroy the ws connection).

I came up with 2 different solutions, but none of them feels correct to me:

  1. Open the mailto: link on a new tab, and close it afterwards.

Cons:

  • Popup blocker may invoke and block the mailto:
  • User will see a flash on her screen.
  1. Open the mailto: link on an iframe. Solution seems legit but it seems to me a bit dirty, plus I don't know if future browser versions will allow that behaviour.

Here is the sample code

import * as React from "react";
import "./styles.css";

export default function App() {
  const iframeRef = React.useRef<HTMLIFrameElement>(null);
  React.useEffect(() => {
    console.log("component mount!");

    return () => {
      console.log("component unmounting");
    };
  }, []);

  React.useEffect(() => {
    const webSocket = new WebSocket("wss://echo.websocket.events/");

    webSocket.onopen = (...args) => {
      console.log("ws opened!", args);
    };
    webSocket.onerror = (err) => {
      console.log("ws err received!", err);
    };
    webSocket.onclose = (err) => {
      console.log("ws closing!", err);
    };

    return () => {
      webSocket.close();
    };
  }, []);

  const openInDocument = React.useCallback(() => {
    window.location.href = "mailto:[email protected]";
  }, []);

  const openInNewWindow = React.useCallback(() => {
    const win = window.open("mailto:[email protected]");
    win?.close();
  }, []);

  const openThroughIframe = React.useCallback(() => {
    iframeRef.current?.setAttribute("src", "mailto:[email protected]");
  }, []);

  return (
    <div className="App">
      <iframe
        title="mailto opener"
        ref={iframeRef}
        style={{ width: 0, height: 0, border: 0 }}
      ></iframe>

      <button onClick={openInDocument}>Open mail client</button>
      <br />
      <button onClick={openInNewWindow}>
        Open mail client using new tab/window
      </button>
      <br />
      <button onClick={openThroughIframe}>Open through iframe</button>
    </div>
  );
}

and a codesandbox link

Can you suggest any better solution? Should I proceed with the iframe solution?



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source