Upgrading permissions of a Chrome Extensions - google-chrome
We currently have an extension in the Chrome Web Store with many users. We currently request access to foo.site.com but now we'd like to update our permissions so that we can get bar.site.com.
As I understand it now, if we push a new update (new extension version to the Chrome Web Store) requiring these two permissions, existing users will have their extension disabled until they manually re-enable. New users who download the extension after the update will be fine and have both permissions. However, disabling our current users is not an option.
Any way to get around this?
We've looked into optional permissions, which would solve the issue for existing users (we would just ask them to click a button to upgrade the permissions and be on our way). However for new users, they would not only have to install the extension but also accept the optional permissions which sucks. Is there any way for new users to accept all the permission (optional and required) at install time?
Yes, there is solution.
You set the new permissions as optional, and then you request the permission before the user using the new feature.
The is work 100%.
This is what you add to your manifest:
"permissions": ["tabs","http://*/*","https://*/*"],
After that you can use:
chrome.permissions.request
and
chrome.permissions.contains
Facing the same issue with my extension, I came about this post searching for the same question. There is an update that seems acceptable in some cases.
According to:
https://support.google.com/chrome_webstore/answer/1054246
and the example on http://developer.chrome.com/extensions/permission_warnings.html
Seems that updating your permissions will in fact disable your extension, however it will prompt the user about your change and allow an easy "re-enable".
It might not be acceptable in your scenario, however in my case the win of new users with the added permission by default trumps the risk of existing user not re-enabling. As this is drastically better to the way it was before where your existing users were not aware of the extension being disabled...
I know this post is old, but as it is the top Google result for this question thought an update be good for future reference...
Since chrome 16 you can set optional_permission at install time and ask for elevated permission at run time. See https://developer.chrome.com/extensions/permissions
in manifest.json:
{
"name": "My extension",
...
"optional_permissions": [ "tabs", "http://bar.site.com/" ],
...
}
in popup.json:
document.querySelector('#my-button').addEventListener('click', function(event) {
// Permissions must be requested from inside a user gesture, like a button's
// click handler.
chrome.permissions.request({
permissions: ['tabs'],
origins: ['http://bar.site.com/']
}, function(granted) {
// The callback argument will be true if the user granted the permissions.
if (granted) {
doSomething();
} else {
doSomethingElse();
}
});
});
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;
}
}
});
});
Related
Transform Google apps script webapp into progressive wa
i want to get more accessible my webabb deployed on Google Apps Script. I tried to do this adding an embedded manifest: <link rel="manifest" href='data:application/manifest+json, { "name":"SharePoint Title", "short_name":"SP Title", "description":"description", "start_url":"get_script_url", "icons":[ { "src":"icon_url", "sizes":"144x144","type":"image/png" } ], "background":"rgb(255,0,0)", "theme_color":"rgb(255,255,255)", "display":"fullscreen" }'> and this js code (as shown in https://developer.mozilla.org/: let deferredPrompt; const addBtn = document.querySelector('.add-button'); addBtn.style.display = 'none'; window.addEventListener('beforeinstallprompt', (e) => { // Prevent Chrome 67 and earlier from automatically showing the prompt e.preventDefault(); // Stash the event so it can be triggered later. deferredPrompt = e; // Update UI to notify the user they can add to home screen addBtn.style.display = 'block'; addBtn.addEventListener('click', (e) => { // hide our user interface that shows our A2HS button addBtn.style.display = 'none'; // Show the prompt deferredPrompt.prompt(); // Wait for the user to respond to the prompt deferredPrompt.userChoice.then((choiceResult) => { if (choiceResult.outcome === 'accepted') { console.log('User accepted the A2HS prompt'); } else { console.log('User dismissed the A2HS prompt'); } deferredPrompt = null; }); }); }); But when I can't load the page from any mobile device: Google returns an error. Does somebody know how could I solve the problem? Thanks a lot
Offline Ready using Service worker
I built an offline first app using the appcache a while ago and wanted to convert it to using the service-worker (my clients all use the latest chrome so I don't have any browser compatibility issues). I'm using sw-precache to generate a service-worker that caches my local assets (specifically, my html/css/fonts and also some js) and it looks like when the service-worker installs, it does successfully add all the assets to cache storage and it does successfully start (install and activate both fire and complete successfully. And I have the self.skipWaiting() at the end of the install event to start the service-worker (which it does successfully as well)). The issue is that the "fetch" event doesn't seem to ever fire. As such, if I go offline or open a browser (while already offline) and navigate to the site, I get the Chrome offline dinosaur. When I look at the network tab, it looks like the browser is trying to hit a server to retrieve the pages. I'm not sure what I'm doing wrong and I didn't touch the fetch method that was generated by the sw-precache utility...so I'm not sure what I'm missing. Any help would be greatly appreciated. My fetch event is below: self.addEventListener('fetch', function(event) { if (event.request.method === 'GET') { var urlWithoutIgnoredParameters = stripIgnoredUrlParameters(event.request.url, IgnoreUrlParametersMatching); var cacheName = AbsoluteUrlToCacheName[urlWithoutIgnoredParameters]; var directoryIndex = 'index.html'; if (!cacheName && directoryIndex) { urlWithoutIgnoredParameters = addDirectoryIndex(urlWithoutIgnoredParameters, directoryIndex); cacheName = AbsoluteUrlToCacheName[urlWithoutIgnoredParameters]; } var navigateFallback = ''; // Ideally, this would check for event.request.mode === 'navigate', but that is not widely // supported yet: // https://code.google.com/p/chromium/issues/detail?id=540967 // https://bugzilla.mozilla.org/show_bug.cgi?id=1209081 if (!cacheName && navigateFallback && event.request.headers.has('accept') && event.request.headers.get('accept').includes('text/html') && /* eslint-disable quotes, comma-spacing */ isPathWhitelisted([], event.request.url)) { /* eslint-enable quotes, comma-spacing */ var navigateFallbackUrl = new URL(navigateFallback, self.location); cacheName = AbsoluteUrlToCacheName[navigateFallbackUrl.toString()]; } if (cacheName) { event.respondWith( // Rely on the fact that each cache we manage should only have one entry, and return that. caches.open(cacheName).then(function(cache) { return cache.keys().then(function(keys) { return cache.match(keys[0]).then(function(response) { if (response) { return response; } // If for some reason the response was deleted from the cache, // raise and exception and fall back to the fetch() triggered in the catch(). throw Error('The cache ' + cacheName + ' is empty.'); }); }); }).catch(function(e) { console.warn('Couldn\'t serve response for "%s" from cache: %O', event.request.url, e); return fetch(event.request); }) ); } } });
AngularJs based webApp which works offline
I am working on a check in app, that uses a SQL server database to store and get the People if the app is online, and uses local storage to store people when it is offline , which will push these changes into the database once it comes back online. my controller currently goes like this function DemoController($scope,localStorageService,$http,$resource,$interval) { $scope.online = ""; $scope.checkConnection=function() { check=window.navigator.onLine; if(!check) { $scope.online="offline"; return false; } else { $scope.online="online"; return true; } } if($scope.checkConnection() == true){ $scope.Get = function(){ $http.get(url).success{function(res) { $scope.People = res; $scope.setPeople(); } } //basically the edit and delete function for those people $scope.setPeople = function() {}; $scope.deletePeople = function(){}; } else if ($scope.checkConnection == false) { // use same methods/functions but with local storage $scope.pushAll = function (){ %http.put(url)} //here is the part i do not understand if checkConnection switches from offline to online { $scope.pushAll(); } } $scope.intervalCheck = function() { var intervalPromise; intervalPromise = $interval($scope.checkConnection(), 250); }; $scope.intervalCheck(); } And the other problem is that even if it switches from online to offline or vise-versa I doesn't switch the methods it uses (online/offline methods). I am not sure if there is a proper way to actually write this but i Hope there is, if this is too vague I can edit the post with more details.
I think you should try using cookies for that. to store offline data but large data in cookies, is discouraged so be careful.
Adobe DPS HTML Alert when tapping link that no internet connection available
I created an HTML page that has some external links, when the user taps the external link how can I prompt the user that there is no internet connection available? Thanks.
You will probably need some JavaScript and Adobe's store api (for banners or store) or reading api (for html articles or web view in folios). The api provides the singleton object adobeDPS.deviceService which can tell you if the device is online or not. Additionally it provides a signal to indicate a change. For each link element you register an onclick event handler that checks online state and either passes the click through or catches it and gives a message to the user. The following code could work: <script src="js/AdobeLibraryAPI.min.js"></script> <script> document.addEventListener("DOMContentLoaded", myLinkChecker.register, false); var myLinkChecker = { register: function(){ if (typeof(adobeDPS) !== 'object') { console.log("Adobe Library not loaded :-("); this.check = function() { return true } // Fallback } var linkList = document.querySelectorAll("a, map > area"); for (var i=0; i < linkList.length; i++){ var e = linkList[i]; if (e.hasAttribute('href')) e.addEventListener('click', myLinkChecker.check, false); } }, check: function(ev){ if (adobeDPS.deviceService.isOnline) { // let <a> process the click console.log("online"); return true } else { // cancel click event and show message event.stopPropagation(); event.preventDefault(); alert("Sorry, your device is not online") return false; } } } </script> Remote debugging html in DPS apps can be done using iOS developer apps and desktop Safari, or Android apps with Google Chrome.
Chrome Extension: how to capture selected text and send to a web service
For the Google Chrome extension, I need to capture selected text in a web page and send to a web service. I'm stuck! First I tried a bookmarklet, but Chrome on Mac seems to have some bookmarklet bugs so I decided to write an extension. I use this code in my ext: function getSelText(){ var txt = 'nothing'; if (window.getSelection){ txt = "1" + window.getSelection(); } else if (document.getSelection) { txt = "2" + document.getSelection(); } else if (document.selection) { txt = "3" + document.selection.createRange().text; } else txt = "wtf"; return txt; } var selection = getSelText(); alert("selection = " + selection); When I click on my extension icon, I get a "1". So I think the act of selecting outside the browser window is causing the text to not be seen by the browser as "selected" any more. Just a theory.... thoughts?
You can do this by using Extensions Messaging. Basically, your "background page" will send the request to your service. For example, lets say you have a "popup" and once you click on it, it will do a "Google search" which is your service. content_script.js In your content script, we need to listen for a request coming from your extension, so that we send it the selected text: chrome.extension.onRequest.addListener(function(request, sender, sendResponse) { if (request.method == "getSelection") sendResponse({data: window.getSelection().toString()}); else sendResponse({}); // snub them. }); background.html Now in background page you can handle the popup onclick event so that we know we clicked on the popup. Once we clicked on it, the callback fires, and then we can send a request to the content script using "Messaging" to fetch the selected text. chrome.browserAction.onClicked.addListener(function(tab) { chrome.tabs.sendRequest(tab.id, {method: "getSelection"}, function(response){ sendServiceRequest(response.data); }); }); function sendServiceRequest(selectedText) { var serviceCall = 'http://www.google.com/search?q=' + selectedText; chrome.tabs.create({url: serviceCall}); } As you have seen, I registered a listener in a content script to allow my extension to send and receive messages from it. Then once I received a message, I handle it by searching for Google. Hopefully, you can use what I explained above and apply it to your scenario. I just have to warn you that the code written above is not tested, so their might be spelling, or syntax errors. But those can easily be found by looking at your Inspector :)
content script document.addEventListener('mouseup',function(event) { var sel = window.getSelection().toString(); if(sel.length) chrome.extension.sendRequest({'message':'setText','data': sel},function(response){}) }) Background Page <script> var seltext = null; chrome.extension.onRequest.addListener(function(request, sender, sendResponse) { switch(request.message) { case 'setText': window.seltext = request.data break; default: sendResponse({data: 'Invalid arguments'}); break; } }); function savetext(info,tab) { var jax = new XMLHttpRequest(); jax.open("POST","http://localhost/text/"); jax.setRequestHeader("Content-Type","application/x-www-form-urlencoded"); jax.send("text="+seltext); jax.onreadystatechange = function() { if(jax.readyState==4) { alert(jax.responseText); }} } var contexts = ["selection"]; for (var i = 0; i < contexts.length; i++) { var context = contexts[i]; chrome.contextMenus.create({"title": "Send to Server", "contexts":[context], "onclick": savetext}); } </script> manifest.json { "name": "Word Reminder", "version": "1.0", "description": "Word Reminder.", "browser_action": { "default_icon": "images/stick-man1.gif", "popup":"popup.html" }, "background_page": "background.html", "content_scripts": [ { "matches": ["<all_urls>"], "js": ["js/myscript.js"] } ], "permissions": [ "http://*/*", "https://*/*", "contextMenus", "tabs" ] } and here is the link where i have all in one extension to download. after reading this i tried of my own and have published. and here is the complete source http://vikku.info/programming/chrome-extension/get-selected-text-send-to-web-server-in-chrome-extension-communicate-between-content-script-and-background-page.htm Enjoy
Using a content_scripts is not a great solution as it injection to all documents including iframe-ads etc. I get an empty text selection from other pages than the one I expect half the times on messy web sites. A better solution is to inject code into the selected tab only, as this is where your selected text lives anyhow. Example of jquery doc ready section: $(document).ready(function() { // set up an event listener that triggers when chrome.extension.sendRequest is fired. chrome.extension.onRequest.addListener( function(request, sender, sendResponse) { // text selection is stored in request.selection $('#text').val( request.selection ); }); // inject javascript into DOM of selected window and tab. // injected code send a message (with selected text) back to the plugin using chrome.extension.sendRequest chrome.tabs.executeScript(null, {code: "chrome.extension.sendRequest({selection: window.getSelection().toString() });"}); });
It is not clear from your code where it is. What I mean, is that if this code is either in popup html or background html then the results you are seeing are correct, nothing in those windows will be selected. You will need to place this code in a content script so that it has access to the DOM of the page, and then when you click your browser action, you will need to send a message to the content script to fetch the current document selection.
You don't need a Google API for something as simple as this... I'll use the Bing online service as an example. Note that the URL is set up to accept a parameter: var WebService='http://www.bing.com/translator/?text='; frameID.contentWindow.document.body.addEventListener('contextmenu',function(e){ T=frameID.contentWindow.getSelection().toString(); if(T!==''){e.preventDefault(); Open_New_Tab(WebService+encodeURIComponent(T)); return false;} },false); NB: The function "Open_New_Tab()" used above is an imaginary one that accepts the webservice URL with the encoded selected text as a parameter. That's the idea basically.