JS IPC; send messages from one tab to another? - javascript

For example with youtube center (a userscript) I can have a youtube video playing in one tab and I open another video the first one will pause. If I switch tabs and hit play the other will now pause. How does the userscript do this? I tried looking at the source
It appears to use a socket but it looks like socket is a plain object. It says it mimics socket.io which I don't know either but I believe this one doesn't connect to a site while socket.io does?

The comment says what it's happening. It's using localstorage (HTML5 functionality) to write and notify:
/**
* A cross-window broadcast service built on top
* of the HTML5 localStorage API. The interface
* mimic socket.io in design.
*
The following lines set up the listeners.
if (identifier === 6) {
session_addEventListener("storage", storageHandler);
} else if (window.attachEvent) {
document.attachEvent('onstorage', storageHandler);
ytcenter.unload(function(){
document.detachEvent("storage", storageHandler);
});
} else {
window.addEventListener('storage', storageHandler, false);
ytcenter.unload(function(){
window.removeEventListener("storage", storageHandler, false);
});
}
Clearly, there are listeners on both tabs such that every time you write/read to localstorage the other tab is notified.
So in short, no, the data doesn't go to a server. It simply writes to localstorage and relies on the browser to make available changes to localstorage across all tabs simultaneously.
You can read more about the storage event here: http://dev.w3.org/html5/webstorage/#the-storage-event
Worth noting events aren't immediately available, only on activation of the Document (i.e. your events don't / might not get processed until the tab is active). I'd assume different browsers handle the 'activation' differently.
Such a Document object is not necessarily fully active, but events fired on such objects are ignored by the event loop until the Document becomes fully active again.

Related

Watch website and wait for events

I want to analyze a website that is not mine.
So, I want to use Javascript to do it at my end in the browser.
After I click a button on the website I want to trigger a timer and as soon as a notification from the website comes back, the timer should stop and save the notification that came back.
How can I do this the easiest way?
I cannot give you the link to the website, because it's hosted in a private network.
My first question would be, how I can log all events that are triggered on a website to the console, so I know the name of the button I want to wait for.
Thanks!
dave
All events on a specific DOM node:
To see all the events for a specific DOMnode, or window (only works on chrome i believe, didnt test it elsewhere):
getEventListeners(window)
this will give you an object with all the events, then you can intercept them with
window.addEventListener(eventName, fn, true);
The whole application:
this way your event will be called whenever an event on that node is triggered (window in this case)
if you want absolutely all events on the whole app, you can achieve it with using something like firebug
Specific event on a specific element:
if you want a button click only, you can do the following:
var specificButton = document.querySelector('#specific-button')
specificButton.addEventListener('click', function() {});
Implementation:
if you do not own the sourcecode, you can use something like greasemonkey or tampermonkey to inject your javascript into the page.
if you are using it on a server, you can use cheerio to parse the returned html from the get request, and apply queries on it, but you will lose the ability for listening to live events from io devices.
If I understand you, the easiest option I see is to open your browser developer tools and using the console get the button (document.getElementById, i.e.) and change its onclick callback, including a call to the old callback in your new callback, and trigger your timer.
To intercept the response to this button (I assume that it triggers a network request), you'll have to analyze a bit the code of the web to see how you can detect it.
You could also edit the website javascript throught "Sources" tab of your browser's dev tools.
It's an idea. I have never done something like that. I have to admit that it sounds a little weird to me.

In Chrome Event Pages, can I use the Alarms API to run code at specified intervals, while also ignoring code I want to run only once?

As I understand it, in Chrome Extensions, Event Pages go inactive after a short period of time to free up memory in Chrome. Whereas Background Pages run all the time. I'd like to code my extension to use Event Pages and allow my extension to not be a memory hog.
Since the event page goes inactive, I am unable to use setInterval to periodically run code in the background. The recommended solution is to use the chrome.alarms API as seen here:
chrome.alarms.onAlarm.addListener(function(alarm) {
if (alarm.name == 'name-of-alarm') {
// Do something...
testFunc();
}
});
// Create the alarm:
chrome.alarms.create('name-of-alarm', {
periodInMinutes: 10
});
My issue is that when this code wakes up my event page, it runs ALL of the code inside it and not just the testFunc() I specified. I have code in my event page that I only want to run once (when the browser starts or when the extension is reloaded/updated). The alarms API does not seem to be fit for what I'm trying to do.
What's the best way to handle this while keeping my event page an event page?

SessionStorage browser wide (scope) or LocalStorage deleted on exit

Is there any mechanism that would allow me handle the values on a browser level? What I mean is either:
sessionStorage that I can access values in ANY tab in browser (something like server side sessions)
localStorage that would be removed on session end (when closing the browser, not tab)
For example, the video starts in player in one tab. Some flag is stored in that kind of storage. When user opens another tab with the same URL, app should read that flag and dissalow playing the video. Of course it should be removed on exit, otherwise flag would dissalow all the future requests in that browser. Any suggestions?
every tab or window data can save/read to local/session storage but it's limited to that domain only.
The question that you asked about video handling over two tabs, it can be pulled of, but that is very tricky to handle, and I would not suggest to go that road! You can periodicaly save timestamps of video to browser storage but it also depends on server that is sending the video to browser, and you could end up not serving the video to the user at all!
For clearing the data when browser window close I think there is no event for that but there is event for window loosing focus so you can use that I guess.
hth,
$(window).on('beforeunload', function DecideAction() {
if (('localStorage' in window) && window['localStorage'] !== null) {
//get value from localstorage using getItem and allow/deny the further access
}
});
If the requirement is to have this last "as long as the browser window is open", you're going to hit repeated issues as browsers don't work on that level any more - there is tab-level and domain-level (persists like cookies). The "Browser Window" is just a collection of tabs, unless you specifically set up your browser in a certain way (to remove cookies and session data on closing and not share data between window instances). This however is browser setup (and isn't even standard across different browsers), and not something you can control client-side.
If you're willing to consider some alternatives that will provide the end result you seem to require, if not in the specific manner you have specified, read on:
To Expand on AkshayJ's original comment, use localStorage as sessionStorage is only ever tab-specific (it can't be shared).
In order to clear the flag, as part of the same functionality that sets the flag, add an onunload event to the tab playing the video that will clear it when the tab is closed or the window location moves away from the video. This will allow greater functionality than you originally requested, because in your original case the user would have to close down the browser entirely before they could play the video again, even if the tab that was playing the video was long gone or had moved on to another page.
UPDATE:
If the security/authorization around this is of paramount importance (rather than just wanting to stop it happening "by accident"), then using localStorage is completely the wrong approach - this data and its existence is ultimately controlled by the user. They can remove it, or set up their browser so that window instances don't share the data, so all they need to do is open a new window to view your video twice at the same time. A determined user would find their way around this in minutes.
If you want to control it absolutely, you have to take this domain side rather than relying on browser storage, and use some other tag like a list of currently-accessing IPs, or some other method of identifying a unique user, to determine whether the video can be played or not. Bear in mind that you would have the same issues as before regarding when to clear the flag whether it's browser side of domain side.
UPDATE:
re: what event to use, it appears that onunload and onbeforeunload are both fully supported across all common browsers (ref: Here and Here). This Answer recommends using both in order to be on the safe side.
UPDATE:
The OP has expressed worry that unload events are unreliable and that the user might remain locked out forever if something goes wrong. Personally I haven't experienced any unreliability here, but if you're worried, then introduce a timeout aspect. Have the tab playing the video update the flag (wherever it is stored) with a timestamp every 30 seconds/1 minute/whatever. Then when a new instance of the page loads, have it check the timestamp. if something has happened to the existing page such that it froze and unload events didn't run, the timestamp will be out of date because it will have also stopped updating, so you just have to check whether the timestamp is out of date as well as checking for presence.
Finally I gave up of the server side sessions because it raised other issues, and solved it with this workflow:
After page load, localStorage value is set if it hasn't been before, as well as flag that the player is opened in this tab. If the localStorage is already set, flag is set to false.
If flag is set, play video, otherwise prohibit.
On page unload, only if the flag is set (that is, if user opened video in this tab), remove localStorage value.
$(function () {
if (localStorage.playerTabOpened) {
var dateNow = Date.now();
var diffSinceLastTabOpened = (dateNow - localStorage.playerTabOpened) / 1000;
// if playerTabOpened value was stored more than 1 day ago, delete it anyway because it could be left by chance
if (diffSinceLastTabOpened > 86400) {
localStorage.removeItem("playerTabOpened");
};
}
if (!localStorage.playerTabOpened) {
shared.playerTabOpenedHere = true;
localStorage.setItem("playerTabOpened", Date.now());
} else {
shared.playerTabOpenedHere = false;
}
});
$(window).on("beforeunload", function () {
if (shared.playerTabOpenedHere) {
localStorage.removeItem("playerTabOpened");
}
});
if (shared.playerTabOpenedHere) {
// play
} else {
// throw error
}

How to pause a video when focus leaves my web page (html5)

I want to be able to pause a video when the user clicks on an href that links to a third party web site and start it again when focus returns. I have searched for events but am unable to find one that works eg onunload onchange. I have an event handler that starts a new video when one stops and scrolls down the page (javascript) but I am stuck on this problem. I tried an href that called a javascript function but it became messy (the href is generated dynamically).
window.addEventListener('focus', function() {
document.title = 'focused';
});
window.addEventListener('blur', function() {
document.title = 'not focused';
});
You can use this code to get focus and blue event for window tab and call your play and pause functions from here.
If the third party link opens in the same tab (as opposed to a new tab or popup window) you won't be able to just pick up where you left off until you save it.
You could potentially store current state data on the client using html5 web storage. You would want to fire this on the window.onbeforeunload event. When they return just check for any stored data to resume playback with. This obviously isn't supported on all browsers yet. If saving server side is an option you could do that as well.
web storage spec from w3 here
web storage currently supported by browsers here
If, however, the page never unloads, it just loses focus, you could just add a listener on the page's html to trigger pause on the blur event. Resume on focus.

Communication between multiple user opened windows with same url [duplicate]

This question already has answers here:
Communication between tabs or windows
(9 answers)
Closed 6 years ago.
I want to place a music player at the homepage (even maybe every page within same application/domain) and play songs on document load. But the user may open more than one instance of the page that contains this player at the same time.
So, if the user opened the windows w1, w2, w3 with same url, how to let only one window play songs at a time? For example, w1 plays, w2 and w3 do not. And when w1 is closed, w2 and w3 should be able to find this event, then elect for playing songs, and only the winner will be able to play songs.
Update:according the giving hints, I think this should be able to work:
// LOGICAL CODE
// Cookie["playerId"] - record which player instance is running
// Cookie["lastRefreshTime"] - record the running player last update time
// when running player instance is destoried (window is closed), and timeout,
// the left player instances should be able to modify Cookie["playerId"] for running.
function Palyer(){
this.playerId = randomString();
var _this = this;
this.refreshHandle = setInterval(function(){
// non-running players to check whether timeout, then competing
if( Cookie["playerId"] != _this.playerId
&& sysTime - Cookie["lastRefreshTime"] > 1000*2){
// competing for run, may have small probability going wrong
Cookie["playerId"] = _this.palyerId;
}
// running player to update timestamp for avoding timeout
if( Cookie["playerId"] == _this.playerId){
Cookie["lastRefreshTime"] = sysTime;
if (!_this.isInitialized()) {
_this.init();
}
}
}, 1000);
...
}
var player = new Player();
actually, you can do a workaround with cookies.
you check the cookies on load if you set a cookie that says a player is open.
you can declare a window closed cookie using onbeforeunload or onunload.
you can let the other windows poll the cookie value to check if a window was closed. then they can pick up from there.
but the problem is the accuracy of the cookie as well as timing.
what if the browser "skipped a beat" and forgot to declare a closed window?
what if the other tabs picked-up the close value event at the same time? both players will play?
what if the browser crashed and the window status cookie is left "open". how would you know if you came from a crash?
another way you can do this is via storage events using localStorage.
One LocalStorage per web application, with a max size of 5MB, is available for a given browser and is shared by all windows and tabs of that browser... If you run MyWebApp in multiple tabs and windows, they all share the same LocalStorage data , subject to a max limit of 5MB.(Chrome)
When data is added to, modified, or removed from LocalStorage or SessionStorage, a StorageEvent is fired within the current browser tab or window. That Storage event contains the storage object in which the event occurred, the URL of the document to which this storage applies, and both the old and the new values of the key that was changed. Any listener registered for this event can handle it.
but, the caveat, besides that it's an HTML5 tech:
Although the HTML5 spec calls for Storage events to be fired in all tabs of the same browser or all windows of the same browser, few browsers currently implement this.
#adeneo answered it: you have to use cookies to manage who has the power to play. It is complex but possible.

Categories

Resources