Skip to content
This repository was archived by the owner on Mar 2, 2026. It is now read-only.
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions _raw/manifest/manifest.dev.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,19 @@
"background": {
"service_worker": "background.js"
},
"content_scripts": [
{
"js": ["content-script.js", "script.js"],
"matches": ["file://*/*", "http://*/*", "https://*/*"]
}
],
"content_security_policy": {
"extension_pages": "script-src 'self' 'wasm-unsafe-eval'; object-src 'self' 'wasm-unsafe-eval';"
},
"permissions": ["storage", "activeTab", "tabs", "notifications", "identity", "camera"],
"host_permissions": ["https://api.mixpanel.com/*"],
"permissions": [
"storage",
"activeTab",
"tabs",
"notifications",
"identity",
"camera",
"scripting"
],
"host_permissions": ["<all_urls>"],
Comment thread
tombeckenham marked this conversation as resolved.
"web_accessible_resources": [
{
"resources": [
Expand Down
19 changes: 11 additions & 8 deletions _raw/manifest/manifest.pro.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,20 @@
"background": {
"service_worker": "sw.js"
},
"content_scripts": [
{
"js": ["content-script.js", "script.js"],
"matches": ["file://*/*", "http://*/*", "https://*/*"]
}
],
"content_security_policy": {
"extension_pages": "script-src 'self' 'wasm-unsafe-eval'; object-src 'self' 'wasm-unsafe-eval';"
},
"permissions": ["storage", "activeTab", "tabs", "notifications", "identity", "camera", "*://*/*"],
"host_permissions": ["https://api.mixpanel.com/*"],
"permissions": [
"storage",
"activeTab",
"tabs",
"notifications",
"identity",
"camera",
"scripting",
"*://*/*"
],
"host_permissions": ["<all_urls>"],
"web_accessible_resources": [
{
"resources": [
Expand Down
1 change: 1 addition & 0 deletions build/webpack.common.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const config = (env: { config: 'dev' | 'pro' | 'none' }): webpack.Configuration
entry: {
background: paths.rootResolve('src/background/index.ts'),
'content-script': paths.rootResolve('src/content-script/index.ts'),
'message-bridge': paths.rootResolve('src/content-script/message-bridge.ts'),
pageProvider: paths.rootResolve('src/content-script/pageProvider/eth/index.ts'),
// pageProvider: paths.rootResolve(
// 'node_modules/@rabby-wallet/page-provider/dist/index.js'
Expand Down
1 change: 0 additions & 1 deletion src/background/controller/provider/rpcFlow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ const flowContext = flow
mapMethod !== 'walletWatchAsset' &&
mapMethod !== 'walletConnect' &&
mapMethod !== 'walletDisconnect' &&
mapMethod !== 'walletConnect' &&
!Reflect.getMetadata('SAFE', providerController, mapMethod)
) {
if (!permissionService.hasPermission(origin) || !(await Wallet.isUnlocked())) {
Comment on lines 60 to 66

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The condition check for mapMethod !== 'walletConnect' appears twice in the original code, and one instance was removed. However, this could indicate that there was a need to check for different types of wallet connections. Consider reviewing if removing one of these checks was intentional and doesn't break any functionality.

Expand Down
2 changes: 2 additions & 0 deletions src/background/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import {
tokenListService,
remoteConfigService,
newsService,
contentScriptInjectionService,
} from './service';
import { getFirbaseConfig } from './utils/firebaseConfig';
import { setEnvironmentBadge } from './utils/setEnvironmentBadge';
Expand Down Expand Up @@ -131,6 +132,7 @@ async function restoreAppState() {
await tokenListService.init();
await remoteConfigService.init();
await newsService.init();
await contentScriptInjectionService.init();
// rpcCache.start();

appStoreLoaded = true;
Expand Down
103 changes: 103 additions & 0 deletions src/background/service/content-script-injection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { consoleError, consoleLog } from '@/shared/utils/console-log';

/*
* This content script is injected programmatically because
* MAIN world injection does not work properly via manifest
* https://bugs.chromium.org/p/chromium/issues/detail?id=634381
*/

class ContentScriptInjectionService {
private registeredScripts = new Set<string>();

init = async () => {
// Register message bridge first and wait a bit to ensure it's loaded
await this.registerMessageBridge();

// Small delay to ensure message bridge is loaded and has injected UUIDs
await new Promise((resolve) => setTimeout(resolve, 50));

// Then register the scripts that run in MAIN world
await this.registerPageProvider();
await this.registerFlowScript();
};

private registerMessageBridge = async () => {
try {
await chrome.scripting.registerContentScripts([
{
id: 'messageBridge',
matches: ['file://*/*', 'http://*/*', 'https://*/*'],
js: ['message-bridge.js'],
runAt: 'document_start',
world: 'ISOLATED',
allFrames: true,
},
]);
this.registeredScripts.add('messageBridge');
consoleLog('Successfully registered message bridge content script');
} catch (err) {
consoleError(`Failed to register message bridge content script: ${err}`);
}
};

private registerPageProvider = async () => {
try {
await chrome.scripting.registerContentScripts([
{
id: 'pageProvider',
matches: ['file://*/*', 'http://*/*', 'https://*/*'],
js: ['pageProvider.js'],
runAt: 'document_start',
world: 'MAIN',
allFrames: true,
},
]);
this.registeredScripts.add('pageProvider');
consoleLog('Successfully registered pageProvider content script');
} catch (err) {
consoleError(`Failed to register pageProvider content script: ${err}`);
}
};

private registerFlowScript = async () => {
try {
await chrome.scripting.registerContentScripts([
{
id: 'flowScript',
matches: ['file://*/*', 'http://*/*', 'https://*/*'],
js: ['script.js'],
runAt: 'document_start',
world: 'MAIN',
allFrames: true,
},
]);
this.registeredScripts.add('flowScript');
consoleLog('Successfully registered flow script content script');
} catch (err) {
consoleError(`Failed to register flow script content script: ${err}`);
}
};

// Method to unregister scripts if needed
unregisterAllScripts = async () => {
try {
const registeredIds = Array.from(this.registeredScripts);
if (registeredIds.length > 0) {
await chrome.scripting.unregisterContentScripts({
ids: registeredIds,
});
this.registeredScripts.clear();
consoleLog('Successfully unregistered all content scripts');
}
} catch (err) {
consoleError(`Failed to unregister content scripts: ${err}`);
}
};

// Method to check if scripts are registered
getRegisteredScripts = () => {
return Array.from(this.registeredScripts);
};
}

export default new ContentScriptInjectionService();
1 change: 1 addition & 0 deletions src/background/service/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export { default as evmNftService } from './nft-evm';
export { default as googleDriveService } from './googleDrive';
export { default as googleSafeHostService } from './googleSafeHost';
export { default as signTextHistoryService } from './signTextHistory';
export { default as contentScriptInjectionService } from './content-script-injection';
export { default as i18n } from './i18n';
export { default as newsService } from './news';
export { default as tokenListService } from './token-list';
Expand Down
138 changes: 7 additions & 131 deletions src/content-script/index.ts
Original file line number Diff line number Diff line change
@@ -1,134 +1,10 @@
import { nanoid } from 'nanoid';
import { v4 as uuid } from 'uuid';
// This content script is no longer used.
// We now inject scripts directly from the background service worker using chrome.scripting API.
// This file is kept as a stub to avoid build issues.

import { Message } from '@/shared/utils/messaging';
import { consoleLog } from '@/shared/utils/console-log';

const channelName = nanoid();
consoleLog('Legacy content script loaded but not used.');

const injectProviderScript = async (isDefaultWallet) => {
// Set local storage variables
await localStorage.setItem('frw:channelName', channelName);
await localStorage.setItem('frw:isDefaultWallet', isDefaultWallet);
await localStorage.setItem('frw:uuid', uuid());

const container = document.head || document.documentElement;
const scriptElement = document.createElement('script');
scriptElement.id = 'injectedScript';
scriptElement.setAttribute('src', chrome.runtime.getURL('pageProvider.js'));

container.insertBefore(scriptElement, container.children[0]);

return scriptElement;
};

injectProviderScript(true); // Initial call to check and inject if needed

const initListener = (channelName: string) => {
const { BroadcastChannelMessage, PortMessage } = Message;
const pm = new PortMessage().connect();
const bcm = new BroadcastChannelMessage(channelName).listen((data) => pm.request(data));

// background notification
pm.on('message', (data) => bcm.send('message', data));

// pm.request({
// type: EVENTS.UIToBackground,
// method: 'getScreen',
// params: { availHeight: screen.availHeight },
// });

document.addEventListener('beforeunload', () => {
bcm.dispose();
pm.dispose();
});
};

initListener(channelName);

// because the content script run at document start
setTimeout(() => {
document.body.setAttribute('data-channel-name', channelName);
}, 0);

/**
* Inject script
*/
// Listener for messages from window/FCL

function injectScript(file_path, tag) {
const node = document.getElementsByTagName(tag)[0];
const script = document.createElement('script');
script.setAttribute('type', 'text/javascript');
script.setAttribute('src', file_path);
node.appendChild(script);
chrome.runtime.sendMessage({ type: 'LILICO:CS:LOADED' });
}

injectScript(chrome.runtime.getURL('script.js'), 'body');

// Listener for messages from window/FCL
window.addEventListener('message', function (event) {
chrome.runtime.sendMessage(event.data);
});

// Listener for Custom Flow Transaction event from FCL send
// window.addEventListener('FLOW::TX', function (event) {
// // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// // @ts-ignore: Event detail
// chrome.runtime.sendMessage({type: 'FLOW::TX', ...event.detail})
// })

const extMessageHandler = (msg, _sender) => {
if (msg.type === 'FCL:VIEW:READY') {
if (window) {
window.postMessage(JSON.parse(JSON.stringify(msg || {})), '*');
}
}

if (msg.f_type && msg.f_type === 'PollingResponse') {
if (window) {
window.postMessage(JSON.parse(JSON.stringify({ ...msg, type: 'FCL:VIEW:RESPONSE' })), '*');
}
}

if (msg.data?.f_type && msg.data?.f_type === 'PreAuthzResponse') {
if (window) {
window.postMessage(JSON.parse(JSON.stringify({ ...msg, type: 'FCL:VIEW:RESPONSE' })), '*');
}
}

if (msg.type === 'FCL:VIEW:CLOSE') {
if (window) {
window.postMessage(JSON.parse(JSON.stringify(msg || {})), '*');
}
}

if (msg.type === 'FLOW::TX') {
if (window) {
window.postMessage(JSON.parse(JSON.stringify(msg || {})), '*');
}
}

if (msg.type === 'LILICO:NETWORK') {
if (window) {
window.postMessage(JSON.parse(JSON.stringify(msg || {})), '*');
}
}

return true;
};

/**
* Fired when a message is sent from either an extension process or another content script.
*/
chrome.runtime.onMessage.addListener(extMessageHandler);

const wakeup = function () {
setTimeout(function () {
chrome.runtime.sendMessage('ping', function () {
return false;
});
wakeup();
}, 2000);
};
wakeup();
// Export empty object to satisfy module requirements
export {};
Loading
Loading