JavaScript will not run from Chrome extension context menu - javascript

Disclaimer: I am new to JavaScript and I have never developed a Chrome extension before.
I am trying to develop a Chrome extension that runs some JavaScript when the user selects some text on a page, right-clicks, then clicks a context menu button. I have determined (based on running it from the Chrome console) that the JavaScript I've written runs as expected. Now all is left is to make an extension.
I can get the extension to load, and I can get it to appear on the page and to appear to run. However, it doesn't seem to do anything, and the console doesn't return any output. (I read that I can't run inline JavaScript with event pages, hence using addListener.) Have I set up the context menu incorrectly? Is there an error (or several) in my script?
manifest.json
{
"name": "My Extension",
"description": "sample",
"version": "0.0.1",
"permissions": ["contextMenus"],
"background": {
"persistent": false,
"scripts": ["background.js"]
},
"manifest_version": 2
}
background.js
chrome.runtime.onInstalled.addListener(function() {
var context = "selection";
var title = "My Extension";
var id = chrome.contextMenus.create({"title": title, "contexts":[context],
"id": "context" + context});
});
chrome.contextMenus.onClicked.addListener(getSHA);
// Get file path of file to be staged
// Get SHA
function getSHA(){
stagedFile = window.getSelection().toString()
console.log(stagedFile)
baseURL = window.location.href.slice(0, -6);
prNumber = baseURL.slice(-4);
xhr = new XMLHttpRequest();
xhr.open("GET", "https://api.github.com/repos/kubernetes/kubernetes.github.io/pulls/"+prNumber, false);
xhr.send();
json_data = JSON.parse(xhr.responseText);
shaValue = (json_data.head.sha)
console.log("SHA: "+shaValue)
getNetlify;
};
// Get Netlify URL
function getNetlify(){
xhr2 = new XMLHttpRequest();
xhr2.open("GET", "https://api.github.com/repos/kubernetes/kubernetes.github.io/commits/"+shaValue+"/status", false);
xhr2.send();
json_data2 = JSON.parse(xhr2.responseText, function(key, value) { if (key == "target_url" && value.includes("netlify")) { netlifyURL = value; }});
openStaging
};
// Stage file
function openStaging(){
window.open(netlifyURL+"/"+stagedFile)
};

You need to add a "content script" to your manifest.json. That is the kind of code that gets injected into the page to run. The background script didn't have access to the page at all. So, check out the documentation on content scripts. https://developer.chrome.com/extensions/content_scripts
You need to add the following piece to your code to your manifest.json:
"content_scripts": [
{
"matches": ["http://www.google.com/*"],
"css": ["mystyles.css"],
"js": ["myscript.js"]
}
]
With this code, any time the user goes to a site that "matches" the url I provided, then the extension will inject into that page mystyles.css and myscript.js. So... your pattern would be something like http*://*/* . That will inject the script onto any page that the user will go to.
Next, to accomplish what you are trying to accomplish, you don't need a background script. So you can remove that from your manifest.json.
So your manifest.json would look like this:
{
"name": "My Extension",
"description": "sample",
"version": "0.0.1",
"permissions": ["contextMenus"],
"content_scripts": [
{
"matches": ["http://www.google.com/*"],
"css": ["mystyles.css"],
"js": ["myscript.js"]
}
],
"manifest_version": 2
}
Then put your code into the myscript.js file (or whatever you want to call yours), and you should see this start to run on the page.

Related

Can't access content of page when attempting to add Dom to chrome-extension:// URL

My chrome extension spawns a temp .html page. I want to manipulate the DOM of the sample.html page that was created, but can't. I can manipulate the DOM for any other page without issue. The problem seems to be with the fact my temp .html page resides within chrome-extension://
Error Message:
Unchecked runtime.lastError while running tabs.executeScript: Cannot access contents of url "chrome-extension://123/sample.html?id=100". Extension manifest must request permission to access this host.
Note: for simplicity sake I provided sample code that exhibits the same Error. Once loaded I can use the key combo to inject a div and some text into any webpage (Mac-> Cmd+Shift+P or PC Ctrl+Shift+P)
I've tried adding all possible permissions and even web_accessible_resources to the manifest.json. (I don't believe this to be the issue). I've tried different ways to inject the code into the sample.html by calling out the specific tabId, activeTab or even setting the tabId to null within the background file. I've read through stackoverflow, googled and looked around but came up short.
manifest.json
{
"manifest_version": 2,
"name": "sample1",
"description": "sample1",
"version": "0.0.1",
"browser_action":
{
"default_title": "sample"
},
"commands":
{
"saveImageCommand":
{
"suggested_key":
{
"default": "Ctrl+Shift+Z",
"mac": "Command+Shift+Z"
},
"description": "Toggle Save Image"
},
"playback":
{
"suggested_key":
{
"default": "Ctrl+Shift+P",
"mac": "Command+Shift+P"
},
"description": "load player Image"
}
},
"permissions": [
"tabs",
"activeTab",
"storage",
"<all_urls>",
"*://*/*"
],
"background":
{
"persistent": false,
"scripts": ["background.js"]
}
,
"web_accessible_resources": [
"chrome-extension://*/sample.html?id=*"
]
}
background.js
chrome.commands.onCommand.addListener(function(command) {
if (command === 'saveImageCommand') {
capturecurrent();
}
if (command === 'playback') {
chrome.tabs.executeScript(null, {
code: 'var divNode = document.createElement("div");divNode.setAttribute("id", "video1Div");var instructions = document.createTextNode("testing");divNode.appendChild(instructions);document.body.appendChild(divNode)'
});
}
});
chrome.browserAction.onClicked.addListener(function() {
chrome.tabs.captureVisibleTab(function(screenshotUrl) {
var viewTabUrl = chrome.extension.getURL('sample.html')
chrome.tabs.create({ url: viewTabUrl });
});
});
sample.html
<html>
<head></head>
<body>
<div id="firstDiv">firstDiv</div>
</body>
</html>
Expected Results:
For me to interact directly with the DOM on the temp sample.html page.
Note:
I don't want to build out buttons for DOM manipulation directly within the sample.html page. That defeats the purpose of this exercise. Esp since I want to use shortcut key combos to call this DOM manipulation (Mac-> Cmd+Shift+P or PC Ctrl+Shift+P)
Actual Results:
I am able to interact with the DOM on any normal website using the shortcut key combo but not the sample.html that URL starts with chrome-extension://

How to solve Chrome extension cannot find DOM event when fired on click?

I have a chrome extension that fires on a button click, the extension still can't access DOM elements, even though the page has clearly loaded.
I've seen other posts say it is because the elements are being created dynamically but not in my case.
The only thing that works is to wrap my debugTools.js in a timeout and fire it after 800 or so milliseconds.
The code inside debugTools.js is the part not working. I don't understand why is says "theWrapper" is undefined even if I wait 10 minutes before clicking my button which executes the code.
wrapper definitely exists on the page I navigate to before clicking my button. It is there in regular HMTL code. Not dynamically generated. I feel posting this page code would just confuse the question.
// manifest.json
{
"name": "Jams Dev Tools",
"version": "1.0",
"description": "Provides dev tools",
"background": {
"scripts": ["background.js"],
"persistent": true
},
... // icons are included here
"manifest_version": 2,
"web_accessible_resources": [
"debugTools.css",
"debugTools.js"
],
"permissions": [
"https://*/*",
"http://*/*",
"tabs",
"activeTab"
]
}
// debugTools.js
var theWrapper = document.getElementById("wrapper");
console.log(theWrapper.style.width);
// injectFiles.js
if(document.getElementById("debugJs") == undefined) {
// Inject Css
var debugJs = document.createElement('script');
var theSource = chrome.extension.getURL("debugTools.js");
debugJs.setAttribute( 'src', theSource );
debugJs.id = "debugJs";
document.body.appendChild(debugJs);
}
// background.js
chrome.browserAction.onClicked.addListener(function (tab) {
// for the current tab, inject the "inject.js" file & execute it
chrome.tabs.executeScript(tab.id, {
file: 'injectFiles.js'
});
});
Ok, so this was one of those times when you just can't see the wood for the trees. Thank you all for any help you have given. It turns out I'm an idiot. The problem can be summed up with the below snippet.
debug();
var theWrapper = document.getElementById("wrapper");
function debug() {
console.log(theWrapper.width);
}

Reading specific DOM items from a website and putting the content into HTML items with a google extension

I setup this layout for an extension I am trying to build to make my work easier.
manifest.json
{
"manifest_version": 2,
"name": "Work Order Dispatcher",
"version": "0.1",
"description": "Work order dispatcher for BeHome/v12",
"background": {
"persistent": false,
"scripts": ["background.js"]
},
"content_scripts": [{
"matches": ["*://*.v12.instantsoftware.com/*"],
//"matches": ["www"],
"js": ["content.js"]
}],
"browser_action": {
"default_title": "Work Order Dispatcher"
},
"permissions": [
"activeTab"
]
}
Background.js
// Regex-pattern to check URLs against.
// It matches URLs like
//var urlRegex = /^https?:\/\/(?:[^./?#]+\.)?v12.instantsoftware\.com/;
var urlRegex = /^https?:\/\/(?:[^./?#]+\.)?v12\.instantsoftware\.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);
}
});
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.getElementsByTagName('body'));
}
});
I am trying to pull data from a text field on a website and insert it in another text field on a different website. I use the two sites side by side. In my console I get:
4background.js:8 I received the following DOM content:
undefined
I think it is only pulling the background DOM of the extension itself?
I guess the confusion comes in at would I be able to store the content of a dom item and place it into an HTML text area of my own (which I have yet to build) and then be able to send that off to my other website so I can dispatch my guys. Let me know if you need more details about what I am trying to accomplish.
Thanks in advance.

Chrome-extension undefined response from background to popup

I'm currently trying to write a Chrome extension that notifies me about DOM changes on the target page.
The content script uses a a long lived connection to send a message to the background page if the element changes. Console.log displays the content of the message and everything seems to be fine.
But if I send the same message from background page to popup console.log displays undefined. I used chrome.runtime.sendMessage and chrome.extension.sendRequest but the result is the same. Using chrome.runtime.connect to connect background to popup throws a Attempting to use a disconnected port object although it worked for sending messages from content script to background page.
I want to send the notification from content_script to background to popup. Though I'm not sure if I even need to send it to the background in the first place or if it's better to send it straight to the popup.
I'm still new to Chrome extensions and trying to figure out how the examples from Google's site work.
my code :
Manifest.json
{
"name": "Chrome Extension \\o.o/",
"description": "doing stuff",
"version": "1.0",
"manifest_version": 2,
"permissions":
[
"tabs",
"http://*/*",
"background"
],
"background":
{
"scripts": ["background.js"]
},
"content_scripts":
[{
"matches": [" /* my website */ "],
"js": ["content_script.js"]
}],
"browser_action":
{
"default_popup": "popup.html"
}
}
content_script.js
var audio = document.getElementById("audioSource");
// Open up a long-lived connection to background page
var port = chrome.runtime.connect({name:"stuffChanged"});
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
console.log("stuff happened, audio source changed to " + audio.src);
// notify background page
port.postMessage({notification: audio.src});
})
});
observer.observe(document.getElementById("audioSource"), {attributes: true});
background.js
var toPopup = chrome.runtime.connect({name:"update"});
chrome.runtime.onConnect.addListener(function(port){
if(port.name == "stuffChanged"){
port.onMessage.addListener(function(msg){
var notif = msg.notification;
// message from content_script, works
console.log(notif);
// send to pop up
// returns undefined
chrome.runtime.sendMessage({update: notif});
// disconnected port object
toPopup.postMessage({notification: notif});
});
}
});
popup.js
// returns undefined
chrome.extension.onMessage.addListener(function(msg){
console.log(msg.udpate);
});
// doesn't work at all
chrome.runtime.onConnect.addListener(function(toPopup){
toPopup.onMessage.addListener(function(msg){
console.log(toPopup.notification);
});
});
Can anyone help ?

How to log fetched network resources in JavaScript?

Is there a way to access the list of resources that the browser requested (the ones found in this Chrome inspector's network panel)?
I would like to be able to iterate through these fetched resources to show the domains that have been accessed, something like:
for (var i = 0; i < window.navigator.resources.length; i++) {
var resource = window.navigator.resources[i];
console.log(resource); //=> e.g. `{domain: "www.google-analytics.com", name: "ga.js"}`
}
Or, maybe there is some event to write a handler for, such as:
window.navigator.onrequest = function(resource) {
console.log(resource); //=> e.g. `{domain: "www.google-analytics.com", name: "ga.js"}`
}
It doesn't need to work cross browser, or even be possible using client-side JavaScript. Just being able to access this information in any way would work (maybe there's some way to do this using phantomjs or watching network traffic from a shell/node script). Any ideas?
You can do this, but you will need to use Chrome extensions.
Chrome extensions have a lot of sandbox-style security. Communication between the Chrome extension and the web page is a multi-step process. Here's the most concise explanation I can offer with a full working example at the end:
A Chrome extension has full access to the chrome.* APIs, but a Chrome extension cannot communicate directly with the web page JS nor can the web page JS communicate directly with the Chrome extension.
To bridge the gap between the Chrome extension and the web page, you need to use a content script . A content script is essentially JavaScript that is injected at the window scope of the targeted web page. The content script cannot invoke functions nor access variables that are created by the web page JS, but they do share access to the same DOM and therefore events as well.
Because directly accessing variables and invoking functions is not allowed, the only way the web page and the content script can communicate is through firing custom events.
For example, if I wanted to pass a message from the Chrome extension to the page I could do this:
content_script.js
document.getElementById("theButton").addEventListener("click", function() {
window.postMessage({ type: "TO_PAGE", text: "Hello from the extension!" }, "*");
}, false);
web_page.js
window.addEventListener("message", function(event) {
// We only accept messages from ourselves
if (event.source != window)
return;
if (event.data.type && (event.data.type == "TO_PAGE")) {
alert("Received from the content script: " + event.data.text);
}
}, false);
`4. Now that you can send a message from the content script to the web page, you now need the Chrome extension gather up all the network info you want. You can accomplish this through a couple different modules, but the most simple option is the webRequest module (see background.js below).
`5. Use message passing to relay the info on the web requests to the content script and then on to the web page JavaScript.
Visually, you can think of it like this:
Full working example:
The first three files comprise your Google Chrome Extension and the last file is the HTML file you should upload to http:// web space somewhere.
icon.png
Use any 16x16 PNG file.
manifest.json
{
"name": "webRequest Logging",
"description": "Displays the network log on the web page",
"version": "0.1",
"permissions": [
"tabs",
"debugger",
"webRequest",
"http://*/*"
],
"background": {
"scripts": ["background.js"]
},
"browser_action": {
"default_icon": "icon.png",
"default_title": "webRequest Logging"
},
"content_scripts": [
{
"matches": ["http://*/*"],
"js": ["content_script.js"]
}
],
"manifest_version": 2
}
background.js
var aNetworkLog = [];
chrome.webRequest.onCompleted.addListener(function(oCompleted) {
var sCompleted = JSON.stringify(oCompleted);
aNetworkLog.push(sCompleted);
}
,{urls: ["http://*/*"]}
);
chrome.extension.onConnect.addListener(function (port) {
port.onMessage.addListener(function (message) {
if (message.action == "getNetworkLog") {
port.postMessage(aNetworkLog);
}
});
});
content_script.js
var port = chrome.extension.connect({name:'test'});
document.getElementById("theButton").addEventListener("click", function() {
port.postMessage({action:"getNetworkLog"});
}, false);
port.onMessage.addListener(function(msg) {
document.getElementById('outputDiv').innerHTML = JSON.stringify(msg);
});
And use the following for the web page (named whatever you want):
<!doctype html>
<html>
<head>
<title>webRequest Log</title>
</head>
<body>
<input type="button" value="Retrieve webRequest Log" id="theButton">
<div id="outputDiv"></div>
</head>
</html>
Big shoutout to #Elliot B.
I essentially used what he did but I wanted events to trigger in the content script rather than listeners triggering in the background. For whatever reason, I was unable to connect to the port from the background script so this is what I came up with.
PS: you need jquery.js in the extension folder to make this work.
manifest.json
{
"manifest_version": 2,
"name": "MNC",
"version": "0.0.1",
"description": "Monitor Network Comms",
"permissions":["webRequest","*://*/"],
"content_scripts": [{
"matches": ["<all_urls>"],
"run_at": "document_start",
"js": ["content.js",
"jquery.js"]
}],
"background": {
"scripts": ["background.js"]
}
}
background.js
var aNetworkLog = [];
chrome.webRequest.onResponseStarted.addListener(
function(oCompleted) {
var sCompleted = JSON.stringify(oCompleted);
aNetworkLog.push(sCompleted);
},{urls: ["https://*/*"]}
);
chrome.extension.onConnect.addListener(function (port) {
chrome.webRequest.onResponseStarted.addListener(
function(){
port.postMessage({networkLog:JSON.stringify(aNetworkLog)});
},{urls: ["https://*/*"]}
);
port.onMessage.addListener(function (message) {
if (message.disconnect==true) {
port.disconnect();
}
});
});
content.js
div = $('<div id="outputDiv" style="float:left;max-width:fit-content;position:fixed;display:none;"></div>').appendTo(document.body);
var port = chrome.extension.connect({name:'networkLogging'});
port.onMessage.addListener(function (message) {
if (message.networkLog) {
div[0].innerHTML = message.networkLog;
}
});
observer = new WebKitMutationObserver(function(mutation,observer){
JSON.parse(mutation[0]['target'].innerHTML).forEach(function(item){
JSON.parse(item);
})
});
observer.observe(div[0],{childList:true});
This is definitely not the most efficient way of doing things but it works for me. Thought that I would add it in here just in case someone is needing it.

Categories