public/content.js (393 lines of code) (raw):
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
/*global chrome*/
// Function to get full hostname from URL
function getBaseDomain(url) {
try {
const urlObj = new URL(url);
return urlObj.hostname;
} catch (error) {
return '';
}
}
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
sendMessageToPage(request);
sendResponse(true);
});
// Function to send a message to the webpage
function sendMessageToPage(request) {
window.postMessage({ type: 'FROM_CONTENT_SCRIPT', data: request }, '*');
}
// Add event listener to listen for messages from the web page
window.addEventListener('message', (event) => {
if (event.source === window) {
const { direction } = event.data;
if (direction === 'commit') {
handleCommitOperation(event);
} else if (direction === 'login') {
handleLoginOperation(event);
} else if (direction === 'custom') {
handleCustomOperation(event);
}
}
});
// Handle commit operation and display the modal
function handleCommitOperation(event) {
const { amount, data, recipient, styles } = event.data;
// Ensure the amount is present before proceeding
if (amount.trim() !== '') {
const modal = createOrUpdateModal('COMMIT', amount, { amount, data, recipient }, styles);
// Event delegation handles event listeners
}
}
// Handle custom operation and display the modal
function handleCustomOperation(event) {
const { data, recipient, styles, customMessage } = event.data;
const amount = '1'; // Automatically assigned value 1
const transactionData = { amount, data, recipient };
const modal = createOrUpdateModal('', amount, transactionData, styles, true, customMessage || '');
// Event delegation handles event listeners
}
// Handle login operation and display the login modal
function handleLoginOperation(event) {
// Create or update the login modal
const modal = createOrUpdateLoginModal();
// Event delegation handles event listeners
}
// Create or update the login modal
function createOrUpdateLoginModal() {
let modal = document.getElementById('resVaultLoginModal');
const modalContent = generateLoginModalContent();
if (!modal) {
// Create the modal if it doesn't exist
modal = document.createElement('div');
modal.id = 'resVaultLoginModal';
modal.style.cssText = `
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
z-index: 1000;
display: block;
`;
modal.innerHTML = modalContent;
document.body.appendChild(modal);
// Attach a single event listener to the modal for event delegation
modal.addEventListener('click', async (event) => {
if (event.target.id === 'resVaultLoginModalClose') {
modal.style.display = 'none';
} else if (event.target.id === 'resVaultLoginModalAuthenticate') {
const isConnected = await checkConnectionStatus();
if (isConnected) {
handleLoginTransactionSubmit();
} else {
sendMessageToPage('error');
}
modal.style.display = 'none';
}
});
} else {
// Update the modal content if it already exists
modal.innerHTML = modalContent;
modal.style.display = 'block'; // Ensure the modal is visible again
}
return modal;
}
// Generate the HTML content for the login modal
function generateLoginModalContent() {
return `
<div id="resVaultLoginModalContent" style="
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: #0f0638;
padding: 20px;
border-radius: 10px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
z-index: 1001;
font-family: Poppins, sans-serif;
width: 300px;
">
<div style="width: calc(100%);
padding: 20px;
background-color: #291f57;
border-radius: 15px;
margin-bottom: 20px;
box-shadow: 5px 5px 35px -14px rgba(0,0,0,.17);
text-align: center;">
<p style="
color: #fff;
font-size: 14px;
font-weight: bold;
margin: 0;
">
Authenticate with Res<span style="color: #47e7ce;">Vault</span>
</p>
</div>
<div style="display: flex; justify-content: space-between;">
<button id="resVaultLoginModalClose" style="
background-color: #291f57;
border: 1px #47e7ce solid;
color: white;
border: none;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
width: 50%;
box-sizing: border-box;
margin-right: 5px;">
Close
</button>
<button id="resVaultLoginModalAuthenticate" style="
background: linear-gradient(60deg, #47e7ce, #4fa8c4);
color: white;
border: none;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
width: 50%;
box-sizing: border-box;">
Authenticate
</button>
</div>
</div>
`;
}
// Check if the user is connected to the website and net
function checkConnectionStatus() {
return new Promise((resolve) => {
const domain = window.location.hostname;
chrome.storage.local.get(['keys', 'connectedNets'], (result) => {
const keys = result.keys || {};
const connectedNets = result.connectedNets || {};
const net = connectedNets[domain];
if (net && keys[domain] && keys[domain][net]) {
resolve(true);
} else {
resolve(false);
}
});
});
}
// Handle login transaction submission and send data to background script
function handleLoginTransactionSubmit() {
chrome.runtime.sendMessage(
{
action: 'submitLoginTransaction',
},
(response) => {
if (response) {
// Send the response to the page script
window.postMessage({ type: 'FROM_CONTENT_SCRIPT', data: response }, '*');
}
}
);
}
// Create or update the modal with the necessary transaction details
function createOrUpdateModal(operation, amount, transactionData, styles = {}, isCustom = false, customMessage = '') {
let modal = document.getElementById('resVaultModal');
const modalContent = generateModalContent(operation, amount, styles, isCustom, customMessage);
if (!modal) {
// Create the modal if it doesn't exist
modal = document.createElement('div');
modal.id = 'resVaultModal';
modal.style.cssText = `
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
z-index: 1000;
display: block;
`;
modal.innerHTML = modalContent;
document.body.appendChild(modal);
// Attach a single event listener to the modal for event delegation
modal.addEventListener('click', (event) => {
if (event.target.id === 'resVaultModalClose') {
modal.style.display = 'none';
} else if (event.target.id === 'resVaultModalSubmit') {
handleTransactionSubmit(modal.transactionData); // Use modal.transactionData
modal.style.display = 'none';
}
});
} else {
// Update the modal content if it already exists
modal.innerHTML = modalContent;
modal.style.display = 'block'; // Ensure the modal is visible again
}
// Update transactionData on the modal element
modal.transactionData = transactionData;
// Apply user styles if any
applyStylesToModal(modal, styles);
return modal;
}
// Generate the HTML content for the commit or custom modal
function generateModalContent(operation, amount, styles = {}, isCustom = false, customMessage = '') {
if (isCustom) {
return `
<div id="resVaultModalContent" style="
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: #0f0638;
padding: 20px;
border-radius: 10px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
z-index: 1001;
font-family: Poppins, sans-serif;
width: 300px;
">
<div id="resVaultMessageBox" style="width: calc(100%);
padding: 20px;
background-color: #291f57;
border-radius: 15px;
margin-bottom: 20px;
box-shadow: 5px 5px 35px -14px rgba(0,0,0,.17);">
<form id="Form" style="margin: auto;">
<div style="width: 100%; margin-bottom: 10px;">
<div id="customMessage" style="padding: 0;
width: 100%;
margin: 0;
overflow: hidden;
border-radius: 0px;
color: #fff;
text-align: center;
">
<p>
${customMessage || ''}
</p>
</div>
</div>
<p id="poweredBy" style="font-size: small; color: #fff">Powered by Res<strong style="color: #47e7ce">Vault</strong></p>
</form>
</div>
<span style="display: flex;">
<button id="resVaultModalClose" style="
background-color: #291f57;
border: none;
color: white;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
width: 50%;
box-sizing: border-box;
margin-right: 5px;">Cancel</button>
<button id="resVaultModalSubmit" style="
background: linear-gradient(60deg, #47e7ce, #4fa8c4);
color: white;
border: none;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
width: 50%;
box-sizing: border-box;">Submit</button>
</span>
</div>`;
} else {
return `
<div id="resVaultModalContent" style="
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: #0f0638;
padding: 20px;
border-radius: 10px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
z-index: 1001;
font-family: Poppins, sans-serif;
width: 300px;
">
<div id="resVaultMessageBox" style="width: calc(100%);
padding: 20px;
background-color: #291f57;
border-radius: 15px;
margin-bottom: 20px;
box-shadow: 5px 5px 35px -14px rgba(0,0,0,.17);">
<form id="Form" style="margin: auto;">
<div style="width: 100%; margin-bottom: 10px;">
<div style="padding: 0;
width: 100%;
margin: 0;
overflow: hidden;
border-radius: 0px;
color: #fff;
border-bottom: 1px rgba(255, 255, 255, 0.3) solid;">
<p>
Operation: ${operation}
</p>
</div>
</div>
<div style="width: 100%;
margin-bottom: 10px; display: flex; align-items: center; justify-content: space-between;">
<p id="amountDisplay" style="width: calc(((100% / 3) * 2) - 35px);
border: none;
background-color: transparent;
padding: 10px 0px;
border-radius: 0px;
border-bottom: 1px rgba(255, 255, 255, 0.3) solid;
color: #fff;
text-align: center;
">
${amount}
</p>
<div style="display: flex;
align-items: center;">
<div style="display: block; font-family: Arial, Helvetica, sans-serif; font-size: 20px; font-weight: bold; color: #f0f0f0; background-color: #808080; border-radius: 50%; width: 30px; height: 30px; display: flex; align-items: center; justify-content: center; margin: 10px; transform: rotate(20deg);">R</div>
<span style="color: #fff; font-weight: 600; padding-left: 5px;">RoK</span>
</div>
</div>
<p style="font-size: small; color: #fff">Powered by Res<strong style="color: #47e7ce">Vault</strong></p>
</form>
</div>
<span style="display: flex;">
<button id="resVaultModalClose" style="
background-color: #291f57;
border: none;
color: white;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
width: 50%;
box-sizing: border-box;
margin-right: 5px;">Cancel</button>
<button id="resVaultModalSubmit" style="
background: linear-gradient(60deg, #47e7ce, #4fa8c4);
color: white;
border: none;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
width: 50%;
box-sizing: border-box;">Submit</button>
</span>
</div>`;
}
}
// Apply user styles to the modal elements
function applyStylesToModal(modal, styles) {
const allowedSelectors = [
'#resVaultModalContent',
"#resVaultMessageBox",
'#amountDisplay',
'#resVaultModalClose',
'#resVaultModalSubmit',
'#customMessage',
'#poweredBy',
];
const allowedStyles = {
'#resVaultModalContent': ['background-color', 'padding', 'border-radius', 'box-shadow', 'font-family', 'width', 'position', 'top', 'left', 'transform', 'z-index'],
'#amountDisplay': ['color', 'font-size', 'text-align', 'border', 'background-color', 'padding', 'border-radius'],
'#resVaultMessageBox': ['width', 'padding', 'background-color', 'border-radius', 'margin-bottom', 'box-shadow'],
'#resVaultModalClose': ['background-color', 'color', 'border', 'padding', 'border-radius', 'cursor', 'width'],
'#resVaultModalSubmit': ['background-color', 'color', 'border', 'padding', 'border-radius', 'cursor', 'width'],
'#customMessage': ['color', 'font-size', 'text-align', 'background-color', 'padding', 'border-radius'],
'#poweredBy': ['color', 'font-size', 'text-align'],
};
for (const selector in styles) {
if (!allowedSelectors.includes(selector)) {
continue; // Skip disallowed selectors
}
const elements = modal.querySelectorAll(selector);
elements.forEach(element => {
const styleObj = styles[selector];
const allowedProperties = allowedStyles[selector] || [];
for (const styleName in styleObj) {
if (allowedProperties.includes(styleName)) {
// Apply the style with !important to ensure precedence
element.style.setProperty(styleName, styleObj[styleName], 'important');
}
}
});
}
}
// Handle transaction submission and send data to background script
function handleTransactionSubmit({ amount, data, recipient }) {
chrome.runtime.sendMessage(
{
action: 'submitTransaction',
amount,
data,
recipient,
},
(response) => {
if (response) {
// Send the response to the page script
window.postMessage({ type: 'FROM_CONTENT_SCRIPT', data: response }, '*');
}
}
);
}