In short, I want that when you press my extension button from the context menu, the content script will be added to the web page temporarily. I tried to use sendMessage() but it just didn't work.
Any help will be appreciated:)
//This is the service worker (eventPage)
chrome.runtime.onInstalled.addListener(() => {
chrome.contextMenus.create({
id: 'select',
title: 'select',
type: 'normal',
contexts: ['all']
});
})
chrome.contextMenus.onClicked.addListener(function(clickDate) {
if (clickDate.menuItemId == 'select') {
//send message to make content-script start operate
chrome.runtime.sendMessage('start');
}
});
//let's say that this is the content-script
chrome.runtime.onMessage.addListener(function(response, sender, sendResponse) {
if (response == 'start') {
// js code that is inserted into the site
}
});
{
"manifest_version": 3,
"name": "SendFast",
"description": "This extension makes it easier to send information",
"version": "1.0",
"icons": {
"128": "16-nahum.png",
"48": "64-nahum.png",
"16": "16-nahum.png"
},
"action": {
"default_icon": "16-nahum.png",
"default_popup": "popup.html"
},
"permissions":["contextMenus","activeTab"],
"background":{
"service_worker": "eventPage.js"
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content-script.js"]
}
]
}
so I tried to use chrome.scripting but failed. that's what I came up with:
//eventPage.js(after changes)
chrome.runtime.onInstalled.addListener(() => {
chrome.contextMenus.create({
id: 'select',
title: 'select',
type: 'normal',
contexts: ['all']
});
})
async function addJsScript() {
const [tab] = await chrome.tabs.query({active: true, currentWindow: true});
await chrome.scripting.executeScript({
target: {tabId: tab.id},
script:["content-script"],
});
}
chrome.contextMenus.onClicked.addListener(function(clickDate) {
if (clickDate.menuItemId == 'select') {
addJsScript()
}
});
and i added this to the manifest:
"permissions":["contextMenus","activeTab","scripting"],
"host_permissions":["<all_urls>"],
There are a few problems that I have been having that is related to this. Here's what you need to know.
First, to access the tabs, you need to add "host_permissions": ["<all_urls>"] to your manifest (v3) to assure that you can access the web pages.
Second, instead of script:["content-script.js"] as seen in the addJsScript() function, use files:["content-script.js"].
Lastly, make sure you have the scripting permission added in your manifest file.
That is what I had to do to fix it for me.
Also, as stated above, remove the content script from your manifest and add .js to the end of the script name in files
Related
I need to send some data to the newly created tab. I found some answers here to implement the listener first and then send a message. My event listener isn't working and can't catch the message.
manifest:
{
"manifest_version": 2,
"name": "My Cool Extension",
"version": "0.1",
"permissions": ["tabs",
"http://*/*",
"https://*/*",
"activeTab"
],
"browser_action": {
"default_icon": "icon.png"
},
"background": {
"scripts": ["background.js"]
}
}
background:
chrome.browserAction.onClicked.addListener((tab)=>{
chrome.tabs.query({active: true, currentWindow: true}, tabs => {
if(tabs.length === 1 ){
chrome.tabs.create({url:"https://www.youtube.com/", active: true}, (tab)=>{
chrome.tabs.executeScript(tab.id, {file:"content.js"},tab=>{
chrome.tabs.sendMessage(tab.id, {"Active Objects": "elo"})
})
})
}
else{
alert("wrong page")
}
});
});
content:
chrome.runtime.onUpdate.addListener(
(request, sender, sendResponse)=>{
alert("elo")
}
);
I've finally got it to work. I didn't actually tested it the first time.
In addition to replacing onUpdate with onMessage in the content script you
might want to add setTimeout functions in the background script to delay the executions of
chrome.tabs.executeScript and chrome.tabs.sendMessage. Otherwise, you might
get (as I have) a runtime.lastError: The tab was closed.
And also you should avoid having multiple function callback arguments named
tab. Otherwise, they get overriden.
I simplified the background script to get it to work. You don't actually need
to query the current tab to create a tab and execute a script inside it. So,
I've simply kept chrome.tabs.create.
background.js:
chrome.browserAction.onClicked.addListener(function (_) {
chrome.tabs.create({url: "https://www.youtube.com/", active: true},
function (yt_tab) {
setTimeout(function () {
chrome.tabs.executeScript(yt_tab.id, {file: "content.js"});
setTimeout(function () {
chrome.tabs.sendMessage(yt_tab.id, {"Active Objects":"elo"});
}, 1000);
}, 1000);
});
});
content.js:
chrome.runtime.onMessage.addListener(
function (request, sender, sendResponse) {
console.log(request);
}
);
I know there are similar questions and I've tried almost everything in them. I'm trying to build a chrome extension and it needs to pass a message from content.js to background.js.
The code:
background.js
var xstext;
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
xstext=request.stext;
});
var wtf = "https://www.google.com/search?q="+xstext;
chrome.commands.onCommand.addListener(function(command) {
if(command=="ntab"){
chrome.tabs.create({ url: wtf});
}
});
content.js
var text = window.getSelection();
var stext=text.toString();
chrome.runtime.sendMessage({stext: stext});
manifest.json
"manifest_version": 2,
"name": "vind",
"version": "1.0",
"description": "Search stuff easily!",
"background": {
"persistent": false,
"scripts": ["background.js"]
},
"content_scripts": [
{
"all_frames": true,
"run_at": "document_start",
"matches": [
"<all_urls>"
],
"js": ["content.js"]
}
],
"browser_action": {
"default_icon": {
"16": "images/icon16.png",
"32": "images/icon32.png"
},
"default_popup": "popup.html"
},
"commands": {
"ntab": {
"suggested_key": {
"default": "Alt+G",
"windows": "Alt+G",
"mac": "Alt+G",
"chromeos": "Alt+G",
"linux": "Alt+G"
},
"description": "New tab for the query"
}
}
}
I want to pass the selected text from content.js to background.js, I have tried adding return: true; in the listener to no avail. I'm getting 'undefined' added to the main string, so nothing seems to get passed. what should I do?
This approach won't work because 1) your content script is saving the text at the moment it runs which happens just one time at page load and 2) since the background script is not persistent it'll unload after receiving a message and xstext will disappear.
Do the reverse: ask the content script when the hotkey is pressed.
background.js, entire contents:
chrome.commands.onCommand.addListener(command => {
if (command === 'ntab') {
chrome.tabs.query({active: true, lastFocusedWindow: true}, ([tab]) => {
if (!tab) return;
chrome.tabs.sendMessage(tab.id, 'getText', text => {
chrome.tabs.create({
url: 'https://www.google.com/search?q=' + encodeURIComponent(text),
});
});
});
}
});
content.js, entire contents:
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
if (msg === 'getText' && document.hasFocus()
&& (!document.activeElement || !/^I?FRAME$/.test(document.activeElement.tagName))) {
sendResponse(getSelection().toString());
}
});
P.S. An advanced solution would be to run the content script on demand (using chrome.tabs.executeScript in background.js onCommand listener) so you can remove content_scripts section from manifest.json and use activeTab permission instead.
I am trying to receive some info from the content page to the popup page in chrome extension.
Here is my manifest.json:
{
"name": " Downloader",
"description": "history ",
"version": "1.0",
"permissions": [
"activeTab",
"notifications"
],
"background": {
"persistent": false,
"scripts": ["background.js"]
},
"content_scripts": [
{
"all_frames": false,
"matches": ["<all_urls>"],
"exclude_matches": [],
"js": [
"/src/jquery.js",
"/src/sheet-min.js",
"/src/file-saver-min.js"
]
// "css": [
// "js/content/page.css"
// ]
}
],
"content_scripts": [{
"matches": ["*://*.ebay.com/*"],
"js": ["content.js"],
"run_at": "document_idle",
"all_frames": false
}],
"browser_action": {
"default_title": "Download History.",
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"manifest_version": 2
}
background.js
chrome.runtime.onMessage.addListener((msg, sender) => {
// First, validate the message's structure.
if ((msg.from === 'content') && (msg.subject === 'showPageAction')) {
// Enable the page-action for the requesting tab.
chrome.browserAction.show(sender.tab.id);
}
});
content.js
// Inform the background page that
// this tab should have a page-action.
function ping() {
chrome.runtime.sendMessage('ping', response => {
if(chrome.runtime.lastError) {
setTimeout(ping, 1000);
} else {
chrome.runtime.sendMessage({
from: 'content',
subject: 'showPageAction',
});
}
});
}
ping();
// Listen for messages from the popup.
chrome.runtime.onMessage.addListener((msg, sender, response) => {
// First, validate the message's structure.
if ((msg.from === 'popup') && (msg.subject === 'DOMInfo')) {
// Collect the necessary data.
// (For your specific requirements `document.querySelectorAll(...)`
// should be equivalent to jquery's `$(...)`.)
var domInfo = {
total: document.querySelectorAll('*').length,
inputs: document.querySelectorAll('input').length,
buttons: document.querySelectorAll('button').length,
};
// Directly respond to the sender (popup),
// through the specified callback.
response(domInfo);
}
});
popup.js
const setDOMInfo = info => {
console.log(info)
};
window.addEventListener('DOMContentLoaded', () => {
// ...query for the active tab...
chrome.tabs.query({
active: true,
currentWindow: true
}, tabs => {
// ...and send a request for the DOM info...
chrome.tabs.sendMessage(
tabs[0].id,
{from: 'popup', subject: 'DOMInfo'},
// ...also specifying a callback to be called
// from the receiving end (content script).
setDOMInfo);
});
});
I know that this error occurs when the content script sends message to the background script but the background script is not ready to receive the message. After looking for a solution on stackoverflow I decided to use the ping function but as you can see above but it still gives me the same error message.
There's no chrome.browserAction.show as you can see in the documentation so the listener in background.js throws and aborts the execution. The messaging cycle never completes so to the sender it looks just like an absence of any receiver.
Each part of an extension has its own devtools.
Open devtools for the background script and you'll see the error.
There's no need for the background script here.
No need for showPageAction message either because browser_action is enabled by default.
P.S. the entire code can be simplified by switching to programmatic injection (example) so you can remove content_scripts, background script, and messaging.
Here is my scenario: By clicking the browser icon, I create a sidebar (html and css) next to the whole page, thus creating two columns (one is my sidebar, the other one is the actual page).
What I to achieve is having the sidebar stay when I reload the page or navigate to another page WITHIN the same domain. What I have right now is just the creation of the sidebar, but I have to click the browser action every time I navigate or reload the web page.
Manifest:
{
"name": "apdrop",
"version": "0.1",
"manifest_version": 2,
"description": "first prototype for apdrop extension",
"icons": {
"16": "icons/icon16.png",
"48": "icons/icon48.png",
"128": "icons/icon128.png"
},"background": {
"scripts": ["background.js"],
"persistent": false
},
"browser_action": {
"default_icon": "icons/icon19.png",
"default_title": "apdrop"
},
"permissions": [
"background",
"tabs",
"http://*/*/",
"https://*/*/"
]
}
Background.js
function injectedScript(tab, method){
chrome.tabs.insertCSS(tab.id, {file:"style.css"});
//chrome.tabs.insertCSS(tab.id, {file:"bootstrap.css"});
chrome.tabs.executeScript(tab.id, { file: 'jquery-2.1.1.min.js'});
//chrome.tabs.executeScript(tab.id, { file: 'bootstrap.min.js'});
chrome.tabs.executeScript(tab.id, { file: 'inject.js'});
}
function click(tab){
console.log("browser action clicked");
injectedScript(tab, 'click');
//alert("action button was clicked");
}
chrome.browserAction.onClicked.addListener(click);
Inject.js
var ev = $("body > *");
if (!document.getElementById('contentxf343487d32'))
{
ev.wrapAll("<div id='insidecontent65675f526567'>");
$("#insidecontent65675f526567").wrapAll("<div id='contentxf343487d32'>");
$("<div id='sidebar343gf87897fh'><div id='insidesidebar87678bbbb'><p>this is my name</p></div></div>").insertBefore("#contentxf343487d32");
}
else
{
$("#sidebar343gf87897fh").remove();
$("#insidecontent65675f526567").unwrap();
$("#insidecontent65675f526567 > div").unwrap();
}
Hope this helps clarify a bit more.
The simplest strategy would be to save state in domain's sessionStorage and have a "detector" script that re-injects your UI.
Add setting the state in your content script:
// inject.js
if (!document.getElementById('contentxf343487d32'))
{
// ...
sessionStorage["contentxf343487d32"] = true;
}
else
{
// ...
sessionStorage["contentxf343487d32"] = false;
}
Add a "detector" script:
// detect.js
if(sessionStorage["contentxf343487d32"])
{
chrome.runtime.sendMessage({injectSidebar: true});
}
Always inject the script on page load, via the manifest (and change to a better permission):
"content_scripts" : [
{
"matches": ["<all_urls>"],
"js": ["detect.js"]
}
],
"permissions": [
"background",
"tabs",
"<all_urls>"
]
In the background, inject the script upon message:
// background.js
chrome.runtime.onMessage.addListener( function (message, sender, sendResponse){
if(message.injectSidebar)
{
click(sender.tab);
}
});
If you need more persistence than sessionStorage provides, use localStorage. If you need a different logic, you can still use this skeleton of a detector signalling the background.
I have been searching around on how to accomplish this. I have found some articles most notably
Accessing Current Tab DOM Object from "popup.html"?
However I am very new to JavaScript and making chrome extensions and I have hit a dead end.
My guess is that the response isn't being received which explains why document.write("Hellp")
isn't working. Any help to fix this up would be appreciated.
I have three main files
manifest.json
{
"name": "My First Extension",
"version": "1.0",
"description": "The first extension that I made.",
"browser_action":
{
"default_icon": "icon.png",
"popup": "popup.html"
},
"permissions":
[
"tabs"
],
"content_scripts":
[{
"matches": ["<all_urls>"],
"js": ["dom.js"]
}]
}
popup.html
<html>
<body>
</body>
<script>
chrome.tabs.getSelected(null, function(tab)
{
// Send a request to the content script.
chrome.tabs.sendRequest(tab.id, {action: "getDOM"}, function(response)
{
document.write("Hello");
document.write(response.title)
});
});
</script>
</html>
dom.js
chrome.extension.onRequest.addListener(function(request, sender, sendResponse)
{
if (request.action == "getDOM")
sendResponse({dom: document.getElementsByTagName("body")[0]});
else
sendResponse({}); // Send nothing..
});
I see this is an older question, but it's unanswered and I ran into the same issue. Maybe it's a security feature, but you don't seem to be able to return a DOM object. You can, however, return text. So for dom.js:
chrome.extension.onRequest.addListener(function(request, sender, sendResponse) {
if (request.action == "getDOM")
sendResponse({dom: document.body.innerHTML});
else
sendResponse({}); // Send nothing..
});
I'm workin on an extension that transfers the html of the element as a text and then rebuilding the element back using innerHTML.
Hope that clarifies how to get the DOM elements from the current page**
This is the way I've got the DOM:
Manifest.json
{
"manifest_version": 2,
"version" : "2.0",
"name": "Prettify search",
"description": "This extension shows a search result from the current page",
"icons":{
"128": "./img/icon128.png",
"48": "./img/icon48.png",
"16": "./img/icon16.png"
},
"page_action": {
"default_icon": "./img/icon16.png",
"default_popup": "popup.html",
"default_title": "Prettify Results!"
},
// If the context is the Mach url = sends a message to eventPage.js: active icon
"content_scripts": [
{
"matches": ["http://www.whatever.cat/*"],
"js": ["./js/content.js", "./js/jquery-3.1.1.min.js"]
}
],
"permissions": [
"tabs",
"http://www.whatever.cat/*",
"http://loripsum.net/*" //If you need to call and API here it goes
],
"background":{
"scripts": ["./js/eventPage.js"],
"persistent": false
}
}
Popup.js
$(function() {
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {action: "getPage"}, function(response) {
var importedCode = response.searchResults;
var fakeHtml = document.createElement( 'html' );
fakeHtml.innerHTML = importedCode; // recieved String becomes html
});
});
Eventpage.js
>Able/disable the extension button
chrome.runtime.onMessage.addListener(function(req, sender, resp) {
if(req.todo === 'showPageAction'){
chrome.tabs.query({active:true, currentWindow:true}, function(tabs) {
chrome.pageAction.show(tabs[0].id);
});
}
});
content.js
Content_Scripts can not use the Chrome API to enable or disable the >extension icon. We pass a message to Event_Page, js, he can indeed
use the Api
chrome.runtime.sendMessage({ todo: "showPageAction"});
window.onload = function() {
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
console.log(sender.tab ?
"from a content script:" + sender.tab.url :
"from the extension");
if (request.action == "getPage"){
sendResponse({searchResults: document.body.innerHTML});
}
});
};
popup.html
Just link popup.js