File indexing completed on 2024-05-12 04:58:29
0001 /* ============================================================ 0002 * Falkon - Qt web browser 0003 * Copyright (C) 2015-2018 David Rosca <nowrep@gmail.com> 0004 * 0005 * This program is free software: you can redistribute it and/or modify 0006 * it under the terms of the GNU General Public License as published by 0007 * the Free Software Foundation, either version 3 of the License, or 0008 * (at your option) any later version. 0009 * 0010 * This program is distributed in the hope that it will be useful, 0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0013 * GNU General Public License for more details. 0014 * 0015 * You should have received a copy of the GNU General Public License 0016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 0017 * ============================================================ */ 0018 #include "webhittestresult.h" 0019 #include "webpage.h" 0020 0021 #include <QWebEngineContextMenuRequest> 0022 0023 WebHitTestResult::WebHitTestResult(const WebPage *page, const QPoint &pos) 0024 : m_isNull(true) 0025 , m_isContentEditable(false) 0026 , m_isContentSelected(false) 0027 , m_mediaPaused(false) 0028 , m_mediaMuted(false) 0029 , m_pos(pos) 0030 { 0031 QString source = QL1S("(function() {" 0032 "var e = document.elementFromPoint(%1, %2);" 0033 "if (!e)" 0034 " return;" 0035 "function isMediaElement(e) {" 0036 " return e.tagName.toLowerCase() == 'audio' || e.tagName.toLowerCase() == 'video';" 0037 "}" 0038 "function isEditableElement(e) {" 0039 " if (e.isContentEditable)" 0040 " return true;" 0041 " if (e.tagName.toLowerCase() == 'input' || e.tagName.toLowerCase() == 'textarea')" 0042 " return e.getAttribute('readonly') != 'readonly';" 0043 " return false;" 0044 "}" 0045 "function isSelected(e) {" 0046 " var selection = window.getSelection();" 0047 " if (selection.type != 'Range')" 0048 " return false;" 0049 " return window.getSelection().containsNode(e, true);" 0050 "}" 0051 "function attributeStr(e, a) {" 0052 " return e.getAttribute(a) || '';" 0053 "}" 0054 "var res = {" 0055 " baseUrl: document.baseURI," 0056 " alternateText: e.getAttribute('alt')," 0057 " boundingRect: ''," 0058 " imageUrl: ''," 0059 " contentEditable: isEditableElement(e)," 0060 " contentSelected: isSelected(e)," 0061 " linkTitle: ''," 0062 " linkUrl: ''," 0063 " mediaUrl: ''," 0064 " tagName: e.tagName.toLowerCase()" 0065 "};" 0066 "var r = e.getBoundingClientRect();" 0067 "res.boundingRect = [r.top, r.left, r.width, r.height];" 0068 "if (e.tagName.toLowerCase() == 'img')" 0069 " res.imageUrl = attributeStr(e, 'src').trim();" 0070 "if (e.tagName.toLowerCase() == 'a') {" 0071 " res.linkTitle = e.text;" 0072 " res.linkUrl = attributeStr(e, 'href').trim();" 0073 "}" 0074 "while (e) {" 0075 " if (res.linkTitle == '' && e.tagName.toLowerCase() == 'a')" 0076 " res.linkTitle = e.text;" 0077 " if (res.linkUrl == '' && e.tagName.toLowerCase() == 'a')" 0078 " res.linkUrl = attributeStr(e, 'href').trim();" 0079 " if (res.mediaUrl == '' && isMediaElement(e)) {" 0080 " res.mediaUrl = e.currentSrc;" 0081 " res.mediaPaused = e.paused;" 0082 " res.mediaMuted = e.muted;" 0083 " }" 0084 " e = e.parentElement;" 0085 "}" 0086 "return res;" 0087 "})()"); 0088 0089 auto *p = const_cast<WebPage*>(page); 0090 m_viewportPos = p->mapToViewport(m_pos); 0091 const QString &js = source.arg(m_viewportPos.x()).arg(m_viewportPos.y()); 0092 init(p->url(), p->execJavaScript(js, WebPage::SafeJsWorld).toMap()); 0093 } 0094 0095 void WebHitTestResult::updateWithContextMenuData(const QWebEngineContextMenuRequest &data) 0096 { 0097 if (data.position() != m_pos) { 0098 return; 0099 } 0100 0101 m_linkTitle = data.linkText(); 0102 m_linkUrl = data.linkUrl(); 0103 m_isContentEditable = data.isContentEditable(); 0104 m_isContentSelected = !data.selectedText().isEmpty(); 0105 0106 switch (data.mediaType()) { 0107 case QWebEngineContextMenuRequest::MediaTypeImage: 0108 m_imageUrl = data.mediaUrl(); 0109 break; 0110 0111 case QWebEngineContextMenuRequest::MediaTypeVideo: 0112 case QWebEngineContextMenuRequest::MediaTypeAudio: 0113 m_mediaUrl = data.mediaUrl(); 0114 break; 0115 0116 default: 0117 break; 0118 } 0119 } 0120 0121 QUrl WebHitTestResult::baseUrl() const 0122 { 0123 return m_baseUrl; 0124 } 0125 0126 QString WebHitTestResult::alternateText() const 0127 { 0128 return m_alternateText; 0129 } 0130 0131 QRect WebHitTestResult::boundingRect() const 0132 { 0133 return m_boundingRect; 0134 } 0135 0136 QUrl WebHitTestResult::imageUrl() const 0137 { 0138 return m_imageUrl; 0139 } 0140 0141 bool WebHitTestResult::isContentEditable() const 0142 { 0143 return m_isContentEditable; 0144 } 0145 0146 bool WebHitTestResult::isContentSelected() const 0147 { 0148 return m_isContentSelected; 0149 } 0150 0151 bool WebHitTestResult::isNull() const 0152 { 0153 return m_isNull; 0154 } 0155 0156 QString WebHitTestResult::linkTitle() const 0157 { 0158 return m_linkTitle; 0159 } 0160 0161 QUrl WebHitTestResult::linkUrl() const 0162 { 0163 return m_linkUrl; 0164 } 0165 0166 QUrl WebHitTestResult::mediaUrl() const 0167 { 0168 return m_mediaUrl; 0169 } 0170 0171 bool WebHitTestResult::mediaPaused() const 0172 { 0173 return m_mediaPaused; 0174 } 0175 0176 bool WebHitTestResult::mediaMuted() const 0177 { 0178 return m_mediaMuted; 0179 } 0180 0181 QPoint WebHitTestResult::pos() const 0182 { 0183 return m_pos; 0184 } 0185 0186 QPointF WebHitTestResult::viewportPos() const 0187 { 0188 return m_viewportPos; 0189 } 0190 0191 QString WebHitTestResult::tagName() const 0192 { 0193 return m_tagName; 0194 } 0195 0196 void WebHitTestResult::init(const QUrl &url, const QVariantMap &map) 0197 { 0198 if (map.isEmpty()) 0199 return; 0200 0201 m_isNull = false; 0202 m_baseUrl = map.value(QSL("baseUrl")).toUrl(); 0203 m_alternateText = map.value(QSL("alternateText")).toString(); 0204 m_imageUrl = map.value(QSL("imageUrl")).toUrl(); 0205 m_isContentEditable = map.value(QSL("contentEditable")).toBool(); 0206 m_isContentSelected = map.value(QSL("contentSelected")).toBool(); 0207 m_linkTitle = map.value(QSL("linkTitle")).toString(); 0208 m_linkUrl = map.value(QSL("linkUrl")).toUrl(); 0209 m_mediaUrl = map.value(QSL("mediaUrl")).toUrl(); 0210 m_mediaPaused = map.value(QSL("mediaPaused")).toBool(); 0211 m_mediaMuted = map.value(QSL("mediaMuted")).toBool(); 0212 m_tagName = map.value(QSL("tagName")).toString(); 0213 0214 const QVariantList &rect = map.value(QSL("boundingRect")).toList(); 0215 if (rect.size() == 4) 0216 m_boundingRect = QRect(rect.at(0).toInt(), rect.at(1).toInt(), rect.at(2).toInt(), rect.at(3).toInt()); 0217 0218 if (!m_imageUrl.isEmpty()) 0219 m_imageUrl = url.resolved(m_imageUrl); 0220 if (!m_linkUrl.isEmpty()) 0221 m_linkUrl = m_baseUrl.resolved(m_linkUrl); 0222 if (!m_mediaUrl.isEmpty()) 0223 m_mediaUrl = url.resolved(m_mediaUrl); 0224 }