node-phantom icon indicating copy to clipboard operation
node-phantom copied to clipboard

second call to page.injectJs fail

Open stumpyfr opened this issue 12 years ago • 8 comments

Hi,

I have this code:

phantom.create(function (err, pha) {
        ph = pha;
        ph.createPage(function (err, p) {
                page = p;
                page.onConsoleMessage = function(msg) { console.log(msg); };
                page.open(signInUrl, function (err, status) {
                console.log(status);
                page.includeJs(jquery, function (err) {
                        page.evaluate(function (params) {
                                $('input[type=email]').val(params.login);
                                $('input[type=password]').val(params.password);
                                $('input[type=submit]').click();
                            }, function (err, out) {
                                setTimeout(verifCode, 5000);
                            }, {login: login, password: password});
                          });
                    });
            });
    });

var verifCode = function() {
    page.includeJs(jquery, function (err) {
...

The first page contains a form and when I submit it, phantom navigates to a new page so I need to inject jquery a second time. But node-phantom crash in node-phantom.js:178 during the call of page.includeJs(

case 'pageEvaluatedAsync':
    cmds[cmdId].cb(null);
    delete cmds[cmdId];

I patched the code and it's working but not sure to really understand what happened and if my patch will not have side effect.

case 'pageEvaluatedAsync':
    if (cmds[cmdId] != null) {
        cmds[cmdId].cb(null);
        delete cmds[cmdId];
    }

stumpyfr avatar Oct 17 '13 10:10 stumpyfr

Your fix will simply ignore what is an error-condition and allow you to continue. What seems to happen is that the cmdId is unknown - meaning it is getting a response from phantomjs to a request it is unaware of having sent. This should not happen.

You talk of multiple pages, perhaps something is getting mixed up there. Could you provide a full example so I can try to replicate the issue?

alexscheelmeyer avatar Oct 17 '13 10:10 alexscheelmeyer

Will be difficult to send a complete example of that case because I am working on an intranet with specific account but the code on the issue is a good summary. The only thing I can imagine is when I click on the submit button, the intranet navigates to many different pages before coming on what I call the "second page", maybe phantom is in a strange state after that type of complexe navigation?

stumpyfr avatar Oct 17 '13 11:10 stumpyfr

You could try and uncomment line 73 and 82 of node-phantom.js to let it output the requests and responses.

the cmdid's are in second index both for request and response. Then it might be possible to see why a response for an unknown request ends up happening.

alexscheelmeyer avatar Oct 17 '13 11:10 alexscheelmeyer

[ 1, 2, 'pageJsIncluded' ]
requesting:1,3,pageEvaluate,function (params) {
                $('input[type=email]').val(params.login);
                $('input[type=password]').val(params.password);
                $('input[type=submit]').click();
                },[object Object]
[ 1, 3, 'pageEvaluated', 'null' ]

then

[ 1, 2, 'pageJsIncluded' ]
[ 1, 4, 'pageJsIncluded' ]
requesting:1,5,pageEvaluate,function (params) {
            $('input[type=text]').val(params.code);
                $('input[type=submit]').click();
        },[object Object]
[ 1, 5, 'pageEvaluated', 'null' ]

it's looks like PageJsIncluded is called twice at the second Injection.

stumpyfr avatar Oct 17 '13 11:10 stumpyfr

So between those two snippets you have a "requesting 1,4,..." ? And there is two responses to that, one with cmdid 2 and one with cmdid 4?

It would be interesting to log in the phantomjs context also then. You can uncomment line 9 in bridge.js to get which responses are requested by the code.

alexscheelmeyer avatar Oct 17 '13 12:10 alexscheelmeyer

[ 1, 2, 'pageJsIncluded' ]
requesting:1,3,pageEvaluate,function (params) {
                $('input[type=email]').val(params.login);
                $('input[type=password]').val(params.password);
                $('input[type=submit]').click();
                },[object Object]
phantom stdout: responding:1,3,pageEvaluated,null

[ 1, 3, 'pageEvaluated', 'null' ]
requesting:1,4,pageIncludeJs,http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js
phantom stdout: responding:1,2,pageJsIncluded

phantom stdout: responding:1,4,pageJsIncluded

[ 1, 2, 'pageJsIncluded' ]
[ 1, 4, 'pageJsIncluded' ]

stumpyfr avatar Oct 17 '13 12:10 stumpyfr

I am able to reproduce this, seems to be weird behavior by phantomjs. It will call the callback for the first invocation of page.includeJs again on the second invocation before calling the new callback. It does not need to be two different pages or anything: if you call includeJs twice with same parameter you will get 3 callbacks, 1 for the first invocation and 2 for the second - where one of them is actually re-calling the first callback.

in bridge.js update the code starting at line 85 with this instead and it should fix it:

        case 'pageIncludeJs':
            var alreadyGotCallback=false;
            page.includeJs(request[3], function(){
                if(alreadyGotCallback)return;
                respond([id,cmdId,'pageJsIncluded']);
                alreadyGotCallback=true;
            });
            break;

I will include this work-around in next commit.

If you like you can also report it as a bug to the phantomjs developers.

alexscheelmeyer avatar Oct 17 '13 12:10 alexscheelmeyer

Nice work :). I will patch my local version with your fix.

stumpyfr avatar Oct 17 '13 13:10 stumpyfr