You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
431 lines
14 KiB
431 lines
14 KiB
/* |
|
SPDX-FileCopyrightText: 2008 Pino Toscano <pino@kde.org> |
|
SPDX-FileCopyrightText: 2008 Harri Porten <porten@kde.org> |
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later |
|
*/ |
|
|
|
#include "kjs_app_p.h" |
|
|
|
#include <kjs/kjsarguments.h> |
|
#include <kjs/kjsinterpreter.h> |
|
#include <kjs/kjsobject.h> |
|
#include <kjs/kjsprototype.h> |
|
#include <kjs_version.h> |
|
|
|
#include <QApplication> |
|
|
|
#include <QLocale> |
|
#include <QTimer> |
|
|
|
#include <KLocalizedString> |
|
#include <QCheckBox> |
|
#include <QMessageBox> |
|
|
|
#include "../document_p.h" |
|
#include "../scripter.h" |
|
#include "config-okular.h" |
|
#include "kjs_fullscreen_p.h" |
|
|
|
using namespace Okular; |
|
|
|
#define OKULAR_TIMERID QStringLiteral("okular_timerID") |
|
|
|
static KJSPrototype *g_appProto; |
|
typedef QHash<int, QTimer *> TimerCache; |
|
Q_GLOBAL_STATIC(TimerCache, g_timerCache) |
|
|
|
// the acrobat version we fake |
|
static const double fake_acroversion = 8.00; |
|
|
|
static const struct FakePluginInfo { |
|
const char *name; |
|
bool certified; |
|
bool loaded; |
|
const char *path; |
|
} s_fake_plugins[] = {{"Annots", true, true, ""}, {"EFS", true, true, ""}, {"EScript", true, true, ""}, {"Forms", true, true, ""}, {"ReadOutLoud", true, true, ""}, {"WebLink", true, true, ""}}; |
|
static const int s_num_fake_plugins = sizeof(s_fake_plugins) / sizeof(s_fake_plugins[0]); |
|
|
|
static KJSObject appGetFormsVersion(KJSContext *, void *) |
|
{ |
|
// faking a bit... |
|
return KJSNumber(fake_acroversion); |
|
} |
|
|
|
static KJSObject appGetLanguage(KJSContext *, void *) |
|
{ |
|
QLocale locale; |
|
QString lang = QLocale::languageToString(locale.language()); |
|
QString country = QLocale::countryToString(locale.country()); |
|
QString acroLang = QStringLiteral("ENU"); |
|
if (lang == QLatin1String("da")) |
|
acroLang = QStringLiteral("DAN"); // Danish |
|
else if (lang == QLatin1String("de")) |
|
acroLang = QStringLiteral("DEU"); // German |
|
else if (lang == QLatin1String("en")) |
|
acroLang = QStringLiteral("ENU"); // English |
|
else if (lang == QLatin1String("es")) |
|
acroLang = QStringLiteral("ESP"); // Spanish |
|
else if (lang == QLatin1String("fr")) |
|
acroLang = QStringLiteral("FRA"); // French |
|
else if (lang == QLatin1String("it")) |
|
acroLang = QStringLiteral("ITA"); // Italian |
|
else if (lang == QLatin1String("ko")) |
|
acroLang = QStringLiteral("KOR"); // Korean |
|
else if (lang == QLatin1String("ja")) |
|
acroLang = QStringLiteral("JPN"); // Japanese |
|
else if (lang == QLatin1String("nl")) |
|
acroLang = QStringLiteral("NLD"); // Dutch |
|
else if (lang == QLatin1String("pt") && country == QLatin1String("BR")) |
|
acroLang = QStringLiteral("PTB"); // Brazilian Portuguese |
|
else if (lang == QLatin1String("fi")) |
|
acroLang = QStringLiteral("SUO"); // Finnish |
|
else if (lang == QLatin1String("sv")) |
|
acroLang = QStringLiteral("SVE"); // Swedish |
|
else if (lang == QLatin1String("zh") && country == QLatin1String("CN")) |
|
acroLang = QStringLiteral("CHS"); // Chinese Simplified |
|
else if (lang == QLatin1String("zh") && country == QLatin1String("TW")) |
|
acroLang = QStringLiteral("CHT"); // Chinese Traditional |
|
return KJSString(acroLang); |
|
} |
|
|
|
static KJSObject appGetNumPlugins(KJSContext *, void *) |
|
{ |
|
return KJSNumber(s_num_fake_plugins); |
|
} |
|
|
|
static KJSObject appGetPlatform(KJSContext *, void *) |
|
{ |
|
#if defined(Q_OS_WIN) |
|
return KJSString(QString::fromLatin1("WIN")); |
|
#elif defined(Q_OS_MAC) |
|
return KJSString(QString::fromLatin1("MAC")); |
|
#else |
|
return KJSString(QStringLiteral("UNIX")); |
|
#endif |
|
} |
|
|
|
static KJSObject appGetPlugIns(KJSContext *context, void *) |
|
{ |
|
KJSArray plugins(context, s_num_fake_plugins); |
|
for (int i = 0; i < s_num_fake_plugins; ++i) { |
|
const FakePluginInfo &info = s_fake_plugins[i]; |
|
KJSObject plugin; |
|
plugin.setProperty(context, QStringLiteral("certified"), info.certified); |
|
plugin.setProperty(context, QStringLiteral("loaded"), info.loaded); |
|
plugin.setProperty(context, QStringLiteral("name"), info.name); |
|
plugin.setProperty(context, QStringLiteral("path"), info.path); |
|
plugin.setProperty(context, QStringLiteral("version"), fake_acroversion); |
|
plugins.setProperty(context, QString::number(i), plugin); |
|
} |
|
return plugins; |
|
} |
|
|
|
static KJSObject appGetPrintColorProfiles(KJSContext *context, void *) |
|
{ |
|
return KJSArray(context, 0); |
|
} |
|
|
|
static KJSObject appGetPrinterNames(KJSContext *context, void *) |
|
{ |
|
return KJSArray(context, 0); |
|
} |
|
|
|
static KJSObject appGetViewerType(KJSContext *, void *) |
|
{ |
|
// faking a bit... |
|
return KJSString(QStringLiteral("Reader")); |
|
} |
|
|
|
static KJSObject appGetViewerVariation(KJSContext *, void *) |
|
{ |
|
// faking a bit... |
|
return KJSString(QStringLiteral("Reader")); |
|
} |
|
|
|
static KJSObject appGetViewerVersion(KJSContext *, void *) |
|
{ |
|
// faking a bit... |
|
return KJSNumber(fake_acroversion); |
|
} |
|
|
|
/* |
|
Alert function defined in the reference, it shows a Dialog Box with options. |
|
app.alert() |
|
*/ |
|
static KJSObject appAlert(KJSContext *context, void *, const KJSArguments &arguments) |
|
{ |
|
if (arguments.count() < 1) { |
|
return context->throwException(i18n("Missing alert type")); |
|
} |
|
KJSObject cMsg, nIcon, nType, cTitle, oCheckbox; |
|
|
|
if (arguments.at(0).isObject()) { |
|
KJSObject obj = arguments.at(0); |
|
cMsg = obj.property(context, QStringLiteral("cMsg")); |
|
nIcon = obj.property(context, QStringLiteral("nIcon")); |
|
nType = obj.property(context, QStringLiteral("nType")); |
|
cTitle = obj.property(context, QStringLiteral("cTitle")); |
|
oCheckbox = obj.property(context, QStringLiteral("oCheckbox")); |
|
} else { |
|
cMsg = arguments.at(0); |
|
nIcon = arguments.at(1); |
|
nType = arguments.at(2); |
|
cTitle = arguments.at(3); |
|
oCheckbox = arguments.at(5); |
|
} |
|
|
|
QMessageBox::Icon icon = QMessageBox::Critical; |
|
if (nIcon.isNumber()) { |
|
switch (nIcon.toInt32(context)) { |
|
case 0: |
|
icon = QMessageBox::Critical; |
|
break; |
|
case 1: |
|
icon = QMessageBox::Warning; |
|
break; |
|
case 2: |
|
icon = QMessageBox::Question; |
|
break; |
|
case 3: |
|
icon = QMessageBox::Information; |
|
break; |
|
} |
|
} |
|
|
|
const QString title = cTitle.isString() ? cTitle.toString(context) : QStringLiteral("Okular"); |
|
QMessageBox box(icon, title, cMsg.toString(context)); |
|
|
|
QMessageBox::StandardButtons buttons = QMessageBox::Ok; |
|
if (nType.isNumber()) { |
|
switch (nType.toInt32(context)) { |
|
case 0: |
|
buttons = QMessageBox::Ok; |
|
break; |
|
case 1: |
|
buttons = QMessageBox::Ok | QMessageBox::Cancel; |
|
break; |
|
case 2: |
|
buttons = QMessageBox::Yes | QMessageBox::No; |
|
break; |
|
case 3: |
|
buttons = QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel; |
|
break; |
|
} |
|
} |
|
box.setStandardButtons(buttons); |
|
|
|
QCheckBox *checkBox = nullptr; |
|
if (oCheckbox.isObject()) { |
|
KJSObject oMsg = oCheckbox.property(context, QStringLiteral("cMsg")); |
|
QString msg = i18n("Do not show this message again"); |
|
|
|
if (oMsg.isString()) |
|
msg = oMsg.toString(context); |
|
|
|
bool bInitialValue = false; |
|
KJSObject value = oCheckbox.property(context, QStringLiteral("bInitialValue")); |
|
if (value.isBoolean()) |
|
bInitialValue = value.toBoolean(context); |
|
checkBox = new QCheckBox(msg); |
|
checkBox->setChecked(bInitialValue); |
|
box.setCheckBox(checkBox); |
|
} |
|
|
|
#if KJS_VERSION > QT_VERSION_CHECK(5, 71, 0) |
|
// halt timeout until the user has responded |
|
context->interpreter().stopTimeoutCheck(); |
|
#endif |
|
|
|
int button = box.exec(); |
|
|
|
#if KJS_VERSION > QT_VERSION_CHECK(5, 71, 0) |
|
// restart max allowed time |
|
context->interpreter().startTimeoutCheck(); |
|
#endif |
|
|
|
int ret; |
|
|
|
switch (button) { |
|
case QMessageBox::Ok: |
|
ret = 1; |
|
break; |
|
case QMessageBox::Cancel: |
|
ret = 2; |
|
break; |
|
case QMessageBox::No: |
|
ret = 3; |
|
break; |
|
case QMessageBox::Yes: |
|
ret = 4; |
|
break; |
|
} |
|
|
|
if (checkBox) |
|
oCheckbox.setProperty(context, QStringLiteral("bAfterValue"), checkBox->isChecked()); |
|
|
|
delete checkBox; |
|
|
|
return KJSNumber(ret); |
|
} |
|
|
|
static KJSObject appBeep(KJSContext *context, void *, const KJSArguments &arguments) |
|
{ |
|
if (arguments.count() < 1) { |
|
return context->throwException(QStringLiteral("Missing beep type")); |
|
} |
|
QApplication::beep(); |
|
return KJSUndefined(); |
|
} |
|
|
|
static KJSObject appGetNthPlugInName(KJSContext *context, void *, const KJSArguments &arguments) |
|
{ |
|
if (arguments.count() < 1) { |
|
return context->throwException(QStringLiteral("Missing plugin index")); |
|
} |
|
const int nIndex = arguments.at(0).toInt32(context); |
|
|
|
if (nIndex < 0 || nIndex >= s_num_fake_plugins) |
|
return context->throwException(QStringLiteral("PlugIn index out of bounds")); |
|
|
|
const FakePluginInfo &info = s_fake_plugins[nIndex]; |
|
return KJSString(info.name); |
|
} |
|
|
|
static KJSObject appGoBack(KJSContext *, void *object, const KJSArguments &) |
|
{ |
|
const DocumentPrivate *doc = reinterpret_cast<DocumentPrivate *>(object); |
|
if (doc->m_parent->historyAtBegin()) |
|
return KJSUndefined(); |
|
|
|
doc->m_parent->setPrevViewport(); |
|
return KJSUndefined(); |
|
} |
|
|
|
static KJSObject appGoForward(KJSContext *, void *object, const KJSArguments &) |
|
{ |
|
const DocumentPrivate *doc = reinterpret_cast<DocumentPrivate *>(object); |
|
if (doc->m_parent->historyAtEnd()) |
|
return KJSUndefined(); |
|
|
|
doc->m_parent->setNextViewport(); |
|
return KJSUndefined(); |
|
} |
|
|
|
// app.setInterval() |
|
static KJSObject appSetInterval(KJSContext *ctx, void *object, const KJSArguments &arguments) |
|
{ |
|
DocumentPrivate *doc = reinterpret_cast<DocumentPrivate *>(object); |
|
const QString function = arguments.at(0).toString(ctx) + ';'; |
|
const int interval = arguments.at(1).toInt32(ctx); |
|
|
|
QTimer *timer = new QTimer(); |
|
|
|
QObject::connect(timer, &QTimer::timeout, doc->m_parent, [=]() { doc->executeScript(function); }); |
|
|
|
timer->start(interval); |
|
|
|
return JSApp::wrapTimer(ctx, timer); |
|
} |
|
|
|
// app.clearInterval() |
|
static KJSObject appClearInterval(KJSContext *ctx, void *, const KJSArguments &arguments) |
|
{ |
|
KJSObject timerObject = arguments.at(0); |
|
const int timerId = timerObject.property(ctx, OKULAR_TIMERID).toInt32(ctx); |
|
QTimer *timer = g_timerCache->value(timerId); |
|
if (timer != nullptr) { |
|
timer->stop(); |
|
g_timerCache->remove(timerId); |
|
delete timer; |
|
} |
|
|
|
return KJSUndefined(); |
|
} |
|
|
|
// app.setTimeOut() |
|
static KJSObject appSetTimeOut(KJSContext *ctx, void *object, const KJSArguments &arguments) |
|
{ |
|
DocumentPrivate *doc = reinterpret_cast<DocumentPrivate *>(object); |
|
const QString function = arguments.at(0).toString(ctx) + ';'; |
|
const int interval = arguments.at(1).toInt32(ctx); |
|
|
|
QTimer *timer = new QTimer(); |
|
timer->setSingleShot(true); |
|
|
|
QObject::connect(timer, &QTimer::timeout, doc->m_parent, [=]() { doc->executeScript(function); }); |
|
|
|
timer->start(interval); |
|
|
|
return JSApp::wrapTimer(ctx, timer); |
|
} |
|
|
|
// app.clearTimeOut() |
|
static KJSObject appClearTimeOut(KJSContext *ctx, void *, const KJSArguments &arguments) |
|
{ |
|
KJSObject timerObject = arguments.at(0); |
|
const int timerId = timerObject.property(ctx, OKULAR_TIMERID).toInt32(ctx); |
|
QTimer *timer = g_timerCache->value(timerId); |
|
|
|
if (timer != nullptr) { |
|
timer->stop(); |
|
g_timerCache->remove(timerId); |
|
delete timer; |
|
} |
|
|
|
return KJSUndefined(); |
|
} |
|
|
|
void JSApp::initType(KJSContext *ctx) |
|
{ |
|
static bool initialized = false; |
|
if (initialized) |
|
return; |
|
initialized = true; |
|
|
|
g_appProto = new KJSPrototype(); |
|
|
|
g_appProto->defineProperty(ctx, QStringLiteral("formsVersion"), appGetFormsVersion); |
|
g_appProto->defineProperty(ctx, QStringLiteral("language"), appGetLanguage); |
|
g_appProto->defineProperty(ctx, QStringLiteral("numPlugIns"), appGetNumPlugins); |
|
g_appProto->defineProperty(ctx, QStringLiteral("platform"), appGetPlatform); |
|
g_appProto->defineProperty(ctx, QStringLiteral("plugIns"), appGetPlugIns); |
|
g_appProto->defineProperty(ctx, QStringLiteral("printColorProfiles"), appGetPrintColorProfiles); |
|
g_appProto->defineProperty(ctx, QStringLiteral("printerNames"), appGetPrinterNames); |
|
g_appProto->defineProperty(ctx, QStringLiteral("viewerType"), appGetViewerType); |
|
g_appProto->defineProperty(ctx, QStringLiteral("viewerVariation"), appGetViewerVariation); |
|
g_appProto->defineProperty(ctx, QStringLiteral("viewerVersion"), appGetViewerVersion); |
|
|
|
g_appProto->defineFunction(ctx, QStringLiteral("alert"), appAlert); |
|
g_appProto->defineFunction(ctx, QStringLiteral("beep"), appBeep); |
|
g_appProto->defineFunction(ctx, QStringLiteral("getNthPlugInName"), appGetNthPlugInName); |
|
g_appProto->defineFunction(ctx, QStringLiteral("goBack"), appGoBack); |
|
g_appProto->defineFunction(ctx, QStringLiteral("goForward"), appGoForward); |
|
g_appProto->defineFunction(ctx, QStringLiteral("setInterval"), appSetInterval); |
|
g_appProto->defineFunction(ctx, QStringLiteral("clearInterval"), appClearInterval); |
|
g_appProto->defineFunction(ctx, QStringLiteral("setTimeOut"), appSetTimeOut); |
|
g_appProto->defineFunction(ctx, QStringLiteral("clearTimeOut"), appClearTimeOut); |
|
} |
|
|
|
KJSObject JSApp::object(KJSContext *ctx, DocumentPrivate *doc) |
|
{ |
|
return g_appProto->constructObject(ctx, doc); |
|
} |
|
|
|
KJSObject JSApp::wrapTimer(KJSContext *ctx, QTimer *timer) |
|
{ |
|
KJSObject timerObject = g_appProto->constructObject(ctx, timer); |
|
timerObject.setProperty(ctx, OKULAR_TIMERID, timer->timerId()); |
|
|
|
g_timerCache->insert(timer->timerId(), timer); |
|
|
|
return timerObject; |
|
} |
|
|
|
void JSApp::clearCachedFields() |
|
{ |
|
if (g_timerCache) { |
|
qDeleteAll(g_timerCache->begin(), g_timerCache->end()); |
|
g_timerCache->clear(); |
|
} |
|
}
|
|
|