I use pageMod() in my Firefox SDK app, and i want to convert to Webextension.
I declare on top on my background js file:
var pageMod = require("sdk/page-mod");
I have undesrtood in Webextension is declared in manifest.json
"content_scripts": [
{
"matches": ["*://*.mytestsite.com/*"],
"js": ["background.js"]
}
]
But i call pageMod() inside my background script and i have a lot of attachment and other. I dont know how i can convert this.
For example what i want convert:
pageMod.PageMod({
include: "*.mytestsite.com",
contentScriptFile: [self.data.url("jquery-2.0.3.min.js"), self.data.url("jquery-ui.min.js"), self.data.url("site_modal_min.js"), self.data.url("timerreview.js")],
onAttach: function onAttach(worker) {
How use my PageMod() call with Webxtension
Please note that in manifest.json, you cannot have background.js inside of a content_scripts tag. You would instead do it like this (assuming your match selector for the domain is correct):
"background": {
"scripts": ["background.js"]
},
"content_scripts": [
{
"matches": ["*://*.mytestsite.com/*"],
"js": ["timerreview.js", "jquery.js"]
}
]
If you want to communicate (variables, etc) use messaging between the background script and the content script. This is especially useful since most of the WebExtensions API is only available for background scripts.
In the content script, add:
// Listen for messages from the background script
browser.runtime.onMessage.addListener(onMessage);
function onMessage(message) {
switch(message.action)
case "testAction":
testAction(message.data);
break;
default:
break;
}
}
function sendMessage(action, data){
browser.runtime.sendMessage({"action": action, "data": data});
}
In the background script, add:
// listen for messages from the content or options script
browser.runtime.onMessage.addListener(function(message) {
switch (message.action) {
case "actionFromContent":
doSomething(); // message.data is not needed
break;
default:
break;
}
});
// See also https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/Tabs/sendMessage
function sendMessage(action, data){
function logTabs(tabs) {
for (tab of tabs) {
browser.tabs.sendMessage(tab.id, {"action": action, "data": data}).catch(function(){
onError("failed to execute " + action + "with data " + data);
});
}
}
browser.tabs.query({currentWindow: true, active: true}).then(logTabs, onError);
}
function onError(error){
console.error(error);
}
Now you can use:
sendMessage("actionFromContent") from within the content script to send a message to the background script
sendMessage("testAction", "someData") from within the background script to the active content script (the active tab)
Related
This question already has answers here:
What is the difference between a function call and function reference?
(6 answers)
Closed 1 year ago.
So I'm trying to make an extension that allows you to write custom JS to be injected on any page for a given domain. My popup loads the saved JS code, and when clicking save, the JS is evaluated, and that works just fine. I can't figure out how to get the code to evaluate on page load, though.
Here is what I have so far.
//Content.js
//Globals
var entries = {"test": "test"}; //Entries dictionary "domain": "js code"
var url = window.location.href; //Full URL of the tab
var parts = url.split("/"); //URL split by '/' character
var domain = parts[2] + ''; //Just the domain (global)
loadChanges();
chrome.runtime.onMessage.addListener(listener);
window.onload=eval(entries[domain]); //doesn't work
function listener (request, sender, sendResponse) {
console.log("Manipulating data for: " + domain);
if (request == "LOAD"){
if(entries.hasOwnProperty(domain)){
console.log("PE - Loaded Value: " + entries[domain].toString());
sendResponse(entries[domain]);
} else {
console.log("Nothing to load");
sendResponse('');
}
} else {
entries[domain] = request;
console.log(entries[domain]);
saveChanges();
eval(request); //This one DOES work
}
}
//Load saved code (on startup)
function loadChanges() {
chrome.storage.local.get(['PE'], function (data){
console.log(data.PE);
if (data.PE == null){
return;
}
entries=data.PE;
});
if(entries.hasOwnProperty(domain)){
eval(entries[domain]); //doesn't work
}
}
//Save changes to code (on button press)
function saveChanges() {
chrome.storage.local.set({PE: entries}, function(data){
console.log("Saved Value: " + entries[domain])
});
}
Note the "doesn't work" comments in there.
manifest.json
{
"name": "PersistEdit",
"version": "0.1.1",
"manifest_version": 2,
"content_scripts":[
{
"matches": ["<all_urls>"],
"js": ["content.js"],
"run_at": "document_end",
"persistent": false
}
],
"background": {
"scripts": [
"background.js"
],
"persistent": false
},
"browser_action": {
"default_popup": "popup.html",
"default_title": "PersistEdit"
},
"permissions": [
"storage"
]
}
document.addEventListener('DOMContentLoaded', onload, false);
function onload(){
chrome.tabs.query({currentWindow: true, active: true}, function (tabs){
chrome.tabs.sendMessage(tabs[0].id, "LOAD");
});
}
Didn't include my popup.html or popup.js because those parts of it work as intended, but I can include them if necessary. I'm not sure what I'm missing here, any guidance would be appreciated.
window.onload is supposed to be a function.
Here window.onload=eval(entries[domain]); you are just assigning the result of eval to onload(which happens immediately during the assignment). It's possible that entries isn't properly populated at that time.
Try the following code
window.onload=function () {
eval(entries[domain]);
}
I trying send some data from my web application to my chrome extension (like it described in the google documentation), but I have an error: Unchecked runtime.lastError: Could not establish connection. Receiving end does not exist.
My content script:
chrome.runtime.onMessageExternal.addListener(
function(request, sender, sendResponse) {
if (sender.url == blocklistedWebsite)
return; // don't allow this web page access
if (request.openUrlInEditor)
openUrl(request.openUrlInEditor);
});
And it's my manifest:
{
"name": "test-extension",
"version": "0.0.1",
"manifest_version": 2,
"background": {
"scripts": ["src/bg/background.js"],
"persistent": false
},
"content_scripts": [
{
"matches": ["http://localhost/*"],
"js": ["src/inject/inject.js"]
}
],
"externally_connectable": {
"ids": ["abcdefghijklmnoabcdefhijklmnoabc"],
"matches": ["http://localhost/*"],
"accepts_tls_channel_id": false
}
}
And test page, where I'm trying to send data:
<body>
<button onclick="processData()">Send data to extension</button>
</body>
<script>
function processData() {
/* ... */
// The ID of the extension we want to talk to.
var editorExtensionId = "abcdefghijklmnoabcdefhijklmnoabc";
// Make a simple request:
chrome.runtime.sendMessage(
editorExtensionId,
{ openUrlInEditor: 'https://google.com' },
function(response) {
if (!response.success) handleError(url);
}
);
}
</script>
Problem was been in the externally_connectable configuration. It's dosen't work with localhost. For work with it on the localhost I added in my host file this lines:
127.0.0.1 my.localhost
Then changed manifest to:
"externally_connectable": {
"ids": ["*"],
"matches": [
"http://my.localhost/*",
]
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" are prohibited. From the web page, use the runtime.sendMessage or runtime.connect APIs to send a message to a specific app or extension
ref: https://developer.chrome.com/extensions/messaging#external-webpage
Maybe it's because of your http://localhost/*
I wrote this script to send a message from a background script to a script in a new tab but for some reason, the script in the tab isn't receiving the message. Is this a problem with my script or my browser (Firefox 62.0.3)
my "manifest":
{
"manifest_version":2,
"name": "test",
"version": "1.0",
"description": "this is a test extension",
"background":{
"scripts": ["OnButtonClick.js"]
},
"permissions": [
"tabs"
],
"content_scripts": [{
"matches": ["www.youtube.com"],
"js": ["input.js"]
}],
"browser_action": {
"default_icon": "button.png",
"default_title": "test button"
}
}
my "OnButtonClick.js":
function action(){
browser.tabs.create({
url: "www.youtube.com"
});
browser.tabs.sendMessage(1,{"message":"hi"})
}
browser.browserAction.onClicked.addListener(action);
and my "input.js":
function handleMessage(msg){
console.log(msg);
}
browser.runtime.onMessage.addListener(handleMessage)
browser.tabs.create() is a asynchronous therefore, browser.tabs.sendMessage() runs even before a tab is created.
You have to wait for it to run first.
Here are some suggestions:
// first create the tab
const newTab = browser.tabs.create({
url: 'https://example.org'
});
newTAb.then(onCreated, onError);
// after tab is created
function onCreated(tab) {
browser.tabs.sendMessage(tab.id,{message: 'hi'});
}
// in case of error
function onError(error) {
console.log(`Error: ${error}`);
}
// above can also be written as this
browser.tabs.create({
url: 'https://example.org'
}).then(
tab => browser.tabs.sendMessage(tab.id,{message: 'hi'}),
error => console.log(error)
);
// another alternative for above
browser.tabs.create({url: 'https://example.org'})
.then(tab => browser.tabs.sendMessage(tab.id,{message: 'hi'}))
.catch(error => console.log(error));
// Using chrome and callback function
chrome.tabs.create({url: 'https://example.org'}, tab =>
browser.tabs.sendMessage(tab.id,{message: 'hi'})
);
// same as above, all with chrome
chrome.tabs.create({url: 'https://example.org'}, tab =>
chrome.tabs.sendMessage(tab.id,{message: 'hi'})
);
You can also use async/await but that may make it more complicated in this case.
Update on comment:
content_scripts by default run at "document_idle" (corresponds to complete. The document and all its resources have finished loading.)
"content_scripts": [{
"matches": ["www.youtube.com"],
"js": ["input.js"]
}],
Therefore, the input.js is injected once everything is loaded. However, the sendMessage() runs as soon as tab is created and thus there is no listener to listen to its message.
In your simple example, that can be fixed by "run_at": "document_start"
"content_scripts": [{
"matches": ["www.youtube.com"],
"js": ["input.js"],
"run_at": "document_start"
}],
However, if input.js needs to access DOM after receiving message, then you need to add a DOMContentLoaded or load listener and run it after the document is loaded.
why do you want to use "sendMessage"?
you can use this code
browser.tabs.executeScript(tabID, { code: "func()" /* your function in content script*/,frameId:0 /* for send to all frame or put id for use a special frame id*/});
or this code as file
browser.tabs.executeScript(tabID, { file: "/filename.js",frameId:0});
I am writing my First Chrome Plugin and I just want to get some text present on the current webpage and show it as a alert when i click the Extension. Lets say I am using any any webpage on www.google.com after some Search query, Google shows something like "About 1,21,00,00,000 results (0.39 seconds) " . I want to show this Text as an alert when i execute my plugin. This is what i am doing.
here is the manifest.json that i am using
{
"manifest_version": 2,
"name": "Getting started example",
"description": "This extension shows a Google Image search result for the current page",
"version": "1.0",
"background": {
"persistent": false,
"scripts": ["background.js"]
},
"content_scripts": [{
"matches": ["*://*.google.com/*"],
"js": ["content.js"]
}],
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"permissions": [
"activeTab"
]
}
Here is my popup.js
document.addEventListener('DOMContentLoaded', function() {
document.getElementById("checkPage").addEventListener("click", handler);
});`
function handler() {
var a = document.getElementById("resultStats");
alert(a.innerText); // or alert(a.innerHTML);
}
Here is my content.js
// Listen for messages
chrome.runtime.onMessage.addListener(function (msg, sender, sendResponse) {
// If the received message has the expected format...
if (msg.text === 'report_back') {
// Call the specified callback, passing
// the web-page's DOM content as argument
sendResponse(document.all[0].outerHTML);
}
});
Here is my background.js
var urlRegex = /^https?:\/\/(?:[^./?#]+\.)?google\.com/;
// A function to use as callback
function doStuffWithDom(domContent) {
console.log('I received the following DOM content:\n' + domContent);
}
// When the browser-action button is clicked...
chrome.browserAction.onClicked.addListener(function (tab) {
// ...check the URL of the active tab against our pattern and...
if (urlRegex.test(tab.url)) {
// ...if it matches, send a message specifying a callback too
chrome.tabs.sendMessage(tab.id, {text: 'report_back'}, doStuffWithDom);
}
});
1) run content scrip after document ready ** check "run_at"
"content_scripts": [{
"run_at": "document_idle",
"matches"["*://*.google.com/*"],
"js": ["content.js"]
}],
2) on click of extension make another js to run( popup js). The popup js has access to the ( open page document)
// A function to use as callback
function doStuffWithDom() {
//console.log('I received the following DOM content:\n' + domContent);
//get tabID when browser action clicked # tabId = tab.id
chrome.tabs.executeScript(tabId, {file: "js/popup.js"});
}
3) In popup JS simple you can set alert
var a = document.getElementById("resultStats");
alert(a.innerText); // or alert(a.innerHTML);
Just remove "default_popup" part in manifest.json, as you have listened to chrome.browserAction.onClicked event in background page. They can't live at the same time.
I have an chrome extension, with 2 content script injected by manifest and one background script.
{
"manifest_version": 2,
"name": "Test",
"permissions": [
"tabs", "<all_urls>", "activeTab", "storage"
],
"content_scripts": [
{
"matches": ["http://*/*", "https://*/*"],
"js": [
"content/autofill/lib_generic.js",
"content/autofill/lib.js"],
"run_at": "document_end"
}
],
"web_accessible_resources": [
"content/specific_scripts/*"
],
"background": {
"scripts": ["background.js"],
"persistent": false
}
}
lib_generic.js contains one function named apply_forms(...) (its description is not important). The function is called from lib.js file. But this procedure doesn't work with several pages, so for each such page a I have a special script - also with only one function named apply_forms(...).
I have a function, which takes current domain as input and returns name of desired specific script or false if generic should be used.
There is too many files and it's logic is more complicated, so I can't just list all (url, script) pairs in "content_scripts" directive (I also don't want to inject all specific files as content script).
I've tried something like this in background (note that it's only for demonstration):
var url = ""; //url of current tab
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
if(changeInfo.status == "complete") {
var filename = getSpecificFilename(url);
chrome.tabs.executeScript(tabId, {file: filename}, function() {
//script injected
});
}
});
NOTE: getSpecificFilename(...) will always return a name
But I get Unchecked runtime.lastError while running tabs.executeScript: Cannot access a chrome:// URL on the 5th line.
Can anyone help me with this? Is it the good way to "override` function definition dynamically, or should I go different way (which one, then).
Thanks.
This means, probably, that you're getting an onUpdated event on an extension/internals page (popup? options page? detached dev tools?).
One option is to filter by URL:
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
if(changeInfo.status == "complete") {
if(!tab.url.match(/^http/)) { return; } // Wrong scheme
var filename = getSpecificFilename(url);
chrome.tabs.executeScript(tabId, {file: filename}, function() {
//script injected
});
}
});
Another (and probably better) option is to make your content script request this injection:
// content script
chrome.runtime.sendMessage({injectSpecific : true}, function(response) {
// Script injected, we can proceed
if(response.done) { apply_forms(/*...*/); }
else { /* error handling */ }
});
// background script
chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {
if(message.injectSpecific){
var filename = getSpecificFilename(sender.url);
chrome.tabs.executeScript(sender.tab.id, {file: filename}, function() {
sendResponse({ done: true });
});
return true; // Required for async sendResponse()
}
});
This way you know that a content script is injected and initiated this.