Firefox extension getSelection().toString() not getting the selected text - javascript

I'm making a basic Firefox extension, which takes the selected text on a page, puts it into an URL and opens it in a new tab. I'm struggling with taking the selected text.
I'm aware of the Firefox bug in textarea and input, however, I'm testing it on regular page text.
manifest.json:
{
"manifest_version": 2,
"name": "Send2search test",
"version": "1.0",
"permissions": ["menus"],
"background": {
"scripts": ["bg.js"]
},
}
bg.js:
browser.menus.create(
{
id: "gglSearch",
title: "Search in new tab.",
contexts: ["selection"],
},
);
browser.menus.onClicked.addListener(async function (info, tab) {
var selectedTxt = window.getSelection().toString();
if (info.menuItemId == "gglSearch") {
var searchUrl = `https://www.google.com/search?q=${selectedTxt}`;
let openNewTab = browser.tabs.create(
{
active: false,
url: searchUrl,
index: tab.index+1
}
);
};
});
Every time it just opens blank google search, so nothing is saved in the selectedTxt variable. What am I doing wrong?
I also tried content.getSelection().toString() instead (https://stackoverflow.com/a/670258/20367262), but that caused the new tab to not get created at all.
PS: I know Firefox has "Search Google for" on right click by default, I'm just trying to work out extension developing basics on this.

Related

Why does my browser extension only works the first time it's added or when it's reloaded?

I'm trying to create a simple extension for Chrome and Firefox which just gets some content from the DOM of certain pages and adds other content.
But the issue is that it only works the first time I load it as a temporary Add-on for testing on both Chrome and Firefox, or when I hit the extension's reload button on about:debugging on Firefox.
My manifest.json only contains the following information:
{
"manifest_version": 2,
"name": "ft_blackhole",
"version": "0.1",
"description": "Shows how many days you have left before you get absorbed by the Blackhole.",
"icons": {
"48": "icons/blackhole.png"
},
"content_scripts": [
{
"matches": [
"https://profile.intra.42.fr/",
"https://profile.intra.42.fr/users/*",
"https://profile.intra.42.fr/users/*/"
],
"js": [
"ft_blackhole.js"
]
}
],
"browser_specific_settings": {
"gecko": {
"id": "ft_blackhole-0.1#intra.42.fr"
}
}
}
I use almost the same exact manifest file (same exact matches) for another extension that works and depends on the same URLs. That extension works fine. The difference between it and this one is that while they both insert content in the DOM, my older extension does it after it fetches data from an API, but this one just gets some text from the current page's DOM:
ft_blackhole.js:
console.log("Hello World");
let blackholeDiv = document
.getElementById("bh")
.getElementsByClassName("emote-bh")[0];
let daysLeft = blackholeDiv.getAttribute("data-original-title");
let daysNum = daysLeft.split(" ")[0];
let status = (() => {
if (daysNum <= 14)
return {cat: "😿", color: "#D8636F"};
else if (daysNum <= 42)
return {cat: "🙀", color: "#F0AD4E"};
else
return {cat: "😸", color: "#5CB85C"};
})();
let daysLeftDiv = document.createElement("div");
daysLeftDiv.innerText = daysLeft + ' ' + status["cat"];
daysLeftDiv.style.color = status["color"];
daysLeftDiv.style.fontSize = "0.7em";
daysLeftDiv.style.fontWeight = "400";
blackholeDiv
.children[1]
.appendChild(daysLeftDiv);
I searched all over the internet, I couldn't understand what is causing it to run only the first time the extension is installed, but then when I refresh the page, it doesn't add anything to the page anymore, until I reload the extension again from about:debugging.
I hope someone could help.
Edit:
I also noticed that it also works after a hard reload of the page on Chrome, but not after a normal reload.
Edit 2:
When I console.log() something in the content script, it works all the time, always logs when I reload the page (normal reload), but the other code for DOM manipulation doesn't...
Edit 3:
I have uploaded a static copy of the profile page if anyone wants to take a look at the HTML content: https://haddi.me/intra-example/intra.html
Edit 4:
It seems the issue is caused after everything is loaded in the DOM, by some javascript that runs after that and makes changes to it, I used Mutation Observer on the target node to log those changes, and there were indeed some few ones, what I can't figure out is how am I supposed to run my code only after that last change (Which is their modification of span#bh-date's style attribute)? This the code I added:
const blackholeDiv = document
.getElementById("bh")
.getElementsByClassName("emote-bh")[0];
// Callback function to execute when mutations are observed
• const callback = function (mutationsList, observer) {
for (const mutation of mutationsList) {
if (mutation.type === 'childList') {
console.log('A child node has been added or removed: ', mutation);
}
else if (mutation.type === 'attributes') {
console.log('The ' + mutation.attributeName + ' attribute was modified: ', mutation);
}
}
};
const config = {attributes: true, childList: true, subtree: true};
const observer = new MutationObserver(callback);
observer.observe(blackholeDiv, config);
And these are the logs:
Screenshot from Chrome's Devtools Console
In your manifest.json file, I do not see a "run_at", to make sure the DOM is ready and then immediately inject the script. Use the "document_end" string in your code.
Source:
https://developer.chrome.com/docs/extensions/mv3/content_scripts/#run_time
manifest.json
{
"name": "Test Extenstion",
"description": "Content test!",
"version": "1.0",
"manifest_version": 2,
"content_scripts": [
{
"matches": ["https://haddi.me/*"],
"js": ["content.js"],
"run_at": "document_end"
}
],
"permissions": ["activeTab"]
}
Tested in Google Chrome web browser version 99.0.4844.83.

onActiveChanged not working every time

I created a simple chrome extension with the following data but for some reason it is not working all the time, there are times which I need to click on the extension button for it to work next time in the current tab.
I am unable to understand why.. it should ALWAYS work when you click on a tab and activate it (even when you create a new tab - it become activated and should run do-something.js
manifest.json
{
"manifest_version": 2,
"name": "Test",
"description": "Test",
"version": "1",
"browser_action": {
"default_icon": "icon.png",
},
"background": {
"scripts": ["background.js"],
"persistent": false
},
"permissions": [
"activeTab",
"tabs"
]
}
background.js
chrome.tabs.onActiveChanged.addListener(function () {
chrome.tabs.executeScript({
file: 'do-something.js'
});
});
do-something.js
function createNotification() {
var notification = document.createElement('div');
Object.assign(notification.style, {
position: 'fixed',
zIndex: 10000,
textAlign: 'center',
width: '100%',
background: '#f5ae20',
padding: '5px',
top: 0,
left: 0
});
notification.innerHTML = `Test`;
document.body.appendChild(notification);
setTimeout(function() {
notification.remove();
}, 4000);
return notification;
}
createNotification();
Why is it not working all the time?
there is some issue with your code. do-something.js should be mentioned in the manifest file otherwise chrome will not find anything, you can put is as content script, background script or web accessible resource.
But if you put it as content script than it will run every time when the page will load (according) to your current code.
Here is my approach
I put the do-something.js in content script and made a communication channel between background js and content script when in the background it finds that active tab has changed then send a message to content script and display the notification
message passing from background
//listener for detecting tab change
chrome.tabs.onActiveChanged.addListener(function () {
console.log("tab changed");
//query about the active tab and get the tab id
//if you add debug point here it will throw exception because debugger is the current active window , which doesnot have tab
chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
//send the message to the content sctipt
chrome.tabs.sendMessage(tabs[0].id, "showNotification", null);//here null is the callback
});
});
Message reception on content script
chrome.runtime.onMessage.addListener(
function (message, sender, sendResponse) {
//you can receive different type of message here
if (message == "showNotification") {
createNotification();
}
});
I have added a github repo for you, you can find more details there.
https://github.com/pfrng/tabChangeListener
Thanks

How to run script on ContextMenu Click Chrome Extension

I'm tried to run script on ContextMenu Click event. I'm used example ContextMenu which have manifest:
{
"name": "Context Menus Sample (with Event Page)",
"description": "Shows some of the features of the Context Menus API using an event page",
"version": "0.7",
"permissions": ["contextMenus"],
"background": {
"persistent": false,
"scripts": ["sample.js"]
},
"manifest_version": 2
}
sample.js:
function onClickHandler(info, tab) {
chrome.tabs.executeScript(null,
{code:"document.body.style.backgroundColor='" + e.target.id + "'"});
};
chrome.contextMenus.onClicked.addListener(onClickHandler);
chrome.runtime.onInstalled.addListener(function() {
var contexts = ["page","selection","link","editable","image","video",
"audio"];
for (var i = 0; i < contexts.length; i++) {
var context = contexts[i];
var title = "Test '" + context + "' menu item";
var id = chrome.contextMenus.create({"title": title, "contexts":[context],
"id": "context" + context});
}
chrome.contextMenus.create({"title": "Oops", "id": "child1"}, function() {
if (chrome.extension.lastError) {
console.log("Got expected error: " + chrome.extension.lastError.message);
}
});
});
and i also change onclick handler to:
function onClickHandler(info, tab) {
chrome.tabs.executeScript(null, {file: "content.js"});
};
when content.js is:
document.body.innerHTML = document.body.innerHTML.replace(new RegExp("text", "gi"), "replaced");
but both of that doesn't working. How to solve it?
Because this question has hundreds of views so I will answer this my old question to help anyone who has the same problem. The answer is based on wOxxOm's comment. I just need to add activeTab permission to the manifest. So should be like this:
{
"name": "Context Menus Sample (with Event Page)",
"description": "Shows some of the features of the Context Menus API using an event page",
"version": "0.7",
"permissions": ["contextMenus","activeTab"],
"background": {
"persistent": false,
"scripts": ["sample.js"]
},
"manifest_version": 2
}
My script before doesn't work because I have code that needs to read/change current active tab content. activeTab permission gives an extension temporary access to the currently active tab when the user invokes the extension - for example by clicking its browser action.

Chrome App Popup On Notification

I'm new to Chrome Apps, however I did successfully get my app to work with GCM. I'm so happy! What I would like the app to do however, is open a popup window when the user gets a notification. I'm making it a video chat app. Please Any Help at all would be greatly appreciated! This code that I have is currently not working for popups, tabs or anything of the sort... :(
background.js
// Returns a new notification ID used in the notification.
function getNotificationId() {
var id = Math.floor(Math.random() * 9007199254740992) + 1;
return id.toString();
}
function messageReceived(message) {
// A message is an object with a data property that
// consists of key-value pairs.
// Concatenate all key-value pairs to form a display string.
var messageString = "";
for (var key in message.data) {
if (messageString != "")
messageString += ", "
messageString += key + ":" + message.data[key];
}
console.log("Message received: " + messageString);
// Pop up a notification to show the GCM message.
chrome.notifications.create(getNotificationId(), {
title: 'GCM Message',
iconUrl: 'gcm_128.png',
type: 'basic',
message: messageString
}, function() {});
chrome.tabs.create({url:"http://www.google.com"});
}
var registerWindowCreated = false;
function firstTimeRegistration() {
chrome.storage.local.get("registered", function(result) {
// If already registered, bail out.
if (result["registered"])
return;
registerWindowCreated = true;
chrome.app.window.create(
"register.html",
{ width: 500,
height: 400,
frame: 'chrome'
},
function(appWin) {}
);
});
}
// Set up a listener for GCM message event.
chrome.gcm.onMessage.addListener(messageReceived);
// Set up listeners to trigger the first time registration.
chrome.runtime.onInstalled.addListener(firstTimeRegistration);
chrome.runtime.onStartup.addListener(firstTimeRegistration);
Manifest.json
{
"name": "GCM Notifications",
"description": "Chrome platform app.",
"manifest_version": 2,
"version": "0.3",
"app": {
"background": {
"scripts": ["background.js"]
}
},
"permissions": ["gcm", "storage", "notifications", "tabs", "<all_urls>"],
"icons": { "128": "gcm_128.png" }
}
chrome.notifications.create should appear outside the browser. I can't see anything wrong with the call you made, though. We need to determine if you're not breaking the Content Security Policy
In Chrome Apps, due to a strict Content Security Policy these URLs must point to a local resource or use a blob or data URL. Use a 3:2 ratio for your image; otherwise a black border frames the image.
Additional references you can look at is the official notification sample repo as well as how to integrate notification with GCM.

Trigger Chrome extension on new tab open

I need to run Chrome extension when new tab is opened and html document is loaded.
Extension needs to check for new tab title and if it's equal to predefined string, tab should close.
For now, I have manage to write extension that works when I click on it's icon. But I want to make it to run without click on icon after the page is loaded in new tab.
Here is the current code.
function getCurrentTabData(callback) {
var queryInfo = {
active: true,
currentWindow: true
};
chrome.tabs.query(queryInfo, function(tabs) {
var tab = tabs[0];
var title = tab.title;
var id = tab.id;
callback(title, id);
});
}
document.addEventListener('DOMContentLoaded', function() {
getCurrentTabData(function(title, id) {
if(title == 'Page title') {
chrome.tabs.remove(id, function() { });
}
});
});
And here is my manifest.json
{
"manifest_version": 2,
"name": "Auto close tab",
"description": "Auto closes tab if title is matched",
"version": "1.0",
"browser_action": {
"default_icon": "icon.png"
},
"permissions": [
"activeTab"
]
}
How to make it run without click on it's icon?
To accomplish this first of all you will need to have a Background Page that will manage your extension state. You can read about it here: https://developer.chrome.com/extensions/background_pages
Then in the background page script you will need to listen when the tab is created with this piece of code:
chrome.tabs.onCreated.addListener(function callback)
Here is documentation for this: https://developer.chrome.com/extensions/tabs#event-onCreated
Hope this will help to solve your issue.

Categories

Resources