morzel.net

.net, js, html, arduino, java... no rants or clickbaits.

How to close pop-ups upon main window closure or logout?

Imagine you have to provide support for some really old web application. The app has one main window and pop-up windows that show some sensitive information (for example payroll list). Client wants to ensure that all pop-ups are closed when user leaves main window or clicks “logout” button in this window...

So... how to close all the windows opened with window.open?

On the web this question comes up very often. Unfortunately, most common answer is really naive. Proposed solution is based on keeping references to opened pop-ups and subsequent invocation of close method: 

var popups = []; 
 
function openPopup() {
    var wnd = window.open('Home/Popup', 'popup' + popups.length, 'height=300,width=300');
     
    popups.push(wnd);
}
 
function closePopups() {
    for (var i = 0; i < popups.length; i++) {
        popups[i].close();
    }
 
    popups = [];
}

In practice this doesn’t work because the array of references is cleared at full page reload (for example after clicking on a link or upon postback)...

Other suggested solution is to give the pop-up a unique name (using the second parameter of the open method) and later acquisition of a reference to the window:

var wnd = window.open('', 'popup0');
wnd.close();

This is based on the fact, that window.open method works in two modes:

  1. If a window with a given name doesn’t exist, it is created.
  2. If a window with a given name does exist, it will not be recreated, instead a reference to that window will be returned (if non empty URL is passed to the open method pop-up will be reloaded).

The problem lies at point no. 1. If pop-up window with given name wasn’t previously opened, the call to open and close methods will cause the pop-up to be briefly visible. It sucks…

But maybe a reference to pop-up can be retained between page reloads?

If there is no need to support older browsers (unlikely for the old application) we can try to put reference to the pop-up window into localStorage. However, this will not work:

var popup = window.open('http://morzel.net', 'test');
localStorage.setItem('key', JSON.stringify(popup)); 
 
TypeError: Converting circular structure to JSON

Old tricks for keeping page state between reloads that are based on cookies or window.name will not work too.

 

So… what to do?

Even if you can’t afford to have a major change such as introducing frames, don’t give up :)

Pop-up windows have opener property that points to parent window (that is the window in which the call to window.open was placed). Pop-ups can therefore periodically check whether the main window still remains open. Additionally, pop-ups can also access variables from parent window. This can be used to enforce pop-ups closure when main window is closed or when user clicks on “logout” button in parent window. When user is logged-in (and only then!), a marker variable (i.e. loggedIn) should be set in main window.

Here is the JS code that should be placed on a page displayed in a pup-up:

window.setInterval(function () {
    try {
        if (!window.opener || window.opener.closed === true || window.opener.loggedIn !== true) {
            window.close();
        }
    } catch (ex) {
        window.close(); // FF may throw security exception when you try to access loggedIn (for external site)
    }
}, 1000);

Checking variable from the opener window has another advantage. If user moves away from our application in main window (for example by clicking back button or a link to an external website), then the pop-up window will detect the lack of monitored variable in window.opener and close automatically.

Well, it's not the kind of code you enjoy to write but it achieves the desired result despite the painful gaps in the browsers API. If only they provide us with window.exists('name') method...

Ref modifier for reference types and a bit of SOS

Take a look at the following code and think what value will be displayed on the console (note that string is a reference type)?

using System;
               
class Program
{
    static void Test(string y)
    {
        y = "bbb";
    }

    static void Main()
    {
        string x = "aaa";
        Test(x);
        Console.WriteLine(x);
    }
}

The correct answer (aaa) is not all that obvious. You will see the words aaa, because without a ref modifier, a program written in C# provides a copy of the parameter value (for value types) or a copy of a reference (for reference types).

When parameter y in method Test receives a new text value, CLR does not modify the array of chars. Instead, a new string is created and a reference to it is assigned to variable y (more info here). Variable y contained in method Test is, however, just a copy of a reference hold under x variable from method named Main.

To actually change the text hidden under x variable, use the ref modifier (you have to set it both in the method declaration and its invocation - C# enforces such behavior for clarity):

using System;
               
class Program
{
    static void Test(ref string y)
    {
        y = "bbb";
    }

    static void Main()
    {
        string x = "aaa";
        Test(ref x);
        Console.WriteLine(x);
    }
}

After this change, console will show bbb text.

 

SOS

Way in which parameters are passed to a method can be examined by using tool called SOS (Son of Strike). We will use CLRStack -a command, which displays information about parameters and local variables on managed code stack (if you don't know how to use SOS look here and here, if you wonder where the name "Son of Strike" came from, click here)...

Below are the results of CLRStack -a command executed at the time of entry to the Test method.

For code without ref modifier:

!CLRStack -a
OS Thread Id: 0x176c (5996)
Child SP IP       Call Site
0031f114 00390104 Program.Test(System.String)
    PARAMETERS:
        y (0x0031f114) = 0x025cb948

0031f158 003900af Program.Main()
    LOCALS:
        0x0031f158 = 0x025cb948

0031f3c0 656721bb [GCFrame: 0031f3c0]

For code with ref modifier:

!CLRStack -a
OS Thread Id: 0x934 (2356)
Child SP IP       Call Site
001dee34 002f00f4 Program.Test(System.String ByRef)
    PARAMETERS:
        y (0x001dee34) = 0x001dee78

001dee78 002f00aa Program.Main()
    LOCALS:
        0x001dee78 = 0x027fb948

001df0ec 656721bb [GCFrame: 001df0ec]

An important difference that is exhibited by these results is the value of y parameter. In the case of code without ref modifier, it is the address of aaa string (0x025cb948). For the code with ref modifier, the value of y parameter is the address of x variable (0x001dee78) from Main method (that variable points to aaa string).

View State for TextBox and other controls that implement IPostBackDataHandler

While reading the official training kit for 70-515 exam I came across this text: "With view state, data is stored within controls on a page. For example, if a user types an address into a TextBox and view state is enabled, the address will remain in the TextBox between requests.". If such statements can be found in recommended study guide, it should not come as a surprise, that confusion about the way ASP.NET Web Forms tries to cope with inherent statelessness of HTTP is so common… ;)

TextBox control from ASPX page:

<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>

is rendered on HTML page as an input tag:

<input name="TextBox1" type="text" id="TextBox1" />

If so, then the preservation of TextBox value between requests does not require any use of __VIEWSTATE hidden field. To illustrate this, create a simple page that contains TextBox and Button controls:

...
 
<body>
    <form id="form1" runat="server">
        <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
        <asp:Button ID="Button1" runat="server" Text="Button" onclick="Button1_Click" /
    </form>
</body>
</html>

and add a handler for button’s Click event, which only task is to extend the text in TextBox1 control:

protected void Button1_Click(object sender, EventArgs e)
{
    TextBox1.Text += "X";
}

Then, run the page and activate a tool for monitoring communication between browser and server. We are interested in testing form data that is sent to the server at postback... If you are using IE, I can recommend you a debugging proxy called Fiddler. Under Firefox, use Firebug. You can also use built-in ASP.NET Trace feature – to do so, add Trace = "true" to @Page directive. I performed my tests using development tools provided with Chrome browser ("Network" tab).

The following screenshot shows what form data (HTTP POST request) was sent after first button press:

Dane formularza przy pierwszym postbacku

And here is data from second postback:

Dane formularza przy drugim postbacku

If you compare data from first and second requests, you will see that a change in the value of TextBox1.Text does not affect the value of __VIEWSTATE field. Expanding the field would be a waste of network resources if text is being sent to server in a separate field called TextBox1.

System.Web.UI.WebControls.TextBox class is one of several classes that implement IPostBackDataHandler interface. This interface requires LoadPostData method. After page initialization is completed (but before the Load event) loading of View State data is invoked (LoadViewState) and then (if the control implements IPostBackDataHandler), loading of form data is invoked (LoadPostData). Text property of a TextBox control can therefore be set even if View State mechanism is disabled (via EnableViewState = "false" setting).

So... Can we completely disable View State mechanism for TextBox controls and the like?

No. For example, View State is useful when TextChanged event is handled (for comparison between current and previous value). It can also be used when the value that is being set is other than the one related to control’s value (e.g. ForeColor).

Detection of loading an iframe created in Ext JS

Suppose that you need to execute a block of code when iframe's content is loaded. In case when iframe is created statically in HTML markup, the solution is really simple. All you have to do is to connect some JavaScript function with load event:

<iframe src="http://wikipedia.org" width="600" height="400" onload="someFunction();" ></iframe>

Note: The load event (onload) is invoked when the entire contents of the document is loaded (including its external elements such as images). If you need to act earlier, that is at a time when the DOM is ready, use the other methods...

But what if the iframe is created with Ext JS code?

A simple way to set it up it is to use Ext.BoxComponent with correct autoEl property value. This gives you the ability to easily use iframe in Ext JS layout (e. g. as a child item of Ext.Window), without extending document tree with redundant elements. 

var iframeContainer = new Ext.BoxComponent({
    autoEl: {
        tag: 'iframe',
        frameborder: '0',
        src: 'http://wikipedia.org'
    },
    listeners: {
        afterrender: function () {
            console.log('rendered');

            this.getEl().on('load', function () {
                console.log('loaded');
            });
        }
    }
});

In the above code (Ext JS 3.2.1), really important thing is the time when iframe's load event is hooked. You can do it only after the control (BoxComponent) is rendered. If you try this earlier, then getEl() will return undefined and the code will fail. Prior to rendering, an Ext JS control is just a JavaScript objects, for which no document tree elements exist. Below are two screenshots showing the HTML snippets created by Ext.Window in which the only item was BoxComponent creating the iframe tag...

beforerender:

DOM beforerender

afterrender:

DOM afterrender

You can clearly see that premature connecting to load event is futile, becasue you simply cannot listen to events on something that does not exist.

Those screenshots come from Elements window of Chrome Developer Tools. A quick way to show that tool (of course in Google's browser) is to press F12 or Ctrl+Shift+I. Nice feature of CDT is the ability to show events being listened on a DOM element. To see the list you have to select DOM element and, on the right side menu, choose "Event Listeners" tab. On the screenshot below, you can see that iframe's load event is indeed used:

CDT Event Listeners

BadImageFormatException, x86 i x64

Have you ever seen BadImageFormatException or “An attempt was made to load a program with an incorrect format” message?

If so, maybe the program that you tried to run hasn’t been compiled with /platform:x86 option. You are probably wondering why, while writing in C# you should think about what kind of machine (x86 or x64) your program will execute on. Well… in most cases you don’t have to care about it. If your application doesn’t contain any unsafe code and doesn’t import any native modules that are destined for specific CPU architecture, then you can forget about 32/64 bit dilemma. After all C# code gets compiled into intermediate language (CIL) and upon execution framework’s just-in-time compiler will emit native instructions suitable for current platform. Great, but there’s a catch…

Imagine a situation when you import 32 bit DLL in your application. If you run that software on 32 bit operating system everything works as expected. Unfortunately on x64 machine you can get BadImageFormatException while trying to load DLL. Why? Your assembly may be compiled with /platform:anycpu setting. It means that on 32 bit systems your code will be executed with 32 bit CLR version. On 64 bit systems a 64 bit process will be used – and it will cause problems with loading 32 bit DLL. If you provide /platform:x86 setting, then 32 bit version of the CLR will be choosen even on the x64 versions of Windows (utilizing WoW64: Windows 32-bit on Windows 64-bit).

In Visual Studio 2010 you can set targeted platform in project “Properties” window (on “Build” tab). To get there, right click on project file in Solution Explorer and choose “Properties” or use main menu “Project | <you project name> Properties…”.

Setting targeted platform in Visual Studio (click to enlarge)

Microsoft has created useful tool called CorFlags which can be used to show or set the targeted platform of an managed assembly. You can access this program by using Visual Studio Command Prompt or find it directly on your disk (I have it under C:\Program Files\Microsoft.NET\SDK\v2.0\Bin\CorFlags.exe).

Here are some examples of what you may see after checking an EXE file created with various /platform option values (use CorFlags file.name command to check a file):

anycpu x86 x64
Version   : v4.0.30319
CLR Header: 2.5
PE        : PE32
CorFlags  : 1
ILONLY    : 1
32BIT     : 0
Signed    : 0
Version   : v4.0.30319
CLR Header: 2.5
PE        : PE32
CorFlags  : 3
ILONLY    : 1
32BIT     : 1
Signed    : 0
Version   : v4.0.30319
CLR Header: 2.5
PE        : PE32+
CorFlags  : 1
ILONLY    : 1
32BIT     : 0
Signed    : 0

For the scope of this post 2 rows of the CorFlags output are important: PE and 32BIT.

  • PE: PE32 means that file can be executed on both x86 and x64
  • PE: PE32+ means that it can only be run on 64 bit version of OS
  • 32BIT: 1 means that program must be executed on x86 environment.

Understanding 32BIT: 1 meaning is really important if you want to avoid problems with importing 32 bit DLL on 64 bit systems. If 32BIT flag is set and you run PE32 file on x64, then your app will be executed in 32 bit process (under WoW), hence will have a chance to properly import 32 bit DLL. Without this setting 64 bit environment will be chosen which will cause problems.

The good news is that you can easily modify 32BIT flag with CorFlags tool, to do this execute that command:

CorFlags file.exe /32BIT+

And to remove it, try this

CorFlags file.exe /32BIT-

So even if you cannot recompile problematic assembly with proper /platform option you still have a chance of using legacy 32 bit DLLs on 64 bit Windows :)