[GTK][WK2] Blocks when fetching plugins information
authorcarlosgc@webkit.org <carlosgc@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 27 Feb 2014 17:59:16 +0000 (17:59 +0000)
committercarlosgc@webkit.org <carlosgc@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 27 Feb 2014 17:59:16 +0000 (17:59 +0000)
https://bugs.webkit.org/show_bug.cgi?id=115650

Reviewed by Gustavo Noronha Silva.

Use a persistent cache to store the plugins metadata to avoid
having to load all the plugins everytime a plugin is used for the
first time.

* GNUmakefile.am:
* GNUmakefile.list.am:
* PlatformGTK.cmake:
* Shared/Plugins/Netscape/NetscapePluginModule.h:
* Shared/Plugins/Netscape/x11/NetscapePluginModuleX11.cpp:
(WebKit::NetscapePluginModule::parseMIMEDescription): Make this
method public.
(WebKit::NetscapePluginModule::buildMIMEDescription): Added this
helper to build the MIME description string.
* UIProcess/Plugins/gtk/PluginInfoCache.cpp: Added.
(WebKit::PluginInfoCache::shared):
(WebKit::PluginInfoCache::PluginInfoCache):
(WebKit::PluginInfoCache::~PluginInfoCache):
(WebKit::PluginInfoCache::saveToFileIdleCallback):
(WebKit::PluginInfoCache::saveToFile):
(WebKit::PluginInfoCache::getPluginInfo):
(WebKit::PluginInfoCache::updatePluginInfo):
* UIProcess/Plugins/gtk/PluginInfoCache.h: Added.
* UIProcess/Plugins/unix/PluginInfoStoreUnix.cpp:
(WebKit::PluginInfoStore::getPluginInfo): Check first if we have
metadata of the plugin in the cache and update the cache if we
loaded the plugin to get its metadata.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@164808 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebKit2/ChangeLog
Source/WebKit2/GNUmakefile.am
Source/WebKit2/GNUmakefile.list.am
Source/WebKit2/PlatformGTK.cmake
Source/WebKit2/Shared/Plugins/Netscape/NetscapePluginModule.h
Source/WebKit2/Shared/Plugins/Netscape/x11/NetscapePluginModuleX11.cpp
Source/WebKit2/UIProcess/Plugins/gtk/PluginInfoCache.cpp [new file with mode: 0644]
Source/WebKit2/UIProcess/Plugins/gtk/PluginInfoCache.h [new file with mode: 0644]
Source/WebKit2/UIProcess/Plugins/unix/PluginInfoStoreUnix.cpp

index 31aba89..2732d1c 100644 (file)
@@ -1,3 +1,37 @@
+2014-02-27  Carlos Garcia Campos  <cgarcia@igalia.com>
+
+        [GTK][WK2] Blocks when fetching plugins information
+        https://bugs.webkit.org/show_bug.cgi?id=115650
+
+        Reviewed by Gustavo Noronha Silva.
+
+        Use a persistent cache to store the plugins metadata to avoid
+        having to load all the plugins everytime a plugin is used for the
+        first time.
+
+        * GNUmakefile.am:
+        * GNUmakefile.list.am:
+        * PlatformGTK.cmake:
+        * Shared/Plugins/Netscape/NetscapePluginModule.h:
+        * Shared/Plugins/Netscape/x11/NetscapePluginModuleX11.cpp:
+        (WebKit::NetscapePluginModule::parseMIMEDescription): Make this
+        method public.
+        (WebKit::NetscapePluginModule::buildMIMEDescription): Added this
+        helper to build the MIME description string.
+        * UIProcess/Plugins/gtk/PluginInfoCache.cpp: Added.
+        (WebKit::PluginInfoCache::shared):
+        (WebKit::PluginInfoCache::PluginInfoCache):
+        (WebKit::PluginInfoCache::~PluginInfoCache):
+        (WebKit::PluginInfoCache::saveToFileIdleCallback):
+        (WebKit::PluginInfoCache::saveToFile):
+        (WebKit::PluginInfoCache::getPluginInfo):
+        (WebKit::PluginInfoCache::updatePluginInfo):
+        * UIProcess/Plugins/gtk/PluginInfoCache.h: Added.
+        * UIProcess/Plugins/unix/PluginInfoStoreUnix.cpp:
+        (WebKit::PluginInfoStore::getPluginInfo): Check first if we have
+        metadata of the plugin in the cache and update the cache if we
+        loaded the plugin to get its metadata.
+
 2014-02-27  Ryan Lortie  <desrt@desrt.ca>
 
         need to #include <libgen.h> for basename
index 68f7196..892969c 100644 (file)
@@ -126,6 +126,7 @@ libwebkit2gtk_@WEBKITGTK_API_MAJOR_VERSION@_@WEBKITGTK_API_MINOR_VERSION@_la_CPP
        -I$(srcdir)/Source/WebKit2/UIProcess/Network/CustomProtocols/soup \
        -I$(srcdir)/Source/WebKit2/UIProcess/Notifications \
        -I$(srcdir)/Source/WebKit2/UIProcess/Plugins \
+       -I$(srcdir)/Source/WebKit2/UIProcess/Plugins/gtk \
        -I$(srcdir)/Source/WebKit2/UIProcess/Plugins/unix \
        -I$(srcdir)/Source/WebKit2/UIProcess/Storage \
        -I$(srcdir)/Source/WebKit2/UIProcess/soup \
index 69dbe81..decf701 100644 (file)
@@ -866,6 +866,8 @@ webkit2_sources += \
        Source/WebKit2/UIProcess/Plugins/PluginProcessProxy.h \
        Source/WebKit2/UIProcess/Plugins/WebPluginSiteDataManager.h \
        Source/WebKit2/UIProcess/Plugins/WebPluginSiteDataManager.cpp \
+       Source/WebKit2/UIProcess/Plugins/gtk/PluginInfoCache.cpp \
+       Source/WebKit2/UIProcess/Plugins/gtk/PluginInfoCache.h \
        Source/WebKit2/UIProcess/Plugins/unix/PluginInfoStoreUnix.cpp \
        Source/WebKit2/UIProcess/Plugins/unix/PluginProcessProxyUnix.cpp \
        Source/WebKit2/UIProcess/ProcessModel.h \
index 8b17119..720cc47 100644 (file)
@@ -248,6 +248,8 @@ list(APPEND WebKit2_SOURCES
     UIProcess/Network/CustomProtocols/soup/WebSoupCustomProtocolRequestManagerClient.cpp
     UIProcess/Network/CustomProtocols/soup/WebSoupCustomProtocolRequestManager.cpp
 
+    UIProcess/Plugins/gtk/PluginInfoCache.cpp
+
     UIProcess/Plugins/unix/PluginInfoStoreUnix.cpp
     UIProcess/Plugins/unix/PluginProcessProxyUnix.cpp
 
@@ -409,6 +411,7 @@ list(APPEND WebKit2_INCLUDE_DIRECTORIES
     "${WEBKIT2_DIR}/UIProcess/API/cpp/gtk"
     "${WEBKIT2_DIR}/UIProcess/API/gtk"
     "${WEBKIT2_DIR}/UIProcess/Network/CustomProtocols/soup"
+    "${WEBKIT2_DIR}/UIProcess/Plugins/gtk"
     "${WEBKIT2_DIR}/UIProcess/gtk"
     "${WEBKIT2_DIR}/UIProcess/soup"
     "${WEBKIT2_DIR}/WebProcess/InjectedBundle/API/gtk"
index 658cfc9..4ceeeca 100644 (file)
 #include <wtf/RefCounted.h>
 #include <wtf/text/WTFString.h>
 
+namespace WebCore {
+struct MimeClassInfo;
+}
+
 namespace WebKit {
 
 struct RawPluginMetaData;
@@ -67,6 +71,8 @@ public:
 
 #if PLUGIN_ARCHITECTURE(X11)
     static bool scanPlugin(const String& pluginPath);
+    static void parseMIMEDescription(const String& mimeDescription, Vector<WebCore::MimeClassInfo>& result);
+    static String buildMIMEDescription(const Vector<WebCore::MimeClassInfo>&);
 #endif
 
 private:
index 48da58d..d4a003a 100644 (file)
@@ -36,6 +36,7 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
+#include <wtf/text/StringBuilder.h>
 
 using namespace WebCore;
 
@@ -67,7 +68,7 @@ StdoutDevNullRedirector::~StdoutDevNullRedirector()
 }
 
 
-static void parseMIMEDescription(const String& mimeDescription, Vector<MimeClassInfo>& result)
+void NetscapePluginModule::parseMIMEDescription(const String& mimeDescription, Vector<MimeClassInfo>& result)
 {
     ASSERT_ARG(result, result.isEmpty());
 
@@ -94,6 +95,32 @@ static void parseMIMEDescription(const String& mimeDescription, Vector<MimeClass
     }
 }
 
+String NetscapePluginModule::buildMIMEDescription(const Vector<MimeClassInfo>& mimeDescription)
+{
+    StringBuilder builder;
+
+    size_t mimeInfoCount = mimeDescription.size();
+    for (size_t i = 0; i < mimeInfoCount; ++i) {
+        const MimeClassInfo& mimeInfo = mimeDescription[i];
+        builder.append(mimeInfo.type);
+        builder.append(':');
+
+        size_t extensionsCount = mimeInfo.extensions.size();
+        for (size_t j = 0; j < extensionsCount; ++j) {
+            builder.append(mimeInfo.extensions[j]);
+            if (j != extensionsCount - 1)
+                builder.append(',');
+        }
+        builder.append(':');
+
+        builder.append(mimeInfo.desc);
+        if (i != mimeInfoCount - 1)
+            builder.append(';');
+    }
+
+    return builder.toString();
+}
+
 bool NetscapePluginModule::getPluginInfoForLoadedPlugin(RawPluginMetaData& metaData)
 {
     ASSERT(m_isInitialized);
diff --git a/Source/WebKit2/UIProcess/Plugins/gtk/PluginInfoCache.cpp b/Source/WebKit2/UIProcess/Plugins/gtk/PluginInfoCache.cpp
new file mode 100644 (file)
index 0000000..72e4514
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2014 Igalia S.L.
+ *
+ * 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 "PluginInfoCache.h"
+
+#if ENABLE(NETSCAPE_PLUGIN_API)
+
+#include "NetscapePluginModule.h"
+#include <WebCore/FileSystem.h>
+#include <wtf/text/CString.h>
+
+namespace WebKit {
+
+static const unsigned gSchemaVersion = 1;
+
+PluginInfoCache& PluginInfoCache::shared()
+{
+    static NeverDestroyed<PluginInfoCache> pluginInfoCache;
+    return pluginInfoCache;
+}
+
+PluginInfoCache::PluginInfoCache()
+    : m_cacheFile(g_key_file_new())
+    , m_cachePath(g_build_filename(g_get_user_cache_dir(), "webkitgtk", "plugins", nullptr))
+    , m_saveToFileIdleId(0)
+{
+    g_key_file_load_from_file(m_cacheFile.get(), m_cachePath.get(), G_KEY_FILE_NONE, nullptr);
+
+    if (g_key_file_has_group(m_cacheFile.get(), "schema")) {
+        unsigned schemaVersion = static_cast<unsigned>(g_key_file_get_integer(m_cacheFile.get(), "schema", "version", nullptr));
+        if (schemaVersion == gSchemaVersion)
+            return;
+
+        // Cache file using an old schema, create a new empty file.
+        m_cacheFile.reset(g_key_file_new());
+    }
+
+    g_key_file_set_integer(m_cacheFile.get(), "schema", "version", static_cast<unsigned>(gSchemaVersion));
+}
+
+PluginInfoCache::~PluginInfoCache()
+{
+    if (m_saveToFileIdleId) {
+        g_source_remove(m_saveToFileIdleId);
+        saveToFile();
+    }
+}
+
+gboolean PluginInfoCache::saveToFileIdleCallback(PluginInfoCache* cache)
+{
+    cache->saveToFile();
+    return FALSE;
+}
+
+void PluginInfoCache::saveToFile()
+{
+    std::lock_guard<std::mutex> lock(m_mutex);
+    m_saveToFileIdleId = 0;
+
+    gsize dataLength;
+    GUniquePtr<char> data(g_key_file_to_data(m_cacheFile.get(), &dataLength, nullptr));
+    if (!data)
+        return;
+
+    g_file_set_contents(m_cachePath.get(), data.get(), dataLength, nullptr);
+}
+
+bool PluginInfoCache::getPluginInfo(const String& pluginPath, PluginModuleInfo& plugin)
+{
+    CString pluginGroup = pluginPath.utf8();
+    if (!g_key_file_has_group(m_cacheFile.get(), pluginGroup.data()))
+        return false;
+
+    time_t lastModified;
+    if (!WebCore::getFileModificationTime(pluginPath, lastModified))
+        return false;
+    time_t cachedLastModified = static_cast<time_t>(g_key_file_get_uint64(m_cacheFile.get(), pluginGroup.data(), "mtime", nullptr));
+    if (lastModified != cachedLastModified)
+        return false;
+
+    plugin.path = pluginPath;
+    plugin.info.file = WebCore::pathGetFileName(pluginPath);
+
+    GUniquePtr<char> stringValue(g_key_file_get_string(m_cacheFile.get(), pluginGroup.data(), "name", nullptr));
+    plugin.info.name = String::fromUTF8(stringValue.get());
+
+    stringValue.reset(g_key_file_get_string(m_cacheFile.get(), pluginGroup.data(), "description", nullptr));
+    plugin.info.desc = String::fromUTF8(stringValue.get());
+
+    stringValue.reset(g_key_file_get_string(m_cacheFile.get(), pluginGroup.data(), "mime-description", nullptr));
+    NetscapePluginModule::parseMIMEDescription(String::fromUTF8(stringValue.get()), plugin.info.mimes);
+
+    return true;
+}
+
+void PluginInfoCache::updatePluginInfo(const String& pluginPath, const PluginModuleInfo& plugin)
+{
+    time_t lastModified;
+    if (!WebCore::getFileModificationTime(pluginPath, lastModified))
+        return;
+
+    CString pluginGroup = pluginPath.utf8();
+    g_key_file_set_uint64(m_cacheFile.get(), pluginGroup.data(), "mtime", static_cast<guint64>(lastModified));
+    g_key_file_set_string(m_cacheFile.get(), pluginGroup.data(), "name", plugin.info.name.utf8().data());
+    g_key_file_set_string(m_cacheFile.get(), pluginGroup.data(), "description", plugin.info.desc.utf8().data());
+
+    String mimeDescription = NetscapePluginModule::buildMIMEDescription(plugin.info.mimes);
+    g_key_file_set_string(m_cacheFile.get(), pluginGroup.data(), "mime-description", mimeDescription.utf8().data());
+
+    // Save the cache file in an idle to make sure it happens in the main thread and
+    // it's done only once when this is called multiple times in a very short time.
+    std::lock_guard<std::mutex> lock(m_mutex);
+    if (m_saveToFileIdleId)
+        return;
+
+    m_saveToFileIdleId = g_idle_add(reinterpret_cast<GSourceFunc>(PluginInfoCache::saveToFileIdleCallback), this);
+}
+
+} // namespace WebKit
+
+#endif // ENABLE(NETSCAPE_PLUGIN_API)
diff --git a/Source/WebKit2/UIProcess/Plugins/gtk/PluginInfoCache.h b/Source/WebKit2/UIProcess/Plugins/gtk/PluginInfoCache.h
new file mode 100644 (file)
index 0000000..3272dc0
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2014 Igalia S.L.
+ *
+ * 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 PluginInfoCache_h
+#define PluginInfoCache_h
+
+#if ENABLE(NETSCAPE_PLUGIN_API)
+
+#include "PluginModuleInfo.h"
+#include <mutex>
+#include <wtf/NeverDestroyed.h>
+#include <wtf/gobject/GUniquePtr.h>
+
+namespace WebKit {
+
+class PluginInfoCache {
+    WTF_MAKE_NONCOPYABLE(PluginInfoCache);
+    friend class NeverDestroyed<PluginInfoCache>;
+public:
+    static PluginInfoCache& shared();
+
+    bool getPluginInfo(const String& pluginPath, PluginModuleInfo&);
+    void updatePluginInfo(const String& pluginPath, const PluginModuleInfo&);
+
+private:
+    PluginInfoCache();
+    ~PluginInfoCache();
+
+    void saveToFile();
+    static gboolean saveToFileIdleCallback(PluginInfoCache*);
+
+    GUniquePtr<GKeyFile> m_cacheFile;
+    GUniquePtr<char> m_cachePath;
+    unsigned m_saveToFileIdleId;
+    std::mutex m_mutex;
+};
+
+} // namespace WebKit
+
+#endif // ENABLE(NETSCAPE_PLUGIN_API)
+
+#endif // PluginInfoCache_h
index 3726401..a968561 100644 (file)
 #include "PluginDatabase.h"
 #include <WebCore/FileSystem.h>
 
+#if PLATFORM(GTK)
+#include "PluginInfoCache.h"
+#endif
+
 using namespace WebCore;
 
 namespace WebKit {
@@ -98,7 +102,18 @@ Vector<String> PluginInfoStore::individualPluginPaths()
 
 bool PluginInfoStore::getPluginInfo(const String& pluginPath, PluginModuleInfo& plugin)
 {
+#if PLATFORM(GTK)
+    if (PluginInfoCache::shared().getPluginInfo(pluginPath, plugin))
+        return true;
+
+    if (NetscapePluginModule::getPluginInfo(pluginPath, plugin)) {
+        PluginInfoCache::shared().updatePluginInfo(pluginPath, plugin);
+        return true;
+    }
+    return false;
+#else
     return NetscapePluginModule::getPluginInfo(pluginPath, plugin);
+#endif
 }
 
 bool PluginInfoStore::shouldUsePlugin(Vector<PluginModuleInfo>& /*alreadyLoadedPlugins*/, const PluginModuleInfo& /*plugin*/)