2

I have some problems observing event-propertys of html elements in a QWebpage with pyqt. The webpage I want to load and execute with pyqt:

<html>
<head>
<title>Test</title>

<script>
/*
 * object.watch polyfill
 *
 * 2012-04-03
 *
 * By Eli Grey, http://eligrey.com
 * Public Domain.
 * NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
 */

    // object.watch
    if (!Object.prototype.watch) {
        console.log("Watch defined...")
        Object.defineProperty(Object.prototype, "watch", {
            enumerable : false,
            configurable : true,
            writable : true,
            value : function(prop, handler) {
                var oldval = this[prop], newval = oldval, getter = function() {
                    return newval;
                }, setter = function(val) {
                    console.log("Set: " + prop);
                    oldval = newval;
                    return newval = handler.call(this, prop, oldval, val);
                };
                console.log("Before if statement...")
                if (true) { //here in original code is: "delete this[prop]", in every browser it is true but in a QWebpage not... why?
                    console.log("After if statement: " + prop
                        + " is observed...")
                    Object.defineProperty(this, prop, {
                        enumerable : true,
                        configurable : true,
                        get : getter,
                        set : function(val) {
                            console.log("Set: " + prop)
                            oldval = newval;
                            var newval = handler.call(this, prop, oldval, val)
                                    || val;
                            return newval;
                        }
                    });
                } else { 
                    console.log("Error: can't be observed")
                }
            }
        });
    }

    // object.unwatch
    if (!Object.prototype.unwatch) {
        Object.defineProperty(Object.prototype, "unwatch", {
            enumerable : false,
            configurable : true,
            writable : false,
            value : function(prop) {
                var val = this[prop];
                delete this[prop]; // remove accessors
                this[prop] = val;
            }
        });
    }
</script>

</head>

<body>

<div id="target" href="#">LINK WITH HANDLER</div>

<script>
    link = document.getElementById("target");
    //
    //link.onclick = myClick2
    link.watch("onclick", function(prop, oldVal, newVal) {
        console.log("watch > onclick has changed!!");
        return newVal;
    });
    link.onclick = myClick;

    function myClick(e) {
        console.log("Dear sir, here is a click");
    }
</script>

When I'm putting that in a html-page and execute it on a browser(Chrome, Firefox, Epiphany, ...) I get the desired behaviour and could read the console log message.

But when I'm trying to load the page with QWebpage in python with pyqt4(I also tried pyqt5) then the page doesn't behave in the same way. After some tests, I noticed the problem is in the if-statement. In every browser it is true, in contrast using QWebpage. When I'm deleting it, then I get the error message from the headline.

Can someone please tell me why this is so and what is the different in a webpage rendered with QWebpage and a normal browser using the same JavaScript engine? I hope I give enough information. Thank you for your help!

Here is the code to fetch the html page:

import sys
import logging
from PyQt4.QtWebKit import QWebPage
from time import time, sleep
from PyQt4.QtCore import QUrl
from PyQt4.Qt import QApplication


class Browser(QWebPage):

def __init__(self, app, proxy = "", port = 0):
    QWebPage.__init__(self)
    self.loadFinished.connect(self._loadFinished)
    self.app = app

def get(self, requested_url, timeout=20):
    logging.debug("Browser started on {}...".format(requested_url))
    self._loading_complete = False
    self.mainFrame().load(QUrl(requested_url))
    t = 0
    while(not self._loading_complete and t < timeout ): # Waiting for finish processing
        self._wait(0.1) 
        t += 0.1

    if not self._loading_complete:
        logging.debug("Timeout Occurs")

    self._analyzing_finished = True
    return self.mainFrame().toHtml()

def _loadFinished(self, result):
        if result:
            self._loading_complete = True

def _wait(self, waittime=1):
    """Wait for delay time
    """
    deadline = time() + waittime
    while time() < deadline:
        sleep(0)
        self.app.processEvents()

def javaScriptConsoleMessage(self, message, lineNumber, sourceID):
    logging.debug("Console: " + message + " at: " + str(lineNumber))       
if __name__ == '__main__':

logging.basicConfig(level=logging.DEBUG,
                format='%(asctime)s: %(levelname)s - %(message)s',
                datefmt='%d.%m.%Y %H:%M:%S',
                #filename='Crawler.log',
                #filemode='w'
                )

app = QApplication(sys.argv)
browser = Browser(app)
browser.get("http://localhost/") #Console output is important
Pumba
  • 181
  • 5
  • Which `if` statement are you referrring to? (And note that WebKit uses its own [Javascript engine](https://www.webkit.org/projects/javascript/)). – ekhumoro Nov 24 '14 at 18:38
  • The if in the JavaScript-Code: [...]if( delete this[prop])[...] – Pumba Nov 25 '14 at 07:09
  • The `delete this[prop]` returns `false`, but I can't see how you're getting that error. Please post a complete, self-contained example that reproduces the problem. – ekhumoro Nov 26 '14 at 21:23
  • Thank you very much for your reply! Here are now the html page, that should be loaded with qwebpage and and the python code to do that. I have edited the original question text. – Pumba Nov 28 '14 at 21:26
  • The [source of the webkit js engine](https://qt.gitorious.org/qt/webkit/source/26a95e634c52382518001482c89948ef3eea181e:src/3rdparty/webkit/JavaScriptCore/runtime/JSObject.cpp#L559-573) (around line 570), shows that the error is thrown when trying to re-define an existing property descriptor that is not configurable. This means that, in webkit, `onclick` is *not* a configurable property, whereas it seems that it *is* in other browsers. I don't know why this is. It could be a bug, or it could be that webkit is complying with some standard that the other browsers aren't... – ekhumoro Nov 29 '14 at 00:11
  • Ok thank you very much! Have you an other idea how I can observe the change of .onclick and other event-propertys with pyqt? – Pumba Nov 30 '14 at 14:24

1 Answers1

0

I think I may have discovered why the return value delete is different. The javascript spec allows browsers to implement delete in whatever way they like, and as a result of this, it seems that you cannot rely on the correctness of its return value.

To give an example, here's what happens in Firefox (using Scatchpad):

// page = about:blank

document.body.onclick;
/*
null
*/
document.body.onclick = function(){alert('boo!')};
/*
function (){alert('boo!')}
*/
delete document.body.onclick;
/*
true
*/
document.body.onclick
/*
function (){alert('boo!')}
*/ 

Liar! It is clearly not possible to delete the onclick property, but Firefox returns true anyway. This leads me to the tentative conclusion that the webkit javascript engine may actually be doing the right thing here, and it is the other browsers that have the problems.

For more information about the behviour of delete, see the excellent article Understanding delete, and in particular, the section delete and host objects.

ekhumoro
  • 115,249
  • 20
  • 229
  • 336
  • Thank you for the two artickels... can you give me a hint how I can observe with pyqt when an event-attribute is attached to a html-element? – Pumba Dec 01 '14 at 14:22
  • @Pumba88. Have you seen [this question](http://stackoverflow.com/q/1029241/984421)? The accepted answer is the same as the one in your question, but there are other solutions there that you could try out. Alternatively, Qt-5.4 will be available very soon, which has the chromium-based [QWebEngine](http://qt-project.org/wiki/QtWebEngine) classes. The current [PyQt5 snapshot](http://www.riverbankcomputing.com/software/pyqt/download5) already has support for QWebEngine. – ekhumoro Dec 01 '14 at 17:38
  • Yes I know that question and have also tried the proposed solutions...but nothing works. Ok maybe I will wait or try the snapshot. Thank you very much!! – Pumba Dec 03 '14 at 07:39