Building an extension over chrome browser adds a lot of flexibility and functionality to any website. We can make use of a browser engine to do additional stuff which is not possible using simple html and javascript.
In this blog we will develop a chrome extension named Tab++. This extension will help in searching any text on the opened chrome browser tabs. For example, if we have 20 tabs opened on the chrome browser, any text can be searched using this extension on each of the tabs.By clicking on a result, it will open the tab with yellow highlight. In the long side, this extension can also be used in positioning and managing all open tabs. The Source code of this extension is mentioned in the last section of this blog, for you to manually download and try on your PC.
Our first step is to define a manifest file. Manifest file holds all extension metadata like name, description and certain permissions from the browser to use the extension. Here is the content of manifest.json file:
{
"manifest_version": 2,
"name": "Tab++",
"version": "1.1",
"icons": {
"128": "logo.png"
},
"permissions": [
"tabs",
"storage",
"http://*/*",
"https://*/*"
],
"minimum_chrome_version": "23",
"description": "Arrange the tabs in chrome easily",
"content_security_policy": "script-src 'self' https://ssl.google-analytics.com; object-src 'self'",
"short_name": "Tabs Search & Navigator",
"update_url": "https://clients2.google.com/service/update2/crx",
"background": {
"scripts": ["jquery-3.2.1.min.js","script.js"]
},
"web_accessible_resources": [
"close.png"
],
"browser_action": {
"default_icon": "logo.png",
"default_popup": "popup.html"
},
"commands": {
"_execute_browser_action": {
"suggested_key": {
"default": "Alt+O",
"mac": "Command+O"
},
"description": "Activate Tab++"
},
"ordertitle": {
"suggested_key": {
"default": "Alt+T"
},
"description": "Order By Title"
},
"orderurl": {
"suggested_key": {
"default": "Alt+U"
},
"description": "Order By URL"
}
},
"content_scripts": [{
"js": ["jquery-3.2.1.min.js","jquery.mark.min.js","content.js"],
"all_frames" : false,
"css": [],
"matches": ["<all_urls>"],
"run_at": "document_end"
}]
}
A couple of background script files have been added on the manifest. One is a normal Jquery file downloaded to our folder and the other one is our own script. Background scripts are directly injected to the browser. They are more powerful than the normal script files injected into the page. Using them we can call browser’s own API to do things which browser only can do.
There are also a couple of scripts in the content script part of the manifest. Content script is just like normal javascript file that will be embedded on any webpage opened on the browser. Through this we can manipulate with DOM objects just like normal javascript. Additionally, we can also communicate with background script in both directions using content script.
There is one more html file called popup.html in the manifest, which is just like a HTML file that opens a small pop up when you click on the extension icon. In that pop up we can place various html elements like textbox, checkbox etc.
Here is how our background script looks like:
var from="";
var to="";
var url="";
var country="";
var multiply="1";
var conv=0.0;
var alltabs;
var myport=null;
var chk1=false;
var chk2=false;
var chk3=false;
var term="";
var mybookmarks;
var tabmove=false;
chrome.extension.onConnect.addListener(function(port) {
port.onMessage.addListener(function(msg) {
if(msg=="GetTabs")
{
myport=port;
chrome.storage.local.get('tabplusplus', function (result) {
var str = result.tabplusplus;
if(str==undefined)
{
getTabs();
return;
}
var res = str.split("##mysplit##");
var chkk1 = res[0];
var chkk2=res[1];
var chkk3=res[2];
chk1=JSON.parse(chkk1);
chk2=JSON.parse(chkk2);
getTabs();
});
}
if(msg.indexOf("search=")!=-1)
{
msg=msg.replace("search=","");
term=msg;
}
if(msg.indexOf("closetab")!=-1)
{
msg=msg.replace("closetab","");
chrome.tabs.remove(parseInt(msg), function() { });
}
if(msg.indexOf("updatetabs")!=-1)
{
msg=msg.replace("updatetabs","");
var arr=msg.split(",");
var newtabs=[];
var v1 = 'tabplusplus';
var obj= {};
obj[v1] = "false##mysplit##false##mysplit##false";
chrome.storage.local.set(obj);
tabmove=true;
for(var i=0;i<arr.length-1;i++)
{
chrome.tabs.move(parseInt(arr[i]), {index: i});
for(var j=0;j<alltabs.length;j++)
{
if(arr[i]==alltabs[j].id)
{
newtabs.push(alltabs[j]);
break;
}
}
}
setTimeout(function(){
tabmove=false;
}, 200);
alltabs=newtabs;
//myport.postMessage(alltabs);
}
if(msg=="ordertitle")
{
getTabs();
setTimeout(function(){
alltabs.sort(sortOn("title"));
tabmove=true;
for(var i=0;i<alltabs.length;i++)
{
chrome.tabs.move(alltabs[i].id, {index: i});
}
setTimeout(function(){
tabmove=false;
}, 200);
myport.postMessage(alltabs);
}, 100);
}
if(msg=="orderurl")
{
getTabs();
setTimeout(function(){
alltabs.sort(sortOn("url"));
tabmove=true;
for(var i=0;i<alltabs.length;i++)
{
chrome.tabs.move(alltabs[i].id, {index: i});
}
setTimeout(function(){
tabmove=false;
}, 200);
myport.postMessage(alltabs);
}, 100);
}
if(msg.indexOf("changetab")!=-1)
{
msg=msg.replace("changetab","");
chrome.tabs.update(parseInt(msg), {active: true});
if(term!="")
{
chrome.tabs.query({
windowId: chrome.windows.WINDOW_ID_CURRENT
}, function (tabs) {
chrome.tabs.sendMessage(parseInt(msg), {
"functiontoInvoke": "highlight",
"val":term
});
});
}
}
});
});
function getTabs()
{
alltabs=[];
//alert(JSON.stringify(mybookmarks));
chrome.tabs.query({
windowId: chrome.windows.WINDOW_ID_CURRENT
}, function (tabs) {
for(var i=0;i<tabs.length;i++)
{
var tab = new Object();
tab.id=tabs[i].id;
tab.img=tabs[i].favIconUrl;
tab.url=tabs[i].url;
tab.title=tabs[i].title;
tab.data="";
chrome.tabs.sendMessage(tabs[i].id, {
"functiontoInvoke": "getcontent",
"index":i,
});
alltabs.push(tab);
}
setTimeout(function(){ senddata(); }, 100);
});
}
function sortOn(property){
return function(a, b){
if(a[property].toLowerCase() < b[property].toLowerCase()){
return -1;
}else if(a[property].toLowerCase() > b[property].toLowerCase()){
return 1;
}else{
return 0;
}
}
}
function senddata()
{
if(chk1)
{
alltabs.sort(sortOn("url"));
}
if(chk2)
{
alltabs.sort(sortOn("title"));
}
chrome.tabs.query({
active: true,
currentWindow: true
}, function(tabs) {
var tab = tabs[0];
var id = tab.id;
myport.postMessage("activetab="+id);
myport.postMessage(alltabs);
});
}
chrome.runtime.onMessage.addListener(function(msg, sender, sendResponse) {
if(msg.text == "takecontent")
{
var tabid=sender.tab.id;
var data=msg.val;
var index=msg.index;
alltabs[index].data=data.toLowerCase();
}
});
updateTabCount();
function updateTabCount()
{
chrome.tabs.query({
windowId: chrome.windows.WINDOW_ID_CURRENT
}, function (tabs) {
chrome.browserAction.setBadgeText({text: ''+tabs.length});
});
}
chrome.tabs.onMoved.addListener(function() {
if(!tabmove)
{
getTabs();
var v1 = 'tabplusplus';
var obj= {};
obj[v1] = "false##mysplit##false##mysplit##false";
chrome.storage.local.set(obj);
}
});
chrome.tabs.onRemoved.addListener(function(tabid, removed) {
updateTabCount();
//getTabs();
});
chrome.tabs.onCreated.addListener(function() {
updateTabCount();
//getTabs();
});
chrome.windows.onFocusChanged.addListener(function(windowId) {
updateTabCount();
//getTabs();
});
getTabs();
chrome.commands.onCommand.addListener( function(command) {
if(command === "ordertitle")
{
getTabs();
setTimeout(function(){
tabmove=true;
alltabs.sort(sortOn("title"));
for(var i=0;i<alltabs.length;i++)
{
chrome.tabs.move(alltabs[i].id, {index: i});
}
var v1 = 'tabplusplus';
var obj= {};
obj[v1] = "false##mysplit##true##mysplit##false";
chrome.storage.local.set(obj);
setTimeout(function(){
tabmove=false;
}, 200);
myport.postMessage(alltabs);
}, 100);
}
if(command === "orderurl")
{
getTabs();
setTimeout(function(){
tabmove=true;
alltabs.sort(sortOn("url"));
for(var i=0;i<alltabs.length;i++)
{
chrome.tabs.move(alltabs[i].id, {index: i});
}
var v1 = 'tabplusplus';
var obj= {};
obj[v1] = "true##mysplit##false##mysplit##false";
chrome.storage.local.set(obj);
setTimeout(function(){
tabmove=false;
}, 200);
myport.postMessage(alltabs);
}, 100);
}
});
Here is how our content script looks like:
chrome.extension.onMessage.addListener(function (message, sender, callback) {
if (message.functiontoInvoke == "getcontent") {
var data="";
if(document)
{
if(document.getElementsByTagName('body')[0])
{
data=document.getElementsByTagName('body')[0].innerText;
}
}
chrome.runtime.sendMessage({text: "takecontent",val:data,index:message.index}, function(response) {
});
}
if (message.functiontoInvoke == "highlight") {
var term=message.val;
hightlight(term);
}
});
var currentIndex = 0;
var $results;
var offsetTop = 50;
function hightlight(term)
{
$("body").unmark();
$("body").mark(term, {
separateWordSearch: false,
done: function() {
$results = $("body").find("mark");
currentIndex = 0;
jumpTo();
}
});
}
function jumpTo() {
if ($results.length) {
var position,
$current = $results.eq(currentIndex);
if ($current.length) {
position = $current.offset().top - offsetTop;
window.scrollTo(0, position);
}
}
}
Here is the GITHUB link of the complete project:
https://github.com/sarathisahoovt/tab-
Steps to install any chrome extension from your local PC:
1 - Download the source code to any folder on your PC.
2 - Go to chrome://extensions/ on browser
3 - Click Developer mode checkbox
4 - Click load unpacked extension button
5 - Give the path of the source code folder
6 - That's it. Your extension is ready. Enable it by clicking on the puzzle icon on right top panel of your browser.