File indexing completed on 2025-01-26 04:57:25
0001 /* 0002 SPDX-FileCopyrightText: 2016-2024 Laurent Montel <montel@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "webhittest.h" 0008 #include "webenginemanagescript.h" 0009 #include "webhittestresult.h" 0010 #include <QWebEnginePage> 0011 0012 using namespace WebEngineViewer; 0013 template<typename Arg, typename R, typename C> 0014 0015 struct InvokeWrapperWebhittest { 0016 R *receiver; 0017 void (C::*memberFunction)(Arg); 0018 void operator()(Arg result) 0019 { 0020 (receiver->*memberFunction)(result); 0021 } 0022 }; 0023 0024 template<typename Arg, typename R, typename C> 0025 0026 InvokeWrapperWebhittest<Arg, R, C> invoke(R *receiver, void (C::*memberFunction)(Arg)) 0027 { 0028 InvokeWrapperWebhittest<Arg, R, C> wrapper = {receiver, memberFunction}; 0029 return wrapper; 0030 } 0031 0032 class WebEngineViewer::WebHitTestPrivate 0033 { 0034 public: 0035 WebHitTestPrivate(const QPoint &pos, const QPoint &zoomedPos) 0036 : m_zoomedPos(zoomedPos) 0037 , m_pos(pos) 0038 { 0039 } 0040 0041 QPoint m_zoomedPos; 0042 QPoint m_pos; 0043 QUrl m_pageUrl; 0044 }; 0045 0046 WebHitTest::WebHitTest(QWebEnginePage *page, const QPoint &zoomedPos, const QPoint &pos, QObject *parent) 0047 : QObject(parent) 0048 , d(new WebHitTestPrivate(pos, zoomedPos)) 0049 { 0050 QString source = QStringLiteral( 0051 "(function() {" 0052 "var e = document.elementFromPoint(%1, %2);" 0053 "if (!e)" 0054 " return;" 0055 "function isMediaElement(e) {" 0056 " return e.tagName.toLowerCase() == 'audio' || e.tagName.toLowerCase() == 'video';" 0057 "}" 0058 "function isEditableElement(e) {" 0059 " if (e.isContentEditable)" 0060 " return true;" 0061 " if (e.tagName.toLowerCase() == 'input' || e.tagName.toLowerCase() == 'textarea')" 0062 " return e.getAttribute('readonly') != 'readonly';" 0063 " return false;" 0064 "}" 0065 "function isSelected(e) {" 0066 " var selection = window.getSelection();" 0067 " if (selection.type != 'Range')" 0068 " return false;" 0069 " return window.getSelection().containsNode(e, true);" 0070 "}" 0071 "function attributeStr(e, a) {" 0072 " return e.getAttribute(a) || '';" 0073 "}" 0074 "var res = {" 0075 " alternateText: e.getAttribute('alt')," 0076 " boundingRect: ''," 0077 " imageUrl: ''," 0078 " contentEditable: isEditableElement(e)," 0079 " contentSelected: isSelected(e)," 0080 " linkTitle: ''," 0081 " linkUrl: ''," 0082 " mediaUrl: ''," 0083 " tagName: e.tagName.toLowerCase()" 0084 "};" 0085 "var r = e.getBoundingClientRect();" 0086 "res.boundingRect = [r.top, r.left, r.width, r.height];" 0087 "if (e.tagName.toLowerCase() == 'img')" 0088 " res.imageUrl = attributeStr(e, 'src').trim();" 0089 "if (e.tagName.toLowerCase() == 'a') {" 0090 " res.linkTitle = e.text;" 0091 " res.linkUrl = attributeStr(e, 'href').trim();" 0092 "}" 0093 "while (e) {" 0094 " if (res.linkTitle == '' && e.tagName.toLowerCase() == 'a')" 0095 " res.linkTitle = e.text;" 0096 " if (res.linkUrl == '' && e.tagName.toLowerCase() == 'a')" 0097 " res.linkUrl = attributeStr(e, 'href').trim();" 0098 " if (res.mediaUrl == '' && isMediaElement(e)) {" 0099 " res.mediaUrl = e.currentSrc;" 0100 " res.mediaPaused = e.paused;" 0101 " res.mediaMuted = e.muted;" 0102 " }" 0103 " e = e.parentElement;" 0104 "}" 0105 "return res;" 0106 "})()"); 0107 0108 const QString &js = source.arg(d->m_zoomedPos.x()).arg(d->m_zoomedPos.y()); 0109 d->m_pageUrl = page->url(); 0110 page->runJavaScript(js, WebEngineViewer::WebEngineManageScript::scriptWordId(), invoke(this, &WebHitTest::handleHitTest)); 0111 } 0112 0113 WebHitTest::~WebHitTest() = default; 0114 0115 void WebHitTest::handleHitTest(const QVariant &result) 0116 { 0117 const WebHitTestResult webHitResult(d->m_pos, d->m_pageUrl, result); 0118 Q_EMIT finished(webHitResult); 0119 deleteLater(); 0120 } 0121 0122 #include "moc_webhittest.cpp"