Search code examples
javascriptmodulenw.js

NW.js not clearing cache correctly


Environment

Problem

New window is opened from parent window. Webchimera module is loaded but it crashes because its window object points to parent window. That is because NW.js caches its modules.

PARENT

<div id="chimera_container"></div>

...
var gui; // global variable for parent object
var wjs; // global variable for parent object
....
gui = require('nw.gui');
wjs = wjs = require('wcjs-player');
....
video_emit = gui.Window.open('video_emit.html',{
    "window" : {
        "focus": true,
        "toolbar": true,
        "frame": true,
        "width" : 640,
        "height" : 640
    },
    "dependencies": {
        "require-new": "^1.1.0",
        "wcjs-player": "^0.5.6"
    },  
});

video_emit window

<div id="vid"></div>

....
var wjs; // global variable for new window object
wjs = require('wcjs-player');  
video_container = new wjs("#vid"); <---- CRASHES **** !!!

In the node_modules\wcjs-player\index.js line 82 I added

console.log(window.document);

PARENT CONSOLE OUTPUT

#document
...
<div id="chimera_container"></div>

video_emit CONSOLE OUTPUT

#document
...
<div id="chimera_container"></div>  

Solutions tried

Manually clearing cache

Calling function clearRequiredModules before requiring 'wcjs-player' module on video_emit window.

var clearRequiredModules = function(){
    // clear wcjs-player from require cache to force new load
    var clearModules = [
        "wcjs-player",
        "jquery" // https://github.com/jaruba/wcjs-player/issues/38
    ];
    for (var i in clearModules) {
        for (var module in global.require.cache) {
            if ( global.require.cache.hasOwnProperty(module) 
                 && module.indexOf(clearModules[i]) > -1
                )
                delete global.require.cache[module];
        }
    }
};

Not working. Same problem as if I hadn't cleared at all. Still points to parent window object.

Using require-new module

There's no science to this code, I load module and use it in video_emit window.

var req_new = require('require-new');
wjs = req_new('wcjs-player');

Still crashes.


I know that adding ** new-instance: true** to the gui.Window.open of the parent would fix this, but I need parent and new window to communicate via the global variable usable only if they are on the same render.

------------------------ Solution -----------------------------------------

Clearing cache on its own didn't work, in order for it to be valid the following had to be done from the parent window.

video_emit = gui.Window.open('video_emit.html',{
    ...
});
video_emit .on('document-start', function() {
    video_emit .reload(3);
});

And in the child window:

clearRequiredModules(); //Same code as protrayed above
wjs = require('wcjs-player');  
video_container = new wjs("#vid");

PROBLEM

BEWARE, doing this renders the global variable (the one used to share information between windows, useless. So it's the same as doing this in parent window (and not clearing cache in child window):

video_emit = gui.Window.open('video_emit.html',{
        "window" : {
            "focus": true,
            "toolbar": true,
            "frame": true,
            "width" : 640,
            "height" : 640
        },
        "new-instance" : true // <---- That right there
    });

A possible solution to this problem is to use localStorage instead, to communicate between windows.


Solution

  • wcjs-player is heavily reliant on being able to access window.document. NW.js v0.12.3 has a bug that leaks the parent's window object to the child window.

    I've seen this create numerous issues including pushing all errors and logs to the main window, and it always breaks all modules that are dependent on window.

    The fix for this is to do a node.js level reload of the child window after creating it.

    Proof of Concept

    Discussion

    Example Usage:

    var new_win = require('nw.gui').Window.open('index.html');
    new_win.on('document-start', function() {
        new_win.reload(3);
    });
    

    Notes:

    • It should also be mentioned that this is a NW.js specific issue, Electron does not have this issue with new windows.

    • As this is a high level page reload, the global object will also be cleared, an alternative solution to passing information to this new window is using localStorage (that is persistent), or for more complex needs websockets.