diff options
Diffstat (limited to 'Source/WebKit2/WebProcess/UserContent/WebUserContentController.cpp')
-rw-r--r-- | Source/WebKit2/WebProcess/UserContent/WebUserContentController.cpp | 518 |
1 files changed, 518 insertions, 0 deletions
diff --git a/Source/WebKit2/WebProcess/UserContent/WebUserContentController.cpp b/Source/WebKit2/WebProcess/UserContent/WebUserContentController.cpp new file mode 100644 index 000000000..3874ccc54 --- /dev/null +++ b/Source/WebKit2/WebProcess/UserContent/WebUserContentController.cpp @@ -0,0 +1,518 @@ +/* + * Copyright (C) 2014 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 "WebUserContentController.h" + +#include "DataReference.h" +#include "FrameInfoData.h" +#include "InjectedBundleScriptWorld.h" +#include "WebCompiledContentExtension.h" +#include "WebFrame.h" +#include "WebPage.h" +#include "WebProcess.h" +#include "WebUserContentControllerMessages.h" +#include "WebUserContentControllerProxyMessages.h" +#include <WebCore/DOMWrapperWorld.h> +#include <WebCore/SecurityOriginData.h> +#include <WebCore/SerializedScriptValue.h> +#include <WebCore/UserStyleSheet.h> +#include <wtf/NeverDestroyed.h> + +#if ENABLE(USER_MESSAGE_HANDLERS) +#include <WebCore/UserMessageHandler.h> +#include <WebCore/UserMessageHandlerDescriptor.h> +#endif + +using namespace WebCore; + +namespace WebKit { + +static HashMap<uint64_t, WebUserContentController*>& userContentControllers() +{ + static NeverDestroyed<HashMap<uint64_t, WebUserContentController*>> userContentControllers; + + return userContentControllers; +} + +typedef HashMap<uint64_t, std::pair<RefPtr<InjectedBundleScriptWorld>, unsigned>> WorldMap; + +static WorldMap& worldMap() +{ + static NeverDestroyed<WorldMap> map(std::initializer_list<WorldMap::KeyValuePairType> { { 1, std::make_pair(InjectedBundleScriptWorld::normalWorld(), 1) } }); + + return map; +} + +Ref<WebUserContentController> WebUserContentController::getOrCreate(uint64_t identifier) +{ + auto& userContentControllerPtr = userContentControllers().add(identifier, nullptr).iterator->value; + if (userContentControllerPtr) + return *userContentControllerPtr; + + RefPtr<WebUserContentController> userContentController = adoptRef(new WebUserContentController(identifier)); + userContentControllerPtr = userContentController.get(); + + return userContentController.releaseNonNull(); +} + +WebUserContentController::WebUserContentController(uint64_t identifier) + : m_identifier(identifier) +{ + WebProcess::singleton().addMessageReceiver(Messages::WebUserContentController::messageReceiverName(), m_identifier, *this); +} + +WebUserContentController::~WebUserContentController() +{ + ASSERT(userContentControllers().contains(m_identifier)); + + WebProcess::singleton().removeMessageReceiver(Messages::WebUserContentController::messageReceiverName(), m_identifier); + + userContentControllers().remove(m_identifier); +} + +void WebUserContentController::addUserContentWorlds(const Vector<std::pair<uint64_t, String>>& worlds) +{ + for (auto& world : worlds) { + ASSERT(world.first); + ASSERT(world.first != 1); + + worldMap().ensure(world.first, [&] { return std::make_pair(InjectedBundleScriptWorld::create(world.second), 1); }); + } +} + +void WebUserContentController::removeUserContentWorlds(const Vector<uint64_t>& worldIdentifiers) +{ + for (auto& worldIdentifier : worldIdentifiers) { + ASSERT(worldIdentifier); + ASSERT(worldIdentifier != 1); + + auto it = worldMap().find(worldIdentifier); + if (it == worldMap().end()) { + WTFLogAlways("Trying to remove a UserContentWorld (id=%" PRIu64 ") that does not exist.", worldIdentifier); + return; + } + + it->value.second--; + + if (!it->value.second) + worldMap().remove(it); + } +} + +void WebUserContentController::addUserScripts(const Vector<WebUserScriptData>& userScripts) +{ + for (const auto& userScriptData : userScripts) { + auto it = worldMap().find(userScriptData.worldIdentifier); + if (it == worldMap().end()) { + WTFLogAlways("Trying to add a UserScript to a UserContentWorld (id=%" PRIu64 ") that does not exist.", userScriptData.worldIdentifier); + continue; + } + + UserScript script = userScriptData.userScript; + addUserScriptInternal(*it->value.first, userScriptData.identifier, WTFMove(script)); + } +} + +void WebUserContentController::removeUserScript(uint64_t worldIdentifier, uint64_t userScriptIdentifier) +{ + auto it = worldMap().find(worldIdentifier); + if (it == worldMap().end()) { + WTFLogAlways("Trying to remove a UserScript from a UserContentWorld (id=%" PRIu64 ") that does not exist.", worldIdentifier); + return; + } + + removeUserScriptInternal(*it->value.first, userScriptIdentifier); +} + +void WebUserContentController::removeAllUserScripts(const Vector<uint64_t>& worldIdentifiers) +{ + for (auto& worldIdentifier : worldIdentifiers) { + auto it = worldMap().find(worldIdentifier); + if (it == worldMap().end()) { + WTFLogAlways("Trying to remove all UserScripts from a UserContentWorld (id=%" PRIu64 ") that does not exist.", worldIdentifier); + return; + } + + removeUserScripts(*it->value.first); + } +} + +void WebUserContentController::addUserStyleSheets(const Vector<WebUserStyleSheetData>& userStyleSheets) +{ + for (const auto& userStyleSheetData : userStyleSheets) { + auto it = worldMap().find(userStyleSheetData.worldIdentifier); + if (it == worldMap().end()) { + WTFLogAlways("Trying to add a UserStyleSheet to a UserContentWorld (id=%" PRIu64 ") that does not exist.", userStyleSheetData.worldIdentifier); + continue; + } + + UserStyleSheet sheet = userStyleSheetData.userStyleSheet; + addUserStyleSheetInternal(*it->value.first, userStyleSheetData.identifier, WTFMove(sheet)); + } + + invalidateInjectedStyleSheetCacheInAllFramesInAllPages(); +} + +void WebUserContentController::removeUserStyleSheet(uint64_t worldIdentifier, uint64_t userStyleSheetIdentifier) +{ + auto it = worldMap().find(worldIdentifier); + if (it == worldMap().end()) { + WTFLogAlways("Trying to remove a UserStyleSheet from a UserContentWorld (id=%" PRIu64 ") that does not exist.", worldIdentifier); + return; + } + + removeUserStyleSheetInternal(*it->value.first, userStyleSheetIdentifier); +} + +void WebUserContentController::removeAllUserStyleSheets(const Vector<uint64_t>& worldIdentifiers) +{ + bool sheetsChanged = false; + for (auto& worldIdentifier : worldIdentifiers) { + auto it = worldMap().find(worldIdentifier); + if (it == worldMap().end()) { + WTFLogAlways("Trying to remove all UserStyleSheets from a UserContentWorld (id=%" PRIu64 ") that does not exist.", worldIdentifier); + return; + } + + if (m_userStyleSheets.remove(it->value.first.get())) + sheetsChanged = true; + } + + if (sheetsChanged) + invalidateInjectedStyleSheetCacheInAllFramesInAllPages(); +} + +#if ENABLE(USER_MESSAGE_HANDLERS) +class WebUserMessageHandlerDescriptorProxy : public WebCore::UserMessageHandlerDescriptor { +public: + static PassRefPtr<WebUserMessageHandlerDescriptorProxy> create(WebUserContentController* controller, const String& name, InjectedBundleScriptWorld& world, uint64_t identifier) + { + return adoptRef(new WebUserMessageHandlerDescriptorProxy(controller, name, world, identifier)); + } + + virtual ~WebUserMessageHandlerDescriptorProxy() + { + } + + uint64_t identifier() { return m_identifier; } + +private: + WebUserMessageHandlerDescriptorProxy(WebUserContentController* controller, const String& name, InjectedBundleScriptWorld& world, uint64_t identifier) + : WebCore::UserMessageHandlerDescriptor(name, world.coreWorld()) + , m_controller(controller) + , m_identifier(identifier) + { + } + + // WebCore::UserMessageHandlerDescriptor + void didPostMessage(WebCore::UserMessageHandler& handler, WebCore::SerializedScriptValue* value) override + { + WebCore::Frame* frame = handler.frame(); + if (!frame) + return; + + WebFrame* webFrame = WebFrame::fromCoreFrame(*frame); + if (!webFrame) + return; + + WebPage* webPage = webFrame->page(); + if (!webPage) + return; + + WebProcess::singleton().parentProcessConnection()->send(Messages::WebUserContentControllerProxy::DidPostMessage(webPage->pageID(), webFrame->info(), m_identifier, IPC::DataReference(value->data())), m_controller->identifier()); + } + + RefPtr<WebUserContentController> m_controller; + uint64_t m_identifier; +}; +#endif + +void WebUserContentController::addUserScriptMessageHandlers(const Vector<WebScriptMessageHandlerData>& scriptMessageHandlers) +{ +#if ENABLE(USER_MESSAGE_HANDLERS) + for (auto& handler : scriptMessageHandlers) { + auto it = worldMap().find(handler.worldIdentifier); + if (it == worldMap().end()) { + WTFLogAlways("Trying to add a UserScriptMessageHandler to a UserContentWorld (id=%" PRIu64 ") that does not exist.", handler.worldIdentifier); + continue; + } + + addUserScriptMessageHandlerInternal(*it->value.first, handler.identifier, handler.name); + } +#else + UNUSED_PARAM(scriptMessageHandlers); +#endif +} + +void WebUserContentController::removeUserScriptMessageHandler(uint64_t worldIdentifier, uint64_t userScriptMessageHandlerIdentifier) +{ +#if ENABLE(USER_MESSAGE_HANDLERS) + auto it = worldMap().find(worldIdentifier); + if (it == worldMap().end()) { + WTFLogAlways("Trying to remove a UserScriptMessageHandler from a UserContentWorld (id=%" PRIu64 ") that does not exist.", worldIdentifier); + return; + } + + removeUserScriptMessageHandlerInternal(*it->value.first, userScriptMessageHandlerIdentifier); +#else + UNUSED_PARAM(worldIdentifier); + UNUSED_PARAM(userScriptMessageHandlerIdentifier); +#endif +} + +void WebUserContentController::removeAllUserScriptMessageHandlers(const Vector<uint64_t>& worldIdentifiers) +{ +#if ENABLE(USER_MESSAGE_HANDLERS) + bool userMessageHandlersChanged = false; + for (auto& worldIdentifier : worldIdentifiers) { + auto it = worldMap().find(worldIdentifier); + if (it == worldMap().end()) { + WTFLogAlways("Trying to remove all UserScriptMessageHandler from a UserContentWorld (id=%" PRIu64 ") that does not exist.", worldIdentifier); + return; + } + + if (m_userMessageHandlers.remove(it->value.first.get())) + userMessageHandlersChanged = true; + } + + if (userMessageHandlersChanged) + invalidateAllRegisteredUserMessageHandlerInvalidationClients(); +#else + UNUSED_PARAM(worldIdentifiers); +#endif +} + +#if ENABLE(USER_MESSAGE_HANDLERS) +void WebUserContentController::addUserScriptMessageHandlerInternal(InjectedBundleScriptWorld& world, uint64_t userScriptMessageHandlerIdentifier, const String& name) +{ + auto& messageHandlersInWorld = m_userMessageHandlers.ensure(&world, [] { return Vector<RefPtr<WebUserMessageHandlerDescriptorProxy>>(); }).iterator->value; + messageHandlersInWorld.append(WebUserMessageHandlerDescriptorProxy::create(this, name, world, userScriptMessageHandlerIdentifier)); +} + +void WebUserContentController::removeUserScriptMessageHandlerInternal(InjectedBundleScriptWorld& world, uint64_t userScriptMessageHandlerIdentifier) +{ + auto it = m_userMessageHandlers.find(&world); + if (it == m_userMessageHandlers.end()) + return; + + auto& userMessageHandlers = it->value; + + bool userMessageHandlersChanged = false; + for (int i = userMessageHandlers.size() - 1; i >= 0; --i) { + if (userMessageHandlers[i]->identifier() == userScriptMessageHandlerIdentifier) { + userMessageHandlers.remove(i); + userMessageHandlersChanged = true; + } + } + + if (!userMessageHandlersChanged) + return; + + if (userMessageHandlers.isEmpty()) + m_userMessageHandlers.remove(it); + + invalidateAllRegisteredUserMessageHandlerInvalidationClients(); +} +#endif + +#if ENABLE(CONTENT_EXTENSIONS) +void WebUserContentController::addUserContentExtensions(const Vector<std::pair<String, WebCompiledContentExtensionData>>& userContentExtensions) +{ + for (const auto& userContentExtension : userContentExtensions) { + WebCompiledContentExtensionData contentExtensionData = userContentExtension.second; + RefPtr<WebCompiledContentExtension> compiledContentExtension = WebCompiledContentExtension::create(WTFMove(contentExtensionData)); + + m_contentExtensionBackend.addContentExtension(userContentExtension.first, WTFMove(compiledContentExtension)); + } +} + +void WebUserContentController::removeUserContentExtension(const String& name) +{ + m_contentExtensionBackend.removeContentExtension(name); +} + +void WebUserContentController::removeAllUserContentExtensions() +{ + m_contentExtensionBackend.removeAllContentExtensions(); +} +#endif + +void WebUserContentController::addUserScriptInternal(InjectedBundleScriptWorld& world, uint64_t userScriptIdentifier, UserScript&& userScript) +{ + auto& scriptsInWorld = m_userScripts.ensure(&world, [] { return Vector<std::pair<uint64_t, WebCore::UserScript>>(); }).iterator->value; + scriptsInWorld.append(std::make_pair(userScriptIdentifier, WTFMove(userScript))); +} + +void WebUserContentController::addUserScript(InjectedBundleScriptWorld& world, UserScript&& userScript) +{ + addUserScriptInternal(world, 0, WTFMove(userScript)); +} + +void WebUserContentController::removeUserScriptWithURL(InjectedBundleScriptWorld& world, const URL& url) +{ + auto it = m_userScripts.find(&world); + if (it == m_userScripts.end()) + return; + + auto& scripts = it->value; + for (int i = scripts.size() - 1; i >= 0; --i) { + if (scripts[i].second.url() == url) + scripts.remove(i); + } + + if (scripts.isEmpty()) + m_userScripts.remove(it); +} + +void WebUserContentController::removeUserScriptInternal(InjectedBundleScriptWorld& world, uint64_t userScriptIdentifier) +{ + auto it = m_userScripts.find(&world); + if (it == m_userScripts.end()) + return; + + auto& scripts = it->value; + for (int i = scripts.size() - 1; i >= 0; --i) { + if (scripts[i].first == userScriptIdentifier) + scripts.remove(i); + } + + if (scripts.isEmpty()) + m_userScripts.remove(it); +} + +void WebUserContentController::removeUserScripts(InjectedBundleScriptWorld& world) +{ + m_userScripts.remove(&world); +} + +void WebUserContentController::addUserStyleSheetInternal(InjectedBundleScriptWorld& world, uint64_t userStyleSheetIdentifier, UserStyleSheet&& userStyleSheet) +{ + auto& styleSheetsInWorld = m_userStyleSheets.ensure(&world, [] { return Vector<std::pair<uint64_t, WebCore::UserStyleSheet>>(); }).iterator->value; + styleSheetsInWorld.append(std::make_pair(userStyleSheetIdentifier, WTFMove(userStyleSheet))); +} + +void WebUserContentController::addUserStyleSheet(InjectedBundleScriptWorld& world, UserStyleSheet&& userStyleSheet) +{ + addUserStyleSheetInternal(world, 0, WTFMove(userStyleSheet)); + invalidateInjectedStyleSheetCacheInAllFramesInAllPages(); +} + +void WebUserContentController::removeUserStyleSheetWithURL(InjectedBundleScriptWorld& world, const URL& url) +{ + auto it = m_userStyleSheets.find(&world); + if (it == m_userStyleSheets.end()) + return; + + auto& stylesheets = it->value; + + bool sheetsChanged = false; + for (int i = stylesheets.size() - 1; i >= 0; --i) { + if (stylesheets[i].second.url() == url) { + stylesheets.remove(i); + sheetsChanged = true; + } + } + + if (!sheetsChanged) + return; + + if (stylesheets.isEmpty()) + m_userStyleSheets.remove(it); + + invalidateInjectedStyleSheetCacheInAllFramesInAllPages(); +} + +void WebUserContentController::removeUserStyleSheetInternal(InjectedBundleScriptWorld& world, uint64_t userStyleSheetIdentifier) +{ + auto it = m_userStyleSheets.find(&world); + if (it == m_userStyleSheets.end()) + return; + + auto& stylesheets = it->value; + + bool sheetsChanged = false; + for (int i = stylesheets.size() - 1; i >= 0; --i) { + if (stylesheets[i].first == userStyleSheetIdentifier) { + stylesheets.remove(i); + sheetsChanged = true; + } + } + + if (!sheetsChanged) + return; + + if (stylesheets.isEmpty()) + m_userStyleSheets.remove(it); + + invalidateInjectedStyleSheetCacheInAllFramesInAllPages(); +} + +void WebUserContentController::removeUserStyleSheets(InjectedBundleScriptWorld& world) +{ + if (!m_userStyleSheets.remove(&world)) + return; + + invalidateInjectedStyleSheetCacheInAllFramesInAllPages(); +} + +void WebUserContentController::removeAllUserContent() +{ + m_userScripts.clear(); + + if (!m_userStyleSheets.isEmpty()) { + m_userStyleSheets.clear(); + invalidateInjectedStyleSheetCacheInAllFramesInAllPages(); + } +} + +void WebUserContentController::forEachUserScript(const std::function<void(WebCore::DOMWrapperWorld&, const WebCore::UserScript&)>& functor) const +{ + for (const auto& worldAndUserScriptVector : m_userScripts) { + auto& world = worldAndUserScriptVector.key->coreWorld(); + for (const auto& identifierUserScriptPair : worldAndUserScriptVector.value) + functor(world, identifierUserScriptPair.second); + } +} + +void WebUserContentController::forEachUserStyleSheet(const std::function<void(const WebCore::UserStyleSheet&)>& functor) const +{ + for (auto& styleSheetVector : m_userStyleSheets.values()) { + for (const auto& identifierUserStyleSheetPair : styleSheetVector) + functor(identifierUserStyleSheetPair.second); + } +} + +#if ENABLE(USER_MESSAGE_HANDLERS) +void WebUserContentController::forEachUserMessageHandler(const std::function<void(const WebCore::UserMessageHandlerDescriptor&)>& functor) const +{ + for (const auto& userMessageHandlerVector : m_userMessageHandlers.values()) { + for (const auto& userMessageHandler : userMessageHandlerVector) + functor(*userMessageHandler.get()); + } +} +#endif + +} // namespace WebKit |