File indexing completed on 2024-04-21 15:32:04

0001 /*
0002     SPDX-FileCopyrightText: 2012 Dawit Alemayehu <adawit@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-or-later
0005 */
0006 
0007 #include "webpluginfactory.h"
0008 
0009 #include "webpage.h"
0010 #include "kwebkitpart_debug.h"
0011 #include "kwebkitpart.h"
0012 #include "settings/webkitsettings.h"
0013 
0014 #include <KLocalizedString>
0015 #include <KParts/LiveConnectExtension>
0016 #include <KParts/ScriptableExtension>
0017 
0018 #include <QHBoxLayout>
0019 #include <QSpacerItem>
0020 #include <QPushButton>
0021 
0022 #include <QWebFrame>
0023 #include <QWebView>
0024 #include <QWebElement>
0025 
0026 #define QL1S(x)  QLatin1String(x)
0027 #define QL1C(x)  QLatin1Char(x)
0028 
0029 static QWebView* webViewFrom(QWidget* widget)
0030 {
0031     QWidget* parent = widget;
0032     QWebView *view = nullptr;
0033     while (parent) {
0034         if (QWebView *aView = qobject_cast<QWebView*>(parent)) {
0035             view = aView;
0036             break;
0037         }
0038         parent = parent->parentWidget();
0039     }
0040 
0041     return view;
0042 }
0043 
0044 FakePluginWidget::FakePluginWidget (uint id, const QUrl& url, const QString& mimeType, QWidget* parent)
0045                  :QWidget(parent)
0046                  ,m_swapping(false)
0047                  ,m_updateScrollPosition(false)
0048                  ,m_mimeType(mimeType)
0049                  ,m_id(id)
0050 {
0051     QHBoxLayout* horizontalLayout = new QHBoxLayout;
0052     setLayout(horizontalLayout);
0053 
0054     QSpacerItem* horizontalSpacer = new QSpacerItem(0, 0, QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
0055     horizontalLayout->addSpacerItem(horizontalSpacer);
0056 
0057     QPushButton* startPluginButton = new QPushButton(this);
0058     startPluginButton->setText(i18n("Start Plugin"));
0059     horizontalLayout->addWidget(startPluginButton);
0060 
0061     horizontalSpacer = new QSpacerItem(0, 0, QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
0062     horizontalLayout->addSpacerItem(horizontalSpacer);
0063 
0064     setContextMenuPolicy(Qt::CustomContextMenu);
0065     connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint)));
0066     connect(startPluginButton, SIGNAL(clicked()), this, SLOT(load()));
0067     setToolTip(url.toString());
0068 }
0069 
0070 void FakePluginWidget::loadAll()
0071 {
0072     load (true);
0073 }
0074 
0075 void FakePluginWidget::load (bool loadAll)
0076 {
0077     QWebView *view = webViewFrom(parentWidget());
0078     if (!view)
0079         return;
0080 
0081     // WORKAROUND: For some reason, when we load on demand plugins the scroll
0082     // position gets utterly screwed up and reset to the beginning of the
0083     // document. This is an effort to workaround that issue.
0084     connect(view->page(), SIGNAL(scrollRequested(int,int,QRect)),
0085             this, SLOT(updateScrollPoisition(int,int,QRect)), Qt::QueuedConnection);
0086 
0087     hide();
0088     m_swapping = true;
0089 
0090     QList<QWebFrame*> frames;
0091     frames.append(view->page()->mainFrame());
0092 
0093     QString selector (QLatin1String("applet:not([type]),embed:not([type]),object:not([type]),applet[type=\""));
0094     selector += m_mimeType;
0095     selector += QLatin1String("\"],embed[type=\"");
0096     selector += m_mimeType;
0097     selector += QLatin1String("\"],object[type=\"");
0098     selector += m_mimeType;
0099     selector += QLatin1String("\"]");
0100 
0101     while (!frames.isEmpty()) {
0102         bool loaded = false;
0103         QWebFrame *frame = frames.takeFirst();
0104         QWebElement docElement = frame->documentElement();
0105         QWebElementCollection elements = docElement.findAll(selector);
0106 
0107         Q_FOREACH (QWebElement element, elements) {
0108             if (loadAll || element.evaluateJavaScript(QLatin1String("this.swapping")).toBool()) {
0109                 QWebElement substitute = element.clone();
0110                 emit pluginLoaded(m_id);
0111                 m_updateScrollPosition = true;
0112                 element.replace(substitute);
0113                 deleteLater();
0114                 if (!loadAll) {
0115                     loaded = true;
0116                     break;  // Found the one plugin we wanted to start so exit loop.
0117                 }
0118             }
0119         }
0120         if (loaded && !loadAll) {
0121             break;      // Loading only one item, exit the outer loop as well...
0122         }
0123         frames += frame->childFrames();
0124     }
0125 
0126     m_swapping = false;
0127 }
0128 
0129 
0130 void FakePluginWidget::showContextMenu(const QPoint&)
0131 {
0132     // TODO: Implement context menu, e.g. load all and configure plugins.
0133 }
0134 
0135 void FakePluginWidget::updateScrollPoisition (int dx, int dy, const QRect& rect)
0136 {
0137     if (m_updateScrollPosition) {
0138         QWebView* view = webViewFrom(parentWidget());
0139         if (view)
0140             view->page()->mainFrame()->setScrollPosition(QPoint(dx, dy));
0141     }
0142 
0143     Q_UNUSED(rect);
0144 }
0145 
0146 
0147 WebPluginFactory::WebPluginFactory (KWebKitPart* part, QObject* parent)
0148     : KWebPluginFactory (parent)
0149     , mPart (part)
0150 {
0151 }
0152 
0153 static uint pluginId(const QUrl& url, const QStringList& argumentNames, const QStringList& argumentValues)
0154 {
0155     static const char* properties[] = {"src","data","width","height","type","id","name",nullptr};
0156 
0157     QString signature = url.toString();
0158     for (int i = 0; properties[i]; ++i) {
0159         const int index = argumentNames.indexOf(properties[i]);
0160         if (index > -1) {
0161             signature += argumentNames.at(index);
0162             signature += QL1C('=');
0163             signature += argumentValues.at(index);
0164         }
0165     }
0166 
0167     return qHash(signature);
0168 }
0169 
0170 QObject* WebPluginFactory::create (const QString& _mimeType, const QUrl& url, const QStringList& argumentNames, const QStringList& argumentValues) const
0171 {
0172     //qCDebug(KWEBKITPART_LOG) << _mimeType << url << argumentNames;
0173     QString mimeType (_mimeType.trimmed());
0174     if (mimeType.isEmpty()) {
0175         extractGuessedMimeType (url, &mimeType);
0176     }
0177 
0178     const bool noPluginHandling = WebKitSettings::self()->isInternalPluginHandlingDisabled();
0179 
0180     if (!noPluginHandling && WebKitSettings::self()->isLoadPluginsOnDemandEnabled()) {
0181         const uint id = pluginId(url, argumentNames, argumentValues);
0182         if (!mPluginsLoadedOnDemand.contains(id)) {
0183             FakePluginWidget* widget = new FakePluginWidget(id, url, mimeType);
0184             connect(widget, SIGNAL(pluginLoaded(uint)), this, SLOT(loadedPlugin(uint)));
0185             return widget;
0186         }
0187     }
0188 
0189     Q_ASSERT(mPart); // should never happen!!
0190     KParts::ReadOnlyPart* part = nullptr;
0191     QWebView* view = (mPart ? mPart->view() : nullptr);
0192 
0193     if (view) {
0194         if (noPluginHandling || !excludedMimeType(mimeType)) {
0195             QWebFrame* frame = view->page()->currentFrame();
0196             if (frame) {
0197                 part = createPartInstanceFrom(mimeType, argumentNames, argumentValues, view, frame);
0198             }
0199         }
0200 
0201         qCDebug(KWEBKITPART_LOG) << "Asked for" << mimeType << "plugin, got" << part;
0202 
0203         if (part) {
0204             connect (part->browserExtension(), SIGNAL (openUrlNotify()),
0205                      mPart->browserExtension(), SIGNAL (openUrlNotify()));
0206 
0207             connect (part->browserExtension(), SIGNAL(openUrlRequest(QUrl,KParts::OpenUrlArguments,KParts::BrowserArguments)),
0208                      mPart->browserExtension(), SIGNAL(openUrlRequest(QUrl,KParts::OpenUrlArguments,KParts::BrowserArguments)));
0209 
0210             // Check if this part is scriptable
0211             KParts::ScriptableExtension* scriptExt = KParts::ScriptableExtension::childObject(part);
0212             if (!scriptExt) {
0213                 // Try to fall back to LiveConnectExtension compat
0214                 KParts::LiveConnectExtension* lc = KParts::LiveConnectExtension::childObject(part);
0215                 if (lc) {
0216                     scriptExt = KParts::ScriptableExtension::adapterFromLiveConnect(part, lc);
0217                 }
0218             }
0219 
0220             if (scriptExt) {
0221                 scriptExt->setHost(KParts::ScriptableExtension::childObject(mPart));
0222             }
0223 
0224             QMap<QString, QString> metaData = part->arguments().metaData();
0225             QString urlStr = url.toString (QUrl::RemovePath | QUrl::RemoveQuery | QUrl::RemoveFragment);
0226             metaData.insert ("PropagateHttpHeader", "true");
0227             metaData.insert ("referrer", urlStr);
0228             metaData.insert ("cross-domain", urlStr);
0229             metaData.insert ("main_frame_request", "TRUE");
0230             metaData.insert ("ssl_activate_warnings", "TRUE");
0231 
0232             KWebPage *page = qobject_cast<KWebPage*>(view->page());
0233 
0234             if (page) {
0235                 const QString scheme = page->currentFrame()->url().scheme();
0236                 if (page && (QString::compare (scheme, QL1S ("https"), Qt::CaseInsensitive) == 0 ||
0237                             QString::compare (scheme, QL1S ("webdavs"), Qt::CaseInsensitive) == 0))
0238                     metaData.insert ("ssl_was_in_use", "TRUE");
0239                 else
0240                     metaData.insert ("ssl_was_in_use", "FALSE");
0241             }
0242 
0243             KParts::OpenUrlArguments openUrlArgs = part->arguments();
0244             openUrlArgs.metaData() = metaData;
0245             openUrlArgs.setMimeType(mimeType);
0246             part->setArguments(openUrlArgs);
0247             QMetaObject::invokeMethod(part, "openUrl", Qt::QueuedConnection, Q_ARG(QUrl, QUrl(url)));
0248             return part->widget();
0249         }
0250     }
0251 
0252     return nullptr;
0253 }
0254 
0255 void WebPluginFactory::loadedPlugin (uint id)
0256 {
0257     mPluginsLoadedOnDemand << id;
0258 }
0259 
0260 void WebPluginFactory::resetPluginOnDemandList()
0261 {
0262     mPluginsLoadedOnDemand.clear();
0263 }