--- a/dirbrowser/cdbrowser.cpp
+++ b/dirbrowser/cdbrowser.cpp
@@ -16,18 +16,25 @@
*/
#include <iostream>
+#include <functional>
+#include <time.h>
+
#include "libupnpp/config.h"
#include <QUrl>
#ifdef USING_WEBENGINE
-#warning acceptNavigationRequest is only called for http://. Change all links
// Notes for WebEngine
-// - All links must be changed to http://
-// - It seems that the links passed to acceptNav.. have the host part
-// lowercased -> change S0 to http://s0, not http://S0
-
+// - All links must begin with http:// for acceptNavigationRequest to be
+// called.
+// - The links passed to acceptNav.. have the host part
+// lowercased -> we change S0 to http://h/S0, not http://S0
+// As far as I can see, 2016-09, two relatively small issues remaining:
+// - No way to know when page load is done (see mySetHtml())
+// - No way to save/restore the scroll position, all the rest works ok.
+
#include <QWebEnginePage>
#include <QWebEngineSettings>
+#include <QLoggingCategory>
#else
#include <QWebFrame>
#include <QWebSettings>
@@ -56,10 +63,19 @@
using namespace std;
+using namespace std::placeholders;
using namespace UPnPP;
using namespace UPnPClient;
static const string minimFoldersViewPrefix("0$folders");
+
+static void msleep(int millis)
+{
+ struct timespec spec;
+ spec.tv_sec = millis / 1000;
+ spec.tv_nsec = (millis % 1000) * 1000000;
+ nanosleep(&spec, 0);
+}
void CDWebPage::javaScriptConsoleMessage(
#ifdef USING_WEBENGINE
@@ -69,44 +85,54 @@
{
Q_UNUSED(msg);
Q_UNUSED(lineNum);
- //qDebug() << "JAVASCRIPT: "<< msg << "at line " << lineNum;
-}
-
+ LOGDEB("JAVASCRIPT: "<< qs2utf8s(msg) << " at line " << lineNum << endl);
+}
+
+static const QByteArray html_top_orig(
+ "<html><head>"
+ "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">"
+ );
+static QByteArray html_top;
CDBrowser::CDBrowser(QWidget* parent)
- : QWebView(parent), m_reader(0), m_reaper(0), m_progressD(0),
+ : QWEBVIEW(parent), m_reader(0), m_reaper(0), m_progressD(0),
m_browsers(0), m_lastbutton(Qt::LeftButton), m_sysUpdId(0)
{
setPage(new CDWebPage(this));
-#ifndef USING_WEBENGINE
+ html_top = html_top_orig;
+
+#ifdef USING_WEBENGINE
+ QLoggingCategory("js").setEnabled(QtDebugMsg, true);
+ QString jsfn = Helper::getSharePath() + "/cdbrowser/containerscript.js";
+ QString js = "<script type=\"text/javascript\">\n";
+ js += QString::fromUtf8(Helper::readFileToByteArray(jsfn));
+ js += "</script>\n";
+ html_top += js;
+#else
connect(this, SIGNAL(linkClicked(const QUrl &)),
this, SLOT(onLinkClicked(const QUrl &)));
-
+ // Not available and not sure that this is needed with webengine ?
+ connect(page()->mainFrame(), SIGNAL(contentsSizeChanged(const QSize&)),
+ this, SLOT(onContentsSizeChanged(const QSize&)));
page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks);
#endif
- settings()->setAttribute(QWebSettings::JavascriptEnabled, true);
+ setStyleSheet(CSettingsStorage::getInstance()->getPlayerStyle());
+
+ settings()->setAttribute(QWEBSETTINGS::JavascriptEnabled, true);
if (parent) {
- settings()->setFontSize(QWebSettings::DefaultFontSize,
+ settings()->setFontSize(QWEBSETTINGS::DefaultFontSize,
parent->font().pointSize()+4);
- settings()->setFontFamily(QWebSettings::StandardFont,
+ settings()->setFontFamily(QWEBSETTINGS::StandardFont,
parent->font().family());
}
- setStyleSheet(CSettingsStorage::getInstance()->getPlayerStyle());
setContextMenuPolicy(Qt::CustomContextMenu);
connect(this, SIGNAL(customContextMenuRequested(const QPoint&)),
this, SLOT(createPopupMenu(const QPoint&)));
-#ifdef USING_WEBENGINE
-#warning tobedone
-#else
- connect(page()->mainFrame(), SIGNAL(contentsSizeChanged(const QSize&)),
- this, SLOT(onContentsSizeChanged(const QSize&)));
-#endif
-
- setHtml("<html><head><title>Upplay directory browser !</title></head>"
- "<body><p>Looking for servers...</p>"
- "</body></html>");
-
+
+ QByteArray html = html_top +
+ "</head><body><p>Looking for servers...</p></body></html>";
+ mySetHtml(html);
m_timer.setSingleShot(1);
connect(&m_timer, SIGNAL(timeout()), this, SLOT(initialPage()));
m_timer.start(0);
@@ -117,44 +143,51 @@
deleteReaders();
}
+void CDBrowser::mySetHtml(const QString& html)
+{
+ // Tried to wait using an event loop, but it seems that the
+ // loadFinished() signal is only ever triggered once, even if we
+ // replace with a new page ???
+
+ // QEventLoop pause;
+ // setPage(new CDWebPage(this));
+ // connect(page(), SIGNAL(loadFinished(bool)), &pause, SLOT(quit()));
+ setHtml(html);
+ msleep(100);
+ //pause.exec();
+}
+
void CDBrowser::mouseReleaseEvent(QMouseEvent *event)
{
//qDebug() << "CDBrowser::mouseReleaseEvent";
m_lastbutton = event->button();
- QWebView::mouseReleaseEvent(event);
+ QWEBVIEW::mouseReleaseEvent(event);
}
void CDBrowser::setStyleSheet(bool dark)
{
QString cssfn = Helper::getSharePath() + "/cdbrowser/cdbrowser.css";
- QByteArray css = Helper::readFileToByteArray(cssfn);
+ QByteArray cssdata = Helper::readFileToByteArray(cssfn);
if (dark) {
cssfn = Helper::getSharePath() + "/cdbrowser/dark.css";
- css += Helper::readFileToByteArray(cssfn);
+ cssdata += Helper::readFileToByteArray(cssfn);
} else {
cssfn = Helper::getSharePath() + "/cdbrowser/standard.css";
}
- css += Helper::readFileToByteArray(cssfn);
-
- css = QByteArray("data:text/css;charset=utf-8;base64,") +
- css.toBase64();
-
- QUrl cssurl(QString::fromUtf8((const char*)css));
-#ifdef USING_WEBENGINE
-#warning tobedone
-#else
- settings()->setUserStyleSheetUrl(cssurl);
-#endif
+ cssdata += Helper::readFileToByteArray(cssfn);
+
+ html_top += "<style>\n";
+ html_top += cssdata;
+ html_top += "</style>\n";
}
void CDBrowser::onContentsSizeChanged(const QSize&)
{
//qDebug() << "CDBrowser::onContentsSizeChanged: scrollpos " <<
// page()->mainFrame()->scrollPosition();
-#ifdef USING_WEBENGINE
+#ifndef USING_WEBENGINE
// No way to do this with webengine ?
-#else
page()->mainFrame()->setScrollPosition(m_savedscrollpos);
#endif
}
@@ -166,35 +199,28 @@
ba.append(string);
return ba.toBase64();
}
-static void dumpHtml(QString html)
-{
- qDebug() << "showHtml: " << html;
-}
-
-static void jsResult(QVariant res)
-{
- qDebug() << "jsResult: " << res.toString();
-}
#endif
void CDBrowser::appendHtml(const QString& elt_id, const QString& html)
{
- //qDebug()<<"CDBrowser::appendHtml: elt_id ["<<elt_id<< "] html "<< html;
+ LOGDEB1("CDBrowser::appendHtml: elt_id [" << qs2utf8s(elt_id) <<
+ "] html "<< qs2utf8s(html) << endl);
#ifdef USING_WEBENGINE
// With webengine, we can't access qt object from the page, so we
// do everything in js. The data is encoded as base64 to avoid
// quoting issues
QString js;
- js = "var morehtml = \"" + base64_encode(html) + "\";\n";
+ js = "var morehtml = '" + base64_encode(html) + "';\n";
if (elt_id.isEmpty()) {
js += QString("document.body.innerHTML += window.atob(morehtml);\n");
} else {
js += QString("document.getElementById(\"%1\").innerHTML += "
"window.atob(morehtml);\n").arg(elt_id);
}
- qDebug() << "Executing JS: [" << js << "]";
- page()->runJavaScript(js, jsResult);
+ page()->runJavaScript(js, [] (QVariant res) {
+ Q_UNUSED(res);
+ });
#else
QWebFrame *mainframe = page()->mainFrame();
StringObj morehtml(html);
@@ -215,7 +241,7 @@
bool CDBrowser::newCds(int cdsidx)
{
if (cdsidx > int(m_msdescs.size())) {
- LOGERR("CDBrowser::onLinkClicked: bad link index: " << cdsidx
+ LOGERR("CDBrowser::newCds: bad link index: " << cdsidx
<< " cd count: " << m_msdescs.size() << endl);
return false;
}
@@ -226,7 +252,7 @@
}
CDSH cds = ms->cds();
if (!cds) {
- LOGERR("CDBrowser::onLinkClicked: null cds" << endl);
+ LOGERR("CDBrowser::newCds: null cds" << endl);
return false;
}
m_cds = std::shared_ptr<ContentDirectoryQO>(new ContentDirectoryQO(cds));
@@ -283,8 +309,15 @@
{
m_timer.stop();
string scurl = qs2utf8s(url.toString());
- //qDebug() << "CDBrowser::onLinkClicked: " << url.toString() <<
- //" button " << m_lastbutton << " mid " << Qt::MidButton;
+ qDebug() << "CDBrowser::onLinkClicked: " << url.toString() <<
+ " button " << m_lastbutton << " mid " << Qt::MidButton;
+ // Get rid of http://
+ if (scurl.find("http://h/") != 0) {
+ qDebug() << "CDBrowser::onLinkClicked: bad link ! : " << url.toString();
+ return;
+ }
+ scurl = scurl.substr(9);
+ LOGDEB("CDBrowser::onLinkClicked: corrected url: [" << scurl << "]" <<endl);
int what = scurl[0];
@@ -429,23 +462,21 @@
qDebug() << "CDBrowser::browsein: " << UDN << " not found";
}
-static const QString init_container_pagetop(
- "<html><head>"
- "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">"
- "<script type=\"text/javascript\">\n");
-
-static const QString init_container_pagebot(
- "</script>\n"
+static const QByteArray init_container_pagemid(
#ifdef USING_WEBENGINE
- "</head><body onload=\"addEventListener('click', saveLoc)\">\n"
+ "</head><body onload=\"addEventListener('contextmenu', saveLoc)\">\n"
#else
"</head><body>\n"
#endif
+ );
+static const QByteArray init_container_pagebot(
+ "<table id=\"entrylist\"></table>"
"</body></html>"
);
void CDBrowser::initContainerHtml(const string& ss)
{
+ LOGDEB("CDBrowser::initContainerHtml\n");
QString htmlpath("<div id=\"browsepath\"><ul>");
bool current_is_search = false;
for (unsigned i = 0; i < m_curpath.size(); i++) {
@@ -463,7 +494,7 @@
if (i == 0)
sep = "";
htmlpath += QString("<li class=\"container\" objid=\"%3\">"
- " %4 <a href=\"L%1\">%2</a></li>").
+ " %4 <a href=\"http://h/L%1\">%2</a></li>").
arg(i).arg(title).arg(objid).arg(sep);
}
htmlpath += QString("</ul></div><br clear=\"all\"/>");
@@ -472,15 +503,11 @@
QString::fromUtf8(ss.c_str()) + "<br/>";
}
- QString js;
-#ifdef USING_WEBENGINE
- QString jsfn = Helper::getSharePath() + "/cdbrowser/containerscript.js";
- js = QString::fromUtf8(Helper::readFileToByteArray(jsfn));
-#endif
- setHtml(init_container_pagetop + js + init_container_pagebot);
- waitForPage();
- appendHtml(QString(), htmlpath);
- appendHtml(QString(), "<table id=\"entrylist\"></table>");
+ QByteArray html = html_top + init_container_pagemid +
+ htmlpath.toUtf8() + init_container_pagebot;
+ LOGDEB1("initContainerHtml: initial content: " << qs2utf8s(html) << endl);
+ mySetHtml(html);
+ LOGDEB("CDBrowser::initContainerHtml done\n");
}
// Re-browse (because sort criteria changed probably)
@@ -493,7 +520,7 @@
void CDBrowser::browseContainer(string ctid, string cttitle, QPoint scrollpos)
{
- qDebug() << "CDBrowser::browseContainer: " << " ctid " << ctid.c_str();
+ LOGDEB("CDBrowser::browseContainer: " << " ctid " << ctid << endl);
deleteReaders();
emit sig_now_in(this, QString::fromUtf8(cttitle.c_str()));
@@ -548,9 +575,9 @@
static QString CTToHtml(unsigned int idx, const UPnPDirObject& e)
{
QString out;
- out += QString("<tr class=\"container\" objid=\"%1\" objidx=\"%2\">"
+ out += QString("<tr class=\"container\" objid=\"%1\" objidx=\"\">"
"<td></td><td>").arg(e.m_id.c_str());
- out += QString("<a class=\"ct_title\" href=\"C%1\">").arg(idx);
+ out += QString("<a class=\"ct_title\" href=\"http://h/C%1\">").arg(idx);
out += QString::fromUtf8(Helper::escapeHtml(e.m_title).c_str());
out += "</a></td>";
string val;
@@ -587,7 +614,7 @@
Helper::escapeHtml(val).c_str() + "</td>";
out += "<td class=\"tk_title\">";
- out += QString("<a href=\"I%1\">").arg(idx);
+ out += QString("<a href=\"http://h/I%1\">").arg(idx);
out += QString::fromUtf8(Helper::escapeHtml(e.m_title).c_str());
out += "</a>";
out += "</td>";
@@ -797,9 +824,7 @@
}
-static const string init_server_page(
- "<html><head>"
- "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">"
+static const QByteArray init_server_page_bot(
"</head><body>"
"<h2 id=\"cdstitle\">Content Directory Services</h2>"
"</body></html>"
@@ -809,23 +834,10 @@
{
QString out;
out += QString("<p class=\"cdserver\" cdsid=\"%1\">").arg(idx);
- out += QString("<a href=\"S%1\">").arg(idx);
+ out += QString("<a href=\"http://h/S%1\">").arg(idx);
out += QString::fromUtf8(dev.friendlyName.c_str());
out += QString("</a></p>");
return out;
-}
-
-void CDBrowser::waitForPage()
-{
-#ifdef USING_WEBENGINE
- QEventLoop loop;
- QTimer tT;
- tT.setSingleShot(true);
- connect(&tT, SIGNAL(timeout()), &loop, SLOT(quit()));
- connect(this, SIGNAL(loadFinished(bool)), &loop, SLOT(quit()));
- tT.start(2000);
- loop.exec();
-#endif
}
void CDBrowser::initialPage()
@@ -861,8 +873,6 @@
//LOGDEB("CDBrowser::initialPage: no change" << endl);
m_timer.start(5000);
return;
- } else {
- //qDebug() << "CDBrowser::initialPage: updating";
}
m_msdescs = msdescs;
@@ -874,8 +884,8 @@
browseIn(s, m_curpath);
} else {
// Show servers list
- setHtml(QString::fromUtf8(init_server_page.c_str()));
- waitForPage();
+ QByteArray html = html_top + init_server_page_bot;
+ mySetHtml(html);
for (unsigned i = 0; i < msdescs.size(); i++) {
appendHtml("", DSToHtml(i, msdescs[i]));
}
@@ -894,16 +904,59 @@
PUP_RAND_STOP,
};
+void CDBrowser::onLoadFinished(bool)
+{
+ LOGDEB("CDBrowser::onLoadFinished\n");
+}
+
+void CDBrowser::onPopupJsDone(const QVariant &jr)
+{
+ QString qs(jr.toString());
+ LOGDEB("onPopupJsDone: parameter: " << qs2utf8s(qs) << endl);
+ QStringList lst1 = qs.split("\n", QString::SkipEmptyParts);
+ for (int i = 0 ; i < lst1.size(); i++) {
+ int eq = lst1[i].indexOf("=");
+ if (eq > 0) {
+ QString nm = lst1[i].left(eq).trimmed();
+ QString value = lst1[i].right(lst1[i].size() - (eq+1)).trimmed();
+ if (!nm.compare("objid")) {
+ m_popupobjid = qs2utf8s(value);
+ } else if (!nm.compare("title")) {
+ m_popupobjtitle = qs2utf8s(value);
+ } else if (!nm.compare("objidx")) {
+ m_popupidx = value.toInt();
+ } else if (!nm.compare("otype")) {
+ m_popupotype = qs2utf8s(value);
+ } else {
+ LOGERR("onPopupJsDone: unknown key: " << qs2utf8s(nm) << endl);
+ }
+ }
+ }
+ LOGDEB1("onPopupJsDone: class [" << m_popupotype <<
+ "] objid [" << m_popupobjid <<
+ "] title [" << m_popupobjtitle <<
+ "] objidx [" << m_popupidx << "]\n");
+ doCreatePopupMenu();
+}
+
void CDBrowser::createPopupMenu(const QPoint& pos)
{
if (!m_browsers || m_browsers->insertActive()) {
- qDebug() << "CDBrowser::createPopupMenu: no popup: insert active";
- return;
- }
- qDebug() << "CDBrowser::createPopupMenu";
-
+ LOGDEB("CDBrowser::createPopupMenu: no popup: insert active\n");
+ return;
+ }
+ LOGDEB("CDBrowser::createPopupMenu\n");
+ m_popupobjid = m_popupobjtitle = m_popupotype = "";
+ m_popupidx = -1;
+ m_popupos = pos;
+
#ifdef USING_WEBENGINE
-#warning tobedone
+ Q_UNUSED(pos);
+ QString js("window.locDetails;");
+ CDWebPage *mypage = dynamic_cast<CDWebPage*>(page());
+ QEventLoop pause;
+ connect(this, SIGNAL(sig_js_done()), &pause, SLOT(quit()));
+ mypage->runJavaScript(js, bind(&CDBrowser::onPopupJsDone, this, _1));
#else
QWebHitTestResult htr = page()->mainFrame()->hitTestContent(pos);
if (htr.isNull()) {
@@ -913,7 +966,23 @@
QWebElement el = htr.enclosingBlockElement();
while (!el.isNull() && !el.hasAttribute("objid"))
el = el.parent();
-
+ if (!el.isNull()) {
+ m_popupobjid = qs2utf8s(el.attribute("objid"));
+ m_popupobjtitle = qs2utf8s(el.toPlainText());
+ if (el.hasAttribute("objidx"))
+ m_popupidx = el.attribute("objidx").toInt();
+ else
+ m_popupidx = -1;
+ m_popupotype = qs2utf8s(el.attribute("class"));
+ LOGDEB("Popup: " << " class " << m_popupotype << " objid " <<
+ m_popupobjid << endl);
+ }
+ doCreatePopupMenu();
+#endif
+}
+
+void CDBrowser::doCreatePopupMenu()
+{
QMenu *popup = new QMenu(this);
QAction *act;
QVariant v;
@@ -926,27 +995,14 @@
}
// Click in blank area, or no playlist the only entry is Back
- if (el.isNull() || (m_browsers && !m_browsers->have_playlist())) {
- popup->connect(popup, SIGNAL(triggered(QAction *)), this,
- SLOT(back(QAction *)));
- popup->popup(mapToGlobal(pos));
- return;
- }
-
- // Clicked on some object. Dir entries inside the path have no objidx attr
- // and that's ok
- m_popupobjid = qs2utf8s(el.attribute("objid"));
- m_popupobjtitle = qs2utf8s(el.toPlainText());
- if (el.hasAttribute("objidx"))
- m_popupidx = el.attribute("objidx").toInt();
- else
- m_popupidx = -1;
-
- QString otype = el.attribute("class");
- qDebug() << "Popup: " << " class " << otype << " objid " <<
- m_popupobjid.c_str();
-
- if (m_popupidx == -1 && otype.compare("container")) {
+ if (m_popupobjid.empty() || (m_browsers && !m_browsers->have_playlist())) {
+ popup->connect(popup, SIGNAL(triggered(QAction *)),
+ this, SLOT(back(QAction *)));
+ popup->popup(mapToGlobal(m_popupos));
+ return;
+ }
+
+ if (m_popupidx == -1 && m_popupotype.compare("container")) {
// All path elements should be containers !
qDebug() << "Not container and no objidx??";
return;
@@ -957,7 +1013,7 @@
act->setData(v);
popup->addAction(act);
- if (!otype.compare("item")) {
+ if (!m_popupotype.compare("item")) {
act = new QAction(tr("Send all to playlist"), this);
v = QVariant(int(PUP_ADD_ALL));
act->setData(v);
@@ -970,7 +1026,7 @@
}
// Connect to either recursive add or simpleAdd depending on entry type.
- if (!otype.compare("container")) {
+ if (!m_popupotype.compare("container")) {
act = new QAction(tr("Open in new tab"), this);
v = QVariant(int(PUP_OPEN_IN_NEW_TAB));
act->setData(v);
@@ -988,13 +1044,13 @@
popup->connect(popup, SIGNAL(triggered(QAction *)), this,
SLOT(recursiveAdd(QAction *)));
- } else if (!otype.compare("item")) {
+ } else if (!m_popupotype.compare("item")) {
popup->connect(popup, SIGNAL(triggered(QAction *)), this,
SLOT(simpleAdd(QAction *)));
} else {
// ??
- qDebug() << "CDBrowser::createPopup: obj type neither ct nor it: " <<
- otype;
+ LOGDEB("CDBrowser::createPopup: obj type neither ct nor it: " <<
+ m_popupotype << endl);
return;
}
@@ -1006,8 +1062,7 @@
popup->addAction(act);
act->setEnabled(m_browsers->randPlayActive());
- popup->popup(mapToGlobal(pos));
-#endif
+ popup->popup(mapToGlobal(m_popupos));
}
void CDBrowser::back(QAction *)