everyone.
Can anybody tell me how I can access DOM of another pages in my extension from content script or background page? I tried to make a connection from content script to popup.html. But it doesn't work. But the same code works with background.html...
Here's the code I place in myscript.js:
function sendLast(){
var result = document.getElementById('last_3212164'); //document.evaluate("//*[#id='last_3212164']", document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null );
alert(result.textContent);
chrome.extension.sendRequest({greeting: "hello"}, function(response) {
console.log(response.farewell);
});
}
var t=setTimeout("sendLast()", 3000);
Here is code from popup.html:
<html>
<head>
<script>
chrome.extension.onRequest.addListener(
function(request, sender, sendResponse) {
alert("Got it!");
if (request.greeting == "hello")
sendResponse({farewell: "goodbye"});
else
sendResponse({}); // snub them.
}
);
</script>
</head>
<body>
Here will be the values
</body>
</html>
This code is supposed to give alert "Got it" every 3 second. But It doesn't do anything, silence... I open Popup.html 1 sec after loading the main page.
If popup is currently opened then you can access its DOM from a background page. If you need it in a content script then you would need to send a request to a background page first.
Lets say you want to read a value of test input field from a popup inside a content script:
content_script.js:
chrome.extension.sendRequest({cmd: "findElementInPopup"}, function(response){
console.log("value:", response);
});
background.html:
chrome.extension.onRequest.addListener(function(request, sender, sendResponse) {
if(request.cmd == "findElementInPopup") {
var winArray = chrome.extension.getViews({type:"popup"});
//if popup is opened
if(winArray.length > 0) {
sendResponse(winArray[0].document.getElementById("test").value);
}
}
});
You would need to do all the DOM manipulations in a background page as trying sending whole window through a response back to a content script would result in an error (object structure is too complex for json serialization).
Related
I am injecting an iframe in a tab. Now inside the the iframe, based on user's actions, I need to show some error/warning/success notifications. However I want to display these notifications in the tab not in iframe. So I need to communicate between iframe and content script. Now these notifications are dynamic based on user's actions so I thought of message passing between iframe and content script via background page.
So what i have done is send messages from iframe to background. Now both background page and content script listens to these messages but only background page is able to receive them. On receiving messages it reflects them back to sender tab. Now content script can receive these messages as they are sent from background page.
Now I tried the same using custom events but it didn't work.
But i would like to know any other method which is more efficient than what i am doing??
EDIT : Here's the relevant code
iframe.js:
$scope.hideFrame = function(){
sendMessageToBackground("hideFrame");
};
$scope.checkIfFormValid = function(){
if(!($scope.taskName === "" || $scope.group.selectedGroup === null )){
$scope.addTask();
}
else{
sendMessageToBackground("invalidForm");
}
};
function sendMessageToBackground(msg){
chrome.runtime.sendMessage({type: msg});
}
background.js:
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse){
switch (request.type){
case "hideFrame":
chrome.tabs.sendMessage(sender.tab.id,{type:"hideFrame"});
break;
case "invalidForm":
chrome.tabs.sendMessage(sender.tab.id,{type:"invalidForm"});
break;
}
});
content.js:
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse){
switch (request.type){
case "invalidForm":
var n = noty({
text : ' Please fill all details',
type : 'error',
layout : 'topRight',
timeout : 10000,
theme : 'defaultTheme'
});
break;
case "hideFrame":
$("#isolatedFrame").hide();
break;
}
});
Using window.parent.postMessage(not working):
iframe.js:
function sendMessageToContent(msg){
// chrome.runtime.sendMessage({type: msg});
window.parent.postMessage({ type: "fromFrame", message: msg }, "*");
}
content.js:
window.addEventListener("message", function(event) {
if (event.source != window)
return;
if (event.data.type && (event.data.type == "fromFrame")) {
console.log("Content script received: " + event.data.message);
}
}, false);
Also, when i add a breakpoint at window.parent.postMessage({ type: "fromFrame", message: msg }, "*"); and try to see window.parent object , inspected target is disconnected. I don't why this is happening??
Yes, there's a more effective way to achieve it - using tabs.sendMessage.
extensions cannot send messages to content scripts using this method. To send messages to content scripts, use tabs.sendMessage. (see runtime.sendMessage document )
Here's the code.
iframe.js:
chrome.runtime.sendMessage({greeting: "hello"}, function(response) {
console.log(response.farewell);
});
content.js:
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
console.log(sender.tab ?
"from a content script:" + sender.tab.url :
"from the extension");
console.log('greeting:", request.greeting);
sendResponse({farewell: "goodbye"});
You can also refer to https://developer.chrome.com/extensions/messaging#simple for more detail.
EDIT
Since iframe.js is a content script, you should send the message with chrome.runtime.sendMessage and without using chrome.tabs API. Otherwise, chrome.tabs will be undefined.
I am creating a chrome extension. In that I am getting problem in sending message from background.js to popup.js. I am successfull in sending some static data from background.js to popup.js. But, when I try to send data dynamically (i.e. the data sent to background.js by content-script.js) from background.js to popup.js, it is giving undefined as I print it on popup.html.
This is my popup.js:
var value;
window.onload = function(){
chrome.runtime.sendMessage({getVal: "Hi"}, function(response){
value = response.responseSeltext;
alert(value);
});
document.getElementById('save').onclick = test;
}
This is my background.js:
var seltext;
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if (request.ezynotes != ""){
seltext = request.ezynotes;
alert("In extension "+seltext);
//document.getElementById("notes").innerHTML += request.ezynotes;
}
if (request.getVal != ""){
sendResponse({responseSeltext:seltext});
//seltext = request.ezynotes;
alert("In extension "+seltext);
//document.getElementById("notes").innerHTML += request.ezynotes;
}
});
I m assuming u r listening for message in background from content.js and trying to send it to popup.js. Right??In that case u have to send message from background and add a listening event in popup.js which is not present in your code.
In the background page of my Chrome extension I need to do the following:
create a tab with an html page that resides in my extension folder or that is dynamically generated by the background page;
once the new tab is loaded, inject a content-script in it in order to use message passing;
once the script has been injected, send a message to the new tab with some data.
This always turns into the error:
tabs.executeScript: Cannot access contents of url "". Extension manifest must request permission to access this host. or, if the page or script are stored into .html and .js dedicated files in my extensions folder:
tabs.executeScript: Cannot access contents of url "chrome-extension://ehdjfh.../page.html". Extension manifest must request permission to access this host.
The error is generated when chrome.tabs.executeScript() is executed and the content-script injection is blocked. (I would need this structure to pass some big data to the tab that will be truncated if passed directly as string into the coded html url)
Here is a code sample that can be used to test the behavior into a working extension, just copy-paste and load the extension in Chrome:
background.js
chrome.browserAction.onClicked.addListener(function() {
var getSampleHTML = function() {
return 'javascript:\'<!doctype html><html>' +
'<head></head>' +
'<body>' +
'<p id="myId">page created...</p>' +
'</body>' +
'</html>\'';
};
// create a new tab with an html page that resides in extension domain:
chrome.tabs.create({'url': getSampleHTML(), 'active': false}, function(tab){
var getSampleScript = function() {
return 'chrome.extension.onRequest.addListener(' +
'function(request, sender, sendResponse) {' +
'if(request.action == "print_data" && sender.tab.id == ' + tab.id + '){' +
'var p = document.getElementById("myId");' +
'p += "<br>data received: " + request.data;' +
'}' +
'});'
'document.getElementById("myId").innerHTML += "<br>content-script loaded...";'
};
// inject the content-script in the page created:
chrome.tabs.executeScript(tab.id, {code: getSampleScript()}, function(){
// send the data to the content-script:
chrome.tabs.sendRequest(tab.id, {action: "print_data",
data: "some long data"});
});
});
});
manifest.json
{
"name": "ContentScript Injection Sample",
"description": "",
"version": "0.1",
"permissions": ["tabs"],
"background": {
"persistent": false,
"scripts": ["background.js"]
},
"browser_action": {
"default_title": "open a new tab and inject contentscript"
},
"manifest_version": 2
}
ok, this is a possible solution, thanks to #RobW and #方觉 for pointing out my mistake (my error was to consider a local web page opened into a new created tab as normal web page, instead it has direct access to chrome.extension API like eg. a popup):
background.js
chrome.browserAction.onClicked.addListener(function() {
// create a new tab with an html page that resides in extension domain:
chrome.tabs.create({'url': chrome.extension.getURL("page.html"),
'active': false}, function(tab){
var selfTabId = tab.id;
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
if (changeInfo.status == "complete" && tabId == selfTabId){
// send the data to the page's script:
var tabs = chrome.extension.getViews({type: "tab"});
tabs[0].doSomething("some long data");
}
});
});
});
page.html
<!doctype html>
<html>
<head>
</head>
<body>
<p id="myId">page created...</p>
<script src="page.js" type="text/javascript"></script>
</body>
</html>
page.js
var alreadyRun = false;
function doSomething(data){
if (!alreadyRun) {
var p = document.getElementById("myId");
p.innerHTML += "<br>data received: " + data;
// needed because opening eg. DevTools to inpect the page
// will trigger both the "complete" state and the tabId conditions
// in background.js:
alreadyRun = true;
}
}
document.getElementById("myId").innerHTML += "<br>script loaded...";
what I don't like so much of this solution is that use of chrome.tabs.onUpdated.addListener() to check if the created tab is completely loaded and that in this way it's triggered for any change of state of any tabs opened (it's useless... would be great a way to run the check only for the interested tab)
For my Chrome extension, I am attempting to post selected text to a PHP webpage. A solved question on this website (Chrome Extension: how to capture selected text and send to a web service) has helped me a lot in achieving this, but I want a different way of posting the text.
Instead of XMLHttpRequest as mentioned there, I want to send a hidden JS form from the content script. This method allows me to view or change the text before importing it to the database.
The problem is to get the trigger from the background to the content script. I already have a message the other way, so using the function(response) is desired. However, outside the “sendMessage”, I can’t listen for the response.cmd. And inside the “sendMessage”, I can’t get the response.cmd to trigger a function. Is there a solution for this, other than sending an all new message from the background script?
The code I am referring to:
Background.js
chrome.extension.onMessage.addListener(function(request, sender, sendResponse) {
if(request.cmd == "createSelectionMenu") {
sendResponse({cmd: "saveText"}); //Do things
}
});
Content_script.js
chrome.extension.sendMessage({ cmd: "createSelectionMenu", data: selectedText },
function(response) {
if(response.cmd == "saveText") {
createForm();
}
}
});
What I do is following:
I keep track of my opened tabs
content script:
// connect to the background script
var port = chrome.extension.connect();
background script
// a tab requests connection to the background script
chrome.extension.onConnect.addListener(function(port) {
var tabId = port.sender.tab.id;
console.log('Received request from content script', port);
// add tab when opened
if (channelTabs.indexOf(tabId) == -1) {
channelTabs.push(tabId);
}
// remove when closed/directed to another url
port.onDisconnect.addListener(function() {
channelTabs.splice(channelTabs.indexOf(tabId), 1);
});
});
Now I can notify all my registered tabs (i.e. content scripts) from my background script when a certain action happened:
var notification = { foo: 'bar' };
for(var i = 0, len = channelTabs.length; i < len; i++) {
chrome.tabs.sendMessage(channelTabs[i], notification, function(responseMessage) {
// message coming back from content script
console.log(responseMessage);
});
}
And again, on the other side in the content script, you can add a listener on these messages:
chrome.extension.onMessage.addListener(function(request, sender, sendResponse) {
if (request.foo == 'bar') {
executeStuff();
// if a callback is given:
sendResponse && sendResponse('success');
}
});
It's a bit of a brainf*ck, because it's redundant at some places. But I like it best that way, because you can wrap it and make it a bit easier.
If you want to see how I am using this, see my repository on GitHub: chrome-extension-communicator.
I'm trying to pass data that is saved in sessionStorage from background.html to popup.html
background.html:
chrome.extension.onRequest.addListener(function(request, sender, sendResponse) {
data = sessionStorage.getItem(request.tabId);
alert(data);
sendResponse({ data: data });
});
and in popup.html:
chrome.tabs.getSelected(null, function(tab) {
chrome.extension.sendRequest({ tabId: tab.id }, function(response) {
alert(response.data);
});
});
The popup is opened by a pageAction button, when I click the button I get an alert box with "null" on the popup and then an alert box with the data that I stored in sessionStorage on the background!
Any ideas how to fix this?
You don't need to use message/request APIs. I think this response may help you.
You also don't need sessionStorage, just store your data in a global variable of the background page. It will persist until the browser is closed or until the extension is restarted.
So, here is how I would rewrite your code:
background.html:
var data = {}; // Object storing data indexed by tab id
and in popup.html:
chrome.tabs.getSelected(null, function(tab) {
alert(chrome.extension.getBackgroundPage().data[tab.id]);
});
Note that chrome.tabs.getSelected is deprecated since Chrome 16, so popup code should be:
chrome.windows.getCurrent(function(win) {
chrome.tabs.query({'windowId': win.id, 'active': true}, function(tabArray) {
alert(chrome.extension.getBackgroundPage().data[tabArray[0].id]);
});
});
Well, I've done something dumb.
I inspected the background page by opening chrome-extension://[extension-id]/background.html in a tab instead of clicking on "inspect active views: background.html" in the extensions management page. This caused the tab to catch the request and call sendResponse, but the popup expected the REAL background page to call sendResponse (and if I understand Google's documentation regarding message passing, the fact that sendResponse was called twice is root of the problem, because the first call clears the request object)