blob: b0a5b230d36e67c228d29a5d9cc6844369288ae1 [file] [log] [blame]
/*
* Copyright (C) 2011 Nokia Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "PluginProcessProxy.h"
#if ENABLE(PLUGIN_PROCESS)
#include "ProcessExecutablePath.h"
#include "QtDefaultDataLocation.h"
#include <QByteArray>
#include <QCoreApplication>
#include <QDateTime>
#include <QDir>
#include <QEventLoop>
#include <QFile>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QMap>
#include <QProcess>
#include <QStringBuilder>
#include <QVariant>
#include <WebCore/FileSystem.h>
#include <wtf/OwnPtr.h>
#include <wtf/PassOwnPtr.h>
namespace WebKit {
class PluginProcessCreationParameters;
void PluginProcessProxy::platformInitializePluginProcess(PluginProcessCreationParameters&)
{
}
static PassOwnPtr<QFile> cacheFile()
{
static QString cacheFilePath = defaultDataLocation() % QStringLiteral("plugin_meta_data.json");
return adoptPtr(new QFile(cacheFilePath));
}
struct ReadResult {
enum Tag {
Empty,
Error,
Success
};
};
static ReadResult::Tag readMetaDataFromCacheFile(QJsonDocument& result)
{
OwnPtr<QFile> file = cacheFile();
if (!file->open(QFile::ReadOnly))
return ReadResult::Empty;
QByteArray data = file->readAll();
if (data.isEmpty())
return ReadResult::Empty;
QJsonParseError error;
result = QJsonDocument::fromJson(data, &error);
if (error.error != QJsonParseError::NoError || !result.isArray()) {
// Corrupted file.
file->remove();
return ReadResult::Error;
}
return ReadResult::Success;
}
static void writeToCacheFile(const QJsonArray& array)
{
OwnPtr<QFile> file = cacheFile();
if (!file->open(QFile::WriteOnly | QFile::Truncate)) {
// It should never but let's be pessimistic, it is the file system after all.
qWarning("Cannot write into plugin meta data cache file: %s\n", qPrintable(file->fileName()));
return;
}
// Don't care about write error here. We will detect it later.
file->write(QJsonDocument(array).toJson());
}
static void appendToCacheFile(const QJsonObject& object)
{
QJsonDocument jsonDocument;
ReadResult::Tag result = readMetaDataFromCacheFile(jsonDocument);
if (result == ReadResult::Error)
return;
if (result == ReadResult::Empty)
jsonDocument.setArray(QJsonArray());
QJsonArray array = jsonDocument.array();
array.append(object);
writeToCacheFile(array);
}
struct MetaDataResult {
enum Tag {
NotAvailable,
Unloadable,
Available
};
};
static MetaDataResult::Tag tryReadPluginMetaDataFromCacheFile(const QString& canonicalPluginPath, RawPluginMetaData& result)
{
QJsonDocument jsonDocument;
if (readMetaDataFromCacheFile(jsonDocument) != ReadResult::Success)
return MetaDataResult::NotAvailable;
QJsonArray array = jsonDocument.array();
QDateTime pluginLastModified = QFileInfo(canonicalPluginPath).lastModified();
for (QJsonArray::const_iterator i = array.constBegin(); i != array.constEnd(); ++i) {
QJsonValue item = *i;
if (!item.isObject()) {
cacheFile()->remove();
return MetaDataResult::NotAvailable;
}
QJsonObject object = item.toObject();
if (object.value(QStringLiteral("path")).toString() == canonicalPluginPath) {
QString timestampString = object.value(QStringLiteral("timestamp")).toString();
if (timestampString.isEmpty()) {
cacheFile()->remove();
return MetaDataResult::NotAvailable;
}
QDateTime timestamp = QDateTime::fromString(timestampString);
if (timestamp < pluginLastModified) {
// Out of date data for this plugin => remove it from the file.
array.removeAt(i.i);
writeToCacheFile(array);
return MetaDataResult::NotAvailable;
}
if (object.contains(QLatin1String("unloadable")))
return MetaDataResult::Unloadable;
// Match.
result.name = object.value(QStringLiteral("name")).toString();
result.description = object.value(QStringLiteral("description")).toString();
result.mimeDescription = object.value(QStringLiteral("mimeDescription")).toString();
if (result.mimeDescription.isEmpty()) {
// Only the mime description is mandatory.
// Don't trust in the cache file if it is empty.
cacheFile()->remove();
return MetaDataResult::NotAvailable;
}
return MetaDataResult::Available;
}
}
return MetaDataResult::NotAvailable;
}
bool PluginProcessProxy::scanPlugin(const String& pluginPath, RawPluginMetaData& result)
{
QFileInfo pluginFileInfo(pluginPath);
if (!pluginFileInfo.exists())
return false;
MetaDataResult::Tag metaDataResult = tryReadPluginMetaDataFromCacheFile(pluginFileInfo.canonicalFilePath(), result);
if (metaDataResult == MetaDataResult::Available)
return true;
if (metaDataResult == MetaDataResult::Unloadable)
return false;
// Scan the plugin via the plugin process.
QString commandLine = QString(executablePathOfPluginProcess()) % QLatin1Char(' ')
% QStringLiteral("-scanPlugin") % QLatin1Char(' ') % pluginFileInfo.canonicalFilePath();
QProcess process;
process.setReadChannel(QProcess::StandardOutput);
process.start(commandLine);
bool ranSuccessfully = process.waitForFinished()
&& process.exitStatus() == QProcess::NormalExit
&& process.exitCode() == EXIT_SUCCESS;
if (ranSuccessfully) {
QByteArray outputBytes = process.readAll();
ASSERT(!(outputBytes.size() % sizeof(UChar)));
String output(reinterpret_cast<const UChar*>(outputBytes.constData()), outputBytes.size() / sizeof(UChar));
Vector<String> lines;
output.split(UChar('\n'), lines);
ASSERT(lines.size() == 3);
result.name.swap(lines[0]);
result.description.swap(lines[1]);
result.mimeDescription.swap(lines[2]);
} else
process.kill();
QVariantMap map;
map[QStringLiteral("path")] = QString(pluginFileInfo.canonicalFilePath());
map[QStringLiteral("timestamp")] = QDateTime::currentDateTime().toString();
if (!ranSuccessfully || result.mimeDescription.isEmpty()) {
// We failed getting the meta data in some way. Cache this information, so we don't
// need to rescan such plugins every time. We will retry it once the plugin is updated.
map[QStringLiteral("unloadable")] = QStringLiteral("true");
appendToCacheFile(QJsonObject::fromVariantMap(map));
return false;
}
map[QStringLiteral("name")] = QString(result.name);
map[QStringLiteral("description")] = QString(result.description);
map[QStringLiteral("mimeDescription")] = QString(result.mimeDescription);
appendToCacheFile(QJsonObject::fromVariantMap(map));
return true;
}
} // namespace WebKit
#endif // ENABLE(PLUGIN_PROCESS)