I've been trying to communicate from my Content Script to a background page (for XHR), but then, I've been failing to even establish a communication.
Here's a snippet from content_script.js
$('.genericStreamStory').each(function(){
var link = $(this).find('.uiStreamSource a').attr('href');
$(this).find('.uiStreamFooter').append("<span class='a1' style='color:red !important;'> ยท Sample</span>");
document.querySelector('.a1').onclick=function(){
//alert('span clicked');
chrome.extension.sendMessage({method: "getHTML", data: 'hello'});
};//end of anonymous function
});
back.js
chrome.extension.onMessage.addListener(function(request, sender, sendResponse) {
if (request.method === "getHTML") {
console.log('check.. '+request.data);
}
});
and finally, manifest.json
{
"name": "App1",
"version": "0.1",
"description": "Yay, I'm so useless.",
"content_scripts": [
{
"matches": ["https://*.facebook.com/*"],
"js": [
"/js/external/jquery.js",
"/js/content_script.js"
]
}
],
"background": {
"scripts": ["/js/back.js"]
},
"manifest_version": 2
}
So this extension basically tries to create append a 'Span' after every facebook post.
When I click the Span element, I get the message in the alert box (which I've commented now). Ideally, this action should communicate with my back.js page.
In back.js, I'm logging the message sent by the content script to the console.
But then, I'm literally getting nothing in the console screen!
I've tried the following :
I replaced onMessage & sendMessage with onRequest & sendRequest. Got a PORT Error
Also tried loading an external script from a .js file replacing inline script. - Blank console!
Is there any bug in my code? Where am I making a mistake?
Related
I'm testing a Chrome extension that can display some information about a PDF file the user is currently viewing, ideally inserted as an iFrame. I don't want this to happen automatically, so I'm using a background script to listen for the click event, and then injecting the content script. (The extension communicates with an external server but that isn't relevant to this question.)
Here is my manifest.json:
{
"name": "Test",
"version": "0.0.1",
"manifest_version": 2,
"description": "Test",
"background": {
"scripts": [
"background.js"
],
"persistent": false
},
"browser_action": {
"default_title": "Test"
},
"permissions": [
"activeTab"
]
}
Here is background.js:
hrome.browserAction.onClicked.addListener(function (tab) {
chrome.tabs.executeScript({'file': 'popup.js'})
});
And here is popup.js:
var widgetHtml =
'<div id="main" style="position: fixed;top: 14px;">'+
' <p>some stuff goes here</p>'+
'</div>';
var iframe = document.createElement('iframe');
document.body.insertBefore(iframe,document.body.firstChild)
iframe.contentDocument.body.innerHTML = widgetHtml;
This works like a charm on non-PDF pages, but it doesn't inject anything on PDF pages. If I use the exact popup.js script in the console, it of course works fine as well.
I've read through Run a Chrome Plugin On a PDF Page, which tries to automatically detect PDFs, and this one, How to inject content script view on the PDF viewer?, but it doesn't seem to work. What am I missing?
I've taken a look at other related SO posts and the solutions haven't helped solve my issue. This is my first chrome extension, so please bear with me!
I'm writing a simple chrome extension that searches for user provided keywords on a webpage. I can't get the content script that returns the DOM content to run. Some of the code, I've taken from an answer in another SO post, but I can't seem to get it to work for me.
I put a console.log("hello world") at the top of the file, and it doesn't show up, so I think it might be the structure of my project.
manifest.json
{
"name": "keyword search",
"version": "0.0.1",
"manifest_version": 2,
"permissions": [ "tabs" , "storage", "activeTab", "<all_urls>"],
"browser_action": {
"default_popup": "html/form.html"
},
"content_scripts": [{
"matches": [ "<all_urls>" ],
"js": [ "js/jquery.min.js", "content_scripts/content_script.js" ]
}],
"homepage_url": "http://google.com/"
}
js/popup.js
function run() {
running = true;
console.log('running');
var url = "https://www.stackoverflow.com/"
// Get KW & category for search
chrome.storage.local.get(["kw"],
function (data) {
kw = data.kw;
console.log("redirecting to find kw: " + kw);
// Send current tab to url
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.update(tabs[0].id, {url: url});
chrome.tabs.sendMessage(tabs[0].id, {type: 'DOM_request'}, searchDOM);
});
}
);
}
function searchDOM(domContent) {
console.log("beginning dom search \n" + domContent);
}
content_scripts/content_script.js
// Listen for messages
console.log("hello world")
chrome.runtime.onMessageExternal.addListener(function (msg, sender, sendResponse) {
// If the received message has the expected format...
if (msg.type === 'DOM_request') {
// Call the specified callback, passing
// the web-page's DOM content as argument
sendResponse(document.all[0].outerHTML);
}
});
console
running
redirecting to find kw: TestKeyword
beginning dom search
undefined
First, onMessageExternal is the wrong event (it's for external messaging):
you should use the standard onMessage.
Second, chrome extensions API is asynchronous so it only registers a job, returns immediately to continue to the next statement in your code without waiting for the job to complete:
chrome.tabs.update enqueues a navigation to a new URL
chrome.tabs.sendMessage enqueues a message sending job
the current page context in the tab gets destroyed along with the running content scripts
the tab starts loading the new URL
the message is delivered into the tab but there are no listeners,
but this step may instead run right after step 2 depending on various factors so the content script running in the old page will receive it which is not what you want
the tab loads the served HTML and emits a DOMContentLoaded event
your content scripts run shortly after that because of the default "run_at": "document_idle"
There are at least three methods to properly time it all:
make your content script emit a message and add an onMessage listener in the popup
use chrome.tabs.onUpdated to wait for the tab to load
use chrome.tabs.onUpdated + chrome.tabs.executeScript to simplify the entire thing
Let's take the executeScript approach.
remove "content_scripts" from manifest.json
instead of chrome.tabs.query (it's not needed) use the following:
chrome.tabs.update({url}, tab => {
chrome.tabs.onUpdated.addListener(function onUpdated(tabId, change, updatedTab) {
if (tabId === tab.id && change.status === 'complete') {
chrome.tabs.onUpdated.removeListener(onUpdated);
chrome.tabs.executeScript(tab.id, {
code: 'document.documentElement.innerHTML',
}, results => {
searchDOM(results[0]);
});
}
});
});
chrome-extension, stucked with the error
Uncaught TypeError: Cannot read property 'sendRequest' of undefined
here is my code
manifest.json
{
"manifest_version": 2,
"name": "blah",
"version": "1.0",
"description": "blah",
"browser_action": {
"default_icon": "icon.png"
},
"background": "bg.html", // change to your background page
"permissions": ["http://*/*", "tabs"], //need permission to access all pages & tabs
"content_scripts": [
{
"matches": ["http://*/*", "https://*/*"], // run for http & https pages
"js": ["key_event.js"], // key_event.js is injected to the page, this handles key press
"run_at": "document_start" // run before everything else, else there will be conflicts at pages which accept keyboard inputs ( eg:google search)
}
]
}
key_event.js
if (window == top) {
window.addEventListener('keyup', doKeyPress, false); //add the keyboard handler
}
function doKeyPress(e){
if (e.keyCode == 17){ // if e.shiftKey is not provided then script will run at all instances of typing "G"
alert("pressed");
chrome.extension.sendRequest({redirect: "https://www.google.co.in"});//build newurl as per viewtext URL generated earlier.
}
}
bg.html
chrome.extension.onRequest.addListener(function(request, sender) {
chrome.tabs.update(sender.tab.id, {url: request.redirect});
});
plz help me
As already mentioned in comments
The background section of manifest version 2 has to be like
"background": {"scripts": ["bg.js"]}
There is no background page, only background scripts. So you'll have to move your code from bg.html to bg.js and remove all the extra HTML from it.
chrome.extension.sendRequest and chrome.extension.onRequest have been deprecated in favor of chrome.runtime.sendMessage and chrome.runtime.onMessage respectively. That means, you can still use sendRequest and onRequest but it might be subject to removal in a future version of Chrome
The keyCode for G is 71 rather than 17
UPDATE
From what I can tell, it is impossible to send a message from the background script to the content script using the "sendMessage" function. However there is a horrible workaround,
In your content script's window.onload, send a message to the background script:
chrome.runtime.sendMessage( { action: "messaging", window: "app" }, this.listenForFutureMessages );
Also in the content script, have the following function:
listenForFutureMessages: function(someAction)
{
//Take some action based on the message
//If we want the background script to be able to contact
//us again, we need to give them another callback. This
//is because Chrome only allows one use per callback
chrome.runtime.sendMessage( { action: "messaging", window: "app" }, this.listenForFutureMessages );
},
In the background script, have a listener that does something like this:
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse)
{
if ( request.action === "messaging" )
{
//Save the callback for later
this.listeners[ request.window ] = sendResponse;
//Tell chrome we will be using the callback later
return true;
}
}
);
When your background script wants to send the content script a message, simply call it like this:
this.listeners[ "app" ]( { someProperty: "some value" } );
This is a stupid way to do this, but it makes this actually possible. Hope this helps anyone else who needs this functionality.
ORIGINAL
I'm unable to send a message from my background script to a content script. When I try to find the tab id, it tells me I don't have permissions even though my app has that permission. And when I receive a message from the content script, and print out the sender object, it shows tab.id = -1. The API to send a message to a content script requires a tab id!
chrome.tabs.sendMessage(integer tabId, any message, function responseCallback)
The error:
chrome.tabs is not available: You do not have permission to access this API. Ensure that the required permission or manifest property is included in your manifest.json.
Error in event handler for 'undefined': Cannot call method 'sendMessage' of undefined TypeError: Cannot call method 'sendMessage' of undefined
at chrome-extension://panoaieakcofaegcjfbmhndaekfgpijh/scripts/background.js:109:16
at Event.dispatchToListener (event_bindings:356:21)
at Event.dispatch_ (event_bindings:342:27)
at Event.dispatch (event_bindings:362:17)
at miscellaneous_bindings:167:33
at Event.dispatchToListener (event_bindings:356:21)
at Event.dispatch_ (event_bindings:342:27)
at Event.dispatch (event_bindings:362:17)
at Object.chromeHidden.Port.dispatchOnMessage (miscellaneous_bindings:253:22)
So how do I contact my content script? (I have multiple windows and need to be able to contact them individually)
My manifest:
{
"manifest_version": 2,
"name": "App",
"description": "App",
"version": "0.75",
"minimum_chrome_version": "27",
"offline_enabled": true,
"icons":
{
"16": "images/icon16.png",
"48": "images/icon48.png",
"128": "images/icon128.png"
},
"app":
{
"background":
{
"scripts":
[
"scripts/background.js"
]
}
},
"permissions":
[
"unlimitedStorage",
"fullscreen",
{
"fileSystem":
[
"write"
]
},
"background",
"<all_urls>",
"tabs"
],
"update_url": "http://192.168.1.121/app.xml"
}
There's not such a thing called "Content scripts" in a Chrome app. Your manifest file looks like a mixture of a Chrome extension. Open chrome://extensions/, enable developer mode, and you would see a warning that the "background" and "tabs" permissions are invalid for a Chrome app.
If you're implementing a Chrome app, just use chrome.runtime.sendMessage and chrome.runtime.onMessage. These messages can be send from and to your event page and the main page. For example:
// event page (aka background page)
chrome.app.runtime.onLaunched.addListener(function() {
chrome.app.window.create('main.html');
});
// Later, when you want to notify the app window
chrome.runtime.sendMessage(" ... any message ... ");
<!-- main.html -->
<script src="main.js"></script>
// main.js
chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {
// Do something with the message
});
I am trying to show a simple desktop notification code from a content script, but it doesn't seem to work.. I have added the permissions in the maifest.json file. Is there a restriction on showing them from the content script ?
You can't show notifications directly through a content script.
But, you can show them through the background page.
Your manifest.js should look something like this:
{
"name": "Notify This",
"version": "0.1",
"permissions": [
"notifications"
],
"background_page": "background.html",
"content_scripts": [
{
"matches": ["http://www.example.com/*"],
"js": ["contentscript.js"]
}
]
}
Then use the chrome.extension.sendRequest():
// in your contentscript.js
chrome.extension.sendRequest({msg: "Sup?"}, function(response) { // optional callback - gets response
console.log(response.returnMsg);
});
And on the receiving end you should have a onRequest listener:
// in your background.html
chrome.extension.onRequest.addListener(
function(request, sender, sendResponse) {
// Create a simple text notification:
var notify = webkitNotifications.createNotification(
'48.png', // icon url - can be relative
'Hello!', // notification title
request.msg // notification body text
);
notify.show();
setTimeout(function(){ notify.cancel(); },5000);
sendResponse({returnMsg: "All good!"}); // optional response
});
Yes, notifications use Chrome specific API, and the content script is only valid for general javascript etc... The background page is where all chrome specific API's are capable of running... First you'll need to register your background page in the manifest.json file - like this:
"background_page": "background.html",
Also in the manifest file, Allow the required permissions:
"permissions": [ "notifications" ],
Then your script in the background page should look like this :
<script>
setTimeout("setNotification();",1);
function setNotification(){
var n
if (window.webkitNotifications.checkPermission() != 0){
setNotification();
return false;
}
n = window.webkitNotifications.createHTMLNotification('http://www.your-notification-address.com');
n.show();}
</script>