Chrome Extension: sendRequest to content script inside inner iframes? - google-chrome-extension

I have injected a content script file to all pages and inner iframes using "all_frames": true but when I send a request from popup only top window is receiving it. Any idea how can send it to all inner frames or at least last focused window/document?
Popup.html (page action):
===========
chrome.tabs.getSelected(null, function(tab) {
chrome.tabs.sendRequest(tab.id, {...}, function(response) {...});
});
content_script.js
=================
chrome.extension.onRequest.addListener(function(request, sender, sendResponse) {
sendResponse({...});
});
I have checked that content script is working in inner frames.
I'm trying to retrieve text selection which may be in inner frames.
(By the way I prefer not to open connection by every and all content script, that's a big overhead.)

The problem is that one tab can have only one onRequest listener. So if you want to send messages to only iframe script, and there is only one iframe on the page, then you should create a listener inside iframe only:
//listen to requests only inside iframe
if(window!=window.top) {
chrome.extension.onRequest.addListener(function(request, sender, sendResponse) {
...
});
}
Now if you want to send requests to both parent page and iframe, or there are many iframes on the page that need to listen to requests - you need to communicate through ports.
Ports still have a similar limitation - only one port listener will be called, but you can open many ports. If there is a parent page with only one iframe - you just open two connections on two different ports. But if there are many iframes on the page, and each iframe has subframes - you would need to enumerate all iframes first to create unique ports for each of them.
PS. I am not sure what you meant by
I prefer not to open connection by every and all content script
but if you want to inject content script on demand, there is chrome.tabs.executeScript().

Related

Web extension: send message to an automatically created service worker [duplicate]

I want to send message from the console of the random web page to my chrome extension.
chrome.extension.sendMessage doesn't seem to work.
According to the official docs you should use postMessage in the sender and message event listener in the receiver.
Here is an example:
Your website's page.html
var data = { type: "FROM_PAGE", text: "Hello from the webpage!" };
window.postMessage(data, "*");
Content script: (injected using chrome.tabs.executeScript(tabid, {code:...)
window.addEventListener("message", function(event) {
// We only accept messages from ourselves
if (event.source != window)
return;
if (event.data.type && (event.data.type == "FROM_PAGE")) {
console.log("Content script received message: " + event.data.text);
}
});
Here page.html (which is not a part of the extension) posts messages to itself, which are intercepted and inspected by the content script. The reverse is possible through similar means.
To pass from content script to extension, you will have to use one of the available message-passing techniques.
It looks complicated and it is somewhat complicated but all this mumbo-jumbo is very secure.
Here's a quote from the latest https://developer.chrome.com/docs/extensions/mv3/messaging/, It's much simpler to support this kind of feature now, here's how:
###Sending messages from web pages
Similar to cross-extension messaging, your app or extension can
receive and respond to messages from regular web pages. To use this
feature, you must first specify in your manifest.json which web
sites you want to communicate with. For example:
"externally_connectable": {
"matches": ["*://*.example.com/*"]
}
This will expose the messaging API to any page which matches the URL
patterns you specify. The URL pattern must contain at least a
second-level domain - that is, hostname patterns like "", ".com",
".co.uk", and ".appspot.com" and <all_urls> are prohibited.
From the web page, use the runtime.sendMessage or runtime.connect APIs
to send a message to a specific app or extension. For example:
// The ID of the extension we want to talk to.
var editorExtensionId = "abcdefghijklmnoabcdefhijklmnoabc";
// Make a simple request:
chrome.runtime.sendMessage(editorExtensionId, {openUrlInEditor: url},
function(response) {
if (!response.success)
handleError(url);
});
From your app or extension, you may listen to messages from web pages
via the runtime.onMessageExternal or runtime.onConnectExternal APIs,
similar to cross-extension messaging. Only the web page can initiate a
connection. Here is an example:
chrome.runtime.onMessageExternal.addListener(
function(request, sender, sendResponse) {
if (sender.url == blacklistedWebsite)
return; // don't allow this web page access
if (request.openUrlInEditor)
openUrl(request.openUrlInEditor);
});
So to elaborate, a more concreate example: An issue with chrome.runtime.sendMessage(...) style is that you have to specify the pag you're on as externally_connectable which doesn't take a global wildcard like "https:///". So if you want that ability, you have to use the postMessage style communication. Capture the message from the window into the contentscript, then from the contentscript, you can send it elsewhere (if desired, like to the background.js etc.)
So in the normal web page, or in injected source that you embed into the normal page, from your contentscript.js, send a message like this:
window.postMessage({ type: "FROM_PAGE_TO_CONTENT_SCRIPT",
text: "Hello from the webpage!" }, "*");
ex, you could add it to a button like this:
document.getElementById("theButton").addEventListener("click",
function() {
window.postMessage({ type: "FROM_PAGE_TO_CONTENT_SCRIPT",
text: "Hello from the webpage!" }, "*");
}, false);
Then to capture it in the contentscript.js and "send it on" to the rest of the extension, the only caveat is that you only want to "select" messages that seem to be ones that you care about:
window.addEventListener("message", function(event) {
// We only accept messages from this window to itself [i.e. not from any iframes]
if (event.source != window)
return;
if (event.data.type && (event.data.type == "FROM_PAGE_TO_CONTENT_SCRIPT")) {
chrome.runtime.sendMessage(event.data); // broadcasts it to rest of extension, or could just broadcast event.data.payload...
} // else ignore messages seemingly not sent to yourself
}, false);
You can switch to the JS execution context of your content script using the <page context> menu at the bottom of a page’s developer JS console, then use chrome.runtime.sendMessage and other chrome.* APIs just as you would within the content script.
In addition to #hewigovens , I don't have enough points to comment...
I'm explaining #renatoargh and #sbichenko
If sending message from a default web page -
1) the webpage needs to be quoted in the manifest. e.g.:
"externally_connectable": {
"matches": ["http://abcde/abcde/main.aspx*"]
}
2) the background.js (background page)
excepts the call with onMessageExternal e.g. (calling an extension):
var host_name = "com.my_chrome_extension.com";
chrome.runtime.onMessageExternal.addListener(function(message, sender, sendResponse) {
chrome.runtime.sendNativeMessage(host_name, {"run":message});
sendResponse({"success": "success"});
return true;
});
Here it shows how to send messages from chrome extension to web page https://developer.chrome.com/docs/extensions/mv3/messaging/#external-webpage
Add externally connectible resources in manifest.json that matches your web page URL or localhost, if you testing locally.
"externally_connectable": {
"matches": ["https://*.example.com/*"]
}
From the web page, use the runtime.sendMessage or runtime.connect APIs to send a message to a specific app or extension. For example:
// The ID of the extension we want to talk to.
var editorExtensionId = "abcdefghijklmnoabcdefhijklmnoabc";
// Make a simple request:
chrome.runtime.sendMessage(editorExtensionId, {openUrlInEditor: url},
function(response) {
if (!response.success)
handleError(url);
});
From your extension, you may listen to messages from web pages via the runtime.onMessageExternal or runtime.onConnectExternal APIs, similar to cross-extension messaging. Only the web page can initiate a connection. Here is an example:
chrome.runtime.onMessageExternal.addListener(
function(request, sender, sendResponse) {
if (request.openUrlInEditor)
openUrl(request.openUrlInEditor);
});

Messaging directly from web page in Safari like Chrome

Is there any way to post messages directly from web pages to global page like Chrome's
chrome.runtime.sendMessage, chrome.runtime.onMessageExternal
API functions in Safari?
I've used AngularJS in my web app, and can't think of any clean way integrating it with injected scripts to pass messages to background. My web app is going to initiate some commands to background and get results basically. Currently, trying to convert my Chrome extension to Safari.
Or is there any way my Angular controller speak with injected script if above approach is not possible so that Angular be responsible for updating DOM not injected script?
Content scripts injected into page are isolated from page's javascript. However they share DOM. Event if you can't call javascript function from content script directly you can use window.postMessage to send some message to content script and listen for message inside CS:
(according to extensions documentation):
contentscript.js
window.addEventListener("message", function(event) {
// We only accept messages from ourselves
if (event.source != window)
return;
var passedDataObject = event.data;
//...
}
}, false);
And on the web page:
window.postMessage({ /* Object to pass */ }, "*");
This is regular message passing on the web sites. postMessage takes two arguments: 1st is a data to pass and second is a target origin (see https://developer.mozilla.org/en-US/docs/Web/API/Window.postMessage for explanations).
Onmessage event have "source" attribute which tells you from which window/frame message come from. In your case you want to listen only for events from page where CS was injected to: if (event.source != window) return;
Edited:
Documentation for content scripts and message passing you'll find at https://developer.chrome.com/extensions/content_scripts

How can I listen to notifications?

Is there a way for an extension to add a listener to browser notifications, and access it content?
I'm trying to use Google Calendar's notifications to fire a custom function.
You can hook the webkitNotifications.createNotification function so that whenever a notification is created you run some particular code.
Create a file called notifhook.js:
(function() {
// save the original function
var origCreateNotif = webkitNotifications.createNotification;
// overwrite createNotification with a new function
webkitNotifications.createNotification = function(img, title, body) {
// call the original notification function
var result = origCreateNotif.apply(this, arguments);
// bind a listener for when the notification is displayed
result.addEventListener("display", function() {
// do something when the notification is displayed
// use img, title, and body to read the notification
// YOUR TRIGGERED CODE HERE!
});
return result;
}
})();
Next, include notifhook.js in your web_accessible_resources list in your manifest.
Finally, in a content script, inject a <script> element into the page with notifhook.js as its src:
var s = document.createElement("script");
s.src = chrome.extension.getURL("notifhook.js");
document.documentElement.appendChild(s);
You might be tempted to just use notifhook.js as your content script, but that won't work because the content script and the web page have separate execution environments. Overwriting the content script's version of webkitNotifications.createNotification won't affect the Google Calendar page at all. Thus, you need to inject it via <script> tag, which will affect both the page and the content script.
The above code is quite old and won't work anymore. However the method author has approached to intercept remains same and very useful trick that can be applied at multiple pages.
I have update on how we can access: How to capture or listen to browser notifications?
Not if what you want is a Chrome extension that sends notifications capture Google Calendar. If I'm not mistaken, Google's Calendar sends mail and SMS notifications. Also, I think there is no API function to which you could ask to see if there are pending notifications. The only thing that gets in the documentation is that the Google+ event notifications are sent to Google+ and perhaps access to the API can capture.

Chrome Extension: How to get page content of current tab from devtools when requests finished?

I'm developing a chrome extension to verify page content of current tab from devtools when request finishes. I'm only interested in following types of requests: main_frame, sub_frame, and xmlhttprequest.
It can be done like this:
listen for chrome.webRequest.onCompleted event in background.js and filter out non-interested requests
communicate with contentScript.js to get the page content
send page content as a message to devtools
But will such situation happen?
In background.js, request A completes and send a message M_A to contentScript.js to get page content.
Request B completes and page content changed.
In contentScript.js, M_A is received, then ...
So I want to find a better way, like put all the logic in devtools:
var requestFinishedHandler = function(request) {
// do something here ...
}
chrome.devtools.network.onRequestFinished.addListener(requestFinishedHandler);
Any advice?
There is a chrome.devtools.inspectedWindow.onResourceAdded. I use it in DevTools Autosave. It doesn’t specify request type such as main_frame, sub_frame, and xmlhttprequest.

sending message to chrome extension from a web page

I want to send message from the console of the random web page to my chrome extension.
chrome.extension.sendMessage doesn't seem to work.
According to the official docs you should use postMessage in the sender and message event listener in the receiver.
Here is an example:
Your website's page.html
var data = { type: "FROM_PAGE", text: "Hello from the webpage!" };
window.postMessage(data, "*");
Content script: (injected using chrome.tabs.executeScript(tabid, {code:...)
window.addEventListener("message", function(event) {
// We only accept messages from ourselves
if (event.source != window)
return;
if (event.data.type && (event.data.type == "FROM_PAGE")) {
console.log("Content script received message: " + event.data.text);
}
});
Here page.html (which is not a part of the extension) posts messages to itself, which are intercepted and inspected by the content script. The reverse is possible through similar means.
To pass from content script to extension, you will have to use one of the available message-passing techniques.
It looks complicated and it is somewhat complicated but all this mumbo-jumbo is very secure.
Here's a quote from the latest https://developer.chrome.com/docs/extensions/mv3/messaging/, It's much simpler to support this kind of feature now, here's how:
###Sending messages from web pages
Similar to cross-extension messaging, your app or extension can
receive and respond to messages from regular web pages. To use this
feature, you must first specify in your manifest.json which web
sites you want to communicate with. For example:
"externally_connectable": {
"matches": ["*://*.example.com/*"]
}
This will expose the messaging API to any page which matches the URL
patterns you specify. The URL pattern must contain at least a
second-level domain - that is, hostname patterns like "", ".com",
".co.uk", and ".appspot.com" and <all_urls> are prohibited.
From the web page, use the runtime.sendMessage or runtime.connect APIs
to send a message to a specific app or extension. For example:
// The ID of the extension we want to talk to.
var editorExtensionId = "abcdefghijklmnoabcdefhijklmnoabc";
// Make a simple request:
chrome.runtime.sendMessage(editorExtensionId, {openUrlInEditor: url},
function(response) {
if (!response.success)
handleError(url);
});
From your app or extension, you may listen to messages from web pages
via the runtime.onMessageExternal or runtime.onConnectExternal APIs,
similar to cross-extension messaging. Only the web page can initiate a
connection. Here is an example:
chrome.runtime.onMessageExternal.addListener(
function(request, sender, sendResponse) {
if (sender.url == blacklistedWebsite)
return; // don't allow this web page access
if (request.openUrlInEditor)
openUrl(request.openUrlInEditor);
});
So to elaborate, a more concreate example: An issue with chrome.runtime.sendMessage(...) style is that you have to specify the pag you're on as externally_connectable which doesn't take a global wildcard like "https:///". So if you want that ability, you have to use the postMessage style communication. Capture the message from the window into the contentscript, then from the contentscript, you can send it elsewhere (if desired, like to the background.js etc.)
So in the normal web page, or in injected source that you embed into the normal page, from your contentscript.js, send a message like this:
window.postMessage({ type: "FROM_PAGE_TO_CONTENT_SCRIPT",
text: "Hello from the webpage!" }, "*");
ex, you could add it to a button like this:
document.getElementById("theButton").addEventListener("click",
function() {
window.postMessage({ type: "FROM_PAGE_TO_CONTENT_SCRIPT",
text: "Hello from the webpage!" }, "*");
}, false);
Then to capture it in the contentscript.js and "send it on" to the rest of the extension, the only caveat is that you only want to "select" messages that seem to be ones that you care about:
window.addEventListener("message", function(event) {
// We only accept messages from this window to itself [i.e. not from any iframes]
if (event.source != window)
return;
if (event.data.type && (event.data.type == "FROM_PAGE_TO_CONTENT_SCRIPT")) {
chrome.runtime.sendMessage(event.data); // broadcasts it to rest of extension, or could just broadcast event.data.payload...
} // else ignore messages seemingly not sent to yourself
}, false);
You can switch to the JS execution context of your content script using the <page context> menu at the bottom of a page’s developer JS console, then use chrome.runtime.sendMessage and other chrome.* APIs just as you would within the content script.
In addition to #hewigovens , I don't have enough points to comment...
I'm explaining #renatoargh and #sbichenko
If sending message from a default web page -
1) the webpage needs to be quoted in the manifest. e.g.:
"externally_connectable": {
"matches": ["http://abcde/abcde/main.aspx*"]
}
2) the background.js (background page)
excepts the call with onMessageExternal e.g. (calling an extension):
var host_name = "com.my_chrome_extension.com";
chrome.runtime.onMessageExternal.addListener(function(message, sender, sendResponse) {
chrome.runtime.sendNativeMessage(host_name, {"run":message});
sendResponse({"success": "success"});
return true;
});
Here it shows how to send messages from chrome extension to web page https://developer.chrome.com/docs/extensions/mv3/messaging/#external-webpage
Add externally connectible resources in manifest.json that matches your web page URL or localhost, if you testing locally.
"externally_connectable": {
"matches": ["https://*.example.com/*"]
}
From the web page, use the runtime.sendMessage or runtime.connect APIs to send a message to a specific app or extension. For example:
// The ID of the extension we want to talk to.
var editorExtensionId = "abcdefghijklmnoabcdefhijklmnoabc";
// Make a simple request:
chrome.runtime.sendMessage(editorExtensionId, {openUrlInEditor: url},
function(response) {
if (!response.success)
handleError(url);
});
From your extension, you may listen to messages from web pages via the runtime.onMessageExternal or runtime.onConnectExternal APIs, similar to cross-extension messaging. Only the web page can initiate a connection. Here is an example:
chrome.runtime.onMessageExternal.addListener(
function(request, sender, sendResponse) {
if (request.openUrlInEditor)
openUrl(request.openUrlInEditor);
});

Resources