Bug 1297475 - Move content permission prompts into a JSM and add an Integration. r=Paolo

MozReview-Commit-ID: Dq3I9pzcdyY

--HG--
extra : rebase_source : a5d5008631ba85762c3c265552f42e50266bd9b8
This commit is contained in:
Mike Conley 2016-08-27 23:11:07 -04:00
parent 0e1c4d7b3a
commit cb6b23a1c2
3 changed files with 667 additions and 284 deletions

View File

@ -37,6 +37,7 @@ XPCOMUtils.defineLazyServiceGetter(this, "AlertsService", "@mozilla.org/alerts-s
["Feeds", "resource:///modules/Feeds.jsm"],
["FileUtils", "resource://gre/modules/FileUtils.jsm"],
["FormValidationHandler", "resource:///modules/FormValidationHandler.jsm"],
["Integration", "resource://gre/modules/Integration.jsm"],
["LightweightThemeManager", "resource://gre/modules/LightweightThemeManager.jsm"],
["LoginHelper", "resource://gre/modules/LoginHelper.jsm"],
["LoginManagerParent", "resource://gre/modules/LoginManagerParent.jsm"],
@ -46,6 +47,7 @@ XPCOMUtils.defineLazyServiceGetter(this, "AlertsService", "@mozilla.org/alerts-s
["OS", "resource://gre/modules/osfile.jsm"],
["PageThumbs", "resource://gre/modules/PageThumbs.jsm"],
["PdfJs", "resource://pdf.js/PdfJs.jsm"],
["PermissionUI", "resource:///modules/PermissionUI.jsm"],
["PlacesBackups", "resource://gre/modules/PlacesBackups.jsm"],
["PlacesUtils", "resource://gre/modules/PlacesUtils.jsm"],
["PluralForm", "resource://gre/modules/PluralForm.jsm"],
@ -2431,6 +2433,49 @@ BrowserGlue.prototype = {
_xpcom_factory: BrowserGlueServiceFactory,
}
/**
* ContentPermissionIntegration is responsible for showing the user
* simple permission prompts when content requests additional
* capabilities.
*
* While there are some built-in permission prompts, createPermissionPrompt
* can also be overridden by system add-ons or tests to provide new ones.
*
* This override ability is provided by Integration.jsm. See
* PermissionUI.jsm for an example of how to provide a new prompt
* from an add-on.
*/
const ContentPermissionIntegration = {
/**
* Creates a PermissionPrompt for a given permission type and
* nsIContentPermissionRequest.
*
* @param {string} type
* The type of the permission request from content. This normally
* matches the "type" field of an nsIContentPermissionType, but it
* can be something else if the permission does not use the
* nsIContentPermissionRequest model. Note that this type might also
* be different from the permission key used in the permissions
* database.
* Example: "geolocation"
* @param {nsIContentPermissionRequest} request
* The request for a permission from content.
* @return {PermissionPrompt} (see PermissionUI.jsm),
* or undefined if the type cannot be handled.
*/
createPermissionPrompt(type, request) {
switch (type) {
case "geolocation": {
return new PermissionUI.GeolocationPermissionPrompt(request);
}
case "desktop-notification": {
return new PermissionUI.DesktopNotificationPermissionPrompt(request);
}
}
return undefined;
},
};
function ContentPermissionPrompt() {}
ContentPermissionPrompt.prototype = {
@ -2438,301 +2483,50 @@ ContentPermissionPrompt.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPermissionPrompt]),
_getBrowserForRequest: function (aRequest) {
// "element" is only defined in e10s mode.
let browser = aRequest.element;
if (!browser) {
// Find the requesting browser.
browser = aRequest.window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell)
.chromeEventHandler;
}
return browser;
},
/**
* Show a permission prompt.
* This implementation of nsIContentPermissionPrompt.prompt ensures
* that there's only one nsIContentPermissionType in the request,
* and that it's of type nsIContentPermissionType. Failing to
* satisfy either of these conditions will result in this method
* throwing NS_ERRORs. If the combined ContentPermissionIntegration
* cannot construct a prompt for this particular request, an
* NS_ERROR_FAILURE will be thrown.
*
* @param aRequest The permission request.
* @param aMessage The message to display on the prompt.
* @param aPermission The type of permission to prompt.
* @param aActions An array of actions of the form:
* [main action, secondary actions, ...]
* Actions are of the form { stringId, action, expireType, callback }
* Permission is granted if action is null or ALLOW_ACTION.
* @param aNotificationId The id of the PopupNotification.
* @param aAnchorId The id for the PopupNotification anchor.
* @param aOptions Options for the PopupNotification
* Any time an error is thrown, the nsIContentPermissionRequest is
* cancelled automatically.
*
* @param {nsIContentPermissionRequest} request
* The request that we're to show a prompt for.
*/
_showPrompt: function CPP_showPrompt(aRequest, aMessage, aPermission, aActions,
aNotificationId, aAnchorId, aOptions) {
var browser = this._getBrowserForRequest(aRequest);
var chromeWin = browser.ownerGlobal;
var requestPrincipal = aRequest.principal;
// Transform the prompt actions into PopupNotification actions.
var popupNotificationActions = [];
for (var i = 0; i < aActions.length; i++) {
let promptAction = aActions[i];
// Don't offer action in PB mode if the action remembers permission for more than a session.
if (PrivateBrowsingUtils.isWindowPrivate(chromeWin) &&
promptAction.expireType != Ci.nsIPermissionManager.EXPIRE_SESSION &&
promptAction.action) {
continue;
prompt(request) {
try {
// Only allow exactly one permission request here.
let types = request.types.QueryInterface(Ci.nsIArray);
if (types.length != 1) {
throw Components.Exception(
"Expected an nsIContentPermissionRequest with only 1 type.",
Cr.NS_ERROR_UNEXPECTED);
}
var action = {
label: gBrowserBundle.GetStringFromName(promptAction.stringId),
accessKey: gBrowserBundle.GetStringFromName(promptAction.stringId + ".accesskey"),
callback: function() {
if (promptAction.callback) {
promptAction.callback();
}
let type = types.queryElementAt(0, Ci.nsIContentPermissionType).type;
let combinedIntegration =
Integration.contentPermission.getCombined(ContentPermissionIntegration);
// Remember permissions.
if (promptAction.action) {
Services.perms.addFromPrincipal(requestPrincipal, aPermission,
promptAction.action, promptAction.expireType);
}
// Grant permission if action is null or ALLOW_ACTION.
if (!promptAction.action || promptAction.action == Ci.nsIPermissionManager.ALLOW_ACTION) {
aRequest.allow();
} else {
aRequest.cancel();
}
},
};
popupNotificationActions.push(action);
}
var mainAction = popupNotificationActions.length ?
popupNotificationActions[0] : null;
var secondaryActions = popupNotificationActions.splice(1);
// Only allow exactly one permission request here.
let types = aRequest.types.QueryInterface(Ci.nsIArray);
if (types.length != 1) {
aRequest.cancel();
return undefined;
}
if (!aOptions)
aOptions = {};
aOptions.displayURI = requestPrincipal.URI;
return chromeWin.PopupNotifications.show(browser, aNotificationId, aMessage, aAnchorId,
mainAction, secondaryActions, aOptions);
},
_promptGeo : function(aRequest) {
var secHistogram = Services.telemetry.getHistogramById("SECURITY_UI");
var message;
// Share location action.
var actions = [{
stringId: "geolocation.shareLocation",
action: null,
expireType: null,
callback: function() {
secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_GEOLOCATION_REQUEST_SHARE_LOCATION);
},
}];
let options = {
learnMoreURL: Services.urlFormatter.formatURLPref("browser.geolocation.warning.infoURL"),
};
if (aRequest.principal.URI.schemeIs("file")) {
message = gBrowserBundle.GetStringFromName("geolocation.shareWithFile2");
} else {
message = gBrowserBundle.GetStringFromName("geolocation.shareWithSite2");
// Always share location action.
actions.push({
stringId: "geolocation.alwaysShareLocation",
action: Ci.nsIPermissionManager.ALLOW_ACTION,
expireType: null,
callback: function() {
secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_GEOLOCATION_REQUEST_ALWAYS_SHARE);
},
});
// Never share location action.
actions.push({
stringId: "geolocation.neverShareLocation",
action: Ci.nsIPermissionManager.DENY_ACTION,
expireType: null,
callback: function() {
secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_GEOLOCATION_REQUEST_NEVER_SHARE);
},
});
}
secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_GEOLOCATION_REQUEST);
this._showPrompt(aRequest, message, "geo", actions, "geolocation",
"geo-notification-icon", options);
},
_promptFlyWebPublishServer : function(aRequest) {
var message = "Would you like to let this site start a server accessible to nearby devices and people?";
var actions = [
{
stringId: "flyWebPublishServer.allowPublishServer",
action: Ci.nsIPermissionManager.ALLOW_ACTION,
expireType: Ci.nsIPermissionManager.EXPIRE_SESSION
},
{
stringId: "flyWebPublishServer.denyPublishServer",
action: Ci.nsIPermissionManager.DENY_ACTION,
expireType: Ci.nsIPermissionManager.EXPIRE_SESSION
let permissionPrompt =
combinedIntegration.createPermissionPrompt(type, request);
if (!permissionPrompt) {
throw Components.Exception(
`Failed to handle permission of type ${type}`,
Cr.NS_ERROR_FAILURE);
}
];
let options = {
learnMoreURL: "https://flyweb.github.io",
popupIconURL: "chrome://flyweb/skin/icon-64.png"
};
let browser = this._getBrowserForRequest(aRequest);
let chromeDoc = browser.ownerDocument;
let iconElem = chromeDoc.getElementById("flyweb-publish-server-notification-icon");
if (!iconElem) {
let notificationPopupBox = chromeDoc.getElementById("notification-popup-box");
let notificationIcon = chromeDoc.createElement("image");
notificationIcon.setAttribute("id", "flyweb-publish-server-notification-icon");
notificationIcon.setAttribute("src", "chrome://flyweb/skin/icon-64.png");
notificationIcon.setAttribute("class", "notification-anchor-icon flyweb-publish-server-icon");
notificationIcon.setAttribute("style", "filter: url(chrome://browser/skin/filters.svg#fill); fill: currentColor; opacity: .4;");
notificationIcon.setAttribute("role", "button");
notificationIcon.setAttribute("aria-label", "View the publish-server request");
notificationPopupBox.appendChild(notificationIcon);
}
this._showPrompt(aRequest, message, "flyweb-publish-server", actions, "flyweb-publish-server",
"flyweb-publish-server-notification-icon", options);
},
_promptWebNotifications : function(aRequest) {
var message = gBrowserBundle.GetStringFromName("webNotifications.receiveFromSite");
var actions;
var browser = this._getBrowserForRequest(aRequest);
// Only show "allow for session" in PB mode, we don't
// support "allow for session" in non-PB mode.
if (PrivateBrowsingUtils.isBrowserPrivate(browser)) {
actions = [
{
stringId: "webNotifications.receiveForSession",
action: Ci.nsIPermissionManager.ALLOW_ACTION,
expireType: Ci.nsIPermissionManager.EXPIRE_SESSION,
callback: function() {},
}
];
} else {
actions = [
{
stringId: "webNotifications.alwaysReceive",
action: Ci.nsIPermissionManager.ALLOW_ACTION,
expireType: null,
callback: function() {},
},
{
stringId: "webNotifications.neverShow",
action: Ci.nsIPermissionManager.DENY_ACTION,
expireType: null,
callback: function() {},
},
];
}
var options = {
learnMoreURL:
Services.urlFormatter.formatURLPref("app.support.baseURL") + "push",
eventCallback(type) {
if (type == "dismissed") {
// Bug 1259148: Hide the doorhanger icon. Unlike other permission
// doorhangers, the user can't restore the doorhanger using the icon
// in the location bar. Instead, the site will be notified that the
// doorhanger was dismissed.
this.remove();
aRequest.cancel();
}
},
};
this._showPrompt(aRequest, message, "desktop-notification", actions,
"web-notifications",
"web-notifications-notification-icon", options);
},
prompt: function CPP_prompt(request) {
// Only allow exactly one permission request here.
let types = request.types.QueryInterface(Ci.nsIArray);
if (types.length != 1) {
permissionPrompt.prompt();
} catch (ex) {
Cu.reportError(ex);
request.cancel();
return;
}
let perm = types.queryElementAt(0, Ci.nsIContentPermissionType);
const kFeatureKeys = { "geolocation" : "geo",
"desktop-notification" : "desktop-notification",
"flyweb-publish-server": "flyweb-publish-server"
};
// Make sure that we support the request.
if (!(perm.type in kFeatureKeys)) {
return;
}
var requestingPrincipal = request.principal;
var requestingURI = requestingPrincipal.URI;
// Ignore requests from non-nsIStandardURLs
if (!(requestingURI instanceof Ci.nsIStandardURL))
return;
var permissionKey = kFeatureKeys[perm.type];
var result = Services.perms.testExactPermissionFromPrincipal(requestingPrincipal, permissionKey);
if (result == Ci.nsIPermissionManager.DENY_ACTION) {
request.cancel();
return;
}
if (result == Ci.nsIPermissionManager.ALLOW_ACTION) {
request.allow();
return;
}
var browser = this._getBrowserForRequest(request);
var chromeWin = browser.ownerGlobal;
if (!chromeWin.PopupNotifications)
// Ignore requests from browsers hosted in windows that don't support
// PopupNotifications.
return;
// Show the prompt.
switch (perm.type) {
case "geolocation":
this._promptGeo(request);
break;
case "desktop-notification":
this._promptWebNotifications(request);
break;
case "flyweb-publish-server":
if (AppConstants.NIGHTLY_BUILD) {
this._promptFlyWebPublishServer(request);
}
break;
throw ex;
}
},
};
var DefaultBrowserCheck = {

View File

@ -0,0 +1,588 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
this.EXPORTED_SYMBOLS = [
"PermissionUI",
];
/**
* PermissionUI is responsible for exposing both a prototype
* PermissionPrompt that can be used by arbitrary browser
* components and add-ons, but also hosts the implementations of
* built-in permission prompts.
*
* If you're developing a feature that requires web content to ask
* for special permissions from the user, this module is for you.
*
* Suppose a system add-on wants to add a new prompt for a new request
* for getting more low-level access to the user's sound card, and the
* permission request is coming up from content by way of the
* nsContentPermissionHelper. The system add-on could then do the following:
*
* Cu.import("resource://gre/modules/Integration.jsm");
* Cu.import("resource:///modules/PermissionUI.jsm");
*
* const SoundCardIntegration = (base) => ({
* __proto__: base,
* createPermissionPrompt(type, request) {
* if (type != "sound-api") {
* return super.createPermissionPrompt(...arguments);
* }
*
* return {
* __proto__: PermissionUI.PermissionPromptForRequestPrototype,
* get permissionKey() {
* return "sound-permission";
* }
* // etc - see the documentation for PermissionPrompt for
* // a better idea of what things one can and should override.
* }
* },
* });
*
* // Add-on startup:
* Integration.contentPermission.register(SoundCardIntegration);
* // ...
* // Add-on shutdown:
* Integration.contentPermission.unregister(SoundCardIntegration);
*
* Note that PermissionPromptForRequestPrototype must be used as the
* prototype, since the prompt is wrapping an nsIContentPermissionRequest,
* and going through nsIContentPermissionPrompt.
*
* It is, however, possible to take advantage of PermissionPrompt without
* having to go through nsIContentPermissionPrompt or with a
* nsIContentPermissionRequest. The PermissionPromptPrototype can be
* imported, subclassed, and have prompt() called directly, without
* the caller having called into createPermissionPrompt.
*/
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Services",
"resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm");
XPCOMUtils.defineLazyGetter(this, "gBrandBundle", function() {
return Services.strings
.createBundle('chrome://branding/locale/brand.properties');
});
XPCOMUtils.defineLazyGetter(this, "gBrowserBundle", function() {
return Services.strings
.createBundle('chrome://browser/locale/browser.properties');
});
this.PermissionUI = {};
/**
* PermissionPromptPrototype should be subclassed by callers that
* want to display prompts to the user. See each method and property
* below for guidance on what to override.
*
* Note that if you're creating a prompt for an
* nsIContentPermissionRequest, you'll want to subclass
* PermissionPromptForRequestPrototype instead.
*/
this.PermissionPromptPrototype = {
/**
* Returns the associated <xul:browser> for the request. This should
* work for the e10s and non-e10s case.
*
* Subclasses must override this.
*
* @return {<xul:browser>}
*/
get browser() {
throw new Error("Not implemented.");
},
/**
* Returns the nsIPrincipal associated with the request.
*
* Subclasses must override this.
*
* @return {nsIPrincipal}
*/
get principal() {
throw new Error("Not implemented.");
},
/**
* If the nsIPermissionManager is being queried and written
* to for this permission request, set this to the key to be
* used. If this is undefined, user permissions will not be
* read from or written to.
*
* Note that if a permission is set, in any follow-up
* prompting within the expiry window of that permission,
* the prompt will be skipped and the allow or deny choice
* will be selected automatically.
*/
get permissionKey() {
return undefined;
},
/**
* These are the options that will be passed to the
* PopupNotification when it is shown. See the documentation
* for PopupNotification for more details.
*
* Note that prompt() will automatically set displayURI to
* be the URI of the requesting pricipal.
*/
get popupOptions() {
return {};
},
/**
* PopupNotification requires a unique ID to open the notification.
* You must return a unique ID string here, for which PopupNotification
* will then create a <xul:popupnotification> node with the ID
* "<notificationID>-notification".
*
* If there's a custom <xul:popupnotification> you're hoping to show,
* then you need to make sure its ID has the "-notification" suffix,
* and then return the prefix here.
*
* See PopupNotification.jsm for more details.
*
* @return {string}
* The unique ID that will be used to as the
* "<unique ID>-notification" ID for the <xul:popupnotification>
* to use or create.
*/
get notificationID() {
throw new Error("Not implemented.");
},
/**
* The ID of the element to anchor the PopupNotification to.
*
* @return {string}
*/
get anchorID() {
return "default-notification-icon";
},
/**
* The message to show the user in the PopupNotification. This
* is usually a string describing the permission that is being
* requested.
*
* Subclasses must override this.
*
* @return {string}
*/
get message() {
throw new Error("Not implemented.");
},
/**
* This will be called if the request is to be cancelled.
*
* Subclasses only need to override this if they provide a
* permissionKey.
*/
cancel() {
throw new Error("Not implemented.")
},
/**
* This will be called if the request is to be allowed.
*
* Subclasses only need to override this if they provide a
* permissionKey.
*/
allow() {
throw new Error("Not implemented.");
},
/**
* The actions that will be displayed in the PopupNotification
* via a dropdown menu. The first item in this array will be
* the default selection. Each action is an Object with the
* following properties:
*
* label (string):
* The label that will be displayed for this choice.
* accessKey (string):
* The access key character that will be used for this choice.
* action (Ci.nsIPermissionManager action, optional)
* The nsIPermissionManager action that will be associated with
* this choice. For example, Ci.nsIPermissionManager.DENY_ACTION.
*
* If omitted, the nsIPermissionManager will not be written to
* when this choice is chosen.
* expireType (Ci.nsIPermissionManager expiration policy, optional)
* The nsIPermissionManager expiration policy that will be associated
* with this choice. For example, Ci.nsIPermissionManager.EXPIRE_SESSION.
*
* If action is not set, expireType will be ignored.
* callback (function, optional)
* A callback function that will fire if the user makes this choice.
*/
get promptActions() {
return [];
},
/**
* If the prompt will be shown to the user, this callback will
* be called just before. Subclasses may want to override this
* in order to, for example, bump a counter Telemetry probe for
* how often a particular permission request is seen.
*/
onBeforeShow() {},
/**
* Will determine if a prompt should be shown to the user, and if so,
* will show it.
*
* If a permissionKey is defined prompt() might automatically
* allow or cancel itself based on the user's current
* permission settings without displaying the prompt.
*
* If the <xul:browser> that the request is associated with
* does not belong to a browser window with the PopupNotifications
* global set, the prompt request is ignored.
*/
prompt() {
let chromeWin = this.browser.ownerGlobal;
if (!chromeWin.PopupNotifications) {
return;
}
// We ignore requests from non-nsIStandardURLs
let requestingURI = this.principal.URI;
if (!(requestingURI instanceof Ci.nsIStandardURL)) {
return;
}
if (this.permissionKey) {
// If we're reading and setting permissions, then we need
// to check to see if we already have a permission setting
// for this particular principal.
let result =
Services.perms.testExactPermissionFromPrincipal(this.principal,
this.permissionKey);
if (result == Ci.nsIPermissionManager.DENY_ACTION) {
this.cancel();
return;
}
if (result == Ci.nsIPermissionManager.ALLOW_ACTION) {
this.allow();
return;
}
}
// Transform the PermissionPrompt actions into PopupNotification actions.
let popupNotificationActions = [];
for (let promptAction of this.promptActions) {
// Don't offer action in PB mode if the action remembers permission
// for more than a session.
if (PrivateBrowsingUtils.isWindowPrivate(chromeWin) &&
promptAction.expireType != Ci.nsIPermissionManager.EXPIRE_SESSION &&
promptAction.action) {
continue;
}
let action = {
label: promptAction.label,
accessKey: promptAction.accessKey,
callback: () => {
if (promptAction.callback) {
promptAction.callback();
}
if (this.permissionKey) {
// Remember permissions.
if (promptAction.action) {
Services.perms.addFromPrincipal(this.principal,
this.permissionKey,
promptAction.action,
promptAction.expireType);
}
// Grant permission if action is null or ALLOW_ACTION.
if (!promptAction.action ||
promptAction.action == Ci.nsIPermissionManager.ALLOW_ACTION) {
this.allow();
} else {
this.cancel();
}
}
},
};
popupNotificationActions.push(action);
}
let mainAction = popupNotificationActions.length ?
popupNotificationActions[0] : null;
let secondaryActions = popupNotificationActions.splice(1);
let options = this.popupOptions;
options.displayURI = this.principal.URI;
this.onBeforeShow();
chromeWin.PopupNotifications.show(this.browser,
this.notificationID,
this.message,
this.anchorID,
mainAction,
secondaryActions,
options);
},
};
PermissionUI.PermissionPromptPrototype = PermissionPromptPrototype;
/**
* A subclass of PermissionPromptPrototype that assumes
* that this.request is an nsIContentPermissionRequest
* and fills in some of the required properties on the
* PermissionPrompt. For callers that are wrapping an
* nsIContentPermissionRequest, this should be subclassed
* rather than PermissionPromptPrototype.
*/
this.PermissionPromptForRequestPrototype = {
__proto__: PermissionPromptPrototype,
get browser() {
// In the e10s-case, the <xul:browser> will be at request.element.
// In the single-process case, we have to use some XPCOM incantations
// to resolve to the <xul:browser>.
if (this.request.element) {
return this.request.element;
}
return this.request
.window
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell)
.chromeEventHandler;
},
get principal() {
return this.request.principal;
},
cancel() {
this.request.cancel();
},
allow() {
this.request.allow();
},
};
PermissionUI.PermissionPromptForRequestPrototype =
PermissionPromptForRequestPrototype;
/**
* Creates a PermissionPrompt for a nsIContentPermissionRequest for
* the GeoLocation API.
*
* @param request (nsIContentPermissionRequest)
* The request for a permission from content.
*/
function GeolocationPermissionPrompt(request) {
this.request = request;
}
GeolocationPermissionPrompt.prototype = {
__proto__: PermissionPromptForRequestPrototype,
get permissionKey() {
return "geo";
},
get popupOptions() {
let pref = "browser.geolocation.warning.infoURL";
return {
learnMoreURL: Services.urlFormatter.formatURLPref(pref),
};
},
get notificationID() {
return "geolocation";
},
get anchorID() {
return "geo-notification-icon";
},
get message() {
let message;
if (this.principal.URI.schemeIs("file")) {
message = gBrowserBundle.GetStringFromName("geolocation.shareWithFile2");
} else {
message = gBrowserBundle.GetStringFromName("geolocation.shareWithSite2");
}
return message;
},
get promptActions() {
// We collect Telemetry data on Geolocation prompts and how users
// respond to them. The probe keys are a bit verbose, so let's alias them.
const SHARE_LOCATION =
Ci.nsISecurityUITelemetry.WARNING_GEOLOCATION_REQUEST_SHARE_LOCATION;
const ALWAYS_SHARE =
Ci.nsISecurityUITelemetry.WARNING_GEOLOCATION_REQUEST_ALWAYS_SHARE;
const NEVER_SHARE =
Ci.nsISecurityUITelemetry.WARNING_GEOLOCATION_REQUEST_NEVER_SHARE;
let secHistogram = Services.telemetry.getHistogramById("SECURITY_UI");
let actions = [{
label: gBrowserBundle.GetStringFromName("geolocation.shareLocation"),
accessKey:
gBrowserBundle.GetStringFromName("geolocation.shareLocation.accesskey"),
action: null,
expireType: null,
callback: function() {
secHistogram.add(SHARE_LOCATION);
},
}];
if (!this.principal.URI.schemeIs("file")) {
// Always share location action.
actions.push({
label: gBrowserBundle.GetStringFromName("geolocation.alwaysShareLocation"),
accessKey:
gBrowserBundle.GetStringFromName("geolocation.alwaysShareLocation.accesskey"),
action: Ci.nsIPermissionManager.ALLOW_ACTION,
expireType: null,
callback: function() {
secHistogram.add(ALWAYS_SHARE);
},
});
// Never share location action.
actions.push({
label: gBrowserBundle.GetStringFromName("geolocation.neverShareLocation"),
accessKey:
gBrowserBundle.GetStringFromName("geolocation.neverShareLocation.accesskey"),
action: Ci.nsIPermissionManager.DENY_ACTION,
expireType: null,
callback: function() {
secHistogram.add(NEVER_SHARE);
},
});
}
return actions;
},
onBeforeShow() {
let secHistogram = Services.telemetry.getHistogramById("SECURITY_UI");
const SHOW_REQUEST = Ci.nsISecurityUITelemetry.WARNING_GEOLOCATION_REQUEST;
secHistogram.add(SHOW_REQUEST);
},
};
PermissionUI.GeolocationPermissionPrompt = GeolocationPermissionPrompt;
/**
* Creates a PermissionPrompt for a nsIContentPermissionRequest for
* the Desktop Notification API.
*
* @param request (nsIContentPermissionRequest)
* The request for a permission from content.
* @return {PermissionPrompt} (see documentation in header)
*/
function DesktopNotificationPermissionPrompt(request) {
this.request = request;
}
DesktopNotificationPermissionPrompt.prototype = {
__proto__: PermissionPromptForRequestPrototype,
get permissionKey() {
return "desktop-notification";
},
get popupOptions() {
let learnMoreURL =
Services.urlFormatter.formatURLPref("app.support.baseURL") + "push";
// The eventCallback is bound to the Notification that's being
// shown. We'll stash a reference to this in the closure so that
// the request can be cancelled.
let prompt = this;
let eventCallback = function(type) {
if (type == "dismissed") {
// Bug 1259148: Hide the doorhanger icon. Unlike other permission
// doorhangers, the user can't restore the doorhanger using the icon
// in the location bar. Instead, the site will be notified that the
// doorhanger was dismissed.
this.remove();
prompt.request.cancel();
}
};
return {
learnMoreURL,
eventCallback,
};
},
get notificationID() {
return "web-notifications";
},
get anchorID() {
return "web-notifications-notification-icon";
},
get message() {
return gBrowserBundle.GetStringFromName("webNotifications.receiveFromSite");
},
get promptActions() {
let promptActions;
// Only show "allow for session" in PB mode, we don't
// support "allow for session" in non-PB mode.
if (PrivateBrowsingUtils.isBrowserPrivate(this.browser)) {
promptActions = [
{
label: gBrowserBundle.GetStringFromName("webNotifications.receiveForSession"),
accessKey:
gBrowserBundle.GetStringFromName("webNotifications.receiveForSession.accesskey"),
action: Ci.nsIPermissionManager.ALLOW_ACTION,
expireType: Ci.nsIPermissionManager.EXPIRE_SESSION,
}
];
} else {
promptActions = [
{
label: gBrowserBundle.GetStringFromName("webNotifications.alwaysReceive"),
accessKey:
gBrowserBundle.GetStringFromName("webNotifications.alwaysReceive.accesskey"),
action: Ci.nsIPermissionManager.ALLOW_ACTION,
expireType: null,
},
{
label: gBrowserBundle.GetStringFromName("webNotifications.neverShow"),
accessKey:
gBrowserBundle.GetStringFromName("webNotifications.neverShow.accesskey"),
action: Ci.nsIPermissionManager.DENY_ACTION,
expireType: null,
},
];
}
return promptActions;
},
};
PermissionUI.DesktopNotificationPermissionPrompt =
DesktopNotificationPermissionPrompt;

View File

@ -34,6 +34,7 @@ EXTRA_JS_MODULES += [
'NetworkPrioritizer.jsm',
'offlineAppCache.jsm',
'PanelFrame.jsm',
'PermissionUI.jsm',
'PluginContent.jsm',
'ProcessHangMonitor.jsm',
'ReaderParent.jsm',