从网页发送消息到 chrome 分机
Send message from web page to chrome extension
我希望能够从我的 Web 应用程序向我的 chrome 扩展程序发送一条消息,以便更易于使用(发送身份验证令牌,这样用户就不必登录两次)。然而,在查看了 documentation 并阅读了一堆 SO 问题之后,我无法得到任何适合我的东西。
这是我的一些部分 manifest.json
:
"content_security_policy": "script-src 'self' https://330218550995.ngrok.io; object-src 'self'",
"background": {
"scripts": ["background.js"],
"persistent": false
},
"externally_connectable": {
"matches": [
"*://localhost/*",
"*://*.ngrok.io/*"
]
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"exclude_matches": [
"*://*.olympiatasks.com/*",
"https://app.olympiatasks.com/*",
"https://*.olympiatasks.com/*"
],
"css": ["/static/css/main.css"],
"js": [
"/static/js/runtime-main.js",
"/static/js/2.js",
"/static/js/main.js"
]
}
],
在 content script
内部,我这样做:
const ExtensionID = process.env.REACT_APP_EXTENSION_ID || '';
chrome?.runtime.connect(ExtensionID, { name: 'example' });
chrome?.runtime?.sendMessage('Hi from content script')
在 web page
内部,我这样做:
const ExtensionID = process.env.REACT_APP_EXTENSION_ID || "";
chrome.runtime.connect(ExtensionID, { name: "example" });
chrome?.runtime?.sendMessage(ExtensionID, "Hi from app");
然后这里是background.js
中的监听器:
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
console.log({ request })
});
chrome.runtime.onMessageExternal.addListener((request, sender, sendResponse) => {
console.log("Received message from " + sender + ": ", request);
sendResponse({ received: true }); //respond however you like
});
当我打开网站时,扩展被成功注入并且在 background.js
页面的开发控制台中我得到以下信息:
Hello world from background.js
{request: "Hi from content script"}
缺少“来自应用程序的嗨”,这意味着它不是 sent/received。我使用 ngrok
设置转发到我的应用程序认为:
- 域名为
localhost
- 协议不是
https
可能是问题所在,但如您所料,运气不佳。
到目前为止,我已经完成了以下工作:
- 在我的
manifest.json
中设置我的 externally_connectable
- 在
background.js
中设置 onMessageExternal
侦听器
- 使用显示的分机 ID 调用
runtime.sendMessage
in the docs
- 使用
https
网站进行安全连接
非常感谢对此问题的任何帮助
我们可以使用 content-script 和后台 js 文件在网站和 chrome 扩展程序之间同步身份验证令牌。
Inside web page
I do this
...
我不知道你是怎么做到的 web page
这是我将访问令牌从 web page
发送到 extension
所做的。
这是我的 content-script
文件。
/* eslint-disable no-undef */
const hostScript = () => {
// ----------------- Function Declarations -----------------
let listenGetTokenResponseFromWindow = () => {};
const sendMessagesToExtension = (msg, callback = null) => {
chrome.runtime.sendMessage(msg);
};
const sendMessagesToWindow = (msg, callback = null) => {
const { type } = msg;
switch (type) {
case ExtensionMessagesTypes.GetTokenFromWindow:
// Can not pass the function with window.postMessage. Only JSON object can be passed.
// So reset the listener
window.postMessage(msg, document.location.origin);
window.removeEventListener("message", listenGetTokenResponseFromWindow);
listenGetTokenResponseFromWindow = event => {
if (event.source !== window) return;
if (event.data.type === ExtensionMessagesTypes.GetTokenFromWindowResponse) {
const { payload } = event.data;
!!callback && callback(payload.token);
}
};
window.addEventListener("message", listenGetTokenResponseFromWindow);
break;
case ExtensionMessagesTypes.SetWindowToken:
window.postMessage(msg, document.location.origin);
!!callback && callback();
break;
default:
break;
}
};
const listenMessagesFromWindow = event => {
if (event.source !== window) return;
const msg = event.data;
Object.values(ExtensionMessagesTypes).includes(msg.type) && sendMessagesToExtension(msg);
};
const listenMessagesFromExtension = (msg, sender, response) => {
sendMessagesToWindow(msg, response);
return true; // means response is async
};
// ----------------- Listener Definitions -----------------
window.removeEventListener("message", listenMessagesFromWindow);
window.addEventListener("message", listenMessagesFromWindow);
chrome.runtime.onMessage.removeListener(listenMessagesFromExtension);
chrome.runtime.onMessage.addListener(listenMessagesFromExtension);
// Reset extension token as windows token
sendMessagesToWindow({ type: ExtensionMessagesTypes.GetTokenFromWindow }, token => {
sendMessagesToExtension({
type: ExtensionMessagesTypes.SetExtensionToken,
payload: { token }
});
});
};
这是 background.js
文件。
(() => {
chrome.runtime.onMessage.addListener((msg, sender, response) => {
switch (msg.type) {
case ExtensionMessagesTypes.GetTokens:
getAccessTokens(response);
break;
case ExtensionMessagesTypes.SetExtensionToken:
!!msg.payload && !!msg.payload.token && setAccessTokenToExtensionLocalStorage(msg.payload.token, response);
break;
case ExtensionMessagesTypes.SetWindowToken:
!!msg.payload && !!msg.payload.token && sendMessageFromExtensionToWindow(msg, response);
break;
}
});
})();
const sendMessageFromExtensionToWindow = (msg, callback = null) => {
chrome.tabs.query({ currentWindow: true, url: `${HostURL}/*` }, tabs => {
if (tabs.length < 1) {
!!callback && callback(null);
return;
}
if (msg.type === ExtensionMessagesTypes.GetTokenFromWindow) {
chrome.tabs.sendMessage(tabs[0].id, msg, token => {
!!callback && callback(token);
});
} else if (msg.type === ExtensionMessagesTypes.SetWindowToken) {
chrome.tabs.sendMessage(tabs[0].id, msg, () => {
!!callback && callback();
});
}
});
};
// Authentication
const setAccessTokenToExtensionLocalStorage = (access_token, callback = null) => {
chrome.storage.local.set({ access_token }, () => {
!!callback && callback();
});
};
const getAccessTokenFromChromeStorage = callback => {
chrome.storage.local.get(['access_token'], result => {
!!callback && callback(result.access_token);
});
};
const getAccessTokens = callback => {
getAccessTokenFromChromeStorage(accessTokenFromExtension => {
sendMessageFromExtensionToWindow({ type: ExtensionMessagesTypes.GetTokenFromWindow }, accessTokenFromWindow => {
callback({
accessTokenFromExtension: accessTokenFromExtension || '',
accessTokenFromWindow: accessTokenFromWindow || ''
});
});
});
};
const handleLogin = payload => {
//TODO: Handling refresh token
const { token } = payload;
if (!token) return;
setAccessTokenToExtensionLocalStorage(token);
};
const handleLogout = () => {
setAccessTokenToExtensionLocalStorage(null);
};
PS:您不需要 manifest.json 文件中的 externally_connectable
选项。
感谢@wOxxOm 的评论,我得以解决问题。
我引用:
Tentatively, since ngrok.io is in public suffix list it means it's basically like com which is forbidden in externally_connectable. Try using a more specific pattern for the site.
我更改了 URL 以使用一个更具体的 https://330218550995.ngrok.io/*
,现在可以使用了
我希望能够从我的 Web 应用程序向我的 chrome 扩展程序发送一条消息,以便更易于使用(发送身份验证令牌,这样用户就不必登录两次)。然而,在查看了 documentation 并阅读了一堆 SO 问题之后,我无法得到任何适合我的东西。
这是我的一些部分 manifest.json
:
"content_security_policy": "script-src 'self' https://330218550995.ngrok.io; object-src 'self'",
"background": {
"scripts": ["background.js"],
"persistent": false
},
"externally_connectable": {
"matches": [
"*://localhost/*",
"*://*.ngrok.io/*"
]
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"exclude_matches": [
"*://*.olympiatasks.com/*",
"https://app.olympiatasks.com/*",
"https://*.olympiatasks.com/*"
],
"css": ["/static/css/main.css"],
"js": [
"/static/js/runtime-main.js",
"/static/js/2.js",
"/static/js/main.js"
]
}
],
在 content script
内部,我这样做:
const ExtensionID = process.env.REACT_APP_EXTENSION_ID || '';
chrome?.runtime.connect(ExtensionID, { name: 'example' });
chrome?.runtime?.sendMessage('Hi from content script')
在 web page
内部,我这样做:
const ExtensionID = process.env.REACT_APP_EXTENSION_ID || "";
chrome.runtime.connect(ExtensionID, { name: "example" });
chrome?.runtime?.sendMessage(ExtensionID, "Hi from app");
然后这里是background.js
中的监听器:
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
console.log({ request })
});
chrome.runtime.onMessageExternal.addListener((request, sender, sendResponse) => {
console.log("Received message from " + sender + ": ", request);
sendResponse({ received: true }); //respond however you like
});
当我打开网站时,扩展被成功注入并且在 background.js
页面的开发控制台中我得到以下信息:
Hello world from background.js
{request: "Hi from content script"}
缺少“来自应用程序的嗨”,这意味着它不是 sent/received。我使用 ngrok
设置转发到我的应用程序认为:
- 域名为
localhost
- 协议不是
https
可能是问题所在,但如您所料,运气不佳。
到目前为止,我已经完成了以下工作:
- 在我的
manifest.json
中设置我的 - 在
background.js
中设置 - 使用显示的分机 ID 调用
runtime.sendMessage
in the docs - 使用
https
网站进行安全连接
externally_connectable
onMessageExternal
侦听器
非常感谢对此问题的任何帮助
我们可以使用 content-script 和后台 js 文件在网站和 chrome 扩展程序之间同步身份验证令牌。
Inside
web page
I do this ...
我不知道你是怎么做到的 web page
这是我将访问令牌从 web page
发送到 extension
所做的。
这是我的 content-script
文件。
/* eslint-disable no-undef */
const hostScript = () => {
// ----------------- Function Declarations -----------------
let listenGetTokenResponseFromWindow = () => {};
const sendMessagesToExtension = (msg, callback = null) => {
chrome.runtime.sendMessage(msg);
};
const sendMessagesToWindow = (msg, callback = null) => {
const { type } = msg;
switch (type) {
case ExtensionMessagesTypes.GetTokenFromWindow:
// Can not pass the function with window.postMessage. Only JSON object can be passed.
// So reset the listener
window.postMessage(msg, document.location.origin);
window.removeEventListener("message", listenGetTokenResponseFromWindow);
listenGetTokenResponseFromWindow = event => {
if (event.source !== window) return;
if (event.data.type === ExtensionMessagesTypes.GetTokenFromWindowResponse) {
const { payload } = event.data;
!!callback && callback(payload.token);
}
};
window.addEventListener("message", listenGetTokenResponseFromWindow);
break;
case ExtensionMessagesTypes.SetWindowToken:
window.postMessage(msg, document.location.origin);
!!callback && callback();
break;
default:
break;
}
};
const listenMessagesFromWindow = event => {
if (event.source !== window) return;
const msg = event.data;
Object.values(ExtensionMessagesTypes).includes(msg.type) && sendMessagesToExtension(msg);
};
const listenMessagesFromExtension = (msg, sender, response) => {
sendMessagesToWindow(msg, response);
return true; // means response is async
};
// ----------------- Listener Definitions -----------------
window.removeEventListener("message", listenMessagesFromWindow);
window.addEventListener("message", listenMessagesFromWindow);
chrome.runtime.onMessage.removeListener(listenMessagesFromExtension);
chrome.runtime.onMessage.addListener(listenMessagesFromExtension);
// Reset extension token as windows token
sendMessagesToWindow({ type: ExtensionMessagesTypes.GetTokenFromWindow }, token => {
sendMessagesToExtension({
type: ExtensionMessagesTypes.SetExtensionToken,
payload: { token }
});
});
};
这是 background.js
文件。
(() => {
chrome.runtime.onMessage.addListener((msg, sender, response) => {
switch (msg.type) {
case ExtensionMessagesTypes.GetTokens:
getAccessTokens(response);
break;
case ExtensionMessagesTypes.SetExtensionToken:
!!msg.payload && !!msg.payload.token && setAccessTokenToExtensionLocalStorage(msg.payload.token, response);
break;
case ExtensionMessagesTypes.SetWindowToken:
!!msg.payload && !!msg.payload.token && sendMessageFromExtensionToWindow(msg, response);
break;
}
});
})();
const sendMessageFromExtensionToWindow = (msg, callback = null) => {
chrome.tabs.query({ currentWindow: true, url: `${HostURL}/*` }, tabs => {
if (tabs.length < 1) {
!!callback && callback(null);
return;
}
if (msg.type === ExtensionMessagesTypes.GetTokenFromWindow) {
chrome.tabs.sendMessage(tabs[0].id, msg, token => {
!!callback && callback(token);
});
} else if (msg.type === ExtensionMessagesTypes.SetWindowToken) {
chrome.tabs.sendMessage(tabs[0].id, msg, () => {
!!callback && callback();
});
}
});
};
// Authentication
const setAccessTokenToExtensionLocalStorage = (access_token, callback = null) => {
chrome.storage.local.set({ access_token }, () => {
!!callback && callback();
});
};
const getAccessTokenFromChromeStorage = callback => {
chrome.storage.local.get(['access_token'], result => {
!!callback && callback(result.access_token);
});
};
const getAccessTokens = callback => {
getAccessTokenFromChromeStorage(accessTokenFromExtension => {
sendMessageFromExtensionToWindow({ type: ExtensionMessagesTypes.GetTokenFromWindow }, accessTokenFromWindow => {
callback({
accessTokenFromExtension: accessTokenFromExtension || '',
accessTokenFromWindow: accessTokenFromWindow || ''
});
});
});
};
const handleLogin = payload => {
//TODO: Handling refresh token
const { token } = payload;
if (!token) return;
setAccessTokenToExtensionLocalStorage(token);
};
const handleLogout = () => {
setAccessTokenToExtensionLocalStorage(null);
};
PS:您不需要 manifest.json 文件中的 externally_connectable
选项。
感谢@wOxxOm 的评论,我得以解决问题。
我引用:
Tentatively, since ngrok.io is in public suffix list it means it's basically like com which is forbidden in externally_connectable. Try using a more specific pattern for the site.
我更改了 URL 以使用一个更具体的 https://330218550995.ngrok.io/*
,现在可以使用了