I'm well aware that there are older questions regarding this issue, but I havent't found a solution from them.
My extension is supposed to detect a copy event in contentScript.js and pass the information, that event has been detected to oncopy.js. After that oncopy.js is supposed to copy users clipboard contents and pass them to popup.js, where the content is stored using Googles storage API, and set to the input fields value in popup.html.
The copy detection works perfectly, but I don't know what to do after that. This is my first extension, so I'm still trying to get hang of things.
Here are my manifest.jsons relevant parts:
"permissions": [
"activeTab",
"storage",
"clipboardRead"
],
"background": {
"scripts": ["oncopy.js"],
"persistent": false
},
"content_scripts": [
{
"matches": ["http://*/*", "https://*/*"],
"js": ["contentScript.js"]
}
]
contentScript.js:
// Fires when copy event is detected
document.addEventListener("copy", () => {
chrome.runtime.sendMessage({event: "copy"}, msg => console.log(msg))
})
oncopy.js e.g. the background script:
console.log("oncopy.js background scipt is running...");
// When copy event is detected and message of it is received, this starts to run
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
chrome.runtime.sendMessage({ event: "new clipboard" }, () => {
// This is supposed to get the clipboard contents from user
bg = chrome.extension.getBackgroundPage();
bg.document.body.innerHTML = "";
var helperdiv = bg.document.createElement("div");
document.body.appendChild(helperdiv);
helperdiv.contentEditable = true;
var range = document.createRange();
range.selectNode(helperdiv);
window.getSelection().removeAllRanges();
window.getSelection().addRange(range);
helperdiv.focus();
bg.document.execCommand("Paste");
var clipboardContents = helperdiv.innerHTML;
});
sendResponse("Message has been processed by background page");
});
popup.js:
// This is supposed to set all of the clipboards to input fields values
document.body.onload = () => {
chrome.storage.sync.get("clipboards", (clipboards) => {
if (!chrome.runtime.error) {
document.getElementById("clipboard1").value = clipboards[0];
}
});
};
// The function that gets clipboard contents in oncopy.js is supposed to pass the contents here
// addClipboard is supposed to handle multiple clipboards, but for the sake of simplicity I'm using one as an example
function addClipboard(clipboard) {
chrome.storage.get("clipboards", (clipboards) => {
clipboards[0] = clipboard;
chrome.storage.sync.set({'clipboards': clipboards}, () => {
message('Clipboard saved');
});
document.getElementById("clipboard1").value = clipboards[0];
});
}
and finally the popup.html:
<h2>Clipboards</h2>
<form>
<input type="text" id="clipboard1" value="Empty" readonly>
</form>
Related
I want to call a function that is implemented in the content script of an extension, that gets the selected text from webpages, from a function in the background script that will be later called in a listener connected to a menu item.
Is that possible and what would be the shortest way to do it?
Here are the relevant code snippets:
manifest.json
"background": {
"scripts": ["background.js"]
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content.js"]
}
]
content.js
var text = "";
function highlightedText() {
text = content.getSelection();
}
background.js
function listenerFunction() {
highlightedText();
/* Doing various stuff that have to use the text variable */
}
browser.menus.onClicked.addListener((info, tab) => {
highlightedText();
});
Obviously, the above code is not working as the "highlighted" function is now visible from the background script.
So, what's the quickest / shortest way to make the code work?
OK. I'm having to crib this from one of my own private extensions but the gist is this:
In the background script set up the menu, and assign a function to the onclick prop:
browser.menus.create({
id: 'images',
title: 'imageDownload',
contexts: ['all'],
onclick: downloadImages
}, onCreated);
Still in the same script get the current tab information, and send a message to the content script.
function getCurrentTab() {
return browser.tabs.query({ currentWindow: true, active: true });
}
async function downloadImages() {
const tabInfo = await getCurrentTab();
const [{ id: tabId }] = tabInfo;
browser.tabs.sendMessage(tabId, { trigger: 'downloadImages' });
}
The content script listens for the message:
browser.runtime.onMessage.addListener(data => {
const { trigger } = data;
if (trigger === 'downloadImages') doSomething();
});
And once the processing is done pass a new message back to the background script.
function doSomething() {
const data = [1, 2, 3];
browser.runtime.sendMessage({ trigger: 'downloadImages', data });
}
And in a separate background script I have the something like the following:
browser.runtime.onMessage.addListener(data => {
const { trigger } = data;
if (trigger === 'downloadImages') ...
});
"content_scripts": [
{
"matches": [
"https://www.google.com/*"
],
"js": ["contentScript.bundle.js"],
"css": ["styles.css"]
}
],
How can I make extension turned off or not clickable if its not matched with content_script link?
You can implement something like this in the background file,
Steps
Create an Array of domains which has been specified in your content script.
Add a listener on the tab selection changed and tab updated.
Checkout the currently active tab is in your array or not.
Set a flag value true false using the above check.
On extension click now, check if the flag is true then perform the task else ignore the click.
let activeExtension = false;
const contentScriptMatchArray = ['https://www.google.com', 'https://www.youtube.com'];
chrome.tabs.onActivated.addListener(handleExtensionActiveState);
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
handleExtensionActiveState(tab);
});
function handleExtensionActiveState(tabs) {
if (tabs.tabId) {
chrome.tabs.get(tabs.tabId, (tab) => {
activeExtension = checkWhiteListedOrigin(tab);
});
} else {
activeExtension = checkWhiteListedOrigin(tabs);
}
}
function checkWhiteListedOrigin(tab) {
const tabURl = new URL(tab.url);
return contentScriptMatchArray.includes(tabURl.origin);
}
chrome.browserAction.onClicked.addListener(function () {
if (activeExtension) {
console.log('Clicked');
}
});
Note
you can also change your extension icon, so your extension will look like it's actually disabled
References
https://developer.chrome.com/docs/extensions/reference/tabs/#event-onActiveChanged
I am working on building a Javascript (in-browser) Instagram bot. However, I ran into a problem.
If you run this script, the first function will be called and the page will be redirected to "https://www.instagram.com/explore/tags/samplehashtag/" and the second function will be called immediately after (on the previous URL before the page changes to the new URL). Is there a way to make the second function be called after this second URL has been loaded completely?
I have tried setting it to a Window setInterval() Method for an extended time period, window.onload and a couple of other methods. However, I can't seem to get anything to work. Any chance someone has a solution?
This is my first chrome extension and my first real project, so I may be missing something simple..
manifest.json
{
"name": "Inject Me",
"version": "1.0",
"manifest_version": 2,
"description": "Injecting stuff",
"homepage_url": "http://danharper.me",
"background": {
"scripts": [
"background.js"
],
"persistent": true
},
"browser_action": {
"default_title": "Inject!"
},
"permissions": [
"https://*/*",
"http://*/*",
"tabs"
]
}
inject.js
(function() {
let findUrl = () => {
let hashtag = "explore/tags/samplehashtag/";
location.replace("https://www.instagram.com/" + hashtag);
}
findUrl();
})();
background.js
// this is the background code...
// listen for our browerAction to be clicked
chrome.browserAction.onClicked.addListener(function(tab) {
// for the current tab, inject the "inject.js" file & execute it
chrome.tabs.executeScript(tab.ib, {
file: 'inject.js'
});
});
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
chrome.tabs.executeScript(tab.ib, {
file: 'inject2.js'
});
});
inject2.js
(function() {
if (window.location.href.indexOf("https://www.instagram.com/explore/tags/samplehashtag/") != -1){
let likeAndRepeat = () => {
let counter = 0;
let grabPhoto = document.querySelector('._9AhH0');
grabPhoto.click();
let likeAndSkip = function() {
let heart = document.querySelector('.glyphsSpriteHeart__outline__24__grey_9.u-__7');
let arrow = document.querySelector('a.coreSpriteRightPaginationArrow');
if (heart) {
heart.click();
counter++;
console.log(`You have liked ${counter} photographs`)
}
arrow.click();
}
setInterval(likeAndSkip, 3000);
//alert('likeAndRepeat Inserted');
};
likeAndRepeat();
}
})();
It is not clear from the question and the example, when you want to run your function. But in chrome extension there is something called Message Passing
https://developer.chrome.com/extensions/messaging
With message passing you can pass messages from one file to another, and similarly listen for messages.
So as it looks from your use case, you can listen for a particular message and then fire your method.
For example
background.js
chrome.runtime.sendMessage({message: "FIRE_SOME_METHOD"})
popup.js
chrome.runtime.onMessage.addListener(
function(request) {
if (request.message == "FIRE_SOME_METHOD")
someMethod();
});
EDIT
Also if you want to listen for the URL changes, you can simply put a listener provided as in the documentation.
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
console.log('updated tab');
});
I have a Chrome extension that scrapes some values from a webpage based on some query selectors that are provided via an API call.
Relevant portion of manifest.json:
"background": {
"scripts": ["js/background.js"],
"persistent": false
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["js/jquery.min.js"]
}
],
"permissions": [
"<all_urls>",
"storage",
"activeTab"
]
}
js/background.js:
The idea here is that if a user has entered an atsmap value on their options page, we should perform an API call.
chrome.storage.sync.get(['atsmap'], function(result) {
if (result.atsmap) {
var url = "https://myurl.com/AtsMapping.aspx?AtsCode=" +
encodeURIComponent(result.atsmap)
fetch(url).then(r => r.text()).then(text => {
console.log(text);
response = JSON.stringify(text);
chrome.storage.sync.set({"fieldmapping": response}, function() {
console.log('Fieldmapping is set to ' + response);
});
})
}
return true;
});
This portion appears to be working properly, here is the console from the background page:
In popup.js (which is included at the bottom of popup.html), I call an inject.js script after the DOM is loaded:
// DOM Ready
$(() => {
'use strict';
chrome.tabs.executeScript({file: 'js/inject.js'}, () => {
// We don't need to inject code everwhere
// for example on chrome:// URIs so we just
// catch the error and log it as a warning.
if (chrome.runtime.lastError) {
console.warn(chrome.runtime.lastError.message);
}
});
// injected code will send an event with the parsed data
chrome.runtime.onMessage.addListener(handleInjectResults);
});
And finally, in js/inject.js, I get the value of fieldmapping from storage and attempt to use it:
(function () {
'use strict';
let fieldmap;
let message;
console.log("test");
chrome.storage.sync.get(['atsmap'], function(result) {
if (result.atsmap) {
chrome.storage.sync.get(['fieldmapping'], function(result) {
console.log('Value currently is ' + result.fieldmapping);
fieldmap = JSON.parse(result.fieldmapping);
console.log(fieldmap);
// <key> : { // ID of input on popup.js
// selector: <selector> // DOM selector of value in page
// value: <value> // value to use in popup.js
// }
if(fieldmap.AtsMapping[4].atsMapNotes == 'John Smith (2)') {
message = {
txtLName: {
selector: fieldmap.AtsMapping[6].lastName,
value: null
},
When I go to a demo page that I've setup for the scraping, then click my extension icon, rather than scraping the page for the form values, I get the following in the console:
I don't understand how, on inject.js line 32, I can console.log(fieldmap); and get what appears to be the proper response, and yet on inject.js line 39, the same fieldmap is undefined.
Any suggestions would be helpful as I'm completely lost here.
Edit: Modified code using https://developer.chrome.com/extensions/devtools#evaluated-scripts-to-devtools as reference. Still no luck.
I'm trying to code a chrome-extension which uses chrome.* API call and save portions of the result in a file. I want to automate everything from the loading of the page to the text file download and hence, I don't want to use the browser.onclick() event.
My current attempt has no effect.
What changes would I need to make?
https://stackoverflow.com/a/16720024
Using the above answer as reference, I attempted the following:
manifest.json
{
"name":"Test Extension",
"version":"0.0.1",
"manifest_version": 2,
"description":"Description",
"permissions":["tabs"],
"background": {
"scripts": ["background.js"]
},
"devtools_page": "devtools.html"
}
background.js
// Background page -- background.js
chrome.runtime.onConnect.addListener(function(devToolsConnection) {
// assign the listener function to a variable so we can remove it later
var devToolsListener = function(message, sender, sendResponse) {
// Inject a content script into the identified tab
chrome.tabs.executeScript(message.tabId,
{ file: message.scriptToInject });
}
// add the listener
devToolsConnection.onMessage.addListener(devToolsListener);
devToolsConnection.onDisconnect.addListener(function() {
devToolsConnection.onMessage.removeListener(devToolsListener);
});
}
devtools.js
var backgroundPageConnection = chrome.runtime.connect({
name: "devtools-page"
});
backgroundPageConnection.onMessage.addListener(function (message) {
// Handle responses from the background page, if any
});
chrome.devtools.network.onRequestFinished.addListener(
function(request) {
chrome.runtime.sendMessage({
string: "Hi",
tabId: chrome.devtools.inspectedWindow.tabId,
scriptToInject: "content.js"
});
}
);
chrome.runtime.sendMessage({
string: "Hi",
tabId: chrome.devtools.inspectedWindow.tabId,
scriptToInject: "content.js"
});
content.js
alert("Hello");