Google Chrome Extension - Get DIV and Other Info Near Clicked Element - google-chrome

My Google Chrome Extension currently retrieves the ImageURL which was right-clicked from the menu, but that's not enough. I also need to do this:
Get the DIV Element inside which this specific Image was clicked.
Get some text strings stored inside this DIV, or in sub-DIVs, next
to the clicked image.
So far I have this, which is working:
background.js
// Add context menu for Images
chrome.contextMenus.create({
"title": "Get this Image",
"contexts": ["image"],
"onclick" : getInfo
});
function getInfo(e)
{
var imgURL = e.srcUrl;
// Set up an event which Content will receive
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {url: imgURL}, function(response) {
alert('Response in Background');
});
});
}
contentscript.js
// Listener for Background Event Sends
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
// print Image Src URL in a custom Content-created place
divHTML = request.url;
document.getElementById('infoDiv').innerHTML = divHTML;
});
Where do I do the extra work to analyze the DOM tree and retrieve extra surrounding elements? In the Background or in the Content?

Related

How to read database from chrome extension?

I am working on a dictionary extension where I will get the word and meaning. Here is the expected output.
User selects the text from DOM.
extension should show the meaning of the word in a pop-up like a google dictionary.
I am not using any API instead I wanted to look for words from an SQL database(dictionarydb.sqlite 5.8mb).
Question: is it possible to add a pre-build database within the extension? if so how can we load and search for a word?
Here is the snippet of content.js
//get the selected word from the document and send it to the background js
document.addEventListener("mouseup", (event) => {
if (window.getSelection().toString().length) {
let exactText = window.getSelection().toString();
chrome.runtime.sendMessage({
selectedText: exactText,
});
}
});
//get the word meaning from background js
chrome.runtime.onMessage.addListener(function (msg) {
if (msg.from == "background") {
var wordMeaning = msg.meaning;
alert("Meaning: " + wordMeaning);
}
});
In background.js,
//get selected word from content.js
chrome.runtime.onMessage.addListener(function (request, sender) {
var text = request.selectedText;
var textMeaning;
/*
textMeaning = get Meaning from dictionary
*/
//send word meaning to content.js
chrome.tabs.sendMessage(sender.tab.id, {
from: "background",
meaning: textMeaning,
});
});
Thanks in advance

Can't get the selected text / highlighted text from the tab in my chrome extension

I am developing a chrome extension. I am trying to get the selected/highlighted text from the active tab and do something with it. For now, say all the extension does is writing what the selected text is on a popup. I can't seem to do it. I tried a lot of methods. content script, background scripts. Nothing works. in my manifest.jsonI have permissions for activeTab, contextMenus. I tried multiple functions that take the selected text but nothing works. Example of some functions
const text = (window.getSelection) ?
window.getSelection().toString() :
document.selection.createRange().text;
console.log(text)
chrome.contextMenus.create({
id: 'selectionGetter',
title: 'send selected text',
contexts: ['selection'],
});
chrome.browserAction.onClicked.addListener(function(tab) {
chrome.tabs.sendRequest(tab.id, {method: "getSelection"},
function(response){
const url=response.url;
const subject=response.subject;
const body = response.body;
console.log(body)
});
});
chrome.browserAction.onClicked.addListener(function(tab) {
chrome.tabs.sendRequest(tab.id, {method: "getSelection"},
function(response){
sendServiceRequest(response.data);
});
});
Would love your help
See How to access the webpage DOM rather than the extension page DOM? for full explanation, here's a quick illustration:
chrome.tabs.executeScript({code: 'getSelection().toString()'}, ([sel] = []) => {
if (!chrome.runtime.lastError) {
document.body.textContent = 'Selection: ' + sel;
}
});
If you have default_popup declared in manifest.json then put this code in popup.js, and put <script src=popup.js></script> in popup.html. It will run each time the popup is shown.
Otherwise put it inside chrome.browserAction.onClicked listener in the background script.

Chrome extension onConnect listener fires more than once from different tabs

I'm creating an extension that works with this structure of .js files:
Page A (content script) -> B (background script) -> Page C (content script)
On page A, there are links that, when clicked, send a message to B. The idea of B is to open up a new tab and load page C; when C is fully loaded, it'll send a message back to B, which will send some data to C to fill in a form.
This all works, but after putting in some logging I realized that my listeners in B are actually firing twice if more than one link is clicked from page A. Here's what the code looks like:
Page A:
$(document).ready(function () {
$('.click-test').click(function() {
chrome.runtime.sendMessage({formData: here}, function(response) {
});
});
});
B:
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
chrome.runtime.onConnect.addListener(function(port) {
console.assert(port.name == "formStatus");
port.onMessage.addListener(function(msg) {
if (msg.status == "formReady") {
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {formData: here}, function(response) {
// The below is being called twice incorrectly if two links from page A are clicked
console.log("sent message for tab ID " + tabs[0].id);
});
});
}
});
});
chrome.tabs.create({url: requestFormUrl});
});
Page C:
$(document).ready(function() {
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
// Fill in form with request data
});
var port = chrome.runtime.connect({name: "formStatus"});
port.postMessage({status: "formReady"});
});
When the first link on page A is clicked, things work fine. When the second link is clicked, the onConnect listener in B fires twice: first with the same data as the first click, then with the correct data corresponding to the second click.
Is there some way to disable B's listener as soon as it fires once? Or, have I just made this code way too complicated?
Rob W. is right in the comments - removing the listener as he did within the addListener part worked. Thanks!

pass dom element from background script to chrome.tabs.executeScript

I'm trying to pass the active dom element when the contextmenu is clicked from my background script to a script that is being called through chrome.tabs.executeScript. I can pass booleans and strings just fine, but i always get an error when i pass dom elements. I'm starting to think it's not possible.
//doScripts function called from browser action
chrome.browserAction.onClicked.addListener(function(tab) {
doScripts(true, null);
});
//doScripts function called from context menu click
function getClickHandler(info, tab) {
var currTarg = document.activeElement;
console.log("currTarg = " + currTarg);
doScripts(false, currTarg);
}
//i reference doingBrowserAction and contextTarg in myscript.js
function doScripts(context, targ){
chrome.tabs.executeScript(null, {code: "var doingBrowserAction = "+context+"; var contextTarg = "+targ+";"}, function(){
chrome.tabs.executeScript(null, {file: "js/myscript.js"}, function(){
//all injected
});
});
}
//setup context menu
chrome.contextMenus.create({
"title" : "DESTROY!",
"type" : "normal",
"contexts" : ["page","selection","link","editable","image","video","audio"],
"onclick" : getClickHandler
});
i reference doingBrowserAction and contextTarg in myscript.js. I know what i'm trying to do is possible because the adblock extension does it, but having a hard time figuring out how. thanks in advance.
You cannot get a direct reference to a content script's DOM element from the background page, because the background page runs in the extension's process, and the content script runs in the tab's process. See also https://code.google.com/p/chromium/issues/detail?id=39507.
The document.activeElement property in the background page refers to the active element in the background page's document. As you can imagine, this value is quite useless.
If you query the state of the currently right-clicked element, bind an event in the content script. In the next example, I've chosen the contextmenu event, because context menus can also be opened through the keyboard.
This example adds a context menu option that removes the last active element from the document.
// content script
var lastElementContext;
document.addEventListener('contextmenu', function(event) {
lastElementContext = event.target;
}, true);
chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {
if (lastElementContext && lastElementContext.parentNode) {
lastElementContext.parentNode.removeChild(lastElementContext);
lastElementContext = null;
}
});
Background script:
chrome.contextMenus.create({
title: 'DESTROY!',
contexts: ['page', 'link', 'editable', 'image', 'video', 'audio'],
onclick: function(info, tab) {
chrome.tabs.sendMessage(tab.id, 'doDestroy');
}
});

Chrome Extension: how to capture selected text and send to a web service

For the Google Chrome extension, I need to capture selected text in a web page and send to a web service. I'm stuck!
First I tried a bookmarklet, but Chrome on Mac seems to have some bookmarklet bugs so I decided to write an extension.
I use this code in my ext:
function getSelText(){
var txt = 'nothing';
if (window.getSelection){
txt = "1" + window.getSelection();
} else if (document.getSelection) {
txt = "2" + document.getSelection();
} else if (document.selection) {
txt = "3" + document.selection.createRange().text;
} else txt = "wtf";
return txt;
}
var selection = getSelText();
alert("selection = " + selection);
When I click on my extension icon, I get a "1". So I think the act of selecting outside the browser window is causing the text to not be seen by the browser as "selected" any more.
Just a theory....
thoughts?
You can do this by using Extensions Messaging. Basically, your "background page" will send the request to your service. For example, lets say you have a "popup" and once you click on it, it will do a "Google search" which is your service.
content_script.js
In your content script, we need to listen for a request coming from your extension, so that we send it the selected text:
chrome.extension.onRequest.addListener(function(request, sender, sendResponse) {
if (request.method == "getSelection")
sendResponse({data: window.getSelection().toString()});
else
sendResponse({}); // snub them.
});
background.html
Now in background page you can handle the popup onclick event so that we know we clicked on the popup. Once we clicked on it, the callback fires, and then we can send a request to the content script using "Messaging" to fetch the selected text.
chrome.browserAction.onClicked.addListener(function(tab) {
chrome.tabs.sendRequest(tab.id, {method: "getSelection"}, function(response){
sendServiceRequest(response.data);
});
});
function sendServiceRequest(selectedText) {
var serviceCall = 'http://www.google.com/search?q=' + selectedText;
chrome.tabs.create({url: serviceCall});
}
As you have seen, I registered a listener in a content script to allow my extension to send and receive messages from it. Then once I received a message, I handle it by searching for Google.
Hopefully, you can use what I explained above and apply it to your scenario. I just have to warn you that the code written above is not tested, so their might be spelling, or syntax errors. But those can easily be found by looking at your Inspector :)
content script
document.addEventListener('mouseup',function(event)
{
var sel = window.getSelection().toString();
if(sel.length)
chrome.extension.sendRequest({'message':'setText','data': sel},function(response){})
})
Background Page
<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); }}
}
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>
manifest.json
{
"name": "Word Reminder",
"version": "1.0",
"description": "Word Reminder.",
"browser_action": {
"default_icon": "images/stick-man1.gif",
"popup":"popup.html"
},
"background_page": "background.html",
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["js/myscript.js"]
}
],
"permissions": [
"http://*/*",
"https://*/*",
"contextMenus",
"tabs"
]
}
and here is the link where i have all in one extension to download.
after reading this i tried of my own and have published.
and here is the complete source
http://vikku.info/programming/chrome-extension/get-selected-text-send-to-web-server-in-chrome-extension-communicate-between-content-script-and-background-page.htm
Enjoy
Using a content_scripts is not a great solution as it injection to all documents including iframe-ads etc. I get an empty text selection from other pages than the one I expect half the times on messy web sites.
A better solution is to inject code into the selected tab only, as this is where your selected text lives anyhow. Example of jquery doc ready section:
$(document).ready(function() {
// set up an event listener that triggers when chrome.extension.sendRequest is fired.
chrome.extension.onRequest.addListener(
function(request, sender, sendResponse) {
// text selection is stored in request.selection
$('#text').val( request.selection );
});
// inject javascript into DOM of selected window and tab.
// injected code send a message (with selected text) back to the plugin using chrome.extension.sendRequest
chrome.tabs.executeScript(null, {code: "chrome.extension.sendRequest({selection: window.getSelection().toString() });"});
});
It is not clear from your code where it is. What I mean, is that if this code is either in popup html or background html then the results you are seeing are correct, nothing in those windows will be selected.
You will need to place this code in a content script so that it has access to the DOM of the page, and then when you click your browser action, you will need to send a message to the content script to fetch the current document selection.
You don't need a Google API for something as simple as this...
I'll use the Bing online service as an example. Note that the URL is set up to accept a parameter:
var WebService='http://www.bing.com/translator/?text=';
frameID.contentWindow.document.body.addEventListener('contextmenu',function(e){
T=frameID.contentWindow.getSelection().toString();
if(T!==''){e.preventDefault(); Open_New_Tab(WebService+encodeURIComponent(T)); return false;}
},false);
NB: The function "Open_New_Tab()" used above is an imaginary one that accepts the webservice URL with the encoded selected text as a parameter.
That's the idea basically.