diff options
Diffstat (limited to 'src/core/renderer/user_resource_controller.cpp')
-rw-r--r-- | src/core/renderer/user_resource_controller.cpp | 262 |
1 files changed, 262 insertions, 0 deletions
diff --git a/src/core/renderer/user_resource_controller.cpp b/src/core/renderer/user_resource_controller.cpp new file mode 100644 index 000000000..30a04958f --- /dev/null +++ b/src/core/renderer/user_resource_controller.cpp @@ -0,0 +1,262 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "user_resource_controller.h" + +#include "content/public/renderer/render_view.h" +#include "content/public/renderer/render_view_observer.h" +#include "third_party/WebKit/public/web/WebLocalFrame.h" +#include "third_party/WebKit/public/web/WebScriptSource.h" +#include "third_party/WebKit/public/web/WebView.h" +#include "v8/include/v8.h" + +#include "common/qt_messages.h" +#include "common/user_script_data.h" + +Q_GLOBAL_STATIC(UserResourceController, qt_webengine_userResourceController) + +static content::RenderView * const globalScriptsIndex = 0; + +// Scripts meant to run after the load event will be run 500ms after DOMContentLoaded if the load event doesn't come within that delay. +static const int afterLoadTimeout = 500; + +class UserResourceController::RenderViewObserverHelper : public content::RenderViewObserver +{ +public: + RenderViewObserverHelper(content::RenderView *); +private: + // RenderViewObserver implementation. + virtual void DidCreateDocumentElement(blink::WebLocalFrame* frame) Q_DECL_OVERRIDE; + virtual void DidFinishDocumentLoad(blink::WebLocalFrame* frame) Q_DECL_OVERRIDE; + virtual void DidFinishLoad(blink::WebLocalFrame* frame) Q_DECL_OVERRIDE; + virtual void DidStartProvisionalLoad(blink::WebLocalFrame* frame) Q_DECL_OVERRIDE; + virtual void FrameDetached(blink::WebFrame* frame) Q_DECL_OVERRIDE; + virtual void OnDestruct() Q_DECL_OVERRIDE; + virtual bool OnMessageReceived(const IPC::Message& message) Q_DECL_OVERRIDE; + + void onUserScriptAdded(const UserScriptData &); + void onUserScriptRemoved(const UserScriptData &); + void onScriptsCleared(); + + void runScripts(UserScriptData::InjectionPoint, blink::WebLocalFrame *); + QSet<blink::WebLocalFrame *> m_pendingFrames; +}; + +void UserResourceController::RenderViewObserverHelper::runScripts(UserScriptData::InjectionPoint p, blink::WebLocalFrame *frame) +{ + if (p == UserScriptData::AfterLoad && !m_pendingFrames.remove(frame)) + return; + content::RenderView *renderView = content::RenderView::FromWebView(frame->view()); + const bool isMainFrame = (frame == renderView->GetWebView()->mainFrame()); + + QList<uint64_t> scriptsToRun = UserResourceController::instance()->m_viewUserScriptMap.value(globalScriptsIndex).toList(); + scriptsToRun.append(UserResourceController::instance()->m_viewUserScriptMap.value(renderView).toList()); + + Q_FOREACH (uint64_t id, scriptsToRun) { + const UserScriptData &script = UserResourceController::instance()->m_scripts.value(id); + if (script.injectionPoint != p + || (!script.injectForSubframes && !isMainFrame)) + continue; + blink::WebScriptSource source(blink::WebString::fromUTF8(script.source), script.url); + if (script.worldId) + frame->executeScriptInIsolatedWorld(script.worldId, &source, /*numSources = */1, /*contentScriptExtentsionGroup = */ 0); + else + frame->executeScript(source); + } +} + + +UserResourceController::RenderViewObserverHelper::RenderViewObserverHelper(content::RenderView *renderView) + : content::RenderViewObserver(renderView) +{ +} + +void UserResourceController::RenderViewObserverHelper::DidCreateDocumentElement(blink::WebLocalFrame *frame) +{ + runScripts(UserScriptData::DocumentElementCreation, frame); +} + +void UserResourceController::RenderViewObserverHelper::DidFinishDocumentLoad(blink::WebLocalFrame *frame) +{ + runScripts(UserScriptData::DocumentLoadFinished, frame); + m_pendingFrames.insert(frame); + base::MessageLoop::current()->PostDelayedTask(FROM_HERE, base::Bind(&UserResourceController::RenderViewObserverHelper::runScripts, + base::Unretained(this), UserScriptData::AfterLoad, frame), + base::TimeDelta::FromMilliseconds(afterLoadTimeout)); +} + +void UserResourceController::RenderViewObserverHelper::DidFinishLoad(blink::WebLocalFrame *frame) +{ + // DidFinishDocumentLoad always comes before this, so frame has already been marked as pending. + base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(&UserResourceController::RenderViewObserverHelper::runScripts, + base::Unretained(this), UserScriptData::AfterLoad, frame)); +} + +void UserResourceController::RenderViewObserverHelper::DidStartProvisionalLoad(blink::WebLocalFrame *frame) +{ + m_pendingFrames.remove(frame); +} + +void UserResourceController::RenderViewObserverHelper::FrameDetached(blink::WebFrame *frame) +{ + if (frame->isWebLocalFrame()) + m_pendingFrames.remove(frame->toWebLocalFrame()); +} + +void UserResourceController::RenderViewObserverHelper::OnDestruct() +{ + UserResourceController::instance()->renderViewDestroyed(render_view()); +} + +bool UserResourceController::RenderViewObserverHelper::OnMessageReceived(const IPC::Message &message) +{ + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(UserResourceController::RenderViewObserverHelper, message) + IPC_MESSAGE_HANDLER(RenderViewObserverHelper_AddScript, onUserScriptAdded) + IPC_MESSAGE_HANDLER(RenderViewObserverHelper_RemoveScript, onUserScriptRemoved) + IPC_MESSAGE_HANDLER(RenderViewObserverHelper_ClearScripts, onScriptsCleared) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} + +void UserResourceController::RenderViewObserverHelper::onUserScriptAdded(const UserScriptData &script) +{ + UserResourceController::instance()->addScriptForView(script, render_view()); +} + +void UserResourceController::RenderViewObserverHelper::onUserScriptRemoved(const UserScriptData &script) +{ + UserResourceController::instance()->removeScriptForView(script, render_view()); +} + +void UserResourceController::RenderViewObserverHelper::onScriptsCleared() +{ + UserResourceController::instance()->clearScriptsForView(render_view()); +} + +UserResourceController *UserResourceController::instance() +{ + return qt_webengine_userResourceController(); +} + +bool UserResourceController::OnControlMessageReceived(const IPC::Message &message) +{ + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(UserResourceController, message) + IPC_MESSAGE_HANDLER(UserResourceController_AddScript, onAddScript) + IPC_MESSAGE_HANDLER(UserResourceController_RemoveScript, onRemoveScript) + IPC_MESSAGE_HANDLER(UserResourceController_ClearScripts, onClearScripts) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} + +UserResourceController::UserResourceController() +{ +#if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS) + static bool onlyCalledOnce = true; + Q_ASSERT(onlyCalledOnce); + onlyCalledOnce = false; +#endif // !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS) +} + +void UserResourceController::renderViewCreated(content::RenderView *renderView) +{ + // Will destroy itself with their RenderView. + new RenderViewObserverHelper(renderView); +} + +void UserResourceController::renderViewDestroyed(content::RenderView *renderView) +{ + ViewUserScriptMap::iterator it = m_viewUserScriptMap.find(renderView); + if (it == m_viewUserScriptMap.end()) // ASSERT maybe? + return; + Q_FOREACH (uint64_t id, it.value()) { + m_scripts.remove(id); + } + m_viewUserScriptMap.remove(renderView); +} + +void UserResourceController::addScriptForView(const UserScriptData &script, content::RenderView *view) +{ + ViewUserScriptMap::iterator it = m_viewUserScriptMap.find(view); + if (it == m_viewUserScriptMap.end()) + it = m_viewUserScriptMap.insert(view, UserScriptSet()); + + (*it).insert(script.scriptId); + m_scripts.insert(script.scriptId, script); +} + +void UserResourceController::removeScriptForView(const UserScriptData &script, content::RenderView *view) +{ + ViewUserScriptMap::iterator it = m_viewUserScriptMap.find(view); + if (it == m_viewUserScriptMap.end()) + return; + + (*it).remove(script.scriptId); + m_scripts.remove(script.scriptId); +} + +void UserResourceController::clearScriptsForView(content::RenderView *view) +{ + ViewUserScriptMap::iterator it = m_viewUserScriptMap.find(view); + if (it == m_viewUserScriptMap.end()) + return; + Q_FOREACH (uint64_t id, it.value()) + m_scripts.remove(id); + + m_viewUserScriptMap.remove(view); +} + +void UserResourceController::onAddScript(const UserScriptData &script) +{ + addScriptForView(script, globalScriptsIndex); +} + +void UserResourceController::onRemoveScript(const UserScriptData &script) +{ + removeScriptForView(script, globalScriptsIndex); +} + +void UserResourceController::onClearScripts() +{ + clearScriptsForView(globalScriptsIndex); +} + |