Communicating between different windows on the same domain [duplicate] - javascript

This question already has answers here:
Communication between tabs or windows
(9 answers)
Closed 6 years ago.
I am building an app that performs a lot of client side data downloading and processing. The data processing is isolated from the main app by being processed in an iframe that resides on a sub domain. It is this iframe that downloads the data. Communication is via postMessage.
Everything works fine, except it could be better.
If the user opens extra tabs/windows, the app currently reloads all the data and may even do duplicate processing work, which isn't a problem other than that it slows everything down and pages take longer to load.
What I would like to do is have each top level tab/window communicate with just the one processing iframe, which could be reinstated if the original window is closed. The trouble is, these are not opened via javascript, but via the normal browser methods to open links in tabs so I can't get a reference to the iframe that is needed to send a message.
Is there anyway I can communicate the window reference for the iframe to the other tabs so that they can communicate with it via a postMessage? Could this in someway be achieved using shared workers?
I realize I could use shared workers for the whole processing task, but this would have it's own problems as the the data comes from third party domains, which can't be accessed from within a worker.
Only compatibility with the latest versions of all major browsers is needed.
Edit: I've just discovered that SharedWorker is not yet implemented in firefox, so I guess that is not going to work. Any other way I could achieve this?
Edit 2: I've discovered that you can use :
var win = window.open('', 'my_window_name');
to capture a reference to an iframe from any other window. However, if the iframe does not already exist then it will open it as a window. Even if it is closed immediately, it causes a flicker and causes the 'popup blocked' messages, making it unusable.

In case any one else finds this, I've come up with a solution. It is somewhat hacky and requires further testing. But so far it is working. It works cross domain if that is needed.
It uses a combination of two tricks.
The first is to use
remote_window = window.open("", "remote_window_name");
to fetch a reference to the window. This works because if a window is already open with the given name then a reference is returned to it rather than opening a new window.
It does however have the problem that if the iframe does not exist then a new window will pop up. Local storage is used in order to prevent this. When a window/tab loads, it checks localStorage to see if there is another page already with a shared iframe. If not it inserts the the iframe and sets a flag in local storage to say that it is available.
As a last ditched resort, if the window still opens, a try block is used to close the newly opened window. The try block prevents cross domain errors. This means that the worst that will happen is the user sees a window pop up and disappear or they will see the 'enable pop-ups' message. I've yet to manage to trigger this in testing - it is only an edge case fall back.
try {
if(store_window.location.href === "about:blank" ){
remote_window.close();
remote_window = insertIfame();
}
} catch(err) {
}
An onunload event is added which removes the flag should the page be closed.
Also a setInterval is created that constantly refreshes a timeout flag. I have it running 4 times a second; when a second window/tab is loaded it checks that the iframe flag has not timed out before trying to communicate with it. This is a small overhead, but far less than the cost to me of having that second iframe loading. This serves the purpose of preventing stale data if the browser crashes or the the onunload does not fire for any reason. I include a small leeway when checking the timeout - currently 1 second - in case the main window is stuck in a loop. The leeway is only on the timeout, not the unload event which removes the flag entirely.
The flag needs to be checked every time a message is sent in case the original window with the iframe has closed. When this happens the iframe is reinserted in the first open window that requires it and the flag is reset.
Sending messages back is easy. Just use the event.source property of the receiveMessage -this points to the sending window.
One final edge case to account for is if the primary window closes whilst it's iframe is mid process for a secondary window. Theoretically this could be dealt with by using an onunload event in the iframe to send a message back to any windows with data in process. But I've yet to implement it and it may not finish before the page unloads. Another way of dealing with it would be by having a timeout in the secondary window which checks the flag and retries, I'll probably go this route as the messages already have timeouts attached.

Related

Using external javascript code to run a snippet on the Chrome console

Is it possible in an external javascript code (for example, a userscript through tampermonkey) to run a code snippet on the Chrome console. For example, console.log prints text to the console. Is there some way, like a function console.eval or some more complex way where I can run code on the console without manually opening it on the given website, but using the original javascript code behind the website or a userscript?
Notes: I use Google Chrome on Windows 10. Preferably this answer should be as generally applicable as possible, but first priority for me is for it to work in my environment.
Thanks,
Mike
Uk, when i said if the page is reloading constantly, the "console" that u think of would also reload??, a lot of us knew about what I'm doing below(if not all of us) but I finally connected it with your question. Using one tab to control the other tab
ONE EDIT: I used an interval to determine if the controlled tab is CLOSED(since a certain value eventually changes if the tab is closed for good)
HOW TO USE:
Open a tab with the same origin as desired url(but not the constantly reloading site)..
eg: opening a tab on "https://example.com/404" if desired url is "https://example.com" is the desired url(the constantly reloading one)
In the code snippet I have below, you can put your tab controlling code in the loadFn function, where myWindow and this point to the controlled tab's window
eg: in the loadFn function, myWindow.console.log(1) or this.console.log(1) would both log 1 to the controlled tab's console
SECOND EDIT: I shall explain how it works(and talk about unloadFn as you requested in comments)
I use a combination of unload and load listening to be able to repeatedly send code "on reload" which is not an event in itself so I had to create it. In case I didn't explain myself, I'd go into detail now..
When a page is reloading(or when I'm JUST SPAWNING the page, eg: var myWindow=window.open(desiredUrl)), the unload event happens. There's just one problem however; every time the page is reloading, all event listeners and any code you put is removed(because reload unloads to then reload)
The solution is simple: on every unload, I set the listners again, and since the function would call itself(every time the page unloads), the listeners would successfully be reloaded every time the page reloads(and that is why loadFn could run in the other tab after every reload)
DO NOTE: You might ask "why use a setTimeout then?". Actually it's quite important. Without the setTimeout, the event listeners DO NOT GET ADDED, I think it's because the tab would ignore your commands(since it would be focusing on loading its default stuff(like event listeners for instance)), and asynchronous programming does wonders in this case because it will wait until the other stuff are processed(like event handling stuff) then run
SIDE NOTE: If that's not why setTimeout works and NOT USING it doesn't, all I know is that without it, it doesn't work, and with it, it works
var myWindow=window.open(desiredUrl) //remember to run this code on the same origin as the desiredUrl
function loadFn(){
//this will happen every time myWindow loads or reloads
myWindow.alert("It runs in the controlled tab")
myWindow.console.log("Even in the controlled tab's console it works >:D")
}
function unloadFn(){setTimeout(()=>{
myWindow.addEventListener('unload',unloadFn)
myWindow.addEventListener('load',loadFn)
if(!myWindow.Window){console.warn("myWindow was CLOSED")}
},0)}
myWindow.addEventListener('unload',unloadFn)
//extra thing below to tell if controlled tab is closed >:D
var i=setInterval(()=>{
//for if controlled tab is closed
if(!myWindow.document.location){clearInterval(i);console.warn("myWindow was CLOSED")}
},0)

Get tab URL from page action (WebExtensions, Android)

I would like to get the URL of the current tab within a page action popup.
At first it seems obvious: Just use the tabs API. But that doesn't seem to be available on Android if I deciphering the docs correctly. So I kept looking for something else and found the onClicked event of the pageAction API.
The pageAction API seems to be listed as compatible with Android and the onClicked event is marked as supported. So that implies that it would actually return a tabs.Tab object. But does it really? Has anyone tried it?
What is the best way to retrieve the URL? I know I could just use a content script and let that run in every single tab and create a long lived messaging connection to send the URL to the page action popup whenever it is requested. But that would be very inefficient and make the code insanely complicated compared to how easy it would be using the tabs API.
Is there anything else I could do?
Current (Firefox 54 and later)
As of Firefox 54, the tabs API is available in Firefox for Android. This means you can use the normal methods available to desktop Firefox. Specifically, chrome.tabs.query() or browser.tabs.query(). However, you will need the activeTab and/or tabs permissions in your manifest.json.
chrome.tabs.query:
chrome.tabs.query({active:true,currentWindow:true},function(tabs){
//'tabs' will be an array with only one element: an Object describing the active tab
// in the current window.
var currentTabUrl = tabs[0].url;
});
browser.tabs.query:
browser.tabs.query({active:true,currentWindow:true}).then(function(tabs){
//'tabs' will be an array with only one element: an Object describing the active tab
// in the current window.
var currentTabUrl = tabs[0].url;
});
Prior to Firefox 54
If you have defined a page/browser action popup
If you have defined a popup for your page/browser action, then the onClicked event does not fire. Your popup is not passed any information when it is created/shown. Thus, you will not receive a tabs.Tab object. The normal way to obtain tab information is from tabs.query, which, as you have already determined, is not (yet) available in Firefox for Android.
The APIs available to Firefox on Android are quite limited. For what you are wanting to do, using webNavigation events to keep a record of each tab's frame 0 URL would be more efficient than a content script in every page. You could use the webNavigation.onCommitted or webNavigation.onCompleted events depending on your needs. You will need to assume that the active tab of the current window is the one which most recently had a webNavigation event, or perhaps you could also monitor webRequest events. However, any way that you do it, which tab you assume to be the current tab will just be an assumption, and will be inaccurate under some circumstances.
A more accurate URL (and active tab determination) requires using a content script
If the page that is being visited changes the tab's URL through some method that does not trigger navigation, using webNavigation events will not give you the updated URL. As you have identified, without the tabs API, the only way to be certain you have the actual current URL for the tab, when you define an actual page/browser action popup (and thus don't get a tabs.Tab object), is to inject a content script into every page. You will either need the content scripts to continuously update the URLs, or be listening with storage.onChanged for a request for that information from your background/popup script. Communication from the background/popup scripts to content scripts must be accomplished through the storage API due to not having the tabs API (i.e. no tabs.sendMessage).
Using page/browser action onClicked
An alternative, which I have not tried on Firefox on Android, would be to not define an actual page/browser action popup. If you don't define an actual popup, you receive the onClicked event (getting the tabs.Tab Object with both the active tab's ID and the URL) and then can open a pseudo-popup1.
1. The implementation of opening a a pseudo-popup in my linked answer uses the tabs and windows APIs which are both currently unavailable for Firefox for Android. It could be re-written to use the above mentioned webNavigation listener to track tab URLs and window.open() to open the window used for the pseudo-popup. Again, I have not tested this with Firefox on Android to determine that it actually works.
You can get it this way with webextensions. Take into account that if you want to debug a popup, you have to "prevent popups to be closed" (4-squares icon at the top-right of the browser's toolbox)
var activeTabPromise = browser.tabs.query({active: true, currentWindow: true});
activeTabPromise.then((tabs) => {
console.log(tabs[0].url);
});
I hope this will help you,

Window focus for a faster loading pop-up

I am very new to JavaScript. Kindly note that I am trying below issue in a shell which overrides many JavaScript functions.
I have an issue with focusing a window: on a single "click" action, I navigate to a new page which has two JavaScript methods which launch two external URLs which I don't own. For example I launch Yahoo.com and Google.com. My JS launches Yahoo.com in current window (as a page navigate) and Google.com as a pop-up. I WANT Google.com WINDOW TO BE FOCUSED irrespective of loading time of either URLs. The major issue is I cannot use the setTimeout JS function as this function's behavior is altered within the shell and is not usable.
Note: I am using a custom reusable JS function to launch external URLs and I just pass values to that method. So I don't even have access to window object. If I can somehow achieve a time delay without using setTimeout, it will be ideal case. If not, I will have to override that custom JS function, get access to the window object. Even if I have control over those window objects for external URLs, since loading times are different, setting focus to the Google window object is not always giving me the focus on Google window.
(IE6 & 7)
You cannot guarantee the behavior you want, in general; browsers will not let you.
Safari generally ignores requests to focus windows. Firefox and I think Chrome can be configured by their users (not by your code) to allow focus requests, but by default they won't.

How distinguish refresh and close event using jQuery? [duplicate]

This question already has answers here:
Identifying Between Refresh And Close Browser Actions
(13 answers)
Closed 6 years ago.
I am currently looking at the "unload" event of a window to try to determine how the "unload" event was triggered, but am having little success. Is there a way to determine how the javascript event was triggered?
Page Refresh
Back Button (or navigate away from the page)
Closing the Browser
Essentially I need to execute some code only when the browser window is being closed, not refreshed or navigated away from.
Purpose: When a customer does an update of our software, the update will redirect their first Internet request to an offer page. There is a button for a "Do Not Bother" option, but some users will simply close their browser. Upon closing the browser, I need to duplicate the "Do Not Bother" functionality so the user no longer gets redirected to the offer page. Simply attaching to the "unload" event will not work due to the different ways of leaving a page.
No, and if there was it would be browser dependent.
What kind of code are you trying to run when the user closes the page?
Is it to logout the user?
Then the user would not be logged out if the browser crashes or the network connection breaks (and probably not if the computer goes to sleep/hibernation mode).
If it is for logout-purposes you should probably use a timestamp variable at the server that gets updated with every request (or use a ajax-ping), and logout the user if it hasn't been seen for a specified time.
Update: Found this answer here at stackoverflow.
Yes, there is a solution!
I've designed a solution based on onBeforeUnload+onLoad events, HTML5 local storage and client/server communication. See the details on https://stackoverflow.com/a/13916847/698168.
I use a method of doing keyboard "sniffing", in that it looks for keydown's of "F5", "ctrl+r", "alt-f4", "backspace" and others, and if it finds them flowing through the keyboard event queue, it sets boolean variables appropriately to trap that status... then I use a "onbeforeunload" function handler, which tests against those boolean status variables to decide what to do.
You can even shut down various keyboard strokes (like "ctrl+n" or "F1" for instance) by using preventDefault(), bubbles=false and returnValue=false in your keyboard handling.
This stuff is not for the faint of heart, but its certainly doable with some persistence and lots of cross browser testing!

Ways to detect CTRL-N or when a user opens a new window

How can we detect when a user opens a new window. The user is already authenticated and we make heavy use of sessions.
We were trying to avoid Ctrl+N javascript hooks but maybe that is an option.
I am assuming the request is the exact same URL...with Ctrl+N?
We were trying to avoid ctrl-n javascript hooks
Forget it. Whilst you could in theory try to catch keypress events for ‘n’ with the Control key modifier, there are any number of other ways to open a new window or tab which may be more likely to be used, and you won't be able to catch. File->New Window/Tab, middle click or shift-click link, middle click back/forward buttons, right-click-open-in-new-window, open bookmark in new tab, double-click browser icon...
The user is already authenticated and we make heavy use of sessions.
That shouldn't be a problem in itself. I guess what you mean is that your application is dumping all sorts of page-specific data in the session that it shouldn't have, and now you find the application breaks when you have more than one window open on it? Well, commiserations and happy rewriting.
In the meantime about all you can do is tell the user “please don't try to open two browser windows on the same application”. There are potential ways you can make JavaScript on one page notice that JavaScript is running on another page in the same domain at the same time, generally involving using document.cookie as a inter-page communications conduit. But that's also a bit fragile.
If opening a new window causes a problem in your application, then you should fix the application code to handle it instead of trying to apply an inconsistent and unreliable client-side "bandage". That's my opinion.
Why?
And anyway you can't detect it. User can open new window not only with Ctrl+N but also with File->New Window.
You could possibly put a window count into the session and increment it on window.onload and decrement it on window.onunload.
Imagine me tutting, sucking air through my teeth and going "better you than me, guvna" if you use that, though.
What I have done to solve this issue is when the user authenticates set the window name on valid login.
<script>
window.name = 'oneWindow';
</script>
And then on the master page do a javascript check:
<script>
if (window.history.length == 0 || window.name != 'oneWindow')
//history length to see if it's a new tab or opened in a new window 0 for IE, 1 for FF
//window name to see if it's a CTRL + N new window
</script>
If the check is true then hide/remove the main content of the page and show a message stating they are doing something unsupported.
This works when your login page is not tied into the master page.
If you do not have a master page then I would suggest putting the check on all your pages.
Yes and no,
You'll always see it if a control has focus, else the event is sent directly to the browser and the code on the page never hear about it.
In my experience you can't hijack the browser's shortcut, your mileage may vary. You are likely to know it happened but the browser will do its thing (for obvious reason)
In most browsers, the effect of Ctrl-N is to open a new window at the same URL as the old one and associate it with the same sessionID.
Your best bet would be to modify the back end code if possible and allow for such things. Breaking the browser's feature is never a good thing.

Categories

Resources