I learning Chrome Extensions and trying build the following context menu that should open a pop-up with a link to wikipedia when a text is selected. Here is my code
manifest.json
{
"manifest_version": 2,
"name": "Wikit",
"description": "Search Wikipedia on the go!",
"version": "1.0",
"icons" : {
"16":"icon16.png",
"48":"icon48.png",
"128":"icon128.png"
},
"background": {
"scripts": ["eventPage.js"],
"persistent" : false
},
"permissions" : [
"tabs",
"contextMenus"
]
}
eventPage.js
var menuItem = {
"id" : "wikit",
"title" : "Wikit",
"contexts" : [ "selection" ]
}
chrome.contextMenus.create(menuItem);
function fixedEncodeURI(str) {
return encodeURI(str).replace(/%5B/g, '[').replace(/%5D/g, ']');
}
chrome.contextMenus.onClicked.addListener(function(clickData) {
if (clickData.menuItemId == "wikit" && clickData.selectionText) {
var wikiUrl = "https://en.wikipedia.org/wiki/"
+ fixedEncodeURI(clickData.selectionText);
var createData = {
"url" : wikiUrl,
"type" : "popup",
"top" : 5,
"left" : 5,
"width" : screen.availWidth / 2,
"height" : screen.availHeight / 2
};
chrome.windows.create(createData, function() {
});
}
})
When I select a text and click Wikit, it is not opening the new pop-up/tab/page. What am I doing wrong?
Related
I'm making a Chrome extension from redirecting youtube.com/shorts/... to youtube.com/watch?v=...
Everything works fine when I open those shorts links in new tabs or when I type them out but when I click from the homepage itself, they don't get redirected.
Here is my rules.json file:
[
{
"id": 1,
"priority": 1,
"action": { "type": "redirect", "redirect": { "regexSubstitution":"https://youtube.com/watch?v=\\1" } },
"condition": { "regexFilter": "^.*youtube\\.com/shorts/(.*)", "resourceTypes": ["main_frame"] }
}
]
Here is my manifest.json file:
{
"manifest_version": 3,
"name": "No Shorts",
"version": "0.5",
"description": "Play YT shorts as regular videos instead of in a separate player",
"action": {
"default_icon": "images/no-shorts-ico.png"
},
"declarative_net_request": {
"rule_resources": [{
"id": "ruleset_1",
"enabled": true,
"path": "rules.json"
}]
},
"icons":{
"16": "images/16.png",
"48": "images/48.png",
"128": "images/128.png"
},
"permissions":[ "declarativeNetRequest"],
"host_permissions":["*://*.youtube.com/*"]
}
I clicked on a short video from the homepage and it did not get redirected. However, when I refreshed it, it did get redirected. It also got redirected when I clicked open in new tab or typed out the url myself.
If I had to guess, I think it is happening because of something that is similar to client-side navigation but I really can't say for sure. Is there a fix for this?
There is no network request to intercept in such inner navigation as it uses the history API in JS.
You can run a script on the entire youtube domain and intercept the click event:
// page.js:
addEventListener('click', e => {
const thumb = e.target.closest('ytd-thumbnail');
const cmd = thumb?.__data.data.navigationEndpoint.commandMetadata.webCommandMetadata;
if (cmd?.webPageType !== 'WEB_PAGE_TYPE_SHORTS') return;
cmd.webPageType = 'WEB_PAGE_TYPE_WATCH';
cmd.url = cmd.url.replace('/shorts/', '/watch?v=');
for (const a of thumb.querySelectorAll('a'))
a.href = a.href.replace('/shorts/', '/watch?v=');
}, true);
// manifest.json:
"background": { "service_worker": "bg.js" },
"permissions": ["scripting"],
"host_permissions": ["*://www.youtube.com/"],
// bg.js
chrome.runtime.onInstalled.addListener(async () => {
const scripts = [{
id: 'page',
world: 'MAIN',
matches: ['*://www.youtube.com/*'],
runAt: 'document_start',
js: ['page.js'],
}];
await chrome.scripting.unregisterContentScripts({ids: scripts.map(s => s.id)})
.catch(() => {});
await chrome.scripting.registerContentScripts(scripts);
for (const script of scripts) {
const execCfg = {
target: {},
files: script.js,
injectImmediately: true,
world: script.world,
};
for (const tab of await chrome.tabs.query({url: script.matches})) {
execCfg.target.tabId = tab.id;
chrome.scripting.executeScript(execCfg);
}
}
});
I inherited the a extension that is used internally on Chromebooks to pass authentication to a web server for the currently logged in user. The extension itself works fine as is right now with manifest V2 but we are trying prepare for manifest v3 and are having issues getting the extension to fire.
Here is an example of the extension as it is currently setup.
Manifest File
{
"background": {
"scripts": [ "background.min.js" ]
},
"description": "User Agent",
"icons": {
"128": "icon.png",
"16": "icon.png"
},
"manifest_version": 2,
"name": "User Agent",
"permissions": [ "background", "identity", "identity.email", "http://*/", "https://*/" ],
"version": "1.0.3"
}
Background File
(function() {
var e = void 0,
n = new Event("userInfoChanged"),
t = 0,
a = function() {
chrome.identity.getProfileUserInfo(function(a) {
var o = !1;
o = (new Date).getTime() > t + 179e4 || e != a.email, e = a.email, o && (n.username = e, document.dispatchEvent(n))
})
};
document.addEventListener("userInfoChanged", function(e) {
t = (new Date).getTime();
var n = new XMLHttpRequest;
n.open("GET", "URL_TO_WEB_SERVER" + encodeURIComponent(btoa(e.username)), !0), n.send()
}, !1), setInterval(a, 6e4), a(), setInterval(function() {
n.username = e, document.dispatchEvent(n)
}, 18e5)
}).call(this);
From what I have read to update this you need to move from "background page" to using a "service worker"
We need to the authentication that is happening in the current background.min.js file to happen on boot / extension install. I have been reading and it looks as though using the chrome.alarms is the way to go.
I have updated the manifest file
Updated Manifest File
{
"background": {
"service_worker": "background.min.js"
},
"description": "User Agent",
"icons": {
"128": "icon.png",
"16": "icon.png"
},
"manifest_version": 3,
"name": "User Agent",
"permissions": ["alarms", "background", "identity", "identity.email", "http://*/",
"https://*/" ],
"version": "1.2.0"
}
and updated the background.min.js file
Updated Background File
chrome.alarms.onAlarm.addListener(a => {
console.log('Alarm! Alarm!', a);
});
chrome.runtime.onInstalled.addListener(() => {
chrome.alarms.get('alarm', a => {
if (!a) {
chrome.alarms.create('alarm', {periodInMinutes: 1});
}
});
});
(function() {
var e = void 0,
n = new Event("userInfoChanged"),
t = 0,
a = function() {
chrome.identity.getProfileUserInfo(function(a) {
var o = !1;
o = (new Date).getTime() > t + 179e4 || e != a.email, e = a.email, o && (n.username = e, document.dispatchEvent(n))
})
};
document.addEventListener("userInfoChanged", function(e) {
t = (new Date).getTime();
var n = new XMLHttpRequest;
n.open("GET", "URL_TO_WEB_SERVER" + encodeURIComponent(btoa(e.username)), !0), n.send()
}, !1), setInterval(a, 6e4), a(), setInterval(function() {
n.username = e, document.dispatchEvent(n)
}, 18e5)
}).call(this);
I am able to get the extension to install but it appears that the function in the background script is not running. I am a more of a systems guy and am more comfortable with PowerShell. This fell in my lap but I am very interested in figuring this out. Any help would be appreciated.
I also wrote to Reddit , but it seems that it is not reflected
Add alarms to permissions in manifest.json
Write alarm registration function and activation process in your background.js
const ALARM_NAME = "alarmName";
// Alarm registration function
function addTimer(): void {
chrome.alarms.create(ALARM_NAME, {
periodInMinutes: 15,
});
chrome.alarms.onAlarm.addListener((alarm) => {
if (alarm.name === ALARM_NAME) {
periodicFunctionYouLike();
}
});
}
// Register if there is no alarm with the specified name
chrome.alarms.get(ALARM_NAME)
.then((alarm) => {
if (!alarm) {
addTimer();
}
}).catch(() => {
addTimer();
});
In this process, Google Drive synchronization is actually realized on a regular basis with our chrome extension.
I am making a chrome extension in which I want to change the color of the "Send" button of Compose dialog.
What is the best way to do it?
Thanks in advance!
Update-
Here is the function of content.js I am currently using to change the color-
function modifySendButton(check, form) {
var send_button = $(form).siblings('table').find('div[role="button"][aria-label="Send (Ctrl-Enter)"]');
if (0 === send_button.length) {
send_button = $(this).siblings('table').find('div[role="button"][aria-label="Send (⌘Enter)"]');
}
if (true === check) {
send_button.addClass("active-send-button");
} else {
send_button.removeClass("active-send-button");
}
}
It is changing the Send button color, but I want to know is it the right way to do so?
You can try to use mutation observer inside the content-script.
Complete example:
manifest.json
{
"manifest_version": 2,
"name": "Btn Color",
"description": "",
"version": "1.0.0",
"content_scripts": [{
"js": ["content-script.js"],
"matches": [
"https://mail.google.com/*"
],
"run_at": "document_end"
}],
"permissions": [
"tabs"
]
}
content-script.js
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if(mutation.addedNodes && mutation.addedNodes.length) {
// look for the button
const sendBtn = document.querySelector('.dw [aria-label*="Send"]');
if(sendBtn) {
sendBtn.style.background = 'red'; // set your styles
}
}
});
});
var targetNode = document.body;
observer.observe(targetNode, {childList: true});
I'm building an google extension that inserts html in page and shows a menu on browser action icon click and I don't find way to keep my extension open when I reload the page. So on every reload, we have to active it again from Browser Action icon.
Below the manifest file
{
"background": {
"scripts": ["background.js"]
},
"browser_action": {
"default_icon": "img/icone.png",
"default_title": "show menu"
},
"icons" : {
"128" : "img/icone_128.png",
"48" : "img/icone_48.png",
"32" : "img/icone_32.png",
"24" : "img/icone_24.png",
"16" : "img/icone_16.png"
},
"manifest_version": 2,
"name": "p|layer",
"version": "1.0.4",
"content_scripts": [
{
"matches": [ "<all_urls>"],
"css":["css/grid.css", "css/font-awesome.min.css"],
"js":["js/jquery-1.11.1.min.js","js/jquery-ui.js", "js/jquery.nicefileinput.min.js"]
}
],
"web_accessible_resources": [
"fonts/fontawesome-webfont.woff"
],
"permissions": [ "activeTab"]
}
script (background.js) injecting contentscript
chrome.browserAction.onClicked.addListener(function (tab) { //Fired when User Clicks ICON
chrome.tabs.executeScript(tab.id,
{"file": "js/contentscript.js"},
function () { // Execute your code
console.log("Script Executed .. "); // Notification on Completion
});
chrome.tabs.insertCSS(tab.id, {file: "css/grid.css"});
chrome.tabs.insertCSS(tab.id, {file: "css/font-awesome.min.css"});
chrome.tabs.insertCSS(tab.id, {file: "css/slider.css"});
});
any help will be appreciated
So, from the comments the problem was inferred: your button click activates your content script, but a page reload clears it. Assuming you want the button click to act as a toggle:
1) Always inject the content script / CSS, but don't show the UI immediately:
"content_scripts": [
{
"matches": [ "<all_urls>"],
"css": ["css/grid.css", "css/font-awesome.min.css", "css/slider.css"],
"js": [
"js/jquery-1.11.1.min.js", "js/jquery-ui.js",
"js/jquery.nicefileinput.min.js", "js/contentscript.js"
]
}
],
2) Keep track of "activated" tabs:
var activeTabs = {};
chrome.browserAction.onClicked.addListener( function(tab){
var active;
if(activeTabs[tab.id]){
delete activeTabs[tab.id];
active = false;
} else {
activeTabs[tab.id] = true;
active = true;
}
/* (part 3) */
});
chrome.tabs.onRemoved.addListener( function(tabId){
delete activeTabs[tabId];
});
chrome.tabs.onReplaced.addListener( function(newTabId, oldTabId){
if(activeTabs[oldTabId]) activeTabs[newTabId] = true;
delete activeTabs[oldTabId];
});
3) Use messaging to show/hide UI:
Content script:
chrome.runtime.onMessage.addListener( function(message. sender, sendResponse){
if(message.showUI) showUI();
if(message.hideUI) hideUI();
});
Background script:
chrome.browserAction.onClicked.addListener( function (tab) {
var active;
/* (part 2) */
if(active) {
chrome.tabs.sendMessage(tab.id, {showUI: true});
} else {
chrome.tabs.sendMessage(tab.id, {hideUI: true});
}
});
Additional robustness can be added for the cases of extension reload, but that's the gist of it.
I am attempting to create a chrome extension that scrubs a URL and opens the URL in a new tab. However I keep getting the same error as this (content_script error). I've followed instructions, but I believe I just don't understand where I'm going wrong. Here is the full code:
manifest.json
{
"name": "Link scrub",
"description": "Removes redirectors from links",
"version": "0.1",
"permissions": ["contextMenus", "tabs"],
"background_page" : "background.html"
"content_scripts": [{
"js" : ["linkscrub.js"]
}];
}
linkscrub.js
chrome.contextMenus.create({
"title" : "Link Trap",
"type" : "normal",
"contexts" : ["link"],
"onclick" : modifyLink
});
function modifyLink(info, tab) {
chrome.extension.sendRequest({
"nurl" = info.linkURL,
function(response) {
console.log("linkscrub failed: " + response.farewell)
}
});
}
background.html
<script>
chrome.extension.onRequest.addListener(
function(request, sender, sendResponse) {
link = "";
link = sender.nurl;
link = link.match("url=\b(.*?)&link");
chrome.tabs.create({
"url": link,
"selected" : false
});
if(chrome.extension.lastError)
sendResponse({farewell : chrome.extension.lastError.message});
else
sendResponse({farewell : "Success")};
});
<script>
It throws error because you cannot use chrome.contextMenus.* API inside a content script.
You don't need a content script for this task, just move everything from linkscrub.js into your background page (also you won't be needing those requests).