/* * Copyright (C) 2016 Apple Inc. 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 "WebPluginInfoProvider.h" #include "HangDetectionDisabler.h" #include "WebCoreArgumentCoders.h" #include "WebProcess.h" #include "WebProcessProxyMessages.h" #include #include #include #include #include #include #include #if PLATFORM(MAC) #include "StringUtilities.h" #endif namespace WebKit { WebPluginInfoProvider& WebPluginInfoProvider::singleton() { static WebPluginInfoProvider& pluginInfoProvider = adoptRef(*new WebPluginInfoProvider).leakRef(); return pluginInfoProvider; } WebPluginInfoProvider::WebPluginInfoProvider() { } WebPluginInfoProvider::~WebPluginInfoProvider() { } #if PLATFORM(MAC) void WebPluginInfoProvider::setPluginLoadClientPolicy(WebCore::PluginLoadClientPolicy clientPolicy, const String& host, const String& bundleIdentifier, const String& versionString) { String hostToSet = host.isNull() || !host.length() ? "*" : host; String bundleIdentifierToSet = bundleIdentifier.isNull() || !bundleIdentifier.length() ? "*" : bundleIdentifier; String versionStringToSet = versionString.isNull() || !versionString.length() ? "*" : versionString; PluginPolicyMapsByIdentifier policiesByIdentifier; if (m_hostsToPluginIdentifierData.contains(hostToSet)) policiesByIdentifier = m_hostsToPluginIdentifierData.get(hostToSet); PluginLoadClientPoliciesByBundleVersion versionsToPolicies; if (policiesByIdentifier.contains(bundleIdentifierToSet)) versionsToPolicies = policiesByIdentifier.get(bundleIdentifierToSet); versionsToPolicies.set(versionStringToSet, clientPolicy); policiesByIdentifier.set(bundleIdentifierToSet, versionsToPolicies); m_hostsToPluginIdentifierData.set(hostToSet, policiesByIdentifier); } void WebPluginInfoProvider::clearPluginClientPolicies() { m_hostsToPluginIdentifierData.clear(); } #endif void WebPluginInfoProvider::refreshPlugins() { #if ENABLE(NETSCAPE_PLUGIN_API) m_cachedPlugins.clear(); m_pluginCacheIsPopulated = false; m_shouldRefreshPlugins = true; #endif } void WebPluginInfoProvider::getPluginInfo(WebCore::Page& page, Vector& plugins) { #if ENABLE(NETSCAPE_PLUGIN_API) populatePluginCache(page); if (page.mainFrame().loader().subframeLoader().allowPlugins()) { plugins = m_cachedPlugins; return; } plugins = m_cachedApplicationPlugins; #else UNUSED_PARAM(page); UNUSED_PARAM(plugins); #endif // ENABLE(NETSCAPE_PLUGIN_API) } void WebPluginInfoProvider::getWebVisiblePluginInfo(WebCore::Page& page, Vector& plugins) { ASSERT_ARG(plugins, plugins.isEmpty()); getPluginInfo(page, plugins); #if PLATFORM(MAC) if (auto* document = page.mainFrame().document()) { if (document->securityOrigin().isLocal()) return; } for (int32_t i = plugins.size() - 1; i >= 0; --i) { auto& info = plugins.at(i); // Allow built-in plugins. Also tentatively allow plugins that the client might later selectively permit. if (info.isApplicationPlugin || info.clientLoadPolicy == WebCore::PluginLoadClientPolicyAsk) continue; if (info.clientLoadPolicy == WebCore::PluginLoadClientPolicyBlock) plugins.remove(i); } #endif } #if ENABLE(NETSCAPE_PLUGIN_API) void WebPluginInfoProvider::populatePluginCache(const WebCore::Page& page) { if (!m_pluginCacheIsPopulated) { HangDetectionDisabler hangDetectionDisabler; if (!WebProcess::singleton().parentProcessConnection()->sendSync(Messages::WebProcessProxy::GetPlugins(m_shouldRefreshPlugins), Messages::WebProcessProxy::GetPlugins::Reply(m_cachedPlugins, m_cachedApplicationPlugins), 0)) return; m_shouldRefreshPlugins = false; m_pluginCacheIsPopulated = true; } #if PLATFORM(MAC) String pageHost = page.mainFrame().loader().documentLoader()->responseURL().host(); for (auto& info : m_cachedPlugins) { if (auto clientPolicy = pluginLoadClientPolicyForHost(pageHost, info)) info.clientLoadPolicy = *clientPolicy; } #else UNUSED_PARAM(page); #endif // not PLATFORM(MAC) } #endif #if PLATFORM(MAC) std::optional WebPluginInfoProvider::pluginLoadClientPolicyForHost(const String& host, const WebCore::PluginInfo& info) const { String hostToLookUp = host; String identifier = info.bundleIdentifier; auto policiesByIdentifierIterator = m_hostsToPluginIdentifierData.find(hostToLookUp); if (!identifier.isNull() && policiesByIdentifierIterator == m_hostsToPluginIdentifierData.end()) { if (!replaceHostWithMatchedWildcardHost(hostToLookUp, identifier)) hostToLookUp = "*"; policiesByIdentifierIterator = m_hostsToPluginIdentifierData.find(hostToLookUp); if (hostToLookUp != "*" && policiesByIdentifierIterator == m_hostsToPluginIdentifierData.end()) { hostToLookUp = "*"; policiesByIdentifierIterator = m_hostsToPluginIdentifierData.find(hostToLookUp); } } if (policiesByIdentifierIterator == m_hostsToPluginIdentifierData.end()) return std::nullopt; auto& policiesByIdentifier = policiesByIdentifierIterator->value; if (!identifier) identifier = "*"; auto identifierPolicyIterator = policiesByIdentifier.find(identifier); if (identifier != "*" && identifierPolicyIterator == policiesByIdentifier.end()) { identifier = "*"; identifierPolicyIterator = policiesByIdentifier.find(identifier); } if (identifierPolicyIterator == policiesByIdentifier.end()) return std::nullopt; auto& versionsToPolicies = identifierPolicyIterator->value; String version = info.versionString; if (!version) version = "*"; auto policyIterator = versionsToPolicies.find(version); if (version != "*" && policyIterator == versionsToPolicies.end()) { version = "*"; policyIterator = versionsToPolicies.find(version); } if (policyIterator == versionsToPolicies.end()) return std::nullopt; return policyIterator->value; } String WebPluginInfoProvider::longestMatchedWildcardHostForHost(const String& host) const { String longestMatchedHost; for (auto& key : m_hostsToPluginIdentifierData.keys()) { if (key.contains('*') && key != "*" && stringMatchesWildcardString(host, key)) { if (key.length() > longestMatchedHost.length()) longestMatchedHost = key; else if (key.length() == longestMatchedHost.length() && codePointCompareLessThan(key, longestMatchedHost)) longestMatchedHost = key; } } return longestMatchedHost; } bool WebPluginInfoProvider::replaceHostWithMatchedWildcardHost(String& host, const String& identifier) const { String matchedWildcardHost = longestMatchedWildcardHostForHost(host); if (matchedWildcardHost.isNull()) return false; auto plugInIdentifierData = m_hostsToPluginIdentifierData.find(matchedWildcardHost); if (plugInIdentifierData == m_hostsToPluginIdentifierData.end() || !plugInIdentifierData->value.contains(identifier)) return false; host = matchedWildcardHost; return true; } #endif }