I basically want to automate a website by using a Chrome extension. But this website has extremely much client-side code, so it's really hard to find out which request I need to make in order to get the needed information.
The easiest way I can think of would be to use a content script to enter text in input elements and click buttons, like this (with jQuery in this case):
$.ready(function(){
$("#input").val("foo");
$("#submit").click();
});
Quite similar to this question: Injecting input into a tab opened by a Chrome Extension, but the interaction with that website should not be visible.
So: Can I open pages in chrome from an extension, that are not visible to the user and use them to interact with websites?
If you want the page to be completely invisible, I believe that the only option is to load it into an iframe on the background page. You can then access the page within the iframe using content scripts, just as you would access any normal visible page.
Since many sites limit embedding using the X-Frame-Options header, you will likely have to use the webRequest API to remove that header before loading the page into an iframe. Some pages also use other techniques to prevent embedding that might further complicate this.
Example code:
manifest.json
{
"manifest_version": 2,
"name": "Hidden page in background",
"description": "Interact with a hidden page in background",
"version": "1.0",
"background": {
"page": "background.html",
"persistent": true
},
"content_scripts": [
{
"matches": ["*://*.google.fr/*"],
"js": ["contentscript.js"],
"all_frames": true
}
],
"permissions": ["*://*.google.fr/*", "webRequest", "webRequestBlocking"]
}
background.html
<!DOCTYPE html>
<html>
<head>
<script src="background.js"></script>
</head>
<body>
<iframe id="iframe1" width="1000 px" height="600 px" src="http://www.google.fr"></iframe>
</body>
</html>
background.js
// This is to remove X-Frame-Options header, if present
chrome.webRequest.onHeadersReceived.addListener(
function(info) {
var headers = info.responseHeaders;
var index = headers.findIndex(x=>x.name.toLowerCase() == "x-frame-options");
if (index !=-1) {
headers.splice(index, 1);
}
return {responseHeaders: headers};
},
{
urls: ['*://*.google.fr/*'], //
types: ['sub_frame']
},
['blocking', 'responseHeaders']
);
contentscript.js
var elementToInsert = document.createElement("h1");
elementToInsert.textContent = "This text comes from my content script.";
document.body.insertBefore(elementToInsert, document.body.firstChild);
Couple of notes:
The removal of X-Frame-Options header is not limited to the background page here. It would allow embedding of the relevant page in iframes on any other page as well. Unfortunately, Chrome does not seem to support the ALLOW-FROM uri value that could be used to limit embedding to your extension only.
Content script is being injected to all pages here. You could inject it programmatically only to the iframe on the background page, but that gets a bit more complicated.
I used www.google.fr as an example, because it uses X-Frame-Options, but does not use any other techniques to prevent embedding. I used the French domain because google.com tends to redirect to local country-level domains automatically.
See tabs.create, you could call the following code creating an invisible tab (not active).
chrome.tabs.create({ url: 'https://www.google.com', active: false, });
Related
With Chrome 27, it seems that extensions that override Chrome's New Tab Page can't take focus away from Chrome's Omnibox like they used to in previous versions of Chrome.
Is there a new way to focus an input box in a New Tab Page, or has this functionality been disabled completely? :(
To test this, create an extension folder with three files:
1. manifest.json:
{
"name": "Focus Test",
"version": "0",
"minimum_chrome_version": "27",
"chrome_url_overrides": {
"newtab": "newTab.html"
},
"manifest_version": 2
}
2. focus.js:
document.getElementById('foo').focus();
3. newTab.html:
<html>
<body>
<input id="foo" type="text" />
<script type="text/javascript" src="focus.js"></script>
</body>
</html>
Then, when you load the extension and open a new tab, the input field does not get focused on the new tab page.
I have also tried adding the autofocus attribute to the input field, but no luck either. The extension's new tab page can't take focus away from Chrome's Omnibox.
Any ideas? Is this a bug or a new "feature"?
ManifestV3 update
This answer is adapted from https://stackoverflow.com/a/11348302/1754517.
This has been tested with both Manifest V2 and V3.
Tested in Google Chrome 99.0.4844.51 64-bit (Windows 10).
Replace the content of focus.js with:
if (location.search !== "?x") {
location.search = "?x";
throw new Error; // load everything on the next page;
// stop execution on this page
}
Add the autofocus attribute to the <input>.
Go to the Extensions page in Chrome and click the Load unpacked button. Choose the folder of your extension.
Open your new tab page. You might see a modal dialogue reading Change back to Google?. Click Keep it to keep your custom new tab page.
Inline Javascript - Manifest V2 only
If you're inlining the Javascript in the HTML file, then you'll need to take some extra steps:
After adding your inline Javascript to your HTML file, open DevTools (F12 key) and observe the error output in the Console. Example output you should see:
Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self' blob: filesystem:".
Either the 'unsafe-inline' keyword, a hash ('sha256-MK0Gypb4mkZTI11eCOtWT+mGYcJNpN5zccvhfeaRb6E='), or a nonce ('nonce-...') is required to enable inline execution.
Select & copy this hash.
Add a line to manifest.json to allow the JS to run, pasting in the hash you just copied between the single-quotes. E.g.:
"content_security_policy": "script-src 'self' 'sha256-MK0Gypb4mkZTI11eCOtWT+mGYcJNpN5zccvhfeaRb6E='"
Go to the Extensions page again. Remove the extension, then re-add it using the Load unpacked button.
Open your new tab page. Your extension should now autofocus on the <input>.
Note inlining only works with Manifest V2; Manifest V3 returns a failure message when attempting to load the extension (even with a properly formed "content_security_policy" object in manifest.json, to replace the Manifest V2 "content_security_policy" string):
Failed to load extension
File C:\path\to\extension
Error 'content_security_policy.extension_pages': Insecure CSP value "'sha256-...'" in directive 'script-src'.
Could not load manifest.
As per the Chrome Extension Documentation,
Don't rely on the page having the keyboard focus.
The address bar always gets the focus first when the user creates a new tab.
See reference here: Override Pages
Here's the solution for Manifest v3
chrome.tabs.onCreated.addListener((tab) => {
if (tab.pendingUrl === 'chrome://newtab/') {
chrome.tabs.remove(tab.id)
chrome.tabs.create({
url: '/index.html',
})
}
})
I saw a pretty old blog which updates the new tab conditionally. However, simply updating the tab does not steal the focus. I had to close the pending tab and open a new one.
Cons: An ugly chrome-extension://akfdobdepdedlohhjdalbeadhkbelajj/index.html in the URL bar.
I have a cheap work around that allows stealing focus from address bar focus. It's not for everyone. I do actually do use this because I want to control a new tab focus just that bad in my own custom new tab solution:
<script>
alert('Use enter key to cancel this alert and then I will control your focus');
document.getElementById('...AckerAppleIsCrafty...').focus()
</script>
USE CASE: I built my own HTML chrome custom tab that has a search input that custom searches my history and bookmarks the way I like it too.
Cash me focusing outside how bout dat?
I want to ask is there ANY way or extension that can pre-highlight text within the iframe whenever a new window is opened containing iframe? I have tried many extension but none of them works.
I need to filter out content based on certain keywords and the content is within iframe. I can do it with CTRL+F but there are many keywords like 10-15 within each article to be found. So it makes my job very tough and time consuming. Few extensions that I have tried from chrome are multi highlighter, pearls, FF but none of them seems to work.
I also know the reason why these extension can't access content within the iframe i.e. due to cross origin policies.
But I also remember around an year ago I worked with chrome extension named 'Autofill' that could pre-select form elements whenever I opened new chrome window containing iframe.
So is there any work around?
You can set your extension permission to run content scripts in all frames as document at http://developer.chrome.com/extensions/content_scripts.html#registration by setting all_frames to true in the content scripts section of your manifest file. Adding to Google's example from that page, part of your manifest file might look like
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["http://www.google.com/*"],
"css": ["mystyles.css"],
"js": ["jquery.js", "myscript.js"],
"all_frames": true
}
],
...
}
You'll need to be careful since your content scripts are going to be inject into the page once for the parent page and one for each iFrame on the page. Once your content script is injected into all frames on the page you can work your magic with finding and highlighting text.
if (window === top) {
console.log('Running inside the main document', location.href);
} else {
console.log('Running inside the frame document', location.href,
[...document.querySelectorAll('*')]);
}
I am trying to understand chrome background pages. I managed to get background.js script running after cannibalizing on the of examples and it pops up with an alert box every time a user visits a page. However, when I take the same script and move it to a background.html file, I cannot seem to get the file to execute.
I have updated the the manifest file to a page (instead of script) and the extension loads fine. I have also tried playing around with either having the javascript in a script tag directly in the body or in a function as it is now that is called onload on the body or in the head.
Perhaps I don't understand the concept of what a background.html page is used for in a Chrome extension?
Manifest file:
{
"name": "Testing Extension",
"version": "0.2",
"background": { "pages": ["background.html"] },
"permissions": [
"tabs", "https://mail.google.com/*", "http://*/*, https://*/*"
],
"browser_action": {
"name": "Do some action",
"icons": ["icon.png"]
},
"manifest_version": 2,
"web_accessible_resources": ["injectImage.js", "background.html"]
}
injectImage.js
alert('Got Here');
'use strict';
chrome.browserAction.onClicked.addListener(function (tab) {
chrome.tabs.executeScript(null, {file: "injectImage.js"});
});
chrome.browserAction.setBadgeBackgroundColor({color: [0, 200, 0, 100]});
var i = 0;
window.setInterval(function () {
chrome.browserAction.setBadgeText({text: String(i)});
i++;
}, 10);
background.html
<!DOCTYPE html>
<html>
<head>
<script src="jquery-1.8.0.min.js"></script>
<script src="injectImage.js"></script>
</head>
<body>
</body>
</html>
currently this code doesn't seem to do anything other than put an icon in the top right corner.
Your background is only one page and acts like a local server that can send and receive data between the content_scripts and default_popup or other extension pages.
For anyone starting out, look at each example's manifest version, which is defined in manifest.json. Version 2 has stricter security policies and inline javascript is invalid so copying sample v1 code and pasting it into background.html will not work anymore. All scripts need to be in an external file that can be included in an HTML file using the src attribute like <script src="main.js"></script>
My understanding is that you have four commonly used components for a basic extension:
DOM of the tab being viewed which can only be accessed by content_scripts
content_scripts is an array of javascript files identified in manifest.json that are sequentially injected directly into the tab's DOM. Apparently the JS variables and functions from the tab and your content_scripts cannot interact.
One background HTML page which Chrome either generates by concatenating an array of scripts OR is defined as one page, commonly named background.html.
{"browser_action" : {"default_popup" : "popup.html"}} is an optional user interface defined in manifest.json that is displayed when someone clicks your extension icon. The popup has no way to reach the tab's DOM so if the tab's content matters then it must be requested using Chrome messages like chrome.extension.sendMessage
Your background is defined either as one or more scripts:
"background": {
"scripts": ["background.js"]
},
Or as a page:
"background": {
"page": "background.html"
},
If you want to find out what is happening inside of the background then you can use console.log but in order to inspect the output you have to launch the inspector from the Extensions page. You can even see that the background file is always named "_generated_background_page.html".
You have a typo in your manifest file. Including a background page should look like this:
"background": {"page": "background.html"}
OR you can directly define js instead of defining a background page and referencing a script file in it (Inline scripts are not allowed as per Content Security Policy (CSP))
"background": {
"scripts": ["background.js"]
},
For version 3
background pages are replaced by service_workers in MV3
Replace background.page or background.scripts with background.service_worker in manifest.json.
{
"manifest_version": 3,
"name": "",
"background": {
"service_worker": "background.js"
}
}
One more gotcha not mentioned here. Doing <script src="myscript"/> doesn't work :P, you have to type it out completely with <script src="myscript"></script>. Took me a bit to figure that out.
I'm trying to work on a Chrome browser extension which does fun things on a contextmenu. The problem is, I can't get the JS files to load within the manifest.json content_scripts.
I don't receive an error, the files simply do not load.
The scripts are good, if I put them in my background page, they fire fine through that background page. The downside of that is it's restricted to only the background page.
{
"name": "Mini",
"description": "Mini",
"permissions": ["contextMenus","management","contentSettings","tabs","http://*/*","https://*/*","editable"],
"version": "0.1",
"manifest_version": 1,
"background_page": "mini.html",
"icons" : {"16" : "mini.png"},
"contexts" : ["link","tab","page"],
"content_scripts": [{"matches":["<all_urls>"],"js":["jquery172min.js","mini.js"]}]
}
I've tried all forms of matches, including "http://\*/\*", "https://\*/\*", "\*://\*/\*"
I've tried whitespaces, no whitespaces. I'm drawing a blank here.
Any help would be appreciated.
$(document).ready(function () {
alert("page is ready");
},true);
My apologies for that. This is a copy of the javascript/Jquery I'm using to test whether the extension has loaded or not. It's just a simple alert.
Content scripts cannot use the chrome.contextMenus API (in fact, these can only use some of the chrome.extension methods).
It's possible to apply match patterns to individual menu items, via chrome.contextMenus.create({title: 'Test', documentUrlPatterns: 'http://*/*'});.
Created menu items can also be modified (chrome.contextMenus.update) and removed (chrome.contextMenus.remove).
To communicate between a Content script and the background page, see Message passing.
I was wondering if it was possible to add CSS to pages through a CSS file, similarly to adding JS to pages through a JS file (contentscript). So, I could just define what pages said CSS file would work on and then it would take effect just like a contentscript. I understand that it is possible to add CSS through a JavaScript file, but it would be more convenient to do it this way instead if possible. If it's not possible, then of course I must use contentscript, but I would first like to know before I rule out any possibilities. I've searched and received no answers - either I'm searching wrong or it really isn't possible. Chrome extensions have come a long way, though, so I'm still not ruling it out until someone who knows can tell me yes & how or no. Thanks for any responses.
Your manifest file must contain a content script pointing to a CSS file, that will load when a url matches your selected url fields...
"content_scripts": [ {
"css": [ "scrollbars.css" ],
"matches": [ "http://www.google.com/*" ]
} ],
This is the same method you would use to inject javascript code, except you would point to a js file instead...
More information here...
manifest.json:
"web_accessible_resources": [
"css/style.css"
],
content-script.js:
var a = chrome.extension.getURL("css/style.css");
$('<link rel="stylesheet" type="text/css" href="' + a + '" >').appendTo("head");
My extension using jQuery
similarly like we add JavaScript in content_scripts also add CSS
Method-1
"content_scripts": [
{
"matches": ["<all_urls>"],
"css": ["css/style.css"],
"js": ["jquery-1.10.2.min.js","content.js","my.min.js"]
}],
add one more thing on web_accessible_resources
"web_accessible_resources": [
"css/style.css"
]
if you used any external JavaScript then also add URL in permission tab.
"permissions": ["tabs", <all_urls>","http://*/","http://example.com/"]
Method-2
also same this thing using vai programming injection and insertCSS().
chrome.tabs.insertCSS(integer tabId, object details, function callback)
NOTE : web_accessible_resources add .css is not mandatory but as of chrome extension forum is good practice.