I successfully created my first Chrome extension. It now runs only when the extension icon is clicked instead of on the background, and that is great. However, I would like to add more actions to my extension I have been trying to use an extension popup to run other functions but I can't make it work. It doesn't have to be like that, so I am open for suggestions. I do not want to use context menus. I want people to click on the extension icon and show them a "menu".
Right now my extension only alerts a message when it finds a valid page (from mydomain.com), and it finds a hidden field with the name "returnURL". It alerts the value.
I would like to be able to add the ability to click on the icon but instead show an options menu with multiple options.
Something like this:
Click on the extension icon and show two options
Get Response URL (this option will run the current functionality I have now)
Do something else (So I could have another function to execute on the
loaded page)
...and more options if I needed to add them on future versions of my extension.
How do I modify my extension to do that.
Here is my code:
manifest.json
{
"name": "Get Response URL",
"version": "1.0",
"manifest_version": 2,
"browser_action": {
"default_icon": "mkto_icon.png",
"name": "Click to get URL"
},
"background":{
"scripts":["background.js"]
},
"permissions":["http://mydomain.com/*"]
}
background.js
chrome.browserAction.onClicked.addListener(function (tab) { //Fired when User Clicks ICON
if (tab.url.indexOf("http://mydomain.com/") != -1) { // Inspect whether the place where user clicked matches with our list of URL
chrome.tabs.executeScript(tab.id, {
"file": "contentscript.js"
}, function () { // Execute your code
console.log("Script Executed .. "); // Notification on Completion
});
}
});
contentscript.js
if (document.getElementsByName("returnURL")){
alert("\nThe Response URL on this form is:\n\n" + document.getElementsByName("returnURL")[0].value);
}
I followed the documentation I found on the Google Extensions developer site but I couldn't make it work. Your help is much appreciated.
You cannot have a real menu with a Chrome extension. You can however show an HTML page when the button is clicked (a pop-up). You can style this HTML page in a way that looks similar to a menu:
"browser_action": {
"default_icon": "mkto_icon.png",
"default_title": "Click here to open menu",
"default_popup": "popup.html"
},
Add a file popup.html to you extension and whatever content you want to show up. Scripts loaded by the pop-up page can load content scripts and communicate with them just like the background page.
For reference: browser actions documentation.
Related
I am developing a chrome extension, in which I have used iframe, which I want to keep open even if we do some activity on the active web page itself or open a new tab or a new window. The extension should get closed only on clicking on the extension icon again or by clicking a close button, present on the extension itself. I am hinting on something like join pouch extension.
Other questions pertaining to this topic only caters to opening a window through extension which remains open or keeping the extension popup open just for debugging purposes, by inspecting the popup.
I know there is a way to do this, since I have seen some extensions as such, but am unable to find.
You can embed that iframe into the DOM structure of every single page you open. Unfortunately, Google doesn't provide any solution besides a default popup that disappears when the first click outside of it happens. The flipside is, you'll need to run one instance of your content script per page, which might mean there will be hundreds of them running (at least for me as I'm a compulsive new-tab-opener).
This is one of the ways, how you can approach this.
Step 1. As I've already said, the default action on icon click is opening a popup. You can disable that behaviour by including the following entry in your manifest.json:
"browser_action": {},
Step 2. The next thing would be creating a content script that runs on every page. In your manifest, it would look like this:
"content_scripts": [
{
"run_at": "document_end",
"matches": ["*://*/*"],
"js": ["path/to/your/content/script.js"]
}
],
Step 3.
Your content script should embed that iframe you mentioned into the currently open page (when the DOM fully loaded).
window.addEventListener('load', function load(event) {
var iframe = document.createElement('iframe');
/* some settings, these are mine */
iframe.style.width = "250px";
iframe.style.background = "#eee";
iframe.style.height = "100%";
iframe.style.position = "fixed";
iframe.style.top = "0px";
iframe.style.right = "0px";
iframe.style.zIndex = "1000000000000000";
iframe.frameBorder = "none";
/* end of settings */
iframe.src =
chrome.runtime.getURL("path/to/contents/of/your/iframe.html");
document.body.appendChild(iframe);
});
Step 4.
You must make sure, people can access iframe.html. Add this line to your manifest file:
"web_accessible_resources": ["path/to/contents/of/your/iframe.html"],
(or just add that file to the list if it was already there.
Step 5.
Create a function in your content script that knows how to hide your iframe. Something like that:
function toggle_iframe() {
if (iframe.style.width == "0px"){
iframe.style.width="250px";
} else {
iframe.style.width="0px";
}
}
Now, the only thing left is to know when to call it.
Step 6.
Unfortunately, background script is the only place that can get information about extension icon being clicked. Add this snippet to your background script:
chrome.browserAction.onClicked.addListener(function(){
chrome.tabs.query({active: true, currentWindow: true}, function(tabs){
chrome.tabs.sendMessage(tabs[0].id, { action: "must_toggle_iframe" });
})
});
It sends a message, when the icon has been clicked.
Step 7.
The last part is to allow your content script to receive that message. Your content script needs a listener.
chrome.runtime.onMessage.addListener(function(msg, sender) {
if (msg.action == "must_toggle_iframe"){
toggle_iframe();
}
}
Of course, the solution is not ideal, but you could refine it. For example by remembering in the chrome.storage.local/chrome.storage.sync whether the iframe has been hidden.
I'm developing a Chome extension. When you click on the extension icon, the contextMenu is accessible by a right-click and a left-click.
Actually, I don't have a browserAction in my manifest.json, so my problem is my icon is grayed out. The solution is to add a browserAction. But if I add a browserAction, the left-click don't show the default menu, but shows nothing.
What I want is a colored icon and when I left-click that the default Chrome extension popup is opened.
Default Chrome Extension popup example:
My manifest.json
{
"manifest_version": 2,
"name": "Awesome app",
"version": "0.1",
"background": {
"scripts": ["background.js"],
"persistent": true
},
"permissions": [
"storage",
"contextMenus"
],
"icons": {
"16": "icons/se16.png",
"32": "icons/se32.png",
"158": "icons/se158.png"
}
}
Can someone help me?
It's not possible to have the default menu AND a colorful icon because the very presence of "browser_action" key means the extension wants to interact with the user and instructs the browser to either delegate icon click event to background page script in chrome.browserAction.onClicked listener or show a popup window if "default_popup" is declared in manifest or the popup was set programmatically via chrome.browserAction.setPopup.
The only way to "ignore" the click event in browserAction API is to disable the icon via chrome.browserAction.disable which will gray it out thus defeating the initial goal.
Well, you can show/do something useful on click. The default menu isn't very useful, anyway.
When the browserAction icon is clicked for my Chrome extension, I want to prevent it from showing the browserAction popup. I'm trying to use the browserAction icon to trigger the loading of a content script instead.
How would I do this?
The following doesn't prevent it from opening:
chrome.browserAction.onClicked.addListener(function (tab) {
alert('browserAction clicked');
return false;
});
Here is my manifest.json entry for this:
"browser_action": {
"default_icon": {
"19": "images/icon-19.png",
"38": "images/icon-38.png"
},
"default_title": "My extension"
}
The docs say This event will not fire if the browser action has a popup, however, as you can see I don't have a popup defined but it still doesn't work.
When you press the extension button, the following happens.
If you have a popup defined for the current tab (e.g. by the default_popup key in the manifest or chrome.browserAction.setPopup), it is shown. The chrome.browserAction.onClicked is not raised.
If the popup is not set (or set as empty) for the current tab, chrome.browserAction.onClicked is dispatched.
So, to disable this permanently, you just need to remove default_popup from the manifest. To disable it programmatically, you need to unset it:
chrome.browserAction.setPopup({
popup: "",
// tabId: id // optional, restrict to a single tab
});
Let's say I have an extension that loads when you arrive at a YouTube video page.I have noticed that when one navigates back and forth using the Chrome buttons, the extension most probably won't load.
As an example, I have 2 files, the manifest:
{
"name": "back forth",
"version": "0.1",
"manifest_version": 2,
"description": "back forth",
"permissions": ["storage", "*://www.youtube.com/watch*"],
"content_scripts": [
{
"matches": ["*://www.youtube.com/watch*"],
"js": ["contentscript.js"]
}
]
}
and the contentscript
alert("loaded");
The alert does not always show up when navigating back and forth. How can I overcome this, so that the extension loads every time?
YouTube has started a trial with pushState-based navigation. In general, such navigations can only be detected within content scripts by injecting code that intercept calls to history.replaceState / history.pushState (or by using the chrome.webNavigation.onHistoryStateUpdated event in the background page).
The remainder of this answer is specific to YouTube.
YouTube shows a (red) progress bar on top of the page during load. This progress bar is animated using a CSS transition. Since transition events bubble, you can bind a transition event listener to <body>, and check navigation in these cases.
You have to insert your content script at *://www.youtube.com/* instead of *://www.youtube.com/watch*, because pushState can be used to navigate from / to /watch...
function afterNavigate() {
if ('/watch' === location.pathname) {
alert('Watch page!');
}
}
(document.body || document.documentElement).addEventListener('transitionend',
function(/*TransitionEvent*/ event) {
if (event.propertyName === 'width' && event.target.id === 'progress') {
afterNavigate();
}
}, true);
// After page load
afterNavigate();
Note: This method depends on the fact that the progress bar is inserted. Whenever Google decides to rename the ID of the progress bar, or remove the progress bar altogether, your code will cease to work.
Note 2: This only works for active tabs. If you need to detect navigation changes while the tab is not focused, then you need to bind a window.onfocus and window.onblur event, and check whether document.title has changed between these events.
I'm not getting how to pass data between content script and page action popup.
I've started with the following simple skeleton, that shows an page action for any page having a minus-dash in title:
Extension manifest (manifest.json):
{
…
"permissions": ["http://example.org/*"],
"background": {"scripts": ["background.js"]},
"page_action": {"default_popup": "popup.html", …},
"content_scripts": {
"matches": ["http://example.org/*"],
"js": ["content.js"]
}
}
Background script (background.js):
chrome.extension.onRequest.addListener(function (msg, sender, respond) {
if (msg && msg.what === "match") {
console.log("Match:", msg.title);
chrome.pageAction.show(sender.tab.id);
}
}
Content script (content.js), checking document titles:
var title = document.title;
if (title.indexOf("-") >= 0) {
chrome.extension.sendRequest({"what": "match", "title": title});
}
Now, in a page action's popup, I want to show matched page's title. Not the last loaded page's title (as would be done by using this answer), but the title of the active tab.
My first though was to send a query to the content script, but according to documentation chrome.extension.sendMessage will send it to all listeners (i.e. all my content scripts on all tabs), without any clear definition on whose response I'll receive back. Also, I can't use chrome.tabs.sendMessage as it requires tabId. Trying to find the current tab using chrome.tabs.getCurrent will return null, as the query comes from non-tab context (from a popup).
I guess I could probably use chrome.tabs.executeScript to communicate, but this just feels dirty.
Still, I believe, this is a basic thing that should be very simple to do and I'm just missing something. Could someone, please, help me, preferably, with an example code?
Update: I've found Mappy example extension and it uses chrome.tabs.onUpdated to keep track of the active tab. This, unfortunately, requires tabs permission. Personally, I'd like to use least privileges possible.
So, is it just unfortunately bad permission system design and I have no choice but to do it this way, or there's any workaround? I'd be happy if chrome.pageAction.onClicked event handler (which provides Tab object that I need) would allow me to show a popup...
I think you need to add the onClick event listener in your popup:
chrome.pageAction.onClicked.addListener(function(tabs.Tab tab) {...});
See documentation here.
Callback of the event listener would provide you the tabId which would surely be the active tab.
There are multiple Problems in your code
chrome.extension.sendRequest in chrome.extension.sendRequest({"what": "match", "title": title}); is deprecated
chrome.pageAction.onClicked will not fire when you have "page_action": {"default_popup": "popup.html", …}, in your manifest.
chrome.extension.sendMessage will send it to all listeners (i.e. all my content scripts on all tabs), is an invalid assumption, it will send to Extension Pages.
I tried to read your question multiple times but couldn't understand what is you want to achieve, could you explain it?