From 9414007bde4fb7030787d7a8eb642f436a2a68aa Mon Sep 17 00:00:00 2001 From: janekptacijarabaci Date: Thu, 26 Apr 2018 23:12:13 +0200 Subject: [PALEMOON] Bug 723951 - Popup notification does not continue when the tab move to other window Issue #121 --- .../palemoon/modules/PopupNotifications.jsm | 66 ++++++++++- application/palemoon/modules/webrtcUI.jsm | 126 ++++++++++++--------- 2 files changed, 136 insertions(+), 56 deletions(-) (limited to 'application/palemoon/modules') diff --git a/application/palemoon/modules/PopupNotifications.jsm b/application/palemoon/modules/PopupNotifications.jsm index 15c8915ed..0cb970230 100644 --- a/application/palemoon/modules/PopupNotifications.jsm +++ b/application/palemoon/modules/PopupNotifications.jsm @@ -12,6 +12,7 @@ const NOTIFICATION_EVENT_DISMISSED = "dismissed"; const NOTIFICATION_EVENT_REMOVED = "removed"; const NOTIFICATION_EVENT_SHOWING = "showing"; const NOTIFICATION_EVENT_SHOWN = "shown"; +const NOTIFICATION_EVENT_SWAPPING = "swapping"; const ICON_SELECTOR = ".notification-anchor-icon"; const ICON_ATTRIBUTE_SHOWING = "showing"; @@ -237,9 +238,23 @@ PopupNotifications.prototype = { * tabs) * "removed": notification has been removed (due to * location change or user action) + * "showing": notification is about to be shown + * (this can be fired multiple times as + * notifications are dismissed and re-shown) * "shown": notification has been shown (this can be fired * multiple times as notifications are dismissed * and re-shown) + * "swapping": the docshell of the browser that created + * the notification is about to be swapped to + * another browser. A second parameter contains + * the browser that is receiving the docshell, + * so that the event callback can transfer stuff + * specific to this notification. + * If the callback returns true, the notification + * will be moved to the new browser. + * If the callback isn't implemented, returns false, + * or doesn't return any value, the notification + * will be removed. * neverShow: Indicate that no popup should be shown for this * notification. Useful for just showing the anchor icon. * removeOnDismissal: @@ -829,13 +844,60 @@ PopupNotifications.prototype = { this._update(notifications, anchor); }, - _fireCallback: function PopupNotifications_fireCallback(n, event) { + _swapBrowserNotifications: function PopupNotifications_swapBrowserNoficications(ourBrowser, otherBrowser) { + // When swaping browser docshells (e.g. dragging tab to new window) we need + // to update our notification map. + + let ourNotifications = this._getNotificationsForBrowser(ourBrowser); + let other = otherBrowser.ownerDocument.defaultView.PopupNotifications; + if (!other) { + if (ourNotifications.length > 0) + Cu.reportError("unable to swap notifications: otherBrowser doesn't support notifications"); + return; + } + let otherNotifications = other._getNotificationsForBrowser(otherBrowser); + if (ourNotifications.length < 1 && otherNotifications.length < 1) { + // No notification to swap. + return; + } + + otherNotifications = otherNotifications.filter(n => { + if (this._fireCallback(n, NOTIFICATION_EVENT_SWAPPING, ourBrowser)) { + n.browser = ourBrowser; + n.owner = this; + return true; + } + other._fireCallback(n, NOTIFICATION_EVENT_REMOVED); + return false; + }); + + ourNotifications = ourNotifications.filter(n => { + if (this._fireCallback(n, NOTIFICATION_EVENT_SWAPPING, otherBrowser)) { + n.browser = otherBrowser; + n.owner = other; + return true; + } + this._fireCallback(n, NOTIFICATION_EVENT_REMOVED); + return false; + }); + + this._setNotificationsForBrowser(otherBrowser, ourNotifications); + other._setNotificationsForBrowser(ourBrowser, otherNotifications); + + if (otherNotifications.length > 0) + this._update(otherNotifications, otherNotifications[0].anchorElement); + if (ourNotifications.length > 0) + other._update(ourNotifications, ourNotifications[0].anchorElement); + }, + + _fireCallback: function PopupNotifications_fireCallback(n, event, ...args) { try { if (n.options.eventCallback) - n.options.eventCallback.call(n, event); + return n.options.eventCallback.call(n, event, ...args); } catch (error) { Cu.reportError(error); } + return undefined; }, _onPopupHidden: function PopupNotifications_onPopupHidden(event) { diff --git a/application/palemoon/modules/webrtcUI.jsm b/application/palemoon/modules/webrtcUI.jsm index c957bfd9a..ae8af8f2e 100644 --- a/application/palemoon/modules/webrtcUI.jsm +++ b/application/palemoon/modules/webrtcUI.jsm @@ -128,62 +128,14 @@ function prompt(aWindowID, aCallID, aAudioRequested, aVideoRequested, aDevices) let message = stringBundle.getFormattedString("getUserMedia.share" + requestType + ".message", [ host ]); - function listDevices(menupopup, devices) { - while (menupopup.lastChild) - menupopup.removeChild(menupopup.lastChild); - - let deviceIndex = 0; - for (let device of devices) { - addDeviceToList(menupopup, device.name, deviceIndex); - deviceIndex++; - } - } - - function addDeviceToList(menupopup, deviceName, deviceIndex) { - let menuitem = chromeDoc.createElement("menuitem"); - menuitem.setAttribute("value", deviceIndex); - menuitem.setAttribute("label", deviceName); - menuitem.setAttribute("tooltiptext", deviceName); - menupopup.appendChild(menuitem); - } - - chromeDoc.getElementById("webRTC-selectCamera").hidden = !videoDevices.length; - chromeDoc.getElementById("webRTC-selectMicrophone").hidden = !audioDevices.length; - - let camMenupopup = chromeDoc.getElementById("webRTC-selectCamera-menupopup"); - let micMenupopup = chromeDoc.getElementById("webRTC-selectMicrophone-menupopup"); - listDevices(camMenupopup, videoDevices); - listDevices(micMenupopup, audioDevices); - if (requestType == "CameraAndMicrophone") { - addDeviceToList(camMenupopup, stringBundle.getString("getUserMedia.noVideo.label"), "-1"); - addDeviceToList(micMenupopup, stringBundle.getString("getUserMedia.noAudio.label"), "-1"); - } - let mainAction = { label: PluralForm.get(requestType == "CameraAndMicrophone" ? 2 : 1, stringBundle.getString("getUserMedia.shareSelectedDevices.label")), accessKey: stringBundle.getString("getUserMedia.shareSelectedDevices.accesskey"), - callback: function () { - let allowedDevices = Cc["@mozilla.org/supports-array;1"] - .createInstance(Ci.nsISupportsArray); - if (videoDevices.length) { - let videoDeviceIndex = chromeDoc.getElementById("webRTC-selectCamera-menulist").value; - if (videoDeviceIndex != "-1") - allowedDevices.AppendElement(videoDevices[videoDeviceIndex]); - } - if (audioDevices.length) { - let audioDeviceIndex = chromeDoc.getElementById("webRTC-selectMicrophone-menulist").value; - if (audioDeviceIndex != "-1") - allowedDevices.AppendElement(audioDevices[audioDeviceIndex]); - } - - if (allowedDevices.Count() == 0) { - denyRequest(aCallID); - return; - } - - Services.obs.notifyObservers(allowedDevices, "getUserMedia:response:allow", aCallID); - } + // The real callback will be set during the "showing" event. The + // empty function here is so that PopupNotifications.show doesn't + // reject the action. + callback: function() {} }; let secondaryActions = [{ @@ -194,7 +146,72 @@ function prompt(aWindowID, aCallID, aAudioRequested, aVideoRequested, aDevices) } }]; - let options = null; + let options = { + eventCallback: function(aTopic, aNewBrowser) { + if (aTopic == "swapping") + return true; + + if (aTopic != "showing") + return false; + + let chromeDoc = this.browser.ownerDocument; + + function listDevices(menupopup, devices) { + while (menupopup.lastChild) + menupopup.removeChild(menupopup.lastChild); + + let deviceIndex = 0; + for (let device of devices) { + addDeviceToList(menupopup, device.name, deviceIndex); + deviceIndex++; + } + } + + function addDeviceToList(menupopup, deviceName, deviceIndex) { + let menuitem = chromeDoc.createElement("menuitem"); + menuitem.setAttribute("value", deviceIndex); + menuitem.setAttribute("label", deviceName); + menuitem.setAttribute("tooltiptext", deviceName); + menupopup.appendChild(menuitem); + } + + chromeDoc.getElementById("webRTC-selectCamera").hidden = !videoDevices.length; + chromeDoc.getElementById("webRTC-selectMicrophone").hidden = !audioDevices.length; + + let camMenupopup = chromeDoc.getElementById("webRTC-selectCamera-menupopup"); + let micMenupopup = chromeDoc.getElementById("webRTC-selectMicrophone-menupopup"); + listDevices(camMenupopup, videoDevices); + listDevices(micMenupopup, audioDevices); + if (requestType == "CameraAndMicrophone") { + let stringBundle = chromeDoc.defaultView.gNavigatorBundle; + addDeviceToList(camMenupopup, stringBundle.getString("getUserMedia.noVideo.label"), "-1"); + addDeviceToList(micMenupopup, stringBundle.getString("getUserMedia.noAudio.label"), "-1"); + } + + this.mainAction.callback = function() { + let allowedDevices = Cc["@mozilla.org/supports-array;1"] + .createInstance(Ci.nsISupportsArray); + if (videoDevices.length) { + let videoDeviceIndex = chromeDoc.getElementById("webRTC-selectCamera-menulist").value; + if (videoDeviceIndex != "-1") + allowedDevices.AppendElement(videoDevices[videoDeviceIndex]); + } + if (audioDevices.length) { + let audioDeviceIndex = chromeDoc.getElementById("webRTC-selectMicrophone-menulist").value; + if (audioDeviceIndex != "-1") + allowedDevices.AppendElement(audioDevices[audioDeviceIndex]); + } + + if (allowedDevices.Count() == 0) { + denyRequest(aCallID); + return; + } + + Services.obs.notifyObservers(allowedDevices, "getUserMedia:response:allow", aCallID); + }; + return true; + } + }; chromeWin.PopupNotifications.show(browser, "webRTC-shareDevices", message, "webRTC-shareDevices-notification-icon", mainAction, @@ -254,7 +271,8 @@ function showBrowserSpecificIndicator(aBrowser) { }]; let options = { hideNotNow: true, - dismissed: true + dismissed: true, + eventCallback: function(aTopic) aTopic == "swapping" }; chromeWin.PopupNotifications.show(aBrowser, "webRTC-sharingDevices", message, "webRTC-sharingDevices-notification-icon", mainAction, -- cgit v1.2.3