Bug 29489 - [Qt] WebKit bug resizing a plugin with JavaScript
Summary: [Qt] WebKit bug resizing a plugin with JavaScript
Status: RESOLVED INVALID
Alias: None
Product: WebKit
Classification: Unclassified
Component: Layout and Rendering (show other bugs)
Version: 528+ (Nightly build)
Hardware: Other OS X 10.5
: P2 Normal
Assignee: Nobody
URL:
Keywords: Qt, QtTriaged
Depends on:
Blocks:
 
Reported: 2009-09-18 08:58 PDT by Tor Arne Vestbø
Modified: 2014-02-03 03:15 PST (History)
3 users (show)

See Also:


Attachments
Testcase (10.09 KB, text/x-c++src)
2010-03-12 07:29 PST, Kent Hansen
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Tor Arne Vestbø 2009-09-18 08:58:38 PDT
This bug report originated from issue QTBUG-3503
http://bugreports.qt.nokia.com/browse/QTBUG-3503

--- Description ---

The attached test cases will show two WebKit bugs. Tests have been
performed with both Qt 4.5.0-beta1 and the latest available snapshot
(20090130).


Test 1: tst_QWebPage::resizeJS()

This will add both a <img> and a <object> tag to the web page, the
latter used to embed a QWidget.

The test shows that WebKit won't allow changing the width or height
attributes of the <object> tag using JavaScript, while it will allow
to change other attributes (or the image size).


Test 2: tst_QWebPage::resizeWidgetNoRepaint() and
tst_QWebPage::resizeWidgetWithRepaint()

The tests will embed a QWidget as in the previous test but resize the 
QWidget.

Both will actually run the same code, except the latter adds a call to
QWebView::repaint().

The tests show that the <object> width and height are updated
correctly right after the widget is resized but that a paint event
will cause the widget size to be reset back to the initial size.

Looking at RenderWidget::paint() in webcore\rendering\renderwidget.cpp
I can see that m_widget->move() is being executed. This will,
unfortunately, use the size stored in WebCore::Widget::IntRect, which
is no longer the correct size.
Comment 1 Tor Arne Vestbø 2009-09-18 08:59:08 PDT
/*
    Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to
    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
    Boston, MA 02110-1301, USA.
*/


#include <QtTest/QtTest>

#include <qwebpage.h>
#include <qwidget.h>
#include <qwebview.h>
#include <qwebframe.h>
#include <QDebug>
#include <QPushButton>

// Will try to wait for the condition while allowing event processing
#define QTRY_COMPARE(__expr, __expected) \
    do { \
        const int __step = 50; \
        const int __timeout = 5000; \
        if ((__expr) != (__expected)) { \
            QTest::qWait(0); \
        } \
        for (int __i = 0; __i < __timeout && ((__expr) != (__expected)); __i+=__step) { \
            QTest::qWait(__step); \
        } \
        QCOMPARE(__expr, __expected); \
    } while(0)

//TESTED_CLASS=
//TESTED_FILES=


class tst_QWebPage : public QObject
{
    Q_OBJECT

public:
    tst_QWebPage();
    virtual ~tst_QWebPage();

public slots:
    void init();
    void cleanup();

private slots:
    void resizeJS();
    void resizeWidgetNoRepaint();
    void resizeWidgetWithRepaint();

private:
    void resizeWidget(bool resize);

    QWebView* m_view;
    QWebPage* m_page;
};

tst_QWebPage::tst_QWebPage()
{
}

tst_QWebPage::~tst_QWebPage()
{
}

void tst_QWebPage::init()
{
    m_view = new QWebView();
    m_page = m_view->page();
}

void tst_QWebPage::cleanup()
{
    delete m_view;
}

class PluginPage : public QWebPage
{
public:
    PluginPage(QObject *parent = 0)
        : QWebPage(parent) {}

    ~PluginPage() {

    }

    struct CallInfo
    {
        CallInfo(const QString &c, const QUrl &u,
                 const QStringList &pn, const QStringList &pv,
                 QObject *r)
            : classid(c), url(u), paramNames(pn),
              paramValues(pv), returnValue(r)
            {}
        QString classid;
        QUrl url;
        QStringList paramNames;
        QStringList paramValues;
        QObject *returnValue;
    };

    QList<CallInfo> calls;

protected:
    virtual QObject *createPlugin(const QString &classid, const QUrl &url,
                                  const QStringList &paramNames,
                                  const QStringList &paramValues)
    {
        QObject *result = 0;
        if (classid == "pushbutton")
            result = new QPushButton();
        if (result) {
            result->setObjectName(classid);
        }
        calls.append(CallInfo(classid, url, paramNames, paramValues, result));
        return result;
    }
};

void tst_QWebPage::resizeJS()
{
    QSignalSpy loadSpy(m_view, SIGNAL(loadFinished(bool)));

    PluginPage* newPage = new PluginPage(m_view);
    m_view->setPage(newPage);

    m_view->settings()->setAttribute(QWebSettings::PluginsEnabled, true);
    m_view->settings()->setAttribute(QWebSettings::JavascriptEnabled, true);

    // add an image and a widget to the page using <img> and <object>.
    m_view->setHtml(QString("<html><body><img id='qtlogo' src='http://www.qtsoftware.com/logo.png' width='71' height='174'/><object type='application/x-qt-plugin' classid='pushbutton' id='mybutton' width='100' height='100' foo='bar'/></body></html>"));

    // wait for the page to load
    QTRY_COMPARE(loadSpy.count(), 1);

    // ensure the widget has been created correctly
    QCOMPARE(newPage->calls.count(), 1);
    {
        PluginPage::CallInfo ci = newPage->calls.first();
        QCOMPARE(ci.classid, QString::fromLatin1("pushbutton"));
        QVERIFY(ci.returnValue != 0);
        QVERIFY(ci.returnValue->inherits("QPushButton"));
        const int w_index = ci.paramNames.indexOf("width");
        const int h_index = ci.paramNames.indexOf("height");
        QVERIFY(w_index != -1);
        QVERIFY(h_index != -1);
        QVERIFY(ci.paramValues.value(w_index) == "100");
        QVERIFY(ci.paramValues.value(h_index) == "100");

        // test widget size
        QPushButton* pb = dynamic_cast<QPushButton*>(ci.returnValue);
        QVERIFY(pb);
        QVERIFY(pb->size() == QSize(100, 100));

        // test widget size (JS)
        {
        QString js = QLatin1String("document.getElementById('mybutton').width + 'x' + document.getElementById('mybutton').height");
        QString js_res = newPage->mainFrame()->evaluateJavaScript(js).toString();
        QVERIFY(js_res == QString("100x100"));
        }

        // test image size (JS)
        {
        QString js = QLatin1String("document.getElementById('qtlogo').width + 'x' + document.getElementById('qtlogo').height");
        QString js_res = newPage->mainFrame()->evaluateJavaScript(js).toString();
        QVERIFY(js_res == QString("71x174"));
        }
    }

    // resize image (JS)
    {
    QString js = QLatin1String("document.getElementById('qtlogo').width = '142';");
    QString js_res = newPage->mainFrame()->evaluateJavaScript(js).toString();
    QVERIFY(js_res == QString("142"));

    js = QLatin1String("document.getElementById('qtlogo').height = '348';");
    js_res = newPage->mainFrame()->evaluateJavaScript(js).toString();
    QVERIFY(js_res == QString("348"));

    // check image size
    js = QLatin1String("document.getElementById('qtlogo').width + 'x' + document.getElementById('qtlogo').height");
    js_res = newPage->mainFrame()->evaluateJavaScript(js).toString();
    QVERIFY(js_res == QString("142x348"));
    }


    // resize widget (JS)
    {
    QString js, js_res;

    // test custom attribute (this works!)
    js = QLatin1String("document.getElementById('mybutton').foo = 'bar2';");
    js_res = newPage->mainFrame()->evaluateJavaScript(js).toString();
    QVERIFY(js_res == QString("bar2"));

    js = QLatin1String("document.getElementById('mybutton').foo");
    js_res = newPage->mainFrame()->evaluateJavaScript(js).toString();
    QVERIFY(js_res == QString("bar2"));

    // test width and height attributes
    js = QLatin1String("document.getElementById('mybutton').width = 200;");
    js_res = newPage->mainFrame()->evaluateJavaScript(js).toString();
    QVERIFY(js_res == QString("200"));

    js = QLatin1String("document.getElementById('mybutton').height = 200;");
    js_res = newPage->mainFrame()->evaluateJavaScript(js).toString();
    QVERIFY(js_res == QString("200"));

    // check widget size
    js = QLatin1String("document.getElementById('mybutton').width + 'x' + document.getElementById('mybutton').height");
    js_res = newPage->mainFrame()->evaluateJavaScript(js).toString();
    QVERIFY(js_res == QString("200x200"));
    }

    // ensure the plugin has been resized as well
    {
        PluginPage::CallInfo ci = newPage->calls.first();
        QPushButton* pb = dynamic_cast<QPushButton*>(ci.returnValue);
        QVERIFY(pb);
        QVERIFY(pb->size() == QSize(200, 200));
    }
}

void tst_QWebPage::resizeWidgetNoRepaint()
{
    resizeWidget(false);
}

void tst_QWebPage::resizeWidgetWithRepaint()
{
    resizeWidget(true);
}

void tst_QWebPage::resizeWidget(bool repaint)
{
    QSignalSpy loadSpy(m_view, SIGNAL(loadFinished(bool)));

    PluginPage* newPage = new PluginPage(m_view);
    m_view->setPage(newPage);

    m_view->settings()->setAttribute(QWebSettings::PluginsEnabled, true);
    m_view->settings()->setAttribute(QWebSettings::JavascriptEnabled, true);

    // add a 100x100 widget
    m_view->setHtml(QString("<html><body><object type='application/x-qt-plugin' classid='pushbutton' id='mybutton' width='100' height='100'/></body></html>"));

    // wait for the page to load
    QTRY_COMPARE(loadSpy.count(), 1);

    m_view->show();

    // ensure the widget has been created correctly
    QCOMPARE(newPage->calls.count(), 1);
    {
        PluginPage::CallInfo ci = newPage->calls.first();
        QCOMPARE(ci.classid, QString::fromLatin1("pushbutton"));
        QVERIFY(ci.returnValue != 0);
        QVERIFY(ci.returnValue->inherits("QPushButton"));
        const int w_index = ci.paramNames.indexOf("width");
        const int h_index = ci.paramNames.indexOf("height");
        QVERIFY(w_index != -1);
        QVERIFY(h_index != -1);
        QVERIFY(ci.paramValues.value(w_index) == "100");
        QVERIFY(ci.paramValues.value(h_index) == "100");

        // test widget size
        QPushButton* pb = dynamic_cast<QPushButton*>(ci.returnValue);
        QVERIFY(pb);
        QVERIFY(pb->size() == QSize(100, 100));

        // now resize the widget
        pb->resize(QSize(300, 300));
        QVERIFY(pb->size() == QSize(300, 300));
    }

    // check <object> size
    {
    QString js, js_res;
    js = QLatin1String("document.getElementById('mybutton').width + 'x' + document.getElementById('mybutton').height");
    js_res = newPage->mainFrame()->evaluateJavaScript(js).toString();
    QVERIFY(js_res == QString("300x300"));
    }

    if (repaint)
        m_view->repaint();

    {
    QString js, js_res;
    js = QLatin1String("document.getElementById('mybutton').width + 'x' + document.getElementById('mybutton').height");
    js_res = newPage->mainFrame()->evaluateJavaScript(js).toString();
    QVERIFY(js_res == QString("300x300"));
    }

    // re-check <object> size
    {
	    QString js, js_res;
	    js = QLatin1String("document.getElementById('mybutton').width + 'x' + document.getElementById('mybutton').height");
	    js_res = newPage->mainFrame()->evaluateJavaScript(js).toString();
	    QVERIFY(js_res == QString("300x300"));
    }

    // re-check the plugin size
    {
        PluginPage::CallInfo ci = newPage->calls.first();
        QPushButton* pb = dynamic_cast<QPushButton*>(ci.returnValue);
        QVERIFY(pb);
        QVERIFY(pb->size() == QSize(300, 300));
    }
}

QTEST_MAIN(tst_QWebPage)
#include "tst_qwebpage.moc"
Comment 2 Kent Hansen 2010-03-12 07:29:30 PST
Created attachment 50596 [details]
Testcase
Comment 3 Kent Hansen 2010-03-12 07:31:05 PST
Reproduced with r55658.
Comment 4 Caio Marcelo de Oliveira Filho 2011-02-23 08:29:33 PST
Reproduced with r79439.
Comment 5 Jocelyn Turcotte 2014-02-03 03:15:46 PST
=== Bulk closing of Qt bugs ===

If you believe that this bug report is still relevant for a non-Qt port of webkit.org, please re-open it and remove [Qt] from the summary.

If you believe that this is still an important QtWebKit bug, please fill a new report at https://bugreports.qt-project.org and add a link to this issue. See http://qt-project.org/wiki/ReportingBugsInQt for additional guidelines.