File indexing completed on 2024-04-28 15:29:19

0001 /*
0002     This file is part of the KDE project
0003     SPDX-FileCopyrightText: 1999 Simon Hausmann <hausmann@kde.org>
0004     SPDX-FileCopyrightText: 1999 David Faure <faure@kde.org>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 #include "browserextension.h"
0010 
0011 #include "kparts_logging.h"
0012 
0013 #include <KLocalizedString>
0014 #include <KMessageBox>
0015 #include <KUriFilter>
0016 
0017 #include <QApplication>
0018 #include <QClipboard>
0019 #include <QMap>
0020 #include <QRegularExpression>
0021 #include <QTimer>
0022 
0023 using namespace KParts;
0024 
0025 namespace KParts
0026 {
0027 // Internal class, use to store the status of the actions
0028 class KBitArray
0029 {
0030 public:
0031     int val = 0;
0032     bool operator[](int index)
0033     {
0034         return (val & (1 << index)) ? true : false;
0035     }
0036     void setBit(int index, bool value)
0037     {
0038         if (value) {
0039             val = val | (1 << index);
0040         } else {
0041             val = val & ~(1 << index);
0042         }
0043     }
0044 };
0045 
0046 class BrowserExtensionPrivate
0047 {
0048 public:
0049     BrowserExtensionPrivate(KParts::ReadOnlyPart *parent)
0050         : m_urlDropHandlingEnabled(false)
0051         , m_browserInterface(nullptr)
0052         , m_part(parent)
0053     {
0054     }
0055 
0056     struct DelayedRequest {
0057         QUrl m_delayedURL;
0058         KParts::OpenUrlArguments m_delayedArgs;
0059         KParts::BrowserArguments m_delayedBrowserArgs;
0060     };
0061 
0062     QList<DelayedRequest> m_requests;
0063     bool m_urlDropHandlingEnabled;
0064     KBitArray m_actionStatus;
0065     QMap<int, QString> m_actionText;
0066     BrowserInterface *m_browserInterface;
0067 
0068     static void createActionSlotMap();
0069 
0070     KParts::ReadOnlyPart *m_part;
0071     OpenUrlArguments m_args;
0072     BrowserArguments m_browserArgs;
0073 };
0074 
0075 Q_GLOBAL_STATIC(BrowserExtension::ActionSlotMap, s_actionSlotMap)
0076 Q_GLOBAL_STATIC(BrowserExtension::ActionNumberMap, s_actionNumberMap)
0077 
0078 void BrowserExtensionPrivate::createActionSlotMap()
0079 {
0080     s_actionSlotMap()->insert("cut", SLOT(cut()));
0081     s_actionSlotMap()->insert("copy", SLOT(copy()));
0082     s_actionSlotMap()->insert("paste", SLOT(paste()));
0083     s_actionSlotMap()->insert("print", SLOT(print()));
0084     // Tricky. Those aren't actions in fact, but simply methods that a browserextension
0085     // can have or not. No need to return them here.
0086     // s_actionSlotMap()->insert( "reparseConfiguration", SLOT(reparseConfiguration()) );
0087     // s_actionSlotMap()->insert( "refreshMimeTypes", SLOT(refreshMimeTypes()) );
0088 
0089     // Create the action-number map
0090     BrowserExtension::ActionSlotMap::ConstIterator it = s_actionSlotMap()->constBegin();
0091     BrowserExtension::ActionSlotMap::ConstIterator itEnd = s_actionSlotMap()->constEnd();
0092     for (int i = 0; it != itEnd; ++it, ++i) {
0093         // qDebug() << " action " << it.key() << " number " << i;
0094         s_actionNumberMap()->insert(it.key(), i);
0095     }
0096 }
0097 
0098 }
0099 
0100 BrowserExtension::BrowserExtension(KParts::ReadOnlyPart *parent)
0101     : QObject(parent)
0102     , d(new BrowserExtensionPrivate(parent))
0103 {
0104     // qDebug() << "BrowserExtension::BrowserExtension() " << this;
0105 
0106     if (s_actionSlotMap()->isEmpty())
0107     // Create the action-slot map
0108     {
0109         BrowserExtensionPrivate::createActionSlotMap();
0110     }
0111 
0112     // Set the initial status of the actions depending on whether
0113     // they're supported or not
0114     const QMetaObject *metaobj = metaObject();
0115     ActionSlotMap::ConstIterator it = s_actionSlotMap()->constBegin();
0116     ActionSlotMap::ConstIterator itEnd = s_actionSlotMap()->constEnd();
0117     for (int i = 0; it != itEnd; ++it, ++i) {
0118         // Does the extension have a slot with the name of this action ?
0119         QByteArray slotSig = it.key() + "()";
0120         d->m_actionStatus.setBit(i, metaobj->indexOfMethod(slotSig.constData()) != -1);
0121     }
0122 
0123     connect(d->m_part, static_cast<void (KParts::ReadOnlyPart::*)()>(&KParts::ReadOnlyPart::completed), this, &BrowserExtension::slotCompleted);
0124     connect(this, &BrowserExtension::openUrlRequest, this, &BrowserExtension::slotOpenUrlRequest);
0125     connect(this, &BrowserExtension::enableAction, this, &BrowserExtension::slotEnableAction);
0126     connect(this, &BrowserExtension::setActionText, this, &BrowserExtension::slotSetActionText);
0127 }
0128 
0129 BrowserExtension::~BrowserExtension()
0130 {
0131     // qDebug() << "BrowserExtension::~BrowserExtension() " << this;
0132 }
0133 
0134 void BrowserExtension::setBrowserArguments(const BrowserArguments &args)
0135 {
0136     d->m_browserArgs = args;
0137 }
0138 
0139 BrowserArguments BrowserExtension::browserArguments() const
0140 {
0141     return d->m_browserArgs;
0142 }
0143 
0144 int BrowserExtension::xOffset()
0145 {
0146     return 0;
0147 }
0148 
0149 int BrowserExtension::yOffset()
0150 {
0151     return 0;
0152 }
0153 
0154 void BrowserExtension::saveState(QDataStream &stream)
0155 {
0156     // TODO add d->m_part->mimeType()
0157     stream << d->m_part->url() << static_cast<qint32>(xOffset()) << static_cast<qint32>(yOffset());
0158 }
0159 
0160 void BrowserExtension::restoreState(QDataStream &stream)
0161 {
0162     QUrl u;
0163     qint32 xOfs;
0164     qint32 yOfs;
0165     stream >> u >> xOfs >> yOfs;
0166 
0167     OpenUrlArguments args;
0168     args.setXOffset(xOfs);
0169     args.setYOffset(yOfs);
0170     // TODO add args.setMimeType
0171     d->m_part->setArguments(args);
0172     d->m_part->openUrl(u);
0173 }
0174 
0175 bool BrowserExtension::isURLDropHandlingEnabled() const
0176 {
0177     return d->m_urlDropHandlingEnabled;
0178 }
0179 
0180 void BrowserExtension::setURLDropHandlingEnabled(bool enable)
0181 {
0182     d->m_urlDropHandlingEnabled = enable;
0183 }
0184 
0185 void BrowserExtension::slotCompleted()
0186 {
0187     // empty the argument stuff, to avoid bogus/invalid values when opening a new url
0188     setBrowserArguments(BrowserArguments());
0189 }
0190 
0191 void BrowserExtension::pasteRequest()
0192 {
0193     QString plain(QStringLiteral("plain"));
0194     QString url = QApplication::clipboard()->text(plain, QClipboard::Selection).trimmed();
0195     // Remove linefeeds and any whitespace surrounding it.
0196     url.remove(QRegularExpression(QStringLiteral("[\\ ]*\\n+[\\ ]*")));
0197 
0198     // Check if it's a URL
0199     QStringList filters = KUriFilter::self()->pluginNames();
0200     filters.removeAll(QStringLiteral("kuriikwsfilter"));
0201     filters.removeAll(QStringLiteral("localdomainurifilter"));
0202     KUriFilterData filterData;
0203     filterData.setData(url);
0204     filterData.setCheckForExecutables(false);
0205     if (KUriFilter::self()->filterUri(filterData, filters)) {
0206         switch (filterData.uriType()) {
0207         case KUriFilterData::LocalFile:
0208         case KUriFilterData::LocalDir:
0209         case KUriFilterData::NetProtocol:
0210             slotOpenUrlRequest(filterData.uri());
0211             break;
0212         case KUriFilterData::Error:
0213             KMessageBox::error(d->m_part->widget(), filterData.errorMsg());
0214             break;
0215         default:
0216             break;
0217         }
0218     } else if (KUriFilter::self()->filterUri(filterData, QStringList(QStringLiteral("kuriikwsfilter"))) && url.length() < 250) {
0219         if (KMessageBox::questionTwoActions(d->m_part->widget(),
0220                                             i18n("<qt>Do you want to search the Internet for <b>%1</b>?</qt>", url.toHtmlEscaped()),
0221                                             i18n("Internet Search"),
0222                                             KGuiItem(i18n("&Search"), QStringLiteral("edit-find")),
0223                                             KStandardGuiItem::cancel(),
0224                                             QStringLiteral("MiddleClickSearch"))
0225             == KMessageBox::PrimaryAction) {
0226             slotOpenUrlRequest(filterData.uri());
0227         }
0228     }
0229 }
0230 
0231 void BrowserExtension::slotOpenUrlRequest(const QUrl &url, const KParts::OpenUrlArguments &args, const KParts::BrowserArguments &browserArgs)
0232 {
0233     // qDebug() << this << " BrowserExtension::slotOpenURLRequest(): url=" << url.url();
0234     BrowserExtensionPrivate::DelayedRequest req;
0235     req.m_delayedURL = url;
0236     req.m_delayedArgs = args;
0237     req.m_delayedBrowserArgs = browserArgs;
0238     d->m_requests.append(req);
0239     QTimer::singleShot(0, this, &BrowserExtension::slotEmitOpenUrlRequestDelayed);
0240 }
0241 
0242 void BrowserExtension::slotEmitOpenUrlRequestDelayed()
0243 {
0244     if (d->m_requests.isEmpty()) {
0245         return;
0246     }
0247     BrowserExtensionPrivate::DelayedRequest req = d->m_requests.front();
0248     d->m_requests.pop_front();
0249     Q_EMIT openUrlRequestDelayed(req.m_delayedURL, req.m_delayedArgs, req.m_delayedBrowserArgs);
0250     // tricky: do not do anything here! (no access to member variables, etc.)
0251 }
0252 
0253 void BrowserExtension::setBrowserInterface(BrowserInterface *impl)
0254 {
0255     d->m_browserInterface = impl;
0256 }
0257 
0258 BrowserInterface *BrowserExtension::browserInterface() const
0259 {
0260     return d->m_browserInterface;
0261 }
0262 
0263 void BrowserExtension::slotEnableAction(const char *name, bool enabled)
0264 {
0265     // qDebug() << "BrowserExtension::slotEnableAction " << name << " " << enabled;
0266     ActionNumberMap::ConstIterator it = s_actionNumberMap()->constFind(name);
0267     if (it != s_actionNumberMap()->constEnd()) {
0268         d->m_actionStatus.setBit(it.value(), enabled);
0269         // qDebug() << "BrowserExtension::slotEnableAction setting bit " << it.data() << " to " << enabled;
0270     } else {
0271         qCWarning(KPARTSLOG) << "BrowserExtension::slotEnableAction unknown action " << name;
0272     }
0273 }
0274 
0275 bool BrowserExtension::isActionEnabled(const char *name) const
0276 {
0277     int actionNumber = (*s_actionNumberMap())[name];
0278     return d->m_actionStatus[actionNumber];
0279 }
0280 
0281 void BrowserExtension::slotSetActionText(const char *name, const QString &text)
0282 {
0283     // qDebug() << "BrowserExtension::slotSetActionText " << name << " " << text;
0284     ActionNumberMap::ConstIterator it = s_actionNumberMap()->constFind(name);
0285     if (it != s_actionNumberMap()->constEnd()) {
0286         d->m_actionText[it.value()] = text;
0287     } else {
0288         qCWarning(KPARTSLOG) << "BrowserExtension::slotSetActionText unknown action " << name;
0289     }
0290 }
0291 
0292 QString BrowserExtension::actionText(const char *name) const
0293 {
0294     int actionNumber = (*s_actionNumberMap())[name];
0295     QMap<int, QString>::ConstIterator it = d->m_actionText.constFind(actionNumber);
0296     if (it != d->m_actionText.constEnd()) {
0297         return *it;
0298     }
0299     return QString();
0300 }
0301 
0302 #if KPARTS_BUILD_DEPRECATED_SINCE(5, 83)
0303 BrowserExtension::ActionSlotMap BrowserExtension::actionSlotMap()
0304 {
0305     return *actionSlotMapPtr();
0306 }
0307 #endif
0308 
0309 BrowserExtension::ActionSlotMap *BrowserExtension::actionSlotMapPtr()
0310 {
0311     if (s_actionSlotMap()->isEmpty()) {
0312         BrowserExtensionPrivate::createActionSlotMap();
0313     }
0314     return s_actionSlotMap();
0315 }
0316 
0317 BrowserExtension *BrowserExtension::childObject(QObject *obj)
0318 {
0319     return obj->findChild<KParts::BrowserExtension *>(QString(), Qt::FindDirectChildrenOnly);
0320 }
0321 
0322 #include "moc_browserextension.cpp"