I'm following this tutorial on how to create a context menu when a user right clicks on selected text, the menu offers the user the option to send the text to a server:
http://vikku.info/programming/chrome-extension/get-selected-text-send-to-web-server-in-chrome-extension-communicate-between-content-script-and-background-page.htm#Comments
Here are the files:
The myscript.js file:
document.addEventListener('mouseup',function(event)
{
var sel = window.getSelection().toString();
alert('selection is '+sel)
if(sel.length)
chrome.extension.sendRequest({'message':'setText','data': sel},function(response){})
})
The background.html file:
<script>
var seltext = null;
chrome.extension.onRequest.addListener(function(request, sender, sendResponse)
{
switch(request.message)
{
case 'setText':
window.seltext = request.data
break;
default:
sendResponse({data: 'Invalid arguments'});
break;
}
});
function savetext(info,tab)
{
var jax = new XMLHttpRequest();
jax.open("POST","http://localhost/text/");
jax.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
jax.send("text="+seltext);
jax.onreadystatechange = function() { if(jax.readyState==4) { alert(jax.responseText); }}
}
alert('here')
var contexts = ["selection"];
for (var i = 0; i < contexts.length; i++)
{
var context = contexts[i];
chrome.contextMenus.create({"title": "Send to Server", "contexts":[context], "onclick": savetext});
}
</script>
The manifest.json file:
<script>
var seltext = null;
chrome.extension.onRequest.addListener(function(request, sender, sendResponse)
{
switch(request.message)
{
case 'setText':
window.seltext = request.data
break;
default:
sendResponse({data: 'Invalid arguments'});
break;
}
});
function savetext(info,tab)
{
var jax = new XMLHttpRequest();
jax.open("POST","http://localhost/text/");
jax.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
jax.send("text="+seltext);
jax.onreadystatechange = function() { if(jax.readyState==4) { alert(jax.responseText); }}
}
alert('here')
var contexts = ["selection"];
for (var i = 0; i < contexts.length; i++)
{
var context = contexts[i];
chrome.contextMenus.create({"title": "Send to Server", "contexts":[context], "onclick": savetext});
}
</script>
The popup.html file:
<body>
Just a sample popup
</body>
In myscript.js the function document.addEventListener('mouseup',function(event) is called every time a mouseup event is fired ed but I think this should be called if the user decides to send the request to the server. The context menu should be fired when the user right clicks selected text but I don't know why this is occurring? I needed to update the manifest version to 2.
i don't know, if i got you right...the user select text, press the right mouse button and than you want to do "something"...
try this:
'get the right mouse click
window.oncontextmenu = function ()
{
showSelection(); // call a function or do something here
return false; // cancel the default right click mouse menu (necessary)
}
'in the function get the selected text and do something
function showSelection()
{
var sel = window.getSelection().toString();
alert('selection is '+sel);
.....
}
i understood that your problem is that context menu is not firing... for that you need to do small changes to your manifest
add manifest vesrion as 2
change background_page as background which is a key and value as {scripts:["background.js"]}
create a new javascript file with name as background.js and copy the script tag which is there in background html.i added manifest and javascript. let me know if you found any difficulty.
manifest:
{
"name": "Word Reminder",
"version": "1.0",
"manifest_version": 2,
"description": "Word Reminder.",
"browser_action": {
"default_icon": "images/stick-man1.gif",
"popup":"popup.html"
},
"background": {
"scripts": ["background.js"]
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["js/myscript.js"]
}
],
"permissions": [
"http://*/*",
"https://*/*",
"contextMenus",
"tabs"
],
"content_security_policy": "script-src 'self' https://ajax.googleapis.com; object-src 'self'"
}
background.js(new file in the same folder should contain below code):
var seltext = null;
chrome.extension.onRequest.addListener(function(request, sender, sendResponse)
{
switch(request.message)
{
case 'setText':
window.seltext = request.data
break;
default:
sendResponse({data: 'Invalid arguments'});
break;
}
});
function savetext(info,tab)
{
var jax = new XMLHttpRequest();
jax.open("POST","http://localhost/text/");
jax.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
jax.send("text="+seltext);
jax.onreadystatechange = function() { if(jax.readyState==4) { alert(jax.responseText); }}
}
var contexts = ["selection"];
for (var i = 0; i < contexts.length; i++)
{
var context = contexts[i];
chrome.contextMenus.create({"title": "Send to Server", "contexts":[context], "onclick": savetext});
}
Related
I'm stuck trying to run a function placed in content.js by a shortcut.
I tested a running of a simple function out of background.js, and it worked for me - I added to manifest.json these lines:
"commands":{
"run-script": {
"suggested_key": {
"default": "Alt+1",
"windows": "Alt+1",
"mac": "Command+E"
},
"description": "Run",
"global": true
}
}
and just "wore" my function with
chrome.commands.onCommand.addListener((command) => {
//myFunc ...
});
But not in the content.js - I get just an error here.
This is my content script - the function I want to be run by shortcut is from the beginning to line 54.
content.js
//Listening for double click on the page
$(document).dblclick(function(e){
chrome.storage.local.get(['enabled'], function(res){
if(res.enabled)
{
if((e.pageX+400) < screen.availWidth)
{
innerDiv.css("left", e.pageX);
}
else
{
innerDiv.css("right", e.pageX);
}
if((e.pageY+300) < document.body.clientHeight)
{
innerDiv.css("top", e.pageY);
}
else
{
innerDiv.css("bottom", e.pageY);
}
innerDiv.css("left", e.pageX);
innerDiv.css("top", e.pageY);
//getting selected / highlighted word
var word = window.getSelection().toString().trim();
if(word.length==0)
return;
//Stating that extension is going to request definition from api
innerDivBody.html("Fetching defenition...");
//fading Up popup
innerDiv.fadeIn();
innerDivBody.empty();
setupHeader(word);
//Ajax request to get definition of selected word
chrome.runtime.sendMessage({word:word}, function(content){
innerDivBody.append(content);
});
}
});
});
//detecting click on outside popup to hide popup
$(document).click(function(e){
if(e.target.id != 'mzrdiv')
{
innerDiv.hide();
}
});
//popup outer div which is appended to page
var outerDiv = $("<div></div>");
//setting id
outerDiv.attr('id', 'mzrdiv');
//appending outer div
document.documentElement.appendChild(outerDiv[0]);
//Using dom shadow to avoid mixing up of page styles
var root = outerDiv[0].attachShadow({mode: 'open'});
//inner div of popup
var innerDiv = $("<div></div>");
innerDiv.css(inner_div_css);
var peak = $("<div></div>");
innerDiv.append(peak);
peak.css(peak_css);
var innerDivBody = $("<div></div>");
innerDivBody.css(inner_div_body_css)
innerDiv.append(innerDivBody);
//appending inner div to shadow dom
root.appendChild(innerDiv[0]);
function setupHeader(word)
{
var innerDivHeader = $("<div></div>");
innerDivHeader.css(inner_div_header_css);
var wordSpan = $("<span></span>");
wordSpan.css(word_span_css);
wordSpan.html(word);
innerDivHeader.append(wordSpan)
var btn_close = $("<button></button>");
btn_close.css(btn_close_css);
btn_close.click(function(){
innerDiv.hide();
})
innerDivHeader.append(btn_close);
innerDivBody.append(innerDivHeader);
}
background.js
chrome.browserAction.onClicked.addListener(function(){
chrome.storage.local.get(['enabled'], function(res){
if(res.enabled)
{
chrome.storage.local.set({enabled:false}, function(){
chrome.browserAction.setIcon({
path:'icon32gray.png'
})
});
}
else
{
chrome.storage.local.set({enabled:true}, function(){
chrome.browserAction.setIcon({
path:'icon32.png'
})
});
}
});
});
chrome.storage.local.get(['enabled'], function(res){
if(typeof(res.enabled) == "undefined")
{
chrome.storage.local.set({enabled:true}, function(){
chrome.browserAction.setIcon({
path:'icon32.png'
});
});
}
else if(res.enabled)
{
chrome.browserAction.setIcon({
path:'icon32.png'
});
}
else
{
chrome.browserAction.setIcon({
path:'icon32gray.png'
});
}
});
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
$.get('https://www.duden.de/rechtschreibung/'+request.word, function(res){
var content = "Not Found";
if($(res).find(".lemma__determiner").length>0)
{
content = $(res).find(".lemma__determiner")[0].outerHTML;
}
sendResponse(content);
})
.fail(function(xhr){
//If request error show word is not found
var content = "Not Found";
sendResponse(content);
});
return true;
});
I'm having a problem importing the file "score.js" that is in same library of the "background.js" (scripts libary).
I'm new to both js and chrome extensions.
I looked into require.js and did this.
Background.html:
<!doctype html>
<html>
<head>
<title>Tab Manager</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<h1>Tab Manager</h1>
<script data-main="scripts/score.js" src="scripts/require.js"></script>
</body>
</html>
background.js:
// gets a dictionary of existing windows ids as keys and tab arrays as values.
function get_windows()
{
var window_dict = {};
chrome.windows.getAll({populate:true}, windowArr =>{
var i;
for (i = 0; i < windowArr.length; i++) {
var window = windowArr[i];
var window_id = window.id;
var tabs = window.tabs;
window_dict[window_id] = tabs;
}
console.log(window_dict);
main(window_dict);
})
}
function starting_scores(window_dict)
{
var scores = [];
var i;
var j = 0;
var total_tabs = [];
for (const [window_id, tabs] of Object.entries(window_dict)) {
for (i = 0; i < tabs.length; i++){
scores[j] = new Score(tabs[i], window_id);
j = j++;
}}
return scores;
}
function main(window_dict)
{
var scores = starting_scores(window_dict);
console.log(scores);
}
get_windows();
score.js:
class Score
{
// Constructor
constructor(tab, window_id)
{
this.window_id = window_id; // The window id of the window that contains the tab.
this.tab = tab;
this.url = tab.url;
this.active = tab.active; // If the tab is currently looked at then it's active.
this.audible = tab.audible; // If the tab is playing audio then audible will be true.
this.discarded = tab.discarded; // If the tab is already unloaded but not closed this will be true.
/* If the new tab is active then the timer is set to zero because
the user hasn't spent any time not using the tab. Otherwise a timer starts. */
if(this.active == true){
this.timer == 0;}
else{
this.timer = performance.now;}
}
// Get methods
getWindowId() {
return this.window_id;
}
getTab() {
return this.tab;
}
getUrl() {
return this.url;
}
getActive() {
return this.active;
}
getAudible() {
return this.audible;
}
getDiscarded() {
return this.discarded;
}
getTimer() {
return this.timer;
}
// Set methods
setWindowId(window_id) {
this.window_id = window_id;
}
setTab(tab) {
this.tab = tab;
}
setUrl(url) {
this.url = url;
}
setActive(active) {
this.active = active;
}
setAudible(audible) {
this.audible = audible;
}
setDiscarded(discarded) {
this.discarded = discarded;
}
setTimer(timer) {
this.timer = timer;
}
}
I'm trying to build an extension that makes a score for usage for each tab and then removes the ones with low score. Don't mind the logic I just can't get score.js to work in background.js
Any help is apreciated.
An update to #wOxxOm's answer, starting with Manifest v3, you cannot use background.page, but you can load the script as a module directly.
manifest.json
"background": {
"service_worker": "background.js",
"type": "module"
}
background.js
import Score from './score.js';
score.js
export default class Score
ManifestV3
There are several methods:
https://stackoverflow.com/a/66408379
ManifestV2
Remove require.js and use the built-in ES modules import.
manifest.json:
"background": {
"page": "background.html"
}
background.html, the entire file is just one line:
<script src="background.js" type="module"></script>
background.js, insert at the beginning of the file:
import Score from './score.js';
The imported script score.js, change the first line:
export default class Score
Remember that if you don't use a bundler/compiler like WebPack the file name in import must include the path (./ means "same directory") and the file extension, otherwise it won't work. A typical file extension for a module is .mjs.
I am new to Firexfox Add-On development and I would like to parse meta-tags from a website in the internet. My Firefox Add-On development environment is setup completely and a panel in the toolbar appears as it should be. When I open the panel the current visited website should be read by its meta-tags. In the following you will see amazon.de as example.
This is how my main.js file looks like:
var { ToggleButton } = require('sdk/ui/button/toggle');
var panels = require("sdk/panel");
var self = require("sdk/self");
var button = ToggleButton({
id: "my-button",
label: "my button",
icon: {
"16": "./icon-16.png",
"32": "./icon-32.png",
"64": "./icon-64.png"
},
onChange: handleChange
});
var panel = panels.Panel({
contentURL: self.data.url("panel.html"),
onHide: handleHide
});
function handleChange(state) {
if (state.checked) {
panel.show({
position: button
});
}
}
function handleHide() {
button.state('window', {checked: false});
}
And this is the panel where the current visited website should be read by its meta-tags:
<script>
// https://developer.mozilla.org/en-US/docs/Introduction_to_using_XPath_in_JavaScript
var req = new XMLHttpRequest();
req.open("GET", "http://www.amazon.de/Apple-Smartphone-Retina-Display-Megapixel-Bluetooth/dp/B00F8JF2OM/", false);
req.send(null);
var xmlDoc = req.responseXML;
var nsResolver = xmlDoc.createNSResolver( xmlDoc.ownerDocument == null ? xmlDoc.documentElement : xmlDoc.ownerDocument.documentElement);
//XPath 'html//meta[#name="title"]/#content'
var itemIterator = xmlDoc.evaluate('html//meta[#name="title"]/#content', xmlDoc, nsResolver, XPathResult.ANY_TYPE, null );
var thisHeading = itemIterator.iterateNext();
var alertText = 'Items:\n'
while (thisHeading) {
alertText += thisHeading.textContent + '\n';
thisHeading = itemIterator.iterateNext();
}
</script>
I don't get any errors and don't get any alert or output data.
However the Firfox XPath add-on tells me the path is correct and shows the data.
Trying to get my feet wet with Chrome Extensions. I created a tab limiter, but its buggy.
I want the event of clicking the browser action icon to toggle on and off the extension. I believe I have that set up properly. When toggled on, it creates a listener from the chrome.tabs API, but when toggled off, I'm having a hard time removing this listener.
I've tried some of the common answers on Stack Overflow (one is shown below) for removing listeners, but none are functioning properly for me.
Here is the background.js
var tabUrls, bookmarkFolder, currentWindow;
var toggle = false;
chrome.browserAction.onClicked.addListener(function(tab){
toggle = !toggle;
if(toggle) {
chrome.browserAction.setBadgeText({"text": "ON"});
console.log("I'm listening to the browser action");
runExtension();
} else {
console.log("i shouldn't be working now.");
chrome.browserAction.setBadgeText({"text": ""});
// Need to remove the listener somehow. This isn't working.
chrome.tabs.onCreated.addListener(doStuff);
}
});
// Functions
// Potential function to removeListener. Not working.
function doStuff(tab){
chrome.tabs.onCreated.removeListener(doStuff);
}
var runExtension = function(){
chrome.tabs.onCreated.addListener(function(tab){
// Find and set reference to the current window
chrome.windows.getCurrent(function(window){
currentWindow = window;
});
// Logic to limit tabs (remove window, create bookmarks, play audio, manipulate DOM on new window)
chrome.tabs.getAllInWindow(function(tabs) {
tabUrls = lengthRequirement(tabs);
console.log(tabUrls);
if (typeof tabUrls != "string") {
createBookmarkFolder(tabUrls);
chrome.windows.remove(currentWindow.id);
playAudioClip();
chrome.windows.create({ "url": "http://www.justinstewart.info/"});
chrome.tabs.executeScript({file: "content_script.js"});
}
});
});
}
var playAudioClip = function(){
var audio = new Audio("audio_file.mp3");
audio.play();
}
var createBookmarkFolder = function(tabUrls){
chrome.bookmarks.create(
{'title': "Caroline Strikes Again # " + new Date()},
function(bookmark){
createBookmarks(tabUrls, bookmark);
}
)
}
var createBookmarks = function(urls, folder){
for(i=0; i<urls.length; i++) {
chrome.bookmarks.create({
'parentId': folder.id,
'url': urls[i][0],
'title': urls[i][1]
})
}
}
var lengthRequirement = function(tabsArray){
if (tabsArray.length > 9) {
var urls = [];
for(i=0; i<tabsArray.length; i++) {
info = [];
info.push(tabsArray[i].url);
info.push(tabsArray[i].title);
urls.push(info);
}
return urls;
} else {
return "Not there yet.....";
}
}
manifest.json file:
{
"manifest_version": 2,
"name": "Caroline",
"version": "0.2",
"description": "A fun tab limiter to help increase productivity and focus.",
"icons": {
"16": "icon16.png",
"48": "icon48.png",
"128": "icon128.png"
},
"background": {
"scripts": ["background.js"],
"persistent": false
},
"browser_action": {
"default_icon": "icon48.png",
"default_title": "Sweet Caroline"
},
"permissions": [
"tabs", "bookmarks", "*://*/"
]
}
Any help is greatly appreciated!
.removeListener(func) will only remove a listener if func is identical to one of the previously added event listeners.
First, remove the anonymous function within runExtension, because if it is anonymous, you cannot get a reference to the function object any more:
var runExtension = function(){
chrome.tabs.onCreated.addListener(function(tab){
...
});
};
Then, replace runExtension() with .addListener(runExtension);, and use .removeListener(runExtension); whenever you want to remove the listener:
chrome.browserAction.onClicked.addListener(function(tab){
toggle = !toggle;
if (toggle) {
...
chrome.tabs.onCreated.addListener(runExtension);
} else {
...
chrome.tabs.onCreated.removeListener(runExtension);
}
});
I am writing a chrome extension which should change the content-type of http responses. It is working fine. Now, I want to control it using keypress i.e. user should be able to bypass the default action of extension on keypress. I tried this, but it doesn't work:
background.js
var enableEXT = true;
window.onkeydown = function()
{
console.log('Testing hello');
if (event.keyCode == 70)
enableEXT = false;
console.log(event.keyCode);
console.log(enableEXT);
};
window.onkeyup = function()
{
enableEXT = true;
console.log(event.keyCode);
};
chrome.webRequest.onHeadersReceived.addListener(function(details) {
for (var i = 0; i < details.responseHeaders.length; ++i)
{
if (details.responseHeaders[i].name.toLowerCase() == 'content-type' && !enableEXT)
details.responseHeaders[i].value = 'application/xyz';
}
return {responseHeaders: details.responseHeaders};
}, {urls: ["<all_urls>"]}, ['blocking', 'responseHeaders']);
Try using a content script, injected into the web page, instead of a background script. Use this in your manifest.json:
"content_scripts" : [{
"js" : ["myScript.js"]
}]
"permissions": [
"tabs", "http://*/*", "https://*/*"
]
However, chrome extensions are sandboxed from other scripts in the page. However, you do have access to the DOM and you can inject your own tags. So, create a new <script> tag and append it to the DOM, and the new script (script.js) it references can include the event listeners you want.
background.js:
chrome.runtime.onConnect.addListener(function(port) {
port.onMessage.addListener(function(msg) {
enableEXT = msg.enableEXT;
});
});
chrome.webRequest.onHeadersReceived.addListener(function(details) {
for (var i = 0; i < details.responseHeaders.length; ++i)
{
if (details.responseHeaders[i].name.toLowerCase() == 'content-type' && !enableEXT)
details.responseHeaders[i].value = 'application/xyz';
}
return {responseHeaders: details.responseHeaders};
}, {urls: ["<all_urls>"]}, ['blocking', 'responseHeaders']);
myScript.js:
//to inject into the page
var s = document.createElement('script');
s.src = chrome.extension.getURL("script.js");
s.onload = function() {
this.parentNode.removeChild(this);
};
(document.head||document.documentElement).appendChild(s);
//to communicate with our background
var port = chrome.runtime.connect();
window.addEventListener("message", function(event) {
// We only accept messages from ourselves
if (event.source != window)
return;
if (event.data.type && (event.data.type == "FROM_PAGE")) {
port.postMessage(event.data);
}
}, false);
script.js:
window.onkeydown = function()
{
if (event.keyCode == 70)
window.postMessage({ enableEXT: true, "*");
};
window.onkeyup = function()
{
window.postMessage({ enableEXT: false, "*");
};
To communicate between your injected code, your content script, and your background page is a very awkward situation, but can be dealt with, with all the postMessage APIs...
Add a parameter named event to the event handlers you assign to the events like this:
window.onkeydown = function(event)
{
console.log(event.keyCode);
};