forked from mirrors/gecko-dev
MozReview-Commit-ID: DqJdTGopR9G --HG-- extra : rebase_source : e8c9eb03468c075b79013b6e0bd8b367229c24cd
253 lines
8.7 KiB
JavaScript
253 lines
8.7 KiB
JavaScript
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
|
/* 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/. */
|
|
|
|
/*
|
|
* This is the implementation of nsIPresentationDevicePrompt XPCOM.
|
|
* It will be registered into a XPCOM component by Presentation.jsm.
|
|
*
|
|
* This component will prompt a device selection UI for users to choose which
|
|
* devices they want to connect, when PresentationRequest is started.
|
|
*/
|
|
|
|
"use strict";
|
|
|
|
var EXPORTED_SYMBOLS = ["PresentationDevicePrompt"];
|
|
|
|
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
|
|
|
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
Cu.import("resource://gre/modules/Services.jsm");
|
|
|
|
// An string bundle for localization.
|
|
XPCOMUtils.defineLazyGetter(this, "Strings", function() {
|
|
return Services.strings.createBundle("chrome://presentation/locale/presentation.properties");
|
|
});
|
|
// To generate a device selection prompt.
|
|
XPCOMUtils.defineLazyModuleGetter(this, "PermissionUI",
|
|
"resource:///modules/PermissionUI.jsm");
|
|
/*
|
|
* Utils
|
|
*/
|
|
function log(aMsg) {
|
|
// Prefix is useful to grep log.
|
|
// dump("@ PresentationDevicePrompt: " + aMsg + "\n");
|
|
}
|
|
|
|
function GetString(aName) {
|
|
return Strings.GetStringFromName(aName);
|
|
}
|
|
|
|
/*
|
|
* Device Selection UI
|
|
*/
|
|
const kNotificationId = "presentation-device-selection";
|
|
const kNotificationPopupIcon = "chrome://presentation-shared/skin/link.svg";
|
|
|
|
// There is no dependancy between kNotificationId and kNotificationAnchorId,
|
|
// so it's NOT necessary to name them by same prefix
|
|
// (e.g., presentation-device-selection-notification-icon).
|
|
const kNotificationAnchorId = "presentation-device-notification-icon";
|
|
const kNotificationAnchorIcon = "chrome://presentation-shared/skin/link.svg";
|
|
|
|
// This will insert our own popupnotification content with the device list
|
|
// into the displayed popupnotification element.
|
|
// PopupNotifications.jsm will automatically generate a popupnotification
|
|
// element whose id is <notification id> + "-notification" and show it,
|
|
// so kPopupNotificationId must be kNotificationId + "-notification".
|
|
// Read more detail in PopupNotifications._refreshPanel.
|
|
const kPopupNotificationId = kNotificationId + "-notification";
|
|
|
|
function PresentationPermissionPrompt(aRequest, aDevices) {
|
|
this.request = aRequest;
|
|
this._isResponded = false;
|
|
this._devices = aDevices;
|
|
}
|
|
|
|
PresentationPermissionPrompt.prototype = {
|
|
__proto__: PermissionUI.PermissionPromptForRequestPrototype,
|
|
// PUBLIC APIs
|
|
get browser() {
|
|
return this.request.chromeEventHandler;
|
|
},
|
|
get principal() {
|
|
return this.request.principal;
|
|
},
|
|
get popupOptions() {
|
|
return {
|
|
removeOnDismissal: true,
|
|
popupIconURL: kNotificationPopupIcon, // Icon shown on prompt content
|
|
eventCallback: (aTopic, aNewBrowser) => {
|
|
log("eventCallback: " + aTopic);
|
|
let handler = {
|
|
// dismissed: () => { // Won't be fired if removeOnDismissal is true.
|
|
// log("Dismissed by user. Cancel the request.");
|
|
// },
|
|
removed: () => {
|
|
log("Prompt is removed.");
|
|
if (!this._isResponded) {
|
|
log("Dismissed by user. Cancel the request.");
|
|
this.request.cancel(Cr.NS_ERROR_NOT_AVAILABLE);
|
|
}
|
|
},
|
|
showing: () => {
|
|
log("Prompt is showing.");
|
|
// We cannot insert the device list at "showing" phase because
|
|
// the popupnotification content whose id is kPopupNotificationId
|
|
// is not generated yet.
|
|
},
|
|
shown: () => {
|
|
log("Prompt is shown.");
|
|
// Insert device selection list into popupnotification element.
|
|
this._createPopupContent();
|
|
},
|
|
};
|
|
|
|
// Call the handler for Notification events.
|
|
handler[aTopic]();
|
|
},
|
|
};
|
|
},
|
|
get notificationID() {
|
|
return kNotificationId;
|
|
},
|
|
get anchorID() {
|
|
let chromeDoc = this.browser.ownerDocument;
|
|
let anchor = chromeDoc.getElementById(kNotificationAnchorId);
|
|
if (!anchor) {
|
|
let notificationPopupBox =
|
|
chromeDoc.getElementById("notification-popup-box");
|
|
// Icon shown on URL bar
|
|
let notificationIcon = chromeDoc.createElement("image");
|
|
notificationIcon.id = kNotificationAnchorId;
|
|
notificationIcon.setAttribute("src", kNotificationAnchorIcon);
|
|
notificationIcon.classList.add("notification-anchor-icon");
|
|
notificationIcon.setAttribute("role", "button");
|
|
notificationIcon.setAttribute("tooltiptext",
|
|
GetString("presentation.urlbar.tooltiptext"));
|
|
notificationIcon.style.setProperty("-moz-context-properties", "fill");
|
|
notificationIcon.style.fill = "currentcolor";
|
|
notificationIcon.style.opacity = "0.4";
|
|
notificationPopupBox.appendChild(notificationIcon);
|
|
}
|
|
|
|
return kNotificationAnchorId;
|
|
},
|
|
get message() {
|
|
return GetString("presentation.message", this._domainName);
|
|
},
|
|
get promptActions() {
|
|
return [{
|
|
label: GetString("presentation.deviceprompt.select.label"),
|
|
accessKey: GetString("presentation.deviceprompt.select.accessKey"),
|
|
callback: () => {
|
|
log("Select");
|
|
this._isResponded = true;
|
|
if (!this._listbox || !this._devices.length) {
|
|
log("No device can be selected!");
|
|
this.request.cancel(Cr.NS_ERROR_NOT_AVAILABLE);
|
|
return;
|
|
}
|
|
let device = this._devices[this._listbox.selectedIndex];
|
|
this.request.select(device);
|
|
log("device: " + device.name + "(" + device.id + ") is selected!");
|
|
},
|
|
}, {
|
|
label: GetString("presentation.deviceprompt.cancel.label"),
|
|
accessKey: GetString("presentation.deviceprompt.cancel.accessKey"),
|
|
callback: () => {
|
|
log("Cancel selection.");
|
|
this._isResponded = true;
|
|
this.request.cancel(Cr.NS_ERROR_NOT_AVAILABLE);
|
|
},
|
|
dismiss: true,
|
|
}];
|
|
},
|
|
// PRIVATE APIs
|
|
get _domainName() {
|
|
if (this.principal.URI instanceof Ci.nsIFileURL) {
|
|
return this.principal.URI.pathQueryRef.split("/")[1];
|
|
}
|
|
return this.principal.URI.hostPort;
|
|
},
|
|
_createPopupContent() {
|
|
log("_createPopupContent");
|
|
|
|
if (!this._devices.length) {
|
|
log("No available devices can be listed!");
|
|
return;
|
|
}
|
|
|
|
let chromeDoc = this.browser.ownerDocument;
|
|
|
|
let popupnotification = chromeDoc.getElementById(kPopupNotificationId);
|
|
if (!popupnotification) {
|
|
log("No available popupnotification element to be inserted!");
|
|
return;
|
|
}
|
|
|
|
let popupnotificationcontent =
|
|
chromeDoc.createElement("popupnotificationcontent");
|
|
|
|
this._listbox = chromeDoc.createElement("richlistbox");
|
|
this._listbox.setAttribute("flex", "1");
|
|
this._devices.forEach((device) => {
|
|
let listitem = chromeDoc.createElement("richlistitem");
|
|
let label = chromeDoc.createElement("label");
|
|
label.setAttribute("value", device.name);
|
|
listitem.appendChild(label);
|
|
this._listbox.appendChild(listitem);
|
|
});
|
|
|
|
popupnotificationcontent.appendChild(this._listbox);
|
|
popupnotification.appendChild(popupnotificationcontent);
|
|
},
|
|
};
|
|
|
|
|
|
/*
|
|
* nsIPresentationDevicePrompt
|
|
*/
|
|
// For XPCOM registration
|
|
const PRESENTATIONDEVICEPROMPT_CONTRACTID = "@mozilla.org/presentation-device/prompt;1";
|
|
const PRESENTATIONDEVICEPROMPT_CID = Components.ID("{388bd149-c919-4a43-b646-d7ec57877689}");
|
|
|
|
function PresentationDevicePrompt() {}
|
|
|
|
PresentationDevicePrompt.prototype = {
|
|
// properties required for XPCOM registration:
|
|
classID: PRESENTATIONDEVICEPROMPT_CID,
|
|
classDescription: "Presentation API Device Prompt",
|
|
contractID: PRESENTATIONDEVICEPROMPT_CONTRACTID,
|
|
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationDevicePrompt]),
|
|
|
|
// This will be fired when window.PresentationRequest(URL).start() is called.
|
|
promptDeviceSelection(aRequest) {
|
|
log("promptDeviceSelection");
|
|
|
|
// Cancel request if no available device.
|
|
let devices = this._loadDevices();
|
|
if (!devices.length) {
|
|
log("No available device.");
|
|
aRequest.cancel(Cr.NS_ERROR_NOT_AVAILABLE);
|
|
return;
|
|
}
|
|
|
|
// Show the prompt to users.
|
|
let promptUI = new PresentationPermissionPrompt(aRequest, devices);
|
|
promptUI.prompt();
|
|
},
|
|
_loadDevices() {
|
|
let deviceManager = Cc["@mozilla.org/presentation-device/manager;1"]
|
|
.getService(Ci.nsIPresentationDeviceManager);
|
|
let devices = deviceManager.getAvailableDevices().QueryInterface(Ci.nsIArray);
|
|
let list = [];
|
|
for (let i = 0; i < devices.length; i++) {
|
|
let device = devices.queryElementAt(i, Ci.nsIPresentationDevice);
|
|
list.push(device);
|
|
}
|
|
|
|
return list;
|
|
},
|
|
};
|