Skip to content

Commit

Permalink
FXVPN-337 Return of the loading screen (#180)
Browse files Browse the repository at this point in the history
If you look closely on the Screen Recording Shared with QA, you see the
Red dot on the Icon not appearing for quite a while.
This indicates firefox takes it's time inizializing the background
script.

You can easily replicate that by dropping ` await new Promise((r) =>
setTimeout(r, Math.round(Math.random() * 5000)));` somewhere in
`main::init()`.

Currently if we open the message port before the background script is
done inizializing we are screaming into the void.
Sooo, let's allow ipc.js to close and re-open ports if there is noone
answering on the other side.

For that ofc we need to show "Something" so i'm bringing back the
loading screen we added and then removed. 🙈

Here is a demo with a random delay. 


![output](https://github.com/user-attachments/assets/806c9150-c0be-4555-a5ab-27a48d92b9a9)
  • Loading branch information
strseb authored Jan 16, 2025
1 parent 3bf229d commit 49b02f7
Show file tree
Hide file tree
Showing 5 changed files with 196 additions and 10 deletions.
48 changes: 39 additions & 9 deletions src/shared/ipc.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,21 +75,51 @@

import { IBindable, property, ReadOnlyProperty } from "./property.js";

export const getExposedObject = async (name = "ohno") => {
export const getExposedObject = async (name = "ohno", maxRetrys = 20) => {
// TODO: Lesley noted this port might disconnect
// So we should handle this here and auto hook up a new
// port with the message port JUST IN CASE.
/** @type {browser.runtime.Port} */
const port = globalThis.chrome.runtime.connect({ name });

const messagePort = toMessagePort(port);
/** @type {IPC_INFO_MESSAGE?} */
let info = null;
let port;
/** @type {MessagePort?} */
let messagePort = null;
for (let retryCount = 0; retryCount <= maxRetrys && !info; retryCount++) {
if (!port) {
port = globalThis.chrome.runtime.connect({ name });
}
messagePort = toMessagePort(port);
try {
// @ts-ignore
info = await requestFromPort(
messagePort,
{
...new IPC_INFO_MESSAGE(),
id: makeID(),
},
100 + Math.pow(2, Math.min(retryCount, 10)) // Scale from 1ß2ms -> 1124ms Delay between attempts
);
} catch (error) {
if (port) {
port.disconnect();
port = null;
}
console.log(
`Attempt ${retryCount}/${maxRetrys} to fetch ${name}-Info failed.`
);
}
}
if (info == null) {
throw new Error(`Background script did not Provide info for ${name}`);
return;
}
if (!messagePort) {
throw new Error(`Failed to establish a message port`);
return;
}

/** @type {IPC_INFO_MESSAGE} */
// @ts-ignore
const info = await requestFromPort(messagePort, {
...new IPC_INFO_MESSAGE(),
id: makeID(),
});
return await createReplica(info.data, messagePort);
};

Expand Down
4 changes: 4 additions & 0 deletions src/ui/browserAction/backend.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { getExposedObject } from "../../shared/ipc.js";
const t0 = performance.now();

/**
* Import Types
Expand Down Expand Up @@ -45,3 +46,6 @@ export const butterBarService = await getExposedObject("ButterBarService");
export const telemetry = await getExposedObject("Telemetry");

export const ready = Promise.all([vpnController, proxyHandler]);

var t1 = performance.now();
console.log("Inizializing ipc took " + (t1 - t0) + " milliseconds.");
139 changes: 139 additions & 0 deletions src/ui/browserAction/popup-placeholder.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import { html, css, LitElement } from "../../vendor/lit-all.min.js";
/**
* This Element shows a Placeholder Animation until the
* Backend Script is finalized, after finalization, it will remove itself from the DOM.
*/
export class PopupPlaceHolder extends LitElement {
static properties = {
// Show placeholder until this promise is resolved.
untilPromise: { type: Object },
};
constructor() {
super();
// This module uses top-level await, so if this module is loaded,
// we know ipc is working
this.untilPromise = import("./backend.js");
}
connectedCallback() {
super.connectedCallback();
this.untilPromise.then(() => {
// Goodbye!
this.parentElement.removeChild(this);
});
}

render() {
return html`
<vpn-titlebar title="Loading"> </vpn-titlebar>
<div class="outer">
<main class="box">
<div style="filter:grayscale();">
<div class="" style="position:relative;">
<span class="placeholder placeholderimg"></span>
</div>
</div>
<div class="infobox">
<h1 class="placeholder">Loading</h1>
<p class="placeholder">Loading</p>
</div>
<button class="pill placeholder"></button>
</main>
</div>
`;
}

static styles = css`
:host {
}
main {
padding: var(--padding-default);
display: flex;
}
* {
color: var(--text-color-primary);
font-family: var(--font-family);
}
.outer {
padding: 15px;
}
:host {
font-size: 1rem;
font-family: var(--font-family);
--default-padding: 16px;
}
.placeholderimg {
width: 50px;
height: 50px;
display: block;
top: 0px;
z-index: 10;
mix-blend-mode: initial;
border-radius: 30px;
}
.box {
border-radius: 8px;
background: lch(from var(--panel-bg-color) calc(l + 5) c h);
display: flex;
align-items: center;
justify-content: space-between;
flex-direction: row;
}
.infobox {
flex: 4;
padding: 0px 10px;
}
img {
margin-right: var(--default-padding);
position: relative;
}
h1,
p {
color: transparent;
border-radius: 5px;
}
h1 {
font-size: 18px;
line-height: 20px;
font-weight: 700;
}
p {
line-height: 14px;
font-weight: 400;
opacity: 0.7;
line-height: 14px;
}
.pill {
width: 45px;
height: 24px;
border-radius: 30px;
border: none;
background: #6d6d6e;
position: relative;
}
.placeholder {
background: linear-gradient(
90deg,
rgba(105, 105, 105, 1) 20%,
rgba(255, 255, 255, 1) 48%,
rgba(105, 105, 105, 1) 80%
);
background-size: 800% 100%;
animation: gradient-animation 0.6s infinite;
}
@keyframes gradient-animation {
0% {
background-position: 0% 0%;
}
100% {
background-position: 100% 0%;
}
}
`;
}
customElements.define("popup-placeholder", PopupPlaceHolder);
10 changes: 10 additions & 0 deletions src/ui/browserAction/popup.css
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,13 @@ html {
.needsSlotted > * {
display: none;
}

.stack {
display: grid;
grid-template-rows: 1fr;
grid-template-columns: 1fr;
}
.stack > * {
grid-row: 1 / 2;
grid-column: 1 / 2;
}
5 changes: 4 additions & 1 deletion src/ui/browserAction/popup.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<link rel="stylesheet" href="popup.css" />
<link rel="stylesheet" href="../variables.css" />
<!-- Load placeholder sync so it's ready first -->
<script src="./popup-placeholder.js" type="module"></script>
<script src="./popupPage.js" type="module"></script>
<script src="./popupConditional.js" type="module"></script>
<script src="../../components/message-screen.js" type="module"></script>
Expand All @@ -18,7 +20,8 @@

<title>Mozilla VPN</title>
</head>
<body>
<body class="stack">
<popup-placeholder></popup-placeholder>
<popup-condview id="conditional-view" class="needsSlotted">
<needs-update-message-screen
slot="MessageUpdateClient"
Expand Down

0 comments on commit 49b02f7

Please sign in to comment.