File indexing completed on 2024-05-19 05:01:20

0001 /*
0002     This file is part of the KDE project.
0003 
0004     SPDX-FileCopyrightText: 2008 Dirk Mueller <mueller@kde.org>
0005     SPDX-FileCopyrightText: 2008-2010 Urs Wolfer <uwolfer @ kde.org>
0006     SPDX-FileCopyrightText: 2009 Dawit Alemayehu <adawit@kde.org>
0007 
0008     SPDX-License-Identifier: LGPL-2.1-or-later
0009 */
0010 
0011 #include "webenginepage.h"
0012 
0013 #include "webenginepart.h"
0014 #include "webengineview.h"
0015 #include "settings/webenginesettings.h"
0016 #include "webenginepartdownloadmanager.h"
0017 #include "webenginewallet.h"
0018 #include <webenginepart_debug.h>
0019 #include "webenginepartcontrols.h"
0020 #include "navigationrecorder.h"
0021 #include "profile.h"
0022 #include "webenginepart_ext.h"
0023 #include "qtwebengine6compat.h"
0024 
0025 #include "libkonq_utils.h"
0026 #include "interfaces/browser.h"
0027 
0028 #include <QWebEngineCertificateError>
0029 #include <QWebEngineSettings>
0030 #include <QWebEngineProfile>
0031 #include <KDialogJobUiDelegate>
0032 #include <QWebEngineView>
0033 
0034 #include <KMessageBox>
0035 #include <KLocalizedString>
0036 #include <KShell>
0037 #include <KAuthorized>
0038 #include <KStringHandler>
0039 #include <KUrlAuthorized>
0040 #include <KSharedConfig>
0041 #include <KIO/AuthInfo>
0042 #include <KIO/Job>
0043 #include <KIO/CommandLauncherJob>
0044 #include <KJobTrackerInterface>
0045 #include <KUserTimestamp>
0046 #include <KPasswdServerClient>
0047 #include <KJobWidgets>
0048 #include <KPluginMetaData>
0049 #include <KIO/JobUiDelegateFactory>
0050 
0051 #include <QStandardPaths>
0052 #include <QScreen>
0053 #include <QFileDialog>
0054 #include <QDialogButtonBox>
0055 #include <QMimeDatabase>
0056 
0057 #include <QFile>
0058 #include <QAuthenticator>
0059 #include <QApplication>
0060 #include <QNetworkReply>
0061 #include <QTimer>
0062 #include <QWebEngineHistory>
0063 #include <QWebEngineHistoryItem>
0064 #include <QUrlQuery>
0065 #include <KConfigGroup>
0066 #include <KToggleFullScreenAction>
0067 //#include <QWebSecurityOrigin>
0068 #include "utils.h"
0069 #include "htmlextension.h"
0070 
0071 using namespace KonqInterfaces;
0072 
0073 WebEnginePage::WebEnginePage(WebEnginePart *part, QWidget *parent)
0074     : QWebEnginePage(KonqWebEnginePart::Profile::defaultProfile(), parent),
0075         m_kioErrorCode(0),
0076         m_ignoreError(false),
0077         m_part(part),
0078         m_passwdServerClient(new KPasswdServerClient)
0079 #ifndef REMOTE_DND_NOT_HANDLED_BY_WEBENGINE
0080         , m_dropOperationTimer(new QTimer(this))
0081 #endif
0082 {
0083     if (view()) {
0084         WebEngineSettings::self()->computeFontSizes(view()->logicalDpiY());
0085     }
0086 
0087     //setForwardUnsupportedContent(true);
0088 
0089     connect(this, &QWebEnginePage::geometryChangeRequested,
0090             this, &WebEnginePage::slotGeometryChangeRequested);
0091     //    connect(this, SIGNAL(unsupportedContent(QNetworkReply*)),
0092     //            this, SLOT(slotUnsupportedContent(QNetworkReply*)));
0093     connect(this, &QWebEnginePage::featurePermissionRequested,
0094             this, &WebEnginePage::slotFeaturePermissionRequested);
0095     connect(this, &QWebEnginePage::loadFinished,
0096             this, &WebEnginePage::slotLoadFinished);
0097     connect(this, &QWebEnginePage::authenticationRequired,
0098             this, &WebEnginePage::slotAuthenticationRequired);
0099     connect(this, &QWebEnginePage::fullScreenRequested, this, &WebEnginePage::changeFullScreenMode);
0100     connect(this, &QWebEnginePage::recommendedStateChanged, this, &WebEnginePage::changeLifecycleState);
0101 
0102 #ifndef REMOTE_DND_NOT_HANDLED_BY_WEBENGINE
0103     connect(this, &QWebEnginePage::loadStarted, this, [this](){m_dropOperationTimer->stop();});
0104     m_dropOperationTimer->setSingleShot(true);
0105 #endif
0106 
0107 #if QT_VERSION_MAJOR == 6
0108     connect(this, &QWebEnginePage::certificateError, this, &WebEnginePage::handleCertificateError);
0109 #endif
0110 
0111 //If this part is displaying the developer tools for another part, inform the other page it's not displaying the developer tools anymore.
0112 //I'm not sure this is needed, but I think it's better to do it, just to be on the safe side
0113     auto unsetInspectedPageIfNeeded = [this](bool ok) {
0114         if (ok && inspectedPage() && url().scheme() != QLatin1String("devtools")) {
0115             setInspectedPage(nullptr);
0116         }
0117     };
0118     connect(this, &QWebEnginePage::loadFinished, this, unsetInspectedPageIfNeeded);
0119 
0120     WebEnginePartControls::self()->navigationRecorder()->registerPage(this);
0121     m_part->downloadManager()->addPage(this);
0122 
0123     connect(WebEnginePartControls::self(), &WebEnginePartControls::updateStyleSheet, this, &WebEnginePage::updateUserStyleSheet);
0124 }
0125 
0126 WebEnginePage::~WebEnginePage()
0127 {
0128 //qCDebug(WEBENGINEPART_LOG) << this;
0129 }
0130 
0131 const WebSslInfo& WebEnginePage::sslInfo() const
0132 {
0133     return m_sslInfo;
0134 }
0135 
0136 #if QT_VERSION_MAJOR > 5
0137 QWidget *WebEnginePage::view() const
0138 {
0139     return QWebEngineView::forPage(this);
0140 }
0141 #endif
0142 
0143 void WebEnginePage::setSslInfo (const WebSslInfo& info)
0144 {
0145     m_sslInfo = info;
0146 }
0147 
0148 static QString checkForDownloadManager(QWidget* widget)
0149 {
0150     KConfigGroup cfg (KSharedConfig::openConfig(QStringLiteral("konquerorrc"), KConfig::NoGlobals), "HTML Settings");
0151     const QString fileName (cfg.readPathEntry("DownloadManager", QString()));
0152     if (fileName.isEmpty()) {
0153         return QString();
0154     }
0155 
0156     const QString exeName = QStandardPaths::findExecutable(fileName);
0157     if (exeName.isEmpty()) {
0158         KMessageBox::detailedError(widget,
0159                                     i18n("The download manager (%1) could not be found in your installation.", fileName),
0160                                     i18n("Try to reinstall it and make sure that it is available in $PATH. \n\nThe integration will be disabled."));
0161         cfg.writePathEntry("DownloadManager", QString());
0162         cfg.sync();
0163         return QString();
0164     }
0165     return exeName;
0166 }
0167 
0168 bool WebEnginePage::downloadWithExternalDonwloadManager(const QUrl &url)
0169 {
0170     if (url.isLocalFile()) {
0171         return false;
0172     }
0173 
0174     KConfigGroup cfg (KSharedConfig::openConfig(QStringLiteral("konquerorrc"), KConfig::NoGlobals), "HTML Settings");
0175     const QString fileName (cfg.readPathEntry("DownloadManager", QString()));
0176     if (fileName.isEmpty()) {
0177         return false;
0178     }
0179 
0180     const QString managerExe = QStandardPaths::findExecutable(fileName);
0181     if (managerExe.isEmpty()) {
0182         KMessageBox::detailedError(view(),
0183                                     i18n("The download manager (%1) could not be found in your installation.", fileName),
0184                                     i18n("Try to reinstall it and make sure that it is available in $PATH. \n\nThe integration will be disabled."));
0185         cfg.writePathEntry("DownloadManager", QString());
0186         cfg.sync();
0187         return false;
0188     }
0189 
0190     //qCDebug(WEBENGINEPART_LOG) << "Calling command" << cmd;
0191     KIO::CommandLauncherJob *job = new KIO::CommandLauncherJob(managerExe, {url.toString()});
0192     job->setUiDelegate(new KDialogJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, view()));
0193     job->start();
0194     return true;
0195 }
0196 
0197 void WebEnginePage::requestDownload(QWebEngineDownloadRequest *item, bool newWindow, WebEnginePartDownloadManager::DownloadObjective objective)
0198 {
0199     QUrl url = item->url();
0200     if (downloadWithExternalDonwloadManager(url)) {
0201         item->cancel();
0202         item->deleteLater();
0203         return;
0204     }
0205 
0206     WebEngineDownloaderExtension *downloader = m_part->downloader();
0207     Q_ASSERT(downloader);
0208 
0209     downloader->addDownloadRequest(item);
0210 
0211     BrowserArguments bArgs;
0212     bArgs.setForcesNewWindow(newWindow);
0213     KParts::OpenUrlArguments args;
0214 
0215     QMimeDatabase db;
0216     QMimeType mime = db.mimeTypeForName(item->mimeType());
0217     if (!mime.isValid() || mime.isDefault()) {
0218         mime = db.mimeTypeForFile(item->suggestedFileName(), QMimeDatabase::MatchExtension);
0219     }
0220     args.setMimeType(mime.name());
0221 
0222     args.metaData().insert(QStringLiteral("DontSendToDefaultHTMLPart"), QString());
0223     args.metaData().insert(QStringLiteral("SuggestedFileName"), item->suggestedFileName());
0224 
0225     if (objective == WebEnginePartDownloadManager::DownloadObjective::SaveOnly) {
0226         args.metaData().insert(QStringLiteral("EmbedOrNothing"), {});
0227         bArgs.setForcesNewWindow(true);
0228         saveUrlToDiskAndDisplay(item, args, bArgs);
0229         return;
0230     } else if (objective == WebEnginePartDownloadManager::DownloadObjective::SaveAs) {
0231         saveAs(item);
0232     } else {
0233     //TODO KF6: this lamba only exists to reduce code duplication in the two #ifdef
0234     //branches below. When KF5 compatibility will be removed, cookies will always
0235     //be managed internally, so remove the #ifdef and the lambda and move its body
0236     //inside the `if (downloader)`
0237         auto requestDownloadAndOpen = [&]() {
0238             args.metaData().insert(QStringLiteral("TempFile"), QString());
0239             emit downloader->downloadAndOpenUrl(url, item->id(), args, bArgs, true);
0240             if (item->state() == QWebEngineDownloadRequest::DownloadRequested) {
0241                 qCDebug(WEBENGINEPART_LOG()) << "Automatically accepting download for" << item->url() << "This shouldn't happen";
0242                 item->accept();
0243             }
0244         };
0245 
0246 //TODO KF6: remove #ifdef and the #else block when dropping compatibility with KF5
0247 #ifdef MANAGE_COOKIES_INTERNALLY
0248         requestDownloadAndOpen();
0249 #else //This can only happen in KF5, as MANAGE_COOKIES_INTERNALLY is always defined in KF6
0250         // blob URLs can't be downloaded by KIO, so use the part to download them even when using KCookieJar
0251         if (item->url().scheme() == QStringLiteral("blob") && downloader) {
0252             requestDownloadAndOpen();
0253         } else {
0254             emit m_part->browserExtension()->openUrlRequest(url, args, bArgs);
0255             item->cancel();
0256             item->deleteLater();
0257         }
0258 #endif
0259     }
0260 }
0261 
0262 void WebEnginePage::saveUrlToDiskAndDisplay(QWebEngineDownloadRequest* req, const KParts::OpenUrlArguments& args, const BrowserArguments& bArgs)
0263 {
0264     QWidget *window = view() ? view()->window() : nullptr;
0265 
0266     QString suggestedName = !req->suggestedFileName().isEmpty() ? req->suggestedFileName() : req->url().fileName();
0267     QString downloadPath = Konq::askDownloadLocation(suggestedName, window);
0268     if (downloadPath.isEmpty()) {
0269         req->cancel();
0270         return;
0271     }
0272 
0273     WebEngineDownloaderExtension *downloader = m_part->downloader();
0274     DownloaderJob *job = downloader->downloadJob(req->url(), req->id(), this);
0275     if (!job) {
0276         return;
0277     }
0278 
0279     auto lambda = [this, args, bArgs](DownloaderJob *, const QUrl &url) {
0280     #if QT_VERSION_MAJOR < 6
0281         emit m_part->browserExtension()->openUrlRequest(url, args, bArgs);
0282     #else
0283         emit m_part->browserExtension()->browserOpenUrlRequest(url, args, bArgs);
0284     #endif
0285     };
0286     job->startDownload(downloadPath, window, this, lambda);
0287 }
0288 
0289 void WebEnginePage::saveAs(QWebEngineDownloadRequest* req)
0290 {
0291     QWidget *window = view() ? view()->window() : nullptr;
0292 
0293     QString suggestedName = !req->suggestedFileName().isEmpty() ? req->suggestedFileName() : req->url().fileName();
0294     QString downloadPath = Konq::askDownloadLocation(suggestedName, window);
0295     if (downloadPath.isEmpty()) {
0296         req->cancel();
0297         return;
0298     }
0299 
0300     WebEngineDownloaderExtension *downloader = m_part->downloader();
0301     DownloaderJob *job = downloader->downloadJob(req->url(), req->id(), this);
0302     if (!job) {
0303         return;
0304     }
0305 
0306     auto lambda = [this](DownloaderJob *dj, const QUrl &url) {
0307         if (dj->error() == 0) {
0308             m_part->openUrl(url);
0309             return;
0310         }
0311         BrowserArguments bArgs;
0312         bArgs.setForcesNewWindow(true);
0313 #if QT_VERSION_MAJOR < 6
0314         emit m_part->browserExtension()->openUrlRequest(url, {}, bArgs);
0315 #else
0316         emit m_part->browserExtension()->browserOpenUrlRequest(url, {}, bArgs);
0317 #endif
0318     };
0319     job->startDownload(downloadPath, window, this, lambda);
0320 }
0321 
0322 # ifndef REMOTE_DND_NOT_HANDLED_BY_WEBENGINE
0323 void WebEnginePage::setDropOperationStarted()
0324 {
0325     m_dropOperationTimer->start(100);
0326 }
0327 #endif
0328 
0329 
0330 QWebEnginePage *WebEnginePage::createWindow(WebWindowType type)
0331 {
0332 #ifndef REMOTE_DND_NOT_HANDLED_BY_WEBENGINE
0333     if (m_dropOperationTimer->isActive()) {
0334         m_dropOperationTimer->stop();
0335         return this;
0336     }
0337 #endif
0338 
0339     //qCDebug(WEBENGINEPART_LOG) << "window type:" << type;
0340     // Crete an instance of NewWindowPage class to capture all the
0341     // information we need to create a new window. See documentation of
0342     // the class for more information...
0343     NewWindowPage* page = new NewWindowPage(type, part());
0344     return page;
0345 }
0346 
0347 // Returns true if the scheme and domain of the two urls match...
0348 static bool domainSchemeMatch(const QUrl& u1, const QUrl& u2)
0349 {
0350     if (u1.scheme() != u2.scheme())
0351         return false;
0352 
0353 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
0354     QStringList u1List = u1.host().split(QL1C('.'), QString::SkipEmptyParts);
0355 #else
0356     QStringList u1List = u1.host().split(QL1C('.'), Qt::SkipEmptyParts);
0357 #endif
0358 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
0359     QStringList u2List = u2.host().split(QL1C('.'), QString::SkipEmptyParts);
0360 #else
0361     QStringList u2List = u2.host().split(QL1C('.'), Qt::SkipEmptyParts);
0362 #endif
0363 
0364     if (qMin(u1List.count(), u2List.count()) < 2)
0365         return false;  // better safe than sorry...
0366 
0367     while (u1List.count() > 2)
0368         u1List.removeFirst();
0369 
0370     while (u2List.count() > 2)
0371         u2List.removeFirst();
0372 
0373     return (u1List == u2List);
0374 }
0375 
0376 bool WebEnginePage::askBrowserToOpenUrl(const QUrl& url, const QString& mimetype, const KParts::OpenUrlArguments &_args, const BrowserArguments &bargs)
0377 {
0378     KParts::OpenUrlArguments args(_args);
0379     args.setMimeType(mimetype);
0380     args.metaData().insert("DontSendToDefaultHTMLPart", "");
0381 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0382     emit m_part->browserExtension()->openUrlRequest(url, args, bargs);
0383 #else
0384     emit m_part->browserExtension()->browserOpenUrlRequest(url, args, bargs);
0385 #endif
0386     return false;
0387 }
0388 
0389 bool WebEnginePage::shouldOpenUrl(const QUrl& url) const
0390 {
0391     static const QStringList s_forcedSchemes{QStringLiteral("remote"), QStringLiteral("trash")};
0392     if (s_forcedSchemes.contains(url.scheme())) {
0393         return false;
0394     }
0395     if (!url.isLocalFile()) {
0396         return true;
0397     }
0398     BrowserInterface *bi = m_part->browserExtension()->browserInterface();
0399     bool useThisPart = false;
0400     //We don't check whether bi is valid, as invokeMethod will fail if it's nullptr
0401     //If invokeMethod fails, useThisPart will keep its default value (false) which is what we need to return, so there's no
0402     //need to check the return value of invokeMethod
0403     QMetaObject::invokeMethod(bi, "isCorrectPartForLocalFile", Q_RETURN_ARG(bool, useThisPart), Q_ARG(KParts::ReadOnlyPart*, part()), Q_ARG(QString, url.path()));
0404     return useThisPart;
0405 }
0406 
0407 bool WebEnginePage::acceptNavigationRequest(const QUrl& url, NavigationType type, bool isMainFrame)
0408 {
0409     //Ask the browser for permission to navigate away. In Konqueror, if a view is locked, it can't navigate to somewhere else
0410     if (isMainFrame) {
0411         KonqInterfaces::Browser *browser = KonqInterfaces::Browser::browser(qApp);
0412         if (browser && !browser->canNavigateTo(part(), url)) {
0413             return false;
0414         }
0415     }
0416 
0417     if (isMainFrame) {
0418         if (!shouldOpenUrl(url)) {
0419             return askBrowserToOpenUrl(url);
0420         }
0421     }
0422 
0423     QUrl reqUrl(url);
0424 
0425     // Handle "mailto:" url here...
0426     if (handleMailToUrl(reqUrl, type))
0427         return false;
0428 
0429     const bool isTypedUrl = property("NavigationTypeUrlEntered").toBool();
0430 
0431     /*
0432       NOTE: We use a dynamic QObject property called "NavigationTypeUrlEntered"
0433       to distinguish between requests generated by user entering a url vs those
0434       that were generated programmatically through javascript (AJAX requests).
0435     */
0436     if (isMainFrame && isTypedUrl)
0437       setProperty("NavigationTypeUrlEntered", QVariant());
0438 
0439     // inPage requests are those generarted within the current page through
0440     // link clicks, javascript queries, and button clicks (form submission).
0441     bool inPageRequest = true;
0442     switch (type) {
0443         case QWebEnginePage::NavigationTypeFormSubmitted:
0444             if (!checkFormData(url))
0445                return false;
0446             if (part() && part()->wallet()) {
0447                 part()->wallet()->saveFormsInPage(this);
0448             }
0449 
0450             break;
0451 #if 0
0452         case QWebEnginePage::NavigationTypeFormResubmitted:
0453             if (!checkFormData(request))
0454                 return false;
0455             if (KMessageBox::warningContinueCancel(view(),
0456                             i18n("<qt><p>To display the requested web page again, "
0457                                   "the browser needs to resend information you have "
0458                                   "previously submitted.</p>"
0459                                   "<p>If you were shopping online and made a purchase, "
0460                                   "click the Cancel button to prevent a duplicate purchase."
0461                                   "Otherwise, click the Continue button to display the web"
0462                                   "page again.</p>"),
0463                             i18n("Resubmit Information")) == KMessageBox::Cancel) {
0464                 return false;
0465             }
0466             break;
0467 #endif
0468         case QWebEnginePage::NavigationTypeBackForward:
0469             // If history navigation is locked, ignore all such requests...
0470             if (property("HistoryNavigationLocked").toBool()) {
0471                 setProperty("HistoryNavigationLocked", QVariant());
0472                 qCDebug(WEBENGINEPART_LOG) << "Rejected history navigation because 'HistoryNavigationLocked' property is set!";
0473                 return false;
0474             }
0475             //qCDebug(WEBENGINEPART_LOG) << "Navigating to item (" << history()->currentItemIndex()
0476             //         << "of" << history()->count() << "):" << history()->currentItem().url();
0477             inPageRequest = false;
0478             break;
0479         case QWebEnginePage::NavigationTypeReload:
0480 //            setRequestMetaData(QL1S("cache"), QL1S("reload"));
0481             inPageRequest = false;
0482             break;
0483         case QWebEnginePage::NavigationTypeOther: // triggered by javascript
0484             qCDebug(WEBENGINEPART_LOG) << "Triggered by javascript";
0485             inPageRequest = !isTypedUrl;
0486             break;
0487         default:
0488             break;
0489     }
0490 
0491     if (inPageRequest) {
0492         // if (!checkLinkSecurity(request, type))
0493         //      return false;
0494 
0495         //  if (m_sslInfo.isValid())
0496         //      setRequestMetaData(QL1S("ssl_was_in_use"), QL1S("TRUE"));
0497     }
0498 
0499 
0500     // Honor the enabling/disabling of plugins per host.
0501     settings()->setAttribute(QWebEngineSettings::PluginsEnabled, WebEngineSettings::self()->isPluginsEnabled(reqUrl.host()));
0502 
0503     if (isMainFrame) {
0504         emit mainFrameNavigationRequested(this, url);
0505     }
0506     return QWebEnginePage::acceptNavigationRequest(url, type, isMainFrame);
0507 }
0508 
0509 #if 0
0510 static int errorCodeFromReply(QNetworkReply* reply)
0511 {
0512     // First check if there is a KIO error code sent back and use that,
0513     // if not attempt to convert QNetworkReply's NetworkError to KIO::Error.
0514     QVariant attr = reply->attribute(static_cast<QNetworkRequest::Attribute>(KIO::AccessManager::KioError));
0515     if (attr.isValid() && attr.type() == QVariant::Int)
0516         return attr.toInt();
0517 
0518     switch (reply->error()) {
0519         case QNetworkReply::ConnectionRefusedError:
0520             return KIO::ERR_CANNOT_CONNECT;
0521         case QNetworkReply::HostNotFoundError:
0522             return KIO::ERR_UNKNOWN_HOST;
0523         case QNetworkReply::TimeoutError:
0524             return KIO::ERR_SERVER_TIMEOUT;
0525         case QNetworkReply::OperationCanceledError:
0526             return KIO::ERR_USER_CANCELED;
0527         case QNetworkReply::ProxyNotFoundError:
0528             return KIO::ERR_UNKNOWN_PROXY_HOST;
0529         case QNetworkReply::ContentAccessDenied:
0530             return KIO::ERR_ACCESS_DENIED;
0531         case QNetworkReply::ContentOperationNotPermittedError:
0532             return KIO::ERR_WRITE_ACCESS_DENIED;
0533         case QNetworkReply::ContentNotFoundError:
0534             return KIO::ERR_NO_CONTENT;
0535         case QNetworkReply::AuthenticationRequiredError:
0536             return KIO::ERR_CANNOT_AUTHENTICATE;
0537         case QNetworkReply::ProtocolUnknownError:
0538             return KIO::ERR_UNSUPPORTED_PROTOCOL;
0539         case QNetworkReply::ProtocolInvalidOperationError:
0540             return KIO::ERR_UNSUPPORTED_ACTION;
0541         case QNetworkReply::UnknownNetworkError:
0542             return KIO::ERR_UNKNOWN;
0543         case QNetworkReply::NoError:
0544         default:
0545             break;
0546     }
0547 
0548     return 0;
0549 }
0550 #endif
0551 
0552 #if QT_VERSION_MAJOR < 6
0553 bool WebEnginePage::certificateError(const QWebEngineCertificateError& ce)
0554 {
0555     return WebEnginePartControls::self()->handleCertificateError(ce, this);
0556 }
0557 #else
0558 void WebEnginePage::handleCertificateError(const QWebEngineCertificateError &ce) {
0559     WebEnginePartControls::self()->handleCertificateError(ce, this);
0560 }
0561 #endif
0562 
0563 
0564 WebEnginePart* WebEnginePage::part() const
0565 {
0566     return m_part.data();
0567 }
0568 
0569 void WebEnginePage::setPart(WebEnginePart* part)
0570 {
0571     m_part = part;
0572 }
0573 
0574 void WebEnginePage::slotLoadFinished(bool ok)
0575 {
0576     QUrl requestUrl = url();
0577     requestUrl.setUserInfo(QString());
0578 #if 0
0579     const bool shouldResetSslInfo = (m_sslInfo.isValid() && !domainSchemeMatch(requestUrl, m_sslInfo.url()));
0580     QWebFrame* frame = qobject_cast<QWebFrame *>(reply->request().originatingObject());
0581     if (!frame)
0582         return;
0583     const bool isMainFrameRequest = (frame == mainFrame());
0584 #else
0585     // PORTING_TODO
0586     const bool isMainFrameRequest = true;
0587 #endif
0588 
0589 #if 0
0590     // Only deal with non-redirect responses...
0591     const QVariant redirectVar = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
0592 
0593     if (isMainFrameRequest && redirectVar.isValid()) {
0594         m_sslInfo.restoreFrom(reply->attribute(static_cast<QNetworkRequest::Attribute>(KIO::AccessManager::MetaData)),
0595                               reply->url(), shouldResetSslInfo);
0596         return;
0597     }
0598 
0599     const int errCode = errorCodeFromReply(reply);
0600     qCDebug(WEBENGINEPART_LOG) << frame << "is main frame request?" << isMainFrameRequest << requestUrl;
0601 #endif
0602 
0603     if (ok) {
0604         if (isMainFrameRequest) {
0605 #if 0
0606             m_sslInfo.restoreFrom(reply->attribute(static_cast<QNetworkRequest::Attribute>(KIO::AccessManager::MetaData)),
0607                     reply->url(), shouldResetSslInfo);
0608 #endif
0609             setPageJScriptPolicy(url());
0610         }
0611     } else {
0612     // Handle any error...
0613 #if 0
0614     switch (errCode) {
0615         case 0:
0616         case KIO::ERR_NO_CONTENT:
0617             break;
0618         case KIO::ERR_ABORTED:
0619         case KIO::ERR_USER_CANCELED: // Do nothing if request is cancelled/aborted
0620             //qCDebug(WEBENGINEPART_LOG) << "User aborted request!";
0621             m_ignoreError = true;
0622             emit loadAborted(QUrl());
0623             return;
0624         // Handle the user clicking on a link that refers to a directory
0625         // Since KIO cannot automatically convert a GET request to a LISTDIR one.
0626         case KIO::ERR_IS_DIRECTORY:
0627             m_ignoreError = true;
0628             emit loadAborted(reply->url());
0629             return;
0630         default:
0631             // Make sure the saveFrameStateRequested signal is emitted so
0632             // the page can restored properly.
0633             if (isMainFrameRequest)
0634                 emit saveFrameStateRequested(frame, 0);
0635 
0636             m_ignoreError = (reply->attribute(QNetworkRequest::User).toInt() == QNetworkReply::ContentAccessDenied);
0637             m_kioErrorCode = errCode;
0638             break;
0639 #endif
0640     }
0641 
0642     if (isMainFrameRequest) {
0643         const WebEnginePageSecurity security = (m_sslInfo.isValid() ? PageEncrypted : PageUnencrypted);
0644         emit m_part->navigationExtension()->setPageSecurity(security);
0645     }
0646 }
0647 
0648 void WebEnginePage::slotFeaturePermissionRequested(const QUrl& url, QWebEnginePage::Feature feature)
0649 {
0650     //url.path() is always / (meaning that permissions should be granted site-wide and not per page)
0651     QUrl thisUrl(this->url());
0652     thisUrl.setPath("/");
0653     thisUrl.setQuery(QString());
0654     thisUrl.setFragment(QString());
0655     if (url == thisUrl) {
0656         part()->slotShowFeaturePermissionBar(url, feature);
0657         return;
0658     }
0659     switch(feature) {
0660     case QWebEnginePage::Notifications:
0661         // FIXME: We should have a setting to tell if this is enabled, but so far it is always enabled.
0662         setFeaturePermission(url, feature, QWebEnginePage::PermissionGrantedByUser);
0663         break;
0664     case QWebEnginePage::Geolocation:
0665         if (KMessageBox::warningContinueCancel(nullptr, i18n("This site is attempting to "
0666                                                        "access information about your "
0667                                                        "physical location.\n"
0668                                                        "Do you want to allow it access?"),
0669                                             i18n("Network Transmission"),
0670                                             KGuiItem(i18n("Allow access")),
0671                                             KStandardGuiItem::cancel(),
0672                                             QStringLiteral("WarnGeolocation")) == KMessageBox::Cancel) {
0673             setFeaturePermission(url, feature, QWebEnginePage::PermissionDeniedByUser);
0674         } else {
0675             setFeaturePermission(url, feature, QWebEnginePage::PermissionGrantedByUser);
0676         }
0677         break;
0678     default:
0679         setFeaturePermission(url, feature, QWebEnginePage::PermissionUnknown);
0680         break;
0681     }
0682 }
0683 
0684 void WebEnginePage::slotGeometryChangeRequested(const QRect & rect)
0685 {
0686     const QString host = url().host();
0687 
0688     // NOTE: If a new window was created from another window which is in
0689     // maximized mode and its width and/or height were not specified at the
0690     // time of its creation, which is always the case in QWebEnginePage::createWindow,
0691     // then any move operation will seem not to work. That is because the new
0692     // window will be in maximized mode where moving it will not be possible...
0693     if (WebEngineSettings::self()->windowMovePolicy(host) == HtmlSettingsInterface::JSWindowMoveAllow &&
0694         (view()->x() != rect.x() || view()->y() != rect.y()))
0695         emit m_part->navigationExtension()->moveTopLevelWidget(rect.x(), rect.y());
0696 
0697     const int height = rect.height();
0698     const int width = rect.width();
0699 
0700     // parts of following code are based on kjs_window.cpp
0701     // Security check: within desktop limits and bigger than 100x100 (per spec)
0702     if (width < 100 || height < 100) {
0703         qCWarning(WEBENGINEPART_LOG) << "Window resize refused, window would be too small (" << width << "," << height << ")";
0704         return;
0705     }
0706 
0707     QRect sg = view()->screen()->virtualGeometry();
0708 
0709     if (width > sg.width() || height > sg.height()) {
0710         qCWarning(WEBENGINEPART_LOG) << "Window resize refused, window would be too big (" << width << "," << height << ")";
0711         return;
0712     }
0713 
0714     if (WebEngineSettings::self()->windowResizePolicy(host) == HtmlSettingsInterface::JSWindowResizeAllow) {
0715         //qCDebug(WEBENGINEPART_LOG) << "resizing to " << width << "x" << height;
0716         emit m_part->navigationExtension()->resizeTopLevelWidget(width, height);
0717     }
0718 
0719     // If the window is out of the desktop, move it up/left
0720     // (maybe we should use workarea instead of sg, otherwise the window ends up below kicker)
0721     const int right = view()->x() + view()->frameGeometry().width();
0722     const int bottom = view()->y() + view()->frameGeometry().height();
0723     int moveByX = 0, moveByY = 0;
0724     if (right > sg.right())
0725         moveByX = - right + sg.right(); // always <0
0726     if (bottom > sg.bottom())
0727         moveByY = - bottom + sg.bottom(); // always <0
0728 
0729     if ((moveByX || moveByY) && WebEngineSettings::self()->windowMovePolicy(host) == HtmlSettingsInterface::JSWindowMoveAllow)
0730         emit m_part->navigationExtension()->moveTopLevelWidget(view()->x() + moveByX, view()->y() + moveByY);
0731 }
0732 
0733 bool WebEnginePage::checkFormData(const QUrl &url) const
0734 {
0735     const QString scheme (url.scheme());
0736 
0737     if (m_sslInfo.isValid() &&
0738         !scheme.compare(QL1S("https")) && !scheme.compare(QL1S("mailto")) &&
0739         (KMessageBox::warningContinueCancel(nullptr,
0740                                            i18n("Warning: This is a secure form "
0741                                                 "but it is attempting to send "
0742                                                 "your data back unencrypted.\n"
0743                                                 "A third party may be able to "
0744                                                 "intercept and view this "
0745                                                 "information.\nAre you sure you "
0746                                                 "want to send the data unencrypted?"),
0747                                            i18n("Network Transmission"),
0748                                            KGuiItem(i18n("&Send Unencrypted")))  == KMessageBox::Cancel)) {
0749 
0750         return false;
0751     }
0752 
0753 
0754     if (scheme.compare(QL1S("mailto")) == 0 &&
0755         (KMessageBox::warningContinueCancel(nullptr, i18n("This site is attempting to "
0756                                                     "submit form data via email.\n"
0757                                                     "Do you want to continue?"),
0758                                             i18n("Network Transmission"),
0759                                             KGuiItem(i18n("&Send Email")),
0760                                             KStandardGuiItem::cancel(),
0761                                             QStringLiteral("WarnTriedEmailSubmit")) == KMessageBox::Cancel)) {
0762         return false;
0763     }
0764 
0765     return true;
0766 }
0767 
0768 // Sanitizes the "mailto:" url, e.g. strips out any "attach" parameters.
0769 static QUrl sanitizeMailToUrl(const QUrl &url, QStringList& files) {
0770     QUrl sanitizedUrl;
0771 
0772     // NOTE: This is necessary to ensure we can properly use QUrl's query
0773     // related APIs to process 'mailto:' urls of form 'mailto:foo@bar.com'.
0774     if (url.hasQuery())
0775       sanitizedUrl = url;
0776     else
0777       sanitizedUrl = QUrl(url.scheme() + QL1S(":?") + url.path());
0778 
0779     QUrlQuery query(sanitizedUrl);
0780     const QList<QPair<QString, QString> > items (query.queryItems());
0781 
0782     QUrlQuery sanitizedQuery;
0783     for(auto queryItem : items) {
0784         if (queryItem.first.contains(QL1C('@')) && queryItem.second.isEmpty()) {
0785             // ### DF: this hack breaks mailto:faure@kde.org, kmail doesn't expect mailto:?to=faure@kde.org
0786             queryItem.second = queryItem.first;
0787             queryItem.first = QStringLiteral("to");
0788         } else if (QString::compare(queryItem.first, QL1S("attach"), Qt::CaseInsensitive) == 0) {
0789             files << queryItem.second;
0790             continue;
0791         }
0792         sanitizedQuery.addQueryItem(queryItem.first, queryItem.second);
0793     }
0794 
0795     sanitizedUrl.setQuery(sanitizedQuery);
0796     return sanitizedUrl;
0797 }
0798 
0799 bool WebEnginePage::handleMailToUrl (const QUrl &url, NavigationType type) const
0800 {
0801     if (url.scheme() == QL1S("mailto")) {
0802         QStringList files;
0803         QUrl mailtoUrl (sanitizeMailToUrl(url, files));
0804 
0805         switch (type) {
0806             case QWebEnginePage::NavigationTypeLinkClicked:
0807                 if (!files.isEmpty() && KMessageBox::warningContinueCancelList(nullptr,
0808                                                                                i18n("<qt>Do you want to allow this site to attach "
0809                                                                                     "the following files to the email message?</qt>"),
0810                                                                                files, i18n("Email Attachment Confirmation"),
0811                                                                                KGuiItem(i18n("&Allow attachments")),
0812                                                                                KGuiItem(i18n("&Ignore attachments")), QL1S("WarnEmailAttachment")) == KMessageBox::Continue) {
0813 
0814                    // Re-add the attachments...
0815                     QStringListIterator filesIt (files);
0816                     QUrlQuery query(mailtoUrl);
0817                     while (filesIt.hasNext()) {
0818                         query.addQueryItem(QL1S("attach"), filesIt.next());
0819                     }
0820                     mailtoUrl.setQuery(query);
0821                 }
0822                 break;
0823             case QWebEnginePage::NavigationTypeFormSubmitted:
0824             //case QWebEnginePage::NavigationTypeFormResubmitted:
0825                 if (!files.isEmpty()) {
0826                     KMessageBox::information(nullptr, i18n("This site attempted to attach a file from your "
0827                                                      "computer in the form submission. The attachment "
0828                                                      "was removed for your protection."),
0829                                              i18n("Attachment Removed"), QStringLiteral("InfoTriedAttach"));
0830                 }
0831                 break;
0832             default:
0833                  break;
0834         }
0835 
0836         //qCDebug(WEBENGINEPART_LOG) << "Emitting openUrlRequest with " << mailtoUrl;
0837         emit m_part->navigationExtension()->openUrlRequest(mailtoUrl);
0838         return true;
0839     }
0840 
0841     return false;
0842 }
0843 
0844 void WebEnginePage::setPageJScriptPolicy(const QUrl &url)
0845 {
0846     const QString hostname (url.host());
0847     settings()->setAttribute(QWebEngineSettings::JavascriptEnabled,
0848                              WebEngineSettings::self()->isJavaScriptEnabled(hostname));
0849 
0850     const HtmlSettingsInterface::JSWindowOpenPolicy policy = WebEngineSettings::self()->windowOpenPolicy(hostname);
0851     settings()->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows,
0852                              (policy != HtmlSettingsInterface::JSWindowOpenDeny &&
0853                               policy != HtmlSettingsInterface::JSWindowOpenSmart));
0854 }
0855 
0856 void WebEnginePage::slotAuthenticationRequired(const QUrl &requestUrl, QAuthenticator *auth)
0857 {
0858     KIO::AuthInfo info;
0859     info.url = requestUrl;
0860     info.username = auth->user();
0861     info.realmValue = auth->realm();
0862     // If no realm metadata, then make sure path matching is turned on.
0863     info.verifyPath = info.realmValue.isEmpty();
0864 
0865     const QString errorMsg = QString();
0866     const int ret = m_passwdServerClient->queryAuthInfo(&info, errorMsg, view()->window()->winId(), KUserTimestamp::userTimestamp());
0867     if (ret == KJob::NoError) {
0868         auth->setUser(info.username);
0869         auth->setPassword(info.password);
0870     } else {
0871         // Set authenticator null if dialog is cancelled
0872         // or if we couldn't communicate with kpasswdserver
0873         *auth = QAuthenticator();
0874     }
0875 }
0876 
0877 void WebEnginePage::changeFullScreenMode(QWebEngineFullScreenRequest req)
0878 {
0879         BrowserInterface *iface = part()->browserExtension()->browserInterface();
0880         if (iface) {
0881             req.accept();
0882             iface->callMethod("toggleCompleteFullScreen", req.toggleOn());
0883         } else {
0884             req.reject();
0885         }
0886 }
0887 
0888 
0889 void WebEnginePage::setStatusBarText(const QString& text)
0890 {
0891     if (m_part) {
0892         emit m_part->setStatusBarText(text);
0893     }
0894 }
0895 
0896 void WebEnginePage::changeLifecycleState(QWebEnginePage::LifecycleState recommendedState)
0897 {
0898     if (recommendedState != QWebEnginePage::LifecycleState::Active && !isVisible()) {
0899         setLifecycleState(QWebEnginePage::LifecycleState::Frozen);
0900     } else {
0901         setLifecycleState(QWebEnginePage::LifecycleState::Active);
0902     }
0903 }
0904 
0905 void WebEnginePage::updateUserStyleSheet(const QString& script)
0906 {
0907     runJavaScript(script, QWebEngineScript::ApplicationWorld);
0908 }
0909 
0910 /************************************* Begin NewWindowPage ******************************************/
0911 
0912 NewWindowPage::NewWindowPage(WebWindowType type, WebEnginePart* part, QWidget* parent)
0913               :WebEnginePage(part, parent) , m_type(type) , m_createNewWindow(true)
0914 {
0915     Q_ASSERT_X (part, "NewWindowPage", "Must specify a valid KPart");
0916 
0917     // FIXME: are these 3 signals actually defined or used?
0918     connect(this, SIGNAL(menuBarVisibilityChangeRequested(bool)),
0919             this, SLOT(slotMenuBarVisibilityChangeRequested(bool)));
0920     connect(this, SIGNAL(toolBarVisibilityChangeRequested(bool)),
0921             this, SLOT(slotToolBarVisibilityChangeRequested(bool)));
0922     connect(this, SIGNAL(statusBarVisibilityChangeRequested(bool)),
0923             this, SLOT(slotStatusBarVisibilityChangeRequested(bool)));
0924     connect(this, &QWebEnginePage::loadFinished, this, &NewWindowPage::slotLoadFinished);
0925     if (m_type == WebBrowserBackgroundTab) {
0926         m_windowArgs.setLowerWindow(true);
0927     }
0928 }
0929 
0930 NewWindowPage::~NewWindowPage()
0931 {
0932 }
0933 
0934 bool NewWindowPage::decideHandlingOfJavascripWindow(const QUrl url) const
0935 {
0936     const HtmlSettingsInterface::JSWindowOpenPolicy policy = WebEngineSettings::self()->windowOpenPolicy(url.host());
0937     switch (policy) {
0938     case HtmlSettingsInterface::JSWindowOpenDeny:
0939         // TODO: Implement support for dealing with blocked pop up windows.
0940         return false;
0941     case HtmlSettingsInterface::JSWindowOpenAsk: {
0942         const QString message = (url.isEmpty() ?
0943                                     i18n("This site is requesting to open a new popup window.\n"
0944                                         "Do you want to allow this?") :
0945                                     i18n("<qt>This site is requesting to open a popup window to"
0946                                         "<p>%1</p><br/>Do you want to allow this?</qt>",
0947                                         KStringHandler::rsqueeze(url.toDisplayString().toHtmlEscaped(), 100)));
0948         return KMessageBox::questionTwoActions(view(), message, i18n("Javascript Popup Confirmation"),
0949                                         KGuiItem(i18n("Allow")), KGuiItem(i18n("Do Not Allow"))) == KMessageBox::PrimaryAction;
0950         // TODO: Implement support for dealing with blocked pop up windows.
0951     }
0952     default:
0953         break;
0954     }
0955     return true;
0956 }
0957 
0958 bool NewWindowPage::acceptNavigationRequest(const QUrl &url, NavigationType type, bool isMainFrame)
0959 {
0960     //qCDebug(WEBENGINEPART_LOG) << "url:" << url << ", type:" << type << ", isMainFrame:" << isMainFrame << "m_createNewWindow=" << m_createNewWindow;
0961     if (!m_createNewWindow) {
0962         return WebEnginePage::acceptNavigationRequest(url, type, isMainFrame);
0963     }
0964 
0965     const QUrl reqUrl (url);
0966     const bool actionRequestedByUser = type != QWebEnginePage::NavigationTypeOther;
0967     const bool actionRequestsNewTab = m_type == QWebEnginePage::WebBrowserBackgroundTab || m_type == QWebEnginePage::WebBrowserTab;
0968 
0969     if (actionRequestedByUser && !actionRequestsNewTab) {
0970         if (!part() && !isMainFrame) {
0971             return false;
0972         }
0973         if (!decideHandlingOfJavascripWindow(reqUrl)) {
0974             deleteLater();
0975             return false;
0976         }
0977     }
0978 
0979     // Browser args...
0980     BrowserArguments bargs;
0981     //Don't set forcesNewWindow for if m_type is WebDialog because it include popups, which the user may want to open in a new tab
0982     bargs.setForcesNewWindow(m_type == WebBrowserWindow);
0983 
0984     // OpenUrl args...
0985     KParts::OpenUrlArguments uargs;
0986     uargs.setMimeType(QL1S("text/html"));
0987     uargs.setActionRequestedByUser(actionRequestedByUser);
0988 
0989     // Window args...
0990     WindowArgs wargs (m_windowArgs);
0991 
0992     KParts::ReadOnlyPart* newWindowPart = nullptr;
0993 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0994     emit part()->browserExtension()->createNewWindow(url, uargs, bargs, wargs, &newWindowPart);
0995 #else
0996     emit part()->browserExtension()->browserCreateNewWindow(url, uargs, bargs, wargs, &newWindowPart);
0997 #endif
0998     qCDebug(WEBENGINEPART_LOG) << "Created new window" << newWindowPart;
0999 
1000     if (newWindowPart && newWindowPart->widget()->topLevelWidget() != part()->widget()->topLevelWidget()) {
1001         KParts::OpenUrlArguments args;
1002         args.metaData().insert(QL1S("new-window"), QL1S("true"));
1003         newWindowPart->setArguments(args);
1004     }
1005     deleteLater();
1006     return false;
1007 }
1008 
1009 void NewWindowPage::slotGeometryChangeRequested(const QRect & rect)
1010 {
1011     if (!rect.isValid())
1012         return;
1013 
1014     if (!m_createNewWindow) {
1015         WebEnginePage::slotGeometryChangeRequested(rect);
1016         return;
1017     }
1018 
1019     m_windowArgs.setX(rect.x());
1020     m_windowArgs.setY(rect.y());
1021     m_windowArgs.setWidth(qMax(rect.width(), 100));
1022     m_windowArgs.setHeight(qMax(rect.height(), 100));
1023 }
1024 
1025 void NewWindowPage::slotMenuBarVisibilityChangeRequested(bool visible)
1026 {
1027     //qCDebug(WEBENGINEPART_LOG) << visible;
1028     m_windowArgs.setMenuBarVisible(visible);
1029 }
1030 
1031 void NewWindowPage::slotStatusBarVisibilityChangeRequested(bool visible)
1032 {
1033     //qCDebug(WEBENGINEPART_LOG) << visible;
1034     m_windowArgs.setStatusBarVisible(visible);
1035 }
1036 
1037 void NewWindowPage::slotToolBarVisibilityChangeRequested(bool visible)
1038 {
1039     //qCDebug(WEBENGINEPART_LOG) << visible;
1040     m_windowArgs.setToolBarsVisible(visible);
1041 }
1042 
1043 // When is this called? (and acceptNavigationRequest is not called?)
1044 // The only case I found is Ctrl+click on link to data URL (like in konqviewmgrtest), that's quite specific...
1045 // Everything else seems to work with this method being commented out...
1046 void NewWindowPage::slotLoadFinished(bool ok)
1047 {
1048     Q_UNUSED(ok)
1049     if (!m_createNewWindow)
1050         return;
1051 
1052     const bool actionRequestedByUser = true; // ### we don't have the information here, unlike in acceptNavigationRequest
1053 
1054     // Browser args...
1055     BrowserArguments bargs;
1056     //Don't set forcesNewWindow for if m_type is WebDialog because it include popups, which the user may want to open in a new tab
1057     bargs.setForcesNewWindow(m_type == WebBrowserWindow);
1058 
1059     // OpenUrl args...
1060     KParts::OpenUrlArguments uargs;
1061     uargs.setMimeType(QL1S("text/html"));
1062     uargs.setActionRequestedByUser(actionRequestedByUser);
1063 
1064     // Window args...
1065     WindowArgs wargs (m_windowArgs);
1066 
1067     KParts::ReadOnlyPart* newWindowPart =nullptr;
1068 
1069 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
1070     emit part()->browserExtension()->createNewWindow(QUrl(), uargs, bargs, wargs, &newWindowPart);
1071 #else
1072     emit part()->browserExtension()->browserCreateNewWindow(QUrl(), uargs, bargs, wargs, &newWindowPart);
1073 #endif
1074 
1075     qCDebug(WEBENGINEPART_LOG) << "Created new window or tab" << newWindowPart;
1076 
1077     // Get the webview...
1078     WebEnginePart* webenginePart = newWindowPart ? qobject_cast<WebEnginePart*>(newWindowPart) : nullptr;
1079     WebEngineView* webView = webenginePart ? qobject_cast<WebEngineView*>(webenginePart->view()) : nullptr;
1080 
1081     if (webView) {
1082         // if a new window is created, set a new window meta-data flag.
1083         if (newWindowPart->widget()->topLevelWidget() != part()->widget()->topLevelWidget()) {
1084             KParts::OpenUrlArguments args;
1085             args.metaData().insert(QL1S("new-window"), QL1S("true"));
1086             newWindowPart->setArguments(args);
1087         }
1088         // Reparent this page to the new webview to prevent memory leaks.
1089         setParent(webView);
1090         // Replace the webpage of the new webview with this one. Nice trick...
1091         webView->setPage(this);
1092         // Set the new part as the one this page will use going forward.
1093         setPart(webenginePart);
1094         // Connect all the signals from this page to the slots in the new part.
1095         webenginePart->connectWebEnginePageSignals(this);
1096     }
1097 
1098     //Set the create new window flag to false...
1099     m_createNewWindow = false;
1100 }
1101 
1102 /****************************** End NewWindowPage *************************************************/
1103