Skip to content

Commit

Permalink
feat: add connect per site into the web extension (#3626)
Browse files Browse the repository at this point in the history
  • Loading branch information
Cifko authored Nov 29, 2021
1 parent 3a80adf commit 75aa0a3
Show file tree
Hide file tree
Showing 45 changed files with 39,554 additions and 203 deletions.
141 changes: 118 additions & 23 deletions applications/tari_web_extension/public/background.js
Original file line number Diff line number Diff line change
@@ -1,52 +1,136 @@
console.log("background.js execute");

let selectedAsset = undefined;
let credentials = undefined;
let accounts = []; // Accounts registered
let triggered_port = []; // All the triggered ports
let connecting = {}; // Callbacks for the connect requests

const login = () => ({ successful: true, token: "token" });
// I'm not sure if this changes yet
const extension_url = "chrome-extension://kljpkkadbelknhmpabjkkhmljaebnopo";

const getAssets = () => ({
let allowedOrigins = {
[extension_url]: true,
};

const createAccount = (origin) => {
const account = "0xDEADBEEF";
accounts = [account];
return { successful: true, accounts };
};

const connect = (origin, sendResponse) => {
// This will popup a connect request, we store the callback
let callback_id = Math.random();
connecting[callback_id] = { origin, sendResponse };
window.open(
`${extension_url}/index.html#/connecting/${btoa(origin)}/${callback_id}`,
"_blank",
"width=400,height=600,titlebar=no,status=no,scrollbars=no,resizable=no,menubar=no,toolbar=no"
);
};

const connectSite = (origin, callback_id) => {
// Call the stored callback from connect function
connecting?.[callback_id]?.sendResponse?.({
successful: true,
payload: 0xdeadbeef,
});
// Add origin to allowed urls
const original_origin = connecting?.[callback_id]?.origin;
console.log("adding to allowed origins", original_origin);
if (original_origin) {
allowedOrigins[original_origin] = true;
}
triggered_port.forEach(({ port, origin }) => {
// We send it only to the port that belongs to this origin
if (origin === original_origin) {
port.postMessage({
event: "accountChange",
payload: { accounts },
});
}
});
};

const getConnectedSites = (origin) => {
if (origin in allowedOrigins) {
return {
successful: true,
// We don't want to expose the permission for the extension itself
sites: Object.keys(allowedOrigins)
.filter((site) => site !== extension_url)
.reduce(
(filtered, site) => ({ ...filtered, [site]: allowedOrigins[site] }),
{}
),
};
}
return { successful: false, sites: {} };
};

const getAccounts = (origin) => {
if (origin in allowedOrigins)
return { successful: true, payload: { accounts } };
else return { successful: false, payload: { accounts: [] } };
};

const getAssets = (origin) => ({
successful: true,
assets: ["asset1", "asset2"],
selected: selectedAsset,
});

const selectAsset = (request) => {
const selectAsset = (origin, request) => {
selectedAsset = request.name;
return { successful: true, selected: selectedAsset };
return { successful: true, payload: { selected: selectedAsset } };
};

const getSelectedAsset = () => ({ successful: true, selected: selectedAsset });

const loginRefresh = () => ({ successful: !!credentials, token: credentials });
const getSelectedAsset = (origin) => ({
successful: true,
payload: { selected: selectedAsset },
});

const getSeedWords = () => ({
const getSeedWords = (origin) => ({
successful: true,
seedWords:
"theme panther ladder custom field aspect misery shine bundle worry senior velvet brush tourist glide jump example vanish embody enemy struggle air extend empty",
payload: {
seedWords:
"theme panther ladder custom field aspect misery shine bundle worry senior velvet brush tourist glide jump example vanish embody enemy struggle air extend empty",
},
});

function messageCallback(request, sender, sendResponse) {
console.log(request);
const { origin } = sender;
console.log("messageCallback", request, origin);
switch (request?.action) {
case "tari-login":
credentials = "token";
sendResponse(login());
case "tari-create-account":
sendResponse(createAccount(origin));
break;
case "tari-connect": // Connecting app to the web extension
connect(origin, sendResponse);
break;
case "tari-connect-site":
connectSite(origin, request?.callback_id);
break;
case "tari-get-connected-sites":
sendResponse(getConnectedSites(origin));
break;
case "tari-get-accounts":
sendResponse(getAccounts(origin));
break;
case "tari-get-assets":
sendResponse(getAssets());
sendResponse(getAssets(origin));
break;
case "tari-select-asset":
sendResponse(selectAsset(request));
sendResponse(selectAsset(origin, request));
break;
case "tari-get-selected-asset":
sendResponse(getSelectedAsset());
sendResponse(getSelectedAsset(origin));
break;
case "tari-login-refresh":
sendResponse(loginRefresh());
sendResponse(loginRefresh(origin));
break;
case "tari-get-seedwords":
sendResponse(getSeedWords());
sendResponse(getSeedWords(origin));
break;
default:
console.log("unknown message", request?.action);
Expand All @@ -55,15 +139,26 @@ function messageCallback(request, sender, sendResponse) {
}

chrome.runtime.onConnect.addListener(function (port) {
if (port.name === "tari-port") {
port.onMessage.addListener(function (data) {
messageCallback(data, {}, (response) => {
if (port.name === "tari-port-injected") {
// We received message from the content.js script.
port.onMessage.addListener(function ({ data, origin }) {
messageCallback(data, { origin: origin }, (response) => {
port.postMessage({
...response,
id: data.id,
});
});
});
} else if (port.name === "tari-port-triggered") {
port.onDisconnect.addListener(function (delete_port) {
triggered_port = triggered_port.filter(
({ port, _origin }) => port !== delete_port
);
});
port.onMessage.addListener(function (origin) {
// Add ports once we know it origin
triggered_port.push({ port, origin });
});
}
});

Expand Down
91 changes: 70 additions & 21 deletions applications/tari_web_extension/public/content.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,17 @@ console.log("content.js executed");
function script() {
// Injected script into the website, to add window.tari
let promises = {};

function createPromise(payload, id) {
return new Promise((resolve, reject) => {
promises[id] = { resolve, reject };
window.postMessage({
id: id,
...payload,
});
});
}

function createTimeoutPromise(payload, timeout_ms = 1000) {
let id = Math.random();
const timeout = new Promise((_resolve, reject) =>
Expand All @@ -11,38 +22,62 @@ function script() {
reject("timed out");
}, timeout_ms)
);
const response = new Promise((resolve, reject) => {
promises[id] = { resolve, reject };
window.postMessage({
id: id,
...payload,
});
});
return Promise.race([timeout, response]);
return Promise.race([timeout, createPromise(payload, id)]);
}

class Tari {
constructor() {
console.log("initiating tari");
this.listeners = {};
}
connect() {
return createPromise({ action: "tari-connect" });
}
checkAccounts() {
return createTimeoutPromise({ action: "tari-get-accounts" });
}
getSelectedAsset(timeout_ms = 1000) {
return createTimeoutPromise(
{ action: "tari-get-selected-asset" },
timeout_ms
);
}
on(event, listener) {
// Add listener for background events.
if (!(event in this.listeners)) {
this.listeners[event] = listener;
}
}
triggerEvent(event, payload) {
// Trigger event from background process.
console.log("triggering event", event, "with payload", payload);
this.listeners[event]?.(payload);
}
}

window.tari = new Tari();

const handleBackgroundResponse = (data) => {
if (promises?.[data?.id]) {
console.log("handling response", data);
promises[data.id].resolve(data.payload);
delete promises[data.id];
}
};
const handleBackgroundEvent = ({ event, payload }) => {
window.tari.triggerEvent(event, payload);
};

window.addEventListener("message", (event) => {
if (
event?.data?.action === "tari-background-response" &&
promises?.[event?.data?.id]
) {
promises[event.data.id].resolve(event?.data?.selected);
delete promises[event.data.id];
switch (event?.data?.action) {
case "tari-background-response":
handleBackgroundResponse(event?.data?.payload);
break;
case "tari-background-event":
handleBackgroundEvent(event?.data?.payload);
break;
}
});
window.tari = new Tari();
console.log("injecting tari");
// The end of injected script
}
Expand All @@ -55,28 +90,42 @@ function inject(fn) {

inject(script);

let port = chrome.runtime.connect({ name: "tari-port" });
// We open two ports. One is for function calls from the injected script
let portInjected = chrome.runtime.connect({ name: "tari-port-injected" });
// The other is from event triggered on the background script
let portTriggered = chrome.runtime.connect({ name: "tari-port-triggered" });
// We need to tell background.js where is this port coming from
portTriggered.postMessage(window.location.origin);

// This listeners is catching message from the injected script and from content.js as well.
// So we need to check what types of message is it.
window.addEventListener(
// Receive message from injected script and resend it to the background.js
"message",
(event) => {
if (event.source != window) {
return;
}
if (
event?.data?.action?.startsWith("tari-") &&
event?.data?.action !== "tari-background-response"
event?.data?.action !== "tari-background-event" && // tari-background-event and tari-background-response ..
event?.data?.action !== "tari-background-response" // .. are handled in the injected script
) {
port.postMessage(event.data);
// If we received a tari-message, and it's not response message, send it to the background.js
portInjected.postMessage({ data: event.data, origin: event.origin });
}
},
false
);

port.onMessage.addListener(function (payload) {
portInjected.onMessage.addListener(function (payload) {
// Receive message from background.js and resend to the injected script
console.log("Received from background", payload);
window.postMessage({
action: "tari-background-response",
...payload,
payload,
});
});

portTriggered.onMessage.addListener(function (payload) {
window.postMessage({ action: "tari-background-event", payload });
});
21 changes: 11 additions & 10 deletions applications/tari_web_extension/src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,33 @@ import "./app.scss";
import React, { useEffect } from "react";
import { Navigate, Route, Routes } from "react-router";
import { useDispatch, useSelector } from "react-redux";
import {
getCredentials,
getCredentialsCalled,
refreshLogin,
} from "./redux/loginSlice";
import Onboarding from "./onboarding/Onboarding";
import { HashRouter } from "react-router-dom";
import Popup from "./popup/Popup";
import { getAccounts, getAccountsStatusSelector } from "./redux/accountSlice";
import Connecting from "./connecting/Connecting";

export default function App() {
const credentials = useSelector(getCredentials);
const credentialsCalled = useSelector(getCredentialsCalled);
const accountsStatus = useSelector(getAccountsStatusSelector);

const dispatch = useDispatch();
useEffect(() => {
dispatch(refreshLogin());
dispatch(getAccounts());
}, [dispatch]);
if (!credentials) {
if (!window.location.href.includes("#/onboarding") && credentialsCalled) {
console.log("accountsStatus", accountsStatus);
if (accountsStatus === "empty") {
if (!window.location.href.includes("#/onboarding")) {
window.open("#/onboarding");
window.close();
return <div></div>;
}
}
return (
<div className="main">
<HashRouter>
<Routes>
<Route path="/onboarding/*" element={<Onboarding />} />
<Route path="/connecting/:site/:id" element={<Connecting />} />
<Route path="/popup/*" element={<Popup />} />
<Route path="" element={<Navigate replace to="popup" />} />
</Routes>
Expand Down
Loading

0 comments on commit 75aa0a3

Please sign in to comment.