postMessage once the window loads successfully - javascript

I am trying to implement following feature
User opens up a page 1 on which there is a button. On clicking that button another window opens up but before that program checks whether the window is already open or not if it is already open than re-open the existing window or else open the new window.
While opening a window , I am passing some data to newly / existing open window using postMessage.
Now, the problem is when I am opening a window ( if it is NOT already open) then sometimes window is not able to receive the message sent by postMessage` hence I have used the setTimeout in following code.
However, this is not a reliable solution so I need a way where in newly open window receive the message each time.
Following is the code of service worker
let matchingClient = null;
for (let i = 0; i < windowClients.length; i++) {
const windowClient = windowClients[i];
if (windowClient.url === urlToOpen) {
matchingClient = windowClient;
break;
}
}
if (matchingClient) {
// if window is already open then just focus on window
matchingClient.focus();
// post the message
channel.postMessage({action: event.action,information:event.notification.data ? event.notification.data.info : ""});
} else {
//if window is not open yet, then open a new window
clients.openWindow(urlToOpen);
//have used setTimeout because opening a window may take time , however this is just a workaround
setTimeout(function(){
console.log("Delayed");
//post the message
channel.postMessage({action: event.action,information:event.notification.data ? event.notification.data.info : ""});
},4000);
}
});

Related

Why does JavaScript "window.postMessage" create duplicate messages?

I'm trying to open a popup window, do some stuff, and send a message back to the opening window so I can do some more stuff with the data it sends.
Essentially, I'm adapting the process outlined here.
This is my code on the "opening window". It runs when a social connect button is clicked. The code opens a popup window and assigns a listener event on the opening window to receive a message from the popup:
//Do the operation
let windowObjectReference = null;
let previousUrl = null;
const openSignInWindow = (url, name) => {
// remove any existing event listeners
window.removeEventListener('message', receiveMessage);
// window features
const strWindowFeatures =
'toolbar=no, menubar=no, width=600, height=700, top=100, left=100';
if (windowObjectReference === null || windowObjectReference.closed) {
/* if the pointer to the window object in memory does not exist
or if such pointer exists but the window was closed */
windowObjectReference = window.open(url, name, strWindowFeatures);
} else if (previousUrl !== url) {
/* if the resource to load is different,
then we load it in the already opened secondary window and then
we bring such window back on top/in front of its parent window. */
windowObjectReference = window.open(url, name, strWindowFeatures);
windowObjectReference.focus();
} else {
/* else the window reference must exist and the window
is not closed; therefore, we can bring it back on top of any other
window with the focus() method. There would be no need to re-create
the window or to reload the referenced resource. */
windowObjectReference.focus();
}
// add the listener for receiving a message from the popup
window.addEventListener('message', event => receiveMessage(event), false);
// assign the previous URL
previousUrl = url;
};
const receiveMessage = event => {
// Do we trust the sender of this message? (might be different from what we originally opened, for example).
if (event.origin !== websiteHomeUrlNoSlash) {
return;
}
const { data } = event;
console.log(data); //<--- THIS WHERE I'm SEEING DUPLICATES
};
//Invoke the function
openSignInWindow(url, name);
In the popup users login to their social account and then get redirected to a page on my app where the below code is run. The code posts a message back to the opening window and then closes the popup:
// Get the message data
const messageObj = {
pluginReason: pluginReasonVar,
displayName: displayNameVar,
provider: providerVar,
};
if (window.opener) {
// send them to the opening window
window.opener.postMessage(messageObj, websiteHomeUrlNoSlash);
// close the popup
if (closePopup) {
window.close();
}
}
Everything almost works as expected. Users can login to their social accounts and all the redirections and opening and closing of the popup works fine.
The Problem:
If users go through the Social Connect process multiple times without refreshing the page, then the message data that is printed to the console is duplicated more and more each run.
For example:
On the 1st run console.log(data) is printed once. So far this works as expected.
On the 2nd run console.log(data) prints twice. It should only be printed once.
On the 3rd run console.log(data) prints three times. It should only be printed once.
Each time the Social Connect process is run it should only print once. But somehow it's adding a duplicate copy on each subsequent run.
This duplication keeps growing until the users refreshes the page, which starts the count back at one.
I want to do more data manipulation at the point of the console.log(data) but I can't do that while it's creating duplicates copies on each subsequent run.
How do I stop that from happening?
Maybe it's because the listener event is not detaching? If so, how do I fix that?
You have created an anonymous method (event) => { } as a wrapper and attached it to the addEventListener method.
window.addEventListener('message', event => receiveMessage(event), false);
It can't be removed by
window.removeEventListener('message', receiveMessage);
To fix it, make changes like this:
window.addEventListener('message', receiveMessage, false);
Meanwhile, if the method receiveMessage gets lost every time the window has been closed, it's better to move the removeEventListener part inside the receiveMessage.
const receiveMessage = (event)=> {
window.removeEventListener('message', receiveMessage);
// do something else
}

Firefox add-on: how to tell if a window is in the background

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?

titanium, save the state of last viewed window

I am wondering if there is a function for saving the last viewed window when a user quits the application, so when they go into the app again, it goes to the last viewed page, instead of restarting. I have looked at Ti.App.Properties but haven't really found what I was looking for. Can anyone point me in the right direction, or if this is even possible.
Thanks
Ti.App.Properties is the way to go.
If you are just looking to save which window the user was on, simply save the current window that the user is on every time the window changes.
If you want to save the current state, as your title suggests, you will also want to create a javascript object that holds all the data on the page, do JSON.stringify(object) and then save that string in a property as well.
There is an example of code you can use to manage the state of the last opened window :
// Windows types
var TYPE_HOME = 'HomeWindow',
TYPE_BLUE = 'BlueWindow';
// Properties' keys
var PROPERTY_LAST_OPENED_WIN = 'lastOpenedWindow';
// open the last opened window
var win = openLastWindow();
// Uncomment to open the blue window
// Then, comment and reopen the app => the blue window will be opened
//createAndOpenBlueWindow();
// Save the last window configuration
function setLastWindow(params) {
Ti.App.Properties.setString(PROPERTY_LAST_OPENED_WIN, JSON.stringify(params));
}
// Create and open the last window opened
// (the function you have to call when you re-open the app)
function openLastWindow() {
var params = JSON.parse(Ti.App.Properties.getString(PROPERTY_LAST_OPENED_WIN, '{}'));
switch (params.type) {
case TYPE_HOME: return createAndOpenHomeWindow();
case TYPE_BLUE: return createAndOpenBlueWindow();
default : return createAndOpenHomeWindow();
}
}
// Create a window with the given params AND
// save this window as last opened window
function createAndOpenWindow(params) {
var win = Ti.UI.createWindow(params);
setLastWindow(params);
win.open();
return win;
}
// Create windows of type "HomeWindow"
function createAndOpenHomeWindow() {
return createAndOpenWindow({type:TYPE_HOME, backgroundColor:'#FF0000'});
}
// Create windows of type "BlueWindow"
function createAndOpenBlueWindow() {
return createAndOpenWindow({type:TYPE_BLUE, backgroundColor:'#0000FF'});
}
This code creates and opens the last window opened thanks to the openLastWindow function.
The first time you open the app, the Home one will be opened (the red).
By uncommenting this line :
//createAndOpenBlueWIndow();
you will open the BlueWindow the next time you run the app.
Then, re-comment this line. The blue window will be opened (since it is the last opened).
Hope this helps !

Check if window is already open window.open

I have a html page. In the body of the page I am calling onload event which calls javascript function to open a pop up window. here is the code:
var newWindow = null;
function launchApplication()
{
if ((newWindow == null) || (newWindow.closed))
{
newWindow = window.open('abc.html','','height=960px,width=940px');
}
}
when I move to another page, and come back to that page again, popup reopens, although it is already opened. Please guide me to proper direction so that if pop up is already open then it should not open again. I tried document.referred but it requires the site online, currently I am working offline.
newWindow = window.open('abc.html','com_MyDomain_myWindowForThisPurpose','height=960px,width=940px');
Give the window a name. Basing the name on your domain like this, prevents the chances of you picking a name someone else happened to choose.
Never make up a name that begins with _, those are reserved for special names the browser treats differently (same as with the "target" attribute of anchor elements).
Note that if the window of that name was opened with different options (e.g. different height), then it'll keep those options. The options here will only take effect if there is no window of that name, so you do create a new one.
Edit:
Note that the "name" is of the window, not of the content. It doesn't affect the title (newWindow.document.title will affect that, as of course will code in abc.html). It does affect other attempts to do stuff across windows. Hence another window.open with the same name will reuse this window. Also a link like clicky! will re-use it. Normal caveats about browsers resisting window-opening in various scenarios (popup-blocking) apply.
To open a window and keep a reference to it between page refresh.
var winref = window.open('', 'MyWindowName', '');
if(winref.location.href === 'about:blank'){
winref.location.href = 'http://example.com';
}
or in function format
function openOnce(url, target){
// open a blank "target" window
// or get the reference to the existing "target" window
var winref = window.open('', target, '');
// if the "target" window was just opened, change its url
if(winref.location.href === 'about:blank'){
winref.location.href = url;
}
return winref;
}
openOnce('http://example.com', 'MyWindowName');
You can check if the window is open or closed by re-assigning a reference to it when it closes. Example:
var newWindow;
var openWindow = function(){
newWindow = newWindow || window.open('newpage.html');
newWindow.focus();
newWindow.onbeforeunload = function(){
newWindow = null;
};
};
Use the "closed" property: if a window has been closed its closed property will be true.
https://developer.mozilla.org/en-US/docs/Web/API/Window/closed
When you move on another page (on the same domain), you can re-set the window.open variable with popup page like this :
https://jsfiddle.net/u5w9v4gf/
Step to try :
Click on Run (on jsfiddle editor).
Click on Try me (on preview).
Click on Run to move on another page, the variable will be re-set.
Code :
window.currentChild = false;
$("#tryme").click(function() {
if (currentChild) currentChild.close();
const child = window.open("about:blank", "lmao", 'width=250,height=300');
currentChild = child;
//Scrope script in child windows
child.frames.eval(`
setInterval(function () {
if (!window.opener.currentChild)
window.opener.currentChild = window;
}, 500);
`);
});
setInterval(function() {
console.log(currentChild)
if (!currentChild || (currentChild && currentChild.closed))
$("p").text("No popup/child. :(")
else
$("p").text("Child detected !")
}, 500);

Navigating Between HTML Windows

and thanks for taking a look at this seemingly simple question.
The scenario is as follows: I have a website Homepage named 'welcome.html'. From here users can select from a list of URLs and on clicking on the URL a simple Javascript function is called which opens a new Window.
Once the user has finished in the newly opened Window I want them to click on a button which calls a Javascript function to return them to the Homepage. It seems so simple.
However, if the Homepage Window is still open I want to return to this and not open another new Window displaying the Homepage. If the Homepage Window has been closed then I want a new Window to open displaying the Homepage.
I would be very grateful for some guidance here as I can't seem to get it to work.
Regards
Chris
As others have said, it is not the greatest design. However, I've run into scenarios like this in the past, where the business logic stipulates that a new window must be opened and there is no ability to change that.
As Pointy said, the biggest thing is just to keep track of the windows opened, and if you need some further help, perhaps this may help:
http://www.quirksmode.org/js/popup.html
But if possible, I would consider a different design (ask here if you need help implementing it!)
and thank you everyone who went to the trouble of sharing their thoughts.
I solved the problem in the following way:
When navigating from the Homepage to the new Window the following Javascript is used to open a new Window:
function popupFull(url)
// For explanation of this code see: http://www.quirksmode.org/js/popup.html
// Note: If fullscreen = 1 you can't see the menubar, toolbar, status etc.
// It is advisable to have no spaces around the commas in the parameters.
{
//alert("Opening: " + url)
// Prepare the parameter string
params = 'width='+screen.width;
params += ',height='+screen.height;
params += ',top=0,left=0';
params += ',fullscreen=0';
params += ',menubar=0';
params += ',toolbar=0';
params += ',directories=0';
params += ',status=0';
params += ',scrollbars=0';
params += ',resizable=1';
// Open a new window.
newWin=window.open(url, "fullWindow", params);
// If the current Window is in focus, switch focus to the new Window.
if (window.focus)
{
newWin.focus()
}
// Return the new Window object reference.
return newWin;
}
So the new Window can be opened and I have left the Homepage Window open but out of focus behind the new Window.
In the new Window there is a 'Menu' button. Clicking on this calls the following Javascript function:
function openMenu(winURL, winName, winFeatures)
{
// Create a reference of the Window which opened this Window. This should be
// the Main Menu Window.
var winObj=window.opener;
var menuOuterWidth = 1080;
var menuOuterHeight = 896;
var menuInnerWidth = 1068;
var menuInnerHeight = 767;
var menuX = (screen.width - menuOuterWidth) / 2;
var menuY = (screen.height - menuOuterHeight) / 2;
// Prepare the parameter string for re-opening the Menu
params = 'width='+menuInnerWidth;
params += ',height='+menuInnerHeight;
params += ',top='+menuY+',left='+menuX;
params += ',fullscreen=0';
params += ',menubar=1';
params += ',toolbar=1';
params += ',status=1';
params += ',scrollbars=1';
params += ',location=1';
params += ',resizable=1';
try
{
// Check to see if the window reference already exists.
if (winObj)
{
// Check to see if the Menu window is closed.
if (winObj.closed)
{
// The Menu window is closed.
// Open the Menu Window.
winObj = window.open(winURL, winName, params);
// Close this Course Window.
window.close();
// Return the Menu Window object reference should the caller want it.
return winObj;
}
else
{
// The Menu Window has not been closed. Set the Window's size and position.
// Note: When resizing the outerWidth/outerHeight value has to be passed.
winObj.window.resizeTo(menuOuterWidth, menuOuterHeight);
winObj.window.moveTo(menuX, menuY);
// Bring it into focus (bring to front).
winObj.focus();
// Close this Course Window.
window.close();
// Return the Menu Window object reference should the caller want it.
return winObj;
}
}
else
{
// The winObj object does not exist. Open the Menu.
winObj = window.open(winURL, winName, params);
// Close this Course Window.
window.close();
// Return the Menu Window object reference should the caller want it.
return winObj;
}
}
catch(err)
{
txt="There was an error on this page.\n\n";
txt+="Error description: " + err.description + "\n\n";
txt+="Click OK to continue.\n\n";
//alert(txt);
// When IE6 tries to obtain the winObj.closed property, when the window is closed, it can cause
// an error "Permission Denied". This error is caught here. Open the Menu.
// Open the Menu Window.
winObj = window.open(winURL, winName, params);
// Close this Course Window.
window.close();
// Return the Menu Window object reference should the caller want it.
return winObj;
}
}
The comments should explain everything. The key is to get a reference to the Homepage Window where we started. (var winObj=window.opener;).
What caused me such a headache was that if I opened the new Window (using IE6), switched back to the Homepage and closed the Homepage Window, and then in the new Window clicked on the 'Menu' button nothing happened! I tried everything and then, after a cup of tea, realised that I would never write code in any application I was developing without any form of error capture. I added a Try, Catch statement and reported the error in an 'alert'. I got a 'Permission Denied' error.
After a lot of reading I figured I couldn't eliminate the error I would simply handle the error as elegantly as I could. This resulted in the code above.
It works a treat and I hope this helps someone.

Categories

Resources