1 /* ============================================================
2 * QuiteRSS is a open-source cross-platform RSS/Atom news feeds reader
3 * Copyright (C) 2011-2020 QuiteRSS Team <quiterssteam@gmail.com>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 * ============================================================ */
18 /* ============================================================
19 * QupZilla - WebKit based browser
20 * Copyright (C) 2010-2014 David Rosca <nowrep@gmail.com>
21 *
22 * This program is free software: you can redistribute it and/or modify
23 * it under the terms of the GNU General Public License as published by
24 * the Free Software Foundation, either version 3 of the License, or
25 * (at your option) any later version.
26 *
27 * This program is distributed in the hope that it will be useful,
28 * but WITHOUT ANY WARRANTY; without even the implied warranty of
29 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30 * GNU General Public License for more details.
31 *
32 * You should have received a copy of the GNU General Public License
33 * along with this program. If not, see <https://www.gnu.org/licenses/>.
34 * ============================================================ */
35 /* ============================================================
36 *
37 * Copyright (C) 2009 by Benjamin C. Meyer <ben@meyerhome.net>
38 * Copyright (C) 2010 by Matthieu Gicquel <matgic78@gmail.com>
39 *
40 * This program is free software; you can redistribute it and/or
41 * modify it under the terms of the GNU General Public License as
42 * published by the Free Software Foundation; either version 2 of
43 * the License or (at your option) version 3 or any later version
44 * accepted by the membership of KDE e.V. (or its successor approved
45 * by the membership of KDE e.V.), which shall act as a proxy
46 * defined in Section 14 of version 3 of the license.
47 *
48 * This program is distributed in the hope that it will be useful,
49 * but WITHOUT ANY WARRANTY; without even the implied warranty of
50 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
51 * GNU General Public License for more details.
52 *
53 * You should have received a copy of the GNU General Public License
54 * along with this program. If not, see <https://www.gnu.org/licenses/>.
55 *
56 * ============================================================ */
57 #include "clicktoflash.h"
58
59 #include "mainapplication.h"
60 #include "webpage.h"
61
62 #include <QHBoxLayout>
63 #include <QToolButton>
64 #include <QFormLayout>
65 #include <QFrame>
66 #include <QMenu>
67 #include <QTimer>
68 #include <QWebView>
69 #include <QNetworkRequest>
70 #include <QWebHitTestResult>
71
72 QUrl ClickToFlash::acceptedUrl;
73 QStringList ClickToFlash::acceptedArgNames;
74 QStringList ClickToFlash::acceptedArgValues;
75
ClickToFlash(const QUrl & pluginUrl,const QStringList & argumentNames,const QStringList & argumentValues,WebPage * parentPage,QWidget * parent)76 ClickToFlash::ClickToFlash(const QUrl &pluginUrl,
77 const QStringList &argumentNames,
78 const QStringList &argumentValues,
79 WebPage *parentPage,
80 QWidget *parent)
81 : QWidget(parent)
82 , argumentNames_(argumentNames)
83 , argumentValues_(argumentValues)
84 , url_(pluginUrl)
85 , page_(parentPage)
86 {
87 frame_ = new QFrame(this);
88 frame_->setObjectName("click2flash-frame");
89 frame_->setContentsMargins(0, 0, 0, 0);
90
91 loadButton_ = new QToolButton(this);
92 loadButton_->setObjectName("click2flash-toolbutton");
93 loadButton_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
94 loadButton_->setCursor(Qt::PointingHandCursor);
95
96 QHBoxLayout *layout1 = new QHBoxLayout(this);
97 layout1->setContentsMargins(0, 0, 0, 0);
98 layout1->addWidget(frame_);
99
100 QHBoxLayout *layout2 = new QHBoxLayout(frame_);
101 layout2->setContentsMargins(0, 0, 0, 0);
102 layout2->addWidget(loadButton_);
103
104 connect(loadButton_, SIGNAL(clicked()), this, SLOT(load()));
105 setMinimumSize(29, 29);
106
107 setContextMenuPolicy(Qt::CustomContextMenu);
108 connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(customContextMenuRequested(QPoint)));
109
110 QTimer::singleShot(0, this, SLOT(ensurePluginVisible()));
111 }
112
isAlreadyAccepted(const QUrl & url,const QStringList & argumentNames,const QStringList & argumentValues)113 bool ClickToFlash::isAlreadyAccepted(const QUrl &url,
114 const QStringList &argumentNames,
115 const QStringList &argumentValues)
116 {
117 return (url == acceptedUrl &&
118 argumentNames == acceptedArgNames &&
119 argumentValues == acceptedArgValues);
120 }
121
ensurePluginVisible()122 void ClickToFlash::ensurePluginVisible()
123 {
124 page_->scheduleAdjustPage();
125 }
126
customContextMenuRequested(const QPoint & pos)127 void ClickToFlash::customContextMenuRequested(const QPoint &pos)
128 {
129 QMenu menu;
130 menu.addAction(tr("Object blocked by ClickToFlash"));
131 menu.addSeparator();
132 menu.addAction(tr("Hide object"), this, SLOT(hideObject()));
133 menu.addAction(tr("Add '%1' to whitelist").arg(url_.host()), this, SLOT(toWhitelist()));
134 menu.actions().at(0)->setEnabled(false);
135 menu.exec(mapToGlobal(pos));
136 }
137
toWhitelist()138 void ClickToFlash::toWhitelist()
139 {
140 mainApp->c2fAddWhitelist(url_.host());
141 load();
142 }
143
hideObject()144 void ClickToFlash::hideObject()
145 {
146 findElement();
147 if (!element_.isNull()) {
148 element_.setStyleProperty("visibility", "hidden");
149 } else {
150 hide();
151 }
152 }
153
findElement()154 void ClickToFlash::findElement()
155 {
156 if (!loadButton_)
157 return;
158
159 QPoint objectPos = page_->view()->mapFromGlobal(loadButton_->mapToGlobal(loadButton_->pos()));
160 QWebFrame* objectFrame = page_->frameAt(objectPos);
161 QWebHitTestResult hitResult;
162 QWebElement hitElement;
163
164 if (objectFrame) {
165 hitResult = objectFrame->hitTestContent(objectPos);
166 hitElement = hitResult.element();
167 }
168
169 if (!hitElement.isNull() && (hitElement.tagName().compare("embed", Qt::CaseInsensitive) == 0 ||
170 hitElement.tagName().compare("object", Qt::CaseInsensitive) == 0)) {
171 element_ = hitElement;
172 return;
173 }
174
175 // HitTestResult failed, trying to find element by src
176 // attribute in elements at all frames on page (less accurate)
177
178 QList<QWebFrame*> frames;
179 frames.append(objectFrame);
180 frames.append(page_->mainFrame());
181
182 while (!frames.isEmpty()) {
183 QWebFrame* frame = frames.takeFirst();
184 if (!frame) {
185 continue;
186 }
187 QWebElement docElement = frame->documentElement();
188
189 QWebElementCollection elements;
190 elements.append(docElement.findAll(QLatin1String("embed")));
191 elements.append(docElement.findAll(QLatin1String("object")));
192
193 foreach (const QWebElement &element, elements) {
194 if (!checkElement(element) && !checkUrlOnElement(element)) {
195 continue;
196 }
197 element_ = element;
198 return;
199 }
200 frames += frame->childFrames();
201 }
202 }
203
load()204 void ClickToFlash::load()
205 {
206 findElement();
207 if (element_.isNull()) {
208 qWarning("Click2Flash: Cannot find Flash object.");
209 } else {
210 acceptedUrl = url_;
211 acceptedArgNames = argumentNames_;
212 acceptedArgValues = argumentValues_;
213
214 QString js = "var c2f_clone=this.cloneNode(true);var c2f_parentNode=this.parentNode;"
215 "var c2f_substituteElement=document.createElement(this.tagName);"
216 "c2f_substituteElement.width=this.width;c2f_substituteElement.height=this.height;"
217 "c2f_substituteElement.type=\"application/futuresplash\";"
218 "this.parentNode.replaceChild(c2f_substituteElement,this);setTimeout(function(){"
219 "c2f_parentNode.replaceChild(c2f_clone,c2f_substituteElement);},250);";
220
221 element_.evaluateJavaScript(js);
222 }
223 }
224
checkUrlOnElement(QWebElement el)225 bool ClickToFlash::checkUrlOnElement(QWebElement el)
226 {
227 QString checkString = el.attribute("src");
228 if (checkString.isEmpty()) {
229 checkString = el.attribute("data");
230 }
231 if (checkString.isEmpty()) {
232 checkString = el.attribute("value");
233 }
234
235 checkString = page_->mainFrame()->url().resolved(QUrl(checkString)).toString(QUrl::RemoveQuery);
236
237 return url_.toEncoded().contains(checkString.toUtf8());
238 }
239
checkElement(QWebElement el)240 bool ClickToFlash::checkElement(QWebElement el)
241 {
242 if (argumentNames_ == el.attributeNames()) {
243 foreach (const QString &name, argumentNames_) {
244 if (argumentValues_.indexOf(el.attribute(name)) == -1) {
245 return false;
246 }
247 }
248 return true;
249 }
250 return false;
251 }
252