Files
GoldenCheetah/src/Train/WebPageWindow.cpp
Mark Liversedge a404c859b8 Fix Webpage Download Intercept Duplicates
.. only process the download request if we are visible.

.. otherwise you get an import wizard for every active
   WebPageWindow (!).
2017-05-20 11:34:37 +01:00

407 lines
13 KiB
C++

/*
* Copyright (c) 2016 Damien Grauser (Damien.Grauser@gmail.com)
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program 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 General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "WebPageWindow.h"
#include "MainWindow.h"
#include "RideItem.h"
#include "RideFile.h"
#include "RideImportWizard.h"
#include "IntervalItem.h"
#include "IntervalTreeView.h"
#include "SmallPlot.h"
#include "Context.h"
#include "Athlete.h"
#include "Zones.h"
#include "Settings.h"
#include "Colors.h"
#include "Units.h"
#include "TimeUtils.h"
#include "HelpWhatsThis.h"
#ifdef NOWEBKIT
#include <QtWebChannel>
#include <QWebEngineProfile>
#include <QWebEngineDownloadItem>
#endif
// overlay helper
#include "TabView.h"
#include "GcOverlayWidget.h"
#include "IntervalSummaryWindow.h"
#include <QDebug>
#ifdef NOWEBKIT
#include <QWebEngineUrlRequestInterceptor>
#include <QWebEngineSettings>
#include <QWebEngineUrlSchemeHandler>
#include <QWebEngineUrlRequestJob>
// request interceptor to get downloads in QtWebEngine
class WebDownloadInterceptor : public QWebEngineUrlRequestInterceptor
{
public:
WebDownloadInterceptor() : QWebEngineUrlRequestInterceptor(Q_NULLPTR) {}
public slots:
void interceptRequest(QWebEngineUrlRequestInfo &) {
//qDebug()<<info.requestUrl().toString();
}
};
// custom scheme handler
class WebSchemeHandler : public QWebEngineUrlSchemeHandler
{
public:
WebSchemeHandler(QWebEngineView *view) : QWebEngineUrlSchemeHandler(Q_NULLPTR), view(view) {}
public slots:
void requestStarted(QWebEngineUrlRequestJob *request) {
QString inject = QString("window.resolveLocalFileSystemURL = window.resolveLocalFileSystemURL || window.webkitResolveLocalFileSystemURL; "
"window.resolveLocalFileSystemURL('%1', function(fs) {console.warn(fs.name);}, function(evt) {console.warn(evt.target.error.code);} );")
.arg(request->requestUrl().toString());
view->page()->runJavaScript(inject);
}
private:
QWebEngineView *view;
};
#endif
// declared in main, we only want to use it to get QStyle
extern QApplication *application;
WebPageWindow::WebPageWindow(Context *context) : GcChartWindow(context), context(context), firstShow(true)
{
//
// reveal controls widget
//
// layout reveal controls
QHBoxLayout *revealLayout = new QHBoxLayout;
revealLayout->setContentsMargins(0,0,0,0);
rButton = new QPushButton(application->style()->standardIcon(QStyle::SP_ArrowRight), "", this);
rCustomUrl = new QLineEdit(this);
revealLayout->addStretch();
revealLayout->addWidget(rButton);
revealLayout->addWidget(rCustomUrl);
revealLayout->addStretch();
connect(rCustomUrl, SIGNAL(returnPressed()), this, SLOT(userUrl()));
connect(rButton, SIGNAL(clicked(bool)), this, SLOT(userUrl()));
setRevealLayout(revealLayout);
//
// Chart settings
//
QWidget *settingsWidget = new QWidget(this);
settingsWidget->setContentsMargins(0,0,0,0);
//HelpWhatsThis *helpSettings = new HelpWhatsThis(settingsWidget);
//settingsWidget->setWhatsThis(helpSettings->getWhatsThisText(HelpWhatsThis::ChartRides_Critical_MM_Config_Settings));
QFormLayout *commonLayout = new QFormLayout(settingsWidget);
customUrlLabel = new QLabel(tr("URL"));
customUrl = new QLineEdit(this);
customUrl->setFixedWidth(250);
customUrl->setText("");
commonLayout->addRow(customUrlLabel, customUrl);
commonLayout->addRow(new QLabel("Hit return to apply URL"));
setControls(settingsWidget);
setContentsMargins(0,0,0,0);
layout = new QVBoxLayout();
layout->setSpacing(0);
layout->setContentsMargins(2,0,2,2);
setChartLayout(layout);
#ifdef NOWEBKIT
view = new QWebEngineView(this);
connect(view, SIGNAL(loadFinished(bool)), this, SLOT(loadFinished(bool)));
//view->page()->profile()->setHttpUserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.79 Safari/537.36 Edge/14.14393");
// add a download interceptor
WebDownloadInterceptor *interceptor = new WebDownloadInterceptor;
view->page()->profile()->setRequestInterceptor(interceptor);
// cookies and storage
view->page()->profile()->setPersistentCookiesPolicy(QWebEngineProfile::ForcePersistentCookies);
QString temp = const_cast<AthleteDirectoryStructure*>(context->athlete->directoryStructure())->temp().absolutePath();
view->page()->profile()->setCachePath(temp);
view->page()->profile()->setPersistentStoragePath(temp);
// web scheme handler - not needed must compile with QT 5.9 or higher
//WebSchemeHandler *handler = new WebSchemeHandler(view);
//view->page()->profile()->installUrlSchemeHandler("filesystem:https", handler);
//view->page()->profile()->installUrlSchemeHandler("filesystem", handler);
// add some settings
view->settings()->setAttribute(QWebEngineSettings::LocalContentCanAccessRemoteUrls, true);
view->settings()->setAttribute(QWebEngineSettings::LocalStorageEnabled, true);
#else
view = new QWebView();
connect(view, SIGNAL(loadFinished(bool)), this, SLOT(loadFinished(bool)));
#endif
view->setPage(new simpleWebPage());
view->setContentsMargins(0,0,0,0);
view->page()->view()->setContentsMargins(0,0,0,0);
view->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
view->setAcceptDrops(false);
layout->addWidget(view);
HelpWhatsThis *help = new HelpWhatsThis(view);
view->setWhatsThis(help->getWhatsThisText(HelpWhatsThis::ChartRides_Map));
// if we change in settings, force replot by pressing return
connect(customUrl, SIGNAL(returnPressed()), this, SLOT(forceReplot()));
first = true;
configChanged(CONFIG_APPEARANCE);
// intercept downloads
#ifdef NOWEBKIT
connect(view->page()->profile(), SIGNAL(downloadRequested(QWebEngineDownloadItem*)), this, SLOT(downloadRequested(QWebEngineDownloadItem*)));
connect(view->page(), SIGNAL(linkHovered(QString)), this, SLOT(linkHovered(QString)));
#else
view->page()->setForwardUnsupportedContent(true);
connect(view->page(),SIGNAL(downloadRequested(QNetworkRequest)),this,SLOT(download(QNetworkRequest)));
connect(view->page(),SIGNAL(unsupportedContent(QNetworkReply*)),this,SLOT(unsupportedContent(QNetworkReply*)));
#endif
}
WebPageWindow::~WebPageWindow()
{
}
void
WebPageWindow::configChanged(qint32)
{
// tinted palette for headings etc
QPalette palette;
palette.setBrush(QPalette::Window, QBrush(GColor(CPLOTBACKGROUND)));
palette.setColor(QPalette::WindowText, GColor(CPLOTMARKER));
palette.setColor(QPalette::Text, GColor(CPLOTMARKER));
palette.setColor(QPalette::Base, GCColor::alternateColor(GColor(CPLOTBACKGROUND)));
setPalette(palette);
}
void
WebPageWindow::forceReplot()
{
#ifdef NOWEBKIT
view->setZoomFactor(dpiXFactor);
view->setUrl(QUrl(customUrl->text()));
#else
view->page()->mainFrame()->load(QUrl(customUrl->text()));
//qDebug()<<"load page"<<customUrl->text();
#endif
}
void
WebPageWindow::userUrl()
{
// add http:// if scheme is missing
QRegExp hasscheme("^[^:]*://.*");
QString url = rCustomUrl->text();
if (!hasscheme.exactMatch(url)) url = "http://" + url;
#ifdef NOWEBKIT
view->setZoomFactor(dpiXFactor);
view->setUrl(QUrl(url));
#else
view->page()->mainFrame()->load(QUrl(url));
//qDebug()<<"load page"<<rCustomUrl->text();
#endif
}
void
WebPageWindow::loadFinished(bool ok)
{
QString string;
#ifdef NOWEBKIT
if (ok) string = view->url().toString();
#else
if (ok) string = view->page()->mainFrame()->url().toString();
#endif
rCustomUrl->setText(string);
}
void
WebPageWindow::finished(QNetworkReply * reply)
{
if(reply->error() !=QNetworkReply::NoError) {
QMessageBox::warning(0, QString("NetworkAccessManager NetworkReply"),
QString("QNetworkReply error string: \n") + reply->errorString(),
QMessageBox::Ok);
}
}
bool
WebPageWindow::event(QEvent *event)
{
// nasty nasty nasty hack to move widgets as soon as the widget geometry
// is set properly by the layout system, by default the width is 100 and
// we wait for it to be set properly then put our helper widget on the RHS
if (event->type() == QEvent::Resize && geometry().width() != 100) {
// put somewhere nice on first show
if (firstShow) {
firstShow = false;
//helperWidget()->move(mainWidget()->geometry().width()-275, 50);
}
// if off the screen move on screen
/*if (helperWidget()->geometry().x() > geometry().width()) {
helperWidget()->move(mainWidget()->geometry().width()-275, 50);
}*/
}
return QWidget::event(event);
}
#ifdef NOWEBKIT
void
WebPageWindow::downloadRequested(QWebEngineDownloadItem *item)
{
// only do it if I am visible, as shared across web page instances
if (!amVisible()) return;
//qDebug()<<"Download Requested:"<<item->path()<<item->url().toString();
//qDebug() << "Format: " << item->savePageFormat();
//qDebug() << "Path: " << item->path();
//qDebug() << "Type: " << item->type();
//qDebug() << "MimeType: " << item->mimeType();
// lets go get it!
filenames.clear();
filenames << item->path();
// set save
connect(item, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(downloadProgress(qint64,qint64)));
connect(item, SIGNAL(finished()), this, SLOT(downloadFinished()));
// kick off download
item->accept(); // lets download it!
}
void
WebPageWindow::downloadFinished()
{
// now try and import it (if download failed file won't exist)
// dialog is self deleting
RideImportWizard *dialog = new RideImportWizard(filenames, context);
dialog->process(); // do it!
}
void
WebPageWindow::downloadProgress(qint64 a, qint64 b)
{
Q_UNUSED(a)
Q_UNUSED(b)
//qDebug()<<"downloading..." << a<< b;
}
void
WebPageWindow::linkHovered(QString)
{
//qDebug()<<"hovering over:" << link;
}
#else
// downloads requested ...
void
WebPageWindow::download(const QNetworkRequest &request)
{
//qDebug()<<"Download Requested: "<<request.url();
}
void
WebPageWindow::unsupportedContent(QNetworkReply * reply)
{
QRegExp filenameRegEx("^attachment; filename=\"(.*)\"$");
if (filenameRegEx.exactMatch(reply->rawHeader("Content-Disposition"))) {
QString filename = filenameRegEx.cap(1);
//qDebug()<<"Unsupported Content: "<<reply->url()<<filename;
// connect the dots
connect(reply, SIGNAL(readyRead()), this, SLOT(readyRead()));
connect(reply, SIGNAL(finished()), this, SLOT(readFileCompleted()));
// remember what we started, but only one at a time XXX
filenames.clear();
filenames << filename;
QByteArray *data = new QByteArray;
buffers.insert(reply, data);
// kick off the fetch
data->append(reply->readAll());
}
return;
}
void
WebPageWindow::readyRead()
{
QNetworkReply *reply = static_cast<QNetworkReply*>(QObject::sender());
buffers.value(reply)->append(reply->readAll());
}
void
WebPageWindow::readFileCompleted()
{
//qDebug()<<"WebPageWindow::readFileCompleted\n";
QNetworkReply *reply = static_cast<QNetworkReply*>(QObject::sender());
//qDebug()<<"WebPageWindow::reply" << buffers.value(reply)->constData();
// process it then ........
QString name = QString("%1/%2")
.arg(const_cast<AthleteDirectoryStructure*>(context->athlete->directoryStructure())->temp().absolutePath())
.arg(filenames.at(0));
QFile readme(name);
if (readme.open(QFile::ReadWrite)) {
filenames.clear();
filenames << name;
// save the file away
readme.write(*buffers.value(reply), buffers.value(reply)->length());
readme.close();
// now process it
RideImportWizard *dialog = new RideImportWizard(filenames, context);
dialog->process(); // do it!
}
// clean up
delete buffers.value(reply);
buffers.remove(reply);
}
#endif