1/* This file is part of the KDE project
2 Copyright (C) 1999 David Faure <faure@kde.org>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 Boston, MA 02110-1301, USA.
18*/
19
20#include "khtml_settings.h"
21#include "khtmldefaults.h"
22
23#include <kconfig.h>
24#include <kconfiggroup.h>
25#include <kdebug.h>
26#include <kglobal.h>
27#include <kglobalsettings.h>
28#include <klocale.h>
29#include <kmessagebox.h>
30#include <khtml_filter_p.h>
31#include <kstandarddirs.h>
32#include <kjob.h>
33#include <kio/job.h>
34
35#include <QFile>
36#include <QFileInfo>
37#include <QtGui/QFontDatabase>
38#include <QByteArray>
39
40/**
41 * @internal
42 * Contains all settings which are both available globally and per-domain
43 */
44struct KPerDomainSettings {
45 bool m_bEnableJava : 1;
46 bool m_bEnableJavaScript : 1;
47 bool m_bEnablePlugins : 1;
48 // don't forget to maintain the bitfields as the enums grow
49 KHTMLSettings::KJSWindowOpenPolicy m_windowOpenPolicy : 2;
50 KHTMLSettings::KJSWindowStatusPolicy m_windowStatusPolicy : 1;
51 KHTMLSettings::KJSWindowFocusPolicy m_windowFocusPolicy : 1;
52 KHTMLSettings::KJSWindowMovePolicy m_windowMovePolicy : 1;
53 KHTMLSettings::KJSWindowResizePolicy m_windowResizePolicy : 1;
54
55#ifdef DEBUG_SETTINGS
56 void dump(const QString &infix = QString()) const {
57 kDebug() << "KPerDomainSettings " << infix << " @" << this << ":";
58 kDebug() << " m_bEnableJava: " << m_bEnableJava;
59 kDebug() << " m_bEnableJavaScript: " << m_bEnableJavaScript;
60 kDebug() << " m_bEnablePlugins: " << m_bEnablePlugins;
61 kDebug() << " m_windowOpenPolicy: " << m_windowOpenPolicy;
62 kDebug() << " m_windowStatusPolicy: " << m_windowStatusPolicy;
63 kDebug() << " m_windowFocusPolicy: " << m_windowFocusPolicy;
64 kDebug() << " m_windowMovePolicy: " << m_windowMovePolicy;
65 kDebug() << " m_windowResizePolicy: " << m_windowResizePolicy;
66 }
67#endif
68};
69
70QString *KHTMLSettings::avFamilies = 0;
71typedef QMap<QString,KPerDomainSettings> PolicyMap;
72
73// The "struct" that contains all the data. Must be copiable (no pointers).
74class KHTMLSettingsData
75{
76public:
77 bool m_bChangeCursor : 1;
78 bool m_bOpenMiddleClick : 1;
79 bool m_underlineLink : 1;
80 bool m_hoverLink : 1;
81 bool m_bEnableJavaScriptDebug : 1;
82 bool m_bEnableJavaScriptErrorReporting : 1;
83 bool enforceCharset : 1;
84 bool m_bAutoLoadImages : 1;
85 bool m_bUnfinishedImageFrame : 1;
86 bool m_formCompletionEnabled : 1;
87 bool m_autoDelayedActionsEnabled : 1;
88 bool m_jsErrorsEnabled : 1;
89 bool m_follow_system_colors : 1;
90 bool m_allowTabulation : 1;
91 bool m_autoSpellCheck : 1;
92 bool m_adFilterEnabled : 1;
93 bool m_hideAdsEnabled : 1;
94 bool m_jsPopupBlockerPassivePopup : 1;
95 bool m_accessKeysEnabled : 1;
96
97 // the virtual global "domain"
98 KPerDomainSettings global;
99
100 int m_fontSize;
101 int m_minFontSize;
102 int m_maxFormCompletionItems;
103 KHTMLSettings::KAnimationAdvice m_showAnimations;
104 KHTMLSettings::KSmoothScrollingMode m_smoothScrolling;
105 KHTMLSettings::KDNSPrefetch m_dnsPrefetch;
106
107 QString m_encoding;
108 QString m_userSheet;
109
110 QColor m_textColor;
111 QColor m_baseColor;
112 QColor m_linkColor;
113 QColor m_vLinkColor;
114
115 PolicyMap domainPolicy;
116 QStringList fonts;
117 QStringList defaultFonts;
118
119 khtml::FilterSet adBlackList;
120 khtml::FilterSet adWhiteList;
121 QList< QPair< QString, QChar > > m_fallbackAccessKeysAssignments;
122};
123
124class KHTMLSettingsPrivate : public QObject, public KHTMLSettingsData
125{
126 Q_OBJECT
127public:
128
129 void adblockFilterLoadList(const QString& filename)
130 {
131 kDebug(6000) << "Loading filter list from" << filename;
132 /** load list file and process each line */
133 QFile file(filename);
134 if (file.open(QIODevice::ReadOnly)) {
135 QTextStream ts(&file);
136 QString line = ts.readLine();
137#ifndef NDEBUG /// only count when compiled for debugging
138 int whiteCounter = 0, blackCounter = 0;
139#endif // NDEBUG
140 while (!line.isEmpty()) {
141 /** white list lines start with "@@" */
142 if (line.startsWith(QLatin1String("@@")))
143 {
144#ifndef NDEBUG
145 ++whiteCounter;
146#endif // NDEBUG
147 adWhiteList.addFilter(line);
148 }
149 else
150 {
151#ifndef NDEBUG
152 ++blackCounter;
153#endif // NDEBUG
154 adBlackList.addFilter(line);
155 }
156
157 line = ts.readLine();
158 }
159 file.close();
160
161#ifndef NDEBUG
162 kDebug(6000) << "Filter list loaded" << whiteCounter << "white list entries and" << blackCounter << "black list entries";
163#endif // NDEBUG
164 }
165 }
166
167public slots:
168 void adblockFilterResult(KJob *job)
169 {
170 KIO::StoredTransferJob *tJob = qobject_cast<KIO::StoredTransferJob*>(job);
171 Q_ASSERT(tJob);
172
173 if ( tJob->error() ) {
174 kDebug(6000) << "Failed to download" << tJob->url() << "with message:" << tJob->errorText();
175 }
176 else if ( tJob->isErrorPage() ) { // 4XX error code
177 kDebug(6000) << "Failed to fetch filter list" << tJob->url();
178 }
179 else {
180 const QByteArray byteArray = tJob->data();
181 const QString localFileName = tJob->property( "khtmlsettings_adBlock_filename" ).toString();
182
183 QFile file(localFileName);
184 if ( file.open(QFile::WriteOnly) ) {
185 bool success = file.write(byteArray) == byteArray.size();
186 file.close();
187 if ( success )
188 adblockFilterLoadList(localFileName);
189 else
190 kDebug(6000) << "Could not write" << byteArray.size() << "to file" << localFileName;
191 }
192 else
193 kDebug(6000) << "Cannot open file" << localFileName << "for filter list";
194 }
195
196 }
197};
198
199
200/** Returns a writeable per-domains settings instance for the given domain
201 * or a deep copy of the global settings if not existent.
202 */
203static KPerDomainSettings &setup_per_domain_policy(
204 KHTMLSettingsPrivate* const d,
205 const QString &domain) {
206 if (domain.isEmpty()) {
207 kWarning(6000) << "setup_per_domain_policy: domain is empty";
208 }
209 const QString ldomain = domain.toLower();
210 PolicyMap::iterator it = d->domainPolicy.find(ldomain);
211 if (it == d->domainPolicy.end()) {
212 // simply copy global domain settings (they should have been initialized
213 // by this time)
214 it = d->domainPolicy.insert(ldomain,d->global);
215 }
216 return *it;
217}
218
219
220KHTMLSettings::KJavaScriptAdvice KHTMLSettings::strToAdvice(const QString& _str)
221{
222 return static_cast<KJavaScriptAdvice>(KParts::HtmlSettingsInterface::textToJavascriptAdvice(_str));
223}
224
225const char* KHTMLSettings::adviceToStr(KJavaScriptAdvice _advice)
226{
227 return KParts::HtmlSettingsInterface::javascriptAdviceToText(static_cast<KParts::HtmlSettingsInterface::JavaScriptAdvice>(_advice));
228}
229
230
231void KHTMLSettings::splitDomainAdvice(const QString& configStr, QString &domain,
232 KJavaScriptAdvice &javaAdvice, KJavaScriptAdvice& javaScriptAdvice)
233{
234 KParts::HtmlSettingsInterface::JavaScriptAdvice jAdvice, jsAdvice;
235 KParts::HtmlSettingsInterface::splitDomainAdvice(configStr, domain, jAdvice, jsAdvice);
236 javaAdvice = static_cast<KJavaScriptAdvice>(jAdvice);
237 javaScriptAdvice = static_cast<KJavaScriptAdvice>(jsAdvice);
238}
239
240void KHTMLSettings::readDomainSettings(const KConfigGroup &config, bool reset,
241 bool global, KPerDomainSettings &pd_settings) {
242 QString jsPrefix = global ? QString()
243 : QString::fromLatin1("javascript.");
244 QString javaPrefix = global ? QString()
245 : QString::fromLatin1("java.");
246 QString pluginsPrefix = global ? QString()
247 : QString::fromLatin1("plugins.");
248
249 // The setting for Java
250 QString key = javaPrefix + QLatin1String("EnableJava");
251 if ( (global && reset) || config.hasKey( key ) )
252 pd_settings.m_bEnableJava = config.readEntry( key, false );
253 else if ( !global )
254 pd_settings.m_bEnableJava = d->global.m_bEnableJava;
255
256 // The setting for Plugins
257 key = pluginsPrefix + QLatin1String("EnablePlugins");
258 if ( (global && reset) || config.hasKey( key ) )
259 pd_settings.m_bEnablePlugins = config.readEntry( key, true );
260 else if ( !global )
261 pd_settings.m_bEnablePlugins = d->global.m_bEnablePlugins;
262
263 // The setting for JavaScript
264 key = jsPrefix + QLatin1String("EnableJavaScript");
265 if ( (global && reset) || config.hasKey( key ) )
266 pd_settings.m_bEnableJavaScript = config.readEntry( key, true );
267 else if ( !global )
268 pd_settings.m_bEnableJavaScript = d->global.m_bEnableJavaScript;
269
270 // window property policies
271 key = jsPrefix + QLatin1String("WindowOpenPolicy");
272 if ( (global && reset) || config.hasKey( key ) )
273 pd_settings.m_windowOpenPolicy = (KJSWindowOpenPolicy)
274 config.readEntry( key, uint(KJSWindowOpenSmart) );
275 else if ( !global )
276 pd_settings.m_windowOpenPolicy = d->global.m_windowOpenPolicy;
277
278 key = jsPrefix + QLatin1String("WindowMovePolicy");
279 if ( (global && reset) || config.hasKey( key ) )
280 pd_settings.m_windowMovePolicy = (KJSWindowMovePolicy)
281 config.readEntry( key, uint(KJSWindowMoveAllow) );
282 else if ( !global )
283 pd_settings.m_windowMovePolicy = d->global.m_windowMovePolicy;
284
285 key = jsPrefix + QLatin1String("WindowResizePolicy");
286 if ( (global && reset) || config.hasKey( key ) )
287 pd_settings.m_windowResizePolicy = (KJSWindowResizePolicy)
288 config.readEntry( key, uint(KJSWindowResizeAllow) );
289 else if ( !global )
290 pd_settings.m_windowResizePolicy = d->global.m_windowResizePolicy;
291
292 key = jsPrefix + QLatin1String("WindowStatusPolicy");
293 if ( (global && reset) || config.hasKey( key ) )
294 pd_settings.m_windowStatusPolicy = (KJSWindowStatusPolicy)
295 config.readEntry( key, uint(KJSWindowStatusAllow) );
296 else if ( !global )
297 pd_settings.m_windowStatusPolicy = d->global.m_windowStatusPolicy;
298
299 key = jsPrefix + QLatin1String("WindowFocusPolicy");
300 if ( (global && reset) || config.hasKey( key ) )
301 pd_settings.m_windowFocusPolicy = (KJSWindowFocusPolicy)
302 config.readEntry( key, uint(KJSWindowFocusAllow) );
303 else if ( !global )
304 pd_settings.m_windowFocusPolicy = d->global.m_windowFocusPolicy;
305
306}
307
308
309KHTMLSettings::KHTMLSettings()
310 :d (new KHTMLSettingsPrivate())
311{
312 init();
313}
314
315KHTMLSettings::KHTMLSettings(const KHTMLSettings &other)
316 :d(new KHTMLSettingsPrivate())
317{
318 KHTMLSettingsData* data = d;
319 *data = *other.d;
320}
321
322KHTMLSettings::~KHTMLSettings()
323{
324 delete d;
325}
326
327bool KHTMLSettings::changeCursor() const
328{
329 return d->m_bChangeCursor;
330}
331
332bool KHTMLSettings::underlineLink() const
333{
334 return d->m_underlineLink;
335}
336
337bool KHTMLSettings::hoverLink() const
338{
339 return d->m_hoverLink;
340}
341
342void KHTMLSettings::init()
343{
344 KConfig global( "khtmlrc", KConfig::NoGlobals );
345 init( &global, true );
346
347 KSharedConfig::Ptr local = KGlobal::config();
348 if ( !local )
349 return;
350
351 init( local.data(), false );
352}
353
354void KHTMLSettings::init( KConfig * config, bool reset )
355{
356 KConfigGroup cg( config, "MainView Settings" );
357 if (reset || cg.exists() )
358 {
359 if ( reset || cg.hasKey( "OpenMiddleClick" ) )
360 d->m_bOpenMiddleClick = cg.readEntry( "OpenMiddleClick", true );
361 }
362
363 KConfigGroup cgAccess(config,"Access Keys" );
364 if (reset || cgAccess.exists() ) {
365 d->m_accessKeysEnabled = cgAccess.readEntry( "Enabled", true );
366 }
367
368 KConfigGroup cgFilter( config, "Filter Settings" );
369
370 if (reset || cgFilter.exists() )
371 {
372 d->m_adFilterEnabled = cgFilter.readEntry("Enabled", false);
373 d->m_hideAdsEnabled = cgFilter.readEntry("Shrink", false);
374
375 d->adBlackList.clear();
376 d->adWhiteList.clear();
377
378 if (d->m_adFilterEnabled) {
379
380 /** read maximum age for filter list files, minimum is one day */
381 int htmlFilterListMaxAgeDays = cgFilter.readEntry(QString("HTMLFilterListMaxAgeDays")).toInt();
382 if (htmlFilterListMaxAgeDays < 1)
383 htmlFilterListMaxAgeDays = 1;
384
385 QMap<QString,QString> entryMap = cgFilter.entryMap();
386 QMap<QString,QString>::ConstIterator it;
387 for( it = entryMap.constBegin(); it != entryMap.constEnd(); ++it )
388 {
389 int id = -1;
390 QString name = it.key();
391 QString url = it.value();
392
393 if (name.startsWith("Filter"))
394 {
395 if (url.startsWith(QLatin1String("@@")))
396 d->adWhiteList.addFilter(url);
397 else
398 d->adBlackList.addFilter(url);
399 } else if (name.startsWith("HTMLFilterListName-") && (id = name.mid(19).toInt()) > 0)
400 {
401 /** check if entry is enabled */
402 bool filterEnabled = cgFilter.readEntry(QString("HTMLFilterListEnabled-").append(QString::number(id))) != QLatin1String("false");
403
404 /** get url for HTMLFilterList */
405 KUrl url(cgFilter.readEntry(QString("HTMLFilterListURL-").append(QString::number(id))));
406
407 if (filterEnabled && url.isValid()) {
408 /** determine where to cache HTMLFilterList file */
409 QString localFile = cgFilter.readEntry(QString("HTMLFilterListLocalFilename-").append(QString::number(id)));
410 localFile = KStandardDirs::locateLocal("data", "khtml/" + localFile);
411
412 /** determine existence and age of cache file */
413 QFileInfo fileInfo(localFile);
414
415 /** load cached file if it exists, irrespective of age */
416 if (fileInfo.exists())
417 d->adblockFilterLoadList( localFile );
418
419 /** if no cache list file exists or if it is too old ... */
420 if (!fileInfo.exists() || fileInfo.lastModified().daysTo(QDateTime::currentDateTime()) > htmlFilterListMaxAgeDays)
421 {
422 /** ... in this case, refetch list asynchronously */
423 kDebug(6000) << "Asynchronously fetching filter list from" << url << "to" << localFile;
424
425 KIO::StoredTransferJob *job = KIO::storedGet( url, KIO::Reload, KIO::HideProgressInfo );
426 QObject::connect( job, SIGNAL(result(KJob*)), d, SLOT(adblockFilterResult(KJob*)) );
427 /** for later reference, store name of cache file */
428 job->setProperty("khtmlsettings_adBlock_filename", localFile);
429 }
430 }
431 }
432 }
433 }
434
435 }
436
437 KConfigGroup cgHtml( config, "HTML Settings" );
438 if (reset || cgHtml.exists() )
439 {
440 // Fonts and colors
441 if( reset ) {
442 d->defaultFonts = QStringList();
443 d->defaultFonts.append( cgHtml.readEntry( "StandardFont", KGlobalSettings::generalFont().family() ) );
444 d->defaultFonts.append( cgHtml.readEntry( "FixedFont", KGlobalSettings::fixedFont().family() ) );
445 // Resolve generic font family names
446 const QString serifFont = QFontInfo(QFont(QLatin1String(HTML_DEFAULT_VIEW_SERIF_FONT))).family();
447 const QString sansSerifFont = QFontInfo(QFont(QLatin1String(HTML_DEFAULT_VIEW_SANSSERIF_FONT))).family();
448 const QString cursiveFont = QFontInfo(QFont(QLatin1String(HTML_DEFAULT_VIEW_CURSIVE_FONT))).family();
449 const QString fantasyFont = QFontInfo(QFont(QLatin1String(HTML_DEFAULT_VIEW_FANTASY_FONT))).family();
450 d->defaultFonts.append( cgHtml.readEntry( "SerifFont", serifFont ) );
451 d->defaultFonts.append( cgHtml.readEntry( "SansSerifFont", sansSerifFont ) );
452 d->defaultFonts.append( cgHtml.readEntry( "CursiveFont", cursiveFont ) );
453 d->defaultFonts.append( cgHtml.readEntry( "FantasyFont", fantasyFont ) );
454 d->defaultFonts.append( QString( "0" ) ); // font size adjustment
455 }
456
457 if ( reset || cgHtml.hasKey( "MinimumFontSize" ) )
458 d->m_minFontSize = cgHtml.readEntry( "MinimumFontSize", HTML_DEFAULT_MIN_FONT_SIZE );
459
460 if ( reset || cgHtml.hasKey( "MediumFontSize" ) )
461 d->m_fontSize = cgHtml.readEntry( "MediumFontSize", 12 );
462
463 d->fonts = cgHtml.readEntry( "Fonts", QStringList() );
464 const int fontsListLength = d->fonts.length();
465 // Resolve generic font family names
466 for (int i = 0; i < fontsListLength; ++i) {
467 const QString fontFamily = d->fonts.at(i);
468 if (!fontFamily.isEmpty()) {
469 d->fonts[i] = QFontInfo(QFont(fontFamily)).family();
470 //kWarning() << "Font family name:" << fontFamily << "resolved to:" << d->fonts.at(i);
471 }
472 }
473
474 if ( reset || cgHtml.hasKey( "DefaultEncoding" ) )
475 d->m_encoding = cgHtml.readEntry( "DefaultEncoding", "" );
476
477 if ( reset || cgHtml.hasKey( "EnforceDefaultCharset" ) )
478 d->enforceCharset = cgHtml.readEntry( "EnforceDefaultCharset", false );
479
480 // Behavior
481 if ( reset || cgHtml.hasKey( "ChangeCursor" ) )
482 d->m_bChangeCursor = cgHtml.readEntry( "ChangeCursor", KDE_DEFAULT_CHANGECURSOR );
483
484 if ( reset || cgHtml.hasKey("UnderlineLinks") )
485 d->m_underlineLink = cgHtml.readEntry( "UnderlineLinks", true );
486
487 if ( reset || cgHtml.hasKey( "HoverLinks" ) )
488 {
489 if ( (d->m_hoverLink = cgHtml.readEntry( "HoverLinks", false )))
490 d->m_underlineLink = false;
491 }
492
493 if ( reset || cgHtml.hasKey( "AllowTabulation" ) )
494 d->m_allowTabulation = cgHtml.readEntry( "AllowTabulation", false );
495
496 if ( reset || cgHtml.hasKey( "AutoSpellCheck" ) )
497 d->m_autoSpellCheck = cgHtml.readEntry( "AutoSpellCheck", true );
498
499 // Other
500 if ( reset || cgHtml.hasKey( "AutoLoadImages" ) )
501 d->m_bAutoLoadImages = cgHtml.readEntry( "AutoLoadImages", true );
502
503 if ( reset || cgHtml.hasKey( "UnfinishedImageFrame" ) )
504 d->m_bUnfinishedImageFrame = cgHtml.readEntry( "UnfinishedImageFrame", true );
505
506 if ( reset || cgHtml.hasKey( "ShowAnimations" ) )
507 {
508 QString value = cgHtml.readEntry( "ShowAnimations").toLower();
509 if (value == "disabled")
510 d->m_showAnimations = KAnimationDisabled;
511 else if (value == "looponce")
512 d->m_showAnimations = KAnimationLoopOnce;
513 else
514 d->m_showAnimations = KAnimationEnabled;
515 }
516
517 if ( reset || cgHtml.hasKey( "SmoothScrolling" ) )
518 {
519 QString value = cgHtml.readEntry( "SmoothScrolling", "whenefficient" ).toLower();
520 if (value == "disabled")
521 d->m_smoothScrolling = KSmoothScrollingDisabled;
522 else if (value == "whenefficient")
523 d->m_smoothScrolling = KSmoothScrollingWhenEfficient;
524 else
525 d->m_smoothScrolling = KSmoothScrollingEnabled;
526 }
527
528 if ( reset || cgHtml.hasKey( "DNSPrefetch" ) )
529 {
530 // Enabled, Disabled, OnlyWWWAndSLD
531 QString value = cgHtml.readEntry( "DNSPrefetch", "Enabled" ).toLower();
532 if (value == "enabled")
533 d->m_dnsPrefetch = KDNSPrefetchEnabled;
534 else if (value == "onlywwwandsld")
535 d->m_dnsPrefetch = KDNSPrefetchOnlyWWWAndSLD;
536 else
537 d->m_dnsPrefetch = KDNSPrefetchDisabled;
538 }
539
540 if ( cgHtml.readEntry( "UserStyleSheetEnabled", false ) == true ) {
541 if ( reset || cgHtml.hasKey( "UserStyleSheet" ) )
542 d->m_userSheet = cgHtml.readEntry( "UserStyleSheet", "" );
543 }
544
545 d->m_formCompletionEnabled = cgHtml.readEntry("FormCompletion", true);
546 d->m_maxFormCompletionItems = cgHtml.readEntry("MaxFormCompletionItems", 10);
547 d->m_autoDelayedActionsEnabled = cgHtml.readEntry ("AutoDelayedActions", true);
548 d->m_jsErrorsEnabled = cgHtml.readEntry("ReportJSErrors", true);
549 const QStringList accesskeys = cgHtml.readEntry("FallbackAccessKeysAssignments", QStringList());
550 d->m_fallbackAccessKeysAssignments.clear();
551 for( QStringList::ConstIterator it = accesskeys.begin(); it != accesskeys.end(); ++it )
552 if( (*it).length() > 2 && (*it)[ 1 ] == ':' )
553 d->m_fallbackAccessKeysAssignments.append( qMakePair( (*it).mid( 2 ), (*it)[ 0 ] ));
554 }
555
556 // Colors
557 //In which group ?????
558 if ( reset || cg.hasKey( "FollowSystemColors" ) )
559 d->m_follow_system_colors = cg.readEntry( "FollowSystemColors", false );
560
561 KConfigGroup cgGeneral( config, "General" );
562 if ( reset || cgGeneral.exists( ) )
563 {
564 if ( reset || cgGeneral.hasKey( "foreground" ) ) {
565 QColor def(HTML_DEFAULT_TXT_COLOR);
566 d->m_textColor = cgGeneral.readEntry( "foreground", def );
567 }
568
569 if ( reset || cgGeneral.hasKey( "linkColor" ) ) {
570 QColor def(HTML_DEFAULT_LNK_COLOR);
571 d->m_linkColor = cgGeneral.readEntry( "linkColor", def );
572 }
573
574 if ( reset || cgGeneral.hasKey( "visitedLinkColor" ) ) {
575 QColor def(HTML_DEFAULT_VLNK_COLOR);
576 d->m_vLinkColor = cgGeneral.readEntry( "visitedLinkColor", def);
577 }
578
579 if ( reset || cgGeneral.hasKey( "background" ) ) {
580 QColor def(HTML_DEFAULT_BASE_COLOR);
581 d->m_baseColor = cgGeneral.readEntry( "background", def);
582 }
583 }
584
585 KConfigGroup cgJava( config, "Java/JavaScript Settings" );
586 if( reset || cgJava.exists() )
587 {
588 // The global setting for JavaScript debugging
589 // This is currently always enabled by default
590 if ( reset || cgJava.hasKey( "EnableJavaScriptDebug" ) )
591 d->m_bEnableJavaScriptDebug = cgJava.readEntry( "EnableJavaScriptDebug", false );
592
593 // The global setting for JavaScript error reporting
594 if ( reset || cgJava.hasKey( "ReportJavaScriptErrors" ) )
595 d->m_bEnableJavaScriptErrorReporting = cgJava.readEntry( "ReportJavaScriptErrors", false );
596
597 // The global setting for popup block passive popup
598 if ( reset || cgJava.hasKey( "PopupBlockerPassivePopup" ) )
599 d->m_jsPopupBlockerPassivePopup = cgJava.readEntry("PopupBlockerPassivePopup", true );
600
601 // Read options from the global "domain"
602 readDomainSettings(cgJava,reset,true,d->global);
603#ifdef DEBUG_SETTINGS
604 d->global.dump("init global");
605#endif
606
607 // The domain-specific settings.
608
609 static const char *const domain_keys[] = { // always keep order of keys
610 "ECMADomains", "JavaDomains", "PluginDomains"
611 };
612 bool check_old_ecma_settings = true;
613 bool check_old_java_settings = true;
614 // merge all domains into one list
615 QMap<QString,int> domainList; // why can't Qt have a QSet?
616 for (unsigned i = 0; i < sizeof domain_keys/sizeof domain_keys[0]; ++i) {
617 if ( reset || cgJava.hasKey(domain_keys[i]) ) {
618 if (i == 0) check_old_ecma_settings = false;
619 else if (i == 1) check_old_java_settings = false;
620 const QStringList dl = cgJava.readEntry( domain_keys[i], QStringList() );
621 const QMap<QString,int>::Iterator notfound = domainList.end();
622 QStringList::ConstIterator it = dl.begin();
623 const QStringList::ConstIterator itEnd = dl.end();
624 for (; it != itEnd; ++it) {
625 const QString domain = (*it).toLower();
626 QMap<QString,int>::Iterator pos = domainList.find(domain);
627 if (pos == notfound) domainList.insert(domain,0);
628 }/*next it*/
629 }
630 }/*next i*/
631
632 if (reset)
633 d->domainPolicy.clear();
634
635 {
636 QMap<QString,int>::ConstIterator it = domainList.constBegin();
637 const QMap<QString,int>::ConstIterator itEnd = domainList.constEnd();
638 for ( ; it != itEnd; ++it)
639 {
640 const QString domain = it.key();
641 KConfigGroup cg( config, domain );
642 readDomainSettings(cg,reset,false,d->domainPolicy[domain]);
643#ifdef DEBUG_SETTINGS
644 d->domainPolicy[domain].dump("init "+domain);
645#endif
646 }
647 }
648
649 bool check_old_java = true;
650 if( ( reset || cgJava.hasKey( "JavaDomainSettings" ) )
651 && check_old_java_settings )
652 {
653 check_old_java = false;
654 const QStringList domainList = cgJava.readEntry( "JavaDomainSettings", QStringList() );
655 QStringList::ConstIterator it = domainList.constBegin();
656 const QStringList::ConstIterator itEnd = domainList.constEnd();
657 for ( ; it != itEnd; ++it)
658 {
659 QString domain;
660 KJavaScriptAdvice javaAdvice;
661 KJavaScriptAdvice javaScriptAdvice;
662 splitDomainAdvice(*it, domain, javaAdvice, javaScriptAdvice);
663 setup_per_domain_policy(d,domain).m_bEnableJava =
664 javaAdvice == KJavaScriptAccept;
665#ifdef DEBUG_SETTINGS
666 setup_per_domain_policy(d,domain).dump("JavaDomainSettings 4 "+domain);
667#endif
668 }
669 }
670
671 bool check_old_ecma = true;
672 if( ( reset || cgJava.hasKey( "ECMADomainSettings" ) )
673 && check_old_ecma_settings )
674 {
675 check_old_ecma = false;
676 const QStringList domainList = cgJava.readEntry( "ECMADomainSettings", QStringList() );
677 QStringList::ConstIterator it = domainList.constBegin();
678 const QStringList::ConstIterator itEnd = domainList.constEnd();
679 for ( ; it != itEnd; ++it)
680 {
681 QString domain;
682 KJavaScriptAdvice javaAdvice;
683 KJavaScriptAdvice javaScriptAdvice;
684 splitDomainAdvice(*it, domain, javaAdvice, javaScriptAdvice);
685 setup_per_domain_policy(d,domain).m_bEnableJavaScript =
686 javaScriptAdvice == KJavaScriptAccept;
687#ifdef DEBUG_SETTINGS
688 setup_per_domain_policy(d,domain).dump("ECMADomainSettings 4 "+domain);
689#endif
690 }
691 }
692
693 if( ( reset || cgJava.hasKey( "JavaScriptDomainAdvice" ) )
694 && ( check_old_java || check_old_ecma )
695 && ( check_old_ecma_settings || check_old_java_settings ) )
696 {
697 const QStringList domainList = cgJava.readEntry( "JavaScriptDomainAdvice", QStringList() );
698 QStringList::ConstIterator it = domainList.constBegin();
699 const QStringList::ConstIterator itEnd = domainList.constEnd();
700 for ( ; it != itEnd; ++it)
701 {
702 QString domain;
703 KJavaScriptAdvice javaAdvice;
704 KJavaScriptAdvice javaScriptAdvice;
705 splitDomainAdvice(*it, domain, javaAdvice, javaScriptAdvice);
706 if( check_old_java )
707 setup_per_domain_policy(d,domain).m_bEnableJava =
708 javaAdvice == KJavaScriptAccept;
709 if( check_old_ecma )
710 setup_per_domain_policy(d,domain).m_bEnableJavaScript =
711 javaScriptAdvice == KJavaScriptAccept;
712#ifdef DEBUG_SETTINGS
713 setup_per_domain_policy(d,domain).dump("JavaScriptDomainAdvice 4 "+domain);
714#endif
715 }
716
717 //save all the settings into the new keywords if they don't exist
718#if 0
719 if( check_old_java )
720 {
721 QStringList domainConfig;
722 PolicyMap::Iterator it;
723 for( it = d->javaDomainPolicy.begin(); it != d->javaDomainPolicy.end(); ++it )
724 {
725 QByteArray javaPolicy = adviceToStr( it.value() );
726 QByteArray javaScriptPolicy = adviceToStr( KJavaScriptDunno );
727 domainConfig.append(QString::fromLatin1("%1:%2:%3").arg(it.key()).arg(javaPolicy).arg(javaScriptPolicy));
728 }
729 cg.writeEntry( "JavaDomainSettings", domainConfig );
730 }
731
732 if( check_old_ecma )
733 {
734 QStringList domainConfig;
735 PolicyMap::Iterator it;
736 for( it = d->javaScriptDomainPolicy.begin(); it != d->javaScriptDomainPolicy.end(); ++it )
737 {
738 QByteArray javaPolicy = adviceToStr( KJavaScriptDunno );
739 QByteArray javaScriptPolicy = adviceToStr( it.value() );
740 domainConfig.append(QString::fromLatin1("%1:%2:%3").arg(it.key()).arg(javaPolicy).arg(javaScriptPolicy));
741 }
742 cg.writeEntry( "ECMADomainSettings", domainConfig );
743 }
744#endif
745 }
746 }
747}
748
749
750/** Local helper for retrieving per-domain settings.
751 *
752 * In case of doubt, the global domain is returned.
753 */
754static const KPerDomainSettings &lookup_hostname_policy(
755 const KHTMLSettingsPrivate* const d,
756 const QString& hostname)
757{
758#ifdef DEBUG_SETTINGS
759 kDebug() << "lookup_hostname_policy(" << hostname << ")";
760#endif
761 if (hostname.isEmpty()) {
762#ifdef DEBUG_SETTINGS
763 d->global.dump("global");
764#endif
765 return d->global;
766 }
767
768 const PolicyMap::const_iterator notfound = d->domainPolicy.constEnd();
769
770 // First check whether there is a perfect match.
771 PolicyMap::const_iterator it = d->domainPolicy.find(hostname);
772 if( it != notfound ) {
773#ifdef DEBUG_SETTINGS
774 kDebug() << "perfect match";
775 (*it).dump(hostname);
776#endif
777 // yes, use it (unless dunno)
778 return *it;
779 }
780
781 // Now, check for partial match. Chop host from the left until
782 // there's no dots left.
783 QString host_part = hostname;
784 int dot_idx = -1;
785 while( (dot_idx = host_part.indexOf(QChar('.'))) >= 0 ) {
786 host_part.remove(0,dot_idx);
787 it = d->domainPolicy.find(host_part);
788 Q_ASSERT(notfound == d->domainPolicy.end());
789 if( it != notfound ) {
790#ifdef DEBUG_SETTINGS
791 kDebug() << "partial match";
792 (*it).dump(host_part);
793#endif
794 return *it;
795 }
796 // assert(host_part[0] == QChar('.'));
797 host_part.remove(0,1); // Chop off the dot.
798 }
799
800 // No domain-specific entry: use global domain
801#ifdef DEBUG_SETTINGS
802 kDebug() << "no match";
803 d->global.dump("global");
804#endif
805 return d->global;
806}
807
808bool KHTMLSettings::isOpenMiddleClickEnabled()
809{
810 return d->m_bOpenMiddleClick;
811}
812
813bool KHTMLSettings::isBackRightClickEnabled()
814{
815 return false; // ## the feature moved to konqueror
816}
817
818bool KHTMLSettings::accessKeysEnabled() const
819{
820 return d->m_accessKeysEnabled;
821}
822
823bool KHTMLSettings::isAdFilterEnabled() const
824{
825 return d->m_adFilterEnabled;
826}
827
828bool KHTMLSettings::isHideAdsEnabled() const
829{
830 return d->m_hideAdsEnabled;
831}
832
833bool KHTMLSettings::isAdFiltered( const QString &url ) const
834{
835 if (d->m_adFilterEnabled)
836 {
837 if (!url.startsWith("data:"))
838 {
839 // Check the blacklist, and only if that matches, the whitelist
840 return d->adBlackList.isUrlMatched(url) && !d->adWhiteList.isUrlMatched(url);
841 }
842 }
843 return false;
844}
845
846QString KHTMLSettings::adFilteredBy( const QString &url, bool *isWhiteListed ) const
847{
848 QString m = d->adWhiteList.urlMatchedBy(url);
849 if (!m.isEmpty())
850 {
851 if (isWhiteListed != 0)
852 *isWhiteListed = true;
853 return (m);
854 }
855
856 m = d->adBlackList.urlMatchedBy(url);
857 if (!m.isEmpty())
858 {
859 if (isWhiteListed != 0)
860 *isWhiteListed = false;
861 return (m);
862 }
863
864 return (QString());
865}
866
867void KHTMLSettings::addAdFilter( const QString &url )
868{
869 KConfigGroup config = KSharedConfig::openConfig( "khtmlrc", KConfig::NoGlobals )->group( "Filter Settings" );
870
871 QRegExp rx;
872
873 // Try compiling to avoid invalid stuff. Only support the basic syntax here...
874 // ### refactor somewhat
875 if (url.length()>2 && url[0]=='/' && url[url.length()-1] == '/')
876 {
877 QString inside = url.mid(1, url.length()-2);
878 rx.setPattern(inside);
879 }
880 else
881 {
882 rx.setPatternSyntax(QRegExp::Wildcard);
883 rx.setPattern(url);
884 }
885
886 if (rx.isValid())
887 {
888 int last=config.readEntry("Count", 0);
889 QString key = "Filter-" + QString::number(last);
890 config.writeEntry(key, url);
891 config.writeEntry("Count",last+1);
892 config.sync();
893 if (url.startsWith(QLatin1String("@@")))
894 d->adWhiteList.addFilter(url);
895 else
896 d->adBlackList.addFilter(url);
897 }
898 else
899 {
900 KMessageBox::error(0,
901 rx.errorString(),
902 i18n("Filter error"));
903 }
904}
905
906bool KHTMLSettings::isJavaEnabled( const QString& hostname ) const
907{
908 return lookup_hostname_policy(d,hostname.toLower()).m_bEnableJava;
909}
910
911bool KHTMLSettings::isJavaScriptEnabled( const QString& hostname ) const
912{
913 return lookup_hostname_policy(d,hostname.toLower()).m_bEnableJavaScript;
914}
915
916bool KHTMLSettings::isJavaScriptDebugEnabled( const QString& /*hostname*/ ) const
917{
918 // debug setting is global for now, but could change in the future
919 return d->m_bEnableJavaScriptDebug;
920}
921
922bool KHTMLSettings::isJavaScriptErrorReportingEnabled( const QString& /*hostname*/ ) const
923{
924 // error reporting setting is global for now, but could change in the future
925 return d->m_bEnableJavaScriptErrorReporting;
926}
927
928bool KHTMLSettings::isPluginsEnabled( const QString& hostname ) const
929{
930 return lookup_hostname_policy(d,hostname.toLower()).m_bEnablePlugins;
931}
932
933KHTMLSettings::KJSWindowOpenPolicy KHTMLSettings::windowOpenPolicy(
934 const QString& hostname) const {
935 return lookup_hostname_policy(d,hostname.toLower()).m_windowOpenPolicy;
936}
937
938KHTMLSettings::KJSWindowMovePolicy KHTMLSettings::windowMovePolicy(
939 const QString& hostname) const {
940 return lookup_hostname_policy(d,hostname.toLower()).m_windowMovePolicy;
941}
942
943KHTMLSettings::KJSWindowResizePolicy KHTMLSettings::windowResizePolicy(
944 const QString& hostname) const {
945 return lookup_hostname_policy(d,hostname.toLower()).m_windowResizePolicy;
946}
947
948KHTMLSettings::KJSWindowStatusPolicy KHTMLSettings::windowStatusPolicy(
949 const QString& hostname) const {
950 return lookup_hostname_policy(d,hostname.toLower()).m_windowStatusPolicy;
951}
952
953KHTMLSettings::KJSWindowFocusPolicy KHTMLSettings::windowFocusPolicy(
954 const QString& hostname) const {
955 return lookup_hostname_policy(d,hostname.toLower()).m_windowFocusPolicy;
956}
957
958int KHTMLSettings::mediumFontSize() const
959{
960 return d->m_fontSize;
961}
962
963int KHTMLSettings::minFontSize() const
964{
965 return d->m_minFontSize;
966}
967
968QString KHTMLSettings::settingsToCSS() const
969{
970 // lets start with the link properties
971 QString str = "a:link {\ncolor: ";
972 str += d->m_linkColor.name();
973 str += ';';
974 if(d->m_underlineLink)
975 str += "\ntext-decoration: underline;";
976
977 if( d->m_bChangeCursor )
978 {
979 str += "\ncursor: pointer;";
980 str += "\n}\ninput[type=image] { cursor: pointer;";
981 }
982 str += "\n}\n";
983 str += "a:visited {\ncolor: ";
984 str += d->m_vLinkColor.name();
985 str += ';';
986 if(d->m_underlineLink)
987 str += "\ntext-decoration: underline;";
988
989 if( d->m_bChangeCursor )
990 str += "\ncursor: pointer;";
991 str += "\n}\n";
992
993 if(d->m_hoverLink)
994 str += "a:link:hover, a:visited:hover { text-decoration: underline; }\n";
995
996 return str;
997}
998
999const QString &KHTMLSettings::availableFamilies()
1000{
1001 if ( !avFamilies ) {
1002 avFamilies = new QString;
1003 QFontDatabase db;
1004 QStringList families = db.families();
1005 QStringList s;
1006 QRegExp foundryExp(" \\[.+\\]");
1007
1008 //remove foundry info
1009 QStringList::Iterator f = families.begin();
1010 const QStringList::Iterator fEnd = families.end();
1011
1012 for ( ; f != fEnd; ++f ) {
1013 (*f).replace( foundryExp, "");
1014 if (!s.contains(*f))
1015 s << *f;
1016 }
1017 s.sort();
1018
1019 *avFamilies = ',' + s.join(",") + ',';
1020 }
1021
1022 return *avFamilies;
1023}
1024
1025QString KHTMLSettings::lookupFont(int i) const
1026{
1027 QString font;
1028 if (d->fonts.count() > i)
1029 font = d->fonts[i];
1030 if (font.isEmpty())
1031 font = d->defaultFonts[i];
1032 return font;
1033}
1034
1035QString KHTMLSettings::stdFontName() const
1036{
1037 return lookupFont(0);
1038}
1039
1040QString KHTMLSettings::fixedFontName() const
1041{
1042 return lookupFont(1);
1043}
1044
1045QString KHTMLSettings::serifFontName() const
1046{
1047 return lookupFont(2);
1048}
1049
1050QString KHTMLSettings::sansSerifFontName() const
1051{
1052 return lookupFont(3);
1053}
1054
1055QString KHTMLSettings::cursiveFontName() const
1056{
1057 return lookupFont(4);
1058}
1059
1060QString KHTMLSettings::fantasyFontName() const
1061{
1062 return lookupFont(5);
1063}
1064
1065void KHTMLSettings::setStdFontName(const QString &n)
1066{
1067 while(d->fonts.count() <= 0)
1068 d->fonts.append(QString());
1069 d->fonts[0] = n;
1070}
1071
1072void KHTMLSettings::setFixedFontName(const QString &n)
1073{
1074 while(d->fonts.count() <= 1)
1075 d->fonts.append(QString());
1076 d->fonts[1] = n;
1077}
1078
1079QString KHTMLSettings::userStyleSheet() const
1080{
1081 return d->m_userSheet;
1082}
1083
1084bool KHTMLSettings::isFormCompletionEnabled() const
1085{
1086 return d->m_formCompletionEnabled;
1087}
1088
1089int KHTMLSettings::maxFormCompletionItems() const
1090{
1091 return d->m_maxFormCompletionItems;
1092}
1093
1094const QString &KHTMLSettings::encoding() const
1095{
1096 return d->m_encoding;
1097}
1098
1099bool KHTMLSettings::followSystemColors() const
1100{
1101 return d->m_follow_system_colors;
1102}
1103
1104const QColor& KHTMLSettings::textColor() const
1105{
1106 return d->m_textColor;
1107}
1108
1109const QColor& KHTMLSettings::baseColor() const
1110{
1111 return d->m_baseColor;
1112}
1113
1114const QColor& KHTMLSettings::linkColor() const
1115{
1116 return d->m_linkColor;
1117}
1118
1119const QColor& KHTMLSettings::vLinkColor() const
1120{
1121 return d->m_vLinkColor;
1122}
1123
1124bool KHTMLSettings::autoLoadImages() const
1125{
1126 return d->m_bAutoLoadImages;
1127}
1128
1129bool KHTMLSettings::unfinishedImageFrame() const
1130{
1131 return d->m_bUnfinishedImageFrame;
1132}
1133
1134KHTMLSettings::KAnimationAdvice KHTMLSettings::showAnimations() const
1135{
1136 return d->m_showAnimations;
1137}
1138
1139KHTMLSettings::KSmoothScrollingMode KHTMLSettings::smoothScrolling() const
1140{
1141 return d->m_smoothScrolling;
1142}
1143
1144KHTMLSettings::KDNSPrefetch KHTMLSettings::dnsPrefetch() const
1145{
1146 return d->m_dnsPrefetch;
1147}
1148
1149bool KHTMLSettings::isAutoDelayedActionsEnabled() const
1150{
1151 return d->m_autoDelayedActionsEnabled;
1152}
1153
1154bool KHTMLSettings::jsErrorsEnabled() const
1155{
1156 return d->m_jsErrorsEnabled;
1157}
1158
1159void KHTMLSettings::setJSErrorsEnabled(bool enabled)
1160{
1161 d->m_jsErrorsEnabled = enabled;
1162 // save it
1163 KConfigGroup cg( KGlobal::config(), "HTML Settings");
1164 cg.writeEntry("ReportJSErrors", enabled);
1165 cg.sync();
1166}
1167
1168bool KHTMLSettings::allowTabulation() const
1169{
1170 return d->m_allowTabulation;
1171}
1172
1173bool KHTMLSettings::autoSpellCheck() const
1174{
1175 return d->m_autoSpellCheck;
1176}
1177
1178QList< QPair< QString, QChar > > KHTMLSettings::fallbackAccessKeysAssignments() const
1179{
1180 return d->m_fallbackAccessKeysAssignments;
1181}
1182
1183void KHTMLSettings::setJSPopupBlockerPassivePopup(bool enabled)
1184{
1185 d->m_jsPopupBlockerPassivePopup = enabled;
1186 // save it
1187 KConfigGroup cg( KGlobal::config(), "Java/JavaScript Settings");
1188 cg.writeEntry("PopupBlockerPassivePopup", enabled);
1189 cg.sync();
1190}
1191
1192bool KHTMLSettings::jsPopupBlockerPassivePopup() const
1193{
1194 return d->m_jsPopupBlockerPassivePopup;
1195}
1196
1197#include "khtml_settings.moc"
1198