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