phridge icon indicating copy to clipboard operation
phridge copied to clipboard

Adding support for calling back from phantom to node

Open reflectiz opened this issue 9 years ago • 5 comments

I want to be able to call from the phantom code to the node module. The code added a function calls "nodeCallback" at the phantom side and property calls "onCallback" on the "page" object.

reflectiz avatar Aug 21 '16 14:08 reflectiz

Coverage Status

Coverage decreased (-1.7%) to 98.328% when pulling 397b6f2687469dacbc4d47cca52972411c469848 on ad-venture:master into 0aa95f81dffcaab48357ebf2aab42ce64ce69247 on peerigon:master.

coveralls avatar Aug 21 '16 16:08 coveralls

Coverage Status

Coverage decreased (-1.7%) to 98.328% when pulling ff308e25520c739a7120b95aceeba5c35d084e34 on ad-venture:master into 0aa95f81dffcaab48357ebf2aab42ce64ce69247 on peerigon:master.

coveralls avatar Aug 26 '16 10:08 coveralls

Thank you for your pull-request.

Can you describe a use-case where this nodeCallback is necessary and cannot be solved in other ways?

jhnns avatar Aug 29 '16 16:08 jhnns

Hi jhnns

I've added this functionally because I needed to initiate an action base options for events triggered at the phantomjs client side code. I'm using MutationObserver to monitor changes on the page, and when change occurs, the nodeJS server need to react. I haven't found any other option in phridge current code.

Thanks Ysrael

reflectiz avatar Aug 30 '16 12:08 reflectiz

You're right. There is currently no way to achieve this.

However, there are some problems with your implementation. What happens if I have multiple instances of Page and I would like to install event listeners individually. We somehow need to have a record so that we now where "to call back".

For instance, we could have a third argument emit:

phantom.run(function (resolve, reject, emit) {
    setInterval(() => {
        emit("hi");
    }, 100);
    resolve();
});

somePage.run(function (resolve, reject, emit) {
    setInterval(() => {
        emit("hi");
    }, 100);
});

phantom.on("hi", function () { ... });
somePage.on("hi", function () { ... });

This way...

  • we don't have a "magical" nodeCallback variable in the upper scope
  • we can collate the callbacks to their respective instance in node

However, there might be another problem: When we're inside PhantomJS, we are not inside the actual page context. When using the evaluate function provided by PhantomJS, we're not allowed to access the scope either. Hence, this code will not work:

page.run(function (resolve, reject, emit) {
    this.evaluate(function () {
        // emit is not defined because we're in the actual page context now
        emit("hi");
    });
    resolve();
});

We would need to use the onCallback mechanism as described here:

page.run(function (resolve, reject, emit) {
    this.onCallback(function (message) {
        emit(message);
    });
    this.evaluate(function () {
        window.callPhantom('hi');
    });
    resolve();
});

This is very ugly – and again, we have the same problem that we need to collate callbacks from different sources...

I totally see the use-case and I wish, phridge would provide an easy API for this. But a proper implementation takes a lot of effort...

For instance, it would be cool to have a evaluate method on our page instance that mitigates all the cruft around it. This is probably what you would like to use:

page.evaluate(function (resolve, reject, emit) {
    var observer = new MutationObserver(function(mutations) {
      mutations.forEach(function(mutation) {
        emit("mutation", mutation.type);
      });    
    });

    observer.observe(document.body, {
      attributes: true,
      childList: true,
      characterData: true
    });
});

page.on("mutation", function (mutationType) {

});

jhnns avatar Sep 06 '16 09:09 jhnns