How to get iframeId in a contextMenus onclick event handler? - javascript

I am developing a Mozilla WebExtension. I want to inject a JavaScript file only into the frame in which I have clicked on the context menu selection created with contextMenus.create().
I am using:
browser.contextMenus.create({
"title": "Records",
"contexts": ["all","link"],
"id" : "M1",
"onclick": onClickContextMenu,
}, function() { });
function onClickContextMenu(info, tab){
var clickedFrameID=""; //How do I get the actual frameId where click occurred?
browser.tabs.executeScript(tab.id,{
file: "fileName.js",
allFrames: false,
frameId: clickedFrameID
}, function() {
console.log("Script injected");
});
}
How to get clickedFrameID?

After Making so many testing me (Chandrakant Thakkar) and my team leader Mr. Nitin Makwana got the solution for this situation,
First I have injected "messageListener.js" file in all frames from manifest.json file as
"content_scripts": [
{
"matches": ["https://*/*","http://*/*"],
"css":["jquery-ui.css"],
"js": [
"jquery.js",
"jquery-ui.js",
"content_scripts/msg_listener.js"
],
"all_frames":true
}
Then in "messageListener.js" file created "contextMenu" Listener and send message to Background js file when I click contextMenu (Right Click Of Mouse)
as
document.addEventListener("contextmenu", handleContextMenu, false);
function handleContextMenu(event){
browser.runtime.sendMessage("abc#gmail.com",{type:
"onContextMenuClicked", sender:event });
}
here, "abc#gmail.com" is id of my web extension where I want to send message.
Then, In my background js file I have declared on global variable named "clickedFrameID" and in same file I have added onMessage Listener as below
browser.runtime.onMessage.addListener(
function(msg, sender, callback) {
if(msg.type === 'onContextMenuClicked')
{
if(sender.hasOwnProperty("frameId")){
clickedFrameID=sender.frameId;
}else{
clickedFrameID="";
}
}
});
Now I have injected "fileName.js" file in specific Frame as Below
var clickedFrameID=""; //This is a Global Variable
browser.contextMenus.create({
"title": "Records",
"contexts": ["all","link"],
"id" : "M1",
"onclick": onClickContextMenu,
}, function() { });
function onClickContextMenu(info, tab){
var options={};
if(clickedFrameID !="" && clickedFrameID!=null &&
clickedFrameID!=undefined )
{
options={
file: "fileName.js",
allFrames: false,
frameId: clickedFrameID
};
}
browser.tabs.executeScript(tab.id,options, function() {
console.log("Script injected");
});
}
Now "fileName.js" will be injected in specific frame in that I have right clicked.
Thanks #wOxxOm, #MohammedAshrafali, #Makyen to take interest

Related

Context Menu array?

I have written a simple extension for a company website that reads the array of projects, and alerts the title of the project when I hover over it. This works perfectly, but it is not what I'm looking for:
let projects = document.getElementsByClassName("project-name");
extract_data = function() {
let title = this.getElementsByClassName("project-name ng-binding")[0].getAttribute("title")
alert(title);
};
for (var i = 0; i < projects.length; i++) {
projects[i].addEventListener('mouseover', extract_data, false);
}
I would like to slightly modify my code so that instead of alerting on mouseover, the alert is added as an option to the Chrome Context Menus. Something similar to:
chrome.contextMenus.create({
title: "Alert Project Title",
contexts:["selection"], // ContextType
onclick: extract_data // A callback function
});
This is not quite right because I need to change the context menu to be dependent on the project row, and as this is written now, there is no reference to projects for the Context Menu.
Is there any easy way to change my mouseover to become the value of the contextMenu?
Perhaps I was too hasty.
If you want that the menù displays an items with the TITLE of selected project (not selected text)
and if you want to move ALERT as single contextMenu item onclick event
I think you HAVE A CHANCE!
But you have to inject a content script declaratively
/* in manifest.json */
...
"content_scripts": [{
"matches": ["<all_urls>"],
"js": ["cs.js"],
"run_at": "document_idle"
}]
...
/* in content script "cs.js" */
function extract_data() {
var title = this.getElementsByClassName("project-name ng-binding")[0].getAttribute("title");
chrome.runtime.sendMessage({'title': title}, resp =>
console.log('backgroung script has received the message and send back this message: ' + resp.msg)
)
}
var projects = document.getElementsByClassName("project-name");
for (var i = 0; i < projects.length; i++)
projects[i].addEventListener('mouseover', extract_data, false)
/* in background script */
function createMenu() {
chrome.contextMenus.create({
'id': '1',
'title': 'Alert Project Title',
'contexts': ["selection"],
'documentUrlPatterns': ["<all_urls>"],
'enabled': false
}, _ => {
if (chrome.runtime.lastError) {} else {}
})
}
function handleInstalled(details) {
createMenu()
}
chrome.runtime.onInstalled.addListener(handleInstalled);
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
chrome.contextMenus.update(
'1',
{
"title": "Do you want to alert " + msg.title + " project title",
'contexts': ["selection"],
'documentUrlPatterns': ["<all_urls>"],
"enabled": true,
"onclick": _ => alert(msg.title)
},
_ => {
sendResponse( {'msg': chrome.runtime.lastError ? 'Some error...' : 'Fine!' } )
}
)
return true
})
If you want that the menù displays an items with the title of selected project I think you are out of luck. I don't believe there is a way to do it.
But If you want the menù displays an items with your selection try this.
chrome.contextMenus.create({
"id": "1",
title: "Do you want to alert '%s' project title",
"contexts": ["selection"],
"documentUrlPatterns": [...],
onclick: extract_data // A callback function
})

chrome.contextmenus how to get html instead of string

I'm new on chrome extension,
I'm creating a basic extension, thats will show the selected html with tags (spans, divs and all other DOMs) on an alert
I try this chrome.contextMenus.create (code below) but it showing just the selected string
chrome.contextMenus.create({title: "Selected HTML", contexts:["selection"], onclick: showSelectedHtml});
I just want to know if theres a way to get selected html doms(with tags if available) instead of string!
Thanks;
As you can see in the documentation contextMenus API can't do that.
The solution is to run a content script that gets the HTML and returns it into your background script.
manifest.json:
"permissions": ["activeTab", "contextMenus"],
"background": {
"scripts": ["background.js"],
"persistent": false
}
background.js:
// in Chrome the menus should be created only at install/update
chrome.runtime.onInstalled.addListener(() => {
chrome.contextMenus.create({
id: 'sel',
title: 'Selected HTML',
contexts: ['selection'],
}, () => chrome.runtime.lastError); // ignore errors about an existing id
});
chrome.contextMenus.onClicked.addListener((info, tab) => {
chrome.tabs.executeScript(tab.id, {
code: `(${getSelectedHtml})()`,
frameId: info.frameId,
matchAboutBlank: true,
runAt: 'document_start',
}, data => {
if (chrome.runtime.lastError) {
alert('Error: ' + chrome.runtime.lastError.message);
} else {
prompt('HTML', data[0]);
}
});
});
// this function's code will be executed as a content script in the web page
function getSelectedHtml() {
const sel = getSelection();
let html;
if (sel.rangeCount) {
const div = document.createElement('div');
div.appendChild(sel.getRangeAt(0).cloneContents());
html = div.innerHTML;
}
return html || '';
}
Note: don't use the onclick property in contextMenus.create() because it doesn't work with "persistent":false background script, which is the preferred type (more info).

Chrome plugin message passing callback undefined

I managed to pass a message between two files/instances, namely my popup and my background scripts, but the callback function still returns undefined, so my popup script does not receive a reply. Can somebody assist me? My code is based on the docs from Google.
Background.js
chrome.runtime.onMessage.addListener(function (sender, sendResponse) {
var resp = {'navURL': "Not set yet"};
if (sender.greeting === "GetURL") {
sendResponse(resp);
}
});
popup.js
function getURL() {
chrome.runtime.sendMessage({
greeting: "GetURL"
},
function (response) {
// RESPONSE IS UNDEFINED //
console.log(response);
alert(response.navURL);
});
}
$("input[type='checkbox']").on('change', function () {
getURL();
});
Manifest.json
{
"name": "FaceBlock",
"description": "This extention gets rid of unwanted content on Facebook like sponsored posts, adds or annoying suggestions. The content you wish to see is enlarged for a better and richer social experience.",
"version": "0.0.1",
"manifest_version": 2,
"content_scripts": [
{
"matches": [
"http://*.facebook.com/*", "https://*.facebook.com/*"],
"css": ["css/popup.css"],
"js": ["js/jquery.min.js", "js/content.js"],
"run_at": "document_end",
"all_frames": true
}],
"options_page": "options.html",
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html",
"default_title": "Faceblock"
},
"permissions": [
"activeTab",
"tabs",
"https://www.facebook.com/*",
"https://ajax.googleapis.com/",
"storage"
],
"background": {
"scripts": ["js/background.js"],
"persistent": false
},
"options_ui": {
// Required.
"page": "popup.html",
// Recommended.
"chrome_style": true
// Not recommended; only provided for backwards compatibility,
// and will be unsupported in a future version of Chrome (TBD).
//"open_in_tab": true
},
"web_accessible_resources": [
"images/faceblock.jpg",
"images/seigaiha.png",
"js/popup.js",
"icon.png",
"js/options.js",
"css/popup.css",
"popup.html",
"options.html"
]
}
Thanks in advance,
Niels Vermeiren
EDIT: I now have the following with the same problem
Background.js
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
sendResponse({msg: "Element " + sender.element + " zijn zichtbaarheid wordt nu: " + sender.checked});
return true;
});
Popup.js
function getCurrentTabUrl(callback) {
return new Promise(function (resolve, reject) {
// https://developer.chrome.com/extensions/tabs#method-query
var queryInfo = {
active: true,
currentWindow: true
};
chrome.tabs.query(queryInfo, function (tabs) {
if (callback(tabs[0].url)) {
return resolve();
} else {
return reject();
}
});
});
}
function changeFacebook(data) {
console.log(data);
chrome.runtime.sendMessage(
"changeFacebook",
data,
function (response //undefined) {
console.log(response.msg); // UNDEFINED
});
}
document.addEventListener('DOMContentLoaded', function () {
var callback = function getCurrentTab(tab) {
if (tab == "https://www.facebook.com/") {
return true;
} else {
return false;
}
}
getCurrentTabUrl(callback).then(function () {
alert('on facebook');
$('.section').hide();
}, function () {
alert('not on facebook');
});
$("input[type='checkbox']").on('change', function () {
var id = $(this).attr('id');
var check = this.checked;
var data = {
'element': id,
'checked': check
};
changeFacebook(data);
});
$('.menuItem').hide();
$('.menuItem:first').show();
jQuery('.navitem').on('click', function () {
var value = $(this).html().toLocaleLowerCase();
jQuery('.menuItem').hide();
jQuery('#' + value).show();
});
});
According to doc here:
https://developer.chrome.com/extensions/runtime#event-onMessage
In your background.js, it should be
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
...
});
Since you are missing the last argument, what you really have is this:
chrome.runtime.onMessage.addListener(function (request, sender) {
...
});
So your code pretty much renamed request => sender, sender => sendResponse.
Therefore, you are trying to invoke sender as a function. But the actual sendResponse callback function is undefined. If you inspect your background page (see tips for how), you should see the error.
Tips:
When developing chrome extension, you can Inspect views: background page in the chrome://extensions page if you have any background page running.
When your popup is opened, you can inspect the popup page just you normally do.
And you can throw debuggers wherever you want, and you will be able to play around with it.
Edit:
So I have tested your code, the issue is just missing argument as I stated above. I also noticed the doc says the first two arguments are optional (not sure what the fuzz that is). But if you change your code to as below, it will work.
Background.js
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
var resp = {'navURL': "Not set yet"};
if (request.greeting === "GetURL") {
sendResponse(resp);
}
});
Popup.js
function getURL() {
chrome.runtime.sendMessage({
greeting: "GetURL"
},
function (response) {
console.log(response);
alert(response.navURL);
});
}
To test it
Go to the background page inspect, and paste in the background page snippet.
Go to the popup page inspect (open your popup, and right click within the popup, then choose inspect)
Paste in the popup page snippet
In the popup page inspect, call getURL()

Content.js script never called to count table rows and refresh page

I am making a chrome extension with two buttons : Start and Stop. On click of start button the html page, should start refreshing after a particular timer, and on click of stop it should stop refreshing.
The HTML page has a table in it say with id myTable.So after every refresh, I want to have row count of this table.
To get this I did something like this :
First for pop up, I made popup.js
window.onload = function() {
document.getElementById("startbutton").onclick = function() {
chrome.extension.sendMessage({
type: "table-row-count_start"
});
}
document.getElementById("stopbutton").onclick = function() {
chrome.extension.sendMessage({
type: "table-row-count_stop"
});
}
}
In background.js
chrome.extension.onMessage.addListener(function(request, sender, sendResponse) {
switch(request.type) {
case "table-row-count_start":
alert("Refershing started");
RefreshAndCount();
break;
case "table-row-count_stop":
alert("Stop Refershing");
break;
}
return true;
});
var RefreshAndCount = function(){
chrome.tabs.getSelected(null, function(tab){
chrome.tabs.sendMessage(tab.id, {type: "table-row-count"});
chrome.browserAction.setBadgeText({text: "Counting!"});
});
}
This will make call to content.js as we need to interact with DOM of HTML page. In this script I did something like this :
chrome.extension.onMessage.addListener(function(message, sender, sendResponse) {
switch(message.type) {
case "table-row-count":
var x = document.getElementById("myTable").rows.length;
alert("Row count = " + x);
var Refresh = confirm("Do you want to refresh the page?");
if (Refresh)
{
setTimeout("location.reload(true);",5000);
}
break;
}
});
The script content.js is never called. I don't know why so. Please help.
Also it will be refreshing only once, how to keep refershing after fixed timer.
Here is manifest.json
{
"name": "Refresher",
"version": "0.0.1",
"manifest_version": 2,
"description" : "Refresh the site contents after a while",
"icons": { "32": "icon.jpg" },
"browser_action": {
"default_icon": { "32": "icon.jpg" },
"default_title": "Refersher",
"default_popup": "browseraction/popup.html"
},
"background": {
"scripts": ["background.js"],
"persistent": true
},
"content_scripts": [{
"matches": ["http://www.w3schools.com/html/tryit.asp?filename=tryhtml_table"],
"js": ["content.js"]
}]
}
In this case you've used a deprecated chrome.tabs.getSelected which returned the wrong tab with id -1 so the message was never delivered and an error was displayed in the console.
Use chrome.tabs.query:
var RefreshAndCount = function() {
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {type: "table-row-count"});
chrome.browserAction.setBadgeText({tabId: tabs[0].id, text: "Counting!"});
});
};
Also note how setBadgeText was used just for the specified tab.
N.B. Always, always use the debugger to check what's wrong. Thus you can stop using alert and start using console.log or inspect/watch the variables in the debugger.

Need to send message from a context menu to a content script

I am writing a small extension that allows me to easy submit links to reddit.
This extensions adds a new context menu ('Submit Page'). If the users right clicks and selects this menu the www.redddit.com/submit page is opened in another tab, and the page from where the menu was triggered is submitted.
I've added the context menus:
contextMenu.js
// Setup where the menu is presents;
// A list of [context, context menu text, id]
var redditURL = 'http://www.reddit.com/submit';
var contexts = [["page", "Submit page", "id-submitPage"], ["link", "Submit link", "id-submitLink"], ["editable", "Submit text", "id-submitText"], ["image", "Submit image", "id-submitImage"]];
// Add all menus to their context
contexts.forEach(function(element) {
chrome.contextMenus.create({
"title" : element[1],
"contexts" : [element[0]],
"id" : element[2]
});
});
// Add actions to menus
chrome.contextMenus.onClicked.addListener(function(info, tab) {
var submittedURL = tab && tab.url;
if (info["menuItemId"] == "id-submitPage") {
chrome.tabs.create({
"url" : redditURL
}, function(tab) {
// After we create the tab we also send a message to the content
// script associated with the page to intercept our info
console.log(submittedURL);
chrome.tabs.sendMessage(tab.id, {
"url" : submittedURL,
"type" : "submitPage"
});
});
}
});
As you can in see in the addListener I am using chrome.tabs.sendMessage to send the URL I am submitting to the content script associated with: redditURL .
The content script: contextMenu-RedditSubmit.js
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
alert('here');
console.log(sender.tab ? "from a content script:" + sender.tab.url : "from the extension");
});
And the manifest file:
...
"background": {
"scripts": ["contextMenu.js"],
"persistent": false
},
"content_scripts": [
{
"matches": ["http://www.reddit.com/submit"],
"js": ["contextMenu-RedditSubmit.js"],
"run_at": "document_start"
}
],
...
Problem is my messages are not received inside the contextMenu-RedditSubmit.js content script. I cannot see neither the console.log nor the alert. Any tips ?
Your message is sent before event document_start content scripts execute.
To ensure it works, switch to using programmatic injection:
chrome.tabs.create({
"url" : redditURL
}, function(tab) {
// After we create the tab we also send a message to the content
// script associated with the page to intercept our info
chrome.tabs.executeScript(
tab.id,
{file: "contextMenu-RedditSubmit.js"},
function() {
// Here, it is guaranteed that the script finished executing
// (or there was an error)
chrome.tabs.sendMessage(tab.id, {
"url" : submittedURL,
"type" : "submitPage"
});
}
);
});

Categories