1// -*- mode: c++; c-basic-offset: 4 -*-
2/*
3 Copyright (c) 2008 Laurent Montel <montel@kde.org>
4 Copyright (C) 2006 Daniele Galdi <daniele.galdi@gmail.com>
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301, USA.
20*/
21
22/* Project related */
23#include "adblock.h"
24#include "adblockdialog.h"
25
26/* Kde related */
27#include <kpluginfactory.h>
28#include <kdebug.h>
29#include <kiconloader.h>
30#include <klibloader.h>
31#include <kparts/statusbarextension.h>
32#include <khtml_part.h>
33#include <khtml_settings.h>
34#include <kstatusbar.h>
35#include <kurllabel.h>
36#include <kurl.h>
37#include <kconfig.h>
38#include <kmessagebox.h>
39#include <kcmultidialog.h>
40#include <klocale.h>
41#include <KActionCollection>
42#include <KActionMenu>
43#include <KMenu>
44#include <dom/html_document.h>
45#include <dom/html_image.h>
46#include <dom/html_inline.h>
47#include <dom/html_misc.h>
48#include <dom/html_element.h>
49#include <dom/dom_doc.h>
50
51using namespace DOM;
52
53#include <qpixmap.h>
54#include <qcursor.h>
55#include <qregexp.h>
56
57
58K_PLUGIN_FACTORY( AdBlockFactory, registerPlugin< AdBlock >(); )
59K_EXPORT_PLUGIN( AdBlockFactory( "adblock" ) )
60
61AdBlock::AdBlock(QObject *parent, const QVariantList & /*args*/) :
62 Plugin(parent),
63 m_menu(0), m_elements(0)
64{
65 m_part = dynamic_cast<KHTMLPart *>(parent);
66 if(!m_part)
67 {
68 kDebug() << "couldn't get KHTMLPart";
69 return;
70 }
71 m_menu = new KActionMenu(KIcon( "preferences-web-browser-adblock" ), i18n("Adblock"),
72 actionCollection() );
73 actionCollection()->addAction( "action adblock", m_menu );
74 m_menu->setDelayed( false );
75
76 QAction *a = actionCollection()->addAction( "show_elements");
77 a->setText(i18n("Show Blockable Elements..."));
78 connect(a, SIGNAL(triggered()), this, SLOT(slotConfigure()));
79 m_menu->addAction(a);
80
81 a = actionCollection()->addAction( "configure");
82 a->setText(i18n("Configure Filters..."));
83 connect(a, SIGNAL(triggered()), this, SLOT(showKCModule()));
84 m_menu->addAction(a);
85
86 a = actionCollection()->addAction( "separator" );
87 a->setSeparator( true );
88 m_menu->addAction(a);
89
90 a = actionCollection()->addAction( "disable_for_this_page");
91 a->setText(i18n("No blocking for this page"));
92 connect(a, SIGNAL(triggered()), this, SLOT(slotDisableForThisPage()));
93 m_menu->addAction(a);
94
95 a = actionCollection()->addAction( "disable_for_this_site");
96 a->setText(i18n("No blocking for this site"));
97 connect(a, SIGNAL(triggered()), this, SLOT(slotDisableForThisSite()));
98 m_menu->addAction(a);
99
100 connect(m_part, SIGNAL(completed()), this, SLOT(initLabel()));
101}
102
103AdBlock::~AdBlock()
104{
105 KParts::StatusBarExtension *statusBarEx = KParts::StatusBarExtension::childObject(m_part);
106
107 if (statusBarEx && m_label)
108 statusBarEx->removeStatusBarItem(m_label.data());
109 delete m_label.data();
110 m_label.clear();
111 delete m_menu;
112 m_menu = 0;
113 delete m_elements;
114 m_elements = 0;
115}
116
117void AdBlock::initLabel()
118{
119 if (m_label) return;
120
121 KParts::StatusBarExtension *statusBarEx = KParts::StatusBarExtension::childObject(m_part);
122
123 if (!statusBarEx) {
124 kDebug() << "couldn't get KParts::StatusBarExtension";
125 return;
126 }
127
128 KUrlLabel* label = new KUrlLabel(statusBarEx->statusBar());
129
130 KIconLoader *loader = KIconLoader::global();
131
132 label->setFixedHeight(loader->currentSize(KIconLoader::Small));
133 label->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
134 label->setUseCursor(false);
135 label->setPixmap(loader->loadIcon("preferences-web-browser-adblock", KIconLoader::Small));
136
137 statusBarEx->addStatusBarItem(label, 0, false);
138 connect(label, SIGNAL(leftClickedUrl()), this, SLOT(slotConfigure()));
139 connect(label, SIGNAL(rightClickedUrl()), this, SLOT(contextMenu()));
140
141 m_label = label;
142}
143
144
145void AdBlock::disableForUrl(KUrl url)
146{
147 url.setQuery(QString());
148 url.setRef(QString());
149
150 KHTMLSettings *settings = const_cast<KHTMLSettings *>(m_part->settings());
151 settings->addAdFilter("@@"+url.url());
152}
153
154void AdBlock::slotDisableForThisPage()
155{
156 disableForUrl(m_part->toplevelURL().url());
157}
158
159void AdBlock::slotDisableForThisSite()
160{
161 KUrl u(m_part->toplevelURL().url());
162 u.setPath("/*");
163 disableForUrl(u);
164}
165
166
167void AdBlock::slotConfigure()
168{
169 if (!m_part->settings()->isAdFilterEnabled())
170 {
171 KMessageBox::error(0,
172 i18n("Please enable Konqueror's Adblock"),
173 i18nc("@title:window", "Adblock disabled"));
174
175 return;
176 }
177
178 m_elements = new AdElementList;
179 fillBlockableElements();
180
181 AdBlockDlg *dlg = new AdBlockDlg(m_part->widget(), m_elements, m_part);
182 connect(dlg, SIGNAL(notEmptyFilter(QString)), this, SLOT(addAdFilter(QString)));
183 connect(dlg, SIGNAL(configureFilters()), this, SLOT(showKCModule()));
184 dlg->exec();
185 delete dlg;
186}
187
188void AdBlock::showKCModule()
189{
190 KCMultiDialog* dialogue = new KCMultiDialog(m_part->widget());
191 dialogue->addModule("khtml_filter");
192 connect(dialogue, SIGNAL(cancelClicked()), dialogue, SLOT(delayedDestruct()));
193 connect(dialogue, SIGNAL(closeClicked()), dialogue, SLOT(delayedDestruct()));
194 dialogue->show();
195}
196
197void AdBlock::contextMenu()
198{
199 m_menu->menu()->exec(QCursor::pos());
200}
201
202
203
204void AdBlock::fillBlockableElements()
205{
206 fillWithHtmlTag("script", "src", i18n( "script" ));
207 fillWithHtmlTag("embed" , "src", i18n( "object" ));
208 fillWithHtmlTag("object", "src", i18n( "object" ));
209 // TODO: iframe's are not blocked by KHTML yet
210 fillWithHtmlTag("iframe", "src", i18n( "frame" ));
211 fillWithImages();
212
213 updateFilters();
214}
215
216void AdBlock::fillWithImages()
217{
218 HTMLDocument htmlDoc = m_part->htmlDocument();
219
220 HTMLCollection images = htmlDoc.images();
221
222 for (unsigned int i = 0; i < images.length(); i++)
223 {
224 HTMLImageElement image = static_cast<HTMLImageElement>( images.item(i) );
225
226 DOMString src = image.src();
227
228 QString url = htmlDoc.completeURL(src).string();
229 if (!url.isEmpty() && url != m_part->baseURL().url())
230 {
231 AdElement element(url, i18n( "image" ), "IMG", false, image);
232 if (!m_elements->contains( element ))
233 m_elements->append( element);
234 }
235 }
236}
237
238void AdBlock::fillWithHtmlTag(const DOMString &tagName,
239 const DOMString &attrName,
240 const QString &category)
241{
242 Document doc = m_part->document();
243
244 NodeList nodes = doc.getElementsByTagName(tagName);
245
246 for (unsigned int i = 0; i < nodes.length(); i++)
247 {
248 Node node = nodes.item(i);
249 Node attr = node.attributes().getNamedItem(attrName);
250
251 DOMString src = attr.nodeValue();
252 if (src.isNull()) continue;
253
254 QString url = doc.completeURL(src).string();
255 if (!url.isEmpty() && url != m_part->baseURL().url())
256 {
257 AdElement element(url, category, tagName.string().toUpper(), false, attr);
258 if (!m_elements->contains( element ))
259 m_elements->append( element);
260 }
261 }
262}
263
264void AdBlock::addAdFilter(const QString &url)
265{
266 //FIXME hackish
267 KHTMLSettings *settings = const_cast<KHTMLSettings *>(m_part->settings());
268 settings->addAdFilter(url);
269 updateFilters();
270}
271
272
273
274void AdBlock::updateFilters()
275{
276 const KHTMLSettings *settings = m_part->settings();
277
278 AdElementList::iterator it;
279 for ( it = m_elements->begin(); it != m_elements->end(); ++it )
280 {
281 AdElement &element = (*it);
282
283 bool isWhitelist;
284 QString filter = settings->adFilteredBy(element.url(), &isWhitelist);
285 if (!filter.isEmpty())
286 {
287 if (!isWhitelist)
288 {
289 element.setBlocked(true);
290 element.setBlockedBy(i18n("Blocked by %1",filter));
291 }
292 else
293 element.setBlockedBy(i18n("Allowed by %1",filter));
294 }
295 }
296}
297
298
299
300// ----------------------------------------------------------------------------
301
302AdElement::AdElement() :
303 m_blocked(false) {}
304
305AdElement::AdElement(const QString &url, const QString &category,
306 const QString &type, bool blocked, const DOM::Node&node) :
307 m_url(url), m_category(category), m_type(type), m_blocked(blocked),m_node( node ) {}
308
309AdElement &AdElement::operator=(const AdElement &obj)
310{
311 m_blocked = obj.m_blocked;
312 m_blockedBy = obj.m_blockedBy;
313 m_url = obj.m_url;
314 m_category = obj.m_category;
315 m_type = obj.m_type;
316 m_node = obj.m_node;
317
318 return *this;
319}
320
321bool AdElement::operator==(const AdElement &obj)
322{
323 return m_url == obj.m_url;
324}
325
326bool AdElement::isBlocked() const
327{
328 return m_blocked;
329}
330
331
332
333
334
335QString AdElement::blockedBy() const
336{
337 return m_blockedBy;
338}
339
340
341void AdElement::setBlockedBy(const QString &by)
342{
343 m_blockedBy = by;
344}
345
346
347
348
349
350
351void AdElement::setBlocked(bool blocked)
352{
353 m_blocked = blocked;
354}
355
356QString AdElement::url() const
357{
358 return m_url;
359}
360
361QString AdElement::category() const
362{
363 return m_category;
364}
365
366QString AdElement::type() const
367{
368 return m_type;
369}
370
371DOM::Node AdElement::node() const
372{
373 return m_node;
374}
375
376#include "adblock.moc"
377