Title: [120205] trunk/Source/WebKit2
Revision
120205
Author
[email protected]
Date
2012-06-13 07:39:32 -0700 (Wed, 13 Jun 2012)

Log Message

[Qt][WK2] Scanning plugins blocks the UI for a long time
https://bugs.webkit.org/show_bug.cgi?id=88535

Reviewed by Simon Hausmann.

Implement a persistent cache for the meta data of plugins.
This way the UI process will not block for too long when
it's time to scan the plugins (except the first time).
The cache is a json file stored in a standard hidden cache
directory.

* Shared/qt/QtDefaultDataLocation.cpp: Added.
(WebKit):
(WebKit::defaultDataLocation):
* Shared/qt/QtDefaultDataLocation.h: Added.
(WebKit):
Added a common helper for the path we use to
put stuff into.

* Target.pri:
* UIProcess/Plugins/qt/PluginProcessProxyQt.cpp:
(WebKit::cacheFile):
(WebKit):
(WebKit::readMetaDataFromCacheFile):
(WebKit::writeToCacheFile):
(WebKit::appendToCacheFile):
(WebKit::tryReadPluginMetaDataFromCacheFile):
(WebKit::PluginProcessProxy::scanPlugin):
* UIProcess/qt/WebContextQt.cpp:

Modified Paths

Added Paths

Diff

Modified: trunk/Source/WebKit2/ChangeLog (120204 => 120205)


--- trunk/Source/WebKit2/ChangeLog	2012-06-13 14:37:49 UTC (rev 120204)
+++ trunk/Source/WebKit2/ChangeLog	2012-06-13 14:39:32 UTC (rev 120205)
@@ -1,3 +1,35 @@
+2012-06-13  Balazs Kelemen  <[email protected]>
+
+        [Qt][WK2] Scanning plugins blocks the UI for a long time
+        https://bugs.webkit.org/show_bug.cgi?id=88535
+
+        Reviewed by Simon Hausmann.
+
+        Implement a persistent cache for the meta data of plugins.
+        This way the UI process will not block for too long when
+        it's time to scan the plugins (except the first time).
+        The cache is a json file stored in a standard hidden cache
+        directory.
+
+        * Shared/qt/QtDefaultDataLocation.cpp: Added.
+        (WebKit):
+        (WebKit::defaultDataLocation):
+        * Shared/qt/QtDefaultDataLocation.h: Added.
+        (WebKit):
+        Added a common helper for the path we use to
+        put stuff into.
+
+        * Target.pri:
+        * UIProcess/Plugins/qt/PluginProcessProxyQt.cpp:
+        (WebKit::cacheFile):
+        (WebKit):
+        (WebKit::readMetaDataFromCacheFile):
+        (WebKit::writeToCacheFile):
+        (WebKit::appendToCacheFile):
+        (WebKit::tryReadPluginMetaDataFromCacheFile):
+        (WebKit::PluginProcessProxy::scanPlugin):
+        * UIProcess/qt/WebContextQt.cpp:
+
 2012-06-13  Kenneth Rohde Christiansen  <[email protected]>
 
         [Qt] Do not set contents pos to the current position

Added: trunk/Source/WebKit2/Shared/qt/QtDefaultDataLocation.cpp (0 => 120205)


--- trunk/Source/WebKit2/Shared/qt/QtDefaultDataLocation.cpp	                        (rev 0)
+++ trunk/Source/WebKit2/Shared/qt/QtDefaultDataLocation.cpp	2012-06-13 14:39:32 UTC (rev 120205)
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ * Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies)
+ * Copyright (C) 2012 University of Szeged.  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 "QtDefaultDataLocation.h"
+
+#include <QCoreApplication>
+#include <QDir>
+#include <QStandardPaths>
+#include <QStringBuilder>
+#include <WebCore/FileSystem.h>
+
+namespace WebKit {
+
+QString defaultDataLocation()
+{
+    static QString s_dataLocation;
+
+    if (!s_dataLocation.isEmpty())
+        return s_dataLocation;
+
+    QString dataLocation = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
+    if (dataLocation.isEmpty())
+        dataLocation = QDir::homePath() % QDir::separator() % QCoreApplication::applicationName();
+    s_dataLocation = dataLocation % QDir::separator() % QStringLiteral(".QtWebKit") % QDir::separator();
+    QDir::root().mkpath(s_dataLocation);
+
+    return s_dataLocation;
+}
+
+}

Added: trunk/Source/WebKit2/Shared/qt/QtDefaultDataLocation.h (0 => 120205)


--- trunk/Source/WebKit2/Shared/qt/QtDefaultDataLocation.h	                        (rev 0)
+++ trunk/Source/WebKit2/Shared/qt/QtDefaultDataLocation.h	2012-06-13 14:39:32 UTC (rev 120205)
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ * Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies)
+ * Copyright (C) 2012 University of Szeged.  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.
+ */
+
+#ifndef QtDefaultDataLocation_h
+#define QtDefaultDataLocation_h
+
+#include <QString>
+
+namespace WebKit {
+
+QString defaultDataLocation();
+
+}
+
+#endif

Modified: trunk/Source/WebKit2/Target.pri (120204 => 120205)


--- trunk/Source/WebKit2/Target.pri	2012-06-13 14:37:49 UTC (rev 120204)
+++ trunk/Source/WebKit2/Target.pri	2012-06-13 14:39:32 UTC (rev 120205)
@@ -137,6 +137,7 @@
     Shared/qt/ArgumentCodersQt.h \
     Shared/qt/PlatformCertificateInfo.h \
     Shared/qt/WebEventFactoryQt.h \
+    Shared/qt/QtDefaultDataLocation.h \
     Shared/qt/QtNetworkReplyData.h \
     Shared/qt/QtNetworkRequestData.h \
     UIProcess/API/C/WKAPICast.h \
@@ -489,6 +490,7 @@
     Shared/qt/ProcessExecutablePathQt.cpp \
     Shared/qt/WebCoreArgumentCodersQt.cpp \
     Shared/qt/WebEventFactoryQt.cpp \
+    Shared/qt/QtDefaultDataLocation.cpp \
     Shared/qt/QtNetworkReplyData.cpp \
     Shared/qt/QtNetworkRequestData.cpp \
     Shared/qt/WebURLRequestQt.cpp \

Modified: trunk/Source/WebKit2/UIProcess/Plugins/qt/PluginProcessProxyQt.cpp (120204 => 120205)


--- trunk/Source/WebKit2/UIProcess/Plugins/qt/PluginProcessProxyQt.cpp	2012-06-13 14:37:49 UTC (rev 120204)
+++ trunk/Source/WebKit2/UIProcess/Plugins/qt/PluginProcessProxyQt.cpp	2012-06-13 14:39:32 UTC (rev 120205)
@@ -29,12 +29,23 @@
 #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 <QString>
+#include <QStringBuilder>
+#include <QVariant>
+#include <WebCore/FileSystem.h>
+#include <wtf/OwnPtr.h>
+#include <wtf/PassOwnPtr.h>
 
 namespace WebKit {
 
@@ -44,35 +55,181 @@
 {
 }
 
+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 = ""
+    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)
 {
-    QString commandLine = QLatin1String("%1 %2 %3");
-    commandLine = commandLine.arg(executablePathOfPluginProcess());
-    commandLine = commandLine.arg(QStringLiteral("-scanPlugin")).arg(static_cast<QString>(pluginPath));
+    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);
 
-    if (!process.waitForFinished()
-        || process.exitStatus() != QProcess::NormalExit
-        || process.exitCode() != EXIT_SUCCESS) {
+    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;
     }
 
-    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]);
-    return !result.mimeDescription.isEmpty();
+    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

Modified: trunk/Source/WebKit2/UIProcess/qt/WebContextQt.cpp (120204 => 120205)


--- trunk/Source/WebKit2/UIProcess/qt/WebContextQt.cpp	2012-06-13 14:37:49 UTC (rev 120204)
+++ trunk/Source/WebKit2/UIProcess/qt/WebContextQt.cpp	2012-06-13 14:39:32 UTC (rev 120205)
@@ -29,33 +29,18 @@
 
 #include "ApplicationCacheStorage.h"
 #include "FileSystem.h"
+#include "QtDefaultDataLocation.h"
 #include "WKSharedAPICast.h"
 #if ENABLE(GEOLOCATION)
 #include "WebGeolocationProviderQt.h"
 #endif
 #include "WebProcessCreationParameters.h"
 #include <QCoreApplication>
-#include <QStandardPaths>
 #include <QDir>
 #include <QProcess>
 
 namespace WebKit {
 
-static QString defaultDataLocation()
-{
-    static QString s_dataLocation;
-
-    if (!s_dataLocation.isEmpty())
-        return s_dataLocation;
-
-    QString dataLocation = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
-    if (dataLocation.isEmpty())
-        dataLocation = WebCore::pathByAppendingComponent(QDir::homePath(), QCoreApplication::applicationName());
-    s_dataLocation = WebCore::pathByAppendingComponent(dataLocation, ".QtWebKit/");
-    WebCore::makeAllDirectories(s_dataLocation);
-    return s_dataLocation;
-}
-
 static QString s_defaultDatabaseDirectory;
 static QString s_defaultLocalStorageDirectory;
 
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to