Receive postMessage from chrome new tab extension - javascript

I have a popup (that is not made by me) that sends a postMessage for a login callback.
In the new tab page (that opened the popup), I am unable to receive this message.
Here is my code:
window.addEventListener("message", receiveMessage, false);
function receiveMessage(event)
{
console.log("MESSAGE RECEIVED!");
console.log(event.data);
}
^This does not work.
(I am aware of the following API, chrome.runtime.onMessageExternal , but since the popup does not send a message via the chrome runtime, I cannot use this)
How do I solve this problem?

Post message is a back and forth deal. Try this.
/* in the extension */
var description = {};
chrome.tabs.sendMessage(tabs[0].id, {
desc: 'some value'
}, function(response) {
description.value = response.details;
/* do your thing */
});
/* in the content script (your popup) */
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
var desc = request.desc;
sendResponse(desc);
});

Related

Issues with tabs[0].id in onMessage callback

I'm writing an extension to demonstrate the use of messages. Here's the idea.
There is an HTML page with a mytext text field and a popup page that also contains a mytext field.
Both text fields are always synchronized in content.
Direct messages between the popup page and the content page do not work. Therefore, all communication takes place through an intermediary - the background page.
Messages are passed between the popup page and the background page via port.postMessage. The background page and content page communicate via chrome.runtime.sendMessage and chrome.tabs.sendMessage.
background.js
let popup_port;
debugger;
chrome.extension.onConnect.addListener(function (port)
{
popup_port = port;
popup_port.onMessage.addListener(function (msg)
{
chrome.tabs.query(
{
currentWindow: true,
active: true
},
function (tabs)
{
// This is where the error occurs.
chrome.tabs.sendMessage(tabs [0].id, msg);
});
return true;
});
});
chrome.runtime.onMessage.addListener(function (msg)
{
popup_port.postMessage(msg);
});
content.js
debugger;
chrome.runtime.onMessage.addListener(function (msg)
{
if (msg.query)
chrome.runtime.sendMessage(mytext.value);
else
mytext.value = msg.input;
});
popup.js
let port = chrome.extension.connect({name: '...'});
debugger;
port.postMessage({query: true});
port.onMessage.addListener(function (textMsg)
{
mytext.value = textMsg;
});
function postMyMessage ()
{
port.postMessage({input: mytext.value});
}
mytext.oninput = postMyMessage;
When I open a popup window, everything works correctly - the text from the main page also appears in the field of the popup page.
But when I enter text into the field from the popup page, an error occurs in the background page:
TypeError: Cannot read property 'id' of undefined
When I tested my code while writing this post, it (suddenly!!!) worked correctly - when I enter text in the popup, it appears in the main page as well. What was it !?
You say that it is possible to directly establish a connection between the popup page and the content page. How to do it right?
I placed this in the popup.js file:
let port = chrome.extension.connect({name: 'abc'});
debugger;
port.postMessage('hello');
while in content.js:
chrome.extension.onConnect.addListener(function (port)
{
port.onMessage.addListener(function (msg)
{
alert(msg);
});
});
As a result an error occurs:
Could not establish connection. Receiving end does not exist.
What is the correct way to organize two-way communication between popup.js and other parts of the extension (content.js)?

Why is my message being sent from Chrome page action popup not being received by content script (Chrome Extension)?

I am trying to send a message from my popup to my main content script which will change some values that I have added to a page using the method updateInfo(val). However, I am running into an issue where my message is either not being sent at all or just not being received by content.js. I tried using chrome.runtime.sendMessage(), but that didn't work, so I have went overkill and am sending a message to every tab. My content.js is still not receiving the message. I have confirmed this in the chrome debugger. When I change the select box which lives in the popup, it hits changeYearInfo(), but then the message is never received in content.js.
My content.js:
chrome.runtime.onMessage.addListener(
function (request, sender, sendResponse) {
if (request.message === "Year Value Changed") {
let newVal = request.value;
updateInfo(newVal);
}
});
My popup.js:
function changeYearInfo() {
let newYearVal = document.getElementById('yearSessions').value;
chrome.tabs.query({}, tabs => {
tabs.forEach(tab => {
chrome.tabs.sendMessage(tab.id, {
"message": "Year Value Changed",
"value": newYearVal
});
});
});
}
document.addEventListener('DOMContentLoaded', function () {
document.getElementById('yearSessions').addEventListener("change", changeYearInfo);
}
);
How would I fix this issue? I've successfully used message passing multiple times from background.js to content.js and vice versa, but this is the first time I've tried sending a message from popup to content.
EDIT: This has been solved. The issue was not in the message passing, but that I was basing my conclusion off of the debugger tool, and I was incorrectly using the popup debugger when trying to check if my message was being received in content.js

Chrome extension: How to remove orphaned script after chrom extension update

I have a chrome extension with a popup page which passes a boolean variable to my content page via simple one-time requests. The content page would then do some action based on the status of the boolean variable passed from the popup page. This was working perfectly until I accidentally removed the extension (still in developer mode, the extension is unpacked) and had to re-load it.
This caused an extension context invalidated error to appear in the popup inspection console and the webpage console seems to validate that the popup page and content script are not communicating. The webpage with the chrome extension active shows this error: Unchecked runtime.lastError: The message port closed before a response was received.
Based on a few answers I've already seen, it seems that reloading my chrome extension has "orphaned" my original working content script from the rest of my extension, which causes the aforementioned "Unchecked runtime.lastError: The message port closed before a response was received." error on the webpage console.
I believe that I cannot just reinject my content script again as my content script has DOM event listeners. Is there a possible way to remove the currently running orphan script? Or is there any suggested workaround to this problem?
Here is my popup.js:
chrome.tabs.query({'active': true, 'currentWindow': true}, function (tabs) {
chrome.tabs.sendMessage(tabs[0].id, {cTabSettings: (some boolean variable)});
});
Here is my content.js:
// Listening for message from popup.js
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if (request.cTabSettings === true) {
enabled = true;
} else if (request.cTabSettings === false) {
enabled = false;
}
});
// DOM listener and action
document.addEventListener('mousemove', function (e) {
// Some action
chrome.runtime.sendMessage({sender: "content", selText : "blah"}, function () {
console.log("success");
});
}, false);
I am using chrome developer mode version 76. Just to rephrase, this chrome extension was working (content script communicates with popup) before I accidentally reloaded it.
Since the orphaned content script can still receive DOM messages, send one from your new working content script to the ghosted content script via window, for example. Upon receiving the message you'll unregister all listeners (and nullify any global variables) which will also make your old script eligible for automatic garbage collection.
content.js:
var orphanMessageId = chrome.runtime.id + 'orphanCheck';
window.dispatchEvent(new Event(orphanMessageId));
window.addEventListener(orphanMessageId, unregisterOrphan);
// register all listeners with named functions to preserve their object reference
chrome.runtime.onMessage.addListener(onMessage);
document.addEventListener('mousemove', onMouseMove);
// the popup script checks it to see if a usable instance of content script is running
window.running = true;
function unregisterOrphan() {
if (chrome.runtime.id) {
// someone tried to kick us out but we're not orphaned!
return;
}
window.removeEventListener(orphanMessageId, unregisterOrphan);
document.removeEventListener('mousemove', onMouseMove);
try {
// 'try' is needed to avoid an exception being thrown in some cases
chrome.runtime.onMessage.removeListener(onMessage);
} catch (e) {}
return true;
});
function onMessage(msg, sender, sendResponse) {
//...........
}
function onMouseMove(event) {
// DOM events still fire in the orphaned content script after the extension
// was disabled/removed and before it's re-enabled or re-installed
if (unregisterOrphan()) { return }
//...........
}
popup.js should ensure a content script is injected before sending a message:
async function sendMessage(data) {
const [tab] = await chrome.tabs.query({active: true, currentWindow: true});
if (await ensureContentScript(tab.id)) {
return await chrome.tabs.sendMessage(tab.id, data);
}
}
async function ensureContentScript(tabId) {
try {
const [{result}] = await chrome.scripting.executeScript({
target: {tabId},
func: () => window.running === true,
});
if (!result) {
await chrome.scripting.executeScript({
target: {tabId},
files: ['content.js'],
});
}
return true;
} catch (e) {}
}
Please check this answer
It's not about removal of script but to avoiding error in case.

Porting Chrome extension to Edge

I have created a chrome extension to enable clipboard data access. The solution is explained in details here Implementing 'Paste' in custom context menu. Now the problem is how to port this extension to Edge. There is a tool for that I know I used it, and maybe it is working, but my problem is how to "consume" this extension, what is equivalent to chrome.runtime.sendMessage in Edge? In Chrome I used this https://developer.chrome.com/apps/messaging#external-webpage - the part 'Sending messages from webpages', but in Edge I just can't find anything similar. Thanks for your time and help.
There is runtime.sendMessage() in Edge too.
https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/runtime/sendMessage
The thing to keep in mind is that the runtime object is defined on the browser object, not chrome.
Sends a single message to event listeners within your extension or a different extension.
If sending to your extension, omit the extensionId argument. The runtime.onMessage event will be fired in each page in your extension, except for the frame that called runtime.sendMessage.
If sending to a different extension, include the extensionId argument set to the other extension's ID. runtime.onMessageExternal will be fired in the other extension.
Extensions cannot send messages to content scripts using this method. To send messages to content scripts, use tabs.sendMessage.
This is an asynchronous function that returns a Promise.
I managed to solve this. There is no way (at least I couldn't find it) to communicate from web page with extension background script (and only the background script can get data from the clipboard and has the 'browser' object defined). So what I did, I communicated with content script and content script communicated with background script. Here is the code.
PAGE CODE:
contextMenuPaste: function () {
if (getBrowserName() == 'EDGE') {
window.postMessage({
direction: "from-page-script"
}, "*");
}
},
window.addEventListener("message", function (event) {
if (event.source == window &&
event.data.direction &&
event.data.direction == "from-content-script") {
console.log('Data in page script', event.data.message);
}
});
CONTENT SCRIPT CODE
window.addEventListener("message", (event) => {
// If message came from page-script send request to background script to get clipboard data
if (event.source == window &&
event.data &&
event.data.direction == "from-page-script") {
browser.runtime.sendMessage({
message: "getClipboardData"
},
function(clipboardData) {
messagePageScript(clipboardData);
}
);
}
});
// Send clipboard data to page script
function messagePageScript(clipboardData) {
window.postMessage({
direction: "from-content-script",
message: clipboardData
}, "*");
}
BACKGROUND SCRIPT CODE
browser.runtime.onMessage.addListener(
function(req, sender, callback) {
if (req) {
if (req.message) {
if (req.message == "installed") {
console.log('Checking is extension is installed!');
callback(true);
}
else if(req.message = "getClipboardData") {
console.log('Get clipboard data');
callback(getDataFromClipboard());
}
}
}
return true;
}
);
function getDataFromClipboard() {
var bg = browser.extension.getBackgroundPage();
var helperTextArea = bg.document.getElementById('sandbox');
if (helperTextArea == null) {
helperTextArea = bg.document.createElement("textarea");
document.body.appendChild(helperTextArea);
}
helperTextArea.value = '';
helperTextArea.select();
// Clipboard data
var clipboardData = '';
bg.document.execCommand("Paste");
clipboardData = helperTextArea.value;
helperTextArea.value = '';
return clipboardData;
}
But there is one tiny issue. This code works if I have a break-point set on line
bg.document.execCommand("Paste");
and it doesn't if I don't have that break-point. I thought it is a trimming issue, added pauses, delayed executions but nothing helped. I will start a new question for that issues and will copy solution here (if I find one).

embed _generated_background_page to options page

I made Chrome extension which working fine, but need to be modified this way:
console.log of background.js should be monitored in real time by user constantly, while options page opened, as an element of this page. At the worst case, it may be duplicate of log, but generated in background.js.
How can I access and display this data, where background script process external data once a second?
You can't embed the background page in another page.
To do what you are looking to do, you can override console.log to do extra stuff:
// background.js
(function() {
if(window.console && console.log) {
var old = console.log;
console.log = function() {
// Do extra stuff here
chrome.runtime.sendMessage({
type: "backgroundLogEvent",
content: arguments
});
// Call normal console.log
old.apply(this, arguments);
}
}
})();
And in the options page, you can receive those messages:
// options.js
chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {
if(message.type == "backgroundLogEvent") {
// Do something with message.arguments
// I.e. log them here:
message.arguments.unshift('From background:');
console.log.apply(console, message.arguments);
}
});
This will transmit all console.log messages from background in realtime.
Exercise for the reader: implement a buffer that shows a short history before opening the options page.

Categories

Resources