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/MIMETypeRegistry.h>
33#include <WebCore/SecurityOrigin.h>
34#include <algorithm>
35#include <wtf/ListHashSet.h>
36#include <wtf/StdLibExtras.h>
37#include <wtf/URL.h>
38
39namespace WebKit {
40using namespace WebCore;
41
42PluginInfoStore::PluginInfoStore()
43 : m_pluginListIsUpToDate(false)
44{
45}
46
47void PluginInfoStore::setAdditionalPluginsDirectories(const Vector<String>& directories)
48{
49 m_additionalPluginsDirectories = directories;
50 refresh();
51}
52
53void PluginInfoStore::refresh()
54{
55 m_pluginListIsUpToDate = false;
56}
57
58template <typename T, typename U>
59static void addFromVector(T& hashSet, const U& vector)
60{
61 for (size_t i = 0; i < vector.size(); ++i)
62 hashSet.add(vector[i]);
63}
64
65void PluginInfoStore::loadPluginsIfNecessary()
66{
67 if (m_pluginListIsUpToDate)
68 return;
69
70 ListHashSet<String> uniquePluginPaths;
71
72 // First, load plug-ins from the additional plug-ins directories specified.
73 for (size_t i = 0; i < m_additionalPluginsDirectories.size(); ++i)
74 addFromVector(uniquePluginPaths, pluginPathsInDirectory(m_additionalPluginsDirectories[i]));
75
76 // Then load plug-ins from the standard plug-ins directories.
77 Vector<String> directories = pluginsDirectories();
78 for (size_t i = 0; i < directories.size(); ++i)
79 addFromVector(uniquePluginPaths, pluginPathsInDirectory(directories[i]));
80
81 // Then load plug-ins that are not in the standard plug-ins directories.
82 addFromVector(uniquePluginPaths, individualPluginPaths());
83
84 m_plugins.clear();
85
86 for (const auto& pluginPath : uniquePluginPaths)
87 loadPlugin(m_plugins, pluginPath);
88
89 m_pluginListIsUpToDate = true;
90}
91
92void PluginInfoStore::loadPlugin(Vector<PluginModuleInfo>& plugins, const String& pluginPath)
93{
94 PluginModuleInfo plugin;
95
96 if (!getPluginInfo(pluginPath, plugin))
97 return;
98
99 if (!shouldUsePlugin(plugins, plugin))
100 return;
101
102 plugins.append(plugin);
103}
104
105Vector<PluginModuleInfo> PluginInfoStore::plugins()
106{
107 loadPluginsIfNecessary();
108 return m_plugins;
109}
110
111PluginModuleInfo PluginInfoStore::findPluginForMIMEType(const String& mimeType, PluginData::AllowedPluginTypes allowedPluginTypes) const
112{
113 ASSERT(!mimeType.isNull());
114
115 for (const auto& plugin : m_plugins) {
116 if (allowedPluginTypes == PluginData::OnlyApplicationPlugins && !plugin.info.isApplicationPlugin)
117 continue;
118
119 for (const auto& mimeClassInfo : plugin.info.mimes) {
120 if (mimeClassInfo.type == mimeType)
121 return plugin;
122 }
123 }
124
125 return PluginModuleInfo();
126}
127
128PluginModuleInfo PluginInfoStore::findPluginForExtension(const String& extension, String& mimeType, PluginData::AllowedPluginTypes allowedPluginTypes) const
129{
130 ASSERT(!extension.isNull());
131
132 for (const auto& plugin : m_plugins) {
133 if (allowedPluginTypes == PluginData::OnlyApplicationPlugins && !plugin.info.isApplicationPlugin)
134 continue;
135
136 for (const auto& mimeClassInfo : plugin.info.mimes) {
137 if (mimeClassInfo.extensions.contains(extension)) {
138 // We found a supported extension, set the correct MIME type.
139 mimeType = mimeClassInfo.type;
140 return plugin;
141 }
142 }
143 }
144
145 return PluginModuleInfo();
146}
147
148static inline String pathExtension(const URL& url)
149{
150 String extension;
151 String filename = url.lastPathComponent();
152 if (!filename.endsWith('/')) {
153 size_t extensionPos = filename.reverseFind('.');
154 if (extensionPos != notFound)
155 extension = filename.substring(extensionPos + 1);
156 }
157 return extension.convertToASCIILowercase();
158}
159
160#if !PLATFORM(COCOA)
161
162PluginModuleLoadPolicy PluginInfoStore::defaultLoadPolicyForPlugin(const PluginModuleInfo&)
163{
164 return PluginModuleLoadNormally;
165}
166
167PluginModuleInfo PluginInfoStore::findPluginWithBundleIdentifier(const String&)
168{
169 ASSERT_NOT_REACHED();
170 return PluginModuleInfo();
171}
172
173#endif
174
175PluginModuleInfo PluginInfoStore::findPlugin(String& mimeType, const URL& url, PluginData::AllowedPluginTypes allowedPluginTypes)
176{
177 loadPluginsIfNecessary();
178
179 // First, check if we can get the plug-in based on its MIME type.
180 if (!mimeType.isNull()) {
181 PluginModuleInfo plugin = findPluginForMIMEType(mimeType, allowedPluginTypes);
182 if (!plugin.path.isNull())
183 return plugin;
184 }
185
186 // Next, check if any plug-ins claim to support the URL extension.
187 String extension = pathExtension(url);
188 if (!extension.isNull() && mimeType.isEmpty()) {
189 PluginModuleInfo plugin = findPluginForExtension(extension, mimeType, allowedPluginTypes);
190 if (!plugin.path.isNull())
191 return plugin;
192
193 // Finally, try to get the MIME type from the extension in a platform specific manner and use that.
194 String extensionMimeType = MIMETypeRegistry::getMIMETypeForExtension(extension);
195 if (!extensionMimeType.isNull()) {
196 PluginModuleInfo plugin = findPluginForMIMEType(extensionMimeType, allowedPluginTypes);
197 if (!plugin.path.isNull()) {
198 mimeType = extensionMimeType;
199 return plugin;
200 }
201 }
202 }
203
204 return PluginModuleInfo();
205}
206
207bool PluginInfoStore::isSupportedPlugin(const PluginInfoStore::SupportedPlugin& plugin, const String& mimeType, const URL& pluginURL)
208{
209 if (!mimeType.isEmpty() && plugin.mimeTypes.contains(mimeType))
210 return true;
211 auto extension = pathExtension(pluginURL);
212 return extension.isEmpty() ? false : plugin.extensions.contains(extension);
213}
214
215bool PluginInfoStore::isSupportedPlugin(const String& mimeType, const URL& pluginURL, const String&, const URL& pageURL)
216{
217 // We check only pageURL for consistency with WebProcess visible plugins.
218 if (!m_supportedPlugins)
219 return true;
220
221 return m_supportedPlugins->findMatching([&] (auto&& plugin) {
222 return pageURL.isMatchingDomain(plugin.matchingDomain) && isSupportedPlugin(plugin, mimeType, pluginURL);
223 }) != notFound;
224}
225
226Optional<Vector<SupportedPluginIdentifier>> PluginInfoStore::supportedPluginIdentifiers()
227{
228 if (!m_supportedPlugins)
229 return WTF::nullopt;
230
231 return WTF::map(*m_supportedPlugins, [] (auto&& item) {
232 return SupportedPluginIdentifier { item.matchingDomain, item.identifier };
233 });
234}
235
236void PluginInfoStore::addSupportedPlugin(String&& domainName, String&& identifier, HashSet<String>&& mimeTypes, HashSet<String> extensions)
237{
238 if (!m_supportedPlugins)
239 m_supportedPlugins = Vector<SupportedPlugin> { };
240
241 m_supportedPlugins->append(SupportedPlugin { WTFMove(domainName), WTFMove(identifier), WTFMove(mimeTypes), WTFMove(extensions) });
242}
243
244PluginModuleInfo PluginInfoStore::infoForPluginWithPath(const String& pluginPath) const
245{
246 for (const auto& plugin : m_plugins) {
247 if (plugin.path == pluginPath)
248 return plugin;
249 }
250
251 ASSERT_NOT_REACHED();
252 return PluginModuleInfo();
253}
254
255} // namespace WebKit
256
257#endif // ENABLE(NETSCAPE_PLUGIN_API)
258