the spec says:
the user agent MUST still offer the user unlimited choice of any
display surface.
https://www.w3.org/TR/screen-capture/#dom-mediadevices
But Firefox only offers "Window" or "Screen", and not the browser tab contents like Chrome/Edge. Am I missing something? Is there a way to capture only the tab?
You're right. Firefox (and Safari 16) currently allows windows and screens only to be captured with getDisplayMedia(). Chrome allows window, screens, and tabs.
Note that you can detect afterwards which display surface type (tab, window, screen) the user picked thanks to the displaySurface track setting.
// Prompt the user to share a tab, a window or a screen.
const stream = await navigator.mediaDevices.getDisplayMedia();
const [track] = stream.getVideoTracks();
const displaySurface = track.getSettings().displaySurface;
if (displaySurface == "browser") {
// User picked a tab.
} else if (displaySurface == "window") {
// User picked a window.
} else if (displaySurface == "monitor") {
// User picked a screen.
}
Related
For a web Application, when the user makes a choice of radio button on a previous unrelated field, I am trigerring location for the next step by calling attemptLocation().
const attemptLocation = () => {
if ("geolocation" in navigator) {
The possible scenarios are:
A popup appears in browser and user allows the location immediately - Works !
The user clicks on 'Block' and location is not available. The user then realizes that they cannot proceed so they click on the location icon in browser and allow location.
How to detect this change they made from block to allow in the browser because right now
In Chrome: the page does not detect change to allow and users get
stuck.
In Firefox: Unless the user clicks remember this selection the
browser keeps asking the same allow or not question even when user
said allow and refreshed the page.
In Edge: When the user changes to allow, location is updated and works but again only after they refresh the page and start over
To simplify the question:
After page loads, the user who blocked location, changes from block to allow location, how can I alert ("thanks for changing from block location to allow location") ?
Thanks to #GabrielePetrioli's comment. The code below uses navigator.permissions.queryPermissions Status Change Event
I am checking if permission was granted and updating the application by calling the function which updates location.
const [locationAccess, setLocationAccess] = useState(false);//user changes in browser
...
//check user location changes in navigator
navigator.permissions.query({ name: 'geolocation' }).then((permissionStatus) => {
permissionStatus.onchange = () => {
setLocationAccess(permissionStatus.state=="granted")
if (permissionStatus.state=="granted") {
attemptLocation();
}
};
});
In your App using the useEffect() when your App mounts check this:
if ("geolocation" in navigator) {
console.log("Available");
} else {
console.log("Not Available");
}
If it is Available you can then access various geolocation properties. If you cannot access this properties then the user has disabled the location access.
Based on this you can create some states logic using useState() hook to use across your application.
I am building a two person game app using vue.js. The app uses vuex for state management and Firestore as the backend server.
If the user leaves the app by either closing the browser tab or navigating away, the games Firestore files need to be deleted. However, if the user refreshes the page, the Firestore files need to remain so that the reload process can repopulate the game.
So I need to determine if the user has refreshed the page as opposed to closing the browser or navigating away.
As shown below, in vue's created lifecycle I setup a "beforeunload" event Listener and also start my Firestore listeners
created() {
// This window event listener fires when the user
// navigates away from or closes the browser window
window.addEventListener("beforeunload", (event) => {
const isByRefresh = getUnloadInitiator();
if (!isByRefresh) {
this.stopFirestoreListeners("beforeunload");
}
// Cancel the event. This allows the user to cancel via popup. (for debug purposes)
event.preventDefault();
event.returnValue = "";
// the absence of a returnValue property on the event
// guarantees the browser unload happens
// delete event["returnValue"];
});
this.startFirestoreListeners("created");
},
The getUnloadInitiator function is shown below. This is where I need help. Right now all this function does is console.log various performance values.
function getUnloadInitiator() {
// check for feature support before continuing
if (performance.mark === undefined) {
console.log("performance.mark NOT supported");
return false;
}
console.log("===============================");
// Yes I know that performance.navigation is depreciated.
const nav = performance.navigation;
console.log("nav=", nav);
console.log("===============================");
// Use getEntriesByType() to just get the "navigation" events
var perfEntries = performance.getEntriesByType("navigation");
for (var i = 0; i < perfEntries.length; i++) {
var p = perfEntries[i];
console.log("= Navigation entry[" + i + "]=", p);
// other properties
console.log("type = " + p.type);
}
console.log("===============================");
performance.mark("beginLoop");
const entries = performance.getEntries({
name: "beginLoop",
entryType: "mark",
});
const firstEntry = entries[0];
console.log("firstEntry.type=", firstEntry.type);
console.log("===============================");
//TODO: Determine how unload was initiated
return true;
}
Below is the output from my console.logs. They are the same for refreshing the page, closing the browser tab, or navigating away. All show "reload" as the navigation type.
===============================
nav= PerformanceNavigation {type: 1, redirectCount: 0}
===============================
= Navigation entry[0]= PerformanceNavigationTiming {unloadEventStart: 25.399999976158142, unloadEventEnd: 25.69999998807907, domInteractive: 633, domContentLoadedEventStart: 633, domContentLoadedEventEnd: 633, …}
type = reload
===============================
firstEntry.type= reload
===============================
Any help on how to differentiate between refreshing the page, closing the browser tab, or navigating away would be appreciated. There must be away, because the native cancel browser popup I'm using for debug purposes differentiates between fresh and browser tab close.
Thanks
You can use a source of authority as persistence, be it firestore, local storage, or cookies. you are able to get the browser's tab ID with tab.id and compare it to an existing one should one exist.
browser.pageAction.show(tab.id);
Source: https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Working_with_the_Tabs_API
I have a WebExtension for Mozilla, which notifies me with the desktop notification function.
It works exactly how I want, but Firefox will close the notification automatically after X seconds. Is it possible to display the notification until the user clicks on it?
The thing I did is to close and reopen the notification every 5 seconds, so the user has to click on it to close it permanently.
This looks like this:
// This is the notification function
function notifyMeFunction() {
var notification = new Notification('Alert', {
icon: chrome.extension.getURL('icons.png'),
body: "New Notification",
tag: "DesktopNotification",
});
notification.onclick = function(event) {
notificationClicked = true;
}
notification.onclose = function(event) {
notificationClicked = true;
}
}
// Function which will self-open every 5 seconds
function notifyMe() {
if (notificationClicked == false) {
notifyMeFunction();
setTimeout(notifyMe, 5000);
} else {
notificationClicked = false;
}
}
Any ideas how to set the display time to something like "must interact"?
There is, currently (Firefox version <= 51.0a1), no method of indicating to the API that the user must interact with these notifications. Nor is there any way to specify for how long the notification is displayed to the user.
Note: You are using the Web Notifications API, not the WebExtensions chrome.notifications API. Neither has a way to require user interacting in Firefox.
From Chrome 50, Google Chrome does have an option to require that the user must interact with the notification: requireInteraction. Thus, at some point in time, Firefox will probably support such an option.
However, at least as of this point in time, the string requireInteraction does not exist in the Firefox source code.
In a Firefox Add-on SDK add-on, how do I tell whether a window is in the background, ie. visible but not focused?
For example, if I bring a different application to the foreground, the Firefox window becomes unfocused but is still visible.
The reason why I want to do this is because I have a CPU-intensive content script running in the active window, and I'd like to pause it to avoid unnecessary overhead whenever the user isn't actively engaged with the window - meaning it's in the background or minimized.
require("sdk/windows").activeWindow keeps returning the last clicked window even if it's in the background or minimized. There doesn't seem to be any property for the window's focus state.
I can also get use the following code to get an nsIDocShell:
var mostRecentWindow = require("sdk/window/utils").getMostRecentBrowserWindow();
var docShell = require("sdk/window/utils").getWindowDocShell(mostRecentWindow);
Now when I query the docShell.isActive property, it returns true even if the window is in the background.
The one advantage of docShell.isActive is that it returns false when the window is minimized, while activeWindow returns true even in this case. But it's still missing information about whether the window is in the background or not.
Based on the suggestion by #willlma, this code seems to do the trick:
const windows = require('sdk/windows').browserWindows;
const tabs = require("sdk/tabs");
var anyWindowActive = true;
var refreshTimeoutId;
windows.on('deactivate', function(window) {
if (window == windows.activeWindow) {
anyWindowActive = false;
}
clearTimeout(refreshTimeoutId);
refreshTimeoutId = setTimeout(refreshTabStates, 50);
});
windows.on('activate', function(window) {
anyWindowActive = true;
clearTimeout(refreshTimeoutId);
refreshTimeoutId = setTimeout(refreshTabStates, 50);
});
tabs.on('activate', function(tab) {
clearTimeout(refreshTimeoutId);
refreshTimeoutId = setTimeout(refreshTabStates, 50);
});
function refreshTabStates() {
refreshTimeoutId = null;
for (let win of windows) {
for (let tab of win.tabs) {
var shouldBeActive = anyWindowActive
&& tab == tabs.activeTab
&& win == windows.activeWindow;
notifyTab(tab, shouldBeActive);
}
}
}
where notifyTab() is a function that posts a message to that tab's content script (if any) about whether it should be running or not.
setTimeout is used to avoid multiple calls to refreshTabStates in quick succession. For example, if you click on an inactive tab in a window that's not the current one, that one click results in window.deactivate, window.activate and tab.activate events.
Also, the initial state is a problem. What if the user launches Firefox and puts it in the background before any script has managed to run?
I know it is a very asked question, but believe me I don't find the answer through the Web.
My purpose is to trigger the message box only if the user clicks on the close (X) button.
The user continues to get the message box if he clicks on the back/forward button and also if he uses F5, CTRL+R, ...
I do not want to associate any other action than the window close button click as behind, there will be a session kill with Ajax. So it is not acceptable to kill the session if the user types F5 button.
Here is my code. For info, I know that there is a way in IE to check the event object clientY, but this does not work in Firefox.
$("a").click(function () {
window.onbeforeunload = null;
});
window.onbeforeunload = function (e) {
var closewindowmessage="If you leave this page, your session will be definitely closed.";
e = e || window.event;
// For IE and Firefox
if (e) {
e.returnValue = closewindowmessage;
}
// For Safari
return closewindowmessage;
};
There's no definitive way of detecting why/how a page is being unloaded. You could rebuild your site to use the now ever so popular "anchor navigation" method which stores data in the HTML anchor, such as http://www.example.com/page#something=something. This would at least typically solve the problem for the back/forward buttons but not when the user is reloading the page.
Other than that, you could employ various ad hoc ways of tracking the mouse and keyboard action before the user tries to unload the page. You could for example track when the user drags the mouse diagonally up to the right – that probably means he's just about to close the the window/tab, so keep the message. Diagonally up to the left – that probably means he's just about to click the back forward buttons or maybe enter something to the address field. If you're really serious, conduct a study of how people move the cursor and correlate that with whether they're about to close the page or do something "allowed". Then again, on a Mac the close button is in the upper left corner of the window. And so on and so forth. It'll still just be best guesses.
You could also track upward mouse movements and show a big red message in the browser viewport (not a popup/alert) to warn the user before he even considers leaving the page.
Tracking keyboard events is a little bit more deterministic, but still requires some cross browser and platform research. I leave you with this code, which I'm hoping may work as a boilerplate. It logs the key presses and suppresses the message if F5 or Apple+R (Mac) was pressed. Otherwise it will show a message containing a list of all logged key presses.
The analysis needs testing and extension; it's only been tested on Firefox Mac. One bug that I can immediately point out is that if you press Apple+R,R you'll still get prompted because the second page instance never recorded any keydown event for the Apple key – only for the R key. It will also fail if the user presses something inbetween, like Apple+L,R. You might be fine with just checking if the last key pressed was R.
<script>
// Create an empty array.
window.keys = [];
// Log every key press
window.onkeydown = function (e) {
var evt = window.event || e;
var keyCode = e.keyCode || e.which;
window.keys.push(keyCode)
}
function analyzeKeyPresses(){
keys.reverse(); // Reverse the array so it's easier to handle.
var doBlock = true;
// Here we only apply certain checks if there are enough keys in the array. Don't want a JS error...
switch(window.keys.length){
case 0:
doBlock = true; // Redundant. If there are no key presses logged, assume we should prompt the user.
break;
default: // Two or more key presses logged.
if(keys[0] == 82 && keys[1] == 224) doBlock = false; // User pressed apple+r on a Mac - don't prompt!
if(keys[0] == 82 && keys[1] == 17) doBlock = false; // User pressed ctrl+r on Windovs (untested) - don't prompt!
// Note: No break! Intentional fall-through! We still want to check for F5!
case 1: // One or more key presses logged.
if(keys[0] == 116) doBlock = false; // User pressed F5 - don't prompt!
}
keys.reverse(); // Un-reverse the array in case we need to use it again. (Easier to read...)
return doBlock;
}
window.onbeforeunload = function (e) {
var closewindowmessage=window.keys.join(" ");
var blockUnload = analyzeKeyPresses();
if(blockUnload){
e = e || window.event;
// For IE and Firefox
if (e) {
e.returnValue = closewindowmessage;
}
// For Safari
return closewindowmessage;
}
};
</script>
1 2