How do you detect changes in url with a chrome extension? - javascript

I am learning to make chrome extensions. I ran into a problem where context scripts that I want to run, even just alert("test");, are unable to when onload is not activated. This also occurs when you press the back arrow to visit the last visited page. I notice that the url changed, but nothing activates. How do I detect this? If the answer is with service workers, a detailed explanation would be greatly appreciated.

maifest version 2.0
Try using chrome.tabs.onUpdated.addListener((id, change, tab)=>{}). This should run every time the URL changes! Here is a minimalistic example of some code that injects js to a site when the URL changes.
background.js:
// inject code on change
chrome.tabs.onUpdated.addListener((id, change, tab) => {
// inject js file called 'inject.js'
chrome.tabs.executeScript(id, {
file: 'inject.js'
});
});
mainfest version 3.0
You can do it by using chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {}). However, this will actually trigger multiple times when a page URL is changed So you need to add a check for the URL in the changeInfo variable, so it only triggers once!
manifest.json:
{
"name": "URL change detector",
"description": "detect a URL change in a tab, and inject a script to the page!",
"version": "1.0",
"manifest_version": 3,
"permissions": [
"scripting",
"tabs"
],
"host_permissions": [
"http://*/*",
"https://*/*"
],
"background": {
"service_worker": "background.js"
}
}
background.js:
// function that injects code to a specific tab
function injectScript(tabId) {
chrome.scripting.executeScript(
{
target: {tabId: tabId},
files: ['inject.js'],
}
);
}
// adds a listener to tab change
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
// check for a URL in the changeInfo parameter (url is only added when it is changed)
if (changeInfo.url) {
// calls the inject function
injectScript(tabId);
}
});
inject.js:
// you can write the code here that you want to inject
alert('Hello world!');

Related

chrome extension call specific function

I have a popup.js:
function registerButtonAction(tabId, button, action) {
// clicking button will send a message to
// content script in the same tab as the popup
button.addEventListener('click', () => chrome.tabs.sendMessage(tabId, { [action]: true }));
}
function setupButtons(tabId) {
// add click actions to each 3 buttons
registerButtonAction(tabId, document.getElementById('start-btn'), 'startSearch');
registerButtonAction(tabId, document.getElementById('deals-btn'), 'startDeals');
}
function injectStartSearchScript() {
chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
// Injects JavaScript code into a page
// chrome.tabs.executeScript(tabs[0].id, { file: 'main.js' });
// dd click handlers for buttons
setupButtons(tabs[0].id);
});
}
injectStartSearchScript()
// document.getElementById('inject-btn').addEventListener('click', injectStartSearchScript);
Which I use to inject my script into the page with the "start-btn" inside my popup.html.
This is my main.js which includes my functions I would like to call on a page:
function pong() {
// do something
}
function ping() {
// do something else
}
my manifest.json:
{
"manifest_version": 2,
"name": "test app",
"description": "test desc",
"version": "1.0",
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"permissions": ["tabs", "<all_urls>"],
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["main.js"]
}
]
}
So basically my setup is that I have a popup.html which includes 3 buttons and they should call one of the functions inside my main.js dpending on what button i press.
But I can not get that working. Currently I only can make at least one function call if I directly call pong() inside main.js on load. But I would need to call one of the functions after I click on a button inside my popup.html.
EDIT: I have updated the code as far as I understood. I am very sorry but I don't understand what would be needed to be changed else to fulfill your proposal. I mean how to write it to be more correct.
EDIT 2: I have removed the line chrome.tabs.executeScript(tabs[0].id, { file: 'main.js' }); as well as document.getElementById('inject-btn').addEventListener('click', injectStartSearchScript)and added injectStartSearchScript()into the popup.js file. Is that what you meant?
Updated and complete example with explanation (you are almost there!)
manifest
You manifest looks good, no changes needed there.
This configuration says to load the content script in each tab. So before popup even opens each tab will have "main.js" injected into it (exactly once).
popup.js
Popup script looks good, no changes needed there.
The tab lookup is still necessary, since to send a message to a specific tab, must know its id. The popup looks for the currently active tab in current window (same as the popup is in) and sets up the button click actions to send a message to the tab.
main.js
Will need minor changes here
Make sure to register the onMessage listener, as included in the example below.
note the conditions: message.startSearch and message.startDeals -- these must match the messages sent from the popup, i.e. when popup sends a message with content {startDeals: true}, the if condition is startDeals. It is matching by a key in the sent message and if the key does not match any condition, the message is going to be ignored.
function pong() {
// do something
alert('Start!');
}
function ping() {
// do something else
alert('Deals!');
}
// register listener to receive messages
chrome.runtime.onMessage.addListener(message => {
// what to do on each received message:
if (message.startSearch) pong();
else if (message.startDeals) ping();
});
// sanity check: content has loaded in the tab
console.log('content loaded');
One more note as it relates to debugging extensions (and perhaps a source of some of these debugging issues) when the content script has been configured to be injected in the manifest, Chrome will inject main.js into tabs, in a separate extension context. If, after this injection, the developer reloads the extension (circular arrow in chrome://extensions or other tool), this will invalidate the context of the content script in the tab. The tab has to be reloaded to reactivate the content script. This is not an issue with a real user, but it does happen during debugging, so double check this is not the cause of issues while debugging.

Chrome Blocking Redirect to Extension Page

I am trying to make a chrome extension that blocks users from accessing sites by redirecting them to the extension's custom HTML block page. The user can then choose to click "Unblock" to exclude the current tab from being checked by the filter.
The extension works as expected. For example, if you try to access https://www.youtube.com/ while "youtube.com" is in the blocked list, it will redirect you to "blocked.html".
However, it seems that the extension only works on the CURRENT TAB that you are working with. If you try to shift click a hyperlink (Which opens the link in a new tab) which leads to https://www.youtube.com, it will redirect to "blocked.html", but Chrome would block the redirect and give you this screen:
Even if you now focus on the tab and press refresh, "blocked.html" still does not load.
I believe this may be because I am missing permissions in my manifest file, however, I looked at the docs for the permissions page and I could not find any relevant permissions I could add.
Thanks in advance.
Note: Interestingly, the yellow error message shown above only appears on pages that have been blocked by chrome. The message is this: "crbug/1173575, non-JS module files deprecated."
Also, if you try to refresh the page, the line number that the message appears becomes higher. (I refreshed a few times and right now it is at VM712:7146). Not sure if this message is related to the error.
manifest.json
"manifest_version": 2,
"background": {
"service_worker": "background.js"
},
"options_page": "options.html",
"permissions": [
"storage",
"activeTab",
"tabs",
"webRequest",
"webRequestBlocking",
"<all_urls>"
],
"page_action": {
"default_popup": "popup.html"
}
blocked.js (Shortened)
// Unblock button redirect
let unblockButton = document.getElementById("unblockButton");
updateOriginalUrl();
chrome.runtime.onMessage.addListener(function update(message) {
updateOriginalUrl();
chrome.runtime.onMessage.removeListener(update);
})
function updateOriginalUrl() {
chrome.storage.sync.get("originalUrl", (result) => {
console.log("Unblock button URL set to: " + result.originalUrl);
unblockButton.addEventListener("click", () => {
location.href = result.originalUrl;
chrome.runtime.sendMessage("exclude")
})
});
}
background.js
chrome.webRequest.onBeforeRequest.addListener((details) => {
console.log("New request detected")
console.log("Request URL: " + details.url);
if(enabled && !excludedTabs.includes(details.tabId)) {
for(let blockedUrl of blockedList) {
if(details.url.includes(blockedUrl)) {
console.log("Match detected, redirecting");
chrome.storage.sync.set( {"originalUrl": details.url}, () => {
chrome.runtime.sendMessage("updateOriginalUrl");
});
return {
redirectUrl: chrome.runtime.getURL("blocked.html")
};
}
}
}
}, {
urls: ["<all_urls>"],
types: ["main_frame"]
}, ["blocking"]);
Thanks #wOxxOm:
Either add blocked.html to web_accessible_resources in manifest.json or switch to using declarativeNetRequest API.
This worked.

Chrome extension tabs onUpdated event

I am building a chrome extension that should get notified every time a new tab has opened and load page, and for that purpose I'am using chrome.tabs.onUpdated event.
The problem is that in case an iframe is inserted on that page/tab that is hosted on some domain(has src), onUpdated event is trigered. Is there a way to differentiate these events for "real" tab load from those triggered for iframe load?
This question helped me with my extension, recognizing the difference between a new page and loading content on the same page, so thought I'd share my solution. First you need to call onUpdated in background.js:
Manifest
{
"name": "My test extension",
"version": "1",
"manifest_version": 2,
"background": {
"scripts":["background.js"]
},
"content_scripts": [
{
"matches": ["http://*/*", "https://*/*"],
"js": ["contentscript.js"]
}
],
"permissions": [
"tabs"
]
}
background.js
chrome.tabs.onUpdated.addListener(
function(tabId, changeInfo, tab) {
// read changeInfo data
if (changeInfo.url) {
// url has changed; do something here
}
}
);
Then you can expand that script to send message data from background.js to your content script (using chrome.runtime.sendMessage):
background.js (con't)
chrome.tabs.onUpdated.addListener(
function(tabId, changeInfo, tab) {
// read changeInfo data
if (changeInfo.url) {
// url has changed; do something here
// like send message to content script
chrome.tabs.sendMessage( tabId, {
message: 'hello!',
url: changeInfo.url
})
}
}
);
And finally listen for that data in your extension's content script to be used however:
contentscript.js
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
// listen for messages sent from background.js
if (request.message === 'hello!') {
console.log(request.url) // new url is now in content scripts!
}
});
You can pass whatever data you'd like from background.js. Hope that helps someone! \ (•◡•) /
tabs.onUpdated triggers when state changes between loading to complete. Presumably, inserting an iframe puts the tab to loading state again.
You could see if details.url is defined in onUpdated listener - if not, you know that the document's URL did not change.
Perhaps you should use webNavigation API instead for your purpose. There, you get a TransitionQualifier that you can use to filter out subframe navigation.

Chrome Extension: Edit the current url on click and then redirect to the edited one

I am a psychology student and I read papers very often. The university libraries provide the access to the databases but I need to use library search engine and log in every time. Quite annoying. I found a way to avoid jumping around the pages.
Here is the method:
I add "ezp.lib.unimelb.edu.au" to the end of the target database address after I found a paper in Google Scholar, then it will redirect to the library login page.
For example, the paper's address is:
http://www.sciencedirect.com/science/article/pii/S0006899315008550
I modified it as:
http://www.sciencedirect.com.ezp.lib.unimelb.edu.au/science/article/pii/S000689315008550
I want to create a Chrome Extension to finish this job on click (too lazy). I tried for hours but it does not work.
Here is what I have done:
I have three files in a folder:
First file: manifest.json
{
"manifest_version": 2,
"name": "Damn! Take me to the library!",
"description": "This extension automatically adds the 'ezp.lib.unimelb.edu.au' to the browser's address, allowing you to visit the databases bought by the library quickly",
"version": "1.0",
"browser_action": {
"default_icon": "unimelb.png",
"default_title": "Damn! Take me to the library!"
},
"background":{
"scripts":["popup.js"]
},
"permissions": [
"activeTab",
"tabs"
]
}
Second file: popup.js
function getCurrentTabUrlthenChangeIt(callback) {
var queryInfo = {
active: true,
currentWindow: true
};
chrome.tabs.query(queryInfo, function(tabs) {
var tab = tabs[0];
var url = tab.url;
callback(url);
var newurl = url.replace('/',"ezp.lib.unimelb.edu.au/");
window.location.replace(newurl);
});
}
Third file: unimelb.png
When I load this folder into Chrome, it does not work.
It's the first time I use JS, anyone has any suggestions?
Thanks!
You can do this even without clicking. You can use the content script for this URL pattern so that your script gets injected to this page. Then you can send a message to the background script using chrome.runtime.sendMessage() and your listener will create a link you want here and then just reload the tab using chrome.tabs.update() with the new URL.
manifest.json
{
"name": "My extension",
...
"content_scripts": [{
"matches": ["http://www.sciencedirect.com/science/article/pii/*"],
"js": ["content-script.js"]
}],
...
}
content-script.js
chrome.runtime.sendMessage({loadURL: true});
background.js
chrome.runtime.onMessage.addListener(function(message, sender, response) {
if (message.loadURL) {
var newurl = sender.tab.url.replace("/", "ezp.lib.unimelb.edu.au/");
chrome.tabs.update(sender.tab.id, {url: newURL})
}
);
This is my first answer to the StackOverflow Community, I hope it helps.
Instead of making an extension, it would be a lot easier to make a bookmarklet which can be used in any browser...
Right click on the bookmark bar
Choose "Add page..."
Under "Name", enter whatever you want "Journal redirect" or whatever
Under "URL", copy and paste the following code (no spaces)
javascript:(function(){location.href=location.href.replace('sciencedirect.com/','sciencedirect.com/ezp.lib.unimelb.edu.au/');})();
Now when you're on the page, click that bookmark and it'll redirect you.
Update: Try this code in the URL for other domains
javascript:(function(){var%20l=location;l.href=l.origin+l.href.replace(l.origin,'ezp.lib.unimelb.edu.au/');})();
manifest.json
{
"manifest_version": 2,
"name": "Damn! Take me to the library!",
"description": "This extension automatically adds the 'ezp.lib.unimelb.edu.au' to the browser's address, allowing you to visit the databases bought by the library quickly",
"version": "1.0",
"browser_action": {
"default_icon": "unimelb.png",
"default_title": "Damn! Take me to the library!"
},
"background":{
"scripts":["background.js"]
},
"permissions": [
"activeTab",
"tabs"
]
}
background.js
//Wait for click
chrome.browserAction.onClicked.addListener(function(tab) {
chrome.tabs.executeScript(null, {
"file": "popup.js"
}, function(){
"popup.js";
console.log("Script Executed ...");
});
})
popup.js
// Change the url to library when on click
var l=location;l.href=l.origin+l.href.replace(l.origin, '.ezp.lib.unimelb.edu.au');
They work well.
It's so cool to finish the first chrome extension. Thank for the help from Mottie.
Anyone looking to edit the url based on some pattern can use the chrome extension Edit Url by Regex
For example for the scenario in this post, while using the extension, you can provide the regex as http.*/science/ and the value as http://www.sciencedirect.com.ezp.lib.unimelb.edu.au/science/
and click submit. The url will get updated as expected.

Which URLs are restricted to operate on with Chrome extensions? [duplicate]

I have created a Chrome extension that, as part of it's operation, opens a new tab with a specified url.
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if( request.message === "open_new_tab" ) {
chrome.tabs.create({"url": request.url});
}
}
);
(Full code available on GitHub)
This works fine on tabs with webpages, but I cannot get it to work on empty tabs, for example: chrome://apps/ To clarify, if I have a tab open and it is on stackoverflow.com, then when I click on my extension button it opens a new tab loading a generated url. When I am on a new tab, or a tab where the url begins with chrome:// then the extension does not work.
What permissions do I need to include to allow the extension to open in ANY tab? Including new tabs and any chrome:// tab?
Manifest.json:
{
"manifest_version": 2,
"name": "MyMiniCity Checker",
"short_name": "MyMiniCity Checker",
"description": "Checks what your city needs most and redirects the browser accordingly.",
"version": "0.2",
"author":"Richard Parnaby-King",
"homepage_url": "https://github.com/richard-parnaby-king/MyMiniCity-Checker/",
"icons": {
"128": "icon-big.png"
},
"options_page": "options/options.html",
"browser_action": {
"default_icon": "icon.png"
},
"permissions": ["tabs","storage","http://*.myminicity.com/","http://*/*", "https://*/*"],
"background": {
"scripts": ["background.js"],
"persistent": false
},
"content_scripts": [ {
"matches": [ "http://*/*", "https://*/*"],
"js": [ "jquery-1.11.3.min.js" ]
}]
}
Background.js:
//When user clicks on button, run script
chrome.browserAction.onClicked.addListener(function(tab) {
chrome.tabs.executeScript(null, { file: "jquery-1.11.3.min.js" }, function() {
chrome.tabs.executeScript(null, { file: "contentscript.js" });
});
});
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if( request.message === "open_new_tab" ) {
chrome.tabs.create({"url": request.url});
}
}
);
It appears as though the background.js file is not being executed. I suspect this to be a permissions. What permissions do I need in order to run this extension in every tab?
Well, this message is supposed to come from a content script you're trying to inject into the current tab.
The widest permission you can request is "<all_urls>", however, there are still URLs that are excluded from access.
You can only normally access http:, https:, file: and ftp: schemes.
file: scheme requires the user to manually approve the access in chrome://extensions/:
Chrome Web Store URLs are specifically blacklisted from access for security reasons. There is no override.
chrome:// URLs (also called WebUI) are excluded for security reasons. There is a manual override in the flags: chrome://flags/#extensions-on-chrome-urls, but you can never expect it to be there.
There is an exception to the above, chrome://favicon/ URLs are accessible if you declare the exact permission.
All in all, even with the widest permissions you cannot be sure you have access. Check for chrome.runtime.lastError in the callback of executeScript and fail gracefully.
As I was wanting this to run on EVERY page it meant I could not have the code in the content script. I moved all the code into the background script:
chrome.browserAction.onClicked.addListener(function(tab) {
//...
chrome.tabs.create({"url": newTabUrl});
//...
});
So when I click on my button the above code is called, using the enclosed jquery script.

Categories

Resources