I'm trying to add DOM elements to the page https://anilist.co/user/diskxo/animelist
var divEntryRow = document.getElementsByClassName("entry row")[x]
var playbutton = document.createElement("DIV");
playbutton.className = "playbutton";
var aInput = document.createElement("A");
var img = document.createElement("IMG");
aInput.appendChild(img);
playbutton.appendChild(aInput);
divEntryRow.appendChild(playbutton);
, but not the whole page is shown on loading, so my extension doesn't
add the elements in some places (play buttons on right)
also, when I move between the tabs of the site, the extension is not reloaded, since updates are made in the background only by ajax. I've been looking for ways to detect changes and reload the extension, including these:
$.ajax({
processData: false,
contentType: false,
success: function() {
doThings();
},
});
Or:
function DOMModificationHandler(){
$(this).unbind('DOMSubtreeModified.event1');
setTimeout(function(){
doThings();
$('#ContentContainer').bind('DOMSubtreeModified.event1',DOMModificationHandler);
},1000);
}
//after document-load
$('#ContentContainer').bind('DOMSubtreeModified.event1',DOMModificationHandler);
I included jquery library in my Extension. This is my manifest.json file:
{
"manifest_version": 2,
"name": "JiyuMe",
"version": "1.0",
"description": "Your next anime streaming website... it's not a streaming website!",
"content_scripts": [{
"matches": ["*://*.anilist.co/*"],
"js": ["js/jquery-3.6.0.min.js", "js/client.js"]
}],
"background": {
"scripts": ["js/background.js"]
},
"browser_action": {
"default_popup": "popup.html"
},
"permissions": [
"*://anilist.co/*",
"nativeMessaging",
"tabs",
"activeTab"
],
"content_security_policy": "script-src 'self' https://apis.google.com; object-src 'self'"
}
Do you have any ideas to understand how to fix this problem?
For the second problem I resolved with a simple function:
//Sleep function
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
//Wait for Ajax site load
$.ajax({
processData: false,
contentType: false,
success: async function() {
onPageChange("1");
},
});
async function onPageChange(oldPage_val) {
var currentPage = window.location.href;
if (currentPage != oldPage_val) {
oldPage_val = currentPage;
pageScan();
await sleep(500);
onPageChange(oldPage_val);
} else {
await sleep(500);
onPageChange(oldPage_val);
}
}
I wait for the load of the page, and then I start onPageChange() function, that check every 500 ms if the link of the page changes.
compare the old link with the new one thanks to the variable: oldPage_val
Related
I want to extract the HTML of a webpage so that I can analyze it and send a notification to my chrome extension. Sort of like how an adblocker does it when analyzing a web page for ads and then tell the extension how many possible ads there are.
I am trying to use the document object in content-scripts to get the HTML, however, I always seem to get the HTML of my popup file instead. Can anybody help?
content-script.js
chrome.tabs.onActivated.addListener(function(activeInfo) {
chrome.tabs.get(activeInfo.tabId, function(tab) {
console.log("[content.js] onActivated");
chrome.tabs.sendMessage(
activeInfo.tabId,
{
content: document.all[0].innerText,
type: "from_content_script",
url: tab.url
},
{},
function(response) {
console.log("[content.js]" + window.document.all[0].innerText);
}
);
});
});
chrome.tabs.onUpdated.addListener((tabId, change, tab) => {
if (tab.active && change.url) {
console.log("[content.js] onUpdated");
chrome.tabs.sendMessage(
tabId,
{
content: document.all[0].innerText,
type: "from_content_script",
url: change.url
},
{},
function(response) {
console.log("[content.js]" + window.document.all[0].innerText);
}
);
}
});
background.js
let messageObj = {};
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
// Arbitrary string allowing the background to distinguish
// message types. You might also be able to determine this
// from the `sender`.
if (message.type === "from_content_script") {
messageObj = message;
} else if (message.type === "from_popup") {
sendResponse(messageObj);
}
});
manifest.json
{
"short_name": "Extension",
"version": "1.0.0",
"manifest_version": 3,
"name": "My Extension",
"description": "My Extension Description",
"permissions": ["identity", "activeTab", "tabs"],
"icons": {
"16": "logo-16.png",
"48": "logo-48.png",
"128": "logo-128.png"
},
"action": {
"default_icon": "ogo_alt-16.png",
"default_popup": "popup.html"
},
"content_scripts": [
{
"matches": ["http://*/*", "https://*/*"],
"js": ["./static/js/content-script.js"],
"run_at": "document_end"
}
],
"background": {
"service_worker": "./static/js/background.js"
}
}
Your current content script is nonfunctional because content scripts cannot access chrome.tabs API. If it kinda worked for you, the only explanation is that you loaded it in the popup, which is wrong because the popup is not a web page, it's a separate page with a chrome-extension:// URL.
For your current goal, there's no need for the background script at all because you can simply send a message from the popup to the content script directly to get the data. Since you're showing the info on demand there's also no need to run the content scripts all the time in all the sites i.e. you can remove content_scripts from manifest.json and inject the code on demand from the popup.
TL;DR. Remove content_scripts and background from manifest.json, remove background.js and content-script.js files.
manifest.json:
"permissions": ["activeTab", "scripting"],
popup.html:
<body>
your UI
<script src=popup.js></script>
</body>
popup.js:
(async () => {
const [tab] = await chrome.tabs.query({active: true, currentWindow: true});
let result;
try {
[{result}] = await chrome.scripting.executeScript({
target: {tabId: tab.id},
func: () => document.documentElement.innerText,
});
} catch (e) {
document.body.textContent = 'Cannot access page';
return;
}
// process the result
document.body.textContent = result;
})();
If you want to to analyze the page automatically and display some number on the icon then you will need the background script and possibly content_scripts in manifest.json, but that's a different task.
Basically what I am trying to do is send a message from the popup.js by grabbing values from the input fields, and onMessage in the content_script to assign those values from that local scope to the global scope. I can't manage to do it! I can close the whole code in the onMessage function, but then to get to next function of the code I have to keep clicking save on the chrome extension. If someone knows this way better than I do with chrome extensions and what not please review my code and guide me to progress. I have been on this for like a week and my head is about to expload!
I have tried enclosing all the code in the onMessage function in the content_script. I have tried flipping which .js files are sending the message and trying to send the input fields from the extension as a response. I have tried to exclude the sendMessage out of the .click DOM function. Reviewed Messaging API on Google Chrome Extension and im puzzled.
content.js file
chrome.runtime.onMessage.addListener (
function(request) {
item_name = request.name;
item_color = request.color;
item_category = request.category;
item_size = request.size;
console.log(item_name);
console.log(item_color);
console.log(item_category);
console.log(item_size);
});
var url = window.location.href;
var i;
var item_category;
var item_name;
var item_color;
var item_size;
console.log(item_name);
console.log(item_color);
console.log(item_category);
console.log(item_size);
popup.js file
document.getElementById("save_input").onclick = function() {
var item_name_save = document.getElementById("item_name_save").value;
var item_color_save = document.getElementById("item_color_save").value;
var item_category_save = document.getElementById("item_category_save").value;
var item_size_save = document.getElementById("item_size_save").value;
chrome.storage.sync.set({'item_name_save' : item_name_save}, function() {
console.log('Value is set to ' + item_name_save);
});
chrome.storage.sync.set({'item_color_save' : item_color_save}, function() {
console.log('Value is set to ' + item_color_save);
});
chrome.storage.sync.set({'item_category_save' : item_category_save}, function() {
console.log('Value is set to ' + item_category_save);
});
chrome.storage.sync.set({'item_size_save' : item_size_save}, function() {
console.log('Value is set to ' + item_size_save);
});
const msg = { name: item_name_save,
color: item_color_save,
category: item_category_save,
size: item_size_save
};
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, msg, function() {
});
});
location.reload();
// window.close();
}
manifest.json
{
"manifest_version": 2,
"name": "Supreme bot",
"description": "A bot for Supreme",
"version": "1.0",
"background": {
"scripts": ["background.js"]
},
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html",
"default_title": "Supreme bot"
},
"content_scripts": [
{
"matches": ["http://*/*", "https://*/*"],
"js": ["bot-functionallity.js"]
}
],
"permissions": [
"activeTab",
"tabs",
"storage",
"tabs"
]
}
(When all code is in the onMessage function, works but not as intended)
The Incorrect Functionality
I basically want the content.js to NOT be all inside of the onMessage function because that doesn't work properly, and have it assign values in the global scope from the onMessage function. I just want that info to be sent 1 time from the popup.js to the content.js.
I just set the value with chrome.storage.sync.set and use chrome.storage.sync.get in the other file...
I am trying to use a google extension to keep name of people that I have already seen on Linkedin
Form this picture when I click the next page or change the page I am trying to get the google extension to keep the track of the name of person in this page.
This is my Manifest:
{
"manifest_version": 2,
"name": "One test",
"description": "test.",
"version": "1.0",
"background": {"scripts": ["test.js"]},
"page_action":
{
"default_icon": "pic1.png"
},
"permissions": ["tabs"],
"content_security_policy": "script-src 'self' https://www.jbhired.com; object-src 'self' "
}
And these are the functions I am trying to use to keep track of the data:
function check(tab_id, data, tab) {
if (tab.url.indexOf('https://www.linkedin.com/search/results/people/') > -1) {
chrome.pageAction.show(tab_id);
}
};
chrome.tabs.onUpdated.addListener(check);
// _______________________________________________________________________
var data = (function(){
return {
bigArr: [],
}
})();
var interfaceCtrl = (function() {
var selector = document.querySelector('.page-list');
return {
selectTwo: selector.childNodes[1].childNodes[5],
cons: document.querySelector('.nav-item__profile-member-photo').alt,
nextClass: document.querySelector('.next'),
selectOne: selector.childNodes[1].childNodes[2].classList.value
}
})();
var listeners = (function(arr, ui){
document.querySelector('.results-paginator').addEventListener('click',function(event){
if(event.target.innerText === ui.selectTwo.innerText || ( event.target.classList.value === 'next-text' && ui.selectOne == 'active')) {
arr.bigArr.push({
consultant: ui.cons,
search: window.location.href,
time: Date.now()
});
}
});
})(data,interfaceCtrl);
I am trying try but it's still not working. I'm really a newbie on both javascript and google extensions. help me please :(
I'm writing my first Chrome extension and have hit a brick wall when it comes to setting and using "options." I've used Google's documentation to learn how to set up an options page and have opted to set it as my default popup in the extension.
Here's my manifest for reference:
{
"manifest_version": 2,
"name": "MyExtension",
"description": "MyDescription",
"version": "0.0",
"options_page": "options.html",
"browser_action": {
"default_icon": "on.png",
"default_popup": "options.html",
"default_title": "Manage Tools!"
},
"permissions": [
"storage",
"tabs",
"activeTab",
"https://ajax.googleapis.com/"
],
"content_scripts": [{
"matches": ["specialURL.com*"],
"js": ["jquery-3.1.1.min.js", "content.js"]
}],
"web_accessible_resources": [
"script.js"
],
"background": {
"scripts": ["background.js"]
}
}
My content.js page contains the following:
var s = document.createElement('script');
s.src = chrome.extension.getURL('script.js');
s.onload = function() {
this.remove();
};
(document.head || document.documentElement).appendChild(s);
which loads my script file (script.js). Inside of script.js there are two methods
function foo() { -code- }
function bar() { -code- }
options.js:
function save_options() {
var alltoggle = document.getElementById('alltoggle').checked;
var footoggle = document.getElementById('footoggle').checked;
var bartoggle = document.getElementById('bartoggle').checked;
chrome.storage.sync.set({
allsetting: alltoggle,
foosetting: footoggle,
barsetting: bartoggle
}, function () {
// Update status to let user know options were saved.
var status = document.getElementById('status');
status.textContent = 'Options saved.';
setTimeout(function () {
status.textContent = '';
}, 750);
});
}
// Restores select box and checkbox state using the preferences
// stored in chrome.storage.
function restore_options() {
// Default to true
chrome.storage.sync.get({
allsetting: true,
foosetting: true,
barsetting: true
}, function (items) {
document.getElementById('alltoggle').checked = items.allsetting;
document.getElementById('footoggle').checked = items.foosetting;
document.getElementById('bartoggle').checked = items.barsetting;
});
}
document.addEventListener('DOMContentLoaded', restore_options);
document.getElementById('save').addEventListener('click', save_options);
document.addEventListener('DOMContentLoaded', function () {
document.querySelector('#alltoggle').addEventListener('change', allHandler);
// Turn on/off all features
function allHandler() {
$("input:checkbox").prop('checked', $(this).prop("checked"));
}
});
The problem comes in when I try to load settings back and apply them to my content scripts. Specifically, I can't find how to do that anywhere.
Solved
At first I thought this was a messages issue, but it wasn't. By adding the following to content.js I was able to check storage for my settings and execute code from there.
var fooON;
chrome.storage.sync.get("foosetting", function(result) {
fooON = result.foosetting;
//confirm
console.log(result.foosetting);
});
var barON;
chrome.storage.sync.get("barsetting", function(result) {
barON = result.barsetting;
//confirm
console.log(result.barsetting);
});
Then, by separating foo() and bar() into two scripts I could make the script injection in content.js selective by adding if(fooOn){-inject foo()-} etc.
For others facing similar issues
You can access options saved to storage.sync by using the chrome.storage.sync.get() API call in your content scripts.
var yourSetting;
chrome.storage.sync.get("yourSetting", function(result) {
yourSetting = result.yourSetting;
});
Hi I've written the following extension for our company machines that should in practice remove any numbers from a list that is held on our webserver.
Whilst this works in it dramatically slows down loading of medium/webpages and chrome appears to look through the list of numbers (the favicon for example refreshes whilst this happens).
Any help or guidance for a better way of achieving the same result would be immensely appreciated.
[highlight.js]
// This array will all of the numbers to highlight
var numberArray = [''];
var UPDATE_INTERVAL = 57600; // Update after 1 Minute
// Retrieve script from storage
chrome.storage.local.get({
lastUpdated: 0,
code: ''
}, function(items) {
if (Date.now() - items.lastUpdated > UPDATE_INTERVAL) {
// Get updated file, and if found, save it.
get('http://webaddress/highlight.js', function(code) {
if (!code) return;
chrome.storage.local.set({lastUpdated: Date.now(), code: code});
});
}
if (items.code) // Cached script is available, use it
execute(items.code);
else // No cached version yet. Load from extension
get(chrome.extension.getURL('highlight.js'), execute);
});
//
function execute(code) {
try { window.eval(code); } catch (e) { console.error(e); }
// Run number replacement.
numberArray.forEach(function(v){
var number = v;
ve = new RegExp(number, "g");
document.body.innerHTML = document.body.innerHTML.replace(ve,"DO-NOT-CALL");
});
}
function get(url, callback) {
var x = new XMLHttpRequest();
x.onload = x.onerror = function() { callback(x.responseText); };
x.open('GET', url);
x.send();
}
[manifest.json]
{
"background": {
},
"content_scripts": [ {
"js": [ "highlight.js"],
"run_at" : "document_idle",
"matches": [ "http://*/*", "https://*/*" ],
"css": [ "style.css" ]
} ],
"content_security_policy": "script-src 'self' 'unsafe-eval' https://ssl.google-analytics.com; object-src 'self'",
"description": "Detect telephone numbers and remove blocked numbers.",
"icons": {
"128": "icon_128.png",
"16": "icon_16.png",
"32": "icon_32.png",
"48": "icon_48.png"
},
"manifest_version": 2,
"name": "CLS Call Bar",
"permissions": [ "tabs", "storage", "http://*/*", "https://*/*" ],
"version": "1.0.0"
}
You are replacing innerHTML of whole document for every number which seems highly inefficient
document.body.innerHTML = document.body.innerHTML.replace(ve,"DO-NOT-CALL");
Instead you can use numbers in their immediate parent id/class as identifier like so
<span id="number-holder-{{number}}">{{number}}</span>
( it might already be like this as it is a common templating practice )
and in your content script do something like
numberArray.forEach(function(v){
document.getElementById("number-holder-"+v).innerHTML = "DO-NOT-CALL"
}
If changing the HTML is not an option then do the regex replace on a temporary variable with document.body.innerHTML as initial value and assign it back after the loop.
I've managed to solve this with thanks to Aman Verma for pointing out my replacing the whole innerHTML I've changed the following and its now very efficient...
From
numberArray.forEach(function(v){
var number = v;
ve = new RegExp(number, "g");
document.body.innerHTML = document.body.innerHTML.replace(ve,"DO-NOT-CALL");
});
To
numberArray.forEach(function(v){
var treeWalker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT),
textNode;
while(textNode = treeWalker.nextNode()) {
if(textNode.parentElement.tagName !== 'SCRIPT') {
textNode.nodeValue = textNode.nodeValue.replace(v, "DO NOT CALL THIS NUMBER");
}
}
});