I am a very novice when it comes to writing code, this is my first attempt. I have done a fair amount of research and learning, but there is so much to learn. So i'm looking for some help or advice. At work we have to click a button to get us a work order. I am trying to automate the process, I have some code written that clicks the button for me. Unfortunately when it returns with "No Matches" it automatically reloads the page and my code is gone in the DOM. Is there a way to automatically inject my code every time the webpage reloads?
My Current Code:
var button = document.getElementById('GetMeAWorkOrder')
setInterval
button.click()
,1500
You can look into creating your own Chrome extension that injects the code through a content script. To make it easier, I've included the starter files for your specific project. Simply entering your specific URL into the marked lines should be enough for the code you posted.
Manifest.json
{
"manifest_version": 2,
"name": "My Extension",
"description": "Injects custom code to [website]",
"version": "1",
"permissions": [
"*://URL HERE*"
],
"content_scripts": [
{
"matches": [
"*://URL HERE*"
],
"js": ["content.js"]
}
]
}
content.js
//code you want to inject into the website
var button = document.getElementById('GetMeAWorkOrder');
setInterval( function() { button.click() },1500 );
Remember to replace the URLS with the website you want to inject the code into; here's a reference on match patterns.
Good luck and have fun coding! :)
Related
I'm making a WebExtension for Chrome and Firefox that adds more information to GitHub. It's supposed to be faster than existing extensions.
I have my manifest set up like Mozilla's documentation recommends.
{
"manifest_version": 2,
"name": "GitHub Extended",
"version": "0.0.1",
"description": "Adds information to GitHub normally accessible only by the API.",
"permissions": [
"https://github.com/*"
],
"content_scripts": [
{
"all_frames": true,
"run_at": "document_start",
"matches": [
"https://github.com/*"
],
"js": [
"source/github.js",
"source/repository.js"
]
}
]
}
When the page is loaded, the content scripts are injected. The file github.js is a light wrapper around GitHub's API, and repository.js is the code to modfy the DOM of the main repository root page.
The most important code in there is this the preloader, which makes an API request while the page is loading and waits for both events to complete before adding to the DOM.
While this current code works fine in Chrome, in Firefox it simply does nothing. I tried testing it by putting console.log("I'm loaded!"); in repository.js. Nothing is printed. Why is this code not working in Firefox?
function beginPreload() {
console.log("Test from preload scope!");
let urlMatch = window.location.pathname.match(/\/([\w-]+)\/([\w-]+)/);
console.log(urlMatch);
Promise.all([
getSortedReleases(urlMatch[1], urlMatch[2]),
documentReady()
]).then((values) => {
let releaseJson = values[0];
let actionsEl = document.getElementsByClassName("pagehead-actions")[0];
let dlCount = 0;
for (release of releaseJson)
for (asset of release.assets)
dlCount += asset.download_count;
let buttonEl = createDownloadButton(
releaseJson[0].html_url,
window.location.pathname + "/releases",
formatNum(dlCount)
);
actionsEl.appendChild(buttonEl);
});
}
beginPreload();
console.log("Test from global scope!");
This was the solution.
"permissions": [
"https://api.github.com/*"
]
All that needed to happen was add permission for the extension to use GitHub's API. AFAIK, this is only required for content scripts using XHR.
You need to go step by step and first ask yourself if script is really injected in the FF github page: remove everything thing from your contentScript, reload extension and check your FF console. If you see the log then start adding code progressively until it breaks, if not you have a problem with your build content.
I'm developing a Chrome extension for text parsing of Google search results. I want the user to insert a certain text in the omnibox, and then be direct to a Google search page.
function navigate(url) {
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.update(tabs[0].id, {url: url});
});
}
chrome.omnibox.onInputEntered.addListener(function(text) {
navigate("https://www.google.com.br/search?hl=pt-BR&lr=lang_pt&q=" + text + "%20%2B%20cnpj");
});
alert('Here is where the text will be extracted');
After directing the current tab to the search page, I want to get the plain text form of the page, to parse it afterwards. What is the most straightforward way to accomplish this?
Well, parsing the webpage is probably going to be easier to do as a DOM instead of plain text. However, that is not what your question asked.
Your code has issues with how you are navigating to the page and dealing with the asynchronous nature of web navigation. This is also not what your question asked, but impacts how what you did ask about, getting text from a webpage, is implemented.
As such, to answer your question of how to extract the plain text from a webpage, I implemented doing so upon the user clicking a browser_action button. This separates answering how this can be done from the other issues in your code.
As wOxxOm mentioned in a comment, to have access to the DOM of a webpage, you have to use a content script. As he did, I suggest you read the Overview of Chrome extensions. You can inject a content script using chrome.tabs.executeScript. Normally, you would inject a script contained in a separate file using the file property of the details parameter. For code that is just the simple task of sending back the text of the webpage (without parsing, etc), it is reasonable to just insert the single line of code that is required for the most basic way of doing so. To insert a short segment of code, you can do so using the code property of the details parameter. In this case, given that you have said nothing about your requirements for the text, document.body.innerText is the text returned.
To send the text back to the background script, chrome.runtime.sendMessage() is used.
To receive the text in the background script, a listener, receiveText, is added to chrome.runtime.onMessage.
background.js:
chrome.browserAction.onClicked.addListener(function(tab) {
console.log('Injecting content script(s)');
//On Firefox document.body.textContent is probably more appropriate
chrome.tabs.executeScript(tab.id,{
code: 'document.body.innerText;'
//If you had something somewhat more complex you can use an IIFE:
//code: '(function (){return document.body.innerText})();'
//If your code was complex, you should store it in a
// separate .js file, which you inject with the file: property.
},receiveText);
});
//tabs.executeScript() returns the results of the executed script
// in an array of results, one entry per frame in which the script
// was injected.
function receiveText(resultsArray){
console.log(resultsArray[0]);
}
manifest.json:
{
"description": "Gets the text of a webpage and logs it to the console",
"manifest_version": 2,
"name": "Get Webpage Text",
"version": "0.1",
"permissions": [
"activeTab"
],
"background": {
"scripts": [
"background.js"
]
},
"browser_action": {
"default_icon": {
"32": "myIcon.png"
},
"default_title": "Get Webpage Text",
"browser_style": true
}
}
I am writing a chrome extension which detects the type of file being opened and based on that injects a script on the page which does many other things. Here is the part of my code for the background.js which is injecting the script:
chrome.webRequest.onHeadersReceived.addListener(function(details){
console.log("Here: " + details.url + " Tab ID: " + details.tabId);
if(toInject(details))
{
console.log("PDF Detected: " + details.url);
if(some-condition)
{
//some code
}
else
{
chrome.tabs.executeScript(details.tabId, { file: "contentscript.js", runAt: "document_start"}, function(result){
if(chrome.runtime.lastError)
{
console.log(chrome.runtime.lastError.message + " Tab ID: " + details.tabId);
}
});
}
return {
responseHeaders: [{
name: 'X-Content-Type-Options',
value: 'nosniff'
},
{
name: 'X-Frame-Options',
/*
Deny rendering of the obtained data.
Cant use {cancel:true} as we still need the frame to be accessible.
*/
value: 'deny'
}]
};
}
}, {
urls: ['*://*/*'],
types: ['main_frame', 'sub_frame']
}, ['blocking', 'responseHeaders']);
Here is the manifest file:
{
"manifest_version": 2,
"name": "ABCD",
"description": "ABCD",
"version": "1.2",
"icons": {
"16" : "images/16.png",
"32" : "images/32.png",
"48" : "images/48.png",
"128" : "images/128.png"
},
"background": {
"scripts": ["chrome.tabs.executeScriptInFrame.js", "background.js"],
"persistent": true
},
"permissions": [
"webRequest",
"<all_urls>",
"webRequestBlocking",
"tabs",
"nativeMessaging"
],
"web_accessible_resources": [ "getFrameId", "aux.html", "chrome-extension:/*", "images/*.png", "images/*.gif", "style.css"]
}
The problem is that when injecting script the last error part runs and it shows the tab was closed and the script is not injected. If I press enter on the omnibox a several times the script is injected and things work fine. Here is a sample run of events:
Sorry for my naive photo editing :P
There are a few more things we can deduce from this image:
The first thing being loaded in the tab with tab id 86 is something related to my google account. I have logged out and also turned off the prerender feature of chrome.
On pressing enter several times the tab was closed error goes but the script which maintains a chrome.runtime connection with the background.js gets disconnected.
And then finally things work fine.
I have been banging my head around this for days. No other question on SO addresses this problem. Nor anywhere else on the internet as well.
EDIT:
One more thing to note: The sample run shown in the image above is one such. There are many different behaviors. Sometimes 3 enters wouldn't make it work. Sometimes just one will. Is there something wrong because of the custom headers i am sending?
UPDATE #1
One must notice the headers I am returning in OnHeadersReceived. It's being done to stop chrome from rendering the document. But on doing that all the data of the file is dumped on the screen and I don't want that to appear. So i think I need document_start so that I can hide the dumped data before my content script does other things like putting a custom UI on the page.
UPDATE #2
Noticed one more thing. If I open a new tab, and then paste a url there and then press enter the following is the output of the background page on the console.
So I guess, the location of the window is updated at a later time by chrome. Am I right? Any workarounds?
"The tab was closed" error message is a bit misleading, because the tab obviously is not closed. In chrome sources the variable with the string is called kRendererDestroyed. So the error is because the corresponding renderer is being destroyed for some reason.
I was getting the error if the the page opened in tab redirected (thus one renderer destroyed, another one created for the same tab, but different url this time), in this case extension will got tab updates with statuses like:
loading url: 'example.com', here tab is already returned to callbacks etc, but will get the error, if tried to inject script
loading url: 'example.com/other_url'
title: 'some title'
complete
I managed to get around by injecting script only after receiving status: 'complete' (but probably injecting on title should also do)
Did not try with pdfs, but chrome probably will replace renderer for those too like with a redirect. So look more into page statuses and redirects/renderer replaces. Hope this helps anyone stumbling upon this question.
A simple setTimeout call to wait for the page to load worked for me.
I am having problems figuring out what Is wrong with my code so far because when I click the icon, it says that I run both background.js as well as autofill.js. But does not autofill the gmail site. This is my first chrome extension as well as working with javascript. my ultimate goal is that it can autofill all sites (not just gmail) and be able to store/read all the passwords on a .txt file. Another thing is that when i try to run this code is says that something is wrong with my autofill.js file and gives me the error "Uncaught TypeError: Cannot set property 'value' of null. This is for autofill.js right under the comment //fills in your username and password.
Thanks for taking your time to help me out and anything input would help me because I am stuck and hit a wall
manifest.json:
{
"name": "Test",
"manifest_version": 2,
"version": "1.0",
"description": "This is a Chrome extension that will autofill passwords",
"browser_action": {
"default_icon": "icon.png",
"default_popup":"popup.html",
"default_title": "PasswordFill"
},
//*********************************************************************
//declaring the permissions that will be used in this extension
"permissions": ["*://*.google.com/", "tabs", "activeTab", "*://*.yahoo.com/"],
"background": {
"scripts": ["background.js", "autofill.js"]
},
//*********************************************************************
/* Content scripts are JavaScript files that run in the context of web pages. By using the standard Document Object Model (DOM), they can read details of the web pages the browser visits, or make changes to them */
"content_scripts": [
{
//Specifies which pages this content script will be injected into
"matches": ["https://accounts.google.com/ServiceLoginAuth"],
//The list of JavaScript files to be injected into matching pages
"js": ["autofill.js"], //was background.js
//Controls when the files at "js" are being injected
"run_at": "document_end",
"all_frames": true
}
]
}
background.js:
console.log("Background.js Started .. "); //for debug purpose so i can see it in the console log
chrome.browserAction.onClicked.addListener(function (tab) { //Starts when User Clicks on ICON
chrome.tabs.executeScript(tab.id, {file: 'autofill.js'});
console.log("Script Executed .. "); // Notification on Completion
});
autofill.js:
console.log("Autofill.js Started .. "); //for debug purpose so i can see it in the console log
//define username and password
var myUsername = 'McAfee.sdp';
var myPassword = 'baskin310';
//finds the fields in your login form
var loginField = document.getElementById('Email');
var passwordField = document.getElementById('Passwd');
//fills in your username and password
loginField.value = myUsername;
passwordField.value = myPassword;
//automatically submits the login form
var loginForm = document.getElementById ('signIn');
loginForm.submit();
You should take some time and read the development guides first. Make sure you understand how debugging extensions works.
Also, as a general rule, if your script crashes at some line of code, execution will stop and the extension will most likely fail at whatever you wanted it to do (depends on where the crash happens) - just like in any other application.
"Uncaught TypeError: Cannot set property 'value' of null.
That error tells you that you're trying to "access" (set) the property of a "missing" (null) object. loginField.value = myUsername; tries to access value of loginField so you can easily deduce that loginField is null which, in turn, means that var loginField = document.getElementById('Email'); didn't really work. But don't take my word for it, learn to debug it yourself.
Why it fails is a different story: extensions are "sandboxed" and can't run around changing page content whenever they feel like - you have "content scripts" for that. Go back to the docs and read the overview and content scripts sections.
In your specific case:
the only background script file should be background.js; remove autofill.js
make use of event pages instead of background ones whenever possible
autofill.js is a content script and you have it added to the manifest. no need to use programatic injection using chrome.tabs.executeScript
learn how to communicate between backgroud/toolbar and content scripts - you'll need it
your extension needs permission to access `chrome.tabs.* so add "tabs" to the list of permissions in your extension manifest
After reviewing the suggested responses, I was not able to resolve the following issue:
My javascript is not accessing the page DOM, but it is running.
Manifest.json
{
"name": "Clicky",
"version": "1.0",
"background": { "scripts": ["jquery.js", "clickclickboom.js"] },
"permissions": [
"tabs", "http://*/*"
],
"browser_action": {
"name": "Find all links",
"icons": ["icon.jpg"]
},
"manifest_version": 2
}
clickclickboom.js
alert("script runs");
function clicky() {
alert ("clicky got called");
jQuery(".testClass").find("a");
}
chrome.browserAction.onClicked.addListener(function(tab) {
chrome.tabs.executeScript(
null, {code: clicky()});
});
Both alerts pop up but when I debug, I see the extension accessing the background.html DOM but not the targeted pages' DOM.
Any help would be greatly appreciated, thank you in advance!
In your chrome.tabs.executeScript(), the code property must be a string containing the code.
What's happening currently with your code is: clicky() is executed, which returns undefined, which basically is making your executeScript a no-operation call...
One thing you can do is use clicky.toString() though I don't personally recommend it! In this case, the alert ("clicky got called") may work (if you are lucky). The next line will not work because jQuery is not defined in the content script. Your inclusion of jquery is limited to background page.
From your code, it seems you are not really familiar with chrome extension's architecture, so I suggest you start with http://code.google.com/chrome/extensions/getstarted.html