2 * Copyright (C) 2011 Nokia Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
27 #include "PluginProcessProxy.h"
29 #if ENABLE(PLUGIN_PROCESS)
31 #include "ProcessExecutablePath.h"
32 #include "QtDefaultDataLocation.h"
34 #include <QCoreApplication>
40 #include <QJsonDocument>
41 #include <QJsonObject>
44 #include <QStringBuilder>
46 #include <WebCore/FileSystem.h>
47 #include <wtf/OwnPtr.h>
48 #include <wtf/PassOwnPtr.h>
52 class PluginProcessCreationParameters;
54 void PluginProcessProxy::platformInitializeLaunchOptions(ProcessLauncher::LaunchOptions& launchOptions, const PluginModuleInfo& pluginInfo)
56 launchOptions.extraInitializationData.add("plugin-path", pluginInfo.path);
59 void PluginProcessProxy::platformInitializePluginProcess(PluginProcessCreationParameters&)
63 static PassOwnPtr<QFile> cacheFile()
65 static QString cacheFilePath = defaultDataLocation() % QStringLiteral("plugin_meta_data.json");
66 return adoptPtr(new QFile(cacheFilePath));
77 static ReadResult::Tag readMetaDataFromCacheFile(QJsonDocument& result)
79 OwnPtr<QFile> file = cacheFile();
80 if (!file->open(QFile::ReadOnly))
81 return ReadResult::Empty;
82 QByteArray data = file->readAll();
84 return ReadResult::Empty;
86 QJsonParseError error;
87 result = QJsonDocument::fromJson(data, &error);
88 if (error.error != QJsonParseError::NoError || !result.isArray()) {
91 return ReadResult::Error;
94 return ReadResult::Success;
97 static void writeToCacheFile(const QJsonArray& array)
99 OwnPtr<QFile> file = cacheFile();
100 if (!file->open(QFile::WriteOnly | QFile::Truncate)) {
101 // It should never but let's be pessimistic, it is the file system after all.
102 qWarning("Cannot write into plugin meta data cache file: %s\n", qPrintable(file->fileName()));
106 // Don't care about write error here. We will detect it later.
107 file->write(QJsonDocument(array).toJson());
110 static void appendToCacheFile(const QJsonObject& object)
112 QJsonDocument jsonDocument;
113 ReadResult::Tag result = readMetaDataFromCacheFile(jsonDocument);
114 if (result == ReadResult::Error)
116 if (result == ReadResult::Empty)
117 jsonDocument.setArray(QJsonArray());
119 QJsonArray array = jsonDocument.array();
120 array.append(object);
121 writeToCacheFile(array);
124 struct MetaDataResult {
132 static MetaDataResult::Tag tryReadPluginMetaDataFromCacheFile(const QString& canonicalPluginPath, RawPluginMetaData& result)
134 QJsonDocument jsonDocument;
135 if (readMetaDataFromCacheFile(jsonDocument) != ReadResult::Success)
136 return MetaDataResult::NotAvailable;
138 QJsonArray array = jsonDocument.array();
139 QDateTime pluginLastModified = QFileInfo(canonicalPluginPath).lastModified();
140 for (QJsonArray::const_iterator i = array.constBegin(); i != array.constEnd(); ++i) {
141 QJsonValue item = *i;
142 if (!item.isObject()) {
143 cacheFile()->remove();
144 return MetaDataResult::NotAvailable;
147 QJsonObject object = item.toObject();
148 if (object.value(QStringLiteral("path")).toString() == canonicalPluginPath) {
149 QString timestampString = object.value(QStringLiteral("timestamp")).toString();
150 if (timestampString.isEmpty()) {
151 cacheFile()->remove();
152 return MetaDataResult::NotAvailable;
154 QDateTime timestamp = QDateTime::fromString(timestampString);
155 if (timestamp < pluginLastModified) {
156 // Out of date data for this plugin => remove it from the file.
158 writeToCacheFile(array);
159 return MetaDataResult::NotAvailable;
162 if (object.contains(QLatin1String("unloadable")))
163 return MetaDataResult::Unloadable;
166 result.name = object.value(QStringLiteral("name")).toString();
167 result.description = object.value(QStringLiteral("description")).toString();
168 result.mimeDescription = object.value(QStringLiteral("mimeDescription")).toString();
169 if (result.mimeDescription.isEmpty()) {
170 // Only the mime description is mandatory.
171 // Don't trust in the cache file if it is empty.
172 cacheFile()->remove();
173 return MetaDataResult::NotAvailable;
176 return MetaDataResult::Available;
180 return MetaDataResult::NotAvailable;
183 bool PluginProcessProxy::scanPlugin(const String& pluginPath, RawPluginMetaData& result)
185 QFileInfo pluginFileInfo(pluginPath);
186 if (!pluginFileInfo.exists())
189 MetaDataResult::Tag metaDataResult = tryReadPluginMetaDataFromCacheFile(pluginFileInfo.canonicalFilePath(), result);
190 if (metaDataResult == MetaDataResult::Available)
192 if (metaDataResult == MetaDataResult::Unloadable)
195 // Scan the plugin via the plugin process.
196 QString commandLine = QString(executablePathOfPluginProcess()) % QLatin1Char(' ')
197 % QStringLiteral("-scanPlugin") % QLatin1Char(' ') % pluginFileInfo.canonicalFilePath();
199 process.setReadChannel(QProcess::StandardOutput);
200 process.start(commandLine);
202 bool ranSuccessfully = process.waitForFinished()
203 && process.exitStatus() == QProcess::NormalExit
204 && process.exitCode() == EXIT_SUCCESS;
205 if (ranSuccessfully) {
206 QByteArray outputBytes = process.readAll();
207 ASSERT(!(outputBytes.size() % sizeof(UChar)));
209 String output(reinterpret_cast<const UChar*>(outputBytes.constData()), outputBytes.size() / sizeof(UChar));
210 Vector<String> lines;
211 output.split(UChar('\n'), true, lines);
212 ASSERT(lines.size() == 4 && lines.last().isEmpty());
214 result.name.swap(lines[0]);
215 result.description.swap(lines[1]);
216 result.mimeDescription.swap(lines[2]);
221 map[QStringLiteral("path")] = QString(pluginFileInfo.canonicalFilePath());
222 map[QStringLiteral("timestamp")] = QDateTime::currentDateTime().toString();
224 if (!ranSuccessfully || result.mimeDescription.isEmpty()) {
225 // We failed getting the meta data in some way. Cache this information, so we don't
226 // need to rescan such plugins every time. We will retry it once the plugin is updated.
228 map[QStringLiteral("unloadable")] = QStringLiteral("true");
229 appendToCacheFile(QJsonObject::fromVariantMap(map));
233 map[QStringLiteral("name")] = QString(result.name);
234 map[QStringLiteral("description")] = QString(result.description);
235 map[QStringLiteral("mimeDescription")] = QString(result.mimeDescription);
236 appendToCacheFile(QJsonObject::fromVariantMap(map));
240 } // namespace WebKit
242 #endif // ENABLE(PLUGIN_PROCESS)