I am trying to create entries on the Chrome context menu based on what is selected.
I found several questions about this on Stackoverflow, and for all of them the answer is: use a content script with a "mousedown" listener that looks at the current selection and creates the Context Menu.
I implemented this, but it does not always work. Sometimes all the log messages say that the context menu was modified as I wanted, but the context menu that appears is not updated.
Based on this I suspected it was a race condition: sometimes chrome starts rendering the context menu before the code ran completely.
I tried adding a eventListener to "contextmenu" and "mouseup". The later triggers when the user selects the text with the mouse, so it changes the contextmenu much before it appears (even seconds). Even with this technique, I still see the same error happening!
This happens very often in Chrome 22.0.1229.94 (Mac), occasionally in Chromium 20.0.1132.47 (linux) and it did not happen in 2 minutes trying on Windows (Chrome 22.0.1229.94).
What is happening exactly? How can I fix that? Is there any other workaround?
Here is a simplified version of my code (not so simple because I am keeping the log messages):
manifest.json:
{
"name": "Test",
"version": "0.1",
"permissions": ["contextMenus"],
"content_scripts": [{
"matches": ["http://*/*", "https://*/*"],
"js": ["content_script.js"]
}],
"background": {
"scripts": ["background.js"]
},
"manifest_version": 2
}
content_script.js
function loadContextMenu() {
var selection = window.getSelection().toString().trim();
chrome.extension.sendMessage({request: 'loadContextMenu', selection: selection}, function (response) {
console.log('sendMessage callback');
});
}
document.addEventListener('mousedown', function(event){
if (event.button == 2) {
loadContextMenu();
}
}, true);
background.js
function SelectionType(str) {
if (str.match("^[0-9]+$"))
return "number";
else if (str.match("^[a-z]+$"))
return "lowercase string";
else
return "other";
}
chrome.extension.onMessage.addListener(function(msg, sender, sendResponse) {
console.log("msg.request = " + msg.request);
if (msg.request == "loadContextMenu") {
var type = SelectionType(msg.selection);
console.log("selection = " + msg.selection + ", type = " + type);
if (type == "number" || type == "lowercase string") {
console.log("Creating context menu with title = " + type);
chrome.contextMenus.removeAll(function() {
console.log("contextMenus.removeAll callback");
chrome.contextMenus.create(
{"title": type,
"contexts": ["selection"],
"onclick": function(info, tab) {alert(1);}},
function() {
console.log("ContextMenu.create callback! Error? " + chrome.extension.lastError);});
});
} else {
console.log("Removing context menu")
chrome.contextMenus.removeAll(function() {
console.log("contextMenus.removeAll callback");
});
}
console.log("handling message 'loadContextMenu' done.");
}
sendResponse({});
});
The contextMenus API is used to define context menu entries. It does not need to be called right before a context menu is opened. So, instead of creating the entries on the contextmenu event, use the selectionchange event to continuously update the contextmenu entry.
I will show a simple example which just displays the selected text in the context menu entry, to show that the entries are synchronized well.
Use this content script:
document.addEventListener('selectionchange', function() {
var selection = window.getSelection().toString().trim();
chrome.runtime.sendMessage({
request: 'updateContextMenu',
selection: selection
});
});
At the background, we're going to create the contextmenu entry only once. After that, we update the contextmenu item (using the ID which we get from chrome.contextMenus.create).
When the selection is empty, we remove the context menu entry if needed.
// ID to manage the context menu entry
var cmid;
var cm_clickHandler = function(clickData, tab) {
alert('Selected ' + clickData.selectionText + ' in ' + tab.url);
};
chrome.runtime.onMessage.addListener(function(msg, sender, sendResponse) {
if (msg.request === 'updateContextMenu') {
var type = msg.selection;
if (type == '') {
// Remove the context menu entry
if (cmid != null) {
chrome.contextMenus.remove(cmid);
cmid = null; // Invalidate entry now to avoid race conditions
} // else: No contextmenu ID, so nothing to remove
} else { // Add/update context menu entry
var options = {
title: type,
contexts: ['selection'],
onclick: cm_clickHandler
};
if (cmid != null) {
chrome.contextMenus.update(cmid, options);
} else {
// Create new menu, and remember the ID
cmid = chrome.contextMenus.create(options);
}
}
}
});
To keep this example simple, I assumed that there's only one context menu entry. If you want to support more entries, create an array or hash to store the IDs.
Tips
Optimization - To reduce the number of chrome.contextMenus API calls, cache the relevant values of the parameters. Then, use a simple === comparison to check whether the contextMenu item need to be created/updated.
Debugging - All chrome.contextMenus methods are asynchronous. To debug your code, pass a callback function to the .create, .remove or .update methods.
MDN doc for menus.create(), 'title' param
You can use "%s" in the string. If you do this in a menu item, and some text is selected in the page when the menu is shown, then the selected text will be interpolated into the title.
https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/menus/create
Thus
browser.contextMenus.create({
id: 'menu-search',
title: "Search '%s'", // selected text as %s
contexts: ['selection'], // show only if selection exist
})
Related
I have created a Chrome extension which upon selecting text, offers a context menu link to Salesforce using the selected text:
function doSearch (search_target, tab)
{
chrome.tabs.create( {
url : "https://my.salesforce.com/apex/BR_caseRedirectDependingLicense?number="+search_target.replace(/\D/g,''),
selected : true,
index : tab.index + 1
} );
}
function selectionHandler (info, tab)
{
doSearch( info.selectionText, tab );
}
function resetContextMenus ()
{
chrome.contextMenus.removeAll(
function()
{
var id = chrome.contextMenus.create( {
title: "Open in Salesforce",
contexts: [ "selection" ],
onclick: selectionHandler
} );
}
);
}
resetContextMenus();
The intention here is to mark ticket numbers and open them in SF quickly, and it works perfectly.
However, I was wondering if it's possible to update an open salesforce tab instead of launching a new one every time.
I have tried looking around and encountered the following sample extension:
https://chromium.googlesource.com/chromium/src/+/master/chrome/common/extensions/docs/examples/api/tabs/inspector/
But it doesn't seem to work at all (perhaps because it's outdated).
I would much appreciate any help/guidance on how to approach this.
Yes, you can do that, you can do it using the chrome.tabs.update(..) function, here is an example, this will update the tab in which your context menu item was clicked:
function selectionHandler (info, tab) {
chrome.tabs.update(tab.id, {
url : "https://my.salesforce.com/apex/BR_caseRedirectDependingLicense?number="+info.selectionText.replace(/\D/g,''),
active : true
});
}
If you want to first create a new tab and then keep updating it, you can use something like this:
let tabId = -1;
function doSearch (search_target, tab)
{
const tabDetails = {
url : "https://my.salesforce.com/apex/BR_caseRedirectDependingLicense?number="+search_target.replace(/\D/g,''),
active : true,
index : tab.index + 1
};
if (tabId == -1) {
chrome.tabs.create(tabDetails, tab => {
tabId = tab.id;
});
} else {
// check if tab is still around
chrome.tabs.get(tabId, (tab) => {
if (tab) {
chrome.tabs.update(tab.id, tabDetails);
} else {
chrome.tabs.create(tabDetails, tab => {
tabId = tab.id;
});
}
});
}
}
Here is the chrome.tabs API documentation beside this two examples, you may also want to look into chrome.tabs.query(..), you can use that to find a specific tab.`
Also, in all these examples I've used active instead of selected because selected is deprecated.
here is the problem I am trying to solve - I am not sure it is possible at all. I have a web app and I need to enable data copy/paste from the app and to the app, and I have a problem with paste. If I past with CTRL + V shortcut I can get the data from the clipboard using
e.originalEvent.clipboardData.getData('text')
in 'paste' eventhandler and it works fine. What I need to enable is 'Paste' from custom context menu and my first try was to dispatch paste event manually like this
var event = new KeyboardEvent('paste', {
view: window,
bubbles: true,
cancelable: true
});
document.dispatchEvent(event);
and it actually hit paste eventhandler, but I couldn't get access to clipboard data like in the previous case. I understand that this is forbidden because of security issues - if this was allowed any page would be able to access data from the clipboard. My question is how to implement this - we are able to copy data from excel to e.g. google drive document and paste it there using a custom context menu (http://pokit.org/get/?1b5f6f4f0ef4b80bb8637649121bcd75.jpg), so I believe it is possible. Thank u all!
So, in my web application I have a custom context menu which has 'Paste' action (bunch of '<li>' tags in a popup). And when the user click on 'Paste' I call this function
if (browser === 'CHROME') {
var extensionId = 'some_id';
chrome.runtime.sendMessage(extensionId, { message: "getClipboardData" },
function (clipboardData) {
console.log('Clipboard data: ', clipboardData);
var txt = $('.helper_textarea');
$(txt).val(clipboardData);
// Call 'paste' function we have clipboard data
}
);
}
In my extension I have i paste.js file I have
function getDataFromClipboard() {
var bg = chrome.extension.getBackgroundPage();
var helperTextArea = bg.document.getElementById('sandbox');
if (helperTextArea == null) {
helperTextArea = bg.document.createElement('textarea');
document.body.appendChild(helperTextArea);
}
helperTextArea.value = '';
helperTextArea.select();
// Clipboard data
var clipboardData = '';
bg.document.execCommand("Paste");
clipboardData = helperTextArea.value;
helperTextArea.value = '';
return clipboardData;
}
chrome.runtime.onMessageExternal.addListener(
function(req, sender, callback) {
if (req) {
if (req.message) {
if (req.message == "installed") {
console.log('Checking is extension is installed!');
callback(true);
}
else if(req.message = "getClipboardData") {
console.log('Get clipboard data');
callback(getDataFromClipboard());
}
}
}
return true;
}
);
And in manifest file
"background" : {
"scripts" : [ "paste.js" ]
},
"externally_connectable": {
"matches": ["*://localhost:*/*"]
},
and of course
"permissions": ["clipboardRead" ],
I use this function to check if extension is added
isExtensionInstalled: function (extensionId, callback) {
chrome.runtime.sendMessage(extensionId, { message: "installed" },
function (reply) {
if (reply) {
callback(true);
} else {
callback(false);
}
});
},
And this is working great. Now the problem is how to port this to Edge. What is equivalent to 'chrome.runtime.sendMessage' in Edge? Thanks for your help.
Is there a way to show context menu actions, only when the user right-clicks on classes that start with "story".
For example: if the user right-clicks on an object in the page of class "story ....", the context menu buttons should appear, otherwise nothing should happen.
Here is my code (though it does not work):
var divs = document.querySelectorAll("[class^=story]"); //get all classes that start with "Story"
window.oncontextmenu = function() {
for(var i=0; i < divs.length; i++)
{
divs[i].onclick = function() {
chrome.contextMenus.create
(
{"id": "butto1", "title": "1", "contexts":["all"], "onclick": genericOnClick}
);
chrome.contextMenus.create
(
{"id": "button2", "title": "2", "contexts":["all"], "onclick": genericOnClick}
);
chrome.contextMenus.create
(
{"id": "button3", "title": "3", "contexts":["all"], "onclick": genericOnClick}
);
};
}
return true;
};
function genericOnClick(info, tab) {
//do some crap here
chrome.contextMenus.removeAll();
}
In this related answer, I explained that context menu items cannot be created on the fly, because the time between a contextmenu event and the appearance of the context menu item is not sufficient to get a chrome.contextMenus.create call in between.
The other answer explains how to make sure that the context menu entry shows the selected text. This was done by listening to the selectionchange event. For your purpose, we want to use an event which has the desired timing.
I'm going to use the mouseover and mouseout events. By depending on mouse events, the context menu will not work when you use the keyboard, e.g. by focusing an element using JavaScript or the Tab key, followed by pressing the context menu key. I did not implement it in the solution below.
The demo consists of three files (plus an HTML page to test it). I put all files in a zip file, available at https://robwu.nl/contextmenu-dom.zip.
manifest.json
Every Chrome extension requires this file in order to work. For this application, a background page and content script is used. In addition, the contextMenus permission is required.
{
"name": "Contextmenu based on activated element",
"description": "Demo for https://stackoverflow.com/q/14829677",
"version": "1",
"manifest_version": 2,
"background": {
"scripts": ["background.js"]
},
"content_scripts": [{
"run_at": "document_idle",
"js": ["contentscript.js"],
"matches": ["<all_urls>"]
}],
"permissions": [
"contextMenus"
]
}
background.js
The background page will create the context menus on behalf of the content script. This is achieved by binding an event listener to chrome.runtime.onConnect, which offers a simple interface to replace context menu entries.
chrome.contextMenus.create is called whenever a message is received over this port (from the content script). All properties, except for onclick are JSON-serializable, so only the "onclick" handler needs a special treatment. I've used a string to identify a pre-defined function in a dictionary (var clickHandlers).
var lastTabId;
// Remove context menus for a given tab, if needed
function removeContextMenus(tabId) {
if (lastTabId === tabId) chrome.contextMenus.removeAll();
}
// chrome.contextMenus onclick handlers:
var clickHandlers = {
'example': function(info, tab) {
// This event handler receives two arguments, as defined at
// https://developer.chrome.com/extensions/contextMenus#property-onClicked-callback
// Example: Notify the tab's content script of something
// chrome.tabs.sendMessage(tab.id, ...some JSON-serializable data... );
// Example: Remove contextmenus for context
removeContextMenus(tab.id);
}
};
chrome.runtime.onConnect.addListener(function(port) {
if (!port.sender.tab || port.name != 'contextMenus') {
// Unexpected / unknown port, do not interfere with it
return;
}
var tabId = port.sender.tab.id;
port.onDisconnect.addListener(function() {
removeContextMenus(tabId);
});
// Whenever a message is posted, expect that it's identical to type
// createProperties of chrome.contextMenus.create, except for onclick.
// "onclick" should be a string which maps to a predefined function
port.onMessage.addListener(function(newEntries) {
chrome.contextMenus.removeAll(function() {
for (var i=0; i<newEntries.length; i++) {
var createProperties = newEntries[i];
createProperties.onclick = clickHandlers[createProperties.onclick];
chrome.contextMenus.create(createProperties);
}
});
});
});
// When a tab is removed, check if it added any context menu entries. If so, remove it
chrome.tabs.onRemoved.addListener(removeContextMenus);
contentscript.js
The first part of this script creates simple methods for creating context menus.
In the last 5 lines, the context menu entries are defined and bound to all current and future elements which match the given selector. Like I said in the previous section, the argument type is identical to the createProperties argument of chrome.contextMenus.create except for "onclick", which is a string which maps to a function in the background page.
// Port management
var _port;
var getPort = function() {
if (_port) return _port;
_port = chrome.runtime.connect({name: 'contextMenus'});
_port.onDisconnect.addListener(function() {
_port = null;
});
return _port;
}
// listOfCreateProperties is an array of createProperties, which is defined at
// https://developer.chrome.com/extensions/contextMenus#method-create
// with a single exception: "onclick" is a string which corresponds to a function
// at the background page. (Functions are not JSON-serializable, hence this approach)
function addContextMenuTo(selector, listOfCreateProperties) {
// Selector used to match an element. Match if an element, or its child is hovered
selector = selector + ', ' + selector + ' *';
var matches;
['matches', 'webkitMatchesSelector', 'webkitMatches', 'matchesSelector'].some(function(m) {
if (m in document.documentElement) {
matches = m;
return true;
}
});
// Bind a single mouseover+mouseout event to catch hovers over all current and future elements.
var isHovering = false;
document.addEventListener('mouseover', function(event) {
if (event.target && event.target[matches](selector)) {
getPort().postMessage(listOfCreateProperties);
isHovering = true;
} else if(isHovering) {
getPort().postMessage([]);
isHovering = false;
}
});
document.addEventListener('mouseout', function(event) {
if (isHovering && (!event.target || !event.target[matches](selector))) {
getPort().postMessage([]);
isHovering = false;
}
});
}
// Example: Bind the context menus to the elements which contain a class attribute starts with "story"
addContextMenuTo('[class^=story]', [
{"id": "butto1", "title": "1", "contexts":["all"], "onclick": 'example'},
{"id": "button2", "title": "2", "contexts":["all"], "onclick": 'example'},
{"id": "button3", "title": "3", "contexts":["all"], "onclick": 'example'}
]);
The previous code assumes that all context menu clicks are handled by the background page. If you want to handle the logic in the content script instead, you need to bind message events in the content script. I've shown an (commented) instance of chrome.tabs.sendMessage in the background.js example, to show where this event should be triggered.
If you need to identify which element triggered the event, don't use a predefined function (in a dictionary) as shown in my example, but an inline function or a factory function. To identify the element, a message needs to be paired with an unique identifier. I'll leave the task of creating this implementation to the reader (it's not difficult).
Till date
"all", "page", "frame", "selection", "link", "editable", "image", "video", "audio", "launcher" are the contexts only supported. It is not possible to customize per class of an Object.
Work Around
If you are looking to show context menu actions, only when the user right-clicks on certain div.class on the page use mouse over menu as shown here
I'm developing a Chrome extension which sends the highlighted selection to a speech engine API. I want to implement both context menu and on icon click. Here's the problem:
This works perfectly:
chrome.contextMenus.create({
"title" : "Speak Me",
"contexts" : ["selection"],
onclick: function (info, tab) {
speakMe(info.selectionText);
}
});
Directly underneath it I have:
chrome.browserAction.onClicked.addListener(function() {
speakMe(info.selectionText);
});
Which doesn't work.
If I leave the parameter empty it returns an audio saying "Undefined". So I guess the speech engine is telling me it got no text. What am I doing wrong?
This is the function in question, placed above:
var speakMe = function (text) {
var key, lang, url, audio;
key = "key=12345678910";
lang = "sv_se";
url = "http://tts.engine.com/api/speak?" + key + "&lang=en_us&voice=male1&speed=100&audioformat=ogg&oggbitrate=100&text=" + text;
audio = new Audio(url);
audio.play();
};
The selection text comes from another JS file:
function getSelectedText() {
var text = "";
if (typeof window.getSelection != "undefined") {
text = window.getSelection().toString();
} else if (typeof document.selection != "undefined" && document.selection.type == "Text") {
text = document.selection.createRange().text;
}
return text;
}
But since the context menu works perfectly, I don't think there's a problem with that. It's just the browserAction that I don't get how to use properly.
Because the browser action's onClicked event doesn't have any information about the selected text. You need to figure it out yourself. You can inject a content script into the current page and get the selected text with window.getSelection().toString(). Then send the message back to the extension and speak the text.
Here's an example:
chrome.browserAction.onClicked.addListener(function(tab) {
chrome.tabs.executeScript(tab.id,
{code: 'window.getSelection().toString()'}, function(results) {
alert(results[0]);
});
});
It just shows an alert, but it's very easy to change it to call speakMe.
Is there a way to show context menu actions, only when the user right-clicks on classes that start with "story".
For example: if the user right-clicks on an object in the page of class "story ....", the context menu buttons should appear, otherwise nothing should happen.
Here is my code (though it does not work):
var divs = document.querySelectorAll("[class^=story]"); //get all classes that start with "Story"
window.oncontextmenu = function() {
for(var i=0; i < divs.length; i++)
{
divs[i].onclick = function() {
chrome.contextMenus.create
(
{"id": "butto1", "title": "1", "contexts":["all"], "onclick": genericOnClick}
);
chrome.contextMenus.create
(
{"id": "button2", "title": "2", "contexts":["all"], "onclick": genericOnClick}
);
chrome.contextMenus.create
(
{"id": "button3", "title": "3", "contexts":["all"], "onclick": genericOnClick}
);
};
}
return true;
};
function genericOnClick(info, tab) {
//do some crap here
chrome.contextMenus.removeAll();
}
In this related answer, I explained that context menu items cannot be created on the fly, because the time between a contextmenu event and the appearance of the context menu item is not sufficient to get a chrome.contextMenus.create call in between.
The other answer explains how to make sure that the context menu entry shows the selected text. This was done by listening to the selectionchange event. For your purpose, we want to use an event which has the desired timing.
I'm going to use the mouseover and mouseout events. By depending on mouse events, the context menu will not work when you use the keyboard, e.g. by focusing an element using JavaScript or the Tab key, followed by pressing the context menu key. I did not implement it in the solution below.
The demo consists of three files (plus an HTML page to test it). I put all files in a zip file, available at https://robwu.nl/contextmenu-dom.zip.
manifest.json
Every Chrome extension requires this file in order to work. For this application, a background page and content script is used. In addition, the contextMenus permission is required.
{
"name": "Contextmenu based on activated element",
"description": "Demo for https://stackoverflow.com/q/14829677",
"version": "1",
"manifest_version": 2,
"background": {
"scripts": ["background.js"]
},
"content_scripts": [{
"run_at": "document_idle",
"js": ["contentscript.js"],
"matches": ["<all_urls>"]
}],
"permissions": [
"contextMenus"
]
}
background.js
The background page will create the context menus on behalf of the content script. This is achieved by binding an event listener to chrome.runtime.onConnect, which offers a simple interface to replace context menu entries.
chrome.contextMenus.create is called whenever a message is received over this port (from the content script). All properties, except for onclick are JSON-serializable, so only the "onclick" handler needs a special treatment. I've used a string to identify a pre-defined function in a dictionary (var clickHandlers).
var lastTabId;
// Remove context menus for a given tab, if needed
function removeContextMenus(tabId) {
if (lastTabId === tabId) chrome.contextMenus.removeAll();
}
// chrome.contextMenus onclick handlers:
var clickHandlers = {
'example': function(info, tab) {
// This event handler receives two arguments, as defined at
// https://developer.chrome.com/extensions/contextMenus#property-onClicked-callback
// Example: Notify the tab's content script of something
// chrome.tabs.sendMessage(tab.id, ...some JSON-serializable data... );
// Example: Remove contextmenus for context
removeContextMenus(tab.id);
}
};
chrome.runtime.onConnect.addListener(function(port) {
if (!port.sender.tab || port.name != 'contextMenus') {
// Unexpected / unknown port, do not interfere with it
return;
}
var tabId = port.sender.tab.id;
port.onDisconnect.addListener(function() {
removeContextMenus(tabId);
});
// Whenever a message is posted, expect that it's identical to type
// createProperties of chrome.contextMenus.create, except for onclick.
// "onclick" should be a string which maps to a predefined function
port.onMessage.addListener(function(newEntries) {
chrome.contextMenus.removeAll(function() {
for (var i=0; i<newEntries.length; i++) {
var createProperties = newEntries[i];
createProperties.onclick = clickHandlers[createProperties.onclick];
chrome.contextMenus.create(createProperties);
}
});
});
});
// When a tab is removed, check if it added any context menu entries. If so, remove it
chrome.tabs.onRemoved.addListener(removeContextMenus);
contentscript.js
The first part of this script creates simple methods for creating context menus.
In the last 5 lines, the context menu entries are defined and bound to all current and future elements which match the given selector. Like I said in the previous section, the argument type is identical to the createProperties argument of chrome.contextMenus.create except for "onclick", which is a string which maps to a function in the background page.
// Port management
var _port;
var getPort = function() {
if (_port) return _port;
_port = chrome.runtime.connect({name: 'contextMenus'});
_port.onDisconnect.addListener(function() {
_port = null;
});
return _port;
}
// listOfCreateProperties is an array of createProperties, which is defined at
// https://developer.chrome.com/extensions/contextMenus#method-create
// with a single exception: "onclick" is a string which corresponds to a function
// at the background page. (Functions are not JSON-serializable, hence this approach)
function addContextMenuTo(selector, listOfCreateProperties) {
// Selector used to match an element. Match if an element, or its child is hovered
selector = selector + ', ' + selector + ' *';
var matches;
['matches', 'webkitMatchesSelector', 'webkitMatches', 'matchesSelector'].some(function(m) {
if (m in document.documentElement) {
matches = m;
return true;
}
});
// Bind a single mouseover+mouseout event to catch hovers over all current and future elements.
var isHovering = false;
document.addEventListener('mouseover', function(event) {
if (event.target && event.target[matches](selector)) {
getPort().postMessage(listOfCreateProperties);
isHovering = true;
} else if(isHovering) {
getPort().postMessage([]);
isHovering = false;
}
});
document.addEventListener('mouseout', function(event) {
if (isHovering && (!event.target || !event.target[matches](selector))) {
getPort().postMessage([]);
isHovering = false;
}
});
}
// Example: Bind the context menus to the elements which contain a class attribute starts with "story"
addContextMenuTo('[class^=story]', [
{"id": "butto1", "title": "1", "contexts":["all"], "onclick": 'example'},
{"id": "button2", "title": "2", "contexts":["all"], "onclick": 'example'},
{"id": "button3", "title": "3", "contexts":["all"], "onclick": 'example'}
]);
The previous code assumes that all context menu clicks are handled by the background page. If you want to handle the logic in the content script instead, you need to bind message events in the content script. I've shown an (commented) instance of chrome.tabs.sendMessage in the background.js example, to show where this event should be triggered.
If you need to identify which element triggered the event, don't use a predefined function (in a dictionary) as shown in my example, but an inline function or a factory function. To identify the element, a message needs to be paired with an unique identifier. I'll leave the task of creating this implementation to the reader (it's not difficult).
Till date
"all", "page", "frame", "selection", "link", "editable", "image", "video", "audio", "launcher" are the contexts only supported. It is not possible to customize per class of an Object.
Work Around
If you are looking to show context menu actions, only when the user right-clicks on certain div.class on the page use mouse over menu as shown here