2011-04-09 21:34:04 +04:00
|
|
|
/*
|
|
|
|
This file is part of the PhantomJS project from Ofi Labs.
|
|
|
|
|
|
|
|
Copyright (C) 2011 Ariya Hidayat <ariya.hidayat@gmail.com>
|
2011-08-31 19:29:40 +04:00
|
|
|
Copyright (C) 2011 Ivan De Marino <ivan.de.marino@gmail.com>
|
2011-04-09 21:34:04 +04:00
|
|
|
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
|
|
modification, are permitted provided that the following conditions are met:
|
|
|
|
|
|
|
|
* Redistributions of source code must retain the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer.
|
|
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer in the
|
|
|
|
documentation and/or other materials provided with the distribution.
|
|
|
|
* Neither the name of the <organization> nor the
|
|
|
|
names of its contributors may be used to endorse or promote products
|
|
|
|
derived from this software without specific prior written permission.
|
|
|
|
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
|
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
|
|
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
|
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
|
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
|
|
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
2011-05-25 21:46:51 +04:00
|
|
|
#include "webpage.h"
|
|
|
|
|
|
|
|
#include <math.h>
|
|
|
|
|
2011-04-08 17:30:50 +04:00
|
|
|
#include <QApplication>
|
2011-05-26 11:27:55 +04:00
|
|
|
#include <QDesktopServices>
|
2011-05-25 21:46:51 +04:00
|
|
|
#include <QDir>
|
|
|
|
#include <QFileInfo>
|
2011-09-15 08:41:26 +04:00
|
|
|
#include <QMouseEvent>
|
2011-05-28 12:38:11 +04:00
|
|
|
#include <QNetworkAccessManager>
|
2011-09-15 08:41:26 +04:00
|
|
|
#include <QNetworkRequest>
|
2011-05-25 21:46:51 +04:00
|
|
|
#include <QPainter>
|
|
|
|
#include <QPrinter>
|
2011-06-27 08:51:30 +04:00
|
|
|
#include <QWebElement>
|
2011-05-25 21:46:51 +04:00
|
|
|
#include <QWebFrame>
|
|
|
|
#include <QWebPage>
|
2011-04-08 17:30:50 +04:00
|
|
|
|
2011-09-15 10:46:01 +04:00
|
|
|
#include "networkaccessmanager.h"
|
2011-06-10 19:50:49 +04:00
|
|
|
#include "utils.h"
|
|
|
|
|
2011-05-25 21:46:51 +04:00
|
|
|
#include <gifwriter.h>
|
|
|
|
|
2011-06-07 20:20:22 +04:00
|
|
|
#include "consts.h"
|
|
|
|
|
2011-09-16 18:12:57 +04:00
|
|
|
// Ensure we have at least head and body.
|
|
|
|
#define BLANK_HTML "<html><head></head><body></body></html>"
|
2011-09-15 11:15:56 +04:00
|
|
|
|
2011-05-25 21:46:51 +04:00
|
|
|
class CustomPage: public QWebPage
|
|
|
|
{
|
2011-08-02 04:31:13 +04:00
|
|
|
Q_OBJECT
|
2011-05-25 21:46:51 +04:00
|
|
|
public:
|
2011-05-29 08:16:18 +04:00
|
|
|
CustomPage(WebPage *parent = 0)
|
2011-05-25 21:46:51 +04:00
|
|
|
: QWebPage(parent)
|
2011-05-29 08:16:18 +04:00
|
|
|
, m_webPage(parent)
|
2011-05-25 21:46:51 +04:00
|
|
|
{
|
|
|
|
m_userAgent = QWebPage::userAgentForUrl(QUrl());
|
2011-08-20 12:24:56 +04:00
|
|
|
setForwardUnsupportedContent(true);
|
2011-05-25 21:46:51 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
public slots:
|
|
|
|
bool shouldInterruptJavaScript() {
|
|
|
|
QApplication::processEvents(QEventLoop::AllEvents, 42);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
2011-06-05 11:28:14 +04:00
|
|
|
|
|
|
|
QString chooseFile(QWebFrame *originatingFrame, const QString &oldFile) {
|
|
|
|
Q_UNUSED(originatingFrame);
|
|
|
|
Q_UNUSED(oldFile);
|
|
|
|
return m_uploadFile;
|
|
|
|
}
|
|
|
|
|
2011-05-25 21:46:51 +04:00
|
|
|
void javaScriptAlert(QWebFrame *originatingFrame, const QString &msg) {
|
|
|
|
Q_UNUSED(originatingFrame);
|
2011-05-29 11:29:29 +04:00
|
|
|
m_webPage->emitAlert(msg);
|
2011-05-25 21:46:51 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void javaScriptConsoleMessage(const QString &message, int lineNumber, const QString &sourceID) {
|
2011-06-20 08:06:27 +04:00
|
|
|
m_webPage->emitConsoleMessage(message, lineNumber, sourceID);
|
2011-05-25 21:46:51 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
QString userAgentForUrl(const QUrl &url) const {
|
|
|
|
Q_UNUSED(url);
|
|
|
|
return m_userAgent;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2011-05-29 08:16:18 +04:00
|
|
|
WebPage *m_webPage;
|
2011-05-25 21:46:51 +04:00
|
|
|
QString m_userAgent;
|
2011-06-05 11:28:14 +04:00
|
|
|
QString m_uploadFile;
|
2011-05-25 21:46:51 +04:00
|
|
|
friend class WebPage;
|
|
|
|
};
|
2011-04-08 17:30:50 +04:00
|
|
|
|
2011-09-15 10:46:01 +04:00
|
|
|
WebPage::WebPage(QObject *parent, const Config *config)
|
2011-05-25 21:46:51 +04:00
|
|
|
: QObject(parent)
|
|
|
|
{
|
|
|
|
setObjectName("WebPage");
|
|
|
|
m_webPage = new CustomPage(this);
|
|
|
|
m_mainFrame = m_webPage->mainFrame();
|
|
|
|
|
2011-08-21 04:56:18 +04:00
|
|
|
connect(m_mainFrame, SIGNAL(javaScriptWindowObjectCleared()), SIGNAL(initialized()));
|
2011-06-06 12:22:32 +04:00
|
|
|
connect(m_webPage, SIGNAL(loadStarted()), SIGNAL(loadStarted()));
|
2011-05-25 21:46:51 +04:00
|
|
|
connect(m_webPage, SIGNAL(loadFinished(bool)), SLOT(finish(bool)));
|
|
|
|
|
|
|
|
// Start with transparent background.
|
|
|
|
QPalette palette = m_webPage->palette();
|
|
|
|
palette.setBrush(QPalette::Base, Qt::transparent);
|
|
|
|
m_webPage->setPalette(palette);
|
|
|
|
|
|
|
|
// Page size does not need to take scrollbars into account.
|
|
|
|
m_mainFrame->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff);
|
|
|
|
m_mainFrame->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAlwaysOff);
|
|
|
|
|
2011-05-26 11:27:55 +04:00
|
|
|
m_webPage->settings()->setAttribute(QWebSettings::OfflineStorageDatabaseEnabled, true);
|
|
|
|
m_webPage->settings()->setOfflineStoragePath(QDesktopServices::storageLocation(QDesktopServices::DataLocation));
|
|
|
|
|
2011-06-24 18:28:17 +04:00
|
|
|
m_webPage->settings()->setAttribute(QWebSettings::OfflineWebApplicationCacheEnabled, true);
|
|
|
|
m_webPage->settings()->setOfflineWebApplicationCachePath(QDesktopServices::storageLocation(QDesktopServices::DataLocation));
|
|
|
|
|
2011-05-26 11:27:55 +04:00
|
|
|
#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
|
|
|
|
m_webPage->settings()->setAttribute(QWebSettings::FrameFlatteningEnabled, true);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
m_webPage->settings()->setAttribute(QWebSettings::LocalStorageEnabled, true);
|
|
|
|
m_webPage->settings()->setLocalStoragePath(QDesktopServices::storageLocation(QDesktopServices::DataLocation));
|
|
|
|
|
2011-09-15 11:15:56 +04:00
|
|
|
m_mainFrame->setHtml(BLANK_HTML);
|
2011-05-25 21:46:51 +04:00
|
|
|
|
2011-09-15 10:46:01 +04:00
|
|
|
// Custom network access manager to allow traffic monitoring.
|
2011-09-17 04:34:02 +04:00
|
|
|
m_networkAccessManager = new NetworkAccessManager(this, config);
|
|
|
|
m_webPage->setNetworkAccessManager(m_networkAccessManager);
|
|
|
|
connect(m_networkAccessManager, SIGNAL(resourceRequested(QVariant)),
|
2011-09-15 10:46:01 +04:00
|
|
|
SIGNAL(resourceRequested(QVariant)));
|
2011-09-17 04:34:02 +04:00
|
|
|
connect(m_networkAccessManager, SIGNAL(resourceReceived(QVariant)),
|
2011-09-15 10:46:01 +04:00
|
|
|
SIGNAL(resourceReceived(QVariant)));
|
|
|
|
|
2011-05-25 21:46:51 +04:00
|
|
|
m_webPage->setViewportSize(QSize(400, 300));
|
|
|
|
}
|
|
|
|
|
2011-05-26 01:00:20 +04:00
|
|
|
QWebFrame *WebPage::mainFrame()
|
|
|
|
{
|
|
|
|
return m_mainFrame;
|
|
|
|
}
|
|
|
|
|
2011-05-25 21:46:51 +04:00
|
|
|
QString WebPage::content() const
|
|
|
|
{
|
|
|
|
return m_mainFrame->toHtml();
|
|
|
|
}
|
|
|
|
|
|
|
|
void WebPage::setContent(const QString &content)
|
|
|
|
{
|
|
|
|
m_mainFrame->setHtml(content);
|
|
|
|
}
|
|
|
|
|
2011-06-08 02:22:41 +04:00
|
|
|
|
2011-06-18 07:17:58 +04:00
|
|
|
QString WebPage::libraryPath() const
|
2011-06-08 02:22:41 +04:00
|
|
|
{
|
2011-06-18 07:17:58 +04:00
|
|
|
return m_libraryPath;
|
2011-06-08 02:22:41 +04:00
|
|
|
}
|
|
|
|
|
2011-06-18 07:17:58 +04:00
|
|
|
void WebPage::setLibraryPath(const QString &libraryPath)
|
2011-06-08 02:22:41 +04:00
|
|
|
{
|
2011-06-18 07:17:58 +04:00
|
|
|
m_libraryPath = libraryPath;
|
2011-06-08 02:22:41 +04:00
|
|
|
}
|
|
|
|
|
2011-05-28 09:36:34 +04:00
|
|
|
void WebPage::applySettings(const QVariantMap &def)
|
2011-05-28 07:37:36 +04:00
|
|
|
{
|
|
|
|
QWebSettings *opt = m_webPage->settings();
|
|
|
|
|
2011-06-24 18:57:55 +04:00
|
|
|
opt->setAttribute(QWebSettings::AutoLoadImages, def[PAGE_SETTINGS_LOAD_IMAGES].toBool());
|
|
|
|
opt->setAttribute(QWebSettings::PluginsEnabled, def[PAGE_SETTINGS_LOAD_PLUGINS].toBool());
|
2011-08-20 02:47:34 +04:00
|
|
|
opt->setAttribute(QWebSettings::JavascriptEnabled, def[PAGE_SETTINGS_JS_ENABLED].toBool());
|
2011-08-24 16:48:07 +04:00
|
|
|
#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
|
2011-08-20 02:47:34 +04:00
|
|
|
opt->setAttribute(QWebSettings::XSSAuditingEnabled, def[PAGE_SETTINGS_XSS_AUDITING].toBool());
|
2011-08-24 16:48:07 +04:00
|
|
|
#endif
|
2011-06-24 18:57:55 +04:00
|
|
|
opt->setAttribute(QWebSettings::LocalContentCanAccessRemoteUrls, def[PAGE_SETTINGS_LOCAL_ACCESS_REMOTE].toBool());
|
2011-05-28 07:37:36 +04:00
|
|
|
|
2011-06-24 18:57:55 +04:00
|
|
|
if (def.contains(PAGE_SETTINGS_USER_AGENT))
|
|
|
|
m_webPage->m_userAgent = def[PAGE_SETTINGS_USER_AGENT].toString();
|
2011-09-17 04:34:02 +04:00
|
|
|
|
|
|
|
if (def.contains(PAGE_SETTINGS_USERNAME))
|
|
|
|
m_networkAccessManager->setUserName(def[PAGE_SETTINGS_USERNAME].toString());
|
|
|
|
|
|
|
|
if (def.contains(PAGE_SETTINGS_PASSWORD))
|
|
|
|
m_networkAccessManager->setUserName(def[PAGE_SETTINGS_PASSWORD].toString());
|
2011-05-25 21:46:51 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
QString WebPage::userAgent() const
|
2011-04-08 17:30:50 +04:00
|
|
|
{
|
2011-05-25 21:46:51 +04:00
|
|
|
return m_webPage->m_userAgent;
|
2011-04-08 17:30:50 +04:00
|
|
|
}
|
|
|
|
|
2011-05-25 21:46:51 +04:00
|
|
|
void WebPage::setViewportSize(const QVariantMap &size)
|
2011-04-08 17:30:50 +04:00
|
|
|
{
|
2011-05-25 21:46:51 +04:00
|
|
|
int w = size.value("width").toInt();
|
|
|
|
int h = size.value("height").toInt();
|
|
|
|
if (w > 0 && h > 0)
|
|
|
|
m_webPage->setViewportSize(QSize(w, h));
|
2011-04-08 17:30:50 +04:00
|
|
|
}
|
|
|
|
|
2011-05-25 21:46:51 +04:00
|
|
|
QVariantMap WebPage::viewportSize() const
|
|
|
|
{
|
|
|
|
QVariantMap result;
|
|
|
|
QSize size = m_webPage->viewportSize();
|
|
|
|
result["width"] = size.width();
|
|
|
|
result["height"] = size.height();
|
|
|
|
return result;
|
2011-04-08 17:30:50 +04:00
|
|
|
}
|
|
|
|
|
2011-05-25 21:46:51 +04:00
|
|
|
void WebPage::setClipRect(const QVariantMap &size)
|
|
|
|
{
|
|
|
|
int w = size.value("width").toInt();
|
|
|
|
int h = size.value("height").toInt();
|
|
|
|
int top = size.value("top").toInt();
|
|
|
|
int left = size.value("left").toInt();
|
|
|
|
|
|
|
|
if (w >= 0 && h >= 0)
|
|
|
|
m_clipRect = QRect(left, top, w, h);
|
2011-04-08 17:30:50 +04:00
|
|
|
}
|
|
|
|
|
2011-05-25 21:46:51 +04:00
|
|
|
QVariantMap WebPage::clipRect() const
|
2011-04-08 17:30:50 +04:00
|
|
|
{
|
2011-05-25 21:46:51 +04:00
|
|
|
QVariantMap result;
|
|
|
|
result["width"] = m_clipRect.width();
|
|
|
|
result["height"] = m_clipRect.height();
|
|
|
|
result["top"] = m_clipRect.top();
|
|
|
|
result["left"] = m_clipRect.left();
|
|
|
|
return result;
|
2011-04-08 17:30:50 +04:00
|
|
|
}
|
|
|
|
|
2011-07-22 12:11:30 +04:00
|
|
|
|
|
|
|
void WebPage::setScrollPosition(const QVariantMap &size)
|
|
|
|
{
|
|
|
|
int top = size.value("top").toInt();
|
|
|
|
int left = size.value("left").toInt();
|
|
|
|
m_scrollPosition = QPoint(left,top);
|
2011-09-09 10:36:06 +04:00
|
|
|
m_mainFrame->setScrollPosition(m_scrollPosition);
|
2011-07-22 12:11:30 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
QVariantMap WebPage::scrollPosition() const
|
|
|
|
{
|
|
|
|
QVariantMap result;
|
|
|
|
result["top"] = m_scrollPosition.y();
|
|
|
|
result["left"] = m_scrollPosition.x();
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2011-05-25 21:46:51 +04:00
|
|
|
void WebPage::setPaperSize(const QVariantMap &size)
|
2011-04-08 17:30:50 +04:00
|
|
|
{
|
2011-05-25 21:46:51 +04:00
|
|
|
m_paperSize = size;
|
2011-04-08 17:30:50 +04:00
|
|
|
}
|
|
|
|
|
2011-05-25 21:46:51 +04:00
|
|
|
QVariantMap WebPage::paperSize() const
|
2011-04-08 17:30:50 +04:00
|
|
|
{
|
2011-05-25 21:46:51 +04:00
|
|
|
return m_paperSize;
|
2011-04-08 17:30:50 +04:00
|
|
|
}
|
|
|
|
|
2011-05-25 21:46:51 +04:00
|
|
|
QVariant WebPage::evaluate(const QString &code)
|
2011-04-08 17:30:50 +04:00
|
|
|
{
|
2011-06-01 23:14:20 +04:00
|
|
|
QString function = "(" + code + ")()";
|
2011-05-25 21:46:51 +04:00
|
|
|
return m_mainFrame->evaluateJavaScript(function);
|
|
|
|
}
|
|
|
|
|
2011-05-29 11:29:29 +04:00
|
|
|
void WebPage::emitAlert(const QString &msg)
|
|
|
|
{
|
|
|
|
emit javaScriptAlertSent(msg);
|
|
|
|
}
|
|
|
|
|
2011-06-20 08:06:27 +04:00
|
|
|
void WebPage::emitConsoleMessage(const QString &message, int lineNumber, const QString &source)
|
2011-05-29 08:16:18 +04:00
|
|
|
{
|
2011-06-20 08:06:27 +04:00
|
|
|
emit javaScriptConsoleMessageSent(message, lineNumber, source);
|
2011-05-29 08:16:18 +04:00
|
|
|
}
|
|
|
|
|
2011-05-25 21:46:51 +04:00
|
|
|
void WebPage::finish(bool ok)
|
|
|
|
{
|
|
|
|
QString status = ok ? "success" : "fail";
|
2011-06-06 12:31:37 +04:00
|
|
|
emit loadFinished(status);
|
2011-05-25 21:46:51 +04:00
|
|
|
}
|
|
|
|
|
2011-05-28 12:38:11 +04:00
|
|
|
void WebPage::openUrl(const QString &address, const QVariant &op, const QVariantMap &settings)
|
2011-05-25 21:46:51 +04:00
|
|
|
{
|
2011-05-28 12:38:11 +04:00
|
|
|
QString operation;
|
|
|
|
QByteArray body;
|
|
|
|
|
2011-05-28 09:36:34 +04:00
|
|
|
applySettings(settings);
|
2011-05-25 21:46:51 +04:00
|
|
|
m_webPage->triggerAction(QWebPage::Stop);
|
2011-05-28 12:38:11 +04:00
|
|
|
|
|
|
|
if (op.type() == QVariant::String)
|
|
|
|
operation = op.toString();
|
|
|
|
|
|
|
|
if (op.type() == QVariant::Map) {
|
|
|
|
operation = op.toMap().value("operation").toString();
|
|
|
|
body = op.toMap().value("data").toByteArray();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (operation.isEmpty())
|
|
|
|
operation = "get";
|
|
|
|
|
|
|
|
QNetworkAccessManager::Operation networkOp = QNetworkAccessManager::UnknownOperation;
|
|
|
|
operation = operation.toLower();
|
|
|
|
if (operation == "get")
|
|
|
|
networkOp = QNetworkAccessManager::GetOperation;
|
|
|
|
else if (operation == "head")
|
|
|
|
networkOp = QNetworkAccessManager::HeadOperation;
|
|
|
|
else if (operation == "put")
|
|
|
|
networkOp = QNetworkAccessManager::PutOperation;
|
|
|
|
else if (operation == "post")
|
|
|
|
networkOp = QNetworkAccessManager::PostOperation;
|
|
|
|
else if (operation == "delete")
|
|
|
|
networkOp = QNetworkAccessManager::DeleteOperation;
|
|
|
|
|
|
|
|
if (networkOp == QNetworkAccessManager::UnknownOperation) {
|
|
|
|
m_mainFrame->evaluateJavaScript("console.error('Unknown network operation: " + operation + "');");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-09-15 11:15:56 +04:00
|
|
|
if (address == "about:blank") {
|
|
|
|
m_mainFrame->setHtml(BLANK_HTML);
|
|
|
|
} else {
|
|
|
|
m_mainFrame->load(QNetworkRequest(QUrl(address)), networkOp, body);
|
|
|
|
}
|
2011-05-25 21:46:51 +04:00
|
|
|
}
|
|
|
|
|
2011-08-24 05:53:53 +04:00
|
|
|
void WebPage::release()
|
|
|
|
{
|
|
|
|
deleteLater();
|
|
|
|
}
|
|
|
|
|
2011-05-25 21:46:51 +04:00
|
|
|
bool WebPage::render(const QString &fileName)
|
|
|
|
{
|
2011-05-29 00:51:39 +04:00
|
|
|
if (m_mainFrame->contentsSize().isEmpty())
|
|
|
|
return false;
|
|
|
|
|
2011-05-25 21:46:51 +04:00
|
|
|
QFileInfo fileInfo(fileName);
|
|
|
|
QDir dir;
|
|
|
|
dir.mkpath(fileInfo.absolutePath());
|
|
|
|
|
|
|
|
if (fileName.endsWith(".pdf", Qt::CaseInsensitive))
|
|
|
|
return renderPdf(fileName);
|
|
|
|
|
2011-05-29 00:51:39 +04:00
|
|
|
QImage buffer = renderImage();
|
|
|
|
if (fileName.toLower().endsWith(".gif")) {
|
|
|
|
return exportGif(buffer, fileName);
|
|
|
|
}
|
2011-05-25 21:46:51 +04:00
|
|
|
|
2011-05-29 00:51:39 +04:00
|
|
|
return buffer.save(fileName);
|
|
|
|
}
|
2011-05-29 00:41:01 +04:00
|
|
|
|
2011-05-29 00:51:39 +04:00
|
|
|
QImage WebPage::renderImage()
|
|
|
|
{
|
2011-09-09 10:36:06 +04:00
|
|
|
QSize contentsSize = m_mainFrame->contentsSize();
|
|
|
|
contentsSize -= QSize(m_scrollPosition.x(), m_scrollPosition.y());
|
|
|
|
QRect frameRect = QRect(QPoint(0, 0), contentsSize);
|
2011-05-29 00:41:01 +04:00
|
|
|
if (!m_clipRect.isNull())
|
|
|
|
frameRect = m_clipRect;
|
|
|
|
|
2011-09-09 10:36:06 +04:00
|
|
|
QSize viewportSize = m_webPage->viewportSize();
|
|
|
|
m_webPage->setViewportSize(contentsSize);
|
2011-05-25 21:46:51 +04:00
|
|
|
|
2011-05-29 02:45:16 +04:00
|
|
|
QImage buffer(frameRect.size(), QImage::Format_ARGB32);
|
|
|
|
buffer.fill(qRgba(255, 255, 255, 0));
|
|
|
|
|
|
|
|
QPainter painter;
|
|
|
|
|
|
|
|
// We use tiling approach to work-around Qt software rasterizer bug
|
|
|
|
// when dealing with very large paint device.
|
2011-08-21 09:07:18 +04:00
|
|
|
// See http://code.google.com/p/phantomjs/issues/detail?id=54.
|
2011-05-29 02:45:16 +04:00
|
|
|
const int tileSize = 4096;
|
|
|
|
int htiles = (buffer.width() + tileSize - 1) / tileSize;
|
|
|
|
int vtiles = (buffer.height() + tileSize - 1) / tileSize;
|
|
|
|
for (int x = 0; x < htiles; ++x) {
|
|
|
|
for (int y = 0; y < vtiles; ++y) {
|
|
|
|
|
|
|
|
QImage tileBuffer(tileSize, tileSize, QImage::Format_ARGB32);
|
|
|
|
tileBuffer.fill(qRgba(255, 255, 255, 0));
|
|
|
|
|
|
|
|
// Render the web page onto the small tile first
|
|
|
|
painter.begin(&tileBuffer);
|
|
|
|
painter.setRenderHint(QPainter::Antialiasing, true);
|
|
|
|
painter.setRenderHint(QPainter::TextAntialiasing, true);
|
|
|
|
painter.setRenderHint(QPainter::SmoothPixmapTransform, true);
|
|
|
|
painter.translate(-frameRect.left(), -frameRect.top());
|
|
|
|
painter.translate(-x * tileSize, -y * tileSize);
|
|
|
|
m_mainFrame->render(&painter, QRegion(frameRect));
|
|
|
|
painter.end();
|
|
|
|
|
|
|
|
// Copy the tile to the main buffer
|
|
|
|
painter.begin(&buffer);
|
|
|
|
painter.setCompositionMode(QPainter::CompositionMode_Source);
|
|
|
|
painter.drawImage(x * tileSize, y * tileSize, tileBuffer);
|
|
|
|
painter.end();
|
|
|
|
}
|
|
|
|
}
|
2011-05-29 00:41:01 +04:00
|
|
|
|
2011-05-25 21:46:51 +04:00
|
|
|
m_webPage->setViewportSize(viewportSize);
|
|
|
|
|
2011-05-29 00:51:39 +04:00
|
|
|
return buffer;
|
2011-05-25 21:46:51 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
#define PHANTOMJS_PDF_DPI 72 // Different defaults. OSX: 72, X11: 75(?), Windows: 96
|
|
|
|
|
|
|
|
qreal stringToPointSize(const QString &string)
|
|
|
|
{
|
|
|
|
static const struct {
|
|
|
|
QString unit;
|
|
|
|
qreal factor;
|
|
|
|
} units[] = {
|
|
|
|
{ "mm", 72 / 25.4 },
|
|
|
|
{ "cm", 72 / 2.54 },
|
|
|
|
{ "in", 72 },
|
|
|
|
{ "px", 72.0 / PHANTOMJS_PDF_DPI / 2.54 },
|
|
|
|
{ "", 72.0 / PHANTOMJS_PDF_DPI / 2.54 }
|
|
|
|
};
|
|
|
|
for (uint i = 0; i < sizeof(units) / sizeof(units[0]); ++i) {
|
|
|
|
if (string.endsWith(units[i].unit)) {
|
|
|
|
QString value = string;
|
|
|
|
value.chop(units[i].unit.length());
|
|
|
|
return value.toDouble() * units[i].factor;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WebPage::renderPdf(const QString &fileName)
|
|
|
|
{
|
|
|
|
QPrinter printer;
|
|
|
|
printer.setOutputFormat(QPrinter::PdfFormat);
|
|
|
|
printer.setOutputFileName(fileName);
|
|
|
|
printer.setResolution(PHANTOMJS_PDF_DPI);
|
|
|
|
QVariantMap paperSize = m_paperSize;
|
|
|
|
|
|
|
|
if (paperSize.isEmpty()) {
|
|
|
|
const QSize pageSize = m_mainFrame->contentsSize();
|
|
|
|
paperSize.insert("width", QString::number(pageSize.width()) + "px");
|
|
|
|
paperSize.insert("height", QString::number(pageSize.height()) + "px");
|
|
|
|
paperSize.insert("border", "0px");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (paperSize.contains("width") && paperSize.contains("height")) {
|
|
|
|
const QSizeF sizePt(ceil(stringToPointSize(paperSize.value("width").toString())),
|
|
|
|
ceil(stringToPointSize(paperSize.value("height").toString())));
|
|
|
|
printer.setPaperSize(sizePt, QPrinter::Point);
|
|
|
|
} else if (paperSize.contains("format")) {
|
|
|
|
const QPrinter::Orientation orientation = paperSize.contains("orientation")
|
|
|
|
&& paperSize.value("orientation").toString().compare("landscape", Qt::CaseInsensitive) == 0 ?
|
|
|
|
QPrinter::Landscape : QPrinter::Portrait;
|
|
|
|
printer.setOrientation(orientation);
|
|
|
|
static const struct {
|
|
|
|
QString format;
|
|
|
|
QPrinter::PaperSize paperSize;
|
|
|
|
} formats[] = {
|
2011-06-07 21:57:08 +04:00
|
|
|
{ "A0", QPrinter::A0 },
|
|
|
|
{ "A1", QPrinter::A1 },
|
|
|
|
{ "A2", QPrinter::A2 },
|
2011-05-25 21:46:51 +04:00
|
|
|
{ "A3", QPrinter::A3 },
|
|
|
|
{ "A4", QPrinter::A4 },
|
|
|
|
{ "A5", QPrinter::A5 },
|
2011-06-07 21:57:08 +04:00
|
|
|
{ "A6", QPrinter::A6 },
|
|
|
|
{ "A7", QPrinter::A7 },
|
|
|
|
{ "A8", QPrinter::A8 },
|
|
|
|
{ "A9", QPrinter::A9 },
|
|
|
|
{ "B0", QPrinter::B0 },
|
|
|
|
{ "B1", QPrinter::B1 },
|
|
|
|
{ "B2", QPrinter::B2 },
|
|
|
|
{ "B3", QPrinter::B3 },
|
|
|
|
{ "B4", QPrinter::B4 },
|
|
|
|
{ "B5", QPrinter::B5 },
|
|
|
|
{ "B6", QPrinter::B6 },
|
|
|
|
{ "B7", QPrinter::B7 },
|
|
|
|
{ "B8", QPrinter::B8 },
|
|
|
|
{ "B9", QPrinter::B9 },
|
|
|
|
{ "B10", QPrinter::B10 },
|
2011-06-07 22:26:41 +04:00
|
|
|
{ "C5E", QPrinter::C5E },
|
2011-06-07 21:57:08 +04:00
|
|
|
{ "Comm10E", QPrinter::Comm10E },
|
|
|
|
{ "DLE", QPrinter::DLE },
|
|
|
|
{ "Executive", QPrinter::Executive },
|
|
|
|
{ "Folio", QPrinter::Folio },
|
|
|
|
{ "Ledger", QPrinter::Ledger },
|
2011-05-25 21:46:51 +04:00
|
|
|
{ "Legal", QPrinter::Legal },
|
|
|
|
{ "Letter", QPrinter::Letter },
|
|
|
|
{ "Tabloid", QPrinter::Tabloid }
|
|
|
|
};
|
|
|
|
printer.setPaperSize(QPrinter::A4); // Fallback
|
|
|
|
for (uint i = 0; i < sizeof(formats) / sizeof(formats[0]); ++i) {
|
|
|
|
if (paperSize.value("format").toString().compare(formats[i].format, Qt::CaseInsensitive) == 0) {
|
|
|
|
printer.setPaperSize(formats[i].paperSize);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const qreal border = paperSize.contains("border") ?
|
|
|
|
floor(stringToPointSize(paperSize.value("border").toString())) : 0;
|
|
|
|
printer.setPageMargins(border, border, border, border, QPrinter::Point);
|
|
|
|
|
|
|
|
m_mainFrame->print(&printer);
|
|
|
|
return true;
|
2011-04-08 17:30:50 +04:00
|
|
|
}
|
2011-06-05 11:28:14 +04:00
|
|
|
|
|
|
|
void WebPage::uploadFile(const QString &selector, const QString &fileName)
|
|
|
|
{
|
|
|
|
QWebElement el = m_mainFrame->findFirstElement(selector);
|
|
|
|
if (el.isNull())
|
|
|
|
return;
|
|
|
|
|
|
|
|
m_webPage->m_uploadFile = fileName;
|
2011-06-07 20:20:22 +04:00
|
|
|
el.evaluateJavaScript(JS_ELEMENT_CLICK);
|
|
|
|
}
|
|
|
|
|
2011-06-08 02:22:41 +04:00
|
|
|
bool WebPage::injectJs(const QString &jsFilePath) {
|
2011-06-18 07:17:58 +04:00
|
|
|
return Utils::injectJsInFrame(jsFilePath, m_libraryPath, m_mainFrame);
|
2011-06-07 20:20:22 +04:00
|
|
|
}
|
2011-06-09 19:11:39 +04:00
|
|
|
|
|
|
|
void WebPage::_appendScriptElement(const QString &scriptUrl) {
|
|
|
|
m_mainFrame->evaluateJavaScript( QString(JS_APPEND_SCRIPT_ELEMENT).arg(scriptUrl) );
|
|
|
|
}
|
2011-08-02 04:31:13 +04:00
|
|
|
|
2011-09-16 18:15:21 +04:00
|
|
|
void WebPage::sendEvent(const QString &type, const QVariant &arg1, const QVariant &arg2)
|
|
|
|
{
|
|
|
|
if (type == "mousedown" || type == "mouseup" || type == "mousemove") {
|
|
|
|
QMouseEvent::Type eventType = QEvent::None;
|
|
|
|
Qt::MouseButton button = Qt::LeftButton;
|
|
|
|
Qt::MouseButtons buttons = Qt::LeftButton;
|
|
|
|
|
|
|
|
if (type == "mousedown")
|
|
|
|
eventType = QEvent::MouseButtonPress;
|
|
|
|
if (type == "mouseup")
|
|
|
|
eventType = QEvent::MouseButtonRelease;
|
|
|
|
if (type == "mousemove") {
|
|
|
|
eventType = QEvent::MouseMove;
|
|
|
|
button = Qt::NoButton;
|
|
|
|
buttons = Qt::NoButton;
|
|
|
|
}
|
|
|
|
Q_ASSERT(eventType != QEvent::None);
|
2011-08-07 17:29:50 +04:00
|
|
|
|
2011-09-16 18:15:21 +04:00
|
|
|
int x = arg1.toInt();
|
|
|
|
int y = arg2.toInt();
|
|
|
|
QMouseEvent *event = new QMouseEvent(eventType, QPoint(x, y), button, buttons, Qt::NoModifier);
|
|
|
|
QApplication::postEvent(m_webPage, event);
|
|
|
|
QApplication::processEvents();
|
|
|
|
return;
|
|
|
|
}
|
2011-08-07 17:29:50 +04:00
|
|
|
|
2011-09-16 18:15:21 +04:00
|
|
|
if (type == "click") {
|
|
|
|
sendEvent("mousedown", arg1, arg2);
|
|
|
|
sendEvent("mouseup", arg1, arg2);
|
|
|
|
return;
|
|
|
|
}
|
2011-08-07 17:29:50 +04:00
|
|
|
}
|
|
|
|
|
2011-08-02 04:31:13 +04:00
|
|
|
#include "webpage.moc"
|