File indexing completed on 2024-03-24 04:45:17
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 }