tejacques / crosstab Goto Github PK
View Code? Open in Web Editor NEWA utility library for cross-tab communication using localStorage.
License: Apache License 2.0
A utility library for cross-tab communication using localStorage.
License: Apache License 2.0
README should be updated to include more comprehensive documentation, and additional examples should be added.
If crosstab.notSupported
is true, crosstab should work by only handling events processed on it's own tab, and always believing that it is the master tab, and that it is the only tab. This way, logic doesn't need to be duplicated, you can always rely on the events firing from within a tab. This will also allow crosstab to be used on mobile without issue, it basically just acts as a fancy event emitter.
In onStorageEvent, it is optimistically parsing values as JSON and then assuming that the result is an object. This causes "eventValue" to be "null" and then "eventValue.id" to fail.
There are different ways of solving this, but I would have thought that crosstab should be checking the "key" property of all incoming StorageEvents and only processing ones that are relevant to crosstab.
Is using:
if (event.key.indexOf('crosstab') !== 0) return;
a possible fix?
Here is the wrapper used by umdjs: https://github.com/umdjs/umd/blob/master/amdWeb.js
Here is the wrapper used by some folks at Mozilla: https://gist.github.com/wilsonpage/8598603
Hi tejacques, I am using your cross tab js. This issue occurs on Windows 7 IE9. As higher version of IE on windows 7 machine is IE9.
Steps to reproduce:-
There are a few implementations of such shims available on the internet, but it's not clear to me whether there can be data lost due to race conditions. If there is a way of shimming this on IE8 in a way that is fully compliant and not subject to race conditions then it we should support and test that shim.
Here is what i've found.
Say we have 2 pages: http://localhost:3000 and http://localhost:3000/index2.html. Each of these pages runs a script which 'unshifts' current url into array which is in localStorage. When we first open http://localhost:3000 we have [http://localhost:3000]. After navigating to http://localhost:3000/index2.html array becomes [http://localhost:3000/index2, http://localhost:3000] and so on. But some browsers (I tested in Google Chrome 47 and Safari 9) prerender pages. So when we type 'http://local' in address field, then choose http://localhost:3000/index2, change out mind, delete /index2 and go to http://localhost:3000 instead we have this: [http://localhost:3000, http://localhost:3000/index2, http://localhost:3000/index2, http://localhost:3000]. It happens because script on http://localhost:3000/index2 was executed in background mode.
I noticed that in same cases crosstab decides that my tab is not master though I only have one tab open. After studying this problem by looking at localStorage I found out that when I open new tab another nonexistent tab is already set as master. After some time (3 secs if I'm not mistaken) my tab regains master status (obviously because of keepalive loop) and everything is ok. It happens in 90% of cases. But sometimes frozen-environment problem occurs.
I think that this problem is caused by page prerender. I was able to solve it by removing frozen tab detection (here https://github.com/tejacques/crosstab/blob/master/src/crosstab.js#L753 and here https://github.com/tejacques/crosstab/blob/master/src/crosstab.js#L646-L653). Instead I forced new master tab reelection.
I suggest using page visibility API (https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API) and perform side effects only when page is not in prerender mode.
Repro:
crosstab/samples/index.html
You may need to do this a few times to see the behaviour, but I usually get it within a few tries. Once in this mode of no Master performing subsequent "Refresh All"s does not fix the problem. One of the tabs appears to think it is Master (but is not reflecting that state) as refreshing the individual pages one by one eventually fixes the issue (on a specific tab).
While this may not be a typical user scenario I have seen it in production when a number of pages load at once (automatic logon after re-authentication).
I've also noticed that under heavy load (JS intensive pages) localStorage can become a bit flaky with the timing of events, causing similar issues with the election process (this is a bit hard to show repro steps for).
Edge supports service workers as of Edge 17. This should finally make it possible to support Edge, as it'll be possible to pass messages through a service worker instead of through local storage.
Just wondering if this is something you'd be interested in implementing / accepting a PR for, or if the project is pretty much unmaintained now?
This can be resolved by combining the general detection check with the frozen tab environment check.
Currently they don't because crosstab isn't supported, however there's no technical reason this can't work. This should be investigated.
is it possible to have only the active tab execute a setInterval() function only on the active tab?
so if I am on tab A then execute setInterval() function which triggers an API page. then when I open tab B then the setInterval()on tab A will need to stop and the function on tab B starts.
is this possible and how?
thanks you
Looking into ci.testling.com and saucelabs.com.
I think it would be quite helpful to add the feature of setting a custom master tab from any of the other tabs. One of the use cases would be when someone wants one of the non-master tabs to open a websocket and disconnect from the master tab.
For example, in my case, I want to limit the number of sockets my application opens. But at the same time, I want to avoid cases where user experience is affected due to closed sockets. So, by default, my app opens the connection on the master tab. But when the user interacts with one of the other tabs, I would want to make it a master tab and then open a socket. At the same time, previous master tab will close the socket.
Hi,
You included the Apache license but I can't seem to find your copyright.
(See 'How to apply the Apache License to your work.' of the license)
Would you be able to add that?
Thank you
This is pretty damning for Edge, because it takes about 3 seconds for the localStorage data to sync across tabs, meaning a 6s latency (tested on saucelabs). This is unusable. For now, the only thing that can be done is to detect Edge and mark it as supported = false
in combination with the upcoming v0.3.0
changes.
No reelection occurring after MASTER_TAB refresh on a iframe.
Steps for reproducing:
Result:
{
"14488945006270009571866": {
"id": "14488945006270009571866",
"lastUpdated": 1448895188388
},
"MASTER_TAB": {
"id": "14488944316952140456572",
"lastUpdated": 1448894501270
},
"14488945024330039719292": {
"id": "14488945024330039719292",
"lastUpdated": 1448895189539
}
}
localStorage currently has several issues and is not reliable in IE/Edge on Windows 8 and 10. Microsoft will need to fix this before localStorage can be used reliably. There might be an alternative to use with IndexedDB and polling, but it's not as performant.
Is it possible that the detection sometimes has false positives for some reason, or that the timeout is simply too low? Maybe the detection mechanism is fuzzy or racing?
Could it be possible to make this automatic detection of "frozen tabs" optional?
It could be let to the application how to deal with the fact that a broadcast
call might not reach all tabs eventually or with a substantial delay.
Hi,
I am using this js in my web application however in chrome i always get this error. When i clear localastorage first time it works fine as soon as i open second tab with same web app url i am getting "Uncaught Error: crosstab not supported: frozen tab environment detected" message in console.
Any suggestions?
Found because of mozilla/fxa-content-server#1848.
Firefox lets a user block access to localStorage & sessionStorage through a browser preference independently of cookies. If the browser preference is set to disable access to localStorage, the initial tab fetch excepts.
about:config
dom.storage.enabled
to false
Line 241:
var json = localStorage.getItem(key);
TypeError: localStorage is null
Startup completes, but crosstab.supported is false
The above exception
Example doesn't work - please check it. Browser Chrome - errorText - crosstab not supported - without explain. FF - ok
I see in the code you have an isMaster
function that is not being used anywhere. I was wondering why that is not exposed. It seems like it would be useful.
I'm trying to make my app only open a websocket connection on the master tab. Maybe I am approaching it wrong. My idea was to do something like this:
var crosstab = require("crosstab");
if (crosstab.isMaster()) {
//open websocket connection & broadcast on crosstab when websocket messages come in
} else {
//listen for crosstab events
}
How would I go about making this work?
This is a pretty simple addition and can be done similarly to how analytics and gpt work.
<script>
(function (window, document, script, url, crosstab, scriptTag, firstScriptTag) {
window[crosstab] = window[crosstab] || (function (fn) {
(window[crosstab].q = window[crosstab].q || []).push(fn);
})();
scriptTag = document.createElement(script);
scriptTag.async = true;
scriptTag.src = url;
firstScriptTag = document.getElementsByTagName(script)[0];
firstScriptTag.parentNode.insertBefore(scriptTag, firstScriptTag);
})(window, document, 'script', '//url.to/crosstab.js', 'crosstab');
</script>
This minifies to the following:(this is beautified to be readable but get the point across)
(function(w, d, s, u, c, n, r) {
w[c] = w[c] || function(f) {
(w[c].q = w[c].q || []).push(f)
}(),
t = d.createElement(s),
t.async = !0,
t.src = u,
r = d.getElementsByTagName(s)[0],
r.parentNode.insertBefore(t, r)
})(window, document, 'script', '//url.to/crosstab.js', 'crosstab');
When crosstab is setting up, it should look for a global crosstab variable, and try to grab the cmds out of it. If present, it should call crosstab(fn) for each one.
This isn't a critical feature, but it's nice to have
Hi,
We ran into an issue when language is changed from one tab, it is not being reflected in another already opened tab. We are using below code to set language in cookie.
if (angular.equals(addcookie, "Yes")) {
$cookieStore.put('lang', langKey);
}
Could you please explain how do we use your extension in AngularJS in order to reflect the language change across all the opened tabs/windows ? Also does it work with open windows too apart from open tabs ?
Unless I'm missing something, crosstab doesn't actually open/enable communication between browser tabs. Is that right?
The sample seems to only add a Div to the same tab you're on. I was hoping for an effective way to coordinate two different browser tabs, one spawned by another calling window.open().
I'm going to play with it some more, as maybe the sample doesn't illustrate this functionality even if it does exist.
I notice that the becomeMaster
event doesn't seem to work as expected, it doesn't necessarily fire when a tab becomes master.
I also notice that your sample crosstab\samples\index.html
doesn't use the becomeMaster
event, instead it looks at the tabPromoted
and tabUpdated
events and updates the state of the UI (after a setTimeout
) to the new state.
I would have thought the becomeMaster
event would be the only event I would need to subscribe to for knowledge of who the master is (unless I specifically wanted to know when a tab was updated).
Link to #2
Similar to #2, if the user disables cookies in Firefox, then access to localStorage is also disabled. As soon as the library loads and fetches a reference for window.localStorage
, an exception is thrown.
about:preferences#privacy
History
, change Remember History
to Use custom settings for history
Accept cookies from sites
Startup completes and crosstab.supported
is false
.
8
var localStorage = window.localStorage;
SecurityError: The operation is insecure.
Hi,
I'm not sure why this happening, I'm using crosstab in my application. It works fine initially, but when the page load increases or if my application get crashed, crosstab is not working. It throws "crosstab not supported" error. Even after restarting the browser this issue is coming.
Steps to reproduce:
This totally bones my tests, and is a common crosstab use case. Can potentially be fixed by ensuring events have unique IDs and only acting on a unique ID once. We can just keep track of the eventIDs that have happened in the last ~2 seconds and clear out the rest.
Uncaught Error: crosstab not supported: localStorage not availabe, addEventListener not available, localStorage.setItem not allowed
The context variable this
passed to the factory function refers to module.export instead of window object in Browserify environment.
2bf8403 seems to cause this so the problem exists only in 0.2.8
Currently thinking something along the lines of this:
crosstab({ event, data, destination, callback, timeout })
It could be used like this:
Setup
crosstab.on('PING', function(message, reply) {
if (message.destination === crosstab.id
|| (!message.destination && crosstab.isMaster())) {
reply('PONG'); // replies directly to this message
}
});
It's still a little cumbersome to indicate that this should only handle direct messages, or if it is the master. This could be resolved by having some helper defaults:
crosstab.onDirect(event, fn(message, reply));
crosstab.onMaster(event, fn(message, reply));
crosstab.on(event, fn(message, reply), type=DIRECT | MASTER);
Call
crosstab({
event: 'PING',
callback: function(response, err) {
if(!err) {
console.log("Received: ", response.data, " from: ", response.origin);
} else {
console.log("Error: ", err);
}
},
timeout: 1000
});
crosstab.broadcast(event, data, destination, callback = null, timeout = infinity);
crosstab.broadcast(event, destination, callback = null, timeout = infinity);
crosstab.broadcast(event, callback = null, timeout = infinity);
crosstab.broadcastMaster(event, data, callback = null, timeout = infinity);
crosstab.broadcastMaster(event, callback = null, timeout = infinity);
This will greatly reduce boilerplate and increase ease of use for creating tools based on crosstab.
It also makes it easy to create a Promise
wrapper using the callbacks.
Look into using mocha/phantomJS for testing.
I've tested reloading 4 tabs at the same time, and in the end I've got the following result:
1st Tab - 14455266368170676283082
2nd Tab - 14455266379341197551397:
3rd Tab - 14455266379810761529770:
4th Tab - 14455266385181543926377:
So, every tab thinks that 2nd tab is the master, except the 2nd tab itself and the localStorage.
So nobody is assuming the master work.
On localStorage (crosstab.TABS_KEY) there's only 2 tabs registered:
{
"id": "14455266368170676283082",
"data": {
"14455266368170676283082": {
"id": "14455266368170676283082",
"lastUpdated": 1445526636819
},
"MASTER_TAB": {
"id": "14455266368170676283082",
"lastUpdated": 1445526636819
},
"14455266379341197551397": {
"id": "14455266379341197551397",
"lastUpdated": 1445526637942
}
},
"timestamp": 1445526638301
}
I'm thinking that the problem could be on localStorage concurrency (everyone is writing and just the last one result wins).
I'm using crosstab version v0.2.11 on Chrome 46.
I have a very basic implementation of this using on 1 page:
crosstab.on('issue-a-item', function(data) { console.log('Called Crosstab'); $('#barcode-search').val(data.data.barcode); lookupItem(true); });
on another page:
$('body').on('click', '.issue-this-item', function(e){ e.preventDefault(); var tr = $(this).closest('tr'); var row = window.LaravelDataTables.dataTableBuilder.row(tr); var data = row.data(); crosstab.broadcast('issue-a-item', { 'barcode' : data.barcode }); setTimeout(function(){ window.close(); },500); });
Still works on firefox but on chrome and IE (IE broke last year) it now gives me the Frozen Tab error on clicking "issue-this-item".
Is there any alternatives to crosstab because I need this to work and I see from another issue you have said that you don't have the motivation to work on this.
Currently the event emitter doesn't fire listeners in the order they were added. Could potentially just swap to node's event emitter.
Currently frozen tab environments, where only the active tab is executing javascript, are detected using the user-agent. There is no feature or group of features than can be used to cover all scenarios, so in order to detect this, we'll have to get inventive.
Change crosstab from an object to a function, which takes an object / function that will be executed once crosstab setup is complete.
When a tab comes up, check if localStorage crosstab.frozenTabEnvironment
is set to true. If it is, crosstab is not supported.
Otherwise check the user-agent against known frozen tab devices. If there is a match, set localStorage crosstab.frozenTabEnvironment
and set crosstab to not supported.
Otherwise check if there is another tab registered as master. If there is attempt to PING the master. If we do not hear back within a short timeout (<50ms), then set localStorage crosstab.frozenTabEnvironment
to true. When we hear back from the master, we will fire the crosstab.setupComplete
event.
Otherwise register as normal, and fire the crosstab.setupComplete
event, but check crosstab.frozenTabEnvironment
when updating. If it is set, disable support.
Anywhere in code where you want to use crosstab, you should always check something in the style of:
function crosstabSupported() {
// code here will execute once crosstab setup is complete if crosstab is supported.
}
function crosstabUnsupported() {
// code here will execute once crosstab setup is complete if crosstab is unsupported
}
if (window.crosstab) {
crosstab(function() {
if (crosstab.supported) {
crosstabSupported();
} else {
crosstabUnsupported();
}
});
} else {
crosstabUnsupported();
}
This method will fail if the master tab is currently under enough load where 50ms is not sufficient to respond to a PING, however this should never practically occur. The one-time 50ms penalty for nondetected frozen tab environments should not be prohibitive
Digging into it revealed that localStorage in IE seems to be absolutely broken by design.
http://kntajus.blogspot.com/2014/08/local-storage-fundamentally-broken-in.html
https://connect.microsoft.com/IE/feedback/details/811546/ie11-localstorage-events-fire-twice-or-not-at-all-in-iframes
Even better!
https://connect.microsoft.com/IE/feedback/details/812563/ie-11-local-storage-synchronization-issues
Rising the PING_TIMEOUT or even all the timeouts didn't work.
So here's a starting point to see the problem:
10.0.2.2 host1
and 10.0.2.2 host2
to c:/window/system32/drivers/etc/hosts (for this launch Notepad as Admin)ng-src="http://host1:9000/samples/index.html"
Egde
or IE10
http://host1:9000
and try to add more iframeshttp://host2:9000
and try to add more iframesA declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.