25 #include <QApplication>
27 #include <QWebHistory>
33 #include <QDomDocument>
34 #include <QNetworkCookieJar>
35 #include <QNetworkConfiguration>
49 {
"audio/mpeg3",
"mp3",
false },
50 {
"audio/x-mpeg-3",
"mp3",
false },
51 {
"audio/mpeg",
"mp2",
false },
52 {
"audio/x-mpeg",
"mp2",
false },
53 {
"audio/ogg",
"ogg",
false },
54 {
"audio/ogg",
"oga",
false },
55 {
"audio/flac",
"flac",
false },
56 {
"audio/x-ms-wma",
"wma",
false },
57 {
"audio/wav",
"wav",
false },
58 {
"audio/x-wav",
"wav",
false },
59 {
"audio/ac3",
"ac3",
false },
60 {
"audio/x-ac3",
"ac3",
false },
61 {
"audio/x-oma",
"oma",
false },
62 {
"audio/x-realaudio",
"ra",
false },
63 {
"audio/dts",
"dts",
false },
64 {
"audio/x-dts",
"dts",
false },
65 {
"audio/aac",
"aac",
false },
66 {
"audio/x-aac",
"aac",
false },
67 {
"audio/m4a",
"m4a",
false },
68 {
"audio/x-m4a",
"m4a",
false },
69 {
"video/mpeg",
"mpg",
true },
70 {
"video/mpeg",
"mpeg",
true },
71 {
"video/x-ms-wmv",
"wmv",
true },
72 {
"video/x-ms-wmv",
"avi",
true },
73 {
"application/x-troff-msvideo",
"avi",
true },
74 {
"video/avi",
"avi",
true },
75 {
"video/msvideo",
"avi",
true },
76 {
"video/x-msvideo",
"avi",
true }
81 QNetworkReply* reply = QNetworkAccessManager::createRequest(op, req, outgoingData);
82 reply->ignoreSslErrors();
107 LOG(VB_GENERAL, LOG_DEBUG,
"Copying DLManager's Cookie Jar");
123 m_frame(nullptr), m_gotAnswer(
false)
135 QWebPage *page = view->page();
139 connect(
m_frame, &QWebFrame::javaScriptWindowObjectCleared,
this,
145 m_frame->addToJavaScriptWindowObject(QString(
"MusicPlayer"),
this);
168 MythEvent me(QString(
"MUSIC_COMMAND %1 SET_VOLUME %2")
177 MythEvent me(QString(
"MUSIC_COMMAND %1 GET_VOLUME")
186 QCoreApplication::processEvents();
187 std::this_thread::sleep_for(10ms);
198 MythEvent me(QString(
"MUSIC_COMMAND %1 PLAY_FILE '%2'")
205 MythEvent me(QString(
"MUSIC_COMMAND %1 PLAY_TRACK %2")
212 MythEvent me(QString(
"MUSIC_COMMAND %1 PLAY_URL %2")
221 MythEvent me(QString(
"MUSIC_COMMAND %1 GET_METADATA")
230 QCoreApplication::processEvents();
231 std::this_thread::sleep_for(10ms);
237 return QString(
"unknown");
248 const QString& message = me->
Message();
250 if (!message.startsWith(
"MUSIC_CONTROL"))
253 QStringList tokens = message.simplified().split(
" ");
255 if ((tokens.size() >= 4) && (tokens[1] ==
"ANSWER")
260 for (
int i = 4; i < tokens.size(); i++)
261 m_answer += QString(
" ") + tokens[i];
276 LOG(VB_GENERAL, LOG_DEBUG,
"Refreshing DLManager's Cookie Jar");
283 return extension == QWebPage::ErrorPageExtension;
289 if (
extension == QWebPage::ErrorPageExtension)
301 const auto *erroroption =
static_cast<const ErrorPageExtensionOption *
>(option);
302 ErrorPageExtensionReturn *erroroutput =
nullptr;
304 erroroutput =
static_cast<ErrorPageExtensionReturn *
>(
output);
306 QString
filename =
"htmls/notfound.html";
312 bool isOpened =
file.open(QIODevice::ReadOnly);
317 QString
title = tr(
"Error loading page: %1").arg(erroroption->url.toString());
318 QString html = QString(QLatin1String(
file.readAll()))
320 .arg(erroroption->errorString)
321 .arg(erroroption->url.toString());
324 imageBuffer.open(QBuffer::ReadWrite);
325 QIcon icon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxWarning,
327 QPixmap pixmap = icon.pixmap(QSize(32, 32));
329 if (pixmap.save(&imageBuffer,
"PNG"))
331 html.replace(QLatin1String(
"IMAGE_BINARY_DATA_HERE"),
332 QString(QLatin1String(imageBuffer.buffer().toBase64())));
335 erroroutput->content = html.toUtf8();
345 return QWebPage::userAgentForUrl(url).replace(
"Safari",
"MythBrowser");
361 connect(page(), &QWebPage::unsupportedContent,
364 connect(page(), &QWebPage::downloadRequested,
367 page()->setForwardUnsupportedContent(
true);
384 function activeElement()\
387 type = document.activeElement.type;\
390 activeElement();") };
396 .toString().toLower();
397 bool editHasFocus = (
type ==
"text" ||
type ==
"textarea" ||
405 QWebView::keyPressEvent(event);
412 bool handled =
false;
416 for (
int i = 0; i < actions.size() && !handled; i++)
418 QString
action = actions[i];
423 QKeyEvent tabKey(event->type(), Qt::Key_Tab,
424 event->modifiers(), QString(),
425 event->isAutoRepeat(), event->count());
427 QWebView::keyPressEvent(event);
430 if (
action ==
"PREVIOUSLINK")
432 QKeyEvent shiftTabKey(event->type(), Qt::Key_Tab,
433 event->modifiers() | Qt::ShiftModifier,
435 event->isAutoRepeat(), event->count());
436 *
event = shiftTabKey;
437 QWebView::keyPressEvent(event);
440 if (
action ==
"FOLLOWLINK")
442 QKeyEvent returnKey(event->type(), Qt::Key_Return,
443 event->modifiers(), QString(),
444 event->isAutoRepeat(), event->count());
446 QWebView::keyPressEvent(event);
459 if (reply->error() == QNetworkReply::NoError)
463 QVariant header = reply->header(QNetworkRequest::ContentTypeHeader);
465 if (header != QVariant())
467 LOG(VB_GENERAL, LOG_ERR,
468 QString(
"MythWebView::handleUnsupportedContent - %1")
469 .
arg(header.toString()));
492 QFileInfo fi(request.url().path());
493 QString basename(fi.completeBaseName());
494 QString extension = fi.suffix().toLower();
498 QString saveBaseName = basename;
503 saveBaseName = savefi.completeBaseName();
507 if (saveBaseName.isEmpty())
508 saveBaseName =
"unnamed_download";
511 if (extension.isEmpty())
514 if (!extension.isEmpty())
515 extension =
'.' + extension;
517 QString saveFilename = QString(
"%1%2%3")
523 if (QFile::exists(saveFilename))
529 saveFilename = QString(
"%1%2-%3%4")
532 .arg(QString::number(i++))
535 while (QFile::exists(saveFilename));
547 QString msg = tr(
"Enter filename to save file");
549 false, saveFilename);
553 input->SetReturnEvent(
this,
"filenamedialog");
563 if (saveFilename.isEmpty())
579 QString msg(tr(
"Downloading..."));
581 if (!message.isEmpty())
606 if (dce->GetResult() < 0)
609 QString resultid = dce->GetId();
610 QString resulttext = dce->GetResultText();
612 if (resultid ==
"filenamedialog")
614 else if (resultid ==
"downloadmenu")
616 if (resulttext == tr(
"Play the file"))
619 QString extension = fi.suffix();
624 MythEvent me(QString(
"MUSIC_COMMAND %1 PLAY_URL %2")
636 LOG(VB_GENERAL, LOG_ERR,
637 QString(
"MythWebView: Asked to play a file with "
638 "extension '%1' but don't know how")
642 else if (resulttext == tr(
"Download the file"))
646 else if (resulttext == tr(
"Download and play the file"))
655 auto *me =
dynamic_cast<MythEvent *
>(event);
659 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
660 QStringList tokens = me->
Message().split(
" ", QString::SkipEmptyParts);
662 QStringList tokens = me->Message().split(
" ", Qt::SkipEmptyParts);
664 if (tokens.isEmpty())
667 if (tokens[0] ==
"DOWNLOAD_FILE")
669 QStringList
args = me->ExtraDataList();
671 if (tokens[1] ==
"UPDATE")
675 else if (tokens[1] ==
"FINISHED")
677 int fileSize =
args[2].toInt();
678 int errorCode =
args[4].toInt();
683 if ((errorCode != 0) || (fileSize == 0))
698 QString extension = fi.suffix();
701 QString label = tr(
"What do you want to do with this file?");
713 menu->SetReturnEvent(
this,
"downloadmenu");
716 menu->AddButton(tr(
"Play the file"));
719 menu->AddButton(tr(
"Download and play the file"));
721 menu->AddButton(tr(
"Download the file"));
722 menu->AddButton(tr(
"Cancel"));
729 if (mimetype.isEmpty())
733 [mimetype] (
const MimeType& entry) ->
bool
734 { return mimetype == entry.m_mimeType; });
736 return it->m_extension;
743 [extension, mimetype](
const auto &entry){
746 if (!mimetype.isEmpty() &&
747 mimetype == entry.m_mimeType)
749 if (!extension.isEmpty() &&
750 extension.toLower() == entry.m_extension)
758 [extension, mimetype](
const auto &entry) {
759 if (!entry.m_isVideo)
761 if (!mimetype.isEmpty() &&
762 mimetype == entry.m_mimeType)
764 if (!extension.isEmpty() &&
765 extension.toLower() == entry.m_extension)
776 QVariant header =
m_downloadReply->header(QNetworkRequest::ContentTypeHeader);
778 if (header != QVariant())
779 mimeType = header.toString();
786 return (QWebView *)
this;
831 m_parentScreen(nullptr),
832 m_browser(nullptr), m_image(nullptr),
834 m_initialized(
false),
835 m_updateInterval(0), m_zoom(1.0),
836 m_bgColor(
"White"), m_userCssFile(
""),
837 m_defaultSaveDir(
GetConfDir() +
"/MythBrowser/"),
838 m_defaultSaveFilename(
""),
839 m_inputToggled(
false), m_lastMouseAction(
""),
841 m_horizontalScrollbar(nullptr), m_verticalScrollbar(nullptr)
882 m_browser->setPalette(QApplication::style()->standardPalette());
886 m_browser->page()->setLinkDelegationPolicy(QWebPage::DontDelegateLinks);
893 QWebFrame* frame =
m_browser->page()->currentFrame();
894 frame->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff);
903 QWebFrame* frame =
m_browser->page()->currentFrame();
904 frame->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAlwaysOff);
922 QString
filename =
"htmls/mythbrowser.css";
932 connect(
m_browser, &QWebView::loadStarted,
934 connect(
m_browser, &QWebView::loadFinished,
936 connect(
m_browser, &QWebView::loadProgress,
938 connect(
m_browser, &QWebView::titleChanged,
940 connect(
m_browser, &QWebView::iconChanged,
942 connect(
m_browser, &QWebView::statusBarMessage,
944 connect(
m_browser->page(), &QWebPage::linkHovered,
946 connect(
m_browser, &QWebView::linkClicked,
951 QObject *parentObject = parent();
960 parentObject = parentObject->parent();
964 LOG(VB_GENERAL, LOG_ERR,
965 "MythUIWebBrowser: failed to find our parent screen");
984 path +=
"/MythBrowser";
990 QWebSettings::setIconDatabasePath(path);
994 LOG(VB_GENERAL, LOG_INFO,
"MythUIWebBrowser: enabling plugins");
995 QWebSettings::globalSettings()->setAttribute(QWebSettings::PluginsEnabled,
1000 LOG(VB_GENERAL, LOG_INFO,
"MythUIWebBrowser: disabling plugins");
1001 QWebSettings::globalSettings()->setAttribute(QWebSettings::PluginsEnabled,
1007 LOG(VB_GENERAL, LOG_INFO,
"MythUIWebBrowser: disabling JavaScript");
1008 QWebSettings::globalSettings()->setAttribute(QWebSettings::JavascriptEnabled,
false);
1086 LOG(VB_GENERAL, LOG_INFO,
1087 "MythUIWebBrowser: Loading css from - " + url.toString());
1089 m_browser->page()->settings()->setUserStyleSheetUrl(url);
1103 color.setAlpha(255);
1104 QPalette palette =
m_browser->page()->palette();
1105 palette.setBrush(QPalette::Window, QBrush(color));
1106 palette.setBrush(QPalette::Base, QBrush(color));
1139 if (QGuiApplication::platformName().contains(
"egl"))
1152 if (QGuiApplication::platformName().contains(
"egl"))
1201 if (!saveDir.isEmpty())
1234 return m_browser->history()->canGoForward();
1247 return m_browser->history()->canGoBack();
1280 return QWebSettings::iconForUrl(
m_browser->url());
1317 QWebFrame *frame =
m_browser->page()->currentFrame();
1318 return frame->evaluateJavaScript(scriptSource);
1328 QPoint startPos =
m_browser->page()->currentFrame()->scrollPosition();
1329 QPoint endPos = startPos + QPoint(dx, dy);
1347 m_browser->page()->currentFrame()->setScrollPosition(endPos);
1440 QPoint position =
m_browser->page()->currentFrame()->scrollPosition();
1444 m_browser->page()->currentFrame()->contentsSize().height() -
1454 m_browser->page()->currentFrame()->contentsSize().width() -
1490 m_browser->page()->currentFrame()->scrollPosition())
1495 m_browser->page()->currentFrame()->setScrollPosition(scrollPosition);
1513 int alphaMod, QRect clipRect)
1519 area.translate(xoffset, yoffset);
1521 p->SetClipRect(clipRect);
1522 p->DrawImage(area.x(), area.y(),
m_image, alphaMod);
1533 QStringList actions;
1534 bool handled =
false;
1537 for (
int i = 0; i < actions.size() && !handled; i++)
1539 QString
action = actions[i];
1542 if (
action ==
"TOGGLEINPUT")
1562 QWebFrame *frame =
m_browser->page()->currentFrame();
1565 int pos = frame->scrollPosition().y();
1574 else if (
action ==
"DOWN")
1576 int pos = frame->scrollPosition().y();
1579 if (pos != maximum.height())
1586 else if (
action ==
"LEFT")
1588 int pos = frame->scrollPosition().x();
1597 else if (
action ==
"RIGHT")
1599 int pos = frame->scrollPosition().x();
1602 if (pos != maximum.width())
1609 else if (
action ==
"PAGEUP")
1613 else if (
action ==
"PAGEDOWN")
1617 else if (
action ==
"ZOOMIN")
1621 else if (
action ==
"ZOOMOUT")
1625 else if (
action ==
"MOUSEUP" ||
action ==
"MOUSEDOWN" ||
1627 action ==
"MOUSELEFTBUTTON")
1631 else if (
action ==
"PAGELEFT")
1635 else if (
action ==
"PAGERIGHT")
1639 else if ((
action ==
"NEXTLINK") ||
1640 (
action ==
"PREVIOUSLINK") ||
1641 (
action ==
"FOLLOWLINK"))
1645 else if (
action ==
"HISTORYBACK")
1649 else if (
action ==
"HISTORYFORWARD")
1684 QPoint curPos = QCursor::pos();
1685 QCursor::setPos(curPos.x(), curPos.y() - step);
1687 else if (
action ==
"MOUSELEFT")
1689 QPoint curPos = QCursor::pos();
1690 QCursor::setPos(curPos.x() - step, curPos.y());
1692 else if (
action ==
"MOUSERIGHT")
1694 QPoint curPos = QCursor::pos();
1695 QCursor::setPos(curPos.x() + step, curPos.y());
1697 else if (
action ==
"MOUSEDOWN")
1699 QPoint curPos = QCursor::pos();
1700 QCursor::setPos(curPos.x(), curPos.y() + step);
1702 else if (
action ==
"MOUSELEFTBUTTON")
1704 QPoint curPos = QCursor::pos();
1705 QWidget *widget = QApplication::widgetAt(curPos);
1709 curPos = widget->mapFromGlobal(curPos);
1711 auto *me =
new QMouseEvent(QEvent::MouseButtonPress, curPos,
1712 Qt::LeftButton, Qt::LeftButton,
1714 QCoreApplication::postEvent(widget, me);
1716 me =
new QMouseEvent(QEvent::MouseButtonRelease, curPos,
1717 Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
1718 QCoreApplication::postEvent(widget, me);
1742 const QString &
filename, QDomElement &element,
bool showWarnings)
1744 if (element.tagName() ==
"zoom")
1749 else if (element.tagName() ==
"url")
1753 else if (element.tagName() ==
"userstylesheet")
1757 else if (element.tagName() ==
"updateinterval")
1762 else if (element.tagName() ==
"background")
1764 m_bgColor = QColor(element.attribute(
"color",
"#ffffff"));
1765 int alpha = element.attribute(
"alpha",
"255").toInt();
1768 else if (element.tagName() ==
"browserarea")
1772 else if (element.tagName() ==
"scrollduration")
1777 else if (element.tagName() ==
"acceptsfocus")
1797 LOG(VB_GENERAL, LOG_ERR,
"ERROR, bad parsing");
1804 m_zoom = browser->m_zoom;
1820 browser->CopyFrom(
this);