I've got desktop application that works as simple browser. It has tabs with webviews that show web pages.
Some websites have social sign-in option. And I really need it to be available for my users.
In general browser (e.g. chrome) when you sign in via Facebook, it creates new window with social authorization form. After submitting this form the new window closes and parent window receives callback code, reloads and sign you in.
In my app when you try to do the same, webview that contains the web page, creates new BrowserWindow with the same auth form. At this point everything works fine. When you submit form, the new window closes but nothing happens after. Parent window doesn't receive any callback, the code that should run isn't triggered at all. Or probably it has been received, but not triggered, since if I reload page, it shows that I'm signed in.
According to the electron documentation <webview> runs in a separate process than renderer process. So I think when new window closes, the callback is received by parent BrowserWindow (renderer process) and not runs exactly in webview frame.
While searching through different sources for solution, I found that this problem is also concerned with window.opener, that was not fully supported in the earliest versions of electron. But according to the issue#1865 on github full window.opener support is now available if I open window by window.open method.
To make this work I should prevent webview to open new BrowserWindow and create it by myself.
The following code fails:
// in renderer process
webview.addEventListener('new-window', function(event) {
event.preventDefault();
// I also tried 'event.defaultPrevented = true;'
// or 'return false;'
});
The listener is triggered, but I can't prevent new-window opening.
Someone wrote that new-window can be prevented in main process only.
So I add the following code in my main.js:
// in main process
var electron = require('electron');
var BrowserWindow = electron.BrowserWindow;
mainWindow = new BrowserWindow({
"width": 970,
"height": 500,
"center": true,
'title': 'Main window',
"autoHideMenuBar": true
});
mainWindow.loadURL('file://' + root + '/index.html');
mainWindow.webContents.on('new-window', function(event) {
console.log(event);
});
This time event 'new-window' is not triggered at all. I think this is because the event works only if renderer process requests to open a new window by window.open for example. But in my case new window is opened by <webview> tag, not by renderer process. So I failed to prevent new-window to open since these are two separate processes.
So I decided just to get new-window after it opens.
// in renderer process
var electron = require("electron");
var remote = electron.remote;
webview.addEventListener('new-window', function(event) {
var win = remote.BrowserWindow.getFocusedWindow();
var contents = win.webContents;
win.setIcon(root+"/img/favicon.ico");
win.setMenuBarVisibility(false);
contents.on('dom-ready', function() {
contents.openDevTools();
})
});
After that I really have no idea what to do next... how to append window.opener and solve my issue
I found many similar issues on github. But all of them are merged to one issue #1865 and there is no clear answer so far.
I know solution exists. Brave developers fixed this. Brave browser also made with electron works correctly the same as general browser does. But I can't find answer while looking through its sources.
They even made tests to check whether window.opener works correctly. But I failed to install Brave from git repository. After 'npm-start' it says 'Error: %1 is not valid Win32 application'. I have Windows 10. (just another reason makes me switch to Linux. Hope to make it soon.)
So, my question is: how to send callback data from new window back to webview that created this window. Thanks in advance.
First, to handles new-window popups yourself, you have to disable allowpopups (i.e. remove allowpopups attribute from the webview)
This way the new-window event will be triggered but no popup will be opened until you explicitelly code it.
For the window.opener feature which is required for some social or sso login, electron does not have enough support of it. See issue 1865
The Brave team have implemented it in a restricted way in their fork of Electron. See issue 4175.
You may try to either:
Use the fork of electron from Brave (not sure it will fit your needs as they have made some major changes)
Make a PR to electron to integrate the support of window.opener from the fork.
Make your own polyfill for the methods you need by injecting a script that implements missing window.opener methods. You can use window.opener.send to communicate with renderer process)
Related
I am working on a Outlook VSTO add-in, in which I am using 'System.Windows.Forms.WebBrowser' to display the add-in functionality.
One functional requirement is to have oauth connection to cloud accounts (like OneDrive, Dropbox). When user clicks a button (e.g. 'Connect OneDrive'), we call 'window.open' in JavaScript code (ES6) with the oauth-url.
The issue which I am facing is, if user doesn't enter any credentials and close the window, and then again try to connect the cloud account (by clicking the 'Connect OneDrive'), I am getting an exception (Error: Could not complete the operation due to error 8150002e.).
I couldn't find any information about the error code '8150002e' on web.
This exception is not always present but comes around 50% of the times.
Any help would be appreciated in this.
what I have tried:
changing the windowName param every time window.open is called
having global var for window Object.
Using _blank parameter to open a new window every time.
After 5-6 times, the error comes up, after 5-6 times error goes away and auth window start coming up like before.
Opening a simple static HTML page in 'window.open' to verify if the issue has something to do with HTML page. The above issue is still present.
Resetting the System.Windows.Forms.WebBrowser programmatically.
Removing the cookies.
Instead of calling window.open from JavaScript code, we call VSTO code to open the browser window, the error is still there.
Edit: Created a minimal viable example at
https://github.com/vinay-x/SampleAddin
Code related to the issue:
SDXOLForm1.cs (navigates the browser to SamplePage.HTML hosted on localhost:8001)
SamplePage.HTML (contains a button, which calls window.open function).
The sample application has a windows form which contains a webBrowser control, which navigates to a simple HTML page which contains a button.
Had to deal with some IE11 stuff and I ran across this question. The solution I found to fix this issue is to set the window variable to null prior to calling window.open.
So for your example you have this:
function myFunction() {
window.open("https://www.w3schools.com", 'BackfliptOAuth', "width=800,height=800,center=true,useContentSize=true");
}
I modified it to this:
var win = null;
function myFunction() {
win = null;
win = window.open("https://www.w3schools.com", 'BackfliptOAuth', "width=800,height=800,center=true,useContentSize=true");
}
Goal:
I have a ReactJs web app called "ImageGallery" that's currently integrated in our business suite system(which is a huge system contains older technologies, mainly web forms). In our system, we need to add a button to launch a browser window to spin the app.
ImageGallery app rely on the current system to pass data to it to show, which means, when the ImageGallery app loads, it calls a function on the business suite system side like
window.parent.loadData()
Since the data is passed in by JavaScript, there is no ajax call.
Code:
function showImage() {
var imageGalleryWindow = window.open('/ImageGallery', '_blank', 'height=' + window.screen.availHeight + ',width=' + window.screen.availWidth + ',scrollbars=yes,menubar=no,resizable=yes,toolbar=no,location=no,status=no');
imageGalleryWindow.loadData= loadData;
}
loadData() function is available on the current JavaScript file and will be passed to the new window so it can be used by the ReactJS app.
Problem:
The app works perfectly with Chrome/Firefox/Edge, but not IE 11. IE 11 sometimes loads, sometimes not. After taking a closer look, I found there's a race condition.
Basically whenever the app failed, loadData() function has not been passed to the newly created window. Again, that's only for IE11, all other browsers seems fine.
What I've tried:
I tried the following:
1> Tried to put a wait function in ImageGallery app like this:
static resolveDocuments(){
if(typeof window.parent.loadData === 'function'){
// do the work to show image
} else {
setTimeout(this.resolveDocuments, 2000);
}
}
I am using redux thunk, I didn't get any luck in getting this to work.
2> put imageGalleryWindow.loadData= loadData; into window.onload(), bad idea but tried anyways
3> put imageGalleryWindow.loadData= loadData; into imageViewerWindow.document.addEventListener("DOMContentLoaded", function (event) {} still bad idea but tried anyways
4> created a new ImageGallery.html, use an iframe to host ImageGallery app. In section, include the JavaScript file from parent. No luck
5> created a webform page ImageGallery.aspx, use an iframe to host ImageGallery app. In section, include the JavaScript file from parent. No luck.
Thanks for taking time to finish reading this problem, and please let me know if you have an idea I can try.
Thanks so much!
Ray
The solution I end up having is html 5 local storage:
Before calling window.open() to populate the new window, create a entry in html 5 local storage with all the data I need to pass in.
When the image gallery app loads, check if local storage items exists, if so, pull the data. The data will always be there, since it sets before new window generated, and since both pages are from the same domain, they could both access the same local storage.
To avoid security issues, after image gallery app read data from the local storage, I keep a copy of the data within the app, then delete the local storage entry.
Hope this can be helpful to other people.
I'd like confirmation, should I be able to set onebeforeunload on an opened window in IE11.
Edit: some extra info
The page opening the window is located on a page such as
https://subsub.sub.company.tld/someapp/#/somepage
The window is opening an url like
https://subsub.sub.company.tld/someService/SomeIdentifier?timestamp=214124
As far as I am aware this should be fine?
var pop = window.open("url-on-same-domain");
pop.addEventListener("beforeunload", function() {
//something here
}):
Currently I can't add this listener due to Permissions (as far as I know this shouldn't be an issue on a same-domain environment). It does however have a unverified SSL connection e.g. defect certificate due to dev env.
Error: Permission denied
... some stack which goes back to pop.addEventListener
I also tried setting pop.onbeforeunload = function(){} but this is being ignored.
Anyone who can fill me in on this? We also have an issue on our test environment which is on localhost where attaching the evenListener won't work on IE and Edge.
My problem was caused by the popup opening a PDF File. In IE there is never access to Files opened in a window.
We worked around this by wrapping the file in an iFrame, which might sound nasty, but we needed to be able to display custom messages sometimes so it was OK for us as no other solution was workable.
I'd like my HTML5 app to draw onto two different screens. This (I think) means that I need have two distinct browser windows, one on each screen. Is this possible? It seems that I'd really have to load the same app into two windows, and somehow have the windows communicate to each other. I couldn't find an example of how to accomplish this. How can I make it happen?
For extra wonderfulness: There may not be a server involved, just an app served locally from the filesystem.
There's no need for a messy polling infrastructure using local storage or (shudder) cookies. Assuming the two pages are served from the same domain, it is trivial to implement cross-window communication so long as the second window is opened by the first.
In your main window: (click here for JSFiddle demo and here for the secondary page's code)
var win2;
function openSecondaryWindow() {
return win2 = window.open('win2.html','secondary','width=300,height=100');
}
$(function() {
if (!openSecondaryWindow()) // Try to open the window. This will likely be blocked by a popup blocker (unless you disable it).
$(document.body) // If it is blocked, clicking a link usually allows the popup to be spawned.
.prepend('Popup blocked. Click here to open the secondary window.')
.click(function() { openSecondaryWindow(); return false; });
$('#somelink').click(function() {
if (win2) win2.doSomething(); // Obviously, you'll need to define doSomething in the second page
else alert('The secondary window is not open.');
return false;
});
});
Once the secondary window has been opened by your main window, win2 will refer to the window object of the second page – in other words, the page's global scope. You'll have access to all of the functions and variables you've defined in the second page.
Thus, you can pass data through function calls.
win2.update({ key: 'value', ... });
From your secondary window, you can call back to functions in the main window through the opener property, which will refer to the window object of your main window. (This is demonstrated in the JSFiddle demo.)
Update: Intercom is a library that uses local storage to implement broadcast messaging between windows. Local storage fires an event (onstorage) when data changes, so polling is not actually necessary. Intercom allows all pages on a domain to communicate, regardless of how they were opened.
This is probably something that you could use websockets for, have each window send its info back to the app and then have them updated, but those are not supported across all browsers and in fact I believe are currently removed from most builds due to security issues with the spec.
For offline apps if they were on the same domain, which I'm assuming they would be locally, you could use local storage or even cookies and then have the apps poll for changes to the storage api?
I have been doing some experiments with offline local storage myself recently and I'm able to maintain state between windows with Chrome locally, in firefox this does not work, but I believe it is fixed in the FF4 RC
Edit 2:
Slapped together a quick and dirty proof of concept in two files:
Index 1:
<body>
<div id="result">x</div>
</body>
<script type="text/javascript">
var i = 0;
function update(){
setTimeout(function(){
localStorage.setItem("bar", i);
document.getElementById('result').innerHTML = "localstorage set to " + localStorage.getItem("bar");
i++;
console.log(i);
update();
}, 2000);
}
update();
</script>
Index 2:
<body>
<div id="result">s</div>
</body>
<script type="text/javascript">
function update(){
setTimeout(function(){
document.getElementById('result').innerHTML = localStorage.getItem("bar");
update();
}, 1000);
}
update();
</script>
Opening them both up in different windows in Chrome locally, the second one will display the state that is set by the loop in the first window. Still not working in FireFox 4 I found (a Mozilla Dev swore to me yesterday that offline local storage works now, oh well). You can probably getting it working in IE via http://www.modernizr.com/ but I have yet to test that.
I'm working on a project and run into an issue where I need to distinguish a chrome app window from normal ones. (Specifically I'm using the --app=URL from a bash script) Because of the way things are setup, I have to have run a js script on all windows, but only do something if they are an app window. It seems that the API listed here is what I need to distinguish one window from another, but all I've managed to get are errors saying that a function or object is undefined. So how am I suppose to get the window type from the API with something like window.type?
Additionally, if you know of some other way to tell the difference between chrome windows if they are an app window or not, then that would also work. I really just need to be able to do:
if (window is app) //I don't really care how it's done
{
doSomething();
}
More information:
Tried in both Chrome and Chromium (both fully updated)
Using Ubuntu 18.04
JavaScript is running in the app window and not an extension (not developing an extension)
Can you try the following. In your console
windowType=window.location.host
It should return if you are in app window it will return as "app". Using this you can write your logic
if (windowType === 'app' ) //I don't really care how it's done
{
doSomething();
}
Hope it helps.
Doing windowType.window.location.host returned not the type of window but rather the url provided with the --app=url flag in my bash script. This means that if you open a normal window and go to the same url as provided in the app window, both would return the same url. However, since the normal window would be the same content just a different window type, the JavaScript code that I need to run on the webpage is the same, thus I would want it to run on both windows. So this solution works for me, but for anyone else who is looking for a window specific identifier, and not just a url, I suppose that is still up in the air.
(Thanks Ragavan Rajan)