There is a way (using optional permissions) to add new extension permissions without disabling the extension for the existing users, while also ensuring new users get prompted to accept both required and optional permissions after installation.
Optional permissions can be requested using chrome.permissions.request API, but it functions only if the request is preceded by a user action. The simplest way to fulfill this requirement and still provide a seamless user experience is to check for the permissions in the background page and request the optional permissions in a popup at runtime.
Permission request can be made in the background page right after loading the extension (after installation and subsequent browser launches). Any existing user missing any of the permissions will be prompted to accept the permission after relaunching the browser.
The following example includes:
- The background page JavaScript (background_page.js)
- Notification popup html file (notification_popup.html)
- Notification popup JavaScript (notification_popup.js)
You will also need declare the "optional_permissions" in manifest.json for the example to work. The example will work with both a Chrome Extension and a Firefox Add-on.
background_page.js
var brwsr = null;
if (typeof chrome != "undefined") {
brwsr = chrome;
}
else{
brwsr = browser;
}
var opt_perms = brwsr.runtime.getManifest().optional_permissions;
var requiredOptionalPermissions = {}
if(typeof opt_perms!="undefined" && opt_perms.length>0){
var perms = []
var origins = []
var re = new RegExp("^(http|https)://", "i");
for(var i=0; i<opt_perms.length; i++){
if(opt_perms[i]==="<all_urls>"|| re.test(opt_perms[i])){
origins.push(opt_perms[i])
}
else{
perms.push(opt_perms[i])
}
}
if(perms.length>0){
requiredOptionalPermissions.permissions = perms
}
if(origins.length>0){
requiredOptionalPermissions.origins = origins
}
}
var requiresPermission = ()=>{
// add your code here
}
// check if optional permission exists and request if not
var runFunctionsRequiringOptPermissions = function(requiredPermissions={}, userGesture=false, callback=()=>{}){
if(typeof requiredPermissions.permissions!="undefined" || typeof requiredPermissions.origins!="undefined"){
brwsr.permissions.contains(requiredPermissions, function(res) {
if (!res) {
// The extension doesn't have the permissions.)
if(userGesture){
brwsr.permissions.request(requiredPermissions, function(granted){
if(granted){
// perform actions that required the permission
requiresPermission()
}
callback();
});
return;
}
// open the notification popup
window.open("notification_popup.html", "extension_popup", "width=530,height=190,status=no,scrollbars=yes,resizable=no,screenY=+"+(screen.height/2-95)+",screenX="+(screen.width/2-265));
}
else{
// perform actions that required the permission
requiresPermission()
}
});
}
}
runFunctionsRequiringOptPermissions(requiredOptionalPermissions)
// add listener
brwsr.runtime.onConnect.addListener(function(port) {
if(port.name == "optionalPermRequestPopup"){
port.onMessage.addListener(
function(request, sender, sendResponse) {
if (request.okButtonClicked === true) {
runFunctionsRequiringOptPermissions(requiredOptionalPermissions, true, function(){
// callback to close the notification popup
port.postMessage({'close':true});
})
return;
}
port.postMessage({'close':true})
}
);
}
});
notification_popup.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Extension Name</title>
<style>
.container {
font-size:medium;
text-align:center;
}
.button{
height: 30px;
width: 70px;
background-color: #448be9;
color: white;
border-color: #448be9;
border-width: thin;
margin-right: 10px;
float: right;
}
.btnCancel{
background-color: white;
color: #448be9
}
</style>
</head>
<body>
<div class="container">
<p class='text'>Please upgrade the permissions to enable the latest features.</p>
<button class="button btnCancel">Cancel</button>
<button class="button btnOk">OK</button>
</div>
<script src="notification_popup.js"></script>
</body>
</html>
notification_popup.js
var brwsr = null;
if (typeof chrome != "undefined") {
brwsr = chrome;
}
else{
brwsr = browser;
}
var port = brwsr.runtime.connect({
name : "optionalPermRequestPopup"
});
window.addEventListener("load", function(event) {
var okButtons = document.querySelectorAll(".btnOk");
okButtons.forEach(function(okButton) {
okButton.addEventListener("click", function(event) {
port.postMessage({okButtonClicked: true})
});
});
port.onMessage.addListener(function(request) {
for (var key in request) {
switch(key){
case "close":
window.close();
break;
default:
break;
}
}
});
});