1/*
2 * Copyright (C) 2010, 2012 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
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.
12 *
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.
24 */
25
26#include "config.h"
27#include "PluginInfoStore.h"
28
29#if ENABLE(NETSCAPE_PLUGIN_API)
30
31#include "PluginModuleInfo.h"
32#include <WebCore/URL.h>
33#include <WebCore/MIMETypeRegistry.h>
34#include <algorithm>
35#include <wtf/ListHashSet.h>
36#include <wtf/StdLibExtras.h>
37
38using namespace WebCore;
39
40namespace WebKit {
41
42PluginInfoStore::PluginInfoStore()
43    : m_pluginListIsUpToDate(false)
44    , m_client(0)
45{
46}
47
48void PluginInfoStore::setAdditionalPluginsDirectories(const Vector<String>& directories)
49{
50    m_additionalPluginsDirectories = directories;
51    refresh();
52}
53
54void PluginInfoStore::refresh()
55{
56    m_pluginListIsUpToDate = false;
57}
58
59template <typename T, typename U>
60static void addFromVector(T& hashSet, const U& vector)
61{
62    for (size_t i = 0; i < vector.size(); ++i)
63        hashSet.add(vector[i]);
64}
65
66void PluginInfoStore::loadPluginsIfNecessary()
67{
68    if (m_pluginListIsUpToDate)
69        return;
70
71    ListHashSet<String, 32> uniquePluginPaths;
72
73    // First, load plug-ins from the additional plug-ins directories specified.
74    for (size_t i = 0; i < m_additionalPluginsDirectories.size(); ++i)
75        addFromVector(uniquePluginPaths, pluginPathsInDirectory(m_additionalPluginsDirectories[i]));
76
77    // Then load plug-ins from the standard plug-ins directories.
78    Vector<String> directories = pluginsDirectories();
79    for (size_t i = 0; i < directories.size(); ++i)
80        addFromVector(uniquePluginPaths, pluginPathsInDirectory(directories[i]));
81
82    // Then load plug-ins that are not in the standard plug-ins directories.
83    addFromVector(uniquePluginPaths, individualPluginPaths());
84
85    m_plugins.clear();
86
87    for (const auto& pluginPath : uniquePluginPaths)
88        loadPlugin(m_plugins, pluginPath);
89
90    m_pluginListIsUpToDate = true;
91
92    if (m_client)
93        m_client->pluginInfoStoreDidLoadPlugins(this);
94}
95
96void PluginInfoStore::loadPlugin(Vector<PluginModuleInfo>& plugins, const String& pluginPath)
97{
98    PluginModuleInfo plugin;
99
100    if (!getPluginInfo(pluginPath, plugin))
101        return;
102
103    if (!shouldUsePlugin(plugins, plugin))
104        return;
105
106    plugins.append(plugin);
107}
108
109Vector<PluginModuleInfo> PluginInfoStore::plugins()
110{
111    loadPluginsIfNecessary();
112    return m_plugins;
113}
114
115PluginModuleInfo PluginInfoStore::findPluginForMIMEType(const String& mimeType, PluginData::AllowedPluginTypes allowedPluginTypes) const
116{
117    ASSERT(!mimeType.isNull());
118
119    for (const auto& plugin : m_plugins) {
120        if (allowedPluginTypes == PluginData::OnlyApplicationPlugins && !plugin.info.isApplicationPlugin)
121            continue;
122
123        for (const auto& mimeClassInfo : plugin.info.mimes) {
124            if (mimeClassInfo.type == mimeType)
125                return plugin;
126        }
127    }
128
129    return PluginModuleInfo();
130}
131
132PluginModuleInfo PluginInfoStore::findPluginForExtension(const String& extension, String& mimeType, PluginData::AllowedPluginTypes allowedPluginTypes) const
133{
134    ASSERT(!extension.isNull());
135
136    for (const auto& plugin : m_plugins) {
137        if (allowedPluginTypes == PluginData::OnlyApplicationPlugins && !plugin.info.isApplicationPlugin)
138            continue;
139
140        for (const auto& mimeClassInfo : plugin.info.mimes) {
141            const Vector<String>& extensions = mimeClassInfo.extensions;
142
143            if (std::find(extensions.begin(), extensions.end(), extension) != extensions.end()) {
144                // We found a supported extension, set the correct MIME type.
145                mimeType = mimeClassInfo.type;
146                return plugin;
147            }
148        }
149    }
150
151    return PluginModuleInfo();
152}
153
154static inline String pathExtension(const URL& url)
155{
156    String extension;
157    String filename = url.lastPathComponent();
158    if (!filename.endsWith('/')) {
159        int extensionPos = filename.reverseFind('.');
160        if (extensionPos != -1)
161            extension = filename.substring(extensionPos + 1);
162    }
163
164    return extension;
165}
166
167#if !PLATFORM(COCOA)
168PluginModuleLoadPolicy PluginInfoStore::defaultLoadPolicyForPlugin(const PluginModuleInfo&)
169{
170    return PluginModuleLoadNormally;
171}
172
173PluginModuleInfo PluginInfoStore::findPluginWithBundleIdentifier(const String&)
174{
175    ASSERT_NOT_REACHED();
176    return PluginModuleInfo();
177}
178
179#endif
180
181PluginModuleInfo PluginInfoStore::findPlugin(String& mimeType, const URL& url, PluginData::AllowedPluginTypes allowedPluginTypes)
182{
183    loadPluginsIfNecessary();
184
185    // First, check if we can get the plug-in based on its MIME type.
186    if (!mimeType.isNull()) {
187        PluginModuleInfo plugin = findPluginForMIMEType(mimeType, allowedPluginTypes);
188        if (!plugin.path.isNull())
189            return plugin;
190    }
191
192    // Next, check if any plug-ins claim to support the URL extension.
193    String extension = pathExtension(url).lower();
194    if (!extension.isNull() && mimeType.isEmpty()) {
195        PluginModuleInfo plugin = findPluginForExtension(extension, mimeType, allowedPluginTypes);
196        if (!plugin.path.isNull())
197            return plugin;
198
199        // Finally, try to get the MIME type from the extension in a platform specific manner and use that.
200        String extensionMimeType = MIMETypeRegistry::getMIMETypeForExtension(extension);
201        if (!extensionMimeType.isNull()) {
202            PluginModuleInfo plugin = findPluginForMIMEType(extensionMimeType, allowedPluginTypes);
203            if (!plugin.path.isNull()) {
204                mimeType = extensionMimeType;
205                return plugin;
206            }
207        }
208    }
209
210    return PluginModuleInfo();
211}
212
213PluginModuleInfo PluginInfoStore::infoForPluginWithPath(const String& pluginPath) const
214{
215    for (const auto& plugin : m_plugins) {
216        if (plugin.path == pluginPath)
217            return plugin;
218    }
219
220    ASSERT_NOT_REACHED();
221    return PluginModuleInfo();
222}
223
224} // namespace WebKit
225
226#endif // ENABLE(NETSCAPE_PLUGIN_API)
227