Access window object from a background Chrome extension - javascript

I want to access the properties of a window object from a background script. I have this in manifest.json:
{
"..": "..",
"permissions": ["http://*.mysite.net/"],
"background": {
"scripts": ["extension.js"]
}
}
and this in extension.js:
chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
if (changeInfo.status === 'complete') {
var tabWindowObject = ??
setInterval(tabWindowObject.someFunction, 10);
}
});
I need it here, not in another place (no content scripts and no script injection). How do I get the tabWindowObject in extension.js? In other words, I want to access the context of a tab inside a background script Chrome extension.

You can't. The extension's background page runs in one process, while the tab that was updated runs in a separate process. Different processes can't share objects, so you can't directly access the window object of a tab from an extension's background page. You have to use a content script to get extension code to run inside the tab's process.

Related

Chrome Extension tab and contextmenu trouble

I am trying to sendMessage from my contextmenu.js file to my content.js file anytime the user right clicks on any website on chrome.
This will include sites
- that are on the current tab and is active
- that are popups and is inactive
- that are popups and is active
- on another window and is inactive
- on another window and is active
My code looks like this:
//contextmenu.js
chrome.contextMenus.onClicked.addListener((clickData, tab) => {
chrome.tabs.sendMessage(tab.id, {text: 'rightClicked'}, (response) => {
console.log(response)
})
})
//content.js
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
if (msg.text === 'rightClicked') {
sendResponse('performing operation')
}
})
I'm getting the error message:
"Unchecked runtime.lastError: Could not establish connection. Receiving end does not exist."
Assuming contextmenu.js is declared in "background" section and content.js in "content_scripts" section of manifest.json, the posted code is fine, but extensions have many moving parts so the problem is elsewhere. The error message means there was no content script running at the time the message was sent, which could happen in these cases:
the page was still loading - to fix this add "run_at": "document_start" in manifest.json's content_scripts section, more info.
the extension was recently installed or updated or reloaded but the tab wasn't reloaded, see content script re-injection after upgrade or install or switch to programmatic injection instead of declaring content_scripts section in manifest.json, more info.
you clicked inside an iframe but you didn't allow the content script to run inside iframes - add "all_frames": true in manifest.json's content_scripts section, more info and specify frameId in sendMessage like this:
chrome.contextMenus.onClicked.addListener((clickData, tab) => {
const {frameId} = clickData;
chrome.tabs.sendMessage(tab.id, {text: 'rightClicked'}, {frameId}, response => {
console.log(response)
});
});
the page can't run content scripts at all (e.g. a chrome:// page or another extension) - can't be fixed in general but for a personal use you can start Chrome with --extensions-on-chrome-urls command line switch and add chrome://*/* pattern to the content_scripts section's matches list.
the page URL is blacklisted - check chrome://policy for the presence of runtime_blocked_hosts and contact your admin
I figured it out.
There is nothing wrong with my code. I was just not matching the right URLs, because I was testing the onClick on a blank page which has no URL or chrome://extension itself. The code works on a any website.
//manifest.json
"content_scripts": [
{
"matches": ["<all_urls>"]
...
}

Chrome extension - page action: defining pages

I'm trying to build a somehow dummy Chrome extension. I want it to run only in specific pages, so I'm using a Page Action.
Let's say I want the page action to run on the Instagram website, then (accordingly the docs), I would need something like this on my manifest.json right?
{
"manifest_version": 2,
"name": "Some name",
"version": "0.0.3",
"description": "Some description",
"content_scripts": [
{
"matches": [
"https://www.instagram.com/*"
],
"js": ["content.js"]
}
],
"page_action": {
"default_icon": "icon.png"
},
"background": {
"scripts": ["background.js"]
}
}
while the content script runs only on instagram pages as one would expect, the browser extension is not clickable (gray look, and when I click most options are not clickable).
this makes impossible to act upon extension button click. In my background.js I have:
function click(tab) {
console.log('click from ' + tab);
}
chrome.pageAction.onClicked.addListener(click);
that never gets called.
So, what's wrong that makes impossible to act upon extension click on some pages?
Note: I saw this question/answer, but couldn't find the problem/solution How can I add a click for pageAction?
You have to call pageAction.show in order for your pageAction button to be enabled (clickable).
The pageAction documentation says (emphasis mine):
You make a page action appear and be grayed out using the pageAction.show and pageAction.hide methods, respectively. By default, a page action appears grayed out. When you show it, you specify the tab in which the icon should appear. The icon remains visible until the tab is closed or starts displaying a different URL (because the user clicks a link, for example).
With a manifest.json content_scripts entry
Because you already have a content script that runs on the page you desire to have this function on, probably the easiest way to do this is to have your content script send a message to your background script telling it to show the page-action button for that tab.
Your content script could look something like:
chrome.runtime.sendMessage({type: showPageAction});
Your background script could look something like:
chrome.runtime.onMessage(function(message, sender, sendResponse) {
if(typeof message === 'object' && message.type === 'showPageAction') {
chrome.pageAction.show(sender.tab.id);
}
});
Without a manifest.json content_scripts entry
If you did not have a content script, you would probably want to use a webNavigation.onCompleted listener, or tabs.onUpdated listener, to listen for a change in the tab's URL in order to determine that the page-action button should be shown. Obviously, the trigger for calling pageAction.show() does not have to be the URL which is currently displayed in the tab, but that is the most common.

Unable to receive message in popup once popup closes on tab change

I've made a chrome extension that downloads some stuff from a certain site. Basically, it goes around through all links I'm interested in, stores them in an array and then downloads it one by one. The thing is, the storing is done in a separate file called download.js. Then, I proceed to send a message to popup.js using chrome.extension.sendRequest. I pick it up in popup.js with chrome.extension.onRequest.addListener. It works perfectly when I don't switch my tab, but I'd like it to work while I'm browsing some other stuff in the meantime. I can see the code reaching the point to send the request to popup.js through a console log, but I can't see what's going on in popup.js because when I switch my tab the popup console immediately closes.
download.js:
// logic behind link gathering and storing that works
...
gatherLinks().then(function() {
// logs is defined, don't worry
chrome.extension.sendRequest(logs);
)};
popup.js:
document.addEventListener('DOMContentLoaded', function() {
var downloadButton = document.getElementById('download');
downloadButton.addEventListener('click', downloadStuff);
});
function downloadStuff() {
chrome.tabs.executeScript({
file: 'jquery-3.1.1.min.js'
});
chrome.tabs.executeScript({
file: 'download.js'
});
chrome.extension.onRequest.addListener(function(message_logs) {
message_logs.forEach(function(log) {
chrome.downloads.download({
url: log.link,
filename: log.filename
});
}
}
manifest.json:
{
...
"permissions": [
"tabs",
"activeTab",
"downloads"
],
"background": {
"scripts": ["download.js"]
}
}
When you switch away, the popup window closes.
It does not hide the popup, the popup is properly closed, as you would close a tab. Therefore, its code is no longer executing and there's nothing to listen for your messages.
This is a job for background (or better, event) pages. They exist, invisibly, independent of what you're doing with the browser. Therefore, such a page should be the one to receive commands when the popup may not exist.
Also,
BIG SCARY WARNING!
If your content script and your background script are the same, there is a 99% probability you're doing something wrong. Do not try to reuse code in both, unless it's some auxilliary library - main logic should never be the same in those very different contexts.

Communication Between JS/HTML in Chrome Extensions

I'm trying to make my first Chrome Extension without any prior JS knowledge, and I have some trouble doing it.
What does the extension do?
It's a page action extension for generating a string and copying it to the clipboard. The string includes certain element attributes from the DOM.
Scope
It's only applicable on two pages (the domains below are examples):
https://xxx.abc.com/CFM/Messages/CFMEWFA/*
https://xxx.abc.com/CFM/Messages/FraudPrevention/*
Elements of the extension
The extension has a popup.html with three clickable options to be chosen at the user's discretion:
No response
Invalid
Valid
The string is formatted based on the user's choice from the popup, and whether the tab URL contains "CFMEWFA" or "FraudPrevention".
popup.html
<!doctype html>
<html>
<body>
<script src="popup.js"></script>
<ul id="MENU">
<li id="MENUnoResponse">No reponse
</li>
<li id="MENUinValid">Invalid
</li>
<li id="MENUvalid">Valid
</li>
</ul>
</body>
</html>
popup.js is supposed to listen for clicks in popup.html, employ a multi item clickhandler, then message background.js at the event of a click. The message should include an argument corresponding to the li id in popup.html.
popup.js
var theParentMenu = document.querySelector("#MENU");
theParentMenu.addEventListener("click", userHasClicked, false);
function userHasClicked(e) {
if (e.target !== e.currentTarget) {
var clickedItem = e.target.id;
chrome.runtime.sendMessage({
directive: e.target.id
}, function(response) {
this.close();
});
};
e.stopPropagation();
}
background.js is governing where the extension icon is shown. It also listens for messages from popup.js (containing an argument determined by the user's choice from popup.html) before executing content.js, a script which runs in the tab.url fetching attributes from the DOM and generating the string. I have yet to start building content.js because of unresolved issues earlier in other files.
background.js
//Displays the page action extension only on specific pages
function checkForValidUrl(tabId, changeInfo, tab) {
if (tab.url.indexOf("https://xxx.abc.com/CFM/Messages/FraudPrevention/") == 0)
{
chrome.pageAction.show(tabId);
}
else if (tab.url.indexOf("https://xxx.abc.com/CFM/Messages/CFMEWFA/") == 0)
{
chrome.pageAction.show(tabId);
}
};
chrome.tabs.onUpdated.addListener(checkForValidUrl)
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
switch (request.directive) {
case "MENUnoReponse":
// execute the content script
chrome.tabs.executeScript(null, { // defaults to the current tab
//file: "contentscript.js", // script to inject into page and run in sandbox
//allFrames: true // This injects script into iframes in the page.
});
sendResponse({}); // sending back empty response to sender
case "MENUinValid":
// execute the content script
chrome.tabs.executeScript(null, { // defaults to the current tab
//file: "contentscript.js", // script to inject into page and run in sandbox
//allFrames: true // This injects script into iframes in the page.
});
sendResponse({}); // sending back empty response to sender
case "MENUvalid":
// execute the content script
chrome.tabs.executeScript(null, { // defaults to the current tab
//file: "contentscript.js", // script to inject into page and run in sandbox
//allFrames: true // This injects script into iframes in the page.
});
sendResponse({}); // sending back empty response to sender
break;
default:
// helps debug when request directive doesn't match
alert("Unmatched request of '" + request + "' from script to background.js from " + sender);
}
}
);
manifest.json
{
"manifest_version": 2,
"name": "EW logger",
"description": "This extension creates logs for early warning and fraud prevention cases",
"version": "1.0",
"page_action": {
"default_title": "EW",
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"background": {
"scripts": ["background.js"]
},
"permissions": [
"tabs",
"clipboardWrite",
"https://xxx.abc.com/*"
]
}
What works:
The extension icon appears like it should.
My problems:
The options in popup.html are not working. Popup.js doesn't do anything when I click.
Do you have any suggestion to how I can "listen" for clicks in popup.html properly, and then send a message containing an argument to background.js?
Your script is running before the body is loaded, so the element is not found. You can fix this by moving the script tag to the bottom of the body. Alternatively, use <script src="popup.js" defer></script> to delay execution until the dom is loaded.
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-defer
Also, you should use console.log(message) and the Chrome Devtools console to debug and check for errors.
https://developer.mozilla.org/en-US/docs/Web/API/Console/log
https://developers.google.com/web/tools/chrome-devtools/

How can I manipulate a new window in a Chrome Extension?

I am writing a chrome extension to allow users to login to social media sites from a single page. I am able to create a new incognito window but am unable to manipulate anything inside of the window that I created. I want to create an onload function for the new window to execute jquery. Thanks for getting me pointed in the right direction!
Refer the following demonstration for manipulation of new incognito window created and injecting some jquery into it.
References
Windows API
Tabs API
Background Pages
Content Scripts
Manifest file.
manifest file
This is used to bind permissions and register background pages to extension.Ensure it has all permissions needed.
{
"name":"Hanlder for Incognito window",
"description":"http://stackoverflow.com/questions/14044338",
"version":"1",
"manifest_version":2,
"background":{
"scripts":["background.js"]
},
"permissions":["tabs","http://www.google.co.in/"]
}
background.js
Inject jquery into new incognito window, from background page.
var _tabId_To_Look_For;
// Create a new incognito Window with some arbitary URL and give it focus
chrome.windows.create({
"url": "http://www.google.co.in/",
"focused": true,
"incognito": true
}, function (window) {
// Trace tab id which is created with this query
_tabId_To_Look_For = window.tabs[0].id
});
// Add an event Listener for new tab created
chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
// Inject script into chosen tab after it is loaded completely
if (tabId == _tabId_To_Look_For && changeInfo.status == "complete") {
// Inject Jquery and in current tab
chrome.tabs.executeScript(tabId, {
"file": "jquery.js"
}, function () {
// I am in call back
console.log("Injected some jquery ");
});
}
});
Ensure you have enabled incognito access.
Output
You will observe a new window with jquery injected.

Categories