Get Title of selected tab - javascript

Is there a way of getting the value of the title of the page from a Google Extension?

At first you should declare the tabs API permission in your manifest.json:
{
"name": "My extension",
...
"permissions": ["tabs"],
...
}
Then you will be able to use the tabs API, you are looking for the chrome.tabs.getSelected(windowId, callback) method.
To get the selected tab of the current window, you can simply pass null as the windowId.
This method will execute the callback function passing a Tab object as its first argument, where you can simply get the title property:
chrome.tabs.getSelected(null,function(tab) { // null defaults to current window
var title = tab.title;
// ...
});

Notice that the above method mentioned by CMS is deprecated since Chrome 33.
You don't really need to specify the tabs permission in your manifest file since what you're doing here isn't some advanced action. You can perform most of the tabs actions without specifying the permission; only for some certain methods will you need to.
The new way of querying the currently selected tab is by the following code:
chrome.tabs.query({ active: true }, function (tab) {
// do some stuff here
});
This will give you the selected tabs in all windows, if you have multiple windows open. If you want to get only the selected tab in the current window, use the following:
chrome.tabs.query({ active: true, currentWindow: true }, function (tab) {
// do some other fanciful stuff here
});
For more details, refer to https://developer.chrome.com/extensions/tabs#method-query

Related

What does tabs[0] mean in browser extensions?

In the Firefox example of browser extensions, the popup script sends a message to the content script using the following:
browser.tabs.sendMessage(tabs[0].id, {
command: "beastify", beastURL: url }
Looking through the MDN documentation doesn't give a clear cut answer as to what tabs really is. It seems like its an array containing all the tab objects in the browser. But how do I know what tabs[0] is? and what about the rest of the array?
Is using tabs[0] equivalent to finding the current active tab?
This is from Firefox documentation
browser.tabs
.query({ active: true, currentWindow: true })
.then(beastify)
....
function beastify(tabs) {
browser.tabs.insertCSS({ code: hidePage }).then(() => {
let url = beastNameToURL(e.target.textContent);
browser.tabs.sendMessage(tabs[0].id, {
command: "beastify",
beastURL: url
});
});
}
As you can see the beastify function is called with list (array) of tabs from browser.tabs.query()
Because only one tab in the same window can be active, it's save to say that the list will contain only single item, hens we can safely use tabs[0] to access first and only item in the array.

chrome extension call specific function

I have a popup.js:
function registerButtonAction(tabId, button, action) {
// clicking button will send a message to
// content script in the same tab as the popup
button.addEventListener('click', () => chrome.tabs.sendMessage(tabId, { [action]: true }));
}
function setupButtons(tabId) {
// add click actions to each 3 buttons
registerButtonAction(tabId, document.getElementById('start-btn'), 'startSearch');
registerButtonAction(tabId, document.getElementById('deals-btn'), 'startDeals');
}
function injectStartSearchScript() {
chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
// Injects JavaScript code into a page
// chrome.tabs.executeScript(tabs[0].id, { file: 'main.js' });
// dd click handlers for buttons
setupButtons(tabs[0].id);
});
}
injectStartSearchScript()
// document.getElementById('inject-btn').addEventListener('click', injectStartSearchScript);
Which I use to inject my script into the page with the "start-btn" inside my popup.html.
This is my main.js which includes my functions I would like to call on a page:
function pong() {
// do something
}
function ping() {
// do something else
}
my manifest.json:
{
"manifest_version": 2,
"name": "test app",
"description": "test desc",
"version": "1.0",
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"permissions": ["tabs", "<all_urls>"],
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["main.js"]
}
]
}
So basically my setup is that I have a popup.html which includes 3 buttons and they should call one of the functions inside my main.js dpending on what button i press.
But I can not get that working. Currently I only can make at least one function call if I directly call pong() inside main.js on load. But I would need to call one of the functions after I click on a button inside my popup.html.
EDIT: I have updated the code as far as I understood. I am very sorry but I don't understand what would be needed to be changed else to fulfill your proposal. I mean how to write it to be more correct.
EDIT 2: I have removed the line chrome.tabs.executeScript(tabs[0].id, { file: 'main.js' }); as well as document.getElementById('inject-btn').addEventListener('click', injectStartSearchScript)and added injectStartSearchScript()into the popup.js file. Is that what you meant?
Updated and complete example with explanation (you are almost there!)
manifest
You manifest looks good, no changes needed there.
This configuration says to load the content script in each tab. So before popup even opens each tab will have "main.js" injected into it (exactly once).
popup.js
Popup script looks good, no changes needed there.
The tab lookup is still necessary, since to send a message to a specific tab, must know its id. The popup looks for the currently active tab in current window (same as the popup is in) and sets up the button click actions to send a message to the tab.
main.js
Will need minor changes here
Make sure to register the onMessage listener, as included in the example below.
note the conditions: message.startSearch and message.startDeals -- these must match the messages sent from the popup, i.e. when popup sends a message with content {startDeals: true}, the if condition is startDeals. It is matching by a key in the sent message and if the key does not match any condition, the message is going to be ignored.
function pong() {
// do something
alert('Start!');
}
function ping() {
// do something else
alert('Deals!');
}
// register listener to receive messages
chrome.runtime.onMessage.addListener(message => {
// what to do on each received message:
if (message.startSearch) pong();
else if (message.startDeals) ping();
});
// sanity check: content has loaded in the tab
console.log('content loaded');
One more note as it relates to debugging extensions (and perhaps a source of some of these debugging issues) when the content script has been configured to be injected in the manifest, Chrome will inject main.js into tabs, in a separate extension context. If, after this injection, the developer reloads the extension (circular arrow in chrome://extensions or other tool), this will invalidate the context of the content script in the tab. The tab has to be reloaded to reactivate the content script. This is not an issue with a real user, but it does happen during debugging, so double check this is not the cause of issues while debugging.

How to get Chrome Extension's Options value in Extension? [duplicate]

I have an options page where the user can define certain options and it saves it in localStorage: options.html
Now, I also have a content script that needs to get the options that were defined in the options.html page, but when I try to access localStorage from the content script, it doesn't return the value from the options page.
How do I make my content script get values from localStorage, from the options page or even the background page?
Update 2016:
Google Chrome released the storage API: https://developer.chrome.com/docs/extensions/reference/storage/
It is pretty easy to use like the other Chrome APIs and you can use it from any page context within Chrome.
// Save it using the Chrome extension storage API.
chrome.storage.sync.set({'foo': 'hello', 'bar': 'hi'}, function() {
console.log('Settings saved');
});
// Read it using the storage API
chrome.storage.sync.get(['foo', 'bar'], function(items) {
message('Settings retrieved', items);
});
To use it, make sure you define it in the manifest:
"permissions": [
"storage"
],
There are methods to "remove", "clear", "getBytesInUse", and an event listener to listen for changed storage "onChanged"
Using native localStorage (old reply from 2011)
Content scripts run in the context of webpages, not extension pages. Therefore, if you're accessing localStorage from your contentscript, it will be the storage from that webpage, not the extension page storage.
Now, to let your content script to read your extension storage (where you set them from your options page), you need to use extension message passing.
The first thing you do is tell your content script to send a request to your extension to fetch some data, and that data can be your extension localStorage:
contentscript.js
chrome.runtime.sendMessage({method: "getStatus"}, function(response) {
console.log(response.status);
});
background.js
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
if (request.method == "getStatus")
sendResponse({status: localStorage['status']});
else
sendResponse({}); // snub them.
});
You can do an API around that to get generic localStorage data to your content script, or perhaps, get the whole localStorage array.
I hope that helped solve your problem.
To be fancy and generic ...
contentscript.js
chrome.runtime.sendMessage({method: "getLocalStorage", key: "status"}, function(response) {
console.log(response.data);
});
background.js
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
if (request.method == "getLocalStorage")
sendResponse({data: localStorage[request.key]});
else
sendResponse({}); // snub them.
});
Sometimes it may be better to use chrome.storage API. It's better then localStorage because you can:
store information from your content script without the need for
message passing between content script and extension;
store your data as JavaScript objects without serializing them to JSON (localStorage only stores strings).
Here's a simple code demonstrating the use of chrome.storage. Content script gets the url of visited page and timestamp and stores it, popup.js gets it from storage area.
content_script.js
(function () {
var visited = window.location.href;
var time = +new Date();
chrome.storage.sync.set({'visitedPages':{pageUrl:visited,time:time}}, function () {
console.log("Just visited",visited)
});
})();
popup.js
(function () {
chrome.storage.onChanged.addListener(function (changes,areaName) {
console.log("New item in storage",changes.visitedPages.newValue);
})
})();
"Changes" here is an object that contains old and new value for a given key. "AreaName" argument refers to name of storage area, either 'local', 'sync' or 'managed'.
Remember to declare storage permission in manifest.json.
manifest.json
...
"permissions": [
"storage"
],
...
Another option would be to use the chromestorage API. This allows storage of user data with optional syncing across sessions.
One downside is that it is asynchronous.
https://developer.chrome.com/extensions/storage.html
[For manifest v3]
You can execute a script that returns localstorage items from the webpage. This script can be executed from popup or background service worker.
Add this line in manifest.json:
"permissions": ["scripting"]
let [tab] = await chrome.tabs.query({ active: true, currentWindow: true })
// Execute script in the current tab
const fromPageLocalStore = await chrome.scripting.executeScript({
target: { tabId: tabId },
func: () => {
return JSON.stringify(localStorage)
}
})
const localStorageItems = JSON.parse(fromPageLocalStore[0].result)

Getting active tab information using chrome extension api

I am working on a chrome extension, and I need information about the active tab (when I say "active", I mean the tab that I am looking at in the current window that is focused).
Using the chrome.tabs api, I should be able to do something like the following to get what I want:
function getActiveTab() {
var activeTabInfo = {"currentWindow": true, "active" : true};
return chrome.tabs.query(activeTabInfo,function (tabs) {
return tabs[0];
});
}
However, when I log the length of tabs within the callback, I'm getting a length of 0. I modeled this snippet after How to fetch URL of current Tab in my chrome extension using javascript, but can't seem to get it to work.
Any thoughts?

How to detect page title change in Google Chrome from an extension?

I'm creating a Google Chrome extension and I need to detect when a page's title changes. The page's title is changed like in Twitter: (num) Twitter (see the screenshot below) - when a new tweet is posted, the number increments. Example:
I'm trying to detect the title changes of a URL that's loaded in one of my tabs and play a beep sound whenever there's a difference. This check is to be done in a repeated interval and I think that can be accomplished using setTimeOut() function.
I've created a manifest.json as follows:
{
"manifest_version": 2,
"name": "Detect Page Title Changes",
"description": "Blah",
"version": "1.0",
"browser_action": {
"default_icon": "icon.png",
"default_popup": "background.html"
},
"permissions": [
"tabs"
]
}
However, I'm clueless about the rest. I've searched through the docs 1 2 and tried the solutions on similar Stack Overflow threads such as this one I but couldn't find anything that suits my requirements.
Do you have any suggestions? Please include an example, if possible.
Instead of arguing in comments that a certain approach is better, let me be more constructive and add an answer by showing a particular implementation I co-wrote myself, and explain some gotchas you may run into. Code snippets refer to a service different from Twitter, but the goal was the same. In fact, this code's goal is to report the exact number of unread messages, so yours might be simpler.
My approach is based on an answer here on SO, and instead of being polling-driven (check condition at fixed intervals) is event-driven (be notified of potential changes in condition).
Advantages include immediate detection of a change (which would otherwise not be detected until the next poll) and not wasting resources on polls while the condition does not change. Admittedly, the second argument hardly applies here, but the first one still stands.
Architecture at a glance:
Inject a content script into the page in question.
Analyze initial state of the title, report to background page via sendMessage.
Register a handler for a title change event.
Whenever the event fires and the handler is called, analyze the new state of the title, report to background page via sendMessage.
Already step 1 has a gotcha to it. Normal content script injection mechanism, when the content script is defined in the manifest, will inject it in pages upon navigation to a page that matches the URL.
"content_scripts": [
{
"matches": [
"*://theoldreader.com/*"
],
"js": ["observer.js"],
"run_at": "document_idle"
}
]
This works pretty well, until your extension is reloaded. This can happen in development as you're applying changes you've made, or in deployed instances as it is auto-updated. What happens then is that content scripts are not re-injected in existing open pages (until navigation happens, like a reload). Therefore, if you rely on manifest-based injection, you should also consider including programmatic injection into already-open tabs when extension initializes:
function startupInject() {
chrome.tabs.query(
{url: "*://theoldreader.com/*"},
function (tabs) {
for (var i in tabs) {
chrome.tabs.executeScript(tabs[i].id, {file: "observer.js"});
}
}
);
}
On the other end, content script instances that were active at the time of extension reload are not terminated, but are orphaned: any sendMessage or similar request will fail. It is, therefore, recommended to always check for exceptions when trying to communicate with the parent extension, and self-terminate (by removing handlers) if it fails:
try {
chrome.runtime.sendMessage({'count' : count});
} catch(e) { // Happens when parent extension is no longer available or was reloaded
console.warn("Could not communicate with parent extension, deregistering observer");
observer.disconnect();
}
Step 2 also has a gotcha to it, though it depends on the specifics of the service you're watching. Some pages inside the scope of the content script will not show the number of unread items, but it does not mean that there are no new messages.
After observing how the web service works, I concluded that if the title changes to something without navigation, it's safe to assume the new value if correct, but for the initial title "no new items" should be ignored as unreliable.
So, the analysis code accounts for whether it's the initial reading or handling an update:
function notify(title, changed) {
// ...
var match = /^\((\d+)\)/.exec(title);
var match_zero = /^The Old Reader$/.exec(title);
if (match && match[1]) {
count = match[1];
} else if (match_zero && changed) {
count = 0;
}
// else, consider that we don't know the count
//...
}
It is called with the initial title and changed = false in step 2.
Steps 3 & 4 are the main answer to "how to watch for title changes" (in an event-driven way).
var target = document.querySelector('head > title');
var observer = new window.MutationObserver(
function(mutations) {
mutations.forEach(
function(mutation){
notify(mutation.target.textContent, true);
}
);
}
);
observer.observe(target, { subtree: true, characterData: true, childList: true });
For specifics as to why certain options of observer.observe are set, see the original answer.
Note that notify is called with changed = true, so going from "(1) The Old Reader" to "The Old Reader" without navigation is considered to be a "true" change to zero unread messages.
Put chrome.tabs.onUpdated.addListener in your background script:
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
console.log(changeInfo);
});
changeInfo is an object which includes title changes, e.g. here:
Can then filter on the object so that an action only occurs if changeInfo includes a title change. For additional manipulation, e.g. responding to page title changes with page content / actions, you can send a message to content script from inside the listener after whatever conditions are met.
Create an event page.
Create a content script that gets injected into a webpage when a webpage loads.
Within the content script, use setInterval to poll the page to see if window.document.title changes.
If the title has changed, use chrome.runtime.sendMessage to send a message to your event page.
On your event page, listen for messages with chrome.runtime.onMessage and play a sound.
After researching Chrome's tabs API, it doesn't look like anything stands out to help you directly. However, you should be able to attach an event listener to the title node of the tab(s) you're interested in. The DOMSubtreeModified mutation event works in Chrome, and a quick test in a normal html document proves to work for me - should be no different from within an extension.
var title = document.getElementsByTagName('title')[0];
if (title) {
title.addEventListener('DOMSubtreeModified', function (e) {
// title changed
}, false);
}

Categories

Resources