How can I open my extension's pop-up with JavaScript? - google-chrome

I am trying to write a JavaScript function that will open my extension like when the extension icon is clicked. I know how to open my extension in a new tab:
var url = "chrome-extension://kelodmiboakdjlbcdfoceeiafckgojel/login.html";
window.open(url);
But I want to open a pop-up in the upper right corner of the browser, like when the extension icon is clicked.

The Chromium dev team has explicitly said they will not enable this functionality. See Feature request: open extension popup bubble programmatically :
The philosophy for browser and page action popups is that they must be triggered by user action. Our suggestion is to use the new html notifications feature...
Desktop notifications can be used progammatically to present the user with a small HTML page much like your popup. It's not a perfect substitution, but it might provide the type of functionality you need.

Chrome team did create a method to open the popup programmatically, but it's only enabled as a private API, and plans to make it generally available have stalled due to security concerns.
So, as of March 2018 as of now, you still can't do it.

Short answer is that you cannot open browserAction programmatically. But you can create a dialog with your content script which emulates your browserAction and display that isntead (programmatically). However you won't be able to access your extension's background page from this popup directly as you can from your popup.html. You will have to pass message instead to your extension.

As mentioned there is no public API for this.
One workaround I have come up with is launching the extension as an iframe inside a content script with a button click. Whereby the background script emits the extension URL to the content script to be set as the iframe's src, something like below.
background.js
browser.runtime.onMessage.addListener((request) => {
if (request.open) {
return new Promise(resolve => {
chrome.browserAction.getPopup({}, (popup) => {
return resolve(popup)
})
})
}
})
content-scipt.js
const i = document.createElement('iframe')
const b = document.createElement('button')
const p = document.getElementById('some-id')
b.innerHTML = 'Open'
b.addEventListener('click', (evt) => {
evt.preventDefault()
chrome.runtime.sendMessage({ open: true }, (response) => {
i.src = response
p.appendChild(i)
})
})
p.appendChild(b)
This opens the extension in the DOM of the page the script is running on. You will also need to add the below to the manifest.
manifest.json
....
"web_accessible_resources": [
"popup.html"
]
....

You could emulate the popup by displaying a fixed html element on the page in the same location the popup would be and style it to look like the popup.

I had the same requirement: When the user clicks on the extension icon a small popup should open. In my case, I was writing an extension which will give updates on selective stocks whenever the icon is clicked. This is how my popup looked.
If you were having the same requirement then please read the answer below.
This is how my manifest.json file looked.
All the heavy lifting was handled by manifest.json file only. There is a section browser_action inside which there is a key called default_popup, just put the name of the HTML file that you want the popup to display.
I wanted my extension to work on all the pages that's why I added the attribute matches under content_scripts. I really didn't need to put the jquery file jquery-3.2.1.js inside the js array but the extension manager was not allowing me to keep that array empty.
Hope this helps, do comment if you have any doubt regarding the answer.

Related

JS Userscript: accessing DOM of new tab

I'm writing a Chrome userscript to open a new tab, then click on some DOM elements. But how can I access the DOM of the new tab?
/* background script */
chrome.browserAction.onClicked.addListener(() => {
chrome.tabs.create({"url": MY_URL, (tab) => {
// What ought I to do?
});
});
You will need to have at least one content script file loaded in your tab (with the right permissions to allow the content script to be loaded).
In the content script, you will have to leverage the Messaging API to communicate with the background page.
If your logic is simple enough, you could even run all logic directly inside content script, as they have access to be same set of chrome API as the background page.

Changing chrome extension html using chrome storage event listener

With this code I want to create an event listener for whenever chrome storage updates.
I want 2 things to happen when the event listener is triggered:
The code will console log the updated values. This part works.
I want the HTML for the extension (the document that opens in the corner when you click the icon) to update and render the data value that is in chrome storage. This is that part I need help with.
chrome.storage.onChanged.addListener(function(changes, namespace) {
//part 1
console.log('New data type is %s. New value is %s',
changes['type'].newValue, changes['data'].newValue)
//part 2
document.getElementById('output').innerHTML =
changes['data'].newValue
});
I realize that calling "document" inside the function doesn't make sense, but I'm unsure how to move forward to get it to render in the extension's HTML.
I tried creating an event listener for when the context menu is accessed (users can update the chrome storage but clicking a button in the context menu) but I couldn't get it to work. Also the event should trigger when chrome storage is updated, not when the context menu is simply accessed.
Right now I get this error:
Error in event handler: TypeError: Cannot set property 'innerHTML' of null
(There is an element with id 'output', so that isn't the problem)
Thanks for your help!
The background script runs in a separate hidden background page. It's not related to the browserAction or pageAction popup page, it doesn't have any of the popup page elements, its DOM is empty except for the auto-generated script tags of the background scripts.
The popup is also a separate page and just like any normal page its environment/DOM exists only when the page is shown. You can't modify it when it's not shown. You can't show it from your code in general case either.
Solution 1
Put that onChanged listener in popup.js script that's loaded in your popup.html (declared as "browser_action": {"default_popup":"popup.html"} in your manifest.json) using the standard <script src="popup.js"></script> tag. It will update the popup page if it's shown, and to display the current values when the popup opens read them with chrome.storage.local.get or chrome.storage.sync.get depending on which storage you're using in your extension.
Solution 2
Use chrome.notifications API to show a small notification at the bottom of the screen, see also the official demo extensions.
Solution 3
Use chrome.browserAction.setBadgeText to display short text like a temperature right under the extension icon. Don't forget to declare at least "browser_action": {} in your manifest.json.

Google Chrome - add to homescreen - force refresh

I have a website with "add to homescreen" enabled - i.e. I have got a manifest.json file with "display": "standalone".
The problem I'm having is when I open the website via the homescreen shortcut, it will resume from when I last accessed it. I have to pull to refresh to make it fetch the latest content.
My question is, is it possible to make it do a refresh every time it is accessed?
If you'd like to take specific action inside of your web app whenever it moves from the "background" to the "foreground" again, you could listen for the appropriate events using the Page Lifecycle API.
The most straightforward way of doing this would probably be to listen for visibilitychange events, and programmatically refresh your data source when you detect that the current visibilityState has transitioned to 'visible'.
This could look like:
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'visible') {
// Your refresh logic goes here.
}
});

Chrome extension - Collect elements from active tab

I am learning to create a chrome extension. To start, I am trying to just gather all links on the page and display them in the popup window of the extension when the button is clicked. I can't seem to get it right. I am able to use messages and send a message from my chrome tab to the extension. But when I try to pass the array of a tags, then it breaks.
My content script:
window.addEventListener('DOMContentLoaded', function () {
chrome.tabs.executeScript(null, {file: "content.js"});
});
My extension script:
window.addEventListener('message', function(e) {
if (event.source != window)
return;
if (event.data.type && (event.data.type == "FROM_PAGE")) {
console.log("Content script received: " + event.data.text);
console.log(event.data.links);
}
}, false);
if I dont do the links, it works fine and sends the messages. So I can't find another way to send all the links to the extension so I can process them. I have this in a github repo here https://github.com/skiftio/chrome-linkman
Your understanding of the architecture is a bit off. Please read through the Overview page, especially the Architecture part.
I'll even include a helpful picture here:
The popup is counted as "other pages" on this picture. It's an HTML page on its own domain (chrome-extension://yourextensionidhere), which is created when you open the popup and destroyed when you close it.
A content script is a script attached to a real Chrome tab; it gets access to its DOM, but is isolated from the page's own scripts. It also has very limited access to Chrome APIs.
More importantly, there are 2 ways of telling Chrome to add a content script to the page: you can declare it in the manifest so that Chrome automatically injects it upon navigation, or you can programmatically inject it from somewhere in your extension pages. You are mixing up those two.
Your manifest entry refers to scripts.js which is NOT a content script and you should not call it such. For instance, chrome.tabs.executeScript is not allowed to be called from a content script, and it will just throw an error. Since you're also injecting the script from the popup, you should just remove the section from the manifest, you don't need it.
As for messaging, you're trying to use window.postMessage, but this is not standard in Chrome extensions.
Take a look at the full Messaging docs, and I recently gave a short overview here.
In your situation, you could add a listener to chrome.runtime.onMessage to the popup and send a message with chrome.runtime.sendMessage from the content script:
/* --- Popup code (scripts.js) --- */
chrome.runtime.onMessage.addListener(function(message, sender, sendResponse){
if(message.links) {
/* do work */
}
});
chrome.tabs.executeScript({file: "content.js"});
/* --- Content script code (content.js) --- */
var links = document.getElementsByTagName("a");
chrome.runtime.sendMessage({links: links});

How can I know if my Chrome Extension is called from a New Tab page?

My Chrome Extension has a popup with a few links, which I would like to be opened in the current tab if it's a New Tab page, or open in a new tab otherwise. So I believe I need to know the active tab's URL. Or is there another way to identify a New Tab?
I'd like to use the "activeTab" permission rather than "tabs" - I want the user to see as few permissions listed as possible.
The only way I've found to identify the tab's URL is by using a background page and
chrome.browserAction.onClicked.addListener(function(tab))
But this is not compatible with having a popup defined in the manifest. I can set the popup page programatically, but I can't see a way to make the popup appear. Is there a way to do that?
When I have default_popup defined in the manifest I use
document.addEventListener('DOMContentLoaded', function ())
to launch the related code, so no reference to the active tab is available. Is there another way to run the code, or to get the active tab?
Thanks.
The activeTab permission allows you to "Get the URL, title, and favicon for that tab via an API that returns a tabs.Tab object". So, to get the current tab URL from the popup you can do:
chrome.tabs.query( {active:true, currentWindow: true}, function(tabs) {
currentUrl = tabs[0].url;
});