How to read database from chrome extension? - google-chrome

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

Related

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.omnibox ceases working after period of time. Begins working after restarting extension

I'm leveraging Google Chrome's omnibox API in my extension.
Current users, including myself, have noticed that the omnibox ceases responding entirely after an undetermined state change or a period of time lapsing. Typing the word to trigger entering into "omnibox" stops having any effect and the URL bar does not shift into omnibox mode.
Restarting Google Chrome does not fix the issue, but restarting my plugin by unchecking and then re-checking the 'enabled' checkbox on chrome://extensions does resolve the issue.
Does anyone have any suggestions on what to investigate? Below is the code used. It is only loaded once through my permanently persisted background page:
// Displays streamus search suggestions and allows instant playing in the stream
define([
'background/collection/streamItems',
'background/model/video',
'common/model/youTubeV2API',
'common/model/utility'
], function (StreamItems, Video, YouTubeV2API, Utility) {
'use strict';
console.log("Omnibox LOADED", chrome.omnibox);
var Omnibox = Backbone.Model.extend({
defaults: function () {
return {
suggestedVideos: [],
searchJqXhr: null
};
},
initialize: function () {
console.log("Omnibox INITIALIZED");
var self = this;
chrome.omnibox.setDefaultSuggestion({
// TODO: i18n
description: 'Press enter to play.'
});
// User has started a keyword input session by typing the extension's keyword. This is guaranteed to be sent exactly once per input session, and before any onInputChanged events.
chrome.omnibox.onInputChanged.addListener(function (text, suggest) {
// Clear suggested videos
self.get('suggestedVideos').length = 0;
var trimmedSearchText = $.trim(text);
// Clear suggestions if there is no text.
if (trimmedSearchText === '') {
suggest();
} else {
// Do not display results if searchText was modified while searching, abort old request.
var previousSearchJqXhr = self.get('searchJqXhr');
if (previousSearchJqXhr) {
previousSearchJqXhr.abort();
self.set('searchJqXhr', null);
}
var searchJqXhr = YouTubeV2API.search({
text: trimmedSearchText,
// Omnibox can only show 6 results
maxResults: 6,
success: function(videoInformationList) {
self.set('searchJqXhr', null);
var suggestions = self.buildSuggestions(videoInformationList, trimmedSearchText);
suggest(suggestions);
}
});
self.set('searchJqXhr', searchJqXhr);
}
});
chrome.omnibox.onInputEntered.addListener(function (text) {
// Find the cached video data by url
var pickedVideo = _.find(self.get('suggestedVideos'), function(suggestedVideo) {
return suggestedVideo.get('url') === text;
});
// If the user doesn't make a selection (commonly when typing and then just hitting enter on their query)
// take the best suggestion related to their text.
if (pickedVideo === undefined) {
pickedVideo = self.get('suggestedVideos')[0];
}
StreamItems.addByVideo(pickedVideo, true);
});
},
buildSuggestions: function(videoInformationList, text) {
var self = this;
var suggestions = _.map(videoInformationList, function (videoInformation) {
var video = new Video({
videoInformation: videoInformation
});
self.get('suggestedVideos').push(video);
var safeTitle = _.escape(video.get('title'));
var textStyleRegExp = new RegExp(Utility.escapeRegExp(text), "i");
var styledTitle = safeTitle.replace(textStyleRegExp, '<match>$&</match>');
var description = '<dim>' + video.get('prettyDuration') + "</dim> " + styledTitle;
return {
content: video.get('url'),
description: description
};
});
return suggestions;
}
});
return new Omnibox();
});
As far as I'm aware the code itself is fine and wouldn't have any effect on whether I see omnibox or not.
You can find full source code here: https://github.com/MeoMix/StreamusChromeExtension/blob/master/src/js/background/model/omnibox.js

Repeatedly Grab DOM in Chrome Extension

I'm trying to teach myself how to write Chrome extensions and ran into a snag when I realized that my jQuery was breaking because it was getting information from the extension page itself and not the tab's current page like I had expected.
Quick summary, my sample extension will refresh the page every x seconds, look at the contents/DOM, and then do some stuff with it. The first and last parts are fine, but getting the DOM from the page that I'm on has proven very difficult, and the documentation hasn't been terribly helpful for me.
You can see the code that I have so far at these links:
Current manifest
Current js script
Current popup.html
If I want to have the ability to grab the DOM on each cycle of my setInterval call, what more needs to be done? I know that, for example, I'll need to have a content script. But do I also need to specify a background page in my manifest? Where do I need to call the content script within my extension? What's the easiest/best way to have it communicate with my current js file on each reload? Will my content script also be expecting me to use jQuery?
I know that these questions are basic and will seem trivial to me in retrospect, but they've really been a headache trying to explore completely on my own. Thanks in advance.
In order to access the web-pages DOM you'll need to programmatically inject some code into it (using chrome.tabs.executeScript()).
That said, although it is possible to grab the DOM as a string, pass it back to your popup, load it into a new element and look for what ever you want, this is a really bad approach (for various reasons).
The best option (in terms of efficiency and accuracy) is to do the processing in web-page itself and then pass just the results back to the popup. Note that in order to be able to inject code into a web-page, you have to include the corresponding host match pattern in your permissions property in manifest.
What I describe above can be achieved like this:
editorMarket.js
var refresherID = 0;
var currentID = 0;
$(document).ready(function(){
$('.start-button').click(function(){
oldGroupedHTML = null;
oldIndividualHTML = null;
chrome.tabs.query({ active: true }, function(tabs) {
if (tabs.length === 0) {
return;
}
currentID = tabs[0].id;
refresherID = setInterval(function() {
chrome.tabs.reload(currentID, { bypassCache: true }, function() {
chrome.tabs.executeScript(currentID, {
file: 'content.js',
runAt: 'document_idle',
allFrames: false
}, function(results) {
if (chrome.runtime.lastError) {
alert('ERROR:\n' + chrome.runtime.lastError.message);
return;
} else if (results.length === 0) {
alert('ERROR: No results !');
return;
}
var nIndyJobs = results[0].nIndyJobs;
var nGroupJobs = results[0].nGroupJobs;
$('.lt').text('Indy: ' + nIndyJobs + '; '
+ 'Grouped: ' + nGroupJobs);
});
});
}, 5000);
});
});
$('.stop-button').click(function(){
clearInterval(refresherID);
});
});
content.js:
(function() {
function getNumberOfIndividualJobs() {...}
function getNumberOfGroupedJobs() {...}
function comparator(grouped, individual) {
var IndyJobs = getNumberOfIndividualJobs();
var GroupJobs = getNumberOfGroupedJobs();
nIndyJobs = IndyJobs[1];
nGroupJobs = GroupJobs[1];
console.log(GroupJobs);
return {
nIndyJobs: nIndyJobs,
nGroupJobs: nGroupJobs
};
}
var currentGroupedHTML = $(".grouped_jobs").html();
var currentIndividualHTML = $(".individual_jobs").html();
var result = comparator(currentGroupedHTML, currentIndividualHTML);
return result;
})();

Get current tab and pass it to variable in a Chrome Extension

I'm trying to create a function that returns the current tab url:
function tabURL() {
var url="";
chrome.tabs.getSelected(null, function(tab) {url = tab.url;});
return url;
}
When I use:
chrome.tabs.getSelected(null, function(tab) {alert(tab.url);});
Chrome shows the url, but if I use my function inside the chrome console, the function returns "".
Is there a way to pass the tab.url to a variable and then return this variable?
chrome.tabs.getSelected is asynchronous. That means that when the callback function is called, return url "has already occurred".
You've got two options to achieve the desired effect.
Properly rewrite your code, to correctly implement the asynchronous aspect (the exact details depends on your extension's implementation).
Note that getSelected has been deprecated and replaced with chrome.tabs.query since Chrome 16.
Maintain a hash with the current URLs using chrome.tabs.onUpdated (add tabID + URL), chrome.tabs.onRemoved (to remove obsolete entries) and chrome.tabs.onActivated (to set the current active tab).
Code for 2:
// Our hash
var tabIdToURL = {};
var currentTabId = -1;
// Add changes to the hash (tab creation, tab's page load)
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
tabIdToURL[tabId] = tab.url; // also available as tab.id and changeInfo.url
});
// Remove entries from closed tabs
chrome.tabs.onRemoved.addListener(function(tabId) {
delete tabIdToURL[tabId];
});
// Set the ID of the current active tab
chrome.tabs.onActivated.addListener(function(activeInfo) {
currentTabId = activeInfo.tabId;
});
// Usage, based on the question's function
function getURL() {
return tabIdToURL[currentTabId] || '';
}

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.