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 | |
51 | using namespace DOM; |
52 | |
53 | #include <qpixmap.h> |
54 | #include <qcursor.h> |
55 | #include <qregexp.h> |
56 | |
57 | |
58 | K_PLUGIN_FACTORY( AdBlockFactory, registerPlugin< AdBlock >(); ) |
59 | K_EXPORT_PLUGIN( AdBlockFactory( "adblock" ) ) |
60 | |
61 | AdBlock::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 | |
103 | AdBlock::~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 | |
117 | void 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 | |
145 | void 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 | |
154 | void AdBlock::slotDisableForThisPage() |
155 | { |
156 | disableForUrl(m_part->toplevelURL().url()); |
157 | } |
158 | |
159 | void AdBlock::slotDisableForThisSite() |
160 | { |
161 | KUrl u(m_part->toplevelURL().url()); |
162 | u.setPath("/*" ); |
163 | disableForUrl(u); |
164 | } |
165 | |
166 | |
167 | void 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 | |
188 | void 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 | |
197 | void AdBlock::() |
198 | { |
199 | m_menu->menu()->exec(QCursor::pos()); |
200 | } |
201 | |
202 | |
203 | |
204 | void 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 | |
216 | void 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 | |
238 | void 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 | |
264 | void 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 | |
274 | void 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 | |
302 | AdElement::AdElement() : |
303 | m_blocked(false) {} |
304 | |
305 | AdElement::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 | |
309 | AdElement &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 | |
321 | bool AdElement::operator==(const AdElement &obj) |
322 | { |
323 | return m_url == obj.m_url; |
324 | } |
325 | |
326 | bool AdElement::isBlocked() const |
327 | { |
328 | return m_blocked; |
329 | } |
330 | |
331 | |
332 | |
333 | |
334 | |
335 | QString AdElement::blockedBy() const |
336 | { |
337 | return m_blockedBy; |
338 | } |
339 | |
340 | |
341 | void AdElement::setBlockedBy(const QString &by) |
342 | { |
343 | m_blockedBy = by; |
344 | } |
345 | |
346 | |
347 | |
348 | |
349 | |
350 | |
351 | void AdElement::setBlocked(bool blocked) |
352 | { |
353 | m_blocked = blocked; |
354 | } |
355 | |
356 | QString AdElement::url() const |
357 | { |
358 | return m_url; |
359 | } |
360 | |
361 | QString AdElement::category() const |
362 | { |
363 | return m_category; |
364 | } |
365 | |
366 | QString AdElement::type() const |
367 | { |
368 | return m_type; |
369 | } |
370 | |
371 | DOM::Node AdElement::node() const |
372 | { |
373 | return m_node; |
374 | } |
375 | |
376 | #include "adblock.moc" |
377 | |