forked from Floorp-Projects/Floorp
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Bug 1289974 part 1: Device selection for presentation API on Firefox;…
… r=mconley MozReview-Commit-ID: 8z8xM4hr2F3
- Loading branch information
1 parent
4610a31
commit fcf3ec8
Showing
11 changed files
with
437 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,4 +17,5 @@ if 'a' in CONFIG['GRE_MILESTONE']: | |
DIRS += [ | ||
'flyweb', | ||
'formautofill', | ||
'presentation', | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
/* -*- 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/. */ | ||
|
||
const {classes: Cc, interfaces: Ci, utils: Cu, manager: Cm} = Components; | ||
|
||
Cu.import("resource://gre/modules/XPCOMUtils.jsm"); | ||
|
||
const PRESENTATION_DEVICE_PROMPT_PATH = | ||
"chrome://presentation/content/PresentationDevicePrompt.jsm"; | ||
|
||
function log(aMsg) { | ||
// dump("@ Presentation: " + aMsg + "\n"); | ||
} | ||
|
||
function install(aData, aReason) { | ||
} | ||
|
||
function uninstall(aData, aReason) { | ||
} | ||
|
||
function startup(aData, aReason) { | ||
log("startup"); | ||
Presentation.init(); | ||
} | ||
|
||
function shutdown(aData, aReason) { | ||
log("shutdown"); | ||
Presentation.uninit(); | ||
} | ||
|
||
// Register/unregister a constructor as a factory. | ||
function Factory() {} | ||
Factory.prototype = { | ||
register: function(targetConstructor) { | ||
let proto = targetConstructor.prototype; | ||
this._classID = proto.classID; | ||
|
||
let factory = XPCOMUtils._getFactory(targetConstructor); | ||
this._factory = factory; | ||
|
||
let registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar); | ||
registrar.registerFactory(proto.classID, proto.classDescription, | ||
proto.contractID, factory); | ||
}, | ||
|
||
unregister: function() { | ||
let registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar); | ||
registrar.unregisterFactory(this._classID, this._factory); | ||
this._factory = null; | ||
this._classID = null; | ||
}, | ||
}; | ||
|
||
var Presentation = { | ||
// PUBLIC APIs | ||
init: function() { | ||
log("init"); | ||
// Register PresentationDevicePrompt into a XPCOM component. | ||
Cu.import(PRESENTATION_DEVICE_PROMPT_PATH); | ||
this._register(); | ||
}, | ||
|
||
uninit: function() { | ||
log("uninit"); | ||
// Unregister PresentationDevicePrompt XPCOM component. | ||
this._unregister(); | ||
Cu.unload(PRESENTATION_DEVICE_PROMPT_PATH); | ||
}, | ||
|
||
// PRIVATE APIs | ||
_register: function() { | ||
log("_register"); | ||
this._devicePromptFactory = new Factory(); | ||
this._devicePromptFactory.register(PresentationDevicePrompt); | ||
}, | ||
|
||
_unregister: function() { | ||
log("_unregister"); | ||
this._devicePromptFactory.unregister(); | ||
delete this._devicePromptFactory; | ||
}, | ||
}; |
254 changes: 254 additions & 0 deletions
254
browser/extensions/presentation/content/PresentationDevicePrompt.jsm
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,254 @@ | ||
/* -*- 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 { | ||
hideNotNow: true, | ||
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.filter = "url('chrome://browser/skin/filters.svg#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, // For hideNotNow. | ||
}]; | ||
}, | ||
// PRIVATE APIs | ||
get _domainName() { | ||
if (this.principal.URI instanceof Ci.nsIFileURL) { | ||
return this.principal.URI.path.split('/')[1]; | ||
} | ||
return this.principal.URI.hostPort; | ||
}, | ||
_createPopupContent: function() { | ||
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: function(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: function() { | ||
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; | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
<?xml version="1.0"?> | ||
<!-- 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/. --> | ||
|
||
#filter substitution | ||
|
||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" | ||
xmlns:em="http://www.mozilla.org/2004/em-rdf#"> | ||
|
||
<Description about="urn:mozilla:install-manifest"> | ||
<em:id>[email protected]</em:id> | ||
<em:version>1.0.0</em:version> | ||
<em:type>2</em:type> | ||
<em:bootstrap>true</em:bootstrap> | ||
<em:multiprocessCompatible>true</em:multiprocessCompatible> | ||
|
||
<!-- Target Application this theme can install into, | ||
with minimum and maximum supported versions. --> | ||
<em:targetApplication> | ||
<Description> | ||
<!-- Firefox GUID --> | ||
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id> | ||
<em:minVersion>@MOZ_APP_VERSION@</em:minVersion> | ||
<em:maxVersion>@MOZ_APP_MAXVERSION@</em:maxVersion> | ||
</Description> | ||
</em:targetApplication> | ||
|
||
<!-- Front End MetaData --> | ||
<em:name>Presentation</em:name> | ||
<em:description>Discover nearby devices in the browser</em:description> | ||
</Description> | ||
</RDF> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
[features/[email protected]] chrome.jar: | ||
% content presentation %content/ | ||
content/ (content/*) | ||
% skin presentation-shared classic/1.0 %skin/shared/ | ||
skin/ (skin/*) |
6 changes: 6 additions & 0 deletions
6
browser/extensions/presentation/locale/en-US/presentation.properties
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
presentation.message=Select one device to send the content. | ||
presentation.urlbar.tooltiptext=View the device-selection request | ||
presentation.deviceprompt.select.label=Send | ||
presentation.deviceprompt.select.accessKey=S | ||
presentation.deviceprompt.cancel.label=Cancel | ||
presentation.deviceprompt.cancel.accessKey=C |
Oops, something went wrong.