Bug 1369107 - 2. Ask for app location permission from JS; r=esawin

For location permission, we first show a prompt for the website, then on
higher Android versions, we show a prompt for the app. For the app
prompt, right now we show that from GeckoView code in GeckoAppShell.
However, if we move the app prompt into ContentPermissionPrompt.js
alongside the website prompt, we can then remove the code in
GeckoAppShell and eliminate this app permission dependency from
GeckoView code.

MozReview-Commit-ID: GSCqClPROwn
This commit is contained in:
Jim Chen 2017-06-02 16:13:41 -04:00
parent 5674b33e77
commit 7eab0ee43f
3 changed files with 78 additions and 53 deletions

View File

@ -7,8 +7,9 @@ const Cr = Components.results;
const Cu = Components.utils;
const Cc = Components.classes;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/RuntimePermissions.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
const kEntities = {
"contacts": "contacts",
@ -31,20 +32,20 @@ ContentPermissionPrompt.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPermissionPrompt]),
handleExistingPermission: function handleExistingPermission(request, type, denyUnknown) {
handleExistingPermission: function handleExistingPermission(request, type, denyUnknown, callback) {
let result = Services.perms.testExactPermissionFromPrincipal(request.principal, type);
if (result == Ci.nsIPermissionManager.ALLOW_ACTION) {
request.allow();
callback(/* allow */ true);
return true;
}
if (result == Ci.nsIPermissionManager.DENY_ACTION) {
request.cancel();
callback(/* allow */ false);
return true;
}
if (denyUnknown && result == Ci.nsIPermissionManager.UNKNOWN_ACTION) {
request.cancel();
callback(/* allow */ false);
return true;
}
@ -79,19 +80,37 @@ ContentPermissionPrompt.prototype = {
request.cancel();
return;
}
let perm = types.queryElementAt(0, Ci.nsIContentPermissionType);
let callback = (allow) => {
if (!allow) {
request.cancel();
return;
}
if (perm.type === "geolocation") {
RuntimePermissions.waitForPermissions(
RuntimePermissions.ACCESS_FINE_LOCATION).then((granted) => {
(granted ? request.allow : request.cancel)();
});
return;
}
request.allow();
};
// Returns true if the request was handled
let access = (perm.access && perm.access !== "unused") ?
(perm.type + "-" + perm.access) : perm.type;
if (this.handleExistingPermission(request, access,
/* denyUnknown */ isApp || PROMPT_FOR_UNKNOWN.indexOf(perm.type) < 0))
/* denyUnknown */ isApp || PROMPT_FOR_UNKNOWN.indexOf(perm.type) < 0, callback)) {
return;
}
let chromeWin = this.getChromeForRequest(request);
let tab = chromeWin.BrowserApp.getTabForWindow(request.window.top);
if (!tab)
if (!tab) {
return;
}
let browserBundle = Services.strings.createBundle("chrome://browser/locale/browser.properties");
let entityName = kEntities[perm.type];
@ -103,7 +122,7 @@ ContentPermissionPrompt.prototype = {
if (aChecked || entityName == "desktopNotification2")
Services.perms.addFromPrincipal(request.principal, access, Ci.nsIPermissionManager.DENY_ACTION);
request.cancel();
callback(/* allow */ false);
}
},
{
@ -117,7 +136,7 @@ ContentPermissionPrompt.prototype = {
Services.perms.addFromPrincipal(request.principal, access, Ci.nsIPermissionManager.ALLOW_ACTION, Ci.nsIPermissionManager.EXPIRE_SESSION);
}
request.allow();
callback(/* allow */ true);
},
positive: true
}];

View File

@ -285,7 +285,8 @@ public class GeckoAppShell
return (location.hasAccuracy() && radius > 0) ? radius : 1001;
}
@SuppressLint("MissingPermission") // Permissions are explicitly checked for in enableLocation()
// Permissions are explicitly checked when requesting content permission.
@SuppressLint("MissingPermission")
private static Location getLastKnownLocation(LocationManager lm) {
Location lastKnownLocation = null;
List<String> providers = lm.getAllProviders();
@ -313,55 +314,58 @@ public class GeckoAppShell
}
@WrapForJNI(calledFrom = "gecko")
@SuppressLint("MissingPermission") // Permissions are explicitly checked for within this method
private static void enableLocation(final boolean enable) {
final Runnable requestLocation = new Runnable() {
@Override
public void run() {
LocationManager lm = getLocationManager(getApplicationContext());
if (lm == null) {
return;
// Permissions are explicitly checked when requesting content permission.
@SuppressLint("MissingPermission")
/* package */ static void enableLocation(final boolean enable) {
if (!ThreadUtils.isOnUiThread()) {
ThreadUtils.postToUiThread(new Runnable() {
@Override
public void run() {
try {
enableLocation(enable);
} catch (final SecurityException e) {
Log.e(LOGTAG, "No location permission", e);
}
}
});
return;
}
if (!enable) {
lm.removeUpdates(getLocationListener());
return;
}
LocationManager lm = getLocationManager(getApplicationContext());
if (lm == null) {
return;
}
Location lastKnownLocation = getLastKnownLocation(lm);
if (lastKnownLocation != null) {
getLocationListener().onLocationChanged(lastKnownLocation);
}
if (!enable) {
lm.removeUpdates(getLocationListener());
return;
}
Criteria criteria = new Criteria();
criteria.setSpeedRequired(false);
criteria.setBearingRequired(false);
criteria.setAltitudeRequired(false);
if (locationHighAccuracyEnabled) {
criteria.setAccuracy(Criteria.ACCURACY_FINE);
criteria.setCostAllowed(true);
criteria.setPowerRequirement(Criteria.POWER_HIGH);
} else {
criteria.setAccuracy(Criteria.ACCURACY_COARSE);
criteria.setCostAllowed(false);
criteria.setPowerRequirement(Criteria.POWER_LOW);
}
Location lastKnownLocation = getLastKnownLocation(lm);
if (lastKnownLocation != null) {
getLocationListener().onLocationChanged(lastKnownLocation);
}
String provider = lm.getBestProvider(criteria, true);
if (provider == null)
return;
Criteria criteria = new Criteria();
criteria.setSpeedRequired(false);
criteria.setBearingRequired(false);
criteria.setAltitudeRequired(false);
if (locationHighAccuracyEnabled) {
criteria.setAccuracy(Criteria.ACCURACY_FINE);
criteria.setCostAllowed(true);
criteria.setPowerRequirement(Criteria.POWER_HIGH);
} else {
criteria.setAccuracy(Criteria.ACCURACY_COARSE);
criteria.setCostAllowed(false);
criteria.setPowerRequirement(Criteria.POWER_LOW);
}
Looper l = Looper.getMainLooper();
lm.requestLocationUpdates(provider, 100, 0.5f, getLocationListener(), l);
}
};
String provider = lm.getBestProvider(criteria, true);
if (provider == null)
return;
Permissions
.from((Activity) getContext())
.withPermissions(Manifest.permission.ACCESS_FINE_LOCATION)
.onUIThread()
.doNotPromptIf(!enable)
.run(requestLocation);
Looper l = Looper.getMainLooper();
lm.requestLocationUpdates(provider, 100, 0.5f, getLocationListener(), l);
}
private static LocationManager getLocationManager(Context context) {

View File

@ -11,11 +11,13 @@ this.EXPORTED_SYMBOLS = ["RuntimePermissions"];
Cu.import("resource://gre/modules/Services.jsm");
// See: http://developer.android.com/reference/android/Manifest.permission.html
const ACCESS_FINE_LOCATION = "android.permission.ACCESS_FINE_LOCATION";
const CAMERA = "android.permission.CAMERA";
const WRITE_EXTERNAL_STORAGE = "android.permission.WRITE_EXTERNAL_STORAGE";
const RECORD_AUDIO = "android.permission.RECORD_AUDIO";
const WRITE_EXTERNAL_STORAGE = "android.permission.WRITE_EXTERNAL_STORAGE";
var RuntimePermissions = {
ACCESS_FINE_LOCATION: ACCESS_FINE_LOCATION,
CAMERA: CAMERA,
RECORD_AUDIO: RECORD_AUDIO,
WRITE_EXTERNAL_STORAGE: WRITE_EXTERNAL_STORAGE,