Moving download history management from HistoryManager to Application class
[rekonq:nikhilms-mainline.git] / src / webpage.cpp
1 /* ============================================================
2 *
3 * This file is a part of the rekonq project
4 *
5 * Copyright (C) 2008 Benjamin C. Meyer <ben@meyerhome.net>
6 * Copyright (C) 2008 Dirk Mueller <mueller@kde.org>
7 * Copyright (C) 2008 Urs Wolfer <uwolfer @ kde.org>
8 * Copyright (C) 2008 Michael Howell <mhowell123@gmail.com>
9 * Copyright (C) 2008-2010 by Andrea Diamantini <adjam7 at gmail dot com>
10 * Copyright (C) 2010 by Matthieu Gicquel <matgic78 at gmail dot com>
11 * Copyright (C) 2009-2010 Dawit Alemayehu <adawit at kde dot org>
12 *
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License as
16 * published by the Free Software Foundation; either version 2 of
17 * the License or (at your option) version 3 or any later version
18 * accepted by the membership of KDE e.V. (or its successor approved
19 * by the membership of KDE e.V.), which shall act as a proxy
20 * defined in Section 14 of version 3 of the license.
21 *
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25 * GNU General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
29 *
30 * ============================================================ */
31
32
33 // Self Includes
34 #include "webpage.h"
35 #include "webpage.moc"
36
37 // Auto Includes
38 #include "rekonq.h"
39
40 // Local Includes
41 #include "application.h"
42 #include "mainwindow.h"
43 #include "mainview.h"
44 #include "webtab.h"
45 #include "webpluginfactory.h"
46 #include "networkaccessmanager.h"
47 #include "adblockmanager.h"
48 #include "urlbar.h"
49 //#include "websnap.h"
50
51 #include "sslinfodialog_p.h"
52
53 // KDE Includes
54 #include <KStandardDirs>
55 #include <KUrl>
56 #include <KToolInvocation>
57 #include <KProtocolManager>
58 #include <kwebwallet.h>
59
60 #include <kparts/browseropenorsavequestion.h>
61
62 #include <kio/renamedialog.h>
63
64 #include <KDE/KMimeTypeTrader>
65 #include <KDE/KRun>
66 #include <KDE/KFileDialog>
67 #include <KDE/KMessageBox>
68 #include <KDE/KJobUiDelegate>
69
70 // Qt Includes
71 #include <QtGui/QContextMenuEvent>
72 #include <QtGui/QWheelEvent>
73 #include <QtGui/QMouseEvent>
74 #include <QtGui/QClipboard>
75 #include <QtGui/QKeyEvent>
76
77 #include <QtWebKit/QWebFrame>
78
79
80 // Returns true if the scheme and domain of the two urls match...
81 static bool domainSchemeMatch(const QUrl& u1, const QUrl& u2)
82 {
83     if (u1.scheme() != u2.scheme())
84         return false;
85
86     QStringList u1List = u1.host().split(QL1C('.'), QString::SkipEmptyParts);
87     QStringList u2List = u2.host().split(QL1C('.'), QString::SkipEmptyParts);
88
89     if (qMin(u1List.count(), u2List.count()) < 2)
90         return false;  // better safe than sorry...
91
92     while (u1List.count() > 2)
93         u1List.removeFirst();
94
95     while (u2List.count() > 2)
96         u2List.removeFirst();
97
98     return (u1List == u2List);
99 }
100
101
102 // NOTE
103 // This is heavily based on the one from KdeWebKit and
104 // extended to provide the extra functionality we need:
105 // 1. KGet Integration
106 // 2. Save downloads history
107 static bool downloadResource (const KUrl& srcUrl, const KIO::MetaData& metaData = KIO::MetaData(),
108                               QWidget* parent = 0, const QString& suggestedName = QString())
109 {
110     KUrl destUrl;
111     
112     int result = KIO::R_OVERWRITE;
113     const QUrl fileName ((suggestedName.isEmpty() ? srcUrl.fileName() : suggestedName));
114
115     do 
116     {
117         destUrl = KFileDialog::getSaveFileName(fileName, QString(), parent);
118         
119         if(destUrl.isEmpty())
120             return false;
121
122         if (destUrl.isLocalFile()) 
123         {
124             QFileInfo finfo (destUrl.toLocalFile());
125             if (finfo.exists()) 
126             {
127                 QDateTime now = QDateTime::currentDateTime();
128                 KIO::RenameDialog dlg (parent, i18n("Overwrite File?"), srcUrl, destUrl,
129                                        KIO::RenameDialog_Mode(KIO::M_OVERWRITE | KIO::M_SKIP),
130                                        -1, finfo.size(),
131                                        now.toTime_t(), finfo.created().toTime_t(),
132                                        now.toTime_t(), finfo.lastModified().toTime_t());
133                 result = dlg.exec();
134             }
135         }
136     } 
137     while (result == KIO::R_CANCEL && destUrl.isValid());
138     
139     // Save download history
140     Application::instance()->addDownload(srcUrl.pathOrUrl() , destUrl.pathOrUrl());
141
142     if (ReKonfig::kgetDownload())
143     {
144         //KGet integration:
145         if (!QDBusConnection::sessionBus().interface()->isServiceRegistered("org.kde.kget"))
146         {
147             KToolInvocation::kdeinitExecWait("kget");
148         }
149         QDBusInterface kget("org.kde.kget", "/KGet", "org.kde.kget.main");
150         if (kget.isValid())
151         {
152             kget.call("addTransfer", srcUrl.prettyUrl(), destUrl.prettyUrl(), true);
153             return true;
154         }
155         return false;
156     }
157     
158     KIO::Job *job = KIO::file_copy(srcUrl, destUrl, -1, KIO::Overwrite);
159
160     if (!metaData.isEmpty())
161         job->setMetaData(metaData);
162
163     job->addMetaData(QL1S("MaxCacheSize"), QL1S("0")); // Don't store in http cache.
164     job->addMetaData(QL1S("cache"), QL1S("cache")); // Use entry from cache if available.
165     job->uiDelegate()->setAutoErrorHandlingEnabled(true);
166     return true;
167     
168 }
169
170
171 // ---------------------------------------------------------------------------------
172
173
174 WebPage::WebPage(QWidget *parent)
175         : KWebPage(parent, KWalletIntegration)
176         , _networkAnalyzer(false)
177         , _isOnRekonqPage(false)
178 {
179     // ----- handling unsupported content...
180     setForwardUnsupportedContent(true);
181     connect(this, SIGNAL(unsupportedContent(QNetworkReply *)), this, SLOT(handleUnsupportedContent(QNetworkReply *)));
182
183     // ----- rekonq Network Manager
184     NetworkAccessManager *manager = new NetworkAccessManager(this);
185     manager->setCache(0);   // disable QtWebKit cache to just use KIO one..
186
187     // set cookieJar window ID..
188     if (parent && parent->window())
189         manager->setCookieJarWindowId(parent->window()->winId());
190
191     setNetworkAccessManager(manager);
192
193     // activate ssl warnings
194     setSessionMetaData("ssl_activate_warnings", "TRUE");
195
196     // Override the 'Accept' header sent by QtWebKit which favors XML over HTML!
197     // Setting the accept meta-data to null will force kio_http to use its own
198     // default settings for this header.
199     setSessionMetaData(QL1S("accept"), QString());
200
201     // ----- Web Plugin Factory
202     setPluginFactory(new WebPluginFactory(this));
203
204     // ----- last stuffs
205     connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(manageNetworkErrors(QNetworkReply*)));
206     connect(this, SIGNAL(loadFinished(bool)), this, SLOT(loadFinished(bool)));
207
208     // protocol handler signals
209     connect(&_protHandler, SIGNAL(downloadUrl(const KUrl &)), this, SLOT(downloadUrl(const KUrl &)));
210 }
211
212
213 WebPage::~WebPage()
214 {
215     disconnect();
216 }
217
218
219 bool WebPage::acceptNavigationRequest(QWebFrame *frame, const QNetworkRequest &request, NavigationType type)
220 {
221     _isOnRekonqPage = false;
222     _loadingUrl = request.url();
223
224     KIO::AccessManager *manager = qobject_cast<KIO::AccessManager*>(networkAccessManager());
225     KIO::MetaData metaData = manager->requestMetaData();
226
227     // Get the SSL information sent, if any...
228     if (metaData.contains(QL1S("ssl_in_use")))
229     {
230         WebSslInfo info;
231         info.fromMetaData(metaData.toVariant());
232         info.setUrl(request.url());
233         _sslInfo = info;
234     }
235
236     if (frame)
237     {
238         if (_protHandler.preHandling(request, frame))
239         {
240             return false;
241         }
242
243         switch (type)
244         {
245         case QWebPage::NavigationTypeLinkClicked:
246             if (_sslInfo.isValid())
247             {
248                 setRequestMetaData("ssl_was_in_use", "TRUE");
249             }
250             break;
251
252         case QWebPage::NavigationTypeFormSubmitted:
253             break;
254
255         case QWebPage::NavigationTypeFormResubmitted:
256             if (KMessageBox::warningContinueCancel(view(),
257                                                    i18n("Are you sure you want to send your data again?"),
258                                                    i18n("Resend form data")
259                                                   )
260                     == KMessageBox::Cancel)
261             {
262                 return false;
263             }
264             break;
265
266         case QWebPage::NavigationTypeReload:
267         case QWebPage::NavigationTypeBackOrForward:
268         case QWebPage::NavigationTypeOther:
269             break;
270
271         default:
272             break;
273         }
274
275         if (frame == mainFrame())
276         {
277             setRequestMetaData("main_frame_request", "TRUE");
278         }
279         else
280         {
281             setRequestMetaData("main_frame_request", "FALSE");
282         }
283     }
284     return KWebPage::acceptNavigationRequest(frame, request, type);
285 }
286
287
288 WebPage *WebPage::createWindow(QWebPage::WebWindowType type)
289 {
290     // added to manage web modal dialogs
291     if (type == QWebPage::WebModalDialog)
292         kDebug() << "Modal Dialog";
293
294     WebTab *w = 0;
295     if (ReKonfig::openTabNoWindow())
296     {
297         w = Application::instance()->mainWindow()->mainView()->newWebTab( !ReKonfig::openTabsBack() );
298     }
299     else
300     {
301         w = Application::instance()->newMainWindow()->mainView()->currentWebTab();
302     }
303     return w->page();
304 }
305
306
307 void WebPage::handleUnsupportedContent(QNetworkReply *reply)
308 {
309     Q_ASSERT (reply);
310     // NOTE: 
311     // Until kio implements a way to resume/continue a network
312     // request. We must abort the reply to prevent a zombie process
313     // from continuing to download the unsupported content!
314     reply->abort();
315
316     // This is probably needed just in ONE stupid case..
317     if (_protHandler.postHandling(reply->request(), mainFrame()))
318         return;
319
320     if (reply->error() != QNetworkReply::NoError)
321         return;
322     
323     KUrl replyUrl = reply->url();
324
325     // HACK -------------------------------------------
326     // This is done to fix #231204 && #212808
327     
328     QString mimeType;
329     QString suggestedFileName;
330     
331     QString app = reply->header(QNetworkRequest::ContentTypeHeader).toString();
332     QStringList headerList = app.split( ';' );
333     
334     if(headerList.count() > 0)
335     {
336         mimeType = headerList.takeFirst().trimmed();
337         Q_FOREACH(const QString &head, headerList)
338         {
339             if( head.contains( QL1S("name") ) )
340             {
341                 // this is not so sure.. :)
342                 suggestedFileName = head;
343                 suggestedFileName = suggestedFileName.remove( QL1S("name=") );
344                 suggestedFileName = suggestedFileName.remove( '"' );
345                 suggestedFileName = suggestedFileName.trimmed();
346                 break;
347             }
348         }
349     }
350     else
351     {
352         mimeType = reply->header(QNetworkRequest::ContentTypeHeader).toString();
353     }
354     
355     // NOTE
356     // This part has been copied from KWebPage::downloadResponse code
357     if (reply->hasRawHeader("Content-Disposition")) 
358     {
359         KIO::MetaData metaData = reply->attribute(static_cast<QNetworkRequest::Attribute>(KIO::AccessManager::MetaData)).toMap();
360         if (metaData.value(QL1S("content-disposition-type")).compare(QL1S("attachment"), Qt::CaseInsensitive) == 0) 
361         {
362             suggestedFileName = metaData.value(QL1S("content-disposition-filename"));
363         } 
364         else 
365         {
366             const QString value = QL1S(reply->rawHeader("Content-Disposition").simplified());
367             if (value.startsWith(QL1S("attachment"), Qt::CaseInsensitive)) 
368             {
369                 const int length = value.size();
370                 int pos = value.indexOf(QL1S("filename"), 0, Qt::CaseInsensitive);
371                 if (pos > -1) 
372                 {
373                     pos += 9;
374                     while (pos < length && (value.at(pos) == QL1C(' ') || value.at(pos) == QL1C('=') || value.at(pos) == QL1C('"')))
375                         pos++;
376
377                     int endPos = pos;
378                     while (endPos < length && value.at(endPos) != QL1C('"') && value.at(endPos) != QL1C(';'))
379                         endPos++;
380
381                     if (endPos > pos) 
382                     {
383                         suggestedFileName = value.mid(pos, (endPos-pos)).trimmed();
384                     }
385                 }
386             }
387         }
388     }
389     
390     kDebug() << "Detected MimeType = " << mimeType;
391     kDebug() << "Suggested File Name = " << suggestedFileName;
392     // ------------------------------------------------
393     
394     KService::Ptr appService = KMimeTypeTrader::self()->preferredService(mimeType);
395
396     bool isLocal = replyUrl.isLocalFile();
397
398     if (appService.isNull())  // no service can handle this. We can just download it..
399     {
400         kDebug() << "no service can handle this. We can just download it..";
401
402         isLocal
403         ? KMessageBox::sorry(view(), i18n("No service can handle this file."))
404         : downloadReply(reply, suggestedFileName);
405
406         return;
407     }
408
409     if (!isLocal)
410     {
411
412         KParts::BrowserOpenOrSaveQuestion dlg(Application::instance()->mainWindow(), replyUrl, mimeType);
413         if(!suggestedFileName.isEmpty())
414             dlg.setSuggestedFileName(suggestedFileName);
415         
416         switch (dlg.askEmbedOrSave())
417         {
418         case KParts::BrowserOpenOrSaveQuestion::Save:
419             kDebug() << "user choice: no services, just download!";
420             downloadReply(reply, suggestedFileName);
421             return;
422
423         case KParts::BrowserOpenOrSaveQuestion::Cancel:
424             return;
425
426         default: // non extant case
427             break;
428         }
429     }
430
431     // case KParts::BrowserRun::Embed
432     KService::List partServices = KMimeTypeTrader::self()->query(mimeType, QL1S("KParts/ReadOnlyPart"));
433     if (partServices.count() > 0)
434     {
435         QString p = replyUrl.pathOrUrl();
436         
437         // A part can handle this. Embed it!
438         QString html;
439         html += "<html>";
440         html += "<head>";
441         html += "<title>";
442         html += p;
443         html += "</title>";
444         html += "<style type=\"text/css\">";
445         html += "* { border: 0; padding: 0; margin: 0; }";
446         html += "</style>";
447         html += "</head>";
448         html += "<body>";
449         html += "<object type=\"" + mimeType + "\" data=\"" + p + "\" width=\"100%\" height=\"100%\" />";
450         html += "</body>";
451         html += "</html>";
452
453         mainFrame()->setHtml(html);            
454         _isOnRekonqPage = true;
455         Application::instance()->mainWindow()->mainView()->urlBar()->setQUrl(replyUrl);
456         Application::instance()->mainWindow()->updateActions();
457     }
458     else
459     {
460         // No parts, just app services. Load it!
461         KRun::run(*appService, replyUrl, 0);
462     }
463
464     return;
465 }
466
467
468 void WebPage::loadFinished(bool ok)
469 {
470     Q_UNUSED(ok);
471     
472     Application::adblockManager()->applyHidingRules(this);
473
474     QStringList list = ReKonfig::walletBlackList();
475
476     // KWallet Integration
477     if (wallet()
478             && !list.contains(mainFrame()->url().toString())
479        )
480     {
481         wallet()->fillFormData(mainFrame());
482     }
483
484 /* this dead code is for try WebSnap::renderVisiblePagePreview()
485     if (ok)
486     {        
487         QPixmap preview = WebSnap::renderVisiblePagePreview(*this);
488         QString path = WebSnap::imagePathFromUrl(mainFrame()->url().toString());
489         QFile::remove(path);
490         preview.save(path);
491     }
492 */
493 }
494
495
496 void WebPage::manageNetworkErrors(QNetworkReply *reply)
497 {
498     Q_ASSERT(reply);
499
500     QWebFrame* frame = qobject_cast<QWebFrame *>(reply->request().originatingObject());
501     const bool isMainFrameRequest = (frame == mainFrame());
502
503     if (isMainFrameRequest
504             && _sslInfo.isValid()
505             && !domainSchemeMatch(reply->url(), _sslInfo.url())
506        )
507     {
508         // Reseting cached SSL info...
509         _sslInfo = WebSslInfo();
510     }
511
512     // NOTE: These are not all networkreply errors,
513     // but just that supported directly by KIO
514     switch (reply->error())
515     {
516
517     case QNetworkReply::NoError:                             // no error. Simple :)
518         if (isMainFrameRequest && !_sslInfo.isValid())
519         {
520             // Obtain and set the SSL information if any...
521             _sslInfo.fromMetaData(reply->attribute(static_cast<QNetworkRequest::Attribute>(KIO::AccessManager::MetaData)));
522             _sslInfo.setUrl(reply->url());
523         }
524         break;
525
526     case QNetworkReply::OperationCanceledError:              // operation canceled via abort() or close() calls
527         // ignore this..
528         return;
529         
530     case QNetworkReply::ContentAccessDenied:                 // access to remote content denied (similar to HTTP error 401)
531         kDebug() << "We (hopefully) are managing this through the adblock :)";
532         break;
533
534     case QNetworkReply::UnknownNetworkError:                 // unknown network-related error detected
535         _protHandler.postHandling(reply->request(), frame);
536         return;
537
538     case QNetworkReply::ConnectionRefusedError:              // remote server refused connection
539     case QNetworkReply::HostNotFoundError:                   // invalid hostname
540     case QNetworkReply::TimeoutError:                        // connection time out
541     case QNetworkReply::ProxyNotFoundError:                  // invalid proxy hostname
542     case QNetworkReply::ContentOperationNotPermittedError:   // operation requested on remote content not permitted
543     case QNetworkReply::ContentNotFoundError:                // remote content not found on server (similar to HTTP error 404)
544     case QNetworkReply::ProtocolUnknownError:                // Unknown protocol
545     case QNetworkReply::ProtocolInvalidOperationError:       // requested operation is invalid for this protocol
546
547         kDebug() << "ERROR " << reply->error() << ": " << reply->errorString();
548         if (reply->url() == _loadingUrl)
549         {
550             frame->setHtml(errorPage(reply));
551             if(isMainFrameRequest)
552             {
553             _isOnRekonqPage = true;
554             Application::instance()->mainWindow()->mainView()->urlBar()->setQUrl(_loadingUrl);
555             Application::instance()->mainWindow()->updateActions();
556             }
557         }
558         break;
559
560     default:
561         // Nothing to do here..
562         break;
563
564     }
565 }
566
567
568 QString WebPage::errorPage(QNetworkReply *reply)
569 {
570     // display "not found" page
571     QString notfoundFilePath =  KStandardDirs::locate("data", "rekonq/htmls/rekonqinfo.html");
572     QFile file(notfoundFilePath);
573
574     bool isOpened = file.open(QIODevice::ReadOnly);
575     if (!isOpened)
576     {
577         return QString("Couldn't open the rekonqinfo.html file");
578     }
579
580     QString title = i18n("Error loading: %1", reply->url().toString());
581     QString urlString = reply->url().toString(QUrl::RemoveUserInfo | QUrl::RemoveQuery);
582
583     QString iconPath = QString("file://") + KIconLoader::global()->iconPath("dialog-warning" , KIconLoader::Small);
584     iconPath.replace(QL1S("16"), QL1S("128"));
585
586     QString msg;
587     msg += "<table>";
588     msg += "<tr><td>";
589     msg += "<img src=\"" + iconPath + "\" />";
590     msg += "</td><td>";
591     msg += "<h1>" + reply->errorString() + "</h1>";
592     msg += "<h2>" + i18nc("%1=an URL, e.g.'kde.org'", "When connecting to: <b>%1</b>", urlString) + "</h2>";
593     msg += "</td></tr></table>";
594
595     msg += "<ul><li>" + i18n("Check the address for errors such as <b>ww</b>.kde.org instead of <b>www</b>.kde.org");
596     msg += "</li><li>" + i18n("If the address is correct, try to check the network connection.") + "</li><li>" ;
597     msg += i18n("If your computer or network is protected by a firewall or proxy, make sure that rekonq is permitted to access the network.");
598     msg += "</li><li>" + i18n("Of course, if rekonq does not work properly, you can always say it is a programmer error ;)");
599     msg += "</li></ul><br/><br/>";
600     msg += "<input type=\"button\" id=\"reloadButton\" onClick=\"document.location.href='" + urlString + "';\" value=\"";
601     msg += i18n("Try Again") + "\" />";
602
603     QString html = QString(QL1S(file.readAll()))
604                    .arg(title)
605                    .arg(msg)
606                    ;
607     return html;
608 }
609
610
611 void WebPage::downloadReply(const QNetworkReply *reply, const QString &suggestedFileName)
612 {
613     downloadResource( reply->url(), KIO::MetaData(), view(), suggestedFileName);
614 }
615
616
617 void WebPage::downloadRequest(const QNetworkRequest &request)
618 {
619     downloadResource(request.url(),
620                      request.attribute(static_cast<QNetworkRequest::Attribute>(KIO::AccessManager::MetaData)).toMap(),
621                      view());
622 }
623
624
625 void WebPage::downloadUrl(const KUrl &url)
626 {
627     downloadResource( url, KIO::MetaData(), view() );
628 }
629
630
631 void WebPage::downloadAllContentsWithKGet(QPoint)
632 {
633     QSet<QString> contents;
634     KUrl baseUrl(currentFrame()->url());
635     KUrl relativeUrl;
636
637     QWebElementCollection images = mainFrame()->documentElement().findAll("img");
638     foreach(const QWebElement &img, images)
639     {
640         relativeUrl.setEncodedUrl(img.attribute("src").toUtf8(), KUrl::TolerantMode);
641         contents << baseUrl.resolved(relativeUrl).toString();
642     }
643
644     QWebElementCollection links = mainFrame()->documentElement().findAll("a");
645     foreach(const QWebElement &link, links)
646     {
647         relativeUrl.setEncodedUrl(link.attribute("href").toUtf8(), KUrl::TolerantMode);
648         contents << baseUrl.resolved(relativeUrl).toString();
649     }
650
651     if (!QDBusConnection::sessionBus().interface()->isServiceRegistered("org.kde.kget"))
652     {
653         KToolInvocation::kdeinitExecWait("kget");
654     }
655     QDBusInterface kget("org.kde.kget", "/KGet", "org.kde.kget.main");
656     if (kget.isValid())
657     {
658         kget.call("importLinks", QVariant(contents.toList()));
659     }
660 }
661
662
663 void WebPage::showSSLInfo(QPoint)
664 {
665     if (_sslInfo.isValid())
666     {
667         QPointer<KSslInfoDialog> dlg = new KSslInfoDialog(view());
668         dlg->setSslInfo(_sslInfo.certificateChain(),
669                         _sslInfo.peerAddress().toString(),
670                         mainFrame()->url().host(),
671                         _sslInfo.protocol(),
672                         _sslInfo.ciphers(),
673                         _sslInfo.usedChiperBits(),
674                         _sslInfo.supportedChiperBits(),
675                         KSslInfoDialog::errorsFromString(_sslInfo.certificateErrors())
676                        );
677
678         dlg->exec();
679         delete dlg;
680
681         return;
682     }
683
684     if (mainFrame()->url().scheme() == QL1S("https"))
685     {
686         KMessageBox::error(view(),
687                            i18n("The SSL information for this site appears to be corrupt."),
688                            i18nc("Secure Sockets Layer", "SSL")
689                           );
690     }
691     else
692     {
693         KMessageBox::information(view(),
694                                  i18n("This site does not contain SSL information."),
695                                  i18nc("Secure Sockets Layer", "SSL")
696                                 );
697     }
698 }
699
700
701 void WebPage::updateImage(bool ok)
702 {
703     if (ok)
704     {
705         NewTabPage p(mainFrame());
706         p.snapFinished();
707     }
708 }